Samsung SoC driver changes for v6.9, part two

1. Extend Exynos PMU (Power Management Unit) driver being also the
    syscon to main system controller registers block, to support Google
    GS101.  The Google GS101 has PMU registers protected and writing is
    available only via SMC.  The Exynos PMU will register its own custom
    regmap for such case of mixed MMIO+SMC.
 
 2. Rework Samsung watchdog driver to get the regmap to PMU block not
    via syscon API, but from the Exynos PMU driver.  This is necessary
    for the watchdog driver to work on Google GS101.
 -----BEGIN PGP SIGNATURE-----
 
 iQJEBAABCgAuFiEE3dJiKD0RGyM7briowTdm5oaLg9cFAmXdl6oQHGtyemtAa2Vy
 bmVsLm9yZwAKCRDBN2bmhouD1wZ2D/9NYhjQFu7PfnRUBIcrTIyTBSf11b61EIKR
 KwxDR/MhJ/+C7Zo4YKiWbJGQEhm1sG8LUsOWV4RBy5QhqeyqFCqDC8Pba6YH15MO
 9SfBT2eGgw9NVNxULGPMmuI9/It9umDKc9q9UyBxBGxXo2C8S8vtX4HLfIIVZ2fv
 /uSxW073K7qbWb0c5WUvxMYgdHHwkPyZAecK3yLRNIsFnEZuo7uytmCcVcIBKSoz
 LAv18fa65oZt2NOvvKrzLWNDaYcobzRfEW9r6XPkGkuh5SUJ/076T3W3IGbfNJAq
 DaB7XAFw3EsvORs7gMQAoyGA/+4RQVTMLcKM//2Et3A3ug+gKJ9i0/Y08Ay4FbAU
 cpryMGmb81XWb82yvP9p5XjynGFHGFd5we7ZFbcJeXn/uc4W0l4Zca33h7ulWBRY
 iUD+n0r/cWpVuA1pxK0cldfxOn0yZuq1Y1pOcZY/neDQdb2C7tTgZ2xBgNwWZdhz
 G3BTHo+oU72oMoAR7AJKJymVUzHQftEkWwTiD4NQenNQ11rBeNcEgtcZevxUuGff
 mnLrxaf+Bf+ykZOFGQJwHFIG0nRbIyRtZ+uv/Tqe/FEam6O7P159PJjDgf7Yvy9/
 SY7J28u3x6zjeFZ4PbiqGcoYecl5mSQwZSLY/IaiT2bao6Z54C8JYiMOS4Iw5VG2
 aLCpaAzlDw==
 =FkS9
 -----END PGP SIGNATURE-----
gpgsig -----BEGIN PGP SIGNATURE-----
 
 iQIzBAABCgAdFiEEiK/NIGsWEZVxh/FrYKtH/8kJUicFAmXl7rQACgkQYKtH/8kJ
 Uif1IA/9FXTi9VfB32rMoC6FOMl5AgYO1rW7d3VvLIhv503xkWTc8anW/LhljajC
 CMZ4xTC8XUvS2RkgKFleqi51sAXuuov+uTX4zfm1HPKXk0ZUFlcdllJvlD1PDL84
 IcLTSNKl3Eo+aNt8ccBaoAQ8X2bOAu4a9Zj0uV89OUtuVCKarKwZ4iLGBs/BRJMU
 ajQBgAX6CN8dgivyrm7y4RSdT0pSWudzpeqAdhaXrTXOa51aooUXLsM+hMuN/QFb
 zDbmYlnQFHZZBNhFDC4/+XWPopgvW+dNCri7kRnmuJWWgBFkNh/X4sv3FxA2J9oA
 mo3/EtLDy/WZuZaFGv5OCj0DO9UJVdGfs3ybGYbgKpnqwb2eRkdWdMnsxK53HOLK
 rSGet+6NiPQbjpApJaBzOePRHfUf/nMueYvep3SfDB0unSaquhTrR1SM3G5y5JFW
 oXzXyqi0GuerZdOEK9OVXJ+D85yz8BSKmfo0zyTSFbwmDrHdbHXknlXSwflZJHtC
 1qX3vuiuFo1bqCfGjQRb/tcojU6cU0HAJnswSOvOH8paYCb2mK1sX23dv1QgEEQl
 xdJBaERrmuN3ppZXsxkmKsttzKU9go3Q2TiWJgnUXXFe1Vz2csbn1jFczzLFxEXH
 kYQKvx0EfV2KxjPDNBWymJHoI0XqNSsyYtAPenDhKp4/biVIW0E=
 =mJ3H
 -----END PGP SIGNATURE-----

