blob: e6951ec4840feef9b0255c9dd9e6b077179f1b5f [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,
Tomas Winkler3870c322012-11-01 21:17:14 +0200187 dev->recvd_msg,
188 mei_secs_to_jiffies(MEI_INTEROP_TIMEOUT));
Oren Weil91f01c62011-05-15 13:43:44 +0300189 mutex_lock(&dev->device_lock);
190 }
191
Tomas Winklera534bb62011-06-13 16:39:31 +0300192 if (err <= 0 && !dev->recvd_msg) {
Tomas Winklerb210d752012-08-07 00:03:56 +0300193 dev->dev_state = MEI_DEV_DISABLED;
Oren Weil91f01c62011-05-15 13:43:44 +0300194 dev_dbg(&dev->pdev->dev,
195 "wait_event_interruptible_timeout failed"
196 "on wait for ME to turn on ME_RDY.\n");
197 ret = -ENODEV;
198 goto out;
199 }
200
201 if (!(((dev->host_hw_state & H_RDY) == H_RDY) &&
202 ((dev->me_hw_state & ME_RDY_HRA) == ME_RDY_HRA))) {
Tomas Winklerb210d752012-08-07 00:03:56 +0300203 dev->dev_state = MEI_DEV_DISABLED;
Oren Weil91f01c62011-05-15 13:43:44 +0300204 dev_dbg(&dev->pdev->dev,
205 "host_hw_state = 0x%08x, me_hw_state = 0x%08x.\n",
206 dev->host_hw_state, dev->me_hw_state);
207
Dan Carpenter8eb73c62011-06-09 10:18:23 +0300208 if (!(dev->host_hw_state & H_RDY))
Oren Weil91f01c62011-05-15 13:43:44 +0300209 dev_dbg(&dev->pdev->dev, "host turn off H_RDY.\n");
210
Dan Carpenter8eb73c62011-06-09 10:18:23 +0300211 if (!(dev->me_hw_state & ME_RDY_HRA))
Oren Weil91f01c62011-05-15 13:43:44 +0300212 dev_dbg(&dev->pdev->dev, "ME turn off ME_RDY.\n");
213
Tomas Winklerd39a4642012-03-19 17:58:42 +0200214 dev_err(&dev->pdev->dev, "link layer initialization failed.\n");
Oren Weil91f01c62011-05-15 13:43:44 +0300215 ret = -ENODEV;
216 goto out;
217 }
218
219 if (dev->version.major_version != HBM_MAJOR_VERSION ||
220 dev->version.minor_version != HBM_MINOR_VERSION) {
221 dev_dbg(&dev->pdev->dev, "MEI start failed.\n");
222 ret = -ENODEV;
223 goto out;
224 }
225
Tomas Winklereb9af0a2011-05-25 17:28:22 +0300226 dev->recvd_msg = false;
Oren Weil91f01c62011-05-15 13:43:44 +0300227 dev_dbg(&dev->pdev->dev, "host_hw_state = 0x%08x, me_hw_state = 0x%08x.\n",
228 dev->host_hw_state, dev->me_hw_state);
229 dev_dbg(&dev->pdev->dev, "ME turn on ME_RDY and host turn on H_RDY.\n");
230 dev_dbg(&dev->pdev->dev, "link layer has been established.\n");
231 dev_dbg(&dev->pdev->dev, "MEI start success.\n");
232 ret = 0;
233
234out:
235 mutex_unlock(&dev->device_lock);
236 return ret;
237}
238
239/**
240 * mei_hw_reset - resets fw via mei csr register.
241 *
242 * @dev: the device structure
243 * @interrupts_enabled: if interrupt should be enabled after reset.
244 */
245static void mei_hw_reset(struct mei_device *dev, int interrupts_enabled)
246{
247 dev->host_hw_state |= (H_RST | H_IG);
248
249 if (interrupts_enabled)
250 mei_enable_interrupts(dev);
251 else
252 mei_disable_interrupts(dev);
253}
254
255/**
256 * mei_reset - resets host and fw.
257 *
258 * @dev: the device structure
259 * @interrupts_enabled: if interrupt should be enabled after reset.
260 */
261void mei_reset(struct mei_device *dev, int interrupts_enabled)
262{
263 struct mei_cl *cl_pos = NULL;
264 struct mei_cl *cl_next = NULL;
265 struct mei_cl_cb *cb_pos = NULL;
266 struct mei_cl_cb *cb_next = NULL;
267 bool unexpected;
268
Tomas Winklerb210d752012-08-07 00:03:56 +0300269 if (dev->dev_state == MEI_DEV_RECOVERING_FROM_RESET) {
Tomas Winklereb9af0a2011-05-25 17:28:22 +0300270 dev->need_reset = true;
Oren Weil91f01c62011-05-15 13:43:44 +0300271 return;
272 }
273
Tomas Winklerb210d752012-08-07 00:03:56 +0300274 unexpected = (dev->dev_state != MEI_DEV_INITIALIZING &&
275 dev->dev_state != MEI_DEV_DISABLED &&
276 dev->dev_state != MEI_DEV_POWER_DOWN &&
277 dev->dev_state != MEI_DEV_POWER_UP);
Oren Weil91f01c62011-05-15 13:43:44 +0300278
279 dev->host_hw_state = mei_hcsr_read(dev);
280
281 dev_dbg(&dev->pdev->dev, "before reset host_hw_state = 0x%08x.\n",
282 dev->host_hw_state);
283
284 mei_hw_reset(dev, interrupts_enabled);
285
286 dev->host_hw_state &= ~H_RST;
287 dev->host_hw_state |= H_IG;
288
289 mei_hcsr_set(dev);
290
291 dev_dbg(&dev->pdev->dev, "currently saved host_hw_state = 0x%08x.\n",
292 dev->host_hw_state);
293
Tomas Winklereb9af0a2011-05-25 17:28:22 +0300294 dev->need_reset = false;
Oren Weil91f01c62011-05-15 13:43:44 +0300295
Tomas Winklerb210d752012-08-07 00:03:56 +0300296 if (dev->dev_state != MEI_DEV_INITIALIZING) {
297 if (dev->dev_state != MEI_DEV_DISABLED &&
298 dev->dev_state != MEI_DEV_POWER_DOWN)
299 dev->dev_state = MEI_DEV_RESETING;
Oren Weil91f01c62011-05-15 13:43:44 +0300300
301 list_for_each_entry_safe(cl_pos,
302 cl_next, &dev->file_list, link) {
303 cl_pos->state = MEI_FILE_DISCONNECTED;
304 cl_pos->mei_flow_ctrl_creds = 0;
305 cl_pos->read_cb = NULL;
306 cl_pos->timer_count = 0;
307 }
308 /* remove entry if already in list */
309 dev_dbg(&dev->pdev->dev, "list del iamthif and wd file list.\n");
310 mei_remove_client_from_file_list(dev,
311 dev->wd_cl.host_client_id);
312
313 mei_remove_client_from_file_list(dev,
314 dev->iamthif_cl.host_client_id);
315
316 mei_reset_iamthif_params(dev);
Oren Weil91f01c62011-05-15 13:43:44 +0300317 dev->extra_write_index = 0;
318 }
319
Tomas Winklercf9673d2011-06-06 10:44:33 +0300320 dev->me_clients_num = 0;
Oren Weil91f01c62011-05-15 13:43:44 +0300321 dev->rd_msg_hdr = 0;
Tomas Winklereb9af0a2011-05-25 17:28:22 +0300322 dev->wd_pending = false;
Oren Weil91f01c62011-05-15 13:43:44 +0300323
324 /* update the state of the registers after reset */
325 dev->host_hw_state = mei_hcsr_read(dev);
326 dev->me_hw_state = mei_mecsr_read(dev);
327
328 dev_dbg(&dev->pdev->dev, "after reset host_hw_state = 0x%08x, me_hw_state = 0x%08x.\n",
329 dev->host_hw_state, dev->me_hw_state);
330
331 if (unexpected)
Tomas Winklerb210d752012-08-07 00:03:56 +0300332 dev_warn(&dev->pdev->dev, "unexpected reset: dev_state = %s\n",
333 mei_dev_state_str(dev->dev_state));
Oren Weil91f01c62011-05-15 13:43:44 +0300334
335 /* Wake up all readings so they can be interrupted */
336 list_for_each_entry_safe(cl_pos, cl_next, &dev->file_list, link) {
337 if (waitqueue_active(&cl_pos->rx_wait)) {
338 dev_dbg(&dev->pdev->dev, "Waking up client!\n");
339 wake_up_interruptible(&cl_pos->rx_wait);
340 }
341 }
342 /* remove all waiting requests */
Tomas Winklerfb601ad2012-10-15 12:06:48 +0200343 list_for_each_entry_safe(cb_pos, cb_next, &dev->write_list.list, list) {
344 list_del(&cb_pos->list);
Tomas Winkler601a1ef2012-10-09 16:50:20 +0200345 mei_io_cb_free(cb_pos);
Oren Weil91f01c62011-05-15 13:43:44 +0300346 }
347}
348
349
350
351/**
352 * host_start_message - mei host sends start message.
353 *
354 * @dev: the device structure
355 *
356 * returns none.
357 */
Tomas Winklerc95efb72011-05-25 17:28:21 +0300358void mei_host_start_message(struct mei_device *dev)
Oren Weil91f01c62011-05-15 13:43:44 +0300359{
360 struct mei_msg_hdr *mei_hdr;
361 struct hbm_host_version_request *host_start_req;
362
363 /* host start message */
364 mei_hdr = (struct mei_msg_hdr *) &dev->wr_msg_buf[0];
365 mei_hdr->host_addr = 0;
366 mei_hdr->me_addr = 0;
367 mei_hdr->length = sizeof(struct hbm_host_version_request);
368 mei_hdr->msg_complete = 1;
369 mei_hdr->reserved = 0;
370
371 host_start_req =
372 (struct hbm_host_version_request *) &dev->wr_msg_buf[1];
373 memset(host_start_req, 0, sizeof(struct hbm_host_version_request));
Tomas Winkler1ca7e782012-02-26 23:18:57 +0200374 host_start_req->hbm_cmd = HOST_START_REQ_CMD;
Oren Weil91f01c62011-05-15 13:43:44 +0300375 host_start_req->host_version.major_version = HBM_MAJOR_VERSION;
376 host_start_req->host_version.minor_version = HBM_MINOR_VERSION;
Tomas Winklereb9af0a2011-05-25 17:28:22 +0300377 dev->recvd_msg = false;
Tomas Winkler1ccb7b62012-03-14 14:39:42 +0200378 if (mei_write_message(dev, mei_hdr, (unsigned char *)host_start_req,
Oren Weil91f01c62011-05-15 13:43:44 +0300379 mei_hdr->length)) {
380 dev_dbg(&dev->pdev->dev, "write send version message to FW fail.\n");
Tomas Winklerb210d752012-08-07 00:03:56 +0300381 dev->dev_state = MEI_DEV_RESETING;
Oren Weil91f01c62011-05-15 13:43:44 +0300382 mei_reset(dev, 1);
383 }
384 dev->init_clients_state = MEI_START_MESSAGE;
Tomas Winkler3870c322012-11-01 21:17:14 +0200385 dev->init_clients_timer = MEI_CLIENTS_INIT_TIMEOUT;
Oren Weil91f01c62011-05-15 13:43:44 +0300386 return ;
387}
388
389/**
390 * host_enum_clients_message - host sends enumeration client request message.
391 *
392 * @dev: the device structure
393 *
394 * returns none.
395 */
Tomas Winklerc95efb72011-05-25 17:28:21 +0300396void mei_host_enum_clients_message(struct mei_device *dev)
Oren Weil91f01c62011-05-15 13:43:44 +0300397{
398 struct mei_msg_hdr *mei_hdr;
399 struct hbm_host_enum_request *host_enum_req;
400 mei_hdr = (struct mei_msg_hdr *) &dev->wr_msg_buf[0];
401 /* enumerate clients */
402 mei_hdr->host_addr = 0;
403 mei_hdr->me_addr = 0;
404 mei_hdr->length = sizeof(struct hbm_host_enum_request);
405 mei_hdr->msg_complete = 1;
406 mei_hdr->reserved = 0;
407
408 host_enum_req = (struct hbm_host_enum_request *) &dev->wr_msg_buf[1];
409 memset(host_enum_req, 0, sizeof(struct hbm_host_enum_request));
Tomas Winkler1ca7e782012-02-26 23:18:57 +0200410 host_enum_req->hbm_cmd = HOST_ENUM_REQ_CMD;
Tomas Winkler1ccb7b62012-03-14 14:39:42 +0200411 if (mei_write_message(dev, mei_hdr, (unsigned char *)host_enum_req,
Oren Weil91f01c62011-05-15 13:43:44 +0300412 mei_hdr->length)) {
Tomas Winklerb210d752012-08-07 00:03:56 +0300413 dev->dev_state = MEI_DEV_RESETING;
Oren Weil91f01c62011-05-15 13:43:44 +0300414 dev_dbg(&dev->pdev->dev, "write send enumeration request message to FW fail.\n");
415 mei_reset(dev, 1);
416 }
417 dev->init_clients_state = MEI_ENUM_CLIENTS_MESSAGE;
Tomas Winkler3870c322012-11-01 21:17:14 +0200418 dev->init_clients_timer = MEI_CLIENTS_INIT_TIMEOUT;
Tomas Winkler1ccb7b62012-03-14 14:39:42 +0200419 return;
Oren Weil91f01c62011-05-15 13:43:44 +0300420}
421
422
423/**
424 * allocate_me_clients_storage - allocates storage for me clients
425 *
426 * @dev: the device structure
427 *
428 * returns none.
429 */
Tomas Winklerc95efb72011-05-25 17:28:21 +0300430void mei_allocate_me_clients_storage(struct mei_device *dev)
Oren Weil91f01c62011-05-15 13:43:44 +0300431{
432 struct mei_me_client *clients;
433 int b;
434
435 /* count how many ME clients we have */
436 for_each_set_bit(b, dev->me_clients_map, MEI_CLIENTS_MAX)
Tomas Winklercf9673d2011-06-06 10:44:33 +0300437 dev->me_clients_num++;
Oren Weil91f01c62011-05-15 13:43:44 +0300438
Tomas Winklercf9673d2011-06-06 10:44:33 +0300439 if (dev->me_clients_num <= 0)
Oren Weil91f01c62011-05-15 13:43:44 +0300440 return ;
441
442
443 if (dev->me_clients != NULL) {
444 kfree(dev->me_clients);
445 dev->me_clients = NULL;
446 }
447 dev_dbg(&dev->pdev->dev, "memory allocation for ME clients size=%zd.\n",
Tomas Winklercf9673d2011-06-06 10:44:33 +0300448 dev->me_clients_num * sizeof(struct mei_me_client));
Oren Weil91f01c62011-05-15 13:43:44 +0300449 /* allocate storage for ME clients representation */
Tomas Winklercf9673d2011-06-06 10:44:33 +0300450 clients = kcalloc(dev->me_clients_num,
Oren Weil91f01c62011-05-15 13:43:44 +0300451 sizeof(struct mei_me_client), GFP_KERNEL);
452 if (!clients) {
453 dev_dbg(&dev->pdev->dev, "memory allocation for ME clients failed.\n");
Tomas Winklerb210d752012-08-07 00:03:56 +0300454 dev->dev_state = MEI_DEV_RESETING;
Oren Weil91f01c62011-05-15 13:43:44 +0300455 mei_reset(dev, 1);
456 return ;
457 }
458 dev->me_clients = clients;
459 return ;
460}
461/**
462 * host_client_properties - reads properties for client
463 *
464 * @dev: the device structure
465 *
Oren Weilabc51b62011-09-21 16:45:30 +0300466 * returns:
467 * < 0 - Error.
468 * = 0 - no more clients.
469 * = 1 - still have clients to send properties request.
Oren Weil91f01c62011-05-15 13:43:44 +0300470 */
Oren Weilabc51b62011-09-21 16:45:30 +0300471int mei_host_client_properties(struct mei_device *dev)
Oren Weil91f01c62011-05-15 13:43:44 +0300472{
473 struct mei_msg_hdr *mei_header;
474 struct hbm_props_request *host_cli_req;
475 int b;
476 u8 client_num = dev->me_client_presentation_num;
477
478 b = dev->me_client_index;
479 b = find_next_bit(dev->me_clients_map, MEI_CLIENTS_MAX, b);
480 if (b < MEI_CLIENTS_MAX) {
481 dev->me_clients[client_num].client_id = b;
482 dev->me_clients[client_num].mei_flow_ctrl_creds = 0;
483 mei_header = (struct mei_msg_hdr *)&dev->wr_msg_buf[0];
484 mei_header->host_addr = 0;
485 mei_header->me_addr = 0;
486 mei_header->length = sizeof(struct hbm_props_request);
487 mei_header->msg_complete = 1;
488 mei_header->reserved = 0;
489
490 host_cli_req = (struct hbm_props_request *)&dev->wr_msg_buf[1];
491
492 memset(host_cli_req, 0, sizeof(struct hbm_props_request));
493
Tomas Winkler1ca7e782012-02-26 23:18:57 +0200494 host_cli_req->hbm_cmd = HOST_CLIENT_PROPERTIES_REQ_CMD;
Oren Weil91f01c62011-05-15 13:43:44 +0300495 host_cli_req->address = b;
496
Tomas Winkler1ccb7b62012-03-14 14:39:42 +0200497 if (mei_write_message(dev, mei_header,
Oren Weil91f01c62011-05-15 13:43:44 +0300498 (unsigned char *)host_cli_req,
499 mei_header->length)) {
Tomas Winklerb210d752012-08-07 00:03:56 +0300500 dev->dev_state = MEI_DEV_RESETING;
Oren Weil91f01c62011-05-15 13:43:44 +0300501 dev_dbg(&dev->pdev->dev, "write send enumeration request message to FW fail.\n");
502 mei_reset(dev, 1);
Oren Weilabc51b62011-09-21 16:45:30 +0300503 return -EIO;
Oren Weil91f01c62011-05-15 13:43:44 +0300504 }
505
Tomas Winkler3870c322012-11-01 21:17:14 +0200506 dev->init_clients_timer = MEI_CLIENTS_INIT_TIMEOUT;
Oren Weil91f01c62011-05-15 13:43:44 +0300507 dev->me_client_index = b;
Oren Weilabc51b62011-09-21 16:45:30 +0300508 return 1;
Oren Weil91f01c62011-05-15 13:43:44 +0300509 }
510
Oren Weilabc51b62011-09-21 16:45:30 +0300511 return 0;
Oren Weil91f01c62011-05-15 13:43:44 +0300512}
513
514/**
515 * mei_init_file_private - initializes private file structure.
516 *
517 * @priv: private file structure to be initialized
518 * @file: the file structure
519 */
Tomas Winklerc95efb72011-05-25 17:28:21 +0300520void mei_cl_init(struct mei_cl *priv, struct mei_device *dev)
Oren Weil91f01c62011-05-15 13:43:44 +0300521{
522 memset(priv, 0, sizeof(struct mei_cl));
523 init_waitqueue_head(&priv->wait);
524 init_waitqueue_head(&priv->rx_wait);
525 init_waitqueue_head(&priv->tx_wait);
526 INIT_LIST_HEAD(&priv->link);
527 priv->reading_state = MEI_IDLE;
528 priv->writing_state = MEI_IDLE;
529 priv->dev = dev;
530}
531
Tomas Winkler07b509b2012-07-23 14:05:39 +0300532int mei_me_cl_by_uuid(const struct mei_device *dev, const uuid_le *cuuid)
Oren Weil91f01c62011-05-15 13:43:44 +0300533{
Tomas Winkler07b509b2012-07-23 14:05:39 +0300534 int i, res = -ENOENT;
Oren Weil91f01c62011-05-15 13:43:44 +0300535
Tomas Winklercf9673d2011-06-06 10:44:33 +0300536 for (i = 0; i < dev->me_clients_num; ++i)
Tomas Winkler07b509b2012-07-23 14:05:39 +0300537 if (uuid_le_cmp(*cuuid,
Oren Weil91f01c62011-05-15 13:43:44 +0300538 dev->me_clients[i].props.protocol_name) == 0) {
539 res = i;
540 break;
541 }
542
543 return res;
544}
545
546
547/**
Tomas Winkler07b509b2012-07-23 14:05:39 +0300548 * mei_me_cl_update_filext - searches for ME client guid
Oren Weil91f01c62011-05-15 13:43:44 +0300549 * sets client_id in mei_file_private if found
550 * @dev: the device structure
Tomas Winkler07b509b2012-07-23 14:05:39 +0300551 * @cl: private file structure to set client_id in
552 * @cuuid: searched uuid of ME client
Oren Weil91f01c62011-05-15 13:43:44 +0300553 * @client_id: id of host client to be set in file private structure
554 *
555 * returns ME client index
556 */
Tomas Winkler07b509b2012-07-23 14:05:39 +0300557int mei_me_cl_update_filext(struct mei_device *dev, struct mei_cl *cl,
558 const uuid_le *cuuid, u8 host_cl_id)
Oren Weil91f01c62011-05-15 13:43:44 +0300559{
560 int i;
561
Tomas Winkler07b509b2012-07-23 14:05:39 +0300562 if (!dev || !cl || !cuuid)
563 return -EINVAL;
Oren Weil91f01c62011-05-15 13:43:44 +0300564
565 /* check for valid client id */
Tomas Winkler07b509b2012-07-23 14:05:39 +0300566 i = mei_me_cl_by_uuid(dev, cuuid);
Oren Weil91f01c62011-05-15 13:43:44 +0300567 if (i >= 0) {
Tomas Winkler07b509b2012-07-23 14:05:39 +0300568 cl->me_client_id = dev->me_clients[i].client_id;
569 cl->state = MEI_FILE_CONNECTING;
570 cl->host_client_id = host_cl_id;
Oren Weil91f01c62011-05-15 13:43:44 +0300571
Tomas Winkler07b509b2012-07-23 14:05:39 +0300572 list_add_tail(&cl->link, &dev->file_list);
Oren Weil91f01c62011-05-15 13:43:44 +0300573 return (u8)i;
574 }
575
Tomas Winkler07b509b2012-07-23 14:05:39 +0300576 return -ENOENT;
Oren Weil91f01c62011-05-15 13:43:44 +0300577}
578
579/**
580 * host_init_iamthif - mei initialization iamthif client.
581 *
582 * @dev: the device structure
583 *
584 */
Tomas Winklerc95efb72011-05-25 17:28:21 +0300585void mei_host_init_iamthif(struct mei_device *dev)
Oren Weil91f01c62011-05-15 13:43:44 +0300586{
Tomas Winkler07b509b2012-07-23 14:05:39 +0300587 int i;
Oren Weil91f01c62011-05-15 13:43:44 +0300588 unsigned char *msg_buf;
589
Tomas Winklerc95efb72011-05-25 17:28:21 +0300590 mei_cl_init(&dev->iamthif_cl, dev);
Oren Weil91f01c62011-05-15 13:43:44 +0300591 dev->iamthif_cl.state = MEI_FILE_DISCONNECTED;
592
593 /* find ME amthi client */
Tomas Winkler07b509b2012-07-23 14:05:39 +0300594 i = mei_me_cl_update_filext(dev, &dev->iamthif_cl,
Oren Weil91f01c62011-05-15 13:43:44 +0300595 &mei_amthi_guid, MEI_IAMTHIF_HOST_CLIENT_ID);
Tomas Winkler07b509b2012-07-23 14:05:39 +0300596 if (i < 0) {
Oren Weil91f01c62011-05-15 13:43:44 +0300597 dev_dbg(&dev->pdev->dev, "failed to find iamthif client.\n");
598 return;
599 }
600
Tomas Winklerc9667bf2011-12-13 23:39:33 +0200601 /* Assign iamthif_mtu to the value received from ME */
Oren Weil91f01c62011-05-15 13:43:44 +0300602
Oren Weil91f01c62011-05-15 13:43:44 +0300603 dev->iamthif_mtu = dev->me_clients[i].props.max_msg_length;
Tomas Winklerc9667bf2011-12-13 23:39:33 +0200604 dev_dbg(&dev->pdev->dev, "IAMTHIF_MTU = %d\n",
Oren Weil91f01c62011-05-15 13:43:44 +0300605 dev->me_clients[i].props.max_msg_length);
606
607 kfree(dev->iamthif_msg_buf);
608 dev->iamthif_msg_buf = NULL;
609
610 /* allocate storage for ME message buffer */
611 msg_buf = kcalloc(dev->iamthif_mtu,
612 sizeof(unsigned char), GFP_KERNEL);
613 if (!msg_buf) {
614 dev_dbg(&dev->pdev->dev, "memory allocation for ME message buffer failed.\n");
615 return;
616 }
617
618 dev->iamthif_msg_buf = msg_buf;
619
Tomas Winkler1ccb7b62012-03-14 14:39:42 +0200620 if (mei_connect(dev, &dev->iamthif_cl)) {
Oren Weil91f01c62011-05-15 13:43:44 +0300621 dev_dbg(&dev->pdev->dev, "Failed to connect to AMTHI client\n");
622 dev->iamthif_cl.state = MEI_FILE_DISCONNECTED;
623 dev->iamthif_cl.host_client_id = 0;
624 } else {
Tomas Winkler3870c322012-11-01 21:17:14 +0200625 dev->iamthif_cl.timer_count = MEI_CONNECT_TIMEOUT;
Oren Weil91f01c62011-05-15 13:43:44 +0300626 }
627}
628
629/**
630 * mei_alloc_file_private - allocates a private file structure and sets it up.
631 * @file: the file structure
632 *
633 * returns The allocated file or NULL on failure
634 */
Tomas Winklerc95efb72011-05-25 17:28:21 +0300635struct mei_cl *mei_cl_allocate(struct mei_device *dev)
Oren Weil91f01c62011-05-15 13:43:44 +0300636{
Tomas Winklerc95efb72011-05-25 17:28:21 +0300637 struct mei_cl *cl;
Oren Weil91f01c62011-05-15 13:43:44 +0300638
Tomas Winklerc95efb72011-05-25 17:28:21 +0300639 cl = kmalloc(sizeof(struct mei_cl), GFP_KERNEL);
640 if (!cl)
Oren Weil91f01c62011-05-15 13:43:44 +0300641 return NULL;
642
Tomas Winklerc95efb72011-05-25 17:28:21 +0300643 mei_cl_init(cl, dev);
Oren Weil91f01c62011-05-15 13:43:44 +0300644
Tomas Winklerc95efb72011-05-25 17:28:21 +0300645 return cl;
Oren Weil91f01c62011-05-15 13:43:44 +0300646}
647
648
649
650/**
651 * mei_disconnect_host_client - sends disconnect message to fw from host client.
652 *
653 * @dev: the device structure
654 * @cl: private data of the file object
655 *
656 * Locking: called under "dev->device_lock" lock
657 *
658 * returns 0 on success, <0 on failure.
659 */
660int mei_disconnect_host_client(struct mei_device *dev, struct mei_cl *cl)
661{
Oren Weil91f01c62011-05-15 13:43:44 +0300662 struct mei_cl_cb *cb;
Tomas Winkler3870c322012-11-01 21:17:14 +0200663 int rets, err;
Oren Weil91f01c62011-05-15 13:43:44 +0300664
665 if (!dev || !cl)
666 return -ENODEV;
667
668 if (cl->state != MEI_FILE_DISCONNECTING)
669 return 0;
670
Tomas Winkler664df382012-10-11 16:35:08 +0200671 cb = mei_io_cb_init(cl, NULL);
Oren Weil91f01c62011-05-15 13:43:44 +0300672 if (!cb)
673 return -ENOMEM;
674
Oren Weil91f01c62011-05-15 13:43:44 +0300675 cb->major_file_operations = MEI_CLOSE;
676 if (dev->mei_host_buffer_is_empty) {
Tomas Winklereb9af0a2011-05-25 17:28:22 +0300677 dev->mei_host_buffer_is_empty = false;
Oren Weil91f01c62011-05-15 13:43:44 +0300678 if (mei_disconnect(dev, cl)) {
Oren Weil91f01c62011-05-15 13:43:44 +0300679 rets = -ENODEV;
680 dev_dbg(&dev->pdev->dev, "failed to call mei_disconnect.\n");
681 goto free;
682 }
Tomas Winkler1ccb7b62012-03-14 14:39:42 +0200683 mdelay(10); /* Wait for hardware disconnection ready */
Tomas Winklerfb601ad2012-10-15 12:06:48 +0200684 list_add_tail(&cb->list, &dev->ctrl_rd_list.list);
Oren Weil91f01c62011-05-15 13:43:44 +0300685 } else {
686 dev_dbg(&dev->pdev->dev, "add disconnect cb to control write list\n");
Tomas Winklerfb601ad2012-10-15 12:06:48 +0200687 list_add_tail(&cb->list, &dev->ctrl_wr_list.list);
688
Oren Weil91f01c62011-05-15 13:43:44 +0300689 }
690 mutex_unlock(&dev->device_lock);
691
692 err = wait_event_timeout(dev->wait_recvd_msg,
Tomas Winkler3870c322012-11-01 21:17:14 +0200693 MEI_FILE_DISCONNECTED == cl->state,
694 mei_secs_to_jiffies(MEI_CL_CONNECT_TIMEOUT));
Oren Weil91f01c62011-05-15 13:43:44 +0300695
696 mutex_lock(&dev->device_lock);
697 if (MEI_FILE_DISCONNECTED == cl->state) {
698 rets = 0;
699 dev_dbg(&dev->pdev->dev, "successfully disconnected from FW client.\n");
700 } else {
701 rets = -ENODEV;
702 if (MEI_FILE_DISCONNECTED != cl->state)
703 dev_dbg(&dev->pdev->dev, "wrong status client disconnect.\n");
704
705 if (err)
706 dev_dbg(&dev->pdev->dev,
707 "wait failed disconnect err=%08x\n",
708 err);
709
710 dev_dbg(&dev->pdev->dev, "failed to disconnect from FW client.\n");
711 }
712
Tomas Winkler0288c7c2011-06-06 10:44:34 +0300713 mei_io_list_flush(&dev->ctrl_rd_list, cl);
714 mei_io_list_flush(&dev->ctrl_wr_list, cl);
Oren Weil91f01c62011-05-15 13:43:44 +0300715free:
Tomas Winkler601a1ef2012-10-09 16:50:20 +0200716 mei_io_cb_free(cb);
Oren Weil91f01c62011-05-15 13:43:44 +0300717 return rets;
718}
719
720/**
721 * mei_remove_client_from_file_list -
722 * removes file private data from device file list
723 *
724 * @dev: the device structure
725 * @host_client_id: host client id to be removed
726 */
727void mei_remove_client_from_file_list(struct mei_device *dev,
728 u8 host_client_id)
729{
730 struct mei_cl *cl_pos = NULL;
731 struct mei_cl *cl_next = NULL;
732 list_for_each_entry_safe(cl_pos, cl_next, &dev->file_list, link) {
733 if (host_client_id == cl_pos->host_client_id) {
734 dev_dbg(&dev->pdev->dev, "remove host client = %d, ME client = %d\n",
735 cl_pos->host_client_id,
736 cl_pos->me_client_id);
737 list_del_init(&cl_pos->link);
738 break;
739 }
740 }
741}