diff --git a/drivers/watchdog/booke_wdt.c b/drivers/watchdog/booke_wdt.c index 225398fd5049..e8380ef65c1c 100644 --- a/drivers/watchdog/booke_wdt.c +++ b/drivers/watchdog/booke_wdt.c @@ -22,6 +22,8 @@ #include #include +#include +#include /* 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,7 +43,7 @@ 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)) @@ -50,6 +52,45 @@ u32 booke_wdt_period = WDT_PERIOD_DEFAULT; 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); @@ -93,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); @@ -115,8 +156,16 @@ static long booke_wdt_ioctl(struct file *file, booke_wdt_ping(); return 0; case WDIOC_SETTIMEOUT: - if (get_user(booke_wdt_period, p)) + 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;