linux设备驱动-SD卡驱动详解2core层

core层作为整个MMC 的核心,这部分完成了不同协议和规范的实现,并为HOST 层的驱动提供了接口函数。

CORE 部分: 这是整个MMC 的核心存,这部分完成了不同协议和规范的实现,并为HOST 层的驱动提供了接口函数。

HOST 部分是针对不同主机的驱动程序,这一部是驱动程序工程师需要根据自己的特点平台来完成的。

CARD 部分:因为这些记忆卡都是块设备,当然需要提供块设备的驱动程序,这部分就是实现了将你的SD 卡如何实现为块设备的。

它们分布于下面的文件夹中 Linux/drivers/mmc中:

1 数据结构

1.1 mmc_host

定义位于:linux-3.10.73\include\linux\mmc\host.h

 1 struct mmc_host {
 2     struct device        *parent;
 3     struct device        class_dev;
 4     int            index;
 5     const struct mmc_host_ops *ops;
 6     unsigned int        f_min;
 7     unsigned int        f_max;
 8     unsigned int        f_init;
 9     u32            ocr_avail;
10     u32            ocr_avail_sdio;    /* SDIO-specific OCR */
11     u32            ocr_avail_sd;    /* SD-specific OCR */
12     u32            ocr_avail_mmc;    /* MMC-specific OCR */
13     struct notifier_block    pm_notify;
14     u32            max_current_330;
15     u32            max_current_300;
16     u32            max_current_180;
17 
18 ...
19 
20     unsigned long        private[0] ____cacheline_aligned;
21 }

1.2 总线

platform bus //MMC host controller 作为一种 platform device, 它是需要注册到 platform bus上 的  。

1 struct bus_type platform_bus_type = {  
2     .name        = "platform",  
3     .dev_attrs    = platform_dev_attrs,  
4     .match        = platform_match,  
5     .uevent        = platform_uevent,  
6     .pm        = &platform_dev_pm_ops,  
7 }; 

mmc bus type  ,mmc_init()中被创建的.通过调用 mmc_register_bus() 来注册 MMC 总线  。定义位于:drivers\mmc\core\bus.c

1 static struct bus_type mmc_bus_type = {
2     .name        = "mmc",
3     .dev_attrs    = mmc_dev_attrs,
4     .match        = mmc_bus_match,
5     .uevent        = mmc_bus_uevent,
6     .probe        = mmc_bus_probe,
7     .remove        = mmc_bus_remove,
8     .pm        = &mmc_bus_pm_ops,
9 };

sdio bus type,在mmc_init()中被创建的.通过调用sdio_register_bus() 来注册 SDIO 总线 。定义位于:drivers\mmc\core\sdio_bus.c

1 static struct bus_type sdio_bus_type = {
2     .name        = "sdio",
3     .dev_attrs    = sdio_dev_attrs,
4     .match        = sdio_bus_match,
5     .uevent        = sdio_bus_uevent,
6     .probe        = sdio_bus_probe,
7     .remove        = sdio_bus_remove,
8     .pm        = SDIO_PM_OPS_PTR,
9 };

1.3 操作结构体

其中mmc总线操作相关函数,由于mmc卡支持多种总数据线,如SPI、SDIO、8LineMMC而不同的总线的操作控制方式不尽相同,所以通过此结构与相应的总线回调函数相关联。

sdio的总线操作 core/sdio.c

1 static const struct mmc_bus_ops mmc_sdio_ops = {
2     .remove = mmc_sdio_remove,
3     .detect = mmc_sdio_detect,
4     .suspend = mmc_sdio_suspend,
5     .resume = mmc_sdio_resume,
6     .power_restore = mmc_sdio_power_restore,
7     .alive = mmc_sdio_alive,
8 };

mmc卡的总线操作 core/mmc.c

 1 static const struct mmc_bus_ops mmc_ops = {
 2     .awake = mmc_awake,
 3     .sleep = mmc_sleep,
 4     .remove = mmc_remove,
 5     .detect = mmc_detect,
 6     .suspend = NULL,
 7     .resume = NULL,
 8     .power_restore = mmc_power_restore,
 9     .alive = mmc_alive,
10 };

sd卡的总线操作 core/sd.c

1 static const struct mmc_bus_ops mmc_sd_ops = {
2     .remove = mmc_sd_remove,
3     .detect = mmc_sd_detect,
4     .suspend = NULL,
5     .resume = NULL,
6     .power_restore = mmc_sd_power_restore,
7     .alive = mmc_sd_alive,
8 };

