From: Florian Fainelli Date: Tue, 9 Jun 2009 21:28:15 +0000 (+0000) Subject: [brcm63xx] fix the watchdog driver and enable it by default X-Git-Url: http://git.openwrt.org/?p=openwrt%2Fsvn-archive%2Farchive.git;a=commitdiff_plain;h=dadfb436680c3e6a4a85351c1402ec145a315c6f [brcm63xx] fix the watchdog driver and enable it by default SVN-Revision: 16394 --- diff --git a/target/linux/brcm63xx/config-2.6.27 b/target/linux/brcm63xx/config-2.6.27 index 255080286b..83af14f8ae 100644 --- a/target/linux/brcm63xx/config-2.6.27 +++ b/target/linux/brcm63xx/config-2.6.27 @@ -19,6 +19,7 @@ CONFIG_BCM63XX_CPU_6348=y CONFIG_BCM63XX_CPU_6358=y CONFIG_BCM63XX_ENET=y CONFIG_BCM63XX_PHY=y +CONFIG_BCM63XX_WDT=y CONFIG_BITREVERSE=y CONFIG_BLK_DEV_IO_TRACE=y CONFIG_BOARD_BCM963XX=y @@ -59,7 +60,6 @@ CONFIG_CPU_SUPPORTS_HIGHMEM=y # CONFIG_CPU_VR41XX is not set CONFIG_CRAMFS=y CONFIG_CSRC_R4K=y -CONFIG_DEFAULT_BIC=y CONFIG_DEFAULT_CFQ=y # CONFIG_DEFAULT_DEADLINE is not set CONFIG_DEFAULT_IOSCHED="cfq" @@ -106,7 +106,6 @@ CONFIG_INOTIFY=y CONFIG_INOTIFY_USER=y CONFIG_IOSCHED_CFQ=y # CONFIG_IOSCHED_DEADLINE is not set -CONFIG_IP_MROUTE=y CONFIG_IP_PIMSM_V1=y CONFIG_IP_PIMSM_V2=y CONFIG_IRQ_CPU=y @@ -198,12 +197,11 @@ CONFIG_SYS_SUPPORTS_BIG_ENDIAN=y CONFIG_TICK_ONESHOT=y CONFIG_TRAD_SIGNALS=y CONFIG_USB_EHCI_BIG_ENDIAN_MMIO=y -# CONFIG_USB_EHCI_ROOT_HUB_TT is not set CONFIG_USB_OHCI_BIG_ENDIAN_DESC=y CONFIG_USB_OHCI_BIG_ENDIAN_MMIO=y CONFIG_USB_SUPPORT=y # CONFIG_VGASTATE is not set # CONFIG_VIA_RHINE is not set CONFIG_VM_EVENT_COUNTERS=y -# CONFIG_WATCHDOG is not set +CONFIG_WATCHDOG_NOWAYOUT=y CONFIG_ZONE_DMA_FLAG=0 diff --git a/target/linux/brcm63xx/files/drivers/watchdog/bcm63xx_wdt.c b/target/linux/brcm63xx/files/drivers/watchdog/bcm63xx_wdt.c index b40c52ad96..cc41ec5b9d 100644 --- a/target/linux/brcm63xx/files/drivers/watchdog/bcm63xx_wdt.c +++ b/target/linux/brcm63xx/files/drivers/watchdog/bcm63xx_wdt.c @@ -10,19 +10,22 @@ * 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 -#include +#include #include +#include #include +#include +#include +#include #include #include @@ -31,102 +34,96 @@ #define PFX KBUILD_MODNAME #define WDT_HZ 50000000 /* Fclk */ -#define WDT_INTERVAL (40) /* in seconds */ +#define WDT_DEFAULT_TIME 30 /* seconds */ +#define WDT_MAX_TIME 256 /* seconds */ static struct { void __iomem *regs; - struct completion stop; - int running; struct timer_list timer; - int queue; int default_ticks; unsigned long inuse; + atomic_t ticks; } bcm63xx_wdt_device; -static int ticks = 100 * WDT_HZ; - static int expect_close; static int timeout; +static int wdt_time = WDT_DEFAULT_TIME; 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 void bcm63xx_wdt_toggle(void) +/* HW functions */ +static void bcm63xx_wdt_hw_start(void) { + bcm_writel(0xffffffff, bcm63xx_wdt_device.regs + WDT_DEFVAL_REG); bcm_writel(WDT_START_1, bcm63xx_wdt_device.regs + WDT_CTL_REG); bcm_writel(WDT_START_2, bcm63xx_wdt_device.regs + WDT_CTL_REG); } -static void bcm63xx_wdt_start(void) +static void bcm63xx_wdt_hw_stop(void) { - if (!bcm63xx_wdt_device.inuse) { - bcm63xx_wdt_toggle(); - mod_timer(&bcm63xx_wdt_device.timer, jiffies + WDT_INTERVAL); - } - - bcm63xx_wdt_device.running++; + bcm_writel(WDT_STOP_1, bcm63xx_wdt_device.regs + WDT_CTL_REG); + bcm_writel(WDT_STOP_2, bcm63xx_wdt_device.regs + WDT_CTL_REG); } -static void bcm63xx_wdt_stop(void) +static void bcm63xx_timer_tick(unsigned long unused) { - if (bcm63xx_wdt_device.running) { - bcm_writel(WDT_STOP_1, bcm63xx_wdt_device.regs + WDT_CTL_REG); - bcm_writel(WDT_STOP_2, bcm63xx_wdt_device.regs + WDT_CTL_REG); + if (!atomic_dec_and_test(&bcm63xx_wdt_device.ticks)) { + bcm63xx_wdt_hw_start(); + mod_timer(&bcm63xx_wdt_device.timer, jiffies + HZ); + } else + printk(KERN_CRIT PFX "watchdog will restart system\n"); +} - bcm63xx_wdt_device.running = 0; - } +static void bcm63xx_wdt_pet(void) +{ + atomic_set(&bcm63xx_wdt_device.ticks, wdt_time); } -static void bcm63xx_wdt_set(int new_timeout) +static void bcm63xx_wdt_start(void) { - new_timeout *= WDT_HZ; - bcm_writel(new_timeout, bcm63xx_wdt_device.regs + WDT_DEFVAL_REG); + bcm63xx_wdt_pet(); + bcm63xx_timer_tick(0); } -static void bcm63xx_wdt_reset(void) +static void bcm63xx_wdt_pause(void) { - ticks = bcm63xx_wdt_device.default_ticks; + del_timer_sync(&bcm63xx_wdt_device.timer); + bcm63xx_wdt_hw_stop(); } -static void bcm63xx_wdt_update(unsigned long unused) +static int bcm63xx_wdt_settimeout(int new_time) { - if (bcm63xx_wdt_device.running) - ticks--; + if ((new_time <= 0) || (new_time > WDT_MAX_TIME)) + return -EINVAL; - bcm63xx_wdt_toggle(); + wdt_time = new_time; - if (bcm63xx_wdt_device.queue && ticks) - mod_timer(&bcm63xx_wdt_device.timer, - jiffies + WDT_INTERVAL); - else - complete(&bcm63xx_wdt_device.stop); + return 0; } static int bcm63xx_wdt_open(struct inode *inode, struct file *file) { if (test_and_set_bit(0, &bcm63xx_wdt_device.inuse)) return -EBUSY; - - if (nowayout) - __module_get(THIS_MODULE); - + + bcm63xx_wdt_start(); return nonseekable_open(inode, file); } static int bcm63xx_wdt_release(struct inode *inode, struct file *file) { - if (expect_close && nowayout == 0) { - bcm63xx_wdt_stop(); - printk(KERN_INFO PFX ": disabling watchdog timer\n"); - module_put(THIS_MODULE); - } else + if (expect_close == 42) + bcm63xx_wdt_pause(); + else { printk(KERN_CRIT PFX - ": device closed unexpectedly. WDT will not stop !\n"); - + ": Unexpected close, not stopping watchdog!\n"); + bcm63xx_wdt_start(); + } clear_bit(0, &bcm63xx_wdt_device.inuse); + expect_close = 0; return 0; } @@ -145,70 +142,81 @@ static ssize_t bcm63xx_wdt_write(struct file *file, const char *data, if (get_user(c, data + i)) return -EFAULT; if (c == 'V') - expect_close = 1; + expect_close = 42; } } - bcm63xx_wdt_update(0); - return len; + bcm63xx_wdt_pet(); } - return 0; + return len; } +static struct watchdog_info bcm63xx_wdt_info = { + .identity = PFX, + .options = WDIOF_SETTIMEOUT | + WDIOF_KEEPALIVEPING | + WDIOF_MAGICCLOSE, +}; + + static long bcm63xx_wdt_ioctl(struct file *file, unsigned int cmd, unsigned long arg) { void __user *argp = (void __user *)arg; - int new_timeout; - unsigned int value; - static struct watchdog_info ident = { - .options = WDIOF_SETTIMEOUT | - WDIOF_KEEPALIVEPING | - WDIOF_MAGICCLOSE, - .identity = "BCM63xx Watchdog", - }; + int __user *p = argp; + int new_value, retval = -EINVAL; + switch (cmd) { - case WDIOC_KEEPALIVE: - bcm63xx_wdt_reset(); - break; + case WDIOC_GETSUPPORT: + return copy_to_user(argp, &bcm63xx_wdt_info, + sizeof(bcm63xx_wdt_info)) ? -EFAULT : 0; + case WDIOC_GETSTATUS: case WDIOC_GETBOOTSTATUS: - value = bcm_readl(bcm63xx_wdt_device.regs + WDT_DEFVAL_REG); - if (copy_to_user(argp, &value, sizeof(int))) - return -EFAULT; - break; - case WDIOC_GETSUPPORT: - if (copy_to_user(argp, &ident, sizeof(ident))) - return -EFAULT; - break; + return put_user(0, p); + case WDIOC_SETOPTIONS: - if (copy_from_user(&value, argp, sizeof(int))) + if (get_user(new_value, p)) return -EFAULT; - switch (value) { - case WDIOS_ENABLECARD: + + if (new_value & WDIOS_DISABLECARD) { + bcm63xx_wdt_pause(); + retval = 0; + } + if (new_value & WDIOS_ENABLECARD) { bcm63xx_wdt_start(); - break; - case WDIOS_DISABLECARD: - bcm63xx_wdt_stop(); - default: - return -EINVAL; + retval = 0; } - break; + + return retval; + + case WDIOC_KEEPALIVE: + bcm63xx_wdt_pet(); + return 0; + case WDIOC_SETTIMEOUT: - if (copy_from_user(&new_timeout, argp, sizeof(int))) + if (get_user(new_value, p)) return -EFAULT; - if (new_timeout < 5) - return -EINVAL; - if (new_timeout > 40) + + if (bcm63xx_wdt_settimeout(new_value)) return -EINVAL; - bcm63xx_wdt_set(new_timeout); - bcm63xx_wdt_toggle(); + + bcm63xx_wdt_pet(); + case WDIOC_GETTIMEOUT: - return copy_to_user(argp, &timeout, sizeof(int)); + return put_user(wdt_time, p); + default: return -ENOTTY; + } +} - return 0; +static int bcm63xx_wdt_notify_sys(struct notifier_block *this, + unsigned long code, void *unused) +{ + if (code == SYS_DOWN || code == SYS_HALT) + bcm63xx_wdt_pause(); + return NOTIFY_DONE; } static struct file_operations bcm63xx_wdt_fops = { @@ -226,10 +234,17 @@ static struct miscdevice bcm63xx_wdt_miscdev = { .fops = &bcm63xx_wdt_fops, }; +static struct notifier_block bcm63xx_wdt_notifier = { + .notifier_call = bcm63xx_wdt_notify_sys, +}; + + static int bcm63xx_wdt_probe(struct platform_device *pdev) { int ret; struct resource *r; + + setup_timer(&bcm63xx_wdt_device.timer, bcm63xx_timer_tick, 0L); r = platform_get_resource(pdev, IORESOURCE_MEM, 0); if (!r) { @@ -245,6 +260,20 @@ static int bcm63xx_wdt_probe(struct platform_device *pdev) return -ENXIO; } + if (bcm63xx_wdt_settimeout(wdt_time)) { + bcm63xx_wdt_settimeout(WDT_DEFAULT_TIME); + printk(KERN_INFO PFX + ": wdt_time value must be 1 <= wdt_time <= 256, using %d\n", + wdt_time); + } + + ret = register_reboot_notifier(&bcm63xx_wdt_notifier); + if (ret) { + printk(KERN_ERR PFX + "failed to register reboot_notifier\n"); + return ret; + } + ret = misc_register(&bcm63xx_wdt_miscdev); if (ret < 0) { printk(KERN_ERR PFX @@ -252,37 +281,27 @@ static int bcm63xx_wdt_probe(struct platform_device *pdev) goto unmap; } - init_completion(&bcm63xx_wdt_device.stop); - bcm63xx_wdt_device.queue = 0; - - clear_bit(0, &bcm63xx_wdt_device.inuse); - - setup_timer(&bcm63xx_wdt_device.timer, bcm63xx_wdt_update, 0L); - - bcm63xx_wdt_device.default_ticks = ticks; - bcm63xx_wdt_set(ticks); - bcm63xx_wdt_start(); - - printk(KERN_INFO PFX " started, timer margin: %d sec\n", WDT_INTERVAL); + printk(KERN_INFO PFX " started, timer margin: %d sec\n", WDT_DEFAULT_TIME); return 0; unmap: + unregister_reboot_notifier(&bcm63xx_wdt_notifier); iounmap(bcm63xx_wdt_device.regs); return ret; } static int bcm63xx_wdt_remove(struct platform_device *pdev) { - if (bcm63xx_wdt_device.queue) { - bcm63xx_wdt_device.queue = 0; - wait_for_completion(&bcm63xx_wdt_device.stop); - } + if (!nowayout) + bcm63xx_wdt_pause(); misc_deregister(&bcm63xx_wdt_miscdev); iounmap(bcm63xx_wdt_device.regs); + unregister_reboot_notifier(&bcm63xx_wdt_notifier); + return 0; }