Skip to content

Commit 63caaee

Browse files
committed
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/[email protected] Signed-off-by: Arnd Bergmann <[email protected]>
2 parents daa0987 + 746f077 commit 63caaee

File tree

7 files changed

+250
-8
lines changed

7 files changed

+250
-8
lines changed

MAINTAINERS

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9079,6 +9079,7 @@ F: Documentation/devicetree/bindings/clock/google,gs101-clock.yaml
90799079
F: arch/arm64/boot/dts/exynos/google/
90809080
F: drivers/clk/samsung/clk-gs101.c
90819081
F: include/dt-bindings/clock/google,gs101.h
9082+
K: [gG]oogle.?[tT]ensor
90829083

90839084
GPD POCKET FAN DRIVER
90849085
M: Hans de Goede <[email protected]>

drivers/soc/samsung/Kconfig

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ config EXYNOS_PMU
4242
depends on ARCH_EXYNOS || ((ARM || ARM64) && COMPILE_TEST)
4343
select EXYNOS_PMU_ARM_DRIVERS if ARM && ARCH_EXYNOS
4444
select MFD_CORE
45+
select REGMAP_MMIO
4546

4647
# There is no need to enable these drivers for ARMv8
4748
config EXYNOS_PMU_ARM_DRIVERS

drivers/soc/samsung/exynos-pmu.c

Lines changed: 233 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,26 +5,142 @@
55
//
66
// Exynos - CPU PMU(Power Management Unit) support
77

8+
#include <linux/arm-smccc.h>
89
#include <linux/of.h>
910
#include <linux/of_address.h>
1011
#include <linux/mfd/core.h>
1112
#include <linux/mfd/syscon.h>
1213
#include <linux/of_platform.h>
1314
#include <linux/platform_device.h>
1415
#include <linux/delay.h>
16+
#include <linux/regmap.h>
1517

1618
#include <linux/soc/samsung/exynos-regs-pmu.h>
1719
#include <linux/soc/samsung/exynos-pmu.h>
1820

1921
#include "exynos-pmu.h"
2022

23+
#define PMUALIVE_MASK GENMASK(13, 0)
24+
#define TENSOR_SET_BITS (BIT(15) | BIT(14))
25+
#define TENSOR_CLR_BITS BIT(15)
26+
#define TENSOR_SMC_PMU_SEC_REG 0x82000504
27+
#define TENSOR_PMUREG_READ 0
28+
#define TENSOR_PMUREG_WRITE 1
29+
#define TENSOR_PMUREG_RMW 2
30+
2131
struct exynos_pmu_context {
2232
struct device *dev;
2333
const struct exynos_pmu_data *pmu_data;
34+
struct regmap *pmureg;
2435
};
2536

