#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.
*/
#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 */
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);
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)
/* 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);
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);
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;
} 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;
}
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);
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);