Commit | Line | Data |
---|---|---|
0e545f57 LW |
1 | /* |
2 | * Copyright (C) 2014 Linaro Ltd. | |
3 | * | |
4 | * Author: Linus Walleij <linus.walleij@linaro.org> | |
5 | * | |
6 | * This program is free software; you can redistribute it and/or modify | |
7 | * it under the terms of the GNU General Public License version 2, as | |
8 | * published by the Free Software Foundation. | |
9 | * | |
10 | */ | |
11 | #include <linux/init.h> | |
12 | #include <linux/mfd/syscon.h> | |
13 | #include <linux/reboot.h> | |
14 | #include <linux/regmap.h> | |
15 | #include <linux/of.h> | |
0e545f57 | 16 | |
0160817d LW |
17 | #define INTEGRATOR_HDR_CTRL_OFFSET 0x0C |
18 | #define INTEGRATOR_HDR_LOCK_OFFSET 0x14 | |
19 | #define INTEGRATOR_CM_CTRL_RESET (1 << 3) | |
20 | ||
0e545f57 | 21 | #define REALVIEW_SYS_LOCK_OFFSET 0x20 |
0e545f57 LW |
22 | #define REALVIEW_SYS_RESETCTL_OFFSET 0x40 |
23 | ||
0160817d LW |
24 | /* Magic unlocking token used on all Versatile boards */ |
25 | #define VERSATILE_LOCK_VAL 0xA05F | |
26 | ||
0e545f57 LW |
27 | /* |
28 | * We detect the different syscon types from the compatible strings. | |
29 | */ | |
30 | enum versatile_reboot { | |
0160817d | 31 | INTEGRATOR_REBOOT_CM, |
0e545f57 LW |
32 | REALVIEW_REBOOT_EB, |
33 | REALVIEW_REBOOT_PB1176, | |
34 | REALVIEW_REBOOT_PB11MP, | |
35 | REALVIEW_REBOOT_PBA8, | |
36 | REALVIEW_REBOOT_PBX, | |
37 | }; | |
38 | ||
39 | /* Pointer to the system controller */ | |
40 | static struct regmap *syscon_regmap; | |
41 | static enum versatile_reboot versatile_reboot_type; | |
42 | ||
43 | static const struct of_device_id versatile_reboot_of_match[] = { | |
0160817d LW |
44 | { |
45 | .compatible = "arm,core-module-integrator", | |
46 | .data = (void *)INTEGRATOR_REBOOT_CM | |
47 | }, | |
0e545f57 LW |
48 | { |
49 | .compatible = "arm,realview-eb-syscon", | |
50 | .data = (void *)REALVIEW_REBOOT_EB, | |
51 | }, | |
52 | { | |
53 | .compatible = "arm,realview-pb1176-syscon", | |
54 | .data = (void *)REALVIEW_REBOOT_PB1176, | |
55 | }, | |
56 | { | |
57 | .compatible = "arm,realview-pb11mp-syscon", | |
58 | .data = (void *)REALVIEW_REBOOT_PB11MP, | |
59 | }, | |
60 | { | |
61 | .compatible = "arm,realview-pba8-syscon", | |
62 | .data = (void *)REALVIEW_REBOOT_PBA8, | |
63 | }, | |
64 | { | |
65 | .compatible = "arm,realview-pbx-syscon", | |
66 | .data = (void *)REALVIEW_REBOOT_PBX, | |
67 | }, | |
0160817d | 68 | {}, |
0e545f57 LW |
69 | }; |
70 | ||
25a5b57d GR |
71 | static int versatile_reboot(struct notifier_block *this, unsigned long mode, |
72 | void *cmd) | |
0e545f57 LW |
73 | { |
74 | /* Unlock the reset register */ | |
0e545f57 LW |
75 | /* Then hit reset on the different machines */ |
76 | switch (versatile_reboot_type) { | |
0160817d LW |
77 | case INTEGRATOR_REBOOT_CM: |
78 | regmap_write(syscon_regmap, INTEGRATOR_HDR_LOCK_OFFSET, | |
79 | VERSATILE_LOCK_VAL); | |
80 | regmap_update_bits(syscon_regmap, | |
81 | INTEGRATOR_HDR_CTRL_OFFSET, | |
82 | INTEGRATOR_CM_CTRL_RESET, | |
83 | INTEGRATOR_CM_CTRL_RESET); | |
84 | break; | |
0e545f57 | 85 | case REALVIEW_REBOOT_EB: |
0160817d LW |
86 | regmap_write(syscon_regmap, REALVIEW_SYS_LOCK_OFFSET, |
87 | VERSATILE_LOCK_VAL); | |
0e545f57 LW |
88 | regmap_write(syscon_regmap, |
89 | REALVIEW_SYS_RESETCTL_OFFSET, 0x0008); | |
90 | break; | |
91 | case REALVIEW_REBOOT_PB1176: | |
0160817d LW |
92 | regmap_write(syscon_regmap, REALVIEW_SYS_LOCK_OFFSET, |
93 | VERSATILE_LOCK_VAL); | |
0e545f57 LW |
94 | regmap_write(syscon_regmap, |
95 | REALVIEW_SYS_RESETCTL_OFFSET, 0x0100); | |
96 | break; | |
97 | case REALVIEW_REBOOT_PB11MP: | |
98 | case REALVIEW_REBOOT_PBA8: | |
0160817d LW |
99 | regmap_write(syscon_regmap, REALVIEW_SYS_LOCK_OFFSET, |
100 | VERSATILE_LOCK_VAL); | |
0e545f57 LW |
101 | regmap_write(syscon_regmap, REALVIEW_SYS_RESETCTL_OFFSET, |
102 | 0x0000); | |
103 | regmap_write(syscon_regmap, REALVIEW_SYS_RESETCTL_OFFSET, | |
104 | 0x0004); | |
105 | break; | |
106 | case REALVIEW_REBOOT_PBX: | |
0160817d LW |
107 | regmap_write(syscon_regmap, REALVIEW_SYS_LOCK_OFFSET, |
108 | VERSATILE_LOCK_VAL); | |
0e545f57 LW |
109 | regmap_write(syscon_regmap, REALVIEW_SYS_RESETCTL_OFFSET, |
110 | 0x00f0); | |
111 | regmap_write(syscon_regmap, REALVIEW_SYS_RESETCTL_OFFSET, | |
112 | 0x00f4); | |
113 | break; | |
114 | } | |
115 | dsb(); | |
25a5b57d GR |
116 | |
117 | return NOTIFY_DONE; | |
0e545f57 LW |
118 | } |
119 | ||
25a5b57d GR |
120 | static struct notifier_block versatile_reboot_nb = { |
121 | .notifier_call = versatile_reboot, | |
122 | .priority = 192, | |
123 | }; | |
124 | ||
0e545f57 LW |
125 | static int __init versatile_reboot_probe(void) |
126 | { | |
127 | const struct of_device_id *reboot_id; | |
128 | struct device_node *np; | |
25a5b57d | 129 | int err; |
0e545f57 LW |
130 | |
131 | np = of_find_matching_node_and_match(NULL, versatile_reboot_of_match, | |
132 | &reboot_id); | |
133 | if (!np) | |
134 | return -ENODEV; | |
135 | versatile_reboot_type = (enum versatile_reboot)reboot_id->data; | |
136 | ||
137 | syscon_regmap = syscon_node_to_regmap(np); | |
138 | if (IS_ERR(syscon_regmap)) | |
139 | return PTR_ERR(syscon_regmap); | |
140 | ||
25a5b57d GR |
141 | err = register_restart_handler(&versatile_reboot_nb); |
142 | if (err) | |
143 | return err; | |
144 | ||
0e545f57 LW |
145 | pr_info("versatile reboot driver registered\n"); |
146 | return 0; | |
147 | } | |
148 | device_initcall(versatile_reboot_probe); |