diff options
author | Andy Shevchenko <andriy.shevchenko@linux.intel.com> | 2020-04-24 13:56:46 +0300 |
---|---|---|
committer | Andy Shevchenko <andriy.shevchenko@linux.intel.com> | 2020-04-24 13:56:46 +0300 |
commit | 4a65ed6562bcfa58fe0c2ca5855c45268f40d365 (patch) | |
tree | 072de56917dc218b75a4796f4605428959ec0dc9 /drivers | |
parent | b5f7311d3a2ed153f26b21c56d54a46d07a4da3f (diff) | |
parent | 2f72d35ea75ee8bf8c9e63fb43d43d5bfb4ac8fd (diff) |
Merge branch 'ib-mfd-x86-usb-watchdog-v5.7'
Merge branch 'ib-mfd-x86-usb-watchdog-v5.7' of
git://git.kernel.org/pub/scm/linux/kernel/git/lee/mfd.git
to avoid conflicts in PDx86.
Signed-off-by: Andy Shevchenko <andriy.shevchenko@linux.intel.com>
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/mfd/Kconfig | 20 | ||||
-rw-r--r-- | drivers/mfd/Makefile | 1 | ||||
-rw-r--r-- | drivers/mfd/intel_pmc_bxt.c | 468 | ||||
-rw-r--r-- | drivers/mfd/intel_soc_pmic_bxtwc.c | 34 | ||||
-rw-r--r-- | drivers/mfd/intel_soc_pmic_mrfld.c | 10 | ||||
-rw-r--r-- | drivers/platform/x86/Kconfig | 46 | ||||
-rw-r--r-- | drivers/platform/x86/Makefile | 2 | ||||
-rw-r--r-- | drivers/platform/x86/intel_mid_powerbtn.c | 15 | ||||
-rw-r--r-- | drivers/platform/x86/intel_pmc_ipc.c | 949 | ||||
-rw-r--r-- | drivers/platform/x86/intel_scu_ipc.c | 447 | ||||
-rw-r--r-- | drivers/platform/x86/intel_scu_ipcutil.c | 43 | ||||
-rw-r--r-- | drivers/platform/x86/intel_scu_pcidrv.c | 68 | ||||
-rw-r--r-- | drivers/platform/x86/intel_telemetry_core.c | 17 | ||||
-rw-r--r-- | drivers/platform/x86/intel_telemetry_debugfs.c | 15 | ||||
-rw-r--r-- | drivers/platform/x86/intel_telemetry_pltdrv.c | 97 | ||||
-rw-r--r-- | drivers/usb/typec/mux/Kconfig | 2 | ||||
-rw-r--r-- | drivers/usb/typec/mux/intel_pmc_mux.c | 12 | ||||
-rw-r--r-- | drivers/usb/typec/tcpm/Kconfig | 2 | ||||
-rw-r--r-- | drivers/watchdog/iTCO_wdt.c | 25 | ||||
-rw-r--r-- | drivers/watchdog/intel-mid_wdt.c | 53 |
20 files changed, 1136 insertions, 1190 deletions
diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig index 0a59249198d3..54b6aa4aaab1 100644 --- a/drivers/mfd/Kconfig +++ b/drivers/mfd/Kconfig @@ -551,7 +551,7 @@ config INTEL_SOC_PMIC config INTEL_SOC_PMIC_BXTWC tristate "Support for Intel Broxton Whiskey Cove PMIC" - depends on INTEL_PMC_IPC + depends on MFD_INTEL_PMC_BXT select MFD_CORE select REGMAP_IRQ help @@ -593,7 +593,7 @@ config INTEL_SOC_PMIC_MRFLD tristate "Support for Intel Merrifield Basin Cove PMIC" depends on GPIOLIB depends on ACPI - depends on INTEL_SCU_IPC + depends on INTEL_SCU select MFD_CORE select REGMAP_IRQ help @@ -625,13 +625,27 @@ config MFD_INTEL_LPSS_PCI config MFD_INTEL_MSIC bool "Intel MSIC" - depends on INTEL_SCU_IPC + depends on INTEL_SCU select MFD_CORE help Select this option to enable access to Intel MSIC (Avatele Passage) chip. This chip embeds audio, battery, GPIO, etc. devices used in Intel Medfield platforms. +config MFD_INTEL_PMC_BXT + tristate "Intel PMC Driver for Broxton" + depends on X86 + depends on X86_PLATFORM_DEVICES + depends on ACPI + select INTEL_SCU_IPC + select MFD_CORE + help + This driver provides support for the PMC (Power Management + Controller) on Intel Broxton and Apollo Lake. The PMC is a + multi-function device that exposes IPC, General Control + Register and P-unit access. In addition this creates devices + for iTCO watchdog and telemetry that are part of the PMC. + config MFD_IPAQ_MICRO bool "Atmel Micro ASIC (iPAQ h3100/h3600/h3700) Support" depends on SA1100_H3100 || SA1100_H3600 diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile index f935d10cbf0f..7761f84a96eb 100644 --- a/drivers/mfd/Makefile +++ b/drivers/mfd/Makefile @@ -212,6 +212,7 @@ obj-$(CONFIG_MFD_INTEL_LPSS) += intel-lpss.o obj-$(CONFIG_MFD_INTEL_LPSS_PCI) += intel-lpss-pci.o obj-$(CONFIG_MFD_INTEL_LPSS_ACPI) += intel-lpss-acpi.o obj-$(CONFIG_MFD_INTEL_MSIC) += intel_msic.o +obj-$(CONFIG_MFD_INTEL_PMC_BXT) += intel_pmc_bxt.o obj-$(CONFIG_MFD_PALMAS) += palmas.o obj-$(CONFIG_MFD_VIPERBOARD) += viperboard.o obj-$(CONFIG_MFD_RC5T583) += rc5t583.o rc5t583-irq.o diff --git a/drivers/mfd/intel_pmc_bxt.c b/drivers/mfd/intel_pmc_bxt.c new file mode 100644 index 000000000000..9f01d38acc7f --- /dev/null +++ b/drivers/mfd/intel_pmc_bxt.c @@ -0,0 +1,468 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Driver for the Intel Broxton PMC + * + * (C) Copyright 2014 - 2020 Intel Corporation + * + * This driver is based on Intel SCU IPC driver (intel_scu_ipc.c) by + * Sreedhara DS <sreedhara.ds@intel.com> + * + * The PMC (Power Management Controller) running on the ARC processor + * communicates with another entity running in the IA (Intel Architecture) + * core through an IPC (Intel Processor Communications) mechanism which in + * turn sends messages between the IA and the PMC. + */ + +#include <linux/acpi.h> +#include <linux/delay.h> +#include <linux/errno.h> +#include <linux/interrupt.h> +#include <linux/io-64-nonatomic-lo-hi.h> +#include <linux/mfd/core.h> +#include <linux/mfd/intel_pmc_bxt.h> +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/platform_data/itco_wdt.h> + +#include <asm/intel_scu_ipc.h> + +/* Residency with clock rate at 19.2MHz to usecs */ +#define S0IX_RESIDENCY_IN_USECS(d, s) \ +({ \ + u64 result = 10ull * ((d) + (s)); \ + do_div(result, 192); \ + result; \ +}) + +/* Resources exported from IFWI */ +#define PLAT_RESOURCE_IPC_INDEX 0 +#define PLAT_RESOURCE_IPC_SIZE 0x1000 +#define PLAT_RESOURCE_GCR_OFFSET 0x1000 +#define PLAT_RESOURCE_GCR_SIZE 0x1000 +#define PLAT_RESOURCE_BIOS_DATA_INDEX 1 +#define PLAT_RESOURCE_BIOS_IFACE_INDEX 2 +#define PLAT_RESOURCE_TELEM_SSRAM_INDEX 3 +#define PLAT_RESOURCE_ISP_DATA_INDEX 4 +#define PLAT_RESOURCE_ISP_IFACE_INDEX 5 +#define PLAT_RESOURCE_GTD_DATA_INDEX 6 +#define PLAT_RESOURCE_GTD_IFACE_INDEX 7 +#define PLAT_RESOURCE_ACPI_IO_INDEX 0 + +/* + * BIOS does not create an ACPI device for each PMC function, but + * exports multiple resources from one ACPI device (IPC) for multiple + * functions. This driver is responsible for creating a child device and + * to export resources for those functions. + */ +#define SMI_EN_OFFSET 0x0040 +#define SMI_EN_SIZE 4 +#define TCO_BASE_OFFSET 0x0060 +#define TCO_REGS_SIZE 16 +#define TELEM_SSRAM_SIZE 240 +#define TELEM_PMC_SSRAM_OFFSET 0x1b00 +#define TELEM_PUNIT_SSRAM_OFFSET 0x1a00 + +/* Commands */ +#define PMC_NORTHPEAK_CTRL 0xed + +static inline bool is_gcr_valid(u32 offset) +{ + return offset < PLAT_RESOURCE_GCR_SIZE - 8; +} + +/** + * intel_pmc_gcr_read64() - Read a 64-bit PMC GCR register + * @pmc: PMC device pointer + * @offset: offset of GCR register from GCR address base + * @data: data pointer for storing the register output + * + * Reads the 64-bit PMC GCR register at given offset. + * + * Return: Negative value on error or 0 on success. + */ +int intel_pmc_gcr_read64(struct intel_pmc_dev *pmc, u32 offset, u64 *data) +{ + if (!is_gcr_valid(offset)) + return -EINVAL; + + spin_lock(&pmc->gcr_lock); + *data = readq(pmc->gcr_mem_base + offset); + spin_unlock(&pmc->gcr_lock); + + return 0; +} +EXPORT_SYMBOL_GPL(intel_pmc_gcr_read64); + +/** + * intel_pmc_gcr_update() - Update PMC GCR register bits + * @pmc: PMC device pointer + * @offset: offset of GCR register from GCR address base + * @mask: bit mask for update operation + * @val: update value + * + * Updates the bits of given GCR register as specified by + * @mask and @val. + * + * Return: Negative value on error or 0 on success. + */ +int intel_pmc_gcr_update(struct intel_pmc_dev *pmc, u32 offset, u32 mask, u32 val) +{ + u32 new_val; + + if (!is_gcr_valid(offset)) + return -EINVAL; + + spin_lock(&pmc->gcr_lock); + new_val = readl(pmc->gcr_mem_base + offset); + + new_val = (new_val & ~mask) | (val & mask); + writel(new_val, pmc->gcr_mem_base + offset); + + new_val = readl(pmc->gcr_mem_base + offset); + spin_unlock(&pmc->gcr_lock); + + /* Check whether the bit update is successful */ + return (new_val & mask) != (val & mask) ? -EIO : 0; +} +EXPORT_SYMBOL_GPL(intel_pmc_gcr_update); + +/** + * intel_pmc_s0ix_counter_read() - Read S0ix residency + * @pmc: PMC device pointer + * @data: Out param that contains current S0ix residency count. + * + * Writes to @data how many usecs the system has been in low-power S0ix + * state. + * + * Return: An error code or 0 on success. + */ +int intel_pmc_s0ix_counter_read(struct intel_pmc_dev *pmc, u64 *data) +{ + u64 deep, shlw; + + spin_lock(&pmc->gcr_lock); + deep = readq(pmc->gcr_mem_base + PMC_GCR_TELEM_DEEP_S0IX_REG); + shlw = readq(pmc->gcr_mem_base + PMC_GCR_TELEM_SHLW_S0IX_REG); + spin_unlock(&pmc->gcr_lock); + + *data = S0IX_RESIDENCY_IN_USECS(deep, shlw); + return 0; +} +EXPORT_SYMBOL_GPL(intel_pmc_s0ix_counter_read); + +/** + * simplecmd_store() - Send a simple IPC command + * @dev: Device under the attribute is + * @attr: Attribute in question + * @buf: Buffer holding data to be stored to the attribute + * @count: Number of bytes in @buf + * + * Expects a string with two integers separated with space. These two + * values hold command and subcommand that is send to PMC. + * + * Return: Number number of bytes written (@count) or negative errno in + * case of error. + */ +static ssize_t simplecmd_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct intel_pmc_dev *pmc = dev_get_drvdata(dev); + struct intel_scu_ipc_dev *scu = pmc->scu; + int subcmd; + int cmd; + int ret; + + ret = sscanf(buf, "%d %d", &cmd, &subcmd); + if (ret != 2) { + dev_err(dev, "Invalid values, expected: cmd subcmd\n"); + return -EINVAL; + } + + ret = intel_scu_ipc_dev_simple_command(scu, cmd, subcmd); + if (ret) + return ret; + + return count; +} +static DEVICE_ATTR_WO(simplecmd); + +/** + * northpeak_store() - Enable or disable Northpeak + * @dev: Device under the attribute is + * @attr: Attribute in question + * @buf: Buffer holding data to be stored to the attribute + * @count: Number of bytes in @buf + * + * Expects an unsigned integer. Non-zero enables Northpeak and zero + * disables it. + * + * Return: Number number of bytes written (@count) or negative errno in + * case of error. + */ +static ssize_t northpeak_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct intel_pmc_dev *pmc = dev_get_drvdata(dev); + struct intel_scu_ipc_dev *scu = pmc->scu; + unsigned long val; + int subcmd; + int ret; + + ret = kstrtoul(buf, 0, &val); + if (ret) + return ret; + + /* Northpeak is enabled if subcmd == 1 and disabled if it is 0 */ + if (val) + subcmd = 1; + else + subcmd = 0; + + ret = intel_scu_ipc_dev_simple_command(scu, PMC_NORTHPEAK_CTRL, subcmd); + if (ret) + return ret; + + return count; +} +static DEVICE_ATTR_WO(northpeak); + +static struct attribute *intel_pmc_attrs[] = { + &dev_attr_northpeak.attr, + &dev_attr_simplecmd.attr, + NULL +}; + +static const struct attribute_group intel_pmc_group = { + .attrs = intel_pmc_attrs, +}; + +static const struct attribute_group *intel_pmc_groups[] = { + &intel_pmc_group, + NULL +}; + +static struct resource punit_res[6]; + +static struct mfd_cell punit = { + .name = "intel_punit_ipc", + .resources = punit_res, +}; + +static struct itco_wdt_platform_data tco_pdata = { + .name = "Apollo Lake SoC", + .version = 5, + .no_reboot_use_pmc = true, +}; + +static struct resource tco_res[2]; + +static const struct mfd_cell tco = { + .name = "iTCO_wdt", + .ignore_resource_conflicts = true, + .resources = tco_res, + .num_resources = ARRAY_SIZE(tco_res), + .platform_data = &tco_pdata, + .pdata_size = sizeof(tco_pdata), +}; + +static const struct resource telem_res[] = { + DEFINE_RES_MEM(TELEM_PUNIT_SSRAM_OFFSET, TELEM_SSRAM_SIZE), + DEFINE_RES_MEM(TELEM_PMC_SSRAM_OFFSET, TELEM_SSRAM_SIZE), +}; + +static const struct mfd_cell telem = { + .name = "intel_telemetry", + .resources = telem_res, + .num_resources = ARRAY_SIZE(telem_res), +}; + +static int intel_pmc_get_tco_resources(struct platform_device *pdev) +{ + struct resource *res; + + if (acpi_has_watchdog()) + return 0; + + res = platform_get_resource(pdev, IORESOURCE_IO, + PLAT_RESOURCE_ACPI_IO_INDEX); + if (!res) { + dev_err(&pdev->dev, "Failed to get IO resource\n"); + return -EINVAL; + } + + tco_res[0].flags = IORESOURCE_IO; + tco_res[0].start = res->start + TCO_BASE_OFFSET; + tco_res[0].end = tco_res[0].start + TCO_REGS_SIZE - 1; + tco_res[1].flags = IORESOURCE_IO; + tco_res[1].start = res->start + SMI_EN_OFFSET; + tco_res[1].end = tco_res[1].start + SMI_EN_SIZE - 1; + + return 0; +} + +static int intel_pmc_get_resources(struct platform_device *pdev, + struct intel_pmc_dev *pmc, + struct intel_scu_ipc_data *scu_data) +{ + struct resource gcr_res; + size_t npunit_res = 0; + struct resource *res; + int ret; + + scu_data->irq = platform_get_irq_optional(pdev, 0); + + res = platform_get_resource(pdev, IORESOURCE_MEM, + PLAT_RESOURCE_IPC_INDEX); + if (!res) { + dev_err(&pdev->dev, "Failed to get IPC resource\n"); + return -EINVAL; + } + + /* IPC registers */ + scu_data->mem.flags = res->flags; + scu_data->mem.start = res->start; + scu_data->mem.end = res->start + PLAT_RESOURCE_IPC_SIZE - 1; + + /* GCR registers */ + gcr_res.flags = res->flags; + gcr_res.start = res->start + PLAT_RESOURCE_GCR_OFFSET; + gcr_res.end = gcr_res.start + PLAT_RESOURCE_GCR_SIZE - 1; + + pmc->gcr_mem_base = devm_ioremap_resource(&pdev->dev, &gcr_res); + if (IS_ERR(pmc->gcr_mem_base)) + return PTR_ERR(pmc->gcr_mem_base); + + /* Only register iTCO watchdog if there is no WDAT ACPI table */ + ret = intel_pmc_get_tco_resources(pdev); + if (ret) + return ret; + + /* BIOS data register */ + res = platform_get_resource(pdev, IORESOURCE_MEM, + PLAT_RESOURCE_BIOS_DATA_INDEX); + if (!res) { + dev_err(&pdev->dev, "Failed to get resource of P-unit BIOS data\n"); + return -EINVAL; + } + punit_res[npunit_res++] = *res; + + /* BIOS interface register */ + res = platform_get_resource(pdev, IORESOURCE_MEM, + PLAT_RESOURCE_BIOS_IFACE_INDEX); + if (!res) { + dev_err(&pdev->dev, "Failed to get resource of P-unit BIOS interface\n"); + return -EINVAL; + } + punit_res[npunit_res++] = *res; + + /* ISP data register, optional */ + res = platform_get_resource(pdev, IORESOURCE_MEM, + PLAT_RESOURCE_ISP_DATA_INDEX); + if (res) + punit_res[npunit_res++] = *res; + + /* ISP interface register, optional */ + res = platform_get_resource(pdev, IORESOURCE_MEM, + PLAT_RESOURCE_ISP_IFACE_INDEX); + if (res) + punit_res[npunit_res++] = *res; + + /* GTD data register, optional */ + res = platform_get_resource(pdev, IORESOURCE_MEM, + PLAT_RESOURCE_GTD_DATA_INDEX); + if (res) + punit_res[npunit_res++] = *res; + + /* GTD interface register, optional */ + res = platform_get_resource(pdev, IORESOURCE_MEM, + PLAT_RESOURCE_GTD_IFACE_INDEX); + if (res) + punit_res[npunit_res++] = *res; + + punit.num_resources = npunit_res; + + /* Telemetry SSRAM is optional */ + res = platform_get_resource(pdev, IORESOURCE_MEM, + PLAT_RESOURCE_TELEM_SSRAM_INDEX); + if (res) + pmc->telem_base = res; + + return 0; +} + +static int intel_pmc_create_devices(struct intel_pmc_dev *pmc) +{ + int ret; + + if (!acpi_has_watchdog()) { + ret = devm_mfd_add_devices(pmc->dev, PLATFORM_DEVID_AUTO, &tco, + 1, NULL, 0, NULL); + if (ret) + return ret; + } + + ret = devm_mfd_add_devices(pmc->dev, PLATFORM_DEVID_AUTO, &punit, 1, + NULL, 0, NULL); + if (ret) + return ret; + + if (pmc->telem_base) { + ret = devm_mfd_add_devices(pmc->dev, PLATFORM_DEVID_AUTO, + &telem, 1, pmc->telem_base, 0, NULL); + } + + return ret; +} + +static const struct acpi_device_id intel_pmc_acpi_ids[] = { + { "INT34D2" }, + { } +}; +MODULE_DEVICE_TABLE(acpi, intel_pmc_acpi_ids); + +static int intel_pmc_probe(struct platform_device *pdev) +{ + struct intel_scu_ipc_data scu_data = {}; + struct intel_pmc_dev *pmc; + int ret; + + pmc = devm_kzalloc(&pdev->dev, sizeof(*pmc), GFP_KERNEL); + if (!pmc) + return -ENOMEM; + + pmc->dev = &pdev->dev; + spin_lock_init(&pmc->gcr_lock); + + ret = intel_pmc_get_resources(pdev, pmc, &scu_data); + if (ret) { + dev_err(&pdev->dev, "Failed to request resources\n"); + return ret; + } + + pmc->scu = devm_intel_scu_ipc_register(&pdev->dev, &scu_data); + if (IS_ERR(pmc->scu)) + return PTR_ERR(pmc->scu); + + platform_set_drvdata(pdev, pmc); + + ret = intel_pmc_create_devices(pmc); + if (ret) + dev_err(&pdev->dev, "Failed to create PMC devices\n"); + + return ret; +} + +static struct platform_driver intel_pmc_driver = { + .probe = intel_pmc_probe, + .driver = { + .name = "intel_pmc_bxt", + .acpi_match_table = intel_pmc_acpi_ids, + .dev_groups = intel_pmc_groups, + }, +}; +module_platform_driver(intel_pmc_driver); + +MODULE_AUTHOR("Mika Westerberg <mika.westerberg@linux.intel.com>"); +MODULE_AUTHOR("Zha Qipeng <qipeng.zha@intel.com>"); +MODULE_DESCRIPTION("Intel Broxton PMC driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/mfd/intel_soc_pmic_bxtwc.c b/drivers/mfd/intel_soc_pmic_bxtwc.c index 739cfb5b69fe..eba89780dbe7 100644 --- a/drivers/mfd/intel_soc_pmic_bxtwc.c +++ b/drivers/mfd/intel_soc_pmic_bxtwc.c @@ -15,7 +15,7 @@ #include <linux/mfd/intel_soc_pmic_bxtwc.h> #include <linux/module.h> -#include <asm/intel_pmc_ipc.h> +#include <asm/intel_scu_ipc.h> /* PMIC device registers */ #define REG_ADDR_MASK 0xFF00 @@ -58,6 +58,10 @@ /* Whiskey Cove PMIC share same ACPI ID between different platforms */ #define BROXTON_PMIC_WC_HRV 4 +#define PMC_PMIC_ACCESS 0xFF +#define PMC_PMIC_READ 0x0 +#define PMC_PMIC_WRITE 0x1 + enum bxtwc_irqs { BXTWC_PWRBTN_LVL1_IRQ = 0, BXTWC_TMU_LVL1_IRQ, @@ -288,13 +292,12 @@ static int regmap_ipc_byte_reg_read(void *context, unsigned int reg, ipc_in[0] = reg; ipc_in[1] = i2c_addr; - ret = intel_pmc_ipc_command(PMC_IPC_PMIC_ACCESS, - PMC_IPC_PMIC_ACCESS_READ, - ipc_in, sizeof(ipc_in), (u32 *)ipc_out, 1); - if (ret) { - dev_err(pmic->dev, "Failed to read from PMIC\n"); + ret = intel_scu_ipc_dev_command(pmic->scu, PMC_PMIC_ACCESS, + PMC_PMIC_READ, ipc_in, sizeof(ipc_in), + ipc_out, sizeof(ipc_out)); + if (ret) return ret; - } + *val = ipc_out[0]; return 0; @@ -303,7 +306,6 @@ static int regmap_ipc_byte_reg_read(void *context, unsigned int reg, static int regmap_ipc_byte_reg_write(void *context, unsigned int reg, unsigned int val) { - int ret; int i2c_addr; u8 ipc_in[3]; struct intel_soc_pmic *pmic = context; @@ -321,15 +323,9 @@ static int regmap_ipc_byte_reg_write(void *context, unsigned int reg, ipc_in[0] = reg; ipc_in[1] = i2c_addr; ipc_in[2] = val; - ret = intel_pmc_ipc_command(PMC_IPC_PMIC_ACCESS, - PMC_IPC_PMIC_ACCESS_WRITE, - ipc_in, sizeof(ipc_in), NULL, 0); - if (ret) { - dev_err(pmic->dev, "Failed to write to PMIC\n"); - return ret; - } - - return 0; + return intel_scu_ipc_dev_command(pmic->scu, PMC_PMIC_ACCESS, + PMC_PMIC_WRITE, ipc_in, sizeof(ipc_in), + NULL, 0); } /* sysfs interfaces to r/w PMIC registers, required by initial script */ @@ -457,6 +453,10 @@ static int bxtwc_probe(struct platform_device *pdev) dev_set_drvdata(&pdev->dev, pmic); pmic->dev = &pdev->dev; + pmic->scu = devm_intel_scu_ipc_dev_get(&pdev->dev); + if (!pmic->scu) + return -EPROBE_DEFER; + pmic->regmap = devm_regmap_init(&pdev->dev, NULL, pmic, &bxtwc_regmap_config); if (IS_ERR(pmic->regmap)) { diff --git a/drivers/mfd/intel_soc_pmic_mrfld.c b/drivers/mfd/intel_soc_pmic_mrfld.c index 26a1551c5faf..bd94c989d232 100644 --- a/drivers/mfd/intel_soc_pmic_mrfld.c +++ b/drivers/mfd/intel_soc_pmic_mrfld.c @@ -74,10 +74,11 @@ static const struct mfd_cell bcove_dev[] = { static int bcove_ipc_byte_reg_read(void *context, unsigned int reg, unsigned int *val) { + struct intel_soc_pmic *pmic = context; u8 ipc_out; int ret; - ret = intel_scu_ipc_ioread8(reg, &ipc_out); + ret = intel_scu_ipc_dev_ioread8(pmic->scu, reg, &ipc_out); if (ret) return ret; @@ -88,10 +89,11 @@ static int bcove_ipc_byte_reg_read(void *context, unsigned int reg, static int bcove_ipc_byte_reg_write(void *context, unsigned int reg, unsigned int val) { + struct intel_soc_pmic *pmic = context; u8 ipc_in = val; int ret; - ret = intel_scu_ipc_iowrite8(reg, ipc_in); + ret = intel_scu_ipc_dev_iowrite8(pmic->scu, reg, ipc_in); if (ret) return ret; @@ -117,6 +119,10 @@ static int bcove_probe(struct platform_device *pdev) if (!pmic) return -ENOMEM; + pmic->scu = devm_intel_scu_ipc_dev_get(dev); + if (!pmic->scu) + return -ENOMEM; + platform_set_drvdata(pdev, pmic); pmic->dev = &pdev->dev; diff --git a/drivers/platform/x86/Kconfig b/drivers/platform/x86/Kconfig index 0ad7ad8cf8e1..642316761443 100644 --- a/drivers/platform/x86/Kconfig +++ b/drivers/platform/x86/Kconfig @@ -1269,7 +1269,8 @@ config INTEL_UNCORE_FREQ_CONTROL config INTEL_BXTWC_PMIC_TMU tristate "Intel BXT Whiskey Cove TMU Driver" depends on REGMAP - depends on INTEL_SOC_PMIC_BXTWC && INTEL_PMC_IPC + depends on MFD_INTEL_PMC_BXT + depends on INTEL_SOC_PMIC_BXTWC ---help--- Select this driver to use Intel BXT Whiskey Cove PMIC TMU feature. This driver enables the alarm wakeup functionality in the TMU unit @@ -1295,7 +1296,7 @@ config INTEL_MFLD_THERMAL config INTEL_MID_POWER_BUTTON tristate "power button driver for Intel MID platforms" - depends on INTEL_SCU_IPC && INPUT + depends on INTEL_SCU && INPUT help This driver handles the power button on the Intel MID platforms. @@ -1327,14 +1328,6 @@ config INTEL_PMC_CORE - LTR Ignore - MPHY/PLL gating status (Sunrisepoint PCH only) -config INTEL_PMC_IPC - tristate "Intel PMC IPC Driver" - depends on ACPI && PCI - ---help--- - This driver provides support for PMC control on some Intel platforms. - The PMC is an ARC processor which defines IPC commands for communication - with other entities in the CPU. - config INTEL_PUNIT_IPC tristate "Intel P-Unit IPC Driver" ---help--- @@ -1342,17 +1335,30 @@ config INTEL_PUNIT_IPC which is used to bridge the communications between kernel and P-Unit. config INTEL_SCU_IPC - bool "Intel SCU IPC Support" - depends on X86_INTEL_MID - default y - ---help--- - IPC is used to bridge the communications between kernel and SCU on - some embedded Intel x86 platforms. This is not needed for PC-type - machines. + bool + +config INTEL_SCU + bool + select INTEL_SCU_IPC + +config INTEL_SCU_PCI + bool "Intel SCU PCI driver" + depends on PCI + select INTEL_SCU + help + This driver is used to bridge the communications between kernel + and SCU on some embedded Intel x86 platforms. It also creates + devices that are connected to the SoC through the SCU. + Platforms supported: + Medfield + Clovertrail + Merrifield + Broxton + Apollo Lake config INTEL_SCU_IPC_UTIL tristate "Intel SCU IPC utility driver" - depends on INTEL_SCU_IPC + depends on INTEL_SCU ---help--- The IPC Util driver provides an interface with the SCU enabling low level access for debug work and updating the firmware. Say @@ -1360,7 +1366,9 @@ config INTEL_SCU_IPC_UTIL config INTEL_TELEMETRY tristate "Intel SoC Telemetry Driver" - depends on INTEL_PMC_IPC && INTEL_PUNIT_IPC && X86_64 + depends on X86_64 + depends on MFD_INTEL_PMC_BXT + depends on INTEL_PUNIT_IPC ---help--- This driver provides interfaces to configure and use telemetry for INTEL SoC from APL onwards. It is also diff --git a/drivers/platform/x86/Makefile b/drivers/platform/x86/Makefile index 53408d965874..04db27a25946 100644 --- a/drivers/platform/x86/Makefile +++ b/drivers/platform/x86/Makefile @@ -138,9 +138,9 @@ obj-$(CONFIG_INTEL_MFLD_THERMAL) += intel_mid_thermal.o obj-$(CONFIG_INTEL_MID_POWER_BUTTON) += intel_mid_powerbtn.o obj-$(CONFIG_INTEL_MRFLD_PWRBTN) += intel_mrfld_pwrbtn.o obj-$(CONFIG_INTEL_PMC_CORE) += intel_pmc_core.o intel_pmc_core_pltdrv.o -obj-$(CONFIG_INTEL_PMC_IPC) += intel_pmc_ipc.o obj-$(CONFIG_INTEL_PUNIT_IPC) += intel_punit_ipc.o obj-$(CONFIG_INTEL_SCU_IPC) += intel_scu_ipc.o +obj-$(CONFIG_INTEL_SCU_PCI) += intel_scu_pcidrv.o obj-$(CONFIG_INTEL_SCU_IPC_UTIL) += intel_scu_ipcutil.o obj-$(CONFIG_INTEL_TELEMETRY) += intel_telemetry_core.o \ intel_telemetry_pltdrv.o \ diff --git a/drivers/platform/x86/intel_mid_powerbtn.c b/drivers/platform/x86/intel_mid_powerbtn.c index 9c9f209c8a33..df434abbb66f 100644 --- a/drivers/platform/x86/intel_mid_powerbtn.c +++ b/drivers/platform/x86/intel_mid_powerbtn.c @@ -46,6 +46,7 @@ struct mid_pb_ddata { unsigned short mirqlvl1_addr; unsigned short pbstat_addr; u8 pbstat_mask; + struct intel_scu_ipc_dev *scu; int (*setup)(struct mid_pb_ddata *ddata); }; @@ -55,7 +56,8 @@ static int mid_pbstat(struct mid_pb_ddata *ddata, int *value) int ret; u8 pbstat; - ret = intel_scu_ipc_ioread8(ddata->pbstat_addr, &pbstat); + ret = intel_scu_ipc_dev_ioread8(ddata->scu, ddata->pbstat_addr, + &pbstat); if (ret) return ret; @@ -67,14 +69,15 @@ static int mid_pbstat(struct mid_pb_ddata *ddata, int *value) static int mid_irq_ack(struct mid_pb_ddata *ddata) { - return intel_scu_ipc_update_register(ddata->mirqlvl1_addr, 0, MSIC_PWRBTNM); + return intel_scu_ipc_dev_update(ddata->scu, ddata->mirqlvl1_addr, 0, + MSIC_PWRBTNM); } static int mrfld_setup(struct mid_pb_ddata *ddata) { /* Unmask the PBIRQ and MPBIRQ on Tangier */ - intel_scu_ipc_update_register(BCOVE_PBIRQ, 0, MSIC_PWRBTNM); - intel_scu_ipc_update_register(BCOVE_PBIRQMASK, 0, MSIC_PWRBTNM); + intel_scu_ipc_dev_update(ddata->scu, BCOVE_PBIRQ, 0, MSIC_PWRBTNM); + intel_scu_ipc_dev_update(ddata->scu, BCOVE_PBIRQMASK, 0, MSIC_PWRBTNM); return 0; } @@ -161,6 +164,10 @@ static int mid_pb_probe(struct platform_device *pdev) return error; } + ddata->scu = devm_intel_scu_ipc_dev_get(&pdev->dev); + if (!ddata->scu) + return -EPROBE_DEFER; + error = devm_request_threaded_irq(&pdev->dev, irq, NULL, mid_pb_isr, IRQF_ONESHOT, DRIVER_NAME, ddata); if (error) { diff --git a/drivers/platform/x86/intel_pmc_ipc.c b/drivers/platform/x86/intel_pmc_ipc.c deleted file mode 100644 index 2433bf73f1ed..000000000000 --- a/drivers/platform/x86/intel_pmc_ipc.c +++ /dev/null @@ -1,949 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - * Driver for the Intel PMC IPC mechanism - * - * (C) Copyright 2014-2015 Intel Corporation - * - * This driver is based on Intel SCU IPC driver(intel_scu_ipc.c) by - * Sreedhara DS <sreedhara.ds@intel.com> - * - * PMC running in ARC processor communicates with other entity running in IA - * core through IPC mechanism which in turn messaging between IA core ad PMC. - */ - -#include <linux/acpi.h> -#include <linux/delay.h> -#include <linux/errno.h> -#include <linux/interrupt.h> -#include <linux/io-64-nonatomic-lo-hi.h> -#include <linux/module.h> -#include <linux/pci.h> -#include <linux/platform_device.h> - -#include <asm/intel_pmc_ipc.h> - -#include <linux/platform_data/itco_wdt.h> - -/* - * IPC registers - * The IA write to IPC_CMD command register triggers an interrupt to the ARC, - * The ARC handles the interrupt and services it, writing optional data to - * the IPC1 registers, updates the IPC_STS response register with the status. - */ -#define IPC_CMD 0x00 -#define IPC_CMD_MSI BIT(8) -#define IPC_CMD_SIZE 16 -#define IPC_CMD_SUBCMD 12 -#define IPC_STATUS 0x04 -#define IPC_STATUS_IRQ BIT(2) -#define IPC_STATUS_ERR BIT(1) -#define IPC_STATUS_BUSY BIT(0) -#define IPC_SPTR 0x08 -#define IPC_DPTR 0x0C -#define IPC_WRITE_BUFFER 0x80 -#define IPC_READ_BUFFER 0x90 - -/* Residency with clock rate at 19.2MHz to usecs */ -#define S0IX_RESIDENCY_IN_USECS(d, s) \ -({ \ - u64 result = 10ull * ((d) + (s)); \ - do_div(result, 192); \ - result; \ -}) - -/* - * 16-byte buffer for sending data associated with IPC command. - */ -#define IPC_DATA_BUFFER_SIZE 16 - -#define IPC_LOOP_CNT 3000000 -#define IPC_MAX_SEC 3 - -#define IPC_TRIGGER_MODE_IRQ true - -/* exported resources from IFWI */ -#define PLAT_RESOURCE_IPC_INDEX 0 -#define PLAT_RESOURCE_IPC_SIZE 0x1000 -#define PLAT_RESOURCE_GCR_OFFSET 0x1000 -#define PLAT_RESOURCE_GCR_SIZE 0x1000 -#define PLAT_RESOURCE_BIOS_DATA_INDEX 1 -#define PLAT_RESOURCE_BIOS_IFACE_INDEX 2 -#define PLAT_RESOURCE_TELEM_SSRAM_INDEX 3 -#define PLAT_RESOURCE_ISP_DATA_INDEX 4 -#define PLAT_RESOURCE_ISP_IFACE_INDEX 5 -#define PLAT_RESOURCE_GTD_DATA_INDEX 6 -#define PLAT_RESOURCE_GTD_IFACE_INDEX 7 -#define PLAT_RESOURCE_ACPI_IO_INDEX 0 - -/* - * BIOS does not create an ACPI device for each PMC function, - * but exports multiple resources from one ACPI device(IPC) for - * multiple functions. This driver is responsible to create a - * platform device and to export resources for those functions. - */ -#define TCO_DEVICE_NAME "iTCO_wdt" -#define SMI_EN_OFFSET 0x40 -#define SMI_EN_SIZE 4 -#define TCO_BASE_OFFSET 0x60 -#define TCO_REGS_SIZE 16 -#define PUNIT_DEVICE_NAME "intel_punit_ipc" -#define TELEMETRY_DEVICE_NAME "intel_telemetry" -#define TELEM_SSRAM_SIZE 240 -#define TELEM_PMC_SSRAM_OFFSET 0x1B00 -#define TELEM_PUNIT_SSRAM_OFFSET 0x1A00 -#define TCO_PMC_OFFSET 0x08 -#define TCO_PMC_SIZE 0x04 - -/* PMC register bit definitions */ - -/* PMC_CFG_REG bit masks */ -#define PMC_CFG_NO_REBOOT_MASK BIT_MASK(4) -#define PMC_CFG_NO_REBOOT_EN (1 << 4) -#define PMC_CFG_NO_REBOOT_DIS (0 << 4) - -static struct intel_pmc_ipc_dev { - struct device *dev; - void __iomem *ipc_base; - bool irq_mode; - int irq; - int cmd; - struct completion cmd_complete; - - /* The following PMC BARs share the same ACPI device with the IPC */ - resource_size_t acpi_io_base; - int acpi_io_size; - struct platform_device *tco_dev; - - /* gcr */ - void __iomem *gcr_mem_base; - bool has_gcr_regs; - spinlock_t gcr_lock; - - /* punit */ - struct platform_device *punit_dev; - unsigned int punit_res_count; - - /* Telemetry */ - resource_size_t telem_pmc_ssram_base; - resource_size_t telem_punit_ssram_base; - int telem_pmc_ssram_size; - int telem_punit_ssram_size; - u8 telem_res_inval; - struct platform_device *telemetry_dev; -} ipcdev; - -static char *ipc_err_sources[] = { - [IPC_ERR_NONE] = - "no error", - [IPC_ERR_CMD_NOT_SUPPORTED] = - "command not supported", - [IPC_ERR_CMD_NOT_SERVICED] = - "command not serviced", - [IPC_ERR_UNABLE_TO_SERVICE] = - "unable to service", - [IPC_ERR_CMD_INVALID] = - "command invalid", - [IPC_ERR_CMD_FAILED] = - "command failed", - [IPC_ERR_EMSECURITY] = - "Invalid Battery", - [IPC_ERR_UNSIGNEDKERNEL] = - "Unsigned kernel", -}; - -/* Prevent concurrent calls to the PMC */ -static DEFINE_MUTEX(ipclock); - -static inline void ipc_send_command(u32 cmd) -{ - ipcdev.cmd = cmd; - if (ipcdev.irq_mode) { - reinit_completion(&ipcdev.cmd_complete); - cmd |= IPC_CMD_MSI; - } - writel(cmd, ipcdev.ipc_base + IPC_CMD); -} - -static inline u32 ipc_read_status(void) -{ - return readl(ipcdev.ipc_base + IPC_STATUS); -} - -static inline void ipc_data_writel(u32 data, u32 offset) -{ - writel(data, ipcdev.ipc_base + IPC_WRITE_BUFFER + offset); -} - -static inline u32 ipc_data_readl(u32 offset) -{ - return readl(ipcdev.ipc_base + IPC_READ_BUFFER + offset); -} - -static inline u64 gcr_data_readq(u32 offset) -{ - return readq(ipcdev.gcr_mem_base + offset); -} - -static inline int is_gcr_valid(u32 offset) -{ - if (!ipcdev.has_gcr_regs) - return -EACCES; - - if (offset > PLAT_RESOURCE_GCR_SIZE) - return -EINVAL; - - return 0; -} - -/** - * intel_pmc_gcr_read64() - Read a 64-bit PMC GCR register - * @offset: offset of GCR register from GCR address base - * @data: data pointer for storing the register output - * - * Reads the 64-bit PMC GCR register at given offset. - * - * Return: negative value on error or 0 on success. - */ -int intel_pmc_gcr_read64(u32 offset, u64 *data) -{ - int ret; - - spin_lock(&ipcdev.gcr_lock); - - ret = is_gcr_valid(offset); - if (ret < 0) { - spin_unlock(&ipcdev.gcr_lock); - return ret; - } - - *data = readq(ipcdev.gcr_mem_base + offset); - - spin_unlock(&ipcdev.gcr_lock); - - return 0; -} -EXPORT_SYMBOL_GPL(intel_pmc_gcr_read64); - -/** - * intel_pmc_gcr_update() - Update PMC GCR register bits - * @offset: offset of GCR register from GCR address base - * @mask: bit mask for update operation - * @val: update value - * - * Updates the bits of given GCR register as specified by - * @mask and @val. - * - * Return: negative value on error or 0 on success. - */ -static int intel_pmc_gcr_update(u32 offset, u32 mask, u32 val) -{ - u32 new_val; - int ret = 0; - - spin_lock(&ipcdev.gcr_lock); - - ret = is_gcr_valid(offset); - if (ret < 0) - goto gcr_ipc_unlock; - - new_val = readl(ipcdev.gcr_mem_base + offset); - - new_val &= ~mask; - new_val |= val & mask; - - writel(new_val, ipcdev.gcr_mem_base + offset); - - new_val = readl(ipcdev.gcr_mem_base + offset); - - /* check whether the bit update is successful */ - if ((new_val & mask) != (val & mask)) { - ret = -EIO; - goto gcr_ipc_unlock; - } - -gcr_ipc_unlock: - spin_unlock(&ipcdev.gcr_lock); - return ret; -} - -static int update_no_reboot_bit(void *priv, bool set) -{ - u32 value = set ? PMC_CFG_NO_REBOOT_EN : PMC_CFG_NO_REBOOT_DIS; - - return intel_pmc_gcr_update(PMC_GCR_PMC_CFG_REG, - PMC_CFG_NO_REBOOT_MASK, value); -} - -static int intel_pmc_ipc_check_status(void) -{ - int status; - int ret = 0; - - if (ipcdev.irq_mode) { - if (0 == wait_for_completion_timeout( - &ipcdev.cmd_complete, IPC_MAX_SEC * HZ)) - ret = -ETIMEDOUT; - } else { - int loop_count = IPC_LOOP_CNT; - - while ((ipc_read_status() & IPC_STATUS_BUSY) && --loop_count) - udelay(1); - if (loop_count == 0) - ret = -ETIMEDOUT; - } - - status = ipc_read_status(); - if (ret == -ETIMEDOUT) { - dev_err(ipcdev.dev, - "IPC timed out, TS=0x%x, CMD=0x%x\n", - status, ipcdev.cmd); - return ret; - } - - if (status & IPC_STATUS_ERR) { - int i; - - ret = -EIO; - i = (status >> IPC_CMD_SIZE) & 0xFF; - if (i < ARRAY_SIZE(ipc_err_sources)) - dev_err(ipcdev.dev, - "IPC failed: %s, STS=0x%x, CMD=0x%x\n", - ipc_err_sources[i], status, ipcdev.cmd); - else - dev_err(ipcdev.dev, - "IPC failed: unknown, STS=0x%x, CMD=0x%x\n", - status, ipcdev.cmd); - if ((i == IPC_ERR_UNSIGNEDKERNEL) || (i == IPC_ERR_EMSECURITY)) - ret = -EACCES; - } - - return ret; -} - -/** - * intel_pmc_ipc_simple_command() - Simple IPC command - * @cmd: IPC command code. - * @sub: IPC command sub type. - * - * Send a simple IPC command to PMC when don't need to specify - * input/output data and source/dest pointers. - * - * Return: an IPC error code or 0 on success. - */ -static int intel_pmc_ipc_simple_command(int cmd, int sub) -{ - int ret; - - mutex_lock(&ipclock); - if (ipcdev.dev == NULL) { - mutex_unlock(&ipclock); - return -ENODEV; - } - ipc_send_command(sub << IPC_CMD_SUBCMD | cmd); - ret = intel_pmc_ipc_check_status(); - mutex_unlock(&ipclock); - - return ret; -} - -/** - * intel_pmc_ipc_raw_cmd() - IPC command with data and pointers - * @cmd: IPC command code. - * @sub: IPC command sub type. - * @in: input data of this IPC command. - * @inlen: input data length in bytes. - * @out: output data of this IPC command. - * @outlen: output data length in dwords. - * @sptr: data writing to SPTR register. - * @dptr: data writing to DPTR register. - * - * Send an IPC command to PMC with input/output data and source/dest pointers. - * - * Return: an IPC error code or 0 on success. - */ -static int intel_pmc_ipc_raw_cmd(u32 cmd, u32 sub, u8 *in, u32 inlen, u32 *out, - u32 outlen, u32 dptr, u32 sptr) -{ - u32 wbuf[4] = { 0 }; - int ret; - int i; - - if (inlen > IPC_DATA_BUFFER_SIZE || outlen > IPC_DATA_BUFFER_SIZE / 4) - return -EINVAL; - - mutex_lock(&ipclock); - if (ipcdev.dev == NULL) { - mutex_unlock(&ipclock); - return -ENODEV; - } - memcpy(wbuf, in, inlen); - writel(dptr, ipcdev.ipc_base + IPC_DPTR); - writel(sptr, ipcdev.ipc_base + IPC_SPTR); - /* The input data register is 32bit register and inlen is in Byte */ - for (i = 0; i < ((inlen + 3) / 4); i++) - ipc_data_writel(wbuf[i], 4 * i); - ipc_send_command((inlen << IPC_CMD_SIZE) | - (sub << IPC_CMD_SUBCMD) | cmd); - ret = intel_pmc_ipc_check_status(); - if (!ret) { - /* out is read from 32bit register and outlen is in 32bit */ - for (i = 0; i < outlen; i++) - *out++ = ipc_data_readl(4 * i); - } - mutex_unlock(&ipclock); - - return ret; -} - -/** - * intel_pmc_ipc_command() - IPC command with input/output data - * @cmd: IPC command code. - * @sub: IPC command sub type. - * @in: input data of this IPC command. - * @inlen: input data length in bytes. - * @out: output data of this IPC command. - * @outlen: output data length in dwords. - * - * Send an IPC command to PMC with input/output data. - * - * Return: an IPC error code or 0 on success. - */ -int intel_pmc_ipc_command(u32 cmd, u32 sub, u8 *in, u32 inlen, - u32 *out, u32 outlen) -{ - return intel_pmc_ipc_raw_cmd(cmd, sub, in, inlen, out, outlen, 0, 0); -} -EXPORT_SYMBOL_GPL(intel_pmc_ipc_command); - -static irqreturn_t ioc(int irq, void *dev_id) -{ - int status; - - if (ipcdev.irq_mode) { - status = ipc_read_status(); - writel(status | IPC_STATUS_IRQ, ipcdev.ipc_base + IPC_STATUS); - } - complete(&ipcdev.cmd_complete); - - return IRQ_HANDLED; -} - -static int ipc_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id) -{ - struct intel_pmc_ipc_dev *pmc = &ipcdev; - int ret; - - /* Only one PMC is supported */ - if (pmc->dev) - return -EBUSY; - - pmc->irq_mode = IPC_TRIGGER_MODE_IRQ; - - spin_lock_init(&ipcdev.gcr_lock); - - ret = pcim_enable_device(pdev); - if (ret) - return ret; - - ret = pcim_iomap_regions(pdev, 1 << 0, pci_name(pdev)); - if (ret) - return ret; - - init_completion(&pmc->cmd_complete); - - pmc->ipc_base = pcim_iomap_table(pdev)[0]; - - ret = devm_request_irq(&pdev->dev, pdev->irq, ioc, 0, "intel_pmc_ipc", - pmc); - if (ret) { - dev_err(&pdev->dev, "Failed to request irq\n"); - return ret; - } - - pmc->dev = &pdev->dev; - - pci_set_drvdata(pdev, pmc); - - return 0; -} - -static const struct pci_device_id ipc_pci_ids[] = { - {PCI_VDEVICE(INTEL, 0x0a94), 0}, - {PCI_VDEVICE(INTEL, 0x1a94), 0}, - {PCI_VDEVICE(INTEL, 0x5a94), 0}, - { 0,} -}; -MODULE_DEVICE_TABLE(pci, ipc_pci_ids); - -static struct pci_driver ipc_pci_driver = { - .name = "intel_pmc_ipc", - .id_table = ipc_pci_ids, - .probe = ipc_pci_probe, -}; - -static ssize_t intel_pmc_ipc_simple_cmd_store(struct device *dev, - struct device_attribute *attr, - const char *buf, size_t count) -{ - int subcmd; - int cmd; - int ret; - - ret = sscanf(buf, "%d %d", &cmd, &subcmd); - if (ret != 2) { - dev_err(dev, "Error args\n"); - return -EINVAL; - } - - ret = intel_pmc_ipc_simple_command(cmd, subcmd); - if (ret) { - dev_err(dev, "command %d error with %d\n", cmd, ret); - return ret; - } - return (ssize_t)count; -} -static DEVICE_ATTR(simplecmd, 0200, NULL, intel_pmc_ipc_simple_cmd_store); - -static ssize_t intel_pmc_ipc_northpeak_store(struct device *dev, - struct device_attribute *attr, - const char *buf, size_t count) -{ - unsigned long val; - int subcmd; - int ret; - - ret = kstrtoul(buf, 0, &val); - if (ret) - return ret; - - if (val) - subcmd = 1; - else - subcmd = 0; - ret = intel_pmc_ipc_simple_command(PMC_IPC_NORTHPEAK_CTRL, subcmd); - if (ret) { - dev_err(dev, "command north %d error with %d\n", subcmd, ret); - return ret; - } - return (ssize_t)count; -} -static DEVICE_ATTR(northpeak, 0200, NULL, intel_pmc_ipc_northpeak_store); - -static struct attribute *intel_ipc_attrs[] = { - &dev_attr_northpeak.attr, - &dev_attr_simplecmd.attr, - NULL -}; - -static const struct attribute_group intel_ipc_group = { - .attrs = intel_ipc_attrs, -}; - -static const struct attribute_group *intel_ipc_groups[] = { - &intel_ipc_group, - NULL -}; - -static struct resource punit_res_array[] = { - /* Punit BIOS */ - { - .flags = IORESOURCE_MEM, - }, - { - .flags = IORESOURCE_MEM, - }, - /* Punit ISP */ - { - .flags = IORESOURCE_MEM, - }, - { - .flags = IORESOURCE_MEM, - }, - /* Punit GTD */ - { - .flags = IORESOURCE_MEM, - }, - { - .flags = IORESOURCE_MEM, - }, -}; - -#define TCO_RESOURCE_ACPI_IO 0 -#define TCO_RESOURCE_SMI_EN_IO 1 -#define TCO_RESOURCE_GCR_MEM 2 -static struct resource tco_res[] = { - /* ACPI - TCO */ - { - .flags = IORESOURCE_IO, - }, - /* ACPI - SMI */ - { - .flags = IORESOURCE_IO, - }, -}; - -static struct itco_wdt_platform_data tco_info = { - .name = "Apollo Lake SoC", - .version = 5, - .no_reboot_priv = &ipcdev, - .update_no_reboot_bit = update_no_reboot_bit, -}; - -#define TELEMETRY_RESOURCE_PUNIT_SSRAM 0 -#define TELEMETRY_RESOURCE_PMC_SSRAM 1 -static struct resource telemetry_res[] = { - /*Telemetry*/ - { - .flags = IORESOURCE_MEM, - }, - { - .flags = IORESOURCE_MEM, - }, -}; - -static int ipc_create_punit_device(void) -{ - struct platform_device *pdev; - const struct platform_device_info pdevinfo = { - .parent = ipcdev.dev, - .name = PUNIT_DEVICE_NAME, - .id = -1, - .res = punit_res_array, - .num_res = ipcdev.punit_res_count, - }; - - pdev = platform_device_register_full(&pdevinfo); - if (IS_ERR(pdev)) - return PTR_ERR(pdev); - - ipcdev.punit_dev = pdev; - - return 0; -} - -static int ipc_create_tco_device(void) -{ - struct platform_device *pdev; - struct resource *res; - const struct platform_device_info pdevinfo = { - .parent = ipcdev.dev, - .name = TCO_DEVICE_NAME, - .id = -1, - .res = tco_res, - .num_res = ARRAY_SIZE(tco_res), - .data = &tco_info, - .size_data = sizeof(tco_info), - }; - - res = tco_res + TCO_RESOURCE_ACPI_IO; - res->start = ipcdev.acpi_io_base + TCO_BASE_OFFSET; - res->end = res->start + TCO_REGS_SIZE - 1; - - res = tco_res + TCO_RESOURCE_SMI_EN_IO; - res->start = ipcdev.acpi_io_base + SMI_EN_OFFSET; - res->end = res->start + SMI_EN_SIZE - 1; - - pdev = platform_device_register_full(&pdevinfo); - if (IS_ERR(pdev)) - return PTR_ERR(pdev); - - ipcdev.tco_dev = pdev; - - return 0; -} - -static int ipc_create_telemetry_device(void) -{ - struct platform_device *pdev; - struct resource *res; - const struct platform_device_info pdevinfo = { - .parent = ipcdev.dev, - .name = TELEMETRY_DEVICE_NAME, - .id = -1, - .res = telemetry_res, - .num_res = ARRAY_SIZE(telemetry_res), - }; - - res = telemetry_res + TELEMETRY_RESOURCE_PUNIT_SSRAM; - res->start = ipcdev.telem_punit_ssram_base; - res->end = res->start + ipcdev.telem_punit_ssram_size - 1; - - res = telemetry_res + TELEMETRY_RESOURCE_PMC_SSRAM; - res->start = ipcdev.telem_pmc_ssram_base; - res->end = res->start + ipcdev.telem_pmc_ssram_size - 1; - - pdev = platform_device_register_full(&pdevinfo); - if (IS_ERR(pdev)) - return PTR_ERR(pdev); - - ipcdev.telemetry_dev = pdev; - - return 0; -} - -static int ipc_create_pmc_devices(void) -{ - int ret; - - /* If we have ACPI based watchdog use that instead */ - if (!acpi_has_watchdog()) { - ret = ipc_create_tco_device(); - if (ret) { - dev_err(ipcdev.dev, "Failed to add tco platform device\n"); - return ret; - } - } - - ret = ipc_create_punit_device(); - if (ret) { - dev_err(ipcdev.dev, "Failed to add punit platform device\n"); - platform_device_unregister(ipcdev.tco_dev); - return ret; - } - - if (!ipcdev.telem_res_inval) { - ret = ipc_create_telemetry_device(); - if (ret) { - dev_warn(ipcdev.dev, - "Failed to add telemetry platform device\n"); - platform_device_unregister(ipcdev.punit_dev); - platform_device_unregister(ipcdev.tco_dev); - } - } - - return ret; -} - -static int ipc_plat_get_res(struct platform_device *pdev) -{ - struct resource *res, *punit_res = punit_res_array; - void __iomem *addr; - int size; - - res = platform_get_resource(pdev, IORESOURCE_IO, - PLAT_RESOURCE_ACPI_IO_INDEX); - if (!res) { - dev_err(&pdev->dev, "Failed to get io resource\n"); - return -ENXIO; - } - size = resource_size(res); - ipcdev.acpi_io_base = res->start; - ipcdev.acpi_io_size = size; - dev_info(&pdev->dev, "io res: %pR\n", res); - - ipcdev.punit_res_count = 0; - - /* This is index 0 to cover BIOS data register */ - res = platform_get_resource(pdev, IORESOURCE_MEM, - PLAT_RESOURCE_BIOS_DATA_INDEX); - if (!res) { - dev_err(&pdev->dev, "Failed to get res of punit BIOS data\n"); - return -ENXIO; - } - punit_res[ipcdev.punit_res_count++] = *res; - dev_info(&pdev->dev, "punit BIOS data res: %pR\n", res); - - /* This is index 1 to cover BIOS interface register */ - res = platform_get_resource(pdev, IORESOURCE_MEM, - PLAT_RESOURCE_BIOS_IFACE_INDEX); - if (!res) { - dev_err(&pdev->dev, "Failed to get res of punit BIOS iface\n"); - return -ENXIO; - } - punit_res[ipcdev.punit_res_count++] = *res; - dev_info(&pdev->dev, "punit BIOS interface res: %pR\n", res); - - /* This is index 2 to cover ISP data register, optional */ - res = platform_get_resource(pdev, IORESOURCE_MEM, - PLAT_RESOURCE_ISP_DATA_INDEX); - if (res) { - punit_res[ipcdev.punit_res_count++] = *res; - dev_info(&pdev->dev, "punit ISP data res: %pR\n", res); - } - - /* This is index 3 to cover ISP interface register, optional */ - res = platform_get_resource(pdev, IORESOURCE_MEM, - PLAT_RESOURCE_ISP_IFACE_INDEX); - if (res) { - punit_res[ipcdev.punit_res_count++] = *res; - dev_info(&pdev->dev, "punit ISP interface res: %pR\n", res); - } - - /* This is index 4 to cover GTD data register, optional */ - res = platform_get_resource(pdev, IORESOURCE_MEM, - PLAT_RESOURCE_GTD_DATA_INDEX); - if (res) { - punit_res[ipcdev.punit_res_count++] = *res; - dev_info(&pdev->dev, "punit GTD data res: %pR\n", res); - } - - /* This is index 5 to cover GTD interface register, optional */ - res = platform_get_resource(pdev, IORESOURCE_MEM, - PLAT_RESOURCE_GTD_IFACE_INDEX); - if (res) { - punit_res[ipcdev.punit_res_count++] = *res; - dev_info(&pdev->dev, "punit GTD interface res: %pR\n", res); - } - - res = platform_get_resource(pdev, IORESOURCE_MEM, - PLAT_RESOURCE_IPC_INDEX); - if (!res) { - dev_err(&pdev->dev, "Failed to get ipc resource\n"); - return -ENXIO; - } - size = PLAT_RESOURCE_IPC_SIZE + PLAT_RESOURCE_GCR_SIZE; - res->end = res->start + size - 1; - - addr = devm_ioremap_resource(&pdev->dev, res); - if (IS_ERR(addr)) - return PTR_ERR(addr); - - ipcdev.ipc_base = addr; - - ipcdev.gcr_mem_base = addr + PLAT_RESOURCE_GCR_OFFSET; - dev_info(&pdev->dev, "ipc res: %pR\n", res); - - ipcdev.telem_res_inval = 0; - res = platform_get_resource(pdev, IORESOURCE_MEM, - PLAT_RESOURCE_TELEM_SSRAM_INDEX); - if (!res) { - dev_err(&pdev->dev, "Failed to get telemetry ssram resource\n"); - ipcdev.telem_res_inval = 1; - } else { - ipcdev.telem_punit_ssram_base = res->start + - TELEM_PUNIT_SSRAM_OFFSET; - ipcdev.telem_punit_ssram_size = TELEM_SSRAM_SIZE; - ipcdev.telem_pmc_ssram_base = res->start + - TELEM_PMC_SSRAM_OFFSET; - ipcdev.telem_pmc_ssram_size = TELEM_SSRAM_SIZE; - dev_info(&pdev->dev, "telemetry ssram res: %pR\n", res); - } - - return 0; -} - -/** - * intel_pmc_s0ix_counter_read() - Read S0ix residency. - * @data: Out param that contains current S0ix residency count. - * - * Return: an error code or 0 on success. - */ -int intel_pmc_s0ix_counter_read(u64 *data) -{ - u64 deep, shlw; - - if (!ipcdev.has_gcr_regs) - return -EACCES; - - deep = gcr_data_readq(PMC_GCR_TELEM_DEEP_S0IX_REG); - shlw = gcr_data_readq(PMC_GCR_TELEM_SHLW_S0IX_REG); - - *data = S0IX_RESIDENCY_IN_USECS(deep, shlw); - - return 0; -} -EXPORT_SYMBOL_GPL(intel_pmc_s0ix_counter_read); - -#ifdef CONFIG_ACPI -static const struct acpi_device_id ipc_acpi_ids[] = { - { "INT34D2", 0}, - { } -}; -MODULE_DEVICE_TABLE(acpi, ipc_acpi_ids); -#endif - -static int ipc_plat_probe(struct platform_device *pdev) -{ - int ret; - - ipcdev.dev = &pdev->dev; - ipcdev.irq_mode = IPC_TRIGGER_MODE_IRQ; - init_completion(&ipcdev.cmd_complete); - spin_lock_init(&ipcdev.gcr_lock); - - ipcdev.irq = platform_get_irq(pdev, 0); - if (ipcdev.irq < 0) - return -EINVAL; - - ret = ipc_plat_get_res(pdev); - if (ret) { - dev_err(&pdev->dev, "Failed to request resource\n"); - return ret; - } - - ret = ipc_create_pmc_devices(); - if (ret) { - dev_err(&pdev->dev, "Failed to create pmc devices\n"); - return ret; - } - - if (devm_request_irq(&pdev->dev, ipcdev.irq, ioc, IRQF_NO_SUSPEND, - "intel_pmc_ipc", &ipcdev)) { - dev_err(&pdev->dev, "Failed to request irq\n"); - ret = -EBUSY; - goto err_irq; - } - - ipcdev.has_gcr_regs = true; - - return 0; - -err_irq: - platform_device_unregister(ipcdev.tco_dev); - platform_device_unregister(ipcdev.punit_dev); - platform_device_unregister(ipcdev.telemetry_dev); - - return ret; -} - -static int ipc_plat_remove(struct platform_device *pdev) -{ - devm_free_irq(&pdev->dev, ipcdev.irq, &ipcdev); - platform_device_unregister(ipcdev.tco_dev); - platform_device_unregister(ipcdev.punit_dev); - platform_device_unregister(ipcdev.telemetry_dev); - ipcdev.dev = NULL; - return 0; -} - -static struct platform_driver ipc_plat_driver = { - .remove = ipc_plat_remove, - .probe = ipc_plat_probe, - .driver = { - .name = "pmc-ipc-plat", - .acpi_match_table = ACPI_PTR(ipc_acpi_ids), - .dev_groups = intel_ipc_groups, - }, -}; - -static int __init intel_pmc_ipc_init(void) -{ - int ret; - - ret = platform_driver_register(&ipc_plat_driver); - if (ret) { - pr_err("Failed to register PMC ipc platform driver\n"); - return ret; - } - ret = pci_register_driver(&ipc_pci_driver); - if (ret) { - pr_err("Failed to register PMC ipc pci driver\n"); - platform_driver_unregister(&ipc_plat_driver); - return ret; - } - return ret; -} - -static void __exit intel_pmc_ipc_exit(void) -{ - pci_unregister_driver(&ipc_pci_driver); - platform_driver_unregister(&ipc_plat_driver); -} - -MODULE_AUTHOR("Zha Qipeng <qipeng.zha@intel.com>"); -MODULE_DESCRIPTION("Intel PMC IPC driver"); -MODULE_LICENSE("GPL v2"); - -/* Some modules are dependent on this, so init earlier */ -fs_initcall(intel_pmc_ipc_init); -module_exit(intel_pmc_ipc_exit); diff --git a/drivers/platform/x86/intel_scu_ipc.c b/drivers/platform/x86/intel_scu_ipc.c index 3d7da5266136..d9cf7f7602b0 100644 --- a/drivers/platform/x86/intel_scu_ipc.c +++ b/drivers/platform/x86/intel_scu_ipc.c @@ -18,11 +18,10 @@ #include <linux/errno.h> #include <linux/init.h> #include <linux/interrupt.h> -#include <linux/pci.h> -#include <linux/pm.h> -#include <linux/sfi.h> +#include <linux/io.h> +#include <linux/module.h> +#include <linux/slab.h> -#include <asm/intel-mid.h> #include <asm/intel_scu_ipc.h> /* IPC defines the following message types */ @@ -55,14 +54,14 @@ #define IPC_IOC 0x100 /* IPC command register IOC bit */ struct intel_scu_ipc_dev { - struct device *dev; + struct device dev; + struct resource mem; + struct module *owner; + int irq; void __iomem *ipc_base; struct completion cmd_complete; - u8 irq_mode; }; -static struct intel_scu_ipc_dev ipcdev; /* Only one for now */ - #define IPC_STATUS 0x04 #define IPC_STATUS_IRQ BIT(2) #define IPC_STATUS_ERR BIT(1) @@ -78,8 +77,110 @@ static struct intel_scu_ipc_dev ipcdev; /* Only one for now */ /* Timeout in jiffies */ #define IPC_TIMEOUT (3 * HZ) +static struct intel_scu_ipc_dev *ipcdev; /* Only one for now */ static DEFINE_MUTEX(ipclock); /* lock used to prevent multiple call to SCU */ +static struct class intel_scu_ipc_class = { + .name = "intel_scu_ipc", + .owner = THIS_MODULE, +}; + +/** + * intel_scu_ipc_dev_get() - Get SCU IPC instance + * + * The recommended new API takes SCU IPC instance as parameter and this + * function can be called by driver to get the instance. This also makes + * sure the driver providing the IPC functionality cannot be unloaded + * while the caller has the instance. + * + * Call intel_scu_ipc_dev_put() to release the instance. + * + * Returns %NULL if SCU IPC is not currently available. + */ +struct intel_scu_ipc_dev *intel_scu_ipc_dev_get(void) +{ + struct intel_scu_ipc_dev *scu = NULL; + + mutex_lock(&ipclock); + if (ipcdev) { + get_device(&ipcdev->dev); + /* + * Prevent the IPC provider from being unloaded while it + * is being used. + */ + if (!try_module_get(ipcdev->owner)) + put_device(&ipcdev->dev); + else + scu = ipcdev; + } + + mutex_unlock(&ipclock); + return scu; +} +EXPORT_SYMBOL_GPL(intel_scu_ipc_dev_get); + +/** + * intel_scu_ipc_dev_put() - Put SCU IPC instance + * @scu: SCU IPC instance + * + * This function releases the SCU IPC instance retrieved from + * intel_scu_ipc_dev_get() and allows the driver providing IPC to be + * unloaded. + */ +void intel_scu_ipc_dev_put(struct intel_scu_ipc_dev *scu) +{ + if (scu) { + module_put(scu->owner); + put_device(&scu->dev); + } +} +EXPORT_SYMBOL_GPL(intel_scu_ipc_dev_put); + +struct intel_scu_ipc_devres { + struct intel_scu_ipc_dev *scu; +}; + +static void devm_intel_scu_ipc_dev_release(struct device *dev, void *res) +{ + struct intel_scu_ipc_devres *dr = res; + struct intel_scu_ipc_dev *scu = dr->scu; + + intel_scu_ipc_dev_put(scu); +} + +/** + * devm_intel_scu_ipc_dev_get() - Allocate managed SCU IPC device + * @dev: Device requesting the SCU IPC device + * + * The recommended new API takes SCU IPC instance as parameter and this + * function can be called by driver to get the instance. This also makes + * sure the driver providing the IPC functionality cannot be unloaded + * while the caller has the instance. + * + * Returns %NULL if SCU IPC is not currently available. + */ +struct intel_scu_ipc_dev *devm_intel_scu_ipc_dev_get(struct device *dev) +{ + struct intel_scu_ipc_devres *dr; + struct intel_scu_ipc_dev *scu; + + dr = devres_alloc(devm_intel_scu_ipc_dev_release, sizeof(*dr), GFP_KERNEL); + if (!dr) + return NULL; + + scu = intel_scu_ipc_dev_get(); + if (!scu) { + devres_free(dr); + return NULL; + } + + dr->scu = scu; + devres_add(dev, dr); + + return scu; +} +EXPORT_SYMBOL_GPL(devm_intel_scu_ipc_dev_get); + /* * Send ipc command * Command Register (Write Only): @@ -143,7 +244,6 @@ static inline int busy_loop(struct intel_scu_ipc_dev *scu) usleep_range(50, 100); } while (time_before(jiffies, end)); - dev_err(scu->dev, "IPC timed out"); return -ETIMEDOUT; } @@ -152,10 +252,8 @@ static inline int ipc_wait_for_interrupt(struct intel_scu_ipc_dev *scu) { int status; - if (!wait_for_completion_timeout(&scu->cmd_complete, IPC_TIMEOUT)) { - dev_err(scu->dev, "IPC timed out\n"); + if (!wait_for_completion_timeout(&scu->cmd_complete, IPC_TIMEOUT)) return -ETIMEDOUT; - } status = ipc_read_status(scu); if (status & IPC_STATUS_ERR) @@ -166,13 +264,13 @@ static inline int ipc_wait_for_interrupt(struct intel_scu_ipc_dev *scu) static int intel_scu_ipc_check_status(struct intel_scu_ipc_dev *scu) { - return scu->irq_mode ? ipc_wait_for_interrupt(scu) : busy_loop(scu); + return scu->irq > 0 ? ipc_wait_for_interrupt(scu) : busy_loop(scu); } /* Read/Write power control(PMIC in Langwell, MSIC in PenWell) registers */ -static int pwr_reg_rdwr(u16 *addr, u8 *data, u32 count, u32 op, u32 id) +static int pwr_reg_rdwr(struct intel_scu_ipc_dev *scu, u16 *addr, u8 *data, + u32 count, u32 op, u32 id) { - struct intel_scu_ipc_dev *scu = &ipcdev; int nc; u32 offset = 0; int err; @@ -182,8 +280,9 @@ static int pwr_reg_rdwr(u16 *addr, u8 *data, u32 count, u32 op, u32 id) memset(cbuf, 0, sizeof(cbuf)); mutex_lock(&ipclock); - - if (scu->dev == NULL) { + if (!scu) + scu = ipcdev; + if (!scu) { mutex_unlock(&ipclock); return -ENODEV; } @@ -222,7 +321,8 @@ static int pwr_reg_rdwr(u16 *addr, u8 *data, u32 count, u32 op, u32 id) } /** - * intel_scu_ipc_ioread8 - read a word via the SCU + * intel_scu_ipc_dev_ioread8() - Read a byte via the SCU + * @scu: Optional SCU IPC instance * @addr: Register on SCU * @data: Return pointer for read byte * @@ -231,14 +331,15 @@ static int pwr_reg_rdwr(u16 *addr, u8 *data, u32 count, u32 op, u32 id) * * This function may sleep. */ -int intel_scu_ipc_ioread8(u16 addr, u8 *data) +int intel_scu_ipc_dev_ioread8(struct intel_scu_ipc_dev *scu, u16 addr, u8 *data) { - return pwr_reg_rdwr(&addr, data, 1, IPCMSG_PCNTRL, IPC_CMD_PCNTRL_R); + return pwr_reg_rdwr(scu, &addr, data, 1, IPCMSG_PCNTRL, IPC_CMD_PCNTRL_R); } -EXPORT_SYMBOL(intel_scu_ipc_ioread8); +EXPORT_SYMBOL(intel_scu_ipc_dev_ioread8); /** - * intel_scu_ipc_iowrite8 - write a byte via the SCU + * intel_scu_ipc_dev_iowrite8() - Write a byte via the SCU + * @scu: Optional SCU IPC instance * @addr: Register on SCU * @data: Byte to write * @@ -247,14 +348,15 @@ EXPORT_SYMBOL(intel_scu_ipc_ioread8); * * This function may sleep. */ -int intel_scu_ipc_iowrite8(u16 addr, u8 data) +int intel_scu_ipc_dev_iowrite8(struct intel_scu_ipc_dev *scu, u16 addr, u8 data) { - return pwr_reg_rdwr(&addr, &data, 1, IPCMSG_PCNTRL, IPC_CMD_PCNTRL_W); + return pwr_reg_rdwr(scu, &addr, &data, 1, IPCMSG_PCNTRL, IPC_CMD_PCNTRL_W); } -EXPORT_SYMBOL(intel_scu_ipc_iowrite8); +EXPORT_SYMBOL(intel_scu_ipc_dev_iowrite8); /** - * intel_scu_ipc_readvv - read a set of registers + * intel_scu_ipc_dev_readv() - Read a set of registers + * @scu: Optional SCU IPC instance * @addr: Register list * @data: Bytes to return * @len: Length of array @@ -266,14 +368,16 @@ EXPORT_SYMBOL(intel_scu_ipc_iowrite8); * * This function may sleep. */ -int intel_scu_ipc_readv(u16 *addr, u8 *data, int len) +int intel_scu_ipc_dev_readv(struct intel_scu_ipc_dev *scu, u16 *addr, u8 *data, + size_t len) { - return pwr_reg_rdwr(addr, data, len, IPCMSG_PCNTRL, IPC_CMD_PCNTRL_R); + return pwr_reg_rdwr(scu, addr, data, len, IPCMSG_PCNTRL, IPC_CMD_PCNTRL_R); } -EXPORT_SYMBOL(intel_scu_ipc_readv); +EXPORT_SYMBOL(intel_scu_ipc_dev_readv); /** - * intel_scu_ipc_writev - write a set of registers + * intel_scu_ipc_dev_writev() - Write a set of registers + * @scu: Optional SCU IPC instance * @addr: Register list * @data: Bytes to write * @len: Length of array @@ -285,16 +389,18 @@ EXPORT_SYMBOL(intel_scu_ipc_readv); * * This function may sleep. */ -int intel_scu_ipc_writev(u16 *addr, u8 *data, int len) +int intel_scu_ipc_dev_writev(struct intel_scu_ipc_dev *scu, u16 *addr, u8 *data, + size_t len) { - return pwr_reg_rdwr(addr, data, len, IPCMSG_PCNTRL, IPC_CMD_PCNTRL_W); + return pwr_reg_rdwr(scu, addr, data, len, IPCMSG_PCNTRL, IPC_CMD_PCNTRL_W); } -EXPORT_SYMBOL(intel_scu_ipc_writev); +EXPORT_SYMBOL(intel_scu_ipc_dev_writev); /** - * intel_scu_ipc_update_register - r/m/w a register + * intel_scu_ipc_dev_update() - Update a register + * @scu: Optional SCU IPC instance * @addr: Register address - * @bits: Bits to update + * @data: Bits to update * @mask: Mask of bits to update * * Read-modify-write power control unit register. The first data argument @@ -305,15 +411,17 @@ EXPORT_SYMBOL(intel_scu_ipc_writev); * This function may sleep. Locking between SCU accesses is handled * for the caller. */ -int intel_scu_ipc_update_register(u16 addr, u8 bits, u8 mask) +int intel_scu_ipc_dev_update(struct intel_scu_ipc_dev *scu, u16 addr, u8 data, + u8 mask) { - u8 data[2] = { bits, mask }; - return pwr_reg_rdwr(&addr, data, 1, IPCMSG_PCNTRL, IPC_CMD_PCNTRL_M); + u8 tmp[2] = { data, mask }; + return pwr_reg_rdwr(scu, &addr, tmp, 1, IPCMSG_PCNTRL, IPC_CMD_PCNTRL_M); } -EXPORT_SYMBOL(intel_scu_ipc_update_register); +EXPORT_SYMBOL(intel_scu_ipc_dev_update); /** - * intel_scu_ipc_simple_command - send a simple command + * intel_scu_ipc_dev_simple_command() - Send a simple command + * @scu: Optional SCU IPC instance * @cmd: Command * @sub: Sub type * @@ -324,62 +432,89 @@ EXPORT_SYMBOL(intel_scu_ipc_update_register); * This function may sleep. Locking for SCU accesses is handled for the * caller. */ -int intel_scu_ipc_simple_command(int cmd, int sub) +int intel_scu_ipc_dev_simple_command(struct intel_scu_ipc_dev *scu, int cmd, + int sub) { - struct intel_scu_ipc_dev *scu = &ipcdev; + u32 cmdval; int err; mutex_lock(&ipclock); - if (scu->dev == NULL) { + if (!scu) + scu = ipcdev; + if (!scu) { mutex_unlock(&ipclock); return -ENODEV; } - ipc_command(scu, sub << 12 | cmd); + scu = ipcdev; + cmdval = sub << 12 | cmd; + ipc_command(scu, cmdval); err = intel_scu_ipc_check_status(scu); mutex_unlock(&ipclock); + if (err) + dev_err(&scu->dev, "IPC command %#x failed with %d\n", cmdval, err); return err; } -EXPORT_SYMBOL(intel_scu_ipc_simple_command); +EXPORT_SYMBOL(intel_scu_ipc_dev_simple_command); /** - * intel_scu_ipc_command - command with data + * intel_scu_ipc_command_with_size() - Command with data + * @scu: Optional SCU IPC instance * @cmd: Command * @sub: Sub type * @in: Input data - * @inlen: Input length in dwords + * @inlen: Input length in bytes + * @size: Input size written to the IPC command register in whatever + * units (dword, byte) the particular firmware requires. Normally + * should be the same as @inlen. * @out: Output data - * @outlen: Output length in dwords + * @outlen: Output length in bytes * * Issue a command to the SCU which involves data transfers. Do the * data copies under the lock but leave it for the caller to interpret. */ -int intel_scu_ipc_command(int cmd, int sub, u32 *in, int inlen, - u32 *out, int outlen) +int intel_scu_ipc_dev_command_with_size(struct intel_scu_ipc_dev *scu, int cmd, + int sub, const void *in, size_t inlen, + size_t size, void *out, size_t outlen) { - struct intel_scu_ipc_dev *scu = &ipcdev; + size_t outbuflen = DIV_ROUND_UP(outlen, sizeof(u32)); + size_t inbuflen = DIV_ROUND_UP(inlen, sizeof(u32)); + u32 cmdval, inbuf[4] = {}; int i, err; + if (inbuflen > 4 || outbuflen > 4) + return -EINVAL; + mutex_lock(&ipclock); - if (scu->dev == NULL) { + if (!scu) + scu = ipcdev; + if (!scu) { mutex_unlock(&ipclock); return -ENODEV; } - for (i = 0; i < inlen; i++) - ipc_data_writel(scu, *in++, 4 * i); + memcpy(inbuf, in, inlen); + for (i = 0; i < inbuflen; i++) + ipc_data_writel(scu, inbuf[i], 4 * i); - ipc_command(scu, (inlen << 16) | (sub << 12) | cmd); + cmdval = (size << 16) | (sub << 12) | cmd; + ipc_command(scu, cmdval); err = intel_scu_ipc_check_status(scu); if (!err) { - for (i = 0; i < outlen; i++) - *out++ = ipc_data_readl(scu, 4 * i); + u32 outbuf[4] = {}; + + for (i = 0; i < outbuflen; i++) + outbuf[i] = ipc_data_readl(scu, 4 * i); + + memcpy(out, outbuf, outlen); } mutex_unlock(&ipclock); + if (err) + dev_err(&scu->dev, "IPC command %#x failed with %d\n", cmdval, err); return err; } -EXPORT_SYMBOL(intel_scu_ipc_command); +EXPORT_SYMBOL(intel_scu_ipc_dev_command_with_size); /* * Interrupt handler gets called when ioc bit of IPC_COMMAND_REG set to 1 @@ -399,61 +534,179 @@ static irqreturn_t ioc(int irq, void *dev_id) return IRQ_HANDLED; } +static void intel_scu_ipc_release(struct device *dev) +{ + struct intel_scu_ipc_dev *scu; + + scu = container_of(dev, struct intel_scu_ipc_dev, dev); + if (scu->irq > 0) + free_irq(scu->irq, scu); + iounmap(scu->ipc_base); + release_mem_region(scu->mem.start, resource_size(&scu->mem)); + kfree(scu); +} + /** - * ipc_probe - probe an Intel SCU IPC - * @pdev: the PCI device matching - * @id: entry in the match table + * __intel_scu_ipc_register() - Register SCU IPC device + * @parent: Parent device + * @scu_data: Data used to configure SCU IPC + * @owner: Module registering the SCU IPC device * - * Enable and install an intel SCU IPC. This appears in the PCI space - * but uses some hard coded addresses as well. + * Call this function to register SCU IPC mechanism under @parent. + * Returns pointer to the new SCU IPC device or ERR_PTR() in case of + * failure. The caller may use the returned instance if it needs to do + * SCU IPC calls itself. */ -static int ipc_probe(struct pci_dev *pdev, const struct pci_device_id *id) +struct intel_scu_ipc_dev * +__intel_scu_ipc_register(struct device *parent, + const struct intel_scu_ipc_data *scu_data, + struct module *owner) { int err; - struct intel_scu_ipc_dev *scu = &ipcdev; + struct intel_scu_ipc_dev *scu; + void __iomem *ipc_base; - if (scu->dev) /* We support only one SCU */ - return -EBUSY; + mutex_lock(&ipclock); + /* We support only one IPC */ + if (ipcdev) { + err = -EBUSY; + goto err_unlock; + } - err = pcim_enable_device(pdev); - if (err) - return err; + scu = kzalloc(sizeof(*scu), GFP_KERNEL); + if (!scu) { + err = -ENOMEM; + goto err_unlock; + } - err = pcim_iomap_regions(pdev, 1 << 0, pci_name(pdev)); - if (err) - return err; + scu->owner = owner; + scu->dev.parent = parent; + scu->dev.class = &intel_scu_ipc_class; + scu->dev.release = intel_scu_ipc_release; + dev_set_name(&scu->dev, "intel_scu_ipc"); + + if (!request_mem_region(scu_data->mem.start, resource_size(&scu_data->mem), + "intel_scu_ipc")) { + err = -EBUSY; + goto err_free; + } + ipc_base = ioremap(scu_data->mem.start, resource_size(&scu_data->mem)); + if (!ipc_base) { + err = -ENOMEM; + goto err_release; + } + + scu->ipc_base = ipc_base; + scu->mem = scu_data->mem; + scu->irq = scu_data->irq; init_completion(&scu->cmd_complete); - scu->ipc_base = pcim_iomap_table(pdev)[0]; + if (scu->irq > 0) { + err = request_irq(scu->irq, ioc, 0, "intel_scu_ipc", scu); + if (err) + goto err_unmap; + } - err = devm_request_irq(&pdev->dev, pdev->irq, ioc, 0, "intel_scu_ipc", - scu); - if (err) - return err; + /* + * After this point intel_scu_ipc_release() takes care of + * releasing the SCU IPC resources once refcount drops to zero. + */ + err = device_register(&scu->dev); + if (err) { + put_device(&scu->dev); + goto err_unlock; + } /* Assign device at last */ - scu->dev = &pdev->dev; + ipcdev = scu; + mutex_unlock(&ipclock); - intel_scu_devices_create(); + return scu; - pci_set_drvdata(pdev, scu); - return 0; +err_unmap: + iounmap(ipc_base); +err_release: + release_mem_region(scu_data->mem.start, resource_size(&scu_data->mem)); +err_free: + kfree(scu); +err_unlock: + mutex_unlock(&ipclock); + + return ERR_PTR(err); } +EXPORT_SYMBOL_GPL(__intel_scu_ipc_register); -static const struct pci_device_id pci_ids[] = { - { PCI_VDEVICE(INTEL, 0x080e) }, - { PCI_VDEVICE(INTEL, 0x08ea) }, - { PCI_VDEVICE(INTEL, 0x11a0) }, - {} -}; +/** + * intel_scu_ipc_unregister() - Unregister SCU IPC + * @scu: SCU IPC handle + * + * This unregisters the SCU IPC device and releases the acquired + * resources once the refcount goes to zero. + */ +void intel_scu_ipc_unregister(struct intel_scu_ipc_dev *scu) +{ + mutex_lock(&ipclock); + if (!WARN_ON(!ipcdev)) { + ipcdev = NULL; + device_unregister(&scu->dev); + } + mutex_unlock(&ipclock); +} +EXPORT_SYMBOL_GPL(intel_scu_ipc_unregister); -static struct pci_driver ipc_driver = { - .driver = { - .suppress_bind_attrs = true, - }, - .name = "intel_scu_ipc", - .id_table = pci_ids, - .probe = ipc_probe, -}; -builtin_pci_driver(ipc_driver); +static void devm_intel_scu_ipc_unregister(struct device *dev, void *res) +{ + struct intel_scu_ipc_devres *dr = res; + struct intel_scu_ipc_dev *scu = dr->scu; + + intel_scu_ipc_unregister(scu); +} + +/** + * __devm_intel_scu_ipc_register() - Register managed SCU IPC device + * @parent: Parent device + * @scu_data: Data used to configure SCU IPC + * @owner: Module registering the SCU IPC device + * + * Call this function to register managed SCU IPC mechanism under + * @parent. Returns pointer to the new SCU IPC device or ERR_PTR() in + * case of failure. The caller may use the returned instance if it needs + * to do SCU IPC calls itself. + */ +struct intel_scu_ipc_dev * +__devm_intel_scu_ipc_register(struct device *parent, + const struct intel_scu_ipc_data *scu_data, + struct module *owner) +{ + struct intel_scu_ipc_devres *dr; + struct intel_scu_ipc_dev *scu; + + dr = devres_alloc(devm_intel_scu_ipc_unregister, sizeof(*dr), GFP_KERNEL); + if (!dr) + return NULL; + + scu = __intel_scu_ipc_register(parent, scu_data, owner); + if (IS_ERR(scu)) { + devres_free(dr); + return scu; + } + + dr->scu = scu; + devres_add(parent, dr); + + return scu; +} +EXPORT_SYMBOL_GPL(__devm_intel_scu_ipc_register); + +static int __init intel_scu_ipc_init(void) +{ + return class_register(&intel_scu_ipc_class); +} +subsys_initcall(intel_scu_ipc_init); + +static void __exit intel_scu_ipc_exit(void) +{ + class_unregister(&intel_scu_ipc_class); +} +module_exit(intel_scu_ipc_exit); diff --git a/drivers/platform/x86/intel_scu_ipcutil.c b/drivers/platform/x86/intel_scu_ipcutil.c index 8afe6fa06d7b..b7c10c15a3d6 100644 --- a/drivers/platform/x86/intel_scu_ipcutil.c +++ b/drivers/platform/x86/intel_scu_ipcutil.c @@ -22,6 +22,9 @@ static int major; +struct intel_scu_ipc_dev *scu; +static DEFINE_MUTEX(scu_lock); + /* IOCTL commands */ #define INTE_SCU_IPC_REGISTER_READ 0 #define INTE_SCU_IPC_REGISTER_WRITE 1 @@ -52,12 +55,12 @@ static int scu_reg_access(u32 cmd, struct scu_ipc_data *data) switch (cmd) { case INTE_SCU_IPC_REGISTER_READ: - return intel_scu_ipc_readv(data->addr, data->data, count); + return intel_scu_ipc_dev_readv(scu, data->addr, data->data, count); case INTE_SCU_IPC_REGISTER_WRITE: - return intel_scu_ipc_writev(data->addr, data->data, count); + return intel_scu_ipc_dev_writev(scu, data->addr, data->data, count); case INTE_SCU_IPC_REGISTER_UPDATE: - return intel_scu_ipc_update_register(data->addr[0], - data->data[0], data->mask); + return intel_scu_ipc_dev_update(scu, data->addr[0], data->data[0], + data->mask); default: return -ENOTTY; } @@ -91,8 +94,40 @@ static long scu_ipc_ioctl(struct file *fp, unsigned int cmd, return 0; } +static int scu_ipc_open(struct inode *inode, struct file *file) +{ + int ret = 0; + + /* Only single open at the time */ + mutex_lock(&scu_lock); + if (scu) { + ret = -EBUSY; + goto unlock; + } + + scu = intel_scu_ipc_dev_get(); + if (!scu) + ret = -ENODEV; + +unlock: + mutex_unlock(&scu_lock); + return ret; +} + +static int scu_ipc_release(struct inode *inode, struct file *file) +{ + mutex_lock(&scu_lock); + intel_scu_ipc_dev_put(scu); + scu = NULL; + mutex_unlock(&scu_lock); + + return 0; +} + static const struct file_operations scu_ipc_fops = { .unlocked_ioctl = scu_ipc_ioctl, + .open = scu_ipc_open, + .release = scu_ipc_release, }; static int __init ipc_module_init(void) diff --git a/drivers/platform/x86/intel_scu_pcidrv.c b/drivers/platform/x86/intel_scu_pcidrv.c new file mode 100644 index 000000000000..8c5fd8240da9 --- /dev/null +++ b/drivers/platform/x86/intel_scu_pcidrv.c @@ -0,0 +1,68 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * PCI driver for the Intel SCU. + * + * Copyright (C) 2008-2010, 2015, 2020 Intel Corporation + * Authors: Sreedhara DS (sreedhara.ds@intel.com) + * Mika Westerberg <mika.westerberg@linux.intel.com> + */ + +#include <linux/errno.h> +#include <linux/init.h> +#include <linux/pci.h> + +#include <asm/intel-mid.h> +#include <asm/intel_scu_ipc.h> + +static int intel_scu_pci_probe(struct pci_dev *pdev, + const struct pci_device_id *id) +{ + void (*setup_fn)(void) = (void (*)(void))id->driver_data; + struct intel_scu_ipc_data scu_data = {}; + struct intel_scu_ipc_dev *scu; + int ret; + + ret = pcim_enable_device(pdev); + if (ret) + return ret; + + scu_data.mem = pdev->resource[0]; + scu_data.irq = pdev->irq; + + scu = intel_scu_ipc_register(&pdev->dev, &scu_data); + if (IS_ERR(scu)) + return PTR_ERR(scu); + + if (setup_fn) + setup_fn(); + return 0; +} + +static void intel_mid_scu_setup(void) +{ + intel_scu_devices_create(); +} + +static const struct pci_device_id pci_ids[] = { + { PCI_VDEVICE(INTEL, 0x080e), + .driver_data = (kernel_ulong_t)intel_mid_scu_setup }, + { PCI_VDEVICE(INTEL, 0x08ea), + .driver_data = (kernel_ulong_t)intel_mid_scu_setup }, + { PCI_VDEVICE(INTEL, 0x0a94) }, + { PCI_VDEVICE(INTEL, 0x11a0), + .driver_data = (kernel_ulong_t)intel_mid_scu_setup }, + { PCI_VDEVICE(INTEL, 0x1a94) }, + { PCI_VDEVICE(INTEL, 0x5a94) }, + {} +}; + +static struct pci_driver intel_scu_pci_driver = { + .driver = { + .suppress_bind_attrs = true, + }, + .name = "intel_scu", + .id_table = pci_ids, + .probe = intel_scu_pci_probe, +}; + +builtin_pci_driver(intel_scu_pci_driver); diff --git a/drivers/platform/x86/intel_telemetry_core.c b/drivers/platform/x86/intel_telemetry_core.c index d4040bb222b4..fdf55b5d6948 100644 --- a/drivers/platform/x86/intel_telemetry_core.c +++ b/drivers/platform/x86/intel_telemetry_core.c @@ -353,21 +353,16 @@ int telemetry_clear_pltdata(void) EXPORT_SYMBOL_GPL(telemetry_clear_pltdata); /** - * telemetry_pltconfig_valid() - Checkif platform config is valid + * telemetry_get_pltdata() - Return telemetry platform config * - * Usage by other than telemetry module is invalid - * - * Return: 0 success, < 0 for failure + * May be used by other telemetry modules to get platform specific + * configuration. */ -int telemetry_pltconfig_valid(void) +struct telemetry_plt_config *telemetry_get_pltdata(void) { - if (telm_core_conf.plt_config) - return 0; - - else - return -EINVAL; + return telm_core_conf.plt_config; } -EXPORT_SYMBOL_GPL(telemetry_pltconfig_valid); +EXPORT_SYMBOL_GPL(telemetry_get_pltdata); static inline int telemetry_get_pssevtname(enum telemetry_unit telem_unit, const char **name, int len) diff --git a/drivers/platform/x86/intel_telemetry_debugfs.c b/drivers/platform/x86/intel_telemetry_debugfs.c index 8a53d3b485b3..1d4d0fbfd63c 100644 --- a/drivers/platform/x86/intel_telemetry_debugfs.c +++ b/drivers/platform/x86/intel_telemetry_debugfs.c @@ -15,6 +15,7 @@ */ #include <linux/debugfs.h> #include <linux/device.h> +#include <linux/mfd/intel_pmc_bxt.h> #include <linux/module.h> #include <linux/pci.h> #include <linux/seq_file.h> @@ -22,7 +23,6 @@ #include <asm/cpu_device_id.h> #include <asm/intel-family.h> -#include <asm/intel_pmc_ipc.h> #include <asm/intel_telemetry.h> #define DRIVER_NAME "telemetry_soc_debugfs" @@ -647,10 +647,11 @@ DEFINE_SHOW_ATTRIBUTE(telem_soc_states); static int telem_s0ix_res_get(void *data, u64 *val) { + struct telemetry_plt_config *plt_config = telemetry_get_pltdata(); u64 s0ix_total_res; int ret; - ret = intel_pmc_s0ix_counter_read(&s0ix_total_res); + ret = intel_pmc_s0ix_counter_read(plt_config->pmc, &s0ix_total_res); if (ret) { pr_err("Failed to read S0ix residency"); return ret; @@ -837,12 +838,15 @@ static int pm_suspend_exit_cb(void) */ if (suspend_shlw_ctr_exit == suspend_shlw_ctr_temp && suspend_deep_ctr_exit == suspend_deep_ctr_temp) { - ret = intel_pmc_gcr_read64(PMC_GCR_TELEM_SHLW_S0IX_REG, + struct telemetry_plt_config *plt_config = telemetry_get_pltdata(); + struct intel_pmc_dev *pmc = plt_config->pmc; + + ret = intel_pmc_gcr_read64(pmc, PMC_GCR_TELEM_SHLW_S0IX_REG, &suspend_shlw_res_exit); if (ret < 0) goto out; - ret = intel_pmc_gcr_read64(PMC_GCR_TELEM_DEEP_S0IX_REG, + ret = intel_pmc_gcr_read64(pmc, PMC_GCR_TELEM_DEEP_S0IX_REG, &suspend_deep_res_exit); if (ret < 0) goto out; @@ -910,8 +914,7 @@ static int __init telemetry_debugfs_init(void) debugfs_conf = (struct telemetry_debugfs_conf *)id->driver_data; - err = telemetry_pltconfig_valid(); - if (err < 0) { + if (!telemetry_get_pltdata()) { pr_info("Invalid pltconfig, ensure IPC1 device is enabled in BIOS\n"); return -ENODEV; } diff --git a/drivers/platform/x86/intel_telemetry_pltdrv.c b/drivers/platform/x86/intel_telemetry_pltdrv.c index 987a24e3344e..405dea87de6b 100644 --- a/drivers/platform/x86/intel_telemetry_pltdrv.c +++ b/drivers/platform/x86/intel_telemetry_pltdrv.c @@ -15,7 +15,6 @@ #include <asm/cpu_device_id.h> #include <asm/intel-family.h> -#include <asm/intel_pmc_ipc.h> #include <asm/intel_punit_ipc.h> #include <asm/intel_telemetry.h> @@ -35,6 +34,7 @@ #define TELEM_SSRAM_STARTTIME_OFFSET 8 #define TELEM_SSRAM_EVTLOG_OFFSET 16 +#define IOSS_TELEM 0xeb #define IOSS_TELEM_EVENT_READ 0x0 #define IOSS_TELEM_EVENT_WRITE 0x1 #define IOSS_TELEM_INFO_READ 0x2 @@ -42,9 +42,6 @@ #define IOSS_TELEM_TRACE_CTL_WRITE 0x6 #define IOSS_TELEM_EVENT_CTL_READ 0x7 #define IOSS_TELEM_EVENT_CTL_WRITE 0x8 -#define IOSS_TELEM_EVT_CTRL_WRITE_SIZE 0x4 -#define IOSS_TELEM_READ_WORD 0x1 -#define IOSS_TELEM_WRITE_FOURBYTES 0x4 #define IOSS_TELEM_EVT_WRITE_SIZE 0x3 #define TELEM_INFO_SRAMEVTS_MASK 0xFF00 @@ -250,17 +247,14 @@ static int telemetry_check_evtid(enum telemetry_unit telem_unit, static inline int telemetry_plt_config_ioss_event(u32 evt_id, int index) { u32 write_buf; - int ret; write_buf = evt_id | TELEM_EVENT_ENABLE; write_buf <<= BITS_PER_BYTE; write_buf |= index; - ret = intel_pmc_ipc_command(PMC_IPC_PMC_TELEMTRY, - IOSS_TELEM_EVENT_WRITE, (u8 *)&write_buf, - IOSS_TELEM_EVT_WRITE_SIZE, NULL, 0); - - return ret; + return intel_scu_ipc_dev_command(telm_conf->scu, IOSS_TELEM, + IOSS_TELEM_EVENT_WRITE, &write_buf, + IOSS_TELEM_EVT_WRITE_SIZE, NULL, 0); } static inline int telemetry_plt_config_pss_event(u32 evt_id, int index) @@ -278,6 +272,7 @@ static inline int telemetry_plt_config_pss_event(u32 evt_id, int index) static int telemetry_setup_iossevtconfig(struct telemetry_evtconfig evtconfig, enum telemetry_action action) { + struct intel_scu_ipc_dev *scu = telm_conf->scu; u8 num_ioss_evts, ioss_period; int ret, index, idx; u32 *ioss_evtmap; @@ -288,9 +283,9 @@ static int telemetry_setup_iossevtconfig(struct telemetry_evtconfig evtconfig, ioss_evtmap = evtconfig.evtmap; /* Get telemetry EVENT CTL */ - ret = intel_pmc_ipc_command(PMC_IPC_PMC_TELEMTRY, + ret = intel_scu_ipc_dev_command(scu, IOSS_TELEM, IOSS_TELEM_EVENT_CTL_READ, NULL, 0, - &telem_ctrl, IOSS_TELEM_READ_WORD); + &telem_ctrl, sizeof(telem_ctrl)); if (ret) { pr_err("IOSS TELEM_CTRL Read Failed\n"); return ret; @@ -299,11 +294,9 @@ static int telemetry_setup_iossevtconfig(struct telemetry_evtconfig evtconfig, /* Disable Telemetry */ TELEM_DISABLE(telem_ctrl); - ret = intel_pmc_ipc_command(PMC_IPC_PMC_TELEMTRY, - IOSS_TELEM_EVENT_CTL_WRITE, - (u8 *)&telem_ctrl, - IOSS_TELEM_EVT_CTRL_WRITE_SIZE, - NULL, 0); + ret = intel_scu_ipc_dev_command(scu, IOSS_TELEM, + IOSS_TELEM_EVENT_CTL_WRITE, &telem_ctrl, + sizeof(telem_ctrl), NULL, 0); if (ret) { pr_err("IOSS TELEM_CTRL Event Disable Write Failed\n"); return ret; @@ -315,10 +308,9 @@ static int telemetry_setup_iossevtconfig(struct telemetry_evtconfig evtconfig, /* Clear All Events */ TELEM_CLEAR_EVENTS(telem_ctrl); - ret = intel_pmc_ipc_command(PMC_IPC_PMC_TELEMTRY, + ret = intel_scu_ipc_dev_command(scu, IOSS_TELEM, IOSS_TELEM_EVENT_CTL_WRITE, - (u8 *)&telem_ctrl, - IOSS_TELEM_EVT_CTRL_WRITE_SIZE, + &telem_ctrl, sizeof(telem_ctrl), NULL, 0); if (ret) { pr_err("IOSS TELEM_CTRL Event Disable Write Failed\n"); @@ -344,10 +336,9 @@ static int telemetry_setup_iossevtconfig(struct telemetry_evtconfig evtconfig, /* Clear All Events */ TELEM_CLEAR_EVENTS(telem_ctrl); - ret = intel_pmc_ipc_command(PMC_IPC_PMC_TELEMTRY, + ret = intel_scu_ipc_dev_command(scu, IOSS_TELEM, IOSS_TELEM_EVENT_CTL_WRITE, - (u8 *)&telem_ctrl, - IOSS_TELEM_EVT_CTRL_WRITE_SIZE, + &telem_ctrl, sizeof(telem_ctrl), NULL, 0); if (ret) { pr_err("IOSS TELEM_CTRL Event Disable Write Failed\n"); @@ -396,10 +387,9 @@ static int telemetry_setup_iossevtconfig(struct telemetry_evtconfig evtconfig, TELEM_ENABLE_PERIODIC(telem_ctrl); telem_ctrl |= ioss_period; - ret = intel_pmc_ipc_command(PMC_IPC_PMC_TELEMTRY, + ret = intel_scu_ipc_dev_command(scu, IOSS_TELEM, IOSS_TELEM_EVENT_CTL_WRITE, - (u8 *)&telem_ctrl, - IOSS_TELEM_EVT_CTRL_WRITE_SIZE, NULL, 0); + &telem_ctrl, sizeof(telem_ctrl), NULL, 0); if (ret) { pr_err("IOSS TELEM_CTRL Event Enable Write Failed\n"); return ret; @@ -586,8 +576,9 @@ static int telemetry_setup(struct platform_device *pdev) u32 read_buf, events, event_regs; int ret; - ret = intel_pmc_ipc_command(PMC_IPC_PMC_TELEMTRY, IOSS_TELEM_INFO_READ, - NULL, 0, &read_buf, IOSS_TELEM_READ_WORD); + ret = intel_scu_ipc_dev_command(telm_conf->scu, IOSS_TELEM, + IOSS_TELEM_INFO_READ, NULL, 0, + &read_buf, sizeof(read_buf)); if (ret) { dev_err(&pdev->dev, "IOSS TELEM_INFO Read Failed\n"); return ret; @@ -681,6 +672,8 @@ static int telemetry_plt_set_sampling_period(u8 pss_period, u8 ioss_period) mutex_lock(&(telm_conf->telem_lock)); if (ioss_period) { + struct intel_scu_ipc_dev *scu = telm_conf->scu; + if (TELEM_SAMPLE_PERIOD_INVALID(ioss_period)) { pr_err("IOSS Sampling Period Out of Range\n"); ret = -EINVAL; @@ -688,9 +681,9 @@ static int telemetry_plt_set_sampling_period(u8 pss_period, u8 ioss_period) } /* Get telemetry EVENT CTL */ - ret = intel_pmc_ipc_command(PMC_IPC_PMC_TELEMTRY, + ret = intel_scu_ipc_dev_command(scu, IOSS_TELEM, IOSS_TELEM_EVENT_CTL_READ, NULL, 0, - &telem_ctrl, IOSS_TELEM_READ_WORD); + &telem_ctrl, sizeof(telem_ctrl)); if (ret) { pr_err("IOSS TELEM_CTRL Read Failed\n"); goto out; @@ -699,11 +692,10 @@ static int telemetry_plt_set_sampling_period(u8 pss_period, u8 ioss_period) /* Disable Telemetry */ TELEM_DISABLE(telem_ctrl); - ret = intel_pmc_ipc_command(PMC_IPC_PMC_TELEMTRY, - IOSS_TELEM_EVENT_CTL_WRITE, - (u8 *)&telem_ctrl, - IOSS_TELEM_EVT_CTRL_WRITE_SIZE, - NULL, 0); + ret = intel_scu_ipc_dev_command(scu, IOSS_TELEM, + IOSS_TELEM_EVENT_CTL_WRITE, + &telem_ctrl, sizeof(telem_ctrl), + NULL, 0); if (ret) { pr_err("IOSS TELEM_CTRL Event Disable Write Failed\n"); goto out; @@ -715,11 +707,10 @@ static int telemetry_plt_set_sampling_period(u8 pss_period, u8 ioss_period) TELEM_ENABLE_PERIODIC(telem_ctrl); telem_ctrl |= ioss_period; - ret = intel_pmc_ipc_command(PMC_IPC_PMC_TELEMTRY, - IOSS_TELEM_EVENT_CTL_WRITE, - (u8 *)&telem_ctrl, - IOSS_TELEM_EVT_CTRL_WRITE_SIZE, - NULL, 0); + ret = intel_scu_ipc_dev_command(scu, IOSS_TELEM, + IOSS_TELEM_EVENT_CTL_WRITE, + &telem_ctrl, sizeof(telem_ctrl), + NULL, 0); if (ret) { pr_err("IOSS TELEM_CTRL Event Enable Write Failed\n"); goto out; @@ -1014,9 +1005,9 @@ static int telemetry_plt_get_trace_verbosity(enum telemetry_unit telem_unit, break; case TELEM_IOSS: - ret = intel_pmc_ipc_command(PMC_IPC_PMC_TELEMTRY, - IOSS_TELEM_TRACE_CTL_READ, NULL, 0, &temp, - IOSS_TELEM_READ_WORD); + ret = intel_scu_ipc_dev_command(telm_conf->scu, + IOSS_TELEM, IOSS_TELEM_TRACE_CTL_READ, + NULL, 0, &temp, sizeof(temp)); if (ret) { pr_err("IOSS TRACE_CTL Read Failed\n"); goto out; @@ -1068,9 +1059,9 @@ static int telemetry_plt_set_trace_verbosity(enum telemetry_unit telem_unit, break; case TELEM_IOSS: - ret = intel_pmc_ipc_command(PMC_IPC_PMC_TELEMTRY, - IOSS_TELEM_TRACE_CTL_READ, NULL, 0, &temp, - IOSS_TELEM_READ_WORD); + ret = intel_scu_ipc_dev_command(telm_conf->scu, IOSS_TELEM, + IOSS_TELEM_TRACE_CTL_READ, + NULL, 0, &temp, sizeof(temp)); if (ret) { pr_err("IOSS TRACE_CTL Read Failed\n"); goto out; @@ -1079,9 +1070,9 @@ static int telemetry_plt_set_trace_verbosity(enum telemetry_unit telem_unit, TELEM_CLEAR_VERBOSITY_BITS(temp); TELEM_SET_VERBOSITY_BITS(temp, verbosity); - ret = intel_pmc_ipc_command(PMC_IPC_PMC_TELEMTRY, - IOSS_TELEM_TRACE_CTL_WRITE, (u8 *)&temp, - IOSS_TELEM_WRITE_FOURBYTES, NULL, 0); + ret = intel_scu_ipc_dev_command(telm_conf->scu, IOSS_TELEM, + IOSS_TELEM_TRACE_CTL_WRITE, + &temp, sizeof(temp), NULL, 0); if (ret) { pr_err("IOSS TRACE_CTL Verbosity Set Failed\n"); goto out; @@ -1124,6 +1115,8 @@ static int telemetry_pltdrv_probe(struct platform_device *pdev) telm_conf = (struct telemetry_plt_config *)id->driver_data; + telm_conf->pmc = dev_get_drvdata(pdev->dev.parent); + mem = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(mem)) return PTR_ERR(mem); @@ -1136,6 +1129,12 @@ static int telemetry_pltdrv_probe(struct platform_device *pdev) telm_conf->ioss_config.regmap = mem; + telm_conf->scu = devm_intel_scu_ipc_dev_get(&pdev->dev); + if (!telm_conf->scu) { + ret = -EPROBE_DEFER; + goto out; + } + mutex_init(&telm_conf->telem_lock); mutex_init(&telm_conf->telem_trace_lock); diff --git a/drivers/usb/typec/mux/Kconfig b/drivers/usb/typec/mux/Kconfig index 77eb97b2aa86..a4dbd11f8ee2 100644 --- a/drivers/usb/typec/mux/Kconfig +++ b/drivers/usb/typec/mux/Kconfig @@ -11,7 +11,7 @@ config TYPEC_MUX_PI3USB30532 config TYPEC_MUX_INTEL_PMC tristate "Intel PMC mux control" - depends on INTEL_PMC_IPC + depends on INTEL_SCU_IPC select USB_ROLE_SWITCH help Driver for USB muxes controlled by Intel PMC FW. Intel PMC FW can diff --git a/drivers/usb/typec/mux/intel_pmc_mux.c b/drivers/usb/typec/mux/intel_pmc_mux.c index f5c5e0aef66f..fce255e28a00 100644 --- a/drivers/usb/typec/mux/intel_pmc_mux.c +++ b/drivers/usb/typec/mux/intel_pmc_mux.c @@ -15,7 +15,7 @@ #include <linux/usb/typec_dp.h> #include <linux/usb/typec_tbt.h> -#include <asm/intel_pmc_ipc.h> +#include <asm/intel_scu_ipc.h> #define PMC_USBC_CMD 0xa7 @@ -96,6 +96,7 @@ struct pmc_usb_port { struct pmc_usb { u8 num_ports; struct device *dev; + struct intel_scu_ipc_dev *ipc; struct pmc_usb_port *port; }; @@ -107,9 +108,8 @@ static int pmc_usb_command(struct pmc_usb_port *port, u8 *msg, u32 len) * Error bit will always be 0 with the USBC command. * Status can be checked from the response message. */ - intel_pmc_ipc_command(PMC_USBC_CMD, 0, msg, len, - (void *)response, 1); - + intel_scu_ipc_dev_command(port->pmc->ipc, PMC_USBC_CMD, 0, msg, len, + response, sizeof(response)); if (response[2]) { if (response[2] & BIT(1)) return -EIO; @@ -370,6 +370,10 @@ static int pmc_usb_probe(struct platform_device *pdev) if (!pmc->port) return -ENOMEM; + pmc->ipc = devm_intel_scu_ipc_dev_get(&pdev->dev); + if (!pmc->ipc) + return -ENODEV; + pmc->dev = &pdev->dev; /* diff --git a/drivers/usb/typec/tcpm/Kconfig b/drivers/usb/typec/tcpm/Kconfig index 5b986d6c801d..fa3f39336246 100644 --- a/drivers/usb/typec/tcpm/Kconfig +++ b/drivers/usb/typec/tcpm/Kconfig @@ -41,8 +41,8 @@ config TYPEC_FUSB302 config TYPEC_WCOVE tristate "Intel WhiskeyCove PMIC USB Type-C PHY driver" depends on ACPI + depends on MFD_INTEL_PMC_BXT depends on INTEL_SOC_PMIC - depends on INTEL_PMC_IPC depends on BXT_WC_PMIC_OPREGION help This driver adds support for USB Type-C on Intel Broxton platforms diff --git a/drivers/watchdog/iTCO_wdt.c b/drivers/watchdog/iTCO_wdt.c index e707c4797f76..a370a185a41c 100644 --- a/drivers/watchdog/iTCO_wdt.c +++ b/drivers/watchdog/iTCO_wdt.c @@ -64,6 +64,7 @@ #include <linux/uaccess.h> /* For copy_to_user/put_user/... */ #include <linux/io.h> /* For inb/outb/... */ #include <linux/platform_data/itco_wdt.h> +#include <linux/mfd/intel_pmc_bxt.h> #include "iTCO_vendor.h" @@ -233,12 +234,24 @@ static int update_no_reboot_bit_cnt(void *priv, bool set) return val != newval ? -EIO : 0; } +static int update_no_reboot_bit_pmc(void *priv, bool set) +{ + struct intel_pmc_dev *pmc = priv; + u32 bits = PMC_CFG_NO_REBOOT_EN; + u32 value = set ? bits : 0; + + return intel_pmc_gcr_update(pmc, PMC_GCR_PMC_CFG_REG, bits, value); +} + static void iTCO_wdt_no_reboot_bit_setup(struct iTCO_wdt_private *p, - struct itco_wdt_platform_data *pdata) + struct platform_device *pdev, + struct itco_wdt_platform_data *pdata) { - if (pdata->update_no_reboot_bit) { - p->update_no_reboot_bit = pdata->update_no_reboot_bit; - p->no_reboot_priv = pdata->no_reboot_priv; + if (pdata->no_reboot_use_pmc) { + struct intel_pmc_dev *pmc = dev_get_drvdata(pdev->dev.parent); + + p->update_no_reboot_bit = update_no_reboot_bit_pmc; + p->no_reboot_priv = pmc; return; } @@ -478,14 +491,14 @@ static int iTCO_wdt_probe(struct platform_device *pdev) return -ENODEV; } - iTCO_wdt_no_reboot_bit_setup(p, pdata); + iTCO_wdt_no_reboot_bit_setup(p, pdev, pdata); /* * Get the Memory-Mapped GCS or PMC register, we need it for the * NO_REBOOT flag (TCO v2 and v3). */ if (p->iTCO_version >= 2 && p->iTCO_version < 6 && - !pdata->update_no_reboot_bit) { + !pdata->no_reboot_use_pmc) { p->gcs_pmc_res = platform_get_resource(pdev, IORESOURCE_MEM, ICH_RES_MEM_GCS_PMC); diff --git a/drivers/watchdog/intel-mid_wdt.c b/drivers/watchdog/intel-mid_wdt.c index 470213abfd3d..1ae03b64ef8b 100644 --- a/drivers/watchdog/intel-mid_wdt.c +++ b/drivers/watchdog/intel-mid_wdt.c @@ -33,14 +33,24 @@ enum { SCU_WATCHDOG_KEEPALIVE, }; -static inline int wdt_command(int sub, u32 *in, int inlen) +struct mid_wdt { + struct watchdog_device wd; + struct device *dev; + struct intel_scu_ipc_dev *scu; +}; + +static inline int +wdt_command(struct mid_wdt *mid, int sub, const void *in, size_t inlen, size_t size) { - return intel_scu_ipc_command(IPC_WATCHDOG, sub, in, inlen, NULL, 0); + struct intel_scu_ipc_dev *scu = mid->scu; + + return intel_scu_ipc_dev_command_with_size(scu, IPC_WATCHDOG, sub, in, + inlen, size, NULL, 0); } static int wdt_start(struct watchdog_device *wd) { - struct device *dev = watchdog_get_drvdata(wd); + struct mid_wdt *mid = watchdog_get_drvdata(wd); int ret, in_size; int timeout = wd->timeout; struct ipc_wd_start { @@ -49,38 +59,41 @@ static int wdt_start(struct watchdog_device *wd) } ipc_wd_start = { timeout - MID_WDT_PRETIMEOUT, timeout }; /* - * SCU expects the input size for watchdog IPC to - * be based on 4 bytes + * SCU expects the input size for watchdog IPC to be 2 which is the + * size of the structure in dwords. SCU IPC normally takes bytes + * but this is a special case where we specify size to be different + * than inlen. */ in_size = DIV_ROUND_UP(sizeof(ipc_wd_start), 4); - ret = wdt_command(SCU_WATCHDOG_START, (u32 *)&ipc_wd_start, in_size); + ret = wdt_command(mid, SCU_WATCHDOG_START, &ipc_wd_start, + sizeof(ipc_wd_start), in_size); if (ret) - dev_crit(dev, "error starting watchdog: %d\n", ret); + dev_crit(mid->dev, "error starting watchdog: %d\n", ret); return ret; } static int wdt_ping(struct watchdog_device *wd) { - struct device *dev = watchdog_get_drvdata(wd); + struct mid_wdt *mid = watchdog_get_drvdata(wd); int ret; - ret = wdt_command(SCU_WATCHDOG_KEEPALIVE, NULL, 0); + ret = wdt_command(mid, SCU_WATCHDOG_KEEPALIVE, NULL, 0, 0); if (ret) - dev_crit(dev, "Error executing keepalive: %d\n", ret); + dev_crit(mid->dev, "Error executing keepalive: %d\n", ret); return ret; } static int wdt_stop(struct watchdog_device *wd) { - struct device *dev = watchdog_get_drvdata(wd); + struct mid_wdt *mid = watchdog_get_drvdata(wd); int ret; - ret = wdt_command(SCU_WATCHDOG_STOP, NULL, 0); + ret = wdt_command(mid, SCU_WATCHDOG_STOP, NULL, 0, 0); if (ret) - dev_crit(dev, "Error stopping watchdog: %d\n", ret); + dev_crit(mid->dev, "Error stopping watchdog: %d\n", ret); return ret; } @@ -110,6 +123,7 @@ static int mid_wdt_probe(struct platform_device *pdev) struct device *dev = &pdev->dev; struct watchdog_device *wdt_dev; struct intel_mid_wdt_pdata *pdata = dev->platform_data; + struct mid_wdt *mid; int ret; if (!pdata) { @@ -123,10 +137,13 @@ static int mid_wdt_probe(struct platform_device *pdev) return ret; } - wdt_dev = devm_kzalloc(dev, sizeof(*wdt_dev), GFP_KERNEL); - if (!wdt_dev) + mid = devm_kzalloc(dev, sizeof(*mid), GFP_KERNEL); + if (!mid) return -ENOMEM; + mid->dev = dev; + wdt_dev = &mid->wd; + wdt_dev->info = &mid_wdt_info; wdt_dev->ops = &mid_wdt_ops; wdt_dev->min_timeout = MID_WDT_TIMEOUT_MIN; @@ -135,7 +152,7 @@ static int mid_wdt_probe(struct platform_device *pdev) wdt_dev->parent = dev; watchdog_set_nowayout(wdt_dev, WATCHDOG_NOWAYOUT); - watchdog_set_drvdata(wdt_dev, dev); + watchdog_set_drvdata(wdt_dev, mid); ret = devm_request_irq(dev, pdata->irq, mid_wdt_irq, IRQF_SHARED | IRQF_NO_SUSPEND, "watchdog", @@ -145,6 +162,10 @@ static int mid_wdt_probe(struct platform_device *pdev) return ret; } + mid->scu = devm_intel_scu_ipc_dev_get(dev); + if (!mid->scu) + return -EPROBE_DEFER; + /* * The firmware followed by U-Boot leaves the watchdog running * with the default threshold which may vary. When we get here |