基于S3C2440的嵌入式Linux驱动——MMC/SD子系统解读(二)

发布者:自由思考最新更新时间:2017-09-23 来源: eefocus关键字:S3C2440  Linux驱动  MMC  SD子系统 手机看文章 扫描二维码
随时随地手机看文章

本文的内容基于如下硬件和软件平台:

目标平台:TQ2440

CPU:s3c2440

内核版本:3.12.5

基于SD规范4.10,即《SD Specifications Part 1 Physical Layer Simplified Specification Version 4.10》。


在阅读MMC子系统时,一个问题随之就会产生:当我们插入一张SD卡时,系统是如何识别到这张SD卡并将它注册进系统的呢?

这一过程,源于MMC控制器驱动的不懈努力。。。。。。下面,我们从控制器驱动开始,来深入挖掘这一过程。

1. MMC控制器驱动

1.1 MMC控制器入口函数及probe方法

本文以三星的s3c2440上的MMC控制器驱动为例,来进行简要的说明。

从MMC控制器驱动的入口函数开始,如下:

下列代码位于:linux/drivers/mmc/host/s3cmci.c


  1. static struct platform_driver s3cmci_driver = {  

  2.     .driver = {  

  3.         .name   = "s3c-sdi",  

  4.         .owner  = THIS_MODULE,  

  5.         .pm = s3cmci_pm_ops,  

  6.     },  

  7.     .id_table   = s3cmci_driver_ids,  

  8.     .probe      = s3cmci_probe,  

  9.     .remove     = s3cmci_remove,  

  10.     .shutdown   = s3cmci_shutdown,  

  11. };  

  12.   

  13. module_platform_driver(s3cmci_driver);  


这里直接调用了platform的驱动注册函数来注册一个名为s3c-sdi的驱动,该驱动将绑定mmc主控制器设备。


为了让该驱动成功绑定MMC主控制器设备,需要先进行移植操作,具体可见:S3C2440 Linux驱动移植——SD卡驱动

