diff options
Diffstat (limited to 'drivers/watchdog/xilinx_wwdt.c')
-rw-r--r-- | drivers/watchdog/xilinx_wwdt.c | 100 |
1 files changed, 64 insertions, 36 deletions
diff --git a/drivers/watchdog/xilinx_wwdt.c b/drivers/watchdog/xilinx_wwdt.c index c8e6c60cdd..d582e3cc8f 100644 --- a/drivers/watchdog/xilinx_wwdt.c +++ b/drivers/watchdog/xilinx_wwdt.c @@ -3,7 +3,7 @@ * Xilinx window watchdog timer driver. * * Author(s): Michal Simek <michal.simek@xilinx.com> - * Ashok Reddy Soma <ashokred@xilinx.com> + * Ashok Reddy Soma <ashok.reddy.soma@xilinx.com> * * Copyright (c) 2020, Xilinx Inc. */ @@ -23,13 +23,22 @@ /* Generic Control/Status Register Masks */ #define XWT_WWCSR_GWEN_MASK BIT(0) /* Enable Bit */ -/* Register offsets for the Wdt device */ -#define XWT_WWREF_OFFSET 0x1000 /* Refresh Register */ -#define XWT_WWCSR_OFFSET 0x2000 /* Control/Status Register */ -#define XWT_WWOFF_OFFSET 0x2008 /* Offset Register */ -#define XWT_WWCMP0_OFFSET 0x2010 /* Compare Value Register0 */ -#define XWT_WWCMP1_OFFSET 0x2014 /* Compare Value Register1 */ -#define XWT_WWWRST_OFFSET 0x2FD0 /* Warm Reset Register */ +/* Register offsets for the WWDT device */ +#define XWT_WWDT_MWR_OFFSET 0x00 +#define XWT_WWDT_ESR_OFFSET 0x04 +#define XWT_WWDT_FCR_OFFSET 0x08 +#define XWT_WWDT_FWR_OFFSET 0x0c +#define XWT_WWDT_SWR_OFFSET 0x10 +#define XWT_WWDT_CNT_MIN 1 +#define XWT_WWDT_CNT_MAX 0xffffffff + +/* Master Write Control Register Masks */ +#define XWT_WWDT_MWR_MASK BIT(0) + +/* Enable and Status Register Masks */ +#define XWT_WWDT_ESR_WINT_MASK BIT(16) +#define XWT_WWDT_ESR_WSW_MASK BIT(8) +#define XWT_WWDT_ESR_WEN_MASK BIT(0) struct xlnx_wwdt_priv { bool enable_once; @@ -43,16 +52,23 @@ struct xlnx_wwdt_plat { static int xlnx_wwdt_reset(struct udevice *dev) { + u32 esr; struct xlnx_wwdt_priv *wdt = dev_get_priv(dev); - regmap_write(wdt->regs, XWT_WWREF_OFFSET, XWT_WWREF_GWRR_MASK); + regmap_write(wdt->regs, XWT_WWDT_MWR_OFFSET, XWT_WWDT_MWR_MASK); + regmap_read(wdt->regs, XWT_WWDT_ESR_OFFSET, &esr); + esr |= XWT_WWDT_ESR_WINT_MASK; + esr &= ~XWT_WWDT_ESR_WSW_MASK; + regmap_write(wdt->regs, XWT_WWDT_ESR_OFFSET, esr); + regmap_read(wdt->regs, XWT_WWDT_ESR_OFFSET, &esr); + esr |= XWT_WWDT_ESR_WSW_MASK; + regmap_write(wdt->regs, XWT_WWDT_ESR_OFFSET, esr); return 0; } static int xlnx_wwdt_stop(struct udevice *dev) { - u32 csr; struct xlnx_wwdt_priv *wdt = dev_get_priv(dev); if (wdt->enable_once) { @@ -60,10 +76,9 @@ static int xlnx_wwdt_stop(struct udevice *dev) return -EBUSY; } - /* Disable the generic watchdog timer */ - regmap_read(wdt->regs, XWT_WWCSR_OFFSET, &csr); - csr &= ~(XWT_WWCSR_GWEN_MASK); - regmap_write(wdt->regs, XWT_WWCSR_OFFSET, csr); + /* Disable the window watchdog timer */ + regmap_write(wdt->regs, XWT_WWDT_MWR_OFFSET, XWT_WWDT_MWR_MASK); + regmap_write(wdt->regs, XWT_WWDT_ESR_OFFSET, ~(u32)XWT_WWDT_ESR_WEN_MASK); clk_disable(&wdt->clk); @@ -72,11 +87,11 @@ static int xlnx_wwdt_stop(struct udevice *dev) return 0; } -static int xlnx_wwdt_start(struct udevice *dev, u64 timeout, ulong flags) +static int xlnx_wwdt_start(struct udevice *dev, u64 timeout_ms, ulong flags) { int ret; - u32 csr; - u64 count; + u32 esr; + u64 count, timeout; unsigned long clock_f; struct xlnx_wwdt_priv *wdt = dev_get_priv(dev); @@ -88,40 +103,52 @@ static int xlnx_wwdt_start(struct udevice *dev, u64 timeout, ulong flags) dev_dbg(dev, "%s: CLK %ld\n", __func__, clock_f); + /* Convert timeout from msec to sec */ + timeout = timeout_ms / 1000; + /* Calculate timeout count */ count = timeout * clock_f; + /* Count should be at least 1 */ + if (count < XWT_WWDT_CNT_MIN) { + debug("%s: watchdog won't fire with 0 ticks\n", __func__); + count = XWT_WWDT_CNT_MIN; + } + + /* Limit the count to maximum possible value */ + if (count > XWT_WWDT_CNT_MAX) { + debug("%s: maximum watchdog timeout exceeded\n", __func__); + count = XWT_WWDT_CNT_MAX; + } + ret = clk_enable(&wdt->clk); if (ret) { dev_err(dev, "failed to enable clock\n"); return ret; } - /* - * Timeout count is half as there are two windows - * first window overflow is ignored (interrupt), - * reset is only generated at second window overflow - */ - count = count >> 1; - - /* Disable the generic watchdog timer */ - regmap_read(wdt->regs, XWT_WWCSR_OFFSET, &csr); - csr &= ~(XWT_WWCSR_GWEN_MASK); - regmap_write(wdt->regs, XWT_WWCSR_OFFSET, csr); + /* Disable the window watchdog timer */ + regmap_write(wdt->regs, XWT_WWDT_MWR_OFFSET, XWT_WWDT_MWR_MASK); + regmap_write(wdt->regs, XWT_WWDT_ESR_OFFSET, ~(u32)XWT_WWDT_ESR_WEN_MASK); - /* Set compare and offset registers for generic watchdog timeout */ - regmap_write(wdt->regs, XWT_WWCMP0_OFFSET, (u32)count); - regmap_write(wdt->regs, XWT_WWCMP1_OFFSET, 0); - regmap_write(wdt->regs, XWT_WWOFF_OFFSET, (u32)count); + /* Set first window and second window registers with timeout */ + regmap_write(wdt->regs, XWT_WWDT_FWR_OFFSET, 0); /* No pre-timeout */ + regmap_write(wdt->regs, XWT_WWDT_SWR_OFFSET, (u32)count); + regmap_write(wdt->regs, XWT_WWDT_FCR_OFFSET, 0); - /* Enable the generic watchdog timer */ - regmap_read(wdt->regs, XWT_WWCSR_OFFSET, &csr); - csr |= (XWT_WWCSR_GWEN_MASK); - regmap_write(wdt->regs, XWT_WWCSR_OFFSET, csr); + /* Enable the window watchdog timer */ + regmap_read(wdt->regs, XWT_WWDT_ESR_OFFSET, &esr); + esr |= XWT_WWDT_ESR_WEN_MASK; + regmap_write(wdt->regs, XWT_WWDT_ESR_OFFSET, esr); return 0; } +static int xlnx_wwdt_expire_now(struct udevice *dev, ulong flags) +{ + return xlnx_wwdt_start(dev, XWT_WWDT_CNT_MIN, flags); +} + static int xlnx_wwdt_probe(struct udevice *dev) { int ret; @@ -160,6 +187,7 @@ static const struct wdt_ops xlnx_wwdt_ops = { .start = xlnx_wwdt_start, .reset = xlnx_wwdt_reset, .stop = xlnx_wwdt_stop, + .expire_now = xlnx_wwdt_expire_now, }; static const struct udevice_id xlnx_wwdt_ids[] = { |