xref: /OK3568_Linux_fs/kernel/drivers/watchdog/coh901327_wdt.c (revision 4882a59341e53eb6f0b4789bf948001014eff981)
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