diff options
author | Harini Katakam <harinik@xilinx.com> | 2014-08-22 05:28:01 -0400 |
---|---|---|
committer | Wim Van Sebroeck <wim@iguana.be> | 2014-10-20 14:48:02 -0400 |
commit | 58bf016426594e5370e7e7059698a278294db997 (patch) | |
tree | 53fdbd7793b7a5cf60b0f7f819c116b3b698769f /drivers/watchdog | |
parent | 4846e3784585173f48e267b76f968bcb4a12d3b2 (diff) |
watchdog: Add Cadence WDT driver
Add Cadence WDT driver. This is used by Xilinx Zynq.
Signed-off-by: Harini Katakam <harinik@xilinx.com>
Reviewed-by: Guenter Roeck <linux@roeck-us.net>
Signed-off-by: Wim Van Sebroeck <wim@iguana.be>
Diffstat (limited to 'drivers/watchdog')
-rw-r--r-- | drivers/watchdog/Kconfig | 8 | ||||
-rw-r--r-- | drivers/watchdog/Makefile | 1 | ||||
-rw-r--r-- | drivers/watchdog/cadence_wdt.c | 516 |
3 files changed, 525 insertions, 0 deletions
diff --git a/drivers/watchdog/Kconfig b/drivers/watchdog/Kconfig index e3d5bf0a5021..786f8c8338fb 100644 --- a/drivers/watchdog/Kconfig +++ b/drivers/watchdog/Kconfig | |||
@@ -157,6 +157,14 @@ config AT91SAM9X_WATCHDOG | |||
157 | Watchdog timer embedded into AT91SAM9X and AT91CAP9 chips. This will | 157 | Watchdog timer embedded into AT91SAM9X and AT91CAP9 chips. This will |
158 | reboot your system when the timeout is reached. | 158 | reboot your system when the timeout is reached. |
159 | 159 | ||
160 | config CADENCE_WATCHDOG | ||
161 | tristate "Cadence Watchdog Timer" | ||
162 | depends on ARM | ||
163 | select WATCHDOG_CORE | ||
164 | help | ||
165 | Say Y here if you want to include support for the watchdog | ||
166 | timer in the Xilinx Zynq. | ||
167 | |||
160 | config 21285_WATCHDOG | 168 | config 21285_WATCHDOG |
161 | tristate "DC21285 watchdog" | 169 | tristate "DC21285 watchdog" |
162 | depends on FOOTBRIDGE | 170 | depends on FOOTBRIDGE |
diff --git a/drivers/watchdog/Makefile b/drivers/watchdog/Makefile index de1701470c14..82af9956f96a 100644 --- a/drivers/watchdog/Makefile +++ b/drivers/watchdog/Makefile | |||
@@ -32,6 +32,7 @@ obj-$(CONFIG_USBPCWATCHDOG) += pcwd_usb.o | |||
32 | obj-$(CONFIG_ARM_SP805_WATCHDOG) += sp805_wdt.o | 32 | obj-$(CONFIG_ARM_SP805_WATCHDOG) += sp805_wdt.o |
33 | obj-$(CONFIG_AT91RM9200_WATCHDOG) += at91rm9200_wdt.o | 33 | obj-$(CONFIG_AT91RM9200_WATCHDOG) += at91rm9200_wdt.o |
34 | obj-$(CONFIG_AT91SAM9X_WATCHDOG) += at91sam9_wdt.o | 34 | obj-$(CONFIG_AT91SAM9X_WATCHDOG) += at91sam9_wdt.o |
35 | obj-$(CONFIG_CADENCE_WATCHDOG) += cadence_wdt.o | ||
35 | obj-$(CONFIG_OMAP_WATCHDOG) += omap_wdt.o | 36 | obj-$(CONFIG_OMAP_WATCHDOG) += omap_wdt.o |
36 | obj-$(CONFIG_TWL4030_WATCHDOG) += twl4030_wdt.o | 37 | obj-$(CONFIG_TWL4030_WATCHDOG) += twl4030_wdt.o |
37 | obj-$(CONFIG_21285_WATCHDOG) += wdt285.o | 38 | obj-$(CONFIG_21285_WATCHDOG) += wdt285.o |
diff --git a/drivers/watchdog/cadence_wdt.c b/drivers/watchdog/cadence_wdt.c new file mode 100644 index 000000000000..5927c0a98a74 --- /dev/null +++ b/drivers/watchdog/cadence_wdt.c | |||
@@ -0,0 +1,516 @@ | |||
1 | /* | ||
2 | * Cadence WDT driver - Used by Xilinx Zynq | ||
3 | * | ||
4 | * Copyright (C) 2010 - 2014 Xilinx, Inc. | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or | ||
7 | * modify it under the terms of the GNU General Public License | ||
8 | * as published by the Free Software Foundation; either version | ||
9 | * 2 of the License, or (at your option) any later version. | ||
10 | */ | ||
11 | |||
12 | #include <linux/clk.h> | ||
13 | #include <linux/init.h> | ||
14 | #include <linux/interrupt.h> | ||
15 | #include <linux/io.h> | ||
16 | #include <linux/irq.h> | ||
17 | #include <linux/kernel.h> | ||
18 | #include <linux/module.h> | ||
19 | #include <linux/of.h> | ||
20 | #include <linux/platform_device.h> | ||
21 | #include <linux/reboot.h> | ||
22 | #include <linux/watchdog.h> | ||
23 | |||
24 | #define CDNS_WDT_DEFAULT_TIMEOUT 10 | ||
25 | /* Supports 1 - 516 sec */ | ||
26 | #define CDNS_WDT_MIN_TIMEOUT 1 | ||
27 | #define CDNS_WDT_MAX_TIMEOUT 516 | ||
28 | |||
29 | /* Restart key */ | ||
30 | #define CDNS_WDT_RESTART_KEY 0x00001999 | ||
31 | |||
32 | /* Counter register access key */ | ||
33 | #define CDNS_WDT_REGISTER_ACCESS_KEY 0x00920000 | ||
34 | |||
35 | /* Counter value divisor */ | ||
36 | #define CDNS_WDT_COUNTER_VALUE_DIVISOR 0x1000 | ||
37 | |||
38 | /* Clock prescaler value and selection */ | ||
39 | #define CDNS_WDT_PRESCALE_64 64 | ||
40 | #define CDNS_WDT_PRESCALE_512 512 | ||
41 | #define CDNS_WDT_PRESCALE_4096 4096 | ||
42 | #define CDNS_WDT_PRESCALE_SELECT_64 1 | ||
43 | #define CDNS_WDT_PRESCALE_SELECT_512 2 | ||
44 | #define CDNS_WDT_PRESCALE_SELECT_4096 3 | ||
45 | |||
46 | /* Input clock frequency */ | ||
47 | #define CDNS_WDT_CLK_10MHZ 10000000 | ||
48 | #define CDNS_WDT_CLK_75MHZ 75000000 | ||
49 | |||
50 | /* Counter maximum value */ | ||
51 | #define CDNS_WDT_COUNTER_MAX 0xFFF | ||
52 | |||
53 | static int wdt_timeout = CDNS_WDT_DEFAULT_TIMEOUT; | ||
54 | static int nowayout = WATCHDOG_NOWAYOUT; | ||
55 | |||
56 | module_param(wdt_timeout, int, 0); | ||
57 | MODULE_PARM_DESC(wdt_timeout, | ||
58 | "Watchdog time in seconds. (default=" | ||
59 | __MODULE_STRING(CDNS_WDT_DEFAULT_TIMEOUT) ")"); | ||
60 | |||
61 | module_param(nowayout, int, 0); | ||
62 | MODULE_PARM_DESC(nowayout, | ||
63 | "Watchdog cannot be stopped once started (default=" | ||
64 | __MODULE_STRING(WATCHDOG_NOWAYOUT) ")"); | ||
65 | |||
66 | /** | ||
67 | * struct cdns_wdt - Watchdog device structure | ||
68 | * @regs: baseaddress of device | ||
69 | * @rst: reset flag | ||
70 | * @clk: struct clk * of a clock source | ||
71 | * @prescaler: for saving prescaler value | ||
72 | * @ctrl_clksel: counter clock prescaler selection | ||
73 | * @io_lock: spinlock for IO register access | ||
74 | * @cdns_wdt_device: watchdog device structure | ||
75 | * @cdns_wdt_notifier: notifier structure | ||
76 | * | ||
77 | * Structure containing parameters specific to cadence watchdog. | ||
78 | */ | ||
79 | struct cdns_wdt { | ||
80 | void __iomem *regs; | ||
81 | bool rst; | ||
82 | struct clk *clk; | ||
83 | u32 prescaler; | ||
84 | u32 ctrl_clksel; | ||
85 | spinlock_t io_lock; | ||
86 | struct watchdog_device cdns_wdt_device; | ||
87 | struct notifier_block cdns_wdt_notifier; | ||
88 | }; | ||
89 | |||
90 | /* Write access to Registers */ | ||
91 | static inline void cdns_wdt_writereg(struct cdns_wdt *wdt, u32 offset, u32 val) | ||
92 | { | ||
93 | writel_relaxed(val, wdt->regs + offset); | ||
94 | } | ||
95 | |||
96 | /*************************Register Map**************************************/ | ||
97 | |||
98 | /* Register Offsets for the WDT */ | ||
99 | #define CDNS_WDT_ZMR_OFFSET 0x0 /* Zero Mode Register */ | ||
100 | #define CDNS_WDT_CCR_OFFSET 0x4 /* Counter Control Register */ | ||
101 | #define CDNS_WDT_RESTART_OFFSET 0x8 /* Restart Register */ | ||
102 | #define CDNS_WDT_SR_OFFSET 0xC /* Status Register */ | ||
103 | |||
104 | /* | ||
105 | * Zero Mode Register - This register controls how the time out is indicated | ||
106 | * and also contains the access code to allow writes to the register (0xABC). | ||
107 | */ | ||
108 | #define CDNS_WDT_ZMR_WDEN_MASK 0x00000001 /* Enable the WDT */ | ||
109 | #define CDNS_WDT_ZMR_RSTEN_MASK 0x00000002 /* Enable the reset output */ | ||
110 | #define CDNS_WDT_ZMR_IRQEN_MASK 0x00000004 /* Enable IRQ output */ | ||
111 | #define CDNS_WDT_ZMR_RSTLEN_16 0x00000030 /* Reset pulse of 16 pclk cycles */ | ||
112 | #define CDNS_WDT_ZMR_ZKEY_VAL 0x00ABC000 /* Access key, 0xABC << 12 */ | ||
113 | /* | ||
114 | * Counter Control register - This register controls how fast the timer runs | ||
115 | * and the reset value and also contains the access code to allow writes to | ||
116 | * the register. | ||
117 | */ | ||
118 | #define CDNS_WDT_CCR_CRV_MASK 0x00003FFC /* Counter reset value */ | ||
119 | |||
120 | /** | ||
121 | * cdns_wdt_stop - Stop the watchdog. | ||
122 | * | ||
123 | * @wdd: watchdog device | ||
124 | * | ||
125 | * Read the contents of the ZMR register, clear the WDEN bit | ||
126 | * in the register and set the access key for successful write. | ||
127 | * | ||
128 | * Return: always 0 | ||
129 | */ | ||
130 | static int cdns_wdt_stop(struct watchdog_device *wdd) | ||
131 | { | ||
132 | struct cdns_wdt *wdt = watchdog_get_drvdata(wdd); | ||
133 | |||
134 | spin_lock(&wdt->io_lock); | ||
135 | cdns_wdt_writereg(wdt, CDNS_WDT_ZMR_OFFSET, | ||
136 | CDNS_WDT_ZMR_ZKEY_VAL & (~CDNS_WDT_ZMR_WDEN_MASK)); | ||
137 | spin_unlock(&wdt->io_lock); | ||
138 | |||
139 | return 0; | ||
140 | } | ||
141 | |||
142 | /** | ||
143 | * cdns_wdt_reload - Reload the watchdog timer (i.e. pat the watchdog). | ||
144 | * | ||
145 | * @wdd: watchdog device | ||
146 | * | ||
147 | * Write the restart key value (0x00001999) to the restart register. | ||
148 | * | ||
149 | * Return: always 0 | ||
150 | */ | ||
151 | static int cdns_wdt_reload(struct watchdog_device *wdd) | ||
152 | { | ||
153 | struct cdns_wdt *wdt = watchdog_get_drvdata(wdd); | ||
154 | |||
155 | spin_lock(&wdt->io_lock); | ||
156 | cdns_wdt_writereg(wdt, CDNS_WDT_RESTART_OFFSET, | ||
157 | CDNS_WDT_RESTART_KEY); | ||
158 | spin_unlock(&wdt->io_lock); | ||
159 | |||
160 | return 0; | ||
161 | } | ||
162 | |||
163 | /** | ||
164 | * cdns_wdt_start - Enable and start the watchdog. | ||
165 | * | ||
166 | * @wdd: watchdog device | ||
167 | * | ||
168 | * The counter value is calculated according to the formula: | ||
169 | * calculated count = (timeout * clock) / prescaler + 1. | ||
170 | * The calculated count is divided by 0x1000 to obtain the field value | ||
171 | * to write to counter control register. | ||
172 | * Clears the contents of prescaler and counter reset value. Sets the | ||
173 | * prescaler to 4096 and the calculated count and access key | ||
174 | * to write to CCR Register. | ||
175 | * Sets the WDT (WDEN bit) and either the Reset signal(RSTEN bit) | ||
176 | * or Interrupt signal(IRQEN) with a specified cycles and the access | ||
177 | * key to write to ZMR Register. | ||
178 | * | ||
179 | * Return: always 0 | ||
180 | */ | ||
181 | static int cdns_wdt_start(struct watchdog_device *wdd) | ||
182 | { | ||
183 | struct cdns_wdt *wdt = watchdog_get_drvdata(wdd); | ||
184 | unsigned int data = 0; | ||
185 | unsigned short count; | ||
186 | unsigned long clock_f = clk_get_rate(wdt->clk); | ||
187 | |||
188 | /* | ||
189 | * Counter value divisor to obtain the value of | ||
190 | * counter reset to be written to control register. | ||
191 | */ | ||
192 | count = (wdd->timeout * (clock_f / wdt->prescaler)) / | ||
193 | CDNS_WDT_COUNTER_VALUE_DIVISOR + 1; | ||
194 | |||
195 | if (count > CDNS_WDT_COUNTER_MAX) | ||
196 | count = CDNS_WDT_COUNTER_MAX; | ||
197 | |||
198 | spin_lock(&wdt->io_lock); | ||
199 | cdns_wdt_writereg(wdt, CDNS_WDT_ZMR_OFFSET, | ||
200 | CDNS_WDT_ZMR_ZKEY_VAL); | ||
201 | |||
202 | count = (count << 2) & CDNS_WDT_CCR_CRV_MASK; | ||
203 | |||
204 | /* Write counter access key first to be able write to register */ | ||
205 | data = count | CDNS_WDT_REGISTER_ACCESS_KEY | wdt->ctrl_clksel; | ||
206 | cdns_wdt_writereg(wdt, CDNS_WDT_CCR_OFFSET, data); | ||
207 | data = CDNS_WDT_ZMR_WDEN_MASK | CDNS_WDT_ZMR_RSTLEN_16 | | ||
208 | CDNS_WDT_ZMR_ZKEY_VAL; | ||
209 | |||
210 | /* Reset on timeout if specified in device tree. */ | ||
211 | if (wdt->rst) { | ||
212 | data |= CDNS_WDT_ZMR_RSTEN_MASK; | ||
213 | data &= ~CDNS_WDT_ZMR_IRQEN_MASK; | ||
214 | } else { | ||
215 | data &= ~CDNS_WDT_ZMR_RSTEN_MASK; | ||
216 | data |= CDNS_WDT_ZMR_IRQEN_MASK; | ||
217 | } | ||
218 | cdns_wdt_writereg(wdt, CDNS_WDT_ZMR_OFFSET, data); | ||
219 | cdns_wdt_writereg(wdt, CDNS_WDT_RESTART_OFFSET, | ||
220 | CDNS_WDT_RESTART_KEY); | ||
221 | spin_unlock(&wdt->io_lock); | ||
222 | |||
223 | return 0; | ||
224 | } | ||
225 | |||
226 | /** | ||
227 | * cdns_wdt_settimeout - Set a new timeout value for the watchdog device. | ||
228 | * | ||
229 | * @wdd: watchdog device | ||
230 | * @new_time: new timeout value that needs to be set | ||
231 | * Return: 0 on success | ||
232 | * | ||
233 | * Update the watchdog_device timeout with new value which is used when | ||
234 | * cdns_wdt_start is called. | ||
235 | */ | ||
236 | static int cdns_wdt_settimeout(struct watchdog_device *wdd, | ||
237 | unsigned int new_time) | ||
238 | { | ||
239 | wdd->timeout = new_time; | ||
240 | |||
241 | return cdns_wdt_start(wdd); | ||
242 | } | ||
243 | |||
244 | /** | ||
245 | * cdns_wdt_irq_handler - Notifies of watchdog timeout. | ||
246 | * | ||
247 | * @irq: interrupt number | ||
248 | * @dev_id: pointer to a platform device structure | ||
249 | * Return: IRQ_HANDLED | ||
250 | * | ||
251 | * The handler is invoked when the watchdog times out and a | ||
252 | * reset on timeout has not been enabled. | ||
253 | */ | ||
254 | static irqreturn_t cdns_wdt_irq_handler(int irq, void *dev_id) | ||
255 | { | ||
256 | struct platform_device *pdev = dev_id; | ||
257 | |||
258 | dev_info(&pdev->dev, | ||
259 | "Watchdog timed out. Internal reset not enabled\n"); | ||
260 | |||
261 | return IRQ_HANDLED; | ||
262 | } | ||
263 | |||
264 | /* | ||
265 | * Info structure used to indicate the features supported by the device | ||
266 | * to the upper layers. This is defined in watchdog.h header file. | ||
267 | */ | ||
268 | static struct watchdog_info cdns_wdt_info = { | ||
269 | .identity = "cdns_wdt watchdog", | ||
270 | .options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING | | ||
271 | WDIOF_MAGICCLOSE, | ||
272 | }; | ||
273 | |||
274 | /* Watchdog Core Ops */ | ||
275 | static struct watchdog_ops cdns_wdt_ops = { | ||
276 | .owner = THIS_MODULE, | ||
277 | .start = cdns_wdt_start, | ||
278 | .stop = cdns_wdt_stop, | ||
279 | .ping = cdns_wdt_reload, | ||
280 | .set_timeout = cdns_wdt_settimeout, | ||
281 | }; | ||
282 | |||
283 | /** | ||
284 | * cdns_wdt_notify_sys - Notifier for reboot or shutdown. | ||
285 | * | ||
286 | * @this: handle to notifier block | ||
287 | * @code: turn off indicator | ||
288 | * @unused: unused | ||
289 | * Return: NOTIFY_DONE | ||
290 | * | ||
291 | * This notifier is invoked whenever the system reboot or shutdown occur | ||
292 | * because we need to disable the WDT before system goes down as WDT might | ||
293 | * reset on the next boot. | ||
294 | */ | ||
295 | static int cdns_wdt_notify_sys(struct notifier_block *this, unsigned long code, | ||
296 | void *unused) | ||
297 | { | ||
298 | struct cdns_wdt *wdt = container_of(this, struct cdns_wdt, | ||
299 | cdns_wdt_notifier); | ||
300 | if (code == SYS_DOWN || code == SYS_HALT) | ||
301 | cdns_wdt_stop(&wdt->cdns_wdt_device); | ||
302 | |||
303 | return NOTIFY_DONE; | ||
304 | } | ||
305 | |||
306 | /************************Platform Operations*****************************/ | ||
307 | /** | ||
308 | * cdns_wdt_probe - Probe call for the device. | ||
309 | * | ||
310 | * @pdev: handle to the platform device structure. | ||
311 | * Return: 0 on success, negative error otherwise. | ||
312 | * | ||
313 | * It does all the memory allocation and registration for the device. | ||
314 | */ | ||
315 | static int cdns_wdt_probe(struct platform_device *pdev) | ||
316 | { | ||
317 | struct resource *res; | ||
318 | int ret, irq; | ||
319 | unsigned long clock_f; | ||
320 | struct cdns_wdt *wdt; | ||
321 | struct watchdog_device *cdns_wdt_device; | ||
322 | |||
323 | wdt = devm_kzalloc(&pdev->dev, sizeof(*wdt), GFP_KERNEL); | ||
324 | if (!wdt) | ||
325 | return -ENOMEM; | ||
326 | |||
327 | cdns_wdt_device = &wdt->cdns_wdt_device; | ||
328 | cdns_wdt_device->info = &cdns_wdt_info; | ||
329 | cdns_wdt_device->ops = &cdns_wdt_ops; | ||
330 | cdns_wdt_device->timeout = CDNS_WDT_DEFAULT_TIMEOUT; | ||
331 | cdns_wdt_device->min_timeout = CDNS_WDT_MIN_TIMEOUT; | ||
332 | cdns_wdt_device->max_timeout = CDNS_WDT_MAX_TIMEOUT; | ||
333 | |||
334 | res = platform_get_resource(pdev, IORESOURCE_MEM, 0); | ||
335 | wdt->regs = devm_ioremap_resource(&pdev->dev, res); | ||
336 | if (IS_ERR(wdt->regs)) | ||
337 | return PTR_ERR(wdt->regs); | ||
338 | |||
339 | /* Register the interrupt */ | ||
340 | wdt->rst = of_property_read_bool(pdev->dev.of_node, "reset-on-timeout"); | ||
341 | irq = platform_get_irq(pdev, 0); | ||
342 | if (!wdt->rst && irq >= 0) { | ||
343 | ret = devm_request_irq(&pdev->dev, irq, cdns_wdt_irq_handler, 0, | ||
344 | pdev->name, pdev); | ||
345 | if (ret) { | ||
346 | dev_err(&pdev->dev, | ||
347 | "cannot register interrupt handler err=%d\n", | ||
348 | ret); | ||
349 | return ret; | ||
350 | } | ||
351 | } | ||
352 | |||
353 | /* Initialize the members of cdns_wdt structure */ | ||
354 | cdns_wdt_device->parent = &pdev->dev; | ||
355 | |||
356 | ret = watchdog_init_timeout(cdns_wdt_device, wdt_timeout, &pdev->dev); | ||
357 | if (ret) { | ||
358 | dev_err(&pdev->dev, "unable to set timeout value\n"); | ||
359 | return ret; | ||
360 | } | ||
361 | |||
362 | watchdog_set_nowayout(cdns_wdt_device, nowayout); | ||
363 | watchdog_set_drvdata(cdns_wdt_device, wdt); | ||
364 | |||
365 | wdt->clk = devm_clk_get(&pdev->dev, NULL); | ||
366 | if (IS_ERR(wdt->clk)) { | ||
367 | dev_err(&pdev->dev, "input clock not found\n"); | ||
368 | ret = PTR_ERR(wdt->clk); | ||
369 | return ret; | ||
370 | } | ||
371 | |||
372 | ret = clk_prepare_enable(wdt->clk); | ||
373 | if (ret) { | ||
374 | dev_err(&pdev->dev, "unable to enable clock\n"); | ||
375 | return ret; | ||
376 | } | ||
377 | |||
378 | clock_f = clk_get_rate(wdt->clk); | ||
379 | if (clock_f <= CDNS_WDT_CLK_75MHZ) { | ||
380 | wdt->prescaler = CDNS_WDT_PRESCALE_512; | ||
381 | wdt->ctrl_clksel = CDNS_WDT_PRESCALE_SELECT_512; | ||
382 | } else { | ||
383 | wdt->prescaler = CDNS_WDT_PRESCALE_4096; | ||
384 | wdt->ctrl_clksel = CDNS_WDT_PRESCALE_SELECT_4096; | ||
385 | } | ||
386 | |||
387 | spin_lock_init(&wdt->io_lock); | ||
388 | |||
389 | wdt->cdns_wdt_notifier.notifier_call = &cdns_wdt_notify_sys; | ||
390 | ret = register_reboot_notifier(&wdt->cdns_wdt_notifier); | ||
391 | if (ret != 0) { | ||
392 | dev_err(&pdev->dev, "cannot register reboot notifier err=%d)\n", | ||
393 | ret); | ||
394 | goto err_clk_disable; | ||
395 | } | ||
396 | |||
397 | ret = watchdog_register_device(cdns_wdt_device); | ||
398 | if (ret) { | ||
399 | dev_err(&pdev->dev, "Failed to register wdt device\n"); | ||
400 | goto err_clk_disable; | ||
401 | } | ||
402 | platform_set_drvdata(pdev, wdt); | ||
403 | |||
404 | dev_dbg(&pdev->dev, "Xilinx Watchdog Timer at %p with timeout %ds%s\n", | ||
405 | wdt->regs, cdns_wdt_device->timeout, | ||
406 | nowayout ? ", nowayout" : ""); | ||
407 | |||
408 | return 0; | ||
409 | |||
410 | err_clk_disable: | ||
411 | clk_disable_unprepare(wdt->clk); | ||
412 | |||
413 | return ret; | ||
414 | } | ||
415 | |||
416 | /** | ||
417 | * cdns_wdt_remove - Probe call for the device. | ||
418 | * | ||
419 | * @pdev: handle to the platform device structure. | ||
420 | * Return: 0 on success, otherwise negative error. | ||
421 | * | ||
422 | * Unregister the device after releasing the resources. | ||
423 | */ | ||
424 | static int cdns_wdt_remove(struct platform_device *pdev) | ||
425 | { | ||
426 | struct cdns_wdt *wdt = platform_get_drvdata(pdev); | ||
427 | |||
428 | cdns_wdt_stop(&wdt->cdns_wdt_device); | ||
429 | watchdog_unregister_device(&wdt->cdns_wdt_device); | ||
430 | unregister_reboot_notifier(&wdt->cdns_wdt_notifier); | ||
431 | clk_disable_unprepare(wdt->clk); | ||
432 | |||
433 | return 0; | ||
434 | } | ||
435 | |||
436 | /** | ||
437 | * cdns_wdt_shutdown - Stop the device. | ||
438 | * | ||
439 | * @pdev: handle to the platform structure. | ||
440 | * | ||
441 | */ | ||
442 | static void cdns_wdt_shutdown(struct platform_device *pdev) | ||
443 | { | ||
444 | struct cdns_wdt *wdt = platform_get_drvdata(pdev); | ||
445 | |||
446 | cdns_wdt_stop(&wdt->cdns_wdt_device); | ||
447 | clk_disable_unprepare(wdt->clk); | ||
448 | } | ||
449 | |||
450 | /** | ||
451 | * cdns_wdt_suspend - Stop the device. | ||
452 | * | ||
453 | * @dev: handle to the device structure. | ||
454 | * Return: 0 always. | ||
455 | */ | ||
456 | static int __maybe_unused cdns_wdt_suspend(struct device *dev) | ||
457 | { | ||
458 | struct platform_device *pdev = container_of(dev, | ||
459 | struct platform_device, dev); | ||
460 | struct cdns_wdt *wdt = platform_get_drvdata(pdev); | ||
461 | |||
462 | cdns_wdt_stop(&wdt->cdns_wdt_device); | ||
463 | clk_disable_unprepare(wdt->clk); | ||
464 | |||
465 | return 0; | ||
466 | } | ||
467 | |||
468 | /** | ||
469 | * cdns_wdt_resume - Resume the device. | ||
470 | * | ||
471 | * @dev: handle to the device structure. | ||
472 | * Return: 0 on success, errno otherwise. | ||
473 | */ | ||
474 | static int __maybe_unused cdns_wdt_resume(struct device *dev) | ||
475 | { | ||
476 | int ret; | ||
477 | struct platform_device *pdev = container_of(dev, | ||
478 | struct platform_device, dev); | ||
479 | struct cdns_wdt *wdt = platform_get_drvdata(pdev); | ||
480 | |||
481 | ret = clk_prepare_enable(wdt->clk); | ||
482 | if (ret) { | ||
483 | dev_err(dev, "unable to enable clock\n"); | ||
484 | return ret; | ||
485 | } | ||
486 | cdns_wdt_start(&wdt->cdns_wdt_device); | ||
487 | |||
488 | return 0; | ||
489 | } | ||
490 | |||
491 | static SIMPLE_DEV_PM_OPS(cdns_wdt_pm_ops, cdns_wdt_suspend, cdns_wdt_resume); | ||
492 | |||
493 | static struct of_device_id cdns_wdt_of_match[] = { | ||
494 | { .compatible = "cdns,wdt-r1p2", }, | ||
495 | { /* end of table */ } | ||
496 | }; | ||
497 | MODULE_DEVICE_TABLE(of, cdns_wdt_of_match); | ||
498 | |||
499 | /* Driver Structure */ | ||
500 | static struct platform_driver cdns_wdt_driver = { | ||
501 | .probe = cdns_wdt_probe, | ||
502 | .remove = cdns_wdt_remove, | ||
503 | .shutdown = cdns_wdt_shutdown, | ||
504 | .driver = { | ||
505 | .name = "cdns-wdt", | ||
506 | .owner = THIS_MODULE, | ||
507 | .of_match_table = cdns_wdt_of_match, | ||
508 | .pm = &cdns_wdt_pm_ops, | ||
509 | }, | ||
510 | }; | ||
511 | |||
512 | module_platform_driver(cdns_wdt_driver); | ||
513 | |||
514 | MODULE_AUTHOR("Xilinx, Inc."); | ||
515 | MODULE_DESCRIPTION("Watchdog driver for Cadence WDT"); | ||
516 | MODULE_LICENSE("GPL"); | ||