diff options
author | David S. Miller <davem@davemloft.net> | 2008-09-16 14:11:43 -0700 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2008-09-16 14:11:43 -0700 |
commit | 2e57572a50a4de41c6cbc879a4866a312d4cd316 (patch) | |
tree | c4f58ec96c06642c4b415b881d3f0a3b673d5b44 /drivers/watchdog | |
parent | 9b2e43ae4e9609f80034dfe8de895045cac52d77 (diff) | |
parent | f948cc6ab9e61a8e88d70ee9aafc690e6d26f92c (diff) |
Merge branch 'master' of master.kernel.org:/pub/scm/linux/kernel/git/davem/sparc-2.6
Conflicts:
arch/sparc64/kernel/pci_psycho.c
Diffstat (limited to 'drivers/watchdog')
-rw-r--r-- | drivers/watchdog/Kconfig | 24 | ||||
-rw-r--r-- | drivers/watchdog/Makefile | 3 | ||||
-rw-r--r-- | drivers/watchdog/at91rm9200_wdt.c | 2 | ||||
-rw-r--r-- | drivers/watchdog/hpwdt.c | 93 | ||||
-rw-r--r-- | drivers/watchdog/mpc8xx_wdt.c | 170 | ||||
-rw-r--r-- | drivers/watchdog/mpc8xxx_wdt.c | 11 | ||||
-rw-r--r-- | drivers/watchdog/pc87413_wdt.c | 1 | ||||
-rw-r--r-- | drivers/watchdog/rc32434_wdt.c | 344 | ||||
-rw-r--r-- | drivers/watchdog/rdc321x_wdt.c | 285 | ||||
-rw-r--r-- | drivers/watchdog/s3c2410_wdt.c | 16 |
10 files changed, 714 insertions, 235 deletions
diff --git a/drivers/watchdog/Kconfig b/drivers/watchdog/Kconfig index db20542796bf..c51036716700 100644 --- a/drivers/watchdog/Kconfig +++ b/drivers/watchdog/Kconfig @@ -465,6 +465,16 @@ config PC87413_WDT Most people will say N. +config RDC321X_WDT + tristate "RDC R-321x SoC watchdog" + depends on X86_RDC321X + help + This is the driver for the built in hardware watchdog + in the RDC R-321x SoC. + + To compile this driver as a module, choose M here: the + module will be called rdc321x_wdt. + config 60XX_WDT tristate "SBC-60XX Watchdog Timer" depends on X86 @@ -633,6 +643,16 @@ config SBC_EPX_C3_WATCHDOG # MIPS Architecture +config RC32434_WDT + tristate "IDT RC32434 SoC Watchdog Timer" + depends on MIKROTIK_RB532 + help + Hardware driver for the IDT RC32434 SoC built-in + watchdog timer. + + To compile this driver as a module, choose M here: the + module will be called rc32434_wdt. + config INDYDOG tristate "Indy/I2 Hardware Watchdog" depends on SGI_HAS_INDYDOG @@ -692,10 +712,6 @@ config MPC5200_WDT tristate "MPC5200 Watchdog Timer" depends on PPC_MPC52xx -config 8xx_WDT - tristate "MPC8xx Watchdog Timer" - depends on 8xx - config 8xxx_WDT tristate "MPC8xxx Platform Watchdog Timer" depends on PPC_8xx || PPC_83xx || PPC_86xx diff --git a/drivers/watchdog/Makefile b/drivers/watchdog/Makefile index b214612d819d..6702d2ef0434 100644 --- a/drivers/watchdog/Makefile +++ b/drivers/watchdog/Makefile @@ -75,6 +75,7 @@ obj-$(CONFIG_HP_WATCHDOG) += hpwdt.o obj-$(CONFIG_SC1200_WDT) += sc1200wdt.o obj-$(CONFIG_SCx200_WDT) += scx200_wdt.o obj-$(CONFIG_PC87413_WDT) += pc87413_wdt.o +obj-$(CONFIG_RDC321X_WDT) += rdc321x_wdt.o obj-$(CONFIG_60XX_WDT) += sbc60xxwdt.o obj-$(CONFIG_SBC8360_WDT) += sbc8360.o obj-$(CONFIG_SBC7240_WDT) += sbc7240_wdt.o @@ -94,6 +95,7 @@ obj-$(CONFIG_SBC_EPX_C3_WATCHDOG) += sbc_epx_c3.o # M68KNOMMU Architecture # MIPS Architecture +obj-$(CONFIG_RC32434_WDT) += rc32434_wdt.o obj-$(CONFIG_INDYDOG) += indydog.o obj-$(CONFIG_WDT_MTX1) += mtx-1_wdt.o obj-$(CONFIG_WDT_RM9K_GPI) += rm9k_wdt.o @@ -104,7 +106,6 @@ obj-$(CONFIG_TXX9_WDT) += txx9wdt.o # PARISC Architecture # POWERPC Architecture -obj-$(CONFIG_8xx_WDT) += mpc8xx_wdt.o obj-$(CONFIG_MPC5200_WDT) += mpc5200_wdt.o obj-$(CONFIG_8xxx_WDT) += mpc8xxx_wdt.o obj-$(CONFIG_MV64X60_WDT) += mv64x60_wdt.o diff --git a/drivers/watchdog/at91rm9200_wdt.c b/drivers/watchdog/at91rm9200_wdt.c index d061f0ad2d20..993e5f52afef 100644 --- a/drivers/watchdog/at91rm9200_wdt.c +++ b/drivers/watchdog/at91rm9200_wdt.c @@ -241,7 +241,7 @@ static int at91wdt_resume(struct platform_device *pdev) { if (at91wdt_busy) at91_wdt_start(); - return 0; + return 0; } #else diff --git a/drivers/watchdog/hpwdt.c b/drivers/watchdog/hpwdt.c index d039d5f2fd1c..a3765e0be4a8 100644 --- a/drivers/watchdog/hpwdt.c +++ b/drivers/watchdog/hpwdt.c @@ -116,6 +116,7 @@ static unsigned int reload; /* the computed soft_margin */ static int nowayout = WATCHDOG_NOWAYOUT; static char expect_release; static unsigned long hpwdt_is_open; +static unsigned int allow_kdump; static void __iomem *pci_mem_addr; /* the PCI-memory address */ static unsigned long __iomem *hpwdt_timer_reg; @@ -221,19 +222,19 @@ static int __devinit cru_detect(unsigned long map_entry, if (cmn_regs.u1.ral != 0) { printk(KERN_WARNING - "hpwdt: Call succeeded but with an error: 0x%x\n", - cmn_regs.u1.ral); + "hpwdt: Call succeeded but with an error: 0x%x\n", + cmn_regs.u1.ral); } else { physical_bios_base = cmn_regs.u2.rebx; physical_bios_offset = cmn_regs.u4.redx; cru_length = cmn_regs.u3.recx; cru_physical_address = - physical_bios_base + physical_bios_offset; + physical_bios_base + physical_bios_offset; /* If the values look OK, then map it in. */ if ((physical_bios_base + physical_bios_offset)) { cru_rom_addr = - ioremap(cru_physical_address, cru_length); + ioremap(cru_physical_address, cru_length); if (cru_rom_addr) retval = 0; } @@ -356,7 +357,6 @@ asm(".text \n\t" "call *%r12 \n\t" "pushfq \n\t" "popq %r12 \n\t" - "popfq \n\t" "movl %eax, (%r9) \n\t" "movl %ebx, 4(%r9) \n\t" "movl %ecx, 8(%r9) \n\t" @@ -390,10 +390,10 @@ static void __devinit dmi_find_cru(const struct dmi_header *dm) smbios_cru64_ptr = (struct smbios_cru64_info *) dm; if (smbios_cru64_ptr->signature == CRU_BIOS_SIGNATURE_VALUE) { cru_physical_address = - smbios_cru64_ptr->physical_address + - smbios_cru64_ptr->double_offset; + smbios_cru64_ptr->physical_address + + smbios_cru64_ptr->double_offset; cru_rom_addr = ioremap(cru_physical_address, - smbios_cru64_ptr->double_length); + smbios_cru64_ptr->double_length); } } } @@ -405,7 +405,7 @@ static int __devinit detect_cru_service(void) dmi_walk(dmi_find_cru); /* if cru_rom_addr has been set then we found a CRU service */ - return ((cru_rom_addr != NULL) ? 0: -ENODEV); + return ((cru_rom_addr != NULL) ? 0 : -ENODEV); } /* ------------------------------------------------------------------------- */ @@ -413,34 +413,6 @@ static int __devinit detect_cru_service(void) #endif /* - * NMI Handler - */ -static int hpwdt_pretimeout(struct notifier_block *nb, unsigned long ulReason, - void *data) -{ - unsigned long rom_pl; - static int die_nmi_called; - - if (ulReason != DIE_NMI && ulReason != DIE_NMI_IPI) - return NOTIFY_OK; - - spin_lock_irqsave(&rom_lock, rom_pl); - if (!die_nmi_called) - asminline_call(&cmn_regs, cru_rom_addr); - die_nmi_called = 1; - spin_unlock_irqrestore(&rom_lock, rom_pl); - if (cmn_regs.u1.ral == 0) { - printk(KERN_WARNING "hpwdt: An NMI occurred, " - "but unable to determine source.\n"); - } else { - panic("An NMI occurred, please see the Integrated " - "Management Log for details.\n"); - } - - return NOTIFY_STOP; -} - -/* * Watchdog operations */ static void hpwdt_start(void) @@ -484,6 +456,36 @@ static int hpwdt_change_timer(int new_margin) } /* + * NMI Handler + */ +static int hpwdt_pretimeout(struct notifier_block *nb, unsigned long ulReason, + void *data) +{ + unsigned long rom_pl; + static int die_nmi_called; + + if (ulReason != DIE_NMI && ulReason != DIE_NMI_IPI) + return NOTIFY_OK; + + spin_lock_irqsave(&rom_lock, rom_pl); + if (!die_nmi_called) + asminline_call(&cmn_regs, cru_rom_addr); + die_nmi_called = 1; + spin_unlock_irqrestore(&rom_lock, rom_pl); + if (cmn_regs.u1.ral == 0) { + printk(KERN_WARNING "hpwdt: An NMI occurred, " + "but unable to determine source.\n"); + } else { + if (allow_kdump) + hpwdt_stop(); + panic("An NMI occurred, please see the Integrated " + "Management Log for details.\n"); + } + + return NOTIFY_STOP; +} + +/* * /dev/watchdog handling */ static int hpwdt_open(struct inode *inode, struct file *file) @@ -625,17 +627,18 @@ static struct notifier_block die_notifier = { */ static int __devinit hpwdt_init_one(struct pci_dev *dev, - const struct pci_device_id *ent) + const struct pci_device_id *ent) { int retval; /* * First let's find out if we are on an iLO2 server. We will * not run on a legacy ASM box. + * So we only support the G5 ProLiant servers and higher. */ if (dev->subsystem_vendor != PCI_VENDOR_ID_HP) { dev_warn(&dev->dev, - "This server does not have an iLO2 ASIC.\n"); + "This server does not have an iLO2 ASIC.\n"); return -ENODEV; } @@ -669,7 +672,7 @@ static int __devinit hpwdt_init_one(struct pci_dev *dev, retval = detect_cru_service(); if (retval < 0) { dev_warn(&dev->dev, - "Unable to detect the %d Bit CRU Service.\n", + "Unable to detect the %d Bit CRU Service.\n", HPWDT_ARCH); goto error_get_cru; } @@ -684,7 +687,7 @@ static int __devinit hpwdt_init_one(struct pci_dev *dev, retval = register_die_notifier(&die_notifier); if (retval != 0) { dev_warn(&dev->dev, - "Unable to register a die notifier (err=%d).\n", + "Unable to register a die notifier (err=%d).\n", retval); goto error_die_notifier; } @@ -699,8 +702,9 @@ static int __devinit hpwdt_init_one(struct pci_dev *dev, printk(KERN_INFO "hp Watchdog Timer Driver: 1.00" - ", timer margin: %d seconds( nowayout=%d).\n", - soft_margin, nowayout); + ", timer margin: %d seconds (nowayout=%d)" + ", allow kernel dump: %s (default = 0/OFF).\n", + soft_margin, nowayout, (allow_kdump == 0) ? "OFF" : "ON"); return 0; @@ -755,6 +759,9 @@ MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR); module_param(soft_margin, int, 0); MODULE_PARM_DESC(soft_margin, "Watchdog timeout in seconds"); +module_param(allow_kdump, int, 0); +MODULE_PARM_DESC(allow_kdump, "Start a kernel dump after NMI occurs"); + module_param(nowayout, int, 0); MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default=" __MODULE_STRING(WATCHDOG_NOWAYOUT) ")"); diff --git a/drivers/watchdog/mpc8xx_wdt.c b/drivers/watchdog/mpc8xx_wdt.c deleted file mode 100644 index 1336425acf20..000000000000 --- a/drivers/watchdog/mpc8xx_wdt.c +++ /dev/null @@ -1,170 +0,0 @@ -/* - * mpc8xx_wdt.c - MPC8xx watchdog userspace interface - * - * Author: Florian Schirmer <jolt@tuxbox.org> - * - * 2002 (c) Florian Schirmer <jolt@tuxbox.org> 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 <linux/fs.h> -#include <linux/init.h> -#include <linux/kernel.h> -#include <linux/miscdevice.h> -#include <linux/module.h> -#include <linux/watchdog.h> -#include <asm/8xx_immap.h> -#include <linux/uaccess.h> -#include <linux/io.h> -#include <syslib/m8xx_wdt.h> - -static unsigned long wdt_opened; -static int wdt_status; -static spinlock_t wdt_lock; - -static void mpc8xx_wdt_handler_disable(void) -{ - volatile uint __iomem *piscr; - piscr = (uint *)&((immap_t *)IMAP_ADDR)->im_sit.sit_piscr; - - if (!m8xx_has_internal_rtc) - m8xx_wdt_stop_timer(); - else - out_be32(piscr, in_be32(piscr) & ~(PISCR_PIE | PISCR_PTE)); - printk(KERN_NOTICE "mpc8xx_wdt: keep-alive handler deactivated\n"); -} - -static void mpc8xx_wdt_handler_enable(void) -{ - volatile uint __iomem *piscr; - piscr = (uint *)&((immap_t *)IMAP_ADDR)->im_sit.sit_piscr; - - if (!m8xx_has_internal_rtc) - m8xx_wdt_install_timer(); - else - out_be32(piscr, in_be32(piscr) | PISCR_PIE | PISCR_PTE); - printk(KERN_NOTICE "mpc8xx_wdt: keep-alive handler activated\n"); -} - -static int mpc8xx_wdt_open(struct inode *inode, struct file *file) -{ - if (test_and_set_bit(0, &wdt_opened)) - return -EBUSY; - m8xx_wdt_reset(); - mpc8xx_wdt_handler_disable(); - return nonseekable_open(inode, file); -} - -static int mpc8xx_wdt_release(struct inode *inode, struct file *file) -{ - m8xx_wdt_reset(); -#if !defined(CONFIG_WATCHDOG_NOWAYOUT) - mpc8xx_wdt_handler_enable(); -#endif - clear_bit(0, &wdt_opened); - return 0; -} - -static ssize_t mpc8xx_wdt_write(struct file *file, const char *data, - size_t len, loff_t *ppos) -{ - if (len) { - spin_lock(&wdt_lock); - m8xx_wdt_reset(); - spin_unlock(&wdt_lock); - } - return len; -} - -static long mpc8xx_wdt_ioctl(struct file *file, - unsigned int cmd, unsigned long arg) -{ - int timeout; - static struct watchdog_info info = { - .options = WDIOF_KEEPALIVEPING, - .firmware_version = 0, - .identity = "MPC8xx watchdog", - }; - - switch (cmd) { - case WDIOC_GETSUPPORT: - if (copy_to_user((void *)arg, &info, sizeof(info))) - return -EFAULT; - break; - - case WDIOC_GETSTATUS: - case WDIOC_GETBOOTSTATUS: - if (put_user(wdt_status, (int *)arg)) - return -EFAULT; - wdt_status &= ~WDIOF_KEEPALIVEPING; - break; - - case WDIOC_GETTEMP: - return -EOPNOTSUPP; - - case WDIOC_SETOPTIONS: - return -EOPNOTSUPP; - - case WDIOC_KEEPALIVE: - spin_lock(&wdt_lock); - m8xx_wdt_reset(); - wdt_status |= WDIOF_KEEPALIVEPING; - spin_unlock(&wdt_lock); - break; - - case WDIOC_SETTIMEOUT: - return -EOPNOTSUPP; - - case WDIOC_GETTIMEOUT: - spin_lock(&wdt_lock); - timeout = m8xx_wdt_get_timeout(); - spin_unlock(&wdt_lock); - if (put_user(timeout, (int *)arg)) - return -EFAULT; - break; - - default: - return -ENOTTY; - } - - return 0; -} - -static const struct file_operations mpc8xx_wdt_fops = { - .owner = THIS_MODULE, - .llseek = no_llseek, - .write = mpc8xx_wdt_write, - .unlocked_ioctl = mpc8xx_wdt_ioctl, - .open = mpc8xx_wdt_open, - .release = mpc8xx_wdt_release, -}; - -static struct miscdevice mpc8xx_wdt_miscdev = { - .minor = WATCHDOG_MINOR, - .name = "watchdog", - .fops = &mpc8xx_wdt_fops, -}; - -static int __init mpc8xx_wdt_init(void) -{ - spin_lock_init(&wdt_lock); - return misc_register(&mpc8xx_wdt_miscdev); -} - -static void __exit mpc8xx_wdt_exit(void) -{ - misc_deregister(&mpc8xx_wdt_miscdev); - - m8xx_wdt_reset(); - mpc8xx_wdt_handler_enable(); -} - -module_init(mpc8xx_wdt_init); -module_exit(mpc8xx_wdt_exit); - -MODULE_AUTHOR("Florian Schirmer <jolt@tuxbox.org>"); -MODULE_DESCRIPTION("MPC8xx watchdog driver"); -MODULE_LICENSE("GPL"); -MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR); diff --git a/drivers/watchdog/mpc8xxx_wdt.c b/drivers/watchdog/mpc8xxx_wdt.c index f2094960e662..38c588ee694f 100644 --- a/drivers/watchdog/mpc8xxx_wdt.c +++ b/drivers/watchdog/mpc8xxx_wdt.c @@ -48,6 +48,7 @@ struct mpc8xxx_wdt_type { }; static struct mpc8xxx_wdt __iomem *wd_base; +static int mpc8xxx_wdt_init_late(void); static u16 timeout = 0xffff; module_param(timeout, ushort, 0); @@ -213,6 +214,12 @@ static int __devinit mpc8xxx_wdt_probe(struct of_device *ofdev, else timeout_sec = timeout / freq; +#ifdef MODULE + ret = mpc8xxx_wdt_init_late(); + if (ret) + goto err_unmap; +#endif + pr_info("WDT driver for MPC8xxx initialized. mode:%s timeout=%d " "(%d seconds)\n", reset ? "reset" : "interrupt", timeout, timeout_sec); @@ -280,7 +287,7 @@ static struct of_platform_driver mpc8xxx_wdt_driver = { * very early to start pinging the watchdog (misc devices are not yet * available), and later module_init() just registers the misc device. */ -static int __init mpc8xxx_wdt_init_late(void) +static int mpc8xxx_wdt_init_late(void) { int ret; @@ -295,7 +302,9 @@ static int __init mpc8xxx_wdt_init_late(void) } return 0; } +#ifndef MODULE module_init(mpc8xxx_wdt_init_late); +#endif static int __init mpc8xxx_wdt_init(void) { diff --git a/drivers/watchdog/pc87413_wdt.c b/drivers/watchdog/pc87413_wdt.c index e91ada72da1d..484c215e9f3f 100644 --- a/drivers/watchdog/pc87413_wdt.c +++ b/drivers/watchdog/pc87413_wdt.c @@ -30,7 +30,6 @@ #include <linux/init.h> #include <linux/spinlock.h> #include <linux/moduleparam.h> -#include <linux/version.h> #include <linux/io.h> #include <linux/uaccess.h> diff --git a/drivers/watchdog/rc32434_wdt.c b/drivers/watchdog/rc32434_wdt.c new file mode 100644 index 000000000000..6756bcb009ed --- /dev/null +++ b/drivers/watchdog/rc32434_wdt.c @@ -0,0 +1,344 @@ +/* + * IDT Interprise 79RC32434 watchdog driver + * + * Copyright (C) 2006, Ondrej Zajicek <santiago@crfreenet.org> + * Copyright (C) 2008, Florian Fainelli <florian@openwrt.org> + * + * based on + * SoftDog 0.05: A Software Watchdog Device + * + * (c) Copyright 1996 Alan Cox <alan@redhat.com>, 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. + * + */ + +#include <linux/module.h> +#include <linux/types.h> +#include <linux/kernel.h> +#include <linux/fs.h> +#include <linux/mm.h> +#include <linux/miscdevice.h> +#include <linux/watchdog.h> +#include <linux/reboot.h> +#include <linux/smp_lock.h> +#include <linux/init.h> +#include <linux/platform_device.h> +#include <linux/uaccess.h> + +#include <asm/bootinfo.h> +#include <asm/time.h> +#include <asm/mach-rc32434/integ.h> + +#define MAX_TIMEOUT 20 +#define RC32434_WDT_INTERVAL (15 * HZ) + +#define VERSION "0.2" + +static struct { + struct completion stop; + int running; + struct timer_list timer; + int queue; + int default_ticks; + unsigned long inuse; +} rc32434_wdt_device; + +static struct integ __iomem *wdt_reg; +static int ticks = 100 * HZ; + +static int expect_close; +static int timeout; + +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 rc32434_wdt_start(void) +{ + u32 val; + + if (!rc32434_wdt_device.inuse) { + writel(0, &wdt_reg->wtcount); + + val = RC32434_ERR_WRE; + writel(readl(&wdt_reg->errcs) | val, &wdt_reg->errcs); + + val = RC32434_WTC_EN; + writel(readl(&wdt_reg->wtc) | val, &wdt_reg->wtc); + } + rc32434_wdt_device.running++; +} + +static void rc32434_wdt_stop(void) +{ + u32 val; + + if (rc32434_wdt_device.running) { + + val = ~RC32434_WTC_EN; + writel(readl(&wdt_reg->wtc) & val, &wdt_reg->wtc); + + val = ~RC32434_ERR_WRE; + writel(readl(&wdt_reg->errcs) & val, &wdt_reg->errcs); + + rc32434_wdt_device.running = 0; + } +} + +static void rc32434_wdt_set(int new_timeout) +{ + u32 cmp = new_timeout * HZ; + u32 state, val; + + timeout = new_timeout; + /* + * store and disable WTC + */ + state = (u32)(readl(&wdt_reg->wtc) & RC32434_WTC_EN); + val = ~RC32434_WTC_EN; + writel(readl(&wdt_reg->wtc) & val, &wdt_reg->wtc); + + writel(0, &wdt_reg->wtcount); + writel(cmp, &wdt_reg->wtcompare); + + /* + * restore WTC + */ + + writel(readl(&wdt_reg->wtc) | state, &wdt_reg); +} + +static void rc32434_wdt_reset(void) +{ + ticks = rc32434_wdt_device.default_ticks; +} + +static void rc32434_wdt_update(unsigned long unused) +{ + if (rc32434_wdt_device.running) + ticks--; + + writel(0, &wdt_reg->wtcount); + + if (rc32434_wdt_device.queue && ticks) + mod_timer(&rc32434_wdt_device.timer, + jiffies + RC32434_WDT_INTERVAL); + else + complete(&rc32434_wdt_device.stop); +} + +static int rc32434_wdt_open(struct inode *inode, struct file *file) +{ + if (test_and_set_bit(0, &rc32434_wdt_device.inuse)) + return -EBUSY; + + if (nowayout) + __module_get(THIS_MODULE); + + return nonseekable_open(inode, file); +} + +static int rc32434_wdt_release(struct inode *inode, struct file *file) +{ + if (expect_close && nowayout == 0) { + rc32434_wdt_stop(); + printk(KERN_INFO KBUILD_MODNAME ": disabling watchdog timer\n"); + module_put(THIS_MODULE); + } else + printk(KERN_CRIT KBUILD_MODNAME + ": device closed unexpectedly. WDT will not stop !\n"); + + clear_bit(0, &rc32434_wdt_device.inuse); + return 0; +} + +static ssize_t rc32434_wdt_write(struct file *file, const char *data, + size_t len, loff_t *ppos) +{ + if (len) { + if (!nowayout) { + size_t i; + + /* In case it was set long ago */ + expect_close = 0; + + for (i = 0; i != len; i++) { + char c; + if (get_user(c, data + i)) + return -EFAULT; + if (c == 'V') + expect_close = 1; + } + } + rc32434_wdt_update(0); + return len; + } + return 0; +} + +static int rc32434_wdt_ioctl(struct inode *inode, 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 = "RC32434_WDT Watchdog", + }; + switch (cmd) { + case WDIOC_KEEPALIVE: + rc32434_wdt_reset(); + break; + case WDIOC_GETSTATUS: + case WDIOC_GETBOOTSTATUS: + value = readl(&wdt_reg->wtcount); + 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; + case WDIOC_SETOPTIONS: + if (copy_from_user(&value, argp, sizeof(int))) + return -EFAULT; + switch (value) { + case WDIOS_ENABLECARD: + rc32434_wdt_start(); + break; + case WDIOS_DISABLECARD: + rc32434_wdt_stop(); + default: + return -EINVAL; + } + break; + case WDIOC_SETTIMEOUT: + if (copy_from_user(&new_timeout, argp, sizeof(int))) + return -EFAULT; + if (new_timeout < 1) + return -EINVAL; + if (new_timeout > MAX_TIMEOUT) + return -EINVAL; + rc32434_wdt_set(new_timeout); + case WDIOC_GETTIMEOUT: + return copy_to_user(argp, &timeout, sizeof(int)); + default: + return -ENOTTY; + } + + return 0; +} + +static struct file_operations rc32434_wdt_fops = { + .owner = THIS_MODULE, + .llseek = no_llseek, + .write = rc32434_wdt_write, + .ioctl = rc32434_wdt_ioctl, + .open = rc32434_wdt_open, + .release = rc32434_wdt_release, +}; + +static struct miscdevice rc32434_wdt_miscdev = { + .minor = WATCHDOG_MINOR, + .name = "watchdog", + .fops = &rc32434_wdt_fops, +}; + +static char banner[] = KERN_INFO KBUILD_MODNAME + ": Watchdog Timer version " VERSION ", timer margin: %d sec\n"; + +static int rc32434_wdt_probe(struct platform_device *pdev) +{ + int ret; + struct resource *r; + + r = platform_get_resource_byname(pdev, IORESOURCE_MEM, "rb500_wdt_res"); + if (!r) { + printk(KERN_ERR KBUILD_MODNAME + "failed to retrieve resources\n"); + return -ENODEV; + } + + wdt_reg = ioremap_nocache(r->start, r->end - r->start); + if (!wdt_reg) { + printk(KERN_ERR KBUILD_MODNAME + "failed to remap I/O resources\n"); + return -ENXIO; + } + + ret = misc_register(&rc32434_wdt_miscdev); + + if (ret < 0) { + printk(KERN_ERR KBUILD_MODNAME + "failed to register watchdog device\n"); + goto unmap; + } + + init_completion(&rc32434_wdt_device.stop); + rc32434_wdt_device.queue = 0; + + clear_bit(0, &rc32434_wdt_device.inuse); + + setup_timer(&rc32434_wdt_device.timer, rc32434_wdt_update, 0L); + + rc32434_wdt_device.default_ticks = ticks; + + rc32434_wdt_start(); + + printk(banner, timeout); + + return 0; + +unmap: + iounmap(wdt_reg); + return ret; +} + +static int rc32434_wdt_remove(struct platform_device *pdev) +{ + if (rc32434_wdt_device.queue) { + rc32434_wdt_device.queue = 0; + wait_for_completion(&rc32434_wdt_device.stop); + } + misc_deregister(&rc32434_wdt_miscdev); + + iounmap(wdt_reg); + + return 0; +} + +static struct platform_driver rc32434_wdt = { + .probe = rc32434_wdt_probe, + .remove = rc32434_wdt_remove, + .driver = { + .name = "rc32434_wdt", + } +}; + +static int __init rc32434_wdt_init(void) +{ + return platform_driver_register(&rc32434_wdt); +} + +static void __exit rc32434_wdt_exit(void) +{ + platform_driver_unregister(&rc32434_wdt); +} + +module_init(rc32434_wdt_init); +module_exit(rc32434_wdt_exit); + +MODULE_AUTHOR("Ondrej Zajicek <santiago@crfreenet.org>," + "Florian Fainelli <florian@openwrt.org>"); +MODULE_DESCRIPTION("Driver for the IDT RC32434 SoC watchdog"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR); diff --git a/drivers/watchdog/rdc321x_wdt.c b/drivers/watchdog/rdc321x_wdt.c new file mode 100644 index 000000000000..9108efa73e7d --- /dev/null +++ b/drivers/watchdog/rdc321x_wdt.c @@ -0,0 +1,285 @@ +/* + * RDC321x watchdog driver + * + * Copyright (C) 2007 Florian Fainelli <florian@openwrt.org> + * + * This driver is highly inspired from the cpu5_wdt driver + * + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#include <linux/module.h> +#include <linux/moduleparam.h> +#include <linux/types.h> +#include <linux/errno.h> +#include <linux/miscdevice.h> +#include <linux/fs.h> +#include <linux/init.h> +#include <linux/ioport.h> +#include <linux/timer.h> +#include <linux/completion.h> +#include <linux/jiffies.h> +#include <linux/platform_device.h> +#include <linux/watchdog.h> +#include <linux/io.h> +#include <linux/uaccess.h> + +#include <asm/mach-rdc321x/rdc321x_defs.h> + +#define RDC_WDT_MASK 0x80000000 /* Mask */ +#define RDC_WDT_EN 0x00800000 /* Enable bit */ +#define RDC_WDT_WTI 0x00200000 /* Generate CPU reset/NMI/WDT on timeout */ +#define RDC_WDT_RST 0x00100000 /* Reset bit */ +#define RDC_WDT_WIF 0x00040000 /* WDT IRQ Flag */ +#define RDC_WDT_IRT 0x00000100 /* IRQ Routing table */ +#define RDC_WDT_CNT 0x00000001 /* WDT count */ + +#define RDC_CLS_TMR 0x80003844 /* Clear timer */ + +#define RDC_WDT_INTERVAL (HZ/10+1) + +static int ticks = 1000; + +/* some device data */ + +static struct { + struct completion stop; + int running; + struct timer_list timer; + int queue; + int default_ticks; + unsigned long inuse; + spinlock_t lock; +} rdc321x_wdt_device; + +/* generic helper functions */ + +static void rdc321x_wdt_trigger(unsigned long unused) +{ + unsigned long flags; + + if (rdc321x_wdt_device.running) + ticks--; + + /* keep watchdog alive */ + spin_lock_irqsave(&rdc321x_wdt_device.lock, flags); + outl(RDC_WDT_EN | inl(RDC3210_CFGREG_DATA), + RDC3210_CFGREG_DATA); + spin_unlock_irqrestore(&rdc321x_wdt_device.lock, flags); + + /* requeue?? */ + if (rdc321x_wdt_device.queue && ticks) + mod_timer(&rdc321x_wdt_device.timer, + jiffies + RDC_WDT_INTERVAL); + else { + /* ticks doesn't matter anyway */ + complete(&rdc321x_wdt_device.stop); + } + +} + +static void rdc321x_wdt_reset(void) +{ + ticks = rdc321x_wdt_device.default_ticks; +} + +static void rdc321x_wdt_start(void) +{ + unsigned long flags; + + if (!rdc321x_wdt_device.queue) { + rdc321x_wdt_device.queue = 1; + + /* Clear the timer */ + spin_lock_irqsave(&rdc321x_wdt_device.lock, flags); + outl(RDC_CLS_TMR, RDC3210_CFGREG_ADDR); + + /* Enable watchdog and set the timeout to 81.92 us */ + outl(RDC_WDT_EN | RDC_WDT_CNT, RDC3210_CFGREG_DATA); + spin_unlock_irqrestore(&rdc321x_wdt_device.lock, flags); + + mod_timer(&rdc321x_wdt_device.timer, + jiffies + RDC_WDT_INTERVAL); + } + + /* if process dies, counter is not decremented */ + rdc321x_wdt_device.running++; +} + +static int rdc321x_wdt_stop(void) +{ + if (rdc321x_wdt_device.running) + rdc321x_wdt_device.running = 0; + + ticks = rdc321x_wdt_device.default_ticks; + + return -EIO; +} + +/* filesystem operations */ +static int rdc321x_wdt_open(struct inode *inode, struct file *file) +{ + if (test_and_set_bit(0, &rdc321x_wdt_device.inuse)) + return -EBUSY; + + return nonseekable_open(inode, file); +} + +static int rdc321x_wdt_release(struct inode *inode, struct file *file) +{ + clear_bit(0, &rdc321x_wdt_device.inuse); + return 0; +} + +static int rdc321x_wdt_ioctl(struct inode *inode, struct file *file, + unsigned int cmd, unsigned long arg) +{ + void __user *argp = (void __user *)arg; + unsigned int value; + static struct watchdog_info ident = { + .options = WDIOF_CARDRESET, + .identity = "RDC321x WDT", + }; + unsigned long flags; + + switch (cmd) { + case WDIOC_KEEPALIVE: + rdc321x_wdt_reset(); + break; + case WDIOC_GETSTATUS: + /* Read the value from the DATA register */ + spin_lock_irqsave(&rdc321x_wdt_device.lock, flags); + value = inl(RDC3210_CFGREG_DATA); + spin_unlock_irqrestore(&rdc321x_wdt_device.lock, flags); + 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; + case WDIOC_SETOPTIONS: + if (copy_from_user(&value, argp, sizeof(int))) + return -EFAULT; + switch (value) { + case WDIOS_ENABLECARD: + rdc321x_wdt_start(); + break; + case WDIOS_DISABLECARD: + return rdc321x_wdt_stop(); + default: + return -EINVAL; + } + break; + default: + return -ENOTTY; + } + return 0; +} + +static ssize_t rdc321x_wdt_write(struct file *file, const char __user *buf, + size_t count, loff_t *ppos) +{ + if (!count) + return -EIO; + + rdc321x_wdt_reset(); + + return count; +} + +static const struct file_operations rdc321x_wdt_fops = { + .owner = THIS_MODULE, + .llseek = no_llseek, + .ioctl = rdc321x_wdt_ioctl, + .open = rdc321x_wdt_open, + .write = rdc321x_wdt_write, + .release = rdc321x_wdt_release, +}; + +static struct miscdevice rdc321x_wdt_misc = { + .minor = WATCHDOG_MINOR, + .name = "watchdog", + .fops = &rdc321x_wdt_fops, +}; + +static int __devinit rdc321x_wdt_probe(struct platform_device *pdev) +{ + int err; + + err = misc_register(&rdc321x_wdt_misc); + if (err < 0) { + printk(KERN_ERR |