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