162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * PCI glue for ISHTP provider device (ISH) driver 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (c) 2014-2016, Intel Corporation. 662306a36Sopenharmony_ci */ 762306a36Sopenharmony_ci 862306a36Sopenharmony_ci#include <linux/acpi.h> 962306a36Sopenharmony_ci#include <linux/module.h> 1062306a36Sopenharmony_ci#include <linux/moduleparam.h> 1162306a36Sopenharmony_ci#include <linux/kernel.h> 1262306a36Sopenharmony_ci#include <linux/device.h> 1362306a36Sopenharmony_ci#include <linux/fs.h> 1462306a36Sopenharmony_ci#include <linux/errno.h> 1562306a36Sopenharmony_ci#include <linux/types.h> 1662306a36Sopenharmony_ci#include <linux/pci.h> 1762306a36Sopenharmony_ci#include <linux/sched.h> 1862306a36Sopenharmony_ci#include <linux/suspend.h> 1962306a36Sopenharmony_ci#include <linux/interrupt.h> 2062306a36Sopenharmony_ci#include <linux/workqueue.h> 2162306a36Sopenharmony_ci#define CREATE_TRACE_POINTS 2262306a36Sopenharmony_ci#include <trace/events/intel_ish.h> 2362306a36Sopenharmony_ci#include "ishtp-dev.h" 2462306a36Sopenharmony_ci#include "hw-ish.h" 2562306a36Sopenharmony_ci 2662306a36Sopenharmony_cistatic const struct pci_device_id ish_pci_tbl[] = { 2762306a36Sopenharmony_ci {PCI_DEVICE(PCI_VENDOR_ID_INTEL, CHV_DEVICE_ID)}, 2862306a36Sopenharmony_ci {PCI_DEVICE(PCI_VENDOR_ID_INTEL, BXT_Ax_DEVICE_ID)}, 2962306a36Sopenharmony_ci {PCI_DEVICE(PCI_VENDOR_ID_INTEL, BXT_Bx_DEVICE_ID)}, 3062306a36Sopenharmony_ci {PCI_DEVICE(PCI_VENDOR_ID_INTEL, APL_Ax_DEVICE_ID)}, 3162306a36Sopenharmony_ci {PCI_DEVICE(PCI_VENDOR_ID_INTEL, SPT_Ax_DEVICE_ID)}, 3262306a36Sopenharmony_ci {PCI_DEVICE(PCI_VENDOR_ID_INTEL, CNL_Ax_DEVICE_ID)}, 3362306a36Sopenharmony_ci {PCI_DEVICE(PCI_VENDOR_ID_INTEL, GLK_Ax_DEVICE_ID)}, 3462306a36Sopenharmony_ci {PCI_DEVICE(PCI_VENDOR_ID_INTEL, CNL_H_DEVICE_ID)}, 3562306a36Sopenharmony_ci {PCI_DEVICE(PCI_VENDOR_ID_INTEL, ICL_MOBILE_DEVICE_ID)}, 3662306a36Sopenharmony_ci {PCI_DEVICE(PCI_VENDOR_ID_INTEL, SPT_H_DEVICE_ID)}, 3762306a36Sopenharmony_ci {PCI_DEVICE(PCI_VENDOR_ID_INTEL, CML_LP_DEVICE_ID)}, 3862306a36Sopenharmony_ci {PCI_DEVICE(PCI_VENDOR_ID_INTEL, CMP_H_DEVICE_ID)}, 3962306a36Sopenharmony_ci {PCI_DEVICE(PCI_VENDOR_ID_INTEL, EHL_Ax_DEVICE_ID)}, 4062306a36Sopenharmony_ci {PCI_DEVICE(PCI_VENDOR_ID_INTEL, TGL_LP_DEVICE_ID)}, 4162306a36Sopenharmony_ci {PCI_DEVICE(PCI_VENDOR_ID_INTEL, TGL_H_DEVICE_ID)}, 4262306a36Sopenharmony_ci {PCI_DEVICE(PCI_VENDOR_ID_INTEL, ADL_S_DEVICE_ID)}, 4362306a36Sopenharmony_ci {PCI_DEVICE(PCI_VENDOR_ID_INTEL, ADL_P_DEVICE_ID)}, 4462306a36Sopenharmony_ci {PCI_DEVICE(PCI_VENDOR_ID_INTEL, ADL_N_DEVICE_ID)}, 4562306a36Sopenharmony_ci {PCI_DEVICE(PCI_VENDOR_ID_INTEL, RPL_S_DEVICE_ID)}, 4662306a36Sopenharmony_ci {PCI_DEVICE(PCI_VENDOR_ID_INTEL, MTL_P_DEVICE_ID)}, 4762306a36Sopenharmony_ci {PCI_DEVICE(PCI_VENDOR_ID_INTEL, ARL_H_DEVICE_ID)}, 4862306a36Sopenharmony_ci {0, } 4962306a36Sopenharmony_ci}; 5062306a36Sopenharmony_ciMODULE_DEVICE_TABLE(pci, ish_pci_tbl); 5162306a36Sopenharmony_ci 5262306a36Sopenharmony_ci/** 5362306a36Sopenharmony_ci * ish_event_tracer() - Callback function to dump trace messages 5462306a36Sopenharmony_ci * @dev: ishtp device 5562306a36Sopenharmony_ci * @format: printf style format 5662306a36Sopenharmony_ci * 5762306a36Sopenharmony_ci * Callback to direct log messages to Linux trace buffers 5862306a36Sopenharmony_ci */ 5962306a36Sopenharmony_cistatic __printf(2, 3) 6062306a36Sopenharmony_civoid ish_event_tracer(struct ishtp_device *dev, const char *format, ...) 6162306a36Sopenharmony_ci{ 6262306a36Sopenharmony_ci if (trace_ishtp_dump_enabled()) { 6362306a36Sopenharmony_ci va_list args; 6462306a36Sopenharmony_ci char tmp_buf[100]; 6562306a36Sopenharmony_ci 6662306a36Sopenharmony_ci va_start(args, format); 6762306a36Sopenharmony_ci vsnprintf(tmp_buf, sizeof(tmp_buf), format, args); 6862306a36Sopenharmony_ci va_end(args); 6962306a36Sopenharmony_ci 7062306a36Sopenharmony_ci trace_ishtp_dump(tmp_buf); 7162306a36Sopenharmony_ci } 7262306a36Sopenharmony_ci} 7362306a36Sopenharmony_ci 7462306a36Sopenharmony_ci/** 7562306a36Sopenharmony_ci * ish_init() - Init function 7662306a36Sopenharmony_ci * @dev: ishtp device 7762306a36Sopenharmony_ci * 7862306a36Sopenharmony_ci * This function initialize wait queues for suspend/resume and call 7962306a36Sopenharmony_ci * calls hadware initialization function. This will initiate 8062306a36Sopenharmony_ci * startup sequence 8162306a36Sopenharmony_ci * 8262306a36Sopenharmony_ci * Return: 0 for success or error code for failure 8362306a36Sopenharmony_ci */ 8462306a36Sopenharmony_cistatic int ish_init(struct ishtp_device *dev) 8562306a36Sopenharmony_ci{ 8662306a36Sopenharmony_ci int ret; 8762306a36Sopenharmony_ci 8862306a36Sopenharmony_ci /* Set the state of ISH HW to start */ 8962306a36Sopenharmony_ci ret = ish_hw_start(dev); 9062306a36Sopenharmony_ci if (ret) { 9162306a36Sopenharmony_ci dev_err(dev->devc, "ISH: hw start failed.\n"); 9262306a36Sopenharmony_ci return ret; 9362306a36Sopenharmony_ci } 9462306a36Sopenharmony_ci 9562306a36Sopenharmony_ci /* Start the inter process communication to ISH processor */ 9662306a36Sopenharmony_ci ret = ishtp_start(dev); 9762306a36Sopenharmony_ci if (ret) { 9862306a36Sopenharmony_ci dev_err(dev->devc, "ISHTP: Protocol init failed.\n"); 9962306a36Sopenharmony_ci return ret; 10062306a36Sopenharmony_ci } 10162306a36Sopenharmony_ci 10262306a36Sopenharmony_ci return 0; 10362306a36Sopenharmony_ci} 10462306a36Sopenharmony_ci 10562306a36Sopenharmony_cistatic const struct pci_device_id ish_invalid_pci_ids[] = { 10662306a36Sopenharmony_ci /* Mehlow platform special pci ids */ 10762306a36Sopenharmony_ci {PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0xA309)}, 10862306a36Sopenharmony_ci {PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0xA30A)}, 10962306a36Sopenharmony_ci {} 11062306a36Sopenharmony_ci}; 11162306a36Sopenharmony_ci 11262306a36Sopenharmony_cistatic inline bool ish_should_enter_d0i3(struct pci_dev *pdev) 11362306a36Sopenharmony_ci{ 11462306a36Sopenharmony_ci return !pm_suspend_via_firmware() || pdev->device == CHV_DEVICE_ID; 11562306a36Sopenharmony_ci} 11662306a36Sopenharmony_ci 11762306a36Sopenharmony_cistatic inline bool ish_should_leave_d0i3(struct pci_dev *pdev) 11862306a36Sopenharmony_ci{ 11962306a36Sopenharmony_ci return !pm_resume_via_firmware() || pdev->device == CHV_DEVICE_ID; 12062306a36Sopenharmony_ci} 12162306a36Sopenharmony_ci 12262306a36Sopenharmony_cistatic int enable_gpe(struct device *dev) 12362306a36Sopenharmony_ci{ 12462306a36Sopenharmony_ci#ifdef CONFIG_ACPI 12562306a36Sopenharmony_ci acpi_status acpi_sts; 12662306a36Sopenharmony_ci struct acpi_device *adev; 12762306a36Sopenharmony_ci struct acpi_device_wakeup *wakeup; 12862306a36Sopenharmony_ci 12962306a36Sopenharmony_ci adev = ACPI_COMPANION(dev); 13062306a36Sopenharmony_ci if (!adev) { 13162306a36Sopenharmony_ci dev_err(dev, "get acpi handle failed\n"); 13262306a36Sopenharmony_ci return -ENODEV; 13362306a36Sopenharmony_ci } 13462306a36Sopenharmony_ci wakeup = &adev->wakeup; 13562306a36Sopenharmony_ci 13662306a36Sopenharmony_ci /* 13762306a36Sopenharmony_ci * Call acpi_disable_gpe(), so that reference count 13862306a36Sopenharmony_ci * gpe_event_info->runtime_count doesn't overflow. 13962306a36Sopenharmony_ci * When gpe_event_info->runtime_count = 0, the call 14062306a36Sopenharmony_ci * to acpi_disable_gpe() simply return. 14162306a36Sopenharmony_ci */ 14262306a36Sopenharmony_ci acpi_disable_gpe(wakeup->gpe_device, wakeup->gpe_number); 14362306a36Sopenharmony_ci 14462306a36Sopenharmony_ci acpi_sts = acpi_enable_gpe(wakeup->gpe_device, wakeup->gpe_number); 14562306a36Sopenharmony_ci if (ACPI_FAILURE(acpi_sts)) { 14662306a36Sopenharmony_ci dev_err(dev, "enable ose_gpe failed\n"); 14762306a36Sopenharmony_ci return -EIO; 14862306a36Sopenharmony_ci } 14962306a36Sopenharmony_ci 15062306a36Sopenharmony_ci return 0; 15162306a36Sopenharmony_ci#else 15262306a36Sopenharmony_ci return -ENODEV; 15362306a36Sopenharmony_ci#endif 15462306a36Sopenharmony_ci} 15562306a36Sopenharmony_ci 15662306a36Sopenharmony_cistatic void enable_pme_wake(struct pci_dev *pdev) 15762306a36Sopenharmony_ci{ 15862306a36Sopenharmony_ci if ((pci_pme_capable(pdev, PCI_D0) || 15962306a36Sopenharmony_ci pci_pme_capable(pdev, PCI_D3hot) || 16062306a36Sopenharmony_ci pci_pme_capable(pdev, PCI_D3cold)) && !enable_gpe(&pdev->dev)) { 16162306a36Sopenharmony_ci pci_pme_active(pdev, true); 16262306a36Sopenharmony_ci dev_dbg(&pdev->dev, "ish ipc driver pme wake enabled\n"); 16362306a36Sopenharmony_ci } 16462306a36Sopenharmony_ci} 16562306a36Sopenharmony_ci 16662306a36Sopenharmony_ci/** 16762306a36Sopenharmony_ci * ish_probe() - PCI driver probe callback 16862306a36Sopenharmony_ci * @pdev: pci device 16962306a36Sopenharmony_ci * @ent: pci device id 17062306a36Sopenharmony_ci * 17162306a36Sopenharmony_ci * Initialize PCI function, setup interrupt and call for ISH initialization 17262306a36Sopenharmony_ci * 17362306a36Sopenharmony_ci * Return: 0 for success or error code for failure 17462306a36Sopenharmony_ci */ 17562306a36Sopenharmony_cistatic int ish_probe(struct pci_dev *pdev, const struct pci_device_id *ent) 17662306a36Sopenharmony_ci{ 17762306a36Sopenharmony_ci int ret; 17862306a36Sopenharmony_ci struct ish_hw *hw; 17962306a36Sopenharmony_ci unsigned long irq_flag = 0; 18062306a36Sopenharmony_ci struct ishtp_device *ishtp; 18162306a36Sopenharmony_ci struct device *dev = &pdev->dev; 18262306a36Sopenharmony_ci 18362306a36Sopenharmony_ci /* Check for invalid platforms for ISH support */ 18462306a36Sopenharmony_ci if (pci_dev_present(ish_invalid_pci_ids)) 18562306a36Sopenharmony_ci return -ENODEV; 18662306a36Sopenharmony_ci 18762306a36Sopenharmony_ci /* enable pci dev */ 18862306a36Sopenharmony_ci ret = pcim_enable_device(pdev); 18962306a36Sopenharmony_ci if (ret) { 19062306a36Sopenharmony_ci dev_err(dev, "ISH: Failed to enable PCI device\n"); 19162306a36Sopenharmony_ci return ret; 19262306a36Sopenharmony_ci } 19362306a36Sopenharmony_ci 19462306a36Sopenharmony_ci /* set PCI host mastering */ 19562306a36Sopenharmony_ci pci_set_master(pdev); 19662306a36Sopenharmony_ci 19762306a36Sopenharmony_ci /* pci request regions for ISH driver */ 19862306a36Sopenharmony_ci ret = pcim_iomap_regions(pdev, 1 << 0, KBUILD_MODNAME); 19962306a36Sopenharmony_ci if (ret) { 20062306a36Sopenharmony_ci dev_err(dev, "ISH: Failed to get PCI regions\n"); 20162306a36Sopenharmony_ci return ret; 20262306a36Sopenharmony_ci } 20362306a36Sopenharmony_ci 20462306a36Sopenharmony_ci /* allocates and initializes the ISH dev structure */ 20562306a36Sopenharmony_ci ishtp = ish_dev_init(pdev); 20662306a36Sopenharmony_ci if (!ishtp) { 20762306a36Sopenharmony_ci ret = -ENOMEM; 20862306a36Sopenharmony_ci return ret; 20962306a36Sopenharmony_ci } 21062306a36Sopenharmony_ci hw = to_ish_hw(ishtp); 21162306a36Sopenharmony_ci ishtp->print_log = ish_event_tracer; 21262306a36Sopenharmony_ci 21362306a36Sopenharmony_ci /* mapping IO device memory */ 21462306a36Sopenharmony_ci hw->mem_addr = pcim_iomap_table(pdev)[0]; 21562306a36Sopenharmony_ci ishtp->pdev = pdev; 21662306a36Sopenharmony_ci 21762306a36Sopenharmony_ci /* request and enable interrupt */ 21862306a36Sopenharmony_ci ret = pci_alloc_irq_vectors(pdev, 1, 1, PCI_IRQ_ALL_TYPES); 21962306a36Sopenharmony_ci if (!pdev->msi_enabled && !pdev->msix_enabled) 22062306a36Sopenharmony_ci irq_flag = IRQF_SHARED; 22162306a36Sopenharmony_ci 22262306a36Sopenharmony_ci ret = devm_request_irq(dev, pdev->irq, ish_irq_handler, 22362306a36Sopenharmony_ci irq_flag, KBUILD_MODNAME, ishtp); 22462306a36Sopenharmony_ci if (ret) { 22562306a36Sopenharmony_ci dev_err(dev, "ISH: request IRQ %d failed\n", pdev->irq); 22662306a36Sopenharmony_ci return ret; 22762306a36Sopenharmony_ci } 22862306a36Sopenharmony_ci 22962306a36Sopenharmony_ci dev_set_drvdata(ishtp->devc, ishtp); 23062306a36Sopenharmony_ci 23162306a36Sopenharmony_ci init_waitqueue_head(&ishtp->suspend_wait); 23262306a36Sopenharmony_ci init_waitqueue_head(&ishtp->resume_wait); 23362306a36Sopenharmony_ci 23462306a36Sopenharmony_ci /* Enable PME for EHL */ 23562306a36Sopenharmony_ci if (pdev->device == EHL_Ax_DEVICE_ID) 23662306a36Sopenharmony_ci enable_pme_wake(pdev); 23762306a36Sopenharmony_ci 23862306a36Sopenharmony_ci ret = ish_init(ishtp); 23962306a36Sopenharmony_ci if (ret) 24062306a36Sopenharmony_ci return ret; 24162306a36Sopenharmony_ci 24262306a36Sopenharmony_ci return 0; 24362306a36Sopenharmony_ci} 24462306a36Sopenharmony_ci 24562306a36Sopenharmony_ci/** 24662306a36Sopenharmony_ci * ish_remove() - PCI driver remove callback 24762306a36Sopenharmony_ci * @pdev: pci device 24862306a36Sopenharmony_ci * 24962306a36Sopenharmony_ci * This function does cleanup of ISH on pci remove callback 25062306a36Sopenharmony_ci */ 25162306a36Sopenharmony_cistatic void ish_remove(struct pci_dev *pdev) 25262306a36Sopenharmony_ci{ 25362306a36Sopenharmony_ci struct ishtp_device *ishtp_dev = pci_get_drvdata(pdev); 25462306a36Sopenharmony_ci 25562306a36Sopenharmony_ci ishtp_bus_remove_all_clients(ishtp_dev, false); 25662306a36Sopenharmony_ci ish_device_disable(ishtp_dev); 25762306a36Sopenharmony_ci} 25862306a36Sopenharmony_ci 25962306a36Sopenharmony_cistatic struct device __maybe_unused *ish_resume_device; 26062306a36Sopenharmony_ci 26162306a36Sopenharmony_ci/* 50ms to get resume response */ 26262306a36Sopenharmony_ci#define WAIT_FOR_RESUME_ACK_MS 50 26362306a36Sopenharmony_ci 26462306a36Sopenharmony_ci/** 26562306a36Sopenharmony_ci * ish_resume_handler() - Work function to complete resume 26662306a36Sopenharmony_ci * @work: work struct 26762306a36Sopenharmony_ci * 26862306a36Sopenharmony_ci * The resume work function to complete resume function asynchronously. 26962306a36Sopenharmony_ci * There are two resume paths, one where ISH is not powered off, 27062306a36Sopenharmony_ci * in that case a simple resume message is enough, others we need 27162306a36Sopenharmony_ci * a reset sequence. 27262306a36Sopenharmony_ci */ 27362306a36Sopenharmony_cistatic void __maybe_unused ish_resume_handler(struct work_struct *work) 27462306a36Sopenharmony_ci{ 27562306a36Sopenharmony_ci struct pci_dev *pdev = to_pci_dev(ish_resume_device); 27662306a36Sopenharmony_ci struct ishtp_device *dev = pci_get_drvdata(pdev); 27762306a36Sopenharmony_ci uint32_t fwsts = dev->ops->get_fw_status(dev); 27862306a36Sopenharmony_ci 27962306a36Sopenharmony_ci if (ish_should_leave_d0i3(pdev) && !dev->suspend_flag 28062306a36Sopenharmony_ci && IPC_IS_ISH_ILUP(fwsts)) { 28162306a36Sopenharmony_ci if (device_may_wakeup(&pdev->dev)) 28262306a36Sopenharmony_ci disable_irq_wake(pdev->irq); 28362306a36Sopenharmony_ci 28462306a36Sopenharmony_ci ish_set_host_ready(dev); 28562306a36Sopenharmony_ci 28662306a36Sopenharmony_ci ishtp_send_resume(dev); 28762306a36Sopenharmony_ci 28862306a36Sopenharmony_ci /* Waiting to get resume response */ 28962306a36Sopenharmony_ci if (dev->resume_flag) 29062306a36Sopenharmony_ci wait_event_interruptible_timeout(dev->resume_wait, 29162306a36Sopenharmony_ci !dev->resume_flag, 29262306a36Sopenharmony_ci msecs_to_jiffies(WAIT_FOR_RESUME_ACK_MS)); 29362306a36Sopenharmony_ci 29462306a36Sopenharmony_ci /* 29562306a36Sopenharmony_ci * If the flag is not cleared, something is wrong with ISH FW. 29662306a36Sopenharmony_ci * So on resume, need to go through init sequence again. 29762306a36Sopenharmony_ci */ 29862306a36Sopenharmony_ci if (dev->resume_flag) 29962306a36Sopenharmony_ci ish_init(dev); 30062306a36Sopenharmony_ci } else { 30162306a36Sopenharmony_ci /* 30262306a36Sopenharmony_ci * Resume from the D3, full reboot of ISH processor will happen, 30362306a36Sopenharmony_ci * so need to go through init sequence again. 30462306a36Sopenharmony_ci */ 30562306a36Sopenharmony_ci ish_init(dev); 30662306a36Sopenharmony_ci } 30762306a36Sopenharmony_ci} 30862306a36Sopenharmony_ci 30962306a36Sopenharmony_ci/** 31062306a36Sopenharmony_ci * ish_suspend() - ISH suspend callback 31162306a36Sopenharmony_ci * @device: device pointer 31262306a36Sopenharmony_ci * 31362306a36Sopenharmony_ci * ISH suspend callback 31462306a36Sopenharmony_ci * 31562306a36Sopenharmony_ci * Return: 0 to the pm core 31662306a36Sopenharmony_ci */ 31762306a36Sopenharmony_cistatic int __maybe_unused ish_suspend(struct device *device) 31862306a36Sopenharmony_ci{ 31962306a36Sopenharmony_ci struct pci_dev *pdev = to_pci_dev(device); 32062306a36Sopenharmony_ci struct ishtp_device *dev = pci_get_drvdata(pdev); 32162306a36Sopenharmony_ci 32262306a36Sopenharmony_ci if (ish_should_enter_d0i3(pdev)) { 32362306a36Sopenharmony_ci /* 32462306a36Sopenharmony_ci * If previous suspend hasn't been asnwered then ISH is likely 32562306a36Sopenharmony_ci * dead, don't attempt nested notification 32662306a36Sopenharmony_ci */ 32762306a36Sopenharmony_ci if (dev->suspend_flag) 32862306a36Sopenharmony_ci return 0; 32962306a36Sopenharmony_ci 33062306a36Sopenharmony_ci dev->resume_flag = 0; 33162306a36Sopenharmony_ci dev->suspend_flag = 1; 33262306a36Sopenharmony_ci ishtp_send_suspend(dev); 33362306a36Sopenharmony_ci 33462306a36Sopenharmony_ci /* 25 ms should be enough for live ISH to flush all IPC buf */ 33562306a36Sopenharmony_ci if (dev->suspend_flag) 33662306a36Sopenharmony_ci wait_event_interruptible_timeout(dev->suspend_wait, 33762306a36Sopenharmony_ci !dev->suspend_flag, 33862306a36Sopenharmony_ci msecs_to_jiffies(25)); 33962306a36Sopenharmony_ci 34062306a36Sopenharmony_ci if (dev->suspend_flag) { 34162306a36Sopenharmony_ci /* 34262306a36Sopenharmony_ci * It looks like FW halt, clear the DMA bit, and put 34362306a36Sopenharmony_ci * ISH into D3, and FW would reset on resume. 34462306a36Sopenharmony_ci */ 34562306a36Sopenharmony_ci ish_disable_dma(dev); 34662306a36Sopenharmony_ci } else { 34762306a36Sopenharmony_ci /* 34862306a36Sopenharmony_ci * Save state so PCI core will keep the device at D0, 34962306a36Sopenharmony_ci * the ISH would enter D0i3 35062306a36Sopenharmony_ci */ 35162306a36Sopenharmony_ci pci_save_state(pdev); 35262306a36Sopenharmony_ci 35362306a36Sopenharmony_ci if (device_may_wakeup(&pdev->dev)) 35462306a36Sopenharmony_ci enable_irq_wake(pdev->irq); 35562306a36Sopenharmony_ci } 35662306a36Sopenharmony_ci } else { 35762306a36Sopenharmony_ci /* 35862306a36Sopenharmony_ci * Clear the DMA bit before putting ISH into D3, 35962306a36Sopenharmony_ci * or ISH FW would reset automatically. 36062306a36Sopenharmony_ci */ 36162306a36Sopenharmony_ci ish_disable_dma(dev); 36262306a36Sopenharmony_ci } 36362306a36Sopenharmony_ci 36462306a36Sopenharmony_ci return 0; 36562306a36Sopenharmony_ci} 36662306a36Sopenharmony_ci 36762306a36Sopenharmony_cistatic __maybe_unused DECLARE_WORK(resume_work, ish_resume_handler); 36862306a36Sopenharmony_ci/** 36962306a36Sopenharmony_ci * ish_resume() - ISH resume callback 37062306a36Sopenharmony_ci * @device: device pointer 37162306a36Sopenharmony_ci * 37262306a36Sopenharmony_ci * ISH resume callback 37362306a36Sopenharmony_ci * 37462306a36Sopenharmony_ci * Return: 0 to the pm core 37562306a36Sopenharmony_ci */ 37662306a36Sopenharmony_cistatic int __maybe_unused ish_resume(struct device *device) 37762306a36Sopenharmony_ci{ 37862306a36Sopenharmony_ci struct pci_dev *pdev = to_pci_dev(device); 37962306a36Sopenharmony_ci struct ishtp_device *dev = pci_get_drvdata(pdev); 38062306a36Sopenharmony_ci 38162306a36Sopenharmony_ci /* add this to finish power flow for EHL */ 38262306a36Sopenharmony_ci if (dev->pdev->device == EHL_Ax_DEVICE_ID) { 38362306a36Sopenharmony_ci pci_set_power_state(pdev, PCI_D0); 38462306a36Sopenharmony_ci enable_pme_wake(pdev); 38562306a36Sopenharmony_ci dev_dbg(dev->devc, "set power state to D0 for ehl\n"); 38662306a36Sopenharmony_ci } 38762306a36Sopenharmony_ci 38862306a36Sopenharmony_ci ish_resume_device = device; 38962306a36Sopenharmony_ci dev->resume_flag = 1; 39062306a36Sopenharmony_ci 39162306a36Sopenharmony_ci schedule_work(&resume_work); 39262306a36Sopenharmony_ci 39362306a36Sopenharmony_ci return 0; 39462306a36Sopenharmony_ci} 39562306a36Sopenharmony_ci 39662306a36Sopenharmony_cistatic SIMPLE_DEV_PM_OPS(ish_pm_ops, ish_suspend, ish_resume); 39762306a36Sopenharmony_ci 39862306a36Sopenharmony_cistatic struct pci_driver ish_driver = { 39962306a36Sopenharmony_ci .name = KBUILD_MODNAME, 40062306a36Sopenharmony_ci .id_table = ish_pci_tbl, 40162306a36Sopenharmony_ci .probe = ish_probe, 40262306a36Sopenharmony_ci .remove = ish_remove, 40362306a36Sopenharmony_ci .driver.pm = &ish_pm_ops, 40462306a36Sopenharmony_ci}; 40562306a36Sopenharmony_ci 40662306a36Sopenharmony_cimodule_pci_driver(ish_driver); 40762306a36Sopenharmony_ci 40862306a36Sopenharmony_ci/* Original author */ 40962306a36Sopenharmony_ciMODULE_AUTHOR("Daniel Drubin <daniel.drubin@intel.com>"); 41062306a36Sopenharmony_ci/* Adoption to upstream Linux kernel */ 41162306a36Sopenharmony_ciMODULE_AUTHOR("Srinivas Pandruvada <srinivas.pandruvada@linux.intel.com>"); 41262306a36Sopenharmony_ci 41362306a36Sopenharmony_ciMODULE_DESCRIPTION("Intel(R) Integrated Sensor Hub PCI Device Driver"); 41462306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 415