soc: samsung: exynos-pmu: move some gs101 related code into new file
To avoid cluttering common code, move most of the gs101 code into a new file, gs101-pmu.c More code is going to be added for gs101 - having it all in one file helps keeping the common code (file) more readable. While at it, rename variables 'ctx' to 'context' for consistency. No functional change. Reviewed-by: Sam Protsenko <semen.protsenko@linaro.org> Signed-off-by: André Draszik <andre.draszik@linaro.org> Link: https://patch.msgid.link/20251009-gs101-pmu-regmap-tables-v2-2-2d64f5261952@linaro.org Signed-off-by: Krzysztof Kozlowski <krzysztof.kozlowski@linaro.org>
This commit is contained in:
committed by
Krzysztof Kozlowski
parent
1fce7e4d6c
commit
b320711e4c
@@ -10600,6 +10600,7 @@ F: Documentation/devicetree/bindings/clock/google,gs101-clock.yaml
|
||||
F: Documentation/devicetree/bindings/soc/google/google,gs101-pmu-intr-gen.yaml
|
||||
F: arch/arm64/boot/dts/exynos/google/
|
||||
F: drivers/clk/samsung/clk-gs101.c
|
||||
F: drivers/soc/samsung/gs101-pmu.c
|
||||
F: drivers/phy/samsung/phy-gs101-ufs.c
|
||||
F: include/dt-bindings/clock/google,gs101.h
|
||||
K: [gG]oogle.?[tT]ensor
|
||||
|
||||
@@ -6,7 +6,8 @@ exynos_chipid-y += exynos-chipid.o exynos-asv.o
|
||||
|
||||
obj-$(CONFIG_EXYNOS_USI) += exynos-usi.o
|
||||
|
||||
obj-$(CONFIG_EXYNOS_PMU) += exynos-pmu.o
|
||||
obj-$(CONFIG_EXYNOS_PMU) += exynos_pmu.o
|
||||
exynos_pmu-y += exynos-pmu.o gs101-pmu.o
|
||||
|
||||
obj-$(CONFIG_EXYNOS_PMU_ARM_DRIVERS) += exynos3250-pmu.o exynos4-pmu.o \
|
||||
exynos5250-pmu.o exynos5420-pmu.o
|
||||
|
||||
@@ -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.
|
||||
*/
|
||||
|
||||
@@ -70,7 +70,14 @@ extern const struct exynos_pmu_data exynos4412_pmu_data;
|
||||
extern const struct exynos_pmu_data exynos5250_pmu_data;
|
||||
extern const struct exynos_pmu_data exynos5420_pmu_data;
|
||||
#endif
|
||||
extern const struct exynos_pmu_data gs101_pmu_data;
|
||||
|
||||
extern void pmu_raw_writel(u32 val, u32 offset);
|
||||
extern u32 pmu_raw_readl(u32 offset);
|
||||
|
||||
int tensor_sec_reg_write(void *context, unsigned int reg, unsigned int val);
|
||||
int tensor_sec_reg_read(void *context, unsigned int reg, unsigned int *val);
|
||||
int tensor_sec_update_bits(void *context, unsigned int reg, unsigned int mask,
|
||||
unsigned int val);
|
||||
|
||||
#endif /* __EXYNOS_PMU_H */
|
||||
|
||||
142
drivers/soc/samsung/gs101-pmu.c
Normal file
142
drivers/soc/samsung/gs101-pmu.c
Normal file
@@ -0,0 +1,142 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* Copyright 2025 Linaro Ltd.
|
||||
*
|
||||
* GS101 PMU (Power Management Unit) support
|
||||
*/
|
||||
|
||||
#include <linux/arm-smccc.h>
|
||||
#include <linux/array_size.h>
|
||||
#include <linux/soc/samsung/exynos-pmu.h>
|
||||
#include <linux/soc/samsung/exynos-regs-pmu.h>
|
||||
|
||||
#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
|
||||
|
||||
const struct exynos_pmu_data gs101_pmu_data = {
|
||||
.pmu_secure = true,
|
||||
.pmu_cpuhp = true,
|
||||
};
|
||||
|
||||
/*
|
||||
* 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. */
|
||||
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.
|
||||
*/
|
||||
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 *context, 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(context, 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;
|
||||
}
|
||||
}
|
||||
|
||||
int tensor_sec_update_bits(void *context, unsigned int reg, unsigned int mask,
|
||||
unsigned int val)
|
||||
{
|
||||
if (!tensor_is_atomic(reg))
|
||||
return tensor_sec_reg_rmw(context, reg, mask, val);
|
||||
|
||||
return tensor_set_bits_atomic(context, reg, val, mask);
|
||||
}
|
||||
Reference in New Issue
Block a user