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