162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Driver for the Intel Broxton PMC 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * (C) Copyright 2014 - 2020 Intel Corporation 662306a36Sopenharmony_ci * 762306a36Sopenharmony_ci * This driver is based on Intel SCU IPC driver (intel_scu_ipc.c) by 862306a36Sopenharmony_ci * Sreedhara DS <sreedhara.ds@intel.com> 962306a36Sopenharmony_ci * 1062306a36Sopenharmony_ci * The PMC (Power Management Controller) running on the ARC processor 1162306a36Sopenharmony_ci * communicates with another entity running in the IA (Intel Architecture) 1262306a36Sopenharmony_ci * core through an IPC (Intel Processor Communications) mechanism which in 1362306a36Sopenharmony_ci * turn sends messages between the IA and the PMC. 1462306a36Sopenharmony_ci */ 1562306a36Sopenharmony_ci 1662306a36Sopenharmony_ci#include <linux/acpi.h> 1762306a36Sopenharmony_ci#include <linux/delay.h> 1862306a36Sopenharmony_ci#include <linux/errno.h> 1962306a36Sopenharmony_ci#include <linux/interrupt.h> 2062306a36Sopenharmony_ci#include <linux/io-64-nonatomic-lo-hi.h> 2162306a36Sopenharmony_ci#include <linux/mfd/core.h> 2262306a36Sopenharmony_ci#include <linux/mfd/intel_pmc_bxt.h> 2362306a36Sopenharmony_ci#include <linux/module.h> 2462306a36Sopenharmony_ci#include <linux/platform_device.h> 2562306a36Sopenharmony_ci#include <linux/platform_data/itco_wdt.h> 2662306a36Sopenharmony_ci 2762306a36Sopenharmony_ci#include <asm/intel_scu_ipc.h> 2862306a36Sopenharmony_ci 2962306a36Sopenharmony_ci/* Residency with clock rate at 19.2MHz to usecs */ 3062306a36Sopenharmony_ci#define S0IX_RESIDENCY_IN_USECS(d, s) \ 3162306a36Sopenharmony_ci({ \ 3262306a36Sopenharmony_ci u64 result = 10ull * ((d) + (s)); \ 3362306a36Sopenharmony_ci do_div(result, 192); \ 3462306a36Sopenharmony_ci result; \ 3562306a36Sopenharmony_ci}) 3662306a36Sopenharmony_ci 3762306a36Sopenharmony_ci/* Resources exported from IFWI */ 3862306a36Sopenharmony_ci#define PLAT_RESOURCE_IPC_INDEX 0 3962306a36Sopenharmony_ci#define PLAT_RESOURCE_IPC_SIZE 0x1000 4062306a36Sopenharmony_ci#define PLAT_RESOURCE_GCR_OFFSET 0x1000 4162306a36Sopenharmony_ci#define PLAT_RESOURCE_GCR_SIZE 0x1000 4262306a36Sopenharmony_ci#define PLAT_RESOURCE_BIOS_DATA_INDEX 1 4362306a36Sopenharmony_ci#define PLAT_RESOURCE_BIOS_IFACE_INDEX 2 4462306a36Sopenharmony_ci#define PLAT_RESOURCE_TELEM_SSRAM_INDEX 3 4562306a36Sopenharmony_ci#define PLAT_RESOURCE_ISP_DATA_INDEX 4 4662306a36Sopenharmony_ci#define PLAT_RESOURCE_ISP_IFACE_INDEX 5 4762306a36Sopenharmony_ci#define PLAT_RESOURCE_GTD_DATA_INDEX 6 4862306a36Sopenharmony_ci#define PLAT_RESOURCE_GTD_IFACE_INDEX 7 4962306a36Sopenharmony_ci#define PLAT_RESOURCE_ACPI_IO_INDEX 0 5062306a36Sopenharmony_ci 5162306a36Sopenharmony_ci/* 5262306a36Sopenharmony_ci * BIOS does not create an ACPI device for each PMC function, but 5362306a36Sopenharmony_ci * exports multiple resources from one ACPI device (IPC) for multiple 5462306a36Sopenharmony_ci * functions. This driver is responsible for creating a child device and 5562306a36Sopenharmony_ci * to export resources for those functions. 5662306a36Sopenharmony_ci */ 5762306a36Sopenharmony_ci#define SMI_EN_OFFSET 0x0040 5862306a36Sopenharmony_ci#define SMI_EN_SIZE 4 5962306a36Sopenharmony_ci#define TCO_BASE_OFFSET 0x0060 6062306a36Sopenharmony_ci#define TCO_REGS_SIZE 16 6162306a36Sopenharmony_ci#define TELEM_SSRAM_SIZE 240 6262306a36Sopenharmony_ci#define TELEM_PMC_SSRAM_OFFSET 0x1b00 6362306a36Sopenharmony_ci#define TELEM_PUNIT_SSRAM_OFFSET 0x1a00 6462306a36Sopenharmony_ci 6562306a36Sopenharmony_ci/* Commands */ 6662306a36Sopenharmony_ci#define PMC_NORTHPEAK_CTRL 0xed 6762306a36Sopenharmony_ci 6862306a36Sopenharmony_cistatic inline bool is_gcr_valid(u32 offset) 6962306a36Sopenharmony_ci{ 7062306a36Sopenharmony_ci return offset < PLAT_RESOURCE_GCR_SIZE - 8; 7162306a36Sopenharmony_ci} 7262306a36Sopenharmony_ci 7362306a36Sopenharmony_ci/** 7462306a36Sopenharmony_ci * intel_pmc_gcr_read64() - Read a 64-bit PMC GCR register 7562306a36Sopenharmony_ci * @pmc: PMC device pointer 7662306a36Sopenharmony_ci * @offset: offset of GCR register from GCR address base 7762306a36Sopenharmony_ci * @data: data pointer for storing the register output 7862306a36Sopenharmony_ci * 7962306a36Sopenharmony_ci * Reads the 64-bit PMC GCR register at given offset. 8062306a36Sopenharmony_ci * 8162306a36Sopenharmony_ci * Return: Negative value on error or 0 on success. 8262306a36Sopenharmony_ci */ 8362306a36Sopenharmony_ciint intel_pmc_gcr_read64(struct intel_pmc_dev *pmc, u32 offset, u64 *data) 8462306a36Sopenharmony_ci{ 8562306a36Sopenharmony_ci if (!is_gcr_valid(offset)) 8662306a36Sopenharmony_ci return -EINVAL; 8762306a36Sopenharmony_ci 8862306a36Sopenharmony_ci spin_lock(&pmc->gcr_lock); 8962306a36Sopenharmony_ci *data = readq(pmc->gcr_mem_base + offset); 9062306a36Sopenharmony_ci spin_unlock(&pmc->gcr_lock); 9162306a36Sopenharmony_ci 9262306a36Sopenharmony_ci return 0; 9362306a36Sopenharmony_ci} 9462306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(intel_pmc_gcr_read64); 9562306a36Sopenharmony_ci 9662306a36Sopenharmony_ci/** 9762306a36Sopenharmony_ci * intel_pmc_gcr_update() - Update PMC GCR register bits 9862306a36Sopenharmony_ci * @pmc: PMC device pointer 9962306a36Sopenharmony_ci * @offset: offset of GCR register from GCR address base 10062306a36Sopenharmony_ci * @mask: bit mask for update operation 10162306a36Sopenharmony_ci * @val: update value 10262306a36Sopenharmony_ci * 10362306a36Sopenharmony_ci * Updates the bits of given GCR register as specified by 10462306a36Sopenharmony_ci * @mask and @val. 10562306a36Sopenharmony_ci * 10662306a36Sopenharmony_ci * Return: Negative value on error or 0 on success. 10762306a36Sopenharmony_ci */ 10862306a36Sopenharmony_ciint intel_pmc_gcr_update(struct intel_pmc_dev *pmc, u32 offset, u32 mask, u32 val) 10962306a36Sopenharmony_ci{ 11062306a36Sopenharmony_ci u32 new_val; 11162306a36Sopenharmony_ci 11262306a36Sopenharmony_ci if (!is_gcr_valid(offset)) 11362306a36Sopenharmony_ci return -EINVAL; 11462306a36Sopenharmony_ci 11562306a36Sopenharmony_ci spin_lock(&pmc->gcr_lock); 11662306a36Sopenharmony_ci new_val = readl(pmc->gcr_mem_base + offset); 11762306a36Sopenharmony_ci 11862306a36Sopenharmony_ci new_val = (new_val & ~mask) | (val & mask); 11962306a36Sopenharmony_ci writel(new_val, pmc->gcr_mem_base + offset); 12062306a36Sopenharmony_ci 12162306a36Sopenharmony_ci new_val = readl(pmc->gcr_mem_base + offset); 12262306a36Sopenharmony_ci spin_unlock(&pmc->gcr_lock); 12362306a36Sopenharmony_ci 12462306a36Sopenharmony_ci /* Check whether the bit update is successful */ 12562306a36Sopenharmony_ci return (new_val & mask) != (val & mask) ? -EIO : 0; 12662306a36Sopenharmony_ci} 12762306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(intel_pmc_gcr_update); 12862306a36Sopenharmony_ci 12962306a36Sopenharmony_ci/** 13062306a36Sopenharmony_ci * intel_pmc_s0ix_counter_read() - Read S0ix residency 13162306a36Sopenharmony_ci * @pmc: PMC device pointer 13262306a36Sopenharmony_ci * @data: Out param that contains current S0ix residency count. 13362306a36Sopenharmony_ci * 13462306a36Sopenharmony_ci * Writes to @data how many usecs the system has been in low-power S0ix 13562306a36Sopenharmony_ci * state. 13662306a36Sopenharmony_ci * 13762306a36Sopenharmony_ci * Return: An error code or 0 on success. 13862306a36Sopenharmony_ci */ 13962306a36Sopenharmony_ciint intel_pmc_s0ix_counter_read(struct intel_pmc_dev *pmc, u64 *data) 14062306a36Sopenharmony_ci{ 14162306a36Sopenharmony_ci u64 deep, shlw; 14262306a36Sopenharmony_ci 14362306a36Sopenharmony_ci spin_lock(&pmc->gcr_lock); 14462306a36Sopenharmony_ci deep = readq(pmc->gcr_mem_base + PMC_GCR_TELEM_DEEP_S0IX_REG); 14562306a36Sopenharmony_ci shlw = readq(pmc->gcr_mem_base + PMC_GCR_TELEM_SHLW_S0IX_REG); 14662306a36Sopenharmony_ci spin_unlock(&pmc->gcr_lock); 14762306a36Sopenharmony_ci 14862306a36Sopenharmony_ci *data = S0IX_RESIDENCY_IN_USECS(deep, shlw); 14962306a36Sopenharmony_ci return 0; 15062306a36Sopenharmony_ci} 15162306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(intel_pmc_s0ix_counter_read); 15262306a36Sopenharmony_ci 15362306a36Sopenharmony_ci/** 15462306a36Sopenharmony_ci * simplecmd_store() - Send a simple IPC command 15562306a36Sopenharmony_ci * @dev: Device under the attribute is 15662306a36Sopenharmony_ci * @attr: Attribute in question 15762306a36Sopenharmony_ci * @buf: Buffer holding data to be stored to the attribute 15862306a36Sopenharmony_ci * @count: Number of bytes in @buf 15962306a36Sopenharmony_ci * 16062306a36Sopenharmony_ci * Expects a string with two integers separated with space. These two 16162306a36Sopenharmony_ci * values hold command and subcommand that is send to PMC. 16262306a36Sopenharmony_ci * 16362306a36Sopenharmony_ci * Return: Number number of bytes written (@count) or negative errno in 16462306a36Sopenharmony_ci * case of error. 16562306a36Sopenharmony_ci */ 16662306a36Sopenharmony_cistatic ssize_t simplecmd_store(struct device *dev, struct device_attribute *attr, 16762306a36Sopenharmony_ci const char *buf, size_t count) 16862306a36Sopenharmony_ci{ 16962306a36Sopenharmony_ci struct intel_pmc_dev *pmc = dev_get_drvdata(dev); 17062306a36Sopenharmony_ci struct intel_scu_ipc_dev *scu = pmc->scu; 17162306a36Sopenharmony_ci int subcmd; 17262306a36Sopenharmony_ci int cmd; 17362306a36Sopenharmony_ci int ret; 17462306a36Sopenharmony_ci 17562306a36Sopenharmony_ci ret = sscanf(buf, "%d %d", &cmd, &subcmd); 17662306a36Sopenharmony_ci if (ret != 2) { 17762306a36Sopenharmony_ci dev_err(dev, "Invalid values, expected: cmd subcmd\n"); 17862306a36Sopenharmony_ci return -EINVAL; 17962306a36Sopenharmony_ci } 18062306a36Sopenharmony_ci 18162306a36Sopenharmony_ci ret = intel_scu_ipc_dev_simple_command(scu, cmd, subcmd); 18262306a36Sopenharmony_ci if (ret) 18362306a36Sopenharmony_ci return ret; 18462306a36Sopenharmony_ci 18562306a36Sopenharmony_ci return count; 18662306a36Sopenharmony_ci} 18762306a36Sopenharmony_cistatic DEVICE_ATTR_WO(simplecmd); 18862306a36Sopenharmony_ci 18962306a36Sopenharmony_ci/** 19062306a36Sopenharmony_ci * northpeak_store() - Enable or disable Northpeak 19162306a36Sopenharmony_ci * @dev: Device under the attribute is 19262306a36Sopenharmony_ci * @attr: Attribute in question 19362306a36Sopenharmony_ci * @buf: Buffer holding data to be stored to the attribute 19462306a36Sopenharmony_ci * @count: Number of bytes in @buf 19562306a36Sopenharmony_ci * 19662306a36Sopenharmony_ci * Expects an unsigned integer. Non-zero enables Northpeak and zero 19762306a36Sopenharmony_ci * disables it. 19862306a36Sopenharmony_ci * 19962306a36Sopenharmony_ci * Return: Number number of bytes written (@count) or negative errno in 20062306a36Sopenharmony_ci * case of error. 20162306a36Sopenharmony_ci */ 20262306a36Sopenharmony_cistatic ssize_t northpeak_store(struct device *dev, struct device_attribute *attr, 20362306a36Sopenharmony_ci const char *buf, size_t count) 20462306a36Sopenharmony_ci{ 20562306a36Sopenharmony_ci struct intel_pmc_dev *pmc = dev_get_drvdata(dev); 20662306a36Sopenharmony_ci struct intel_scu_ipc_dev *scu = pmc->scu; 20762306a36Sopenharmony_ci unsigned long val; 20862306a36Sopenharmony_ci int subcmd; 20962306a36Sopenharmony_ci int ret; 21062306a36Sopenharmony_ci 21162306a36Sopenharmony_ci ret = kstrtoul(buf, 0, &val); 21262306a36Sopenharmony_ci if (ret) 21362306a36Sopenharmony_ci return ret; 21462306a36Sopenharmony_ci 21562306a36Sopenharmony_ci /* Northpeak is enabled if subcmd == 1 and disabled if it is 0 */ 21662306a36Sopenharmony_ci if (val) 21762306a36Sopenharmony_ci subcmd = 1; 21862306a36Sopenharmony_ci else 21962306a36Sopenharmony_ci subcmd = 0; 22062306a36Sopenharmony_ci 22162306a36Sopenharmony_ci ret = intel_scu_ipc_dev_simple_command(scu, PMC_NORTHPEAK_CTRL, subcmd); 22262306a36Sopenharmony_ci if (ret) 22362306a36Sopenharmony_ci return ret; 22462306a36Sopenharmony_ci 22562306a36Sopenharmony_ci return count; 22662306a36Sopenharmony_ci} 22762306a36Sopenharmony_cistatic DEVICE_ATTR_WO(northpeak); 22862306a36Sopenharmony_ci 22962306a36Sopenharmony_cistatic struct attribute *intel_pmc_attrs[] = { 23062306a36Sopenharmony_ci &dev_attr_northpeak.attr, 23162306a36Sopenharmony_ci &dev_attr_simplecmd.attr, 23262306a36Sopenharmony_ci NULL 23362306a36Sopenharmony_ci}; 23462306a36Sopenharmony_ci 23562306a36Sopenharmony_cistatic const struct attribute_group intel_pmc_group = { 23662306a36Sopenharmony_ci .attrs = intel_pmc_attrs, 23762306a36Sopenharmony_ci}; 23862306a36Sopenharmony_ci 23962306a36Sopenharmony_cistatic const struct attribute_group *intel_pmc_groups[] = { 24062306a36Sopenharmony_ci &intel_pmc_group, 24162306a36Sopenharmony_ci NULL 24262306a36Sopenharmony_ci}; 24362306a36Sopenharmony_ci 24462306a36Sopenharmony_cistatic struct resource punit_res[6]; 24562306a36Sopenharmony_ci 24662306a36Sopenharmony_cistatic struct mfd_cell punit = { 24762306a36Sopenharmony_ci .name = "intel_punit_ipc", 24862306a36Sopenharmony_ci .resources = punit_res, 24962306a36Sopenharmony_ci}; 25062306a36Sopenharmony_ci 25162306a36Sopenharmony_cistatic struct itco_wdt_platform_data tco_pdata = { 25262306a36Sopenharmony_ci .name = "Apollo Lake SoC", 25362306a36Sopenharmony_ci .version = 5, 25462306a36Sopenharmony_ci .no_reboot_use_pmc = true, 25562306a36Sopenharmony_ci}; 25662306a36Sopenharmony_ci 25762306a36Sopenharmony_cistatic struct resource tco_res[2]; 25862306a36Sopenharmony_ci 25962306a36Sopenharmony_cistatic const struct mfd_cell tco = { 26062306a36Sopenharmony_ci .name = "iTCO_wdt", 26162306a36Sopenharmony_ci .ignore_resource_conflicts = true, 26262306a36Sopenharmony_ci .resources = tco_res, 26362306a36Sopenharmony_ci .num_resources = ARRAY_SIZE(tco_res), 26462306a36Sopenharmony_ci .platform_data = &tco_pdata, 26562306a36Sopenharmony_ci .pdata_size = sizeof(tco_pdata), 26662306a36Sopenharmony_ci}; 26762306a36Sopenharmony_ci 26862306a36Sopenharmony_cistatic const struct resource telem_res[] = { 26962306a36Sopenharmony_ci DEFINE_RES_MEM(TELEM_PUNIT_SSRAM_OFFSET, TELEM_SSRAM_SIZE), 27062306a36Sopenharmony_ci DEFINE_RES_MEM(TELEM_PMC_SSRAM_OFFSET, TELEM_SSRAM_SIZE), 27162306a36Sopenharmony_ci}; 27262306a36Sopenharmony_ci 27362306a36Sopenharmony_cistatic const struct mfd_cell telem = { 27462306a36Sopenharmony_ci .name = "intel_telemetry", 27562306a36Sopenharmony_ci .resources = telem_res, 27662306a36Sopenharmony_ci .num_resources = ARRAY_SIZE(telem_res), 27762306a36Sopenharmony_ci}; 27862306a36Sopenharmony_ci 27962306a36Sopenharmony_cistatic int intel_pmc_get_tco_resources(struct platform_device *pdev) 28062306a36Sopenharmony_ci{ 28162306a36Sopenharmony_ci struct resource *res; 28262306a36Sopenharmony_ci 28362306a36Sopenharmony_ci if (acpi_has_watchdog()) 28462306a36Sopenharmony_ci return 0; 28562306a36Sopenharmony_ci 28662306a36Sopenharmony_ci res = platform_get_resource(pdev, IORESOURCE_IO, 28762306a36Sopenharmony_ci PLAT_RESOURCE_ACPI_IO_INDEX); 28862306a36Sopenharmony_ci if (!res) { 28962306a36Sopenharmony_ci dev_err(&pdev->dev, "Failed to get IO resource\n"); 29062306a36Sopenharmony_ci return -EINVAL; 29162306a36Sopenharmony_ci } 29262306a36Sopenharmony_ci 29362306a36Sopenharmony_ci tco_res[0].flags = IORESOURCE_IO; 29462306a36Sopenharmony_ci tco_res[0].start = res->start + TCO_BASE_OFFSET; 29562306a36Sopenharmony_ci tco_res[0].end = tco_res[0].start + TCO_REGS_SIZE - 1; 29662306a36Sopenharmony_ci tco_res[1].flags = IORESOURCE_IO; 29762306a36Sopenharmony_ci tco_res[1].start = res->start + SMI_EN_OFFSET; 29862306a36Sopenharmony_ci tco_res[1].end = tco_res[1].start + SMI_EN_SIZE - 1; 29962306a36Sopenharmony_ci 30062306a36Sopenharmony_ci return 0; 30162306a36Sopenharmony_ci} 30262306a36Sopenharmony_ci 30362306a36Sopenharmony_cistatic int intel_pmc_get_resources(struct platform_device *pdev, 30462306a36Sopenharmony_ci struct intel_pmc_dev *pmc, 30562306a36Sopenharmony_ci struct intel_scu_ipc_data *scu_data) 30662306a36Sopenharmony_ci{ 30762306a36Sopenharmony_ci struct resource gcr_res; 30862306a36Sopenharmony_ci size_t npunit_res = 0; 30962306a36Sopenharmony_ci struct resource *res; 31062306a36Sopenharmony_ci int ret; 31162306a36Sopenharmony_ci 31262306a36Sopenharmony_ci scu_data->irq = platform_get_irq_optional(pdev, 0); 31362306a36Sopenharmony_ci 31462306a36Sopenharmony_ci res = platform_get_resource(pdev, IORESOURCE_MEM, 31562306a36Sopenharmony_ci PLAT_RESOURCE_IPC_INDEX); 31662306a36Sopenharmony_ci if (!res) { 31762306a36Sopenharmony_ci dev_err(&pdev->dev, "Failed to get IPC resource\n"); 31862306a36Sopenharmony_ci return -EINVAL; 31962306a36Sopenharmony_ci } 32062306a36Sopenharmony_ci 32162306a36Sopenharmony_ci /* IPC registers */ 32262306a36Sopenharmony_ci scu_data->mem.flags = res->flags; 32362306a36Sopenharmony_ci scu_data->mem.start = res->start; 32462306a36Sopenharmony_ci scu_data->mem.end = res->start + PLAT_RESOURCE_IPC_SIZE - 1; 32562306a36Sopenharmony_ci 32662306a36Sopenharmony_ci /* GCR registers */ 32762306a36Sopenharmony_ci gcr_res.flags = res->flags; 32862306a36Sopenharmony_ci gcr_res.start = res->start + PLAT_RESOURCE_GCR_OFFSET; 32962306a36Sopenharmony_ci gcr_res.end = gcr_res.start + PLAT_RESOURCE_GCR_SIZE - 1; 33062306a36Sopenharmony_ci 33162306a36Sopenharmony_ci pmc->gcr_mem_base = devm_ioremap_resource(&pdev->dev, &gcr_res); 33262306a36Sopenharmony_ci if (IS_ERR(pmc->gcr_mem_base)) 33362306a36Sopenharmony_ci return PTR_ERR(pmc->gcr_mem_base); 33462306a36Sopenharmony_ci 33562306a36Sopenharmony_ci /* Only register iTCO watchdog if there is no WDAT ACPI table */ 33662306a36Sopenharmony_ci ret = intel_pmc_get_tco_resources(pdev); 33762306a36Sopenharmony_ci if (ret) 33862306a36Sopenharmony_ci return ret; 33962306a36Sopenharmony_ci 34062306a36Sopenharmony_ci /* BIOS data register */ 34162306a36Sopenharmony_ci res = platform_get_resource(pdev, IORESOURCE_MEM, 34262306a36Sopenharmony_ci PLAT_RESOURCE_BIOS_DATA_INDEX); 34362306a36Sopenharmony_ci if (!res) { 34462306a36Sopenharmony_ci dev_err(&pdev->dev, "Failed to get resource of P-unit BIOS data\n"); 34562306a36Sopenharmony_ci return -EINVAL; 34662306a36Sopenharmony_ci } 34762306a36Sopenharmony_ci punit_res[npunit_res++] = *res; 34862306a36Sopenharmony_ci 34962306a36Sopenharmony_ci /* BIOS interface register */ 35062306a36Sopenharmony_ci res = platform_get_resource(pdev, IORESOURCE_MEM, 35162306a36Sopenharmony_ci PLAT_RESOURCE_BIOS_IFACE_INDEX); 35262306a36Sopenharmony_ci if (!res) { 35362306a36Sopenharmony_ci dev_err(&pdev->dev, "Failed to get resource of P-unit BIOS interface\n"); 35462306a36Sopenharmony_ci return -EINVAL; 35562306a36Sopenharmony_ci } 35662306a36Sopenharmony_ci punit_res[npunit_res++] = *res; 35762306a36Sopenharmony_ci 35862306a36Sopenharmony_ci /* ISP data register, optional */ 35962306a36Sopenharmony_ci res = platform_get_resource(pdev, IORESOURCE_MEM, 36062306a36Sopenharmony_ci PLAT_RESOURCE_ISP_DATA_INDEX); 36162306a36Sopenharmony_ci if (res) 36262306a36Sopenharmony_ci punit_res[npunit_res++] = *res; 36362306a36Sopenharmony_ci 36462306a36Sopenharmony_ci /* ISP interface register, optional */ 36562306a36Sopenharmony_ci res = platform_get_resource(pdev, IORESOURCE_MEM, 36662306a36Sopenharmony_ci PLAT_RESOURCE_ISP_IFACE_INDEX); 36762306a36Sopenharmony_ci if (res) 36862306a36Sopenharmony_ci punit_res[npunit_res++] = *res; 36962306a36Sopenharmony_ci 37062306a36Sopenharmony_ci /* GTD data register, optional */ 37162306a36Sopenharmony_ci res = platform_get_resource(pdev, IORESOURCE_MEM, 37262306a36Sopenharmony_ci PLAT_RESOURCE_GTD_DATA_INDEX); 37362306a36Sopenharmony_ci if (res) 37462306a36Sopenharmony_ci punit_res[npunit_res++] = *res; 37562306a36Sopenharmony_ci 37662306a36Sopenharmony_ci /* GTD interface register, optional */ 37762306a36Sopenharmony_ci res = platform_get_resource(pdev, IORESOURCE_MEM, 37862306a36Sopenharmony_ci PLAT_RESOURCE_GTD_IFACE_INDEX); 37962306a36Sopenharmony_ci if (res) 38062306a36Sopenharmony_ci punit_res[npunit_res++] = *res; 38162306a36Sopenharmony_ci 38262306a36Sopenharmony_ci punit.num_resources = npunit_res; 38362306a36Sopenharmony_ci 38462306a36Sopenharmony_ci /* Telemetry SSRAM is optional */ 38562306a36Sopenharmony_ci res = platform_get_resource(pdev, IORESOURCE_MEM, 38662306a36Sopenharmony_ci PLAT_RESOURCE_TELEM_SSRAM_INDEX); 38762306a36Sopenharmony_ci if (res) 38862306a36Sopenharmony_ci pmc->telem_base = res; 38962306a36Sopenharmony_ci 39062306a36Sopenharmony_ci return 0; 39162306a36Sopenharmony_ci} 39262306a36Sopenharmony_ci 39362306a36Sopenharmony_cistatic int intel_pmc_create_devices(struct intel_pmc_dev *pmc) 39462306a36Sopenharmony_ci{ 39562306a36Sopenharmony_ci int ret; 39662306a36Sopenharmony_ci 39762306a36Sopenharmony_ci if (!acpi_has_watchdog()) { 39862306a36Sopenharmony_ci ret = devm_mfd_add_devices(pmc->dev, PLATFORM_DEVID_AUTO, &tco, 39962306a36Sopenharmony_ci 1, NULL, 0, NULL); 40062306a36Sopenharmony_ci if (ret) 40162306a36Sopenharmony_ci return ret; 40262306a36Sopenharmony_ci } 40362306a36Sopenharmony_ci 40462306a36Sopenharmony_ci ret = devm_mfd_add_devices(pmc->dev, PLATFORM_DEVID_AUTO, &punit, 1, 40562306a36Sopenharmony_ci NULL, 0, NULL); 40662306a36Sopenharmony_ci if (ret) 40762306a36Sopenharmony_ci return ret; 40862306a36Sopenharmony_ci 40962306a36Sopenharmony_ci if (pmc->telem_base) { 41062306a36Sopenharmony_ci ret = devm_mfd_add_devices(pmc->dev, PLATFORM_DEVID_AUTO, 41162306a36Sopenharmony_ci &telem, 1, pmc->telem_base, 0, NULL); 41262306a36Sopenharmony_ci } 41362306a36Sopenharmony_ci 41462306a36Sopenharmony_ci return ret; 41562306a36Sopenharmony_ci} 41662306a36Sopenharmony_ci 41762306a36Sopenharmony_cistatic const struct acpi_device_id intel_pmc_acpi_ids[] = { 41862306a36Sopenharmony_ci { "INT34D2" }, 41962306a36Sopenharmony_ci { } 42062306a36Sopenharmony_ci}; 42162306a36Sopenharmony_ciMODULE_DEVICE_TABLE(acpi, intel_pmc_acpi_ids); 42262306a36Sopenharmony_ci 42362306a36Sopenharmony_cistatic int intel_pmc_probe(struct platform_device *pdev) 42462306a36Sopenharmony_ci{ 42562306a36Sopenharmony_ci struct intel_scu_ipc_data scu_data = {}; 42662306a36Sopenharmony_ci struct intel_pmc_dev *pmc; 42762306a36Sopenharmony_ci int ret; 42862306a36Sopenharmony_ci 42962306a36Sopenharmony_ci pmc = devm_kzalloc(&pdev->dev, sizeof(*pmc), GFP_KERNEL); 43062306a36Sopenharmony_ci if (!pmc) 43162306a36Sopenharmony_ci return -ENOMEM; 43262306a36Sopenharmony_ci 43362306a36Sopenharmony_ci pmc->dev = &pdev->dev; 43462306a36Sopenharmony_ci spin_lock_init(&pmc->gcr_lock); 43562306a36Sopenharmony_ci 43662306a36Sopenharmony_ci ret = intel_pmc_get_resources(pdev, pmc, &scu_data); 43762306a36Sopenharmony_ci if (ret) { 43862306a36Sopenharmony_ci dev_err(&pdev->dev, "Failed to request resources\n"); 43962306a36Sopenharmony_ci return ret; 44062306a36Sopenharmony_ci } 44162306a36Sopenharmony_ci 44262306a36Sopenharmony_ci pmc->scu = devm_intel_scu_ipc_register(&pdev->dev, &scu_data); 44362306a36Sopenharmony_ci if (IS_ERR(pmc->scu)) 44462306a36Sopenharmony_ci return PTR_ERR(pmc->scu); 44562306a36Sopenharmony_ci 44662306a36Sopenharmony_ci platform_set_drvdata(pdev, pmc); 44762306a36Sopenharmony_ci 44862306a36Sopenharmony_ci ret = intel_pmc_create_devices(pmc); 44962306a36Sopenharmony_ci if (ret) 45062306a36Sopenharmony_ci dev_err(&pdev->dev, "Failed to create PMC devices\n"); 45162306a36Sopenharmony_ci 45262306a36Sopenharmony_ci return ret; 45362306a36Sopenharmony_ci} 45462306a36Sopenharmony_ci 45562306a36Sopenharmony_cistatic struct platform_driver intel_pmc_driver = { 45662306a36Sopenharmony_ci .probe = intel_pmc_probe, 45762306a36Sopenharmony_ci .driver = { 45862306a36Sopenharmony_ci .name = "intel_pmc_bxt", 45962306a36Sopenharmony_ci .acpi_match_table = intel_pmc_acpi_ids, 46062306a36Sopenharmony_ci .dev_groups = intel_pmc_groups, 46162306a36Sopenharmony_ci }, 46262306a36Sopenharmony_ci}; 46362306a36Sopenharmony_cimodule_platform_driver(intel_pmc_driver); 46462306a36Sopenharmony_ci 46562306a36Sopenharmony_ciMODULE_AUTHOR("Mika Westerberg <mika.westerberg@linux.intel.com>"); 46662306a36Sopenharmony_ciMODULE_AUTHOR("Zha Qipeng <qipeng.zha@intel.com>"); 46762306a36Sopenharmony_ciMODULE_DESCRIPTION("Intel Broxton PMC driver"); 46862306a36Sopenharmony_ciMODULE_LICENSE("GPL v2"); 469