blob: 3c9914038490f97cd47d7785b6493cab5d58f74c [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
17#include <linux/pci.h>
18#include <linux/sched.h>
19#include <linux/wait.h>
20#include <linux/mei.h>
21
22#include "mei_dev.h"
23#include "interface.h"
24
25/**
Tomas Winklercd51ed62012-12-25 19:06:09 +020026 * mei_hbm_cl_hdr - construct client hbm header
27 * @cl: - client
28 * @hbm_cmd: host bus message command
29 * @buf: buffer for cl header
30 * @len: buffer length
31 */
32static inline
33void mei_hbm_cl_hdr(struct mei_cl *cl, u8 hbm_cmd, void *buf, size_t len)
34{
35 struct mei_hbm_cl_cmd *cmd = buf;
36
37 memset(cmd, 0, len);
38
39 cmd->hbm_cmd = hbm_cmd;
40 cmd->host_addr = cl->host_client_id;
41 cmd->me_addr = cl->me_client_id;
42}
43
44/**
45 * same_disconn_addr - tells if they have the same address
46 *
47 * @file: private data of the file object.
48 * @disconn: disconnection request.
49 *
50 * returns true if addres are same
51 */
52static inline
53bool mei_hbm_cl_addr_equal(struct mei_cl *cl, void *buf)
54{
55 struct mei_hbm_cl_cmd *cmd = buf;
56 return cl->host_client_id == cmd->host_addr &&
57 cl->me_client_id == cmd->me_addr;
58}
59
60
61/**
Tomas Winkler8120e722012-12-25 19:06:11 +020062 * mei_hbm_start_req - sends start request message.
Tomas Winklerbb1b0132012-12-25 19:06:07 +020063 *
64 * @dev: the device structure
Tomas Winklerbb1b0132012-12-25 19:06:07 +020065 */
Tomas Winkler8120e722012-12-25 19:06:11 +020066void mei_hbm_start_req(struct mei_device *dev)
Tomas Winklerbb1b0132012-12-25 19:06:07 +020067{
Tomas Winklere46f1872012-12-25 19:06:10 +020068 struct mei_msg_hdr *mei_hdr = &dev->wr_msg.hdr;
Tomas Winklerbb1b0132012-12-25 19:06:07 +020069 struct hbm_host_version_request *start_req;
70 const size_t len = sizeof(struct hbm_host_version_request);
71
Tomas Winklere46f1872012-12-25 19:06:10 +020072 mei_hbm_hdr(mei_hdr, len);
Tomas Winklerbb1b0132012-12-25 19:06:07 +020073
74 /* host start message */
Tomas Winklere46f1872012-12-25 19:06:10 +020075 start_req = (struct hbm_host_version_request *)dev->wr_msg.data;
Tomas Winklerbb1b0132012-12-25 19:06:07 +020076 memset(start_req, 0, len);
77 start_req->hbm_cmd = HOST_START_REQ_CMD;
78 start_req->host_version.major_version = HBM_MAJOR_VERSION;
79 start_req->host_version.minor_version = HBM_MINOR_VERSION;
80
81 dev->recvd_msg = false;
Tomas Winklere46f1872012-12-25 19:06:10 +020082 if (mei_write_message(dev, mei_hdr, dev->wr_msg.data)) {
Tomas Winklerbb1b0132012-12-25 19:06:07 +020083 dev_dbg(&dev->pdev->dev, "write send version message to FW fail.\n");
84 dev->dev_state = MEI_DEV_RESETING;
85 mei_reset(dev, 1);
86 }
87 dev->init_clients_state = MEI_START_MESSAGE;
88 dev->init_clients_timer = MEI_CLIENTS_INIT_TIMEOUT;
89 return ;
90}
91
92/**
Tomas Winkler8120e722012-12-25 19:06:11 +020093 * mei_hbm_enum_clients_req - sends enumeration client request message.
Tomas Winklerbb1b0132012-12-25 19:06:07 +020094 *
95 * @dev: the device structure
96 *
97 * returns none.
98 */
Tomas Winkler8120e722012-12-25 19:06:11 +020099static void mei_hbm_enum_clients_req(struct mei_device *dev)
Tomas Winklerbb1b0132012-12-25 19:06:07 +0200100{
Tomas Winklere46f1872012-12-25 19:06:10 +0200101 struct mei_msg_hdr *mei_hdr = &dev->wr_msg.hdr;
Tomas Winklerbb1b0132012-12-25 19:06:07 +0200102 struct hbm_host_enum_request *enum_req;
103 const size_t len = sizeof(struct hbm_host_enum_request);
104 /* enumerate clients */
Tomas Winklere46f1872012-12-25 19:06:10 +0200105 mei_hbm_hdr(mei_hdr, len);
Tomas Winklerbb1b0132012-12-25 19:06:07 +0200106
Tomas Winklere46f1872012-12-25 19:06:10 +0200107 enum_req = (struct hbm_host_enum_request *)dev->wr_msg.data;
108 memset(enum_req, 0, len);
Tomas Winklerbb1b0132012-12-25 19:06:07 +0200109 enum_req->hbm_cmd = HOST_ENUM_REQ_CMD;
110
Tomas Winklere46f1872012-12-25 19:06:10 +0200111 if (mei_write_message(dev, mei_hdr, dev->wr_msg.data)) {
Tomas Winklerbb1b0132012-12-25 19:06:07 +0200112 dev->dev_state = MEI_DEV_RESETING;
113 dev_dbg(&dev->pdev->dev, "write send enumeration request message to FW fail.\n");
114 mei_reset(dev, 1);
115 }
116 dev->init_clients_state = MEI_ENUM_CLIENTS_MESSAGE;
117 dev->init_clients_timer = MEI_CLIENTS_INIT_TIMEOUT;
118 return;
119}
120
Tomas Winkler8120e722012-12-25 19:06:11 +0200121/**
122 * mei_hbm_prop_requsest - request property for a single client
123 *
124 * @dev: the device structure
125 *
126 * returns none.
127 */
Tomas Winklerbb1b0132012-12-25 19:06:07 +0200128
Tomas Winkler8120e722012-12-25 19:06:11 +0200129static int mei_hbm_prop_req(struct mei_device *dev)
Tomas Winklerbb1b0132012-12-25 19:06:07 +0200130{
131
Tomas Winklere46f1872012-12-25 19:06:10 +0200132 struct mei_msg_hdr *mei_hdr = &dev->wr_msg.hdr;
Tomas Winklerbb1b0132012-12-25 19:06:07 +0200133 struct hbm_props_request *prop_req;
134 const size_t len = sizeof(struct hbm_props_request);
135 unsigned long next_client_index;
136 u8 client_num;
137
138
139 client_num = dev->me_client_presentation_num;
140
141 next_client_index = find_next_bit(dev->me_clients_map, MEI_CLIENTS_MAX,
142 dev->me_client_index);
143
144 /* We got all client properties */
145 if (next_client_index == MEI_CLIENTS_MAX) {
146 schedule_work(&dev->init_work);
147
148 return 0;
149 }
150
151 dev->me_clients[client_num].client_id = next_client_index;
152 dev->me_clients[client_num].mei_flow_ctrl_creds = 0;
153
Tomas Winklere46f1872012-12-25 19:06:10 +0200154 mei_hbm_hdr(mei_hdr, len);
155 prop_req = (struct hbm_props_request *)dev->wr_msg.data;
Tomas Winklerbb1b0132012-12-25 19:06:07 +0200156
157 memset(prop_req, 0, sizeof(struct hbm_props_request));
158
159
160 prop_req->hbm_cmd = HOST_CLIENT_PROPERTIES_REQ_CMD;
161 prop_req->address = next_client_index;
162
Tomas Winklere46f1872012-12-25 19:06:10 +0200163 if (mei_write_message(dev, mei_hdr, dev->wr_msg.data)) {
Tomas Winklerbb1b0132012-12-25 19:06:07 +0200164 dev->dev_state = MEI_DEV_RESETING;
165 dev_err(&dev->pdev->dev, "Properties request command failed\n");
166 mei_reset(dev, 1);
167
168 return -EIO;
169 }
170
171 dev->init_clients_timer = MEI_CLIENTS_INIT_TIMEOUT;
172 dev->me_client_index = next_client_index;
173
174 return 0;
175}
176
177/**
Tomas Winklere46f1872012-12-25 19:06:10 +0200178 * mei_hbm_stop_req_prepare - perpare stop request message
179 *
180 * @dev - mei device
181 * @mei_hdr - mei message header
182 * @data - hbm message body buffer
183 */
184static void mei_hbm_stop_req_prepare(struct mei_device *dev,
185 struct mei_msg_hdr *mei_hdr, unsigned char *data)
186{
187 struct hbm_host_stop_request *req =
188 (struct hbm_host_stop_request *)data;
189 const size_t len = sizeof(struct hbm_host_stop_request);
190
191 mei_hbm_hdr(mei_hdr, len);
192
193 memset(req, 0, len);
194 req->hbm_cmd = HOST_STOP_REQ_CMD;
195 req->reason = DRIVER_STOP_REQUEST;
196}
197
198/**
Tomas Winkler8120e722012-12-25 19:06:11 +0200199 * mei_hbm_cl_flow_control_req - sends flow control requst.
Tomas Winklerbb1b0132012-12-25 19:06:07 +0200200 *
201 * @dev: the device structure
Tomas Winkler8120e722012-12-25 19:06:11 +0200202 * @cl: client info
Tomas Winklerbb1b0132012-12-25 19:06:07 +0200203 *
204 * This function returns -EIO on write failure
205 */
Tomas Winkler8120e722012-12-25 19:06:11 +0200206int mei_hbm_cl_flow_control_req(struct mei_device *dev, struct mei_cl *cl)
Tomas Winklerbb1b0132012-12-25 19:06:07 +0200207{
Tomas Winklere46f1872012-12-25 19:06:10 +0200208 struct mei_msg_hdr *mei_hdr = &dev->wr_msg.hdr;
Tomas Winklerbb1b0132012-12-25 19:06:07 +0200209 const size_t len = sizeof(struct hbm_flow_control);
210
Tomas Winklere46f1872012-12-25 19:06:10 +0200211 mei_hbm_hdr(mei_hdr, len);
212 mei_hbm_cl_hdr(cl, MEI_FLOW_CONTROL_CMD, dev->wr_msg.data, len);
Tomas Winklerbb1b0132012-12-25 19:06:07 +0200213
Tomas Winklerbb1b0132012-12-25 19:06:07 +0200214 dev_dbg(&dev->pdev->dev, "sending flow control host client = %d, ME client = %d\n",
215 cl->host_client_id, cl->me_client_id);
216
Tomas Winklere46f1872012-12-25 19:06:10 +0200217 return mei_write_message(dev, mei_hdr, dev->wr_msg.data);
Tomas Winklerbb1b0132012-12-25 19:06:07 +0200218}
219
220/**
Tomas Winkler8120e722012-12-25 19:06:11 +0200221 * mei_hbm_cl_disconnect_req - sends disconnect message to fw.
Tomas Winklerbb1b0132012-12-25 19:06:07 +0200222 *
223 * @dev: the device structure
Tomas Winkler8120e722012-12-25 19:06:11 +0200224 * @cl: a client to disconnect from
Tomas Winklerbb1b0132012-12-25 19:06:07 +0200225 *
226 * This function returns -EIO on write failure
227 */
Tomas Winkler8120e722012-12-25 19:06:11 +0200228int mei_hbm_cl_disconnect_req(struct mei_device *dev, struct mei_cl *cl)
Tomas Winklerbb1b0132012-12-25 19:06:07 +0200229{
Tomas Winklere46f1872012-12-25 19:06:10 +0200230 struct mei_msg_hdr *mei_hdr = &dev->wr_msg.hdr;
Tomas Winklerbb1b0132012-12-25 19:06:07 +0200231 const size_t len = sizeof(struct hbm_client_connect_request);
232
Tomas Winklere46f1872012-12-25 19:06:10 +0200233 mei_hbm_hdr(mei_hdr, len);
234 mei_hbm_cl_hdr(cl, CLIENT_DISCONNECT_REQ_CMD, dev->wr_msg.data, len);
Tomas Winklerbb1b0132012-12-25 19:06:07 +0200235
Tomas Winklere46f1872012-12-25 19:06:10 +0200236 return mei_write_message(dev, mei_hdr, dev->wr_msg.data);
Tomas Winklerbb1b0132012-12-25 19:06:07 +0200237}
238
239/**
Tomas Winkler8120e722012-12-25 19:06:11 +0200240 * mei_hbm_cl_connect_req - send connection request to specific me client
Tomas Winklerbb1b0132012-12-25 19:06:07 +0200241 *
242 * @dev: the device structure
Tomas Winkler8120e722012-12-25 19:06:11 +0200243 * @cl: a client to connect to
Tomas Winklerbb1b0132012-12-25 19:06:07 +0200244 *
Tomas Winkler8120e722012-12-25 19:06:11 +0200245 * returns -EIO on write failure
Tomas Winklerbb1b0132012-12-25 19:06:07 +0200246 */
Tomas Winkler8120e722012-12-25 19:06:11 +0200247int mei_hbm_cl_connect_req(struct mei_device *dev, struct mei_cl *cl)
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 const size_t len = sizeof(struct hbm_client_connect_request);
251
Tomas Winklere46f1872012-12-25 19:06:10 +0200252 mei_hbm_hdr(mei_hdr, len);
253 mei_hbm_cl_hdr(cl, CLIENT_CONNECT_REQ_CMD, dev->wr_msg.data, len);
Tomas Winklerbb1b0132012-12-25 19:06:07 +0200254
Tomas Winklere46f1872012-12-25 19:06:10 +0200255 return mei_write_message(dev, mei_hdr, dev->wr_msg.data);
Tomas Winklerbb1b0132012-12-25 19:06:07 +0200256}
257
258/**
Tomas Winkler8120e722012-12-25 19:06:11 +0200259 * mei_client_disconnect_request - disconnect request initiated by me
260 * host sends disoconnect response
Tomas Winklerbb1b0132012-12-25 19:06:07 +0200261 *
262 * @dev: the device structure.
Tomas Winkler8120e722012-12-25 19:06:11 +0200263 * @disconnect_req: disconnect request bus message from the me
Tomas Winklerbb1b0132012-12-25 19:06:07 +0200264 */
Tomas Winkler8120e722012-12-25 19:06:11 +0200265static void mei_hbm_fw_disconnect_req(struct mei_device *dev,
Tomas Winklerbb1b0132012-12-25 19:06:07 +0200266 struct hbm_client_connect_request *disconnect_req)
267{
Tomas Winklercd51ed62012-12-25 19:06:09 +0200268 struct mei_cl *cl, *next;
Tomas Winklerbb1b0132012-12-25 19:06:07 +0200269 const size_t len = sizeof(struct hbm_client_connect_response);
270
Tomas Winklercd51ed62012-12-25 19:06:09 +0200271 list_for_each_entry_safe(cl, next, &dev->file_list, link) {
272 if (mei_hbm_cl_addr_equal(cl, disconnect_req)) {
Tomas Winklerbb1b0132012-12-25 19:06:07 +0200273 dev_dbg(&dev->pdev->dev, "disconnect request host client %d ME client %d.\n",
274 disconnect_req->host_addr,
275 disconnect_req->me_addr);
Tomas Winklercd51ed62012-12-25 19:06:09 +0200276 cl->state = MEI_FILE_DISCONNECTED;
277 cl->timer_count = 0;
278 if (cl == &dev->wd_cl)
Tomas Winklerbb1b0132012-12-25 19:06:07 +0200279 dev->wd_pending = false;
Tomas Winklercd51ed62012-12-25 19:06:09 +0200280 else if (cl == &dev->iamthif_cl)
Tomas Winklerbb1b0132012-12-25 19:06:07 +0200281 dev->iamthif_timer = 0;
282
283 /* prepare disconnect response */
Tomas Winklere46f1872012-12-25 19:06:10 +0200284 mei_hbm_hdr(&dev->wr_ext_msg.hdr, len);
Tomas Winklercd51ed62012-12-25 19:06:09 +0200285 mei_hbm_cl_hdr(cl, CLIENT_DISCONNECT_RES_CMD,
Tomas Winklere46f1872012-12-25 19:06:10 +0200286 dev->wr_ext_msg.data, len);
Tomas Winklerbb1b0132012-12-25 19:06:07 +0200287 break;
288 }
289 }
290}
291
292
293/**
294 * mei_hbm_dispatch - bottom half read routine after ISR to
295 * handle the read bus message cmd processing.
296 *
297 * @dev: the device structure
298 * @mei_hdr: header of bus message
299 */
300void mei_hbm_dispatch(struct mei_device *dev, struct mei_msg_hdr *hdr)
301{
302 struct mei_bus_message *mei_msg;
303 struct mei_me_client *me_client;
304 struct hbm_host_version_response *version_res;
305 struct hbm_client_connect_response *connect_res;
306 struct hbm_client_connect_response *disconnect_res;
307 struct hbm_client_connect_request *disconnect_req;
308 struct hbm_flow_control *flow_control;
309 struct hbm_props_response *props_res;
310 struct hbm_host_enum_response *enum_res;
Tomas Winklerbb1b0132012-12-25 19:06:07 +0200311
312 /* read the message to our buffer */
313 BUG_ON(hdr->length >= sizeof(dev->rd_msg_buf));
314 mei_read_slots(dev, dev->rd_msg_buf, hdr->length);
315 mei_msg = (struct mei_bus_message *)dev->rd_msg_buf;
316
317 switch (mei_msg->hbm_cmd) {
318 case HOST_START_RES_CMD:
319 version_res = (struct hbm_host_version_response *)mei_msg;
Tomas Winklere46f1872012-12-25 19:06:10 +0200320 if (!version_res->host_version_supported) {
Tomas Winklerbb1b0132012-12-25 19:06:07 +0200321 dev->version = version_res->me_max_version;
Tomas Winklerbb1b0132012-12-25 19:06:07 +0200322 dev_dbg(&dev->pdev->dev, "version mismatch.\n");
Tomas Winklere46f1872012-12-25 19:06:10 +0200323
324 mei_hbm_stop_req_prepare(dev, &dev->wr_msg.hdr,
325 dev->wr_msg.data);
326 mei_write_message(dev, &dev->wr_msg.hdr,
327 dev->wr_msg.data);
328 return;
329 }
330
331 dev->version.major_version = HBM_MAJOR_VERSION;
332 dev->version.minor_version = HBM_MINOR_VERSION;
333 if (dev->dev_state == MEI_DEV_INIT_CLIENTS &&
334 dev->init_clients_state == MEI_START_MESSAGE) {
335 dev->init_clients_timer = 0;
Tomas Winkler8120e722012-12-25 19:06:11 +0200336 mei_hbm_enum_clients_req(dev);
Tomas Winklere46f1872012-12-25 19:06:10 +0200337 } else {
338 dev->recvd_msg = false;
339 dev_dbg(&dev->pdev->dev, "reset due to received hbm: host start\n");
340 mei_reset(dev, 1);
Tomas Winklerbb1b0132012-12-25 19:06:07 +0200341 return;
342 }
343
344 dev->recvd_msg = true;
345 dev_dbg(&dev->pdev->dev, "host start response message received.\n");
346 break;
347
348 case CLIENT_CONNECT_RES_CMD:
349 connect_res = (struct hbm_client_connect_response *) mei_msg;
350 mei_client_connect_response(dev, connect_res);
351 dev_dbg(&dev->pdev->dev, "client connect response message received.\n");
352 wake_up(&dev->wait_recvd_msg);
353 break;
354
355 case CLIENT_DISCONNECT_RES_CMD:
356 disconnect_res = (struct hbm_client_connect_response *) mei_msg;
357 mei_client_disconnect_response(dev, disconnect_res);
358 dev_dbg(&dev->pdev->dev, "client disconnect response message received.\n");
359 wake_up(&dev->wait_recvd_msg);
360 break;
361
362 case MEI_FLOW_CONTROL_CMD:
363 flow_control = (struct hbm_flow_control *) mei_msg;
364 mei_client_flow_control_response(dev, flow_control);
365 dev_dbg(&dev->pdev->dev, "client flow control response message received.\n");
366 break;
367
368 case HOST_CLIENT_PROPERTIES_RES_CMD:
369 props_res = (struct hbm_props_response *)mei_msg;
370 me_client = &dev->me_clients[dev->me_client_presentation_num];
371
372 if (props_res->status || !dev->me_clients) {
373 dev_dbg(&dev->pdev->dev, "reset due to received host client properties response bus message wrong status.\n");
374 mei_reset(dev, 1);
375 return;
376 }
377
378 if (me_client->client_id != props_res->address) {
379 dev_err(&dev->pdev->dev,
380 "Host client properties reply mismatch\n");
381 mei_reset(dev, 1);
382
383 return;
384 }
385
386 if (dev->dev_state != MEI_DEV_INIT_CLIENTS ||
387 dev->init_clients_state != MEI_CLIENT_PROPERTIES_MESSAGE) {
388 dev_err(&dev->pdev->dev,
389 "Unexpected client properties reply\n");
390 mei_reset(dev, 1);
391
392 return;
393 }
394
395 me_client->props = props_res->client_properties;
396 dev->me_client_index++;
397 dev->me_client_presentation_num++;
398
Tomas Winkler8120e722012-12-25 19:06:11 +0200399 /* request property for the next client */
400 mei_hbm_prop_req(dev);
Tomas Winklerbb1b0132012-12-25 19:06:07 +0200401
402 break;
403
404 case HOST_ENUM_RES_CMD:
405 enum_res = (struct hbm_host_enum_response *) mei_msg;
406 memcpy(dev->me_clients_map, enum_res->valid_addresses, 32);
407 if (dev->dev_state == MEI_DEV_INIT_CLIENTS &&
408 dev->init_clients_state == MEI_ENUM_CLIENTS_MESSAGE) {
409 dev->init_clients_timer = 0;
410 dev->me_client_presentation_num = 0;
411 dev->me_client_index = 0;
412 mei_allocate_me_clients_storage(dev);
413 dev->init_clients_state =
414 MEI_CLIENT_PROPERTIES_MESSAGE;
415
Tomas Winkler8120e722012-12-25 19:06:11 +0200416 /* first property reqeust */
417 mei_hbm_prop_req(dev);
Tomas Winklerbb1b0132012-12-25 19:06:07 +0200418 } else {
419 dev_dbg(&dev->pdev->dev, "reset due to received host enumeration clients response bus message.\n");
420 mei_reset(dev, 1);
421 return;
422 }
423 break;
424
425 case HOST_STOP_RES_CMD:
426 dev->dev_state = MEI_DEV_DISABLED;
427 dev_dbg(&dev->pdev->dev, "resetting because of FW stop response.\n");
428 mei_reset(dev, 1);
429 break;
430
431 case CLIENT_DISCONNECT_REQ_CMD:
432 /* search for client */
433 disconnect_req = (struct hbm_client_connect_request *)mei_msg;
Tomas Winkler8120e722012-12-25 19:06:11 +0200434 mei_hbm_fw_disconnect_req(dev, disconnect_req);
Tomas Winklerbb1b0132012-12-25 19:06:07 +0200435 break;
436
437 case ME_STOP_REQ_CMD:
Tomas Winklerbb1b0132012-12-25 19:06:07 +0200438
Tomas Winklere46f1872012-12-25 19:06:10 +0200439 mei_hbm_stop_req_prepare(dev, &dev->wr_ext_msg.hdr,
440 dev->wr_ext_msg.data);
Tomas Winklerbb1b0132012-12-25 19:06:07 +0200441 break;
Tomas Winklerbb1b0132012-12-25 19:06:07 +0200442 default:
443 BUG();
444 break;
445
446 }
447}
448