EMMC架构

现在EMMC盛行,分析总结还是很有必要的。以下以全志a64为实例切入主题。

这里a64有三个sdc0~2,硬件上sdc2是连接EMMC,这里只分析sdc2的代码。

初始化的代码在linux-3.10/drivers/mmc/host/sunxi-mmc.c
以下忽略部分冗余代码:

 1 static const struct of_device_id sunxi_mmc_of_match[] = {
 2     ......
 3     ......
 4     { .compatible = "allwinner,sun50i-sdmmc2", },
 5     ......
 6     ......
 7 };
 8 MODULE_DEVICE_TABLE(of, sunxi_mmc_of_match);
 9
10 static struct platform_driver sunxi_mmc_driver = {
11     .driver = {
12         .name    = "sunxi-mmc",
13         .of_match_table = of_match_ptr(sunxi_mmc_of_match),
14         .pm = sunxi_mmc_pm_ops,
15     },
16     .probe        = sunxi_mmc_probe,
17     .remove        = sunxi_mmc_remove,
18     .shutdown   = sunxi_shutdown_mmc,
19 };
20 module_platform_driver(sunxi_mmc_driver);

设备树会初始化deivce,这里有driver,下面直接进sunxi_mmc_probe分析。
以下忽略部分冗余代码:

 1 static int sunxi_mmc_probe(struct platform_device *pdev)
 2 {
 3     struct sunxi_mmc_host *host;    //全志a64主控硬件相关
 4     struct mmc_host *mmc;    //emmc架构相关
 5     int ret;
 6
 7     dev_info(&pdev->dev,"%s\n",DRIVER_VERSION);
 8
 9     mmc = mmc_alloc_host(sizeof(struct sunxi_mmc_host), &pdev->dev);    //初始化mmc_host,后面会分析
10
11     host = mmc_priv(mmc);    //host = mmc->private;
12     host->mmc = mmc;        //sunxi_mmc_host和mmc_host建立纽带
13
14     //配置硬件相关,如时钟,sdc控制寄存器,IO复用,中断,电源控制
15     ret = sunxi_mmc_resource_request(host, pdev);
16
17     //初始化dma     #define     4*PAGE_SIZE
18     host->dma_mask = DMA_BIT_MASK(32);
19     pdev->dev.dma_mask = &host->dma_mask;
20     pdev->dev.coherent_dma_mask = DMA_BIT_MASK(32);
21     host->sg_cpu = dma_alloc_coherent(&pdev->dev, SUNXI_REQ_PAGE_SIZE,
22                       &host->sg_dma, GFP_KERNEL);
23
24     mmc->ops        = &sunxi_mmc_ops;
25     mmc->max_blk_count    = 8192;
26     mmc->max_blk_size    = 4096;
27     mmc->max_segs        = SUNXI_REQ_PAGE_SIZE / sizeof(struct sunxi_idma_des);
28     mmc->max_seg_size    = (1 << host->idma_des_size_bits);
29     mmc->max_req_size    = mmc->max_seg_size * mmc->max_segs;
30     /* 400kHz ~ 50MHz */
31     mmc->f_min        =   400000;
32     mmc->f_max        = 50000000;
33     mmc->caps           |= MMC_CAP_MMC_HIGHSPEED | MMC_CAP_SD_HIGHSPEED | MMC_CAP_ERASE 34                         | MMC_CAP_WAIT_WHILE_BUSY;
35     //mmc->caps2      |= MMC_CAP2_HS400_1_8V;
36
37
38     mmc_of_parse(mmc);    //解析设备树相关的信息
39
40     ret = mmc_add_host(mmc);    //重点,真正初始化emmc并启动,后面会分析
41
42     ret = mmc_create_sys_fs(host,pdev);
43
44     dev_info(&pdev->dev, "base:0x%p irq:%u\n", host->reg_base, host->irq);
45     platform_set_drvdata(pdev, mmc);
46     return 0;
47 }

