diff options
Diffstat (limited to 'target/linux/storm/patches/009-watchdog.patch')
-rw-r--r-- | target/linux/storm/patches/009-watchdog.patch | 398 |
1 files changed, 398 insertions, 0 deletions
diff --git a/target/linux/storm/patches/009-watchdog.patch b/target/linux/storm/patches/009-watchdog.patch new file mode 100644 index 0000000000..3055a94822 --- /dev/null +++ b/target/linux/storm/patches/009-watchdog.patch @@ -0,0 +1,398 @@ +--- 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); |