您的位置:首页 > 运维架构 > Linux

s3c6410在linux下的WATCHDOG TIMER(看门狗定时器)驱动(2)

2012-05-21 21:29 435 查看
s3c6410硬件WATCHDOG TIMER(看门狗定时器)的链接地址

s3c6410在linux下的WATCHDOG TIMER(看门狗定时器)驱动(1)的链接地址

在上一篇中看了看门狗在linux中驱动实现的整体架构,作为混杂设备和平台设备存在。现在开始看平台设备对应的probe函数。

static struct platform_driver s3c2410wdt_driver = {

.probe
= s3c2410wdt_probe,

.remove
= s3c2410wdt_remove,

.shutdown
= s3c2410wdt_shutdown,

.suspend
= s3c2410wdt_suspend,

.resume
= s3c2410wdt_resume,

.driver
= {

.owner
= THIS_MODULE,

.name = "s3c2410-wdt",

},

};

1、probe函数:

s3c2410wdt_probe函数源码如下:

/* device interface */

static int s3c2410wdt_probe(struct platform_device *pdev)

{

struct resource *res;

struct device *dev;

unsigned int wtcon;

int started = 0;

int ret;

int size;

DBG("%s: probe=%p\n", __func__, pdev);

dev = &pdev->dev;

wdt_dev = &pdev->dev;

/* get the memory region for the watchdog timer */

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);申请I/O内存

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;

}

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

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;

}申请中断,并注册中断处理函数s3c2410wdt_irq()

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);使能看门狗时钟

/* see if we can actually set the requested timer margin, and if

* not, try the default value */设置看门狗复位时间为tmr_margin,如果时间不合法,则返回非0,重新设置为默认值。主要涉及到s3c2410wdt_set_heartbeat函数,源码看下面:

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

}

s3c2410wdt_set_heartbeat函数源码:

static int s3c2410wdt_set_heartbeat(int timeout)

{

unsigned int freq = clk_get_rate(wdt_clock);得到看门狗的时钟频率PCLK

unsigned int count;

unsigned int divisor = 1;

unsigned long wtcon;

if (timeout < 1)看门狗的复位时间不能小于1秒

return -EINVAL;

freq /= 128;看门狗默认使用128的四项分频系数,不太懂。看下图,明明默认值是16,为何说成是128呢?





count = timeout * freq;秒数乘以每秒的时钟数等于计数值

DBG("%s: count=%d, timeout=%d, freq=%d\n",

__func__, count, timeout, freq);

/* if the count is bigger than the watchdog register,

then work out what we need to do (and if) we can

actually make this value

*/

填入WTCNT寄存器的值不能大于0x10000,因为它是16位

if (count >= 0x10000) {

for (divisor = 1; divisor <= 0x100; divisor++) {从1到256之间寻找一个合适的预分频系数

if ((count / divisor) < 0x10000)

break;

}

if ((count / divisor) >= 0x10000) {如果经过预分频和四项分频的计数值仍然大于0x10000,则复位时间太长,看门狗不能达到

dev_err(wdt_dev, "timeout %d too big\n", timeout);

return -EINVAL;

}

}

tmr_margin = timeout;

DBG("%s: timeout=%d, divisor=%d, count=%d (%08x)\n",

__func__, timeout, divisor, count, count/divisor);

count /= divisor;分频后的计数值

wdt_count = count;

/* update the pre-scaler */

wtcon = readl(wdt_base + S3C2410_WTCON);读WTCON寄存器

wtcon &= ~S3C2410_WTCON_PRESCALE_MASK;将WTCON寄存器高8为清零

其中:#define S3C2410_WTCON_PRESCALE_MASK (0xff00)

wtcon |= S3C2410_WTCON_PRESCALE(divisor-1);填入预分频系数,其中有:

#define S3C2410_WTCON_PRESCALE(x) ((x) << 8)

writel(count, wdt_base + S3C2410_WTDAT);将计数值写入数据寄存器WTDAT

writel(wtcon, wdt_base + S3C2410_WTCON);写回WTCON寄存器

return 0;

}s3c2410wdt_set_heartbeat到这里就结束了。回到s3c2410wdt_probe函数中接着看:

ret = misc_register(&s3c2410wdt_miscdev);注册混杂设备的函数,源码如下:

/**

* misc_register-register a miscellaneous device

* @misc: device structure

*

* Register a miscellaneous device with the kernel. If the minor

* number is set to %MISC_DYNAMIC_MINOR a minor number is assigned

* and placed in the minor field of the structure. For other cases

* the minor number requested is used.

*

* The structure passed is linked into the kernel and may not be

* destroyed until it has been unregistered.

*

* A zero is returned on success and a negative errno code for

* failure.

*/



int misc_register(struct miscdevice * misc)

{

struct miscdevice *c;

dev_t dev;

int err = 0;

INIT_LIST_HEAD(&misc->list);

mutex_lock(&misc_mtx);

list_for_each_entry(c, &misc_list, list) {

if (c->minor == misc->minor) {

mutex_unlock(&misc_mtx);

return -EBUSY;

}

}判断次设备号的有效性

if (misc->minor == MISC_DYNAMIC_MINOR) {

int i = DYNAMIC_MINORS;

while (--i >= 0)

if ( (misc_minors[i>>3] & (1 << (i&7))) == 0)

break;

if (i<0) {

mutex_unlock(&misc_mtx);

return -EBUSY;

}

misc->minor = i;

}

if (misc->minor < DYNAMIC_MINORS)

misc_minors[misc->minor >> 3] |= 1 << (misc->minor & 7);

dev = MKDEV(MISC_MAJOR, misc->minor);

动态分配次设备号时的操作

misc->this_device = device_create(misc_class, misc->parent, dev, NULL,

"%s", misc->name); 看下面这句注释:

* device_create - creates a device and registers it with sysfs

if (IS_ERR(misc->this_device)) {

err = PTR_ERR(misc->this_device);

goto out;

}

/*

* Add it to the front, so that later devices can "override"

* earlier defaults

*/

list_add(&misc->list, &misc_list); 把此设备加入到混杂设备链表

out:

mutex_unlock(&misc_mtx);

return err;

} misc_register函数终于完了。

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);读控制寄存器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;

}

注:如果大家对混杂设备的结构还不是很明白,那推荐大家看一篇博文,链接地址在下面:

Linux驱动修炼之道-混杂设备的链接地址

在上面的misc_register函数中,有段动态分配混杂设备次设备号的处理,我但是没具体分析,因为我也不太明白,后来找了下资料,找到篇博文,分析的很好,下面是链接地址:

混杂设备动态次设备号分析的链接地址

s3c6410在linux下的WATCHDOG TIMER(看门狗定时器)驱动(3)的链接地址
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: