mmc子系统

Linux设备模型之device_add

1、使用mmc子系统管理mmc,sd,sdio等设备;
2、mmc子系统涉及到的文件夹在driver/mmc下,包括core,host,card文件夹;
3、几个重要的结构体;

struct mmc_card;
struct mmc_driver;
struct mmc_host;
struct mmc_host_ops;

mmc核心层提供了一些接口

mmc core的入口

主要就是在执行mmc_init的时候,注册了mmc总线和sdio总线;

// drivers/mmc/core/core.c
2754 static int __init mmc_init(void)
2755 {
    
    
2756     int ret;
2758     workqueue = alloc_ordered_workqueue("kmmcd", 0);
2759     if (!workqueue)
2760         return -ENOMEM;
2762     mmc_of_reserve_idx();
2764     ret = mmc_register_bus();
2765     if (ret)
2766         goto destroy_workqueue;
2768     ret = mmc_register_host_class();
2769     if (ret)
2770         goto unregister_bus;
2772     ret = sdio_register_bus();
2773     if (ret)
2774         goto unregister_host_class;
2776     return 0;
2778 unregister_host_class:
2779     mmc_unregister_host_class();
2780 unregister_bus:
2781     mmc_unregister_bus();
2782 destroy_workqueue:
2783     destroy_workqueue(workqueue);
2785     return ret;
2786 }

对mmc host操作的接口

struct mmc_host;
203 struct mmc_host {
    
    
204     struct device       *parent;
205     struct device       class_dev; // 由此看,mmc_host是作为内核中的一个设备;
206     int         index;
207     const struct mmc_host_ops *ops;
// ...
}

// 在drivers/mmc/core/host.c中提供了对mmc_host操作的一组接口
588 void mmc_free_host(struct mmc_host *host)
589 {
    
    

451 struct mmc_host *mmc_alloc_host(int extra, struct device *dev)
452 {
    
    

564 void mmc_remove_host(struct mmc_host *host)
565 {
    
    

530 int mmc_add_host(struct mmc_host *host)
531 {
    
       
///
//

看看
mmc_add_host(struct mmc_host *host)
到底做了什么

530 int mmc_add_host(struct mmc_host *host)
531 {
    
       
532     int err;  
534     WARN_ON((host->caps & MMC_CAP_SDIO_IRQ) &&
535         !host->ops->enable_sdio_irq);
// 加入内核设备管理
537     err = device_add(&host->class_dev);
538     if (err)
539         return err; 
541     led_trigger_register_simple(dev_name(&host->class_dev), &host->led);  
543 #ifdef CONFIG_DEBUG_FS
544     mmc_add_host_debugfs(host);
545 #endif
546     mmc_host_clk_sysfs_init(host);
547     
548     mmc_start_host(host); // 这里将会去调用扫描任务
549     register_pm_notifier(&host->pm_notify);
551     return 0;
552 }   
554 EXPORT_SYMBOL(mmc_add_host);

对mmc card的接口


选择一个host分析

使用imx6的host
首先注册了一个platform设备驱动,这里会跟设备树或平台设备匹配;

// drivers/mmc/host/sdhci-esdhc-imx.c
static struct platform_driver sdhci_esdhc_imx_driver = {
    
    
1387     .driver     = {
    
    
1388         .name   = "sdhci-esdhc-imx",
1389         .owner  = THIS_MODULE,
1390         .of_match_table = imx_esdhc_dt_ids,
1391         .pm = &sdhci_esdhc_pmops,
1392     },
1393     .id_table   = imx_esdhc_devtype,
1394     .probe      = sdhci_esdhc_imx_probe,
1395     .remove     = sdhci_esdhc_imx_remove,
1396 };
1397 
1398 module_platform_driver(sdhci_esdhc_imx_driver);

匹配后会执行probe;

// drivers/mmc/host/sdhci-esdhc-imx.c
1116 static int sdhci_esdhc_imx_probe(struct platform_device *pdev)
1117 {
    
    
1118     const struct of_device_id *of_id =
1119             of_match_device(imx_esdhc_dt_ids, &pdev->dev);
1120     struct sdhci_pltfm_host *pltfm_host;
// ...
1299 
1300     err = sdhci_add_host(host);
1301     if (err)
1302         goto disable_clk;
// ...
}

最后会调用
sdhci_add_host(host)
最后还是调用到了
mmc_add_host(struct mmc_host *host)

// drivers/mmc/host/sdhci.c
// 这个文件是imx6自己给mmc core的接口又封装了一层
2884 int sdhci_add_host(struct sdhci_host *host)
2885 {
    
    
2886     struct mmc_host *mmc;
2887     u32 caps[2] = {
    
    0, 0};
2888     u32 max_current_caps;
2889     unsigned int ocr_avail;
2890     int ret;
2891 
2892     WARN_ON(host == NULL);
2893     if (host == NULL)
2894         return -EINVAL;
2895 
2896     mmc = host->mmc;
// ...
// 这里给mmc一组操作集,是imx实现了的
3036     mmc->ops = &sdhci_ops;
3037     mmc->f_max = host->max_clk;
// ...
3396     mmc_add_host(mmc);
3397 
3398     pr_info("%s: SDHCI controller on %s [%s] using %s\n",
3399         mmc_hostname(mmc), host->hw_name, dev_name(mmc_dev(mmc)),
3400         (host->flags & SDHCI_USE_ADMA) ? "ADMA" :
3401         (host->flags & SDHCI_USE_SDMA) ? "DMA" : "PIO");
3402 
3403     sdhci_enable_card_detection(host);
3404 
3405     return 0;
3406 
3407 #ifdef SDHCI_USE_LEDS_CLASS
3408 reset:
3409     sdhci_reset(host, SDHCI_RESET_ALL);
3410     sdhci_mask_irqs(host, SDHCI_INT_ALL_MASK);
3411     free_irq(host->irq, host);
3412 #endif
3413 untasklet:
3414     tasklet_kill(&host->card_tasklet);
3415     tasklet_kill(&host->finish_tasklet);
3416 
3417     return ret;
3418 }

imx对mmc core接口封装一层,主要是简化了使用者的很多初始化工作,比如下面已经实现了mmc_host的操作集;

// drivers/mmc/host/sdhci.c
2200 static const struct mmc_host_ops sdhci_ops = {
    
    
2201     .request    = sdhci_request,
2202     .post_req   = sdhci_post_req,
2203     .pre_req    = sdhci_pre_req,
2204     .set_ios    = sdhci_set_ios,
2205     .get_cd     = sdhci_get_cd,
2206     .get_ro     = sdhci_get_ro,
2207     .hw_reset   = sdhci_hw_reset,
2208     .enable_sdio_irq = sdhci_enable_sdio_irq,
2209     .start_signal_voltage_switch    = sdhci_start_signal_voltage_switch,
2210     .prepare_hs400_tuning       = sdhci_prepare_hs400_tuning,
2211     .execute_tuning         = sdhci_execute_tuning,
2212     .card_event         = sdhci_card_event,
2213     .card_busy  = sdhci_card_busy,
2214 };

mmc_host的创建,是probe
sdhci_esdhc_imx_probe(struct platform_device *pdev)
调用
host = sdhci_pltfm_init(pdev, &sdhci_esdhc_imx_pdata, 0);
创建的

117 struct sdhci_host *sdhci_pltfm_init(struct platform_device *pdev,
118                     const struct sdhci_pltfm_data *pdata,
119                     size_t priv_size)
120 {
    
    
121     struct sdhci_host *host;
// ...
134 
135     /* Some PCI-based MFD need the parent here */
136     if (pdev->dev.parent != &platform_bus && !np)
137         host = sdhci_alloc_host(pdev->dev.parent,
138             sizeof(struct sdhci_pltfm_host) + priv_size);
139     else
140         host = sdhci_alloc_host(&pdev->dev,
141             sizeof(struct sdhci_pltfm_host) + priv_size);
// ...
192 }

sdhci_alloc_host()
也是imx对
mmc_alloc_host()
做了一层封装;

// drivers/mmc/host/sdhci.c
2864 struct sdhci_host *sdhci_alloc_host(struct device *dev,
2865     size_t priv_size)
2866 {
    
      
2867     struct mmc_host *mmc;
2868     struct sdhci_host *host; 
2870     WARN_ON(dev == NULL);
// 这里调用了mmc core的接口
2872     mmc = mmc_alloc_host(sizeof(struct sdhci_host) + priv_size, dev);
2873     if (!mmc)
2874         return ERR_PTR(-ENOMEM);
2876     host = mmc_priv(mmc);
2877     host->mmc = mmc;  
2879     return host;
2880 }

我们现在知道了,mmc host层主要就是调用mmc core的接口,注册了一个mmc host,以及实现了mmc host的操作集mmc host opts,这里暂时先不管这个mmc host怎么被使用到,后面再分析

还有一个比较重要的点就是
mmc_detect_change函数
最后会调用到
mmc_rescan函数,
mmc_rescan函数会去扫描有没有有效的sd卡插入;

除了在probe的时候会最终调用mmc_rescan;
在probe的时候注册了一个中断,
这个中断会去调用
mmc_detect_change
并最终调用了
mmc_rescan

mmc_rescan是在分配mmc_host的时候给初始化的

// drivers/mmc/core/host.c
451 struct mmc_host *mmc_alloc_host(int extra, struct device *dev)
452 {
    
    
453     int err;
454     struct mmc_host *host;
455     int alias_id, min_idx, max_idx;
// ...
495     spin_lock_init(&host->lock);
496     init_waitqueue_head(&host->wq);
// 初始化了一个工作队列
497     INIT_DELAYED_WORK(&host->detect, mmc_rescan);
498 #ifdef CONFIG_PM
499     host->pm_notify.notifier_call = mmc_pm_notify;
500 #endif
// ...
}

所以probe会检查一下有效sd卡,后期sd卡插入,产生中断会去检查一下有效sd卡


接下来看下mmc card是怎么注册到内核的

上面说到,mmc_add_host()注册mmc_host的时候会去调用
mmc_detect_change()
最终会去调用
mmc_rescan()

先看看mmc_rescan()做了什么

在这里插入代码片

在扫描到sd卡后,
会调用
mmc_add_card
去添加一个 mmc_card
struct mmc_card

这个mmc_card也是注册在mmc_bus上的一个设备
mmc_card添加后符合总线驱动模型,会去扫描mmc_driver;


最后看下mmc_driver是怎么注册到总线上的

// drivers/mmc/card/block.c
2518 static struct mmc_driver mmc_driver = {
    
    
2519     .drv        = {
    
    
2520         .name   = "mmcblk",
2521     },
2522     .probe      = mmc_blk_probe,
2523     .remove     = mmc_blk_remove,
2524     .suspend    = mmc_blk_suspend,
2525     .resume     = mmc_blk_resume,
2526     .shutdown   = mmc_blk_shutdown,
2527 };
2529 static int __init mmc_blk_init(void)
2530 {
    
    
2531     int res;
2533     if (perdev_minors != CONFIG_MMC_BLOCK_MINORS)
2534         pr_info("mmcblk: using %d minors per device\n", perdev_minors);
2536     max_devices = 256 / perdev_minors;
2537 
2538     res = register_blkdev(MMC_BLOCK_MAJOR, "mmc");
2539     if (res)
2540         goto out;
2542     res = mmc_register_driver(&mmc_driver);
2543     if (res)
2544         goto out2;
2546     return 0;
2547  out2:
2548     unregister_blkdev(MMC_BLOCK_MAJOR, "mmc");
2549  out:
2550     return res;
2551 }
2553 static void __exit mmc_blk_exit(void)
2554 {
    
    
2555     mmc_unregister_driver(&mmc_driver);
2556     unregister_blkdev(MMC_BLOCK_MAJOR, "mmc");
2557 }
2559 module_init(mmc_blk_init);
2560 module_exit(mmc_blk_exit);

小结

1、mmc子系统,分成3部分,
2、core提供了一些api供其他2部分调用;
3、host,负责初始化mmc控制器(cpu硬件),注册mmc_host设备;
4、host,负责扫描合法sd卡,并在成功扫描后,注册一个mmc_card到mmc_bus总线;
5、card,负责注册mmc_driver驱动到mmc_bus总线,匹配设备后创建设备节点供应用层调用;


参考:
linux mmc 框架源码分析
emmc框架分析
Linux SD/MMC/SDIO驱动分析
eMMC驱动分析

Guess you like

Origin blog.csdn.net/GeiGe123/article/details/116955848