1 /*
2 * Copyright (c) 2006-2022, RT-Thread Development Team
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 *
6 * Change Logs:
7 * Date Author Notes
8 * 2022-3-08 GuEe-GUI the first version
9 */
10
11 #include <rthw.h>
12 #include <rtthread.h>
13 #include <rtdevice.h>
14
15 #define WDOG_CONTROL_REG_OFFSET 0x00
16 #define WDOG_CONTROL_REG_WDT_EN_MASK 0x01
17 #define WDOG_CONTROL_REG_RESP_MODE_MASK 0x02
18 #define WDOG_TIMEOUT_RANGE_REG_OFFSET 0x04
19 #define WDOG_TIMEOUT_RANGE_TOPINIT_SHIFT 4
20 #define WDOG_CURRENT_COUNT_REG_OFFSET 0x08
21 #define WDOG_COUNTER_RESTART_REG_OFFSET 0x0c
22 #define WDOG_COUNTER_RESTART_KICK_VALUE 0x76
23 #define WDOG_INTERRUPT_STATUS_REG_OFFSET 0x10
24 #define WDOG_INTERRUPT_CLEAR_REG_OFFSET 0x14
25 #define WDOG_COMP_PARAMS_5_REG_OFFSET 0xe4
26 #define WDOG_COMP_PARAMS_4_REG_OFFSET 0xe8
27 #define WDOG_COMP_PARAMS_3_REG_OFFSET 0xec
28 #define WDOG_COMP_PARAMS_2_REG_OFFSET 0xf0
29 #define WDOG_COMP_PARAMS_1_REG_OFFSET 0xf4
30 #define WDOG_COMP_PARAMS_1_USE_FIX_TOP (1 << 6)
31 #define WDOG_COMP_VERSION_REG_OFFSET 0xf8
32 #define WDOG_COMP_TYPE_REG_OFFSET 0xfc
33
34 /* There are sixteen TOPs (timeout periods) that can be set in the watchdog. */
35 #define DW_WDT_NUM_TOPS 16
36 #define DW_WDT_FIX_TOP(idx) (1U << (16 + idx))
37 #define DW_WDT_DEFAULT_SECONDS 30
38
39 #define MSEC_PER_SEC 1000L
40
41 enum dw_wdt_rmod
42 {
43 DW_WDT_RMOD_RESET = 1,
44 DW_WDT_RMOD_IRQ
45 };
46
47 struct dw_wdt_timeout
48 {
49 rt_uint32_t top_val;
50 rt_uint32_t sec;
51 rt_uint32_t msec;
52 };
53
54 struct dw_wdt
55 {
56 rt_watchdog_t parent;
57
58 void *base;
59 int irq;
60 struct rt_clk *clk;
61 struct rt_reset_control *rstc;
62
63 rt_ubase_t rate;
64 enum dw_wdt_rmod rmod;
65 struct dw_wdt_timeout timeouts[DW_WDT_NUM_TOPS];
66 /* Save/Restore */
67 rt_uint32_t user;
68 rt_uint32_t timeout;
69 rt_uint32_t pretimeout;
70 rt_uint32_t max_hw_heartbeat_ms;
71
72 struct rt_device_notify pretimeout_notify;
73 };
74
75 #define raw_to_dw_wdt(raw) rt_container_of(raw, struct dw_wdt, parent)
76
77 static const rt_uint32_t dw_wdt_fix_tops[DW_WDT_NUM_TOPS] =
78 {
79 DW_WDT_FIX_TOP(0), DW_WDT_FIX_TOP(1), DW_WDT_FIX_TOP(2), DW_WDT_FIX_TOP(3),
80 DW_WDT_FIX_TOP(4), DW_WDT_FIX_TOP(5), DW_WDT_FIX_TOP(6), DW_WDT_FIX_TOP(7),
81 DW_WDT_FIX_TOP(8), DW_WDT_FIX_TOP(9), DW_WDT_FIX_TOP(10), DW_WDT_FIX_TOP(11),
82 DW_WDT_FIX_TOP(12), DW_WDT_FIX_TOP(13), DW_WDT_FIX_TOP(14), DW_WDT_FIX_TOP(15)
83 };
84
dw_wdt_is_enabled(struct dw_wdt * dw_wdt)85 rt_inline int dw_wdt_is_enabled(struct dw_wdt *dw_wdt)
86 {
87 return HWREG32(dw_wdt->base + WDOG_CONTROL_REG_OFFSET) & WDOG_CONTROL_REG_WDT_EN_MASK;
88 }
89
dw_wdt_update_mode(struct dw_wdt * dw_wdt,enum dw_wdt_rmod rmod)90 static void dw_wdt_update_mode(struct dw_wdt *dw_wdt, enum dw_wdt_rmod rmod)
91 {
92 rt_uint32_t val = HWREG32(dw_wdt->base + WDOG_CONTROL_REG_OFFSET);
93
94 if (rmod == DW_WDT_RMOD_IRQ)
95 {
96 val |= WDOG_CONTROL_REG_RESP_MODE_MASK;
97 }
98 else
99 {
100 val &= ~WDOG_CONTROL_REG_RESP_MODE_MASK;
101 }
102
103 HWREG32(dw_wdt->base + WDOG_CONTROL_REG_OFFSET) = val;
104
105 dw_wdt->rmod = rmod;
106 }
107
dw_wdt_find_best_top(struct dw_wdt * dw_wdt,rt_uint32_t timeout,rt_uint32_t * top_val)108 static rt_uint32_t dw_wdt_find_best_top(struct dw_wdt *dw_wdt, rt_uint32_t timeout, rt_uint32_t *top_val)
109 {
110 int idx;
111
112 /*
113 * Find a TOP with timeout greater or equal to the requested number. Note
114 * we'll select a TOP with maximum timeout if the requested timeout couldn't
115 * be reached.
116 */
117 for (idx = 0; idx < DW_WDT_NUM_TOPS; ++idx)
118 {
119 if (dw_wdt->timeouts[idx].sec >= timeout)
120 {
121 break;
122 }
123 }
124
125 if (idx == DW_WDT_NUM_TOPS)
126 {
127 --idx;
128 }
129
130 *top_val = dw_wdt->timeouts[idx].top_val;
131
132 return dw_wdt->timeouts[idx].sec;
133 }
134
dw_wdt_get_max_timeout_ms(struct dw_wdt * dw_wdt)135 static rt_uint32_t dw_wdt_get_max_timeout_ms(struct dw_wdt *dw_wdt)
136 {
137 rt_uint64_t msec;
138 struct dw_wdt_timeout *timeout = &dw_wdt->timeouts[DW_WDT_NUM_TOPS - 1];
139
140 msec = (rt_uint64_t)timeout->sec * MSEC_PER_SEC + timeout->msec;
141
142 return msec < RT_UINT32_MAX ? msec : RT_UINT32_MAX;
143 }
144
dw_wdt_get_timeout(struct dw_wdt * dw_wdt)145 static rt_uint32_t dw_wdt_get_timeout(struct dw_wdt *dw_wdt)
146 {
147 int idx;
148 int top_val = HWREG32(dw_wdt->base + WDOG_TIMEOUT_RANGE_REG_OFFSET) & 0xF;
149
150 for (idx = 0; idx < DW_WDT_NUM_TOPS; ++idx)
151 {
152 if (dw_wdt->timeouts[idx].top_val == top_val)
153 {
154 break;
155 }
156 }
157
158 /*
159 * In IRQ mode due to the two stages counter, the actual timeout is twice
160 * greater than the TOP setting.
161 */
162 return dw_wdt->timeouts[idx].sec * dw_wdt->rmod;
163 }
164
dw_wdt_keep_alive(struct dw_wdt * dw_wdt)165 static int dw_wdt_keep_alive(struct dw_wdt *dw_wdt)
166 {
167 HWREG32(dw_wdt->base + WDOG_COUNTER_RESTART_REG_OFFSET) = WDOG_COUNTER_RESTART_KICK_VALUE;
168
169 return 0;
170 }
171
dw_wdt_set_timeout(struct dw_wdt * dw_wdt,rt_uint32_t top_s)172 static void dw_wdt_set_timeout(struct dw_wdt *dw_wdt, rt_uint32_t top_s)
173 {
174 rt_uint32_t top_val;
175 rt_uint32_t timeout;
176
177 /*
178 * Note IRQ mode being enabled means having a non-zero pre-timeout setup.
179 * In this case we try to find a TOP as close to the half of the requested
180 * timeout as possible since DW Watchdog IRQ mode is designed in two stages
181 * way - first timeout rises the pre-timeout interrupt, second timeout
182 * performs the system reset. So basically the effective watchdog-caused
183 * reset happens after two watchdog TOPs elapsed.
184 */
185 timeout = dw_wdt_find_best_top(dw_wdt, RT_DIV_ROUND_UP(top_s, dw_wdt->rmod), &top_val);
186
187 if (dw_wdt->rmod == DW_WDT_RMOD_IRQ)
188 {
189 dw_wdt->pretimeout = timeout;
190 }
191 else
192 {
193 dw_wdt->pretimeout = 0;
194 }
195
196 /*
197 * Set the new value in the watchdog. Some versions of dw_wdt have have
198 * TOPINIT in the TIMEOUT_RANGE register (as per CP_WDT_DUAL_TOP in
199 * WDT_COMP_PARAMS_1). On those we effectively get a pat of the watchdog
200 * right here.
201 */
202 HWREG32(dw_wdt->base + WDOG_TIMEOUT_RANGE_REG_OFFSET) = top_val | top_val << WDOG_TIMEOUT_RANGE_TOPINIT_SHIFT;
203
204 dw_wdt_keep_alive(dw_wdt);
205
206 /*
207 * In case users set bigger timeout value than HW can support,
208 * kernel(watchdog_dev.c) helps to feed watchdog before wdd->max_hw_heartbeat_ms
209 */
210 if (top_s * 1000 <= dw_wdt->max_hw_heartbeat_ms)
211 {
212 dw_wdt->timeout = timeout * dw_wdt->rmod;
213 }
214 else
215 {
216 dw_wdt->timeout = top_s;
217 }
218 }
219
dw_wdt_set_pretimeout(struct dw_wdt * dw_wdt,rt_uint32_t req)220 static void dw_wdt_set_pretimeout(struct dw_wdt *dw_wdt, rt_uint32_t req)
221 {
222 /*
223 * We ignore actual value of the timeout passed from user-space using it as
224 * a flag whether the pretimeout functionality is intended to be activated.
225 */
226 dw_wdt_update_mode(dw_wdt, req ? DW_WDT_RMOD_IRQ : DW_WDT_RMOD_RESET);
227 dw_wdt_set_timeout(dw_wdt, dw_wdt->timeout);
228 }
229
dw_wdt_arm_system_reset(struct dw_wdt * dw_wdt)230 static void dw_wdt_arm_system_reset(struct dw_wdt *dw_wdt)
231 {
232 rt_uint32_t val = HWREG32(dw_wdt->base + WDOG_CONTROL_REG_OFFSET);
233
234 /* Disable/enable interrupt mode depending on the RMOD flag. */
235 if (dw_wdt->rmod == DW_WDT_RMOD_IRQ)
236 {
237 val |= WDOG_CONTROL_REG_RESP_MODE_MASK;
238 }
239 else
240 {
241 val &= ~WDOG_CONTROL_REG_RESP_MODE_MASK;
242 }
243
244 /* Enable watchdog. */
245 HWREG32(dw_wdt->base + WDOG_CONTROL_REG_OFFSET) = val | WDOG_CONTROL_REG_WDT_EN_MASK;
246 }
247
dw_wdt_start(struct dw_wdt * dw_wdt)248 static int dw_wdt_start(struct dw_wdt *dw_wdt)
249 {
250 rt_clk_enable(dw_wdt->clk);
251
252 dw_wdt_set_timeout(dw_wdt, dw_wdt->timeout);
253 dw_wdt_keep_alive(dw_wdt);
254 dw_wdt_arm_system_reset(dw_wdt);
255
256 return 0;
257 }
258
dw_wdt_stop(struct dw_wdt * dw_wdt)259 static int dw_wdt_stop(struct dw_wdt *dw_wdt)
260 {
261 /*
262 * The DesignWare watchdog cannot be stopped once it has been started so we
263 * do not implement a stop function. The watchdog core will continue to send
264 * heartbeat requests after the watchdog device has been closed.
265 */
266 rt_clk_disable(dw_wdt->clk);
267
268 rt_reset_control_assert(dw_wdt->rstc);
269 rt_reset_control_deassert(dw_wdt->rstc);
270
271 return 0;
272 }
273
dw_wdt_get_timeleft(struct dw_wdt * dw_wdt)274 static rt_uint32_t dw_wdt_get_timeleft(struct dw_wdt *dw_wdt)
275 {
276 rt_uint32_t val, sec;
277
278 val = HWREG32(dw_wdt->base + WDOG_CURRENT_COUNT_REG_OFFSET);
279 sec = val / dw_wdt->rate;
280
281 if (dw_wdt->rmod == DW_WDT_RMOD_IRQ)
282 {
283 val = HWREG32(dw_wdt->base + WDOG_INTERRUPT_STATUS_REG_OFFSET);
284
285 if (!val)
286 {
287 sec += dw_wdt->pretimeout;
288 }
289 }
290
291 return sec;
292 }
293
dw_wdt_init_timeouts(struct dw_wdt * dw_wdt)294 static rt_err_t dw_wdt_init_timeouts(struct dw_wdt *dw_wdt)
295 {
296 int val, tidx;
297 rt_uint64_t msec;
298 struct dw_wdt_timeout tout, *dst;
299 const rt_uint32_t *tops = dw_wdt_fix_tops;
300
301 /*
302 * Convert the specified TOPs into an array of watchdog timeouts. We walk
303 * over the passed TOPs array and calculate corresponding timeouts in
304 * seconds and milliseconds. The milliseconds granularity is needed to
305 * distinguish the TOPs with very close timeouts and to set the watchdog max
306 * heartbeat setting further.
307 */
308 for (val = 0; val < DW_WDT_NUM_TOPS; ++val)
309 {
310 tout.top_val = val;
311 tout.sec = tops[val] / dw_wdt->rate;
312 msec = (rt_uint64_t)tops[val] * MSEC_PER_SEC;
313 rt_do_div(msec, dw_wdt->rate);
314 tout.msec = msec - ((rt_uint64_t)tout.sec * MSEC_PER_SEC);
315
316 /*
317 * Find a suitable place for the current TOP in the timeouts array so
318 * that the list is remained in the ascending order.
319 */
320 for (tidx = 0; tidx < val; ++tidx)
321 {
322 dst = &dw_wdt->timeouts[tidx];
323
324 if (tout.sec > dst->sec || (tout.sec == dst->sec && tout.msec >= dst->msec))
325 {
326 continue;
327 }
328 else
329 {
330 struct dw_wdt_timeout tmp = *dst;
331
332 *dst = tout;
333 tout = tmp;
334 }
335 }
336
337 dw_wdt->timeouts[val] = tout;
338 }
339
340 if (!dw_wdt->timeouts[DW_WDT_NUM_TOPS - 1].sec)
341 {
342 return -RT_ENOSYS;
343 }
344
345 return RT_EOK;
346 }
347
dw_wdt_init(rt_watchdog_t * wdt)348 static rt_err_t dw_wdt_init(rt_watchdog_t *wdt)
349 {
350 rt_err_t status = RT_EOK;
351 struct dw_wdt *dw_wdt = raw_to_dw_wdt(wdt);
352
353 /* Enable normal reset without pre-timeout by default. */
354 dw_wdt_update_mode(dw_wdt, DW_WDT_RMOD_RESET);
355
356 if (dw_wdt_init_timeouts(dw_wdt))
357 {
358 return -RT_ERROR;
359 }
360
361 dw_wdt->max_hw_heartbeat_ms = dw_wdt_get_max_timeout_ms(dw_wdt);
362
363 /*
364 * If the watchdog is already running, use its already configured timeout.
365 * Otherwise use the default or the value provided through devicetree.
366 */
367 if (dw_wdt_is_enabled(dw_wdt))
368 {
369 dw_wdt->timeout = dw_wdt_get_timeout(dw_wdt);
370 }
371 else
372 {
373 dw_wdt->timeout = DW_WDT_DEFAULT_SECONDS;
374 }
375
376 return status;
377 }
378
dw_wdt_control(rt_watchdog_t * wdt,int cmd,void * args)379 static rt_err_t dw_wdt_control(rt_watchdog_t *wdt, int cmd, void *args)
380 {
381 rt_err_t status = RT_EOK;
382 struct dw_wdt *dw_wdt = raw_to_dw_wdt(wdt);
383
384 switch (cmd)
385 {
386 case RT_DEVICE_CTRL_WDT_GET_TIMEOUT:
387 *(rt_uint32_t *)args = dw_wdt_get_timeout(dw_wdt);
388 break;
389
390 case RT_DEVICE_CTRL_WDT_SET_TIMEOUT:
391 dw_wdt_set_timeout(dw_wdt, *(rt_uint32_t *)args);
392 break;
393
394 case RT_DEVICE_CTRL_WDT_GET_TIMELEFT:
395 *(rt_uint32_t *)args = dw_wdt_get_timeleft(dw_wdt);
396 break;
397
398 case RT_DEVICE_CTRL_WDT_KEEPALIVE:
399 dw_wdt_set_pretimeout(dw_wdt, dw_wdt->pretimeout);
400 dw_wdt_keep_alive(dw_wdt);
401 break;
402
403 case RT_DEVICE_CTRL_WDT_START:
404 dw_wdt_start(dw_wdt);
405 dw_wdt->user++;
406 break;
407
408 case RT_DEVICE_CTRL_WDT_STOP:
409 dw_wdt_stop(dw_wdt);
410 dw_wdt->user--;
411 break;
412
413 case RT_DEVICE_CTRL_NOTIFY_SET:
414 rt_hw_interrupt_mask(dw_wdt->irq);
415 if (args)
416 {
417 rt_memcpy(&dw_wdt->pretimeout_notify, args, sizeof(dw_wdt->pretimeout_notify));
418 }
419 else
420 {
421 rt_memset(&dw_wdt->pretimeout_notify, 0, sizeof(dw_wdt->pretimeout_notify));
422 }
423 rt_hw_interrupt_umask(dw_wdt->irq);
424 break;
425
426 default:
427 status = -RT_EINVAL;
428 }
429
430 return status;
431 }
432
433 static const struct rt_watchdog_ops dw_wdt_ops =
434 {
435 .init = dw_wdt_init,
436 .control = dw_wdt_control,
437 };
438
439 #ifdef RT_USING_PM
dw_wdt_pm_suspend(const struct rt_device * device,rt_uint8_t mode)440 static rt_err_t dw_wdt_pm_suspend(const struct rt_device *device, rt_uint8_t mode)
441 {
442 rt_watchdog_t *wdt = rt_container_of(device, rt_watchdog_t, parent);
443 struct dw_wdt *dw_wdt = raw_to_dw_wdt(wdt);
444
445 dw_wdt->timeout = dw_wdt_get_timeleft(dw_wdt) / dw_wdt->rate;
446 dw_wdt_stop(dw_wdt);
447
448 return RT_EOK;
449 }
450
dw_wdt_pm_resume(const struct rt_device * device,rt_uint8_t mode)451 static void dw_wdt_pm_resume(const struct rt_device *device, rt_uint8_t mode)
452 {
453 rt_watchdog_t *wdt = rt_container_of(device, rt_watchdog_t, parent);
454 struct dw_wdt *dw_wdt = raw_to_dw_wdt(wdt);
455
456 if (!dw_wdt->user)
457 {
458 return;
459 }
460
461 if (!dw_wdt_init(wdt))
462 {
463 dw_wdt_start(dw_wdt);
464 }
465 }
466
467 static const struct rt_device_pm_ops dw_wdt_pm_ops =
468 {
469 .suspend = dw_wdt_pm_suspend,
470 .resume = dw_wdt_pm_resume,
471 };
472 #endif /* RT_USING_PM */
473
dw_wdt_isr(int irqno,void * param)474 static void dw_wdt_isr(int irqno, void *param)
475 {
476 struct dw_wdt *wdt = (struct dw_wdt *)param;
477
478 if (!HWREG32(wdt->base + WDOG_INTERRUPT_STATUS_REG_OFFSET))
479 {
480 return;
481 }
482
483 /* Clear the IRQ status (EOI) */
484 (void)HWREG32(wdt->base + WDOG_INTERRUPT_CLEAR_REG_OFFSET);
485
486 if (wdt->pretimeout_notify.notify)
487 {
488 wdt->pretimeout_notify.notify(wdt->pretimeout_notify.dev);
489 }
490 }
491
dw_wdt_free(struct dw_wdt * dw_wdt)492 static void dw_wdt_free(struct dw_wdt *dw_wdt)
493 {
494 if (dw_wdt->base)
495 {
496 rt_iounmap(dw_wdt->base);
497 }
498
499 if (!rt_is_err_or_null(dw_wdt->clk))
500 {
501 rt_clk_disable(dw_wdt->clk);
502 }
503
504 if (!rt_is_err_or_null(dw_wdt->rstc))
505 {
506 rt_reset_control_assert(dw_wdt->rstc);
507 rt_reset_control_put(dw_wdt->rstc);
508 }
509
510 rt_free(dw_wdt);
511 }
512
dw_wdt_probe(struct rt_platform_device * pdev)513 static rt_err_t dw_wdt_probe(struct rt_platform_device *pdev)
514 {
515 rt_err_t err = RT_EOK;
516 const char *dev_name;
517 struct rt_device *dev = &pdev->parent;
518 struct dw_wdt *dw_wdt = rt_calloc(1, sizeof(*dw_wdt));
519
520 if (!dw_wdt)
521 {
522 return -RT_ENOMEM;
523 }
524
525 dw_wdt->base = rt_dm_dev_iomap(dev, 0);
526
527 if (!dw_wdt->base)
528 {
529 err = -RT_EIO;
530 goto _free_res;
531 }
532
533 dw_wdt->irq = rt_dm_dev_get_irq(dev, 0);
534
535 if (dw_wdt->irq < 0)
536 {
537 err = dw_wdt->irq;
538 goto _free_res;
539 }
540
541 dw_wdt->clk = rt_clk_get_by_name(dev, "pclk");
542
543 if (rt_is_err(dw_wdt->clk))
544 {
545 dw_wdt->clk = rt_clk_get_by_index(dev, 0);
546
547 if (rt_is_err(dw_wdt->clk))
548 {
549 err = rt_ptr_err(dw_wdt->clk);
550 goto _free_res;
551 }
552 }
553
554 dw_wdt->rstc = rt_reset_control_get_by_index(dev, 0);
555
556 if (rt_is_err(dw_wdt->rstc))
557 {
558 err = rt_ptr_err(dw_wdt->rstc);
559 goto _free_res;
560 }
561
562 rt_reset_control_deassert(dw_wdt->rstc);
563
564 dev->user_data = dw_wdt;
565
566 dw_wdt->rate = rt_clk_get_rate(dw_wdt->clk);
567 dw_wdt->parent.ops = &dw_wdt_ops;
568
569 rt_dm_dev_set_name_auto(&dw_wdt->parent.parent, "wdt");
570 dev_name = rt_dm_dev_get_name(&dw_wdt->parent.parent);
571
572 rt_hw_interrupt_install(dw_wdt->irq, dw_wdt_isr, dw_wdt, dev_name);
573 rt_hw_interrupt_umask(dw_wdt->irq);
574
575 #ifdef RT_USING_PM
576 rt_pm_device_register(&dw_wdt->parent.parent, &dw_wdt_pm_ops);
577 #endif
578
579 rt_hw_watchdog_register(&dw_wdt->parent, dev_name, 0, dw_wdt);
580
581 return RT_EOK;
582
583 _free_res:
584 dw_wdt_free(dw_wdt);
585
586 return err;
587 }
588
dw_wdt_remove(struct rt_platform_device * pdev)589 static rt_err_t dw_wdt_remove(struct rt_platform_device *pdev)
590 {
591 struct dw_wdt *dw_wdt = pdev->parent.user_data;
592
593 rt_hw_interrupt_mask(dw_wdt->irq);
594 rt_pic_detach_irq(dw_wdt->irq, dw_wdt);
595
596 #ifdef RT_USING_PM
597 rt_pm_device_unregister(&dw_wdt->parent.parent);
598 #endif
599
600 rt_device_unregister(&dw_wdt->parent.parent);
601
602 dw_wdt_free(dw_wdt);
603
604 return RT_EOK;
605 }
606
607 static const struct rt_ofw_node_id dw_wdt_ofw_ids[] =
608 {
609 { .compatible = "snps,dw-wdt" },
610 { /* sentinel */ }
611 };
612
613 static struct rt_platform_driver dw_wdt_driver =
614 {
615 .name = "dw-wdt",
616 .ids = dw_wdt_ofw_ids,
617
618 .probe = dw_wdt_probe,
619 .remove = dw_wdt_remove,
620 };
621 RT_PLATFORM_DRIVER_EXPORT(dw_wdt_driver);
622