// 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.
*/
/* * 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
/** * 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;
/** * 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;
/* 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;
/** * 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, constchar *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 = 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, constchar *buf, size_t count)
{ struct intel_pmc_dev *pmc = dev_get_drvdata(dev); struct intel_scu_ipc_dev *scu = pmc->scu; unsignedlong 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;
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;
}
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;
}
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;
}
staticint 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);
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.