diff options
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 */ |
