Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

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>

authored by

André Draszik and committed by
Sebastian Reichel
8c7cf0fc 1495c1aa

+76 -20
+76 -20
drivers/power/reset/syscon-reboot.c
··· 14 14 #include <linux/reboot.h> 15 15 #include <linux/regmap.h> 16 16 17 + struct reboot_mode_bits { 18 + u32 offset; 19 + 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 + 17 29 struct syscon_reboot_context { 18 30 struct regmap *map; 19 - u32 offset; 20 - u32 value; 21 - u32 mask; 31 + 32 + const struct reboot_data *rd; /* from of match data, if any */ 33 + struct reboot_mode_bits catchall; /* from DT */ 34 + 22 35 struct notifier_block restart_handler; 23 36 }; 24 37 ··· 41 28 struct syscon_reboot_context *ctx = 42 29 container_of(this, struct syscon_reboot_context, 43 30 restart_handler); 31 + const struct reboot_mode_bits *mode_bits; 32 + 33 + if (ctx->rd) { 34 + if (mode < ARRAY_SIZE(ctx->rd->mode_bits) && 35 + ctx->rd->mode_bits[mode].valid) 36 + mode_bits = &ctx->rd->mode_bits[mode]; 37 + else 38 + mode_bits = &ctx->rd->catchall; 39 + } else { 40 + mode_bits = &ctx->catchall; 41 + } 44 42 45 43 /* Issue the reboot */ 46 - regmap_update_bits(ctx->map, ctx->offset, ctx->mask, ctx->value); 44 + regmap_update_bits(ctx->map, mode_bits->offset, mode_bits->mask, 45 + mode_bits->value); 47 46 48 47 mdelay(1000); 49 48 ··· 67 42 { 68 43 struct syscon_reboot_context *ctx; 69 44 struct device *dev = &pdev->dev; 70 - int mask_err, value_err; 71 45 int priority; 72 46 int err; 73 47 ··· 84 60 if (of_property_read_s32(pdev->dev.of_node, "priority", &priority)) 85 61 priority = 192; 86 62 87 - if (of_property_read_u32(pdev->dev.of_node, "offset", &ctx->offset)) 88 - if (of_property_read_u32(pdev->dev.of_node, "reg", &ctx->offset)) 63 + ctx->rd = of_device_get_match_data(dev); 64 + if (!ctx->rd) { 65 + int mask_err, value_err; 66 + 67 + if (of_property_read_u32(pdev->dev.of_node, "offset", 68 + &ctx->catchall.offset) && 69 + of_property_read_u32(pdev->dev.of_node, "reg", 70 + &ctx->catchall.offset)) 89 71 return -EINVAL; 90 72 91 - value_err = of_property_read_u32(pdev->dev.of_node, "value", &ctx->value); 92 - mask_err = of_property_read_u32(pdev->dev.of_node, "mask", &ctx->mask); 93 - if (value_err && mask_err) { 94 - dev_err(dev, "unable to read 'value' and 'mask'"); 95 - return -EINVAL; 96 - } 73 + value_err = of_property_read_u32(pdev->dev.of_node, "value", 74 + &ctx->catchall.value); 75 + mask_err = of_property_read_u32(pdev->dev.of_node, "mask", 76 + &ctx->catchall.mask); 77 + if (value_err && mask_err) { 78 + dev_err(dev, "unable to read 'value' and 'mask'"); 79 + return -EINVAL; 80 + } 97 81 98 - if (value_err) { 99 - /* support old binding */ 100 - ctx->value = ctx->mask; 101 - ctx->mask = 0xFFFFFFFF; 102 - } else if (mask_err) { 103 - /* support value without mask*/ 104 - ctx->mask = 0xFFFFFFFF; 82 + if (value_err) { 83 + /* support old binding */ 84 + ctx->catchall.value = ctx->catchall.mask; 85 + ctx->catchall.mask = 0xFFFFFFFF; 86 + } else if (mask_err) { 87 + /* support value without mask */ 88 + ctx->catchall.mask = 0xFFFFFFFF; 89 + } 105 90 } 106 91 107 92 ctx->restart_handler.notifier_call = syscon_restart_handle; ··· 122 89 return err; 123 90 } 124 91 92 + static const struct reboot_data gs101_reboot_data = { 93 + .mode_bits = { 94 + [REBOOT_WARM] = { 95 + .offset = 0x3a00, /* SYSTEM_CONFIGURATION */ 96 + .mask = 0x00000002, /* SWRESET_SYSTEM */ 97 + .value = 0x00000002, 98 + .valid = true, 99 + }, 100 + [REBOOT_SOFT] = { 101 + .offset = 0x3a00, /* SYSTEM_CONFIGURATION */ 102 + .mask = 0x00000002, /* SWRESET_SYSTEM */ 103 + .value = 0x00000002, 104 + .valid = true, 105 + }, 106 + }, 107 + .catchall = { 108 + .offset = 0x3e9c, /* PAD_CTRL_PWR_HOLD */ 109 + .mask = 0x00000100, 110 + .value = 0x00000000, 111 + }, 112 + }; 113 + 125 114 static const struct of_device_id syscon_reboot_of_match[] = { 115 + { .compatible = "google,gs101-reboot", .data = &gs101_reboot_data }, 126 116 { .compatible = "syscon-reboot" }, 127 117 {} 128 118 };