blob: a16b47c855aa48262eb4f78f7c2e26b7cdbad104 [file] [log] [blame]
Tomas Winklerbb1b0132012-12-25 19:06:07 +02001/*
2 *
3 * Intel Management Engine Interface (Intel MEI) Linux driver
4 * Copyright (c) 2003-2012, Intel Corporation.
5 *
6 * This program is free software; you can redistribute it and/or modify it
7 * under the terms and conditions of the GNU General Public License,
8 * version 2, as published by the Free Software Foundation.
9 *
10 * This program is distributed in the hope it will be useful, but WITHOUT
11 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
12 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
13 * more details.
14 *
15 */
16
Tomas Winkler4fcbc992014-03-18 22:51:55 +020017#include <linux/export.h>
Tomas Winklerbb1b0132012-12-25 19:06:07 +020018#include <linux/pci.h>
19#include <linux/sched.h>
20#include <linux/wait.h>
21#include <linux/mei.h>
22
23#include "mei_dev.h"
Tomas Winkler0edb23f2013-01-08 23:07:12 +020024#include "hbm.h"
Alexander Usyskin12d00662014-02-17 15:13:23 +020025#include "client.h"
Tomas Winklerbb1b0132012-12-25 19:06:07 +020026
Alexander Usyskin285e2992014-02-17 15:13:20 +020027static const char *mei_cl_conn_status_str(enum mei_cl_connect_status status)
28{
29#define MEI_CL_CS(status) case MEI_CL_CONN_##status: return #status
30 switch (status) {
31 MEI_CL_CS(SUCCESS);
32 MEI_CL_CS(NOT_FOUND);
33 MEI_CL_CS(ALREADY_STARTED);
34 MEI_CL_CS(OUT_OF_RESOURCES);
35 MEI_CL_CS(MESSAGE_SMALL);
36 default: return "unknown";
37 }
38#undef MEI_CL_CCS
39}
40
41/**
42 * mei_cl_conn_status_to_errno - convert client connect response
43 * status to error code
44 *
45 * @status: client connect response status
46 *
47 * returns corresponding error code
48 */
49static int mei_cl_conn_status_to_errno(enum mei_cl_connect_status status)
50{
51 switch (status) {
52 case MEI_CL_CONN_SUCCESS: return 0;
53 case MEI_CL_CONN_NOT_FOUND: return -ENOTTY;
54 case MEI_CL_CONN_ALREADY_STARTED: return -EBUSY;
55 case MEI_CL_CONN_OUT_OF_RESOURCES: return -EBUSY;
56 case MEI_CL_CONN_MESSAGE_SMALL: return -EINVAL;
57 default: return -EINVAL;
58 }
59}
60
Tomas Winklerbb1b0132012-12-25 19:06:07 +020061/**
Tomas Winklera40b2602013-01-08 23:07:19 +020062 * mei_hbm_me_cl_allocate - allocates storage for me clients
63 *
64 * @dev: the device structure
65 *
Tomas Winkler544f9462014-01-08 20:19:21 +020066 * returns 0 on success -ENOMEM on allocation failure
Tomas Winklera40b2602013-01-08 23:07:19 +020067 */
Tomas Winkler544f9462014-01-08 20:19:21 +020068static int mei_hbm_me_cl_allocate(struct mei_device *dev)
Tomas Winklera40b2602013-01-08 23:07:19 +020069{
70 struct mei_me_client *clients;
71 int b;
72
Tomas Winkler1aee3512013-09-02 13:29:45 +030073 dev->me_clients_num = 0;
74 dev->me_client_presentation_num = 0;
75 dev->me_client_index = 0;
76
Tomas Winklera40b2602013-01-08 23:07:19 +020077 /* count how many ME clients we have */
78 for_each_set_bit(b, dev->me_clients_map, MEI_CLIENTS_MAX)
79 dev->me_clients_num++;
80
Tomas Winkler1aee3512013-09-02 13:29:45 +030081 if (dev->me_clients_num == 0)
Tomas Winkler544f9462014-01-08 20:19:21 +020082 return 0;
Tomas Winklera40b2602013-01-08 23:07:19 +020083
84 kfree(dev->me_clients);
85 dev->me_clients = NULL;
86
Tomas Winklere19555c2013-09-02 03:11:03 +030087 dev_dbg(&dev->pdev->dev, "memory allocation for ME clients size=%ld.\n",
Tomas Winklera40b2602013-01-08 23:07:19 +020088 dev->me_clients_num * sizeof(struct mei_me_client));
89 /* allocate storage for ME clients representation */
90 clients = kcalloc(dev->me_clients_num,
91 sizeof(struct mei_me_client), GFP_KERNEL);
92 if (!clients) {
93 dev_err(&dev->pdev->dev, "memory allocation for ME clients failed.\n");
Tomas Winkler544f9462014-01-08 20:19:21 +020094 return -ENOMEM;
Tomas Winklera40b2602013-01-08 23:07:19 +020095 }
96 dev->me_clients = clients;
Tomas Winkler544f9462014-01-08 20:19:21 +020097 return 0;
Tomas Winklera40b2602013-01-08 23:07:19 +020098}
99
100/**
Tomas Winklercd51ed62012-12-25 19:06:09 +0200101 * mei_hbm_cl_hdr - construct client hbm header
Masanari Iida393b1482013-04-05 01:05:05 +0900102 *
Tomas Winklercd51ed62012-12-25 19:06:09 +0200103 * @cl: - client
104 * @hbm_cmd: host bus message command
105 * @buf: buffer for cl header
106 * @len: buffer length
107 */
108static inline
109void mei_hbm_cl_hdr(struct mei_cl *cl, u8 hbm_cmd, void *buf, size_t len)
110{
111 struct mei_hbm_cl_cmd *cmd = buf;
112
113 memset(cmd, 0, len);
114
115 cmd->hbm_cmd = hbm_cmd;
116 cmd->host_addr = cl->host_client_id;
117 cmd->me_addr = cl->me_client_id;
118}
119
120/**
Alexander Usyskin83ce0742014-01-08 22:31:46 +0200121 * mei_hbm_cl_addr_equal - tells if they have the same address
Tomas Winklercd51ed62012-12-25 19:06:09 +0200122 *
Alexander Usyskin83ce0742014-01-08 22:31:46 +0200123 * @cl: - client
124 * @buf: buffer with cl header
Tomas Winklercd51ed62012-12-25 19:06:09 +0200125 *
Alexander Usyskin83ce0742014-01-08 22:31:46 +0200126 * returns true if addresses are the same
Tomas Winklercd51ed62012-12-25 19:06:09 +0200127 */
128static inline
129bool mei_hbm_cl_addr_equal(struct mei_cl *cl, void *buf)
130{
131 struct mei_hbm_cl_cmd *cmd = buf;
132 return cl->host_client_id == cmd->host_addr &&
133 cl->me_client_id == cmd->me_addr;
134}
135
136
137/**
Tomas Winkler66ae4602014-01-08 20:19:22 +0200138 * mei_hbm_idle - set hbm to idle state
139 *
140 * @dev: the device structure
141 */
142void mei_hbm_idle(struct mei_device *dev)
143{
144 dev->init_clients_timer = 0;
145 dev->hbm_state = MEI_HBM_IDLE;
146}
147
Tomas Winkler9b0d5ef2013-04-18 23:03:48 +0300148int mei_hbm_start_wait(struct mei_device *dev)
149{
150 int ret;
151 if (dev->hbm_state > MEI_HBM_START)
152 return 0;
153
154 mutex_unlock(&dev->device_lock);
155 ret = wait_event_interruptible_timeout(dev->wait_recvd_msg,
156 dev->hbm_state == MEI_HBM_IDLE ||
Tomas Winkler544f9462014-01-08 20:19:21 +0200157 dev->hbm_state >= MEI_HBM_STARTED,
Tomas Winkler7d93e582014-01-14 23:10:10 +0200158 mei_secs_to_jiffies(MEI_HBM_TIMEOUT));
Tomas Winkler9b0d5ef2013-04-18 23:03:48 +0300159 mutex_lock(&dev->device_lock);
160
161 if (ret <= 0 && (dev->hbm_state <= MEI_HBM_START)) {
162 dev->hbm_state = MEI_HBM_IDLE;
Masanari Iida8b513d02013-05-21 23:13:12 +0900163 dev_err(&dev->pdev->dev, "waiting for mei start failed\n");
Alexander Usyskin7ca96aa2014-02-19 17:35:49 +0200164 return -ETIME;
Tomas Winkler9b0d5ef2013-04-18 23:03:48 +0300165 }
166 return 0;
167}
168
Tomas Winkler6bbda152012-12-25 19:06:12 +0200169/**
Tomas Winkler8120e722012-12-25 19:06:11 +0200170 * mei_hbm_start_req - sends start request message.
Tomas Winklerbb1b0132012-12-25 19:06:07 +0200171 *
172 * @dev: the device structure
Tomas Winkler544f9462014-01-08 20:19:21 +0200173 *
174 * returns 0 on success and < 0 on failure
Tomas Winklerbb1b0132012-12-25 19:06:07 +0200175 */
Tomas Winkler9b0d5ef2013-04-18 23:03:48 +0300176int mei_hbm_start_req(struct mei_device *dev)
Tomas Winklerbb1b0132012-12-25 19:06:07 +0200177{
Tomas Winklere46f1872012-12-25 19:06:10 +0200178 struct mei_msg_hdr *mei_hdr = &dev->wr_msg.hdr;
Tomas Winklerbb1b0132012-12-25 19:06:07 +0200179 struct hbm_host_version_request *start_req;
180 const size_t len = sizeof(struct hbm_host_version_request);
Tomas Winkler544f9462014-01-08 20:19:21 +0200181 int ret;
Tomas Winklerbb1b0132012-12-25 19:06:07 +0200182
Tomas Winklere46f1872012-12-25 19:06:10 +0200183 mei_hbm_hdr(mei_hdr, len);
Tomas Winklerbb1b0132012-12-25 19:06:07 +0200184
185 /* host start message */
Tomas Winklere46f1872012-12-25 19:06:10 +0200186 start_req = (struct hbm_host_version_request *)dev->wr_msg.data;
Tomas Winklerbb1b0132012-12-25 19:06:07 +0200187 memset(start_req, 0, len);
188 start_req->hbm_cmd = HOST_START_REQ_CMD;
189 start_req->host_version.major_version = HBM_MAJOR_VERSION;
190 start_req->host_version.minor_version = HBM_MINOR_VERSION;
191
Tomas Winkler9b0d5ef2013-04-18 23:03:48 +0300192 dev->hbm_state = MEI_HBM_IDLE;
Tomas Winkler544f9462014-01-08 20:19:21 +0200193 ret = mei_write_message(dev, mei_hdr, dev->wr_msg.data);
194 if (ret) {
195 dev_err(&dev->pdev->dev, "version message write failed: ret = %d\n",
196 ret);
197 return ret;
Tomas Winklerbb1b0132012-12-25 19:06:07 +0200198 }
Tomas Winkler544f9462014-01-08 20:19:21 +0200199
Tomas Winkler9b0d5ef2013-04-18 23:03:48 +0300200 dev->hbm_state = MEI_HBM_START;
Tomas Winklerbb1b0132012-12-25 19:06:07 +0200201 dev->init_clients_timer = MEI_CLIENTS_INIT_TIMEOUT;
Tomas Winkler9b0d5ef2013-04-18 23:03:48 +0300202 return 0;
Tomas Winklerbb1b0132012-12-25 19:06:07 +0200203}
204
Tomas Winkler9b0d5ef2013-04-18 23:03:48 +0300205/*
Tomas Winkler8120e722012-12-25 19:06:11 +0200206 * mei_hbm_enum_clients_req - sends enumeration client request message.
Tomas Winklerbb1b0132012-12-25 19:06:07 +0200207 *
208 * @dev: the device structure
209 *
Tomas Winkler544f9462014-01-08 20:19:21 +0200210 * returns 0 on success and < 0 on failure
Tomas Winklerbb1b0132012-12-25 19:06:07 +0200211 */
Tomas Winkler544f9462014-01-08 20:19:21 +0200212static int mei_hbm_enum_clients_req(struct mei_device *dev)
Tomas Winklerbb1b0132012-12-25 19:06:07 +0200213{
Tomas Winklere46f1872012-12-25 19:06:10 +0200214 struct mei_msg_hdr *mei_hdr = &dev->wr_msg.hdr;
Tomas Winklerbb1b0132012-12-25 19:06:07 +0200215 struct hbm_host_enum_request *enum_req;
216 const size_t len = sizeof(struct hbm_host_enum_request);
Tomas Winkler544f9462014-01-08 20:19:21 +0200217 int ret;
218
Tomas Winklerbb1b0132012-12-25 19:06:07 +0200219 /* enumerate clients */
Tomas Winklere46f1872012-12-25 19:06:10 +0200220 mei_hbm_hdr(mei_hdr, len);
Tomas Winklerbb1b0132012-12-25 19:06:07 +0200221
Tomas Winklere46f1872012-12-25 19:06:10 +0200222 enum_req = (struct hbm_host_enum_request *)dev->wr_msg.data;
223 memset(enum_req, 0, len);
Tomas Winklerbb1b0132012-12-25 19:06:07 +0200224 enum_req->hbm_cmd = HOST_ENUM_REQ_CMD;
225
Tomas Winkler544f9462014-01-08 20:19:21 +0200226 ret = mei_write_message(dev, mei_hdr, dev->wr_msg.data);
227 if (ret) {
228 dev_err(&dev->pdev->dev, "enumeration request write failed: ret = %d.\n",
229 ret);
230 return ret;
Tomas Winklerbb1b0132012-12-25 19:06:07 +0200231 }
Tomas Winkler9b0d5ef2013-04-18 23:03:48 +0300232 dev->hbm_state = MEI_HBM_ENUM_CLIENTS;
Tomas Winklerbb1b0132012-12-25 19:06:07 +0200233 dev->init_clients_timer = MEI_CLIENTS_INIT_TIMEOUT;
Tomas Winkler544f9462014-01-08 20:19:21 +0200234 return 0;
Tomas Winklerbb1b0132012-12-25 19:06:07 +0200235}
236
Tomas Winkler8120e722012-12-25 19:06:11 +0200237/**
Masanari Iida393b1482013-04-05 01:05:05 +0900238 * mei_hbm_prop_req - request property for a single client
Tomas Winkler8120e722012-12-25 19:06:11 +0200239 *
240 * @dev: the device structure
241 *
Tomas Winkler544f9462014-01-08 20:19:21 +0200242 * returns 0 on success and < 0 on failure
Tomas Winkler8120e722012-12-25 19:06:11 +0200243 */
Tomas Winklerbb1b0132012-12-25 19:06:07 +0200244
Tomas Winkler8120e722012-12-25 19:06:11 +0200245static int mei_hbm_prop_req(struct mei_device *dev)
Tomas Winklerbb1b0132012-12-25 19:06:07 +0200246{
247
Tomas Winklere46f1872012-12-25 19:06:10 +0200248 struct mei_msg_hdr *mei_hdr = &dev->wr_msg.hdr;
Tomas Winklerbb1b0132012-12-25 19:06:07 +0200249 struct hbm_props_request *prop_req;
250 const size_t len = sizeof(struct hbm_props_request);
251 unsigned long next_client_index;
Tomas Winkler1aee3512013-09-02 13:29:45 +0300252 unsigned long client_num;
Tomas Winkler544f9462014-01-08 20:19:21 +0200253 int ret;
Tomas Winklerbb1b0132012-12-25 19:06:07 +0200254
255 client_num = dev->me_client_presentation_num;
256
257 next_client_index = find_next_bit(dev->me_clients_map, MEI_CLIENTS_MAX,
258 dev->me_client_index);
259
260 /* We got all client properties */
261 if (next_client_index == MEI_CLIENTS_MAX) {
Tomas Winkler9b0d5ef2013-04-18 23:03:48 +0300262 dev->hbm_state = MEI_HBM_STARTED;
Tomas Winklerbb1b0132012-12-25 19:06:07 +0200263 schedule_work(&dev->init_work);
264
265 return 0;
266 }
267
268 dev->me_clients[client_num].client_id = next_client_index;
269 dev->me_clients[client_num].mei_flow_ctrl_creds = 0;
270
Tomas Winklere46f1872012-12-25 19:06:10 +0200271 mei_hbm_hdr(mei_hdr, len);
272 prop_req = (struct hbm_props_request *)dev->wr_msg.data;
Tomas Winklerbb1b0132012-12-25 19:06:07 +0200273
274 memset(prop_req, 0, sizeof(struct hbm_props_request));
275
276
277 prop_req->hbm_cmd = HOST_CLIENT_PROPERTIES_REQ_CMD;
278 prop_req->address = next_client_index;
279
Tomas Winkler544f9462014-01-08 20:19:21 +0200280 ret = mei_write_message(dev, mei_hdr, dev->wr_msg.data);
281 if (ret) {
282 dev_err(&dev->pdev->dev, "properties request write failed: ret = %d\n",
283 ret);
284 return ret;
Tomas Winklerbb1b0132012-12-25 19:06:07 +0200285 }
286
287 dev->init_clients_timer = MEI_CLIENTS_INIT_TIMEOUT;
288 dev->me_client_index = next_client_index;
289
290 return 0;
291}
292
Tomas Winkler4fcbc992014-03-18 22:51:55 +0200293/*
294 * mei_hbm_pg - sends pg command
295 *
296 * @dev: the device structure
297 * @pg_cmd: the pg command code
298 *
299 * This function returns -EIO on write failure
300 */
301int mei_hbm_pg(struct mei_device *dev, u8 pg_cmd)
302{
303 struct mei_msg_hdr *mei_hdr = &dev->wr_msg.hdr;
304 struct hbm_power_gate *req;
305 const size_t len = sizeof(struct hbm_power_gate);
306 int ret;
307
308 mei_hbm_hdr(mei_hdr, len);
309
310 req = (struct hbm_power_gate *)dev->wr_msg.data;
311 memset(req, 0, len);
312 req->hbm_cmd = pg_cmd;
313
314 ret = mei_write_message(dev, mei_hdr, dev->wr_msg.data);
315 if (ret)
316 dev_err(&dev->pdev->dev, "power gate command write failed.\n");
317 return ret;
318}
319EXPORT_SYMBOL_GPL(mei_hbm_pg);
320
Tomas Winklerbb1b0132012-12-25 19:06:07 +0200321/**
Tomas Winkler6bb948c2014-02-12 21:41:52 +0200322 * mei_hbm_stop_req - send stop request message
Tomas Winklere46f1872012-12-25 19:06:10 +0200323 *
324 * @dev - mei device
Tomas Winkler6bb948c2014-02-12 21:41:52 +0200325 * @cl: client info
326 *
327 * This function returns -EIO on write failure
Tomas Winklere46f1872012-12-25 19:06:10 +0200328 */
Tomas Winkler6bb948c2014-02-12 21:41:52 +0200329static int mei_hbm_stop_req(struct mei_device *dev)
Tomas Winklere46f1872012-12-25 19:06:10 +0200330{
Tomas Winkler6bb948c2014-02-12 21:41:52 +0200331 struct mei_msg_hdr *mei_hdr = &dev->wr_msg.hdr;
Tomas Winklere46f1872012-12-25 19:06:10 +0200332 struct hbm_host_stop_request *req =
Tomas Winkler6bb948c2014-02-12 21:41:52 +0200333 (struct hbm_host_stop_request *)dev->wr_msg.data;
Tomas Winklere46f1872012-12-25 19:06:10 +0200334 const size_t len = sizeof(struct hbm_host_stop_request);
335
336 mei_hbm_hdr(mei_hdr, len);
337
338 memset(req, 0, len);
339 req->hbm_cmd = HOST_STOP_REQ_CMD;
340 req->reason = DRIVER_STOP_REQUEST;
Tomas Winkler6bb948c2014-02-12 21:41:52 +0200341
342 return mei_write_message(dev, mei_hdr, dev->wr_msg.data);
Tomas Winklere46f1872012-12-25 19:06:10 +0200343}
344
345/**
Alexander Usyskin83ce0742014-01-08 22:31:46 +0200346 * mei_hbm_cl_flow_control_req - sends flow control request.
Tomas Winklerbb1b0132012-12-25 19:06:07 +0200347 *
348 * @dev: the device structure
Tomas Winkler8120e722012-12-25 19:06:11 +0200349 * @cl: client info
Tomas Winklerbb1b0132012-12-25 19:06:07 +0200350 *
351 * This function returns -EIO on write failure
352 */
Tomas Winkler8120e722012-12-25 19:06:11 +0200353int mei_hbm_cl_flow_control_req(struct mei_device *dev, struct mei_cl *cl)
Tomas Winklerbb1b0132012-12-25 19:06:07 +0200354{
Tomas Winklere46f1872012-12-25 19:06:10 +0200355 struct mei_msg_hdr *mei_hdr = &dev->wr_msg.hdr;
Tomas Winklerbb1b0132012-12-25 19:06:07 +0200356 const size_t len = sizeof(struct hbm_flow_control);
357
Tomas Winklere46f1872012-12-25 19:06:10 +0200358 mei_hbm_hdr(mei_hdr, len);
359 mei_hbm_cl_hdr(cl, MEI_FLOW_CONTROL_CMD, dev->wr_msg.data, len);
Tomas Winklerbb1b0132012-12-25 19:06:07 +0200360
Tomas Winkler46922182014-03-16 14:35:55 +0200361 cl_dbg(dev, cl, "sending flow control\n");
Tomas Winklerbb1b0132012-12-25 19:06:07 +0200362
Tomas Winklere46f1872012-12-25 19:06:10 +0200363 return mei_write_message(dev, mei_hdr, dev->wr_msg.data);
Tomas Winklerbb1b0132012-12-25 19:06:07 +0200364}
365
366/**
Masanari Iida393b1482013-04-05 01:05:05 +0900367 * mei_hbm_add_single_flow_creds - adds single buffer credentials.
Tomas Winkler6bbda152012-12-25 19:06:12 +0200368 *
Masanari Iida393b1482013-04-05 01:05:05 +0900369 * @dev: the device structure
Tomas Winkler6bbda152012-12-25 19:06:12 +0200370 * @flow: flow control.
Alexander Usyskin12d00662014-02-17 15:13:23 +0200371 *
372 * return 0 on success, < 0 otherwise
Tomas Winkler6bbda152012-12-25 19:06:12 +0200373 */
Alexander Usyskin12d00662014-02-17 15:13:23 +0200374static int mei_hbm_add_single_flow_creds(struct mei_device *dev,
Tomas Winkler6bbda152012-12-25 19:06:12 +0200375 struct hbm_flow_control *flow)
376{
Alexander Usyskin12d00662014-02-17 15:13:23 +0200377 struct mei_me_client *me_cl;
378 int id;
Tomas Winkler6bbda152012-12-25 19:06:12 +0200379
Alexander Usyskin12d00662014-02-17 15:13:23 +0200380 id = mei_me_cl_by_id(dev, flow->me_addr);
381 if (id < 0) {
382 dev_err(&dev->pdev->dev, "no such me client %d\n",
383 flow->me_addr);
384 return id;
Tomas Winkler6bbda152012-12-25 19:06:12 +0200385 }
Alexander Usyskin12d00662014-02-17 15:13:23 +0200386
387 me_cl = &dev->me_clients[id];
388 if (me_cl->props.single_recv_buf) {
389 me_cl->mei_flow_ctrl_creds++;
390 dev_dbg(&dev->pdev->dev, "recv flow ctrl msg ME %d (single).\n",
391 flow->me_addr);
392 dev_dbg(&dev->pdev->dev, "flow control credentials =%d.\n",
393 me_cl->mei_flow_ctrl_creds);
394 } else {
395 BUG(); /* error in flow control */
396 }
397
398 return 0;
Tomas Winkler6bbda152012-12-25 19:06:12 +0200399}
400
401/**
402 * mei_hbm_cl_flow_control_res - flow control response from me
403 *
404 * @dev: the device structure
405 * @flow_control: flow control response bus message
406 */
407static void mei_hbm_cl_flow_control_res(struct mei_device *dev,
408 struct hbm_flow_control *flow_control)
409{
Tomas Winkler31f88f52014-02-17 15:13:25 +0200410 struct mei_cl *cl;
Tomas Winkler6bbda152012-12-25 19:06:12 +0200411
412 if (!flow_control->host_addr) {
413 /* single receive buffer */
414 mei_hbm_add_single_flow_creds(dev, flow_control);
415 return;
416 }
417
418 /* normal connection */
Tomas Winkler31f88f52014-02-17 15:13:25 +0200419 list_for_each_entry(cl, &dev->file_list, link) {
Tomas Winkler6bbda152012-12-25 19:06:12 +0200420 if (mei_hbm_cl_addr_equal(cl, flow_control)) {
421 cl->mei_flow_ctrl_creds++;
422 dev_dbg(&dev->pdev->dev, "flow ctrl msg for host %d ME %d.\n",
423 flow_control->host_addr, flow_control->me_addr);
424 dev_dbg(&dev->pdev->dev, "flow control credentials = %d.\n",
425 cl->mei_flow_ctrl_creds);
426 break;
427 }
428 }
429}
430
431
432/**
Tomas Winkler8120e722012-12-25 19:06:11 +0200433 * mei_hbm_cl_disconnect_req - sends disconnect message to fw.
Tomas Winklerbb1b0132012-12-25 19:06:07 +0200434 *
435 * @dev: the device structure
Tomas Winkler8120e722012-12-25 19:06:11 +0200436 * @cl: a client to disconnect from
Tomas Winklerbb1b0132012-12-25 19:06:07 +0200437 *
438 * This function returns -EIO on write failure
439 */
Tomas Winkler8120e722012-12-25 19:06:11 +0200440int mei_hbm_cl_disconnect_req(struct mei_device *dev, struct mei_cl *cl)
Tomas Winklerbb1b0132012-12-25 19:06:07 +0200441{
Tomas Winklere46f1872012-12-25 19:06:10 +0200442 struct mei_msg_hdr *mei_hdr = &dev->wr_msg.hdr;
Tomas Winklerbb1b0132012-12-25 19:06:07 +0200443 const size_t len = sizeof(struct hbm_client_connect_request);
444
Tomas Winklere46f1872012-12-25 19:06:10 +0200445 mei_hbm_hdr(mei_hdr, len);
446 mei_hbm_cl_hdr(cl, CLIENT_DISCONNECT_REQ_CMD, dev->wr_msg.data, len);
Tomas Winklerbb1b0132012-12-25 19:06:07 +0200447
Tomas Winklere46f1872012-12-25 19:06:10 +0200448 return mei_write_message(dev, mei_hdr, dev->wr_msg.data);
Tomas Winklerbb1b0132012-12-25 19:06:07 +0200449}
450
451/**
Tomas Winkler6bb948c2014-02-12 21:41:52 +0200452 * mei_hbm_cl_disconnect_rsp - sends disconnect respose to the FW
453 *
454 * @dev: the device structure
455 * @cl: a client to disconnect from
456 *
457 * This function returns -EIO on write failure
458 */
459int mei_hbm_cl_disconnect_rsp(struct mei_device *dev, struct mei_cl *cl)
460{
461 struct mei_msg_hdr *mei_hdr = &dev->wr_msg.hdr;
462 const size_t len = sizeof(struct hbm_client_connect_response);
463
464 mei_hbm_hdr(mei_hdr, len);
465 mei_hbm_cl_hdr(cl, CLIENT_DISCONNECT_RES_CMD, dev->wr_msg.data, len);
466
467 return mei_write_message(dev, mei_hdr, dev->wr_msg.data);
468}
469
470/**
Tomas Winkler6bbda152012-12-25 19:06:12 +0200471 * mei_hbm_cl_disconnect_res - disconnect response from ME
472 *
473 * @dev: the device structure
474 * @rs: disconnect response bus message
475 */
476static void mei_hbm_cl_disconnect_res(struct mei_device *dev,
477 struct hbm_client_connect_response *rs)
478{
479 struct mei_cl *cl;
Tomas Winkler64092852014-02-17 15:13:21 +0200480 struct mei_cl_cb *cb, *next;
Tomas Winkler6bbda152012-12-25 19:06:12 +0200481
Alexander Usyskin285e2992014-02-17 15:13:20 +0200482 dev_dbg(&dev->pdev->dev, "hbm: disconnect response cl:host=%02d me=%02d status=%d\n",
483 rs->me_addr, rs->host_addr, rs->status);
Tomas Winkler6bbda152012-12-25 19:06:12 +0200484
Tomas Winkler64092852014-02-17 15:13:21 +0200485 list_for_each_entry_safe(cb, next, &dev->ctrl_rd_list.list, list) {
486 cl = cb->cl;
Tomas Winkler6bbda152012-12-25 19:06:12 +0200487
Tomas Winkler64092852014-02-17 15:13:21 +0200488 /* this should not happen */
489 if (WARN_ON(!cl)) {
490 list_del(&cb->list);
Tomas Winkler6bbda152012-12-25 19:06:12 +0200491 return;
492 }
493
Tomas Winkler6bbda152012-12-25 19:06:12 +0200494 if (mei_hbm_cl_addr_equal(cl, rs)) {
Tomas Winkler64092852014-02-17 15:13:21 +0200495 list_del(&cb->list);
Alexander Usyskin285e2992014-02-17 15:13:20 +0200496 if (rs->status == MEI_CL_DISCONN_SUCCESS)
Tomas Winkler6bbda152012-12-25 19:06:12 +0200497 cl->state = MEI_FILE_DISCONNECTED;
498
499 cl->status = 0;
500 cl->timer_count = 0;
501 break;
502 }
503 }
504}
505
506/**
Tomas Winkler8120e722012-12-25 19:06:11 +0200507 * mei_hbm_cl_connect_req - send connection request to specific me client
Tomas Winklerbb1b0132012-12-25 19:06:07 +0200508 *
509 * @dev: the device structure
Tomas Winkler8120e722012-12-25 19:06:11 +0200510 * @cl: a client to connect to
Tomas Winklerbb1b0132012-12-25 19:06:07 +0200511 *
Tomas Winkler8120e722012-12-25 19:06:11 +0200512 * returns -EIO on write failure
Tomas Winklerbb1b0132012-12-25 19:06:07 +0200513 */
Tomas Winkler8120e722012-12-25 19:06:11 +0200514int mei_hbm_cl_connect_req(struct mei_device *dev, struct mei_cl *cl)
Tomas Winklerbb1b0132012-12-25 19:06:07 +0200515{
Tomas Winklere46f1872012-12-25 19:06:10 +0200516 struct mei_msg_hdr *mei_hdr = &dev->wr_msg.hdr;
Tomas Winklerbb1b0132012-12-25 19:06:07 +0200517 const size_t len = sizeof(struct hbm_client_connect_request);
518
Tomas Winklere46f1872012-12-25 19:06:10 +0200519 mei_hbm_hdr(mei_hdr, len);
520 mei_hbm_cl_hdr(cl, CLIENT_CONNECT_REQ_CMD, dev->wr_msg.data, len);
Tomas Winklerbb1b0132012-12-25 19:06:07 +0200521
Tomas Winklere46f1872012-12-25 19:06:10 +0200522 return mei_write_message(dev, mei_hdr, dev->wr_msg.data);
Tomas Winklerbb1b0132012-12-25 19:06:07 +0200523}
524
525/**
Alexander Usyskin83ce0742014-01-08 22:31:46 +0200526 * mei_hbm_cl_connect_res - connect response from the ME
Tomas Winkler6bbda152012-12-25 19:06:12 +0200527 *
528 * @dev: the device structure
529 * @rs: connect response bus message
530 */
531static void mei_hbm_cl_connect_res(struct mei_device *dev,
532 struct hbm_client_connect_response *rs)
533{
534
535 struct mei_cl *cl;
Tomas Winkler64092852014-02-17 15:13:21 +0200536 struct mei_cl_cb *cb, *next;
Tomas Winkler6bbda152012-12-25 19:06:12 +0200537
Alexander Usyskin285e2992014-02-17 15:13:20 +0200538 dev_dbg(&dev->pdev->dev, "hbm: connect response cl:host=%02d me=%02d status=%s\n",
539 rs->me_addr, rs->host_addr,
540 mei_cl_conn_status_str(rs->status));
Tomas Winkler6bbda152012-12-25 19:06:12 +0200541
Tomas Winkler64092852014-02-17 15:13:21 +0200542 cl = NULL;
Tomas Winkler6bbda152012-12-25 19:06:12 +0200543
Tomas Winkler64092852014-02-17 15:13:21 +0200544 list_for_each_entry_safe(cb, next, &dev->ctrl_rd_list.list, list) {
Tomas Winkler6bbda152012-12-25 19:06:12 +0200545
Tomas Winkler64092852014-02-17 15:13:21 +0200546 cl = cb->cl;
547 /* this should not happen */
548 if (WARN_ON(!cl)) {
549 list_del_init(&cb->list);
550 continue;
Tomas Winkler6bbda152012-12-25 19:06:12 +0200551 }
Tomas Winkler64092852014-02-17 15:13:21 +0200552
553 if (cb->fop_type != MEI_FOP_CONNECT)
554 continue;
555
556 if (mei_hbm_cl_addr_equal(cl, rs)) {
557 list_del(&cb->list);
558 break;
Tomas Winkler6bbda152012-12-25 19:06:12 +0200559 }
560 }
Tomas Winkler64092852014-02-17 15:13:21 +0200561
562 if (!cl)
563 return;
564
565 cl->timer_count = 0;
566 if (rs->status == MEI_CL_CONN_SUCCESS)
567 cl->state = MEI_FILE_CONNECTED;
568 else
569 cl->state = MEI_FILE_DISCONNECTED;
570 cl->status = mei_cl_conn_status_to_errno(rs->status);
Tomas Winkler6bbda152012-12-25 19:06:12 +0200571}
572
573
574/**
Alexander Usyskin83ce0742014-01-08 22:31:46 +0200575 * mei_hbm_fw_disconnect_req - disconnect request initiated by ME firmware
576 * host sends disconnect response
Tomas Winklerbb1b0132012-12-25 19:06:07 +0200577 *
578 * @dev: the device structure.
Tomas Winkler8120e722012-12-25 19:06:11 +0200579 * @disconnect_req: disconnect request bus message from the me
Tomas Winkler6bb948c2014-02-12 21:41:52 +0200580 *
581 * returns -ENOMEM on allocation failure
Tomas Winklerbb1b0132012-12-25 19:06:07 +0200582 */
Tomas Winkler6bb948c2014-02-12 21:41:52 +0200583static int mei_hbm_fw_disconnect_req(struct mei_device *dev,
Tomas Winklerbb1b0132012-12-25 19:06:07 +0200584 struct hbm_client_connect_request *disconnect_req)
585{
Tomas Winkler31f88f52014-02-17 15:13:25 +0200586 struct mei_cl *cl;
Tomas Winkler6bb948c2014-02-12 21:41:52 +0200587 struct mei_cl_cb *cb;
Tomas Winklerbb1b0132012-12-25 19:06:07 +0200588
Tomas Winkler31f88f52014-02-17 15:13:25 +0200589 list_for_each_entry(cl, &dev->file_list, link) {
Tomas Winklercd51ed62012-12-25 19:06:09 +0200590 if (mei_hbm_cl_addr_equal(cl, disconnect_req)) {
Tomas Winklerbb1b0132012-12-25 19:06:07 +0200591 dev_dbg(&dev->pdev->dev, "disconnect request host client %d ME client %d.\n",
592 disconnect_req->host_addr,
593 disconnect_req->me_addr);
Tomas Winklercd51ed62012-12-25 19:06:09 +0200594 cl->state = MEI_FILE_DISCONNECTED;
595 cl->timer_count = 0;
Tomas Winklerbb1b0132012-12-25 19:06:07 +0200596
Tomas Winkler6bb948c2014-02-12 21:41:52 +0200597 cb = mei_io_cb_init(cl, NULL);
598 if (!cb)
599 return -ENOMEM;
600 cb->fop_type = MEI_FOP_DISCONNECT_RSP;
601 cl_dbg(dev, cl, "add disconnect response as first\n");
602 list_add(&cb->list, &dev->ctrl_wr_list.list);
603
Tomas Winklerbb1b0132012-12-25 19:06:07 +0200604 break;
605 }
606 }
Tomas Winkler6bb948c2014-02-12 21:41:52 +0200607 return 0;
Tomas Winklerbb1b0132012-12-25 19:06:07 +0200608}
609
610
611/**
Tomas Winkler2c9b48a2013-06-16 09:16:31 +0300612 * mei_hbm_version_is_supported - checks whether the driver can
613 * support the hbm version of the device
614 *
615 * @dev: the device structure
616 * returns true if driver can support hbm version of the device
617 */
618bool mei_hbm_version_is_supported(struct mei_device *dev)
619{
620 return (dev->version.major_version < HBM_MAJOR_VERSION) ||
621 (dev->version.major_version == HBM_MAJOR_VERSION &&
622 dev->version.minor_version <= HBM_MINOR_VERSION);
623}
624
625/**
Tomas Winklerbb1b0132012-12-25 19:06:07 +0200626 * mei_hbm_dispatch - bottom half read routine after ISR to
627 * handle the read bus message cmd processing.
628 *
629 * @dev: the device structure
630 * @mei_hdr: header of bus message
Tomas Winkler544f9462014-01-08 20:19:21 +0200631 *
632 * returns 0 on success and < 0 on failure
Tomas Winklerbb1b0132012-12-25 19:06:07 +0200633 */
Tomas Winkler544f9462014-01-08 20:19:21 +0200634int mei_hbm_dispatch(struct mei_device *dev, struct mei_msg_hdr *hdr)
Tomas Winklerbb1b0132012-12-25 19:06:07 +0200635{
636 struct mei_bus_message *mei_msg;
637 struct mei_me_client *me_client;
638 struct hbm_host_version_response *version_res;
639 struct hbm_client_connect_response *connect_res;
640 struct hbm_client_connect_response *disconnect_res;
641 struct hbm_client_connect_request *disconnect_req;
642 struct hbm_flow_control *flow_control;
643 struct hbm_props_response *props_res;
644 struct hbm_host_enum_response *enum_res;
Tomas Winklerbb1b0132012-12-25 19:06:07 +0200645
646 /* read the message to our buffer */
647 BUG_ON(hdr->length >= sizeof(dev->rd_msg_buf));
648 mei_read_slots(dev, dev->rd_msg_buf, hdr->length);
649 mei_msg = (struct mei_bus_message *)dev->rd_msg_buf;
650
Tomas Winkler66ae4602014-01-08 20:19:22 +0200651 /* ignore spurious message and prevent reset nesting
652 * hbm is put to idle during system reset
653 */
654 if (dev->hbm_state == MEI_HBM_IDLE) {
655 dev_dbg(&dev->pdev->dev, "hbm: state is idle ignore spurious messages\n");
656 return 0;
657 }
658
Tomas Winklerbb1b0132012-12-25 19:06:07 +0200659 switch (mei_msg->hbm_cmd) {
660 case HOST_START_RES_CMD:
Tomas Winkler544f9462014-01-08 20:19:21 +0200661 dev_dbg(&dev->pdev->dev, "hbm: start: response message received.\n");
662
663 dev->init_clients_timer = 0;
664
Tomas Winklerbb1b0132012-12-25 19:06:07 +0200665 version_res = (struct hbm_host_version_response *)mei_msg;
Tomas Winkler2c9b48a2013-06-16 09:16:31 +0300666
667 dev_dbg(&dev->pdev->dev, "HBM VERSION: DRIVER=%02d:%02d DEVICE=%02d:%02d\n",
668 HBM_MAJOR_VERSION, HBM_MINOR_VERSION,
669 version_res->me_max_version.major_version,
670 version_res->me_max_version.minor_version);
671
672 if (version_res->host_version_supported) {
673 dev->version.major_version = HBM_MAJOR_VERSION;
674 dev->version.minor_version = HBM_MINOR_VERSION;
675 } else {
676 dev->version.major_version =
677 version_res->me_max_version.major_version;
678 dev->version.minor_version =
679 version_res->me_max_version.minor_version;
680 }
681
682 if (!mei_hbm_version_is_supported(dev)) {
Tomas Winkler544f9462014-01-08 20:19:21 +0200683 dev_warn(&dev->pdev->dev, "hbm: start: version mismatch - stopping the driver.\n");
Tomas Winklere46f1872012-12-25 19:06:10 +0200684
Tomas Winkler544f9462014-01-08 20:19:21 +0200685 dev->hbm_state = MEI_HBM_STOPPED;
Tomas Winkler6bb948c2014-02-12 21:41:52 +0200686 if (mei_hbm_stop_req(dev)) {
Tomas Winkler544f9462014-01-08 20:19:21 +0200687 dev_err(&dev->pdev->dev, "hbm: start: failed to send stop request\n");
688 return -EIO;
689 }
690 break;
Tomas Winklere46f1872012-12-25 19:06:10 +0200691 }
692
Tomas Winkler544f9462014-01-08 20:19:21 +0200693 if (dev->dev_state != MEI_DEV_INIT_CLIENTS ||
694 dev->hbm_state != MEI_HBM_START) {
695 dev_err(&dev->pdev->dev, "hbm: start: state mismatch, [%d, %d]\n",
696 dev->dev_state, dev->hbm_state);
697 return -EPROTO;
698 }
699
700 dev->hbm_state = MEI_HBM_STARTED;
701
702 if (mei_hbm_enum_clients_req(dev)) {
703 dev_err(&dev->pdev->dev, "hbm: start: failed to send enumeration request\n");
704 return -EIO;
Tomas Winklerbb1b0132012-12-25 19:06:07 +0200705 }
706
Tomas Winkler9b0d5ef2013-04-18 23:03:48 +0300707 wake_up_interruptible(&dev->wait_recvd_msg);
Tomas Winklerbb1b0132012-12-25 19:06:07 +0200708 break;
709
710 case CLIENT_CONNECT_RES_CMD:
Tomas Winkler544f9462014-01-08 20:19:21 +0200711 dev_dbg(&dev->pdev->dev, "hbm: client connect response: message received.\n");
712
Tomas Winklerbb1b0132012-12-25 19:06:07 +0200713 connect_res = (struct hbm_client_connect_response *) mei_msg;
Tomas Winkler6bbda152012-12-25 19:06:12 +0200714 mei_hbm_cl_connect_res(dev, connect_res);
Tomas Winklerbb1b0132012-12-25 19:06:07 +0200715 wake_up(&dev->wait_recvd_msg);
716 break;
717
718 case CLIENT_DISCONNECT_RES_CMD:
Tomas Winkler544f9462014-01-08 20:19:21 +0200719 dev_dbg(&dev->pdev->dev, "hbm: client disconnect response: message received.\n");
720
Tomas Winklerbb1b0132012-12-25 19:06:07 +0200721 disconnect_res = (struct hbm_client_connect_response *) mei_msg;
Tomas Winkler6bbda152012-12-25 19:06:12 +0200722 mei_hbm_cl_disconnect_res(dev, disconnect_res);
Tomas Winklerbb1b0132012-12-25 19:06:07 +0200723 wake_up(&dev->wait_recvd_msg);
724 break;
725
726 case MEI_FLOW_CONTROL_CMD:
Tomas Winkler544f9462014-01-08 20:19:21 +0200727 dev_dbg(&dev->pdev->dev, "hbm: client flow control response: message received.\n");
728
Tomas Winklerbb1b0132012-12-25 19:06:07 +0200729 flow_control = (struct hbm_flow_control *) mei_msg;
Tomas Winkler6bbda152012-12-25 19:06:12 +0200730 mei_hbm_cl_flow_control_res(dev, flow_control);
Tomas Winklerbb1b0132012-12-25 19:06:07 +0200731 break;
732
Tomas Winkler4fcbc992014-03-18 22:51:55 +0200733 case MEI_PG_ISOLATION_ENTRY_RES_CMD:
734 dev_dbg(&dev->pdev->dev, "power gate isolation entry response received\n");
735 if (waitqueue_active(&dev->wait_pg))
736 wake_up(&dev->wait_pg);
737 break;
738
739 case MEI_PG_ISOLATION_EXIT_REQ_CMD:
740 dev_dbg(&dev->pdev->dev, "power gate isolation exit request received\n");
741 if (waitqueue_active(&dev->wait_pg))
742 wake_up(&dev->wait_pg);
743 break;
744
Tomas Winklerbb1b0132012-12-25 19:06:07 +0200745 case HOST_CLIENT_PROPERTIES_RES_CMD:
Tomas Winkler544f9462014-01-08 20:19:21 +0200746 dev_dbg(&dev->pdev->dev, "hbm: properties response: message received.\n");
747
748 dev->init_clients_timer = 0;
749
750 if (dev->me_clients == NULL) {
751 dev_err(&dev->pdev->dev, "hbm: properties response: mei_clients not allocated\n");
752 return -EPROTO;
753 }
754
Tomas Winklerbb1b0132012-12-25 19:06:07 +0200755 props_res = (struct hbm_props_response *)mei_msg;
756 me_client = &dev->me_clients[dev->me_client_presentation_num];
757
Tomas Winkler544f9462014-01-08 20:19:21 +0200758 if (props_res->status) {
759 dev_err(&dev->pdev->dev, "hbm: properties response: wrong status = %d\n",
760 props_res->status);
761 return -EPROTO;
Tomas Winklerbb1b0132012-12-25 19:06:07 +0200762 }
763
764 if (me_client->client_id != props_res->address) {
Tomas Winkler544f9462014-01-08 20:19:21 +0200765 dev_err(&dev->pdev->dev, "hbm: properties response: address mismatch %d ?= %d\n",
766 me_client->client_id, props_res->address);
767 return -EPROTO;
Tomas Winklerbb1b0132012-12-25 19:06:07 +0200768 }
769
770 if (dev->dev_state != MEI_DEV_INIT_CLIENTS ||
Tomas Winkler9b0d5ef2013-04-18 23:03:48 +0300771 dev->hbm_state != MEI_HBM_CLIENT_PROPERTIES) {
Tomas Winkler544f9462014-01-08 20:19:21 +0200772 dev_err(&dev->pdev->dev, "hbm: properties response: state mismatch, [%d, %d]\n",
773 dev->dev_state, dev->hbm_state);
774 return -EPROTO;
Tomas Winklerbb1b0132012-12-25 19:06:07 +0200775 }
776
777 me_client->props = props_res->client_properties;
778 dev->me_client_index++;
779 dev->me_client_presentation_num++;
780
Tomas Winkler8120e722012-12-25 19:06:11 +0200781 /* request property for the next client */
Tomas Winkler544f9462014-01-08 20:19:21 +0200782 if (mei_hbm_prop_req(dev))
783 return -EIO;
Tomas Winklerbb1b0132012-12-25 19:06:07 +0200784
785 break;
786
787 case HOST_ENUM_RES_CMD:
Tomas Winkler544f9462014-01-08 20:19:21 +0200788 dev_dbg(&dev->pdev->dev, "hbm: enumeration response: message received\n");
789
790 dev->init_clients_timer = 0;
791
Tomas Winklerbb1b0132012-12-25 19:06:07 +0200792 enum_res = (struct hbm_host_enum_response *) mei_msg;
Tomas Winkler23f5a322013-09-02 03:11:01 +0300793 BUILD_BUG_ON(sizeof(dev->me_clients_map)
794 < sizeof(enum_res->valid_addresses));
795 memcpy(dev->me_clients_map, enum_res->valid_addresses,
796 sizeof(enum_res->valid_addresses));
Tomas Winklerbb1b0132012-12-25 19:06:07 +0200797
Tomas Winkler544f9462014-01-08 20:19:21 +0200798 if (dev->dev_state != MEI_DEV_INIT_CLIENTS ||
799 dev->hbm_state != MEI_HBM_ENUM_CLIENTS) {
800 dev_err(&dev->pdev->dev, "hbm: enumeration response: state mismatch, [%d, %d]\n",
801 dev->dev_state, dev->hbm_state);
802 return -EPROTO;
Tomas Winklerbb1b0132012-12-25 19:06:07 +0200803 }
Tomas Winkler544f9462014-01-08 20:19:21 +0200804
805 if (mei_hbm_me_cl_allocate(dev)) {
806 dev_err(&dev->pdev->dev, "hbm: enumeration response: cannot allocate clients array\n");
807 return -ENOMEM;
808 }
809
810 dev->hbm_state = MEI_HBM_CLIENT_PROPERTIES;
811
812 /* first property request */
813 if (mei_hbm_prop_req(dev))
814 return -EIO;
815
Tomas Winklerbb1b0132012-12-25 19:06:07 +0200816 break;
817
818 case HOST_STOP_RES_CMD:
Tomas Winkler544f9462014-01-08 20:19:21 +0200819 dev_dbg(&dev->pdev->dev, "hbm: stop response: message received\n");
Tomas Winkler9b0d5ef2013-04-18 23:03:48 +0300820
Tomas Winkler544f9462014-01-08 20:19:21 +0200821 dev->init_clients_timer = 0;
822
823 if (dev->hbm_state != MEI_HBM_STOPPED) {
824 dev_err(&dev->pdev->dev, "hbm: stop response: state mismatch, [%d, %d]\n",
825 dev->dev_state, dev->hbm_state);
826 return -EPROTO;
827 }
828
Tomas Winkler33ec0822014-01-12 00:36:09 +0200829 dev->dev_state = MEI_DEV_POWER_DOWN;
Tomas Winkler544f9462014-01-08 20:19:21 +0200830 dev_info(&dev->pdev->dev, "hbm: stop response: resetting.\n");
831 /* force the reset */
832 return -EPROTO;
Tomas Winklerbb1b0132012-12-25 19:06:07 +0200833 break;
834
835 case CLIENT_DISCONNECT_REQ_CMD:
Tomas Winkler544f9462014-01-08 20:19:21 +0200836 dev_dbg(&dev->pdev->dev, "hbm: disconnect request: message received\n");
837
Tomas Winklerbb1b0132012-12-25 19:06:07 +0200838 disconnect_req = (struct hbm_client_connect_request *)mei_msg;
Tomas Winkler8120e722012-12-25 19:06:11 +0200839 mei_hbm_fw_disconnect_req(dev, disconnect_req);
Tomas Winklerbb1b0132012-12-25 19:06:07 +0200840 break;
841
842 case ME_STOP_REQ_CMD:
Tomas Winkler544f9462014-01-08 20:19:21 +0200843 dev_dbg(&dev->pdev->dev, "hbm: stop request: message received\n");
Tomas Winkler544f9462014-01-08 20:19:21 +0200844 dev->hbm_state = MEI_HBM_STOPPED;
Tomas Winkler6bb948c2014-02-12 21:41:52 +0200845 if (mei_hbm_stop_req(dev)) {
846 dev_err(&dev->pdev->dev, "hbm: start: failed to send stop request\n");
847 return -EIO;
848 }
Tomas Winklerbb1b0132012-12-25 19:06:07 +0200849 break;
Tomas Winklerbb1b0132012-12-25 19:06:07 +0200850 default:
851 BUG();
852 break;
853
854 }
Tomas Winkler544f9462014-01-08 20:19:21 +0200855 return 0;
Tomas Winklerbb1b0132012-12-25 19:06:07 +0200856}
857