.detect操作函数:驱动程序经常需要调用此函数去检测mmc卡的状态,具体实现是发送CMD13命令,并读回响应,如果响应错误,则依次调用.removedetach_bus来移除卡及释放总线。

2 core的flow

内核启动时,首先执行core/core.c的mmc_init,注册mmc、sd总线,以及一个host class设备。接着执行card/block.c中mmc_blk_init(),申请一个块设备。

2.1 初始化

2.1.1 mmc_init

定义位于:linux-3.10.73\drivers\mmc\core\core.c

 1 static int __init mmc_init(void)
 2 {
 3     int ret;
 4 
 5     workqueue = alloc_ordered_workqueue("kmmcd", 0);//建立队列,主要用来支持热插热拔
 6     if (!workqueue)
 7         return -ENOMEM;
 8 
 9     ret = mmc_register_bus();//注册mmc总线
10     if (ret)
11         goto destroy_workqueue;
12 
13     ret = mmc_register_host_class();//注册mmc host class
14     if (ret)
15         goto unregister_bus;
16 
17     ret = sdio_register_bus();//注册SDIO总线
18     if (ret)
19         goto unregister_host_class;
20 
21     return 0;
22 
23 unregister_host_class:
24     mmc_unregister_host_class();
25 unregister_bus:
26     mmc_unregister_bus();
27 destroy_workqueue:
28     destroy_workqueue(workqueue);
29 
30     return ret;
31 }

2.1.2 函数mmc_blk_init

定义位于:mmc/card/block.c

 1 static int __init mmc_blk_init(void)
 2 {
 3     int res;
 4 
 5     if (perdev_minors != CONFIG_MMC_BLOCK_MINORS)
 6         pr_info("mmcblk: using %d minors per device\n", perdev_minors);
 7 
 8     max_devices = 256 / perdev_minors;
 9 
10     res = register_blkdev(MMC_BLOCK_MAJOR, "mmc");//注册一个块设备
11     if (res)
12         goto out;
13 
14     res = mmc_register_driver(&mmc_driver);//注册一个mmc设备驱动
15     if (res)
16         goto out2;
17 
18     return 0;
19  out2:
20     unregister_blkdev(MMC_BLOCK_MAJOR, "mmc");
21  out:
22     return res;
23 }

mmc_driver

1 static struct mmc_driver mmc_driver = {
2     .drv        = {
3         .name    = "mmcblk",
4     },
5     .probe        = mmc_blk_probe,
6     .remove        = mmc_blk_remove,
7     .suspend    = mmc_blk_suspend,
8     .resume        = mmc_blk_resume,
9 };

mmc_driver probe函数

 1 static int mmc_blk_probe(struct mmc_card *card)
 2 {
 3     struct mmc_blk_data *md, *part_md;
 4     char cap_str[10];
 5 
 6     /*
 7      * Check that the card supports the command class(es) we need.
 8      */
 9     if (!(card->csd.cmdclass & CCC_BLOCK_READ))
10         return -ENODEV;
11 
12     md = mmc_blk_alloc(card);
13     if (IS_ERR(md))
14         return PTR_ERR(md);
15 
16     string_get_size((u64)get_capacity(md->disk) << 9, STRING_UNITS_2,
17             cap_str, sizeof(cap_str));
18     pr_info("%s: %s %s %s %s\n",
19         md->disk->disk_name, mmc_card_id(card), mmc_card_name(card),
20         cap_str, md->read_only ? "(ro)" : "");
21 
22     if (mmc_blk_alloc_parts(card, md))
23         goto out;
24 
25     mmc_set_drvdata(card, md);
26     mmc_fixup_device(card, blk_fixups);
27 
28     if (mmc_add_disk(md))
29         goto out;
30 
31     list_for_each_entry(part_md, &md->part, part) {
32         if (mmc_add_disk(part_md))
33             goto out;
34     }
35     return 0;
36 
37  out:
38     mmc_blk_remove_parts(card, md);
39     mmc_blk_remove_req(md);
40     return 0;
41 }

2.2 注册mmc总线