到此probe结束,有两个比较关键的函数mmc_alloc_host和mmc_add_host,真正启动emmc的操作都在这里
mmc_alloc_host和mmc_add_host的代码在linux-3.10/drivers/mmc/core/host.c
以下忽略部分冗余代码:

 1 struct mmc_host *mmc_alloc_host(int extra, struct device *dev)
 2 {
 3     int err;
 4     struct mmc_host *host;
 5
 6     host = kzalloc(sizeof(struct mmc_host) + extra, GFP_KERNEL);
 7
 8     /* scanning will be enabled when we‘re ready */
 9     host->rescan_disable = 1;
10     idr_preload(GFP_KERNEL);
11
12     err = idr_alloc(&mmc_host_idr, host, 0, 0, GFP_NOWAIT);
13     if (err >= 0)
14         host->index = err;    //通过整数id管理地址,具体细节本人不太清楚
15
16     idr_preload_end();
17
18     dev_set_name(&host->class_dev, "mmc%d", host->index);
19
20     host->parent = dev;
21     host->class_dev.parent = dev;
22     host->class_dev.class = &mmc_host_class;
23     device_initialize(&host->class_dev);
24
25     mmc_host_clk_init(host);    //这里初始化框架相关的clock,不涉及硬件
26
27     host->slot.cd_irq = -EINVAL;
28
29     init_waitqueue_head(&host->wq);
30     wake_lock_init(&host->detect_wake_lock, WAKE_LOCK_SUSPEND,    //初始化唤醒内核的锁
31         kasprintf(GFP_KERNEL, "%s_detect", mmc_hostname(host)));
32     INIT_DELAYED_WORK(&host->detect, mmc_rescan);        //重要,初始化检查card的队列
33
34     host->pm_notify.notifier_call = mmc_pm_notify;
35     /*
36      * By default, hosts do not support SGIO or large requests.
37      * They have to set these according to their abilities.
38      */
39     host->max_segs = 1;
40     host->max_seg_size = PAGE_CACHE_SIZE;    //#define  PAGE_CACHE_SIZE    1UL << 16
41
42     host->max_req_size = PAGE_CACHE_SIZE;
43     host->max_blk_size = 512;
44     host->max_blk_count = PAGE_CACHE_SIZE / 512;
45
46     return host;
47 }
48
49 int mmc_add_host(struct mmc_host *host)
50 {
51     int err;
52
53     err = device_add(&host->class_dev);
54
55     led_trigger_register_simple(dev_name(&host->class_dev), &host->led);    //;类似硬盘的LED闪烁
56
57 #ifdef CONFIG_DEBUG_FS
58     mmc_add_host_debugfs(host);    //debug用
59 #endif
60     mmc_host_clk_sysfs_init(host);
61
62     mmc_start_host(host);    //重要,真正操作都在这里
63
64     return 0;
65 }

mmc_start_host的代码在linux-3.10/drivers/mmc/core/core.c
以下忽略部分冗余代码:

void mmc_start_host(struct mmc_host *host)
{
    host->f_init = max(freqs[0], host->f_min);    //初始时钟
    host->rescan_disable = 0;    //开启rescan
    mmc_power_up(host);
    mmc_detect_change(host, 0);
}

