162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Intel MID Power Management Unit (PWRMU) device driver 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (C) 2016, Intel Corporation 662306a36Sopenharmony_ci * 762306a36Sopenharmony_ci * Author: Andy Shevchenko <andriy.shevchenko@linux.intel.com> 862306a36Sopenharmony_ci * 962306a36Sopenharmony_ci * Intel MID Power Management Unit device driver handles the South Complex PCI 1062306a36Sopenharmony_ci * devices such as GPDMA, SPI, I2C, PWM, and so on. By default PCI core 1162306a36Sopenharmony_ci * modifies bits in PMCSR register in the PCI configuration space. This is not 1262306a36Sopenharmony_ci * enough on some SoCs like Intel Tangier. In such case PCI core sets a new 1362306a36Sopenharmony_ci * power state of the device in question through a PM hook registered in struct 1462306a36Sopenharmony_ci * pci_platform_pm_ops (see drivers/pci/pci-mid.c). 1562306a36Sopenharmony_ci */ 1662306a36Sopenharmony_ci 1762306a36Sopenharmony_ci#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt 1862306a36Sopenharmony_ci 1962306a36Sopenharmony_ci#include <linux/delay.h> 2062306a36Sopenharmony_ci#include <linux/errno.h> 2162306a36Sopenharmony_ci#include <linux/interrupt.h> 2262306a36Sopenharmony_ci#include <linux/kernel.h> 2362306a36Sopenharmony_ci#include <linux/export.h> 2462306a36Sopenharmony_ci#include <linux/mutex.h> 2562306a36Sopenharmony_ci#include <linux/pci.h> 2662306a36Sopenharmony_ci 2762306a36Sopenharmony_ci#include <asm/intel-mid.h> 2862306a36Sopenharmony_ci 2962306a36Sopenharmony_ci/* Registers */ 3062306a36Sopenharmony_ci#define PM_STS 0x00 3162306a36Sopenharmony_ci#define PM_CMD 0x04 3262306a36Sopenharmony_ci#define PM_ICS 0x08 3362306a36Sopenharmony_ci#define PM_WKC(x) (0x10 + (x) * 4) 3462306a36Sopenharmony_ci#define PM_WKS(x) (0x18 + (x) * 4) 3562306a36Sopenharmony_ci#define PM_SSC(x) (0x20 + (x) * 4) 3662306a36Sopenharmony_ci#define PM_SSS(x) (0x30 + (x) * 4) 3762306a36Sopenharmony_ci 3862306a36Sopenharmony_ci/* Bits in PM_STS */ 3962306a36Sopenharmony_ci#define PM_STS_BUSY (1 << 8) 4062306a36Sopenharmony_ci 4162306a36Sopenharmony_ci/* Bits in PM_CMD */ 4262306a36Sopenharmony_ci#define PM_CMD_CMD(x) ((x) << 0) 4362306a36Sopenharmony_ci#define PM_CMD_IOC (1 << 8) 4462306a36Sopenharmony_ci#define PM_CMD_CM_NOP (0 << 9) 4562306a36Sopenharmony_ci#define PM_CMD_CM_IMMEDIATE (1 << 9) 4662306a36Sopenharmony_ci#define PM_CMD_CM_DELAY (2 << 9) 4762306a36Sopenharmony_ci#define PM_CMD_CM_TRIGGER (3 << 9) 4862306a36Sopenharmony_ci 4962306a36Sopenharmony_ci/* System states */ 5062306a36Sopenharmony_ci#define PM_CMD_SYS_STATE_S5 (5 << 16) 5162306a36Sopenharmony_ci 5262306a36Sopenharmony_ci/* Trigger variants */ 5362306a36Sopenharmony_ci#define PM_CMD_CFG_TRIGGER_NC (3 << 19) 5462306a36Sopenharmony_ci 5562306a36Sopenharmony_ci/* Message to wait for TRIGGER_NC case */ 5662306a36Sopenharmony_ci#define TRIGGER_NC_MSG_2 (2 << 22) 5762306a36Sopenharmony_ci 5862306a36Sopenharmony_ci/* List of commands */ 5962306a36Sopenharmony_ci#define CMD_SET_CFG 0x01 6062306a36Sopenharmony_ci 6162306a36Sopenharmony_ci/* Bits in PM_ICS */ 6262306a36Sopenharmony_ci#define PM_ICS_INT_STATUS(x) ((x) & 0xff) 6362306a36Sopenharmony_ci#define PM_ICS_IE (1 << 8) 6462306a36Sopenharmony_ci#define PM_ICS_IP (1 << 9) 6562306a36Sopenharmony_ci#define PM_ICS_SW_INT_STS (1 << 10) 6662306a36Sopenharmony_ci 6762306a36Sopenharmony_ci/* List of interrupts */ 6862306a36Sopenharmony_ci#define INT_INVALID 0 6962306a36Sopenharmony_ci#define INT_CMD_COMPLETE 1 7062306a36Sopenharmony_ci#define INT_CMD_ERR 2 7162306a36Sopenharmony_ci#define INT_WAKE_EVENT 3 7262306a36Sopenharmony_ci#define INT_LSS_POWER_ERR 4 7362306a36Sopenharmony_ci#define INT_S0iX_MSG_ERR 5 7462306a36Sopenharmony_ci#define INT_NO_C6 6 7562306a36Sopenharmony_ci#define INT_TRIGGER_ERR 7 7662306a36Sopenharmony_ci#define INT_INACTIVITY 8 7762306a36Sopenharmony_ci 7862306a36Sopenharmony_ci/* South Complex devices */ 7962306a36Sopenharmony_ci#define LSS_MAX_SHARED_DEVS 4 8062306a36Sopenharmony_ci#define LSS_MAX_DEVS 64 8162306a36Sopenharmony_ci 8262306a36Sopenharmony_ci#define LSS_WS_BITS 1 /* wake state width */ 8362306a36Sopenharmony_ci#define LSS_PWS_BITS 2 /* power state width */ 8462306a36Sopenharmony_ci 8562306a36Sopenharmony_ci/* Supported device IDs */ 8662306a36Sopenharmony_ci#define PCI_DEVICE_ID_PENWELL 0x0828 8762306a36Sopenharmony_ci#define PCI_DEVICE_ID_TANGIER 0x11a1 8862306a36Sopenharmony_ci 8962306a36Sopenharmony_cistruct mid_pwr_dev { 9062306a36Sopenharmony_ci struct pci_dev *pdev; 9162306a36Sopenharmony_ci pci_power_t state; 9262306a36Sopenharmony_ci}; 9362306a36Sopenharmony_ci 9462306a36Sopenharmony_cistruct mid_pwr { 9562306a36Sopenharmony_ci struct device *dev; 9662306a36Sopenharmony_ci void __iomem *regs; 9762306a36Sopenharmony_ci int irq; 9862306a36Sopenharmony_ci bool available; 9962306a36Sopenharmony_ci 10062306a36Sopenharmony_ci struct mutex lock; 10162306a36Sopenharmony_ci struct mid_pwr_dev lss[LSS_MAX_DEVS][LSS_MAX_SHARED_DEVS]; 10262306a36Sopenharmony_ci}; 10362306a36Sopenharmony_ci 10462306a36Sopenharmony_cistatic struct mid_pwr *midpwr; 10562306a36Sopenharmony_ci 10662306a36Sopenharmony_cistatic u32 mid_pwr_get_state(struct mid_pwr *pwr, int reg) 10762306a36Sopenharmony_ci{ 10862306a36Sopenharmony_ci return readl(pwr->regs + PM_SSS(reg)); 10962306a36Sopenharmony_ci} 11062306a36Sopenharmony_ci 11162306a36Sopenharmony_cistatic void mid_pwr_set_state(struct mid_pwr *pwr, int reg, u32 value) 11262306a36Sopenharmony_ci{ 11362306a36Sopenharmony_ci writel(value, pwr->regs + PM_SSC(reg)); 11462306a36Sopenharmony_ci} 11562306a36Sopenharmony_ci 11662306a36Sopenharmony_cistatic void mid_pwr_set_wake(struct mid_pwr *pwr, int reg, u32 value) 11762306a36Sopenharmony_ci{ 11862306a36Sopenharmony_ci writel(value, pwr->regs + PM_WKC(reg)); 11962306a36Sopenharmony_ci} 12062306a36Sopenharmony_ci 12162306a36Sopenharmony_cistatic void mid_pwr_interrupt_disable(struct mid_pwr *pwr) 12262306a36Sopenharmony_ci{ 12362306a36Sopenharmony_ci writel(~PM_ICS_IE, pwr->regs + PM_ICS); 12462306a36Sopenharmony_ci} 12562306a36Sopenharmony_ci 12662306a36Sopenharmony_cistatic bool mid_pwr_is_busy(struct mid_pwr *pwr) 12762306a36Sopenharmony_ci{ 12862306a36Sopenharmony_ci return !!(readl(pwr->regs + PM_STS) & PM_STS_BUSY); 12962306a36Sopenharmony_ci} 13062306a36Sopenharmony_ci 13162306a36Sopenharmony_ci/* Wait 500ms that the latest PWRMU command finished */ 13262306a36Sopenharmony_cistatic int mid_pwr_wait(struct mid_pwr *pwr) 13362306a36Sopenharmony_ci{ 13462306a36Sopenharmony_ci unsigned int count = 500000; 13562306a36Sopenharmony_ci bool busy; 13662306a36Sopenharmony_ci 13762306a36Sopenharmony_ci do { 13862306a36Sopenharmony_ci busy = mid_pwr_is_busy(pwr); 13962306a36Sopenharmony_ci if (!busy) 14062306a36Sopenharmony_ci return 0; 14162306a36Sopenharmony_ci udelay(1); 14262306a36Sopenharmony_ci } while (--count); 14362306a36Sopenharmony_ci 14462306a36Sopenharmony_ci return -EBUSY; 14562306a36Sopenharmony_ci} 14662306a36Sopenharmony_ci 14762306a36Sopenharmony_cistatic int mid_pwr_wait_for_cmd(struct mid_pwr *pwr, u8 cmd) 14862306a36Sopenharmony_ci{ 14962306a36Sopenharmony_ci writel(PM_CMD_CMD(cmd) | PM_CMD_CM_IMMEDIATE, pwr->regs + PM_CMD); 15062306a36Sopenharmony_ci return mid_pwr_wait(pwr); 15162306a36Sopenharmony_ci} 15262306a36Sopenharmony_ci 15362306a36Sopenharmony_cistatic int __update_power_state(struct mid_pwr *pwr, int reg, int bit, int new) 15462306a36Sopenharmony_ci{ 15562306a36Sopenharmony_ci int curstate; 15662306a36Sopenharmony_ci u32 power; 15762306a36Sopenharmony_ci int ret; 15862306a36Sopenharmony_ci 15962306a36Sopenharmony_ci /* Check if the device is already in desired state */ 16062306a36Sopenharmony_ci power = mid_pwr_get_state(pwr, reg); 16162306a36Sopenharmony_ci curstate = (power >> bit) & 3; 16262306a36Sopenharmony_ci if (curstate == new) 16362306a36Sopenharmony_ci return 0; 16462306a36Sopenharmony_ci 16562306a36Sopenharmony_ci /* Update the power state */ 16662306a36Sopenharmony_ci mid_pwr_set_state(pwr, reg, (power & ~(3 << bit)) | (new << bit)); 16762306a36Sopenharmony_ci 16862306a36Sopenharmony_ci /* Send command to SCU */ 16962306a36Sopenharmony_ci ret = mid_pwr_wait_for_cmd(pwr, CMD_SET_CFG); 17062306a36Sopenharmony_ci if (ret) 17162306a36Sopenharmony_ci return ret; 17262306a36Sopenharmony_ci 17362306a36Sopenharmony_ci /* Check if the device is already in desired state */ 17462306a36Sopenharmony_ci power = mid_pwr_get_state(pwr, reg); 17562306a36Sopenharmony_ci curstate = (power >> bit) & 3; 17662306a36Sopenharmony_ci if (curstate != new) 17762306a36Sopenharmony_ci return -EAGAIN; 17862306a36Sopenharmony_ci 17962306a36Sopenharmony_ci return 0; 18062306a36Sopenharmony_ci} 18162306a36Sopenharmony_ci 18262306a36Sopenharmony_cistatic pci_power_t __find_weakest_power_state(struct mid_pwr_dev *lss, 18362306a36Sopenharmony_ci struct pci_dev *pdev, 18462306a36Sopenharmony_ci pci_power_t state) 18562306a36Sopenharmony_ci{ 18662306a36Sopenharmony_ci pci_power_t weakest = PCI_D3hot; 18762306a36Sopenharmony_ci unsigned int j; 18862306a36Sopenharmony_ci 18962306a36Sopenharmony_ci /* Find device in cache or first free cell */ 19062306a36Sopenharmony_ci for (j = 0; j < LSS_MAX_SHARED_DEVS; j++) { 19162306a36Sopenharmony_ci if (lss[j].pdev == pdev || !lss[j].pdev) 19262306a36Sopenharmony_ci break; 19362306a36Sopenharmony_ci } 19462306a36Sopenharmony_ci 19562306a36Sopenharmony_ci /* Store the desired state in cache */ 19662306a36Sopenharmony_ci if (j < LSS_MAX_SHARED_DEVS) { 19762306a36Sopenharmony_ci lss[j].pdev = pdev; 19862306a36Sopenharmony_ci lss[j].state = state; 19962306a36Sopenharmony_ci } else { 20062306a36Sopenharmony_ci dev_WARN(&pdev->dev, "No room for device in PWRMU LSS cache\n"); 20162306a36Sopenharmony_ci weakest = state; 20262306a36Sopenharmony_ci } 20362306a36Sopenharmony_ci 20462306a36Sopenharmony_ci /* Find the power state we may use */ 20562306a36Sopenharmony_ci for (j = 0; j < LSS_MAX_SHARED_DEVS; j++) { 20662306a36Sopenharmony_ci if (lss[j].state < weakest) 20762306a36Sopenharmony_ci weakest = lss[j].state; 20862306a36Sopenharmony_ci } 20962306a36Sopenharmony_ci 21062306a36Sopenharmony_ci return weakest; 21162306a36Sopenharmony_ci} 21262306a36Sopenharmony_ci 21362306a36Sopenharmony_cistatic int __set_power_state(struct mid_pwr *pwr, struct pci_dev *pdev, 21462306a36Sopenharmony_ci pci_power_t state, int id, int reg, int bit) 21562306a36Sopenharmony_ci{ 21662306a36Sopenharmony_ci const char *name; 21762306a36Sopenharmony_ci int ret; 21862306a36Sopenharmony_ci 21962306a36Sopenharmony_ci state = __find_weakest_power_state(pwr->lss[id], pdev, state); 22062306a36Sopenharmony_ci name = pci_power_name(state); 22162306a36Sopenharmony_ci 22262306a36Sopenharmony_ci ret = __update_power_state(pwr, reg, bit, (__force int)state); 22362306a36Sopenharmony_ci if (ret) { 22462306a36Sopenharmony_ci dev_warn(&pdev->dev, "Can't set power state %s: %d\n", name, ret); 22562306a36Sopenharmony_ci return ret; 22662306a36Sopenharmony_ci } 22762306a36Sopenharmony_ci 22862306a36Sopenharmony_ci dev_vdbg(&pdev->dev, "Set power state %s\n", name); 22962306a36Sopenharmony_ci return 0; 23062306a36Sopenharmony_ci} 23162306a36Sopenharmony_ci 23262306a36Sopenharmony_cistatic int mid_pwr_set_power_state(struct mid_pwr *pwr, struct pci_dev *pdev, 23362306a36Sopenharmony_ci pci_power_t state) 23462306a36Sopenharmony_ci{ 23562306a36Sopenharmony_ci int id, reg, bit; 23662306a36Sopenharmony_ci int ret; 23762306a36Sopenharmony_ci 23862306a36Sopenharmony_ci id = intel_mid_pwr_get_lss_id(pdev); 23962306a36Sopenharmony_ci if (id < 0) 24062306a36Sopenharmony_ci return id; 24162306a36Sopenharmony_ci 24262306a36Sopenharmony_ci reg = (id * LSS_PWS_BITS) / 32; 24362306a36Sopenharmony_ci bit = (id * LSS_PWS_BITS) % 32; 24462306a36Sopenharmony_ci 24562306a36Sopenharmony_ci /* We support states between PCI_D0 and PCI_D3hot */ 24662306a36Sopenharmony_ci if (state < PCI_D0) 24762306a36Sopenharmony_ci state = PCI_D0; 24862306a36Sopenharmony_ci if (state > PCI_D3hot) 24962306a36Sopenharmony_ci state = PCI_D3hot; 25062306a36Sopenharmony_ci 25162306a36Sopenharmony_ci mutex_lock(&pwr->lock); 25262306a36Sopenharmony_ci ret = __set_power_state(pwr, pdev, state, id, reg, bit); 25362306a36Sopenharmony_ci mutex_unlock(&pwr->lock); 25462306a36Sopenharmony_ci return ret; 25562306a36Sopenharmony_ci} 25662306a36Sopenharmony_ci 25762306a36Sopenharmony_ciint intel_mid_pci_set_power_state(struct pci_dev *pdev, pci_power_t state) 25862306a36Sopenharmony_ci{ 25962306a36Sopenharmony_ci struct mid_pwr *pwr = midpwr; 26062306a36Sopenharmony_ci int ret = 0; 26162306a36Sopenharmony_ci 26262306a36Sopenharmony_ci might_sleep(); 26362306a36Sopenharmony_ci 26462306a36Sopenharmony_ci if (pwr && pwr->available) 26562306a36Sopenharmony_ci ret = mid_pwr_set_power_state(pwr, pdev, state); 26662306a36Sopenharmony_ci dev_vdbg(&pdev->dev, "set_power_state() returns %d\n", ret); 26762306a36Sopenharmony_ci 26862306a36Sopenharmony_ci return 0; 26962306a36Sopenharmony_ci} 27062306a36Sopenharmony_ci 27162306a36Sopenharmony_cipci_power_t intel_mid_pci_get_power_state(struct pci_dev *pdev) 27262306a36Sopenharmony_ci{ 27362306a36Sopenharmony_ci struct mid_pwr *pwr = midpwr; 27462306a36Sopenharmony_ci int id, reg, bit; 27562306a36Sopenharmony_ci u32 power; 27662306a36Sopenharmony_ci 27762306a36Sopenharmony_ci if (!pwr || !pwr->available) 27862306a36Sopenharmony_ci return PCI_UNKNOWN; 27962306a36Sopenharmony_ci 28062306a36Sopenharmony_ci id = intel_mid_pwr_get_lss_id(pdev); 28162306a36Sopenharmony_ci if (id < 0) 28262306a36Sopenharmony_ci return PCI_UNKNOWN; 28362306a36Sopenharmony_ci 28462306a36Sopenharmony_ci reg = (id * LSS_PWS_BITS) / 32; 28562306a36Sopenharmony_ci bit = (id * LSS_PWS_BITS) % 32; 28662306a36Sopenharmony_ci power = mid_pwr_get_state(pwr, reg); 28762306a36Sopenharmony_ci return (__force pci_power_t)((power >> bit) & 3); 28862306a36Sopenharmony_ci} 28962306a36Sopenharmony_ci 29062306a36Sopenharmony_civoid intel_mid_pwr_power_off(void) 29162306a36Sopenharmony_ci{ 29262306a36Sopenharmony_ci struct mid_pwr *pwr = midpwr; 29362306a36Sopenharmony_ci u32 cmd = PM_CMD_SYS_STATE_S5 | 29462306a36Sopenharmony_ci PM_CMD_CMD(CMD_SET_CFG) | 29562306a36Sopenharmony_ci PM_CMD_CM_TRIGGER | 29662306a36Sopenharmony_ci PM_CMD_CFG_TRIGGER_NC | 29762306a36Sopenharmony_ci TRIGGER_NC_MSG_2; 29862306a36Sopenharmony_ci 29962306a36Sopenharmony_ci /* Send command to SCU */ 30062306a36Sopenharmony_ci writel(cmd, pwr->regs + PM_CMD); 30162306a36Sopenharmony_ci mid_pwr_wait(pwr); 30262306a36Sopenharmony_ci} 30362306a36Sopenharmony_ci 30462306a36Sopenharmony_ciint intel_mid_pwr_get_lss_id(struct pci_dev *pdev) 30562306a36Sopenharmony_ci{ 30662306a36Sopenharmony_ci int vndr; 30762306a36Sopenharmony_ci u8 id; 30862306a36Sopenharmony_ci 30962306a36Sopenharmony_ci /* 31062306a36Sopenharmony_ci * Mapping to PWRMU index is kept in the Logical SubSystem ID byte of 31162306a36Sopenharmony_ci * Vendor capability. 31262306a36Sopenharmony_ci */ 31362306a36Sopenharmony_ci vndr = pci_find_capability(pdev, PCI_CAP_ID_VNDR); 31462306a36Sopenharmony_ci if (!vndr) 31562306a36Sopenharmony_ci return -EINVAL; 31662306a36Sopenharmony_ci 31762306a36Sopenharmony_ci /* Read the Logical SubSystem ID byte */ 31862306a36Sopenharmony_ci pci_read_config_byte(pdev, vndr + INTEL_MID_PWR_LSS_OFFSET, &id); 31962306a36Sopenharmony_ci if (!(id & INTEL_MID_PWR_LSS_TYPE)) 32062306a36Sopenharmony_ci return -ENODEV; 32162306a36Sopenharmony_ci 32262306a36Sopenharmony_ci id &= ~INTEL_MID_PWR_LSS_TYPE; 32362306a36Sopenharmony_ci if (id >= LSS_MAX_DEVS) 32462306a36Sopenharmony_ci return -ERANGE; 32562306a36Sopenharmony_ci 32662306a36Sopenharmony_ci return id; 32762306a36Sopenharmony_ci} 32862306a36Sopenharmony_ci 32962306a36Sopenharmony_cistatic irqreturn_t mid_pwr_irq_handler(int irq, void *dev_id) 33062306a36Sopenharmony_ci{ 33162306a36Sopenharmony_ci struct mid_pwr *pwr = dev_id; 33262306a36Sopenharmony_ci u32 ics; 33362306a36Sopenharmony_ci 33462306a36Sopenharmony_ci ics = readl(pwr->regs + PM_ICS); 33562306a36Sopenharmony_ci if (!(ics & PM_ICS_IP)) 33662306a36Sopenharmony_ci return IRQ_NONE; 33762306a36Sopenharmony_ci 33862306a36Sopenharmony_ci writel(ics | PM_ICS_IP, pwr->regs + PM_ICS); 33962306a36Sopenharmony_ci 34062306a36Sopenharmony_ci dev_warn(pwr->dev, "Unexpected IRQ: %#x\n", PM_ICS_INT_STATUS(ics)); 34162306a36Sopenharmony_ci return IRQ_HANDLED; 34262306a36Sopenharmony_ci} 34362306a36Sopenharmony_ci 34462306a36Sopenharmony_cistruct mid_pwr_device_info { 34562306a36Sopenharmony_ci int (*set_initial_state)(struct mid_pwr *pwr); 34662306a36Sopenharmony_ci}; 34762306a36Sopenharmony_ci 34862306a36Sopenharmony_cistatic int mid_pwr_probe(struct pci_dev *pdev, const struct pci_device_id *id) 34962306a36Sopenharmony_ci{ 35062306a36Sopenharmony_ci struct mid_pwr_device_info *info = (void *)id->driver_data; 35162306a36Sopenharmony_ci struct device *dev = &pdev->dev; 35262306a36Sopenharmony_ci struct mid_pwr *pwr; 35362306a36Sopenharmony_ci int ret; 35462306a36Sopenharmony_ci 35562306a36Sopenharmony_ci ret = pcim_enable_device(pdev); 35662306a36Sopenharmony_ci if (ret < 0) { 35762306a36Sopenharmony_ci dev_err(&pdev->dev, "error: could not enable device\n"); 35862306a36Sopenharmony_ci return ret; 35962306a36Sopenharmony_ci } 36062306a36Sopenharmony_ci 36162306a36Sopenharmony_ci ret = pcim_iomap_regions(pdev, 1 << 0, pci_name(pdev)); 36262306a36Sopenharmony_ci if (ret) { 36362306a36Sopenharmony_ci dev_err(&pdev->dev, "I/O memory remapping failed\n"); 36462306a36Sopenharmony_ci return ret; 36562306a36Sopenharmony_ci } 36662306a36Sopenharmony_ci 36762306a36Sopenharmony_ci pwr = devm_kzalloc(dev, sizeof(*pwr), GFP_KERNEL); 36862306a36Sopenharmony_ci if (!pwr) 36962306a36Sopenharmony_ci return -ENOMEM; 37062306a36Sopenharmony_ci 37162306a36Sopenharmony_ci pwr->dev = dev; 37262306a36Sopenharmony_ci pwr->regs = pcim_iomap_table(pdev)[0]; 37362306a36Sopenharmony_ci pwr->irq = pdev->irq; 37462306a36Sopenharmony_ci 37562306a36Sopenharmony_ci mutex_init(&pwr->lock); 37662306a36Sopenharmony_ci 37762306a36Sopenharmony_ci /* Disable interrupts */ 37862306a36Sopenharmony_ci mid_pwr_interrupt_disable(pwr); 37962306a36Sopenharmony_ci 38062306a36Sopenharmony_ci if (info && info->set_initial_state) { 38162306a36Sopenharmony_ci ret = info->set_initial_state(pwr); 38262306a36Sopenharmony_ci if (ret) 38362306a36Sopenharmony_ci dev_warn(dev, "Can't set initial state: %d\n", ret); 38462306a36Sopenharmony_ci } 38562306a36Sopenharmony_ci 38662306a36Sopenharmony_ci ret = devm_request_irq(dev, pdev->irq, mid_pwr_irq_handler, 38762306a36Sopenharmony_ci IRQF_NO_SUSPEND, pci_name(pdev), pwr); 38862306a36Sopenharmony_ci if (ret) 38962306a36Sopenharmony_ci return ret; 39062306a36Sopenharmony_ci 39162306a36Sopenharmony_ci pwr->available = true; 39262306a36Sopenharmony_ci midpwr = pwr; 39362306a36Sopenharmony_ci 39462306a36Sopenharmony_ci pci_set_drvdata(pdev, pwr); 39562306a36Sopenharmony_ci return 0; 39662306a36Sopenharmony_ci} 39762306a36Sopenharmony_ci 39862306a36Sopenharmony_cistatic int mid_set_initial_state(struct mid_pwr *pwr, const u32 *states) 39962306a36Sopenharmony_ci{ 40062306a36Sopenharmony_ci unsigned int i, j; 40162306a36Sopenharmony_ci int ret; 40262306a36Sopenharmony_ci 40362306a36Sopenharmony_ci /* 40462306a36Sopenharmony_ci * Enable wake events. 40562306a36Sopenharmony_ci * 40662306a36Sopenharmony_ci * PWRMU supports up to 32 sources for wake up the system. Ungate them 40762306a36Sopenharmony_ci * all here. 40862306a36Sopenharmony_ci */ 40962306a36Sopenharmony_ci mid_pwr_set_wake(pwr, 0, 0xffffffff); 41062306a36Sopenharmony_ci mid_pwr_set_wake(pwr, 1, 0xffffffff); 41162306a36Sopenharmony_ci 41262306a36Sopenharmony_ci /* 41362306a36Sopenharmony_ci * Power off South Complex devices. 41462306a36Sopenharmony_ci * 41562306a36Sopenharmony_ci * There is a map (see a note below) of 64 devices with 2 bits per each 41662306a36Sopenharmony_ci * on 32-bit HW registers. The following calls set all devices to one 41762306a36Sopenharmony_ci * known initial state, i.e. PCI_D3hot. This is done in conjunction 41862306a36Sopenharmony_ci * with PMCSR setting in arch/x86/pci/intel_mid_pci.c. 41962306a36Sopenharmony_ci * 42062306a36Sopenharmony_ci * NOTE: The actual device mapping is provided by a platform at run 42162306a36Sopenharmony_ci * time using vendor capability of PCI configuration space. 42262306a36Sopenharmony_ci */ 42362306a36Sopenharmony_ci mid_pwr_set_state(pwr, 0, states[0]); 42462306a36Sopenharmony_ci mid_pwr_set_state(pwr, 1, states[1]); 42562306a36Sopenharmony_ci mid_pwr_set_state(pwr, 2, states[2]); 42662306a36Sopenharmony_ci mid_pwr_set_state(pwr, 3, states[3]); 42762306a36Sopenharmony_ci 42862306a36Sopenharmony_ci /* Send command to SCU */ 42962306a36Sopenharmony_ci ret = mid_pwr_wait_for_cmd(pwr, CMD_SET_CFG); 43062306a36Sopenharmony_ci if (ret) 43162306a36Sopenharmony_ci return ret; 43262306a36Sopenharmony_ci 43362306a36Sopenharmony_ci for (i = 0; i < LSS_MAX_DEVS; i++) { 43462306a36Sopenharmony_ci for (j = 0; j < LSS_MAX_SHARED_DEVS; j++) 43562306a36Sopenharmony_ci pwr->lss[i][j].state = PCI_D3hot; 43662306a36Sopenharmony_ci } 43762306a36Sopenharmony_ci 43862306a36Sopenharmony_ci return 0; 43962306a36Sopenharmony_ci} 44062306a36Sopenharmony_ci 44162306a36Sopenharmony_cistatic int pnw_set_initial_state(struct mid_pwr *pwr) 44262306a36Sopenharmony_ci{ 44362306a36Sopenharmony_ci /* On Penwell SRAM must stay powered on */ 44462306a36Sopenharmony_ci static const u32 states[] = { 44562306a36Sopenharmony_ci 0xf00fffff, /* PM_SSC(0) */ 44662306a36Sopenharmony_ci 0xffffffff, /* PM_SSC(1) */ 44762306a36Sopenharmony_ci 0xffffffff, /* PM_SSC(2) */ 44862306a36Sopenharmony_ci 0xffffffff, /* PM_SSC(3) */ 44962306a36Sopenharmony_ci }; 45062306a36Sopenharmony_ci return mid_set_initial_state(pwr, states); 45162306a36Sopenharmony_ci} 45262306a36Sopenharmony_ci 45362306a36Sopenharmony_cistatic int tng_set_initial_state(struct mid_pwr *pwr) 45462306a36Sopenharmony_ci{ 45562306a36Sopenharmony_ci static const u32 states[] = { 45662306a36Sopenharmony_ci 0xffffffff, /* PM_SSC(0) */ 45762306a36Sopenharmony_ci 0xffffffff, /* PM_SSC(1) */ 45862306a36Sopenharmony_ci 0xffffffff, /* PM_SSC(2) */ 45962306a36Sopenharmony_ci 0xffffffff, /* PM_SSC(3) */ 46062306a36Sopenharmony_ci }; 46162306a36Sopenharmony_ci return mid_set_initial_state(pwr, states); 46262306a36Sopenharmony_ci} 46362306a36Sopenharmony_ci 46462306a36Sopenharmony_cistatic const struct mid_pwr_device_info pnw_info = { 46562306a36Sopenharmony_ci .set_initial_state = pnw_set_initial_state, 46662306a36Sopenharmony_ci}; 46762306a36Sopenharmony_ci 46862306a36Sopenharmony_cistatic const struct mid_pwr_device_info tng_info = { 46962306a36Sopenharmony_ci .set_initial_state = tng_set_initial_state, 47062306a36Sopenharmony_ci}; 47162306a36Sopenharmony_ci 47262306a36Sopenharmony_ci/* This table should be in sync with the one in drivers/pci/pci-mid.c */ 47362306a36Sopenharmony_cistatic const struct pci_device_id mid_pwr_pci_ids[] = { 47462306a36Sopenharmony_ci { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_PENWELL), (kernel_ulong_t)&pnw_info }, 47562306a36Sopenharmony_ci { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_TANGIER), (kernel_ulong_t)&tng_info }, 47662306a36Sopenharmony_ci {} 47762306a36Sopenharmony_ci}; 47862306a36Sopenharmony_ci 47962306a36Sopenharmony_cistatic struct pci_driver mid_pwr_pci_driver = { 48062306a36Sopenharmony_ci .name = "intel_mid_pwr", 48162306a36Sopenharmony_ci .probe = mid_pwr_probe, 48262306a36Sopenharmony_ci .id_table = mid_pwr_pci_ids, 48362306a36Sopenharmony_ci}; 48462306a36Sopenharmony_ci 48562306a36Sopenharmony_cibuiltin_pci_driver(mid_pwr_pci_driver); 486