TS-72xx watchdog driver Signed-off-by: Matthieu Crapet Index: linux-2.6.24/arch/arm/mach-ep93xx/ts72xx.c =================================================================== --- linux-2.6.24.orig/arch/arm/mach-ep93xx/ts72xx.c 2008-02-03 14:09:54.000000000 +0100 +++ linux-2.6.24/arch/arm/mach-ep93xx/ts72xx.c 2008-02-03 14:16:09.000000000 +0100 @@ -183,6 +183,26 @@ .resource = ts72xx_eth_resource, }; +static struct resource ts72xx_watchdog_resources[] = { + [0] = { + .start = TS72XX_WATCHDOG_CONTROL_PHYS_BASE, + .end = TS72XX_WATCHDOG_CONTROL_PHYS_BASE + 0x0fff, + .flags = IORESOURCE_MEM, + }, + [1] = { + .start = TS72XX_WATCHDOG_FEED_PHYS_BASE, + .end = TS72XX_WATCHDOG_FEED_PHYS_BASE + 0x0fff, + .flags = IORESOURCE_MEM, + }, +}; + +static struct platform_device ts72xx_watchdog_device = { + .name = "ts72xx_wdt", + .id = -1, + .num_resources = ARRAY_SIZE(ts72xx_watchdog_resources), + .resource = ts72xx_watchdog_resources, +}; + static void __init ts72xx_init_machine(void) { ep93xx_init_devices(); @@ -193,6 +213,7 @@ memcpy(ts72xx_eth_data.dev_addr, (void *)(EP93XX_ETHERNET_BASE + 0x50), 6); platform_device_register(&ts72xx_eth_device); + platform_device_register(&ts72xx_watchdog_device); } MACHINE_START(TS72XX, "Technologic Systems TS-72xx SBC") Index: linux-2.6.24/drivers/watchdog/Kconfig =================================================================== --- linux-2.6.24.orig/drivers/watchdog/Kconfig 2008-01-24 23:58:37.000000000 +0100 +++ linux-2.6.24/drivers/watchdog/Kconfig 2008-02-03 14:16:09.000000000 +0100 @@ -247,6 +247,18 @@ # H8300 Architecture +config TS72XX_WATCHDOG + tristate "TS-72xx Watchdog" + depends on WATCHDOG && ARCH_EP93XX && MACH_TS72XX + help + Say Y here if to include support for the CPLD watchdog + included on Technologic Systems SBC. + + NOTE: timeout value is given in milliseconds, not in seconds. + + To compile this driver as a module, choose M here: the + module will be called ts72xx_wdt. + # X86 (i386 + ia64 + x86_64) Architecture config ACQUIRE_WDT Index: linux-2.6.24/drivers/watchdog/Makefile =================================================================== --- linux-2.6.24.orig/drivers/watchdog/Makefile 2008-01-24 23:58:37.000000000 +0100 +++ linux-2.6.24/drivers/watchdog/Makefile 2008-02-03 14:16:09.000000000 +0100 @@ -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_TS72XX_WATCHDOG) += ts72xx_wdt.o obj-$(CONFIG_PNX4008_WATCHDOG) += pnx4008_wdt.o obj-$(CONFIG_IOP_WATCHDOG) += iop_wdt.o obj-$(CONFIG_DAVINCI_WATCHDOG) += davinci_wdt.o Index: linux-2.6.24/drivers/watchdog/ts72xx_wdt.c =================================================================== --- /dev/null 1970-01-01 00:00:00.000000000 +0000 +++ linux-2.6.24/drivers/watchdog/ts72xx_wdt.c 2008-02-03 14:16:09.000000000 +0100 @@ -0,0 +1,332 @@ +/* + * TS-72xx Watchdog Driver for Technologic Systems boards. + * + * Based on ep93xx_wdt.c by Lehtiniemi & + * Alessandro Zummo + * and ib700wdt.c by Charles Howes + * and mpc83xx_wdt.c by Dave Updegraff & + * Kumar Gala + * + * (c) Copyright 2006 Matthieu Crapet + * + * 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 driver only deals with native timeout provided by CPLD : + * 1/4s, 1/2s, 1s, 2s, 4s and 8s. No external timer is used. + * Notice that we must ping before modifying the control register. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define WATCHDOG_VERSION "0.2" +#define PFX "ts72xx_wdt: " + +#define WATCHDOG_TIMEOUT 8000 /* 8 seconds */ +#define WDT_IN_USE 0 +#define WDT_OK_TO_CLOSE 1 + +static unsigned long ts72xx_wdt_status; +static unsigned char ts72xx_wdt_cpld_value = 0x7; +static int nowayout = WATCHDOG_NOWAYOUT; +static int timeout = WATCHDOG_TIMEOUT; + +static int ts72xx_wdt_times[12] = { + 6000, 3000, 1500, 750, 275, 0, + 8000, 4000, 2000, 1000, 500, 250 +}; + +static void __iomem *control_register; +static void __iomem *feed_register; + + +/* + * Kernel methods. + */ + +static inline void ts72xx_wdt_ping(void) +{ + __raw_writew(0x05, feed_register); +} + +static inline void ts72xx_wdt_enable(void) +{ + __raw_writew(0x05, feed_register); + __raw_writew(ts72xx_wdt_cpld_value, control_register); +} + +static inline void ts72xx_wdt_disable(void) +{ + __raw_writew(0x05, feed_register); + __raw_writew(0, control_register); +} + +static inline void ts72xx_parse_timeout(int value) +{ + unsigned char cpld_value = 0x7; + int i; + + if ((value > 8000) || (value < 250)) { + timeout = WATCHDOG_TIMEOUT; + printk(KERN_INFO PFX "Timeout value out of range, set to %d\n", timeout); + } else { + for (i = 0; i < 6; i++) { + if (value >= ts72xx_wdt_times[i]) { + timeout = ts72xx_wdt_times[i+6]; + + if (value != timeout) + printk(KERN_INFO PFX "Timeout value rounded to %d\n", timeout); + + if (i >= 3) /* cpld_value can't be 4 */ + i++; + + cpld_value = 7 - i; + break; + } + } + } + + ts72xx_wdt_cpld_value = cpld_value; +} + +static ssize_t ts72xx_wdt_write(struct file *file, const char __user *buf, + size_t count, loff_t *ppos) +{ + /* Can't seek (pwrite) on this device */ + if (*ppos != file->f_pos) + return -ESPIPE; + + if (count) { + if (!nowayout) { + size_t i; + + clear_bit(WDT_OK_TO_CLOSE, &ts72xx_wdt_status); + + for (i = 0; i != count; i++) { + char c; + + if (get_user(c, buf + i)) + return -EFAULT; + + if (c == 'V') + set_bit(WDT_OK_TO_CLOSE, &ts72xx_wdt_status); + else + clear_bit(WDT_OK_TO_CLOSE, &ts72xx_wdt_status); + } + } + ts72xx_wdt_ping(); + } + + return count; +} + +static int ts72xx_wdt_ioctl(struct inode *inode, struct file *file, + unsigned int cmd, unsigned long arg) +{ + int new_margin; + int ret = -ENOIOCTLCMD; + + static struct watchdog_info ident = { + .options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING | WDIOF_MAGICCLOSE, + .firmware_version = 1, + .identity = "TS-72xx Watchdog", + }; + + switch (cmd) { + case WDIOC_GETSUPPORT: + ret = copy_to_user((struct watchdog_info __user *)arg, &ident, + sizeof(ident)) ? -EFAULT : 0; + break; + + case WDIOC_GETSTATUS: + case WDIOC_GETBOOTSTATUS: + ret = put_user(0, (int __user *)arg); + break; + + case WDIOC_KEEPALIVE: + ts72xx_wdt_ping(); + ret = 0; + break; + + case WDIOC_SETTIMEOUT: + if (get_user(new_margin, (int __user *)arg)) + return -EFAULT; + + ts72xx_parse_timeout(new_margin); + ts72xx_wdt_enable(); + /* Fall */ + + case WDIOC_GETTIMEOUT: + ret = put_user(timeout, (int __user *)arg); + break; + } + + return ret; +} + +static int ts72xx_wdt_open(struct inode *inode, struct file *file) +{ + if (test_and_set_bit(WDT_IN_USE, &ts72xx_wdt_status)) + return -EBUSY; + + if (nowayout) { + __module_get(THIS_MODULE); + } + + ts72xx_wdt_enable(); + ts72xx_wdt_ping(); + + return nonseekable_open(inode, file); +} + +static int ts72xx_wdt_close(struct inode *inode, struct file *file) +{ + if (test_bit(WDT_OK_TO_CLOSE, &ts72xx_wdt_status)) + ts72xx_wdt_disable(); + else + printk(KERN_CRIT PFX "Device file closed unexpectedly. " + "Will not stop the WDT!\n"); + + clear_bit(WDT_IN_USE, &ts72xx_wdt_status); + + return 0; +} + +/* + * Kernel Interfaces + */ + +static struct file_operations ts72xx_wdt_fops = { + .owner = THIS_MODULE, + .llseek = no_llseek, + .write = ts72xx_wdt_write, + .ioctl = ts72xx_wdt_ioctl, + .open = ts72xx_wdt_open, + .release = ts72xx_wdt_close, +}; + +static struct miscdevice ts72xx_wdt_miscdev = { + .minor = WATCHDOG_MINOR, + .name = "watchdog", + .fops = &ts72xx_wdt_fops, +}; + +static void ts72xx_wdt_shutdown(struct platform_device *dev) +{ + ts72xx_wdt_disable(); +} + +static int __devinit ts72xx_wdt_probe(struct platform_device *dev) +{ + struct resource *r; + int ret; + + if (!machine_is_ts72xx()) + return -ENODEV; + + r = platform_get_resource(dev, IORESOURCE_MEM, 0); + + if (!r) { + ret = -ENODEV; + goto err_out; + } + + control_register = ioremap(r->start, r->end - r->start + 1); + + if (control_register == NULL) { + ret = -ENOMEM; + goto err_out; + } + + r = platform_get_resource(dev, IORESOURCE_MEM, 1); + + if (!r) { + ret = -ENODEV; + goto err_unmap1; + } + + feed_register = ioremap(r->start, r->end - r->start + 1); + + if (feed_register == NULL) { + ret = -ENOMEM; + goto err_unmap1; + } + + ret = misc_register(&ts72xx_wdt_miscdev); + if (ret) { + printk(KERN_ERR PFX "cannot register miscdev on minor=%d " + "(err=%d), ep93xx_watchdog already loaded?!\n", WATCHDOG_MINOR, ret); + goto err_unmap2; + } + + printk(KERN_INFO PFX "TS-72xx watchdog driver, v%s\n", WATCHDOG_VERSION); + ts72xx_parse_timeout(timeout); + + return 0; + +err_unmap2: + iounmap(feed_register); +err_unmap1: + iounmap(control_register); +err_out: + return ret; +} + +static int __devexit ts72xx_wdt_remove(struct platform_device *dev) +{ + misc_deregister(&ts72xx_wdt_miscdev); + iounmap(feed_register); + iounmap(control_register); + + return 0; +} + +static struct platform_driver ts72xx_wdt_driver = { + .probe = ts72xx_wdt_probe, + .remove = __devexit_p(ts72xx_wdt_remove), + .shutdown = ts72xx_wdt_shutdown, + .driver = { + .owner = THIS_MODULE, + .name = "ts72xx_wdt", + }, +}; + +static int __init ts72xx_wdt_init(void) +{ + return platform_driver_register(&ts72xx_wdt_driver); +} + +static void __exit ts72xx_wdt_exit(void) +{ + platform_driver_unregister(&ts72xx_wdt_driver); +} + +module_init(ts72xx_wdt_init); +module_exit(ts72xx_wdt_exit); + +#ifdef CONFIG_WATCHDOG_NOWAYOUT +module_param(nowayout, int, 0); +MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default=CONFIG_WATCHDOG_NOWAYOUT)"); +#endif + +module_param(timeout, int, 0); +MODULE_PARM_DESC(timeout,"Watchdog timeout in milliseconds (250..8000, default=" __MODULE_STRING(WATCHDOG_TIMEOUT) ")"); + +MODULE_AUTHOR("Matthieu Crapet "); +MODULE_DESCRIPTION("TS-72xx watchdog driver"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR); Index: linux-2.6.24/include/asm-arm/arch-ep93xx/ts72xx.h =================================================================== --- linux-2.6.24.orig/include/asm-arm/arch-ep93xx/ts72xx.h 2008-02-03 14:09:54.000000000 +0100 +++ linux-2.6.24/include/asm-arm/arch-ep93xx/ts72xx.h 2008-02-03 14:16:09.000000000 +0100 @@ -69,6 +69,9 @@ #define TS72XX_RTC_DATA_SIZE 0x00001000 +#define TS72XX_WATCHDOG_CONTROL_PHYS_BASE 0x23800000 +#define TS72XX_WATCHDOG_FEED_PHYS_BASE 0x23c00000 + #ifndef __ASSEMBLY__ #include