| 在上一篇中看了看门狗在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 sysfsif (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函数中,有段动态分配混杂设备次设备号的处理,我但是没具体分析,因为我也不太明白,后来找了下资料,找到篇博文,分析的很好,下面是链接地址: 混杂设备动态次设备号分析的链接地址 
 
 |