diff options
| -rw-r--r-- | package/avila-wdt/Makefile | 40 | ||||
| -rw-r--r-- | package/avila-wdt/src/Makefile | 1 | ||||
| -rw-r--r-- | package/avila-wdt/src/avila-wdt.c | 229 | 
3 files changed, 270 insertions, 0 deletions
| diff --git a/package/avila-wdt/Makefile b/package/avila-wdt/Makefile new file mode 100644 index 000000000..660c6c333 --- /dev/null +++ b/package/avila-wdt/Makefile @@ -0,0 +1,40 @@ +# +# Copyright (C) 2008 OpenWrt.org +# +# This is free software, licensed under the GNU General Public License v2. +# See /LICENSE for more information. +# + +include $(TOPDIR)/rules.mk +include $(INCLUDE_DIR)/kernel.mk + +PKG_NAME:=avila-wdt +PKG_RELEASE:=1 + +include $(INCLUDE_DIR)/package.mk + +define KernelPackage/avila-wdt +  SUBMENU:=Other modules +  TITLE:=GPIO hardware watchdog driver for modified Avila boards +  DEPENDS:=@GPIO_SUPPORT @TARGET_ixp4xx +  FILES:=$(PKG_BUILD_DIR)/avila-wdt.$(LINUX_KMOD_SUFFIX) +  AUTOLOAD:=$(call AutoLoad,10,avila-wdt) +endef + +MAKE_OPTS:= \ +	ARCH="$(LINUX_KARCH)" \ +	CROSS_COMPILE="$(TARGET_CROSS)" \ +	SUBDIRS="$(PKG_BUILD_DIR)" + +define Build/Prepare +	mkdir -p $(PKG_BUILD_DIR) +	$(CP) ./src/* $(PKG_BUILD_DIR)/ +endef + +define Build/Compile +	$(MAKE) -C "$(LINUX_DIR)" \ +		$(MAKE_OPTS) \ +		modules +endef + +$(eval $(call KernelPackage,avila-wdt)) diff --git a/package/avila-wdt/src/Makefile b/package/avila-wdt/src/Makefile new file mode 100644 index 000000000..90d90657e --- /dev/null +++ b/package/avila-wdt/src/Makefile @@ -0,0 +1 @@ +obj-m := avila-wdt.o diff --git a/package/avila-wdt/src/avila-wdt.c b/package/avila-wdt/src/avila-wdt.c new file mode 100644 index 000000000..6b3b42b09 --- /dev/null +++ b/package/avila-wdt/src/avila-wdt.c @@ -0,0 +1,229 @@ +/* + * avila-wdt.c  + * Copyright (C) 2009 Felix Fietkau <nbd@openwrt.org> + * + * based on: + * drivers/char/watchdog/ixp4xx_wdt.c + * + * Watchdog driver for Intel IXP4xx network processors + * + * Author: Deepak Saxena <dsaxena@plexity.net> + * + * Copyright 2004 (c) MontaVista, Software, Inc. + * Based on sa1100 driver, Copyright (C) 2000 Oleg Drokin <green@crimea.edu> + * + * 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 <linux/module.h> +#include <linux/moduleparam.h> +#include <linux/types.h> +#include <linux/kernel.h> +#include <linux/fs.h> +#include <linux/miscdevice.h> +#include <linux/watchdog.h> +#include <linux/init.h> +#include <linux/bitops.h> +#include <linux/uaccess.h> +#include <mach/hardware.h> + +static int nowayout = WATCHDOG_NOWAYOUT; +static int heartbeat = 20;	/* (secs) Default is 20 seconds */ +static unsigned long wdt_status; +static atomic_t wdt_counter; +struct timer_list wdt_timer; + +#define	WDT_IN_USE		0 +#define	WDT_OK_TO_CLOSE		1 +#define WDT_RUNNING		2 + +static void wdt_refresh(unsigned long data) +{ +	if (test_bit(WDT_RUNNING, &wdt_status)) { +		if (atomic_dec_and_test(&wdt_counter)) { +			printk(KERN_WARNING "Avila watchdog expired, expect a reboot soon!\n"); +			clear_bit(WDT_RUNNING, &wdt_status); +			return; +		} +	} + +	/* strobe to the watchdog */ +	gpio_line_set(14, IXP4XX_GPIO_HIGH); +	gpio_line_set(14, IXP4XX_GPIO_LOW); + +	mod_timer(&wdt_timer, jiffies + msecs_to_jiffies(500)); +} + +static void wdt_enable(void) +{ +	atomic_set(&wdt_counter, heartbeat * 2); + +	/* Disable clock generator output on GPIO 14/15 */ +	*IXP4XX_GPIO_GPCLKR &= ~(1 << 8); + +	/* activate GPIO 14 out */ +	gpio_line_config(14, IXP4XX_GPIO_OUT); +	gpio_line_set(14, IXP4XX_GPIO_LOW); + +	if (!test_bit(WDT_RUNNING, &wdt_status)) +		wdt_refresh(0); +	set_bit(WDT_RUNNING, &wdt_status); +} + +static void wdt_disable(void) +{ +	/* NB: we can't turn off the watchdog anymore, so we +	 * have to keep running the strobe timer without decrementing +	 * the counter */ +	clear_bit(WDT_RUNNING, &wdt_status); +} + +static int avila_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 +avila_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_enable(); +	} +	return len; +} + +static struct watchdog_info ident = { +	.options	= WDIOF_CARDRESET | WDIOF_MAGICCLOSE | +			  WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING, +	.identity	= "Avila Watchdog", +}; + + +static long avila_wdt_ioctl(struct file *file, unsigned int cmd, +							unsigned long arg) +{ +	int ret = -ENOTTY; +	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_KEEPALIVE: +		wdt_enable(); +		ret = 0; +		break; + +	case WDIOC_SETTIMEOUT: +		ret = get_user(time, (int *)arg); +		if (ret) +			break; + +		if (time <= 0 || time > 60) { +			ret = -EINVAL; +			break; +		} + +		heartbeat = time; +		wdt_enable(); +		/* Fall through */ + +	case WDIOC_GETTIMEOUT: +		ret = put_user(heartbeat, (int *)arg); +		break; +	} +	return ret; +} + +static int avila_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 unexpectedly - " +					"timer will not stop\n"); +	clear_bit(WDT_IN_USE, &wdt_status); +	clear_bit(WDT_OK_TO_CLOSE, &wdt_status); + +	return 0; +} + + +static const struct file_operations avila_wdt_fops = { +	.owner		= THIS_MODULE, +	.llseek		= no_llseek, +	.write		= avila_wdt_write, +	.unlocked_ioctl	= avila_wdt_ioctl, +	.open		= avila_wdt_open, +	.release	= avila_wdt_release, +}; + +static struct miscdevice avila_wdt_miscdev = { +	.minor		= WATCHDOG_MINOR + 1, +	.name		= "avila_watchdog", +	.fops		= &avila_wdt_fops, +}; + +static int __init avila_wdt_init(void) +{ +	int ret; + +	init_timer(&wdt_timer); +	wdt_timer.expires = 0; +	wdt_timer.data = 0; +	wdt_timer.function = wdt_refresh; +	ret = misc_register(&avila_wdt_miscdev); +	if (ret == 0) +		printk(KERN_INFO "Avila Watchdog Timer: heartbeat %d sec\n", +			heartbeat); +	return ret; +} + +static void __exit avila_wdt_exit(void) +{ +	misc_deregister(&avila_wdt_miscdev); +} + + +module_init(avila_wdt_init); +module_exit(avila_wdt_exit); + +MODULE_AUTHOR("Felix Fietkau <nbd@openwrt.org>"); +MODULE_DESCRIPTION("Gateworks Avila Hardware Watchdog"); + +module_param(heartbeat, int, 0); +MODULE_PARM_DESC(heartbeat, "Watchdog heartbeat in seconds (default 20s)"); + +module_param(nowayout, int, 0); +MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started"); + +MODULE_LICENSE("GPL"); +MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR); + | 
