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