diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2012-12-21 20:10:29 -0500 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2012-12-21 20:10:29 -0500 |
commit | 4fe19a136a8871e5fc6e44d72979f18a4968c2ab (patch) | |
tree | 785366514a250acd3f38f91686f2c886dc34cfb9 /drivers | |
parent | 769cb858c23ba7379ea27208624b444cd7b61af2 (diff) | |
parent | d692170037c0338b31dac5ac4722c1360a4b5257 (diff) |
Merge git://www.linux-watchdog.org/linux-watchdog
Pull watchdog updates from Wim Van Sebroeck:
"This includes some fixes and code improvements (like
clk_prepare_enable and clk_disable_unprepare), conversion from the
omap_wdt and twl4030_wdt drivers to the watchdog framework, addition
of the SB8x0 chipset support and the DA9055 Watchdog driver and some
OF support for the davinci_wdt driver."
* git://www.linux-watchdog.org/linux-watchdog: (22 commits)
watchdog: mei: avoid oops in watchdog unregister code path
watchdog: Orion: Fix possible null-deference in orion_wdt_probe
watchdog: sp5100_tco: Add SB8x0 chipset support
watchdog: davinci_wdt: add OF support
watchdog: da9052: Fix invalid free of devm_ allocated data
watchdog: twl4030_wdt: Change TWL4030_MODULE_PM_RECEIVER to TWL_MODULE_PM_RECEIVER
watchdog: remove depends on CONFIG_EXPERIMENTAL
watchdog: Convert dev_printk(KERN_<LEVEL> to dev_<level>(
watchdog: DA9055 Watchdog driver
watchdog: omap_wdt: eliminate goto
watchdog: omap_wdt: delete redundant platform_set_drvdata() calls
watchdog: omap_wdt: convert to devm_ functions
watchdog: omap_wdt: convert to new watchdog core
watchdog: WatchDog Timer Driver Core: fix comment
watchdog: s3c2410_wdt: use clk_prepare_enable and clk_disable_unprepare
watchdog: imx2_wdt: Select the driver via ARCH_MXC
watchdog: cpu5wdt.c: add missing del_timer call
watchdog: hpwdt.c: Increase version string
watchdog: Convert twl4030_wdt to watchdog core
davinci_wdt: preparation for switch to common clock framework
...
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/misc/mei/wd.c | 2 | ||||
-rw-r--r-- | drivers/watchdog/Kconfig | 18 | ||||
-rw-r--r-- | drivers/watchdog/Makefile | 1 | ||||
-rw-r--r-- | drivers/watchdog/ath79_wdt.c | 13 | ||||
-rw-r--r-- | drivers/watchdog/cpu5wdt.c | 1 | ||||
-rw-r--r-- | drivers/watchdog/da9052_wdt.c | 4 | ||||
-rw-r--r-- | drivers/watchdog/da9055_wdt.c | 216 | ||||
-rw-r--r-- | drivers/watchdog/davinci_wdt.c | 11 | ||||
-rw-r--r-- | drivers/watchdog/hpwdt.c | 2 | ||||
-rw-r--r-- | drivers/watchdog/mpcore_wdt.c | 19 | ||||
-rw-r--r-- | drivers/watchdog/omap_wdt.c | 311 | ||||
-rw-r--r-- | drivers/watchdog/orion_wdt.c | 2 | ||||
-rw-r--r-- | drivers/watchdog/s3c2410_wdt.c | 6 | ||||
-rw-r--r-- | drivers/watchdog/sp5100_tco.c | 321 | ||||
-rw-r--r-- | drivers/watchdog/sp5100_tco.h | 46 | ||||
-rw-r--r-- | drivers/watchdog/sp805_wdt.c | 11 | ||||
-rw-r--r-- | drivers/watchdog/twl4030_wdt.c | 185 |
17 files changed, 725 insertions, 444 deletions
diff --git a/drivers/misc/mei/wd.c b/drivers/misc/mei/wd.c index 636409f9667f..9299a8c29a6f 100644 --- a/drivers/misc/mei/wd.c +++ b/drivers/misc/mei/wd.c | |||
@@ -370,7 +370,7 @@ void mei_watchdog_register(struct mei_device *dev) | |||
370 | 370 | ||
371 | void mei_watchdog_unregister(struct mei_device *dev) | 371 | void mei_watchdog_unregister(struct mei_device *dev) |
372 | { | 372 | { |
373 | if (test_bit(WDOG_UNREGISTERED, &amt_wd_dev.status)) | 373 | if (watchdog_get_drvdata(&amt_wd_dev) == NULL) |
374 | return; | 374 | return; |
375 | 375 | ||
376 | watchdog_set_drvdata(&amt_wd_dev, NULL); | 376 | watchdog_set_drvdata(&amt_wd_dev, NULL); |
diff --git a/drivers/watchdog/Kconfig b/drivers/watchdog/Kconfig index ad1bb9382a96..7f809fd4a57f 100644 --- a/drivers/watchdog/Kconfig +++ b/drivers/watchdog/Kconfig | |||
@@ -76,6 +76,16 @@ config DA9052_WATCHDOG | |||
76 | Alternatively say M to compile the driver as a module, | 76 | Alternatively say M to compile the driver as a module, |
77 | which will be called da9052_wdt. | 77 | which will be called da9052_wdt. |
78 | 78 | ||
79 | config DA9055_WATCHDOG | ||
80 | tristate "Dialog Semiconductor DA9055 Watchdog" | ||
81 | depends on MFD_DA9055 | ||
82 | help | ||
83 | If you say yes here you get support for watchdog on the Dialog | ||
84 | Semiconductor DA9055 PMIC. | ||
85 | |||
86 | This driver can also be built as a module. If so, the module | ||
87 | will be called da9055_wdt. | ||
88 | |||
79 | config WM831X_WATCHDOG | 89 | config WM831X_WATCHDOG |
80 | tristate "WM831x watchdog" | 90 | tristate "WM831x watchdog" |
81 | depends on MFD_WM831X | 91 | depends on MFD_WM831X |
@@ -232,6 +242,7 @@ config EP93XX_WATCHDOG | |||
232 | config OMAP_WATCHDOG | 242 | config OMAP_WATCHDOG |
233 | tristate "OMAP Watchdog" | 243 | tristate "OMAP Watchdog" |
234 | depends on ARCH_OMAP16XX || ARCH_OMAP2PLUS | 244 | depends on ARCH_OMAP16XX || ARCH_OMAP2PLUS |
245 | select WATCHDOG_CORE | ||
235 | help | 246 | help |
236 | Support for TI OMAP1610/OMAP1710/OMAP2420/OMAP3430/OMAP4430 watchdog. Say 'Y' | 247 | Support for TI OMAP1610/OMAP1710/OMAP2420/OMAP3430/OMAP4430 watchdog. Say 'Y' |
237 | here to enable the OMAP1610/OMAP1710/OMAP2420/OMAP3430/OMAP4430 watchdog timer. | 248 | here to enable the OMAP1610/OMAP1710/OMAP2420/OMAP3430/OMAP4430 watchdog timer. |
@@ -300,6 +311,7 @@ config COH901327_WATCHDOG | |||
300 | config TWL4030_WATCHDOG | 311 | config TWL4030_WATCHDOG |
301 | tristate "TWL4030 Watchdog" | 312 | tristate "TWL4030 Watchdog" |
302 | depends on TWL4030_CORE | 313 | depends on TWL4030_CORE |
314 | select WATCHDOG_CORE | ||
303 | help | 315 | help |
304 | Support for TI TWL4030 watchdog. Say 'Y' here to enable the | 316 | Support for TI TWL4030 watchdog. Say 'Y' here to enable the |
305 | watchdog timer support for TWL4030 chips. | 317 | watchdog timer support for TWL4030 chips. |
@@ -342,7 +354,7 @@ config MAX63XX_WATCHDOG | |||
342 | 354 | ||
343 | config IMX2_WDT | 355 | config IMX2_WDT |
344 | tristate "IMX2+ Watchdog" | 356 | tristate "IMX2+ Watchdog" |
345 | depends on IMX_HAVE_PLATFORM_IMX2_WDT | 357 | depends on ARCH_MXC |
346 | help | 358 | help |
347 | This is the driver for the hardware watchdog | 359 | This is the driver for the hardware watchdog |
348 | on the Freescale IMX2 and later processors. | 360 | on the Freescale IMX2 and later processors. |
@@ -431,7 +443,7 @@ config ALIM7101_WDT | |||
431 | 443 | ||
432 | config F71808E_WDT | 444 | config F71808E_WDT |
433 | tristate "Fintek F71808E, F71862FG, F71869, F71882FG and F71889FG Watchdog" | 445 | tristate "Fintek F71808E, F71862FG, F71869, F71882FG and F71889FG Watchdog" |
434 | depends on X86 && EXPERIMENTAL | 446 | depends on X86 |
435 | help | 447 | help |
436 | This is the driver for the hardware watchdog on the Fintek | 448 | This is the driver for the hardware watchdog on the Fintek |
437 | F71808E, F71862FG, F71869, F71882FG and F71889FG Super I/O controllers. | 449 | F71808E, F71862FG, F71869, F71882FG and F71889FG Super I/O controllers. |
@@ -622,7 +634,7 @@ config IT8712F_WDT | |||
622 | 634 | ||
623 | config IT87_WDT | 635 | config IT87_WDT |
624 | tristate "IT87 Watchdog Timer" | 636 | tristate "IT87 Watchdog Timer" |
625 | depends on X86 && EXPERIMENTAL | 637 | depends on X86 |
626 | ---help--- | 638 | ---help--- |
627 | This is the driver for the hardware watchdog on the ITE IT8702, | 639 | This is the driver for the hardware watchdog on the ITE IT8702, |
628 | IT8712, IT8716, IT8718, IT8720, IT8721, IT8726 and IT8728 | 640 | IT8712, IT8716, IT8718, IT8720, IT8721, IT8726 and IT8728 |
diff --git a/drivers/watchdog/Makefile b/drivers/watchdog/Makefile index 572b39bed06a..97bbdb3a4648 100644 --- a/drivers/watchdog/Makefile +++ b/drivers/watchdog/Makefile | |||
@@ -164,6 +164,7 @@ obj-$(CONFIG_XEN_WDT) += xen_wdt.o | |||
164 | 164 | ||
165 | # Architecture Independent | 165 | # Architecture Independent |
166 | obj-$(CONFIG_DA9052_WATCHDOG) += da9052_wdt.o | 166 | obj-$(CONFIG_DA9052_WATCHDOG) += da9052_wdt.o |
167 | obj-$(CONFIG_DA9055_WATCHDOG) += da9055_wdt.o | ||
167 | obj-$(CONFIG_WM831X_WATCHDOG) += wm831x_wdt.o | 168 | obj-$(CONFIG_WM831X_WATCHDOG) += wm831x_wdt.o |
168 | obj-$(CONFIG_WM8350_WATCHDOG) += wm8350_wdt.o | 169 | obj-$(CONFIG_WM8350_WATCHDOG) += wm8350_wdt.o |
169 | obj-$(CONFIG_MAX63XX_WATCHDOG) += max63xx_wdt.o | 170 | obj-$(CONFIG_MAX63XX_WATCHDOG) += max63xx_wdt.o |
diff --git a/drivers/watchdog/ath79_wdt.c b/drivers/watchdog/ath79_wdt.c index 7c8ede7816b1..38a999e60c0d 100644 --- a/drivers/watchdog/ath79_wdt.c +++ b/drivers/watchdog/ath79_wdt.c | |||
@@ -284,6 +284,7 @@ static void ath97_wdt_shutdown(struct platform_device *pdev) | |||
284 | } | 284 | } |
285 | 285 | ||
286 | static struct platform_driver ath79_wdt_driver = { | 286 | static struct platform_driver ath79_wdt_driver = { |
287 | .probe = ath79_wdt_probe, | ||
287 | .remove = ath79_wdt_remove, | 288 | .remove = ath79_wdt_remove, |
288 | .shutdown = ath97_wdt_shutdown, | 289 | .shutdown = ath97_wdt_shutdown, |
289 | .driver = { | 290 | .driver = { |
@@ -292,17 +293,7 @@ static struct platform_driver ath79_wdt_driver = { | |||
292 | }, | 293 | }, |
293 | }; | 294 | }; |
294 | 295 | ||
295 | static int __init ath79_wdt_init(void) | 296 | module_platform_driver(ath79_wdt_driver); |
296 | { | ||
297 | return platform_driver_probe(&ath79_wdt_driver, ath79_wdt_probe); | ||
298 | } | ||
299 | module_init(ath79_wdt_init); | ||
300 | |||
301 | static void __exit ath79_wdt_exit(void) | ||
302 | { | ||
303 | platform_driver_unregister(&ath79_wdt_driver); | ||
304 | } | ||
305 | module_exit(ath79_wdt_exit); | ||
306 | 297 | ||
307 | MODULE_DESCRIPTION("Atheros AR71XX/AR724X/AR913X hardware watchdog driver"); | 298 | MODULE_DESCRIPTION("Atheros AR71XX/AR724X/AR913X hardware watchdog driver"); |
308 | MODULE_AUTHOR("Gabor Juhos <juhosg@openwrt.org"); | 299 | MODULE_AUTHOR("Gabor Juhos <juhosg@openwrt.org"); |
diff --git a/drivers/watchdog/cpu5wdt.c b/drivers/watchdog/cpu5wdt.c index cd87758abac3..f270bb7bc456 100644 --- a/drivers/watchdog/cpu5wdt.c +++ b/drivers/watchdog/cpu5wdt.c | |||
@@ -266,6 +266,7 @@ static void cpu5wdt_exit(void) | |||
266 | if (cpu5wdt_device.queue) { | 266 | if (cpu5wdt_device.queue) { |
267 | cpu5wdt_device.queue = 0; | 267 | cpu5wdt_device.queue = 0; |
268 | wait_for_completion(&cpu5wdt_device.stop); | 268 | wait_for_completion(&cpu5wdt_device.stop); |
269 | del_timer(&cpu5wdt_device.timer); | ||
269 | } | 270 | } |
270 | 271 | ||
271 | misc_deregister(&cpu5wdt_misc); | 272 | misc_deregister(&cpu5wdt_misc); |
diff --git a/drivers/watchdog/da9052_wdt.c b/drivers/watchdog/da9052_wdt.c index 8be70d8f2680..367445009c64 100644 --- a/drivers/watchdog/da9052_wdt.c +++ b/drivers/watchdog/da9052_wdt.c | |||
@@ -53,10 +53,6 @@ static const struct { | |||
53 | 53 | ||
54 | static void da9052_wdt_release_resources(struct kref *r) | 54 | static void da9052_wdt_release_resources(struct kref *r) |
55 | { | 55 | { |
56 | struct da9052_wdt_data *driver_data = | ||
57 | container_of(r, struct da9052_wdt_data, kref); | ||
58 | |||
59 | kfree(driver_data); | ||
60 | } | 56 | } |
61 | 57 | ||
62 | static int da9052_wdt_set_timeout(struct watchdog_device *wdt_dev, | 58 | static int da9052_wdt_set_timeout(struct watchdog_device *wdt_dev, |
diff --git a/drivers/watchdog/da9055_wdt.c b/drivers/watchdog/da9055_wdt.c new file mode 100644 index 000000000000..709ea1aefebb --- /dev/null +++ b/drivers/watchdog/da9055_wdt.c | |||
@@ -0,0 +1,216 @@ | |||
1 | /* | ||
2 | * System monitoring driver for DA9055 PMICs. | ||
3 | * | ||
4 | * Copyright(c) 2012 Dialog Semiconductor Ltd. | ||
5 | * | ||
6 | * Author: David Dajun Chen <dchen@diasemi.com> | ||
7 | * | ||
8 | * This program is free software; you can redistribute it and/or modify | ||
9 | * it under the terms of the GNU General Public License as published by | ||
10 | * the Free Software Foundation; either version 2 of the License, or | ||
11 | * (at your option) any later version. | ||
12 | * | ||
13 | */ | ||
14 | |||
15 | #include <linux/module.h> | ||
16 | #include <linux/types.h> | ||
17 | #include <linux/kernel.h> | ||
18 | #include <linux/slab.h> | ||
19 | #include <linux/platform_device.h> | ||
20 | #include <linux/watchdog.h> | ||
21 | #include <linux/delay.h> | ||
22 | |||
23 | #include <linux/mfd/da9055/core.h> | ||
24 | #include <linux/mfd/da9055/reg.h> | ||
25 | |||
26 | static bool nowayout = WATCHDOG_NOWAYOUT; | ||
27 | module_param(nowayout, bool, 0); | ||
28 | MODULE_PARM_DESC(nowayout, | ||
29 | "Watchdog cannot be stopped once started (default=" | ||
30 | __MODULE_STRING(WATCHDOG_NOWAYOUT) ")"); | ||
31 | |||
32 | #define DA9055_DEF_TIMEOUT 4 | ||
33 | #define DA9055_TWDMIN 256 | ||
34 | |||
35 | struct da9055_wdt_data { | ||
36 | struct watchdog_device wdt; | ||
37 | struct da9055 *da9055; | ||
38 | struct kref kref; | ||
39 | }; | ||
40 | |||
41 | static const struct { | ||
42 | u8 reg_val; | ||
43 | int user_time; /* In seconds */ | ||
44 | } da9055_wdt_maps[] = { | ||
45 | { 0, 0 }, | ||
46 | { 1, 2 }, | ||
47 | { 2, 4 }, | ||
48 | { 3, 8 }, | ||
49 | { 4, 16 }, | ||
50 | { 5, 32 }, | ||
51 | { 5, 33 }, /* Actual time 32.768s so included both 32s and 33s */ | ||
52 | { 6, 65 }, | ||
53 | { 6, 66 }, /* Actual time 65.536s so include both, 65s and 66s */ | ||
54 | { 7, 131 }, | ||
55 | }; | ||
56 | |||
57 | static int da9055_wdt_set_timeout(struct watchdog_device *wdt_dev, | ||
58 | unsigned int timeout) | ||
59 | { | ||
60 | struct da9055_wdt_data *driver_data = watchdog_get_drvdata(wdt_dev); | ||
61 | struct da9055 *da9055 = driver_data->da9055; | ||
62 | int ret, i; | ||
63 | |||
64 | for (i = 0; i < ARRAY_SIZE(da9055_wdt_maps); i++) | ||
65 | if (da9055_wdt_maps[i].user_time == timeout) | ||
66 | break; | ||
67 | |||
68 | if (i == ARRAY_SIZE(da9055_wdt_maps)) | ||
69 | ret = -EINVAL; | ||
70 | else | ||
71 | ret = da9055_reg_update(da9055, DA9055_REG_CONTROL_B, | ||
72 | DA9055_TWDSCALE_MASK, | ||
73 | da9055_wdt_maps[i].reg_val << | ||
74 | DA9055_TWDSCALE_SHIFT); | ||
75 | if (ret < 0) | ||
76 | dev_err(da9055->dev, | ||
77 | "Failed to update timescale bit, %d\n", ret); | ||
78 | |||
79 | wdt_dev->timeout = timeout; | ||
80 | |||
81 | return ret; | ||
82 | } | ||
83 | |||
84 | static int da9055_wdt_ping(struct watchdog_device *wdt_dev) | ||
85 | { | ||
86 | struct da9055_wdt_data *driver_data = watchdog_get_drvdata(wdt_dev); | ||
87 | struct da9055 *da9055 = driver_data->da9055; | ||
88 | int ret; | ||
89 | |||
90 | /* | ||
91 | * We have a minimum time for watchdog window called TWDMIN. A write | ||
92 | * to the watchdog before this elapsed time will cause an error. | ||
93 | */ | ||
94 | mdelay(DA9055_TWDMIN); | ||
95 | |||
96 | /* Reset the watchdog timer */ | ||
97 | ret = da9055_reg_update(da9055, DA9055_REG_CONTROL_E, | ||
98 | DA9055_WATCHDOG_MASK, 1); | ||
99 | |||
100 | return ret; | ||
101 | } | ||
102 | |||
103 | static void da9055_wdt_release_resources(struct kref *r) | ||
104 | { | ||
105 | struct da9055_wdt_data *driver_data = | ||
106 | container_of(r, struct da9055_wdt_data, kref); | ||
107 | |||
108 | kfree(driver_data); | ||
109 | } | ||
110 | |||
111 | static void da9055_wdt_ref(struct watchdog_device *wdt_dev) | ||
112 | { | ||
113 | struct da9055_wdt_data *driver_data = watchdog_get_drvdata(wdt_dev); | ||
114 | |||
115 | kref_get(&driver_data->kref); | ||
116 | } | ||
117 | |||
118 | static void da9055_wdt_unref(struct watchdog_device *wdt_dev) | ||
119 | { | ||
120 | struct da9055_wdt_data *driver_data = watchdog_get_drvdata(wdt_dev); | ||
121 | |||
122 | kref_put(&driver_data->kref, da9055_wdt_release_resources); | ||
123 | } | ||
124 | |||
125 | static int da9055_wdt_start(struct watchdog_device *wdt_dev) | ||
126 | { | ||
127 | return da9055_wdt_set_timeout(wdt_dev, wdt_dev->timeout); | ||
128 | } | ||
129 | |||
130 | static int da9055_wdt_stop(struct watchdog_device *wdt_dev) | ||
131 | { | ||
132 | return da9055_wdt_set_timeout(wdt_dev, 0); | ||
133 | } | ||
134 | |||
135 | static struct watchdog_info da9055_wdt_info = { | ||
136 | .options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING, | ||
137 | .identity = "DA9055 Watchdog", | ||
138 | }; | ||
139 | |||
140 | static const struct watchdog_ops da9055_wdt_ops = { | ||
141 | .owner = THIS_MODULE, | ||
142 | .start = da9055_wdt_start, | ||
143 | .stop = da9055_wdt_stop, | ||
144 | .ping = da9055_wdt_ping, | ||
145 | .set_timeout = da9055_wdt_set_timeout, | ||
146 | .ref = da9055_wdt_ref, | ||
147 | .unref = da9055_wdt_unref, | ||
148 | }; | ||
149 | |||
150 | static int da9055_wdt_probe(struct platform_device *pdev) | ||
151 | { | ||
152 | struct da9055 *da9055 = dev_get_drvdata(pdev->dev.parent); | ||
153 | struct da9055_wdt_data *driver_data; | ||
154 | struct watchdog_device *da9055_wdt; | ||
155 | int ret; | ||
156 | |||
157 | driver_data = devm_kzalloc(&pdev->dev, sizeof(*driver_data), | ||
158 | GFP_KERNEL); | ||
159 | if (!driver_data) { | ||
160 | dev_err(da9055->dev, "Failed to allocate watchdog device\n"); | ||
161 | return -ENOMEM; | ||
162 | } | ||
163 | |||
164 | driver_data->da9055 = da9055; | ||
165 | |||
166 | da9055_wdt = &driver_data->wdt; | ||
167 | |||
168 | da9055_wdt->timeout = DA9055_DEF_TIMEOUT; | ||
169 | da9055_wdt->info = &da9055_wdt_info; | ||
170 | da9055_wdt->ops = &da9055_wdt_ops; | ||
171 | watchdog_set_nowayout(da9055_wdt, nowayout); | ||
172 | watchdog_set_drvdata(da9055_wdt, driver_data); | ||
173 | |||
174 | kref_init(&driver_data->kref); | ||
175 | |||
176 | ret = da9055_wdt_stop(da9055_wdt); | ||
177 | if (ret < 0) { | ||
178 | dev_err(&pdev->dev, "Failed to stop watchdog, %d\n", ret); | ||
179 | goto err; | ||
180 | } | ||
181 | |||
182 | dev_set_drvdata(&pdev->dev, driver_data); | ||
183 | |||
184 | ret = watchdog_register_device(&driver_data->wdt); | ||
185 | if (ret != 0) | ||
186 | dev_err(da9055->dev, "watchdog_register_device() failed: %d\n", | ||
187 | ret); | ||
188 | |||
189 | err: | ||
190 | return ret; | ||
191 | } | ||
192 | |||
193 | static int da9055_wdt_remove(struct platform_device *pdev) | ||
194 | { | ||
195 | struct da9055_wdt_data *driver_data = dev_get_drvdata(&pdev->dev); | ||
196 | |||
197 | watchdog_unregister_device(&driver_data->wdt); | ||
198 | kref_put(&driver_data->kref, da9055_wdt_release_resources); | ||
199 | |||
200 | return 0; | ||
201 | } | ||
202 | |||
203 | static struct platform_driver da9055_wdt_driver = { | ||
204 | .probe = da9055_wdt_probe, | ||
205 | .remove = da9055_wdt_remove, | ||
206 | .driver = { | ||
207 | .name = "da9055-watchdog", | ||
208 | }, | ||
209 | }; | ||
210 | |||
211 | module_platform_driver(da9055_wdt_driver); | ||
212 | |||
213 | MODULE_AUTHOR("David Dajun Chen <dchen@diasemi.com>"); | ||
214 | MODULE_DESCRIPTION("DA9055 watchdog"); | ||
215 | MODULE_LICENSE("GPL"); | ||
216 | MODULE_ALIAS("platform:da9055-watchdog"); | ||
diff --git a/drivers/watchdog/davinci_wdt.c b/drivers/watchdog/davinci_wdt.c index 8791879e5181..e8e87246ea6d 100644 --- a/drivers/watchdog/davinci_wdt.c +++ b/drivers/watchdog/davinci_wdt.c | |||
@@ -208,7 +208,7 @@ static int davinci_wdt_probe(struct platform_device *pdev) | |||
208 | if (WARN_ON(IS_ERR(wdt_clk))) | 208 | if (WARN_ON(IS_ERR(wdt_clk))) |
209 | return PTR_ERR(wdt_clk); | 209 | return PTR_ERR(wdt_clk); |
210 | 210 | ||
211 | clk_enable(wdt_clk); | 211 | clk_prepare_enable(wdt_clk); |
212 | 212 | ||
213 | if (heartbeat < 1 || heartbeat > MAX_HEARTBEAT) | 213 | if (heartbeat < 1 || heartbeat > MAX_HEARTBEAT) |
214 | heartbeat = DEFAULT_HEARTBEAT; | 214 | heartbeat = DEFAULT_HEARTBEAT; |
@@ -256,16 +256,23 @@ static int davinci_wdt_remove(struct platform_device *pdev) | |||
256 | wdt_mem = NULL; | 256 | wdt_mem = NULL; |
257 | } | 257 | } |
258 | 258 | ||
259 | clk_disable(wdt_clk); | 259 | clk_disable_unprepare(wdt_clk); |
260 | clk_put(wdt_clk); | 260 | clk_put(wdt_clk); |
261 | 261 | ||
262 | return 0; | 262 | return 0; |
263 | } | 263 | } |
264 | 264 | ||
265 | static const struct of_device_id davinci_wdt_of_match[] = { | ||
266 | { .compatible = "ti,davinci-wdt", }, | ||
267 | {}, | ||
268 | }; | ||
269 | MODULE_DEVICE_TABLE(of, davinci_wdt_of_match); | ||
270 | |||
265 | static struct platform_driver platform_wdt_driver = { | 271 | static struct platform_driver platform_wdt_driver = { |
266 | .driver = { | 272 | .driver = { |
267 | .name = "watchdog", | 273 | .name = "watchdog", |
268 | .owner = THIS_MODULE, | 274 | .owner = THIS_MODULE, |
275 | .of_match_table = davinci_wdt_of_match, | ||
269 | }, | 276 | }, |
270 | .probe = davinci_wdt_probe, | 277 | .probe = davinci_wdt_probe, |
271 | .remove = davinci_wdt_remove, | 278 | .remove = davinci_wdt_remove, |
diff --git a/drivers/watchdog/hpwdt.c b/drivers/watchdog/hpwdt.c index 8717255ec7be..11796b9b864e 100644 --- a/drivers/watchdog/hpwdt.c +++ b/drivers/watchdog/hpwdt.c | |||
@@ -39,7 +39,7 @@ | |||
39 | #endif /* CONFIG_HPWDT_NMI_DECODING */ | 39 | #endif /* CONFIG_HPWDT_NMI_DECODING */ |
40 | #include <asm/nmi.h> | 40 | #include <asm/nmi.h> |
41 | 41 | ||
42 | #define HPWDT_VERSION "1.3.0" | 42 | #define HPWDT_VERSION "1.3.1" |
43 | #define SECS_TO_TICKS(secs) ((secs) * 1000 / 128) | 43 | #define SECS_TO_TICKS(secs) ((secs) * 1000 / 128) |
44 | #define TICKS_TO_SECS(ticks) ((ticks) * 128 / 1000) | 44 | #define TICKS_TO_SECS(ticks) ((ticks) * 128 / 1000) |
45 | #define HPWDT_MAX_TIMER TICKS_TO_SECS(65535) | 45 | #define HPWDT_MAX_TIMER TICKS_TO_SECS(65535) |
diff --git a/drivers/watchdog/mpcore_wdt.c b/drivers/watchdog/mpcore_wdt.c index a84eb551ea27..233cfadcb21f 100644 --- a/drivers/watchdog/mpcore_wdt.c +++ b/drivers/watchdog/mpcore_wdt.c | |||
@@ -80,8 +80,7 @@ static irqreturn_t mpcore_wdt_fire(int irq, void *arg) | |||
80 | 80 | ||
81 | /* Check it really was our interrupt */ | 81 | /* Check it really was our interrupt */ |
82 | if (readl(wdt->base + TWD_WDOG_INTSTAT)) { | 82 | if (readl(wdt->base + TWD_WDOG_INTSTAT)) { |
83 | dev_printk(KERN_CRIT, wdt->dev, | 83 | dev_crit(wdt->dev, "Triggered - Reboot ignored\n"); |
84 | "Triggered - Reboot ignored.\n"); | ||
85 | /* Clear the interrupt on the watchdog */ | 84 | /* Clear the interrupt on the watchdog */ |
86 | writel(1, wdt->base + TWD_WDOG_INTSTAT); | 85 | writel(1, wdt->base + TWD_WDOG_INTSTAT); |
87 | return IRQ_HANDLED; | 86 | return IRQ_HANDLED; |
@@ -123,7 +122,7 @@ static void mpcore_wdt_stop(struct mpcore_wdt *wdt) | |||
123 | 122 | ||
124 | static void mpcore_wdt_start(struct mpcore_wdt *wdt) | 123 | static void mpcore_wdt_start(struct mpcore_wdt *wdt) |
125 | { | 124 | { |
126 | dev_printk(KERN_INFO, wdt->dev, "enabling watchdog.\n"); | 125 | dev_info(wdt->dev, "enabling watchdog\n"); |
127 | 126 | ||
128 | /* This loads the count register but does NOT start the count yet */ | 127 | /* This loads the count register but does NOT start the count yet */ |
129 | mpcore_wdt_keepalive(wdt); | 128 | mpcore_wdt_keepalive(wdt); |
@@ -180,8 +179,8 @@ static int mpcore_wdt_release(struct inode *inode, struct file *file) | |||
180 | if (wdt->expect_close == 42) | 179 | if (wdt->expect_close == 42) |
181 | mpcore_wdt_stop(wdt); | 180 | mpcore_wdt_stop(wdt); |
182 | else { | 181 | else { |
183 | dev_printk(KERN_CRIT, wdt->dev, | 182 | dev_crit(wdt->dev, |
184 | "unexpected close, not stopping watchdog!\n"); | 183 | "unexpected close, not stopping watchdog!\n"); |
185 | mpcore_wdt_keepalive(wdt); | 184 | mpcore_wdt_keepalive(wdt); |
186 | } | 185 | } |
187 | clear_bit(0, &wdt->timer_alive); | 186 | clear_bit(0, &wdt->timer_alive); |
@@ -351,9 +350,9 @@ static int mpcore_wdt_probe(struct platform_device *pdev) | |||
351 | ret = devm_request_irq(wdt->dev, wdt->irq, mpcore_wdt_fire, 0, | 350 | ret = devm_request_irq(wdt->dev, wdt->irq, mpcore_wdt_fire, 0, |
352 | "mpcore_wdt", wdt); | 351 | "mpcore_wdt", wdt); |
353 | if (ret) { | 352 | if (ret) { |
354 | dev_printk(KERN_ERR, wdt->dev, | 353 | dev_err(wdt->dev, |
355 | "cannot register IRQ%d for watchdog\n", | 354 | "cannot register IRQ%d for watchdog\n", |
356 | wdt->irq); | 355 | wdt->irq); |
357 | return ret; | 356 | return ret; |
358 | } | 357 | } |
359 | } | 358 | } |
@@ -365,9 +364,9 @@ static int mpcore_wdt_probe(struct platform_device *pdev) | |||
365 | mpcore_wdt_miscdev.parent = &pdev->dev; | 364 | mpcore_wdt_miscdev.parent = &pdev->dev; |
366 | ret = misc_register(&mpcore_wdt_miscdev); | 365 | ret = misc_register(&mpcore_wdt_miscdev); |
367 | if (ret) { | 366 | if (ret) { |
368 | dev_printk(KERN_ERR, wdt->dev, | 367 | dev_err(wdt->dev, |
369 | "cannot register miscdev on minor=%d (err=%d)\n", | 368 | "cannot register miscdev on minor=%d (err=%d)\n", |
370 | WATCHDOG_MINOR, ret); | 369 | WATCHDOG_MINOR, ret); |
371 | return ret; | 370 | return ret; |
372 | } | 371 | } |
373 | 372 | ||
diff --git a/drivers/watchdog/omap_wdt.c b/drivers/watchdog/omap_wdt.c index 3e3ebbc83faf..34ed61ea02b4 100644 --- a/drivers/watchdog/omap_wdt.c +++ b/drivers/watchdog/omap_wdt.c | |||
@@ -31,42 +31,34 @@ | |||
31 | #include <linux/module.h> | 31 | #include <linux/module.h> |
32 | #include <linux/types.h> | 32 | #include <linux/types.h> |
33 | #include <linux/kernel.h> | 33 | #include <linux/kernel.h> |
34 | #include <linux/fs.h> | ||
35 | #include <linux/mm.h> | 34 | #include <linux/mm.h> |
36 | #include <linux/miscdevice.h> | ||
37 | #include <linux/watchdog.h> | 35 | #include <linux/watchdog.h> |
38 | #include <linux/reboot.h> | 36 | #include <linux/reboot.h> |
39 | #include <linux/init.h> | 37 | #include <linux/init.h> |
40 | #include <linux/err.h> | 38 | #include <linux/err.h> |
41 | #include <linux/platform_device.h> | 39 | #include <linux/platform_device.h> |
42 | #include <linux/moduleparam.h> | 40 | #include <linux/moduleparam.h> |
43 | #include <linux/bitops.h> | ||
44 | #include <linux/io.h> | 41 | #include <linux/io.h> |
45 | #include <linux/uaccess.h> | ||
46 | #include <linux/slab.h> | 42 | #include <linux/slab.h> |
47 | #include <linux/pm_runtime.h> | 43 | #include <linux/pm_runtime.h> |
48 | #include <linux/platform_data/omap-wd-timer.h> | 44 | #include <linux/platform_data/omap-wd-timer.h> |
49 | 45 | ||
50 | #include "omap_wdt.h" | 46 | #include "omap_wdt.h" |
51 | 47 | ||
52 | static struct platform_device *omap_wdt_dev; | ||
53 | |||
54 | static unsigned timer_margin; | 48 | static unsigned timer_margin; |
55 | module_param(timer_margin, uint, 0); | 49 | module_param(timer_margin, uint, 0); |
56 | MODULE_PARM_DESC(timer_margin, "initial watchdog timeout (in seconds)"); | 50 | MODULE_PARM_DESC(timer_margin, "initial watchdog timeout (in seconds)"); |
57 | 51 | ||
58 | static unsigned int wdt_trgr_pattern = 0x1234; | ||
59 | static DEFINE_SPINLOCK(wdt_lock); | ||
60 | |||
61 | struct omap_wdt_dev { | 52 | struct omap_wdt_dev { |
62 | void __iomem *base; /* physical */ | 53 | void __iomem *base; /* physical */ |
63 | struct device *dev; | 54 | struct device *dev; |
64 | int omap_wdt_users; | 55 | bool omap_wdt_users; |
65 | struct resource *mem; | 56 | struct resource *mem; |
66 | struct miscdevice omap_wdt_miscdev; | 57 | int wdt_trgr_pattern; |
58 | struct mutex lock; /* to avoid races with PM */ | ||
67 | }; | 59 | }; |
68 | 60 | ||
69 | static void omap_wdt_ping(struct omap_wdt_dev *wdev) | 61 | static void omap_wdt_reload(struct omap_wdt_dev *wdev) |
70 | { | 62 | { |
71 | void __iomem *base = wdev->base; | 63 | void __iomem *base = wdev->base; |
72 | 64 | ||
@@ -74,8 +66,8 @@ static void omap_wdt_ping(struct omap_wdt_dev *wdev) | |||
74 | while ((__raw_readl(base + OMAP_WATCHDOG_WPS)) & 0x08) | 66 | while ((__raw_readl(base + OMAP_WATCHDOG_WPS)) & 0x08) |
75 | cpu_relax(); | 67 | cpu_relax(); |
76 | 68 | ||
77 | wdt_trgr_pattern = ~wdt_trgr_pattern; | 69 | wdev->wdt_trgr_pattern = ~wdev->wdt_trgr_pattern; |
78 | __raw_writel(wdt_trgr_pattern, (base + OMAP_WATCHDOG_TGR)); | 70 | __raw_writel(wdev->wdt_trgr_pattern, (base + OMAP_WATCHDOG_TGR)); |
79 | 71 | ||
80 | /* wait for posted write to complete */ | 72 | /* wait for posted write to complete */ |
81 | while ((__raw_readl(base + OMAP_WATCHDOG_WPS)) & 0x08) | 73 | while ((__raw_readl(base + OMAP_WATCHDOG_WPS)) & 0x08) |
@@ -111,18 +103,10 @@ static void omap_wdt_disable(struct omap_wdt_dev *wdev) | |||
111 | cpu_relax(); | 103 | cpu_relax(); |
112 | } | 104 | } |
113 | 105 | ||
114 | static void omap_wdt_adjust_timeout(unsigned new_timeout) | 106 | static void omap_wdt_set_timer(struct omap_wdt_dev *wdev, |
115 | { | 107 | unsigned int timeout) |
116 | if (new_timeout < TIMER_MARGIN_MIN) | ||
117 | new_timeout = TIMER_MARGIN_DEFAULT; | ||
118 | if (new_timeout > TIMER_MARGIN_MAX) | ||
119 | new_timeout = TIMER_MARGIN_MAX; | ||
120 | timer_margin = new_timeout; | ||
121 | } | ||
122 | |||
123 | static void omap_wdt_set_timeout(struct omap_wdt_dev *wdev) | ||
124 | { | 108 | { |
125 | u32 pre_margin = GET_WLDR_VAL(timer_margin); | 109 | u32 pre_margin = GET_WLDR_VAL(timeout); |
126 | void __iomem *base = wdev->base; | 110 | void __iomem *base = wdev->base; |
127 | 111 | ||
128 | /* just count up at 32 KHz */ | 112 | /* just count up at 32 KHz */ |
@@ -134,16 +118,14 @@ static void omap_wdt_set_timeout(struct omap_wdt_dev *wdev) | |||
134 | cpu_relax(); | 118 | cpu_relax(); |
135 | } | 119 | } |
136 | 120 | ||
137 | /* | 121 | static int omap_wdt_start(struct watchdog_device *wdog) |
138 | * Allow only one task to hold it open | ||
139 | */ | ||
140 | static int omap_wdt_open(struct inode *inode, struct file *file) | ||
141 | { | 122 | { |
142 | struct omap_wdt_dev *wdev = platform_get_drvdata(omap_wdt_dev); | 123 | struct omap_wdt_dev *wdev = watchdog_get_drvdata(wdog); |
143 | void __iomem *base = wdev->base; | 124 | void __iomem *base = wdev->base; |
144 | 125 | ||
145 | if (test_and_set_bit(1, (unsigned long *)&(wdev->omap_wdt_users))) | 126 | mutex_lock(&wdev->lock); |
146 | return -EBUSY; | 127 | |
128 | wdev->omap_wdt_users = true; | ||
147 | 129 | ||
148 | pm_runtime_get_sync(wdev->dev); | 130 | pm_runtime_get_sync(wdev->dev); |
149 | 131 | ||
@@ -155,223 +137,169 @@ static int omap_wdt_open(struct inode *inode, struct file *file) | |||
155 | while (__raw_readl(base + OMAP_WATCHDOG_WPS) & 0x01) | 137 | while (__raw_readl(base + OMAP_WATCHDOG_WPS) & 0x01) |
156 | cpu_relax(); | 138 | cpu_relax(); |
157 | 139 | ||
158 | file->private_data = (void *) wdev; | 140 | omap_wdt_set_timer(wdev, wdog->timeout); |
159 | 141 | omap_wdt_reload(wdev); /* trigger loading of new timeout value */ | |
160 | omap_wdt_set_timeout(wdev); | ||
161 | omap_wdt_ping(wdev); /* trigger loading of new timeout value */ | ||
162 | omap_wdt_enable(wdev); | 142 | omap_wdt_enable(wdev); |
163 | 143 | ||
164 | return nonseekable_open(inode, file); | 144 | mutex_unlock(&wdev->lock); |
145 | |||
146 | return 0; | ||
165 | } | 147 | } |
166 | 148 | ||
167 | static int omap_wdt_release(struct inode *inode, struct file *file) | 149 | static int omap_wdt_stop(struct watchdog_device *wdog) |
168 | { | 150 | { |
169 | struct omap_wdt_dev *wdev = file->private_data; | 151 | struct omap_wdt_dev *wdev = watchdog_get_drvdata(wdog); |
170 | 152 | ||
171 | /* | 153 | mutex_lock(&wdev->lock); |
172 | * Shut off the timer unless NOWAYOUT is defined. | ||
173 | */ | ||
174 | #ifndef CONFIG_WATCHDOG_NOWAYOUT | ||
175 | omap_wdt_disable(wdev); | 154 | omap_wdt_disable(wdev); |
176 | |||
177 | pm_runtime_put_sync(wdev->dev); | 155 | pm_runtime_put_sync(wdev->dev); |
178 | #else | 156 | wdev->omap_wdt_users = false; |
179 | pr_crit("Unexpected close, not stopping!\n"); | 157 | mutex_unlock(&wdev->lock); |
180 | #endif | ||
181 | wdev->omap_wdt_users = 0; | ||
182 | |||
183 | return 0; | 158 | return 0; |
184 | } | 159 | } |
185 | 160 | ||
186 | static ssize_t omap_wdt_write(struct file *file, const char __user *data, | 161 | static int omap_wdt_ping(struct watchdog_device *wdog) |
187 | size_t len, loff_t *ppos) | ||
188 | { | 162 | { |
189 | struct omap_wdt_dev *wdev = file->private_data; | 163 | struct omap_wdt_dev *wdev = watchdog_get_drvdata(wdog); |
190 | 164 | ||
191 | /* Refresh LOAD_TIME. */ | 165 | mutex_lock(&wdev->lock); |
192 | if (len) { | 166 | omap_wdt_reload(wdev); |
193 | spin_lock(&wdt_lock); | 167 | mutex_unlock(&wdev->lock); |
194 | omap_wdt_ping(wdev); | 168 | |
195 | spin_unlock(&wdt_lock); | 169 | return 0; |
196 | } | ||
197 | return len; | ||
198 | } | 170 | } |
199 | 171 | ||
200 | static long omap_wdt_ioctl(struct file *file, unsigned int cmd, | 172 | static int omap_wdt_set_timeout(struct watchdog_device *wdog, |
201 | unsigned long arg) | 173 | unsigned int timeout) |
202 | { | 174 | { |
203 | struct omap_wd_timer_platform_data *pdata; | 175 | struct omap_wdt_dev *wdev = watchdog_get_drvdata(wdog); |
204 | struct omap_wdt_dev *wdev; | ||
205 | u32 rs; | ||
206 | int new_margin, bs; | ||
207 | static const struct watchdog_info ident = { | ||
208 | .identity = "OMAP Watchdog", | ||
209 | .options = WDIOF_SETTIMEOUT, | ||
210 | .firmware_version = 0, | ||
211 | }; | ||
212 | |||
213 | wdev = file->private_data; | ||
214 | pdata = wdev->dev->platform_data; | ||
215 | |||
216 | switch (cmd) { | ||
217 | case WDIOC_GETSUPPORT: | ||
218 | return copy_to_user((struct watchdog_info __user *)arg, &ident, | ||
219 | sizeof(ident)); | ||
220 | case WDIOC_GETSTATUS: | ||
221 | return put_user(0, (int __user *)arg); | ||
222 | case WDIOC_GETBOOTSTATUS: | ||
223 | if (!pdata || !pdata->read_reset_sources) | ||
224 | return put_user(0, (int __user *)arg); | ||
225 | rs = pdata->read_reset_sources(); | ||
226 | bs = (rs & (1 << OMAP_MPU_WD_RST_SRC_ID_SHIFT)) ? | ||
227 | WDIOF_CARDRESET : 0; | ||
228 | return put_user(bs, (int __user *)arg); | ||
229 | case WDIOC_KEEPALIVE: | ||
230 | spin_lock(&wdt_lock); | ||
231 | omap_wdt_ping(wdev); | ||
232 | spin_unlock(&wdt_lock); | ||
233 | return 0; | ||
234 | case WDIOC_SETTIMEOUT: | ||
235 | if (get_user(new_margin, (int __user *)arg)) | ||
236 | return -EFAULT; | ||
237 | omap_wdt_adjust_timeout(new_margin); | ||
238 | |||
239 | spin_lock(&wdt_lock); | ||
240 | omap_wdt_disable(wdev); | ||
241 | omap_wdt_set_timeout(wdev); | ||
242 | omap_wdt_enable(wdev); | ||
243 | 176 | ||
244 | omap_wdt_ping(wdev); | 177 | mutex_lock(&wdev->lock); |
245 | spin_unlock(&wdt_lock); | 178 | omap_wdt_disable(wdev); |
246 | /* Fall */ | 179 | omap_wdt_set_timer(wdev, timeout); |
247 | case WDIOC_GETTIMEOUT: | 180 | omap_wdt_enable(wdev); |
248 | return put_user(timer_margin, (int __user *)arg); | 181 | omap_wdt_reload(wdev); |
249 | default: | 182 | wdog->timeout = timeout; |
250 | return -ENOTTY; | 183 | mutex_unlock(&wdev->lock); |
251 | } | 184 | |
185 | return 0; | ||
252 | } | 186 | } |
253 | 187 | ||
254 | static const struct file_operations omap_wdt_fops = { | 188 | static const struct watchdog_info omap_wdt_info = { |
255 | .owner = THIS_MODULE, | 189 | .options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING, |
256 | .write = omap_wdt_write, | 190 | .identity = "OMAP Watchdog", |
257 | .unlocked_ioctl = omap_wdt_ioctl, | 191 | }; |
258 | .open = omap_wdt_open, | 192 | |
259 | .release = omap_wdt_release, | 193 | static const struct watchdog_ops omap_wdt_ops = { |
260 | .llseek = no_llseek, | 194 | .owner = THIS_MODULE, |
195 | .start = omap_wdt_start, | ||
196 | .stop = omap_wdt_stop, | ||
197 | .ping = omap_wdt_ping, | ||
198 | .set_timeout = omap_wdt_set_timeout, | ||
261 | }; | 199 | }; |
262 | 200 | ||
263 | static int omap_wdt_probe(struct platform_device *pdev) | 201 | static int omap_wdt_probe(struct platform_device *pdev) |
264 | { | 202 | { |
203 | struct omap_wd_timer_platform_data *pdata = pdev->dev.platform_data; | ||
204 | bool nowayout = WATCHDOG_NOWAYOUT; | ||
205 | struct watchdog_device *omap_wdt; | ||
265 | struct resource *res, *mem; | 206 | struct resource *res, *mem; |
266 | struct omap_wdt_dev *wdev; | 207 | struct omap_wdt_dev *wdev; |
208 | u32 rs; | ||
267 | int ret; | 209 | int ret; |
268 | 210 | ||
211 | omap_wdt = devm_kzalloc(&pdev->dev, sizeof(*omap_wdt), GFP_KERNEL); | ||
212 | if (!omap_wdt) | ||
213 | return -ENOMEM; | ||
214 | |||
269 | /* reserve static register mappings */ | 215 | /* reserve static register mappings */ |
270 | res = platform_get_resource(pdev, IORESOURCE_MEM, 0); | 216 | res = platform_get_resource(pdev, IORESOURCE_MEM, 0); |
271 | if (!res) { | 217 | if (!res) |
272 | ret = -ENOENT; | 218 | return -ENOENT; |
273 | goto err_get_resource; | ||
274 | } | ||
275 | 219 | ||
276 | if (omap_wdt_dev) { | 220 | mem = devm_request_mem_region(&pdev->dev, res->start, |
277 | ret = -EBUSY; | 221 | resource_size(res), pdev->name); |
278 | goto err_busy; | 222 | if (!mem) |
279 | } | 223 | return -EBUSY; |
280 | 224 | ||
281 | mem = request_mem_region(res->start, resource_size(res), pdev->name); | 225 | wdev = devm_kzalloc(&pdev->dev, sizeof(*wdev), GFP_KERNEL); |
282 | if (!mem) { | 226 | if (!wdev) |
283 | ret = -EBUSY; | 227 | return -ENOMEM; |
284 | goto err_busy; | ||
285 | } | ||
286 | 228 | ||
287 | wdev = kzalloc(sizeof(struct omap_wdt_dev), GFP_KERNEL); | 229 | wdev->omap_wdt_users = false; |
288 | if (!wdev) { | 230 | wdev->mem = mem; |
289 | ret = -ENOMEM; | 231 | wdev->dev = &pdev->dev; |
290 | goto err_kzalloc; | 232 | wdev->wdt_trgr_pattern = 0x1234; |
291 | } | 233 | mutex_init(&wdev->lock); |
292 | 234 | ||
293 | wdev->omap_wdt_users = 0; | 235 | wdev->base = devm_ioremap(&pdev->dev, res->start, resource_size(res)); |
294 | wdev->mem = mem; | 236 | if (!wdev->base) |
295 | wdev->dev = &pdev->dev; | 237 | return -ENOMEM; |
296 | 238 | ||
297 | wdev->base = ioremap(res->start, resource_size(res)); | 239 | omap_wdt->info = &omap_wdt_info; |
298 | if (!wdev->base) { | 240 | omap_wdt->ops = &omap_wdt_ops; |
299 | ret = -ENOMEM; | 241 | omap_wdt->min_timeout = TIMER_MARGIN_MIN; |
300 | goto err_ioremap; | 242 | omap_wdt->max_timeout = TIMER_MARGIN_MAX; |
301 | } | 243 | |
244 | if (timer_margin >= TIMER_MARGIN_MIN && | ||
245 | timer_margin <= TIMER_MARGIN_MAX) | ||
246 | omap_wdt->timeout = timer_margin; | ||
247 | else | ||
248 | omap_wdt->timeout = TIMER_MARGIN_DEFAULT; | ||
302 | 249 | ||
303 | platform_set_drvdata(pdev, wdev); | 250 | watchdog_set_drvdata(omap_wdt, wdev); |
251 | watchdog_set_nowayout(omap_wdt, nowayout); | ||
252 | |||
253 | platform_set_drvdata(pdev, omap_wdt); | ||
304 | 254 | ||
305 | pm_runtime_enable(wdev->dev); | 255 | pm_runtime_enable(wdev->dev); |
306 | pm_runtime_get_sync(wdev->dev); | 256 | pm_runtime_get_sync(wdev->dev); |
307 | 257 | ||
308 | omap_wdt_disable(wdev); | 258 | if (pdata && pdata->read_reset_sources) |
309 | omap_wdt_adjust_timeout(timer_margin); | 259 | rs = pdata->read_reset_sources(); |
260 | else | ||
261 | rs = 0; | ||
262 | omap_wdt->bootstatus = (rs & (1 << OMAP_MPU_WD_RST_SRC_ID_SHIFT)) ? | ||
263 | WDIOF_CARDRESET : 0; | ||
310 | 264 | ||
311 | wdev->omap_wdt_miscdev.parent = &pdev->dev; | 265 | omap_wdt_disable(wdev); |
312 | wdev->omap_wdt_miscdev.minor = WATCHDOG_MINOR; | ||
313 | wdev->omap_wdt_miscdev.name = "watchdog"; | ||
314 | wdev->omap_wdt_miscdev.fops = &omap_wdt_fops; | ||
315 | 266 | ||
316 | ret = misc_register(&(wdev->omap_wdt_miscdev)); | 267 | ret = watchdog_register_device(omap_wdt); |
317 | if (ret) | 268 | if (ret) { |
318 | goto err_misc; | 269 | pm_runtime_disable(wdev->dev); |
270 | return ret; | ||
271 | } | ||
319 | 272 | ||
320 | pr_info("OMAP Watchdog Timer Rev 0x%02x: initial timeout %d sec\n", | 273 | pr_info("OMAP Watchdog Timer Rev 0x%02x: initial timeout %d sec\n", |
321 | __raw_readl(wdev->base + OMAP_WATCHDOG_REV) & 0xFF, | 274 | __raw_readl(wdev->base + OMAP_WATCHDOG_REV) & 0xFF, |
322 | timer_margin); | 275 | omap_wdt->timeout); |
323 | 276 | ||
324 | pm_runtime_put_sync(wdev->dev); | 277 | pm_runtime_put_sync(wdev->dev); |
325 | 278 | ||
326 | omap_wdt_dev = pdev; | ||
327 | |||
328 | return 0; | 279 | return 0; |
329 | |||
330 | err_misc: | ||
331 | pm_runtime_disable(wdev->dev); | ||
332 | platform_set_drvdata(pdev, NULL); | ||
333 | iounmap(wdev->base); | ||
334 | |||
335 | err_ioremap: | ||
336 | wdev->base = NULL; | ||
337 | kfree(wdev); | ||
338 | |||
339 | err_kzalloc: | ||
340 | release_mem_region(res->start, resource_size(res)); | ||
341 | |||
342 | err_busy: | ||
343 | err_get_resource: | ||
344 | |||
345 | return ret; | ||
346 | } | 280 | } |
347 | 281 | ||
348 | static void omap_wdt_shutdown(struct platform_device *pdev) | 282 | static void omap_wdt_shutdown(struct platform_device *pdev) |
349 | { | 283 | { |
350 | struct omap_wdt_dev *wdev = platform_get_drvdata(pdev); | 284 | struct watchdog_device *wdog = platform_get_drvdata(pdev); |
285 | struct omap_wdt_dev *wdev = watchdog_get_drvdata(wdog); | ||
351 | 286 | ||
287 | mutex_lock(&wdev->lock); | ||
352 | if (wdev->omap_wdt_users) { | 288 | if (wdev->omap_wdt_users) { |
353 | omap_wdt_disable(wdev); | 289 | omap_wdt_disable(wdev); |
354 | pm_runtime_put_sync(wdev->dev); | 290 | pm_runtime_put_sync(wdev->dev); |
355 | } | 291 | } |
292 | mutex_unlock(&wdev->lock); | ||
356 | } | 293 | } |
357 | 294 | ||
358 | static int omap_wdt_remove(struct platform_device *pdev) | 295 | static int omap_wdt_remove(struct platform_device *pdev) |
359 | { | 296 | { |
360 | struct omap_wdt_dev *wdev = platform_get_drvdata(pdev); | 297 | struct watchdog_device *wdog = platform_get_drvdata(pdev); |
298 | struct omap_wdt_dev *wdev = watchdog_get_drvdata(wdog); | ||
361 | struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0); | 299 | struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0); |
362 | 300 | ||
363 | pm_runtime_disable(wdev->dev); | 301 | pm_runtime_disable(wdev->dev); |
364 | if (!res) | 302 | watchdog_unregister_device(wdog); |
365 | return -ENOENT; | ||
366 | |||
367 | misc_deregister(&(wdev->omap_wdt_miscdev)); | ||
368 | release_mem_region(res->start, resource_size(res)); | ||
369 | platform_set_drvdata(pdev, NULL); | ||
370 | |||
371 | iounmap(wdev->base); | ||
372 | |||
373 | kfree(wdev); | ||
374 | omap_wdt_dev = NULL; | ||
375 | 303 | ||
376 | return 0; | 304 | return 0; |
377 | } | 305 | } |
@@ -386,25 +314,31 @@ static int omap_wdt_remove(struct platform_device *pdev) | |||
386 | 314 | ||
387 | static int omap_wdt_suspend(struct platform_device *pdev, pm_message_t state) | 315 | static int omap_wdt_suspend(struct platform_device *pdev, pm_message_t state) |
388 | { | 316 | { |
389 | struct omap_wdt_dev *wdev = platform_get_drvdata(pdev); | 317 | struct watchdog_device *wdog = platform_get_drvdata(pdev); |
318 | struct omap_wdt_dev *wdev = watchdog_get_drvdata(wdog); | ||
390 | 319 | ||
320 | mutex_lock(&wdev->lock); | ||
391 | if (wdev->omap_wdt_users) { | 321 | if (wdev->omap_wdt_users) { |
392 | omap_wdt_disable(wdev); | 322 | omap_wdt_disable(wdev); |
393 | pm_runtime_put_sync(wdev->dev); | 323 | pm_runtime_put_sync(wdev->dev); |
394 | } | 324 | } |
325 | mutex_unlock(&wdev->lock); | ||
395 | 326 | ||
396 | return 0; | 327 | return 0; |
397 | } | 328 | } |
398 | 329 | ||
399 | static int omap_wdt_resume(struct platform_device *pdev) | 330 | static int omap_wdt_resume(struct platform_device *pdev) |
400 | { | 331 | { |
401 | struct omap_wdt_dev *wdev = platform_get_drvdata(pdev); | 332 | struct watchdog_device *wdog = platform_get_drvdata(pdev); |
333 | struct omap_wdt_dev *wdev = watchdog_get_drvdata(wdog); | ||
402 | 334 | ||
335 | mutex_lock(&wdev->lock); | ||
403 | if (wdev->omap_wdt_users) { | 336 | if (wdev->omap_wdt_users) { |
404 | pm_runtime_get_sync(wdev->dev); | 337 | pm_runtime_get_sync(wdev->dev); |
405 | omap_wdt_enable(wdev); | 338 | omap_wdt_enable(wdev); |
406 | omap_wdt_ping(wdev); | 339 | omap_wdt_reload(wdev); |
407 | } | 340 | } |
341 | mutex_unlock(&wdev->lock); | ||
408 | 342 | ||
409 | return 0; | 343 | return 0; |
410 | } | 344 | } |
@@ -437,5 +371,4 @@ module_platform_driver(omap_wdt_driver); | |||
437 | 371 | ||
438 | MODULE_AUTHOR("George G. Davis"); | 372 | MODULE_AUTHOR("George G. Davis"); |
439 | MODULE_LICENSE("GPL"); | 373 | MODULE_LICENSE("GPL"); |
440 | MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR); | ||
441 | MODULE_ALIAS("platform:omap_wdt"); | 374 | MODULE_ALIAS("platform:omap_wdt"); |
diff --git a/drivers/watchdog/orion_wdt.c b/drivers/watchdog/orion_wdt.c index 0478b001b1ef..7c18b3bffcf7 100644 --- a/drivers/watchdog/orion_wdt.c +++ b/drivers/watchdog/orion_wdt.c | |||
@@ -156,6 +156,8 @@ static int orion_wdt_probe(struct platform_device *pdev) | |||
156 | wdt_tclk = clk_get_rate(clk); | 156 | wdt_tclk = clk_get_rate(clk); |
157 | 157 | ||
158 | res = platform_get_resource(pdev, IORESOURCE_MEM, 0); | 158 | res = platform_get_resource(pdev, IORESOURCE_MEM, 0); |
159 | if (!res) | ||
160 | return -ENODEV; | ||
159 | wdt_reg = devm_ioremap(&pdev->dev, res->start, resource_size(res)); | 161 | wdt_reg = devm_ioremap(&pdev->dev, res->start, resource_size(res)); |
160 | if (!wdt_reg) | 162 | if (!wdt_reg) |
161 | return -ENOMEM; | 163 | return -ENOMEM; |
diff --git a/drivers/watchdog/s3c2410_wdt.c b/drivers/watchdog/s3c2410_wdt.c index b0dab10fc6a5..27bcd4e2c4a4 100644 --- a/drivers/watchdog/s3c2410_wdt.c +++ b/drivers/watchdog/s3c2410_wdt.c | |||
@@ -354,7 +354,7 @@ static int s3c2410wdt_probe(struct platform_device *pdev) | |||
354 | goto err_map; | 354 | goto err_map; |
355 | } | 355 | } |
356 | 356 | ||
357 | clk_enable(wdt_clock); | 357 | clk_prepare_enable(wdt_clock); |
358 | 358 | ||
359 | ret = s3c2410wdt_cpufreq_register(); | 359 | ret = s3c2410wdt_cpufreq_register(); |
360 | if (ret < 0) { | 360 | if (ret < 0) { |
@@ -421,7 +421,7 @@ static int s3c2410wdt_probe(struct platform_device *pdev) | |||
421 | s3c2410wdt_cpufreq_deregister(); | 421 | s3c2410wdt_cpufreq_deregister(); |
422 | 422 | ||
423 | err_clk: | 423 | err_clk: |
424 | clk_disable(wdt_clock); | 424 | clk_disable_unprepare(wdt_clock); |
425 | clk_put(wdt_clock); | 425 | clk_put(wdt_clock); |
426 | wdt_clock = NULL; | 426 | wdt_clock = NULL; |
427 | 427 | ||
@@ -445,7 +445,7 @@ static int s3c2410wdt_remove(struct platform_device *dev) | |||
445 | 445 | ||
446 | s3c2410wdt_cpufreq_deregister(); | 446 | s3c2410wdt_cpufreq_deregister(); |
447 | 447 | ||
448 | clk_disable(wdt_clock); | 448 | clk_disable_unprepare(wdt_clock); |
449 | clk_put(wdt_clock); | 449 | clk_put(wdt_clock); |
450 | wdt_clock = NULL; | 450 | wdt_clock = NULL; |
451 | 451 | ||
diff --git a/drivers/watchdog/sp5100_tco.c b/drivers/watchdog/sp5100_tco.c index b3876812ff07..2b0e000d4377 100644 --- a/drivers/watchdog/sp5100_tco.c +++ b/drivers/watchdog/sp5100_tco.c | |||
@@ -13,7 +13,9 @@ | |||
13 | * as published by the Free Software Foundation; either version | 13 | * as published by the Free Software Foundation; either version |
14 | * 2 of the License, or (at your option) any later version. | 14 | * 2 of the License, or (at your option) any later version. |
15 | * | 15 | * |
16 | * See AMD Publication 43009 "AMD SB700/710/750 Register Reference Guide" | 16 | * See AMD Publication 43009 "AMD SB700/710/750 Register Reference Guide", |
17 | * AMD Publication 45482 "AMD SB800-Series Southbridges Register | ||
18 | * Reference Guide" | ||
17 | */ | 19 | */ |
18 | 20 | ||
19 | /* | 21 | /* |
@@ -38,18 +40,24 @@ | |||
38 | #include "sp5100_tco.h" | 40 | #include "sp5100_tco.h" |
39 | 41 | ||
40 | /* Module and version information */ | 42 | /* Module and version information */ |
41 | #define TCO_VERSION "0.01" | 43 | #define TCO_VERSION "0.03" |
42 | #define TCO_MODULE_NAME "SP5100 TCO timer" | 44 | #define TCO_MODULE_NAME "SP5100 TCO timer" |
43 | #define TCO_DRIVER_NAME TCO_MODULE_NAME ", v" TCO_VERSION | 45 | #define TCO_DRIVER_NAME TCO_MODULE_NAME ", v" TCO_VERSION |
44 | 46 | ||
45 | /* internal variables */ | 47 | /* internal variables */ |
46 | static u32 tcobase_phys; | 48 | static u32 tcobase_phys; |
49 | static u32 resbase_phys; | ||
50 | static u32 tco_wdt_fired; | ||
47 | static void __iomem *tcobase; | 51 | static void __iomem *tcobase; |
48 | static unsigned int pm_iobase; | 52 | static unsigned int pm_iobase; |
49 | static DEFINE_SPINLOCK(tco_lock); /* Guards the hardware */ | 53 | static DEFINE_SPINLOCK(tco_lock); /* Guards the hardware */ |
50 | static unsigned long timer_alive; | 54 | static unsigned long timer_alive; |
51 | static char tco_expect_close; | 55 | static char tco_expect_close; |
52 | static struct pci_dev *sp5100_tco_pci; | 56 | static struct pci_dev *sp5100_tco_pci; |
57 | static struct resource wdt_res = { | ||
58 | .name = "Watchdog Timer", | ||
59 | .flags = IORESOURCE_MEM, | ||
60 | }; | ||
53 | 61 | ||
54 | /* the watchdog platform device */ | 62 | /* the watchdog platform device */ |
55 | static struct platform_device *sp5100_tco_platform_device; | 63 | static struct platform_device *sp5100_tco_platform_device; |
@@ -64,9 +72,15 @@ MODULE_PARM_DESC(heartbeat, "Watchdog heartbeat in seconds. (default=" | |||
64 | 72 | ||
65 | static bool nowayout = WATCHDOG_NOWAYOUT; | 73 | static bool nowayout = WATCHDOG_NOWAYOUT; |
66 | module_param(nowayout, bool, 0); | 74 | module_param(nowayout, bool, 0); |
67 | MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started" | 75 | MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started." |
68 | " (default=" __MODULE_STRING(WATCHDOG_NOWAYOUT) ")"); | 76 | " (default=" __MODULE_STRING(WATCHDOG_NOWAYOUT) ")"); |
69 | 77 | ||
78 | static unsigned int force_addr; | ||
79 | module_param(force_addr, uint, 0); | ||
80 | MODULE_PARM_DESC(force_addr, "Force the use of specified MMIO address." | ||
81 | " ONLY USE THIS PARAMETER IF YOU REALLY KNOW" | ||
82 | " WHAT YOU ARE DOING (default=none)"); | ||
83 | |||
70 | /* | 84 | /* |
71 | * Some TCO specific functions | 85 | * Some TCO specific functions |
72 | */ | 86 | */ |
@@ -122,6 +136,79 @@ static int tco_timer_set_heartbeat(int t) | |||
122 | return 0; | 136 | return 0; |
123 | } | 137 | } |
124 | 138 | ||
139 | static void tco_timer_enable(void) | ||
140 | { | ||
141 | int val; | ||
142 | |||
143 | if (sp5100_tco_pci->revision >= 0x40) { | ||
144 | /* For SB800 or later */ | ||
145 | /* Set the Watchdog timer resolution to 1 sec */ | ||
146 | outb(SB800_PM_WATCHDOG_CONFIG, SB800_IO_PM_INDEX_REG); | ||
147 | val = inb(SB800_IO_PM_DATA_REG); | ||
148 | val |= SB800_PM_WATCHDOG_SECOND_RES; | ||
149 | outb(val, SB800_IO_PM_DATA_REG); | ||
150 | |||
151 | /* Enable watchdog decode bit and watchdog timer */ | ||
152 | outb(SB800_PM_WATCHDOG_CONTROL, SB800_IO_PM_INDEX_REG); | ||
153 | val = inb(SB800_IO_PM_DATA_REG); | ||
154 | val |= SB800_PCI_WATCHDOG_DECODE_EN; | ||
155 | val &= ~SB800_PM_WATCHDOG_DISABLE; | ||
156 | outb(val, SB800_IO_PM_DATA_REG); | ||
157 | } else { | ||
158 | /* For SP5100 or SB7x0 */ | ||
159 | /* Enable watchdog decode bit */ | ||
160 | pci_read_config_dword(sp5100_tco_pci, | ||
161 | SP5100_PCI_WATCHDOG_MISC_REG, | ||
162 | &val); | ||
163 | |||
164 | val |= SP5100_PCI_WATCHDOG_DECODE_EN; | ||
165 | |||
166 | pci_write_config_dword(sp5100_tco_pci, | ||
167 | SP5100_PCI_WATCHDOG_MISC_REG, | ||
168 | val); | ||
169 | |||
170 | /* Enable Watchdog timer and set the resolution to 1 sec */ | ||
171 | outb(SP5100_PM_WATCHDOG_CONTROL, SP5100_IO_PM_INDEX_REG); | ||
172 | val = inb(SP5100_IO_PM_DATA_REG); | ||
173 | val |= SP5100_PM_WATCHDOG_SECOND_RES; | ||
174 | val &= ~SP5100_PM_WATCHDOG_DISABLE; | ||
175 | outb(val, SP5100_IO_PM_DATA_REG); | ||
176 | } | ||
177 | } | ||
178 | |||
179 | static void tco_timer_disable(void) | ||
180 | { | ||
181 | int val; | ||
182 | |||
183 | if (sp5100_tco_pci->revision >= 0x40) { | ||
184 | /* For SB800 or later */ | ||
185 | /* Enable watchdog decode bit and Disable watchdog timer */ | ||
186 | outb(SB800_PM_WATCHDOG_CONTROL, SB800_IO_PM_INDEX_REG); | ||
187 | val = inb(SB800_IO_PM_DATA_REG); | ||
188 | val |= SB800_PCI_WATCHDOG_DECODE_EN; | ||
189 | val |= SB800_PM_WATCHDOG_DISABLE; | ||
190 | outb(val, SB800_IO_PM_DATA_REG); | ||
191 | } else { | ||
192 | /* For SP5100 or SB7x0 */ | ||
193 | /* Enable watchdog decode bit */ | ||
194 | pci_read_config_dword(sp5100_tco_pci, | ||
195 | SP5100_PCI_WATCHDOG_MISC_REG, | ||
196 | &val); | ||
197 | |||
198 | val |= SP5100_PCI_WATCHDOG_DECODE_EN; | ||
199 | |||
200 | pci_write_config_dword(sp5100_tco_pci, | ||
201 | SP5100_PCI_WATCHDOG_MISC_REG, | ||
202 | val); | ||
203 | |||
204 | /* Disable Watchdog timer */ | ||
205 | outb(SP5100_PM_WATCHDOG_CONTROL, SP5100_IO_PM_INDEX_REG); | ||
206 | val = inb(SP5100_IO_PM_DATA_REG); | ||
207 | val |= SP5100_PM_WATCHDOG_DISABLE; | ||
208 | outb(val, SP5100_IO_PM_DATA_REG); | ||
209 | } | ||
210 | } | ||
211 | |||
125 | /* | 212 | /* |
126 | * /dev/watchdog handling | 213 | * /dev/watchdog handling |
127 | */ | 214 | */ |
@@ -270,11 +357,12 @@ MODULE_DEVICE_TABLE(pci, sp5100_tco_pci_tbl); | |||
270 | /* | 357 | /* |
271 | * Init & exit routines | 358 | * Init & exit routines |
272 | */ | 359 | */ |
273 | |||
274 | static unsigned char sp5100_tco_setupdevice(void) | 360 | static unsigned char sp5100_tco_setupdevice(void) |
275 | { | 361 | { |
276 | struct pci_dev *dev = NULL; | 362 | struct pci_dev *dev = NULL; |
363 | const char *dev_name = NULL; | ||
277 | u32 val; | 364 | u32 val; |
365 | u32 index_reg, data_reg, base_addr; | ||
278 | 366 | ||
279 | /* Match the PCI device */ | 367 | /* Match the PCI device */ |
280 | for_each_pci_dev(dev) { | 368 | for_each_pci_dev(dev) { |
@@ -287,29 +375,160 @@ static unsigned char sp5100_tco_setupdevice(void) | |||
287 | if (!sp5100_tco_pci) | 375 | if (!sp5100_tco_pci) |
288 | return 0; | 376 | return 0; |
289 | 377 | ||
378 | pr_info("PCI Revision ID: 0x%x\n", sp5100_tco_pci->revision); | ||
379 | |||
380 | /* | ||
381 | * Determine type of southbridge chipset. | ||
382 | */ | ||
383 | if (sp5100_tco_pci->revision >= 0x40) { | ||
384 | dev_name = SB800_DEVNAME; | ||
385 | index_reg = SB800_IO_PM_INDEX_REG; | ||
386 | data_reg = SB800_IO_PM_DATA_REG; | ||
387 | base_addr = SB800_PM_WATCHDOG_BASE; | ||
388 | } else { | ||
389 | dev_name = SP5100_DEVNAME; | ||
390 | index_reg = SP5100_IO_PM_INDEX_REG; | ||
391 | data_reg = SP5100_IO_PM_DATA_REG; | ||
392 | base_addr = SP5100_PM_WATCHDOG_BASE; | ||
393 | } | ||
394 | |||
290 | /* Request the IO ports used by this driver */ | 395 | /* Request the IO ports used by this driver */ |
291 | pm_iobase = SP5100_IO_PM_INDEX_REG; | 396 | pm_iobase = SP5100_IO_PM_INDEX_REG; |
292 | if (!request_region(pm_iobase, SP5100_PM_IOPORTS_SIZE, "SP5100 TCO")) { | 397 | if (!request_region(pm_iobase, SP5100_PM_IOPORTS_SIZE, dev_name)) { |
293 | pr_err("I/O address 0x%04x already in use\n", pm_iobase); | 398 | pr_err("I/O address 0x%04x already in use\n", pm_iobase); |
294 | goto exit; | 399 | goto exit; |
295 | } | 400 | } |
296 | 401 | ||
297 | /* Find the watchdog base address. */ | 402 | /* |
298 | outb(SP5100_PM_WATCHDOG_BASE3, SP5100_IO_PM_INDEX_REG); | 403 | * First, Find the watchdog timer MMIO address from indirect I/O. |
299 | val = inb(SP5100_IO_PM_DATA_REG); | 404 | */ |
300 | outb(SP5100_PM_WATCHDOG_BASE2, SP5100_IO_PM_INDEX_REG); | 405 | outb(base_addr+3, index_reg); |
301 | val = val << 8 | inb(SP5100_IO_PM_DATA_REG); | 406 | val = inb(data_reg); |
302 | outb(SP5100_PM_WATCHDOG_BASE1, SP5100_IO_PM_INDEX_REG); | 407 | outb(base_addr+2, index_reg); |
303 | val = val << 8 | inb(SP5100_IO_PM_DATA_REG); | 408 | val = val << 8 | inb(data_reg); |
304 | outb(SP5100_PM_WATCHDOG_BASE0, SP5100_IO_PM_INDEX_REG); | 409 | outb(base_addr+1, index_reg); |
305 | /* Low three bits of BASE0 are reserved. */ | 410 | val = val << 8 | inb(data_reg); |
306 | val = val << 8 | (inb(SP5100_IO_PM_DATA_REG) & 0xf8); | 411 | outb(base_addr+0, index_reg); |
412 | /* Low three bits of BASE are reserved */ | ||
413 | val = val << 8 | (inb(data_reg) & 0xf8); | ||
414 | |||
415 | pr_debug("Got 0x%04x from indirect I/O\n", val); | ||
416 | |||
417 | /* Check MMIO address conflict */ | ||
418 | if (request_mem_region_exclusive(val, SP5100_WDT_MEM_MAP_SIZE, | ||
419 | dev_name)) | ||
420 | goto setup_wdt; | ||
421 | else | ||
422 | pr_debug("MMIO address 0x%04x already in use\n", val); | ||
423 | |||
424 | /* | ||
425 | * Secondly, Find the watchdog timer MMIO address | ||
426 | * from SBResource_MMIO register. | ||
427 | */ | ||
428 | if (sp5100_tco_pci->revision >= 0x40) { | ||
429 | /* Read SBResource_MMIO from AcpiMmioEn(PM_Reg: 24h) */ | ||
430 | outb(SB800_PM_ACPI_MMIO_EN+3, SB800_IO_PM_INDEX_REG); | ||
431 | val = inb(SB800_IO_PM_DATA_REG); | ||
432 | outb(SB800_PM_ACPI_MMIO_EN+2, SB800_IO_PM_INDEX_REG); | ||
433 | val = val << 8 | inb(SB800_IO_PM_DATA_REG); | ||
434 | outb(SB800_PM_ACPI_MMIO_EN+1, SB800_IO_PM_INDEX_REG); | ||
435 | val = val << 8 | inb(SB800_IO_PM_DATA_REG); | ||
436 | outb(SB800_PM_ACPI_MMIO_EN+0, SB800_IO_PM_INDEX_REG); | ||
437 | val = val << 8 | inb(SB800_IO_PM_DATA_REG); | ||
438 | } else { | ||
439 | /* Read SBResource_MMIO from PCI config(PCI_Reg: 9Ch) */ | ||
440 | pci_read_config_dword(sp5100_tco_pci, | ||
441 | SP5100_SB_RESOURCE_MMIO_BASE, &val); | ||
442 | } | ||
443 | |||
444 | /* The SBResource_MMIO is enabled and mapped memory space? */ | ||
445 | if ((val & (SB800_ACPI_MMIO_DECODE_EN | SB800_ACPI_MMIO_SEL)) == | ||
446 | SB800_ACPI_MMIO_DECODE_EN) { | ||
447 | /* Clear unnecessary the low twelve bits */ | ||
448 | val &= ~0xFFF; | ||
449 | /* Add the Watchdog Timer offset to base address. */ | ||
450 | val += SB800_PM_WDT_MMIO_OFFSET; | ||
451 | /* Check MMIO address conflict */ | ||
452 | if (request_mem_region_exclusive(val, SP5100_WDT_MEM_MAP_SIZE, | ||
453 | dev_name)) { | ||
454 | pr_debug("Got 0x%04x from SBResource_MMIO register\n", | ||
455 | val); | ||
456 | goto setup_wdt; | ||
457 | } else | ||
458 | pr_debug("MMIO address 0x%04x already in use\n", val); | ||
459 | } else | ||
460 | pr_debug("SBResource_MMIO is disabled(0x%04x)\n", val); | ||
461 | |||
462 | /* | ||
463 | * Lastly re-programming the watchdog timer MMIO address, | ||
464 | * This method is a last resort... | ||
465 | * | ||
466 | * Before re-programming, to ensure that the watchdog timer | ||
467 | * is disabled, disable the watchdog timer. | ||
468 | */ | ||
469 | tco_timer_disable(); | ||
470 | |||
471 | if (force_addr) { | ||
472 | /* | ||
473 | * Force the use of watchdog timer MMIO address, and aligned to | ||
474 | * 8byte boundary. | ||
475 | */ | ||
476 | force_addr &= ~0x7; | ||
477 | val = force_addr; | ||
478 | |||
479 | pr_info("Force the use of 0x%04x as MMIO address\n", val); | ||
480 | } else { | ||
481 | /* | ||
482 | * Get empty slot into the resource tree for watchdog timer. | ||
483 | */ | ||
484 | if (allocate_resource(&iomem_resource, | ||
485 | &wdt_res, | ||
486 | SP5100_WDT_MEM_MAP_SIZE, | ||
487 | 0xf0000000, | ||
488 | 0xfffffff8, | ||
489 | 0x8, | ||
490 | NULL, | ||
491 | NULL)) { | ||
492 | pr_err("MMIO allocation failed\n"); | ||
493 | goto unreg_region; | ||
494 | } | ||
495 | |||
496 | val = resbase_phys = wdt_res.start; | ||
497 | pr_debug("Got 0x%04x from resource tree\n", val); | ||
498 | } | ||
499 | |||
500 | /* Restore to the low three bits, if chipset is SB8x0(or later) */ | ||
501 | if (sp5100_tco_pci->revision >= 0x40) { | ||
502 | u8 reserved_bit; | ||
503 | reserved_bit = inb(base_addr) & 0x7; | ||
504 | val |= (u32)reserved_bit; | ||
505 | } | ||
506 | |||
507 | /* Re-programming the watchdog timer base address */ | ||
508 | outb(base_addr+0, index_reg); | ||
509 | /* Low three bits of BASE are reserved */ | ||
510 | outb((val >> 0) & 0xf8, data_reg); | ||
511 | outb(base_addr+1, index_reg); | ||
512 | outb((val >> 8) & 0xff, data_reg); | ||
513 | outb(base_addr+2, index_reg); | ||
514 | outb((val >> 16) & 0xff, data_reg); | ||
515 | outb(base_addr+3, index_reg); | ||
516 | outb((val >> 24) & 0xff, data_reg); | ||
517 | |||
518 | /* | ||
519 | * Clear unnecessary the low three bits, | ||
520 | * if chipset is SB8x0(or later) | ||
521 | */ | ||
522 | if (sp5100_tco_pci->revision >= 0x40) | ||
523 | val &= ~0x7; | ||
307 | 524 | ||
308 | if (!request_mem_region_exclusive(val, SP5100_WDT_MEM_MAP_SIZE, | 525 | if (!request_mem_region_exclusive(val, SP5100_WDT_MEM_MAP_SIZE, |
309 | "SP5100 TCO")) { | 526 | dev_name)) { |
310 | pr_err("mmio address 0x%04x already in use\n", val); | 527 | pr_err("MMIO address 0x%04x already in use\n", val); |
311 | goto unreg_region; | 528 | goto unreg_resource; |
312 | } | 529 | } |
530 | |||
531 | setup_wdt: | ||
313 | tcobase_phys = val; | 532 | tcobase_phys = val; |
314 | 533 | ||
315 | tcobase = ioremap(val, SP5100_WDT_MEM_MAP_SIZE); | 534 | tcobase = ioremap(val, SP5100_WDT_MEM_MAP_SIZE); |
@@ -318,26 +537,18 @@ static unsigned char sp5100_tco_setupdevice(void) | |||
318 | goto unreg_mem_region; | 537 | goto unreg_mem_region; |
319 | } | 538 | } |
320 | 539 | ||
321 | /* Enable watchdog decode bit */ | 540 | pr_info("Using 0x%04x for watchdog MMIO address\n", val); |
322 | pci_read_config_dword(sp5100_tco_pci, | ||
323 | SP5100_PCI_WATCHDOG_MISC_REG, | ||
324 | &val); | ||
325 | |||
326 | val |= SP5100_PCI_WATCHDOG_DECODE_EN; | ||
327 | 541 | ||
328 | pci_write_config_dword(sp5100_tco_pci, | 542 | /* Setup the watchdog timer */ |
329 | SP5100_PCI_WATCHDOG_MISC_REG, | 543 | tco_timer_enable(); |
330 | val); | ||
331 | 544 | ||
332 | /* Enable Watchdog timer and set the resolution to 1 sec. */ | 545 | /* Check that the watchdog action is set to reset the system */ |
333 | outb(SP5100_PM_WATCHDOG_CONTROL, SP5100_IO_PM_INDEX_REG); | ||
334 | val = inb(SP5100_IO_PM_DATA_REG); | ||
335 | val |= SP5100_PM_WATCHDOG_SECOND_RES; | ||
336 | val &= ~SP5100_PM_WATCHDOG_DISABLE; | ||
337 | outb(val, SP5100_IO_PM_DATA_REG); | ||
338 | |||
339 | /* Check that the watchdog action is set to reset the system. */ | ||
340 | val = readl(SP5100_WDT_CONTROL(tcobase)); | 546 | val = readl(SP5100_WDT_CONTROL(tcobase)); |
547 | /* | ||
548 | * Save WatchDogFired status, because WatchDogFired flag is | ||
549 | * cleared here. | ||
550 | */ | ||
551 | tco_wdt_fired = val & SP5100_PM_WATCHDOG_FIRED; | ||
341 | val &= ~SP5100_PM_WATCHDOG_ACTION_RESET; | 552 | val &= ~SP5100_PM_WATCHDOG_ACTION_RESET; |
342 | writel(val, SP5100_WDT_CONTROL(tcobase)); | 553 | writel(val, SP5100_WDT_CONTROL(tcobase)); |
343 | 554 | ||
@@ -355,6 +566,9 @@ static unsigned char sp5100_tco_setupdevice(void) | |||
355 | 566 | ||
356 | unreg_mem_region: | 567 | unreg_mem_region: |
357 | release_mem_region(tcobase_phys, SP5100_WDT_MEM_MAP_SIZE); | 568 | release_mem_region(tcobase_phys, SP5100_WDT_MEM_MAP_SIZE); |
569 | unreg_resource: | ||
570 | if (resbase_phys) | ||
571 | release_resource(&wdt_res); | ||
358 | unreg_region: | 572 | unreg_region: |
359 | release_region(pm_iobase, SP5100_PM_IOPORTS_SIZE); | 573 | release_region(pm_iobase, SP5100_PM_IOPORTS_SIZE); |
360 | exit: | 574 | exit: |
@@ -364,23 +578,18 @@ exit: | |||
364 | static int sp5100_tco_init(struct platform_device *dev) | 578 | static int sp5100_tco_init(struct platform_device *dev) |
365 | { | 579 | { |
366 | int ret; | 580 | int ret; |
367 | u32 val; | 581 | char addr_str[16]; |
368 | 582 | ||
369 | /* Check whether or not the hardware watchdog is there. If found, then | 583 | /* |
584 | * Check whether or not the hardware watchdog is there. If found, then | ||
370 | * set it up. | 585 | * set it up. |
371 | */ | 586 | */ |
372 | if (!sp5100_tco_setupdevice()) | 587 | if (!sp5100_tco_setupdevice()) |
373 | return -ENODEV; | 588 | return -ENODEV; |
374 | 589 | ||
375 | /* Check to see if last reboot was due to watchdog timeout */ | 590 | /* Check to see if last reboot was due to watchdog timeout */ |
376 | pr_info("Watchdog reboot %sdetected\n", | 591 | pr_info("Last reboot was %striggered by watchdog.\n", |
377 | readl(SP5100_WDT_CONTROL(tcobase)) & SP5100_PM_WATCHDOG_FIRED ? | 592 | tco_wdt_fired ? "" : "not "); |
378 | "" : "not "); | ||
379 | |||
380 | /* Clear out the old status */ | ||
381 | val = readl(SP5100_WDT_CONTROL(tcobase)); | ||
382 | val &= ~SP5100_PM_WATCHDOG_FIRED; | ||
383 | writel(val, SP5100_WDT_CONTROL(tcobase)); | ||
384 | 593 | ||
385 | /* | 594 | /* |
386 | * Check that the heartbeat value is within it's range. | 595 | * Check that the heartbeat value is within it's range. |
@@ -400,14 +609,24 @@ static int sp5100_tco_init(struct platform_device *dev) | |||
400 | 609 | ||
401 | clear_bit(0, &timer_alive); | 610 | clear_bit(0, &timer_alive); |
402 | 611 | ||
403 | pr_info("initialized (0x%p). heartbeat=%d sec (nowayout=%d)\n", | 612 | /* Show module parameters */ |
404 | tcobase, heartbeat, nowayout); | 613 | if (force_addr == tcobase_phys) |
614 | /* The force_addr is vaild */ | ||
615 | sprintf(addr_str, "0x%04x", force_addr); | ||
616 | else | ||
617 | strcpy(addr_str, "none"); | ||
618 | |||
619 | pr_info("initialized (0x%p). heartbeat=%d sec (nowayout=%d, " | ||
620 | "force_addr=%s)\n", | ||
621 | tcobase, heartbeat, nowayout, addr_str); | ||
405 | 622 | ||
406 | return 0; | 623 | return 0; |
407 | 624 | ||
408 | exit: | 625 | exit: |
409 | iounmap(tcobase); | 626 | iounmap(tcobase); |
410 | release_mem_region(tcobase_phys, SP5100_WDT_MEM_MAP_SIZE); | 627 | release_mem_region(tcobase_phys, SP5100_WDT_MEM_MAP_SIZE); |
628 | if (resbase_phys) | ||
629 | release_resource(&wdt_res); | ||
411 | release_region(pm_iobase, SP5100_PM_IOPORTS_SIZE); | 630 | release_region(pm_iobase, SP5100_PM_IOPORTS_SIZE); |
412 | return ret; | 631 | return ret; |
413 | } | 632 | } |
@@ -422,6 +641,8 @@ static void sp5100_tco_cleanup(void) | |||
422 | misc_deregister(&sp5100_tco_miscdev); | 641 | misc_deregister(&sp5100_tco_miscdev); |
423 | iounmap(tcobase); | 642 | iounmap(tcobase); |
424 | release_mem_region(tcobase_phys, SP5100_WDT_MEM_MAP_SIZE); | 643 | release_mem_region(tcobase_phys, SP5100_WDT_MEM_MAP_SIZE); |
644 | if (resbase_phys) | ||
645 | release_resource(&wdt_res); | ||
425 | release_region(pm_iobase, SP5100_PM_IOPORTS_SIZE); | 646 | release_region(pm_iobase, SP5100_PM_IOPORTS_SIZE); |
426 | } | 647 | } |
427 | 648 | ||
@@ -451,7 +672,7 @@ static int __init sp5100_tco_init_module(void) | |||
451 | { | 672 | { |
452 | int err; | 673 | int err; |
453 | 674 | ||
454 | pr_info("SP5100 TCO WatchDog Timer Driver v%s\n", TCO_VERSION); | 675 | pr_info("SP5100/SB800 TCO WatchDog Timer Driver v%s\n", TCO_VERSION); |
455 | 676 | ||
456 | err = platform_driver_register(&sp5100_tco_driver); | 677 | err = platform_driver_register(&sp5100_tco_driver); |
457 | if (err) | 678 | if (err) |
@@ -475,13 +696,13 @@ static void __exit sp5100_tco_cleanup_module(void) | |||
475 | { | 696 | { |
476 | platform_device_unregister(sp5100_tco_platform_device); | 697 | platform_device_unregister(sp5100_tco_platform_device); |
477 | platform_driver_unregister(&sp5100_tco_driver); | 698 | platform_driver_unregister(&sp5100_tco_driver); |
478 | pr_info("SP5100 TCO Watchdog Module Unloaded\n"); | 699 | pr_info("SP5100/SB800 TCO Watchdog Module Unloaded\n"); |
479 | } | 700 | } |
480 | 701 | ||
481 | module_init(sp5100_tco_init_module); | 702 | module_init(sp5100_tco_init_module); |
482 | module_exit(sp5100_tco_cleanup_module); | 703 | module_exit(sp5100_tco_cleanup_module); |
483 | 704 | ||
484 | MODULE_AUTHOR("Priyanka Gupta"); | 705 | MODULE_AUTHOR("Priyanka Gupta"); |
485 | MODULE_DESCRIPTION("TCO timer driver for SP5100 chipset"); | 706 | MODULE_DESCRIPTION("TCO timer driver for SP5100/SB800 chipset"); |
486 | MODULE_LICENSE("GPL"); | 707 | MODULE_LICENSE("GPL"); |
487 | MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR); | 708 | MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR); |
diff --git a/drivers/watchdog/sp5100_tco.h b/drivers/watchdog/sp5100_tco.h index a5a16cc90a34..71594a0c14b7 100644 --- a/drivers/watchdog/sp5100_tco.h +++ b/drivers/watchdog/sp5100_tco.h | |||
@@ -9,33 +9,57 @@ | |||
9 | /* | 9 | /* |
10 | * Some address definitions for the Watchdog | 10 | * Some address definitions for the Watchdog |
11 | */ | 11 | */ |
12 | |||
13 | #define SP5100_WDT_MEM_MAP_SIZE 0x08 | 12 | #define SP5100_WDT_MEM_MAP_SIZE 0x08 |
14 | #define SP5100_WDT_CONTROL(base) ((base) + 0x00) /* Watchdog Control */ | 13 | #define SP5100_WDT_CONTROL(base) ((base) + 0x00) /* Watchdog Control */ |
15 | #define SP5100_WDT_COUNT(base) ((base) + 0x04) /* Watchdog Count */ | 14 | #define SP5100_WDT_COUNT(base) ((base) + 0x04) /* Watchdog Count */ |
16 | 15 | ||
17 | #define SP5100_WDT_START_STOP_BIT 1 | 16 | #define SP5100_WDT_START_STOP_BIT (1 << 0) |
18 | #define SP5100_WDT_TRIGGER_BIT (1 << 7) | 17 | #define SP5100_WDT_TRIGGER_BIT (1 << 7) |
19 | 18 | ||
20 | #define SP5100_PCI_WATCHDOG_MISC_REG 0x41 | ||
21 | #define SP5100_PCI_WATCHDOG_DECODE_EN (1 << 3) | ||
22 | |||
23 | #define SP5100_PM_IOPORTS_SIZE 0x02 | 19 | #define SP5100_PM_IOPORTS_SIZE 0x02 |
24 | 20 | ||
25 | /* These two IO registers are hardcoded and there doesn't seem to be a way to | 21 | /* |
22 | * These two IO registers are hardcoded and there doesn't seem to be a way to | ||
26 | * read them from a register. | 23 | * read them from a register. |
27 | */ | 24 | */ |
25 | |||
26 | /* For SP5100/SB7x0 chipset */ | ||
28 | #define SP5100_IO_PM_INDEX_REG 0xCD6 | 27 | #define SP5100_IO_PM_INDEX_REG 0xCD6 |
29 | #define SP5100_IO_PM_DATA_REG 0xCD7 | 28 | #define SP5100_IO_PM_DATA_REG 0xCD7 |
30 | 29 | ||
30 | #define SP5100_SB_RESOURCE_MMIO_BASE 0x9C | ||
31 | |||
31 | #define SP5100_PM_WATCHDOG_CONTROL 0x69 | 32 | #define SP5100_PM_WATCHDOG_CONTROL 0x69 |
32 | #define SP5100_PM_WATCHDOG_BASE0 0x6C | 33 | #define SP5100_PM_WATCHDOG_BASE 0x6C |
33 | #define SP5100_PM_WATCHDOG_BASE1 0x6D | ||
34 | #define SP5100_PM_WATCHDOG_BASE2 0x6E | ||
35 | #define SP5100_PM_WATCHDOG_BASE3 0x6F | ||
36 | 34 | ||
37 | #define SP5100_PM_WATCHDOG_FIRED (1 << 1) | 35 | #define SP5100_PM_WATCHDOG_FIRED (1 << 1) |
38 | #define SP5100_PM_WATCHDOG_ACTION_RESET (1 << 2) | 36 | #define SP5100_PM_WATCHDOG_ACTION_RESET (1 << 2) |
39 | 37 | ||
40 | #define SP5100_PM_WATCHDOG_DISABLE 1 | 38 | #define SP5100_PCI_WATCHDOG_MISC_REG 0x41 |
39 | #define SP5100_PCI_WATCHDOG_DECODE_EN (1 << 3) | ||
40 | |||
41 | #define SP5100_PM_WATCHDOG_DISABLE (1 << 0) | ||
41 | #define SP5100_PM_WATCHDOG_SECOND_RES (3 << 1) | 42 | #define SP5100_PM_WATCHDOG_SECOND_RES (3 << 1) |
43 | |||
44 | #define SP5100_DEVNAME "SP5100 TCO" | ||
45 | |||
46 | |||
47 | /* For SB8x0(or later) chipset */ | ||
48 | #define SB800_IO_PM_INDEX_REG 0xCD6 | ||
49 | #define SB800_IO_PM_DATA_REG 0xCD7 | ||
50 | |||
51 | #define SB800_PM_ACPI_MMIO_EN 0x24 | ||
52 | #define SB800_PM_WATCHDOG_CONTROL 0x48 | ||
53 | #define SB800_PM_WATCHDOG_BASE 0x48 | ||
54 | #define SB800_PM_WATCHDOG_CONFIG 0x4C | ||
55 | |||
56 | #define SB800_PCI_WATCHDOG_DECODE_EN (1 << 0) | ||
57 | #define SB800_PM_WATCHDOG_DISABLE (1 << 2) | ||
58 | #define SB800_PM_WATCHDOG_SECOND_RES (3 << 0) | ||
59 | #define SB800_ACPI_MMIO_DECODE_EN (1 << 0) | ||
60 | #define SB800_ACPI_MMIO_SEL (1 << 2) | ||
61 | |||
62 | |||
63 | #define SB800_PM_WDT_MMIO_OFFSET 0xB00 | ||
64 | |||
65 | #define SB800_DEVNAME "SB800 TCO" | ||
diff --git a/drivers/watchdog/sp805_wdt.c b/drivers/watchdog/sp805_wdt.c index 76c73cbf0040..8872642505c0 100644 --- a/drivers/watchdog/sp805_wdt.c +++ b/drivers/watchdog/sp805_wdt.c | |||
@@ -130,16 +130,10 @@ static int wdt_config(struct watchdog_device *wdd, bool ping) | |||
130 | int ret; | 130 | int ret; |
131 | 131 | ||
132 | if (!ping) { | 132 | if (!ping) { |
133 | ret = clk_prepare(wdt->clk); | ||
134 | if (ret) { | ||
135 | dev_err(&wdt->adev->dev, "clock prepare fail"); | ||
136 | return ret; | ||
137 | } | ||
138 | 133 | ||
139 | ret = clk_enable(wdt->clk); | 134 | ret = clk_prepare_enable(wdt->clk); |
140 | if (ret) { | 135 | if (ret) { |
141 | dev_err(&wdt->adev->dev, "clock enable fail"); | 136 | dev_err(&wdt->adev->dev, "clock enable fail"); |
142 | clk_unprepare(wdt->clk); | ||
143 | return ret; | 137 | return ret; |
144 | } | 138 | } |
145 | } | 139 | } |
@@ -190,8 +184,7 @@ static int wdt_disable(struct watchdog_device *wdd) | |||
190 | readl_relaxed(wdt->base + WDTLOCK); | 184 | readl_relaxed(wdt->base + WDTLOCK); |
191 | spin_unlock(&wdt->lock); | 185 | spin_unlock(&wdt->lock); |
192 | 186 | ||
193 | clk_disable(wdt->clk); | 187 | clk_disable_unprepare(wdt->clk); |
194 | clk_unprepare(wdt->clk); | ||
195 | 188 | ||
196 | return 0; | 189 | return 0; |
197 | } | 190 | } |
diff --git a/drivers/watchdog/twl4030_wdt.c b/drivers/watchdog/twl4030_wdt.c index 9f54b1da7185..81918cf8993b 100644 --- a/drivers/watchdog/twl4030_wdt.c +++ b/drivers/watchdog/twl4030_wdt.c | |||
@@ -22,26 +22,12 @@ | |||
22 | #include <linux/types.h> | 22 | #include <linux/types.h> |
23 | #include <linux/slab.h> | 23 | #include <linux/slab.h> |
24 | #include <linux/kernel.h> | 24 | #include <linux/kernel.h> |
25 | #include <linux/fs.h> | ||
26 | #include <linux/watchdog.h> | 25 | #include <linux/watchdog.h> |
27 | #include <linux/platform_device.h> | 26 | #include <linux/platform_device.h> |
28 | #include <linux/miscdevice.h> | ||
29 | #include <linux/uaccess.h> | ||
30 | #include <linux/i2c/twl.h> | 27 | #include <linux/i2c/twl.h> |
31 | 28 | ||
32 | #define TWL4030_WATCHDOG_CFG_REG_OFFS 0x3 | 29 | #define TWL4030_WATCHDOG_CFG_REG_OFFS 0x3 |
33 | 30 | ||
34 | #define TWL4030_WDT_STATE_OPEN 0x1 | ||
35 | #define TWL4030_WDT_STATE_ACTIVE 0x8 | ||
36 | |||
37 | static struct platform_device *twl4030_wdt_dev; | ||
38 | |||
39 | struct twl4030_wdt { | ||
40 | struct miscdevice miscdev; | ||
41 | int timer_margin; | ||
42 | unsigned long state; | ||
43 | }; | ||
44 | |||
45 | static bool nowayout = WATCHDOG_NOWAYOUT; | 31 | static bool nowayout = WATCHDOG_NOWAYOUT; |
46 | module_param(nowayout, bool, 0); | 32 | module_param(nowayout, bool, 0); |
47 | MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started " | 33 | MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started " |
@@ -49,175 +35,75 @@ MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started " | |||
49 | 35 | ||
50 | static int twl4030_wdt_write(unsigned char val) | 36 | static int twl4030_wdt_write(unsigned char val) |
51 | { | 37 | { |
52 | return twl_i2c_write_u8(TWL4030_MODULE_PM_RECEIVER, val, | 38 | return twl_i2c_write_u8(TWL_MODULE_PM_RECEIVER, val, |
53 | TWL4030_WATCHDOG_CFG_REG_OFFS); | 39 | TWL4030_WATCHDOG_CFG_REG_OFFS); |
54 | } | 40 | } |
55 | 41 | ||
56 | static int twl4030_wdt_enable(struct twl4030_wdt *wdt) | 42 | static int twl4030_wdt_start(struct watchdog_device *wdt) |
57 | { | 43 | { |
58 | return twl4030_wdt_write(wdt->timer_margin + 1); | 44 | return twl4030_wdt_write(wdt->timeout + 1); |
59 | } | 45 | } |
60 | 46 | ||
61 | static int twl4030_wdt_disable(struct twl4030_wdt *wdt) | 47 | static int twl4030_wdt_stop(struct watchdog_device *wdt) |
62 | { | 48 | { |
63 | return twl4030_wdt_write(0); | 49 | return twl4030_wdt_write(0); |
64 | } | 50 | } |
65 | 51 | ||
66 | static int twl4030_wdt_set_timeout(struct twl4030_wdt *wdt, int timeout) | 52 | static int twl4030_wdt_set_timeout(struct watchdog_device *wdt, |
67 | { | 53 | unsigned int timeout) |
68 | if (timeout < 0 || timeout > 30) { | ||
69 | dev_warn(wdt->miscdev.parent, | ||
70 | "Timeout can only be in the range [0-30] seconds"); | ||
71 | return -EINVAL; | ||
72 | } | ||
73 | wdt->timer_margin = timeout; | ||
74 | return twl4030_wdt_enable(wdt); | ||
75 | } | ||
76 | |||
77 | static ssize_t twl4030_wdt_write_fop(struct file *file, | ||
78 | const char __user *data, size_t len, loff_t *ppos) | ||
79 | { | 54 | { |
80 | struct twl4030_wdt *wdt = file->private_data; | 55 | wdt->timeout = timeout; |
81 | |||
82 | if (len) | ||
83 | twl4030_wdt_enable(wdt); | ||
84 | |||
85 | return len; | ||
86 | } | ||
87 | |||
88 | static long twl4030_wdt_ioctl(struct file *file, | ||
89 | unsigned int cmd, unsigned long arg) | ||
90 | { | ||
91 | void __user *argp = (void __user *)arg; | ||
92 | int __user *p = argp; | ||
93 | int new_margin; | ||
94 | struct twl4030_wdt *wdt = file->private_data; | ||
95 | |||
96 | static const struct watchdog_info twl4030_wd_ident = { | ||
97 | .identity = "TWL4030 Watchdog", | ||
98 | .options = WDIOF_SETTIMEOUT, | ||
99 | .firmware_version = 0, | ||
100 | }; | ||
101 | |||
102 | switch (cmd) { | ||
103 | case WDIOC_GETSUPPORT: | ||
104 | return copy_to_user(argp, &twl4030_wd_ident, | ||
105 | sizeof(twl4030_wd_ident)) ? -EFAULT : 0; | ||
106 | |||
107 | case WDIOC_GETSTATUS: | ||
108 | case WDIOC_GETBOOTSTATUS: | ||
109 | return put_user(0, p); | ||
110 | |||
111 | case WDIOC_KEEPALIVE: | ||
112 | twl4030_wdt_enable(wdt); | ||
113 | break; | ||
114 | |||
115 | case WDIOC_SETTIMEOUT: | ||
116 | if (get_user(new_margin, p)) | ||
117 | return -EFAULT; | ||
118 | if (twl4030_wdt_set_timeout(wdt, new_margin)) | ||
119 | return -EINVAL; | ||
120 | return put_user(wdt->timer_margin, p); | ||
121 | |||
122 | case WDIOC_GETTIMEOUT: | ||
123 | return put_user(wdt->timer_margin, p); | ||
124 | |||
125 | default: | ||
126 | return -ENOTTY; | ||
127 | } | ||
128 | |||
129 | return 0; | 56 | return 0; |
130 | } | 57 | } |
131 | 58 | ||
132 | static int twl4030_wdt_open(struct inode *inode, struct file *file) | 59 | static const struct watchdog_info twl4030_wdt_info = { |
133 | { | 60 | .options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING, |
134 | struct twl4030_wdt *wdt = platform_get_drvdata(twl4030_wdt_dev); | 61 | .identity = "TWL4030 Watchdog", |
135 | 62 | }; | |
136 | /* /dev/watchdog can only be opened once */ | ||
137 | if (test_and_set_bit(0, &wdt->state)) | ||
138 | return -EBUSY; | ||
139 | |||
140 | wdt->state |= TWL4030_WDT_STATE_ACTIVE; | ||
141 | file->private_data = (void *) wdt; | ||
142 | |||
143 | twl4030_wdt_enable(wdt); | ||
144 | return nonseekable_open(inode, file); | ||
145 | } | ||
146 | |||
147 | static int twl4030_wdt_release(struct inode *inode, struct file *file) | ||
148 | { | ||
149 | struct twl4030_wdt *wdt = file->private_data; | ||
150 | if (nowayout) { | ||
151 | dev_alert(wdt->miscdev.parent, | ||
152 | "Unexpected close, watchdog still running!\n"); | ||
153 | twl4030_wdt_enable(wdt); | ||
154 | } else { | ||
155 | if (twl4030_wdt_disable(wdt)) | ||
156 | return -EFAULT; | ||
157 | wdt->state &= ~TWL4030_WDT_STATE_ACTIVE; | ||
158 | } | ||
159 | |||
160 | clear_bit(0, &wdt->state); | ||
161 | return 0; | ||
162 | } | ||
163 | 63 | ||
164 | static const struct file_operations twl4030_wdt_fops = { | 64 | static const struct watchdog_ops twl4030_wdt_ops = { |
165 | .owner = THIS_MODULE, | 65 | .owner = THIS_MODULE, |
166 | .llseek = no_llseek, | 66 | .start = twl4030_wdt_start, |
167 | .open = twl4030_wdt_open, | 67 | .stop = twl4030_wdt_stop, |
168 | .release = twl4030_wdt_release, | 68 | .set_timeout = twl4030_wdt_set_timeout, |
169 | .unlocked_ioctl = twl4030_wdt_ioctl, | ||
170 | .write = twl4030_wdt_write_fop, | ||
171 | }; | 69 | }; |
172 | 70 | ||
173 | static int twl4030_wdt_probe(struct platform_device *pdev) | 71 | static int twl4030_wdt_probe(struct platform_device *pdev) |
174 | { | 72 | { |
175 | int ret = 0; | 73 | int ret = 0; |
176 | struct twl4030_wdt *wdt; | 74 | struct watchdog_device *wdt; |
177 | 75 | ||
178 | wdt = kzalloc(sizeof(struct twl4030_wdt), GFP_KERNEL); | 76 | wdt = devm_kzalloc(&pdev->dev, sizeof(*wdt), GFP_KERNEL); |
179 | if (!wdt) | 77 | if (!wdt) |
180 | return -ENOMEM; | 78 | return -ENOMEM; |
181 | 79 | ||
182 | wdt->state = 0; | 80 | wdt->info = &twl4030_wdt_info; |
183 | wdt->timer_margin = 30; | 81 | wdt->ops = &twl4030_wdt_ops; |
184 | wdt->miscdev.parent = &pdev->dev; | 82 | wdt->status = 0; |
185 | wdt->miscdev.fops = &twl4030_wdt_fops; | 83 | wdt->timeout = 30; |
186 | wdt->miscdev.minor = WATCHDOG_MINOR; | 84 | wdt->min_timeout = 1; |
187 | wdt->miscdev.name = "watchdog"; | 85 | wdt->max_timeout = 30; |
188 | 86 | ||
87 | watchdog_set_nowayout(wdt, nowayout); | ||
189 | platform_set_drvdata(pdev, wdt); | 88 | platform_set_drvdata(pdev, wdt); |
190 | 89 | ||
191 | twl4030_wdt_dev = pdev; | 90 | twl4030_wdt_stop(wdt); |
192 | 91 | ||
193 | twl4030_wdt_disable(wdt); | 92 | ret = watchdog_register_device(wdt); |
194 | |||
195 | ret = misc_register(&wdt->miscdev); | ||
196 | if (ret) { | 93 | if (ret) { |
197 | dev_err(wdt->miscdev.parent, | ||
198 | "Failed to register misc device\n"); | ||
199 | platform_set_drvdata(pdev, NULL); | 94 | platform_set_drvdata(pdev, NULL); |
200 | kfree(wdt); | ||
201 | twl4030_wdt_dev = NULL; | ||
202 | return ret; | 95 | return ret; |
203 | } | 96 | } |
97 | |||
204 | return 0; | 98 | return 0; |
205 | } | 99 | } |
206 | 100 | ||
207 | static int twl4030_wdt_remove(struct platform_device *pdev) | 101 | static int twl4030_wdt_remove(struct platform_device *pdev) |
208 | { | 102 | { |
209 | struct twl4030_wdt *wdt = platform_get_drvdata(pdev); | 103 | struct watchdog_device *wdt = platform_get_drvdata(pdev); |
210 | |||
211 | if (wdt->state & TWL4030_WDT_STATE_ACTIVE) | ||
212 | if (twl4030_wdt_disable(wdt)) | ||
213 | return -EFAULT; | ||
214 | |||
215 | wdt->state &= ~TWL4030_WDT_STATE_ACTIVE; | ||
216 | misc_deregister(&wdt->miscdev); | ||
217 | 104 | ||
105 | watchdog_unregister_device(wdt); | ||
218 | platform_set_drvdata(pdev, NULL); | 106 | platform_set_drvdata(pdev, NULL); |
219 | kfree(wdt); | ||
220 | twl4030_wdt_dev = NULL; | ||
221 | 107 | ||
222 | return 0; | 108 | return 0; |
223 | } | 109 | } |
@@ -225,18 +111,18 @@ static int twl4030_wdt_remove(struct platform_device *pdev) | |||
225 | #ifdef CONFIG_PM | 111 | #ifdef CONFIG_PM |
226 | static int twl4030_wdt_suspend(struct platform_device *pdev, pm_message_t state) | 112 | static int twl4030_wdt_suspend(struct platform_device *pdev, pm_message_t state) |
227 | { | 113 | { |
228 | struct twl4030_wdt *wdt = platform_get_drvdata(pdev); | 114 | struct watchdog_device *wdt = platform_get_drvdata(pdev); |
229 | if (wdt->state & TWL4030_WDT_STATE_ACTIVE) | 115 | if (watchdog_active(wdt)) |
230 | return twl4030_wdt_disable(wdt); | 116 | return twl4030_wdt_stop(wdt); |
231 | 117 | ||
232 | return 0; | 118 | return 0; |
233 | } | 119 | } |
234 | 120 | ||
235 | static int twl4030_wdt_resume(struct platform_device *pdev) | 121 | static int twl4030_wdt_resume(struct platform_device *pdev) |
236 | { | 122 | { |
237 | struct twl4030_wdt *wdt = platform_get_drvdata(pdev); | 123 | struct watchdog_device *wdt = platform_get_drvdata(pdev); |
238 | if (wdt->state & TWL4030_WDT_STATE_ACTIVE) | 124 | if (watchdog_active(wdt)) |
239 | return twl4030_wdt_enable(wdt); | 125 | return twl4030_wdt_start(wdt); |
240 | 126 | ||
241 | return 0; | 127 | return 0; |
242 | } | 128 | } |
@@ -260,6 +146,5 @@ module_platform_driver(twl4030_wdt_driver); | |||
260 | 146 | ||
261 | MODULE_AUTHOR("Nokia Corporation"); | 147 | MODULE_AUTHOR("Nokia Corporation"); |
262 | MODULE_LICENSE("GPL"); | 148 | MODULE_LICENSE("GPL"); |
263 | MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR); | ||
264 | MODULE_ALIAS("platform:twl4030_wdt"); | 149 | MODULE_ALIAS("platform:twl4030_wdt"); |
265 | 150 | ||