[WATCHDOG] ep93xx: watchdog timer driver for TS-72xx SBCs cleanup
[safe/jmp/linux-2.6] / drivers / watchdog / booke_wdt.c
index 4c423d5..e8380ef 100644 (file)
@@ -22,6 +22,8 @@
 
 #include <asm/reg_booke.h>
 #include <asm/system.h>
+#include <asm/time.h>
+#include <asm/div64.h>
 
 /* If the kernel parameter wdt=1, the watchdog will be enabled at boot.
  * Also, the wdt_period sets the watchdog timer period timeout.
@@ -32,7 +34,7 @@
  */
 
 #ifdef CONFIG_FSL_BOOKE
-#define WDT_PERIOD_DEFAULT 63  /* Ex. wdt_period=28 bus=333Mhz,reset=~40sec */
+#define WDT_PERIOD_DEFAULT 38  /* Ex. wdt_period=28 bus=333Mhz,reset=~40sec */
 #else
 #define WDT_PERIOD_DEFAULT 3   /* Refer to the PPC40x and PPC4xx manuals */
 #endif                         /* for timing information */
@@ -41,13 +43,54 @@ u32 booke_wdt_enabled;
 u32 booke_wdt_period = WDT_PERIOD_DEFAULT;
 
 #ifdef CONFIG_FSL_BOOKE
-#define WDTP(x)                ((((63-x)&0x3)<<30)|(((63-x)&0x3c)<<15))
+#define WDTP(x)                ((((x)&0x3)<<30)|(((x)&0x3c)<<15))
+#define WDTP_MASK      (WDTP(0))
 #else
 #define WDTP(x)                (TCR_WP(x))
+#define WDTP_MASK      (TCR_WP_MASK)
 #endif
 
 static DEFINE_SPINLOCK(booke_wdt_lock);
 
+/* For the specified period, determine the number of seconds
+ * corresponding to the reset time.  There will be a watchdog
+ * exception at approximately 3/5 of this time.
+ *
+ * The formula to calculate this is given by:
+ * 2.5 * (2^(63-period+1)) / timebase_freq
+ *
+ * In order to simplify things, we assume that period is
+ * at least 1.  This will still result in a very long timeout.
+ */
+static unsigned long long period_to_sec(unsigned int period)
+{
+       unsigned long long tmp = 1ULL << (64 - period);
+       unsigned long tmp2 = ppc_tb_freq;
+
+       /* tmp may be a very large number and we don't want to overflow,
+        * so divide the timebase freq instead of multiplying tmp
+        */
+       tmp2 = tmp2 / 5 * 2;
+
+       do_div(tmp, tmp2);
+       return tmp;
+}
+
+/*
+ * This procedure will find the highest period which will give a timeout
+ * greater than the one required. e.g. for a bus speed of 66666666 and
+ * and a parameter of 2 secs, then this procedure will return a value of 38.
+ */
+static unsigned int sec_to_period(unsigned int secs)
+{
+       unsigned int period;
+       for (period = 63; period > 0; period--) {
+               if (period_to_sec(period) >= secs)
+                       return period;
+       }
+       return 0;
+}
+
 static void __booke_wdt_ping(void *data)
 {
        mtspr(SPRN_TSR, TSR_ENW|TSR_WIS);
@@ -55,7 +98,7 @@ static void __booke_wdt_ping(void *data)
 
 static void booke_wdt_ping(void)
 {
-       on_each_cpu(__booke_wdt_ping, NULL, 0, 0);
+       on_each_cpu(__booke_wdt_ping, NULL, 0);
 }
 
 static void __booke_wdt_enable(void *data)
@@ -65,6 +108,7 @@ static void __booke_wdt_enable(void *data)
        /* clear status before enabling watchdog */
        __booke_wdt_ping(NULL);
        val = mfspr(SPRN_TCR);
+       val &= ~WDTP_MASK;
        val |= (TCR_WIE|TCR_WRC(WRC_CHIP)|WDTP(booke_wdt_period));
 
        mtspr(SPRN_TCR, val);
@@ -90,7 +134,7 @@ static long booke_wdt_ioctl(struct file *file,
 
        switch (cmd) {
        case WDIOC_GETSUPPORT:
-               if (copy_to_user(arg, &ident, sizeof(struct watchdog_info)))
+               if (copy_to_user((void *)arg, &ident, sizeof(ident)))
                        return -EFAULT;
        case WDIOC_GETSTATUS:
                return put_user(ident.options, p);
@@ -99,17 +143,6 @@ static long booke_wdt_ioctl(struct file *file,
                tmp = mfspr(SPRN_TSR) & TSR_WRS(3);
                /* returns 1 if last reset was caused by the WDT */
                return (tmp ? 1 : 0);
-       case WDIOC_KEEPALIVE:
-               booke_wdt_ping();
-               return 0;
-       case WDIOC_SETTIMEOUT:
-               if (get_user(booke_wdt_period, p))
-                       return -EFAULT;
-               mtspr(SPRN_TCR, (mfspr(SPRN_TCR) & ~WDTP(0)) |
-                                               WDTP(booke_wdt_period));
-               return 0;
-       case WDIOC_GETTIMEOUT:
-               return put_user(booke_wdt_period, p);
        case WDIOC_SETOPTIONS:
                if (get_user(tmp, p))
                        return -EINVAL;
@@ -119,6 +152,25 @@ static long booke_wdt_ioctl(struct file *file,
                } else
                        return -EINVAL;
                return 0;
+       case WDIOC_KEEPALIVE:
+               booke_wdt_ping();
+               return 0;
+       case WDIOC_SETTIMEOUT:
+               if (get_user(tmp, p))
+                       return -EFAULT;
+#ifdef CONFIG_FSL_BOOKE
+               /* period of 1 gives the largest possible timeout */
+               if (tmp > period_to_sec(1))
+                       return -EINVAL;
+               booke_wdt_period = sec_to_period(tmp);
+#else
+               booke_wdt_period = tmp;
+#endif
+               mtspr(SPRN_TCR, (mfspr(SPRN_TCR) & ~WDTP_MASK) |
+                                               WDTP(booke_wdt_period));
+               return 0;
+       case WDIOC_GETTIMEOUT:
+               return put_user(booke_wdt_period, p);
        default:
                return -ENOTTY;
        }
@@ -131,7 +183,7 @@ static int booke_wdt_open(struct inode *inode, struct file *file)
        spin_lock(&booke_wdt_lock);
        if (booke_wdt_enabled == 0) {
                booke_wdt_enabled = 1;
-               on_each_cpu(__booke_wdt_enable, NULL, 0, 0);
+               on_each_cpu(__booke_wdt_enable, NULL, 0);
                printk(KERN_INFO
                      "PowerPC Book-E Watchdog Timer Enabled (wdt_period=%d)\n",
                                booke_wdt_period);
@@ -179,7 +231,7 @@ static int __init booke_wdt_init(void)
                printk(KERN_INFO
                      "PowerPC Book-E Watchdog Timer Enabled (wdt_period=%d)\n",
                                booke_wdt_period);
-               on_each_cpu(__booke_wdt_enable, NULL, 0, 0);
+               on_each_cpu(__booke_wdt_enable, NULL, 0);
        }
        spin_unlock(&booke_wdt_lock);