[emmc] emmc总线设置

一、说明

1、设置总线时需要设置的内容

  • 时钟频率
    host需要提供给emmc对应的时钟。如上述所说,不同的总线速度模式对应不同的最大时钟频率。
    只需要设置host侧即可。
  • 总线速度模式的设置
    这是host和emmc card通讯的一种时序规范,因此,host和emmc的总线速度模式必须匹配起来才能正常通讯。
    需要设置host侧和emmc侧。
  • 总线宽度模式的设置
    host和emmc通讯的DATA脚数量的定义,同样会影响他们之间的通讯。因此,双方也必须规定一个固定的宽度来进行后续的数据传输。
    需要设置host侧和emmc侧。
  • 信号电压的设置
    也就是IO电压。对于sd card来说,card侧需要知道host提供的信号电压(3.3V或者1.8V)。但是emmc并不需要。
    只需要设置host侧即可。

2、emmc支持的总线速度模式

信号电压、时钟频率、总线宽度很大程度上都取决于总线速度模式。以下来看一下他们的关系。
从《emmc 5.1》协议上看,有如下几种总线速度模式

  • 总线速度模式说明
    • legacy mode
      单边采样(SDR),支持3V/1.8V/1.2V的信号电压,总线宽度支持1、4、8bit模式。
      最大频率支持到26Mhz。
      对应kernel中host的MMC_TIMING_LEGACY时序。
    • HS mode
      单边采样(SDR),支持3V/1.8V/1.2V的信号电压,总线宽度支持1、4、8bit模式。
      最大频率支持到52Mhz
      对应kernel中host的MMC_TIMING_MMC_HS时序。
    • HSDDR mode
      双边采样(DDR),支持3V/1.8V/1.2V的信号电压,总线宽度支持4、8bit模式。
      最大频率支持到52Mhz
      对应kernel中host的MMC_TIMING_UHS_DDR50时序。
    • HS200 mode
      单边采样(SDR),支持1.8V/1.2V的信号电压,总线宽度支持4、8bit模式。
      最大频率支持到200Mhz
      对应kernel中host的MMC_TIMING_MMC_HS200时序。
    • HS400 mode
      双边采样(DDR),支持1.8V/1.2V的信号电压,总线宽度支持8bit模式。
      最大频率支持到200Mhz。
      对应kernel中host的MMC_TIMING_MMC_HS400时序。

二、总线参数设置方法

1、时钟频率的设置

  • 各个总线速度模式下的最大时钟频率在协议中已经是固定的了。
    在代码中定义如下
   #define MMC_HIGH_26_MAX_DTR             26000000
   #define MMC_HIGH_52_MAX_DTR             52000000
   #define MMC_HIGH_DDR_MAX_DTR     52000000
   #define MMC_HS200_MAX_DTR    200000000
   #define MMC_HS400_MAX_DTR    200000000
  • 设置时钟频率方法
    mmc core已经提供了接口,mmc_set_clock,直接调用即可。
    示例如下
    mmc_set_clock(host, MMC_HIGH_52_MAX_DTR);

2、总线速度模式的设置

  • 如何获取emmc支持的总线速度模式?
    从ext_csd中获取:

kernel中定义如下

#define EXT_CSD_CARD_TYPE_26    (1<<0)    /* Card can run at 26MHz */
#define EXT_CSD_CARD_TYPE_52    (1<<1)    /* Card can run at 52MHz */
#define EXT_CSD_CARD_TYPE_MASK    0xFF    /* Mask out reserved bits */
#define EXT_CSD_CARD_TYPE_DDR_1_8V  (1<<2)   /* Card can run at 52MHz */     /* DDR mode @1.8V or 3V I/O */
#define EXT_CSD_CARD_TYPE_DDR_1_2V  (1<<3)   /* Card can run at 52MHz */     /* DDR mode @1.2V I/O */
#define EXT_CSD_CARD_TYPE_DDR_52       (EXT_CSD_CARD_TYPE_DDR_1_8V  | EXT_CSD_CARD_TYPE_DDR_1_2V)
#define EXT_CSD_CARD_TYPE_SDR_1_8V    (1<<4)    /* Card can run at 200MHz */
#define EXT_CSD_CARD_TYPE_SDR_1_2V    (1<<5)    /* Card can run at 200MHz */    /* SDR mode @1.2V I/O */
#define EXT_CSD_CARD_TYPE_HS200        (EXT_CSD_CARD_TYPE_SDR_1_8V  | EXT_CSD_CARD_TYPE_SDR_1_2V)
#define EXT_CSD_CARD_TYPE_HS400_1_8V    (1<<6)    /* Card can run at 200MHz */    /* DDR mode @1.8V I/O */
#define EXT_CSD_CARD_TYPE_HS400_1_2V    (1<<7)    /* Card can run at 200MHz */    /* DDR mode @1.2V I/O */
#define EXT_CSD_CARD_TYPE_HS400        (EXT_CSD_CARD_TYPE_HS400_1_8V  | EXT_CSD_CARD_TYPE_HS400_1_2V)

