linux 3.10.84
qcom,wdt@f9017000 {
compatible = "qcom,msm-watchdog";
reg = <0xf9017000 0x1000>;
reg-names = "wdt-base";
interrupts = <0 3 0>, <0 4 0>;
qcom,bark-time = <11000>;
qcom,pet-time = <10000>;
qcom,ipi-ping;
qcom,userspace-watchdog;
};
bark-time:喂狗超时时间(狗叫),11s
pet-time:喂狗时间,10s
qcom,ipi-ping:如有此行,开启ipi-ping功能,则会在每次喂狗时ping所有CPU,如果任意CPU没响应,则不会喂狗。
qcom,userspace-watchdog:如有此行,则可进行用户空间喂狗:
static int msm_watchdog_probe(struct platform_device *pdev)
和设备树的看门狗设备匹配上后执行
static int msm_watchdog_probe(struct platform_device *pdev)
{
int ret;
struct msm_watchdog_data *wdog_dd;
wdog_wq = alloc_workqueue("wdog", WQ_HIGHPRI, 0);
if (!wdog_wq) {
pr_err("Failed to allocate watchdog workqueue\n");
return -EIO;
}
if (!pdev->dev.of_node || !enable)
return -ENODEV;
wdog_dd = kzalloc(sizeof(struct msm_watchdog_data), GFP_KERNEL);
if (!wdog_dd)
return -EIO;
ret = msm_wdog_dt_to_pdata(pdev, wdog_dd);
if (ret)
goto err;
wdog_data = wdog_dd;
wdog_dd->dev = &pdev->dev;
platform_set_drvdata(pdev, wdog_dd);
cpumask_clear(&wdog_dd->alive_mask);
// 将初始化程序init_watchdog_work 加入任务队列,其实挺没必要,直接执行该函数就行。
INIT_WORK(&wdog_dd->init_dogwork_struct, init_watchdog_work);
// 将喂狗程序加入任务队列,负责定时喂狗
INIT_DELAYED_WORK(&wdog_dd->dogwork_struct, pet_watchdog_work);
// 启动刚才初始化程序 init_watchdog_work
queue_work(wdog_wq, &wdog_dd->init_dogwork_struct);
return 0;
err:
destroy_workqueue(wdog_wq);
kzfree(wdog_dd);
return ret;
}
static void init_watchdog_work(struct work_struct *work)
:
初始化看门狗程序
static void init_watchdog_work(struct work_struct *work)
{
struct msm_watchdog_data *wdog_dd = container_of(work,
struct msm_watchdog_data,
init_dogwork_struct);
unsigned long delay_time;
uint32_t val;
u64 timeout;
int ret;
/*
* Disable the watchdog for cluster 1 so that cluster 0 watchdog will
* be mapped to the entire sub-system.
*/
if (wdog_dd->wdog_absent_base)
__raw_writel(2, wdog_dd->wdog_absent_base + WDOG_ABSENT);
if (wdog_dd->irq_ppi) {
wdog_dd->wdog_cpu_dd = alloc_percpu(struct msm_watchdog_data *);
if (!wdog_dd->wdog_cpu_dd) {
dev_err(wdog_dd->dev, "fail to allocate cpu data\n");
return;
}
*__this_cpu_ptr(wdog_dd->wdog_cpu_dd) = wdog_dd;
ret = request_percpu_irq(wdog_dd->bark_irq, wdog_ppi_bark,
"apps_wdog_bark",
wdog_dd->wdog_cpu_dd);
if (ret) {
dev_err(wdog_dd->dev, "failed to request bark irq\n");
free_percpu(wdog_dd->wdog_cpu_dd);
return;
}
} else {
ret = devm_request_irq(wdog_dd->dev, wdog_dd->bark_irq,
wdog_bark_handler, IRQF_TRIGGER_RISING,
"apps_wdog_bark", wdog_dd);
if (ret) {
dev_err(wdog_dd->dev, "failed to request bark irq\n");
return;
}
}
delay_time = msecs_to_jiffies(wdog_dd->pet_time);
wdog_dd->min_slack_ticks = UINT_MAX;
wdog_dd->min_slack_ns = ULLONG_MAX;
configure_bark_dump(wdog_dd);
timeout = (wdog_dd->bark_time * WDT_HZ)/1000;
// 超过bark时间没有喂狗,则触发bark,bark是1个中断信号,
__raw_writel(timeout, wdog_dd->base + WDT0_BARK_TIME);
// 超过bark+3s仍没有喂狗,则触发bite,bite是个复位信号
__raw_writel(timeout + 3*WDT_HZ, wdog_dd->base + WDT0_BITE_TIME);
wdog_dd->panic_blk.notifier_call = panic_wdog_handler;
atomic_notifier_chain_register(&panic_notifier_list,
&wdog_dd->panic_blk);
mutex_init(&wdog_dd->disable_lock);
wdog_dd->user_pet_complete = true;
wdog_dd->user_pet_enabled = false;
// 初始化等待队列,这个队列主要是给用户空间喂狗用的
init_waitqueue_head(&wdog_dd->pet_complete);
// 启动在msm_watchdog_probe初始化了的喂狗程序
queue_delayed_work(wdog_wq, &wdog_dd->dogwork_struct,
delay_time);
val = BIT(EN);
if (wdog_dd->wakeup_irq_enable)
val |= BIT(UNMASKED_INT_EN);
__raw_writel(val, wdog_dd->base + WDT0_EN);
__raw_writel(1, wdog_dd->base + WDT0_RST);
wdog_dd->last_pet = sched_clock();
wdog_dd->enabled = true;
// 初始化看门狗的sysfs,主要还用来在用户空间喂狗
init_watchdog_sysfs(wdog_dd);
if (wdog_dd->irq_ppi)
enable_percpu_irq(wdog_dd->bark_irq, 0);
if (ipi_opt_en)
cpu_pm_register_notifier(&wdog_cpu_pm_nb);
dev_info(wdog_dd->dev, "MSM Watchdog Initialized\n");
return;
}
static void pet_watchdog_work(struct work_struct *work)
定时喂狗程序,对应wdog_dd->dogwork_struct
任务
static void pet_watchdog_work(struct work_struct *work)
{
unsigned long delay_time;
struct delayed_work *delayed_work = to_delayed_work(work);
struct msm_watchdog_data *wdog_dd = container_of(delayed_work,
struct msm_watchdog_data,
dogwork_struct);
// 如果在设备树里开启了ipi_ping,会在这ping所有cpu,如果任意cpu没响应,会阻塞在这,阻止喂狗
if (wdog_dd->do_ipi_ping)
ping_other_cpus(wdog_dd);
// 如果开启了用户空间喂狗(user_pet_complete = 1),会阻塞在这,
// 等待用户空间写user_pet_enabled文件,触发wake_up
while (wait_event_interruptible(
wdog_dd->pet_complete,
wdog_dd->user_pet_complete) != 0)
;
/* Reset work and wait_event */
wdog_dd->user_pet_complete = !wdog_dd->user_pet_enabled;
delay_time = msecs_to_jiffies(wdog_dd->pet_time);
/* Check again before scheduling *
* Could have been changed on other cpu */
// 只要开启看门狗,enable一般为非0
// 启动下一次定时喂狗任务
if (enable)
queue_delayed_work(wdog_wq,
&wdog_dd->dogwork_struct, delay_time);
// 喂狗
if (enable)
pet_watchdog(wdog_dd);
}
static int init_watchdog_sysfs(struct msm_watchdog_data *wdog_dd)
初始化看门狗的用户空间配置
static int init_watchdog_sysfs(struct msm_watchdog_data *wdog_dd)
{
int error = 0;
// 对应用户空间disable文件,可配置是否关闭看门狗
error |= device_create_file(wdog_dd->dev, &dev_attr_disable);
if (of_property_read_bool(wdog_dd->dev->of_node,
"qcom,userspace-watchdog")) {
// 对应用户空间pet_time文件,可查看喂狗周期,数值和设备树中pet-time配置项相同
error |= device_create_file(wdog_dd->dev, &dev_attr_pet_time);
// 对应用户空间user_pet_enabled文件,可配置是否开启用户空间喂狗功能
// 读该文件时调用wdog_user_pet_enabled_get,
// 写该文件时调用wdog_user_pet_enabled_set。
error |= device_create_file(wdog_dd->dev,
&dev_attr_user_pet_enabled);
}
if (error)
dev_err(wdog_dd->dev, "cannot create sysfs attribute\n");
return error;
}
static ssize_t wdog_user_pet_enabled_set(struct device *dev,struct device_attribute *attr,const char *buf, size_t count)
static void __wdog_user_pet(struct msm_watchdog_data *wdog_dd)
用户空间写user_pet_enabled文件时,执行该函数,进行喂狗和开关用户空间喂狗操作
static ssize_t wdog_user_pet_enabled_set(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
{
int ret;
struct msm_watchdog_data *wdog_dd = dev_get_drvdata(dev);
// 如果写入的是'Y' 'y' '1',user_pet_enabled设置为'1'
// 如果写入的是'N' 'n' '0',user_pet_enabled设置为'0'
ret = strtobool(buf, &wdog_dd->user_pet_enabled);
if (ret) {
dev_err(wdog_dd->dev, "invalid user input\n");
return ret;
}
// 进行喂狗
__wdog_user_pet(wdog_dd);
return count;
}
/*
* Userspace Watchdog Support:
* Write 1 to the "user_pet_enabled" file to enable hw support for a
* userspace watchdog.
* Userspace is required to pet the watchdog by continuing to write 1
* to this file in the expected interval.
* Userspace may disable this requirement by writing 0 to this same
* file.
*/
/*
* 用户空间看门狗支持:
* 将1写入 “user_pet_enabled” 文件,以启用对用户空间看门狗的硬件支持。
* 需要用户空间通过在预期的间隔内继续向该文件写入1来监视看门狗。
* 用户空间可以通过将0写入同一文件来禁用此要求。
*/
static void __wdog_user_pet(struct msm_watchdog_data *wdog_dd)
{
wdog_dd->user_pet_complete = true;
// wake up pet_watchdog_work 程序,进行喂狗
wake_up(&wdog_dd->pet_complete);
}