blob: 3c1ead3dda6ae6d278026056b961eb38a2099f8f [file] [log] [blame]
ETIENNE DUBLE73a6e702023-01-25 10:57:28 +01001// 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};