18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Driver for the Intel Broxton PMC 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * (C) Copyright 2014 - 2020 Intel Corporation 68c2ecf20Sopenharmony_ci * 78c2ecf20Sopenharmony_ci * This driver is based on Intel SCU IPC driver (intel_scu_ipc.c) by 88c2ecf20Sopenharmony_ci * Sreedhara DS <sreedhara.ds@intel.com> 98c2ecf20Sopenharmony_ci * 108c2ecf20Sopenharmony_ci * The PMC (Power Management Controller) running on the ARC processor 118c2ecf20Sopenharmony_ci * communicates with another entity running in the IA (Intel Architecture) 128c2ecf20Sopenharmony_ci * core through an IPC (Intel Processor Communications) mechanism which in 138c2ecf20Sopenharmony_ci * turn sends messages between the IA and the PMC. 148c2ecf20Sopenharmony_ci */ 158c2ecf20Sopenharmony_ci 168c2ecf20Sopenharmony_ci#include <linux/acpi.h> 178c2ecf20Sopenharmony_ci#include <linux/delay.h> 188c2ecf20Sopenharmony_ci#include <linux/errno.h> 198c2ecf20Sopenharmony_ci#include <linux/interrupt.h> 208c2ecf20Sopenharmony_ci#include <linux/io-64-nonatomic-lo-hi.h> 218c2ecf20Sopenharmony_ci#include <linux/mfd/core.h> 228c2ecf20Sopenharmony_ci#include <linux/mfd/intel_pmc_bxt.h> 238c2ecf20Sopenharmony_ci#include <linux/module.h> 248c2ecf20Sopenharmony_ci#include <linux/platform_device.h> 258c2ecf20Sopenharmony_ci#include <linux/platform_data/itco_wdt.h> 268c2ecf20Sopenharmony_ci 278c2ecf20Sopenharmony_ci#include <asm/intel_scu_ipc.h> 288c2ecf20Sopenharmony_ci 298c2ecf20Sopenharmony_ci/* Residency with clock rate at 19.2MHz to usecs */ 308c2ecf20Sopenharmony_ci#define S0IX_RESIDENCY_IN_USECS(d, s) \ 318c2ecf20Sopenharmony_ci({ \ 328c2ecf20Sopenharmony_ci u64 result = 10ull * ((d) + (s)); \ 338c2ecf20Sopenharmony_ci do_div(result, 192); \ 348c2ecf20Sopenharmony_ci result; \ 358c2ecf20Sopenharmony_ci}) 368c2ecf20Sopenharmony_ci 378c2ecf20Sopenharmony_ci/* Resources exported from IFWI */ 388c2ecf20Sopenharmony_ci#define PLAT_RESOURCE_IPC_INDEX 0 398c2ecf20Sopenharmony_ci#define PLAT_RESOURCE_IPC_SIZE 0x1000 408c2ecf20Sopenharmony_ci#define PLAT_RESOURCE_GCR_OFFSET 0x1000 418c2ecf20Sopenharmony_ci#define PLAT_RESOURCE_GCR_SIZE 0x1000 428c2ecf20Sopenharmony_ci#define PLAT_RESOURCE_BIOS_DATA_INDEX 1 438c2ecf20Sopenharmony_ci#define PLAT_RESOURCE_BIOS_IFACE_INDEX 2 448c2ecf20Sopenharmony_ci#define PLAT_RESOURCE_TELEM_SSRAM_INDEX 3 458c2ecf20Sopenharmony_ci#define PLAT_RESOURCE_ISP_DATA_INDEX 4 468c2ecf20Sopenharmony_ci#define PLAT_RESOURCE_ISP_IFACE_INDEX 5 478c2ecf20Sopenharmony_ci#define PLAT_RESOURCE_GTD_DATA_INDEX 6 488c2ecf20Sopenharmony_ci#define PLAT_RESOURCE_GTD_IFACE_INDEX 7 498c2ecf20Sopenharmony_ci#define PLAT_RESOURCE_ACPI_IO_INDEX 0 508c2ecf20Sopenharmony_ci 518c2ecf20Sopenharmony_ci/* 528c2ecf20Sopenharmony_ci * BIOS does not create an ACPI device for each PMC function, but 538c2ecf20Sopenharmony_ci * exports multiple resources from one ACPI device (IPC) for multiple 548c2ecf20Sopenharmony_ci * functions. This driver is responsible for creating a child device and 558c2ecf20Sopenharmony_ci * to export resources for those functions. 568c2ecf20Sopenharmony_ci */ 578c2ecf20Sopenharmony_ci#define SMI_EN_OFFSET 0x0040 588c2ecf20Sopenharmony_ci#define SMI_EN_SIZE 4 598c2ecf20Sopenharmony_ci#define TCO_BASE_OFFSET 0x0060 608c2ecf20Sopenharmony_ci#define TCO_REGS_SIZE 16 618c2ecf20Sopenharmony_ci#define TELEM_SSRAM_SIZE 240 628c2ecf20Sopenharmony_ci#define TELEM_PMC_SSRAM_OFFSET 0x1b00 638c2ecf20Sopenharmony_ci#define TELEM_PUNIT_SSRAM_OFFSET 0x1a00 648c2ecf20Sopenharmony_ci 658c2ecf20Sopenharmony_ci/* Commands */ 668c2ecf20Sopenharmony_ci#define PMC_NORTHPEAK_CTRL 0xed 678c2ecf20Sopenharmony_ci 688c2ecf20Sopenharmony_cistatic inline bool is_gcr_valid(u32 offset) 698c2ecf20Sopenharmony_ci{ 708c2ecf20Sopenharmony_ci return offset < PLAT_RESOURCE_GCR_SIZE - 8; 718c2ecf20Sopenharmony_ci} 728c2ecf20Sopenharmony_ci 738c2ecf20Sopenharmony_ci/** 748c2ecf20Sopenharmony_ci * intel_pmc_gcr_read64() - Read a 64-bit PMC GCR register 758c2ecf20Sopenharmony_ci * @pmc: PMC device pointer 768c2ecf20Sopenharmony_ci * @offset: offset of GCR register from GCR address base 778c2ecf20Sopenharmony_ci * @data: data pointer for storing the register output 788c2ecf20Sopenharmony_ci * 798c2ecf20Sopenharmony_ci * Reads the 64-bit PMC GCR register at given offset. 808c2ecf20Sopenharmony_ci * 818c2ecf20Sopenharmony_ci * Return: Negative value on error or 0 on success. 828c2ecf20Sopenharmony_ci */ 838c2ecf20Sopenharmony_ciint intel_pmc_gcr_read64(struct intel_pmc_dev *pmc, u32 offset, u64 *data) 848c2ecf20Sopenharmony_ci{ 858c2ecf20Sopenharmony_ci if (!is_gcr_valid(offset)) 868c2ecf20Sopenharmony_ci return -EINVAL; 878c2ecf20Sopenharmony_ci 888c2ecf20Sopenharmony_ci spin_lock(&pmc->gcr_lock); 898c2ecf20Sopenharmony_ci *data = readq(pmc->gcr_mem_base + offset); 908c2ecf20Sopenharmony_ci spin_unlock(&pmc->gcr_lock); 918c2ecf20Sopenharmony_ci 928c2ecf20Sopenharmony_ci return 0; 938c2ecf20Sopenharmony_ci} 948c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(intel_pmc_gcr_read64); 958c2ecf20Sopenharmony_ci 968c2ecf20Sopenharmony_ci/** 978c2ecf20Sopenharmony_ci * intel_pmc_gcr_update() - Update PMC GCR register bits 988c2ecf20Sopenharmony_ci * @pmc: PMC device pointer 998c2ecf20Sopenharmony_ci * @offset: offset of GCR register from GCR address base 1008c2ecf20Sopenharmony_ci * @mask: bit mask for update operation 1018c2ecf20Sopenharmony_ci * @val: update value 1028c2ecf20Sopenharmony_ci * 1038c2ecf20Sopenharmony_ci * Updates the bits of given GCR register as specified by 1048c2ecf20Sopenharmony_ci * @mask and @val. 1058c2ecf20Sopenharmony_ci * 1068c2ecf20Sopenharmony_ci * Return: Negative value on error or 0 on success. 1078c2ecf20Sopenharmony_ci */ 1088c2ecf20Sopenharmony_ciint intel_pmc_gcr_update(struct intel_pmc_dev *pmc, u32 offset, u32 mask, u32 val) 1098c2ecf20Sopenharmony_ci{ 1108c2ecf20Sopenharmony_ci u32 new_val; 1118c2ecf20Sopenharmony_ci 1128c2ecf20Sopenharmony_ci if (!is_gcr_valid(offset)) 1138c2ecf20Sopenharmony_ci return -EINVAL; 1148c2ecf20Sopenharmony_ci 1158c2ecf20Sopenharmony_ci spin_lock(&pmc->gcr_lock); 1168c2ecf20Sopenharmony_ci new_val = readl(pmc->gcr_mem_base + offset); 1178c2ecf20Sopenharmony_ci 1188c2ecf20Sopenharmony_ci new_val = (new_val & ~mask) | (val & mask); 1198c2ecf20Sopenharmony_ci writel(new_val, pmc->gcr_mem_base + offset); 1208c2ecf20Sopenharmony_ci 1218c2ecf20Sopenharmony_ci new_val = readl(pmc->gcr_mem_base + offset); 1228c2ecf20Sopenharmony_ci spin_unlock(&pmc->gcr_lock); 1238c2ecf20Sopenharmony_ci 1248c2ecf20Sopenharmony_ci /* Check whether the bit update is successful */ 1258c2ecf20Sopenharmony_ci return (new_val & mask) != (val & mask) ? -EIO : 0; 1268c2ecf20Sopenharmony_ci} 1278c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(intel_pmc_gcr_update); 1288c2ecf20Sopenharmony_ci 1298c2ecf20Sopenharmony_ci/** 1308c2ecf20Sopenharmony_ci * intel_pmc_s0ix_counter_read() - Read S0ix residency 1318c2ecf20Sopenharmony_ci * @pmc: PMC device pointer 1328c2ecf20Sopenharmony_ci * @data: Out param that contains current S0ix residency count. 1338c2ecf20Sopenharmony_ci * 1348c2ecf20Sopenharmony_ci * Writes to @data how many usecs the system has been in low-power S0ix 1358c2ecf20Sopenharmony_ci * state. 1368c2ecf20Sopenharmony_ci * 1378c2ecf20Sopenharmony_ci * Return: An error code or 0 on success. 1388c2ecf20Sopenharmony_ci */ 1398c2ecf20Sopenharmony_ciint intel_pmc_s0ix_counter_read(struct intel_pmc_dev *pmc, u64 *data) 1408c2ecf20Sopenharmony_ci{ 1418c2ecf20Sopenharmony_ci u64 deep, shlw; 1428c2ecf20Sopenharmony_ci 1438c2ecf20Sopenharmony_ci spin_lock(&pmc->gcr_lock); 1448c2ecf20Sopenharmony_ci deep = readq(pmc->gcr_mem_base + PMC_GCR_TELEM_DEEP_S0IX_REG); 1458c2ecf20Sopenharmony_ci shlw = readq(pmc->gcr_mem_base + PMC_GCR_TELEM_SHLW_S0IX_REG); 1468c2ecf20Sopenharmony_ci spin_unlock(&pmc->gcr_lock); 1478c2ecf20Sopenharmony_ci 1488c2ecf20Sopenharmony_ci *data = S0IX_RESIDENCY_IN_USECS(deep, shlw); 1498c2ecf20Sopenharmony_ci return 0; 1508c2ecf20Sopenharmony_ci} 1518c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(intel_pmc_s0ix_counter_read); 1528c2ecf20Sopenharmony_ci 1538c2ecf20Sopenharmony_ci/** 1548c2ecf20Sopenharmony_ci * simplecmd_store() - Send a simple IPC command 1558c2ecf20Sopenharmony_ci * @dev: Device under the attribute is 1568c2ecf20Sopenharmony_ci * @attr: Attribute in question 1578c2ecf20Sopenharmony_ci * @buf: Buffer holding data to be stored to the attribute 1588c2ecf20Sopenharmony_ci * @count: Number of bytes in @buf 1598c2ecf20Sopenharmony_ci * 1608c2ecf20Sopenharmony_ci * Expects a string with two integers separated with space. These two 1618c2ecf20Sopenharmony_ci * values hold command and subcommand that is send to PMC. 1628c2ecf20Sopenharmony_ci * 1638c2ecf20Sopenharmony_ci * Return: Number number of bytes written (@count) or negative errno in 1648c2ecf20Sopenharmony_ci * case of error. 1658c2ecf20Sopenharmony_ci */ 1668c2ecf20Sopenharmony_cistatic ssize_t simplecmd_store(struct device *dev, struct device_attribute *attr, 1678c2ecf20Sopenharmony_ci const char *buf, size_t count) 1688c2ecf20Sopenharmony_ci{ 1698c2ecf20Sopenharmony_ci struct intel_pmc_dev *pmc = dev_get_drvdata(dev); 1708c2ecf20Sopenharmony_ci struct intel_scu_ipc_dev *scu = pmc->scu; 1718c2ecf20Sopenharmony_ci int subcmd; 1728c2ecf20Sopenharmony_ci int cmd; 1738c2ecf20Sopenharmony_ci int ret; 1748c2ecf20Sopenharmony_ci 1758c2ecf20Sopenharmony_ci ret = sscanf(buf, "%d %d", &cmd, &subcmd); 1768c2ecf20Sopenharmony_ci if (ret != 2) { 1778c2ecf20Sopenharmony_ci dev_err(dev, "Invalid values, expected: cmd subcmd\n"); 1788c2ecf20Sopenharmony_ci return -EINVAL; 1798c2ecf20Sopenharmony_ci } 1808c2ecf20Sopenharmony_ci 1818c2ecf20Sopenharmony_ci ret = intel_scu_ipc_dev_simple_command(scu, cmd, subcmd); 1828c2ecf20Sopenharmony_ci if (ret) 1838c2ecf20Sopenharmony_ci return ret; 1848c2ecf20Sopenharmony_ci 1858c2ecf20Sopenharmony_ci return count; 1868c2ecf20Sopenharmony_ci} 1878c2ecf20Sopenharmony_cistatic DEVICE_ATTR_WO(simplecmd); 1888c2ecf20Sopenharmony_ci 1898c2ecf20Sopenharmony_ci/** 1908c2ecf20Sopenharmony_ci * northpeak_store() - Enable or disable Northpeak 1918c2ecf20Sopenharmony_ci * @dev: Device under the attribute is 1928c2ecf20Sopenharmony_ci * @attr: Attribute in question 1938c2ecf20Sopenharmony_ci * @buf: Buffer holding data to be stored to the attribute 1948c2ecf20Sopenharmony_ci * @count: Number of bytes in @buf 1958c2ecf20Sopenharmony_ci * 1968c2ecf20Sopenharmony_ci * Expects an unsigned integer. Non-zero enables Northpeak and zero 1978c2ecf20Sopenharmony_ci * disables it. 1988c2ecf20Sopenharmony_ci * 1998c2ecf20Sopenharmony_ci * Return: Number number of bytes written (@count) or negative errno in 2008c2ecf20Sopenharmony_ci * case of error. 2018c2ecf20Sopenharmony_ci */ 2028c2ecf20Sopenharmony_cistatic ssize_t northpeak_store(struct device *dev, struct device_attribute *attr, 2038c2ecf20Sopenharmony_ci const char *buf, size_t count) 2048c2ecf20Sopenharmony_ci{ 2058c2ecf20Sopenharmony_ci struct intel_pmc_dev *pmc = dev_get_drvdata(dev); 2068c2ecf20Sopenharmony_ci struct intel_scu_ipc_dev *scu = pmc->scu; 2078c2ecf20Sopenharmony_ci unsigned long val; 2088c2ecf20Sopenharmony_ci int subcmd; 2098c2ecf20Sopenharmony_ci int ret; 2108c2ecf20Sopenharmony_ci 2118c2ecf20Sopenharmony_ci ret = kstrtoul(buf, 0, &val); 2128c2ecf20Sopenharmony_ci if (ret) 2138c2ecf20Sopenharmony_ci return ret; 2148c2ecf20Sopenharmony_ci 2158c2ecf20Sopenharmony_ci /* Northpeak is enabled if subcmd == 1 and disabled if it is 0 */ 2168c2ecf20Sopenharmony_ci if (val) 2178c2ecf20Sopenharmony_ci subcmd = 1; 2188c2ecf20Sopenharmony_ci else 2198c2ecf20Sopenharmony_ci subcmd = 0; 2208c2ecf20Sopenharmony_ci 2218c2ecf20Sopenharmony_ci ret = intel_scu_ipc_dev_simple_command(scu, PMC_NORTHPEAK_CTRL, subcmd); 2228c2ecf20Sopenharmony_ci if (ret) 2238c2ecf20Sopenharmony_ci return ret; 2248c2ecf20Sopenharmony_ci 2258c2ecf20Sopenharmony_ci return count; 2268c2ecf20Sopenharmony_ci} 2278c2ecf20Sopenharmony_cistatic DEVICE_ATTR_WO(northpeak); 2288c2ecf20Sopenharmony_ci 2298c2ecf20Sopenharmony_cistatic struct attribute *intel_pmc_attrs[] = { 2308c2ecf20Sopenharmony_ci &dev_attr_northpeak.attr, 2318c2ecf20Sopenharmony_ci &dev_attr_simplecmd.attr, 2328c2ecf20Sopenharmony_ci NULL 2338c2ecf20Sopenharmony_ci}; 2348c2ecf20Sopenharmony_ci 2358c2ecf20Sopenharmony_cistatic const struct attribute_group intel_pmc_group = { 2368c2ecf20Sopenharmony_ci .attrs = intel_pmc_attrs, 2378c2ecf20Sopenharmony_ci}; 2388c2ecf20Sopenharmony_ci 2398c2ecf20Sopenharmony_cistatic const struct attribute_group *intel_pmc_groups[] = { 2408c2ecf20Sopenharmony_ci &intel_pmc_group, 2418c2ecf20Sopenharmony_ci NULL 2428c2ecf20Sopenharmony_ci}; 2438c2ecf20Sopenharmony_ci 2448c2ecf20Sopenharmony_cistatic struct resource punit_res[6]; 2458c2ecf20Sopenharmony_ci 2468c2ecf20Sopenharmony_cistatic struct mfd_cell punit = { 2478c2ecf20Sopenharmony_ci .name = "intel_punit_ipc", 2488c2ecf20Sopenharmony_ci .resources = punit_res, 2498c2ecf20Sopenharmony_ci}; 2508c2ecf20Sopenharmony_ci 2518c2ecf20Sopenharmony_cistatic struct itco_wdt_platform_data tco_pdata = { 2528c2ecf20Sopenharmony_ci .name = "Apollo Lake SoC", 2538c2ecf20Sopenharmony_ci .version = 5, 2548c2ecf20Sopenharmony_ci .no_reboot_use_pmc = true, 2558c2ecf20Sopenharmony_ci}; 2568c2ecf20Sopenharmony_ci 2578c2ecf20Sopenharmony_cistatic struct resource tco_res[2]; 2588c2ecf20Sopenharmony_ci 2598c2ecf20Sopenharmony_cistatic const struct mfd_cell tco = { 2608c2ecf20Sopenharmony_ci .name = "iTCO_wdt", 2618c2ecf20Sopenharmony_ci .ignore_resource_conflicts = true, 2628c2ecf20Sopenharmony_ci .resources = tco_res, 2638c2ecf20Sopenharmony_ci .num_resources = ARRAY_SIZE(tco_res), 2648c2ecf20Sopenharmony_ci .platform_data = &tco_pdata, 2658c2ecf20Sopenharmony_ci .pdata_size = sizeof(tco_pdata), 2668c2ecf20Sopenharmony_ci}; 2678c2ecf20Sopenharmony_ci 2688c2ecf20Sopenharmony_cistatic const struct resource telem_res[] = { 2698c2ecf20Sopenharmony_ci DEFINE_RES_MEM(TELEM_PUNIT_SSRAM_OFFSET, TELEM_SSRAM_SIZE), 2708c2ecf20Sopenharmony_ci DEFINE_RES_MEM(TELEM_PMC_SSRAM_OFFSET, TELEM_SSRAM_SIZE), 2718c2ecf20Sopenharmony_ci}; 2728c2ecf20Sopenharmony_ci 2738c2ecf20Sopenharmony_cistatic const struct mfd_cell telem = { 2748c2ecf20Sopenharmony_ci .name = "intel_telemetry", 2758c2ecf20Sopenharmony_ci .resources = telem_res, 2768c2ecf20Sopenharmony_ci .num_resources = ARRAY_SIZE(telem_res), 2778c2ecf20Sopenharmony_ci}; 2788c2ecf20Sopenharmony_ci 2798c2ecf20Sopenharmony_cistatic int intel_pmc_get_tco_resources(struct platform_device *pdev) 2808c2ecf20Sopenharmony_ci{ 2818c2ecf20Sopenharmony_ci struct resource *res; 2828c2ecf20Sopenharmony_ci 2838c2ecf20Sopenharmony_ci if (acpi_has_watchdog()) 2848c2ecf20Sopenharmony_ci return 0; 2858c2ecf20Sopenharmony_ci 2868c2ecf20Sopenharmony_ci res = platform_get_resource(pdev, IORESOURCE_IO, 2878c2ecf20Sopenharmony_ci PLAT_RESOURCE_ACPI_IO_INDEX); 2888c2ecf20Sopenharmony_ci if (!res) { 2898c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "Failed to get IO resource\n"); 2908c2ecf20Sopenharmony_ci return -EINVAL; 2918c2ecf20Sopenharmony_ci } 2928c2ecf20Sopenharmony_ci 2938c2ecf20Sopenharmony_ci tco_res[0].flags = IORESOURCE_IO; 2948c2ecf20Sopenharmony_ci tco_res[0].start = res->start + TCO_BASE_OFFSET; 2958c2ecf20Sopenharmony_ci tco_res[0].end = tco_res[0].start + TCO_REGS_SIZE - 1; 2968c2ecf20Sopenharmony_ci tco_res[1].flags = IORESOURCE_IO; 2978c2ecf20Sopenharmony_ci tco_res[1].start = res->start + SMI_EN_OFFSET; 2988c2ecf20Sopenharmony_ci tco_res[1].end = tco_res[1].start + SMI_EN_SIZE - 1; 2998c2ecf20Sopenharmony_ci 3008c2ecf20Sopenharmony_ci return 0; 3018c2ecf20Sopenharmony_ci} 3028c2ecf20Sopenharmony_ci 3038c2ecf20Sopenharmony_cistatic int intel_pmc_get_resources(struct platform_device *pdev, 3048c2ecf20Sopenharmony_ci struct intel_pmc_dev *pmc, 3058c2ecf20Sopenharmony_ci struct intel_scu_ipc_data *scu_data) 3068c2ecf20Sopenharmony_ci{ 3078c2ecf20Sopenharmony_ci struct resource gcr_res; 3088c2ecf20Sopenharmony_ci size_t npunit_res = 0; 3098c2ecf20Sopenharmony_ci struct resource *res; 3108c2ecf20Sopenharmony_ci int ret; 3118c2ecf20Sopenharmony_ci 3128c2ecf20Sopenharmony_ci scu_data->irq = platform_get_irq_optional(pdev, 0); 3138c2ecf20Sopenharmony_ci 3148c2ecf20Sopenharmony_ci res = platform_get_resource(pdev, IORESOURCE_MEM, 3158c2ecf20Sopenharmony_ci PLAT_RESOURCE_IPC_INDEX); 3168c2ecf20Sopenharmony_ci if (!res) { 3178c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "Failed to get IPC resource\n"); 3188c2ecf20Sopenharmony_ci return -EINVAL; 3198c2ecf20Sopenharmony_ci } 3208c2ecf20Sopenharmony_ci 3218c2ecf20Sopenharmony_ci /* IPC registers */ 3228c2ecf20Sopenharmony_ci scu_data->mem.flags = res->flags; 3238c2ecf20Sopenharmony_ci scu_data->mem.start = res->start; 3248c2ecf20Sopenharmony_ci scu_data->mem.end = res->start + PLAT_RESOURCE_IPC_SIZE - 1; 3258c2ecf20Sopenharmony_ci 3268c2ecf20Sopenharmony_ci /* GCR registers */ 3278c2ecf20Sopenharmony_ci gcr_res.flags = res->flags; 3288c2ecf20Sopenharmony_ci gcr_res.start = res->start + PLAT_RESOURCE_GCR_OFFSET; 3298c2ecf20Sopenharmony_ci gcr_res.end = gcr_res.start + PLAT_RESOURCE_GCR_SIZE - 1; 3308c2ecf20Sopenharmony_ci 3318c2ecf20Sopenharmony_ci pmc->gcr_mem_base = devm_ioremap_resource(&pdev->dev, &gcr_res); 3328c2ecf20Sopenharmony_ci if (IS_ERR(pmc->gcr_mem_base)) 3338c2ecf20Sopenharmony_ci return PTR_ERR(pmc->gcr_mem_base); 3348c2ecf20Sopenharmony_ci 3358c2ecf20Sopenharmony_ci /* Only register iTCO watchdog if there is no WDAT ACPI table */ 3368c2ecf20Sopenharmony_ci ret = intel_pmc_get_tco_resources(pdev); 3378c2ecf20Sopenharmony_ci if (ret) 3388c2ecf20Sopenharmony_ci return ret; 3398c2ecf20Sopenharmony_ci 3408c2ecf20Sopenharmony_ci /* BIOS data register */ 3418c2ecf20Sopenharmony_ci res = platform_get_resource(pdev, IORESOURCE_MEM, 3428c2ecf20Sopenharmony_ci PLAT_RESOURCE_BIOS_DATA_INDEX); 3438c2ecf20Sopenharmony_ci if (!res) { 3448c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "Failed to get resource of P-unit BIOS data\n"); 3458c2ecf20Sopenharmony_ci return -EINVAL; 3468c2ecf20Sopenharmony_ci } 3478c2ecf20Sopenharmony_ci punit_res[npunit_res++] = *res; 3488c2ecf20Sopenharmony_ci 3498c2ecf20Sopenharmony_ci /* BIOS interface register */ 3508c2ecf20Sopenharmony_ci res = platform_get_resource(pdev, IORESOURCE_MEM, 3518c2ecf20Sopenharmony_ci PLAT_RESOURCE_BIOS_IFACE_INDEX); 3528c2ecf20Sopenharmony_ci if (!res) { 3538c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "Failed to get resource of P-unit BIOS interface\n"); 3548c2ecf20Sopenharmony_ci return -EINVAL; 3558c2ecf20Sopenharmony_ci } 3568c2ecf20Sopenharmony_ci punit_res[npunit_res++] = *res; 3578c2ecf20Sopenharmony_ci 3588c2ecf20Sopenharmony_ci /* ISP data register, optional */ 3598c2ecf20Sopenharmony_ci res = platform_get_resource(pdev, IORESOURCE_MEM, 3608c2ecf20Sopenharmony_ci PLAT_RESOURCE_ISP_DATA_INDEX); 3618c2ecf20Sopenharmony_ci if (res) 3628c2ecf20Sopenharmony_ci punit_res[npunit_res++] = *res; 3638c2ecf20Sopenharmony_ci 3648c2ecf20Sopenharmony_ci /* ISP interface register, optional */ 3658c2ecf20Sopenharmony_ci res = platform_get_resource(pdev, IORESOURCE_MEM, 3668c2ecf20Sopenharmony_ci PLAT_RESOURCE_ISP_IFACE_INDEX); 3678c2ecf20Sopenharmony_ci if (res) 3688c2ecf20Sopenharmony_ci punit_res[npunit_res++] = *res; 3698c2ecf20Sopenharmony_ci 3708c2ecf20Sopenharmony_ci /* GTD data register, optional */ 3718c2ecf20Sopenharmony_ci res = platform_get_resource(pdev, IORESOURCE_MEM, 3728c2ecf20Sopenharmony_ci PLAT_RESOURCE_GTD_DATA_INDEX); 3738c2ecf20Sopenharmony_ci if (res) 3748c2ecf20Sopenharmony_ci punit_res[npunit_res++] = *res; 3758c2ecf20Sopenharmony_ci 3768c2ecf20Sopenharmony_ci /* GTD interface register, optional */ 3778c2ecf20Sopenharmony_ci res = platform_get_resource(pdev, IORESOURCE_MEM, 3788c2ecf20Sopenharmony_ci PLAT_RESOURCE_GTD_IFACE_INDEX); 3798c2ecf20Sopenharmony_ci if (res) 3808c2ecf20Sopenharmony_ci punit_res[npunit_res++] = *res; 3818c2ecf20Sopenharmony_ci 3828c2ecf20Sopenharmony_ci punit.num_resources = npunit_res; 3838c2ecf20Sopenharmony_ci 3848c2ecf20Sopenharmony_ci /* Telemetry SSRAM is optional */ 3858c2ecf20Sopenharmony_ci res = platform_get_resource(pdev, IORESOURCE_MEM, 3868c2ecf20Sopenharmony_ci PLAT_RESOURCE_TELEM_SSRAM_INDEX); 3878c2ecf20Sopenharmony_ci if (res) 3888c2ecf20Sopenharmony_ci pmc->telem_base = res; 3898c2ecf20Sopenharmony_ci 3908c2ecf20Sopenharmony_ci return 0; 3918c2ecf20Sopenharmony_ci} 3928c2ecf20Sopenharmony_ci 3938c2ecf20Sopenharmony_cistatic int intel_pmc_create_devices(struct intel_pmc_dev *pmc) 3948c2ecf20Sopenharmony_ci{ 3958c2ecf20Sopenharmony_ci int ret; 3968c2ecf20Sopenharmony_ci 3978c2ecf20Sopenharmony_ci if (!acpi_has_watchdog()) { 3988c2ecf20Sopenharmony_ci ret = devm_mfd_add_devices(pmc->dev, PLATFORM_DEVID_AUTO, &tco, 3998c2ecf20Sopenharmony_ci 1, NULL, 0, NULL); 4008c2ecf20Sopenharmony_ci if (ret) 4018c2ecf20Sopenharmony_ci return ret; 4028c2ecf20Sopenharmony_ci } 4038c2ecf20Sopenharmony_ci 4048c2ecf20Sopenharmony_ci ret = devm_mfd_add_devices(pmc->dev, PLATFORM_DEVID_AUTO, &punit, 1, 4058c2ecf20Sopenharmony_ci NULL, 0, NULL); 4068c2ecf20Sopenharmony_ci if (ret) 4078c2ecf20Sopenharmony_ci return ret; 4088c2ecf20Sopenharmony_ci 4098c2ecf20Sopenharmony_ci if (pmc->telem_base) { 4108c2ecf20Sopenharmony_ci ret = devm_mfd_add_devices(pmc->dev, PLATFORM_DEVID_AUTO, 4118c2ecf20Sopenharmony_ci &telem, 1, pmc->telem_base, 0, NULL); 4128c2ecf20Sopenharmony_ci } 4138c2ecf20Sopenharmony_ci 4148c2ecf20Sopenharmony_ci return ret; 4158c2ecf20Sopenharmony_ci} 4168c2ecf20Sopenharmony_ci 4178c2ecf20Sopenharmony_cistatic const struct acpi_device_id intel_pmc_acpi_ids[] = { 4188c2ecf20Sopenharmony_ci { "INT34D2" }, 4198c2ecf20Sopenharmony_ci { } 4208c2ecf20Sopenharmony_ci}; 4218c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(acpi, intel_pmc_acpi_ids); 4228c2ecf20Sopenharmony_ci 4238c2ecf20Sopenharmony_cistatic int intel_pmc_probe(struct platform_device *pdev) 4248c2ecf20Sopenharmony_ci{ 4258c2ecf20Sopenharmony_ci struct intel_scu_ipc_data scu_data = {}; 4268c2ecf20Sopenharmony_ci struct intel_pmc_dev *pmc; 4278c2ecf20Sopenharmony_ci int ret; 4288c2ecf20Sopenharmony_ci 4298c2ecf20Sopenharmony_ci pmc = devm_kzalloc(&pdev->dev, sizeof(*pmc), GFP_KERNEL); 4308c2ecf20Sopenharmony_ci if (!pmc) 4318c2ecf20Sopenharmony_ci return -ENOMEM; 4328c2ecf20Sopenharmony_ci 4338c2ecf20Sopenharmony_ci pmc->dev = &pdev->dev; 4348c2ecf20Sopenharmony_ci spin_lock_init(&pmc->gcr_lock); 4358c2ecf20Sopenharmony_ci 4368c2ecf20Sopenharmony_ci ret = intel_pmc_get_resources(pdev, pmc, &scu_data); 4378c2ecf20Sopenharmony_ci if (ret) { 4388c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "Failed to request resources\n"); 4398c2ecf20Sopenharmony_ci return ret; 4408c2ecf20Sopenharmony_ci } 4418c2ecf20Sopenharmony_ci 4428c2ecf20Sopenharmony_ci pmc->scu = devm_intel_scu_ipc_register(&pdev->dev, &scu_data); 4438c2ecf20Sopenharmony_ci if (IS_ERR(pmc->scu)) 4448c2ecf20Sopenharmony_ci return PTR_ERR(pmc->scu); 4458c2ecf20Sopenharmony_ci 4468c2ecf20Sopenharmony_ci platform_set_drvdata(pdev, pmc); 4478c2ecf20Sopenharmony_ci 4488c2ecf20Sopenharmony_ci ret = intel_pmc_create_devices(pmc); 4498c2ecf20Sopenharmony_ci if (ret) 4508c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "Failed to create PMC devices\n"); 4518c2ecf20Sopenharmony_ci 4528c2ecf20Sopenharmony_ci return ret; 4538c2ecf20Sopenharmony_ci} 4548c2ecf20Sopenharmony_ci 4558c2ecf20Sopenharmony_cistatic struct platform_driver intel_pmc_driver = { 4568c2ecf20Sopenharmony_ci .probe = intel_pmc_probe, 4578c2ecf20Sopenharmony_ci .driver = { 4588c2ecf20Sopenharmony_ci .name = "intel_pmc_bxt", 4598c2ecf20Sopenharmony_ci .acpi_match_table = intel_pmc_acpi_ids, 4608c2ecf20Sopenharmony_ci .dev_groups = intel_pmc_groups, 4618c2ecf20Sopenharmony_ci }, 4628c2ecf20Sopenharmony_ci}; 4638c2ecf20Sopenharmony_cimodule_platform_driver(intel_pmc_driver); 4648c2ecf20Sopenharmony_ci 4658c2ecf20Sopenharmony_ciMODULE_AUTHOR("Mika Westerberg <mika.westerberg@linux.intel.com>"); 4668c2ecf20Sopenharmony_ciMODULE_AUTHOR("Zha Qipeng <qipeng.zha@intel.com>"); 4678c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Intel Broxton PMC driver"); 4688c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL v2"); 469