aboutsummaryrefslogtreecommitdiffstats
path: root/target/linux/brcm2708/patches-3.3/0003-bcm2708-watchdog-driver.patch
diff options
context:
space:
mode:
Diffstat (limited to 'target/linux/brcm2708/patches-3.3/0003-bcm2708-watchdog-driver.patch')
-rw-r--r--target/linux/brcm2708/patches-3.3/0003-bcm2708-watchdog-driver.patch436
1 files changed, 436 insertions, 0 deletions
diff --git a/target/linux/brcm2708/patches-3.3/0003-bcm2708-watchdog-driver.patch b/target/linux/brcm2708/patches-3.3/0003-bcm2708-watchdog-driver.patch
new file mode 100644
index 000000000..cde3eef4e
--- /dev/null
+++ b/target/linux/brcm2708/patches-3.3/0003-bcm2708-watchdog-driver.patch
@@ -0,0 +1,436 @@
+From ed8c2d720954efc5a440912292ed11da2f50aaea Mon Sep 17 00:00:00 2001
+From: popcornmix <popcornmix@gmail.com>
+Date: Tue, 17 Jan 2012 19:20:57 +0000
+Subject: [PATCH 3/7] bcm2708 watchdog driver
+
+Signed-off-by: popcornmix <popcornmix@gmail.com>
+---
+ drivers/watchdog/Kconfig | 6 +
+ drivers/watchdog/Makefile | 1 +
+ drivers/watchdog/bcm2708_wdog.c | 385 +++++++++++++++++++++++++++++++++++++++
+ 3 files changed, 392 insertions(+), 0 deletions(-)
+ create mode 100644 drivers/watchdog/bcm2708_wdog.c
+
+diff --git a/drivers/watchdog/Kconfig b/drivers/watchdog/Kconfig
+index 86b0735..7675ebc 100644
+--- a/drivers/watchdog/Kconfig
++++ b/drivers/watchdog/Kconfig
+@@ -348,6 +348,12 @@ config IMX2_WDT
+ To compile this driver as a module, choose M here: the
+ module will be called imx2_wdt.
+
++config BCM2708_WDT
++ tristate "BCM2708 Watchdog"
++ depends on ARCH_BCM2708
++ help
++ Enables BCM2708 watchdog support.
++
+ # AVR32 Architecture
+
+ config AT32AP700X_WDT
+diff --git a/drivers/watchdog/Makefile b/drivers/watchdog/Makefile
+index 55bd574..803f0bc 100644
+--- a/drivers/watchdog/Makefile
++++ b/drivers/watchdog/Makefile
+@@ -54,6 +54,7 @@ obj-$(CONFIG_NUC900_WATCHDOG) += nuc900_wdt.o
+ obj-$(CONFIG_ADX_WATCHDOG) += adx_wdt.o
+ obj-$(CONFIG_TS72XX_WATCHDOG) += ts72xx_wdt.o
+ obj-$(CONFIG_IMX2_WDT) += imx2_wdt.o
++obj-$(CONFIG_BCM2708_WDT) += bcm2708_wdog.o
+
+ # AVR32 Architecture
+ obj-$(CONFIG_AT32AP700X_WDT) += at32ap700x_wdt.o
+diff --git a/drivers/watchdog/bcm2708_wdog.c b/drivers/watchdog/bcm2708_wdog.c
+new file mode 100644
+index 0000000..dd33c35
+--- /dev/null
++++ b/drivers/watchdog/bcm2708_wdog.c
+@@ -0,0 +1,385 @@
++/*
++ * Broadcom BCM2708 watchdog driver.
++ *
++ * (c) Copyright 2010 Broadcom Europe Ltd
++ *
++ * 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.
++ *
++ * BCM2708 watchdog driver. Loosely based on wdt driver.
++ */
++
++#include <linux/interrupt.h>
++#include <linux/module.h>
++#include <linux/moduleparam.h>
++#include <linux/types.h>
++#include <linux/miscdevice.h>
++#include <linux/watchdog.h>
++#include <linux/fs.h>
++#include <linux/ioport.h>
++#include <linux/notifier.h>
++#include <linux/reboot.h>
++#include <linux/init.h>
++#include <linux/io.h>
++#include <linux/uaccess.h>
++#include <mach/platform.h>
++
++#include <asm/system.h>
++
++#define SECS_TO_WDOG_TICKS(x) ((x) << 16)
++#define WDOG_TICKS_TO_SECS(x) ((x) >> 16)
++
++static unsigned long wdog_is_open;
++static uint32_t wdog_ticks; /* Ticks to load into wdog timer */
++static char expect_close;
++
++/*
++ * Module parameters
++ */
++
++#define WD_TIMO 10 /* Default heartbeat = 60 seconds */
++static int heartbeat = WD_TIMO; /* Heartbeat in seconds */
++
++module_param(heartbeat, int, 0);
++MODULE_PARM_DESC(heartbeat,
++ "Watchdog heartbeat in seconds. (0 < heartbeat < 65536, default="
++ __MODULE_STRING(WD_TIMO) ")");
++
++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 DEFINE_SPINLOCK(wdog_lock);
++
++/**
++ * Start the watchdog driver.
++ */
++
++static int wdog_start(unsigned long timeout)
++{
++ uint32_t cur;
++ unsigned long flags;
++ spin_lock_irqsave(&wdog_lock, flags);
++
++ /* enable the watchdog */
++ iowrite32(PM_PASSWORD | (timeout & PM_WDOG_TIME_SET),
++ __io_address(PM_WDOG));
++ cur = ioread32(__io_address(PM_RSTC));
++ iowrite32(PM_PASSWORD | (cur & PM_RSTC_WRCFG_CLR) |
++ PM_RSTC_WRCFG_FULL_RESET, __io_address(PM_RSTC));
++
++ spin_unlock_irqrestore(&wdog_lock, flags);
++ return 0;
++}
++
++/**
++ * Stop the watchdog driver.
++ */
++
++static int wdog_stop(void)
++{
++ iowrite32(PM_PASSWORD | PM_RSTC_RESET, __io_address(PM_RSTC));
++ printk(KERN_INFO "watchdog stopped\n");
++ return 0;
++}
++
++/**
++ * Reload counter one with the watchdog heartbeat. We don't bother
++ * reloading the cascade counter.
++ */
++
++static void wdog_ping(void)
++{
++ wdog_start(wdog_ticks);
++}
++
++/**
++ * @t: the new heartbeat value that needs to be set.
++ *
++ * Set a new heartbeat value for the watchdog device. If the heartbeat
++ * value is incorrect we keep the old value and return -EINVAL. If
++ * successful we return 0.
++ */
++
++static int wdog_set_heartbeat(int t)
++{
++ if (t < 1 || t > WDOG_TICKS_TO_SECS(PM_WDOG_TIME_SET))
++ return -EINVAL;
++
++ heartbeat = t;
++ wdog_ticks = SECS_TO_WDOG_TICKS(t);
++ return 0;
++}
++
++/**
++ * @file: file handle to the watchdog
++ * @buf: buffer to write (unused as data does not matter here
++ * @count: count of bytes
++ * @ppos: pointer to the position to write. No seeks allowed
++ *
++ * A write to a watchdog device is defined as a keepalive signal.
++ *
++ * if 'nowayout' is set then normally a close() is ignored. But
++ * if you write 'V' first then the close() will stop the timer.
++ */
++
++static ssize_t wdog_write(struct file *file, const char __user *buf,
++ size_t count, loff_t *ppos)
++{
++ if (count) {
++ if (!nowayout) {
++ size_t i;
++
++ /* In case it was set long ago */
++ expect_close = 0;
++
++ for (i = 0; i != count; i++) {
++ char c;
++ if (get_user(c, buf + i))
++ return -EFAULT;
++ if (c == 'V')
++ expect_close = 42;
++ }
++ }
++ wdog_ping();
++ }
++ return count;
++}
++
++static int wdog_get_status(void)
++{
++ unsigned long flags;
++ int status = 0;
++ spin_lock_irqsave(&wdog_lock, flags);
++ /* FIXME: readback reset reason */
++ spin_unlock_irqrestore(&wdog_lock, flags);
++ return status;
++}
++
++static uint32_t wdog_get_remaining(void)
++{
++ uint32_t ret = ioread32(__io_address(PM_WDOG));
++ return ret & PM_WDOG_TIME_SET;
++}
++
++/**
++ * @file: file handle to the device
++ * @cmd: watchdog command
++ * @arg: argument pointer
++ *
++ * The watchdog API defines a common set of functions for all watchdogs
++ * according to their available features. We only actually usefully support
++ * querying capabilities and current status.
++ */
++
++static long wdog_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
++{
++ void __user *argp = (void __user *)arg;
++ int __user *p = argp;
++ int new_heartbeat;
++ int status;
++ int options;
++ uint32_t remaining;
++
++ struct watchdog_info ident = {
++ .options = WDIOF_SETTIMEOUT|
++ WDIOF_MAGICCLOSE|
++ WDIOF_KEEPALIVEPING,
++ .firmware_version = 1,
++ .identity = "BCM2708",
++ };
++
++ switch (cmd) {
++ case WDIOC_GETSUPPORT:
++ return copy_to_user(argp, &ident, sizeof(ident)) ? -EFAULT : 0;
++ case WDIOC_GETSTATUS:
++ status = wdog_get_status();
++ return put_user(status, p);
++ case WDIOC_GETBOOTSTATUS:
++ return put_user(0, p);
++ case WDIOC_KEEPALIVE:
++ wdog_ping();
++ return 0;
++ case WDIOC_SETTIMEOUT:
++ if (get_user(new_heartbeat, p))
++ return -EFAULT;
++ if (wdog_set_heartbeat(new_heartbeat))
++ return -EINVAL;
++ wdog_ping();
++ /* Fall */
++ case WDIOC_GETTIMEOUT:
++ return put_user(heartbeat, p);
++ case WDIOC_GETTIMELEFT:
++ remaining = WDOG_TICKS_TO_SECS(wdog_get_remaining());
++ return put_user(remaining, p);
++ case WDIOC_SETOPTIONS:
++ if (get_user(options, p))
++ return -EFAULT;
++ if (options & WDIOS_DISABLECARD)
++ wdog_stop();
++ if (options & WDIOS_ENABLECARD)
++ wdog_start(wdog_ticks);
++ return 0;
++ default:
++ return -ENOTTY;
++ }
++}
++
++/**
++ * @inode: inode of device
++ * @file: file handle to device
++ *
++ * The watchdog device has been opened. The watchdog device is single
++ * open and on opening we load the counters.
++ */
++
++static int wdog_open(struct inode *inode, struct file *file)
++{
++ if (test_and_set_bit(0, &wdog_is_open))
++ return -EBUSY;
++ /*
++ * Activate
++ */
++ wdog_start(wdog_ticks);
++ return nonseekable_open(inode, file);
++}
++
++/**
++ * @inode: inode to board
++ * @file: file handle to board
++ *
++ * The watchdog has a configurable API. There is a religious dispute
++ * between people who want their watchdog to be able to shut down and
++ * those who want to be sure if the watchdog manager dies the machine
++ * reboots. In the former case we disable the counters, in the latter
++ * case you have to open it again very soon.
++ */
++
++static int wdog_release(struct inode *inode, struct file *file)
++{
++ if (expect_close == 42) {
++ wdog_stop();
++ } else {
++ printk(KERN_CRIT
++ "wdt: WDT device closed unexpectedly. WDT will not stop!\n");
++ wdog_ping();
++ }
++ clear_bit(0, &wdog_is_open);
++ expect_close = 0;
++ return 0;
++}
++
++/**
++ * @this: our notifier block
++ * @code: the event being reported
++ * @unused: unused
++ *
++ * Our notifier is called on system shutdowns. Turn the watchdog
++ * off so that it does not fire during the next reboot.
++ */
++
++static int wdog_notify_sys(struct notifier_block *this, unsigned long code,
++ void *unused)
++{
++ if (code == SYS_DOWN || code == SYS_HALT)
++ wdog_stop();
++ return NOTIFY_DONE;
++}
++
++/*
++ * Kernel Interfaces
++ */
++
++
++static const struct file_operations wdog_fops = {
++ .owner = THIS_MODULE,
++ .llseek = no_llseek,
++ .write = wdog_write,
++ .unlocked_ioctl = wdog_ioctl,
++ .open = wdog_open,
++ .release = wdog_release,
++};
++
++static struct miscdevice wdog_miscdev = {
++ .minor = WATCHDOG_MINOR,
++ .name = "watchdog",
++ .fops = &wdog_fops,
++};
++
++/*
++ * The WDT card needs to learn about soft shutdowns in order to
++ * turn the timebomb registers off.
++ */
++
++static struct notifier_block wdog_notifier = {
++ .notifier_call = wdog_notify_sys,
++};
++
++/**
++ * cleanup_module:
++ *
++ * Unload the watchdog. You cannot do this with any file handles open.
++ * If your watchdog is set to continue ticking on close and you unload
++ * it, well it keeps ticking. We won't get the interrupt but the board
++ * will not touch PC memory so all is fine. You just have to load a new
++ * module in 60 seconds or reboot.
++ */
++
++static void __exit wdog_exit(void)
++{
++ misc_deregister(&wdog_miscdev);
++ unregister_reboot_notifier(&wdog_notifier);
++}
++
++static int __init wdog_init(void)
++{
++ int ret;
++
++ /* Check that the heartbeat value is within it's range;
++ if not reset to the default */
++ if (wdog_set_heartbeat(heartbeat)) {
++ wdog_set_heartbeat(WD_TIMO);
++ printk(KERN_INFO "bcm2708_wdog: heartbeat value must be "
++ "0 < heartbeat < %d, using %d\n",
++ WDOG_TICKS_TO_SECS(PM_WDOG_TIME_SET),
++ WD_TIMO);
++ }
++
++ ret = register_reboot_notifier(&wdog_notifier);
++ if (ret) {
++ printk(KERN_ERR
++ "wdt: cannot register reboot notifier (err=%d)\n", ret);
++ goto out_reboot;
++ }
++
++ ret = misc_register(&wdog_miscdev);
++ if (ret) {
++ printk(KERN_ERR
++ "wdt: cannot register miscdev on minor=%d (err=%d)\n",
++ WATCHDOG_MINOR, ret);
++ goto out_misc;
++ }
++
++ printk(KERN_INFO "bcm2708 watchdog, heartbeat=%d sec (nowayout=%d)\n",
++ heartbeat, nowayout);
++ return 0;
++
++out_misc:
++ unregister_reboot_notifier(&wdog_notifier);
++out_reboot:
++ return ret;
++}
++
++module_init(wdog_init);
++module_exit(wdog_exit);
++
++MODULE_AUTHOR("Luke Diamand");
++MODULE_DESCRIPTION("Driver for BCM2708 watchdog");
++MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);
++MODULE_ALIAS_MISCDEV(TEMP_MINOR);
++MODULE_LICENSE("GPL");
++
+--
+1.7.5.4
+