// SPDX-License-Identifier: GPL-2.0+ /* * DaVinci Watchdog driver * */ #include #include #include #include /* Control Register */ #define DAVINCI_WDT_ID 0x00 #define DAVINCI_WDT_TIM12 0x10 #define DAVINCI_WDT_TIM34 0x14 #define DAVINCI_WDT_PRD12 0x18 #define DAVINCI_WDT_PRD34 0x1C #define DAVINCI_WDT_TCR 0x20 #define DAVINCI_WDT_TGCR 0x24 #define DAVINCI_WDT_WDTCR 0x28 #define DAVINCI_TCR_CONT_EN BIT(7) #define DAVINCI_TGCR_PLUSEN BIT(4) #define DAVINCI_TGCR_WDT_MODE BIT(3) #define DAVINCI_TGCR_TIM34RS BIT(1) #define DAVINCI_TGCR_TIM12RS BIT(0) #define DAVINCI_WDTCR_INVALID_KEY (0x5555 << 16) #define DAVINCI_WDTCR_WDKEY0 (0xA5C6 << 16) #define DAVINCI_WDTCR_WDKEY1 (0xDA7E << 16) #define DAVINCI_WDTCR_WDFLAG BIT(15) #define DAVINCI_WDTCR_WDEN BIT(14) #define DEFAULT_THRESHOLD 0xA03200000 struct davinci_wdt_priv { void __iomem *base; struct clk *ref_clk; }; static int davinci_wdt_start(struct udevice *dev, u64 timeout_ms, ulong flags) { struct davinci_wdt_priv *priv = dev_get_priv(dev); ulong rate = clk_get_rate(priv->ref_clk); u64 threshold; if (!rate) threshold = DEFAULT_THRESHOLD; else threshold = rate * timeout_ms / 1000; /* Reset control registers */ writel(0, priv->base + DAVINCI_WDT_TCR); writel(0, priv->base + DAVINCI_WDT_TGCR); /* Enable watchdog mode and timers */ writel(DAVINCI_TGCR_WDT_MODE | DAVINCI_TGCR_TIM12RS | DAVINCI_TGCR_TIM34RS, priv->base + DAVINCI_WDT_TGCR); /* Reset counters */ writel(0, priv->base + DAVINCI_WDT_TIM12); writel(0, priv->base + DAVINCI_WDT_TIM34); /* Set timeout threshold */ writel(threshold & 0xFFFFFFFF, priv->base + DAVINCI_WDT_PRD12); writel(threshold >> 32, priv->base + DAVINCI_WDT_PRD34); /* Enable counter */ writel(DAVINCI_TCR_CONT_EN, priv->base + DAVINCI_WDT_TCR); /* Go to watchdog's active state */ writel(DAVINCI_WDTCR_WDEN | DAVINCI_WDTCR_WDKEY0, priv->base + DAVINCI_WDT_WDTCR); writel(DAVINCI_WDTCR_WDEN | DAVINCI_WDTCR_WDKEY1, priv->base + DAVINCI_WDT_WDTCR); return 0; } static int davinci_wdt_expire_now(struct udevice *dev, ulong flags) { struct davinci_wdt_priv *priv = dev_get_priv(dev); writel(DAVINCI_WDTCR_INVALID_KEY, priv->base + DAVINCI_WDT_WDTCR); return 0; } static int davinci_wdt_restart(struct udevice *dev) { struct davinci_wdt_priv *priv = dev_get_priv(dev); writel(DAVINCI_WDTCR_WDEN | DAVINCI_WDTCR_WDKEY0, priv->base + DAVINCI_WDT_WDTCR); writel(DAVINCI_WDTCR_WDEN | DAVINCI_WDTCR_WDKEY1, priv->base + DAVINCI_WDT_WDTCR); return 0; } static int davinci_wdt_probe(struct udevice *dev) { struct davinci_wdt_priv *priv = dev_get_priv(dev); priv->base = dev_remap_addr_index(dev, 0); if (!priv->base) return -EFAULT; priv->ref_clk = devm_clk_get(dev, "ref"); if (IS_ERR(priv->ref_clk)) return PTR_ERR(priv->ref_clk); return 0; } static const struct wdt_ops davinci_wdt_ops = { .start = davinci_wdt_start, .reset = davinci_wdt_restart, .expire_now = davinci_wdt_expire_now, }; static const struct udevice_id davinci_wdt_ids[] = { {.compatible = "ti,davinci-wdt"}, {} }; U_BOOT_DRIVER(davinci_wdt) = { .name = "davinci_wdt", .id = UCLASS_WDT, .probe = davinci_wdt_probe, .of_match = davinci_wdt_ids, .ops = &davinci_wdt_ops, .priv_auto = sizeof(struct davinci_wdt_priv), };