blob: 4666f0ba350ab08a3a3e35ec62552a1d0de68603 [file] [log] [blame]
Oren Weil91f01c62011-05-15 13:43:44 +03001/*
2 *
3 * Intel Management Engine Interface (Intel MEI) Linux driver
Tomas Winkler733ba912012-02-09 19:25:53 +02004 * Copyright (c) 2003-2012, Intel Corporation.
Oren Weil91f01c62011-05-15 13:43:44 +03005 *
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/delay.h>
21
22#include "mei_dev.h"
23#include "hw.h"
24#include "interface.h"
Tomas Winkler4f3afe12012-05-09 16:38:59 +030025#include <linux/mei.h>
Oren Weil91f01c62011-05-15 13:43:44 +030026
Tomas Winklerb210d752012-08-07 00:03:56 +030027const char *mei_dev_state_str(int state)
28{
29#define MEI_DEV_STATE(state) case MEI_DEV_##state: return #state
30 switch (state) {
31 MEI_DEV_STATE(INITIALIZING);
32 MEI_DEV_STATE(INIT_CLIENTS);
33 MEI_DEV_STATE(ENABLED);
34 MEI_DEV_STATE(RESETING);
35 MEI_DEV_STATE(DISABLED);
36 MEI_DEV_STATE(RECOVERING_FROM_RESET);
37 MEI_DEV_STATE(POWER_DOWN);
38 MEI_DEV_STATE(POWER_UP);
39 default:
40 return "unkown";
41 }
42#undef MEI_DEV_STATE
43}
44
45
Oren Weil91f01c62011-05-15 13:43:44 +030046const uuid_le mei_amthi_guid = UUID_LE(0x12f80028, 0xb4b7, 0x4b2d, 0xac,
47 0xa8, 0x46, 0xe0, 0xff, 0x65,
48 0x81, 0x4c);
49
50/**
Tomas Winkler0288c7c2011-06-06 10:44:34 +030051 * mei_io_list_flush - removes list entry belonging to cl.
Oren Weil91f01c62011-05-15 13:43:44 +030052 *
53 * @list: An instance of our list structure
54 * @cl: private data of the file object
55 */
Tomas Winklerfb601ad2012-10-15 12:06:48 +020056void mei_io_list_flush(struct mei_cl_cb *list, struct mei_cl *cl)
Oren Weil91f01c62011-05-15 13:43:44 +030057{
Tomas Winkler3a5352f2011-12-04 16:16:27 +020058 struct mei_cl_cb *pos;
59 struct mei_cl_cb *next;
Oren Weil91f01c62011-05-15 13:43:44 +030060
Tomas Winklerfb601ad2012-10-15 12:06:48 +020061 list_for_each_entry_safe(pos, next, &list->list, list) {
Tomas Winkler3a5352f2011-12-04 16:16:27 +020062 if (pos->file_private) {
Tomas Winkler0288c7c2011-06-06 10:44:34 +030063 struct mei_cl *cl_tmp;
Tomas Winklerb7cd2d92011-11-27 21:43:34 +020064 cl_tmp = (struct mei_cl *)pos->file_private;
Tomas Winkler0288c7c2011-06-06 10:44:34 +030065 if (mei_cl_cmp_id(cl, cl_tmp))
Tomas Winklerfb601ad2012-10-15 12:06:48 +020066 list_del(&pos->list);
Oren Weil91f01c62011-05-15 13:43:44 +030067 }
68 }
69}
Tomas Winkler0288c7c2011-06-06 10:44:34 +030070/**
71 * mei_cl_flush_queues - flushes queue lists belonging to cl.
72 *
73 * @dev: the device structure
74 * @cl: private data of the file object
75 */
76int mei_cl_flush_queues(struct mei_cl *cl)
77{
78 if (!cl || !cl->dev)
79 return -EINVAL;
80
81 dev_dbg(&cl->dev->pdev->dev, "remove list entry belonging to cl\n");
82 mei_io_list_flush(&cl->dev->read_list, cl);
83 mei_io_list_flush(&cl->dev->write_list, cl);
84 mei_io_list_flush(&cl->dev->write_waiting_list, cl);
85 mei_io_list_flush(&cl->dev->ctrl_wr_list, cl);
86 mei_io_list_flush(&cl->dev->ctrl_rd_list, cl);
87 mei_io_list_flush(&cl->dev->amthi_cmd_list, cl);
88 mei_io_list_flush(&cl->dev->amthi_read_complete_list, cl);
89 return 0;
90}
91
92
Oren Weil91f01c62011-05-15 13:43:44 +030093
94/**
95 * mei_reset_iamthif_params - initializes mei device iamthif
96 *
97 * @dev: the device structure
98 */
99static void mei_reset_iamthif_params(struct mei_device *dev)
100{
101 /* reset iamthif parameters. */
102 dev->iamthif_current_cb = NULL;
103 dev->iamthif_msg_buf_size = 0;
104 dev->iamthif_msg_buf_index = 0;
Tomas Winklereb9af0a2011-05-25 17:28:22 +0300105 dev->iamthif_canceled = false;
106 dev->iamthif_ioctl = false;
Oren Weil91f01c62011-05-15 13:43:44 +0300107 dev->iamthif_state = MEI_IAMTHIF_IDLE;
108 dev->iamthif_timer = 0;
109}
110
111/**
112 * init_mei_device - allocates and initializes the mei device structure
113 *
114 * @pdev: The pci device structure
115 *
116 * returns The mei_device_device pointer on success, NULL on failure.
117 */
Tomas Winklerc95efb72011-05-25 17:28:21 +0300118struct mei_device *mei_device_init(struct pci_dev *pdev)
Oren Weil91f01c62011-05-15 13:43:44 +0300119{
Oren Weil91f01c62011-05-15 13:43:44 +0300120 struct mei_device *dev;
121
122 dev = kzalloc(sizeof(struct mei_device), GFP_KERNEL);
123 if (!dev)
124 return NULL;
125
126 /* setup our list array */
Oren Weil91f01c62011-05-15 13:43:44 +0300127 INIT_LIST_HEAD(&dev->file_list);
128 INIT_LIST_HEAD(&dev->wd_cl.link);
129 INIT_LIST_HEAD(&dev->iamthif_cl.link);
130 mutex_init(&dev->device_lock);
131 init_waitqueue_head(&dev->wait_recvd_msg);
132 init_waitqueue_head(&dev->wait_stop_wd);
Tomas Winklerb210d752012-08-07 00:03:56 +0300133 dev->dev_state = MEI_DEV_INITIALIZING;
Oren Weil91f01c62011-05-15 13:43:44 +0300134 dev->iamthif_state = MEI_IAMTHIF_IDLE;
Oren Weil9ce178e2011-09-07 09:03:09 +0300135 dev->wd_interface_reg = false;
Tomas Winkler0288c7c2011-06-06 10:44:34 +0300136
137
138 mei_io_list_init(&dev->read_list);
139 mei_io_list_init(&dev->write_list);
140 mei_io_list_init(&dev->write_waiting_list);
141 mei_io_list_init(&dev->ctrl_wr_list);
142 mei_io_list_init(&dev->ctrl_rd_list);
143 mei_io_list_init(&dev->amthi_cmd_list);
144 mei_io_list_init(&dev->amthi_read_complete_list);
Oren Weil91f01c62011-05-15 13:43:44 +0300145 dev->pdev = pdev;
146 return dev;
147}
148
149/**
150 * mei_hw_init - initializes host and fw to start work.
151 *
152 * @dev: the device structure
153 *
154 * returns 0 on success, <0 on failure.
155 */
156int mei_hw_init(struct mei_device *dev)
157{
158 int err = 0;
159 int ret;
160
161 mutex_lock(&dev->device_lock);
162
163 dev->host_hw_state = mei_hcsr_read(dev);
164 dev->me_hw_state = mei_mecsr_read(dev);
165 dev_dbg(&dev->pdev->dev, "host_hw_state = 0x%08x, mestate = 0x%08x.\n",
166 dev->host_hw_state, dev->me_hw_state);
167
168 /* acknowledge interrupt and stop interupts */
169 if ((dev->host_hw_state & H_IS) == H_IS)
170 mei_reg_write(dev, H_CSR, dev->host_hw_state);
171
Tomas Winkler24aadc82012-06-25 23:46:27 +0300172 /* Doesn't change in runtime */
173 dev->hbuf_depth = (dev->host_hw_state & H_CBD) >> 24;
174
Tomas Winklereb9af0a2011-05-25 17:28:22 +0300175 dev->recvd_msg = false;
Oren Weil91f01c62011-05-15 13:43:44 +0300176 dev_dbg(&dev->pdev->dev, "reset in start the mei device.\n");
177
178 mei_reset(dev, 1);
179
180 dev_dbg(&dev->pdev->dev, "host_hw_state = 0x%08x, me_hw_state = 0x%08x.\n",
181 dev->host_hw_state, dev->me_hw_state);
182
183 /* wait for ME to turn on ME_RDY */
184 if (!dev->recvd_msg) {
185 mutex_unlock(&dev->device_lock);
186 err = wait_event_interruptible_timeout(dev->wait_recvd_msg,
187 dev->recvd_msg, MEI_INTEROP_TIMEOUT);
188 mutex_lock(&dev->device_lock);
189 }
190
Tomas Winklera534bb62011-06-13 16:39:31 +0300191 if (err <= 0 && !dev->recvd_msg) {
Tomas Winklerb210d752012-08-07 00:03:56 +0300192 dev->dev_state = MEI_DEV_DISABLED;
Oren Weil91f01c62011-05-15 13:43:44 +0300193 dev_dbg(&dev->pdev->dev,
194 "wait_event_interruptible_timeout failed"
195 "on wait for ME to turn on ME_RDY.\n");
196 ret = -ENODEV;
197 goto out;
198 }
199
200 if (!(((dev->host_hw_state & H_RDY) == H_RDY) &&
201 ((dev->me_hw_state & ME_RDY_HRA) == ME_RDY_HRA))) {
Tomas Winklerb210d752012-08-07 00:03:56 +0300202 dev->dev_state = MEI_DEV_DISABLED;
Oren Weil91f01c62011-05-15 13:43:44 +0300203 dev_dbg(&dev->pdev->dev,
204 "host_hw_state = 0x%08x, me_hw_state = 0x%08x.\n",
205 dev->host_hw_state, dev->me_hw_state);
206
Dan Carpenter8eb73c62011-06-09 10:18:23 +0300207 if (!(dev->host_hw_state & H_RDY))
Oren Weil91f01c62011-05-15 13:43:44 +0300208 dev_dbg(&dev->pdev->dev, "host turn off H_RDY.\n");
209
Dan Carpenter8eb73c62011-06-09 10:18:23 +0300210 if (!(dev->me_hw_state & ME_RDY_HRA))
Oren Weil91f01c62011-05-15 13:43:44 +0300211 dev_dbg(&dev->pdev->dev, "ME turn off ME_RDY.\n");
212
Tomas Winklerd39a4642012-03-19 17:58:42 +0200213 dev_err(&dev->pdev->dev, "link layer initialization failed.\n");
Oren Weil91f01c62011-05-15 13:43:44 +0300214 ret = -ENODEV;
215 goto out;
216 }
217
218 if (dev->version.major_version != HBM_MAJOR_VERSION ||
219 dev->version.minor_version != HBM_MINOR_VERSION) {
220 dev_dbg(&dev->pdev->dev, "MEI start failed.\n");
221 ret = -ENODEV;
222 goto out;
223 }
224
Tomas Winklereb9af0a2011-05-25 17:28:22 +0300225 dev->recvd_msg = false;
Oren Weil91f01c62011-05-15 13:43:44 +0300226 dev_dbg(&dev->pdev->dev, "host_hw_state = 0x%08x, me_hw_state = 0x%08x.\n",
227 dev->host_hw_state, dev->me_hw_state);
228 dev_dbg(&dev->pdev->dev, "ME turn on ME_RDY and host turn on H_RDY.\n");
229 dev_dbg(&dev->pdev->dev, "link layer has been established.\n");
230 dev_dbg(&dev->pdev->dev, "MEI start success.\n");
231 ret = 0;
232
233out:
234 mutex_unlock(&dev->device_lock);
235 return ret;
236}
237
238/**
239 * mei_hw_reset - resets fw via mei csr register.
240 *
241 * @dev: the device structure
242 * @interrupts_enabled: if interrupt should be enabled after reset.
243 */
244static void mei_hw_reset(struct mei_device *dev, int interrupts_enabled)
245{
246 dev->host_hw_state |= (H_RST | H_IG);
247
248 if (interrupts_enabled)
249 mei_enable_interrupts(dev);
250 else
251 mei_disable_interrupts(dev);
252}
253
254/**
255 * mei_reset - resets host and fw.
256 *
257 * @dev: the device structure
258 * @interrupts_enabled: if interrupt should be enabled after reset.
259 */
260void mei_reset(struct mei_device *dev, int interrupts_enabled)
261{
262 struct mei_cl *cl_pos = NULL;
263 struct mei_cl *cl_next = NULL;
264 struct mei_cl_cb *cb_pos = NULL;
265 struct mei_cl_cb *cb_next = NULL;
266 bool unexpected;
267
Tomas Winklerb210d752012-08-07 00:03:56 +0300268 if (dev->dev_state == MEI_DEV_RECOVERING_FROM_RESET) {
Tomas Winklereb9af0a2011-05-25 17:28:22 +0300269 dev->need_reset = true;
Oren Weil91f01c62011-05-15 13:43:44 +0300270 return;
271 }
272
Tomas Winklerb210d752012-08-07 00:03:56 +0300273 unexpected = (dev->dev_state != MEI_DEV_INITIALIZING &&
274 dev->dev_state != MEI_DEV_DISABLED &&
275 dev->dev_state != MEI_DEV_POWER_DOWN &&
276 dev->dev_state != MEI_DEV_POWER_UP);
Oren Weil91f01c62011-05-15 13:43:44 +0300277
278 dev->host_hw_state = mei_hcsr_read(dev);
279
280 dev_dbg(&dev->pdev->dev, "before reset host_hw_state = 0x%08x.\n",
281 dev->host_hw_state);
282
283 mei_hw_reset(dev, interrupts_enabled);
284
285 dev->host_hw_state &= ~H_RST;
286 dev->host_hw_state |= H_IG;
287
288 mei_hcsr_set(dev);
289
290 dev_dbg(&dev->pdev->dev, "currently saved host_hw_state = 0x%08x.\n",
291 dev->host_hw_state);
292
Tomas Winklereb9af0a2011-05-25 17:28:22 +0300293 dev->need_reset = false;
Oren Weil91f01c62011-05-15 13:43:44 +0300294
Tomas Winklerb210d752012-08-07 00:03:56 +0300295 if (dev->dev_state != MEI_DEV_INITIALIZING) {
296 if (dev->dev_state != MEI_DEV_DISABLED &&
297 dev->dev_state != MEI_DEV_POWER_DOWN)
298 dev->dev_state = MEI_DEV_RESETING;
Oren Weil91f01c62011-05-15 13:43:44 +0300299
300 list_for_each_entry_safe(cl_pos,
301 cl_next, &dev->file_list, link) {
302 cl_pos->state = MEI_FILE_DISCONNECTED;
303 cl_pos->mei_flow_ctrl_creds = 0;
304 cl_pos->read_cb = NULL;
305 cl_pos->timer_count = 0;
306 }
307 /* remove entry if already in list */
308 dev_dbg(&dev->pdev->dev, "list del iamthif and wd file list.\n");
309 mei_remove_client_from_file_list(dev,
310 dev->wd_cl.host_client_id);
311
312 mei_remove_client_from_file_list(dev,
313 dev->iamthif_cl.host_client_id);
314
315 mei_reset_iamthif_params(dev);
Oren Weil91f01c62011-05-15 13:43:44 +0300316 dev->extra_write_index = 0;
317 }
318
Tomas Winklercf9673d2011-06-06 10:44:33 +0300319 dev->me_clients_num = 0;
Oren Weil91f01c62011-05-15 13:43:44 +0300320 dev->rd_msg_hdr = 0;
Tomas Winklereb9af0a2011-05-25 17:28:22 +0300321 dev->wd_pending = false;
Oren Weil91f01c62011-05-15 13:43:44 +0300322
323 /* update the state of the registers after reset */
324 dev->host_hw_state = mei_hcsr_read(dev);
325 dev->me_hw_state = mei_mecsr_read(dev);
326
327 dev_dbg(&dev->pdev->dev, "after reset host_hw_state = 0x%08x, me_hw_state = 0x%08x.\n",
328 dev->host_hw_state, dev->me_hw_state);
329
330 if (unexpected)
Tomas Winklerb210d752012-08-07 00:03:56 +0300331 dev_warn(&dev->pdev->dev, "unexpected reset: dev_state = %s\n",
332 mei_dev_state_str(dev->dev_state));
Oren Weil91f01c62011-05-15 13:43:44 +0300333
334 /* Wake up all readings so they can be interrupted */
335 list_for_each_entry_safe(cl_pos, cl_next, &dev->file_list, link) {
336 if (waitqueue_active(&cl_pos->rx_wait)) {
337 dev_dbg(&dev->pdev->dev, "Waking up client!\n");
338 wake_up_interruptible(&cl_pos->rx_wait);
339 }
340 }
341 /* remove all waiting requests */
Tomas Winklerfb601ad2012-10-15 12:06:48 +0200342 list_for_each_entry_safe(cb_pos, cb_next, &dev->write_list.list, list) {
343 list_del(&cb_pos->list);
Tomas Winkler601a1ef2012-10-09 16:50:20 +0200344 mei_io_cb_free(cb_pos);
Oren Weil91f01c62011-05-15 13:43:44 +0300345 }
346}
347
348
349
350/**
351 * host_start_message - mei host sends start message.
352 *
353 * @dev: the device structure
354 *
355 * returns none.
356 */
Tomas Winklerc95efb72011-05-25 17:28:21 +0300357void mei_host_start_message(struct mei_device *dev)
Oren Weil91f01c62011-05-15 13:43:44 +0300358{
359 struct mei_msg_hdr *mei_hdr;
360 struct hbm_host_version_request *host_start_req;
361
362 /* host start message */
363 mei_hdr = (struct mei_msg_hdr *) &dev->wr_msg_buf[0];
364 mei_hdr->host_addr = 0;
365 mei_hdr->me_addr = 0;
366 mei_hdr->length = sizeof(struct hbm_host_version_request);
367 mei_hdr->msg_complete = 1;
368 mei_hdr->reserved = 0;
369
370 host_start_req =
371 (struct hbm_host_version_request *) &dev->wr_msg_buf[1];
372 memset(host_start_req, 0, sizeof(struct hbm_host_version_request));
Tomas Winkler1ca7e782012-02-26 23:18:57 +0200373 host_start_req->hbm_cmd = HOST_START_REQ_CMD;
Oren Weil91f01c62011-05-15 13:43:44 +0300374 host_start_req->host_version.major_version = HBM_MAJOR_VERSION;
375 host_start_req->host_version.minor_version = HBM_MINOR_VERSION;
Tomas Winklereb9af0a2011-05-25 17:28:22 +0300376 dev->recvd_msg = false;
Tomas Winkler1ccb7b62012-03-14 14:39:42 +0200377 if (mei_write_message(dev, mei_hdr, (unsigned char *)host_start_req,
Oren Weil91f01c62011-05-15 13:43:44 +0300378 mei_hdr->length)) {
379 dev_dbg(&dev->pdev->dev, "write send version message to FW fail.\n");
Tomas Winklerb210d752012-08-07 00:03:56 +0300380 dev->dev_state = MEI_DEV_RESETING;
Oren Weil91f01c62011-05-15 13:43:44 +0300381 mei_reset(dev, 1);
382 }
383 dev->init_clients_state = MEI_START_MESSAGE;
384 dev->init_clients_timer = INIT_CLIENTS_TIMEOUT;
385 return ;
386}
387
388/**
389 * host_enum_clients_message - host sends enumeration client request message.
390 *
391 * @dev: the device structure
392 *
393 * returns none.
394 */
Tomas Winklerc95efb72011-05-25 17:28:21 +0300395void mei_host_enum_clients_message(struct mei_device *dev)
Oren Weil91f01c62011-05-15 13:43:44 +0300396{
397 struct mei_msg_hdr *mei_hdr;
398 struct hbm_host_enum_request *host_enum_req;
399 mei_hdr = (struct mei_msg_hdr *) &dev->wr_msg_buf[0];
400 /* enumerate clients */
401 mei_hdr->host_addr = 0;
402 mei_hdr->me_addr = 0;
403 mei_hdr->length = sizeof(struct hbm_host_enum_request);
404 mei_hdr->msg_complete = 1;
405 mei_hdr->reserved = 0;
406
407 host_enum_req = (struct hbm_host_enum_request *) &dev->wr_msg_buf[1];
408 memset(host_enum_req, 0, sizeof(struct hbm_host_enum_request));
Tomas Winkler1ca7e782012-02-26 23:18:57 +0200409 host_enum_req->hbm_cmd = HOST_ENUM_REQ_CMD;
Tomas Winkler1ccb7b62012-03-14 14:39:42 +0200410 if (mei_write_message(dev, mei_hdr, (unsigned char *)host_enum_req,
Oren Weil91f01c62011-05-15 13:43:44 +0300411 mei_hdr->length)) {
Tomas Winklerb210d752012-08-07 00:03:56 +0300412 dev->dev_state = MEI_DEV_RESETING;
Oren Weil91f01c62011-05-15 13:43:44 +0300413 dev_dbg(&dev->pdev->dev, "write send enumeration request message to FW fail.\n");
414 mei_reset(dev, 1);
415 }
416 dev->init_clients_state = MEI_ENUM_CLIENTS_MESSAGE;
417 dev->init_clients_timer = INIT_CLIENTS_TIMEOUT;
Tomas Winkler1ccb7b62012-03-14 14:39:42 +0200418 return;
Oren Weil91f01c62011-05-15 13:43:44 +0300419}
420
421
422/**
423 * allocate_me_clients_storage - allocates storage for me clients
424 *
425 * @dev: the device structure
426 *
427 * returns none.
428 */
Tomas Winklerc95efb72011-05-25 17:28:21 +0300429void mei_allocate_me_clients_storage(struct mei_device *dev)
Oren Weil91f01c62011-05-15 13:43:44 +0300430{
431 struct mei_me_client *clients;
432 int b;
433
434 /* count how many ME clients we have */
435 for_each_set_bit(b, dev->me_clients_map, MEI_CLIENTS_MAX)
Tomas Winklercf9673d2011-06-06 10:44:33 +0300436 dev->me_clients_num++;
Oren Weil91f01c62011-05-15 13:43:44 +0300437
Tomas Winklercf9673d2011-06-06 10:44:33 +0300438 if (dev->me_clients_num <= 0)
Oren Weil91f01c62011-05-15 13:43:44 +0300439 return ;
440
441
442 if (dev->me_clients != NULL) {
443 kfree(dev->me_clients);
444 dev->me_clients = NULL;
445 }
446 dev_dbg(&dev->pdev->dev, "memory allocation for ME clients size=%zd.\n",
Tomas Winklercf9673d2011-06-06 10:44:33 +0300447 dev->me_clients_num * sizeof(struct mei_me_client));
Oren Weil91f01c62011-05-15 13:43:44 +0300448 /* allocate storage for ME clients representation */
Tomas Winklercf9673d2011-06-06 10:44:33 +0300449 clients = kcalloc(dev->me_clients_num,
Oren Weil91f01c62011-05-15 13:43:44 +0300450 sizeof(struct mei_me_client), GFP_KERNEL);
451 if (!clients) {
452 dev_dbg(&dev->pdev->dev, "memory allocation for ME clients failed.\n");
Tomas Winklerb210d752012-08-07 00:03:56 +0300453 dev->dev_state = MEI_DEV_RESETING;
Oren Weil91f01c62011-05-15 13:43:44 +0300454 mei_reset(dev, 1);
455 return ;
456 }
457 dev->me_clients = clients;
458 return ;
459}
460/**
461 * host_client_properties - reads properties for client
462 *
463 * @dev: the device structure
464 *
Oren Weilabc51b62011-09-21 16:45:30 +0300465 * returns:
466 * < 0 - Error.
467 * = 0 - no more clients.
468 * = 1 - still have clients to send properties request.
Oren Weil91f01c62011-05-15 13:43:44 +0300469 */
Oren Weilabc51b62011-09-21 16:45:30 +0300470int mei_host_client_properties(struct mei_device *dev)
Oren Weil91f01c62011-05-15 13:43:44 +0300471{
472 struct mei_msg_hdr *mei_header;
473 struct hbm_props_request *host_cli_req;
474 int b;
475 u8 client_num = dev->me_client_presentation_num;
476
477 b = dev->me_client_index;
478 b = find_next_bit(dev->me_clients_map, MEI_CLIENTS_MAX, b);
479 if (b < MEI_CLIENTS_MAX) {
480 dev->me_clients[client_num].client_id = b;
481 dev->me_clients[client_num].mei_flow_ctrl_creds = 0;
482 mei_header = (struct mei_msg_hdr *)&dev->wr_msg_buf[0];
483 mei_header->host_addr = 0;
484 mei_header->me_addr = 0;
485 mei_header->length = sizeof(struct hbm_props_request);
486 mei_header->msg_complete = 1;
487 mei_header->reserved = 0;
488
489 host_cli_req = (struct hbm_props_request *)&dev->wr_msg_buf[1];
490
491 memset(host_cli_req, 0, sizeof(struct hbm_props_request));
492
Tomas Winkler1ca7e782012-02-26 23:18:57 +0200493 host_cli_req->hbm_cmd = HOST_CLIENT_PROPERTIES_REQ_CMD;
Oren Weil91f01c62011-05-15 13:43:44 +0300494 host_cli_req->address = b;
495
Tomas Winkler1ccb7b62012-03-14 14:39:42 +0200496 if (mei_write_message(dev, mei_header,
Oren Weil91f01c62011-05-15 13:43:44 +0300497 (unsigned char *)host_cli_req,
498 mei_header->length)) {
Tomas Winklerb210d752012-08-07 00:03:56 +0300499 dev->dev_state = MEI_DEV_RESETING;
Oren Weil91f01c62011-05-15 13:43:44 +0300500 dev_dbg(&dev->pdev->dev, "write send enumeration request message to FW fail.\n");
501 mei_reset(dev, 1);
Oren Weilabc51b62011-09-21 16:45:30 +0300502 return -EIO;
Oren Weil91f01c62011-05-15 13:43:44 +0300503 }
504
505 dev->init_clients_timer = INIT_CLIENTS_TIMEOUT;
506 dev->me_client_index = b;
Oren Weilabc51b62011-09-21 16:45:30 +0300507 return 1;
Oren Weil91f01c62011-05-15 13:43:44 +0300508 }
509
Oren Weilabc51b62011-09-21 16:45:30 +0300510 return 0;
Oren Weil91f01c62011-05-15 13:43:44 +0300511}
512
513/**
514 * mei_init_file_private - initializes private file structure.
515 *
516 * @priv: private file structure to be initialized
517 * @file: the file structure
518 */
Tomas Winklerc95efb72011-05-25 17:28:21 +0300519void mei_cl_init(struct mei_cl *priv, struct mei_device *dev)
Oren Weil91f01c62011-05-15 13:43:44 +0300520{
521 memset(priv, 0, sizeof(struct mei_cl));
522 init_waitqueue_head(&priv->wait);
523 init_waitqueue_head(&priv->rx_wait);
524 init_waitqueue_head(&priv->tx_wait);
525 INIT_LIST_HEAD(&priv->link);
526 priv->reading_state = MEI_IDLE;
527 priv->writing_state = MEI_IDLE;
528 priv->dev = dev;
529}
530
Tomas Winkler07b509b2012-07-23 14:05:39 +0300531int mei_me_cl_by_uuid(const struct mei_device *dev, const uuid_le *cuuid)
Oren Weil91f01c62011-05-15 13:43:44 +0300532{
Tomas Winkler07b509b2012-07-23 14:05:39 +0300533 int i, res = -ENOENT;
Oren Weil91f01c62011-05-15 13:43:44 +0300534
Tomas Winklercf9673d2011-06-06 10:44:33 +0300535 for (i = 0; i < dev->me_clients_num; ++i)
Tomas Winkler07b509b2012-07-23 14:05:39 +0300536 if (uuid_le_cmp(*cuuid,
Oren Weil91f01c62011-05-15 13:43:44 +0300537 dev->me_clients[i].props.protocol_name) == 0) {
538 res = i;
539 break;
540 }
541
542 return res;
543}
544
545
546/**
Tomas Winkler07b509b2012-07-23 14:05:39 +0300547 * mei_me_cl_update_filext - searches for ME client guid
Oren Weil91f01c62011-05-15 13:43:44 +0300548 * sets client_id in mei_file_private if found
549 * @dev: the device structure
Tomas Winkler07b509b2012-07-23 14:05:39 +0300550 * @cl: private file structure to set client_id in
551 * @cuuid: searched uuid of ME client
Oren Weil91f01c62011-05-15 13:43:44 +0300552 * @client_id: id of host client to be set in file private structure
553 *
554 * returns ME client index
555 */
Tomas Winkler07b509b2012-07-23 14:05:39 +0300556int mei_me_cl_update_filext(struct mei_device *dev, struct mei_cl *cl,
557 const uuid_le *cuuid, u8 host_cl_id)
Oren Weil91f01c62011-05-15 13:43:44 +0300558{
559 int i;
560
Tomas Winkler07b509b2012-07-23 14:05:39 +0300561 if (!dev || !cl || !cuuid)
562 return -EINVAL;
Oren Weil91f01c62011-05-15 13:43:44 +0300563
564 /* check for valid client id */
Tomas Winkler07b509b2012-07-23 14:05:39 +0300565 i = mei_me_cl_by_uuid(dev, cuuid);
Oren Weil91f01c62011-05-15 13:43:44 +0300566 if (i >= 0) {
Tomas Winkler07b509b2012-07-23 14:05:39 +0300567 cl->me_client_id = dev->me_clients[i].client_id;
568 cl->state = MEI_FILE_CONNECTING;
569 cl->host_client_id = host_cl_id;
Oren Weil91f01c62011-05-15 13:43:44 +0300570
Tomas Winkler07b509b2012-07-23 14:05:39 +0300571 list_add_tail(&cl->link, &dev->file_list);
Oren Weil91f01c62011-05-15 13:43:44 +0300572 return (u8)i;
573 }
574
Tomas Winkler07b509b2012-07-23 14:05:39 +0300575 return -ENOENT;
Oren Weil91f01c62011-05-15 13:43:44 +0300576}
577
578/**
579 * host_init_iamthif - mei initialization iamthif client.
580 *
581 * @dev: the device structure
582 *
583 */
Tomas Winklerc95efb72011-05-25 17:28:21 +0300584void mei_host_init_iamthif(struct mei_device *dev)
Oren Weil91f01c62011-05-15 13:43:44 +0300585{
Tomas Winkler07b509b2012-07-23 14:05:39 +0300586 int i;
Oren Weil91f01c62011-05-15 13:43:44 +0300587 unsigned char *msg_buf;
588
Tomas Winklerc95efb72011-05-25 17:28:21 +0300589 mei_cl_init(&dev->iamthif_cl, dev);
Oren Weil91f01c62011-05-15 13:43:44 +0300590 dev->iamthif_cl.state = MEI_FILE_DISCONNECTED;
591
592 /* find ME amthi client */
Tomas Winkler07b509b2012-07-23 14:05:39 +0300593 i = mei_me_cl_update_filext(dev, &dev->iamthif_cl,
Oren Weil91f01c62011-05-15 13:43:44 +0300594 &mei_amthi_guid, MEI_IAMTHIF_HOST_CLIENT_ID);
Tomas Winkler07b509b2012-07-23 14:05:39 +0300595 if (i < 0) {
Oren Weil91f01c62011-05-15 13:43:44 +0300596 dev_dbg(&dev->pdev->dev, "failed to find iamthif client.\n");
597 return;
598 }
599
Tomas Winklerc9667bf2011-12-13 23:39:33 +0200600 /* Assign iamthif_mtu to the value received from ME */
Oren Weil91f01c62011-05-15 13:43:44 +0300601
Oren Weil91f01c62011-05-15 13:43:44 +0300602 dev->iamthif_mtu = dev->me_clients[i].props.max_msg_length;
Tomas Winklerc9667bf2011-12-13 23:39:33 +0200603 dev_dbg(&dev->pdev->dev, "IAMTHIF_MTU = %d\n",
Oren Weil91f01c62011-05-15 13:43:44 +0300604 dev->me_clients[i].props.max_msg_length);
605
606 kfree(dev->iamthif_msg_buf);
607 dev->iamthif_msg_buf = NULL;
608
609 /* allocate storage for ME message buffer */
610 msg_buf = kcalloc(dev->iamthif_mtu,
611 sizeof(unsigned char), GFP_KERNEL);
612 if (!msg_buf) {
613 dev_dbg(&dev->pdev->dev, "memory allocation for ME message buffer failed.\n");
614 return;
615 }
616
617 dev->iamthif_msg_buf = msg_buf;
618
Tomas Winkler1ccb7b62012-03-14 14:39:42 +0200619 if (mei_connect(dev, &dev->iamthif_cl)) {
Oren Weil91f01c62011-05-15 13:43:44 +0300620 dev_dbg(&dev->pdev->dev, "Failed to connect to AMTHI client\n");
621 dev->iamthif_cl.state = MEI_FILE_DISCONNECTED;
622 dev->iamthif_cl.host_client_id = 0;
623 } else {
624 dev->iamthif_cl.timer_count = CONNECT_TIMEOUT;
625 }
626}
627
628/**
629 * mei_alloc_file_private - allocates a private file structure and sets it up.
630 * @file: the file structure
631 *
632 * returns The allocated file or NULL on failure
633 */
Tomas Winklerc95efb72011-05-25 17:28:21 +0300634struct mei_cl *mei_cl_allocate(struct mei_device *dev)
Oren Weil91f01c62011-05-15 13:43:44 +0300635{
Tomas Winklerc95efb72011-05-25 17:28:21 +0300636 struct mei_cl *cl;
Oren Weil91f01c62011-05-15 13:43:44 +0300637
Tomas Winklerc95efb72011-05-25 17:28:21 +0300638 cl = kmalloc(sizeof(struct mei_cl), GFP_KERNEL);
639 if (!cl)
Oren Weil91f01c62011-05-15 13:43:44 +0300640 return NULL;
641
Tomas Winklerc95efb72011-05-25 17:28:21 +0300642 mei_cl_init(cl, dev);
Oren Weil91f01c62011-05-15 13:43:44 +0300643
Tomas Winklerc95efb72011-05-25 17:28:21 +0300644 return cl;
Oren Weil91f01c62011-05-15 13:43:44 +0300645}
646
647
648
649/**
650 * mei_disconnect_host_client - sends disconnect message to fw from host client.
651 *
652 * @dev: the device structure
653 * @cl: private data of the file object
654 *
655 * Locking: called under "dev->device_lock" lock
656 *
657 * returns 0 on success, <0 on failure.
658 */
659int mei_disconnect_host_client(struct mei_device *dev, struct mei_cl *cl)
660{
661 int rets, err;
662 long timeout = 15; /* 15 seconds */
663 struct mei_cl_cb *cb;
664
665 if (!dev || !cl)
666 return -ENODEV;
667
668 if (cl->state != MEI_FILE_DISCONNECTING)
669 return 0;
670
671 cb = kzalloc(sizeof(struct mei_cl_cb), GFP_KERNEL);
672 if (!cb)
673 return -ENOMEM;
674
Tomas Winklerfb601ad2012-10-15 12:06:48 +0200675 mei_io_list_init(cb);
Oren Weil91f01c62011-05-15 13:43:44 +0300676 cb->file_private = cl;
677 cb->major_file_operations = MEI_CLOSE;
678 if (dev->mei_host_buffer_is_empty) {
Tomas Winklereb9af0a2011-05-25 17:28:22 +0300679 dev->mei_host_buffer_is_empty = false;
Oren Weil91f01c62011-05-15 13:43:44 +0300680 if (mei_disconnect(dev, cl)) {
Oren Weil91f01c62011-05-15 13:43:44 +0300681 rets = -ENODEV;
682 dev_dbg(&dev->pdev->dev, "failed to call mei_disconnect.\n");
683 goto free;
684 }
Tomas Winkler1ccb7b62012-03-14 14:39:42 +0200685 mdelay(10); /* Wait for hardware disconnection ready */
Tomas Winklerfb601ad2012-10-15 12:06:48 +0200686 list_add_tail(&cb->list, &dev->ctrl_rd_list.list);
Oren Weil91f01c62011-05-15 13:43:44 +0300687 } else {
688 dev_dbg(&dev->pdev->dev, "add disconnect cb to control write list\n");
Tomas Winklerfb601ad2012-10-15 12:06:48 +0200689 list_add_tail(&cb->list, &dev->ctrl_wr_list.list);
690
Oren Weil91f01c62011-05-15 13:43:44 +0300691 }
692 mutex_unlock(&dev->device_lock);
693
694 err = wait_event_timeout(dev->wait_recvd_msg,
695 (MEI_FILE_DISCONNECTED == cl->state),
696 timeout * HZ);
697
698 mutex_lock(&dev->device_lock);
699 if (MEI_FILE_DISCONNECTED == cl->state) {
700 rets = 0;
701 dev_dbg(&dev->pdev->dev, "successfully disconnected from FW client.\n");
702 } else {
703 rets = -ENODEV;
704 if (MEI_FILE_DISCONNECTED != cl->state)
705 dev_dbg(&dev->pdev->dev, "wrong status client disconnect.\n");
706
707 if (err)
708 dev_dbg(&dev->pdev->dev,
709 "wait failed disconnect err=%08x\n",
710 err);
711
712 dev_dbg(&dev->pdev->dev, "failed to disconnect from FW client.\n");
713 }
714
Tomas Winkler0288c7c2011-06-06 10:44:34 +0300715 mei_io_list_flush(&dev->ctrl_rd_list, cl);
716 mei_io_list_flush(&dev->ctrl_wr_list, cl);
Oren Weil91f01c62011-05-15 13:43:44 +0300717free:
Tomas Winkler601a1ef2012-10-09 16:50:20 +0200718 mei_io_cb_free(cb);
Oren Weil91f01c62011-05-15 13:43:44 +0300719 return rets;
720}
721
722/**
723 * mei_remove_client_from_file_list -
724 * removes file private data from device file list
725 *
726 * @dev: the device structure
727 * @host_client_id: host client id to be removed
728 */
729void mei_remove_client_from_file_list(struct mei_device *dev,
730 u8 host_client_id)
731{
732 struct mei_cl *cl_pos = NULL;
733 struct mei_cl *cl_next = NULL;
734 list_for_each_entry_safe(cl_pos, cl_next, &dev->file_list, link) {
735 if (host_client_id == cl_pos->host_client_id) {
736 dev_dbg(&dev->pdev->dev, "remove host client = %d, ME client = %d\n",
737 cl_pos->host_client_id,
738 cl_pos->me_client_id);
739 list_del_init(&cl_pos->link);
740 break;
741 }
742 }
743}