1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16#include <linux/module.h>
17#include <linux/moduleparam.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/sched.h>
25#include <linux/interrupt.h>
26#include <linux/workqueue.h>
27#define CREATE_TRACE_POINTS
28#include <trace/events/intel_ish.h>
29#include "ishtp-dev.h"
30#include "hw-ish.h"
31
32static const struct pci_device_id ish_pci_tbl[] = {
33 {PCI_DEVICE(PCI_VENDOR_ID_INTEL, CHV_DEVICE_ID)},
34 {PCI_DEVICE(PCI_VENDOR_ID_INTEL, BXT_Ax_DEVICE_ID)},
35 {PCI_DEVICE(PCI_VENDOR_ID_INTEL, BXT_Bx_DEVICE_ID)},
36 {PCI_DEVICE(PCI_VENDOR_ID_INTEL, APL_Ax_DEVICE_ID)},
37 {PCI_DEVICE(PCI_VENDOR_ID_INTEL, SPT_Ax_DEVICE_ID)},
38 {PCI_DEVICE(PCI_VENDOR_ID_INTEL, CNL_Ax_DEVICE_ID)},
39 {PCI_DEVICE(PCI_VENDOR_ID_INTEL, GLK_Ax_DEVICE_ID)},
40 {PCI_DEVICE(PCI_VENDOR_ID_INTEL, CNL_H_DEVICE_ID)},
41 {0, }
42};
43MODULE_DEVICE_TABLE(pci, ish_pci_tbl);
44
45
46
47
48
49
50
51
52static __printf(2, 3)
53void ish_event_tracer(struct ishtp_device *dev, const char *format, ...)
54{
55 if (trace_ishtp_dump_enabled()) {
56 va_list args;
57 char tmp_buf[100];
58
59 va_start(args, format);
60 vsnprintf(tmp_buf, sizeof(tmp_buf), format, args);
61 va_end(args);
62
63 trace_ishtp_dump(tmp_buf);
64 }
65}
66
67
68
69
70
71
72
73
74
75
76
77static int ish_init(struct ishtp_device *dev)
78{
79 int ret;
80
81
82 ret = ish_hw_start(dev);
83 if (ret) {
84 dev_err(dev->devc, "ISH: hw start failed.\n");
85 return ret;
86 }
87
88
89 ret = ishtp_start(dev);
90 if (ret) {
91 dev_err(dev->devc, "ISHTP: Protocol init failed.\n");
92 return ret;
93 }
94
95 return 0;
96}
97
98
99
100
101
102
103
104
105
106
107static int ish_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
108{
109 struct ishtp_device *dev;
110 struct ish_hw *hw;
111 int ret;
112
113
114 ret = pci_enable_device(pdev);
115 if (ret) {
116 dev_err(&pdev->dev, "ISH: Failed to enable PCI device\n");
117 return ret;
118 }
119
120
121 pci_set_master(pdev);
122
123
124 ret = pci_request_regions(pdev, KBUILD_MODNAME);
125 if (ret) {
126 dev_err(&pdev->dev, "ISH: Failed to get PCI regions\n");
127 goto disable_device;
128 }
129
130
131 dev = ish_dev_init(pdev);
132 if (!dev) {
133 ret = -ENOMEM;
134 goto release_regions;
135 }
136 hw = to_ish_hw(dev);
137 dev->print_log = ish_event_tracer;
138
139
140 hw->mem_addr = pci_iomap(pdev, 0, 0);
141 if (!hw->mem_addr) {
142 dev_err(&pdev->dev, "ISH: mapping I/O range failure\n");
143 ret = -ENOMEM;
144 goto free_device;
145 }
146
147 dev->pdev = pdev;
148
149 pdev->dev_flags |= PCI_DEV_FLAGS_NO_D3;
150
151
152 ret = request_irq(pdev->irq, ish_irq_handler, IRQF_SHARED,
153 KBUILD_MODNAME, dev);
154 if (ret) {
155 dev_err(&pdev->dev, "ISH: request IRQ failure (%d)\n",
156 pdev->irq);
157 goto free_device;
158 }
159
160 dev_set_drvdata(dev->devc, dev);
161
162 init_waitqueue_head(&dev->suspend_wait);
163 init_waitqueue_head(&dev->resume_wait);
164
165 ret = ish_init(dev);
166 if (ret)
167 goto free_irq;
168
169 return 0;
170
171free_irq:
172 free_irq(pdev->irq, dev);
173free_device:
174 pci_iounmap(pdev, hw->mem_addr);
175 kfree(dev);
176release_regions:
177 pci_release_regions(pdev);
178disable_device:
179 pci_clear_master(pdev);
180 pci_disable_device(pdev);
181 dev_err(&pdev->dev, "ISH: PCI driver initialization failed.\n");
182
183 return ret;
184}
185
186
187
188
189
190
191
192static void ish_remove(struct pci_dev *pdev)
193{
194 struct ishtp_device *ishtp_dev = pci_get_drvdata(pdev);
195 struct ish_hw *hw = to_ish_hw(ishtp_dev);
196
197 ishtp_bus_remove_all_clients(ishtp_dev, false);
198 ish_device_disable(ishtp_dev);
199
200 free_irq(pdev->irq, ishtp_dev);
201 pci_iounmap(pdev, hw->mem_addr);
202 pci_release_regions(pdev);
203 pci_clear_master(pdev);
204 pci_disable_device(pdev);
205 kfree(ishtp_dev);
206}
207
208#ifdef CONFIG_PM
209static struct device *ish_resume_device;
210
211
212#define WAIT_FOR_RESUME_ACK_MS 50
213
214
215
216
217
218
219
220
221
222
223static void ish_resume_handler(struct work_struct *work)
224{
225 struct pci_dev *pdev = to_pci_dev(ish_resume_device);
226 struct ishtp_device *dev = pci_get_drvdata(pdev);
227 uint32_t fwsts;
228 int ret;
229
230
231 fwsts = IPC_GET_ISH_FWSTS(dev->ops->get_fw_status(dev));
232
233
234
235
236
237 if (fwsts >= FWSTS_SENSOR_APP_LOADED) {
238 ishtp_send_resume(dev);
239
240
241 if (dev->resume_flag)
242 ret = wait_event_interruptible_timeout(dev->resume_wait,
243 !dev->resume_flag,
244 msecs_to_jiffies(WAIT_FOR_RESUME_ACK_MS));
245 }
246
247
248
249
250
251
252
253 if (dev->resume_flag)
254 ish_init(dev);
255}
256
257
258
259
260
261
262
263
264
265static int ish_suspend(struct device *device)
266{
267 struct pci_dev *pdev = to_pci_dev(device);
268 struct ishtp_device *dev = pci_get_drvdata(pdev);
269
270 enable_irq_wake(pdev->irq);
271
272
273
274
275 if (dev->suspend_flag)
276 return 0;
277
278 dev->resume_flag = 0;
279 dev->suspend_flag = 1;
280 ishtp_send_suspend(dev);
281
282
283 if (dev->suspend_flag)
284 wait_event_interruptible_timeout(dev->suspend_wait,
285 !dev->suspend_flag,
286 msecs_to_jiffies(25));
287
288 return 0;
289}
290
291static DECLARE_WORK(resume_work, ish_resume_handler);
292
293
294
295
296
297
298
299
300static int ish_resume(struct device *device)
301{
302 struct pci_dev *pdev = to_pci_dev(device);
303 struct ishtp_device *dev = pci_get_drvdata(pdev);
304
305 ish_resume_device = device;
306 dev->resume_flag = 1;
307
308 disable_irq_wake(pdev->irq);
309 schedule_work(&resume_work);
310
311 return 0;
312}
313
314static const struct dev_pm_ops ish_pm_ops = {
315 .suspend = ish_suspend,
316 .resume = ish_resume,
317};
318#define ISHTP_ISH_PM_OPS (&ish_pm_ops)
319#else
320#define ISHTP_ISH_PM_OPS NULL
321#endif
322
323static struct pci_driver ish_driver = {
324 .name = KBUILD_MODNAME,
325 .id_table = ish_pci_tbl,
326 .probe = ish_probe,
327 .remove = ish_remove,
328 .driver.pm = ISHTP_ISH_PM_OPS,
329};
330
331module_pci_driver(ish_driver);
332
333
334MODULE_AUTHOR("Daniel Drubin <daniel.drubin@intel.com>");
335
336MODULE_AUTHOR("Srinivas Pandruvada <srinivas.pandruvada@linux.intel.com>");
337
338MODULE_DESCRIPTION("Intel(R) Integrated Sensor Hub PCI Device Driver");
339MODULE_LICENSE("GPL");
340