]>
Commit | Line | Data |
---|---|---|
795536ac TW |
1 | /* |
2 | * | |
3 | * Intel Management Engine Interface (Intel MEI) Linux driver | |
4 | * Copyright (c) 2013-2014, Intel Corporation. | |
5 | * | |
6 | * This program is free software; you can redistribute it and/or modify it | |
7 | * under the terms and conditions of the GNU General Public License, | |
8 | * version 2, as published by the Free Software Foundation. | |
9 | * | |
10 | * This program is distributed in the hope it will be useful, but WITHOUT | |
11 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | |
12 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for | |
13 | * more details. | |
14 | * | |
15 | */ | |
16 | ||
17 | #include <linux/module.h> | |
18 | #include <linux/kernel.h> | |
19 | #include <linux/device.h> | |
20 | #include <linux/fs.h> | |
21 | #include <linux/errno.h> | |
22 | #include <linux/types.h> | |
23 | #include <linux/pci.h> | |
24 | #include <linux/init.h> | |
25 | #include <linux/sched.h> | |
26 | #include <linux/uuid.h> | |
27 | #include <linux/jiffies.h> | |
28 | #include <linux/interrupt.h> | |
29 | #include <linux/workqueue.h> | |
989561de | 30 | #include <linux/pm_domain.h> |
cfe5ab85 | 31 | #include <linux/pm_runtime.h> |
795536ac TW |
32 | |
33 | #include <linux/mei.h> | |
34 | ||
35 | ||
36 | #include "mei_dev.h" | |
37 | #include "hw-txe.h" | |
38 | ||
a05f8f86 | 39 | static const struct pci_device_id mei_txe_pci_tbl[] = { |
4ad96db6 | 40 | {PCI_VDEVICE(INTEL, 0x0F18)}, /* Baytrail */ |
e88281ed | 41 | {PCI_VDEVICE(INTEL, 0x2298)}, /* Cherrytrail */ |
4ad96db6 | 42 | |
795536ac TW |
43 | {0, } |
44 | }; | |
45 | MODULE_DEVICE_TABLE(pci, mei_txe_pci_tbl); | |
46 | ||
bbd6d050 | 47 | #ifdef CONFIG_PM |
d2d56fae AU |
48 | static inline void mei_txe_set_pm_domain(struct mei_device *dev); |
49 | static inline void mei_txe_unset_pm_domain(struct mei_device *dev); | |
50 | #else | |
51 | static inline void mei_txe_set_pm_domain(struct mei_device *dev) {} | |
52 | static inline void mei_txe_unset_pm_domain(struct mei_device *dev) {} | |
bbd6d050 | 53 | #endif /* CONFIG_PM */ |
795536ac | 54 | |
795536ac | 55 | /** |
3908be6f | 56 | * mei_txe_probe - Device Initialization Routine |
795536ac TW |
57 | * |
58 | * @pdev: PCI device structure | |
59 | * @ent: entry in mei_txe_pci_tbl | |
60 | * | |
a8605ea2 | 61 | * Return: 0 on success, <0 on failure. |
795536ac TW |
62 | */ |
63 | static int mei_txe_probe(struct pci_dev *pdev, const struct pci_device_id *ent) | |
64 | { | |
65 | struct mei_device *dev; | |
66 | struct mei_txe_hw *hw; | |
f8a09605 | 67 | const int mask = BIT(SEC_BAR) | BIT(BRIDGE_BAR); |
795536ac | 68 | int err; |
795536ac TW |
69 | |
70 | /* enable pci dev */ | |
f8a09605 | 71 | err = pcim_enable_device(pdev); |
795536ac TW |
72 | if (err) { |
73 | dev_err(&pdev->dev, "failed to enable pci device.\n"); | |
74 | goto end; | |
75 | } | |
76 | /* set PCI host mastering */ | |
77 | pci_set_master(pdev); | |
f8a09605 TW |
78 | /* pci request regions and mapping IO device memory for mei driver */ |
79 | err = pcim_iomap_regions(pdev, mask, KBUILD_MODNAME); | |
795536ac TW |
80 | if (err) { |
81 | dev_err(&pdev->dev, "failed to get pci regions.\n"); | |
f8a09605 | 82 | goto end; |
795536ac TW |
83 | } |
84 | ||
85 | err = pci_set_dma_mask(pdev, DMA_BIT_MASK(36)); | |
86 | if (err) { | |
87 | err = pci_set_dma_mask(pdev, DMA_BIT_MASK(32)); | |
88 | if (err) { | |
89 | dev_err(&pdev->dev, "No suitable DMA available.\n"); | |
f8a09605 | 90 | goto end; |
795536ac TW |
91 | } |
92 | } | |
93 | ||
94 | /* allocates and initializes the mei dev structure */ | |
4ad96db6 | 95 | dev = mei_txe_dev_init(pdev); |
795536ac TW |
96 | if (!dev) { |
97 | err = -ENOMEM; | |
f8a09605 | 98 | goto end; |
795536ac TW |
99 | } |
100 | hw = to_txe_hw(dev); | |
f8a09605 | 101 | hw->mem_addr = pcim_iomap_table(pdev); |
795536ac TW |
102 | |
103 | pci_enable_msi(pdev); | |
104 | ||
105 | /* clear spurious interrupts */ | |
106 | mei_clear_interrupts(dev); | |
107 | ||
108 | /* request and enable interrupt */ | |
109 | if (pci_dev_msi_enabled(pdev)) | |
110 | err = request_threaded_irq(pdev->irq, | |
111 | NULL, | |
112 | mei_txe_irq_thread_handler, | |
113 | IRQF_ONESHOT, KBUILD_MODNAME, dev); | |
114 | else | |
115 | err = request_threaded_irq(pdev->irq, | |
116 | mei_txe_irq_quick_handler, | |
117 | mei_txe_irq_thread_handler, | |
118 | IRQF_SHARED, KBUILD_MODNAME, dev); | |
119 | if (err) { | |
120 | dev_err(&pdev->dev, "mei: request_threaded_irq failure. irq = %d\n", | |
121 | pdev->irq); | |
f8a09605 | 122 | goto end; |
795536ac TW |
123 | } |
124 | ||
125 | if (mei_start(dev)) { | |
126 | dev_err(&pdev->dev, "init hw failure.\n"); | |
127 | err = -ENODEV; | |
128 | goto release_irq; | |
129 | } | |
130 | ||
cfe5ab85 AU |
131 | pm_runtime_set_autosuspend_delay(&pdev->dev, MEI_TXI_RPM_TIMEOUT); |
132 | pm_runtime_use_autosuspend(&pdev->dev); | |
133 | ||
f3d8e878 | 134 | err = mei_register(dev, &pdev->dev); |
795536ac | 135 | if (err) |
1f7e489a | 136 | goto stop; |
795536ac TW |
137 | |
138 | pci_set_drvdata(pdev, dev); | |
139 | ||
557909e1 AU |
140 | /* |
141 | * MEI requires to resume from runtime suspend mode | |
142 | * in order to perform link reset flow upon system suspend. | |
143 | */ | |
144 | pdev->dev_flags |= PCI_DEV_FLAGS_NEEDS_RESUME; | |
145 | ||
d2d56fae | 146 | /* |
b42dc063 AU |
147 | * TXE maps runtime suspend/resume to own power gating states, |
148 | * hence we need to go around native PCI runtime service which | |
149 | * eventually brings the device into D3cold/hot state. | |
150 | * But the TXE device cannot wake up from D3 unlike from own | |
151 | * power gating. To get around PCI device native runtime pm, | |
152 | * TXE uses runtime pm domain handlers which take precedence. | |
153 | */ | |
154 | mei_txe_set_pm_domain(dev); | |
d2d56fae | 155 | |
cfe5ab85 AU |
156 | pm_runtime_put_noidle(&pdev->dev); |
157 | ||
795536ac TW |
158 | return 0; |
159 | ||
1f7e489a AU |
160 | stop: |
161 | mei_stop(dev); | |
795536ac | 162 | release_irq: |
795536ac | 163 | mei_cancel_work(dev); |
795536ac | 164 | mei_disable_interrupts(dev); |
795536ac | 165 | free_irq(pdev->irq, dev); |
795536ac TW |
166 | end: |
167 | dev_err(&pdev->dev, "initialization failed.\n"); | |
168 | return err; | |
169 | } | |
170 | ||
5c4c0106 TW |
171 | /** |
172 | * mei_txe_remove - Device Shutdown Routine | |
173 | * | |
174 | * @pdev: PCI device structure | |
175 | * | |
176 | * mei_txe_shutdown is called from the reboot notifier | |
177 | * it's a simplified version of remove so we go down | |
178 | * faster. | |
179 | */ | |
180 | static void mei_txe_shutdown(struct pci_dev *pdev) | |
181 | { | |
182 | struct mei_device *dev; | |
183 | ||
184 | dev = pci_get_drvdata(pdev); | |
185 | if (!dev) | |
186 | return; | |
187 | ||
188 | dev_dbg(&pdev->dev, "shutdown\n"); | |
189 | mei_stop(dev); | |
190 | ||
b42dc063 | 191 | mei_txe_unset_pm_domain(dev); |
5c4c0106 TW |
192 | |
193 | mei_disable_interrupts(dev); | |
194 | free_irq(pdev->irq, dev); | |
195 | } | |
196 | ||
795536ac | 197 | /** |
3908be6f | 198 | * mei_txe_remove - Device Removal Routine |
795536ac TW |
199 | * |
200 | * @pdev: PCI device structure | |
201 | * | |
202 | * mei_remove is called by the PCI subsystem to alert the driver | |
203 | * that it should release a PCI device. | |
204 | */ | |
205 | static void mei_txe_remove(struct pci_dev *pdev) | |
206 | { | |
207 | struct mei_device *dev; | |
795536ac TW |
208 | |
209 | dev = pci_get_drvdata(pdev); | |
210 | if (!dev) { | |
f8a09605 | 211 | dev_err(&pdev->dev, "mei: dev == NULL\n"); |
795536ac TW |
212 | return; |
213 | } | |
214 | ||
cfe5ab85 AU |
215 | pm_runtime_get_noresume(&pdev->dev); |
216 | ||
795536ac TW |
217 | mei_stop(dev); |
218 | ||
b42dc063 | 219 | mei_txe_unset_pm_domain(dev); |
d2d56fae | 220 | |
795536ac TW |
221 | mei_disable_interrupts(dev); |
222 | free_irq(pdev->irq, dev); | |
795536ac TW |
223 | |
224 | mei_deregister(dev); | |
795536ac TW |
225 | } |
226 | ||
227 | ||
e0270add | 228 | #ifdef CONFIG_PM_SLEEP |
795536ac TW |
229 | static int mei_txe_pci_suspend(struct device *device) |
230 | { | |
231 | struct pci_dev *pdev = to_pci_dev(device); | |
232 | struct mei_device *dev = pci_get_drvdata(pdev); | |
233 | ||
234 | if (!dev) | |
235 | return -ENODEV; | |
236 | ||
237 | dev_dbg(&pdev->dev, "suspend\n"); | |
238 | ||
239 | mei_stop(dev); | |
240 | ||
241 | mei_disable_interrupts(dev); | |
242 | ||
243 | free_irq(pdev->irq, dev); | |
244 | pci_disable_msi(pdev); | |
245 | ||
246 | return 0; | |
247 | } | |
248 | ||
249 | static int mei_txe_pci_resume(struct device *device) | |
250 | { | |
251 | struct pci_dev *pdev = to_pci_dev(device); | |
252 | struct mei_device *dev; | |
253 | int err; | |
254 | ||
255 | dev = pci_get_drvdata(pdev); | |
256 | if (!dev) | |
257 | return -ENODEV; | |
258 | ||
259 | pci_enable_msi(pdev); | |
260 | ||
261 | mei_clear_interrupts(dev); | |
262 | ||
263 | /* request and enable interrupt */ | |
264 | if (pci_dev_msi_enabled(pdev)) | |
265 | err = request_threaded_irq(pdev->irq, | |
266 | NULL, | |
267 | mei_txe_irq_thread_handler, | |
268 | IRQF_ONESHOT, KBUILD_MODNAME, dev); | |
269 | else | |
270 | err = request_threaded_irq(pdev->irq, | |
271 | mei_txe_irq_quick_handler, | |
272 | mei_txe_irq_thread_handler, | |
273 | IRQF_SHARED, KBUILD_MODNAME, dev); | |
274 | if (err) { | |
275 | dev_err(&pdev->dev, "request_threaded_irq failed: irq = %d.\n", | |
276 | pdev->irq); | |
277 | return err; | |
278 | } | |
279 | ||
280 | err = mei_restart(dev); | |
281 | ||
282 | return err; | |
283 | } | |
cfe5ab85 AU |
284 | #endif /* CONFIG_PM_SLEEP */ |
285 | ||
bbd6d050 | 286 | #ifdef CONFIG_PM |
cfe5ab85 AU |
287 | static int mei_txe_pm_runtime_idle(struct device *device) |
288 | { | |
289 | struct pci_dev *pdev = to_pci_dev(device); | |
290 | struct mei_device *dev; | |
291 | ||
292 | dev_dbg(&pdev->dev, "rpm: txe: runtime_idle\n"); | |
293 | ||
294 | dev = pci_get_drvdata(pdev); | |
295 | if (!dev) | |
296 | return -ENODEV; | |
297 | if (mei_write_is_idle(dev)) | |
d5d83f8a | 298 | pm_runtime_autosuspend(device); |
cfe5ab85 AU |
299 | |
300 | return -EBUSY; | |
301 | } | |
302 | static int mei_txe_pm_runtime_suspend(struct device *device) | |
303 | { | |
304 | struct pci_dev *pdev = to_pci_dev(device); | |
305 | struct mei_device *dev; | |
306 | int ret; | |
307 | ||
308 | dev_dbg(&pdev->dev, "rpm: txe: runtime suspend\n"); | |
309 | ||
310 | dev = pci_get_drvdata(pdev); | |
311 | if (!dev) | |
312 | return -ENODEV; | |
795536ac | 313 | |
cfe5ab85 AU |
314 | mutex_lock(&dev->device_lock); |
315 | ||
316 | if (mei_write_is_idle(dev)) | |
317 | ret = mei_txe_aliveness_set_sync(dev, 0); | |
318 | else | |
319 | ret = -EAGAIN; | |
320 | ||
b42dc063 | 321 | /* keep irq on we are staying in D0 */ |
cfe5ab85 AU |
322 | |
323 | dev_dbg(&pdev->dev, "rpm: txe: runtime suspend ret=%d\n", ret); | |
324 | ||
325 | mutex_unlock(&dev->device_lock); | |
77537ad2 AU |
326 | |
327 | if (ret && ret != -EAGAIN) | |
328 | schedule_work(&dev->reset_work); | |
329 | ||
cfe5ab85 AU |
330 | return ret; |
331 | } | |
332 | ||
333 | static int mei_txe_pm_runtime_resume(struct device *device) | |
334 | { | |
335 | struct pci_dev *pdev = to_pci_dev(device); | |
336 | struct mei_device *dev; | |
337 | int ret; | |
338 | ||
339 | dev_dbg(&pdev->dev, "rpm: txe: runtime resume\n"); | |
340 | ||
341 | dev = pci_get_drvdata(pdev); | |
342 | if (!dev) | |
343 | return -ENODEV; | |
344 | ||
345 | mutex_lock(&dev->device_lock); | |
346 | ||
347 | mei_enable_interrupts(dev); | |
348 | ||
349 | ret = mei_txe_aliveness_set_sync(dev, 1); | |
350 | ||
351 | mutex_unlock(&dev->device_lock); | |
352 | ||
353 | dev_dbg(&pdev->dev, "rpm: txe: runtime resume ret = %d\n", ret); | |
354 | ||
77537ad2 AU |
355 | if (ret) |
356 | schedule_work(&dev->reset_work); | |
357 | ||
cfe5ab85 AU |
358 | return ret; |
359 | } | |
d2d56fae AU |
360 | |
361 | /** | |
7efceb55 | 362 | * mei_txe_set_pm_domain - fill and set pm domain structure for device |
d2d56fae AU |
363 | * |
364 | * @dev: mei_device | |
365 | */ | |
366 | static inline void mei_txe_set_pm_domain(struct mei_device *dev) | |
367 | { | |
d08b8fc0 | 368 | struct pci_dev *pdev = to_pci_dev(dev->dev); |
d2d56fae AU |
369 | |
370 | if (pdev->dev.bus && pdev->dev.bus->pm) { | |
371 | dev->pg_domain.ops = *pdev->dev.bus->pm; | |
372 | ||
373 | dev->pg_domain.ops.runtime_suspend = mei_txe_pm_runtime_suspend; | |
374 | dev->pg_domain.ops.runtime_resume = mei_txe_pm_runtime_resume; | |
375 | dev->pg_domain.ops.runtime_idle = mei_txe_pm_runtime_idle; | |
376 | ||
989561de | 377 | dev_pm_domain_set(&pdev->dev, &dev->pg_domain); |
d2d56fae AU |
378 | } |
379 | } | |
380 | ||
381 | /** | |
7efceb55 | 382 | * mei_txe_unset_pm_domain - clean pm domain structure for device |
d2d56fae AU |
383 | * |
384 | * @dev: mei_device | |
385 | */ | |
386 | static inline void mei_txe_unset_pm_domain(struct mei_device *dev) | |
387 | { | |
388 | /* stop using pm callbacks if any */ | |
989561de | 389 | dev_pm_domain_set(dev->dev, NULL); |
d2d56fae | 390 | } |
cfe5ab85 | 391 | |
cfe5ab85 AU |
392 | static const struct dev_pm_ops mei_txe_pm_ops = { |
393 | SET_SYSTEM_SLEEP_PM_OPS(mei_txe_pci_suspend, | |
394 | mei_txe_pci_resume) | |
395 | SET_RUNTIME_PM_OPS( | |
396 | mei_txe_pm_runtime_suspend, | |
397 | mei_txe_pm_runtime_resume, | |
398 | mei_txe_pm_runtime_idle) | |
399 | }; | |
795536ac TW |
400 | |
401 | #define MEI_TXE_PM_OPS (&mei_txe_pm_ops) | |
402 | #else | |
403 | #define MEI_TXE_PM_OPS NULL | |
cfe5ab85 AU |
404 | #endif /* CONFIG_PM */ |
405 | ||
795536ac TW |
406 | /* |
407 | * PCI driver structure | |
408 | */ | |
409 | static struct pci_driver mei_txe_driver = { | |
410 | .name = KBUILD_MODNAME, | |
411 | .id_table = mei_txe_pci_tbl, | |
412 | .probe = mei_txe_probe, | |
413 | .remove = mei_txe_remove, | |
5c4c0106 | 414 | .shutdown = mei_txe_shutdown, |
795536ac TW |
415 | .driver.pm = MEI_TXE_PM_OPS, |
416 | }; | |
417 | ||
418 | module_pci_driver(mei_txe_driver); | |
419 | ||
420 | MODULE_AUTHOR("Intel Corporation"); | |
421 | MODULE_DESCRIPTION("Intel(R) Trusted Execution Environment Interface"); | |
422 | MODULE_LICENSE("GPL v2"); |