使用示例:

card->ext_csd.card_type & EXT_CSD_CARD_TYPE_SDR_1_2V
  • 如何设置emmc的总线速度模式
    主要是设置ext_csd的HS_TIMINIG寄存器

使用示例

    //HS SDR(mmc_select_hs):
       err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,EXT_CSD_HS_TIMING, 1,card->ext_csd.generic_cmd6_time);
    //HS DDR(mmc_select_hsddr->mmc_select_hs):
        err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,EXT_CSD_HS_TIMING, 1,card->ext_csd.generic_cmd6_time);
        其ddr和总线宽度模式一起设置。
    //HS 200(mmc_select_hs200)
        err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL, EXT_CSD_HS_TIMING, 2, 0);
    //HS 400(mmc_select_hs400)
       err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL, EXT_CSD_HS_TIMING, 3, 0);
  • 如何获取host支持的总线速度模式
    根据mmc_host的caps属性和caps2属性进行判断。
#define MMC_CAP2_HS400_1_8V    (1 << 22)        /* can support */
#define MMC_CAP2_HS400_1_2V    (1 << 23)        /* can support */
#define MMC_CAP2_HS400        (MMC_CAP2_HS400_1_8V | MMC_CAP2_HS400_1_2V)
#define MMC_CAP2_HS200_1_8V_SDR    (1 << 5)        /* can support */
#define MMC_CAP2_HS200_1_2V_SDR    (1 << 6)        /* can support */
#define MMC_CAP2_HS200        (MMC_CAP2_HS200_1_8V_SDR | MMC_CAP2_HS200_1_2V_SDR)
#define MMC_CAP_1_8V_DDR    (1 << 11)    /* can support */   /* DDR mode at 1.8V */
#define MMC_CAP_1_2V_DDR    (1 << 12)    /* can support */   /* DDR mode at 1.2V */
#define MMC_CAP_HSDDR        (MMC_CAP_1_8V_DDR | MMC_CAP_1_2V_DDR)

使用示例

host->caps2 & MMC_CAP2_HS200
  • 设置host的总线速度模式
    调用mmc_set_timing来设置host的总线速度模式。
    示例如下
  mmc_set_timing(host, MMC_TIMING_MMC_HS);
  mmc_set_timing(host, MMC_TIMING_UHS_DDR50);
  mmc_set_timing(host, MMC_TIMING_MMC_HS200);
  mmc_set_timing(host, MMC_TIMING_MMC_HS400);

3、总线宽度模式的设置

  • 如何获取emmc支持的总线宽度模式?
    没有寄存器可以读取card支持的总线宽度模式。直接按照8bit、4bit、1bit的顺序进行尝试(假如host支持8bit的情况下)。
    先以相应bit的SDR的模式进行尝试,如果成功并且需要设置成DDR模式,那么再以相应bit的DDR模式进行尝试。

  • 如何设置emmc的总线宽度模式?
    通过ext_csd寄存器进行设置

使用示例如下:

#define EXT_CSD_BUS_WIDTH_1    0    /* Card is in 1 bit mode */
#define EXT_CSD_BUS_WIDTH_4    1    /* Card is in 4 bit mode */
#define EXT_CSD_BUS_WIDTH_8    2    /* Card is in 8 bit mode */
#define EXT_CSD_DDR_BUS_WIDTH_4       5    /* Card is in 4 bit DDR mode */
#define EXT_CSD_DDR_BUS_WIDTH_8       6    /* Card is in 8 bit DDR mode */

