源码说明
源代码位于 bsp/artinchip/:
bsp/artinchip/drv/wdt/drv_wdt.c,WDT Driver 层实现
bsp/artinchip/hal/wdt/hal_wdt.c,WDT HAL 层实现
bsp/artinchip/include/hal/hal_wdt.h,WDT HAL 层接口头文件
bsp/artinchip/include/drv/aic_drv_wdt.h WDT DRV 层头文件
模块架构
WDT 驱动 Driver 层采用 RT-Thread 的 Watchdog 设备驱动框架,如果只使用HAL层也可以支持 baremetal 方式的应用场景。
针对Watchdog控制器的几个特色功能:- 多通道
暂时只提供一个Watchdog设备(对应通道0)。
- 超时中断
在Watchdog超时之前可以产生一些中断信号,让软件有机会做一些预处理。
- 清零窗口
清零窗口设置范围为0~3,看门狗在设置范围内不能clean计数。
- 调试模式的计数状态 当CPU进入Jtag的debug状态时,Watchdog计数可以选择是否暂停。暂未提供设置接口,默认是暂停。
关键流程设计
初始化流程
WDT 驱动的初始化接口通过 INIT_DEVICE_EXPORT(rt_hw_wdt_init)
完成,主要是通过调用 Watchdog子系统的接口 rt_hw_watchdog_register() 注册一个 Watchdog设备。
WDT 控制器的初始化流程放在 drv_wdt_init() 接口中实现,其中主要步骤有:
初始化模块的clk
注册中断
数据结构设计
struct aic_wdt_dev
属于 Driver 层接口,记录 Watchdog控制器的配置信息:
struct aic_wdt_dev {
rt_watchdog_t wdt;
struct aic_wdt chan;
s32 dbg_continue;
u32 cur_chan;
};
2
3
4
5
6
struct aic_wdt
属于 HAL 层接口,记录每一个Watchdog通道的配置信息:
struct aic_wdt {
u32 clr_thd;
u32 irq_thd;
u32 rst_thd;
};
2
3
4
5
Driver 层接口设计
以下接口是 Watchdog 设备驱动框架的标准接口。
struct rt_watchdog_ops
{
rt_err_t (*init)(rt_watchdog_t *wdt);
rt_err_t (*control)(rt_watchdog_t *wdt, int cmd, void *arg);
};
2
3
4
5
drv_wdt_init
函数原型 | rt_err_t drv_wdt_init(rt_watchdog_t* wdt) |
---|---|
功能说明 | Watchdog控制器的初始化 |
参数定义 | 无 |
返回值 | 0, 成功; <0, 失败 |
注意事项 |
drv_wdt_control
函数原型 | rt_err_t drv_wdt_control(rt_watchdog_t* wdt, int cmd, void* args) |
---|---|
功能说明 | Watchdog驱动的ioctl接口 |
参数定义 | wdt - 指向Watchdog设备的指针 cmd - ioctl命令码 args - ioctl命令相应的参数 |
返回值 | 0, 成功; <0, 失败 |
注意事项 |
其中,cmd命令码的定义如下(详见rt-thread/components/drivers/include/watchdog.h):
#define RT_DEVICE_CTRL_WDT_GET_TIMEOUT (RT_DEVICE_CTRL_BASE(WDT) + 1) /* get timeout(in seconds) */
#define RT_DEVICE_CTRL_WDT_SET_TIMEOUT (RT_DEVICE_CTRL_BASE(WDT) + 2) /* set timeout(in seconds) */
#define RT_DEVICE_CTRL_WDT_GET_TIMELEFT (RT_DEVICE_CTRL_BASE(WDT) + 3) /* get the left time before reboot(in seconds) */
#define RT_DEVICE_CTRL_WDT_KEEPALIVE (RT_DEVICE_CTRL_BASE(WDT) + 4) /* refresh watchdog */
#define RT_DEVICE_CTRL_WDT_START (RT_DEVICE_CTRL_BASE(WDT) + 5) /* start watchdog */
#define RT_DEVICE_CTRL_WDT_STOP (RT_DEVICE_CTRL_BASE(WDT) + 6) /* stop watchdog */
2
3
4
5
6
另外,基于aic的watchdog的cmd命令码的定义如下(详见bsp/arcinchip/include/drv/aic_drv_wdt.h):
#define RT_DEVICE_CTRL_WDT_SET_IRQ_TIMEOUT (RT_DEVICE_CTRL_BASE(WDT) + 7) /* set pretimeout(in seconds) */
#define RT_DEVICE_CTRL_WDT_IRQ_ENABLE (RT_DEVICE_CTRL_BASE(WDT) + 8) /* start pretreatment */
#define RT_DEVICE_CTRL_WDT_IRQ_DISABLE (RT_DEVICE_CTRL_BASE(WDT) + 9) /* stop pretreatment */
#define RT_DEVICE_CTRL_WDT_SET_CLR_THD (RT_DEVICE_CTRL_BASE(WDT) + 10) /* set clear threshold */
2
3
4
HAL 层接口设计
HAL 层的函数接口声明存放在 hal_wdt.h 中,主要接口有:
void hal_wdt_op_clr(u32 thd);
s32 hal_wdt_is_running(void);
void hal_wdt_clr_thd_set(u32 ch, struct aic_wdt *wdt);
void hal_wdt_irq_thd_set(u32 ch, struct aic_wdt *wdt);
void hal_wdt_rst_thd_set(u32 ch, struct aic_wdt *wdt);
void hal_wdt_switch_chan(int chan);
u32 hal_wdt_remain(struct aic_wdt *wdt);
void hal_wdt_enable(u32 enable, u32 dbg_continue);
void hal_wdt_irq_enable(u32 enable);
int hal_wdt_irq_sta(void);
void hal_wdt_thd_get(u32 ch, struct aic_wdt *wdt);
int hal_wdt_clr_int(void);
void hal_wdt_status_show(u32 ch);
2
3
4
5
6
7
8
9
10
11
12
13
14
15
Demo
本Demo是通过test-wdt命令的实现(详见bsp/examples/test-wdt/test-wdt.c),可以作为Watchdog设备的使用参考:
#include <rtthread.h>
#include <aic_core.h>
#include <drivers/watchdog.h>
#include <aic_drv_wdt.h>
#include <hal_wdt.h>
#include <getopt.h>
irqreturn_t aic_wdt_irq(int irq, void *arg)
{
rt_kprintf("Watchdog chan0 IRQ happened\n");
return IRQ_HANDLED;
}
static void idle_hook(void)
{
rt_device_t wdt_dev = RT_NULL;
wdt_dev = rt_device_find("wdt");
rt_device_control(wdt_dev, RT_DEVICE_CTRL_WDT_KEEPALIVE, NULL);
}
static void usage(char * program)
{
printf("\n");
printf("Usage: %s [-s timeout] [-p pretimeout] [-c clear threshold] [-g] [-k] [-u]\n",\
program);
printf("\t -s, --set-timeout\tSet a timeout, in second\n");
printf("\t -p, --set-pretimeout\tSet a pretimeout, in second\n");
printf("\t -c, --set-clear threshold\tSet clear threshold,in second(0~3)\n");
printf("\t -g, --get-timeout\tGet the current timeout, in second\n");
printf("\t -k, --keepalive\tKeepalive the watchdog\n");
printf("\t -u, --usage \n");
printf("\n");
}
void test_wdt(int argc, char **argv)
{
int opt;
int timeout = 0;
rt_device_t wdt_dev = RT_NULL;
wdt_dev = rt_device_find("wdt");
rt_device_init(wdt_dev);
optind = 0;
while ((opt = getopt(argc, argv, "s:p:c:gku")) != -1) {
switch (opt) {
case 'c':
timeout = strtoul(optarg, NULL, 10);
rt_device_control(wdt_dev, RT_DEVICE_CTRL_WDT_SET_CLR_THD, &timeout);
rt_kprintf("set clear threshold:%d\n", timeout);
break;
case 's':
timeout = strtoul(optarg, NULL, 10);
rt_device_control(wdt_dev, RT_DEVICE_CTRL_WDT_SET_TIMEOUT, &timeout);
rt_device_control(wdt_dev, RT_DEVICE_CTRL_WDT_START, RT_NULL);
rt_kprintf("set timeout:%d\n", timeout);
break;
case 'g':
rt_device_control(wdt_dev, RT_DEVICE_CTRL_WDT_GET_TIMEOUT, &timeout);
rt_kprintf("timeout:%d\n", timeout);
break;
case 'p':
timeout = strtoul(optarg, NULL, 10);
rt_device_control(wdt_dev, RT_DEVICE_CTRL_WDT_SET_IRQ_TIMEOUT, &timeout);
rt_device_control(wdt_dev, RT_DEVICE_CTRL_WDT_IRQ_ENABLE, &aic_wdt_irq);
rt_kprintf("set pretimeout:%d\n", timeout);
break;
case 'k':
rt_thread_idle_sethook(idle_hook);
rt_kprintf("feed the dog! \n");
break;
case 'u':
default:
usage(argv[0]);
}
}
}
MSH_CMD_EXPORT_ALIAS(test_wdt, test_wdt, Reboot the system);
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79