1*4882a593Smuzhiyun /*
2*4882a593Smuzhiyun * drivers/char/watchdog/sp805-wdt.c
3*4882a593Smuzhiyun *
4*4882a593Smuzhiyun * Watchdog driver for ARM SP805 watchdog module
5*4882a593Smuzhiyun *
6*4882a593Smuzhiyun * Copyright (C) 2010 ST Microelectronics
7*4882a593Smuzhiyun * Viresh Kumar <vireshk@kernel.org>
8*4882a593Smuzhiyun *
9*4882a593Smuzhiyun * This file is licensed under the terms of the GNU General Public
10*4882a593Smuzhiyun * License version 2 or later. This program is licensed "as is" without any
11*4882a593Smuzhiyun * warranty of any kind, whether express or implied.
12*4882a593Smuzhiyun */
13*4882a593Smuzhiyun
14*4882a593Smuzhiyun #include <linux/acpi.h>
15*4882a593Smuzhiyun #include <linux/device.h>
16*4882a593Smuzhiyun #include <linux/resource.h>
17*4882a593Smuzhiyun #include <linux/amba/bus.h>
18*4882a593Smuzhiyun #include <linux/bitops.h>
19*4882a593Smuzhiyun #include <linux/clk.h>
20*4882a593Smuzhiyun #include <linux/io.h>
21*4882a593Smuzhiyun #include <linux/ioport.h>
22*4882a593Smuzhiyun #include <linux/kernel.h>
23*4882a593Smuzhiyun #include <linux/math64.h>
24*4882a593Smuzhiyun #include <linux/module.h>
25*4882a593Smuzhiyun #include <linux/moduleparam.h>
26*4882a593Smuzhiyun #include <linux/of.h>
27*4882a593Smuzhiyun #include <linux/pm.h>
28*4882a593Smuzhiyun #include <linux/slab.h>
29*4882a593Smuzhiyun #include <linux/spinlock.h>
30*4882a593Smuzhiyun #include <linux/types.h>
31*4882a593Smuzhiyun #include <linux/watchdog.h>
32*4882a593Smuzhiyun
33*4882a593Smuzhiyun /* default timeout in seconds */
34*4882a593Smuzhiyun #define DEFAULT_TIMEOUT 60
35*4882a593Smuzhiyun
36*4882a593Smuzhiyun #define MODULE_NAME "sp805-wdt"
37*4882a593Smuzhiyun
38*4882a593Smuzhiyun /* watchdog register offsets and masks */
39*4882a593Smuzhiyun #define WDTLOAD 0x000
40*4882a593Smuzhiyun #define LOAD_MIN 0x00000001
41*4882a593Smuzhiyun #define LOAD_MAX 0xFFFFFFFF
42*4882a593Smuzhiyun #define WDTVALUE 0x004
43*4882a593Smuzhiyun #define WDTCONTROL 0x008
44*4882a593Smuzhiyun /* control register masks */
45*4882a593Smuzhiyun #define INT_ENABLE (1 << 0)
46*4882a593Smuzhiyun #define RESET_ENABLE (1 << 1)
47*4882a593Smuzhiyun #define ENABLE_MASK (INT_ENABLE | RESET_ENABLE)
48*4882a593Smuzhiyun #define WDTINTCLR 0x00C
49*4882a593Smuzhiyun #define WDTRIS 0x010
50*4882a593Smuzhiyun #define WDTMIS 0x014
51*4882a593Smuzhiyun #define INT_MASK (1 << 0)
52*4882a593Smuzhiyun #define WDTLOCK 0xC00
53*4882a593Smuzhiyun #define UNLOCK 0x1ACCE551
54*4882a593Smuzhiyun #define LOCK 0x00000001
55*4882a593Smuzhiyun
56*4882a593Smuzhiyun /**
57*4882a593Smuzhiyun * struct sp805_wdt: sp805 wdt device structure
58*4882a593Smuzhiyun * @wdd: instance of struct watchdog_device
59*4882a593Smuzhiyun * @lock: spin lock protecting dev structure and io access
60*4882a593Smuzhiyun * @base: base address of wdt
61*4882a593Smuzhiyun * @clk: clock structure of wdt
62*4882a593Smuzhiyun * @adev: amba device structure of wdt
63*4882a593Smuzhiyun * @status: current status of wdt
64*4882a593Smuzhiyun * @load_val: load value to be set for current timeout
65*4882a593Smuzhiyun */
66*4882a593Smuzhiyun struct sp805_wdt {
67*4882a593Smuzhiyun struct watchdog_device wdd;
68*4882a593Smuzhiyun spinlock_t lock;
69*4882a593Smuzhiyun void __iomem *base;
70*4882a593Smuzhiyun struct clk *clk;
71*4882a593Smuzhiyun u64 rate;
72*4882a593Smuzhiyun struct amba_device *adev;
73*4882a593Smuzhiyun unsigned int load_val;
74*4882a593Smuzhiyun };
75*4882a593Smuzhiyun
76*4882a593Smuzhiyun static bool nowayout = WATCHDOG_NOWAYOUT;
77*4882a593Smuzhiyun module_param(nowayout, bool, 0);
78*4882a593Smuzhiyun MODULE_PARM_DESC(nowayout,
79*4882a593Smuzhiyun "Set to 1 to keep watchdog running after device release");
80*4882a593Smuzhiyun
81*4882a593Smuzhiyun /* returns true if wdt is running; otherwise returns false */
wdt_is_running(struct watchdog_device * wdd)82*4882a593Smuzhiyun static bool wdt_is_running(struct watchdog_device *wdd)
83*4882a593Smuzhiyun {
84*4882a593Smuzhiyun struct sp805_wdt *wdt = watchdog_get_drvdata(wdd);
85*4882a593Smuzhiyun u32 wdtcontrol = readl_relaxed(wdt->base + WDTCONTROL);
86*4882a593Smuzhiyun
87*4882a593Smuzhiyun return (wdtcontrol & ENABLE_MASK) == ENABLE_MASK;
88*4882a593Smuzhiyun }
89*4882a593Smuzhiyun
90*4882a593Smuzhiyun /* This routine finds load value that will reset system in required timout */
wdt_setload(struct watchdog_device * wdd,unsigned int timeout)91*4882a593Smuzhiyun static int wdt_setload(struct watchdog_device *wdd, unsigned int timeout)
92*4882a593Smuzhiyun {
93*4882a593Smuzhiyun struct sp805_wdt *wdt = watchdog_get_drvdata(wdd);
94*4882a593Smuzhiyun u64 load, rate;
95*4882a593Smuzhiyun
96*4882a593Smuzhiyun rate = wdt->rate;
97*4882a593Smuzhiyun
98*4882a593Smuzhiyun /*
99*4882a593Smuzhiyun * sp805 runs counter with given value twice, after the end of first
100*4882a593Smuzhiyun * counter it gives an interrupt and then starts counter again. If
101*4882a593Smuzhiyun * interrupt already occurred then it resets the system. This is why
102*4882a593Smuzhiyun * load is half of what should be required.
103*4882a593Smuzhiyun */
104*4882a593Smuzhiyun load = div_u64(rate, 2) * timeout - 1;
105*4882a593Smuzhiyun
106*4882a593Smuzhiyun load = (load > LOAD_MAX) ? LOAD_MAX : load;
107*4882a593Smuzhiyun load = (load < LOAD_MIN) ? LOAD_MIN : load;
108*4882a593Smuzhiyun
109*4882a593Smuzhiyun spin_lock(&wdt->lock);
110*4882a593Smuzhiyun wdt->load_val = load;
111*4882a593Smuzhiyun /* roundup timeout to closest positive integer value */
112*4882a593Smuzhiyun wdd->timeout = div_u64((load + 1) * 2 + (rate / 2), rate);
113*4882a593Smuzhiyun spin_unlock(&wdt->lock);
114*4882a593Smuzhiyun
115*4882a593Smuzhiyun return 0;
116*4882a593Smuzhiyun }
117*4882a593Smuzhiyun
118*4882a593Smuzhiyun /* returns number of seconds left for reset to occur */
wdt_timeleft(struct watchdog_device * wdd)119*4882a593Smuzhiyun static unsigned int wdt_timeleft(struct watchdog_device *wdd)
120*4882a593Smuzhiyun {
121*4882a593Smuzhiyun struct sp805_wdt *wdt = watchdog_get_drvdata(wdd);
122*4882a593Smuzhiyun u64 load;
123*4882a593Smuzhiyun
124*4882a593Smuzhiyun spin_lock(&wdt->lock);
125*4882a593Smuzhiyun load = readl_relaxed(wdt->base + WDTVALUE);
126*4882a593Smuzhiyun
127*4882a593Smuzhiyun /*If the interrupt is inactive then time left is WDTValue + WDTLoad. */
128*4882a593Smuzhiyun if (!(readl_relaxed(wdt->base + WDTRIS) & INT_MASK))
129*4882a593Smuzhiyun load += wdt->load_val + 1;
130*4882a593Smuzhiyun spin_unlock(&wdt->lock);
131*4882a593Smuzhiyun
132*4882a593Smuzhiyun return div_u64(load, wdt->rate);
133*4882a593Smuzhiyun }
134*4882a593Smuzhiyun
135*4882a593Smuzhiyun static int
wdt_restart(struct watchdog_device * wdd,unsigned long mode,void * cmd)136*4882a593Smuzhiyun wdt_restart(struct watchdog_device *wdd, unsigned long mode, void *cmd)
137*4882a593Smuzhiyun {
138*4882a593Smuzhiyun struct sp805_wdt *wdt = watchdog_get_drvdata(wdd);
139*4882a593Smuzhiyun
140*4882a593Smuzhiyun writel_relaxed(UNLOCK, wdt->base + WDTLOCK);
141*4882a593Smuzhiyun writel_relaxed(0, wdt->base + WDTCONTROL);
142*4882a593Smuzhiyun writel_relaxed(0, wdt->base + WDTLOAD);
143*4882a593Smuzhiyun writel_relaxed(INT_ENABLE | RESET_ENABLE, wdt->base + WDTCONTROL);
144*4882a593Smuzhiyun
145*4882a593Smuzhiyun /* Flush posted writes. */
146*4882a593Smuzhiyun readl_relaxed(wdt->base + WDTLOCK);
147*4882a593Smuzhiyun
148*4882a593Smuzhiyun return 0;
149*4882a593Smuzhiyun }
150*4882a593Smuzhiyun
wdt_config(struct watchdog_device * wdd,bool ping)151*4882a593Smuzhiyun static int wdt_config(struct watchdog_device *wdd, bool ping)
152*4882a593Smuzhiyun {
153*4882a593Smuzhiyun struct sp805_wdt *wdt = watchdog_get_drvdata(wdd);
154*4882a593Smuzhiyun int ret;
155*4882a593Smuzhiyun
156*4882a593Smuzhiyun if (!ping) {
157*4882a593Smuzhiyun
158*4882a593Smuzhiyun ret = clk_prepare_enable(wdt->clk);
159*4882a593Smuzhiyun if (ret) {
160*4882a593Smuzhiyun dev_err(&wdt->adev->dev, "clock enable fail");
161*4882a593Smuzhiyun return ret;
162*4882a593Smuzhiyun }
163*4882a593Smuzhiyun }
164*4882a593Smuzhiyun
165*4882a593Smuzhiyun spin_lock(&wdt->lock);
166*4882a593Smuzhiyun
167*4882a593Smuzhiyun writel_relaxed(UNLOCK, wdt->base + WDTLOCK);
168*4882a593Smuzhiyun writel_relaxed(wdt->load_val, wdt->base + WDTLOAD);
169*4882a593Smuzhiyun writel_relaxed(INT_MASK, wdt->base + WDTINTCLR);
170*4882a593Smuzhiyun
171*4882a593Smuzhiyun if (!ping)
172*4882a593Smuzhiyun writel_relaxed(INT_ENABLE | RESET_ENABLE, wdt->base +
173*4882a593Smuzhiyun WDTCONTROL);
174*4882a593Smuzhiyun
175*4882a593Smuzhiyun writel_relaxed(LOCK, wdt->base + WDTLOCK);
176*4882a593Smuzhiyun
177*4882a593Smuzhiyun /* Flush posted writes. */
178*4882a593Smuzhiyun readl_relaxed(wdt->base + WDTLOCK);
179*4882a593Smuzhiyun spin_unlock(&wdt->lock);
180*4882a593Smuzhiyun
181*4882a593Smuzhiyun return 0;
182*4882a593Smuzhiyun }
183*4882a593Smuzhiyun
wdt_ping(struct watchdog_device * wdd)184*4882a593Smuzhiyun static int wdt_ping(struct watchdog_device *wdd)
185*4882a593Smuzhiyun {
186*4882a593Smuzhiyun return wdt_config(wdd, true);
187*4882a593Smuzhiyun }
188*4882a593Smuzhiyun
189*4882a593Smuzhiyun /* enables watchdog timers reset */
wdt_enable(struct watchdog_device * wdd)190*4882a593Smuzhiyun static int wdt_enable(struct watchdog_device *wdd)
191*4882a593Smuzhiyun {
192*4882a593Smuzhiyun return wdt_config(wdd, false);
193*4882a593Smuzhiyun }
194*4882a593Smuzhiyun
195*4882a593Smuzhiyun /* disables watchdog timers reset */
wdt_disable(struct watchdog_device * wdd)196*4882a593Smuzhiyun static int wdt_disable(struct watchdog_device *wdd)
197*4882a593Smuzhiyun {
198*4882a593Smuzhiyun struct sp805_wdt *wdt = watchdog_get_drvdata(wdd);
199*4882a593Smuzhiyun
200*4882a593Smuzhiyun spin_lock(&wdt->lock);
201*4882a593Smuzhiyun
202*4882a593Smuzhiyun writel_relaxed(UNLOCK, wdt->base + WDTLOCK);
203*4882a593Smuzhiyun writel_relaxed(0, wdt->base + WDTCONTROL);
204*4882a593Smuzhiyun writel_relaxed(LOCK, wdt->base + WDTLOCK);
205*4882a593Smuzhiyun
206*4882a593Smuzhiyun /* Flush posted writes. */
207*4882a593Smuzhiyun readl_relaxed(wdt->base + WDTLOCK);
208*4882a593Smuzhiyun spin_unlock(&wdt->lock);
209*4882a593Smuzhiyun
210*4882a593Smuzhiyun clk_disable_unprepare(wdt->clk);
211*4882a593Smuzhiyun
212*4882a593Smuzhiyun return 0;
213*4882a593Smuzhiyun }
214*4882a593Smuzhiyun
215*4882a593Smuzhiyun static const struct watchdog_info wdt_info = {
216*4882a593Smuzhiyun .options = WDIOF_MAGICCLOSE | WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING,
217*4882a593Smuzhiyun .identity = MODULE_NAME,
218*4882a593Smuzhiyun };
219*4882a593Smuzhiyun
220*4882a593Smuzhiyun static const struct watchdog_ops wdt_ops = {
221*4882a593Smuzhiyun .owner = THIS_MODULE,
222*4882a593Smuzhiyun .start = wdt_enable,
223*4882a593Smuzhiyun .stop = wdt_disable,
224*4882a593Smuzhiyun .ping = wdt_ping,
225*4882a593Smuzhiyun .set_timeout = wdt_setload,
226*4882a593Smuzhiyun .get_timeleft = wdt_timeleft,
227*4882a593Smuzhiyun .restart = wdt_restart,
228*4882a593Smuzhiyun };
229*4882a593Smuzhiyun
230*4882a593Smuzhiyun static int
sp805_wdt_probe(struct amba_device * adev,const struct amba_id * id)231*4882a593Smuzhiyun sp805_wdt_probe(struct amba_device *adev, const struct amba_id *id)
232*4882a593Smuzhiyun {
233*4882a593Smuzhiyun struct sp805_wdt *wdt;
234*4882a593Smuzhiyun int ret = 0;
235*4882a593Smuzhiyun
236*4882a593Smuzhiyun wdt = devm_kzalloc(&adev->dev, sizeof(*wdt), GFP_KERNEL);
237*4882a593Smuzhiyun if (!wdt) {
238*4882a593Smuzhiyun ret = -ENOMEM;
239*4882a593Smuzhiyun goto err;
240*4882a593Smuzhiyun }
241*4882a593Smuzhiyun
242*4882a593Smuzhiyun wdt->base = devm_ioremap_resource(&adev->dev, &adev->res);
243*4882a593Smuzhiyun if (IS_ERR(wdt->base))
244*4882a593Smuzhiyun return PTR_ERR(wdt->base);
245*4882a593Smuzhiyun
246*4882a593Smuzhiyun if (adev->dev.of_node) {
247*4882a593Smuzhiyun wdt->clk = devm_clk_get(&adev->dev, NULL);
248*4882a593Smuzhiyun if (IS_ERR(wdt->clk)) {
249*4882a593Smuzhiyun dev_err(&adev->dev, "Clock not found\n");
250*4882a593Smuzhiyun return PTR_ERR(wdt->clk);
251*4882a593Smuzhiyun }
252*4882a593Smuzhiyun wdt->rate = clk_get_rate(wdt->clk);
253*4882a593Smuzhiyun } else if (has_acpi_companion(&adev->dev)) {
254*4882a593Smuzhiyun /*
255*4882a593Smuzhiyun * When Driver probe with ACPI device, clock devices
256*4882a593Smuzhiyun * are not available, so watchdog rate get from
257*4882a593Smuzhiyun * clock-frequency property given in _DSD object.
258*4882a593Smuzhiyun */
259*4882a593Smuzhiyun device_property_read_u64(&adev->dev, "clock-frequency",
260*4882a593Smuzhiyun &wdt->rate);
261*4882a593Smuzhiyun if (!wdt->rate) {
262*4882a593Smuzhiyun dev_err(&adev->dev, "no clock-frequency property\n");
263*4882a593Smuzhiyun return -ENODEV;
264*4882a593Smuzhiyun }
265*4882a593Smuzhiyun }
266*4882a593Smuzhiyun
267*4882a593Smuzhiyun wdt->adev = adev;
268*4882a593Smuzhiyun wdt->wdd.info = &wdt_info;
269*4882a593Smuzhiyun wdt->wdd.ops = &wdt_ops;
270*4882a593Smuzhiyun wdt->wdd.parent = &adev->dev;
271*4882a593Smuzhiyun
272*4882a593Smuzhiyun spin_lock_init(&wdt->lock);
273*4882a593Smuzhiyun watchdog_set_nowayout(&wdt->wdd, nowayout);
274*4882a593Smuzhiyun watchdog_set_drvdata(&wdt->wdd, wdt);
275*4882a593Smuzhiyun watchdog_set_restart_priority(&wdt->wdd, 128);
276*4882a593Smuzhiyun
277*4882a593Smuzhiyun /*
278*4882a593Smuzhiyun * If 'timeout-sec' devicetree property is specified, use that.
279*4882a593Smuzhiyun * Otherwise, use DEFAULT_TIMEOUT
280*4882a593Smuzhiyun */
281*4882a593Smuzhiyun wdt->wdd.timeout = DEFAULT_TIMEOUT;
282*4882a593Smuzhiyun watchdog_init_timeout(&wdt->wdd, 0, &adev->dev);
283*4882a593Smuzhiyun wdt_setload(&wdt->wdd, wdt->wdd.timeout);
284*4882a593Smuzhiyun
285*4882a593Smuzhiyun /*
286*4882a593Smuzhiyun * If HW is already running, enable/reset the wdt and set the running
287*4882a593Smuzhiyun * bit to tell the wdt subsystem
288*4882a593Smuzhiyun */
289*4882a593Smuzhiyun if (wdt_is_running(&wdt->wdd)) {
290*4882a593Smuzhiyun wdt_enable(&wdt->wdd);
291*4882a593Smuzhiyun set_bit(WDOG_HW_RUNNING, &wdt->wdd.status);
292*4882a593Smuzhiyun }
293*4882a593Smuzhiyun
294*4882a593Smuzhiyun ret = watchdog_register_device(&wdt->wdd);
295*4882a593Smuzhiyun if (ret)
296*4882a593Smuzhiyun goto err;
297*4882a593Smuzhiyun amba_set_drvdata(adev, wdt);
298*4882a593Smuzhiyun
299*4882a593Smuzhiyun dev_info(&adev->dev, "registration successful\n");
300*4882a593Smuzhiyun return 0;
301*4882a593Smuzhiyun
302*4882a593Smuzhiyun err:
303*4882a593Smuzhiyun dev_err(&adev->dev, "Probe Failed!!!\n");
304*4882a593Smuzhiyun return ret;
305*4882a593Smuzhiyun }
306*4882a593Smuzhiyun
sp805_wdt_remove(struct amba_device * adev)307*4882a593Smuzhiyun static void sp805_wdt_remove(struct amba_device *adev)
308*4882a593Smuzhiyun {
309*4882a593Smuzhiyun struct sp805_wdt *wdt = amba_get_drvdata(adev);
310*4882a593Smuzhiyun
311*4882a593Smuzhiyun watchdog_unregister_device(&wdt->wdd);
312*4882a593Smuzhiyun watchdog_set_drvdata(&wdt->wdd, NULL);
313*4882a593Smuzhiyun }
314*4882a593Smuzhiyun
sp805_wdt_suspend(struct device * dev)315*4882a593Smuzhiyun static int __maybe_unused sp805_wdt_suspend(struct device *dev)
316*4882a593Smuzhiyun {
317*4882a593Smuzhiyun struct sp805_wdt *wdt = dev_get_drvdata(dev);
318*4882a593Smuzhiyun
319*4882a593Smuzhiyun if (watchdog_active(&wdt->wdd))
320*4882a593Smuzhiyun return wdt_disable(&wdt->wdd);
321*4882a593Smuzhiyun
322*4882a593Smuzhiyun return 0;
323*4882a593Smuzhiyun }
324*4882a593Smuzhiyun
sp805_wdt_resume(struct device * dev)325*4882a593Smuzhiyun static int __maybe_unused sp805_wdt_resume(struct device *dev)
326*4882a593Smuzhiyun {
327*4882a593Smuzhiyun struct sp805_wdt *wdt = dev_get_drvdata(dev);
328*4882a593Smuzhiyun
329*4882a593Smuzhiyun if (watchdog_active(&wdt->wdd))
330*4882a593Smuzhiyun return wdt_enable(&wdt->wdd);
331*4882a593Smuzhiyun
332*4882a593Smuzhiyun return 0;
333*4882a593Smuzhiyun }
334*4882a593Smuzhiyun
335*4882a593Smuzhiyun static SIMPLE_DEV_PM_OPS(sp805_wdt_dev_pm_ops, sp805_wdt_suspend,
336*4882a593Smuzhiyun sp805_wdt_resume);
337*4882a593Smuzhiyun
338*4882a593Smuzhiyun static const struct amba_id sp805_wdt_ids[] = {
339*4882a593Smuzhiyun {
340*4882a593Smuzhiyun .id = 0x00141805,
341*4882a593Smuzhiyun .mask = 0x00ffffff,
342*4882a593Smuzhiyun },
343*4882a593Smuzhiyun { 0, 0 },
344*4882a593Smuzhiyun };
345*4882a593Smuzhiyun
346*4882a593Smuzhiyun MODULE_DEVICE_TABLE(amba, sp805_wdt_ids);
347*4882a593Smuzhiyun
348*4882a593Smuzhiyun static struct amba_driver sp805_wdt_driver = {
349*4882a593Smuzhiyun .drv = {
350*4882a593Smuzhiyun .name = MODULE_NAME,
351*4882a593Smuzhiyun .pm = &sp805_wdt_dev_pm_ops,
352*4882a593Smuzhiyun },
353*4882a593Smuzhiyun .id_table = sp805_wdt_ids,
354*4882a593Smuzhiyun .probe = sp805_wdt_probe,
355*4882a593Smuzhiyun .remove = sp805_wdt_remove,
356*4882a593Smuzhiyun };
357*4882a593Smuzhiyun
358*4882a593Smuzhiyun module_amba_driver(sp805_wdt_driver);
359*4882a593Smuzhiyun
360*4882a593Smuzhiyun MODULE_AUTHOR("Viresh Kumar <vireshk@kernel.org>");
361*4882a593Smuzhiyun MODULE_DESCRIPTION("ARM SP805 Watchdog Driver");
362*4882a593Smuzhiyun MODULE_LICENSE("GPL");
363