diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2015-07-01 22:33:16 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2015-07-01 22:33:16 -0400 |
commit | 93899e39e86bfc021a190a9c26e8e516561f2756 (patch) | |
tree | c01fe48641d2a81acfa083748ef267a9607e84af /drivers/watchdog | |
parent | 5f1201d515819e7cfaaac3f0a30ff7b556261386 (diff) | |
parent | b2102eb36e7909c779e46f66595fda75aa219f4c (diff) |
Merge git://www.linux-watchdog.org/linux-watchdog
Pull watchdog updates from Wim Van Sebroeck:
"This contains:
- new driver for ST's LPC Watchdog
- new driver for Conexant Digicolor CX92755 SoC
- new driver for DA9062 watchdog
- Addition of the watchdog registration deferral mechanism
- several improvements on omap_wdt
- several improvements and reboot-support for imgpdc_wdt
- max63xx_wdt improvements
- imx2_wdt improvements
- dw_wdt improvements
- and other small improvements and fixes"
* git://www.linux-watchdog.org/linux-watchdog: (37 commits)
watchdog: omap_wdt: early_enable module parameter
watchdog: gpio_wdt: Add option for early registration
watchdog: watchdog_core: Add watchdog registration deferral mechanism
watchdog: max63xx: dynamically allocate device
watchdog: imx2_wdt: Disable previously acquired clock on error path
watchdog: imx2_wdt: Check for clk_prepare_enable() error
watchdog: hpwdt: Add support for WDIOC_SETOPTIONS
watchdog: docs: omap_wdt also understands nowayout
watchdog: omap_wdt: implement get_timeleft
watchdog: da9062: DA9062 watchdog driver
watchdog: imx2_wdt: set watchdog parent device
watchdog: mena21_wdt: Fix possible NULL pointer dereference
watchdog: dw_wdt: keepalive the watchdog at write time
watchdog: dw_wdt: No need for a spinlock
watchdog: imx2_wdt: also set wdog->timeout to new_timeout
watchdog: Allow compile test of GPIO consumers if !GPIOLIB
watchdog: cadence: Add dependency on HAS_IOMEM
watchdog: max63xx_wdt: Constify platform_device_id
watchdog: MAX63XX_WATCHDOG does not depend on ARM
watchdog: imgpdc: Add some documentation about the timeout
...
Diffstat (limited to 'drivers/watchdog')
-rw-r--r-- | drivers/watchdog/Kconfig | 36 | ||||
-rw-r--r-- | drivers/watchdog/Makefile | 2 | ||||
-rw-r--r-- | drivers/watchdog/at91sam9_wdt.c | 4 | ||||
-rw-r--r-- | drivers/watchdog/da9062_wdt.c | 253 | ||||
-rw-r--r-- | drivers/watchdog/digicolor_wdt.c | 205 | ||||
-rw-r--r-- | drivers/watchdog/dw_wdt.c | 8 | ||||
-rw-r--r-- | drivers/watchdog/gpio_wdt.c | 9 | ||||
-rw-r--r-- | drivers/watchdog/hpwdt.c | 16 | ||||
-rw-r--r-- | drivers/watchdog/imgpdc_wdt.c | 84 | ||||
-rw-r--r-- | drivers/watchdog/imx2_wdt.c | 18 | ||||
-rw-r--r-- | drivers/watchdog/max63xx_wdt.c | 172 | ||||
-rw-r--r-- | drivers/watchdog/mena21_wdt.c | 5 | ||||
-rw-r--r-- | drivers/watchdog/omap_wdt.c | 92 | ||||
-rw-r--r-- | drivers/watchdog/omap_wdt.h | 1 | ||||
-rw-r--r-- | drivers/watchdog/st_lpc_wdt.c | 2 | ||||
-rw-r--r-- | drivers/watchdog/watchdog_core.c | 118 |
16 files changed, 870 insertions, 155 deletions
diff --git a/drivers/watchdog/Kconfig b/drivers/watchdog/Kconfig index 262647bbc614..241fafde42cb 100644 --- a/drivers/watchdog/Kconfig +++ b/drivers/watchdog/Kconfig | |||
@@ -1,3 +1,4 @@ | |||
1 | |||
1 | # | 2 | # |
2 | # Watchdog device configuration | 3 | # Watchdog device configuration |
3 | # | 4 | # |
@@ -96,6 +97,15 @@ config DA9063_WATCHDOG | |||
96 | 97 | ||
97 | This driver can be built as a module. The module name is da9063_wdt. | 98 | This driver can be built as a module. The module name is da9063_wdt. |
98 | 99 | ||
100 | config DA9062_WATCHDOG | ||
101 | tristate "Dialog DA9062 Watchdog" | ||
102 | depends on MFD_DA9062 | ||
103 | select WATCHDOG_CORE | ||
104 | help | ||
105 | Support for the watchdog in the DA9062 PMIC. | ||
106 | |||
107 | This driver can be built as a module. The module name is da9062_wdt. | ||
108 | |||
99 | config GPIO_WATCHDOG | 109 | config GPIO_WATCHDOG |
100 | tristate "Watchdog device controlled through GPIO-line" | 110 | tristate "Watchdog device controlled through GPIO-line" |
101 | depends on OF_GPIO | 111 | depends on OF_GPIO |
@@ -104,6 +114,17 @@ config GPIO_WATCHDOG | |||
104 | If you say yes here you get support for watchdog device | 114 | If you say yes here you get support for watchdog device |
105 | controlled through GPIO-line. | 115 | controlled through GPIO-line. |
106 | 116 | ||
117 | config GPIO_WATCHDOG_ARCH_INITCALL | ||
118 | bool "Register the watchdog as early as possible" | ||
119 | depends on GPIO_WATCHDOG=y | ||
120 | help | ||
121 | In some situations, the default initcall level (module_init) | ||
122 | in not early enough in the boot process to avoid the watchdog | ||
123 | to be triggered. | ||
124 | If you say yes here, the initcall level would be raised to | ||
125 | arch_initcall. | ||
126 | If in doubt, say N. | ||
127 | |||
107 | config MENF21BMC_WATCHDOG | 128 | config MENF21BMC_WATCHDOG |
108 | tristate "MEN 14F021P00 BMC Watchdog" | 129 | tristate "MEN 14F021P00 BMC Watchdog" |
109 | depends on MFD_MENF21BMC | 130 | depends on MFD_MENF21BMC |
@@ -169,6 +190,7 @@ config AT91SAM9X_WATCHDOG | |||
169 | 190 | ||
170 | config CADENCE_WATCHDOG | 191 | config CADENCE_WATCHDOG |
171 | tristate "Cadence Watchdog Timer" | 192 | tristate "Cadence Watchdog Timer" |
193 | depends on HAS_IOMEM | ||
172 | select WATCHDOG_CORE | 194 | select WATCHDOG_CORE |
173 | help | 195 | help |
174 | Say Y here if you want to include support for the watchdog | 196 | Say Y here if you want to include support for the watchdog |
@@ -408,7 +430,7 @@ config TS72XX_WATCHDOG | |||
408 | 430 | ||
409 | config MAX63XX_WATCHDOG | 431 | config MAX63XX_WATCHDOG |
410 | tristate "Max63xx watchdog" | 432 | tristate "Max63xx watchdog" |
411 | depends on ARM && HAS_IOMEM | 433 | depends on HAS_IOMEM |
412 | select WATCHDOG_CORE | 434 | select WATCHDOG_CORE |
413 | help | 435 | help |
414 | Support for memory mapped max63{69,70,71,72,73,74} watchdog timer. | 436 | Support for memory mapped max63{69,70,71,72,73,74} watchdog timer. |
@@ -526,6 +548,16 @@ config MEDIATEK_WATCHDOG | |||
526 | To compile this driver as a module, choose M here: the | 548 | To compile this driver as a module, choose M here: the |
527 | module will be called mtk_wdt. | 549 | module will be called mtk_wdt. |
528 | 550 | ||
551 | config DIGICOLOR_WATCHDOG | ||
552 | tristate "Conexant Digicolor SoCs watchdog support" | ||
553 | depends on ARCH_DIGICOLOR | ||
554 | select WATCHDOG_CORE | ||
555 | help | ||
556 | Say Y here to include support for the watchdog timer | ||
557 | in Conexant Digicolor SoCs. | ||
558 | To compile this driver as a module, choose M here: the | ||
559 | module will be called digicolor_wdt. | ||
560 | |||
529 | # AVR32 Architecture | 561 | # AVR32 Architecture |
530 | 562 | ||
531 | config AT32AP700X_WDT | 563 | config AT32AP700X_WDT |
@@ -1355,7 +1387,7 @@ config BOOKE_WDT_DEFAULT_TIMEOUT | |||
1355 | config MEN_A21_WDT | 1387 | config MEN_A21_WDT |
1356 | tristate "MEN A21 VME CPU Carrier Board Watchdog Timer" | 1388 | tristate "MEN A21 VME CPU Carrier Board Watchdog Timer" |
1357 | select WATCHDOG_CORE | 1389 | select WATCHDOG_CORE |
1358 | depends on GPIOLIB | 1390 | depends on GPIOLIB || COMPILE_TEST |
1359 | help | 1391 | help |
1360 | Watchdog driver for MEN A21 VMEbus CPU Carrier Boards. | 1392 | Watchdog driver for MEN A21 VMEbus CPU Carrier Boards. |
1361 | 1393 | ||
diff --git a/drivers/watchdog/Makefile b/drivers/watchdog/Makefile index d98768c7d928..59ea9a1b8e76 100644 --- a/drivers/watchdog/Makefile +++ b/drivers/watchdog/Makefile | |||
@@ -65,6 +65,7 @@ obj-$(CONFIG_BCM_KONA_WDT) += bcm_kona_wdt.o | |||
65 | obj-$(CONFIG_TEGRA_WATCHDOG) += tegra_wdt.o | 65 | obj-$(CONFIG_TEGRA_WATCHDOG) += tegra_wdt.o |
66 | obj-$(CONFIG_MESON_WATCHDOG) += meson_wdt.o | 66 | obj-$(CONFIG_MESON_WATCHDOG) += meson_wdt.o |
67 | obj-$(CONFIG_MEDIATEK_WATCHDOG) += mtk_wdt.o | 67 | obj-$(CONFIG_MEDIATEK_WATCHDOG) += mtk_wdt.o |
68 | obj-$(CONFIG_DIGICOLOR_WATCHDOG) += digicolor_wdt.o | ||
68 | 69 | ||
69 | # AVR32 Architecture | 70 | # AVR32 Architecture |
70 | obj-$(CONFIG_AT32AP700X_WDT) += at32ap700x_wdt.o | 71 | obj-$(CONFIG_AT32AP700X_WDT) += at32ap700x_wdt.o |
@@ -180,6 +181,7 @@ obj-$(CONFIG_XEN_WDT) += xen_wdt.o | |||
180 | # Architecture Independent | 181 | # Architecture Independent |
181 | obj-$(CONFIG_DA9052_WATCHDOG) += da9052_wdt.o | 182 | obj-$(CONFIG_DA9052_WATCHDOG) += da9052_wdt.o |
182 | obj-$(CONFIG_DA9055_WATCHDOG) += da9055_wdt.o | 183 | obj-$(CONFIG_DA9055_WATCHDOG) += da9055_wdt.o |
184 | obj-$(CONFIG_DA9062_WATCHDOG) += da9062_wdt.o | ||
183 | obj-$(CONFIG_DA9063_WATCHDOG) += da9063_wdt.o | 185 | obj-$(CONFIG_DA9063_WATCHDOG) += da9063_wdt.o |
184 | obj-$(CONFIG_GPIO_WATCHDOG) += gpio_wdt.o | 186 | obj-$(CONFIG_GPIO_WATCHDOG) += gpio_wdt.o |
185 | obj-$(CONFIG_WM831X_WATCHDOG) += wm831x_wdt.o | 187 | obj-$(CONFIG_WM831X_WATCHDOG) += wm831x_wdt.o |
diff --git a/drivers/watchdog/at91sam9_wdt.c b/drivers/watchdog/at91sam9_wdt.c index 1443b3c391de..e4698f7c5f93 100644 --- a/drivers/watchdog/at91sam9_wdt.c +++ b/drivers/watchdog/at91sam9_wdt.c | |||
@@ -40,9 +40,9 @@ | |||
40 | #define DRV_NAME "AT91SAM9 Watchdog" | 40 | #define DRV_NAME "AT91SAM9 Watchdog" |
41 | 41 | ||
42 | #define wdt_read(wdt, field) \ | 42 | #define wdt_read(wdt, field) \ |
43 | __raw_readl((wdt)->base + (field)) | 43 | readl_relaxed((wdt)->base + (field)) |
44 | #define wdt_write(wtd, field, val) \ | 44 | #define wdt_write(wtd, field, val) \ |
45 | __raw_writel((val), (wdt)->base + (field)) | 45 | writel_relaxed((val), (wdt)->base + (field)) |
46 | 46 | ||
47 | /* AT91SAM9 watchdog runs a 12bit counter @ 256Hz, | 47 | /* AT91SAM9 watchdog runs a 12bit counter @ 256Hz, |
48 | * use this to convert a watchdog | 48 | * use this to convert a watchdog |
diff --git a/drivers/watchdog/da9062_wdt.c b/drivers/watchdog/da9062_wdt.c new file mode 100644 index 000000000000..b3a870ce85be --- /dev/null +++ b/drivers/watchdog/da9062_wdt.c | |||
@@ -0,0 +1,253 @@ | |||
1 | /* | ||
2 | * da9062_wdt.c - WDT device driver for DA9062 | ||
3 | * Copyright (C) 2015 Dialog Semiconductor Ltd. | ||
4 | * | ||
5 | * This program is free software; you can redistribute it and/or | ||
6 | * modify it under the terms of the GNU General Public License | ||
7 | * as published by the Free Software Foundation; either version 2 | ||
8 | * of the License, or (at your option) any later version. | ||
9 | * | ||
10 | * This program is distributed in the hope that it will be useful, | ||
11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
13 | * GNU General Public License for more details. | ||
14 | */ | ||
15 | |||
16 | #include <linux/kernel.h> | ||
17 | #include <linux/module.h> | ||
18 | #include <linux/watchdog.h> | ||
19 | #include <linux/platform_device.h> | ||
20 | #include <linux/uaccess.h> | ||
21 | #include <linux/slab.h> | ||
22 | #include <linux/delay.h> | ||
23 | #include <linux/jiffies.h> | ||
24 | #include <linux/mfd/da9062/registers.h> | ||
25 | #include <linux/mfd/da9062/core.h> | ||
26 | #include <linux/regmap.h> | ||
27 | #include <linux/of.h> | ||
28 | |||
29 | static const unsigned int wdt_timeout[] = { 0, 2, 4, 8, 16, 32, 65, 131 }; | ||
30 | #define DA9062_TWDSCALE_DISABLE 0 | ||
31 | #define DA9062_TWDSCALE_MIN 1 | ||
32 | #define DA9062_TWDSCALE_MAX (ARRAY_SIZE(wdt_timeout) - 1) | ||
33 | #define DA9062_WDT_MIN_TIMEOUT wdt_timeout[DA9062_TWDSCALE_MIN] | ||
34 | #define DA9062_WDT_MAX_TIMEOUT wdt_timeout[DA9062_TWDSCALE_MAX] | ||
35 | #define DA9062_WDG_DEFAULT_TIMEOUT wdt_timeout[DA9062_TWDSCALE_MAX-1] | ||
36 | #define DA9062_RESET_PROTECTION_MS 300 | ||
37 | |||
38 | struct da9062_watchdog { | ||
39 | struct da9062 *hw; | ||
40 | struct watchdog_device wdtdev; | ||
41 | unsigned long j_time_stamp; | ||
42 | }; | ||
43 | |||
44 | static void da9062_set_window_start(struct da9062_watchdog *wdt) | ||
45 | { | ||
46 | wdt->j_time_stamp = jiffies; | ||
47 | } | ||
48 | |||
49 | static void da9062_apply_window_protection(struct da9062_watchdog *wdt) | ||
50 | { | ||
51 | unsigned long delay = msecs_to_jiffies(DA9062_RESET_PROTECTION_MS); | ||
52 | unsigned long timeout = wdt->j_time_stamp + delay; | ||
53 | unsigned long now = jiffies; | ||
54 | unsigned int diff_ms; | ||
55 | |||
56 | /* if time-limit has not elapsed then wait for remainder */ | ||
57 | if (time_before(now, timeout)) { | ||
58 | diff_ms = jiffies_to_msecs(timeout-now); | ||
59 | dev_dbg(wdt->hw->dev, | ||
60 | "Kicked too quickly. Delaying %u msecs\n", diff_ms); | ||
61 | msleep(diff_ms); | ||
62 | } | ||
63 | } | ||
64 | |||
65 | static unsigned int da9062_wdt_timeout_to_sel(unsigned int secs) | ||
66 | { | ||
67 | unsigned int i; | ||
68 | |||
69 | for (i = DA9062_TWDSCALE_MIN; i <= DA9062_TWDSCALE_MAX; i++) { | ||
70 | if (wdt_timeout[i] >= secs) | ||
71 | return i; | ||
72 | } | ||
73 | |||
74 | return DA9062_TWDSCALE_MAX; | ||
75 | } | ||
76 | |||
77 | static int da9062_reset_watchdog_timer(struct da9062_watchdog *wdt) | ||
78 | { | ||
79 | int ret; | ||
80 | |||
81 | da9062_apply_window_protection(wdt); | ||
82 | |||
83 | ret = regmap_update_bits(wdt->hw->regmap, | ||
84 | DA9062AA_CONTROL_F, | ||
85 | DA9062AA_WATCHDOG_MASK, | ||
86 | DA9062AA_WATCHDOG_MASK); | ||
87 | |||
88 | da9062_set_window_start(wdt); | ||
89 | |||
90 | return ret; | ||
91 | } | ||
92 | |||
93 | static int da9062_wdt_update_timeout_register(struct da9062_watchdog *wdt, | ||
94 | unsigned int regval) | ||
95 | { | ||
96 | struct da9062 *chip = wdt->hw; | ||
97 | int ret; | ||
98 | |||
99 | ret = da9062_reset_watchdog_timer(wdt); | ||
100 | if (ret) | ||
101 | return ret; | ||
102 | |||
103 | return regmap_update_bits(chip->regmap, | ||
104 | DA9062AA_CONTROL_D, | ||
105 | DA9062AA_TWDSCALE_MASK, | ||
106 | regval); | ||
107 | } | ||
108 | |||
109 | static int da9062_wdt_start(struct watchdog_device *wdd) | ||
110 | { | ||
111 | struct da9062_watchdog *wdt = watchdog_get_drvdata(wdd); | ||
112 | unsigned int selector; | ||
113 | int ret; | ||
114 | |||
115 | selector = da9062_wdt_timeout_to_sel(wdt->wdtdev.timeout); | ||
116 | ret = da9062_wdt_update_timeout_register(wdt, selector); | ||
117 | if (ret) | ||
118 | dev_err(wdt->hw->dev, "Watchdog failed to start (err = %d)\n", | ||
119 | ret); | ||
120 | |||
121 | return ret; | ||
122 | } | ||
123 | |||
124 | static int da9062_wdt_stop(struct watchdog_device *wdd) | ||
125 | { | ||
126 | struct da9062_watchdog *wdt = watchdog_get_drvdata(wdd); | ||
127 | int ret; | ||
128 | |||
129 | ret = da9062_reset_watchdog_timer(wdt); | ||
130 | if (ret) { | ||
131 | dev_err(wdt->hw->dev, "Failed to ping the watchdog (err = %d)\n", | ||
132 | ret); | ||
133 | return ret; | ||
134 | } | ||
135 | |||
136 | ret = regmap_update_bits(wdt->hw->regmap, | ||
137 | DA9062AA_CONTROL_D, | ||
138 | DA9062AA_TWDSCALE_MASK, | ||
139 | DA9062_TWDSCALE_DISABLE); | ||
140 | if (ret) | ||
141 | dev_err(wdt->hw->dev, "Watchdog failed to stop (err = %d)\n", | ||
142 | ret); | ||
143 | |||
144 | return ret; | ||
145 | } | ||
146 | |||
147 | static int da9062_wdt_ping(struct watchdog_device *wdd) | ||
148 | { | ||
149 | struct da9062_watchdog *wdt = watchdog_get_drvdata(wdd); | ||
150 | int ret; | ||
151 | |||
152 | ret = da9062_reset_watchdog_timer(wdt); | ||
153 | if (ret) | ||
154 | dev_err(wdt->hw->dev, "Failed to ping the watchdog (err = %d)\n", | ||
155 | ret); | ||
156 | |||
157 | return ret; | ||
158 | } | ||
159 | |||
160 | static int da9062_wdt_set_timeout(struct watchdog_device *wdd, | ||
161 | unsigned int timeout) | ||
162 | { | ||
163 | struct da9062_watchdog *wdt = watchdog_get_drvdata(wdd); | ||
164 | unsigned int selector; | ||
165 | int ret; | ||
166 | |||
167 | selector = da9062_wdt_timeout_to_sel(timeout); | ||
168 | ret = da9062_wdt_update_timeout_register(wdt, selector); | ||
169 | if (ret) | ||
170 | dev_err(wdt->hw->dev, "Failed to set watchdog timeout (err = %d)\n", | ||
171 | ret); | ||
172 | else | ||
173 | wdd->timeout = wdt_timeout[selector]; | ||
174 | |||
175 | return ret; | ||
176 | } | ||
177 | |||
178 | static const struct watchdog_info da9062_watchdog_info = { | ||
179 | .options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING, | ||
180 | .identity = "DA9062 WDT", | ||
181 | }; | ||
182 | |||
183 | static const struct watchdog_ops da9062_watchdog_ops = { | ||
184 | .owner = THIS_MODULE, | ||
185 | .start = da9062_wdt_start, | ||
186 | .stop = da9062_wdt_stop, | ||
187 | .ping = da9062_wdt_ping, | ||
188 | .set_timeout = da9062_wdt_set_timeout, | ||
189 | }; | ||
190 | |||
191 | static int da9062_wdt_probe(struct platform_device *pdev) | ||
192 | { | ||
193 | int ret; | ||
194 | struct da9062 *chip; | ||
195 | struct da9062_watchdog *wdt; | ||
196 | |||
197 | chip = dev_get_drvdata(pdev->dev.parent); | ||
198 | if (!chip) | ||
199 | return -EINVAL; | ||
200 | |||
201 | wdt = devm_kzalloc(&pdev->dev, sizeof(*wdt), GFP_KERNEL); | ||
202 | if (!wdt) | ||
203 | return -ENOMEM; | ||
204 | |||
205 | wdt->hw = chip; | ||
206 | |||
207 | wdt->wdtdev.info = &da9062_watchdog_info; | ||
208 | wdt->wdtdev.ops = &da9062_watchdog_ops; | ||
209 | wdt->wdtdev.min_timeout = DA9062_WDT_MIN_TIMEOUT; | ||
210 | wdt->wdtdev.max_timeout = DA9062_WDT_MAX_TIMEOUT; | ||
211 | wdt->wdtdev.timeout = DA9062_WDG_DEFAULT_TIMEOUT; | ||
212 | wdt->wdtdev.status = WATCHDOG_NOWAYOUT_INIT_STATUS; | ||
213 | |||
214 | watchdog_set_drvdata(&wdt->wdtdev, wdt); | ||
215 | dev_set_drvdata(&pdev->dev, wdt); | ||
216 | |||
217 | ret = watchdog_register_device(&wdt->wdtdev); | ||
218 | if (ret < 0) { | ||
219 | dev_err(wdt->hw->dev, | ||
220 | "watchdog registration failed (%d)\n", ret); | ||
221 | return ret; | ||
222 | } | ||
223 | |||
224 | da9062_set_window_start(wdt); | ||
225 | |||
226 | ret = da9062_wdt_ping(&wdt->wdtdev); | ||
227 | if (ret < 0) | ||
228 | watchdog_unregister_device(&wdt->wdtdev); | ||
229 | |||
230 | return ret; | ||
231 | } | ||
232 | |||
233 | static int da9062_wdt_remove(struct platform_device *pdev) | ||
234 | { | ||
235 | struct da9062_watchdog *wdt = dev_get_drvdata(&pdev->dev); | ||
236 | |||
237 | watchdog_unregister_device(&wdt->wdtdev); | ||
238 | return 0; | ||
239 | } | ||
240 | |||
241 | static struct platform_driver da9062_wdt_driver = { | ||
242 | .probe = da9062_wdt_probe, | ||
243 | .remove = da9062_wdt_remove, | ||
244 | .driver = { | ||
245 | .name = "da9062-watchdog", | ||
246 | }, | ||
247 | }; | ||
248 | module_platform_driver(da9062_wdt_driver); | ||
249 | |||
250 | MODULE_AUTHOR("S Twiss <stwiss.opensource@diasemi.com>"); | ||
251 | MODULE_DESCRIPTION("WDT device driver for Dialog DA9062"); | ||
252 | MODULE_LICENSE("GPL"); | ||
253 | MODULE_ALIAS("platform:da9062-watchdog"); | ||
diff --git a/drivers/watchdog/digicolor_wdt.c b/drivers/watchdog/digicolor_wdt.c new file mode 100644 index 000000000000..31d8e4936611 --- /dev/null +++ b/drivers/watchdog/digicolor_wdt.c | |||
@@ -0,0 +1,205 @@ | |||
1 | /* | ||
2 | * Watchdog driver for Conexant Digicolor | ||
3 | * | ||
4 | * Copyright (C) 2015 Paradox Innovation Ltd. | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or modify it | ||
7 | * under the terms of the GNU General Public License as published by the | ||
8 | * Free Software Foundation; either version 2 of the License, or (at your | ||
9 | * option) any later version. | ||
10 | */ | ||
11 | |||
12 | #include <linux/types.h> | ||
13 | #include <linux/module.h> | ||
14 | #include <linux/io.h> | ||
15 | #include <linux/delay.h> | ||
16 | #include <linux/clk.h> | ||
17 | #include <linux/watchdog.h> | ||
18 | #include <linux/reboot.h> | ||
19 | #include <linux/platform_device.h> | ||
20 | #include <linux/of_address.h> | ||
21 | |||
22 | #define TIMER_A_CONTROL 0 | ||
23 | #define TIMER_A_COUNT 4 | ||
24 | |||
25 | #define TIMER_A_ENABLE_COUNT BIT(0) | ||
26 | #define TIMER_A_ENABLE_WATCHDOG BIT(1) | ||
27 | |||
28 | struct dc_wdt { | ||
29 | void __iomem *base; | ||
30 | struct clk *clk; | ||
31 | struct notifier_block restart_handler; | ||
32 | spinlock_t lock; | ||
33 | }; | ||
34 | |||
35 | static unsigned timeout; | ||
36 | module_param(timeout, uint, 0); | ||
37 | MODULE_PARM_DESC(timeout, "Watchdog timeout in seconds"); | ||
38 | |||
39 | static void dc_wdt_set(struct dc_wdt *wdt, u32 ticks) | ||
40 | { | ||
41 | unsigned long flags; | ||
42 | |||
43 | spin_lock_irqsave(&wdt->lock, flags); | ||
44 | |||
45 | writel_relaxed(0, wdt->base + TIMER_A_CONTROL); | ||
46 | writel_relaxed(ticks, wdt->base + TIMER_A_COUNT); | ||
47 | writel_relaxed(TIMER_A_ENABLE_COUNT | TIMER_A_ENABLE_WATCHDOG, | ||
48 | wdt->base + TIMER_A_CONTROL); | ||
49 | |||
50 | spin_unlock_irqrestore(&wdt->lock, flags); | ||
51 | } | ||
52 | |||
53 | static int dc_restart_handler(struct notifier_block *this, unsigned long mode, | ||
54 | void *cmd) | ||
55 | { | ||
56 | struct dc_wdt *wdt = container_of(this, struct dc_wdt, restart_handler); | ||
57 | |||
58 | dc_wdt_set(wdt, 1); | ||
59 | /* wait for reset to assert... */ | ||
60 | mdelay(500); | ||
61 | |||
62 | return NOTIFY_DONE; | ||
63 | } | ||
64 | |||
65 | static int dc_wdt_start(struct watchdog_device *wdog) | ||
66 | { | ||
67 | struct dc_wdt *wdt = watchdog_get_drvdata(wdog); | ||
68 | |||
69 | dc_wdt_set(wdt, wdog->timeout * clk_get_rate(wdt->clk)); | ||
70 | |||
71 | return 0; | ||
72 | } | ||
73 | |||
74 | static int dc_wdt_stop(struct watchdog_device *wdog) | ||
75 | { | ||
76 | struct dc_wdt *wdt = watchdog_get_drvdata(wdog); | ||
77 | |||
78 | writel_relaxed(0, wdt->base + TIMER_A_CONTROL); | ||
79 | |||
80 | return 0; | ||
81 | } | ||
82 | |||
83 | static int dc_wdt_set_timeout(struct watchdog_device *wdog, unsigned int t) | ||
84 | { | ||
85 | struct dc_wdt *wdt = watchdog_get_drvdata(wdog); | ||
86 | |||
87 | dc_wdt_set(wdt, t * clk_get_rate(wdt->clk)); | ||
88 | wdog->timeout = t; | ||
89 | |||
90 | return 0; | ||
91 | } | ||
92 | |||
93 | static unsigned int dc_wdt_get_timeleft(struct watchdog_device *wdog) | ||
94 | { | ||
95 | struct dc_wdt *wdt = watchdog_get_drvdata(wdog); | ||
96 | uint32_t count = readl_relaxed(wdt->base + TIMER_A_COUNT); | ||
97 | |||
98 | return count / clk_get_rate(wdt->clk); | ||
99 | } | ||
100 | |||
101 | static struct watchdog_ops dc_wdt_ops = { | ||
102 | .owner = THIS_MODULE, | ||
103 | .start = dc_wdt_start, | ||
104 | .stop = dc_wdt_stop, | ||
105 | .set_timeout = dc_wdt_set_timeout, | ||
106 | .get_timeleft = dc_wdt_get_timeleft, | ||
107 | }; | ||
108 | |||
109 | static struct watchdog_info dc_wdt_info = { | ||
110 | .options = WDIOF_SETTIMEOUT | WDIOF_MAGICCLOSE | ||
111 | | WDIOF_KEEPALIVEPING, | ||
112 | .identity = "Conexant Digicolor Watchdog", | ||
113 | }; | ||
114 | |||
115 | static struct watchdog_device dc_wdt_wdd = { | ||
116 | .info = &dc_wdt_info, | ||
117 | .ops = &dc_wdt_ops, | ||
118 | .min_timeout = 1, | ||
119 | }; | ||
120 | |||
121 | static int dc_wdt_probe(struct platform_device *pdev) | ||
122 | { | ||
123 | struct device *dev = &pdev->dev; | ||
124 | struct device_node *np = dev->of_node; | ||
125 | struct dc_wdt *wdt; | ||
126 | int ret; | ||
127 | |||
128 | wdt = devm_kzalloc(dev, sizeof(struct dc_wdt), GFP_KERNEL); | ||
129 | if (!wdt) | ||
130 | return -ENOMEM; | ||
131 | platform_set_drvdata(pdev, wdt); | ||
132 | |||
133 | wdt->base = of_iomap(np, 0); | ||
134 | if (!wdt->base) { | ||
135 | dev_err(dev, "Failed to remap watchdog regs"); | ||
136 | return -ENODEV; | ||
137 | } | ||
138 | |||
139 | wdt->clk = devm_clk_get(&pdev->dev, NULL); | ||
140 | if (IS_ERR(wdt->clk)) { | ||
141 | ret = PTR_ERR(wdt->clk); | ||
142 | goto err_iounmap; | ||
143 | } | ||
144 | dc_wdt_wdd.max_timeout = U32_MAX / clk_get_rate(wdt->clk); | ||
145 | dc_wdt_wdd.timeout = dc_wdt_wdd.max_timeout; | ||
146 | |||
147 | spin_lock_init(&wdt->lock); | ||
148 | |||
149 | watchdog_set_drvdata(&dc_wdt_wdd, wdt); | ||
150 | watchdog_init_timeout(&dc_wdt_wdd, timeout, dev); | ||
151 | ret = watchdog_register_device(&dc_wdt_wdd); | ||
152 | if (ret) { | ||
153 | dev_err(dev, "Failed to register watchdog device"); | ||
154 | goto err_iounmap; | ||
155 | } | ||
156 | |||
157 | wdt->restart_handler.notifier_call = dc_restart_handler; | ||
158 | wdt->restart_handler.priority = 128; | ||
159 | ret = register_restart_handler(&wdt->restart_handler); | ||
160 | if (ret) | ||
161 | dev_warn(&pdev->dev, "cannot register restart handler\n"); | ||
162 | |||
163 | return 0; | ||
164 | |||
165 | err_iounmap: | ||
166 | iounmap(wdt->base); | ||
167 | return ret; | ||
168 | } | ||
169 | |||
170 | static int dc_wdt_remove(struct platform_device *pdev) | ||
171 | { | ||
172 | struct dc_wdt *wdt = platform_get_drvdata(pdev); | ||
173 | |||
174 | unregister_restart_handler(&wdt->restart_handler); | ||
175 | watchdog_unregister_device(&dc_wdt_wdd); | ||
176 | iounmap(wdt->base); | ||
177 | |||
178 | return 0; | ||
179 | } | ||
180 | |||
181 | static void dc_wdt_shutdown(struct platform_device *pdev) | ||
182 | { | ||
183 | dc_wdt_stop(&dc_wdt_wdd); | ||
184 | } | ||
185 | |||
186 | static const struct of_device_id dc_wdt_of_match[] = { | ||
187 | { .compatible = "cnxt,cx92755-wdt", }, | ||
188 | {}, | ||
189 | }; | ||
190 | MODULE_DEVICE_TABLE(of, dc_wdt_of_match); | ||
191 | |||
192 | static struct platform_driver dc_wdt_driver = { | ||
193 | .probe = dc_wdt_probe, | ||
194 | .remove = dc_wdt_remove, | ||
195 | .shutdown = dc_wdt_shutdown, | ||
196 | .driver = { | ||
197 | .name = "digicolor-wdt", | ||
198 | .of_match_table = dc_wdt_of_match, | ||
199 | }, | ||
200 | }; | ||
201 | module_platform_driver(dc_wdt_driver); | ||
202 | |||
203 | MODULE_AUTHOR("Baruch Siach <baruch@tkos.co.il>"); | ||
204 | MODULE_DESCRIPTION("Driver for Conexant Digicolor watchdog timer"); | ||
205 | MODULE_LICENSE("GPL"); | ||
diff --git a/drivers/watchdog/dw_wdt.c b/drivers/watchdog/dw_wdt.c index d0bb9499d12c..6ea0634345e9 100644 --- a/drivers/watchdog/dw_wdt.c +++ b/drivers/watchdog/dw_wdt.c | |||
@@ -35,7 +35,6 @@ | |||
35 | #include <linux/pm.h> | 35 | #include <linux/pm.h> |
36 | #include <linux/platform_device.h> | 36 | #include <linux/platform_device.h> |
37 | #include <linux/reboot.h> | 37 | #include <linux/reboot.h> |
38 | #include <linux/spinlock.h> | ||
39 | #include <linux/timer.h> | 38 | #include <linux/timer.h> |
40 | #include <linux/uaccess.h> | 39 | #include <linux/uaccess.h> |
41 | #include <linux/watchdog.h> | 40 | #include <linux/watchdog.h> |
@@ -61,7 +60,6 @@ MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started " | |||
61 | #define WDT_TIMEOUT (HZ / 2) | 60 | #define WDT_TIMEOUT (HZ / 2) |
62 | 61 | ||
63 | static struct { | 62 | static struct { |
64 | spinlock_t lock; | ||
65 | void __iomem *regs; | 63 | void __iomem *regs; |
66 | struct clk *clk; | 64 | struct clk *clk; |
67 | unsigned long in_use; | 65 | unsigned long in_use; |
@@ -177,7 +175,6 @@ static int dw_wdt_open(struct inode *inode, struct file *filp) | |||
177 | /* Make sure we don't get unloaded. */ | 175 | /* Make sure we don't get unloaded. */ |
178 | __module_get(THIS_MODULE); | 176 | __module_get(THIS_MODULE); |
179 | 177 | ||
180 | spin_lock(&dw_wdt.lock); | ||
181 | if (!dw_wdt_is_enabled()) { | 178 | if (!dw_wdt_is_enabled()) { |
182 | /* | 179 | /* |
183 | * The watchdog is not currently enabled. Set the timeout to | 180 | * The watchdog is not currently enabled. Set the timeout to |
@@ -190,8 +187,6 @@ static int dw_wdt_open(struct inode *inode, struct file *filp) | |||
190 | 187 | ||
191 | dw_wdt_set_next_heartbeat(); | 188 | dw_wdt_set_next_heartbeat(); |
192 | 189 | ||
193 | spin_unlock(&dw_wdt.lock); | ||
194 | |||
195 | return nonseekable_open(inode, filp); | 190 | return nonseekable_open(inode, filp); |
196 | } | 191 | } |
197 | 192 | ||
@@ -220,6 +215,7 @@ static ssize_t dw_wdt_write(struct file *filp, const char __user *buf, | |||
220 | } | 215 | } |
221 | 216 | ||
222 | dw_wdt_set_next_heartbeat(); | 217 | dw_wdt_set_next_heartbeat(); |
218 | dw_wdt_keepalive(); | ||
223 | mod_timer(&dw_wdt.timer, jiffies + WDT_TIMEOUT); | 219 | mod_timer(&dw_wdt.timer, jiffies + WDT_TIMEOUT); |
224 | 220 | ||
225 | return len; | 221 | return len; |
@@ -348,8 +344,6 @@ static int dw_wdt_drv_probe(struct platform_device *pdev) | |||
348 | if (ret) | 344 | if (ret) |
349 | return ret; | 345 | return ret; |
350 | 346 | ||
351 | spin_lock_init(&dw_wdt.lock); | ||
352 | |||
353 | ret = misc_register(&dw_wdt_miscdev); | 347 | ret = misc_register(&dw_wdt_miscdev); |
354 | if (ret) | 348 | if (ret) |
355 | goto out_disable_clk; | 349 | goto out_disable_clk; |
diff --git a/drivers/watchdog/gpio_wdt.c b/drivers/watchdog/gpio_wdt.c index cbc313d37c59..1687cc2d7122 100644 --- a/drivers/watchdog/gpio_wdt.c +++ b/drivers/watchdog/gpio_wdt.c | |||
@@ -267,7 +267,16 @@ static struct platform_driver gpio_wdt_driver = { | |||
267 | .probe = gpio_wdt_probe, | 267 | .probe = gpio_wdt_probe, |
268 | .remove = gpio_wdt_remove, | 268 | .remove = gpio_wdt_remove, |
269 | }; | 269 | }; |
270 | |||
271 | #ifdef CONFIG_GPIO_WATCHDOG_ARCH_INITCALL | ||
272 | static int __init gpio_wdt_init(void) | ||
273 | { | ||
274 | return platform_driver_register(&gpio_wdt_driver); | ||
275 | } | ||
276 | arch_initcall(gpio_wdt_init); | ||
277 | #else | ||
270 | module_platform_driver(gpio_wdt_driver); | 278 | module_platform_driver(gpio_wdt_driver); |
279 | #endif | ||
271 | 280 | ||
272 | MODULE_AUTHOR("Alexander Shiyan <shc_work@mail.ru>"); | 281 | MODULE_AUTHOR("Alexander Shiyan <shc_work@mail.ru>"); |
273 | MODULE_DESCRIPTION("GPIO Watchdog"); | 282 | MODULE_DESCRIPTION("GPIO Watchdog"); |
diff --git a/drivers/watchdog/hpwdt.c b/drivers/watchdog/hpwdt.c index ada3e44f9932..286369d4f0f5 100644 --- a/drivers/watchdog/hpwdt.c +++ b/drivers/watchdog/hpwdt.c | |||
@@ -588,7 +588,7 @@ static long hpwdt_ioctl(struct file *file, unsigned int cmd, | |||
588 | { | 588 | { |
589 | void __user *argp = (void __user *)arg; | 589 | void __user *argp = (void __user *)arg; |
590 | int __user *p = argp; | 590 | int __user *p = argp; |
591 | int new_margin; | 591 | int new_margin, options; |
592 | int ret = -ENOTTY; | 592 | int ret = -ENOTTY; |
593 | 593 | ||
594 | switch (cmd) { | 594 | switch (cmd) { |
@@ -608,6 +608,20 @@ static long hpwdt_ioctl(struct file *file, unsigned int cmd, | |||
608 | ret = 0; | 608 | ret = 0; |
609 | break; | 609 | break; |
610 | 610 | ||
611 | case WDIOC_SETOPTIONS: | ||
612 | ret = get_user(options, p); | ||
613 | if (ret) | ||
614 | break; | ||
615 | |||
616 | if (options & WDIOS_DISABLECARD) | ||
617 | hpwdt_stop(); | ||
618 | |||
619 | if (options & WDIOS_ENABLECARD) { | ||
620 | hpwdt_start(); | ||
621 | hpwdt_ping(); | ||
622 | } | ||
623 | break; | ||
624 | |||
611 | case WDIOC_SETTIMEOUT: | 625 | case WDIOC_SETTIMEOUT: |
612 | ret = get_user(new_margin, p); | 626 | ret = get_user(new_margin, p); |
613 | if (ret) | 627 | if (ret) |
diff --git a/drivers/watchdog/imgpdc_wdt.c b/drivers/watchdog/imgpdc_wdt.c index 0deaa4f971f5..0f73621827ab 100644 --- a/drivers/watchdog/imgpdc_wdt.c +++ b/drivers/watchdog/imgpdc_wdt.c | |||
@@ -9,6 +9,35 @@ | |||
9 | * | 9 | * |
10 | * Based on drivers/watchdog/sunxi_wdt.c Copyright (c) 2013 Carlo Caione | 10 | * Based on drivers/watchdog/sunxi_wdt.c Copyright (c) 2013 Carlo Caione |
11 | * 2012 Henrik Nordstrom | 11 | * 2012 Henrik Nordstrom |
12 | * | ||
13 | * Notes | ||
14 | * ----- | ||
15 | * The timeout value is rounded to the next power of two clock cycles. | ||
16 | * This is configured using the PDC_WDT_CONFIG register, according to this | ||
17 | * formula: | ||
18 | * | ||
19 | * timeout = 2^(delay + 1) clock cycles | ||
20 | * | ||
21 | * Where 'delay' is the value written in PDC_WDT_CONFIG register. | ||
22 | * | ||
23 | * Therefore, the hardware only allows to program watchdog timeouts, expressed | ||
24 | * as a power of two number of watchdog clock cycles. The current implementation | ||
25 | * guarantees that the actual watchdog timeout will be _at least_ the value | ||
26 | * programmed in the imgpdg_wdt driver. | ||
27 | * | ||
28 | * The following table shows how the user-configured timeout relates | ||
29 | * to the actual hardware timeout (watchdog clock @ 40000 Hz): | ||
30 | * | ||
31 | * input timeout | WD_DELAY | actual timeout | ||
32 | * ----------------------------------- | ||
33 | * 10 | 18 | 13 seconds | ||
34 | * 20 | 19 | 26 seconds | ||
35 | * 30 | 20 | 52 seconds | ||
36 | * 60 | 21 | 104 seconds | ||
37 | * | ||
38 | * Albeit coarse, this granularity would suffice most watchdog uses. | ||
39 | * If the platform allows it, the user should be able to change the watchdog | ||
40 | * clock rate and achieve a finer timeout granularity. | ||
12 | */ | 41 | */ |
13 | 42 | ||
14 | #include <linux/clk.h> | 43 | #include <linux/clk.h> |
@@ -16,6 +45,7 @@ | |||
16 | #include <linux/log2.h> | 45 | #include <linux/log2.h> |
17 | #include <linux/module.h> | 46 | #include <linux/module.h> |
18 | #include <linux/platform_device.h> | 47 | #include <linux/platform_device.h> |
48 | #include <linux/reboot.h> | ||
19 | #include <linux/slab.h> | 49 | #include <linux/slab.h> |
20 | #include <linux/watchdog.h> | 50 | #include <linux/watchdog.h> |
21 | 51 | ||
@@ -42,7 +72,7 @@ | |||
42 | #define PDC_WDT_MIN_TIMEOUT 1 | 72 | #define PDC_WDT_MIN_TIMEOUT 1 |
43 | #define PDC_WDT_DEF_TIMEOUT 64 | 73 | #define PDC_WDT_DEF_TIMEOUT 64 |
44 | 74 | ||
45 | static int heartbeat = PDC_WDT_DEF_TIMEOUT; | 75 | static int heartbeat; |
46 | module_param(heartbeat, int, 0); | 76 | module_param(heartbeat, int, 0); |
47 | MODULE_PARM_DESC(heartbeat, "Watchdog heartbeats in seconds " | 77 | MODULE_PARM_DESC(heartbeat, "Watchdog heartbeats in seconds " |
48 | "(default=" __MODULE_STRING(PDC_WDT_DEF_TIMEOUT) ")"); | 78 | "(default=" __MODULE_STRING(PDC_WDT_DEF_TIMEOUT) ")"); |
@@ -57,6 +87,7 @@ struct pdc_wdt_dev { | |||
57 | struct clk *wdt_clk; | 87 | struct clk *wdt_clk; |
58 | struct clk *sys_clk; | 88 | struct clk *sys_clk; |
59 | void __iomem *base; | 89 | void __iomem *base; |
90 | struct notifier_block restart_handler; | ||
60 | }; | 91 | }; |
61 | 92 | ||
62 | static int pdc_wdt_keepalive(struct watchdog_device *wdt_dev) | 93 | static int pdc_wdt_keepalive(struct watchdog_device *wdt_dev) |
@@ -84,18 +115,24 @@ static int pdc_wdt_stop(struct watchdog_device *wdt_dev) | |||
84 | return 0; | 115 | return 0; |
85 | } | 116 | } |
86 | 117 | ||
118 | static void __pdc_wdt_set_timeout(struct pdc_wdt_dev *wdt) | ||
119 | { | ||
120 | unsigned long clk_rate = clk_get_rate(wdt->wdt_clk); | ||
121 | unsigned int val; | ||
122 | |||
123 | val = readl(wdt->base + PDC_WDT_CONFIG) & ~PDC_WDT_CONFIG_DELAY_MASK; | ||
124 | val |= order_base_2(wdt->wdt_dev.timeout * clk_rate) - 1; | ||
125 | writel(val, wdt->base + PDC_WDT_CONFIG); | ||
126 | } | ||
127 | |||
87 | static int pdc_wdt_set_timeout(struct watchdog_device *wdt_dev, | 128 | static int pdc_wdt_set_timeout(struct watchdog_device *wdt_dev, |
88 | unsigned int new_timeout) | 129 | unsigned int new_timeout) |
89 | { | 130 | { |
90 | unsigned int val; | ||
91 | struct pdc_wdt_dev *wdt = watchdog_get_drvdata(wdt_dev); | 131 | struct pdc_wdt_dev *wdt = watchdog_get_drvdata(wdt_dev); |
92 | unsigned long clk_rate = clk_get_rate(wdt->wdt_clk); | ||
93 | 132 | ||
94 | wdt->wdt_dev.timeout = new_timeout; | 133 | wdt->wdt_dev.timeout = new_timeout; |
95 | 134 | ||
96 | val = readl(wdt->base + PDC_WDT_CONFIG) & ~PDC_WDT_CONFIG_DELAY_MASK; | 135 | __pdc_wdt_set_timeout(wdt); |
97 | val |= order_base_2(new_timeout * clk_rate) - 1; | ||
98 | writel(val, wdt->base + PDC_WDT_CONFIG); | ||
99 | 136 | ||
100 | return 0; | 137 | return 0; |
101 | } | 138 | } |
@@ -106,6 +143,8 @@ static int pdc_wdt_start(struct watchdog_device *wdt_dev) | |||
106 | unsigned int val; | 143 | unsigned int val; |
107 | struct pdc_wdt_dev *wdt = watchdog_get_drvdata(wdt_dev); | 144 | struct pdc_wdt_dev *wdt = watchdog_get_drvdata(wdt_dev); |
108 | 145 | ||
146 | __pdc_wdt_set_timeout(wdt); | ||
147 | |||
109 | val = readl(wdt->base + PDC_WDT_CONFIG); | 148 | val = readl(wdt->base + PDC_WDT_CONFIG); |
110 | val |= PDC_WDT_CONFIG_ENABLE; | 149 | val |= PDC_WDT_CONFIG_ENABLE; |
111 | writel(val, wdt->base + PDC_WDT_CONFIG); | 150 | writel(val, wdt->base + PDC_WDT_CONFIG); |
@@ -128,8 +167,21 @@ static const struct watchdog_ops pdc_wdt_ops = { | |||
128 | .set_timeout = pdc_wdt_set_timeout, | 167 | .set_timeout = pdc_wdt_set_timeout, |
129 | }; | 168 | }; |
130 | 169 | ||
170 | static int pdc_wdt_restart(struct notifier_block *this, unsigned long mode, | ||
171 | void *cmd) | ||
172 | { | ||
173 | struct pdc_wdt_dev *wdt = container_of(this, struct pdc_wdt_dev, | ||
174 | restart_handler); | ||
175 | |||
176 | /* Assert SOFT_RESET */ | ||
177 | writel(0x1, wdt->base + PDC_WDT_SOFT_RESET); | ||
178 | |||
179 | return NOTIFY_OK; | ||
180 | } | ||
181 | |||
131 | static int pdc_wdt_probe(struct platform_device *pdev) | 182 | static int pdc_wdt_probe(struct platform_device *pdev) |
132 | { | 183 | { |
184 | u64 div; | ||
133 | int ret, val; | 185 | int ret, val; |
134 | unsigned long clk_rate; | 186 | unsigned long clk_rate; |
135 | struct resource *res; | 187 | struct resource *res; |
@@ -189,16 +241,15 @@ static int pdc_wdt_probe(struct platform_device *pdev) | |||
189 | 241 | ||
190 | pdc_wdt->wdt_dev.info = &pdc_wdt_info; | 242 | pdc_wdt->wdt_dev.info = &pdc_wdt_info; |
191 | pdc_wdt->wdt_dev.ops = &pdc_wdt_ops; | 243 | pdc_wdt->wdt_dev.ops = &pdc_wdt_ops; |
192 | pdc_wdt->wdt_dev.max_timeout = 1 << PDC_WDT_CONFIG_DELAY_MASK; | 244 | |
245 | div = 1ULL << (PDC_WDT_CONFIG_DELAY_MASK + 1); | ||
246 | do_div(div, clk_rate); | ||
247 | pdc_wdt->wdt_dev.max_timeout = div; | ||
248 | pdc_wdt->wdt_dev.timeout = PDC_WDT_DEF_TIMEOUT; | ||
193 | pdc_wdt->wdt_dev.parent = &pdev->dev; | 249 | pdc_wdt->wdt_dev.parent = &pdev->dev; |
194 | watchdog_set_drvdata(&pdc_wdt->wdt_dev, pdc_wdt); | 250 | watchdog_set_drvdata(&pdc_wdt->wdt_dev, pdc_wdt); |
195 | 251 | ||
196 | ret = watchdog_init_timeout(&pdc_wdt->wdt_dev, heartbeat, &pdev->dev); | 252 | watchdog_init_timeout(&pdc_wdt->wdt_dev, heartbeat, &pdev->dev); |
197 | if (ret < 0) { | ||
198 | pdc_wdt->wdt_dev.timeout = pdc_wdt->wdt_dev.max_timeout; | ||
199 | dev_warn(&pdev->dev, | ||
200 | "Initial timeout out of range! setting max timeout\n"); | ||
201 | } | ||
202 | 253 | ||
203 | pdc_wdt_stop(&pdc_wdt->wdt_dev); | 254 | pdc_wdt_stop(&pdc_wdt->wdt_dev); |
204 | 255 | ||
@@ -238,6 +289,13 @@ static int pdc_wdt_probe(struct platform_device *pdev) | |||
238 | if (ret) | 289 | if (ret) |
239 | goto disable_wdt_clk; | 290 | goto disable_wdt_clk; |
240 | 291 | ||
292 | pdc_wdt->restart_handler.notifier_call = pdc_wdt_restart; | ||
293 | pdc_wdt->restart_handler.priority = 128; | ||
294 | ret = register_restart_handler(&pdc_wdt->restart_handler); | ||
295 | if (ret) | ||
296 | dev_warn(&pdev->dev, "failed to register restart handler: %d\n", | ||
297 | ret); | ||
298 | |||
241 | return 0; | 299 | return 0; |
242 | 300 | ||
243 | disable_wdt_clk: | 301 | disable_wdt_clk: |
diff --git a/drivers/watchdog/imx2_wdt.c b/drivers/watchdog/imx2_wdt.c index 5e6d808d358a..0bb1a1d1b170 100644 --- a/drivers/watchdog/imx2_wdt.c +++ b/drivers/watchdog/imx2_wdt.c | |||
@@ -166,6 +166,8 @@ static int imx2_wdt_set_timeout(struct watchdog_device *wdog, | |||
166 | { | 166 | { |
167 | struct imx2_wdt_device *wdev = watchdog_get_drvdata(wdog); | 167 | struct imx2_wdt_device *wdev = watchdog_get_drvdata(wdog); |
168 | 168 | ||
169 | wdog->timeout = new_timeout; | ||
170 | |||
169 | regmap_update_bits(wdev->regmap, IMX2_WDT_WCR, IMX2_WDT_WCR_WT, | 171 | regmap_update_bits(wdev->regmap, IMX2_WDT_WCR, IMX2_WDT_WCR_WT, |
170 | WDOG_SEC_TO_COUNT(new_timeout)); | 172 | WDOG_SEC_TO_COUNT(new_timeout)); |
171 | return 0; | 173 | return 0; |
@@ -256,8 +258,11 @@ static int __init imx2_wdt_probe(struct platform_device *pdev) | |||
256 | wdog->ops = &imx2_wdt_ops; | 258 | wdog->ops = &imx2_wdt_ops; |
257 | wdog->min_timeout = 1; | 259 | wdog->min_timeout = 1; |
258 | wdog->max_timeout = IMX2_WDT_MAX_TIME; | 260 | wdog->max_timeout = IMX2_WDT_MAX_TIME; |
261 | wdog->parent = &pdev->dev; | ||
259 | 262 | ||
260 | clk_prepare_enable(wdev->clk); | 263 | ret = clk_prepare_enable(wdev->clk); |
264 | if (ret) | ||
265 | return ret; | ||
261 | 266 | ||
262 | regmap_read(wdev->regmap, IMX2_WDT_WRSR, &val); | 267 | regmap_read(wdev->regmap, IMX2_WDT_WRSR, &val); |
263 | wdog->bootstatus = val & IMX2_WDT_WRSR_TOUT ? WDIOF_CARDRESET : 0; | 268 | wdog->bootstatus = val & IMX2_WDT_WRSR_TOUT ? WDIOF_CARDRESET : 0; |
@@ -286,7 +291,7 @@ static int __init imx2_wdt_probe(struct platform_device *pdev) | |||
286 | ret = watchdog_register_device(wdog); | 291 | ret = watchdog_register_device(wdog); |
287 | if (ret) { | 292 | if (ret) { |
288 | dev_err(&pdev->dev, "cannot register watchdog device\n"); | 293 | dev_err(&pdev->dev, "cannot register watchdog device\n"); |
289 | return ret; | 294 | goto disable_clk; |
290 | } | 295 | } |
291 | 296 | ||
292 | wdev->restart_handler.notifier_call = imx2_restart_handler; | 297 | wdev->restart_handler.notifier_call = imx2_restart_handler; |
@@ -299,6 +304,10 @@ static int __init imx2_wdt_probe(struct platform_device *pdev) | |||
299 | wdog->timeout, nowayout); | 304 | wdog->timeout, nowayout); |
300 | 305 | ||
301 | return 0; | 306 | return 0; |
307 | |||
308 | disable_clk: | ||
309 | clk_disable_unprepare(wdev->clk); | ||
310 | return ret; | ||
302 | } | 311 | } |
303 | 312 | ||
304 | static int __exit imx2_wdt_remove(struct platform_device *pdev) | 313 | static int __exit imx2_wdt_remove(struct platform_device *pdev) |
@@ -362,8 +371,11 @@ static int imx2_wdt_resume(struct device *dev) | |||
362 | { | 371 | { |
363 | struct watchdog_device *wdog = dev_get_drvdata(dev); | 372 | struct watchdog_device *wdog = dev_get_drvdata(dev); |
364 | struct imx2_wdt_device *wdev = watchdog_get_drvdata(wdog); | 373 | struct imx2_wdt_device *wdev = watchdog_get_drvdata(wdog); |
374 | int ret; | ||
365 | 375 | ||
366 | clk_prepare_enable(wdev->clk); | 376 | ret = clk_prepare_enable(wdev->clk); |
377 | if (ret) | ||
378 | return ret; | ||
367 | 379 | ||
368 | if (watchdog_active(wdog) && !imx2_wdt_is_running(wdev)) { | 380 | if (watchdog_active(wdog) && !imx2_wdt_is_running(wdev)) { |
369 | /* | 381 | /* |
diff --git a/drivers/watchdog/max63xx_wdt.c b/drivers/watchdog/max63xx_wdt.c index 08da3114accb..f36ca4be0720 100644 --- a/drivers/watchdog/max63xx_wdt.c +++ b/drivers/watchdog/max63xx_wdt.c | |||
@@ -39,10 +39,22 @@ static bool nowayout = WATCHDOG_NOWAYOUT; | |||
39 | #define MAX6369_WDSET (7 << 0) | 39 | #define MAX6369_WDSET (7 << 0) |
40 | #define MAX6369_WDI (1 << 3) | 40 | #define MAX6369_WDI (1 << 3) |
41 | 41 | ||
42 | static DEFINE_SPINLOCK(io_lock); | 42 | #define MAX6369_WDSET_DISABLED 3 |
43 | 43 | ||
44 | static int nodelay; | 44 | static int nodelay; |
45 | static void __iomem *wdt_base; | 45 | |
46 | struct max63xx_wdt { | ||
47 | struct watchdog_device wdd; | ||
48 | const struct max63xx_timeout *timeout; | ||
49 | |||
50 | /* memory mapping */ | ||
51 | void __iomem *base; | ||
52 | spinlock_t lock; | ||
53 | |||
54 | /* WDI and WSET bits write access routines */ | ||
55 | void (*ping)(struct max63xx_wdt *wdt); | ||
56 | void (*set)(struct max63xx_wdt *wdt, u8 set); | ||
57 | }; | ||
46 | 58 | ||
47 | /* | 59 | /* |
48 | * The timeout values used are actually the absolute minimum the chip | 60 | * The timeout values used are actually the absolute minimum the chip |
@@ -59,25 +71,25 @@ static void __iomem *wdt_base; | |||
59 | 71 | ||
60 | /* Timeouts in second */ | 72 | /* Timeouts in second */ |
61 | struct max63xx_timeout { | 73 | struct max63xx_timeout { |
62 | u8 wdset; | 74 | const u8 wdset; |
63 | u8 tdelay; | 75 | const u8 tdelay; |
64 | u8 twd; | 76 | const u8 twd; |
65 | }; | 77 | }; |
66 | 78 | ||
67 | static struct max63xx_timeout max6369_table[] = { | 79 | static const struct max63xx_timeout max6369_table[] = { |
68 | { 5, 1, 1 }, | 80 | { 5, 1, 1 }, |
69 | { 6, 10, 10 }, | 81 | { 6, 10, 10 }, |
70 | { 7, 60, 60 }, | 82 | { 7, 60, 60 }, |
71 | { }, | 83 | { }, |
72 | }; | 84 | }; |
73 | 85 | ||
74 | static struct max63xx_timeout max6371_table[] = { | 86 | static const struct max63xx_timeout max6371_table[] = { |
75 | { 6, 60, 3 }, | 87 | { 6, 60, 3 }, |
76 | { 7, 60, 60 }, | 88 | { 7, 60, 60 }, |
77 | { }, | 89 | { }, |
78 | }; | 90 | }; |
79 | 91 | ||
80 | static struct max63xx_timeout max6373_table[] = { | 92 | static const struct max63xx_timeout max6373_table[] = { |
81 | { 2, 60, 1 }, | 93 | { 2, 60, 1 }, |
82 | { 5, 0, 1 }, | 94 | { 5, 0, 1 }, |
83 | { 1, 3, 3 }, | 95 | { 1, 3, 3 }, |
@@ -86,8 +98,6 @@ static struct max63xx_timeout max6373_table[] = { | |||
86 | { }, | 98 | { }, |
87 | }; | 99 | }; |
88 | 100 | ||
89 | static struct max63xx_timeout *current_timeout; | ||
90 | |||
91 | static struct max63xx_timeout * | 101 | static struct max63xx_timeout * |
92 | max63xx_select_timeout(struct max63xx_timeout *table, int value) | 102 | max63xx_select_timeout(struct max63xx_timeout *table, int value) |
93 | { | 103 | { |
@@ -108,59 +118,32 @@ max63xx_select_timeout(struct max63xx_timeout *table, int value) | |||
108 | 118 | ||
109 | static int max63xx_wdt_ping(struct watchdog_device *wdd) | 119 | static int max63xx_wdt_ping(struct watchdog_device *wdd) |
110 | { | 120 | { |
111 | u8 val; | 121 | struct max63xx_wdt *wdt = watchdog_get_drvdata(wdd); |
112 | |||
113 | spin_lock(&io_lock); | ||
114 | 122 | ||
115 | val = __raw_readb(wdt_base); | 123 | wdt->ping(wdt); |
116 | |||
117 | __raw_writeb(val | MAX6369_WDI, wdt_base); | ||
118 | __raw_writeb(val & ~MAX6369_WDI, wdt_base); | ||
119 | |||
120 | spin_unlock(&io_lock); | ||
121 | return 0; | 124 | return 0; |
122 | } | 125 | } |
123 | 126 | ||
124 | static int max63xx_wdt_start(struct watchdog_device *wdd) | 127 | static int max63xx_wdt_start(struct watchdog_device *wdd) |
125 | { | 128 | { |
126 | struct max63xx_timeout *entry = watchdog_get_drvdata(wdd); | 129 | struct max63xx_wdt *wdt = watchdog_get_drvdata(wdd); |
127 | u8 val; | ||
128 | 130 | ||
129 | spin_lock(&io_lock); | 131 | wdt->set(wdt, wdt->timeout->wdset); |
130 | |||
131 | val = __raw_readb(wdt_base); | ||
132 | val &= ~MAX6369_WDSET; | ||
133 | val |= entry->wdset; | ||
134 | __raw_writeb(val, wdt_base); | ||
135 | |||
136 | spin_unlock(&io_lock); | ||
137 | 132 | ||
138 | /* check for a edge triggered startup */ | 133 | /* check for a edge triggered startup */ |
139 | if (entry->tdelay == 0) | 134 | if (wdt->timeout->tdelay == 0) |
140 | max63xx_wdt_ping(wdd); | 135 | wdt->ping(wdt); |
141 | return 0; | 136 | return 0; |
142 | } | 137 | } |
143 | 138 | ||
144 | static int max63xx_wdt_stop(struct watchdog_device *wdd) | 139 | static int max63xx_wdt_stop(struct watchdog_device *wdd) |
145 | { | 140 | { |
146 | u8 val; | 141 | struct max63xx_wdt *wdt = watchdog_get_drvdata(wdd); |
147 | 142 | ||
148 | spin_lock(&io_lock); | 143 | wdt->set(wdt, MAX6369_WDSET_DISABLED); |
149 | |||
150 | val = __raw_readb(wdt_base); | ||
151 | val &= ~MAX6369_WDSET; | ||
152 | val |= 3; | ||
153 | __raw_writeb(val, wdt_base); | ||
154 | |||
155 | spin_unlock(&io_lock); | ||
156 | return 0; | 144 | return 0; |
157 | } | 145 | } |
158 | 146 | ||
159 | static const struct watchdog_info max63xx_wdt_info = { | ||
160 | .options = WDIOF_KEEPALIVEPING | WDIOF_MAGICCLOSE, | ||
161 | .identity = "max63xx Watchdog", | ||
162 | }; | ||
163 | |||
164 | static const struct watchdog_ops max63xx_wdt_ops = { | 147 | static const struct watchdog_ops max63xx_wdt_ops = { |
165 | .owner = THIS_MODULE, | 148 | .owner = THIS_MODULE, |
166 | .start = max63xx_wdt_start, | 149 | .start = max63xx_wdt_start, |
@@ -168,53 +151,108 @@ static const struct watchdog_ops max63xx_wdt_ops = { | |||
168 | .ping = max63xx_wdt_ping, | 151 | .ping = max63xx_wdt_ping, |
169 | }; | 152 | }; |
170 | 153 | ||
171 | static struct watchdog_device max63xx_wdt_dev = { | 154 | static const struct watchdog_info max63xx_wdt_info = { |
172 | .info = &max63xx_wdt_info, | 155 | .options = WDIOF_KEEPALIVEPING | WDIOF_MAGICCLOSE, |
173 | .ops = &max63xx_wdt_ops, | 156 | .identity = "max63xx Watchdog", |
174 | }; | 157 | }; |
175 | 158 | ||
159 | static void max63xx_mmap_ping(struct max63xx_wdt *wdt) | ||
160 | { | ||
161 | u8 val; | ||
162 | |||
163 | spin_lock(&wdt->lock); | ||
164 | |||
165 | val = __raw_readb(wdt->base); | ||
166 | |||
167 | __raw_writeb(val | MAX6369_WDI, wdt->base); | ||
168 | __raw_writeb(val & ~MAX6369_WDI, wdt->base); | ||
169 | |||
170 | spin_unlock(&wdt->lock); | ||
171 | } | ||
172 | |||
173 | static void max63xx_mmap_set(struct max63xx_wdt *wdt, u8 set) | ||
174 | { | ||
175 | u8 val; | ||
176 | |||
177 | spin_lock(&wdt->lock); | ||
178 | |||
179 | val = __raw_readb(wdt->base); | ||
180 | val &= ~MAX6369_WDSET; | ||
181 | val |= set & MAX6369_WDSET; | ||
182 | __raw_writeb(val, wdt->base); | ||
183 | |||
184 | spin_unlock(&wdt->lock); | ||
185 | } | ||
186 | |||
187 | static int max63xx_mmap_init(struct platform_device *p, struct max63xx_wdt *wdt) | ||
188 | { | ||
189 | struct resource *mem = platform_get_resource(p, IORESOURCE_MEM, 0); | ||
190 | |||
191 | wdt->base = devm_ioremap_resource(&p->dev, mem); | ||
192 | if (IS_ERR(wdt->base)) | ||
193 | return PTR_ERR(wdt->base); | ||
194 | |||
195 | spin_lock_init(&wdt->lock); | ||
196 | |||
197 | wdt->ping = max63xx_mmap_ping; | ||
198 | wdt->set = max63xx_mmap_set; | ||
199 | return 0; | ||
200 | } | ||
201 | |||
176 | static int max63xx_wdt_probe(struct platform_device *pdev) | 202 | static int max63xx_wdt_probe(struct platform_device *pdev) |
177 | { | 203 | { |
178 | struct resource *wdt_mem; | 204 | struct max63xx_wdt *wdt; |
179 | struct max63xx_timeout *table; | 205 | struct max63xx_timeout *table; |
206 | int err; | ||
207 | |||
208 | wdt = devm_kzalloc(&pdev->dev, sizeof(*wdt), GFP_KERNEL); | ||
209 | if (!wdt) | ||
210 | return -ENOMEM; | ||
180 | 211 | ||
181 | table = (struct max63xx_timeout *)pdev->id_entry->driver_data; | 212 | table = (struct max63xx_timeout *)pdev->id_entry->driver_data; |
182 | 213 | ||
183 | if (heartbeat < 1 || heartbeat > MAX_HEARTBEAT) | 214 | if (heartbeat < 1 || heartbeat > MAX_HEARTBEAT) |
184 | heartbeat = DEFAULT_HEARTBEAT; | 215 | heartbeat = DEFAULT_HEARTBEAT; |
185 | 216 | ||
186 | dev_info(&pdev->dev, "requesting %ds heartbeat\n", heartbeat); | 217 | wdt->timeout = max63xx_select_timeout(table, heartbeat); |
187 | current_timeout = max63xx_select_timeout(table, heartbeat); | 218 | if (!wdt->timeout) { |
188 | 219 | dev_err(&pdev->dev, "unable to satisfy %ds heartbeat request\n", | |
189 | if (!current_timeout) { | 220 | heartbeat); |
190 | dev_err(&pdev->dev, "unable to satisfy heartbeat request\n"); | ||
191 | return -EINVAL; | 221 | return -EINVAL; |
192 | } | 222 | } |
193 | 223 | ||
194 | dev_info(&pdev->dev, "using %ds heartbeat with %ds initial delay\n", | 224 | err = max63xx_mmap_init(pdev, wdt); |
195 | current_timeout->twd, current_timeout->tdelay); | 225 | if (err) |
226 | return err; | ||
227 | |||
228 | platform_set_drvdata(pdev, &wdt->wdd); | ||
229 | watchdog_set_drvdata(&wdt->wdd, wdt); | ||
196 | 230 | ||
197 | heartbeat = current_timeout->twd; | 231 | wdt->wdd.parent = &pdev->dev; |
232 | wdt->wdd.timeout = wdt->timeout->twd; | ||
233 | wdt->wdd.info = &max63xx_wdt_info; | ||
234 | wdt->wdd.ops = &max63xx_wdt_ops; | ||
198 | 235 | ||
199 | wdt_mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); | 236 | watchdog_set_nowayout(&wdt->wdd, nowayout); |
200 | wdt_base = devm_ioremap_resource(&pdev->dev, wdt_mem); | ||
201 | if (IS_ERR(wdt_base)) | ||
202 | return PTR_ERR(wdt_base); | ||
203 | 237 | ||
204 | max63xx_wdt_dev.timeout = heartbeat; | 238 | err = watchdog_register_device(&wdt->wdd); |
205 | watchdog_set_nowayout(&max63xx_wdt_dev, nowayout); | 239 | if (err) |
206 | watchdog_set_drvdata(&max63xx_wdt_dev, current_timeout); | 240 | return err; |
207 | 241 | ||
208 | return watchdog_register_device(&max63xx_wdt_dev); | 242 | dev_info(&pdev->dev, "using %ds heartbeat with %ds initial delay\n", |
243 | wdt->timeout->twd, wdt->timeout->tdelay); | ||
244 | return 0; | ||
209 | } | 245 | } |
210 | 246 | ||
211 | static int max63xx_wdt_remove(struct platform_device *pdev) | 247 | static int max63xx_wdt_remove(struct platform_device *pdev) |
212 | { | 248 | { |
213 | watchdog_unregister_device(&max63xx_wdt_dev); | 249 | struct watchdog_device *wdd = platform_get_drvdata(pdev); |
250 | |||
251 | watchdog_unregister_device(wdd); | ||
214 | return 0; | 252 | return 0; |
215 | } | 253 | } |
216 | 254 | ||
217 | static struct platform_device_id max63xx_id_table[] = { | 255 | static const struct platform_device_id max63xx_id_table[] = { |
218 | { "max6369_wdt", (kernel_ulong_t)max6369_table, }, | 256 | { "max6369_wdt", (kernel_ulong_t)max6369_table, }, |
219 | { "max6370_wdt", (kernel_ulong_t)max6369_table, }, | 257 | { "max6370_wdt", (kernel_ulong_t)max6369_table, }, |
220 | { "max6371_wdt", (kernel_ulong_t)max6371_table, }, | 258 | { "max6371_wdt", (kernel_ulong_t)max6371_table, }, |
diff --git a/drivers/watchdog/mena21_wdt.c b/drivers/watchdog/mena21_wdt.c index 96dbba980579..d193a5e79c38 100644 --- a/drivers/watchdog/mena21_wdt.c +++ b/drivers/watchdog/mena21_wdt.c | |||
@@ -208,14 +208,15 @@ static int a21_wdt_probe(struct platform_device *pdev) | |||
208 | else if (reset == 7) | 208 | else if (reset == 7) |
209 | a21_wdt.bootstatus |= WDIOF_EXTERN2; | 209 | a21_wdt.bootstatus |= WDIOF_EXTERN2; |
210 | 210 | ||
211 | drv->wdt = a21_wdt; | ||
212 | dev_set_drvdata(&pdev->dev, drv); | ||
213 | |||
211 | ret = watchdog_register_device(&a21_wdt); | 214 | ret = watchdog_register_device(&a21_wdt); |
212 | if (ret) { | 215 | if (ret) { |
213 | dev_err(&pdev->dev, "Cannot register watchdog device\n"); | 216 | dev_err(&pdev->dev, "Cannot register watchdog device\n"); |
214 | goto err_register_wd; | 217 | goto err_register_wd; |
215 | } | 218 | } |
216 | 219 | ||
217 | dev_set_drvdata(&pdev->dev, drv); | ||
218 | |||
219 | dev_info(&pdev->dev, "MEN A21 watchdog timer driver enabled\n"); | 220 | dev_info(&pdev->dev, "MEN A21 watchdog timer driver enabled\n"); |
220 | 221 | ||
221 | return 0; | 222 | return 0; |
diff --git a/drivers/watchdog/omap_wdt.c b/drivers/watchdog/omap_wdt.c index 1e6be9e40577..de911c7e477c 100644 --- a/drivers/watchdog/omap_wdt.c +++ b/drivers/watchdog/omap_wdt.c | |||
@@ -53,7 +53,15 @@ static unsigned timer_margin; | |||
53 | module_param(timer_margin, uint, 0); | 53 | module_param(timer_margin, uint, 0); |
54 | MODULE_PARM_DESC(timer_margin, "initial watchdog timeout (in seconds)"); | 54 | MODULE_PARM_DESC(timer_margin, "initial watchdog timeout (in seconds)"); |
55 | 55 | ||
56 | #define to_omap_wdt_dev(_wdog) container_of(_wdog, struct omap_wdt_dev, wdog) | ||
57 | |||
58 | static bool early_enable; | ||
59 | module_param(early_enable, bool, 0); | ||
60 | MODULE_PARM_DESC(early_enable, | ||
61 | "Watchdog is started on module insertion (default=0)"); | ||
62 | |||
56 | struct omap_wdt_dev { | 63 | struct omap_wdt_dev { |
64 | struct watchdog_device wdog; | ||
57 | void __iomem *base; /* physical */ | 65 | void __iomem *base; /* physical */ |
58 | struct device *dev; | 66 | struct device *dev; |
59 | bool omap_wdt_users; | 67 | bool omap_wdt_users; |
@@ -123,7 +131,7 @@ static void omap_wdt_set_timer(struct omap_wdt_dev *wdev, | |||
123 | 131 | ||
124 | static int omap_wdt_start(struct watchdog_device *wdog) | 132 | static int omap_wdt_start(struct watchdog_device *wdog) |
125 | { | 133 | { |
126 | struct omap_wdt_dev *wdev = watchdog_get_drvdata(wdog); | 134 | struct omap_wdt_dev *wdev = to_omap_wdt_dev(wdog); |
127 | void __iomem *base = wdev->base; | 135 | void __iomem *base = wdev->base; |
128 | 136 | ||
129 | mutex_lock(&wdev->lock); | 137 | mutex_lock(&wdev->lock); |
@@ -132,6 +140,13 @@ static int omap_wdt_start(struct watchdog_device *wdog) | |||
132 | 140 | ||
133 | pm_runtime_get_sync(wdev->dev); | 141 | pm_runtime_get_sync(wdev->dev); |
134 | 142 | ||
143 | /* | ||
144 | * Make sure the watchdog is disabled. This is unfortunately required | ||
145 | * because writing to various registers with the watchdog running has no | ||
146 | * effect. | ||
147 | */ | ||
148 | omap_wdt_disable(wdev); | ||
149 | |||
135 | /* initialize prescaler */ | 150 | /* initialize prescaler */ |
136 | while (readl_relaxed(base + OMAP_WATCHDOG_WPS) & 0x01) | 151 | while (readl_relaxed(base + OMAP_WATCHDOG_WPS) & 0x01) |
137 | cpu_relax(); | 152 | cpu_relax(); |
@@ -151,7 +166,7 @@ static int omap_wdt_start(struct watchdog_device *wdog) | |||
151 | 166 | ||
152 | static int omap_wdt_stop(struct watchdog_device *wdog) | 167 | static int omap_wdt_stop(struct watchdog_device *wdog) |
153 | { | 168 | { |
154 | struct omap_wdt_dev *wdev = watchdog_get_drvdata(wdog); | 169 | struct omap_wdt_dev *wdev = to_omap_wdt_dev(wdog); |
155 | 170 | ||
156 | mutex_lock(&wdev->lock); | 171 | mutex_lock(&wdev->lock); |
157 | omap_wdt_disable(wdev); | 172 | omap_wdt_disable(wdev); |
@@ -163,7 +178,7 @@ static int omap_wdt_stop(struct watchdog_device *wdog) | |||
163 | 178 | ||
164 | static int omap_wdt_ping(struct watchdog_device *wdog) | 179 | static int omap_wdt_ping(struct watchdog_device *wdog) |
165 | { | 180 | { |
166 | struct omap_wdt_dev *wdev = watchdog_get_drvdata(wdog); | 181 | struct omap_wdt_dev *wdev = to_omap_wdt_dev(wdog); |
167 | 182 | ||
168 | mutex_lock(&wdev->lock); | 183 | mutex_lock(&wdev->lock); |
169 | omap_wdt_reload(wdev); | 184 | omap_wdt_reload(wdev); |
@@ -175,7 +190,7 @@ static int omap_wdt_ping(struct watchdog_device *wdog) | |||
175 | static int omap_wdt_set_timeout(struct watchdog_device *wdog, | 190 | static int omap_wdt_set_timeout(struct watchdog_device *wdog, |
176 | unsigned int timeout) | 191 | unsigned int timeout) |
177 | { | 192 | { |
178 | struct omap_wdt_dev *wdev = watchdog_get_drvdata(wdog); | 193 | struct omap_wdt_dev *wdev = to_omap_wdt_dev(wdog); |
179 | 194 | ||
180 | mutex_lock(&wdev->lock); | 195 | mutex_lock(&wdev->lock); |
181 | omap_wdt_disable(wdev); | 196 | omap_wdt_disable(wdev); |
@@ -188,6 +203,16 @@ static int omap_wdt_set_timeout(struct watchdog_device *wdog, | |||
188 | return 0; | 203 | return 0; |
189 | } | 204 | } |
190 | 205 | ||
206 | static unsigned int omap_wdt_get_timeleft(struct watchdog_device *wdog) | ||
207 | { | ||
208 | struct omap_wdt_dev *wdev = watchdog_get_drvdata(wdog); | ||
209 | void __iomem *base = wdev->base; | ||
210 | u32 value; | ||
211 | |||
212 | value = readl_relaxed(base + OMAP_WATCHDOG_CRR); | ||
213 | return GET_WCCR_SECS(value); | ||
214 | } | ||
215 | |||
191 | static const struct watchdog_info omap_wdt_info = { | 216 | static const struct watchdog_info omap_wdt_info = { |
192 | .options = WDIOF_SETTIMEOUT | WDIOF_MAGICCLOSE | WDIOF_KEEPALIVEPING, | 217 | .options = WDIOF_SETTIMEOUT | WDIOF_MAGICCLOSE | WDIOF_KEEPALIVEPING, |
193 | .identity = "OMAP Watchdog", | 218 | .identity = "OMAP Watchdog", |
@@ -199,21 +224,16 @@ static const struct watchdog_ops omap_wdt_ops = { | |||
199 | .stop = omap_wdt_stop, | 224 | .stop = omap_wdt_stop, |
200 | .ping = omap_wdt_ping, | 225 | .ping = omap_wdt_ping, |
201 | .set_timeout = omap_wdt_set_timeout, | 226 | .set_timeout = omap_wdt_set_timeout, |
227 | .get_timeleft = omap_wdt_get_timeleft, | ||
202 | }; | 228 | }; |
203 | 229 | ||
204 | static int omap_wdt_probe(struct platform_device *pdev) | 230 | static int omap_wdt_probe(struct platform_device *pdev) |
205 | { | 231 | { |
206 | struct omap_wd_timer_platform_data *pdata = dev_get_platdata(&pdev->dev); | 232 | struct omap_wd_timer_platform_data *pdata = dev_get_platdata(&pdev->dev); |
207 | struct watchdog_device *omap_wdt; | ||
208 | struct resource *res; | 233 | struct resource *res; |
209 | struct omap_wdt_dev *wdev; | 234 | struct omap_wdt_dev *wdev; |
210 | u32 rs; | ||
211 | int ret; | 235 | int ret; |
212 | 236 | ||
213 | omap_wdt = devm_kzalloc(&pdev->dev, sizeof(*omap_wdt), GFP_KERNEL); | ||
214 | if (!omap_wdt) | ||
215 | return -ENOMEM; | ||
216 | |||
217 | wdev = devm_kzalloc(&pdev->dev, sizeof(*wdev), GFP_KERNEL); | 237 | wdev = devm_kzalloc(&pdev->dev, sizeof(*wdev), GFP_KERNEL); |
218 | if (!wdev) | 238 | if (!wdev) |
219 | return -ENOMEM; | 239 | return -ENOMEM; |
@@ -229,35 +249,30 @@ static int omap_wdt_probe(struct platform_device *pdev) | |||
229 | if (IS_ERR(wdev->base)) | 249 | if (IS_ERR(wdev->base)) |
230 | return PTR_ERR(wdev->base); | 250 | return PTR_ERR(wdev->base); |
231 | 251 | ||
232 | omap_wdt->info = &omap_wdt_info; | 252 | wdev->wdog.info = &omap_wdt_info; |
233 | omap_wdt->ops = &omap_wdt_ops; | 253 | wdev->wdog.ops = &omap_wdt_ops; |
234 | omap_wdt->min_timeout = TIMER_MARGIN_MIN; | 254 | wdev->wdog.min_timeout = TIMER_MARGIN_MIN; |
235 | omap_wdt->max_timeout = TIMER_MARGIN_MAX; | 255 | wdev->wdog.max_timeout = TIMER_MARGIN_MAX; |
236 | 256 | ||
237 | if (timer_margin >= TIMER_MARGIN_MIN && | 257 | if (watchdog_init_timeout(&wdev->wdog, timer_margin, &pdev->dev) < 0) |
238 | timer_margin <= TIMER_MARGIN_MAX) | 258 | wdev->wdog.timeout = TIMER_MARGIN_DEFAULT; |
239 | omap_wdt->timeout = timer_margin; | ||
240 | else | ||
241 | omap_wdt->timeout = TIMER_MARGIN_DEFAULT; | ||
242 | 259 | ||
243 | watchdog_set_drvdata(omap_wdt, wdev); | 260 | watchdog_set_nowayout(&wdev->wdog, nowayout); |
244 | watchdog_set_nowayout(omap_wdt, nowayout); | ||
245 | 261 | ||
246 | platform_set_drvdata(pdev, omap_wdt); | 262 | platform_set_drvdata(pdev, wdev); |
247 | 263 | ||
248 | pm_runtime_enable(wdev->dev); | 264 | pm_runtime_enable(wdev->dev); |
249 | pm_runtime_get_sync(wdev->dev); | 265 | pm_runtime_get_sync(wdev->dev); |
250 | 266 | ||
251 | if (pdata && pdata->read_reset_sources) | 267 | if (pdata && pdata->read_reset_sources) { |
252 | rs = pdata->read_reset_sources(); | 268 | u32 rs = pdata->read_reset_sources(); |
253 | else | 269 | if (rs & (1 << OMAP_MPU_WD_RST_SRC_ID_SHIFT)) |
254 | rs = 0; | 270 | wdev->wdog.bootstatus = WDIOF_CARDRESET; |
255 | omap_wdt->bootstatus = (rs & (1 << OMAP_MPU_WD_RST_SRC_ID_SHIFT)) ? | 271 | } |
256 | WDIOF_CARDRESET : 0; | ||
257 | 272 | ||
258 | omap_wdt_disable(wdev); | 273 | omap_wdt_disable(wdev); |
259 | 274 | ||
260 | ret = watchdog_register_device(omap_wdt); | 275 | ret = watchdog_register_device(&wdev->wdog); |
261 | if (ret) { | 276 | if (ret) { |
262 | pm_runtime_disable(wdev->dev); | 277 | pm_runtime_disable(wdev->dev); |
263 | return ret; | 278 | return ret; |
@@ -265,17 +280,19 @@ static int omap_wdt_probe(struct platform_device *pdev) | |||
265 | 280 | ||
266 | pr_info("OMAP Watchdog Timer Rev 0x%02x: initial timeout %d sec\n", | 281 | pr_info("OMAP Watchdog Timer Rev 0x%02x: initial timeout %d sec\n", |
267 | readl_relaxed(wdev->base + OMAP_WATCHDOG_REV) & 0xFF, | 282 | readl_relaxed(wdev->base + OMAP_WATCHDOG_REV) & 0xFF, |
268 | omap_wdt->timeout); | 283 | wdev->wdog.timeout); |
269 | 284 | ||
270 | pm_runtime_put_sync(wdev->dev); | 285 | pm_runtime_put_sync(wdev->dev); |
271 | 286 | ||
287 | if (early_enable) | ||
288 | omap_wdt_start(&wdev->wdog); | ||
289 | |||
272 | return 0; | 290 | return 0; |
273 | } | 291 | } |
274 | 292 | ||
275 | static void omap_wdt_shutdown(struct platform_device *pdev) | 293 | static void omap_wdt_shutdown(struct platform_device *pdev) |
276 | { | 294 | { |
277 | struct watchdog_device *wdog = platform_get_drvdata(pdev); | 295 | struct omap_wdt_dev *wdev = platform_get_drvdata(pdev); |
278 | struct omap_wdt_dev *wdev = watchdog_get_drvdata(wdog); | ||
279 | 296 | ||
280 | mutex_lock(&wdev->lock); | 297 | mutex_lock(&wdev->lock); |
281 | if (wdev->omap_wdt_users) { | 298 | if (wdev->omap_wdt_users) { |
@@ -287,11 +304,10 @@ static void omap_wdt_shutdown(struct platform_device *pdev) | |||
287 | 304 | ||
288 | static int omap_wdt_remove(struct platform_device *pdev) | 305 | static int omap_wdt_remove(struct platform_device *pdev) |
289 | { | 306 | { |
290 | struct watchdog_device *wdog = platform_get_drvdata(pdev); | 307 | struct omap_wdt_dev *wdev = platform_get_drvdata(pdev); |
291 | struct omap_wdt_dev *wdev = watchdog_get_drvdata(wdog); | ||
292 | 308 | ||
293 | pm_runtime_disable(wdev->dev); | 309 | pm_runtime_disable(wdev->dev); |
294 | watchdog_unregister_device(wdog); | 310 | watchdog_unregister_device(&wdev->wdog); |
295 | 311 | ||
296 | return 0; | 312 | return 0; |
297 | } | 313 | } |
@@ -306,8 +322,7 @@ static int omap_wdt_remove(struct platform_device *pdev) | |||
306 | 322 | ||
307 | static int omap_wdt_suspend(struct platform_device *pdev, pm_message_t state) | 323 | static int omap_wdt_suspend(struct platform_device *pdev, pm_message_t state) |
308 | { | 324 | { |
309 | struct watchdog_device *wdog = platform_get_drvdata(pdev); | 325 | struct omap_wdt_dev *wdev = platform_get_drvdata(pdev); |
310 | struct omap_wdt_dev *wdev = watchdog_get_drvdata(wdog); | ||
311 | 326 | ||
312 | mutex_lock(&wdev->lock); | 327 | mutex_lock(&wdev->lock); |
313 | if (wdev->omap_wdt_users) { | 328 | if (wdev->omap_wdt_users) { |
@@ -321,8 +336,7 @@ static int omap_wdt_suspend(struct platform_device *pdev, pm_message_t state) | |||
321 | 336 | ||
322 | static int omap_wdt_resume(struct platform_device *pdev) | 337 | static int omap_wdt_resume(struct platform_device *pdev) |
323 | { | 338 | { |
324 | struct watchdog_device *wdog = platform_get_drvdata(pdev); | 339 | struct omap_wdt_dev *wdev = platform_get_drvdata(pdev); |
325 | struct omap_wdt_dev *wdev = watchdog_get_drvdata(wdog); | ||
326 | 340 | ||
327 | mutex_lock(&wdev->lock); | 341 | mutex_lock(&wdev->lock); |
328 | if (wdev->omap_wdt_users) { | 342 | if (wdev->omap_wdt_users) { |
diff --git a/drivers/watchdog/omap_wdt.h b/drivers/watchdog/omap_wdt.h index 09b774cf75b9..42f31ec5e90d 100644 --- a/drivers/watchdog/omap_wdt.h +++ b/drivers/watchdog/omap_wdt.h | |||
@@ -50,5 +50,6 @@ | |||
50 | 50 | ||
51 | #define PTV 0 /* prescale */ | 51 | #define PTV 0 /* prescale */ |
52 | #define GET_WLDR_VAL(secs) (0xffffffff - ((secs) * (32768/(1<<PTV))) + 1) | 52 | #define GET_WLDR_VAL(secs) (0xffffffff - ((secs) * (32768/(1<<PTV))) + 1) |
53 | #define GET_WCCR_SECS(val) ((0xffffffff - (val) + 1) / (32768/(1<<PTV))) | ||
53 | 54 | ||
54 | #endif /* _OMAP_WATCHDOG_H */ | 55 | #endif /* _OMAP_WATCHDOG_H */ |
diff --git a/drivers/watchdog/st_lpc_wdt.c b/drivers/watchdog/st_lpc_wdt.c index f32be155212a..6785afdc0fca 100644 --- a/drivers/watchdog/st_lpc_wdt.c +++ b/drivers/watchdog/st_lpc_wdt.c | |||
@@ -197,7 +197,7 @@ static int st_wdog_probe(struct platform_device *pdev) | |||
197 | return -EINVAL; | 197 | return -EINVAL; |
198 | } | 198 | } |
199 | 199 | ||
200 | /* LPC can either run in RTC or WDT mode */ | 200 | /* LPC can either run as a Clocksource or in RTC or WDT mode */ |
201 | if (mode != ST_LPC_MODE_WDT) | 201 | if (mode != ST_LPC_MODE_WDT) |
202 | return -ENODEV; | 202 | return -ENODEV; |
203 | 203 | ||
diff --git a/drivers/watchdog/watchdog_core.c b/drivers/watchdog/watchdog_core.c index cec9b559647d..1a8059455413 100644 --- a/drivers/watchdog/watchdog_core.c +++ b/drivers/watchdog/watchdog_core.c | |||
@@ -43,6 +43,45 @@ | |||
43 | static DEFINE_IDA(watchdog_ida); | 43 | static DEFINE_IDA(watchdog_ida); |
44 | static struct class *watchdog_class; | 44 | static struct class *watchdog_class; |
45 | 45 | ||
46 | /* | ||
47 | * Deferred Registration infrastructure. | ||
48 | * | ||
49 | * Sometimes watchdog drivers needs to be loaded as soon as possible, | ||
50 | * for example when it's impossible to disable it. To do so, | ||
51 | * raising the initcall level of the watchdog driver is a solution. | ||
52 | * But in such case, the miscdev is maybe not ready (subsys_initcall), and | ||
53 | * watchdog_core need miscdev to register the watchdog as a char device. | ||
54 | * | ||
55 | * The deferred registration infrastructure offer a way for the watchdog | ||
56 | * subsystem to register a watchdog properly, even before miscdev is ready. | ||
57 | */ | ||
58 | |||
59 | static DEFINE_MUTEX(wtd_deferred_reg_mutex); | ||
60 | static LIST_HEAD(wtd_deferred_reg_list); | ||
61 | static bool wtd_deferred_reg_done; | ||
62 | |||
63 | static int watchdog_deferred_registration_add(struct watchdog_device *wdd) | ||
64 | { | ||
65 | list_add_tail(&wdd->deferred, | ||
66 | &wtd_deferred_reg_list); | ||
67 | return 0; | ||
68 | } | ||
69 | |||
70 | static void watchdog_deferred_registration_del(struct watchdog_device *wdd) | ||
71 | { | ||
72 | struct list_head *p, *n; | ||
73 | struct watchdog_device *wdd_tmp; | ||
74 | |||
75 | list_for_each_safe(p, n, &wtd_deferred_reg_list) { | ||
76 | wdd_tmp = list_entry(p, struct watchdog_device, | ||
77 | deferred); | ||
78 | if (wdd_tmp == wdd) { | ||
79 | list_del(&wdd_tmp->deferred); | ||
80 | break; | ||
81 | } | ||
82 | } | ||
83 | } | ||
84 | |||
46 | static void watchdog_check_min_max_timeout(struct watchdog_device *wdd) | 85 | static void watchdog_check_min_max_timeout(struct watchdog_device *wdd) |
47 | { | 86 | { |
48 | /* | 87 | /* |
@@ -98,17 +137,7 @@ int watchdog_init_timeout(struct watchdog_device *wdd, | |||
98 | } | 137 | } |
99 | EXPORT_SYMBOL_GPL(watchdog_init_timeout); | 138 | EXPORT_SYMBOL_GPL(watchdog_init_timeout); |
100 | 139 | ||
101 | /** | 140 | static int __watchdog_register_device(struct watchdog_device *wdd) |
102 | * watchdog_register_device() - register a watchdog device | ||
103 | * @wdd: watchdog device | ||
104 | * | ||
105 | * Register a watchdog device with the kernel so that the | ||
106 | * watchdog timer can be accessed from userspace. | ||
107 | * | ||
108 | * A zero is returned on success and a negative errno code for | ||
109 | * failure. | ||
110 | */ | ||
111 | int watchdog_register_device(struct watchdog_device *wdd) | ||
112 | { | 141 | { |
113 | int ret, id, devno; | 142 | int ret, id, devno; |
114 | 143 | ||
@@ -164,16 +193,33 @@ int watchdog_register_device(struct watchdog_device *wdd) | |||
164 | 193 | ||
165 | return 0; | 194 | return 0; |
166 | } | 195 | } |
167 | EXPORT_SYMBOL_GPL(watchdog_register_device); | ||
168 | 196 | ||
169 | /** | 197 | /** |
170 | * watchdog_unregister_device() - unregister a watchdog device | 198 | * watchdog_register_device() - register a watchdog device |
171 | * @wdd: watchdog device to unregister | 199 | * @wdd: watchdog device |
172 | * | 200 | * |
173 | * Unregister a watchdog device that was previously successfully | 201 | * Register a watchdog device with the kernel so that the |
174 | * registered with watchdog_register_device(). | 202 | * watchdog timer can be accessed from userspace. |
203 | * | ||
204 | * A zero is returned on success and a negative errno code for | ||
205 | * failure. | ||
175 | */ | 206 | */ |
176 | void watchdog_unregister_device(struct watchdog_device *wdd) | 207 | |
208 | int watchdog_register_device(struct watchdog_device *wdd) | ||
209 | { | ||
210 | int ret; | ||
211 | |||
212 | mutex_lock(&wtd_deferred_reg_mutex); | ||
213 | if (wtd_deferred_reg_done) | ||
214 | ret = __watchdog_register_device(wdd); | ||
215 | else | ||
216 | ret = watchdog_deferred_registration_add(wdd); | ||
217 | mutex_unlock(&wtd_deferred_reg_mutex); | ||
218 | return ret; | ||
219 | } | ||
220 | EXPORT_SYMBOL_GPL(watchdog_register_device); | ||
221 | |||
222 | static void __watchdog_unregister_device(struct watchdog_device *wdd) | ||
177 | { | 223 | { |
178 | int ret; | 224 | int ret; |
179 | int devno; | 225 | int devno; |
@@ -189,8 +235,43 @@ void watchdog_unregister_device(struct watchdog_device *wdd) | |||
189 | ida_simple_remove(&watchdog_ida, wdd->id); | 235 | ida_simple_remove(&watchdog_ida, wdd->id); |
190 | wdd->dev = NULL; | 236 | wdd->dev = NULL; |
191 | } | 237 | } |
238 | |||
239 | /** | ||
240 | * watchdog_unregister_device() - unregister a watchdog device | ||
241 | * @wdd: watchdog device to unregister | ||
242 | * | ||
243 | * Unregister a watchdog device that was previously successfully | ||
244 | * registered with watchdog_register_device(). | ||
245 | */ | ||
246 | |||
247 | void watchdog_unregister_device(struct watchdog_device *wdd) | ||
248 | { | ||
249 | mutex_lock(&wtd_deferred_reg_mutex); | ||
250 | if (wtd_deferred_reg_done) | ||
251 | __watchdog_unregister_device(wdd); | ||
252 | else | ||
253 | watchdog_deferred_registration_del(wdd); | ||
254 | mutex_unlock(&wtd_deferred_reg_mutex); | ||
255 | } | ||
256 | |||
192 | EXPORT_SYMBOL_GPL(watchdog_unregister_device); | 257 | EXPORT_SYMBOL_GPL(watchdog_unregister_device); |
193 | 258 | ||
259 | static int __init watchdog_deferred_registration(void) | ||
260 | { | ||
261 | mutex_lock(&wtd_deferred_reg_mutex); | ||
262 | wtd_deferred_reg_done = true; | ||
263 | while (!list_empty(&wtd_deferred_reg_list)) { | ||
264 | struct watchdog_device *wdd; | ||
265 | |||
266 | wdd = list_first_entry(&wtd_deferred_reg_list, | ||
267 | struct watchdog_device, deferred); | ||
268 | list_del(&wdd->deferred); | ||
269 | __watchdog_register_device(wdd); | ||
270 | } | ||
271 | mutex_unlock(&wtd_deferred_reg_mutex); | ||
272 | return 0; | ||
273 | } | ||
274 | |||
194 | static int __init watchdog_init(void) | 275 | static int __init watchdog_init(void) |
195 | { | 276 | { |
196 | int err; | 277 | int err; |
@@ -207,6 +288,7 @@ static int __init watchdog_init(void) | |||
207 | return err; | 288 | return err; |
208 | } | 289 | } |
209 | 290 | ||
291 | watchdog_deferred_registration(); | ||
210 | return 0; | 292 | return 0; |
211 | } | 293 | } |
212 | 294 | ||
@@ -217,7 +299,7 @@ static void __exit watchdog_exit(void) | |||
217 | ida_destroy(&watchdog_ida); | 299 | ida_destroy(&watchdog_ida); |
218 | } | 300 | } |
219 | 301 | ||
220 | subsys_initcall(watchdog_init); | 302 | subsys_initcall_sync(watchdog_init); |
221 | module_exit(watchdog_exit); | 303 | module_exit(watchdog_exit); |
222 | 304 | ||
223 | MODULE_AUTHOR("Alan Cox <alan@lxorguk.ukuu.org.uk>"); | 305 | MODULE_AUTHOR("Alan Cox <alan@lxorguk.ukuu.org.uk>"); |