GIT 3a9eff10075dc88450946e2dee75e78b4dcd8156 git+ssh://master.kernel.org/pub/scm/linux/kernel/git/wim/linux-2.6-watchdog-mm.git commit 3a9eff10075dc88450946e2dee75e78b4dcd8156 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 0559ee9212646e8137595fcee57c6bb98b1b6d26 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 d8a8e606f58d3965379542a1fd830937e53e618d 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 b26489c887b582e1302cdebc6e7a7b54fa2af635 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 ebab17d0f328e6cca0a0d3364a9750d175548e34 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 1c126d45753018b1bc38ba1288a51a0b53784ac6 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 088bbd1fa3df9b766a34a4dea51d773862457738 Author: Matt LaPlante Date: Wed Jul 5 01:20:51 2006 +0000 [WATCHDOG] Kconfig typos fix. Three typos in drivers/char/watchdog/Kconfig... Signed-off-by: Matt LaPlante Signed-off-by: Wim Van Sebroeck commit d483732e5b3c24f3c100eb58ebffc3a27db0352a 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 nclude/linux/watchdog.h . Signed-off-by: Wim Van Sebroeck commit a24457c6231b7fc45eb5804892a83e75506c2a67 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 84f36b1ba4bea91cd76a2e3731b54fae8b618e44 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 f2f4587041171df7b5018fc8749ddae2520e62ee 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 --- Signed-off-by: Andrew Morton --- arch/arm/mach-pnx4008/clock.c | 11 drivers/char/watchdog/Kconfig | 40 + drivers/char/watchdog/Makefile | 2 drivers/char/watchdog/alim1535_wdt.c | 10 drivers/char/watchdog/alim7101_wdt.c | 15 drivers/char/watchdog/i8xx_tco.c | 33 - drivers/char/watchdog/iTCO_wdt.c | 735 +++++++++++++++++++++++++ drivers/char/watchdog/machzwd.c | 3 drivers/char/watchdog/pnx4008_wdt.c | 349 +++++++++++ include/asm-arm/arch-pnx4008/clock.h | 1 10 files changed, 1171 insertions(+), 28 deletions(-) diff -puN arch/arm/mach-pnx4008/clock.c~git-watchdog arch/arm/mach-pnx4008/clock.c --- a/arch/arm/mach-pnx4008/clock.c~git-watchdog +++ a/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 -puN drivers/char/watchdog/Kconfig~git-watchdog drivers/char/watchdog/Kconfig --- a/drivers/char/watchdog/Kconfig~git-watchdog +++ a/drivers/char/watchdog/Kconfig @@ -45,7 +45,7 @@ config WATCHDOG_NOWAYOUT comment "Watchdog Device Drivers" depends on WATCHDOG -# Architecture Independant +# Architecture Independent config SOFT_WATCHDOG tristate "Software watchdog" @@ -127,7 +127,7 @@ config S3C2410_WATCHDOG enabled. The driver is limited by the speed of the system's PCLK - signal, so with reasonbaly fast systems (PCLK around 50-66MHz) + signal, so with reasonably fast systems (PCLK around 50-66MHz) then watchdog intervals of over approximately 20seconds are unavailable. @@ -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 @@ -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 @@ -423,7 +455,7 @@ config SBC_EPX_C3_WATCHDOG is no way to know if writing to its IO address will corrupt your system or have any real effect. The only way to be sure that this driver does what you want is to make sure you - are runnning it on an EPX-C3 from Winsystems with the watchdog + are running it on an EPX-C3 from Winsystems with the watchdog timer at IO address 0x1ee and 0x1ef. It will write to both those IO ports. Basically, the assumption is made that if you compile this driver into your kernel and/or load it as a module, that you @@ -472,7 +504,7 @@ config INDYDOG tristate "Indy/I2 Hardware Watchdog" depends on WATCHDOG && SGI_IP22 help - Hardwaredriver for the Indy's/I2's watchdog. This is a + Hardware driver for the Indy's/I2's watchdog. This is a watchdog timer that will reboot the machine after a 60 second timer expired and no process has written to /dev/watchdog during that time. diff -puN drivers/char/watchdog/Makefile~git-watchdog drivers/char/watchdog/Makefile --- a/drivers/char/watchdog/Makefile~git-watchdog +++ a/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,6 +46,7 @@ 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 diff -puN drivers/char/watchdog/alim1535_wdt.c~git-watchdog drivers/char/watchdog/alim1535_wdt.c --- a/drivers/char/watchdog/alim1535_wdt.c~git-watchdog +++ a/drivers/char/watchdog/alim1535_wdt.c @@ -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 -puN drivers/char/watchdog/alim7101_wdt.c~git-watchdog drivers/char/watchdog/alim7101_wdt.c --- a/drivers/char/watchdog/alim7101_wdt.c~git-watchdog +++ a/drivers/char/watchdog/alim7101_wdt.c @@ -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 -puN drivers/char/watchdog/i8xx_tco.c~git-watchdog drivers/char/watchdog/i8xx_tco.c --- a/drivers/char/watchdog/i8xx_tco.c~git-watchdog +++ a/drivers/char/watchdog/i8xx_tco.c @@ -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 -puN /dev/null drivers/char/watchdog/iTCO_wdt.c --- /dev/null +++ a/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 -ENOIOCTLCMD; + } +} + +/* + * 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 -puN drivers/char/watchdog/machzwd.c~git-watchdog drivers/char/watchdog/machzwd.c --- a/drivers/char/watchdog/machzwd.c~git-watchdog +++ a/drivers/char/watchdog/machzwd.c @@ -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 -puN /dev/null drivers/char/watchdog/pnx4008_wdt.c --- /dev/null +++ a/drivers/char/watchdog/pnx4008_wdt.c @@ -0,0 +1,349 @@ +/* + * 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 + +#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 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) +{ + 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))); + /* 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)); +} + +static void wdt_disable(void) +{ + __raw_writel(0, WDTIM_CTRL(wdt_base)); /*stop counter */ + if (wdt_clk) + clk_set_rate(wdt_clk, 0); +} + +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 = -ENOIOCTLCMD; + 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; + + 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 -puN include/asm-arm/arch-pnx4008/clock.h~git-watchdog include/asm-arm/arch-pnx4008/clock.h --- a/include/asm-arm/arch-pnx4008/clock.h~git-watchdog +++ a/include/asm-arm/arch-pnx4008/clock.h @@ -32,6 +32,7 @@ struct clk; #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) _