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