xref: /linux/drivers/hid/intel-ish-hid/ipc/pci-ish.c (revision 66da65005aa819e0b8d3a08f5ec1491b7690cb67)
12025cf9eSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
2ae02e5d4SSrinivas Pandruvada /*
3ae02e5d4SSrinivas Pandruvada  * PCI glue for ISHTP provider device (ISH) driver
4ae02e5d4SSrinivas Pandruvada  *
5ae02e5d4SSrinivas Pandruvada  * Copyright (c) 2014-2016, Intel Corporation.
6ae02e5d4SSrinivas Pandruvada  */
7ae02e5d4SSrinivas Pandruvada 
82e23a70eSZhang Lixu #include <linux/acpi.h>
9ae02e5d4SSrinivas Pandruvada #include <linux/module.h>
10ae02e5d4SSrinivas Pandruvada #include <linux/moduleparam.h>
11ae02e5d4SSrinivas Pandruvada #include <linux/kernel.h>
12ae02e5d4SSrinivas Pandruvada #include <linux/device.h>
13ae02e5d4SSrinivas Pandruvada #include <linux/fs.h>
14ae02e5d4SSrinivas Pandruvada #include <linux/errno.h>
15ae02e5d4SSrinivas Pandruvada #include <linux/types.h>
16ae02e5d4SSrinivas Pandruvada #include <linux/pci.h>
17ae02e5d4SSrinivas Pandruvada #include <linux/sched.h>
18c1ca58f6SZhang Lixu #include <linux/suspend.h>
19ae02e5d4SSrinivas Pandruvada #include <linux/interrupt.h>
20ae02e5d4SSrinivas Pandruvada #include <linux/workqueue.h>
21ae02e5d4SSrinivas Pandruvada #define CREATE_TRACE_POINTS
22ae02e5d4SSrinivas Pandruvada #include <trace/events/intel_ish.h>
23ae02e5d4SSrinivas Pandruvada #include "ishtp-dev.h"
24ae02e5d4SSrinivas Pandruvada #include "hw-ish.h"
25ae02e5d4SSrinivas Pandruvada 
26ae02e5d4SSrinivas Pandruvada static const struct pci_device_id ish_pci_tbl[] = {
27ae02e5d4SSrinivas Pandruvada 	{PCI_DEVICE(PCI_VENDOR_ID_INTEL, CHV_DEVICE_ID)},
28ae02e5d4SSrinivas Pandruvada 	{PCI_DEVICE(PCI_VENDOR_ID_INTEL, BXT_Ax_DEVICE_ID)},
29ae02e5d4SSrinivas Pandruvada 	{PCI_DEVICE(PCI_VENDOR_ID_INTEL, BXT_Bx_DEVICE_ID)},
30ae02e5d4SSrinivas Pandruvada 	{PCI_DEVICE(PCI_VENDOR_ID_INTEL, APL_Ax_DEVICE_ID)},
31ae02e5d4SSrinivas Pandruvada 	{PCI_DEVICE(PCI_VENDOR_ID_INTEL, SPT_Ax_DEVICE_ID)},
321e3b74a2SSong Hongyan 	{PCI_DEVICE(PCI_VENDOR_ID_INTEL, CNL_Ax_DEVICE_ID)},
3316941309SSong Hongyan 	{PCI_DEVICE(PCI_VENDOR_ID_INTEL, GLK_Ax_DEVICE_ID)},
347103f6b2SSrinivas Pandruvada 	{PCI_DEVICE(PCI_VENDOR_ID_INTEL, CNL_H_DEVICE_ID)},
359ff3541eSSrinivas Pandruvada 	{PCI_DEVICE(PCI_VENDOR_ID_INTEL, ICL_MOBILE_DEVICE_ID)},
36e0ab8b26SAndreas Bosch 	{PCI_DEVICE(PCI_VENDOR_ID_INTEL, SPT_H_DEVICE_ID)},
37a50e8e2eSSrinivas Pandruvada 	{PCI_DEVICE(PCI_VENDOR_ID_INTEL, CML_LP_DEVICE_ID)},
38abb33ee8SEven Xu 	{PCI_DEVICE(PCI_VENDOR_ID_INTEL, CMP_H_DEVICE_ID)},
39b640be5bSEven Xu 	{PCI_DEVICE(PCI_VENDOR_ID_INTEL, EHL_Ax_DEVICE_ID)},
401479a82dSSrinivas Pandruvada 	{PCI_DEVICE(PCI_VENDOR_ID_INTEL, TGL_LP_DEVICE_ID)},
412aefba19SYou-Sheng Yang 	{PCI_DEVICE(PCI_VENDOR_ID_INTEL, TGL_H_DEVICE_ID)},
4222db5e00SYe Xiang 	{PCI_DEVICE(PCI_VENDOR_ID_INTEL, ADL_S_DEVICE_ID)},
4322db5e00SYe Xiang 	{PCI_DEVICE(PCI_VENDOR_ID_INTEL, ADL_P_DEVICE_ID)},
4410ec4afdSEven Xu 	{PCI_DEVICE(PCI_VENDOR_ID_INTEL, ADL_N_DEVICE_ID)},
4510ec4afdSEven Xu 	{PCI_DEVICE(PCI_VENDOR_ID_INTEL, RPL_S_DEVICE_ID)},
46ae02e5d4SSrinivas Pandruvada 	{0, }
47ae02e5d4SSrinivas Pandruvada };
48ae02e5d4SSrinivas Pandruvada MODULE_DEVICE_TABLE(pci, ish_pci_tbl);
49ae02e5d4SSrinivas Pandruvada 
50ae02e5d4SSrinivas Pandruvada /**
51ae02e5d4SSrinivas Pandruvada  * ish_event_tracer() - Callback function to dump trace messages
52ae02e5d4SSrinivas Pandruvada  * @dev:	ishtp device
53ae02e5d4SSrinivas Pandruvada  * @format:	printf style format
54ae02e5d4SSrinivas Pandruvada  *
55ae02e5d4SSrinivas Pandruvada  * Callback to direct log messages to Linux trace buffers
56ae02e5d4SSrinivas Pandruvada  */
570aae34faSNicolas Iooss static __printf(2, 3)
580aae34faSNicolas Iooss void ish_event_tracer(struct ishtp_device *dev, const char *format, ...)
59ae02e5d4SSrinivas Pandruvada {
60ae02e5d4SSrinivas Pandruvada 	if (trace_ishtp_dump_enabled()) {
61ae02e5d4SSrinivas Pandruvada 		va_list args;
62ae02e5d4SSrinivas Pandruvada 		char tmp_buf[100];
63ae02e5d4SSrinivas Pandruvada 
64ae02e5d4SSrinivas Pandruvada 		va_start(args, format);
65ae02e5d4SSrinivas Pandruvada 		vsnprintf(tmp_buf, sizeof(tmp_buf), format, args);
66ae02e5d4SSrinivas Pandruvada 		va_end(args);
67ae02e5d4SSrinivas Pandruvada 
68ae02e5d4SSrinivas Pandruvada 		trace_ishtp_dump(tmp_buf);
69ae02e5d4SSrinivas Pandruvada 	}
70ae02e5d4SSrinivas Pandruvada }
71ae02e5d4SSrinivas Pandruvada 
72ae02e5d4SSrinivas Pandruvada /**
73ae02e5d4SSrinivas Pandruvada  * ish_init() - Init function
74ae02e5d4SSrinivas Pandruvada  * @dev:	ishtp device
75ae02e5d4SSrinivas Pandruvada  *
76ae02e5d4SSrinivas Pandruvada  * This function initialize wait queues for suspend/resume and call
77ae02e5d4SSrinivas Pandruvada  * calls hadware initialization function. This will initiate
78ae02e5d4SSrinivas Pandruvada  * startup sequence
79ae02e5d4SSrinivas Pandruvada  *
80ae02e5d4SSrinivas Pandruvada  * Return: 0 for success or error code for failure
81ae02e5d4SSrinivas Pandruvada  */
82ae02e5d4SSrinivas Pandruvada static int ish_init(struct ishtp_device *dev)
83ae02e5d4SSrinivas Pandruvada {
84ae02e5d4SSrinivas Pandruvada 	int ret;
85ae02e5d4SSrinivas Pandruvada 
86ae02e5d4SSrinivas Pandruvada 	/* Set the state of ISH HW to start */
87ae02e5d4SSrinivas Pandruvada 	ret = ish_hw_start(dev);
88ae02e5d4SSrinivas Pandruvada 	if (ret) {
89ae02e5d4SSrinivas Pandruvada 		dev_err(dev->devc, "ISH: hw start failed.\n");
90ae02e5d4SSrinivas Pandruvada 		return ret;
91ae02e5d4SSrinivas Pandruvada 	}
92ae02e5d4SSrinivas Pandruvada 
93ae02e5d4SSrinivas Pandruvada 	/* Start the inter process communication to ISH processor */
94ae02e5d4SSrinivas Pandruvada 	ret = ishtp_start(dev);
95ae02e5d4SSrinivas Pandruvada 	if (ret) {
96ae02e5d4SSrinivas Pandruvada 		dev_err(dev->devc, "ISHTP: Protocol init failed.\n");
97ae02e5d4SSrinivas Pandruvada 		return ret;
98ae02e5d4SSrinivas Pandruvada 	}
99ae02e5d4SSrinivas Pandruvada 
100ae02e5d4SSrinivas Pandruvada 	return 0;
101ae02e5d4SSrinivas Pandruvada }
102ae02e5d4SSrinivas Pandruvada 
103a1e9a9c0SSrinivas Pandruvada static const struct pci_device_id ish_invalid_pci_ids[] = {
104a1e9a9c0SSrinivas Pandruvada 	/* Mehlow platform special pci ids */
105a1e9a9c0SSrinivas Pandruvada 	{PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0xA309)},
106a1e9a9c0SSrinivas Pandruvada 	{PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0xA30A)},
107a1e9a9c0SSrinivas Pandruvada 	{}
108a1e9a9c0SSrinivas Pandruvada };
109a1e9a9c0SSrinivas Pandruvada 
110c1ca58f6SZhang Lixu static inline bool ish_should_enter_d0i3(struct pci_dev *pdev)
111c1ca58f6SZhang Lixu {
112c1ca58f6SZhang Lixu 	return !pm_suspend_via_firmware() || pdev->device == CHV_DEVICE_ID;
113c1ca58f6SZhang Lixu }
114c1ca58f6SZhang Lixu 
1157e341061SKai-Heng Feng static inline bool ish_should_leave_d0i3(struct pci_dev *pdev)
1167e341061SKai-Heng Feng {
1177e341061SKai-Heng Feng 	return !pm_resume_via_firmware() || pdev->device == CHV_DEVICE_ID;
1187e341061SKai-Heng Feng }
1197e341061SKai-Heng Feng 
1202e23a70eSZhang Lixu static int enable_gpe(struct device *dev)
1212e23a70eSZhang Lixu {
1222e23a70eSZhang Lixu #ifdef CONFIG_ACPI
1232e23a70eSZhang Lixu 	acpi_status acpi_sts;
1242e23a70eSZhang Lixu 	struct acpi_device *adev;
1252e23a70eSZhang Lixu 	struct acpi_device_wakeup *wakeup;
1262e23a70eSZhang Lixu 
1272e23a70eSZhang Lixu 	adev = ACPI_COMPANION(dev);
1282e23a70eSZhang Lixu 	if (!adev) {
1292e23a70eSZhang Lixu 		dev_err(dev, "get acpi handle failed\n");
1302e23a70eSZhang Lixu 		return -ENODEV;
1312e23a70eSZhang Lixu 	}
1322e23a70eSZhang Lixu 	wakeup = &adev->wakeup;
1332e23a70eSZhang Lixu 
1342e23a70eSZhang Lixu 	acpi_sts = acpi_enable_gpe(wakeup->gpe_device, wakeup->gpe_number);
1352e23a70eSZhang Lixu 	if (ACPI_FAILURE(acpi_sts)) {
1362e23a70eSZhang Lixu 		dev_err(dev, "enable ose_gpe failed\n");
1372e23a70eSZhang Lixu 		return -EIO;
1382e23a70eSZhang Lixu 	}
1392e23a70eSZhang Lixu 
1402e23a70eSZhang Lixu 	return 0;
1412e23a70eSZhang Lixu #else
1422e23a70eSZhang Lixu 	return -ENODEV;
1432e23a70eSZhang Lixu #endif
1442e23a70eSZhang Lixu }
1452e23a70eSZhang Lixu 
1462e23a70eSZhang Lixu static void enable_pme_wake(struct pci_dev *pdev)
1472e23a70eSZhang Lixu {
1482e23a70eSZhang Lixu 	if ((pci_pme_capable(pdev, PCI_D0) ||
1492e23a70eSZhang Lixu 	     pci_pme_capable(pdev, PCI_D3hot) ||
1502e23a70eSZhang Lixu 	     pci_pme_capable(pdev, PCI_D3cold)) && !enable_gpe(&pdev->dev)) {
1512e23a70eSZhang Lixu 		pci_pme_active(pdev, true);
1522e23a70eSZhang Lixu 		dev_dbg(&pdev->dev, "ish ipc driver pme wake enabled\n");
1532e23a70eSZhang Lixu 	}
1542e23a70eSZhang Lixu }
1552e23a70eSZhang Lixu 
156ae02e5d4SSrinivas Pandruvada /**
157ae02e5d4SSrinivas Pandruvada  * ish_probe() - PCI driver probe callback
158ae02e5d4SSrinivas Pandruvada  * @pdev:	pci device
159ae02e5d4SSrinivas Pandruvada  * @ent:	pci device id
160ae02e5d4SSrinivas Pandruvada  *
161ae02e5d4SSrinivas Pandruvada  * Initialize PCI function, setup interrupt and call for ISH initialization
162ae02e5d4SSrinivas Pandruvada  *
163ae02e5d4SSrinivas Pandruvada  * Return: 0 for success or error code for failure
164ae02e5d4SSrinivas Pandruvada  */
165ae02e5d4SSrinivas Pandruvada static int ish_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
166ae02e5d4SSrinivas Pandruvada {
167ae02e5d4SSrinivas Pandruvada 	int ret;
168946a7773SHong Liu 	struct ish_hw *hw;
16974fbc7d3SSong Hongyan 	unsigned long irq_flag = 0;
170946a7773SHong Liu 	struct ishtp_device *ishtp;
171946a7773SHong Liu 	struct device *dev = &pdev->dev;
172ae02e5d4SSrinivas Pandruvada 
173a1e9a9c0SSrinivas Pandruvada 	/* Check for invalid platforms for ISH support */
174a1e9a9c0SSrinivas Pandruvada 	if (pci_dev_present(ish_invalid_pci_ids))
175a1e9a9c0SSrinivas Pandruvada 		return -ENODEV;
176a1e9a9c0SSrinivas Pandruvada 
177ae02e5d4SSrinivas Pandruvada 	/* enable pci dev */
178946a7773SHong Liu 	ret = pcim_enable_device(pdev);
179ae02e5d4SSrinivas Pandruvada 	if (ret) {
180946a7773SHong Liu 		dev_err(dev, "ISH: Failed to enable PCI device\n");
181ae02e5d4SSrinivas Pandruvada 		return ret;
182ae02e5d4SSrinivas Pandruvada 	}
183ae02e5d4SSrinivas Pandruvada 
184ae02e5d4SSrinivas Pandruvada 	/* set PCI host mastering */
185ae02e5d4SSrinivas Pandruvada 	pci_set_master(pdev);
186ae02e5d4SSrinivas Pandruvada 
187ae02e5d4SSrinivas Pandruvada 	/* pci request regions for ISH driver */
188946a7773SHong Liu 	ret = pcim_iomap_regions(pdev, 1 << 0, KBUILD_MODNAME);
189ae02e5d4SSrinivas Pandruvada 	if (ret) {
190946a7773SHong Liu 		dev_err(dev, "ISH: Failed to get PCI regions\n");
191946a7773SHong Liu 		return ret;
192ae02e5d4SSrinivas Pandruvada 	}
193ae02e5d4SSrinivas Pandruvada 
194ae02e5d4SSrinivas Pandruvada 	/* allocates and initializes the ISH dev structure */
195946a7773SHong Liu 	ishtp = ish_dev_init(pdev);
196946a7773SHong Liu 	if (!ishtp) {
197ae02e5d4SSrinivas Pandruvada 		ret = -ENOMEM;
198946a7773SHong Liu 		return ret;
199ae02e5d4SSrinivas Pandruvada 	}
200946a7773SHong Liu 	hw = to_ish_hw(ishtp);
201946a7773SHong Liu 	ishtp->print_log = ish_event_tracer;
202ae02e5d4SSrinivas Pandruvada 
203ae02e5d4SSrinivas Pandruvada 	/* mapping IO device memory */
204946a7773SHong Liu 	hw->mem_addr = pcim_iomap_table(pdev)[0];
205946a7773SHong Liu 	ishtp->pdev = pdev;
206ae02e5d4SSrinivas Pandruvada 
207ae02e5d4SSrinivas Pandruvada 	/* request and enable interrupt */
20874fbc7d3SSong Hongyan 	ret = pci_alloc_irq_vectors(pdev, 1, 1, PCI_IRQ_ALL_TYPES);
20974fbc7d3SSong Hongyan 	if (!pdev->msi_enabled && !pdev->msix_enabled)
21074fbc7d3SSong Hongyan 		irq_flag = IRQF_SHARED;
21174fbc7d3SSong Hongyan 
212946a7773SHong Liu 	ret = devm_request_irq(dev, pdev->irq, ish_irq_handler,
21374fbc7d3SSong Hongyan 			       irq_flag, KBUILD_MODNAME, ishtp);
214ae02e5d4SSrinivas Pandruvada 	if (ret) {
215946a7773SHong Liu 		dev_err(dev, "ISH: request IRQ %d failed\n", pdev->irq);
216946a7773SHong Liu 		return ret;
217ae02e5d4SSrinivas Pandruvada 	}
218ae02e5d4SSrinivas Pandruvada 
219946a7773SHong Liu 	dev_set_drvdata(ishtp->devc, ishtp);
220ae02e5d4SSrinivas Pandruvada 
221946a7773SHong Liu 	init_waitqueue_head(&ishtp->suspend_wait);
222946a7773SHong Liu 	init_waitqueue_head(&ishtp->resume_wait);
223ae02e5d4SSrinivas Pandruvada 
2242e23a70eSZhang Lixu 	/* Enable PME for EHL */
2252e23a70eSZhang Lixu 	if (pdev->device == EHL_Ax_DEVICE_ID)
2262e23a70eSZhang Lixu 		enable_pme_wake(pdev);
2272e23a70eSZhang Lixu 
228946a7773SHong Liu 	ret = ish_init(ishtp);
229ae02e5d4SSrinivas Pandruvada 	if (ret)
230946a7773SHong Liu 		return ret;
231ae02e5d4SSrinivas Pandruvada 
232ae02e5d4SSrinivas Pandruvada 	return 0;
233ae02e5d4SSrinivas Pandruvada }
234ae02e5d4SSrinivas Pandruvada 
235ae02e5d4SSrinivas Pandruvada /**
236ae02e5d4SSrinivas Pandruvada  * ish_remove() - PCI driver remove callback
237ae02e5d4SSrinivas Pandruvada  * @pdev:	pci device
238ae02e5d4SSrinivas Pandruvada  *
239ae02e5d4SSrinivas Pandruvada  * This function does cleanup of ISH on pci remove callback
240ae02e5d4SSrinivas Pandruvada  */
241ae02e5d4SSrinivas Pandruvada static void ish_remove(struct pci_dev *pdev)
242ae02e5d4SSrinivas Pandruvada {
243ae02e5d4SSrinivas Pandruvada 	struct ishtp_device *ishtp_dev = pci_get_drvdata(pdev);
244ae02e5d4SSrinivas Pandruvada 
245ae02e5d4SSrinivas Pandruvada 	ishtp_bus_remove_all_clients(ishtp_dev, false);
246ae02e5d4SSrinivas Pandruvada 	ish_device_disable(ishtp_dev);
247ae02e5d4SSrinivas Pandruvada }
248ae02e5d4SSrinivas Pandruvada 
249ebeaa367SEven Xu static struct device __maybe_unused *ish_resume_device;
250ae02e5d4SSrinivas Pandruvada 
251291e9e3fSEven Xu /* 50ms to get resume response */
252291e9e3fSEven Xu #define WAIT_FOR_RESUME_ACK_MS		50
253291e9e3fSEven Xu 
254ae02e5d4SSrinivas Pandruvada /**
255ae02e5d4SSrinivas Pandruvada  * ish_resume_handler() - Work function to complete resume
256ae02e5d4SSrinivas Pandruvada  * @work:	work struct
257ae02e5d4SSrinivas Pandruvada  *
258ae02e5d4SSrinivas Pandruvada  * The resume work function to complete resume function asynchronously.
259291e9e3fSEven Xu  * There are two resume paths, one where ISH is not powered off,
260ae02e5d4SSrinivas Pandruvada  * in that case a simple resume message is enough, others we need
261ae02e5d4SSrinivas Pandruvada  * a reset sequence.
262ae02e5d4SSrinivas Pandruvada  */
263ebeaa367SEven Xu static void __maybe_unused ish_resume_handler(struct work_struct *work)
264ae02e5d4SSrinivas Pandruvada {
265ae02e5d4SSrinivas Pandruvada 	struct pci_dev *pdev = to_pci_dev(ish_resume_device);
266ae02e5d4SSrinivas Pandruvada 	struct ishtp_device *dev = pci_get_drvdata(pdev);
2672f4ec154SZhang Lixu 	uint32_t fwsts = dev->ops->get_fw_status(dev);
268ae02e5d4SSrinivas Pandruvada 
2692f4ec154SZhang Lixu 	if (ish_should_leave_d0i3(pdev) && !dev->suspend_flag
2702f4ec154SZhang Lixu 			&& IPC_IS_ISH_ILUP(fwsts)) {
271086e81f6SThomas Weißschuh 		if (device_may_wakeup(&pdev->dev))
2722db8edaaSZhang Lixu 			disable_irq_wake(pdev->irq);
273291e9e3fSEven Xu 
2742f4ec154SZhang Lixu 		ish_set_host_ready(dev);
2752f4ec154SZhang Lixu 
276ae02e5d4SSrinivas Pandruvada 		ishtp_send_resume(dev);
277ae02e5d4SSrinivas Pandruvada 
278291e9e3fSEven Xu 		/* Waiting to get resume response */
279ae02e5d4SSrinivas Pandruvada 		if (dev->resume_flag)
2803977e00eSLee Jones 			wait_event_interruptible_timeout(dev->resume_wait,
281ae02e5d4SSrinivas Pandruvada 				!dev->resume_flag,
282291e9e3fSEven Xu 				msecs_to_jiffies(WAIT_FOR_RESUME_ACK_MS));
283ae02e5d4SSrinivas Pandruvada 
284ae02e5d4SSrinivas Pandruvada 		/*
285fc19a57dSZhang Lixu 		 * If the flag is not cleared, something is wrong with ISH FW.
286fc19a57dSZhang Lixu 		 * So on resume, need to go through init sequence again.
287ae02e5d4SSrinivas Pandruvada 		 */
288ae02e5d4SSrinivas Pandruvada 		if (dev->resume_flag)
289ae02e5d4SSrinivas Pandruvada 			ish_init(dev);
290fc19a57dSZhang Lixu 	} else {
291fc19a57dSZhang Lixu 		/*
292fc19a57dSZhang Lixu 		 * Resume from the D3, full reboot of ISH processor will happen,
293fc19a57dSZhang Lixu 		 * so need to go through init sequence again.
294fc19a57dSZhang Lixu 		 */
295fc19a57dSZhang Lixu 		ish_init(dev);
296fc19a57dSZhang Lixu 	}
297ae02e5d4SSrinivas Pandruvada }
298ae02e5d4SSrinivas Pandruvada 
299ae02e5d4SSrinivas Pandruvada /**
300ae02e5d4SSrinivas Pandruvada  * ish_suspend() - ISH suspend callback
301ae02e5d4SSrinivas Pandruvada  * @device:	device pointer
302ae02e5d4SSrinivas Pandruvada  *
303ae02e5d4SSrinivas Pandruvada  * ISH suspend callback
304ae02e5d4SSrinivas Pandruvada  *
305ae02e5d4SSrinivas Pandruvada  * Return: 0 to the pm core
306ae02e5d4SSrinivas Pandruvada  */
307ebeaa367SEven Xu static int __maybe_unused ish_suspend(struct device *device)
308ae02e5d4SSrinivas Pandruvada {
309ae02e5d4SSrinivas Pandruvada 	struct pci_dev *pdev = to_pci_dev(device);
310ae02e5d4SSrinivas Pandruvada 	struct ishtp_device *dev = pci_get_drvdata(pdev);
311ae02e5d4SSrinivas Pandruvada 
3122db8edaaSZhang Lixu 	if (ish_should_enter_d0i3(pdev)) {
313ae02e5d4SSrinivas Pandruvada 		/*
3142db8edaaSZhang Lixu 		 * If previous suspend hasn't been asnwered then ISH is likely
3152db8edaaSZhang Lixu 		 * dead, don't attempt nested notification
316ae02e5d4SSrinivas Pandruvada 		 */
317ae02e5d4SSrinivas Pandruvada 		if (dev->suspend_flag)
318ae02e5d4SSrinivas Pandruvada 			return	0;
319ae02e5d4SSrinivas Pandruvada 
320ae02e5d4SSrinivas Pandruvada 		dev->resume_flag = 0;
321ae02e5d4SSrinivas Pandruvada 		dev->suspend_flag = 1;
322ae02e5d4SSrinivas Pandruvada 		ishtp_send_suspend(dev);
323ae02e5d4SSrinivas Pandruvada 
324ae02e5d4SSrinivas Pandruvada 		/* 25 ms should be enough for live ISH to flush all IPC buf */
325ae02e5d4SSrinivas Pandruvada 		if (dev->suspend_flag)
326ae02e5d4SSrinivas Pandruvada 			wait_event_interruptible_timeout(dev->suspend_wait,
327ae02e5d4SSrinivas Pandruvada 					!dev->suspend_flag,
328ae02e5d4SSrinivas Pandruvada 					msecs_to_jiffies(25));
329ae02e5d4SSrinivas Pandruvada 
3302db8edaaSZhang Lixu 		if (dev->suspend_flag) {
3312db8edaaSZhang Lixu 			/*
3322db8edaaSZhang Lixu 			 * It looks like FW halt, clear the DMA bit, and put
3332db8edaaSZhang Lixu 			 * ISH into D3, and FW would reset on resume.
3342db8edaaSZhang Lixu 			 */
3352db8edaaSZhang Lixu 			ish_disable_dma(dev);
3362db8edaaSZhang Lixu 		} else {
3377e341061SKai-Heng Feng 			/*
3387e341061SKai-Heng Feng 			 * Save state so PCI core will keep the device at D0,
3397e341061SKai-Heng Feng 			 * the ISH would enter D0i3
3407e341061SKai-Heng Feng 			 */
3417e341061SKai-Heng Feng 			pci_save_state(pdev);
3422db8edaaSZhang Lixu 
343086e81f6SThomas Weißschuh 			if (device_may_wakeup(&pdev->dev))
3442db8edaaSZhang Lixu 				enable_irq_wake(pdev->irq);
3452db8edaaSZhang Lixu 		}
346c1ca58f6SZhang Lixu 	} else {
347c1ca58f6SZhang Lixu 		/*
348c1ca58f6SZhang Lixu 		 * Clear the DMA bit before putting ISH into D3,
349c1ca58f6SZhang Lixu 		 * or ISH FW would reset automatically.
350c1ca58f6SZhang Lixu 		 */
351c1ca58f6SZhang Lixu 		ish_disable_dma(dev);
352c1ca58f6SZhang Lixu 	}
353c1ca58f6SZhang Lixu 
354ae02e5d4SSrinivas Pandruvada 	return 0;
355ae02e5d4SSrinivas Pandruvada }
356ae02e5d4SSrinivas Pandruvada 
357ebeaa367SEven Xu static __maybe_unused DECLARE_WORK(resume_work, ish_resume_handler);
358ae02e5d4SSrinivas Pandruvada /**
359ae02e5d4SSrinivas Pandruvada  * ish_resume() - ISH resume callback
360ae02e5d4SSrinivas Pandruvada  * @device:	device pointer
361ae02e5d4SSrinivas Pandruvada  *
362ae02e5d4SSrinivas Pandruvada  * ISH resume callback
363ae02e5d4SSrinivas Pandruvada  *
364ae02e5d4SSrinivas Pandruvada  * Return: 0 to the pm core
365ae02e5d4SSrinivas Pandruvada  */
366ebeaa367SEven Xu static int __maybe_unused ish_resume(struct device *device)
367ae02e5d4SSrinivas Pandruvada {
368ae02e5d4SSrinivas Pandruvada 	struct pci_dev *pdev = to_pci_dev(device);
369ae02e5d4SSrinivas Pandruvada 	struct ishtp_device *dev = pci_get_drvdata(pdev);
370ae02e5d4SSrinivas Pandruvada 
3712e23a70eSZhang Lixu 	/* add this to finish power flow for EHL */
3722e23a70eSZhang Lixu 	if (dev->pdev->device == EHL_Ax_DEVICE_ID) {
3732e23a70eSZhang Lixu 		pci_set_power_state(pdev, PCI_D0);
3742e23a70eSZhang Lixu 		enable_pme_wake(pdev);
3752e23a70eSZhang Lixu 		dev_dbg(dev->devc, "set power state to D0 for ehl\n");
3762e23a70eSZhang Lixu 	}
3772e23a70eSZhang Lixu 
378ae02e5d4SSrinivas Pandruvada 	ish_resume_device = device;
379ae02e5d4SSrinivas Pandruvada 	dev->resume_flag = 1;
380ae02e5d4SSrinivas Pandruvada 
381ae02e5d4SSrinivas Pandruvada 	schedule_work(&resume_work);
382ae02e5d4SSrinivas Pandruvada 
383ae02e5d4SSrinivas Pandruvada 	return 0;
384ae02e5d4SSrinivas Pandruvada }
385ae02e5d4SSrinivas Pandruvada 
386ebeaa367SEven Xu static SIMPLE_DEV_PM_OPS(ish_pm_ops, ish_suspend, ish_resume);
387ae02e5d4SSrinivas Pandruvada 
388ae02e5d4SSrinivas Pandruvada static struct pci_driver ish_driver = {
389ae02e5d4SSrinivas Pandruvada 	.name = KBUILD_MODNAME,
390ae02e5d4SSrinivas Pandruvada 	.id_table = ish_pci_tbl,
391ae02e5d4SSrinivas Pandruvada 	.probe = ish_probe,
392ae02e5d4SSrinivas Pandruvada 	.remove = ish_remove,
393ebeaa367SEven Xu 	.driver.pm = &ish_pm_ops,
394ae02e5d4SSrinivas Pandruvada };
395ae02e5d4SSrinivas Pandruvada 
39637becf6eSWei Yongjun module_pci_driver(ish_driver);
397ae02e5d4SSrinivas Pandruvada 
398ae02e5d4SSrinivas Pandruvada /* Original author */
399ae02e5d4SSrinivas Pandruvada MODULE_AUTHOR("Daniel Drubin <daniel.drubin@intel.com>");
400ae02e5d4SSrinivas Pandruvada /* Adoption to upstream Linux kernel */
401ae02e5d4SSrinivas Pandruvada MODULE_AUTHOR("Srinivas Pandruvada <srinivas.pandruvada@linux.intel.com>");
402ae02e5d4SSrinivas Pandruvada 
403ae02e5d4SSrinivas Pandruvada MODULE_DESCRIPTION("Intel(R) Integrated Sensor Hub PCI Device Driver");
404ae02e5d4SSrinivas Pandruvada MODULE_LICENSE("GPL");
405