2637
void __iomem *pmu_base_addr;
2738
static struct exynos_pmu_context *pmu_context;
39+
/* forward declaration */
40+
static struct platform_driver exynos_pmu_driver;
41+
42+
/*
43+
* Tensor SoCs are configured so that PMU_ALIVE registers can only be written
44+
* from EL3, but are still read accessible. As Linux needs to write some of
45+
* these registers, the following functions are provided and exposed via
46+
* regmap.
47+
*
48+
* Note: This SMC interface is known to be implemented on gs101 and derivative
49+
* SoCs.
50+
*/
51+
52+
/* Write to a protected PMU register. */
53+
static int tensor_sec_reg_write(void *context, unsigned int reg,
54+
unsigned int val)
55+
{
56+
struct arm_smccc_res res;
57+
unsigned long pmu_base = (unsigned long)context;
58+
59+
arm_smccc_smc(TENSOR_SMC_PMU_SEC_REG, pmu_base + reg,
60+
TENSOR_PMUREG_WRITE, val, 0, 0, 0, 0, &res);
61+
62+
/* returns -EINVAL if access isn't allowed or 0 */
63+
if (res.a0)
64+
pr_warn("%s(): SMC failed: %d\n", __func__, (int)res.a0);
65+
66+
return (int)res.a0;
67+
}
68+
69+
/* Read/Modify/Write a protected PMU register. */
70+
static int tensor_sec_reg_rmw(void *context, unsigned int reg,
71+
unsigned int mask, unsigned int val)
72+
{
73+
struct arm_smccc_res res;
74+
unsigned long pmu_base = (unsigned long)context;
75+
76+
arm_smccc_smc(TENSOR_SMC_PMU_SEC_REG, pmu_base + reg,
77+
TENSOR_PMUREG_RMW, mask, val, 0, 0, 0, &res);
78+
79+
/* returns -EINVAL if access isn't allowed or 0 */
80+
if (res.a0)
81+
pr_warn("%s(): SMC failed: %d\n", __func__, (int)res.a0);
82+
83+
return (int)res.a0;
84+
}
85+
86+
/*
87+
* Read a protected PMU register. All PMU registers can be read by Linux.
88+
* Note: The SMC read register is not used, as only registers that can be
89+
* written are readable via SMC.
90+
*/
91+
static int tensor_sec_reg_read(void *context, unsigned int reg,
92+
unsigned int *val)
93+
{
94+
*val = pmu_raw_readl(reg);
95+
return 0;
96+
}
97+
98+
/*
99+
* For SoCs that have set/clear bit hardware this function can be used when
100+
* the PMU register will be accessed by multiple masters.
101+
*
102+
* For example, to set bits 13:8 in PMU reg offset 0x3e80
103+
* tensor_set_bits_atomic(ctx, 0x3e80, 0x3f00, 0x3f00);
104+
*
105+
* Set bit 8, and clear bits 13:9 PMU reg offset 0x3e80
106+
* tensor_set_bits_atomic(0x3e80, 0x100, 0x3f00);
107+
*/
108+
static int tensor_set_bits_atomic(void *ctx, unsigned int offset, u32 val,
109+
u32 mask)
110+
{
111+
int ret;
112+
unsigned int i;
113+
114+
for (i = 0; i < 32; i++) {
115+
if (!(mask & BIT(i)))
116+
continue;
117+
118+
offset &= ~TENSOR_SET_BITS;
119+
120+
if (val & BIT(i))
121+
offset |= TENSOR_SET_BITS;
122+
else
123+
offset |= TENSOR_CLR_BITS;
124+
125+
ret = tensor_sec_reg_write(ctx, offset, i);
126+
if (ret)
127+
return ret;
128+
}
129+
return ret;
130+
}
131+
132+
static int tensor_sec_update_bits(void *ctx, unsigned int reg,
133+
unsigned int mask, unsigned int val)
134+
{
135+
/*
136+
* Use atomic operations for PMU_ALIVE registers (offset 0~0x3FFF)
137+
* as the target registers can be accessed by multiple masters.
138+
*/
139+
if (reg > PMUALIVE_MASK)
140+
return tensor_sec_reg_rmw(ctx, reg, mask, val);
141+
142+
return tensor_set_bits_atomic(ctx, reg, val, mask);
143+
}
28144

