diff options
Diffstat (limited to 'drivers/power/reset/at91-reset.c')
-rw-r--r-- | drivers/power/reset/at91-reset.c | 252 |
1 files changed, 252 insertions, 0 deletions
diff --git a/drivers/power/reset/at91-reset.c b/drivers/power/reset/at91-reset.c new file mode 100644 index 000000000000..3611806c9cfd --- /dev/null +++ b/drivers/power/reset/at91-reset.c | |||
@@ -0,0 +1,252 @@ | |||
1 | /* | ||
2 | * Atmel AT91 SAM9 SoCs reset code | ||
3 | * | ||
4 | * Copyright (C) 2007 Atmel Corporation. | ||
5 | * Copyright (C) BitBox Ltd 2010 | ||
6 | * Copyright (C) 2011 Jean-Christophe PLAGNIOL-VILLARD <plagnioj@jcosoft.com> | ||
7 | * Copyright (C) 2014 Free Electrons | ||
8 | * | ||
9 | * This file is licensed under the terms of the GNU General Public | ||
10 | * License version 2. This program is licensed "as is" without any | ||
11 | * warranty of any kind, whether express or implied. | ||
12 | */ | ||
13 | |||
14 | #include <linux/io.h> | ||
15 | #include <linux/module.h> | ||
16 | #include <linux/of_address.h> | ||
17 | #include <linux/platform_device.h> | ||
18 | #include <linux/reboot.h> | ||
19 | |||
20 | #include <asm/system_misc.h> | ||
21 | |||
22 | #include <mach/at91sam9_ddrsdr.h> | ||
23 | #include <mach/at91sam9_sdramc.h> | ||
24 | |||
25 | #define AT91_RSTC_CR 0x00 /* Reset Controller Control Register */ | ||
26 | #define AT91_RSTC_PROCRST BIT(0) /* Processor Reset */ | ||
27 | #define AT91_RSTC_PERRST BIT(2) /* Peripheral Reset */ | ||
28 | #define AT91_RSTC_EXTRST BIT(3) /* External Reset */ | ||
29 | #define AT91_RSTC_KEY (0xa5 << 24) /* KEY Password */ | ||
30 | |||
31 | #define AT91_RSTC_SR 0x04 /* Reset Controller Status Register */ | ||
32 | #define AT91_RSTC_URSTS BIT(0) /* User Reset Status */ | ||
33 | #define AT91_RSTC_RSTTYP GENMASK(10, 8) /* Reset Type */ | ||
34 | #define AT91_RSTC_NRSTL BIT(16) /* NRST Pin Level */ | ||
35 | #define AT91_RSTC_SRCMP BIT(17) /* Software Reset Command in Progress */ | ||
36 | |||
37 | #define AT91_RSTC_MR 0x08 /* Reset Controller Mode Register */ | ||
38 | #define AT91_RSTC_URSTEN BIT(0) /* User Reset Enable */ | ||
39 | #define AT91_RSTC_URSTIEN BIT(4) /* User Reset Interrupt Enable */ | ||
40 | #define AT91_RSTC_ERSTL GENMASK(11, 8) /* External Reset Length */ | ||
41 | |||
42 | enum reset_type { | ||
43 | RESET_TYPE_GENERAL = 0, | ||
44 | RESET_TYPE_WAKEUP = 1, | ||
45 | RESET_TYPE_WATCHDOG = 2, | ||
46 | RESET_TYPE_SOFTWARE = 3, | ||
47 | RESET_TYPE_USER = 4, | ||
48 | }; | ||
49 | |||
50 | static void __iomem *at91_ramc_base[2], *at91_rstc_base; | ||
51 | |||
52 | /* | ||
53 | * unless the SDRAM is cleanly shutdown before we hit the | ||
54 | * reset register it can be left driving the data bus and | ||
55 | * killing the chance of a subsequent boot from NAND | ||
56 | */ | ||
57 | static void at91sam9260_restart(enum reboot_mode mode, const char *cmd) | ||
58 | { | ||
59 | asm volatile( | ||
60 | /* Align to cache lines */ | ||
61 | ".balign 32\n\t" | ||
62 | |||
63 | /* Disable SDRAM accesses */ | ||
64 | "str %2, [%0, #" __stringify(AT91_SDRAMC_TR) "]\n\t" | ||
65 | |||
66 | /* Power down SDRAM */ | ||
67 | "str %3, [%0, #" __stringify(AT91_SDRAMC_LPR) "]\n\t" | ||
68 | |||
69 | /* Reset CPU */ | ||
70 | "str %4, [%1, #" __stringify(AT91_RSTC_CR) "]\n\t" | ||
71 | |||
72 | "b .\n\t" | ||
73 | : | ||
74 | : "r" (at91_ramc_base[0]), | ||
75 | "r" (at91_rstc_base), | ||
76 | "r" (1), | ||
77 | "r" (AT91_SDRAMC_LPCB_POWER_DOWN), | ||
78 | "r" (AT91_RSTC_KEY | AT91_RSTC_PERRST | AT91_RSTC_PROCRST)); | ||
79 | } | ||
80 | |||
81 | static void at91sam9g45_restart(enum reboot_mode mode, const char *cmd) | ||
82 | { | ||
83 | asm volatile( | ||
84 | /* | ||
85 | * Test wether we have a second RAM controller to care | ||
86 | * about. | ||
87 | * | ||
88 | * First, test that we can dereference the virtual address. | ||
89 | */ | ||
90 | "cmp %1, #0\n\t" | ||
91 | "beq 1f\n\t" | ||
92 | |||
93 | /* Then, test that the RAM controller is enabled */ | ||
94 | "ldr r0, [%1]\n\t" | ||
95 | "cmp r0, #0\n\t" | ||
96 | |||
97 | /* Align to cache lines */ | ||
98 | ".balign 32\n\t" | ||
99 | |||
100 | /* Disable SDRAM0 accesses */ | ||
101 | "1: str %3, [%0, #" __stringify(AT91_DDRSDRC_RTR) "]\n\t" | ||
102 | /* Power down SDRAM0 */ | ||
103 | " str %4, [%0, #" __stringify(AT91_DDRSDRC_RTR) "]\n\t" | ||
104 | /* Disable SDRAM1 accesses */ | ||
105 | " strne %3, [%1, #" __stringify(AT91_DDRSDRC_RTR) "]\n\t" | ||
106 | /* Power down SDRAM1 */ | ||
107 | " strne %4, [%1, #" __stringify(AT91_DDRSDRC_RTR) "]\n\t" | ||
108 | /* Reset CPU */ | ||
109 | " str %5, [%2, #" __stringify(AT91_RSTC_CR) "]\n\t" | ||
110 | |||
111 | " b .\n\t" | ||
112 | : | ||
113 | : "r" (at91_ramc_base[0]), | ||
114 | "r" (at91_ramc_base[1]), | ||
115 | "r" (at91_rstc_base), | ||
116 | "r" (1), | ||
117 | "r" (AT91_DDRSDRC_LPCB_POWER_DOWN), | ||
118 | "r" (AT91_RSTC_KEY | AT91_RSTC_PERRST | AT91_RSTC_PROCRST) | ||
119 | : "r0"); | ||
120 | } | ||
121 | |||
122 | static void __init at91_reset_status(struct platform_device *pdev) | ||
123 | { | ||
124 | u32 reg = readl(at91_rstc_base + AT91_RSTC_SR); | ||
125 | char *reason; | ||
126 | |||
127 | switch ((reg & AT91_RSTC_RSTTYP) >> 8) { | ||
128 | case RESET_TYPE_GENERAL: | ||
129 | reason = "general reset"; | ||
130 | break; | ||
131 | case RESET_TYPE_WAKEUP: | ||
132 | reason = "wakeup"; | ||
133 | break; | ||
134 | case RESET_TYPE_WATCHDOG: | ||
135 | reason = "watchdog reset"; | ||
136 | break; | ||
137 | case RESET_TYPE_SOFTWARE: | ||
138 | reason = "software reset"; | ||
139 | break; | ||
140 | case RESET_TYPE_USER: | ||
141 | reason = "user reset"; | ||
142 | break; | ||
143 | default: | ||
144 | reason = "unknown reset"; | ||
145 | break; | ||
146 | } | ||
147 | |||
148 | pr_info("AT91: Starting after %s\n", reason); | ||
149 | } | ||
150 | |||
151 | static struct of_device_id at91_ramc_of_match[] = { | ||
152 | { .compatible = "atmel,at91sam9260-sdramc", }, | ||
153 | { .compatible = "atmel,at91sam9g45-ddramc", }, | ||
154 | { .compatible = "atmel,sama5d3-ddramc", }, | ||
155 | { /* sentinel */ } | ||
156 | }; | ||
157 | |||
158 | static struct of_device_id at91_reset_of_match[] = { | ||
159 | { .compatible = "atmel,at91sam9260-rstc", .data = at91sam9260_restart }, | ||
160 | { .compatible = "atmel,at91sam9g45-rstc", .data = at91sam9g45_restart }, | ||
161 | { /* sentinel */ } | ||
162 | }; | ||
163 | |||
164 | static int at91_reset_of_probe(struct platform_device *pdev) | ||
165 | { | ||
166 | const struct of_device_id *match; | ||
167 | struct device_node *np; | ||
168 | int idx = 0; | ||
169 | |||
170 | at91_rstc_base = of_iomap(pdev->dev.of_node, 0); | ||
171 | if (!at91_rstc_base) { | ||
172 | dev_err(&pdev->dev, "Could not map reset controller address\n"); | ||
173 | return -ENODEV; | ||
174 | } | ||
175 | |||
176 | for_each_matching_node(np, at91_ramc_of_match) { | ||
177 | at91_ramc_base[idx] = of_iomap(np, 0); | ||
178 | if (!at91_ramc_base[idx]) { | ||
179 | dev_err(&pdev->dev, "Could not map ram controller address\n"); | ||
180 | return -ENODEV; | ||
181 | } | ||
182 | idx++; | ||
183 | } | ||
184 | |||
185 | match = of_match_node(at91_reset_of_match, pdev->dev.of_node); | ||
186 | arm_pm_restart = match->data; | ||
187 | |||
188 | return 0; | ||
189 | } | ||
190 | |||
191 | static int at91_reset_platform_probe(struct platform_device *pdev) | ||
192 | { | ||
193 | const struct platform_device_id *match; | ||
194 | struct resource *res; | ||
195 | int idx = 0; | ||
196 | |||
197 | res = platform_get_resource(pdev, IORESOURCE_MEM, 0); | ||
198 | at91_rstc_base = devm_ioremap_resource(&pdev->dev, res); | ||
199 | if (IS_ERR(at91_rstc_base)) { | ||
200 | dev_err(&pdev->dev, "Could not map reset controller address\n"); | ||
201 | return PTR_ERR(at91_rstc_base); | ||
202 | } | ||
203 | |||
204 | for (idx = 0; idx < 2; idx++) { | ||
205 | res = platform_get_resource(pdev, IORESOURCE_MEM, idx + 1 ); | ||
206 | at91_ramc_base[idx] = devm_ioremap(&pdev->dev, res->start, | ||
207 | resource_size(res)); | ||
208 | if (IS_ERR(at91_ramc_base[idx])) { | ||
209 | dev_err(&pdev->dev, "Could not map ram controller address\n"); | ||
210 | return PTR_ERR(at91_ramc_base[idx]); | ||
211 | } | ||
212 | } | ||
213 | |||
214 | match = platform_get_device_id(pdev); | ||
215 | arm_pm_restart = (void (*)(enum reboot_mode, const char*)) | ||
216 | match->driver_data; | ||
217 | |||
218 | return 0; | ||
219 | } | ||
220 | |||
221 | static int at91_reset_probe(struct platform_device *pdev) | ||
222 | { | ||
223 | int ret; | ||
224 | |||
225 | if (pdev->dev.of_node) | ||
226 | ret = at91_reset_of_probe(pdev); | ||
227 | else | ||
228 | ret = at91_reset_platform_probe(pdev); | ||
229 | |||
230 | if (ret) | ||
231 | return ret; | ||
232 | |||
233 | at91_reset_status(pdev); | ||
234 | |||
235 | return 0; | ||
236 | } | ||
237 | |||
238 | static struct platform_device_id at91_reset_plat_match[] = { | ||
239 | { "at91-sam9260-reset", (unsigned long)at91sam9260_restart }, | ||
240 | { "at91-sam9g45-reset", (unsigned long)at91sam9g45_restart }, | ||
241 | { /* sentinel */ } | ||
242 | }; | ||
243 | |||
244 | static struct platform_driver at91_reset_driver = { | ||
245 | .probe = at91_reset_probe, | ||
246 | .driver = { | ||
247 | .name = "at91-reset", | ||
248 | .of_match_table = at91_reset_of_match, | ||
249 | }, | ||
250 | .id_table = at91_reset_plat_match, | ||
251 | }; | ||
252 | module_platform_driver(at91_reset_driver); | ||