blob: d6fe278347fcf1ee53389bd5cb0e1e35df9023d2 [file] [log] [blame]
Oren Weilab841162011-05-15 13:43:41 +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 Weilab841162011-05-15 13:43:41 +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
Tomas Winkler2f3d2b42012-03-19 22:38:13 +020017#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
18
Oren Weilab841162011-05-15 13:43:41 +030019#include <linux/module.h>
20#include <linux/moduleparam.h>
21#include <linux/kernel.h>
22#include <linux/device.h>
23#include <linux/fs.h>
24#include <linux/errno.h>
25#include <linux/types.h>
26#include <linux/fcntl.h>
27#include <linux/aio.h>
28#include <linux/pci.h>
29#include <linux/poll.h>
30#include <linux/init.h>
31#include <linux/ioctl.h>
32#include <linux/cdev.h>
Oren Weilab841162011-05-15 13:43:41 +030033#include <linux/sched.h>
34#include <linux/uuid.h>
35#include <linux/compat.h>
36#include <linux/jiffies.h>
37#include <linux/interrupt.h>
Oren Weil5b881e32011-11-13 09:41:14 +020038#include <linux/miscdevice.h>
Oren Weilab841162011-05-15 13:43:41 +030039
40#include "mei_dev.h"
Tomas Winkler4f3afe12012-05-09 16:38:59 +030041#include <linux/mei.h>
Oren Weilab841162011-05-15 13:43:41 +030042#include "interface.h"
Oren Weilab841162011-05-15 13:43:41 +030043
Tomas Winklerdaed6b52012-08-17 09:54:23 +030044/* AMT device is a singleton on the platform */
45static struct pci_dev *mei_pdev;
Oren Weilab841162011-05-15 13:43:41 +030046
Oren Weilab841162011-05-15 13:43:41 +030047/* mei_pci_tbl - PCI Device ID Table */
48static DEFINE_PCI_DEVICE_TABLE(mei_pci_tbl) = {
49 {PCI_DEVICE(PCI_VENDOR_ID_INTEL, MEI_DEV_ID_82946GZ)},
50 {PCI_DEVICE(PCI_VENDOR_ID_INTEL, MEI_DEV_ID_82G35)},
51 {PCI_DEVICE(PCI_VENDOR_ID_INTEL, MEI_DEV_ID_82Q965)},
52 {PCI_DEVICE(PCI_VENDOR_ID_INTEL, MEI_DEV_ID_82G965)},
53 {PCI_DEVICE(PCI_VENDOR_ID_INTEL, MEI_DEV_ID_82GM965)},
54 {PCI_DEVICE(PCI_VENDOR_ID_INTEL, MEI_DEV_ID_82GME965)},
55 {PCI_DEVICE(PCI_VENDOR_ID_INTEL, MEI_DEV_ID_ICH9_82Q35)},
56 {PCI_DEVICE(PCI_VENDOR_ID_INTEL, MEI_DEV_ID_ICH9_82G33)},
57 {PCI_DEVICE(PCI_VENDOR_ID_INTEL, MEI_DEV_ID_ICH9_82Q33)},
58 {PCI_DEVICE(PCI_VENDOR_ID_INTEL, MEI_DEV_ID_ICH9_82X38)},
59 {PCI_DEVICE(PCI_VENDOR_ID_INTEL, MEI_DEV_ID_ICH9_3200)},
60 {PCI_DEVICE(PCI_VENDOR_ID_INTEL, MEI_DEV_ID_ICH9_6)},
61 {PCI_DEVICE(PCI_VENDOR_ID_INTEL, MEI_DEV_ID_ICH9_7)},
62 {PCI_DEVICE(PCI_VENDOR_ID_INTEL, MEI_DEV_ID_ICH9_8)},
63 {PCI_DEVICE(PCI_VENDOR_ID_INTEL, MEI_DEV_ID_ICH9_9)},
64 {PCI_DEVICE(PCI_VENDOR_ID_INTEL, MEI_DEV_ID_ICH9_10)},
65 {PCI_DEVICE(PCI_VENDOR_ID_INTEL, MEI_DEV_ID_ICH9M_1)},
66 {PCI_DEVICE(PCI_VENDOR_ID_INTEL, MEI_DEV_ID_ICH9M_2)},
67 {PCI_DEVICE(PCI_VENDOR_ID_INTEL, MEI_DEV_ID_ICH9M_3)},
68 {PCI_DEVICE(PCI_VENDOR_ID_INTEL, MEI_DEV_ID_ICH9M_4)},
69 {PCI_DEVICE(PCI_VENDOR_ID_INTEL, MEI_DEV_ID_ICH10_1)},
70 {PCI_DEVICE(PCI_VENDOR_ID_INTEL, MEI_DEV_ID_ICH10_2)},
71 {PCI_DEVICE(PCI_VENDOR_ID_INTEL, MEI_DEV_ID_ICH10_3)},
72 {PCI_DEVICE(PCI_VENDOR_ID_INTEL, MEI_DEV_ID_ICH10_4)},
73 {PCI_DEVICE(PCI_VENDOR_ID_INTEL, MEI_DEV_ID_IBXPK_1)},
74 {PCI_DEVICE(PCI_VENDOR_ID_INTEL, MEI_DEV_ID_IBXPK_2)},
75 {PCI_DEVICE(PCI_VENDOR_ID_INTEL, MEI_DEV_ID_CPT_1)},
76 {PCI_DEVICE(PCI_VENDOR_ID_INTEL, MEI_DEV_ID_PBG_1)},
77 {PCI_DEVICE(PCI_VENDOR_ID_INTEL, MEI_DEV_ID_PPT_1)},
78 {PCI_DEVICE(PCI_VENDOR_ID_INTEL, MEI_DEV_ID_PPT_2)},
79 {PCI_DEVICE(PCI_VENDOR_ID_INTEL, MEI_DEV_ID_PPT_3)},
80
81 /* required last entry */
82 {0, }
83};
84
85MODULE_DEVICE_TABLE(pci, mei_pci_tbl);
86
87static DEFINE_MUTEX(mei_mutex);
88
Oren Weilab841162011-05-15 13:43:41 +030089
90/**
91 * mei_clear_list - removes all callbacks associated with file
92 * from mei_cb_list
93 *
94 * @dev: device structure.
95 * @file: file structure
96 * @mei_cb_list: callbacks list
97 *
98 * mei_clear_list is called to clear resources associated with file
99 * when application calls close function or Ctrl-C was pressed
100 *
101 * returns true if callback removed from the list, false otherwise
102 */
103static bool mei_clear_list(struct mei_device *dev,
104 struct file *file, struct list_head *mei_cb_list)
105{
106 struct mei_cl_cb *cb_pos = NULL;
107 struct mei_cl_cb *cb_next = NULL;
108 struct file *file_temp;
109 bool removed = false;
110
111 /* list all list member */
112 list_for_each_entry_safe(cb_pos, cb_next, mei_cb_list, cb_list) {
113 file_temp = (struct file *)cb_pos->file_object;
114 /* check if list member associated with a file */
115 if (file_temp == file) {
116 /* remove member from the list */
117 list_del(&cb_pos->cb_list);
118 /* check if cb equal to current iamthif cb */
119 if (dev->iamthif_current_cb == cb_pos) {
120 dev->iamthif_current_cb = NULL;
121 /* send flow control to iamthif client */
122 mei_send_flow_control(dev, &dev->iamthif_cl);
123 }
124 /* free all allocated buffers */
125 mei_free_cb_private(cb_pos);
126 cb_pos = NULL;
127 removed = true;
128 }
129 }
130 return removed;
131}
132
133/**
134 * mei_clear_lists - removes all callbacks associated with file
135 *
136 * @dev: device structure
137 * @file: file structure
138 *
139 * mei_clear_lists is called to clear resources associated with file
140 * when application calls close function or Ctrl-C was pressed
141 *
142 * returns true if callback removed from the list, false otherwise
143 */
144static bool mei_clear_lists(struct mei_device *dev, struct file *file)
145{
146 bool removed = false;
147
148 /* remove callbacks associated with a file */
149 mei_clear_list(dev, file, &dev->amthi_cmd_list.mei_cb.cb_list);
150 if (mei_clear_list(dev, file,
151 &dev->amthi_read_complete_list.mei_cb.cb_list))
152 removed = true;
153
154 mei_clear_list(dev, file, &dev->ctrl_rd_list.mei_cb.cb_list);
155
156 if (mei_clear_list(dev, file, &dev->ctrl_wr_list.mei_cb.cb_list))
157 removed = true;
158
159 if (mei_clear_list(dev, file, &dev->write_waiting_list.mei_cb.cb_list))
160 removed = true;
161
162 if (mei_clear_list(dev, file, &dev->write_list.mei_cb.cb_list))
163 removed = true;
164
165 /* check if iamthif_current_cb not NULL */
166 if (dev->iamthif_current_cb && !removed) {
167 /* check file and iamthif current cb association */
168 if (dev->iamthif_current_cb->file_object == file) {
169 /* remove cb */
170 mei_free_cb_private(dev->iamthif_current_cb);
171 dev->iamthif_current_cb = NULL;
172 removed = true;
173 }
174 }
175 return removed;
176}
177/**
178 * find_read_list_entry - find read list entry
179 *
180 * @dev: device structure
181 * @file: pointer to file structure
182 *
183 * returns cb on success, NULL on error
184 */
185static struct mei_cl_cb *find_read_list_entry(
186 struct mei_device *dev,
187 struct mei_cl *cl)
188{
Tomas Winklerb7cd2d92011-11-27 21:43:34 +0200189 struct mei_cl_cb *pos = NULL;
190 struct mei_cl_cb *next = NULL;
Oren Weilab841162011-05-15 13:43:41 +0300191
Tomas Winklerb7cd2d92011-11-27 21:43:34 +0200192 dev_dbg(&dev->pdev->dev, "remove read_list CB\n");
193 list_for_each_entry_safe(pos, next,
194 &dev->read_list.mei_cb.cb_list, cb_list) {
195 struct mei_cl *cl_temp;
196 cl_temp = (struct mei_cl *)pos->file_private;
Oren Weilab841162011-05-15 13:43:41 +0300197
Tomas Winklerb7cd2d92011-11-27 21:43:34 +0200198 if (mei_cl_cmp_id(cl, cl_temp))
199 return pos;
Oren Weilab841162011-05-15 13:43:41 +0300200 }
201 return NULL;
202}
203
204/**
205 * mei_open - the open function
206 *
207 * @inode: pointer to inode structure
208 * @file: pointer to file structure
209 *
210 * returns 0 on success, <0 on error
211 */
212static int mei_open(struct inode *inode, struct file *file)
213{
214 struct mei_cl *cl;
Oren Weilab841162011-05-15 13:43:41 +0300215 struct mei_device *dev;
Tomas Winkler6f37aca2011-11-13 09:41:15 +0200216 unsigned long cl_id;
217 int err;
Oren Weilab841162011-05-15 13:43:41 +0300218
219 err = -ENODEV;
Tomas Winklerdaed6b52012-08-17 09:54:23 +0300220 if (!mei_pdev)
Oren Weilab841162011-05-15 13:43:41 +0300221 goto out;
222
Tomas Winklerdaed6b52012-08-17 09:54:23 +0300223 dev = pci_get_drvdata(mei_pdev);
Oren Weil5b881e32011-11-13 09:41:14 +0200224 if (!dev)
Oren Weilab841162011-05-15 13:43:41 +0300225 goto out;
226
227 mutex_lock(&dev->device_lock);
228 err = -ENOMEM;
Tomas Winklerc95efb72011-05-25 17:28:21 +0300229 cl = mei_cl_allocate(dev);
Oren Weilab841162011-05-15 13:43:41 +0300230 if (!cl)
Alexey Khoroshilov303dfbf2011-08-31 00:41:14 +0400231 goto out_unlock;
Oren Weilab841162011-05-15 13:43:41 +0300232
233 err = -ENODEV;
Tomas Winklerb210d752012-08-07 00:03:56 +0300234 if (dev->dev_state != MEI_DEV_ENABLED) {
235 dev_dbg(&dev->pdev->dev, "dev_state != MEI_ENABLED dev_state = %s\n",
236 mei_dev_state_str(dev->dev_state));
Oren Weilab841162011-05-15 13:43:41 +0300237 goto out_unlock;
238 }
239 err = -EMFILE;
240 if (dev->open_handle_count >= MEI_MAX_OPEN_HANDLE_COUNT)
241 goto out_unlock;
242
Tomas Winkler6f37aca2011-11-13 09:41:15 +0200243 cl_id = find_first_zero_bit(dev->host_clients_map, MEI_CLIENTS_MAX);
244 if (cl_id >= MEI_CLIENTS_MAX)
Oren Weilab841162011-05-15 13:43:41 +0300245 goto out_unlock;
246
Tomas Winkler6f37aca2011-11-13 09:41:15 +0200247 cl->host_client_id = cl_id;
248
Oren Weilab841162011-05-15 13:43:41 +0300249 dev_dbg(&dev->pdev->dev, "client_id = %d\n", cl->host_client_id);
250
251 dev->open_handle_count++;
Tomas Winkler6f37aca2011-11-13 09:41:15 +0200252
Oren Weilab841162011-05-15 13:43:41 +0300253 list_add_tail(&cl->link, &dev->file_list);
254
255 set_bit(cl->host_client_id, dev->host_clients_map);
256 cl->state = MEI_FILE_INITIALIZING;
257 cl->sm_state = 0;
258
259 file->private_data = cl;
260 mutex_unlock(&dev->device_lock);
261
Oren Weil5b881e32011-11-13 09:41:14 +0200262 return nonseekable_open(inode, file);
Oren Weilab841162011-05-15 13:43:41 +0300263
264out_unlock:
265 mutex_unlock(&dev->device_lock);
266 kfree(cl);
267out:
268 return err;
269}
270
271/**
272 * mei_release - the release function
273 *
274 * @inode: pointer to inode structure
275 * @file: pointer to file structure
276 *
277 * returns 0 on success, <0 on error
278 */
279static int mei_release(struct inode *inode, struct file *file)
280{
281 struct mei_cl *cl = file->private_data;
282 struct mei_cl_cb *cb;
283 struct mei_device *dev;
284 int rets = 0;
285
286 if (WARN_ON(!cl || !cl->dev))
287 return -ENODEV;
288
289 dev = cl->dev;
290
291 mutex_lock(&dev->device_lock);
292 if (cl != &dev->iamthif_cl) {
293 if (cl->state == MEI_FILE_CONNECTED) {
294 cl->state = MEI_FILE_DISCONNECTING;
295 dev_dbg(&dev->pdev->dev,
296 "disconnecting client host client = %d, "
297 "ME client = %d\n",
298 cl->host_client_id,
299 cl->me_client_id);
300 rets = mei_disconnect_host_client(dev, cl);
301 }
Tomas Winkler0288c7c2011-06-06 10:44:34 +0300302 mei_cl_flush_queues(cl);
Oren Weilab841162011-05-15 13:43:41 +0300303 dev_dbg(&dev->pdev->dev, "remove client host client = %d, ME client = %d\n",
304 cl->host_client_id,
305 cl->me_client_id);
306
307 if (dev->open_handle_count > 0) {
Tomas Winkler441ab502011-12-13 23:39:34 +0200308 clear_bit(cl->host_client_id, dev->host_clients_map);
Oren Weilab841162011-05-15 13:43:41 +0300309 dev->open_handle_count--;
310 }
311 mei_remove_client_from_file_list(dev, cl->host_client_id);
312
313 /* free read cb */
314 cb = NULL;
315 if (cl->read_cb) {
316 cb = find_read_list_entry(dev, cl);
317 /* Remove entry from read list */
318 if (cb)
319 list_del(&cb->cb_list);
320
321 cb = cl->read_cb;
322 cl->read_cb = NULL;
323 }
324
325 file->private_data = NULL;
326
327 if (cb) {
328 mei_free_cb_private(cb);
329 cb = NULL;
330 }
331
332 kfree(cl);
333 } else {
334 if (dev->open_handle_count > 0)
335 dev->open_handle_count--;
336
337 if (dev->iamthif_file_object == file &&
338 dev->iamthif_state != MEI_IAMTHIF_IDLE) {
339
340 dev_dbg(&dev->pdev->dev, "amthi canceled iamthif state %d\n",
341 dev->iamthif_state);
Tomas Winklereb9af0a2011-05-25 17:28:22 +0300342 dev->iamthif_canceled = true;
Oren Weilab841162011-05-15 13:43:41 +0300343 if (dev->iamthif_state == MEI_IAMTHIF_READ_COMPLETE) {
344 dev_dbg(&dev->pdev->dev, "run next amthi iamthif cb\n");
Tomas Winklerc95efb72011-05-25 17:28:21 +0300345 mei_run_next_iamthif_cmd(dev);
Oren Weilab841162011-05-15 13:43:41 +0300346 }
347 }
348
349 if (mei_clear_lists(dev, file))
350 dev->iamthif_state = MEI_IAMTHIF_IDLE;
351
352 }
353 mutex_unlock(&dev->device_lock);
354 return rets;
355}
356
357
358/**
359 * mei_read - the read function.
360 *
361 * @file: pointer to file structure
362 * @ubuf: pointer to user buffer
363 * @length: buffer length
364 * @offset: data offset in buffer
365 *
366 * returns >=0 data length on success , <0 on error
367 */
368static ssize_t mei_read(struct file *file, char __user *ubuf,
Tomas Winkler441ab502011-12-13 23:39:34 +0200369 size_t length, loff_t *offset)
Oren Weilab841162011-05-15 13:43:41 +0300370{
371 struct mei_cl *cl = file->private_data;
372 struct mei_cl_cb *cb_pos = NULL;
373 struct mei_cl_cb *cb = NULL;
374 struct mei_device *dev;
375 int i;
376 int rets;
377 int err;
378
379
380 if (WARN_ON(!cl || !cl->dev))
381 return -ENODEV;
382
383 dev = cl->dev;
384
385 mutex_lock(&dev->device_lock);
Tomas Winklerb210d752012-08-07 00:03:56 +0300386 if (dev->dev_state != MEI_DEV_ENABLED) {
Oren Weilab841162011-05-15 13:43:41 +0300387 rets = -ENODEV;
388 goto out;
389 }
390
391 if ((cl->sm_state & MEI_WD_STATE_INDEPENDENCE_MSG_SENT) == 0) {
392 /* Do not allow to read watchdog client */
Tomas Winkler07b509b2012-07-23 14:05:39 +0300393 i = mei_me_cl_by_uuid(dev, &mei_wd_guid);
Oren Weilab841162011-05-15 13:43:41 +0300394 if (i >= 0) {
395 struct mei_me_client *me_client = &dev->me_clients[i];
Oren Weilab841162011-05-15 13:43:41 +0300396 if (cl->me_client_id == me_client->client_id) {
397 rets = -EBADF;
398 goto out;
399 }
400 }
401 } else {
402 cl->sm_state &= ~MEI_WD_STATE_INDEPENDENCE_MSG_SENT;
403 }
404
405 if (cl == &dev->iamthif_cl) {
406 rets = amthi_read(dev, file, ubuf, length, offset);
407 goto out;
408 }
409
410 if (cl->read_cb && cl->read_cb->information > *offset) {
411 cb = cl->read_cb;
412 goto copy_buffer;
413 } else if (cl->read_cb && cl->read_cb->information > 0 &&
414 cl->read_cb->information <= *offset) {
415 cb = cl->read_cb;
416 rets = 0;
417 goto free;
418 } else if ((!cl->read_cb || !cl->read_cb->information) &&
419 *offset > 0) {
Justin P. Mattock5f9092f32012-03-12 07:18:09 -0700420 /*Offset needs to be cleaned for contiguous reads*/
Oren Weilab841162011-05-15 13:43:41 +0300421 *offset = 0;
422 rets = 0;
423 goto out;
424 }
425
426 err = mei_start_read(dev, cl);
427 if (err && err != -EBUSY) {
428 dev_dbg(&dev->pdev->dev,
429 "mei start read failure with status = %d\n", err);
430 rets = err;
431 goto out;
432 }
433
434 if (MEI_READ_COMPLETE != cl->reading_state &&
435 !waitqueue_active(&cl->rx_wait)) {
436 if (file->f_flags & O_NONBLOCK) {
437 rets = -EAGAIN;
438 goto out;
439 }
440
441 mutex_unlock(&dev->device_lock);
442
443 if (wait_event_interruptible(cl->rx_wait,
444 (MEI_READ_COMPLETE == cl->reading_state ||
445 MEI_FILE_INITIALIZING == cl->state ||
446 MEI_FILE_DISCONNECTED == cl->state ||
447 MEI_FILE_DISCONNECTING == cl->state))) {
448 if (signal_pending(current))
449 return -EINTR;
450 return -ERESTARTSYS;
451 }
452
453 mutex_lock(&dev->device_lock);
454 if (MEI_FILE_INITIALIZING == cl->state ||
455 MEI_FILE_DISCONNECTED == cl->state ||
456 MEI_FILE_DISCONNECTING == cl->state) {
457 rets = -EBUSY;
458 goto out;
459 }
460 }
461
462 cb = cl->read_cb;
463
464 if (!cb) {
465 rets = -ENODEV;
466 goto out;
467 }
468 if (cl->reading_state != MEI_READ_COMPLETE) {
469 rets = 0;
470 goto out;
471 }
472 /* now copy the data to user space */
473copy_buffer:
474 dev_dbg(&dev->pdev->dev, "cb->response_buffer size - %d\n",
475 cb->response_buffer.size);
476 dev_dbg(&dev->pdev->dev, "cb->information - %lu\n",
477 cb->information);
478 if (length == 0 || ubuf == NULL || *offset > cb->information) {
479 rets = -EMSGSIZE;
480 goto free;
481 }
482
Justin P. Mattock5f9092f32012-03-12 07:18:09 -0700483 /* length is being truncated to PAGE_SIZE, however, */
Oren Weilab841162011-05-15 13:43:41 +0300484 /* information size may be longer */
485 length = min_t(size_t, length, (cb->information - *offset));
486
Tomas Winkler441ab502011-12-13 23:39:34 +0200487 if (copy_to_user(ubuf, cb->response_buffer.data + *offset, length)) {
Oren Weilab841162011-05-15 13:43:41 +0300488 rets = -EFAULT;
489 goto free;
490 }
491
492 rets = length;
493 *offset += length;
494 if ((unsigned long)*offset < cb->information)
495 goto out;
496
497free:
498 cb_pos = find_read_list_entry(dev, cl);
499 /* Remove entry from read list */
500 if (cb_pos)
501 list_del(&cb_pos->cb_list);
502 mei_free_cb_private(cb);
503 cl->reading_state = MEI_IDLE;
504 cl->read_cb = NULL;
505 cl->read_pending = 0;
506out:
507 dev_dbg(&dev->pdev->dev, "end mei read rets= %d\n", rets);
508 mutex_unlock(&dev->device_lock);
509 return rets;
510}
511
512/**
513 * mei_write - the write function.
514 *
515 * @file: pointer to file structure
516 * @ubuf: pointer to user buffer
517 * @length: buffer length
518 * @offset: data offset in buffer
519 *
520 * returns >=0 data length on success , <0 on error
521 */
522static ssize_t mei_write(struct file *file, const char __user *ubuf,
Tomas Winkler441ab502011-12-13 23:39:34 +0200523 size_t length, loff_t *offset)
Oren Weilab841162011-05-15 13:43:41 +0300524{
525 struct mei_cl *cl = file->private_data;
526 struct mei_cl_cb *write_cb = NULL;
527 struct mei_msg_hdr mei_hdr;
528 struct mei_device *dev;
529 unsigned long timeout = 0;
530 int rets;
531 int i;
532
533 if (WARN_ON(!cl || !cl->dev))
534 return -ENODEV;
535
536 dev = cl->dev;
537
538 mutex_lock(&dev->device_lock);
539
Tomas Winklerb210d752012-08-07 00:03:56 +0300540 if (dev->dev_state != MEI_DEV_ENABLED) {
Oren Weilab841162011-05-15 13:43:41 +0300541 mutex_unlock(&dev->device_lock);
542 return -ENODEV;
543 }
544
545 if (cl == &dev->iamthif_cl) {
546 write_cb = find_amthi_read_list_entry(dev, file);
547
548 if (write_cb) {
549 timeout = write_cb->read_time +
550 msecs_to_jiffies(IAMTHIF_READ_TIMER);
551
552 if (time_after(jiffies, timeout) ||
553 cl->reading_state == MEI_READ_COMPLETE) {
554 *offset = 0;
555 list_del(&write_cb->cb_list);
556 mei_free_cb_private(write_cb);
557 write_cb = NULL;
558 }
559 }
560 }
561
562 /* free entry used in read */
563 if (cl->reading_state == MEI_READ_COMPLETE) {
564 *offset = 0;
565 write_cb = find_read_list_entry(dev, cl);
566 if (write_cb) {
567 list_del(&write_cb->cb_list);
568 mei_free_cb_private(write_cb);
569 write_cb = NULL;
570 cl->reading_state = MEI_IDLE;
571 cl->read_cb = NULL;
572 cl->read_pending = 0;
573 }
Tomas Winkler441ab502011-12-13 23:39:34 +0200574 } else if (cl->reading_state == MEI_IDLE && !cl->read_pending)
Oren Weilab841162011-05-15 13:43:41 +0300575 *offset = 0;
576
577
578 write_cb = kzalloc(sizeof(struct mei_cl_cb), GFP_KERNEL);
579 if (!write_cb) {
580 mutex_unlock(&dev->device_lock);
581 return -ENOMEM;
582 }
583
584 write_cb->file_object = file;
585 write_cb->file_private = cl;
586 write_cb->request_buffer.data = kmalloc(length, GFP_KERNEL);
587 rets = -ENOMEM;
588 if (!write_cb->request_buffer.data)
589 goto unlock_dev;
590
591 dev_dbg(&dev->pdev->dev, "length =%d\n", (int) length);
592
593 rets = -EFAULT;
594 if (copy_from_user(write_cb->request_buffer.data, ubuf, length))
595 goto unlock_dev;
596
597 cl->sm_state = 0;
598 if (length == 4 &&
599 ((memcmp(mei_wd_state_independence_msg[0],
600 write_cb->request_buffer.data, 4) == 0) ||
601 (memcmp(mei_wd_state_independence_msg[1],
602 write_cb->request_buffer.data, 4) == 0) ||
603 (memcmp(mei_wd_state_independence_msg[2],
604 write_cb->request_buffer.data, 4) == 0)))
605 cl->sm_state |= MEI_WD_STATE_INDEPENDENCE_MSG_SENT;
606
607 INIT_LIST_HEAD(&write_cb->cb_list);
608 if (cl == &dev->iamthif_cl) {
609 write_cb->response_buffer.data =
610 kmalloc(dev->iamthif_mtu, GFP_KERNEL);
611 if (!write_cb->response_buffer.data) {
612 rets = -ENOMEM;
613 goto unlock_dev;
614 }
Tomas Winklerb210d752012-08-07 00:03:56 +0300615 if (dev->dev_state != MEI_DEV_ENABLED) {
Oren Weilab841162011-05-15 13:43:41 +0300616 rets = -ENODEV;
617 goto unlock_dev;
618 }
Tomas Winkler07b509b2012-07-23 14:05:39 +0300619 i = mei_me_cl_by_id(dev, dev->iamthif_cl.me_client_id);
620 if (i < 0) {
Oren Weilab841162011-05-15 13:43:41 +0300621 rets = -ENODEV;
622 goto unlock_dev;
623 }
Tomas Winkler07b509b2012-07-23 14:05:39 +0300624 if (length > dev->me_clients[i].props.max_msg_length ||
Oren Weilab841162011-05-15 13:43:41 +0300625 length <= 0) {
626 rets = -EMSGSIZE;
627 goto unlock_dev;
628 }
629
630 write_cb->response_buffer.size = dev->iamthif_mtu;
631 write_cb->major_file_operations = MEI_IOCTL;
632 write_cb->information = 0;
633 write_cb->request_buffer.size = length;
634 if (dev->iamthif_cl.state != MEI_FILE_CONNECTED) {
635 rets = -ENODEV;
636 goto unlock_dev;
637 }
638
639 if (!list_empty(&dev->amthi_cmd_list.mei_cb.cb_list) ||
640 dev->iamthif_state != MEI_IAMTHIF_IDLE) {
641 dev_dbg(&dev->pdev->dev, "amthi_state = %d\n",
642 (int) dev->iamthif_state);
643 dev_dbg(&dev->pdev->dev, "add amthi cb to amthi cmd waiting list\n");
644 list_add_tail(&write_cb->cb_list,
645 &dev->amthi_cmd_list.mei_cb.cb_list);
646 rets = length;
647 } else {
648 dev_dbg(&dev->pdev->dev, "call amthi write\n");
649 rets = amthi_write(dev, write_cb);
650
651 if (rets) {
652 dev_dbg(&dev->pdev->dev, "amthi write failed with status = %d\n",
653 rets);
654 goto unlock_dev;
655 }
656 rets = length;
657 }
658 mutex_unlock(&dev->device_lock);
659 return rets;
660 }
661
662 write_cb->major_file_operations = MEI_WRITE;
663 /* make sure information is zero before we start */
664
665 write_cb->information = 0;
666 write_cb->request_buffer.size = length;
667
668 dev_dbg(&dev->pdev->dev, "host client = %d, ME client = %d\n",
669 cl->host_client_id, cl->me_client_id);
670 if (cl->state != MEI_FILE_CONNECTED) {
671 rets = -ENODEV;
672 dev_dbg(&dev->pdev->dev, "host client = %d, is not connected to ME client = %d",
673 cl->host_client_id,
674 cl->me_client_id);
675 goto unlock_dev;
676 }
Tomas Winkler07b509b2012-07-23 14:05:39 +0300677 i = mei_me_cl_by_id(dev, cl->me_client_id);
678 if (i < 0) {
Oren Weilab841162011-05-15 13:43:41 +0300679 rets = -ENODEV;
680 goto unlock_dev;
681 }
682 if (length > dev->me_clients[i].props.max_msg_length || length <= 0) {
683 rets = -EINVAL;
684 goto unlock_dev;
685 }
686 write_cb->file_private = cl;
687
688 rets = mei_flow_ctrl_creds(dev, cl);
689 if (rets < 0)
690 goto unlock_dev;
691
692 if (rets && dev->mei_host_buffer_is_empty) {
693 rets = 0;
Tomas Winklereb9af0a2011-05-25 17:28:22 +0300694 dev->mei_host_buffer_is_empty = false;
Tomas Winkler24aadc82012-06-25 23:46:27 +0300695 if (length > mei_hbuf_max_data(dev)) {
696 mei_hdr.length = mei_hbuf_max_data(dev);
Oren Weilab841162011-05-15 13:43:41 +0300697 mei_hdr.msg_complete = 0;
698 } else {
699 mei_hdr.length = length;
700 mei_hdr.msg_complete = 1;
701 }
702 mei_hdr.host_addr = cl->host_client_id;
703 mei_hdr.me_addr = cl->me_client_id;
704 mei_hdr.reserved = 0;
705 dev_dbg(&dev->pdev->dev, "call mei_write_message header=%08x.\n",
706 *((u32 *) &mei_hdr));
Tomas Winkler1ccb7b62012-03-14 14:39:42 +0200707 if (mei_write_message(dev, &mei_hdr,
Oren Weilab841162011-05-15 13:43:41 +0300708 (unsigned char *) (write_cb->request_buffer.data),
709 mei_hdr.length)) {
710 rets = -ENODEV;
711 goto unlock_dev;
712 }
713 cl->writing_state = MEI_WRITING;
714 write_cb->information = mei_hdr.length;
715 if (mei_hdr.msg_complete) {
716 if (mei_flow_ctrl_reduce(dev, cl)) {
717 rets = -ENODEV;
718 goto unlock_dev;
719 }
720 list_add_tail(&write_cb->cb_list,
721 &dev->write_waiting_list.mei_cb.cb_list);
722 } else {
723 list_add_tail(&write_cb->cb_list,
724 &dev->write_list.mei_cb.cb_list);
725 }
726
727 } else {
728
729 write_cb->information = 0;
730 cl->writing_state = MEI_WRITING;
731 list_add_tail(&write_cb->cb_list,
732 &dev->write_list.mei_cb.cb_list);
733 }
734 mutex_unlock(&dev->device_lock);
735 return length;
736
737unlock_dev:
738 mutex_unlock(&dev->device_lock);
739 mei_free_cb_private(write_cb);
740 return rets;
741}
742
743
744/**
745 * mei_ioctl - the IOCTL function
746 *
747 * @file: pointer to file structure
748 * @cmd: ioctl command
749 * @data: pointer to mei message structure
750 *
751 * returns 0 on success , <0 on error
752 */
753static long mei_ioctl(struct file *file, unsigned int cmd, unsigned long data)
754{
755 struct mei_device *dev;
756 struct mei_cl *cl = file->private_data;
757 struct mei_connect_client_data *connect_data = NULL;
758 int rets;
759
760 if (cmd != IOCTL_MEI_CONNECT_CLIENT)
761 return -EINVAL;
762
763 if (WARN_ON(!cl || !cl->dev))
764 return -ENODEV;
765
766 dev = cl->dev;
767
768 dev_dbg(&dev->pdev->dev, "IOCTL cmd = 0x%x", cmd);
769
770 mutex_lock(&dev->device_lock);
Tomas Winklerb210d752012-08-07 00:03:56 +0300771 if (dev->dev_state != MEI_DEV_ENABLED) {
Oren Weilab841162011-05-15 13:43:41 +0300772 rets = -ENODEV;
773 goto out;
774 }
775
776 dev_dbg(&dev->pdev->dev, ": IOCTL_MEI_CONNECT_CLIENT.\n");
777
778 connect_data = kzalloc(sizeof(struct mei_connect_client_data),
779 GFP_KERNEL);
780 if (!connect_data) {
781 rets = -ENOMEM;
782 goto out;
783 }
784 dev_dbg(&dev->pdev->dev, "copy connect data from user\n");
785 if (copy_from_user(connect_data, (char __user *)data,
786 sizeof(struct mei_connect_client_data))) {
787 dev_dbg(&dev->pdev->dev, "failed to copy data from userland\n");
788 rets = -EFAULT;
789 goto out;
790 }
791 rets = mei_ioctl_connect_client(file, connect_data);
792
793 /* if all is ok, copying the data back to user. */
794 if (rets)
795 goto out;
796
797 dev_dbg(&dev->pdev->dev, "copy connect data to user\n");
798 if (copy_to_user((char __user *)data, connect_data,
799 sizeof(struct mei_connect_client_data))) {
800 dev_dbg(&dev->pdev->dev, "failed to copy data to userland\n");
801 rets = -EFAULT;
802 goto out;
803 }
804
805out:
806 kfree(connect_data);
807 mutex_unlock(&dev->device_lock);
808 return rets;
809}
810
811/**
812 * mei_compat_ioctl - the compat IOCTL function
813 *
814 * @file: pointer to file structure
815 * @cmd: ioctl command
816 * @data: pointer to mei message structure
817 *
818 * returns 0 on success , <0 on error
819 */
820#ifdef CONFIG_COMPAT
821static long mei_compat_ioctl(struct file *file,
Tomas Winkler441ab502011-12-13 23:39:34 +0200822 unsigned int cmd, unsigned long data)
Oren Weilab841162011-05-15 13:43:41 +0300823{
824 return mei_ioctl(file, cmd, (unsigned long)compat_ptr(data));
825}
826#endif
827
828
829/**
830 * mei_poll - the poll function
831 *
832 * @file: pointer to file structure
833 * @wait: pointer to poll_table structure
834 *
835 * returns poll mask
836 */
837static unsigned int mei_poll(struct file *file, poll_table *wait)
838{
839 struct mei_cl *cl = file->private_data;
840 struct mei_device *dev;
841 unsigned int mask = 0;
842
843 if (WARN_ON(!cl || !cl->dev))
844 return mask;
845
846 dev = cl->dev;
847
848 mutex_lock(&dev->device_lock);
849
Tomas Winklerb210d752012-08-07 00:03:56 +0300850 if (dev->dev_state != MEI_DEV_ENABLED)
Oren Weilab841162011-05-15 13:43:41 +0300851 goto out;
852
853
854 if (cl == &dev->iamthif_cl) {
855 mutex_unlock(&dev->device_lock);
856 poll_wait(file, &dev->iamthif_cl.wait, wait);
857 mutex_lock(&dev->device_lock);
858 if (dev->iamthif_state == MEI_IAMTHIF_READ_COMPLETE &&
859 dev->iamthif_file_object == file) {
860 mask |= (POLLIN | POLLRDNORM);
861 dev_dbg(&dev->pdev->dev, "run next amthi cb\n");
Tomas Winklerc95efb72011-05-25 17:28:21 +0300862 mei_run_next_iamthif_cmd(dev);
Oren Weilab841162011-05-15 13:43:41 +0300863 }
864 goto out;
865 }
866
867 mutex_unlock(&dev->device_lock);
868 poll_wait(file, &cl->tx_wait, wait);
869 mutex_lock(&dev->device_lock);
870 if (MEI_WRITE_COMPLETE == cl->writing_state)
871 mask |= (POLLIN | POLLRDNORM);
872
873out:
874 mutex_unlock(&dev->device_lock);
875 return mask;
876}
877
Oren Weil5b881e32011-11-13 09:41:14 +0200878/*
879 * file operations structure will be used for mei char device.
880 */
881static const struct file_operations mei_fops = {
882 .owner = THIS_MODULE,
883 .read = mei_read,
884 .unlocked_ioctl = mei_ioctl,
885#ifdef CONFIG_COMPAT
886 .compat_ioctl = mei_compat_ioctl,
887#endif
888 .open = mei_open,
889 .release = mei_release,
890 .write = mei_write,
891 .poll = mei_poll,
892 .llseek = no_llseek
893};
894
895
896/*
897 * Misc Device Struct
898 */
899static struct miscdevice mei_misc_device = {
Tomas Winklerc38ea242012-04-02 20:32:39 +0300900 .name = "mei",
Oren Weil5b881e32011-11-13 09:41:14 +0200901 .fops = &mei_fops,
902 .minor = MISC_DYNAMIC_MINOR,
903};
904
905/**
Tomas Winkler9a123f12012-08-06 15:23:55 +0300906 * mei_quirk_probe - probe for devices that doesn't valid ME interface
907 * @pdev: PCI device structure
908 * @ent: entry into pci_device_table
909 *
910 * returns true if ME Interface is valid, false otherwise
911 */
912static bool __devinit mei_quirk_probe(struct pci_dev *pdev,
913 const struct pci_device_id *ent)
914{
915 u32 reg;
916 if (ent->device == MEI_DEV_ID_PBG_1) {
917 pci_read_config_dword(pdev, 0x48, &reg);
918 /* make sure that bit 9 is up and bit 10 is down */
919 if ((reg & 0x600) == 0x200) {
920 dev_info(&pdev->dev, "Device doesn't have valid ME Interface\n");
921 return false;
922 }
923 }
924 return true;
925}
926/**
Oren Weil5b881e32011-11-13 09:41:14 +0200927 * mei_probe - Device Initialization Routine
928 *
929 * @pdev: PCI device structure
930 * @ent: entry in kcs_pci_tbl
931 *
932 * returns 0 on success, <0 on failure.
933 */
934static int __devinit mei_probe(struct pci_dev *pdev,
935 const struct pci_device_id *ent)
936{
937 struct mei_device *dev;
938 int err;
939
940 mutex_lock(&mei_mutex);
Tomas Winkler9a123f12012-08-06 15:23:55 +0300941
942 if (!mei_quirk_probe(pdev, ent)) {
943 err = -ENODEV;
944 goto end;
945 }
946
Tomas Winklerdaed6b52012-08-17 09:54:23 +0300947 if (mei_pdev) {
Oren Weil5b881e32011-11-13 09:41:14 +0200948 err = -EEXIST;
949 goto end;
950 }
951 /* enable pci dev */
952 err = pci_enable_device(pdev);
953 if (err) {
Tomas Winkler32c826b2012-05-08 23:04:56 +0300954 dev_err(&pdev->dev, "failed to enable pci device.\n");
Oren Weil5b881e32011-11-13 09:41:14 +0200955 goto end;
956 }
957 /* set PCI host mastering */
958 pci_set_master(pdev);
959 /* pci request regions for mei driver */
Tomas Winkler068c0ae2012-08-07 00:03:54 +0300960 err = pci_request_regions(pdev, KBUILD_MODNAME);
Oren Weil5b881e32011-11-13 09:41:14 +0200961 if (err) {
Tomas Winkler32c826b2012-05-08 23:04:56 +0300962 dev_err(&pdev->dev, "failed to get pci regions.\n");
Oren Weil5b881e32011-11-13 09:41:14 +0200963 goto disable_device;
964 }
965 /* allocates and initializes the mei dev structure */
966 dev = mei_device_init(pdev);
967 if (!dev) {
968 err = -ENOMEM;
969 goto release_regions;
970 }
971 /* mapping IO device memory */
972 dev->mem_addr = pci_iomap(pdev, 0, 0);
973 if (!dev->mem_addr) {
Tomas Winkler32c826b2012-05-08 23:04:56 +0300974 dev_err(&pdev->dev, "mapping I/O device memory failure.\n");
Oren Weil5b881e32011-11-13 09:41:14 +0200975 err = -ENOMEM;
976 goto free_device;
977 }
978 pci_enable_msi(pdev);
979
980 /* request and enable interrupt */
981 if (pci_dev_msi_enabled(pdev))
982 err = request_threaded_irq(pdev->irq,
983 NULL,
984 mei_interrupt_thread_handler,
Tomas Winkler068c0ae2012-08-07 00:03:54 +0300985 IRQF_ONESHOT, KBUILD_MODNAME, dev);
Oren Weil5b881e32011-11-13 09:41:14 +0200986 else
987 err = request_threaded_irq(pdev->irq,
988 mei_interrupt_quick_handler,
989 mei_interrupt_thread_handler,
Tomas Winkler068c0ae2012-08-07 00:03:54 +0300990 IRQF_SHARED, KBUILD_MODNAME, dev);
Oren Weil5b881e32011-11-13 09:41:14 +0200991
992 if (err) {
Tomas Winkler32c826b2012-05-08 23:04:56 +0300993 dev_err(&pdev->dev, "request_threaded_irq failure. irq = %d\n",
Oren Weil5b881e32011-11-13 09:41:14 +0200994 pdev->irq);
Samuel Ortiz169dc382012-06-11 12:18:30 +0300995 goto disable_msi;
Oren Weil5b881e32011-11-13 09:41:14 +0200996 }
997 INIT_DELAYED_WORK(&dev->timer_work, mei_timer);
998 if (mei_hw_init(dev)) {
Tomas Winkler32c826b2012-05-08 23:04:56 +0300999 dev_err(&pdev->dev, "init hw failure.\n");
Oren Weil5b881e32011-11-13 09:41:14 +02001000 err = -ENODEV;
1001 goto release_irq;
1002 }
1003
1004 err = misc_register(&mei_misc_device);
1005 if (err)
1006 goto release_irq;
1007
Tomas Winklerdaed6b52012-08-17 09:54:23 +03001008 mei_pdev = pdev;
Oren Weil5b881e32011-11-13 09:41:14 +02001009 pci_set_drvdata(pdev, dev);
1010
1011
1012 schedule_delayed_work(&dev->timer_work, HZ);
1013
1014 mutex_unlock(&mei_mutex);
1015
Tomas Winkler2f3d2b42012-03-19 22:38:13 +02001016 pr_debug("initialization successful.\n");
Oren Weil5b881e32011-11-13 09:41:14 +02001017
1018 return 0;
1019
1020release_irq:
1021 /* disable interrupts */
1022 dev->host_hw_state = mei_hcsr_read(dev);
1023 mei_disable_interrupts(dev);
1024 flush_scheduled_work();
1025 free_irq(pdev->irq, dev);
Samuel Ortiz169dc382012-06-11 12:18:30 +03001026disable_msi:
Oren Weil5b881e32011-11-13 09:41:14 +02001027 pci_disable_msi(pdev);
Oren Weil5b881e32011-11-13 09:41:14 +02001028 pci_iounmap(pdev, dev->mem_addr);
1029free_device:
1030 kfree(dev);
1031release_regions:
1032 pci_release_regions(pdev);
1033disable_device:
1034 pci_disable_device(pdev);
1035end:
1036 mutex_unlock(&mei_mutex);
Tomas Winkler32c826b2012-05-08 23:04:56 +03001037 dev_err(&pdev->dev, "initialization failed.\n");
Oren Weil5b881e32011-11-13 09:41:14 +02001038 return err;
1039}
1040
1041/**
1042 * mei_remove - Device Removal Routine
1043 *
1044 * @pdev: PCI device structure
1045 *
1046 * mei_remove is called by the PCI subsystem to alert the driver
1047 * that it should release a PCI device.
1048 */
1049static void __devexit mei_remove(struct pci_dev *pdev)
1050{
1051 struct mei_device *dev;
1052
Tomas Winklerdaed6b52012-08-17 09:54:23 +03001053 if (mei_pdev != pdev)
Oren Weil5b881e32011-11-13 09:41:14 +02001054 return;
1055
1056 dev = pci_get_drvdata(pdev);
1057 if (!dev)
1058 return;
1059
1060 mutex_lock(&dev->device_lock);
1061
Tomas Winklerc216fde2012-08-16 19:39:43 +03001062 cancel_delayed_work(&dev->timer_work);
1063
1064 mei_wd_stop(dev);
Oren Weil5b881e32011-11-13 09:41:14 +02001065
Tomas Winklerdaed6b52012-08-17 09:54:23 +03001066 mei_pdev = NULL;
Oren Weil5b881e32011-11-13 09:41:14 +02001067
1068 if (dev->iamthif_cl.state == MEI_FILE_CONNECTED) {
1069 dev->iamthif_cl.state = MEI_FILE_DISCONNECTING;
1070 mei_disconnect_host_client(dev, &dev->iamthif_cl);
1071 }
1072 if (dev->wd_cl.state == MEI_FILE_CONNECTED) {
1073 dev->wd_cl.state = MEI_FILE_DISCONNECTING;
1074 mei_disconnect_host_client(dev, &dev->wd_cl);
1075 }
1076
1077 /* Unregistering watchdog device */
Tomas Winkler70cd5332011-12-22 18:50:50 +02001078 mei_watchdog_unregister(dev);
Oren Weil5b881e32011-11-13 09:41:14 +02001079
1080 /* remove entry if already in list */
1081 dev_dbg(&pdev->dev, "list del iamthif and wd file list.\n");
1082 mei_remove_client_from_file_list(dev, dev->wd_cl.host_client_id);
1083 mei_remove_client_from_file_list(dev, dev->iamthif_cl.host_client_id);
1084
1085 dev->iamthif_current_cb = NULL;
1086 dev->me_clients_num = 0;
1087
1088 mutex_unlock(&dev->device_lock);
1089
1090 flush_scheduled_work();
1091
1092 /* disable interrupts */
1093 mei_disable_interrupts(dev);
1094
1095 free_irq(pdev->irq, dev);
1096 pci_disable_msi(pdev);
1097 pci_set_drvdata(pdev, NULL);
1098
1099 if (dev->mem_addr)
1100 pci_iounmap(pdev, dev->mem_addr);
1101
1102 kfree(dev);
1103
1104 pci_release_regions(pdev);
1105 pci_disable_device(pdev);
Tomas Winklera44cab42012-05-29 16:39:11 +03001106
1107 misc_deregister(&mei_misc_device);
Oren Weil5b881e32011-11-13 09:41:14 +02001108}
Oren Weilab841162011-05-15 13:43:41 +03001109#ifdef CONFIG_PM
1110static int mei_pci_suspend(struct device *device)
1111{
1112 struct pci_dev *pdev = to_pci_dev(device);
1113 struct mei_device *dev = pci_get_drvdata(pdev);
1114 int err;
1115
1116 if (!dev)
1117 return -ENODEV;
1118 mutex_lock(&dev->device_lock);
Tomas Winklerc216fde2012-08-16 19:39:43 +03001119
1120 cancel_delayed_work(&dev->timer_work);
1121
Oren Weilab841162011-05-15 13:43:41 +03001122 /* Stop watchdog if exists */
Tomas Winklerc216fde2012-08-16 19:39:43 +03001123 err = mei_wd_stop(dev);
Oren Weilab841162011-05-15 13:43:41 +03001124 /* Set new mei state */
Tomas Winklerb210d752012-08-07 00:03:56 +03001125 if (dev->dev_state == MEI_DEV_ENABLED ||
1126 dev->dev_state == MEI_DEV_RECOVERING_FROM_RESET) {
1127 dev->dev_state = MEI_DEV_POWER_DOWN;
Oren Weilab841162011-05-15 13:43:41 +03001128 mei_reset(dev, 0);
1129 }
1130 mutex_unlock(&dev->device_lock);
1131
1132 free_irq(pdev->irq, dev);
Tomas Winkler4f61a7a2011-07-14 20:11:25 +03001133 pci_disable_msi(pdev);
Oren Weilab841162011-05-15 13:43:41 +03001134
1135 return err;
1136}
1137
1138static int mei_pci_resume(struct device *device)
1139{
1140 struct pci_dev *pdev = to_pci_dev(device);
1141 struct mei_device *dev;
1142 int err;
1143
1144 dev = pci_get_drvdata(pdev);
1145 if (!dev)
1146 return -ENODEV;
1147
Tomas Winkler4f61a7a2011-07-14 20:11:25 +03001148 pci_enable_msi(pdev);
1149
1150 /* request and enable interrupt */
1151 if (pci_dev_msi_enabled(pdev))
1152 err = request_threaded_irq(pdev->irq,
1153 NULL,
1154 mei_interrupt_thread_handler,
Tomas Winkler068c0ae2012-08-07 00:03:54 +03001155 IRQF_ONESHOT, KBUILD_MODNAME, dev);
Tomas Winkler4f61a7a2011-07-14 20:11:25 +03001156 else
1157 err = request_threaded_irq(pdev->irq,
Oren Weilab841162011-05-15 13:43:41 +03001158 mei_interrupt_quick_handler,
1159 mei_interrupt_thread_handler,
Tomas Winkler068c0ae2012-08-07 00:03:54 +03001160 IRQF_SHARED, KBUILD_MODNAME, dev);
Tomas Winkler4f61a7a2011-07-14 20:11:25 +03001161
Oren Weilab841162011-05-15 13:43:41 +03001162 if (err) {
Tomas Winkler32c826b2012-05-08 23:04:56 +03001163 dev_err(&pdev->dev, "request_threaded_irq failed: irq = %d.\n",
1164 pdev->irq);
Oren Weilab841162011-05-15 13:43:41 +03001165 return err;
1166 }
1167
1168 mutex_lock(&dev->device_lock);
Tomas Winklerb210d752012-08-07 00:03:56 +03001169 dev->dev_state = MEI_DEV_POWER_UP;
Oren Weilab841162011-05-15 13:43:41 +03001170 mei_reset(dev, 1);
1171 mutex_unlock(&dev->device_lock);
1172
Oren Weil6d70e932011-09-07 09:03:14 +03001173 /* Start timer if stopped in suspend */
1174 schedule_delayed_work(&dev->timer_work, HZ);
1175
Oren Weilab841162011-05-15 13:43:41 +03001176 return err;
1177}
1178static SIMPLE_DEV_PM_OPS(mei_pm_ops, mei_pci_suspend, mei_pci_resume);
1179#define MEI_PM_OPS (&mei_pm_ops)
1180#else
Randy Dunlap2d990362011-05-19 08:52:34 -07001181#define MEI_PM_OPS NULL
Oren Weilab841162011-05-15 13:43:41 +03001182#endif /* CONFIG_PM */
1183/*
1184 * PCI driver structure
1185 */
1186static struct pci_driver mei_driver = {
Tomas Winkler068c0ae2012-08-07 00:03:54 +03001187 .name = KBUILD_MODNAME,
Oren Weilab841162011-05-15 13:43:41 +03001188 .id_table = mei_pci_tbl,
1189 .probe = mei_probe,
1190 .remove = __devexit_p(mei_remove),
1191 .shutdown = __devexit_p(mei_remove),
1192 .driver.pm = MEI_PM_OPS,
1193};
1194
Tomas Winkler60781882012-07-19 09:45:32 +03001195module_pci_driver(mei_driver);
Oren Weilab841162011-05-15 13:43:41 +03001196
1197MODULE_AUTHOR("Intel Corporation");
1198MODULE_DESCRIPTION("Intel(R) Management Engine Interface");
1199MODULE_LICENSE("GPL v2");