1 /*
2 * Copyright (C) 2019 Rockchip Electronics Co., Ltd
3 *
4 * SPDX-License-Identifier: GPL-2.0+
5 */
6
7 #include <asm/io.h>
8 #include <asm/utils.h>
9 #include <clk.h>
10 #include <dm.h>
11 #include <reset.h>
12 #include <wdt.h>
13
14 #define WDT_CR 0x00
15 #define WDT_TORR 0x04
16 #define WDT_CRR 0x0C
17 #define WDT_EN_MASK 0x01
18 #define WDT_MODE_MASK 0x02
19 #define WDT_CRR_RESTART_VAL 0x76
20
21 DECLARE_GLOBAL_DATA_PTR;
22
23 struct rockchip_wdt_priv {
24 void __iomem *base;
25 struct clk clk;
26 unsigned long rate;
27 struct reset_ctl rst;
28 };
29
30 /*
31 * Set the watchdog time interval.
32 * Counter is 32 bit.
33 */
rockchip_wdt_settimeout(u64 timeout,struct rockchip_wdt_priv * priv)34 static int rockchip_wdt_settimeout(u64 timeout,
35 struct rockchip_wdt_priv *priv)
36 {
37 signed int i;
38
39 /* calculate the timeout range value */
40 i = log_2_n_round_up(timeout * priv->rate / 1000) - 16;
41 if (i > 15)
42 i = 15;
43 if (i < 0)
44 i = 0;
45
46 writel((i | (i << 4)), priv->base + WDT_TORR);
47
48 return 0;
49 }
50
rockchip_wdt_enable(struct rockchip_wdt_priv * priv)51 static void rockchip_wdt_enable(struct rockchip_wdt_priv *priv)
52 {
53 u32 val = readl(priv->base + WDT_CR);
54
55 /* Disable interrupt mode; always perform system reset. */
56 val &= ~WDT_MODE_MASK;
57 /* Enable watchdog. */
58 val |= WDT_EN_MASK;
59
60 writel(val, priv->base + WDT_CR);
61 }
62
rockchip_wdt_is_enabled(struct rockchip_wdt_priv * priv)63 static unsigned int rockchip_wdt_is_enabled(struct rockchip_wdt_priv *priv)
64 {
65 unsigned long val;
66
67 val = readl(priv->base + WDT_CR);
68
69 return val & WDT_EN_MASK;
70 }
71
rockchip_wdt_reset(struct udevice * dev)72 static int rockchip_wdt_reset(struct udevice *dev)
73 {
74 struct rockchip_wdt_priv *priv = dev_get_priv(dev);
75
76 if (rockchip_wdt_is_enabled(priv))
77 /* restart the watchdog counter */
78 writel(WDT_CRR_RESTART_VAL, priv->base + WDT_CRR);
79
80 return 0;
81 }
82
rockchip_wdt_start(struct udevice * dev,u64 timeout,ulong flags)83 static int rockchip_wdt_start(struct udevice *dev, u64 timeout, ulong flags)
84 {
85 struct rockchip_wdt_priv *priv = dev_get_priv(dev);
86
87 printf("Rockchip watchdog timeout: %lld sec\n", timeout / 1000);
88
89 if (priv->rst.dev)
90 reset_deassert(&priv->rst);
91
92 rockchip_wdt_reset(dev);
93 rockchip_wdt_settimeout(timeout, priv);
94 rockchip_wdt_enable(priv);
95 rockchip_wdt_reset(dev);
96
97 return 0;
98 }
99
rockchip_wdt_stop(struct udevice * dev)100 static int rockchip_wdt_stop(struct udevice *dev)
101 {
102 struct rockchip_wdt_priv *priv = dev_get_priv(dev);
103
104 if (priv->rst.dev) {
105 reset_assert(&priv->rst);
106 reset_deassert(&priv->rst);
107 }
108
109 printf("Rockchip watchdog stop\n");
110
111 return 0;
112 }
113
114 static const struct wdt_ops rockchip_wdt_ops = {
115 .start = rockchip_wdt_start,
116 .reset = rockchip_wdt_reset,
117 .stop = rockchip_wdt_stop,
118 };
119
rockchip_wdt_ofdata_to_platdata(struct udevice * dev)120 static int rockchip_wdt_ofdata_to_platdata(struct udevice *dev)
121 {
122 struct rockchip_wdt_priv *priv = dev_get_priv(dev);
123
124 priv->base = dev_read_addr_ptr(dev);
125 if (!priv->base)
126 return -ENOENT;
127
128 return 0;
129 }
130
rockchip_wdt_probe(struct udevice * dev)131 static int rockchip_wdt_probe(struct udevice *dev)
132 {
133 struct rockchip_wdt_priv *priv = dev_get_priv(dev);
134 int ret;
135
136 ret = reset_get_by_name(dev, "reset", &priv->rst);
137 if (ret) {
138 pr_err("reset_get_by_name(reset) failed: %d\n", ret);
139 priv->rst.dev = NULL;
140 }
141
142 ret = clk_get_by_index(dev, 0, &priv->clk);
143 if (ret < 0)
144 return ret;
145
146 /* Need clk framework support */
147 priv->rate = clk_get_rate(&priv->clk);
148 if (priv->rate < 0)
149 return -EINVAL;
150
151 return 0;
152 }
153
154 static const struct udevice_id rockchip_wdt_ids[] = {
155 { .compatible = "snps,dw-wdt" },
156 {}
157 };
158
159 U_BOOT_DRIVER(rockchip_wdt) = {
160 .name = "rockchip_wdt",
161 .id = UCLASS_WDT,
162 .of_match = rockchip_wdt_ids,
163 .probe = rockchip_wdt_probe,
164 .priv_auto_alloc_size = sizeof(struct rockchip_wdt_priv),
165 .ofdata_to_platdata = rockchip_wdt_ofdata_to_platdata,
166 .ops = &rockchip_wdt_ops,
167 };
168