29145
void pmu_raw_writel(u32 val, u32 offset)
30146
{
@@ -75,11 +191,41 @@ void exynos_sys_powerdown_conf(enum sys_powerdown mode)
75191
#define exynos_pmu_data_arm_ptr(data) NULL
76192
#endif
77193

194+
static const struct regmap_config regmap_smccfg = {
195+
.name = "pmu_regs",
196+
.reg_bits = 32,
197+
.reg_stride = 4,
198+
.val_bits = 32,
199+
.fast_io = true,
200+
.use_single_read = true,
201+
.use_single_write = true,
202+
.reg_read = tensor_sec_reg_read,
203+
.reg_write = tensor_sec_reg_write,
204+
.reg_update_bits = tensor_sec_update_bits,
205+
};
206+
207+
static const struct regmap_config regmap_mmiocfg = {
208+
.name = "pmu_regs",
209+
.reg_bits = 32,
210+
.reg_stride = 4,
211+
.val_bits = 32,
212+
.fast_io = true,
213+
.use_single_read = true,
214+
.use_single_write = true,
215+
};
216+
217+
static const struct exynos_pmu_data gs101_pmu_data = {
218+
.pmu_secure = true
219+
};
220+
78221
/*
79222
* PMU platform driver and devicetree bindings.
80223
*/
81224
static const struct of_device_id exynos_pmu_of_device_ids[] = {
82225
{
226+
.compatible = "google,gs101-pmu",
227+
.data = &gs101_pmu_data,
228+
}, {
83229
.compatible = "samsung,exynos3250-pmu",
84230
.data = exynos_pmu_data_arm_ptr(exynos3250_pmu_data),
85231
}, {
@@ -113,19 +259,75 @@ static const struct mfd_cell exynos_pmu_devs[] = {
113259
{ .name = "exynos-clkout", },
114260
};
115261

262+
/**
263+
* exynos_get_pmu_regmap() - Obtain pmureg regmap
264+
*
265+
* Find the pmureg regmap previously configured in probe() and return regmap
266+
* pointer.
267+
*
268+
* Return: A pointer to regmap if found or ERR_PTR error value.
269+
*/
116270
struct regmap *exynos_get_pmu_regmap(void)
117271
{
118272
struct device_node *np = of_find_matching_node(NULL,
119273
exynos_pmu_of_device_ids);
120274
if (np)
121-
return syscon_node_to_regmap(np);
275+
return exynos_get_pmu_regmap_by_phandle(np, NULL);
122276
return ERR_PTR(-ENODEV);
123277
}
124278
EXPORT_SYMBOL_GPL(exynos_get_pmu_regmap);
125279

280+
/**
281+
* exynos_get_pmu_regmap_by_phandle() - Obtain pmureg regmap via phandle
282+
* @np: Device node holding PMU phandle property
283+
* @propname: Name of property holding phandle value
284+
*
285+
* Find the pmureg regmap previously configured in probe() and return regmap
286+
* pointer.
287+
*
288+
* Return: A pointer to regmap if found or ERR_PTR error value.
289+
*/
290+
struct regmap *exynos_get_pmu_regmap_by_phandle(struct device_node *np,
291+
const char *propname)
292+
{
293+
struct exynos_pmu_context *ctx;
294+
struct device_node *pmu_np;
295+
struct device *dev;
296+
297+
if (propname)
298+
pmu_np = of_parse_phandle(np, propname, 0);
299+
else
300+
pmu_np = np;
301+
302+
if (!pmu_np)
303+
return ERR_PTR(-ENODEV);
304+
305+
/*
306+
* Determine if exynos-pmu device has probed and therefore regmap
307+
* has been created and can be returned to the caller. Otherwise we
308+
* return -EPROBE_DEFER.
309+
*/
310+
dev = driver_find_device_by_of_node(&exynos_pmu_driver.driver,
311+
(void *)pmu_np);
312+
313+
if (propname)
314+
of_node_put(pmu_np);
315+
316+
if (!dev)
317+
return ERR_PTR(-EPROBE_DEFER);
318+
319+
ctx = dev_get_drvdata(dev);
320+
321+
return ctx->pmureg;
322+
}
323+
EXPORT_SYMBOL_GPL(exynos_get_pmu_regmap_by_phandle);
324+
126325
static int exynos_pmu_probe(struct platform_device *pdev)
127326
{
128327
struct device *dev = &pdev->dev;
328+
struct regmap_config pmu_regmcfg;
329+
struct regmap *regmap;
330+
struct resource *res;
129331
int ret;
130332

131333
pmu_base_addr = devm_platform_ioremap_resource(pdev, 0);
@@ -137,9 +339,38 @@ static int exynos_pmu_probe(struct platform_device *pdev)
137339
GFP_KERNEL);
138340
if (!pmu_context)
139341
return -ENOMEM;
140-
pmu_context->dev = dev;
342+
343+
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
344+
if (!res)
345+
return -ENODEV;
346+
141347
pmu_context->pmu_data = of_device_get_match_data(dev);
142348

349+
/* For SoCs that secure PMU register writes use custom regmap */
350+
if (pmu_context->pmu_data && pmu_context->pmu_data->pmu_secure) {
351+
pmu_regmcfg = regmap_smccfg;
352+
pmu_regmcfg.max_register = resource_size(res) -
353+
pmu_regmcfg.reg_stride;
354+
/* Need physical address for SMC call */
355+
regmap = devm_regmap_init(dev, NULL,
356+
(void *)(uintptr_t)res->start,
357+
&pmu_regmcfg);
358+
} else {
359+
/* All other SoCs use a MMIO regmap */
360+
pmu_regmcfg = regmap_mmiocfg;
361+
pmu_regmcfg.max_register = resource_size(res) -
362+
pmu_regmcfg.reg_stride;
363+
regmap = devm_regmap_init_mmio(dev, pmu_base_addr,
364+
&pmu_regmcfg);
365+
}
366+
367+
if (IS_ERR(regmap))
368+
return dev_err_probe(&pdev->dev, PTR_ERR(regmap),
369+
"regmap init failed\n");
370+
371+
pmu_context->pmureg = regmap;
372+
pmu_context->dev = dev;
373+
143374
if (pmu_context->pmu_data && pmu_context->pmu_data->pmu_init)
144375
pmu_context->pmu_data->pmu_init();
145376

drivers/soc/samsung/exynos-pmu.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ struct exynos_pmu_conf {
2121
struct exynos_pmu_data {
2222
const struct exynos_pmu_conf *pmu_config;
2323
const struct exynos_pmu_conf *pmu_config_extra;
24+
bool pmu_secure;
2425

2526
void (*pmu_init)(void);
2627
void (*powerdown_conf)(enum sys_powerdown);

drivers/watchdog/Kconfig

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -512,7 +512,6 @@ config S3C2410_WATCHDOG
512512
tristate "S3C6410/S5Pv210/Exynos Watchdog"
513513
depends on ARCH_S3C64XX || ARCH_S5PV210 || ARCH_EXYNOS || COMPILE_TEST
514514
select WATCHDOG_CORE
515-
select MFD_SYSCON if ARCH_EXYNOS
516515
help
517516
Watchdog timer block in the Samsung S3C64xx, S5Pv210 and Exynos
518517
SoCs. This will reboot the system when the timer expires with

drivers/watchdog/s3c2410_wdt.c

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -24,9 +24,9 @@
2424
#include <linux/slab.h>
2525
#include <linux/err.h>
2626
#include <linux/of.h>
27-
#include <linux/mfd/syscon.h>
2827
#include <linux/regmap.h>
2928
#include <linux/delay.h>
29+
#include <linux/soc/samsung/exynos-pmu.h>
3030

3131
#define S3C2410_WTCON 0x00
3232
#define S3C2410_WTDAT 0x04
@@ -699,11 +699,11 @@ static int s3c2410wdt_probe(struct platform_device *pdev)
699699
return ret;
700700

701701
if (wdt->drv_data->quirks & QUIRKS_HAVE_PMUREG) {
702-
wdt->pmureg = syscon_regmap_lookup_by_phandle(dev->of_node,
703-
"samsung,syscon-phandle");
702+
wdt->pmureg = exynos_get_pmu_regmap_by_phandle(dev->of_node,
703+
"samsung,syscon-phandle");
704704
if (IS_ERR(wdt->pmureg))
705705
return dev_err_probe(dev, PTR_ERR(wdt->pmureg),
706-
"syscon regmap lookup failed.\n");
706+
"PMU regmap lookup failed.\n");
707707
}
708708

709709
wdt_irq = platform_get_irq(pdev, 0);

include/linux/soc/samsung/exynos-pmu.h

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
#define __LINUX_SOC_EXYNOS_PMU_H
1111

1212
struct regmap;
13+
struct device_node;
1314

1415
enum sys_powerdown {
1516
SYS_AFTR,
@@ -20,12 +21,20 @@ enum sys_powerdown {
2021

2122
extern void exynos_sys_powerdown_conf(enum sys_powerdown mode);
2223
#ifdef CONFIG_EXYNOS_PMU
23-
extern struct regmap *exynos_get_pmu_regmap(void);
24+
struct regmap *exynos_get_pmu_regmap(void);
25+
struct regmap *exynos_get_pmu_regmap_by_phandle(struct device_node *np,
26+
const char *propname);
2427
#else
2528
static inline struct regmap *exynos_get_pmu_regmap(void)
2629
{
2730
return ERR_PTR(-ENODEV);
2831
}
32+
33+
static inline struct regmap *exynos_get_pmu_regmap_by_phandle(struct device_node *np,
34+
const char *propname)
35+
{
36+
return ERR_PTR(-ENODEV);
37+
}
2938
#endif
3039

3140
#endif /* __LINUX_SOC_EXYNOS_PMU_H */

0 commit comments

Comments
 (0)