GIT e386099f4842c9f72f3e79ec5a0746fbb5b9bfa5 git+ssh://master.kernel.org/pub/scm/linux/kernel/git/wim/linux-2.6-watchdog-mm.git commit e386099f4842c9f72f3e79ec5a0746fbb5b9bfa5 Author: Wim Van Sebroeck Date: Wed Sep 13 21:27:29 2006 +0200 [WATCHDOG] use ENOTTY instead of ENOIOCTLCMD in ioctl() Return ENOTTY instead of ENOIOCTLCMD in user-visible ioctl() results The watchdog drivers used to return ENOIOCTLCMD for bad ioctl() commands. ENOIOCTLCMD should not be visible by the user, so use ENOTTY instead. Signed-off-by: Samuel Tardieu Signed-off-by: Wim Van Sebroeck Acked-by: Alan Cox Signed-off-by: Andrew Morton commit 3245d743e63a548773c54764fdf3832770f52bfd Author: Samuel Tardieu Date: Sat Sep 9 17:34:31 2006 +0200 [WATCHDOG] use ENOTTY instead of ENOIOCTLCMD in ioctl() Return ENOTTY instead of ENOIOCTLCMD in user-visible ioctl() results The watchdog drivers used to return ENOIOCTLCMD for bad ioctl() commands. ENOIOCTLCMD should not be visible by the user, so use ENOTTY instead. Signed-off-by: Samuel Tardieu Signed-off-by: Wim Van Sebroeck Acked-by: Alan Cox Signed-off-by: Andrew Morton commit 5c4968f163cd7362c88401a990db96378b5f9587 Author: Vitaly Wool Date: Mon Sep 11 14:42:39 2006 +0400 [WATCHDOG] pnx4008: add cpu_relax() Added cpu_relax as suggested by Alan Cox. Signed-off-by: Vitaly Wool Signed-off-by: Wim Van Sebroeck commit 1df90062cf9d0176e14eae2805b3d0ff434af19d Author: Wim Van Sebroeck Date: Sun Sep 10 12:48:15 2006 +0200 [WATCHDOG] pnx4008_wdt.c - spinlock fixes. Add io spinlocks to prevent possible race conditions between start and stop operations that are issued from different child processes where the master process opened /dev/watchdog. Signed-off-by: Wim Van Sebroeck commit 1a4b73eaf3b706c3768c99f41c54383b34d0374e Author: Ben Dooks Date: Wed Sep 6 12:24:35 2006 +0100 [WATCHDOG] s3c24XX nowayout If the driver is not configured for `no way out`, then the open method should not automatically allow the setting of allow_close to CLOSE_STATE_ALLOW. The setting of allow_close nullifies the use of the magic close via the write path. It means that in the default state, the watchdog will shut-down even if the magic close has not been issued. Signed-off-by: Ben Dooks commit 2cafe468176ee35eeab77491a6c31e58c694e1b8 Author: Wim Van Sebroeck Date: Sat Sep 2 20:53:19 2006 +0200 [WATCHDOG] Winbond SMsC37B787 watchdog fixes * Added io spinlocking * Deleted WATCHDOG_MINOR (it's in the miscdevice include * Changed timer_enabled to use set_bit functions * WDIOC_GETSUPPORT should return -EFAULT or 0 * timeout should be correct before we initialize the watchdog * we should initialize the watchdog before we give access to userspace * Third parameter of module_param is not the default or initial value Signed-off-by: Wim Van Sebroeck commit 7889e72c5e8d9ab9e773637f0b084a28f9347d1f Author: Wim Van Sebroeck Date: Sat Sep 2 19:32:26 2006 +0200 [WATCHDOG] Winbond SMsC37B787 - remove trailing whitespace Remove trailing whitespace. Signed-off-by: Wim Van Sebroeck commit e9095936fcd85894c9701c62222f84d0b12647af Author: Sven Anders Date: Thu Aug 24 17:11:50 2006 +0200 [WATCHDOG] Winbond SMsC37B787 watchdog driver New watchdog driver for the Winbond SMsC37B787 chipset. Signed-off-by: Sven Anders Signed-off-by: Wim Van Sebroeck commit ab11b32c940490f2d2d3c4f57d71308845f6ad6b Author: Wim Van Sebroeck Date: Sat Sep 2 19:04:02 2006 +0200 [WATCHDOG] Kconfig clean up fixed some more trailing spaces. Signed-off-by: Wim Van Sebroeck commit e61724ff112251bb30b257e023469e8ed746a820 Author: Wim Van Sebroeck Date: Sat Sep 2 18:50:20 2006 +0200 [WATCHDOG] w836?7hf_wdt spinlock fixes. Add io spinlocks to prevent possible race conditions between start and stop operations that are issued from different child processes where the master process opened /dev/watchdog. Signed-off-by: Wim Van Sebroeck commit 257543ccc2edc7a782b9572be961129a306946d1 Author: Wim Van Sebroeck Date: Sat Sep 2 17:59:54 2006 +0200 [WATCHDOG] Kconfig clean-up * fix typo's according to spellings checker * Fix some leading and trailing spaces Signed-off-by: Wim Van Sebroeck commit d19ea38e6e99c4924c894cb54440e242179bf27d Author: Marcus Junker Date: Thu Aug 24 17:11:50 2006 +0200 [WATCHDOG] w83697hf WDT driver New watchdog driver for the Winbond W83697HF chipset. Signed-off-by: Marcus Junker Signed-off-by: Wim Van Sebroeck commit 01f8698e9a9983d5206dbd29a4fb0b89e3570b57 Author: Wim Van Sebroeck Date: Sun Jul 30 20:06:07 2006 +0200 [WATCHDOG] pnx4008_wdt.c - remove patch Change remove code so that we first detach the driver from userspace, then clean up the clock and then clean up the memory we allocated. Signed-off-by: Wim Van Sebroeck commit 0bd9c842cc6c779a29c12aef0ef521b61ae7bdf5 Author: Wim Van Sebroeck Date: Mon Jul 3 09:03:47 2006 +0200 [WATCHDOG] pnx4008_wdt.c - nowayout patch Change nowayout to: WATCHDOG_NOWAYOUT as defined in include/linux/watchdog.h . Signed-off-by: Wim Van Sebroeck commit 34150b94c2e7e3177803b51e86077acf3a0750b5 Author: Vitaly Wool Date: Mon Jun 26 19:31:49 2006 +0400 [WATCHDOG] pnx4008: add watchdog support Add watchdog support for Philips PNX4008 ARM board inlined. Signed-off-by: Vitaly Wool Signed-off-by: Wim Van Sebroeck commit c9c87d841efdf38d1f3cf96f35d5106170ba0b68 Author: Wim Van Sebroeck Date: Sat Aug 5 20:59:01 2006 +0200 [WATCHDOG] iTCO_wdt.c shutdown patch Since we are using the device driver model, we don't need to arrange the shutdown via a reboot_notifier. Signed-off-by: Wim Van Sebroeck commit 0af4a4b7d890331984a4b48517710f6a46d4a8a4 Author: Wim Van Sebroeck Date: Wed Jul 19 22:39:13 2006 +0200 [WATCHDOG] iTCO_wdt.c - pci_dev_put fix for_each_pci_dev calls pci_get_device (and thus it calls pci_dev_get). So we need to do a pci_dev_put to keep the refcounting correct. (Thanks to Jiri Slaby ) Signed-off-by: Wim Van Sebroeck commit bccea7eda3b84bb8652cf1228940877b48aca00d Author: Wim Van Sebroeck Date: Fri Jun 30 08:44:53 2006 +0200 [WATCHDOG] iTCO_wdt (Intel TCO Timer) driver Convert the iTCO_wdt driver to a platform device driver. Signed-off-by: Wim Van Sebroeck commit cf30225b01ff1c7d07ebc7905e98affe12de7b1c Author: Wim Van Sebroeck Date: Sun May 21 14:37:44 2006 +0200 [WATCHDOG] iTCO_wdt (Intel TCO Timer) driver Hardware driver for the intel TCO timer based watchdog devices. These drivers are included in the Intel 82801 I/O Controller Hub family (from ICH0 up to ICH7) and in the Intel 6300ESB controller hub. This driver will replace the i8xx_tco.c driver. Signed-off-by: Wim Van Sebroeck commit 94f9d9566d8045a89850ea8398363e94b0501260 Author: Dave Jones Date: Tue Aug 1 20:06:43 2006 +0200 [WATCHDOG] improve machzwd detection On a machine with no machzwd, loading the module prints out.. machzwd: MachZ ZF-Logic Watchdog driver initializing. 0xffff machzwd: Watchdog using action = RESET - the 0xffff printk is unnecessary - 0xffff seems to be 'hardware not present' - fix CodingStyle. (This driver could use some more work here) Signed-off-by: Dave Jones Signed-off-by: Wim Van Sebroeck Signed-off-by: Andrew Morton commit aac7ba145ede567197caf4c17ece7886694a8e8e Author: Jiri Slaby Date: Wed Jul 19 02:18:23 2006 +0159 [WATCHDOG] i8xx_tco remove pci_find_device. Use refcounting for pci device obtaining. Use PCI_DEVICE macro. Signed-off-by: Jiri Slaby Signed-off-by: Wim Van Sebroeck Cc: Andrew Morton commit 93ae7d9b94f591c4975ac42453feaea00760e5e5 Author: Jiri Slaby Date: Tue Jul 18 18:29:00 2006 +0159 [WATCHDOG] alim remove pci_find_device Convert pci_find_device to pci_get_device + pci_dev_put in alim watchdog cards' drivers (refcounting). Signed-off-by: Jiri Slaby Signed-off-by: Wim Van Sebroeck Signed-off-by: Andrew Morton commit ecd22ffa7256ed3479e909ceb079f805d8a1593b Author: Ian E. Morgan Date: Tue Aug 29 13:06:46 2006 -0400 [WATCHDOG] wbc8360.c module parameter patch The last argument of module_param is permissions, not default value. From: "Ian E. Morgan" Signed-off-by: Wim Van Sebroeck Signed-off-by: Andrew Morton arch/arm/mach-pnx4008/clock.c | 11 drivers/char/watchdog/Kconfig | 82 +++- drivers/char/watchdog/Makefile | 4 drivers/char/watchdog/acquirewdt.c | 2 drivers/char/watchdog/advantechwdt.c | 2 drivers/char/watchdog/alim1535_wdt.c | 12 - drivers/char/watchdog/alim7101_wdt.c | 17 - drivers/char/watchdog/at91_wdt.c | 2 drivers/char/watchdog/booke_wdt.c | 2 drivers/char/watchdog/cpu5wdt.c | 2 drivers/char/watchdog/ep93xx_wdt.c | 2 drivers/char/watchdog/eurotechwdt.c | 2 drivers/char/watchdog/i6300esb.c | 2 drivers/char/watchdog/i8xx_tco.c | 35 +- drivers/char/watchdog/iTCO_wdt.c | 735 ++++++++++++++++++++++++++++++++ drivers/char/watchdog/ib700wdt.c | 2 drivers/char/watchdog/ibmasr.c | 2 drivers/char/watchdog/indydog.c | 2 drivers/char/watchdog/ixp2000_wdt.c | 2 drivers/char/watchdog/ixp4xx_wdt.c | 2 drivers/char/watchdog/machzwd.c | 5 drivers/char/watchdog/mixcomwd.c | 2 drivers/char/watchdog/mpc83xx_wdt.c | 2 drivers/char/watchdog/mpc8xx_wdt.c | 2 drivers/char/watchdog/mpcore_wdt.c | 4 drivers/char/watchdog/mv64x60_wdt.c | 2 drivers/char/watchdog/pcwd.c | 2 drivers/char/watchdog/pcwd_pci.c | 2 drivers/char/watchdog/pcwd_usb.c | 2 drivers/char/watchdog/pnx4008_wdt.c | 362 ++++++++++++++++ drivers/char/watchdog/s3c2410_wdt.c | 12 - drivers/char/watchdog/sa1100_wdt.c | 2 drivers/char/watchdog/sbc60xxwdt.c | 2 drivers/char/watchdog/sbc_epx_c3.c | 2 drivers/char/watchdog/sc1200wdt.c | 2 drivers/char/watchdog/sc520_wdt.c | 2 drivers/char/watchdog/scx200_wdt.c | 2 drivers/char/watchdog/shwdt.c | 2 drivers/char/watchdog/smsc37b787_wdt.c | 627 +++++++++++++++++++++++++++ drivers/char/watchdog/softdog.c | 2 drivers/char/watchdog/w83627hf_wdt.c | 10 drivers/char/watchdog/w83697hf_wdt.c | 375 ++++++++++++++++ drivers/char/watchdog/w83877f_wdt.c | 2 drivers/char/watchdog/w83977f_wdt.c | 2 drivers/char/watchdog/wafer5823wdt.c | 2 drivers/char/watchdog/wdrtas.c | 2 drivers/char/watchdog/wdt.c | 2 drivers/char/watchdog/wdt285.c | 2 drivers/char/watchdog/wdt977.c | 2 drivers/char/watchdog/wdt_pci.c | 2 include/asm-arm/arch-pnx4008/clock.h | 1 51 files changed, 2282 insertions(+), 82 deletions(-) diff --git a/arch/arm/mach-pnx4008/clock.c b/arch/arm/mach-pnx4008/clock.c index f582ed2..daa8d3d 100644 --- a/arch/arm/mach-pnx4008/clock.c +++ b/arch/arm/mach-pnx4008/clock.c @@ -735,6 +735,16 @@ static struct clk uart6_ck = { .enable_reg = UARTCLKCTRL_REG, }; +static struct clk wdt_ck = { + .name = "wdt_ck", + .parent = &per_ck, + .flags = NEEDS_INITIALIZATION, + .round_rate = &on_off_round_rate, + .set_rate = &on_off_set_rate, + .enable_shift = 0, + .enable_reg = TIMCLKCTRL_REG, +}; + /* These clocks are visible outside this module * and can be initialized */ @@ -765,6 +775,7 @@ static struct clk *onchip_clks[] = { &uart4_ck, &uart5_ck, &uart6_ck, + &wdt_ck, }; static int local_clk_enable(struct clk *clk) diff --git a/drivers/char/watchdog/Kconfig b/drivers/char/watchdog/Kconfig index fff89c2..4bed625 100644 --- a/drivers/char/watchdog/Kconfig +++ b/drivers/char/watchdog/Kconfig @@ -13,7 +13,7 @@ config WATCHDOG subsequently opening the file and then failing to write to it for longer than 1 minute will result in rebooting the machine. This could be useful for a networked machine that needs to come back - online as fast as possible after a lock-up. There's both a watchdog + on-line as fast as possible after a lock-up. There's both a watchdog implementation entirely in software (which can sometimes fail to reboot the machine) and a driver for hardware watchdog boards, which are more robust and can also keep track of the temperature inside @@ -71,7 +71,7 @@ config 21285_WATCHDOG tristate "DC21285 watchdog" depends on WATCHDOG && FOOTBRIDGE help - The Intel Footbridge chip contains a builtin watchdog circuit. Say Y + The Intel Footbridge chip contains a built-in watchdog circuit. Say Y here if you wish to use this. Alternatively say M to compile the driver as a module, which will be called wdt285. @@ -165,6 +165,17 @@ config EP93XX_WATCHDOG To compile this driver as a module, choose M here: the module will be called ep93xx_wdt. +config PNX4008_WATCHDOG + tristate "PNX4008 Watchdog" + depends on WATCHDOG && ARCH_PNX4008 + help + Say Y here if to include support for the watchdog timer + in the PNX4008 processor. + This driver can be built as a module by choosing M. The module + will be called pnx4008_wdt. + + Say N if you are unsure. + # X86 (i386 + ia64 + x86_64) Architecture config ACQUIRE_WDT @@ -251,11 +262,11 @@ config IB700_WDT Most people will say N. config IBMASR - tristate "IBM Automatic Server Restart" - depends on WATCHDOG && X86 - help + tristate "IBM Automatic Server Restart" + depends on WATCHDOG && X86 + help This is the driver for the IBM Automatic Server Restart watchdog - timer builtin into some eServer xSeries machines. + timer built-in into some eServer xSeries machines. To compile this driver as a module, choose M here: the module will be called ibmasr. @@ -298,6 +309,27 @@ config I8XX_TCO To compile this driver as a module, choose M here: the module will be called i8xx_tco. +config ITCO_WDT + tristate "Intel TCO Timer/Watchdog (EXPERIMENTAL)" + depends on WATCHDOG && (X86 || IA64) && PCI && EXPERIMENTAL + ---help--- + Hardware driver for the intel TCO timer based watchdog devices. + These drivers are included in the Intel 82801 I/O Controller + Hub family 'from ICH0 up to ICH7) and in the Intel 6300ESB + controller hub. + + The TCO (Total Cost of Ownership) timer is a watchdog timer + that will reboot the machine after its second expiration. The + expiration time can be configured with the "heartbeat" parameter. + + On some motherboards the driver may fail to reset the chipset's + NO_REBOOT flag which prevents the watchdog from rebooting the + machine. If this is the case you will get a kernel message like + "failed to reset NO_REBOOT flag, reboot disabled by hardware". + + To compile this driver as a module, choose M here: the + module will be called iTCO_wdt. + config SC1200_WDT tristate "National Semiconductor PC87307/PC97307 (ala SC1200) Watchdog" depends on WATCHDOG && X86 @@ -356,6 +388,26 @@ config CPU5_WDT To compile this driver as a module, choose M here: the module will be called cpu5wdt. +config SMSC37B787_WDT + tristate "Winbond SMsC37B787 Watchdog Timer" + depends on WATCHDOG && X86 + ---help--- + This is the driver for the hardware watchdog component on the + Winbond SMsC37B787 chipset as used on the NetRunner Mainboard + from Vision Systems and maybe others. + + This watchdog simply watches your kernel to make sure it doesn't + freeze, and if it does, it reboots your computer after a certain + amount of time. + + Usually a userspace daemon will notify the kernel WDT driver that + userspace is still alive, at regular intervals. + + To compile this driver as a module, choose M here: the + module will be called smsc37b787_wdt. + + Most people will say N. + config W83627HF_WDT tristate "W83627HF Watchdog Timer" depends on WATCHDOG && X86 @@ -371,6 +423,19 @@ config W83627HF_WDT Most people will say N. +config W83697HF_WDT + tristate "W83697HF Watchdog Timer" + depends on WATCHDOG && X86 + ---help--- + This is the driver for the hardware watchdog on the W83697HF chipset + This watchdog simply watches your kernel to make sure it doesn't freeze, + and if it does, it reboots your computer after a certain amount of time. + + To compile this driver as a module, choose M here: the + module will be called w83697hf_wdt. + + Most people will say N. + config W83877F_WDT tristate "W83877F (EMACS) Watchdog Timer" depends on WATCHDOG && X86 @@ -404,7 +469,7 @@ config MACHZ_WDT depends on WATCHDOG && X86 ---help--- If you are using a ZF Micro MachZ processor, say Y here, otherwise - N. This is the driver for the watchdog timer builtin on that + N. This is the driver for the watchdog timer built-in on that processor using ZF-Logic interface. This watchdog simply watches your kernel to make sure it doesn't freeze, and if it does, it reboots your computer after a certain amount of time. @@ -433,7 +498,6 @@ config SBC_EPX_C3_WATCHDOG To compile this driver as a module, choose M here: the module will be called sbc_epx_c3. - # PowerPC Architecture config 8xx_WDT @@ -463,7 +527,7 @@ config WATCHDOG_RTAS help This driver adds watchdog support for the RTAS watchdog. - To compile this driver as a module, choose M here. The module + To compile this driver as a module, choose M here. The module will be called wdrtas. # MIPS Architecture diff --git a/drivers/char/watchdog/Makefile b/drivers/char/watchdog/Makefile index 6ab77b6..2e8cd1e 100644 --- a/drivers/char/watchdog/Makefile +++ b/drivers/char/watchdog/Makefile @@ -32,6 +32,7 @@ obj-$(CONFIG_S3C2410_WATCHDOG) += s3c241 obj-$(CONFIG_SA1100_WATCHDOG) += sa1100_wdt.o obj-$(CONFIG_MPCORE_WATCHDOG) += mpcore_wdt.o obj-$(CONFIG_EP93XX_WATCHDOG) += ep93xx_wdt.o +obj-$(CONFIG_PNX4008_WATCHDOG) += pnx4008_wdt.o # X86 (i386 + ia64 + x86_64) Architecture obj-$(CONFIG_ACQUIRE_WDT) += acquirewdt.o @@ -45,12 +46,15 @@ obj-$(CONFIG_IBMASR) += ibmasr.o obj-$(CONFIG_WAFER_WDT) += wafer5823wdt.o obj-$(CONFIG_I6300ESB_WDT) += i6300esb.o obj-$(CONFIG_I8XX_TCO) += i8xx_tco.o +obj-$(CONFIG_ITCO_WDT) += iTCO_wdt.o obj-$(CONFIG_SC1200_WDT) += sc1200wdt.o obj-$(CONFIG_SCx200_WDT) += scx200_wdt.o obj-$(CONFIG_60XX_WDT) += sbc60xxwdt.o obj-$(CONFIG_SBC8360_WDT) += sbc8360.o obj-$(CONFIG_CPU5_WDT) += cpu5wdt.o +obj-$(CONFIG_SMSC37B787_WDT) += smsc37b787_wdt.o obj-$(CONFIG_W83627HF_WDT) += w83627hf_wdt.o +obj-$(CONFIG_W83697HF_WDT) += w83697hf_wdt.o obj-$(CONFIG_W83877F_WDT) += w83877f_wdt.o obj-$(CONFIG_W83977F_WDT) += w83977f_wdt.o obj-$(CONFIG_MACHZ_WDT) += machzwd.o diff --git a/drivers/char/watchdog/acquirewdt.c b/drivers/char/watchdog/acquirewdt.c index c77fe3c..154d67e 100644 --- a/drivers/char/watchdog/acquirewdt.c +++ b/drivers/char/watchdog/acquirewdt.c @@ -183,7 +183,7 @@ static int acq_ioctl(struct inode *inode } default: - return -ENOIOCTLCMD; + return -ENOTTY; } } diff --git a/drivers/char/watchdog/advantechwdt.c b/drivers/char/watchdog/advantechwdt.c index 8069be4..9d73276 100644 --- a/drivers/char/watchdog/advantechwdt.c +++ b/drivers/char/watchdog/advantechwdt.c @@ -176,7 +176,7 @@ advwdt_ioctl(struct inode *inode, struct } default: - return -ENOIOCTLCMD; + return -ENOTTY; } return 0; } diff --git a/drivers/char/watchdog/alim1535_wdt.c b/drivers/char/watchdog/alim1535_wdt.c index c5c94e4..01b0d13 100644 --- a/drivers/char/watchdog/alim1535_wdt.c +++ b/drivers/char/watchdog/alim1535_wdt.c @@ -236,7 +236,7 @@ static int ali_ioctl(struct inode *inode return put_user(timeout, p); default: - return -ENOIOCTLCMD; + return -ENOTTY; } } @@ -330,17 +330,20 @@ static int __init ali_find_watchdog(void u32 wdog; /* Check for a 1535 series bridge */ - pdev = pci_find_device(PCI_VENDOR_ID_AL, 0x1535, NULL); + pdev = pci_get_device(PCI_VENDOR_ID_AL, 0x1535, NULL); if(pdev == NULL) return -ENODEV; + pci_dev_put(pdev); /* Check for the a 7101 PMU */ - pdev = pci_find_device(PCI_VENDOR_ID_AL, 0x7101, NULL); + pdev = pci_get_device(PCI_VENDOR_ID_AL, 0x7101, NULL); if(pdev == NULL) return -ENODEV; - if(pci_enable_device(pdev)) + if(pci_enable_device(pdev)) { + pci_dev_put(pdev); return -EIO; + } ali_pci = pdev; @@ -447,6 +450,7 @@ static void __exit watchdog_exit(void) /* Deregister */ unregister_reboot_notifier(&ali_notifier); misc_deregister(&ali_miscdev); + pci_dev_put(ali_pci); } module_init(watchdog_init); diff --git a/drivers/char/watchdog/alim7101_wdt.c b/drivers/char/watchdog/alim7101_wdt.c index ffd7684..5948863 100644 --- a/drivers/char/watchdog/alim7101_wdt.c +++ b/drivers/char/watchdog/alim7101_wdt.c @@ -277,7 +277,7 @@ static int fop_ioctl(struct inode *inode case WDIOC_GETTIMEOUT: return put_user(timeout, p); default: - return -ENOIOCTLCMD; + return -ENOTTY; } } @@ -333,6 +333,7 @@ static void __exit alim7101_wdt_unload(v /* Deregister */ misc_deregister(&wdt_miscdev); unregister_reboot_notifier(&wdt_notifier); + pci_dev_put(alim7101_pmu); } static int __init alim7101_wdt_init(void) @@ -342,7 +343,8 @@ static int __init alim7101_wdt_init(void char tmp; printk(KERN_INFO PFX "Steve Hill .\n"); - alim7101_pmu = pci_find_device(PCI_VENDOR_ID_AL, PCI_DEVICE_ID_AL_M7101,NULL); + alim7101_pmu = pci_get_device(PCI_VENDOR_ID_AL, PCI_DEVICE_ID_AL_M7101, + NULL); if (!alim7101_pmu) { printk(KERN_INFO PFX "ALi M7101 PMU not present - WDT not set\n"); return -EBUSY; @@ -351,21 +353,23 @@ static int __init alim7101_wdt_init(void /* Set the WDT in the PMU to 1 second */ pci_write_config_byte(alim7101_pmu, ALI_7101_WDT, 0x02); - ali1543_south = pci_find_device(PCI_VENDOR_ID_AL, PCI_DEVICE_ID_AL_M1533, NULL); + ali1543_south = pci_get_device(PCI_VENDOR_ID_AL, PCI_DEVICE_ID_AL_M1533, + NULL); if (!ali1543_south) { printk(KERN_INFO PFX "ALi 1543 South-Bridge not present - WDT not set\n"); - return -EBUSY; + goto err_out; } pci_read_config_byte(ali1543_south, 0x5e, &tmp); + pci_dev_put(ali1543_south); if ((tmp & 0x1e) == 0x00) { if (!use_gpio) { printk(KERN_INFO PFX "Detected old alim7101 revision 'a1d'. If this is a cobalt board, set the 'use_gpio' module parameter.\n"); - return -EBUSY; + goto err_out; } nowayout = 1; } else if ((tmp & 0x1e) != 0x12 && (tmp & 0x1e) != 0x00) { printk(KERN_INFO PFX "ALi 1543 South-Bridge does not have the correct revision number (???1001?) - WDT not set\n"); - return -EBUSY; + goto err_out; } if(timeout < 1 || timeout > 3600) /* arbitrary upper limit */ @@ -404,6 +408,7 @@ static int __init alim7101_wdt_init(void err_out_miscdev: misc_deregister(&wdt_miscdev); err_out: + pci_dev_put(alim7101_pmu); return rc; } diff --git a/drivers/char/watchdog/at91_wdt.c b/drivers/char/watchdog/at91_wdt.c index cc26671..4e7a114 100644 --- a/drivers/char/watchdog/at91_wdt.c +++ b/drivers/char/watchdog/at91_wdt.c @@ -168,7 +168,7 @@ static int at91_wdt_ioctl(struct inode * return 0; default: - return -ENOIOCTLCMD; + return -ENOTTY; } } diff --git a/drivers/char/watchdog/booke_wdt.c b/drivers/char/watchdog/booke_wdt.c index e3cefc5..4889022 100644 --- a/drivers/char/watchdog/booke_wdt.c +++ b/drivers/char/watchdog/booke_wdt.c @@ -125,7 +125,7 @@ static int booke_wdt_ioctl (struct inode return -EINVAL; return 0; default: - return -ENOIOCTLCMD; + return -ENOTTY; } return 0; diff --git a/drivers/char/watchdog/cpu5wdt.c b/drivers/char/watchdog/cpu5wdt.c index 04c7e49..00bdabb 100644 --- a/drivers/char/watchdog/cpu5wdt.c +++ b/drivers/char/watchdog/cpu5wdt.c @@ -183,7 +183,7 @@ static int cpu5wdt_ioctl(struct inode *i } break; default: - return -ENOIOCTLCMD; + return -ENOTTY; } return 0; } diff --git a/drivers/char/watchdog/ep93xx_wdt.c b/drivers/char/watchdog/ep93xx_wdt.c index 77c8a95..01cf123 100644 --- a/drivers/char/watchdog/ep93xx_wdt.c +++ b/drivers/char/watchdog/ep93xx_wdt.c @@ -144,7 +144,7 @@ static int ep93xx_wdt_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) { - int ret = -ENOIOCTLCMD; + int ret = -ENOTTY; switch (cmd) { case WDIOC_GETSUPPORT: diff --git a/drivers/char/watchdog/eurotechwdt.c b/drivers/char/watchdog/eurotechwdt.c index 62dbccb..4f42697 100644 --- a/drivers/char/watchdog/eurotechwdt.c +++ b/drivers/char/watchdog/eurotechwdt.c @@ -240,7 +240,7 @@ static int eurwdt_ioctl(struct inode *in switch(cmd) { default: - return -ENOIOCTLCMD; + return -ENOTTY; case WDIOC_GETSUPPORT: return copy_to_user(argp, &ident, sizeof(ident)) ? -EFAULT : 0; diff --git a/drivers/char/watchdog/i6300esb.c b/drivers/char/watchdog/i6300esb.c index 870539e..fb64df4 100644 --- a/drivers/char/watchdog/i6300esb.c +++ b/drivers/char/watchdog/i6300esb.c @@ -315,7 +315,7 @@ static int esb_ioctl (struct inode *inod return put_user(heartbeat, p); default: - return -ENOIOCTLCMD; + return -ENOTTY; } } diff --git a/drivers/char/watchdog/i8xx_tco.c b/drivers/char/watchdog/i8xx_tco.c index 8385dd3..e0627d7 100644 --- a/drivers/char/watchdog/i8xx_tco.c +++ b/drivers/char/watchdog/i8xx_tco.c @@ -356,7 +356,7 @@ static int i8xx_tco_ioctl (struct inode } default: - return -ENOIOCTLCMD; + return -ENOTTY; } } @@ -406,18 +406,18 @@ static struct notifier_block i8xx_tco_no * want to register another driver on the same PCI id. */ static struct pci_device_id i8xx_tco_pci_tbl[] = { - { PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801AA_0, PCI_ANY_ID, PCI_ANY_ID, }, - { PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801AB_0, PCI_ANY_ID, PCI_ANY_ID, }, - { PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801BA_0, PCI_ANY_ID, PCI_ANY_ID, }, - { PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801BA_10, PCI_ANY_ID, PCI_ANY_ID, }, - { PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801CA_0, PCI_ANY_ID, PCI_ANY_ID, }, - { PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801CA_12, PCI_ANY_ID, PCI_ANY_ID, }, - { PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801DB_0, PCI_ANY_ID, PCI_ANY_ID, }, - { PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801DB_12, PCI_ANY_ID, PCI_ANY_ID, }, - { PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801E_0, PCI_ANY_ID, PCI_ANY_ID, }, - { PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801EB_0, PCI_ANY_ID, PCI_ANY_ID, }, - { PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_ESB_1, PCI_ANY_ID, PCI_ANY_ID, }, - { 0, }, /* End of list */ + { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801AA_0) }, + { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801AB_0) }, + { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801BA_0) }, + { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801BA_10) }, + { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801CA_0) }, + { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801CA_12) }, + { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801DB_0) }, + { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801DB_12) }, + { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801E_0) }, + { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801EB_0) }, + { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_ESB_1) }, + { }, /* End of list */ }; MODULE_DEVICE_TABLE (pci, i8xx_tco_pci_tbl); @@ -434,12 +434,11 @@ static unsigned char __init i8xx_tco_get * Find the PCI device */ - while ((dev = pci_find_device(PCI_ANY_ID, PCI_ANY_ID, dev)) != NULL) { + for_each_pci_dev(dev) if (pci_match_id(i8xx_tco_pci_tbl, dev)) { i8xx_tco_pci = dev; break; } - } if (i8xx_tco_pci) { /* @@ -454,6 +453,7 @@ static unsigned char __init i8xx_tco_get /* Something's wrong here, ACPIBASE has to be set */ if (badr == 0x0001 || badr == 0x0000) { printk (KERN_ERR PFX "failed to get TCOBASE address\n"); + pci_dev_put(i8xx_tco_pci); return 0; } @@ -465,6 +465,7 @@ static unsigned char __init i8xx_tco_get pci_read_config_byte (i8xx_tco_pci, 0xd4, &val1); if (val1 & 0x02) { printk (KERN_ERR PFX "failed to reset NO_REBOOT flag, reboot disabled by hardware\n"); + pci_dev_put(i8xx_tco_pci); return 0; /* Cannot reset NO_REBOOT bit */ } } @@ -476,6 +477,7 @@ static unsigned char __init i8xx_tco_get if (!request_region (SMI_EN + 1, 1, "i8xx TCO")) { printk (KERN_ERR PFX "I/O address 0x%04x already in use\n", SMI_EN + 1); + pci_dev_put(i8xx_tco_pci); return 0; } val1 = inb (SMI_EN + 1); @@ -542,6 +544,7 @@ unreg_notifier: unreg_region: release_region (TCOBASE, 0x10); out: + pci_dev_put(i8xx_tco_pci); return ret; } @@ -555,6 +558,8 @@ static void __exit watchdog_cleanup (voi misc_deregister (&i8xx_tco_miscdev); unregister_reboot_notifier(&i8xx_tco_notifier); release_region (TCOBASE, 0x10); + + pci_dev_put(i8xx_tco_pci); } module_init(watchdog_init); diff --git a/drivers/char/watchdog/iTCO_wdt.c b/drivers/char/watchdog/iTCO_wdt.c new file mode 100644 index 0000000..8f89948 --- /dev/null +++ b/drivers/char/watchdog/iTCO_wdt.c @@ -0,0 +1,735 @@ +/* + * intel TCO Watchdog Driver (Used in i82801 and i6300ESB chipsets) + * + * (c) Copyright 2006 Wim Van Sebroeck . + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + * + * Neither Wim Van Sebroeck nor Iguana vzw. admit liability nor + * provide warranty for any of this software. This material is + * provided "AS-IS" and at no charge. + * + * The TCO watchdog is implemented in the following I/O controller hubs: + * (See the intel documentation on http://developer.intel.com.) + * 82801AA (ICH) : document number 290655-003, 290677-014, + * 82801AB (ICHO) : document number 290655-003, 290677-014, + * 82801BA (ICH2) : document number 290687-002, 298242-027, + * 82801BAM (ICH2-M) : document number 290687-002, 298242-027, + * 82801CA (ICH3-S) : document number 290733-003, 290739-013, + * 82801CAM (ICH3-M) : document number 290716-001, 290718-007, + * 82801DB (ICH4) : document number 290744-001, 290745-020, + * 82801DBM (ICH4-M) : document number 252337-001, 252663-005, + * 82801E (C-ICH) : document number 273599-001, 273645-002, + * 82801EB (ICH5) : document number 252516-001, 252517-003, + * 82801ER (ICH5R) : document number 252516-001, 252517-003, + * 82801FB (ICH6) : document number 301473-002, 301474-007, + * 82801FR (ICH6R) : document number 301473-002, 301474-007, + * 82801FBM (ICH6-M) : document number 301473-002, 301474-007, + * 82801FW (ICH6W) : document number 301473-001, 301474-007, + * 82801FRW (ICH6RW) : document number 301473-001, 301474-007, + * 82801GB (ICH7) : document number 307013-002, 307014-009, + * 82801GR (ICH7R) : document number 307013-002, 307014-009, + * 82801GDH (ICH7DH) : document number 307013-002, 307014-009, + * 82801GBM (ICH7-M) : document number 307013-002, 307014-009, + * 82801GHM (ICH7-M DH) : document number 307013-002, 307014-009, + * 6300ESB (6300ESB) : document number 300641-003 + */ + +/* + * Includes, defines, variables, module parameters, ... + */ + +/* Module and version information */ +#define DRV_NAME "iTCO_wdt" +#define DRV_VERSION "1.00" +#define DRV_RELDATE "30-Jul-2006" +#define PFX DRV_NAME ": " + +/* Includes */ +#include /* For CONFIG_WATCHDOG_NOWAYOUT/... */ +#include /* For module specific items */ +#include /* For new moduleparam's */ +#include /* For standard types (like size_t) */ +#include /* For the -ENODEV/... values */ +#include /* For printk/panic/... */ +#include /* For MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR) */ +#include /* For the watchdog specific items */ +#include /* For __init/__exit/... */ +#include /* For file operations */ +#include /* For platform_driver framework */ +#include /* For pci functions */ +#include /* For io-port access */ +#include /* For spin_lock/spin_unlock/... */ + +#include /* For copy_to_user/put_user/... */ +#include /* For inb/outb/... */ + +/* TCO related info */ +enum iTCO_chipsets { + TCO_ICH = 0, /* ICH */ + TCO_ICH0, /* ICH0 */ + TCO_ICH2, /* ICH2 */ + TCO_ICH2M, /* ICH2-M */ + TCO_ICH3, /* ICH3-S */ + TCO_ICH3M, /* ICH3-M */ + TCO_ICH4, /* ICH4 */ + TCO_ICH4M, /* ICH4-M */ + TCO_CICH, /* C-ICH */ + TCO_ICH5, /* ICH5 & ICH5R */ + TCO_6300ESB, /* 6300ESB */ + TCO_ICH6, /* ICH6 & ICH6R */ + TCO_ICH6M, /* ICH6-M */ + TCO_ICH6W, /* ICH6W & ICH6RW */ + TCO_ICH7, /* ICH7 & ICH7R */ + TCO_ICH7M, /* ICH7-M */ + TCO_ICH7MDH, /* ICH7-M DH */ +}; + +static struct { + char *name; + unsigned int iTCO_version; +} iTCO_chipset_info[] __devinitdata = { + {"ICH", 1}, + {"ICH0", 1}, + {"ICH2", 1}, + {"ICH2-M", 1}, + {"ICH3-S", 1}, + {"ICH3-M", 1}, + {"ICH4", 1}, + {"ICH4-M", 1}, + {"C-ICH", 1}, + {"ICH5 or ICH5R", 1}, + {"6300ESB", 1}, + {"ICH6 or ICH6R", 2}, + {"ICH6-M", 2}, + {"ICH6W or ICH6RW", 2}, + {"ICH7 or ICH7R", 2}, + {"ICH7-M", 2}, + {"ICH7-M DH", 2}, + {NULL,0} +}; + +/* + * This data only exists for exporting the supported PCI ids + * via MODULE_DEVICE_TABLE. We do not actually register a + * pci_driver, because the I/O Controller Hub has also other + * functions that probably will be registered by other drivers. + */ +static struct pci_device_id iTCO_wdt_pci_tbl[] = { + { PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801AA_0, PCI_ANY_ID, PCI_ANY_ID, 0, 0, TCO_ICH }, + { PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801AB_0, PCI_ANY_ID, PCI_ANY_ID, 0, 0, TCO_ICH0 }, + { PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801BA_0, PCI_ANY_ID, PCI_ANY_ID, 0, 0, TCO_ICH2 }, + { PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801BA_10, PCI_ANY_ID, PCI_ANY_ID, 0, 0, TCO_ICH2M }, + { PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801CA_0, PCI_ANY_ID, PCI_ANY_ID, 0, 0, TCO_ICH3 }, + { PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801CA_12, PCI_ANY_ID, PCI_ANY_ID, 0, 0, TCO_ICH3M }, + { PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801DB_0, PCI_ANY_ID, PCI_ANY_ID, 0, 0, TCO_ICH4 }, + { PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801DB_12, PCI_ANY_ID, PCI_ANY_ID, 0, 0, TCO_ICH4M }, + { PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801E_0, PCI_ANY_ID, PCI_ANY_ID, 0, 0, TCO_CICH }, + { PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801EB_0, PCI_ANY_ID, PCI_ANY_ID, 0, 0, TCO_ICH5 }, + { PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_ESB_1, PCI_ANY_ID, PCI_ANY_ID, 0, 0, TCO_6300ESB }, + { PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_ICH6_0, PCI_ANY_ID, PCI_ANY_ID, 0, 0, TCO_ICH6 }, + { PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_ICH6_1, PCI_ANY_ID, PCI_ANY_ID, 0, 0, TCO_ICH6M }, + { PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_ICH6_2, PCI_ANY_ID, PCI_ANY_ID, 0, 0, TCO_ICH6W }, + { PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_ICH7_0, PCI_ANY_ID, PCI_ANY_ID, 0, 0, TCO_ICH7 }, + { PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_ICH7_1, PCI_ANY_ID, PCI_ANY_ID, 0, 0, TCO_ICH7M }, + { PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_ICH7_31, PCI_ANY_ID, PCI_ANY_ID, 0, 0, TCO_ICH7MDH }, + { 0, }, /* End of list */ +}; +MODULE_DEVICE_TABLE (pci, iTCO_wdt_pci_tbl); + +/* Address definitions for the TCO */ +#define TCOBASE iTCO_wdt_private.ACPIBASE + 0x60 /* TCO base address */ +#define SMI_EN iTCO_wdt_private.ACPIBASE + 0x30 /* SMI Control and Enable Register */ + +#define TCO_RLD TCOBASE + 0x00 /* TCO Timer Reload and Current Value */ +#define TCOv1_TMR TCOBASE + 0x01 /* TCOv1 Timer Initial Value */ +#define TCO_DAT_IN TCOBASE + 0x02 /* TCO Data In Register */ +#define TCO_DAT_OUT TCOBASE + 0x03 /* TCO Data Out Register */ +#define TCO1_STS TCOBASE + 0x04 /* TCO1 Status Register */ +#define TCO2_STS TCOBASE + 0x06 /* TCO2 Status Register */ +#define TCO1_CNT TCOBASE + 0x08 /* TCO1 Control Register */ +#define TCO2_CNT TCOBASE + 0x0a /* TCO2 Control Register */ +#define TCOv2_TMR TCOBASE + 0x12 /* TCOv2 Timer Initial Value */ + +/* internal variables */ +static unsigned long is_active; +static char expect_release; +static struct { /* this is private data for the iTCO_wdt device */ + unsigned int iTCO_version; /* TCO version/generation */ + unsigned long ACPIBASE; /* The cards ACPIBASE address (TCOBASE = ACPIBASE+0x60) */ + unsigned long __iomem *gcs; /* NO_REBOOT flag is Memory-Mapped GCS register bit 5 (TCO version 2) */ + spinlock_t io_lock; /* the lock for io operations */ + struct pci_dev *pdev; /* the PCI-device */ +} iTCO_wdt_private; + +static struct platform_device *iTCO_wdt_platform_device; /* the watchdog platform device */ + +/* module parameters */ +#define WATCHDOG_HEARTBEAT 30 /* 30 sec default heartbeat */ +static int heartbeat = WATCHDOG_HEARTBEAT; /* in seconds */ +module_param(heartbeat, int, 0); +MODULE_PARM_DESC(heartbeat, "Watchdog heartbeat in seconds. (2 0 = The TCO timer is enabled to count */ + val = inw(TCO1_CNT); + val &= 0xf7ff; + outw(val, TCO1_CNT); + val = inw(TCO1_CNT); + spin_unlock(&iTCO_wdt_private.io_lock); + + if (val & 0x0800) + return -1; + return 0; +} + +static int iTCO_wdt_stop(void) +{ + unsigned int val; + + spin_lock(&iTCO_wdt_private.io_lock); + + /* Bit 11: TCO Timer Halt -> 1 = The TCO timer is disabled */ + val = inw(TCO1_CNT); + val |= 0x0800; + outw(val, TCO1_CNT); + val = inw(TCO1_CNT); + + /* Set the NO_REBOOT bit to prevent later reboots, just for sure */ + iTCO_wdt_set_NO_REBOOT_bit(); + + spin_unlock(&iTCO_wdt_private.io_lock); + + if ((val & 0x0800) == 0) + return -1; + return 0; +} + +static int iTCO_wdt_keepalive(void) +{ + spin_lock(&iTCO_wdt_private.io_lock); + + /* Reload the timer by writing to the TCO Timer Counter register */ + if (iTCO_wdt_private.iTCO_version == 2) { + outw(0x01, TCO_RLD); + } else if (iTCO_wdt_private.iTCO_version == 1) { + outb(0x01, TCO_RLD); + } + + spin_unlock(&iTCO_wdt_private.io_lock); + return 0; +} + +static int iTCO_wdt_set_heartbeat(int t) +{ + unsigned int val16; + unsigned char val8; + unsigned int tmrval; + + tmrval = seconds_to_ticks(t); + /* from the specs: */ + /* "Values of 0h-3h are ignored and should not be attempted" */ + if (tmrval < 0x04) + return -EINVAL; + if (((iTCO_wdt_private.iTCO_version == 2) && (tmrval > 0x3ff)) || + ((iTCO_wdt_private.iTCO_version == 1) && (tmrval > 0x03f))) + return -EINVAL; + + /* Write new heartbeat to watchdog */ + if (iTCO_wdt_private.iTCO_version == 2) { + spin_lock(&iTCO_wdt_private.io_lock); + val16 = inw(TCOv2_TMR); + val16 &= 0xfc00; + val16 |= tmrval; + outw(val16, TCOv2_TMR); + val16 = inw(TCOv2_TMR); + spin_unlock(&iTCO_wdt_private.io_lock); + + if ((val16 & 0x3ff) != tmrval) + return -EINVAL; + } else if (iTCO_wdt_private.iTCO_version == 1) { + spin_lock(&iTCO_wdt_private.io_lock); + val8 = inb(TCOv1_TMR); + val8 &= 0xc0; + val8 |= (tmrval & 0xff); + outb(val8, TCOv1_TMR); + val8 = inb(TCOv1_TMR); + spin_unlock(&iTCO_wdt_private.io_lock); + + if ((val8 & 0x3f) != tmrval) + return -EINVAL; + } + + heartbeat = t; + return 0; +} + +static int iTCO_wdt_get_timeleft (int *time_left) +{ + unsigned int val16; + unsigned char val8; + + /* read the TCO Timer */ + if (iTCO_wdt_private.iTCO_version == 2) { + spin_lock(&iTCO_wdt_private.io_lock); + val16 = inw(TCO_RLD); + val16 &= 0x3ff; + spin_unlock(&iTCO_wdt_private.io_lock); + + *time_left = (val16 * 6) / 10; + } else if (iTCO_wdt_private.iTCO_version == 1) { + spin_lock(&iTCO_wdt_private.io_lock); + val8 = inb(TCO_RLD); + val8 &= 0x3f; + spin_unlock(&iTCO_wdt_private.io_lock); + + *time_left = (val8 * 6) / 10; + } + return 0; +} + +/* + * /dev/watchdog handling + */ + +static int iTCO_wdt_open (struct inode *inode, struct file *file) +{ + /* /dev/watchdog can only be opened once */ + if (test_and_set_bit(0, &is_active)) + return -EBUSY; + + /* + * Reload and activate timer + */ + iTCO_wdt_keepalive(); + iTCO_wdt_start(); + return nonseekable_open(inode, file); +} + +static int iTCO_wdt_release (struct inode *inode, struct file *file) +{ + /* + * Shut off the timer. + */ + if (expect_release == 42) { + iTCO_wdt_stop(); + } else { + printk(KERN_CRIT PFX "Unexpected close, not stopping watchdog!\n"); + iTCO_wdt_keepalive(); + } + clear_bit(0, &is_active); + expect_release = 0; + return 0; +} + +static ssize_t iTCO_wdt_write (struct file *file, const char __user *data, + size_t len, loff_t * ppos) +{ + /* See if we got the magic character 'V' and reload the timer */ + if (len) { + if (!nowayout) { + size_t i; + + /* note: just in case someone wrote the magic character + * five months ago... */ + expect_release = 0; + + /* scan to see whether or not we got the magic character */ + for (i = 0; i != len; i++) { + char c; + if (get_user(c, data+i)) + return -EFAULT; + if (c == 'V') + expect_release = 42; + } + } + + /* someone wrote to us, we should reload the timer */ + iTCO_wdt_keepalive(); + } + return len; +} + +static int iTCO_wdt_ioctl (struct inode *inode, struct file *file, + unsigned int cmd, unsigned long arg) +{ + int new_options, retval = -EINVAL; + int new_heartbeat; + int time_left; + void __user *argp = (void __user *)arg; + int __user *p = argp; + static struct watchdog_info ident = { + .options = WDIOF_SETTIMEOUT | + WDIOF_KEEPALIVEPING | + WDIOF_MAGICCLOSE, + .firmware_version = 0, + .identity = DRV_NAME, + }; + + switch (cmd) { + case WDIOC_GETSUPPORT: + return copy_to_user(argp, &ident, + sizeof (ident)) ? -EFAULT : 0; + + case WDIOC_GETSTATUS: + case WDIOC_GETBOOTSTATUS: + return put_user(0, p); + + case WDIOC_KEEPALIVE: + iTCO_wdt_keepalive(); + return 0; + + case WDIOC_SETOPTIONS: + { + if (get_user(new_options, p)) + return -EFAULT; + + if (new_options & WDIOS_DISABLECARD) { + iTCO_wdt_stop(); + retval = 0; + } + + if (new_options & WDIOS_ENABLECARD) { + iTCO_wdt_keepalive(); + iTCO_wdt_start(); + retval = 0; + } + + return retval; + } + + case WDIOC_SETTIMEOUT: + { + if (get_user(new_heartbeat, p)) + return -EFAULT; + + if (iTCO_wdt_set_heartbeat(new_heartbeat)) + return -EINVAL; + + iTCO_wdt_keepalive(); + /* Fall */ + } + + case WDIOC_GETTIMEOUT: + return put_user(heartbeat, p); + + case WDIOC_GETTIMELEFT: + { + if (iTCO_wdt_get_timeleft(&time_left)) + return -EINVAL; + + return put_user(time_left, p); + } + + default: + return -ENOTTY; + } +} + +/* + * Kernel Interfaces + */ + +static struct file_operations iTCO_wdt_fops = { + .owner = THIS_MODULE, + .llseek = no_llseek, + .write = iTCO_wdt_write, + .ioctl = iTCO_wdt_ioctl, + .open = iTCO_wdt_open, + .release = iTCO_wdt_release, +}; + +static struct miscdevice iTCO_wdt_miscdev = { + .minor = WATCHDOG_MINOR, + .name = "watchdog", + .fops = &iTCO_wdt_fops, +}; + +/* + * Init & exit routines + */ + +static int iTCO_wdt_init(struct pci_dev *pdev, const struct pci_device_id *ent, struct platform_device *dev) +{ + int ret; + u32 base_address; + unsigned long RCBA; + unsigned long val32; + + /* + * Find the ACPI/PM base I/O address which is the base + * for the TCO registers (TCOBASE=ACPIBASE + 0x60) + * ACPIBASE is bits [15:7] from 0x40-0x43 + */ + pci_read_config_dword(pdev, 0x40, &base_address); + base_address &= 0x00007f80; + if (base_address == 0x00000000) { + /* Something's wrong here, ACPIBASE has to be set */ + printk(KERN_ERR PFX "failed to get TCOBASE address\n"); + pci_dev_put(pdev); + return -ENODEV; + } + iTCO_wdt_private.iTCO_version = iTCO_chipset_info[ent->driver_data].iTCO_version; + iTCO_wdt_private.ACPIBASE = base_address; + iTCO_wdt_private.pdev = pdev; + + /* Get the Memory-Mapped GCS register, we need it for the NO_REBOOT flag (TCO v2) */ + /* To get access to it you have to read RCBA from PCI Config space 0xf0 + and use it as base. GCS = RCBA + ICH6_GCS(0x3410). */ + if (iTCO_wdt_private.iTCO_version == 2) { + pci_read_config_dword(pdev, 0xf0, &base_address); + RCBA = base_address & 0xffffc000; + iTCO_wdt_private.gcs = ioremap((RCBA + 0x3410),4); + } + + /* Check chipset's NO_REBOOT bit */ + if (iTCO_wdt_unset_NO_REBOOT_bit()) { + printk(KERN_ERR PFX "failed to reset NO_REBOOT flag, reboot disabled by hardware\n"); + ret = -ENODEV; /* Cannot reset NO_REBOOT bit */ + goto out; + } + + /* Set the NO_REBOOT bit to prevent later reboots, just for sure */ + iTCO_wdt_set_NO_REBOOT_bit(); + + /* Set the TCO_EN bit in SMI_EN register */ + if (!request_region(SMI_EN, 4, "iTCO_wdt")) { + printk(KERN_ERR PFX "I/O address 0x%04lx already in use\n", + SMI_EN ); + ret = -EIO; + goto out; + } + val32 = inl(SMI_EN); + val32 &= 0xffffdfff; /* Turn off SMI clearing watchdog */ + outl(val32, SMI_EN); + release_region(SMI_EN, 4); + + /* The TCO I/O registers reside in a 32-byte range pointed to by the TCOBASE value */ + if (!request_region (TCOBASE, 0x20, "iTCO_wdt")) { + printk (KERN_ERR PFX "I/O address 0x%04lx already in use\n", + TCOBASE); + ret = -EIO; + goto out; + } + + printk(KERN_INFO PFX "Found a %s TCO device (Version=%d, TCOBASE=0x%04lx)\n", + iTCO_chipset_info[ent->driver_data].name, + iTCO_chipset_info[ent->driver_data].iTCO_version, + TCOBASE); + + /* Clear out the (probably old) status */ + outb(0, TCO1_STS); + outb(3, TCO2_STS); + + /* Make sure the watchdog is not running */ + iTCO_wdt_stop(); + + /* Check that the heartbeat value is within it's range ; if not reset to the default */ + if (iTCO_wdt_set_heartbeat(heartbeat)) { + iTCO_wdt_set_heartbeat(WATCHDOG_HEARTBEAT); + printk(KERN_INFO PFX "heartbeat value must be 2"); +MODULE_DESCRIPTION("Intel TCO WatchDog Timer Driver"); +MODULE_VERSION(DRV_VERSION); +MODULE_LICENSE("GPL"); +MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR); diff --git a/drivers/char/watchdog/ib700wdt.c b/drivers/char/watchdog/ib700wdt.c index fd95f73..c1ed209 100644 --- a/drivers/char/watchdog/ib700wdt.c +++ b/drivers/char/watchdog/ib700wdt.c @@ -199,7 +199,7 @@ ibwdt_ioctl(struct inode *inode, struct break; default: - return -ENOIOCTLCMD; + return -ENOTTY; } return 0; } diff --git a/drivers/char/watchdog/ibmasr.c b/drivers/char/watchdog/ibmasr.c index 26ceee7..dd6760f 100644 --- a/drivers/char/watchdog/ibmasr.c +++ b/drivers/char/watchdog/ibmasr.c @@ -295,7 +295,7 @@ static int asr_ioctl(struct inode *inode } } - return -ENOIOCTLCMD; + return -ENOTTY; } static int asr_open(struct inode *inode, struct file *file) diff --git a/drivers/char/watchdog/indydog.c b/drivers/char/watchdog/indydog.c index dacc1c2..0bc2393 100644 --- a/drivers/char/watchdog/indydog.c +++ b/drivers/char/watchdog/indydog.c @@ -112,7 +112,7 @@ static int indydog_ioctl(struct inode *i switch (cmd) { default: - return -ENOIOCTLCMD; + return -ENOTTY; case WDIOC_GETSUPPORT: if (copy_to_user((struct watchdog_info *)arg, &ident, sizeof(ident))) diff --git a/drivers/char/watchdog/ixp2000_wdt.c b/drivers/char/watchdog/ixp2000_wdt.c index 6929088..c91d9a6 100644 --- a/drivers/char/watchdog/ixp2000_wdt.c +++ b/drivers/char/watchdog/ixp2000_wdt.c @@ -107,7 +107,7 @@ static int ixp2000_wdt_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) { - int ret = -ENOIOCTLCMD; + int ret = -ENOTTY; int time; switch (cmd) { diff --git a/drivers/char/watchdog/ixp4xx_wdt.c b/drivers/char/watchdog/ixp4xx_wdt.c index 9db5cf2..db477f7 100644 --- a/drivers/char/watchdog/ixp4xx_wdt.c +++ b/drivers/char/watchdog/ixp4xx_wdt.c @@ -102,7 +102,7 @@ static int ixp4xx_wdt_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) { - int ret = -ENOIOCTLCMD; + int ret = -ENOTTY; int time; switch (cmd) { diff --git a/drivers/char/watchdog/machzwd.c b/drivers/char/watchdog/machzwd.c index 23734e0..276577d 100644 --- a/drivers/char/watchdog/machzwd.c +++ b/drivers/char/watchdog/machzwd.c @@ -329,7 +329,7 @@ static int zf_ioctl(struct inode *inode, break; default: - return -ENOIOCTLCMD; + return -ENOTTY; } return 0; @@ -426,8 +426,7 @@ static int __init zf_init(void) printk(KERN_INFO PFX ": MachZ ZF-Logic Watchdog driver initializing.\n"); ret = zf_get_ZFL_version(); - printk("%#x\n", ret); - if((!ret) || (ret != 0xffff)){ + if ((!ret) || (ret == 0xffff)) { printk(KERN_WARNING PFX ": no ZF-Logic found\n"); return -ENODEV; } diff --git a/drivers/char/watchdog/mixcomwd.c b/drivers/char/watchdog/mixcomwd.c index ae94332..c2dac0a 100644 --- a/drivers/char/watchdog/mixcomwd.c +++ b/drivers/char/watchdog/mixcomwd.c @@ -185,7 +185,7 @@ static int mixcomwd_ioctl(struct inode * mixcomwd_ping(); break; default: - return -ENOIOCTLCMD; + return -ENOTTY; } return 0; } diff --git a/drivers/char/watchdog/mpc83xx_wdt.c b/drivers/char/watchdog/mpc83xx_wdt.c index a480903..18ca752 100644 --- a/drivers/char/watchdog/mpc83xx_wdt.c +++ b/drivers/char/watchdog/mpc83xx_wdt.c @@ -125,7 +125,7 @@ static int mpc83xx_wdt_ioctl(struct inod case WDIOC_GETTIMEOUT: return put_user(timeout_sec, p); default: - return -ENOIOCTLCMD; + return -ENOTTY; } } diff --git a/drivers/char/watchdog/mpc8xx_wdt.c b/drivers/char/watchdog/mpc8xx_wdt.c index 35dd9e6..8aaed10 100644 --- a/drivers/char/watchdog/mpc8xx_wdt.c +++ b/drivers/char/watchdog/mpc8xx_wdt.c @@ -126,7 +126,7 @@ static int mpc8xx_wdt_ioctl(struct inode break; default: - return -ENOIOCTLCMD; + return -ENOTTY; } return 0; diff --git a/drivers/char/watchdog/mpcore_wdt.c b/drivers/char/watchdog/mpcore_wdt.c index 54b3c56..02d336a 100644 --- a/drivers/char/watchdog/mpcore_wdt.c +++ b/drivers/char/watchdog/mpcore_wdt.c @@ -221,7 +221,7 @@ static int mpcore_wdt_ioctl(struct inode } uarg; if (_IOC_DIR(cmd) && _IOC_SIZE(cmd) > sizeof(uarg)) - return -ENOIOCTLCMD; + return -ENOTTY; if (_IOC_DIR(cmd) & _IOC_WRITE) { ret = copy_from_user(&uarg, (void __user *)arg, _IOC_SIZE(cmd)); @@ -271,7 +271,7 @@ static int mpcore_wdt_ioctl(struct inode break; default: - return -ENOIOCTLCMD; + return -ENOTTY; } if (ret == 0 && _IOC_DIR(cmd) & _IOC_READ) { diff --git a/drivers/char/watchdog/mv64x60_wdt.c b/drivers/char/watchdog/mv64x60_wdt.c index 5c8fab3..b887cdb 100644 --- a/drivers/char/watchdog/mv64x60_wdt.c +++ b/drivers/char/watchdog/mv64x60_wdt.c @@ -160,7 +160,7 @@ static int mv64x60_wdt_ioctl(struct inod break; default: - return -ENOIOCTLCMD; + return -ENOTTY; } return 0; diff --git a/drivers/char/watchdog/pcwd.c b/drivers/char/watchdog/pcwd.c index cd7d1b6..6f8515d 100644 --- a/drivers/char/watchdog/pcwd.c +++ b/drivers/char/watchdog/pcwd.c @@ -572,7 +572,7 @@ static int pcwd_ioctl(struct inode *inod switch(cmd) { default: - return -ENOIOCTLCMD; + return -ENOTTY; case WDIOC_GETSUPPORT: if(copy_to_user(argp, &ident, sizeof(ident))) diff --git a/drivers/char/watchdog/pcwd_pci.c b/drivers/char/watchdog/pcwd_pci.c index c7cfd6d..2de6e49 100644 --- a/drivers/char/watchdog/pcwd_pci.c +++ b/drivers/char/watchdog/pcwd_pci.c @@ -541,7 +541,7 @@ static int pcipcwd_ioctl(struct inode *i } default: - return -ENOIOCTLCMD; + return -ENOTTY; } } diff --git a/drivers/char/watchdog/pcwd_usb.c b/drivers/char/watchdog/pcwd_usb.c index b7ae73d..77662cb 100644 --- a/drivers/char/watchdog/pcwd_usb.c +++ b/drivers/char/watchdog/pcwd_usb.c @@ -445,7 +445,7 @@ static int usb_pcwd_ioctl(struct inode * } default: - return -ENOIOCTLCMD; + return -ENOTTY; } } diff --git a/drivers/char/watchdog/pnx4008_wdt.c b/drivers/char/watchdog/pnx4008_wdt.c new file mode 100644 index 0000000..db2731b --- /dev/null +++ b/drivers/char/watchdog/pnx4008_wdt.c @@ -0,0 +1,362 @@ +/* + * drivers/char/watchdog/pnx4008_wdt.c + * + * Watchdog driver for PNX4008 board + * + * Authors: Dmitry Chigirev , + * Vitaly Wool + * Based on sa1100 driver, + * Copyright (C) 2000 Oleg Drokin + * + * 2005-2006 (c) MontaVista Software, Inc. This file is licensed under + * the terms of the GNU General Public License version 2. This program + * is licensed "as is" without any warranty of any kind, whether express + * or implied. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#define MODULE_NAME "PNX4008-WDT: " + +/* WatchDog Timer - Chapter 23 Page 207 */ + +#define DEFAULT_HEARTBEAT 19 +#define MAX_HEARTBEAT 60 + +/* Watchdog timer register set definition */ +#define WDTIM_INT(p) ((p) + 0x0) +#define WDTIM_CTRL(p) ((p) + 0x4) +#define WDTIM_COUNTER(p) ((p) + 0x8) +#define WDTIM_MCTRL(p) ((p) + 0xC) +#define WDTIM_MATCH0(p) ((p) + 0x10) +#define WDTIM_EMR(p) ((p) + 0x14) +#define WDTIM_PULSE(p) ((p) + 0x18) +#define WDTIM_RES(p) ((p) + 0x1C) + +/* WDTIM_INT bit definitions */ +#define MATCH_INT 1 + +/* WDTIM_CTRL bit definitions */ +#define COUNT_ENAB 1 +#define RESET_COUNT (1<<1) +#define DEBUG_EN (1<<2) + +/* WDTIM_MCTRL bit definitions */ +#define MR0_INT 1 +#undef RESET_COUNT0 +#define RESET_COUNT0 (1<<2) +#define STOP_COUNT0 (1<<2) +#define M_RES1 (1<<3) +#define M_RES2 (1<<4) +#define RESFRC1 (1<<5) +#define RESFRC2 (1<<6) + +/* WDTIM_EMR bit definitions */ +#define EXT_MATCH0 1 +#define MATCH_OUTPUT_HIGH (2<<4) /*a MATCH_CTRL setting */ + +/* WDTIM_RES bit definitions */ +#define WDOG_RESET 1 /* read only */ + +#define WDOG_COUNTER_RATE 13000000 /*the counter clock is 13 MHz fixed */ + +static int nowayout = WATCHDOG_NOWAYOUT; +static int heartbeat = DEFAULT_HEARTBEAT; + +static spinlock_t io_lock; +static unsigned long wdt_status; +#define WDT_IN_USE 0 +#define WDT_OK_TO_CLOSE 1 +#define WDT_REGION_INITED 2 +#define WDT_DEVICE_INITED 3 + +static unsigned long boot_status; + +static struct resource *wdt_mem; +static void __iomem *wdt_base; +struct clk *wdt_clk; + +static void wdt_enable(void) +{ + spin_lock(&io_lock); + + if (wdt_clk) + clk_set_rate(wdt_clk, 1); + + /* stop counter, initiate counter reset */ + __raw_writel(RESET_COUNT, WDTIM_CTRL(wdt_base)); + /*wait for reset to complete. 100% guarantee event */ + while (__raw_readl(WDTIM_COUNTER(wdt_base))) + cpu_relax(); + /* internal and external reset, stop after that */ + __raw_writel(M_RES2 | STOP_COUNT0 | RESET_COUNT0, + WDTIM_MCTRL(wdt_base)); + /* configure match output */ + __raw_writel(MATCH_OUTPUT_HIGH, WDTIM_EMR(wdt_base)); + /* clear interrupt, just in case */ + __raw_writel(MATCH_INT, WDTIM_INT(wdt_base)); + /* the longest pulse period 65541/(13*10^6) seconds ~ 5 ms. */ + __raw_writel(0xFFFF, WDTIM_PULSE(wdt_base)); + __raw_writel(heartbeat * WDOG_COUNTER_RATE, WDTIM_MATCH0(wdt_base)); + /*enable counter, stop when debugger active */ + __raw_writel(COUNT_ENAB | DEBUG_EN, WDTIM_CTRL(wdt_base)); + + spin_unlock(&io_lock); +} + +static void wdt_disable(void) +{ + spin_lock(&io_lock); + + __raw_writel(0, WDTIM_CTRL(wdt_base)); /*stop counter */ + if (wdt_clk) + clk_set_rate(wdt_clk, 0); + + spin_unlock(&io_lock); +} + +static int pnx4008_wdt_open(struct inode *inode, struct file *file) +{ + if (test_and_set_bit(WDT_IN_USE, &wdt_status)) + return -EBUSY; + + clear_bit(WDT_OK_TO_CLOSE, &wdt_status); + + wdt_enable(); + + return nonseekable_open(inode, file); +} + +static ssize_t +pnx4008_wdt_write(struct file *file, const char *data, size_t len, + loff_t * ppos) +{ + /* Can't seek (pwrite) on this device */ + if (ppos != &file->f_pos) + return -ESPIPE; + + if (len) { + if (!nowayout) { + size_t i; + + clear_bit(WDT_OK_TO_CLOSE, &wdt_status); + + for (i = 0; i != len; i++) { + char c; + + if (get_user(c, data + i)) + return -EFAULT; + if (c == 'V') + set_bit(WDT_OK_TO_CLOSE, &wdt_status); + } + } + wdt_enable(); + } + + return len; +} + +static struct watchdog_info ident = { + .options = WDIOF_CARDRESET | WDIOF_MAGICCLOSE | + WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING, + .identity = "PNX4008 Watchdog", +}; + +static int +pnx4008_wdt_ioctl(struct inode *inode, struct file *file, unsigned int cmd, + unsigned long arg) +{ + int ret = -ENOTTY; + int time; + + switch (cmd) { + case WDIOC_GETSUPPORT: + ret = copy_to_user((struct watchdog_info *)arg, &ident, + sizeof(ident)) ? -EFAULT : 0; + break; + + case WDIOC_GETSTATUS: + ret = put_user(0, (int *)arg); + break; + + case WDIOC_GETBOOTSTATUS: + ret = put_user(boot_status, (int *)arg); + break; + + case WDIOC_SETTIMEOUT: + ret = get_user(time, (int *)arg); + if (ret) + break; + + if (time <= 0 || time > MAX_HEARTBEAT) { + ret = -EINVAL; + break; + } + + heartbeat = time; + wdt_enable(); + /* Fall through */ + + case WDIOC_GETTIMEOUT: + ret = put_user(heartbeat, (int *)arg); + break; + + case WDIOC_KEEPALIVE: + wdt_enable(); + ret = 0; + break; + } + return ret; +} + +static int pnx4008_wdt_release(struct inode *inode, struct file *file) +{ + if (!test_bit(WDT_OK_TO_CLOSE, &wdt_status)) + printk(KERN_WARNING "WATCHDOG: Device closed unexpectdly\n"); + + wdt_disable(); + clear_bit(WDT_IN_USE, &wdt_status); + clear_bit(WDT_OK_TO_CLOSE, &wdt_status); + + return 0; +} + +static struct file_operations pnx4008_wdt_fops = { + .owner = THIS_MODULE, + .llseek = no_llseek, + .write = pnx4008_wdt_write, + .ioctl = pnx4008_wdt_ioctl, + .open = pnx4008_wdt_open, + .release = pnx4008_wdt_release, +}; + +static struct miscdevice pnx4008_wdt_miscdev = { + .minor = WATCHDOG_MINOR, + .name = "watchdog", + .fops = &pnx4008_wdt_fops, +}; + +static int pnx4008_wdt_probe(struct platform_device *pdev) +{ + int ret = 0, size; + struct resource *res; + + spin_lock_init(&io_lock); + + if (heartbeat < 1 || heartbeat > MAX_HEARTBEAT) + heartbeat = DEFAULT_HEARTBEAT; + + printk(KERN_INFO MODULE_NAME + "PNX4008 Watchdog Timer: heartbeat %d sec\n", heartbeat); + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (res == NULL) { + printk(KERN_INFO MODULE_NAME + "failed to get memory region resouce\n"); + return -ENOENT; + } + + size = res->end - res->start + 1; + wdt_mem = request_mem_region(res->start, size, pdev->name); + + if (wdt_mem == NULL) { + printk(KERN_INFO MODULE_NAME "failed to get memory region\n"); + return -ENOENT; + } + wdt_base = (void __iomem *)IO_ADDRESS(res->start); + + wdt_clk = clk_get(&pdev->dev, "wdt_ck"); + if (!wdt_clk) { + release_resource(wdt_mem); + kfree(wdt_mem); + goto out; + } else + clk_set_rate(wdt_clk, 1); + + ret = misc_register(&pnx4008_wdt_miscdev); + if (ret < 0) { + printk(KERN_ERR MODULE_NAME "cannot register misc device\n"); + release_resource(wdt_mem); + kfree(wdt_mem); + clk_set_rate(wdt_clk, 0); + } else { + boot_status = (__raw_readl(WDTIM_RES(wdt_base)) & WDOG_RESET) ? + WDIOF_CARDRESET : 0; + wdt_disable(); /*disable for now */ + set_bit(WDT_DEVICE_INITED, &wdt_status); + } + +out: + return ret; +} + +static int pnx4008_wdt_remove(struct platform_device *pdev) +{ + misc_deregister(&pnx4008_wdt_miscdev); + if (wdt_clk) { + clk_set_rate(wdt_clk, 0); + clk_put(wdt_clk); + wdt_clk = NULL; + } + if (wdt_mem) { + release_resource(wdt_mem); + kfree(wdt_mem); + wdt_mem = NULL; + } + return 0; +} + +static struct platform_driver platform_wdt_driver = { + .driver = { + .name = "watchdog", + }, + .probe = pnx4008_wdt_probe, + .remove = pnx4008_wdt_remove, +}; + +static int __init pnx4008_wdt_init(void) +{ + return platform_driver_register(&platform_wdt_driver); +} + +static void __exit pnx4008_wdt_exit(void) +{ + return platform_driver_unregister(&platform_wdt_driver); +} + +module_init(pnx4008_wdt_init); +module_exit(pnx4008_wdt_exit); + +MODULE_AUTHOR("MontaVista Software, Inc. "); +MODULE_DESCRIPTION("PNX4008 Watchdog Driver"); + +module_param(heartbeat, int, 0); +MODULE_PARM_DESC(heartbeat, + "Watchdog heartbeat period in seconds from 1 to " + __MODULE_STRING(MAX_HEARTBEAT) ", default " + __MODULE_STRING(DEFAULT_HEARTBEAT)); + +module_param(nowayout, int, 0); +MODULE_PARM_DESC(nowayout, + "Set to 1 to keep watchdog running after device release"); + +MODULE_LICENSE("GPL"); +MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR); diff --git a/drivers/char/watchdog/s3c2410_wdt.c b/drivers/char/watchdog/s3c2410_wdt.c index be978e8..b36a04a 100644 --- a/drivers/char/watchdog/s3c2410_wdt.c +++ b/drivers/char/watchdog/s3c2410_wdt.c @@ -62,7 +62,7 @@ #define PFX "s3c2410-wdt: " #define CONFIG_S3C2410_WATCHDOG_ATBOOT (0) #define CONFIG_S3C2410_WATCHDOG_DEFAULT_TIME (15) -static int nowayout = WATCHDOG_NOWAYOUT; +static int nowayout = WATCHDOG_NOWAYOUT; static int tmr_margin = CONFIG_S3C2410_WATCHDOG_DEFAULT_TIME; static int tmr_atboot = CONFIG_S3C2410_WATCHDOG_ATBOOT; static int soft_noboot = 0; @@ -213,11 +213,10 @@ static int s3c2410wdt_open(struct inode if(down_trylock(&open_lock)) return -EBUSY; - if (nowayout) { + if (nowayout) __module_get(THIS_MODULE); - } else { - allow_close = CLOSE_STATE_ALLOW; - } + + allow_close = CLOSE_STATE_NOT; /* start the timer */ s3c2410wdt_start(); @@ -230,6 +229,7 @@ static int s3c2410wdt_release(struct ino * Shut off the timer. * Lock it in if it's a module and we set nowayout */ + if (allow_close == CLOSE_STATE_ALLOW) { s3c2410wdt_stop(); } else { @@ -288,7 +288,7 @@ static int s3c2410wdt_ioctl(struct inode switch (cmd) { default: - return -ENOIOCTLCMD; + return -ENOTTY; case WDIOC_GETSUPPORT: return copy_to_user(argp, &s3c2410_wdt_ident, diff --git a/drivers/char/watchdog/sa1100_wdt.c b/drivers/char/watchdog/sa1100_wdt.c index 1fc16d9..33c1137 100644 --- a/drivers/char/watchdog/sa1100_wdt.c +++ b/drivers/char/watchdog/sa1100_wdt.c @@ -90,7 +90,7 @@ static struct watchdog_info ident = { static int sa1100dog_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) { - int ret = -ENOIOCTLCMD; + int ret = -ENOTTY; int time; void __user *argp = (void __user *)arg; int __user *p = argp; diff --git a/drivers/char/watchdog/sbc60xxwdt.c b/drivers/char/watchdog/sbc60xxwdt.c index 4663c2f..c7b2045 100644 --- a/drivers/char/watchdog/sbc60xxwdt.c +++ b/drivers/char/watchdog/sbc60xxwdt.c @@ -235,7 +235,7 @@ static int fop_ioctl(struct inode *inode switch(cmd) { default: - return -ENOIOCTLCMD; + return -ENOTTY; case WDIOC_GETSUPPORT: return copy_to_user(argp, &ident, sizeof(ident))?-EFAULT:0; case WDIOC_GETSTATUS: diff --git a/drivers/char/watchdog/sbc_epx_c3.c b/drivers/char/watchdog/sbc_epx_c3.c index bfc475d..8882b42 100644 --- a/drivers/char/watchdog/sbc_epx_c3.c +++ b/drivers/char/watchdog/sbc_epx_c3.c @@ -141,7 +141,7 @@ static int epx_c3_ioctl(struct inode *in return retval; default: - return -ENOIOCTLCMD; + return -ENOTTY; } } diff --git a/drivers/char/watchdog/sc1200wdt.c b/drivers/char/watchdog/sc1200wdt.c index 7c3cf29..d8d0f28 100644 --- a/drivers/char/watchdog/sc1200wdt.c +++ b/drivers/char/watchdog/sc1200wdt.c @@ -180,7 +180,7 @@ static int sc1200wdt_ioctl(struct inode switch (cmd) { default: - return -ENOIOCTLCMD; /* Keep Pavel Machek amused ;) */ + return -ENOTTY; case WDIOC_GETSUPPORT: if (copy_to_user(argp, &ident, sizeof ident)) diff --git a/drivers/char/watchdog/sc520_wdt.c b/drivers/char/watchdog/sc520_wdt.c index 2c7c9db..caec37b 100644 --- a/drivers/char/watchdog/sc520_wdt.c +++ b/drivers/char/watchdog/sc520_wdt.c @@ -290,7 +290,7 @@ static int fop_ioctl(struct inode *inode switch(cmd) { default: - return -ENOIOCTLCMD; + return -ENOTTY; case WDIOC_GETSUPPORT: return copy_to_user(argp, &ident, sizeof(ident))?-EFAULT:0; case WDIOC_GETSTATUS: diff --git a/drivers/char/watchdog/scx200_wdt.c b/drivers/char/watchdog/scx200_wdt.c index c561299..fc0e034 100644 --- a/drivers/char/watchdog/scx200_wdt.c +++ b/drivers/char/watchdog/scx200_wdt.c @@ -166,7 +166,7 @@ static int scx200_wdt_ioctl(struct inode switch (cmd) { default: - return -ENOIOCTLCMD; + return -ENOTTY; case WDIOC_GETSUPPORT: if(copy_to_user(argp, &ident, sizeof(ident))) return -EFAULT; diff --git a/drivers/char/watchdog/shwdt.c b/drivers/char/watchdog/shwdt.c index 1355038..1b55ee8 100644 --- a/drivers/char/watchdog/shwdt.c +++ b/drivers/char/watchdog/shwdt.c @@ -318,7 +318,7 @@ static int sh_wdt_ioctl(struct inode *in return retval; default: - return -ENOIOCTLCMD; + return -ENOTTY; } return 0; diff --git a/drivers/char/watchdog/smsc37b787_wdt.c b/drivers/char/watchdog/smsc37b787_wdt.c new file mode 100644 index 0000000..9f56913 --- /dev/null +++ b/drivers/char/watchdog/smsc37b787_wdt.c @@ -0,0 +1,627 @@ +/* + * SMsC 37B787 Watchdog Timer driver for Linux 2.6.x.x + * + * Based on acquirewdt.c by Alan Cox + * and some other existing drivers + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + * + * The authors do NOT admit liability nor provide warranty for + * any of this software. This material is provided "AS-IS" in + * the hope that it may be useful for others. + * + * (C) Copyright 2003-2006 Sven Anders + * + * History: + * 2003 - Created version 1.0 for Linux 2.4.x. + * 2006 - Ported to Linux 2.6, added nowayout and MAGICCLOSE + * features. Released version 1.1 + * + * Theory of operation: + * + * A Watchdog Timer (WDT) is a hardware circuit that can + * reset the computer system in case of a software fault. + * You probably knew that already. + * + * Usually a userspace daemon will notify the kernel WDT driver + * via the /dev/watchdog special device file that userspace is + * still alive, at regular intervals. When such a notification + * occurs, the driver will usually tell the hardware watchdog + * that everything is in order, and that the watchdog should wait + * for yet another little while to reset the system. + * If userspace fails (RAM error, kernel bug, whatever), the + * notifications cease to occur, and the hardware watchdog will + * reset the system (causing a reboot) after the timeout occurs. + * + * Create device with: + * mknod /dev/watchdog c 10 130 + * + * For an example userspace keep-alive daemon, see: + * Documentation/watchdog/watchdog.txt + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +/* enable support for minutes as units? */ +/* (does not always work correctly, so disabled by default!) */ +#define SMSC_SUPPORT_MINUTES +#undef SMSC_SUPPORT_MINUTES + +#define MAX_TIMEOUT 255 + +#define UNIT_SECOND 0 +#define UNIT_MINUTE 1 + +#define MODNAME "smsc37b787_wdt: " +#define VERSION "1.1" + +#define IOPORT 0x3F0 +#define IOPORT_SIZE 2 +#define IODEV_NO 8 + +static int unit = UNIT_SECOND; /* timer's unit */ +static int timeout = 60; /* timeout value: default is 60 "units" */ +static unsigned long timer_enabled = 0; /* is the timer enabled? */ + +static char expect_close; /* is the close expected? */ + +static spinlock_t io_lock; /* to guard the watchdog from io races */ + +static int nowayout = WATCHDOG_NOWAYOUT; + +/* -- Low level function ----------------------------------------*/ + +/* unlock the IO chip */ + +static inline void open_io_config(void) +{ + outb(0x55, IOPORT); + mdelay(1); + outb(0x55, IOPORT); +} + +/* lock the IO chip */ +static inline void close_io_config(void) +{ + outb(0xAA, IOPORT); +} + +/* select the IO device */ +static inline void select_io_device(unsigned char devno) +{ + outb(0x07, IOPORT); + outb(devno, IOPORT+1); +} + +/* write to the control register */ +static inline void write_io_cr(unsigned char reg, unsigned char data) +{ + outb(reg, IOPORT); + outb(data, IOPORT+1); +} + +/* read from the control register */ +static inline char read_io_cr(unsigned char reg) +{ + outb(reg, IOPORT); + return inb(IOPORT+1); +} + +/* -- Medium level functions ------------------------------------*/ + +static inline void gpio_bit12(unsigned char reg) +{ + // -- General Purpose I/O Bit 1.2 -- + // Bit 0, In/Out: 0 = Output, 1 = Input + // Bit 1, Polarity: 0 = No Invert, 1 = Invert + // Bit 2, Group Enable Intr.: 0 = Disable, 1 = Enable + // Bit 3/4, Function select: 00 = GPI/O, 01 = WDT, 10 = P17, + // 11 = Either Edge Triggered Intr. 2 + // Bit 5/6 (Reserved) + // Bit 7, Output Type: 0 = Push Pull Bit, 1 = Open Drain + write_io_cr(0xE2, reg); +} + +static inline void gpio_bit13(unsigned char reg) +{ + // -- General Purpose I/O Bit 1.3 -- + // Bit 0, In/Out: 0 = Output, 1 = Input + // Bit 1, Polarity: 0 = No Invert, 1 = Invert + // Bit 2, Group Enable Intr.: 0 = Disable, 1 = Enable + // Bit 3, Function select: 0 = GPI/O, 1 = LED + // Bit 4-6 (Reserved) + // Bit 7, Output Type: 0 = Push Pull Bit, 1 = Open Drain + write_io_cr(0xE3, reg); +} + +static inline void wdt_timer_units(unsigned char new_units) +{ + // -- Watchdog timer units -- + // Bit 0-6 (Reserved) + // Bit 7, WDT Time-out Value Units Select + // (0 = Minutes, 1 = Seconds) + write_io_cr(0xF1, new_units); +} + +static inline void wdt_timeout_value(unsigned char new_timeout) +{ + // -- Watchdog Timer Time-out Value -- + // Bit 0-7 Binary coded units (0=Disabled, 1..255) + write_io_cr(0xF2, new_timeout); +} + +static inline void wdt_timer_conf(unsigned char conf) +{ + // -- Watchdog timer configuration -- + // Bit 0 Joystick enable: 0* = No Reset, 1 = Reset WDT upon Gameport I/O + // Bit 1 Keyboard enable: 0* = No Reset, 1 = Reset WDT upon KBD Intr. + // Bit 2 Mouse enable: 0* = No Reset, 1 = Reset WDT upon Mouse Intr. + // Bit 3 Reset the timer + // (Wrong in SMsC documentation? Given as: PowerLED Timout Enabled) + // Bit 4-7 WDT Interrupt Mapping: (0000* = Disabled, + // 0001=IRQ1, 0010=(Invalid), 0011=IRQ3 to 1111=IRQ15) + write_io_cr(0xF3, conf); +} + +static inline void wdt_timer_ctrl(unsigned char reg) +{ + // -- Watchdog timer control -- + // Bit 0 Status Bit: 0 = Timer counting, 1 = Timeout occured + // Bit 1 Power LED Toggle: 0 = Disable Toggle, 1 = Toggle at 1 Hz + // Bit 2 Force Timeout: 1 = Forces WD timeout event (self-cleaning) + // Bit 3 P20 Force Timeout enabled: + // 0 = P20 activity does not generate the WD timeout event + // 1 = P20 Allows rising edge of P20, from the keyboard + // controller, to force the WD timeout event. + // Bit 4 (Reserved) + // -- Soft power management -- + // Bit 5 Stop Counter: 1 = Stop software power down counter + // set via register 0xB8, (self-cleaning) + // (Upon read: 0 = Counter running, 1 = Counter stopped) + // Bit 6 Restart Counter: 1 = Restart software power down counter + // set via register 0xB8, (self-cleaning) + // Bit 7 SPOFF: 1 = Force software power down (self-cleaning) + + write_io_cr(0xF4, reg); +} + +/* -- Higher level functions ------------------------------------*/ + +/* initialize watchdog */ + +static void wb_smsc_wdt_initialize(void) +{ + unsigned char old; + + spin_lock(&io_lock); + open_io_config(); + select_io_device(IODEV_NO); + + // enable the watchdog + gpio_bit13(0x08); // Select pin 80 = LED not GPIO + gpio_bit12(0x0A); // Set pin 79 = WDT not GPIO/Output/Polarity=Invert + + // disable the timeout + wdt_timeout_value(0); + + // reset control register + wdt_timer_ctrl(0x00); + + // reset configuration register + wdt_timer_conf(0x00); + + // read old (timer units) register + old = read_io_cr(0xF1) & 0x7F; + if (unit == UNIT_SECOND) old |= 0x80; // set to seconds + + // set the watchdog timer units + wdt_timer_units(old); + + close_io_config(); + spin_unlock(&io_lock); +} + +/* shutdown the watchdog */ + +static void wb_smsc_wdt_shutdown(void) +{ + spin_lock(&io_lock); + open_io_config(); + select_io_device(IODEV_NO); + + // disable the watchdog + gpio_bit13(0x09); + gpio_bit12(0x09); + + // reset watchdog config register + wdt_timer_conf(0x00); + + // reset watchdog control register + wdt_timer_ctrl(0x00); + + // disable timeout + wdt_timeout_value(0x00); + + close_io_config(); + spin_unlock(&io_lock); +} + +/* set timeout => enable watchdog */ + +static void wb_smsc_wdt_set_timeout(unsigned char new_timeout) +{ + spin_lock(&io_lock); + open_io_config(); + select_io_device(IODEV_NO); + + // set Power LED to blink, if we enable the timeout + wdt_timer_ctrl((new_timeout == 0) ? 0x00 : 0x02); + + // set timeout value + wdt_timeout_value(new_timeout); + + close_io_config(); + spin_unlock(&io_lock); +} + +/* get timeout */ + +static unsigned char wb_smsc_wdt_get_timeout(void) +{ + unsigned char set_timeout; + + spin_lock(&io_lock); + open_io_config(); + select_io_device(IODEV_NO); + set_timeout = read_io_cr(0xF2); + close_io_config(); + spin_unlock(&io_lock); + + return set_timeout; +} + +/* disable watchdog */ + +static void wb_smsc_wdt_disable(void) +{ + // set the timeout to 0 to disable the watchdog + wb_smsc_wdt_set_timeout(0); +} + +/* enable watchdog by setting the current timeout */ + +static void wb_smsc_wdt_enable(void) +{ + // set the current timeout... + wb_smsc_wdt_set_timeout(timeout); +} + +/* reset the timer */ + +static void wb_smsc_wdt_reset_timer(void) +{ + spin_lock(&io_lock); + open_io_config(); + select_io_device(IODEV_NO); + + // reset the timer + wdt_timeout_value(timeout); + wdt_timer_conf(0x08); + + close_io_config(); + spin_unlock(&io_lock); +} + +/* return, if the watchdog is enabled (timeout is set...) */ + +static int wb_smsc_wdt_status(void) +{ + return (wb_smsc_wdt_get_timeout() == 0) ? 0 : WDIOF_KEEPALIVEPING; +} + + +/* -- File operations -------------------------------------------*/ + +/* open => enable watchdog and set initial timeout */ + +static int wb_smsc_wdt_open(struct inode *inode, struct file *file) +{ + /* /dev/watchdog can only be opened once */ + + if (test_and_set_bit(0, &timer_enabled)) + return -EBUSY; + + if (nowayout) + __module_get(THIS_MODULE); + + /* Reload and activate timer */ + wb_smsc_wdt_enable(); + + printk(KERN_INFO MODNAME "Watchdog enabled. Timeout set to %d %s.\n", timeout, (unit == UNIT_SECOND) ? "second(s)" : "minute(s)"); + + return nonseekable_open(inode, file); +} + +/* close => shut off the timer */ + +static int wb_smsc_wdt_release(struct inode *inode, struct file *file) +{ + /* Shut off the timer. */ + + if (expect_close == 42) { + wb_smsc_wdt_disable(); + printk(KERN_INFO MODNAME "Watchdog disabled, sleeping again...\n"); + } else { + printk(KERN_CRIT MODNAME "Unexpected close, not stopping watchdog!\n"); + wb_smsc_wdt_reset_timer(); + } + + clear_bit(0, &timer_enabled); + expect_close = 0; + return 0; +} + +/* write => update the timer to keep the machine alive */ + +static ssize_t wb_smsc_wdt_write(struct file *file, const char __user *data, + size_t len, loff_t *ppos) +{ + /* See if we got the magic character 'V' and reload the timer */ + if (len) { + if (!nowayout) { + size_t i; + + /* reset expect flag */ + expect_close = 0; + + /* scan to see whether or not we got the magic character */ + for (i = 0; i != len; i++) { + char c; + if (get_user(c, data+i)) + return -EFAULT; + if (c == 'V') + expect_close = 42; + } + } + + /* someone wrote to us, we should reload the timer */ + wb_smsc_wdt_reset_timer(); + } + return len; +} + +/* ioctl => control interface */ + +static int wb_smsc_wdt_ioctl(struct inode *inode, struct file *file, + unsigned int cmd, unsigned long arg) +{ + int new_timeout; + + union { + struct watchdog_info __user *ident; + int __user *i; + } uarg; + + static struct watchdog_info ident = { + .options = WDIOF_KEEPALIVEPING | + WDIOF_SETTIMEOUT | + WDIOF_MAGICCLOSE, + .firmware_version = 0, + .identity = "SMsC 37B787 Watchdog" + }; + + uarg.i = (int __user *)arg; + + switch (cmd) { + default: + return -ENOTTY; + + case WDIOC_GETSUPPORT: + return copy_to_user(uarg.ident, &ident, + sizeof(ident)) ? -EFAULT : 0; + + case WDIOC_GETSTATUS: + return put_user(wb_smsc_wdt_status(), uarg.i); + + case WDIOC_GETBOOTSTATUS: + return put_user(0, uarg.i); + + case WDIOC_KEEPALIVE: + wb_smsc_wdt_reset_timer(); + return 0; + + case WDIOC_SETTIMEOUT: + if (get_user(new_timeout, uarg.i)) + return -EFAULT; + + // the API states this is given in secs + if (unit == UNIT_MINUTE) + new_timeout /= 60; + + if (new_timeout < 0 || new_timeout > MAX_TIMEOUT) + return -EINVAL; + + timeout = new_timeout; + wb_smsc_wdt_set_timeout(timeout); + + // fall through and return the new timeout... + + case WDIOC_GETTIMEOUT: + + new_timeout = timeout; + + if (unit == UNIT_MINUTE) + new_timeout *= 60; + + return put_user(new_timeout, uarg.i); + + case WDIOC_SETOPTIONS: + { + int options, retval = -EINVAL; + + if (get_user(options, uarg.i)) + return -EFAULT; + + if (options & WDIOS_DISABLECARD) { + wb_smsc_wdt_disable(); + retval = 0; + } + + if (options & WDIOS_ENABLECARD) { + wb_smsc_wdt_enable(); + retval = 0; + } + + return retval; + } + } +} + +/* -- Notifier funtions -----------------------------------------*/ + +static int wb_smsc_wdt_notify_sys(struct notifier_block *this, unsigned long code, void *unused) +{ + if (code == SYS_DOWN || code == SYS_HALT) + { + // set timeout to 0, to avoid possible race-condition + timeout = 0; + wb_smsc_wdt_disable(); + } + return NOTIFY_DONE; +} + +/* -- Module's structures ---------------------------------------*/ + +static struct file_operations wb_smsc_wdt_fops = +{ + .owner = THIS_MODULE, + .llseek = no_llseek, + .write = wb_smsc_wdt_write, + .ioctl = wb_smsc_wdt_ioctl, + .open = wb_smsc_wdt_open, + .release = wb_smsc_wdt_release, +}; + +static struct notifier_block wb_smsc_wdt_notifier = +{ + .notifier_call = wb_smsc_wdt_notify_sys, +}; + +static struct miscdevice wb_smsc_wdt_miscdev = +{ + .minor = WATCHDOG_MINOR, + .name = "watchdog", + .fops = &wb_smsc_wdt_fops, +}; + +/* -- Module init functions -------------------------------------*/ + +/* module's "constructor" */ + +static int __init wb_smsc_wdt_init(void) +{ + int ret; + + spin_lock_init(&io_lock); + + printk("SMsC 37B787 watchdog component driver " VERSION " initialising...\n"); + + if (!request_region(IOPORT, IOPORT_SIZE, "SMsC 37B787 watchdog")) { + printk(KERN_ERR MODNAME "Unable to register IO port %#x\n", IOPORT); + ret = -EBUSY; + goto out_pnp; + } + + // set new maximum, if it's too big + if (timeout > MAX_TIMEOUT) + timeout = MAX_TIMEOUT; + + // init the watchdog timer + wb_smsc_wdt_initialize(); + + ret = register_reboot_notifier(&wb_smsc_wdt_notifier); + if (ret) { + printk(KERN_ERR MODNAME "Unable to register reboot notifier err = %d\n", ret); + goto out_io; + } + + ret = misc_register(&wb_smsc_wdt_miscdev); + if (ret) { + printk(KERN_ERR MODNAME "Unable to register miscdev on minor %d\n", WATCHDOG_MINOR); + goto out_rbt; + } + + // output info + printk(KERN_INFO MODNAME "Timeout set to %d %s.\n", timeout, (unit == UNIT_SECOND) ? "second(s)" : "minute(s)"); + printk(KERN_INFO MODNAME "Watchdog initialized and sleeping (nowayout=%d)...\n", nowayout); + + // ret = 0 + +out_clean: + return ret; + +out_rbt: + unregister_reboot_notifier(&wb_smsc_wdt_notifier); + +out_io: + release_region(IOPORT, IOPORT_SIZE); + +out_pnp: + goto out_clean; +} + +/* module's "destructor" */ + +static void __exit wb_smsc_wdt_exit(void) +{ + /* Stop the timer before we leave */ + if (!nowayout) + { + wb_smsc_wdt_shutdown(); + printk(KERN_INFO MODNAME "Watchdog disabled.\n"); + } + + misc_deregister(&wb_smsc_wdt_miscdev); + unregister_reboot_notifier(&wb_smsc_wdt_notifier); + release_region(IOPORT, IOPORT_SIZE); + + printk("SMsC 37B787 watchdog component driver removed.\n"); +} + +module_init(wb_smsc_wdt_init); +module_exit(wb_smsc_wdt_exit); + +MODULE_AUTHOR("Sven Anders "); +MODULE_DESCRIPTION("Driver for SMsC 37B787 watchdog component (Version " VERSION ")"); +MODULE_LICENSE("GPL"); + +MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR); + +#ifdef SMSC_SUPPORT_MINUTES +module_param(unit, int, 0); +MODULE_PARM_DESC(unit, "set unit to use, 0=seconds or 1=minutes, default is 0"); +#endif + +module_param(timeout, int, 0); +MODULE_PARM_DESC(timeout, "range is 1-255 units, default is 60"); + +module_param(nowayout, int, 0); +MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default=CONFIG_WATCHDOG_NOWAYOUT)"); diff --git a/drivers/char/watchdog/softdog.c b/drivers/char/watchdog/softdog.c index ef8da51..4067e1f 100644 --- a/drivers/char/watchdog/softdog.c +++ b/drivers/char/watchdog/softdog.c @@ -203,7 +203,7 @@ static int softdog_ioctl(struct inode *i }; switch (cmd) { default: - return -ENOIOCTLCMD; + return -ENOTTY; case WDIOC_GETSUPPORT: return copy_to_user(argp, &ident, sizeof(ident)) ? -EFAULT : 0; diff --git a/drivers/char/watchdog/w83627hf_wdt.c b/drivers/char/watchdog/w83627hf_wdt.c index 13f16d4..07d4bff 100644 --- a/drivers/char/watchdog/w83627hf_wdt.c +++ b/drivers/char/watchdog/w83627hf_wdt.c @@ -33,6 +33,7 @@ #include #include #include #include +#include #include #include @@ -44,6 +45,7 @@ #define WATCHDOG_TIMEOUT 60 /* 60 sec d static unsigned long wdt_is_open; static char expect_close; +static spinlock_t io_lock; /* You must set this - there is no sane way to probe for this board. */ static int wdt_io = 0x2E; @@ -110,12 +112,16 @@ w83627hf_init(void) static void wdt_ctrl(int timeout) { + spin_lock(&io_lock); + w83627hf_select_wd_register(); outb_p(0xF6, WDT_EFER); /* Select CRF6 */ outb_p(timeout, WDT_EFDR); /* Write Timeout counter to CRF6 */ w83627hf_unselect_wd_register(); + + spin_unlock(&io_lock); } static int @@ -223,7 +229,7 @@ wdt_ioctl(struct inode *inode, struct fi } default: - return -ENOIOCTLCMD; + return -ENOTTY; } return 0; } @@ -303,6 +309,8 @@ wdt_init(void) { int ret; + spin_lock_init(&io_lock); + printk(KERN_INFO "WDT driver for the Winbond(TM) W83627HF Super I/O chip initialising.\n"); if (wdt_set_heartbeat(timeout)) { diff --git a/drivers/char/watchdog/w83697hf_wdt.c b/drivers/char/watchdog/w83697hf_wdt.c new file mode 100644 index 0000000..32710a9 --- /dev/null +++ b/drivers/char/watchdog/w83697hf_wdt.c @@ -0,0 +1,375 @@ +/* + * w83697hf WDT driver + * + * (c) Copyright 2006 Marcus Junker + * + * Based on w83627hf_wdt.c advantechwdt.c which is based on wdt.c. + * Original copyright messages: + * + * (c) Copyright 2003 Pádraig Brady + * + * (c) Copyright 2000-2001 Marek Michalkiewicz + * + * (c) Copyright 1996 Alan Cox , All Rights Reserved. + * http://www.redhat.com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + * + * Neither Marcus Junker nor ANDURAS AG admit liability nor provide + * warranty for any of this software. This material is provided + * "AS-IS" and at no charge. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#define WATCHDOG_NAME "w83697hf WDT" +#define PFX WATCHDOG_NAME ": " +#define WATCHDOG_TIMEOUT 60 /* 60 sec default timeout */ + +static unsigned long wdt_is_open; +static char expect_close; +static spinlock_t io_lock; + +/* You must set this - there is no sane way to probe for this board. */ +static int wdt_io = 0x2E; +module_param(wdt_io, int, 0); +MODULE_PARM_DESC(wdt_io, "w83697hf WDT io port (default 0x2E)"); + +static int timeout = WATCHDOG_TIMEOUT; /* in seconds */ +module_param(timeout, int, 0); +MODULE_PARM_DESC(timeout, "Watchdog timeout in seconds. 1<= timeout <=63, default=" __MODULE_STRING(WATCHDOG_TIMEOUT) "."); + +static int nowayout = WATCHDOG_NOWAYOUT; +module_param(nowayout, int, 0); +MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default=CONFIG_WATCHDOG_NOWAYOUT)"); + +/* + * Kernel methods. + */ + +#define WDT_EFER (wdt_io+0) /* Extended Function Enable Registers */ +#define WDT_EFIR (wdt_io+0) /* Extended Function Index Register (same as EFER) */ +#define WDT_EFDR (WDT_EFIR+1) /* Extended Function Data Register */ + +static void +w83697hf_select_wd_register(void) +{ + outb_p(0x87, WDT_EFER); /* Enter extended function mode */ + outb_p(0x87, WDT_EFER); /* Again according to manual */ + + outb_p(0x29, WDT_EFER); /* select CR29 */ + outb_p(0x20, WDT_EFDR); /* select WDTO */ + + outb_p(0x07, WDT_EFER); /* point to logical device number reg */ + outb_p(0x08, WDT_EFDR); /* select logical device 8 (GPIO2) */ + outb_p(0x30, WDT_EFER); /* select CR30 */ + outb_p(0x01, WDT_EFDR); /* set bit 0 to activate GPIO2 */ +} + +static void +w83697hf_unselect_wd_register(void) +{ + outb_p(0xAA, WDT_EFER); /* Leave extended function mode */ +} + +/* tyan motherboards seem to set F5 to 0x4C ? + * So explicitly init to appropriate value. */ +static void +w83697hf_init(void) +{ + unsigned char t; + + w83697hf_select_wd_register(); + + outb_p(0xF3, WDT_EFER); /* Select CRF3 */ + + t=inb_p(WDT_EFDR); /* read CRF6 */ + if (t != 0) { + printk (KERN_INFO PFX "Watchdog already running. Resetting timeout to %d sec\n", timeout); + outb_p(timeout, WDT_EFDR); /* Write back to CRF6 */ + } + outb_p(0xF4, WDT_EFER); /* Select CRF4 */ + t=inb_p(WDT_EFDR); /* read CRF4 */ + t&=~0x0C; /* set second mode & disable keyboard turning off watchdog */ + outb_p(t, WDT_EFDR); /* Write back to CRF5 */ + + w83697hf_unselect_wd_register(); +} + +static void +wdt_ctrl(int timeout) +{ + spin_lock(&io_lock); + + w83697hf_select_wd_register(); + + outb_p(0xF4, WDT_EFER); /* Select CRF4 */ + outb_p(timeout, WDT_EFDR); /* Write Timeout counter to CRF4 */ + + w83697hf_unselect_wd_register(); + + spin_unlock(&io_lock); +} + +static int +wdt_ping(void) +{ + wdt_ctrl(timeout); + return 0; +} + +static int +wdt_disable(void) +{ + wdt_ctrl(0); + return 0; +} + +static int +wdt_set_heartbeat(int t) +{ + if ((t < 1) || (t > 63)) + return -EINVAL; + + timeout = t; + return 0; +} + +static ssize_t +wdt_write(struct file *file, const char __user *buf, size_t count, loff_t *ppos) +{ + if (count) { + if (!nowayout) { + size_t i; + + expect_close = 0; + + for (i = 0; i != count; i++) { + char c; + if (get_user(c, buf+i)) + return -EFAULT; + if (c == 'V') + expect_close = 42; + } + } + wdt_ping(); + } + return count; +} + +static int +wdt_ioctl(struct inode *inode, struct file *file, unsigned int cmd, + unsigned long arg) +{ + void __user *argp = (void __user *)arg; + int __user *p = argp; + int new_timeout; + static struct watchdog_info ident = { + .options = WDIOF_KEEPALIVEPING | WDIOF_SETTIMEOUT | WDIOF_MAGICCLOSE, + .firmware_version = 1, + .identity = "W83697HF WDT", + }; + + switch (cmd) { + case WDIOC_GETSUPPORT: + if (copy_to_user(argp, &ident, sizeof(ident))) + return -EFAULT; + break; + + case WDIOC_GETSTATUS: + case WDIOC_GETBOOTSTATUS: + return put_user(0, p); + + case WDIOC_KEEPALIVE: + wdt_ping(); + break; + + case WDIOC_SETTIMEOUT: + if (get_user(new_timeout, p)) + return -EFAULT; + if (wdt_set_heartbeat(new_timeout)) + return -EINVAL; + wdt_ping(); + /* Fall */ + + case WDIOC_GETTIMEOUT: + return put_user(timeout, p); + + case WDIOC_SETOPTIONS: + { + int options, retval = -EINVAL; + + if (get_user(options, p)) + return -EFAULT; + + if (options & WDIOS_DISABLECARD) { + wdt_disable(); + retval = 0; + } + + if (options & WDIOS_ENABLECARD) { + wdt_ping(); + retval = 0; + } + + return retval; + } + + default: + return -ENOTTY; + } + return 0; +} + +static int +wdt_open(struct inode *inode, struct file *file) +{ + if (test_and_set_bit(0, &wdt_is_open)) + return -EBUSY; + /* + * Activate + */ + + wdt_ping(); + return nonseekable_open(inode, file); +} + +static int +wdt_close(struct inode *inode, struct file *file) +{ + if (expect_close == 42) { + wdt_disable(); + } else { + printk(KERN_CRIT PFX "Unexpected close, not stopping watchdog!\n"); + wdt_ping(); + } + expect_close = 0; + clear_bit(0, &wdt_is_open); + return 0; +} + +/* + * Notifier for system down + */ + +static int +wdt_notify_sys(struct notifier_block *this, unsigned long code, + void *unused) +{ + if (code == SYS_DOWN || code == SYS_HALT) { + /* Turn the WDT off */ + wdt_disable(); + } + return NOTIFY_DONE; +} + +/* + * Kernel Interfaces + */ + +static struct file_operations wdt_fops = { + .owner = THIS_MODULE, + .llseek = no_llseek, + .write = wdt_write, + .ioctl = wdt_ioctl, + .open = wdt_open, + .release = wdt_close, +}; + +static struct miscdevice wdt_miscdev = { + .minor = WATCHDOG_MINOR, + .name = "watchdog", + .fops = &wdt_fops, +}; + +/* + * The WDT needs to learn about soft shutdowns in order to + * turn the timebomb registers off. + */ + +static struct notifier_block wdt_notifier = { + .notifier_call = wdt_notify_sys, +}; + +static int __init +wdt_init(void) +{ + int ret; + + spin_lock_init(&io_lock); + + printk(KERN_INFO "WDT driver for the Winbond(TM) W83697HF Super I/O chip initialising.\n"); + + if (wdt_set_heartbeat(timeout)) { + wdt_set_heartbeat(WATCHDOG_TIMEOUT); + printk (KERN_INFO PFX "timeout value must be 1<=timeout<=63, using %d\n", + WATCHDOG_TIMEOUT); + } + + if (!request_region(wdt_io, 1, WATCHDOG_NAME)) { + printk (KERN_ERR PFX "I/O address 0x%04x already in use\n", + wdt_io); + ret = -EIO; + goto out; + } + + w83697hf_init(); + + ret = register_reboot_notifier(&wdt_notifier); + if (ret != 0) { + printk (KERN_ERR PFX "cannot register reboot notifier (err=%d)\n", + ret); + goto unreg_regions; + } + + ret = misc_register(&wdt_miscdev); + if (ret != 0) { + printk (KERN_ERR PFX "cannot register miscdev on minor=%d (err=%d)\n", + WATCHDOG_MINOR, ret); + goto unreg_reboot; + } + + printk (KERN_INFO PFX "initialized. timeout=%d sec (nowayout=%d)\n", + timeout, nowayout); + +out: + return ret; +unreg_reboot: + unregister_reboot_notifier(&wdt_notifier); +unreg_regions: + release_region(wdt_io, 1); + goto out; +} + +static void __exit +wdt_exit(void) +{ + misc_deregister(&wdt_miscdev); + unregister_reboot_notifier(&wdt_notifier); + release_region(wdt_io,1); +} + +module_init(wdt_init); +module_exit(wdt_exit); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Marcus Junker "); +MODULE_DESCRIPTION("w83697hf WDT driver"); +MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR); diff --git a/drivers/char/watchdog/w83877f_wdt.c b/drivers/char/watchdog/w83877f_wdt.c index ccf6c09..b0e5f84 100644 --- a/drivers/char/watchdog/w83877f_wdt.c +++ b/drivers/char/watchdog/w83877f_wdt.c @@ -252,7 +252,7 @@ static int fop_ioctl(struct inode *inode switch(cmd) { default: - return -ENOIOCTLCMD; + return -ENOTTY; case WDIOC_GETSUPPORT: return copy_to_user(argp, &ident, sizeof(ident))?-EFAULT:0; case WDIOC_GETSTATUS: diff --git a/drivers/char/watchdog/w83977f_wdt.c b/drivers/char/watchdog/w83977f_wdt.c index 98f4e17..2c8d5d8 100644 --- a/drivers/char/watchdog/w83977f_wdt.c +++ b/drivers/char/watchdog/w83977f_wdt.c @@ -393,7 +393,7 @@ static int wdt_ioctl(struct inode *inode switch(cmd) { default: - return -ENOIOCTLCMD; + return -ENOTTY; case WDIOC_GETSUPPORT: return copy_to_user(uarg.ident, &ident, sizeof(ident)) ? -EFAULT : 0; diff --git a/drivers/char/watchdog/wafer5823wdt.c b/drivers/char/watchdog/wafer5823wdt.c index 2bb6a9d..163e028 100644 --- a/drivers/char/watchdog/wafer5823wdt.c +++ b/drivers/char/watchdog/wafer5823wdt.c @@ -174,7 +174,7 @@ static int wafwdt_ioctl(struct inode *in } default: - return -ENOIOCTLCMD; + return -ENOTTY; } return 0; } diff --git a/drivers/char/watchdog/wdrtas.c b/drivers/char/watchdog/wdrtas.c index 5c38cdf..1d64e27 100644 --- a/drivers/char/watchdog/wdrtas.c +++ b/drivers/char/watchdog/wdrtas.c @@ -385,7 +385,7 @@ wdrtas_ioctl(struct inode *inode, struct return put_user(wdrtas_interval, argp); default: - return -ENOIOCTLCMD; + return -ENOTTY; } } diff --git a/drivers/char/watchdog/wdt.c b/drivers/char/watchdog/wdt.c index 70be81e..13f23f4 100644 --- a/drivers/char/watchdog/wdt.c +++ b/drivers/char/watchdog/wdt.c @@ -341,7 +341,7 @@ #endif /* CONFIG_WDT_501 */ switch(cmd) { default: - return -ENOIOCTLCMD; + return -ENOTTY; case WDIOC_GETSUPPORT: return copy_to_user(argp, &ident, sizeof(ident))?-EFAULT:0; diff --git a/drivers/char/watchdog/wdt285.c b/drivers/char/watchdog/wdt285.c index 6555fb8..89a249e 100644 --- a/drivers/char/watchdog/wdt285.c +++ b/drivers/char/watchdog/wdt285.c @@ -137,7 +137,7 @@ watchdog_ioctl(struct inode *inode, stru unsigned long arg) { unsigned int new_margin; - int ret = -ENOIOCTLCMD; + int ret = -ENOTTY; switch(cmd) { case WDIOC_GETSUPPORT: diff --git a/drivers/char/watchdog/wdt977.c b/drivers/char/watchdog/wdt977.c index a0935bc..6253041 100644 --- a/drivers/char/watchdog/wdt977.c +++ b/drivers/char/watchdog/wdt977.c @@ -361,7 +361,7 @@ static int wdt977_ioctl(struct inode *in switch(cmd) { default: - return -ENOIOCTLCMD; + return -ENOTTY; case WDIOC_GETSUPPORT: return copy_to_user(uarg.ident, &ident, diff --git a/drivers/char/watchdog/wdt_pci.c b/drivers/char/watchdog/wdt_pci.c index 5918ca2..74d8cf8 100644 --- a/drivers/char/watchdog/wdt_pci.c +++ b/drivers/char/watchdog/wdt_pci.c @@ -386,7 +386,7 @@ #endif /* CONFIG_WDT_501_PCI */ switch(cmd) { default: - return -ENOIOCTLCMD; + return -ENOTTY; case WDIOC_GETSUPPORT: return copy_to_user(argp, &ident, sizeof(ident))?-EFAULT:0; diff --git a/include/asm-arm/arch-pnx4008/clock.h b/include/asm-arm/arch-pnx4008/clock.h index 91ae003..ce155e1 100644 --- a/include/asm-arm/arch-pnx4008/clock.h +++ b/include/asm-arm/arch-pnx4008/clock.h @@ -32,6 +32,7 @@ #define I2CCLKCTRL_REG (PWRMAN_VA_BASE #define KEYCLKCTRL_REG (PWRMAN_VA_BASE + 0xb0) #define TSCLKCTRL_REG (PWRMAN_VA_BASE + 0xb4) #define PWMCLKCTRL_REG (PWRMAN_VA_BASE + 0xb8) +#define TIMCLKCTRL_REG (PWRMAN_VA_BASE + 0xbc) #define SPICTRL_REG (PWRMAN_VA_BASE + 0xc4) #define FLASHCLKCTRL_REG (PWRMAN_VA_BASE + 0xc8) #define UART3CLK_REG (PWRMAN_VA_BASE + 0xd0)