Commit | Line | Data |
---|---|---|
c942fddf | 1 | // SPDX-License-Identifier: GPL-2.0-or-later |
09fb07bc FK |
2 | /* |
3 | * Generic Syscon Reboot Driver | |
4 | * | |
5 | * Copyright (c) 2013, Applied Micro Circuits Corporation | |
6 | * Author: Feng Kan <fkan@apm.com> | |
09fb07bc | 7 | */ |
afaebbdb | 8 | #include <linux/delay.h> |
09fb07bc | 9 | #include <linux/io.h> |
09fb07bc FK |
10 | #include <linux/notifier.h> |
11 | #include <linux/mfd/syscon.h> | |
afaebbdb FK |
12 | #include <linux/of_address.h> |
13 | #include <linux/of_device.h> | |
14 | #include <linux/platform_device.h> | |
09fb07bc | 15 | #include <linux/reboot.h> |
afaebbdb | 16 | #include <linux/regmap.h> |
09fb07bc FK |
17 | |
18 | struct syscon_reboot_context { | |
19 | struct regmap *map; | |
20 | u32 offset; | |
077d9951 | 21 | u32 value; |
09fb07bc FK |
22 | u32 mask; |
23 | struct notifier_block restart_handler; | |
24 | }; | |
25 | ||
09fb07bc FK |
26 | static int syscon_restart_handle(struct notifier_block *this, |
27 | unsigned long mode, void *cmd) | |
28 | { | |
afaebbdb FK |
29 | struct syscon_reboot_context *ctx = |
30 | container_of(this, struct syscon_reboot_context, | |
31 | restart_handler); | |
09fb07bc FK |
32 | |
33 | /* Issue the reboot */ | |
077d9951 | 34 | regmap_update_bits(ctx->map, ctx->offset, ctx->mask, ctx->value); |
09fb07bc | 35 | |
afaebbdb | 36 | mdelay(1000); |
09fb07bc FK |
37 | |
38 | pr_emerg("Unable to restart system\n"); | |
39 | return NOTIFY_DONE; | |
40 | } | |
41 | ||
42 | static int syscon_reboot_probe(struct platform_device *pdev) | |
43 | { | |
44 | struct syscon_reboot_context *ctx; | |
45 | struct device *dev = &pdev->dev; | |
077d9951 | 46 | int mask_err, value_err; |
09fb07bc FK |
47 | int err; |
48 | ||
49 | ctx = devm_kzalloc(&pdev->dev, sizeof(*ctx), GFP_KERNEL); | |
50 | if (!ctx) | |
51 | return -ENOMEM; | |
52 | ||
53 | ctx->map = syscon_regmap_lookup_by_phandle(dev->of_node, "regmap"); | |
c3d80522 SS |
54 | if (IS_ERR(ctx->map)) { |
55 | ctx->map = syscon_node_to_regmap(dev->parent->of_node); | |
56 | if (IS_ERR(ctx->map)) | |
57 | return PTR_ERR(ctx->map); | |
58 | } | |
09fb07bc FK |
59 | |
60 | if (of_property_read_u32(pdev->dev.of_node, "offset", &ctx->offset)) | |
61 | return -EINVAL; | |
62 | ||
077d9951 MS |
63 | value_err = of_property_read_u32(pdev->dev.of_node, "value", &ctx->value); |
64 | mask_err = of_property_read_u32(pdev->dev.of_node, "mask", &ctx->mask); | |
65 | if (value_err && mask_err) { | |
66 | dev_err(dev, "unable to read 'value' and 'mask'"); | |
09fb07bc | 67 | return -EINVAL; |
077d9951 MS |
68 | } |
69 | ||
70 | if (value_err) { | |
71 | /* support old binding */ | |
72 | ctx->value = ctx->mask; | |
73 | ctx->mask = 0xFFFFFFFF; | |
74 | } else if (mask_err) { | |
75 | /* support value without mask*/ | |
76 | ctx->mask = 0xFFFFFFFF; | |
77 | } | |
09fb07bc FK |
78 | |
79 | ctx->restart_handler.notifier_call = syscon_restart_handle; | |
b81180b3 | 80 | ctx->restart_handler.priority = 192; |
09fb07bc FK |
81 | err = register_restart_handler(&ctx->restart_handler); |
82 | if (err) | |
83 | dev_err(dev, "can't register restart notifier (err=%d)\n", err); | |
84 | ||
afaebbdb | 85 | return err; |
09fb07bc FK |
86 | } |
87 | ||
8fb08855 | 88 | static const struct of_device_id syscon_reboot_of_match[] = { |
09fb07bc FK |
89 | { .compatible = "syscon-reboot" }, |
90 | {} | |
91 | }; | |
92 | ||
93 | static struct platform_driver syscon_reboot_driver = { | |
94 | .probe = syscon_reboot_probe, | |
95 | .driver = { | |
96 | .name = "syscon-reboot", | |
97 | .of_match_table = syscon_reboot_of_match, | |
98 | }, | |
99 | }; | |
e35415e5 | 100 | builtin_platform_driver(syscon_reboot_driver); |