Merge tag 'samsung-drivers-6.9-2' of https://git.kernel.org/pub/scm/linux/kernel/git/krzk/linux into soc/drivers

Samsung SoC driver changes for v6.9, part two

1. Extend Exynos PMU (Power Management Unit) driver being also the
   syscon to main system controller registers block, to support Google
   GS101.  The Google GS101 has PMU registers protected and writing is
   available only via SMC.  The Exynos PMU will register its own custom
   regmap for such case of mixed MMIO+SMC.

2. Rework Samsung watchdog driver to get the regmap to PMU block not
   via syscon API, but from the Exynos PMU driver.  This is necessary
   for the watchdog driver to work on Google GS101.

* tag 'samsung-drivers-6.9-2' of https://git.kernel.org/pub/scm/linux/kernel/git/krzk/linux:
  watchdog: s3c2410_wdt: use exynos_get_pmu_regmap_by_phandle() for PMU regs
  soc: samsung: exynos-pmu: Add regmap support for SoCs that protect PMU regs
  MAINTAINERS: samsung: gs101: match patches touching Google Tensor SoC

Link: https://lore.kernel.org/r/20240227080755.34170-1-krzysztof.kozlowski@linaro.org
Signed-off-by: Arnd Bergmann <arnd@arndb.de>
This commit is contained in:
Arnd Bergmann 2024-03-04 16:54:27 +01:00
commit 63caaee9af
7 changed files with 250 additions and 8 deletions

View File

@ -9079,6 +9079,7 @@ F: Documentation/devicetree/bindings/clock/google,gs101-clock.yaml
F: arch/arm64/boot/dts/exynos/google/
F: drivers/clk/samsung/clk-gs101.c
F: include/dt-bindings/clock/google,gs101.h
K: [gG]oogle.?[tT]ensor
GPD POCKET FAN DRIVER
M: Hans de Goede <hdegoede@redhat.com>

View File

@ -42,6 +42,7 @@ config EXYNOS_PMU
depends on ARCH_EXYNOS || ((ARM || ARM64) && COMPILE_TEST)
select EXYNOS_PMU_ARM_DRIVERS if ARM && ARCH_EXYNOS
select MFD_CORE
select REGMAP_MMIO
# There is no need to enable these drivers for ARMv8
config EXYNOS_PMU_ARM_DRIVERS

View File

