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