mmc_switch(card, EXT_CSD_CMD_SET_NORMAL, EXT_CSD_BUS_WIDTH, EXT_CSD_DDR_BUS_WIDTH_8, card->ext_csd.generic_cmd6_time);
  • 如何读取host支持的总线宽度?
    通过host->caps属性来进行判断(1bit是肯定要支持的)
    使用示例如下
    #define MMC_CAP_4_BIT_DATA    (1 << 0)    /* Can the host do 4 bit transfers */
    #define MMC_CAP_8_BIT_DATA    (1 << 6)    /* Can the host do 8 bit transfers */
   host->caps & MMC_CAP_8_BIT_DATA
  • 如何设置host的总线宽度?
    调用mmc_set_bus_width来设置host的总线宽度
    使用示例如下:
   #define MMC_BUS_WIDTH_1        0
   #define MMC_BUS_WIDTH_4        2
   #define MMC_BUS_WIDTH_8        3
   mmc_set_bus_width(host, MMC_BUS_WIDTH_4);
   mmc_set_bus_width(host, MMC_BUS_WIDTH_8);
  • 代码说明
    使用mmc_select_bus_width来选择一个合适的总线宽度,而不依赖于总线速度模式。
static int mmc_select_bus_width(struct mmc_card *card, int ddr, u8 *ext_csd)
{
    struct mmc_host *host;
    static unsigned ext_csd_bits[][2] = {
        { EXT_CSD_BUS_WIDTH_8, EXT_CSD_DDR_BUS_WIDTH_8 },   // 前面是SDR对应的模式,后面是DDR对应的模式
        { EXT_CSD_BUS_WIDTH_4, EXT_CSD_DDR_BUS_WIDTH_4 },
        { EXT_CSD_BUS_WIDTH_1, EXT_CSD_BUS_WIDTH_1 },
    };
    static unsigned bus_widths[] = {
        MMC_BUS_WIDTH_8, MMC_BUS_WIDTH_4, MMC_BUS_WIDTH_1
    };
    unsigned idx, bus_width = 0;
    int err = 0;
    host = card->host;
    if ((card->csd.mmca_vsn < CSD_SPEC_VER_4) || !(host->caps & (MMC_CAP_4_BIT_DATA | MMC_CAP_8_BIT_DATA)))
      // 首先判断emmc是否支持,以及host是否支持,只能往4bit或者8bit去设置
        goto out;

    if (host->caps & MMC_CAP_8_BIT_DATA)
        idx = 0;
    else
        idx = 1;

    for (; idx < ARRAY_SIZE(bus_widths); idx++) {   // 先尝试以SDR对应的总线宽度模式去设置,从8bit-》4bit-》1bit的方式去设置
        bus_width = bus_widths[idx];
        if (bus_width == MMC_BUS_WIDTH_1)
            ddr = 0; /* no DDR for 1-bit width */
        err = mmc_select_powerclass(card, ext_csd_bits[idx][0], ext_csd);

        err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,  EXT_CSD_BUS_WIDTH, ext_csd_bits[idx][0], card->ext_csd.generic_cmd6_time);   // 设置emmc总线宽度
        if (!err) {
            mmc_set_bus_width(host, bus_width);   // 设置host的总线宽度
        }
    }

    if (!err && ddr) {   // 如果是DDR模式,再设置相应的总线宽度为ddr模式
        err = mmc_select_powerclass(card, ext_csd_bits[idx][1], ext_csd);
        err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL, EXT_CSD_BUS_WIDTH, ext_csd_bits[idx][1], card->ext_csd.generic_cmd6_time);
            // 重新设置emmc的总线宽度模式为ddr模式
    }

out:
    return err;
}

4、信号电压的设置

  • 如何获取emmc支持的信号电压模式?
    支持的信号电压模式和总线速度模式是一起定义的。从ext_csd寄存器中读取

