Chinaunix首页 | 论坛 | 博客
  • 博客访问: 81857
  • 博文数量: 62
  • 博客积分: 0
  • 博客等级: 民兵
  • 技术积分: 14
  • 用 户 组: 普通用户
  • 注册时间: 2013-12-23 21:49
文章分类

全部博文(62)

文章存档

2014年(55)

2013年(7)

我的朋友

分类: 嵌入式

2014-05-01 17:09:58

原文地址:ok6410的MMC卡驱动分析 作者:archer239915

内核已经存在SD卡的驱动,所以仅需要添加SD卡设备到设备列表注册就可以了。但是从头到尾分析一下MMC的驱动对于理解linux的驱动设计
思想是很有帮助的。mmc分为控制器和mmc卡,所以也有两个驱动

1. core文件夹的core.c先创建一个工作队列,再注册总线和类。

点击(此处)折叠或打开

  1. static int __init mmc_init(void)
  2. {
  3.     int ret;
  4.     workqueue = alloc_ordered_workqueue("kmmcd", 0);
  5.     if (!workqueue)
  6.         return -ENOMEM;
  7.     ret = mmc_register_bus();
  8.     if (ret)
  9.         goto destroy_workqueue;
  10.     ret = mmc_register_host_class();
  11.     if (ret)
  12.         goto unregister_bus;
  13.     ret = sdio_register_bus();
  14.     if (ret)
  15.         goto unregister_host_class;
  16.     return 0;
  17. unregister_host_class:
  18.     mmc_unregister_host_class();
  19. unregister_bus:
  20.     mmc_unregister_bus();
  21. destroy_workqueue:
  22.     destroy_workqueue(workqueue);
  23.     return ret;
  24. }
2. 接着card文件夹的block.c再申请主设备号,并将块设备的驱动挂载到上面的总线上(mmc_register_driver)

点击(此处)折叠或打开

  1. static int __init mmc_blk_init(void)
  2. {
  3.     int res;

  4.     if (perdev_minors != CONFIG_MMC_BLOCK_MINORS)
  5.         pr_info("mmcblk: using %d minors per device\n", perdev_minors);

  6.     max_devices = 256 / perdev_minors;

  7.     res = register_blkdev(MMC_BLOCK_MAJOR, "mmc");
  8.     if (res)
  9.         goto out;

  10.     res = mmc_register_driver(&mmc_driver);
  11.     if (res)
  12.         goto out2;

  13.     return 0;
  14.  out2:
  15.     unregister_blkdev(MMC_BLOCK_MAJOR, "mmc");
  16.  out:
  17.     return res;
  18. }
3. host文件夹中sdhci-s3c.c再注册platform驱动,板文件注册platform设备,所以platform总线会调用该驱动的probe。

点击(此处)折叠或打开

  1. static int __devinit sdhci_s3c_probe(struct platform_device *pdev)
4. probe中主要先由板文件得到硬件资源,申请后记录到一个结构体中struct sdhci_s3c *sc;这个主要用来方便实现操作函数。

点击(此处)折叠或打开

  1. struct sdhci_s3c {
  2.     struct sdhci_host    *host;    //新创建的host控制器
  3.     struct platform_device    *pdev;
  4.     struct resource        *ioarea;
  5.     struct s3c_sdhci_platdata *pdata;
  6.     unsigned int        cur_clk;
  7.     int            ext_cd_irq;
  8.     int            ext_cd_gpio;

  9.     struct clk        *clk_io;
  10.     struct clk        *clk_bus[MAX_BUS_CLK];
  11. };
5. probe再申请控制器结构体host,并初始化一些成员,关键是指定了操作函数集合sdhci_s3c_ops。
    其实sdhci_alloc_host分配了sdhci_host和mmc_host两个结构体,并将sdhci_host的mmc成员指向mmc_host。

点击(此处)折叠或打开

  1. host = sdhci_alloc_host(dev, sizeof(struct sdhci_s3c));

    点击(此处)折叠或打开

    1. host->ioaddr = ioremap_nocache(res->start, resource_size(res));
    2. if (!host->ioaddr) {
    3. dev_err(dev, "failed to map registers\n");
    4. ret = -ENXIO;
    5. goto err_req_regs;
    6. }
    7. /* Ensure we have minimal gpio selected CMD/CLK/Detect */
    8. if (pdata->cfg_gpio)
    9. pdata->cfg_gpio(pdev, pdata->max_width);

    10. host->hw_name = "samsung-hsmmc";
    11. host->ops = &sdhci_s3c_ops;
    12. host->quirks = 0;
    13. host->irq = irq;

6. probe再调用sdhci_add_host(host),实际上又调用了mmc_add_host将这个控制器设备添加到内核中。

7. 总结:
    其实这个host设备和字符驱动中的cdev作用差不多,仅用platform_driver与设备匹配后再在cdev的fops中做真正的硬件工作。
    所以这个添加的host也是做真正的工作:探测到mmc卡并初始化一个card结构体挂载到总线上。总线用最前面的块设备驱动来匹配,
    最终肯定是用一个块设备来表示卡。那么块设备驱动中的函数是怎样调用到这个控制器host的函数呢?
    很简单,仅需要用一个指针指向host就可以了。
    还有,在platform_driver的probe中添加到内核的控制器设备host添加到哪里了?它又是如何完成上面的探测工作呢?
    由下面,host仅需要探测卡和发送数据命令即可,工作简单,所以就放在/sys/class/mmc/下的mmc0文件。而不需要把控制器也
    挂载到总线上,因为它不需要什么驱动,不像I2C控制器也
挂载在总线上并要驱动。
    探测工作主要依靠工作队列,中断,定时器来完成的,不需要深究。

点击(此处)折叠或打开

  1. /**
  2.  *    mmc_add_host - initialise host hardware
  3.  *    @host: mmc host
  4.  *
  5.  *    Register the host with the driver model. The host must be
  6.  *    prepared to start servicing requests before this function
  7.  *    completes.
  8.  */
  9. int mmc_add_host(struct mmc_host *host)
  10. {
  11.     int err;

  12.     WARN_ON((host->caps & MMC_CAP_SDIO_IRQ) &&
  13.         !host->ops->enable_sdio_irq);

  14.     err = device_add(&host->class_dev);
  15.     if (err)
  16.         return err;

  17.     led_trigger_register_simple(dev_name(&host->class_dev), &host->led);

  18. #ifdef CONFIG_DEBUG_FS
  19.     mmc_add_host_debugfs(host);
  20. #endif

  21.     mmc_start_host(host);
  22.     register_pm_notifier(&host->pm_notify);

  23.     return 0;
  24. }


    
阅读(298) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~