定义位于:drivers\mmc\core\bus.c

 1 int mmc_register_bus(void)
 2 {
 3     return bus_register(&mmc_bus_type);//注册bus
 4 }
 5 
 6 static struct bus_type mmc_bus_type = {
 7     .name        = "mmc",
 8     .dev_attrs    = mmc_dev_attrs,
 9     .match        = mmc_bus_match,
10     .uevent        = mmc_bus_uevent,
11     .probe        = mmc_bus_probe,
12     .remove        = mmc_bus_remove,
13     .pm        = &mmc_bus_pm_ops,
14 };

2.3 注册host class

定义位于:drivers\mmc\core\host.c

1 static struct class mmc_host_class = {
2     .name        = "mmc_host",
3     .dev_release    = mmc_host_classdev_release,
4 };
5 
6 int mmc_register_host_class(void)
7 {
8     return class_register(&mmc_host_class);
9 }

2.4 注册sdio总线

定义位于:drivers\mmc\core\sdio_bus.c

 1 static struct bus_type sdio_bus_type = {
 2     .name        = "sdio",
 3     .dev_attrs    = sdio_dev_attrs,
 4     .match        = sdio_bus_match,
 5     .uevent        = sdio_bus_uevent,
 6     .probe        = sdio_bus_probe,
 7     .remove        = sdio_bus_remove,
 8     .pm        = SDIO_PM_OPS_PTR,
 9 };
10 
11 int sdio_register_bus(void)
12 {
13     return bus_register(&sdio_bus_type);
14 }

2.5 mmc_rescan函数

mmc_rescan 函数是需要重点关注的,因为SD卡协议中的检测,以及卡识别等都是在此函数中实现。

插入SD卡,主控制器产生中断,进入中断处理函数s3cmci_irq_cd,其中调用的函数 mmc_detect_change,它将最终调用queue_delayed_work执行工作队列里的mmc_rescan函数。

mmc_rescan扫描SD总线上是否存在SD卡,是mmc host的detect work的功能函数,用于探测目标卡的类型并且根据mmc/sd/sdio协议进行comm的初始化。

 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     if (host->rescan_disable)
 8         return;
 9 
10     /* If there is a non-removable card registered, only scan once */
11     if ((host->caps & MMC_CAP_NONREMOVABLE) && host->rescan_entered)
12         return;
13     host->rescan_entered = 1;
14 
15     mmc_bus_get(host);//
16 
17     /*
18      * if there is a _removable_ card registered, check whether it is
19      * still present
20      */
21     if (host->bus_ops && host->bus_ops->detect && !host->bus_dead
22         && !(host->caps & MMC_CAP_NONREMOVABLE))
23         host->bus_ops->detect(host);//
24 
25     host->detect_change = 0;//
26 
27     /*
28      * Let mmc_bus_put() free the bus/bus_ops if we've found that
29      * the card is no longer present.
30      */
31     mmc_bus_put(host);//减少引用计数,即释放
32     mmc_bus_get(host);//增加bus引用计数
33 
34     /* if there still is a card present, stop here */
35     if (host->bus_ops != NULL) {
36         mmc_bus_put(host);//如果卡仍然存在,减少引用计数,不必探测了
37         goto out;
38     }
39 
40     /*
41      * Only we can add a new handler, so it's safe to
42      * release the lock here.
43      */
44     mmc_bus_put(host);//
45 
46     if (host->ops->get_cd && host->ops->get_cd(host) == 0) {//有卡,退出
47         mmc_claim_host(host);//
48         mmc_power_off(host);//
49         mmc_release_host(host);//
50         goto out;
51     }
52 
53     mmc_claim_host(host);//
54     for (i = 0; i < ARRAY_SIZE(freqs); i++) {
55         if (!mmc_rescan_try_freq(host, max(freqs[i], host->f_min)))//
56             break;
57         if (freqs[i] <= host->f_min)
58             break;
59     }
60     mmc_release_host(host);//
61 
62  out:
63     if (host->caps & MMC_CAP_NEEDS_POLL)
64         mmc_schedule_delayed_work(&host->detect, HZ);//
65 }

初始化卡接以下流程初始化:

发送CMD0使卡进入IDLE状态
发送CMD8,检查卡是否SD2.0。SD1.1是不支持CMD8的,因此在SD2.0 Spec中提出了先发送CMD8,如响应为无效命令,则卡为SD1.1,否则就是SD2.0(请参考SD2.0 Spec)。
发送CMD5读取OCR寄存器。
发送ACMD55、CMD41,使卡进入工作状态。MMC卡并不支持ACMD55、CMD41,如果这步通过了,则证明这张卡是SD卡。
如果d步骤错误,则发送CMD1判断卡是否为MMC。SD卡不支持CMD1,而MMC卡支持,这就是SD和MMC类型的判断依据。
如果ACMD41和CMD1都不能通过,那这张卡恐怕就是无效卡了,初始化失败。