@ -5,6 +5,7 @@
//
// Exynos - CPU PMU(Power Management Unit) support
#include <linux/arm-smccc.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/mfd/core.h>
@ -12,19 +13,134 @@
#include <linux/of_platform.h>
#include <linux/platform_device.h>
#include <linux/delay.h>
#include <linux/regmap.h>
#include <linux/soc/samsung/exynos-regs-pmu.h>
#include <linux/soc/samsung/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 device *dev;
const struct exynos_pmu_data *pmu_data;
struct regmap *pmureg;
};
void __iomem *pmu_base_addr;
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 ret;
}
static int tensor_sec_update_bits(void *ctx, unsigned int reg,
unsigned int mask, unsigned int val)
{
/*
* Use atomic operations for PMU_ALIVE registers (offset 0~0x3FFF)
* as the target registers can be accessed by multiple masters.
*/
if (reg > PMUALIVE_MASK)
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)
{
@ -75,11 +191,41 @@ void exynos_sys_powerdown_conf(enum sys_powerdown mode)
#define exynos_pmu_data_arm_ptr(data) NULL
#endif
static const struct regmap_config regmap_smccfg = {
.name = "pmu_regs",
.reg_bits = 32,
.reg_stride = 4,
.val_bits = 32,
.fast_io = true,
.use_single_read = true,
.use_single_write = true,
.reg_read = tensor_sec_reg_read,
.reg_write = tensor_sec_reg_write,
.reg_update_bits = tensor_sec_update_bits,
};
static const struct regmap_config regmap_mmiocfg = {
.name = "pmu_regs",
.reg_bits = 32,
.reg_stride = 4,
.val_bits = 32,
.fast_io = true,
.use_single_read = true,
.use_single_write = true,
};
static const struct exynos_pmu_data gs101_pmu_data = {
.pmu_secure = true
};
/*
* PMU platform driver and devicetree bindings.
*/
static const struct of_device_id exynos_pmu_of_device_ids[] = {
{
.compatible = "google,gs101-pmu",
.data = &gs101_pmu_data,
}, {
.compatible = "samsung,exynos3250-pmu",
.data = exynos_pmu_data_arm_ptr(exynos3250_pmu_data),
}, {
@ -113,19 +259,75 @@ static const struct mfd_cell exynos_pmu_devs[] = {
{ .name = "exynos-clkout", },
};
/**
* exynos_get_pmu_regmap() - Obtain pmureg regmap
*
* Find the pmureg regmap previously configured in probe() and return regmap
* pointer.
*
* Return: A pointer to regmap if found or ERR_PTR error value.
*/
struct regmap *exynos_get_pmu_regmap(void)
{
struct device_node *np = of_find_matching_node(NULL,
exynos_pmu_of_device_ids);
if (np)
return syscon_node_to_regmap(np);
return exynos_get_pmu_regmap_by_phandle(np, NULL);
return ERR_PTR(-ENODEV);
}
EXPORT_SYMBOL_GPL(exynos_get_pmu_regmap);
/**
* exynos_get_pmu_regmap_by_phandle() - Obtain pmureg regmap via phandle
* @np: Device node holding PMU phandle property
* @propname: Name of property holding phandle value
*
* Find the pmureg regmap previously configured in probe() and return regmap
* pointer.
*
* Return: A pointer to regmap if found or ERR_PTR error value.
*/
struct regmap *exynos_get_pmu_regmap_by_phandle(struct device_node *np,
const char *propname)
{
struct exynos_pmu_context *ctx;
struct device_node *pmu_np;
struct device *dev;
if (propname)
pmu_np = of_parse_phandle(np, propname, 0);
else
pmu_np = np;
if (!pmu_np)
return ERR_PTR(-ENODEV);
/*
* Determine if exynos-pmu device has probed and therefore regmap
* has been created and can be returned to the caller. Otherwise we
* return -EPROBE_DEFER.
*/
dev = driver_find_device_by_of_node(&exynos_pmu_driver.driver,
(void *)pmu_np);
if (propname)
of_node_put(pmu_np);
if (!dev)
return ERR_PTR(-EPROBE_DEFER);
ctx = dev_get_drvdata(dev);
return ctx->pmureg;
}
EXPORT_SYMBOL_GPL(exynos_get_pmu_regmap_by_phandle);
static int exynos_pmu_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct regmap_config pmu_regmcfg;
struct regmap *regmap;
struct resource *res;
int ret;
pmu_base_addr = devm_platform_ioremap_resource(pdev, 0);
@ -137,9 +339,38 @@ static int exynos_pmu_probe(struct platform_device *pdev)
GFP_KERNEL);
if (!pmu_context)
return -ENOMEM;
pmu_context->dev = dev;
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (!res)
return -ENODEV;
pmu_context->pmu_data = of_device_get_match_data(dev);
/* For SoCs that secure PMU register writes use custom regmap */
if (pmu_context->pmu_data && pmu_context->pmu_data->pmu_secure) {
pmu_regmcfg = regmap_smccfg;
pmu_regmcfg.max_register = resource_size(res) -
pmu_regmcfg.reg_stride;
/* Need physical address for SMC call */
regmap = devm_regmap_init(dev, NULL,
(void *)(uintptr_t)res->start,
&pmu_regmcfg);
} else {
/* All other SoCs use a MMIO regmap */
pmu_regmcfg = regmap_mmiocfg;
pmu_regmcfg.max_register = resource_size(res) -
pmu_regmcfg.reg_stride;
regmap = devm_regmap_init_mmio(dev, pmu_base_addr,
&pmu_regmcfg);
}
if (IS_ERR(regmap))
return dev_err_probe(&pdev->dev, PTR_ERR(regmap),
"regmap init failed\n");
pmu_context->pmureg = regmap;
pmu_context->dev = dev;
if (pmu_context->pmu_data && pmu_context->pmu_data->pmu_init)
pmu_context->pmu_data->pmu_init();

View File

@ -21,6 +21,7 @@ struct exynos_pmu_conf {
struct exynos_pmu_data {
const struct exynos_pmu_conf *pmu_config;
const struct exynos_pmu_conf *pmu_config_extra;
bool pmu_secure;
void (*pmu_init)(void);
void (*powerdown_conf)(enum sys_powerdown);

View File

@ -512,7 +512,6 @@ config S3C2410_WATCHDOG
tristate "S3C6410/S5Pv210/Exynos Watchdog"
depends on ARCH_S3C64XX || ARCH_S5PV210 || ARCH_EXYNOS || COMPILE_TEST
select WATCHDOG_CORE
select MFD_SYSCON if ARCH_EXYNOS
help
Watchdog timer block in the Samsung S3C64xx, S5Pv210 and Exynos
SoCs. This will reboot the system when the timer expires with

View File

@ -24,9 +24,9 @@
#include <linux/slab.h>
#include <linux/err.h>
#include <linux/of.h>
#include <linux/mfd/syscon.h>
#include <linux/regmap.h>
#include <linux/delay.h>
#include <linux/soc/samsung/exynos-pmu.h>
#define S3C2410_WTCON 0x00
#define S3C2410_WTDAT 0x04
@ -699,11 +699,11 @@ static int s3c2410wdt_probe(struct platform_device *pdev)
return ret;
if (wdt->drv_data->quirks & QUIRKS_HAVE_PMUREG) {
wdt->pmureg = syscon_regmap_lookup_by_phandle(dev->of_node,
"samsung,syscon-phandle");
wdt->pmureg = exynos_get_pmu_regmap_by_phandle(dev->of_node,
"samsung,syscon-phandle");
if (IS_ERR(wdt->pmureg))
return dev_err_probe(dev, PTR_ERR(wdt->pmureg),
"syscon regmap lookup failed.\n");
"PMU regmap lookup failed.\n");
}
wdt_irq = platform_get_irq(pdev, 0);

View File

@ -10,6 +10,7 @@
#define __LINUX_SOC_EXYNOS_PMU_H
struct regmap;
struct device_node;
enum sys_powerdown {
SYS_AFTR,
@ -20,12 +21,20 @@ enum sys_powerdown {
extern void exynos_sys_powerdown_conf(enum sys_powerdown mode);
#ifdef CONFIG_EXYNOS_PMU
extern struct regmap *exynos_get_pmu_regmap(void);
struct regmap *exynos_get_pmu_regmap(void);
struct regmap *exynos_get_pmu_regmap_by_phandle(struct device_node *np,
const char *propname);
#else
static inline struct regmap *exynos_get_pmu_regmap(void)
{
return ERR_PTR(-ENODEV);
}
static inline struct regmap *exynos_get_pmu_regmap_by_phandle(struct device_node *np,
const char *propname)
{
return ERR_PTR(-ENODEV);
}
#endif
#endif /* __LINUX_SOC_EXYNOS_PMU_H */