blob: 3311b5c323af23b5575198727fa246c594a3e790 [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>
Tomas Winkler180ea052014-03-18 22:52:02 +020022#include <linux/pm_runtime.h>
Tomas Winklerbb1b0132012-12-25 19:06:07 +020023
24#include "mei_dev.h"
Tomas Winkler0edb23f2013-01-08 23:07:12 +020025#include "hbm.h"
Alexander Usyskin12d00662014-02-17 15:13:23 +020026#include "client.h"
Tomas Winklerbb1b0132012-12-25 19:06:07 +020027
Tomas Winkler89778d62014-08-21 14:29:16 +030028static const char *mei_hbm_status_str(enum mei_hbm_status status)
29{
30#define MEI_HBM_STATUS(status) case MEI_HBMS_##status: return #status
31 switch (status) {
32 MEI_HBM_STATUS(SUCCESS);
33 MEI_HBM_STATUS(CLIENT_NOT_FOUND);
34 MEI_HBM_STATUS(ALREADY_EXISTS);
35 MEI_HBM_STATUS(REJECTED);
36 MEI_HBM_STATUS(INVALID_PARAMETER);
37 MEI_HBM_STATUS(NOT_ALLOWED);
38 MEI_HBM_STATUS(ALREADY_STARTED);
39 MEI_HBM_STATUS(NOT_STARTED);
40 default: return "unknown";
41 }
42#undef MEI_HBM_STATUS
43};
44
Alexander Usyskin285e2992014-02-17 15:13:20 +020045static const char *mei_cl_conn_status_str(enum mei_cl_connect_status status)
46{
47#define MEI_CL_CS(status) case MEI_CL_CONN_##status: return #status
48 switch (status) {
49 MEI_CL_CS(SUCCESS);
50 MEI_CL_CS(NOT_FOUND);
51 MEI_CL_CS(ALREADY_STARTED);
52 MEI_CL_CS(OUT_OF_RESOURCES);
53 MEI_CL_CS(MESSAGE_SMALL);
54 default: return "unknown";
55 }
56#undef MEI_CL_CCS
57}
58
Alexander Usyskin1beeb4b2014-09-29 16:31:33 +030059const char *mei_hbm_state_str(enum mei_hbm_state state)
60{
61#define MEI_HBM_STATE(state) case MEI_HBM_##state: return #state
62 switch (state) {
63 MEI_HBM_STATE(IDLE);
64 MEI_HBM_STATE(STARTING);
65 MEI_HBM_STATE(STARTED);
66 MEI_HBM_STATE(ENUM_CLIENTS);
67 MEI_HBM_STATE(CLIENT_PROPERTIES);
68 MEI_HBM_STATE(STOPPED);
69 default:
70 return "unknown";
71 }
72#undef MEI_HBM_STATE
73}
74
Alexander Usyskin285e2992014-02-17 15:13:20 +020075/**
76 * mei_cl_conn_status_to_errno - convert client connect response
77 * status to error code
78 *
79 * @status: client connect response status
80 *
81 * returns corresponding error code
82 */
83static int mei_cl_conn_status_to_errno(enum mei_cl_connect_status status)
84{
85 switch (status) {
86 case MEI_CL_CONN_SUCCESS: return 0;
87 case MEI_CL_CONN_NOT_FOUND: return -ENOTTY;
88 case MEI_CL_CONN_ALREADY_STARTED: return -EBUSY;
89 case MEI_CL_CONN_OUT_OF_RESOURCES: return -EBUSY;
90 case MEI_CL_CONN_MESSAGE_SMALL: return -EINVAL;
91 default: return -EINVAL;
92 }
93}
94
Tomas Winklerbb1b0132012-12-25 19:06:07 +020095/**
Tomas Winkler84b32942014-05-07 16:51:28 +030096 * mei_hbm_idle - set hbm to idle state
97 *
98 * @dev: the device structure
99 */
100void mei_hbm_idle(struct mei_device *dev)
101{
102 dev->init_clients_timer = 0;
103 dev->hbm_state = MEI_HBM_IDLE;
104}
105
106/**
Tomas Winkler25ca6472014-08-21 14:29:14 +0300107 * mei_me_cl_remove_all - remove all me clients
108 *
109 * @dev: the device structure
110 */
111static void mei_me_cl_remove_all(struct mei_device *dev)
112{
113 struct mei_me_client *me_cl, *next;
114 list_for_each_entry_safe(me_cl, next, &dev->me_clients, list) {
115 list_del(&me_cl->list);
116 kfree(me_cl);
117 }
118}
119
120/**
Tomas Winkler84b32942014-05-07 16:51:28 +0300121 * mei_hbm_reset - reset hbm counters and book keeping data structurs
122 *
123 * @dev: the device structure
124 */
125void mei_hbm_reset(struct mei_device *dev)
126{
Tomas Winkler84b32942014-05-07 16:51:28 +0300127 dev->me_client_presentation_num = 0;
128 dev->me_client_index = 0;
129
Tomas Winkler25ca6472014-08-21 14:29:14 +0300130 mei_me_cl_remove_all(dev);
Tomas Winkler84b32942014-05-07 16:51:28 +0300131
132 mei_hbm_idle(dev);
133}
134
135/**
Tomas Winklercd51ed62012-12-25 19:06:09 +0200136 * mei_hbm_cl_hdr - construct client hbm header
Masanari Iida393b1482013-04-05 01:05:05 +0900137 *
Tomas Winkler68d1aa62014-08-21 14:29:11 +0300138 * @cl: client
Tomas Winklercd51ed62012-12-25 19:06:09 +0200139 * @hbm_cmd: host bus message command
140 * @buf: buffer for cl header
141 * @len: buffer length
142 */
143static inline
144void mei_hbm_cl_hdr(struct mei_cl *cl, u8 hbm_cmd, void *buf, size_t len)
145{
146 struct mei_hbm_cl_cmd *cmd = buf;
147
148 memset(cmd, 0, len);
149
150 cmd->hbm_cmd = hbm_cmd;
151 cmd->host_addr = cl->host_client_id;
152 cmd->me_addr = cl->me_client_id;
153}
154
155/**
Tomas Winkler68d1aa62014-08-21 14:29:11 +0300156 * mei_hbm_cl_write - write simple hbm client message
157 *
158 * @dev: the device structure
159 * @cl: client
160 * @hbm_cmd: host bus message command
161 * @len: buffer length
162 */
163static inline
164int mei_hbm_cl_write(struct mei_device *dev,
165 struct mei_cl *cl, u8 hbm_cmd, size_t len)
166{
167 struct mei_msg_hdr *mei_hdr = &dev->wr_msg.hdr;
168
169 mei_hbm_hdr(mei_hdr, len);
170 mei_hbm_cl_hdr(cl, hbm_cmd, dev->wr_msg.data, len);
171
172 return mei_write_message(dev, mei_hdr, dev->wr_msg.data);
173}
174
175/**
Tomas Winkler2af89db2014-08-21 14:29:20 +0300176 * mei_hbm_cl_addr_equal - check if the client's and
177 * the message address match
Tomas Winklercd51ed62012-12-25 19:06:09 +0200178 *
Tomas Winkler2af89db2014-08-21 14:29:20 +0300179 * @cl: client
180 * @cmd: hbm client message
Tomas Winklercd51ed62012-12-25 19:06:09 +0200181 *
Alexander Usyskin83ce0742014-01-08 22:31:46 +0200182 * returns true if addresses are the same
Tomas Winklercd51ed62012-12-25 19:06:09 +0200183 */
184static inline
Tomas Winkler2af89db2014-08-21 14:29:20 +0300185bool mei_hbm_cl_addr_equal(struct mei_cl *cl, struct mei_hbm_cl_cmd *cmd)
Tomas Winklercd51ed62012-12-25 19:06:09 +0200186{
Tomas Winklercd51ed62012-12-25 19:06:09 +0200187 return cl->host_client_id == cmd->host_addr &&
188 cl->me_client_id == cmd->me_addr;
189}
190
Alexander Usyskincb02efc2014-08-21 14:29:19 +0300191/**
Tomas Winkler2af89db2014-08-21 14:29:20 +0300192 * mei_hbm_cl_find_by_cmd - find recipient client
193 *
194 * @dev: the device structure
195 * @buf: a buffer with hbm cl command
196 *
197 * returns the recipient client or NULL if not found
198 */
199static inline
200struct mei_cl *mei_hbm_cl_find_by_cmd(struct mei_device *dev, void *buf)
201{
202 struct mei_hbm_cl_cmd *cmd = (struct mei_hbm_cl_cmd *)buf;
203 struct mei_cl *cl;
204
205 list_for_each_entry(cl, &dev->file_list, link)
206 if (mei_hbm_cl_addr_equal(cl, cmd))
207 return cl;
208 return NULL;
209}
210
211
212/**
Alexander Usyskincb02efc2014-08-21 14:29:19 +0300213 * mei_hbm_start_wait - wait for start response message.
214 *
215 * @dev: the device structure
216 *
217 * returns 0 on success and < 0 on failure
218 */
Tomas Winkler9b0d5ef2013-04-18 23:03:48 +0300219int mei_hbm_start_wait(struct mei_device *dev)
220{
221 int ret;
Alexander Usyskincb02efc2014-08-21 14:29:19 +0300222
223 if (dev->hbm_state > MEI_HBM_STARTING)
Tomas Winkler9b0d5ef2013-04-18 23:03:48 +0300224 return 0;
225
226 mutex_unlock(&dev->device_lock);
Alexander Usyskincb02efc2014-08-21 14:29:19 +0300227 ret = wait_event_timeout(dev->wait_hbm_start,
228 dev->hbm_state != MEI_HBM_STARTING,
Tomas Winkler7d93e582014-01-14 23:10:10 +0200229 mei_secs_to_jiffies(MEI_HBM_TIMEOUT));
Tomas Winkler9b0d5ef2013-04-18 23:03:48 +0300230 mutex_lock(&dev->device_lock);
231
Alexander Usyskincb02efc2014-08-21 14:29:19 +0300232 if (ret == 0 && (dev->hbm_state <= MEI_HBM_STARTING)) {
Tomas Winkler9b0d5ef2013-04-18 23:03:48 +0300233 dev->hbm_state = MEI_HBM_IDLE;
Masanari Iida8b513d02013-05-21 23:13:12 +0900234 dev_err(&dev->pdev->dev, "waiting for mei start failed\n");
Alexander Usyskin7ca96aa2014-02-19 17:35:49 +0200235 return -ETIME;
Tomas Winkler9b0d5ef2013-04-18 23:03:48 +0300236 }
237 return 0;
238}
239
Tomas Winkler6bbda152012-12-25 19:06:12 +0200240/**
Tomas Winkler8120e722012-12-25 19:06:11 +0200241 * mei_hbm_start_req - sends start request message.
Tomas Winklerbb1b0132012-12-25 19:06:07 +0200242 *
243 * @dev: the device structure
Tomas Winkler544f9462014-01-08 20:19:21 +0200244 *
245 * returns 0 on success and < 0 on failure
Tomas Winklerbb1b0132012-12-25 19:06:07 +0200246 */
Tomas Winkler9b0d5ef2013-04-18 23:03:48 +0300247int mei_hbm_start_req(struct mei_device *dev)
Tomas Winklerbb1b0132012-12-25 19:06:07 +0200248{
Tomas Winklere46f1872012-12-25 19:06:10 +0200249 struct mei_msg_hdr *mei_hdr = &dev->wr_msg.hdr;
Tomas Winklerbb1b0132012-12-25 19:06:07 +0200250 struct hbm_host_version_request *start_req;
251 const size_t len = sizeof(struct hbm_host_version_request);
Tomas Winkler544f9462014-01-08 20:19:21 +0200252 int ret;
Tomas Winklerbb1b0132012-12-25 19:06:07 +0200253
Tomas Winkler5ca2d382014-08-21 14:29:13 +0300254 mei_hbm_reset(dev);
255
Tomas Winklere46f1872012-12-25 19:06:10 +0200256 mei_hbm_hdr(mei_hdr, len);
Tomas Winklerbb1b0132012-12-25 19:06:07 +0200257
258 /* host start message */
Tomas Winklere46f1872012-12-25 19:06:10 +0200259 start_req = (struct hbm_host_version_request *)dev->wr_msg.data;
Tomas Winklerbb1b0132012-12-25 19:06:07 +0200260 memset(start_req, 0, len);
261 start_req->hbm_cmd = HOST_START_REQ_CMD;
262 start_req->host_version.major_version = HBM_MAJOR_VERSION;
263 start_req->host_version.minor_version = HBM_MINOR_VERSION;
264
Tomas Winkler9b0d5ef2013-04-18 23:03:48 +0300265 dev->hbm_state = MEI_HBM_IDLE;
Tomas Winkler544f9462014-01-08 20:19:21 +0200266 ret = mei_write_message(dev, mei_hdr, dev->wr_msg.data);
267 if (ret) {
268 dev_err(&dev->pdev->dev, "version message write failed: ret = %d\n",
269 ret);
270 return ret;
Tomas Winklerbb1b0132012-12-25 19:06:07 +0200271 }
Tomas Winkler544f9462014-01-08 20:19:21 +0200272
Alexander Usyskincb02efc2014-08-21 14:29:19 +0300273 dev->hbm_state = MEI_HBM_STARTING;
Tomas Winklerbb1b0132012-12-25 19:06:07 +0200274 dev->init_clients_timer = MEI_CLIENTS_INIT_TIMEOUT;
Tomas Winkler9b0d5ef2013-04-18 23:03:48 +0300275 return 0;
Tomas Winklerbb1b0132012-12-25 19:06:07 +0200276}
277
Tomas Winkler9b0d5ef2013-04-18 23:03:48 +0300278/*
Tomas Winkler8120e722012-12-25 19:06:11 +0200279 * mei_hbm_enum_clients_req - sends enumeration client request message.
Tomas Winklerbb1b0132012-12-25 19:06:07 +0200280 *
281 * @dev: the device structure
282 *
Tomas Winkler544f9462014-01-08 20:19:21 +0200283 * returns 0 on success and < 0 on failure
Tomas Winklerbb1b0132012-12-25 19:06:07 +0200284 */
Tomas Winkler544f9462014-01-08 20:19:21 +0200285static int mei_hbm_enum_clients_req(struct mei_device *dev)
Tomas Winklerbb1b0132012-12-25 19:06:07 +0200286{
Tomas Winklere46f1872012-12-25 19:06:10 +0200287 struct mei_msg_hdr *mei_hdr = &dev->wr_msg.hdr;
Tomas Winklerbb1b0132012-12-25 19:06:07 +0200288 struct hbm_host_enum_request *enum_req;
289 const size_t len = sizeof(struct hbm_host_enum_request);
Tomas Winkler544f9462014-01-08 20:19:21 +0200290 int ret;
291
Tomas Winklerbb1b0132012-12-25 19:06:07 +0200292 /* enumerate clients */
Tomas Winklere46f1872012-12-25 19:06:10 +0200293 mei_hbm_hdr(mei_hdr, len);
Tomas Winklerbb1b0132012-12-25 19:06:07 +0200294
Tomas Winklere46f1872012-12-25 19:06:10 +0200295 enum_req = (struct hbm_host_enum_request *)dev->wr_msg.data;
296 memset(enum_req, 0, len);
Tomas Winklerbb1b0132012-12-25 19:06:07 +0200297 enum_req->hbm_cmd = HOST_ENUM_REQ_CMD;
298
Tomas Winkler544f9462014-01-08 20:19:21 +0200299 ret = mei_write_message(dev, mei_hdr, dev->wr_msg.data);
300 if (ret) {
301 dev_err(&dev->pdev->dev, "enumeration request write failed: ret = %d.\n",
302 ret);
303 return ret;
Tomas Winklerbb1b0132012-12-25 19:06:07 +0200304 }
Tomas Winkler9b0d5ef2013-04-18 23:03:48 +0300305 dev->hbm_state = MEI_HBM_ENUM_CLIENTS;
Tomas Winklerbb1b0132012-12-25 19:06:07 +0200306 dev->init_clients_timer = MEI_CLIENTS_INIT_TIMEOUT;
Tomas Winkler544f9462014-01-08 20:19:21 +0200307 return 0;
Tomas Winklerbb1b0132012-12-25 19:06:07 +0200308}
309
Tomas Winkler5ca2d382014-08-21 14:29:13 +0300310/*
311 * mei_hbm_me_cl_add - add new me client to the list
312 *
313 * @dev: the device structure
314 * @res: hbm property response
315 *
316 * returns 0 on success and -ENOMEM on allocation failure
317 */
318
319static int mei_hbm_me_cl_add(struct mei_device *dev,
320 struct hbm_props_response *res)
321{
322 struct mei_me_client *me_cl;
323
324 me_cl = kzalloc(sizeof(struct mei_me_client), GFP_KERNEL);
325 if (!me_cl)
326 return -ENOMEM;
327
328 me_cl->props = res->client_properties;
329 me_cl->client_id = res->me_addr;
330 me_cl->mei_flow_ctrl_creds = 0;
331
332 list_add(&me_cl->list, &dev->me_clients);
333 return 0;
334}
335
Tomas Winkler8120e722012-12-25 19:06:11 +0200336/**
Masanari Iida393b1482013-04-05 01:05:05 +0900337 * mei_hbm_prop_req - request property for a single client
Tomas Winkler8120e722012-12-25 19:06:11 +0200338 *
339 * @dev: the device structure
340 *
Tomas Winkler544f9462014-01-08 20:19:21 +0200341 * returns 0 on success and < 0 on failure
Tomas Winkler8120e722012-12-25 19:06:11 +0200342 */
Tomas Winklerbb1b0132012-12-25 19:06:07 +0200343
Tomas Winkler8120e722012-12-25 19:06:11 +0200344static int mei_hbm_prop_req(struct mei_device *dev)
Tomas Winklerbb1b0132012-12-25 19:06:07 +0200345{
346
Tomas Winklere46f1872012-12-25 19:06:10 +0200347 struct mei_msg_hdr *mei_hdr = &dev->wr_msg.hdr;
Tomas Winklerbb1b0132012-12-25 19:06:07 +0200348 struct hbm_props_request *prop_req;
349 const size_t len = sizeof(struct hbm_props_request);
350 unsigned long next_client_index;
Tomas Winkler544f9462014-01-08 20:19:21 +0200351 int ret;
Tomas Winklerbb1b0132012-12-25 19:06:07 +0200352
Tomas Winklerbb1b0132012-12-25 19:06:07 +0200353 next_client_index = find_next_bit(dev->me_clients_map, MEI_CLIENTS_MAX,
354 dev->me_client_index);
355
356 /* We got all client properties */
357 if (next_client_index == MEI_CLIENTS_MAX) {
Tomas Winkler9b0d5ef2013-04-18 23:03:48 +0300358 dev->hbm_state = MEI_HBM_STARTED;
Tomas Winklerbb1b0132012-12-25 19:06:07 +0200359 schedule_work(&dev->init_work);
360
361 return 0;
362 }
363
Tomas Winklere46f1872012-12-25 19:06:10 +0200364 mei_hbm_hdr(mei_hdr, len);
365 prop_req = (struct hbm_props_request *)dev->wr_msg.data;
Tomas Winklerbb1b0132012-12-25 19:06:07 +0200366
367 memset(prop_req, 0, sizeof(struct hbm_props_request));
368
Tomas Winklerbb1b0132012-12-25 19:06:07 +0200369 prop_req->hbm_cmd = HOST_CLIENT_PROPERTIES_REQ_CMD;
Tomas Winkler3438c1f2014-08-21 14:29:10 +0300370 prop_req->me_addr = next_client_index;
Tomas Winklerbb1b0132012-12-25 19:06:07 +0200371
Tomas Winkler544f9462014-01-08 20:19:21 +0200372 ret = mei_write_message(dev, mei_hdr, dev->wr_msg.data);
373 if (ret) {
374 dev_err(&dev->pdev->dev, "properties request write failed: ret = %d\n",
375 ret);
376 return ret;
Tomas Winklerbb1b0132012-12-25 19:06:07 +0200377 }
378
379 dev->init_clients_timer = MEI_CLIENTS_INIT_TIMEOUT;
380 dev->me_client_index = next_client_index;
381
382 return 0;
383}
384
Tomas Winkler4fcbc992014-03-18 22:51:55 +0200385/*
386 * mei_hbm_pg - sends pg command
387 *
388 * @dev: the device structure
389 * @pg_cmd: the pg command code
390 *
Tomas Winklerbae1cc72014-08-21 14:29:21 +0300391 * returns -EIO on write failure
392 * -EOPNOTSUPP if the operation is not supported by the protocol
Tomas Winkler4fcbc992014-03-18 22:51:55 +0200393 */
394int mei_hbm_pg(struct mei_device *dev, u8 pg_cmd)
395{
396 struct mei_msg_hdr *mei_hdr = &dev->wr_msg.hdr;
397 struct hbm_power_gate *req;
398 const size_t len = sizeof(struct hbm_power_gate);
399 int ret;
400
Tomas Winklerbae1cc72014-08-21 14:29:21 +0300401 if (!dev->hbm_f_pg_supported)
402 return -EOPNOTSUPP;
403
Tomas Winkler4fcbc992014-03-18 22:51:55 +0200404 mei_hbm_hdr(mei_hdr, len);
405
406 req = (struct hbm_power_gate *)dev->wr_msg.data;
407 memset(req, 0, len);
408 req->hbm_cmd = pg_cmd;
409
410 ret = mei_write_message(dev, mei_hdr, dev->wr_msg.data);
411 if (ret)
412 dev_err(&dev->pdev->dev, "power gate command write failed.\n");
413 return ret;
414}
415EXPORT_SYMBOL_GPL(mei_hbm_pg);
416
Tomas Winklerbb1b0132012-12-25 19:06:07 +0200417/**
Tomas Winkler6bb948c2014-02-12 21:41:52 +0200418 * mei_hbm_stop_req - send stop request message
Tomas Winklere46f1872012-12-25 19:06:10 +0200419 *
420 * @dev - mei device
Tomas Winkler6bb948c2014-02-12 21:41:52 +0200421 * @cl: client info
422 *
423 * This function returns -EIO on write failure
Tomas Winklere46f1872012-12-25 19:06:10 +0200424 */
Tomas Winkler6bb948c2014-02-12 21:41:52 +0200425static int mei_hbm_stop_req(struct mei_device *dev)
Tomas Winklere46f1872012-12-25 19:06:10 +0200426{
Tomas Winkler6bb948c2014-02-12 21:41:52 +0200427 struct mei_msg_hdr *mei_hdr = &dev->wr_msg.hdr;
Tomas Winklere46f1872012-12-25 19:06:10 +0200428 struct hbm_host_stop_request *req =
Tomas Winkler6bb948c2014-02-12 21:41:52 +0200429 (struct hbm_host_stop_request *)dev->wr_msg.data;
Tomas Winklere46f1872012-12-25 19:06:10 +0200430 const size_t len = sizeof(struct hbm_host_stop_request);
431
432 mei_hbm_hdr(mei_hdr, len);
433
434 memset(req, 0, len);
435 req->hbm_cmd = HOST_STOP_REQ_CMD;
436 req->reason = DRIVER_STOP_REQUEST;
Tomas Winkler6bb948c2014-02-12 21:41:52 +0200437
438 return mei_write_message(dev, mei_hdr, dev->wr_msg.data);
Tomas Winklere46f1872012-12-25 19:06:10 +0200439}
440
441/**
Alexander Usyskin83ce0742014-01-08 22:31:46 +0200442 * mei_hbm_cl_flow_control_req - sends flow control request.
Tomas Winklerbb1b0132012-12-25 19:06:07 +0200443 *
444 * @dev: the device structure
Tomas Winkler8120e722012-12-25 19:06:11 +0200445 * @cl: client info
Tomas Winklerbb1b0132012-12-25 19:06:07 +0200446 *
447 * This function returns -EIO on write failure
448 */
Tomas Winkler8120e722012-12-25 19:06:11 +0200449int mei_hbm_cl_flow_control_req(struct mei_device *dev, struct mei_cl *cl)
Tomas Winklerbb1b0132012-12-25 19:06:07 +0200450{
Tomas Winklerbb1b0132012-12-25 19:06:07 +0200451 const size_t len = sizeof(struct hbm_flow_control);
Tomas Winkler46922182014-03-16 14:35:55 +0200452 cl_dbg(dev, cl, "sending flow control\n");
Tomas Winkler68d1aa62014-08-21 14:29:11 +0300453 return mei_hbm_cl_write(dev, cl, MEI_FLOW_CONTROL_CMD, len);
Tomas Winklerbb1b0132012-12-25 19:06:07 +0200454}
455
456/**
Masanari Iida393b1482013-04-05 01:05:05 +0900457 * mei_hbm_add_single_flow_creds - adds single buffer credentials.
Tomas Winkler6bbda152012-12-25 19:06:12 +0200458 *
Masanari Iida393b1482013-04-05 01:05:05 +0900459 * @dev: the device structure
Tomas Winkler6bbda152012-12-25 19:06:12 +0200460 * @flow: flow control.
Alexander Usyskin12d00662014-02-17 15:13:23 +0200461 *
462 * return 0 on success, < 0 otherwise
Tomas Winkler6bbda152012-12-25 19:06:12 +0200463 */
Alexander Usyskin12d00662014-02-17 15:13:23 +0200464static int mei_hbm_add_single_flow_creds(struct mei_device *dev,
Tomas Winkler6bbda152012-12-25 19:06:12 +0200465 struct hbm_flow_control *flow)
466{
Alexander Usyskin12d00662014-02-17 15:13:23 +0200467 struct mei_me_client *me_cl;
Tomas Winkler6bbda152012-12-25 19:06:12 +0200468
Tomas Winklerd3208322014-08-24 12:08:55 +0300469 me_cl = mei_me_cl_by_id(dev, flow->me_addr);
470 if (!me_cl) {
Alexander Usyskin12d00662014-02-17 15:13:23 +0200471 dev_err(&dev->pdev->dev, "no such me client %d\n",
472 flow->me_addr);
Tomas Winklerd3208322014-08-24 12:08:55 +0300473 return -ENOENT;
Tomas Winkler6bbda152012-12-25 19:06:12 +0200474 }
Alexander Usyskin12d00662014-02-17 15:13:23 +0200475
Tomas Winklerd3208322014-08-24 12:08:55 +0300476 if (WARN_ON(me_cl->props.single_recv_buf == 0))
477 return -EINVAL;
478
479 me_cl->mei_flow_ctrl_creds++;
480 dev_dbg(&dev->pdev->dev, "recv flow ctrl msg ME %d (single) creds = %d.\n",
481 flow->me_addr, me_cl->mei_flow_ctrl_creds);
Alexander Usyskin12d00662014-02-17 15:13:23 +0200482
483 return 0;
Tomas Winkler6bbda152012-12-25 19:06:12 +0200484}
485
486/**
487 * mei_hbm_cl_flow_control_res - flow control response from me
488 *
489 * @dev: the device structure
490 * @flow_control: flow control response bus message
491 */
492static void mei_hbm_cl_flow_control_res(struct mei_device *dev,
Tomas Winkler2af89db2014-08-21 14:29:20 +0300493 struct hbm_flow_control *flow_control)
Tomas Winkler6bbda152012-12-25 19:06:12 +0200494{
Tomas Winkler31f88f52014-02-17 15:13:25 +0200495 struct mei_cl *cl;
Tomas Winkler6bbda152012-12-25 19:06:12 +0200496
497 if (!flow_control->host_addr) {
498 /* single receive buffer */
499 mei_hbm_add_single_flow_creds(dev, flow_control);
500 return;
501 }
502
Tomas Winkler2af89db2014-08-21 14:29:20 +0300503 cl = mei_hbm_cl_find_by_cmd(dev, flow_control);
504 if (cl) {
505 cl->mei_flow_ctrl_creds++;
506 cl_dbg(dev, cl, "flow control creds = %d.\n",
Tomas Winkler5ca2d382014-08-21 14:29:13 +0300507 cl->mei_flow_ctrl_creds);
Tomas Winkler6bbda152012-12-25 19:06:12 +0200508 }
509}
510
511
512/**
Tomas Winkler8120e722012-12-25 19:06:11 +0200513 * mei_hbm_cl_disconnect_req - sends disconnect message to fw.
Tomas Winklerbb1b0132012-12-25 19:06:07 +0200514 *
515 * @dev: the device structure
Tomas Winkler8120e722012-12-25 19:06:11 +0200516 * @cl: a client to disconnect from
Tomas Winklerbb1b0132012-12-25 19:06:07 +0200517 *
518 * This function returns -EIO on write failure
519 */
Tomas Winkler8120e722012-12-25 19:06:11 +0200520int mei_hbm_cl_disconnect_req(struct mei_device *dev, struct mei_cl *cl)
Tomas Winklerbb1b0132012-12-25 19:06:07 +0200521{
Tomas Winklerbb1b0132012-12-25 19:06:07 +0200522 const size_t len = sizeof(struct hbm_client_connect_request);
Tomas Winkler68d1aa62014-08-21 14:29:11 +0300523 return mei_hbm_cl_write(dev, cl, CLIENT_DISCONNECT_REQ_CMD, len);
Tomas Winklerbb1b0132012-12-25 19:06:07 +0200524}
525
526/**
Tomas Winkler6bb948c2014-02-12 21:41:52 +0200527 * mei_hbm_cl_disconnect_rsp - sends disconnect respose to the FW
528 *
529 * @dev: the device structure
530 * @cl: a client to disconnect from
531 *
532 * This function returns -EIO on write failure
533 */
534int mei_hbm_cl_disconnect_rsp(struct mei_device *dev, struct mei_cl *cl)
535{
Tomas Winkler6bb948c2014-02-12 21:41:52 +0200536 const size_t len = sizeof(struct hbm_client_connect_response);
Tomas Winkler68d1aa62014-08-21 14:29:11 +0300537 return mei_hbm_cl_write(dev, cl, CLIENT_DISCONNECT_RES_CMD, len);
Tomas Winkler6bb948c2014-02-12 21:41:52 +0200538}
539
540/**
Tomas Winkler12f45ed2014-08-21 14:29:18 +0300541 * mei_hbm_cl_disconnect_res - update the client state according
542 * disconnect response
Tomas Winkler6bbda152012-12-25 19:06:12 +0200543 *
Tomas Winkler12f45ed2014-08-21 14:29:18 +0300544 * @cl: mei host client
545 * @cmd: disconnect client response host bus message
Tomas Winkler6bbda152012-12-25 19:06:12 +0200546 */
Tomas Winkler12f45ed2014-08-21 14:29:18 +0300547static void mei_hbm_cl_disconnect_res(struct mei_cl *cl,
548 struct mei_hbm_cl_cmd *cmd)
Tomas Winkler6bbda152012-12-25 19:06:12 +0200549{
Tomas Winkler12f45ed2014-08-21 14:29:18 +0300550 struct hbm_client_connect_response *rs =
551 (struct hbm_client_connect_response *)cmd;
Tomas Winkler6bbda152012-12-25 19:06:12 +0200552
Tomas Winkler12f45ed2014-08-21 14:29:18 +0300553 dev_dbg(&cl->dev->pdev->dev, "hbm: disconnect response cl:host=%02d me=%02d status=%d\n",
Alexander Usyskin285e2992014-02-17 15:13:20 +0200554 rs->me_addr, rs->host_addr, rs->status);
Tomas Winkler6bbda152012-12-25 19:06:12 +0200555
Tomas Winkler12f45ed2014-08-21 14:29:18 +0300556 if (rs->status == MEI_CL_DISCONN_SUCCESS)
557 cl->state = MEI_FILE_DISCONNECTED;
558 cl->status = 0;
Tomas Winkler6bbda152012-12-25 19:06:12 +0200559}
560
561/**
Tomas Winkler8120e722012-12-25 19:06:11 +0200562 * mei_hbm_cl_connect_req - send connection request to specific me client
Tomas Winklerbb1b0132012-12-25 19:06:07 +0200563 *
564 * @dev: the device structure
Tomas Winkler8120e722012-12-25 19:06:11 +0200565 * @cl: a client to connect to
Tomas Winklerbb1b0132012-12-25 19:06:07 +0200566 *
Tomas Winkler8120e722012-12-25 19:06:11 +0200567 * returns -EIO on write failure
Tomas Winklerbb1b0132012-12-25 19:06:07 +0200568 */
Tomas Winkler8120e722012-12-25 19:06:11 +0200569int mei_hbm_cl_connect_req(struct mei_device *dev, struct mei_cl *cl)
Tomas Winklerbb1b0132012-12-25 19:06:07 +0200570{
Tomas Winklerbb1b0132012-12-25 19:06:07 +0200571 const size_t len = sizeof(struct hbm_client_connect_request);
Tomas Winkler68d1aa62014-08-21 14:29:11 +0300572 return mei_hbm_cl_write(dev, cl, CLIENT_CONNECT_REQ_CMD, len);
Tomas Winklerbb1b0132012-12-25 19:06:07 +0200573}
574
575/**
Tomas Winkler12f45ed2014-08-21 14:29:18 +0300576 * mei_hbm_cl_connect_res - update the client state according
577 * connection response
Tomas Winkler6bbda152012-12-25 19:06:12 +0200578 *
Tomas Winkler12f45ed2014-08-21 14:29:18 +0300579 * @cl: mei host client
580 * @cmd: connect client response host bus message
Tomas Winkler6bbda152012-12-25 19:06:12 +0200581 */
Tomas Winkler12f45ed2014-08-21 14:29:18 +0300582static void mei_hbm_cl_connect_res(struct mei_cl *cl,
583 struct mei_hbm_cl_cmd *cmd)
Tomas Winkler6bbda152012-12-25 19:06:12 +0200584{
Tomas Winkler12f45ed2014-08-21 14:29:18 +0300585 struct hbm_client_connect_response *rs =
586 (struct hbm_client_connect_response *)cmd;
Tomas Winkler6bbda152012-12-25 19:06:12 +0200587
Tomas Winkler12f45ed2014-08-21 14:29:18 +0300588 dev_dbg(&cl->dev->pdev->dev, "hbm: connect response cl:host=%02d me=%02d status=%s\n",
Alexander Usyskin285e2992014-02-17 15:13:20 +0200589 rs->me_addr, rs->host_addr,
590 mei_cl_conn_status_str(rs->status));
Tomas Winkler6bbda152012-12-25 19:06:12 +0200591
Tomas Winkler12f45ed2014-08-21 14:29:18 +0300592 if (rs->status == MEI_CL_CONN_SUCCESS)
593 cl->state = MEI_FILE_CONNECTED;
594 else
595 cl->state = MEI_FILE_DISCONNECTED;
596 cl->status = mei_cl_conn_status_to_errno(rs->status);
597}
Tomas Winkler6bbda152012-12-25 19:06:12 +0200598
Tomas Winkler12f45ed2014-08-21 14:29:18 +0300599/**
600 * mei_hbm_cl_res - process hbm response received on behalf
601 * an client
602 *
603 * @dev: the device structure
604 * @rs: hbm client message
605 * @fop_type: file operation type
606 */
607static void mei_hbm_cl_res(struct mei_device *dev,
608 struct mei_hbm_cl_cmd *rs,
609 enum mei_cb_file_ops fop_type)
610{
611 struct mei_cl *cl;
612 struct mei_cl_cb *cb, *next;
613
614 cl = NULL;
Tomas Winkler64092852014-02-17 15:13:21 +0200615 list_for_each_entry_safe(cb, next, &dev->ctrl_rd_list.list, list) {
Tomas Winkler6bbda152012-12-25 19:06:12 +0200616
Tomas Winkler64092852014-02-17 15:13:21 +0200617 cl = cb->cl;
618 /* this should not happen */
619 if (WARN_ON(!cl)) {
620 list_del_init(&cb->list);
621 continue;
Tomas Winkler6bbda152012-12-25 19:06:12 +0200622 }
Tomas Winkler64092852014-02-17 15:13:21 +0200623
Tomas Winkler12f45ed2014-08-21 14:29:18 +0300624 if (cb->fop_type != fop_type)
Tomas Winkler64092852014-02-17 15:13:21 +0200625 continue;
626
627 if (mei_hbm_cl_addr_equal(cl, rs)) {
628 list_del(&cb->list);
629 break;
Tomas Winkler6bbda152012-12-25 19:06:12 +0200630 }
631 }
Tomas Winkler64092852014-02-17 15:13:21 +0200632
633 if (!cl)
634 return;
635
Tomas Winkler12f45ed2014-08-21 14:29:18 +0300636 switch (fop_type) {
637 case MEI_FOP_CONNECT:
638 mei_hbm_cl_connect_res(cl, rs);
639 break;
640 case MEI_FOP_DISCONNECT:
641 mei_hbm_cl_disconnect_res(cl, rs);
642 break;
643 default:
644 return;
645 }
646
Tomas Winkler64092852014-02-17 15:13:21 +0200647 cl->timer_count = 0;
Tomas Winkler12f45ed2014-08-21 14:29:18 +0300648 wake_up(&cl->wait);
Tomas Winkler6bbda152012-12-25 19:06:12 +0200649}
650
651
652/**
Alexander Usyskin83ce0742014-01-08 22:31:46 +0200653 * mei_hbm_fw_disconnect_req - disconnect request initiated by ME firmware
654 * host sends disconnect response
Tomas Winklerbb1b0132012-12-25 19:06:07 +0200655 *
656 * @dev: the device structure.
Tomas Winkler8120e722012-12-25 19:06:11 +0200657 * @disconnect_req: disconnect request bus message from the me
Tomas Winkler6bb948c2014-02-12 21:41:52 +0200658 *
659 * returns -ENOMEM on allocation failure
Tomas Winklerbb1b0132012-12-25 19:06:07 +0200660 */
Tomas Winkler6bb948c2014-02-12 21:41:52 +0200661static int mei_hbm_fw_disconnect_req(struct mei_device *dev,
Tomas Winklerbb1b0132012-12-25 19:06:07 +0200662 struct hbm_client_connect_request *disconnect_req)
663{
Tomas Winkler31f88f52014-02-17 15:13:25 +0200664 struct mei_cl *cl;
Tomas Winkler6bb948c2014-02-12 21:41:52 +0200665 struct mei_cl_cb *cb;
Tomas Winklerbb1b0132012-12-25 19:06:07 +0200666
Tomas Winkler2af89db2014-08-21 14:29:20 +0300667 cl = mei_hbm_cl_find_by_cmd(dev, disconnect_req);
668 if (cl) {
669 cl_dbg(dev, cl, "disconnect request received\n");
670 cl->state = MEI_FILE_DISCONNECTED;
671 cl->timer_count = 0;
Tomas Winklerbb1b0132012-12-25 19:06:07 +0200672
Tomas Winkler2af89db2014-08-21 14:29:20 +0300673 cb = mei_io_cb_init(cl, NULL);
674 if (!cb)
675 return -ENOMEM;
676 cb->fop_type = MEI_FOP_DISCONNECT_RSP;
677 cl_dbg(dev, cl, "add disconnect response as first\n");
678 list_add(&cb->list, &dev->ctrl_wr_list.list);
Tomas Winklerbb1b0132012-12-25 19:06:07 +0200679 }
Tomas Winkler6bb948c2014-02-12 21:41:52 +0200680 return 0;
Tomas Winklerbb1b0132012-12-25 19:06:07 +0200681}
682
Tomas Winklerbae1cc72014-08-21 14:29:21 +0300683/**
684 * mei_hbm_config_features: check what hbm features and commands
685 * are supported by the fw
686 *
687 * @dev: the device structure
688 */
689static void mei_hbm_config_features(struct mei_device *dev)
690{
691 /* Power Gating Isolation Support */
692 dev->hbm_f_pg_supported = 0;
693 if (dev->version.major_version > HBM_MAJOR_VERSION_PGI)
694 dev->hbm_f_pg_supported = 1;
695
696 if (dev->version.major_version == HBM_MAJOR_VERSION_PGI &&
697 dev->version.minor_version >= HBM_MINOR_VERSION_PGI)
698 dev->hbm_f_pg_supported = 1;
699}
Tomas Winklerbb1b0132012-12-25 19:06:07 +0200700
701/**
Tomas Winkler2c9b48a2013-06-16 09:16:31 +0300702 * mei_hbm_version_is_supported - checks whether the driver can
703 * support the hbm version of the device
704 *
705 * @dev: the device structure
706 * returns true if driver can support hbm version of the device
707 */
708bool mei_hbm_version_is_supported(struct mei_device *dev)
709{
710 return (dev->version.major_version < HBM_MAJOR_VERSION) ||
711 (dev->version.major_version == HBM_MAJOR_VERSION &&
712 dev->version.minor_version <= HBM_MINOR_VERSION);
713}
714
715/**
Tomas Winklerbb1b0132012-12-25 19:06:07 +0200716 * mei_hbm_dispatch - bottom half read routine after ISR to
717 * handle the read bus message cmd processing.
718 *
719 * @dev: the device structure
720 * @mei_hdr: header of bus message
Tomas Winkler544f9462014-01-08 20:19:21 +0200721 *
722 * returns 0 on success and < 0 on failure
Tomas Winklerbb1b0132012-12-25 19:06:07 +0200723 */
Tomas Winkler544f9462014-01-08 20:19:21 +0200724int mei_hbm_dispatch(struct mei_device *dev, struct mei_msg_hdr *hdr)
Tomas Winklerbb1b0132012-12-25 19:06:07 +0200725{
726 struct mei_bus_message *mei_msg;
Tomas Winklerbb1b0132012-12-25 19:06:07 +0200727 struct hbm_host_version_response *version_res;
Tomas Winklerbb1b0132012-12-25 19:06:07 +0200728 struct hbm_props_response *props_res;
729 struct hbm_host_enum_response *enum_res;
Tomas Winklerbb1b0132012-12-25 19:06:07 +0200730
Tomas Winkler12f45ed2014-08-21 14:29:18 +0300731 struct mei_hbm_cl_cmd *cl_cmd;
732 struct hbm_client_connect_request *disconnect_req;
733 struct hbm_flow_control *flow_control;
734
Tomas Winklerbb1b0132012-12-25 19:06:07 +0200735 /* read the message to our buffer */
736 BUG_ON(hdr->length >= sizeof(dev->rd_msg_buf));
737 mei_read_slots(dev, dev->rd_msg_buf, hdr->length);
738 mei_msg = (struct mei_bus_message *)dev->rd_msg_buf;
Tomas Winkler12f45ed2014-08-21 14:29:18 +0300739 cl_cmd = (struct mei_hbm_cl_cmd *)mei_msg;
Tomas Winklerbb1b0132012-12-25 19:06:07 +0200740
Tomas Winkler66ae4602014-01-08 20:19:22 +0200741 /* ignore spurious message and prevent reset nesting
742 * hbm is put to idle during system reset
743 */
744 if (dev->hbm_state == MEI_HBM_IDLE) {
745 dev_dbg(&dev->pdev->dev, "hbm: state is idle ignore spurious messages\n");
746 return 0;
747 }
748
Tomas Winklerbb1b0132012-12-25 19:06:07 +0200749 switch (mei_msg->hbm_cmd) {
750 case HOST_START_RES_CMD:
Tomas Winkler544f9462014-01-08 20:19:21 +0200751 dev_dbg(&dev->pdev->dev, "hbm: start: response message received.\n");
752
753 dev->init_clients_timer = 0;
754
Tomas Winklerbb1b0132012-12-25 19:06:07 +0200755 version_res = (struct hbm_host_version_response *)mei_msg;
Tomas Winkler2c9b48a2013-06-16 09:16:31 +0300756
757 dev_dbg(&dev->pdev->dev, "HBM VERSION: DRIVER=%02d:%02d DEVICE=%02d:%02d\n",
758 HBM_MAJOR_VERSION, HBM_MINOR_VERSION,
759 version_res->me_max_version.major_version,
760 version_res->me_max_version.minor_version);
761
762 if (version_res->host_version_supported) {
763 dev->version.major_version = HBM_MAJOR_VERSION;
764 dev->version.minor_version = HBM_MINOR_VERSION;
765 } else {
766 dev->version.major_version =
767 version_res->me_max_version.major_version;
768 dev->version.minor_version =
769 version_res->me_max_version.minor_version;
770 }
771
772 if (!mei_hbm_version_is_supported(dev)) {
Tomas Winkler544f9462014-01-08 20:19:21 +0200773 dev_warn(&dev->pdev->dev, "hbm: start: version mismatch - stopping the driver.\n");
Tomas Winklere46f1872012-12-25 19:06:10 +0200774
Tomas Winkler544f9462014-01-08 20:19:21 +0200775 dev->hbm_state = MEI_HBM_STOPPED;
Tomas Winkler6bb948c2014-02-12 21:41:52 +0200776 if (mei_hbm_stop_req(dev)) {
Tomas Winkler544f9462014-01-08 20:19:21 +0200777 dev_err(&dev->pdev->dev, "hbm: start: failed to send stop request\n");
778 return -EIO;
779 }
780 break;
Tomas Winklere46f1872012-12-25 19:06:10 +0200781 }
782
Tomas Winklerbae1cc72014-08-21 14:29:21 +0300783 mei_hbm_config_features(dev);
784
Tomas Winkler544f9462014-01-08 20:19:21 +0200785 if (dev->dev_state != MEI_DEV_INIT_CLIENTS ||
Alexander Usyskincb02efc2014-08-21 14:29:19 +0300786 dev->hbm_state != MEI_HBM_STARTING) {
Tomas Winkler544f9462014-01-08 20:19:21 +0200787 dev_err(&dev->pdev->dev, "hbm: start: state mismatch, [%d, %d]\n",
788 dev->dev_state, dev->hbm_state);
789 return -EPROTO;
790 }
791
792 dev->hbm_state = MEI_HBM_STARTED;
793
794 if (mei_hbm_enum_clients_req(dev)) {
795 dev_err(&dev->pdev->dev, "hbm: start: failed to send enumeration request\n");
796 return -EIO;
Tomas Winklerbb1b0132012-12-25 19:06:07 +0200797 }
798
Alexander Usyskincb02efc2014-08-21 14:29:19 +0300799 wake_up(&dev->wait_hbm_start);
Tomas Winklerbb1b0132012-12-25 19:06:07 +0200800 break;
801
802 case CLIENT_CONNECT_RES_CMD:
Tomas Winkler544f9462014-01-08 20:19:21 +0200803 dev_dbg(&dev->pdev->dev, "hbm: client connect response: message received.\n");
Tomas Winkler12f45ed2014-08-21 14:29:18 +0300804 mei_hbm_cl_res(dev, cl_cmd, MEI_FOP_CONNECT);
Tomas Winklerbb1b0132012-12-25 19:06:07 +0200805 break;
806
807 case CLIENT_DISCONNECT_RES_CMD:
Tomas Winkler544f9462014-01-08 20:19:21 +0200808 dev_dbg(&dev->pdev->dev, "hbm: client disconnect response: message received.\n");
Tomas Winkler12f45ed2014-08-21 14:29:18 +0300809 mei_hbm_cl_res(dev, cl_cmd, MEI_FOP_DISCONNECT);
Tomas Winklerbb1b0132012-12-25 19:06:07 +0200810 break;
811
812 case MEI_FLOW_CONTROL_CMD:
Tomas Winkler544f9462014-01-08 20:19:21 +0200813 dev_dbg(&dev->pdev->dev, "hbm: client flow control response: message received.\n");
814
Tomas Winklerbb1b0132012-12-25 19:06:07 +0200815 flow_control = (struct hbm_flow_control *) mei_msg;
Tomas Winkler6bbda152012-12-25 19:06:12 +0200816 mei_hbm_cl_flow_control_res(dev, flow_control);
Tomas Winklerbb1b0132012-12-25 19:06:07 +0200817 break;
818
Tomas Winkler4fcbc992014-03-18 22:51:55 +0200819 case MEI_PG_ISOLATION_ENTRY_RES_CMD:
820 dev_dbg(&dev->pdev->dev, "power gate isolation entry response received\n");
Tomas Winklerba9cdd02014-03-18 22:52:00 +0200821 dev->pg_event = MEI_PG_EVENT_RECEIVED;
Tomas Winkler4fcbc992014-03-18 22:51:55 +0200822 if (waitqueue_active(&dev->wait_pg))
823 wake_up(&dev->wait_pg);
824 break;
825
826 case MEI_PG_ISOLATION_EXIT_REQ_CMD:
827 dev_dbg(&dev->pdev->dev, "power gate isolation exit request received\n");
Tomas Winklerba9cdd02014-03-18 22:52:00 +0200828 dev->pg_event = MEI_PG_EVENT_RECEIVED;
Tomas Winkler4fcbc992014-03-18 22:51:55 +0200829 if (waitqueue_active(&dev->wait_pg))
830 wake_up(&dev->wait_pg);
Tomas Winkler180ea052014-03-18 22:52:02 +0200831 else
832 /*
833 * If the driver is not waiting on this then
834 * this is HW initiated exit from PG.
835 * Start runtime pm resume sequence to exit from PG.
836 */
837 pm_request_resume(&dev->pdev->dev);
Tomas Winkler4fcbc992014-03-18 22:51:55 +0200838 break;
839
Tomas Winklerbb1b0132012-12-25 19:06:07 +0200840 case HOST_CLIENT_PROPERTIES_RES_CMD:
Tomas Winkler544f9462014-01-08 20:19:21 +0200841 dev_dbg(&dev->pdev->dev, "hbm: properties response: message received.\n");
842
843 dev->init_clients_timer = 0;
844
Tomas Winklerbb1b0132012-12-25 19:06:07 +0200845 if (dev->dev_state != MEI_DEV_INIT_CLIENTS ||
Tomas Winkler9b0d5ef2013-04-18 23:03:48 +0300846 dev->hbm_state != MEI_HBM_CLIENT_PROPERTIES) {
Tomas Winkler544f9462014-01-08 20:19:21 +0200847 dev_err(&dev->pdev->dev, "hbm: properties response: state mismatch, [%d, %d]\n",
848 dev->dev_state, dev->hbm_state);
849 return -EPROTO;
Tomas Winklerbb1b0132012-12-25 19:06:07 +0200850 }
851
Tomas Winkler5ca2d382014-08-21 14:29:13 +0300852 props_res = (struct hbm_props_response *)mei_msg;
853
854 if (props_res->status) {
Tomas Winkler89778d62014-08-21 14:29:16 +0300855 dev_err(&dev->pdev->dev, "hbm: properties response: wrong status = %d %s\n",
856 props_res->status,
857 mei_hbm_status_str(props_res->status));
Tomas Winkler5ca2d382014-08-21 14:29:13 +0300858 return -EPROTO;
859 }
860
861 mei_hbm_me_cl_add(dev, props_res);
862
Tomas Winklerbb1b0132012-12-25 19:06:07 +0200863 dev->me_client_index++;
864 dev->me_client_presentation_num++;
865
Tomas Winkler8120e722012-12-25 19:06:11 +0200866 /* request property for the next client */
Tomas Winkler544f9462014-01-08 20:19:21 +0200867 if (mei_hbm_prop_req(dev))
868 return -EIO;
Tomas Winklerbb1b0132012-12-25 19:06:07 +0200869
870 break;
871
872 case HOST_ENUM_RES_CMD:
Tomas Winkler544f9462014-01-08 20:19:21 +0200873 dev_dbg(&dev->pdev->dev, "hbm: enumeration response: message received\n");
874
875 dev->init_clients_timer = 0;
876
Tomas Winklerbb1b0132012-12-25 19:06:07 +0200877 enum_res = (struct hbm_host_enum_response *) mei_msg;
Tomas Winkler23f5a322013-09-02 03:11:01 +0300878 BUILD_BUG_ON(sizeof(dev->me_clients_map)
879 < sizeof(enum_res->valid_addresses));
880 memcpy(dev->me_clients_map, enum_res->valid_addresses,
Tomas Winkler5ca2d382014-08-21 14:29:13 +0300881 sizeof(enum_res->valid_addresses));
Tomas Winklerbb1b0132012-12-25 19:06:07 +0200882
Tomas Winkler544f9462014-01-08 20:19:21 +0200883 if (dev->dev_state != MEI_DEV_INIT_CLIENTS ||
884 dev->hbm_state != MEI_HBM_ENUM_CLIENTS) {
885 dev_err(&dev->pdev->dev, "hbm: enumeration response: state mismatch, [%d, %d]\n",
886 dev->dev_state, dev->hbm_state);
887 return -EPROTO;
Tomas Winklerbb1b0132012-12-25 19:06:07 +0200888 }
Tomas Winkler544f9462014-01-08 20:19:21 +0200889
Tomas Winkler544f9462014-01-08 20:19:21 +0200890 dev->hbm_state = MEI_HBM_CLIENT_PROPERTIES;
891
892 /* first property request */
893 if (mei_hbm_prop_req(dev))
894 return -EIO;
895
Tomas Winklerbb1b0132012-12-25 19:06:07 +0200896 break;
897
898 case HOST_STOP_RES_CMD:
Tomas Winkler544f9462014-01-08 20:19:21 +0200899 dev_dbg(&dev->pdev->dev, "hbm: stop response: message received\n");
Tomas Winkler9b0d5ef2013-04-18 23:03:48 +0300900
Tomas Winkler544f9462014-01-08 20:19:21 +0200901 dev->init_clients_timer = 0;
902
903 if (dev->hbm_state != MEI_HBM_STOPPED) {
904 dev_err(&dev->pdev->dev, "hbm: stop response: state mismatch, [%d, %d]\n",
905 dev->dev_state, dev->hbm_state);
906 return -EPROTO;
907 }
908
Tomas Winkler33ec0822014-01-12 00:36:09 +0200909 dev->dev_state = MEI_DEV_POWER_DOWN;
Tomas Winkler544f9462014-01-08 20:19:21 +0200910 dev_info(&dev->pdev->dev, "hbm: stop response: resetting.\n");
911 /* force the reset */
912 return -EPROTO;
Tomas Winklerbb1b0132012-12-25 19:06:07 +0200913 break;
914
915 case CLIENT_DISCONNECT_REQ_CMD:
Tomas Winkler544f9462014-01-08 20:19:21 +0200916 dev_dbg(&dev->pdev->dev, "hbm: disconnect request: message received\n");
917
Tomas Winklerbb1b0132012-12-25 19:06:07 +0200918 disconnect_req = (struct hbm_client_connect_request *)mei_msg;
Tomas Winkler8120e722012-12-25 19:06:11 +0200919 mei_hbm_fw_disconnect_req(dev, disconnect_req);
Tomas Winklerbb1b0132012-12-25 19:06:07 +0200920 break;
921
922 case ME_STOP_REQ_CMD:
Tomas Winkler544f9462014-01-08 20:19:21 +0200923 dev_dbg(&dev->pdev->dev, "hbm: stop request: message received\n");
Tomas Winkler544f9462014-01-08 20:19:21 +0200924 dev->hbm_state = MEI_HBM_STOPPED;
Tomas Winkler6bb948c2014-02-12 21:41:52 +0200925 if (mei_hbm_stop_req(dev)) {
Alexander Usyskincb02efc2014-08-21 14:29:19 +0300926 dev_err(&dev->pdev->dev, "hbm: stop request: failed to send stop request\n");
Tomas Winkler6bb948c2014-02-12 21:41:52 +0200927 return -EIO;
928 }
Tomas Winklerbb1b0132012-12-25 19:06:07 +0200929 break;
Tomas Winklerbb1b0132012-12-25 19:06:07 +0200930 default:
931 BUG();
932 break;
933
934 }
Tomas Winkler544f9462014-01-08 20:19:21 +0200935 return 0;
Tomas Winklerbb1b0132012-12-25 19:06:07 +0200936}
937