1*4882a593Smuzhiyun /*
2*4882a593Smuzhiyun * Atmel SAMA5D2-Compatible Shutdown Controller (SHDWC) driver.
3*4882a593Smuzhiyun * Found on some SoCs as the sama5d2 (obviously).
4*4882a593Smuzhiyun *
5*4882a593Smuzhiyun * Copyright (C) 2015 Atmel Corporation,
6*4882a593Smuzhiyun * Nicolas Ferre <nicolas.ferre@atmel.com>
7*4882a593Smuzhiyun *
8*4882a593Smuzhiyun * Evolved from driver at91-poweroff.c.
9*4882a593Smuzhiyun *
10*4882a593Smuzhiyun * This file is licensed under the terms of the GNU General Public
11*4882a593Smuzhiyun * License version 2. This program is licensed "as is" without any
12*4882a593Smuzhiyun * warranty of any kind, whether express or implied.
13*4882a593Smuzhiyun *
14*4882a593Smuzhiyun * TODO:
15*4882a593Smuzhiyun * - addition to status of other wake-up inputs [1 - 15]
16*4882a593Smuzhiyun * - Analog Comparator wake-up alarm
17*4882a593Smuzhiyun * - Serial RX wake-up alarm
18*4882a593Smuzhiyun * - low power debouncer
19*4882a593Smuzhiyun */
20*4882a593Smuzhiyun
21*4882a593Smuzhiyun #include <linux/clk.h>
22*4882a593Smuzhiyun #include <linux/clk/at91_pmc.h>
23*4882a593Smuzhiyun #include <linux/io.h>
24*4882a593Smuzhiyun #include <linux/module.h>
25*4882a593Smuzhiyun #include <linux/of.h>
26*4882a593Smuzhiyun #include <linux/of_address.h>
27*4882a593Smuzhiyun #include <linux/platform_device.h>
28*4882a593Smuzhiyun #include <linux/printk.h>
29*4882a593Smuzhiyun
30*4882a593Smuzhiyun #include <soc/at91/at91sam9_ddrsdr.h>
31*4882a593Smuzhiyun
32*4882a593Smuzhiyun #define SLOW_CLOCK_FREQ 32768
33*4882a593Smuzhiyun
34*4882a593Smuzhiyun #define AT91_SHDW_CR 0x00 /* Shut Down Control Register */
35*4882a593Smuzhiyun #define AT91_SHDW_SHDW BIT(0) /* Shut Down command */
36*4882a593Smuzhiyun #define AT91_SHDW_KEY (0xa5UL << 24) /* KEY Password */
37*4882a593Smuzhiyun
38*4882a593Smuzhiyun #define AT91_SHDW_MR 0x04 /* Shut Down Mode Register */
39*4882a593Smuzhiyun #define AT91_SHDW_WKUPDBC_SHIFT 24
40*4882a593Smuzhiyun #define AT91_SHDW_WKUPDBC_MASK GENMASK(26, 24)
41*4882a593Smuzhiyun #define AT91_SHDW_WKUPDBC(x) (((x) << AT91_SHDW_WKUPDBC_SHIFT) \
42*4882a593Smuzhiyun & AT91_SHDW_WKUPDBC_MASK)
43*4882a593Smuzhiyun
44*4882a593Smuzhiyun #define AT91_SHDW_SR 0x08 /* Shut Down Status Register */
45*4882a593Smuzhiyun #define AT91_SHDW_WKUPIS_SHIFT 16
46*4882a593Smuzhiyun #define AT91_SHDW_WKUPIS_MASK GENMASK(31, 16)
47*4882a593Smuzhiyun #define AT91_SHDW_WKUPIS(x) ((1 << (x)) << AT91_SHDW_WKUPIS_SHIFT \
48*4882a593Smuzhiyun & AT91_SHDW_WKUPIS_MASK)
49*4882a593Smuzhiyun
50*4882a593Smuzhiyun #define AT91_SHDW_WUIR 0x0c /* Shutdown Wake-up Inputs Register */
51*4882a593Smuzhiyun #define AT91_SHDW_WKUPEN_MASK GENMASK(15, 0)
52*4882a593Smuzhiyun #define AT91_SHDW_WKUPEN(x) ((1 << (x)) & AT91_SHDW_WKUPEN_MASK)
53*4882a593Smuzhiyun #define AT91_SHDW_WKUPT_SHIFT 16
54*4882a593Smuzhiyun #define AT91_SHDW_WKUPT_MASK GENMASK(31, 16)
55*4882a593Smuzhiyun #define AT91_SHDW_WKUPT(x) ((1 << (x)) << AT91_SHDW_WKUPT_SHIFT \
56*4882a593Smuzhiyun & AT91_SHDW_WKUPT_MASK)
57*4882a593Smuzhiyun
58*4882a593Smuzhiyun #define SHDW_WK_PIN(reg, cfg) ((reg) & AT91_SHDW_WKUPIS((cfg)->wkup_pin_input))
59*4882a593Smuzhiyun #define SHDW_RTCWK(reg, cfg) (((reg) >> ((cfg)->sr_rtcwk_shift)) & 0x1)
60*4882a593Smuzhiyun #define SHDW_RTTWK(reg, cfg) (((reg) >> ((cfg)->sr_rttwk_shift)) & 0x1)
61*4882a593Smuzhiyun #define SHDW_RTCWKEN(cfg) (1 << ((cfg)->mr_rtcwk_shift))
62*4882a593Smuzhiyun #define SHDW_RTTWKEN(cfg) (1 << ((cfg)->mr_rttwk_shift))
63*4882a593Smuzhiyun
64*4882a593Smuzhiyun #define DBC_PERIOD_US(x) DIV_ROUND_UP_ULL((1000000 * (x)), \
65*4882a593Smuzhiyun SLOW_CLOCK_FREQ)
66*4882a593Smuzhiyun
67*4882a593Smuzhiyun #define SHDW_CFG_NOT_USED (32)
68*4882a593Smuzhiyun
69*4882a593Smuzhiyun struct shdwc_reg_config {
70*4882a593Smuzhiyun u8 wkup_pin_input;
71*4882a593Smuzhiyun u8 mr_rtcwk_shift;
72*4882a593Smuzhiyun u8 mr_rttwk_shift;
73*4882a593Smuzhiyun u8 sr_rtcwk_shift;
74*4882a593Smuzhiyun u8 sr_rttwk_shift;
75*4882a593Smuzhiyun };
76*4882a593Smuzhiyun
77*4882a593Smuzhiyun struct pmc_reg_config {
78*4882a593Smuzhiyun u8 mckr;
79*4882a593Smuzhiyun };
80*4882a593Smuzhiyun
81*4882a593Smuzhiyun struct reg_config {
82*4882a593Smuzhiyun struct shdwc_reg_config shdwc;
83*4882a593Smuzhiyun struct pmc_reg_config pmc;
84*4882a593Smuzhiyun };
85*4882a593Smuzhiyun
86*4882a593Smuzhiyun struct shdwc {
87*4882a593Smuzhiyun const struct reg_config *rcfg;
88*4882a593Smuzhiyun struct clk *sclk;
89*4882a593Smuzhiyun void __iomem *shdwc_base;
90*4882a593Smuzhiyun void __iomem *mpddrc_base;
91*4882a593Smuzhiyun void __iomem *pmc_base;
92*4882a593Smuzhiyun };
93*4882a593Smuzhiyun
94*4882a593Smuzhiyun /*
95*4882a593Smuzhiyun * Hold configuration here, cannot be more than one instance of the driver
96*4882a593Smuzhiyun * since pm_power_off itself is global.
97*4882a593Smuzhiyun */
98*4882a593Smuzhiyun static struct shdwc *at91_shdwc;
99*4882a593Smuzhiyun
100*4882a593Smuzhiyun static const unsigned long long sdwc_dbc_period[] = {
101*4882a593Smuzhiyun 0, 3, 32, 512, 4096, 32768,
102*4882a593Smuzhiyun };
103*4882a593Smuzhiyun
at91_wakeup_status(struct platform_device * pdev)104*4882a593Smuzhiyun static void __init at91_wakeup_status(struct platform_device *pdev)
105*4882a593Smuzhiyun {
106*4882a593Smuzhiyun struct shdwc *shdw = platform_get_drvdata(pdev);
107*4882a593Smuzhiyun const struct reg_config *rcfg = shdw->rcfg;
108*4882a593Smuzhiyun u32 reg;
109*4882a593Smuzhiyun char *reason = "unknown";
110*4882a593Smuzhiyun
111*4882a593Smuzhiyun reg = readl(shdw->shdwc_base + AT91_SHDW_SR);
112*4882a593Smuzhiyun
113*4882a593Smuzhiyun dev_dbg(&pdev->dev, "%s: status = %#x\n", __func__, reg);
114*4882a593Smuzhiyun
115*4882a593Smuzhiyun /* Simple power-on, just bail out */
116*4882a593Smuzhiyun if (!reg)
117*4882a593Smuzhiyun return;
118*4882a593Smuzhiyun
119*4882a593Smuzhiyun if (SHDW_WK_PIN(reg, &rcfg->shdwc))
120*4882a593Smuzhiyun reason = "WKUP pin";
121*4882a593Smuzhiyun else if (SHDW_RTCWK(reg, &rcfg->shdwc))
122*4882a593Smuzhiyun reason = "RTC";
123*4882a593Smuzhiyun else if (SHDW_RTTWK(reg, &rcfg->shdwc))
124*4882a593Smuzhiyun reason = "RTT";
125*4882a593Smuzhiyun
126*4882a593Smuzhiyun pr_info("AT91: Wake-Up source: %s\n", reason);
127*4882a593Smuzhiyun }
128*4882a593Smuzhiyun
at91_poweroff(void)129*4882a593Smuzhiyun static void at91_poweroff(void)
130*4882a593Smuzhiyun {
131*4882a593Smuzhiyun asm volatile(
132*4882a593Smuzhiyun /* Align to cache lines */
133*4882a593Smuzhiyun ".balign 32\n\t"
134*4882a593Smuzhiyun
135*4882a593Smuzhiyun /* Ensure AT91_SHDW_CR is in the TLB by reading it */
136*4882a593Smuzhiyun " ldr r6, [%2, #" __stringify(AT91_SHDW_CR) "]\n\t"
137*4882a593Smuzhiyun
138*4882a593Smuzhiyun /* Power down SDRAM0 */
139*4882a593Smuzhiyun " tst %0, #0\n\t"
140*4882a593Smuzhiyun " beq 1f\n\t"
141*4882a593Smuzhiyun " str %1, [%0, #" __stringify(AT91_DDRSDRC_LPR) "]\n\t"
142*4882a593Smuzhiyun
143*4882a593Smuzhiyun /* Switch the master clock source to slow clock. */
144*4882a593Smuzhiyun "1: ldr r6, [%4, %5]\n\t"
145*4882a593Smuzhiyun " bic r6, r6, #" __stringify(AT91_PMC_CSS) "\n\t"
146*4882a593Smuzhiyun " str r6, [%4, %5]\n\t"
147*4882a593Smuzhiyun /* Wait for clock switch. */
148*4882a593Smuzhiyun "2: ldr r6, [%4, #" __stringify(AT91_PMC_SR) "]\n\t"
149*4882a593Smuzhiyun " tst r6, #" __stringify(AT91_PMC_MCKRDY) "\n\t"
150*4882a593Smuzhiyun " beq 2b\n\t"
151*4882a593Smuzhiyun
152*4882a593Smuzhiyun /* Shutdown CPU */
153*4882a593Smuzhiyun " str %3, [%2, #" __stringify(AT91_SHDW_CR) "]\n\t"
154*4882a593Smuzhiyun
155*4882a593Smuzhiyun " b .\n\t"
156*4882a593Smuzhiyun :
157*4882a593Smuzhiyun : "r" (at91_shdwc->mpddrc_base),
158*4882a593Smuzhiyun "r" cpu_to_le32(AT91_DDRSDRC_LPDDR2_PWOFF),
159*4882a593Smuzhiyun "r" (at91_shdwc->shdwc_base),
160*4882a593Smuzhiyun "r" cpu_to_le32(AT91_SHDW_KEY | AT91_SHDW_SHDW),
161*4882a593Smuzhiyun "r" (at91_shdwc->pmc_base),
162*4882a593Smuzhiyun "r" (at91_shdwc->rcfg->pmc.mckr)
163*4882a593Smuzhiyun : "r6");
164*4882a593Smuzhiyun }
165*4882a593Smuzhiyun
at91_shdwc_debouncer_value(struct platform_device * pdev,u32 in_period_us)166*4882a593Smuzhiyun static u32 at91_shdwc_debouncer_value(struct platform_device *pdev,
167*4882a593Smuzhiyun u32 in_period_us)
168*4882a593Smuzhiyun {
169*4882a593Smuzhiyun int i;
170*4882a593Smuzhiyun int max_idx = ARRAY_SIZE(sdwc_dbc_period) - 1;
171*4882a593Smuzhiyun unsigned long long period_us;
172*4882a593Smuzhiyun unsigned long long max_period_us = DBC_PERIOD_US(sdwc_dbc_period[max_idx]);
173*4882a593Smuzhiyun
174*4882a593Smuzhiyun if (in_period_us > max_period_us) {
175*4882a593Smuzhiyun dev_warn(&pdev->dev,
176*4882a593Smuzhiyun "debouncer period %u too big, reduced to %llu us\n",
177*4882a593Smuzhiyun in_period_us, max_period_us);
178*4882a593Smuzhiyun return max_idx;
179*4882a593Smuzhiyun }
180*4882a593Smuzhiyun
181*4882a593Smuzhiyun for (i = max_idx - 1; i > 0; i--) {
182*4882a593Smuzhiyun period_us = DBC_PERIOD_US(sdwc_dbc_period[i]);
183*4882a593Smuzhiyun dev_dbg(&pdev->dev, "%s: ref[%d] = %llu\n",
184*4882a593Smuzhiyun __func__, i, period_us);
185*4882a593Smuzhiyun if (in_period_us > period_us)
186*4882a593Smuzhiyun break;
187*4882a593Smuzhiyun }
188*4882a593Smuzhiyun
189*4882a593Smuzhiyun return i + 1;
190*4882a593Smuzhiyun }
191*4882a593Smuzhiyun
at91_shdwc_get_wakeup_input(struct platform_device * pdev,struct device_node * np)192*4882a593Smuzhiyun static u32 at91_shdwc_get_wakeup_input(struct platform_device *pdev,
193*4882a593Smuzhiyun struct device_node *np)
194*4882a593Smuzhiyun {
195*4882a593Smuzhiyun struct device_node *cnp;
196*4882a593Smuzhiyun u32 wk_input_mask;
197*4882a593Smuzhiyun u32 wuir = 0;
198*4882a593Smuzhiyun u32 wk_input;
199*4882a593Smuzhiyun
200*4882a593Smuzhiyun for_each_child_of_node(np, cnp) {
201*4882a593Smuzhiyun if (of_property_read_u32(cnp, "reg", &wk_input)) {
202*4882a593Smuzhiyun dev_warn(&pdev->dev, "reg property is missing for %pOF\n",
203*4882a593Smuzhiyun cnp);
204*4882a593Smuzhiyun continue;
205*4882a593Smuzhiyun }
206*4882a593Smuzhiyun
207*4882a593Smuzhiyun wk_input_mask = 1 << wk_input;
208*4882a593Smuzhiyun if (!(wk_input_mask & AT91_SHDW_WKUPEN_MASK)) {
209*4882a593Smuzhiyun dev_warn(&pdev->dev,
210*4882a593Smuzhiyun "wake-up input %d out of bounds ignore\n",
211*4882a593Smuzhiyun wk_input);
212*4882a593Smuzhiyun continue;
213*4882a593Smuzhiyun }
214*4882a593Smuzhiyun wuir |= wk_input_mask;
215*4882a593Smuzhiyun
216*4882a593Smuzhiyun if (of_property_read_bool(cnp, "atmel,wakeup-active-high"))
217*4882a593Smuzhiyun wuir |= AT91_SHDW_WKUPT(wk_input);
218*4882a593Smuzhiyun
219*4882a593Smuzhiyun dev_dbg(&pdev->dev, "%s: (child %d) wuir = %#x\n",
220*4882a593Smuzhiyun __func__, wk_input, wuir);
221*4882a593Smuzhiyun }
222*4882a593Smuzhiyun
223*4882a593Smuzhiyun return wuir;
224*4882a593Smuzhiyun }
225*4882a593Smuzhiyun
at91_shdwc_dt_configure(struct platform_device * pdev)226*4882a593Smuzhiyun static void at91_shdwc_dt_configure(struct platform_device *pdev)
227*4882a593Smuzhiyun {
228*4882a593Smuzhiyun struct shdwc *shdw = platform_get_drvdata(pdev);
229*4882a593Smuzhiyun const struct reg_config *rcfg = shdw->rcfg;
230*4882a593Smuzhiyun struct device_node *np = pdev->dev.of_node;
231*4882a593Smuzhiyun u32 mode = 0, tmp, input;
232*4882a593Smuzhiyun
233*4882a593Smuzhiyun if (!np) {
234*4882a593Smuzhiyun dev_err(&pdev->dev, "device node not found\n");
235*4882a593Smuzhiyun return;
236*4882a593Smuzhiyun }
237*4882a593Smuzhiyun
238*4882a593Smuzhiyun if (!of_property_read_u32(np, "debounce-delay-us", &tmp))
239*4882a593Smuzhiyun mode |= AT91_SHDW_WKUPDBC(at91_shdwc_debouncer_value(pdev, tmp));
240*4882a593Smuzhiyun
241*4882a593Smuzhiyun if (of_property_read_bool(np, "atmel,wakeup-rtc-timer"))
242*4882a593Smuzhiyun mode |= SHDW_RTCWKEN(&rcfg->shdwc);
243*4882a593Smuzhiyun
244*4882a593Smuzhiyun if (of_property_read_bool(np, "atmel,wakeup-rtt-timer"))
245*4882a593Smuzhiyun mode |= SHDW_RTTWKEN(&rcfg->shdwc);
246*4882a593Smuzhiyun
247*4882a593Smuzhiyun dev_dbg(&pdev->dev, "%s: mode = %#x\n", __func__, mode);
248*4882a593Smuzhiyun writel(mode, shdw->shdwc_base + AT91_SHDW_MR);
249*4882a593Smuzhiyun
250*4882a593Smuzhiyun input = at91_shdwc_get_wakeup_input(pdev, np);
251*4882a593Smuzhiyun writel(input, shdw->shdwc_base + AT91_SHDW_WUIR);
252*4882a593Smuzhiyun }
253*4882a593Smuzhiyun
254*4882a593Smuzhiyun static const struct reg_config sama5d2_reg_config = {
255*4882a593Smuzhiyun .shdwc = {
256*4882a593Smuzhiyun .wkup_pin_input = 0,
257*4882a593Smuzhiyun .mr_rtcwk_shift = 17,
258*4882a593Smuzhiyun .mr_rttwk_shift = SHDW_CFG_NOT_USED,
259*4882a593Smuzhiyun .sr_rtcwk_shift = 5,
260*4882a593Smuzhiyun .sr_rttwk_shift = SHDW_CFG_NOT_USED,
261*4882a593Smuzhiyun },
262*4882a593Smuzhiyun .pmc = {
263*4882a593Smuzhiyun .mckr = 0x30,
264*4882a593Smuzhiyun },
265*4882a593Smuzhiyun };
266*4882a593Smuzhiyun
267*4882a593Smuzhiyun static const struct reg_config sam9x60_reg_config = {
268*4882a593Smuzhiyun .shdwc = {
269*4882a593Smuzhiyun .wkup_pin_input = 0,
270*4882a593Smuzhiyun .mr_rtcwk_shift = 17,
271*4882a593Smuzhiyun .mr_rttwk_shift = 16,
272*4882a593Smuzhiyun .sr_rtcwk_shift = 5,
273*4882a593Smuzhiyun .sr_rttwk_shift = 4,
274*4882a593Smuzhiyun },
275*4882a593Smuzhiyun .pmc = {
276*4882a593Smuzhiyun .mckr = 0x28,
277*4882a593Smuzhiyun },
278*4882a593Smuzhiyun };
279*4882a593Smuzhiyun
280*4882a593Smuzhiyun static const struct of_device_id at91_shdwc_of_match[] = {
281*4882a593Smuzhiyun {
282*4882a593Smuzhiyun .compatible = "atmel,sama5d2-shdwc",
283*4882a593Smuzhiyun .data = &sama5d2_reg_config,
284*4882a593Smuzhiyun },
285*4882a593Smuzhiyun {
286*4882a593Smuzhiyun .compatible = "microchip,sam9x60-shdwc",
287*4882a593Smuzhiyun .data = &sam9x60_reg_config,
288*4882a593Smuzhiyun }, {
289*4882a593Smuzhiyun /*sentinel*/
290*4882a593Smuzhiyun }
291*4882a593Smuzhiyun };
292*4882a593Smuzhiyun MODULE_DEVICE_TABLE(of, at91_shdwc_of_match);
293*4882a593Smuzhiyun
294*4882a593Smuzhiyun static const struct of_device_id at91_pmc_ids[] = {
295*4882a593Smuzhiyun { .compatible = "atmel,sama5d2-pmc" },
296*4882a593Smuzhiyun { .compatible = "microchip,sam9x60-pmc" },
297*4882a593Smuzhiyun { /* Sentinel. */ }
298*4882a593Smuzhiyun };
299*4882a593Smuzhiyun
at91_shdwc_probe(struct platform_device * pdev)300*4882a593Smuzhiyun static int __init at91_shdwc_probe(struct platform_device *pdev)
301*4882a593Smuzhiyun {
302*4882a593Smuzhiyun struct resource *res;
303*4882a593Smuzhiyun const struct of_device_id *match;
304*4882a593Smuzhiyun struct device_node *np;
305*4882a593Smuzhiyun u32 ddr_type;
306*4882a593Smuzhiyun int ret;
307*4882a593Smuzhiyun
308*4882a593Smuzhiyun if (!pdev->dev.of_node)
309*4882a593Smuzhiyun return -ENODEV;
310*4882a593Smuzhiyun
311*4882a593Smuzhiyun if (at91_shdwc)
312*4882a593Smuzhiyun return -EBUSY;
313*4882a593Smuzhiyun
314*4882a593Smuzhiyun at91_shdwc = devm_kzalloc(&pdev->dev, sizeof(*at91_shdwc), GFP_KERNEL);
315*4882a593Smuzhiyun if (!at91_shdwc)
316*4882a593Smuzhiyun return -ENOMEM;
317*4882a593Smuzhiyun
318*4882a593Smuzhiyun platform_set_drvdata(pdev, at91_shdwc);
319*4882a593Smuzhiyun
320*4882a593Smuzhiyun res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
321*4882a593Smuzhiyun at91_shdwc->shdwc_base = devm_ioremap_resource(&pdev->dev, res);
322*4882a593Smuzhiyun if (IS_ERR(at91_shdwc->shdwc_base)) {
323*4882a593Smuzhiyun dev_err(&pdev->dev, "Could not map reset controller address\n");
324*4882a593Smuzhiyun return PTR_ERR(at91_shdwc->shdwc_base);
325*4882a593Smuzhiyun }
326*4882a593Smuzhiyun
327*4882a593Smuzhiyun match = of_match_node(at91_shdwc_of_match, pdev->dev.of_node);
328*4882a593Smuzhiyun at91_shdwc->rcfg = match->data;
329*4882a593Smuzhiyun
330*4882a593Smuzhiyun at91_shdwc->sclk = devm_clk_get(&pdev->dev, NULL);
331*4882a593Smuzhiyun if (IS_ERR(at91_shdwc->sclk))
332*4882a593Smuzhiyun return PTR_ERR(at91_shdwc->sclk);
333*4882a593Smuzhiyun
334*4882a593Smuzhiyun ret = clk_prepare_enable(at91_shdwc->sclk);
335*4882a593Smuzhiyun if (ret) {
336*4882a593Smuzhiyun dev_err(&pdev->dev, "Could not enable slow clock\n");
337*4882a593Smuzhiyun return ret;
338*4882a593Smuzhiyun }
339*4882a593Smuzhiyun
340*4882a593Smuzhiyun at91_wakeup_status(pdev);
341*4882a593Smuzhiyun
342*4882a593Smuzhiyun at91_shdwc_dt_configure(pdev);
343*4882a593Smuzhiyun
344*4882a593Smuzhiyun np = of_find_matching_node(NULL, at91_pmc_ids);
345*4882a593Smuzhiyun if (!np) {
346*4882a593Smuzhiyun ret = -ENODEV;
347*4882a593Smuzhiyun goto clk_disable;
348*4882a593Smuzhiyun }
349*4882a593Smuzhiyun
350*4882a593Smuzhiyun at91_shdwc->pmc_base = of_iomap(np, 0);
351*4882a593Smuzhiyun of_node_put(np);
352*4882a593Smuzhiyun
353*4882a593Smuzhiyun if (!at91_shdwc->pmc_base) {
354*4882a593Smuzhiyun ret = -ENOMEM;
355*4882a593Smuzhiyun goto clk_disable;
356*4882a593Smuzhiyun }
357*4882a593Smuzhiyun
358*4882a593Smuzhiyun np = of_find_compatible_node(NULL, NULL, "atmel,sama5d3-ddramc");
359*4882a593Smuzhiyun if (!np) {
360*4882a593Smuzhiyun ret = -ENODEV;
361*4882a593Smuzhiyun goto unmap;
362*4882a593Smuzhiyun }
363*4882a593Smuzhiyun
364*4882a593Smuzhiyun at91_shdwc->mpddrc_base = of_iomap(np, 0);
365*4882a593Smuzhiyun of_node_put(np);
366*4882a593Smuzhiyun
367*4882a593Smuzhiyun if (!at91_shdwc->mpddrc_base) {
368*4882a593Smuzhiyun ret = -ENOMEM;
369*4882a593Smuzhiyun goto unmap;
370*4882a593Smuzhiyun }
371*4882a593Smuzhiyun
372*4882a593Smuzhiyun pm_power_off = at91_poweroff;
373*4882a593Smuzhiyun
374*4882a593Smuzhiyun ddr_type = readl(at91_shdwc->mpddrc_base + AT91_DDRSDRC_MDR) &
375*4882a593Smuzhiyun AT91_DDRSDRC_MD;
376*4882a593Smuzhiyun if (ddr_type != AT91_DDRSDRC_MD_LPDDR2 &&
377*4882a593Smuzhiyun ddr_type != AT91_DDRSDRC_MD_LPDDR3) {
378*4882a593Smuzhiyun iounmap(at91_shdwc->mpddrc_base);
379*4882a593Smuzhiyun at91_shdwc->mpddrc_base = NULL;
380*4882a593Smuzhiyun }
381*4882a593Smuzhiyun
382*4882a593Smuzhiyun return 0;
383*4882a593Smuzhiyun
384*4882a593Smuzhiyun unmap:
385*4882a593Smuzhiyun iounmap(at91_shdwc->pmc_base);
386*4882a593Smuzhiyun clk_disable:
387*4882a593Smuzhiyun clk_disable_unprepare(at91_shdwc->sclk);
388*4882a593Smuzhiyun
389*4882a593Smuzhiyun return ret;
390*4882a593Smuzhiyun }
391*4882a593Smuzhiyun
at91_shdwc_remove(struct platform_device * pdev)392*4882a593Smuzhiyun static int __exit at91_shdwc_remove(struct platform_device *pdev)
393*4882a593Smuzhiyun {
394*4882a593Smuzhiyun struct shdwc *shdw = platform_get_drvdata(pdev);
395*4882a593Smuzhiyun
396*4882a593Smuzhiyun if (pm_power_off == at91_poweroff)
397*4882a593Smuzhiyun pm_power_off = NULL;
398*4882a593Smuzhiyun
399*4882a593Smuzhiyun /* Reset values to disable wake-up features */
400*4882a593Smuzhiyun writel(0, shdw->shdwc_base + AT91_SHDW_MR);
401*4882a593Smuzhiyun writel(0, shdw->shdwc_base + AT91_SHDW_WUIR);
402*4882a593Smuzhiyun
403*4882a593Smuzhiyun if (shdw->mpddrc_base)
404*4882a593Smuzhiyun iounmap(shdw->mpddrc_base);
405*4882a593Smuzhiyun iounmap(shdw->pmc_base);
406*4882a593Smuzhiyun
407*4882a593Smuzhiyun clk_disable_unprepare(shdw->sclk);
408*4882a593Smuzhiyun
409*4882a593Smuzhiyun return 0;
410*4882a593Smuzhiyun }
411*4882a593Smuzhiyun
412*4882a593Smuzhiyun static struct platform_driver at91_shdwc_driver = {
413*4882a593Smuzhiyun .remove = __exit_p(at91_shdwc_remove),
414*4882a593Smuzhiyun .driver = {
415*4882a593Smuzhiyun .name = "at91-shdwc",
416*4882a593Smuzhiyun .of_match_table = at91_shdwc_of_match,
417*4882a593Smuzhiyun },
418*4882a593Smuzhiyun };
419*4882a593Smuzhiyun module_platform_driver_probe(at91_shdwc_driver, at91_shdwc_probe);
420*4882a593Smuzhiyun
421*4882a593Smuzhiyun MODULE_AUTHOR("Nicolas Ferre <nicolas.ferre@atmel.com>");
422*4882a593Smuzhiyun MODULE_DESCRIPTION("Atmel shutdown controller driver");
423*4882a593Smuzhiyun MODULE_LICENSE("GPL v2");
424