From 42bf8a598049f6cb3c90b1f28a2f1b8daebe3de8 Mon Sep 17 00:00:00 2001 From: Alison Wang Date: Thu, 4 Aug 2011 09:59:47 +0800 Subject: [PATCH 31/52] Add watchdog driver support for MCF5445x and MCF547x/MCF548x Add watchdog driver support for MCF5445x and MCF547x/MCF548x. Signed-off-by: Alison Wang --- drivers/watchdog/Kconfig | 9 ++ drivers/watchdog/Makefile | 1 + drivers/watchdog/mcf_wdt.c | 292 ++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 302 insertions(+), 0 deletions(-) create mode 100644 drivers/watchdog/mcf_wdt.c --- a/drivers/watchdog/Kconfig +++ b/drivers/watchdog/Kconfig @@ -974,6 +974,15 @@ config BCM63XX_WDT # PARISC Architecture +# ColdFire Architecture + +config COLDFIRE_WATCHDOG + tristate "ColdFire watchdog support" + depends on M547X_8X || M5445X || M5441X + help + To compile this driver as a module, choose M here: the + module will be called softdog. + # POWERPC Architecture config GEF_WDT --- a/drivers/watchdog/Makefile +++ b/drivers/watchdog/Makefile @@ -107,6 +107,7 @@ obj-$(CONFIG_SBC_EPX_C3_WATCHDOG) += sbc # M68K Architecture obj-$(CONFIG_M54xx_WATCHDOG) += m54xx_wdt.o +obj-$(CONFIG_COLDFIRE_WATCHDOG) += mcf_wdt.o # MIPS Architecture obj-$(CONFIG_ATH79_WDT) += ath79_wdt.o --- /dev/null +++ b/drivers/watchdog/mcf_wdt.c @@ -0,0 +1,292 @@ +/* + * drivers/watchdog/mcf_wdt.c + * + * Watchdog driver for ColdFire processors + * + * Copyright (C) 2006-2007, 2009-2011 Freescale Semiconductor, Inc. + * All Rights Reserved. + * + * Author: Shrek Wu + * ChengJu Cai + * + * 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 = MCF_GPT_MAX_TIMEOUT; +static unsigned long wdt_status; + +#define WDT_IN_USE 0 +#define WDT_OK_TO_CLOSE 1 + +static unsigned long wdt_tick_rate; + +#ifdef CONFIG_M547X_8X +static int +wdt_enable(int time) +{ + if (time > 30 || time < 1) + return -EINVAL; + + heartbeat = time; + + 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; + + return 0; +} + +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; +} + +#elif defined(CONFIG_M5445X) || defined(CONFIG_M5441X) + +/* Enable watchdog and set time-out */ +static int +wdt_enable(int time) +{ + unsigned int sign = 0x01 << 31; + int i = 0, timeout_n = 31; + int max_timeout = sign / MCF_BUSCLK; + unsigned int count; + + if (time > max_timeout || time < 1) + return -EINVAL; + + count = time * MCF_BUSCLK; + + for (i = 0; i < 31; i++) { + if (count & (sign >> i)) { + if ((count & (~(sign >> i))) == 0) + timeout_n = 31 - i; + else + timeout_n = 31 - i + 1; + break; + } + } + + heartbeat = (unsigned int)(0x01 << timeout_n) / MCF_BUSCLK; + + MCF_SCM_CWCR = MCF_SCM_CWCR_CWE + | MCF_SCM_CWCR_CWRI(0x02) + | MCF_SCM_CWCR_CWT(timeout_n); + + return 0; +} + +/* Disable the watchdog */ +static void +wdt_disable(void) +{ + MCF_SCM_CWCR = 0x00; +} + +/* Reset the watchdog timer counter */ +static void +wdt_keepalive(void) +{ + MCF_SCM_CWSR = 0x55; + MCF_SCM_CWSR = 0xAA; +} +#endif + +static int +mcf_wdt_open(struct inode *inode, struct file *file) +{ + int ret; + if (test_and_set_bit(WDT_IN_USE, &wdt_status)) + return -EBUSY; + + clear_bit(WDT_OK_TO_CLOSE, &wdt_status); + + ret = wdt_enable(heartbeat); + if (ret) + return ret; + + 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 long +mcf_wdt_ioctl(struct file *file, unsigned int cmd, unsigned long __user arg) +{ + long ret = -ENOIOCTLCMD; + int time = 0; + + 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; + + ret = wdt_enable(time); + if (ret) + break; + ret = put_user(heartbeat, (int *)arg); + break; + + 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)) { +#ifdef CONFIG_M547X_8X + MCF_GPT_GCIR0 = (0x0A << 16) | 0x09; + MCF_GPT_GMS0 = MCF_GPT_GMS_OCPW(0xA5) | MCF_GPT_GMS0; +#else + wdt_keepalive(); +#endif + } else { + printk(KERN_CRIT "WATCHDOG: Device closed unexpectdly\n"); + wdt_disable(); + } + + clear_bit(WDT_IN_USE, &wdt_status); + clear_bit(WDT_OK_TO_CLOSE, &wdt_status); + +#ifdef CONFIG_M547X_8X + /*disable the XLB priority, otherwise the watchdog reset may fail*/ + MCF_XARB_PRIEN = 0; + /* Also disable the PCI, Ugly! But there is issue between PCI and + * watchdog. Otherwise the watchdog reset may fail, only valuable + * for testing! + */ + MCF_SPCR &= ~0x02; + asm("tpf"); +#endif + + return 0; +} + + +static const struct file_operations mcf_wdt_fops = { + .owner = THIS_MODULE, + .llseek = no_llseek, + .write = mcf_wdt_write, + .unlocked_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(KERN_INFO "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); +