diff options
25 files changed, 2106 insertions, 472 deletions
diff --git a/Documentation/devicetree/bindings/watchdog/fsl-imx-wdt.txt b/Documentation/devicetree/bindings/watchdog/fsl-imx-wdt.txt new file mode 100644 index 000000000000..2144af1a5264 --- /dev/null +++ b/Documentation/devicetree/bindings/watchdog/fsl-imx-wdt.txt | |||
| @@ -0,0 +1,14 @@ | |||
| 1 | * Freescale i.MX Watchdog Timer (WDT) Controller | ||
| 2 | |||
| 3 | Required properties: | ||
| 4 | - compatible : Should be "fsl,<soc>-wdt" | ||
| 5 | - reg : Should contain WDT registers location and length | ||
| 6 | - interrupts : Should contain WDT interrupt | ||
| 7 | |||
| 8 | Examples: | ||
| 9 | |||
| 10 | wdt@73f98000 { | ||
| 11 | compatible = "fsl,imx51-wdt", "fsl,imx21-wdt"; | ||
| 12 | reg = <0x73f98000 0x4000>; | ||
| 13 | interrupts = <58>; | ||
| 14 | }; | ||
diff --git a/Documentation/devicetree/bindings/watchdog/samsung-wdt.txt b/Documentation/devicetree/bindings/watchdog/samsung-wdt.txt new file mode 100644 index 000000000000..79ead8263ae4 --- /dev/null +++ b/Documentation/devicetree/bindings/watchdog/samsung-wdt.txt | |||
| @@ -0,0 +1,11 @@ | |||
| 1 | * Samsung's Watchdog Timer Controller | ||
| 2 | |||
| 3 | The Samsung's Watchdog controller is used for resuming system operation | ||
| 4 | after a preset amount of time during which the WDT reset event has not | ||
| 5 | occured. | ||
| 6 | |||
| 7 | Required properties: | ||
| 8 | - compatible : should be "samsung,s3c2410-wdt" | ||
| 9 | - reg : base physical address of the controller and length of memory mapped | ||
| 10 | region. | ||
| 11 | - interrupts : interrupt number to the cpu. | ||
diff --git a/Documentation/watchdog/00-INDEX b/Documentation/watchdog/00-INDEX index ee994513a9b1..fc51128071c2 100644 --- a/Documentation/watchdog/00-INDEX +++ b/Documentation/watchdog/00-INDEX | |||
| @@ -8,6 +8,8 @@ src/ | |||
| 8 | - directory holding watchdog related example programs. | 8 | - directory holding watchdog related example programs. |
| 9 | watchdog-api.txt | 9 | watchdog-api.txt |
| 10 | - description of the Linux Watchdog driver API. | 10 | - description of the Linux Watchdog driver API. |
| 11 | watchdog-kernel-api.txt | ||
| 12 | - description of the Linux WatchDog Timer Driver Core kernel API. | ||
| 11 | watchdog-parameters.txt | 13 | watchdog-parameters.txt |
| 12 | - information on driver parameters (for drivers other than | 14 | - information on driver parameters (for drivers other than |
| 13 | the ones that have driver-specific files here) | 15 | the ones that have driver-specific files here) |
diff --git a/Documentation/watchdog/watchdog-kernel-api.txt b/Documentation/watchdog/watchdog-kernel-api.txt new file mode 100644 index 000000000000..4f7c894244d2 --- /dev/null +++ b/Documentation/watchdog/watchdog-kernel-api.txt | |||
| @@ -0,0 +1,162 @@ | |||
| 1 | The Linux WatchDog Timer Driver Core kernel API. | ||
| 2 | =============================================== | ||
| 3 | Last reviewed: 22-Jul-2011 | ||
| 4 | |||
| 5 | Wim Van Sebroeck <wim@iguana.be> | ||
| 6 | |||
| 7 | Introduction | ||
| 8 | ------------ | ||
| 9 | This document does not describe what a WatchDog Timer (WDT) Driver or Device is. | ||
| 10 | It also does not describe the API which can be used by user space to communicate | ||
| 11 | with a WatchDog Timer. If you want to know this then please read the following | ||
| 12 | file: Documentation/watchdog/watchdog-api.txt . | ||
| 13 | |||
| 14 | So what does this document describe? It describes the API that can be used by | ||
| 15 | WatchDog Timer Drivers that want to use the WatchDog Timer Driver Core | ||
| 16 | Framework. This framework provides all interfacing towards user space so that | ||
| 17 | the same code does not have to be reproduced each time. This also means that | ||
| 18 | a watchdog timer driver then only needs to provide the different routines | ||
| 19 | (operations) that control the watchdog timer (WDT). | ||
| 20 | |||
| 21 | The API | ||
| 22 | ------- | ||
| 23 | Each watchdog timer driver that wants to use the WatchDog Timer Driver Core | ||
| 24 | must #include <linux/watchdog.h> (you would have to do this anyway when | ||
| 25 | writing a watchdog device driver). This include file contains following | ||
| 26 | register/unregister routines: | ||
| 27 | |||
| 28 | extern int watchdog_register_device(struct watchdog_device *); | ||
| 29 | extern void watchdog_unregister_device(struct watchdog_device *); | ||
| 30 | |||
| 31 | The watchdog_register_device routine registers a watchdog timer device. | ||
| 32 | The parameter of this routine is a pointer to a watchdog_device structure. | ||
| 33 | This routine returns zero on success and a negative errno code for failure. | ||
| 34 | |||
| 35 | The watchdog_unregister_device routine deregisters a registered watchdog timer | ||
| 36 | device. The parameter of this routine is the pointer to the registered | ||
| 37 | watchdog_device structure. | ||
| 38 | |||
| 39 | The watchdog device structure looks like this: | ||
| 40 | |||
| 41 | struct watchdog_device { | ||
| 42 | const struct watchdog_info *info; | ||
| 43 | const struct watchdog_ops *ops; | ||
| 44 | unsigned int bootstatus; | ||
| 45 | unsigned int timeout; | ||
| 46 | unsigned int min_timeout; | ||
| 47 | unsigned int max_timeout; | ||
| 48 | void *driver_data; | ||
| 49 | unsigned long status; | ||
| 50 | }; | ||
| 51 | |||
| 52 | It contains following fields: | ||
| 53 | * info: a pointer to a watchdog_info structure. This structure gives some | ||
| 54 | additional information about the watchdog timer itself. (Like it's unique name) | ||
| 55 | * ops: a pointer to the list of watchdog operations that the watchdog supports. | ||
| 56 | * timeout: the watchdog timer's timeout value (in seconds). | ||
| 57 | * min_timeout: the watchdog timer's minimum timeout value (in seconds). | ||
| 58 | * max_timeout: the watchdog timer's maximum timeout value (in seconds). | ||
| 59 | * bootstatus: status of the device after booting (reported with watchdog | ||
| 60 | WDIOF_* status bits). | ||
| 61 | * driver_data: a pointer to the drivers private data of a watchdog device. | ||
| 62 | This data should only be accessed via the watchdog_set_drvadata and | ||
| 63 | watchdog_get_drvdata routines. | ||
| 64 | * status: this field contains a number of status bits that give extra | ||
| 65 | information about the status of the device (Like: is the watchdog timer | ||
| 66 | running/active, is the nowayout bit set, is the device opened via | ||
| 67 | the /dev/watchdog interface or not, ...). | ||
| 68 | |||
| 69 | The list of watchdog operations is defined as: | ||
| 70 | |||
| 71 | struct watchdog_ops { | ||
| 72 | struct module *owner; | ||
| 73 | /* mandatory operations */ | ||
| 74 | int (*start)(struct watchdog_device *); | ||
| 75 | int (*stop)(struct watchdog_device *); | ||
| 76 | /* optional operations */ | ||
| 77 | int (*ping)(struct watchdog_device *); | ||
| 78 | unsigned int (*status)(struct watchdog_device *); | ||
| 79 | int (*set_timeout)(struct watchdog_device *, unsigned int); | ||
| 80 | long (*ioctl)(struct watchdog_device *, unsigned int, unsigned long); | ||
| 81 | }; | ||
| 82 | |||
| 83 | It is important that you first define the module owner of the watchdog timer | ||
| 84 | driver's operations. This module owner will be used to lock the module when | ||
| 85 | the watchdog is active. (This to avoid a system crash when you unload the | ||
| 86 | module and /dev/watchdog is still open). | ||
| 87 | Some operations are mandatory and some are optional. The mandatory operations | ||
| 88 | are: | ||
| 89 | * start: this is a pointer to the routine that starts the watchdog timer | ||
| 90 | device. | ||
| 91 | The routine needs a pointer to the watchdog timer device structure as a | ||
| 92 | parameter. It returns zero on success or a negative errno code for failure. | ||
| 93 | * stop: with this routine the watchdog timer device is being stopped. | ||
| 94 | The routine needs a pointer to the watchdog timer device structure as a | ||
| 95 | parameter. It returns zero on success or a negative errno code for failure. | ||
| 96 | Some watchdog timer hardware can only be started and not be stopped. The | ||
| 97 | driver supporting this hardware needs to make sure that a start and stop | ||
| 98 | routine is being provided. This can be done by using a timer in the driver | ||
| 99 | that regularly sends a keepalive ping to the watchdog timer hardware. | ||
| 100 | |||
| 101 | Not all watchdog timer hardware supports the same functionality. That's why | ||
| 102 | all other routines/operations are optional. They only need to be provided if | ||
| 103 | they are supported. These optional routines/operations are: | ||
| 104 | * ping: this is the routine that sends a keepalive ping to the watchdog timer | ||
| 105 | hardware. | ||
| 106 | The routine needs a pointer to the watchdog timer device structure as a | ||
| 107 | parameter. It returns zero on success or a negative errno code for failure. | ||
| 108 | Most hardware that does not support this as a separate function uses the | ||
| 109 | start function to restart the watchdog timer hardware. And that's also what | ||
| 110 | the watchdog timer driver core does: to send a keepalive ping to the watchdog | ||
| 111 | timer hardware it will either use the ping operation (when available) or the | ||
| 112 | start operation (when the ping operation is not available). | ||
| 113 | (Note: the WDIOC_KEEPALIVE ioctl call will only be active when the | ||
| 114 | WDIOF_KEEPALIVEPING bit has been set in the option field on the watchdog's | ||
| 115 | info structure). | ||
| 116 | * status: this routine checks the status of the watchdog timer device. The | ||
| 117 | status of the device is reported with watchdog WDIOF_* status flags/bits. | ||
| 118 | * set_timeout: this routine checks and changes the timeout of the watchdog | ||
| 119 | timer device. It returns 0 on success, -EINVAL for "parameter out of range" | ||
| 120 | and -EIO for "could not write value to the watchdog". On success the timeout | ||
| 121 | value of the watchdog_device will be changed to the value that was just used | ||
| 122 | to re-program the watchdog timer device. | ||
| 123 | (Note: the WDIOF_SETTIMEOUT needs to be set in the options field of the | ||
| 124 | watchdog's info structure). | ||
| 125 | * ioctl: if this routine is present then it will be called first before we do | ||
| 126 | our own internal ioctl call handling. This routine should return -ENOIOCTLCMD | ||
| 127 | if a command is not supported. The parameters that are passed to the ioctl | ||
| 128 | call are: watchdog_device, cmd and arg. | ||
| 129 | |||
| 130 | The status bits should (preferably) be set with the set_bit and clear_bit alike | ||
| 131 | bit-operations. The status bits that are defined are: | ||
| 132 | * WDOG_ACTIVE: this status bit indicates whether or not a watchdog timer device | ||
| 133 | is active or not. When the watchdog is active after booting, then you should | ||
| 134 | set this status bit (Note: when you register the watchdog timer device with | ||
| 135 | this bit set, then opening /dev/watchdog will skip the start operation) | ||
| 136 | * WDOG_DEV_OPEN: this status bit shows whether or not the watchdog device | ||
| 137 | was opened via /dev/watchdog. | ||
| 138 | (This bit should only be used by the WatchDog Timer Driver Core). | ||
| 139 | * WDOG_ALLOW_RELEASE: this bit stores whether or not the magic close character | ||
| 140 | has been sent (so that we can support the magic close feature). | ||
| 141 | (This bit should only be used by the WatchDog Timer Driver Core). | ||
| 142 | * WDOG_NO_WAY_OUT: this bit stores the nowayout setting for the watchdog. | ||
| 143 | If this bit is set then the watchdog timer will not be able to stop. | ||
| 144 | |||
| 145 | Note: The WatchDog Timer Driver Core supports the magic close feature and | ||
| 146 | the nowayout feature. To use the magic close feature you must set the | ||
| 147 | WDIOF_MAGICCLOSE bit in the options field of the watchdog's info structure. | ||
| 148 | The nowayout feature will overrule the magic close feature. | ||
| 149 | |||
| 150 | To get or set driver specific data the following two helper functions should be | ||
| 151 | used: | ||
| 152 | |||
| 153 | static inline void watchdog_set_drvdata(struct watchdog_device *wdd, void *data) | ||
| 154 | static inline void *watchdog_get_drvdata(struct watchdog_device *wdd) | ||
| 155 | |||
| 156 | The watchdog_set_drvdata function allows you to add driver specific data. The | ||
| 157 | arguments of this function are the watchdog device where you want to add the | ||
| 158 | driver specific data to and a pointer to the data itself. | ||
| 159 | |||
| 160 | The watchdog_get_drvdata function allows you to retrieve driver specific data. | ||
| 161 | The argument of this function is the watchdog device where you want to retrieve | ||
| 162 | data from. The function retruns the pointer to the driver specific data. | ||
diff --git a/drivers/watchdog/Kconfig b/drivers/watchdog/Kconfig index 21d816e9dfa5..f441726ddf2b 100644 --- a/drivers/watchdog/Kconfig +++ b/drivers/watchdog/Kconfig | |||
| @@ -28,6 +28,17 @@ menuconfig WATCHDOG | |||
| 28 | 28 | ||
| 29 | if WATCHDOG | 29 | if WATCHDOG |
| 30 | 30 | ||
| 31 | config WATCHDOG_CORE | ||
| 32 | bool "WatchDog Timer Driver Core" | ||
| 33 | ---help--- | ||
| 34 | Say Y here if you want to use the new watchdog timer driver core. | ||
| 35 | This driver provides a framework for all watchdog timer drivers | ||
| 36 | and gives them the /dev/watchdog interface (and later also the | ||
| 37 | sysfs interface). | ||
| 38 | |||
| 39 | To compile this driver as a module, choose M here: the module will | ||
| 40 | be called watchdog. | ||
| 41 | |||
| 31 | config WATCHDOG_NOWAYOUT | 42 | config WATCHDOG_NOWAYOUT |
| 32 | bool "Disable watchdog shutdown on close" | 43 | bool "Disable watchdog shutdown on close" |
| 33 | help | 44 | help |
| @@ -186,6 +197,15 @@ config SA1100_WATCHDOG | |||
| 186 | To compile this driver as a module, choose M here: the | 197 | To compile this driver as a module, choose M here: the |
| 187 | module will be called sa1100_wdt. | 198 | module will be called sa1100_wdt. |
| 188 | 199 | ||
| 200 | config DW_WATCHDOG | ||
| 201 | tristate "Synopsys DesignWare watchdog" | ||
| 202 | depends on ARM && HAVE_CLK | ||
| 203 | help | ||
| 204 | Say Y here if to include support for the Synopsys DesignWare | ||
| 205 | watchdog timer found in many ARM chips. | ||
| 206 | To compile this driver as a module, choose M here: the | ||
| 207 | module will be called dw_wdt. | ||
| 208 | |||
| 189 | config MPCORE_WATCHDOG | 209 | config MPCORE_WATCHDOG |
| 190 | tristate "MPcore watchdog" | 210 | tristate "MPcore watchdog" |
| 191 | depends on HAVE_ARM_TWD | 211 | depends on HAVE_ARM_TWD |
| @@ -321,7 +341,7 @@ config MAX63XX_WATCHDOG | |||
| 321 | 341 | ||
| 322 | config IMX2_WDT | 342 | config IMX2_WDT |
| 323 | tristate "IMX2+ Watchdog" | 343 | tristate "IMX2+ Watchdog" |
| 324 | depends on ARCH_MX2 || ARCH_MX25 || ARCH_MX3 || ARCH_MX5 | 344 | depends on IMX_HAVE_PLATFORM_IMX2_WDT |
| 325 | help | 345 | help |
| 326 | This is the driver for the hardware watchdog | 346 | This is the driver for the hardware watchdog |
| 327 | on the Freescale IMX2 and later processors. | 347 | on the Freescale IMX2 and later processors. |
| @@ -879,6 +899,20 @@ config M54xx_WATCHDOG | |||
| 879 | To compile this driver as a module, choose M here: the | 899 | To compile this driver as a module, choose M here: the |
| 880 | module will be called m54xx_wdt. | 900 | module will be called m54xx_wdt. |
| 881 | 901 | ||
| 902 | # MicroBlaze Architecture | ||
| 903 | |||
| 904 | config XILINX_WATCHDOG | ||
| 905 | tristate "Xilinx Watchdog timer" | ||
| 906 | depends on MICROBLAZE | ||
| 907 | ---help--- | ||
| 908 | Watchdog driver for the xps_timebase_wdt ip core. | ||
| 909 | |||
| 910 | IMPORTANT: The xps_timebase_wdt parent must have the property | ||
| 911 | "clock-frequency" at device tree. | ||
| 912 | |||
| 913 | To compile this driver as a module, choose M here: the | ||
| 914 | module will be called of_xilinx_wdt. | ||
| 915 | |||
| 882 | # MIPS Architecture | 916 | # MIPS Architecture |
| 883 | 917 | ||
| 884 | config ATH79_WDT | 918 | config ATH79_WDT |
diff --git a/drivers/watchdog/Makefile b/drivers/watchdog/Makefile index ed26f7094e47..55bd5740e910 100644 --- a/drivers/watchdog/Makefile +++ b/drivers/watchdog/Makefile | |||
| @@ -2,6 +2,10 @@ | |||
| 2 | # Makefile for the WatchDog device drivers. | 2 | # Makefile for the WatchDog device drivers. |
| 3 | # | 3 | # |
| 4 | 4 | ||
| 5 | # The WatchDog Timer Driver Core. | ||
| 6 | watchdog-objs += watchdog_core.o watchdog_dev.o | ||
| 7 | obj-$(CONFIG_WATCHDOG_CORE) += watchdog.o | ||
| 8 | |||
| 5 | # Only one watchdog can succeed. We probe the ISA/PCI/USB based | 9 | # Only one watchdog can succeed. We probe the ISA/PCI/USB based |
| 6 | # watchdog-cards first, then the architecture specific watchdog | 10 | # watchdog-cards first, then the architecture specific watchdog |
| 7 | # drivers and then the architecture independent "softdog" driver. | 11 | # drivers and then the architecture independent "softdog" driver. |
| @@ -37,6 +41,7 @@ obj-$(CONFIG_IXP4XX_WATCHDOG) += ixp4xx_wdt.o | |||
| 37 | obj-$(CONFIG_KS8695_WATCHDOG) += ks8695_wdt.o | 41 | obj-$(CONFIG_KS8695_WATCHDOG) += ks8695_wdt.o |
| 38 | obj-$(CONFIG_S3C2410_WATCHDOG) += s3c2410_wdt.o | 42 | obj-$(CONFIG_S3C2410_WATCHDOG) += s3c2410_wdt.o |
| 39 | obj-$(CONFIG_SA1100_WATCHDOG) += sa1100_wdt.o | 43 | obj-$(CONFIG_SA1100_WATCHDOG) += sa1100_wdt.o |
| 44 | obj-$(CONFIG_DW_WATCHDOG) += dw_wdt.o | ||
| 40 | obj-$(CONFIG_MPCORE_WATCHDOG) += mpcore_wdt.o | 45 | obj-$(CONFIG_MPCORE_WATCHDOG) += mpcore_wdt.o |
| 41 | obj-$(CONFIG_EP93XX_WATCHDOG) += ep93xx_wdt.o | 46 | obj-$(CONFIG_EP93XX_WATCHDOG) += ep93xx_wdt.o |
| 42 | obj-$(CONFIG_PNX4008_WATCHDOG) += pnx4008_wdt.o | 47 | obj-$(CONFIG_PNX4008_WATCHDOG) += pnx4008_wdt.o |
| @@ -109,6 +114,9 @@ obj-$(CONFIG_INTEL_SCU_WATCHDOG) += intel_scu_watchdog.o | |||
| 109 | # M68K Architecture | 114 | # M68K Architecture |
| 110 | obj-$(CONFIG_M54xx_WATCHDOG) += m54xx_wdt.o | 115 | obj-$(CONFIG_M54xx_WATCHDOG) += m54xx_wdt.o |
| 111 | 116 | ||
| 117 | # MicroBlaze Architecture | ||
| 118 | obj-$(CONFIG_XILINX_WATCHDOG) += of_xilinx_wdt.o | ||
| 119 | |||
| 112 | # MIPS Architecture | 120 | # MIPS Architecture |
| 113 | obj-$(CONFIG_ATH79_WDT) += ath79_wdt.o | 121 | obj-$(CONFIG_ATH79_WDT) += ath79_wdt.o |
| 114 | obj-$(CONFIG_BCM47XX_WDT) += bcm47xx_wdt.o | 122 | obj-$(CONFIG_BCM47XX_WDT) += bcm47xx_wdt.o |
diff --git a/drivers/watchdog/at91sam9_wdt.c b/drivers/watchdog/at91sam9_wdt.c index eac26021e8da..87445b2d72a7 100644 --- a/drivers/watchdog/at91sam9_wdt.c +++ b/drivers/watchdog/at91sam9_wdt.c | |||
| @@ -31,7 +31,7 @@ | |||
| 31 | #include <linux/bitops.h> | 31 | #include <linux/bitops.h> |
| 32 | #include <linux/uaccess.h> | 32 | #include <linux/uaccess.h> |
| 33 | 33 | ||
| 34 | #include <mach/at91_wdt.h> | 34 | #include "at91sam9_wdt.h" |
| 35 | 35 | ||
| 36 | #define DRV_NAME "AT91SAM9 Watchdog" | 36 | #define DRV_NAME "AT91SAM9 Watchdog" |
| 37 | 37 | ||
| @@ -284,27 +284,8 @@ static int __exit at91wdt_remove(struct platform_device *pdev) | |||
| 284 | return res; | 284 | return res; |
| 285 | } | 285 | } |
| 286 | 286 | ||
| 287 | #ifdef CONFIG_PM | ||
| 288 | |||
| 289 | static int at91wdt_suspend(struct platform_device *pdev, pm_message_t message) | ||
| 290 | { | ||
| 291 | return 0; | ||
| 292 | } | ||
| 293 | |||
| 294 | static int at91wdt_resume(struct platform_device *pdev) | ||
| 295 | { | ||
| 296 | return 0; | ||
| 297 | } | ||
| 298 | |||
| 299 | #else | ||
| 300 | #define at91wdt_suspend NULL | ||
| 301 | #define at91wdt_resume NULL | ||
| 302 | #endif | ||
| 303 | |||
| 304 | static struct platform_driver at91wdt_driver = { | 287 | static struct platform_driver at91wdt_driver = { |
| 305 | .remove = __exit_p(at91wdt_remove), | 288 | .remove = __exit_p(at91wdt_remove), |
| 306 | .suspend = at91wdt_suspend, | ||
| 307 | .resume = at91wdt_resume, | ||
| 308 | .driver = { | 289 | .driver = { |
| 309 | .name = "at91_wdt", | 290 | .name = "at91_wdt", |
| 310 | .owner = THIS_MODULE, | 291 | .owner = THIS_MODULE, |
diff --git a/arch/arm/mach-at91/include/mach/at91_wdt.h b/drivers/watchdog/at91sam9_wdt.h index fecc2e9f0ca8..757f9cab5c82 100644 --- a/arch/arm/mach-at91/include/mach/at91_wdt.h +++ b/drivers/watchdog/at91sam9_wdt.h | |||
| @@ -1,5 +1,5 @@ | |||
| 1 | /* | 1 | /* |
| 2 | * arch/arm/mach-at91/include/mach/at91_wdt.h | 2 | * drivers/watchdog/at91sam9_wdt.h |
| 3 | * | 3 | * |
| 4 | * Copyright (C) 2007 Andrew Victor | 4 | * Copyright (C) 2007 Andrew Victor |
| 5 | * Copyright (C) 2007 Atmel Corporation. | 5 | * Copyright (C) 2007 Atmel Corporation. |
diff --git a/drivers/watchdog/dw_wdt.c b/drivers/watchdog/dw_wdt.c new file mode 100644 index 000000000000..f10f8c0abba4 --- /dev/null +++ b/drivers/watchdog/dw_wdt.c | |||
| @@ -0,0 +1,376 @@ | |||
| 1 | /* | ||
| 2 | * Copyright 2010-2011 Picochip Ltd., Jamie Iles | ||
| 3 | * http://www.picochip.com | ||
| 4 | * | ||
| 5 | * This program is free software; you can redistribute it and/or | ||
| 6 | * modify it under the terms of the GNU General Public License | ||
| 7 | * as published by the Free Software Foundation; either version | ||
| 8 | * 2 of the License, or (at your option) any later version. | ||
| 9 | * | ||
| 10 | * This file implements a driver for the Synopsys DesignWare watchdog device | ||
| 11 | * in the many ARM subsystems. The watchdog has 16 different timeout periods | ||
| 12 | * and these are a function of the input clock frequency. | ||
| 13 | * | ||
| 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. | ||
| 16 | * If we receive an expected close for the watchdog then we keep the timer | ||
| 17 | * running, otherwise the timer is stopped and the watchdog will expire. | ||
| 18 | */ | ||
| 19 | #define pr_fmt(fmt) "dw_wdt: " fmt | ||
| 20 | |||
| 21 | #include <linux/bitops.h> | ||
| 22 | #include <linux/clk.h> | ||
| 23 | #include <linux/device.h> | ||
| 24 | #include <linux/err.h> | ||
| 25 | #include <linux/fs.h> | ||
| 26 | #include <linux/io.h> | ||
| 27 | #include <linux/kernel.h> | ||
| 28 | #include <linux/miscdevice.h> | ||
| 29 | #include <linux/module.h> | ||
| 30 | #include <linux/moduleparam.h> | ||
| 31 | #include <linux/pm.h> | ||
| 32 | #include <linux/platform_device.h> | ||
| 33 | #include <linux/spinlock.h> | ||
| 34 | #include <linux/timer.h> | ||
| 35 | #include <linux/uaccess.h> | ||
| 36 | #include <linux/watchdog.h> | ||
| 37 | |||
| 38 | #define WDOG_CONTROL_REG_OFFSET 0x00 | ||
| 39 | #define WDOG_CONTROL_REG_WDT_EN_MASK 0x01 | ||
| 40 | #define WDOG_TIMEOUT_RANGE_REG_OFFSET 0x04 | ||
| 41 | #define WDOG_CURRENT_COUNT_REG_OFFSET 0x08 | ||
| 42 | #define WDOG_COUNTER_RESTART_REG_OFFSET 0x0c | ||
| 43 | #define WDOG_COUNTER_RESTART_KICK_VALUE 0x76 | ||
| 44 | |||
| 45 | /* The maximum TOP (timeout period) value that can be set in the watchdog. */ | ||
| 46 | #define DW_WDT_MAX_TOP 15 | ||
| 47 | |||
| 48 | static int nowayout = WATCHDOG_NOWAYOUT; | ||
| 49 | module_param(nowayout, int, 0); | ||
| 50 | MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started " | ||
| 51 | "(default=" __MODULE_STRING(WATCHDOG_NOWAYOUT) ")"); | ||
| 52 | |||
| 53 | #define WDT_TIMEOUT (HZ / 2) | ||
| 54 | |||
| 55 | static struct { | ||
| 56 | spinlock_t lock; | ||
| 57 | void __iomem *regs; | ||
| 58 | struct clk *clk; | ||
| 59 | unsigned long in_use; | ||
| 60 | unsigned long next_heartbeat; | ||
| 61 | struct timer_list timer; | ||
| 62 | int expect_close; | ||
| 63 | } dw_wdt; | ||
| 64 | |||
| 65 | static inline int dw_wdt_is_enabled(void) | ||
| 66 | { | ||
| 67 | return readl(dw_wdt.regs + WDOG_CONTROL_REG_OFFSET) & | ||
| 68 | WDOG_CONTROL_REG_WDT_EN_MASK; | ||
| 69 | } | ||
| 70 | |||
| 71 | static inline int dw_wdt_top_in_seconds(unsigned top) | ||
| 72 | { | ||
| 73 | /* | ||
| 74 | * There are 16 possible timeout values in 0..15 where the number of | ||
| 75 | * cycles is 2 ^ (16 + i) and the watchdog counts down. | ||
| 76 | */ | ||
| 77 | return (1 << (16 + top)) / clk_get_rate(dw_wdt.clk); | ||
| 78 | } | ||
| 79 | |||
| 80 | static int dw_wdt_get_top(void) | ||
| 81 | { | ||
| 82 | int top = readl(dw_wdt.regs + WDOG_TIMEOUT_RANGE_REG_OFFSET) & 0xF; | ||
| 83 | |||
| 84 | return dw_wdt_top_in_seconds(top); | ||
| 85 | } | ||
| 86 | |||
| 87 | static inline void dw_wdt_set_next_heartbeat(void) | ||
| 88 | { | ||
| 89 | dw_wdt.next_heartbeat = jiffies + dw_wdt_get_top() * HZ; | ||
| 90 | } | ||
| 91 | |||
| 92 | static int dw_wdt_set_top(unsigned top_s) | ||
| 93 | { | ||
| 94 | int i, top_val = DW_WDT_MAX_TOP; | ||
| 95 | |||
| 96 | /* | ||
| 97 | * Iterate over the timeout values until we find the closest match. We | ||
| 98 | * always look for >=. | ||
| 99 | */ | ||
| 100 | for (i = 0; i <= DW_WDT_MAX_TOP; ++i) | ||
| 101 | if (dw_wdt_top_in_seconds(i) >= top_s) { | ||
| 102 | top_val = i; | ||
| 103 | break; | ||
| 104 | } | ||
| 105 | |||
| 106 | /* Set the new value in the watchdog. */ | ||
| 107 | writel(top_val, dw_wdt.regs + WDOG_TIMEOUT_RANGE_REG_OFFSET); | ||
| 108 | |||
| 109 | dw_wdt_set_next_heartbeat(); | ||
| 110 | |||
| 111 | return dw_wdt_top_in_seconds(top_val); | ||
| 112 | } | ||
| 113 | |||
| 114 | static void dw_wdt_keepalive(void) | ||
| 115 | { | ||
| 116 | writel(WDOG_COUNTER_RESTART_KICK_VALUE, dw_wdt.regs + | ||
| 117 | WDOG_COUNTER_RESTART_REG_OFFSET); | ||
| 118 | } | ||
| 119 | |||
| 120 | static void dw_wdt_ping(unsigned long data) | ||
| 121 | { | ||
| 122 | if (time_before(jiffies, dw_wdt.next_heartbeat) || | ||
| 123 | (!nowayout && !dw_wdt.in_use)) { | ||
| 124 | dw_wdt_keepalive(); | ||
| 125 | mod_timer(&dw_wdt.timer, jiffies + WDT_TIMEOUT); | ||
| 126 | } else | ||
| 127 | pr_crit("keepalive missed, machine will reset\n"); | ||
| 128 | } | ||
| 129 | |||
| 130 | static int dw_wdt_open(struct inode *inode, struct file *filp) | ||
| 131 | { | ||
| 132 | if (test_and_set_bit(0, &dw_wdt.in_use)) | ||
| 133 | return -EBUSY; | ||
| 134 | |||
| 135 | /* Make sure we don't get unloaded. */ | ||
| 136 | __module_get(THIS_MODULE); | ||
| 137 | |||
| 138 | spin_lock(&dw_wdt.lock); | ||
| 139 | if (!dw_wdt_is_enabled()) { | ||
| 140 | /* | ||
| 141 | * The watchdog is not currently enabled. Set the timeout to | ||
| 142 | * the maximum and then start it. | ||
| 143 | */ | ||
| 144 | dw_wdt_set_top(DW_WDT_MAX_TOP); | ||
| 145 | writel(WDOG_CONTROL_REG_WDT_EN_MASK, | ||
| 146 | dw_wdt.regs + WDOG_CONTROL_REG_OFFSET); | ||
| 147 | } | ||
| 148 | |||
| 149 | dw_wdt_set_next_heartbeat(); | ||
| 150 | |||
| 151 | spin_unlock(&dw_wdt.lock); | ||
| 152 | |||
| 153 | return nonseekable_open(inode, filp); | ||
| 154 | } | ||
| 155 | |||
| 156 | ssize_t dw_wdt_write(struct file *filp, const char __user *buf, size_t len, | ||
| 157 | loff_t *offset) | ||
| 158 | { | ||
| 159 | if (!len) | ||
| 160 | return 0; | ||
| 161 | |||
| 162 | if (!nowayout) { | ||
| 163 | size_t i; | ||
| 164 | |||
| 165 | dw_wdt.expect_close = 0; | ||
| 166 | |||
| 167 | for (i = 0; i < len; ++i) { | ||
| 168 | char c; | ||
| 169 | |||
| 170 | if (get_user(c, buf + i)) | ||
| 171 | return -EFAULT; | ||
| 172 | |||
| 173 | if (c == 'V') { | ||
| 174 | dw_wdt.expect_close = 1; | ||
| 175 | break; | ||
| 176 | } | ||
| 177 | } | ||
| 178 | } | ||
| 179 | |||
| 180 | dw_wdt_set_next_heartbeat(); | ||
| 181 | mod_timer(&dw_wdt.timer, jiffies + WDT_TIMEOUT); | ||
| 182 | |||
| 183 | return len; | ||
| 184 | } | ||
| 185 | |||
| 186 | static u32 dw_wdt_time_left(void) | ||
| 187 | { | ||
| 188 | return readl(dw_wdt.regs + WDOG_CURRENT_COUNT_REG_OFFSET) / | ||
| 189 | clk_get_rate(dw_wdt.clk); | ||
| 190 | } | ||
| 191 | |||
| 192 | static const struct watchdog_info dw_wdt_ident = { | ||
| 193 | .options = WDIOF_KEEPALIVEPING | WDIOF_SETTIMEOUT | | ||
| 194 | WDIOF_MAGICCLOSE, | ||
| 195 | .identity = "Synopsys DesignWare Watchdog", | ||
| 196 | }; | ||
| 197 | |||
| 198 | static long dw_wdt_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) | ||
| 199 | { | ||
| 200 | unsigned long val; | ||
| 201 | int timeout; | ||
| 202 | |||
| 203 | switch (cmd) { | ||
| 204 | case WDIOC_GETSUPPORT: | ||
| 205 | return copy_to_user((struct watchdog_info *)arg, &dw_wdt_ident, | ||
| 206 | sizeof(dw_wdt_ident)) ? -EFAULT : 0; | ||
| 207 | |||
| 208 | case WDIOC_GETSTATUS: | ||
| 209 | case WDIOC_GETBOOTSTATUS: | ||
| 210 | return put_user(0, (int *)arg); | ||
| 211 | |||
| 212 | case WDIOC_KEEPALIVE: | ||
| 213 | dw_wdt_set_next_heartbeat(); | ||
| 214 | return 0; | ||
| 215 | |||
| 216 | case WDIOC_SETTIMEOUT: | ||
| 217 | if (get_user(val, (int __user *)arg)) | ||
| 218 | return -EFAULT; | ||
| 219 | timeout = dw_wdt_set_top(val); | ||
| 220 | return put_user(timeout , (int __user *)arg); | ||
| 221 | |||
| 222 | case WDIOC_GETTIMEOUT: | ||
| 223 | return put_user(dw_wdt_get_top(), (int __user *)arg); | ||
| 224 | |||
| 225 | case WDIOC_GETTIMELEFT: | ||
| 226 | /* Get the time left until expiry. */ | ||
| 227 | if (get_user(val, (int __user *)arg)) | ||
| 228 | return -EFAULT; | ||
| 229 | return put_user(dw_wdt_time_left(), (int __user *)arg); | ||
| 230 | |||
| 231 | default: | ||
| 232 | return -ENOTTY; | ||
| 233 | } | ||
| 234 | } | ||
| 235 | |||
| 236 | static int dw_wdt_release(struct inode *inode, struct file *filp) | ||
| 237 | { | ||
| 238 | clear_bit(0, &dw_wdt.in_use); | ||
| 239 | |||
| 240 | if (!dw_wdt.expect_close) { | ||
| 241 | del_timer(&dw_wdt.timer); | ||
| 242 | |||
| 243 | if (!nowayout) | ||
| 244 | pr_crit("unexpected close, system will reboot soon\n"); | ||
| 245 | else | ||
| 246 | pr_crit("watchdog cannot be disabled, system will reboot soon\n"); | ||
| 247 | } | ||
| 248 | |||
| 249 | dw_wdt.expect_close = 0; | ||
| 250 | |||
| 251 | return 0; | ||
| 252 | } | ||
| 253 | |||
| 254 | #ifdef CONFIG_PM | ||
| 255 | static int dw_wdt_suspend(struct device *dev) | ||
| 256 | { | ||
| 257 | clk_disable(dw_wdt.clk); | ||
| 258 | |||
| 259 | return 0; | ||
| 260 | } | ||
| 261 | |||
| 262 | static int dw_wdt_resume(struct device *dev) | ||
| 263 | { | ||
| 264 | int err = clk_enable(dw_wdt.clk); | ||
| 265 | |||
| 266 | if (err) | ||
| 267 | return err; | ||
| 268 | |||
| 269 | dw_wdt_keepalive(); | ||
| 270 | |||
| 271 | return 0; | ||
| 272 | } | ||
| 273 | |||
| 274 | static const struct dev_pm_ops dw_wdt_pm_ops = { | ||
| 275 | .suspend = dw_wdt_suspend, | ||
| 276 | .resume = dw_wdt_resume, | ||
| 277 | }; | ||
| 278 | #endif /* CONFIG_PM */ | ||
| 279 | |||
| 280 | static const struct file_operations wdt_fops = { | ||
| 281 | .owner = THIS_MODULE, | ||
| 282 | .llseek = no_llseek, | ||
| 283 | .open = dw_wdt_open, | ||
| 284 | .write = dw_wdt_write, | ||
| 285 | .unlocked_ioctl = dw_wdt_ioctl, | ||
| 286 | .release = dw_wdt_release | ||
| 287 | }; | ||
| 288 | |||
| 289 | static struct miscdevice dw_wdt_miscdev = { | ||
| 290 | .fops = &wdt_fops, | ||
| 291 | .name = "watchdog", | ||
| 292 | .minor = WATCHDOG_MINOR, | ||
| 293 | }; | ||
| 294 | |||
| 295 | static int __devinit dw_wdt_drv_probe(struct platform_device *pdev) | ||
| 296 | { | ||
| 297 | int ret; | ||
| 298 | struct resource *mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); | ||
| 299 | |||
| 300 | if (!mem) | ||
| 301 | return -EINVAL; | ||
| 302 | |||
| 303 | if (!devm_request_mem_region(&pdev->dev, mem->start, resource_size(mem), | ||
| 304 | "dw_wdt")) | ||
| 305 | return -ENOMEM; | ||
| 306 | |||
| 307 | dw_wdt.regs = devm_ioremap(&pdev->dev, mem->start, resource_size(mem)); | ||
| 308 | if (!dw_wdt.regs) | ||
| 309 | return -ENOMEM; | ||
| 310 | |||
| 311 | dw_wdt.clk = clk_get(&pdev->dev, NULL); | ||
| 312 | if (IS_ERR(dw_wdt.clk)) | ||
| 313 | return PTR_ERR(dw_wdt.clk); | ||
| 314 | |||
| 315 | ret = clk_enable(dw_wdt.clk); | ||
| 316 | if (ret) | ||
| 317 | goto out_put_clk; | ||
| 318 | |||
| 319 | spin_lock_init(&dw_wdt.lock); | ||
| 320 | |||
| 321 | ret = misc_register(&dw_wdt_miscdev); | ||
| 322 | if (ret) | ||
| 323 | goto out_disable_clk; | ||
| 324 | |||
| 325 | dw_wdt_set_next_heartbeat(); | ||
| 326 | setup_timer(&dw_wdt.timer, dw_wdt_ping, 0); | ||
| 327 | mod_timer(&dw_wdt.timer, jiffies + WDT_TIMEOUT); | ||
| 328 | |||
| 329 | return 0; | ||
| 330 | |||
| 331 | out_disable_clk: | ||
| 332 | clk_disable(dw_wdt.clk); | ||
| 333 | out_put_clk: | ||
| 334 | clk_put(dw_wdt.clk); | ||
| 335 | |||
| 336 | return ret; | ||
| 337 | } | ||
| 338 | |||
| 339 | static int __devexit dw_wdt_drv_remove(struct platform_device *pdev) | ||
| 340 | { | ||
| 341 | misc_deregister(&dw_wdt_miscdev); | ||
| 342 | |||
| 343 | clk_disable(dw_wdt.clk); | ||
| 344 | clk_put(dw_wdt.clk); | ||
| 345 | |||
| 346 | return 0; | ||
| 347 | } | ||
| 348 | |||
| 349 | static struct platform_driver dw_wdt_driver = { | ||
| 350 | .probe = dw_wdt_drv_probe, | ||
| 351 | .remove = __devexit_p(dw_wdt_drv_remove), | ||
| 352 | .driver = { | ||
| 353 | .name = "dw_wdt", | ||
| 354 | .owner = THIS_MODULE, | ||
| 355 | #ifdef CONFIG_PM | ||
| 356 | .pm = &dw_wdt_pm_ops, | ||
| 357 | #endif /* CONFIG_PM */ | ||
| 358 | }, | ||
| 359 | }; | ||
| 360 | |||
| 361 | static int __init dw_wdt_watchdog_init(void) | ||
| 362 | { | ||
| 363 | return platform_driver_register(&dw_wdt_driver); | ||
| 364 | } | ||
| 365 | module_init(dw_wdt_watchdog_init); | ||
| 366 | |||
| 367 | static void __exit dw_wdt_watchdog_exit(void) | ||
| 368 | { | ||
| 369 | platform_driver_unregister(&dw_wdt_driver); | ||
| 370 | } | ||
| 371 | module_exit(dw_wdt_watchdog_exit); | ||
| 372 | |||
| 373 | MODULE_AUTHOR("Jamie Iles"); | ||
| 374 | MODULE_DESCRIPTION("Synopsys DesignWare Watchdog Driver"); | ||
| 375 | MODULE_LICENSE("GPL"); | ||
| 376 | MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR); | ||
diff --git a/drivers/watchdog/hpwdt.c b/drivers/watchdog/hpwdt.c index 8cb26855bfed..410fba45378d 100644 --- a/drivers/watchdog/hpwdt.c +++ b/drivers/watchdog/hpwdt.c | |||
| @@ -36,7 +36,7 @@ | |||
| 36 | #include <asm/cacheflush.h> | 36 | #include <asm/cacheflush.h> |
| 37 | #endif /* CONFIG_HPWDT_NMI_DECODING */ | 37 | #endif /* CONFIG_HPWDT_NMI_DECODING */ |
| 38 | 38 | ||
| 39 | #define HPWDT_VERSION "1.2.0" | 39 | #define HPWDT_VERSION "1.3.0" |
| 40 | #define SECS_TO_TICKS(secs) ((secs) * 1000 / 128) | 40 | #define SECS_TO_TICKS(secs) ((secs) * 1000 / 128) |
| 41 | #define TICKS_TO_SECS(ticks) ((ticks) * 128 / 1000) | 41 | #define TICKS_TO_SECS(ticks) ((ticks) * 128 / 1000) |
| 42 | #define HPWDT_MAX_TIMER TICKS_TO_SECS(65535) | 42 | #define HPWDT_MAX_TIMER TICKS_TO_SECS(65535) |
| @@ -87,6 +87,19 @@ struct smbios_cru64_info { | |||
| 87 | }; | 87 | }; |
| 88 | #define SMBIOS_CRU64_INFORMATION 212 | 88 | #define SMBIOS_CRU64_INFORMATION 212 |
| 89 | 89 | ||
| 90 | /* type 219 */ | ||
| 91 | struct smbios_proliant_info { | ||
| 92 | u8 type; | ||
| 93 | u8 byte_length; | ||
| 94 | u16 handle; | ||
| 95 | u32 power_features; | ||
| 96 | u32 omega_features; | ||
| 97 | u32 reserved; | ||
| 98 | u32 misc_features; | ||
| 99 | }; | ||
| 100 | #define SMBIOS_ICRU_INFORMATION 219 | ||
| 101 | |||
| 102 | |||
| 90 | struct cmn_registers { | 103 | struct cmn_registers { |
| 91 | union { | 104 | union { |
| 92 | struct { | 105 | struct { |
| @@ -132,6 +145,7 @@ struct cmn_registers { | |||
| 132 | static unsigned int hpwdt_nmi_decoding; | 145 | static unsigned int hpwdt_nmi_decoding; |
| 133 | static unsigned int allow_kdump; | 146 | static unsigned int allow_kdump; |
| 134 | static unsigned int priority; /* hpwdt at end of die_notify list */ | 147 | static unsigned int priority; /* hpwdt at end of die_notify list */ |
| 148 | static unsigned int is_icru; | ||
| 135 | static DEFINE_SPINLOCK(rom_lock); | 149 | static DEFINE_SPINLOCK(rom_lock); |
| 136 | static void *cru_rom_addr; | 150 | static void *cru_rom_addr; |
| 137 | static struct cmn_registers cmn_regs; | 151 | static struct cmn_registers cmn_regs; |
| @@ -476,19 +490,22 @@ static int hpwdt_pretimeout(struct notifier_block *nb, unsigned long ulReason, | |||
| 476 | goto out; | 490 | goto out; |
| 477 | 491 | ||
| 478 | spin_lock_irqsave(&rom_lock, rom_pl); | 492 | spin_lock_irqsave(&rom_lock, rom_pl); |
| 479 | if (!die_nmi_called) | 493 | if (!die_nmi_called && !is_icru) |
| 480 | asminline_call(&cmn_regs, cru_rom_addr); | 494 | asminline_call(&cmn_regs, cru_rom_addr); |
| 481 | die_nmi_called = 1; | 495 | die_nmi_called = 1; |
| 482 | spin_unlock_irqrestore(&rom_lock, rom_pl); | 496 | spin_unlock_irqrestore(&rom_lock, rom_pl); |
| 483 | if (cmn_regs.u1.ral == 0) { | 497 | if (!is_icru) { |
| 484 | printk(KERN_WARNING "hpwdt: An NMI occurred, " | 498 | if (cmn_regs.u1.ral == 0) { |
| 485 | "but unable to determine source.\n"); | 499 | printk(KERN_WARNING "hpwdt: An NMI occurred, " |
| 486 | } else { | 500 | "but unable to determine source.\n"); |
| 487 | if (allow_kdump) | 501 | } |
| 488 | hpwdt_stop(); | ||
| 489 | panic("An NMI occurred, please see the Integrated " | ||
| 490 | "Management Log for details.\n"); | ||
| 491 | } | 502 | } |
| 503 | |||
| 504 | if (allow_kdump) | ||
| 505 | hpwdt_stop(); | ||
| 506 | panic("An NMI occurred, please see the Integrated " | ||
| 507 | "Management Log for details.\n"); | ||
| 508 | |||
| 492 | out: | 509 | out: |
| 493 | return NOTIFY_OK; | 510 | return NOTIFY_OK; |
| 494 | } | 511 | } |
| @@ -659,30 +676,63 @@ static void __devinit hpwdt_check_nmi_decoding(struct pci_dev *dev) | |||
| 659 | } | 676 | } |
| 660 | #endif /* CONFIG_X86_LOCAL_APIC */ | 677 | #endif /* CONFIG_X86_LOCAL_APIC */ |
| 661 | 678 | ||
| 679 | /* | ||
| 680 | * dmi_find_icru | ||
| 681 | * | ||
| 682 | * Routine Description: | ||
| 683 | * This function checks whether or not we are on an iCRU-based server. | ||
| 684 | * This check is independent of architecture and needs to be made for | ||
| 685 | * any ProLiant system. | ||
| 686 | */ | ||
| 687 | static void __devinit dmi_find_icru(const struct dmi_header *dm, void *dummy) | ||
| 688 | { | ||
| 689 | struct smbios_proliant_info *smbios_proliant_ptr; | ||
| 690 | |||
| 691 | if (dm->type == SMBIOS_ICRU_INFORMATION) { | ||
| 692 | smbios_proliant_ptr = (struct smbios_proliant_info *) dm; | ||
| 693 | if (smbios_proliant_ptr->misc_features & 0x01) | ||
| 694 | is_icru = 1; | ||
| 695 | } | ||
| 696 | } | ||
| 697 | |||
| 662 | static int __devinit hpwdt_init_nmi_decoding(struct pci_dev *dev) | 698 | static int __devinit hpwdt_init_nmi_decoding(struct pci_dev *dev) |
| 663 | { | 699 | { |
| 664 | int retval; | 700 | int retval; |
| 665 | 701 | ||
| 666 | /* | 702 | /* |
| 667 | * We need to map the ROM to get the CRU service. | 703 | * On typical CRU-based systems we need to map that service in |
| 668 | * For 32 bit Operating Systems we need to go through the 32 Bit | 704 | * the BIOS. For 32 bit Operating Systems we need to go through |
| 669 | * BIOS Service Directory | 705 | * the 32 Bit BIOS Service Directory. For 64 bit Operating |
| 670 | * For 64 bit Operating Systems we get that service through SMBIOS. | 706 | * Systems we get that service through SMBIOS. |
| 707 | * | ||
| 708 | * On systems that support the new iCRU service all we need to | ||
| 709 | * do is call dmi_walk to get the supported flag value and skip | ||
| 710 | * the old cru detect code. | ||
| 671 | */ | 711 | */ |
| 672 | retval = detect_cru_service(); | 712 | dmi_walk(dmi_find_icru, NULL); |
| 673 | if (retval < 0) { | 713 | if (!is_icru) { |
| 674 | dev_warn(&dev->dev, | 714 | |
| 675 | "Unable to detect the %d Bit CRU Service.\n", | 715 | /* |
| 676 | HPWDT_ARCH); | 716 | * We need to map the ROM to get the CRU service. |
| 677 | return retval; | 717 | * For 32 bit Operating Systems we need to go through the 32 Bit |
| 678 | } | 718 | * BIOS Service Directory |
| 719 | * For 64 bit Operating Systems we get that service through SMBIOS. | ||
| 720 | */ | ||
| 721 | retval = detect_cru_service(); | ||
| 722 | if (retval < 0) { | ||
| 723 | dev_warn(&dev->dev, | ||
| 724 | "Unable to detect the %d Bit CRU Service.\n", | ||
| 725 | HPWDT_ARCH); | ||
| 726 | return retval; | ||
| 727 | } | ||
| 679 | 728 | ||
| 680 | /* | 729 | /* |
| 681 | * We know this is the only CRU call we need to make so lets keep as | 730 | * We know this is the only CRU call we need to make so lets keep as |
| 682 | * few instructions as possible once the NMI comes in. | 731 | * few instructions as possible once the NMI comes in. |
| 683 | */ | 732 | */ |
| 684 | cmn_regs.u1.rah = 0x0D; | 733 | cmn_regs.u1.rah = 0x0D; |
| 685 | cmn_regs.u1.ral = 0x02; | 734 | cmn_regs.u1.ral = 0x02; |
| 735 | } | ||
| 686 | 736 | ||
| 687 | /* | 737 | /* |
| 688 | * If the priority is set to 1, then we will be put first on the | 738 | * If the priority is set to 1, then we will be put first on the |
diff --git a/drivers/watchdog/iTCO_wdt.c b/drivers/watchdog/iTCO_wdt.c index 5fd020da7c55..751a591684da 100644 --- a/drivers/watchdog/iTCO_wdt.c +++ b/drivers/watchdog/iTCO_wdt.c | |||
| @@ -120,72 +120,12 @@ enum iTCO_chipsets { | |||
| 120 | TCO_3420, /* 3420 */ | 120 | TCO_3420, /* 3420 */ |
| 121 | TCO_3450, /* 3450 */ | 121 | TCO_3450, /* 3450 */ |
| 122 | TCO_EP80579, /* EP80579 */ | 122 | TCO_EP80579, /* EP80579 */ |
| 123 | TCO_CPT1, /* Cougar Point */ | 123 | TCO_CPT, /* Cougar Point */ |
| 124 | TCO_CPT2, /* Cougar Point Desktop */ | 124 | TCO_CPTD, /* Cougar Point Desktop */ |
| 125 | TCO_CPT3, /* Cougar Point Mobile */ | 125 | TCO_CPTM, /* Cougar Point Mobile */ |
| 126 | TCO_CPT4, /* Cougar Point */ | 126 | TCO_PBG, /* Patsburg */ |
| 127 | TCO_CPT5, /* Cougar Point */ | ||
| 128 | TCO_CPT6, /* Cougar Point */ | ||
| 129 | TCO_CPT7, /* Cougar Point */ | ||
| 130 | TCO_CPT8, /* Cougar Point */ | ||
| 131 | TCO_CPT9, /* Cougar Point */ | ||
| 132 | TCO_CPT10, /* Cougar Point */ | ||
| 133 | TCO_CPT11, /* Cougar Point */ | ||
| 134 | TCO_CPT12, /* Cougar Point */ | ||
| 135 | TCO_CPT13, /* Cougar Point */ | ||
| 136 | TCO_CPT14, /* Cougar Point */ | ||
| 137 | TCO_CPT15, /* Cougar Point */ | ||
| 138 | TCO_CPT16, /* Cougar Point */ | ||
| 139 | TCO_CPT17, /* Cougar Point */ | ||
| 140 | TCO_CPT18, /* Cougar Point */ | ||
| 141 | TCO_CPT19, /* Cougar Point */ | ||
| 142 | TCO_CPT20, /* Cougar Point */ | ||
| 143 | TCO_CPT21, /* Cougar Point */ | ||
| 144 | TCO_CPT22, /* Cougar Point */ | ||
| 145 | TCO_CPT23, /* Cougar Point */ | ||
| 146 | TCO_CPT24, /* Cougar Point */ | ||
| 147 | TCO_CPT25, /* Cougar Point */ | ||
| 148 | TCO_CPT26, /* Cougar Point */ | ||
| 149 | TCO_CPT27, /* Cougar Point */ | ||
| 150 | TCO_CPT28, /* Cougar Point */ | ||
| 151 | TCO_CPT29, /* Cougar Point */ | ||
| 152 | TCO_CPT30, /* Cougar Point */ | ||
| 153 | TCO_CPT31, /* Cougar Point */ | ||
| 154 | TCO_PBG1, /* Patsburg */ | ||
| 155 | TCO_PBG2, /* Patsburg */ | ||
| 156 | TCO_DH89XXCC, /* DH89xxCC */ | 127 | TCO_DH89XXCC, /* DH89xxCC */ |
| 157 | TCO_PPT0, /* Panther Point */ | 128 | TCO_PPT, /* Panther Point */ |
| 158 | TCO_PPT1, /* Panther Point */ | ||
| 159 | TCO_PPT2, /* Panther Point */ | ||
| 160 | TCO_PPT3, /* Panther Point */ | ||
| 161 | TCO_PPT4, /* Panther Point */ | ||
| 162 | TCO_PPT5, /* Panther Point */ | ||
| 163 | TCO_PPT6, /* Panther Point */ | ||
| 164 | TCO_PPT7, /* Panther Point */ | ||
| 165 | TCO_PPT8, /* Panther Point */ | ||
| 166 | TCO_PPT9, /* Panther Point */ | ||
| 167 | TCO_PPT10, /* Panther Point */ | ||
| 168 | TCO_PPT11, /* Panther Point */ | ||
| 169 | TCO_PPT12, /* Panther Point */ | ||
| 170 | TCO_PPT13, /* Panther Point */ | ||
| 171 | TCO_PPT14, /* Panther Point */ | ||
| 172 | TCO_PPT15, /* Panther Point */ | ||
| 173 | TCO_PPT16, /* Panther Point */ | ||
| 174 | TCO_PPT17, /* Panther Point */ | ||
| 175 | TCO_PPT18, /* Panther Point */ | ||
| 176 | TCO_PPT19, /* Panther Point */ | ||
| 177 | TCO_PPT20, /* Panther Point */ | ||
| 178 | TCO_PPT21, /* Panther Point */ | ||
| 179 | TCO_PPT22, /* Panther Point */ | ||
| 180 | TCO_PPT23, /* Panther Point */ | ||
| 181 | TCO_PPT24, /* Panther Point */ | ||
| 182 | TCO_PPT25, /* Panther Point */ | ||
| 183 | TCO_PPT26, /* Panther Point */ | ||
| 184 | TCO_PPT27, /* Panther Point */ | ||
| 185 | TCO_PPT28, /* Panther Point */ | ||
| 186 | TCO_PPT29, /* Panther Point */ | ||
| 187 | TCO_PPT30, /* Panther Point */ | ||
| 188 | TCO_PPT31, /* Panther Point */ | ||
| 189 | }; | 129 | }; |
| 190 | 130 | ||
| 191 | static struct { | 131 | static struct { |
| @@ -244,83 +184,14 @@ static struct { | |||
| 244 | {"3450", 2}, | 184 | {"3450", 2}, |
| 245 | {"EP80579", 2}, | 185 | {"EP80579", 2}, |
| 246 | {"Cougar Point", 2}, | 186 | {"Cougar Point", 2}, |
| 247 | {"Cougar Point", 2}, | 187 | {"Cougar Point Desktop", 2}, |
| 248 | {"Cougar Point", 2}, | 188 | {"Cougar Point Mobile", 2}, |
| 249 | {"Cougar Point", 2}, | ||
| 250 | {"Cougar Point", 2}, | ||
| 251 | {"Cougar Point", 2}, | ||
| 252 | {"Cougar Point", 2}, | ||
| 253 | {"Cougar Point", 2}, | ||
| 254 | {"Cougar Point", 2}, | ||
| 255 | {"Cougar Point", 2}, | ||
| 256 | {"Cougar Point", 2}, | ||
| 257 | {"Cougar Point", 2}, | ||
| 258 | {"Cougar Point", 2}, | ||
| 259 | {"Cougar Point", 2}, | ||
| 260 | {"Cougar Point", 2}, | ||
| 261 | {"Cougar Point", 2}, | ||
| 262 | {"Cougar Point", 2}, | ||
| 263 | {"Cougar Point", 2}, | ||
| 264 | {"Cougar Point", 2}, | ||
| 265 | {"Cougar Point", 2}, | ||
| 266 | {"Cougar Point", 2}, | ||
| 267 | {"Cougar Point", 2}, | ||
| 268 | {"Cougar Point", 2}, | ||
| 269 | {"Cougar Point", 2}, | ||
| 270 | {"Cougar Point", 2}, | ||
| 271 | {"Cougar Point", 2}, | ||
| 272 | {"Cougar Point", 2}, | ||
| 273 | {"Cougar Point", 2}, | ||
| 274 | {"Cougar Point", 2}, | ||
| 275 | {"Cougar Point", 2}, | ||
| 276 | {"Cougar Point", 2}, | ||
| 277 | {"Patsburg", 2}, | ||
| 278 | {"Patsburg", 2}, | 189 | {"Patsburg", 2}, |
| 279 | {"DH89xxCC", 2}, | 190 | {"DH89xxCC", 2}, |
| 280 | {"Panther Point", 2}, | 191 | {"Panther Point", 2}, |
| 281 | {"Panther Point", 2}, | ||
| 282 | {"Panther Point", 2}, | ||
| 283 | {"Panther Point", 2}, | ||
| 284 | {"Panther Point", 2}, | ||
| 285 | {"Panther Point", 2}, | ||
| 286 | {"Panther Point", 2}, | ||
| 287 | {"Panther Point", 2}, | ||
| 288 | {"Panther Point", 2}, | ||
| 289 | {"Panther Point", 2}, | ||
| 290 | {"Panther Point", 2}, | ||
| 291 | {"Panther Point", 2}, | ||
| 292 | {"Panther Point", 2}, | ||
| 293 | {"Panther Point", 2}, | ||
| 294 | {"Panther Point", 2}, | ||
| 295 | {"Panther Point", 2}, | ||
| 296 | {"Panther Point", 2}, | ||
| 297 | {"Panther Point", 2}, | ||
| 298 | {"Panther Point", 2}, | ||
| 299 | {"Panther Point", 2}, | ||
| 300 | {"Panther Point", 2}, | ||
| 301 | {"Panther Point", 2}, | ||
| 302 | {"Panther Point", 2}, | ||
| 303 | {"Panther Point", 2}, | ||
| 304 | {"Panther Point", 2}, | ||
| 305 | {"Panther Point", 2}, | ||
| 306 | {"Panther Point", 2}, | ||
| 307 | {"Panther Point", 2}, | ||
| 308 | {"Panther Point", 2}, | ||
| 309 | {"Panther Point", 2}, | ||
| 310 | {"Panther Point", 2}, | ||
| 311 | {"Panther Point", 2}, | ||
| 312 | {NULL, 0} | 192 | {NULL, 0} |
| 313 | }; | 193 | }; |
| 314 | 194 | ||
| 315 | #define ITCO_PCI_DEVICE(dev, data) \ | ||
| 316 | .vendor = PCI_VENDOR_ID_INTEL, \ | ||
| 317 | .device = dev, \ | ||
| 318 | .subvendor = PCI_ANY_ID, \ | ||
| 319 | .subdevice = PCI_ANY_ID, \ | ||
| 320 | .class = 0, \ | ||
| 321 | .class_mask = 0, \ | ||
| 322 | .driver_data = data | ||
| 323 | |||
| 324 | /* | 195 | /* |
| 325 | * This data only exists for exporting the supported PCI ids | 196 | * This data only exists for exporting the supported PCI ids |
| 326 | * via MODULE_DEVICE_TABLE. We do not actually register a | 197 | * via MODULE_DEVICE_TABLE. We do not actually register a |
| @@ -328,138 +199,138 @@ static struct { | |||
| 328 | * functions that probably will be registered by other drivers. | 199 | * functions that probably will be registered by other drivers. |
| 329 | */ | 200 | */ |
| 330 | static DEFINE_PCI_DEVICE_TABLE(iTCO_wdt_pci_tbl) = { | 201 | static DEFINE_PCI_DEVICE_TABLE(iTCO_wdt_pci_tbl) = { |
| 331 | { ITCO_PCI_DEVICE(PCI_DEVICE_ID_INTEL_82801AA_0, TCO_ICH)}, | 202 | { PCI_VDEVICE(INTEL, 0x2410), TCO_ICH}, |
| 332 | { ITCO_PCI_DEVICE(PCI_DEVICE_ID_INTEL_82801AB_0, TCO_ICH0)}, | 203 | { PCI_VDEVICE(INTEL, 0x2420), TCO_ICH0}, |
| 333 | { ITCO_PCI_DEVICE(PCI_DEVICE_ID_INTEL_82801BA_0, TCO_ICH2)}, | 204 | { PCI_VDEVICE(INTEL, 0x2440), TCO_ICH2}, |
| 334 | { ITCO_PCI_DEVICE(PCI_DEVICE_ID_INTEL_82801BA_10, TCO_ICH2M)}, | 205 | { PCI_VDEVICE(INTEL, 0x244c), TCO_ICH2M}, |
| 335 | { ITCO_PCI_DEVICE(PCI_DEVICE_ID_INTEL_82801CA_0, TCO_ICH3)}, | 206 | { PCI_VDEVICE(INTEL, 0x2480), TCO_ICH3}, |
| 336 | { ITCO_PCI_DEVICE(PCI_DEVICE_ID_INTEL_82801CA_12, TCO_ICH3M)}, | 207 | { PCI_VDEVICE(INTEL, 0x248c), TCO_ICH3M}, |
| 337 | { ITCO_PCI_DEVICE(PCI_DEVICE_ID_INTEL_82801DB_0, TCO_ICH4)}, | 208 | { PCI_VDEVICE(INTEL, 0x24c0), TCO_ICH4}, |
| 338 | { ITCO_PCI_DEVICE(PCI_DEVICE_ID_INTEL_82801DB_12, TCO_ICH4M)}, | 209 | { PCI_VDEVICE(INTEL, 0x24cc), TCO_ICH4M}, |
| 339 | { ITCO_PCI_DEVICE(PCI_DEVICE_ID_INTEL_82801E_0, TCO_CICH)}, | 210 | { PCI_VDEVICE(INTEL, 0x2450), TCO_CICH}, |
| 340 | { ITCO_PCI_DEVICE(PCI_DEVICE_ID_INTEL_82801EB_0, TCO_ICH5)}, | 211 | { PCI_VDEVICE(INTEL, 0x24d0), TCO_ICH5}, |
| 341 | { ITCO_PCI_DEVICE(PCI_DEVICE_ID_INTEL_ESB_1, TCO_6300ESB)}, | 212 | { PCI_VDEVICE(INTEL, 0x25a1), TCO_6300ESB}, |
| 342 | { ITCO_PCI_DEVICE(PCI_DEVICE_ID_INTEL_ICH6_0, TCO_ICH6)}, | 213 | { PCI_VDEVICE(INTEL, 0x2640), TCO_ICH6}, |
| 343 | { ITCO_PCI_DEVICE(PCI_DEVICE_ID_INTEL_ICH6_1, TCO_ICH6M)}, | 214 | { PCI_VDEVICE(INTEL, 0x2641), TCO_ICH6M}, |
| 344 | { ITCO_PCI_DEVICE(PCI_DEVICE_ID_INTEL_ICH6_2, TCO_ICH6W)}, | 215 | { PCI_VDEVICE(INTEL, 0x2642), TCO_ICH6W}, |
| 345 | { ITCO_PCI_DEVICE(PCI_DEVICE_ID_INTEL_ESB2_0, TCO_631XESB)}, | 216 | { PCI_VDEVICE(INTEL, 0x2670), TCO_631XESB}, |
| 346 | { ITCO_PCI_DEVICE(0x2671, TCO_631XESB)}, | 217 | { PCI_VDEVICE(INTEL, 0x2671), TCO_631XESB}, |
| 347 | { ITCO_PCI_DEVICE(0x2672, TCO_631XESB)}, | 218 | { PCI_VDEVICE(INTEL, 0x2672), TCO_631XESB}, |
| 348 | { ITCO_PCI_DEVICE(0x2673, TCO_631XESB)}, | 219 | { PCI_VDEVICE(INTEL, 0x2673), TCO_631XESB}, |
| 349 | { ITCO_PCI_DEVICE(0x2674, TCO_631XESB)}, | 220 | { PCI_VDEVICE(INTEL, 0x2674), TCO_631XESB}, |
| 350 | { ITCO_PCI_DEVICE(0x2675, TCO_631XESB)}, | 221 | { PCI_VDEVICE(INTEL, 0x2675), TCO_631XESB}, |
| 351 | { ITCO_PCI_DEVICE(0x2676, TCO_631XESB)}, | 222 | { PCI_VDEVICE(INTEL, 0x2676), TCO_631XESB}, |
| 352 | { ITCO_PCI_DEVICE(0x2677, TCO_631XESB)}, | 223 | { PCI_VDEVICE(INTEL, 0x2677), TCO_631XESB}, |
| 353 | { ITCO_PCI_DEVICE(0x2678, TCO_631XESB)}, | 224 | { PCI_VDEVICE(INTEL, 0x2678), TCO_631XESB}, |
| 354 | { ITCO_PCI_DEVICE(0x2679, TCO_631XESB)}, | 225 | { PCI_VDEVICE(INTEL, 0x2679), TCO_631XESB}, |
| 355 | { ITCO_PCI_DEVICE(0x267a, TCO_631XESB)}, | 226 | { PCI_VDEVICE(INTEL, 0x267a), TCO_631XESB}, |
| 356 | { ITCO_PCI_DEVICE(0x267b, TCO_631XESB)}, | 227 | { PCI_VDEVICE(INTEL, 0x267b), TCO_631XESB}, |
| 357 | { ITCO_PCI_DEVICE(0x267c, TCO_631XESB)}, | 228 | { PCI_VDEVICE(INTEL, 0x267c), TCO_631XESB}, |
| 358 | { ITCO_PCI_DEVICE(0x267d, TCO_631XESB)}, | 229 | { PCI_VDEVICE(INTEL, 0x267d), TCO_631XESB}, |
| 359 | { ITCO_PCI_DEVICE(0x267e, TCO_631XESB)}, | 230 | { PCI_VDEVICE(INTEL, 0x267e), TCO_631XESB}, |
| 360 | { ITCO_PCI_DEVICE(0x267f, TCO_631XESB)}, | 231 | { PCI_VDEVICE(INTEL, 0x267f), TCO_631XESB}, |
| 361 | { ITCO_PCI_DEVICE(PCI_DEVICE_ID_INTEL_ICH7_0, TCO_ICH7)}, | 232 | { PCI_VDEVICE(INTEL, 0x27b8), TCO_ICH7}, |
| 362 | { ITCO_PCI_DEVICE(PCI_DEVICE_ID_INTEL_ICH7_30, TCO_ICH7DH)}, | 233 | { PCI_VDEVICE(INTEL, 0x27b0), TCO_ICH7DH}, |
| 363 | { ITCO_PCI_DEVICE(PCI_DEVICE_ID_INTEL_ICH7_1, TCO_ICH7M)}, | 234 | { PCI_VDEVICE(INTEL, 0x27b9), TCO_ICH7M}, |
| 364 | { ITCO_PCI_DEVICE(PCI_DEVICE_ID_INTEL_ICH7_31, TCO_ICH7MDH)}, | 235 | { PCI_VDEVICE(INTEL, 0x27bd), TCO_ICH7MDH}, |
| 365 | { ITCO_PCI_DEVICE(0x27bc, TCO_NM10)}, | 236 | { PCI_VDEVICE(INTEL, 0x27bc), TCO_NM10}, |
| 366 | { ITCO_PCI_DEVICE(PCI_DEVICE_ID_INTEL_ICH8_0, TCO_ICH8)}, | 237 | { PCI_VDEVICE(INTEL, 0x2810), TCO_ICH8}, |
| 367 | { ITCO_PCI_DEVICE(PCI_DEVICE_ID_INTEL_ICH8_2, TCO_ICH8DH)}, | 238 | { PCI_VDEVICE(INTEL, 0x2812), TCO_ICH8DH}, |
| 368 | { ITCO_PCI_DEVICE(PCI_DEVICE_ID_INTEL_ICH8_3, TCO_ICH8DO)}, | 239 | { PCI_VDEVICE(INTEL, 0x2814), TCO_ICH8DO}, |
| 369 | { ITCO_PCI_DEVICE(PCI_DEVICE_ID_INTEL_ICH8_4, TCO_ICH8M)}, | 240 | { PCI_VDEVICE(INTEL, 0x2815), TCO_ICH8M}, |
| 370 | { ITCO_PCI_DEVICE(PCI_DEVICE_ID_INTEL_ICH8_1, TCO_ICH8ME)}, | 241 | { PCI_VDEVICE(INTEL, 0x2811), TCO_ICH8ME}, |
| 371 | { ITCO_PCI_DEVICE(0x2918, TCO_ICH9)}, | 242 | { PCI_VDEVICE(INTEL, 0x2918), TCO_ICH9}, |
| 372 | { ITCO_PCI_DEVICE(0x2916, TCO_ICH9R)}, | 243 | { PCI_VDEVICE(INTEL, 0x2916), TCO_ICH9R}, |
| 373 | { ITCO_PCI_DEVICE(PCI_DEVICE_ID_INTEL_ICH9_2, TCO_ICH9DH)}, | 244 | { PCI_VDEVICE(INTEL, 0x2912), TCO_ICH9DH}, |
| 374 | { ITCO_PCI_DEVICE(PCI_DEVICE_ID_INTEL_ICH9_4, TCO_ICH9DO)}, | 245 | { PCI_VDEVICE(INTEL, 0x2914), TCO_ICH9DO}, |
| 375 | { ITCO_PCI_DEVICE(0x2919, TCO_ICH9M)}, | 246 | { PCI_VDEVICE(INTEL, 0x2919), TCO_ICH9M}, |
| 376 | { ITCO_PCI_DEVICE(0x2917, TCO_ICH9ME)}, | 247 | { PCI_VDEVICE(INTEL, 0x2917), TCO_ICH9ME}, |
| 377 | { ITCO_PCI_DEVICE(0x3a18, TCO_ICH10)}, | 248 | { PCI_VDEVICE(INTEL, 0x3a18), TCO_ICH10}, |
| 378 | { ITCO_PCI_DEVICE(0x3a16, TCO_ICH10R)}, | 249 | { PCI_VDEVICE(INTEL, 0x3a16), TCO_ICH10R}, |
| 379 | { ITCO_PCI_DEVICE(0x3a1a, TCO_ICH10D)}, | 250 | { PCI_VDEVICE(INTEL, 0x3a1a), TCO_ICH10D}, |
| 380 | { ITCO_PCI_DEVICE(0x3a14, TCO_ICH10DO)}, | 251 | { PCI_VDEVICE(INTEL, 0x3a14), TCO_ICH10DO}, |
| 381 | { ITCO_PCI_DEVICE(0x3b00, TCO_PCH)}, | 252 | { PCI_VDEVICE(INTEL, 0x3b00), TCO_PCH}, |
| 382 | { ITCO_PCI_DEVICE(0x3b01, TCO_PCHM)}, | 253 | { PCI_VDEVICE(INTEL, 0x3b01), TCO_PCHM}, |
| 383 | { ITCO_PCI_DEVICE(0x3b02, TCO_P55)}, | 254 | { PCI_VDEVICE(INTEL, 0x3b02), TCO_P55}, |
| 384 | { ITCO_PCI_DEVICE(0x3b03, TCO_PM55)}, | 255 | { PCI_VDEVICE(INTEL, 0x3b03), TCO_PM55}, |
| 385 | { ITCO_PCI_DEVICE(0x3b06, TCO_H55)}, | 256 | { PCI_VDEVICE(INTEL, 0x3b06), TCO_H55}, |
| 386 | { ITCO_PCI_DEVICE(0x3b07, TCO_QM57)}, | 257 | { PCI_VDEVICE(INTEL, 0x3b07), TCO_QM57}, |
| 387 | { ITCO_PCI_DEVICE(0x3b08, TCO_H57)}, | 258 | { PCI_VDEVICE(INTEL, 0x3b08), TCO_H57}, |
| 388 | { ITCO_PCI_DEVICE(0x3b09, TCO_HM55)}, | 259 | { PCI_VDEVICE(INTEL, 0x3b09), TCO_HM55}, |
| 389 | { ITCO_PCI_DEVICE(0x3b0a, TCO_Q57)}, | 260 | { PCI_VDEVICE(INTEL, 0x3b0a), TCO_Q57}, |
| 390 | { ITCO_PCI_DEVICE(0x3b0b, TCO_HM57)}, | 261 | { PCI_VDEVICE(INTEL, 0x3b0b), TCO_HM57}, |
| 391 | { ITCO_PCI_DEVICE(0x3b0d, TCO_PCHMSFF)}, | 262 | { PCI_VDEVICE(INTEL, 0x3b0d), TCO_PCHMSFF}, |
| 392 | { ITCO_PCI_DEVICE(0x3b0f, TCO_QS57)}, | 263 | { PCI_VDEVICE(INTEL, 0x3b0f), TCO_QS57}, |
| 393 | { ITCO_PCI_DEVICE(0x3b12, TCO_3400)}, | 264 | { PCI_VDEVICE(INTEL, 0x3b12), TCO_3400}, |
| 394 | { ITCO_PCI_DEVICE(0x3b14, TCO_3420)}, | 265 | { PCI_VDEVICE(INTEL, 0x3b14), TCO_3420}, |
| 395 | { ITCO_PCI_DEVICE(0x3b16, TCO_3450)}, | 266 | { PCI_VDEVICE(INTEL, 0x3b16), TCO_3450}, |
| 396 | { ITCO_PCI_DEVICE(0x5031, TCO_EP80579)}, | 267 | { PCI_VDEVICE(INTEL, 0x5031), TCO_EP80579}, |
| 397 | { ITCO_PCI_DEVICE(0x1c41, TCO_CPT1)}, | 268 | { PCI_VDEVICE(INTEL, 0x1c41), TCO_CPT}, |
| 398 | { ITCO_PCI_DEVICE(0x1c42, TCO_CPT2)}, | 269 | { PCI_VDEVICE(INTEL, 0x1c42), TCO_CPTD}, |
| 399 | { ITCO_PCI_DEVICE(0x1c43, TCO_CPT3)}, | 270 | { PCI_VDEVICE(INTEL, 0x1c43), TCO_CPTM}, |
| 400 | { ITCO_PCI_DEVICE(0x1c44, TCO_CPT4)}, | 271 | { PCI_VDEVICE(INTEL, 0x1c44), TCO_CPT}, |
| 401 | { ITCO_PCI_DEVICE(0x1c45, TCO_CPT5)}, | 272 | { PCI_VDEVICE(INTEL, 0x1c45), TCO_CPT}, |
| 402 | { ITCO_PCI_DEVICE(0x1c46, TCO_CPT6)}, | 273 | { PCI_VDEVICE(INTEL, 0x1c46), TCO_CPT}, |
| 403 | { ITCO_PCI_DEVICE(0x1c47, TCO_CPT7)}, | 274 | { PCI_VDEVICE(INTEL, 0x1c47), TCO_CPT}, |
| 404 | { ITCO_PCI_DEVICE(0x1c48, TCO_CPT8)}, | 275 | { PCI_VDEVICE(INTEL, 0x1c48), TCO_CPT}, |
| 405 | { ITCO_PCI_DEVICE(0x1c49, TCO_CPT9)}, | 276 | { PCI_VDEVICE(INTEL, 0x1c49), TCO_CPT}, |
| 406 | { ITCO_PCI_DEVICE(0x1c4a, TCO_CPT10)}, | 277 | { PCI_VDEVICE(INTEL, 0x1c4a), TCO_CPT}, |
| 407 | { ITCO_PCI_DEVICE(0x1c4b, TCO_CPT11)}, | 278 | { PCI_VDEVICE(INTEL, 0x1c4b), TCO_CPT}, |
| 408 | { ITCO_PCI_DEVICE(0x1c4c, TCO_CPT12)}, | 279 | { PCI_VDEVICE(INTEL, 0x1c4c), TCO_CPT}, |
| 409 | { ITCO_PCI_DEVICE(0x1c4d, TCO_CPT13)}, | 280 | { PCI_VDEVICE(INTEL, 0x1c4d), TCO_CPT}, |
| 410 | { ITCO_PCI_DEVICE(0x1c4e, TCO_CPT14)}, | 281 | { PCI_VDEVICE(INTEL, 0x1c4e), TCO_CPT}, |
| 411 | { ITCO_PCI_DEVICE(0x1c4f, TCO_CPT15)}, | 282 | { PCI_VDEVICE(INTEL, 0x1c4f), TCO_CPT}, |
| 412 | { ITCO_PCI_DEVICE(0x1c50, TCO_CPT16)}, | 283 | { PCI_VDEVICE(INTEL, 0x1c50), TCO_CPT}, |
| 413 | { ITCO_PCI_DEVICE(0x1c51, TCO_CPT17)}, | 284 | { PCI_VDEVICE(INTEL, 0x1c51), TCO_CPT}, |
| 414 | { ITCO_PCI_DEVICE(0x1c52, TCO_CPT18)}, | 285 | { PCI_VDEVICE(INTEL, 0x1c52), TCO_CPT}, |
| 415 | { ITCO_PCI_DEVICE(0x1c53, TCO_CPT19)}, | 286 | { PCI_VDEVICE(INTEL, 0x1c53), TCO_CPT}, |
| 416 | { ITCO_PCI_DEVICE(0x1c54, TCO_CPT20)}, | 287 | { PCI_VDEVICE(INTEL, 0x1c54), TCO_CPT}, |
| 417 | { ITCO_PCI_DEVICE(0x1c55, TCO_CPT21)}, | 288 | { PCI_VDEVICE(INTEL, 0x1c55), TCO_CPT}, |
| 418 | { ITCO_PCI_DEVICE(0x1c56, TCO_CPT22)}, | 289 | { PCI_VDEVICE(INTEL, 0x1c56), TCO_CPT}, |
| 419 | { ITCO_PCI_DEVICE(0x1c57, TCO_CPT23)}, | 290 | { PCI_VDEVICE(INTEL, 0x1c57), TCO_CPT}, |
| 420 | { ITCO_PCI_DEVICE(0x1c58, TCO_CPT24)}, | 291 | { PCI_VDEVICE(INTEL, 0x1c58), TCO_CPT}, |
| 421 | { ITCO_PCI_DEVICE(0x1c59, TCO_CPT25)}, | 292 | { PCI_VDEVICE(INTEL, 0x1c59), TCO_CPT}, |
| 422 | { ITCO_PCI_DEVICE(0x1c5a, TCO_CPT26)}, | 293 | { PCI_VDEVICE(INTEL, 0x1c5a), TCO_CPT}, |
| 423 | { ITCO_PCI_DEVICE(0x1c5b, TCO_CPT27)}, | 294 | { PCI_VDEVICE(INTEL, 0x1c5b), TCO_CPT}, |
| 424 | { ITCO_PCI_DEVICE(0x1c5c, TCO_CPT28)}, | 295 | { PCI_VDEVICE(INTEL, 0x1c5c), TCO_CPT}, |
| 425 | { ITCO_PCI_DEVICE(0x1c5d, TCO_CPT29)}, | 296 | { PCI_VDEVICE(INTEL, 0x1c5d), TCO_CPT}, |
| 426 | { ITCO_PCI_DEVICE(0x1c5e, TCO_CPT30)}, | 297 | { PCI_VDEVICE(INTEL, 0x1c5e), TCO_CPT}, |
| 427 | { ITCO_PCI_DEVICE(0x1c5f, TCO_CPT31)}, | 298 | { PCI_VDEVICE(INTEL, 0x1c5f), TCO_CPT}, |
| 428 | { ITCO_PCI_DEVICE(0x1d40, TCO_PBG1)}, | 299 | { PCI_VDEVICE(INTEL, 0x1d40), TCO_PBG}, |
| 429 | { ITCO_PCI_DEVICE(0x1d41, TCO_PBG2)}, | 300 | { PCI_VDEVICE(INTEL, 0x1d41), TCO_PBG}, |
| 430 | { ITCO_PCI_DEVICE(0x2310, TCO_DH89XXCC)}, | 301 | { PCI_VDEVICE(INTEL, 0x2310), TCO_DH89XXCC}, |
| 431 | { ITCO_PCI_DEVICE(0x1e40, TCO_PPT0)}, | 302 | { PCI_VDEVICE(INTEL, 0x1e40), TCO_PPT}, |
| 432 | { ITCO_PCI_DEVICE(0x1e41, TCO_PPT1)}, | 303 | { PCI_VDEVICE(INTEL, 0x1e41), TCO_PPT}, |
| 433 | { ITCO_PCI_DEVICE(0x1e42, TCO_PPT2)}, | 304 | { PCI_VDEVICE(INTEL, 0x1e42), TCO_PPT}, |
| 434 | { ITCO_PCI_DEVICE(0x1e43, TCO_PPT3)}, | 305 | { PCI_VDEVICE(INTEL, 0x1e43), TCO_PPT}, |
| 435 | { ITCO_PCI_DEVICE(0x1e44, TCO_PPT4)}, | 306 | { PCI_VDEVICE(INTEL, 0x1e44), TCO_PPT}, |
| 436 | { ITCO_PCI_DEVICE(0x1e45, TCO_PPT5)}, | 307 | { PCI_VDEVICE(INTEL, 0x1e45), TCO_PPT}, |
| 437 | { ITCO_PCI_DEVICE(0x1e46, TCO_PPT6)}, | 308 | { PCI_VDEVICE(INTEL, 0x1e46), TCO_PPT}, |
| 438 | { ITCO_PCI_DEVICE(0x1e47, TCO_PPT7)}, | 309 | { PCI_VDEVICE(INTEL, 0x1e47), TCO_PPT}, |
| 439 | { ITCO_PCI_DEVICE(0x1e48, TCO_PPT8)}, | 310 | { PCI_VDEVICE(INTEL, 0x1e48), TCO_PPT}, |
| 440 | { ITCO_PCI_DEVICE(0x1e49, TCO_PPT9)}, | 311 | { PCI_VDEVICE(INTEL, 0x1e49), TCO_PPT}, |
| 441 | { ITCO_PCI_DEVICE(0x1e4a, TCO_PPT10)}, | 312 | { PCI_VDEVICE(INTEL, 0x1e4a), TCO_PPT}, |
| 442 | { ITCO_PCI_DEVICE(0x1e4b, TCO_PPT11)}, | 313 | { PCI_VDEVICE(INTEL, 0x1e4b), TCO_PPT}, |
| 443 | { ITCO_PCI_DEVICE(0x1e4c, TCO_PPT12)}, | 314 | { PCI_VDEVICE(INTEL, 0x1e4c), TCO_PPT}, |
| 444 | { ITCO_PCI_DEVICE(0x1e4d, TCO_PPT13)}, | 315 | { PCI_VDEVICE(INTEL, 0x1e4d), TCO_PPT}, |
| 445 | { ITCO_PCI_DEVICE(0x1e4e, TCO_PPT14)}, | 316 | { PCI_VDEVICE(INTEL, 0x1e4e), TCO_PPT}, |
| 446 | { ITCO_PCI_DEVICE(0x1e4f, TCO_PPT15)}, | 317 | { PCI_VDEVICE(INTEL, 0x1e4f), TCO_PPT}, |
| 447 | { ITCO_PCI_DEVICE(0x1e50, TCO_PPT16)}, | 318 | { PCI_VDEVICE(INTEL, 0x1e50), TCO_PPT}, |
| 448 | { ITCO_PCI_DEVICE(0x1e51, TCO_PPT17)}, | 319 | { PCI_VDEVICE(INTEL, 0x1e51), TCO_PPT}, |
| 449 | { ITCO_PCI_DEVICE(0x1e52, TCO_PPT18)}, | 320 | { PCI_VDEVICE(INTEL, 0x1e52), TCO_PPT}, |
| 450 | { ITCO_PCI_DEVICE(0x1e53, TCO_PPT19)}, | 321 | { PCI_VDEVICE(INTEL, 0x1e53), TCO_PPT}, |
| 451 | { ITCO_PCI_DEVICE(0x1e54, TCO_PPT20)}, | 322 | { PCI_VDEVICE(INTEL, 0x1e54), TCO_PPT}, |
| 452 | { ITCO_PCI_DEVICE(0x1e55, TCO_PPT21)}, | 323 | { PCI_VDEVICE(INTEL, 0x1e55), TCO_PPT}, |
| 453 | { ITCO_PCI_DEVICE(0x1e56, TCO_PPT22)}, | 324 | { PCI_VDEVICE(INTEL, 0x1e56), TCO_PPT}, |
| 454 | { ITCO_PCI_DEVICE(0x1e57, TCO_PPT23)}, | 325 | { PCI_VDEVICE(INTEL, 0x1e57), TCO_PPT}, |
| 455 | { ITCO_PCI_DEVICE(0x1e58, TCO_PPT24)}, | 326 | { PCI_VDEVICE(INTEL, 0x1e58), TCO_PPT}, |
| 456 | { ITCO_PCI_DEVICE(0x1e59, TCO_PPT25)}, | 327 | { PCI_VDEVICE(INTEL, 0x1e59), TCO_PPT}, |
| 457 | { ITCO_PCI_DEVICE(0x1e5a, TCO_PPT26)}, | 328 | { PCI_VDEVICE(INTEL, 0x1e5a), TCO_PPT}, |
| 458 | { ITCO_PCI_DEVICE(0x1e5b, TCO_PPT27)}, | 329 | { PCI_VDEVICE(INTEL, 0x1e5b), TCO_PPT}, |
| 459 | { ITCO_PCI_DEVICE(0x1e5c, TCO_PPT28)}, | 330 | { PCI_VDEVICE(INTEL, 0x1e5c), TCO_PPT}, |
| 460 | { ITCO_PCI_DEVICE(0x1e5d, TCO_PPT29)}, | 331 | { PCI_VDEVICE(INTEL, 0x1e5d), TCO_PPT}, |
| 461 | { ITCO_PCI_DEVICE(0x1e5e, TCO_PPT30)}, | 332 | { PCI_VDEVICE(INTEL, 0x1e5e), TCO_PPT}, |
| 462 | { ITCO_PCI_DEVICE(0x1e5f, TCO_PPT31)}, | 333 | { PCI_VDEVICE(INTEL, 0x1e5f), TCO_PPT}, |
| 463 | { 0, }, /* End of list */ | 334 | { 0, }, /* End of list */ |
| 464 | }; | 335 | }; |
| 465 | MODULE_DEVICE_TABLE(pci, iTCO_wdt_pci_tbl); | 336 | MODULE_DEVICE_TABLE(pci, iTCO_wdt_pci_tbl); |
| @@ -1052,15 +923,10 @@ static void iTCO_wdt_shutdown(struct platform_device *dev) | |||
| 1052 | iTCO_wdt_stop(); | 923 | iTCO_wdt_stop(); |
| 1053 | } | 924 | } |
| 1054 | 925 | ||
| 1055 | #define iTCO_wdt_suspend NULL | ||
| 1056 | #define iTCO_wdt_resume NULL | ||
| 1057 | |||
| 1058 | static struct platform_driver iTCO_wdt_driver = { | 926 | static struct platform_driver iTCO_wdt_driver = { |
| 1059 | .probe = iTCO_wdt_probe, | 927 | .probe = iTCO_wdt_probe, |
| 1060 | .remove = __devexit_p(iTCO_wdt_remove), | 928 | .remove = __devexit_p(iTCO_wdt_remove), |
| 1061 | .shutdown = iTCO_wdt_shutdown, | 929 | .shutdown = iTCO_wdt_shutdown, |
| 1062 | .suspend = iTCO_wdt_suspend, | ||
| 1063 | .resume = iTCO_wdt_resume, | ||
| 1064 | .driver = { | 930 | .driver = { |
| 1065 | .owner = THIS_MODULE, | 931 | .owner = THIS_MODULE, |
| 1066 | .name = DRV_NAME, | 932 | .name = DRV_NAME, |
diff --git a/drivers/watchdog/imx2_wdt.c b/drivers/watchdog/imx2_wdt.c index 86f7cac1026c..b8ef2c6dca7c 100644 --- a/drivers/watchdog/imx2_wdt.c +++ b/drivers/watchdog/imx2_wdt.c | |||
| @@ -329,12 +329,18 @@ static void imx2_wdt_shutdown(struct platform_device *pdev) | |||
| 329 | } | 329 | } |
| 330 | } | 330 | } |
| 331 | 331 | ||
| 332 | static const struct of_device_id imx2_wdt_dt_ids[] = { | ||
| 333 | { .compatible = "fsl,imx21-wdt", }, | ||
| 334 | { /* sentinel */ } | ||
| 335 | }; | ||
| 336 | |||
| 332 | static struct platform_driver imx2_wdt_driver = { | 337 | static struct platform_driver imx2_wdt_driver = { |
| 333 | .remove = __exit_p(imx2_wdt_remove), | 338 | .remove = __exit_p(imx2_wdt_remove), |
| 334 | .shutdown = imx2_wdt_shutdown, | 339 | .shutdown = imx2_wdt_shutdown, |
| 335 | .driver = { | 340 | .driver = { |
| 336 | .name = DRIVER_NAME, | 341 | .name = DRIVER_NAME, |
| 337 | .owner = THIS_MODULE, | 342 | .owner = THIS_MODULE, |
| 343 | .of_match_table = imx2_wdt_dt_ids, | ||
| 338 | }, | 344 | }, |
| 339 | }; | 345 | }; |
| 340 | 346 | ||
diff --git a/drivers/watchdog/it8712f_wdt.c b/drivers/watchdog/it8712f_wdt.c index 6143f52ba6b8..8d2d8502d3e8 100644 --- a/drivers/watchdog/it8712f_wdt.c +++ b/drivers/watchdog/it8712f_wdt.c | |||
| @@ -28,10 +28,10 @@ | |||
| 28 | #include <linux/notifier.h> | 28 | #include <linux/notifier.h> |
| 29 | #include <linux/reboot.h> | 29 | #include <linux/reboot.h> |
| 30 | #include <linux/fs.h> | 30 | #include <linux/fs.h> |
| 31 | #include <linux/pci.h> | ||
| 32 | #include <linux/spinlock.h> | 31 | #include <linux/spinlock.h> |
| 33 | #include <linux/uaccess.h> | 32 | #include <linux/uaccess.h> |
| 34 | #include <linux/io.h> | 33 | #include <linux/io.h> |
| 34 | #include <linux/ioport.h> | ||
| 35 | 35 | ||
| 36 | #define NAME "it8712f_wdt" | 36 | #define NAME "it8712f_wdt" |
| 37 | 37 | ||
| @@ -51,7 +51,6 @@ MODULE_PARM_DESC(nowayout, "Disable watchdog shutdown on close"); | |||
| 51 | 51 | ||
| 52 | static unsigned long wdt_open; | 52 | static unsigned long wdt_open; |
| 53 | static unsigned expect_close; | 53 | static unsigned expect_close; |
| 54 | static spinlock_t io_lock; | ||
| 55 | static unsigned char revision; | 54 | static unsigned char revision; |
| 56 | 55 | ||
| 57 | /* Dog Food address - We use the game port address */ | 56 | /* Dog Food address - We use the game port address */ |
| @@ -121,20 +120,26 @@ static inline void superio_select(int ldn) | |||
| 121 | outb(ldn, VAL); | 120 | outb(ldn, VAL); |
| 122 | } | 121 | } |
| 123 | 122 | ||
| 124 | static inline void superio_enter(void) | 123 | static inline int superio_enter(void) |
| 125 | { | 124 | { |
| 126 | spin_lock(&io_lock); | 125 | /* |
| 126 | * Try to reserve REG and REG + 1 for exclusive access. | ||
| 127 | */ | ||
| 128 | if (!request_muxed_region(REG, 2, NAME)) | ||
| 129 | return -EBUSY; | ||
| 130 | |||
| 127 | outb(0x87, REG); | 131 | outb(0x87, REG); |
| 128 | outb(0x01, REG); | 132 | outb(0x01, REG); |
| 129 | outb(0x55, REG); | 133 | outb(0x55, REG); |
| 130 | outb(0x55, REG); | 134 | outb(0x55, REG); |
| 135 | return 0; | ||
| 131 | } | 136 | } |
| 132 | 137 | ||
| 133 | static inline void superio_exit(void) | 138 | static inline void superio_exit(void) |
| 134 | { | 139 | { |
| 135 | outb(0x02, REG); | 140 | outb(0x02, REG); |
| 136 | outb(0x02, VAL); | 141 | outb(0x02, VAL); |
| 137 | spin_unlock(&io_lock); | 142 | release_region(REG, 2); |
| 138 | } | 143 | } |
| 139 | 144 | ||
| 140 | static inline void it8712f_wdt_ping(void) | 145 | static inline void it8712f_wdt_ping(void) |
| @@ -173,10 +178,13 @@ static int it8712f_wdt_get_status(void) | |||
| 173 | return 0; | 178 | return 0; |
| 174 | } | 179 | } |
| 175 | 180 | ||
| 176 | static void it8712f_wdt_enable(void) | 181 | static int it8712f_wdt_enable(void) |
| 177 | { | 182 | { |
| 183 | int ret = superio_enter(); | ||
| 184 | if (ret) | ||
| 185 | return ret; | ||
| 186 | |||
| 178 | printk(KERN_DEBUG NAME ": enabling watchdog timer\n"); | 187 | printk(KERN_DEBUG NAME ": enabling watchdog timer\n"); |
| 179 | superio_enter(); | ||
| 180 | superio_select(LDN_GPIO); | 188 | superio_select(LDN_GPIO); |
| 181 | 189 | ||
| 182 | superio_outb(wdt_control_reg, WDT_CONTROL); | 190 | superio_outb(wdt_control_reg, WDT_CONTROL); |
| @@ -186,13 +194,17 @@ static void it8712f_wdt_enable(void) | |||
| 186 | superio_exit(); | 194 | superio_exit(); |
| 187 | 195 | ||
| 188 | it8712f_wdt_ping(); | 196 | it8712f_wdt_ping(); |
| 197 | |||
| 198 | return 0; | ||
| 189 | } | 199 | } |
| 190 | 200 | ||
| 191 | static void it8712f_wdt_disable(void) | 201 | static int it8712f_wdt_disable(void) |
| 192 | { | 202 | { |
| 193 | printk(KERN_DEBUG NAME ": disabling watchdog timer\n"); | 203 | int ret = superio_enter(); |
| 204 | if (ret) | ||
| 205 | return ret; | ||
| 194 | 206 | ||
| 195 | superio_enter(); | 207 | printk(KERN_DEBUG NAME ": disabling watchdog timer\n"); |
| 196 | superio_select(LDN_GPIO); | 208 | superio_select(LDN_GPIO); |
| 197 | 209 | ||
| 198 | superio_outb(0, WDT_CONFIG); | 210 | superio_outb(0, WDT_CONFIG); |
| @@ -202,6 +214,7 @@ static void it8712f_wdt_disable(void) | |||
| 202 | superio_outb(0, WDT_TIMEOUT); | 214 | superio_outb(0, WDT_TIMEOUT); |
| 203 | 215 | ||
| 204 | superio_exit(); | 216 | superio_exit(); |
| 217 | return 0; | ||
| 205 | } | 218 | } |
| 206 | 219 | ||
| 207 | static int it8712f_wdt_notify(struct notifier_block *this, | 220 | static int it8712f_wdt_notify(struct notifier_block *this, |
| @@ -252,6 +265,7 @@ static long it8712f_wdt_ioctl(struct file *file, unsigned int cmd, | |||
| 252 | WDIOF_MAGICCLOSE, | 265 | WDIOF_MAGICCLOSE, |
| 253 | }; | 266 | }; |
| 254 | int value; | 267 | int value; |
| 268 | int ret; | ||
| 255 | 269 | ||
| 256 | switch (cmd) { | 270 | switch (cmd) { |
| 257 | case WDIOC_GETSUPPORT: | 271 | case WDIOC_GETSUPPORT: |
| @@ -259,7 +273,9 @@ static long it8712f_wdt_ioctl(struct file *file, unsigned int cmd, | |||
| 259 | return -EFAULT; | 273 | return -EFAULT; |
| 260 | return 0; | 274 | return 0; |
| 261 | case WDIOC_GETSTATUS: | 275 | case WDIOC_GETSTATUS: |
| 262 | superio_enter(); | 276 | ret = superio_enter(); |
| 277 | if (ret) | ||
| 278 | return ret; | ||
| 263 | superio_select(LDN_GPIO); | 279 | superio_select(LDN_GPIO); |
| 264 | 280 | ||
| 265 | value = it8712f_wdt_get_status(); | 281 | value = it8712f_wdt_get_status(); |
| @@ -280,7 +296,9 @@ static long it8712f_wdt_ioctl(struct file *file, unsigned int cmd, | |||
| 280 | if (value > (max_units * 60)) | 296 | if (value > (max_units * 60)) |
| 281 | return -EINVAL; | 297 | return -EINVAL; |
| 282 | margin = value; | 298 | margin = value; |
| 283 | superio_enter(); | 299 | ret = superio_enter(); |
| 300 | if (ret) | ||
| 301 | return ret; | ||
| 284 | superio_select(LDN_GPIO); | 302 | superio_select(LDN_GPIO); |
| 285 | 303 | ||
| 286 | it8712f_wdt_update_margin(); | 304 | it8712f_wdt_update_margin(); |
| @@ -299,10 +317,14 @@ static long it8712f_wdt_ioctl(struct file *file, unsigned int cmd, | |||
| 299 | 317 | ||
| 300 | static int it8712f_wdt_open(struct inode *inode, struct file *file) | 318 | static int it8712f_wdt_open(struct inode *inode, struct file *file) |
| 301 | { | 319 | { |
| 320 | int ret; | ||
| 302 | /* only allow one at a time */ | 321 | /* only allow one at a time */ |
| 303 | if (test_and_set_bit(0, &wdt_open)) | 322 | if (test_and_set_bit(0, &wdt_open)) |
| 304 | return -EBUSY; | 323 | return -EBUSY; |
| 305 | it8712f_wdt_enable(); | 324 | |
| 325 | ret = it8712f_wdt_enable(); | ||
| 326 | if (ret) | ||
| 327 | return ret; | ||
| 306 | return nonseekable_open(inode, file); | 328 | return nonseekable_open(inode, file); |
| 307 | } | 329 | } |
| 308 | 330 | ||
| @@ -313,7 +335,8 @@ static int it8712f_wdt_release(struct inode *inode, struct file *file) | |||
| 313 | ": watchdog device closed unexpectedly, will not" | 335 | ": watchdog device closed unexpectedly, will not" |
| 314 | " disable the watchdog timer\n"); | 336 | " disable the watchdog timer\n"); |
| 315 | } else if (!nowayout) { | 337 | } else if (!nowayout) { |
| 316 | it8712f_wdt_disable(); | 338 | if (it8712f_wdt_disable()) |
| 339 | printk(KERN_WARNING NAME "Watchdog disable failed\n"); | ||
| 317 | } | 340 | } |
| 318 | expect_close = 0; | 341 | expect_close = 0; |
| 319 | clear_bit(0, &wdt_open); | 342 | clear_bit(0, &wdt_open); |
| @@ -340,8 +363,10 @@ static int __init it8712f_wdt_find(unsigned short *address) | |||
| 340 | { | 363 | { |
| 341 | int err = -ENODEV; | 364 | int err = -ENODEV; |
| 342 | int chip_type; | 365 | int chip_type; |
| 366 | int ret = superio_enter(); | ||
| 367 | if (ret) | ||
| 368 | return ret; | ||
| 343 | 369 | ||
| 344 | superio_enter(); | ||
| 345 | chip_type = superio_inw(DEVID); | 370 | chip_type = superio_inw(DEVID); |
| 346 | if (chip_type != IT8712F_DEVID) | 371 | if (chip_type != IT8712F_DEVID) |
| 347 | goto exit; | 372 | goto exit; |
| @@ -382,8 +407,6 @@ static int __init it8712f_wdt_init(void) | |||
| 382 | { | 407 | { |
| 383 | int err = 0; | 408 | int err = 0; |
| 384 | 409 | ||
| 385 | spin_lock_init(&io_lock); | ||
| 386 | |||
| 387 | if (it8712f_wdt_find(&address)) | 410 | if (it8712f_wdt_find(&address)) |
| 388 | return -ENODEV; | 411 | return -ENODEV; |
| 389 | 412 | ||
| @@ -392,7 +415,11 @@ static int __init it8712f_wdt_init(void) | |||
| 392 | return -EBUSY; | 415 | return -EBUSY; |
| 393 | } | 416 | } |
| 394 | 417 | ||
| 395 | it8712f_wdt_disable(); | 418 | err = it8712f_wdt_disable(); |
| 419 | if (err) { | ||
| 420 | printk(KERN_ERR NAME ": unable to disable watchdog timer.\n"); | ||
| 421 | goto out; | ||
| 422 | } | ||
| 396 | 423 | ||
| 397 | err = register_reboot_notifier(&it8712f_wdt_notifier); | 424 | err = register_reboot_notifier(&it8712f_wdt_notifier); |
| 398 | if (err) { | 425 | if (err) { |
diff --git a/drivers/watchdog/it87_wdt.c b/drivers/watchdog/it87_wdt.c index b1bc72f9a209..a2d9a1266a23 100644 --- a/drivers/watchdog/it87_wdt.c +++ b/drivers/watchdog/it87_wdt.c | |||
| @@ -137,7 +137,6 @@ | |||
| 137 | 137 | ||
| 138 | static unsigned int base, gpact, ciract, max_units, chip_type; | 138 | static unsigned int base, gpact, ciract, max_units, chip_type; |
| 139 | static unsigned long wdt_status; | 139 | static unsigned long wdt_status; |
| 140 | static DEFINE_SPINLOCK(spinlock); | ||
| 141 | 140 | ||
| 142 | static int nogameport = DEFAULT_NOGAMEPORT; | 141 | static int nogameport = DEFAULT_NOGAMEPORT; |
| 143 | static int exclusive = DEFAULT_EXCLUSIVE; | 142 | static int exclusive = DEFAULT_EXCLUSIVE; |
| @@ -163,18 +162,26 @@ MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started, default=" | |||
| 163 | 162 | ||
| 164 | /* Superio Chip */ | 163 | /* Superio Chip */ |
| 165 | 164 | ||
| 166 | static inline void superio_enter(void) | 165 | static inline int superio_enter(void) |
| 167 | { | 166 | { |
| 167 | /* | ||
| 168 | * Try to reserve REG and REG + 1 for exclusive access. | ||
| 169 | */ | ||
| 170 | if (!request_muxed_region(REG, 2, WATCHDOG_NAME)) | ||
| 171 | return -EBUSY; | ||
| 172 | |||
| 168 | outb(0x87, REG); | 173 | outb(0x87, REG); |
| 169 | outb(0x01, REG); | 174 | outb(0x01, REG); |
| 170 | outb(0x55, REG); | 175 | outb(0x55, REG); |
| 171 | outb(0x55, REG); | 176 | outb(0x55, REG); |
| 177 | return 0; | ||
| 172 | } | 178 | } |
| 173 | 179 | ||
| 174 | static inline void superio_exit(void) | 180 | static inline void superio_exit(void) |
| 175 | { | 181 | { |
| 176 | outb(0x02, REG); | 182 | outb(0x02, REG); |
| 177 | outb(0x02, VAL); | 183 | outb(0x02, VAL); |
| 184 | release_region(REG, 2); | ||
| 178 | } | 185 | } |
| 179 | 186 | ||
| 180 | static inline void superio_select(int ldn) | 187 | static inline void superio_select(int ldn) |
| @@ -255,12 +262,11 @@ static void wdt_keepalive(void) | |||
| 255 | set_bit(WDTS_KEEPALIVE, &wdt_status); | 262 | set_bit(WDTS_KEEPALIVE, &wdt_status); |
| 256 | } | 263 | } |
| 257 | 264 | ||
| 258 | static void wdt_start(void) | 265 | static int wdt_start(void) |
| 259 | { | 266 | { |
| 260 | unsigned long flags; | 267 | int ret = superio_enter(); |
| 261 | 268 | if (ret) | |
| 262 | spin_lock_irqsave(&spinlock, flags); | 269 | return ret; |
| 263 | superio_enter(); | ||
| 264 | 270 | ||
| 265 | superio_select(GPIO); | 271 | superio_select(GPIO); |
| 266 | if (test_bit(WDTS_USE_GP, &wdt_status)) | 272 | if (test_bit(WDTS_USE_GP, &wdt_status)) |
| @@ -270,15 +276,15 @@ static void wdt_start(void) | |||
| 270 | wdt_update_timeout(); | 276 | wdt_update_timeout(); |
| 271 | 277 | ||
| 272 | superio_exit(); | 278 | superio_exit(); |
| 273 | spin_unlock_irqrestore(&spinlock, flags); | 279 | |
| 280 | return 0; | ||
| 274 | } | 281 | } |
| 275 | 282 | ||
| 276 | static void wdt_stop(void) | 283 | static int wdt_stop(void) |
| 277 | { | 284 | { |
| 278 | unsigned long flags; | 285 | int ret = superio_enter(); |
| 279 | 286 | if (ret) | |
| 280 | spin_lock_irqsave(&spinlock, flags); | 287 | return ret; |
| 281 | superio_enter(); | ||
| 282 | 288 | ||
| 283 | superio_select(GPIO); | 289 | superio_select(GPIO); |
| 284 | superio_outb(0x00, WDTCTRL); | 290 | superio_outb(0x00, WDTCTRL); |
| @@ -288,7 +294,7 @@ static void wdt_stop(void) | |||
| 288 | superio_outb(0x00, WDTVALMSB); | 294 | superio_outb(0x00, WDTVALMSB); |
| 289 | 295 | ||
| 290 | superio_exit(); | 296 | superio_exit(); |
| 291 | spin_unlock_irqrestore(&spinlock, flags); | 297 | return 0; |
| 292 | } | 298 | } |
| 293 | 299 | ||
| 294 | /** | 300 | /** |
| @@ -303,8 +309,6 @@ static void wdt_stop(void) | |||
| 303 | 309 | ||
| 304 | static int wdt_set_timeout(int t) | 310 | static int wdt_set_timeout(int t) |
| 305 | { | 311 | { |
| 306 | unsigned long flags; | ||
| 307 | |||
| 308 | if (t < 1 || t > max_units * 60) | 312 | if (t < 1 || t > max_units * 60) |
| 309 | return -EINVAL; | 313 | return -EINVAL; |
| 310 | 314 | ||
| @@ -313,14 +317,15 @@ static int wdt_set_timeout(int t) | |||
| 313 | else | 317 | else |
| 314 | timeout = t; | 318 | timeout = t; |
| 315 | 319 | ||
| 316 | spin_lock_irqsave(&spinlock, flags); | ||
| 317 | if (test_bit(WDTS_TIMER_RUN, &wdt_status)) { | 320 | if (test_bit(WDTS_TIMER_RUN, &wdt_status)) { |
| 318 | superio_enter(); | 321 | int ret = superio_enter(); |
| 322 | if (ret) | ||
| 323 | return ret; | ||
| 324 | |||
| 319 | superio_select(GPIO); | 325 | superio_select(GPIO); |
| 320 | wdt_update_timeout(); | 326 | wdt_update_timeout(); |
| 321 | superio_exit(); | 327 | superio_exit(); |
| 322 | } | 328 | } |
| 323 | spin_unlock_irqrestore(&spinlock, flags); | ||
| 324 | return 0; | 329 | return 0; |
| 325 | } | 330 | } |
| 326 | 331 | ||
| @@ -339,12 +344,12 @@ static int wdt_set_timeout(int t) | |||
| 339 | 344 | ||
| 340 | static int wdt_get_status(int *status) | 345 | static int wdt_get_status(int *status) |
| 341 | { | 346 | { |
| 342 | unsigned long flags; | ||
| 343 | |||
| 344 | *status = 0; | 347 | *status = 0; |
| 345 | if (testmode) { | 348 | if (testmode) { |
| 346 | spin_lock_irqsave(&spinlock, flags); | 349 | int ret = superio_enter(); |
| 347 | superio_enter(); | 350 | if (ret) |
| 351 | return ret; | ||
| 352 | |||
| 348 | superio_select(GPIO); | 353 | superio_select(GPIO); |
| 349 | if (superio_inb(WDTCTRL) & WDT_ZERO) { | 354 | if (superio_inb(WDTCTRL) & WDT_ZERO) { |
| 350 | superio_outb(0x00, WDTCTRL); | 355 | superio_outb(0x00, WDTCTRL); |
| @@ -353,7 +358,6 @@ static int wdt_get_status(int *status) | |||
| 353 | } | 358 | } |
| 354 | 359 | ||
| 355 | superio_exit(); | 360 | superio_exit(); |
| 356 | spin_unlock_irqrestore(&spinlock, flags); | ||
| 357 | } | 361 | } |
| 358 | if (test_and_clear_bit(WDTS_KEEPALIVE, &wdt_status)) | 362 | if (test_and_clear_bit(WDTS_KEEPALIVE, &wdt_status)) |
| 359 | *status |= WDIOF_KEEPALIVEPING; | 363 | *status |= WDIOF_KEEPALIVEPING; |
| @@ -379,9 +383,17 @@ static int wdt_open(struct inode *inode, struct file *file) | |||
| 379 | if (exclusive && test_and_set_bit(WDTS_DEV_OPEN, &wdt_status)) | 383 | if (exclusive && test_and_set_bit(WDTS_DEV_OPEN, &wdt_status)) |
| 380 | return -EBUSY; | 384 | return -EBUSY; |
| 381 | if (!test_and_set_bit(WDTS_TIMER_RUN, &wdt_status)) { | 385 | if (!test_and_set_bit(WDTS_TIMER_RUN, &wdt_status)) { |
| 386 | int ret; | ||
| 382 | if (nowayout && !test_and_set_bit(WDTS_LOCKED, &wdt_status)) | 387 | if (nowayout && !test_and_set_bit(WDTS_LOCKED, &wdt_status)) |
| 383 | __module_get(THIS_MODULE); | 388 | __module_get(THIS_MODULE); |
| 384 | wdt_start(); | 389 | |
| 390 | ret = wdt_start(); | ||
| 391 | if (ret) { | ||
| 392 | clear_bit(WDTS_LOCKED, &wdt_status); | ||
| 393 | clear_bit(WDTS_TIMER_RUN, &wdt_status); | ||
| 394 | clear_bit(WDTS_DEV_OPEN, &wdt_status); | ||
| 395 | return ret; | ||
| 396 | } | ||
| 385 | } | 397 | } |
| 386 | return nonseekable_open(inode, file); | 398 | return nonseekable_open(inode, file); |
| 387 | } | 399 | } |
| @@ -403,7 +415,16 @@ static int wdt_release(struct inode *inode, struct file *file) | |||
| 403 | { | 415 | { |
| 404 | if (test_bit(WDTS_TIMER_RUN, &wdt_status)) { | 416 | if (test_bit(WDTS_TIMER_RUN, &wdt_status)) { |
| 405 | if (test_and_clear_bit(WDTS_EXPECTED, &wdt_status)) { | 417 | if (test_and_clear_bit(WDTS_EXPECTED, &wdt_status)) { |
| 406 | wdt_stop(); | 418 | int ret = wdt_stop(); |
| 419 | if (ret) { | ||
| 420 | /* | ||
| 421 | * Stop failed. Just keep the watchdog alive | ||
| 422 | * and hope nothing bad happens. | ||
| 423 | */ | ||
| 424 | set_bit(WDTS_EXPECTED, &wdt_status); | ||
| 425 | wdt_keepalive(); | ||
| 426 | return ret; | ||
| 427 | } | ||
| 407 | clear_bit(WDTS_TIMER_RUN, &wdt_status); | 428 | clear_bit(WDTS_TIMER_RUN, &wdt_status); |
| 408 | } else { | 429 | } else { |
| 409 | wdt_keepalive(); | 430 | wdt_keepalive(); |
| @@ -484,7 +505,9 @@ static long wdt_ioctl(struct file *file, unsigned int cmd, unsigned long arg) | |||
| 484 | &ident, sizeof(ident)) ? -EFAULT : 0; | 505 | &ident, sizeof(ident)) ? -EFAULT : 0; |
| 485 | 506 | ||
| 486 | case WDIOC_GETSTATUS: | 507 | case WDIOC_GETSTATUS: |
| 487 | wdt_get_status(&status); | 508 | rc = wdt_get_status(&status); |
| 509 | if (rc) | ||
| 510 | return rc; | ||
| 488 | return put_user(status, uarg.i); | 511 | return put_user(status, uarg.i); |
| 489 | 512 | ||
| 490 | case WDIOC_GETBOOTSTATUS: | 513 | case WDIOC_GETBOOTSTATUS: |
| @@ -500,14 +523,22 @@ static long wdt_ioctl(struct file *file, unsigned int cmd, unsigned long arg) | |||
| 500 | 523 | ||
| 501 | switch (new_options) { | 524 | switch (new_options) { |
| 502 | case WDIOS_DISABLECARD: | 525 | case WDIOS_DISABLECARD: |
| 503 | if (test_bit(WDTS_TIMER_RUN, &wdt_status)) | 526 | if (test_bit(WDTS_TIMER_RUN, &wdt_status)) { |
| 504 | wdt_stop(); | 527 | rc = wdt_stop(); |
| 528 | if (rc) | ||
| 529 | return rc; | ||
| 530 | } | ||
| 505 | clear_bit(WDTS_TIMER_RUN, &wdt_status); | 531 | clear_bit(WDTS_TIMER_RUN, &wdt_status); |
| 506 | return 0; | 532 | return 0; |
| 507 | 533 | ||
| 508 | case WDIOS_ENABLECARD: | 534 | case WDIOS_ENABLECARD: |
| 509 | if (!test_and_set_bit(WDTS_TIMER_RUN, &wdt_status)) | 535 | if (!test_and_set_bit(WDTS_TIMER_RUN, &wdt_status)) { |
| 510 | wdt_start(); | 536 | rc = wdt_start(); |
| 537 | if (rc) { | ||
| 538 | clear_bit(WDTS_TIMER_RUN, &wdt_status); | ||
| 539 | return rc; | ||
| 540 | } | ||
| 541 | } | ||
| 511 | return 0; | 542 | return 0; |
| 512 | 543 | ||
| 513 | default: | 544 | default: |
| @@ -560,16 +591,17 @@ static int __init it87_wdt_init(void) | |||
| 560 | int rc = 0; | 591 | int rc = 0; |
| 561 | int try_gameport = !nogameport; | 592 | int try_gameport = !nogameport; |
| 562 | u8 chip_rev; | 593 | u8 chip_rev; |
| 563 | unsigned long flags; | 594 | int gp_rreq_fail = 0; |
| 564 | 595 | ||
| 565 | wdt_status = 0; | 596 | wdt_status = 0; |
| 566 | 597 | ||
| 567 | spin_lock_irqsave(&spinlock, flags); | 598 | rc = superio_enter(); |
| 568 | superio_enter(); | 599 | if (rc) |
| 600 | return rc; | ||
| 601 | |||
| 569 | chip_type = superio_inw(CHIPID); | 602 | chip_type = superio_inw(CHIPID); |
| 570 | chip_rev = superio_inb(CHIPREV) & 0x0f; | 603 | chip_rev = superio_inb(CHIPREV) & 0x0f; |
| 571 | superio_exit(); | 604 | superio_exit(); |
| 572 | spin_unlock_irqrestore(&spinlock, flags); | ||
| 573 | 605 | ||
| 574 | switch (chip_type) { | 606 | switch (chip_type) { |
| 575 | case IT8702_ID: | 607 | case IT8702_ID: |
| @@ -603,8 +635,9 @@ static int __init it87_wdt_init(void) | |||
| 603 | return -ENODEV; | 635 | return -ENODEV; |
| 604 | } | 636 | } |
| 605 | 637 | ||
| 606 | spin_lock_irqsave(&spinlock, flags); | 638 | rc = superio_enter(); |
| 607 | superio_enter(); | 639 | if (rc) |
| 640 | return rc; | ||
| 608 | 641 | ||
| 609 | superio_select(GPIO); | 642 | superio_select(GPIO); |
| 610 | superio_outb(WDT_TOV1, WDTCFG); | 643 | superio_outb(WDT_TOV1, WDTCFG); |
| @@ -620,21 +653,16 @@ static int __init it87_wdt_init(void) | |||
| 620 | } | 653 | } |
| 621 | gpact = superio_inb(ACTREG); | 654 | gpact = superio_inb(ACTREG); |
| 622 | superio_outb(0x01, ACTREG); | 655 | superio_outb(0x01, ACTREG); |
| 623 | superio_exit(); | ||
| 624 | spin_unlock_irqrestore(&spinlock, flags); | ||
| 625 | if (request_region(base, 1, WATCHDOG_NAME)) | 656 | if (request_region(base, 1, WATCHDOG_NAME)) |
| 626 | set_bit(WDTS_USE_GP, &wdt_status); | 657 | set_bit(WDTS_USE_GP, &wdt_status); |
| 627 | else | 658 | else |
| 628 | rc = -EIO; | 659 | gp_rreq_fail = 1; |
| 629 | } else { | ||
| 630 | superio_exit(); | ||
| 631 | spin_unlock_irqrestore(&spinlock, flags); | ||
| 632 | } | 660 | } |
| 633 | 661 | ||
| 634 | /* If we haven't Gameport support, try to get CIR support */ | 662 | /* If we haven't Gameport support, try to get CIR support */ |
| 635 | if (!test_bit(WDTS_USE_GP, &wdt_status)) { | 663 | if (!test_bit(WDTS_USE_GP, &wdt_status)) { |
| 636 | if (!request_region(CIR_BASE, 8, WATCHDOG_NAME)) { | 664 | if (!request_region(CIR_BASE, 8, WATCHDOG_NAME)) { |
| 637 | if (rc == -EIO) | 665 | if (gp_rreq_fail) |
| 638 | printk(KERN_ERR PFX | 666 | printk(KERN_ERR PFX |
| 639 | "I/O Address 0x%04x and 0x%04x" | 667 | "I/O Address 0x%04x and 0x%04x" |
| 640 | " already in use\n", base, CIR_BASE); | 668 | " already in use\n", base, CIR_BASE); |
| @@ -646,21 +674,16 @@ static int __init it87_wdt_init(void) | |||
| 646 | goto err_out; | 674 | goto err_out; |
| 647 | } | 675 | } |
| 648 | base = CIR_BASE; | 676 | base = CIR_BASE; |
| 649 | spin_lock_irqsave(&spinlock, flags); | ||
| 650 | superio_enter(); | ||
| 651 | 677 | ||
| 652 | superio_select(CIR); | 678 | superio_select(CIR); |
| 653 | superio_outw(base, BASEREG); | 679 | superio_outw(base, BASEREG); |
| 654 | superio_outb(0x00, CIR_ILS); | 680 | superio_outb(0x00, CIR_ILS); |
| 655 | ciract = superio_inb(ACTREG); | 681 | ciract = superio_inb(ACTREG); |
| 656 | superio_outb(0x01, ACTREG); | 682 | superio_outb(0x01, ACTREG); |
| 657 | if (rc == -EIO) { | 683 | if (gp_rreq_fail) { |
| 658 | superio_select(GAMEPORT); | 684 | superio_select(GAMEPORT); |
| 659 | superio_outb(gpact, ACTREG); | 685 | superio_outb(gpact, ACTREG); |
| 660 | } | 686 | } |
| 661 | |||
| 662 | superio_exit(); | ||
| 663 | spin_unlock_irqrestore(&spinlock, flags); | ||
| 664 | } | 687 | } |
| 665 | 688 | ||
| 666 | if (timeout < 1 || timeout > max_units * 60) { | 689 | if (timeout < 1 || timeout > max_units * 60) { |
| @@ -704,6 +727,7 @@ static int __init it87_wdt_init(void) | |||
| 704 | "nogameport=%d)\n", chip_type, chip_rev, timeout, | 727 | "nogameport=%d)\n", chip_type, chip_rev, timeout, |
| 705 | nowayout, testmode, exclusive, nogameport); | 728 | nowayout, testmode, exclusive, nogameport); |
| 706 | 729 | ||
| 730 | superio_exit(); | ||
| 707 | return 0; | 731 | return 0; |
| 708 | 732 | ||
| 709 | err_out_reboot: | 733 | err_out_reboot: |
| @@ -711,49 +735,37 @@ err_out_reboot: | |||
| 711 | err_out_region: | 735 | err_out_region: |
| 712 | release_region(base, test_bit(WDTS_USE_GP, &wdt_status) ? 1 : 8); | 736 | release_region(base, test_bit(WDTS_USE_GP, &wdt_status) ? 1 : 8); |
| 713 | if (!test_bit(WDTS_USE_GP, &wdt_status)) { | 737 | if (!test_bit(WDTS_USE_GP, &wdt_status)) { |
| 714 | spin_lock_irqsave(&spinlock, flags); | ||
| 715 | superio_enter(); | ||
| 716 | superio_select(CIR); | 738 | superio_select(CIR); |
| 717 | superio_outb(ciract, ACTREG); | 739 | superio_outb(ciract, ACTREG); |
| 718 | superio_exit(); | ||
| 719 | spin_unlock_irqrestore(&spinlock, flags); | ||
| 720 | } | 740 | } |
| 721 | err_out: | 741 | err_out: |
| 722 | if (try_gameport) { | 742 | if (try_gameport) { |
| 723 | spin_lock_irqsave(&spinlock, flags); | ||
| 724 | superio_enter(); | ||
| 725 | superio_select(GAMEPORT); | 743 | superio_select(GAMEPORT); |
| 726 | superio_outb(gpact, ACTREG); | 744 | superio_outb(gpact, ACTREG); |
| 727 | superio_exit(); | ||
| 728 | spin_unlock_irqrestore(&spinlock, flags); | ||
| 729 | } | 745 | } |
| 730 | 746 | ||
| 747 | superio_exit(); | ||
| 731 | return rc; | 748 | return rc; |
| 732 | } | 749 | } |
| 733 | 750 | ||
| 734 | static void __exit it87_wdt_exit(void) | 751 | static void __exit it87_wdt_exit(void) |
| 735 | { | 752 | { |
| 736 | unsigned long flags; | 753 | if (superio_enter() == 0) { |
| 737 | int nolock; | 754 | superio_select(GPIO); |
| 738 | 755 | superio_outb(0x00, WDTCTRL); | |
| 739 | nolock = !spin_trylock_irqsave(&spinlock, flags); | 756 | superio_outb(0x00, WDTCFG); |
| 740 | superio_enter(); | 757 | superio_outb(0x00, WDTVALLSB); |
| 741 | superio_select(GPIO); | 758 | if (max_units > 255) |
| 742 | superio_outb(0x00, WDTCTRL); | 759 | superio_outb(0x00, WDTVALMSB); |
| 743 | superio_outb(0x00, WDTCFG); | 760 | if (test_bit(WDTS_USE_GP, &wdt_status)) { |
| 744 | superio_outb(0x00, WDTVALLSB); | 761 | superio_select(GAMEPORT); |
| 745 | if (max_units > 255) | 762 | superio_outb(gpact, ACTREG); |
| 746 | superio_outb(0x00, WDTVALMSB); | 763 | } else { |
| 747 | if (test_bit(WDTS_USE_GP, &wdt_status)) { | 764 | superio_select(CIR); |
| 748 | superio_select(GAMEPORT); | 765 | superio_outb(ciract, ACTREG); |
| 749 | superio_outb(gpact, ACTREG); | 766 | } |
| 750 | } else { | 767 | superio_exit(); |
| 751 | superio_select(CIR); | ||
| 752 | superio_outb(ciract, ACTREG); | ||
| 753 | } | 768 | } |
| 754 | superio_exit(); | ||
| 755 | if (!nolock) | ||
| 756 | spin_unlock_irqrestore(&spinlock, flags); | ||
| 757 | 769 | ||
| 758 | misc_deregister(&wdt_miscdev); | 770 | misc_deregister(&wdt_miscdev); |
| 759 | unregister_reboot_notifier(&wdt_notifier); | 771 | unregister_reboot_notifier(&wdt_notifier); |
diff --git a/drivers/watchdog/mpcore_wdt.c b/drivers/watchdog/mpcore_wdt.c index 2b4af222b5f2..4dc31024d26c 100644 --- a/drivers/watchdog/mpcore_wdt.c +++ b/drivers/watchdog/mpcore_wdt.c | |||
| @@ -407,12 +407,35 @@ static int __devexit mpcore_wdt_remove(struct platform_device *dev) | |||
| 407 | return 0; | 407 | return 0; |
| 408 | } | 408 | } |
| 409 | 409 | ||
| 410 | #ifdef CONFIG_PM | ||
| 411 | static int mpcore_wdt_suspend(struct platform_device *dev, pm_message_t msg) | ||
| 412 | { | ||
| 413 | struct mpcore_wdt *wdt = platform_get_drvdata(dev); | ||
| 414 | mpcore_wdt_stop(wdt); /* Turn the WDT off */ | ||
| 415 | return 0; | ||
| 416 | } | ||
| 417 | |||
| 418 | static int mpcore_wdt_resume(struct platform_device *dev) | ||
| 419 | { | ||
| 420 | struct mpcore_wdt *wdt = platform_get_drvdata(dev); | ||
| 421 | /* re-activate timer */ | ||
| 422 | if (test_bit(0, &wdt->timer_alive)) | ||
| 423 | mpcore_wdt_start(wdt); | ||
| 424 | return 0; | ||
| 425 | } | ||
| 426 | #else | ||
| 427 | #define mpcore_wdt_suspend NULL | ||
| 428 | #define mpcore_wdt_resume NULL | ||
| 429 | #endif | ||
| 430 | |||
| 410 | /* work with hotplug and coldplug */ | 431 | /* work with hotplug and coldplug */ |
| 411 | MODULE_ALIAS("platform:mpcore_wdt"); | 432 | MODULE_ALIAS("platform:mpcore_wdt"); |
| 412 | 433 | ||
| 413 | static struct platform_driver mpcore_wdt_driver = { | 434 | static struct platform_driver mpcore_wdt_driver = { |
| 414 | .probe = mpcore_wdt_probe, | 435 | .probe = mpcore_wdt_probe, |
| 415 | .remove = __devexit_p(mpcore_wdt_remove), | 436 | .remove = __devexit_p(mpcore_wdt_remove), |
| 437 | .suspend = mpcore_wdt_suspend, | ||
| 438 | .resume = mpcore_wdt_resume, | ||
| 416 | .shutdown = mpcore_wdt_shutdown, | 439 | .shutdown = mpcore_wdt_shutdown, |
| 417 | .driver = { | 440 | .driver = { |
| 418 | .owner = THIS_MODULE, | 441 | .owner = THIS_MODULE, |
diff --git a/drivers/watchdog/mtx-1_wdt.c b/drivers/watchdog/mtx-1_wdt.c index 0430e093b1a0..ac37bb82392c 100644 --- a/drivers/watchdog/mtx-1_wdt.c +++ b/drivers/watchdog/mtx-1_wdt.c | |||
| @@ -225,11 +225,11 @@ static int __devinit mtx1_wdt_probe(struct platform_device *pdev) | |||
| 225 | 225 | ||
| 226 | ret = misc_register(&mtx1_wdt_misc); | 226 | ret = misc_register(&mtx1_wdt_misc); |
| 227 | if (ret < 0) { | 227 | if (ret < 0) { |
| 228 | printk(KERN_ERR " mtx-1_wdt : failed to register\n"); | 228 | dev_err(&pdev->dev, "failed to register\n"); |
| 229 | return ret; | 229 | return ret; |
| 230 | } | 230 | } |
| 231 | mtx1_wdt_start(); | 231 | mtx1_wdt_start(); |
| 232 | printk(KERN_INFO "MTX-1 Watchdog driver\n"); | 232 | dev_info(&pdev->dev, "MTX-1 Watchdog driver\n"); |
| 233 | return 0; | 233 | return 0; |
| 234 | } | 234 | } |
| 235 | 235 | ||
diff --git a/drivers/watchdog/of_xilinx_wdt.c b/drivers/watchdog/of_xilinx_wdt.c new file mode 100644 index 000000000000..4ec741ac952c --- /dev/null +++ b/drivers/watchdog/of_xilinx_wdt.c | |||
| @@ -0,0 +1,433 @@ | |||
| 1 | /* | ||
| 2 | * of_xilinx_wdt.c 1.01 A Watchdog Device Driver for Xilinx xps_timebase_wdt | ||
| 3 | * | ||
| 4 | * (C) Copyright 2011 (Alejandro Cabrera <aldaya@gmail.com>) | ||
| 5 | * | ||
| 6 | * ----------------------- | ||
| 7 | * | ||
| 8 | * This program is free software; you can redistribute it and/or | ||
| 9 | * modify it under the terms of the GNU General Public License | ||
| 10 | * as published by the Free Software Foundation; either version | ||
| 11 | * 2 of the License, or (at your option) any later version. | ||
| 12 | * | ||
| 13 | * ----------------------- | ||
| 14 | * 30-May-2011 Alejandro Cabrera <aldaya@gmail.com> | ||
| 15 | * - If "xlnx,wdt-enable-once" wasn't found on device tree the | ||
| 16 | * module will use CONFIG_WATCHDOG_NOWAYOUT | ||
| 17 | * - If the device tree parameters ("clock-frequency" and | ||
| 18 | * "xlnx,wdt-interval") wasn't found the driver won't | ||
| 19 | * know the wdt reset interval | ||
| 20 | */ | ||
| 21 | |||
| 22 | #include <linux/module.h> | ||
| 23 | #include <linux/types.h> | ||
| 24 | #include <linux/kernel.h> | ||
| 25 | #include <linux/fs.h> | ||
| 26 | #include <linux/miscdevice.h> | ||
| 27 | #include <linux/init.h> | ||
| 28 | #include <linux/ioport.h> | ||
| 29 | #include <linux/watchdog.h> | ||
| 30 | #include <linux/io.h> | ||
| 31 | #include <linux/uaccess.h> | ||
| 32 | #include <linux/of.h> | ||
| 33 | #include <linux/of_device.h> | ||
| 34 | #include <linux/of_address.h> | ||
| 35 | |||
| 36 | /* Register offsets for the Wdt device */ | ||
| 37 | #define XWT_TWCSR0_OFFSET 0x0 /* Control/Status Register0 */ | ||
| 38 | #define XWT_TWCSR1_OFFSET 0x4 /* Control/Status Register1 */ | ||
| 39 | #define XWT_TBR_OFFSET 0x8 /* Timebase Register Offset */ | ||
| 40 | |||
| 41 | /* Control/Status Register Masks */ | ||
| 42 | #define XWT_CSR0_WRS_MASK 0x00000008 /* Reset status */ | ||
| 43 | #define XWT_CSR0_WDS_MASK 0x00000004 /* Timer state */ | ||
| 44 | #define XWT_CSR0_EWDT1_MASK 0x00000002 /* Enable bit 1 */ | ||
| 45 | |||
| 46 | /* Control/Status Register 0/1 bits */ | ||
| 47 | #define XWT_CSRX_EWDT2_MASK 0x00000001 /* Enable bit 2 */ | ||
| 48 | |||
| 49 | /* SelfTest constants */ | ||
| 50 | #define XWT_MAX_SELFTEST_LOOP_COUNT 0x00010000 | ||
| 51 | #define XWT_TIMER_FAILED 0xFFFFFFFF | ||
| 52 | |||
| 53 | #define WATCHDOG_NAME "Xilinx Watchdog" | ||
| 54 | #define PFX WATCHDOG_NAME ": " | ||
| 55 | |||
| 56 | struct xwdt_device { | ||
| 57 | struct resource res; | ||
| 58 | void __iomem *base; | ||
| 59 | u32 nowayout; | ||
| 60 | u32 wdt_interval; | ||
| 61 | u32 boot_status; | ||
| 62 | }; | ||
| 63 | |||
| 64 | static struct xwdt_device xdev; | ||
| 65 | |||
| 66 | static u32 timeout; | ||
| 67 | static u32 control_status_reg; | ||
| 68 | static u8 expect_close; | ||
| 69 | static u8 no_timeout; | ||
| 70 | static unsigned long driver_open; | ||
| 71 | |||
| 72 | static DEFINE_SPINLOCK(spinlock); | ||
| 73 | |||
| 74 | static void xwdt_start(void) | ||
| 75 | { | ||
| 76 | spin_lock(&spinlock); | ||
| 77 | |||
| 78 | /* Clean previous status and enable the watchdog timer */ | ||
| 79 | control_status_reg = ioread32(xdev.base + XWT_TWCSR0_OFFSET); | ||
| 80 | control_status_reg |= (XWT_CSR0_WRS_MASK | XWT_CSR0_WDS_MASK); | ||
| 81 | |||
| 82 | iowrite32((control_status_reg | XWT_CSR0_EWDT1_MASK), | ||
| 83 | xdev.base + XWT_TWCSR0_OFFSET); | ||
| 84 | |||
| 85 | iowrite32(XWT_CSRX_EWDT2_MASK, xdev.base + XWT_TWCSR1_OFFSET); | ||
| 86 | |||
| 87 | spin_unlock(&spinlock); | ||
| 88 | } | ||
| 89 | |||
| 90 | static void xwdt_stop(void) | ||
| 91 | { | ||
| 92 | spin_lock(&spinlock); | ||
| 93 | |||
| 94 | control_status_reg = ioread32(xdev.base + XWT_TWCSR0_OFFSET); | ||
| 95 | |||
| 96 | iowrite32((control_status_reg & ~XWT_CSR0_EWDT1_MASK), | ||
| 97 | xdev.base + XWT_TWCSR0_OFFSET); | ||
| 98 | |||
| 99 | iowrite32(0, xdev.base + XWT_TWCSR1_OFFSET); | ||
| 100 | |||
| 101 | spin_unlock(&spinlock); | ||
| 102 | printk(KERN_INFO PFX "Stopped!\n"); | ||
| 103 | } | ||
| 104 | |||
| 105 | static void xwdt_keepalive(void) | ||
| 106 | { | ||
| 107 | spin_lock(&spinlock); | ||
| 108 | |||
| 109 | control_status_reg = ioread32(xdev.base + XWT_TWCSR0_OFFSET); | ||
| 110 | control_status_reg |= (XWT_CSR0_WRS_MASK | XWT_CSR0_WDS_MASK); | ||
| 111 | iowrite32(control_status_reg, xdev.base + XWT_TWCSR0_OFFSET); | ||
| 112 | |||
| 113 | spin_unlock(&spinlock); | ||
| 114 | } | ||
| 115 | |||
| 116 | static void xwdt_get_status(int *status) | ||
| 117 | { | ||
| 118 | int new_status; | ||
| 119 | |||
| 120 | spin_lock(&spinlock); | ||
| 121 | |||
| 122 | control_status_reg = ioread32(xdev.base + XWT_TWCSR0_OFFSET); | ||
| 123 | new_status = ((control_status_reg & | ||
| 124 | (XWT_CSR0_WRS_MASK | XWT_CSR0_WDS_MASK)) != 0); | ||
| 125 | spin_unlock(&spinlock); | ||
| 126 | |||
| 127 | *status = 0; | ||
| 128 | if (new_status & 1) | ||
| 129 | *status |= WDIOF_CARDRESET; | ||
| 130 | } | ||
| 131 | |||
| 132 | static u32 xwdt_selftest(void) | ||
| 133 | { | ||
| 134 | int i; | ||
| 135 | u32 timer_value1; | ||
| 136 | u32 timer_value2; | ||
| 137 | |||
| 138 | spin_lock(&spinlock); | ||
| 139 | |||
| 140 | timer_value1 = ioread32(xdev.base + XWT_TBR_OFFSET); | ||
| 141 | timer_value2 = ioread32(xdev.base + XWT_TBR_OFFSET); | ||
| 142 | |||
| 143 | for (i = 0; | ||
| 144 | ((i <= XWT_MAX_SELFTEST_LOOP_COUNT) && | ||
| 145 | (timer_value2 == timer_value1)); i++) { | ||
| 146 | timer_value2 = ioread32(xdev.base + XWT_TBR_OFFSET); | ||
| 147 | } | ||
| 148 | |||
| 149 | spin_unlock(&spinlock); | ||
| 150 | |||
| 151 | if (timer_value2 != timer_value1) | ||
| 152 | return ~XWT_TIMER_FAILED; | ||
| 153 | else | ||
| 154 | return XWT_TIMER_FAILED; | ||
| 155 | } | ||
| 156 | |||
| 157 | static int xwdt_open(struct inode *inode, struct file *file) | ||
| 158 | { | ||
| 159 | /* Only one process can handle the wdt at a time */ | ||
| 160 | if (test_and_set_bit(0, &driver_open)) | ||
| 161 | return -EBUSY; | ||
| 162 | |||
| 163 | /* Make sure that the module are always loaded...*/ | ||
| 164 | if (xdev.nowayout) | ||
| 165 | __module_get(THIS_MODULE); | ||
| 166 | |||
| 167 | xwdt_start(); | ||
| 168 | printk(KERN_INFO PFX "Started...\n"); | ||
| 169 | |||
| 170 | return nonseekable_open(inode, file); | ||
| 171 | } | ||
| 172 | |||
| 173 | static int xwdt_release(struct inode *inode, struct file *file) | ||
| 174 | { | ||
| 175 | if (expect_close == 42) { | ||
| 176 | xwdt_stop(); | ||
| 177 | } else { | ||
| 178 | printk(KERN_CRIT PFX | ||
| 179 | "Unexpected close, not stopping watchdog!\n"); | ||
| 180 | xwdt_keepalive(); | ||
| 181 | } | ||
| 182 | |||
| 183 | clear_bit(0, &driver_open); | ||
| 184 | expect_close = 0; | ||
| 185 | return 0; | ||
| 186 | } | ||
| 187 | |||
| 188 | /* | ||
| 189 | * xwdt_write: | ||
| 190 | * @file: file handle to the watchdog | ||
| 191 | * @buf: buffer to write (unused as data does not matter here | ||
| 192 | * @count: count of bytes | ||
| 193 | * @ppos: pointer to the position to write. No seeks allowed | ||
| 194 | * | ||
| 195 | * A write to a watchdog device is defined as a keepalive signal. Any | ||
| 196 | * write of data will do, as we don't define content meaning. | ||
| 197 | */ | ||
| 198 | static ssize_t xwdt_write(struct file *file, const char __user *buf, | ||
| 199 | size_t len, loff_t *ppos) | ||
| 200 | { | ||
| 201 | if (len) { | ||
| 202 | if (!xdev.nowayout) { | ||
| 203 | size_t i; | ||
| 204 | |||
| 205 | /* In case it was set long ago */ | ||
| 206 | expect_close = 0; | ||
| 207 | |||
| 208 | for (i = 0; i != len; i++) { | ||
| 209 | char c; | ||
| 210 | |||
| 211 | if (get_user(c, buf + i)) | ||
| 212 | return -EFAULT; | ||
| 213 | if (c == 'V') | ||
| 214 | expect_close = 42; | ||
| 215 | } | ||
| 216 | } | ||
| 217 | xwdt_keepalive(); | ||
| 218 | } | ||
| 219 | return len; | ||
| 220 | } | ||
| 221 | |||
| 222 | static const struct watchdog_info ident = { | ||
| 223 | .options = WDIOF_MAGICCLOSE | | ||
| 224 | WDIOF_KEEPALIVEPING, | ||
| 225 | .firmware_version = 1, | ||
| 226 | .identity = WATCHDOG_NAME, | ||
| 227 | }; | ||
| 228 | |||
| 229 | /* | ||
| 230 | * xwdt_ioctl: | ||
| 231 | * @file: file handle to the device | ||
| 232 | * @cmd: watchdog command | ||
| 233 | * @arg: argument pointer | ||
| 234 | * | ||
| 235 | * The watchdog API defines a common set of functions for all watchdogs | ||
| 236 | * according to their available features. | ||
| 237 | */ | ||
| 238 | static long xwdt_ioctl(struct file *file, unsigned int cmd, unsigned long arg) | ||
| 239 | { | ||
| 240 | int status; | ||
| 241 | |||
| 242 | union { | ||
| 243 | struct watchdog_info __user *ident; | ||
| 244 | int __user *i; | ||
| 245 | } uarg; | ||
| 246 | |||
| 247 | uarg.i = (int __user *)arg; | ||
| 248 | |||
| 249 | switch (cmd) { | ||
| 250 | case WDIOC_GETSUPPORT: | ||
| 251 | return copy_to_user(uarg.ident, &ident, | ||
| 252 | sizeof(ident)) ? -EFAULT : 0; | ||
| 253 | |||
| 254 | case WDIOC_GETBOOTSTATUS: | ||
| 255 | return put_user(xdev.boot_status, uarg.i); | ||
| 256 | |||
| 257 | case WDIOC_GETSTATUS: | ||
| 258 | xwdt_get_status(&status); | ||
| 259 | return put_user(status, uarg.i); | ||
| 260 | |||
| 261 | case WDIOC_KEEPALIVE: | ||
| 262 | xwdt_keepalive(); | ||
| 263 | return 0; | ||
| 264 | |||
| 265 | case WDIOC_GETTIMEOUT: | ||
| 266 | if (no_timeout) | ||
| 267 | return -ENOTTY; | ||
| 268 | else | ||
| 269 | return put_user(timeout, uarg.i); | ||
| 270 | |||
| 271 | default: | ||
| 272 | return -ENOTTY; | ||
| 273 | } | ||
| 274 | } | ||
| 275 | |||
| 276 | static const struct file_operations xwdt_fops = { | ||
| 277 | .owner = THIS_MODULE, | ||
| 278 | .llseek = no_llseek, | ||
| 279 | .write = xwdt_write, | ||
| 280 | .open = xwdt_open, | ||
| 281 | .release = xwdt_release, | ||
| 282 | .unlocked_ioctl = xwdt_ioctl, | ||
| 283 | }; | ||
| 284 | |||
| 285 | static struct miscdevice xwdt_miscdev = { | ||
| 286 | .minor = WATCHDOG_MINOR, | ||
| 287 | .name = "watchdog", | ||
| 288 | .fops = &xwdt_fops, | ||
| 289 | }; | ||
| 290 | |||
| 291 | static int __devinit xwdt_probe(struct platform_device *pdev) | ||
| 292 | { | ||
| 293 | int rc; | ||
| 294 | u32 *tmptr; | ||
| 295 | u32 *pfreq; | ||
| 296 | |||
| 297 | no_timeout = 0; | ||
| 298 | |||
| 299 | pfreq = (u32 *)of_get_property(pdev->dev.of_node->parent, | ||
| 300 | "clock-frequency", NULL); | ||
| 301 | |||
| 302 | if (pfreq == NULL) { | ||
| 303 | printk(KERN_WARNING PFX | ||
| 304 | "The watchdog clock frequency cannot be obtained!\n"); | ||
| 305 | no_timeout = 1; | ||
| 306 | } | ||
| 307 | |||
| 308 | rc = of_address_to_resource(pdev->dev.of_node, 0, &xdev.res); | ||
| 309 | if (rc) { | ||
| 310 | printk(KERN_WARNING PFX "invalid address!\n"); | ||
| 311 | return rc; | ||
| 312 | } | ||
| 313 | |||
| 314 | tmptr = (u32 *)of_get_property(pdev->dev.of_node, | ||
| 315 | "xlnx,wdt-interval", NULL); | ||
| 316 | if (tmptr == NULL) { | ||
| 317 | printk(KERN_WARNING PFX "Parameter \"xlnx,wdt-interval\"" | ||
| 318 | " not found in device tree!\n"); | ||
| 319 | no_timeout = 1; | ||
| 320 | } else { | ||
| 321 | xdev.wdt_interval = *tmptr; | ||
| 322 | } | ||
| 323 | |||
| 324 | tmptr = (u32 *)of_get_property(pdev->dev.of_node, | ||
| 325 | "xlnx,wdt-enable-once", NULL); | ||
| 326 | if (tmptr == NULL) { | ||
| 327 | printk(KERN_WARNING PFX "Parameter \"xlnx,wdt-enable-once\"" | ||
| 328 | " not found in device tree!\n"); | ||
| 329 | xdev.nowayout = WATCHDOG_NOWAYOUT; | ||
| 330 | } | ||
| 331 | |||
| 332 | /* | ||
| 333 | * Twice of the 2^wdt_interval / freq because the first wdt overflow is | ||
| 334 | * ignored (interrupt), reset is only generated at second wdt overflow | ||
| 335 | */ | ||
| 336 | if (!no_timeout) | ||
| 337 | timeout = 2 * ((1<<xdev.wdt_interval) / *pfreq); | ||
| 338 | |||
| 339 | if (!request_mem_region(xdev.res.start, | ||
| 340 | xdev.res.end - xdev.res.start + 1, WATCHDOG_NAME)) { | ||
| 341 | rc = -ENXIO; | ||
| 342 | printk(KERN_ERR PFX "memory request failure!\n"); | ||
| 343 | goto err_out; | ||
| 344 | } | ||
| 345 | |||
| 346 | xdev.base = ioremap(xdev.res.start, xdev.res.end - xdev.res.start + 1); | ||
| 347 | if (xdev.base == NULL) { | ||
| 348 | rc = -ENOMEM; | ||
| 349 | printk(KERN_ERR PFX "ioremap failure!\n"); | ||
| 350 | goto release_mem; | ||
| 351 | } | ||
| 352 | |||
| 353 | rc = xwdt_selftest(); | ||
| 354 | if (rc == XWT_TIMER_FAILED) { | ||
| 355 | printk(KERN_ERR PFX "SelfTest routine error!\n"); | ||
| 356 | goto unmap_io; | ||
| 357 | } | ||
| 358 | |||
| 359 | xwdt_get_status(&xdev.boot_status); | ||
| 360 | |||
| 361 | rc = misc_register(&xwdt_miscdev); | ||
| 362 | if (rc) { | ||
| 363 | printk(KERN_ERR PFX | ||
| 364 | "cannot register miscdev on minor=%d (err=%d)\n", | ||
| 365 | xwdt_miscdev.minor, rc); | ||
| 366 | goto unmap_io; | ||
| 367 | } | ||
| 368 | |||
| 369 | if (no_timeout) | ||
| 370 | printk(KERN_INFO PFX | ||
| 371 | "driver loaded (timeout=? sec, nowayout=%d)\n", | ||
| 372 | xdev.nowayout); | ||
| 373 | else | ||
| 374 | printk(KERN_INFO PFX | ||
| 375 | "driver loaded (timeout=%d sec, nowayout=%d)\n", | ||
| 376 | timeout, xdev.nowayout); | ||
| 377 | |||
| 378 | expect_close = 0; | ||
| 379 | clear_bit(0, &driver_open); | ||
| 380 | |||
| 381 | return 0; | ||
| 382 | |||
| 383 | unmap_io: | ||
| 384 | iounmap(xdev.base); | ||
| 385 | release_mem: | ||
| 386 | release_mem_region(xdev.res.start, resource_size(&xdev.res)); | ||
| 387 | err_out: | ||
| 388 | return rc; | ||
| 389 | } | ||
| 390 | |||
| 391 | static int __devexit xwdt_remove(struct platform_device *dev) | ||
| 392 | { | ||
| 393 | misc_deregister(&xwdt_miscdev); | ||
| 394 | iounmap(xdev.base); | ||
| 395 | release_mem_region(xdev.res.start, resource_size(&xdev.res)); | ||
| 396 | |||
| 397 | return 0; | ||
| 398 | } | ||
| 399 | |||
| 400 | /* Match table for of_platform binding */ | ||
| 401 | static struct of_device_id __devinitdata xwdt_of_match[] = { | ||
| 402 | { .compatible = "xlnx,xps-timebase-wdt-1.01.a", }, | ||
| 403 | {}, | ||
| 404 | }; | ||
| 405 | MODULE_DEVICE_TABLE(of, xwdt_of_match); | ||
| 406 | |||
| 407 | static struct platform_driver xwdt_driver = { | ||
| 408 | .probe = xwdt_probe, | ||
| 409 | .remove = __devexit_p(xwdt_remove), | ||
| 410 | .driver = { | ||
| 411 | .owner = THIS_MODULE, | ||
| 412 | .name = WATCHDOG_NAME, | ||
| 413 | .of_match_table = xwdt_of_match, | ||
| 414 | }, | ||
| 415 | }; | ||
| 416 | |||
| 417 | static int __init xwdt_init(void) | ||
| 418 | { | ||
| 419 | return platform_driver_register(&xwdt_driver); | ||
| 420 | } | ||
| 421 | |||
| 422 | static void __exit xwdt_exit(void) | ||
| 423 | { | ||
| 424 | platform_driver_unregister(&xwdt_driver); | ||
| 425 | } | ||
| 426 | |||
| 427 | module_init(xwdt_init); | ||
| 428 | module_exit(xwdt_exit); | ||
| 429 | |||
| 430 | MODULE_AUTHOR("Alejandro Cabrera <aldaya@gmail.com>"); | ||
| 431 | MODULE_DESCRIPTION("Xilinx Watchdog driver"); | ||
| 432 | MODULE_LICENSE("GPL"); | ||
| 433 | MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR); | ||
diff --git a/drivers/watchdog/pc87413_wdt.c b/drivers/watchdog/pc87413_wdt.c index b7c139051575..e78d89986768 100644 --- a/drivers/watchdog/pc87413_wdt.c +++ b/drivers/watchdog/pc87413_wdt.c | |||
| @@ -56,6 +56,7 @@ | |||
| 56 | #define IO_DEFAULT 0x2E /* Address used on Portwell Boards */ | 56 | #define IO_DEFAULT 0x2E /* Address used on Portwell Boards */ |
| 57 | 57 | ||
| 58 | static int io = IO_DEFAULT; | 58 | static int io = IO_DEFAULT; |
| 59 | static int swc_base_addr = -1; | ||
| 59 | 60 | ||
| 60 | static int timeout = DEFAULT_TIMEOUT; /* timeout value */ | 61 | static int timeout = DEFAULT_TIMEOUT; /* timeout value */ |
| 61 | static unsigned long timer_enabled; /* is the timer enabled? */ | 62 | static unsigned long timer_enabled; /* is the timer enabled? */ |
| @@ -116,9 +117,8 @@ static inline void pc87413_enable_swc(void) | |||
| 116 | 117 | ||
| 117 | /* Read SWC I/O base address */ | 118 | /* Read SWC I/O base address */ |
| 118 | 119 | ||
| 119 | static inline unsigned int pc87413_get_swc_base(void) | 120 | static void pc87413_get_swc_base_addr(void) |
| 120 | { | 121 | { |
| 121 | unsigned int swc_base_addr = 0; | ||
| 122 | unsigned char addr_l, addr_h = 0; | 122 | unsigned char addr_l, addr_h = 0; |
| 123 | 123 | ||
| 124 | /* Step 3: Read SWC I/O Base Address */ | 124 | /* Step 3: Read SWC I/O Base Address */ |
| @@ -136,12 +136,11 @@ static inline unsigned int pc87413_get_swc_base(void) | |||
| 136 | "Read SWC I/O Base Address: low %d, high %d, res %d\n", | 136 | "Read SWC I/O Base Address: low %d, high %d, res %d\n", |
| 137 | addr_l, addr_h, swc_base_addr); | 137 | addr_l, addr_h, swc_base_addr); |
| 138 | #endif | 138 | #endif |
| 139 | return swc_base_addr; | ||
| 140 | } | 139 | } |
| 141 | 140 | ||
| 142 | /* Select Bank 3 of SWC */ | 141 | /* Select Bank 3 of SWC */ |
| 143 | 142 | ||
| 144 | static inline void pc87413_swc_bank3(unsigned int swc_base_addr) | 143 | static inline void pc87413_swc_bank3(void) |
| 145 | { | 144 | { |
| 146 | /* Step 4: Select Bank3 of SWC */ | 145 | /* Step 4: Select Bank3 of SWC */ |
| 147 | outb_p(inb(swc_base_addr + 0x0f) | 0x03, swc_base_addr + 0x0f); | 146 | outb_p(inb(swc_base_addr + 0x0f) | 0x03, swc_base_addr + 0x0f); |
| @@ -152,8 +151,7 @@ static inline void pc87413_swc_bank3(unsigned int swc_base_addr) | |||
| 152 | 151 | ||
| 153 | /* Set watchdog timeout to x minutes */ | 152 | /* Set watchdog timeout to x minutes */ |
| 154 | 153 | ||
| 155 | static inline void pc87413_programm_wdto(unsigned int swc_base_addr, | 154 | static inline void pc87413_programm_wdto(char pc87413_time) |
| 156 | char pc87413_time) | ||
| 157 | { | 155 | { |
| 158 | /* Step 5: Programm WDTO, Twd. */ | 156 | /* Step 5: Programm WDTO, Twd. */ |
| 159 | outb_p(pc87413_time, swc_base_addr + WDTO); | 157 | outb_p(pc87413_time, swc_base_addr + WDTO); |
| @@ -164,7 +162,7 @@ static inline void pc87413_programm_wdto(unsigned int swc_base_addr, | |||
| 164 | 162 | ||
| 165 | /* Enable WDEN */ | 163 | /* Enable WDEN */ |
| 166 | 164 | ||
| 167 | static inline void pc87413_enable_wden(unsigned int swc_base_addr) | 165 | static inline void pc87413_enable_wden(void) |
| 168 | { | 166 | { |
| 169 | /* Step 6: Enable WDEN */ | 167 | /* Step 6: Enable WDEN */ |
| 170 | outb_p(inb(swc_base_addr + WDCTL) | 0x01, swc_base_addr + WDCTL); | 168 | outb_p(inb(swc_base_addr + WDCTL) | 0x01, swc_base_addr + WDCTL); |
| @@ -174,7 +172,7 @@ static inline void pc87413_enable_wden(unsigned int swc_base_addr) | |||
| 174 | } | 172 | } |
| 175 | 173 | ||
| 176 | /* Enable SW_WD_TREN */ | 174 | /* Enable SW_WD_TREN */ |
| 177 | static inline void pc87413_enable_sw_wd_tren(unsigned int swc_base_addr) | 175 | static inline void pc87413_enable_sw_wd_tren(void) |
| 178 | { | 176 | { |
| 179 | /* Enable SW_WD_TREN */ | 177 | /* Enable SW_WD_TREN */ |
| 180 | outb_p(inb(swc_base_addr + WDCFG) | 0x80, swc_base_addr + WDCFG); | 178 | outb_p(inb(swc_base_addr + WDCFG) | 0x80, swc_base_addr + WDCFG); |
| @@ -185,7 +183,7 @@ static inline void pc87413_enable_sw_wd_tren(unsigned int swc_base_addr) | |||
| 185 | 183 | ||
| 186 | /* Disable SW_WD_TREN */ | 184 | /* Disable SW_WD_TREN */ |
| 187 | 185 | ||
| 188 | static inline void pc87413_disable_sw_wd_tren(unsigned int swc_base_addr) | 186 | static inline void pc87413_disable_sw_wd_tren(void) |
| 189 | { | 187 | { |
| 190 | /* Disable SW_WD_TREN */ | 188 | /* Disable SW_WD_TREN */ |
| 191 | outb_p(inb(swc_base_addr + WDCFG) & 0x7f, swc_base_addr + WDCFG); | 189 | outb_p(inb(swc_base_addr + WDCFG) & 0x7f, swc_base_addr + WDCFG); |
| @@ -196,7 +194,7 @@ static inline void pc87413_disable_sw_wd_tren(unsigned int swc_base_addr) | |||
| 196 | 194 | ||
| 197 | /* Enable SW_WD_TRG */ | 195 | /* Enable SW_WD_TRG */ |
| 198 | 196 | ||
| 199 | static inline void pc87413_enable_sw_wd_trg(unsigned int swc_base_addr) | 197 | static inline void pc87413_enable_sw_wd_trg(void) |
| 200 | { | 198 | { |
| 201 | /* Enable SW_WD_TRG */ | 199 | /* Enable SW_WD_TRG */ |
| 202 | outb_p(inb(swc_base_addr + WDCTL) | 0x80, swc_base_addr + WDCTL); | 200 | outb_p(inb(swc_base_addr + WDCTL) | 0x80, swc_base_addr + WDCTL); |
| @@ -207,7 +205,7 @@ static inline void pc87413_enable_sw_wd_trg(unsigned int swc_base_addr) | |||
| 207 | 205 | ||
| 208 | /* Disable SW_WD_TRG */ | 206 | /* Disable SW_WD_TRG */ |
| 209 | 207 | ||
| 210 | static inline void pc87413_disable_sw_wd_trg(unsigned int swc_base_addr) | 208 | static inline void pc87413_disable_sw_wd_trg(void) |
| 211 | { | 209 | { |
| 212 | /* Disable SW_WD_TRG */ | 210 | /* Disable SW_WD_TRG */ |
| 213 | outb_p(inb(swc_base_addr + WDCTL) & 0x7f, swc_base_addr + WDCTL); | 211 | outb_p(inb(swc_base_addr + WDCTL) & 0x7f, swc_base_addr + WDCTL); |
| @@ -222,18 +220,13 @@ static inline void pc87413_disable_sw_wd_trg(unsigned int swc_base_addr) | |||
| 222 | 220 | ||
| 223 | static void pc87413_enable(void) | 221 | static void pc87413_enable(void) |
| 224 | { | 222 | { |
| 225 | unsigned int swc_base_addr; | ||
| 226 | |||
| 227 | spin_lock(&io_lock); | 223 | spin_lock(&io_lock); |
| 228 | 224 | ||
| 229 | pc87413_select_wdt_out(); | 225 | pc87413_swc_bank3(); |
| 230 | pc87413_enable_swc(); | 226 | pc87413_programm_wdto(timeout); |
| 231 | swc_base_addr = pc87413_get_swc_base(); | 227 | pc87413_enable_wden(); |
| 232 | pc87413_swc_bank3(swc_base_addr); | 228 | pc87413_enable_sw_wd_tren(); |
| 233 | pc87413_programm_wdto(swc_base_addr, timeout); | 229 | pc87413_enable_sw_wd_trg(); |
| 234 | pc87413_enable_wden(swc_base_addr); | ||
| 235 | pc87413_enable_sw_wd_tren(swc_base_addr); | ||
| 236 | pc87413_enable_sw_wd_trg(swc_base_addr); | ||
| 237 | 230 | ||
| 238 | spin_unlock(&io_lock); | 231 | spin_unlock(&io_lock); |
| 239 | } | 232 | } |
| @@ -242,17 +235,12 @@ static void pc87413_enable(void) | |||
| 242 | 235 | ||
| 243 | static void pc87413_disable(void) | 236 | static void pc87413_disable(void) |
| 244 | { | 237 | { |
| 245 | unsigned int swc_base_addr; | ||
| 246 | |||
| 247 | spin_lock(&io_lock); | 238 | spin_lock(&io_lock); |
| 248 | 239 | ||
| 249 | pc87413_select_wdt_out(); | 240 | pc87413_swc_bank3(); |
| 250 | pc87413_enable_swc(); | 241 | pc87413_disable_sw_wd_tren(); |
| 251 | swc_base_addr = pc87413_get_swc_base(); | 242 | pc87413_disable_sw_wd_trg(); |
| 252 | pc87413_swc_bank3(swc_base_addr); | 243 | pc87413_programm_wdto(0); |
| 253 | pc87413_disable_sw_wd_tren(swc_base_addr); | ||
| 254 | pc87413_disable_sw_wd_trg(swc_base_addr); | ||
| 255 | pc87413_programm_wdto(swc_base_addr, 0); | ||
| 256 | 244 | ||
| 257 | spin_unlock(&io_lock); | 245 | spin_unlock(&io_lock); |
| 258 | } | 246 | } |
| @@ -261,20 +249,15 @@ static void pc87413_disable(void) | |||
| 261 | 249 | ||
| 262 | static void pc87413_refresh(void) | 250 | static void pc87413_refresh(void) |
| 263 | { | 251 | { |
| 264 | unsigned int swc_base_addr; | ||
| 265 | |||
| 266 | spin_lock(&io_lock); | 252 | spin_lock(&io_lock); |
| 267 | 253 | ||
| 268 | pc87413_select_wdt_out(); | 254 | pc87413_swc_bank3(); |
| 269 | pc87413_enable_swc(); | 255 | pc87413_disable_sw_wd_tren(); |
| 270 | swc_base_addr = pc87413_get_swc_base(); | 256 | pc87413_disable_sw_wd_trg(); |
| 271 | pc87413_swc_bank3(swc_base_addr); | 257 | pc87413_programm_wdto(timeout); |
| 272 | pc87413_disable_sw_wd_tren(swc_base_addr); | 258 | pc87413_enable_wden(); |
| 273 | pc87413_disable_sw_wd_trg(swc_base_addr); | 259 | pc87413_enable_sw_wd_tren(); |
| 274 | pc87413_programm_wdto(swc_base_addr, timeout); | 260 | pc87413_enable_sw_wd_trg(); |
| 275 | pc87413_enable_wden(swc_base_addr); | ||
| 276 | pc87413_enable_sw_wd_tren(swc_base_addr); | ||
| 277 | pc87413_enable_sw_wd_trg(swc_base_addr); | ||
| 278 | 261 | ||
| 279 | spin_unlock(&io_lock); | 262 | spin_unlock(&io_lock); |
| 280 | } | 263 | } |
| @@ -528,7 +511,8 @@ static int __init pc87413_init(void) | |||
| 528 | printk(KERN_INFO PFX "Version " VERSION " at io 0x%X\n", | 511 | printk(KERN_INFO PFX "Version " VERSION " at io 0x%X\n", |
| 529 | WDT_INDEX_IO_PORT); | 512 | WDT_INDEX_IO_PORT); |
| 530 | 513 | ||
| 531 | /* request_region(io, 2, "pc87413"); */ | 514 | if (!request_muxed_region(io, 2, MODNAME)) |
| 515 | return -EBUSY; | ||
| 532 | 516 | ||
| 533 | ret = register_reboot_notifier(&pc87413_notifier); | 517 | ret = register_reboot_notifier(&pc87413_notifier); |
| 534 | if (ret != 0) { | 518 | if (ret != 0) { |
| @@ -541,12 +525,32 @@ static int __init pc87413_init(void) | |||
| 541 | printk(KERN_ERR PFX | 525 | printk(KERN_ERR PFX |
| 542 | "cannot register miscdev on minor=%d (err=%d)\n", | 526 | "cannot register miscdev on minor=%d (err=%d)\n", |
| 543 | WATCHDOG_MINOR, ret); | 527 | WATCHDOG_MINOR, ret); |
| 544 | unregister_reboot_notifier(&pc87413_notifier); | 528 | goto reboot_unreg; |
| 545 | return ret; | ||
| 546 | } | 529 | } |
| 547 | printk(KERN_INFO PFX "initialized. timeout=%d min \n", timeout); | 530 | printk(KERN_INFO PFX "initialized. timeout=%d min \n", timeout); |
| 531 | |||
| 532 | pc87413_select_wdt_out(); | ||
| 533 | pc87413_enable_swc(); | ||
| 534 | pc87413_get_swc_base_addr(); | ||
| 535 | |||
| 536 | if (!request_region(swc_base_addr, 0x20, MODNAME)) { | ||
| 537 | printk(KERN_ERR PFX | ||
| 538 | "cannot request SWC region at 0x%x\n", swc_base_addr); | ||
| 539 | ret = -EBUSY; | ||
| 540 | goto misc_unreg; | ||
| 541 | } | ||
| 542 | |||
| 548 | pc87413_enable(); | 543 | pc87413_enable(); |
| 544 | |||
| 545 | release_region(io, 2); | ||
| 549 | return 0; | 546 | return 0; |
| 547 | |||
| 548 | misc_unreg: | ||
| 549 | misc_deregister(&pc87413_miscdev); | ||
| 550 | reboot_unreg: | ||
| 551 | unregister_reboot_notifier(&pc87413_notifier); | ||
| 552 | release_region(io, 2); | ||
| 553 | return ret; | ||
| 550 | } | 554 | } |
| 551 | 555 | ||
| 552 | /** | 556 | /** |
| @@ -569,7 +573,7 @@ static void __exit pc87413_exit(void) | |||
| 569 | 573 | ||
| 570 | misc_deregister(&pc87413_miscdev); | 574 | misc_deregister(&pc87413_miscdev); |
| 571 | unregister_reboot_notifier(&pc87413_notifier); | 575 | unregister_reboot_notifier(&pc87413_notifier); |
| 572 | /* release_region(io, 2); */ | 576 | release_region(swc_base_addr, 0x20); |
| 573 | 577 | ||
| 574 | printk(KERN_INFO MODNAME " watchdog component driver removed.\n"); | 578 | printk(KERN_INFO MODNAME " watchdog component driver removed.\n"); |
| 575 | } | 579 | } |
diff --git a/drivers/watchdog/s3c2410_wdt.c b/drivers/watchdog/s3c2410_wdt.c index f7f5aa00df60..30da88f47cd3 100644 --- a/drivers/watchdog/s3c2410_wdt.c +++ b/drivers/watchdog/s3c2410_wdt.c | |||
| @@ -589,6 +589,15 @@ static int s3c2410wdt_resume(struct platform_device *dev) | |||
| 589 | #define s3c2410wdt_resume NULL | 589 | #define s3c2410wdt_resume NULL |
| 590 | #endif /* CONFIG_PM */ | 590 | #endif /* CONFIG_PM */ |
| 591 | 591 | ||
| 592 | #ifdef CONFIG_OF | ||
| 593 | static const struct of_device_id s3c2410_wdt_match[] = { | ||
| 594 | { .compatible = "samsung,s3c2410-wdt" }, | ||
| 595 | {}, | ||
| 596 | }; | ||
| 597 | MODULE_DEVICE_TABLE(of, s3c2410_wdt_match); | ||
| 598 | #else | ||
| 599 | #define s3c2410_wdt_match NULL | ||
| 600 | #endif | ||
| 592 | 601 | ||
| 593 | static struct platform_driver s3c2410wdt_driver = { | 602 | static struct platform_driver s3c2410wdt_driver = { |
| 594 | .probe = s3c2410wdt_probe, | 603 | .probe = s3c2410wdt_probe, |
| @@ -599,6 +608,7 @@ static struct platform_driver s3c2410wdt_driver = { | |||
| 599 | .driver = { | 608 | .driver = { |
| 600 | .owner = THIS_MODULE, | 609 | .owner = THIS_MODULE, |
| 601 | .name = "s3c2410-wdt", | 610 | .name = "s3c2410-wdt", |
| 611 | .of_match_table = s3c2410_wdt_match, | ||
| 602 | }, | 612 | }, |
| 603 | }; | 613 | }; |
| 604 | 614 | ||
diff --git a/drivers/watchdog/sch311x_wdt.c b/drivers/watchdog/sch311x_wdt.c index c7cf4b01f58d..029467e34636 100644 --- a/drivers/watchdog/sch311x_wdt.c +++ b/drivers/watchdog/sch311x_wdt.c | |||
| @@ -472,15 +472,10 @@ static void sch311x_wdt_shutdown(struct platform_device *dev) | |||
| 472 | sch311x_wdt_stop(); | 472 | sch311x_wdt_stop(); |
| 473 | } | 473 | } |
| 474 | 474 | ||
| 475 | #define sch311x_wdt_suspend NULL | ||
| 476 | #define sch311x_wdt_resume NULL | ||
| 477 | |||
| 478 | static struct platform_driver sch311x_wdt_driver = { | 475 | static struct platform_driver sch311x_wdt_driver = { |
| 479 | .probe = sch311x_wdt_probe, | 476 | .probe = sch311x_wdt_probe, |
| 480 | .remove = __devexit_p(sch311x_wdt_remove), | 477 | .remove = __devexit_p(sch311x_wdt_remove), |
| 481 | .shutdown = sch311x_wdt_shutdown, | 478 | .shutdown = sch311x_wdt_shutdown, |
| 482 | .suspend = sch311x_wdt_suspend, | ||
| 483 | .resume = sch311x_wdt_resume, | ||
| 484 | .driver = { | 479 | .driver = { |
| 485 | .owner = THIS_MODULE, | 480 | .owner = THIS_MODULE, |
| 486 | .name = DRV_NAME, | 481 | .name = DRV_NAME, |
diff --git a/drivers/watchdog/sp805_wdt.c b/drivers/watchdog/sp805_wdt.c index 0d80e08b6439..cc2cfbe33b30 100644 --- a/drivers/watchdog/sp805_wdt.c +++ b/drivers/watchdog/sp805_wdt.c | |||
| @@ -134,6 +134,8 @@ static void wdt_enable(void) | |||
| 134 | writel(INT_ENABLE | RESET_ENABLE, wdt->base + WDTCONTROL); | 134 | writel(INT_ENABLE | RESET_ENABLE, wdt->base + WDTCONTROL); |
| 135 | writel(LOCK, wdt->base + WDTLOCK); | 135 | writel(LOCK, wdt->base + WDTLOCK); |
| 136 | 136 | ||
| 137 | /* Flush posted writes. */ | ||
| 138 | readl(wdt->base + WDTLOCK); | ||
| 137 | spin_unlock(&wdt->lock); | 139 | spin_unlock(&wdt->lock); |
| 138 | } | 140 | } |
| 139 | 141 | ||
| @@ -144,9 +146,10 @@ static void wdt_disable(void) | |||
| 144 | 146 | ||
| 145 | writel(UNLOCK, wdt->base + WDTLOCK); | 147 | writel(UNLOCK, wdt->base + WDTLOCK); |
| 146 | writel(0, wdt->base + WDTCONTROL); | 148 | writel(0, wdt->base + WDTCONTROL); |
| 147 | writel(0, wdt->base + WDTLOAD); | ||
| 148 | writel(LOCK, wdt->base + WDTLOCK); | 149 | writel(LOCK, wdt->base + WDTLOCK); |
| 149 | 150 | ||
| 151 | /* Flush posted writes. */ | ||
| 152 | readl(wdt->base + WDTLOCK); | ||
| 150 | spin_unlock(&wdt->lock); | 153 | spin_unlock(&wdt->lock); |
| 151 | } | 154 | } |
| 152 | 155 | ||
diff --git a/drivers/watchdog/watchdog_core.c b/drivers/watchdog/watchdog_core.c new file mode 100644 index 000000000000..cfa1a1518aad --- /dev/null +++ b/drivers/watchdog/watchdog_core.c | |||
| @@ -0,0 +1,111 @@ | |||
| 1 | /* | ||
| 2 | * watchdog_core.c | ||
| 3 | * | ||
| 4 | * (c) Copyright 2008-2011 Alan Cox <alan@lxorguk.ukuu.org.uk>, | ||
| 5 | * All Rights Reserved. | ||
| 6 | * | ||
| 7 | * (c) Copyright 2008-2011 Wim Van Sebroeck <wim@iguana.be>. | ||
| 8 | * | ||
| 9 | * This source code is part of the generic code that can be used | ||
| 10 | * by all the watchdog timer drivers. | ||
| 11 | * | ||
| 12 | * Based on source code of the following authors: | ||
| 13 | * Matt Domsch <Matt_Domsch@dell.com>, | ||
| 14 | * Rob Radez <rob@osinvestor.com>, | ||
| 15 | * Rusty Lynch <rusty@linux.co.intel.com> | ||
| 16 | * Satyam Sharma <satyam@infradead.org> | ||
| 17 | * Randy Dunlap <randy.dunlap@oracle.com> | ||
| 18 | * | ||
| 19 | * This program is free software; you can redistribute it and/or | ||
| 20 | * modify it under the terms of the GNU General Public License | ||
| 21 | * as published by the Free Software Foundation; either version | ||
| 22 | * 2 of the License, or (at your option) any later version. | ||
| 23 | * | ||
| 24 | * Neither Alan Cox, CymruNet Ltd., Wim Van Sebroeck nor Iguana vzw. | ||
| 25 | * admit liability nor provide warranty for any of this software. | ||
| 26 | * This material is provided "AS-IS" and at no charge. | ||
| 27 | */ | ||
| 28 | |||
| 29 | #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt | ||
| 30 | |||
| 31 | #include <linux/module.h> /* For EXPORT_SYMBOL/module stuff/... */ | ||
| 32 | #include <linux/types.h> /* For standard types */ | ||
| 33 | #include <linux/errno.h> /* For the -ENODEV/... values */ | ||
| 34 | #include <linux/kernel.h> /* For printk/panic/... */ | ||
| 35 | #include <linux/watchdog.h> /* For watchdog specific items */ | ||
| 36 | #include <linux/init.h> /* For __init/__exit/... */ | ||
| 37 | |||
| 38 | #include "watchdog_dev.h" /* For watchdog_dev_register/... */ | ||
| 39 | |||
| 40 | /** | ||
| 41 | * watchdog_register_device() - register a watchdog device | ||
| 42 | * @wdd: watchdog device | ||
| 43 | * | ||
| 44 | * Register a watchdog device with the kernel so that the | ||
| 45 | * watchdog timer can be accessed from userspace. | ||
| 46 | * | ||
| 47 | * A zero is returned on success and a negative errno code for | ||
| 48 | * failure. | ||
| 49 | */ | ||
| 50 | int watchdog_register_device(struct watchdog_device *wdd) | ||
| 51 | { | ||
| 52 | int ret; | ||
| 53 | |||
| 54 | if (wdd == NULL || wdd->info == NULL || wdd->ops == NULL) | ||
| 55 | return -EINVAL; | ||
| 56 | |||
| 57 | /* Mandatory operations need to be supported */ | ||
| 58 | if (wdd->ops->start == NULL || wdd->ops->stop == NULL) | ||
| 59 | return -EINVAL; | ||
| 60 | |||
| 61 | /* | ||
| 62 | * Check that we have valid min and max timeout values, if | ||
| 63 | * not reset them both to 0 (=not used or unknown) | ||
| 64 | */ | ||
| 65 | if (wdd->min_timeout > wdd->max_timeout) { | ||
| 66 | pr_info("Invalid min and max timeout values, resetting to 0!\n"); | ||
| 67 | wdd->min_timeout = 0; | ||
| 68 | wdd->max_timeout = 0; | ||
| 69 | } | ||
| 70 | |||
| 71 | /* | ||
| 72 | * Note: now that all watchdog_device data has been verified, we | ||
| 73 | * will not check this anymore in other functions. If data gets | ||
| 74 | * corrupted in a later stage then we expect a kernel panic! | ||
| 75 | */ | ||
| 76 | |||
| 77 | /* We only support 1 watchdog device via the /dev/watchdog interface */ | ||
| 78 | ret = watchdog_dev_register(wdd); | ||
| 79 | if (ret) { | ||
| 80 | pr_err("error registering /dev/watchdog (err=%d).\n", ret); | ||
| 81 | return ret; | ||
| 82 | } | ||
| 83 | |||
| 84 | return 0; | ||
| 85 | } | ||
| 86 | EXPORT_SYMBOL_GPL(watchdog_register_device); | ||
| 87 | |||
| 88 | /** | ||
| 89 | * watchdog_unregister_device() - unregister a watchdog device | ||
| 90 | * @wdd: watchdog device to unregister | ||
| 91 | * | ||
| 92 | * Unregister a watchdog device that was previously successfully | ||
| 93 | * registered with watchdog_register_device(). | ||
| 94 | */ | ||
| 95 | void watchdog_unregister_device(struct watchdog_device *wdd) | ||
| 96 | { | ||
| 97 | int ret; | ||
| 98 | |||
| 99 | if (wdd == NULL) | ||
| 100 | return; | ||
| 101 | |||
| 102 | ret = watchdog_dev_unregister(wdd); | ||
| 103 | if (ret) | ||
| 104 | pr_err("error unregistering /dev/watchdog (err=%d).\n", ret); | ||
| 105 | } | ||
| 106 | EXPORT_SYMBOL_GPL(watchdog_unregister_device); | ||
| 107 | |||
| 108 | MODULE_AUTHOR("Alan Cox <alan@lxorguk.ukuu.org.uk>"); | ||
| 109 | MODULE_AUTHOR("Wim Van Sebroeck <wim@iguana.be>"); | ||
| 110 | MODULE_DESCRIPTION("WatchDog Timer Driver Core"); | ||
| 111 | MODULE_LICENSE("GPL"); | ||
diff --git a/drivers/watchdog/watchdog_dev.c b/drivers/watchdog/watchdog_dev.c new file mode 100644 index 000000000000..d33520d0b4c9 --- /dev/null +++ b/drivers/watchdog/watchdog_dev.c | |||
| @@ -0,0 +1,395 @@ | |||
| 1 | /* | ||
| 2 | * watchdog_dev.c | ||
| 3 | * | ||
| 4 | * (c) Copyright 2008-2011 Alan Cox <alan@lxorguk.ukuu.org.uk>, | ||
| 5 | * All Rights Reserved. | ||
| 6 | * | ||
| 7 | * (c) Copyright 2008-2011 Wim Van Sebroeck <wim@iguana.be>. | ||
| 8 | * | ||
| 9 | * | ||
| 10 | * This source code is part of the generic code that can be used | ||
| 11 | * by all the watchdog timer drivers. | ||
| 12 | * | ||
| 13 | * This part of the generic code takes care of the following | ||
| 14 | * misc device: /dev/watchdog. | ||
| 15 | * | ||
| 16 | * Based on source code of the following authors: | ||
| 17 | * Matt Domsch <Matt_Domsch@dell.com>, | ||
| 18 | * Rob Radez <rob@osinvestor.com>, | ||
| 19 | * Rusty Lynch <rusty@linux.co.intel.com> | ||
| 20 | * Satyam Sharma <satyam@infradead.org> | ||
| 21 | * Randy Dunlap <randy.dunlap@oracle.com> | ||
| 22 | * | ||
| 23 | * This program is free software; you can redistribute it and/or | ||
| 24 | * modify it under the terms of the GNU General Public License | ||
| 25 | * as published by the Free Software Foundation; either version | ||
| 26 | * 2 of the License, or (at your option) any later version. | ||
| 27 | * | ||
| 28 | * Neither Alan Cox, CymruNet Ltd., Wim Van Sebroeck nor Iguana vzw. | ||
| 29 | * admit liability nor provide warranty for any of this software. | ||
| 30 | * This material is provided "AS-IS" and at no charge. | ||
| 31 | */ | ||
| 32 | |||
| 33 | #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt | ||
| 34 | |||
| 35 | #include <linux/module.h> /* For module stuff/... */ | ||
| 36 | #include <linux/types.h> /* For standard types (like size_t) */ | ||
| 37 | #include <linux/errno.h> /* For the -ENODEV/... values */ | ||
| 38 | #include <linux/kernel.h> /* For printk/panic/... */ | ||
| 39 | #include <linux/fs.h> /* For file operations */ | ||
| 40 | #include <linux/watchdog.h> /* For watchdog specific items */ | ||
| 41 | #include <linux/miscdevice.h> /* For handling misc devices */ | ||
| 42 | #include <linux/init.h> /* For __init/__exit/... */ | ||
| 43 | #include <linux/uaccess.h> /* For copy_to_user/put_user/... */ | ||
| 44 | |||
| 45 | /* make sure we only register one /dev/watchdog device */ | ||
| 46 | static unsigned long watchdog_dev_busy; | ||
| 47 | /* the watchdog device behind /dev/watchdog */ | ||
| 48 | static struct watchdog_device *wdd; | ||
| 49 | |||
| 50 | /* | ||
| 51 | * watchdog_ping: ping the watchdog. | ||
| 52 | * @wddev: the watchdog device to ping | ||
| 53 | * | ||
| 54 | * If the watchdog has no own ping operation then it needs to be | ||
| 55 | * restarted via the start operation. This wrapper function does | ||
| 56 | * exactly that. | ||
| 57 | * We only ping when the watchdog device is running. | ||
| 58 | */ | ||
| 59 | |||
| 60 | static int watchdog_ping(struct watchdog_device *wddev) | ||
| 61 | { | ||
| 62 | if (test_bit(WDOG_ACTIVE, &wdd->status)) { | ||
| 63 | if (wddev->ops->ping) | ||
| 64 | return wddev->ops->ping(wddev); /* ping the watchdog */ | ||
| 65 | else | ||
| 66 | return wddev->ops->start(wddev); /* restart watchdog */ | ||
| 67 | } | ||
| 68 | return 0; | ||
| 69 | } | ||
| 70 | |||
| 71 | /* | ||
| 72 | * watchdog_start: wrapper to start the watchdog. | ||
| 73 | * @wddev: the watchdog device to start | ||
| 74 | * | ||
| 75 | * Start the watchdog if it is not active and mark it active. | ||
| 76 | * This function returns zero on success or a negative errno code for | ||
| 77 | * failure. | ||
| 78 | */ | ||
| 79 | |||
| 80 | static int watchdog_start(struct watchdog_device *wddev) | ||
| 81 | { | ||
| 82 | int err; | ||
| 83 | |||
| 84 | if (!test_bit(WDOG_ACTIVE, &wdd->status)) { | ||
| 85 | err = wddev->ops->start(wddev); | ||
| 86 | if (err < 0) | ||
| 87 | return err; | ||
| 88 | |||
| 89 | set_bit(WDOG_ACTIVE, &wdd->status); | ||
| 90 | } | ||
| 91 | return 0; | ||
| 92 | } | ||
| 93 | |||
| 94 | /* | ||
| 95 | * watchdog_stop: wrapper to stop the watchdog. | ||
| 96 | * @wddev: the watchdog device to stop | ||
| 97 | * | ||
| 98 | * Stop the watchdog if it is still active and unmark it active. | ||
| 99 | * This function returns zero on success or a negative errno code for | ||
| 100 | * failure. | ||
| 101 | * If the 'nowayout' feature was set, the watchdog cannot be stopped. | ||
| 102 | */ | ||
| 103 | |||
| 104 | static int watchdog_stop(struct watchdog_device *wddev) | ||
| 105 | { | ||
| 106 | int err = -EBUSY; | ||
| 107 | |||
| 108 | if (test_bit(WDOG_NO_WAY_OUT, &wdd->status)) { | ||
| 109 | pr_info("%s: nowayout prevents watchdog to be stopped!\n", | ||
| 110 | wdd->info->identity); | ||
| 111 | return err; | ||
| 112 | } | ||
| 113 | |||
| 114 | if (test_bit(WDOG_ACTIVE, &wdd->status)) { | ||
| 115 | err = wddev->ops->stop(wddev); | ||
| 116 | if (err < 0) | ||
| 117 | return err; | ||
| 118 | |||
| 119 | clear_bit(WDOG_ACTIVE, &wdd->status); | ||
| 120 | } | ||
| 121 | return 0; | ||
| 122 | } | ||
| 123 | |||
| 124 | /* | ||
| 125 | * watchdog_write: writes to the watchdog. | ||
| 126 | * @file: file from VFS | ||
| 127 | * @data: user address of data | ||
| 128 | * @len: length of data | ||
| 129 | * @ppos: pointer to the file offset | ||
| 130 | * | ||
| 131 | * A write to a watchdog device is defined as a keepalive ping. | ||
| 132 | * Writing the magic 'V' sequence allows the next close to turn | ||
| 133 | * off the watchdog (if 'nowayout' is not set). | ||
| 134 | */ | ||
| 135 | |||
| 136 | static ssize_t watchdog_write(struct file *file, const char __user *data, | ||
| 137 | size_t len, loff_t *ppos) | ||
| 138 | { | ||
| 139 | size_t i; | ||
| 140 | char c; | ||
| 141 | |||
| 142 | if (len == 0) | ||
| 143 | return 0; | ||
| 144 | |||
| 145 | /* | ||
| 146 | * Note: just in case someone wrote the magic character | ||
| 147 | * five months ago... | ||
| 148 | */ | ||
| 149 | clear_bit(WDOG_ALLOW_RELEASE, &wdd->status); | ||
| 150 | |||
| 151 | /* scan to see whether or not we got the magic character */ | ||
| 152 | for (i = 0; i != len; i++) { | ||
| 153 | if (get_user(c, data + i)) | ||
| 154 | return -EFAULT; | ||
| 155 | if (c == 'V') | ||
| 156 | set_bit(WDOG_ALLOW_RELEASE, &wdd->status); | ||
| 157 | } | ||
| 158 | |||
| 159 | /* someone wrote to us, so we send the watchdog a keepalive ping */ | ||
| 160 | watchdog_ping(wdd); | ||
| 161 | |||
| 162 | return len; | ||
| 163 | } | ||
| 164 | |||
| 165 | /* | ||
| 166 | * watchdog_ioctl: handle the different ioctl's for the watchdog device. | ||
| 167 | * @file: file handle to the device | ||
| 168 | * @cmd: watchdog command | ||
| 169 | * @arg: argument pointer | ||
| 170 | * | ||
| 171 | * The watchdog API defines a common set of functions for all watchdogs | ||
| 172 | * according to their available features. | ||
| 173 | */ | ||
| 174 | |||
| 175 | static long watchdog_ioctl(struct file *file, unsigned int cmd, | ||
| 176 | unsigned long arg) | ||
| 177 | { | ||
| 178 | void __user *argp = (void __user *)arg; | ||
| 179 | int __user *p = argp; | ||
| 180 | unsigned int val; | ||
| 181 | int err; | ||
| 182 | |||
| 183 | if (wdd->ops->ioctl) { | ||
| 184 | err = wdd->ops->ioctl(wdd, cmd, arg); | ||
| 185 | if (err != -ENOIOCTLCMD) | ||
| 186 | return err; | ||
| 187 | } | ||
| 188 | |||
| 189 | switch (cmd) { | ||
| 190 | case WDIOC_GETSUPPORT: | ||
| 191 | return copy_to_user(argp, wdd->info, | ||
| 192 | sizeof(struct watchdog_info)) ? -EFAULT : 0; | ||
| 193 | case WDIOC_GETSTATUS: | ||
| 194 | val = wdd->ops->status ? wdd->ops->status(wdd) : 0; | ||
| 195 | return put_user(val, p); | ||
| 196 | case WDIOC_GETBOOTSTATUS: | ||
| 197 | return put_user(wdd->bootstatus, p); | ||
| 198 | case WDIOC_SETOPTIONS: | ||
| 199 | if (get_user(val, p)) | ||
| 200 | return -EFAULT; | ||
| 201 | if (val & WDIOS_DISABLECARD) { | ||
| 202 | err = watchdog_stop(wdd); | ||
| 203 | if (err < 0) | ||
| 204 | return err; | ||
| 205 | } | ||
| 206 | if (val & WDIOS_ENABLECARD) { | ||
| 207 | err = watchdog_start(wdd); | ||
| 208 | if (err < 0) | ||
| 209 | return err; | ||
| 210 | } | ||
| 211 | return 0; | ||
| 212 | case WDIOC_KEEPALIVE: | ||
| 213 | if (!(wdd->info->options & WDIOF_KEEPALIVEPING)) | ||
| 214 | return -EOPNOTSUPP; | ||
| 215 | watchdog_ping(wdd); | ||
| 216 | return 0; | ||
| 217 | case WDIOC_SETTIMEOUT: | ||
| 218 | if ((wdd->ops->set_timeout == NULL) || | ||
| 219 | !(wdd->info->options & WDIOF_SETTIMEOUT)) | ||
| 220 | return -EOPNOTSUPP; | ||
| 221 | if (get_user(val, p)) | ||
| 222 | return -EFAULT; | ||
| 223 | if ((wdd->max_timeout != 0) && | ||
| 224 | (val < wdd->min_timeout || val > wdd->max_timeout)) | ||
| 225 | return -EINVAL; | ||
| 226 | err = wdd->ops->set_timeout(wdd, val); | ||
| 227 | if (err < 0) | ||
| 228 | return err; | ||
| 229 | wdd->timeout = val; | ||
| 230 | /* If the watchdog is active then we send a keepalive ping | ||
| 231 | * to make sure that the watchdog keep's running (and if | ||
| 232 | * possible that it takes the new timeout) */ | ||
| 233 | watchdog_ping(wdd); | ||
| 234 | /* Fall */ | ||
| 235 | case WDIOC_GETTIMEOUT: | ||
| 236 | /* timeout == 0 means that we don't know the timeout */ | ||
| 237 | if (wdd->timeout == 0) | ||
| 238 | return -EOPNOTSUPP; | ||
| 239 | return put_user(wdd->timeout, p); | ||
| 240 | default: | ||
| 241 | return -ENOTTY; | ||
| 242 | } | ||
| 243 | } | ||
| 244 | |||
| 245 | /* | ||
| 246 | * watchdog_open: open the /dev/watchdog device. | ||
| 247 | * @inode: inode of device | ||
| 248 | * @file: file handle to device | ||
| 249 | * | ||
| 250 | * When the /dev/watchdog device gets opened, we start the watchdog. | ||
| 251 | * Watch out: the /dev/watchdog device is single open, so we make sure | ||
| 252 | * it can only be opened once. | ||
| 253 | */ | ||
| 254 | |||
| 255 | static int watchdog_open(struct inode *inode, struct file *file) | ||
| 256 | { | ||
| 257 | int err = -EBUSY; | ||
| 258 | |||
| 259 | /* the watchdog is single open! */ | ||
| 260 | if (test_and_set_bit(WDOG_DEV_OPEN, &wdd->status)) | ||
| 261 | return -EBUSY; | ||
| 262 | |||
| 263 | /* | ||
| 264 | * If the /dev/watchdog device is open, we don't want the module | ||
| 265 | * to be unloaded. | ||
| 266 | */ | ||
| 267 | if (!try_module_get(wdd->ops->owner)) | ||
| 268 | goto out; | ||
| 269 | |||
| 270 | err = watchdog_start(wdd); | ||
| 271 | if (err < 0) | ||
| 272 | goto out_mod; | ||
| 273 | |||
| 274 | /* dev/watchdog is a virtual (and thus non-seekable) filesystem */ | ||
| 275 | return nonseekable_open(inode, file); | ||
| 276 | |||
| 277 | out_mod: | ||
| 278 | module_put(wdd->ops->owner); | ||
| 279 | out: | ||
| 280 | clear_bit(WDOG_DEV_OPEN, &wdd->status); | ||
| 281 | return err; | ||
| 282 | } | ||
| 283 | |||
| 284 | /* | ||
| 285 | * watchdog_release: release the /dev/watchdog device. | ||
| 286 | * @inode: inode of device | ||
| 287 | * @file: file handle to device | ||
| 288 | * | ||
| 289 | * This is the code for when /dev/watchdog gets closed. We will only | ||
| 290 | * stop the watchdog when we have received the magic char (and nowayout | ||
| 291 | * was not set), else the watchdog will keep running. | ||
| 292 | */ | ||
| 293 | |||
| 294 | static int watchdog_release(struct inode *inode, struct file *file) | ||
| 295 | { | ||
| 296 | int err = -EBUSY; | ||
| 297 | |||
| 298 | /* | ||
| 299 | * We only stop the watchdog if we received the magic character | ||
| 300 | * or if WDIOF_MAGICCLOSE is not set. If nowayout was set then | ||
| 301 | * watchdog_stop will fail. | ||
| 302 | */ | ||
| 303 | if (test_and_clear_bit(WDOG_ALLOW_RELEASE, &wdd->status) || | ||
| 304 | !(wdd->info->options & WDIOF_MAGICCLOSE)) | ||
| 305 | err = watchdog_stop(wdd); | ||
| 306 | |||
| 307 | /* If the watchdog was not stopped, send a keepalive ping */ | ||
| 308 | if (err < 0) { | ||
| 309 | pr_crit("%s: watchdog did not stop!\n", wdd->info->identity); | ||
| 310 | watchdog_ping(wdd); | ||
| 311 | } | ||
| 312 | |||
| 313 | /* Allow the owner module to be unloaded again */ | ||
| 314 | module_put(wdd->ops->owner); | ||
| 315 | |||
| 316 | /* make sure that /dev/watchdog can be re-opened */ | ||
| 317 | clear_bit(WDOG_DEV_OPEN, &wdd->status); | ||
| 318 | |||
| 319 | return 0; | ||
| 320 | } | ||
| 321 | |||
| 322 | static const struct file_operations watchdog_fops = { | ||
| 323 | .owner = THIS_MODULE, | ||
| 324 | .write = watchdog_write, | ||
| 325 | .unlocked_ioctl = watchdog_ioctl, | ||
| 326 | .open = watchdog_open, | ||
| 327 | .release = watchdog_release, | ||
| 328 | }; | ||
| 329 | |||
| 330 | static struct miscdevice watchdog_miscdev = { | ||
| 331 | .minor = WATCHDOG_MINOR, | ||
| 332 | .name = "watchdog", | ||
| 333 | .fops = &watchdog_fops, | ||
| 334 | }; | ||
| 335 | |||
| 336 | /* | ||
| 337 | * watchdog_dev_register: | ||
| 338 | * @watchdog: watchdog device | ||
| 339 | * | ||
| 340 | * Register a watchdog device as /dev/watchdog. /dev/watchdog | ||
| 341 | * is actually a miscdevice and thus we set it up like that. | ||
| 342 | */ | ||
| 343 | |||
| 344 | int watchdog_dev_register(struct watchdog_device *watchdog) | ||
| 345 | { | ||
| 346 | int err; | ||
| 347 | |||
| 348 | /* Only one device can register for /dev/watchdog */ | ||
| 349 | if (test_and_set_bit(0, &watchdog_dev_busy)) { | ||
| 350 | pr_err("only one watchdog can use /dev/watchdog.\n"); | ||
| 351 | return -EBUSY; | ||
| 352 | } | ||
| 353 | |||
| 354 | wdd = watchdog; | ||
| 355 | |||
| 356 | err = misc_register(&watchdog_miscdev); | ||
| 357 | if (err != 0) { | ||
| 358 | pr_err("%s: cannot register miscdev on minor=%d (err=%d).\n", | ||
| 359 | watchdog->info->identity, WATCHDOG_MINOR, err); | ||
| 360 | goto out; | ||
| 361 | } | ||
| 362 | |||
| 363 | return 0; | ||
| 364 | |||
| 365 | out: | ||
| 366 | wdd = NULL; | ||
| 367 | clear_bit(0, &watchdog_dev_busy); | ||
| 368 | return err; | ||
| 369 | } | ||
| 370 | |||
| 371 | /* | ||
| 372 | * watchdog_dev_unregister: | ||
| 373 | * @watchdog: watchdog device | ||
| 374 | * | ||
| 375 | * Deregister the /dev/watchdog device. | ||
| 376 | */ | ||
| 377 | |||
| 378 | int watchdog_dev_unregister(struct watchdog_device *watchdog) | ||
| 379 | { | ||
| 380 | /* Check that a watchdog device was registered in the past */ | ||
| 381 | if (!test_bit(0, &watchdog_dev_busy) || !wdd) | ||
| 382 | return -ENODEV; | ||
| 383 | |||
| 384 | /* We can only unregister the watchdog device that was registered */ | ||
| 385 | if (watchdog != wdd) { | ||
| 386 | pr_err("%s: watchdog was not registered as /dev/watchdog.\n", | ||
| 387 | watchdog->info->identity); | ||
| 388 | return -ENODEV; | ||
| 389 | } | ||
| 390 | |||
| 391 | misc_deregister(&watchdog_miscdev); | ||
| 392 | wdd = NULL; | ||
| 393 | clear_bit(0, &watchdog_dev_busy); | ||
| 394 | return 0; | ||
| 395 | } | ||
diff --git a/drivers/watchdog/watchdog_dev.h b/drivers/watchdog/watchdog_dev.h new file mode 100644 index 000000000000..bc7612be25ce --- /dev/null +++ b/drivers/watchdog/watchdog_dev.h | |||
| @@ -0,0 +1,33 @@ | |||
| 1 | /* | ||
| 2 | * watchdog_core.h | ||
| 3 | * | ||
| 4 | * (c) Copyright 2008-2011 Alan Cox <alan@lxorguk.ukuu.org.uk>, | ||
| 5 | * All Rights Reserved. | ||
| 6 | * | ||
| 7 | * (c) Copyright 2008-2011 Wim Van Sebroeck <wim@iguana.be>. | ||
| 8 | * | ||
| 9 | * This source code is part of the generic code that can be used | ||
| 10 | * by all the watchdog timer drivers. | ||
| 11 | * | ||
| 12 | * Based on source code of the following authors: | ||
| 13 | * Matt Domsch <Matt_Domsch@dell.com>, | ||
| 14 | * Rob Radez <rob@osinvestor.com>, | ||
| 15 | * Rusty Lynch <rusty@linux.co.intel.com> | ||
| 16 | * Satyam Sharma <satyam@infradead.org> | ||
| 17 | * Randy Dunlap <randy.dunlap@oracle.com> | ||
| 18 | * | ||
| 19 | * This program is free software; you can redistribute it and/or | ||
| 20 | * modify it under the terms of the GNU General Public License | ||
| 21 | * as published by the Free Software Foundation; either version | ||
| 22 | * 2 of the License, or (at your option) any later version. | ||
| 23 | * | ||
| 24 | * Neither Alan Cox, CymruNet Ltd., Wim Van Sebroeck nor Iguana vzw. | ||
| 25 | * admit liability nor provide warranty for any of this software. | ||
| 26 | * This material is provided "AS-IS" and at no charge. | ||
| 27 | */ | ||
| 28 | |||
| 29 | /* | ||
| 30 | * Functions/procedures to be called by the core | ||
| 31 | */ | ||
| 32 | int watchdog_dev_register(struct watchdog_device *); | ||
| 33 | int watchdog_dev_unregister(struct watchdog_device *); | ||
diff --git a/include/linux/watchdog.h b/include/linux/watchdog.h index 011bcfeb9f09..111843f88b2a 100644 --- a/include/linux/watchdog.h +++ b/include/linux/watchdog.h | |||
| @@ -59,6 +59,84 @@ struct watchdog_info { | |||
| 59 | #define WATCHDOG_NOWAYOUT 0 | 59 | #define WATCHDOG_NOWAYOUT 0 |
| 60 | #endif | 60 | #endif |
| 61 | 61 | ||
| 62 | struct watchdog_ops; | ||
| 63 | struct watchdog_device; | ||
| 64 | |||
| 65 | /** struct watchdog_ops - The watchdog-devices operations | ||
| 66 | * | ||
| 67 | * @owner: The module owner. | ||
| 68 | * @start: The routine for starting the watchdog device. | ||
| 69 | * @stop: The routine for stopping the watchdog device. | ||
| 70 | * @ping: The routine that sends a keepalive ping to the watchdog device. | ||
| 71 | * @status: The routine that shows the status of the watchdog device. | ||
| 72 | * @set_timeout:The routine for setting the watchdog devices timeout value. | ||
| 73 | * @ioctl: The routines that handles extra ioctl calls. | ||
| 74 | * | ||
| 75 | * The watchdog_ops structure contains a list of low-level operations | ||
| 76 | * that control a watchdog device. It also contains the module that owns | ||
| 77 | * these operations. The start and stop function are mandatory, all other | ||
| 78 | * functions are optonal. | ||
| 79 | */ | ||
| 80 | struct watchdog_ops { | ||
| 81 | struct module *owner; | ||
| 82 | /* mandatory operations */ | ||
| 83 | int (*start)(struct watchdog_device *); | ||
| 84 | int (*stop)(struct watchdog_device *); | ||
| 85 | /* optional operations */ | ||
| 86 | int (*ping)(struct watchdog_device *); | ||
| 87 | unsigned int (*status)(struct watchdog_device *); | ||
| 88 | int (*set_timeout)(struct watchdog_device *, unsigned int); | ||
| 89 | long (*ioctl)(struct watchdog_device *, unsigned int, unsigned long); | ||
| 90 | }; | ||
| 91 | |||
| 92 | /** struct watchdog_device - The structure that defines a watchdog device | ||
| 93 | * | ||
| 94 | * @info: Pointer to a watchdog_info structure. | ||
| 95 | * @ops: Pointer to the list of watchdog operations. | ||
| 96 | * @bootstatus: Status of the watchdog device at boot. | ||
| 97 | * @timeout: The watchdog devices timeout value. | ||
| 98 | * @min_timeout:The watchdog devices minimum timeout value. | ||
| 99 | * @max_timeout:The watchdog devices maximum timeout value. | ||
| 100 | * @driver-data:Pointer to the drivers private data. | ||
| 101 | * @status: Field that contains the devices internal status bits. | ||
| 102 | * | ||
| 103 | * The watchdog_device structure contains all information about a | ||
| 104 | * watchdog timer device. | ||
| 105 | * | ||
| 106 | * The driver-data field may not be accessed directly. It must be accessed | ||
| 107 | * via the watchdog_set_drvdata and watchdog_get_drvdata helpers. | ||
| 108 | */ | ||
| 109 | struct watchdog_device { | ||
| 110 | const struct watchdog_info *info; | ||
| 111 | const struct watchdog_ops *ops; | ||
| 112 | unsigned int bootstatus; | ||
| 113 | unsigned int timeout; | ||
| 114 | unsigned int min_timeout; | ||
| 115 | unsigned int max_timeout; | ||
| 116 | void *driver_data; | ||
| 117 | unsigned long status; | ||
| 118 | /* Bit numbers for status flags */ | ||
| 119 | #define WDOG_ACTIVE 0 /* Is the watchdog running/active */ | ||
| 120 | #define WDOG_DEV_OPEN 1 /* Opened via /dev/watchdog ? */ | ||
| 121 | #define WDOG_ALLOW_RELEASE 2 /* Did we receive the magic char ? */ | ||
| 122 | #define WDOG_NO_WAY_OUT 3 /* Is 'nowayout' feature set ? */ | ||
| 123 | }; | ||
| 124 | |||
| 125 | /* Use the following functions to manipulate watchdog driver specific data */ | ||
| 126 | static inline void watchdog_set_drvdata(struct watchdog_device *wdd, void *data) | ||
| 127 | { | ||
| 128 | wdd->driver_data = data; | ||
| 129 | } | ||
| 130 | |||
| 131 | static inline void *watchdog_get_drvdata(struct watchdog_device *wdd) | ||
| 132 | { | ||
| 133 | return wdd->driver_data; | ||
| 134 | } | ||
| 135 | |||
| 136 | /* drivers/watchdog/core/watchdog_core.c */ | ||
| 137 | extern int watchdog_register_device(struct watchdog_device *); | ||
| 138 | extern void watchdog_unregister_device(struct watchdog_device *); | ||
| 139 | |||
| 62 | #endif /* __KERNEL__ */ | 140 | #endif /* __KERNEL__ */ |
| 63 | 141 | ||
| 64 | #endif /* ifndef _LINUX_WATCHDOG_H */ | 142 | #endif /* ifndef _LINUX_WATCHDOG_H */ |
