sbsa watchdog的全称是SBSA(Server Base System Architecture) Generic Watchdog driver。也就是sbsa是Server Base System Architecture的缩写
这份code的路径在drivers/watchdog/sbsa_gwdt.c 中
ARM SBSA Generic Watchdog的timeout 分为两个阶段,也就是会发送两次signal,第一次的signal叫做ws0,这会产生一个中断,在中断中会调用panic函数,第二次才是真正的硬件reset信号.在sbsa_gwdt.c 中是否要产生两次信号是有action这个变量决定的.如果只aciton等于0的话,当watchdog time到期后,先忽略WS0,然后被WS1 reset。如果是两次的话,第一次到期后ws0先产生一个中断,第二次到期后ws1就直接reset系统了
* SBSA GWDT:
* if action is 1 (the two stages mode):
* |--------WOR-------WS0--------WOR-------WS1
* |----timeout-----(panic)----timeout-----reset
*
* if action is 0 (the single stage mode):
* |------WOR-----WS0(ignored)-----WOR------WS1
* |--------------timeout-------------------reset
下来看看源码中是怎么做的.
static const struct platform_device_id sbsa_gwdt_pdev_match[] = {
{ .name = DRV_NAME, },
{},
};
MODULE_DEVICE_TABLE(platform, sbsa_gwdt_pdev_match);
static struct platform_driver sbsa_gwdt_driver = {
.driver = {
.name = DRV_NAME,
.pm = &sbsa_gwdt_pm_ops,
.of_match_table = sbsa_gwdt_of_match,
},
.probe = sbsa_gwdt_probe,
.remove = sbsa_gwdt_remove,
.shutdown = sbsa_gwdt_shutdown,
.id_table = sbsa_gwdt_pdev_match,
};
module_platform_driver(sbsa_gwdt_driver);
这里通过module_platform_driver 注册platform driver,由于gtdt中已经注册了platform device了,因此这里会调用sbsa_gwdt_probe
static int sbsa_gwdt_probe(struct platform_device *pdev)
{
void __iomem *rf_base, *cf_base;
struct device *dev = &pdev->dev;
struct watchdog_device *wdd;
struct sbsa_gwdt *gwdt;
struct resource *res;
int ret, irq;
u32 status;
//通过devm_kzalloc 申请sbsa_gwdt 内存.
gwdt = devm_kzalloc(dev, sizeof(*gwdt), GFP_KERNEL);
if (!gwdt)
return -ENOMEM;
platform_set_drvdata(pdev, gwdt);
//得到cf和rf memory分别是4k
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
cf_base = devm_ioremap_resource(dev, res);
if (IS_ERR(cf_base))
return PTR_ERR(cf_base);
res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
rf_base = devm_ioremap_resource(dev, res);
if (IS_ERR(rf_base))
return PTR_ERR(rf_base);
/*
* Get the frequency of system counter from the cp15 interface of ARM
* Generic timer. We don't need to check it, because if it returns "0",
* system would panic in very early stage.
*/
//原来watchog 用来计数的time是arch time 啊啊啊啊啊啊
gwdt->clk = arch_timer_get_cntfrq();
gwdt->refresh_base = rf_base;
gwdt->control_base = cf_base;
wdd = &gwdt->wdd;
wdd->parent = dev;
wdd->info = &sbsa_gwdt_info;
wdd->ops = &sbsa_gwdt_ops;
wdd->min_timeout = 1;
wdd->max_hw_heartbeat_ms = U32_MAX / gwdt->clk * 1000;
wdd->timeout = DEFAULT_TIMEOUT;
watchdog_set_drvdata(wdd, gwdt);
watchdog_set_nowayout(wdd, nowayout);
status = readl(cf_base + SBSA_GWDT_WCS);
if (status & SBSA_GWDT_WCS_WS1) {
dev_warn(dev, "System reset by WDT.\n");
wdd->bootstatus |= WDIOF_CARDRESET;
}
if (status & SBSA_GWDT_WCS_EN)
set_bit(WDOG_HW_RUNNING, &wdd->status);
//判断是用single stage mode还是two stages mode
if (action) {
//得到第一阶段的irq number
irq = platform_get_irq(pdev, 0);
if (irq < 0) {
action = 0;
dev_warn(dev, "unable to get ws0 interrupt.\n");
} else {
/*
* In case there is a pending ws0 interrupt, just ping
* the watchdog before registering the interrupt routine
*/
//注册第一阶段用的中断的callback函数sbsa_gwdt_interrupt,在这个函数总就只调用了 panic(WATCHDOG_NAME " timeout");
writel(0, rf_base + SBSA_GWDT_WRR);
if (devm_request_irq(dev, irq, sbsa_gwdt_interrupt, 0,
pdev->name, gwdt)) {
action = 0;
dev_warn(dev, "unable to request IRQ %d.\n",
irq);
}
}
if (!action)
dev_warn(dev, "falling back to single stage mode.\n");
}
/*
* In the single stage mode, The first signal (WS0) is ignored,
* the timeout is (WOR * 2), so the maximum timeout should be doubled.
*/
//如果是single stage mode。则watchdog timeout是two stages mode的两倍
if (!action)
wdd->max_hw_heartbeat_ms *= 2;
//计算到期时间
watchdog_init_timeout(wdd, timeout, dev);
/*
* Update timeout to WOR.
* Because of the explicit watchdog refresh mechanism,
* it's also a ping, if watchdog is enabled.
*/
//设定到期时间
sbsa_gwdt_set_timeout(wdd, wdd->timeout);
//向watchdog framework 注册字符设备,这样就可以在user space通过ioctl和sys控制watchdog
ret = watchdog_register_device(wdd);
if (ret)
return ret;
dev_info(dev, "Initialized with %ds timeout @ %u Hz, action=%d.%s\n",
wdd->timeout, gwdt->clk, action,
status & SBSA_GWDT_WCS_EN ? " [enabled]" : "");
return 0;
}
int watchdog_init_timeout(struct watchdog_device *wdd,
unsigned int timeout_parm, struct device *dev)
{
unsigned int t = 0;
int ret = 0;
watchdog_check_min_max_timeout(wdd);
/* try to get the timeout module parameter first */
if (!watchdog_timeout_invalid(wdd, timeout_parm) && timeout_parm) {
wdd->timeout = timeout_parm;
return ret;
}
if (timeout_parm)
ret = -EINVAL;
/* try to get the timeout_sec property */
if (dev == NULL || dev->of_node == NULL)
return ret;
of_property_read_u32(dev->of_node, "timeout-sec", &t);
if (!watchdog_timeout_invalid(wdd, t) && t)
wdd->timeout = t;
else
ret = -EINVAL;
return ret;
}
watchdog_init_timeout 首先从模块参数中得到到期时间,如果没有的话,试图通过从dts中的timeout-sec得到到期时间.
最后检查这个时间是有效的后,就赋值给wdd->timeout = t
static int sbsa_gwdt_set_timeout(struct watchdog_device *wdd,
unsigned int timeout)
{
struct sbsa_gwdt *gwdt = watchdog_get_drvdata(wdd);
wdd->timeout = timeout;
if (action)
writel(gwdt->clk * timeout,
gwdt->control_base + SBSA_GWDT_WOR);
else
/*
* In the single stage mode, The first signal (WS0) is ignored,
* the timeout is (WOR * 2), so the WOR should be configured
* to half value of timeout.
*/
writel(gwdt->clk / 2 * timeout,
gwdt->control_base + SBSA_GWDT_WOR);
return 0;
}
sbsa_gwdt_set_timeout 就直接通过writel写cf寄存器
这份code的路径在drivers/watchdog/sbsa_gwdt.c 中
ARM SBSA Generic Watchdog的timeout 分为两个阶段,也就是会发送两次signal,第一次的signal叫做ws0,这会产生一个中断,在中断中会调用panic函数,第二次才是真正的硬件reset信号.在sbsa_gwdt.c 中是否要产生两次信号是有action这个变量决定的.如果只aciton等于0的话,当watchdog time到期后,先忽略WS0,然后被WS1 reset。如果是两次的话,第一次到期后ws0先产生一个中断,第二次到期后ws1就直接reset系统了
* SBSA GWDT:
* if action is 1 (the two stages mode):
* |--------WOR-------WS0--------WOR-------WS1
* |----timeout-----(panic)----timeout-----reset
*
* if action is 0 (the single stage mode):
* |------WOR-----WS0(ignored)-----WOR------WS1
* |--------------timeout-------------------reset
下来看看源码中是怎么做的.
static const struct platform_device_id sbsa_gwdt_pdev_match[] = {
{ .name = DRV_NAME, },
{},
};
MODULE_DEVICE_TABLE(platform, sbsa_gwdt_pdev_match);
static struct platform_driver sbsa_gwdt_driver = {
.driver = {
.name = DRV_NAME,
.pm = &sbsa_gwdt_pm_ops,
.of_match_table = sbsa_gwdt_of_match,
},
.probe = sbsa_gwdt_probe,
.remove = sbsa_gwdt_remove,
.shutdown = sbsa_gwdt_shutdown,
.id_table = sbsa_gwdt_pdev_match,
};
module_platform_driver(sbsa_gwdt_driver);
这里通过module_platform_driver 注册platform driver,由于gtdt中已经注册了platform device了,因此这里会调用sbsa_gwdt_probe
static int sbsa_gwdt_probe(struct platform_device *pdev)
{
void __iomem *rf_base, *cf_base;
struct device *dev = &pdev->dev;
struct watchdog_device *wdd;
struct sbsa_gwdt *gwdt;
struct resource *res;
int ret, irq;
u32 status;
//通过devm_kzalloc 申请sbsa_gwdt 内存.
gwdt = devm_kzalloc(dev, sizeof(*gwdt), GFP_KERNEL);
if (!gwdt)
return -ENOMEM;
platform_set_drvdata(pdev, gwdt);
//得到cf和rf memory分别是4k
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
cf_base = devm_ioremap_resource(dev, res);
if (IS_ERR(cf_base))
return PTR_ERR(cf_base);
res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
rf_base = devm_ioremap_resource(dev, res);
if (IS_ERR(rf_base))
return PTR_ERR(rf_base);
/*
* Get the frequency of system counter from the cp15 interface of ARM
* Generic timer. We don't need to check it, because if it returns "0",
* system would panic in very early stage.
*/
//原来watchog 用来计数的time是arch time 啊啊啊啊啊啊
gwdt->clk = arch_timer_get_cntfrq();
gwdt->refresh_base = rf_base;
gwdt->control_base = cf_base;
wdd = &gwdt->wdd;
wdd->parent = dev;
wdd->info = &sbsa_gwdt_info;
wdd->ops = &sbsa_gwdt_ops;
wdd->min_timeout = 1;
wdd->max_hw_heartbeat_ms = U32_MAX / gwdt->clk * 1000;
wdd->timeout = DEFAULT_TIMEOUT;
watchdog_set_drvdata(wdd, gwdt);
watchdog_set_nowayout(wdd, nowayout);
status = readl(cf_base + SBSA_GWDT_WCS);
if (status & SBSA_GWDT_WCS_WS1) {
dev_warn(dev, "System reset by WDT.\n");
wdd->bootstatus |= WDIOF_CARDRESET;
}
if (status & SBSA_GWDT_WCS_EN)
set_bit(WDOG_HW_RUNNING, &wdd->status);
//判断是用single stage mode还是two stages mode
if (action) {
//得到第一阶段的irq number
irq = platform_get_irq(pdev, 0);
if (irq < 0) {
action = 0;
dev_warn(dev, "unable to get ws0 interrupt.\n");
} else {
/*
* In case there is a pending ws0 interrupt, just ping
* the watchdog before registering the interrupt routine
*/
//注册第一阶段用的中断的callback函数sbsa_gwdt_interrupt,在这个函数总就只调用了 panic(WATCHDOG_NAME " timeout");
writel(0, rf_base + SBSA_GWDT_WRR);
if (devm_request_irq(dev, irq, sbsa_gwdt_interrupt, 0,
pdev->name, gwdt)) {
action = 0;
dev_warn(dev, "unable to request IRQ %d.\n",
irq);
}
}
if (!action)
dev_warn(dev, "falling back to single stage mode.\n");
}
/*
* In the single stage mode, The first signal (WS0) is ignored,
* the timeout is (WOR * 2), so the maximum timeout should be doubled.
*/
//如果是single stage mode。则watchdog timeout是two stages mode的两倍
if (!action)
wdd->max_hw_heartbeat_ms *= 2;
//计算到期时间
watchdog_init_timeout(wdd, timeout, dev);
/*
* Update timeout to WOR.
* Because of the explicit watchdog refresh mechanism,
* it's also a ping, if watchdog is enabled.
*/
//设定到期时间
sbsa_gwdt_set_timeout(wdd, wdd->timeout);
//向watchdog framework 注册字符设备,这样就可以在user space通过ioctl和sys控制watchdog
ret = watchdog_register_device(wdd);
if (ret)
return ret;
dev_info(dev, "Initialized with %ds timeout @ %u Hz, action=%d.%s\n",
wdd->timeout, gwdt->clk, action,
status & SBSA_GWDT_WCS_EN ? " [enabled]" : "");
return 0;
}
int watchdog_init_timeout(struct watchdog_device *wdd,
unsigned int timeout_parm, struct device *dev)
{
unsigned int t = 0;
int ret = 0;
watchdog_check_min_max_timeout(wdd);
/* try to get the timeout module parameter first */
if (!watchdog_timeout_invalid(wdd, timeout_parm) && timeout_parm) {
wdd->timeout = timeout_parm;
return ret;
}
if (timeout_parm)
ret = -EINVAL;
/* try to get the timeout_sec property */
if (dev == NULL || dev->of_node == NULL)
return ret;
of_property_read_u32(dev->of_node, "timeout-sec", &t);
if (!watchdog_timeout_invalid(wdd, t) && t)
wdd->timeout = t;
else
ret = -EINVAL;
return ret;
}
watchdog_init_timeout 首先从模块参数中得到到期时间,如果没有的话,试图通过从dts中的timeout-sec得到到期时间.
最后检查这个时间是有效的后,就赋值给wdd->timeout = t
static int sbsa_gwdt_set_timeout(struct watchdog_device *wdd,
unsigned int timeout)
{
struct sbsa_gwdt *gwdt = watchdog_get_drvdata(wdd);
wdd->timeout = timeout;
if (action)
writel(gwdt->clk * timeout,
gwdt->control_base + SBSA_GWDT_WOR);
else
/*
* In the single stage mode, The first signal (WS0) is ignored,
* the timeout is (WOR * 2), so the WOR should be configured
* to half value of timeout.
*/
writel(gwdt->clk / 2 * timeout,
gwdt->control_base + SBSA_GWDT_WOR);
return 0;
}
sbsa_gwdt_set_timeout 就直接通过writel写cf寄存器