假如扫描到总线上挂有有效的设备,就调用相对应的函数把设备装到系统中,mmc_attach_sdio()、mmc_attach_sd()、mmc_attach_mmc()这三个函数分别是装载sdio设备,sd卡和mmc卡的。

在 sd卡中,驱动循环发送ACMD41、CMD55给卡,读取OCR寄存器,成功后,依次发送CMD2(读CID)、CMD3(得到RCA)、CMD9(读 CSD)、CMD7(选择卡)。后面还有几个命令分别是ACMD41&CMD51,使用CMD6切换一些功能,如切换到高速模式。

经过上述步骤,已经确定当前插入的卡是一张有效、可识别的存储卡。然后调用mmc_add_card()把存储卡加到系统中。正式与系统驱动连接在一起。

2.6 函数mmc_rescan_try_freq

 1 static int mmc_rescan_try_freq(struct mmc_host *host, unsigned freq)
 2 {
 3     host->f_init = freq; //根据mmc_recan函数传进来的参数知道,首先传进来的是400kHZ.一般mmc/sd/sdio的初始化时钟采用的是400kHZ
 4 
 5 #ifdef CONFIG_MMC_DEBUG
 6     pr_info("%s: %s: trying to init card at %u Hz\n",
 7         mmc_hostname(host), __func__, host->f_init);
 8 #endif
 9     mmc_power_up(host);//上电,我们知道,在mmc_add_host时,会调用mmc_start_host,而那里首先是将host掉电的。这里上电
10 
11     /*
12      * Some eMMCs (with VCCQ always on) may not be reset after power up, so
13      * do a hardware reset if possible.
14      */
15     mmc_hw_reset_for_init(host);//复位硬件,可以选择性实现。
16 
17     /*
18      * sdio_reset sends CMD52 to reset card.  Since we do not know
19      * if the card is being re-initialized, just send it.  CMD52
20      * should be ignored by SD/eMMC cards.
21      */
22     sdio_reset(host);
23     mmc_go_idle(host);//发送CMD0 复位SD卡
24 
25     mmc_send_if_cond(host, host->ocr_avail);
26     /*Linux 卡的探测顺序是:先辨别卡是否是sdio功能卡或者是sdio与sd卡的组合卡,然后辨别是否是SD卡,最后才会辨别是否是mmc卡*/
27     /* Order's important: probe SDIO, then SD, then MMC */
28     if (!mmc_attach_sdio(host))//匹配sdio接口
29         return 0;
30     if (!mmc_attach_sd(host))
31         return 0;
32     if (!mmc_attach_mmc(host))
33         return 0;
34 
35     mmc_power_off(host);
36     return -EIO;
37 }

函数sdio_reset(host);/的作用:

(1)如果目标卡是纯SD卡(对MMC卡不了解,所以不加评论),则目标卡不会应答,一般主机host的寄存器会报错,但是这个无关紧要,可以不理它。

(2)如果目标卡是纯SDIO卡,那么这里就是复位SDIO卡,通过命令CMD52来实现的。

(3)如果目标卡是SD卡和SDIO卡的组合卡,则需要先发送CMD52来复位SDIO卡,再复位SD卡,因为CMD52要先于CMD0发送。

函数mmc_send_if_cond(host, host->ocr_avail);的作用 :

为了支持sd version 2.0以上的sd卡,在初始化的过程中必须在发送ACMD41之前,先发送CMD8,CMD8一般是用于检测SD卡是否能运行在host提供的电压范围内。大家可能发现,这个调用过程没有检查是否出错,其实一般CMD8是用来辨别目标卡是否是高容量SD卡,如果是,CMD8 会有R7应答,R7应答中会有目标SD卡支持的电压范围以及CMD8中发送过去的“check pattern(一般是0xAA)”,否则,目标卡不会应答,在Linux 内核代码中,判断是这样的,如果应答,目标卡就是SD高容量卡,否则出现应答超时错误,就是标准SD卡!这里的调用,主要作用是为了在发送ACMD41之前发送CMD8,这是version 2.0及以上的规定顺序,后面还会有发送CMD8的地方,那里才是真正检测目标卡的类型的地方。