#define EXT_CSD_CARD_TYPE_26    (1<<0)    /* Card can run at 26MHz */
#define EXT_CSD_CARD_TYPE_52    (1<<1)    /* Card can run at 52MHz */
#define EXT_CSD_CARD_TYPE_MASK    0xFF    /* Mask out reserved bits */
#define EXT_CSD_CARD_TYPE_DDR_1_8V  (1<<2)   /* Card can run at 52MHz */     /* DDR mode @1.8V or 3V I/O */
#define EXT_CSD_CARD_TYPE_DDR_1_2V  (1<<3)   /* Card can run at 52MHz */     /* DDR mode @1.2V I/O */
#define EXT_CSD_CARD_TYPE_DDR_52       (EXT_CSD_CARD_TYPE_DDR_1_8V  | EXT_CSD_CARD_TYPE_DDR_1_2V)
#define EXT_CSD_CARD_TYPE_SDR_1_8V    (1<<4)    /* Card can run at 200MHz */
#define EXT_CSD_CARD_TYPE_SDR_1_2V    (1<<5)    /* Card can run at 200MHz */    /* SDR mode @1.2V I/O */
#define EXT_CSD_CARD_TYPE_HS200        (EXT_CSD_CARD_TYPE_SDR_1_8V  | EXT_CSD_CARD_TYPE_SDR_1_2V)
#define EXT_CSD_CARD_TYPE_HS400_1_8V    (1<<6)    /* Card can run at 200MHz */    /* DDR mode @1.8V I/O */
#define EXT_CSD_CARD_TYPE_HS400_1_2V    (1<<7)    /* Card can run at 200MHz */    /* DDR mode @1.2V I/O */
#define EXT_CSD_CARD_TYPE_HS400        (EXT_CSD_CARD_TYPE_HS400_1_8V  | EXT_CSD_CARD_TYPE_HS400_1_2V)

使用示例:

card->ext_csd.card_type & EXT_CSD_CARD_TYPE_SDR_1_2V
  • 如何设置emmc的信号电压?
    似乎不需要另外通知emmc信号电压要发生切换了(sd card则是需要的)

  • 如何获取host支持的信号电压?
    同样是和支持的总线速度模式定义在一起的。
    根据mmc_host的caps属性和caps2属性进行判断
    使用示例如下:

#define MMC_CAP2_HS400_1_8V    (1 << 22)        /* can support */
#define MMC_CAP2_HS400_1_2V    (1 << 23)        /* can support */
#define MMC_CAP2_HS400        (MMC_CAP2_HS400_1_8V | MMC_CAP2_HS400_1_2V)
#define MMC_CAP2_HS200_1_8V_SDR    (1 << 5)        /* can support */
#define MMC_CAP2_HS200_1_2V_SDR    (1 << 6)        /* can support */
#define MMC_CAP2_HS200        (MMC_CAP2_HS200_1_8V_SDR | MMC_CAP2_HS200_1_2V_SDR)
#define MMC_CAP_1_8V_DDR    (1 << 11)    /* can support */   /* DDR mode at 1.8V */
#define MMC_CAP_1_2V_DDR    (1 << 12)    /* can support */   /* DDR mode at 1.2V */
#define MMC_CAP_HSDDR        (MMC_CAP_1_8V_DDR | MMC_CAP_1_2V_DDR)
// 补充,似乎HS SDR虽然支持1.8V和1.2V,但是代码上并没有做相应的判断
eg:host->caps2 & MMC_CAP2_HS200_1_2V_SDR
  • 如何设置host输出的信号电压
    调用__mmc_set_signal_voltage来对host输出的信号电压进行设置。
    使用示例如下:
#define MMC_SIGNAL_VOLTAGE_330    0
#define MMC_SIGNAL_VOLTAGE_180    1
#define MMC_SIGNAL_VOLTAGE_120    2
__mmc_set_signal_voltage(host, MMC_SIGNAL_VOLTAGE_120)
__mmc_set_signal_voltage(host, MMC_SIGNAL_VOLTAGE_180)

三、总线设置整体代码

1、总线模式选择

刚上电的时候是处于legacy模式。在设置速度总线模式的过程中,去设置总线宽度、信号电压、时钟等等。
会按照hs400 > hs200 > hsddr > hs的顺序去尝试切换。其实现在mmc_select_bus_speed函数中。

