From 3268b5618f387c6b78b8f8b1190d43380c8170ac Mon Sep 17 00:00:00 2001 From: Martyn Welch Date: Mon, 10 Nov 2008 12:31:26 +0000 Subject: [WATCHDOG] Basic support for GE Fanuc's FPGA based watchdog timer GE Fanuc SBC610 Support for the FPGA based watchdog timer as found on GE Fanuc's SBC310, SBC610 and PPC9A Single Board Computers. This patch adds support for the watchdog timer found in one of the devices FPGAs. There are two identical watchdog timers at different offsets in the above mentioned boards, this driver is capable of supporting one of them. The watchdog timers are also capable of generating interrupts at a user-configurable threshold, though support for this operation is currently not supported by the driver. Signed-off-by: Martyn Welch Signed-off-by: Wim Van Sebroeck --- drivers/watchdog/Kconfig | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'drivers/watchdog/Kconfig') diff --git a/drivers/watchdog/Kconfig b/drivers/watchdog/Kconfig index ec68c741b564..d55d315d66d9 100644 --- a/drivers/watchdog/Kconfig +++ b/drivers/watchdog/Kconfig @@ -770,6 +770,12 @@ config TXX9_WDT # POWERPC Architecture +config GEF_WDT + tristate "GE Fanuc Watchdog Timer" + depends on GEF_SBC610 + ---help--- + Watchdog timer found in a number of GE Fanuc single board computers. + config MPC5200_WDT tristate "MPC5200 Watchdog Timer" depends on PPC_MPC52xx -- cgit v1.2.3 From 618efba999d0e7f4bcde93231dcb9a748223c6e3 Mon Sep 17 00:00:00 2001 From: Sean MacLennan Date: Tue, 23 Sep 2008 20:26:26 -0400 Subject: [WATCHDOG] Pika Warp appliance watchdog timer The FPGA based watchdog timer used by the Pika Warp appliance. Signed-off-by: Sean MacLennan Signed-off-by: Wim Van Sebroeck --- drivers/watchdog/Kconfig | 8 ++ drivers/watchdog/Makefile | 1 + drivers/watchdog/pika_wdt.c | 301 ++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 310 insertions(+) create mode 100644 drivers/watchdog/pika_wdt.c (limited to 'drivers/watchdog/Kconfig') diff --git a/drivers/watchdog/Kconfig b/drivers/watchdog/Kconfig index d55d315d66d9..3efa12f9ee50 100644 --- a/drivers/watchdog/Kconfig +++ b/drivers/watchdog/Kconfig @@ -796,6 +796,14 @@ config MV64X60_WDT tristate "MV64X60 (Marvell Discovery) Watchdog Timer" depends on MV64X60 +config PIKA_WDT + tristate "PIKA FPGA Watchdog" + depends on WARP + default y + help + This enables the watchdog in the PIKA FPGA. Currently used on + the Warp platform. + config BOOKE_WDT bool "PowerPC Book-E Watchdog Timer" depends on BOOKE || 4xx diff --git a/drivers/watchdog/Makefile b/drivers/watchdog/Makefile index e79187a79501..806b3eb08536 100644 --- a/drivers/watchdog/Makefile +++ b/drivers/watchdog/Makefile @@ -115,6 +115,7 @@ obj-$(CONFIG_GEF_WDT) += gef_wdt.o obj-$(CONFIG_MPC5200_WDT) += mpc5200_wdt.o obj-$(CONFIG_8xxx_WDT) += mpc8xxx_wdt.o obj-$(CONFIG_MV64X60_WDT) += mv64x60_wdt.o +obj-$(CONFIG_PIKA_WDT) += pika_wdt.o obj-$(CONFIG_BOOKE_WDT) += booke_wdt.o # PPC64 Architecture diff --git a/drivers/watchdog/pika_wdt.c b/drivers/watchdog/pika_wdt.c new file mode 100644 index 000000000000..2d22e996e996 --- /dev/null +++ b/drivers/watchdog/pika_wdt.c @@ -0,0 +1,301 @@ +/* + * PIKA FPGA based Watchdog Timer + * + * Copyright (c) 2008 PIKA Technologies + * Sean MacLennan + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define DRV_NAME "PIKA-WDT" +#define PFX DRV_NAME ": " + +/* Hardware timeout in seconds */ +#define WDT_HW_TIMEOUT 2 + +/* Timer heartbeat (500ms) */ +#define WDT_TIMEOUT (HZ/2) + +/* User land timeout */ +#define WDT_HEARTBEAT 15 +static int heartbeat = WDT_HEARTBEAT; +module_param(heartbeat, int, 0); +MODULE_PARM_DESC(heartbeat, "Watchdog heartbeats in seconds. " + "(default = " __MODULE_STRING(WDT_HEARTBEAT) ")"); + +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 struct { + void __iomem *fpga; + unsigned long next_heartbeat; /* the next_heartbeat for the timer */ + unsigned long open; + char expect_close; + int bootstatus; + struct timer_list timer; /* The timer that pings the watchdog */ +} pikawdt_private; + +static struct watchdog_info ident = { + .identity = DRV_NAME, + .options = WDIOF_CARDRESET | + WDIOF_SETTIMEOUT | + WDIOF_KEEPALIVEPING | + WDIOF_MAGICCLOSE, +}; + +/* + * Reload the watchdog timer. (ie, pat the watchdog) + */ +static inline void pikawdt_reset(void) +{ + /* -- FPGA: Reset Control Register (32bit R/W) (Offset: 0x14) -- + * Bit 7, WTCHDG_EN: When set to 1, the watchdog timer is enabled. + * Once enabled, it cannot be disabled. The watchdog can be + * kicked by performing any write access to the reset + * control register (this register). + * Bit 8-11, WTCHDG_TIMEOUT_SEC: Sets the watchdog timeout value in + * seconds. Valid ranges are 1 to 15 seconds. The value can + * be modified dynamically. + */ + unsigned reset = in_be32(pikawdt_private.fpga + 0x14); + /* enable with max timeout - 15 seconds */ + reset |= (1 << 7) + (WDT_HW_TIMEOUT << 8); + out_be32(pikawdt_private.fpga + 0x14, reset); +} + +/* + * Timer tick + */ +static void pikawdt_ping(unsigned long data) +{ + if (time_before(jiffies, pikawdt_private.next_heartbeat) || + (!nowayout && !pikawdt_private.open)) { + pikawdt_reset(); + mod_timer(&pikawdt_private.timer, jiffies + WDT_TIMEOUT); + } else + printk(KERN_CRIT PFX "I will reset your machine !\n"); +} + + +static void pikawdt_keepalive(void) +{ + pikawdt_private.next_heartbeat = jiffies + heartbeat * HZ; +} + +static void pikawdt_start(void) +{ + pikawdt_keepalive(); + mod_timer(&pikawdt_private.timer, jiffies + WDT_TIMEOUT); +} + +/* + * Watchdog device is opened, and watchdog starts running. + */ +static int pikawdt_open(struct inode *inode, struct file *file) +{ + /* /dev/watchdog can only be opened once */ + if (test_and_set_bit(0, &pikawdt_private.open)) + return -EBUSY; + + pikawdt_start(); + + return nonseekable_open(inode, file); +} + +/* + * Close the watchdog device. + */ +static int pikawdt_release(struct inode *inode, struct file *file) +{ + /* stop internal ping */ + if (!pikawdt_private.expect_close) + del_timer(&pikawdt_private.timer); + + clear_bit(0, &pikawdt_private.open); + pikawdt_private.expect_close = 0; + return 0; +} + +/* + * Pat the watchdog whenever device is written to. + */ +static ssize_t pikawdt_write(struct file *file, const char __user *data, + size_t len, loff_t *ppos) +{ + if (!len) + return 0; + + /* Scan for magic character */ + if (!nowayout) { + size_t i; + + pikawdt_private.expect_close = 0; + + for (i = 0; i < len; i++) { + char c; + if (get_user(c, data + i)) + return -EFAULT; + if (c == 'V') { + pikawdt_private.expect_close = 42; + break; + } + } + } + + pikawdt_keepalive(); + + return len; +} + +/* + * Handle commands from user-space. + */ +static long pikawdt_ioctl(struct file *file, + unsigned int cmd, unsigned long arg) +{ + void __user *argp = (void __user *)arg; + int __user *p = argp; + int new_value; + + switch (cmd) { + case WDIOC_GETSUPPORT: + return copy_to_user(argp, &ident, sizeof(ident)) ? -EFAULT : 0; + + case WDIOC_GETSTATUS: + return put_user(0, p); + + case WDIOC_GETBOOTSTATUS: + return put_user(pikawdt_private.bootstatus, p); + + case WDIOC_KEEPALIVE: + pikawdt_keepalive(); + return 0; + + case WDIOC_SETTIMEOUT: + if (get_user(new_value, p)) + return -EFAULT; + + heartbeat = new_value; + pikawdt_keepalive(); + + return put_user(new_value, p); /* return current value */ + + case WDIOC_GETTIMEOUT: + return put_user(heartbeat, p); + } + return -ENOTTY; +} + + +static const struct file_operations pikawdt_fops = { + .owner = THIS_MODULE, + .llseek = no_llseek, + .open = pikawdt_open, + .release = pikawdt_release, + .write = pikawdt_write, + .unlocked_ioctl = pikawdt_ioctl, +}; + +static struct miscdevice pikawdt_miscdev = { + .minor = WATCHDOG_MINOR, + .name = "watchdog", + .fops = &pikawdt_fops, +}; + +static int __init pikawdt_init(void) +{ + struct device_node *np; + void __iomem *fpga; + static u32 post1; + int ret; + + np = of_find_compatible_node(NULL, NULL, "pika,fpga"); + if (np == NULL) { + printk(KERN_ERR PFX "Unable to find fpga.\n"); + return -ENOENT; + } + + pikawdt_private.fpga = of_iomap(np, 0); + of_node_put(np); + if (pikawdt_private.fpga == NULL) { + printk(KERN_ERR PFX "Unable to map fpga.\n"); + return -ENOMEM; + } + + ident.firmware_version = in_be32(pikawdt_private.fpga + 0x1c) & 0xffff; + + /* POST information is in the sd area. */ + np = of_find_compatible_node(NULL, NULL, "pika,fpga-sd"); + if (np == NULL) { + printk(KERN_ERR PFX "Unable to find fpga-sd.\n"); + ret = -ENOENT; + goto out; + } + + fpga = of_iomap(np, 0); + of_node_put(np); + if (fpga == NULL) { + printk(KERN_ERR PFX "Unable to map fpga-sd.\n"); + ret = -ENOMEM; + goto out; + } + + /* -- FPGA: POST Test Results Register 1 (32bit R/W) (Offset: 0x4040) -- + * Bit 31, WDOG: Set to 1 when the last reset was caused by a watchdog + * timeout. + */ + post1 = in_be32(fpga + 0x40); + if (post1 & 0x80000000) + pikawdt_private.bootstatus = WDIOF_CARDRESET; + + iounmap(fpga); + + setup_timer(&pikawdt_private.timer, pikawdt_ping, 0); + + ret = misc_register(&pikawdt_miscdev); + if (ret) { + printk(KERN_ERR PFX "Unable to register miscdev.\n"); + goto out; + } + + printk(KERN_INFO PFX "initialized. heartbeat=%d sec (nowayout=%d)\n", + heartbeat, nowayout); + return 0; + +out: + iounmap(pikawdt_private.fpga); + return ret; +} + +static void __exit pikawdt_exit(void) +{ + misc_deregister(&pikawdt_miscdev); + + iounmap(pikawdt_private.fpga); +} + +module_init(pikawdt_init); +module_exit(pikawdt_exit); + +MODULE_AUTHOR("Sean MacLennan "); +MODULE_DESCRIPTION("PIKA FPGA based Watchdog Timer"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR); + -- cgit v1.2.3