2.6.1 函数mmc_go_idle

 1 int mmc_go_idle(struct mmc_host *host)    
 2     struct mmc_command cmd = {0};  
 3     cmd.opcode = MMC_GO_IDLE_STATE; //即CMD0  
 4     cmd.arg = 0;                    //此命令无参数  
 5     err = mmc_wait_for_cmd(host, &cmd, 0)  
 6       
 7 int mmc_wait_for_cmd(struct mmc_host *host, struct mmc_command *cmd, int retries)  
 8     memset(cmd->resp, 0, sizeof(cmd->resp));  //调用了 mmc_start_request,   
 9     cmd->retries = retries;  
10     mrq.cmd = cmd;                                
11     mmc_wait_for_req(host, &mrq);  
12       
13 void mmc_wait_for_req(struct mmc_host *host, struct mmc_request *mrq)   ----重要函数  
14     __mmc_start_req(host, mrq);  
15   
16 static int __mmc_start_req(struct mmc_host *host, struct mmc_request *mrq)  
17     mmc_start_request(host, mrq);  
18           
19 static void mmc_start_request(struct mmc_host *host, struct mmc_request *mrq)  
20     host->ops->request(host, mrq);    //即 msmsdcc_request, MMC 核心与核HOST 层握手了 

host即struct mmc_host类型,在drivers\mmc\host\s3cmci.c的probe函数中定义赋值:

 1 static int s3cmci_probe(struct platform_device *pdev)
 2 {
 3     ...
 4     mmc->ops     = &s3cmci_ops;
 5         ...
 6 }
 7 
 8 static struct mmc_host_ops s3cmci_ops = {
 9     .request    = s3cmci_request,
10     .set_ios    = s3cmci_set_ios,
11     .get_ro        = s3cmci_get_ro,
12     .get_cd        = s3cmci_card_present,
13     .enable_sdio_irq = s3cmci_enable_sdio_irq,
14 };

request函数分析见linux设备驱动-SD卡驱动详解3 host层

2.7 sdio的挂载

2.7.1 函数mmc_attach_sdio

定义位于:drivers\mmc\core\sdio.c

  1 int mmc_attach_sdio(struct mmc_host *host)
  2 {
  3     int err, i, funcs;
  4     u32 ocr;
  5     struct mmc_card *card;
  6 
  7     BUG_ON(!host);
  8     WARN_ON(!host->claimed);
  9 
 10     err = mmc_send_io_op_cond(host, 0, &ocr);//
 11     if (err)
 12         return err;
 13 
 14     mmc_attach_bus(host, &mmc_sdio_ops);//
 15     if (host->ocr_avail_sdio)
 16         host->ocr_avail = host->ocr_avail_sdio;
 17 
 18     /*
 19      * Sanity check the voltages that the card claims to
 20      * support.
 21      */
 22     if (ocr & 0x7F) {
 23         pr_warning("%s: card claims to support voltages "
 24                "below the defined range. These will be ignored.\n",
 25                mmc_hostname(host));
 26         ocr &= ~0x7F;
 27     }
 28 
 29     host->ocr = mmc_select_voltage(host, ocr);//设置时钟和总线  ---------------- 详解1
 30 
 31     /*
 32      * Can we support the voltage(s) of the card(s)?
 33      */
 34     if (!host->ocr) {
 35         err = -EINVAL;
 36         goto err;
 37     }
 38 
 39     /*
 40      * Detect and init the card.
 41      */
 42     if (mmc_host_uhs(host))
 43         /* to query card if 1.8V signalling is supported */
 44         host->ocr |= R4_18V_PRESENT;
 45 
 46     err = mmc_sdio_init_card(host, host->ocr, NULL, 0);//
 47     if (err) {
 48         if (err == -EAGAIN) {
 49             /*
 50              * Retry initialization with S18R set to 0.
 51              */
 52             host->ocr &= ~R4_18V_PRESENT;
 53             err = mmc_sdio_init_card(host, host->ocr, NULL, 0);//
 54         }
 55         if (err)
 56             goto err;
 57     }
 58     card = host->card;
 59 
 60     /*
 61      * Enable runtime PM only if supported by host+card+board
 62      */
 63     if (host->caps & MMC_CAP_POWER_OFF_CARD) {
 64         /*
 65          * Let runtime PM core know our card is active
 66          */
 67         err = pm_runtime_set_active(&card->dev);//
 68         if (err)
 69             goto remove;
 70 
 71         /*
 72          * Enable runtime PM for this card
 73          */
 74         pm_runtime_enable(&card->dev);//
 75     }
 76 
 77     /*
 78      * The number of functions on the card is encoded inside
 79      * the ocr.
 80      */
 81     funcs = (ocr & 0x70000000) >> 28;
 82     card->sdio_funcs = 0;
 83 
 84     /*
 85      * Initialize (but don't add) all present functions.
 86      */
 87     for (i = 0; i < funcs; i++, card->sdio_funcs++) {
 88         err = sdio_init_func(host->card, i + 1);
 89         if (err)
 90             goto remove;
 91 
 92         /*
 93          * Enable Runtime PM for this func (if supported)
 94          */
 95         if (host->caps & MMC_CAP_POWER_OFF_CARD)
 96             pm_runtime_enable(&card->sdio_func[i]->dev);
 97     }
 98 
 99     /*
100      * First add the card to the driver model...
101      */
102     mmc_release_host(host);
103     err = mmc_add_card(host->card);//注册一个mmc card
104     if (err)
105         goto remove_added;
106 
107     /*
108      * ...then the SDIO functions.
109      */
110     for (i = 0;i < funcs;i++) {
111         err = sdio_add_func(host->card->sdio_func[i]);//注册一个sdio func  
112         if (err)
113             goto remove_added;
114     }
115 
116     mmc_claim_host(host);//
117     return 0;
118 
119 
120 remove_added:
121     /* Remove without lock if the device has been added. */
122     mmc_sdio_remove(host);
123     mmc_claim_host(host);//
124 remove:
125     /* And with lock if it hasn't been added. */
126     mmc_release_host(host);//
127     if (host->card)
128         mmc_sdio_remove(host);
129     mmc_claim_host(host);
130 err:
131     mmc_detach_bus(host);//
132 
133     pr_err("%s: error %d whilst initialising SDIO card\n",
134         mmc_hostname(host), err);
135 
136     return err;
137 }