static void mmc_power_up(struct mmc_host *host)
{
    int bit;

    if (host->ios.power_mode == MMC_POWER_ON)    //已经启动
        return;

    mmc_host_clk_hold(host);    //没有定义CONFIG_MMC_CLKGATE 所有gateclock相关都是空操作

    /* If ocr is set, we use it */
    if (host->ocr)    //ocr是电源配置,在sunxi_mmc_resource_request已经设置好
        bit = ffs(host->ocr) - 1;
    else
        bit = fls(host->ocr_avail) - 1;
    //io bus设置
    host->ios.vdd = bit;
    if (mmc_host_is_spi(host))
        host->ios.chip_select = MMC_CS_HIGH;
    else
        host->ios.chip_select = MMC_CS_DONTCARE;
    host->ios.bus_mode = MMC_BUSMODE_PUSHPULL;
    host->ios.power_mode = MMC_POWER_UP;    //上电状态
    host->ios.bus_width = MMC_BUS_WIDTH_1;
    host->ios.timing = MMC_TIMING_LEGACY;
    mmc_set_ios(host);    //一开始用单线模式初始化,最终调用sunxi_mmc_set_ios,设置线宽,总线,时序

    /* Set signal voltage to 3.3V */
    __mmc_set_signal_voltage(host, MMC_SIGNAL_VOLTAGE_330);    //最终调用sunxi_mmc_signal_voltage_switch

    /*
     * This delay should be sufficient to allow the power supply
     * to reach the minimum voltage.
     */
    mmc_delay(10);

    host->ios.clock = host->f_init;

    host->ios.power_mode = MMC_POWER_ON;    //工作状态
    mmc_set_ios(host);

    /*
     * This delay must be at least 74 clock sizes, or 1 ms, or the
     * time required to reach a stable voltage.
     */
    mmc_delay(10);

    mmc_host_clk_release(host);    //没有定义CONFIG_MMC_CLKGATE 所有gateclock相关都是空操作
}

void mmc_detect_change(struct mmc_host *host, unsigned long delay)
{
    host->detect_change = 1;

    //wake_lock(&host->detect_wake_lock);
    if(!(host->caps&MMC_CAP_NEEDS_POLL))
        wake_lock(&host->detect_wake_lock);    //唤醒内核
    mmc_schedule_delayed_work(&host->detect, delay);    //主动执行队列mmc_rescan
}

初始化emmc的程序已经完成,如果是sd卡,会中断或者查询方法调用检测sd卡的程序。

mmc_set_ios这个函数很重要,改变emmc的配置都在这里,会调用硬件底层相关函数。

emmc直接主动调用mmc_rescan。

以下忽略部分冗余和大量无关的逻辑判断的代码:

 1 void mmc_rescan(struct work_struct *work)
 2 {
 3     struct mmc_host *host = container_of(work, struct mmc_host, detect.work);
 4     int i;
 5     bool extend_wakelock = false;
 6     bool present         = false;
 7
 8     if (host->rescan_disable)    //rescan_disable = 0
 9         return;
10
11     /* If there is a non-removable card registered, only scan once */
12     if ((host->caps & MMC_CAP_NONREMOVABLE) && host->rescan_entered){
13         wake_unlock(&host->detect_wake_lock);
14         return;
15     }
16
17     host->rescan_entered = 1;
18
19     host->detect_change = 0;
20
21
22     mmc_claim_host(host);
23     for (i = 0; i < ARRAY_SIZE(freqs); i++) {
24         if (!mmc_rescan_try_freq(host, max(freqs[i], host->f_min))) {
25             extend_wakelock = true;    //mmc_rescan_try_freq真正的初始化
26             present = true;
27             break;        //初始化成功立刻跳出
28         }
29         if (freqs[i] <= host->f_min)
30             break;
31     }
32     mmc_release_host(host);
33
34     if (extend_wakelock)
35         wake_lock_timeout(&host->detect_wake_lock, HZ / 2);
36     else
37         wake_unlock(&host->detect_wake_lock);
38 }

核心函数是mmc_rescan_try_freq,里面做了什么?

 1 static int mmc_rescan_try_freq(struct mmc_host *host, unsigned freq)
 2 {
 3     host->f_init = freq;
 4
 5     mmc_power_up(host);
 6
 7     /*
 8      * Some eMMCs (with VCCQ always on) may not be reset after power up, so
 9      * do a hardware reset if possible.
10      */
11     mmc_hw_reset_for_init(host);    //硬件复位,调用mmc_host_ops.hw_reset = sunxi_mmc_hw_reset
12
13     mmc_go_idle(host);    //使mmc空闲
14
15     mmc_send_if_cond(host, host->ocr_avail);
16
17     /* Order‘s important: probe SDIO, then SD, then MMC */
18     if (!mmc_attach_sdio(host))    //不是sdio,不会响应对应命令
19         return 0;
20     if (!mmc_attach_sd(host))    //不是sd,不会响应对应命令
21         return 0;
22     if (!mmc_attach_mmc(host))    //zho真正attach对应的emmc
23         return 0;
24
25     mmc_power_off(host);
26     return -EIO;
27 }

