brcm2708: update against latest rpi-3.10.y branch
[openwrt.git] / target / linux / brcm2708 / patches-3.10 / 0003-bcm2708-watchdog-driver.patch
diff --git a/target/linux/brcm2708/patches-3.10/0003-bcm2708-watchdog-driver.patch b/target/linux/brcm2708/patches-3.10/0003-bcm2708-watchdog-driver.patch
new file mode 100644 (file)
index 0000000..be05d83
--- /dev/null
@@ -0,0 +1,426 @@
+From dd611b3364087514ef5fc59c6106d8924d2a2a30 Mon Sep 17 00:00:00 2001
+From: popcornmix <popcornmix@gmail.com>
+Date: Wed, 1 May 2013 19:54:32 +0100
+Subject: [PATCH 003/174] 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(+)
+ create mode 100644 drivers/watchdog/bcm2708_wdog.c
+
+--- a/drivers/watchdog/Kconfig
++++ b/drivers/watchdog/Kconfig
+@@ -391,6 +391,12 @@ config RETU_WATCHDOG
+         To compile this driver as a module, choose M here: the
+         module will be called retu_wdt.
++config BCM2708_WDT
++      tristate "BCM2708 Watchdog"
++      depends on ARCH_BCM2708
++      help
++        Enables BCM2708 watchdog support.
++
+ # AVR32 Architecture
+ config AT32AP700X_WDT
+--- a/drivers/watchdog/Makefile
++++ b/drivers/watchdog/Makefile
+@@ -54,6 +54,7 @@ obj-$(CONFIG_TS72XX_WATCHDOG) += ts72xx_
+ obj-$(CONFIG_IMX2_WDT) += imx2_wdt.o
+ obj-$(CONFIG_UX500_WATCHDOG) += ux500_wdt.o
+ obj-$(CONFIG_RETU_WATCHDOG) += retu_wdt.o
++obj-$(CONFIG_BCM2708_WDT) += bcm2708_wdog.o
+ # AVR32 Architecture
+ obj-$(CONFIG_AT32AP700X_WDT) += at32ap700x_wdt.o
+--- /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");
++