static int mmc_select_bus_speed(struct mmc_card *card, u8 *ext_csd)
{
    int err = 0;

    BUG_ON(!card);
    if (!mmc_select_hs400_strobe(card, ext_csd))    // 先尝试切换到hs400模式,并设置strobe
        goto out;
    if (!mmc_select_hs400(card, ext_csd))    // 尝试切换到hs400模式
        goto out;
    if (!mmc_select_hs200(card, ext_csd))    // 尝试切换到hs200模式
        goto out;
    if (!mmc_select_hsddr(card, ext_csd))    // 尝试切换到hsddr模式
        goto out;
    if (!mmc_select_hs(card, ext_csd))    // 尝试切换到hs模式
        goto out;

        // 上述都失败的情况下,还是处于legacy模式
    mmc_set_clock(card->host, card->csd.max_dtr);
    err = mmc_select_bus_width(card, 0, ext_csd);

out:
    return err;
}

后面分别对mmc_select_hs、mmc_select_hsddr、mmc_select_hs200、mmc_select_hs400这几个函数进行说明

2、mmc_select_hs

尝试设置总线模式为hs模式。

static int mmc_select_hs(struct mmc_card *card, u8 *ext_csd)
{
    int err = 0;
    struct mmc_host *host;

    host = card->host;
        // hs模式:host——》MMC_CAP_MMC_HIGHSPEED
        // hs模式:emmc——》EXT_CSD_CARD_TYPE_52
    if (!(host->caps & MMC_CAP_MMC_HIGHSPEED) || !(card->ext_csd.card_type & EXT_CSD_CARD_TYPE_52)) {
        err = -EOPNOTSUPP;
        goto out;
    }

        // 切换emmc的总线速度模式
    err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL, EXT_CSD_HS_TIMING, 1, card->ext_csd.generic_cmd6_time);

    if (err && err != -EBADMSG)
        goto out;
        // 切换成功,后续emmc都使用hs模式
    mmc_card_set_highspeed(card);
        // 切换host的总线速度模式(时序)
    mmc_set_timing(host, MMC_TIMING_MMC_HS);
        // 设置时钟频率
    mmc_set_clock(host, MMC_HIGH_52_MAX_DTR);
        // 切换总线宽度
    err = mmc_select_bus_width(card, 0, ext_csd);

out:
    if (err && err != -EOPNOTSUPP)
        pr_warning("%s: Switch to HighSpeed mode failed (err:%d)\n",
                mmc_hostname(host), err);
    return err;
}

注意,并没有为hs模式设置总线信号电压,所以其信号电压还是3V.

3、mmc_select_hsddr

尝试设置总线模式为hsddr模式。

static int mmc_select_hsddr(struct mmc_card *card, u8 *ext_csd)
{
    int ddr = 0, err = 0;
    struct mmc_host *host;

    host = card->host;

    if (!(host->caps & MMC_CAP_HSDDR) ||    // 判断host是否支持hsddr模式
        !(card->ext_csd.card_type & EXT_CSD_CARD_TYPE_DDR_52)) {    // 判断emmc是否支持hsddr模式
        err = -EOPNOTSUPP;
        goto out;
    }

    err = mmc_select_hs(card, ext_csd);    // 先尝试设置为hs模式,对于emmc来说,hs_timing的值是一样的,都是EXT_CSD_HS_TIMING
    if (err)
        goto out;
    mmc_card_clr_highspeed(card);

    if ((card->ext_csd.card_type & EXT_CSD_CARD_TYPE_DDR_1_8V)
        && ((host->caps & (MMC_CAP_1_8V_DDR | MMC_CAP_UHS_DDR50))
            == (MMC_CAP_1_8V_DDR | MMC_CAP_UHS_DDR50)))
            ddr = MMC_1_8V_DDR_MODE;    // 判断是否要设置成ddr 1.8V模式
    else if ((card->ext_csd.card_type & EXT_CSD_CARD_TYPE_DDR_1_2V)
        && ((host->caps & (MMC_CAP_1_2V_DDR | MMC_CAP_UHS_DDR50))
            == (MMC_CAP_1_2V_DDR | MMC_CAP_UHS_DDR50)))
            ddr = MMC_1_2V_DDR_MODE;    // 判断是否要设置成ddr 1.2V模式

    err = mmc_select_bus_width(card, ddr, ext_csd);    // 重新设置总线宽度,在这里面选择DDR模式的总线宽度模式
    if (err)
        goto out;

    if (host->ios.bus_width == MMC_BUS_WIDTH_1) {    // hsddr模式不支持1bit的总线宽度
        pr_err("%s: failed to switch to wide bus\n",
            mmc_hostname(host));
        goto out;
    }

    if (ddr == MMC_1_2V_DDR_MODE) {
        err = __mmc_set_signal_voltage(host, MMC_SIGNAL_VOLTAGE_120);   // 切换信号电压为1.2V
        if (err)
            goto out;
    }
    mmc_card_set_ddr_mode(card);
    mmc_set_timing(host, MMC_TIMING_UHS_DDR50);    // 设置host的总线速度模式为MMC_TIMING_UHS_DDR50
    mmc_set_bus_width(host, host->ios.bus_width);    // 设置host的总线宽度,其实在mmc_select_bus_width已经设置过一次了。

out:
    if (err && err != -EOPNOTSUPP)
        pr_warning("%s: Switch to HighSpeed DDR mode failed (err:%d)\n",
                mmc_hostname(host), err);
    return err;
}

