Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-only
0002 /*
0003  * PCI glue for ISHTP provider device (ISH) driver
0004  *
0005  * Copyright (c) 2014-2016, Intel Corporation.
0006  */
0007 
0008 #include <linux/acpi.h>
0009 #include <linux/module.h>
0010 #include <linux/moduleparam.h>
0011 #include <linux/kernel.h>
0012 #include <linux/device.h>
0013 #include <linux/fs.h>
0014 #include <linux/errno.h>
0015 #include <linux/types.h>
0016 #include <linux/pci.h>
0017 #include <linux/sched.h>
0018 #include <linux/suspend.h>
0019 #include <linux/interrupt.h>
0020 #include <linux/workqueue.h>
0021 #define CREATE_TRACE_POINTS
0022 #include <trace/events/intel_ish.h>
0023 #include "ishtp-dev.h"
0024 #include "hw-ish.h"
0025 
0026 static const struct pci_device_id ish_pci_tbl[] = {
0027     {PCI_DEVICE(PCI_VENDOR_ID_INTEL, CHV_DEVICE_ID)},
0028     {PCI_DEVICE(PCI_VENDOR_ID_INTEL, BXT_Ax_DEVICE_ID)},
0029     {PCI_DEVICE(PCI_VENDOR_ID_INTEL, BXT_Bx_DEVICE_ID)},
0030     {PCI_DEVICE(PCI_VENDOR_ID_INTEL, APL_Ax_DEVICE_ID)},
0031     {PCI_DEVICE(PCI_VENDOR_ID_INTEL, SPT_Ax_DEVICE_ID)},
0032     {PCI_DEVICE(PCI_VENDOR_ID_INTEL, CNL_Ax_DEVICE_ID)},
0033     {PCI_DEVICE(PCI_VENDOR_ID_INTEL, GLK_Ax_DEVICE_ID)},
0034     {PCI_DEVICE(PCI_VENDOR_ID_INTEL, CNL_H_DEVICE_ID)},
0035     {PCI_DEVICE(PCI_VENDOR_ID_INTEL, ICL_MOBILE_DEVICE_ID)},
0036     {PCI_DEVICE(PCI_VENDOR_ID_INTEL, SPT_H_DEVICE_ID)},
0037     {PCI_DEVICE(PCI_VENDOR_ID_INTEL, CML_LP_DEVICE_ID)},
0038     {PCI_DEVICE(PCI_VENDOR_ID_INTEL, CMP_H_DEVICE_ID)},
0039     {PCI_DEVICE(PCI_VENDOR_ID_INTEL, EHL_Ax_DEVICE_ID)},
0040     {PCI_DEVICE(PCI_VENDOR_ID_INTEL, TGL_LP_DEVICE_ID)},
0041     {PCI_DEVICE(PCI_VENDOR_ID_INTEL, TGL_H_DEVICE_ID)},
0042     {PCI_DEVICE(PCI_VENDOR_ID_INTEL, ADL_S_DEVICE_ID)},
0043     {PCI_DEVICE(PCI_VENDOR_ID_INTEL, ADL_P_DEVICE_ID)},
0044     {PCI_DEVICE(PCI_VENDOR_ID_INTEL, ADL_N_DEVICE_ID)},
0045     {PCI_DEVICE(PCI_VENDOR_ID_INTEL, RPL_S_DEVICE_ID)},
0046     {PCI_DEVICE(PCI_VENDOR_ID_INTEL, MTL_P_DEVICE_ID)},
0047     {0, }
0048 };
0049 MODULE_DEVICE_TABLE(pci, ish_pci_tbl);
0050 
0051 /**
0052  * ish_event_tracer() - Callback function to dump trace messages
0053  * @dev:    ishtp device
0054  * @format: printf style format
0055  *
0056  * Callback to direct log messages to Linux trace buffers
0057  */
0058 static __printf(2, 3)
0059 void ish_event_tracer(struct ishtp_device *dev, const char *format, ...)
0060 {
0061     if (trace_ishtp_dump_enabled()) {
0062         va_list args;
0063         char tmp_buf[100];
0064 
0065         va_start(args, format);
0066         vsnprintf(tmp_buf, sizeof(tmp_buf), format, args);
0067         va_end(args);
0068 
0069         trace_ishtp_dump(tmp_buf);
0070     }
0071 }
0072 
0073 /**
0074  * ish_init() - Init function
0075  * @dev:    ishtp device
0076  *
0077  * This function initialize wait queues for suspend/resume and call
0078  * calls hadware initialization function. This will initiate
0079  * startup sequence
0080  *
0081  * Return: 0 for success or error code for failure
0082  */
0083 static int ish_init(struct ishtp_device *dev)
0084 {
0085     int ret;
0086 
0087     /* Set the state of ISH HW to start */
0088     ret = ish_hw_start(dev);
0089     if (ret) {
0090         dev_err(dev->devc, "ISH: hw start failed.\n");
0091         return ret;
0092     }
0093 
0094     /* Start the inter process communication to ISH processor */
0095     ret = ishtp_start(dev);
0096     if (ret) {
0097         dev_err(dev->devc, "ISHTP: Protocol init failed.\n");
0098         return ret;
0099     }
0100 
0101     return 0;
0102 }
0103 
0104 static const struct pci_device_id ish_invalid_pci_ids[] = {
0105     /* Mehlow platform special pci ids */
0106     {PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0xA309)},
0107     {PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0xA30A)},
0108     {}
0109 };
0110 
0111 static inline bool ish_should_enter_d0i3(struct pci_dev *pdev)
0112 {
0113     return !pm_suspend_via_firmware() || pdev->device == CHV_DEVICE_ID;
0114 }
0115 
0116 static inline bool ish_should_leave_d0i3(struct pci_dev *pdev)
0117 {
0118     return !pm_resume_via_firmware() || pdev->device == CHV_DEVICE_ID;
0119 }
0120 
0121 static int enable_gpe(struct device *dev)
0122 {
0123 #ifdef CONFIG_ACPI
0124     acpi_status acpi_sts;
0125     struct acpi_device *adev;
0126     struct acpi_device_wakeup *wakeup;
0127 
0128     adev = ACPI_COMPANION(dev);
0129     if (!adev) {
0130         dev_err(dev, "get acpi handle failed\n");
0131         return -ENODEV;
0132     }
0133     wakeup = &adev->wakeup;
0134 
0135     acpi_sts = acpi_enable_gpe(wakeup->gpe_device, wakeup->gpe_number);
0136     if (ACPI_FAILURE(acpi_sts)) {
0137         dev_err(dev, "enable ose_gpe failed\n");
0138         return -EIO;
0139     }
0140 
0141     return 0;
0142 #else
0143     return -ENODEV;
0144 #endif
0145 }
0146 
0147 static void enable_pme_wake(struct pci_dev *pdev)
0148 {
0149     if ((pci_pme_capable(pdev, PCI_D0) ||
0150          pci_pme_capable(pdev, PCI_D3hot) ||
0151          pci_pme_capable(pdev, PCI_D3cold)) && !enable_gpe(&pdev->dev)) {
0152         pci_pme_active(pdev, true);
0153         dev_dbg(&pdev->dev, "ish ipc driver pme wake enabled\n");
0154     }
0155 }
0156 
0157 /**
0158  * ish_probe() - PCI driver probe callback
0159  * @pdev:   pci device
0160  * @ent:    pci device id
0161  *
0162  * Initialize PCI function, setup interrupt and call for ISH initialization
0163  *
0164  * Return: 0 for success or error code for failure
0165  */
0166 static int ish_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
0167 {
0168     int ret;
0169     struct ish_hw *hw;
0170     unsigned long irq_flag = 0;
0171     struct ishtp_device *ishtp;
0172     struct device *dev = &pdev->dev;
0173 
0174     /* Check for invalid platforms for ISH support */
0175     if (pci_dev_present(ish_invalid_pci_ids))
0176         return -ENODEV;
0177 
0178     /* enable pci dev */
0179     ret = pcim_enable_device(pdev);
0180     if (ret) {
0181         dev_err(dev, "ISH: Failed to enable PCI device\n");
0182         return ret;
0183     }
0184 
0185     /* set PCI host mastering */
0186     pci_set_master(pdev);
0187 
0188     /* pci request regions for ISH driver */
0189     ret = pcim_iomap_regions(pdev, 1 << 0, KBUILD_MODNAME);
0190     if (ret) {
0191         dev_err(dev, "ISH: Failed to get PCI regions\n");
0192         return ret;
0193     }
0194 
0195     /* allocates and initializes the ISH dev structure */
0196     ishtp = ish_dev_init(pdev);
0197     if (!ishtp) {
0198         ret = -ENOMEM;
0199         return ret;
0200     }
0201     hw = to_ish_hw(ishtp);
0202     ishtp->print_log = ish_event_tracer;
0203 
0204     /* mapping IO device memory */
0205     hw->mem_addr = pcim_iomap_table(pdev)[0];
0206     ishtp->pdev = pdev;
0207 
0208     /* request and enable interrupt */
0209     ret = pci_alloc_irq_vectors(pdev, 1, 1, PCI_IRQ_ALL_TYPES);
0210     if (!pdev->msi_enabled && !pdev->msix_enabled)
0211         irq_flag = IRQF_SHARED;
0212 
0213     ret = devm_request_irq(dev, pdev->irq, ish_irq_handler,
0214                    irq_flag, KBUILD_MODNAME, ishtp);
0215     if (ret) {
0216         dev_err(dev, "ISH: request IRQ %d failed\n", pdev->irq);
0217         return ret;
0218     }
0219 
0220     dev_set_drvdata(ishtp->devc, ishtp);
0221 
0222     init_waitqueue_head(&ishtp->suspend_wait);
0223     init_waitqueue_head(&ishtp->resume_wait);
0224 
0225     /* Enable PME for EHL */
0226     if (pdev->device == EHL_Ax_DEVICE_ID)
0227         enable_pme_wake(pdev);
0228 
0229     ret = ish_init(ishtp);
0230     if (ret)
0231         return ret;
0232 
0233     return 0;
0234 }
0235 
0236 /**
0237  * ish_remove() - PCI driver remove callback
0238  * @pdev:   pci device
0239  *
0240  * This function does cleanup of ISH on pci remove callback
0241  */
0242 static void ish_remove(struct pci_dev *pdev)
0243 {
0244     struct ishtp_device *ishtp_dev = pci_get_drvdata(pdev);
0245 
0246     ishtp_bus_remove_all_clients(ishtp_dev, false);
0247     ish_device_disable(ishtp_dev);
0248 }
0249 
0250 static struct device __maybe_unused *ish_resume_device;
0251 
0252 /* 50ms to get resume response */
0253 #define WAIT_FOR_RESUME_ACK_MS      50
0254 
0255 /**
0256  * ish_resume_handler() - Work function to complete resume
0257  * @work:   work struct
0258  *
0259  * The resume work function to complete resume function asynchronously.
0260  * There are two resume paths, one where ISH is not powered off,
0261  * in that case a simple resume message is enough, others we need
0262  * a reset sequence.
0263  */
0264 static void __maybe_unused ish_resume_handler(struct work_struct *work)
0265 {
0266     struct pci_dev *pdev = to_pci_dev(ish_resume_device);
0267     struct ishtp_device *dev = pci_get_drvdata(pdev);
0268     uint32_t fwsts = dev->ops->get_fw_status(dev);
0269 
0270     if (ish_should_leave_d0i3(pdev) && !dev->suspend_flag
0271             && IPC_IS_ISH_ILUP(fwsts)) {
0272         if (device_may_wakeup(&pdev->dev))
0273             disable_irq_wake(pdev->irq);
0274 
0275         ish_set_host_ready(dev);
0276 
0277         ishtp_send_resume(dev);
0278 
0279         /* Waiting to get resume response */
0280         if (dev->resume_flag)
0281             wait_event_interruptible_timeout(dev->resume_wait,
0282                 !dev->resume_flag,
0283                 msecs_to_jiffies(WAIT_FOR_RESUME_ACK_MS));
0284 
0285         /*
0286          * If the flag is not cleared, something is wrong with ISH FW.
0287          * So on resume, need to go through init sequence again.
0288          */
0289         if (dev->resume_flag)
0290             ish_init(dev);
0291     } else {
0292         /*
0293          * Resume from the D3, full reboot of ISH processor will happen,
0294          * so need to go through init sequence again.
0295          */
0296         ish_init(dev);
0297     }
0298 }
0299 
0300 /**
0301  * ish_suspend() - ISH suspend callback
0302  * @device: device pointer
0303  *
0304  * ISH suspend callback
0305  *
0306  * Return: 0 to the pm core
0307  */
0308 static int __maybe_unused ish_suspend(struct device *device)
0309 {
0310     struct pci_dev *pdev = to_pci_dev(device);
0311     struct ishtp_device *dev = pci_get_drvdata(pdev);
0312 
0313     if (ish_should_enter_d0i3(pdev)) {
0314         /*
0315          * If previous suspend hasn't been asnwered then ISH is likely
0316          * dead, don't attempt nested notification
0317          */
0318         if (dev->suspend_flag)
0319             return  0;
0320 
0321         dev->resume_flag = 0;
0322         dev->suspend_flag = 1;
0323         ishtp_send_suspend(dev);
0324 
0325         /* 25 ms should be enough for live ISH to flush all IPC buf */
0326         if (dev->suspend_flag)
0327             wait_event_interruptible_timeout(dev->suspend_wait,
0328                     !dev->suspend_flag,
0329                     msecs_to_jiffies(25));
0330 
0331         if (dev->suspend_flag) {
0332             /*
0333              * It looks like FW halt, clear the DMA bit, and put
0334              * ISH into D3, and FW would reset on resume.
0335              */
0336             ish_disable_dma(dev);
0337         } else {
0338             /*
0339              * Save state so PCI core will keep the device at D0,
0340              * the ISH would enter D0i3
0341              */
0342             pci_save_state(pdev);
0343 
0344             if (device_may_wakeup(&pdev->dev))
0345                 enable_irq_wake(pdev->irq);
0346         }
0347     } else {
0348         /*
0349          * Clear the DMA bit before putting ISH into D3,
0350          * or ISH FW would reset automatically.
0351          */
0352         ish_disable_dma(dev);
0353     }
0354 
0355     return 0;
0356 }
0357 
0358 static __maybe_unused DECLARE_WORK(resume_work, ish_resume_handler);
0359 /**
0360  * ish_resume() - ISH resume callback
0361  * @device: device pointer
0362  *
0363  * ISH resume callback
0364  *
0365  * Return: 0 to the pm core
0366  */
0367 static int __maybe_unused ish_resume(struct device *device)
0368 {
0369     struct pci_dev *pdev = to_pci_dev(device);
0370     struct ishtp_device *dev = pci_get_drvdata(pdev);
0371 
0372     /* add this to finish power flow for EHL */
0373     if (dev->pdev->device == EHL_Ax_DEVICE_ID) {
0374         pci_set_power_state(pdev, PCI_D0);
0375         enable_pme_wake(pdev);
0376         dev_dbg(dev->devc, "set power state to D0 for ehl\n");
0377     }
0378 
0379     ish_resume_device = device;
0380     dev->resume_flag = 1;
0381 
0382     schedule_work(&resume_work);
0383 
0384     return 0;
0385 }
0386 
0387 static SIMPLE_DEV_PM_OPS(ish_pm_ops, ish_suspend, ish_resume);
0388 
0389 static struct pci_driver ish_driver = {
0390     .name = KBUILD_MODNAME,
0391     .id_table = ish_pci_tbl,
0392     .probe = ish_probe,
0393     .remove = ish_remove,
0394     .driver.pm = &ish_pm_ops,
0395 };
0396 
0397 module_pci_driver(ish_driver);
0398 
0399 /* Original author */
0400 MODULE_AUTHOR("Daniel Drubin <daniel.drubin@intel.com>");
0401 /* Adoption to upstream Linux kernel */
0402 MODULE_AUTHOR("Srinivas Pandruvada <srinivas.pandruvada@linux.intel.com>");
0403 
0404 MODULE_DESCRIPTION("Intel(R) Integrated Sensor Hub PCI Device Driver");
0405 MODULE_LICENSE("GPL");