Skip to content

Commit 8c7cf0f

Browse files
andredsre
authored andcommitted
power: reset: syscon-reboot: add gs101-specific reset
Linux supports a couple different reset modes, but this driver here doesn't distinguish between them and issues the same syscon register write irrespective of the reset mode requested by the kernel. Since DTs should not encode register writes (see e.g. [1]), update this driver to support different reset modes based on DT compatible match. At the same time, add support for Google GS101, which does support cold, hard, warm, and soft. As an example why this is useful, other than properly supporting the Linux reboot= kernel command line option or sysfs entry, this change allows gs101-platforms to default to a more secure cold-reset, but also to warm-reset in case RAM contents needs to be retained across the reset. Link: https://lore.kernel.org/all/20250227132644.GA1924628-robh@kernel.org/ [1] Signed-off-by: André Draszik <andre.draszik@linaro.org> Link: https://lore.kernel.org/r/20250401-syscon-reboot-reset-mode-v5-2-5b9357442363@linaro.org Signed-off-by: Sebastian Reichel <sebastian.reichel@collabora.com>
1 parent 1495c1a commit 8c7cf0f

File tree

1 file changed

+77
-21
lines changed

1 file changed

+77
-21
lines changed

drivers/power/reset/syscon-reboot.c

Lines changed: 77 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -14,11 +14,24 @@
1414
#include <linux/reboot.h>
1515
#include <linux/regmap.h>
1616

17-
struct syscon_reboot_context {
18-
struct regmap *map;
17+
struct reboot_mode_bits {
1918
u32 offset;
20-
u32 value;
2119
u32 mask;
20+
u32 value;
21+
bool valid;
22+
};
23+
24+
struct reboot_data {
25+
struct reboot_mode_bits mode_bits[REBOOT_SOFT + 1];
26+
struct reboot_mode_bits catchall;
27+
};
28+
29+
struct syscon_reboot_context {
30+
struct regmap *map;
31+
32+
const struct reboot_data *rd; /* from of match data, if any */
33+
struct reboot_mode_bits catchall; /* from DT */
34+
2235
struct notifier_block restart_handler;
2336
};
2437

@@ -28,9 +41,21 @@ static int syscon_restart_handle(struct notifier_block *this,
2841
struct syscon_reboot_context *ctx =
2942
container_of(this, struct syscon_reboot_context,
3043
restart_handler);
44+
const struct reboot_mode_bits *mode_bits;
45+
46+
if (ctx->rd) {
47+
if (mode < ARRAY_SIZE(ctx->rd->mode_bits) &&
48+
ctx->rd->mode_bits[mode].valid)
49+
mode_bits = &ctx->rd->mode_bits[mode];
50+
else
51+
mode_bits = &ctx->rd->catchall;
52+
} else {
53+
mode_bits = &ctx->catchall;
54+
}
3155

3256
/* Issue the reboot */
33-
regmap_update_bits(ctx->map, ctx->offset, ctx->mask, ctx->value);
57+
regmap_update_bits(ctx->map, mode_bits->offset, mode_bits->mask,
58+
mode_bits->value);
3459

3560
mdelay(1000);
3661

@@ -42,7 +67,6 @@ static int syscon_reboot_probe(struct platform_device *pdev)
4267
{
4368
struct syscon_reboot_context *ctx;
4469
struct device *dev = &pdev->dev;
45-
int mask_err, value_err;
4670
int priority;
4771
int err;
4872

@@ -60,24 +84,33 @@ static int syscon_reboot_probe(struct platform_device *pdev)
6084
if (of_property_read_s32(pdev->dev.of_node, "priority", &priority))
6185
priority = 192;
6286

63-
if (of_property_read_u32(pdev->dev.of_node, "offset", &ctx->offset))
64-
if (of_property_read_u32(pdev->dev.of_node, "reg", &ctx->offset))
65-
return -EINVAL;
87+
ctx->rd = of_device_get_match_data(dev);
88+
if (!ctx->rd) {
89+
int mask_err, value_err;
6690

67-
value_err = of_property_read_u32(pdev->dev.of_node, "value", &ctx->value);
68-
mask_err = of_property_read_u32(pdev->dev.of_node, "mask", &ctx->mask);
69-
if (value_err && mask_err) {
70-
dev_err(dev, "unable to read 'value' and 'mask'");
71-
return -EINVAL;
72-
}
91+
if (of_property_read_u32(pdev->dev.of_node, "offset",
92+
&ctx->catchall.offset) &&
93+
of_property_read_u32(pdev->dev.of_node, "reg",
94+
&ctx->catchall.offset))
95+
return -EINVAL;
7396

74-
if (value_err) {
75-
/* support old binding */
76-
ctx->value = ctx->mask;
77-
ctx->mask = 0xFFFFFFFF;
78-
} else if (mask_err) {
79-
/* support value without mask*/
80-
ctx->mask = 0xFFFFFFFF;
97+
value_err = of_property_read_u32(pdev->dev.of_node, "value",
98+
&ctx->catchall.value);
99+
mask_err = of_property_read_u32(pdev->dev.of_node, "mask",
100+
&ctx->catchall.mask);
101+
if (value_err && mask_err) {
102+
dev_err(dev, "unable to read 'value' and 'mask'");
103+
return -EINVAL;
104+
}
105+
106+
if (value_err) {
107+
/* support old binding */
108+
ctx->catchall.value = ctx->catchall.mask;
109+
ctx->catchall.mask = 0xFFFFFFFF;
110+
} else if (mask_err) {
111+
/* support value without mask */
112+
ctx->catchall.mask = 0xFFFFFFFF;
113+
}
81114
}
82115

83116
ctx->restart_handler.notifier_call = syscon_restart_handle;
@@ -89,7 +122,30 @@ static int syscon_reboot_probe(struct platform_device *pdev)
89122
return err;
90123
}
91124

125+
static const struct reboot_data gs101_reboot_data = {
126+
.mode_bits = {
127+
[REBOOT_WARM] = {
128+
.offset = 0x3a00, /* SYSTEM_CONFIGURATION */
129+
.mask = 0x00000002, /* SWRESET_SYSTEM */
130+
.value = 0x00000002,
131+
.valid = true,
132+
},
133+
[REBOOT_SOFT] = {
134+
.offset = 0x3a00, /* SYSTEM_CONFIGURATION */
135+
.mask = 0x00000002, /* SWRESET_SYSTEM */
136+
.value = 0x00000002,
137+
.valid = true,
138+
},
139+
},
140+
.catchall = {
141+
.offset = 0x3e9c, /* PAD_CTRL_PWR_HOLD */
142+
.mask = 0x00000100,
143+
.value = 0x00000000,
144+
},
145+
};
146+
92147
static const struct of_device_id syscon_reboot_of_match[] = {
148+
{ .compatible = "google,gs101-reboot", .data = &gs101_reboot_data },
93149
{ .compatible = "syscon-reboot" },
94150
{}
95151
};

0 commit comments

Comments
 (0)