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