mmc_attach_mmc的代码在linux-3.10/drivers/mmc/core/mmc.c
以下忽略部分冗余和大量无关的逻辑判断的代码:

  1 int mmc_attach_mmc(struct mmc_host *host)
  2 {
  3     int err;
  4     u32 ocr;
  5
  6     mmc_set_bus_mode(host, MMC_BUSMODE_OPENDRAIN);    //bus开漏
  7
  8     err = mmc_send_op_cond(host, 0, &ocr);    //配置ocr电源状态
  9
 10     mmc_attach_bus_ops(host);    //设置bus_ops,mmc_bus_ops = mmc_ops_unsafe
 11
 12
 13     host->ocr = mmc_select_voltage(host, ocr);
 14
 15     /*
 16      * Detect and init the card.
 17      */
 18     err = mmc_init_card(host, host->ocr, NULL);
 19
 20     mmc_release_host(host);
 21     err = mmc_add_card(host->card);
 22     mmc_claim_host(host);
 23     return 0;
 24
 25 }
 26
 27 static int mmc_init_card(struct mmc_host *host, u32 ocr,
 28     struct mmc_card *oldcard)
 29 {
 30     struct mmc_card *card;
 31     int err, ddr = 0;
 32     u32 cid[4];
 33     unsigned int max_dtr;
 34     u32 rocr;
 35     u8 *ext_csd = NULL;
 36
 37
 38     mmc_set_bus_mode(host, MMC_BUSMODE_OPENDRAIN);
 39
 40     /*
 41      * Since we‘re changing the OCR value, we seem to
 42      * need to tell some cards to go back to the idle
 43      * state.  We wait 1ms to give cards time to
 44      * respond.
 45      * mmc_go_idle is needed for eMMC that are asleep
 46      */
 47     mmc_go_idle(host);
 48
 49     /* The extra bit indicates that we support high capacity */
 50     err = mmc_send_op_cond(host, ocr | (1 << 30), &rocr);    //识别大容量
 51
 52     /*
 53      * Fetch CID from card.
 54      */
 55     err = mmc_all_send_cid(host, cid);    //识别card id
 56
 57     /*
 58      * Allocate card structure.
 59      */
 60     card = mmc_alloc_card(host, &mmc_type);    //重要,后续分析
 61
 62
 63     card->type = MMC_TYPE_MMC;
 64     card->rca = 1;
 65     memcpy(card->raw_cid, cid, sizeof(card->raw_cid));
 66
 67     /*
 68      * For native busses:  set card RCA and quit open drain mode.
 69      */
 70
 71     err = mmc_set_relative_addr(card);
 72
 73     mmc_set_bus_mode(host, MMC_BUSMODE_PUSHPULL);
 74
 75     /*
 76      * Fetch CSD from card.
 77      */
 78     err = mmc_send_csd(card, card->raw_csd);
 79
 80     err = mmc_decode_csd(card);
 81
 82     err = mmc_decode_cid(card);
 83
 84     /*
 85      * Select card, as all following commands rely on that.
 86      */
 87
 88     err = mmc_select_card(card);
 89
 90     /*
 91      * Fetch and process extended CSD.
 92      */
 93
 94     err = mmc_get_ext_csd(card, &ext_csd);
 95
 96     err = mmc_read_ext_csd(card, ext_csd);
 97
 98     /* If doing byte addressing, check if required to do sector
 99      * addressing.  Handle the case of <2GB cards needing sector
100      * addressing.  See section 8.1 JEDEC Standard JED84-A441;
101      * ocr register has bit 30 set for sector addressing.
102      */
103     if (!(mmc_card_blockaddr(card)) && (rocr & (1<<30)))
104         mmc_card_set_blockaddr(card);
105
106     /* Erase size depends on CSD and Extended CSD */
107     mmc_set_erase_size(card);
108
109     /*
110      * Activate high speed (if supported)
111      */
112     if (card->ext_csd.hs_max_dtr != 0) {
113         err = 0;
114         if (card->ext_csd.hs_max_dtr > 52000000 &&
115             host->caps2 & MMC_CAP2_HS200)
116             err = mmc_select_hs200(card);
117         else if    (host->caps & MMC_CAP_MMC_HIGHSPEED)
118             //err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,
119             //         EXT_CSD_HS_TIMING, 1,
120             //         card->ext_csd.generic_cmd6_time);
121             err = __mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,
122                    EXT_CSD_HS_TIMING, 1,
123                    card->ext_csd.generic_cmd6_time,
124                    true, true, true);
125
126         if (card->ext_csd.hs_max_dtr > 52000000 &&
127             host->caps2 & MMC_CAP2_HS200) {
128             mmc_card_set_hs200(card);
129             mmc_set_timing(card->host,
130                        MMC_TIMING_MMC_HS200);
131         } else {
132             mmc_card_set_highspeed(card);
133             mmc_set_timing(card->host, MMC_TIMING_MMC_HS);
134         }
135     }
136
137     /*
138      * Compute bus speed.
139      */
140     max_dtr = (unsigned int)-1;
141
142     if (mmc_card_highspeed(card) || (mmc_card_hs200(card)|| mmc_card_hs400(card))) {
143         if (max_dtr > card->ext_csd.hs_max_dtr)
144             max_dtr = card->ext_csd.hs_max_dtr;
145         if (mmc_card_highspeed(card) && (max_dtr > 52000000))
146             max_dtr = 52000000;
147     } else if (max_dtr > card->csd.max_dtr) {
148         max_dtr = card->csd.max_dtr;
149     }
150
151     mmc_set_clock(host, max_dtr);
152     //mmc_set_ios(host)一开始用单线模式初始化,最终调用sunxi_mmc_set_ios
153     //设置线宽,总线,时序
154
155     /*
156      * Indicate HS200 SDR mode (if supported).
157      */
158     if (mmc_card_hs200(card)) {
159         u32 ext_csd_bits;
160         u32 bus_width = card->host->ios.bus_width;
161
162
163         if(host->caps2 & MMC_CAP2_HS400){
164             //pr_info("************%s: %s,%d************\n",
165             //    mmc_hostname(card->host),__FUNCTION__,__LINE__);
166             err =  mmc_select_hs400(card);
167         }
168
169
170         if(mmc_card_hs400(card)){
171             ext_csd_bits = EXT_CSD_DDR_BUS_WIDTH_8;
172             err = mmc_select_powerclass(card, ext_csd_bits, ext_csd);    //配置mmc电源
173         }else{
174             ext_csd_bits = (bus_width == MMC_BUS_WIDTH_8) ?
175                     EXT_CSD_BUS_WIDTH_8 : EXT_CSD_BUS_WIDTH_4;
176             err = mmc_select_powerclass(card, ext_csd_bits, ext_csd);
177         }
178     }
179
180     /*
181      * Enable HPI feature (if supported)
182      */
183     if (card->ext_csd.hpi) {
184         err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,
185                 EXT_CSD_HPI_MGMT, 1,
186                 card->ext_csd.generic_cmd6_time);
187
188
189         card->ext_csd.hpi_en = 1;
190     }
191
192     host->card = card;
193
194     mmc_free_ext_csd(ext_csd);
195     return 0;
196 }