4、mmc_select_hs200

尝试设置总线模式为hs200模式。

static int mmc_select_hs200(struct mmc_card *card, u8 *ext_csd)
{
    int err = 0;
    struct mmc_host *host;

    host = card->host;

    if (!(host->caps2 & MMC_CAP2_HS200) ||    // 判断host是否支持hs200模式
        !(card->ext_csd.card_type & EXT_CSD_CARD_TYPE_HS200)) {    // 判断emmc是否支持hs200模式
        err = -EOPNOTSUPP;
        goto out;
    }

        // HS200只支持1.2V或者1.8V的信号电压
    if (card->ext_csd.card_type & EXT_CSD_CARD_TYPE_SDR_1_2V &&    // 判断emmc是否支持hs200_1.2V模式
            host->caps2 & MMC_CAP2_HS200_1_2V_SDR)    // 判断host是否支持hs200_1.2V模式
        if (__mmc_set_signal_voltage(host, MMC_SIGNAL_VOLTAGE_120))    // 如果是的话切换信号电压到1.2V
            err = __mmc_set_signal_voltage(host,    MMC_SIGNAL_VOLTAGE_180);     // 否则,切换信号电压到1.8V
        // 这里很不能理解,如果不支持1.2V的话,连1.8V的信号电压都不用设置了???
        // 根据实际结果来看,确实hs200模式下如果没有设置1.2V的话,确实也就没有设置成1.8V了,而是直接使用3V的信号电压

        // 注意如下顺序:
    /*
     * For devices supporting HS200 mode, the bus width has
     * to be set before executing the tuning function. If
     * set before tuning, then device will respond with CRC
     * errors for responses on CMD line. So for HS200 the
     * sequence will be
     * 1. set bus width 4bit / 8 bit (1 bit not supported) // 先设置总线宽度,hs200不支持1bit的总线宽度       
     * 2. switch to HS200 mode    // 切换emmc和host的总线速度模式为hs200模式
     * 3. set the clock to > 52Mhz <=200MHz and    // 设置时钟,200MHZ以内
     * 4. execute tuning for HS200        // hs200需要进行tuning操作获取一个合适的采样点
     */
    err = mmc_select_bus_width(card, 0, ext_csd);    // 设置总线宽度

    /* switch to HS200 mode if bus width set successfully */
    err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL, EXT_CSD_HS_TIMING, 2, 0);   // 切换emmc的总线速度模式

    /*
     * When HS200 activation is performed as part of HS400 selection
     * set the timing appropriately
     */
    if (mmc_card_hs400(card))
        mmc_set_timing(host, MMC_TIMING_MMC_HS400);
    else
        mmc_set_timing(host, MMC_TIMING_MMC_HS200);    // 设置host的总线速度模式(时序)

    mmc_set_clock(host, MMC_HS200_MAX_DTR);    // 设置时钟

    if (host->ops->execute_tuning) {    // 进行tuning操作
        mmc_host_clk_hold(host);
        err = host->ops->execute_tuning(host,
                MMC_SEND_TUNING_BLOCK_HS200);
        mmc_host_clk_release(host);
    }
    if (err) {
        pr_warning("%s: tuning execution failed\n",
               mmc_hostname(host));
        goto out;
    }
    mmc_card_set_hs200(card);    // 设置mmc_card状态为MMC_STATE_HIGHSPEED_200

