.. | .. |
---|
19 | 19 | */ |
---|
20 | 20 | |
---|
21 | 21 | #include <linux/clk.h> |
---|
| 22 | +#include <linux/clk/at91_pmc.h> |
---|
22 | 23 | #include <linux/io.h> |
---|
23 | 24 | #include <linux/module.h> |
---|
24 | 25 | #include <linux/of.h> |
---|
.. | .. |
---|
56 | 57 | |
---|
57 | 58 | #define SHDW_WK_PIN(reg, cfg) ((reg) & AT91_SHDW_WKUPIS((cfg)->wkup_pin_input)) |
---|
58 | 59 | #define SHDW_RTCWK(reg, cfg) (((reg) >> ((cfg)->sr_rtcwk_shift)) & 0x1) |
---|
| 60 | +#define SHDW_RTTWK(reg, cfg) (((reg) >> ((cfg)->sr_rttwk_shift)) & 0x1) |
---|
59 | 61 | #define SHDW_RTCWKEN(cfg) (1 << ((cfg)->mr_rtcwk_shift)) |
---|
| 62 | +#define SHDW_RTTWKEN(cfg) (1 << ((cfg)->mr_rttwk_shift)) |
---|
60 | 63 | |
---|
61 | 64 | #define DBC_PERIOD_US(x) DIV_ROUND_UP_ULL((1000000 * (x)), \ |
---|
62 | 65 | SLOW_CLOCK_FREQ) |
---|
63 | 66 | |
---|
64 | | -struct shdwc_config { |
---|
| 67 | +#define SHDW_CFG_NOT_USED (32) |
---|
| 68 | + |
---|
| 69 | +struct shdwc_reg_config { |
---|
65 | 70 | u8 wkup_pin_input; |
---|
66 | 71 | u8 mr_rtcwk_shift; |
---|
| 72 | + u8 mr_rttwk_shift; |
---|
67 | 73 | u8 sr_rtcwk_shift; |
---|
| 74 | + u8 sr_rttwk_shift; |
---|
| 75 | +}; |
---|
| 76 | + |
---|
| 77 | +struct pmc_reg_config { |
---|
| 78 | + u8 mckr; |
---|
| 79 | +}; |
---|
| 80 | + |
---|
| 81 | +struct reg_config { |
---|
| 82 | + struct shdwc_reg_config shdwc; |
---|
| 83 | + struct pmc_reg_config pmc; |
---|
68 | 84 | }; |
---|
69 | 85 | |
---|
70 | 86 | struct shdwc { |
---|
71 | | - const struct shdwc_config *cfg; |
---|
72 | | - void __iomem *at91_shdwc_base; |
---|
| 87 | + const struct reg_config *rcfg; |
---|
| 88 | + struct clk *sclk; |
---|
| 89 | + void __iomem *shdwc_base; |
---|
| 90 | + void __iomem *mpddrc_base; |
---|
| 91 | + void __iomem *pmc_base; |
---|
73 | 92 | }; |
---|
74 | 93 | |
---|
75 | 94 | /* |
---|
.. | .. |
---|
77 | 96 | * since pm_power_off itself is global. |
---|
78 | 97 | */ |
---|
79 | 98 | static struct shdwc *at91_shdwc; |
---|
80 | | -static struct clk *sclk; |
---|
81 | | -static void __iomem *mpddrc_base; |
---|
82 | 99 | |
---|
83 | 100 | static const unsigned long long sdwc_dbc_period[] = { |
---|
84 | 101 | 0, 3, 32, 512, 4096, 32768, |
---|
.. | .. |
---|
87 | 104 | static void __init at91_wakeup_status(struct platform_device *pdev) |
---|
88 | 105 | { |
---|
89 | 106 | struct shdwc *shdw = platform_get_drvdata(pdev); |
---|
| 107 | + const struct reg_config *rcfg = shdw->rcfg; |
---|
90 | 108 | u32 reg; |
---|
91 | 109 | char *reason = "unknown"; |
---|
92 | 110 | |
---|
93 | | - reg = readl(shdw->at91_shdwc_base + AT91_SHDW_SR); |
---|
| 111 | + reg = readl(shdw->shdwc_base + AT91_SHDW_SR); |
---|
94 | 112 | |
---|
95 | 113 | dev_dbg(&pdev->dev, "%s: status = %#x\n", __func__, reg); |
---|
96 | 114 | |
---|
.. | .. |
---|
98 | 116 | if (!reg) |
---|
99 | 117 | return; |
---|
100 | 118 | |
---|
101 | | - if (SHDW_WK_PIN(reg, shdw->cfg)) |
---|
| 119 | + if (SHDW_WK_PIN(reg, &rcfg->shdwc)) |
---|
102 | 120 | reason = "WKUP pin"; |
---|
103 | | - else if (SHDW_RTCWK(reg, shdw->cfg)) |
---|
| 121 | + else if (SHDW_RTCWK(reg, &rcfg->shdwc)) |
---|
104 | 122 | reason = "RTC"; |
---|
| 123 | + else if (SHDW_RTTWK(reg, &rcfg->shdwc)) |
---|
| 124 | + reason = "RTT"; |
---|
105 | 125 | |
---|
106 | 126 | pr_info("AT91: Wake-Up source: %s\n", reason); |
---|
107 | 127 | } |
---|
108 | 128 | |
---|
109 | 129 | static void at91_poweroff(void) |
---|
110 | | -{ |
---|
111 | | - writel(AT91_SHDW_KEY | AT91_SHDW_SHDW, |
---|
112 | | - at91_shdwc->at91_shdwc_base + AT91_SHDW_CR); |
---|
113 | | -} |
---|
114 | | - |
---|
115 | | -static void at91_lpddr_poweroff(void) |
---|
116 | 130 | { |
---|
117 | 131 | asm volatile( |
---|
118 | 132 | /* Align to cache lines */ |
---|
.. | .. |
---|
122 | 136 | " ldr r6, [%2, #" __stringify(AT91_SHDW_CR) "]\n\t" |
---|
123 | 137 | |
---|
124 | 138 | /* Power down SDRAM0 */ |
---|
| 139 | + " tst %0, #0\n\t" |
---|
| 140 | + " beq 1f\n\t" |
---|
125 | 141 | " str %1, [%0, #" __stringify(AT91_DDRSDRC_LPR) "]\n\t" |
---|
| 142 | + |
---|
| 143 | + /* Switch the master clock source to slow clock. */ |
---|
| 144 | + "1: ldr r6, [%4, %5]\n\t" |
---|
| 145 | + " bic r6, r6, #" __stringify(AT91_PMC_CSS) "\n\t" |
---|
| 146 | + " str r6, [%4, %5]\n\t" |
---|
| 147 | + /* Wait for clock switch. */ |
---|
| 148 | + "2: ldr r6, [%4, #" __stringify(AT91_PMC_SR) "]\n\t" |
---|
| 149 | + " tst r6, #" __stringify(AT91_PMC_MCKRDY) "\n\t" |
---|
| 150 | + " beq 2b\n\t" |
---|
| 151 | + |
---|
126 | 152 | /* Shutdown CPU */ |
---|
127 | 153 | " str %3, [%2, #" __stringify(AT91_SHDW_CR) "]\n\t" |
---|
128 | 154 | |
---|
129 | 155 | " b .\n\t" |
---|
130 | 156 | : |
---|
131 | | - : "r" (mpddrc_base), |
---|
| 157 | + : "r" (at91_shdwc->mpddrc_base), |
---|
132 | 158 | "r" cpu_to_le32(AT91_DDRSDRC_LPDDR2_PWOFF), |
---|
133 | | - "r" (at91_shdwc->at91_shdwc_base), |
---|
134 | | - "r" cpu_to_le32(AT91_SHDW_KEY | AT91_SHDW_SHDW) |
---|
| 159 | + "r" (at91_shdwc->shdwc_base), |
---|
| 160 | + "r" cpu_to_le32(AT91_SHDW_KEY | AT91_SHDW_SHDW), |
---|
| 161 | + "r" (at91_shdwc->pmc_base), |
---|
| 162 | + "r" (at91_shdwc->rcfg->pmc.mckr) |
---|
135 | 163 | : "r6"); |
---|
136 | 164 | } |
---|
137 | 165 | |
---|
.. | .. |
---|
198 | 226 | static void at91_shdwc_dt_configure(struct platform_device *pdev) |
---|
199 | 227 | { |
---|
200 | 228 | struct shdwc *shdw = platform_get_drvdata(pdev); |
---|
| 229 | + const struct reg_config *rcfg = shdw->rcfg; |
---|
201 | 230 | struct device_node *np = pdev->dev.of_node; |
---|
202 | 231 | u32 mode = 0, tmp, input; |
---|
203 | 232 | |
---|
.. | .. |
---|
210 | 239 | mode |= AT91_SHDW_WKUPDBC(at91_shdwc_debouncer_value(pdev, tmp)); |
---|
211 | 240 | |
---|
212 | 241 | if (of_property_read_bool(np, "atmel,wakeup-rtc-timer")) |
---|
213 | | - mode |= SHDW_RTCWKEN(shdw->cfg); |
---|
| 242 | + mode |= SHDW_RTCWKEN(&rcfg->shdwc); |
---|
| 243 | + |
---|
| 244 | + if (of_property_read_bool(np, "atmel,wakeup-rtt-timer")) |
---|
| 245 | + mode |= SHDW_RTTWKEN(&rcfg->shdwc); |
---|
214 | 246 | |
---|
215 | 247 | dev_dbg(&pdev->dev, "%s: mode = %#x\n", __func__, mode); |
---|
216 | | - writel(mode, shdw->at91_shdwc_base + AT91_SHDW_MR); |
---|
| 248 | + writel(mode, shdw->shdwc_base + AT91_SHDW_MR); |
---|
217 | 249 | |
---|
218 | 250 | input = at91_shdwc_get_wakeup_input(pdev, np); |
---|
219 | | - writel(input, shdw->at91_shdwc_base + AT91_SHDW_WUIR); |
---|
| 251 | + writel(input, shdw->shdwc_base + AT91_SHDW_WUIR); |
---|
220 | 252 | } |
---|
221 | 253 | |
---|
222 | | -static const struct shdwc_config sama5d2_shdwc_config = { |
---|
223 | | - .wkup_pin_input = 0, |
---|
224 | | - .mr_rtcwk_shift = 17, |
---|
225 | | - .sr_rtcwk_shift = 5, |
---|
| 254 | +static const struct reg_config sama5d2_reg_config = { |
---|
| 255 | + .shdwc = { |
---|
| 256 | + .wkup_pin_input = 0, |
---|
| 257 | + .mr_rtcwk_shift = 17, |
---|
| 258 | + .mr_rttwk_shift = SHDW_CFG_NOT_USED, |
---|
| 259 | + .sr_rtcwk_shift = 5, |
---|
| 260 | + .sr_rttwk_shift = SHDW_CFG_NOT_USED, |
---|
| 261 | + }, |
---|
| 262 | + .pmc = { |
---|
| 263 | + .mckr = 0x30, |
---|
| 264 | + }, |
---|
| 265 | +}; |
---|
| 266 | + |
---|
| 267 | +static const struct reg_config sam9x60_reg_config = { |
---|
| 268 | + .shdwc = { |
---|
| 269 | + .wkup_pin_input = 0, |
---|
| 270 | + .mr_rtcwk_shift = 17, |
---|
| 271 | + .mr_rttwk_shift = 16, |
---|
| 272 | + .sr_rtcwk_shift = 5, |
---|
| 273 | + .sr_rttwk_shift = 4, |
---|
| 274 | + }, |
---|
| 275 | + .pmc = { |
---|
| 276 | + .mckr = 0x28, |
---|
| 277 | + }, |
---|
226 | 278 | }; |
---|
227 | 279 | |
---|
228 | 280 | static const struct of_device_id at91_shdwc_of_match[] = { |
---|
229 | 281 | { |
---|
230 | 282 | .compatible = "atmel,sama5d2-shdwc", |
---|
231 | | - .data = &sama5d2_shdwc_config, |
---|
| 283 | + .data = &sama5d2_reg_config, |
---|
| 284 | + }, |
---|
| 285 | + { |
---|
| 286 | + .compatible = "microchip,sam9x60-shdwc", |
---|
| 287 | + .data = &sam9x60_reg_config, |
---|
232 | 288 | }, { |
---|
233 | 289 | /*sentinel*/ |
---|
234 | 290 | } |
---|
235 | 291 | }; |
---|
236 | 292 | MODULE_DEVICE_TABLE(of, at91_shdwc_of_match); |
---|
| 293 | + |
---|
| 294 | +static const struct of_device_id at91_pmc_ids[] = { |
---|
| 295 | + { .compatible = "atmel,sama5d2-pmc" }, |
---|
| 296 | + { .compatible = "microchip,sam9x60-pmc" }, |
---|
| 297 | + { /* Sentinel. */ } |
---|
| 298 | +}; |
---|
237 | 299 | |
---|
238 | 300 | static int __init at91_shdwc_probe(struct platform_device *pdev) |
---|
239 | 301 | { |
---|
.. | .. |
---|
256 | 318 | platform_set_drvdata(pdev, at91_shdwc); |
---|
257 | 319 | |
---|
258 | 320 | res = platform_get_resource(pdev, IORESOURCE_MEM, 0); |
---|
259 | | - at91_shdwc->at91_shdwc_base = devm_ioremap_resource(&pdev->dev, res); |
---|
260 | | - if (IS_ERR(at91_shdwc->at91_shdwc_base)) { |
---|
| 321 | + at91_shdwc->shdwc_base = devm_ioremap_resource(&pdev->dev, res); |
---|
| 322 | + if (IS_ERR(at91_shdwc->shdwc_base)) { |
---|
261 | 323 | dev_err(&pdev->dev, "Could not map reset controller address\n"); |
---|
262 | | - return PTR_ERR(at91_shdwc->at91_shdwc_base); |
---|
| 324 | + return PTR_ERR(at91_shdwc->shdwc_base); |
---|
263 | 325 | } |
---|
264 | 326 | |
---|
265 | 327 | match = of_match_node(at91_shdwc_of_match, pdev->dev.of_node); |
---|
266 | | - at91_shdwc->cfg = match->data; |
---|
| 328 | + at91_shdwc->rcfg = match->data; |
---|
267 | 329 | |
---|
268 | | - sclk = devm_clk_get(&pdev->dev, NULL); |
---|
269 | | - if (IS_ERR(sclk)) |
---|
270 | | - return PTR_ERR(sclk); |
---|
| 330 | + at91_shdwc->sclk = devm_clk_get(&pdev->dev, NULL); |
---|
| 331 | + if (IS_ERR(at91_shdwc->sclk)) |
---|
| 332 | + return PTR_ERR(at91_shdwc->sclk); |
---|
271 | 333 | |
---|
272 | | - ret = clk_prepare_enable(sclk); |
---|
| 334 | + ret = clk_prepare_enable(at91_shdwc->sclk); |
---|
273 | 335 | if (ret) { |
---|
274 | 336 | dev_err(&pdev->dev, "Could not enable slow clock\n"); |
---|
275 | 337 | return ret; |
---|
.. | .. |
---|
279 | 341 | |
---|
280 | 342 | at91_shdwc_dt_configure(pdev); |
---|
281 | 343 | |
---|
282 | | - pm_power_off = at91_poweroff; |
---|
| 344 | + np = of_find_matching_node(NULL, at91_pmc_ids); |
---|
| 345 | + if (!np) { |
---|
| 346 | + ret = -ENODEV; |
---|
| 347 | + goto clk_disable; |
---|
| 348 | + } |
---|
283 | 349 | |
---|
284 | | - np = of_find_compatible_node(NULL, NULL, "atmel,sama5d3-ddramc"); |
---|
285 | | - if (!np) |
---|
286 | | - return 0; |
---|
287 | | - |
---|
288 | | - mpddrc_base = of_iomap(np, 0); |
---|
| 350 | + at91_shdwc->pmc_base = of_iomap(np, 0); |
---|
289 | 351 | of_node_put(np); |
---|
290 | 352 | |
---|
291 | | - if (!mpddrc_base) |
---|
292 | | - return 0; |
---|
| 353 | + if (!at91_shdwc->pmc_base) { |
---|
| 354 | + ret = -ENOMEM; |
---|
| 355 | + goto clk_disable; |
---|
| 356 | + } |
---|
293 | 357 | |
---|
294 | | - ddr_type = readl(mpddrc_base + AT91_DDRSDRC_MDR) & AT91_DDRSDRC_MD; |
---|
295 | | - if ((ddr_type == AT91_DDRSDRC_MD_LPDDR2) || |
---|
296 | | - (ddr_type == AT91_DDRSDRC_MD_LPDDR3)) |
---|
297 | | - pm_power_off = at91_lpddr_poweroff; |
---|
298 | | - else |
---|
299 | | - iounmap(mpddrc_base); |
---|
| 358 | + np = of_find_compatible_node(NULL, NULL, "atmel,sama5d3-ddramc"); |
---|
| 359 | + if (!np) { |
---|
| 360 | + ret = -ENODEV; |
---|
| 361 | + goto unmap; |
---|
| 362 | + } |
---|
| 363 | + |
---|
| 364 | + at91_shdwc->mpddrc_base = of_iomap(np, 0); |
---|
| 365 | + of_node_put(np); |
---|
| 366 | + |
---|
| 367 | + if (!at91_shdwc->mpddrc_base) { |
---|
| 368 | + ret = -ENOMEM; |
---|
| 369 | + goto unmap; |
---|
| 370 | + } |
---|
| 371 | + |
---|
| 372 | + pm_power_off = at91_poweroff; |
---|
| 373 | + |
---|
| 374 | + ddr_type = readl(at91_shdwc->mpddrc_base + AT91_DDRSDRC_MDR) & |
---|
| 375 | + AT91_DDRSDRC_MD; |
---|
| 376 | + if (ddr_type != AT91_DDRSDRC_MD_LPDDR2 && |
---|
| 377 | + ddr_type != AT91_DDRSDRC_MD_LPDDR3) { |
---|
| 378 | + iounmap(at91_shdwc->mpddrc_base); |
---|
| 379 | + at91_shdwc->mpddrc_base = NULL; |
---|
| 380 | + } |
---|
300 | 381 | |
---|
301 | 382 | return 0; |
---|
| 383 | + |
---|
| 384 | +unmap: |
---|
| 385 | + iounmap(at91_shdwc->pmc_base); |
---|
| 386 | +clk_disable: |
---|
| 387 | + clk_disable_unprepare(at91_shdwc->sclk); |
---|
| 388 | + |
---|
| 389 | + return ret; |
---|
302 | 390 | } |
---|
303 | 391 | |
---|
304 | 392 | static int __exit at91_shdwc_remove(struct platform_device *pdev) |
---|
305 | 393 | { |
---|
306 | 394 | struct shdwc *shdw = platform_get_drvdata(pdev); |
---|
307 | 395 | |
---|
308 | | - if (pm_power_off == at91_poweroff || |
---|
309 | | - pm_power_off == at91_lpddr_poweroff) |
---|
| 396 | + if (pm_power_off == at91_poweroff) |
---|
310 | 397 | pm_power_off = NULL; |
---|
311 | 398 | |
---|
312 | 399 | /* Reset values to disable wake-up features */ |
---|
313 | | - writel(0, shdw->at91_shdwc_base + AT91_SHDW_MR); |
---|
314 | | - writel(0, shdw->at91_shdwc_base + AT91_SHDW_WUIR); |
---|
| 400 | + writel(0, shdw->shdwc_base + AT91_SHDW_MR); |
---|
| 401 | + writel(0, shdw->shdwc_base + AT91_SHDW_WUIR); |
---|
315 | 402 | |
---|
316 | | - clk_disable_unprepare(sclk); |
---|
| 403 | + if (shdw->mpddrc_base) |
---|
| 404 | + iounmap(shdw->mpddrc_base); |
---|
| 405 | + iounmap(shdw->pmc_base); |
---|
| 406 | + |
---|
| 407 | + clk_disable_unprepare(shdw->sclk); |
---|
317 | 408 | |
---|
318 | 409 | return 0; |
---|
319 | 410 | } |
---|