详解1:mmc_select_voltage函数设置时钟和总线,协议层里利用回调函数为所有满足该协议的设备提供统一的接口,而具体实现由底层不同的设备驱动各自完成。注意到,之所以要定义一些放之四海而皆准的公用的类,比如struct mmc_host,就是需要通过struct mmc_host *host指针作为形参传到协议层所提供的接口函数中,从而得以调用。

 1 u32 mmc_select_voltage(struct mmc_host *host, u32 ocr)
 2 {
 3     mmc_set_ios(host);
 4  
 5     ... ...
 6 }
 7  
 8 static inline void mmc_set_ios(struct mmc_host *host)
 9 {
10     struct mmc_ios *ios = &host->ios;
11  
12     host->ops->set_ios(host, ios);  // 设置主控制器时钟和总线的回调函数,具体实现由主控制器驱动完成
13 }

2.7.2 函数mmc_add_card

定义位于:drivers\mmc\core\bus.c

最终call device_add,就是将card注册进linux设备模型 ,注册结果就是可以在/sys/bus/mmc/devices目录下见到card 的名字,如mmc2:0001

 1 /*
 2  * Register a new MMC card with the driver model.
 3  */
 4 int mmc_add_card(struct mmc_card *card)
 5 {
 6     int ret;
 7     const char *type;
 8     const char *uhs_bus_speed_mode = "";
 9     static const char *const uhs_speeds[] = {
10         [UHS_SDR12_BUS_SPEED] = "SDR12 ",
11         [UHS_SDR25_BUS_SPEED] = "SDR25 ",
12         [UHS_SDR50_BUS_SPEED] = "SDR50 ",
13         [UHS_SDR104_BUS_SPEED] = "SDR104 ",
14         [UHS_DDR50_BUS_SPEED] = "DDR50 ",
15     };
16 
17 
18     dev_set_name(&card->dev, "%s:%04x", mmc_hostname(card->host), card->rca);//设置device 名字
19 
20     switch (card->type) {
21     case MMC_TYPE_MMC:
22         type = "MMC";
23         break;
24     case MMC_TYPE_SD:
25         type = "SD";
26         if (mmc_card_blockaddr(card)) {
27             if (mmc_card_ext_capacity(card))
28                 type = "SDXC";
29             else
30                 type = "SDHC";
31         }
32         break;
33     case MMC_TYPE_SDIO:
34         type = "SDIO";
35         break;
36     case MMC_TYPE_SD_COMBO:
37         type = "SD-combo";
38         if (mmc_card_blockaddr(card))
39             type = "SDHC-combo";
40         break;
41     default:
42         type = "?";
43         break;
44     }
45 
46     if (mmc_sd_card_uhs(card) &&
47         (card->sd_bus_speed < ARRAY_SIZE(uhs_speeds)))
48         uhs_bus_speed_mode = uhs_speeds[card->sd_bus_speed];
49 
50     if (mmc_host_is_spi(card->host)) {
51         pr_info("%s: new %s%s%s card on SPI\n",
52             mmc_hostname(card->host),
53             mmc_card_highspeed(card) ? "high speed " : "",
54             mmc_card_ddr_mode(card) ? "DDR " : "",
55             type);
56     } else {
57         pr_info("%s: new %s%s%s%s%s card at address %04x\n",
58             mmc_hostname(card->host),
59             mmc_card_uhs(card) ? "ultra high speed " :
60             (mmc_card_highspeed(card) ? "high speed " : ""),
61             (mmc_card_hs200(card) ? "HS200 " : ""),
62             mmc_card_ddr_mode(card) ? "DDR " : "",
63             uhs_bus_speed_mode, type, card->rca);
64     }
65 
66 #ifdef CONFIG_DEBUG_FS
67     mmc_add_card_debugfs(card);
68 #endif
69     mmc_init_context_info(card->host);
70 
71     ret = device_add(&card->dev);//添加设备device
72     if (ret)
73         return ret;
74 
75     mmc_card_set_present(card);
76 
77     return 0;
78 }

