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