Переглянути джерело

add watchdog driver (#5238)

SVN-Revision: 16381
Florian Fainelli 17 роки тому
батько
коміт
03b7eda3c0

+ 2 - 1
target/linux/brcm47xx/config-2.6.28

@@ -14,6 +14,7 @@ CONFIG_B44_PCICORE_AUTOSELECT=y
 CONFIG_B44_PCI_AUTOSELECT=y
 CONFIG_BASE_SMALL=0
 CONFIG_BCM47XX=y
+CONFIG_BCM47XX_WDT=y
 CONFIG_BITREVERSE=y
 # CONFIG_BSD_DISKLABEL is not set
 # CONFIG_BSD_PROCESS_ACCT is not set
@@ -167,5 +168,5 @@ CONFIG_USB_OHCI_HCD_SSB=y
 CONFIG_USB_SUPPORT=y
 # CONFIG_VGASTATE is not set
 # CONFIG_VIA_RHINE is not set
-# CONFIG_WATCHDOG is not set
+CONFIG_WATCHDOG_NOWAYOUT=y
 CONFIG_ZONE_DMA_FLAG=0

+ 18 - 0
target/linux/brcm47xx/patches-2.6.28/814-export_ssb_watchdog.patch

@@ -0,0 +1,18 @@
+this patch export ssb_watchdog_timer_set to allow to use it in a Linux 
+watchdog driver.
+
+
+Signed-off-by: Matthieu CASTET <[email protected]>
+Acked-by : Michael Buesch <[email protected]>
+Index: linux-2.6/drivers/ssb/embedded.c
+===================================================================
+--- linux-2.6.orig/drivers/ssb/embedded.c	2009-05-22 21:45:50.000000000 +0200
++++ linux-2.6/drivers/ssb/embedded.c	2009-05-22 21:46:07.000000000 +0200
+@@ -29,6 +29,7 @@
+ 	}
+ 	return -ENODEV;
+ }
++EXPORT_SYMBOL(ssb_watchdog_timer_set);
+ 
+ u32 ssb_gpio_in(struct ssb_bus *bus, u32 mask)
+ {

+ 332 - 0
target/linux/brcm47xx/patches-2.6.28/815-watchdog.patch

@@ -0,0 +1,332 @@
+This add watchdog driver for broadcom 47xx device.
+It uses the ssb subsytem to access embeded watchdog device.
+
+Because the watchdog timeout is very short (about 2s), a soft timer is used
+to increase the watchdog period.
+
+Note : A patch for exporting the ssb_watchdog_timer_set will
+be submitted on next linux-mips merge. Without this patch it can't 
+be build as a module.
+
+Signed-off-by: Aleksandar Radovanovic <[email protected]>
+Signed-off-by: Matthieu CASTET <[email protected]>
+Index: linux-2.6/drivers/watchdog/Kconfig
+===================================================================
+--- linux-2.6.orig/drivers/watchdog/Kconfig	2009-05-25 22:22:02.000000000 +0200
++++ linux-2.6/drivers/watchdog/Kconfig	2009-06-05 22:05:19.000000000 +0200
+@@ -764,6 +764,12 @@
+ 	help
+ 	  Hardware driver for the built-in watchdog timer on TXx9 MIPS SoCs.
+ 
++config BCM47XX_WDT
++	tristate "Broadcom BCM47xx Watchdog Timer"
++	depends on BCM47XX
++	help
++	  Hardware driver for the Broadcom BCM47xx Watchog Timer.
++
+ # PARISC Architecture
+ 
+ # POWERPC Architecture
+Index: linux-2.6/drivers/watchdog/Makefile
+===================================================================
+--- linux-2.6.orig/drivers/watchdog/Makefile	2009-05-25 22:22:02.000000000 +0200
++++ linux-2.6/drivers/watchdog/Makefile	2009-05-25 23:02:27.000000000 +0200
+@@ -105,6 +105,7 @@
+ obj-$(CONFIG_SIBYTE_WDOG) += sb_wdog.o
+ obj-$(CONFIG_AR7_WDT) += ar7_wdt.o
+ obj-$(CONFIG_TXX9_WDT) += txx9wdt.o
++obj-$(CONFIG_BCM47XX_WDT) += bcm47xx_wdt.o
+ 
+ # PARISC Architecture
+ 
+Index: linux-2.6/drivers/watchdog/bcm47xx_wdt.c
+===================================================================
+--- /dev/null	1970-01-01 00:00:00.000000000 +0000
++++ linux-2.6/drivers/watchdog/bcm47xx_wdt.c	2009-06-05 22:25:37.000000000 +0200
+@@ -0,0 +1,286 @@
++/*
++ *  Watchdog driver for Broadcom BCM47XX
++ *
++ *  Copyright (C) 2008 Aleksandar Radovanovic <[email protected]>
++ *  Copyright (C) 2009 Matthieu CASTET <[email protected]>
++ *
++ *  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.
++ */
++
++#include <linux/bitops.h>
++#include <linux/errno.h>
++#include <linux/fs.h>
++#include <linux/init.h>
++#include <linux/kernel.h>
++#include <linux/miscdevice.h>
++#include <linux/module.h>
++#include <linux/moduleparam.h>
++#include <linux/reboot.h>
++#include <linux/types.h>
++#include <linux/uaccess.h>
++#include <linux/watchdog.h>
++#include <linux/timer.h>
++#include <linux/jiffies.h>
++#include <linux/ssb/ssb_embedded.h>
++#include <asm/mach-bcm47xx/bcm47xx.h>
++
++#define DRV_NAME		"bcm47xx_wdt"
++
++#define WDT_DEFAULT_TIME	30	/* seconds */
++#define WDT_MAX_TIME		256	/* seconds */
++
++static int wdt_time = WDT_DEFAULT_TIME;
++static int nowayout = WATCHDOG_NOWAYOUT;
++
++module_param(wdt_time, int, 0);
++MODULE_PARM_DESC(wdt_time, "Watchdog time in seconds. (default="
++				__MODULE_STRING(WDT_DEFAULT_TIME) ")");
++
++#ifdef CONFIG_WATCHDOG_NOWAYOUT
++module_param(nowayout, int, 0);
++MODULE_PARM_DESC(nowayout,
++		"Watchdog cannot be stopped once started (default="
++				__MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
++#endif
++
++static unsigned long bcm47xx_wdt_busy;
++static char expect_release;
++static struct timer_list wdt_timer;
++static atomic_t ticks;
++
++static inline void bcm47xx_wdt_hw_start(void)
++{
++	/* this is 2,5s on 100Mhz clock  and 2s on 133 Mhz */
++	ssb_watchdog_timer_set(&ssb_bcm47xx, 0xfffffff);
++}
++
++static inline int bcm47xx_wdt_hw_stop(void)
++{
++	return ssb_watchdog_timer_set(&ssb_bcm47xx, 0);
++}
++
++static void bcm47xx_timer_tick(unsigned long unused)
++{
++	if (!atomic_dec_and_test(&ticks)) {
++		bcm47xx_wdt_hw_start();
++		mod_timer(&wdt_timer, jiffies + HZ);
++	} else {
++		printk(KERN_CRIT DRV_NAME "Watchdog will fire soon!!!\n");
++	}
++}
++
++static inline void bcm47xx_wdt_pet(void)
++{
++	atomic_set(&ticks, wdt_time);
++}
++
++static void bcm47xx_wdt_start(void)
++{
++	bcm47xx_wdt_pet();
++	bcm47xx_timer_tick(0);
++}
++
++static void bcm47xx_wdt_pause(void)
++{
++	del_timer_sync(&wdt_timer);
++	bcm47xx_wdt_hw_stop();
++}
++
++static void bcm47xx_wdt_stop(void)
++{
++	bcm47xx_wdt_pause();
++}
++
++static int bcm47xx_wdt_settimeout(int new_time)
++{
++	if ((new_time <= 0) || (new_time > WDT_MAX_TIME))
++		return -EINVAL;
++
++	wdt_time = new_time;
++	return 0;
++}
++
++static int bcm47xx_wdt_open(struct inode *inode, struct file *file)
++{
++	if (test_and_set_bit(0, &bcm47xx_wdt_busy))
++		return -EBUSY;
++
++	bcm47xx_wdt_start();
++	return nonseekable_open(inode, file);
++}
++
++static int bcm47xx_wdt_release(struct inode *inode, struct file *file)
++{
++	if (expect_release == 42) {
++		bcm47xx_wdt_stop();
++	} else {
++		printk(KERN_CRIT DRV_NAME
++			": Unexpected close, not stopping watchdog!\n");
++		bcm47xx_wdt_start();
++	}
++
++	clear_bit(0, &bcm47xx_wdt_busy);
++	expect_release = 0;
++	return 0;
++}
++
++static ssize_t bcm47xx_wdt_write(struct file *file, const char __user *data,
++			      size_t len, loff_t *ppos)
++{
++	if (len) {
++		if (!nowayout) {
++			size_t i;
++
++			expect_release = 0;
++
++			for (i = 0; i != len; i++) {
++				char c;
++				if (get_user(c, data + i))
++					return -EFAULT;
++				if (c == 'V')
++					expect_release = 42;
++			}
++		}
++		bcm47xx_wdt_pet();
++	}
++	return len;
++}
++
++static struct watchdog_info bcm47xx_wdt_info = {
++	.identity 	= DRV_NAME,
++	.options 	= WDIOF_SETTIMEOUT |
++				WDIOF_KEEPALIVEPING |
++				WDIOF_MAGICCLOSE,
++};
++
++static long bcm47xx_wdt_ioctl(struct file *file,
++					unsigned int cmd, unsigned long arg)
++{
++	void __user *argp = (void __user *)arg;
++	int __user *p = argp;
++	int new_value, retval = -EINVAL;;
++
++	switch (cmd) {
++	case WDIOC_GETSUPPORT:
++		return copy_to_user(argp, &bcm47xx_wdt_info,
++				sizeof(bcm47xx_wdt_info)) ? -EFAULT : 0;
++
++	case WDIOC_GETSTATUS:
++	case WDIOC_GETBOOTSTATUS:
++		return put_user(0, p);
++
++	case WDIOC_SETOPTIONS:
++		if (get_user(new_value, p))
++			return -EFAULT;
++
++		if (new_value & WDIOS_DISABLECARD) {
++			bcm47xx_wdt_stop();
++			retval = 0;
++		}
++
++		if (new_value & WDIOS_ENABLECARD) {
++			bcm47xx_wdt_start();
++			retval = 0;
++		}
++
++		return retval;
++
++	case WDIOC_KEEPALIVE:
++		bcm47xx_wdt_pet();
++		return 0;
++
++	case WDIOC_SETTIMEOUT:
++		if (get_user(new_value, p))
++			return -EFAULT;
++
++		if (bcm47xx_wdt_settimeout(new_value))
++			return -EINVAL;
++
++		bcm47xx_wdt_pet();
++
++	case WDIOC_GETTIMEOUT:
++		return put_user(wdt_time, p);
++
++	default:
++		return -ENOTTY;
++	}
++}
++
++static int bcm47xx_wdt_notify_sys(struct notifier_block *this,
++	   unsigned long code, void *unused)
++{
++	if (code == SYS_DOWN || code == SYS_HALT)
++		bcm47xx_wdt_stop();
++	return NOTIFY_DONE;
++}
++
++static const struct file_operations bcm47xx_wdt_fops = {
++	.owner		= THIS_MODULE,
++	.llseek		= no_llseek,
++	.unlocked_ioctl	= bcm47xx_wdt_ioctl,
++	.open		= bcm47xx_wdt_open,
++	.release	= bcm47xx_wdt_release,
++	.write		= bcm47xx_wdt_write,
++};
++
++static struct miscdevice bcm47xx_wdt_miscdev = {
++	.minor		= WATCHDOG_MINOR,
++	.name		= "watchdog",
++	.fops		= &bcm47xx_wdt_fops,
++};
++
++static struct notifier_block bcm47xx_wdt_notifier = {
++	.notifier_call = bcm47xx_wdt_notify_sys,
++};
++
++static int __init bcm47xx_wdt_init(void)
++{
++	int ret;
++
++	if (bcm47xx_wdt_hw_stop() < 0)
++		return -ENODEV;
++
++	setup_timer(&wdt_timer, bcm47xx_timer_tick, 0L);
++
++	if (bcm47xx_wdt_settimeout(wdt_time)) {
++		bcm47xx_wdt_settimeout(WDT_DEFAULT_TIME);
++		printk(KERN_INFO DRV_NAME
++			": wdt_time value must be 1 <= wdt_time <= 256, using %d\n",
++			wdt_time);
++	}
++
++	ret = register_reboot_notifier(&bcm47xx_wdt_notifier);
++	if (ret)
++		return ret;
++
++	ret = misc_register(&bcm47xx_wdt_miscdev);
++	if (ret) {
++		unregister_reboot_notifier(&bcm47xx_wdt_notifier);
++		return ret;
++	}
++
++	printk(KERN_INFO "BCM47xx Watchdog Timer enabled (%d seconds%s)\n",
++				wdt_time, nowayout ? ", nowayout" : "");
++	return 0;
++}
++
++static void __exit bcm47xx_wdt_exit(void)
++{
++	if (!nowayout)
++		bcm47xx_wdt_stop();
++
++	misc_deregister(&bcm47xx_wdt_miscdev);
++
++	unregister_reboot_notifier(&bcm47xx_wdt_notifier);
++}
++
++module_init(bcm47xx_wdt_init);
++module_exit(bcm47xx_wdt_exit);
++
++MODULE_AUTHOR("Aleksandar Radovanovic");
++MODULE_DESCRIPTION("Watchdog driver for Broadcom BCM47xx");
++MODULE_LICENSE("GPL");
++MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);