From 33c050c586fec34dae36eb314bfc3a2c44654c05 Mon Sep 17 00:00:00 2001 From: Daniel Laird Date: Wed, 5 Nov 2008 16:46:49 +0100 Subject: [WATCHDOG] Add pnx833x_wdt Add support for PNX833x watchdog timer. Signed-off-by: Daniel Laird Signed-off-by: Wim Van Sebroeck --- drivers/watchdog/Kconfig | 9 ++ drivers/watchdog/Makefile | 1 + drivers/watchdog/pnx833x_wdt.c | 282 +++++++++++++++++++++++++++++++++++++++++ 3 files changed, 292 insertions(+) create mode 100644 drivers/watchdog/pnx833x_wdt.c (limited to 'drivers') diff --git a/drivers/watchdog/Kconfig b/drivers/watchdog/Kconfig index b166f2852a64..373c22fd24ea 100644 --- a/drivers/watchdog/Kconfig +++ b/drivers/watchdog/Kconfig @@ -729,6 +729,15 @@ config WDT_MTX1 Hardware driver for the MTX-1 boards. This is a watchdog timer that will reboot the machine after a 100 seconds timer expired. +config PNX833X_WDT + tristate "PNX833x Hardware Watchdog" + depends on SOC_PNX8335 + help + Hardware driver for the PNX833x's watchdog. This is a + watchdog timer that will reboot the machine after a programable + timer has expired and no process has written to /dev/watchdog during + that time. + config WDT_RM9K_GPI tristate "RM9000/GPI hardware watchdog" depends on CPU_RM9000 diff --git a/drivers/watchdog/Makefile b/drivers/watchdog/Makefile index c3afa14d5be1..7231a1500860 100644 --- a/drivers/watchdog/Makefile +++ b/drivers/watchdog/Makefile @@ -101,6 +101,7 @@ obj-$(CONFIG_SBC_EPX_C3_WATCHDOG) += sbc_epx_c3.o obj-$(CONFIG_RC32434_WDT) += rc32434_wdt.o obj-$(CONFIG_INDYDOG) += indydog.o obj-$(CONFIG_WDT_MTX1) += mtx-1_wdt.o +obj-$(CONFIG_PNX833X_WDT) += pnx833x_wdt.o obj-$(CONFIG_WDT_RM9K_GPI) += rm9k_wdt.o obj-$(CONFIG_SIBYTE_WDOG) += sb_wdog.o obj-$(CONFIG_AR7_WDT) += ar7_wdt.o diff --git a/drivers/watchdog/pnx833x_wdt.c b/drivers/watchdog/pnx833x_wdt.c new file mode 100644 index 000000000000..538ec2c05197 --- /dev/null +++ b/drivers/watchdog/pnx833x_wdt.c @@ -0,0 +1,282 @@ +/* + * PNX833x Hardware Watchdog Driver + * Copyright 2008 NXP Semiconductors + * Daniel Laird + * Andre McCurdy + * + * Heavily based upon - IndyDog 0.3 + * A Hardware Watchdog Device for SGI IP22 + * + * (c) Copyright 2002 Guido Guenther , All Rights Reserved. + * + * 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. + * + * based on softdog.c by Alan Cox + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define PFX "pnx833x: " +#define WATCHDOG_TIMEOUT 30 /* 30 sec Maximum timeout */ +#define WATCHDOG_COUNT_FREQUENCY 68000000U /* Watchdog counts at 68MHZ. */ + +/** CONFIG block */ +#define PNX833X_CONFIG (0x07000U) +#define PNX833X_CONFIG_CPU_WATCHDOG (0x54) +#define PNX833X_CONFIG_CPU_WATCHDOG_COMPARE (0x58) +#define PNX833X_CONFIG_CPU_COUNTERS_CONTROL (0x1c) + +/** RESET block */ +#define PNX833X_RESET (0x08000U) +#define PNX833X_RESET_CONFIG (0x08) + +static int pnx833x_wdt_alive; + +/* Set default timeout in MHZ.*/ +static int pnx833x_wdt_timeout = (WATCHDOG_TIMEOUT * WATCHDOG_COUNT_FREQUENCY); +module_param(pnx833x_wdt_timeout, int, 0); +MODULE_PARM_DESC(timeout, "Watchdog timeout in Mhz. (68Mhz clock), default=" + __MODULE_STRING(pnx833x_wdt_timeout) "(30 seconds)."); + +static int nowayout = WATCHDOG_NOWAYOUT; +module_param(nowayout, int, 0); +MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default=" + __MODULE_STRING(WATCHDOG_NOWAYOUT) ")"); + +static int start_enabled = 1; +module_param(start_enabled, int, 0); +MODULE_PARM_DESC(start_enabled, "Watchdog is started on module insertion " + "(default=" __MODULE_STRING(start_enabled) ")"); + +static void pnx833x_wdt_start(void) +{ + /* Enable watchdog causing reset. */ + PNX833X_REG(PNX833X_RESET + PNX833X_RESET_CONFIG) |= 0x1; + /* Set timeout.*/ + PNX833X_REG(PNX833X_CONFIG + + PNX833X_CONFIG_CPU_WATCHDOG_COMPARE) = pnx833x_wdt_timeout; + /* Enable watchdog. */ + PNX833X_REG(PNX833X_CONFIG + + PNX833X_CONFIG_CPU_COUNTERS_CONTROL) |= 0x1; + + printk(KERN_INFO PFX "Started watchdog timer.\n"); +} + +static void pnx833x_wdt_stop(void) +{ + /* Disable watchdog causing reset. */ + PNX833X_REG(PNX833X_RESET + PNX833X_CONFIG) &= 0xFFFFFFFE; + /* Disable watchdog.*/ + PNX833X_REG(PNX833X_CONFIG + + PNX833X_CONFIG_CPU_COUNTERS_CONTROL) &= 0xFFFFFFFE; + + printk(KERN_INFO PFX "Stopped watchdog timer.\n"); +} + +static void pnx833x_wdt_ping(void) +{ + PNX833X_REG(PNX833X_CONFIG + + PNX833X_CONFIG_CPU_WATCHDOG_COMPARE) = pnx833x_wdt_timeout; +} + +/* + * Allow only one person to hold it open + */ +static int pnx833x_wdt_open(struct inode *inode, struct file *file) +{ + if (test_and_set_bit(0, &pnx833x_wdt_alive)) + return -EBUSY; + + if (nowayout) + __module_get(THIS_MODULE); + + /* Activate timer */ + if (!start_enabled) + pnx833x_wdt_start(); + + pnx833x_wdt_ping(); + + printk(KERN_INFO "Started watchdog timer.\n"); + + return nonseekable_open(inode, file); +} + +static int pnx833x_wdt_release(struct inode *inode, struct file *file) +{ + /* Shut off the timer. + * Lock it in if it's a module and we defined ...NOWAYOUT */ + if (!nowayout) + pnx833x_wdt_stop(); /* Turn the WDT off */ + + clear_bit(0, &pnx833x_wdt_alive); + return 0; +} + +static ssize_t pnx833x_wdt_write(struct file *file, const char *data, size_t len, loff_t *ppos) +{ + /* Refresh the timer. */ + if (len) + pnx833x_wdt_ping(); + + return len; +} + +static long pnx833x_wdt_ioctl(struct file *file, unsigned int cmd, + unsigned long arg) +{ + int options, new_timeout = 0; + uint32_t timeout, timeout_left = 0; + + static struct watchdog_info ident = { + .options = WDIOF_KEEPALIVEPING | WDIOF_SETTIMEOUT, + .firmware_version = 0, + .identity = "Hardware Watchdog for PNX833x", + }; + + switch (cmd) { + default: + return -ENOTTY; + + case WDIOC_GETSUPPORT: + if (copy_to_user((struct watchdog_info *)arg, + &ident, sizeof(ident))) + return -EFAULT; + return 0; + + case WDIOC_GETSTATUS: + case WDIOC_GETBOOTSTATUS: + return put_user(0, (int *)arg); + + case WDIOC_SETOPTIONS: + if (get_user(options, (int *)arg)) + return -EFAULT; + + if (options & WDIOS_DISABLECARD) + pnx833x_wdt_stop(); + + if (options & WDIOS_ENABLECARD) + pnx833x_wdt_start(); + + return 0; + + case WDIOC_KEEPALIVE: + pnx833x_wdt_ping(); + return 0; + + case WDIOC_SETTIMEOUT: + { + if (get_user(new_timeout, (int *)arg)) + return -EFAULT; + + pnx833x_wdt_timeout = new_timeout; + PNX833X_REG(PNX833X_CONFIG + + PNX833X_CONFIG_CPU_WATCHDOG_COMPARE) = new_timeout; + return put_user(new_timeout, (int *)arg); + } + + case WDIOC_GETTIMEOUT: + timeout = PNX833X_REG(PNX833X_CONFIG + + PNX833X_CONFIG_CPU_WATCHDOG_COMPARE); + return put_user(timeout, (int *)arg); + + case WDIOC_GETTIMELEFT: + timeout_left = PNX833X_REG(PNX833X_CONFIG + + PNX833X_CONFIG_CPU_WATCHDOG); + return put_user(timeout_left, (int *)arg); + + } +} + +static int pnx833x_wdt_notify_sys(struct notifier_block *this, + unsigned long code, void *unused) +{ + if (code == SYS_DOWN || code == SYS_HALT) + pnx833x_wdt_stop(); /* Turn the WDT off */ + + return NOTIFY_DONE; +} + +static const struct file_operations pnx833x_wdt_fops = { + .owner = THIS_MODULE, + .llseek = no_llseek, + .write = pnx833x_wdt_write, + .unlocked_ioctl = pnx833x_wdt_ioctl, + .open = pnx833x_wdt_open, + .release = pnx833x_wdt_release, +}; + +static struct miscdevice pnx833x_wdt_miscdev = { + .minor = WATCHDOG_MINOR, + .name = "watchdog", + .fops = &pnx833x_wdt_fops, +}; + +static struct notifier_block pnx833x_wdt_notifier = { + .notifier_call = pnx833x_wdt_notify_sys, +}; + +static char banner[] __initdata = + KERN_INFO PFX "Hardware Watchdog Timer for PNX833x: Version 0.1\n"; + +static int __init watchdog_init(void) +{ + int ret, cause; + + /* Lets check the reason for the reset.*/ + cause = PNX833X_REG(PNX833X_RESET); + /*If bit 31 is set then watchdog was cause of reset.*/ + if (cause & 0x80000000) { + printk(KERN_INFO PFX "The system was previously reset due to " + "the watchdog firing - please investigate...\n"); + } + + ret = register_reboot_notifier(&pnx833x_wdt_notifier); + if (ret) { + printk(KERN_ERR PFX + "cannot register reboot notifier (err=%d)\n", ret); + return ret; + } + + ret = misc_register(&pnx833x_wdt_miscdev); + if (ret) { + printk(KERN_ERR PFX + "cannot register miscdev on minor=%d (err=%d)\n", + WATCHDOG_MINOR, ret); + unregister_reboot_notifier(&pnx833x_wdt_notifier); + return ret; + } + + printk(banner); + if (start_enabled) + pnx833x_wdt_start(); + + return 0; +} + +static void __exit watchdog_exit(void) +{ + misc_deregister(&pnx833x_wdt_miscdev); + unregister_reboot_notifier(&pnx833x_wdt_notifier); +} + +module_init(watchdog_init); +module_exit(watchdog_exit); + +MODULE_AUTHOR("Daniel Laird/Andre McCurdy"); +MODULE_DESCRIPTION("Hardware Watchdog Device for PNX833x"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR); -- cgit v1.2.3 From 01480701d5cef5b3b0f8406d2eab1eaff82f9d5c Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Wed, 6 May 2009 15:35:40 +0200 Subject: [WATCHDOG] U300 COH 901 327 watchdog driver This patch adds support for the U300 COH 901 327 watchdog for the U300 platform recently added to RMK:s ARM tree. Signed-off-by: Linus Walleij Signed-off-by: Wim Van Sebroeck --- drivers/watchdog/Kconfig | 10 + drivers/watchdog/Makefile | 1 + drivers/watchdog/coh901327_wdt.c | 537 +++++++++++++++++++++++++++++++++++++++ 3 files changed, 548 insertions(+) create mode 100644 drivers/watchdog/coh901327_wdt.c (limited to 'drivers') diff --git a/drivers/watchdog/Kconfig b/drivers/watchdog/Kconfig index 373c22fd24ea..631599b7e6a2 100644 --- a/drivers/watchdog/Kconfig +++ b/drivers/watchdog/Kconfig @@ -240,6 +240,16 @@ config ORION_WATCHDOG To compile this driver as a module, choose M here: the module will be called orion_wdt. +config COH901327_WATCHDOG + bool "ST-Ericsson COH 901 327 watchdog" + depends on ARCH_U300 + default y if MACH_U300 + help + Say Y here to include Watchdog timer support for the + watchdog embedded into the ST-Ericsson U300 series platforms. + This watchdog is used to reset the system and thus cannot be + compiled as a module. + # AVR32 Architecture config AT32AP700X_WDT diff --git a/drivers/watchdog/Makefile b/drivers/watchdog/Makefile index 7231a1500860..c3a22d3d5498 100644 --- a/drivers/watchdog/Makefile +++ b/drivers/watchdog/Makefile @@ -41,6 +41,7 @@ obj-$(CONFIG_PNX4008_WATCHDOG) += pnx4008_wdt.o obj-$(CONFIG_IOP_WATCHDOG) += iop_wdt.o obj-$(CONFIG_DAVINCI_WATCHDOG) += davinci_wdt.o obj-$(CONFIG_ORION_WATCHDOG) += orion_wdt.o +obj-$(CONFIG_COH901327_WATCHDOG) += coh901327_wdt.o # AVR32 Architecture obj-$(CONFIG_AT32AP700X_WDT) += at32ap700x_wdt.o diff --git a/drivers/watchdog/coh901327_wdt.c b/drivers/watchdog/coh901327_wdt.c new file mode 100644 index 000000000000..fecb307d28e9 --- /dev/null +++ b/drivers/watchdog/coh901327_wdt.c @@ -0,0 +1,537 @@ +/* + * coh901327_wdt.c + * + * Copyright (C) 2008-2009 ST-Ericsson AB + * License terms: GNU General Public License (GPL) version 2 + * Watchdog driver for the ST-Ericsson AB COH 901 327 IP core + * Author: Linus Walleij + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define DRV_NAME "WDOG COH 901 327" + +/* + * COH 901 327 register definitions + */ + +/* WDOG_FEED Register 32bit (-/W) */ +#define U300_WDOG_FR 0x00 +#define U300_WDOG_FR_FEED_RESTART_TIMER 0xFEEDU +/* WDOG_TIMEOUT Register 32bit (R/W) */ +#define U300_WDOG_TR 0x04 +#define U300_WDOG_TR_TIMEOUT_MASK 0x7FFFU +/* WDOG_DISABLE1 Register 32bit (-/W) */ +#define U300_WDOG_D1R 0x08 +#define U300_WDOG_D1R_DISABLE1_DISABLE_TIMER 0x2BADU +/* WDOG_DISABLE2 Register 32bit (R/W) */ +#define U300_WDOG_D2R 0x0C +#define U300_WDOG_D2R_DISABLE2_DISABLE_TIMER 0xCAFEU +#define U300_WDOG_D2R_DISABLE_STATUS_DISABLED 0xDABEU +#define U300_WDOG_D2R_DISABLE_STATUS_ENABLED 0x0000U +/* WDOG_STATUS Register 32bit (R/W) */ +#define U300_WDOG_SR 0x10 +#define U300_WDOG_SR_STATUS_TIMED_OUT 0xCFE8U +#define U300_WDOG_SR_STATUS_NORMAL 0x0000U +#define U300_WDOG_SR_RESET_STATUS_RESET 0xE8B4U +/* WDOG_COUNT Register 32bit (R/-) */ +#define U300_WDOG_CR 0x14 +#define U300_WDOG_CR_VALID_IND 0x8000U +#define U300_WDOG_CR_VALID_STABLE 0x0000U +#define U300_WDOG_CR_COUNT_VALUE_MASK 0x7FFFU +/* WDOG_JTAGOVR Register 32bit (R/W) */ +#define U300_WDOG_JOR 0x18 +#define U300_WDOG_JOR_JTAG_MODE_IND 0x0002U +#define U300_WDOG_JOR_JTAG_WATCHDOG_ENABLE 0x0001U +/* WDOG_RESTART Register 32bit (-/W) */ +#define U300_WDOG_RR 0x1C +#define U300_WDOG_RR_RESTART_VALUE_RESUME 0xACEDU +/* WDOG_IRQ_EVENT Register 32bit (R/W) */ +#define U300_WDOG_IER 0x20 +#define U300_WDOG_IER_WILL_BARK_IRQ_EVENT_IND 0x0001U +#define U300_WDOG_IER_WILL_BARK_IRQ_ACK_ENABLE 0x0001U +/* WDOG_IRQ_MASK Register 32bit (R/W) */ +#define U300_WDOG_IMR 0x24 +#define U300_WDOG_IMR_WILL_BARK_IRQ_ENABLE 0x0001U +/* WDOG_IRQ_FORCE Register 32bit (R/W) */ +#define U300_WDOG_IFR 0x28 +#define U300_WDOG_IFR_WILL_BARK_IRQ_FORCE_ENABLE 0x0001U + +/* Default timeout in seconds = 1 minute */ +static int margin = 60; +static resource_size_t phybase; +static resource_size_t physize; +static int irq; +static void __iomem *virtbase; +static unsigned long coh901327_users; +static unsigned long boot_status; +static u16 wdogenablestore; +static u16 irqmaskstore; +static struct device *parent; + +/* + * The watchdog block is of course always clocked, the + * clk_enable()/clk_disable() calls are mainly for performing reference + * counting higher up in the clock hierarchy. + */ +static struct clk *clk; + +/* + * Enabling and disabling functions. + */ +static void coh901327_enable(u16 timeout) +{ + u16 val; + + clk_enable(clk); + /* Restart timer if it is disabled */ + val = readw(virtbase + U300_WDOG_D2R); + if (val == U300_WDOG_D2R_DISABLE_STATUS_DISABLED) + writew(U300_WDOG_RR_RESTART_VALUE_RESUME, + virtbase + U300_WDOG_RR); + /* Acknowledge any pending interrupt so it doesn't just fire off */ + writew(U300_WDOG_IER_WILL_BARK_IRQ_ACK_ENABLE, + virtbase + U300_WDOG_IER); + /* Enable the watchdog interrupt */ + writew(U300_WDOG_IMR_WILL_BARK_IRQ_ENABLE, virtbase + U300_WDOG_IMR); + /* Activate the watchdog timer */ + writew(timeout, virtbase + U300_WDOG_TR); + /* Start the watchdog timer */ + writew(U300_WDOG_FR_FEED_RESTART_TIMER, virtbase + U300_WDOG_FR); + /* + * Extra read so that this change propagate in the watchdog. + */ + (void) readw(virtbase + U300_WDOG_CR); + val = readw(virtbase + U300_WDOG_D2R); + clk_disable(clk); + if (val != U300_WDOG_D2R_DISABLE_STATUS_ENABLED) + dev_err(parent, + "%s(): watchdog not enabled! D2R value %04x\n", + __func__, val); +} + +static void coh901327_disable(void) +{ + u16 val; + + clk_enable(clk); + /* Disable the watchdog interrupt if it is active */ + writew(0x0000U, virtbase + U300_WDOG_IMR); + /* If the watchdog is currently enabled, attempt to disable it */ + val = readw(virtbase + U300_WDOG_D2R); + if (val != U300_WDOG_D2R_DISABLE_STATUS_DISABLED) { + writew(U300_WDOG_D1R_DISABLE1_DISABLE_TIMER, + virtbase + U300_WDOG_D1R); + writew(U300_WDOG_D2R_DISABLE2_DISABLE_TIMER, + virtbase + U300_WDOG_D2R); + /* Write this twice (else problems occur) */ + writew(U300_WDOG_D2R_DISABLE2_DISABLE_TIMER, + virtbase + U300_WDOG_D2R); + } + val = readw(virtbase + U300_WDOG_D2R); + clk_disable(clk); + if (val != U300_WDOG_D2R_DISABLE_STATUS_DISABLED) + dev_err(parent, + "%s(): watchdog not disabled! D2R value %04x\n", + __func__, val); +} + +static void coh901327_start(void) +{ + coh901327_enable(margin * 100); +} + +static void coh901327_keepalive(void) +{ + clk_enable(clk); + /* Feed the watchdog */ + writew(U300_WDOG_FR_FEED_RESTART_TIMER, + virtbase + U300_WDOG_FR); + clk_disable(clk); +} + +static int coh901327_settimeout(int time) +{ + /* + * Max margin is 327 since the 10ms + * timeout register is max + * 0x7FFF = 327670ms ~= 327s. + */ + if (time <= 0 || time > 327) + return -EINVAL; + + margin = time; + clk_enable(clk); + /* Set new timeout value */ + writew(margin * 100, virtbase + U300_WDOG_TR); + /* Feed the dog */ + writew(U300_WDOG_FR_FEED_RESTART_TIMER, + virtbase + U300_WDOG_FR); + clk_disable(clk); + return 0; +} + +/* + * This interrupt occurs 10 ms before the watchdog WILL bark. + */ +static irqreturn_t coh901327_interrupt(int irq, void *data) +{ + u16 val; + + /* + * Ack IRQ? If this occurs we're FUBAR anyway, so + * just acknowledge, disable the interrupt and await the imminent end. + * If you at some point need a host of callbacks to be called + * when the system is about to watchdog-reset, add them here! + * + * NOTE: on future versions of this IP-block, it will be possible + * to prevent a watchdog reset by feeding the watchdog at this + * point. + */ + clk_enable(clk); + val = readw(virtbase + U300_WDOG_IER); + if (val == U300_WDOG_IER_WILL_BARK_IRQ_EVENT_IND) + writew(U300_WDOG_IER_WILL_BARK_IRQ_ACK_ENABLE, + virtbase + U300_WDOG_IER); + writew(0x0000U, virtbase + U300_WDOG_IMR); + clk_disable(clk); + dev_crit(parent, "watchdog is barking!\n"); + return IRQ_HANDLED; +} + +/* + * Allow only one user (daemon) to open the watchdog + */ +static int coh901327_open(struct inode *inode, struct file *file) +{ + if (test_and_set_bit(1, &coh901327_users)) + return -EBUSY; + coh901327_start(); + return nonseekable_open(inode, file); +} + +static int coh901327_release(struct inode *inode, struct file *file) +{ + clear_bit(1, &coh901327_users); + coh901327_disable(); + return 0; +} + +static ssize_t coh901327_write(struct file *file, const char __user *data, + size_t len, loff_t *ppos) +{ + if (len) + coh901327_keepalive(); + return len; +} + +static long coh901327_ioctl(struct file *file, unsigned int cmd, + unsigned long arg) +{ + int ret = -ENOTTY; + u16 val; + int time; + int new_options; + union { + struct watchdog_info __user *ident; + int __user *i; + } uarg; + static struct watchdog_info ident = { + .options = WDIOF_CARDRESET | + WDIOF_SETTIMEOUT | + WDIOF_KEEPALIVEPING, + .identity = "COH 901 327 Watchdog", + .firmware_version = 1, + }; + uarg.i = (int __user *)arg; + + switch (cmd) { + case WDIOC_GETSUPPORT: + ret = copy_to_user(uarg.ident, &ident, + sizeof(ident)) ? -EFAULT : 0; + break; + + case WDIOC_GETSTATUS: + ret = put_user(0, uarg.i); + break; + + case WDIOC_GETBOOTSTATUS: + ret = put_user(boot_status, uarg.i); + break; + + case WDIOC_SETOPTIONS: + ret = get_user(new_options, uarg.i); + if (ret) + break; + if (new_options & WDIOS_DISABLECARD) + coh901327_disable(); + if (new_options & WDIOS_ENABLECARD) + coh901327_start(); + ret = 0; + break; + + case WDIOC_KEEPALIVE: + coh901327_keepalive(); + ret = 0; + break; + + case WDIOC_SETTIMEOUT: + ret = get_user(time, uarg.i); + if (ret) + break; + + ret = coh901327_settimeout(time); + if (ret) + break; + /* Then fall through to return set value */ + + case WDIOC_GETTIMEOUT: + ret = put_user(margin, uarg.i); + break; + + case WDIOC_GETTIMELEFT: + clk_enable(clk); + /* Read repeatedly until the value is stable! */ + val = readw(virtbase + U300_WDOG_CR); + while (val & U300_WDOG_CR_VALID_IND) + val = readw(virtbase + U300_WDOG_CR); + val &= U300_WDOG_CR_COUNT_VALUE_MASK; + clk_disable(clk); + if (val != 0) + val /= 100; + ret = put_user(val, uarg.i); + break; + } + return ret; +} + +static const struct file_operations coh901327_fops = { + .owner = THIS_MODULE, + .llseek = no_llseek, + .write = coh901327_write, + .unlocked_ioctl = coh901327_ioctl, + .open = coh901327_open, + .release = coh901327_release, +}; + +static struct miscdevice coh901327_miscdev = { + .minor = WATCHDOG_MINOR, + .name = "watchdog", + .fops = &coh901327_fops, +}; + +static int __exit coh901327_remove(struct platform_device *pdev) +{ + misc_deregister(&coh901327_miscdev); + coh901327_disable(); + free_irq(irq, pdev); + clk_put(clk); + iounmap(virtbase); + release_mem_region(phybase, physize); + return 0; +} + + +static int __init coh901327_probe(struct platform_device *pdev) +{ + int ret; + u16 val; + struct resource *res; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) + return -ENOENT; + + parent = &pdev->dev; + physize = resource_size(res); + phybase = res->start; + + if (request_mem_region(phybase, physize, DRV_NAME) == NULL) { + ret = -EBUSY; + goto out; + } + + virtbase = ioremap(phybase, physize); + if (!virtbase) { + ret = -ENOMEM; + goto out_no_remap; + } + + clk = clk_get(&pdev->dev, NULL); + if (IS_ERR(clk)) { + ret = PTR_ERR(clk); + dev_err(&pdev->dev, "could not get clock\n"); + goto out_no_clk; + } + ret = clk_enable(clk); + if (ret) { + dev_err(&pdev->dev, "could not enable clock\n"); + goto out_no_clk_enable; + } + + val = readw(virtbase + U300_WDOG_SR); + switch (val) { + case U300_WDOG_SR_STATUS_TIMED_OUT: + dev_info(&pdev->dev, + "watchdog timed out since last chip reset!\n"); + boot_status = WDIOF_CARDRESET; + /* Status will be cleared below */ + break; + case U300_WDOG_SR_STATUS_NORMAL: + dev_info(&pdev->dev, + "in normal status, no timeouts have occurred.\n"); + break; + default: + dev_info(&pdev->dev, + "contains an illegal status code (%08x)\n", val); + break; + } + + val = readw(virtbase + U300_WDOG_D2R); + switch (val) { + case U300_WDOG_D2R_DISABLE_STATUS_DISABLED: + dev_info(&pdev->dev, "currently disabled.\n"); + break; + case U300_WDOG_D2R_DISABLE_STATUS_ENABLED: + dev_info(&pdev->dev, + "currently enabled! (disabling it now)\n"); + coh901327_disable(); + break; + default: + dev_err(&pdev->dev, + "contains an illegal enable/disable code (%08x)\n", + val); + break; + } + + /* Reset the watchdog */ + writew(U300_WDOG_SR_RESET_STATUS_RESET, virtbase + U300_WDOG_SR); + + irq = platform_get_irq(pdev, 0); + if (request_irq(irq, coh901327_interrupt, IRQF_DISABLED, + DRV_NAME " Bark", pdev)) { + ret = -EIO; + goto out_no_irq; + } + + clk_disable(clk); + + ret = misc_register(&coh901327_miscdev); + if (ret == 0) + dev_info(&pdev->dev, + "initialized. timer margin=%d sec\n", margin); + else + goto out_no_wdog; + + return 0; + +out_no_wdog: + free_irq(irq, pdev); +out_no_irq: + clk_disable(clk); +out_no_clk_enable: + clk_put(clk); +out_no_clk: + iounmap(virtbase); +out_no_remap: + release_mem_region(phybase, SZ_4K); +out: + return ret; +} + +#ifdef CONFIG_PM +static int coh901327_suspend(struct platform_device *pdev, pm_message_t state) +{ + irqmaskstore = readw(virtbase + U300_WDOG_IMR) & 0x0001U; + wdogenablestore = readw(virtbase + U300_WDOG_D2R); + /* If watchdog is on, disable it here and now */ + if (wdogenablestore == U300_WDOG_D2R_DISABLE_STATUS_ENABLED) + coh901327_disable(); + return 0; +} + +static int coh901327_resume(struct platform_device *pdev) +{ + /* Restore the watchdog interrupt */ + writew(irqmaskstore, virtbase + U300_WDOG_IMR); + if (wdogenablestore == U300_WDOG_D2R_DISABLE_STATUS_ENABLED) { + /* Restart the watchdog timer */ + writew(U300_WDOG_RR_RESTART_VALUE_RESUME, + virtbase + U300_WDOG_RR); + writew(U300_WDOG_FR_FEED_RESTART_TIMER, + virtbase + U300_WDOG_FR); + } + return 0; +} +#else +#define coh901327_suspend NULL +#define coh901327_resume NULL +#endif + +/* + * Mistreating the watchdog is the only way to perform a software reset of the + * system on EMP platforms. So we implement this and export a symbol for it. + */ +void coh901327_watchdog_reset(void) +{ + /* Enable even if on JTAG too */ + writew(U300_WDOG_JOR_JTAG_WATCHDOG_ENABLE, + virtbase + U300_WDOG_JOR); + /* + * Timeout = 5s, we have to wait for the watchdog reset to + * actually take place: the watchdog will be reloaded with the + * default value immediately, so we HAVE to reboot and get back + * into the kernel in 30s, or the device will reboot again! + * The boot loader will typically deactivate the watchdog, so we + * need time enough for the boot loader to get to the point of + * deactivating the watchdog before it is shut down by it. + * + * NOTE: on future versions of the watchdog, this restriction is + * gone: the watchdog will be reloaded with a defaul value (1 min) + * instead of last value, and you can conveniently set the watchdog + * timeout to 10ms (value = 1) without any problems. + */ + coh901327_enable(500); + /* Return and await doom */ +} + +static struct platform_driver coh901327_driver = { + .driver = { + .owner = THIS_MODULE, + .name = "coh901327_wdog", + }, + .remove = __exit_p(coh901327_remove), + .suspend = coh901327_suspend, + .resume = coh901327_resume, +}; + +static int __init coh901327_init(void) +{ + return platform_driver_probe(&coh901327_driver, coh901327_probe); +} +module_init(coh901327_init); + +static void __exit coh901327_exit(void) +{ + platform_driver_unregister(&coh901327_driver); +} +module_exit(coh901327_exit); + +MODULE_AUTHOR("Linus Walleij "); +MODULE_DESCRIPTION("COH 901 327 Watchdog"); + +module_param(margin, int, 0); +MODULE_PARM_DESC(margin, "Watchdog margin in seconds (default 60s)"); + +MODULE_LICENSE("GPL"); +MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR); -- cgit v1.2.3 From 80e45b1e9edbca746618724d5b0a31500bdb6f39 Mon Sep 17 00:00:00 2001 From: Timo Kokkonen Date: Fri, 27 Mar 2009 16:42:17 +0200 Subject: [WATCHDOG] twl4030 watchdog driver Implementation of twl4030 watchdog driver. Signed-off-by: Timo Kokkonen Signed-off-by: Atal Shargorodsky Signed-off-by: Wim Van Sebroeck --- drivers/mfd/twl4030-core.c | 12 ++ drivers/watchdog/Kconfig | 7 ++ drivers/watchdog/Makefile | 1 + drivers/watchdog/twl4030_wdt.c | 272 +++++++++++++++++++++++++++++++++++++++++ 4 files changed, 292 insertions(+) create mode 100644 drivers/watchdog/twl4030_wdt.c (limited to 'drivers') diff --git a/drivers/mfd/twl4030-core.c b/drivers/mfd/twl4030-core.c index cd1008c19cd7..ca54996ffd0e 100644 --- a/drivers/mfd/twl4030-core.c +++ b/drivers/mfd/twl4030-core.c @@ -101,6 +101,12 @@ #define twl_has_usb() false #endif +#if defined(CONFIG_TWL4030_WATCHDOG) || \ + defined(CONFIG_TWL4030_WATCHDOG_MODULE) +#define twl_has_watchdog() true +#else +#define twl_has_watchdog() false +#endif /* Triton Core internal information (BEGIN) */ @@ -526,6 +532,12 @@ add_children(struct twl4030_platform_data *pdata, unsigned long features) usb_transceiver = child; } + if (twl_has_watchdog()) { + child = add_child(0, "twl4030_wdt", NULL, 0, false, 0, 0); + if (IS_ERR(child)) + return PTR_ERR(child); + } + if (twl_has_regulator()) { /* child = add_regulator(TWL4030_REG_VPLL1, pdata->vpll1); diff --git a/drivers/watchdog/Kconfig b/drivers/watchdog/Kconfig index 631599b7e6a2..c4c2206d6f3e 100644 --- a/drivers/watchdog/Kconfig +++ b/drivers/watchdog/Kconfig @@ -250,6 +250,13 @@ config COH901327_WATCHDOG This watchdog is used to reset the system and thus cannot be compiled as a module. +config TWL4030_WATCHDOG + tristate "TWL4030 Watchdog" + depends on TWL4030_CORE + help + Support for TI TWL4030 watchdog. Say 'Y' here to enable the + watchdog timer support for TWL4030 chips. + # AVR32 Architecture config AT32AP700X_WDT diff --git a/drivers/watchdog/Makefile b/drivers/watchdog/Makefile index c3a22d3d5498..2c54c04858b2 100644 --- a/drivers/watchdog/Makefile +++ b/drivers/watchdog/Makefile @@ -28,6 +28,7 @@ obj-$(CONFIG_USBPCWATCHDOG) += pcwd_usb.o obj-$(CONFIG_AT91RM9200_WATCHDOG) += at91rm9200_wdt.o obj-$(CONFIG_AT91SAM9X_WATCHDOG) += at91sam9_wdt.o obj-$(CONFIG_OMAP_WATCHDOG) += omap_wdt.o +obj-$(CONFIG_TWL4030_WATCHDOG) += twl4030_wdt.o obj-$(CONFIG_21285_WATCHDOG) += wdt285.o obj-$(CONFIG_977_WATCHDOG) += wdt977.o obj-$(CONFIG_IXP2000_WATCHDOG) += ixp2000_wdt.o diff --git a/drivers/watchdog/twl4030_wdt.c b/drivers/watchdog/twl4030_wdt.c new file mode 100644 index 000000000000..cb46556f2973 --- /dev/null +++ b/drivers/watchdog/twl4030_wdt.c @@ -0,0 +1,272 @@ +/* + * Copyright (C) Nokia Corporation + * + * Written by Timo Kokkonen + * + * 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. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define TWL4030_WATCHDOG_CFG_REG_OFFS 0x3 + +#define TWL4030_WDT_STATE_OPEN 0x1 +#define TWL4030_WDT_STATE_ACTIVE 0x8 + +static struct platform_device *twl4030_wdt_dev; + +struct twl4030_wdt { + struct miscdevice miscdev; + int timer_margin; + unsigned long state; +}; + +static int nowayout = WATCHDOG_NOWAYOUT; +module_param(nowayout, int, 0); +MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started " + "(default=" __MODULE_STRING(WATCHDOG_NOWAYOUT) ")"); + +static int twl4030_wdt_write(unsigned char val) +{ + return twl4030_i2c_write_u8(TWL4030_MODULE_PM_RECEIVER, val, + TWL4030_WATCHDOG_CFG_REG_OFFS); +} + +static int twl4030_wdt_enable(struct twl4030_wdt *wdt) +{ + return twl4030_wdt_write(wdt->timer_margin + 1); +} + +static int twl4030_wdt_disable(struct twl4030_wdt *wdt) +{ + return twl4030_wdt_write(0); +} + +static int twl4030_wdt_set_timeout(struct twl4030_wdt *wdt, int timeout) +{ + if (timeout < 0 || timeout > 30) { + dev_warn(wdt->miscdev.parent, + "Timeout can only be in the range [0-30] seconds"); + return -EINVAL; + } + wdt->timer_margin = timeout; + return twl4030_wdt_enable(wdt); +} + +static ssize_t twl4030_wdt_write_fop(struct file *file, + const char __user *data, size_t len, loff_t *ppos) +{ + struct twl4030_wdt *wdt = file->private_data; + + if (len) + twl4030_wdt_enable(wdt); + + return len; +} + +static long twl4030_wdt_ioctl(struct file *file, + unsigned int cmd, unsigned long arg) +{ + void __user *argp = (void __user *)arg; + int __user *p = argp; + int new_margin; + struct twl4030_wdt *wdt = file->private_data; + + static const struct watchdog_info twl4030_wd_ident = { + .identity = "TWL4030 Watchdog", + .options = WDIOF_SETTIMEOUT, + .firmware_version = 0, + }; + + switch (cmd) { + case WDIOC_GETSUPPORT: + return copy_to_user(argp, &twl4030_wd_ident, + sizeof(twl4030_wd_ident)) ? -EFAULT : 0; + + case WDIOC_GETSTATUS: + case WDIOC_GETBOOTSTATUS: + return put_user(0, p); + + case WDIOC_KEEPALIVE: + twl4030_wdt_enable(wdt); + break; + + case WDIOC_SETTIMEOUT: + if (get_user(new_margin, p)) + return -EFAULT; + if (twl4030_wdt_set_timeout(wdt, new_margin)) + return -EINVAL; + return put_user(wdt->timer_margin, p); + + case WDIOC_GETTIMEOUT: + return put_user(wdt->timer_margin, p); + + default: + return -ENOTTY; + } + + return 0; +} + +static int twl4030_wdt_open(struct inode *inode, struct file *file) +{ + struct twl4030_wdt *wdt = platform_get_drvdata(twl4030_wdt_dev); + + /* /dev/watchdog can only be opened once */ + if (test_and_set_bit(0, &wdt->state)) + return -EBUSY; + + wdt->state |= TWL4030_WDT_STATE_ACTIVE; + file->private_data = (void *) wdt; + + twl4030_wdt_enable(wdt); + return nonseekable_open(inode, file); +} + +static int twl4030_wdt_release(struct inode *inode, struct file *file) +{ + struct twl4030_wdt *wdt = file->private_data; + if (nowayout) { + dev_alert(wdt->miscdev.parent, + "Unexpected close, watchdog still running!\n"); + twl4030_wdt_enable(wdt); + } else { + if (twl4030_wdt_disable(wdt)) + return -EFAULT; + wdt->state &= ~TWL4030_WDT_STATE_ACTIVE; + } + + clear_bit(0, &wdt->state); + return 0; +} + +static const struct file_operations twl4030_wdt_fops = { + .owner = THIS_MODULE, + .llseek = no_llseek, + .open = twl4030_wdt_open, + .release = twl4030_wdt_release, + .unlocked_ioctl = twl4030_wdt_ioctl, + .write = twl4030_wdt_write_fop, +}; + +static int __devinit twl4030_wdt_probe(struct platform_device *pdev) +{ + int ret = 0; + struct twl4030_wdt *wdt; + + wdt = kzalloc(sizeof(struct twl4030_wdt), GFP_KERNEL); + if (!wdt) + return -ENOMEM; + + wdt->state = 0; + wdt->timer_margin = 30; + wdt->miscdev.parent = &pdev->dev; + wdt->miscdev.fops = &twl4030_wdt_fops; + wdt->miscdev.minor = WATCHDOG_MINOR; + wdt->miscdev.name = "watchdog"; + + platform_set_drvdata(pdev, wdt); + + twl4030_wdt_dev = pdev; + + ret = misc_register(&wdt->miscdev); + if (ret) { + dev_err(wdt->miscdev.parent, + "Failed to register misc device\n"); + platform_set_drvdata(pdev, NULL); + kfree(wdt); + twl4030_wdt_dev = NULL; + return ret; + } + return 0; +} + +static int __devexit twl4030_wdt_remove(struct platform_device *pdev) +{ + struct twl4030_wdt *wdt = platform_get_drvdata(pdev); + + if (wdt->state & TWL4030_WDT_STATE_ACTIVE) + if (twl4030_wdt_disable(wdt)) + return -EFAULT; + + wdt->state &= ~TWL4030_WDT_STATE_ACTIVE; + misc_deregister(&wdt->miscdev); + + platform_set_drvdata(pdev, NULL); + kfree(wdt); + twl4030_wdt_dev = NULL; + + return 0; +} + +#ifdef CONFIG_PM +static int twl4030_wdt_suspend(struct platform_device *pdev, pm_message_t state) +{ + struct twl4030_wdt *wdt = platform_get_drvdata(pdev); + if (wdt->state & TWL4030_WDT_STATE_ACTIVE) + return twl4030_wdt_disable(wdt); + + return 0; +} + +static int twl4030_wdt_resume(struct platform_device *pdev) +{ + struct twl4030_wdt *wdt = platform_get_drvdata(pdev); + if (wdt->state & TWL4030_WDT_STATE_ACTIVE) + return twl4030_wdt_enable(wdt); + + return 0; +} +#else +#define twl4030_wdt_suspend NULL +#define twl4030_wdt_resume NULL +#endif + +static struct platform_driver twl4030_wdt_driver = { + .probe = twl4030_wdt_probe, + .remove = __devexit_p(twl4030_wdt_remove), + .suspend = twl4030_wdt_suspend, + .resume = twl4030_wdt_resume, + .driver = { + .owner = THIS_MODULE, + .name = "twl4030_wdt", + }, +}; + +static int __devinit twl4030_wdt_init(void) +{ + return platform_driver_register(&twl4030_wdt_driver); +} +module_init(twl4030_wdt_init); + +static void __devexit twl4030_wdt_exit(void) +{ + platform_driver_unregister(&twl4030_wdt_driver); +} +module_exit(twl4030_wdt_exit); + +MODULE_AUTHOR("Nokia Corporation"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR); +MODULE_ALIAS("platform:twl4030_wdt"); + -- cgit v1.2.3 From accde1684ff1ea607557fb7224d2dd57775117e1 Mon Sep 17 00:00:00 2001 From: dmitry pervushin Date: Wed, 3 Jun 2009 20:21:21 +0400 Subject: [WATCHDOG] Freescale STMP: watchdog driver Add watchdog timer support for Freescale STMP3xxx boards Signed-off-by: Vitaly Wool Signed-off-by: Dmitry Pervushin Signed-off-by: Wim Van Sebroeck --- drivers/watchdog/Kconfig | 9 ++ drivers/watchdog/Makefile | 1 + drivers/watchdog/stmp3xxx_wdt.c | 296 ++++++++++++++++++++++++++++++++++++++++ 3 files changed, 306 insertions(+) create mode 100644 drivers/watchdog/stmp3xxx_wdt.c (limited to 'drivers') diff --git a/drivers/watchdog/Kconfig b/drivers/watchdog/Kconfig index c4c2206d6f3e..70d7f8cc06b7 100644 --- a/drivers/watchdog/Kconfig +++ b/drivers/watchdog/Kconfig @@ -257,6 +257,15 @@ config TWL4030_WATCHDOG Support for TI TWL4030 watchdog. Say 'Y' here to enable the watchdog timer support for TWL4030 chips. +config STMP3XXX_WATCHDOG + tristate "Freescale STMP3XXX watchdog" + depends on ARCH_STMP3XXX + help + Say Y here if to include support for the watchdog timer + for the Sigmatel STMP37XX/378X SoC. + To compile this driver as a module, choose M here: the + module will be called stmp3xxx_wdt. + # AVR32 Architecture config AT32AP700X_WDT diff --git a/drivers/watchdog/Makefile b/drivers/watchdog/Makefile index 2c54c04858b2..d437d5c2d22e 100644 --- a/drivers/watchdog/Makefile +++ b/drivers/watchdog/Makefile @@ -43,6 +43,7 @@ obj-$(CONFIG_IOP_WATCHDOG) += iop_wdt.o obj-$(CONFIG_DAVINCI_WATCHDOG) += davinci_wdt.o obj-$(CONFIG_ORION_WATCHDOG) += orion_wdt.o obj-$(CONFIG_COH901327_WATCHDOG) += coh901327_wdt.o +obj-$(CONFIG_STMP3XXX_WATCHDOG) += stmp3xxx_wdt.o # AVR32 Architecture obj-$(CONFIG_AT32AP700X_WDT) += at32ap700x_wdt.o diff --git a/drivers/watchdog/stmp3xxx_wdt.c b/drivers/watchdog/stmp3xxx_wdt.c new file mode 100644 index 000000000000..5dd952681f32 --- /dev/null +++ b/drivers/watchdog/stmp3xxx_wdt.c @@ -0,0 +1,296 @@ +/* + * Watchdog driver for Freescale STMP37XX/STMP378X + * + * Author: Vitaly Wool + * + * Copyright 2008 Freescale Semiconductor, Inc. All Rights Reserved. + * Copyright 2008 Embedded Alley Solutions, Inc All Rights Reserved. + */ +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#define DEFAULT_HEARTBEAT 19 +#define MAX_HEARTBEAT (0x10000000 >> 6) + +/* missing bitmask in headers */ +#define BV_RTC_PERSISTENT1_GENERAL__RTC_FORCE_UPDATER 0x80000000 + +#define WDT_IN_USE 0 +#define WDT_OK_TO_CLOSE 1 + +#define WDOG_COUNTER_RATE 1000 /* 1 kHz clock */ + +static DEFINE_SPINLOCK(stmp3xxx_wdt_io_lock); +static unsigned long wdt_status; +static const int nowayout = WATCHDOG_NOWAYOUT; +static int heartbeat = DEFAULT_HEARTBEAT; +static unsigned long boot_status; + +static void wdt_enable(u32 value) +{ + spin_lock(&stmp3xxx_wdt_io_lock); + __raw_writel(value, REGS_RTC_BASE + HW_RTC_WATCHDOG); + stmp3xxx_setl(BM_RTC_CTRL_WATCHDOGEN, REGS_RTC_BASE + HW_RTC_CTRL); + stmp3xxx_setl(BV_RTC_PERSISTENT1_GENERAL__RTC_FORCE_UPDATER, + REGS_RTC_BASE + HW_RTC_PERSISTENT1); + spin_unlock(&stmp3xxx_wdt_io_lock); +} + +static void wdt_disable(void) +{ + spin_lock(&stmp3xxx_wdt_io_lock); + stmp3xxx_clearl(BV_RTC_PERSISTENT1_GENERAL__RTC_FORCE_UPDATER, + REGS_RTC_BASE + HW_RTC_PERSISTENT1); + stmp3xxx_clearl(BM_RTC_CTRL_WATCHDOGEN, REGS_RTC_BASE + HW_RTC_CTRL); + spin_unlock(&stmp3xxx_wdt_io_lock); +} + +static void wdt_ping(void) +{ + wdt_enable(heartbeat * WDOG_COUNTER_RATE); +} + +static int stmp3xxx_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_ping(); + + return nonseekable_open(inode, file); +} + +static ssize_t stmp3xxx_wdt_write(struct file *file, const char __user *data, + size_t len, loff_t *ppos) +{ + 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_ping(); + } + + return len; +} + +static struct watchdog_info ident = { + .options = WDIOF_CARDRESET | + WDIOF_MAGICCLOSE | + WDIOF_SETTIMEOUT | + WDIOF_KEEPALIVEPING, + .identity = "STMP3XXX Watchdog", +}; + +static long stmp3xxx_wdt_ioctl(struct file *file, unsigned int cmd, + unsigned long arg) +{ + void __user *argp = (void __user *)arg; + int __user *p = argp; + int new_heartbeat, opts; + int ret = -ENOTTY; + + switch (cmd) { + case WDIOC_GETSUPPORT: + ret = copy_to_user(argp, &ident, sizeof(ident)) ? -EFAULT : 0; + break; + + case WDIOC_GETSTATUS: + ret = put_user(0, p); + break; + + case WDIOC_GETBOOTSTATUS: + ret = put_user(boot_status, p); + break; + + case WDIOC_SETOPTIONS: + if (get_user(opts, p)) { + ret = -EFAULT; + break; + } + if (opts & WDIOS_DISABLECARD) + wdt_disable(); + else if (opts & WDIOS_ENABLECARD) + wdt_ping(); + else { + pr_debug("%s: unknown option 0x%x\n", __func__, opts); + ret = -EINVAL; + break; + } + ret = 0; + break; + + case WDIOC_KEEPALIVE: + wdt_ping(); + ret = 0; + break; + + case WDIOC_SETTIMEOUT: + if (get_user(new_heartbeat, p)) { + ret = -EFAULT; + break; + } + if (new_heartbeat <= 0 || new_heartbeat > MAX_HEARTBEAT) { + ret = -EINVAL; + break; + } + + heartbeat = new_heartbeat; + wdt_ping(); + /* Fall through */ + + case WDIOC_GETTIMEOUT: + ret = put_user(heartbeat, p); + break; + } + return ret; +} + +static int stmp3xxx_wdt_release(struct inode *inode, struct file *file) +{ + int ret = 0; + + if (!nowayout) { + if (!test_bit(WDT_OK_TO_CLOSE, &wdt_status)) { + wdt_ping(); + pr_debug("%s: Device closed unexpectdly\n", __func__); + ret = -EINVAL; + } else { + wdt_disable(); + clear_bit(WDT_OK_TO_CLOSE, &wdt_status); + } + } + clear_bit(WDT_IN_USE, &wdt_status); + + return ret; +} + +static const struct file_operations stmp3xxx_wdt_fops = { + .owner = THIS_MODULE, + .llseek = no_llseek, + .write = stmp3xxx_wdt_write, + .unlocked_ioctl = stmp3xxx_wdt_ioctl, + .open = stmp3xxx_wdt_open, + .release = stmp3xxx_wdt_release, +}; + +static struct miscdevice stmp3xxx_wdt_miscdev = { + .minor = WATCHDOG_MINOR, + .name = "watchdog", + .fops = &stmp3xxx_wdt_fops, +}; + +static int __devinit stmp3xxx_wdt_probe(struct platform_device *pdev) +{ + int ret = 0; + + if (heartbeat < 1 || heartbeat > MAX_HEARTBEAT) + heartbeat = DEFAULT_HEARTBEAT; + + boot_status = __raw_readl(REGS_RTC_BASE + HW_RTC_PERSISTENT1) & + BV_RTC_PERSISTENT1_GENERAL__RTC_FORCE_UPDATER; + boot_status = !!boot_status; + stmp3xxx_clearl(BV_RTC_PERSISTENT1_GENERAL__RTC_FORCE_UPDATER, + REGS_RTC_BASE + HW_RTC_PERSISTENT1); + wdt_disable(); /* disable for now */ + + ret = misc_register(&stmp3xxx_wdt_miscdev); + if (ret < 0) { + dev_err(&pdev->dev, "cannot register misc device\n"); + return ret; + } + + printk(KERN_INFO "stmp3xxx watchdog: initialized, heartbeat %d sec\n", + heartbeat); + + return ret; +} + +static int __devexit stmp3xxx_wdt_remove(struct platform_device *pdev) +{ + misc_deregister(&stmp3xxx_wdt_miscdev); + return 0; +} + +#ifdef CONFIG_PM +static int wdt_suspended; +static u32 wdt_saved_time; + +static int stmp3xxx_wdt_suspend(struct platform_device *pdev, + pm_message_t state) +{ + if (__raw_readl(REGS_RTC_BASE + HW_RTC_CTRL) & + BM_RTC_CTRL_WATCHDOGEN) { + wdt_suspended = 1; + wdt_saved_time = __raw_readl(REGS_RTC_BASE + HW_RTC_WATCHDOG); + wdt_disable(); + } + return 0; +} + +static int stmp3xxx_wdt_resume(struct platform_device *pdev) +{ + if (wdt_suspended) { + wdt_enable(wdt_saved_time); + wdt_suspended = 0; + } + return 0; +} +#else +#define stmp3xxx_wdt_suspend NULL +#define stmp3xxx_wdt_resume NULL +#endif + +static struct platform_driver platform_wdt_driver = { + .driver = { + .name = "stmp3xxx_wdt", + }, + .probe = stmp3xxx_wdt_probe, + .remove = __devexit_p(stmp3xxx_wdt_remove), + .suspend = stmp3xxx_wdt_suspend, + .resume = stmp3xxx_wdt_resume, +}; + +static int __init stmp3xxx_wdt_init(void) +{ + return platform_driver_register(&platform_wdt_driver); +} + +static void __exit stmp3xxx_wdt_exit(void) +{ + return platform_driver_unregister(&platform_wdt_driver); +} + +module_init(stmp3xxx_wdt_init); +module_exit(stmp3xxx_wdt_exit); + +MODULE_DESCRIPTION("STMP3XXX Watchdog Driver"); +MODULE_LICENSE("GPL"); + +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_ALIAS_MISCDEV(WATCHDOG_MINOR); -- cgit v1.2.3 From 90074dce5537e87b27125505bb89a373567a7ede Mon Sep 17 00:00:00 2001 From: matthieu castet Date: Fri, 5 Jun 2009 18:57:18 +0200 Subject: [WATCHDOG] add bcm47xx watchdog driver This add watchdog driver for broadcom 47xx device. It uses the ssb subsytem to access embeded watchdog device. Because the watchdog timeout is very short (about 2s), a soft timer is used to increase the watchdog period. Note : A patch for exporting the ssb_watchdog_timer_set will be submitted on next linux-mips merge. Without this patch it can't be build as a module. Signed-off-by: Aleksandar Radovanovic Signed-off-by: Matthieu CASTET Tested-by: Florian Fainelli Cc: Ralf Baechle Signed-off-by: Wim Van Sebroeck Signed-off-by: Andrew Morton --- drivers/watchdog/Kconfig | 6 + drivers/watchdog/Makefile | 1 + drivers/watchdog/bcm47xx_wdt.c | 286 +++++++++++++++++++++++++++++++++++++++++ 3 files changed, 293 insertions(+) create mode 100644 drivers/watchdog/bcm47xx_wdt.c (limited to 'drivers') diff --git a/drivers/watchdog/Kconfig b/drivers/watchdog/Kconfig index 70d7f8cc06b7..e8d45b6ccef8 100644 --- a/drivers/watchdog/Kconfig +++ b/drivers/watchdog/Kconfig @@ -729,6 +729,12 @@ config SBC_EPX_C3_WATCHDOG # MIPS Architecture +config BCM47XX_WDT + tristate "Broadcom BCM47xx Watchdog Timer" + depends on BCM47XX + help + Hardware driver for the Broadcom BCM47xx Watchog Timer. + config RC32434_WDT tristate "IDT RC32434 SoC Watchdog Timer" depends on MIKROTIK_RB532 diff --git a/drivers/watchdog/Makefile b/drivers/watchdog/Makefile index d437d5c2d22e..3d774294a2b7 100644 --- a/drivers/watchdog/Makefile +++ b/drivers/watchdog/Makefile @@ -101,6 +101,7 @@ obj-$(CONFIG_SBC_EPX_C3_WATCHDOG) += sbc_epx_c3.o # M68KNOMMU Architecture # MIPS Architecture +obj-$(CONFIG_BCM47XX_WDT) += bcm47xx_wdt.o obj-$(CONFIG_RC32434_WDT) += rc32434_wdt.o obj-$(CONFIG_INDYDOG) += indydog.o obj-$(CONFIG_WDT_MTX1) += mtx-1_wdt.o diff --git a/drivers/watchdog/bcm47xx_wdt.c b/drivers/watchdog/bcm47xx_wdt.c new file mode 100644 index 000000000000..5c7011cda6a6 --- /dev/null +++ b/drivers/watchdog/bcm47xx_wdt.c @@ -0,0 +1,286 @@ +/* + * Watchdog driver for Broadcom BCM47XX + * + * Copyright (C) 2008 Aleksandar Radovanovic + * Copyright (C) 2009 Matthieu CASTET + * + * 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. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define DRV_NAME "bcm47xx_wdt" + +#define WDT_DEFAULT_TIME 30 /* seconds */ +#define WDT_MAX_TIME 255 /* seconds */ + +static int wdt_time = WDT_DEFAULT_TIME; +static int nowayout = WATCHDOG_NOWAYOUT; + +module_param(wdt_time, int, 0); +MODULE_PARM_DESC(wdt_time, "Watchdog time in seconds. (default=" + __MODULE_STRING(WDT_DEFAULT_TIME) ")"); + +#ifdef CONFIG_WATCHDOG_NOWAYOUT +module_param(nowayout, int, 0); +MODULE_PARM_DESC(nowayout, + "Watchdog cannot be stopped once started (default=" + __MODULE_STRING(WATCHDOG_NOWAYOUT) ")"); +#endif + +static unsigned long bcm47xx_wdt_busy; +static char expect_release; +static struct timer_list wdt_timer; +static atomic_t ticks; + +static inline void bcm47xx_wdt_hw_start(void) +{ + /* this is 2,5s on 100Mhz clock and 2s on 133 Mhz */ + ssb_watchdog_timer_set(&ssb_bcm47xx, 0xfffffff); +} + +static inline int bcm47xx_wdt_hw_stop(void) +{ + return ssb_watchdog_timer_set(&ssb_bcm47xx, 0); +} + +static void bcm47xx_timer_tick(unsigned long unused) +{ + if (!atomic_dec_and_test(&ticks)) { + bcm47xx_wdt_hw_start(); + mod_timer(&wdt_timer, jiffies + HZ); + } else { + printk(KERN_CRIT DRV_NAME "Watchdog will fire soon!!!\n"); + } +} + +static inline void bcm47xx_wdt_pet(void) +{ + atomic_set(&ticks, wdt_time); +} + +static void bcm47xx_wdt_start(void) +{ + bcm47xx_wdt_pet(); + bcm47xx_timer_tick(0); +} + +static void bcm47xx_wdt_pause(void) +{ + del_timer_sync(&wdt_timer); + bcm47xx_wdt_hw_stop(); +} + +static void bcm47xx_wdt_stop(void) +{ + bcm47xx_wdt_pause(); +} + +static int bcm47xx_wdt_settimeout(int new_time) +{ + if ((new_time <= 0) || (new_time > WDT_MAX_TIME)) + return -EINVAL; + + wdt_time = new_time; + return 0; +} + +static int bcm47xx_wdt_open(struct inode *inode, struct file *file) +{ + if (test_and_set_bit(0, &bcm47xx_wdt_busy)) + return -EBUSY; + + bcm47xx_wdt_start(); + return nonseekable_open(inode, file); +} + +static int bcm47xx_wdt_release(struct inode *inode, struct file *file) +{ + if (expect_release == 42) { + bcm47xx_wdt_stop(); + } else { + printk(KERN_CRIT DRV_NAME + ": Unexpected close, not stopping watchdog!\n"); + bcm47xx_wdt_start(); + } + + clear_bit(0, &bcm47xx_wdt_busy); + expect_release = 0; + return 0; +} + +static ssize_t bcm47xx_wdt_write(struct file *file, const char __user *data, + size_t len, loff_t *ppos) +{ + if (len) { + if (!nowayout) { + size_t i; + + expect_release = 0; + + for (i = 0; i != len; i++) { + char c; + if (get_user(c, data + i)) + return -EFAULT; + if (c == 'V') + expect_release = 42; + } + } + bcm47xx_wdt_pet(); + } + return len; +} + +static struct watchdog_info bcm47xx_wdt_info = { + .identity = DRV_NAME, + .options = WDIOF_SETTIMEOUT | + WDIOF_KEEPALIVEPING | + WDIOF_MAGICCLOSE, +}; + +static long bcm47xx_wdt_ioctl(struct file *file, + unsigned int cmd, unsigned long arg) +{ + void __user *argp = (void __user *)arg; + int __user *p = argp; + int new_value, retval = -EINVAL;; + + switch (cmd) { + case WDIOC_GETSUPPORT: + return copy_to_user(argp, &bcm47xx_wdt_info, + sizeof(bcm47xx_wdt_info)) ? -EFAULT : 0; + + case WDIOC_GETSTATUS: + case WDIOC_GETBOOTSTATUS: + return put_user(0, p); + + case WDIOC_SETOPTIONS: + if (get_user(new_value, p)) + return -EFAULT; + + if (new_value & WDIOS_DISABLECARD) { + bcm47xx_wdt_stop(); + retval = 0; + } + + if (new_value & WDIOS_ENABLECARD) { + bcm47xx_wdt_start(); + retval = 0; + } + + return retval; + + case WDIOC_KEEPALIVE: + bcm47xx_wdt_pet(); + return 0; + + case WDIOC_SETTIMEOUT: + if (get_user(new_value, p)) + return -EFAULT; + + if (bcm47xx_wdt_settimeout(new_value)) + return -EINVAL; + + bcm47xx_wdt_pet(); + + case WDIOC_GETTIMEOUT: + return put_user(wdt_time, p); + + default: + return -ENOTTY; + } +} + +static int bcm47xx_wdt_notify_sys(struct notifier_block *this, + unsigned long code, void *unused) +{ + if (code == SYS_DOWN || code == SYS_HALT) + bcm47xx_wdt_stop(); + return NOTIFY_DONE; +} + +static const struct file_operations bcm47xx_wdt_fops = { + .owner = THIS_MODULE, + .llseek = no_llseek, + .unlocked_ioctl = bcm47xx_wdt_ioctl, + .open = bcm47xx_wdt_open, + .release = bcm47xx_wdt_release, + .write = bcm47xx_wdt_write, +}; + +static struct miscdevice bcm47xx_wdt_miscdev = { + .minor = WATCHDOG_MINOR, + .name = "watchdog", + .fops = &bcm47xx_wdt_fops, +}; + +static struct notifier_block bcm47xx_wdt_notifier = { + .notifier_call = bcm47xx_wdt_notify_sys, +}; + +static int __init bcm47xx_wdt_init(void) +{ + int ret; + + if (bcm47xx_wdt_hw_stop() < 0) + return -ENODEV; + + setup_timer(&wdt_timer, bcm47xx_timer_tick, 0L); + + if (bcm47xx_wdt_settimeout(wdt_time)) { + bcm47xx_wdt_settimeout(WDT_DEFAULT_TIME); + printk(KERN_INFO DRV_NAME ": " + "wdt_time value must be 0 < wdt_time < %d, using %d\n", + (WDT_MAX_TIME + 1), wdt_time); + } + + ret = register_reboot_notifier(&bcm47xx_wdt_notifier); + if (ret) + return ret; + + ret = misc_register(&bcm47xx_wdt_miscdev); + if (ret) { + unregister_reboot_notifier(&bcm47xx_wdt_notifier); + return ret; + } + + printk(KERN_INFO "BCM47xx Watchdog Timer enabled (%d seconds%s)\n", + wdt_time, nowayout ? ", nowayout" : ""); + return 0; +} + +static void __exit bcm47xx_wdt_exit(void) +{ + if (!nowayout) + bcm47xx_wdt_stop(); + + misc_deregister(&bcm47xx_wdt_miscdev); + + unregister_reboot_notifier(&bcm47xx_wdt_notifier); +} + +module_init(bcm47xx_wdt_init); +module_exit(bcm47xx_wdt_exit); + +MODULE_AUTHOR("Aleksandar Radovanovic"); +MODULE_DESCRIPTION("Watchdog driver for Broadcom BCM47xx"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR); -- cgit v1.2.3 From 789cd4702bf830416d2e1794495407be42fe95ad Mon Sep 17 00:00:00 2001 From: Ulrik Bech Hald Date: Fri, 12 Jun 2009 16:18:32 -0500 Subject: [WATCHDOG] OMAP fixes: enable clock in probe, trigger timer reload This patch contains two fixes: 1)In omap_wdt_probe() the watchdog is reset and disabled. This requires register access and the clks needs to be enabled temporarily 2)In omap_wdt_open() the timer register needs to be reloaded to trigger a new timer value (the default of 60s) Tested on OMAP34xx platform (Zoom1) Reviewed-by: Kevin Hilman Signed-off-by: Ulrik Bech Hald Signed-off-by: Wim Van Sebroeck --- drivers/watchdog/omap_wdt.c | 7 +++++++ 1 file changed, 7 insertions(+) (limited to 'drivers') diff --git a/drivers/watchdog/omap_wdt.c b/drivers/watchdog/omap_wdt.c index f2713851aaab..3ed571a2ab18 100644 --- a/drivers/watchdog/omap_wdt.c +++ b/drivers/watchdog/omap_wdt.c @@ -159,6 +159,7 @@ static int omap_wdt_open(struct inode *inode, struct file *file) file->private_data = (void *) wdev; omap_wdt_set_timeout(wdev); + omap_wdt_ping(wdev); /* trigger loading of new timeout value */ omap_wdt_enable(wdev); return nonseekable_open(inode, file); @@ -313,6 +314,9 @@ static int __devinit omap_wdt_probe(struct platform_device *pdev) platform_set_drvdata(pdev, wdev); + clk_enable(wdev->ick); + clk_enable(wdev->fck); + omap_wdt_disable(wdev); omap_wdt_adjust_timeout(timer_margin); @@ -332,6 +336,9 @@ static int __devinit omap_wdt_probe(struct platform_device *pdev) /* autogate OCP interface clock */ __raw_writel(0x01, wdev->base + OMAP_WATCHDOG_SYS_CONFIG); + clk_disable(wdev->ick); + clk_disable(wdev->fck); + omap_wdt_dev = pdev; return 0; -- cgit v1.2.3 From 44df75353bc8f32e26e049284053a61d4f1047d6 Mon Sep 17 00:00:00 2001 From: Tom Mingarelli Date: Thu, 18 Jun 2009 23:28:57 +0000 Subject: [WATCHDOG] hpwdt: Add NMI priority option Add a priority option so that the user can choose if we do the NMI first or last. Signed-off-by: Thomas Mingarelli Signed-off-by: Wim Van Sebroeck --- Documentation/watchdog/hpwdt.txt | 19 +++++++++++++++---- drivers/watchdog/hpwdt.c | 26 +++++++++++++++++++++----- 2 files changed, 36 insertions(+), 9 deletions(-) (limited to 'drivers') diff --git a/Documentation/watchdog/hpwdt.txt b/Documentation/watchdog/hpwdt.txt index 127839e53043..9c24d5ffbb06 100644 --- a/Documentation/watchdog/hpwdt.txt +++ b/Documentation/watchdog/hpwdt.txt @@ -19,30 +19,41 @@ Last reviewed: 06/02/2009 not be updated in a timely fashion and a hardware system reset (also known as an Automatic Server Recovery (ASR)) event will occur. - The hpwdt driver also has three (3) module parameters. They are the following: + The hpwdt driver also has four (4) module parameters. They are the following: soft_margin - allows the user to set the watchdog timer value allow_kdump - allows the user to save off a kernel dump image after an NMI nowayout - basic watchdog parameter that does not allow the timer to be restarted or an impending ASR to be escaped. + priority - determines whether or not the hpwdt driver is first on the + die_notify list to handle NMIs or last. The default value + for this module parameter is 0 or LAST. If the user wants to + enable NMI sourcing then reload the hpwdt driver with + priority=1 (and boot with nmi_watchdog=0). NOTE: More information about watchdog drivers in general, including the ioctl interface to /dev/watchdog can be found in Documentation/watchdog/watchdog-api.txt and Documentation/IPMI.txt. - The NMI sourcing capability is disabled when the driver discovers that the - nmi_watchdog is turned on (nmi_watchdog = 1). This is due to the inability to + The priority parameter was introduced due to other kernel software that relied + on handling NMIs (like oprofile). Keeping hpwdt's priority at 0 (or LAST) + enables the users of NMIs for non critical events to be work as expected. + + The NMI sourcing capability is disabled by default due to the inability to distinguish between "NMI Watchdog Ticks" and "HW generated NMI events" in the Linux kernel. What this means is that the hpwdt nmi handler code is called each time the NMI signal fires off. This could amount to several thousands of NMIs in a matter of seconds. If a user sees the Linux kernel's "dazed and confused" message in the logs or if the system gets into a hung state, then - the user should reboot with nmi_watchdog=0. + the hpwdt driver can be reloaded with the "priority" module parameter set + (priority=1). 1. If the kernel has not been booted with nmi_watchdog turned off then edit /boot/grub/menu.lst and place the nmi_watchdog=0 at the end of the currently booting kernel line. 2. reboot the sever + 3. Once the system comes up perform a rmmod hpwdt + 4. insmod /lib/modules/`uname -r`/kernel/drivers/char/watchdog/hpwdt.ko priority=1 Now, the hpwdt can successfully receive and source the NMI and provide a log message that details the reason for the NMI (as determined by the HP BIOS). diff --git a/drivers/watchdog/hpwdt.c b/drivers/watchdog/hpwdt.c index c0b9169ba5d5..a6c5674c78e6 100644 --- a/drivers/watchdog/hpwdt.c +++ b/drivers/watchdog/hpwdt.c @@ -120,7 +120,8 @@ static int nowayout = WATCHDOG_NOWAYOUT; static char expect_release; static unsigned long hpwdt_is_open; static unsigned int allow_kdump; -static int hpwdt_nmi_sourcing; +static unsigned int hpwdt_nmi_sourcing; +static unsigned int priority; /* hpwdt at end of die_notify list */ static void __iomem *pci_mem_addr; /* the PCI-memory address */ static unsigned long __iomem *hpwdt_timer_reg; @@ -623,7 +624,7 @@ static struct miscdevice hpwdt_miscdev = { static struct notifier_block die_notifier = { .notifier_call = hpwdt_pretimeout, - .priority = 0x7FFFFFFF, + .priority = 0, }; /* @@ -641,7 +642,8 @@ static void __devinit hpwdt_check_nmi_sourcing(struct pci_dev *dev) hpwdt_nmi_sourcing = 1; else dev_warn(&dev->dev, "NMI sourcing is disabled. To enable this " - "functionality you must reboot with nmi_watchdog=0.\n"); + "functionality you must reboot with nmi_watchdog=0 " + "and load the hpwdt driver with priority=1.\n"); } #else static void __devinit hpwdt_check_nmi_sourcing(struct pci_dev *dev) @@ -714,6 +716,14 @@ static int __devinit hpwdt_init_one(struct pci_dev *dev, cmn_regs.u1.rah = 0x0D; cmn_regs.u1.ral = 0x02; + /* + * If the priority is set to 1, then we will be put first on the + * die notify list to handle a critical NMI. The default is to + * be last so other users of the NMI signal can function. + */ + if (priority) + die_notifier.priority = 0x7FFFFFFF; + retval = register_die_notifier(&die_notifier); if (retval != 0) { dev_warn(&dev->dev, @@ -733,9 +743,11 @@ static int __devinit hpwdt_init_one(struct pci_dev *dev, printk(KERN_INFO "hp Watchdog Timer Driver: %s" ", timer margin: %d seconds (nowayout=%d)" - ", allow kernel dump: %s (default = 0/OFF).\n", + ", allow kernel dump: %s (default = 0/OFF)" + ", priority: %s (default = 0/LAST).\n", HPWDT_VERSION, soft_margin, nowayout, - (allow_kdump == 0) ? "OFF" : "ON"); + (allow_kdump == 0) ? "OFF" : "ON", + (priority == 0) ? "LAST" : "FIRST"); return 0; @@ -798,5 +810,9 @@ module_param(nowayout, int, 0); MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default=" __MODULE_STRING(WATCHDOG_NOWAYOUT) ")"); +module_param(priority, int, 0); +MODULE_PARM_DESC(priority, "The hpwdt driver handles NMIs first or last" + " (default = 0/Last)\n"); + module_init(hpwdt_init); module_exit(hpwdt_cleanup); -- cgit v1.2.3 From 9b901ee0cb007eb4e2ee056e5b1c5c2837d53bdb Mon Sep 17 00:00:00 2001 From: Wim Van Sebroeck Date: Fri, 19 Jun 2009 09:32:57 +0000 Subject: [WATCHDOG] wdt_pci.c: remove #ifdef CONFIG_WDT_501_PCI Change the wdt_pci.c watchdog driver so that the code is the same for both the PCI-WDT500 as the PCI-WDT501 card. The selection of the card is now being done via the module parameter: 'type' instead of the config option CONFIG_WDT_501_PCI. Signed-off-by: Wim Van Sebroeck --- drivers/watchdog/Kconfig | 18 ++----- drivers/watchdog/wdt_pci.c | 122 ++++++++++++++++++++++----------------------- 2 files changed, 66 insertions(+), 74 deletions(-) (limited to 'drivers') diff --git a/drivers/watchdog/Kconfig b/drivers/watchdog/Kconfig index e8d45b6ccef8..b1ccc04f3c9a 100644 --- a/drivers/watchdog/Kconfig +++ b/drivers/watchdog/Kconfig @@ -1007,24 +1007,16 @@ config WDTPCI ---help--- If you have a PCI-WDT500/501 watchdog board, say Y here, otherwise N. - To compile this driver as a module, choose M here: the - module will be called wdt_pci. - -config WDT_501_PCI - bool "PCI-WDT501 features" - depends on WDTPCI - help - Saying Y here and creating a character special file /dev/temperature - with major number 10 and minor number 131 ("man mknod") will give - you a thermometer inside your computer: reading from - /dev/temperature yields one byte, the temperature in degrees - Fahrenheit. This works only if you have a PCI-WDT501 watchdog board - installed. + If you have a PCI-WDT501 watchdog board then you can enable the + temperature sensor by setting the type parameter to 501. If you want to enable the Fan Tachometer on the PCI-WDT501, then you can do this via the tachometer parameter. Only do this if you have a fan tachometer actually set up. + To compile this driver as a module, choose M here: the + module will be called wdt_pci. + # # USB-based Watchdog Cards # diff --git a/drivers/watchdog/wdt_pci.c b/drivers/watchdog/wdt_pci.c index c45839a4a34d..7a1bdc7c95a9 100644 --- a/drivers/watchdog/wdt_pci.c +++ b/drivers/watchdog/wdt_pci.c @@ -2,7 +2,7 @@ * Industrial Computer Source PCI-WDT500/501 driver * * (c) Copyright 1996-1997 Alan Cox , - * All Rights Reserved. + * All Rights Reserved. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -99,14 +99,16 @@ MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default=" __MODULE_STRING(WATCHDOG_NOWAYOUT) ")"); -#ifdef CONFIG_WDT_501_PCI /* Support for the Fan Tachometer on the PCI-WDT501 */ static int tachometer; - module_param(tachometer, int, 0); MODULE_PARM_DESC(tachometer, - "PCI-WDT501 Fan Tachometer support (0=disable, default=0)"); -#endif /* CONFIG_WDT_501_PCI */ + "PCI-WDT501 Fan Tachometer support (0=disable, default=0)"); + +static int type = 500; +module_param(type, int, 0); +MODULE_PARM_DESC(type, + "PCI-WDT501 Card type (500 or 501 , default=500)"); /* * Programming support @@ -266,22 +268,21 @@ static int wdtpci_get_status(int *status) *status |= WDIOF_EXTERN1; if (new_status & WDC_SR_ISII1) *status |= WDIOF_EXTERN2; -#ifdef CONFIG_WDT_501_PCI - if (!(new_status & WDC_SR_TGOOD)) - *status |= WDIOF_OVERHEAT; - if (!(new_status & WDC_SR_PSUOVER)) - *status |= WDIOF_POWEROVER; - if (!(new_status & WDC_SR_PSUUNDR)) - *status |= WDIOF_POWERUNDER; - if (tachometer) { - if (!(new_status & WDC_SR_FANGOOD)) - *status |= WDIOF_FANFAULT; + if (type == 501) { + if (!(new_status & WDC_SR_TGOOD)) + *status |= WDIOF_OVERHEAT; + if (!(new_status & WDC_SR_PSUOVER)) + *status |= WDIOF_POWEROVER; + if (!(new_status & WDC_SR_PSUUNDR)) + *status |= WDIOF_POWERUNDER; + if (tachometer) { + if (!(new_status & WDC_SR_FANGOOD)) + *status |= WDIOF_FANFAULT; + } } -#endif /* CONFIG_WDT_501_PCI */ return 0; } -#ifdef CONFIG_WDT_501_PCI /** * wdtpci_get_temperature: * @@ -300,7 +301,6 @@ static int wdtpci_get_temperature(int *temperature) *temperature = (c * 11 / 15) + 7; return 0; } -#endif /* CONFIG_WDT_501_PCI */ /** * wdtpci_interrupt: @@ -327,22 +327,22 @@ static irqreturn_t wdtpci_interrupt(int irq, void *dev_id) printk(KERN_CRIT PFX "status %d\n", status); -#ifdef CONFIG_WDT_501_PCI - if (!(status & WDC_SR_TGOOD)) { - u8 alarm = inb(WDT_RT); - printk(KERN_CRIT PFX "Overheat alarm.(%d)\n", alarm); - udelay(8); - } - if (!(status & WDC_SR_PSUOVER)) - printk(KERN_CRIT PFX "PSU over voltage.\n"); - if (!(status & WDC_SR_PSUUNDR)) - printk(KERN_CRIT PFX "PSU under voltage.\n"); - if (tachometer) { - if (!(status & WDC_SR_FANGOOD)) - printk(KERN_CRIT PFX "Possible fan fault.\n"); + if (type == 501) { + if (!(status & WDC_SR_TGOOD)) { + printk(KERN_CRIT PFX "Overheat alarm.(%d)\n", + inb(WDT_RT)); + udelay(8); + } + if (!(status & WDC_SR_PSUOVER)) + printk(KERN_CRIT PFX "PSU over voltage.\n"); + if (!(status & WDC_SR_PSUUNDR)) + printk(KERN_CRIT PFX "PSU under voltage.\n"); + if (tachometer) { + if (!(status & WDC_SR_FANGOOD)) + printk(KERN_CRIT PFX "Possible fan fault.\n"); + } } -#endif /* CONFIG_WDT_501_PCI */ - if (!(status&WDC_SR_WCCR)) { + if (!(status & WDC_SR_WCCR)) { #ifdef SOFTWARE_REBOOT #ifdef ONLY_TESTING printk(KERN_CRIT PFX "Would Reboot.\n"); @@ -371,12 +371,13 @@ static irqreturn_t wdtpci_interrupt(int irq, void *dev_id) */ static ssize_t wdtpci_write(struct file *file, const char __user *buf, - size_t count, loff_t *ppos) + size_t count, loff_t *ppos) { if (count) { if (!nowayout) { size_t i; + /* In case it was set long ago */ expect_close = 0; for (i = 0; i != count; i++) { @@ -406,10 +407,10 @@ static ssize_t wdtpci_write(struct file *file, const char __user *buf, static long wdtpci_ioctl(struct file *file, unsigned int cmd, unsigned long arg) { - int new_heartbeat; - int status; void __user *argp = (void __user *)arg; int __user *p = argp; + int new_heartbeat; + int status; static struct watchdog_info ident = { .options = WDIOF_SETTIMEOUT| @@ -421,11 +422,12 @@ static long wdtpci_ioctl(struct file *file, unsigned int cmd, /* Add options according to the card we have */ ident.options |= (WDIOF_EXTERN1|WDIOF_EXTERN2); -#ifdef CONFIG_WDT_501_PCI - ident.options |= (WDIOF_OVERHEAT|WDIOF_POWERUNDER|WDIOF_POWEROVER); - if (tachometer) - ident.options |= WDIOF_FANFAULT; -#endif /* CONFIG_WDT_501_PCI */ + if (type == 501) { + ident.options |= (WDIOF_OVERHEAT|WDIOF_POWERUNDER| + WDIOF_POWEROVER); + if (tachometer) + ident.options |= WDIOF_FANFAULT; + } switch (cmd) { case WDIOC_GETSUPPORT: @@ -503,7 +505,6 @@ static int wdtpci_release(struct inode *inode, struct file *file) return 0; } -#ifdef CONFIG_WDT_501_PCI /** * wdtpci_temp_read: * @file: file handle to the watchdog board @@ -554,7 +555,6 @@ static int wdtpci_temp_release(struct inode *inode, struct file *file) { return 0; } -#endif /* CONFIG_WDT_501_PCI */ /** * notify_sys: @@ -596,7 +596,6 @@ static struct miscdevice wdtpci_miscdev = { .fops = &wdtpci_fops, }; -#ifdef CONFIG_WDT_501_PCI static const struct file_operations wdtpci_temp_fops = { .owner = THIS_MODULE, .llseek = no_llseek, @@ -610,7 +609,6 @@ static struct miscdevice temp_miscdev = { .name = "temperature", .fops = &wdtpci_temp_fops, }; -#endif /* CONFIG_WDT_501_PCI */ /* * The WDT card needs to learn about soft shutdowns in order to @@ -633,6 +631,11 @@ static int __devinit wdtpci_init_one(struct pci_dev *dev, return -ENODEV; } + if (type != 500 && type != 501) { + printk(KERN_ERR PFX "unknown card type '%d'.\n", type); + return -ENODEV; + } + if (pci_enable_device(dev)) { printk(KERN_ERR PFX "Not possible to enable PCI Device\n"); return -ENODEV; @@ -678,15 +681,15 @@ static int __devinit wdtpci_init_one(struct pci_dev *dev, goto out_irq; } -#ifdef CONFIG_WDT_501_PCI - ret = misc_register(&temp_miscdev); - if (ret) { - printk(KERN_ERR PFX + if (type == 501) { + ret = misc_register(&temp_miscdev); + if (ret) { + printk(KERN_ERR PFX "cannot register miscdev on minor=%d (err=%d)\n", - TEMP_MINOR, ret); - goto out_rbt; + TEMP_MINOR, ret); + goto out_rbt; + } } -#endif /* CONFIG_WDT_501_PCI */ ret = misc_register(&wdtpci_miscdev); if (ret) { @@ -698,20 +701,18 @@ static int __devinit wdtpci_init_one(struct pci_dev *dev, printk(KERN_INFO PFX "initialized. heartbeat=%d sec (nowayout=%d)\n", heartbeat, nowayout); -#ifdef CONFIG_WDT_501_PCI - printk(KERN_INFO "wdt: Fan Tachometer is %s\n", + if (type == 501) + printk(KERN_INFO "wdt: Fan Tachometer is %s\n", (tachometer ? "Enabled" : "Disabled")); -#endif /* CONFIG_WDT_501_PCI */ ret = 0; out: return ret; out_misc: -#ifdef CONFIG_WDT_501_PCI - misc_deregister(&temp_miscdev); + if (type == 501) + misc_deregister(&temp_miscdev); out_rbt: -#endif /* CONFIG_WDT_501_PCI */ unregister_reboot_notifier(&wdtpci_notifier); out_irq: free_irq(irq, &wdtpci_miscdev); @@ -728,9 +729,8 @@ static void __devexit wdtpci_remove_one(struct pci_dev *pdev) /* here we assume only one device will ever have * been picked up and registered by probe function */ misc_deregister(&wdtpci_miscdev); -#ifdef CONFIG_WDT_501_PCI - misc_deregister(&temp_miscdev); -#endif /* CONFIG_WDT_501_PCI */ + if (type == 501) + misc_deregister(&temp_miscdev); unregister_reboot_notifier(&wdtpci_notifier); free_irq(irq, &wdtpci_miscdev); release_region(io, 16); -- cgit v1.2.3