*
* The timer is implemented in the following I/O controller hubs:
* (See the intel documentation on http://developer.intel.com.)
- * 6300ESB chip : document number 300641-003
+ * 6300ESB chip : document number 300641-004
*
* 2004YYZZ Ross Biro
* Initial version 0.01
#include <linux/mm.h>
#include <linux/miscdevice.h>
#include <linux/watchdog.h>
-#include <linux/reboot.h>
+#include <linux/platform_device.h>
#include <linux/init.h>
#include <linux/pci.h>
#include <linux/ioport.h>
#include <linux/io.h>
/* Module and version information */
-#define ESB_VERSION "0.03"
+#define ESB_VERSION "0.04"
#define ESB_MODULE_NAME "i6300ESB timer"
#define ESB_DRIVER_NAME ESB_MODULE_NAME ", v" ESB_VERSION
#define PFX ESB_MODULE_NAME ": "
#define ESB_LOCK_REG 0x68 /* WDT lock register */
/* Memory mapped registers */
-#define ESB_TIMER1_REG BASEADDR + 0x00 /* Timer1 value after each reset */
-#define ESB_TIMER2_REG BASEADDR + 0x04 /* Timer2 value after each reset */
-#define ESB_GINTSR_REG BASEADDR + 0x08 /* General Interrupt Status Register */
-#define ESB_RELOAD_REG BASEADDR + 0x0c /* Reload register */
+#define ESB_TIMER1_REG (BASEADDR + 0x00)/* Timer1 value after each reset */
+#define ESB_TIMER2_REG (BASEADDR + 0x04)/* Timer2 value after each reset */
+#define ESB_GINTSR_REG (BASEADDR + 0x08)/* General Interrupt Status Register */
+#define ESB_RELOAD_REG (BASEADDR + 0x0c)/* Reload register */
/* Lock register bits */
#define ESB_WDT_FUNC (0x01 << 2) /* Watchdog functionality */
#define ESB_WDT_INTTYPE (0x11 << 0) /* Interrupt type on timer1 timeout */
/* Reload register bits */
+#define ESB_WDT_TIMEOUT (0x01 << 9) /* Watchdog timed out */
#define ESB_WDT_RELOAD (0x01 << 8) /* prevent timeout */
/* Magic constants */
static struct pci_dev *esb_pci;
static unsigned short triggered; /* The status of the watchdog upon boot */
static char esb_expect_close;
+static struct platform_device *esb_platform_device;
/* module parameters */
/* 30 sec default heartbeat (1 < heartbeat < 2*1023) */
#define WATCHDOG_HEARTBEAT 30
static int heartbeat = WATCHDOG_HEARTBEAT; /* in seconds */
-
module_param(heartbeat, int, 0);
MODULE_PARM_DESC(heartbeat,
"Watchdog heartbeat in seconds. (1<heartbeat<2046, default="
writeb(ESB_UNLOCK2, ESB_RELOAD_REG);
}
-static void esb_timer_start(void)
+static int esb_timer_start(void)
{
u8 val;
+ spin_lock(&esb_lock);
+ esb_unlock_registers();
+ writew(ESB_WDT_RELOAD, ESB_RELOAD_REG);
/* Enable or Enable + Lock? */
- val = 0x02 | (nowayout ? 0x01 : 0x00);
+ val = ESB_WDT_ENABLE | (nowayout ? ESB_WDT_LOCK : 0x00);
pci_write_config_byte(esb_pci, ESB_LOCK_REG, val);
+ spin_unlock(&esb_lock);
+ return 0;
}
static int esb_timer_stop(void)
spin_unlock(&esb_lock);
/* Returns 0 if the timer was disabled, non-zero otherwise */
- return (val & 0x01);
+ return val & ESB_WDT_ENABLE;
}
static void esb_timer_keepalive(void)
return 0;
}
-static int esb_timer_read(void)
-{
- u32 count;
-
- /* This isn't documented, and doesn't take into
- * acount which stage is running, but it looks
- * like a 20 bit count down, so we might as well report it.
- */
- pci_read_config_dword(esb_pci, 0x64, &count);
- return (int)count;
-}
-
/*
* /dev/watchdog handling
*/
return -EBUSY;
/* Reload and activate timer */
- esb_timer_keepalive();
esb_timer_start();
return nonseekable_open(inode, file);
sizeof(ident)) ? -EFAULT : 0;
case WDIOC_GETSTATUS:
- return put_user(esb_timer_read(), p);
+ return put_user(0, p);
case WDIOC_GETBOOTSTATUS:
return put_user(triggered, p);
}
if (new_options & WDIOS_ENABLECARD) {
- esb_timer_keepalive();
esb_timer_start();
retval = 0;
}
}
/*
- * Notify system
- */
-
-static int esb_notify_sys(struct notifier_block *this,
- unsigned long code, void *unused)
-{
- if (code == SYS_DOWN || code == SYS_HALT)
- esb_timer_stop(); /* Turn the WDT off */
-
- return NOTIFY_DONE;
-}
-
-/*
* Kernel Interfaces
*/
.fops = &esb_fops,
};
-static struct notifier_block esb_notifier = {
- .notifier_call = esb_notify_sys,
-};
-
/*
* Data for PCI driver interface
*
* Init & exit routines
*/
-static unsigned char __init esb_getdevice(void)
+static unsigned char __devinit esb_getdevice(void)
{
- u8 val1;
- unsigned short val2;
/*
* Find the PCI device
*/
esb_pci = pci_get_device(PCI_VENDOR_ID_INTEL,
PCI_DEVICE_ID_INTEL_ESB_9, NULL);
- if (esb_pci) {
- if (pci_enable_device(esb_pci)) {
- printk(KERN_ERR PFX "failed to enable device\n");
- goto err_devput;
- }
+ if (!esb_pci)
+ return 0;
- if (pci_request_region(esb_pci, 0, ESB_MODULE_NAME)) {
- printk(KERN_ERR PFX "failed to request region\n");
- goto err_disable;
- }
+ if (pci_enable_device(esb_pci)) {
+ printk(KERN_ERR PFX "failed to enable device\n");
+ goto err_devput;
+ }
- BASEADDR = pci_ioremap_bar(esb_pci, 0);
- if (BASEADDR == NULL) {
- /* Something's wrong here, BASEADDR has to be set */
- printk(KERN_ERR PFX "failed to get BASEADDR\n");
- goto err_release;
- }
+ if (pci_request_region(esb_pci, 0, ESB_MODULE_NAME)) {
+ printk(KERN_ERR PFX "failed to request region\n");
+ goto err_disable;
+ }
- /*
- * The watchdog has two timers, it can be setup so that the
- * expiry of timer1 results in an interrupt and the expiry of
- * timer2 results in a reboot. We set it to not generate
- * any interrupts as there is not much we can do with it
- * right now.
- *
- * We also enable reboots and set the timer frequency to
- * the PCI clock divided by 2^15 (approx 1KHz).
- */
- pci_write_config_word(esb_pci, ESB_CONFIG_REG, 0x0003);
-
- /* Check that the WDT isn't already locked */
- pci_read_config_byte(esb_pci, ESB_LOCK_REG, &val1);
- if (val1 & ESB_WDT_LOCK)
- printk(KERN_WARNING PFX "nowayout already set\n");
-
- /* Set the timer to watchdog mode and disable it for now */
- pci_write_config_byte(esb_pci, ESB_LOCK_REG, 0x00);
-
- /* Check if the watchdog was previously triggered */
- esb_unlock_registers();
- val2 = readw(ESB_RELOAD_REG);
- triggered = (val2 & (0x01 << 9) >> 9);
-
- /* Reset trigger flag and timers */
- esb_unlock_registers();
- writew((0x11 << 8), ESB_RELOAD_REG);
-
- /* Done */
- return 1;
+ BASEADDR = pci_ioremap_bar(esb_pci, 0);
+ if (BASEADDR == NULL) {
+ /* Something's wrong here, BASEADDR has to be set */
+ printk(KERN_ERR PFX "failed to get BASEADDR\n");
+ goto err_release;
+ }
+
+ /* Done */
+ return 1;
err_release:
- pci_release_region(esb_pci, 0);
+ pci_release_region(esb_pci, 0);
err_disable:
- pci_disable_device(esb_pci);
+ pci_disable_device(esb_pci);
err_devput:
- pci_dev_put(esb_pci);
- }
+ pci_dev_put(esb_pci);
return 0;
}
-static int __init watchdog_init(void)
+static void __devinit esb_initdevice(void)
+{
+ u8 val1;
+ u16 val2;
+
+ /*
+ * Config register:
+ * Bit 5 : 0 = Enable WDT_OUTPUT
+ * Bit 2 : 0 = set the timer frequency to the PCI clock
+ * divided by 2^15 (approx 1KHz).
+ * Bits 1:0 : 11 = WDT_INT_TYPE Disabled.
+ * The watchdog has two timers, it can be setup so that the
+ * expiry of timer1 results in an interrupt and the expiry of
+ * timer2 results in a reboot. We set it to not generate
+ * any interrupts as there is not much we can do with it
+ * right now.
+ */
+ pci_write_config_word(esb_pci, ESB_CONFIG_REG, 0x0003);
+
+ /* Check that the WDT isn't already locked */
+ pci_read_config_byte(esb_pci, ESB_LOCK_REG, &val1);
+ if (val1 & ESB_WDT_LOCK)
+ printk(KERN_WARNING PFX "nowayout already set\n");
+
+ /* Set the timer to watchdog mode and disable it for now */
+ pci_write_config_byte(esb_pci, ESB_LOCK_REG, 0x00);
+
+ /* Check if the watchdog was previously triggered */
+ esb_unlock_registers();
+ val2 = readw(ESB_RELOAD_REG);
+ if (val2 & ESB_WDT_TIMEOUT)
+ triggered = WDIOF_CARDRESET;
+
+ /* Reset WDT_TIMEOUT flag and timers */
+ esb_unlock_registers();
+ writew((ESB_WDT_TIMEOUT | ESB_WDT_RELOAD), ESB_RELOAD_REG);
+
+ /* And set the correct timeout value */
+ esb_timer_set_heartbeat(heartbeat);
+}
+
+static int __devinit esb_probe(struct platform_device *dev)
{
int ret;
/* Check that the heartbeat value is within it's range;
if not reset to the default */
- if (esb_timer_set_heartbeat(heartbeat)) {
- esb_timer_set_heartbeat(WATCHDOG_HEARTBEAT);
+ if (heartbeat < 0x1 || heartbeat > 2 * 0x03ff) {
+ heartbeat = WATCHDOG_HEARTBEAT;
printk(KERN_INFO PFX
"heartbeat value must be 1<heartbeat<2046, using %d\n",
heartbeat);
}
- ret = register_reboot_notifier(&esb_notifier);
- if (ret != 0) {
- printk(KERN_ERR PFX
- "cannot register reboot notifier (err=%d)\n", ret);
- goto err_unmap;
- }
+ /* Initialize the watchdog and make sure it does not run */
+ esb_initdevice();
+
+ /* Register the watchdog so that userspace has access to it */
ret = misc_register(&esb_miscdev);
if (ret != 0) {
printk(KERN_ERR PFX
"cannot register miscdev on minor=%d (err=%d)\n",
WATCHDOG_MINOR, ret);
- goto err_notifier;
+ goto err_unmap;
}
- esb_timer_stop();
printk(KERN_INFO PFX
"initialized (0x%p). heartbeat=%d sec (nowayout=%d)\n",
BASEADDR, heartbeat, nowayout);
return 0;
-err_notifier:
- unregister_reboot_notifier(&esb_notifier);
err_unmap:
iounmap(BASEADDR);
-/* err_release: */
pci_release_region(esb_pci, 0);
-/* err_disable: */
pci_disable_device(esb_pci);
-/* err_devput: */
pci_dev_put(esb_pci);
return ret;
}
-static void __exit watchdog_cleanup(void)
+static int __devexit esb_remove(struct platform_device *dev)
{
/* Stop the timer before we leave */
if (!nowayout)
/* Deregister */
misc_deregister(&esb_miscdev);
- unregister_reboot_notifier(&esb_notifier);
iounmap(BASEADDR);
pci_release_region(esb_pci, 0);
pci_disable_device(esb_pci);
pci_dev_put(esb_pci);
+ return 0;
+}
+
+static void esb_shutdown(struct platform_device *dev)
+{
+ esb_timer_stop();
+}
+
+static struct platform_driver esb_platform_driver = {
+ .probe = esb_probe,
+ .remove = __devexit_p(esb_remove),
+ .shutdown = esb_shutdown,
+ .driver = {
+ .owner = THIS_MODULE,
+ .name = ESB_MODULE_NAME,
+ },
+};
+
+static int __init watchdog_init(void)
+{
+ int err;
+
+ printk(KERN_INFO PFX "Intel 6300ESB WatchDog Timer Driver v%s\n",
+ ESB_VERSION);
+
+ err = platform_driver_register(&esb_platform_driver);
+ if (err)
+ return err;
+
+ esb_platform_device = platform_device_register_simple(ESB_MODULE_NAME,
+ -1, NULL, 0);
+ if (IS_ERR(esb_platform_device)) {
+ err = PTR_ERR(esb_platform_device);
+ goto unreg_platform_driver;
+ }
+
+ return 0;
+
+unreg_platform_driver:
+ platform_driver_unregister(&esb_platform_driver);
+ return err;
+}
+
+static void __exit watchdog_cleanup(void)
+{
+ platform_device_unregister(esb_platform_device);
+ platform_driver_unregister(&esb_platform_driver);
+ printk(KERN_INFO PFX "Watchdog Module Unloaded.\n");
}
module_init(watchdog_init);