2.7.3 sdio总线上driver

以上已向系统添加设备device。以TIdrivers\net\wireless\ti\wlcore\sdio.c的wlan为例:

 1 static struct sdio_driver wl1271_sdio_driver = {
 2     .name        = "wl1271_sdio",
 3     .id_table    = wl1271_devices,
 4     .probe        = wl1271_probe,
 5     .remove        = wl1271_remove,
 6 #ifdef CONFIG_PM
 7     .drv = {
 8         .pm = &wl1271_sdio_pm_ops,
 9     },
10 #endif
11 };
12 
13 static int __init wl1271_init(void)
14 {
15     return sdio_register_driver(&wl1271_sdio_driver);//向sdio总线注册driver
16 }

函数sdio_register_driver,定义位于:drivers\mmc\core\sdio_bus.c

 1 /**
 2  *    sdio_register_driver - register a function driver
 3  *    @drv: SDIO function driver
 4  */
 5 int sdio_register_driver(struct sdio_driver *drv)
 6 {
 7     drv->drv.name = drv->name;
 8     drv->drv.bus = &sdio_bus_type;
 9     return driver_register(&drv->drv);
10 }

总结:(1)以上device和driver是挂在sdio总线上的,sdio总线在kernel启动时加载的;

(2)以drivers\mmc\host\s3cmci.c为例作为host,sdio设备插入时产生中断,调用mmc_resan建立host等工作,最终添加sdio总线上的设备device;

(3)以下的mmc总线和sd卡也是类似。

2.8 sd卡的挂载

2.8.1 函数mmc_attach_sd