调用完mmc_attach_mmc,card和host已经准备就绪,怎么通知drv呢?当然需要device!

剩下mmc_alloc_card和mmc_add_card没有分析完,代码都在linux-3.10/drivers/mmc/core/bus.c
以下忽略部分冗余代码:

  1 struct mmc_card *mmc_alloc_card(struct mmc_host *host, struct device_type *type)
  2 {
  3     struct mmc_card *card;
  4
  5     card = kzalloc(sizeof(struct mmc_card), GFP_KERNEL);
  6     if (!card)
  7         return ERR_PTR(-ENOMEM);
  8
  9     card->host = host;
 10
 11     device_initialize(&card->dev);
 12
 13     card->dev.parent = mmc_classdev(host);
 14     card->dev.bus = &mmc_bus_type;    //dev.bus,device_attach优先选择bus->probe
 15     card->dev.release = mmc_release_card;
 16     card->dev.type = type;
 17
 18     return card;
 19 }
 20
 21 /*
 22  * This currently matches any MMC driver to any MMC card - drivers
 23  * themselves make the decision whether to drive this card in their
 24  * probe method.
 25  */
 26 static int mmc_bus_match(struct device *dev, struct device_driver *drv)
 27 {
 28     return 1;    //只要在这个总线就匹配成功
 29 }
 30
 31 static int mmc_bus_probe(struct device *dev)
 32 {
 33     struct mmc_driver *drv = to_mmc_driver(dev->driver);
 34     struct mmc_card *card = mmc_dev_to_card(dev);
 35
 36     return drv->probe(card);
 37 }
 38
 39 static struct bus_type mmc_bus_type = {
 40     .name        = "mmc",
 41     .dev_attrs    = mmc_dev_attrs,
 42     .match        = mmc_bus_match,
 43     .uevent        = mmc_bus_uevent,
 44     .probe        = mmc_bus_probe,
 45     .remove        = mmc_bus_remove,
 46     .pm        = &mmc_bus_pm_ops,
 47 };
 48
 49 int mmc_register_bus(void)    //在linux-3.10/drivers/mmc/core/core.c里subsys_initcall(mmc_init)被调用
 50 {
 51     return bus_register(&mmc_bus_type);
 52 }
 53
 54 int mmc_add_card(struct mmc_card *card)
 55 {
 56     int ret;
 57     const char *type;
 58     const char *uhs_bus_speed_mode = "";
 59     static const char *const uhs_speeds[] = {
 60         [UHS_SDR12_BUS_SPEED] = "SDR12 ",
 61         [UHS_SDR25_BUS_SPEED] = "SDR25 ",
 62         [UHS_SDR50_BUS_SPEED] = "SDR50 ",
 63         [UHS_SDR104_BUS_SPEED] = "SDR104 ",
 64         [UHS_DDR50_BUS_SPEED] = "DDR50 ",
 65     };
 66
 67     dev_set_name(&card->dev, "%s:%04x", mmc_hostname(card->host), card->rca);
 68
 69     switch (card->type) {
 70     case MMC_TYPE_MMC:
 71         type = "MMC";
 72         break;
 73     ....
 74     ....
 75     default:
 76         type = "?";
 77         break;
 78     }
 79
 80     if (mmc_sd_card_uhs(card) &&
 81         (card->sd_bus_speed < ARRAY_SIZE(uhs_speeds)))
 82         uhs_bus_speed_mode = uhs_speeds[card->sd_bus_speed];
 83
 84
 85     pr_info("%s: new %s%s%s%s%s card at address %04x\n",
 86         mmc_hostname(card->host),
 87         mmc_card_uhs(card) ? "ultra high speed " :
 88         (mmc_card_highspeed(card) ? "high speed " : ""),
 89         mmc_card_hs400(card) ? "HS400 " :
 90         (mmc_card_hs200(card) ? "HS200 " : ""),
 91         mmc_card_ddr_mode(card) ? "DDR " : "",
 92         uhs_bus_speed_mode, type, card->rca);
 93
 94     mmc_init_context_info(card->host);    //emmc队列等信息(block设备会用到)
 95
 96     ret = device_add(&card->dev);    //等待driver
 97
 98     mmc_card_set_present(card);
 99
100     return 0;
101 }
102
103 /**
104  *    mmc_register_driver - register a media driver
105  *    @drv: MMC media driver
106  */
107 int mmc_register_driver(struct mmc_driver *drv)
108 {
109     drv->drv.bus = &mmc_bus_type;
110     return driver_register(&drv->drv);
111 }

