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