diff options
| -rw-r--r-- | drivers/char/watchdog/Kconfig | 93 | ||||
| -rw-r--r-- | drivers/char/watchdog/Makefile | 7 | ||||
| -rw-r--r-- | drivers/char/watchdog/i6300esb.c | 527 | ||||
| -rw-r--r-- | drivers/char/watchdog/ibmasr.c | 405 | ||||
| -rw-r--r-- | drivers/char/watchdog/mv64x60_wdt.c | 252 | ||||
| -rw-r--r-- | drivers/char/watchdog/pcwd_pci.c | 44 | ||||
| -rw-r--r-- | drivers/char/watchdog/s3c2410_wdt.c | 2 | ||||
| -rw-r--r-- | drivers/char/watchdog/sbc8360.c | 414 | ||||
| -rw-r--r-- | drivers/char/watchdog/w83977f_wdt.c | 543 | ||||
| -rw-r--r-- | include/asm-ppc/mv64x60.h | 8 |
10 files changed, 2252 insertions, 43 deletions
diff --git a/drivers/char/watchdog/Kconfig b/drivers/char/watchdog/Kconfig index fa789ea36bbe..344001b45af9 100644 --- a/drivers/char/watchdog/Kconfig +++ b/drivers/char/watchdog/Kconfig | |||
| @@ -84,6 +84,17 @@ config 977_WATCHDOG | |||
| 84 | 84 | ||
| 85 | Not sure? It's safe to say N. | 85 | Not sure? It's safe to say N. |
| 86 | 86 | ||
| 87 | config IXP2000_WATCHDOG | ||
| 88 | tristate "IXP2000 Watchdog" | ||
| 89 | depends on WATCHDOG && ARCH_IXP2000 | ||
| 90 | help | ||
| 91 | Say Y here if to include support for the watchdog timer | ||
| 92 | in the Intel IXP2000(2400, 2800, 2850) network processors. | ||
| 93 | This driver can be built as a module by choosing M. The module | ||
| 94 | will be called ixp2000_wdt. | ||
| 95 | |||
| 96 | Say N if you are unsure. | ||
| 97 | |||
| 87 | config IXP4XX_WATCHDOG | 98 | config IXP4XX_WATCHDOG |
| 88 | tristate "IXP4xx Watchdog" | 99 | tristate "IXP4xx Watchdog" |
| 89 | depends on WATCHDOG && ARCH_IXP4XX | 100 | depends on WATCHDOG && ARCH_IXP4XX |
| @@ -100,17 +111,6 @@ config IXP4XX_WATCHDOG | |||
| 100 | 111 | ||
| 101 | Say N if you are unsure. | 112 | Say N if you are unsure. |
| 102 | 113 | ||
| 103 | config IXP2000_WATCHDOG | ||
| 104 | tristate "IXP2000 Watchdog" | ||
| 105 | depends on WATCHDOG && ARCH_IXP2000 | ||
| 106 | help | ||
| 107 | Say Y here if to include support for the watchdog timer | ||
| 108 | in the Intel IXP2000(2400, 2800, 2850) network processors. | ||
| 109 | This driver can be built as a module by choosing M. The module | ||
| 110 | will be called ixp2000_wdt. | ||
| 111 | |||
| 112 | Say N if you are unsure. | ||
| 113 | |||
| 114 | config S3C2410_WATCHDOG | 114 | config S3C2410_WATCHDOG |
| 115 | tristate "S3C2410 Watchdog" | 115 | tristate "S3C2410 Watchdog" |
| 116 | depends on WATCHDOG && ARCH_S3C2410 | 116 | depends on WATCHDOG && ARCH_S3C2410 |
| @@ -233,6 +233,16 @@ config IB700_WDT | |||
| 233 | 233 | ||
| 234 | Most people will say N. | 234 | Most people will say N. |
| 235 | 235 | ||
| 236 | config IBMASR | ||
| 237 | tristate "IBM Automatic Server Restart" | ||
| 238 | depends on WATCHDOG && X86 | ||
| 239 | help | ||
| 240 | This is the driver for the IBM Automatic Server Restart watchdog | ||
| 241 | timer builtin into some eServer xSeries machines. | ||
| 242 | |||
| 243 | To compile this driver as a module, choose M here: the | ||
| 244 | module will be called ibmasr. | ||
| 245 | |||
| 236 | config WAFER_WDT | 246 | config WAFER_WDT |
| 237 | tristate "ICP Wafer 5823 Single Board Computer Watchdog" | 247 | tristate "ICP Wafer 5823 Single Board Computer Watchdog" |
| 238 | depends on WATCHDOG && X86 | 248 | depends on WATCHDOG && X86 |
| @@ -243,6 +253,16 @@ config WAFER_WDT | |||
| 243 | To compile this driver as a module, choose M here: the | 253 | To compile this driver as a module, choose M here: the |
| 244 | module will be called wafer5823wdt. | 254 | module will be called wafer5823wdt. |
| 245 | 255 | ||
| 256 | config I6300ESB_WDT | ||
| 257 | tristate "Intel 6300ESB Timer/Watchdog" | ||
| 258 | depends on WATCHDOG && X86 && PCI | ||
| 259 | ---help--- | ||
| 260 | Hardware driver for the watchdog timer built into the Intel | ||
| 261 | 6300ESB controller hub. | ||
| 262 | |||
| 263 | To compile this driver as a module, choose M here: the | ||
| 264 | module will be called i6300esb. | ||
| 265 | |||
| 246 | config I8XX_TCO | 266 | config I8XX_TCO |
| 247 | tristate "Intel i8xx TCO Timer/Watchdog" | 267 | tristate "Intel i8xx TCO Timer/Watchdog" |
| 248 | depends on WATCHDOG && (X86 || IA64) && PCI | 268 | depends on WATCHDOG && (X86 || IA64) && PCI |
| @@ -298,6 +318,19 @@ config 60XX_WDT | |||
| 298 | You can compile this driver directly into the kernel, or use | 318 | You can compile this driver directly into the kernel, or use |
| 299 | it as a module. The module will be called sbc60xxwdt. | 319 | it as a module. The module will be called sbc60xxwdt. |
| 300 | 320 | ||
| 321 | config SBC8360_WDT | ||
| 322 | tristate "SBC8360 Watchdog Timer" | ||
| 323 | depends on WATCHDOG && X86 | ||
| 324 | ---help--- | ||
| 325 | |||
| 326 | This is the driver for the hardware watchdog on the SBC8360 Single | ||
| 327 | Board Computer produced by Axiomtek Co., Ltd. (www.axiomtek.com). | ||
| 328 | |||
| 329 | To compile this driver as a module, choose M here: the | ||
| 330 | module will be called sbc8360.ko. | ||
| 331 | |||
| 332 | Most people will say N. | ||
| 333 | |||
| 301 | config CPU5_WDT | 334 | config CPU5_WDT |
| 302 | tristate "SMA CPU5 Watchdog" | 335 | tristate "SMA CPU5 Watchdog" |
| 303 | depends on WATCHDOG && X86 | 336 | depends on WATCHDOG && X86 |
| @@ -336,6 +369,19 @@ config W83877F_WDT | |||
| 336 | 369 | ||
| 337 | Most people will say N. | 370 | Most people will say N. |
| 338 | 371 | ||
| 372 | config W83977F_WDT | ||
| 373 | tristate "W83977F (PCM-5335) Watchdog Timer" | ||
| 374 | depends on WATCHDOG && X86 | ||
| 375 | ---help--- | ||
| 376 | This is the driver for the hardware watchdog on the W83977F I/O chip | ||
| 377 | as used in AAEON's PCM-5335 SBC (and likely others). This | ||
| 378 | watchdog simply watches your kernel to make sure it doesn't freeze, | ||
| 379 | and if it does, it reboots your computer after a certain amount of | ||
| 380 | time. | ||
| 381 | |||
| 382 | To compile this driver as a module, choose M here: the | ||
| 383 | module will be called w83977f_wdt. | ||
| 384 | |||
| 339 | config MACHZ_WDT | 385 | config MACHZ_WDT |
| 340 | tristate "ZF MachZ Watchdog" | 386 | tristate "ZF MachZ Watchdog" |
| 341 | depends on WATCHDOG && X86 | 387 | depends on WATCHDOG && X86 |
| @@ -355,6 +401,10 @@ config 8xx_WDT | |||
| 355 | tristate "MPC8xx Watchdog Timer" | 401 | tristate "MPC8xx Watchdog Timer" |
| 356 | depends on WATCHDOG && 8xx | 402 | depends on WATCHDOG && 8xx |
| 357 | 403 | ||
| 404 | config MV64X60_WDT | ||
| 405 | tristate "MV64X60 (Marvell Discovery) Watchdog Timer" | ||
| 406 | depends on WATCHDOG && MV64X60 | ||
| 407 | |||
| 358 | config BOOKE_WDT | 408 | config BOOKE_WDT |
| 359 | tristate "PowerPC Book-E Watchdog Timer" | 409 | tristate "PowerPC Book-E Watchdog Timer" |
| 360 | depends on WATCHDOG && (BOOKE || 4xx) | 410 | depends on WATCHDOG && (BOOKE || 4xx) |
| @@ -362,6 +412,17 @@ config BOOKE_WDT | |||
| 362 | Please see Documentation/watchdog/watchdog-api.txt for | 412 | Please see Documentation/watchdog/watchdog-api.txt for |
| 363 | more information. | 413 | more information. |
| 364 | 414 | ||
| 415 | # PPC64 Architecture | ||
| 416 | |||
| 417 | config WATCHDOG_RTAS | ||
| 418 | tristate "RTAS watchdog" | ||
| 419 | depends on WATCHDOG && PPC_RTAS | ||
| 420 | help | ||
| 421 | This driver adds watchdog support for the RTAS watchdog. | ||
| 422 | |||
| 423 | To compile this driver as a module, choose M here. The module | ||
| 424 | will be called wdrtas. | ||
| 425 | |||
| 365 | # MIPS Architecture | 426 | # MIPS Architecture |
| 366 | 427 | ||
| 367 | config INDYDOG | 428 | config INDYDOG |
| @@ -430,16 +491,6 @@ config WATCHDOG_RIO | |||
| 430 | machines. The watchdog timeout period is normally one minute but | 491 | machines. The watchdog timeout period is normally one minute but |
| 431 | can be changed with a boot-time parameter. | 492 | can be changed with a boot-time parameter. |
| 432 | 493 | ||
| 433 | # ppc64 RTAS watchdog | ||
| 434 | config WATCHDOG_RTAS | ||
| 435 | tristate "RTAS watchdog" | ||
| 436 | depends on WATCHDOG && PPC_RTAS | ||
| 437 | help | ||
| 438 | This driver adds watchdog support for the RTAS watchdog. | ||
| 439 | |||
| 440 | To compile this driver as a module, choose M here. The module | ||
| 441 | will be called wdrtas. | ||
| 442 | |||
| 443 | # | 494 | # |
| 444 | # ISA-based Watchdog Cards | 495 | # ISA-based Watchdog Cards |
| 445 | # | 496 | # |
diff --git a/drivers/char/watchdog/Makefile b/drivers/char/watchdog/Makefile index bc6f5fe88c8c..cfd0a3987710 100644 --- a/drivers/char/watchdog/Makefile +++ b/drivers/char/watchdog/Makefile | |||
| @@ -39,22 +39,27 @@ obj-$(CONFIG_ALIM7101_WDT) += alim7101_wdt.o | |||
| 39 | obj-$(CONFIG_SC520_WDT) += sc520_wdt.o | 39 | obj-$(CONFIG_SC520_WDT) += sc520_wdt.o |
| 40 | obj-$(CONFIG_EUROTECH_WDT) += eurotechwdt.o | 40 | obj-$(CONFIG_EUROTECH_WDT) += eurotechwdt.o |
| 41 | obj-$(CONFIG_IB700_WDT) += ib700wdt.o | 41 | obj-$(CONFIG_IB700_WDT) += ib700wdt.o |
| 42 | obj-$(CONFIG_IBMASR) += ibmasr.o | ||
| 42 | obj-$(CONFIG_WAFER_WDT) += wafer5823wdt.o | 43 | obj-$(CONFIG_WAFER_WDT) += wafer5823wdt.o |
| 44 | obj-$(CONFIG_I6300ESB_WDT) += i6300esb.o | ||
| 43 | obj-$(CONFIG_I8XX_TCO) += i8xx_tco.o | 45 | obj-$(CONFIG_I8XX_TCO) += i8xx_tco.o |
| 44 | obj-$(CONFIG_SC1200_WDT) += sc1200wdt.o | 46 | obj-$(CONFIG_SC1200_WDT) += sc1200wdt.o |
| 45 | obj-$(CONFIG_SCx200_WDT) += scx200_wdt.o | 47 | obj-$(CONFIG_SCx200_WDT) += scx200_wdt.o |
| 46 | obj-$(CONFIG_60XX_WDT) += sbc60xxwdt.o | 48 | obj-$(CONFIG_60XX_WDT) += sbc60xxwdt.o |
| 49 | obj-$(CONFIG_SBC8360_WDT) += sbc8360.o | ||
| 47 | obj-$(CONFIG_CPU5_WDT) += cpu5wdt.o | 50 | obj-$(CONFIG_CPU5_WDT) += cpu5wdt.o |
| 48 | obj-$(CONFIG_W83627HF_WDT) += w83627hf_wdt.o | 51 | obj-$(CONFIG_W83627HF_WDT) += w83627hf_wdt.o |
| 49 | obj-$(CONFIG_W83877F_WDT) += w83877f_wdt.o | 52 | obj-$(CONFIG_W83877F_WDT) += w83877f_wdt.o |
| 53 | obj-$(CONFIG_W83977F_WDT) += w83977f_wdt.o | ||
| 50 | obj-$(CONFIG_MACHZ_WDT) += machzwd.o | 54 | obj-$(CONFIG_MACHZ_WDT) += machzwd.o |
| 51 | 55 | ||
| 52 | # PowerPC Architecture | 56 | # PowerPC Architecture |
| 53 | obj-$(CONFIG_8xx_WDT) += mpc8xx_wdt.o | 57 | obj-$(CONFIG_8xx_WDT) += mpc8xx_wdt.o |
| 58 | obj-$(CONFIG_MV64X60_WDT) += mv64x60_wdt.o | ||
| 59 | obj-$(CONFIG_BOOKE_WDT) += booke_wdt.o | ||
| 54 | 60 | ||
| 55 | # PPC64 Architecture | 61 | # PPC64 Architecture |
| 56 | obj-$(CONFIG_WATCHDOG_RTAS) += wdrtas.o | 62 | obj-$(CONFIG_WATCHDOG_RTAS) += wdrtas.o |
| 57 | obj-$(CONFIG_BOOKE_WDT) += booke_wdt.o | ||
| 58 | 63 | ||
| 59 | # MIPS Architecture | 64 | # MIPS Architecture |
| 60 | obj-$(CONFIG_INDYDOG) += indydog.o | 65 | obj-$(CONFIG_INDYDOG) += indydog.o |
diff --git a/drivers/char/watchdog/i6300esb.c b/drivers/char/watchdog/i6300esb.c new file mode 100644 index 000000000000..93785f13242e --- /dev/null +++ b/drivers/char/watchdog/i6300esb.c | |||
| @@ -0,0 +1,527 @@ | |||
| 1 | /* | ||
| 2 | * i6300esb: Watchdog timer driver for Intel 6300ESB chipset | ||
| 3 | * | ||
| 4 | * (c) Copyright 2004 Google Inc. | ||
| 5 | * (c) Copyright 2005 David Härdeman <david@2gen.com> | ||
| 6 | * | ||
| 7 | * This program is free software; you can redistribute it and/or | ||
| 8 | * modify it under the terms of the GNU General Public License | ||
| 9 | * as published by the Free Software Foundation; either version | ||
| 10 | * 2 of the License, or (at your option) any later version. | ||
| 11 | * | ||
| 12 | * based on i810-tco.c which is in turn based on softdog.c | ||
| 13 | * | ||
| 14 | * The timer is implemented in the following I/O controller hubs: | ||
| 15 | * (See the intel documentation on http://developer.intel.com.) | ||
| 16 | * 6300ESB chip : document number 300641-003 | ||
| 17 | * | ||
| 18 | * 2004YYZZ Ross Biro | ||
| 19 | * Initial version 0.01 | ||
| 20 | * 2004YYZZ Ross Biro | ||
| 21 | * Version 0.02 | ||
| 22 | * 20050210 David Härdeman <david@2gen.com> | ||
| 23 | * Ported driver to kernel 2.6 | ||
| 24 | */ | ||
| 25 | |||
| 26 | /* | ||
| 27 | * Includes, defines, variables, module parameters, ... | ||
| 28 | */ | ||
| 29 | |||
| 30 | #include <linux/module.h> | ||
| 31 | #include <linux/types.h> | ||
| 32 | #include <linux/kernel.h> | ||
| 33 | #include <linux/fs.h> | ||
| 34 | #include <linux/mm.h> | ||
| 35 | #include <linux/miscdevice.h> | ||
| 36 | #include <linux/watchdog.h> | ||
| 37 | #include <linux/reboot.h> | ||
| 38 | #include <linux/init.h> | ||
| 39 | #include <linux/pci.h> | ||
| 40 | #include <linux/ioport.h> | ||
| 41 | |||
| 42 | #include <asm/uaccess.h> | ||
| 43 | #include <asm/io.h> | ||
| 44 | |||
| 45 | /* Module and version information */ | ||
| 46 | #define ESB_VERSION "0.03" | ||
| 47 | #define ESB_MODULE_NAME "i6300ESB timer" | ||
| 48 | #define ESB_DRIVER_NAME ESB_MODULE_NAME ", v" ESB_VERSION | ||
| 49 | #define PFX ESB_MODULE_NAME ": " | ||
| 50 | |||
| 51 | /* PCI configuration registers */ | ||
| 52 | #define ESB_CONFIG_REG 0x60 /* Config register */ | ||
| 53 | #define ESB_LOCK_REG 0x68 /* WDT lock register */ | ||
| 54 | |||
| 55 | /* Memory mapped registers */ | ||
| 56 | #define ESB_TIMER1_REG BASEADDR + 0x00 /* Timer1 value after each reset */ | ||
| 57 | #define ESB_TIMER2_REG BASEADDR + 0x04 /* Timer2 value after each reset */ | ||
| 58 | #define ESB_GINTSR_REG BASEADDR + 0x08 /* General Interrupt Status Register */ | ||
| 59 | #define ESB_RELOAD_REG BASEADDR + 0x0c /* Reload register */ | ||
| 60 | |||
| 61 | /* Lock register bits */ | ||
| 62 | #define ESB_WDT_FUNC ( 0x01 << 2 ) /* Watchdog functionality */ | ||
| 63 | #define ESB_WDT_ENABLE ( 0x01 << 1 ) /* Enable WDT */ | ||
| 64 | #define ESB_WDT_LOCK ( 0x01 << 0 ) /* Lock (nowayout) */ | ||
| 65 | |||
| 66 | /* Config register bits */ | ||
| 67 | #define ESB_WDT_REBOOT ( 0x01 << 5 ) /* Enable reboot on timeout */ | ||
| 68 | #define ESB_WDT_FREQ ( 0x01 << 2 ) /* Decrement frequency */ | ||
| 69 | #define ESB_WDT_INTTYPE ( 0x11 << 0 ) /* Interrupt type on timer1 timeout */ | ||
| 70 | |||
| 71 | /* Reload register bits */ | ||
| 72 | #define ESB_WDT_RELOAD ( 0x01 << 8 ) /* prevent timeout */ | ||
| 73 | |||
| 74 | /* Magic constants */ | ||
| 75 | #define ESB_UNLOCK1 0x80 /* Step 1 to unlock reset registers */ | ||
| 76 | #define ESB_UNLOCK2 0x86 /* Step 2 to unlock reset registers */ | ||
| 77 | |||
| 78 | /* internal variables */ | ||
| 79 | static void __iomem *BASEADDR; | ||
| 80 | static spinlock_t esb_lock; /* Guards the hardware */ | ||
| 81 | static unsigned long timer_alive; | ||
| 82 | static struct pci_dev *esb_pci; | ||
| 83 | static unsigned short triggered; /* The status of the watchdog upon boot */ | ||
| 84 | static char esb_expect_close; | ||
| 85 | |||
| 86 | /* module parameters */ | ||
| 87 | #define WATCHDOG_HEARTBEAT 30 /* 30 sec default heartbeat (1<heartbeat<2*1023) */ | ||
| 88 | static int heartbeat = WATCHDOG_HEARTBEAT; /* in seconds */ | ||
| 89 | module_param(heartbeat, int, 0); | ||
| 90 | MODULE_PARM_DESC(heartbeat, "Watchdog heartbeat in seconds. (1<heartbeat<2046, default=" __MODULE_STRING(WATCHDOG_HEARTBEAT) ")"); | ||
| 91 | |||
| 92 | static int nowayout = WATCHDOG_NOWAYOUT; | ||
| 93 | module_param(nowayout, int, 0); | ||
| 94 | MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default=CONFIG_WATCHDOG_NOWAYOUT)"); | ||
| 95 | |||
| 96 | /* | ||
| 97 | * Some i6300ESB specific functions | ||
| 98 | */ | ||
| 99 | |||
| 100 | /* | ||
| 101 | * Prepare for reloading the timer by unlocking the proper registers. | ||
| 102 | * This is performed by first writing 0x80 followed by 0x86 to the | ||
| 103 | * reload register. After this the appropriate registers can be written | ||
| 104 | * to once before they need to be unlocked again. | ||
| 105 | */ | ||
| 106 | static inline void esb_unlock_registers(void) { | ||
| 107 | writeb(ESB_UNLOCK1, ESB_RELOAD_REG); | ||
| 108 | writeb(ESB_UNLOCK2, ESB_RELOAD_REG); | ||
| 109 | } | ||
| 110 | |||
| 111 | static void esb_timer_start(void) | ||
| 112 | { | ||
| 113 | u8 val; | ||
| 114 | |||
| 115 | /* Enable or Enable + Lock? */ | ||
| 116 | val = 0x02 | (nowayout ? 0x01 : 0x00); | ||
| 117 | |||
| 118 | pci_write_config_byte(esb_pci, ESB_LOCK_REG, val); | ||
| 119 | } | ||
| 120 | |||
| 121 | static int esb_timer_stop(void) | ||
| 122 | { | ||
| 123 | u8 val; | ||
| 124 | |||
| 125 | spin_lock(&esb_lock); | ||
| 126 | /* First, reset timers as suggested by the docs */ | ||
| 127 | esb_unlock_registers(); | ||
| 128 | writew(ESB_WDT_RELOAD, ESB_RELOAD_REG); | ||
| 129 | /* Then disable the WDT */ | ||
| 130 | pci_write_config_byte(esb_pci, ESB_LOCK_REG, 0x0); | ||
| 131 | pci_read_config_byte(esb_pci, ESB_LOCK_REG, &val); | ||
| 132 | spin_unlock(&esb_lock); | ||
| 133 | |||
| 134 | /* Returns 0 if the timer was disabled, non-zero otherwise */ | ||
| 135 | return (val & 0x01); | ||
| 136 | } | ||
| 137 | |||
| 138 | static void esb_timer_keepalive(void) | ||
| 139 | { | ||
| 140 | spin_lock(&esb_lock); | ||
| 141 | esb_unlock_registers(); | ||
| 142 | writew(ESB_WDT_RELOAD, ESB_RELOAD_REG); | ||
| 143 | /* FIXME: Do we need to flush anything here? */ | ||
| 144 | spin_unlock(&esb_lock); | ||
| 145 | } | ||
| 146 | |||
| 147 | static int esb_timer_set_heartbeat(int time) | ||
| 148 | { | ||
| 149 | u32 val; | ||
| 150 | |||
| 151 | if (time < 0x1 || time > (2 * 0x03ff)) | ||
| 152 | return -EINVAL; | ||
| 153 | |||
| 154 | spin_lock(&esb_lock); | ||
| 155 | |||
| 156 | /* We shift by 9, so if we are passed a value of 1 sec, | ||
| 157 | * val will be 1 << 9 = 512, then write that to two | ||
| 158 | * timers => 2 * 512 = 1024 (which is decremented at 1KHz) | ||
| 159 | */ | ||
| 160 | val = time << 9; | ||
| 161 | |||
| 162 | /* Write timer 1 */ | ||
| 163 | esb_unlock_registers(); | ||
| 164 | writel(val, ESB_TIMER1_REG); | ||
| 165 | |||
| 166 | /* Write timer 2 */ | ||
| 167 | esb_unlock_registers(); | ||
| 168 | writel(val, ESB_TIMER2_REG); | ||
| 169 | |||
| 170 | /* Reload */ | ||
| 171 | esb_unlock_registers(); | ||
| 172 | writew(ESB_WDT_RELOAD, ESB_RELOAD_REG); | ||
| 173 | |||
| 174 | /* FIXME: Do we need to flush everything out? */ | ||
| 175 | |||
| 176 | /* Done */ | ||
| 177 | heartbeat = time; | ||
| 178 | spin_unlock(&esb_lock); | ||
| 179 | return 0; | ||
| 180 | } | ||
| 181 | |||
| 182 | static int esb_timer_read (void) | ||
| 183 | { | ||
| 184 | u32 count; | ||
| 185 | |||
| 186 | /* This isn't documented, and doesn't take into | ||
| 187 | * acount which stage is running, but it looks | ||
| 188 | * like a 20 bit count down, so we might as well report it. | ||
| 189 | */ | ||
| 190 | pci_read_config_dword(esb_pci, 0x64, &count); | ||
| 191 | return (int)count; | ||
| 192 | } | ||
| 193 | |||
| 194 | /* | ||
| 195 | * /dev/watchdog handling | ||
| 196 | */ | ||
| 197 | |||
| 198 | static int esb_open (struct inode *inode, struct file *file) | ||
| 199 | { | ||
| 200 | /* /dev/watchdog can only be opened once */ | ||
| 201 | if (test_and_set_bit(0, &timer_alive)) | ||
| 202 | return -EBUSY; | ||
| 203 | |||
| 204 | /* Reload and activate timer */ | ||
| 205 | esb_timer_keepalive (); | ||
| 206 | esb_timer_start (); | ||
| 207 | |||
| 208 | return nonseekable_open(inode, file); | ||
| 209 | } | ||
| 210 | |||
| 211 | static int esb_release (struct inode *inode, struct file *file) | ||
| 212 | { | ||
| 213 | /* Shut off the timer. */ | ||
| 214 | if (esb_expect_close == 42) { | ||
| 215 | esb_timer_stop (); | ||
| 216 | } else { | ||
| 217 | printk(KERN_CRIT PFX "Unexpected close, not stopping watchdog!\n"); | ||
| 218 | esb_timer_keepalive (); | ||
| 219 | } | ||
| 220 | clear_bit(0, &timer_alive); | ||
| 221 | esb_expect_close = 0; | ||
| 222 | return 0; | ||
| 223 | } | ||
| 224 | |||
| 225 | static ssize_t esb_write (struct file *file, const char __user *data, | ||
| 226 | size_t len, loff_t * ppos) | ||
| 227 | { | ||
| 228 | /* See if we got the magic character 'V' and reload the timer */ | ||
| 229 | if (len) { | ||
| 230 | if (!nowayout) { | ||
| 231 | size_t i; | ||
| 232 | |||
| 233 | /* note: just in case someone wrote the magic character | ||
| 234 | * five months ago... */ | ||
| 235 | esb_expect_close = 0; | ||
| 236 | |||
| 237 | /* scan to see whether or not we got the magic character */ | ||
| 238 | for (i = 0; i != len; i++) { | ||
| 239 | char c; | ||
| 240 | if(get_user(c, data+i)) | ||
| 241 | return -EFAULT; | ||
| 242 | if (c == 'V') | ||
| 243 | esb_expect_close = 42; | ||
| 244 | } | ||
| 245 | } | ||
| 246 | |||
| 247 | /* someone wrote to us, we should reload the timer */ | ||
| 248 | esb_timer_keepalive (); | ||
| 249 | } | ||
| 250 | return len; | ||
| 251 | } | ||
| 252 | |||
| 253 | static int esb_ioctl (struct inode *inode, struct file *file, | ||
| 254 | unsigned int cmd, unsigned long arg) | ||
| 255 | { | ||
| 256 | int new_options, retval = -EINVAL; | ||
| 257 | int new_heartbeat; | ||
| 258 | void __user *argp = (void __user *)arg; | ||
| 259 | int __user *p = argp; | ||
| 260 | static struct watchdog_info ident = { | ||
| 261 | .options = WDIOF_SETTIMEOUT | | ||
| 262 | WDIOF_KEEPALIVEPING | | ||
| 263 | WDIOF_MAGICCLOSE, | ||
| 264 | .firmware_version = 0, | ||
| 265 | .identity = ESB_MODULE_NAME, | ||
| 266 | }; | ||
| 267 | |||
| 268 | switch (cmd) { | ||
| 269 | case WDIOC_GETSUPPORT: | ||
| 270 | return copy_to_user(argp, &ident, | ||
| 271 | sizeof (ident)) ? -EFAULT : 0; | ||
| 272 | |||
| 273 | case WDIOC_GETSTATUS: | ||
| 274 | return put_user (esb_timer_read(), p); | ||
| 275 | |||
| 276 | case WDIOC_GETBOOTSTATUS: | ||
| 277 | return put_user (triggered, p); | ||
| 278 | |||
| 279 | case WDIOC_KEEPALIVE: | ||
| 280 | esb_timer_keepalive (); | ||
| 281 | return 0; | ||
| 282 | |||
| 283 | case WDIOC_SETOPTIONS: | ||
| 284 | { | ||
| 285 | if (get_user (new_options, p)) | ||
| 286 | return -EFAULT; | ||
| 287 | |||
| 288 | if (new_options & WDIOS_DISABLECARD) { | ||
| 289 | esb_timer_stop (); | ||
| 290 | retval = 0; | ||
| 291 | } | ||
| 292 | |||
| 293 | if (new_options & WDIOS_ENABLECARD) { | ||
| 294 | esb_timer_keepalive (); | ||
| 295 | esb_timer_start (); | ||
| 296 | retval = 0; | ||
| 297 | } | ||
| 298 | |||
| 299 | return retval; | ||
| 300 | } | ||
| 301 | |||
| 302 | case WDIOC_SETTIMEOUT: | ||
| 303 | { | ||
| 304 | if (get_user(new_heartbeat, p)) | ||
| 305 | return -EFAULT; | ||
| 306 | |||
| 307 | if (esb_timer_set_heartbeat(new_heartbeat)) | ||
| 308 | return -EINVAL; | ||
| 309 | |||
| 310 | esb_timer_keepalive (); | ||
| 311 | /* Fall */ | ||
| 312 | } | ||
| 313 | |||
| 314 | case WDIOC_GETTIMEOUT: | ||
| 315 | return put_user(heartbeat, p); | ||
| 316 | |||
| 317 | default: | ||
| 318 | return -ENOIOCTLCMD; | ||
| 319 | } | ||
| 320 | } | ||
| 321 | |||
| 322 | /* | ||
| 323 | * Notify system | ||
| 324 | */ | ||
| 325 | |||
| 326 | static int esb_notify_sys (struct notifier_block *this, unsigned long code, void *unused) | ||
| 327 | { | ||
| 328 | if (code==SYS_DOWN || code==SYS_HALT) { | ||
| 329 | /* Turn the WDT off */ | ||
| 330 | esb_timer_stop (); | ||
| 331 | } | ||
| 332 | |||
| 333 | return NOTIFY_DONE; | ||
| 334 | } | ||
| 335 | |||
| 336 | /* | ||
| 337 | * Kernel Interfaces | ||
| 338 | */ | ||
| 339 | |||
| 340 | static struct file_operations esb_fops = { | ||
| 341 | .owner = THIS_MODULE, | ||
| 342 | .llseek = no_llseek, | ||
| 343 | .write = esb_write, | ||
| 344 | .ioctl = esb_ioctl, | ||
| 345 | .open = esb_open, | ||
| 346 | .release = esb_release, | ||
| 347 | }; | ||
| 348 | |||
| 349 | static struct miscdevice esb_miscdev = { | ||
| 350 | .minor = WATCHDOG_MINOR, | ||
| 351 | .name = "watchdog", | ||
| 352 | .fops = &esb_fops, | ||
| 353 | }; | ||
| 354 | |||
| 355 | static struct notifier_block esb_notifier = { | ||
| 356 | .notifier_call = esb_notify_sys, | ||
| 357 | }; | ||
| 358 | |||
| 359 | /* | ||
| 360 | * Data for PCI driver interface | ||
| 361 | * | ||
| 362 | * This data only exists for exporting the supported | ||
| 363 | * PCI ids via MODULE_DEVICE_TABLE. We do not actually | ||
| 364 | * register a pci_driver, because someone else might one day | ||
| 365 | * want to register another driver on the same PCI id. | ||
| 366 | */ | ||
| 367 | static struct pci_device_id esb_pci_tbl[] = { | ||
| 368 | { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_ESB_9), }, | ||
| 369 | { 0, }, /* End of list */ | ||
| 370 | }; | ||
| 371 | MODULE_DEVICE_TABLE (pci, esb_pci_tbl); | ||
| 372 | |||
| 373 | /* | ||
| 374 | * Init & exit routines | ||
| 375 | */ | ||
| 376 | |||
| 377 | static unsigned char __init esb_getdevice (void) | ||
| 378 | { | ||
| 379 | u8 val1; | ||
| 380 | unsigned short val2; | ||
| 381 | |||
| 382 | struct pci_dev *dev = NULL; | ||
| 383 | /* | ||
| 384 | * Find the PCI device | ||
| 385 | */ | ||
| 386 | |||
| 387 | for_each_pci_dev(dev) { | ||
| 388 | if (pci_match_id(esb_pci_tbl, dev)) { | ||
| 389 | esb_pci = dev; | ||
| 390 | break; | ||
| 391 | } | ||
| 392 | } | ||
| 393 | |||
| 394 | if (esb_pci) { | ||
| 395 | if (pci_enable_device(esb_pci)) { | ||
| 396 | printk (KERN_ERR PFX "failed to enable device\n"); | ||
| 397 | goto err_devput; | ||
| 398 | } | ||
| 399 | |||
| 400 | if (pci_request_region(esb_pci, 0, ESB_MODULE_NAME)) { | ||
| 401 | printk (KERN_ERR PFX "failed to request region\n"); | ||
| 402 | goto err_disable; | ||
| 403 | } | ||
| 404 | |||
| 405 | BASEADDR = ioremap(pci_resource_start(esb_pci, 0), | ||
| 406 | pci_resource_len(esb_pci, 0)); | ||
| 407 | if (BASEADDR == NULL) { | ||
| 408 | /* Something's wrong here, BASEADDR has to be set */ | ||
| 409 | printk (KERN_ERR PFX "failed to get BASEADDR\n"); | ||
| 410 | goto err_release; | ||
| 411 | } | ||
| 412 | |||
| 413 | /* | ||
| 414 | * The watchdog has two timers, it can be setup so that the | ||
| 415 | * expiry of timer1 results in an interrupt and the expiry of | ||
| 416 | * timer2 results in a reboot. We set it to not generate | ||
| 417 | * any interrupts as there is not much we can do with it | ||
| 418 | * right now. | ||
| 419 | * | ||
| 420 | * We also enable reboots and set the timer frequency to | ||
| 421 | * the PCI clock divided by 2^15 (approx 1KHz). | ||
| 422 | */ | ||
| 423 | pci_write_config_word(esb_pci, ESB_CONFIG_REG, 0x0003); | ||
| 424 | |||
| 425 | /* Check that the WDT isn't already locked */ | ||
| 426 | pci_read_config_byte(esb_pci, ESB_LOCK_REG, &val1); | ||
| 427 | if (val1 & ESB_WDT_LOCK) | ||
| 428 | printk (KERN_WARNING PFX "nowayout already set\n"); | ||
| 429 | |||
| 430 | /* Set the timer to watchdog mode and disable it for now */ | ||
| 431 | pci_write_config_byte(esb_pci, ESB_LOCK_REG, 0x00); | ||
| 432 | |||
| 433 | /* Check if the watchdog was previously triggered */ | ||
| 434 | esb_unlock_registers(); | ||
| 435 | val2 = readw(ESB_RELOAD_REG); | ||
| 436 | triggered = (val2 & (0x01 << 9) >> 9); | ||
| 437 | |||
| 438 | /* Reset trigger flag and timers */ | ||
| 439 | esb_unlock_registers(); | ||
| 440 | writew((0x11 << 8), ESB_RELOAD_REG); | ||
| 441 | |||
| 442 | /* Done */ | ||
| 443 | return 1; | ||
| 444 | |||
| 445 | err_release: | ||
| 446 | pci_release_region(esb_pci, 0); | ||
| 447 | err_disable: | ||
| 448 | pci_disable_device(esb_pci); | ||
| 449 | err_devput: | ||
| 450 | pci_dev_put(esb_pci); | ||
| 451 | } | ||
| 452 | return 0; | ||
| 453 | } | ||
| 454 | |||
| 455 | static int __init watchdog_init (void) | ||
| 456 | { | ||
| 457 | int ret; | ||
| 458 | |||
| 459 | spin_lock_init(&esb_lock); | ||
| 460 | |||
| 461 | /* Check whether or not the hardware watchdog is there */ | ||
| 462 | if (!esb_getdevice () || esb_pci == NULL) | ||
| 463 | return -ENODEV; | ||
| 464 | |||
| 465 | /* Check that the heartbeat value is within it's range ; if not reset to the default */ | ||
| 466 | if (esb_timer_set_heartbeat (heartbeat)) { | ||
| 467 | esb_timer_set_heartbeat (WATCHDOG_HEARTBEAT); | ||
| 468 | printk(KERN_INFO PFX "heartbeat value must be 1<heartbeat<2046, using %d\n", | ||
| 469 | heartbeat); | ||
| 470 | } | ||
| 471 | |||
| 472 | ret = register_reboot_notifier(&esb_notifier); | ||
| 473 | if (ret != 0) { | ||
| 474 | printk(KERN_ERR PFX "cannot register reboot notifier (err=%d)\n", | ||
| 475 | ret); | ||
| 476 | goto err_unmap; | ||
| 477 | } | ||
| 478 | |||
| 479 | ret = misc_register(&esb_miscdev); | ||
| 480 | if (ret != 0) { | ||
| 481 | printk(KERN_ERR PFX "cannot register miscdev on minor=%d (err=%d)\n", | ||
| 482 | WATCHDOG_MINOR, ret); | ||
| 483 | goto err_notifier; | ||
| 484 | } | ||
| 485 | |||
| 486 | esb_timer_stop (); | ||
| 487 | |||
| 488 | printk (KERN_INFO PFX "initialized (0x%p). heartbeat=%d sec (nowayout=%d)\n", | ||
| 489 | BASEADDR, heartbeat, nowayout); | ||
| 490 | |||
| 491 | return 0; | ||
| 492 | |||
| 493 | err_notifier: | ||
| 494 | unregister_reboot_notifier(&esb_notifier); | ||
| 495 | err_unmap: | ||
| 496 | iounmap(BASEADDR); | ||
| 497 | /* err_release: */ | ||
| 498 | pci_release_region(esb_pci, 0); | ||
| 499 | /* err_disable: */ | ||
| 500 | pci_disable_device(esb_pci); | ||
| 501 | /* err_devput: */ | ||
| 502 | pci_dev_put(esb_pci); | ||
| 503 | return ret; | ||
| 504 | } | ||
| 505 | |||
| 506 | static void __exit watchdog_cleanup (void) | ||
| 507 | { | ||
| 508 | /* Stop the timer before we leave */ | ||
| 509 | if (!nowayout) | ||
| 510 | esb_timer_stop (); | ||
| 511 | |||
| 512 | /* Deregister */ | ||
| 513 | misc_deregister(&esb_miscdev); | ||
| 514 | unregister_reboot_notifier(&esb_notifier); | ||
| 515 | iounmap(BASEADDR); | ||
| 516 | pci_release_region(esb_pci, 0); | ||
| 517 | pci_disable_device(esb_pci); | ||
| 518 | pci_dev_put(esb_pci); | ||
| 519 | } | ||
| 520 | |||
| 521 | module_init(watchdog_init); | ||
| 522 | module_exit(watchdog_cleanup); | ||
| 523 | |||
| 524 | MODULE_AUTHOR("Ross Biro and David Härdeman"); | ||
| 525 | MODULE_DESCRIPTION("Watchdog driver for Intel 6300ESB chipsets"); | ||
| 526 | MODULE_LICENSE("GPL"); | ||
| 527 | MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR); | ||
diff --git a/drivers/char/watchdog/ibmasr.c b/drivers/char/watchdog/ibmasr.c new file mode 100644 index 000000000000..294c474ae485 --- /dev/null +++ b/drivers/char/watchdog/ibmasr.c | |||
| @@ -0,0 +1,405 @@ | |||
| 1 | /* | ||
| 2 | * IBM Automatic Server Restart driver. | ||
| 3 | * | ||
| 4 | * Copyright (c) 2005 Andrey Panin <pazke@donpac.ru> | ||
| 5 | * | ||
| 6 | * Based on driver written by Pete Reynolds. | ||
| 7 | * Copyright (c) IBM Corporation, 1998-2004. | ||
| 8 | * | ||
| 9 | * This software may be used and distributed according to the terms | ||
| 10 | * of the GNU Public License, incorporated herein by reference. | ||
| 11 | */ | ||
| 12 | |||
| 13 | #include <linux/config.h> | ||
| 14 | #include <linux/fs.h> | ||
| 15 | #include <linux/kernel.h> | ||
| 16 | #include <linux/slab.h> | ||
| 17 | #include <linux/module.h> | ||
| 18 | #include <linux/pci.h> | ||
| 19 | #include <linux/timer.h> | ||
| 20 | #include <linux/miscdevice.h> | ||
| 21 | #include <linux/watchdog.h> | ||
| 22 | #include <linux/dmi.h> | ||
| 23 | |||
| 24 | #include <asm/io.h> | ||
| 25 | #include <asm/uaccess.h> | ||
| 26 | |||
| 27 | |||
| 28 | enum { | ||
| 29 | ASMTYPE_UNKNOWN, | ||
| 30 | ASMTYPE_TOPAZ, | ||
| 31 | ASMTYPE_JASPER, | ||
| 32 | ASMTYPE_PEARL, | ||
| 33 | ASMTYPE_JUNIPER, | ||
| 34 | ASMTYPE_SPRUCE, | ||
| 35 | }; | ||
| 36 | |||
| 37 | #define PFX "ibmasr: " | ||
| 38 | |||
| 39 | #define TOPAZ_ASR_REG_OFFSET 4 | ||
| 40 | #define TOPAZ_ASR_TOGGLE 0x40 | ||
| 41 | #define TOPAZ_ASR_DISABLE 0x80 | ||
| 42 | |||
| 43 | /* PEARL ASR S/W REGISTER SUPERIO PORT ADDRESSES */ | ||
| 44 | #define PEARL_BASE 0xe04 | ||
| 45 | #define PEARL_WRITE 0xe06 | ||
| 46 | #define PEARL_READ 0xe07 | ||
| 47 | |||
| 48 | #define PEARL_ASR_DISABLE_MASK 0x80 /* bit 7: disable = 1, enable = 0 */ | ||
| 49 | #define PEARL_ASR_TOGGLE_MASK 0x40 /* bit 6: 0, then 1, then 0 */ | ||
| 50 | |||
| 51 | /* JASPER OFFSET FROM SIO BASE ADDR TO ASR S/W REGISTERS. */ | ||
| 52 | #define JASPER_ASR_REG_OFFSET 0x38 | ||
| 53 | |||
| 54 | #define JASPER_ASR_DISABLE_MASK 0x01 /* bit 0: disable = 1, enable = 0 */ | ||
| 55 | #define JASPER_ASR_TOGGLE_MASK 0x02 /* bit 1: 0, then 1, then 0 */ | ||
| 56 | |||
| 57 | #define JUNIPER_BASE_ADDRESS 0x54b /* Base address of Juniper ASR */ | ||
| 58 | #define JUNIPER_ASR_DISABLE_MASK 0x01 /* bit 0: disable = 1 enable = 0 */ | ||
| 59 | #define JUNIPER_ASR_TOGGLE_MASK 0x02 /* bit 1: 0, then 1, then 0 */ | ||
| 60 | |||
| 61 | #define SPRUCE_BASE_ADDRESS 0x118e /* Base address of Spruce ASR */ | ||
| 62 | #define SPRUCE_ASR_DISABLE_MASK 0x01 /* bit 1: disable = 1 enable = 0 */ | ||
| 63 | #define SPRUCE_ASR_TOGGLE_MASK 0x02 /* bit 0: 0, then 1, then 0 */ | ||
| 64 | |||
| 65 | |||
| 66 | static int nowayout = WATCHDOG_NOWAYOUT; | ||
| 67 | |||
| 68 | static unsigned long asr_is_open; | ||
| 69 | static char asr_expect_close; | ||
| 70 | |||
| 71 | static unsigned int asr_type, asr_base, asr_length; | ||
| 72 | static unsigned int asr_read_addr, asr_write_addr; | ||
| 73 | static unsigned char asr_toggle_mask, asr_disable_mask; | ||
| 74 | |||
| 75 | static void asr_toggle(void) | ||
| 76 | { | ||
| 77 | unsigned char reg = inb(asr_read_addr); | ||
| 78 | |||
| 79 | outb(reg & ~asr_toggle_mask, asr_write_addr); | ||
| 80 | reg = inb(asr_read_addr); | ||
| 81 | |||
| 82 | outb(reg | asr_toggle_mask, asr_write_addr); | ||
| 83 | reg = inb(asr_read_addr); | ||
| 84 | |||
| 85 | outb(reg & ~asr_toggle_mask, asr_write_addr); | ||
| 86 | reg = inb(asr_read_addr); | ||
| 87 | } | ||
| 88 | |||
| 89 | static void asr_enable(void) | ||
| 90 | { | ||
| 91 | unsigned char reg; | ||
| 92 | |||
| 93 | if (asr_type == ASMTYPE_TOPAZ) { | ||
| 94 | /* asr_write_addr == asr_read_addr */ | ||
| 95 | reg = inb(asr_read_addr); | ||
| 96 | outb(reg & ~(TOPAZ_ASR_TOGGLE | TOPAZ_ASR_DISABLE), | ||
| 97 | asr_read_addr); | ||
| 98 | } else { | ||
| 99 | /* | ||
| 100 | * First make sure the hardware timer is reset by toggling | ||
| 101 | * ASR hardware timer line. | ||
| 102 | */ | ||
| 103 | asr_toggle(); | ||
| 104 | |||
| 105 | reg = inb(asr_read_addr); | ||
| 106 | outb(reg & ~asr_disable_mask, asr_write_addr); | ||
| 107 | } | ||
| 108 | reg = inb(asr_read_addr); | ||
| 109 | } | ||
| 110 | |||
| 111 | static void asr_disable(void) | ||
| 112 | { | ||
| 113 | unsigned char reg = inb(asr_read_addr); | ||
| 114 | |||
| 115 | if (asr_type == ASMTYPE_TOPAZ) | ||
| 116 | /* asr_write_addr == asr_read_addr */ | ||
| 117 | outb(reg | TOPAZ_ASR_TOGGLE | TOPAZ_ASR_DISABLE, | ||
| 118 | asr_read_addr); | ||
| 119 | else { | ||
| 120 | outb(reg | asr_toggle_mask, asr_write_addr); | ||
| 121 | reg = inb(asr_read_addr); | ||
| 122 | |||
| 123 | outb(reg | asr_disable_mask, asr_write_addr); | ||
| 124 | } | ||
| 125 | reg = inb(asr_read_addr); | ||
| 126 | } | ||
| 127 | |||
| 128 | static int __init asr_get_base_address(void) | ||
| 129 | { | ||
| 130 | unsigned char low, high; | ||
| 131 | const char *type = ""; | ||
| 132 | |||
| 133 | asr_length = 1; | ||
| 134 | |||
| 135 | switch (asr_type) { | ||
| 136 | case ASMTYPE_TOPAZ: | ||
| 137 | /* SELECT SuperIO CHIP FOR QUERYING (WRITE 0x07 TO BOTH 0x2E and 0x2F) */ | ||
| 138 | outb(0x07, 0x2e); | ||
| 139 | outb(0x07, 0x2f); | ||
| 140 | |||
| 141 | /* SELECT AND READ THE HIGH-NIBBLE OF THE GPIO BASE ADDRESS */ | ||
| 142 | outb(0x60, 0x2e); | ||
| 143 | high = inb(0x2f); | ||
| 144 | |||
| 145 | /* SELECT AND READ THE LOW-NIBBLE OF THE GPIO BASE ADDRESS */ | ||
| 146 | outb(0x61, 0x2e); | ||
| 147 | low = inb(0x2f); | ||
| 148 | |||
| 149 | asr_base = (high << 16) | low; | ||
| 150 | asr_read_addr = asr_write_addr = | ||
| 151 | asr_base + TOPAZ_ASR_REG_OFFSET; | ||
| 152 | asr_length = 5; | ||
| 153 | |||
| 154 | break; | ||
| 155 | |||
| 156 | case ASMTYPE_JASPER: | ||
| 157 | type = "Jaspers "; | ||
| 158 | |||
| 159 | /* FIXME: need to use pci_config_lock here, but it's not exported */ | ||
| 160 | |||
| 161 | /* spin_lock_irqsave(&pci_config_lock, flags);*/ | ||
| 162 | |||
| 163 | /* Select the SuperIO chip in the PCI I/O port register */ | ||
| 164 | outl(0x8000f858, 0xcf8); | ||
| 165 | |||
| 166 | /* | ||
| 167 | * Read the base address for the SuperIO chip. | ||
| 168 | * Only the lower 16 bits are valid, but the address is word | ||
| 169 | * aligned so the last bit must be masked off. | ||
| 170 | */ | ||
| 171 | asr_base = inl(0xcfc) & 0xfffe; | ||
| 172 | |||
| 173 | /* spin_unlock_irqrestore(&pci_config_lock, flags);*/ | ||
| 174 | |||
| 175 | asr_read_addr = asr_write_addr = | ||
| 176 | asr_base + JASPER_ASR_REG_OFFSET; | ||
| 177 | asr_toggle_mask = JASPER_ASR_TOGGLE_MASK; | ||
| 178 | asr_disable_mask = JASPER_ASR_DISABLE_MASK; | ||
| 179 | asr_length = JASPER_ASR_REG_OFFSET + 1; | ||
| 180 | |||
| 181 | break; | ||
| 182 | |||
| 183 | case ASMTYPE_PEARL: | ||
| 184 | type = "Pearls "; | ||
| 185 | asr_base = PEARL_BASE; | ||
| 186 | asr_read_addr = PEARL_READ; | ||
| 187 | asr_write_addr = PEARL_WRITE; | ||
| 188 | asr_toggle_mask = PEARL_ASR_TOGGLE_MASK; | ||
| 189 | asr_disable_mask = PEARL_ASR_DISABLE_MASK; | ||
| 190 | asr_length = 4; | ||
| 191 | break; | ||
| 192 | |||
| 193 | case ASMTYPE_JUNIPER: | ||
| 194 | type = "Junipers "; | ||
| 195 | asr_base = JUNIPER_BASE_ADDRESS; | ||
| 196 | asr_read_addr = asr_write_addr = asr_base; | ||
| 197 | asr_toggle_mask = JUNIPER_ASR_TOGGLE_MASK; | ||
| 198 | asr_disable_mask = JUNIPER_ASR_DISABLE_MASK; | ||
| 199 | break; | ||
| 200 | |||
| 201 | case ASMTYPE_SPRUCE: | ||
| 202 | type = "Spruce's "; | ||
| 203 | asr_base = SPRUCE_BASE_ADDRESS; | ||
| 204 | asr_read_addr = asr_write_addr = asr_base; | ||
| 205 | asr_toggle_mask = SPRUCE_ASR_TOGGLE_MASK; | ||
| 206 | asr_disable_mask = SPRUCE_ASR_DISABLE_MASK; | ||
| 207 | break; | ||
| 208 | } | ||
| 209 | |||
| 210 | if (!request_region(asr_base, asr_length, "ibmasr")) { | ||
| 211 | printk(KERN_ERR PFX "address %#x already in use\n", | ||
| 212 | asr_base); | ||
| 213 | return -EBUSY; | ||
| 214 | } | ||
| 215 | |||
| 216 | printk(KERN_INFO PFX "found %sASR @ addr %#x\n", type, asr_base); | ||
| 217 | |||
| 218 | return 0; | ||
| 219 | } | ||
| 220 | |||
| 221 | |||
| 222 | static ssize_t asr_write(struct file *file, const char __user *buf, | ||
| 223 | size_t count, loff_t *ppos) | ||
| 224 | { | ||
| 225 | if (count) { | ||
| 226 | if (!nowayout) { | ||
| 227 | size_t i; | ||
| 228 | |||
| 229 | /* In case it was set long ago */ | ||
| 230 | asr_expect_close = 0; | ||
| 231 | |||
| 232 | for (i = 0; i != count; i++) { | ||
| 233 | char c; | ||
| 234 | if (get_user(c, buf + i)) | ||
| 235 | return -EFAULT; | ||
| 236 | if (c == 'V') | ||
| 237 | asr_expect_close = 42; | ||
| 238 | } | ||
| 239 | } | ||
| 240 | asr_toggle(); | ||
| 241 | } | ||
| 242 | return count; | ||
| 243 | } | ||
| 244 | |||
| 245 | static int asr_ioctl(struct inode *inode, struct file *file, | ||
| 246 | unsigned int cmd, unsigned long arg) | ||
| 247 | { | ||
| 248 | static const struct watchdog_info ident = { | ||
| 249 | .options = WDIOF_KEEPALIVEPING | | ||
| 250 | WDIOF_MAGICCLOSE, | ||
| 251 | .identity = "IBM ASR" | ||
| 252 | }; | ||
| 253 | void __user *argp = (void __user *)arg; | ||
| 254 | int __user *p = argp; | ||
| 255 | int heartbeat; | ||
| 256 | |||
| 257 | switch (cmd) { | ||
| 258 | case WDIOC_GETSUPPORT: | ||
| 259 | return copy_to_user(argp, &ident, sizeof(ident)) ? | ||
| 260 | -EFAULT : 0; | ||
| 261 | |||
| 262 | case WDIOC_GETSTATUS: | ||
| 263 | case WDIOC_GETBOOTSTATUS: | ||
| 264 | return put_user(0, p); | ||
| 265 | |||
| 266 | case WDIOC_KEEPALIVE: | ||
| 267 | asr_toggle(); | ||
| 268 | return 0; | ||
| 269 | |||
| 270 | /* | ||
| 271 | * The hardware has a fixed timeout value, so no WDIOC_SETTIMEOUT | ||
| 272 | * and WDIOC_GETTIMEOUT always returns 256. | ||
| 273 | */ | ||
| 274 | case WDIOC_GETTIMEOUT: | ||
| 275 | heartbeat = 256; | ||
| 276 | return put_user(heartbeat, p); | ||
| 277 | |||
| 278 | case WDIOC_SETOPTIONS: { | ||
| 279 | int new_options, retval = -EINVAL; | ||
| 280 | |||
| 281 | if (get_user(new_options, p)) | ||
| 282 | return -EFAULT; | ||
| 283 | |||
| 284 | if (new_options & WDIOS_DISABLECARD) { | ||
| 285 | asr_disable(); | ||
| 286 | retval = 0; | ||
| 287 | } | ||
| 288 | |||
| 289 | if (new_options & WDIOS_ENABLECARD) { | ||
| 290 | asr_enable(); | ||
| 291 | asr_toggle(); | ||
| 292 | retval = 0; | ||
| 293 | } | ||
| 294 | |||
| 295 | return retval; | ||
| 296 | } | ||
| 297 | } | ||
| 298 | |||
| 299 | return -ENOIOCTLCMD; | ||
| 300 | } | ||
| 301 | |||
| 302 | static int asr_open(struct inode *inode, struct file *file) | ||
| 303 | { | ||
| 304 | if(test_and_set_bit(0, &asr_is_open)) | ||
| 305 | return -EBUSY; | ||
| 306 | |||
| 307 | asr_toggle(); | ||
| 308 | asr_enable(); | ||
| 309 | |||
| 310 | return nonseekable_open(inode, file); | ||
| 311 | } | ||
| 312 | |||
| 313 | static int asr_release(struct inode *inode, struct file *file) | ||
| 314 | { | ||
| 315 | if (asr_expect_close == 42) | ||
| 316 | asr_disable(); | ||
| 317 | else { | ||
| 318 | printk(KERN_CRIT PFX "unexpected close, not stopping watchdog!\n"); | ||
| 319 | asr_toggle(); | ||
| 320 | } | ||
| 321 | clear_bit(0, &asr_is_open); | ||
| 322 | asr_expect_close = 0; | ||
| 323 | return 0; | ||
| 324 | } | ||
| 325 | |||
| 326 | static struct file_operations asr_fops = { | ||
| 327 | .owner = THIS_MODULE, | ||
| 328 | .llseek = no_llseek, | ||
| 329 | .write = asr_write, | ||
| 330 | .ioctl = asr_ioctl, | ||
| 331 | .open = asr_open, | ||
| 332 | .release = asr_release, | ||
| 333 | }; | ||
| 334 | |||
| 335 | static struct miscdevice asr_miscdev = { | ||
| 336 | .minor = WATCHDOG_MINOR, | ||
| 337 | .name = "watchdog", | ||
| 338 | .fops = &asr_fops, | ||
| 339 | }; | ||
| 340 | |||
| 341 | |||
| 342 | struct ibmasr_id { | ||
| 343 | const char *desc; | ||
| 344 | int type; | ||
| 345 | }; | ||
| 346 | |||
| 347 | static struct ibmasr_id __initdata ibmasr_id_table[] = { | ||
| 348 | { "IBM Automatic Server Restart - eserver xSeries 220", ASMTYPE_TOPAZ }, | ||
| 349 | { "IBM Automatic Server Restart - Machine Type 8673", ASMTYPE_PEARL }, | ||
| 350 | { "IBM Automatic Server Restart - Machine Type 8480", ASMTYPE_JASPER }, | ||
| 351 | { "IBM Automatic Server Restart - Machine Type 8482", ASMTYPE_JUNIPER }, | ||
| 352 | { "IBM Automatic Server Restart - Machine Type 8648", ASMTYPE_SPRUCE }, | ||
| 353 | { NULL } | ||
| 354 | }; | ||
| 355 | |||
| 356 | static int __init ibmasr_init(void) | ||
| 357 | { | ||
| 358 | struct ibmasr_id *id; | ||
| 359 | int rc; | ||
| 360 | |||
| 361 | for (id = ibmasr_id_table; id->desc; id++) { | ||
| 362 | if (dmi_find_device(DMI_DEV_TYPE_OTHER, id->desc, NULL)) { | ||
| 363 | asr_type = id->type; | ||
| 364 | break; | ||
| 365 | } | ||
| 366 | } | ||
| 367 | |||
| 368 | if (!asr_type) | ||
| 369 | return -ENODEV; | ||
| 370 | |||
| 371 | rc = misc_register(&asr_miscdev); | ||
| 372 | if (rc < 0) { | ||
| 373 | printk(KERN_ERR PFX "failed to register misc device\n"); | ||
| 374 | return rc; | ||
| 375 | } | ||
| 376 | |||
| 377 | rc = asr_get_base_address(); | ||
| 378 | if (rc) { | ||
| 379 | misc_deregister(&asr_miscdev); | ||
| 380 | return rc; | ||
| 381 | } | ||
| 382 | |||
| 383 | return 0; | ||
| 384 | } | ||
| 385 | |||
| 386 | static void __exit ibmasr_exit(void) | ||
| 387 | { | ||
| 388 | if (!nowayout) | ||
| 389 | asr_disable(); | ||
| 390 | |||
| 391 | misc_deregister(&asr_miscdev); | ||
| 392 | |||
| 393 | release_region(asr_base, asr_length); | ||
| 394 | } | ||
| 395 | |||
| 396 | module_init(ibmasr_init); | ||
| 397 | module_exit(ibmasr_exit); | ||
| 398 | |||
| 399 | module_param(nowayout, int, 0); | ||
| 400 | MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default=CONFIG_WATCHDOG_NOWAYOUT)"); | ||
| 401 | |||
| 402 | MODULE_DESCRIPTION("IBM Automatic Server Restart driver"); | ||
| 403 | MODULE_AUTHOR("Andrey Panin"); | ||
| 404 | MODULE_LICENSE("GPL"); | ||
| 405 | MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR); | ||
diff --git a/drivers/char/watchdog/mv64x60_wdt.c b/drivers/char/watchdog/mv64x60_wdt.c new file mode 100644 index 000000000000..1436aea3b28f --- /dev/null +++ b/drivers/char/watchdog/mv64x60_wdt.c | |||
| @@ -0,0 +1,252 @@ | |||
| 1 | /* | ||
| 2 | * mv64x60_wdt.c - MV64X60 (Marvell Discovery) watchdog userspace interface | ||
| 3 | * | ||
| 4 | * Author: James Chapman <jchapman@katalix.com> | ||
| 5 | * | ||
| 6 | * Platform-specific setup code should configure the dog to generate | ||
| 7 | * interrupt or reset as required. This code only enables/disables | ||
| 8 | * and services the watchdog. | ||
| 9 | * | ||
| 10 | * Derived from mpc8xx_wdt.c, with the following copyright. | ||
| 11 | * | ||
| 12 | * 2002 (c) Florian Schirmer <jolt@tuxbox.org> This file is licensed under | ||
| 13 | * the terms of the GNU General Public License version 2. This program | ||
| 14 | * is licensed "as is" without any warranty of any kind, whether express | ||
| 15 | * or implied. | ||
| 16 | */ | ||
| 17 | |||
| 18 | #include <linux/config.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/watchdog.h> | ||
| 25 | #include <asm/mv64x60.h> | ||
| 26 | #include <asm/uaccess.h> | ||
| 27 | #include <asm/io.h> | ||
| 28 | |||
| 29 | /* MV64x60 WDC (config) register access definitions */ | ||
| 30 | #define MV64x60_WDC_CTL1_MASK (3 << 24) | ||
| 31 | #define MV64x60_WDC_CTL1(val) ((val & 3) << 24) | ||
| 32 | #define MV64x60_WDC_CTL2_MASK (3 << 26) | ||
| 33 | #define MV64x60_WDC_CTL2(val) ((val & 3) << 26) | ||
| 34 | |||
| 35 | /* Flags bits */ | ||
| 36 | #define MV64x60_WDOG_FLAG_OPENED 0 | ||
| 37 | #define MV64x60_WDOG_FLAG_ENABLED 1 | ||
| 38 | |||
| 39 | static unsigned long wdt_flags; | ||
| 40 | static int wdt_status; | ||
| 41 | static void __iomem *mv64x60_regs; | ||
| 42 | static int mv64x60_wdt_timeout; | ||
| 43 | |||
| 44 | static void mv64x60_wdt_reg_write(u32 val) | ||
| 45 | { | ||
| 46 | /* Allow write only to CTL1 / CTL2 fields, retaining values in | ||
| 47 | * other fields. | ||
| 48 | */ | ||
| 49 | u32 data = readl(mv64x60_regs + MV64x60_WDT_WDC); | ||
| 50 | data &= ~(MV64x60_WDC_CTL1_MASK | MV64x60_WDC_CTL2_MASK); | ||
| 51 | data |= val; | ||
| 52 | writel(data, mv64x60_regs + MV64x60_WDT_WDC); | ||
| 53 | } | ||
| 54 | |||
| 55 | static void mv64x60_wdt_service(void) | ||
| 56 | { | ||
| 57 | /* Write 01 followed by 10 to CTL2 */ | ||
| 58 | mv64x60_wdt_reg_write(MV64x60_WDC_CTL2(0x01)); | ||
| 59 | mv64x60_wdt_reg_write(MV64x60_WDC_CTL2(0x02)); | ||
| 60 | } | ||
| 61 | |||
| 62 | static void mv64x60_wdt_handler_disable(void) | ||
| 63 | { | ||
| 64 | if (test_and_clear_bit(MV64x60_WDOG_FLAG_ENABLED, &wdt_flags)) { | ||
| 65 | /* Write 01 followed by 10 to CTL1 */ | ||
| 66 | mv64x60_wdt_reg_write(MV64x60_WDC_CTL1(0x01)); | ||
| 67 | mv64x60_wdt_reg_write(MV64x60_WDC_CTL1(0x02)); | ||
| 68 | printk(KERN_NOTICE "mv64x60_wdt: watchdog deactivated\n"); | ||
| 69 | } | ||
| 70 | } | ||
| 71 | |||
| 72 | static void mv64x60_wdt_handler_enable(void) | ||
| 73 | { | ||
| 74 | if (!test_and_set_bit(MV64x60_WDOG_FLAG_ENABLED, &wdt_flags)) { | ||
| 75 | /* Write 01 followed by 10 to CTL1 */ | ||
| 76 | mv64x60_wdt_reg_write(MV64x60_WDC_CTL1(0x01)); | ||
| 77 | mv64x60_wdt_reg_write(MV64x60_WDC_CTL1(0x02)); | ||
| 78 | printk(KERN_NOTICE "mv64x60_wdt: watchdog activated\n"); | ||
| 79 | } | ||
| 80 | } | ||
| 81 | |||
| 82 | static int mv64x60_wdt_open(struct inode *inode, struct file *file) | ||
| 83 | { | ||
| 84 | if (test_and_set_bit(MV64x60_WDOG_FLAG_OPENED, &wdt_flags)) | ||
| 85 | return -EBUSY; | ||
| 86 | |||
| 87 | mv64x60_wdt_service(); | ||
| 88 | mv64x60_wdt_handler_enable(); | ||
| 89 | |||
| 90 | return 0; | ||
| 91 | } | ||
| 92 | |||
| 93 | static int mv64x60_wdt_release(struct inode *inode, struct file *file) | ||
| 94 | { | ||
| 95 | mv64x60_wdt_service(); | ||
| 96 | |||
| 97 | #if !defined(CONFIG_WATCHDOG_NOWAYOUT) | ||
| 98 | mv64x60_wdt_handler_disable(); | ||
| 99 | #endif | ||
| 100 | |||
| 101 | clear_bit(MV64x60_WDOG_FLAG_OPENED, &wdt_flags); | ||
| 102 | |||
| 103 | return 0; | ||
| 104 | } | ||
| 105 | |||
| 106 | static ssize_t mv64x60_wdt_write(struct file *file, const char *data, | ||
| 107 | size_t len, loff_t * ppos) | ||
| 108 | { | ||
| 109 | if (*ppos != file->f_pos) | ||
| 110 | return -ESPIPE; | ||
| 111 | |||
| 112 | if (len) | ||
| 113 | mv64x60_wdt_service(); | ||
| 114 | |||
| 115 | return len; | ||
| 116 | } | ||
| 117 | |||
| 118 | static int mv64x60_wdt_ioctl(struct inode *inode, struct file *file, | ||
| 119 | unsigned int cmd, unsigned long arg) | ||
| 120 | { | ||
| 121 | int timeout; | ||
| 122 | static struct watchdog_info info = { | ||
| 123 | .options = WDIOF_KEEPALIVEPING, | ||
| 124 | .firmware_version = 0, | ||
| 125 | .identity = "MV64x60 watchdog", | ||
| 126 | }; | ||
| 127 | |||
| 128 | switch (cmd) { | ||
| 129 | case WDIOC_GETSUPPORT: | ||
| 130 | if (copy_to_user((void *)arg, &info, sizeof(info))) | ||
| 131 | return -EFAULT; | ||
| 132 | break; | ||
| 133 | |||
| 134 | case WDIOC_GETSTATUS: | ||
| 135 | case WDIOC_GETBOOTSTATUS: | ||
| 136 | if (put_user(wdt_status, (int *)arg)) | ||
| 137 | return -EFAULT; | ||
| 138 | wdt_status &= ~WDIOF_KEEPALIVEPING; | ||
| 139 | break; | ||
| 140 | |||
| 141 | case WDIOC_GETTEMP: | ||
| 142 | return -EOPNOTSUPP; | ||
| 143 | |||
| 144 | case WDIOC_SETOPTIONS: | ||
| 145 | return -EOPNOTSUPP; | ||
| 146 | |||
| 147 | case WDIOC_KEEPALIVE: | ||
| 148 | mv64x60_wdt_service(); | ||
| 149 | wdt_status |= WDIOF_KEEPALIVEPING; | ||
| 150 | break; | ||
| 151 | |||
| 152 | case WDIOC_SETTIMEOUT: | ||
| 153 | return -EOPNOTSUPP; | ||
| 154 | |||
| 155 | case WDIOC_GETTIMEOUT: | ||
| 156 | timeout = mv64x60_wdt_timeout * HZ; | ||
| 157 | if (put_user(timeout, (int *)arg)) | ||
| 158 | return -EFAULT; | ||
| 159 | break; | ||
| 160 | |||
| 161 | default: | ||
| 162 | return -ENOIOCTLCMD; | ||
| 163 | } | ||
| 164 | |||
| 165 | return 0; | ||
| 166 | } | ||
| 167 | |||
| 168 | static struct file_operations mv64x60_wdt_fops = { | ||
| 169 | .owner = THIS_MODULE, | ||
| 170 | .llseek = no_llseek, | ||
| 171 | .write = mv64x60_wdt_write, | ||
| 172 | .ioctl = mv64x60_wdt_ioctl, | ||
| 173 | .open = mv64x60_wdt_open, | ||
| 174 | .release = mv64x60_wdt_release, | ||
| 175 | }; | ||
| 176 | |||
| 177 | static struct miscdevice mv64x60_wdt_miscdev = { | ||
| 178 | .minor = WATCHDOG_MINOR, | ||
| 179 | .name = "watchdog", | ||
| 180 | .fops = &mv64x60_wdt_fops, | ||
| 181 | }; | ||
| 182 | |||
| 183 | static int __devinit mv64x60_wdt_probe(struct device *dev) | ||
| 184 | { | ||
| 185 | struct platform_device *pd = to_platform_device(dev); | ||
| 186 | struct mv64x60_wdt_pdata *pdata = pd->dev.platform_data; | ||
| 187 | int bus_clk = 133; | ||
| 188 | |||
| 189 | mv64x60_wdt_timeout = 10; | ||
| 190 | if (pdata) { | ||
| 191 | mv64x60_wdt_timeout = pdata->timeout; | ||
| 192 | bus_clk = pdata->bus_clk; | ||
| 193 | } | ||
| 194 | |||
| 195 | mv64x60_regs = mv64x60_get_bridge_vbase(); | ||
| 196 | |||
| 197 | writel((mv64x60_wdt_timeout * (bus_clk * 1000000)) >> 8, | ||
| 198 | mv64x60_regs + MV64x60_WDT_WDC); | ||
| 199 | |||
| 200 | return misc_register(&mv64x60_wdt_miscdev); | ||
| 201 | } | ||
| 202 | |||
| 203 | static int __devexit mv64x60_wdt_remove(struct device *dev) | ||
| 204 | { | ||
| 205 | misc_deregister(&mv64x60_wdt_miscdev); | ||
| 206 | |||
| 207 | mv64x60_wdt_service(); | ||
| 208 | mv64x60_wdt_handler_disable(); | ||
| 209 | |||
| 210 | return 0; | ||
| 211 | } | ||
| 212 | |||
| 213 | static struct device_driver mv64x60_wdt_driver = { | ||
| 214 | .name = MV64x60_WDT_NAME, | ||
| 215 | .bus = &platform_bus_type, | ||
| 216 | .probe = mv64x60_wdt_probe, | ||
| 217 | .remove = __devexit_p(mv64x60_wdt_remove), | ||
| 218 | }; | ||
| 219 | |||
| 220 | static struct platform_device *mv64x60_wdt_dev; | ||
| 221 | |||
| 222 | static int __init mv64x60_wdt_init(void) | ||
| 223 | { | ||
| 224 | int ret; | ||
| 225 | |||
| 226 | printk(KERN_INFO "MV64x60 watchdog driver\n"); | ||
| 227 | |||
| 228 | mv64x60_wdt_dev = platform_device_register_simple(MV64x60_WDT_NAME, | ||
| 229 | -1, NULL, 0); | ||
| 230 | if (IS_ERR(mv64x60_wdt_dev)) { | ||
| 231 | ret = PTR_ERR(mv64x60_wdt_dev); | ||
| 232 | goto out; | ||
| 233 | } | ||
| 234 | |||
| 235 | ret = driver_register(&mv64x60_wdt_driver); | ||
| 236 | out: | ||
| 237 | return ret; | ||
| 238 | } | ||
| 239 | |||
| 240 | static void __exit mv64x60_wdt_exit(void) | ||
| 241 | { | ||
| 242 | driver_unregister(&mv64x60_wdt_driver); | ||
| 243 | platform_device_unregister(mv64x60_wdt_dev); | ||
| 244 | } | ||
| 245 | |||
| 246 | module_init(mv64x60_wdt_init); | ||
| 247 | module_exit(mv64x60_wdt_exit); | ||
| 248 | |||
| 249 | MODULE_AUTHOR("James Chapman <jchapman@katalix.com>"); | ||
| 250 | MODULE_DESCRIPTION("MV64x60 watchdog driver"); | ||
| 251 | MODULE_LICENSE("GPL"); | ||
| 252 | MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR); | ||
diff --git a/drivers/char/watchdog/pcwd_pci.c b/drivers/char/watchdog/pcwd_pci.c index 2b13afb09c5d..5a80adbf8032 100644 --- a/drivers/char/watchdog/pcwd_pci.c +++ b/drivers/char/watchdog/pcwd_pci.c | |||
| @@ -29,27 +29,29 @@ | |||
| 29 | * Includes, defines, variables, module parameters, ... | 29 | * Includes, defines, variables, module parameters, ... |
| 30 | */ | 30 | */ |
| 31 | 31 | ||
| 32 | #include <linux/config.h> | 32 | #include <linux/config.h> /* For CONFIG_WATCHDOG_NOWAYOUT/... */ |
| 33 | #include <linux/module.h> | 33 | #include <linux/module.h> /* For module specific items */ |
| 34 | #include <linux/moduleparam.h> | 34 | #include <linux/moduleparam.h> /* For new moduleparam's */ |
| 35 | #include <linux/types.h> | 35 | #include <linux/types.h> /* For standard types (like size_t) */ |
| 36 | #include <linux/delay.h> | 36 | #include <linux/errno.h> /* For the -ENODEV/... values */ |
| 37 | #include <linux/miscdevice.h> | 37 | #include <linux/kernel.h> /* For printk/panic/... */ |
| 38 | #include <linux/watchdog.h> | 38 | #include <linux/delay.h> /* For mdelay function */ |
| 39 | #include <linux/notifier.h> | 39 | #include <linux/miscdevice.h> /* For MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR) */ |
| 40 | #include <linux/reboot.h> | 40 | #include <linux/watchdog.h> /* For the watchdog specific items */ |
| 41 | #include <linux/init.h> | 41 | #include <linux/notifier.h> /* For notifier support */ |
| 42 | #include <linux/fs.h> | 42 | #include <linux/reboot.h> /* For reboot_notifier stuff */ |
| 43 | #include <linux/pci.h> | 43 | #include <linux/init.h> /* For __init/__exit/... */ |
| 44 | #include <linux/ioport.h> | 44 | #include <linux/fs.h> /* For file operations */ |
| 45 | #include <linux/spinlock.h> | 45 | #include <linux/pci.h> /* For pci functions */ |
| 46 | 46 | #include <linux/ioport.h> /* For io-port access */ | |
| 47 | #include <asm/uaccess.h> | 47 | #include <linux/spinlock.h> /* For spin_lock/spin_unlock/... */ |
| 48 | #include <asm/io.h> | 48 | |
| 49 | #include <asm/uaccess.h> /* For copy_to_user/put_user/... */ | ||
| 50 | #include <asm/io.h> /* For inb/outb/... */ | ||
| 49 | 51 | ||
| 50 | /* Module and version information */ | 52 | /* Module and version information */ |
| 51 | #define WATCHDOG_VERSION "1.01" | 53 | #define WATCHDOG_VERSION "1.01" |
| 52 | #define WATCHDOG_DATE "15 Mar 2005" | 54 | #define WATCHDOG_DATE "02 Sep 2005" |
| 53 | #define WATCHDOG_DRIVER_NAME "PCI-PC Watchdog" | 55 | #define WATCHDOG_DRIVER_NAME "PCI-PC Watchdog" |
| 54 | #define WATCHDOG_NAME "pcwd_pci" | 56 | #define WATCHDOG_NAME "pcwd_pci" |
| 55 | #define PFX WATCHDOG_NAME ": " | 57 | #define PFX WATCHDOG_NAME ": " |
| @@ -335,12 +337,14 @@ static int pcipcwd_ioctl(struct inode *inode, struct file *file, | |||
| 335 | return -EFAULT; | 337 | return -EFAULT; |
| 336 | 338 | ||
| 337 | if (new_options & WDIOS_DISABLECARD) { | 339 | if (new_options & WDIOS_DISABLECARD) { |
| 338 | pcipcwd_stop(); | 340 | if (pcipcwd_stop()) |
| 341 | return -EIO; | ||
| 339 | retval = 0; | 342 | retval = 0; |
| 340 | } | 343 | } |
| 341 | 344 | ||
| 342 | if (new_options & WDIOS_ENABLECARD) { | 345 | if (new_options & WDIOS_ENABLECARD) { |
| 343 | pcipcwd_start(); | 346 | if (pcipcwd_start()) |
| 347 | return -EIO; | ||
| 344 | retval = 0; | 348 | retval = 0; |
| 345 | } | 349 | } |
| 346 | 350 | ||
diff --git a/drivers/char/watchdog/s3c2410_wdt.c b/drivers/char/watchdog/s3c2410_wdt.c index 8b292bf343c4..3625b2601b42 100644 --- a/drivers/char/watchdog/s3c2410_wdt.c +++ b/drivers/char/watchdog/s3c2410_wdt.c | |||
| @@ -464,7 +464,7 @@ static void s3c2410wdt_shutdown(struct device *dev) | |||
| 464 | static unsigned long wtcon_save; | 464 | static unsigned long wtcon_save; |
| 465 | static unsigned long wtdat_save; | 465 | static unsigned long wtdat_save; |
| 466 | 466 | ||
| 467 | static int s3c2410wdt_suspend(struct device *dev, u32 state, u32 level) | 467 | static int s3c2410wdt_suspend(struct device *dev, pm_message_t state, u32 level) |
| 468 | { | 468 | { |
| 469 | if (level == SUSPEND_POWER_DOWN) { | 469 | if (level == SUSPEND_POWER_DOWN) { |
| 470 | /* Save watchdog state, and turn it off. */ | 470 | /* Save watchdog state, and turn it off. */ |
diff --git a/drivers/char/watchdog/sbc8360.c b/drivers/char/watchdog/sbc8360.c new file mode 100644 index 000000000000..c6cbf808d8c2 --- /dev/null +++ b/drivers/char/watchdog/sbc8360.c | |||
| @@ -0,0 +1,414 @@ | |||
| 1 | /* | ||
| 2 | * SBC8360 Watchdog driver | ||
| 3 | * | ||
| 4 | * (c) Copyright 2005 Webcon, Inc. | ||
| 5 | * | ||
| 6 | * Based on ib700wdt.c, which is based on advantechwdt.c which is based | ||
| 7 | * on acquirewdt.c which is based on wdt.c. | ||
| 8 | * | ||
| 9 | * (c) Copyright 2001 Charles Howes <chowes@vsol.net> | ||
| 10 | * | ||
| 11 | * Based on advantechwdt.c which is based on acquirewdt.c which | ||
| 12 | * is based on wdt.c. | ||
| 13 | * | ||
| 14 | * (c) Copyright 2000-2001 Marek Michalkiewicz <marekm@linux.org.pl> | ||
| 15 | * | ||
| 16 | * Based on acquirewdt.c which is based on wdt.c. | ||
| 17 | * Original copyright messages: | ||
| 18 | * | ||
| 19 | * (c) Copyright 1996 Alan Cox <alan@redhat.com>, All Rights Reserved. | ||
| 20 | * http://www.redhat.com | ||
| 21 | * | ||
| 22 | * This program is free software; you can redistribute it and/or | ||
| 23 | * modify it under the terms of the GNU General Public License | ||
| 24 | * as published by the Free Software Foundation; either version | ||
| 25 | * 2 of the License, or (at your option) any later version. | ||
| 26 | * | ||
| 27 | * Neither Alan Cox nor CymruNet Ltd. admit liability nor provide | ||
| 28 | * warranty for any of this software. This material is provided | ||
| 29 | * "AS-IS" and at no charge. | ||
| 30 | * | ||
| 31 | * (c) Copyright 1995 Alan Cox <alan@redhat.com> | ||
| 32 | * | ||
| 33 | * 14-Dec-2001 Matt Domsch <Matt_Domsch@dell.com> | ||
| 34 | * Added nowayout module option to override CONFIG_WATCHDOG_NOWAYOUT | ||
| 35 | * Added timeout module option to override default | ||
| 36 | * | ||
| 37 | */ | ||
| 38 | |||
| 39 | #include <linux/config.h> | ||
| 40 | #include <linux/module.h> | ||
| 41 | #include <linux/types.h> | ||
| 42 | #include <linux/miscdevice.h> | ||
| 43 | #include <linux/watchdog.h> | ||
| 44 | #include <linux/ioport.h> | ||
| 45 | #include <linux/delay.h> | ||
| 46 | #include <linux/notifier.h> | ||
| 47 | #include <linux/fs.h> | ||
| 48 | #include <linux/reboot.h> | ||
| 49 | #include <linux/init.h> | ||
| 50 | #include <linux/spinlock.h> | ||
| 51 | #include <linux/moduleparam.h> | ||
| 52 | |||
| 53 | #include <asm/io.h> | ||
| 54 | #include <asm/uaccess.h> | ||
| 55 | #include <asm/system.h> | ||
| 56 | |||
| 57 | static unsigned long sbc8360_is_open; | ||
| 58 | static spinlock_t sbc8360_lock; | ||
| 59 | static char expect_close; | ||
| 60 | |||
| 61 | #define PFX "sbc8360: " | ||
| 62 | |||
| 63 | /* | ||
| 64 | * | ||
| 65 | * Watchdog Timer Configuration | ||
| 66 | * | ||
| 67 | * The function of the watchdog timer is to reset the system automatically | ||
| 68 | * and is defined at I/O port 0120H and 0121H. To enable the watchdog timer | ||
| 69 | * and allow the system to reset, write appropriate values from the table | ||
| 70 | * below to I/O port 0120H and 0121H. To disable the timer, write a zero | ||
| 71 | * value to I/O port 0121H for the system to stop the watchdog function. | ||
| 72 | * | ||
| 73 | * The following describes how the timer should be programmed (according to | ||
| 74 | * the vendor documentation) | ||
| 75 | * | ||
| 76 | * Enabling Watchdog: | ||
| 77 | * MOV AX,000AH (enable, phase I) | ||
| 78 | * MOV DX,0120H | ||
| 79 | * OUT DX,AX | ||
| 80 | * MOV AX,000BH (enable, phase II) | ||
| 81 | * MOV DX,0120H | ||
| 82 | * OUT DX,AX | ||
| 83 | * MOV AX,000nH (set multiplier n, from 1-4) | ||
| 84 | * MOV DX,0120H | ||
| 85 | * OUT DX,AX | ||
| 86 | * MOV AX,000mH (set base timer m, from 0-F) | ||
| 87 | * MOV DX,0121H | ||
| 88 | * OUT DX,AX | ||
| 89 | * | ||
| 90 | * Reset timer: | ||
| 91 | * MOV AX,000mH (same as set base timer, above) | ||
| 92 | * MOV DX,0121H | ||
| 93 | * OUT DX,AX | ||
| 94 | * | ||
| 95 | * Disabling Watchdog: | ||
| 96 | * MOV AX,0000H (a zero value) | ||
| 97 | * MOV DX,0120H | ||
| 98 | * OUT DX,AX | ||
| 99 | * | ||
| 100 | * Watchdog timeout configuration values: | ||
| 101 | * N | ||
| 102 | * M | 1 2 3 4 | ||
| 103 | * --|---------------------------------- | ||
| 104 | * 0 | 0.5s 5s 50s 100s | ||
| 105 | * 1 | 1s 10s 100s 200s | ||
| 106 | * 2 | 1.5s 15s 150s 300s | ||
| 107 | * 3 | 2s 20s 200s 400s | ||
| 108 | * 4 | 2.5s 25s 250s 500s | ||
| 109 | * 5 | 3s 30s 300s 600s | ||
| 110 | * 6 | 3.5s 35s 350s 700s | ||
| 111 | * 7 | 4s 40s 400s 800s | ||
| 112 | * 8 | 4.5s 45s 450s 900s | ||
| 113 | * 9 | 5s 50s 500s 1000s | ||
| 114 | * A | 5.5s 55s 550s 1100s | ||
| 115 | * B | 6s 60s 600s 1200s | ||
| 116 | * C | 6.5s 65s 650s 1300s | ||
| 117 | * D | 7s 70s 700s 1400s | ||
| 118 | * E | 7.5s 75s 750s 1500s | ||
| 119 | * F | 8s 80s 800s 1600s | ||
| 120 | * | ||
| 121 | * Another way to say the same things is: | ||
| 122 | * For N=1, Timeout = (M+1) * 0.5s | ||
| 123 | * For N=2, Timeout = (M+1) * 5s | ||
| 124 | * For N=3, Timeout = (M+1) * 50s | ||
| 125 | * For N=4, Timeout = (M+1) * 100s | ||
| 126 | * | ||
| 127 | */ | ||
| 128 | |||
| 129 | static int wd_times[64][2] = { | ||
| 130 | {0, 1}, /* 0 = 0.5s */ | ||
| 131 | {1, 1}, /* 1 = 1s */ | ||
| 132 | {2, 1}, /* 2 = 1.5s */ | ||
| 133 | {3, 1}, /* 3 = 2s */ | ||
| 134 | {4, 1}, /* 4 = 2.5s */ | ||
| 135 | {5, 1}, /* 5 = 3s */ | ||
| 136 | {6, 1}, /* 6 = 3.5s */ | ||
| 137 | {7, 1}, /* 7 = 4s */ | ||
| 138 | {8, 1}, /* 8 = 4.5s */ | ||
| 139 | {9, 1}, /* 9 = 5s */ | ||
| 140 | {0xA, 1}, /* 10 = 5.5s */ | ||
| 141 | {0xB, 1}, /* 11 = 6s */ | ||
| 142 | {0xC, 1}, /* 12 = 6.5s */ | ||
| 143 | {0xD, 1}, /* 13 = 7s */ | ||
| 144 | {0xE, 1}, /* 14 = 7.5s */ | ||
| 145 | {0xF, 1}, /* 15 = 8s */ | ||
| 146 | {0, 2}, /* 16 = 5s */ | ||
| 147 | {1, 2}, /* 17 = 10s */ | ||
| 148 | {2, 2}, /* 18 = 15s */ | ||
| 149 | {3, 2}, /* 19 = 20s */ | ||
| 150 | {4, 2}, /* 20 = 25s */ | ||
| 151 | {5, 2}, /* 21 = 30s */ | ||
| 152 | {6, 2}, /* 22 = 35s */ | ||
| 153 | {7, 2}, /* 23 = 40s */ | ||
| 154 | {8, 2}, /* 24 = 45s */ | ||
| 155 | {9, 2}, /* 25 = 50s */ | ||
| 156 | {0xA, 2}, /* 26 = 55s */ | ||
| 157 | {0xB, 2}, /* 27 = 60s */ | ||
| 158 | {0xC, 2}, /* 28 = 65s */ | ||
| 159 | {0xD, 2}, /* 29 = 70s */ | ||
| 160 | {0xE, 2}, /* 30 = 75s */ | ||
| 161 | {0xF, 2}, /* 31 = 80s */ | ||
| 162 | {0, 3}, /* 32 = 50s */ | ||
| 163 | {1, 3}, /* 33 = 100s */ | ||
| 164 | {2, 3}, /* 34 = 150s */ | ||
| 165 | {3, 3}, /* 35 = 200s */ | ||
| 166 | {4, 3}, /* 36 = 250s */ | ||
| 167 | {5, 3}, /* 37 = 300s */ | ||
| 168 | {6, 3}, /* 38 = 350s */ | ||
| 169 | {7, 3}, /* 39 = 400s */ | ||
| 170 | {8, 3}, /* 40 = 450s */ | ||
| 171 | {9, 3}, /* 41 = 500s */ | ||
| 172 | {0xA, 3}, /* 42 = 550s */ | ||
| 173 | {0xB, 3}, /* 43 = 600s */ | ||
| 174 | {0xC, 3}, /* 44 = 650s */ | ||
| 175 | {0xD, 3}, /* 45 = 700s */ | ||
| 176 | {0xE, 3}, /* 46 = 750s */ | ||
| 177 | {0xF, 3}, /* 47 = 800s */ | ||
| 178 | {0, 4}, /* 48 = 100s */ | ||
| 179 | {1, 4}, /* 49 = 200s */ | ||
| 180 | {2, 4}, /* 50 = 300s */ | ||
| 181 | {3, 4}, /* 51 = 400s */ | ||
| 182 | {4, 4}, /* 52 = 500s */ | ||
| 183 | {5, 4}, /* 53 = 600s */ | ||
| 184 | {6, 4}, /* 54 = 700s */ | ||
| 185 | {7, 4}, /* 55 = 800s */ | ||
| 186 | {8, 4}, /* 56 = 900s */ | ||
| 187 | {9, 4}, /* 57 = 1000s */ | ||
| 188 | {0xA, 4}, /* 58 = 1100s */ | ||
| 189 | {0xB, 4}, /* 59 = 1200s */ | ||
| 190 | {0xC, 4}, /* 60 = 1300s */ | ||
| 191 | {0xD, 4}, /* 61 = 1400s */ | ||
| 192 | {0xE, 4}, /* 62 = 1500s */ | ||
| 193 | {0xF, 4} /* 63 = 1600s */ | ||
| 194 | }; | ||
| 195 | |||
| 196 | #define SBC8360_ENABLE 0x120 | ||
| 197 | #define SBC8360_BASETIME 0x121 | ||
| 198 | |||
| 199 | static int timeout = 27; | ||
| 200 | static int wd_margin = 0xB; | ||
| 201 | static int wd_multiplier = 2; | ||
| 202 | static int nowayout = WATCHDOG_NOWAYOUT; | ||
| 203 | |||
| 204 | module_param(timeout, int, 27); | ||
| 205 | MODULE_PARM_DESC(timeout, "Index into timeout table (0-63) (default=27 (60s))"); | ||
| 206 | module_param(nowayout, int, 0); | ||
| 207 | MODULE_PARM_DESC(nowayout, | ||
| 208 | "Watchdog cannot be stopped once started (default=CONFIG_WATCHDOG_NOWAYOUT)"); | ||
| 209 | |||
| 210 | /* | ||
| 211 | * Kernel methods. | ||
| 212 | */ | ||
| 213 | |||
| 214 | /* Activate and pre-configure watchdog */ | ||
| 215 | static void sbc8360_activate(void) | ||
| 216 | { | ||
| 217 | /* Enable the watchdog */ | ||
| 218 | outb(0x0A, SBC8360_ENABLE); | ||
| 219 | msleep_interruptible(100); | ||
| 220 | outb(0x0B, SBC8360_ENABLE); | ||
| 221 | msleep_interruptible(100); | ||
| 222 | /* Set timeout multiplier */ | ||
| 223 | outb(wd_multiplier, SBC8360_ENABLE); | ||
| 224 | msleep_interruptible(100); | ||
| 225 | /* Nothing happens until first sbc8360_ping() */ | ||
| 226 | } | ||
| 227 | |||
| 228 | /* Kernel pings watchdog */ | ||
| 229 | static void sbc8360_ping(void) | ||
| 230 | { | ||
| 231 | /* Write the base timer register */ | ||
| 232 | outb(wd_margin, SBC8360_BASETIME); | ||
| 233 | } | ||
| 234 | |||
| 235 | /* Userspace pings kernel driver, or requests clean close */ | ||
| 236 | static ssize_t sbc8360_write(struct file *file, const char __user * buf, | ||
| 237 | size_t count, loff_t * ppos) | ||
| 238 | { | ||
| 239 | if (count) { | ||
| 240 | if (!nowayout) { | ||
| 241 | size_t i; | ||
| 242 | |||
| 243 | /* In case it was set long ago */ | ||
| 244 | expect_close = 0; | ||
| 245 | |||
| 246 | for (i = 0; i != count; i++) { | ||
| 247 | char c; | ||
| 248 | if (get_user(c, buf + i)) | ||
| 249 | return -EFAULT; | ||
| 250 | if (c == 'V') | ||
| 251 | expect_close = 42; | ||
| 252 | } | ||
| 253 | } | ||
| 254 | sbc8360_ping(); | ||
| 255 | } | ||
| 256 | return count; | ||
| 257 | } | ||
| 258 | |||
| 259 | static int sbc8360_open(struct inode *inode, struct file *file) | ||
| 260 | { | ||
| 261 | spin_lock(&sbc8360_lock); | ||
| 262 | if (test_and_set_bit(0, &sbc8360_is_open)) { | ||
| 263 | spin_unlock(&sbc8360_lock); | ||
| 264 | return -EBUSY; | ||
| 265 | } | ||
| 266 | if (nowayout) | ||
| 267 | __module_get(THIS_MODULE); | ||
| 268 | |||
| 269 | /* Activate and ping once to start the countdown */ | ||
| 270 | spin_unlock(&sbc8360_lock); | ||
| 271 | sbc8360_activate(); | ||
| 272 | sbc8360_ping(); | ||
| 273 | return nonseekable_open(inode, file); | ||
| 274 | } | ||
| 275 | |||
| 276 | static int sbc8360_close(struct inode *inode, struct file *file) | ||
| 277 | { | ||
| 278 | spin_lock(&sbc8360_lock); | ||
| 279 | if (expect_close == 42) | ||
| 280 | outb(0, SBC8360_ENABLE); | ||
| 281 | else | ||
| 282 | printk(KERN_CRIT PFX | ||
| 283 | "SBC8360 device closed unexpectedly. SBC8360 will not stop!\n"); | ||
| 284 | |||
| 285 | clear_bit(0, &sbc8360_is_open); | ||
| 286 | expect_close = 0; | ||
| 287 | spin_unlock(&sbc8360_lock); | ||
| 288 | return 0; | ||
| 289 | } | ||
| 290 | |||
| 291 | /* | ||
| 292 | * Notifier for system down | ||
| 293 | */ | ||
| 294 | |||
| 295 | static int sbc8360_notify_sys(struct notifier_block *this, unsigned long code, | ||
| 296 | void *unused) | ||
| 297 | { | ||
| 298 | if (code == SYS_DOWN || code == SYS_HALT) { | ||
| 299 | /* Disable the SBC8360 Watchdog */ | ||
| 300 | outb(0, SBC8360_ENABLE); | ||
| 301 | } | ||
| 302 | return NOTIFY_DONE; | ||
| 303 | } | ||
| 304 | |||
| 305 | /* | ||
| 306 | * Kernel Interfaces | ||
| 307 | */ | ||
| 308 | |||
| 309 | static struct file_operations sbc8360_fops = { | ||
| 310 | .owner = THIS_MODULE, | ||
| 311 | .llseek = no_llseek, | ||
| 312 | .write = sbc8360_write, | ||
| 313 | .open = sbc8360_open, | ||
| 314 | .release = sbc8360_close, | ||
| 315 | }; | ||
| 316 | |||
| 317 | static struct miscdevice sbc8360_miscdev = { | ||
| 318 | .minor = WATCHDOG_MINOR, | ||
| 319 | .name = "watchdog", | ||
| 320 | .fops = &sbc8360_fops, | ||
| 321 | }; | ||
| 322 | |||
| 323 | /* | ||
| 324 | * The SBC8360 needs to learn about soft shutdowns in order to | ||
| 325 | * turn the timebomb registers off. | ||
| 326 | */ | ||
| 327 | |||
| 328 | static struct notifier_block sbc8360_notifier = { | ||
| 329 | .notifier_call = sbc8360_notify_sys, | ||
| 330 | }; | ||
| 331 | |||
| 332 | static int __init sbc8360_init(void) | ||
| 333 | { | ||
| 334 | int res; | ||
| 335 | unsigned long int mseconds = 60000; | ||
| 336 | |||
| 337 | spin_lock_init(&sbc8360_lock); | ||
| 338 | res = misc_register(&sbc8360_miscdev); | ||
| 339 | if (res) { | ||
| 340 | printk(KERN_ERR PFX "failed to register misc device\n"); | ||
| 341 | goto out_nomisc; | ||
| 342 | } | ||
| 343 | |||
| 344 | if (!request_region(SBC8360_ENABLE, 1, "SBC8360")) { | ||
| 345 | printk(KERN_ERR PFX "ENABLE method I/O %X is not available.\n", | ||
| 346 | SBC8360_ENABLE); | ||
| 347 | res = -EIO; | ||
| 348 | goto out_noenablereg; | ||
| 349 | } | ||
| 350 | if (!request_region(SBC8360_BASETIME, 1, "SBC8360")) { | ||
| 351 | printk(KERN_ERR PFX | ||
| 352 | "BASETIME method I/O %X is not available.\n", | ||
| 353 | SBC8360_BASETIME); | ||
| 354 | res = -EIO; | ||
| 355 | goto out_nobasetimereg; | ||
| 356 | } | ||
| 357 | |||
| 358 | res = register_reboot_notifier(&sbc8360_notifier); | ||
| 359 | if (res) { | ||
| 360 | printk(KERN_ERR PFX "Failed to register reboot notifier.\n"); | ||
| 361 | goto out_noreboot; | ||
| 362 | } | ||
| 363 | |||
| 364 | if (timeout < 0 || timeout > 63) { | ||
| 365 | printk(KERN_ERR PFX "Invalid timeout index (must be 0-63).\n"); | ||
| 366 | res = -EINVAL; | ||
| 367 | goto out_noreboot; | ||
| 368 | } | ||
| 369 | |||
| 370 | wd_margin = wd_times[timeout][0]; | ||
| 371 | wd_multiplier = wd_times[timeout][1]; | ||
| 372 | |||
| 373 | if (wd_multiplier == 1) | ||
| 374 | mseconds = (wd_margin + 1) * 500; | ||
| 375 | else if (wd_multiplier == 2) | ||
| 376 | mseconds = (wd_margin + 1) * 5000; | ||
| 377 | else if (wd_multiplier == 3) | ||
| 378 | mseconds = (wd_margin + 1) * 50000; | ||
| 379 | else if (wd_multiplier == 4) | ||
| 380 | mseconds = (wd_margin + 1) * 100000; | ||
| 381 | |||
| 382 | /* My kingdom for the ability to print "0.5 seconds" in the kernel! */ | ||
| 383 | printk(KERN_INFO PFX "Timeout set at %ld ms.\n", mseconds); | ||
| 384 | |||
| 385 | return 0; | ||
| 386 | |||
| 387 | out_noreboot: | ||
| 388 | release_region(SBC8360_ENABLE, 1); | ||
| 389 | release_region(SBC8360_BASETIME, 1); | ||
| 390 | out_noenablereg: | ||
| 391 | out_nobasetimereg: | ||
| 392 | misc_deregister(&sbc8360_miscdev); | ||
| 393 | out_nomisc: | ||
| 394 | return res; | ||
| 395 | } | ||
| 396 | |||
| 397 | static void __exit sbc8360_exit(void) | ||
| 398 | { | ||
| 399 | misc_deregister(&sbc8360_miscdev); | ||
| 400 | unregister_reboot_notifier(&sbc8360_notifier); | ||
| 401 | release_region(SBC8360_ENABLE, 1); | ||
| 402 | release_region(SBC8360_BASETIME, 1); | ||
| 403 | } | ||
| 404 | |||
| 405 | module_init(sbc8360_init); | ||
| 406 | module_exit(sbc8360_exit); | ||
| 407 | |||
| 408 | MODULE_AUTHOR("Ian E. Morgan <imorgan@webcon.ca>"); | ||
| 409 | MODULE_DESCRIPTION("SBC8360 watchdog driver"); | ||
| 410 | MODULE_LICENSE("GPL"); | ||
| 411 | MODULE_VERSION("1.0"); | ||
| 412 | MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR); | ||
| 413 | |||
| 414 | /* end of sbc8360.c */ | ||
diff --git a/drivers/char/watchdog/w83977f_wdt.c b/drivers/char/watchdog/w83977f_wdt.c new file mode 100644 index 000000000000..a7ff64c8921f --- /dev/null +++ b/drivers/char/watchdog/w83977f_wdt.c | |||
| @@ -0,0 +1,543 @@ | |||
| 1 | /* | ||
| 2 | * W83977F Watchdog Timer Driver for Winbond W83977F I/O Chip | ||
| 3 | * | ||
| 4 | * (c) Copyright 2005 Jose Goncalves <jose.goncalves@inov.pt> | ||
| 5 | * | ||
| 6 | * Based on w83877f_wdt.c by Scott Jennings, | ||
| 7 | * and wdt977.c by Woody Suwalski | ||
| 8 | * | ||
| 9 | * ----------------------- | ||
| 10 | * | ||
| 11 | * This program is free software; you can redistribute it and/or | ||
| 12 | * modify it under the terms of the GNU General Public License | ||
| 13 | * as published by the Free Software Foundation; either version | ||
| 14 | * 2 of the License, or (at your option) any later version. | ||
| 15 | * | ||
| 16 | */ | ||
| 17 | |||
| 18 | #include <linux/module.h> | ||
| 19 | #include <linux/moduleparam.h> | ||
| 20 | #include <linux/config.h> | ||
| 21 | #include <linux/types.h> | ||
| 22 | #include <linux/kernel.h> | ||
| 23 | #include <linux/fs.h> | ||
| 24 | #include <linux/miscdevice.h> | ||
| 25 | #include <linux/init.h> | ||
| 26 | #include <linux/ioport.h> | ||
| 27 | #include <linux/watchdog.h> | ||
| 28 | #include <linux/notifier.h> | ||
| 29 | #include <linux/reboot.h> | ||
| 30 | |||
| 31 | #include <asm/io.h> | ||
| 32 | #include <asm/system.h> | ||
| 33 | #include <asm/uaccess.h> | ||
| 34 | |||
| 35 | #define WATCHDOG_VERSION "1.00" | ||
| 36 | #define WATCHDOG_NAME "W83977F WDT" | ||
| 37 | #define PFX WATCHDOG_NAME ": " | ||
| 38 | #define DRIVER_VERSION WATCHDOG_NAME " driver, v" WATCHDOG_VERSION "\n" | ||
| 39 | |||
| 40 | #define IO_INDEX_PORT 0x3F0 | ||
| 41 | #define IO_DATA_PORT (IO_INDEX_PORT+1) | ||
| 42 | |||
| 43 | #define UNLOCK_DATA 0x87 | ||
| 44 | #define LOCK_DATA 0xAA | ||
| 45 | #define DEVICE_REGISTER 0x07 | ||
| 46 | |||
| 47 | #define DEFAULT_TIMEOUT 45 /* default timeout in seconds */ | ||
| 48 | |||
| 49 | static int timeout = DEFAULT_TIMEOUT; | ||
| 50 | static int timeoutW; /* timeout in watchdog counter units */ | ||
| 51 | static unsigned long timer_alive; | ||
| 52 | static int testmode; | ||
| 53 | static char expect_close; | ||
| 54 | static spinlock_t spinlock; | ||
| 55 | |||
| 56 | module_param(timeout, int, 0); | ||
| 57 | MODULE_PARM_DESC(timeout,"Watchdog timeout in seconds (15..7635), default=" __MODULE_STRING(DEFAULT_TIMEOUT) ")"); | ||
| 58 | module_param(testmode, int, 0); | ||
| 59 | MODULE_PARM_DESC(testmode,"Watchdog testmode (1 = no reboot), default=0"); | ||
| 60 | |||
| 61 | static int nowayout = WATCHDOG_NOWAYOUT; | ||
| 62 | module_param(nowayout, int, 0); | ||
| 63 | MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default=CONFIG_WATCHDOG_NOWAYOUT)"); | ||
| 64 | |||
| 65 | /* | ||
| 66 | * Start the watchdog | ||
| 67 | */ | ||
| 68 | |||
| 69 | static int wdt_start(void) | ||
| 70 | { | ||
| 71 | unsigned long flags; | ||
| 72 | |||
| 73 | spin_lock_irqsave(&spinlock, flags); | ||
| 74 | |||
| 75 | /* Unlock the SuperIO chip */ | ||
| 76 | outb_p(UNLOCK_DATA,IO_INDEX_PORT); | ||
| 77 | outb_p(UNLOCK_DATA,IO_INDEX_PORT); | ||
| 78 | |||
| 79 | /* | ||
| 80 | * Select device Aux2 (device=8) to set watchdog regs F2, F3 and F4. | ||
| 81 | * F2 has the timeout in watchdog counter units. | ||
| 82 | * F3 is set to enable watchdog LED blink at timeout. | ||
| 83 | * F4 is used to just clear the TIMEOUT'ed state (bit 0). | ||
| 84 | */ | ||
| 85 | outb_p(DEVICE_REGISTER,IO_INDEX_PORT); | ||
| 86 | outb_p(0x08,IO_DATA_PORT); | ||
| 87 | outb_p(0xF2,IO_INDEX_PORT); | ||
| 88 | outb_p(timeoutW,IO_DATA_PORT); | ||
| 89 | outb_p(0xF3,IO_INDEX_PORT); | ||
| 90 | outb_p(0x08,IO_DATA_PORT); | ||
| 91 | outb_p(0xF4,IO_INDEX_PORT); | ||
| 92 | outb_p(0x00,IO_DATA_PORT); | ||
| 93 | |||
| 94 | /* Set device Aux2 active */ | ||
| 95 | outb_p(0x30,IO_INDEX_PORT); | ||
| 96 | outb_p(0x01,IO_DATA_PORT); | ||
| 97 | |||
| 98 | /* | ||
| 99 | * Select device Aux1 (dev=7) to set GP16 as the watchdog output | ||
| 100 | * (in reg E6) and GP13 as the watchdog LED output (in reg E3). | ||
| 101 | * Map GP16 at pin 119. | ||
| 102 | * In test mode watch the bit 0 on F4 to indicate "triggered" or | ||
| 103 | * check watchdog LED on SBC. | ||
| 104 | */ | ||
| 105 | outb_p(DEVICE_REGISTER,IO_INDEX_PORT); | ||
| 106 | outb_p(0x07,IO_DATA_PORT); | ||
| 107 | if (!testmode) | ||
| 108 | { | ||
| 109 | unsigned pin_map; | ||
| 110 | |||
| 111 | outb_p(0xE6,IO_INDEX_PORT); | ||
| 112 | outb_p(0x0A,IO_DATA_PORT); | ||
| 113 | outb_p(0x2C,IO_INDEX_PORT); | ||
| 114 | pin_map = inb_p(IO_DATA_PORT); | ||
| 115 | pin_map |= 0x10; | ||
| 116 | pin_map &= ~(0x20); | ||
| 117 | outb_p(0x2C,IO_INDEX_PORT); | ||
| 118 | outb_p(pin_map,IO_DATA_PORT); | ||
| 119 | } | ||
| 120 | outb_p(0xE3,IO_INDEX_PORT); | ||
| 121 | outb_p(0x08,IO_DATA_PORT); | ||
| 122 | |||
| 123 | /* Set device Aux1 active */ | ||
| 124 | outb_p(0x30,IO_INDEX_PORT); | ||
| 125 | outb_p(0x01,IO_DATA_PORT); | ||
| 126 | |||
| 127 | /* Lock the SuperIO chip */ | ||
| 128 | outb_p(LOCK_DATA,IO_INDEX_PORT); | ||
| 129 | |||
| 130 | spin_unlock_irqrestore(&spinlock, flags); | ||
| 131 | |||
| 132 | printk(KERN_INFO PFX "activated.\n"); | ||
| 133 | |||
| 134 | return 0; | ||
| 135 | } | ||
| 136 | |||
| 137 | /* | ||
| 138 | * Stop the watchdog | ||
| 139 | */ | ||
| 140 | |||
| 141 | static int wdt_stop(void) | ||
| 142 | { | ||
| 143 | unsigned long flags; | ||
| 144 | |||
| 145 | spin_lock_irqsave(&spinlock, flags); | ||
| 146 | |||
| 147 | /* Unlock the SuperIO chip */ | ||
| 148 | outb_p(UNLOCK_DATA,IO_INDEX_PORT); | ||
| 149 | outb_p(UNLOCK_DATA,IO_INDEX_PORT); | ||
| 150 | |||
| 151 | /* | ||
| 152 | * Select device Aux2 (device=8) to set watchdog regs F2, F3 and F4. | ||
| 153 | * F2 is reset to its default value (watchdog timer disabled). | ||
| 154 | * F3 is reset to its default state. | ||
| 155 | * F4 clears the TIMEOUT'ed state (bit 0) - back to default. | ||
| 156 | */ | ||
| 157 | outb_p(DEVICE_REGISTER,IO_INDEX_PORT); | ||
| 158 | outb_p(0x08,IO_DATA_PORT); | ||
| 159 | outb_p(0xF2,IO_INDEX_PORT); | ||
| 160 | outb_p(0xFF,IO_DATA_PORT); | ||
| 161 | outb_p(0xF3,IO_INDEX_PORT); | ||
| 162 | outb_p(0x00,IO_DATA_PORT); | ||
| 163 | outb_p(0xF4,IO_INDEX_PORT); | ||
| 164 | outb_p(0x00,IO_DATA_PORT); | ||
| 165 | outb_p(0xF2,IO_INDEX_PORT); | ||
| 166 | outb_p(0x00,IO_DATA_PORT); | ||
| 167 | |||
| 168 | /* | ||
| 169 | * Select device Aux1 (dev=7) to set GP16 (in reg E6) and | ||
| 170 | * Gp13 (in reg E3) as inputs. | ||
| 171 | */ | ||
| 172 | outb_p(DEVICE_REGISTER,IO_INDEX_PORT); | ||
| 173 | outb_p(0x07,IO_DATA_PORT); | ||
| 174 | if (!testmode) | ||
| 175 | { | ||
| 176 | outb_p(0xE6,IO_INDEX_PORT); | ||
| 177 | outb_p(0x01,IO_DATA_PORT); | ||
| 178 | } | ||
| 179 | outb_p(0xE3,IO_INDEX_PORT); | ||
| 180 | outb_p(0x01,IO_DATA_PORT); | ||
| 181 | |||
| 182 | /* Lock the SuperIO chip */ | ||
| 183 | outb_p(LOCK_DATA,IO_INDEX_PORT); | ||
| 184 | |||
| 185 | spin_unlock_irqrestore(&spinlock, flags); | ||
| 186 | |||
| 187 | printk(KERN_INFO PFX "shutdown.\n"); | ||
| 188 | |||
| 189 | return 0; | ||
| 190 | } | ||
| 191 | |||
| 192 | /* | ||
| 193 | * Send a keepalive ping to the watchdog | ||
| 194 | * This is done by simply re-writing the timeout to reg. 0xF2 | ||
| 195 | */ | ||
| 196 | |||
| 197 | static int wdt_keepalive(void) | ||
| 198 | { | ||
| 199 | unsigned long flags; | ||
| 200 | |||
| 201 | spin_lock_irqsave(&spinlock, flags); | ||
| 202 | |||
| 203 | /* Unlock the SuperIO chip */ | ||
| 204 | outb_p(UNLOCK_DATA,IO_INDEX_PORT); | ||
| 205 | outb_p(UNLOCK_DATA,IO_INDEX_PORT); | ||
| 206 | |||
| 207 | /* Select device Aux2 (device=8) to kick watchdog reg F2 */ | ||
| 208 | outb_p(DEVICE_REGISTER,IO_INDEX_PORT); | ||
| 209 | outb_p(0x08,IO_DATA_PORT); | ||
| 210 | outb_p(0xF2,IO_INDEX_PORT); | ||
| 211 | outb_p(timeoutW,IO_DATA_PORT); | ||
| 212 | |||
| 213 | /* Lock the SuperIO chip */ | ||
| 214 | outb_p(LOCK_DATA,IO_INDEX_PORT); | ||
| 215 | |||
| 216 | spin_unlock_irqrestore(&spinlock, flags); | ||
| 217 | |||
| 218 | return 0; | ||
| 219 | } | ||
| 220 | |||
| 221 | /* | ||
| 222 | * Set the watchdog timeout value | ||
| 223 | */ | ||
| 224 | |||
| 225 | static int wdt_set_timeout(int t) | ||
| 226 | { | ||
| 227 | int tmrval; | ||
| 228 | |||
| 229 | /* | ||
| 230 | * Convert seconds to watchdog counter time units, rounding up. | ||
| 231 | * On PCM-5335 watchdog units are 30 seconds/step with 15 sec startup | ||
| 232 | * value. This information is supplied in the PCM-5335 manual and was | ||
| 233 | * checked by me on a real board. This is a bit strange because W83977f | ||
| 234 | * datasheet says counter unit is in minutes! | ||
| 235 | */ | ||
| 236 | if (t < 15) | ||
| 237 | return -EINVAL; | ||
| 238 | |||
| 239 | tmrval = ((t + 15) + 29) / 30; | ||
| 240 | |||
| 241 | if (tmrval > 255) | ||
| 242 | return -EINVAL; | ||
| 243 | |||
| 244 | /* | ||
| 245 | * timeout is the timeout in seconds, | ||
| 246 | * timeoutW is the timeout in watchdog counter units. | ||
| 247 | */ | ||
| 248 | timeoutW = tmrval; | ||
| 249 | timeout = (timeoutW * 30) - 15; | ||
| 250 | return 0; | ||
| 251 | } | ||
| 252 | |||
| 253 | /* | ||
| 254 | * Get the watchdog status | ||
| 255 | */ | ||
| 256 | |||
| 257 | static int wdt_get_status(int *status) | ||
| 258 | { | ||
| 259 | int new_status; | ||
| 260 | unsigned long flags; | ||
| 261 | |||
| 262 | spin_lock_irqsave(&spinlock, flags); | ||
| 263 | |||
| 264 | /* Unlock the SuperIO chip */ | ||
| 265 | outb_p(UNLOCK_DATA,IO_INDEX_PORT); | ||
| 266 | outb_p(UNLOCK_DATA,IO_INDEX_PORT); | ||
| 267 | |||
| 268 | /* Select device Aux2 (device=8) to read watchdog reg F4 */ | ||
| 269 | outb_p(DEVICE_REGISTER,IO_INDEX_PORT); | ||
| 270 | outb_p(0x08,IO_DATA_PORT); | ||
| 271 | outb_p(0xF4,IO_INDEX_PORT); | ||
| 272 | new_status = inb_p(IO_DATA_PORT); | ||
| 273 | |||
| 274 | /* Lock the SuperIO chip */ | ||
| 275 | outb_p(LOCK_DATA,IO_INDEX_PORT); | ||
| 276 | |||
| 277 | spin_unlock_irqrestore(&spinlock, flags); | ||
| 278 | |||
| 279 | *status = 0; | ||
| 280 | if (new_status & 1) | ||
| 281 | *status |= WDIOF_CARDRESET; | ||
| 282 | |||
| 283 | return 0; | ||
| 284 | } | ||
| 285 | |||
| 286 | |||
| 287 | /* | ||
| 288 | * /dev/watchdog handling | ||
| 289 | */ | ||
| 290 | |||
| 291 | static int wdt_open(struct inode *inode, struct file *file) | ||
| 292 | { | ||
| 293 | /* If the watchdog is alive we don't need to start it again */ | ||
| 294 | if( test_and_set_bit(0, &timer_alive) ) | ||
| 295 | return -EBUSY; | ||
| 296 | |||
| 297 | if (nowayout) | ||
| 298 | __module_get(THIS_MODULE); | ||
| 299 | |||
| 300 | wdt_start(); | ||
| 301 | return nonseekable_open(inode, file); | ||
| 302 | } | ||
| 303 | |||
| 304 | static int wdt_release(struct inode *inode, struct file *file) | ||
| 305 | { | ||
| 306 | /* | ||
| 307 | * Shut off the timer. | ||
| 308 | * Lock it in if it's a module and we set nowayout | ||
| 309 | */ | ||
| 310 | if (expect_close == 42) | ||
| 311 | { | ||
| 312 | wdt_stop(); | ||
| 313 | clear_bit(0, &timer_alive); | ||
| 314 | } else { | ||
| 315 | wdt_keepalive(); | ||
| 316 | printk(KERN_CRIT PFX "unexpected close, not stopping watchdog!\n"); | ||
| 317 | } | ||
| 318 | expect_close = 0; | ||
| 319 | return 0; | ||
| 320 | } | ||
| 321 | |||
| 322 | /* | ||
| 323 | * wdt_write: | ||
| 324 | * @file: file handle to the watchdog | ||
| 325 | * @buf: buffer to write (unused as data does not matter here | ||
| 326 | * @count: count of bytes | ||
| 327 | * @ppos: pointer to the position to write. No seeks allowed | ||
| 328 | * | ||
| 329 | * A write to a watchdog device is defined as a keepalive signal. Any | ||
| 330 | * write of data will do, as we we don't define content meaning. | ||
| 331 | */ | ||
| 332 | |||
| 333 | static ssize_t wdt_write(struct file *file, const char __user *buf, | ||
| 334 | size_t count, loff_t *ppos) | ||
| 335 | { | ||
| 336 | /* See if we got the magic character 'V' and reload the timer */ | ||
| 337 | if(count) | ||
| 338 | { | ||
| 339 | if (!nowayout) | ||
| 340 | { | ||
| 341 | size_t ofs; | ||
| 342 | |||
| 343 | /* note: just in case someone wrote the magic character long ago */ | ||
| 344 | expect_close = 0; | ||
| 345 | |||
| 346 | /* scan to see whether or not we got the magic character */ | ||
| 347 | for(ofs = 0; ofs != count; ofs++) | ||
| 348 | { | ||
| 349 | char c; | ||
| 350 | if (get_user(c, buf + ofs)) | ||
| 351 | return -EFAULT; | ||
| 352 | if (c == 'V') { | ||
| 353 | expect_close = 42; | ||
| 354 | } | ||
| 355 | } | ||
| 356 | } | ||
| 357 | |||
| 358 | /* someone wrote to us, we should restart timer */ | ||
| 359 | wdt_keepalive(); | ||
| 360 | } | ||
| 361 | return count; | ||
| 362 | } | ||
| 363 | |||
| 364 | /* | ||
| 365 | * wdt_ioctl: | ||
| 366 | * @inode: inode of the device | ||
| 367 | * @file: file handle to the device | ||
| 368 | * @cmd: watchdog command | ||
| 369 | * @arg: argument pointer | ||
| 370 | * | ||
| 371 | * The watchdog API defines a common set of functions for all watchdogs | ||
| 372 | * according to their available features. | ||
| 373 | */ | ||
| 374 | |||
| 375 | static struct watchdog_info ident = { | ||
| 376 | .options = WDIOF_SETTIMEOUT | WDIOF_MAGICCLOSE | WDIOF_KEEPALIVEPING, | ||
| 377 | .firmware_version = 1, | ||
| 378 | .identity = WATCHDOG_NAME, | ||
| 379 | }; | ||
| 380 | |||
| 381 | static int wdt_ioctl(struct inode *inode, struct file *file, | ||
| 382 | unsigned int cmd, unsigned long arg) | ||
| 383 | { | ||
| 384 | int status; | ||
| 385 | int new_options, retval = -EINVAL; | ||
| 386 | int new_timeout; | ||
| 387 | union { | ||
| 388 | struct watchdog_info __user *ident; | ||
| 389 | int __user *i; | ||
| 390 | } uarg; | ||
| 391 | |||
| 392 | uarg.i = (int __user *)arg; | ||
| 393 | |||
| 394 | switch(cmd) | ||
| 395 | { | ||
| 396 | default: | ||
| 397 | return -ENOIOCTLCMD; | ||
| 398 | |||
| 399 | case WDIOC_GETSUPPORT: | ||
| 400 | return copy_to_user(uarg.ident, &ident, sizeof(ident)) ? -EFAULT : 0; | ||
| 401 | |||
| 402 | case WDIOC_GETSTATUS: | ||
| 403 | wdt_get_status(&status); | ||
| 404 | return put_user(status, uarg.i); | ||
| 405 | |||
| 406 | case WDIOC_GETBOOTSTATUS: | ||
| 407 | return put_user(0, uarg.i); | ||
| 408 | |||
| 409 | case WDIOC_KEEPALIVE: | ||
| 410 | wdt_keepalive(); | ||
| 411 | return 0; | ||
| 412 | |||
| 413 | case WDIOC_SETOPTIONS: | ||
| 414 | if (get_user (new_options, uarg.i)) | ||
| 415 | return -EFAULT; | ||
| 416 | |||
| 417 | if (new_options & WDIOS_DISABLECARD) { | ||
| 418 | wdt_stop(); | ||
| 419 | retval = 0; | ||
| 420 | } | ||
| 421 | |||
| 422 | if (new_options & WDIOS_ENABLECARD) { | ||
| 423 | wdt_start(); | ||
| 424 | retval = 0; | ||
| 425 | } | ||
| 426 | |||
| 427 | return retval; | ||
| 428 | |||
| 429 | case WDIOC_SETTIMEOUT: | ||
| 430 | if (get_user(new_timeout, uarg.i)) | ||
| 431 | return -EFAULT; | ||
| 432 | |||
| 433 | if (wdt_set_timeout(new_timeout)) | ||
| 434 | return -EINVAL; | ||
| 435 | |||
| 436 | wdt_keepalive(); | ||
| 437 | /* Fall */ | ||
| 438 | |||
| 439 | case WDIOC_GETTIMEOUT: | ||
| 440 | return put_user(timeout, uarg.i); | ||
| 441 | |||
| 442 | } | ||
| 443 | } | ||
| 444 | |||
| 445 | static int wdt_notify_sys(struct notifier_block *this, unsigned long code, | ||
| 446 | void *unused) | ||
| 447 | { | ||
| 448 | if (code==SYS_DOWN || code==SYS_HALT) | ||
| 449 | wdt_stop(); | ||
| 450 | return NOTIFY_DONE; | ||
| 451 | } | ||
| 452 | |||
| 453 | static struct file_operations wdt_fops= | ||
| 454 | { | ||
| 455 | .owner = THIS_MODULE, | ||
| 456 | .llseek = no_llseek, | ||
| 457 | .write = wdt_write, | ||
| 458 | .ioctl = wdt_ioctl, | ||
| 459 | .open = wdt_open, | ||
| 460 | .release = wdt_release, | ||
| 461 | }; | ||
| 462 | |||
| 463 | static struct miscdevice wdt_miscdev= | ||
| 464 | { | ||
| 465 | .minor = WATCHDOG_MINOR, | ||
| 466 | .name = "watchdog", | ||
| 467 | .fops = &wdt_fops, | ||
| 468 | }; | ||
| 469 | |||
| 470 | static struct notifier_block wdt_notifier = { | ||
| 471 | .notifier_call = wdt_notify_sys, | ||
| 472 | }; | ||
| 473 | |||
| 474 | static int __init w83977f_wdt_init(void) | ||
| 475 | { | ||
| 476 | int rc; | ||
| 477 | |||
| 478 | printk(KERN_INFO PFX DRIVER_VERSION); | ||
| 479 | |||
| 480 | spin_lock_init(&spinlock); | ||
| 481 | |||
| 482 | /* | ||
| 483 | * Check that the timeout value is within it's range ; | ||
| 484 | * if not reset to the default | ||
| 485 | */ | ||
| 486 | if (wdt_set_timeout(timeout)) { | ||
| 487 | wdt_set_timeout(DEFAULT_TIMEOUT); | ||
| 488 | printk(KERN_INFO PFX "timeout value must be 15<=timeout<=7635, using %d\n", | ||
| 489 | DEFAULT_TIMEOUT); | ||
| 490 | } | ||
| 491 | |||
| 492 | if (!request_region(IO_INDEX_PORT, 2, WATCHDOG_NAME)) | ||
| 493 | { | ||
| 494 | printk(KERN_ERR PFX "I/O address 0x%04x already in use\n", | ||
| 495 | IO_INDEX_PORT); | ||
| 496 | rc = -EIO; | ||
| 497 | goto err_out; | ||
| 498 | } | ||
| 499 | |||
| 500 | rc = misc_register(&wdt_miscdev); | ||
| 501 | if (rc) | ||
| 502 | { | ||
| 503 | printk(KERN_ERR PFX "cannot register miscdev on minor=%d (err=%d)\n", | ||
| 504 | wdt_miscdev.minor, rc); | ||
| 505 | goto err_out_region; | ||
| 506 | } | ||
| 507 | |||
| 508 | rc = register_reboot_notifier(&wdt_notifier); | ||
| 509 | if (rc) | ||
| 510 | { | ||
| 511 | printk(KERN_ERR PFX "cannot register reboot notifier (err=%d)\n", | ||
| 512 | rc); | ||
| 513 | goto err_out_miscdev; | ||
| 514 | } | ||
| 515 | |||
| 516 | printk(KERN_INFO PFX "initialized. timeout=%d sec (nowayout=%d testmode=%d)\n", | ||
| 517 | timeout, nowayout, testmode); | ||
| 518 | |||
| 519 | return 0; | ||
| 520 | |||
| 521 | err_out_miscdev: | ||
| 522 | misc_deregister(&wdt_miscdev); | ||
| 523 | err_out_region: | ||
| 524 | release_region(IO_INDEX_PORT,2); | ||
| 525 | err_out: | ||
| 526 | return rc; | ||
| 527 | } | ||
| 528 | |||
| 529 | static void __exit w83977f_wdt_exit(void) | ||
| 530 | { | ||
| 531 | wdt_stop(); | ||
| 532 | misc_deregister(&wdt_miscdev); | ||
| 533 | unregister_reboot_notifier(&wdt_notifier); | ||
| 534 | release_region(IO_INDEX_PORT,2); | ||
| 535 | } | ||
| 536 | |||
| 537 | module_init(w83977f_wdt_init); | ||
| 538 | module_exit(w83977f_wdt_exit); | ||
| 539 | |||
| 540 | MODULE_AUTHOR("Jose Goncalves <jose.goncalves@inov.pt>"); | ||
| 541 | MODULE_DESCRIPTION("Driver for watchdog timer in W83977F I/O chip"); | ||
| 542 | MODULE_LICENSE("GPL"); | ||
| 543 | MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR); | ||
diff --git a/include/asm-ppc/mv64x60.h b/include/asm-ppc/mv64x60.h index 835930d6faa1..75c2ffa26b26 100644 --- a/include/asm-ppc/mv64x60.h +++ b/include/asm-ppc/mv64x60.h | |||
| @@ -119,6 +119,14 @@ extern spinlock_t mv64x60_lock; | |||
| 119 | 119 | ||
| 120 | #define MV64x60_64BIT_WIN_COUNT 24 | 120 | #define MV64x60_64BIT_WIN_COUNT 24 |
| 121 | 121 | ||
| 122 | /* Watchdog Platform Device, Driver Data */ | ||
| 123 | #define MV64x60_WDT_NAME "wdt" | ||
| 124 | |||
| 125 | struct mv64x60_wdt_pdata { | ||
| 126 | int timeout; /* watchdog expiry in seconds, default 10 */ | ||
| 127 | int bus_clk; /* bus clock in MHz, default 133 */ | ||
| 128 | }; | ||
| 129 | |||
| 122 | /* | 130 | /* |
| 123 | * Define a structure that's used to pass in config information to the | 131 | * Define a structure that's used to pass in config information to the |
| 124 | * core routines. | 132 | * core routines. |
