--- a/arch/arm/mach-sl2312/sl3516_device.c +++ b/arch/arm/mach-sl2312/sl3516_device.c @@ -76,9 +76,30 @@ .resource = sl3516_sata0_resources, }; +static struct resource sl351x_wdt_resources[] = { + [0] = { + .start = SL2312_WAQTCHDOG_BASE + 0x00, + .end = SL2312_WAQTCHDOG_BASE + 0x1C, + .flags = IORESOURCE_MEM, + }, + [1] = { + .start = IRQ_WATCHDOG, + .end = IRQ_WATCHDOG, + .flags = IORESOURCE_IRQ, + }, +}; + +static struct platform_device sl351x_wdt = { + .name = "sl351x-wdt", + .id = -1, + .resource = sl351x_wdt_resources, + .num_resources = ARRAY_SIZE(sl351x_wdt_resources), +}; + static struct platform_device *sata_devices[] __initdata = { &sata_device, &sata0_device, + &sl351x_wdt, }; static int __init sl3516_init(void) --- a/drivers/char/watchdog/Kconfig +++ b/drivers/char/watchdog/Kconfig @@ -171,6 +171,17 @@ To compile this driver as a module, choose M here: the module will be called ep93xx_wdt. +config WATCHDOG_SL351X + tristate "SL351x Watchdog" + depends on WATCHDOG && ARCH_SL2312 + help + This driver adds watchdog support for the integrated watchdog in the + SL351x processors (Farraday core). If you have one of these processors + and wish to have watchdog support enabled, say Y, otherwise say N. + + To compile this driver as a module, choose M here: the + module will be called sl351x_wdt. + config OMAP_WATCHDOG tristate "OMAP Watchdog" depends on ARCH_OMAP16XX || ARCH_OMAP24XX --- a/drivers/char/watchdog/Makefile +++ b/drivers/char/watchdog/Makefile @@ -36,6 +36,7 @@ obj-$(CONFIG_SA1100_WATCHDOG) += sa1100_wdt.o obj-$(CONFIG_MPCORE_WATCHDOG) += mpcore_wdt.o obj-$(CONFIG_EP93XX_WATCHDOG) += ep93xx_wdt.o +obj-$(CONFIG_WATCHDOG_SL351X) += sl351x_wdt.o obj-$(CONFIG_PNX4008_WATCHDOG) += pnx4008_wdt.o obj-$(CONFIG_IOP_WATCHDOG) += iop_wdt.o obj-$(CONFIG_DAVINCI_WATCHDOG) += davinci_wdt.o --- /dev/null +++ b/drivers/char/watchdog/sl351x_wdt.c @@ -0,0 +1,332 @@ +#include <linux/module.h> +#include <linux/types.h> +#include <linux/fs.h> +#include <linux/mm.h> +#include <linux/errno.h> +#include <linux/init.h> +#include <linux/miscdevice.h> +#include <linux/watchdog.h> +#include <linux/platform_device.h> +#include <asm/uaccess.h> +#include <asm/arch/sl2312.h> +#include <asm/arch/hardware.h> +#include <asm/arch/irqs.h> +#include <asm/arch/watchdog.h> +#include <asm/io.h> +#include <linux/interrupt.h> + +#define WATCHDOG_TEST 1 +#define PFX "sl351x-wdt: " + +#define _WATCHDOG_COUNTER 0x00 +#define _WATCHDOG_LOAD 0x04 +#define _WATCHDOG_RESTART 0x08 +#define _WATCHDOG_CR 0x0C +#define _WATCHDOG_STATUS 0x10 +#define _WATCHDOG_CLEAR 0x14 +#define _WATCHDOG_INTRLEN 0x18 + +static struct resource *wdt_mem; +static struct resource *wdt_irq; +static void __iomem *wdt_base; +static int wdt_margin = WATCHDOG_TIMEOUT_MARGIN; /* in range of 0 .. 60s */ + +static int open_state = WATCHDOG_DRIVER_CLOSE; +static int wd_expire = 0; + +static void watchdog_enable(void) +{ + unsigned long wdcr; + + wdcr = readl(wdt_base + _WATCHDOG_CR); + wdcr |= (WATCHDOG_WDENABLE_MSK|WATCHDOG_WDRST_MSK); +#ifdef WATCHDOG_TEST + wdcr |= WATCHDOG_WDINTR_MSK; +// wdcr &= ~WATCHDOG_WDRST_MSK; +#endif + wdcr &= ~WATCHDOG_WDCLOCK_MSK; + writel(wdcr, wdt_base + _WATCHDOG_CR); +} + +static void watchdog_set_timeout(unsigned long timeout) +{ + timeout = WATCHDOG_TIMEOUT_SCALE * timeout; + writel(timeout, wdt_base + _WATCHDOG_LOAD); + writel(WATCHDOG_RESTART_VALUE, wdt_base + _WATCHDOG_RESTART); +} + +static void watchdog_keepalive(void) +{ + writel(WATCHDOG_RESTART_VALUE, wdt_base + _WATCHDOG_RESTART); +} + +static void watchdog_disable(void) +{ + unsigned long wdcr; + + wdcr = readl(wdt_base + _WATCHDOG_CR); + wdcr &= ~WATCHDOG_WDENABLE_MSK; + writel(wdcr, wdt_base + _WATCHDOG_CR); +} + + +#ifdef WATCHDOG_TEST +static irqreturn_t watchdog_irq(int irq, void *dev_id, struct pt_regs *regs) +{ + unsigned int clear; + + writel(WATCHDOG_CLEAR_STATUS, wdt_base + _WATCHDOG_CLEAR); + printk(KERN_INFO PFX "Watchdog timeout, resetting system...\n"); + + clear = __raw_readl(IO_ADDRESS(SL2312_INTERRUPT_BASE)+0x0C); + clear &= 0x01; + __raw_writel(clear,IO_ADDRESS(SL2312_INTERRUPT_BASE)+0x08); + wd_expire = 1; + return IRQ_HANDLED; +} + +#endif + +#define OPTIONS WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING | WDIOF_MAGICCLOSE +static struct watchdog_info sl351x_wdt_ident = { + .options = OPTIONS, + .firmware_version = 0, + .identity = "sl351x Watchdog", +}; + +struct file_operations watchdog_fops = { + .write = watchdog_write, + .read = watchdog_read, + .open = watchdog_open, + .release = watchdog_release, + .ioctl = watchdog_ioctl, +}; + +static int watchdog_open(struct inode *inode, struct file *filp) +{ + if (open_state == WATCHDOG_DRIVER_OPEN) + return -EBUSY; + + wd_expire = 0; + + watchdog_disable(); + watchdog_set_timeout(wdt_margin); + watchdog_enable(); + + printk(KERN_INFO PFX "watchog timer enabled, margin: %ds.\n", wdt_margin); + open_state = WATCHDOG_DRIVER_OPEN; + + return nonseekable_open(inode, filp); +} + +static int watchdog_release(struct inode *inode, struct file *filp) +{ + watchdog_disable(); + + open_state = WATCHDOG_DRIVER_CLOSE; + wd_expire = 0; + printk(KERN_INFO PFX "watchog timer disabled, margin: %ds.\n", wdt_margin); + + return 0; +} + +static ssize_t watchdog_read(struct file *filp, char *buf, size_t count, loff_t *off) +{ + int i; + unsigned long val; + + + for(i=0;i< count;i++) + { + if ((i%4)==0) + val = *((unsigned long *)WATCHDOG_COUNTER); + buf[i] = (val & 0xFF); + val >>= 8; + } + return count; +} + +static ssize_t watchdog_write(struct file *filp, const char *buf, size_t len, loff_t *off) +{ + /* Refresh the timer. */ + if (len) { + watchdog_keepalive(); + } + return len; + +} + +static int watchdog_ioctl(struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg) +{ + void __user *argp = (void __user *)arg; + int margin; + + switch(cmd) + { + case WDIOC_GETSUPPORT: + return copy_to_user(argp, &sl351x_wdt_ident, + sizeof(sl351x_wdt_ident)) ? -EFAULT : 0; + + case WDIOC_GETSTATUS: + case WDIOC_GETBOOTSTATUS: + return put_user(0, (int __user*)argp); + + case WDIOC_KEEPALIVE: + watchdog_keepalive(); + return 0; + + case WDIOC_SETTIMEOUT: + if (get_user(margin, (int __user*)argp)) + return -EFAULT; + + /* Arbitrary, can't find the card's limits */ + if ((margin < 0) || (margin > 60)) + return -EINVAL; + + // watchdog_disable(); + wdt_margin = margin; + watchdog_set_timeout(margin); + watchdog_keepalive(); + // watchdog_enable(); + + /* Fall through */ + + case WDIOC_GETTIMEOUT: + return put_user(wdt_margin, (int *)arg); + + default: + return -ENOIOCTLCMD; + } +} + +static struct miscdevice wd_dev= { + WATCHDOG_MINOR, + "watchdog", + &watchdog_fops +}; + +static char banner[] __initdata = KERN_INFO "SL351x Watchdog Timer, (c) 2007 WILIBOX\n"; + +static int sl351x_wdt_probe(struct platform_device *pdev) +{ + struct resource *res; + int ret, size; + unsigned long wdcr; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (res == NULL) { + printk(KERN_INFO PFX "failed to get memory region resouce\n"); + return -ENOMEM; + } + + size = (res->end-res->start)+1; + + wdt_mem = request_mem_region(res->start, size, pdev->name); + if (wdt_mem == NULL) { + printk(KERN_INFO PFX "failed to get memory region\n"); + return -ENOENT; + } + + wdt_base = ioremap(res->start, size); + if (wdt_base == NULL) { + printk(KERN_INFO PFX "failed to ioremap() region\n"); + return -EINVAL; + } + + res = platform_get_resource(pdev, IORESOURCE_IRQ, 0); + if (res == NULL) { + printk(KERN_INFO PFX "failed to get irq resource\n"); + return -ENOENT; + } + + wdt_irq = res; + + ret = request_irq(res->start, watchdog_irq, 0, pdev->name, pdev); + if (ret != 0) { + printk(KERN_INFO PFX "failed to install irq (%d)\n", ret); + return ret; + } + + wdcr = readl(wdt_base + _WATCHDOG_CR); + if (wdcr & WATCHDOG_WDENABLE_MSK) { + printk(KERN_INFO PFX "Found watchdog in enabled state, reseting ...\n"); + wdcr &= ~WATCHDOG_WDENABLE_MSK; + writel(wdcr, wdt_base + _WATCHDOG_CR); + } + + ret = misc_register(&wd_dev); + + return ret; +} + +static int sl351x_wdt_remove(struct platform_device *pdev) +{ + if (wdt_base != NULL) { + iounmap(wdt_base); + wdt_base = NULL; + } + + if (wdt_irq != NULL) { + free_irq(wdt_irq->start, pdev); + release_resource(wdt_irq); + wdt_irq = NULL; + } + + if (wdt_mem != NULL) { + release_resource(wdt_mem); + wdt_mem = NULL; + } + + misc_deregister(&wd_dev); + + return 0; +} + +static void sl351x_wdt_shutdown(struct platform_device *dev) +{ + watchdog_disable(); +} + +#ifdef CONFIG_PM +static int sl351x_wdt_suspend(struct platform_device *dev, pm_message_t state) +{ + watchdog_disable(); +} + +static int sl351x_wdt_resume(struct platform_device *dev) +{ + watchdog_set_timeout(wdt_margin); + watchdog_enable(); +} + +#else +#define sl351x_wdt_suspend NULL +#define sl351x_wdt_resume NULL +#endif + +static struct platform_driver sl351x_wdt_driver = { + .probe = sl351x_wdt_probe, + .remove = sl351x_wdt_remove, + .shutdown = sl351x_wdt_shutdown, + .suspend = sl351x_wdt_suspend, + .resume = sl351x_wdt_resume, + .driver = { + .owner = THIS_MODULE, + .name = "sl351x-wdt", + }, +}; + +static int __init watchdog_init(void) +{ + printk(banner); + return platform_driver_register(&sl351x_wdt_driver); +} + +static void __exit watchdog_exit(void) +{ + platform_driver_unregister(&sl351x_wdt_driver); +} + +module_init(watchdog_init); +module_exit(watchdog_exit);