From 3c6c80a04d6ca99c4f9fbb5e4f279bd644b2df48 Mon Sep 17 00:00:00 2001 From: Kurt Mahan Date: Tue, 8 Jul 2008 14:10:46 -0600 Subject: [PATCH] Add Coldfire Watchdog support. LTIBName: mcfv4e-watchdog Signed-off-by: Kurt Mahan --- drivers/watchdog/Kconfig | 9 ++ drivers/watchdog/Makefile | 1 + drivers/watchdog/mcf_wdt.c | 220 ++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 230 insertions(+), 0 deletions(-) create mode 100644 drivers/watchdog/mcf_wdt.c --- a/drivers/watchdog/Kconfig +++ b/drivers/watchdog/Kconfig @@ -672,6 +672,15 @@ config TXX9_WDT # PARISC Architecture +# ColdFire Architecture + +config COLDFIRE_WATCHDOG + tristate "ColdFire watchdog support" + depends on WATCHDOG + help + To compile this driver as a module, choose M here: the + module will be called softdog. + # POWERPC Architecture config MPC5200_WDT --- a/drivers/watchdog/Makefile +++ b/drivers/watchdog/Makefile @@ -86,6 +86,7 @@ obj-$(CONFIG_SBC_EPX_C3_WATCHDOG) += sbc # M32R Architecture # M68K Architecture +obj-$(CONFIG_COLDFIRE_WATCHDOG) += mcf_wdt.o # M68KNOMMU Architecture --- /dev/null +++ b/drivers/watchdog/mcf_wdt.c @@ -0,0 +1,220 @@ +/* + * drivers/watchdog/mcf_wdt.c + * + * Watchdog driver for ColdFire processors + * + * Adapted from the IXP4xx watchdog driver. + * The original version carries these notices: + * + * Author: Deepak Saxena + * + * Copyright 2004 (c) MontaVista, Software, Inc. + * Based on sa1100 driver, Copyright (C) 2000 Oleg Drokin + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +static int nowayout; +static unsigned int heartbeat = 30; /* (secs) Default is 0.5 minute */ +static unsigned long wdt_status; + +#define WDT_IN_USE 0 +#define WDT_OK_TO_CLOSE 1 + +static unsigned long wdt_tick_rate; + +static void +wdt_enable(void) +{ + MCF_GPT_GMS0 = 0; + MCF_GPT_GCIR0 = MCF_GPT_GCIR_PRE(heartbeat*wdt_tick_rate) | + MCF_GPT_GCIR_CNT(0xffff); + MCF_GPT_GMS0 = MCF_GPT_GMS_OCPW(0xA5) | MCF_GPT_GMS_WDEN | + MCF_GPT_GMS_CE | MCF_GPT_GMS_TMS_GPIO; +} + +static void +wdt_disable(void) +{ + MCF_GPT_GMS0 = 0; +} + +static void +wdt_keepalive(void) +{ + MCF_GPT_GMS0 = MCF_GPT_GMS_OCPW(0xA5) | MCF_GPT_GMS0; +} + +static int +mcf_wdt_open(struct inode *inode, struct file *file) +{ + if (test_and_set_bit(WDT_IN_USE, &wdt_status)) + return -EBUSY; + + clear_bit(WDT_OK_TO_CLOSE, &wdt_status); + + wdt_enable(); + + return nonseekable_open(inode, file); +} + +static ssize_t +mcf_wdt_write(struct file *file, const char *data, size_t len, loff_t *ppos) +{ + if (len) { + if (!nowayout) { + size_t i; + + clear_bit(WDT_OK_TO_CLOSE, &wdt_status); + + for (i = 0; i != len; i++) { + char c; + + if (get_user(c, data + i)) + return -EFAULT; + if (c == 'V') + set_bit(WDT_OK_TO_CLOSE, &wdt_status); + } + } + wdt_keepalive(); + } + + return len; +} + + +static struct watchdog_info ident = { + .options = WDIOF_MAGICCLOSE | WDIOF_SETTIMEOUT | + WDIOF_KEEPALIVEPING, + .identity = "Coldfire Watchdog", +}; + +static int +mcf_wdt_ioctl(struct inode *inode, struct file *file, unsigned int cmd, + unsigned long arg) +{ + int ret = -ENOIOCTLCMD; + int time; + + switch (cmd) { + case WDIOC_GETSUPPORT: + ret = copy_to_user((struct watchdog_info *)arg, &ident, + sizeof(ident)) ? -EFAULT : 0; + break; + + case WDIOC_GETSTATUS: + ret = put_user(0, (int *)arg); + break; + + case WDIOC_GETBOOTSTATUS: + ret = put_user(0, (int *)arg); + break; + + case WDIOC_SETTIMEOUT: + ret = get_user(time, (int *)arg); + if (ret) + break; + + if (time <= 0 || time > 30) { + ret = -EINVAL; + break; + } + + heartbeat = time; + wdt_enable(); + /* Fall through */ + + case WDIOC_GETTIMEOUT: + ret = put_user(heartbeat, (int *)arg); + break; + + case WDIOC_KEEPALIVE: + wdt_keepalive(); + ret = 0; + break; + } + + return ret; +} + +static int +mcf_wdt_release(struct inode *inode, struct file *file) +{ + if (test_bit(WDT_OK_TO_CLOSE, &wdt_status)) { + wdt_disable(); + } else { + printk(KERN_CRIT "WATCHDOG: Device closed unexpectdly - " + "timer will not stop\n"); + } + + clear_bit(WDT_IN_USE, &wdt_status); + clear_bit(WDT_OK_TO_CLOSE, &wdt_status); + + return 0; +} + + +static struct file_operations mcf_wdt_fops = { + .owner = THIS_MODULE, + .llseek = no_llseek, + .write = mcf_wdt_write, + .ioctl = mcf_wdt_ioctl, + .open = mcf_wdt_open, + .release = mcf_wdt_release, +}; + +static struct miscdevice mcf_wdt_miscdev = { + .minor = WATCHDOG_MINOR, + .name = "watchdog", + .fops = &mcf_wdt_fops, +}; + +static int __init mcf_wdt_init(void) +{ + wdt_tick_rate = MCF_BUSCLK/0xffff; +#ifdef CONFIG_WATCHDOG_NOWAYOUT + nowayout = 1; +#else + nowayout = 0; +#endif + printk("ColdFire watchdog driver is loaded.\n"); + + return misc_register(&mcf_wdt_miscdev); +} + +static void __exit mcf_wdt_exit(void) +{ + misc_deregister(&mcf_wdt_miscdev); +} + +module_init(mcf_wdt_init); +module_exit(mcf_wdt_exit); + +MODULE_AUTHOR("Deepak Saxena"); +MODULE_DESCRIPTION("ColdFire Watchdog"); + +module_param(heartbeat, int, 0); +MODULE_PARM_DESC(heartbeat, "Watchdog heartbeat in seconds (default 60s)"); + +module_param(nowayout, int, 0); +MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started"); + +MODULE_LICENSE("GPL"); +MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR); +