]>
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 | ||
3f652c96 LW |
21 | #define VERSATILE_SYS_LOCK_OFFSET 0x20 |
22 | #define VERSATILE_SYS_RESETCTL_OFFSET 0x40 | |
0e545f57 | 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, |
3f652c96 | 32 | VERSATILE_REBOOT_CM, |
0e545f57 LW |
33 | REALVIEW_REBOOT_EB, |
34 | REALVIEW_REBOOT_PB1176, | |
35 | REALVIEW_REBOOT_PB11MP, | |
36 | REALVIEW_REBOOT_PBA8, | |
37 | REALVIEW_REBOOT_PBX, | |
38 | }; | |
39 | ||
40 | /* Pointer to the system controller */ | |
41 | static struct regmap *syscon_regmap; | |
42 | static enum versatile_reboot versatile_reboot_type; | |
43 | ||
44 | static const struct of_device_id versatile_reboot_of_match[] = { | |
0160817d LW |
45 | { |
46 | .compatible = "arm,core-module-integrator", | |
47 | .data = (void *)INTEGRATOR_REBOOT_CM | |
48 | }, | |
3f652c96 LW |
49 | { |
50 | .compatible = "arm,core-module-versatile", | |
51 | .data = (void *)VERSATILE_REBOOT_CM, | |
52 | }, | |
0e545f57 LW |
53 | { |
54 | .compatible = "arm,realview-eb-syscon", | |
55 | .data = (void *)REALVIEW_REBOOT_EB, | |
56 | }, | |
57 | { | |
58 | .compatible = "arm,realview-pb1176-syscon", | |
59 | .data = (void *)REALVIEW_REBOOT_PB1176, | |
60 | }, | |
61 | { | |
62 | .compatible = "arm,realview-pb11mp-syscon", | |
63 | .data = (void *)REALVIEW_REBOOT_PB11MP, | |
64 | }, | |
65 | { | |
66 | .compatible = "arm,realview-pba8-syscon", | |
67 | .data = (void *)REALVIEW_REBOOT_PBA8, | |
68 | }, | |
69 | { | |
70 | .compatible = "arm,realview-pbx-syscon", | |
71 | .data = (void *)REALVIEW_REBOOT_PBX, | |
72 | }, | |
0160817d | 73 | {}, |
0e545f57 LW |
74 | }; |
75 | ||
25a5b57d GR |
76 | static int versatile_reboot(struct notifier_block *this, unsigned long mode, |
77 | void *cmd) | |
0e545f57 LW |
78 | { |
79 | /* Unlock the reset register */ | |
0e545f57 LW |
80 | /* Then hit reset on the different machines */ |
81 | switch (versatile_reboot_type) { | |
0160817d LW |
82 | case INTEGRATOR_REBOOT_CM: |
83 | regmap_write(syscon_regmap, INTEGRATOR_HDR_LOCK_OFFSET, | |
84 | VERSATILE_LOCK_VAL); | |
85 | regmap_update_bits(syscon_regmap, | |
86 | INTEGRATOR_HDR_CTRL_OFFSET, | |
87 | INTEGRATOR_CM_CTRL_RESET, | |
88 | INTEGRATOR_CM_CTRL_RESET); | |
89 | break; | |
3f652c96 LW |
90 | case VERSATILE_REBOOT_CM: |
91 | regmap_write(syscon_regmap, VERSATILE_SYS_LOCK_OFFSET, | |
92 | VERSATILE_LOCK_VAL); | |
93 | regmap_update_bits(syscon_regmap, | |
94 | VERSATILE_SYS_RESETCTL_OFFSET, | |
95 | 0x0107, | |
96 | 0x0105); | |
97 | regmap_write(syscon_regmap, VERSATILE_SYS_LOCK_OFFSET, | |
98 | 0); | |
99 | break; | |
0e545f57 | 100 | case REALVIEW_REBOOT_EB: |
3f652c96 | 101 | regmap_write(syscon_regmap, VERSATILE_SYS_LOCK_OFFSET, |
0160817d | 102 | VERSATILE_LOCK_VAL); |
0e545f57 | 103 | regmap_write(syscon_regmap, |
3f652c96 | 104 | VERSATILE_SYS_RESETCTL_OFFSET, 0x0008); |
0e545f57 LW |
105 | break; |
106 | case REALVIEW_REBOOT_PB1176: | |
3f652c96 | 107 | regmap_write(syscon_regmap, VERSATILE_SYS_LOCK_OFFSET, |
0160817d | 108 | VERSATILE_LOCK_VAL); |
0e545f57 | 109 | regmap_write(syscon_regmap, |
3f652c96 | 110 | VERSATILE_SYS_RESETCTL_OFFSET, 0x0100); |
0e545f57 LW |
111 | break; |
112 | case REALVIEW_REBOOT_PB11MP: | |
113 | case REALVIEW_REBOOT_PBA8: | |
3f652c96 | 114 | regmap_write(syscon_regmap, VERSATILE_SYS_LOCK_OFFSET, |
0160817d | 115 | VERSATILE_LOCK_VAL); |
3f652c96 | 116 | regmap_write(syscon_regmap, VERSATILE_SYS_RESETCTL_OFFSET, |
0e545f57 | 117 | 0x0000); |
3f652c96 | 118 | regmap_write(syscon_regmap, VERSATILE_SYS_RESETCTL_OFFSET, |
0e545f57 LW |
119 | 0x0004); |
120 | break; | |
121 | case REALVIEW_REBOOT_PBX: | |
3f652c96 | 122 | regmap_write(syscon_regmap, VERSATILE_SYS_LOCK_OFFSET, |
0160817d | 123 | VERSATILE_LOCK_VAL); |
3f652c96 | 124 | regmap_write(syscon_regmap, VERSATILE_SYS_RESETCTL_OFFSET, |
0e545f57 | 125 | 0x00f0); |
3f652c96 | 126 | regmap_write(syscon_regmap, VERSATILE_SYS_RESETCTL_OFFSET, |
0e545f57 LW |
127 | 0x00f4); |
128 | break; | |
129 | } | |
130 | dsb(); | |
25a5b57d GR |
131 | |
132 | return NOTIFY_DONE; | |
0e545f57 LW |
133 | } |
134 | ||
25a5b57d GR |
135 | static struct notifier_block versatile_reboot_nb = { |
136 | .notifier_call = versatile_reboot, | |
137 | .priority = 192, | |
138 | }; | |
139 | ||
0e545f57 LW |
140 | static int __init versatile_reboot_probe(void) |
141 | { | |
142 | const struct of_device_id *reboot_id; | |
143 | struct device_node *np; | |
25a5b57d | 144 | int err; |
0e545f57 LW |
145 | |
146 | np = of_find_matching_node_and_match(NULL, versatile_reboot_of_match, | |
147 | &reboot_id); | |
148 | if (!np) | |
149 | return -ENODEV; | |
150 | versatile_reboot_type = (enum versatile_reboot)reboot_id->data; | |
151 | ||
152 | syscon_regmap = syscon_node_to_regmap(np); | |
153 | if (IS_ERR(syscon_regmap)) | |
154 | return PTR_ERR(syscon_regmap); | |
155 | ||
25a5b57d GR |
156 | err = register_restart_handler(&versatile_reboot_nb); |
157 | if (err) | |
158 | return err; | |
159 | ||
0e545f57 LW |
160 | pr_info("versatile reboot driver registered\n"); |
161 | return 0; | |
162 | } | |
163 | device_initcall(versatile_reboot_probe); |