Linux watchdog驱动详解

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/weixin_38696651/article/details/89431178

本文是基于mini2440开发板Linux版本号是linux-2.6.32.2的学习笔记

一. watchdog硬件信息

watchdog的device信息如下:

static struct resource s3c_wdt_resource[] = 
{
	[0] = {
		.start = S3C24XX_PA_WATCHDOG,
		.end   = S3C24XX_PA_WATCHDOG + S3C24XX_SZ_WATCHDOG - 1,
		.flags = IORESOURCE_MEM,
	},
	[1] = {
		.start = IRQ_WDT,
		.end   = IRQ_WDT,
		.flags = IORESOURCE_IRQ,
	}

};

struct platform_device s3c_device_wdt = {
	.name		  = "s3c2410-wdt",
	.id		  = -1,
	.num_resources	  = ARRAY_SIZE(s3c_wdt_resource),
	.resource	  = s3c_wdt_resource,
};

  • 控制寄存器: WTCON,地址是 0x53000000
  • 初始时间寄存器: WTDAT,地址是 0x53000004
  • 计数寄存器: WTCNT,地址是 0x53000008
  • 中断号是:IRQ_WDT,就是1
二. watchdog device 注册

调用platform_device_register注册平台s3c_device_wdt设备,设备名称是: “s3c2410-wdt”

三. watchdog driver 注册
  • watchdog的driver结构体如下:
static struct platform_driver s3c2410wdt_driver =
{
	.probe		= s3c2410wdt_probe,
	.remove		= __devexit_p(s3c2410wdt_remove),
	.shutdown	= s3c2410wdt_shutdown,
	.suspend	= s3c2410wdt_suspend,
	.resume		= s3c2410wdt_resume,
	.driver		= {
		.owner	= THIS_MODULE,
		.name	= "s3c2410-wdt",
	},
};
  • 注册watchdog driver
static int __init watchdog_init(void)
{
	return platform_driver_register(&s3c2410wdt_driver);
}

三. watchdog driver的probe函数

  • 获取mem资源并进行io映射
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (res == NULL)
{
    dev_err(dev, "no memory resource specified\n");
    return -ENOENT;
}

size = (res->end - res->start) + 1;
wdt_mem = request_mem_region(res->start, size, pdev->name);
if (wdt_mem == NULL)
{
    dev_err(dev, "failed to get memory region\n");
    ret = -ENOENT;
    goto err_req;
}

wdt_base = ioremap(res->start, size);
if (wdt_base == NULL)
{
    dev_err(dev, "failed to ioremap() region\n");
    ret = -EINVAL;
    goto err_req;
}
  • 获取中断号,申请中断
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;
}

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;
}
  • 获取watchdog时钟,并使能时钟
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;
}

clk_enable(wdt_clock);
  • 设置定时超时时间
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");
}

如果tmr_margin太大,超出了范围,就使用默认值15,就是15m超时。

  • 启动watchdog定时器
s3c2410wdt_start();
四. 设置定时时间

设置定时时间调用的函数是:s3c2410wdt_set_heartbeat

  • 获取watchdog时钟
 freq = clk_get_rate(wdt_clock);
  • 除以分频系数
freq /= 128;

这里的分频系数写死成了128,实际上可以通过寄存器设置。
在这里插入图片描述

  • 计算count值
count = timeout * freq;

假设时钟频率为1kHz,即1s中计数1000次,那么15s超时,15s内计数15000次。
找到预分频系数

if (count >= 0x10000) 
{
    for (divisor = 1; divisor <= 0x100; divisor++) 
    {
        if ((count / divisor) < 0x10000)
            break;
    }

    if ((count / divisor) >= 0x10000) 
    {
        dev_err(wdt_dev, "timeout %d too big\n", timeout);
        return -EINVAL;
    }
}

预分频系数divisor的取值为:count / divisor) < 0x10000
在这里插入图片描述
WTCNT寄存器保存的count的最大值即为0x10000,所以要小于0x10000

得到count值

count /= divisor;
  • 将预分频的数值写入寄存器
/* update the pre-scaler */
wtcon = readl(wdt_base + S3C2410_WTCON);
wtcon &= ~S3C2410_WTCON_PRESCALE_MASK;
wtcon |= S3C2410_WTCON_PRESCALE(divisor-1);
writel(wtcon, wdt_base + S3C2410_WTCON);

在这里插入图片描述
预分频的值为0 ~ 255

将count值写入data装在寄存器

writel(count, wdt_base + S3C2410_WTDAT);

在这里插入图片描述
count值为0 ~ (0x10000 - 1)

五.启动watchdog定时器
  • 首先若定时器已经启动了,先停止定时器。
__s3c2410wdt_stop();
  • 启动定时器,设置分频系数,预分频系统在上一步已经设置到寄存器中。分频系数是128
#define S3C2410_WTCON_ENABLE  (1<<5)
wtcon |= S3C2410_WTCON_ENABLE | S3C2410_WTCON_DIV128;

在这里插入图片描述

  • 如果soft_noboot设置成了1,把watchdog定时器当成普通的定时器来使用,这个时候要使能中断,并且关掉watchdog定时器的复位功能。
wtcon |= S3C2410_WTCON_INTEN;
wtcon &= ~S3C2410_WTCON_RSTEN;

在这里插入图片描述
在这里插入图片描述

六. 关闭watchdog定时器

WTCON寄存器的第5位设置为0,第0位设置为0

wtcon &= ~(S3C2410_WTCON_ENABLE | S3C2410_WTCON_RSTEN);
writel(wtcon, wdt_base + S3C2410_WTCON);
七. watchdog定时器的中断

定时器中断主要是把count值重新写到 WTDAT寄存器

writel(wdt_count, wdt_base + S3C2410_WTCNT);

猜你喜欢

转载自blog.csdn.net/weixin_38696651/article/details/89431178
今日推荐