blob: ec5fd7a0e2894e1e1b22b2c03f777bbef496af93 [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
Tomas Winkler4f3afe12012-05-09 16:38:59 +030040#include <linux/mei.h>
Tomas Winkler47a73802012-12-25 19:06:03 +020041
42#include "mei_dev.h"
Tomas Winkler9dc64d62013-01-08 23:07:17 +020043#include "hw-me.h"
Tomas Winkler90e0b5f2013-01-08 23:07:14 +020044#include "client.h"
Oren Weilab841162011-05-15 13:43:41 +030045
Tomas Winklerdaed6b52012-08-17 09:54:23 +030046/* AMT device is a singleton on the platform */
47static struct pci_dev *mei_pdev;
Oren Weilab841162011-05-15 13:43:41 +030048
Oren Weilab841162011-05-15 13:43:41 +030049/* mei_pci_tbl - PCI Device ID Table */
50static DEFINE_PCI_DEVICE_TABLE(mei_pci_tbl) = {
51 {PCI_DEVICE(PCI_VENDOR_ID_INTEL, MEI_DEV_ID_82946GZ)},
52 {PCI_DEVICE(PCI_VENDOR_ID_INTEL, MEI_DEV_ID_82G35)},
53 {PCI_DEVICE(PCI_VENDOR_ID_INTEL, MEI_DEV_ID_82Q965)},
54 {PCI_DEVICE(PCI_VENDOR_ID_INTEL, MEI_DEV_ID_82G965)},
55 {PCI_DEVICE(PCI_VENDOR_ID_INTEL, MEI_DEV_ID_82GM965)},
56 {PCI_DEVICE(PCI_VENDOR_ID_INTEL, MEI_DEV_ID_82GME965)},
57 {PCI_DEVICE(PCI_VENDOR_ID_INTEL, MEI_DEV_ID_ICH9_82Q35)},
58 {PCI_DEVICE(PCI_VENDOR_ID_INTEL, MEI_DEV_ID_ICH9_82G33)},
59 {PCI_DEVICE(PCI_VENDOR_ID_INTEL, MEI_DEV_ID_ICH9_82Q33)},
60 {PCI_DEVICE(PCI_VENDOR_ID_INTEL, MEI_DEV_ID_ICH9_82X38)},
61 {PCI_DEVICE(PCI_VENDOR_ID_INTEL, MEI_DEV_ID_ICH9_3200)},
62 {PCI_DEVICE(PCI_VENDOR_ID_INTEL, MEI_DEV_ID_ICH9_6)},
63 {PCI_DEVICE(PCI_VENDOR_ID_INTEL, MEI_DEV_ID_ICH9_7)},
64 {PCI_DEVICE(PCI_VENDOR_ID_INTEL, MEI_DEV_ID_ICH9_8)},
65 {PCI_DEVICE(PCI_VENDOR_ID_INTEL, MEI_DEV_ID_ICH9_9)},
66 {PCI_DEVICE(PCI_VENDOR_ID_INTEL, MEI_DEV_ID_ICH9_10)},
67 {PCI_DEVICE(PCI_VENDOR_ID_INTEL, MEI_DEV_ID_ICH9M_1)},
68 {PCI_DEVICE(PCI_VENDOR_ID_INTEL, MEI_DEV_ID_ICH9M_2)},
69 {PCI_DEVICE(PCI_VENDOR_ID_INTEL, MEI_DEV_ID_ICH9M_3)},
70 {PCI_DEVICE(PCI_VENDOR_ID_INTEL, MEI_DEV_ID_ICH9M_4)},
71 {PCI_DEVICE(PCI_VENDOR_ID_INTEL, MEI_DEV_ID_ICH10_1)},
72 {PCI_DEVICE(PCI_VENDOR_ID_INTEL, MEI_DEV_ID_ICH10_2)},
73 {PCI_DEVICE(PCI_VENDOR_ID_INTEL, MEI_DEV_ID_ICH10_3)},
74 {PCI_DEVICE(PCI_VENDOR_ID_INTEL, MEI_DEV_ID_ICH10_4)},
75 {PCI_DEVICE(PCI_VENDOR_ID_INTEL, MEI_DEV_ID_IBXPK_1)},
76 {PCI_DEVICE(PCI_VENDOR_ID_INTEL, MEI_DEV_ID_IBXPK_2)},
77 {PCI_DEVICE(PCI_VENDOR_ID_INTEL, MEI_DEV_ID_CPT_1)},
78 {PCI_DEVICE(PCI_VENDOR_ID_INTEL, MEI_DEV_ID_PBG_1)},
79 {PCI_DEVICE(PCI_VENDOR_ID_INTEL, MEI_DEV_ID_PPT_1)},
80 {PCI_DEVICE(PCI_VENDOR_ID_INTEL, MEI_DEV_ID_PPT_2)},
81 {PCI_DEVICE(PCI_VENDOR_ID_INTEL, MEI_DEV_ID_PPT_3)},
Tomas Winkler9af51422012-08-29 01:15:50 +030082 {PCI_DEVICE(PCI_VENDOR_ID_INTEL, MEI_DEV_ID_LPT)},
83 {PCI_DEVICE(PCI_VENDOR_ID_INTEL, MEI_DEV_ID_LPT_LP)},
Oren Weilab841162011-05-15 13:43:41 +030084
85 /* required last entry */
86 {0, }
87};
88
89MODULE_DEVICE_TABLE(pci, mei_pci_tbl);
90
91static DEFINE_MUTEX(mei_mutex);
92
Oren Weilab841162011-05-15 13:43:41 +030093
94/**
Oren Weilab841162011-05-15 13:43:41 +030095 * mei_open - the open function
96 *
97 * @inode: pointer to inode structure
98 * @file: pointer to file structure
99 *
100 * returns 0 on success, <0 on error
101 */
102static int mei_open(struct inode *inode, struct file *file)
103{
104 struct mei_cl *cl;
Oren Weilab841162011-05-15 13:43:41 +0300105 struct mei_device *dev;
Tomas Winkler6f37aca2011-11-13 09:41:15 +0200106 int err;
Oren Weilab841162011-05-15 13:43:41 +0300107
108 err = -ENODEV;
Tomas Winklerdaed6b52012-08-17 09:54:23 +0300109 if (!mei_pdev)
Oren Weilab841162011-05-15 13:43:41 +0300110 goto out;
111
Tomas Winklerdaed6b52012-08-17 09:54:23 +0300112 dev = pci_get_drvdata(mei_pdev);
Oren Weil5b881e32011-11-13 09:41:14 +0200113 if (!dev)
Oren Weilab841162011-05-15 13:43:41 +0300114 goto out;
115
116 mutex_lock(&dev->device_lock);
117 err = -ENOMEM;
Tomas Winklerc95efb72011-05-25 17:28:21 +0300118 cl = mei_cl_allocate(dev);
Oren Weilab841162011-05-15 13:43:41 +0300119 if (!cl)
Alexey Khoroshilov303dfbf2011-08-31 00:41:14 +0400120 goto out_unlock;
Oren Weilab841162011-05-15 13:43:41 +0300121
122 err = -ENODEV;
Tomas Winklerb210d752012-08-07 00:03:56 +0300123 if (dev->dev_state != MEI_DEV_ENABLED) {
124 dev_dbg(&dev->pdev->dev, "dev_state != MEI_ENABLED dev_state = %s\n",
125 mei_dev_state_str(dev->dev_state));
Oren Weilab841162011-05-15 13:43:41 +0300126 goto out_unlock;
127 }
128 err = -EMFILE;
Tomas Winkler1b812942012-09-11 00:43:20 +0300129 if (dev->open_handle_count >= MEI_MAX_OPEN_HANDLE_COUNT) {
130 dev_err(&dev->pdev->dev, "open_handle_count exceded %d",
131 MEI_MAX_OPEN_HANDLE_COUNT);
Oren Weilab841162011-05-15 13:43:41 +0300132 goto out_unlock;
Tomas Winkler1b812942012-09-11 00:43:20 +0300133 }
Oren Weilab841162011-05-15 13:43:41 +0300134
Tomas Winkler781d0d82013-01-08 23:07:22 +0200135 err = mei_cl_link(cl, MEI_HOST_CLIENT_ID_ANY);
136 if (err)
Oren Weilab841162011-05-15 13:43:41 +0300137 goto out_unlock;
Oren Weilab841162011-05-15 13:43:41 +0300138
139 file->private_data = cl;
140 mutex_unlock(&dev->device_lock);
141
Oren Weil5b881e32011-11-13 09:41:14 +0200142 return nonseekable_open(inode, file);
Oren Weilab841162011-05-15 13:43:41 +0300143
144out_unlock:
145 mutex_unlock(&dev->device_lock);
146 kfree(cl);
147out:
148 return err;
149}
150
151/**
152 * mei_release - the release function
153 *
154 * @inode: pointer to inode structure
155 * @file: pointer to file structure
156 *
157 * returns 0 on success, <0 on error
158 */
159static int mei_release(struct inode *inode, struct file *file)
160{
161 struct mei_cl *cl = file->private_data;
162 struct mei_cl_cb *cb;
163 struct mei_device *dev;
164 int rets = 0;
165
166 if (WARN_ON(!cl || !cl->dev))
167 return -ENODEV;
168
169 dev = cl->dev;
170
171 mutex_lock(&dev->device_lock);
Tomas Winklera562d5c2012-11-11 17:38:01 +0200172 if (cl == &dev->iamthif_cl) {
173 rets = mei_amthif_release(dev, file);
174 goto out;
175 }
176 if (cl->state == MEI_FILE_CONNECTED) {
177 cl->state = MEI_FILE_DISCONNECTING;
178 dev_dbg(&dev->pdev->dev,
179 "disconnecting client host client = %d, "
180 "ME client = %d\n",
Oren Weilab841162011-05-15 13:43:41 +0300181 cl->host_client_id,
182 cl->me_client_id);
Tomas Winkler90e0b5f2013-01-08 23:07:14 +0200183 rets = mei_cl_disconnect(cl);
Oren Weilab841162011-05-15 13:43:41 +0300184 }
Tomas Winklera562d5c2012-11-11 17:38:01 +0200185 mei_cl_flush_queues(cl);
186 dev_dbg(&dev->pdev->dev, "remove client host client = %d, ME client = %d\n",
187 cl->host_client_id,
188 cl->me_client_id);
189
190 if (dev->open_handle_count > 0) {
191 clear_bit(cl->host_client_id, dev->host_clients_map);
192 dev->open_handle_count--;
193 }
Tomas Winkler90e0b5f2013-01-08 23:07:14 +0200194 mei_cl_unlink(cl);
Tomas Winklera562d5c2012-11-11 17:38:01 +0200195
Tomas Winkler781d0d82013-01-08 23:07:22 +0200196
Tomas Winklera562d5c2012-11-11 17:38:01 +0200197 /* free read cb */
198 cb = NULL;
199 if (cl->read_cb) {
Tomas Winkler90e0b5f2013-01-08 23:07:14 +0200200 cb = mei_cl_find_read_cb(cl);
Tomas Winklera562d5c2012-11-11 17:38:01 +0200201 /* Remove entry from read list */
202 if (cb)
203 list_del(&cb->list);
204
205 cb = cl->read_cb;
206 cl->read_cb = NULL;
207 }
208
209 file->private_data = NULL;
210
211 if (cb) {
212 mei_io_cb_free(cb);
213 cb = NULL;
214 }
215
216 kfree(cl);
217out:
Oren Weilab841162011-05-15 13:43:41 +0300218 mutex_unlock(&dev->device_lock);
219 return rets;
220}
221
222
223/**
224 * mei_read - the read function.
225 *
226 * @file: pointer to file structure
227 * @ubuf: pointer to user buffer
228 * @length: buffer length
229 * @offset: data offset in buffer
230 *
231 * returns >=0 data length on success , <0 on error
232 */
233static ssize_t mei_read(struct file *file, char __user *ubuf,
Tomas Winkler441ab502011-12-13 23:39:34 +0200234 size_t length, loff_t *offset)
Oren Weilab841162011-05-15 13:43:41 +0300235{
236 struct mei_cl *cl = file->private_data;
237 struct mei_cl_cb *cb_pos = NULL;
238 struct mei_cl_cb *cb = NULL;
239 struct mei_device *dev;
240 int i;
241 int rets;
242 int err;
243
244
245 if (WARN_ON(!cl || !cl->dev))
246 return -ENODEV;
247
248 dev = cl->dev;
249
250 mutex_lock(&dev->device_lock);
Tomas Winklerb210d752012-08-07 00:03:56 +0300251 if (dev->dev_state != MEI_DEV_ENABLED) {
Oren Weilab841162011-05-15 13:43:41 +0300252 rets = -ENODEV;
253 goto out;
254 }
255
256 if ((cl->sm_state & MEI_WD_STATE_INDEPENDENCE_MSG_SENT) == 0) {
257 /* Do not allow to read watchdog client */
Tomas Winkler07b509b2012-07-23 14:05:39 +0300258 i = mei_me_cl_by_uuid(dev, &mei_wd_guid);
Oren Weilab841162011-05-15 13:43:41 +0300259 if (i >= 0) {
260 struct mei_me_client *me_client = &dev->me_clients[i];
Oren Weilab841162011-05-15 13:43:41 +0300261 if (cl->me_client_id == me_client->client_id) {
262 rets = -EBADF;
263 goto out;
264 }
265 }
266 } else {
267 cl->sm_state &= ~MEI_WD_STATE_INDEPENDENCE_MSG_SENT;
268 }
269
270 if (cl == &dev->iamthif_cl) {
Tomas Winkler19838fb2012-11-01 21:17:15 +0200271 rets = mei_amthif_read(dev, file, ubuf, length, offset);
Oren Weilab841162011-05-15 13:43:41 +0300272 goto out;
273 }
274
Tomas Winklerebb108ef2012-10-09 16:50:16 +0200275 if (cl->read_cb && cl->read_cb->buf_idx > *offset) {
Oren Weilab841162011-05-15 13:43:41 +0300276 cb = cl->read_cb;
277 goto copy_buffer;
Tomas Winklerebb108ef2012-10-09 16:50:16 +0200278 } else if (cl->read_cb && cl->read_cb->buf_idx > 0 &&
279 cl->read_cb->buf_idx <= *offset) {
Oren Weilab841162011-05-15 13:43:41 +0300280 cb = cl->read_cb;
281 rets = 0;
282 goto free;
Tomas Winklerebb108ef2012-10-09 16:50:16 +0200283 } else if ((!cl->read_cb || !cl->read_cb->buf_idx) && *offset > 0) {
Justin P. Mattock5f9092f32012-03-12 07:18:09 -0700284 /*Offset needs to be cleaned for contiguous reads*/
Oren Weilab841162011-05-15 13:43:41 +0300285 *offset = 0;
286 rets = 0;
287 goto out;
288 }
289
Tomas Winkler90e0b5f2013-01-08 23:07:14 +0200290 err = mei_cl_read_start(cl);
Oren Weilab841162011-05-15 13:43:41 +0300291 if (err && err != -EBUSY) {
292 dev_dbg(&dev->pdev->dev,
293 "mei start read failure with status = %d\n", err);
294 rets = err;
295 goto out;
296 }
297
298 if (MEI_READ_COMPLETE != cl->reading_state &&
299 !waitqueue_active(&cl->rx_wait)) {
300 if (file->f_flags & O_NONBLOCK) {
301 rets = -EAGAIN;
302 goto out;
303 }
304
305 mutex_unlock(&dev->device_lock);
306
307 if (wait_event_interruptible(cl->rx_wait,
308 (MEI_READ_COMPLETE == cl->reading_state ||
309 MEI_FILE_INITIALIZING == cl->state ||
310 MEI_FILE_DISCONNECTED == cl->state ||
311 MEI_FILE_DISCONNECTING == cl->state))) {
312 if (signal_pending(current))
313 return -EINTR;
314 return -ERESTARTSYS;
315 }
316
317 mutex_lock(&dev->device_lock);
318 if (MEI_FILE_INITIALIZING == cl->state ||
319 MEI_FILE_DISCONNECTED == cl->state ||
320 MEI_FILE_DISCONNECTING == cl->state) {
321 rets = -EBUSY;
322 goto out;
323 }
324 }
325
326 cb = cl->read_cb;
327
328 if (!cb) {
329 rets = -ENODEV;
330 goto out;
331 }
332 if (cl->reading_state != MEI_READ_COMPLETE) {
333 rets = 0;
334 goto out;
335 }
336 /* now copy the data to user space */
337copy_buffer:
338 dev_dbg(&dev->pdev->dev, "cb->response_buffer size - %d\n",
339 cb->response_buffer.size);
Tomas Winklerebb108ef2012-10-09 16:50:16 +0200340 dev_dbg(&dev->pdev->dev, "cb->buf_idx - %lu\n", cb->buf_idx);
341 if (length == 0 || ubuf == NULL || *offset > cb->buf_idx) {
Oren Weilab841162011-05-15 13:43:41 +0300342 rets = -EMSGSIZE;
343 goto free;
344 }
345
Tomas Winklerebb108ef2012-10-09 16:50:16 +0200346 /* length is being truncated to PAGE_SIZE,
347 * however buf_idx may point beyond that */
348 length = min_t(size_t, length, cb->buf_idx - *offset);
Oren Weilab841162011-05-15 13:43:41 +0300349
Tomas Winkler441ab502011-12-13 23:39:34 +0200350 if (copy_to_user(ubuf, cb->response_buffer.data + *offset, length)) {
Oren Weilab841162011-05-15 13:43:41 +0300351 rets = -EFAULT;
352 goto free;
353 }
354
355 rets = length;
356 *offset += length;
Tomas Winklerebb108ef2012-10-09 16:50:16 +0200357 if ((unsigned long)*offset < cb->buf_idx)
Oren Weilab841162011-05-15 13:43:41 +0300358 goto out;
359
360free:
Tomas Winkler90e0b5f2013-01-08 23:07:14 +0200361 cb_pos = mei_cl_find_read_cb(cl);
Oren Weilab841162011-05-15 13:43:41 +0300362 /* Remove entry from read list */
363 if (cb_pos)
Tomas Winklerfb601ad2012-10-15 12:06:48 +0200364 list_del(&cb_pos->list);
Tomas Winkler601a1ef2012-10-09 16:50:20 +0200365 mei_io_cb_free(cb);
Oren Weilab841162011-05-15 13:43:41 +0300366 cl->reading_state = MEI_IDLE;
367 cl->read_cb = NULL;
Oren Weilab841162011-05-15 13:43:41 +0300368out:
369 dev_dbg(&dev->pdev->dev, "end mei read rets= %d\n", rets);
370 mutex_unlock(&dev->device_lock);
371 return rets;
372}
Tomas Winkler33d28c92012-10-09 16:50:17 +0200373/**
Oren Weilab841162011-05-15 13:43:41 +0300374 * mei_write - the write function.
375 *
376 * @file: pointer to file structure
377 * @ubuf: pointer to user buffer
378 * @length: buffer length
379 * @offset: data offset in buffer
380 *
381 * returns >=0 data length on success , <0 on error
382 */
383static ssize_t mei_write(struct file *file, const char __user *ubuf,
Tomas Winkler441ab502011-12-13 23:39:34 +0200384 size_t length, loff_t *offset)
Oren Weilab841162011-05-15 13:43:41 +0300385{
386 struct mei_cl *cl = file->private_data;
387 struct mei_cl_cb *write_cb = NULL;
388 struct mei_msg_hdr mei_hdr;
389 struct mei_device *dev;
390 unsigned long timeout = 0;
391 int rets;
392 int i;
393
394 if (WARN_ON(!cl || !cl->dev))
395 return -ENODEV;
396
397 dev = cl->dev;
398
399 mutex_lock(&dev->device_lock);
400
Tomas Winklerb210d752012-08-07 00:03:56 +0300401 if (dev->dev_state != MEI_DEV_ENABLED) {
Tomas Winkler75f0ee12012-10-09 16:50:18 +0200402 rets = -ENODEV;
Tomas Winklerb0d0cf72012-11-01 21:17:13 +0200403 goto err;
Oren Weilab841162011-05-15 13:43:41 +0300404 }
405
Tomas Winkler75f0ee12012-10-09 16:50:18 +0200406 i = mei_me_cl_by_id(dev, cl->me_client_id);
407 if (i < 0) {
408 rets = -ENODEV;
Tomas Winklerb0d0cf72012-11-01 21:17:13 +0200409 goto err;
Tomas Winkler75f0ee12012-10-09 16:50:18 +0200410 }
411 if (length > dev->me_clients[i].props.max_msg_length || length <= 0) {
412 rets = -EMSGSIZE;
Tomas Winklerb0d0cf72012-11-01 21:17:13 +0200413 goto err;
Tomas Winkler75f0ee12012-10-09 16:50:18 +0200414 }
415
416 if (cl->state != MEI_FILE_CONNECTED) {
417 rets = -ENODEV;
418 dev_err(&dev->pdev->dev, "host client = %d, is not connected to ME client = %d",
419 cl->host_client_id, cl->me_client_id);
Tomas Winklerb0d0cf72012-11-01 21:17:13 +0200420 goto err;
Tomas Winkler75f0ee12012-10-09 16:50:18 +0200421 }
Oren Weilab841162011-05-15 13:43:41 +0300422 if (cl == &dev->iamthif_cl) {
Tomas Winkler19838fb2012-11-01 21:17:15 +0200423 write_cb = mei_amthif_find_read_list_entry(dev, file);
Oren Weilab841162011-05-15 13:43:41 +0300424
425 if (write_cb) {
426 timeout = write_cb->read_time +
Tomas Winkler3870c322012-11-01 21:17:14 +0200427 mei_secs_to_jiffies(MEI_IAMTHIF_READ_TIMER);
Oren Weilab841162011-05-15 13:43:41 +0300428
429 if (time_after(jiffies, timeout) ||
Tomas Winkler75f0ee12012-10-09 16:50:18 +0200430 cl->reading_state == MEI_READ_COMPLETE) {
431 *offset = 0;
Tomas Winklerfb601ad2012-10-15 12:06:48 +0200432 list_del(&write_cb->list);
Tomas Winkler601a1ef2012-10-09 16:50:20 +0200433 mei_io_cb_free(write_cb);
Tomas Winkler75f0ee12012-10-09 16:50:18 +0200434 write_cb = NULL;
Oren Weilab841162011-05-15 13:43:41 +0300435 }
436 }
437 }
438
439 /* free entry used in read */
440 if (cl->reading_state == MEI_READ_COMPLETE) {
441 *offset = 0;
Tomas Winkler90e0b5f2013-01-08 23:07:14 +0200442 write_cb = mei_cl_find_read_cb(cl);
Oren Weilab841162011-05-15 13:43:41 +0300443 if (write_cb) {
Tomas Winklerfb601ad2012-10-15 12:06:48 +0200444 list_del(&write_cb->list);
Tomas Winkler601a1ef2012-10-09 16:50:20 +0200445 mei_io_cb_free(write_cb);
Oren Weilab841162011-05-15 13:43:41 +0300446 write_cb = NULL;
447 cl->reading_state = MEI_IDLE;
448 cl->read_cb = NULL;
Oren Weilab841162011-05-15 13:43:41 +0300449 }
Tomas Winklerd91aaed2013-01-08 23:07:18 +0200450 } else if (cl->reading_state == MEI_IDLE)
Oren Weilab841162011-05-15 13:43:41 +0300451 *offset = 0;
452
453
Tomas Winkler33d28c92012-10-09 16:50:17 +0200454 write_cb = mei_io_cb_init(cl, file);
Oren Weilab841162011-05-15 13:43:41 +0300455 if (!write_cb) {
Tomas Winkler33d28c92012-10-09 16:50:17 +0200456 dev_err(&dev->pdev->dev, "write cb allocation failed\n");
457 rets = -ENOMEM;
Tomas Winklerb0d0cf72012-11-01 21:17:13 +0200458 goto err;
Oren Weilab841162011-05-15 13:43:41 +0300459 }
Tomas Winkler33d28c92012-10-09 16:50:17 +0200460 rets = mei_io_cb_alloc_req_buf(write_cb, length);
461 if (rets)
Tomas Winklerb0d0cf72012-11-01 21:17:13 +0200462 goto err;
Oren Weilab841162011-05-15 13:43:41 +0300463
Tomas Winkler33d28c92012-10-09 16:50:17 +0200464 dev_dbg(&dev->pdev->dev, "cb request size = %zd\n", length);
Oren Weilab841162011-05-15 13:43:41 +0300465
Tomas Winkler75f0ee12012-10-09 16:50:18 +0200466 rets = copy_from_user(write_cb->request_buffer.data, ubuf, length);
467 if (rets)
Tomas Winklerb0d0cf72012-11-01 21:17:13 +0200468 goto err;
Oren Weilab841162011-05-15 13:43:41 +0300469
470 cl->sm_state = 0;
471 if (length == 4 &&
472 ((memcmp(mei_wd_state_independence_msg[0],
473 write_cb->request_buffer.data, 4) == 0) ||
474 (memcmp(mei_wd_state_independence_msg[1],
475 write_cb->request_buffer.data, 4) == 0) ||
476 (memcmp(mei_wd_state_independence_msg[2],
477 write_cb->request_buffer.data, 4) == 0)))
478 cl->sm_state |= MEI_WD_STATE_INDEPENDENCE_MSG_SENT;
479
Oren Weilab841162011-05-15 13:43:41 +0300480 if (cl == &dev->iamthif_cl) {
Tomas Winklerab5c4a52012-11-01 21:17:18 +0200481 rets = mei_amthif_write(dev, write_cb);
482
483 if (rets) {
484 dev_err(&dev->pdev->dev,
Tomas Winkler1a1aca42013-01-08 23:07:21 +0200485 "amthif write failed with status = %d\n", rets);
Tomas Winklerb0d0cf72012-11-01 21:17:13 +0200486 goto err;
Oren Weilab841162011-05-15 13:43:41 +0300487 }
488 mutex_unlock(&dev->device_lock);
Tomas Winkler75f0ee12012-10-09 16:50:18 +0200489 return length;
Oren Weilab841162011-05-15 13:43:41 +0300490 }
491
Tomas Winkler4b8960b2012-11-11 17:38:00 +0200492 write_cb->fop_type = MEI_FOP_WRITE;
Oren Weilab841162011-05-15 13:43:41 +0300493
494 dev_dbg(&dev->pdev->dev, "host client = %d, ME client = %d\n",
495 cl->host_client_id, cl->me_client_id);
Tomas Winkler90e0b5f2013-01-08 23:07:14 +0200496 rets = mei_cl_flow_ctrl_creds(cl);
Oren Weilab841162011-05-15 13:43:41 +0300497 if (rets < 0)
Tomas Winklerb0d0cf72012-11-01 21:17:13 +0200498 goto err;
Oren Weilab841162011-05-15 13:43:41 +0300499
Tomas Winklerb0d0cf72012-11-01 21:17:13 +0200500 if (rets == 0 || dev->mei_host_buffer_is_empty == false) {
Tomas Winklerebb108ef2012-10-09 16:50:16 +0200501 write_cb->buf_idx = 0;
Tomas Winklerb0d0cf72012-11-01 21:17:13 +0200502 mei_hdr.msg_complete = 0;
Oren Weilab841162011-05-15 13:43:41 +0300503 cl->writing_state = MEI_WRITING;
Tomas Winklerb0d0cf72012-11-01 21:17:13 +0200504 goto out;
505 }
506
507 dev->mei_host_buffer_is_empty = false;
508 if (length > mei_hbuf_max_data(dev)) {
509 mei_hdr.length = mei_hbuf_max_data(dev);
510 mei_hdr.msg_complete = 0;
511 } else {
512 mei_hdr.length = length;
513 mei_hdr.msg_complete = 1;
514 }
515 mei_hdr.host_addr = cl->host_client_id;
516 mei_hdr.me_addr = cl->me_client_id;
517 mei_hdr.reserved = 0;
Tomas Winkler15d4acc2012-12-25 19:06:00 +0200518
519 dev_dbg(&dev->pdev->dev, "write " MEI_HDR_FMT "\n",
520 MEI_HDR_PRM(&mei_hdr));
Tomas Winkler438763f2012-12-25 19:05:59 +0200521 if (mei_write_message(dev, &mei_hdr, write_cb->request_buffer.data)) {
Tomas Winklerb0d0cf72012-11-01 21:17:13 +0200522 rets = -ENODEV;
523 goto err;
524 }
525 cl->writing_state = MEI_WRITING;
526 write_cb->buf_idx = mei_hdr.length;
527
528out:
529 if (mei_hdr.msg_complete) {
Tomas Winkler90e0b5f2013-01-08 23:07:14 +0200530 if (mei_cl_flow_ctrl_reduce(cl)) {
Tomas Winklerb0d0cf72012-11-01 21:17:13 +0200531 rets = -ENODEV;
532 goto err;
533 }
534 list_add_tail(&write_cb->list, &dev->write_waiting_list.list);
535 } else {
Tomas Winklerfb601ad2012-10-15 12:06:48 +0200536 list_add_tail(&write_cb->list, &dev->write_list.list);
Oren Weilab841162011-05-15 13:43:41 +0300537 }
Tomas Winklerb0d0cf72012-11-01 21:17:13 +0200538
Oren Weilab841162011-05-15 13:43:41 +0300539 mutex_unlock(&dev->device_lock);
540 return length;
541
Tomas Winklerb0d0cf72012-11-01 21:17:13 +0200542err:
Oren Weilab841162011-05-15 13:43:41 +0300543 mutex_unlock(&dev->device_lock);
Tomas Winkler601a1ef2012-10-09 16:50:20 +0200544 mei_io_cb_free(write_cb);
Oren Weilab841162011-05-15 13:43:41 +0300545 return rets;
546}
547
Tomas Winkler9f81abda2013-01-08 23:07:15 +0200548/**
549 * mei_ioctl_connect_client - the connect to fw client IOCTL function
550 *
551 * @dev: the device structure
552 * @data: IOCTL connect data, input and output parameters
553 * @file: private data of the file object
554 *
555 * Locking: called under "dev->device_lock" lock
556 *
557 * returns 0 on success, <0 on failure.
558 */
559static int mei_ioctl_connect_client(struct file *file,
560 struct mei_connect_client_data *data)
561{
562 struct mei_device *dev;
563 struct mei_client *client;
564 struct mei_cl *cl;
565 int i;
566 int rets;
567
568 cl = file->private_data;
569 if (WARN_ON(!cl || !cl->dev))
570 return -ENODEV;
571
572 dev = cl->dev;
573
574 if (dev->dev_state != MEI_DEV_ENABLED) {
575 rets = -ENODEV;
576 goto end;
577 }
578
579 if (cl->state != MEI_FILE_INITIALIZING &&
580 cl->state != MEI_FILE_DISCONNECTED) {
581 rets = -EBUSY;
582 goto end;
583 }
584
585 /* find ME client we're trying to connect to */
586 i = mei_me_cl_by_uuid(dev, &data->in_client_uuid);
587 if (i >= 0 && !dev->me_clients[i].props.fixed_address) {
588 cl->me_client_id = dev->me_clients[i].client_id;
589 cl->state = MEI_FILE_CONNECTING;
590 }
591
592 dev_dbg(&dev->pdev->dev, "Connect to FW Client ID = %d\n",
593 cl->me_client_id);
594 dev_dbg(&dev->pdev->dev, "FW Client - Protocol Version = %d\n",
595 dev->me_clients[i].props.protocol_version);
596 dev_dbg(&dev->pdev->dev, "FW Client - Max Msg Len = %d\n",
597 dev->me_clients[i].props.max_msg_length);
598
Tomas Winkler1a1aca42013-01-08 23:07:21 +0200599 /* if we're connecting to amthif client then we will use the
Tomas Winkler9f81abda2013-01-08 23:07:15 +0200600 * existing connection
601 */
Tomas Winkler1a1aca42013-01-08 23:07:21 +0200602 if (uuid_le_cmp(data->in_client_uuid, mei_amthif_guid) == 0) {
Tomas Winkler9f81abda2013-01-08 23:07:15 +0200603 dev_dbg(&dev->pdev->dev, "FW Client is amthi\n");
604 if (dev->iamthif_cl.state != MEI_FILE_CONNECTED) {
605 rets = -ENODEV;
606 goto end;
607 }
608 clear_bit(cl->host_client_id, dev->host_clients_map);
609 mei_cl_unlink(cl);
610
611 kfree(cl);
612 cl = NULL;
613 file->private_data = &dev->iamthif_cl;
614
615 client = &data->out_client_properties;
616 client->max_msg_length =
617 dev->me_clients[i].props.max_msg_length;
618 client->protocol_version =
619 dev->me_clients[i].props.protocol_version;
620 rets = dev->iamthif_cl.status;
621
622 goto end;
623 }
624
625 if (cl->state != MEI_FILE_CONNECTING) {
626 rets = -ENODEV;
627 goto end;
628 }
629
630
631 /* prepare the output buffer */
632 client = &data->out_client_properties;
633 client->max_msg_length = dev->me_clients[i].props.max_msg_length;
634 client->protocol_version = dev->me_clients[i].props.protocol_version;
635 dev_dbg(&dev->pdev->dev, "Can connect?\n");
636
637
638 rets = mei_cl_connect(cl, file);
639
640end:
641 dev_dbg(&dev->pdev->dev, "free connect cb memory.");
642 return rets;
643}
644
Oren Weilab841162011-05-15 13:43:41 +0300645
646/**
647 * mei_ioctl - the IOCTL function
648 *
649 * @file: pointer to file structure
650 * @cmd: ioctl command
651 * @data: pointer to mei message structure
652 *
653 * returns 0 on success , <0 on error
654 */
655static long mei_ioctl(struct file *file, unsigned int cmd, unsigned long data)
656{
657 struct mei_device *dev;
658 struct mei_cl *cl = file->private_data;
659 struct mei_connect_client_data *connect_data = NULL;
660 int rets;
661
662 if (cmd != IOCTL_MEI_CONNECT_CLIENT)
663 return -EINVAL;
664
665 if (WARN_ON(!cl || !cl->dev))
666 return -ENODEV;
667
668 dev = cl->dev;
669
670 dev_dbg(&dev->pdev->dev, "IOCTL cmd = 0x%x", cmd);
671
672 mutex_lock(&dev->device_lock);
Tomas Winklerb210d752012-08-07 00:03:56 +0300673 if (dev->dev_state != MEI_DEV_ENABLED) {
Oren Weilab841162011-05-15 13:43:41 +0300674 rets = -ENODEV;
675 goto out;
676 }
677
678 dev_dbg(&dev->pdev->dev, ": IOCTL_MEI_CONNECT_CLIENT.\n");
679
680 connect_data = kzalloc(sizeof(struct mei_connect_client_data),
681 GFP_KERNEL);
682 if (!connect_data) {
683 rets = -ENOMEM;
684 goto out;
685 }
686 dev_dbg(&dev->pdev->dev, "copy connect data from user\n");
687 if (copy_from_user(connect_data, (char __user *)data,
688 sizeof(struct mei_connect_client_data))) {
689 dev_dbg(&dev->pdev->dev, "failed to copy data from userland\n");
690 rets = -EFAULT;
691 goto out;
692 }
Tomas Winkler9f81abda2013-01-08 23:07:15 +0200693
Oren Weilab841162011-05-15 13:43:41 +0300694 rets = mei_ioctl_connect_client(file, connect_data);
695
696 /* if all is ok, copying the data back to user. */
697 if (rets)
698 goto out;
699
700 dev_dbg(&dev->pdev->dev, "copy connect data to user\n");
701 if (copy_to_user((char __user *)data, connect_data,
702 sizeof(struct mei_connect_client_data))) {
703 dev_dbg(&dev->pdev->dev, "failed to copy data to userland\n");
704 rets = -EFAULT;
705 goto out;
706 }
707
708out:
709 kfree(connect_data);
710 mutex_unlock(&dev->device_lock);
711 return rets;
712}
713
714/**
715 * mei_compat_ioctl - the compat IOCTL function
716 *
717 * @file: pointer to file structure
718 * @cmd: ioctl command
719 * @data: pointer to mei message structure
720 *
721 * returns 0 on success , <0 on error
722 */
723#ifdef CONFIG_COMPAT
724static long mei_compat_ioctl(struct file *file,
Tomas Winkler441ab502011-12-13 23:39:34 +0200725 unsigned int cmd, unsigned long data)
Oren Weilab841162011-05-15 13:43:41 +0300726{
727 return mei_ioctl(file, cmd, (unsigned long)compat_ptr(data));
728}
729#endif
730
731
732/**
733 * mei_poll - the poll function
734 *
735 * @file: pointer to file structure
736 * @wait: pointer to poll_table structure
737 *
738 * returns poll mask
739 */
740static unsigned int mei_poll(struct file *file, poll_table *wait)
741{
742 struct mei_cl *cl = file->private_data;
743 struct mei_device *dev;
744 unsigned int mask = 0;
745
746 if (WARN_ON(!cl || !cl->dev))
747 return mask;
748
749 dev = cl->dev;
750
751 mutex_lock(&dev->device_lock);
752
Tomas Winklerb210d752012-08-07 00:03:56 +0300753 if (dev->dev_state != MEI_DEV_ENABLED)
Oren Weilab841162011-05-15 13:43:41 +0300754 goto out;
755
756
757 if (cl == &dev->iamthif_cl) {
Tomas Winkler744f0f22012-11-11 17:38:02 +0200758 mask = mei_amthif_poll(dev, file, wait);
Oren Weilab841162011-05-15 13:43:41 +0300759 goto out;
760 }
761
762 mutex_unlock(&dev->device_lock);
763 poll_wait(file, &cl->tx_wait, wait);
764 mutex_lock(&dev->device_lock);
765 if (MEI_WRITE_COMPLETE == cl->writing_state)
766 mask |= (POLLIN | POLLRDNORM);
767
768out:
769 mutex_unlock(&dev->device_lock);
770 return mask;
771}
772
Oren Weil5b881e32011-11-13 09:41:14 +0200773/*
774 * file operations structure will be used for mei char device.
775 */
776static const struct file_operations mei_fops = {
777 .owner = THIS_MODULE,
778 .read = mei_read,
779 .unlocked_ioctl = mei_ioctl,
780#ifdef CONFIG_COMPAT
781 .compat_ioctl = mei_compat_ioctl,
782#endif
783 .open = mei_open,
784 .release = mei_release,
785 .write = mei_write,
786 .poll = mei_poll,
787 .llseek = no_llseek
788};
789
790
791/*
792 * Misc Device Struct
793 */
794static struct miscdevice mei_misc_device = {
Tomas Winklerc38ea242012-04-02 20:32:39 +0300795 .name = "mei",
Oren Weil5b881e32011-11-13 09:41:14 +0200796 .fops = &mei_fops,
797 .minor = MISC_DYNAMIC_MINOR,
798};
799
800/**
Tomas Winkler9a123f12012-08-06 15:23:55 +0300801 * mei_quirk_probe - probe for devices that doesn't valid ME interface
802 * @pdev: PCI device structure
803 * @ent: entry into pci_device_table
804 *
805 * returns true if ME Interface is valid, false otherwise
806 */
Bill Pemberton80c8ae22012-11-19 13:23:05 -0500807static bool mei_quirk_probe(struct pci_dev *pdev,
Tomas Winkler9a123f12012-08-06 15:23:55 +0300808 const struct pci_device_id *ent)
809{
810 u32 reg;
811 if (ent->device == MEI_DEV_ID_PBG_1) {
812 pci_read_config_dword(pdev, 0x48, &reg);
813 /* make sure that bit 9 is up and bit 10 is down */
814 if ((reg & 0x600) == 0x200) {
815 dev_info(&pdev->dev, "Device doesn't have valid ME Interface\n");
816 return false;
817 }
818 }
819 return true;
820}
821/**
Oren Weil5b881e32011-11-13 09:41:14 +0200822 * mei_probe - Device Initialization Routine
823 *
824 * @pdev: PCI device structure
825 * @ent: entry in kcs_pci_tbl
826 *
827 * returns 0 on success, <0 on failure.
828 */
Bill Pemberton80c8ae22012-11-19 13:23:05 -0500829static int mei_probe(struct pci_dev *pdev,
Oren Weil5b881e32011-11-13 09:41:14 +0200830 const struct pci_device_id *ent)
831{
832 struct mei_device *dev;
833 int err;
834
835 mutex_lock(&mei_mutex);
Tomas Winkler9a123f12012-08-06 15:23:55 +0300836
837 if (!mei_quirk_probe(pdev, ent)) {
838 err = -ENODEV;
839 goto end;
840 }
841
Tomas Winklerdaed6b52012-08-17 09:54:23 +0300842 if (mei_pdev) {
Oren Weil5b881e32011-11-13 09:41:14 +0200843 err = -EEXIST;
844 goto end;
845 }
846 /* enable pci dev */
847 err = pci_enable_device(pdev);
848 if (err) {
Tomas Winkler32c826b2012-05-08 23:04:56 +0300849 dev_err(&pdev->dev, "failed to enable pci device.\n");
Oren Weil5b881e32011-11-13 09:41:14 +0200850 goto end;
851 }
852 /* set PCI host mastering */
853 pci_set_master(pdev);
854 /* pci request regions for mei driver */
Tomas Winkler068c0ae2012-08-07 00:03:54 +0300855 err = pci_request_regions(pdev, KBUILD_MODNAME);
Oren Weil5b881e32011-11-13 09:41:14 +0200856 if (err) {
Tomas Winkler32c826b2012-05-08 23:04:56 +0300857 dev_err(&pdev->dev, "failed to get pci regions.\n");
Oren Weil5b881e32011-11-13 09:41:14 +0200858 goto disable_device;
859 }
860 /* allocates and initializes the mei dev structure */
861 dev = mei_device_init(pdev);
862 if (!dev) {
863 err = -ENOMEM;
864 goto release_regions;
865 }
866 /* mapping IO device memory */
867 dev->mem_addr = pci_iomap(pdev, 0, 0);
868 if (!dev->mem_addr) {
Tomas Winkler32c826b2012-05-08 23:04:56 +0300869 dev_err(&pdev->dev, "mapping I/O device memory failure.\n");
Oren Weil5b881e32011-11-13 09:41:14 +0200870 err = -ENOMEM;
871 goto free_device;
872 }
873 pci_enable_msi(pdev);
874
875 /* request and enable interrupt */
876 if (pci_dev_msi_enabled(pdev))
877 err = request_threaded_irq(pdev->irq,
878 NULL,
879 mei_interrupt_thread_handler,
Tomas Winkler068c0ae2012-08-07 00:03:54 +0300880 IRQF_ONESHOT, KBUILD_MODNAME, dev);
Oren Weil5b881e32011-11-13 09:41:14 +0200881 else
882 err = request_threaded_irq(pdev->irq,
883 mei_interrupt_quick_handler,
884 mei_interrupt_thread_handler,
Tomas Winkler068c0ae2012-08-07 00:03:54 +0300885 IRQF_SHARED, KBUILD_MODNAME, dev);
Oren Weil5b881e32011-11-13 09:41:14 +0200886
887 if (err) {
Tomas Winkler32c826b2012-05-08 23:04:56 +0300888 dev_err(&pdev->dev, "request_threaded_irq failure. irq = %d\n",
Oren Weil5b881e32011-11-13 09:41:14 +0200889 pdev->irq);
Samuel Ortiz169dc382012-06-11 12:18:30 +0300890 goto disable_msi;
Oren Weil5b881e32011-11-13 09:41:14 +0200891 }
892 INIT_DELAYED_WORK(&dev->timer_work, mei_timer);
Samuel Ortizc1174c02012-11-18 15:13:20 +0200893 INIT_WORK(&dev->init_work, mei_host_client_init);
894
Oren Weil5b881e32011-11-13 09:41:14 +0200895 if (mei_hw_init(dev)) {
Tomas Winkler32c826b2012-05-08 23:04:56 +0300896 dev_err(&pdev->dev, "init hw failure.\n");
Oren Weil5b881e32011-11-13 09:41:14 +0200897 err = -ENODEV;
898 goto release_irq;
899 }
900
901 err = misc_register(&mei_misc_device);
902 if (err)
903 goto release_irq;
904
Tomas Winklerdaed6b52012-08-17 09:54:23 +0300905 mei_pdev = pdev;
Oren Weil5b881e32011-11-13 09:41:14 +0200906 pci_set_drvdata(pdev, dev);
907
908
909 schedule_delayed_work(&dev->timer_work, HZ);
910
911 mutex_unlock(&mei_mutex);
912
Tomas Winkler2f3d2b42012-03-19 22:38:13 +0200913 pr_debug("initialization successful.\n");
Oren Weil5b881e32011-11-13 09:41:14 +0200914
915 return 0;
916
917release_irq:
918 /* disable interrupts */
919 dev->host_hw_state = mei_hcsr_read(dev);
920 mei_disable_interrupts(dev);
921 flush_scheduled_work();
922 free_irq(pdev->irq, dev);
Samuel Ortiz169dc382012-06-11 12:18:30 +0300923disable_msi:
Oren Weil5b881e32011-11-13 09:41:14 +0200924 pci_disable_msi(pdev);
Oren Weil5b881e32011-11-13 09:41:14 +0200925 pci_iounmap(pdev, dev->mem_addr);
926free_device:
927 kfree(dev);
928release_regions:
929 pci_release_regions(pdev);
930disable_device:
931 pci_disable_device(pdev);
932end:
933 mutex_unlock(&mei_mutex);
Tomas Winkler32c826b2012-05-08 23:04:56 +0300934 dev_err(&pdev->dev, "initialization failed.\n");
Oren Weil5b881e32011-11-13 09:41:14 +0200935 return err;
936}
937
938/**
939 * mei_remove - Device Removal Routine
940 *
941 * @pdev: PCI device structure
942 *
943 * mei_remove is called by the PCI subsystem to alert the driver
944 * that it should release a PCI device.
945 */
Bill Pemberton486a5c22012-11-19 13:26:02 -0500946static void mei_remove(struct pci_dev *pdev)
Oren Weil5b881e32011-11-13 09:41:14 +0200947{
948 struct mei_device *dev;
949
Tomas Winklerdaed6b52012-08-17 09:54:23 +0300950 if (mei_pdev != pdev)
Oren Weil5b881e32011-11-13 09:41:14 +0200951 return;
952
953 dev = pci_get_drvdata(pdev);
954 if (!dev)
955 return;
956
957 mutex_lock(&dev->device_lock);
958
Tomas Winklerc216fde2012-08-16 19:39:43 +0300959 cancel_delayed_work(&dev->timer_work);
960
961 mei_wd_stop(dev);
Oren Weil5b881e32011-11-13 09:41:14 +0200962
Tomas Winklerdaed6b52012-08-17 09:54:23 +0300963 mei_pdev = NULL;
Oren Weil5b881e32011-11-13 09:41:14 +0200964
965 if (dev->iamthif_cl.state == MEI_FILE_CONNECTED) {
966 dev->iamthif_cl.state = MEI_FILE_DISCONNECTING;
Tomas Winkler90e0b5f2013-01-08 23:07:14 +0200967 mei_cl_disconnect(&dev->iamthif_cl);
Oren Weil5b881e32011-11-13 09:41:14 +0200968 }
969 if (dev->wd_cl.state == MEI_FILE_CONNECTED) {
970 dev->wd_cl.state = MEI_FILE_DISCONNECTING;
Tomas Winkler90e0b5f2013-01-08 23:07:14 +0200971 mei_cl_disconnect(&dev->wd_cl);
Oren Weil5b881e32011-11-13 09:41:14 +0200972 }
973
974 /* Unregistering watchdog device */
Tomas Winkler70cd5332011-12-22 18:50:50 +0200975 mei_watchdog_unregister(dev);
Oren Weil5b881e32011-11-13 09:41:14 +0200976
977 /* remove entry if already in list */
978 dev_dbg(&pdev->dev, "list del iamthif and wd file list.\n");
Tomas Winkler781d0d82013-01-08 23:07:22 +0200979
980 if (dev->open_handle_count > 0)
981 dev->open_handle_count--;
Tomas Winkler90e0b5f2013-01-08 23:07:14 +0200982 mei_cl_unlink(&dev->wd_cl);
Tomas Winkler781d0d82013-01-08 23:07:22 +0200983
984 if (dev->open_handle_count > 0)
985 dev->open_handle_count--;
Tomas Winkler90e0b5f2013-01-08 23:07:14 +0200986 mei_cl_unlink(&dev->iamthif_cl);
Oren Weil5b881e32011-11-13 09:41:14 +0200987
988 dev->iamthif_current_cb = NULL;
989 dev->me_clients_num = 0;
990
991 mutex_unlock(&dev->device_lock);
992
993 flush_scheduled_work();
994
995 /* disable interrupts */
996 mei_disable_interrupts(dev);
997
998 free_irq(pdev->irq, dev);
999 pci_disable_msi(pdev);
1000 pci_set_drvdata(pdev, NULL);
1001
1002 if (dev->mem_addr)
1003 pci_iounmap(pdev, dev->mem_addr);
1004
1005 kfree(dev);
1006
1007 pci_release_regions(pdev);
1008 pci_disable_device(pdev);
Tomas Winklera44cab42012-05-29 16:39:11 +03001009
1010 misc_deregister(&mei_misc_device);
Oren Weil5b881e32011-11-13 09:41:14 +02001011}
Oren Weilab841162011-05-15 13:43:41 +03001012#ifdef CONFIG_PM
1013static int mei_pci_suspend(struct device *device)
1014{
1015 struct pci_dev *pdev = to_pci_dev(device);
1016 struct mei_device *dev = pci_get_drvdata(pdev);
1017 int err;
1018
1019 if (!dev)
1020 return -ENODEV;
1021 mutex_lock(&dev->device_lock);
Tomas Winklerc216fde2012-08-16 19:39:43 +03001022
1023 cancel_delayed_work(&dev->timer_work);
1024
Oren Weilab841162011-05-15 13:43:41 +03001025 /* Stop watchdog if exists */
Tomas Winklerc216fde2012-08-16 19:39:43 +03001026 err = mei_wd_stop(dev);
Oren Weilab841162011-05-15 13:43:41 +03001027 /* Set new mei state */
Tomas Winklerb210d752012-08-07 00:03:56 +03001028 if (dev->dev_state == MEI_DEV_ENABLED ||
1029 dev->dev_state == MEI_DEV_RECOVERING_FROM_RESET) {
1030 dev->dev_state = MEI_DEV_POWER_DOWN;
Oren Weilab841162011-05-15 13:43:41 +03001031 mei_reset(dev, 0);
1032 }
1033 mutex_unlock(&dev->device_lock);
1034
1035 free_irq(pdev->irq, dev);
Tomas Winkler4f61a7a2011-07-14 20:11:25 +03001036 pci_disable_msi(pdev);
Oren Weilab841162011-05-15 13:43:41 +03001037
1038 return err;
1039}
1040
1041static int mei_pci_resume(struct device *device)
1042{
1043 struct pci_dev *pdev = to_pci_dev(device);
1044 struct mei_device *dev;
1045 int err;
1046
1047 dev = pci_get_drvdata(pdev);
1048 if (!dev)
1049 return -ENODEV;
1050
Tomas Winkler4f61a7a2011-07-14 20:11:25 +03001051 pci_enable_msi(pdev);
1052
1053 /* request and enable interrupt */
1054 if (pci_dev_msi_enabled(pdev))
1055 err = request_threaded_irq(pdev->irq,
1056 NULL,
1057 mei_interrupt_thread_handler,
Tomas Winkler068c0ae2012-08-07 00:03:54 +03001058 IRQF_ONESHOT, KBUILD_MODNAME, dev);
Tomas Winkler4f61a7a2011-07-14 20:11:25 +03001059 else
1060 err = request_threaded_irq(pdev->irq,
Oren Weilab841162011-05-15 13:43:41 +03001061 mei_interrupt_quick_handler,
1062 mei_interrupt_thread_handler,
Tomas Winkler068c0ae2012-08-07 00:03:54 +03001063 IRQF_SHARED, KBUILD_MODNAME, dev);
Tomas Winkler4f61a7a2011-07-14 20:11:25 +03001064
Oren Weilab841162011-05-15 13:43:41 +03001065 if (err) {
Tomas Winkler32c826b2012-05-08 23:04:56 +03001066 dev_err(&pdev->dev, "request_threaded_irq failed: irq = %d.\n",
1067 pdev->irq);
Oren Weilab841162011-05-15 13:43:41 +03001068 return err;
1069 }
1070
1071 mutex_lock(&dev->device_lock);
Tomas Winklerb210d752012-08-07 00:03:56 +03001072 dev->dev_state = MEI_DEV_POWER_UP;
Oren Weilab841162011-05-15 13:43:41 +03001073 mei_reset(dev, 1);
1074 mutex_unlock(&dev->device_lock);
1075
Oren Weil6d70e932011-09-07 09:03:14 +03001076 /* Start timer if stopped in suspend */
1077 schedule_delayed_work(&dev->timer_work, HZ);
1078
Oren Weilab841162011-05-15 13:43:41 +03001079 return err;
1080}
1081static SIMPLE_DEV_PM_OPS(mei_pm_ops, mei_pci_suspend, mei_pci_resume);
1082#define MEI_PM_OPS (&mei_pm_ops)
1083#else
Randy Dunlap2d990362011-05-19 08:52:34 -07001084#define MEI_PM_OPS NULL
Oren Weilab841162011-05-15 13:43:41 +03001085#endif /* CONFIG_PM */
1086/*
1087 * PCI driver structure
1088 */
1089static struct pci_driver mei_driver = {
Tomas Winkler068c0ae2012-08-07 00:03:54 +03001090 .name = KBUILD_MODNAME,
Oren Weilab841162011-05-15 13:43:41 +03001091 .id_table = mei_pci_tbl,
1092 .probe = mei_probe,
Bill Pemberton9306a8b2012-11-19 13:20:25 -05001093 .remove = mei_remove,
1094 .shutdown = mei_remove,
Oren Weilab841162011-05-15 13:43:41 +03001095 .driver.pm = MEI_PM_OPS,
1096};
1097
Tomas Winkler60781882012-07-19 09:45:32 +03001098module_pci_driver(mei_driver);
Oren Weilab841162011-05-15 13:43:41 +03001099
1100MODULE_AUTHOR("Intel Corporation");
1101MODULE_DESCRIPTION("Intel(R) Management Engine Interface");
1102MODULE_LICENSE("GPL v2");