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: Documentation/devicetree/bindings/soc/google/google,gs101-pmu-intr-gen.yaml
|
||||||
F: arch/arm64/boot/dts/exynos/google/
|
F: arch/arm64/boot/dts/exynos/google/
|
||||||
F: drivers/clk/samsung/clk-gs101.c
|
F: drivers/clk/samsung/clk-gs101.c
|
||||||
|
F: drivers/soc/samsung/gs101-pmu.c
|
||||||
F: drivers/phy/samsung/phy-gs101-ufs.c
|
F: drivers/phy/samsung/phy-gs101-ufs.c
|
||||||
F: include/dt-bindings/clock/google,gs101.h
|
F: include/dt-bindings/clock/google,gs101.h
|
||||||
K: [gG]oogle.?[tT]ensor
|
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_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 \
|
obj-$(CONFIG_EXYNOS_PMU_ARM_DRIVERS) += exynos3250-pmu.o exynos4-pmu.o \
|
||||||
exynos5250-pmu.o exynos5420-pmu.o
|
exynos5250-pmu.o exynos5420-pmu.o
|
||||||
|
|||||||
@@ -6,7 +6,6 @@
|
|||||||
// Exynos - CPU PMU(Power Management Unit) support
|
// Exynos - CPU PMU(Power Management Unit) support
|
||||||
|
|
||||||
#include <linux/array_size.h>
|
#include <linux/array_size.h>
|
||||||
#include <linux/arm-smccc.h>
|
|
||||||
#include <linux/bitmap.h>
|
#include <linux/bitmap.h>
|
||||||
#include <linux/cpuhotplug.h>
|
#include <linux/cpuhotplug.h>
|
||||||
#include <linux/cpu_pm.h>
|
#include <linux/cpu_pm.h>
|
||||||
@@ -25,14 +24,6 @@
|
|||||||
|
|
||||||
#include "exynos-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
|
|
||||||
|
|
||||||
struct exynos_pmu_context {
|
struct exynos_pmu_context {
|
||||||
struct device *dev;
|
struct device *dev;
|
||||||
const struct exynos_pmu_data *pmu_data;
|
const struct exynos_pmu_data *pmu_data;
|
||||||
@@ -54,125 +45,6 @@ static struct exynos_pmu_context *pmu_context;
|
|||||||
/* forward declaration */
|
/* forward declaration */
|
||||||
static struct platform_driver exynos_pmu_driver;
|
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)
|
void pmu_raw_writel(u32 val, u32 offset)
|
||||||
{
|
{
|
||||||
writel_relaxed(val, pmu_base_addr + offset);
|
writel_relaxed(val, pmu_base_addr + offset);
|
||||||
@@ -244,11 +116,6 @@ static const struct regmap_config regmap_pmu_intr = {
|
|||||||
.use_raw_spinlock = true,
|
.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.
|
* 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 exynos5250_pmu_data;
|
||||||
extern const struct exynos_pmu_data exynos5420_pmu_data;
|
extern const struct exynos_pmu_data exynos5420_pmu_data;
|
||||||
#endif
|
#endif
|
||||||
|
extern const struct exynos_pmu_data gs101_pmu_data;
|
||||||
|
|
||||||
extern void pmu_raw_writel(u32 val, u32 offset);
|
extern void pmu_raw_writel(u32 val, u32 offset);
|
||||||
extern u32 pmu_raw_readl(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 */
|
#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