diff options
Diffstat (limited to 'drivers/soc/samsung/exynos-pmu.c')
| -rw-r--r-- | drivers/soc/samsung/exynos-pmu.c | 147 |
1 files changed, 10 insertions, 137 deletions
diff --git a/drivers/soc/samsung/exynos-pmu.c b/drivers/soc/samsung/exynos-pmu.c index 22c50ca2aa79..d58376c38179 100644 --- a/drivers/soc/samsung/exynos-pmu.c +++ b/drivers/soc/samsung/exynos-pmu.c @@ -6,7 +6,6 @@ // Exynos - CPU PMU(Power Management Unit) support #include <linux/array_size.h> -#include <linux/arm-smccc.h> #include <linux/bitmap.h> #include <linux/cpuhotplug.h> #include <linux/cpu_pm.h> @@ -25,14 +24,6 @@ #include "exynos-pmu.h" -#define PMUALIVE_MASK GENMASK(13, 0) -#define TENSOR_SET_BITS (BIT(15) | BIT(14)) -#define TENSOR_CLR_BITS BIT(15) -#define TENSOR_SMC_PMU_SEC_REG 0x82000504 -#define TENSOR_PMUREG_READ 0 -#define TENSOR_PMUREG_WRITE 1 -#define TENSOR_PMUREG_RMW 2 - struct exynos_pmu_context { struct device *dev; const struct exynos_pmu_data *pmu_data; @@ -54,125 +45,6 @@ static struct exynos_pmu_context *pmu_context; /* forward declaration */ static struct platform_driver exynos_pmu_driver; -/* - * Tensor SoCs are configured so that PMU_ALIVE registers can only be written - * from EL3, but are still read accessible. As Linux needs to write some of - * these registers, the following functions are provided and exposed via - * regmap. - * - * Note: This SMC interface is known to be implemented on gs101 and derivative - * SoCs. - */ - -/* Write to a protected PMU register. */ -static int tensor_sec_reg_write(void *context, unsigned int reg, - unsigned int val) -{ - struct arm_smccc_res res; - unsigned long pmu_base = (unsigned long)context; - - arm_smccc_smc(TENSOR_SMC_PMU_SEC_REG, pmu_base + reg, - TENSOR_PMUREG_WRITE, val, 0, 0, 0, 0, &res); - - /* returns -EINVAL if access isn't allowed or 0 */ - if (res.a0) - pr_warn("%s(): SMC failed: %d\n", __func__, (int)res.a0); - - return (int)res.a0; -} - -/* Read/Modify/Write a protected PMU register. */ -static int tensor_sec_reg_rmw(void *context, unsigned int reg, - unsigned int mask, unsigned int val) -{ - struct arm_smccc_res res; - unsigned long pmu_base = (unsigned long)context; - - arm_smccc_smc(TENSOR_SMC_PMU_SEC_REG, pmu_base + reg, - TENSOR_PMUREG_RMW, mask, val, 0, 0, 0, &res); - - /* returns -EINVAL if access isn't allowed or 0 */ - if (res.a0) - pr_warn("%s(): SMC failed: %d\n", __func__, (int)res.a0); - - return (int)res.a0; -} - -/* - * Read a protected PMU register. All PMU registers can be read by Linux. - * Note: The SMC read register is not used, as only registers that can be - * written are readable via SMC. - */ -static int tensor_sec_reg_read(void *context, unsigned int reg, - unsigned int *val) -{ - *val = pmu_raw_readl(reg); - return 0; -} - -/* - * For SoCs that have set/clear bit hardware this function can be used when - * the PMU register will be accessed by multiple masters. - * - * For example, to set bits 13:8 in PMU reg offset 0x3e80 - * tensor_set_bits_atomic(ctx, 0x3e80, 0x3f00, 0x3f00); - * - * Set bit 8, and clear bits 13:9 PMU reg offset 0x3e80 - * tensor_set_bits_atomic(0x3e80, 0x100, 0x3f00); - */ -static int tensor_set_bits_atomic(void *ctx, unsigned int offset, u32 val, - u32 mask) -{ - int ret; - unsigned int i; - - for (i = 0; i < 32; i++) { - if (!(mask & BIT(i))) - continue; - - offset &= ~TENSOR_SET_BITS; - - if (val & BIT(i)) - offset |= TENSOR_SET_BITS; - else - offset |= TENSOR_CLR_BITS; - - ret = tensor_sec_reg_write(ctx, offset, i); - if (ret) - return ret; - } - return 0; -} - -static bool tensor_is_atomic(unsigned int reg) -{ - /* - * Use atomic operations for PMU_ALIVE registers (offset 0~0x3FFF) - * as the target registers can be accessed by multiple masters. SFRs - * that don't support atomic are added to the switch statement below. - */ - if (reg > PMUALIVE_MASK) - return false; - - switch (reg) { - case GS101_SYSIP_DAT0: - case GS101_SYSTEM_CONFIGURATION: - return false; - default: - return true; - } -} - -static int tensor_sec_update_bits(void *ctx, unsigned int reg, - unsigned int mask, unsigned int val) -{ - - if (!tensor_is_atomic(reg)) - return tensor_sec_reg_rmw(ctx, reg, mask, val); - - return tensor_set_bits_atomic(ctx, reg, val, mask); -} - void pmu_raw_writel(u32 val, u32 offset) { writel_relaxed(val, pmu_base_addr + offset); @@ -244,11 +116,6 @@ static const struct regmap_config regmap_pmu_intr = { .use_raw_spinlock = true, }; -static const struct exynos_pmu_data gs101_pmu_data = { - .pmu_secure = true, - .pmu_cpuhp = true, -}; - /* * PMU platform driver and devicetree bindings. */ @@ -346,6 +213,8 @@ struct regmap *exynos_get_pmu_regmap_by_phandle(struct device_node *np, if (!dev) return ERR_PTR(-EPROBE_DEFER); + put_device(dev); + return syscon_node_to_regmap(pmu_np); } EXPORT_SYMBOL_GPL(exynos_get_pmu_regmap_by_phandle); @@ -364,6 +233,7 @@ EXPORT_SYMBOL_GPL(exynos_get_pmu_regmap_by_phandle); * disabled and cpupm_lock held. */ static int __gs101_cpu_pmu_online(unsigned int cpu) + __must_hold(&pmu_context->cpupm_lock) { unsigned int cpuhint = smp_processor_id(); u32 reg, mask; @@ -424,6 +294,7 @@ static int gs101_cpuhp_pmu_online(unsigned int cpu) /* Common function shared by both CPU hot plug and CPUIdle */ static int __gs101_cpu_pmu_offline(unsigned int cpu) + __must_hold(&pmu_context->cpupm_lock) { unsigned int cpuhint = smp_processor_id(); u32 reg, mask; @@ -585,10 +456,6 @@ static int setup_cpuhp_and_cpuidle(struct device *dev) if (!pmu_context->in_cpuhp) return -ENOMEM; - raw_spin_lock_init(&pmu_context->cpupm_lock); - pmu_context->sys_inreboot = false; - pmu_context->sys_insuspend = false; - /* set PMU to power on */ for_each_online_cpu(cpu) gs101_cpuhp_pmu_online(cpu); @@ -635,6 +502,9 @@ static int exynos_pmu_probe(struct platform_device *pdev) pmu_regmcfg = regmap_smccfg; pmu_regmcfg.max_register = resource_size(res) - pmu_regmcfg.reg_stride; + pmu_regmcfg.wr_table = pmu_context->pmu_data->wr_table; + pmu_regmcfg.rd_table = pmu_context->pmu_data->rd_table; + /* Need physical address for SMC call */ regmap = devm_regmap_init(dev, NULL, (void *)(uintptr_t)res->start, @@ -657,6 +527,9 @@ static int exynos_pmu_probe(struct platform_device *pdev) pmu_context->pmureg = regmap; pmu_context->dev = dev; + raw_spin_lock_init(&pmu_context->cpupm_lock); + pmu_context->sys_inreboot = false; + pmu_context->sys_insuspend = false; if (pmu_context->pmu_data && pmu_context->pmu_data->pmu_cpuhp) { ret = setup_cpuhp_and_cpuidle(dev); |
