]> Git Repo - linux.git/blob - drivers/hid/intel-ish-hid/ipc/pci-ish.c
Linux 6.14-rc3
[linux.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/acpi.h>
9 #include <linux/module.h>
10 #include <linux/moduleparam.h>
11 #include <linux/kernel.h>
12 #include <linux/device.h>
13 #include <linux/fs.h>
14 #include <linux/errno.h>
15 #include <linux/types.h>
16 #include <linux/pci.h>
17 #include <linux/sched.h>
18 #include <linux/suspend.h>
19 #include <linux/interrupt.h>
20 #include <linux/workqueue.h>
21 #define CREATE_TRACE_POINTS
22 #include <trace/events/intel_ish.h>
23 #include "ishtp-dev.h"
24 #include "hw-ish.h"
25
26 enum ishtp_driver_data_index {
27         ISHTP_DRIVER_DATA_NONE,
28         ISHTP_DRIVER_DATA_LNL_M,
29         ISHTP_DRIVER_DATA_PTL,
30 };
31
32 #define ISH_FW_GEN_LNL_M "lnlm"
33 #define ISH_FW_GEN_PTL "ptl"
34
35 #define ISH_FIRMWARE_PATH(gen) "intel/ish/ish_" gen ".bin"
36 #define ISH_FIRMWARE_PATH_ALL "intel/ish/ish_*.bin"
37
38 static struct ishtp_driver_data ishtp_driver_data[] = {
39         [ISHTP_DRIVER_DATA_LNL_M] = {
40                 .fw_generation = ISH_FW_GEN_LNL_M,
41         },
42         [ISHTP_DRIVER_DATA_PTL] = {
43                 .fw_generation = ISH_FW_GEN_PTL,
44         },
45 };
46
47 static const struct pci_device_id ish_pci_tbl[] = {
48         {PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_ISH_CHV)},
49         {PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_ISH_BXT_Ax)},
50         {PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_ISH_BXT_Bx)},
51         {PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_ISH_APL_Ax)},
52         {PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_ISH_SPT_Ax)},
53         {PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_ISH_CNL_Ax)},
54         {PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_ISH_GLK_Ax)},
55         {PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_ISH_CNL_H)},
56         {PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_ISH_ICL_MOBILE)},
57         {PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_ISH_SPT_H)},
58         {PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_ISH_CML_LP)},
59         {PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_ISH_CMP_H)},
60         {PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_ISH_EHL_Ax)},
61         {PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_ISH_TGL_LP)},
62         {PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_ISH_TGL_H)},
63         {PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_ISH_ADL_S)},
64         {PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_ISH_ADL_P)},
65         {PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_ISH_ADL_N)},
66         {PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_ISH_RPL_S)},
67         {PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_ISH_MTL_P)},
68         {PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_ISH_ARL_H)},
69         {PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_ISH_ARL_S)},
70         {PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_ISH_LNL_M), .driver_data = ISHTP_DRIVER_DATA_LNL_M},
71         {PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_ISH_PTL_H), .driver_data = ISHTP_DRIVER_DATA_PTL},
72         {PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_ISH_PTL_P), .driver_data = ISHTP_DRIVER_DATA_PTL},
73         {}
74 };
75 MODULE_DEVICE_TABLE(pci, ish_pci_tbl);
76
77 /**
78  * ish_event_tracer() - Callback function to dump trace messages
79  * @dev:        ishtp device
80  * @format:     printf style format
81  *
82  * Callback to direct log messages to Linux trace buffers
83  */
84 static __printf(2, 3)
85 void ish_event_tracer(struct ishtp_device *dev, const char *format, ...)
86 {
87         if (trace_ishtp_dump_enabled()) {
88                 va_list args;
89                 char tmp_buf[100];
90
91                 va_start(args, format);
92                 vsnprintf(tmp_buf, sizeof(tmp_buf), format, args);
93                 va_end(args);
94
95                 trace_ishtp_dump(tmp_buf);
96         }
97 }
98
99 /**
100  * ish_init() - Init function
101  * @dev:        ishtp device
102  *
103  * This function initialize wait queues for suspend/resume and call
104  * calls hadware initialization function. This will initiate
105  * startup sequence
106  *
107  * Return: 0 for success or error code for failure
108  */
109 static int ish_init(struct ishtp_device *dev)
110 {
111         int ret;
112
113         /* Set the state of ISH HW to start */
114         ret = ish_hw_start(dev);
115         if (ret) {
116                 dev_err(dev->devc, "ISH: hw start failed.\n");
117                 return ret;
118         }
119
120         /* Start the inter process communication to ISH processor */
121         ret = ishtp_start(dev);
122         if (ret) {
123                 dev_err(dev->devc, "ISHTP: Protocol init failed.\n");
124                 return ret;
125         }
126
127         return 0;
128 }
129
130 static const struct pci_device_id ish_invalid_pci_ids[] = {
131         /* Mehlow platform special pci ids */
132         {PCI_VDEVICE(INTEL, 0xA309)},
133         {PCI_VDEVICE(INTEL, 0xA30A)},
134         {}
135 };
136
137 static inline bool ish_should_enter_d0i3(struct pci_dev *pdev)
138 {
139         return !pm_suspend_via_firmware() || pdev->device == PCI_DEVICE_ID_INTEL_ISH_CHV;
140 }
141
142 static inline bool ish_should_leave_d0i3(struct pci_dev *pdev)
143 {
144         return !pm_resume_via_firmware() || pdev->device == PCI_DEVICE_ID_INTEL_ISH_CHV;
145 }
146
147 /**
148  * ish_probe() - PCI driver probe callback
149  * @pdev:       pci device
150  * @ent:        pci device id
151  *
152  * Initialize PCI function, setup interrupt and call for ISH initialization
153  *
154  * Return: 0 for success or error code for failure
155  */
156 static int ish_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
157 {
158         int ret;
159         struct ish_hw *hw;
160         unsigned long irq_flag = 0;
161         struct ishtp_device *ishtp;
162         struct device *dev = &pdev->dev;
163
164         /* Check for invalid platforms for ISH support */
165         if (pci_dev_present(ish_invalid_pci_ids))
166                 return -ENODEV;
167
168         /* enable pci dev */
169         ret = pcim_enable_device(pdev);
170         if (ret) {
171                 dev_err(dev, "ISH: Failed to enable PCI device\n");
172                 return ret;
173         }
174
175         /* set PCI host mastering */
176         pci_set_master(pdev);
177
178         /* pci request regions for ISH driver */
179         ret = pcim_iomap_regions(pdev, 1 << 0, KBUILD_MODNAME);
180         if (ret) {
181                 dev_err(dev, "ISH: Failed to get PCI regions\n");
182                 return ret;
183         }
184
185         /* allocates and initializes the ISH dev structure */
186         ishtp = ish_dev_init(pdev);
187         if (!ishtp) {
188                 ret = -ENOMEM;
189                 return ret;
190         }
191         hw = to_ish_hw(ishtp);
192         ishtp->print_log = ish_event_tracer;
193         ishtp->driver_data = &ishtp_driver_data[ent->driver_data];
194
195         /* mapping IO device memory */
196         hw->mem_addr = pcim_iomap_table(pdev)[0];
197         ishtp->pdev = pdev;
198
199         /* request and enable interrupt */
200         ret = pci_alloc_irq_vectors(pdev, 1, 1, PCI_IRQ_ALL_TYPES);
201         if (ret < 0) {
202                 dev_err(dev, "ISH: Failed to allocate IRQ vectors\n");
203                 return ret;
204         }
205
206         if (!pdev->msi_enabled && !pdev->msix_enabled)
207                 irq_flag = IRQF_SHARED;
208
209         ret = devm_request_irq(dev, pdev->irq, ish_irq_handler,
210                                irq_flag, KBUILD_MODNAME, ishtp);
211         if (ret) {
212                 dev_err(dev, "ISH: request IRQ %d failed\n", pdev->irq);
213                 return ret;
214         }
215
216         dev_set_drvdata(ishtp->devc, ishtp);
217
218         init_waitqueue_head(&ishtp->suspend_wait);
219         init_waitqueue_head(&ishtp->resume_wait);
220
221         /* Enable PME for EHL */
222         if (pdev->device == PCI_DEVICE_ID_INTEL_ISH_EHL_Ax)
223                 device_init_wakeup(dev, true);
224
225         ret = ish_init(ishtp);
226         if (ret)
227                 return ret;
228
229         return 0;
230 }
231
232 /**
233  * ish_remove() - PCI driver remove callback
234  * @pdev:       pci device
235  *
236  * This function does cleanup of ISH on pci remove callback
237  */
238 static void ish_remove(struct pci_dev *pdev)
239 {
240         struct ishtp_device *ishtp_dev = pci_get_drvdata(pdev);
241
242         ishtp_bus_remove_all_clients(ishtp_dev, false);
243         ish_device_disable(ishtp_dev);
244 }
245
246
247 /**
248  * ish_shutdown() - PCI driver shutdown callback
249  * @pdev:       pci device
250  *
251  * This function sets up wakeup for S5
252  */
253 static void ish_shutdown(struct pci_dev *pdev)
254 {
255         if (pdev->device == PCI_DEVICE_ID_INTEL_ISH_EHL_Ax)
256                 pci_prepare_to_sleep(pdev);
257 }
258
259 static struct device __maybe_unused *ish_resume_device;
260
261 /* 50ms to get resume response */
262 #define WAIT_FOR_RESUME_ACK_MS          50
263
264 /**
265  * ish_resume_handler() - Work function to complete resume
266  * @work:       work struct
267  *
268  * The resume work function to complete resume function asynchronously.
269  * There are two resume paths, one where ISH is not powered off,
270  * in that case a simple resume message is enough, others we need
271  * a reset sequence.
272  */
273 static void __maybe_unused ish_resume_handler(struct work_struct *work)
274 {
275         struct pci_dev *pdev = to_pci_dev(ish_resume_device);
276         struct ishtp_device *dev = pci_get_drvdata(pdev);
277         uint32_t fwsts = dev->ops->get_fw_status(dev);
278
279         if (ish_should_leave_d0i3(pdev) && !dev->suspend_flag
280                         && IPC_IS_ISH_ILUP(fwsts)) {
281                 if (device_may_wakeup(&pdev->dev))
282                         disable_irq_wake(pdev->irq);
283
284                 ish_set_host_ready(dev);
285
286                 ishtp_send_resume(dev);
287
288                 /* Waiting to get resume response */
289                 if (dev->resume_flag)
290                         wait_event_interruptible_timeout(dev->resume_wait,
291                                 !dev->resume_flag,
292                                 msecs_to_jiffies(WAIT_FOR_RESUME_ACK_MS));
293
294                 /*
295                  * If the flag is not cleared, something is wrong with ISH FW.
296                  * So on resume, need to go through init sequence again.
297                  */
298                 if (dev->resume_flag)
299                         ish_init(dev);
300         } else {
301                 /*
302                  * Resume from the D3, full reboot of ISH processor will happen,
303                  * so need to go through init sequence again.
304                  */
305                 ish_init(dev);
306         }
307 }
308
309 /**
310  * ish_suspend() - ISH suspend callback
311  * @device:     device pointer
312  *
313  * ISH suspend callback
314  *
315  * Return: 0 to the pm core
316  */
317 static int __maybe_unused ish_suspend(struct device *device)
318 {
319         struct pci_dev *pdev = to_pci_dev(device);
320         struct ishtp_device *dev = pci_get_drvdata(pdev);
321
322         if (ish_should_enter_d0i3(pdev)) {
323                 /*
324                  * If previous suspend hasn't been asnwered then ISH is likely
325                  * dead, don't attempt nested notification
326                  */
327                 if (dev->suspend_flag)
328                         return  0;
329
330                 dev->resume_flag = 0;
331                 dev->suspend_flag = 1;
332                 ishtp_send_suspend(dev);
333
334                 /* 25 ms should be enough for live ISH to flush all IPC buf */
335                 if (dev->suspend_flag)
336                         wait_event_interruptible_timeout(dev->suspend_wait,
337                                         !dev->suspend_flag,
338                                         msecs_to_jiffies(25));
339
340                 if (dev->suspend_flag) {
341                         /*
342                          * It looks like FW halt, clear the DMA bit, and put
343                          * ISH into D3, and FW would reset on resume.
344                          */
345                         ish_disable_dma(dev);
346                 } else {
347                         /*
348                          * Save state so PCI core will keep the device at D0,
349                          * the ISH would enter D0i3
350                          */
351                         pci_save_state(pdev);
352
353                         if (device_may_wakeup(&pdev->dev))
354                                 enable_irq_wake(pdev->irq);
355                 }
356         } else {
357                 /*
358                  * Clear the DMA bit before putting ISH into D3,
359                  * or ISH FW would reset automatically.
360                  */
361                 ish_disable_dma(dev);
362         }
363
364         return 0;
365 }
366
367 static __maybe_unused DECLARE_WORK(resume_work, ish_resume_handler);
368 /**
369  * ish_resume() - ISH resume callback
370  * @device:     device pointer
371  *
372  * ISH resume callback
373  *
374  * Return: 0 to the pm core
375  */
376 static int __maybe_unused ish_resume(struct device *device)
377 {
378         struct pci_dev *pdev = to_pci_dev(device);
379         struct ishtp_device *dev = pci_get_drvdata(pdev);
380
381         ish_resume_device = device;
382         dev->resume_flag = 1;
383
384         schedule_work(&resume_work);
385
386         return 0;
387 }
388
389 static SIMPLE_DEV_PM_OPS(ish_pm_ops, ish_suspend, ish_resume);
390
391 static ssize_t base_version_show(struct device *cdev,
392                                  struct device_attribute *attr, char *buf)
393 {
394         struct ishtp_device *dev = dev_get_drvdata(cdev);
395
396         return sysfs_emit(buf, "%u.%u.%u.%u\n", dev->base_ver.major,
397                           dev->base_ver.minor, dev->base_ver.hotfix,
398                           dev->base_ver.build);
399 }
400 static DEVICE_ATTR_RO(base_version);
401
402 static ssize_t project_version_show(struct device *cdev,
403                                     struct device_attribute *attr, char *buf)
404 {
405         struct ishtp_device *dev = dev_get_drvdata(cdev);
406
407         return sysfs_emit(buf, "%u.%u.%u.%u\n", dev->prj_ver.major,
408                           dev->prj_ver.minor, dev->prj_ver.hotfix,
409                           dev->prj_ver.build);
410 }
411 static DEVICE_ATTR_RO(project_version);
412
413 static struct attribute *ish_firmware_attrs[] = {
414         &dev_attr_base_version.attr,
415         &dev_attr_project_version.attr,
416         NULL
417 };
418
419 static umode_t firmware_is_visible(struct kobject *kobj, struct attribute *attr,
420                                    int i)
421 {
422         struct ishtp_device *dev = dev_get_drvdata(kobj_to_dev(kobj));
423
424         return dev->driver_data->fw_generation ? attr->mode : 0;
425 }
426
427 static const struct attribute_group ish_firmware_group = {
428         .name = "firmware",
429         .attrs = ish_firmware_attrs,
430         .is_visible = firmware_is_visible,
431 };
432
433 __ATTRIBUTE_GROUPS(ish_firmware);
434
435 static struct pci_driver ish_driver = {
436         .name = KBUILD_MODNAME,
437         .id_table = ish_pci_tbl,
438         .probe = ish_probe,
439         .remove = ish_remove,
440         .shutdown = ish_shutdown,
441         .driver.pm = &ish_pm_ops,
442         .dev_groups = ish_firmware_groups,
443 };
444
445 module_pci_driver(ish_driver);
446
447 /* Original author */
448 MODULE_AUTHOR("Daniel Drubin <[email protected]>");
449 /* Adoption to upstream Linux kernel */
450 MODULE_AUTHOR("Srinivas Pandruvada <[email protected]>");
451
452 MODULE_DESCRIPTION("Intel(R) Integrated Sensor Hub PCI Device Driver");
453 MODULE_LICENSE("GPL");
454
455 MODULE_FIRMWARE(ISH_FIRMWARE_PATH(ISH_FW_GEN_LNL_M));
456 MODULE_FIRMWARE(ISH_FIRMWARE_PATH_ALL);
This page took 0.057198 seconds and 4 git commands to generate.