1*4882a593Smuzhiyun // SPDX-License-Identifier: GPL-2.0-only
2*4882a593Smuzhiyun /*
3*4882a593Smuzhiyun * PCI glue for ISHTP provider device (ISH) driver
4*4882a593Smuzhiyun *
5*4882a593Smuzhiyun * Copyright (c) 2014-2016, Intel Corporation.
6*4882a593Smuzhiyun */
7*4882a593Smuzhiyun
8*4882a593Smuzhiyun #include <linux/module.h>
9*4882a593Smuzhiyun #include <linux/moduleparam.h>
10*4882a593Smuzhiyun #include <linux/kernel.h>
11*4882a593Smuzhiyun #include <linux/device.h>
12*4882a593Smuzhiyun #include <linux/fs.h>
13*4882a593Smuzhiyun #include <linux/errno.h>
14*4882a593Smuzhiyun #include <linux/types.h>
15*4882a593Smuzhiyun #include <linux/pci.h>
16*4882a593Smuzhiyun #include <linux/sched.h>
17*4882a593Smuzhiyun #include <linux/suspend.h>
18*4882a593Smuzhiyun #include <linux/interrupt.h>
19*4882a593Smuzhiyun #include <linux/workqueue.h>
20*4882a593Smuzhiyun #define CREATE_TRACE_POINTS
21*4882a593Smuzhiyun #include <trace/events/intel_ish.h>
22*4882a593Smuzhiyun #include "ishtp-dev.h"
23*4882a593Smuzhiyun #include "hw-ish.h"
24*4882a593Smuzhiyun
25*4882a593Smuzhiyun static const struct pci_device_id ish_pci_tbl[] = {
26*4882a593Smuzhiyun {PCI_DEVICE(PCI_VENDOR_ID_INTEL, CHV_DEVICE_ID)},
27*4882a593Smuzhiyun {PCI_DEVICE(PCI_VENDOR_ID_INTEL, BXT_Ax_DEVICE_ID)},
28*4882a593Smuzhiyun {PCI_DEVICE(PCI_VENDOR_ID_INTEL, BXT_Bx_DEVICE_ID)},
29*4882a593Smuzhiyun {PCI_DEVICE(PCI_VENDOR_ID_INTEL, APL_Ax_DEVICE_ID)},
30*4882a593Smuzhiyun {PCI_DEVICE(PCI_VENDOR_ID_INTEL, SPT_Ax_DEVICE_ID)},
31*4882a593Smuzhiyun {PCI_DEVICE(PCI_VENDOR_ID_INTEL, CNL_Ax_DEVICE_ID)},
32*4882a593Smuzhiyun {PCI_DEVICE(PCI_VENDOR_ID_INTEL, GLK_Ax_DEVICE_ID)},
33*4882a593Smuzhiyun {PCI_DEVICE(PCI_VENDOR_ID_INTEL, CNL_H_DEVICE_ID)},
34*4882a593Smuzhiyun {PCI_DEVICE(PCI_VENDOR_ID_INTEL, ICL_MOBILE_DEVICE_ID)},
35*4882a593Smuzhiyun {PCI_DEVICE(PCI_VENDOR_ID_INTEL, SPT_H_DEVICE_ID)},
36*4882a593Smuzhiyun {PCI_DEVICE(PCI_VENDOR_ID_INTEL, CML_LP_DEVICE_ID)},
37*4882a593Smuzhiyun {PCI_DEVICE(PCI_VENDOR_ID_INTEL, CMP_H_DEVICE_ID)},
38*4882a593Smuzhiyun {PCI_DEVICE(PCI_VENDOR_ID_INTEL, EHL_Ax_DEVICE_ID)},
39*4882a593Smuzhiyun {PCI_DEVICE(PCI_VENDOR_ID_INTEL, TGL_LP_DEVICE_ID)},
40*4882a593Smuzhiyun {0, }
41*4882a593Smuzhiyun };
42*4882a593Smuzhiyun MODULE_DEVICE_TABLE(pci, ish_pci_tbl);
43*4882a593Smuzhiyun
44*4882a593Smuzhiyun /**
45*4882a593Smuzhiyun * ish_event_tracer() - Callback function to dump trace messages
46*4882a593Smuzhiyun * @dev: ishtp device
47*4882a593Smuzhiyun * @format: printf style format
48*4882a593Smuzhiyun *
49*4882a593Smuzhiyun * Callback to direct log messages to Linux trace buffers
50*4882a593Smuzhiyun */
51*4882a593Smuzhiyun static __printf(2, 3)
ish_event_tracer(struct ishtp_device * dev,const char * format,...)52*4882a593Smuzhiyun void ish_event_tracer(struct ishtp_device *dev, const char *format, ...)
53*4882a593Smuzhiyun {
54*4882a593Smuzhiyun if (trace_ishtp_dump_enabled()) {
55*4882a593Smuzhiyun va_list args;
56*4882a593Smuzhiyun char tmp_buf[100];
57*4882a593Smuzhiyun
58*4882a593Smuzhiyun va_start(args, format);
59*4882a593Smuzhiyun vsnprintf(tmp_buf, sizeof(tmp_buf), format, args);
60*4882a593Smuzhiyun va_end(args);
61*4882a593Smuzhiyun
62*4882a593Smuzhiyun trace_ishtp_dump(tmp_buf);
63*4882a593Smuzhiyun }
64*4882a593Smuzhiyun }
65*4882a593Smuzhiyun
66*4882a593Smuzhiyun /**
67*4882a593Smuzhiyun * ish_init() - Init function
68*4882a593Smuzhiyun * @dev: ishtp device
69*4882a593Smuzhiyun *
70*4882a593Smuzhiyun * This function initialize wait queues for suspend/resume and call
71*4882a593Smuzhiyun * calls hadware initialization function. This will initiate
72*4882a593Smuzhiyun * startup sequence
73*4882a593Smuzhiyun *
74*4882a593Smuzhiyun * Return: 0 for success or error code for failure
75*4882a593Smuzhiyun */
ish_init(struct ishtp_device * dev)76*4882a593Smuzhiyun static int ish_init(struct ishtp_device *dev)
77*4882a593Smuzhiyun {
78*4882a593Smuzhiyun int ret;
79*4882a593Smuzhiyun
80*4882a593Smuzhiyun /* Set the state of ISH HW to start */
81*4882a593Smuzhiyun ret = ish_hw_start(dev);
82*4882a593Smuzhiyun if (ret) {
83*4882a593Smuzhiyun dev_err(dev->devc, "ISH: hw start failed.\n");
84*4882a593Smuzhiyun return ret;
85*4882a593Smuzhiyun }
86*4882a593Smuzhiyun
87*4882a593Smuzhiyun /* Start the inter process communication to ISH processor */
88*4882a593Smuzhiyun ret = ishtp_start(dev);
89*4882a593Smuzhiyun if (ret) {
90*4882a593Smuzhiyun dev_err(dev->devc, "ISHTP: Protocol init failed.\n");
91*4882a593Smuzhiyun return ret;
92*4882a593Smuzhiyun }
93*4882a593Smuzhiyun
94*4882a593Smuzhiyun return 0;
95*4882a593Smuzhiyun }
96*4882a593Smuzhiyun
97*4882a593Smuzhiyun static const struct pci_device_id ish_invalid_pci_ids[] = {
98*4882a593Smuzhiyun /* Mehlow platform special pci ids */
99*4882a593Smuzhiyun {PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0xA309)},
100*4882a593Smuzhiyun {PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0xA30A)},
101*4882a593Smuzhiyun {}
102*4882a593Smuzhiyun };
103*4882a593Smuzhiyun
ish_should_enter_d0i3(struct pci_dev * pdev)104*4882a593Smuzhiyun static inline bool ish_should_enter_d0i3(struct pci_dev *pdev)
105*4882a593Smuzhiyun {
106*4882a593Smuzhiyun return !pm_suspend_via_firmware() || pdev->device == CHV_DEVICE_ID;
107*4882a593Smuzhiyun }
108*4882a593Smuzhiyun
ish_should_leave_d0i3(struct pci_dev * pdev)109*4882a593Smuzhiyun static inline bool ish_should_leave_d0i3(struct pci_dev *pdev)
110*4882a593Smuzhiyun {
111*4882a593Smuzhiyun return !pm_resume_via_firmware() || pdev->device == CHV_DEVICE_ID;
112*4882a593Smuzhiyun }
113*4882a593Smuzhiyun
114*4882a593Smuzhiyun /**
115*4882a593Smuzhiyun * ish_probe() - PCI driver probe callback
116*4882a593Smuzhiyun * @pdev: pci device
117*4882a593Smuzhiyun * @ent: pci device id
118*4882a593Smuzhiyun *
119*4882a593Smuzhiyun * Initialize PCI function, setup interrupt and call for ISH initialization
120*4882a593Smuzhiyun *
121*4882a593Smuzhiyun * Return: 0 for success or error code for failure
122*4882a593Smuzhiyun */
ish_probe(struct pci_dev * pdev,const struct pci_device_id * ent)123*4882a593Smuzhiyun static int ish_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
124*4882a593Smuzhiyun {
125*4882a593Smuzhiyun int ret;
126*4882a593Smuzhiyun struct ish_hw *hw;
127*4882a593Smuzhiyun unsigned long irq_flag = 0;
128*4882a593Smuzhiyun struct ishtp_device *ishtp;
129*4882a593Smuzhiyun struct device *dev = &pdev->dev;
130*4882a593Smuzhiyun
131*4882a593Smuzhiyun /* Check for invalid platforms for ISH support */
132*4882a593Smuzhiyun if (pci_dev_present(ish_invalid_pci_ids))
133*4882a593Smuzhiyun return -ENODEV;
134*4882a593Smuzhiyun
135*4882a593Smuzhiyun /* enable pci dev */
136*4882a593Smuzhiyun ret = pcim_enable_device(pdev);
137*4882a593Smuzhiyun if (ret) {
138*4882a593Smuzhiyun dev_err(dev, "ISH: Failed to enable PCI device\n");
139*4882a593Smuzhiyun return ret;
140*4882a593Smuzhiyun }
141*4882a593Smuzhiyun
142*4882a593Smuzhiyun /* set PCI host mastering */
143*4882a593Smuzhiyun pci_set_master(pdev);
144*4882a593Smuzhiyun
145*4882a593Smuzhiyun /* pci request regions for ISH driver */
146*4882a593Smuzhiyun ret = pcim_iomap_regions(pdev, 1 << 0, KBUILD_MODNAME);
147*4882a593Smuzhiyun if (ret) {
148*4882a593Smuzhiyun dev_err(dev, "ISH: Failed to get PCI regions\n");
149*4882a593Smuzhiyun return ret;
150*4882a593Smuzhiyun }
151*4882a593Smuzhiyun
152*4882a593Smuzhiyun /* allocates and initializes the ISH dev structure */
153*4882a593Smuzhiyun ishtp = ish_dev_init(pdev);
154*4882a593Smuzhiyun if (!ishtp) {
155*4882a593Smuzhiyun ret = -ENOMEM;
156*4882a593Smuzhiyun return ret;
157*4882a593Smuzhiyun }
158*4882a593Smuzhiyun hw = to_ish_hw(ishtp);
159*4882a593Smuzhiyun ishtp->print_log = ish_event_tracer;
160*4882a593Smuzhiyun
161*4882a593Smuzhiyun /* mapping IO device memory */
162*4882a593Smuzhiyun hw->mem_addr = pcim_iomap_table(pdev)[0];
163*4882a593Smuzhiyun ishtp->pdev = pdev;
164*4882a593Smuzhiyun
165*4882a593Smuzhiyun /* request and enable interrupt */
166*4882a593Smuzhiyun ret = pci_alloc_irq_vectors(pdev, 1, 1, PCI_IRQ_ALL_TYPES);
167*4882a593Smuzhiyun if (!pdev->msi_enabled && !pdev->msix_enabled)
168*4882a593Smuzhiyun irq_flag = IRQF_SHARED;
169*4882a593Smuzhiyun
170*4882a593Smuzhiyun ret = devm_request_irq(dev, pdev->irq, ish_irq_handler,
171*4882a593Smuzhiyun irq_flag, KBUILD_MODNAME, ishtp);
172*4882a593Smuzhiyun if (ret) {
173*4882a593Smuzhiyun dev_err(dev, "ISH: request IRQ %d failed\n", pdev->irq);
174*4882a593Smuzhiyun return ret;
175*4882a593Smuzhiyun }
176*4882a593Smuzhiyun
177*4882a593Smuzhiyun dev_set_drvdata(ishtp->devc, ishtp);
178*4882a593Smuzhiyun
179*4882a593Smuzhiyun init_waitqueue_head(&ishtp->suspend_wait);
180*4882a593Smuzhiyun init_waitqueue_head(&ishtp->resume_wait);
181*4882a593Smuzhiyun
182*4882a593Smuzhiyun ret = ish_init(ishtp);
183*4882a593Smuzhiyun if (ret)
184*4882a593Smuzhiyun return ret;
185*4882a593Smuzhiyun
186*4882a593Smuzhiyun return 0;
187*4882a593Smuzhiyun }
188*4882a593Smuzhiyun
189*4882a593Smuzhiyun /**
190*4882a593Smuzhiyun * ish_remove() - PCI driver remove callback
191*4882a593Smuzhiyun * @pdev: pci device
192*4882a593Smuzhiyun *
193*4882a593Smuzhiyun * This function does cleanup of ISH on pci remove callback
194*4882a593Smuzhiyun */
ish_remove(struct pci_dev * pdev)195*4882a593Smuzhiyun static void ish_remove(struct pci_dev *pdev)
196*4882a593Smuzhiyun {
197*4882a593Smuzhiyun struct ishtp_device *ishtp_dev = pci_get_drvdata(pdev);
198*4882a593Smuzhiyun
199*4882a593Smuzhiyun ishtp_bus_remove_all_clients(ishtp_dev, false);
200*4882a593Smuzhiyun ish_device_disable(ishtp_dev);
201*4882a593Smuzhiyun }
202*4882a593Smuzhiyun
203*4882a593Smuzhiyun static struct device __maybe_unused *ish_resume_device;
204*4882a593Smuzhiyun
205*4882a593Smuzhiyun /* 50ms to get resume response */
206*4882a593Smuzhiyun #define WAIT_FOR_RESUME_ACK_MS 50
207*4882a593Smuzhiyun
208*4882a593Smuzhiyun /**
209*4882a593Smuzhiyun * ish_resume_handler() - Work function to complete resume
210*4882a593Smuzhiyun * @work: work struct
211*4882a593Smuzhiyun *
212*4882a593Smuzhiyun * The resume work function to complete resume function asynchronously.
213*4882a593Smuzhiyun * There are two resume paths, one where ISH is not powered off,
214*4882a593Smuzhiyun * in that case a simple resume message is enough, others we need
215*4882a593Smuzhiyun * a reset sequence.
216*4882a593Smuzhiyun */
ish_resume_handler(struct work_struct * work)217*4882a593Smuzhiyun static void __maybe_unused ish_resume_handler(struct work_struct *work)
218*4882a593Smuzhiyun {
219*4882a593Smuzhiyun struct pci_dev *pdev = to_pci_dev(ish_resume_device);
220*4882a593Smuzhiyun struct ishtp_device *dev = pci_get_drvdata(pdev);
221*4882a593Smuzhiyun int ret;
222*4882a593Smuzhiyun
223*4882a593Smuzhiyun if (ish_should_leave_d0i3(pdev) && !dev->suspend_flag) {
224*4882a593Smuzhiyun disable_irq_wake(pdev->irq);
225*4882a593Smuzhiyun
226*4882a593Smuzhiyun ishtp_send_resume(dev);
227*4882a593Smuzhiyun
228*4882a593Smuzhiyun /* Waiting to get resume response */
229*4882a593Smuzhiyun if (dev->resume_flag)
230*4882a593Smuzhiyun ret = wait_event_interruptible_timeout(dev->resume_wait,
231*4882a593Smuzhiyun !dev->resume_flag,
232*4882a593Smuzhiyun msecs_to_jiffies(WAIT_FOR_RESUME_ACK_MS));
233*4882a593Smuzhiyun
234*4882a593Smuzhiyun /*
235*4882a593Smuzhiyun * If the flag is not cleared, something is wrong with ISH FW.
236*4882a593Smuzhiyun * So on resume, need to go through init sequence again.
237*4882a593Smuzhiyun */
238*4882a593Smuzhiyun if (dev->resume_flag)
239*4882a593Smuzhiyun ish_init(dev);
240*4882a593Smuzhiyun } else {
241*4882a593Smuzhiyun /*
242*4882a593Smuzhiyun * Resume from the D3, full reboot of ISH processor will happen,
243*4882a593Smuzhiyun * so need to go through init sequence again.
244*4882a593Smuzhiyun */
245*4882a593Smuzhiyun ish_init(dev);
246*4882a593Smuzhiyun }
247*4882a593Smuzhiyun }
248*4882a593Smuzhiyun
249*4882a593Smuzhiyun /**
250*4882a593Smuzhiyun * ish_suspend() - ISH suspend callback
251*4882a593Smuzhiyun * @device: device pointer
252*4882a593Smuzhiyun *
253*4882a593Smuzhiyun * ISH suspend callback
254*4882a593Smuzhiyun *
255*4882a593Smuzhiyun * Return: 0 to the pm core
256*4882a593Smuzhiyun */
ish_suspend(struct device * device)257*4882a593Smuzhiyun static int __maybe_unused ish_suspend(struct device *device)
258*4882a593Smuzhiyun {
259*4882a593Smuzhiyun struct pci_dev *pdev = to_pci_dev(device);
260*4882a593Smuzhiyun struct ishtp_device *dev = pci_get_drvdata(pdev);
261*4882a593Smuzhiyun
262*4882a593Smuzhiyun if (ish_should_enter_d0i3(pdev)) {
263*4882a593Smuzhiyun /*
264*4882a593Smuzhiyun * If previous suspend hasn't been asnwered then ISH is likely
265*4882a593Smuzhiyun * dead, don't attempt nested notification
266*4882a593Smuzhiyun */
267*4882a593Smuzhiyun if (dev->suspend_flag)
268*4882a593Smuzhiyun return 0;
269*4882a593Smuzhiyun
270*4882a593Smuzhiyun dev->resume_flag = 0;
271*4882a593Smuzhiyun dev->suspend_flag = 1;
272*4882a593Smuzhiyun ishtp_send_suspend(dev);
273*4882a593Smuzhiyun
274*4882a593Smuzhiyun /* 25 ms should be enough for live ISH to flush all IPC buf */
275*4882a593Smuzhiyun if (dev->suspend_flag)
276*4882a593Smuzhiyun wait_event_interruptible_timeout(dev->suspend_wait,
277*4882a593Smuzhiyun !dev->suspend_flag,
278*4882a593Smuzhiyun msecs_to_jiffies(25));
279*4882a593Smuzhiyun
280*4882a593Smuzhiyun if (dev->suspend_flag) {
281*4882a593Smuzhiyun /*
282*4882a593Smuzhiyun * It looks like FW halt, clear the DMA bit, and put
283*4882a593Smuzhiyun * ISH into D3, and FW would reset on resume.
284*4882a593Smuzhiyun */
285*4882a593Smuzhiyun ish_disable_dma(dev);
286*4882a593Smuzhiyun } else {
287*4882a593Smuzhiyun /*
288*4882a593Smuzhiyun * Save state so PCI core will keep the device at D0,
289*4882a593Smuzhiyun * the ISH would enter D0i3
290*4882a593Smuzhiyun */
291*4882a593Smuzhiyun pci_save_state(pdev);
292*4882a593Smuzhiyun
293*4882a593Smuzhiyun enable_irq_wake(pdev->irq);
294*4882a593Smuzhiyun }
295*4882a593Smuzhiyun } else {
296*4882a593Smuzhiyun /*
297*4882a593Smuzhiyun * Clear the DMA bit before putting ISH into D3,
298*4882a593Smuzhiyun * or ISH FW would reset automatically.
299*4882a593Smuzhiyun */
300*4882a593Smuzhiyun ish_disable_dma(dev);
301*4882a593Smuzhiyun }
302*4882a593Smuzhiyun
303*4882a593Smuzhiyun return 0;
304*4882a593Smuzhiyun }
305*4882a593Smuzhiyun
306*4882a593Smuzhiyun static __maybe_unused DECLARE_WORK(resume_work, ish_resume_handler);
307*4882a593Smuzhiyun /**
308*4882a593Smuzhiyun * ish_resume() - ISH resume callback
309*4882a593Smuzhiyun * @device: device pointer
310*4882a593Smuzhiyun *
311*4882a593Smuzhiyun * ISH resume callback
312*4882a593Smuzhiyun *
313*4882a593Smuzhiyun * Return: 0 to the pm core
314*4882a593Smuzhiyun */
ish_resume(struct device * device)315*4882a593Smuzhiyun static int __maybe_unused ish_resume(struct device *device)
316*4882a593Smuzhiyun {
317*4882a593Smuzhiyun struct pci_dev *pdev = to_pci_dev(device);
318*4882a593Smuzhiyun struct ishtp_device *dev = pci_get_drvdata(pdev);
319*4882a593Smuzhiyun
320*4882a593Smuzhiyun ish_resume_device = device;
321*4882a593Smuzhiyun dev->resume_flag = 1;
322*4882a593Smuzhiyun
323*4882a593Smuzhiyun schedule_work(&resume_work);
324*4882a593Smuzhiyun
325*4882a593Smuzhiyun return 0;
326*4882a593Smuzhiyun }
327*4882a593Smuzhiyun
328*4882a593Smuzhiyun static SIMPLE_DEV_PM_OPS(ish_pm_ops, ish_suspend, ish_resume);
329*4882a593Smuzhiyun
330*4882a593Smuzhiyun static struct pci_driver ish_driver = {
331*4882a593Smuzhiyun .name = KBUILD_MODNAME,
332*4882a593Smuzhiyun .id_table = ish_pci_tbl,
333*4882a593Smuzhiyun .probe = ish_probe,
334*4882a593Smuzhiyun .remove = ish_remove,
335*4882a593Smuzhiyun .driver.pm = &ish_pm_ops,
336*4882a593Smuzhiyun };
337*4882a593Smuzhiyun
338*4882a593Smuzhiyun module_pci_driver(ish_driver);
339*4882a593Smuzhiyun
340*4882a593Smuzhiyun /* Original author */
341*4882a593Smuzhiyun MODULE_AUTHOR("Daniel Drubin <daniel.drubin@intel.com>");
342*4882a593Smuzhiyun /* Adoption to upstream Linux kernel */
343*4882a593Smuzhiyun MODULE_AUTHOR("Srinivas Pandruvada <srinivas.pandruvada@linux.intel.com>");
344*4882a593Smuzhiyun
345*4882a593Smuzhiyun MODULE_DESCRIPTION("Intel(R) Integrated Sensor Hub PCI Device Driver");
346*4882a593Smuzhiyun MODULE_LICENSE("GPL");
347