diff options
author | Kuppuswamy Sathyanarayanan <sathyanarayanan.kuppuswamy@linux.intel.com> | 2017-04-09 15:00:17 -0700 |
---|---|---|
committer | Andy Shevchenko <andriy.shevchenko@linux.intel.com> | 2017-04-28 21:51:27 +0300 |
commit | 496702068597ff29092e724301f77b91864454b3 (patch) | |
tree | 3ba2e5248b269aad1e4725af55e13f573aa0c231 | |
parent | e6749c89b4b0acaeddf4909eb75b7f6c8a4c15b2 (diff) |
platform/x86: intel_pmc_ipc: Add pmc gcr read/write/update api's
This patch adds API's to read/write/update PMC GC registers.
PMC dependent devices like iTCO_wdt, Telemetry has requirement
to acces GCR registers. These API's can be used for this
purpose.
Signed-off-by: Kuppuswamy Sathyanarayanan <sathyanarayanan.kuppuswamy@linux.intel.com>
Signed-off-by: Andy Shevchenko <andriy.shevchenko@linux.intel.com>
-rw-r--r-- | arch/x86/include/asm/intel_pmc_ipc.h | 21 | ||||
-rw-r--r-- | drivers/platform/x86/intel_pmc_ipc.c | 115 |
2 files changed, 136 insertions, 0 deletions
diff --git a/arch/x86/include/asm/intel_pmc_ipc.h b/arch/x86/include/asm/intel_pmc_ipc.h index 4291b6a5ddf7..8402efef10ad 100644 --- a/arch/x86/include/asm/intel_pmc_ipc.h +++ b/arch/x86/include/asm/intel_pmc_ipc.h @@ -23,6 +23,9 @@ #define IPC_ERR_EMSECURITY 6 #define IPC_ERR_UNSIGNEDKERNEL 7 +/* GCR reg offsets from gcr base*/ +#define PMC_GCR_PMC_CFG_REG 0x08 + #if IS_ENABLED(CONFIG_INTEL_PMC_IPC) int intel_pmc_ipc_simple_command(int cmd, int sub); @@ -31,6 +34,9 @@ int intel_pmc_ipc_raw_cmd(u32 cmd, u32 sub, u8 *in, u32 inlen, int intel_pmc_ipc_command(u32 cmd, u32 sub, u8 *in, u32 inlen, u32 *out, u32 outlen); int intel_pmc_s0ix_counter_read(u64 *data); +int intel_pmc_gcr_read(u32 offset, u32 *data); +int intel_pmc_gcr_write(u32 offset, u32 data); +int intel_pmc_gcr_update(u32 offset, u32 mask, u32 val); #else @@ -56,6 +62,21 @@ static inline int intel_pmc_s0ix_counter_read(u64 *data) return -EINVAL; } +static inline int intel_pmc_gcr_read(u32 offset, u32 *data) +{ + return -EINVAL; +} + +static inline int intel_pmc_gcr_write(u32 offset, u32 data) +{ + return -EINVAL; +} + +static inline int intel_pmc_gcr_update(u32 offset, u32 mask, u32 val) +{ + return -EINVAL; +} + #endif /*CONFIG_INTEL_PMC_IPC*/ #endif diff --git a/drivers/platform/x86/intel_pmc_ipc.c b/drivers/platform/x86/intel_pmc_ipc.c index 0a3359240227..a0c773b3910e 100644 --- a/drivers/platform/x86/intel_pmc_ipc.c +++ b/drivers/platform/x86/intel_pmc_ipc.c @@ -128,6 +128,7 @@ static struct intel_pmc_ipc_dev { /* gcr */ resource_size_t gcr_base; int gcr_size; + void __iomem *gcr_mem_base; bool has_gcr_regs; /* punit */ @@ -199,6 +200,119 @@ static inline u64 gcr_data_readq(u32 offset) return readq(ipcdev.ipc_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_read() - Read PMC GCR register + * @offset: offset of GCR register from GCR address base + * @data: data pointer for storing the register output + * + * Reads the PMC GCR register of given offset. + * + * Return: negative value on error or 0 on success. + */ +int intel_pmc_gcr_read(u32 offset, u32 *data) +{ + int ret; + + mutex_lock(&ipclock); + + ret = is_gcr_valid(offset); + if (ret < 0) { + mutex_unlock(&ipclock); + return ret; + } + + *data = readl(ipcdev.gcr_mem_base + offset); + + mutex_unlock(&ipclock); + + return 0; +} +EXPORT_SYMBOL_GPL(intel_pmc_gcr_read); + +/** + * intel_pmc_gcr_write() - Write PMC GCR register + * @offset: offset of GCR register from GCR address base + * @data: register update value + * + * Writes the PMC GCR register of given offset with given + * value. + * + * Return: negative value on error or 0 on success. + */ +int intel_pmc_gcr_write(u32 offset, u32 data) +{ + int ret; + + mutex_lock(&ipclock); + + ret = is_gcr_valid(offset); + if (ret < 0) { + mutex_unlock(&ipclock); + return ret; + } + + writel(data, ipcdev.gcr_mem_base + offset); + + mutex_unlock(&ipclock); + + return 0; +} +EXPORT_SYMBOL_GPL(intel_pmc_gcr_write); + +/** + * 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. + */ +int intel_pmc_gcr_update(u32 offset, u32 mask, u32 val) +{ + u32 new_val; + int ret = 0; + + mutex_lock(&ipclock); + + 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: + mutex_unlock(&ipclock); + return ret; +} +EXPORT_SYMBOL_GPL(intel_pmc_gcr_update); + static int intel_pmc_ipc_check_status(void) { int status; @@ -747,6 +861,7 @@ static int ipc_plat_get_res(struct platform_device *pdev) ipcdev.ipc_base = addr; ipcdev.gcr_base = res->start + PLAT_RESOURCE_GCR_OFFSET; + ipcdev.gcr_mem_base = addr + PLAT_RESOURCE_GCR_OFFSET; ipcdev.gcr_size = PLAT_RESOURCE_GCR_SIZE; dev_info(&pdev->dev, "ipc res: %pR\n", res); |