现在device这边已经准备就绪,就差某个drv调用mmc_register_driver进行match,最后调用某个drv->probe。

其实在linux-3.10/drivers/mmc/card/block.c就调用了mmc_register_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 };
10
11 static int __init mmc_blk_init(void)
12 {
13     int res;
14
15     max_devices = 256 / perdev_minors;    //perdev_minors = 8
16
17     res = register_blkdev(MMC_BLOCK_MAJOR, "mmc");    //注册块设备
18
19     res = mmc_register_driver(&mmc_driver);    //我们要找的目标函数
20
21     return 0;
22 }
23
24 static void __exit mmc_blk_exit(void)
25 {
26     mmc_unregister_driver(&mmc_driver);
27     unregister_blkdev(MMC_BLOCK_MAJOR, "mmc");
28 }
29
30 module_init(mmc_blk_init);
31 module_exit(mmc_blk_exit);
32
33 static int mmc_blk_probe(struct mmc_card *card)
34 {
35     struct mmc_blk_data *md, *part_md;
36     char cap_str[10];
37
38     md = mmc_blk_alloc(card);
39
40     string_get_size((u64)get_capacity(md->disk) << 9, STRING_UNITS_2,
41             cap_str, sizeof(cap_str));
42     pr_info("%s: %s %s %s %s\n",
43         md->disk->disk_name, mmc_card_id(card), mmc_cardrd_name(card),
44         cap_str, md->read_only ? "(ro)" : "");
45
46     if (mmc_blk_alloc_parts(card, md))
47         goto out;
48
49     mmc_set_drvdata(card, md);
50     mmc_fixup_device(card, blk_fixups);
51
52     mmc_add_disk(md);
53
54     list_for_each_entry(part_md, &md->part, part) {
55         mmc_add_disk(part_md);
56
57     }
58     return 0;
59 }

