diff options
| -rw-r--r-- | arch/arm/mach-orion5x/include/mach/orion5x.h | 2 | ||||
| -rw-r--r-- | arch/arm/plat-omap/devices.c | 21 | ||||
| -rw-r--r-- | drivers/watchdog/Kconfig | 43 | ||||
| -rw-r--r-- | drivers/watchdog/Makefile | 4 | ||||
| -rw-r--r-- | drivers/watchdog/at91sam9_wdt.c | 328 | ||||
| -rw-r--r-- | drivers/watchdog/it87_wdt.c | 725 | ||||
| -rw-r--r-- | drivers/watchdog/omap_wdt.c | 337 | ||||
| -rw-r--r-- | drivers/watchdog/omap_wdt.h | 28 | ||||
| -rw-r--r-- | drivers/watchdog/orion5x_wdt.c | 245 | ||||
| -rw-r--r-- | drivers/watchdog/w83697ug_wdt.c | 392 |
10 files changed, 1985 insertions, 140 deletions
diff --git a/arch/arm/mach-orion5x/include/mach/orion5x.h b/arch/arm/mach-orion5x/include/mach/orion5x.h index e67c843baa02..9f5ce1ce5840 100644 --- a/arch/arm/mach-orion5x/include/mach/orion5x.h +++ b/arch/arm/mach-orion5x/include/mach/orion5x.h | |||
| @@ -157,9 +157,11 @@ | |||
| 157 | #define CPU_CONF ORION5X_BRIDGE_REG(0x100) | 157 | #define CPU_CONF ORION5X_BRIDGE_REG(0x100) |
| 158 | #define CPU_CTRL ORION5X_BRIDGE_REG(0x104) | 158 | #define CPU_CTRL ORION5X_BRIDGE_REG(0x104) |
| 159 | #define CPU_RESET_MASK ORION5X_BRIDGE_REG(0x108) | 159 | #define CPU_RESET_MASK ORION5X_BRIDGE_REG(0x108) |
| 160 | #define WDT_RESET 0x0002 | ||
| 160 | #define CPU_SOFT_RESET ORION5X_BRIDGE_REG(0x10c) | 161 | #define CPU_SOFT_RESET ORION5X_BRIDGE_REG(0x10c) |
| 161 | #define POWER_MNG_CTRL_REG ORION5X_BRIDGE_REG(0x11C) | 162 | #define POWER_MNG_CTRL_REG ORION5X_BRIDGE_REG(0x11C) |
| 162 | #define BRIDGE_CAUSE ORION5X_BRIDGE_REG(0x110) | 163 | #define BRIDGE_CAUSE ORION5X_BRIDGE_REG(0x110) |
| 164 | #define WDT_INT_REQ 0x0008 | ||
| 163 | #define BRIDGE_MASK ORION5X_BRIDGE_REG(0x114) | 165 | #define BRIDGE_MASK ORION5X_BRIDGE_REG(0x114) |
| 164 | #define BRIDGE_INT_TIMER0 0x0002 | 166 | #define BRIDGE_INT_TIMER0 0x0002 |
| 165 | #define BRIDGE_INT_TIMER1 0x0004 | 167 | #define BRIDGE_INT_TIMER1 0x0004 |
diff --git a/arch/arm/plat-omap/devices.c b/arch/arm/plat-omap/devices.c index a716ecd1db27..97187fa0ae52 100644 --- a/arch/arm/plat-omap/devices.c +++ b/arch/arm/plat-omap/devices.c | |||
| @@ -441,16 +441,8 @@ static inline void omap_init_uwire(void) {} | |||
| 441 | 441 | ||
| 442 | #if defined(CONFIG_OMAP_WATCHDOG) || defined(CONFIG_OMAP_WATCHDOG_MODULE) | 442 | #if defined(CONFIG_OMAP_WATCHDOG) || defined(CONFIG_OMAP_WATCHDOG_MODULE) |
| 443 | 443 | ||
| 444 | #ifdef CONFIG_ARCH_OMAP24XX | ||
| 445 | #define OMAP_WDT_BASE 0x48022000 | ||
| 446 | #else | ||
| 447 | #define OMAP_WDT_BASE 0xfffeb000 | ||
| 448 | #endif | ||
| 449 | |||
| 450 | static struct resource wdt_resources[] = { | 444 | static struct resource wdt_resources[] = { |
| 451 | { | 445 | { |
| 452 | .start = OMAP_WDT_BASE, | ||
| 453 | .end = OMAP_WDT_BASE + 0x4f, | ||
| 454 | .flags = IORESOURCE_MEM, | 446 | .flags = IORESOURCE_MEM, |
| 455 | }, | 447 | }, |
| 456 | }; | 448 | }; |
| @@ -464,6 +456,19 @@ static struct platform_device omap_wdt_device = { | |||
| 464 | 456 | ||
| 465 | static void omap_init_wdt(void) | 457 | static void omap_init_wdt(void) |
| 466 | { | 458 | { |
| 459 | if (cpu_is_omap16xx()) | ||
| 460 | wdt_resources[0].start = 0xfffeb000; | ||
| 461 | else if (cpu_is_omap2420()) | ||
| 462 | wdt_resources[0].start = 0x48022000; /* WDT2 */ | ||
| 463 | else if (cpu_is_omap2430()) | ||
| 464 | wdt_resources[0].start = 0x49016000; /* WDT2 */ | ||
| 465 | else if (cpu_is_omap343x()) | ||
| 466 | wdt_resources[0].start = 0x48314000; /* WDT2 */ | ||
| 467 | else | ||
| 468 | return; | ||
| 469 | |||
| 470 | wdt_resources[0].end = wdt_resources[0].start + 0x4f; | ||
| 471 | |||
| 467 | (void) platform_device_register(&omap_wdt_device); | 472 | (void) platform_device_register(&omap_wdt_device); |
| 468 | } | 473 | } |
| 469 | #else | 474 | #else |
diff --git a/drivers/watchdog/Kconfig b/drivers/watchdog/Kconfig index c51036716700..1a22fe782a27 100644 --- a/drivers/watchdog/Kconfig +++ b/drivers/watchdog/Kconfig | |||
| @@ -66,6 +66,13 @@ config AT91RM9200_WATCHDOG | |||
| 66 | Watchdog timer embedded into AT91RM9200 chips. This will reboot your | 66 | Watchdog timer embedded into AT91RM9200 chips. This will reboot your |
| 67 | system when the timeout is reached. | 67 | system when the timeout is reached. |
| 68 | 68 | ||
| 69 | config AT91SAM9X_WATCHDOG | ||
| 70 | tristate "AT91SAM9X watchdog" | ||
| 71 | depends on WATCHDOG && (ARCH_AT91SAM9260 || ARCH_AT91SAM9261) | ||
| 72 | help | ||
| 73 | Watchdog timer embedded into AT91SAM9X chips. This will reboot your | ||
| 74 | system when the timeout is reached. | ||
| 75 | |||
| 69 | config 21285_WATCHDOG | 76 | config 21285_WATCHDOG |
| 70 | tristate "DC21285 watchdog" | 77 | tristate "DC21285 watchdog" |
| 71 | depends on FOOTBRIDGE | 78 | depends on FOOTBRIDGE |
| @@ -217,6 +224,15 @@ config DAVINCI_WATCHDOG | |||
| 217 | NOTE: once enabled, this timer cannot be disabled. | 224 | NOTE: once enabled, this timer cannot be disabled. |
| 218 | Say N if you are unsure. | 225 | Say N if you are unsure. |
| 219 | 226 | ||
| 227 | config ORION5X_WATCHDOG | ||
| 228 | tristate "Orion5x watchdog" | ||
| 229 | depends on ARCH_ORION5X | ||
| 230 | help | ||
| 231 | Say Y here if to include support for the watchdog timer | ||
| 232 | in the Orion5x ARM SoCs. | ||
| 233 | To compile this driver as a module, choose M here: the | ||
| 234 | module will be called orion5x_wdt. | ||
| 235 | |||
| 220 | # ARM26 Architecture | 236 | # ARM26 Architecture |
| 221 | 237 | ||
| 222 | # AVR32 Architecture | 238 | # AVR32 Architecture |
| @@ -416,6 +432,18 @@ config IT8712F_WDT | |||
| 416 | To compile this driver as a module, choose M here: the | 432 | To compile this driver as a module, choose M here: the |
| 417 | module will be called it8712f_wdt. | 433 | module will be called it8712f_wdt. |
| 418 | 434 | ||
| 435 | config IT87_WDT | ||
| 436 | tristate "IT87 Watchdog Timer" | ||
| 437 | depends on X86 && EXPERIMENTAL | ||
| 438 | ---help--- | ||
| 439 | This is the driver for the hardware watchdog on the ITE IT8716, | ||
| 440 | IT8718, IT8726, IT8712(Version J,K) Super I/O chips. This watchdog | ||
| 441 | simply watches your kernel to make sure it doesn't freeze, and if | ||
| 442 | it does, it reboots your computer after a certain amount of time. | ||
| 443 | |||
| 444 | To compile this driver as a module, choose M here: the module will | ||
| 445 | be called it87_wdt. | ||
| 446 | |||
| 419 | config HP_WATCHDOG | 447 | config HP_WATCHDOG |
| 420 | tristate "HP Proliant iLO 2 Hardware Watchdog Timer" | 448 | tristate "HP Proliant iLO 2 Hardware Watchdog Timer" |
| 421 | depends on X86 | 449 | depends on X86 |
| @@ -573,6 +601,21 @@ config W83697HF_WDT | |||
| 573 | 601 | ||
| 574 | Most people will say N. | 602 | Most people will say N. |
| 575 | 603 | ||
| 604 | config W83697UG_WDT | ||
| 605 | tristate "W83697UG/W83697UF Watchdog Timer" | ||
| 606 | depends on X86 | ||
| 607 | ---help--- | ||
| 608 | This is the driver for the hardware watchdog on the W83697UG/UF | ||
| 609 | chipset as used in MSI Fuzzy CX700 VIA motherboards (and likely others). | ||
| 610 | This watchdog simply watches your kernel to make sure it doesn't | ||
| 611 | freeze, and if it does, it reboots your computer after a certain | ||
| 612 | amount of time. | ||
| 613 | |||
| 614 | To compile this driver as a module, choose M here: the | ||
| 615 | module will be called w83697ug_wdt. | ||
| 616 | |||
| 617 | Most people will say N. | ||
| 618 | |||
| 576 | config W83877F_WDT | 619 | config W83877F_WDT |
| 577 | tristate "W83877F (EMACS) Watchdog Timer" | 620 | tristate "W83877F (EMACS) Watchdog Timer" |
| 578 | depends on X86 | 621 | depends on X86 |
diff --git a/drivers/watchdog/Makefile b/drivers/watchdog/Makefile index 6702d2ef0434..e352bbb7630b 100644 --- a/drivers/watchdog/Makefile +++ b/drivers/watchdog/Makefile | |||
| @@ -26,6 +26,7 @@ obj-$(CONFIG_USBPCWATCHDOG) += pcwd_usb.o | |||
| 26 | 26 | ||
| 27 | # ARM Architecture | 27 | # ARM Architecture |
| 28 | obj-$(CONFIG_AT91RM9200_WATCHDOG) += at91rm9200_wdt.o | 28 | obj-$(CONFIG_AT91RM9200_WATCHDOG) += at91rm9200_wdt.o |
| 29 | obj-$(CONFIG_AT91SAM9X_WATCHDOG) += at91sam9_wdt.o | ||
| 29 | obj-$(CONFIG_OMAP_WATCHDOG) += omap_wdt.o | 30 | obj-$(CONFIG_OMAP_WATCHDOG) += omap_wdt.o |
| 30 | obj-$(CONFIG_21285_WATCHDOG) += wdt285.o | 31 | obj-$(CONFIG_21285_WATCHDOG) += wdt285.o |
| 31 | obj-$(CONFIG_977_WATCHDOG) += wdt977.o | 32 | obj-$(CONFIG_977_WATCHDOG) += wdt977.o |
| @@ -39,6 +40,7 @@ obj-$(CONFIG_EP93XX_WATCHDOG) += ep93xx_wdt.o | |||
| 39 | obj-$(CONFIG_PNX4008_WATCHDOG) += pnx4008_wdt.o | 40 | obj-$(CONFIG_PNX4008_WATCHDOG) += pnx4008_wdt.o |
| 40 | obj-$(CONFIG_IOP_WATCHDOG) += iop_wdt.o | 41 | obj-$(CONFIG_IOP_WATCHDOG) += iop_wdt.o |
| 41 | obj-$(CONFIG_DAVINCI_WATCHDOG) += davinci_wdt.o | 42 | obj-$(CONFIG_DAVINCI_WATCHDOG) += davinci_wdt.o |
| 43 | obj-$(CONFIG_ORION5X_WATCHDOG) += orion5x_wdt.o | ||
| 42 | 44 | ||
| 43 | # ARM26 Architecture | 45 | # ARM26 Architecture |
| 44 | 46 | ||
| @@ -71,6 +73,7 @@ ifeq ($(CONFIG_ITCO_VENDOR_SUPPORT),y) | |||
| 71 | obj-$(CONFIG_ITCO_WDT) += iTCO_vendor_support.o | 73 | obj-$(CONFIG_ITCO_WDT) += iTCO_vendor_support.o |
| 72 | endif | 74 | endif |
| 73 | obj-$(CONFIG_IT8712F_WDT) += it8712f_wdt.o | 75 | obj-$(CONFIG_IT8712F_WDT) += it8712f_wdt.o |
| 76 | obj-$(CONFIG_IT87_WDT) += it87_wdt.o | ||
| 74 | obj-$(CONFIG_HP_WATCHDOG) += hpwdt.o | 77 | obj-$(CONFIG_HP_WATCHDOG) += hpwdt.o |
| 75 | obj-$(CONFIG_SC1200_WDT) += sc1200wdt.o | 78 | obj-$(CONFIG_SC1200_WDT) += sc1200wdt.o |
| 76 | obj-$(CONFIG_SCx200_WDT) += scx200_wdt.o | 79 | obj-$(CONFIG_SCx200_WDT) += scx200_wdt.o |
| @@ -83,6 +86,7 @@ obj-$(CONFIG_CPU5_WDT) += cpu5wdt.o | |||
| 83 | obj-$(CONFIG_SMSC37B787_WDT) += smsc37b787_wdt.o | 86 | obj-$(CONFIG_SMSC37B787_WDT) += smsc37b787_wdt.o |
| 84 | obj-$(CONFIG_W83627HF_WDT) += w83627hf_wdt.o | 87 | obj-$(CONFIG_W83627HF_WDT) += w83627hf_wdt.o |
| 85 | obj-$(CONFIG_W83697HF_WDT) += w83697hf_wdt.o | 88 | obj-$(CONFIG_W83697HF_WDT) += w83697hf_wdt.o |
| 89 | obj-$(CONFIG_W83697UG_WDT) += w83697ug_wdt.o | ||
| 86 | obj-$(CONFIG_W83877F_WDT) += w83877f_wdt.o | 90 | obj-$(CONFIG_W83877F_WDT) += w83877f_wdt.o |
| 87 | obj-$(CONFIG_W83977F_WDT) += w83977f_wdt.o | 91 | obj-$(CONFIG_W83977F_WDT) += w83977f_wdt.o |
| 88 | obj-$(CONFIG_MACHZ_WDT) += machzwd.o | 92 | obj-$(CONFIG_MACHZ_WDT) += machzwd.o |
diff --git a/drivers/watchdog/at91sam9_wdt.c b/drivers/watchdog/at91sam9_wdt.c new file mode 100644 index 000000000000..b4babfc31586 --- /dev/null +++ b/drivers/watchdog/at91sam9_wdt.c | |||
| @@ -0,0 +1,328 @@ | |||
| 1 | /* | ||
| 2 | * Watchdog driver for Atmel AT91SAM9x processors. | ||
| 3 | * | ||
| 4 | * Copyright (C) 2008 Renaud CERRATO r.cerrato@til-technologies.fr | ||
| 5 | * | ||
| 6 | * This program is free software; you can redistribute it and/or | ||
| 7 | * modify it under the terms of the GNU General Public License | ||
| 8 | * as published by the Free Software Foundation; either version | ||
| 9 | * 2 of the License, or (at your option) any later version. | ||
| 10 | */ | ||
| 11 | |||
| 12 | /* | ||
| 13 | * The Watchdog Timer Mode Register can be only written to once. If the | ||
| 14 | * timeout need to be set from Linux, be sure that the bootstrap or the | ||
| 15 | * bootloader doesn't write to this register. | ||
| 16 | */ | ||
| 17 | |||
| 18 | #include <linux/errno.h> | ||
| 19 | #include <linux/fs.h> | ||
| 20 | #include <linux/init.h> | ||
| 21 | #include <linux/kernel.h> | ||
| 22 | #include <linux/miscdevice.h> | ||
| 23 | #include <linux/module.h> | ||
| 24 | #include <linux/moduleparam.h> | ||
| 25 | #include <linux/platform_device.h> | ||
| 26 | #include <linux/types.h> | ||
| 27 | #include <linux/watchdog.h> | ||
| 28 | #include <linux/jiffies.h> | ||
| 29 | #include <linux/timer.h> | ||
| 30 | #include <linux/bitops.h> | ||
| 31 | #include <linux/uaccess.h> | ||
| 32 | |||
| 33 | #include <asm/arch/at91_wdt.h> | ||
| 34 | |||
| 35 | #define DRV_NAME "AT91SAM9 Watchdog" | ||
| 36 | |||
| 37 | /* AT91SAM9 watchdog runs a 12bit counter @ 256Hz, | ||
| 38 | * use this to convert a watchdog | ||
| 39 | * value from/to milliseconds. | ||
| 40 | */ | ||
| 41 | #define ms_to_ticks(t) (((t << 8) / 1000) - 1) | ||
| 42 | #define ticks_to_ms(t) (((t + 1) * 1000) >> 8) | ||
| 43 | |||
| 44 | /* Hardware timeout in seconds */ | ||
| 45 | #define WDT_HW_TIMEOUT 2 | ||
| 46 | |||
| 47 | /* Timer heartbeat (500ms) */ | ||
| 48 | #define WDT_TIMEOUT (HZ/2) | ||
| 49 | |||
| 50 | /* User land timeout */ | ||
| 51 | #define WDT_HEARTBEAT 15 | ||
| 52 | static int heartbeat = WDT_HEARTBEAT; | ||
| 53 | module_param(heartbeat, int, 0); | ||
| 54 | MODULE_PARM_DESC(heartbeat, "Watchdog heartbeats in seconds. " | ||
| 55 | "(default = " __MODULE_STRING(WDT_HEARTBEAT) ")"); | ||
| 56 | |||
| 57 | static int nowayout = WATCHDOG_NOWAYOUT; | ||
| 58 | module_param(nowayout, int, 0); | ||
| 59 | MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started " | ||
| 60 | "(default=" __MODULE_STRING(WATCHDOG_NOWAYOUT) ")"); | ||
| 61 | |||
| 62 | static void at91_ping(unsigned long data); | ||
| 63 | |||
| 64 | static struct { | ||
| 65 | unsigned long next_heartbeat; /* the next_heartbeat for the timer */ | ||
| 66 | unsigned long open; | ||
| 67 | char expect_close; | ||
| 68 | struct timer_list timer; /* The timer that pings the watchdog */ | ||
| 69 | } at91wdt_private; | ||
| 70 | |||
| 71 | /* ......................................................................... */ | ||
| 72 | |||
| 73 | |||
| 74 | /* | ||
| 75 | * Reload the watchdog timer. (ie, pat the watchdog) | ||
| 76 | */ | ||
| 77 | static inline void at91_wdt_reset(void) | ||
| 78 | { | ||
| 79 | at91_sys_write(AT91_WDT_CR, AT91_WDT_KEY | AT91_WDT_WDRSTT); | ||
| 80 | } | ||
| 81 | |||
| 82 | /* | ||
| 83 | * Timer tick | ||
| 84 | */ | ||
| 85 | static void at91_ping(unsigned long data) | ||
| 86 | { | ||
| 87 | if (time_before(jiffies, at91wdt_private.next_heartbeat) || | ||
| 88 | (!nowayout && !at91wdt_private.open)) { | ||
| 89 | at91_wdt_reset(); | ||
| 90 | mod_timer(&at91wdt_private.timer, jiffies + WDT_TIMEOUT); | ||
| 91 | } else | ||
| 92 | printk(KERN_CRIT DRV_NAME": I will reset your machine !\n"); | ||
| 93 | } | ||
| 94 | |||
| 95 | /* | ||
| 96 | * Watchdog device is opened, and watchdog starts running. | ||
| 97 | */ | ||
| 98 | static int at91_wdt_open(struct inode *inode, struct file *file) | ||
| 99 | { | ||
| 100 | if (test_and_set_bit(0, &at91wdt_private.open)) | ||
| 101 | return -EBUSY; | ||
| 102 | |||
| 103 | at91wdt_private.next_heartbeat = jiffies + heartbeat * HZ; | ||
| 104 | mod_timer(&at91wdt_private.timer, jiffies + WDT_TIMEOUT); | ||
| 105 | |||
| 106 | return nonseekable_open(inode, file); | ||
| 107 | } | ||
| 108 | |||
| 109 | /* | ||
| 110 | * Close the watchdog device. | ||
| 111 | */ | ||
| 112 | static int at91_wdt_close(struct inode *inode, struct file *file) | ||
| 113 | { | ||
| 114 | clear_bit(0, &at91wdt_private.open); | ||
| 115 | |||
| 116 | /* stop internal ping */ | ||
| 117 | if (!at91wdt_private.expect_close) | ||
| 118 | del_timer(&at91wdt_private.timer); | ||
| 119 | |||
| 120 | at91wdt_private.expect_close = 0; | ||
| 121 | return 0; | ||
| 122 | } | ||
| 123 | |||
| 124 | /* | ||
| 125 | * Set the watchdog time interval in 1/256Hz (write-once) | ||
| 126 | * Counter is 12 bit. | ||
| 127 | */ | ||
| 128 | static int at91_wdt_settimeout(unsigned int timeout) | ||
| 129 | { | ||
| 130 | unsigned int reg; | ||
| 131 | unsigned int mr; | ||
| 132 | |||
| 133 | /* Check if disabled */ | ||
| 134 | mr = at91_sys_read(AT91_WDT_MR); | ||
| 135 | if (mr & AT91_WDT_WDDIS) { | ||
| 136 | printk(KERN_ERR DRV_NAME": sorry, watchdog is disabled\n"); | ||
| 137 | return -EIO; | ||
| 138 | } | ||
| 139 | |||
| 140 | /* | ||
| 141 | * All counting occurs at SLOW_CLOCK / 128 = 256 Hz | ||
| 142 | * | ||
| 143 | * Since WDV is a 12-bit counter, the maximum period is | ||
| 144 | * 4096 / 256 = 16 seconds. | ||
| 145 | */ | ||
| 146 | reg = AT91_WDT_WDRSTEN /* causes watchdog reset */ | ||
| 147 | /* | AT91_WDT_WDRPROC causes processor reset only */ | ||
| 148 | | AT91_WDT_WDDBGHLT /* disabled in debug mode */ | ||
| 149 | | AT91_WDT_WDD /* restart at any time */ | ||
| 150 | | (timeout & AT91_WDT_WDV); /* timer value */ | ||
| 151 | at91_sys_write(AT91_WDT_MR, reg); | ||
| 152 | |||
| 153 | return 0; | ||
| 154 | } | ||
| 155 | |||
| 156 | static const struct watchdog_info at91_wdt_info = { | ||
| 157 | .identity = DRV_NAME, | ||
| 158 | .options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING, | ||
| 159 | }; | ||
| 160 | |||
| 161 | /* | ||
| 162 | * Handle commands from user-space. | ||
| 163 | */ | ||
| 164 | static long at91_wdt_ioctl(struct file *file, | ||
| 165 | unsigned int cmd, unsigned long arg) | ||
| 166 | { | ||
| 167 | void __user *argp = (void __user *)arg; | ||
| 168 | int __user *p = argp; | ||
| 169 | int new_value; | ||
| 170 | |||
| 171 | switch (cmd) { | ||
| 172 | case WDIOC_GETSUPPORT: | ||
| 173 | return copy_to_user(argp, &at91_wdt_info, | ||
| 174 | sizeof(at91_wdt_info)) ? -EFAULT : 0; | ||
| 175 | |||
| 176 | case WDIOC_GETSTATUS: | ||
| 177 | case WDIOC_GETBOOTSTATUS: | ||
| 178 | return put_user(0, p); | ||
| 179 | |||
| 180 | case WDIOC_KEEPALIVE: | ||
| 181 | at91wdt_private.next_heartbeat = jiffies + heartbeat * HZ; | ||
| 182 | return 0; | ||
| 183 | |||
| 184 | case WDIOC_SETTIMEOUT: | ||
| 185 | if (get_user(new_value, p)) | ||
| 186 | return -EFAULT; | ||
| 187 | |||
| 188 | heartbeat = new_value; | ||
| 189 | at91wdt_private.next_heartbeat = jiffies + heartbeat * HZ; | ||
| 190 | |||
| 191 | return put_user(new_value, p); /* return current value */ | ||
| 192 | |||
| 193 | case WDIOC_GETTIMEOUT: | ||
| 194 | return put_user(heartbeat, p); | ||
| 195 | } | ||
| 196 | return -ENOTTY; | ||
| 197 | } | ||
| 198 | |||
| 199 | /* | ||
| 200 | * Pat the watchdog whenever device is written to. | ||
| 201 | */ | ||
| 202 | static ssize_t at91_wdt_write(struct file *file, const char *data, size_t len, | ||
| 203 | loff_t *ppos) | ||
| 204 | { | ||
| 205 | if (!len) | ||
| 206 | return 0; | ||
| 207 | |||
| 208 | /* Scan for magic character */ | ||
| 209 | if (!nowayout) { | ||
| 210 | size_t i; | ||
| 211 | |||
| 212 | at91wdt_private.expect_close = 0; | ||
| 213 | |||
| 214 | for (i = 0; i < len; i++) { | ||
| 215 | char c; | ||
| 216 | if (get_user(c, data + i)) | ||
| 217 | return -EFAULT; | ||
| 218 | if (c == 'V') { | ||
| 219 | at91wdt_private.expect_close = 42; | ||
| 220 | break; | ||
| 221 | } | ||
| 222 | } | ||
| 223 | } | ||
| 224 | |||
| 225 | at91wdt_private.next_heartbeat = jiffies + heartbeat * HZ; | ||
| 226 | |||
| 227 | return len; | ||
| 228 | } | ||
| 229 | |||
| 230 | /* ......................................................................... */ | ||
| 231 | |||
| 232 | static const struct file_operations at91wdt_fops = { | ||
| 233 | .owner = THIS_MODULE, | ||
| 234 | .llseek = no_llseek, | ||
| 235 | .unlocked_ioctl = at91_wdt_ioctl, | ||
| 236 | .open = at91_wdt_open, | ||
| 237 | .release = at91_wdt_close, | ||
| 238 | .write = at91_wdt_write, | ||
| 239 | }; | ||
| 240 | |||
| 241 | static struct miscdevice at91wdt_miscdev = { | ||
| 242 | .minor = WATCHDOG_MINOR, | ||
| 243 | .name = "watchdog", | ||
| 244 | .fops = &at91wdt_fops, | ||
| 245 | }; | ||
| 246 | |||
| 247 | static int __init at91wdt_probe(struct platform_device *pdev) | ||
| 248 | { | ||
| 249 | int res; | ||
| 250 | |||
| 251 | if (at91wdt_miscdev.parent) | ||
| 252 | return -EBUSY; | ||
| 253 | at91wdt_miscdev.parent = &pdev->dev; | ||
| 254 | |||
| 255 | /* Set watchdog */ | ||
| 256 | res = at91_wdt_settimeout(ms_to_ticks(WDT_HW_TIMEOUT * 1000)); | ||
| 257 | if (res) | ||
| 258 | return res; | ||
| 259 | |||
| 260 | res = misc_register(&at91wdt_miscdev); | ||
| 261 | if (res) | ||
| 262 | return res; | ||
| 263 | |||
| 264 | at91wdt_private.next_heartbeat = jiffies + heartbeat * HZ; | ||
| 265 | setup_timer(&at91wdt_private.timer, at91_ping, 0); | ||
| 266 | mod_timer(&at91wdt_private.timer, jiffies + WDT_TIMEOUT); | ||
| 267 | |||
| 268 | printk(KERN_INFO DRV_NAME " enabled (heartbeat=%d sec, nowayout=%d)\n", | ||
| 269 | heartbeat, nowayout); | ||
| 270 | |||
| 271 | return 0; | ||
| 272 | } | ||
| 273 | |||
| 274 | static int __exit at91wdt_remove(struct platform_device *pdev) | ||
| 275 | { | ||
| 276 | int res; | ||
| 277 | |||
| 278 | res = misc_deregister(&at91wdt_miscdev); | ||
| 279 | if (!res) | ||
| 280 | at91wdt_miscdev.parent = NULL; | ||
| 281 | |||
| 282 | return res; | ||
| 283 | } | ||
| 284 | |||
| 285 | #ifdef CONFIG_PM | ||
| 286 | |||
| 287 | static int at91wdt_suspend(struct platform_device *pdev, pm_message_t message) | ||
| 288 | { | ||
| 289 | return 0; | ||
| 290 | } | ||
| 291 | |||
| 292 | static int at91wdt_resume(struct platform_device *pdev) | ||
| 293 | { | ||
| 294 | return 0; | ||
| 295 | } | ||
| 296 | |||
| 297 | #else | ||
| 298 | #define at91wdt_suspend NULL | ||
| 299 | #define at91wdt_resume NULL | ||
| 300 | #endif | ||
| 301 | |||
| 302 | static struct platform_driver at91wdt_driver = { | ||
| 303 | .remove = __exit_p(at91wdt_remove), | ||
| 304 | .suspend = at91wdt_suspend, | ||
| 305 | .resume = at91wdt_resume, | ||
| 306 | .driver = { | ||
| 307 | .name = "at91_wdt", | ||
| 308 | .owner = THIS_MODULE, | ||
| 309 | }, | ||
| 310 | }; | ||
| 311 | |||
| 312 | static int __init at91sam_wdt_init(void) | ||
| 313 | { | ||
| 314 | return platform_driver_probe(&at91wdt_driver, at91wdt_probe); | ||
| 315 | } | ||
| 316 | |||
| 317 | static void __exit at91sam_wdt_exit(void) | ||
| 318 | { | ||
| 319 | platform_driver_unregister(&at91wdt_driver); | ||
| 320 | } | ||
| 321 | |||
| 322 | module_init(at91sam_wdt_init); | ||
| 323 | module_exit(at91sam_wdt_exit); | ||
| 324 | |||
| 325 | MODULE_AUTHOR("Renaud CERRATO <r.cerrato@til-technologies.fr>"); | ||
| 326 | MODULE_DESCRIPTION("Watchdog driver for Atmel AT91SAM9x processors"); | ||
| 327 | MODULE_LICENSE("GPL"); | ||
| 328 | MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR); | ||
diff --git a/drivers/watchdog/it87_wdt.c b/drivers/watchdog/it87_wdt.c new file mode 100644 index 000000000000..afb8af397a9f --- /dev/null +++ b/drivers/watchdog/it87_wdt.c | |||
| @@ -0,0 +1,725 @@ | |||
| 1 | /* | ||
| 2 | * Watchdog Timer Driver | ||
| 3 | * for ITE IT87xx Environment Control - Low Pin Count Input / Output | ||
| 4 | * | ||
| 5 | * (c) Copyright 2007 Oliver Schuster <olivers137@aol.com> | ||
| 6 | * | ||
| 7 | * Based on softdog.c by Alan Cox, | ||
| 8 | * 83977f_wdt.c by Jose Goncalves, | ||
| 9 | * it87.c by Chris Gauthron, Jean Delvare | ||
| 10 | * | ||
| 11 | * Data-sheets: Publicly available at the ITE website | ||
| 12 | * http://www.ite.com.tw/ | ||
| 13 | * | ||
| 14 | * Support of the watchdog timers, which are available on | ||
| 15 | * IT8716, IT8718, IT8726 and IT8712 (J,K version). | ||
| 16 | * | ||
| 17 | * This program is free software; you can redistribute it and/or | ||
| 18 | * modify it under the terms of the GNU General Public License | ||
| 19 | * as published by the Free Software Foundation; either version | ||
| 20 | * 2 of the License, or (at your option) any later version. | ||
| 21 | * | ||
| 22 | * This program is distributed in the hope that it will be useful, | ||
| 23 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 24 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 25 | * GNU General Public License for more details. | ||
| 26 | * | ||
| 27 | * You should have received a copy of the GNU General Public License | ||
| 28 | * along with this program; if not, write to the Free Software | ||
| 29 | * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. | ||
| 30 | */ | ||
| 31 | |||
| 32 | #include <linux/module.h> | ||
| 33 | #include <linux/moduleparam.h> | ||
| 34 | #include <linux/types.h> | ||
| 35 | #include <linux/kernel.h> | ||
| 36 | #include <linux/fs.h> | ||
| 37 | #include <linux/miscdevice.h> | ||
| 38 | #include <linux/init.h> | ||
| 39 | #include <linux/ioport.h> | ||
| 40 | #include <linux/watchdog.h> | ||
| 41 | #include <linux/notifier.h> | ||
| 42 | #include <linux/reboot.h> | ||
| 43 | #include <linux/uaccess.h> | ||
| 44 | #include <linux/io.h> | ||
| 45 | |||
| 46 | #include <asm/system.h> | ||
| 47 | |||
| 48 | #define WATCHDOG_VERSION "1.12" | ||
| 49 | #define WATCHDOG_NAME "IT87 WDT" | ||
| 50 | #define PFX WATCHDOG_NAME ": " | ||
| 51 | #define DRIVER_VERSION WATCHDOG_NAME " driver, v" WATCHDOG_VERSION "\n" | ||
| 52 | #define WD_MAGIC 'V' | ||
| 53 | |||
| 54 | /* Defaults for Module Parameter */ | ||
| 55 | #define DEFAULT_NOGAMEPORT 0 | ||
| 56 | #define DEFAULT_EXCLUSIVE 1 | ||
| 57 | #define DEFAULT_TIMEOUT 60 | ||
| 58 | #define DEFAULT_TESTMODE 0 | ||
| 59 | #define DEFAULT_NOWAYOUT WATCHDOG_NOWAYOUT | ||
| 60 | |||
| 61 | /* IO Ports */ | ||
| 62 | #define REG 0x2e | ||
| 63 | #define VAL 0x2f | ||
| 64 | |||
| 65 | /* Logical device Numbers LDN */ | ||
| 66 | #define GPIO 0x07 | ||
| 67 | #define GAMEPORT 0x09 | ||
| 68 | #define CIR 0x0a | ||
| 69 | |||
| 70 | /* Configuration Registers and Functions */ | ||
| 71 | #define LDNREG 0x07 | ||
| 72 | #define CHIPID 0x20 | ||
| 73 | #define CHIPREV 0x22 | ||
| 74 | #define ACTREG 0x30 | ||
| 75 | #define BASEREG 0x60 | ||
| 76 | |||
| 77 | /* Chip Id numbers */ | ||
| 78 | #define NO_DEV_ID 0xffff | ||
| 79 | #define IT8705_ID 0x8705 | ||
| 80 | #define IT8712_ID 0x8712 | ||
| 81 | #define IT8716_ID 0x8716 | ||
| 82 | #define IT8718_ID 0x8718 | ||
| 83 | #define IT8726_ID 0x8726 /* the data sheet suggest wrongly 0x8716 */ | ||
| 84 | |||
| 85 | /* GPIO Configuration Registers LDN=0x07 */ | ||
| 86 | #define WDTCTRL 0x71 | ||
| 87 | #define WDTCFG 0x72 | ||
| 88 | #define WDTVALLSB 0x73 | ||
| 89 | #define WDTVALMSB 0x74 | ||
| 90 | |||
| 91 | /* GPIO Bits WDTCTRL */ | ||
| 92 | #define WDT_CIRINT 0x80 | ||
| 93 | #define WDT_MOUSEINT 0x40 | ||
| 94 | #define WDT_KYBINT 0x20 | ||
| 95 | #define WDT_GAMEPORT 0x10 /* not it8718 */ | ||
| 96 | #define WDT_FORCE 0x02 | ||
| 97 | #define WDT_ZERO 0x01 | ||
| 98 | |||
| 99 | /* GPIO Bits WDTCFG */ | ||
| 100 | #define WDT_TOV1 0x80 | ||
| 101 | #define WDT_KRST 0x40 | ||
| 102 | #define WDT_TOVE 0x20 | ||
| 103 | #define WDT_PWROK 0x10 | ||
| 104 | #define WDT_INT_MASK 0x0f | ||
| 105 | |||
| 106 | /* CIR Configuration Register LDN=0x0a */ | ||
| 107 | #define CIR_ILS 0x70 | ||
| 108 | |||
| 109 | /* The default Base address is not always available, we use this */ | ||
| 110 | #define CIR_BASE 0x0208 | ||
| 111 | |||
| 112 | /* CIR Controller */ | ||
| 113 | #define CIR_DR(b) (b) | ||
| 114 | #define CIR_IER(b) (b + 1) | ||
| 115 | #define CIR_RCR(b) (b + 2) | ||
| 116 | #define CIR_TCR1(b) (b + 3) | ||
| 117 | #define CIR_TCR2(b) (b + 4) | ||
| 118 | #define CIR_TSR(b) (b + 5) | ||
| 119 | #define CIR_RSR(b) (b + 6) | ||
| 120 | #define CIR_BDLR(b) (b + 5) | ||
| 121 | #define CIR_BDHR(b) (b + 6) | ||
| 122 | #define CIR_IIR(b) (b + 7) | ||
| 123 | |||
| 124 | /* Default Base address of Game port */ | ||
| 125 | #define GP_BASE_DEFAULT 0x0201 | ||
| 126 | |||
| 127 | /* wdt_status */ | ||
| 128 | #define WDTS_TIMER_RUN 0 | ||
| 129 | #define WDTS_DEV_OPEN 1 | ||
| 130 | #define WDTS_KEEPALIVE 2 | ||
| 131 | #define WDTS_LOCKED 3 | ||
| 132 | #define WDTS_USE_GP 4 | ||
| 133 | #define WDTS_EXPECTED 5 | ||
| 134 | |||
| 135 | static unsigned int base, gpact, ciract; | ||
| 136 | static unsigned long wdt_status; | ||
| 137 | static DEFINE_SPINLOCK(spinlock); | ||
| 138 | |||
| 139 | static int nogameport = DEFAULT_NOGAMEPORT; | ||
| 140 | static int exclusive = DEFAULT_EXCLUSIVE; | ||
| 141 | static int timeout = DEFAULT_TIMEOUT; | ||
| 142 | static int testmode = DEFAULT_TESTMODE; | ||
| 143 | static int nowayout = DEFAULT_NOWAYOUT; | ||
| 144 | |||
| 145 | module_param(nogameport, int, 0); | ||
| 146 | MODULE_PARM_DESC(nogameport, "Forbid the activation of game port, default=" | ||
| 147 | __MODULE_STRING(DEFAULT_NOGAMEPORT)); | ||
| 148 | module_param(exclusive, int, 0); | ||
| 149 | MODULE_PARM_DESC(exclusive, "Watchdog exclusive device open, default=" | ||
| 150 | __MODULE_STRING(DEFAULT_EXCLUSIVE)); | ||
| 151 | module_param(timeout, int, 0); | ||
| 152 | MODULE_PARM_DESC(timeout, "Watchdog timeout in seconds, default=" | ||
| 153 | __MODULE_STRING(DEFAULT_TIMEOUT)); | ||
| 154 | module_param(testmode, int, 0); | ||
| 155 | MODULE_PARM_DESC(testmode, "Watchdog test mode (1 = no reboot), default=" | ||
| 156 | __MODULE_STRING(DEFAULT_TESTMODE)); | ||
| 157 | module_param(nowayout, int, 0); | ||
| 158 | MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started, default=" | ||
| 159 | __MODULE_STRING(WATCHDOG_NOWAYOUT)); | ||
| 160 | |||
| 161 | /* Superio Chip */ | ||
| 162 | |||
| 163 | static inline void superio_enter(void) | ||
| 164 | { | ||
| 165 | outb(0x87, REG); | ||
| 166 | outb(0x01, REG); | ||
| 167 | outb(0x55, REG); | ||
| 168 | outb(0x55, REG); | ||
| 169 | } | ||
| 170 | |||
| 171 | static inline void superio_exit(void) | ||
| 172 | { | ||
| 173 | outb(0x02, REG); | ||
| 174 | outb(0x02, VAL); | ||
| 175 | } | ||
| 176 | |||
| 177 | static inline void superio_select(int ldn) | ||
| 178 | { | ||
| 179 | outb(LDNREG, REG); | ||
| 180 | outb(ldn, VAL); | ||
| 181 | } | ||
| 182 | |||
| 183 | static inline int superio_inb(int reg) | ||
| 184 | { | ||
| 185 | outb(reg, REG); | ||
| 186 | return inb(VAL); | ||
| 187 | } | ||
| 188 | |||
| 189 | static inline void superio_outb(int val, int reg) | ||
| 190 | { | ||
| 191 | outb(reg, REG); | ||
| 192 | outb(val, VAL); | ||
| 193 | } | ||
| 194 | |||
| 195 | static inline int superio_inw(int reg) | ||
| 196 | { | ||
| 197 | int val; | ||
| 198 | outb(reg++, REG); | ||
| 199 | val = inb(VAL) << 8; | ||
| 200 | outb(reg, REG); | ||
| 201 | val |= inb(VAL); | ||
| 202 | return val; | ||
| 203 | } | ||
| 204 | |||
| 205 | static inline void superio_outw(int val, int reg) | ||
| 206 | { | ||
| 207 | outb(reg++, REG); | ||
| 208 | outb(val >> 8, VAL); | ||
| 209 | outb(reg, REG); | ||
| 210 | outb(val, VAL); | ||
| 211 | } | ||
| 212 | |||
| 213 | /* watchdog timer handling */ | ||
| 214 | |||
| 215 | static void wdt_keepalive(void) | ||
| 216 | { | ||
| 217 | if (test_bit(WDTS_USE_GP, &wdt_status)) | ||
| 218 | inb(base); | ||
| 219 | else | ||
| 220 | /* The timer reloads with around 5 msec delay */ | ||
| 221 | outb(0x55, CIR_DR(base)); | ||
| 222 | set_bit(WDTS_KEEPALIVE, &wdt_status); | ||
| 223 | } | ||
| 224 | |||
| 225 | static void wdt_start(void) | ||
| 226 | { | ||
| 227 | unsigned long flags; | ||
| 228 | |||
| 229 | spin_lock_irqsave(&spinlock, flags); | ||
| 230 | superio_enter(); | ||
| 231 | |||
| 232 | superio_select(GPIO); | ||
| 233 | if (test_bit(WDTS_USE_GP, &wdt_status)) | ||
| 234 | superio_outb(WDT_GAMEPORT, WDTCTRL); | ||
| 235 | else | ||
| 236 | superio_outb(WDT_CIRINT, WDTCTRL); | ||
| 237 | if (!testmode) | ||
| 238 | superio_outb(WDT_TOV1 | WDT_KRST | WDT_PWROK, WDTCFG); | ||
| 239 | else | ||
| 240 | superio_outb(WDT_TOV1, WDTCFG); | ||
| 241 | superio_outb(timeout>>8, WDTVALMSB); | ||
| 242 | superio_outb(timeout, WDTVALLSB); | ||
| 243 | |||
| 244 | superio_exit(); | ||
| 245 | spin_unlock_irqrestore(&spinlock, flags); | ||
| 246 | } | ||
| 247 | |||
| 248 | static void wdt_stop(void) | ||
| 249 | { | ||
| 250 | unsigned long flags; | ||
| 251 | |||
| 252 | spin_lock_irqsave(&spinlock, flags); | ||
| 253 | superio_enter(); | ||
| 254 | |||
| 255 | superio_select(GPIO); | ||
| 256 | superio_outb(0x00, WDTCTRL); | ||
| 257 | superio_outb(WDT_TOV1, WDTCFG); | ||
| 258 | superio_outb(0x00, WDTVALMSB); | ||
| 259 | superio_outb(0x00, WDTVALLSB); | ||
| 260 | |||
| 261 | superio_exit(); | ||
| 262 | spin_unlock_irqrestore(&spinlock, flags); | ||
| 263 | } | ||
| 264 | |||
| 265 | /** | ||
| 266 | * wdt_set_timeout - set a new timeout value with watchdog ioctl | ||
| 267 | * @t: timeout value in seconds | ||
| 268 | * | ||
| 269 | * The hardware device has a 16 bit watchdog timer, thus the | ||
| 270 | * timeout time ranges between 1 and 65535 seconds. | ||
| 271 | * | ||
| 272 | * Used within WDIOC_SETTIMEOUT watchdog device ioctl. | ||
| 273 | */ | ||
| 274 | |||
| 275 | static int wdt_set_timeout(int t) | ||
| 276 | { | ||
| 277 | unsigned long flags; | ||
| 278 | |||
| 279 | if (t < 1 || t > 65535) | ||
| 280 | return -EINVAL; | ||
| 281 | |||
| 282 | timeout = t; | ||
| 283 | |||
| 284 | spin_lock_irqsave(&spinlock, flags); | ||
| 285 | if (test_bit(WDTS_TIMER_RUN, &wdt_status)) { | ||
| 286 | superio_enter(); | ||
| 287 | |||
| 288 | superio_select(GPIO); | ||
| 289 | superio_outb(t>>8, WDTVALMSB); | ||
| 290 | superio_outb(t, WDTVALLSB); | ||
| 291 | |||
| 292 | superio_exit(); | ||
| 293 | } | ||
| 294 | spin_unlock_irqrestore(&spinlock, flags); | ||
| 295 | return 0; | ||
| 296 | } | ||
| 297 | |||
| 298 | /** | ||
| 299 | * wdt_get_status - determines the status supported by watchdog ioctl | ||
| 300 | * @status: status returned to user space | ||
| 301 | * | ||
| 302 | * The status bit of the device does not allow to distinguish | ||
| 303 | * between a regular system reset and a watchdog forced reset. | ||
| 304 | * But, in test mode it is useful, so it is supported through | ||
| 305 | * WDIOC_GETSTATUS watchdog ioctl. Additionally the driver | ||
| 306 | * reports the keepalive signal and the acception of the magic. | ||
| 307 | * | ||
| 308 | * Used within WDIOC_GETSTATUS watchdog device ioctl. | ||
| 309 | */ | ||
| 310 | |||
| 311 | static int wdt_get_status(int *status) | ||
| 312 | { | ||
| 313 | unsigned long flags; | ||
| 314 | |||
| 315 | *status = 0; | ||
| 316 | if (testmode) { | ||
| 317 | spin_lock_irqsave(&spinlock, flags); | ||
| 318 | superio_enter(); | ||
| 319 | superio_select(GPIO); | ||
| 320 | if (superio_inb(WDTCTRL) & WDT_ZERO) { | ||
| 321 | superio_outb(0x00, WDTCTRL); | ||
| 322 | clear_bit(WDTS_TIMER_RUN, &wdt_status); | ||
| 323 | *status |= WDIOF_CARDRESET; | ||
| 324 | } | ||
| 325 | |||
| 326 | superio_exit(); | ||
| 327 | spin_unlock_irqrestore(&spinlock, flags); | ||
| 328 | } | ||
| 329 | if (test_and_clear_bit(WDTS_KEEPALIVE, &wdt_status)) | ||
| 330 | *status |= WDIOF_KEEPALIVEPING; | ||
| 331 | if (test_bit(WDTS_EXPECTED, &wdt_status)) | ||
| 332 | *status |= WDIOF_MAGICCLOSE; | ||
| 333 | return 0; | ||
| 334 | } | ||
| 335 | |||
| 336 | /* /dev/watchdog handling */ | ||
| 337 | |||
| 338 | /** | ||
| 339 | * wdt_open - watchdog file_operations .open | ||
| 340 | * @inode: inode of the device | ||
| 341 | * @file: file handle to the device | ||
| 342 | * | ||
| 343 | * The watchdog timer starts by opening the device. | ||
| 344 | * | ||
| 345 | * Used within the file operation of the watchdog device. | ||
| 346 | */ | ||
| 347 | |||
| 348 | static int wdt_open(struct inode *inode, struct file *file) | ||
| 349 | { | ||
| 350 | if (exclusive && test_and_set_bit(WDTS_DEV_OPEN, &wdt_status)) | ||
| 351 | return -EBUSY; | ||
| 352 | if (!test_and_set_bit(WDTS_TIMER_RUN, &wdt_status)) { | ||
| 353 | if (nowayout && !test_and_set_bit(WDTS_LOCKED, &wdt_status)) | ||
| 354 | __module_get(THIS_MODULE); | ||
| 355 | wdt_start(); | ||
| 356 | } | ||
| 357 | return nonseekable_open(inode, file); | ||
| 358 | } | ||
| 359 | |||
| 360 | /** | ||
| 361 | * wdt_release - watchdog file_operations .release | ||
| 362 | * @inode: inode of the device | ||
| 363 | * @file: file handle to the device | ||
| 364 | * | ||
| 365 | * Closing the watchdog device either stops the watchdog timer | ||
| 366 | * or in the case, that nowayout is set or the magic character | ||
| 367 | * wasn't written, a critical warning about an running watchdog | ||
| 368 | * timer is given. | ||
| 369 | * | ||
| 370 | * Used within the file operation of the watchdog device. | ||
| 371 | */ | ||
| 372 | |||
| 373 | static int wdt_release(struct inode *inode, struct file *file) | ||
| 374 | { | ||
| 375 | if (test_bit(WDTS_TIMER_RUN, &wdt_status)) { | ||
| 376 | if (test_and_clear_bit(WDTS_EXPECTED, &wdt_status)) { | ||
| 377 | wdt_stop(); | ||
| 378 | clear_bit(WDTS_TIMER_RUN, &wdt_status); | ||
| 379 | } else { | ||
| 380 | wdt_keepalive(); | ||
| 381 | printk(KERN_CRIT PFX | ||
| 382 | "unexpected close, not stopping watchdog!\n"); | ||
| 383 | } | ||
| 384 | } | ||
| 385 | clear_bit(WDTS_DEV_OPEN, &wdt_status); | ||
| 386 | return 0; | ||
| 387 | } | ||
| 388 | |||
| 389 | /** | ||
| 390 | * wdt_write - watchdog file_operations .write | ||
| 391 | * @file: file handle to the watchdog | ||
| 392 | * @buf: buffer to write | ||
| 393 | * @count: count of bytes | ||
| 394 | * @ppos: pointer to the position to write. No seeks allowed | ||
| 395 | * | ||
| 396 | * A write to a watchdog device is defined as a keepalive signal. Any | ||
| 397 | * write of data will do, as we don't define content meaning. | ||
| 398 | * | ||
| 399 | * Used within the file operation of the watchdog device. | ||
| 400 | */ | ||
| 401 | |||
| 402 | static ssize_t wdt_write(struct file *file, const char __user *buf, | ||
| 403 | size_t count, loff_t *ppos) | ||
| 404 | { | ||
| 405 | if (count) { | ||
| 406 | clear_bit(WDTS_EXPECTED, &wdt_status); | ||
| 407 | wdt_keepalive(); | ||
| 408 | } | ||
| 409 | if (!nowayout) { | ||
| 410 | size_t ofs; | ||
| 411 | |||
| 412 | /* note: just in case someone wrote the magic character long ago */ | ||
| 413 | for (ofs = 0; ofs != count; ofs++) { | ||
| 414 | char c; | ||
| 415 | if (get_user(c, buf + ofs)) | ||
| 416 | return -EFAULT; | ||
| 417 | if (c == WD_MAGIC) | ||
| 418 | set_bit(WDTS_EXPECTED, &wdt_status); | ||
| 419 | } | ||
| 420 | } | ||
| 421 | return count; | ||
| 422 | } | ||
| 423 | |||
| 424 | static struct watchdog_info ident = { | ||
| 425 | .options = WDIOF_SETTIMEOUT | WDIOF_MAGICCLOSE | WDIOF_KEEPALIVEPING, | ||
| 426 | .firmware_version = 1, | ||
| 427 | .identity = WATCHDOG_NAME, | ||
| 428 | }; | ||
| 429 | |||
| 430 | /** | ||
| 431 | * wdt_ioctl - watchdog file_operations .unlocked_ioctl | ||
| 432 | * @file: file handle to the device | ||
| 433 | * @cmd: watchdog command | ||
| 434 | * @arg: argument pointer | ||
| 435 | * | ||
| 436 | * The watchdog API defines a common set of functions for all watchdogs | ||
| 437 | * according to their available features. | ||
| 438 | * | ||
| 439 | * Used within the file operation of the watchdog device. | ||
| 440 | */ | ||
| 441 | |||
| 442 | static long wdt_ioctl(struct file *file, unsigned int cmd, unsigned long arg) | ||
| 443 | { | ||
| 444 | int rc = 0, status, new_options, new_timeout; | ||
| 445 | union { | ||
| 446 | struct watchdog_info __user *ident; | ||
| 447 | int __user *i; | ||
| 448 | } uarg; | ||
| 449 | |||
| 450 | uarg.i = (int __user *)arg; | ||
| 451 | |||
| 452 | switch (cmd) { | ||
| 453 | case WDIOC_GETSUPPORT: | ||
| 454 | return copy_to_user(uarg.ident, | ||
| 455 | &ident, sizeof(ident)) ? -EFAULT : 0; | ||
| 456 | |||
| 457 | case WDIOC_GETSTATUS: | ||
| 458 | wdt_get_status(&status); | ||
| 459 | return put_user(status, uarg.i); | ||
| 460 | |||
| 461 | case WDIOC_GETBOOTSTATUS: | ||
| 462 | return put_user(0, uarg.i); | ||
| 463 | |||
| 464 | case WDIOC_KEEPALIVE: | ||
| 465 | wdt_keepalive(); | ||
| 466 | return 0; | ||
| 467 | |||
| 468 | case WDIOC_SETOPTIONS: | ||
| 469 | if (get_user(new_options, uarg.i)) | ||
| 470 | return -EFAULT; | ||
| 471 | |||
| 472 | switch (new_options) { | ||
| 473 | case WDIOS_DISABLECARD: | ||
| 474 | if (test_bit(WDTS_TIMER_RUN, &wdt_status)) | ||
| 475 | wdt_stop(); | ||
| 476 | clear_bit(WDTS_TIMER_RUN, &wdt_status); | ||
| 477 | return 0; | ||
| 478 | |||
| 479 | case WDIOS_ENABLECARD: | ||
| 480 | if (!test_and_set_bit(WDTS_TIMER_RUN, &wdt_status)) | ||
| 481 | wdt_start(); | ||
| 482 | return 0; | ||
| 483 | |||
| 484 | default: | ||
| 485 | return -EFAULT; | ||
| 486 | } | ||
| 487 | |||
| 488 | case WDIOC_SETTIMEOUT: | ||
| 489 | if (get_user(new_timeout, uarg.i)) | ||
| 490 | return -EFAULT; | ||
| 491 | rc = wdt_set_timeout(new_timeout); | ||
| 492 | case WDIOC_GETTIMEOUT: | ||
| 493 | if (put_user(timeout, uarg.i)) | ||
| 494 | return -EFAULT; | ||
| 495 | return rc; | ||
| 496 | |||
| 497 | default: | ||
| 498 | return -ENOTTY; | ||
| 499 | } | ||
| 500 | } | ||
| 501 | |||
| 502 | static int wdt_notify_sys(struct notifier_block *this, unsigned long code, | ||
| 503 | void *unused) | ||
| 504 | { | ||
| 505 | if (code == SYS_DOWN || code == SYS_HALT) | ||
| 506 | wdt_stop(); | ||
| 507 | return NOTIFY_DONE; | ||
| 508 | } | ||
| 509 | |||
| 510 | static const struct file_operations wdt_fops = { | ||
| 511 | .owner = THIS_MODULE, | ||
| 512 | .llseek = no_llseek, | ||
| 513 | .write = wdt_write, | ||
| 514 | .unlocked_ioctl = wdt_ioctl, | ||
| 515 | .open = wdt_open, | ||
| 516 | .release = wdt_release, | ||
| 517 | }; | ||
| 518 | |||
| 519 | static struct miscdevice wdt_miscdev = { | ||
| 520 | .minor = WATCHDOG_MINOR, | ||
| 521 | .name = "watchdog", | ||
| 522 | .fops = &wdt_fops, | ||
| 523 | }; | ||
| 524 | |||
| 525 | static struct notifier_block wdt_notifier = { | ||
| 526 | .notifier_call = wdt_notify_sys, | ||
| 527 | }; | ||
| 528 | |||
| 529 | static int __init it87_wdt_init(void) | ||
| 530 | { | ||
| 531 | int rc = 0; | ||
| 532 | u16 chip_type; | ||
| 533 | u8 chip_rev; | ||
| 534 | unsigned long flags; | ||
| 535 | |||
| 536 | spin_lock_irqsave(&spinlock, flags); | ||
| 537 | superio_enter(); | ||
| 538 | chip_type = superio_inw(CHIPID); | ||
| 539 | chip_rev = superio_inb(CHIPREV) & 0x0f; | ||
| 540 | superio_exit(); | ||
| 541 | spin_unlock_irqrestore(&spinlock, flags); | ||
| 542 | |||
| 543 | switch (chip_type) { | ||
| 544 | case IT8716_ID: | ||
| 545 | case IT8718_ID: | ||
| 546 | case IT8726_ID: | ||
| 547 | break; | ||
| 548 | case IT8712_ID: | ||
| 549 | if (chip_rev > 7) | ||
| 550 | break; | ||
| 551 | case IT8705_ID: | ||
| 552 | printk(KERN_ERR PFX | ||
| 553 | "Unsupported Chip found, Chip %04x Revision %02x\n", | ||
| 554 | chip_type, chip_rev); | ||
| 555 | return -ENODEV; | ||
| 556 | case NO_DEV_ID: | ||
| 557 | printk(KERN_ERR PFX "no device\n"); | ||
| 558 | return -ENODEV; | ||
| 559 | default: | ||
| 560 | printk(KERN_ERR PFX | ||
| 561 | "Unknown Chip found, Chip %04x Revision %04x\n", | ||
| 562 | chip_type, chip_rev); | ||
| 563 | return -ENODEV; | ||
| 564 | } | ||
| 565 | |||
| 566 | spin_lock_irqsave(&spinlock, flags); | ||
| 567 | superio_enter(); | ||
| 568 | |||
| 569 | superio_select(GPIO); | ||
| 570 | superio_outb(WDT_TOV1, WDTCFG); | ||
| 571 | superio_outb(0x00, WDTCTRL); | ||
| 572 | |||
| 573 | /* First try to get Gameport support */ | ||
| 574 | if (chip_type != IT8718_ID && !nogameport) { | ||
| 575 | superio_select(GAMEPORT); | ||
| 576 | base = superio_inw(BASEREG); | ||
| 577 | if (!base) { | ||
| 578 | base = GP_BASE_DEFAULT; | ||
| 579 | superio_outw(base, BASEREG); | ||
| 580 | } | ||
| 581 | gpact = superio_inb(ACTREG); | ||
| 582 | superio_outb(0x01, ACTREG); | ||
| 583 | superio_exit(); | ||
| 584 | spin_unlock_irqrestore(&spinlock, flags); | ||
| 585 | if (request_region(base, 1, WATCHDOG_NAME)) | ||
| 586 | set_bit(WDTS_USE_GP, &wdt_status); | ||
| 587 | else | ||
| 588 | rc = -EIO; | ||
| 589 | } else { | ||
| 590 | superio_exit(); | ||
| 591 | spin_unlock_irqrestore(&spinlock, flags); | ||
| 592 | } | ||
| 593 | |||
| 594 | /* If we haven't Gameport support, try to get CIR support */ | ||
| 595 | if (!test_bit(WDTS_USE_GP, &wdt_status)) { | ||
| 596 | if (!request_region(CIR_BASE, 8, WATCHDOG_NAME)) { | ||
| 597 | if (rc == -EIO) | ||
| 598 | printk(KERN_ERR PFX | ||
| 599 | "I/O Address 0x%04x and 0x%04x" | ||
| 600 | " already in use\n", base, CIR_BASE); | ||
| 601 | else | ||
| 602 | printk(KERN_ERR PFX | ||
| 603 | "I/O Address 0x%04x already in use\n", | ||
| 604 | CIR_BASE); | ||
| 605 | rc = -EIO; | ||
| 606 | goto err_out; | ||
| 607 | } | ||
| 608 | base = CIR_BASE; | ||
| 609 | spin_lock_irqsave(&spinlock, flags); | ||
| 610 | superio_enter(); | ||
| 611 | |||
| 612 | superio_select(CIR); | ||
| 613 | superio_outw(base, BASEREG); | ||
| 614 | superio_outb(0x00, CIR_ILS); | ||
| 615 | ciract = superio_inb(ACTREG); | ||
| 616 | superio_outb(0x01, ACTREG); | ||
| 617 | if (rc == -EIO) { | ||
| 618 | superio_select(GAMEPORT); | ||
| 619 | superio_outb(gpact, ACTREG); | ||
| 620 | } | ||
| 621 | |||
| 622 | superio_exit(); | ||
| 623 | spin_unlock_irqrestore(&spinlock, flags); | ||
| 624 | } | ||
| 625 | |||
| 626 | if (timeout < 1 || timeout > 65535) { | ||
| 627 | timeout = DEFAULT_TIMEOUT; | ||
| 628 | printk(KERN_WARNING PFX | ||
| 629 | "Timeout value out of range, use default %d sec\n", | ||
| 630 | DEFAULT_TIMEOUT); | ||
| 631 | } | ||
| 632 | |||
| 633 | rc = register_reboot_notifier(&wdt_notifier); | ||
| 634 | if (rc) { | ||
| 635 | printk(KERN_ERR PFX | ||
| 636 | "Cannot register reboot notifier (err=%d)\n", rc); | ||
| 637 | goto err_out_region; | ||
| 638 | } | ||
| 639 | |||
| 640 | rc = misc_register(&wdt_miscdev); | ||
| 641 | if (rc) { | ||
| 642 | printk(KERN_ERR PFX | ||
| 643 | "Cannot register miscdev on minor=%d (err=%d)\n", | ||
| 644 | wdt_miscdev.minor, rc); | ||
| 645 | goto err_out_reboot; | ||
| 646 | } | ||
| 647 | |||
| 648 | /* Initialize CIR to use it as keepalive source */ | ||
| 649 | if (!test_bit(WDTS_USE_GP, &wdt_status)) { | ||
| 650 | outb(0x00, CIR_RCR(base)); | ||
| 651 | outb(0xc0, CIR_TCR1(base)); | ||
| 652 | outb(0x5c, CIR_TCR2(base)); | ||
| 653 | outb(0x10, CIR_IER(base)); | ||
| 654 | outb(0x00, CIR_BDHR(base)); | ||
| 655 | outb(0x01, CIR_BDLR(base)); | ||
| 656 | outb(0x09, CIR_IER(base)); | ||
| 657 | } | ||
| 658 | |||
| 659 | printk(KERN_INFO PFX "Chip it%04x revision %d initialized. " | ||
| 660 | "timeout=%d sec (nowayout=%d testmode=%d exclusive=%d " | ||
| 661 | "nogameport=%d)\n", chip_type, chip_rev, timeout, | ||
| 662 | nowayout, testmode, exclusive, nogameport); | ||
| 663 | |||
| 664 | return 0; | ||
| 665 | |||
| 666 | err_out_reboot: | ||
| 667 | unregister_reboot_notifier(&wdt_notifier); | ||
| 668 | err_out_region: | ||
| 669 | release_region(base, test_bit(WDTS_USE_GP, &wdt_status) ? 1 : 8); | ||
| 670 | if (!test_bit(WDTS_USE_GP, &wdt_status)) { | ||
| 671 | spin_lock_irqsave(&spinlock, flags); | ||
| 672 | superio_enter(); | ||
| 673 | superio_select(CIR); | ||
| 674 | superio_outb(ciract, ACTREG); | ||
| 675 | superio_exit(); | ||
| 676 | spin_unlock_irqrestore(&spinlock, flags); | ||
| 677 | } | ||
| 678 | err_out: | ||
| 679 | if (chip_type != IT8718_ID && !nogameport) { | ||
| 680 | spin_lock_irqsave(&spinlock, flags); | ||
| 681 | superio_enter(); | ||
| 682 | superio_select(GAMEPORT); | ||
| 683 | superio_outb(gpact, ACTREG); | ||
| 684 | superio_exit(); | ||
| 685 | spin_unlock_irqrestore(&spinlock, flags); | ||
| 686 | } | ||
| 687 | |||
| 688 | return rc; | ||
| 689 | } | ||
| 690 | |||
| 691 | static void __exit it87_wdt_exit(void) | ||
| 692 | { | ||
| 693 | unsigned long flags; | ||
| 694 | int nolock; | ||
| 695 | |||
| 696 | nolock = !spin_trylock_irqsave(&spinlock, flags); | ||
| 697 | superio_enter(); | ||
| 698 | superio_select(GPIO); | ||
| 699 | superio_outb(0x00, WDTCTRL); | ||
| 700 | superio_outb(0x00, WDTCFG); | ||
| 701 | superio_outb(0x00, WDTVALMSB); | ||
| 702 | superio_outb(0x00, WDTVALLSB); | ||
| 703 | if (test_bit(WDTS_USE_GP, &wdt_status)) { | ||
| 704 | superio_select(GAMEPORT); | ||
| 705 | superio_outb(gpact, ACTREG); | ||
| 706 | } else { | ||
| 707 | superio_select(CIR); | ||
| 708 | superio_outb(ciract, ACTREG); | ||
| 709 | } | ||
| 710 | superio_exit(); | ||
| 711 | if (!nolock) | ||
| 712 | spin_unlock_irqrestore(&spinlock, flags); | ||
| 713 | |||
| 714 | misc_deregister(&wdt_miscdev); | ||
| 715 | unregister_reboot_notifier(&wdt_notifier); | ||
| 716 | release_region(base, test_bit(WDTS_USE_GP, &wdt_status) ? 1 : 8); | ||
| 717 | } | ||
| 718 | |||
| 719 | module_init(it87_wdt_init); | ||
| 720 | module_exit(it87_wdt_exit); | ||
| 721 | |||
| 722 | MODULE_AUTHOR("Oliver Schuster"); | ||
| 723 | MODULE_DESCRIPTION("Hardware Watchdog Device Driver for IT87xx EC-LPC I/O"); | ||
| 724 | MODULE_LICENSE("GPL"); | ||
| 725 | MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR); | ||
diff --git a/drivers/watchdog/omap_wdt.c b/drivers/watchdog/omap_wdt.c index 3a11dadfd8e7..7bcbb7f4745f 100644 --- a/drivers/watchdog/omap_wdt.c +++ b/drivers/watchdog/omap_wdt.c | |||
| @@ -1,7 +1,7 @@ | |||
| 1 | /* | 1 | /* |
| 2 | * linux/drivers/char/watchdog/omap_wdt.c | 2 | * omap_wdt.c |
| 3 | * | 3 | * |
| 4 | * Watchdog driver for the TI OMAP 16xx & 24xx 32KHz (non-secure) watchdog | 4 | * Watchdog driver for the TI OMAP 16xx & 24xx/34xx 32KHz (non-secure) watchdog |
| 5 | * | 5 | * |
| 6 | * Author: MontaVista Software, Inc. | 6 | * Author: MontaVista Software, Inc. |
| 7 | * <gdavis@mvista.com> or <source@mvista.com> | 7 | * <gdavis@mvista.com> or <source@mvista.com> |
| @@ -47,50 +47,68 @@ | |||
| 47 | 47 | ||
| 48 | #include "omap_wdt.h" | 48 | #include "omap_wdt.h" |
| 49 | 49 | ||
| 50 | static struct platform_device *omap_wdt_dev; | ||
| 51 | |||
| 50 | static unsigned timer_margin; | 52 | static unsigned timer_margin; |
| 51 | module_param(timer_margin, uint, 0); | 53 | module_param(timer_margin, uint, 0); |
| 52 | MODULE_PARM_DESC(timer_margin, "initial watchdog timeout (in seconds)"); | 54 | MODULE_PARM_DESC(timer_margin, "initial watchdog timeout (in seconds)"); |
| 53 | 55 | ||
| 54 | static int omap_wdt_users; | ||
| 55 | static struct clk *armwdt_ck; | ||
| 56 | static struct clk *mpu_wdt_ick; | ||
| 57 | static struct clk *mpu_wdt_fck; | ||
| 58 | |||
| 59 | static unsigned int wdt_trgr_pattern = 0x1234; | 56 | static unsigned int wdt_trgr_pattern = 0x1234; |
| 60 | static spinlock_t wdt_lock; | 57 | static spinlock_t wdt_lock; |
| 61 | 58 | ||
| 62 | static void omap_wdt_ping(void) | 59 | struct omap_wdt_dev { |
| 60 | void __iomem *base; /* physical */ | ||
| 61 | struct device *dev; | ||
| 62 | int omap_wdt_users; | ||
| 63 | struct clk *armwdt_ck; | ||
| 64 | struct clk *mpu_wdt_ick; | ||
| 65 | struct clk *mpu_wdt_fck; | ||
| 66 | struct resource *mem; | ||
| 67 | struct miscdevice omap_wdt_miscdev; | ||
| 68 | }; | ||
| 69 | |||
| 70 | static void omap_wdt_ping(struct omap_wdt_dev *wdev) | ||
| 63 | { | 71 | { |
| 72 | void __iomem *base = wdev->base; | ||
| 73 | |||
| 64 | /* wait for posted write to complete */ | 74 | /* wait for posted write to complete */ |
| 65 | while ((omap_readl(OMAP_WATCHDOG_WPS)) & 0x08) | 75 | while ((__raw_readl(base + OMAP_WATCHDOG_WPS)) & 0x08) |
| 66 | cpu_relax(); | 76 | cpu_relax(); |
| 77 | |||
| 67 | wdt_trgr_pattern = ~wdt_trgr_pattern; | 78 | wdt_trgr_pattern = ~wdt_trgr_pattern; |
| 68 | omap_writel(wdt_trgr_pattern, (OMAP_WATCHDOG_TGR)); | 79 | __raw_writel(wdt_trgr_pattern, (base + OMAP_WATCHDOG_TGR)); |
| 80 | |||
| 69 | /* wait for posted write to complete */ | 81 | /* wait for posted write to complete */ |
| 70 | while ((omap_readl(OMAP_WATCHDOG_WPS)) & 0x08) | 82 | while ((__raw_readl(base + OMAP_WATCHDOG_WPS)) & 0x08) |
| 71 | cpu_relax(); | 83 | cpu_relax(); |
| 72 | /* reloaded WCRR from WLDR */ | 84 | /* reloaded WCRR from WLDR */ |
| 73 | } | 85 | } |
| 74 | 86 | ||
| 75 | static void omap_wdt_enable(void) | 87 | static void omap_wdt_enable(struct omap_wdt_dev *wdev) |
| 76 | { | 88 | { |
| 89 | void __iomem *base = wdev->base; | ||
| 90 | |||
| 77 | /* Sequence to enable the watchdog */ | 91 | /* Sequence to enable the watchdog */ |
| 78 | omap_writel(0xBBBB, OMAP_WATCHDOG_SPR); | 92 | __raw_writel(0xBBBB, base + OMAP_WATCHDOG_SPR); |
| 79 | while ((omap_readl(OMAP_WATCHDOG_WPS)) & 0x10) | 93 | while ((__raw_readl(base + OMAP_WATCHDOG_WPS)) & 0x10) |
| 80 | cpu_relax(); | 94 | cpu_relax(); |
| 81 | omap_writel(0x4444, OMAP_WATCHDOG_SPR); | 95 | |
| 82 | while ((omap_readl(OMAP_WATCHDOG_WPS)) & 0x10) | 96 | __raw_writel(0x4444, base + OMAP_WATCHDOG_SPR); |
| 97 | while ((__raw_readl(base + OMAP_WATCHDOG_WPS)) & 0x10) | ||
| 83 | cpu_relax(); | 98 | cpu_relax(); |
| 84 | } | 99 | } |
| 85 | 100 | ||
| 86 | static void omap_wdt_disable(void) | 101 | static void omap_wdt_disable(struct omap_wdt_dev *wdev) |
| 87 | { | 102 | { |
| 103 | void __iomem *base = wdev->base; | ||
| 104 | |||
| 88 | /* sequence required to disable watchdog */ | 105 | /* sequence required to disable watchdog */ |
| 89 | omap_writel(0xAAAA, OMAP_WATCHDOG_SPR); /* TIMER_MODE */ | 106 | __raw_writel(0xAAAA, base + OMAP_WATCHDOG_SPR); /* TIMER_MODE */ |
| 90 | while (omap_readl(OMAP_WATCHDOG_WPS) & 0x10) | 107 | while (__raw_readl(base + OMAP_WATCHDOG_WPS) & 0x10) |
| 91 | cpu_relax(); | 108 | cpu_relax(); |
| 92 | omap_writel(0x5555, OMAP_WATCHDOG_SPR); /* TIMER_MODE */ | 109 | |
| 93 | while (omap_readl(OMAP_WATCHDOG_WPS) & 0x10) | 110 | __raw_writel(0x5555, base + OMAP_WATCHDOG_SPR); /* TIMER_MODE */ |
| 111 | while (__raw_readl(base + OMAP_WATCHDOG_WPS) & 0x10) | ||
| 94 | cpu_relax(); | 112 | cpu_relax(); |
| 95 | } | 113 | } |
| 96 | 114 | ||
| @@ -103,83 +121,90 @@ static void omap_wdt_adjust_timeout(unsigned new_timeout) | |||
| 103 | timer_margin = new_timeout; | 121 | timer_margin = new_timeout; |
| 104 | } | 122 | } |
| 105 | 123 | ||
| 106 | static void omap_wdt_set_timeout(void) | 124 | static void omap_wdt_set_timeout(struct omap_wdt_dev *wdev) |
| 107 | { | 125 | { |
| 108 | u32 pre_margin = GET_WLDR_VAL(timer_margin); | 126 | u32 pre_margin = GET_WLDR_VAL(timer_margin); |
| 127 | void __iomem *base = wdev->base; | ||
| 109 | 128 | ||
| 110 | /* just count up at 32 KHz */ | 129 | /* just count up at 32 KHz */ |
| 111 | while (omap_readl(OMAP_WATCHDOG_WPS) & 0x04) | 130 | while (__raw_readl(base + OMAP_WATCHDOG_WPS) & 0x04) |
| 112 | cpu_relax(); | 131 | cpu_relax(); |
| 113 | omap_writel(pre_margin, OMAP_WATCHDOG_LDR); | 132 | |
| 114 | while (omap_readl(OMAP_WATCHDOG_WPS) & 0x04) | 133 | __raw_writel(pre_margin, base + OMAP_WATCHDOG_LDR); |
| 134 | while (__raw_readl(base + OMAP_WATCHDOG_WPS) & 0x04) | ||
| 115 | cpu_relax(); | 135 | cpu_relax(); |
| 116 | } | 136 | } |
| 117 | 137 | ||
| 118 | /* | 138 | /* |
| 119 | * Allow only one task to hold it open | 139 | * Allow only one task to hold it open |
| 120 | */ | 140 | */ |
| 121 | |||
| 122 | static int omap_wdt_open(struct inode *inode, struct file *file) | 141 | static int omap_wdt_open(struct inode *inode, struct file *file) |
| 123 | { | 142 | { |
| 124 | if (test_and_set_bit(1, (unsigned long *)&omap_wdt_users)) | 143 | struct omap_wdt_dev *wdev = platform_get_drvdata(omap_wdt_dev); |
| 144 | void __iomem *base = wdev->base; | ||
| 145 | |||
| 146 | if (test_and_set_bit(1, (unsigned long *)&(wdev->omap_wdt_users))) | ||
| 125 | return -EBUSY; | 147 | return -EBUSY; |
| 126 | 148 | ||
| 127 | if (cpu_is_omap16xx()) | 149 | if (cpu_is_omap16xx()) |
| 128 | clk_enable(armwdt_ck); /* Enable the clock */ | 150 | clk_enable(wdev->armwdt_ck); /* Enable the clock */ |
| 129 | 151 | ||
| 130 | if (cpu_is_omap24xx()) { | 152 | if (cpu_is_omap24xx() || cpu_is_omap34xx()) { |
| 131 | clk_enable(mpu_wdt_ick); /* Enable the interface clock */ | 153 | clk_enable(wdev->mpu_wdt_ick); /* Enable the interface clock */ |
| 132 | clk_enable(mpu_wdt_fck); /* Enable the functional clock */ | 154 | clk_enable(wdev->mpu_wdt_fck); /* Enable the functional clock */ |
| 133 | } | 155 | } |
| 134 | 156 | ||
| 135 | /* initialize prescaler */ | 157 | /* initialize prescaler */ |
| 136 | while (omap_readl(OMAP_WATCHDOG_WPS) & 0x01) | 158 | while (__raw_readl(base + OMAP_WATCHDOG_WPS) & 0x01) |
| 137 | cpu_relax(); | 159 | cpu_relax(); |
| 138 | omap_writel((1 << 5) | (PTV << 2), OMAP_WATCHDOG_CNTRL); | 160 | |
| 139 | while (omap_readl(OMAP_WATCHDOG_WPS) & 0x01) | 161 | __raw_writel((1 << 5) | (PTV << 2), base + OMAP_WATCHDOG_CNTRL); |
| 162 | while (__raw_readl(base + OMAP_WATCHDOG_WPS) & 0x01) | ||
| 140 | cpu_relax(); | 163 | cpu_relax(); |
| 141 | 164 | ||
| 142 | omap_wdt_set_timeout(); | 165 | file->private_data = (void *) wdev; |
| 143 | omap_wdt_enable(); | 166 | |
| 167 | omap_wdt_set_timeout(wdev); | ||
| 168 | omap_wdt_enable(wdev); | ||
| 169 | |||
| 144 | return nonseekable_open(inode, file); | 170 | return nonseekable_open(inode, file); |
| 145 | } | 171 | } |
| 146 | 172 | ||
| 147 | static int omap_wdt_release(struct inode *inode, struct file *file) | 173 | static int omap_wdt_release(struct inode *inode, struct file *file) |
| 148 | { | 174 | { |
| 175 | struct omap_wdt_dev *wdev = file->private_data; | ||
| 176 | |||
| 149 | /* | 177 | /* |
| 150 | * Shut off the timer unless NOWAYOUT is defined. | 178 | * Shut off the timer unless NOWAYOUT is defined. |
| 151 | */ | 179 | */ |
| 152 | #ifndef CONFIG_WATCHDOG_NOWAYOUT | 180 | #ifndef CONFIG_WATCHDOG_NOWAYOUT |
| 153 | omap_wdt_disable(); | ||
| 154 | 181 | ||
| 155 | if (cpu_is_omap16xx()) { | 182 | omap_wdt_disable(wdev); |
| 156 | clk_disable(armwdt_ck); /* Disable the clock */ | ||
| 157 | clk_put(armwdt_ck); | ||
| 158 | armwdt_ck = NULL; | ||
| 159 | } | ||
| 160 | 183 | ||
| 161 | if (cpu_is_omap24xx()) { | 184 | if (cpu_is_omap16xx()) |
| 162 | clk_disable(mpu_wdt_ick); /* Disable the clock */ | 185 | clk_disable(wdev->armwdt_ck); /* Disable the clock */ |
| 163 | clk_disable(mpu_wdt_fck); /* Disable the clock */ | 186 | |
| 164 | clk_put(mpu_wdt_ick); | 187 | if (cpu_is_omap24xx() || cpu_is_omap34xx()) { |
| 165 | clk_put(mpu_wdt_fck); | 188 | clk_disable(wdev->mpu_wdt_ick); /* Disable the clock */ |
| 166 | mpu_wdt_ick = NULL; | 189 | clk_disable(wdev->mpu_wdt_fck); /* Disable the clock */ |
| 167 | mpu_wdt_fck = NULL; | ||
| 168 | } | 190 | } |
| 169 | #else | 191 | #else |
| 170 | printk(KERN_CRIT "omap_wdt: Unexpected close, not stopping!\n"); | 192 | printk(KERN_CRIT "omap_wdt: Unexpected close, not stopping!\n"); |
| 171 | #endif | 193 | #endif |
| 172 | omap_wdt_users = 0; | 194 | wdev->omap_wdt_users = 0; |
| 195 | |||
| 173 | return 0; | 196 | return 0; |
| 174 | } | 197 | } |
| 175 | 198 | ||
| 176 | static ssize_t omap_wdt_write(struct file *file, const char __user *data, | 199 | static ssize_t omap_wdt_write(struct file *file, const char __user *data, |
| 177 | size_t len, loff_t *ppos) | 200 | size_t len, loff_t *ppos) |
| 178 | { | 201 | { |
| 202 | struct omap_wdt_dev *wdev = file->private_data; | ||
| 203 | |||
| 179 | /* Refresh LOAD_TIME. */ | 204 | /* Refresh LOAD_TIME. */ |
| 180 | if (len) { | 205 | if (len) { |
| 181 | spin_lock(&wdt_lock); | 206 | spin_lock(&wdt_lock); |
| 182 | omap_wdt_ping(); | 207 | omap_wdt_ping(wdev); |
| 183 | spin_unlock(&wdt_lock); | 208 | spin_unlock(&wdt_lock); |
| 184 | } | 209 | } |
| 185 | return len; | 210 | return len; |
| @@ -188,6 +213,7 @@ static ssize_t omap_wdt_write(struct file *file, const char __user *data, | |||
| 188 | static long omap_wdt_ioctl(struct file *file, unsigned int cmd, | 213 | static long omap_wdt_ioctl(struct file *file, unsigned int cmd, |
| 189 | unsigned long arg) | 214 | unsigned long arg) |
| 190 | { | 215 | { |
| 216 | struct omap_wdt_dev *wdev; | ||
| 191 | int new_margin; | 217 | int new_margin; |
| 192 | static const struct watchdog_info ident = { | 218 | static const struct watchdog_info ident = { |
| 193 | .identity = "OMAP Watchdog", | 219 | .identity = "OMAP Watchdog", |
| @@ -195,6 +221,8 @@ static long omap_wdt_ioctl(struct file *file, unsigned int cmd, | |||
| 195 | .firmware_version = 0, | 221 | .firmware_version = 0, |
| 196 | }; | 222 | }; |
| 197 | 223 | ||
| 224 | wdev = file->private_data; | ||
| 225 | |||
| 198 | switch (cmd) { | 226 | switch (cmd) { |
| 199 | case WDIOC_GETSUPPORT: | 227 | case WDIOC_GETSUPPORT: |
| 200 | return copy_to_user((struct watchdog_info __user *)arg, &ident, | 228 | return copy_to_user((struct watchdog_info __user *)arg, &ident, |
| @@ -203,14 +231,14 @@ static long omap_wdt_ioctl(struct file *file, unsigned int cmd, | |||
| 203 | return put_user(0, (int __user *)arg); | 231 | return put_user(0, (int __user *)arg); |
| 204 | case WDIOC_GETBOOTSTATUS: | 232 | case WDIOC_GETBOOTSTATUS: |
| 205 | if (cpu_is_omap16xx()) | 233 | if (cpu_is_omap16xx()) |
| 206 | return put_user(omap_readw(ARM_SYSST), | 234 | return put_user(__raw_readw(ARM_SYSST), |
| 207 | (int __user *)arg); | 235 | (int __user *)arg); |
| 208 | if (cpu_is_omap24xx()) | 236 | if (cpu_is_omap24xx()) |
| 209 | return put_user(omap_prcm_get_reset_sources(), | 237 | return put_user(omap_prcm_get_reset_sources(), |
| 210 | (int __user *)arg); | 238 | (int __user *)arg); |
| 211 | case WDIOC_KEEPALIVE: | 239 | case WDIOC_KEEPALIVE: |
| 212 | spin_lock(&wdt_lock); | 240 | spin_lock(&wdt_lock); |
| 213 | omap_wdt_ping(); | 241 | omap_wdt_ping(wdev); |
| 214 | spin_unlock(&wdt_lock); | 242 | spin_unlock(&wdt_lock); |
| 215 | return 0; | 243 | return 0; |
| 216 | case WDIOC_SETTIMEOUT: | 244 | case WDIOC_SETTIMEOUT: |
| @@ -219,11 +247,11 @@ static long omap_wdt_ioctl(struct file *file, unsigned int cmd, | |||
| 219 | omap_wdt_adjust_timeout(new_margin); | 247 | omap_wdt_adjust_timeout(new_margin); |
| 220 | 248 | ||
| 221 | spin_lock(&wdt_lock); | 249 | spin_lock(&wdt_lock); |
| 222 | omap_wdt_disable(); | 250 | omap_wdt_disable(wdev); |
| 223 | omap_wdt_set_timeout(); | 251 | omap_wdt_set_timeout(wdev); |
| 224 | omap_wdt_enable(); | 252 | omap_wdt_enable(wdev); |
| 225 | 253 | ||
| 226 | omap_wdt_ping(); | 254 | omap_wdt_ping(wdev); |
| 227 | spin_unlock(&wdt_lock); | 255 | spin_unlock(&wdt_lock); |
| 228 | /* Fall */ | 256 | /* Fall */ |
| 229 | case WDIOC_GETTIMEOUT: | 257 | case WDIOC_GETTIMEOUT: |
| @@ -241,96 +269,173 @@ static const struct file_operations omap_wdt_fops = { | |||
| 241 | .release = omap_wdt_release, | 269 | .release = omap_wdt_release, |
| 242 | }; | 270 | }; |
| 243 | 271 | ||
| 244 | static struct miscdevice omap_wdt_miscdev = { | ||
| 245 | .minor = WATCHDOG_MINOR, | ||
| 246 | .name = "watchdog", | ||
| 247 | .fops = &omap_wdt_fops, | ||
| 248 | }; | ||
| 249 | |||
| 250 | static int __init omap_wdt_probe(struct platform_device *pdev) | 272 | static int __init omap_wdt_probe(struct platform_device *pdev) |
| 251 | { | 273 | { |
| 252 | struct resource *res, *mem; | 274 | struct resource *res, *mem; |
| 275 | struct omap_wdt_dev *wdev; | ||
| 253 | int ret; | 276 | int ret; |
| 254 | 277 | ||
| 255 | /* reserve static register mappings */ | 278 | /* reserve static register mappings */ |
| 256 | res = platform_get_resource(pdev, IORESOURCE_MEM, 0); | 279 | res = platform_get_resource(pdev, IORESOURCE_MEM, 0); |
| 257 | if (!res) | 280 | if (!res) { |
| 258 | return -ENOENT; | 281 | ret = -ENOENT; |
| 282 | goto err_get_resource; | ||
| 283 | } | ||
| 284 | |||
| 285 | if (omap_wdt_dev) { | ||
| 286 | ret = -EBUSY; | ||
| 287 | goto err_busy; | ||
| 288 | } | ||
| 259 | 289 | ||
| 260 | mem = request_mem_region(res->start, res->end - res->start + 1, | 290 | mem = request_mem_region(res->start, res->end - res->start + 1, |
| 261 | pdev->name); | 291 | pdev->name); |
| 262 | if (mem == NULL) | 292 | if (!mem) { |
| 263 | return -EBUSY; | 293 | ret = -EBUSY; |
| 294 | goto err_busy; | ||
| 295 | } | ||
| 264 | 296 | ||
| 265 | platform_set_drvdata(pdev, mem); | 297 | wdev = kzalloc(sizeof(struct omap_wdt_dev), GFP_KERNEL); |
| 298 | if (!wdev) { | ||
| 299 | ret = -ENOMEM; | ||
| 300 | goto err_kzalloc; | ||
| 301 | } | ||
| 266 | 302 | ||
| 267 | omap_wdt_users = 0; | 303 | wdev->omap_wdt_users = 0; |
| 304 | wdev->mem = mem; | ||
| 268 | 305 | ||
| 269 | if (cpu_is_omap16xx()) { | 306 | if (cpu_is_omap16xx()) { |
| 270 | armwdt_ck = clk_get(&pdev->dev, "armwdt_ck"); | 307 | wdev->armwdt_ck = clk_get(&pdev->dev, "armwdt_ck"); |
| 271 | if (IS_ERR(armwdt_ck)) { | 308 | if (IS_ERR(wdev->armwdt_ck)) { |
| 272 | ret = PTR_ERR(armwdt_ck); | 309 | ret = PTR_ERR(wdev->armwdt_ck); |
| 273 | armwdt_ck = NULL; | 310 | wdev->armwdt_ck = NULL; |
| 274 | goto fail; | 311 | goto err_clk; |
| 275 | } | 312 | } |
| 276 | } | 313 | } |
| 277 | 314 | ||
| 278 | if (cpu_is_omap24xx()) { | 315 | if (cpu_is_omap24xx()) { |
| 279 | mpu_wdt_ick = clk_get(&pdev->dev, "mpu_wdt_ick"); | 316 | wdev->mpu_wdt_ick = clk_get(&pdev->dev, "mpu_wdt_ick"); |
| 280 | if (IS_ERR(mpu_wdt_ick)) { | 317 | if (IS_ERR(wdev->mpu_wdt_ick)) { |
| 281 | ret = PTR_ERR(mpu_wdt_ick); | 318 | ret = PTR_ERR(wdev->mpu_wdt_ick); |
| 282 | mpu_wdt_ick = NULL; | 319 | wdev->mpu_wdt_ick = NULL; |
| 283 | goto fail; | 320 | goto err_clk; |
| 284 | } | 321 | } |
| 285 | mpu_wdt_fck = clk_get(&pdev->dev, "mpu_wdt_fck"); | 322 | wdev->mpu_wdt_fck = clk_get(&pdev->dev, "mpu_wdt_fck"); |
| 286 | if (IS_ERR(mpu_wdt_fck)) { | 323 | if (IS_ERR(wdev->mpu_wdt_fck)) { |
| 287 | ret = PTR_ERR(mpu_wdt_fck); | 324 | ret = PTR_ERR(wdev->mpu_wdt_fck); |
| 288 | mpu_wdt_fck = NULL; | 325 | wdev->mpu_wdt_fck = NULL; |
| 289 | goto fail; | 326 | goto err_clk; |
| 290 | } | 327 | } |
| 291 | } | 328 | } |
| 292 | 329 | ||
| 293 | omap_wdt_disable(); | 330 | if (cpu_is_omap34xx()) { |
| 331 | wdev->mpu_wdt_ick = clk_get(&pdev->dev, "wdt2_ick"); | ||
| 332 | if (IS_ERR(wdev->mpu_wdt_ick)) { | ||
| 333 | ret = PTR_ERR(wdev->mpu_wdt_ick); | ||
| 334 | wdev->mpu_wdt_ick = NULL; | ||
| 335 | goto err_clk; | ||
| 336 | } | ||
| 337 | wdev->mpu_wdt_fck = clk_get(&pdev->dev, "wdt2_fck"); | ||
| 338 | if (IS_ERR(wdev->mpu_wdt_fck)) { | ||
| 339 | ret = PTR_ERR(wdev->mpu_wdt_fck); | ||
| 340 | wdev->mpu_wdt_fck = NULL; | ||
| 341 | goto err_clk; | ||
| 342 | } | ||
| 343 | } | ||
| 344 | wdev->base = ioremap(res->start, res->end - res->start + 1); | ||
| 345 | if (!wdev->base) { | ||
| 346 | ret = -ENOMEM; | ||
| 347 | goto err_ioremap; | ||
| 348 | } | ||
| 349 | |||
| 350 | platform_set_drvdata(pdev, wdev); | ||
| 351 | |||
| 352 | omap_wdt_disable(wdev); | ||
| 294 | omap_wdt_adjust_timeout(timer_margin); | 353 | omap_wdt_adjust_timeout(timer_margin); |
| 295 | 354 | ||
| 296 | omap_wdt_miscdev.parent = &pdev->dev; | 355 | wdev->omap_wdt_miscdev.parent = &pdev->dev; |
| 297 | ret = misc_register(&omap_wdt_miscdev); | 356 | wdev->omap_wdt_miscdev.minor = WATCHDOG_MINOR; |
| 357 | wdev->omap_wdt_miscdev.name = "watchdog"; | ||
| 358 | wdev->omap_wdt_miscdev.fops = &omap_wdt_fops; | ||
| 359 | |||
| 360 | ret = misc_register(&(wdev->omap_wdt_miscdev)); | ||
| 298 | if (ret) | 361 | if (ret) |
| 299 | goto fail; | 362 | goto err_misc; |
| 300 | 363 | ||
| 301 | pr_info("OMAP Watchdog Timer: initial timeout %d sec\n", timer_margin); | 364 | pr_info("OMAP Watchdog Timer Rev 0x%02x: initial timeout %d sec\n", |
| 365 | __raw_readl(wdev->base + OMAP_WATCHDOG_REV) & 0xFF, | ||
| 366 | timer_margin); | ||
| 302 | 367 | ||
| 303 | /* autogate OCP interface clock */ | 368 | /* autogate OCP interface clock */ |
| 304 | omap_writel(0x01, OMAP_WATCHDOG_SYS_CONFIG); | 369 | __raw_writel(0x01, wdev->base + OMAP_WATCHDOG_SYS_CONFIG); |
| 370 | |||
| 371 | omap_wdt_dev = pdev; | ||
| 372 | |||
| 305 | return 0; | 373 | return 0; |
| 306 | 374 | ||
| 307 | fail: | 375 | err_misc: |
| 308 | if (armwdt_ck) | 376 | platform_set_drvdata(pdev, NULL); |
| 309 | clk_put(armwdt_ck); | 377 | iounmap(wdev->base); |
| 310 | if (mpu_wdt_ick) | 378 | |
| 311 | clk_put(mpu_wdt_ick); | 379 | err_ioremap: |
| 312 | if (mpu_wdt_fck) | 380 | wdev->base = NULL; |
| 313 | clk_put(mpu_wdt_fck); | 381 | |
| 314 | release_resource(mem); | 382 | err_clk: |
| 383 | if (wdev->armwdt_ck) | ||
| 384 | clk_put(wdev->armwdt_ck); | ||
| 385 | if (wdev->mpu_wdt_ick) | ||
| 386 | clk_put(wdev->mpu_wdt_ick); | ||
| 387 | if (wdev->mpu_wdt_fck) | ||
| 388 | clk_put(wdev->mpu_wdt_fck); | ||
| 389 | kfree(wdev); | ||
| 390 | |||
| 391 | err_kzalloc: | ||
| 392 | release_mem_region(res->start, res->end - res->start + 1); | ||
| 393 | |||
| 394 | err_busy: | ||
| 395 | err_get_resource: | ||
| 396 | |||
| 315 | return ret; | 397 | return ret; |
| 316 | } | 398 | } |
| 317 | 399 | ||
| 318 | static void omap_wdt_shutdown(struct platform_device *pdev) | 400 | static void omap_wdt_shutdown(struct platform_device *pdev) |
| 319 | { | 401 | { |
| 320 | omap_wdt_disable(); | 402 | struct omap_wdt_dev *wdev = platform_get_drvdata(pdev); |
| 403 | |||
| 404 | if (wdev->omap_wdt_users) | ||
| 405 | omap_wdt_disable(wdev); | ||
| 321 | } | 406 | } |
| 322 | 407 | ||
| 323 | static int omap_wdt_remove(struct platform_device *pdev) | 408 | static int omap_wdt_remove(struct platform_device *pdev) |
| 324 | { | 409 | { |
| 325 | struct resource *mem = platform_get_drvdata(pdev); | 410 | struct omap_wdt_dev *wdev = platform_get_drvdata(pdev); |
| 326 | misc_deregister(&omap_wdt_miscdev); | 411 | struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0); |
| 327 | release_resource(mem); | 412 | |
| 328 | if (armwdt_ck) | 413 | if (!res) |
| 329 | clk_put(armwdt_ck); | 414 | return -ENOENT; |
| 330 | if (mpu_wdt_ick) | 415 | |
| 331 | clk_put(mpu_wdt_ick); | 416 | misc_deregister(&(wdev->omap_wdt_miscdev)); |
| 332 | if (mpu_wdt_fck) | 417 | release_mem_region(res->start, res->end - res->start + 1); |
| 333 | clk_put(mpu_wdt_fck); | 418 | platform_set_drvdata(pdev, NULL); |
| 419 | |||
| 420 | if (wdev->armwdt_ck) { | ||
| 421 | clk_put(wdev->armwdt_ck); | ||
| 422 | wdev->armwdt_ck = NULL; | ||
| 423 | } | ||
| 424 | |||
| 425 | if (wdev->mpu_wdt_ick) { | ||
| 426 | clk_put(wdev->mpu_wdt_ick); | ||
| 427 | wdev->mpu_wdt_ick = NULL; | ||
| 428 | } | ||
| 429 | |||
| 430 | if (wdev->mpu_wdt_fck) { | ||
| 431 | clk_put(wdev->mpu_wdt_fck); | ||
| 432 | wdev->mpu_wdt_fck = NULL; | ||
| 433 | } | ||
| 434 | iounmap(wdev->base); | ||
| 435 | |||
| 436 | kfree(wdev); | ||
| 437 | omap_wdt_dev = NULL; | ||
| 438 | |||
| 334 | return 0; | 439 | return 0; |
| 335 | } | 440 | } |
| 336 | 441 | ||
| @@ -344,17 +449,23 @@ static int omap_wdt_remove(struct platform_device *pdev) | |||
| 344 | 449 | ||
| 345 | static int omap_wdt_suspend(struct platform_device *pdev, pm_message_t state) | 450 | static int omap_wdt_suspend(struct platform_device *pdev, pm_message_t state) |
| 346 | { | 451 | { |
| 347 | if (omap_wdt_users) | 452 | struct omap_wdt_dev *wdev = platform_get_drvdata(pdev); |
| 348 | omap_wdt_disable(); | 453 | |
| 454 | if (wdev->omap_wdt_users) | ||
| 455 | omap_wdt_disable(wdev); | ||
| 456 | |||
| 349 | return 0; | 457 | return 0; |
| 350 | } | 458 | } |
| 351 | 459 | ||
| 352 | static int omap_wdt_resume(struct platform_device *pdev) | 460 | static int omap_wdt_resume(struct platform_device *pdev) |
| 353 | { | 461 | { |
| 354 | if (omap_wdt_users) { | 462 | struct omap_wdt_dev *wdev = platform_get_drvdata(pdev); |
| 355 | omap_wdt_enable(); | 463 | |
| 356 | omap_wdt_ping(); | 464 | if (wdev->omap_wdt_users) { |
| 465 | omap_wdt_enable(wdev); | ||
| 466 | omap_wdt_ping(wdev); | ||
| 357 | } | 467 | } |
| 468 | |||
| 358 | return 0; | 469 | return 0; |
| 359 | } | 470 | } |
| 360 | 471 | ||
diff --git a/drivers/watchdog/omap_wdt.h b/drivers/watchdog/omap_wdt.h index 52a532a5114a..fc02ec6a0386 100644 --- a/drivers/watchdog/omap_wdt.h +++ b/drivers/watchdog/omap_wdt.h | |||
| @@ -30,25 +30,15 @@ | |||
| 30 | #ifndef _OMAP_WATCHDOG_H | 30 | #ifndef _OMAP_WATCHDOG_H |
| 31 | #define _OMAP_WATCHDOG_H | 31 | #define _OMAP_WATCHDOG_H |
| 32 | 32 | ||
| 33 | #define OMAP1610_WATCHDOG_BASE 0xfffeb000 | 33 | #define OMAP_WATCHDOG_REV (0x00) |
| 34 | #define OMAP2420_WATCHDOG_BASE 0x48022000 /*WDT Timer 2 */ | 34 | #define OMAP_WATCHDOG_SYS_CONFIG (0x10) |
| 35 | 35 | #define OMAP_WATCHDOG_STATUS (0x14) | |
| 36 | #ifdef CONFIG_ARCH_OMAP24XX | 36 | #define OMAP_WATCHDOG_CNTRL (0x24) |
| 37 | #define OMAP_WATCHDOG_BASE OMAP2420_WATCHDOG_BASE | 37 | #define OMAP_WATCHDOG_CRR (0x28) |
| 38 | #else | 38 | #define OMAP_WATCHDOG_LDR (0x2c) |
| 39 | #define OMAP_WATCHDOG_BASE OMAP1610_WATCHDOG_BASE | 39 | #define OMAP_WATCHDOG_TGR (0x30) |
| 40 | #define RM_RSTST_WKUP 0 | 40 | #define OMAP_WATCHDOG_WPS (0x34) |
| 41 | #endif | 41 | #define OMAP_WATCHDOG_SPR (0x48) |
| 42 | |||
| 43 | #define OMAP_WATCHDOG_REV (OMAP_WATCHDOG_BASE + 0x00) | ||
| 44 | #define OMAP_WATCHDOG_SYS_CONFIG (OMAP_WATCHDOG_BASE + 0x10) | ||
| 45 | #define OMAP_WATCHDOG_STATUS (OMAP_WATCHDOG_BASE + 0x14) | ||
| 46 | #define OMAP_WATCHDOG_CNTRL (OMAP_WATCHDOG_BASE + 0x24) | ||
| 47 | #define OMAP_WATCHDOG_CRR (OMAP_WATCHDOG_BASE + 0x28) | ||
| 48 | #define OMAP_WATCHDOG_LDR (OMAP_WATCHDOG_BASE + 0x2c) | ||
| 49 | #define OMAP_WATCHDOG_TGR (OMAP_WATCHDOG_BASE + 0x30) | ||
| 50 | #define OMAP_WATCHDOG_WPS (OMAP_WATCHDOG_BASE + 0x34) | ||
| 51 | #define OMAP_WATCHDOG_SPR (OMAP_WATCHDOG_BASE + 0x48) | ||
| 52 | 42 | ||
| 53 | /* Using the prescaler, the OMAP watchdog could go for many | 43 | /* Using the prescaler, the OMAP watchdog could go for many |
| 54 | * months before firing. These limits work without scaling, | 44 | * months before firing. These limits work without scaling, |
diff --git a/drivers/watchdog/orion5x_wdt.c b/drivers/watchdog/orion5x_wdt.c new file mode 100644 index 000000000000..14a339f58b6a --- /dev/null +++ b/drivers/watchdog/orion5x_wdt.c | |||
| @@ -0,0 +1,245 @@ | |||
| 1 | /* | ||
| 2 | * drivers/watchdog/orion5x_wdt.c | ||
| 3 | * | ||
| 4 | * Watchdog driver for Orion5x processors | ||
| 5 | * | ||
| 6 | * Author: Sylver Bruneau <sylver.bruneau@googlemail.com> | ||
| 7 | * | ||
| 8 | * This file is licensed under the terms of the GNU General Public | ||
| 9 | * License version 2. This program is licensed "as is" without any | ||
| 10 | * warranty of any kind, whether express or implied. | ||
| 11 | */ | ||
| 12 | |||
| 13 | #include <linux/module.h> | ||
| 14 | #include <linux/moduleparam.h> | ||
| 15 | #include <linux/types.h> | ||
| 16 | #include <linux/kernel.h> | ||
| 17 | #include <linux/fs.h> | ||
| 18 | #include <linux/miscdevice.h> | ||
| 19 | #include <linux/watchdog.h> | ||
| 20 | #include <linux/init.h> | ||
| 21 | #include <linux/uaccess.h> | ||
| 22 | #include <linux/io.h> | ||
| 23 | #include <linux/spinlock.h> | ||
| 24 | |||
| 25 | /* | ||
| 26 | * Watchdog timer block registers. | ||
| 27 | */ | ||
| 28 | #define TIMER_CTRL (TIMER_VIRT_BASE + 0x0000) | ||
| 29 | #define WDT_EN 0x0010 | ||
| 30 | #define WDT_VAL (TIMER_VIRT_BASE + 0x0024) | ||
| 31 | |||
| 32 | #define WDT_MAX_DURATION (0xffffffff / ORION5X_TCLK) | ||
| 33 | #define WDT_IN_USE 0 | ||
| 34 | #define WDT_OK_TO_CLOSE 1 | ||
| 35 | |||
| 36 | static int nowayout = WATCHDOG_NOWAYOUT; | ||
| 37 | static int heartbeat = WDT_MAX_DURATION; /* (seconds) */ | ||
| 38 | static unsigned long wdt_status; | ||
| 39 | static spinlock_t wdt_lock; | ||
| 40 | |||
| 41 | static void wdt_enable(void) | ||
| 42 | { | ||
| 43 | u32 reg; | ||
| 44 | |||
| 45 | spin_lock(&wdt_lock); | ||
| 46 | |||
| 47 | /* Set watchdog duration */ | ||
| 48 | writel(ORION5X_TCLK * heartbeat, WDT_VAL); | ||
| 49 | |||
| 50 | /* Clear watchdog timer interrupt */ | ||
| 51 | reg = readl(BRIDGE_CAUSE); | ||
| 52 | reg &= ~WDT_INT_REQ; | ||
| 53 | writel(reg, BRIDGE_CAUSE); | ||
| 54 | |||
| 55 | /* Enable watchdog timer */ | ||
| 56 | reg = readl(TIMER_CTRL); | ||
| 57 | reg |= WDT_EN; | ||
| 58 | writel(reg, TIMER_CTRL); | ||
| 59 | |||
| 60 | /* Enable reset on watchdog */ | ||
| 61 | reg = readl(CPU_RESET_MASK); | ||
| 62 | reg |= WDT_RESET; | ||
| 63 | writel(reg, CPU_RESET_MASK); | ||
| 64 | |||
| 65 | spin_unlock(&wdt_lock); | ||
| 66 | } | ||
| 67 | |||
| 68 | static void wdt_disable(void) | ||
| 69 | { | ||
| 70 | u32 reg; | ||
| 71 | |||
| 72 | spin_lock(&wdt_lock); | ||
| 73 | |||
| 74 | /* Disable reset on watchdog */ | ||
| 75 | reg = readl(CPU_RESET_MASK); | ||
| 76 | reg &= ~WDT_RESET; | ||
| 77 | writel(reg, CPU_RESET_MASK); | ||
| 78 | |||
| 79 | /* Disable watchdog timer */ | ||
| 80 | reg = readl(TIMER_CTRL); | ||
| 81 | reg &= ~WDT_EN; | ||
| 82 | writel(reg, TIMER_CTRL); | ||
| 83 | |||
| 84 | spin_unlock(&wdt_lock); | ||
| 85 | } | ||
| 86 | |||
| 87 | static int orion5x_wdt_get_timeleft(int *time_left) | ||
| 88 | { | ||
| 89 | spin_lock(&wdt_lock); | ||
| 90 | *time_left = readl(WDT_VAL) / ORION5X_TCLK; | ||
| 91 | spin_unlock(&wdt_lock); | ||
| 92 | return 0; | ||
| 93 | } | ||
| 94 | |||
| 95 | static int orion5x_wdt_open(struct inode *inode, struct file *file) | ||
| 96 | { | ||
| 97 | if (test_and_set_bit(WDT_IN_USE, &wdt_status)) | ||
| 98 | return -EBUSY; | ||
| 99 | clear_bit(WDT_OK_TO_CLOSE, &wdt_status); | ||
| 100 | wdt_enable(); | ||
| 101 | return nonseekable_open(inode, file); | ||
| 102 | } | ||
| 103 | |||
| 104 | static ssize_t orion5x_wdt_write(struct file *file, const char *data, | ||
| 105 | size_t len, loff_t *ppos) | ||
| 106 | { | ||
| 107 | if (len) { | ||
| 108 | if (!nowayout) { | ||
| 109 | size_t i; | ||
| 110 | |||
| 111 | clear_bit(WDT_OK_TO_CLOSE, &wdt_status); | ||
| 112 | for (i = 0; i != len; i++) { | ||
| 113 | char c; | ||
| 114 | |||
| 115 | if (get_user(c, data + i)) | ||
| 116 | return -EFAULT; | ||
| 117 | if (c == 'V') | ||
| 118 | set_bit(WDT_OK_TO_CLOSE, &wdt_status); | ||
| 119 | } | ||
| 120 | } | ||
| 121 | wdt_enable(); | ||
| 122 | } | ||
| 123 | return len; | ||
| 124 | } | ||
| 125 | |||
| 126 | static struct watchdog_info ident = { | ||
| 127 | .options = WDIOF_MAGICCLOSE | WDIOF_SETTIMEOUT | | ||
| 128 | WDIOF_KEEPALIVEPING, | ||
| 129 | .identity = "Orion5x Watchdog", | ||
| 130 | }; | ||
| 131 | |||
| 132 | |||
| 133 | static long orion5x_wdt_ioctl(struct file *file, unsigned int cmd, | ||
| 134 | unsigned long arg) | ||
| 135 | { | ||
| 136 | int ret = -ENOTTY; | ||
| 137 | int time; | ||
| 138 | |||
| 139 | switch (cmd) { | ||
| 140 | case WDIOC_GETSUPPORT: | ||
| 141 | ret = copy_to_user((struct watchdog_info *)arg, &ident, | ||
| 142 | sizeof(ident)) ? -EFAULT : 0; | ||
| 143 | break; | ||
| 144 | |||
| 145 | case WDIOC_GETSTATUS: | ||
| 146 | case WDIOC_GETBOOTSTATUS: | ||
| 147 | ret = put_user(0, (int *)arg); | ||
| 148 | break; | ||
| 149 | |||
| 150 | case WDIOC_KEEPALIVE: | ||
| 151 | wdt_enable(); | ||
| 152 | ret = 0; | ||
| 153 | break; | ||
| 154 | |||
| 155 | case WDIOC_SETTIMEOUT: | ||
| 156 | ret = get_user(time, (int *)arg); | ||
| 157 | if (ret) | ||
| 158 | break; | ||
| 159 | |||
| 160 | if (time <= 0 || time > WDT_MAX_DURATION) { | ||
| 161 | ret = -EINVAL; | ||
| 162 | break; | ||
| 163 | } | ||
| 164 | heartbeat = time; | ||
| 165 | wdt_enable(); | ||
| 166 | /* Fall through */ | ||
| 167 | |||
| 168 | case WDIOC_GETTIMEOUT: | ||
| 169 | ret = put_user(heartbeat, (int *)arg); | ||
| 170 | break; | ||
| 171 | |||
| 172 | case WDIOC_GETTIMELEFT: | ||
| 173 | if (orion5x_wdt_get_timeleft(&time)) { | ||
| 174 | ret = -EINVAL; | ||
| 175 | break; | ||
| 176 | } | ||
| 177 | ret = put_user(time, (int *)arg); | ||
| 178 | break; | ||
| 179 | } | ||
| 180 | return ret; | ||
| 181 | } | ||
| 182 | |||
| 183 | static int orion5x_wdt_release(struct inode *inode, struct file *file) | ||
| 184 | { | ||
| 185 | if (test_bit(WDT_OK_TO_CLOSE, &wdt_status)) | ||
| 186 | wdt_disable(); | ||
| 187 | else | ||
| 188 | printk(KERN_CRIT "WATCHDOG: Device closed unexpectedly - " | ||
| 189 | "timer will not stop\n"); | ||
| 190 | clear_bit(WDT_IN_USE, &wdt_status); | ||
| 191 | clear_bit(WDT_OK_TO_CLOSE, &wdt_status); | ||
| 192 | |||
| 193 | return 0; | ||
| 194 | } | ||
| 195 | |||
| 196 | |||
| 197 | static const struct file_operations orion5x_wdt_fops = { | ||
| 198 | .owner = THIS_MODULE, | ||
| 199 | .llseek = no_llseek, | ||
| 200 | .write = orion5x_wdt_write, | ||
| 201 | .unlocked_ioctl = orion5x_wdt_ioctl, | ||
| 202 | .open = orion5x_wdt_open, | ||
| 203 | .release = orion5x_wdt_release, | ||
| 204 | }; | ||
| 205 | |||
| 206 | static struct miscdevice orion5x_wdt_miscdev = { | ||
| 207 | .minor = WATCHDOG_MINOR, | ||
| 208 | .name = "watchdog", | ||
| 209 | .fops = &orion5x_wdt_fops, | ||
| 210 | }; | ||
| 211 | |||
| 212 | static int __init orion5x_wdt_init(void) | ||
| 213 | { | ||
| 214 | int ret; | ||
| 215 | |||
| 216 | spin_lock_init(&wdt_lock); | ||
| 217 | |||
| 218 | ret = misc_register(&orion5x_wdt_miscdev); | ||
| 219 | if (ret == 0) | ||
| 220 | printk("Orion5x Watchdog Timer: heartbeat %d sec\n", | ||
| 221 | heartbeat); | ||
| 222 | |||
| 223 | return ret; | ||
| 224 | } | ||
| 225 | |||
| 226 | static void __exit orion5x_wdt_exit(void) | ||
| 227 | { | ||
| 228 | misc_deregister(&orion5x_wdt_miscdev); | ||
| 229 | } | ||
| 230 | |||
| 231 | module_init(orion5x_wdt_init); | ||
| 232 | module_exit(orion5x_wdt_exit); | ||
| 233 | |||
| 234 | MODULE_AUTHOR("Sylver Bruneau <sylver.bruneau@googlemail.com>"); | ||
| 235 | MODULE_DESCRIPTION("Orion5x Processor Watchdog"); | ||
| 236 | |||
| 237 | module_param(heartbeat, int, 0); | ||
| 238 | MODULE_PARM_DESC(heartbeat, "Watchdog heartbeat in seconds (default is " | ||
| 239 | __MODULE_STRING(WDT_MAX_DURATION) ")"); | ||
| 240 | |||
| 241 | module_param(nowayout, int, 0); | ||
| 242 | MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started"); | ||
| 243 | |||
| 244 | MODULE_LICENSE("GPL"); | ||
| 245 | MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR); | ||
diff --git a/drivers/watchdog/w83697ug_wdt.c b/drivers/watchdog/w83697ug_wdt.c new file mode 100644 index 000000000000..c73b5e2919c6 --- /dev/null +++ b/drivers/watchdog/w83697ug_wdt.c | |||
| @@ -0,0 +1,392 @@ | |||
| 1 | /* | ||
| 2 | * w83697ug/uf WDT driver | ||
| 3 | * | ||
| 4 | * (c) Copyright 2008 Flemming Fransen <ff@nrvissing.net> | ||
| 5 | * reused original code to supoprt w83697ug/uf. | ||
| 6 | * | ||
| 7 | * Based on w83627hf_wdt.c which is based on advantechwdt.c | ||
| 8 | * which is based on wdt.c. | ||
| 9 | * Original copyright messages: | ||
| 10 | * | ||
| 11 | * (c) Copyright 2007 Vlad Drukker <vlad@storewiz.com> | ||
| 12 | * added support for W83627THF. | ||
| 13 | * | ||
| 14 | * (c) Copyright 2003 Pádraig Brady <P@draigBrady.com> | ||
| 15 | * | ||
| 16 | * (c) Copyright 2000-2001 Marek Michalkiewicz <marekm@linux.org.pl> | ||
| 17 | * | ||
| 18 | * (c) Copyright 1996 Alan Cox <alan@redhat.com>, All Rights Reserved. | ||
| 19 | * http://www.redhat.com | ||
| 20 | * | ||
| 21 | * This program is free software; you can redistribute it and/or | ||
| 22 | * modify it under the terms of the GNU General Public License | ||
| 23 | * as published by the Free Software Foundation; either version | ||
| 24 | * 2 of the License, or (at your option) any later version. | ||
| 25 | * | ||
| 26 | * Neither Alan Cox nor CymruNet Ltd. admit liability nor provide | ||
| 27 | * warranty for any of this software. This material is provided | ||
| 28 | * "AS-IS" and at no charge. | ||
| 29 | * | ||
| 30 | * (c) Copyright 1995 Alan Cox <alan@redhat.com> | ||
| 31 | */ | ||
| 32 | |||
| 33 | #include <linux/module.h> | ||
| 34 | #include <linux/moduleparam.h> | ||
| 35 | #include <linux/types.h> | ||
| 36 | #include <linux/miscdevice.h> | ||
| 37 | #include <linux/watchdog.h> | ||
| 38 | #include <linux/fs.h> | ||
| 39 | #include <linux/ioport.h> | ||
| 40 | #include <linux/notifier.h> | ||
| 41 | #include <linux/reboot.h> | ||
| 42 | #include <linux/init.h> | ||
| 43 | #include <linux/spinlock.h> | ||
| 44 | #include <linux/io.h> | ||
| 45 | #include <linux/uaccess.h> | ||
| 46 | |||
| 47 | #include <asm/system.h> | ||
| 48 | |||
| 49 | #define WATCHDOG_NAME "w83697ug/uf WDT" | ||
| 50 | #define PFX WATCHDOG_NAME ": " | ||
| 51 | #define WATCHDOG_TIMEOUT 60 /* 60 sec default timeout */ | ||
| 52 | |||
| 53 | static unsigned long wdt_is_open; | ||
| 54 | static char expect_close; | ||
| 55 | static DEFINE_SPINLOCK(io_lock); | ||
| 56 | |||
| 57 | static int wdt_io = 0x2e; | ||
| 58 | module_param(wdt_io, int, 0); | ||
| 59 | MODULE_PARM_DESC(wdt_io, "w83697ug/uf WDT io port (default 0x2e)"); | ||
| 60 | |||
| 61 | static int timeout = WATCHDOG_TIMEOUT; /* in seconds */ | ||
| 62 | module_param(timeout, int, 0); | ||
| 63 | MODULE_PARM_DESC(timeout, | ||
| 64 | "Watchdog timeout in seconds. 1<= timeout <=255 (default=" | ||
| 65 | __MODULE_STRING(WATCHDOG_TIMEOUT) ")"); | ||
| 66 | |||
| 67 | static int nowayout = WATCHDOG_NOWAYOUT; | ||
| 68 | module_param(nowayout, int, 0); | ||
| 69 | MODULE_PARM_DESC(nowayout, | ||
| 70 | "Watchdog cannot be stopped once started (default=" | ||
| 71 | __MODULE_STRING(WATCHDOG_NOWAYOUT) ")"); | ||
| 72 | |||
| 73 | /* | ||
| 74 | * Kernel methods. | ||
| 75 | */ | ||
| 76 | |||
| 77 | #define WDT_EFER (wdt_io+0) /* Extended Function Enable Registers */ | ||
| 78 | #define WDT_EFIR (wdt_io+0) /* Extended Function Index Register | ||
| 79 | (same as EFER) */ | ||
| 80 | #define WDT_EFDR (WDT_EFIR+1) /* Extended Function Data Register */ | ||
| 81 | |||
| 82 | static void w83697ug_select_wd_register(void) | ||
| 83 | { | ||
| 84 | unsigned char c; | ||
| 85 | unsigned char version; | ||
| 86 | |||
| 87 | outb_p(0x87, WDT_EFER); /* Enter extended function mode */ | ||
| 88 | outb_p(0x87, WDT_EFER); /* Again according to manual */ | ||
| 89 | |||
| 90 | outb(0x20, WDT_EFER); /* check chip version */ | ||
| 91 | version = inb(WDT_EFDR); | ||
| 92 | |||
| 93 | if (version == 0x68) { /* W83697UG */ | ||
| 94 | printk(KERN_INFO PFX "Watchdog chip version 0x%02x = " | ||
| 95 | "W83697UG/UF found at 0x%04x\n", version, wdt_io); | ||
| 96 | |||
| 97 | outb_p(0x2b, WDT_EFER); | ||
| 98 | c = inb_p(WDT_EFDR); /* select WDT0 */ | ||
| 99 | c &= ~0x04; | ||
| 100 | outb_p(0x2b, WDT_EFER); | ||
| 101 | outb_p(c, WDT_EFDR); /* set pin118 to WDT0 */ | ||
| 102 | |||
| 103 | } else { | ||
| 104 | printk(KERN_ERR PFX "No W83697UG/UF could be found\n"); | ||
| 105 | return -EIO; | ||
| 106 | } | ||
| 107 | |||
| 108 | outb_p(0x07, WDT_EFER); /* point to logical device number reg */ | ||
| 109 | outb_p(0x08, WDT_EFDR); /* select logical device 8 (GPIO2) */ | ||
| 110 | outb_p(0x30, WDT_EFER); /* select CR30 */ | ||
| 111 | c = inb_p(WDT_EFDR); | ||
| 112 | outb_p(c || 0x01, WDT_EFDR); /* set bit 0 to activate GPIO2 */ | ||
| 113 | } | ||
| 114 | |||
| 115 | static void w83697ug_unselect_wd_register(void) | ||
| 116 | { | ||
| 117 | outb_p(0xAA, WDT_EFER); /* Leave extended function mode */ | ||
| 118 | } | ||
| 119 | |||
| 120 | static void w83697ug_init(void) | ||
| 121 | { | ||
| 122 | unsigned char t; | ||
| 123 | |||
| 124 | w83697ug_select_wd_register(); | ||
| 125 | |||
| 126 | outb_p(0xF6, WDT_EFER); /* Select CRF6 */ | ||
| 127 | t = inb_p(WDT_EFDR); /* read CRF6 */ | ||
| 128 | if (t != 0) { | ||
| 129 | printk(KERN_INFO PFX "Watchdog already running." | ||
| 130 | " Resetting timeout to %d sec\n", timeout); | ||
| 131 | outb_p(timeout, WDT_EFDR); /* Write back to CRF6 */ | ||
| 132 | } | ||
| 133 | outb_p(0xF5, WDT_EFER); /* Select CRF5 */ | ||
| 134 | t = inb_p(WDT_EFDR); /* read CRF5 */ | ||
| 135 | t &= ~0x0C; /* set second mode & | ||
| 136 | disable keyboard turning off watchdog */ | ||
| 137 | outb_p(t, WDT_EFDR); /* Write back to CRF5 */ | ||
| 138 | |||
| 139 | w83697ug_unselect_wd_register(); | ||
| 140 | } | ||
| 141 | |||
| 142 | static void wdt_ctrl(int timeout) | ||
| 143 | { | ||
| 144 | spin_lock(&io_lock); | ||
| 145 | |||
| 146 | w83697ug_select_wd_register(); | ||
| 147 | |||
| 148 | outb_p(0xF4, WDT_EFER); /* Select CRF4 */ | ||
| 149 | outb_p(timeout, WDT_EFDR); /* Write Timeout counter to CRF4 */ | ||
| 150 | |||
| 151 | w83697ug_unselect_wd_register(); | ||
| 152 | |||
| 153 | spin_unlock(&io_lock); | ||
| 154 | } | ||
| 155 | |||
| 156 | static int wdt_ping(void) | ||
| 157 | { | ||
| 158 | wdt_ctrl(timeout); | ||
| 159 | return 0; | ||
| 160 | } | ||
| 161 | |||
| 162 | static int wdt_disable(void) | ||
| 163 | { | ||
| 164 | wdt_ctrl(0); | ||
| 165 | return 0; | ||
| 166 | } | ||
| 167 | |||
| 168 | static int wdt_set_heartbeat(int t) | ||
| 169 | { | ||
| 170 | if (t < 1 || t > 255) | ||
| 171 | return -EINVAL; | ||
| 172 | |||
| 173 | timeout = t; | ||
| 174 | return 0; | ||
| 175 | } | ||
| 176 | |||
| 177 | static ssize_t wdt_write(struct file *file, const char __user *buf, | ||
| 178 | size_t count, loff_t *ppos) | ||
| 179 | { | ||
| 180 | if (count) { | ||
| 181 | if (!nowayout) { | ||
| 182 | size_t i; | ||
| 183 | |||
| 184 | expect_close = 0; | ||
| 185 | |||
| 186 | for (i = 0; i != count; i++) { | ||
| 187 | char c; | ||
| 188 | if (get_user(c, buf + i)) | ||
| 189 | return -EFAULT; | ||
| 190 | if (c == 'V') | ||
| 191 | expect_close = 42; | ||
| 192 | } | ||
| 193 | } | ||
| 194 | wdt_ping(); | ||
| 195 | } | ||
| 196 | return count; | ||
| 197 | } | ||
| 198 | |||
| 199 | static long wdt_ioctl(struct file *file, unsigned int cmd, unsigned long arg) | ||
| 200 | { | ||
| 201 | void __user *argp = (void __user *)arg; | ||
| 202 | int __user *p = argp; | ||
| 203 | int new_timeout; | ||
| 204 | static const struct watchdog_info ident = { | ||
| 205 | .options = WDIOF_KEEPALIVEPING | | ||
| 206 | WDIOF_SETTIMEOUT | | ||
| 207 | WDIOF_MAGICCLOSE, | ||
| 208 | .firmware_version = 1, | ||
| 209 | .identity = "W83697UG WDT", | ||
| 210 | }; | ||
| 211 | |||
| 212 | switch (cmd) { | ||
| 213 | case WDIOC_GETSUPPORT: | ||
| 214 | if (copy_to_user(argp, &ident, sizeof(ident))) | ||
| 215 | return -EFAULT; | ||
| 216 | break; | ||
| 217 | |||
| 218 | case WDIOC_GETSTATUS: | ||
| 219 | case WDIOC_GETBOOTSTATUS: | ||
| 220 | return put_user(0, p); | ||
| 221 | |||
| 222 | case WDIOC_SETOPTIONS: | ||
| 223 | { | ||
| 224 | int options, retval = -EINVAL; | ||
| 225 | |||
| 226 | if (get_user(options, p)) | ||
| 227 | return -EFAULT; | ||
| 228 | |||
| 229 | if (options & WDIOS_DISABLECARD) { | ||
| 230 | wdt_disable(); | ||
| 231 | retval = 0; | ||
| 232 | } | ||
| 233 | |||
| 234 | if (options & WDIOS_ENABLECARD) { | ||
| 235 | wdt_ping(); | ||
| 236 | retval = 0; | ||
| 237 | } | ||
| 238 | |||
| 239 | return retval; | ||
| 240 | } | ||
| 241 | |||
| 242 | case WDIOC_KEEPALIVE: | ||
| 243 | wdt_ping(); | ||
| 244 | break; | ||
| 245 | |||
| 246 | case WDIOC_SETTIMEOUT: | ||
| 247 | if (get_user(new_timeout, p)) | ||
| 248 | return -EFAULT; | ||
| 249 | if (wdt_set_heartbeat(new_timeout)) | ||
| 250 | return -EINVAL; | ||
| 251 | wdt_ping(); | ||
| 252 | /* Fall */ | ||
| 253 | |||
| 254 | case WDIOC_GETTIMEOUT: | ||
| 255 | return put_user(timeout, p); | ||
| 256 | |||
| 257 | default: | ||
| 258 | return -ENOTTY; | ||
| 259 | } | ||
| 260 | return 0; | ||
| 261 | } | ||
| 262 | |||
| 263 | static int wdt_open(struct inode *inode, struct file *file) | ||
| 264 | { | ||
| 265 | if (test_and_set_bit(0, &wdt_is_open)) | ||
| 266 | return -EBUSY; | ||
| 267 | /* | ||
| 268 | * Activate | ||
| 269 | */ | ||
| 270 | |||
| 271 | wdt_ping(); | ||
| 272 | return nonseekable_open(inode, file); | ||
| 273 | } | ||
| 274 | |||
| 275 | static int wdt_close(struct inode *inode, struct file *file) | ||
| 276 | { | ||
| 277 | if (expect_close == 42) | ||
| 278 | wdt_disable(); | ||
| 279 | else { | ||
| 280 | printk(KERN_CRIT PFX | ||
| 281 | "Unexpected close, not stopping watchdog!\n"); | ||
| 282 | wdt_ping(); | ||
| 283 | } | ||
| 284 | expect_close = 0; | ||
| 285 | clear_bit(0, &wdt_is_open); | ||
| 286 | return 0; | ||
| 287 | } | ||
| 288 | |||
| 289 | /* | ||
| 290 | * Notifier for system down | ||
| 291 | */ | ||
| 292 | |||
| 293 | static int wdt_notify_sys(struct notifier_block *this, unsigned long code, | ||
| 294 | void *unused) | ||
| 295 | { | ||
| 296 | if (code == SYS_DOWN || code == SYS_HALT) | ||
| 297 | wdt_disable(); /* Turn the WDT off */ | ||
| 298 | |||
| 299 | return NOTIFY_DONE; | ||
| 300 | } | ||
| 301 | |||
| 302 | /* | ||
| 303 | * Kernel Interfaces | ||
| 304 | */ | ||
| 305 | |||
| 306 | static const struct file_operations wdt_fops = { | ||
| 307 | .owner = THIS_MODULE, | ||
| 308 | .llseek = no_llseek, | ||
| 309 | .write = wdt_write, | ||
| 310 | .unlocked_ioctl = wdt_ioctl, | ||
| 311 | .open = wdt_open, | ||
| 312 | .release = wdt_close, | ||
| 313 | }; | ||
| 314 | |||
| 315 | static struct miscdevice wdt_miscdev = { | ||
| 316 | .minor = WATCHDOG_MINOR, | ||
| 317 | .name = "watchdog", | ||
| 318 | .fops = &wdt_fops, | ||
| 319 | }; | ||
| 320 | |||
| 321 | /* | ||
| 322 | * The WDT needs to learn about soft shutdowns in order to | ||
| 323 | * turn the timebomb registers off. | ||
| 324 | */ | ||
| 325 | |||
| 326 | static struct notifier_block wdt_notifier = { | ||
| 327 | .notifier_call = wdt_notify_sys, | ||
| 328 | }; | ||
| 329 | |||
| 330 | static int __init wdt_init(void) | ||
| 331 | { | ||
| 332 | int ret; | ||
| 333 | |||
| 334 | printk(KERN_INFO "WDT driver for the Winbond(TM) W83697UG/UF Super I/O chip initialising.\n"); | ||
| 335 | |||
| 336 | if (wdt_set_heartbeat(timeout)) { | ||
| 337 | wdt_set_heartbeat(WATCHDOG_TIMEOUT); | ||
| 338 | printk(KERN_INFO PFX | ||
| 339 | "timeout value must be 1<=timeout<=255, using %d\n", | ||
| 340 | WATCHDOG_TIMEOUT); | ||
| 341 | } | ||
| 342 | |||
| 343 | if (!request_region(wdt_io, 1, WATCHDOG_NAME)) { | ||
| 344 | printk(KERN_ERR PFX "I/O address 0x%04x already in use\n", | ||
| 345 | wdt_io); | ||
| 346 | ret = -EIO; | ||
| 347 | goto out; | ||
| 348 | } | ||
| 349 | |||
| 350 | w83697ug_init(); | ||
| 351 | |||
| 352 | ret = register_reboot_notifier(&wdt_notifier); | ||
| 353 | if (ret != 0) { | ||
| 354 | printk(KERN_ERR PFX | ||
| 355 | "cannot register reboot notifier (err=%d)\n", ret); | ||
| 356 | goto unreg_regions; | ||
| 357 | } | ||
| 358 | |||
| 359 | ret = misc_register(&wdt_miscdev); | ||
| 360 | if (ret != 0) { | ||
| 361 | printk(KERN_ERR PFX | ||
| 362 | "cannot register miscdev on minor=%d (err=%d)\n", | ||
| 363 | WATCHDOG_MINOR, ret); | ||
| 364 | goto unreg_reboot; | ||
| 365 | } | ||
| 366 | |||
| 367 | printk(KERN_INFO PFX "initialized. timeout=%d sec (nowayout=%d)\n", | ||
| 368 | timeout, nowayout); | ||
| 369 | |||
| 370 | out: | ||
| 371 | return ret; | ||
| 372 | unreg_reboot: | ||
| 373 | unregister_reboot_notifier(&wdt_notifier); | ||
| 374 | unreg_regions: | ||
| 375 | release_region(wdt_io, 1); | ||
| 376 | goto out; | ||
| 377 | } | ||
| 378 | |||
| 379 | static void __exit wdt_exit(void) | ||
| 380 | { | ||
| 381 | misc_deregister(&wdt_miscdev); | ||
| 382 | unregister_reboot_notifier(&wdt_notifier); | ||
| 383 | release_region(wdt_io, 1); | ||
| 384 | } | ||
| 385 | |||
| 386 | module_init(wdt_init); | ||
| 387 | module_exit(wdt_exit); | ||
| 388 | |||
| 389 | MODULE_LICENSE("GPL"); | ||
| 390 | MODULE_AUTHOR("Flemming Frandsen <ff@nrvissing.net>"); | ||
| 391 | MODULE_DESCRIPTION("w83697ug/uf WDT driver"); | ||
| 392 | MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR); | ||
