Chinaunix首页 | 论坛 | 博客
  • 博客访问: 931244
  • 博文数量: 96
  • 博客积分: 10071
  • 博客等级: 上将
  • 技术积分: 1118
  • 用 户 组: 普通用户
  • 注册时间: 2007-09-20 17:54
文章分类

全部博文(96)

文章存档

2011年(3)

2010年(3)

2009年(29)

2008年(54)

2007年(7)

分类: LINUX

2008-03-12 18:12:49

分析一个看门狗驱动程序
creator
sz111@126.com
http://creatorwu.cublog.cn/

1.首先wdt是一个platform设备,注册的时候采用platform_driver_register。
用platform_driver_register 向系统注册这个驱动程序.而这个函数会在
s3c2410wdt_driver的信息里提取name为搜索内容,搜索系统注册的device
中有没有这个 platform_device。 如果有注册,那么接着会执行platform_driver
里probe函数.在这里显然是s3c2410wdt_probe函数  在probe函数里,用的最多和刚
才platform_device有关的语句是platform_get_resource,这条语句用于获取
platform_device里的resource资料.例如映射的IO地址,中断等.剩下等得就是
ioremap,和 request_irq等的事情了。
而如果调用platform_driver_unregister也会去调用remov了。
2.在wdt驱动里面设定name为s3c2410-wdt,而在devs.c中有定义
struct platform_device s3c_device_wdt = {
    .name          = "s3c2410-wdt",
    .id          = -1,
    .num_resources      = ARRAY_SIZE(s3c_wdt_resource),
    .resource      = s3c_wdt_resource,
};
/* Watchdog */
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,
    }
};
所以可以找到系统里面有这个platform_device,接着就会执行
s3c2410wdt_probe。
static int s3c2410wdt_probe(struct platform_device *pdev)
{
    struct resource *res;
    int started = 0;
    int ret;
    int size;
    DBG("%s: probe=%p\n", __FUNCTION__, pdev);
    /* get the memory region for the watchdog timer */
/**
*    platform_get_resource - get a resource for a device
*    @dev: platform device
*    @type: resource type
*    @num: resource index
*/
//获得特殊寄存器占用的mem资源
    res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
    if (res == NULL) {
        printk(KERN_INFO PFX "failed to get memory region resouce\n");
        return -ENOENT;
    }
    size = (res->end-res->start)+1;
    wdt_mem = request_mem_region(res->start, size, pdev->name);
    if (wdt_mem == NULL) {
        printk(KERN_INFO PFX "failed to get memory region\n");
        return -ENOENT;
    }
  //获得ioremap的地址,这样就可以方便的操作寄存器了。
    wdt_base = ioremap(res->start, size);
    if (wdt_base == 0) {
        printk(KERN_INFO PFX "failed to ioremap() region\n");
        return -EINVAL;
    }
    DBG("probe: mapped wdt_base=%p\n", wdt_base);
  //获得中断的地址
    res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
    if (res == NULL) {
        printk(KERN_INFO PFX "failed to get irq resource\n");
        iounmap(wdt_base);
        return -ENOENT;
    }
/**
*    request_irq - allocate an interrupt line
*    @irq: Interrupt line to allocate  
*    @handler: Function to be called when the IRQ occurs
*    @irqflags: Interrupt type flags
*    @devname: An ascii name for the claiming device
*    @dev_id: A cookie passed back to the handler function
*
*    This call allocates interrupt resources and enables the
*    interrupt line and IRQ handling. From the point this
*    call is made your handler function may be invoked. Since
*    your handler function must clear any interrupt the board
*    raises, you must take care both to initialise your hardware
*    and to set up the interrupt handler in the right order.
*
*    Dev_id must be globally unique. Normally the address of the
*    device data structure is used as the cookie. Since the handler
*    receives this value it makes sense to use it.
*
*    If your interrupt is shared you must pass a non NULL dev_id
*    as this is required when freeing the interrupt.
*
*    Flags:
*
*    IRQF_SHARED        Interrupt is shared
*    IRQF_DISABLED    Disable local interrupts while processing
*    IRQF_SAMPLE_RANDOM    The interrupt can be used for entropy
*
*/
//根据获得的中断向量号申请中断。
    ret = request_irq(res->start, s3c2410wdt_irq, 0, pdev->name, pdev);
    if (ret != 0) {
        printk(KERN_INFO PFX "failed to install irq (%d)\n", ret);
        iounmap(wdt_base);
        return ret;
    }
/* Clock API calls */
//struct clk *clk_get(struct device *dev, const char *id)
//获得wdt的clock
    wdt_clock = clk_get(&pdev->dev, "watchdog");
    if (wdt_clock == NULL) {
        printk(KERN_INFO PFX "failed to  watchdog clock source\n");
        iounmap(wdt_base);
        return -ENOENT;
    }
  //使能wdt clock
    clk_enable(wdt_clock);
    /* see if we can actually set the requested timer margin, and if
     * not, try the default value */
//对wdt 进行配置
    if (s3c2410wdt_set_heartbeat(tmr_margin)) {
        started = s3c2410wdt_set_heartbeat(CONFIG_S3C2410_WATCHDOG_DEFAULT_TIME);
        if (started == 0) {
            printk(KERN_INFO PFX "tmr_margin value out of range, default %d used\n",
                   CONFIG_S3C2410_WATCHDOG_DEFAULT_TIME);
        } else {
            printk(KERN_INFO PFX "default timer value is out of range, cannot start\n");
        }
    }
  //把wdt注册为一个杂项设备
    ret = misc_register(&s3c2410wdt_miscdev);
    if (ret) {
        printk (KERN_ERR PFX "cannot register miscdev on minor=%d (%d)\n",
            WATCHDOG_MINOR, ret);
        iounmap(wdt_base);
        return ret;
    }
    if (tmr_atboot && started == 0) {
        printk(KERN_INFO PFX "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();
    }
    return 0;
}
阅读(4397) | 评论(1) | 转发(2) |
给主人留下些什么吧!~~

chinaunix网友2008-03-25 23:10:26

我可喜欢看你的文章了,对我的帮助很大,感恩.