sbsa watchdog的驱动分析

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寄存器
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值