绑定成功后,立即会调用probe方法,也就是s3cmci_probe函数,我们来看下:


  1. static int s3cmci_probe(struct platform_device *pdev)  

  2. {  

  3.     struct s3cmci_host *host;  

  4.     struct mmc_host *mmc;  

  5.     int ret;  

  6.     int is2440;  

  7.     int i;  

  8.   

  9.     /* */  

  10.     is2440 = platform_get_device_id(pdev)->driver_data;  

  11.   

  12.     /* 分配struct mmc_host和struct s3cmci_host */  

  13.     mmc = mmc_alloc_host(sizeof(struct s3cmci_host), &pdev->dev);  

  14.     if (!mmc) {  

  15.         ret = -ENOMEM;  

  16.         goto probe_out;  

  17.     }  

  18.   

  19.     /* 申请IO管脚*/  

  20.     for (i = S3C2410_GPE(5); i <= S3C2410_GPE(10); i++) {  

  21.         ret = gpio_request(i, dev_name(&pdev->dev));  

  22.         if (ret) {  

  23.             dev_err(&pdev->dev, "failed to get gpio %d\n", i);  

  24.   

  25.             for (i--; i >= S3C2410_GPE(5); i--)  

  26.                 gpio_free(i);  

  27.   

  28.             goto probe_free_host;  

  29.         }  

  30.     }  

  31.   

  32.     host = mmc_priv(mmc);   /* 获取struct s3cmci_host指针*/  

  33.     host->mmc    = mmc;  /* 保存mmc指针*/  

  34.     host->pdev   = pdev; /* 保存平台设备指针*/  

  35.     host->is2440 = is2440; /* 是否为2440*/  

  36.   

  37.     host->pdata = pdev->dev.platform_data; /* 保存板级设备信息*/  

  38.     if (!host->pdata) {  

  39.         /* 如果没有板级设备信息,给出默认的*/  

  40.         pdev->dev.platform_data = &s3cmci_def_pdata;  

  41.         host->pdata = &s3cmci_def_pdata;  

  42.     }  

  43.   

  44.     /* 初始化自旋锁*/  

  45.     spin_lock_init(&host->complete_lock);  

  46.     /* 初始化1个tasklet,执行pio_tasklet*/  

  47.     tasklet_init(&host->pio_tasklet, pio_tasklet, (unsigned long) host);  

  48.   

  49.     if (is2440) {  

  50.         host->sdiimsk    = S3C2440_SDIIMSK;  

  51.         host->sdidata    = S3C2440_SDIDATA;  

  52.         host->clk_div    = 1;  

  53.     } else {  

  54.         host->sdiimsk    = S3C2410_SDIIMSK;  

  55.         host->sdidata    = S3C2410_SDIDATA;  

  56.         host->clk_div    = 2;  

  57.     }  

  58.   

  59.     host->complete_what  = COMPLETION_NONE;  

  60.     host->pio_active     = XFER_NONE;  

  61.   

  62. /* 板级数据可以决定是否使用DMA,也可以通过配置来决定*/  

  63. #ifdef CONFIG_MMC_S3C_PIODMA  

  64.     host->dodma      = host->pdata->use_dma;    

  65. #endif  

  66.   

  67.     /* 获取寄存器资源*/  

  68.     host->mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);  

  69.     if (!host->mem) {  

  70.         dev_err(&pdev->dev,  

  71.             "failed to get io memory region resource.\n");  

  72.   

  73.         ret = -ENOENT;  

  74.         goto probe_free_gpio;  

  75.     }  

  76.     /* 申请IO内存空间*/  

  77.     host->mem = request_mem_region(host->mem->start,  

  78.                        resource_size(host->mem), pdev->name);  

  79.   

  80.     if (!host->mem) {  

  81.         dev_err(&pdev->dev, "failed to request io memory region.\n");  

  82.         ret = -ENOENT;  

  83.         goto probe_free_gpio;  

  84.     }  

  85.   

  86.     /* 映射寄存器*/  

  87.     host->base = ioremap(host->mem->start, resource_size(host->mem));  

  88.     if (!host->base) {  

  89.         dev_err(&pdev->dev, "failed to ioremap() io memory region.\n");  

  90.         ret = -EINVAL;  

  91.         goto probe_free_mem_region;  

  92.     }  

  93.   

  94.     /*获取IRQ */  

  95.     host->irq = platform_get_irq(pdev, 0);  

  96.     if (host->irq == 0) {  

  97.         dev_err(&pdev->dev, "failed to get interrupt resource.\n");  

  98.         ret = -EINVAL;  

  99.         goto probe_iounmap;  

  100.     }  

  101.   

  102.     /* 注册IRQ*/  

  103.     if (request_irq(host->irq, s3cmci_irq, 0, DRIVER_NAME, host)) {  

  104.         dev_err(&pdev->dev, "failed to request mci interrupt.\n");  

  105.         ret = -ENOENT;  

  106.         goto probe_iounmap;  

  107.     }  

  108.   

  109.     /* We get spurious interrupts even when we have set the IMSK 

  110.      * register to ignore everything, so use disable_irq() to make 

  111.      * ensure we don't lock the system with un-serviceable requests. */  

  112.   

  113.     /* 禁止中断并设置中断状态*/  

  114.     disable_irq(host->irq);  

  115.     host->irq_state = false;  

  116.   

  117.     /* 使用detect功能,则申请gpio */  

  118.     if (!host->pdata->no_detect) {  

  119.         ret = gpio_request(host->pdata->gpio_detect, "s3cmci detect");  

  120.         if (ret) {  

  121.             dev_err(&pdev->dev, "failed to get detect gpio\n");  

  122.             goto probe_free_irq;  

  123.         }  

  124.         /* 根据gpio获取中断号*/  

  125.         host->irq_cd = gpio_to_irq(host->pdata->gpio_detect);  

  126.   

  127.         /* 中断号有效则注册该中断*/  

  128.         if (host->irq_cd >= 0) {  

  129.             if (request_irq(host->irq_cd, s3cmci_irq_cd,  

  130.                     IRQF_TRIGGER_RISING |  

  131.                     IRQF_TRIGGER_FALLING,  

  132.                     DRIVER_NAME, host)) {  

  133.                 dev_err(&pdev->dev,  

  134.                     "can't get card detect irq.\n");  

  135.                 ret = -ENOENT;  

  136.                 goto probe_free_gpio_cd;  

  137.             }  

  138.         } else {  

  139.             dev_warn(&pdev->dev,  

  140.                  "host detect has no irq available\n");  

  141.             gpio_direction_input(host->pdata->gpio_detect);  

  142.         }  

  143.     } else  

  144.         host->irq_cd = -1;  

  145.   

  146.       

  147.     /* 使用wprotect功能,则申请gpio */  

  148.     if (!host->pdata->no_wprotect) {  

  149.         ret = gpio_request(host->pdata->gpio_wprotect, "s3cmci wp");  

  150.         if (ret) {  

  151.             dev_err(&pdev->dev, "failed to get writeprotect\n");  

  152.             goto probe_free_irq_cd;  

  153.         }  

  154.           

  155.         gpio_direction_input(host->pdata->gpio_wprotect);  

  156.     }  

  157.   

  158.     /* depending on the dma state, get a dma channel to use. */  

  159.   

  160.     /* 如果使用DMA则申请相应的物理DMA通道*/  

  161.     if (s3cmci_host_usedma(host)) {  

  162.         /* 返回值为通道号*/  

  163.         host->dma = s3c2410_dma_request(DMACH_SDI, &s3cmci_dma_client,  

  164.                         host);  

  165.         if (host->dma < 0) {  

  166.             /* DMA申请失败,尝试使用IO操作*/  

  167.             dev_err(&pdev->dev, "cannot get DMA channel.\n");  

  168.             if (!s3cmci_host_canpio()) {  

  169.                 ret = -EBUSY;  

  170.                 goto probe_free_gpio_wp;  

  171.             } else {  

  172.                 dev_warn(&pdev->dev, "falling back to PIO.\n");  

  173.                 host->dodma = 0; /* 表示不使用DMA?/ 

  174.             } 

  175.         } 

  176.     } 

  177.     /* 获取clk*/  

  178.     host->clk = clk_get(&pdev->dev, "sdi");  

  179.     if (IS_ERR(host->clk)) {  

  180.         dev_err(&pdev->dev, "failed to find clock source.\n");  

  181.         ret = PTR_ERR(host->clk);  

  182.         host->clk = NULL;  

  183.         goto probe_free_dma;  

  184.     }  

  185.   

  186.     /*使能clk*/  

  187.     ret = clk_enable(host->clk);  

  188.     if (ret) {  

  189.         dev_err(&pdev->dev, "failed to enable clock source.\n");  

  190.         goto clk_free;  

  191.     }  

  192.   

  193.     host->clk_rate = clk_get_rate(host->clk);  /* 保存时钟频率*/  

  194.   

  195.     /*开始初始化mmc当中的字段*/  

  196.     mmc->ops     = &s3cmci_ops;  /* 给出控制器operation函数集*/  

  197.     mmc->ocr_avail   = MMC_VDD_32_33 | MMC_VDD_33_34;  

  198. #ifdef CONFIG_MMC_S3C_HW_SDIO_IRQ  

  199.     mmc->caps    = MMC_CAP_4_BIT_DATA | MMC_CAP_SDIO_IRQ;  

  200. #else  

  201.     mmc->caps    = MMC_CAP_4_BIT_DATA;  

  202. #endif  

  203.     /* 计算最大和最小的工作频率*/  

  204.     mmc->f_min   = host->clk_rate / (host->clk_div * 256);  

  205.     mmc->f_max   = host->clk_rate / host->clk_div;  

  206.   

  207.     if (host->pdata->ocr_avail)  

  208.         mmc->ocr_avail = host->pdata->ocr_avail;  

  209.   

  210.     mmc->max_blk_count   = 4095; /* 一个请求的最大block数*/  

  211.     mmc->max_blk_size    = 4095; /* block的最大容量*/  

  212.     mmc->max_req_size    = 4095 * 512;  /* 一个请求的最大字节数*/  

  213.     mmc->max_seg_size    = mmc->max_req_size;  

  214.   

  215.     mmc->max_segs        = 128;  

  216.   

  217.     dbg(host, dbg_debug,  

  218.         "probe: mode:%s mapped mci_base:%p irq:%u irq_cd:%u dma:%u.\n",  

  219.         (host->is2440?"2440":""),  

  220.         host->base, host->irq, host->irq_cd, host->dma);  

  221.   

  222.     ret = s3cmci_cpufreq_register(host);  

  223.     if (ret) {  

  224.         dev_err(&pdev->dev, "failed to register cpufreq\n");  

  225.         goto free_dmabuf;  

  226.     }  

  227.   

  228.     /* 注册该mmc 控制器到子系统中*/  

  229.     ret = mmc_add_host(mmc);  

  230.     if (ret) {  

  231.         dev_err(&pdev->dev, "failed to add mmc host.\n");  

  232.         goto free_cpufreq;  

  233.     }  

  234.   

  235.     s3cmci_debugfs_attach(host);  

  236.   

  237.     /* 设置驱动数据*/  

  238.     platform_set_drvdata(pdev, mmc);  

  239.     dev_info(&pdev->dev, "%s - using %s, %s SDIO IRQ\n", mmc_hostname(mmc),  

  240.          s3cmci_host_usedma(host) ? "dma" : "pio",  

  241.          mmc->caps & MMC_CAP_SDIO_IRQ ? "hw" : "sw");  

  242.   

  243.     return 0;  

  244.   

  245.  free_cpufreq:  

  246.     s3cmci_cpufreq_deregister(host);  

  247.   

  248.  free_dmabuf:  

  249.     clk_disable(host->clk);  

  250.   

  251.  clk_free:  

  252.     clk_put(host->clk);  

  253.   

  254.  probe_free_dma:  

  255.     if (s3cmci_host_usedma(host))  

  256.         s3c2410_dma_free(host->dma, &s3cmci_dma_client);  

  257.   

  258.  probe_free_gpio_wp:  

  259.     if (!host->pdata->no_wprotect)  

  260.         gpio_free(host->pdata->gpio_wprotect);  

  261.   

  262.  probe_free_gpio_cd:  

  263.     if (!host->pdata->no_detect)  

  264.         gpio_free(host->pdata->gpio_detect);  

  265.   

  266.  probe_free_irq_cd:  

  267.     if (host->irq_cd >= 0)  

  268.         free_irq(host->irq_cd, host);  

  269.   

  270.  probe_free_irq:  

  271.     free_irq(host->irq, host);  

  272.   

  273.  probe_iounmap:  

  274.     iounmap(host->base);  

  275.   

  276.  probe_free_mem_region:  

  277.     release_mem_region(host->mem->start, resource_size(host->mem));  

  278.   

  279.  probe_free_gpio:  

  280.     for (i = S3C2410_GPE(5); i <= S3C2410_GPE(10); i++)  

  281.         gpio_free(i);  

  282.   

  283.  probe_free_host:  

  284.     mmc_free_host(mmc);  

  285.   

  286.  probe_out:  

  287.     return ret;  

  288. }  


  这个函数想当的长,差不多300行,我们来简要分析下:


第一步,该函数首先调用mmc_alloc_host分配了struct mmc_host和truct s3cmci_host和两个结构体的内存空间,其中前者包含后者,而后者有一个指针指向前者。

该函数的详细的实现如下:


  1. /** 

  2.  *  mmc_alloc_host - initialise the per-host structure. 

  3.  *  @extra: sizeof private data structure 

  4.  *  @dev: pointer to host device model structure 

  5.  * 

  6.  *  Initialise the per-host structure. 

  7.  */  

  8. struct mmc_host *mmc_alloc_host(int extra, struct device *dev)  

  9. {  

  10.     int err;  

  11.     struct mmc_host *host;  

  12.   

  13.     host = kzalloc(sizeof(struct mmc_host) + extra, GFP_KERNEL);  

  14.     if (!host)  

  15.         return NULL;  

  16.   

  17.     /* scanning will be enabled when we're ready */  

  18.     host->rescan_disable = 1;  

  19.     idr_preload(GFP_KERNEL);  

  20.     /* 获取一个idr,通过自旋锁进行互斥保护*/  

  21.     spin_lock(&mmc_host_lock);  

  22.     err = idr_alloc(&mmc_host_idr, host, 0, 0, GFP_NOWAIT);  

  23.     if (err >= 0)  

  24.         host->index = err;  

  25.     spin_unlock(&mmc_host_lock);  

  26.     idr_preload_end();  

  27.     if (err < 0)  

  28.         goto free;  

  29.   

  30.     /* 设置设备名*/  

  31.     dev_set_name(&host->class_dev, "mmc%d", host->index);  

  32.   

  33.     host->parent = dev;  

  34.     host->class_dev.parent = dev; /* 设置父设备*/  

  35.     host->class_dev.class = &mmc_host_class;  /* 设置设备所属的类*/  

  36.     device_initialize(&host->class_dev);  

  37.   

  38.     mmc_host_clk_init(host);  

  39.   

  40.     mutex_init(&host->slot.lock);  

  41.     host->slot.cd_irq = -EINVAL;  

  42.   

  43.     spin_lock_init(&host->lock);  

  44.     init_waitqueue_head(&host->wq);  

  45.     INIT_DELAYED_WORK(&host->detect, mmc_rescan);   /* 创建延时工作队列*/  

  46. #ifdef CONFIG_PM  

  47.     host->pm_notify.notifier_call = mmc_pm_notify; /* 通知链回调函数*/  

  48. #endif  

  49.   

  50.     /* 

  51.      * By default, hosts do not support SGIO or large requests. 

  52.      * They have to set these according to their abilities. 

  53.      */  

  54.     host->max_segs = 1;  

  55.     host->max_seg_size = PAGE_CACHE_SIZE;  

  56.   

  57.     host->max_req_size = PAGE_CACHE_SIZE;  

  58.     host->max_blk_size = 512;  

  59.     host->max_blk_count = PAGE_CACHE_SIZE / 512;  

  60.   

  61.     return host;  

  62.   

  63. free:  

  64.     kfree(host);  

  65.     return NULL;  

  66. }  

  67.   

  68. EXPORT_SYMBOL(mmc_alloc_host);  

   其大致过程如下:分配相应的结构体,设置标志位rescan_disable为1,表示禁止扫描SD卡。


                         随后利用idr分配了一个编号给该MMC控制器,并初始化了MMC控制器设备对象(device),

                         初始化了控制器时钟信息。

                         很重要的一步,初始化了一个工作(host->detect)为mmc_rescan,该函数将负责执行对SD卡的扫描,该函数将在后面描述。

                         最后,初始化了MMC控制器的访问块大小的能力。

第二步,根据控制器所使用的引脚,向gpio子系统申请该引脚。

第三步,获取板级设备信息(pdev->dev.platform_data),并初始化struct s3cmci_host中的一些字段。

第四步,控制器驱动决定时候需要使用DMA进行传输(host->dodma)。

第五步,和其他驱动一样,获得寄存器空间,申请IO内存,并完成映射工作。

第六步,和其他驱动一样,获取IRQ号,并注册该irq,中断服务程序(ISR)为s3cmci_irq函数,并且关闭中断。

第七步,根据板级设备信息,判断是否使用探测(detect)功能,如果使用则向gpio子系统申请该引脚,并为该gpio注册中断服务程序。

              中断服务程序为s3cmci_irq_cd函数,上下沿有效,该函数就是用来判断SD卡是否插入卡槽中的。。

第八步,根据板级设备信息,判断是否使用写保护(no_wprotect)功能,如果使用则向gpio子系统申请该引脚。

第九步,如果使用DMA传输,则对DMA进行相关的初始化工作。

第十步,获取clk,并使能,然后获取时钟速率。

第十一步,设置控制器的访问函数集为s3cmci_ops,该结构体包括了MMC控制器行为的抽象,非常重要,我们来看下:


  1. static struct mmc_host_ops s3cmci_ops = {  

  2.     .request    = s3cmci_request,  

  3.     .set_ios    = s3cmci_set_ios,  

  4.     .get_ro     = s3cmci_get_ro,        /* 判断是否只读*/  

  5.     .get_cd     = s3cmci_card_present,    /* 判断card是否存在*/  

  6.     .enable_sdio_irq = s3cmci_enable_sdio_irq,  

  7. };  

struct mmc_host_ops其中包含了很多MMC控制器行为的抽象,S3C2440的MMC控制器驱动只使用了5个,功能如下:

    第一个是请求函数,用于向SD卡发送命令,并获取应答。

    第二个用于设置MMC控制器参数。

    第三个用于判断SD卡是否为只读。

    第四个用于判断SD卡是否在卡槽内。

    第五个用户使能sdio中断。

接下来开始初始化struct mmc_host 结构体里的字段,这些字段的具体含义就不细说了。

第十二步,调用s3cmci_cpufreq_register注册一个通知链,有关通知链的东西就不在这里赘述了。

第十三步,调用mmc_add_host注册MMC控制器,该函数将在1.2小结叙说。

第十三步,调用s3cmci_debugfs_attach向debuf文件系统注册有关访问接口,debugfs有关的我们就略过了。

第十四步,保存mmc至驱动的私有平台数据中(dpev->dev->p->driver_data)。


作为MMC控制器的probe方法,自然而然的需要注册MMC控制器,从上面我们可以看到在进行大量的初始化工作后,最终在第十三步注册该控制器驱动。

下以小结我们来看看这个函数干了点什么。

1.2 函数mmc_add_host的使命

下列代码位于:linux/drivers/mmc/core/host.c


  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.   

  13.     WARN_ON((host->caps & MMC_CAP_SDIO_IRQ) &&  

  14.         !host->ops->enable_sdio_irq);  

  15.   

  16.     /* 注册控制器设备实例*/  

  17.     err = device_add(&host->class_dev);  

  18.     if (err)  

  19.         return err;  

  20.   

  21.     /* 注册一个led trigger*/  

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

  23.   

  24. #ifdef CONFIG_DEBUG_FS  

  25.     mmc_add_host_debugfs(host);  

  26. #endif  

  27.     mmc_host_clk_sysfs_init(host);  

  28.   

  29.   

  30.     mmc_start_host(host);  

  31.       

  32.     /* 注册一个通知链*/  

  33.     register_pm_notifier(&host->pm_notify);  

  34.   

  35.     return 0;  

  36. }  

  37.   

  38. EXPORT_SYMBOL(mmc_add_host);  


该函数首先调用device_add注册了一个设备实例,随后注册了一个led trigger。

并调用mmc_host_clk_sysfs_init,后者利用sysfs向用户提供了门控相关的信息,

接着调用mmc_start_host来启动MMC控制器的时钟,并且判断SD卡是否已在卡槽中。

最后,调用register_pm_notifier向注册了一个用于电源管理的通知链。

很明显,这里调用的5个函数,我们需要关心的是mmc_start_host函数。


  1. void mmc_start_host(struct mmc_host *host)  

  2. {  

  3.     host->f_init = max(freqs[0], host->f_min);  

  4.     host->rescan_disable = 0;  

  5.     /* 必须重新scan才能上电,则关闭mmc控制器*/  

  6.     if (host->caps2 & MMC_CAP2_NO_PRESCAN_POWERUP)  

  7.         mmc_power_off(host);    /* 关闭MMC控制器*/  

  8.     else  

  9.         mmc_power_up(host); /* 打卡MMC控制器*/  

  10.     mmc_detect_change(host, 0);  

  11. }  

MMC_CAP2_NO_PRESCAN_POWERUP 表示需要在上电前扫面SD卡,

如果定义了该宏,则需要关闭MMC控制器的电压,否则打开电源,该宏默认是不定义的。

mmc_power_up函数还是比较复杂的,不过它不是我们关心的重点,简单说句,该函数最后会调用在1.1小结中MMC控制器的抽象行为函数中的

set_ios来使能MMC控制器的电源。

该函数最后调用mmc_detect_change来探测SD卡是否存在。


  1. /** 

  2.  *    mmc_detect_change - process change of state on a MMC socket 

  3.  *    @host: host which changed state. 

  4.  *    @delay: optional delay to wait before detection (jiffies) 

  5.  * 

  6.  *    MMC drivers should call this when they detect a card has been 

  7.  *    inserted or removed. The MMC layer will confirm that any 

  8.  *    present card is still functional, and initialize any newly 

  9.  *    inserted. 

  10.  */  

  11. void mmc_detect_change(struct mmc_host *host, unsigned long delay)  

  12. {  

  13. #ifdef CONFIG_MMC_DEBUG  

  14.     unsigned long flags;  

  15.     spin_lock_irqsave(&host->lock, flags);  

  16.     WARN_ON(host->removed);  

  17.     spin_unlock_irqrestore(&host->lock, flags);  

  18. #endif  

  19.     host->detect_change = 1;  

  20.     mmc_schedule_delayed_work(&host->detect, delay);  

  21. }  


最开始的宏中的代码可以直接略过了。
将标志位detect_change置1后,函数紧接着调用了mmc_schedule_delayed_work函数,该函数如下:


  1. /* 

  2.  * Internal function. Schedule delayed work in the MMC work queue. 

  3.  */  

  4. static int mmc_schedule_delayed_work(struct delayed_work *work,  

  5.                      unsigned long delay)  

  6. {  

  7.     return queue_delayed_work(workqueue, work, delay);  

  8. }  


该函数简单调用了queue_delayed_work来添加一个工作到工作队列workqueue中。


基于S3C2440的嵌入式Linux驱动——MMC/SD子系统解读(一)中的第三章,MMC子系统在初始化时就注册了一个工作队列,并保存到全局变量workqueue中,

同时函数将detect工作添加到了该工作队列中,而detect工作的执行函数即为在1.1中分析时说道的mmc_rescan函数。

因此该函数的作用就是将detec表示的工作函数mmc_rescan添加到了工作队列中。

创建完工作队列后,mmc_start_host函数的使命也算结束了,它的存在极其短暂,但是它却调用了一个非常关键的函数mmc_detect_change,

后者创建的工作函数mmc_rescan将会扫描是否有SD卡插入。


2. SD卡扫描函数mmc_rescan

整个mmc_rescan函数执行分为两个部分。

第一部分,探测SD卡是否存在。

第二部分,按照SD规范,初始化SD卡。

2.1 detect SD卡

下列代码位于:linux/drivers/mmc/core/core.c


  1. void mmc_rescan(struct work_struct *work)  

  2. {  

  3.     struct mmc_host *host =  

  4.         container_of(work, struct mmc_host, detect.work);  

  5.     int i;  

  6.   

  7.     /* host禁止再次scan,则直接返回*/  

  8.     if (host->rescan_disable)  

  9.         return;  

  10.   

  11.     /* If there is a non-removable card registered, only scan once */  

  12.     /* 是不可插拔的sd卡,则只scan一次*/  

  13.     if ((host->caps & MMC_CAP_NONREMOVABLE) && host->rescan_entered)  

  14.         return;  

  15.     host->rescan_entered = 1;  /* 表示至少scan一次了*/  

  16.   

  17.     mmc_bus_get(host);  /* 增加总线引用技术*/  

  18.   

  19.     /* 

  20.      * if there is a _removable_ card registered, check whether it is 

  21.      * still present 

  22.      */  

  23.      /* 如果总线提供detect方法则调用*/  

  24.     if (host->bus_ops && host->bus_ops->detect && !host->bus_dead  

  25.         && !(host->caps & MMC_CAP_NONREMOVABLE))  

  26.         host->bus_ops->detect(host);    

  27.   

  28.     host->detect_change = 0;  

  29.   

  30.     /* 

  31.      * Let mmc_bus_put() free the bus/bus_ops if we've found that 

  32.      * the card is no longer present. 

  33.      */  

  34.     mmc_bus_put(host);  /* 减少总线引用技术*/  

  35.     mmc_bus_get(host);  

  36.   

  37.     /* if there still is a card present, stop here */  

  38.     /* 有card存在,无需继续了*/  

  39.     if (host->bus_ops != NULL) {  

  40.         mmc_bus_put(host);  

  41.         goto out;  

  42.     }  

  43.   

  44.     /* 

  45.      * Only we can add a new handler, so it's safe to 

  46.      * release the lock here. 

  47.      */  

  48.     mmc_bus_put(host);  

  49.   

  50.     /* 调用get_cd方法,判断card是否存在*/  

  51.     if (host->ops->get_cd && host->ops->get_cd(host) == 0) {  

  52.         mmc_claim_host(host);  

  53.         mmc_power_off(host);  

  54.         mmc_release_host(host);  

  55.         goto out;  

  56.     }  

  57.   

  58.       

  59.     mmc_claim_host(host);  

  60.     /**/  

  61.     for (i = 0; i < ARRAY_SIZE(freqs); i++) {  

  62.         if (!mmc_rescan_try_freq(host, max(freqs[i], host->f_min)))  

  63.             break;  

  64.         if (freqs[i] <= host->f_min)  

  65.             break;  

  66.     }  

  67.     mmc_release_host(host);  

  68.   

  69.  out:  

  70.     /* 如果需要再次扫描*/  

  71.     if (host->caps & MMC_CAP_NEEDS_POLL)  

  72.         mmc_schedule_delayed_work(&host->detect, HZ);  

  73. }  


该函数首先判断rescan_disable是否为真,如果为真则不用扫描SD卡,直接推出了。


在mmc_start_host时,该变量被置为0,因此这里函数不返回,程序继续往下走。



未完,待续。。。。。。。。。。。


关键字:S3C2440  Linux驱动  MMC  SD子系统 引用地址:基于S3C2440的嵌入式Linux驱动——MMC/SD子系统解读(二)

上一篇:STM32外设驱动篇——AT24C02(I2C接口)
下一篇:基于S3C2440的嵌入式Linux驱动——MMC/SD子系统解读(一)

推荐阅读最新更新时间:2024-03-16 15:38

MMC卡的文件系统及其实现方法
多媒体卡MMC(MultiMedia Card)是由美国SanDisk公司和德国Simens公司于1997年共同开发推出的一种多功能存卡。内置控制电路,可以使用在手机、数码相机、MP3、PDA等多种数字设备上,可反复记录30万次。现在市场上的主流容量有128 MB~2 GB。    文中首先介绍单片机对SPI协议下的MMC卡的底层读写操作,然后分析MMC卡文件系统的结构,最后详细说明MMC卡文件的创建、读写、删除等操作。该方法可应用到与Windows有交互的嵌入式系统中,便于文件的统一管理。    1 单片机与MMC卡的接口    1.1 单片机与MMC卡的接口电路 接口电路采用的是Philips公司的增强型LPC93x系列
[单片机]
<font color='red'>MMC</font>卡的文件系统及其实现方法
基于S3C2440处理器和WinCE的智能车载仪表设计
  随着高性能电子显示技术的发展,汽车仪表电子化的程度越来越高。国内外已开发出了多功能全电子显示仪表、平视显示仪表、汽车导航系统、行车记录仪等高技术产品。未来,车用电子化嵌入式仪表具有以下优点:提供大量复杂的信息,使汽车的电子控制程度越来越高;满足小型、轻量化的要求,使有限的驾驶空间更人性化;高精度和高可靠性实现汽车仪表的电子化,降低了故障的发生率;设有在线故障诊断系统,一旦汽车发生故障,可以找到故障来源,方便维修;外形设计自由度高,汽车仪表盘造型美观。基于以上优点,汽车会越来越多地采用各种用途的电子化仪表。造型新颖、功能强大的嵌入式电子化仪表将是今后车用仪表的发展趋势和潮流。    1 智能车载仪表系统结构   本智能车载仪
[单片机]
基于<font color='red'>S3C2440</font>处理器和WinCE的智能车载仪表设计
S3C2440裸机------触摸屏_利用ADC读取触摸屏坐标
前面我们在按下触摸屏时只是打印调试信息,现在我们再按下的中断处理函数里面增加ADC的配置代码,当我们按下后利用ADC读取触摸屏的坐标。 #include ../s3c2440_soc.h #define ADC_INT_BIT (10) #define TC_INT_BIT (9) #define INT_ADC_TC (31) /* ADCTSC's bits */ #define WAIT_PEN_DOWN (0 8) #define WAIT_PEN_UP (1 8) #define YM_ENABLE (1 7) #define YM_DISABLE (0 7) #de
[单片机]
s3c2440——按键中断
s3c2440的异常向量表: IRQ中断地址是0x18.所以,根据之前的异常处理方式,我们编写启动文件: 为什么需要lr减4,可以参考这篇文章: http://blog.csdn.net/zzsfqiuyigui/article/details/23334177 这是为了保证,当cpu正在执行某条指令时被中断打断,中断返回的时候,要继续执行这条被打断的指令,如果不减去4,cpu处理完中断之后,将会在被打断执行的这条指令的下一条指令开始执行(因为lr_irq保存的是下一条指令执行的地址),就会丢失掉这个被打断指令的执行,所以,保存中断处理完毕的返回地址很重要。 void handle_irq_c(void) {
[单片机]
<font color='red'>s3c2440</font>——按键中断
基于S3C2440处理器SPI移植全过程
环境 硬件:S3C2440(ARM920T) 嵌入式操作系统:Linux2.6.24内核 文件系统:Yaffs2文件系统 服务器:SuSe10.0 Linux服务器 第一步:内核配置 需要在内核中选择以上几个选项: 很多网友发邮件说Linux2.6.24内核在SPI选项上未发现有Samsung S3C2440 series SPI 或 Samsung S3C24XX series SPI 和User mode SPI device driver support这两个选项。 其实在Linux2.6.24内核里已经兼容了对SPI的操作。只是在Linux2.6.24/drivers/spi/Kconfig中未能选中此选项
[单片机]
基于<font color='red'>S3C2440</font>处理器SPI移植全过程
uboot-2011.12移植到S3C2440(序二)—— binutils二进制工具集与u-boot
概述 binutils是一组二进制工具集,它包括addr2line、ar、gprof、nm、objcopy、objdumpr、ranlib、size、strings、strip等。 ar软件 ar用于建立、修改、提取库文件。ar至少需要两个参数才能运行,比如: $ ar rv libtest.a add.o minus.o 是指将add.o、minus.o做成库文件libtest.a。其中r是指将文件列表插入归档文件,v是指得到操作版本号。 这样我们引用库文件的时候就可以使用: $ gcc -o test test.c -ltest nm软件 nm软件的作用是现实目标文件的信息和属性,比如: $ nm test.o
[单片机]
ARM-Linux驱动相关头文件知识点
#include linux/***.h 在linux-2.6.31/include/linux下面寻找源文件 #include asm/***.h 在linux-2.6.31/arch/arm/include/asm下面寻找源文件 #include mach/***.h 在linux-2.6.31/arch/arm/mach-s3c2410/include/mach下面寻找源文件 #include plat/regs-adc.h 在linux-2.6.31/arch/arm/plat-s3c/include/plat下面寻找源文件 ---------------------------------------------
[单片机]
基于WinCE的嵌入式视频数据采集系统设计
  随着嵌入式技术的不断发展,各种嵌入式微处理器和控制器不断出现,并广泛应用于工控、通信、PDA、安保等领域。基于ARM920t内核的嵌入式微处理器S3C2440,以其良好的数据处理能力、低功耗、小体积、支持多种嵌入式操作系统(如WinCE、Linux)、集成多种外设(如I2C控制器、LCD控制器等)等优点,广泛应用于手持设备等。WinCE操作系统具有内核可剪裁、实时性好、支持多种通信、模块化设计、具有丰富的API等特点,广泛用于嵌入式实时操作系统。这里提出的嵌入式图像数据采集系统是某“纳米技术与微系统”实验室开发的“嵌入式传感测控系统”中实现图像采集功能的子系统,该系统是以S3C2440为硬件核心,以WinCE为软件平台,能实时
[嵌入式]
小广播
添点儿料...
无论热点新闻、行业分析、技术干货……
热门活动
换一批
更多
设计资源 培训 开发板 精华推荐

最新单片机文章
何立民专栏 单片机及嵌入式宝典

北京航空航天大学教授,20余年来致力于单片机与嵌入式系统推广工作。

更多精选电路图
换一换 更多 相关热搜器件
更多每日新闻
随便看看
电子工程世界版权所有 京B2-20211791 京ICP备10001474号-1 电信业务审批[2006]字第258号函 京公网安备 11010802033920号 Copyright © 2005-2024 EEWORLD.com.cn, Inc. All rights reserved