1*4882a593Smuzhiyun // SPDX-License-Identifier: GPL-2.0+
2*4882a593Smuzhiyun /*
3*4882a593Smuzhiyun * Watchdog Device Driver for Xilinx axi/xps_timebase_wdt
4*4882a593Smuzhiyun *
5*4882a593Smuzhiyun * (C) Copyright 2013 - 2014 Xilinx, Inc.
6*4882a593Smuzhiyun * (C) Copyright 2011 (Alejandro Cabrera <aldaya@gmail.com>)
7*4882a593Smuzhiyun */
8*4882a593Smuzhiyun
9*4882a593Smuzhiyun #include <linux/clk.h>
10*4882a593Smuzhiyun #include <linux/err.h>
11*4882a593Smuzhiyun #include <linux/module.h>
12*4882a593Smuzhiyun #include <linux/types.h>
13*4882a593Smuzhiyun #include <linux/kernel.h>
14*4882a593Smuzhiyun #include <linux/ioport.h>
15*4882a593Smuzhiyun #include <linux/watchdog.h>
16*4882a593Smuzhiyun #include <linux/io.h>
17*4882a593Smuzhiyun #include <linux/of.h>
18*4882a593Smuzhiyun #include <linux/of_device.h>
19*4882a593Smuzhiyun #include <linux/of_address.h>
20*4882a593Smuzhiyun
21*4882a593Smuzhiyun /* Register offsets for the Wdt device */
22*4882a593Smuzhiyun #define XWT_TWCSR0_OFFSET 0x0 /* Control/Status Register0 */
23*4882a593Smuzhiyun #define XWT_TWCSR1_OFFSET 0x4 /* Control/Status Register1 */
24*4882a593Smuzhiyun #define XWT_TBR_OFFSET 0x8 /* Timebase Register Offset */
25*4882a593Smuzhiyun
26*4882a593Smuzhiyun /* Control/Status Register Masks */
27*4882a593Smuzhiyun #define XWT_CSR0_WRS_MASK 0x00000008 /* Reset status */
28*4882a593Smuzhiyun #define XWT_CSR0_WDS_MASK 0x00000004 /* Timer state */
29*4882a593Smuzhiyun #define XWT_CSR0_EWDT1_MASK 0x00000002 /* Enable bit 1 */
30*4882a593Smuzhiyun
31*4882a593Smuzhiyun /* Control/Status Register 0/1 bits */
32*4882a593Smuzhiyun #define XWT_CSRX_EWDT2_MASK 0x00000001 /* Enable bit 2 */
33*4882a593Smuzhiyun
34*4882a593Smuzhiyun /* SelfTest constants */
35*4882a593Smuzhiyun #define XWT_MAX_SELFTEST_LOOP_COUNT 0x00010000
36*4882a593Smuzhiyun #define XWT_TIMER_FAILED 0xFFFFFFFF
37*4882a593Smuzhiyun
38*4882a593Smuzhiyun #define WATCHDOG_NAME "Xilinx Watchdog"
39*4882a593Smuzhiyun
40*4882a593Smuzhiyun struct xwdt_device {
41*4882a593Smuzhiyun void __iomem *base;
42*4882a593Smuzhiyun u32 wdt_interval;
43*4882a593Smuzhiyun spinlock_t spinlock;
44*4882a593Smuzhiyun struct watchdog_device xilinx_wdt_wdd;
45*4882a593Smuzhiyun struct clk *clk;
46*4882a593Smuzhiyun };
47*4882a593Smuzhiyun
xilinx_wdt_start(struct watchdog_device * wdd)48*4882a593Smuzhiyun static int xilinx_wdt_start(struct watchdog_device *wdd)
49*4882a593Smuzhiyun {
50*4882a593Smuzhiyun int ret;
51*4882a593Smuzhiyun u32 control_status_reg;
52*4882a593Smuzhiyun struct xwdt_device *xdev = watchdog_get_drvdata(wdd);
53*4882a593Smuzhiyun
54*4882a593Smuzhiyun ret = clk_enable(xdev->clk);
55*4882a593Smuzhiyun if (ret) {
56*4882a593Smuzhiyun dev_err(wdd->parent, "Failed to enable clock\n");
57*4882a593Smuzhiyun return ret;
58*4882a593Smuzhiyun }
59*4882a593Smuzhiyun
60*4882a593Smuzhiyun spin_lock(&xdev->spinlock);
61*4882a593Smuzhiyun
62*4882a593Smuzhiyun /* Clean previous status and enable the watchdog timer */
63*4882a593Smuzhiyun control_status_reg = ioread32(xdev->base + XWT_TWCSR0_OFFSET);
64*4882a593Smuzhiyun control_status_reg |= (XWT_CSR0_WRS_MASK | XWT_CSR0_WDS_MASK);
65*4882a593Smuzhiyun
66*4882a593Smuzhiyun iowrite32((control_status_reg | XWT_CSR0_EWDT1_MASK),
67*4882a593Smuzhiyun xdev->base + XWT_TWCSR0_OFFSET);
68*4882a593Smuzhiyun
69*4882a593Smuzhiyun iowrite32(XWT_CSRX_EWDT2_MASK, xdev->base + XWT_TWCSR1_OFFSET);
70*4882a593Smuzhiyun
71*4882a593Smuzhiyun spin_unlock(&xdev->spinlock);
72*4882a593Smuzhiyun
73*4882a593Smuzhiyun return 0;
74*4882a593Smuzhiyun }
75*4882a593Smuzhiyun
xilinx_wdt_stop(struct watchdog_device * wdd)76*4882a593Smuzhiyun static int xilinx_wdt_stop(struct watchdog_device *wdd)
77*4882a593Smuzhiyun {
78*4882a593Smuzhiyun u32 control_status_reg;
79*4882a593Smuzhiyun struct xwdt_device *xdev = watchdog_get_drvdata(wdd);
80*4882a593Smuzhiyun
81*4882a593Smuzhiyun spin_lock(&xdev->spinlock);
82*4882a593Smuzhiyun
83*4882a593Smuzhiyun control_status_reg = ioread32(xdev->base + XWT_TWCSR0_OFFSET);
84*4882a593Smuzhiyun
85*4882a593Smuzhiyun iowrite32((control_status_reg & ~XWT_CSR0_EWDT1_MASK),
86*4882a593Smuzhiyun xdev->base + XWT_TWCSR0_OFFSET);
87*4882a593Smuzhiyun
88*4882a593Smuzhiyun iowrite32(0, xdev->base + XWT_TWCSR1_OFFSET);
89*4882a593Smuzhiyun
90*4882a593Smuzhiyun spin_unlock(&xdev->spinlock);
91*4882a593Smuzhiyun
92*4882a593Smuzhiyun clk_disable(xdev->clk);
93*4882a593Smuzhiyun
94*4882a593Smuzhiyun pr_info("Stopped!\n");
95*4882a593Smuzhiyun
96*4882a593Smuzhiyun return 0;
97*4882a593Smuzhiyun }
98*4882a593Smuzhiyun
xilinx_wdt_keepalive(struct watchdog_device * wdd)99*4882a593Smuzhiyun static int xilinx_wdt_keepalive(struct watchdog_device *wdd)
100*4882a593Smuzhiyun {
101*4882a593Smuzhiyun u32 control_status_reg;
102*4882a593Smuzhiyun struct xwdt_device *xdev = watchdog_get_drvdata(wdd);
103*4882a593Smuzhiyun
104*4882a593Smuzhiyun spin_lock(&xdev->spinlock);
105*4882a593Smuzhiyun
106*4882a593Smuzhiyun control_status_reg = ioread32(xdev->base + XWT_TWCSR0_OFFSET);
107*4882a593Smuzhiyun control_status_reg |= (XWT_CSR0_WRS_MASK | XWT_CSR0_WDS_MASK);
108*4882a593Smuzhiyun iowrite32(control_status_reg, xdev->base + XWT_TWCSR0_OFFSET);
109*4882a593Smuzhiyun
110*4882a593Smuzhiyun spin_unlock(&xdev->spinlock);
111*4882a593Smuzhiyun
112*4882a593Smuzhiyun return 0;
113*4882a593Smuzhiyun }
114*4882a593Smuzhiyun
115*4882a593Smuzhiyun static const struct watchdog_info xilinx_wdt_ident = {
116*4882a593Smuzhiyun .options = WDIOF_MAGICCLOSE |
117*4882a593Smuzhiyun WDIOF_KEEPALIVEPING,
118*4882a593Smuzhiyun .firmware_version = 1,
119*4882a593Smuzhiyun .identity = WATCHDOG_NAME,
120*4882a593Smuzhiyun };
121*4882a593Smuzhiyun
122*4882a593Smuzhiyun static const struct watchdog_ops xilinx_wdt_ops = {
123*4882a593Smuzhiyun .owner = THIS_MODULE,
124*4882a593Smuzhiyun .start = xilinx_wdt_start,
125*4882a593Smuzhiyun .stop = xilinx_wdt_stop,
126*4882a593Smuzhiyun .ping = xilinx_wdt_keepalive,
127*4882a593Smuzhiyun };
128*4882a593Smuzhiyun
xwdt_selftest(struct xwdt_device * xdev)129*4882a593Smuzhiyun static u32 xwdt_selftest(struct xwdt_device *xdev)
130*4882a593Smuzhiyun {
131*4882a593Smuzhiyun int i;
132*4882a593Smuzhiyun u32 timer_value1;
133*4882a593Smuzhiyun u32 timer_value2;
134*4882a593Smuzhiyun
135*4882a593Smuzhiyun spin_lock(&xdev->spinlock);
136*4882a593Smuzhiyun
137*4882a593Smuzhiyun timer_value1 = ioread32(xdev->base + XWT_TBR_OFFSET);
138*4882a593Smuzhiyun timer_value2 = ioread32(xdev->base + XWT_TBR_OFFSET);
139*4882a593Smuzhiyun
140*4882a593Smuzhiyun for (i = 0;
141*4882a593Smuzhiyun ((i <= XWT_MAX_SELFTEST_LOOP_COUNT) &&
142*4882a593Smuzhiyun (timer_value2 == timer_value1)); i++) {
143*4882a593Smuzhiyun timer_value2 = ioread32(xdev->base + XWT_TBR_OFFSET);
144*4882a593Smuzhiyun }
145*4882a593Smuzhiyun
146*4882a593Smuzhiyun spin_unlock(&xdev->spinlock);
147*4882a593Smuzhiyun
148*4882a593Smuzhiyun if (timer_value2 != timer_value1)
149*4882a593Smuzhiyun return ~XWT_TIMER_FAILED;
150*4882a593Smuzhiyun else
151*4882a593Smuzhiyun return XWT_TIMER_FAILED;
152*4882a593Smuzhiyun }
153*4882a593Smuzhiyun
xwdt_clk_disable_unprepare(void * data)154*4882a593Smuzhiyun static void xwdt_clk_disable_unprepare(void *data)
155*4882a593Smuzhiyun {
156*4882a593Smuzhiyun clk_disable_unprepare(data);
157*4882a593Smuzhiyun }
158*4882a593Smuzhiyun
xwdt_probe(struct platform_device * pdev)159*4882a593Smuzhiyun static int xwdt_probe(struct platform_device *pdev)
160*4882a593Smuzhiyun {
161*4882a593Smuzhiyun struct device *dev = &pdev->dev;
162*4882a593Smuzhiyun int rc;
163*4882a593Smuzhiyun u32 pfreq = 0, enable_once = 0;
164*4882a593Smuzhiyun struct xwdt_device *xdev;
165*4882a593Smuzhiyun struct watchdog_device *xilinx_wdt_wdd;
166*4882a593Smuzhiyun
167*4882a593Smuzhiyun xdev = devm_kzalloc(dev, sizeof(*xdev), GFP_KERNEL);
168*4882a593Smuzhiyun if (!xdev)
169*4882a593Smuzhiyun return -ENOMEM;
170*4882a593Smuzhiyun
171*4882a593Smuzhiyun xilinx_wdt_wdd = &xdev->xilinx_wdt_wdd;
172*4882a593Smuzhiyun xilinx_wdt_wdd->info = &xilinx_wdt_ident;
173*4882a593Smuzhiyun xilinx_wdt_wdd->ops = &xilinx_wdt_ops;
174*4882a593Smuzhiyun xilinx_wdt_wdd->parent = dev;
175*4882a593Smuzhiyun
176*4882a593Smuzhiyun xdev->base = devm_platform_ioremap_resource(pdev, 0);
177*4882a593Smuzhiyun if (IS_ERR(xdev->base))
178*4882a593Smuzhiyun return PTR_ERR(xdev->base);
179*4882a593Smuzhiyun
180*4882a593Smuzhiyun rc = of_property_read_u32(dev->of_node, "xlnx,wdt-interval",
181*4882a593Smuzhiyun &xdev->wdt_interval);
182*4882a593Smuzhiyun if (rc)
183*4882a593Smuzhiyun dev_warn(dev, "Parameter \"xlnx,wdt-interval\" not found\n");
184*4882a593Smuzhiyun
185*4882a593Smuzhiyun rc = of_property_read_u32(dev->of_node, "xlnx,wdt-enable-once",
186*4882a593Smuzhiyun &enable_once);
187*4882a593Smuzhiyun if (rc)
188*4882a593Smuzhiyun dev_warn(dev,
189*4882a593Smuzhiyun "Parameter \"xlnx,wdt-enable-once\" not found\n");
190*4882a593Smuzhiyun
191*4882a593Smuzhiyun watchdog_set_nowayout(xilinx_wdt_wdd, enable_once);
192*4882a593Smuzhiyun
193*4882a593Smuzhiyun xdev->clk = devm_clk_get(dev, NULL);
194*4882a593Smuzhiyun if (IS_ERR(xdev->clk)) {
195*4882a593Smuzhiyun if (PTR_ERR(xdev->clk) != -ENOENT)
196*4882a593Smuzhiyun return PTR_ERR(xdev->clk);
197*4882a593Smuzhiyun
198*4882a593Smuzhiyun /*
199*4882a593Smuzhiyun * Clock framework support is optional, continue on
200*4882a593Smuzhiyun * anyways if we don't find a matching clock.
201*4882a593Smuzhiyun */
202*4882a593Smuzhiyun xdev->clk = NULL;
203*4882a593Smuzhiyun
204*4882a593Smuzhiyun rc = of_property_read_u32(dev->of_node, "clock-frequency",
205*4882a593Smuzhiyun &pfreq);
206*4882a593Smuzhiyun if (rc)
207*4882a593Smuzhiyun dev_warn(dev,
208*4882a593Smuzhiyun "The watchdog clock freq cannot be obtained\n");
209*4882a593Smuzhiyun } else {
210*4882a593Smuzhiyun pfreq = clk_get_rate(xdev->clk);
211*4882a593Smuzhiyun }
212*4882a593Smuzhiyun
213*4882a593Smuzhiyun /*
214*4882a593Smuzhiyun * Twice of the 2^wdt_interval / freq because the first wdt overflow is
215*4882a593Smuzhiyun * ignored (interrupt), reset is only generated at second wdt overflow
216*4882a593Smuzhiyun */
217*4882a593Smuzhiyun if (pfreq && xdev->wdt_interval)
218*4882a593Smuzhiyun xilinx_wdt_wdd->timeout = 2 * ((1 << xdev->wdt_interval) /
219*4882a593Smuzhiyun pfreq);
220*4882a593Smuzhiyun
221*4882a593Smuzhiyun spin_lock_init(&xdev->spinlock);
222*4882a593Smuzhiyun watchdog_set_drvdata(xilinx_wdt_wdd, xdev);
223*4882a593Smuzhiyun
224*4882a593Smuzhiyun rc = clk_prepare_enable(xdev->clk);
225*4882a593Smuzhiyun if (rc) {
226*4882a593Smuzhiyun dev_err(dev, "unable to enable clock\n");
227*4882a593Smuzhiyun return rc;
228*4882a593Smuzhiyun }
229*4882a593Smuzhiyun rc = devm_add_action_or_reset(dev, xwdt_clk_disable_unprepare,
230*4882a593Smuzhiyun xdev->clk);
231*4882a593Smuzhiyun if (rc)
232*4882a593Smuzhiyun return rc;
233*4882a593Smuzhiyun
234*4882a593Smuzhiyun rc = xwdt_selftest(xdev);
235*4882a593Smuzhiyun if (rc == XWT_TIMER_FAILED) {
236*4882a593Smuzhiyun dev_err(dev, "SelfTest routine error\n");
237*4882a593Smuzhiyun return rc;
238*4882a593Smuzhiyun }
239*4882a593Smuzhiyun
240*4882a593Smuzhiyun rc = devm_watchdog_register_device(dev, xilinx_wdt_wdd);
241*4882a593Smuzhiyun if (rc)
242*4882a593Smuzhiyun return rc;
243*4882a593Smuzhiyun
244*4882a593Smuzhiyun clk_disable(xdev->clk);
245*4882a593Smuzhiyun
246*4882a593Smuzhiyun dev_info(dev, "Xilinx Watchdog Timer at %p with timeout %ds\n",
247*4882a593Smuzhiyun xdev->base, xilinx_wdt_wdd->timeout);
248*4882a593Smuzhiyun
249*4882a593Smuzhiyun platform_set_drvdata(pdev, xdev);
250*4882a593Smuzhiyun
251*4882a593Smuzhiyun return 0;
252*4882a593Smuzhiyun }
253*4882a593Smuzhiyun
254*4882a593Smuzhiyun /**
255*4882a593Smuzhiyun * xwdt_suspend - Suspend the device.
256*4882a593Smuzhiyun *
257*4882a593Smuzhiyun * @dev: handle to the device structure.
258*4882a593Smuzhiyun * Return: 0 always.
259*4882a593Smuzhiyun */
xwdt_suspend(struct device * dev)260*4882a593Smuzhiyun static int __maybe_unused xwdt_suspend(struct device *dev)
261*4882a593Smuzhiyun {
262*4882a593Smuzhiyun struct xwdt_device *xdev = dev_get_drvdata(dev);
263*4882a593Smuzhiyun
264*4882a593Smuzhiyun if (watchdog_active(&xdev->xilinx_wdt_wdd))
265*4882a593Smuzhiyun xilinx_wdt_stop(&xdev->xilinx_wdt_wdd);
266*4882a593Smuzhiyun
267*4882a593Smuzhiyun return 0;
268*4882a593Smuzhiyun }
269*4882a593Smuzhiyun
270*4882a593Smuzhiyun /**
271*4882a593Smuzhiyun * xwdt_resume - Resume the device.
272*4882a593Smuzhiyun *
273*4882a593Smuzhiyun * @dev: handle to the device structure.
274*4882a593Smuzhiyun * Return: 0 on success, errno otherwise.
275*4882a593Smuzhiyun */
xwdt_resume(struct device * dev)276*4882a593Smuzhiyun static int __maybe_unused xwdt_resume(struct device *dev)
277*4882a593Smuzhiyun {
278*4882a593Smuzhiyun struct xwdt_device *xdev = dev_get_drvdata(dev);
279*4882a593Smuzhiyun int ret = 0;
280*4882a593Smuzhiyun
281*4882a593Smuzhiyun if (watchdog_active(&xdev->xilinx_wdt_wdd))
282*4882a593Smuzhiyun ret = xilinx_wdt_start(&xdev->xilinx_wdt_wdd);
283*4882a593Smuzhiyun
284*4882a593Smuzhiyun return ret;
285*4882a593Smuzhiyun }
286*4882a593Smuzhiyun
287*4882a593Smuzhiyun static SIMPLE_DEV_PM_OPS(xwdt_pm_ops, xwdt_suspend, xwdt_resume);
288*4882a593Smuzhiyun
289*4882a593Smuzhiyun /* Match table for of_platform binding */
290*4882a593Smuzhiyun static const struct of_device_id xwdt_of_match[] = {
291*4882a593Smuzhiyun { .compatible = "xlnx,xps-timebase-wdt-1.00.a", },
292*4882a593Smuzhiyun { .compatible = "xlnx,xps-timebase-wdt-1.01.a", },
293*4882a593Smuzhiyun {},
294*4882a593Smuzhiyun };
295*4882a593Smuzhiyun MODULE_DEVICE_TABLE(of, xwdt_of_match);
296*4882a593Smuzhiyun
297*4882a593Smuzhiyun static struct platform_driver xwdt_driver = {
298*4882a593Smuzhiyun .probe = xwdt_probe,
299*4882a593Smuzhiyun .driver = {
300*4882a593Smuzhiyun .name = WATCHDOG_NAME,
301*4882a593Smuzhiyun .of_match_table = xwdt_of_match,
302*4882a593Smuzhiyun .pm = &xwdt_pm_ops,
303*4882a593Smuzhiyun },
304*4882a593Smuzhiyun };
305*4882a593Smuzhiyun
306*4882a593Smuzhiyun module_platform_driver(xwdt_driver);
307*4882a593Smuzhiyun
308*4882a593Smuzhiyun MODULE_AUTHOR("Alejandro Cabrera <aldaya@gmail.com>");
309*4882a593Smuzhiyun MODULE_DESCRIPTION("Xilinx Watchdog driver");
310*4882a593Smuzhiyun MODULE_LICENSE("GPL");
311