这里暂时没时间分析block.c里面的函数了,是比较复杂。

总结emmc初始化流程:

sunxi_mmc_probe---->mmc_alloc_host---->mmc_add_host---->mmc_start_host---->mmc_rescan---->mmc_rescan_try_freq

---->mmc_attach_mmc---->mmc_init_card---->mmc_alloc_card---->mmc_add_card + mmc_blk_init---->mmc_blk_probe......

时间: 2024-10-07 01:16:20

EMMC架构的相关文章

ARM Cortex-A7架构,高通210系列-MSM8909

核心板特性 A7架构 4核(4*1.1GHz(A7)) 产品尺寸小,便于客户集成,减少产品体积: 支持4G LTE超高速上网,单板兼容移动/联通/电信2G/3G/4G: 支持2+32存储器,Micro SD支持32G: 尺寸小.集成度高.大板布板灵活; 支持全网通.wifi.蓝牙.GPS; 支持 HD(1280*720) 接口丰富,可扩展性强 Sim 卡*2/MicroSD 卡*1/IIC*4/串口*2/USB(OTG)*1/3.5mm 耳机*1/GPIO *20/ADC*2/PWM*1 高通骁

Cortex-A53架构,410系列-MSM8916(M9+)

核心板特性 产品尺寸小,便于客户集成,减少产品体积: 支持4G LTE超高速上网,单板兼容移动/联通/电信2G/3G/4G: 多达21个接口,丰富的接口配置,满足大多数客户需求: 高通骁龙8916(M9+ 高通骁龙410系列)产品首款核心板产品; 产品单面布板,可有效降低产品厚度: 产品质量稳定可靠; 提高开发效率.客户系统架构无需从零开始: 降低开发难度.客户重点放在应用方案开发上,不必关注无线网络方案: 增强可维护性.通过核心板的更迭即可实现产品的更新换代,避免重新开模,延长产品生命周期:

