1
2
3
4
5
6
7
8
9#include <common.h>
10#include <dm.h>
11#include <log.h>
12#include <wdt.h>
13#include <clk.h>
14#include <div64.h>
15#include <dm/device_compat.h>
16#include <linux/err.h>
17#include <linux/io.h>
18
19struct cdns_regs {
20 u32 zmr;
21 u32 ccr;
22 u32 restart;
23 u32 status;
24};
25
26struct cdns_wdt_priv {
27 bool rst;
28 struct cdns_regs *regs;
29};
30
31#define CDNS_WDT_DEFAULT_TIMEOUT 10
32
33
34#define CDNS_WDT_MIN_TIMEOUT 1
35#define CDNS_WDT_MAX_TIMEOUT 516
36
37
38#define CDNS_WDT_RESTART_KEY 0x00001999
39
40
41#define CDNS_WDT_REGISTER_ACCESS_KEY 0x00920000
42
43
44#define CDNS_WDT_COUNTER_VALUE_DIVISOR 0x1000
45
46
47#define CDNS_WDT_PRESCALE_64 64
48#define CDNS_WDT_PRESCALE_512 512
49#define CDNS_WDT_PRESCALE_4096 4096
50#define CDNS_WDT_PRESCALE_SELECT_64 1
51#define CDNS_WDT_PRESCALE_SELECT_512 2
52#define CDNS_WDT_PRESCALE_SELECT_4096 3
53
54
55#define CDNS_WDT_CLK_75MHZ 75000000
56
57
58#define CDNS_WDT_COUNTER_MAX 0xFFF
59
60
61
62
63
64
65
66#define CDNS_WDT_ZMR_WDEN_MASK 0x00000001
67#define CDNS_WDT_ZMR_RSTEN_MASK 0x00000002
68#define CDNS_WDT_ZMR_IRQEN_MASK 0x00000004
69#define CDNS_WDT_ZMR_RSTLEN_16 0x00000030
70#define CDNS_WDT_ZMR_ZKEY_VAL 0x00ABC000
71
72
73
74
75
76
77#define CDNS_WDT_CCR_CRV_MASK 0x00003FFC
78
79
80static inline void cdns_wdt_writereg(u32 *addr, u32 val)
81{
82 writel(val, addr);
83}
84
85
86
87
88
89
90
91
92
93
94static int cdns_wdt_reset(struct udevice *dev)
95{
96 struct cdns_wdt_priv *priv = dev_get_priv(dev);
97
98 debug("%s\n", __func__);
99
100 cdns_wdt_writereg(&priv->regs->restart, CDNS_WDT_RESTART_KEY);
101
102 return 0;
103}
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128static int cdns_wdt_start(struct udevice *dev, u64 timeout, ulong flags)
129{
130 ulong clk_f;
131 u32 count, prescaler, ctrl_clksel, data = 0;
132 struct clk clock;
133 struct cdns_wdt_priv *priv = dev_get_priv(dev);
134
135 if (clk_get_by_index(dev, 0, &clock) < 0) {
136 dev_err(dev, "failed to get clock\n");
137 return -1;
138 }
139
140 clk_f = clk_get_rate(&clock);
141 if (IS_ERR_VALUE(clk_f)) {
142 dev_err(dev, "failed to get rate\n");
143 return -1;
144 }
145
146
147 do_div(timeout, 1000);
148 timeout = max_t(u64, timeout, CDNS_WDT_MIN_TIMEOUT);
149 timeout = min_t(u64, timeout, CDNS_WDT_MAX_TIMEOUT);
150
151 debug("%s: CLK_FREQ %ld, timeout %lld\n", __func__, clk_f, timeout);
152
153 if (clk_f <= CDNS_WDT_CLK_75MHZ) {
154 prescaler = CDNS_WDT_PRESCALE_512;
155 ctrl_clksel = CDNS_WDT_PRESCALE_SELECT_512;
156 } else {
157 prescaler = CDNS_WDT_PRESCALE_4096;
158 ctrl_clksel = CDNS_WDT_PRESCALE_SELECT_4096;
159 }
160
161
162
163
164
165 count = (timeout * (clk_f / prescaler)) /
166 CDNS_WDT_COUNTER_VALUE_DIVISOR + 1;
167
168 if (count > CDNS_WDT_COUNTER_MAX)
169 count = CDNS_WDT_COUNTER_MAX;
170
171 cdns_wdt_writereg(&priv->regs->zmr, CDNS_WDT_ZMR_ZKEY_VAL);
172
173 count = (count << 2) & CDNS_WDT_CCR_CRV_MASK;
174
175
176 data = count | CDNS_WDT_REGISTER_ACCESS_KEY | ctrl_clksel;
177 cdns_wdt_writereg(&priv->regs->ccr, data);
178
179 data = CDNS_WDT_ZMR_WDEN_MASK | CDNS_WDT_ZMR_RSTLEN_16 |
180 CDNS_WDT_ZMR_ZKEY_VAL;
181
182
183 if (priv->rst) {
184 data |= CDNS_WDT_ZMR_RSTEN_MASK;
185 data &= ~CDNS_WDT_ZMR_IRQEN_MASK;
186 } else {
187 data &= ~CDNS_WDT_ZMR_RSTEN_MASK;
188 data |= CDNS_WDT_ZMR_IRQEN_MASK;
189 }
190
191 cdns_wdt_writereg(&priv->regs->zmr, data);
192 cdns_wdt_writereg(&priv->regs->restart, CDNS_WDT_RESTART_KEY);
193
194 return 0;
195}
196
197
198
199
200
201
202
203
204
205
206
207static int cdns_wdt_stop(struct udevice *dev)
208{
209 struct cdns_wdt_priv *priv = dev_get_priv(dev);
210
211 cdns_wdt_writereg(&priv->regs->zmr,
212 CDNS_WDT_ZMR_ZKEY_VAL & (~CDNS_WDT_ZMR_WDEN_MASK));
213
214 return 0;
215}
216
217
218
219
220
221
222
223
224
225
226
227
228
229static int cdns_wdt_expire_now(struct udevice *dev, ulong flags)
230{
231 struct cdns_wdt_priv *priv = dev_get_priv(dev);
232 u32 data, count = 0;
233
234#if defined(DEBUG)
235 count = 0x40;
236 debug("%s: Expire wdt%u\n", __func__, dev_seq(dev));
237#endif
238
239 cdns_wdt_writereg(&priv->regs->zmr, CDNS_WDT_ZMR_ZKEY_VAL);
240
241 count = (count << 2) & CDNS_WDT_CCR_CRV_MASK;
242
243
244 data = count | CDNS_WDT_REGISTER_ACCESS_KEY;
245 cdns_wdt_writereg(&priv->regs->ccr, data);
246
247 data = CDNS_WDT_ZMR_WDEN_MASK | CDNS_WDT_ZMR_RSTEN_MASK |
248 CDNS_WDT_ZMR_ZKEY_VAL;
249
250 cdns_wdt_writereg(&priv->regs->zmr, data);
251 cdns_wdt_writereg(&priv->regs->restart, CDNS_WDT_RESTART_KEY);
252
253 return 0;
254}
255
256
257
258
259
260
261
262
263static int cdns_wdt_probe(struct udevice *dev)
264{
265 debug("%s: Probing wdt%u\n", __func__, dev_seq(dev));
266
267 return 0;
268}
269
270static int cdns_wdt_of_to_plat(struct udevice *dev)
271{
272 struct cdns_wdt_priv *priv = dev_get_priv(dev);
273
274 priv->regs = (struct cdns_regs *)dev_read_addr(dev);
275 if (IS_ERR(priv->regs))
276 return PTR_ERR(priv->regs);
277
278 priv->rst = dev_read_bool(dev, "reset-on-timeout");
279
280 debug("%s: reset %d\n", __func__, priv->rst);
281
282 return 0;
283}
284
285static const struct wdt_ops cdns_wdt_ops = {
286 .start = cdns_wdt_start,
287 .reset = cdns_wdt_reset,
288 .stop = cdns_wdt_stop,
289 .expire_now = cdns_wdt_expire_now,
290};
291
292static const struct udevice_id cdns_wdt_ids[] = {
293 { .compatible = "cdns,wdt-r1p2" },
294 {}
295};
296
297U_BOOT_DRIVER(cdns_wdt) = {
298 .name = "cdns_wdt",
299 .id = UCLASS_WDT,
300 .of_match = cdns_wdt_ids,
301 .probe = cdns_wdt_probe,
302 .priv_auto = sizeof(struct cdns_wdt_priv),
303 .of_to_plat = cdns_wdt_of_to_plat,
304 .ops = &cdns_wdt_ops,
305};
306