diff options
Diffstat (limited to 'drivers/watchdog/booke_wdt.c')
-rw-r--r-- | drivers/watchdog/booke_wdt.c | 106 |
1 files changed, 106 insertions, 0 deletions
diff --git a/drivers/watchdog/booke_wdt.c b/drivers/watchdog/booke_wdt.c new file mode 100644 index 0000000000..50c091956e --- /dev/null +++ b/drivers/watchdog/booke_wdt.c @@ -0,0 +1,106 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Watchdog timer for PowerPC Book-E systems + */ + +#include <div64.h> +#include <dm.h> +#include <wdt.h> +#include <asm/processor.h> + +#define WDTP_MASK TCR_WP(0x3f) + +/* 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 = get_tbclk(); + + /* 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 int booke_wdt_reset(struct udevice *dev) +{ + mtspr(SPRN_TSR, TSR_ENW | TSR_WIS); + + return 0; +} + +static int booke_wdt_start(struct udevice *dev, u64 timeout_ms, ulong flags) +{ + u32 val; + unsigned int timeout = DIV_ROUND_UP(timeout_ms, 1000); + + /* clear status before enabling watchdog */ + booke_wdt_reset(dev); + val = mfspr(SPRN_TCR); + val &= ~WDTP_MASK; + val |= (TCR_WIE | TCR_WRC(WRC_CHIP) | TCR_WP(sec_to_period(timeout))); + + mtspr(SPRN_TCR, val); + + return 0; +} + +static int booke_wdt_stop(struct udevice *dev) +{ + u32 val; + + val = mfspr(SPRN_TCR); + val &= ~(TCR_WIE | WDTP_MASK); + mtspr(SPRN_TCR, val); + + /* clear status to make sure nothing is pending */ + booke_wdt_reset(dev); + + return 0; +} + +static const struct wdt_ops booke_wdt_ops = { + .start = booke_wdt_start, + .stop = booke_wdt_stop, + .reset = booke_wdt_reset, +}; + +static const struct udevice_id booke_wdt_ids[] = { + { .compatible = "fsl,booke-wdt" }, + {} +}; + +U_BOOT_DRIVER(booke_wdt) = { + .name = "booke_wdt", + .id = UCLASS_WDT, + .of_match = booke_wdt_ids, + .ops = &booke_wdt_ops, +}; |