EMMC 介绍【转】

本文转载自:http://blog.csdn.net/u014645605/article/details/52061034 定义: eMMC (Embedded Multi Media Card) 采用统一的MMC标准接口, 把高密度NANDFlash以及MMCController封装在一颗BGA芯片中.针对Flash的特性,产品内部已经包含了Flash管理技术,包括错误探测和纠正,flash平均擦写,坏块管理,掉电保护等技术 速度: eMMC4.4的读取速度大约为104MB/s.eMMC 4

Linux mmc framework1-软件架构

[转自] http://www.wowotech.net/comm/mmc_framework_arch.html 1. 前言 由eMMC基础技术1:MMC简介中MMC.SD.SDIO的介绍可知,这三种技术都是起源于MMC技术,有很多共性,因此Linux kernel统一使用MMC framework管理所有和这三种技术有关的设备. 本文将基于eMMC基础技术1:MMC简介对MMC技术的介绍,学习Linux kernel MMC framework的软件架构. 2. 软件架构 Linux ker

Android ROM开发(二)——ROM架构以及Updater-Script脚本分析,常见的Status错误解决的方法

Android ROM开发(二)--ROM架构以及Updater-Script脚本分析,常见的Status错误解决的方法 怪自己二了.写好的不小心弄没了,如今仅仅好又一次写一些了.上篇简单的配置了一下环境.这里呢,就来讲一下相关的仅仅是点 我们先下载一个ROM.随便下,原理都是差点儿相同的,这里我就下载一个红米Note的MIUI稳定版 1.ROM结构 ROM依据厂商的定制可能有所不同,可是大体是不变的 data 内置一些软件 META-INF 脚本文件 update-binary 二进制文件 u

下载-深入浅出Netty源码剖析、Netty实战高性能分布式RPC、NIO+Netty5各种RPC架构实战演练三部曲视频教程

下载-深入浅出Netty源码剖析.Netty实战高性能分布式RPC.NIO+Netty5各种RPC架构实战演练三部曲视频教程 第一部分:入浅出Netty源码剖析 第二部分:Netty实战高性能分布式RPC 第三部分:NIO+Netty5各种RPC架构实战演练

sqlserver 全库查询 带架构

网上现有的全库查询,无法识别自定义架构的数据库结构: declare @str nvarchar(10) declare @tablename varchar(50) declare @colname varchar(50) declare @counts int declare @sql nvarchar(2000)--以上定义变量 declare cur1 cursor for select a.name tablename,B.name colname from sys.objects a

Java精品高级课,架构课,java8新特性,P2P金融项目,程序设计,功能设计,数据库设计,第三方支付,web安全,视频教程

36套精品Java架构师,高并发,高性能,高可用,分布式,集群,电商,缓存,性能调优,设计模式,项目实战,P2P金融项目,大型分布式电商实战视频教程 视频课程包含: 高级Java架构师包含:Spring boot.Spring  cloud.Dubbo.Elasticsearch,Redis.ActiveMQ.Nginx.Mycat.Spring.MongoDB.ZeroMQ.Git.Nosql.Jvm.Mecached.Netty.Nio.Mina.java8新特性,P2P金融项目,程序设计,

搭建连接MySql的三层架构的ASP.NetCore2.0的WebApi

里我们用三层架构搭建一个连接MySql的ASP.netCore模板的WebApi项目 首先添加WebApi项目(ASP.NetCore版本) 右键解决方案>新建项目> 选择Web>ASP.NET Core Web应用程序(.NET Core) 选择Web API 此时的目录结构: 添加实体层Entity 右键添加>新建项目>.Net Core类库 添加后的目录结构 BaseEntity: using System; using System.Collections.Gener