1*4882a593Smuzhiyun // SPDX-License-Identifier: GPL-2.0
2*4882a593Smuzhiyun /*
3*4882a593Smuzhiyun * coh901327_wdt.c
4*4882a593Smuzhiyun *
5*4882a593Smuzhiyun * Copyright (C) 2008-2009 ST-Ericsson AB
6*4882a593Smuzhiyun * Watchdog driver for the ST-Ericsson AB COH 901 327 IP core
7*4882a593Smuzhiyun * Author: Linus Walleij <linus.walleij@stericsson.com>
8*4882a593Smuzhiyun */
9*4882a593Smuzhiyun #include <linux/moduleparam.h>
10*4882a593Smuzhiyun #include <linux/mod_devicetable.h>
11*4882a593Smuzhiyun #include <linux/types.h>
12*4882a593Smuzhiyun #include <linux/watchdog.h>
13*4882a593Smuzhiyun #include <linux/interrupt.h>
14*4882a593Smuzhiyun #include <linux/pm.h>
15*4882a593Smuzhiyun #include <linux/platform_device.h>
16*4882a593Smuzhiyun #include <linux/io.h>
17*4882a593Smuzhiyun #include <linux/bitops.h>
18*4882a593Smuzhiyun #include <linux/clk.h>
19*4882a593Smuzhiyun #include <linux/delay.h>
20*4882a593Smuzhiyun #include <linux/err.h>
21*4882a593Smuzhiyun
22*4882a593Smuzhiyun #define DRV_NAME "WDOG COH 901 327"
23*4882a593Smuzhiyun
24*4882a593Smuzhiyun /*
25*4882a593Smuzhiyun * COH 901 327 register definitions
26*4882a593Smuzhiyun */
27*4882a593Smuzhiyun
28*4882a593Smuzhiyun /* WDOG_FEED Register 32bit (-/W) */
29*4882a593Smuzhiyun #define U300_WDOG_FR 0x00
30*4882a593Smuzhiyun #define U300_WDOG_FR_FEED_RESTART_TIMER 0xFEEDU
31*4882a593Smuzhiyun /* WDOG_TIMEOUT Register 32bit (R/W) */
32*4882a593Smuzhiyun #define U300_WDOG_TR 0x04
33*4882a593Smuzhiyun #define U300_WDOG_TR_TIMEOUT_MASK 0x7FFFU
34*4882a593Smuzhiyun /* WDOG_DISABLE1 Register 32bit (-/W) */
35*4882a593Smuzhiyun #define U300_WDOG_D1R 0x08
36*4882a593Smuzhiyun #define U300_WDOG_D1R_DISABLE1_DISABLE_TIMER 0x2BADU
37*4882a593Smuzhiyun /* WDOG_DISABLE2 Register 32bit (R/W) */
38*4882a593Smuzhiyun #define U300_WDOG_D2R 0x0C
39*4882a593Smuzhiyun #define U300_WDOG_D2R_DISABLE2_DISABLE_TIMER 0xCAFEU
40*4882a593Smuzhiyun #define U300_WDOG_D2R_DISABLE_STATUS_DISABLED 0xDABEU
41*4882a593Smuzhiyun #define U300_WDOG_D2R_DISABLE_STATUS_ENABLED 0x0000U
42*4882a593Smuzhiyun /* WDOG_STATUS Register 32bit (R/W) */
43*4882a593Smuzhiyun #define U300_WDOG_SR 0x10
44*4882a593Smuzhiyun #define U300_WDOG_SR_STATUS_TIMED_OUT 0xCFE8U
45*4882a593Smuzhiyun #define U300_WDOG_SR_STATUS_NORMAL 0x0000U
46*4882a593Smuzhiyun #define U300_WDOG_SR_RESET_STATUS_RESET 0xE8B4U
47*4882a593Smuzhiyun /* WDOG_COUNT Register 32bit (R/-) */
48*4882a593Smuzhiyun #define U300_WDOG_CR 0x14
49*4882a593Smuzhiyun #define U300_WDOG_CR_VALID_IND 0x8000U
50*4882a593Smuzhiyun #define U300_WDOG_CR_VALID_STABLE 0x0000U
51*4882a593Smuzhiyun #define U300_WDOG_CR_COUNT_VALUE_MASK 0x7FFFU
52*4882a593Smuzhiyun /* WDOG_JTAGOVR Register 32bit (R/W) */
53*4882a593Smuzhiyun #define U300_WDOG_JOR 0x18
54*4882a593Smuzhiyun #define U300_WDOG_JOR_JTAG_MODE_IND 0x0002U
55*4882a593Smuzhiyun #define U300_WDOG_JOR_JTAG_WATCHDOG_ENABLE 0x0001U
56*4882a593Smuzhiyun /* WDOG_RESTART Register 32bit (-/W) */
57*4882a593Smuzhiyun #define U300_WDOG_RR 0x1C
58*4882a593Smuzhiyun #define U300_WDOG_RR_RESTART_VALUE_RESUME 0xACEDU
59*4882a593Smuzhiyun /* WDOG_IRQ_EVENT Register 32bit (R/W) */
60*4882a593Smuzhiyun #define U300_WDOG_IER 0x20
61*4882a593Smuzhiyun #define U300_WDOG_IER_WILL_BARK_IRQ_EVENT_IND 0x0001U
62*4882a593Smuzhiyun #define U300_WDOG_IER_WILL_BARK_IRQ_ACK_ENABLE 0x0001U
63*4882a593Smuzhiyun /* WDOG_IRQ_MASK Register 32bit (R/W) */
64*4882a593Smuzhiyun #define U300_WDOG_IMR 0x24
65*4882a593Smuzhiyun #define U300_WDOG_IMR_WILL_BARK_IRQ_ENABLE 0x0001U
66*4882a593Smuzhiyun /* WDOG_IRQ_FORCE Register 32bit (R/W) */
67*4882a593Smuzhiyun #define U300_WDOG_IFR 0x28
68*4882a593Smuzhiyun #define U300_WDOG_IFR_WILL_BARK_IRQ_FORCE_ENABLE 0x0001U
69*4882a593Smuzhiyun
70*4882a593Smuzhiyun /* Default timeout in seconds = 1 minute */
71*4882a593Smuzhiyun #define U300_WDOG_DEFAULT_TIMEOUT 60
72*4882a593Smuzhiyun
73*4882a593Smuzhiyun static unsigned int margin;
74*4882a593Smuzhiyun static int irq;
75*4882a593Smuzhiyun static void __iomem *virtbase;
76*4882a593Smuzhiyun static struct device *parent;
77*4882a593Smuzhiyun
78*4882a593Smuzhiyun static struct clk *clk;
79*4882a593Smuzhiyun
80*4882a593Smuzhiyun /*
81*4882a593Smuzhiyun * Enabling and disabling functions.
82*4882a593Smuzhiyun */
coh901327_enable(u16 timeout)83*4882a593Smuzhiyun static void coh901327_enable(u16 timeout)
84*4882a593Smuzhiyun {
85*4882a593Smuzhiyun u16 val;
86*4882a593Smuzhiyun unsigned long freq;
87*4882a593Smuzhiyun unsigned long delay_ns;
88*4882a593Smuzhiyun
89*4882a593Smuzhiyun /* Restart timer if it is disabled */
90*4882a593Smuzhiyun val = readw(virtbase + U300_WDOG_D2R);
91*4882a593Smuzhiyun if (val == U300_WDOG_D2R_DISABLE_STATUS_DISABLED)
92*4882a593Smuzhiyun writew(U300_WDOG_RR_RESTART_VALUE_RESUME,
93*4882a593Smuzhiyun virtbase + U300_WDOG_RR);
94*4882a593Smuzhiyun /* Acknowledge any pending interrupt so it doesn't just fire off */
95*4882a593Smuzhiyun writew(U300_WDOG_IER_WILL_BARK_IRQ_ACK_ENABLE,
96*4882a593Smuzhiyun virtbase + U300_WDOG_IER);
97*4882a593Smuzhiyun /*
98*4882a593Smuzhiyun * The interrupt is cleared in the 32 kHz clock domain.
99*4882a593Smuzhiyun * Wait 3 32 kHz cycles for it to take effect
100*4882a593Smuzhiyun */
101*4882a593Smuzhiyun freq = clk_get_rate(clk);
102*4882a593Smuzhiyun delay_ns = DIV_ROUND_UP(1000000000, freq); /* Freq to ns and round up */
103*4882a593Smuzhiyun delay_ns = 3 * delay_ns; /* Wait 3 cycles */
104*4882a593Smuzhiyun ndelay(delay_ns);
105*4882a593Smuzhiyun /* Enable the watchdog interrupt */
106*4882a593Smuzhiyun writew(U300_WDOG_IMR_WILL_BARK_IRQ_ENABLE, virtbase + U300_WDOG_IMR);
107*4882a593Smuzhiyun /* Activate the watchdog timer */
108*4882a593Smuzhiyun writew(timeout, virtbase + U300_WDOG_TR);
109*4882a593Smuzhiyun /* Start the watchdog timer */
110*4882a593Smuzhiyun writew(U300_WDOG_FR_FEED_RESTART_TIMER, virtbase + U300_WDOG_FR);
111*4882a593Smuzhiyun /*
112*4882a593Smuzhiyun * Extra read so that this change propagate in the watchdog.
113*4882a593Smuzhiyun */
114*4882a593Smuzhiyun (void) readw(virtbase + U300_WDOG_CR);
115*4882a593Smuzhiyun val = readw(virtbase + U300_WDOG_D2R);
116*4882a593Smuzhiyun if (val != U300_WDOG_D2R_DISABLE_STATUS_ENABLED)
117*4882a593Smuzhiyun dev_err(parent,
118*4882a593Smuzhiyun "%s(): watchdog not enabled! D2R value %04x\n",
119*4882a593Smuzhiyun __func__, val);
120*4882a593Smuzhiyun }
121*4882a593Smuzhiyun
coh901327_disable(void)122*4882a593Smuzhiyun static void coh901327_disable(void)
123*4882a593Smuzhiyun {
124*4882a593Smuzhiyun u16 val;
125*4882a593Smuzhiyun
126*4882a593Smuzhiyun /* Disable the watchdog interrupt if it is active */
127*4882a593Smuzhiyun writew(0x0000U, virtbase + U300_WDOG_IMR);
128*4882a593Smuzhiyun /* If the watchdog is currently enabled, attempt to disable it */
129*4882a593Smuzhiyun val = readw(virtbase + U300_WDOG_D2R);
130*4882a593Smuzhiyun if (val != U300_WDOG_D2R_DISABLE_STATUS_DISABLED) {
131*4882a593Smuzhiyun writew(U300_WDOG_D1R_DISABLE1_DISABLE_TIMER,
132*4882a593Smuzhiyun virtbase + U300_WDOG_D1R);
133*4882a593Smuzhiyun writew(U300_WDOG_D2R_DISABLE2_DISABLE_TIMER,
134*4882a593Smuzhiyun virtbase + U300_WDOG_D2R);
135*4882a593Smuzhiyun /* Write this twice (else problems occur) */
136*4882a593Smuzhiyun writew(U300_WDOG_D2R_DISABLE2_DISABLE_TIMER,
137*4882a593Smuzhiyun virtbase + U300_WDOG_D2R);
138*4882a593Smuzhiyun }
139*4882a593Smuzhiyun val = readw(virtbase + U300_WDOG_D2R);
140*4882a593Smuzhiyun if (val != U300_WDOG_D2R_DISABLE_STATUS_DISABLED)
141*4882a593Smuzhiyun dev_err(parent,
142*4882a593Smuzhiyun "%s(): watchdog not disabled! D2R value %04x\n",
143*4882a593Smuzhiyun __func__, val);
144*4882a593Smuzhiyun }
145*4882a593Smuzhiyun
coh901327_start(struct watchdog_device * wdt_dev)146*4882a593Smuzhiyun static int coh901327_start(struct watchdog_device *wdt_dev)
147*4882a593Smuzhiyun {
148*4882a593Smuzhiyun coh901327_enable(wdt_dev->timeout * 100);
149*4882a593Smuzhiyun return 0;
150*4882a593Smuzhiyun }
151*4882a593Smuzhiyun
coh901327_stop(struct watchdog_device * wdt_dev)152*4882a593Smuzhiyun static int coh901327_stop(struct watchdog_device *wdt_dev)
153*4882a593Smuzhiyun {
154*4882a593Smuzhiyun coh901327_disable();
155*4882a593Smuzhiyun return 0;
156*4882a593Smuzhiyun }
157*4882a593Smuzhiyun
coh901327_ping(struct watchdog_device * wdd)158*4882a593Smuzhiyun static int coh901327_ping(struct watchdog_device *wdd)
159*4882a593Smuzhiyun {
160*4882a593Smuzhiyun /* Feed the watchdog */
161*4882a593Smuzhiyun writew(U300_WDOG_FR_FEED_RESTART_TIMER,
162*4882a593Smuzhiyun virtbase + U300_WDOG_FR);
163*4882a593Smuzhiyun return 0;
164*4882a593Smuzhiyun }
165*4882a593Smuzhiyun
coh901327_settimeout(struct watchdog_device * wdt_dev,unsigned int time)166*4882a593Smuzhiyun static int coh901327_settimeout(struct watchdog_device *wdt_dev,
167*4882a593Smuzhiyun unsigned int time)
168*4882a593Smuzhiyun {
169*4882a593Smuzhiyun wdt_dev->timeout = time;
170*4882a593Smuzhiyun /* Set new timeout value */
171*4882a593Smuzhiyun writew(time * 100, virtbase + U300_WDOG_TR);
172*4882a593Smuzhiyun /* Feed the dog */
173*4882a593Smuzhiyun writew(U300_WDOG_FR_FEED_RESTART_TIMER,
174*4882a593Smuzhiyun virtbase + U300_WDOG_FR);
175*4882a593Smuzhiyun return 0;
176*4882a593Smuzhiyun }
177*4882a593Smuzhiyun
coh901327_gettimeleft(struct watchdog_device * wdt_dev)178*4882a593Smuzhiyun static unsigned int coh901327_gettimeleft(struct watchdog_device *wdt_dev)
179*4882a593Smuzhiyun {
180*4882a593Smuzhiyun u16 val;
181*4882a593Smuzhiyun
182*4882a593Smuzhiyun /* Read repeatedly until the value is stable! */
183*4882a593Smuzhiyun val = readw(virtbase + U300_WDOG_CR);
184*4882a593Smuzhiyun while (val & U300_WDOG_CR_VALID_IND)
185*4882a593Smuzhiyun val = readw(virtbase + U300_WDOG_CR);
186*4882a593Smuzhiyun val &= U300_WDOG_CR_COUNT_VALUE_MASK;
187*4882a593Smuzhiyun if (val != 0)
188*4882a593Smuzhiyun val /= 100;
189*4882a593Smuzhiyun
190*4882a593Smuzhiyun return val;
191*4882a593Smuzhiyun }
192*4882a593Smuzhiyun
193*4882a593Smuzhiyun /*
194*4882a593Smuzhiyun * This interrupt occurs 10 ms before the watchdog WILL bark.
195*4882a593Smuzhiyun */
coh901327_interrupt(int irq,void * data)196*4882a593Smuzhiyun static irqreturn_t coh901327_interrupt(int irq, void *data)
197*4882a593Smuzhiyun {
198*4882a593Smuzhiyun u16 val;
199*4882a593Smuzhiyun
200*4882a593Smuzhiyun /*
201*4882a593Smuzhiyun * Ack IRQ? If this occurs we're FUBAR anyway, so
202*4882a593Smuzhiyun * just acknowledge, disable the interrupt and await the imminent end.
203*4882a593Smuzhiyun * If you at some point need a host of callbacks to be called
204*4882a593Smuzhiyun * when the system is about to watchdog-reset, add them here!
205*4882a593Smuzhiyun *
206*4882a593Smuzhiyun * NOTE: on future versions of this IP-block, it will be possible
207*4882a593Smuzhiyun * to prevent a watchdog reset by feeding the watchdog at this
208*4882a593Smuzhiyun * point.
209*4882a593Smuzhiyun */
210*4882a593Smuzhiyun val = readw(virtbase + U300_WDOG_IER);
211*4882a593Smuzhiyun if (val == U300_WDOG_IER_WILL_BARK_IRQ_EVENT_IND)
212*4882a593Smuzhiyun writew(U300_WDOG_IER_WILL_BARK_IRQ_ACK_ENABLE,
213*4882a593Smuzhiyun virtbase + U300_WDOG_IER);
214*4882a593Smuzhiyun writew(0x0000U, virtbase + U300_WDOG_IMR);
215*4882a593Smuzhiyun dev_crit(parent, "watchdog is barking!\n");
216*4882a593Smuzhiyun return IRQ_HANDLED;
217*4882a593Smuzhiyun }
218*4882a593Smuzhiyun
219*4882a593Smuzhiyun static const struct watchdog_info coh901327_ident = {
220*4882a593Smuzhiyun .options = WDIOF_CARDRESET | WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING,
221*4882a593Smuzhiyun .identity = DRV_NAME,
222*4882a593Smuzhiyun };
223*4882a593Smuzhiyun
224*4882a593Smuzhiyun static const struct watchdog_ops coh901327_ops = {
225*4882a593Smuzhiyun .owner = THIS_MODULE,
226*4882a593Smuzhiyun .start = coh901327_start,
227*4882a593Smuzhiyun .stop = coh901327_stop,
228*4882a593Smuzhiyun .ping = coh901327_ping,
229*4882a593Smuzhiyun .set_timeout = coh901327_settimeout,
230*4882a593Smuzhiyun .get_timeleft = coh901327_gettimeleft,
231*4882a593Smuzhiyun };
232*4882a593Smuzhiyun
233*4882a593Smuzhiyun static struct watchdog_device coh901327_wdt = {
234*4882a593Smuzhiyun .info = &coh901327_ident,
235*4882a593Smuzhiyun .ops = &coh901327_ops,
236*4882a593Smuzhiyun /*
237*4882a593Smuzhiyun * Max timeout is 327 since the 10ms
238*4882a593Smuzhiyun * timeout register is max
239*4882a593Smuzhiyun * 0x7FFF = 327670ms ~= 327s.
240*4882a593Smuzhiyun */
241*4882a593Smuzhiyun .min_timeout = 1,
242*4882a593Smuzhiyun .max_timeout = 327,
243*4882a593Smuzhiyun .timeout = U300_WDOG_DEFAULT_TIMEOUT,
244*4882a593Smuzhiyun };
245*4882a593Smuzhiyun
coh901327_probe(struct platform_device * pdev)246*4882a593Smuzhiyun static int __init coh901327_probe(struct platform_device *pdev)
247*4882a593Smuzhiyun {
248*4882a593Smuzhiyun struct device *dev = &pdev->dev;
249*4882a593Smuzhiyun int ret;
250*4882a593Smuzhiyun u16 val;
251*4882a593Smuzhiyun
252*4882a593Smuzhiyun parent = dev;
253*4882a593Smuzhiyun
254*4882a593Smuzhiyun virtbase = devm_platform_ioremap_resource(pdev, 0);
255*4882a593Smuzhiyun if (IS_ERR(virtbase))
256*4882a593Smuzhiyun return PTR_ERR(virtbase);
257*4882a593Smuzhiyun
258*4882a593Smuzhiyun clk = clk_get(dev, NULL);
259*4882a593Smuzhiyun if (IS_ERR(clk)) {
260*4882a593Smuzhiyun ret = PTR_ERR(clk);
261*4882a593Smuzhiyun dev_err(dev, "could not get clock\n");
262*4882a593Smuzhiyun return ret;
263*4882a593Smuzhiyun }
264*4882a593Smuzhiyun ret = clk_prepare_enable(clk);
265*4882a593Smuzhiyun if (ret) {
266*4882a593Smuzhiyun dev_err(dev, "could not prepare and enable clock\n");
267*4882a593Smuzhiyun goto out_no_clk_enable;
268*4882a593Smuzhiyun }
269*4882a593Smuzhiyun
270*4882a593Smuzhiyun val = readw(virtbase + U300_WDOG_SR);
271*4882a593Smuzhiyun switch (val) {
272*4882a593Smuzhiyun case U300_WDOG_SR_STATUS_TIMED_OUT:
273*4882a593Smuzhiyun dev_info(dev, "watchdog timed out since last chip reset!\n");
274*4882a593Smuzhiyun coh901327_wdt.bootstatus |= WDIOF_CARDRESET;
275*4882a593Smuzhiyun /* Status will be cleared below */
276*4882a593Smuzhiyun break;
277*4882a593Smuzhiyun case U300_WDOG_SR_STATUS_NORMAL:
278*4882a593Smuzhiyun dev_info(dev, "in normal status, no timeouts have occurred.\n");
279*4882a593Smuzhiyun break;
280*4882a593Smuzhiyun default:
281*4882a593Smuzhiyun dev_info(dev, "contains an illegal status code (%08x)\n", val);
282*4882a593Smuzhiyun break;
283*4882a593Smuzhiyun }
284*4882a593Smuzhiyun
285*4882a593Smuzhiyun val = readw(virtbase + U300_WDOG_D2R);
286*4882a593Smuzhiyun switch (val) {
287*4882a593Smuzhiyun case U300_WDOG_D2R_DISABLE_STATUS_DISABLED:
288*4882a593Smuzhiyun dev_info(dev, "currently disabled.\n");
289*4882a593Smuzhiyun break;
290*4882a593Smuzhiyun case U300_WDOG_D2R_DISABLE_STATUS_ENABLED:
291*4882a593Smuzhiyun dev_info(dev, "currently enabled! (disabling it now)\n");
292*4882a593Smuzhiyun coh901327_disable();
293*4882a593Smuzhiyun break;
294*4882a593Smuzhiyun default:
295*4882a593Smuzhiyun dev_err(dev, "contains an illegal enable/disable code (%08x)\n",
296*4882a593Smuzhiyun val);
297*4882a593Smuzhiyun break;
298*4882a593Smuzhiyun }
299*4882a593Smuzhiyun
300*4882a593Smuzhiyun /* Reset the watchdog */
301*4882a593Smuzhiyun writew(U300_WDOG_SR_RESET_STATUS_RESET, virtbase + U300_WDOG_SR);
302*4882a593Smuzhiyun
303*4882a593Smuzhiyun irq = platform_get_irq(pdev, 0);
304*4882a593Smuzhiyun if (request_irq(irq, coh901327_interrupt, 0,
305*4882a593Smuzhiyun DRV_NAME " Bark", pdev)) {
306*4882a593Smuzhiyun ret = -EIO;
307*4882a593Smuzhiyun goto out_no_irq;
308*4882a593Smuzhiyun }
309*4882a593Smuzhiyun
310*4882a593Smuzhiyun watchdog_init_timeout(&coh901327_wdt, margin, dev);
311*4882a593Smuzhiyun
312*4882a593Smuzhiyun coh901327_wdt.parent = dev;
313*4882a593Smuzhiyun ret = watchdog_register_device(&coh901327_wdt);
314*4882a593Smuzhiyun if (ret)
315*4882a593Smuzhiyun goto out_no_wdog;
316*4882a593Smuzhiyun
317*4882a593Smuzhiyun dev_info(dev, "initialized. (timeout=%d sec)\n",
318*4882a593Smuzhiyun coh901327_wdt.timeout);
319*4882a593Smuzhiyun return 0;
320*4882a593Smuzhiyun
321*4882a593Smuzhiyun out_no_wdog:
322*4882a593Smuzhiyun free_irq(irq, pdev);
323*4882a593Smuzhiyun out_no_irq:
324*4882a593Smuzhiyun clk_disable_unprepare(clk);
325*4882a593Smuzhiyun out_no_clk_enable:
326*4882a593Smuzhiyun clk_put(clk);
327*4882a593Smuzhiyun return ret;
328*4882a593Smuzhiyun }
329*4882a593Smuzhiyun
330*4882a593Smuzhiyun #ifdef CONFIG_PM
331*4882a593Smuzhiyun
332*4882a593Smuzhiyun static u16 wdogenablestore;
333*4882a593Smuzhiyun static u16 irqmaskstore;
334*4882a593Smuzhiyun
coh901327_suspend(struct platform_device * pdev,pm_message_t state)335*4882a593Smuzhiyun static int coh901327_suspend(struct platform_device *pdev, pm_message_t state)
336*4882a593Smuzhiyun {
337*4882a593Smuzhiyun irqmaskstore = readw(virtbase + U300_WDOG_IMR) & 0x0001U;
338*4882a593Smuzhiyun wdogenablestore = readw(virtbase + U300_WDOG_D2R);
339*4882a593Smuzhiyun /* If watchdog is on, disable it here and now */
340*4882a593Smuzhiyun if (wdogenablestore == U300_WDOG_D2R_DISABLE_STATUS_ENABLED)
341*4882a593Smuzhiyun coh901327_disable();
342*4882a593Smuzhiyun return 0;
343*4882a593Smuzhiyun }
344*4882a593Smuzhiyun
coh901327_resume(struct platform_device * pdev)345*4882a593Smuzhiyun static int coh901327_resume(struct platform_device *pdev)
346*4882a593Smuzhiyun {
347*4882a593Smuzhiyun /* Restore the watchdog interrupt */
348*4882a593Smuzhiyun writew(irqmaskstore, virtbase + U300_WDOG_IMR);
349*4882a593Smuzhiyun if (wdogenablestore == U300_WDOG_D2R_DISABLE_STATUS_ENABLED) {
350*4882a593Smuzhiyun /* Restart the watchdog timer */
351*4882a593Smuzhiyun writew(U300_WDOG_RR_RESTART_VALUE_RESUME,
352*4882a593Smuzhiyun virtbase + U300_WDOG_RR);
353*4882a593Smuzhiyun writew(U300_WDOG_FR_FEED_RESTART_TIMER,
354*4882a593Smuzhiyun virtbase + U300_WDOG_FR);
355*4882a593Smuzhiyun }
356*4882a593Smuzhiyun return 0;
357*4882a593Smuzhiyun }
358*4882a593Smuzhiyun #else
359*4882a593Smuzhiyun #define coh901327_suspend NULL
360*4882a593Smuzhiyun #define coh901327_resume NULL
361*4882a593Smuzhiyun #endif
362*4882a593Smuzhiyun
363*4882a593Smuzhiyun /*
364*4882a593Smuzhiyun * Mistreating the watchdog is the only way to perform a software reset of the
365*4882a593Smuzhiyun * system on EMP platforms. So we implement this and export a symbol for it.
366*4882a593Smuzhiyun */
coh901327_watchdog_reset(void)367*4882a593Smuzhiyun void coh901327_watchdog_reset(void)
368*4882a593Smuzhiyun {
369*4882a593Smuzhiyun /* Enable even if on JTAG too */
370*4882a593Smuzhiyun writew(U300_WDOG_JOR_JTAG_WATCHDOG_ENABLE,
371*4882a593Smuzhiyun virtbase + U300_WDOG_JOR);
372*4882a593Smuzhiyun /*
373*4882a593Smuzhiyun * Timeout = 5s, we have to wait for the watchdog reset to
374*4882a593Smuzhiyun * actually take place: the watchdog will be reloaded with the
375*4882a593Smuzhiyun * default value immediately, so we HAVE to reboot and get back
376*4882a593Smuzhiyun * into the kernel in 30s, or the device will reboot again!
377*4882a593Smuzhiyun * The boot loader will typically deactivate the watchdog, so we
378*4882a593Smuzhiyun * need time enough for the boot loader to get to the point of
379*4882a593Smuzhiyun * deactivating the watchdog before it is shut down by it.
380*4882a593Smuzhiyun *
381*4882a593Smuzhiyun * NOTE: on future versions of the watchdog, this restriction is
382*4882a593Smuzhiyun * gone: the watchdog will be reloaded with a default value (1 min)
383*4882a593Smuzhiyun * instead of last value, and you can conveniently set the watchdog
384*4882a593Smuzhiyun * timeout to 10ms (value = 1) without any problems.
385*4882a593Smuzhiyun */
386*4882a593Smuzhiyun coh901327_enable(500);
387*4882a593Smuzhiyun /* Return and await doom */
388*4882a593Smuzhiyun }
389*4882a593Smuzhiyun
390*4882a593Smuzhiyun static const struct of_device_id coh901327_dt_match[] = {
391*4882a593Smuzhiyun { .compatible = "stericsson,coh901327" },
392*4882a593Smuzhiyun {},
393*4882a593Smuzhiyun };
394*4882a593Smuzhiyun
395*4882a593Smuzhiyun static struct platform_driver coh901327_driver = {
396*4882a593Smuzhiyun .driver = {
397*4882a593Smuzhiyun .name = "coh901327_wdog",
398*4882a593Smuzhiyun .of_match_table = coh901327_dt_match,
399*4882a593Smuzhiyun .suppress_bind_attrs = true,
400*4882a593Smuzhiyun },
401*4882a593Smuzhiyun .suspend = coh901327_suspend,
402*4882a593Smuzhiyun .resume = coh901327_resume,
403*4882a593Smuzhiyun };
404*4882a593Smuzhiyun builtin_platform_driver_probe(coh901327_driver, coh901327_probe);
405*4882a593Smuzhiyun
406*4882a593Smuzhiyun /* not really modular, but ... */
407*4882a593Smuzhiyun module_param(margin, uint, 0);
408*4882a593Smuzhiyun MODULE_PARM_DESC(margin, "Watchdog margin in seconds (default 60s)");
409