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