Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0
0002 /*
0003  * Dummy driver for Intel's Image Signal Processor found on Bay Trail
0004  * and Cherry Trail devices. The sole purpose of this driver is to allow
0005  * the ISP to be put in D3.
0006  *
0007  * Copyright (C) 2018 Hans de Goede <hdegoede@redhat.com>
0008  *
0009  * Based on various non upstream patches for ISP support:
0010  * Copyright (C) 2010-2017 Intel Corporation. All rights reserved.
0011  * Copyright (c) 2010 Silicon Hive www.siliconhive.com.
0012  */
0013 
0014 #include <linux/delay.h>
0015 #include <linux/module.h>
0016 #include <linux/mod_devicetable.h>
0017 #include <linux/pci.h>
0018 #include <linux/pm_runtime.h>
0019 #include <asm/iosf_mbi.h>
0020 
0021 /* PCI configuration regs */
0022 #define PCI_INTERRUPT_CTRL      0x9c
0023 
0024 #define PCI_CSI_CONTROL         0xe8
0025 #define PCI_CSI_CONTROL_PORTS_OFF_MASK  0x7
0026 
0027 /* IOSF BT_MBI_UNIT_PMC regs */
0028 #define ISPSSPM0            0x39
0029 #define ISPSSPM0_ISPSSC_OFFSET      0
0030 #define ISPSSPM0_ISPSSC_MASK        0x00000003
0031 #define ISPSSPM0_ISPSSS_OFFSET      24
0032 #define ISPSSPM0_ISPSSS_MASK        0x03000000
0033 #define ISPSSPM0_IUNIT_POWER_ON     0x0
0034 #define ISPSSPM0_IUNIT_POWER_OFF    0x3
0035 
0036 static int isp_set_power(struct pci_dev *dev, bool enable)
0037 {
0038     unsigned long timeout;
0039     u32 val = enable ? ISPSSPM0_IUNIT_POWER_ON : ISPSSPM0_IUNIT_POWER_OFF;
0040 
0041     /* Write to ISPSSPM0 bit[1:0] to power on/off the IUNIT */
0042     iosf_mbi_modify(BT_MBI_UNIT_PMC, MBI_REG_READ, ISPSSPM0,
0043             val, ISPSSPM0_ISPSSC_MASK);
0044 
0045     /*
0046      * There should be no IUNIT access while power-down is
0047      * in progress. HW sighting: 4567865.
0048      * Wait up to 50 ms for the IUNIT to shut down.
0049      * And we do the same for power on.
0050      */
0051     timeout = jiffies + msecs_to_jiffies(50);
0052     do {
0053         u32 tmp;
0054 
0055         /* Wait until ISPSSPM0 bit[25:24] shows the right value */
0056         iosf_mbi_read(BT_MBI_UNIT_PMC, MBI_REG_READ, ISPSSPM0, &tmp);
0057         tmp = (tmp & ISPSSPM0_ISPSSS_MASK) >> ISPSSPM0_ISPSSS_OFFSET;
0058         if (tmp == val)
0059             return 0;
0060 
0061         usleep_range(1000, 2000);
0062     } while (time_before(jiffies, timeout));
0063 
0064     dev_err(&dev->dev, "IUNIT power-%s timeout.\n", enable ? "on" : "off");
0065     return -EBUSY;
0066 }
0067 
0068 static int isp_probe(struct pci_dev *dev, const struct pci_device_id *id)
0069 {
0070     pm_runtime_allow(&dev->dev);
0071     pm_runtime_put_sync_suspend(&dev->dev);
0072 
0073     return 0;
0074 }
0075 
0076 static void isp_remove(struct pci_dev *dev)
0077 {
0078     pm_runtime_get_sync(&dev->dev);
0079     pm_runtime_forbid(&dev->dev);
0080 }
0081 
0082 static int isp_pci_suspend(struct device *dev)
0083 {
0084     struct pci_dev *pdev = to_pci_dev(dev);
0085     u32 val;
0086 
0087     pci_write_config_dword(pdev, PCI_INTERRUPT_CTRL, 0);
0088 
0089     /*
0090      * MRFLD IUNIT DPHY is located in an always-power-on island
0091      * MRFLD HW design need all CSI ports are disabled before
0092      * powering down the IUNIT.
0093      */
0094     pci_read_config_dword(pdev, PCI_CSI_CONTROL, &val);
0095     val |= PCI_CSI_CONTROL_PORTS_OFF_MASK;
0096     pci_write_config_dword(pdev, PCI_CSI_CONTROL, val);
0097 
0098     /*
0099      * We lose config space access when punit power gates
0100      * the ISP. Can't use pci_set_power_state() because
0101      * pmcsr won't actually change when we write to it.
0102      */
0103     pci_save_state(pdev);
0104     pdev->current_state = PCI_D3cold;
0105     isp_set_power(pdev, false);
0106 
0107     return 0;
0108 }
0109 
0110 static int isp_pci_resume(struct device *dev)
0111 {
0112     struct pci_dev *pdev = to_pci_dev(dev);
0113 
0114     isp_set_power(pdev, true);
0115     pdev->current_state = PCI_D0;
0116     pci_restore_state(pdev);
0117 
0118     return 0;
0119 }
0120 
0121 static UNIVERSAL_DEV_PM_OPS(isp_pm_ops, isp_pci_suspend,
0122                 isp_pci_resume, NULL);
0123 
0124 static const struct pci_device_id isp_id_table[] = {
0125     { PCI_VDEVICE(INTEL, 0x0f38), },
0126     { PCI_VDEVICE(INTEL, 0x22b8), },
0127     { 0, }
0128 };
0129 MODULE_DEVICE_TABLE(pci, isp_id_table);
0130 
0131 static struct pci_driver isp_pci_driver = {
0132     .name = "intel_atomisp2_pm",
0133     .id_table = isp_id_table,
0134     .probe = isp_probe,
0135     .remove = isp_remove,
0136     .driver.pm = &isp_pm_ops,
0137 };
0138 
0139 module_pci_driver(isp_pci_driver);
0140 
0141 MODULE_DESCRIPTION("Intel AtomISP2 dummy / power-management drv (for suspend)");
0142 MODULE_AUTHOR("Hans de Goede <hdegoede@redhat.com>");
0143 MODULE_LICENSE("GPL v2");