1// SPDX-License-Identifier: GPL-2.0+ 2/* 3 * Copyright (C) 2013 Lubomir Rintel <lkundrak@v3.sk> 4 * Copyright (C) 2023 Etienne Dubl�� (CNRS) <etienne.duble@imag.fr> 5 * 6 * This code is mostly derived from the linux driver. 7 */ 8 9#include <dm.h> 10#include <wdt.h> 11#include <asm/io.h> 12#include <linux/delay.h> 13 14#define PM_RSTC 0x1c 15#define PM_WDOG 0x24 16 17#define PM_PASSWORD 0x5a000000 18 19/* The hardware supports a maximum timeout value of 0xfffff ticks 20 * (just below 16 seconds). 21 */ 22#define PM_WDOG_MAX_TICKS 0x000fffff 23#define PM_RSTC_WRCFG_CLR 0xffffffcf 24#define PM_RSTC_WRCFG_FULL_RESET 0x00000020 25#define PM_RSTC_RESET 0x00000102 26 27#define MS_TO_WDOG_TICKS(x) (((x) << 16) / 1000) 28 29struct bcm2835_wdt_priv { 30 void __iomem *base; 31 u64 timeout_ticks; 32}; 33 34static int bcm2835_wdt_start_ticks(struct udevice *dev, 35 u64 timeout_ticks, ulong flags) 36{ 37 struct bcm2835_wdt_priv *priv = dev_get_priv(dev); 38 void __iomem *base = priv->base; 39 u32 cur; 40 41 writel(PM_PASSWORD | timeout_ticks, base + PM_WDOG); 42 cur = readl(base + PM_RSTC); 43 writel(PM_PASSWORD | (cur & PM_RSTC_WRCFG_CLR) | PM_RSTC_WRCFG_FULL_RESET, 44 base + PM_RSTC); 45 46 return 0; 47} 48 49static int bcm2835_wdt_start(struct udevice *dev, u64 timeout_ms, ulong flags) 50{ 51 struct bcm2835_wdt_priv *priv = dev_get_priv(dev); 52 53 priv->timeout_ticks = MS_TO_WDOG_TICKS(timeout_ms); 54 55 if (priv->timeout_ticks > PM_WDOG_MAX_TICKS) { 56 printf("bcm2835_wdt: the timeout value is too high, using ~16s instead.\n"); 57 priv->timeout_ticks = PM_WDOG_MAX_TICKS; 58 } 59 60 return bcm2835_wdt_start_ticks(dev, priv->timeout_ticks, flags); 61} 62 63static int bcm2835_wdt_reset(struct udevice *dev) 64{ 65 struct bcm2835_wdt_priv *priv = dev_get_priv(dev); 66 67 /* restart the timer with the value of priv->timeout_ticks 68 * saved from the last bcm2835_wdt_start() call. 69 */ 70 return bcm2835_wdt_start_ticks(dev, priv->timeout_ticks, 0); 71} 72 73static int bcm2835_wdt_stop(struct udevice *dev) 74{ 75 struct bcm2835_wdt_priv *priv = dev_get_priv(dev); 76 void __iomem *base = priv->base; 77 78 writel(PM_PASSWORD | PM_RSTC_RESET, base + PM_RSTC); 79 80 return 0; 81} 82 83static int bcm2835_wdt_expire_now(struct udevice *dev, ulong flags) 84{ 85 int ret; 86 87 /* use a timeout of 10 ticks (~150us) */ 88 ret = bcm2835_wdt_start_ticks(dev, 10, flags); 89 if (ret) 90 return ret; 91 92 mdelay(500); 93 94 return 0; 95} 96 97static const struct wdt_ops bcm2835_wdt_ops = { 98 .reset = bcm2835_wdt_reset, 99 .start = bcm2835_wdt_start, 100 .stop = bcm2835_wdt_stop, 101 .expire_now = bcm2835_wdt_expire_now, 102}; 103 104static const struct udevice_id bcm2835_wdt_ids[] = { 105 { .compatible = "brcm,bcm2835-pm" }, 106 { .compatible = "brcm,bcm2835-pm-wdt" }, 107 { /* sentinel */ } 108}; 109 110static int bcm2835_wdt_probe(struct udevice *dev) 111{ 112 struct bcm2835_wdt_priv *priv = dev_get_priv(dev); 113 114 priv->base = dev_remap_addr(dev); 115 if (!priv->base) 116 return -EINVAL; 117 118 priv->timeout_ticks = PM_WDOG_MAX_TICKS; 119 120 bcm2835_wdt_stop(dev); 121 122 return 0; 123} 124 125U_BOOT_DRIVER(bcm2835_wdt) = { 126 .name = "bcm2835_wdt", 127 .id = UCLASS_WDT, 128 .of_match = bcm2835_wdt_ids, 129 .probe = bcm2835_wdt_probe, 130 .priv_auto = sizeof(struct bcm2835_wdt_priv), 131 .ops = &bcm2835_wdt_ops, 132}; 133