diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2016-03-19 22:35:51 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2016-03-19 22:35:51 -0400 |
commit | 1e75a9f34a5ed5902707fb74b468356c55142b71 (patch) | |
tree | 810ff2a5e9d393242e663b321c8a80a17cebe11d | |
parent | 1c3d770043583d99118d52cad309f586ef8e7d4a (diff) | |
parent | d1ed3ba4e3d76b4ebec239c64f990c26d7935700 (diff) |
Merge git://www.linux-watchdog.org/linux-watchdog
Pull watchdog updates from Wim Van Sebroeck:
- new drivers for: NI 903x/913x watchdog driver, WinSystems EBC-C384
watchdog timer and ARM SBSA watchdog driver
- Support for NCT6102D devices
- Improvements of the generic watchdog framework (improve restart
handler, make set_timeout optional, introduce infrastructure
triggered keepalives, ...
- improvements on the pnx4008 watchdog driver
- several smaller fixes and improvements
* git://www.linux-watchdog.org/linux-watchdog: (28 commits)
watchdog: Ensure that wdd is not dereferenced if NULL
watchdog: imx2: Convert to use infrastructure triggered keepalives
watchdog: dw_wdt: Convert to use watchdog infrastructure
watchdog: Add support for minimum time between heartbeats
watchdog: Make stop function optional
watchdog: Introduce WDOG_HW_RUNNING flag
watchdog: Introduce hardware maximum heartbeat in watchdog core
watchdog: Make set_timeout function optional
arm: lpc32xx: remove restart handler
arm: lpc32xx: phy3250 remove restart hook
watchdog: pnx4008: restart: support "cmd" from userspace
watchdog: pnx4008: add support for soft reset
watchdog: pnx4008: add restart handler
watchdog: pnx4008: update logging during power-on
watchdog: tangox_wdt: test clock rate to avoid division by 0
watchdog: atlas7_wdt: test clock rate to avoid division by 0
watchdog: s3c2410_wdt: Add max and min timeout values
Watchdog: introduce ARM SBSA watchdog driver
Documentation: add sbsa-gwdt driver documentation
watchdog: Add watchdog timer support for the WinSystems EBC-C384
...
34 files changed, 1482 insertions, 354 deletions
diff --git a/Documentation/devicetree/bindings/watchdog/sbsa-gwdt.txt b/Documentation/devicetree/bindings/watchdog/sbsa-gwdt.txt new file mode 100644 index 000000000000..6f2d5f91964d --- /dev/null +++ b/Documentation/devicetree/bindings/watchdog/sbsa-gwdt.txt | |||
@@ -0,0 +1,31 @@ | |||
1 | * SBSA (Server Base System Architecture) Generic Watchdog | ||
2 | |||
3 | The SBSA Generic Watchdog Timer is used to force a reset of the system | ||
4 | after two stages of timeout have elapsed. A detailed definition of the | ||
5 | watchdog timer can be found in the ARM document: ARM-DEN-0029 - Server | ||
6 | Base System Architecture (SBSA) | ||
7 | |||
8 | Required properties: | ||
9 | - compatible: Should at least contain "arm,sbsa-gwdt". | ||
10 | |||
11 | - reg: Each entry specifies the base physical address of a register frame | ||
12 | and the length of that frame; currently, two frames must be defined, | ||
13 | in this order: | ||
14 | 1: Watchdog control frame; | ||
15 | 2: Refresh frame. | ||
16 | |||
17 | - interrupts: Should contain the Watchdog Signal 0 (WS0) SPI (Shared | ||
18 | Peripheral Interrupt) number of SBSA Generic Watchdog. | ||
19 | |||
20 | Optional properties | ||
21 | - timeout-sec: Watchdog timeout values (in seconds). | ||
22 | |||
23 | Example for FVP Foundation Model v8: | ||
24 | |||
25 | watchdog@2a440000 { | ||
26 | compatible = "arm,sbsa-gwdt"; | ||
27 | reg = <0x0 0x2a440000 0 0x1000>, | ||
28 | <0x0 0x2a450000 0 0x1000>; | ||
29 | interrupts = <0 27 4>; | ||
30 | timeout-sec = <30>; | ||
31 | }; | ||
diff --git a/Documentation/watchdog/watchdog-kernel-api.txt b/Documentation/watchdog/watchdog-kernel-api.txt index 55120a055a14..917eeeabfa5e 100644 --- a/Documentation/watchdog/watchdog-kernel-api.txt +++ b/Documentation/watchdog/watchdog-kernel-api.txt | |||
@@ -52,6 +52,8 @@ struct watchdog_device { | |||
52 | unsigned int timeout; | 52 | unsigned int timeout; |
53 | unsigned int min_timeout; | 53 | unsigned int min_timeout; |
54 | unsigned int max_timeout; | 54 | unsigned int max_timeout; |
55 | unsigned int min_hw_heartbeat_ms; | ||
56 | unsigned int max_hw_heartbeat_ms; | ||
55 | struct notifier_block reboot_nb; | 57 | struct notifier_block reboot_nb; |
56 | struct notifier_block restart_nb; | 58 | struct notifier_block restart_nb; |
57 | void *driver_data; | 59 | void *driver_data; |
@@ -73,8 +75,21 @@ It contains following fields: | |||
73 | additional information about the watchdog timer itself. (Like it's unique name) | 75 | additional information about the watchdog timer itself. (Like it's unique name) |
74 | * ops: a pointer to the list of watchdog operations that the watchdog supports. | 76 | * ops: a pointer to the list of watchdog operations that the watchdog supports. |
75 | * timeout: the watchdog timer's timeout value (in seconds). | 77 | * timeout: the watchdog timer's timeout value (in seconds). |
78 | This is the time after which the system will reboot if user space does | ||
79 | not send a heartbeat request if WDOG_ACTIVE is set. | ||
76 | * min_timeout: the watchdog timer's minimum timeout value (in seconds). | 80 | * min_timeout: the watchdog timer's minimum timeout value (in seconds). |
77 | * max_timeout: the watchdog timer's maximum timeout value (in seconds). | 81 | If set, the minimum configurable value for 'timeout'. |
82 | * max_timeout: the watchdog timer's maximum timeout value (in seconds), | ||
83 | as seen from userspace. If set, the maximum configurable value for | ||
84 | 'timeout'. Not used if max_hw_heartbeat_ms is non-zero. | ||
85 | * min_hw_heartbeat_ms: Minimum time between heartbeats sent to the chip, | ||
86 | in milli-seconds. | ||
87 | * max_hw_heartbeat_ms: Maximum hardware heartbeat, in milli-seconds. | ||
88 | If set, the infrastructure will send heartbeats to the watchdog driver | ||
89 | if 'timeout' is larger than max_hw_heartbeat_ms, unless WDOG_ACTIVE | ||
90 | is set and userspace failed to send a heartbeat for at least 'timeout' | ||
91 | seconds. max_hw_heartbeat_ms must be set if a driver does not implement | ||
92 | the stop function. | ||
78 | * reboot_nb: notifier block that is registered for reboot notifications, for | 93 | * reboot_nb: notifier block that is registered for reboot notifications, for |
79 | internal use only. If the driver calls watchdog_stop_on_reboot, watchdog core | 94 | internal use only. If the driver calls watchdog_stop_on_reboot, watchdog core |
80 | will stop the watchdog on such notifications. | 95 | will stop the watchdog on such notifications. |
@@ -123,17 +138,20 @@ are: | |||
123 | device. | 138 | device. |
124 | The routine needs a pointer to the watchdog timer device structure as a | 139 | The routine needs a pointer to the watchdog timer device structure as a |
125 | parameter. It returns zero on success or a negative errno code for failure. | 140 | parameter. It returns zero on success or a negative errno code for failure. |
126 | * stop: with this routine the watchdog timer device is being stopped. | ||
127 | The routine needs a pointer to the watchdog timer device structure as a | ||
128 | parameter. It returns zero on success or a negative errno code for failure. | ||
129 | Some watchdog timer hardware can only be started and not be stopped. The | ||
130 | driver supporting this hardware needs to make sure that a start and stop | ||
131 | routine is being provided. This can be done by using a timer in the driver | ||
132 | that regularly sends a keepalive ping to the watchdog timer hardware. | ||
133 | 141 | ||
134 | Not all watchdog timer hardware supports the same functionality. That's why | 142 | Not all watchdog timer hardware supports the same functionality. That's why |
135 | all other routines/operations are optional. They only need to be provided if | 143 | all other routines/operations are optional. They only need to be provided if |
136 | they are supported. These optional routines/operations are: | 144 | they are supported. These optional routines/operations are: |
145 | * stop: with this routine the watchdog timer device is being stopped. | ||
146 | The routine needs a pointer to the watchdog timer device structure as a | ||
147 | parameter. It returns zero on success or a negative errno code for failure. | ||
148 | Some watchdog timer hardware can only be started and not be stopped. A | ||
149 | driver supporting such hardware does not have to implement the stop routine. | ||
150 | If a driver has no stop function, the watchdog core will set WDOG_HW_RUNNING | ||
151 | and start calling the driver's keepalive pings function after the watchdog | ||
152 | device is closed. | ||
153 | If a watchdog driver does not implement the stop function, it must set | ||
154 | max_hw_heartbeat_ms. | ||
137 | * ping: this is the routine that sends a keepalive ping to the watchdog timer | 155 | * ping: this is the routine that sends a keepalive ping to the watchdog timer |
138 | hardware. | 156 | hardware. |
139 | The routine needs a pointer to the watchdog timer device structure as a | 157 | The routine needs a pointer to the watchdog timer device structure as a |
@@ -153,9 +171,18 @@ they are supported. These optional routines/operations are: | |||
153 | and -EIO for "could not write value to the watchdog". On success this | 171 | and -EIO for "could not write value to the watchdog". On success this |
154 | routine should set the timeout value of the watchdog_device to the | 172 | routine should set the timeout value of the watchdog_device to the |
155 | achieved timeout value (which may be different from the requested one | 173 | achieved timeout value (which may be different from the requested one |
156 | because the watchdog does not necessarily has a 1 second resolution). | 174 | because the watchdog does not necessarily have a 1 second resolution). |
175 | Drivers implementing max_hw_heartbeat_ms set the hardware watchdog heartbeat | ||
176 | to the minimum of timeout and max_hw_heartbeat_ms. Those drivers set the | ||
177 | timeout value of the watchdog_device either to the requested timeout value | ||
178 | (if it is larger than max_hw_heartbeat_ms), or to the achieved timeout value. | ||
157 | (Note: the WDIOF_SETTIMEOUT needs to be set in the options field of the | 179 | (Note: the WDIOF_SETTIMEOUT needs to be set in the options field of the |
158 | watchdog's info structure). | 180 | watchdog's info structure). |
181 | If the watchdog driver does not have to perform any action but setting the | ||
182 | watchdog_device.timeout, this callback can be omitted. | ||
183 | If set_timeout is not provided but, WDIOF_SETTIMEOUT is set, the watchdog | ||
184 | infrastructure updates the timeout value of the watchdog_device internally | ||
185 | to the requested value. | ||
159 | * get_timeleft: this routines returns the time that's left before a reset. | 186 | * get_timeleft: this routines returns the time that's left before a reset. |
160 | * restart: this routine restarts the machine. It returns 0 on success or a | 187 | * restart: this routine restarts the machine. It returns 0 on success or a |
161 | negative errno code for failure. | 188 | negative errno code for failure. |
@@ -169,11 +196,19 @@ The 'ref' and 'unref' operations are no longer used and deprecated. | |||
169 | The status bits should (preferably) be set with the set_bit and clear_bit alike | 196 | The status bits should (preferably) be set with the set_bit and clear_bit alike |
170 | bit-operations. The status bits that are defined are: | 197 | bit-operations. The status bits that are defined are: |
171 | * WDOG_ACTIVE: this status bit indicates whether or not a watchdog timer device | 198 | * WDOG_ACTIVE: this status bit indicates whether or not a watchdog timer device |
172 | is active or not. When the watchdog is active after booting, then you should | 199 | is active or not from user perspective. User space is expected to send |
173 | set this status bit (Note: when you register the watchdog timer device with | 200 | heartbeat requests to the driver while this flag is set. |
174 | this bit set, then opening /dev/watchdog will skip the start operation) | ||
175 | * WDOG_NO_WAY_OUT: this bit stores the nowayout setting for the watchdog. | 201 | * WDOG_NO_WAY_OUT: this bit stores the nowayout setting for the watchdog. |
176 | If this bit is set then the watchdog timer will not be able to stop. | 202 | If this bit is set then the watchdog timer will not be able to stop. |
203 | * WDOG_HW_RUNNING: Set by the watchdog driver if the hardware watchdog is | ||
204 | running. The bit must be set if the watchdog timer hardware can not be | ||
205 | stopped. The bit may also be set if the watchdog timer is running after | ||
206 | booting, before the watchdog device is opened. If set, the watchdog | ||
207 | infrastructure will send keepalives to the watchdog hardware while | ||
208 | WDOG_ACTIVE is not set. | ||
209 | Note: when you register the watchdog timer device with this bit set, | ||
210 | then opening /dev/watchdog will skip the start operation but send a keepalive | ||
211 | request instead. | ||
177 | 212 | ||
178 | To set the WDOG_NO_WAY_OUT status bit (before registering your watchdog | 213 | To set the WDOG_NO_WAY_OUT status bit (before registering your watchdog |
179 | timer device) you can either: | 214 | timer device) you can either: |
diff --git a/Documentation/watchdog/watchdog-parameters.txt b/Documentation/watchdog/watchdog-parameters.txt index 4e4b6f10d841..c161399a6b5c 100644 --- a/Documentation/watchdog/watchdog-parameters.txt +++ b/Documentation/watchdog/watchdog-parameters.txt | |||
@@ -200,6 +200,11 @@ mv64x60_wdt: | |||
200 | nowayout: Watchdog cannot be stopped once started | 200 | nowayout: Watchdog cannot be stopped once started |
201 | (default=kernel config parameter) | 201 | (default=kernel config parameter) |
202 | ------------------------------------------------- | 202 | ------------------------------------------------- |
203 | ni903x_wdt: | ||
204 | timeout: Initial watchdog timeout in seconds (0<timeout<516, default=60) | ||
205 | nowayout: Watchdog cannot be stopped once started | ||
206 | (default=kernel config parameter) | ||
207 | ------------------------------------------------- | ||
203 | nuc900_wdt: | 208 | nuc900_wdt: |
204 | heartbeat: Watchdog heartbeats in seconds. | 209 | heartbeat: Watchdog heartbeats in seconds. |
205 | (default = 15) | 210 | (default = 15) |
@@ -284,6 +289,13 @@ sbc_fitpc2_wdt: | |||
284 | margin: Watchdog margin in seconds (default 60s) | 289 | margin: Watchdog margin in seconds (default 60s) |
285 | nowayout: Watchdog cannot be stopped once started | 290 | nowayout: Watchdog cannot be stopped once started |
286 | ------------------------------------------------- | 291 | ------------------------------------------------- |
292 | sbsa_gwdt: | ||
293 | timeout: Watchdog timeout in seconds. (default 10s) | ||
294 | action: Watchdog action at the first stage timeout, | ||
295 | set to 0 to ignore, 1 to panic. (default=0) | ||
296 | nowayout: Watchdog cannot be stopped once started | ||
297 | (default=kernel config parameter) | ||
298 | ------------------------------------------------- | ||
287 | sc1200wdt: | 299 | sc1200wdt: |
288 | isapnp: When set to 0 driver ISA PnP support will be disabled (default=1) | 300 | isapnp: When set to 0 driver ISA PnP support will be disabled (default=1) |
289 | io: io port | 301 | io: io port |
diff --git a/MAINTAINERS b/MAINTAINERS index 0cbfc69a2303..606528bb16af 100644 --- a/MAINTAINERS +++ b/MAINTAINERS | |||
@@ -11965,6 +11965,12 @@ M: David Härdeman <david@hardeman.nu> | |||
11965 | S: Maintained | 11965 | S: Maintained |
11966 | F: drivers/media/rc/winbond-cir.c | 11966 | F: drivers/media/rc/winbond-cir.c |
11967 | 11967 | ||
11968 | WINSYSTEMS EBC-C384 WATCHDOG DRIVER | ||
11969 | M: William Breathitt Gray <vilhelm.gray@gmail.com> | ||
11970 | L: linux-watchdog@vger.kernel.org | ||
11971 | S: Maintained | ||
11972 | F: drivers/watchdog/ebc-c384_wdt.c | ||
11973 | |||
11968 | WINSYSTEMS WS16C48 GPIO DRIVER | 11974 | WINSYSTEMS WS16C48 GPIO DRIVER |
11969 | M: William Breathitt Gray <vilhelm.gray@gmail.com> | 11975 | M: William Breathitt Gray <vilhelm.gray@gmail.com> |
11970 | L: linux-gpio@vger.kernel.org | 11976 | L: linux-gpio@vger.kernel.org |
diff --git a/arch/arm/mach-lpc32xx/common.c b/arch/arm/mach-lpc32xx/common.c index 716e83eb1db8..5b7a1e78c3a5 100644 --- a/arch/arm/mach-lpc32xx/common.c +++ b/arch/arm/mach-lpc32xx/common.c | |||
@@ -194,21 +194,6 @@ void __init lpc32xx_map_io(void) | |||
194 | iotable_init(lpc32xx_io_desc, ARRAY_SIZE(lpc32xx_io_desc)); | 194 | iotable_init(lpc32xx_io_desc, ARRAY_SIZE(lpc32xx_io_desc)); |
195 | } | 195 | } |
196 | 196 | ||
197 | void lpc23xx_restart(enum reboot_mode mode, const char *cmd) | ||
198 | { | ||
199 | /* Make sure WDT clocks are enabled */ | ||
200 | __raw_writel(LPC32XX_CLKPWR_PWMCLK_WDOG_EN, | ||
201 | LPC32XX_CLKPWR_TIMER_CLK_CTRL); | ||
202 | |||
203 | /* Instant assert of RESETOUT_N with pulse length 1mS */ | ||
204 | __raw_writel(13000, io_p2v(LPC32XX_WDTIM_BASE + 0x18)); | ||
205 | __raw_writel(0x70, io_p2v(LPC32XX_WDTIM_BASE + 0xC)); | ||
206 | |||
207 | /* Wait for watchdog to reset system */ | ||
208 | while (1) | ||
209 | ; | ||
210 | } | ||
211 | |||
212 | static int __init lpc32xx_check_uid(void) | 197 | static int __init lpc32xx_check_uid(void) |
213 | { | 198 | { |
214 | u32 uid[4]; | 199 | u32 uid[4]; |
diff --git a/arch/arm/mach-lpc32xx/common.h b/arch/arm/mach-lpc32xx/common.h index 1cd8853b2f9b..2d90801ed1e1 100644 --- a/arch/arm/mach-lpc32xx/common.h +++ b/arch/arm/mach-lpc32xx/common.h | |||
@@ -30,7 +30,6 @@ extern void lpc32xx_timer_init(void); | |||
30 | extern void __init lpc32xx_init_irq(void); | 30 | extern void __init lpc32xx_init_irq(void); |
31 | extern void __init lpc32xx_map_io(void); | 31 | extern void __init lpc32xx_map_io(void); |
32 | extern void __init lpc32xx_serial_init(void); | 32 | extern void __init lpc32xx_serial_init(void); |
33 | extern void lpc23xx_restart(enum reboot_mode, const char *); | ||
34 | 33 | ||
35 | 34 | ||
36 | /* | 35 | /* |
diff --git a/arch/arm/mach-lpc32xx/phy3250.c b/arch/arm/mach-lpc32xx/phy3250.c index ee06fabdf60e..e6f03783ad73 100644 --- a/arch/arm/mach-lpc32xx/phy3250.c +++ b/arch/arm/mach-lpc32xx/phy3250.c | |||
@@ -262,5 +262,4 @@ DT_MACHINE_START(LPC32XX_DT, "LPC32XX SoC (Flattened Device Tree)") | |||
262 | .init_time = lpc32xx_timer_init, | 262 | .init_time = lpc32xx_timer_init, |
263 | .init_machine = lpc3250_machine_init, | 263 | .init_machine = lpc3250_machine_init, |
264 | .dt_compat = lpc32xx_dt_compat, | 264 | .dt_compat = lpc32xx_dt_compat, |
265 | .restart = lpc23xx_restart, | ||
266 | MACHINE_END | 265 | MACHINE_END |
diff --git a/drivers/watchdog/Kconfig b/drivers/watchdog/Kconfig index 9289da313d98..fb947655badd 100644 --- a/drivers/watchdog/Kconfig +++ b/drivers/watchdog/Kconfig | |||
@@ -202,6 +202,26 @@ config ARM_SP805_WATCHDOG | |||
202 | ARM Primecell SP805 Watchdog timer. This will reboot your system when | 202 | ARM Primecell SP805 Watchdog timer. This will reboot your system when |
203 | the timeout is reached. | 203 | the timeout is reached. |
204 | 204 | ||
205 | config ARM_SBSA_WATCHDOG | ||
206 | tristate "ARM SBSA Generic Watchdog" | ||
207 | depends on ARM64 | ||
208 | depends on ARM_ARCH_TIMER | ||
209 | select WATCHDOG_CORE | ||
210 | help | ||
211 | ARM SBSA Generic Watchdog has two stage timeouts: | ||
212 | the first signal (WS0) is for alerting the system by interrupt, | ||
213 | the second one (WS1) is a real hardware reset. | ||
214 | More details: ARM DEN0029B - Server Base System Architecture (SBSA) | ||
215 | |||
216 | This driver can operate ARM SBSA Generic Watchdog as a single stage | ||
217 | or a two stages watchdog, it depends on the module parameter "action". | ||
218 | |||
219 | Note: the maximum timeout in the two stages mode is half of that in | ||
220 | the single stage mode. | ||
221 | |||
222 | To compile this driver as module, choose M here: The module | ||
223 | will be called sbsa_gwdt. | ||
224 | |||
205 | config ASM9260_WATCHDOG | 225 | config ASM9260_WATCHDOG |
206 | tristate "Alphascale ASM9260 watchdog" | 226 | tristate "Alphascale ASM9260 watchdog" |
207 | depends on MACH_ASM9260 | 227 | depends on MACH_ASM9260 |
@@ -330,6 +350,7 @@ config SA1100_WATCHDOG | |||
330 | config DW_WATCHDOG | 350 | config DW_WATCHDOG |
331 | tristate "Synopsys DesignWare watchdog" | 351 | tristate "Synopsys DesignWare watchdog" |
332 | depends on HAS_IOMEM | 352 | depends on HAS_IOMEM |
353 | select WATCHDOG_CORE | ||
333 | help | 354 | help |
334 | Say Y here if to include support for the Synopsys DesignWare | 355 | Say Y here if to include support for the Synopsys DesignWare |
335 | watchdog timer found in many chips. | 356 | watchdog timer found in many chips. |
@@ -399,6 +420,7 @@ config DAVINCI_WATCHDOG | |||
399 | config ORION_WATCHDOG | 420 | config ORION_WATCHDOG |
400 | tristate "Orion watchdog" | 421 | tristate "Orion watchdog" |
401 | depends on ARCH_ORION5X || ARCH_DOVE || MACH_DOVE || ARCH_MVEBU | 422 | depends on ARCH_ORION5X || ARCH_DOVE || MACH_DOVE || ARCH_MVEBU |
423 | depends on ARM | ||
402 | select WATCHDOG_CORE | 424 | select WATCHDOG_CORE |
403 | help | 425 | help |
404 | Say Y here if to include support for the watchdog timer | 426 | Say Y here if to include support for the watchdog timer |
@@ -468,6 +490,7 @@ config NUC900_WATCHDOG | |||
468 | config TS4800_WATCHDOG | 490 | config TS4800_WATCHDOG |
469 | tristate "TS-4800 Watchdog" | 491 | tristate "TS-4800 Watchdog" |
470 | depends on HAS_IOMEM && OF | 492 | depends on HAS_IOMEM && OF |
493 | depends on SOC_IMX51 || COMPILE_TEST | ||
471 | select WATCHDOG_CORE | 494 | select WATCHDOG_CORE |
472 | select MFD_SYSCON | 495 | select MFD_SYSCON |
473 | help | 496 | help |
@@ -713,6 +736,15 @@ config ALIM7101_WDT | |||
713 | 736 | ||
714 | Most people will say N. | 737 | Most people will say N. |
715 | 738 | ||
739 | config EBC_C384_WDT | ||
740 | tristate "WinSystems EBC-C384 Watchdog Timer" | ||
741 | depends on X86 | ||
742 | select WATCHDOG_CORE | ||
743 | help | ||
744 | Enables watchdog timer support for the watchdog timer on the | ||
745 | WinSystems EBC-C384 motherboard. The timeout may be configured via | ||
746 | the timeout module parameter. | ||
747 | |||
716 | config F71808E_WDT | 748 | config F71808E_WDT |
717 | tristate "Fintek F71808E, F71862FG, F71869, F71882FG and F71889FG Watchdog" | 749 | tristate "Fintek F71808E, F71862FG, F71869, F71882FG and F71889FG Watchdog" |
718 | depends on X86 | 750 | depends on X86 |
@@ -1142,6 +1174,7 @@ config W83627HF_WDT | |||
1142 | NCT6779 | 1174 | NCT6779 |
1143 | NCT6791 | 1175 | NCT6791 |
1144 | NCT6792 | 1176 | NCT6792 |
1177 | NCT6102D/04D/06D | ||
1145 | 1178 | ||
1146 | This watchdog simply watches your kernel to make sure it doesn't | 1179 | This watchdog simply watches your kernel to make sure it doesn't |
1147 | freeze, and if it does, it reboots your computer after a certain | 1180 | freeze, and if it does, it reboots your computer after a certain |
@@ -1229,6 +1262,17 @@ config INTEL_MEI_WDT | |||
1229 | To compile this driver as a module, choose M here: | 1262 | To compile this driver as a module, choose M here: |
1230 | the module will be called mei_wdt. | 1263 | the module will be called mei_wdt. |
1231 | 1264 | ||
1265 | config NI903X_WDT | ||
1266 | tristate "NI 903x/913x Watchdog" | ||
1267 | depends on X86 && ACPI | ||
1268 | select WATCHDOG_CORE | ||
1269 | ---help--- | ||
1270 | This is the driver for the watchdog timer on the National Instruments | ||
1271 | 903x/913x real-time controllers. | ||
1272 | |||
1273 | To compile this driver as a module, choose M here: the module will be | ||
1274 | called ni903x_wdt. | ||
1275 | |||
1232 | # M32R Architecture | 1276 | # M32R Architecture |
1233 | 1277 | ||
1234 | # M68K Architecture | 1278 | # M68K Architecture |
@@ -1392,10 +1436,12 @@ config BCM7038_WDT | |||
1392 | tristate "BCM7038 Watchdog" | 1436 | tristate "BCM7038 Watchdog" |
1393 | select WATCHDOG_CORE | 1437 | select WATCHDOG_CORE |
1394 | depends on HAS_IOMEM | 1438 | depends on HAS_IOMEM |
1439 | depends on ARCH_BRCMSTB || BMIPS_GENERIC || COMPILE_TEST | ||
1395 | help | 1440 | help |
1396 | Watchdog driver for the built-in hardware in Broadcom 7038 SoCs. | 1441 | Watchdog driver for the built-in hardware in Broadcom 7038 and |
1397 | 1442 | later SoCs used in set-top boxes. BCM7038 was made public | |
1398 | Say 'Y or 'M' here to enable the driver. | 1443 | during the 2004 CES, and since then, many Broadcom chips use this |
1444 | watchdog block, including some cable modem chips. | ||
1399 | 1445 | ||
1400 | config IMGPDC_WDT | 1446 | config IMGPDC_WDT |
1401 | tristate "Imagination Technologies PDC Watchdog Timer" | 1447 | tristate "Imagination Technologies PDC Watchdog Timer" |
diff --git a/drivers/watchdog/Makefile b/drivers/watchdog/Makefile index 14bd772d3e66..feb6270fdbde 100644 --- a/drivers/watchdog/Makefile +++ b/drivers/watchdog/Makefile | |||
@@ -30,6 +30,7 @@ obj-$(CONFIG_USBPCWATCHDOG) += pcwd_usb.o | |||
30 | 30 | ||
31 | # ARM Architecture | 31 | # ARM Architecture |
32 | obj-$(CONFIG_ARM_SP805_WATCHDOG) += sp805_wdt.o | 32 | obj-$(CONFIG_ARM_SP805_WATCHDOG) += sp805_wdt.o |
33 | obj-$(CONFIG_ARM_SBSA_WATCHDOG) += sbsa_gwdt.o | ||
33 | obj-$(CONFIG_ASM9260_WATCHDOG) += asm9260_wdt.o | 34 | obj-$(CONFIG_ASM9260_WATCHDOG) += asm9260_wdt.o |
34 | obj-$(CONFIG_AT91RM9200_WATCHDOG) += at91rm9200_wdt.o | 35 | obj-$(CONFIG_AT91RM9200_WATCHDOG) += at91rm9200_wdt.o |
35 | obj-$(CONFIG_AT91SAM9X_WATCHDOG) += at91sam9_wdt.o | 36 | obj-$(CONFIG_AT91SAM9X_WATCHDOG) += at91sam9_wdt.o |
@@ -88,6 +89,7 @@ obj-$(CONFIG_ACQUIRE_WDT) += acquirewdt.o | |||
88 | obj-$(CONFIG_ADVANTECH_WDT) += advantechwdt.o | 89 | obj-$(CONFIG_ADVANTECH_WDT) += advantechwdt.o |
89 | obj-$(CONFIG_ALIM1535_WDT) += alim1535_wdt.o | 90 | obj-$(CONFIG_ALIM1535_WDT) += alim1535_wdt.o |
90 | obj-$(CONFIG_ALIM7101_WDT) += alim7101_wdt.o | 91 | obj-$(CONFIG_ALIM7101_WDT) += alim7101_wdt.o |
92 | obj-$(CONFIG_EBC_C384_WDT) += ebc-c384_wdt.o | ||
91 | obj-$(CONFIG_F71808E_WDT) += f71808e_wdt.o | 93 | obj-$(CONFIG_F71808E_WDT) += f71808e_wdt.o |
92 | obj-$(CONFIG_SP5100_TCO) += sp5100_tco.o | 94 | obj-$(CONFIG_SP5100_TCO) += sp5100_tco.o |
93 | obj-$(CONFIG_GEODE_WDT) += geodewdt.o | 95 | obj-$(CONFIG_GEODE_WDT) += geodewdt.o |
@@ -127,6 +129,7 @@ obj-$(CONFIG_SBC_EPX_C3_WATCHDOG) += sbc_epx_c3.o | |||
127 | obj-$(CONFIG_INTEL_SCU_WATCHDOG) += intel_scu_watchdog.o | 129 | obj-$(CONFIG_INTEL_SCU_WATCHDOG) += intel_scu_watchdog.o |
128 | obj-$(CONFIG_INTEL_MID_WATCHDOG) += intel-mid_wdt.o | 130 | obj-$(CONFIG_INTEL_MID_WATCHDOG) += intel-mid_wdt.o |
129 | obj-$(CONFIG_INTEL_MEI_WDT) += mei_wdt.o | 131 | obj-$(CONFIG_INTEL_MEI_WDT) += mei_wdt.o |
132 | obj-$(CONFIG_NI903X_WDT) += ni903x_wdt.o | ||
130 | 133 | ||
131 | # M32R Architecture | 134 | # M32R Architecture |
132 | 135 | ||
diff --git a/drivers/watchdog/atlas7_wdt.c b/drivers/watchdog/atlas7_wdt.c index df6d9242a319..ed80734befae 100644 --- a/drivers/watchdog/atlas7_wdt.c +++ b/drivers/watchdog/atlas7_wdt.c | |||
@@ -154,6 +154,11 @@ static int atlas7_wdt_probe(struct platform_device *pdev) | |||
154 | writel(0, wdt->base + ATLAS7_WDT_CNT_CTRL); | 154 | writel(0, wdt->base + ATLAS7_WDT_CNT_CTRL); |
155 | 155 | ||
156 | wdt->tick_rate = clk_get_rate(clk); | 156 | wdt->tick_rate = clk_get_rate(clk); |
157 | if (!wdt->tick_rate) { | ||
158 | ret = -EINVAL; | ||
159 | goto err1; | ||
160 | } | ||
161 | |||
157 | wdt->clk = clk; | 162 | wdt->clk = clk; |
158 | atlas7_wdd.min_timeout = 1; | 163 | atlas7_wdd.min_timeout = 1; |
159 | atlas7_wdd.max_timeout = UINT_MAX / wdt->tick_rate; | 164 | atlas7_wdd.max_timeout = UINT_MAX / wdt->tick_rate; |
diff --git a/drivers/watchdog/bcm47xx_wdt.c b/drivers/watchdog/bcm47xx_wdt.c index df1c2a4b0165..a1900b9ab6c4 100644 --- a/drivers/watchdog/bcm47xx_wdt.c +++ b/drivers/watchdog/bcm47xx_wdt.c | |||
@@ -87,7 +87,8 @@ static int bcm47xx_wdt_hard_set_timeout(struct watchdog_device *wdd, | |||
87 | return 0; | 87 | return 0; |
88 | } | 88 | } |
89 | 89 | ||
90 | static int bcm47xx_wdt_restart(struct watchdog_device *wdd) | 90 | static int bcm47xx_wdt_restart(struct watchdog_device *wdd, |
91 | unsigned long action, void *data) | ||
91 | { | 92 | { |
92 | struct bcm47xx_wdt *wdt = bcm47xx_wdt_get(wdd); | 93 | struct bcm47xx_wdt *wdt = bcm47xx_wdt_get(wdd); |
93 | 94 | ||
diff --git a/drivers/watchdog/da9063_wdt.c b/drivers/watchdog/da9063_wdt.c index 11e887572649..a100f648880d 100644 --- a/drivers/watchdog/da9063_wdt.c +++ b/drivers/watchdog/da9063_wdt.c | |||
@@ -119,7 +119,8 @@ static int da9063_wdt_set_timeout(struct watchdog_device *wdd, | |||
119 | return ret; | 119 | return ret; |
120 | } | 120 | } |
121 | 121 | ||
122 | static int da9063_wdt_restart(struct watchdog_device *wdd) | 122 | static int da9063_wdt_restart(struct watchdog_device *wdd, unsigned long action, |
123 | void *data) | ||
123 | { | 124 | { |
124 | struct da9063_watchdog *wdt = watchdog_get_drvdata(wdd); | 125 | struct da9063_watchdog *wdt = watchdog_get_drvdata(wdd); |
125 | int ret; | 126 | int ret; |
diff --git a/drivers/watchdog/digicolor_wdt.c b/drivers/watchdog/digicolor_wdt.c index 1ccb0b239348..77df772406b0 100644 --- a/drivers/watchdog/digicolor_wdt.c +++ b/drivers/watchdog/digicolor_wdt.c | |||
@@ -48,7 +48,8 @@ static void dc_wdt_set(struct dc_wdt *wdt, u32 ticks) | |||
48 | spin_unlock_irqrestore(&wdt->lock, flags); | 48 | spin_unlock_irqrestore(&wdt->lock, flags); |
49 | } | 49 | } |
50 | 50 | ||
51 | static int dc_wdt_restart(struct watchdog_device *wdog) | 51 | static int dc_wdt_restart(struct watchdog_device *wdog, unsigned long action, |
52 | void *data) | ||
52 | { | 53 | { |
53 | struct dc_wdt *wdt = watchdog_get_drvdata(wdog); | 54 | struct dc_wdt *wdt = watchdog_get_drvdata(wdog); |
54 | 55 | ||
diff --git a/drivers/watchdog/dw_wdt.c b/drivers/watchdog/dw_wdt.c index 8fefa4ad46d4..2acb51cf5504 100644 --- a/drivers/watchdog/dw_wdt.c +++ b/drivers/watchdog/dw_wdt.c | |||
@@ -12,9 +12,8 @@ | |||
12 | * and these are a function of the input clock frequency. | 12 | * and these are a function of the input clock frequency. |
13 | * | 13 | * |
14 | * The DesignWare watchdog cannot be stopped once it has been started so we | 14 | * The DesignWare watchdog cannot be stopped once it has been started so we |
15 | * use a software timer to implement a ping that will keep the watchdog alive. | 15 | * do not implement a stop function. The watchdog core will continue to send |
16 | * If we receive an expected close for the watchdog then we keep the timer | 16 | * heartbeat requests after the watchdog device has been closed. |
17 | * running, otherwise the timer is stopped and the watchdog will expire. | ||
18 | */ | 17 | */ |
19 | 18 | ||
20 | #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt | 19 | #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt |
@@ -22,12 +21,9 @@ | |||
22 | #include <linux/bitops.h> | 21 | #include <linux/bitops.h> |
23 | #include <linux/clk.h> | 22 | #include <linux/clk.h> |
24 | #include <linux/delay.h> | 23 | #include <linux/delay.h> |
25 | #include <linux/device.h> | ||
26 | #include <linux/err.h> | 24 | #include <linux/err.h> |
27 | #include <linux/fs.h> | ||
28 | #include <linux/io.h> | 25 | #include <linux/io.h> |
29 | #include <linux/kernel.h> | 26 | #include <linux/kernel.h> |
30 | #include <linux/miscdevice.h> | ||
31 | #include <linux/module.h> | 27 | #include <linux/module.h> |
32 | #include <linux/moduleparam.h> | 28 | #include <linux/moduleparam.h> |
33 | #include <linux/notifier.h> | 29 | #include <linux/notifier.h> |
@@ -35,8 +31,6 @@ | |||
35 | #include <linux/pm.h> | 31 | #include <linux/pm.h> |
36 | #include <linux/platform_device.h> | 32 | #include <linux/platform_device.h> |
37 | #include <linux/reboot.h> | 33 | #include <linux/reboot.h> |
38 | #include <linux/timer.h> | ||
39 | #include <linux/uaccess.h> | ||
40 | #include <linux/watchdog.h> | 34 | #include <linux/watchdog.h> |
41 | 35 | ||
42 | #define WDOG_CONTROL_REG_OFFSET 0x00 | 36 | #define WDOG_CONTROL_REG_OFFSET 0x00 |
@@ -57,53 +51,50 @@ module_param(nowayout, bool, 0); | |||
57 | MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started " | 51 | MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started " |
58 | "(default=" __MODULE_STRING(WATCHDOG_NOWAYOUT) ")"); | 52 | "(default=" __MODULE_STRING(WATCHDOG_NOWAYOUT) ")"); |
59 | 53 | ||
60 | #define WDT_TIMEOUT (HZ / 2) | 54 | struct dw_wdt { |
61 | |||
62 | static struct { | ||
63 | void __iomem *regs; | 55 | void __iomem *regs; |
64 | struct clk *clk; | 56 | struct clk *clk; |
65 | unsigned long in_use; | ||
66 | unsigned long next_heartbeat; | ||
67 | struct timer_list timer; | ||
68 | int expect_close; | ||
69 | struct notifier_block restart_handler; | 57 | struct notifier_block restart_handler; |
70 | } dw_wdt; | 58 | struct watchdog_device wdd; |
59 | }; | ||
60 | |||
61 | #define to_dw_wdt(wdd) container_of(wdd, struct dw_wdt, wdd) | ||
71 | 62 | ||
72 | static inline int dw_wdt_is_enabled(void) | 63 | static inline int dw_wdt_is_enabled(struct dw_wdt *dw_wdt) |
73 | { | 64 | { |
74 | return readl(dw_wdt.regs + WDOG_CONTROL_REG_OFFSET) & | 65 | return readl(dw_wdt->regs + WDOG_CONTROL_REG_OFFSET) & |
75 | WDOG_CONTROL_REG_WDT_EN_MASK; | 66 | WDOG_CONTROL_REG_WDT_EN_MASK; |
76 | } | 67 | } |
77 | 68 | ||
78 | static inline int dw_wdt_top_in_seconds(unsigned top) | 69 | static inline int dw_wdt_top_in_seconds(struct dw_wdt *dw_wdt, unsigned top) |
79 | { | 70 | { |
80 | /* | 71 | /* |
81 | * There are 16 possible timeout values in 0..15 where the number of | 72 | * There are 16 possible timeout values in 0..15 where the number of |
82 | * cycles is 2 ^ (16 + i) and the watchdog counts down. | 73 | * cycles is 2 ^ (16 + i) and the watchdog counts down. |
83 | */ | 74 | */ |
84 | return (1U << (16 + top)) / clk_get_rate(dw_wdt.clk); | 75 | return (1U << (16 + top)) / clk_get_rate(dw_wdt->clk); |
85 | } | 76 | } |
86 | 77 | ||
87 | static int dw_wdt_get_top(void) | 78 | static int dw_wdt_get_top(struct dw_wdt *dw_wdt) |
88 | { | 79 | { |
89 | int top = readl(dw_wdt.regs + WDOG_TIMEOUT_RANGE_REG_OFFSET) & 0xF; | 80 | int top = readl(dw_wdt->regs + WDOG_TIMEOUT_RANGE_REG_OFFSET) & 0xF; |
90 | 81 | ||
91 | return dw_wdt_top_in_seconds(top); | 82 | return dw_wdt_top_in_seconds(dw_wdt, top); |
92 | } | 83 | } |
93 | 84 | ||
94 | static inline void dw_wdt_set_next_heartbeat(void) | 85 | static int dw_wdt_ping(struct watchdog_device *wdd) |
95 | { | 86 | { |
96 | dw_wdt.next_heartbeat = jiffies + dw_wdt_get_top() * HZ; | 87 | struct dw_wdt *dw_wdt = to_dw_wdt(wdd); |
97 | } | ||
98 | 88 | ||
99 | static void dw_wdt_keepalive(void) | 89 | writel(WDOG_COUNTER_RESTART_KICK_VALUE, dw_wdt->regs + |
100 | { | ||
101 | writel(WDOG_COUNTER_RESTART_KICK_VALUE, dw_wdt.regs + | ||
102 | WDOG_COUNTER_RESTART_REG_OFFSET); | 90 | WDOG_COUNTER_RESTART_REG_OFFSET); |
91 | |||
92 | return 0; | ||
103 | } | 93 | } |
104 | 94 | ||
105 | static int dw_wdt_set_top(unsigned top_s) | 95 | static int dw_wdt_set_timeout(struct watchdog_device *wdd, unsigned int top_s) |
106 | { | 96 | { |
97 | struct dw_wdt *dw_wdt = to_dw_wdt(wdd); | ||
107 | int i, top_val = DW_WDT_MAX_TOP; | 98 | int i, top_val = DW_WDT_MAX_TOP; |
108 | 99 | ||
109 | /* | 100 | /* |
@@ -111,7 +102,7 @@ static int dw_wdt_set_top(unsigned top_s) | |||
111 | * always look for >=. | 102 | * always look for >=. |
112 | */ | 103 | */ |
113 | for (i = 0; i <= DW_WDT_MAX_TOP; ++i) | 104 | for (i = 0; i <= DW_WDT_MAX_TOP; ++i) |
114 | if (dw_wdt_top_in_seconds(i) >= top_s) { | 105 | if (dw_wdt_top_in_seconds(dw_wdt, i) >= top_s) { |
115 | top_val = i; | 106 | top_val = i; |
116 | break; | 107 | break; |
117 | } | 108 | } |
@@ -123,33 +114,43 @@ static int dw_wdt_set_top(unsigned top_s) | |||
123 | * effectively get a pat of the watchdog right here. | 114 | * effectively get a pat of the watchdog right here. |
124 | */ | 115 | */ |
125 | writel(top_val | top_val << WDOG_TIMEOUT_RANGE_TOPINIT_SHIFT, | 116 | writel(top_val | top_val << WDOG_TIMEOUT_RANGE_TOPINIT_SHIFT, |
126 | dw_wdt.regs + WDOG_TIMEOUT_RANGE_REG_OFFSET); | 117 | dw_wdt->regs + WDOG_TIMEOUT_RANGE_REG_OFFSET); |
127 | 118 | ||
128 | /* | 119 | wdd->timeout = dw_wdt_top_in_seconds(dw_wdt, top_val); |
129 | * Add an explicit pat to handle versions of the watchdog that | ||
130 | * don't have TOPINIT. This won't hurt on versions that have | ||
131 | * it. | ||
132 | */ | ||
133 | dw_wdt_keepalive(); | ||
134 | 120 | ||
135 | dw_wdt_set_next_heartbeat(); | 121 | return 0; |
122 | } | ||
123 | |||
124 | static int dw_wdt_start(struct watchdog_device *wdd) | ||
125 | { | ||
126 | struct dw_wdt *dw_wdt = to_dw_wdt(wdd); | ||
127 | |||
128 | dw_wdt_set_timeout(wdd, wdd->timeout); | ||
136 | 129 | ||
137 | return dw_wdt_top_in_seconds(top_val); | 130 | set_bit(WDOG_HW_RUNNING, &wdd->status); |
131 | |||
132 | writel(WDOG_CONTROL_REG_WDT_EN_MASK, | ||
133 | dw_wdt->regs + WDOG_CONTROL_REG_OFFSET); | ||
134 | |||
135 | return 0; | ||
138 | } | 136 | } |
139 | 137 | ||
140 | static int dw_wdt_restart_handle(struct notifier_block *this, | 138 | static int dw_wdt_restart_handle(struct notifier_block *this, |
141 | unsigned long mode, void *cmd) | 139 | unsigned long mode, void *cmd) |
142 | { | 140 | { |
141 | struct dw_wdt *dw_wdt; | ||
143 | u32 val; | 142 | u32 val; |
144 | 143 | ||
145 | writel(0, dw_wdt.regs + WDOG_TIMEOUT_RANGE_REG_OFFSET); | 144 | dw_wdt = container_of(this, struct dw_wdt, restart_handler); |
146 | val = readl(dw_wdt.regs + WDOG_CONTROL_REG_OFFSET); | 145 | |
146 | writel(0, dw_wdt->regs + WDOG_TIMEOUT_RANGE_REG_OFFSET); | ||
147 | val = readl(dw_wdt->regs + WDOG_CONTROL_REG_OFFSET); | ||
147 | if (val & WDOG_CONTROL_REG_WDT_EN_MASK) | 148 | if (val & WDOG_CONTROL_REG_WDT_EN_MASK) |
148 | writel(WDOG_COUNTER_RESTART_KICK_VALUE, dw_wdt.regs + | 149 | writel(WDOG_COUNTER_RESTART_KICK_VALUE, |
149 | WDOG_COUNTER_RESTART_REG_OFFSET); | 150 | dw_wdt->regs + WDOG_COUNTER_RESTART_REG_OFFSET); |
150 | else | 151 | else |
151 | writel(WDOG_CONTROL_REG_WDT_EN_MASK, | 152 | writel(WDOG_CONTROL_REG_WDT_EN_MASK, |
152 | dw_wdt.regs + WDOG_CONTROL_REG_OFFSET); | 153 | dw_wdt->regs + WDOG_CONTROL_REG_OFFSET); |
153 | 154 | ||
154 | /* wait for reset to assert... */ | 155 | /* wait for reset to assert... */ |
155 | mdelay(500); | 156 | mdelay(500); |
@@ -157,74 +158,12 @@ static int dw_wdt_restart_handle(struct notifier_block *this, | |||
157 | return NOTIFY_DONE; | 158 | return NOTIFY_DONE; |
158 | } | 159 | } |
159 | 160 | ||
160 | static void dw_wdt_ping(unsigned long data) | 161 | static unsigned int dw_wdt_get_timeleft(struct watchdog_device *wdd) |
161 | { | 162 | { |
162 | if (time_before(jiffies, dw_wdt.next_heartbeat) || | 163 | struct dw_wdt *dw_wdt = to_dw_wdt(wdd); |
163 | (!nowayout && !dw_wdt.in_use)) { | ||
164 | dw_wdt_keepalive(); | ||
165 | mod_timer(&dw_wdt.timer, jiffies + WDT_TIMEOUT); | ||
166 | } else | ||
167 | pr_crit("keepalive missed, machine will reset\n"); | ||
168 | } | ||
169 | |||
170 | static int dw_wdt_open(struct inode *inode, struct file *filp) | ||
171 | { | ||
172 | if (test_and_set_bit(0, &dw_wdt.in_use)) | ||
173 | return -EBUSY; | ||
174 | |||
175 | /* Make sure we don't get unloaded. */ | ||
176 | __module_get(THIS_MODULE); | ||
177 | |||
178 | if (!dw_wdt_is_enabled()) { | ||
179 | /* | ||
180 | * The watchdog is not currently enabled. Set the timeout to | ||
181 | * something reasonable and then start it. | ||
182 | */ | ||
183 | dw_wdt_set_top(DW_WDT_DEFAULT_SECONDS); | ||
184 | writel(WDOG_CONTROL_REG_WDT_EN_MASK, | ||
185 | dw_wdt.regs + WDOG_CONTROL_REG_OFFSET); | ||
186 | } | ||
187 | |||
188 | dw_wdt_set_next_heartbeat(); | ||
189 | |||
190 | return nonseekable_open(inode, filp); | ||
191 | } | ||
192 | |||
193 | static ssize_t dw_wdt_write(struct file *filp, const char __user *buf, | ||
194 | size_t len, loff_t *offset) | ||
195 | { | ||
196 | if (!len) | ||
197 | return 0; | ||
198 | |||
199 | if (!nowayout) { | ||
200 | size_t i; | ||
201 | |||
202 | dw_wdt.expect_close = 0; | ||
203 | |||
204 | for (i = 0; i < len; ++i) { | ||
205 | char c; | ||
206 | |||
207 | if (get_user(c, buf + i)) | ||
208 | return -EFAULT; | ||
209 | |||
210 | if (c == 'V') { | ||
211 | dw_wdt.expect_close = 1; | ||
212 | break; | ||
213 | } | ||
214 | } | ||
215 | } | ||
216 | 164 | ||
217 | dw_wdt_set_next_heartbeat(); | 165 | return readl(dw_wdt->regs + WDOG_CURRENT_COUNT_REG_OFFSET) / |
218 | dw_wdt_keepalive(); | 166 | clk_get_rate(dw_wdt->clk); |
219 | mod_timer(&dw_wdt.timer, jiffies + WDT_TIMEOUT); | ||
220 | |||
221 | return len; | ||
222 | } | ||
223 | |||
224 | static u32 dw_wdt_time_left(void) | ||
225 | { | ||
226 | return readl(dw_wdt.regs + WDOG_CURRENT_COUNT_REG_OFFSET) / | ||
227 | clk_get_rate(dw_wdt.clk); | ||
228 | } | 167 | } |
229 | 168 | ||
230 | static const struct watchdog_info dw_wdt_ident = { | 169 | static const struct watchdog_info dw_wdt_ident = { |
@@ -233,78 +172,33 @@ static const struct watchdog_info dw_wdt_ident = { | |||
233 | .identity = "Synopsys DesignWare Watchdog", | 172 | .identity = "Synopsys DesignWare Watchdog", |
234 | }; | 173 | }; |
235 | 174 | ||
236 | static long dw_wdt_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) | 175 | static const struct watchdog_ops dw_wdt_ops = { |
237 | { | 176 | .owner = THIS_MODULE, |
238 | unsigned long val; | 177 | .start = dw_wdt_start, |
239 | int timeout; | 178 | .ping = dw_wdt_ping, |
240 | 179 | .set_timeout = dw_wdt_set_timeout, | |
241 | switch (cmd) { | 180 | .get_timeleft = dw_wdt_get_timeleft, |
242 | case WDIOC_GETSUPPORT: | 181 | }; |
243 | return copy_to_user((void __user *)arg, &dw_wdt_ident, | ||
244 | sizeof(dw_wdt_ident)) ? -EFAULT : 0; | ||
245 | |||
246 | case WDIOC_GETSTATUS: | ||
247 | case WDIOC_GETBOOTSTATUS: | ||
248 | return put_user(0, (int __user *)arg); | ||
249 | |||
250 | case WDIOC_KEEPALIVE: | ||
251 | dw_wdt_set_next_heartbeat(); | ||
252 | return 0; | ||
253 | |||
254 | case WDIOC_SETTIMEOUT: | ||
255 | if (get_user(val, (int __user *)arg)) | ||
256 | return -EFAULT; | ||
257 | timeout = dw_wdt_set_top(val); | ||
258 | return put_user(timeout , (int __user *)arg); | ||
259 | |||
260 | case WDIOC_GETTIMEOUT: | ||
261 | return put_user(dw_wdt_get_top(), (int __user *)arg); | ||
262 | |||
263 | case WDIOC_GETTIMELEFT: | ||
264 | /* Get the time left until expiry. */ | ||
265 | if (get_user(val, (int __user *)arg)) | ||
266 | return -EFAULT; | ||
267 | return put_user(dw_wdt_time_left(), (int __user *)arg); | ||
268 | |||
269 | default: | ||
270 | return -ENOTTY; | ||
271 | } | ||
272 | } | ||
273 | |||
274 | static int dw_wdt_release(struct inode *inode, struct file *filp) | ||
275 | { | ||
276 | clear_bit(0, &dw_wdt.in_use); | ||
277 | |||
278 | if (!dw_wdt.expect_close) { | ||
279 | del_timer(&dw_wdt.timer); | ||
280 | |||
281 | if (!nowayout) | ||
282 | pr_crit("unexpected close, system will reboot soon\n"); | ||
283 | else | ||
284 | pr_crit("watchdog cannot be disabled, system will reboot soon\n"); | ||
285 | } | ||
286 | |||
287 | dw_wdt.expect_close = 0; | ||
288 | |||
289 | return 0; | ||
290 | } | ||
291 | 182 | ||
292 | #ifdef CONFIG_PM_SLEEP | 183 | #ifdef CONFIG_PM_SLEEP |
293 | static int dw_wdt_suspend(struct device *dev) | 184 | static int dw_wdt_suspend(struct device *dev) |
294 | { | 185 | { |
295 | clk_disable_unprepare(dw_wdt.clk); | 186 | struct dw_wdt *dw_wdt = dev_get_drvdata(dev); |
187 | |||
188 | clk_disable_unprepare(dw_wdt->clk); | ||
296 | 189 | ||
297 | return 0; | 190 | return 0; |
298 | } | 191 | } |
299 | 192 | ||
300 | static int dw_wdt_resume(struct device *dev) | 193 | static int dw_wdt_resume(struct device *dev) |
301 | { | 194 | { |
302 | int err = clk_prepare_enable(dw_wdt.clk); | 195 | struct dw_wdt *dw_wdt = dev_get_drvdata(dev); |
196 | int err = clk_prepare_enable(dw_wdt->clk); | ||
303 | 197 | ||
304 | if (err) | 198 | if (err) |
305 | return err; | 199 | return err; |
306 | 200 | ||
307 | dw_wdt_keepalive(); | 201 | dw_wdt_ping(&dw_wdt->wdd); |
308 | 202 | ||
309 | return 0; | 203 | return 0; |
310 | } | 204 | } |
@@ -312,67 +206,82 @@ static int dw_wdt_resume(struct device *dev) | |||
312 | 206 | ||
313 | static SIMPLE_DEV_PM_OPS(dw_wdt_pm_ops, dw_wdt_suspend, dw_wdt_resume); | 207 | static SIMPLE_DEV_PM_OPS(dw_wdt_pm_ops, dw_wdt_suspend, dw_wdt_resume); |
314 | 208 | ||
315 | static const struct file_operations wdt_fops = { | ||
316 | .owner = THIS_MODULE, | ||
317 | .llseek = no_llseek, | ||
318 | .open = dw_wdt_open, | ||
319 | .write = dw_wdt_write, | ||
320 | .unlocked_ioctl = dw_wdt_ioctl, | ||
321 | .release = dw_wdt_release | ||
322 | }; | ||
323 | |||
324 | static struct miscdevice dw_wdt_miscdev = { | ||
325 | .fops = &wdt_fops, | ||
326 | .name = "watchdog", | ||
327 | .minor = WATCHDOG_MINOR, | ||
328 | }; | ||
329 | |||
330 | static int dw_wdt_drv_probe(struct platform_device *pdev) | 209 | static int dw_wdt_drv_probe(struct platform_device *pdev) |
331 | { | 210 | { |
211 | struct device *dev = &pdev->dev; | ||
212 | struct watchdog_device *wdd; | ||
213 | struct dw_wdt *dw_wdt; | ||
214 | struct resource *mem; | ||
332 | int ret; | 215 | int ret; |
333 | struct resource *mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); | ||
334 | 216 | ||
335 | dw_wdt.regs = devm_ioremap_resource(&pdev->dev, mem); | 217 | dw_wdt = devm_kzalloc(dev, sizeof(*dw_wdt), GFP_KERNEL); |
336 | if (IS_ERR(dw_wdt.regs)) | 218 | if (!dw_wdt) |
337 | return PTR_ERR(dw_wdt.regs); | 219 | return -ENOMEM; |
220 | |||
221 | mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); | ||
222 | dw_wdt->regs = devm_ioremap_resource(dev, mem); | ||
223 | if (IS_ERR(dw_wdt->regs)) | ||
224 | return PTR_ERR(dw_wdt->regs); | ||
338 | 225 | ||
339 | dw_wdt.clk = devm_clk_get(&pdev->dev, NULL); | 226 | dw_wdt->clk = devm_clk_get(dev, NULL); |
340 | if (IS_ERR(dw_wdt.clk)) | 227 | if (IS_ERR(dw_wdt->clk)) |
341 | return PTR_ERR(dw_wdt.clk); | 228 | return PTR_ERR(dw_wdt->clk); |
342 | 229 | ||
343 | ret = clk_prepare_enable(dw_wdt.clk); | 230 | ret = clk_prepare_enable(dw_wdt->clk); |
344 | if (ret) | 231 | if (ret) |
345 | return ret; | 232 | return ret; |
346 | 233 | ||
347 | ret = misc_register(&dw_wdt_miscdev); | 234 | wdd = &dw_wdt->wdd; |
235 | wdd->info = &dw_wdt_ident; | ||
236 | wdd->ops = &dw_wdt_ops; | ||
237 | wdd->min_timeout = 1; | ||
238 | wdd->max_hw_heartbeat_ms = | ||
239 | dw_wdt_top_in_seconds(dw_wdt, DW_WDT_MAX_TOP) * 1000; | ||
240 | wdd->parent = dev; | ||
241 | |||
242 | watchdog_set_drvdata(wdd, dw_wdt); | ||
243 | watchdog_set_nowayout(wdd, nowayout); | ||
244 | watchdog_init_timeout(wdd, 0, dev); | ||
245 | |||
246 | /* | ||
247 | * If the watchdog is already running, use its already configured | ||
248 | * timeout. Otherwise use the default or the value provided through | ||
249 | * devicetree. | ||
250 | */ | ||
251 | if (dw_wdt_is_enabled(dw_wdt)) { | ||
252 | wdd->timeout = dw_wdt_get_top(dw_wdt); | ||
253 | set_bit(WDOG_HW_RUNNING, &wdd->status); | ||
254 | } else { | ||
255 | wdd->timeout = DW_WDT_DEFAULT_SECONDS; | ||
256 | watchdog_init_timeout(wdd, 0, dev); | ||
257 | } | ||
258 | |||
259 | platform_set_drvdata(pdev, dw_wdt); | ||
260 | |||
261 | ret = watchdog_register_device(wdd); | ||
348 | if (ret) | 262 | if (ret) |
349 | goto out_disable_clk; | 263 | goto out_disable_clk; |
350 | 264 | ||
351 | dw_wdt.restart_handler.notifier_call = dw_wdt_restart_handle; | 265 | dw_wdt->restart_handler.notifier_call = dw_wdt_restart_handle; |
352 | dw_wdt.restart_handler.priority = 128; | 266 | dw_wdt->restart_handler.priority = 128; |
353 | ret = register_restart_handler(&dw_wdt.restart_handler); | 267 | ret = register_restart_handler(&dw_wdt->restart_handler); |
354 | if (ret) | 268 | if (ret) |
355 | pr_warn("cannot register restart handler\n"); | 269 | pr_warn("cannot register restart handler\n"); |
356 | 270 | ||
357 | dw_wdt_set_next_heartbeat(); | ||
358 | setup_timer(&dw_wdt.timer, dw_wdt_ping, 0); | ||
359 | mod_timer(&dw_wdt.timer, jiffies + WDT_TIMEOUT); | ||
360 | |||
361 | return 0; | 271 | return 0; |
362 | 272 | ||
363 | out_disable_clk: | 273 | out_disable_clk: |
364 | clk_disable_unprepare(dw_wdt.clk); | 274 | clk_disable_unprepare(dw_wdt->clk); |
365 | |||
366 | return ret; | 275 | return ret; |
367 | } | 276 | } |
368 | 277 | ||
369 | static int dw_wdt_drv_remove(struct platform_device *pdev) | 278 | static int dw_wdt_drv_remove(struct platform_device *pdev) |
370 | { | 279 | { |
371 | unregister_restart_handler(&dw_wdt.restart_handler); | 280 | struct dw_wdt *dw_wdt = platform_get_drvdata(pdev); |
372 | |||
373 | misc_deregister(&dw_wdt_miscdev); | ||
374 | 281 | ||
375 | clk_disable_unprepare(dw_wdt.clk); | 282 | unregister_restart_handler(&dw_wdt->restart_handler); |
283 | watchdog_unregister_device(&dw_wdt->wdd); | ||
284 | clk_disable_unprepare(dw_wdt->clk); | ||
376 | 285 | ||
377 | return 0; | 286 | return 0; |
378 | } | 287 | } |
diff --git a/drivers/watchdog/ebc-c384_wdt.c b/drivers/watchdog/ebc-c384_wdt.c new file mode 100644 index 000000000000..77fda0b4b90e --- /dev/null +++ b/drivers/watchdog/ebc-c384_wdt.c | |||
@@ -0,0 +1,188 @@ | |||
1 | /* | ||
2 | * Watchdog timer driver for the WinSystems EBC-C384 | ||
3 | * Copyright (C) 2016 William Breathitt Gray | ||
4 | * | ||
5 | * This program is free software; you can redistribute it and/or modify | ||
6 | * it under the terms of the GNU General Public License, version 2, as | ||
7 | * published by the Free Software Foundation. | ||
8 | * | ||
9 | * This program is distributed in the hope that it will be useful, but | ||
10 | * WITHOUT ANY WARRANTY; without even the implied warranty of | ||
11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
12 | * General Public License for more details. | ||
13 | */ | ||
14 | #include <linux/device.h> | ||
15 | #include <linux/dmi.h> | ||
16 | #include <linux/errno.h> | ||
17 | #include <linux/io.h> | ||
18 | #include <linux/ioport.h> | ||
19 | #include <linux/kernel.h> | ||
20 | #include <linux/module.h> | ||
21 | #include <linux/moduleparam.h> | ||
22 | #include <linux/platform_device.h> | ||
23 | #include <linux/types.h> | ||
24 | #include <linux/watchdog.h> | ||
25 | |||
26 | #define MODULE_NAME "ebc-c384_wdt" | ||
27 | #define WATCHDOG_TIMEOUT 60 | ||
28 | /* | ||
29 | * The timeout value in minutes must fit in a single byte when sent to the | ||
30 | * watchdog timer; the maximum timeout possible is 15300 (255 * 60) seconds. | ||
31 | */ | ||
32 | #define WATCHDOG_MAX_TIMEOUT 15300 | ||
33 | #define BASE_ADDR 0x564 | ||
34 | #define ADDR_EXTENT 5 | ||
35 | #define CFG_ADDR (BASE_ADDR + 1) | ||
36 | #define PET_ADDR (BASE_ADDR + 2) | ||
37 | |||
38 | static bool nowayout = WATCHDOG_NOWAYOUT; | ||
39 | module_param(nowayout, bool, 0); | ||
40 | MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default=" | ||
41 | __MODULE_STRING(WATCHDOG_NOWAYOUT) ")"); | ||
42 | |||
43 | static unsigned timeout; | ||
44 | module_param(timeout, uint, 0); | ||
45 | MODULE_PARM_DESC(timeout, "Watchdog timeout in seconds (default=" | ||
46 | __MODULE_STRING(WATCHDOG_TIMEOUT) ")"); | ||
47 | |||
48 | static int ebc_c384_wdt_start(struct watchdog_device *wdev) | ||
49 | { | ||
50 | unsigned t = wdev->timeout; | ||
51 | |||
52 | /* resolution is in minutes for timeouts greater than 255 seconds */ | ||
53 | if (t > 255) | ||
54 | t = DIV_ROUND_UP(t, 60); | ||
55 | |||
56 | outb(t, PET_ADDR); | ||
57 | |||
58 | return 0; | ||
59 | } | ||
60 | |||
61 | static int ebc_c384_wdt_stop(struct watchdog_device *wdev) | ||
62 | { | ||
63 | outb(0x00, PET_ADDR); | ||
64 | |||
65 | return 0; | ||
66 | } | ||
67 | |||
68 | static int ebc_c384_wdt_set_timeout(struct watchdog_device *wdev, unsigned t) | ||
69 | { | ||
70 | /* resolution is in minutes for timeouts greater than 255 seconds */ | ||
71 | if (t > 255) { | ||
72 | /* round second resolution up to minute granularity */ | ||
73 | wdev->timeout = roundup(t, 60); | ||
74 | |||
75 | /* set watchdog timer for minutes */ | ||
76 | outb(0x00, CFG_ADDR); | ||
77 | } else { | ||
78 | wdev->timeout = t; | ||
79 | |||
80 | /* set watchdog timer for seconds */ | ||
81 | outb(0x80, CFG_ADDR); | ||
82 | } | ||
83 | |||
84 | return 0; | ||
85 | } | ||
86 | |||
87 | static const struct watchdog_ops ebc_c384_wdt_ops = { | ||
88 | .start = ebc_c384_wdt_start, | ||
89 | .stop = ebc_c384_wdt_stop, | ||
90 | .set_timeout = ebc_c384_wdt_set_timeout | ||
91 | }; | ||
92 | |||
93 | static const struct watchdog_info ebc_c384_wdt_info = { | ||
94 | .options = WDIOF_KEEPALIVEPING | WDIOF_MAGICCLOSE | WDIOF_SETTIMEOUT, | ||
95 | .identity = MODULE_NAME | ||
96 | }; | ||
97 | |||
98 | static int __init ebc_c384_wdt_probe(struct platform_device *pdev) | ||
99 | { | ||
100 | struct device *dev = &pdev->dev; | ||
101 | struct watchdog_device *wdd; | ||
102 | |||
103 | if (!devm_request_region(dev, BASE_ADDR, ADDR_EXTENT, dev_name(dev))) { | ||
104 | dev_err(dev, "Unable to lock port addresses (0x%X-0x%X)\n", | ||
105 | BASE_ADDR, BASE_ADDR + ADDR_EXTENT); | ||
106 | return -EBUSY; | ||
107 | } | ||
108 | |||
109 | wdd = devm_kzalloc(dev, sizeof(*wdd), GFP_KERNEL); | ||
110 | if (!wdd) | ||
111 | return -ENOMEM; | ||
112 | |||
113 | wdd->info = &ebc_c384_wdt_info; | ||
114 | wdd->ops = &ebc_c384_wdt_ops; | ||
115 | wdd->timeout = WATCHDOG_TIMEOUT; | ||
116 | wdd->min_timeout = 1; | ||
117 | wdd->max_timeout = WATCHDOG_MAX_TIMEOUT; | ||
118 | |||
119 | watchdog_set_nowayout(wdd, nowayout); | ||
120 | |||
121 | if (watchdog_init_timeout(wdd, timeout, dev)) | ||
122 | dev_warn(dev, "Invalid timeout (%u seconds), using default (%u seconds)\n", | ||
123 | timeout, WATCHDOG_TIMEOUT); | ||
124 | |||
125 | platform_set_drvdata(pdev, wdd); | ||
126 | |||
127 | return watchdog_register_device(wdd); | ||
128 | } | ||
129 | |||
130 | static int ebc_c384_wdt_remove(struct platform_device *pdev) | ||
131 | { | ||
132 | struct watchdog_device *wdd = platform_get_drvdata(pdev); | ||
133 | |||
134 | watchdog_unregister_device(wdd); | ||
135 | |||
136 | return 0; | ||
137 | } | ||
138 | |||
139 | static struct platform_driver ebc_c384_wdt_driver = { | ||
140 | .driver = { | ||
141 | .name = MODULE_NAME | ||
142 | }, | ||
143 | .remove = ebc_c384_wdt_remove | ||
144 | }; | ||
145 | |||
146 | static struct platform_device *ebc_c384_wdt_device; | ||
147 | |||
148 | static int __init ebc_c384_wdt_init(void) | ||
149 | { | ||
150 | int err; | ||
151 | |||
152 | if (!dmi_match(DMI_BOARD_NAME, "EBC-C384 SBC")) | ||
153 | return -ENODEV; | ||
154 | |||
155 | ebc_c384_wdt_device = platform_device_alloc(MODULE_NAME, -1); | ||
156 | if (!ebc_c384_wdt_device) | ||
157 | return -ENOMEM; | ||
158 | |||
159 | err = platform_device_add(ebc_c384_wdt_device); | ||
160 | if (err) | ||
161 | goto err_platform_device; | ||
162 | |||
163 | err = platform_driver_probe(&ebc_c384_wdt_driver, ebc_c384_wdt_probe); | ||
164 | if (err) | ||
165 | goto err_platform_driver; | ||
166 | |||
167 | return 0; | ||
168 | |||
169 | err_platform_driver: | ||
170 | platform_device_del(ebc_c384_wdt_device); | ||
171 | err_platform_device: | ||
172 | platform_device_put(ebc_c384_wdt_device); | ||
173 | return err; | ||
174 | } | ||
175 | |||
176 | static void __exit ebc_c384_wdt_exit(void) | ||
177 | { | ||
178 | platform_device_unregister(ebc_c384_wdt_device); | ||
179 | platform_driver_unregister(&ebc_c384_wdt_driver); | ||
180 | } | ||
181 | |||
182 | module_init(ebc_c384_wdt_init); | ||
183 | module_exit(ebc_c384_wdt_exit); | ||
184 | |||
185 | MODULE_AUTHOR("William Breathitt Gray <vilhelm.gray@gmail.com>"); | ||
186 | MODULE_DESCRIPTION("WinSystems EBC-C384 watchdog timer driver"); | ||
187 | MODULE_LICENSE("GPL v2"); | ||
188 | MODULE_ALIAS("platform:" MODULE_NAME); | ||
diff --git a/drivers/watchdog/imgpdc_wdt.c b/drivers/watchdog/imgpdc_wdt.c index 3679f2e1922f..516fbef00856 100644 --- a/drivers/watchdog/imgpdc_wdt.c +++ b/drivers/watchdog/imgpdc_wdt.c | |||
@@ -150,7 +150,8 @@ static int pdc_wdt_start(struct watchdog_device *wdt_dev) | |||
150 | return 0; | 150 | return 0; |
151 | } | 151 | } |
152 | 152 | ||
153 | static int pdc_wdt_restart(struct watchdog_device *wdt_dev) | 153 | static int pdc_wdt_restart(struct watchdog_device *wdt_dev, |
154 | unsigned long action, void *data) | ||
154 | { | 155 | { |
155 | struct pdc_wdt_dev *wdt = watchdog_get_drvdata(wdt_dev); | 156 | struct pdc_wdt_dev *wdt = watchdog_get_drvdata(wdt_dev); |
156 | 157 | ||
diff --git a/drivers/watchdog/imx2_wdt.c b/drivers/watchdog/imx2_wdt.c index e47966aa2db0..331aed831dac 100644 --- a/drivers/watchdog/imx2_wdt.c +++ b/drivers/watchdog/imx2_wdt.c | |||
@@ -25,14 +25,12 @@ | |||
25 | #include <linux/delay.h> | 25 | #include <linux/delay.h> |
26 | #include <linux/init.h> | 26 | #include <linux/init.h> |
27 | #include <linux/io.h> | 27 | #include <linux/io.h> |
28 | #include <linux/jiffies.h> | ||
29 | #include <linux/kernel.h> | 28 | #include <linux/kernel.h> |
30 | #include <linux/module.h> | 29 | #include <linux/module.h> |
31 | #include <linux/moduleparam.h> | 30 | #include <linux/moduleparam.h> |
32 | #include <linux/of_address.h> | 31 | #include <linux/of_address.h> |
33 | #include <linux/platform_device.h> | 32 | #include <linux/platform_device.h> |
34 | #include <linux/regmap.h> | 33 | #include <linux/regmap.h> |
35 | #include <linux/timer.h> | ||
36 | #include <linux/watchdog.h> | 34 | #include <linux/watchdog.h> |
37 | 35 | ||
38 | #define DRIVER_NAME "imx2-wdt" | 36 | #define DRIVER_NAME "imx2-wdt" |
@@ -60,7 +58,6 @@ | |||
60 | struct imx2_wdt_device { | 58 | struct imx2_wdt_device { |
61 | struct clk *clk; | 59 | struct clk *clk; |
62 | struct regmap *regmap; | 60 | struct regmap *regmap; |
63 | struct timer_list timer; /* Pings the watchdog when closed */ | ||
64 | struct watchdog_device wdog; | 61 | struct watchdog_device wdog; |
65 | }; | 62 | }; |
66 | 63 | ||
@@ -80,7 +77,8 @@ static const struct watchdog_info imx2_wdt_info = { | |||
80 | .options = WDIOF_KEEPALIVEPING | WDIOF_SETTIMEOUT | WDIOF_MAGICCLOSE, | 77 | .options = WDIOF_KEEPALIVEPING | WDIOF_SETTIMEOUT | WDIOF_MAGICCLOSE, |
81 | }; | 78 | }; |
82 | 79 | ||
83 | static int imx2_wdt_restart(struct watchdog_device *wdog) | 80 | static int imx2_wdt_restart(struct watchdog_device *wdog, unsigned long action, |
81 | void *data) | ||
84 | { | 82 | { |
85 | struct imx2_wdt_device *wdev = watchdog_get_drvdata(wdog); | 83 | struct imx2_wdt_device *wdev = watchdog_get_drvdata(wdog); |
86 | unsigned int wcr_enable = IMX2_WDT_WCR_WDE; | 84 | unsigned int wcr_enable = IMX2_WDT_WCR_WDE; |
@@ -146,16 +144,6 @@ static int imx2_wdt_ping(struct watchdog_device *wdog) | |||
146 | return 0; | 144 | return 0; |
147 | } | 145 | } |
148 | 146 | ||
149 | static void imx2_wdt_timer_ping(unsigned long arg) | ||
150 | { | ||
151 | struct watchdog_device *wdog = (struct watchdog_device *)arg; | ||
152 | struct imx2_wdt_device *wdev = watchdog_get_drvdata(wdog); | ||
153 | |||
154 | /* ping it every wdog->timeout / 2 seconds to prevent reboot */ | ||
155 | imx2_wdt_ping(wdog); | ||
156 | mod_timer(&wdev->timer, jiffies + wdog->timeout * HZ / 2); | ||
157 | } | ||
158 | |||
159 | static int imx2_wdt_set_timeout(struct watchdog_device *wdog, | 147 | static int imx2_wdt_set_timeout(struct watchdog_device *wdog, |
160 | unsigned int new_timeout) | 148 | unsigned int new_timeout) |
161 | { | 149 | { |
@@ -172,40 +160,19 @@ static int imx2_wdt_start(struct watchdog_device *wdog) | |||
172 | { | 160 | { |
173 | struct imx2_wdt_device *wdev = watchdog_get_drvdata(wdog); | 161 | struct imx2_wdt_device *wdev = watchdog_get_drvdata(wdog); |
174 | 162 | ||
175 | if (imx2_wdt_is_running(wdev)) { | 163 | if (imx2_wdt_is_running(wdev)) |
176 | /* delete the timer that pings the watchdog after close */ | ||
177 | del_timer_sync(&wdev->timer); | ||
178 | imx2_wdt_set_timeout(wdog, wdog->timeout); | 164 | imx2_wdt_set_timeout(wdog, wdog->timeout); |
179 | } else | 165 | else |
180 | imx2_wdt_setup(wdog); | 166 | imx2_wdt_setup(wdog); |
181 | 167 | ||
182 | return imx2_wdt_ping(wdog); | 168 | set_bit(WDOG_HW_RUNNING, &wdog->status); |
183 | } | ||
184 | |||
185 | static int imx2_wdt_stop(struct watchdog_device *wdog) | ||
186 | { | ||
187 | /* | ||
188 | * We don't need a clk_disable, it cannot be disabled once started. | ||
189 | * We use a timer to ping the watchdog while /dev/watchdog is closed | ||
190 | */ | ||
191 | imx2_wdt_timer_ping((unsigned long)wdog); | ||
192 | return 0; | ||
193 | } | ||
194 | |||
195 | static inline void imx2_wdt_ping_if_active(struct watchdog_device *wdog) | ||
196 | { | ||
197 | struct imx2_wdt_device *wdev = watchdog_get_drvdata(wdog); | ||
198 | 169 | ||
199 | if (imx2_wdt_is_running(wdev)) { | 170 | return imx2_wdt_ping(wdog); |
200 | imx2_wdt_set_timeout(wdog, wdog->timeout); | ||
201 | imx2_wdt_timer_ping((unsigned long)wdog); | ||
202 | } | ||
203 | } | 171 | } |
204 | 172 | ||
205 | static const struct watchdog_ops imx2_wdt_ops = { | 173 | static const struct watchdog_ops imx2_wdt_ops = { |
206 | .owner = THIS_MODULE, | 174 | .owner = THIS_MODULE, |
207 | .start = imx2_wdt_start, | 175 | .start = imx2_wdt_start, |
208 | .stop = imx2_wdt_stop, | ||
209 | .ping = imx2_wdt_ping, | 176 | .ping = imx2_wdt_ping, |
210 | .set_timeout = imx2_wdt_set_timeout, | 177 | .set_timeout = imx2_wdt_set_timeout, |
211 | .restart = imx2_wdt_restart, | 178 | .restart = imx2_wdt_restart, |
@@ -253,7 +220,7 @@ static int __init imx2_wdt_probe(struct platform_device *pdev) | |||
253 | wdog->info = &imx2_wdt_info; | 220 | wdog->info = &imx2_wdt_info; |
254 | wdog->ops = &imx2_wdt_ops; | 221 | wdog->ops = &imx2_wdt_ops; |
255 | wdog->min_timeout = 1; | 222 | wdog->min_timeout = 1; |
256 | wdog->max_timeout = IMX2_WDT_MAX_TIME; | 223 | wdog->max_hw_heartbeat_ms = IMX2_WDT_MAX_TIME * 1000; |
257 | wdog->parent = &pdev->dev; | 224 | wdog->parent = &pdev->dev; |
258 | 225 | ||
259 | ret = clk_prepare_enable(wdev->clk); | 226 | ret = clk_prepare_enable(wdev->clk); |
@@ -274,9 +241,10 @@ static int __init imx2_wdt_probe(struct platform_device *pdev) | |||
274 | watchdog_set_restart_priority(wdog, 128); | 241 | watchdog_set_restart_priority(wdog, 128); |
275 | watchdog_init_timeout(wdog, timeout, &pdev->dev); | 242 | watchdog_init_timeout(wdog, timeout, &pdev->dev); |
276 | 243 | ||
277 | setup_timer(&wdev->timer, imx2_wdt_timer_ping, (unsigned long)wdog); | 244 | if (imx2_wdt_is_running(wdev)) { |
278 | 245 | imx2_wdt_set_timeout(wdog, wdog->timeout); | |
279 | imx2_wdt_ping_if_active(wdog); | 246 | set_bit(WDOG_HW_RUNNING, &wdog->status); |
247 | } | ||
280 | 248 | ||
281 | /* | 249 | /* |
282 | * Disable the watchdog power down counter at boot. Otherwise the power | 250 | * Disable the watchdog power down counter at boot. Otherwise the power |
@@ -309,7 +277,6 @@ static int __exit imx2_wdt_remove(struct platform_device *pdev) | |||
309 | watchdog_unregister_device(wdog); | 277 | watchdog_unregister_device(wdog); |
310 | 278 | ||
311 | if (imx2_wdt_is_running(wdev)) { | 279 | if (imx2_wdt_is_running(wdev)) { |
312 | del_timer_sync(&wdev->timer); | ||
313 | imx2_wdt_ping(wdog); | 280 | imx2_wdt_ping(wdog); |
314 | dev_crit(&pdev->dev, "Device removed: Expect reboot!\n"); | 281 | dev_crit(&pdev->dev, "Device removed: Expect reboot!\n"); |
315 | } | 282 | } |
@@ -323,10 +290,9 @@ static void imx2_wdt_shutdown(struct platform_device *pdev) | |||
323 | 290 | ||
324 | if (imx2_wdt_is_running(wdev)) { | 291 | if (imx2_wdt_is_running(wdev)) { |
325 | /* | 292 | /* |
326 | * We are running, we need to delete the timer but will | 293 | * We are running, configure max timeout before reboot |
327 | * give max timeout before reboot will take place | 294 | * will take place. |
328 | */ | 295 | */ |
329 | del_timer_sync(&wdev->timer); | ||
330 | imx2_wdt_set_timeout(wdog, IMX2_WDT_MAX_TIME); | 296 | imx2_wdt_set_timeout(wdog, IMX2_WDT_MAX_TIME); |
331 | imx2_wdt_ping(wdog); | 297 | imx2_wdt_ping(wdog); |
332 | dev_crit(&pdev->dev, "Device shutdown: Expect reboot!\n"); | 298 | dev_crit(&pdev->dev, "Device shutdown: Expect reboot!\n"); |
@@ -344,10 +310,6 @@ static int imx2_wdt_suspend(struct device *dev) | |||
344 | if (imx2_wdt_is_running(wdev)) { | 310 | if (imx2_wdt_is_running(wdev)) { |
345 | imx2_wdt_set_timeout(wdog, IMX2_WDT_MAX_TIME); | 311 | imx2_wdt_set_timeout(wdog, IMX2_WDT_MAX_TIME); |
346 | imx2_wdt_ping(wdog); | 312 | imx2_wdt_ping(wdog); |
347 | |||
348 | /* The watchdog is not active */ | ||
349 | if (!watchdog_active(wdog)) | ||
350 | del_timer_sync(&wdev->timer); | ||
351 | } | 313 | } |
352 | 314 | ||
353 | clk_disable_unprepare(wdev->clk); | 315 | clk_disable_unprepare(wdev->clk); |
@@ -373,19 +335,10 @@ static int imx2_wdt_resume(struct device *dev) | |||
373 | * watchdog again. | 335 | * watchdog again. |
374 | */ | 336 | */ |
375 | imx2_wdt_setup(wdog); | 337 | imx2_wdt_setup(wdog); |
338 | } | ||
339 | if (imx2_wdt_is_running(wdev)) { | ||
376 | imx2_wdt_set_timeout(wdog, wdog->timeout); | 340 | imx2_wdt_set_timeout(wdog, wdog->timeout); |
377 | imx2_wdt_ping(wdog); | 341 | imx2_wdt_ping(wdog); |
378 | } else if (imx2_wdt_is_running(wdev)) { | ||
379 | /* Resuming from non-deep sleep state. */ | ||
380 | imx2_wdt_set_timeout(wdog, wdog->timeout); | ||
381 | imx2_wdt_ping(wdog); | ||
382 | /* | ||
383 | * But the watchdog is not active, then start | ||
384 | * the timer again. | ||
385 | */ | ||
386 | if (!watchdog_active(wdog)) | ||
387 | mod_timer(&wdev->timer, | ||
388 | jiffies + wdog->timeout * HZ / 2); | ||
389 | } | 342 | } |
390 | 343 | ||
391 | return 0; | 344 | return 0; |
diff --git a/drivers/watchdog/lpc18xx_wdt.c b/drivers/watchdog/lpc18xx_wdt.c index 6914c83aa6d9..fd171e6caa16 100644 --- a/drivers/watchdog/lpc18xx_wdt.c +++ b/drivers/watchdog/lpc18xx_wdt.c | |||
@@ -153,7 +153,8 @@ static int lpc18xx_wdt_start(struct watchdog_device *wdt_dev) | |||
153 | return 0; | 153 | return 0; |
154 | } | 154 | } |
155 | 155 | ||
156 | static int lpc18xx_wdt_restart(struct watchdog_device *wdt_dev) | 156 | static int lpc18xx_wdt_restart(struct watchdog_device *wdt_dev, |
157 | unsigned long action, void *data) | ||
157 | { | 158 | { |
158 | struct lpc18xx_wdt_dev *lpc18xx_wdt = watchdog_get_drvdata(wdt_dev); | 159 | struct lpc18xx_wdt_dev *lpc18xx_wdt = watchdog_get_drvdata(wdt_dev); |
159 | unsigned long flags; | 160 | unsigned long flags; |
diff --git a/drivers/watchdog/meson_wdt.c b/drivers/watchdog/meson_wdt.c index aea5d2f44ad7..56ea1caf71c3 100644 --- a/drivers/watchdog/meson_wdt.c +++ b/drivers/watchdog/meson_wdt.c | |||
@@ -62,7 +62,8 @@ struct meson_wdt_dev { | |||
62 | const struct meson_wdt_data *data; | 62 | const struct meson_wdt_data *data; |
63 | }; | 63 | }; |
64 | 64 | ||
65 | static int meson_wdt_restart(struct watchdog_device *wdt_dev) | 65 | static int meson_wdt_restart(struct watchdog_device *wdt_dev, |
66 | unsigned long action, void *data) | ||
66 | { | 67 | { |
67 | struct meson_wdt_dev *meson_wdt = watchdog_get_drvdata(wdt_dev); | 68 | struct meson_wdt_dev *meson_wdt = watchdog_get_drvdata(wdt_dev); |
68 | u32 tc_reboot = MESON_WDT_DC_RESET; | 69 | u32 tc_reboot = MESON_WDT_DC_RESET; |
diff --git a/drivers/watchdog/moxart_wdt.c b/drivers/watchdog/moxart_wdt.c index 885c81bc4210..2c4a73d1e214 100644 --- a/drivers/watchdog/moxart_wdt.c +++ b/drivers/watchdog/moxart_wdt.c | |||
@@ -31,7 +31,8 @@ struct moxart_wdt_dev { | |||
31 | 31 | ||
32 | static int heartbeat; | 32 | static int heartbeat; |
33 | 33 | ||
34 | static int moxart_wdt_restart(struct watchdog_device *wdt_dev) | 34 | static int moxart_wdt_restart(struct watchdog_device *wdt_dev, |
35 | unsigned long action, void *data) | ||
35 | { | 36 | { |
36 | struct moxart_wdt_dev *moxart_wdt = watchdog_get_drvdata(wdt_dev); | 37 | struct moxart_wdt_dev *moxart_wdt = watchdog_get_drvdata(wdt_dev); |
37 | 38 | ||
diff --git a/drivers/watchdog/mtk_wdt.c b/drivers/watchdog/mtk_wdt.c index b78776c05554..7ed417a765c7 100644 --- a/drivers/watchdog/mtk_wdt.c +++ b/drivers/watchdog/mtk_wdt.c | |||
@@ -64,7 +64,8 @@ struct mtk_wdt_dev { | |||
64 | void __iomem *wdt_base; | 64 | void __iomem *wdt_base; |
65 | }; | 65 | }; |
66 | 66 | ||
67 | static int mtk_wdt_restart(struct watchdog_device *wdt_dev) | 67 | static int mtk_wdt_restart(struct watchdog_device *wdt_dev, |
68 | unsigned long action, void *data) | ||
68 | { | 69 | { |
69 | struct mtk_wdt_dev *mtk_wdt = watchdog_get_drvdata(wdt_dev); | 70 | struct mtk_wdt_dev *mtk_wdt = watchdog_get_drvdata(wdt_dev); |
70 | void __iomem *wdt_base; | 71 | void __iomem *wdt_base; |
diff --git a/drivers/watchdog/ni903x_wdt.c b/drivers/watchdog/ni903x_wdt.c new file mode 100644 index 000000000000..dc67742e9018 --- /dev/null +++ b/drivers/watchdog/ni903x_wdt.c | |||
@@ -0,0 +1,270 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2016 National Instruments Corp. | ||
3 | * | ||
4 | * This program is free software; you can redistribute it and/or modify | ||
5 | * it under the terms of the GNU General Public License as published by | ||
6 | * the Free Software Foundation; either version 2 of the License, or | ||
7 | * (at your option) any later version. | ||
8 | * | ||
9 | * This program is distributed in the hope that it will be useful, | ||
10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
12 | * GNU General Public License for more details. | ||
13 | */ | ||
14 | |||
15 | #include <linux/acpi.h> | ||
16 | #include <linux/device.h> | ||
17 | #include <linux/interrupt.h> | ||
18 | #include <linux/io.h> | ||
19 | #include <linux/module.h> | ||
20 | #include <linux/watchdog.h> | ||
21 | |||
22 | #define NIWD_CONTROL 0x01 | ||
23 | #define NIWD_COUNTER2 0x02 | ||
24 | #define NIWD_COUNTER1 0x03 | ||
25 | #define NIWD_COUNTER0 0x04 | ||
26 | #define NIWD_SEED2 0x05 | ||
27 | #define NIWD_SEED1 0x06 | ||
28 | #define NIWD_SEED0 0x07 | ||
29 | |||
30 | #define NIWD_IO_SIZE 0x08 | ||
31 | |||
32 | #define NIWD_CONTROL_MODE 0x80 | ||
33 | #define NIWD_CONTROL_PROC_RESET 0x20 | ||
34 | #define NIWD_CONTROL_PET 0x10 | ||
35 | #define NIWD_CONTROL_RUNNING 0x08 | ||
36 | #define NIWD_CONTROL_CAPTURECOUNTER 0x04 | ||
37 | #define NIWD_CONTROL_RESET 0x02 | ||
38 | #define NIWD_CONTROL_ALARM 0x01 | ||
39 | |||
40 | #define NIWD_PERIOD_NS 30720 | ||
41 | #define NIWD_MIN_TIMEOUT 1 | ||
42 | #define NIWD_MAX_TIMEOUT 515 | ||
43 | #define NIWD_DEFAULT_TIMEOUT 60 | ||
44 | |||
45 | #define NIWD_NAME "ni903x_wdt" | ||
46 | |||
47 | struct ni903x_wdt { | ||
48 | struct device *dev; | ||
49 | u16 io_base; | ||
50 | struct watchdog_device wdd; | ||
51 | }; | ||
52 | |||
53 | static unsigned int timeout; | ||
54 | module_param(timeout, uint, 0); | ||
55 | MODULE_PARM_DESC(timeout, | ||
56 | "Watchdog timeout in seconds. (default=" | ||
57 | __MODULE_STRING(NIWD_DEFAULT_TIMEOUT) ")"); | ||
58 | |||
59 | static int nowayout = WATCHDOG_NOWAYOUT; | ||
60 | module_param(nowayout, int, S_IRUGO); | ||
61 | MODULE_PARM_DESC(nowayout, | ||
62 | "Watchdog cannot be stopped once started (default=" | ||
63 | __MODULE_STRING(WATCHDOG_NOWAYOUT) ")"); | ||
64 | |||
65 | static void ni903x_start(struct ni903x_wdt *wdt) | ||
66 | { | ||
67 | u8 control = inb(wdt->io_base + NIWD_CONTROL); | ||
68 | |||
69 | outb(control | NIWD_CONTROL_RESET, wdt->io_base + NIWD_CONTROL); | ||
70 | outb(control | NIWD_CONTROL_PET, wdt->io_base + NIWD_CONTROL); | ||
71 | } | ||
72 | |||
73 | static int ni903x_wdd_set_timeout(struct watchdog_device *wdd, | ||
74 | unsigned int timeout) | ||
75 | { | ||
76 | struct ni903x_wdt *wdt = watchdog_get_drvdata(wdd); | ||
77 | u32 counter = timeout * (1000000000 / NIWD_PERIOD_NS); | ||
78 | |||
79 | outb(((0x00FF0000 & counter) >> 16), wdt->io_base + NIWD_SEED2); | ||
80 | outb(((0x0000FF00 & counter) >> 8), wdt->io_base + NIWD_SEED1); | ||
81 | outb((0x000000FF & counter), wdt->io_base + NIWD_SEED0); | ||
82 | |||
83 | wdd->timeout = timeout; | ||
84 | |||
85 | return 0; | ||
86 | } | ||
87 | |||
88 | static unsigned int ni903x_wdd_get_timeleft(struct watchdog_device *wdd) | ||
89 | { | ||
90 | struct ni903x_wdt *wdt = watchdog_get_drvdata(wdd); | ||
91 | u8 control, counter0, counter1, counter2; | ||
92 | u32 counter; | ||
93 | |||
94 | control = inb(wdt->io_base + NIWD_CONTROL); | ||
95 | control |= NIWD_CONTROL_CAPTURECOUNTER; | ||
96 | outb(control, wdt->io_base + NIWD_CONTROL); | ||
97 | |||
98 | counter2 = inb(wdt->io_base + NIWD_COUNTER2); | ||
99 | counter1 = inb(wdt->io_base + NIWD_COUNTER1); | ||
100 | counter0 = inb(wdt->io_base + NIWD_COUNTER0); | ||
101 | |||
102 | counter = (counter2 << 16) | (counter1 << 8) | counter0; | ||
103 | |||
104 | return counter / (1000000000 / NIWD_PERIOD_NS); | ||
105 | } | ||
106 | |||
107 | static int ni903x_wdd_ping(struct watchdog_device *wdd) | ||
108 | { | ||
109 | struct ni903x_wdt *wdt = watchdog_get_drvdata(wdd); | ||
110 | u8 control; | ||
111 | |||
112 | control = inb(wdt->io_base + NIWD_CONTROL); | ||
113 | outb(control | NIWD_CONTROL_PET, wdt->io_base + NIWD_CONTROL); | ||
114 | |||
115 | return 0; | ||
116 | } | ||
117 | |||
118 | static int ni903x_wdd_start(struct watchdog_device *wdd) | ||
119 | { | ||
120 | struct ni903x_wdt *wdt = watchdog_get_drvdata(wdd); | ||
121 | |||
122 | outb(NIWD_CONTROL_RESET | NIWD_CONTROL_PROC_RESET, | ||
123 | wdt->io_base + NIWD_CONTROL); | ||
124 | |||
125 | ni903x_wdd_set_timeout(wdd, wdd->timeout); | ||
126 | ni903x_start(wdt); | ||
127 | |||
128 | return 0; | ||
129 | } | ||
130 | |||
131 | static int ni903x_wdd_stop(struct watchdog_device *wdd) | ||
132 | { | ||
133 | struct ni903x_wdt *wdt = watchdog_get_drvdata(wdd); | ||
134 | |||
135 | outb(NIWD_CONTROL_RESET, wdt->io_base + NIWD_CONTROL); | ||
136 | |||
137 | return 0; | ||
138 | } | ||
139 | |||
140 | static acpi_status ni903x_resources(struct acpi_resource *res, void *data) | ||
141 | { | ||
142 | struct ni903x_wdt *wdt = data; | ||
143 | u16 io_size; | ||
144 | |||
145 | switch (res->type) { | ||
146 | case ACPI_RESOURCE_TYPE_IO: | ||
147 | if (wdt->io_base != 0) { | ||
148 | dev_err(wdt->dev, "too many IO resources\n"); | ||
149 | return AE_ERROR; | ||
150 | } | ||
151 | |||
152 | wdt->io_base = res->data.io.minimum; | ||
153 | io_size = res->data.io.address_length; | ||
154 | |||
155 | if (io_size < NIWD_IO_SIZE) { | ||
156 | dev_err(wdt->dev, "memory region too small\n"); | ||
157 | return AE_ERROR; | ||
158 | } | ||
159 | |||
160 | if (!devm_request_region(wdt->dev, wdt->io_base, io_size, | ||
161 | NIWD_NAME)) { | ||
162 | dev_err(wdt->dev, "failed to get memory region\n"); | ||
163 | return AE_ERROR; | ||
164 | } | ||
165 | |||
166 | return AE_OK; | ||
167 | |||
168 | case ACPI_RESOURCE_TYPE_END_TAG: | ||
169 | default: | ||
170 | /* Ignore unsupported resources, e.g. IRQ */ | ||
171 | return AE_OK; | ||
172 | } | ||
173 | } | ||
174 | |||
175 | static const struct watchdog_info ni903x_wdd_info = { | ||
176 | .options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING | WDIOF_MAGICCLOSE, | ||
177 | .identity = "NI Watchdog", | ||
178 | }; | ||
179 | |||
180 | static const struct watchdog_ops ni903x_wdd_ops = { | ||
181 | .owner = THIS_MODULE, | ||
182 | .start = ni903x_wdd_start, | ||
183 | .stop = ni903x_wdd_stop, | ||
184 | .ping = ni903x_wdd_ping, | ||
185 | .set_timeout = ni903x_wdd_set_timeout, | ||
186 | .get_timeleft = ni903x_wdd_get_timeleft, | ||
187 | }; | ||
188 | |||
189 | static int ni903x_acpi_add(struct acpi_device *device) | ||
190 | { | ||
191 | struct device *dev = &device->dev; | ||
192 | struct watchdog_device *wdd; | ||
193 | struct ni903x_wdt *wdt; | ||
194 | acpi_status status; | ||
195 | int ret; | ||
196 | |||
197 | wdt = devm_kzalloc(dev, sizeof(*wdt), GFP_KERNEL); | ||
198 | if (!wdt) | ||
199 | return -ENOMEM; | ||
200 | |||
201 | device->driver_data = wdt; | ||
202 | wdt->dev = dev; | ||
203 | |||
204 | status = acpi_walk_resources(device->handle, METHOD_NAME__CRS, | ||
205 | ni903x_resources, wdt); | ||
206 | if (ACPI_FAILURE(status) || wdt->io_base == 0) { | ||
207 | dev_err(dev, "failed to get resources\n"); | ||
208 | return -ENODEV; | ||
209 | } | ||
210 | |||
211 | wdd = &wdt->wdd; | ||
212 | wdd->info = &ni903x_wdd_info; | ||
213 | wdd->ops = &ni903x_wdd_ops; | ||
214 | wdd->min_timeout = NIWD_MIN_TIMEOUT; | ||
215 | wdd->max_timeout = NIWD_MAX_TIMEOUT; | ||
216 | wdd->timeout = NIWD_DEFAULT_TIMEOUT; | ||
217 | wdd->parent = dev; | ||
218 | watchdog_set_drvdata(wdd, wdt); | ||
219 | watchdog_set_nowayout(wdd, nowayout); | ||
220 | ret = watchdog_init_timeout(wdd, timeout, dev); | ||
221 | if (ret) | ||
222 | dev_err(dev, "unable to set timeout value, using default\n"); | ||
223 | |||
224 | ret = watchdog_register_device(wdd); | ||
225 | if (ret) { | ||
226 | dev_err(dev, "failed to register watchdog\n"); | ||
227 | return ret; | ||
228 | } | ||
229 | |||
230 | /* Switch from boot mode to user mode */ | ||
231 | outb(NIWD_CONTROL_RESET | NIWD_CONTROL_MODE, | ||
232 | wdt->io_base + NIWD_CONTROL); | ||
233 | |||
234 | dev_dbg(dev, "io_base=0x%04X, timeout=%d, nowayout=%d\n", | ||
235 | wdt->io_base, timeout, nowayout); | ||
236 | |||
237 | return 0; | ||
238 | } | ||
239 | |||
240 | static int ni903x_acpi_remove(struct acpi_device *device) | ||
241 | { | ||
242 | struct ni903x_wdt *wdt = acpi_driver_data(device); | ||
243 | |||
244 | ni903x_wdd_stop(&wdt->wdd); | ||
245 | watchdog_unregister_device(&wdt->wdd); | ||
246 | |||
247 | return 0; | ||
248 | } | ||
249 | |||
250 | static const struct acpi_device_id ni903x_device_ids[] = { | ||
251 | {"NIC775C", 0}, | ||
252 | {"", 0}, | ||
253 | }; | ||
254 | MODULE_DEVICE_TABLE(acpi, ni903x_device_ids); | ||
255 | |||
256 | static struct acpi_driver ni903x_acpi_driver = { | ||
257 | .name = NIWD_NAME, | ||
258 | .ids = ni903x_device_ids, | ||
259 | .ops = { | ||
260 | .add = ni903x_acpi_add, | ||
261 | .remove = ni903x_acpi_remove, | ||
262 | }, | ||
263 | }; | ||
264 | |||
265 | module_acpi_driver(ni903x_acpi_driver); | ||
266 | |||
267 | MODULE_DESCRIPTION("NI 903x Watchdog"); | ||
268 | MODULE_AUTHOR("Jeff Westfahl <jeff.westfahl@ni.com>"); | ||
269 | MODULE_AUTHOR("Kyle Roeschley <kyle.roeschley@ni.com>"); | ||
270 | MODULE_LICENSE("GPL"); | ||
diff --git a/drivers/watchdog/pnx4008_wdt.c b/drivers/watchdog/pnx4008_wdt.c index 313cd1c6fda0..0529aed158a4 100644 --- a/drivers/watchdog/pnx4008_wdt.c +++ b/drivers/watchdog/pnx4008_wdt.c | |||
@@ -31,6 +31,8 @@ | |||
31 | #include <linux/slab.h> | 31 | #include <linux/slab.h> |
32 | #include <linux/err.h> | 32 | #include <linux/err.h> |
33 | #include <linux/of.h> | 33 | #include <linux/of.h> |
34 | #include <linux/delay.h> | ||
35 | #include <linux/reboot.h> | ||
34 | #include <mach/hardware.h> | 36 | #include <mach/hardware.h> |
35 | 37 | ||
36 | /* WatchDog Timer - Chapter 23 Page 207 */ | 38 | /* WatchDog Timer - Chapter 23 Page 207 */ |
@@ -124,6 +126,41 @@ static int pnx4008_wdt_set_timeout(struct watchdog_device *wdd, | |||
124 | return 0; | 126 | return 0; |
125 | } | 127 | } |
126 | 128 | ||
129 | static int pnx4008_restart_handler(struct watchdog_device *wdd, | ||
130 | unsigned long mode, void *cmd) | ||
131 | { | ||
132 | const char *boot_cmd = cmd; | ||
133 | |||
134 | /* | ||
135 | * Verify if a "cmd" passed from the userspace program rebooting | ||
136 | * the system; if available, handle it. | ||
137 | * - For details, see the 'reboot' syscall in kernel/reboot.c | ||
138 | * - If the received "cmd" is not supported, use the default mode. | ||
139 | */ | ||
140 | if (boot_cmd) { | ||
141 | if (boot_cmd[0] == 'h') | ||
142 | mode = REBOOT_HARD; | ||
143 | else if (boot_cmd[0] == 's') | ||
144 | mode = REBOOT_SOFT; | ||
145 | } | ||
146 | |||
147 | if (mode == REBOOT_SOFT) { | ||
148 | /* Force match output active */ | ||
149 | writel(EXT_MATCH0, WDTIM_EMR(wdt_base)); | ||
150 | /* Internal reset on match output (RESOUT_N not asserted) */ | ||
151 | writel(M_RES1, WDTIM_MCTRL(wdt_base)); | ||
152 | } else { | ||
153 | /* Instant assert of RESETOUT_N with pulse length 1mS */ | ||
154 | writel(13000, WDTIM_PULSE(wdt_base)); | ||
155 | writel(M_RES2 | RESFRC1 | RESFRC2, WDTIM_MCTRL(wdt_base)); | ||
156 | } | ||
157 | |||
158 | /* Wait for watchdog to reset system */ | ||
159 | mdelay(1000); | ||
160 | |||
161 | return NOTIFY_DONE; | ||
162 | } | ||
163 | |||
127 | static const struct watchdog_info pnx4008_wdt_ident = { | 164 | static const struct watchdog_info pnx4008_wdt_ident = { |
128 | .options = WDIOF_CARDRESET | WDIOF_MAGICCLOSE | | 165 | .options = WDIOF_CARDRESET | WDIOF_MAGICCLOSE | |
129 | WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING, | 166 | WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING, |
@@ -135,6 +172,7 @@ static const struct watchdog_ops pnx4008_wdt_ops = { | |||
135 | .start = pnx4008_wdt_start, | 172 | .start = pnx4008_wdt_start, |
136 | .stop = pnx4008_wdt_stop, | 173 | .stop = pnx4008_wdt_stop, |
137 | .set_timeout = pnx4008_wdt_set_timeout, | 174 | .set_timeout = pnx4008_wdt_set_timeout, |
175 | .restart = pnx4008_restart_handler, | ||
138 | }; | 176 | }; |
139 | 177 | ||
140 | static struct watchdog_device pnx4008_wdd = { | 178 | static struct watchdog_device pnx4008_wdd = { |
@@ -169,6 +207,7 @@ static int pnx4008_wdt_probe(struct platform_device *pdev) | |||
169 | WDIOF_CARDRESET : 0; | 207 | WDIOF_CARDRESET : 0; |
170 | pnx4008_wdd.parent = &pdev->dev; | 208 | pnx4008_wdd.parent = &pdev->dev; |
171 | watchdog_set_nowayout(&pnx4008_wdd, nowayout); | 209 | watchdog_set_nowayout(&pnx4008_wdd, nowayout); |
210 | watchdog_set_restart_priority(&pnx4008_wdd, 128); | ||
172 | 211 | ||
173 | pnx4008_wdt_stop(&pnx4008_wdd); /* disable for now */ | 212 | pnx4008_wdt_stop(&pnx4008_wdd); /* disable for now */ |
174 | 213 | ||
@@ -178,8 +217,7 @@ static int pnx4008_wdt_probe(struct platform_device *pdev) | |||
178 | goto disable_clk; | 217 | goto disable_clk; |
179 | } | 218 | } |
180 | 219 | ||
181 | dev_info(&pdev->dev, "PNX4008 Watchdog Timer: heartbeat %d sec\n", | 220 | dev_info(&pdev->dev, "heartbeat %d sec\n", pnx4008_wdd.timeout); |
182 | pnx4008_wdd.timeout); | ||
183 | 221 | ||
184 | return 0; | 222 | return 0; |
185 | 223 | ||
diff --git a/drivers/watchdog/qcom-wdt.c b/drivers/watchdog/qcom-wdt.c index 424f9a952fee..20563ccb7be0 100644 --- a/drivers/watchdog/qcom-wdt.c +++ b/drivers/watchdog/qcom-wdt.c | |||
@@ -70,7 +70,8 @@ static int qcom_wdt_set_timeout(struct watchdog_device *wdd, | |||
70 | return qcom_wdt_start(wdd); | 70 | return qcom_wdt_start(wdd); |
71 | } | 71 | } |
72 | 72 | ||
73 | static int qcom_wdt_restart(struct watchdog_device *wdd) | 73 | static int qcom_wdt_restart(struct watchdog_device *wdd, unsigned long action, |
74 | void *data) | ||
74 | { | 75 | { |
75 | struct qcom_wdt *wdt = to_qcom_wdt(wdd); | 76 | struct qcom_wdt *wdt = to_qcom_wdt(wdd); |
76 | u32 timeout; | 77 | u32 timeout; |
diff --git a/drivers/watchdog/rc32434_wdt.c b/drivers/watchdog/rc32434_wdt.c index 71e78ef4b736..3a75f3b53452 100644 --- a/drivers/watchdog/rc32434_wdt.c +++ b/drivers/watchdog/rc32434_wdt.c | |||
@@ -237,7 +237,7 @@ static long rc32434_wdt_ioctl(struct file *file, unsigned int cmd, | |||
237 | return -EINVAL; | 237 | return -EINVAL; |
238 | /* Fall through */ | 238 | /* Fall through */ |
239 | case WDIOC_GETTIMEOUT: | 239 | case WDIOC_GETTIMEOUT: |
240 | return copy_to_user(argp, &timeout, sizeof(int)); | 240 | return copy_to_user(argp, &timeout, sizeof(int)) ? -EFAULT : 0; |
241 | default: | 241 | default: |
242 | return -ENOTTY; | 242 | return -ENOTTY; |
243 | } | 243 | } |
diff --git a/drivers/watchdog/s3c2410_wdt.c b/drivers/watchdog/s3c2410_wdt.c index 0093450441fe..59e95762a6de 100644 --- a/drivers/watchdog/s3c2410_wdt.c +++ b/drivers/watchdog/s3c2410_wdt.c | |||
@@ -47,6 +47,8 @@ | |||
47 | #define S3C2410_WTDAT 0x04 | 47 | #define S3C2410_WTDAT 0x04 |
48 | #define S3C2410_WTCNT 0x08 | 48 | #define S3C2410_WTCNT 0x08 |
49 | 49 | ||
50 | #define S3C2410_WTCNT_MAXCNT 0xffff | ||
51 | |||
50 | #define S3C2410_WTCON_RSTEN (1 << 0) | 52 | #define S3C2410_WTCON_RSTEN (1 << 0) |
51 | #define S3C2410_WTCON_INTEN (1 << 2) | 53 | #define S3C2410_WTCON_INTEN (1 << 2) |
52 | #define S3C2410_WTCON_ENABLE (1 << 5) | 54 | #define S3C2410_WTCON_ENABLE (1 << 5) |
@@ -56,8 +58,11 @@ | |||
56 | #define S3C2410_WTCON_DIV64 (2 << 3) | 58 | #define S3C2410_WTCON_DIV64 (2 << 3) |
57 | #define S3C2410_WTCON_DIV128 (3 << 3) | 59 | #define S3C2410_WTCON_DIV128 (3 << 3) |
58 | 60 | ||
61 | #define S3C2410_WTCON_MAXDIV 0x80 | ||
62 | |||
59 | #define S3C2410_WTCON_PRESCALE(x) ((x) << 8) | 63 | #define S3C2410_WTCON_PRESCALE(x) ((x) << 8) |
60 | #define S3C2410_WTCON_PRESCALE_MASK (0xff << 8) | 64 | #define S3C2410_WTCON_PRESCALE_MASK (0xff << 8) |
65 | #define S3C2410_WTCON_PRESCALE_MAX 0xff | ||
61 | 66 | ||
62 | #define CONFIG_S3C2410_WATCHDOG_ATBOOT (0) | 67 | #define CONFIG_S3C2410_WATCHDOG_ATBOOT (0) |
63 | #define CONFIG_S3C2410_WATCHDOG_DEFAULT_TIME (15) | 68 | #define CONFIG_S3C2410_WATCHDOG_DEFAULT_TIME (15) |
@@ -198,6 +203,14 @@ do { \ | |||
198 | 203 | ||
199 | /* functions */ | 204 | /* functions */ |
200 | 205 | ||
206 | static inline unsigned int s3c2410wdt_max_timeout(struct clk *clock) | ||
207 | { | ||
208 | unsigned long freq = clk_get_rate(clock); | ||
209 | |||
210 | return S3C2410_WTCNT_MAXCNT / (freq / (S3C2410_WTCON_PRESCALE_MAX + 1) | ||
211 | / S3C2410_WTCON_MAXDIV); | ||
212 | } | ||
213 | |||
201 | static inline struct s3c2410_wdt *freq_to_wdt(struct notifier_block *nb) | 214 | static inline struct s3c2410_wdt *freq_to_wdt(struct notifier_block *nb) |
202 | { | 215 | { |
203 | return container_of(nb, struct s3c2410_wdt, freq_transition); | 216 | return container_of(nb, struct s3c2410_wdt, freq_transition); |
@@ -349,7 +362,8 @@ static int s3c2410wdt_set_heartbeat(struct watchdog_device *wdd, unsigned timeou | |||
349 | return 0; | 362 | return 0; |
350 | } | 363 | } |
351 | 364 | ||
352 | static int s3c2410wdt_restart(struct watchdog_device *wdd) | 365 | static int s3c2410wdt_restart(struct watchdog_device *wdd, unsigned long action, |
366 | void *data) | ||
353 | { | 367 | { |
354 | struct s3c2410_wdt *wdt = watchdog_get_drvdata(wdd); | 368 | struct s3c2410_wdt *wdt = watchdog_get_drvdata(wdd); |
355 | void __iomem *wdt_base = wdt->reg_base; | 369 | void __iomem *wdt_base = wdt->reg_base; |
@@ -567,6 +581,9 @@ static int s3c2410wdt_probe(struct platform_device *pdev) | |||
567 | return ret; | 581 | return ret; |
568 | } | 582 | } |
569 | 583 | ||
584 | wdt->wdt_device.min_timeout = 1; | ||
585 | wdt->wdt_device.max_timeout = s3c2410wdt_max_timeout(wdt->clock); | ||
586 | |||
570 | ret = s3c2410wdt_cpufreq_register(wdt); | 587 | ret = s3c2410wdt_cpufreq_register(wdt); |
571 | if (ret < 0) { | 588 | if (ret < 0) { |
572 | dev_err(dev, "failed to register cpufreq\n"); | 589 | dev_err(dev, "failed to register cpufreq\n"); |
diff --git a/drivers/watchdog/sbsa_gwdt.c b/drivers/watchdog/sbsa_gwdt.c new file mode 100644 index 000000000000..ad383f6f15fc --- /dev/null +++ b/drivers/watchdog/sbsa_gwdt.c | |||
@@ -0,0 +1,408 @@ | |||
1 | /* | ||
2 | * SBSA(Server Base System Architecture) Generic Watchdog driver | ||
3 | * | ||
4 | * Copyright (c) 2015, Linaro Ltd. | ||
5 | * Author: Fu Wei <fu.wei@linaro.org> | ||
6 | * Suravee Suthikulpanit <Suravee.Suthikulpanit@amd.com> | ||
7 | * Al Stone <al.stone@linaro.org> | ||
8 | * Timur Tabi <timur@codeaurora.org> | ||
9 | * | ||
10 | * This program is free software; you can redistribute it and/or modify | ||
11 | * it under the terms of the GNU General Public License 2 as published | ||
12 | * by the Free Software Foundation. | ||
13 | * | ||
14 | * This program is distributed in the hope that it will be useful, | ||
15 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
16 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
17 | * GNU General Public License for more details. | ||
18 | * | ||
19 | * ARM SBSA Generic Watchdog has two stage timeouts: | ||
20 | * the first signal (WS0) is for alerting the system by interrupt, | ||
21 | * the second one (WS1) is a real hardware reset. | ||
22 | * More details about the hardware specification of this device: | ||
23 | * ARM DEN0029B - Server Base System Architecture (SBSA) | ||
24 | * | ||
25 | * This driver can operate ARM SBSA Generic Watchdog as a single stage watchdog | ||
26 | * or a two stages watchdog, it's set up by the module parameter "action". | ||
27 | * In the single stage mode, when the timeout is reached, your system | ||
28 | * will be reset by WS1. The first signal (WS0) is ignored. | ||
29 | * In the two stages mode, when the timeout is reached, the first signal (WS0) | ||
30 | * will trigger panic. If the system is getting into trouble and cannot be reset | ||
31 | * by panic or restart properly by the kdump kernel(if supported), then the | ||
32 | * second stage (as long as the first stage) will be reached, system will be | ||
33 | * reset by WS1. This function can help administrator to backup the system | ||
34 | * context info by panic console output or kdump. | ||
35 | * | ||
36 | * SBSA GWDT: | ||
37 | * if action is 1 (the two stages mode): | ||
38 | * |--------WOR-------WS0--------WOR-------WS1 | ||
39 | * |----timeout-----(panic)----timeout-----reset | ||
40 | * | ||
41 | * if action is 0 (the single stage mode): | ||
42 | * |------WOR-----WS0(ignored)-----WOR------WS1 | ||
43 | * |--------------timeout-------------------reset | ||
44 | * | ||
45 | * Note: Since this watchdog timer has two stages, and each stage is determined | ||
46 | * by WOR, in the single stage mode, the timeout is (WOR * 2); in the two | ||
47 | * stages mode, the timeout is WOR. The maximum timeout in the two stages mode | ||
48 | * is half of that in the single stage mode. | ||
49 | * | ||
50 | */ | ||
51 | |||
52 | #include <linux/io.h> | ||
53 | #include <linux/interrupt.h> | ||
54 | #include <linux/module.h> | ||
55 | #include <linux/moduleparam.h> | ||
56 | #include <linux/of.h> | ||
57 | #include <linux/of_device.h> | ||
58 | #include <linux/platform_device.h> | ||
59 | #include <linux/uaccess.h> | ||
60 | #include <linux/watchdog.h> | ||
61 | #include <asm/arch_timer.h> | ||
62 | |||
63 | #define DRV_NAME "sbsa-gwdt" | ||
64 | #define WATCHDOG_NAME "SBSA Generic Watchdog" | ||
65 | |||
66 | /* SBSA Generic Watchdog register definitions */ | ||
67 | /* refresh frame */ | ||
68 | #define SBSA_GWDT_WRR 0x000 | ||
69 | |||
70 | /* control frame */ | ||
71 | #define SBSA_GWDT_WCS 0x000 | ||
72 | #define SBSA_GWDT_WOR 0x008 | ||
73 | #define SBSA_GWDT_WCV 0x010 | ||
74 | |||
75 | /* refresh/control frame */ | ||
76 | #define SBSA_GWDT_W_IIDR 0xfcc | ||
77 | #define SBSA_GWDT_IDR 0xfd0 | ||
78 | |||
79 | /* Watchdog Control and Status Register */ | ||
80 | #define SBSA_GWDT_WCS_EN BIT(0) | ||
81 | #define SBSA_GWDT_WCS_WS0 BIT(1) | ||
82 | #define SBSA_GWDT_WCS_WS1 BIT(2) | ||
83 | |||
84 | /** | ||
85 | * struct sbsa_gwdt - Internal representation of the SBSA GWDT | ||
86 | * @wdd: kernel watchdog_device structure | ||
87 | * @clk: store the System Counter clock frequency, in Hz. | ||
88 | * @refresh_base: Virtual address of the watchdog refresh frame | ||
89 | * @control_base: Virtual address of the watchdog control frame | ||
90 | */ | ||
91 | struct sbsa_gwdt { | ||
92 | struct watchdog_device wdd; | ||
93 | u32 clk; | ||
94 | void __iomem *refresh_base; | ||
95 | void __iomem *control_base; | ||
96 | }; | ||
97 | |||
98 | #define DEFAULT_TIMEOUT 10 /* seconds */ | ||
99 | |||
100 | static unsigned int timeout; | ||
101 | module_param(timeout, uint, 0); | ||
102 | MODULE_PARM_DESC(timeout, | ||
103 | "Watchdog timeout in seconds. (>=0, default=" | ||
104 | __MODULE_STRING(DEFAULT_TIMEOUT) ")"); | ||
105 | |||
106 | /* | ||
107 | * action refers to action taken when watchdog gets WS0 | ||
108 | * 0 = skip | ||
109 | * 1 = panic | ||
110 | * defaults to skip (0) | ||
111 | */ | ||
112 | static int action; | ||
113 | module_param(action, int, 0); | ||
114 | MODULE_PARM_DESC(action, "after watchdog gets WS0 interrupt, do: " | ||
115 | "0 = skip(*) 1 = panic"); | ||
116 | |||
117 | static bool nowayout = WATCHDOG_NOWAYOUT; | ||
118 | module_param(nowayout, bool, S_IRUGO); | ||
119 | MODULE_PARM_DESC(nowayout, | ||
120 | "Watchdog cannot be stopped once started (default=" | ||
121 | __MODULE_STRING(WATCHDOG_NOWAYOUT) ")"); | ||
122 | |||
123 | /* | ||
124 | * watchdog operation functions | ||
125 | */ | ||
126 | static int sbsa_gwdt_set_timeout(struct watchdog_device *wdd, | ||
127 | unsigned int timeout) | ||
128 | { | ||
129 | struct sbsa_gwdt *gwdt = watchdog_get_drvdata(wdd); | ||
130 | |||
131 | wdd->timeout = timeout; | ||
132 | |||
133 | if (action) | ||
134 | writel(gwdt->clk * timeout, | ||
135 | gwdt->control_base + SBSA_GWDT_WOR); | ||
136 | else | ||
137 | /* | ||
138 | * In the single stage mode, The first signal (WS0) is ignored, | ||
139 | * the timeout is (WOR * 2), so the WOR should be configured | ||
140 | * to half value of timeout. | ||
141 | */ | ||
142 | writel(gwdt->clk / 2 * timeout, | ||
143 | gwdt->control_base + SBSA_GWDT_WOR); | ||
144 | |||
145 | return 0; | ||
146 | } | ||
147 | |||
148 | static unsigned int sbsa_gwdt_get_timeleft(struct watchdog_device *wdd) | ||
149 | { | ||
150 | struct sbsa_gwdt *gwdt = watchdog_get_drvdata(wdd); | ||
151 | u64 timeleft = 0; | ||
152 | |||
153 | /* | ||
154 | * In the single stage mode, if WS0 is deasserted | ||
155 | * (watchdog is in the first stage), | ||
156 | * timeleft = WOR + (WCV - system counter) | ||
157 | */ | ||
158 | if (!action && | ||
159 | !(readl(gwdt->control_base + SBSA_GWDT_WCS) & SBSA_GWDT_WCS_WS0)) | ||
160 | timeleft += readl(gwdt->control_base + SBSA_GWDT_WOR); | ||
161 | |||
162 | timeleft += readq(gwdt->control_base + SBSA_GWDT_WCV) - | ||
163 | arch_counter_get_cntvct(); | ||
164 | |||
165 | do_div(timeleft, gwdt->clk); | ||
166 | |||
167 | return timeleft; | ||
168 | } | ||
169 | |||
170 | static int sbsa_gwdt_keepalive(struct watchdog_device *wdd) | ||
171 | { | ||
172 | struct sbsa_gwdt *gwdt = watchdog_get_drvdata(wdd); | ||
173 | |||
174 | /* | ||
175 | * Writing WRR for an explicit watchdog refresh. | ||
176 | * You can write anyting (like 0). | ||
177 | */ | ||
178 | writel(0, gwdt->refresh_base + SBSA_GWDT_WRR); | ||
179 | |||
180 | return 0; | ||
181 | } | ||
182 | |||
183 | static unsigned int sbsa_gwdt_status(struct watchdog_device *wdd) | ||
184 | { | ||
185 | struct sbsa_gwdt *gwdt = watchdog_get_drvdata(wdd); | ||
186 | u32 status = readl(gwdt->control_base + SBSA_GWDT_WCS); | ||
187 | |||
188 | /* is the watchdog timer running? */ | ||
189 | return (status & SBSA_GWDT_WCS_EN) << WDOG_ACTIVE; | ||
190 | } | ||
191 | |||
192 | static int sbsa_gwdt_start(struct watchdog_device *wdd) | ||
193 | { | ||
194 | struct sbsa_gwdt *gwdt = watchdog_get_drvdata(wdd); | ||
195 | |||
196 | /* writing WCS will cause an explicit watchdog refresh */ | ||
197 | writel(SBSA_GWDT_WCS_EN, gwdt->control_base + SBSA_GWDT_WCS); | ||
198 | |||
199 | return 0; | ||
200 | } | ||
201 | |||
202 | static int sbsa_gwdt_stop(struct watchdog_device *wdd) | ||
203 | { | ||
204 | struct sbsa_gwdt *gwdt = watchdog_get_drvdata(wdd); | ||
205 | |||
206 | /* Simply write 0 to WCS to clean WCS_EN bit */ | ||
207 | writel(0, gwdt->control_base + SBSA_GWDT_WCS); | ||
208 | |||
209 | return 0; | ||
210 | } | ||
211 | |||
212 | static irqreturn_t sbsa_gwdt_interrupt(int irq, void *dev_id) | ||
213 | { | ||
214 | panic(WATCHDOG_NAME " timeout"); | ||
215 | |||
216 | return IRQ_HANDLED; | ||
217 | } | ||
218 | |||
219 | static struct watchdog_info sbsa_gwdt_info = { | ||
220 | .identity = WATCHDOG_NAME, | ||
221 | .options = WDIOF_SETTIMEOUT | | ||
222 | WDIOF_KEEPALIVEPING | | ||
223 | WDIOF_MAGICCLOSE | | ||
224 | WDIOF_CARDRESET, | ||
225 | }; | ||
226 | |||
227 | static struct watchdog_ops sbsa_gwdt_ops = { | ||
228 | .owner = THIS_MODULE, | ||
229 | .start = sbsa_gwdt_start, | ||
230 | .stop = sbsa_gwdt_stop, | ||
231 | .status = sbsa_gwdt_status, | ||
232 | .ping = sbsa_gwdt_keepalive, | ||
233 | .set_timeout = sbsa_gwdt_set_timeout, | ||
234 | .get_timeleft = sbsa_gwdt_get_timeleft, | ||
235 | }; | ||
236 | |||
237 | static int sbsa_gwdt_probe(struct platform_device *pdev) | ||
238 | { | ||
239 | void __iomem *rf_base, *cf_base; | ||
240 | struct device *dev = &pdev->dev; | ||
241 | struct watchdog_device *wdd; | ||
242 | struct sbsa_gwdt *gwdt; | ||
243 | struct resource *res; | ||
244 | int ret, irq; | ||
245 | u32 status; | ||
246 | |||
247 | gwdt = devm_kzalloc(dev, sizeof(*gwdt), GFP_KERNEL); | ||
248 | if (!gwdt) | ||
249 | return -ENOMEM; | ||
250 | platform_set_drvdata(pdev, gwdt); | ||
251 | |||
252 | res = platform_get_resource(pdev, IORESOURCE_MEM, 0); | ||
253 | cf_base = devm_ioremap_resource(dev, res); | ||
254 | if (IS_ERR(cf_base)) | ||
255 | return PTR_ERR(cf_base); | ||
256 | |||
257 | res = platform_get_resource(pdev, IORESOURCE_MEM, 1); | ||
258 | rf_base = devm_ioremap_resource(dev, res); | ||
259 | if (IS_ERR(rf_base)) | ||
260 | return PTR_ERR(rf_base); | ||
261 | |||
262 | /* | ||
263 | * Get the frequency of system counter from the cp15 interface of ARM | ||
264 | * Generic timer. We don't need to check it, because if it returns "0", | ||
265 | * system would panic in very early stage. | ||
266 | */ | ||
267 | gwdt->clk = arch_timer_get_cntfrq(); | ||
268 | gwdt->refresh_base = rf_base; | ||
269 | gwdt->control_base = cf_base; | ||
270 | |||
271 | wdd = &gwdt->wdd; | ||
272 | wdd->parent = dev; | ||
273 | wdd->info = &sbsa_gwdt_info; | ||
274 | wdd->ops = &sbsa_gwdt_ops; | ||
275 | wdd->min_timeout = 1; | ||
276 | wdd->max_timeout = U32_MAX / gwdt->clk; | ||
277 | wdd->timeout = DEFAULT_TIMEOUT; | ||
278 | watchdog_set_drvdata(wdd, gwdt); | ||
279 | watchdog_set_nowayout(wdd, nowayout); | ||
280 | |||
281 | status = readl(cf_base + SBSA_GWDT_WCS); | ||
282 | if (status & SBSA_GWDT_WCS_WS1) { | ||
283 | dev_warn(dev, "System reset by WDT.\n"); | ||
284 | wdd->bootstatus |= WDIOF_CARDRESET; | ||
285 | } | ||
286 | |||
287 | if (action) { | ||
288 | irq = platform_get_irq(pdev, 0); | ||
289 | if (irq < 0) { | ||
290 | action = 0; | ||
291 | dev_warn(dev, "unable to get ws0 interrupt.\n"); | ||
292 | } else { | ||
293 | /* | ||
294 | * In case there is a pending ws0 interrupt, just ping | ||
295 | * the watchdog before registering the interrupt routine | ||
296 | */ | ||
297 | writel(0, rf_base + SBSA_GWDT_WRR); | ||
298 | if (devm_request_irq(dev, irq, sbsa_gwdt_interrupt, 0, | ||
299 | pdev->name, gwdt)) { | ||
300 | action = 0; | ||
301 | dev_warn(dev, "unable to request IRQ %d.\n", | ||
302 | irq); | ||
303 | } | ||
304 | } | ||
305 | if (!action) | ||
306 | dev_warn(dev, "falling back to single stage mode.\n"); | ||
307 | } | ||
308 | /* | ||
309 | * In the single stage mode, The first signal (WS0) is ignored, | ||
310 | * the timeout is (WOR * 2), so the maximum timeout should be doubled. | ||
311 | */ | ||
312 | if (!action) | ||
313 | wdd->max_timeout *= 2; | ||
314 | |||
315 | watchdog_init_timeout(wdd, timeout, dev); | ||
316 | /* | ||
317 | * Update timeout to WOR. | ||
318 | * Because of the explicit watchdog refresh mechanism, | ||
319 | * it's also a ping, if watchdog is enabled. | ||
320 | */ | ||
321 | sbsa_gwdt_set_timeout(wdd, wdd->timeout); | ||
322 | |||
323 | ret = watchdog_register_device(wdd); | ||
324 | if (ret) | ||
325 | return ret; | ||
326 | |||
327 | dev_info(dev, "Initialized with %ds timeout @ %u Hz, action=%d.%s\n", | ||
328 | wdd->timeout, gwdt->clk, action, | ||
329 | status & SBSA_GWDT_WCS_EN ? " [enabled]" : ""); | ||
330 | |||
331 | return 0; | ||
332 | } | ||
333 | |||
334 | static void sbsa_gwdt_shutdown(struct platform_device *pdev) | ||
335 | { | ||
336 | struct sbsa_gwdt *gwdt = platform_get_drvdata(pdev); | ||
337 | |||
338 | sbsa_gwdt_stop(&gwdt->wdd); | ||
339 | } | ||
340 | |||
341 | static int sbsa_gwdt_remove(struct platform_device *pdev) | ||
342 | { | ||
343 | struct sbsa_gwdt *gwdt = platform_get_drvdata(pdev); | ||
344 | |||
345 | watchdog_unregister_device(&gwdt->wdd); | ||
346 | |||
347 | return 0; | ||
348 | } | ||
349 | |||
350 | /* Disable watchdog if it is active during suspend */ | ||
351 | static int __maybe_unused sbsa_gwdt_suspend(struct device *dev) | ||
352 | { | ||
353 | struct sbsa_gwdt *gwdt = dev_get_drvdata(dev); | ||
354 | |||
355 | if (watchdog_active(&gwdt->wdd)) | ||
356 | sbsa_gwdt_stop(&gwdt->wdd); | ||
357 | |||
358 | return 0; | ||
359 | } | ||
360 | |||
361 | /* Enable watchdog if necessary */ | ||
362 | static int __maybe_unused sbsa_gwdt_resume(struct device *dev) | ||
363 | { | ||
364 | struct sbsa_gwdt *gwdt = dev_get_drvdata(dev); | ||
365 | |||
366 | if (watchdog_active(&gwdt->wdd)) | ||
367 | sbsa_gwdt_start(&gwdt->wdd); | ||
368 | |||
369 | return 0; | ||
370 | } | ||
371 | |||
372 | static const struct dev_pm_ops sbsa_gwdt_pm_ops = { | ||
373 | SET_SYSTEM_SLEEP_PM_OPS(sbsa_gwdt_suspend, sbsa_gwdt_resume) | ||
374 | }; | ||
375 | |||
376 | static const struct of_device_id sbsa_gwdt_of_match[] = { | ||
377 | { .compatible = "arm,sbsa-gwdt", }, | ||
378 | {}, | ||
379 | }; | ||
380 | MODULE_DEVICE_TABLE(of, sbsa_gwdt_of_match); | ||
381 | |||
382 | static const struct platform_device_id sbsa_gwdt_pdev_match[] = { | ||
383 | { .name = DRV_NAME, }, | ||
384 | {}, | ||
385 | }; | ||
386 | MODULE_DEVICE_TABLE(platform, sbsa_gwdt_pdev_match); | ||
387 | |||
388 | static struct platform_driver sbsa_gwdt_driver = { | ||
389 | .driver = { | ||
390 | .name = DRV_NAME, | ||
391 | .pm = &sbsa_gwdt_pm_ops, | ||
392 | .of_match_table = sbsa_gwdt_of_match, | ||
393 | }, | ||
394 | .probe = sbsa_gwdt_probe, | ||
395 | .remove = sbsa_gwdt_remove, | ||
396 | .shutdown = sbsa_gwdt_shutdown, | ||
397 | .id_table = sbsa_gwdt_pdev_match, | ||
398 | }; | ||
399 | |||
400 | module_platform_driver(sbsa_gwdt_driver); | ||
401 | |||
402 | MODULE_DESCRIPTION("SBSA Generic Watchdog Driver"); | ||
403 | MODULE_AUTHOR("Fu Wei <fu.wei@linaro.org>"); | ||
404 | MODULE_AUTHOR("Suravee Suthikulpanit <Suravee.Suthikulpanit@amd.com>"); | ||
405 | MODULE_AUTHOR("Al Stone <al.stone@linaro.org>"); | ||
406 | MODULE_AUTHOR("Timur Tabi <timur@codeaurora.org>"); | ||
407 | MODULE_LICENSE("GPL v2"); | ||
408 | MODULE_ALIAS("platform:" DRV_NAME); | ||
diff --git a/drivers/watchdog/sunxi_wdt.c b/drivers/watchdog/sunxi_wdt.c index e027deb54740..953bb7b7446f 100644 --- a/drivers/watchdog/sunxi_wdt.c +++ b/drivers/watchdog/sunxi_wdt.c | |||
@@ -83,7 +83,8 @@ static const int wdt_timeout_map[] = { | |||
83 | }; | 83 | }; |
84 | 84 | ||
85 | 85 | ||
86 | static int sunxi_wdt_restart(struct watchdog_device *wdt_dev) | 86 | static int sunxi_wdt_restart(struct watchdog_device *wdt_dev, |
87 | unsigned long action, void *data) | ||
87 | { | 88 | { |
88 | struct sunxi_wdt_dev *sunxi_wdt = watchdog_get_drvdata(wdt_dev); | 89 | struct sunxi_wdt_dev *sunxi_wdt = watchdog_get_drvdata(wdt_dev); |
89 | void __iomem *wdt_base = sunxi_wdt->wdt_base; | 90 | void __iomem *wdt_base = sunxi_wdt->wdt_base; |
diff --git a/drivers/watchdog/tangox_wdt.c b/drivers/watchdog/tangox_wdt.c index 709c1ed6fd79..cfbed7e051b6 100644 --- a/drivers/watchdog/tangox_wdt.c +++ b/drivers/watchdog/tangox_wdt.c | |||
@@ -139,6 +139,10 @@ static int tangox_wdt_probe(struct platform_device *pdev) | |||
139 | return err; | 139 | return err; |
140 | 140 | ||
141 | dev->clk_rate = clk_get_rate(dev->clk); | 141 | dev->clk_rate = clk_get_rate(dev->clk); |
142 | if (!dev->clk_rate) { | ||
143 | err = -EINVAL; | ||
144 | goto err; | ||
145 | } | ||
142 | 146 | ||
143 | dev->wdt.parent = &pdev->dev; | 147 | dev->wdt.parent = &pdev->dev; |
144 | dev->wdt.info = &tangox_wdt_info; | 148 | dev->wdt.info = &tangox_wdt_info; |
@@ -171,10 +175,8 @@ static int tangox_wdt_probe(struct platform_device *pdev) | |||
171 | } | 175 | } |
172 | 176 | ||
173 | err = watchdog_register_device(&dev->wdt); | 177 | err = watchdog_register_device(&dev->wdt); |
174 | if (err) { | 178 | if (err) |
175 | clk_disable_unprepare(dev->clk); | 179 | goto err; |
176 | return err; | ||
177 | } | ||
178 | 180 | ||
179 | platform_set_drvdata(pdev, dev); | 181 | platform_set_drvdata(pdev, dev); |
180 | 182 | ||
@@ -187,6 +189,10 @@ static int tangox_wdt_probe(struct platform_device *pdev) | |||
187 | dev_info(&pdev->dev, "SMP86xx/SMP87xx watchdog registered\n"); | 189 | dev_info(&pdev->dev, "SMP86xx/SMP87xx watchdog registered\n"); |
188 | 190 | ||
189 | return 0; | 191 | return 0; |
192 | |||
193 | err: | ||
194 | clk_disable_unprepare(dev->clk); | ||
195 | return err; | ||
190 | } | 196 | } |
191 | 197 | ||
192 | static int tangox_wdt_remove(struct platform_device *pdev) | 198 | static int tangox_wdt_remove(struct platform_device *pdev) |
diff --git a/drivers/watchdog/w83627hf_wdt.c b/drivers/watchdog/w83627hf_wdt.c index cab14bc9106c..09e8003039dc 100644 --- a/drivers/watchdog/w83627hf_wdt.c +++ b/drivers/watchdog/w83627hf_wdt.c | |||
@@ -45,10 +45,11 @@ | |||
45 | static int wdt_io; | 45 | static int wdt_io; |
46 | static int cr_wdt_timeout; /* WDT timeout register */ | 46 | static int cr_wdt_timeout; /* WDT timeout register */ |
47 | static int cr_wdt_control; /* WDT control register */ | 47 | static int cr_wdt_control; /* WDT control register */ |
48 | static int cr_wdt_csr; /* WDT control & status register */ | ||
48 | 49 | ||
49 | enum chips { w83627hf, w83627s, w83697hf, w83697ug, w83637hf, w83627thf, | 50 | enum chips { w83627hf, w83627s, w83697hf, w83697ug, w83637hf, w83627thf, |
50 | w83687thf, w83627ehf, w83627dhg, w83627uhg, w83667hg, w83627dhg_p, | 51 | w83687thf, w83627ehf, w83627dhg, w83627uhg, w83667hg, w83627dhg_p, |
51 | w83667hg_b, nct6775, nct6776, nct6779, nct6791, nct6792 }; | 52 | w83667hg_b, nct6775, nct6776, nct6779, nct6791, nct6792, nct6102 }; |
52 | 53 | ||
53 | static int timeout; /* in seconds */ | 54 | static int timeout; /* in seconds */ |
54 | module_param(timeout, int, 0); | 55 | module_param(timeout, int, 0); |
@@ -92,15 +93,21 @@ MODULE_PARM_DESC(early_disable, "Disable watchdog at boot time (default=0)"); | |||
92 | #define W83667HG_B_ID 0xb3 | 93 | #define W83667HG_B_ID 0xb3 |
93 | #define NCT6775_ID 0xb4 | 94 | #define NCT6775_ID 0xb4 |
94 | #define NCT6776_ID 0xc3 | 95 | #define NCT6776_ID 0xc3 |
96 | #define NCT6102_ID 0xc4 | ||
95 | #define NCT6779_ID 0xc5 | 97 | #define NCT6779_ID 0xc5 |
96 | #define NCT6791_ID 0xc8 | 98 | #define NCT6791_ID 0xc8 |
97 | #define NCT6792_ID 0xc9 | 99 | #define NCT6792_ID 0xc9 |
98 | 100 | ||
99 | #define W83627HF_WDT_TIMEOUT 0xf6 | 101 | #define W83627HF_WDT_TIMEOUT 0xf6 |
100 | #define W83697HF_WDT_TIMEOUT 0xf4 | 102 | #define W83697HF_WDT_TIMEOUT 0xf4 |
103 | #define NCT6102D_WDT_TIMEOUT 0xf1 | ||
101 | 104 | ||
102 | #define W83627HF_WDT_CONTROL 0xf5 | 105 | #define W83627HF_WDT_CONTROL 0xf5 |
103 | #define W83697HF_WDT_CONTROL 0xf3 | 106 | #define W83697HF_WDT_CONTROL 0xf3 |
107 | #define NCT6102D_WDT_CONTROL 0xf0 | ||
108 | |||
109 | #define W836X7HF_WDT_CSR 0xf7 | ||
110 | #define NCT6102D_WDT_CSR 0xf2 | ||
104 | 111 | ||
105 | static void superio_outb(int reg, int val) | 112 | static void superio_outb(int reg, int val) |
106 | { | 113 | { |
@@ -197,6 +204,7 @@ static int w83627hf_init(struct watchdog_device *wdog, enum chips chip) | |||
197 | case nct6779: | 204 | case nct6779: |
198 | case nct6791: | 205 | case nct6791: |
199 | case nct6792: | 206 | case nct6792: |
207 | case nct6102: | ||
200 | /* | 208 | /* |
201 | * These chips have a fixed WDTO# output pin (W83627UHG), | 209 | * These chips have a fixed WDTO# output pin (W83627UHG), |
202 | * or support more than one WDTO# output pin. | 210 | * or support more than one WDTO# output pin. |
@@ -229,8 +237,8 @@ static int w83627hf_init(struct watchdog_device *wdog, enum chips chip) | |||
229 | superio_outb(cr_wdt_control, t); | 237 | superio_outb(cr_wdt_control, t); |
230 | 238 | ||
231 | /* reset trigger, disable keyboard & mouse turning off watchdog */ | 239 | /* reset trigger, disable keyboard & mouse turning off watchdog */ |
232 | t = superio_inb(0xF7) & ~0xD0; | 240 | t = superio_inb(cr_wdt_csr) & ~0xD0; |
233 | superio_outb(0xF7, t); | 241 | superio_outb(cr_wdt_csr, t); |
234 | 242 | ||
235 | superio_exit(); | 243 | superio_exit(); |
236 | 244 | ||
@@ -322,6 +330,7 @@ static int wdt_find(int addr) | |||
322 | 330 | ||
323 | cr_wdt_timeout = W83627HF_WDT_TIMEOUT; | 331 | cr_wdt_timeout = W83627HF_WDT_TIMEOUT; |
324 | cr_wdt_control = W83627HF_WDT_CONTROL; | 332 | cr_wdt_control = W83627HF_WDT_CONTROL; |
333 | cr_wdt_csr = W836X7HF_WDT_CSR; | ||
325 | 334 | ||
326 | ret = superio_enter(); | 335 | ret = superio_enter(); |
327 | if (ret) | 336 | if (ret) |
@@ -387,6 +396,12 @@ static int wdt_find(int addr) | |||
387 | case NCT6792_ID: | 396 | case NCT6792_ID: |
388 | ret = nct6792; | 397 | ret = nct6792; |
389 | break; | 398 | break; |
399 | case NCT6102_ID: | ||
400 | ret = nct6102; | ||
401 | cr_wdt_timeout = NCT6102D_WDT_TIMEOUT; | ||
402 | cr_wdt_control = NCT6102D_WDT_CONTROL; | ||
403 | cr_wdt_csr = NCT6102D_WDT_CSR; | ||
404 | break; | ||
390 | case 0xff: | 405 | case 0xff: |
391 | ret = -ENODEV; | 406 | ret = -ENODEV; |
392 | break; | 407 | break; |
@@ -422,6 +437,7 @@ static int __init wdt_init(void) | |||
422 | "NCT6779", | 437 | "NCT6779", |
423 | "NCT6791", | 438 | "NCT6791", |
424 | "NCT6792", | 439 | "NCT6792", |
440 | "NCT6102", | ||
425 | }; | 441 | }; |
426 | 442 | ||
427 | wdt_io = 0x2e; | 443 | wdt_io = 0x2e; |
diff --git a/drivers/watchdog/watchdog_core.c b/drivers/watchdog/watchdog_core.c index e600fd93b7de..c1658fe73d58 100644 --- a/drivers/watchdog/watchdog_core.c +++ b/drivers/watchdog/watchdog_core.c | |||
@@ -164,7 +164,7 @@ static int watchdog_restart_notifier(struct notifier_block *nb, | |||
164 | 164 | ||
165 | int ret; | 165 | int ret; |
166 | 166 | ||
167 | ret = wdd->ops->restart(wdd); | 167 | ret = wdd->ops->restart(wdd, action, data); |
168 | if (ret) | 168 | if (ret) |
169 | return NOTIFY_BAD; | 169 | return NOTIFY_BAD; |
170 | 170 | ||
@@ -199,7 +199,7 @@ static int __watchdog_register_device(struct watchdog_device *wdd) | |||
199 | return -EINVAL; | 199 | return -EINVAL; |
200 | 200 | ||
201 | /* Mandatory operations need to be supported */ | 201 | /* Mandatory operations need to be supported */ |
202 | if (wdd->ops->start == NULL || wdd->ops->stop == NULL) | 202 | if (!wdd->ops->start || (!wdd->ops->stop && !wdd->max_hw_heartbeat_ms)) |
203 | return -EINVAL; | 203 | return -EINVAL; |
204 | 204 | ||
205 | watchdog_check_min_max_timeout(wdd); | 205 | watchdog_check_min_max_timeout(wdd); |
diff --git a/drivers/watchdog/watchdog_dev.c b/drivers/watchdog/watchdog_dev.c index ba2ecce4aae6..e2c5abbb45ff 100644 --- a/drivers/watchdog/watchdog_dev.c +++ b/drivers/watchdog/watchdog_dev.c | |||
@@ -36,6 +36,7 @@ | |||
36 | #include <linux/errno.h> /* For the -ENODEV/... values */ | 36 | #include <linux/errno.h> /* For the -ENODEV/... values */ |
37 | #include <linux/fs.h> /* For file operations */ | 37 | #include <linux/fs.h> /* For file operations */ |
38 | #include <linux/init.h> /* For __init/__exit/... */ | 38 | #include <linux/init.h> /* For __init/__exit/... */ |
39 | #include <linux/jiffies.h> /* For timeout functions */ | ||
39 | #include <linux/kernel.h> /* For printk/panic/... */ | 40 | #include <linux/kernel.h> /* For printk/panic/... */ |
40 | #include <linux/kref.h> /* For data references */ | 41 | #include <linux/kref.h> /* For data references */ |
41 | #include <linux/miscdevice.h> /* For handling misc devices */ | 42 | #include <linux/miscdevice.h> /* For handling misc devices */ |
@@ -44,6 +45,7 @@ | |||
44 | #include <linux/slab.h> /* For memory functions */ | 45 | #include <linux/slab.h> /* For memory functions */ |
45 | #include <linux/types.h> /* For standard types (like size_t) */ | 46 | #include <linux/types.h> /* For standard types (like size_t) */ |
46 | #include <linux/watchdog.h> /* For watchdog specific items */ | 47 | #include <linux/watchdog.h> /* For watchdog specific items */ |
48 | #include <linux/workqueue.h> /* For workqueue */ | ||
47 | #include <linux/uaccess.h> /* For copy_to_user/put_user/... */ | 49 | #include <linux/uaccess.h> /* For copy_to_user/put_user/... */ |
48 | 50 | ||
49 | #include "watchdog_core.h" | 51 | #include "watchdog_core.h" |
@@ -61,6 +63,9 @@ struct watchdog_core_data { | |||
61 | struct cdev cdev; | 63 | struct cdev cdev; |
62 | struct watchdog_device *wdd; | 64 | struct watchdog_device *wdd; |
63 | struct mutex lock; | 65 | struct mutex lock; |
66 | unsigned long last_keepalive; | ||
67 | unsigned long last_hw_keepalive; | ||
68 | struct delayed_work work; | ||
64 | unsigned long status; /* Internal status bits */ | 69 | unsigned long status; /* Internal status bits */ |
65 | #define _WDOG_DEV_OPEN 0 /* Opened ? */ | 70 | #define _WDOG_DEV_OPEN 0 /* Opened ? */ |
66 | #define _WDOG_ALLOW_RELEASE 1 /* Did we receive the magic char ? */ | 71 | #define _WDOG_ALLOW_RELEASE 1 /* Did we receive the magic char ? */ |
@@ -71,6 +76,91 @@ static dev_t watchdog_devt; | |||
71 | /* Reference to watchdog device behind /dev/watchdog */ | 76 | /* Reference to watchdog device behind /dev/watchdog */ |
72 | static struct watchdog_core_data *old_wd_data; | 77 | static struct watchdog_core_data *old_wd_data; |
73 | 78 | ||
79 | static struct workqueue_struct *watchdog_wq; | ||
80 | |||
81 | static inline bool watchdog_need_worker(struct watchdog_device *wdd) | ||
82 | { | ||
83 | /* All variables in milli-seconds */ | ||
84 | unsigned int hm = wdd->max_hw_heartbeat_ms; | ||
85 | unsigned int t = wdd->timeout * 1000; | ||
86 | |||
87 | /* | ||
88 | * A worker to generate heartbeat requests is needed if all of the | ||
89 | * following conditions are true. | ||
90 | * - Userspace activated the watchdog. | ||
91 | * - The driver provided a value for the maximum hardware timeout, and | ||
92 | * thus is aware that the framework supports generating heartbeat | ||
93 | * requests. | ||
94 | * - Userspace requests a longer timeout than the hardware can handle. | ||
95 | */ | ||
96 | return hm && ((watchdog_active(wdd) && t > hm) || | ||
97 | (t && !watchdog_active(wdd) && watchdog_hw_running(wdd))); | ||
98 | } | ||
99 | |||
100 | static long watchdog_next_keepalive(struct watchdog_device *wdd) | ||
101 | { | ||
102 | struct watchdog_core_data *wd_data = wdd->wd_data; | ||
103 | unsigned int timeout_ms = wdd->timeout * 1000; | ||
104 | unsigned long keepalive_interval; | ||
105 | unsigned long last_heartbeat; | ||
106 | unsigned long virt_timeout; | ||
107 | unsigned int hw_heartbeat_ms; | ||
108 | |||
109 | virt_timeout = wd_data->last_keepalive + msecs_to_jiffies(timeout_ms); | ||
110 | hw_heartbeat_ms = min(timeout_ms, wdd->max_hw_heartbeat_ms); | ||
111 | keepalive_interval = msecs_to_jiffies(hw_heartbeat_ms / 2); | ||
112 | |||
113 | if (!watchdog_active(wdd)) | ||
114 | return keepalive_interval; | ||
115 | |||
116 | /* | ||
117 | * To ensure that the watchdog times out wdd->timeout seconds | ||
118 | * after the most recent ping from userspace, the last | ||
119 | * worker ping has to come in hw_heartbeat_ms before this timeout. | ||
120 | */ | ||
121 | last_heartbeat = virt_timeout - msecs_to_jiffies(hw_heartbeat_ms); | ||
122 | return min_t(long, last_heartbeat - jiffies, keepalive_interval); | ||
123 | } | ||
124 | |||
125 | static inline void watchdog_update_worker(struct watchdog_device *wdd) | ||
126 | { | ||
127 | struct watchdog_core_data *wd_data = wdd->wd_data; | ||
128 | |||
129 | if (watchdog_need_worker(wdd)) { | ||
130 | long t = watchdog_next_keepalive(wdd); | ||
131 | |||
132 | if (t > 0) | ||
133 | mod_delayed_work(watchdog_wq, &wd_data->work, t); | ||
134 | } else { | ||
135 | cancel_delayed_work(&wd_data->work); | ||
136 | } | ||
137 | } | ||
138 | |||
139 | static int __watchdog_ping(struct watchdog_device *wdd) | ||
140 | { | ||
141 | struct watchdog_core_data *wd_data = wdd->wd_data; | ||
142 | unsigned long earliest_keepalive = wd_data->last_hw_keepalive + | ||
143 | msecs_to_jiffies(wdd->min_hw_heartbeat_ms); | ||
144 | int err; | ||
145 | |||
146 | if (time_is_after_jiffies(earliest_keepalive)) { | ||
147 | mod_delayed_work(watchdog_wq, &wd_data->work, | ||
148 | earliest_keepalive - jiffies); | ||
149 | return 0; | ||
150 | } | ||
151 | |||
152 | wd_data->last_hw_keepalive = jiffies; | ||
153 | |||
154 | if (wdd->ops->ping) | ||
155 | err = wdd->ops->ping(wdd); /* ping the watchdog */ | ||
156 | else | ||
157 | err = wdd->ops->start(wdd); /* restart watchdog */ | ||
158 | |||
159 | watchdog_update_worker(wdd); | ||
160 | |||
161 | return err; | ||
162 | } | ||
163 | |||
74 | /* | 164 | /* |
75 | * watchdog_ping: ping the watchdog. | 165 | * watchdog_ping: ping the watchdog. |
76 | * @wdd: the watchdog device to ping | 166 | * @wdd: the watchdog device to ping |
@@ -85,17 +175,28 @@ static struct watchdog_core_data *old_wd_data; | |||
85 | 175 | ||
86 | static int watchdog_ping(struct watchdog_device *wdd) | 176 | static int watchdog_ping(struct watchdog_device *wdd) |
87 | { | 177 | { |
88 | int err; | 178 | struct watchdog_core_data *wd_data = wdd->wd_data; |
89 | 179 | ||
90 | if (!watchdog_active(wdd)) | 180 | if (!watchdog_active(wdd) && !watchdog_hw_running(wdd)) |
91 | return 0; | 181 | return 0; |
92 | 182 | ||
93 | if (wdd->ops->ping) | 183 | wd_data->last_keepalive = jiffies; |
94 | err = wdd->ops->ping(wdd); /* ping the watchdog */ | 184 | return __watchdog_ping(wdd); |
95 | else | 185 | } |
96 | err = wdd->ops->start(wdd); /* restart watchdog */ | ||
97 | 186 | ||
98 | return err; | 187 | static void watchdog_ping_work(struct work_struct *work) |
188 | { | ||
189 | struct watchdog_core_data *wd_data; | ||
190 | struct watchdog_device *wdd; | ||
191 | |||
192 | wd_data = container_of(to_delayed_work(work), struct watchdog_core_data, | ||
193 | work); | ||
194 | |||
195 | mutex_lock(&wd_data->lock); | ||
196 | wdd = wd_data->wdd; | ||
197 | if (wdd && (watchdog_active(wdd) || watchdog_hw_running(wdd))) | ||
198 | __watchdog_ping(wdd); | ||
199 | mutex_unlock(&wd_data->lock); | ||
99 | } | 200 | } |
100 | 201 | ||
101 | /* | 202 | /* |
@@ -111,14 +212,23 @@ static int watchdog_ping(struct watchdog_device *wdd) | |||
111 | 212 | ||
112 | static int watchdog_start(struct watchdog_device *wdd) | 213 | static int watchdog_start(struct watchdog_device *wdd) |
113 | { | 214 | { |
215 | struct watchdog_core_data *wd_data = wdd->wd_data; | ||
216 | unsigned long started_at; | ||
114 | int err; | 217 | int err; |
115 | 218 | ||
116 | if (watchdog_active(wdd)) | 219 | if (watchdog_active(wdd)) |
117 | return 0; | 220 | return 0; |
118 | 221 | ||
119 | err = wdd->ops->start(wdd); | 222 | started_at = jiffies; |
120 | if (err == 0) | 223 | if (watchdog_hw_running(wdd) && wdd->ops->ping) |
224 | err = wdd->ops->ping(wdd); | ||
225 | else | ||
226 | err = wdd->ops->start(wdd); | ||
227 | if (err == 0) { | ||
121 | set_bit(WDOG_ACTIVE, &wdd->status); | 228 | set_bit(WDOG_ACTIVE, &wdd->status); |
229 | wd_data->last_keepalive = started_at; | ||
230 | watchdog_update_worker(wdd); | ||
231 | } | ||
122 | 232 | ||
123 | return err; | 233 | return err; |
124 | } | 234 | } |
@@ -137,7 +247,7 @@ static int watchdog_start(struct watchdog_device *wdd) | |||
137 | 247 | ||
138 | static int watchdog_stop(struct watchdog_device *wdd) | 248 | static int watchdog_stop(struct watchdog_device *wdd) |
139 | { | 249 | { |
140 | int err; | 250 | int err = 0; |
141 | 251 | ||
142 | if (!watchdog_active(wdd)) | 252 | if (!watchdog_active(wdd)) |
143 | return 0; | 253 | return 0; |
@@ -148,9 +258,15 @@ static int watchdog_stop(struct watchdog_device *wdd) | |||
148 | return -EBUSY; | 258 | return -EBUSY; |
149 | } | 259 | } |
150 | 260 | ||
151 | err = wdd->ops->stop(wdd); | 261 | if (wdd->ops->stop) |
152 | if (err == 0) | 262 | err = wdd->ops->stop(wdd); |
263 | else | ||
264 | set_bit(WDOG_HW_RUNNING, &wdd->status); | ||
265 | |||
266 | if (err == 0) { | ||
153 | clear_bit(WDOG_ACTIVE, &wdd->status); | 267 | clear_bit(WDOG_ACTIVE, &wdd->status); |
268 | watchdog_update_worker(wdd); | ||
269 | } | ||
154 | 270 | ||
155 | return err; | 271 | return err; |
156 | } | 272 | } |
@@ -183,13 +299,22 @@ static unsigned int watchdog_get_status(struct watchdog_device *wdd) | |||
183 | static int watchdog_set_timeout(struct watchdog_device *wdd, | 299 | static int watchdog_set_timeout(struct watchdog_device *wdd, |
184 | unsigned int timeout) | 300 | unsigned int timeout) |
185 | { | 301 | { |
186 | if (!wdd->ops->set_timeout || !(wdd->info->options & WDIOF_SETTIMEOUT)) | 302 | int err = 0; |
303 | |||
304 | if (!(wdd->info->options & WDIOF_SETTIMEOUT)) | ||
187 | return -EOPNOTSUPP; | 305 | return -EOPNOTSUPP; |
188 | 306 | ||
189 | if (watchdog_timeout_invalid(wdd, timeout)) | 307 | if (watchdog_timeout_invalid(wdd, timeout)) |
190 | return -EINVAL; | 308 | return -EINVAL; |
191 | 309 | ||
192 | return wdd->ops->set_timeout(wdd, timeout); | 310 | if (wdd->ops->set_timeout) |
311 | err = wdd->ops->set_timeout(wdd, timeout); | ||
312 | else | ||
313 | wdd->timeout = timeout; | ||
314 | |||
315 | watchdog_update_worker(wdd); | ||
316 | |||
317 | return err; | ||
193 | } | 318 | } |
194 | 319 | ||
195 | /* | 320 | /* |
@@ -538,7 +663,7 @@ static int watchdog_open(struct inode *inode, struct file *file) | |||
538 | * If the /dev/watchdog device is open, we don't want the module | 663 | * If the /dev/watchdog device is open, we don't want the module |
539 | * to be unloaded. | 664 | * to be unloaded. |
540 | */ | 665 | */ |
541 | if (!try_module_get(wdd->ops->owner)) { | 666 | if (!watchdog_hw_running(wdd) && !try_module_get(wdd->ops->owner)) { |
542 | err = -EBUSY; | 667 | err = -EBUSY; |
543 | goto out_clear; | 668 | goto out_clear; |
544 | } | 669 | } |
@@ -549,7 +674,8 @@ static int watchdog_open(struct inode *inode, struct file *file) | |||
549 | 674 | ||
550 | file->private_data = wd_data; | 675 | file->private_data = wd_data; |
551 | 676 | ||
552 | kref_get(&wd_data->kref); | 677 | if (!watchdog_hw_running(wdd)) |
678 | kref_get(&wd_data->kref); | ||
553 | 679 | ||
554 | /* dev/watchdog is a virtual (and thus non-seekable) filesystem */ | 680 | /* dev/watchdog is a virtual (and thus non-seekable) filesystem */ |
555 | return nonseekable_open(inode, file); | 681 | return nonseekable_open(inode, file); |
@@ -585,6 +711,7 @@ static int watchdog_release(struct inode *inode, struct file *file) | |||
585 | struct watchdog_core_data *wd_data = file->private_data; | 711 | struct watchdog_core_data *wd_data = file->private_data; |
586 | struct watchdog_device *wdd; | 712 | struct watchdog_device *wdd; |
587 | int err = -EBUSY; | 713 | int err = -EBUSY; |
714 | bool running; | ||
588 | 715 | ||
589 | mutex_lock(&wd_data->lock); | 716 | mutex_lock(&wd_data->lock); |
590 | 717 | ||
@@ -609,14 +736,24 @@ static int watchdog_release(struct inode *inode, struct file *file) | |||
609 | watchdog_ping(wdd); | 736 | watchdog_ping(wdd); |
610 | } | 737 | } |
611 | 738 | ||
739 | cancel_delayed_work_sync(&wd_data->work); | ||
740 | watchdog_update_worker(wdd); | ||
741 | |||
612 | /* make sure that /dev/watchdog can be re-opened */ | 742 | /* make sure that /dev/watchdog can be re-opened */ |
613 | clear_bit(_WDOG_DEV_OPEN, &wd_data->status); | 743 | clear_bit(_WDOG_DEV_OPEN, &wd_data->status); |
614 | 744 | ||
615 | done: | 745 | done: |
746 | running = wdd && watchdog_hw_running(wdd); | ||
616 | mutex_unlock(&wd_data->lock); | 747 | mutex_unlock(&wd_data->lock); |
617 | /* Allow the owner module to be unloaded again */ | 748 | /* |
618 | module_put(wd_data->cdev.owner); | 749 | * Allow the owner module to be unloaded again unless the watchdog |
619 | kref_put(&wd_data->kref, watchdog_core_data_release); | 750 | * is still running. If the watchdog is still running, it can not |
751 | * be stopped, and its driver must not be unloaded. | ||
752 | */ | ||
753 | if (!running) { | ||
754 | module_put(wd_data->cdev.owner); | ||
755 | kref_put(&wd_data->kref, watchdog_core_data_release); | ||
756 | } | ||
620 | return 0; | 757 | return 0; |
621 | } | 758 | } |
622 | 759 | ||
@@ -658,6 +795,11 @@ static int watchdog_cdev_register(struct watchdog_device *wdd, dev_t devno) | |||
658 | wd_data->wdd = wdd; | 795 | wd_data->wdd = wdd; |
659 | wdd->wd_data = wd_data; | 796 | wdd->wd_data = wd_data; |
660 | 797 | ||
798 | if (!watchdog_wq) | ||
799 | return -ENODEV; | ||
800 | |||
801 | INIT_DELAYED_WORK(&wd_data->work, watchdog_ping_work); | ||
802 | |||
661 | if (wdd->id == 0) { | 803 | if (wdd->id == 0) { |
662 | old_wd_data = wd_data; | 804 | old_wd_data = wd_data; |
663 | watchdog_miscdev.parent = wdd->parent; | 805 | watchdog_miscdev.parent = wdd->parent; |
@@ -688,8 +830,23 @@ static int watchdog_cdev_register(struct watchdog_device *wdd, dev_t devno) | |||
688 | old_wd_data = NULL; | 830 | old_wd_data = NULL; |
689 | kref_put(&wd_data->kref, watchdog_core_data_release); | 831 | kref_put(&wd_data->kref, watchdog_core_data_release); |
690 | } | 832 | } |
833 | return err; | ||
691 | } | 834 | } |
692 | return err; | 835 | |
836 | /* Record time of most recent heartbeat as 'just before now'. */ | ||
837 | wd_data->last_hw_keepalive = jiffies - 1; | ||
838 | |||
839 | /* | ||
840 | * If the watchdog is running, prevent its driver from being unloaded, | ||
841 | * and schedule an immediate ping. | ||
842 | */ | ||
843 | if (watchdog_hw_running(wdd)) { | ||
844 | __module_get(wdd->ops->owner); | ||
845 | kref_get(&wd_data->kref); | ||
846 | queue_delayed_work(watchdog_wq, &wd_data->work, 0); | ||
847 | } | ||
848 | |||
849 | return 0; | ||
693 | } | 850 | } |
694 | 851 | ||
695 | /* | 852 | /* |
@@ -715,6 +872,8 @@ static void watchdog_cdev_unregister(struct watchdog_device *wdd) | |||
715 | wdd->wd_data = NULL; | 872 | wdd->wd_data = NULL; |
716 | mutex_unlock(&wd_data->lock); | 873 | mutex_unlock(&wd_data->lock); |
717 | 874 | ||
875 | cancel_delayed_work_sync(&wd_data->work); | ||
876 | |||
718 | kref_put(&wd_data->kref, watchdog_core_data_release); | 877 | kref_put(&wd_data->kref, watchdog_core_data_release); |
719 | } | 878 | } |
720 | 879 | ||
@@ -780,6 +939,13 @@ int __init watchdog_dev_init(void) | |||
780 | { | 939 | { |
781 | int err; | 940 | int err; |
782 | 941 | ||
942 | watchdog_wq = alloc_workqueue("watchdogd", | ||
943 | WQ_HIGHPRI | WQ_MEM_RECLAIM, 0); | ||
944 | if (!watchdog_wq) { | ||
945 | pr_err("Failed to create watchdog workqueue\n"); | ||
946 | return -ENOMEM; | ||
947 | } | ||
948 | |||
783 | err = class_register(&watchdog_class); | 949 | err = class_register(&watchdog_class); |
784 | if (err < 0) { | 950 | if (err < 0) { |
785 | pr_err("couldn't register class\n"); | 951 | pr_err("couldn't register class\n"); |
@@ -806,4 +972,5 @@ void __exit watchdog_dev_exit(void) | |||
806 | { | 972 | { |
807 | unregister_chrdev_region(watchdog_devt, MAX_DOGS); | 973 | unregister_chrdev_region(watchdog_devt, MAX_DOGS); |
808 | class_unregister(&watchdog_class); | 974 | class_unregister(&watchdog_class); |
975 | destroy_workqueue(watchdog_wq); | ||
809 | } | 976 | } |
diff --git a/drivers/watchdog/ziirave_wdt.c b/drivers/watchdog/ziirave_wdt.c index 0c7cb7302cf0..cbe373de3659 100644 --- a/drivers/watchdog/ziirave_wdt.c +++ b/drivers/watchdog/ziirave_wdt.c | |||
@@ -36,7 +36,7 @@ | |||
36 | #define ZIIRAVE_STATE_OFF 0x1 | 36 | #define ZIIRAVE_STATE_OFF 0x1 |
37 | #define ZIIRAVE_STATE_ON 0x2 | 37 | #define ZIIRAVE_STATE_ON 0x2 |
38 | 38 | ||
39 | static char *ziirave_reasons[] = {"power cycle", "triggered", NULL, NULL, | 39 | static char *ziirave_reasons[] = {"power cycle", "hw watchdog", NULL, NULL, |
40 | "host request", NULL, "illegal configuration", | 40 | "host request", NULL, "illegal configuration", |
41 | "illegal instruction", "illegal trap", | 41 | "illegal instruction", "illegal trap", |
42 | "unknown"}; | 42 | "unknown"}; |
diff --git a/include/linux/watchdog.h b/include/linux/watchdog.h index b585fa2507ee..51732d6c9555 100644 --- a/include/linux/watchdog.h +++ b/include/linux/watchdog.h | |||
@@ -10,8 +10,9 @@ | |||
10 | 10 | ||
11 | 11 | ||
12 | #include <linux/bitops.h> | 12 | #include <linux/bitops.h> |
13 | #include <linux/device.h> | ||
14 | #include <linux/cdev.h> | 13 | #include <linux/cdev.h> |
14 | #include <linux/device.h> | ||
15 | #include <linux/kernel.h> | ||
15 | #include <linux/notifier.h> | 16 | #include <linux/notifier.h> |
16 | #include <uapi/linux/watchdog.h> | 17 | #include <uapi/linux/watchdog.h> |
17 | 18 | ||
@@ -46,7 +47,7 @@ struct watchdog_ops { | |||
46 | unsigned int (*status)(struct watchdog_device *); | 47 | unsigned int (*status)(struct watchdog_device *); |
47 | int (*set_timeout)(struct watchdog_device *, unsigned int); | 48 | int (*set_timeout)(struct watchdog_device *, unsigned int); |
48 | unsigned int (*get_timeleft)(struct watchdog_device *); | 49 | unsigned int (*get_timeleft)(struct watchdog_device *); |
49 | int (*restart)(struct watchdog_device *); | 50 | int (*restart)(struct watchdog_device *, unsigned long, void *); |
50 | long (*ioctl)(struct watchdog_device *, unsigned int, unsigned long); | 51 | long (*ioctl)(struct watchdog_device *, unsigned int, unsigned long); |
51 | }; | 52 | }; |
52 | 53 | ||
@@ -61,14 +62,21 @@ struct watchdog_ops { | |||
61 | * @bootstatus: Status of the watchdog device at boot. | 62 | * @bootstatus: Status of the watchdog device at boot. |
62 | * @timeout: The watchdog devices timeout value (in seconds). | 63 | * @timeout: The watchdog devices timeout value (in seconds). |
63 | * @min_timeout:The watchdog devices minimum timeout value (in seconds). | 64 | * @min_timeout:The watchdog devices minimum timeout value (in seconds). |
64 | * @max_timeout:The watchdog devices maximum timeout value (in seconds). | 65 | * @max_timeout:The watchdog devices maximum timeout value (in seconds) |
66 | * as configurable from user space. Only relevant if | ||
67 | * max_hw_heartbeat_ms is not provided. | ||
68 | * @min_hw_heartbeat_ms: | ||
69 | * Minimum time between heartbeats, in milli-seconds. | ||
70 | * @max_hw_heartbeat_ms: | ||
71 | * Hardware limit for maximum timeout, in milli-seconds. | ||
72 | * Replaces max_timeout if specified. | ||
65 | * @reboot_nb: The notifier block to stop watchdog on reboot. | 73 | * @reboot_nb: The notifier block to stop watchdog on reboot. |
66 | * @restart_nb: The notifier block to register a restart function. | 74 | * @restart_nb: The notifier block to register a restart function. |
67 | * @driver_data:Pointer to the drivers private data. | 75 | * @driver_data:Pointer to the drivers private data. |
68 | * @wd_data: Pointer to watchdog core internal data. | 76 | * @wd_data: Pointer to watchdog core internal data. |
69 | * @status: Field that contains the devices internal status bits. | 77 | * @status: Field that contains the devices internal status bits. |
70 | * @deferred: entry in wtd_deferred_reg_list which is used to | 78 | * @deferred: Entry in wtd_deferred_reg_list which is used to |
71 | * register early initialized watchdogs. | 79 | * register early initialized watchdogs. |
72 | * | 80 | * |
73 | * The watchdog_device structure contains all information about a | 81 | * The watchdog_device structure contains all information about a |
74 | * watchdog timer device. | 82 | * watchdog timer device. |
@@ -89,6 +97,8 @@ struct watchdog_device { | |||
89 | unsigned int timeout; | 97 | unsigned int timeout; |
90 | unsigned int min_timeout; | 98 | unsigned int min_timeout; |
91 | unsigned int max_timeout; | 99 | unsigned int max_timeout; |
100 | unsigned int min_hw_heartbeat_ms; | ||
101 | unsigned int max_hw_heartbeat_ms; | ||
92 | struct notifier_block reboot_nb; | 102 | struct notifier_block reboot_nb; |
93 | struct notifier_block restart_nb; | 103 | struct notifier_block restart_nb; |
94 | void *driver_data; | 104 | void *driver_data; |
@@ -98,6 +108,7 @@ struct watchdog_device { | |||
98 | #define WDOG_ACTIVE 0 /* Is the watchdog running/active */ | 108 | #define WDOG_ACTIVE 0 /* Is the watchdog running/active */ |
99 | #define WDOG_NO_WAY_OUT 1 /* Is 'nowayout' feature set ? */ | 109 | #define WDOG_NO_WAY_OUT 1 /* Is 'nowayout' feature set ? */ |
100 | #define WDOG_STOP_ON_REBOOT 2 /* Should be stopped on reboot */ | 110 | #define WDOG_STOP_ON_REBOOT 2 /* Should be stopped on reboot */ |
111 | #define WDOG_HW_RUNNING 3 /* True if HW watchdog running */ | ||
101 | struct list_head deferred; | 112 | struct list_head deferred; |
102 | }; | 113 | }; |
103 | 114 | ||
@@ -110,6 +121,15 @@ static inline bool watchdog_active(struct watchdog_device *wdd) | |||
110 | return test_bit(WDOG_ACTIVE, &wdd->status); | 121 | return test_bit(WDOG_ACTIVE, &wdd->status); |
111 | } | 122 | } |
112 | 123 | ||
124 | /* | ||
125 | * Use the following function to check whether or not the hardware watchdog | ||
126 | * is running | ||
127 | */ | ||
128 | static inline bool watchdog_hw_running(struct watchdog_device *wdd) | ||
129 | { | ||
130 | return test_bit(WDOG_HW_RUNNING, &wdd->status); | ||
131 | } | ||
132 | |||
113 | /* Use the following function to set the nowayout feature */ | 133 | /* Use the following function to set the nowayout feature */ |
114 | static inline void watchdog_set_nowayout(struct watchdog_device *wdd, bool nowayout) | 134 | static inline void watchdog_set_nowayout(struct watchdog_device *wdd, bool nowayout) |
115 | { | 135 | { |
@@ -128,13 +148,18 @@ static inline bool watchdog_timeout_invalid(struct watchdog_device *wdd, unsigne | |||
128 | { | 148 | { |
129 | /* | 149 | /* |
130 | * The timeout is invalid if | 150 | * The timeout is invalid if |
151 | * - the requested value is larger than UINT_MAX / 1000 | ||
152 | * (since internal calculations are done in milli-seconds), | ||
153 | * or | ||
131 | * - the requested value is smaller than the configured minimum timeout, | 154 | * - the requested value is smaller than the configured minimum timeout, |
132 | * or | 155 | * or |
133 | * - a maximum timeout is configured, and the requested value is larger | 156 | * - a maximum hardware timeout is not configured, a maximum timeout |
134 | * than the maximum timeout. | 157 | * is configured, and the requested value is larger than the |
158 | * configured maximum timeout. | ||
135 | */ | 159 | */ |
136 | return t < wdd->min_timeout || | 160 | return t > UINT_MAX / 1000 || t < wdd->min_timeout || |
137 | (wdd->max_timeout && t > wdd->max_timeout); | 161 | (!wdd->max_hw_heartbeat_ms && wdd->max_timeout && |
162 | t > wdd->max_timeout); | ||
138 | } | 163 | } |
139 | 164 | ||
140 | /* Use the following functions to manipulate watchdog driver specific data */ | 165 | /* Use the following functions to manipulate watchdog driver specific data */ |