高通看门狗驱动源码分析

内核版本

 linux  3.10.84

函数调用关系

高通看门狗驱动源码分析_第1张图片

设备树分析

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:如有此行,则可进行用户空间喂狗:

  • 将1写入 “user_pet_enabled” 文件,以启用对用户空间看门狗的硬件支持。需要用户空间通过在预期的间隔内继续向该文件写入1来监视看门狗。用户空间可以通过将0写入同一文件来禁用此要求。

源码分析

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);
}

你可能感兴趣的:(linux,linux,驱动开发)