out:
    if (err && err != -EOPNOTSUPP)
        pr_warning("%s: Switch to HS200 mode failed (err:%d)\n",
                mmc_hostname(host), err);
    return err;
}

5、mmc_select_hs400

尝试设置总线模式为hs400模式。

static int mmc_select_hs400(struct mmc_card *card, u8 *ext_csd)
{
    int err = 0;
    struct mmc_host *host;

    host = card->host;

    if (!(host->caps2 & MMC_CAP2_HS400) ||    // 判断host是否支持hs400模式
        !(card->ext_csd.card_type & EXT_CSD_CARD_TYPE_HS400)) {    // 判断emmc是否支持hs400模式
        err = -EOPNOTSUPP;
        goto out;
    }

        // 根据协议emmc 5.0,不能直接切换到hs400模式,而是需要结果如下步骤
    /*
     * eMMC5.0 spec doesn't allow switching to HS400 mode from
     * HS200 mode directly. Hence follow these steps to switch
     * to HS400 mode:
     *  Enable HS200 mode
     *  Enable HighSpeed mode (The clk should be low enough
     *      to enable HighSpeed mode) - HS_TIMING is 0x1
     *  Enable DDR mode (Set bus width to 8-bit DDR)
     *  Enable HS400 mode (Set HS_TIMING to 0x3 and change
     *      frequency to <= 200MHz)
     *  Perform tuning if required
     */
    mmc_card_set_hs400(card);
    err = mmc_select_hs200(card, ext_csd);    // 先设置总线速度模式为hs200模式
    mmc_card_clr_hs200(card);

    if ((card->ext_csd.card_type & EXT_CSD_CARD_TYPE_HS400_1_2V)    // 判断emmc是否支持hs400_1.2v模式
        && (host->caps2 & MMC_CAP2_HS400_1_2V))      // 判断host是否支持hs400_1.2v模式
        if (__mmc_set_signal_voltage(host, MMC_SIGNAL_VOLTAGE_120))    // 如果是的话切换信号电压到1.2V
                err = __mmc_set_signal_voltage(host, MMC_SIGNAL_VOLTAGE_180);   // 否则,切换信号电压到1.8V
        // 这里的疑问和前面说的一样

    /*
     * Lower the clock and adjust the timing to be able
     * to switch to HighSpeed mode
     */
    mmc_set_timing(host, MMC_TIMING_LEGACY);    // 设置host总线速度模式为legacy模式
    mmc_set_clock(host, MMC_HIGH_26_MAX_DTR);    // 设置时钟为legacy的最大频率

    /* Switch to 8-bit HighSpeed DDR mode */
    err = mmc_select_hsddr(card, ext_csd);    // 设置总线速度模式为hsddr模式
    mmc_card_clr_ddr_mode(card);


    /* Switch to HS400 mode if bus width set successfully */
    err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,
                 EXT_CSD_HS_TIMING, 3, 0);    // 设置card的总线速度模式为hs400模式
    mmc_set_timing(host, MMC_TIMING_MMC_HS400);    // 设置host的总线速度模式为hs400模式
    mmc_set_clock(host, MMC_HS400_MAX_DTR);    // 设置时钟频率

    if (host->ops->execute_tuning) {
        mmc_host_clk_hold(host);
        err = host->ops->execute_tuning(host, MMC_SEND_TUNING_BLOCK_HS400);   // 执行tuning操作
        mmc_host_clk_release(host);
    }
    mmc_card_set_hs400(card);  // 设置mmc_card状态为MMC_STATE_HIGHSPEED_400

out:
    return err;
}

你可能感兴趣的:(mmc)