完成匹配,和初始化卡的功能 。定义位于:drivers\mmc\core\sd.c

 1 /*
 2  * Starting point for SD card init.
 3  */
 4 int mmc_attach_sd(struct mmc_host *host)
 5 {
 6     int err;
 7     u32 ocr;
 8 
 9     BUG_ON(!host);
10     WARN_ON(!host->claimed);
11 
12     err = mmc_send_app_op_cond(host, 0, &ocr);//检测是否是支持SD卡
13     if (err)
14         return err;
15 
16     mmc_sd_attach_bus_ops(host);
17     if (host->ocr_avail_sd)
18         host->ocr_avail = host->ocr_avail_sd;
19 
20     /*
21      * We need to get OCR a different way for SPI.
22      */
23     if (mmc_host_is_spi(host)) {
24         mmc_go_idle(host);
25 
26         err = mmc_spi_read_ocr(host, 0, &ocr);
27         if (err)
28             goto err;
29     }
30 
31     /*
32      * Sanity check the voltages that the card claims to
33      * support.
34      */
35     if (ocr & 0x7F) {
36         pr_warning("%s: card claims to support voltages "
37                "below the defined range. These will be ignored.\n",
38                mmc_hostname(host));
39         ocr &= ~0x7F;
40     }
41 
42     if ((ocr & MMC_VDD_165_195) &&
43         !(host->ocr_avail_sd & MMC_VDD_165_195)) {
44         pr_warning("%s: SD card claims to support the "
45                "incompletely defined 'low voltage range'. This "
46                "will be ignored.\n", mmc_hostname(host));
47         ocr &= ~MMC_VDD_165_195;
48     }
49 
50     host->ocr = mmc_select_voltage(host, ocr);//设置MMC电压
51 
52     /*
53      * Can we support the voltage(s) of the card(s)?
54      */
55     if (!host->ocr) {
56         err = -EINVAL;
57         goto err;
58     }
59 
60     /*
61      * Detect and init the card.
62      */
63     err = mmc_sd_init_card(host, host->ocr, NULL);//对mmc卡进行初始化,主要是读取mmc卡里的一些寄存器信息,且对这些寄存器的值进行设置
64     if (err)
65         goto err;
66 
67     mmc_release_host(host);
68     err = mmc_add_card(host->card);//调用 mmc_add_card 来把 mmc_card 挂载到 mmc_bus_type 总线去
69     mmc_claim_host(host);
70     if (err)
71         goto remove_card;
72 
73     return 0;
74 
75 remove_card:
76     mmc_release_host(host);
77     mmc_remove_card(host->card);
78     host->card = NULL;
79     mmc_claim_host(host);
80 err:
81     mmc_detach_bus(host);
82 
83     pr_err("%s: error %d whilst initialising SD card\n",
84         mmc_hostname(host), err);
85 
86     return err;
87 }

2.8.2 函数mmc_send_app_op_cond

定义位于:drivers\mmc\core\sd_ops.c

1 int mmc_send_app_op_cond(struct mmc_host *host, u32 ocr, u32 *rocr)  
2 {
3     ...
4     cmd.opcode = SD_APP_OP_COND;    //ACMD41,获取 SDcard 的允许电压范围值,保存在 ocr 中. 所有发送它之前需要发送 CMD_55 命令。执行完后 card 状态变为 READY
5   ...
6 }

2.8.3 函数mmc_sd_init_card

 1 static int mmc_sd_init_card(struct mmc_host *host, u32 ocr,struct mmc_card *oldcard)  
 2 {
 3    ...
 4     err = mmc_sd_get_cid(host, ocr, cid, &rocr);        //发送 CMD2 ,获取卡的身份信息,进入到身份状态  
 5    ...
 6     card = mmc_alloc_card(host, &sd_type);              //分配一张 SD 类型的 card 结构  
 7    ...
 8     err = mmc_send_relative_addr(host, &card->rca);      //获取卡的相对地址,注意一前卡和主机通信都采用默认地址,现在有了自己的地址了,进入到 stand_by 状态  
 9    ...
10     err = mmc_sd_get_csd(host, card); ----mmc_send_csd(card, card->raw_csd);//CMD9, 获取 CSD 寄存器的信息,包括 block 长度,卡容量等信息  
11    ...
12     err = mmc_select_card(card);                        //发送 CMD7, 选中目前 RADD 地址上的卡,任何时候总线上只有一张卡被选中,进入了传输状态   
13    ...
14     err = mmc_sd_setup_card(host, card, oldcard != NULL);     
15     ...
16 }

函数mmc_sd_setup_card

1 int mmc_sd_setup_card(struct mmc_host *host, struct mmc_card *card,bool reinit)  
2 {
3    ...
4     mmc_app_send_scr(card, card->raw_scr);   //发送命令 ACMD51 获取 SRC 寄存器的内容,进入到 SENDING-DATA 状态 
5    ... 
6     if (host->ops->get_ro(host) > 0 )      // get_ro(host) 即是 msmsdcc_get_ro   
7         mmc_card_set_readonly(card);        //是否写保护,如果是的,将 card 状态设置为只读状态 
8    ...
9 }

卡设备加到系统中后,对于块设备来说,会通知mmc块设备驱动。块设备驱动此时调用probe函数,即mmc_blk_probe()函数,mmc_blk_probe() 先分配一个新的mmc_blk_data结构变量,然后调用mmc_init_queue,初始化blk队列。然后建立一个线程 mmc_queue_thread()。

3 sdio core总结

   

 mmc_rescan函数总结

参考博文:https://blog.csdn.net/zqixiao_09/article/list/5

posted @ 2020-08-18 22:23  Action_er  阅读(1720)  评论(0编辑  收藏  举报