Linux的WDT(watchdog)驱动(5)

//将一个IO地址空间映射到内核的虚拟地址空间上去,便于访问
    wdt_base = ioremap(res->start, size);
    if (wdt_base == NULL) {
        dev_err(dev, "failed to ioremap() region\n");
        ret = -EINVAL;
        goto err_req;
    }

DBG("probe: mapped wdt_base=%p\n", wdt_base);

/* get the memory region for the watchdog timer -- flags is IORESOURCE_IRQ */
    wdt_irq = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
    if (wdt_irq == NULL) {
        dev_err(dev, "no irq resource specified\n");
        ret = -ENOENT;
        goto err_map;
    }

//注册中断服务函数s3c2410wdt_irq()
    ret = request_irq(wdt_irq->start, s3c2410wdt_irq, 0, pdev->name, pdev);
    if (ret != 0) {
        dev_err(dev, "failed to install irq (%d)\n", ret);
        goto err_map;
    }

//从平台时钟队列中获取clk
    wdt_clock = clk_get(&pdev->dev, "watchdog");
    if (IS_ERR(wdt_clock)) {
        dev_err(dev, "failed to find watchdog clock source\n");
        ret = PTR_ERR(wdt_clock);
        goto err_irq;
    }

//inform the system when the clock source should be running
    clk_enable(wdt_clock);

/* see if we can actually set the requested timer margin, and if
    * not, try the default value */

if (s3c2410wdt_set_heartbeat(tmr_margin)) {
        started = s3c2410wdt_set_heartbeat(
                    CONFIG_S3C2410_WATCHDOG_DEFAULT_TIME);

if (started == 0)
            dev_info(dev,
              "tmr_margin value out of range, default %d used\n",
                  CONFIG_S3C2410_WATCHDOG_DEFAULT_TIME);
        else
            dev_info(dev, "default timer value is out of range, "
                            "cannot start\n");
    }

ret = misc_register(&s3c2410wdt_miscdev);
    if (ret) {
        dev_err(dev, "cannot register miscdev on minor=%d (%d)\n",
            WATCHDOG_MINOR, ret);
        goto err_clk;
    }

if (tmr_atboot && started == 0) {
        dev_info(dev, "starting watchdog timer\n");
        s3c2410wdt_start();
    } else if (!tmr_atboot) {
        /* if we're not enabling the watchdog, then ensure it is
        * disabled if it has been left running from the bootloader
        * or other source */

s3c2410wdt_stop();
    }

/* print out a statement of readiness */

wtcon = readl(wdt_base + S3C2410_WTCON);

dev_info(dev, "watchdog %sactive, reset %sabled, irq %sabled\n",
        (wtcon & S3C2410_WTCON_ENABLE) ? "" : "in",
        (wtcon & S3C2410_WTCON_RSTEN) ? "" : "dis",
        (wtcon & S3C2410_WTCON_INTEN) ? "" : "en");

return 0;

err_clk:
    clk_disable(wdt_clock);
    clk_put(wdt_clock);

err_irq:
    free_irq(wdt_irq->start, pdev);

err_map:
    iounmap(wdt_base);

err_req:
    release_resource(wdt_mem);
    kfree(wdt_mem);

return ret;
}

static int __devexit s3c2410wdt_remove(struct platform_device *dev)
{
    release_resource(wdt_mem);
    kfree(wdt_mem);
    wdt_mem = NULL;

free_irq(wdt_irq->start, dev);
    wdt_irq = NULL;

clk_disable(wdt_clock);
    clk_put(wdt_clock);
    wdt_clock = NULL;

iounmap(wdt_base);
    misc_deregister(&s3c2410wdt_miscdev);
    return 0;
}

static void s3c2410wdt_shutdown(struct platform_device *dev)
{
    s3c2410wdt_stop();
}

#ifdef CONFIG_PM

static unsigned long wtcon_save;
static unsigned long wtdat_save;

static int s3c2410wdt_suspend(struct platform_device *dev, pm_message_t state)
{
    /* Save watchdog state, and turn it off. */
    wtcon_save = readl(wdt_base + S3C2410_WTCON);
    wtdat_save = readl(wdt_base + S3C2410_WTDAT);

/* Note that WTCNT doesn't need to be saved. */
    s3c2410wdt_stop();

return 0;
}

static int s3c2410wdt_resume(struct platform_device *dev)
{
    /* Restore watchdog state. */

writel(wtdat_save, wdt_base + S3C2410_WTDAT);
    writel(wtdat_save, wdt_base + S3C2410_WTCNT); /* Reset count */
    writel(wtcon_save, wdt_base + S3C2410_WTCON);

printk(KERN_INFO PFX "watchdog %sabled\n",
          (wtcon_save & S3C2410_WTCON_ENABLE) ? "en" : "dis");

return 0;
}
#else
#define s3c2410wdt_suspend NULL
#define s3c2410wdt_resume NULL
#endif /* CONFIG_PM */

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/16180.html