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、时钟频率的设置

  • 各个总线速度模式下的最大时钟频率在协议中已经是固定的了。 
    在代码中定义如下
 
  1. #define MMC_HIGH_26_MAX_DTR 26000000

  2. #define MMC_HIGH_52_MAX_DTR 52000000

  3. #define MMC_HIGH_DDR_MAX_DTR 52000000

  4. #define MMC_HS200_MAX_DTR 200000000

  5. #define MMC_HS400_MAX_DTR 200000000

  • 设置时钟频率方法 
    mmc core已经提供了接口,mmc_set_clock,直接调用即可。 
    示例如下
    mmc_set_clock(host, MMC_HIGH_52_MAX_DTR);
  • 1

2、总线速度模式的设置

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

 

kernel中定义如下

 
  1. #define EXT_CSD_CARD_TYPE_26 (1<<0) /* Card can run at 26MHz */

  2. #define EXT_CSD_CARD_TYPE_52 (1<<1) /* Card can run at 52MHz */

  3. #define EXT_CSD_CARD_TYPE_MASK 0xFF /* Mask out reserved bits */

  4. #define EXT_CSD_CARD_TYPE_DDR_1_8V (1<<2) /* Card can run at 52MHz */ /* DDR mode @1.8V or 3V I/O */

  5. #define EXT_CSD_CARD_TYPE_DDR_1_2V (1<<3) /* Card can run at 52MHz */ /* DDR mode @1.2V I/O */

  6. #define EXT_CSD_CARD_TYPE_DDR_52 (EXT_CSD_CARD_TYPE_DDR_1_8V | EXT_CSD_CARD_TYPE_DDR_1_2V)

  7. #define EXT_CSD_CARD_TYPE_SDR_1_8V (1<<4) /* Card can run at 200MHz */

  8. #define EXT_CSD_CARD_TYPE_SDR_1_2V (1<<5) /* Card can run at 200MHz */ /* SDR mode @1.2V I/O */

  9. #define EXT_CSD_CARD_TYPE_HS200 (EXT_CSD_CARD_TYPE_SDR_1_8V | EXT_CSD_CARD_TYPE_SDR_1_2V)

  10. #define EXT_CSD_CARD_TYPE_HS400_1_8V (1<<6) /* Card can run at 200MHz */ /* DDR mode @1.8V I/O */

  11. #define EXT_CSD_CARD_TYPE_HS400_1_2V (1<<7) /* Card can run at 200MHz */ /* DDR mode @1.2V I/O */

  12. #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
  • 1
  • 如何设置emmc的总线速度模式 
    主要是设置ext_csd的HS_TIMINIG寄存器

 

使用示例

 
  1. //HS SDR(mmc_select_hs):

  2. err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,EXT_CSD_HS_TIMING, 1,card->ext_csd.generic_cmd6_time);

  3. //HS DDR(mmc_select_hsddr->mmc_select_hs):

  4. err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,EXT_CSD_HS_TIMING, 1,card->ext_csd.generic_cmd6_time);

  5. 其ddr和总线宽度模式一起设置。

  6. //HS 200(mmc_select_hs200)

  7. err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL, EXT_CSD_HS_TIMING, 2, 0);

  8. //HS 400(mmc_select_hs400)

  9. err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL, EXT_CSD_HS_TIMING, 3, 0);

  • 如何获取host支持的总线速度模式 
    根据mmc_host的caps属性和caps2属性进行判断。
 
  1. #define MMC_CAP2_HS400_1_8V (1 << 22) /* can support */

  2. #define MMC_CAP2_HS400_1_2V (1 << 23) /* can support */

  3. #define MMC_CAP2_HS400 (MMC_CAP2_HS400_1_8V | MMC_CAP2_HS400_1_2V)

  4. #define MMC_CAP2_HS200_1_8V_SDR (1 << 5) /* can support */

  5. #define MMC_CAP2_HS200_1_2V_SDR (1 << 6) /* can support */

  6. #define MMC_CAP2_HS200 (MMC_CAP2_HS200_1_8V_SDR | MMC_CAP2_HS200_1_2V_SDR)

  7. #define MMC_CAP_1_8V_DDR (1 << 11) /* can support */ /* DDR mode at 1.8V */

  8. #define MMC_CAP_1_2V_DDR (1 << 12) /* can support */ /* DDR mode at 1.2V */

  9. #define MMC_CAP_HSDDR (MMC_CAP_1_8V_DDR | MMC_CAP_1_2V_DDR)

使用示例

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

  2. mmc_set_timing(host, MMC_TIMING_UHS_DDR50);

  3. mmc_set_timing(host, MMC_TIMING_MMC_HS200);

  4. mmc_set_timing(host, MMC_TIMING_MMC_HS400);

3、总线宽度模式的设置

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

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

 

使用示例如下:

 
  1. #define EXT_CSD_BUS_WIDTH_1 0 /* Card is in 1 bit mode */

  2. #define EXT_CSD_BUS_WIDTH_4 1 /* Card is in 4 bit mode */

  3. #define EXT_CSD_BUS_WIDTH_8 2 /* Card is in 8 bit mode */

  4. #define EXT_CSD_DDR_BUS_WIDTH_4 5 /* Card is in 4 bit DDR mode */

  5. #define EXT_CSD_DDR_BUS_WIDTH_8 6 /* Card is in 8 bit DDR mode */

  6.  
  7. 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是肯定要支持的) 
    使用示例如下
 
  1. #define MMC_CAP_4_BIT_DATA (1 << 0) /* Can the host do 4 bit transfers */

  2. #define MMC_CAP_8_BIT_DATA (1 << 6) /* Can the host do 8 bit transfers */

  3. host->caps & MMC_CAP_8_BIT_DATA

  • 1
  • 2
  • 3
  • 如何设置host的总线宽度? 
    调用mmc_set_bus_width来设置host的总线宽度 
    使用示例如下:
 
  1. #define MMC_BUS_WIDTH_1 0

  2. #define MMC_BUS_WIDTH_4 2

  3. #define MMC_BUS_WIDTH_8 3

  4. mmc_set_bus_width(host, MMC_BUS_WIDTH_4);

  5. mmc_set_bus_width(host, MMC_BUS_WIDTH_8);

  • 代码说明 
    使用mmc_select_bus_width来选择一个合适的总线宽度,而不依赖于总线速度模式。
 
  1. static int mmc_select_bus_width(struct mmc_card *card, int ddr, u8 *ext_csd)

  2. {

  3. struct mmc_host *host;

  4. static unsigned ext_csd_bits[][2] = {

  5. { EXT_CSD_BUS_WIDTH_8, EXT_CSD_DDR_BUS_WIDTH_8 }, // 前面是SDR对应的模式,后面是DDR对应的模式

  6. { EXT_CSD_BUS_WIDTH_4, EXT_CSD_DDR_BUS_WIDTH_4 },

  7. { EXT_CSD_BUS_WIDTH_1, EXT_CSD_BUS_WIDTH_1 },

  8. };

  9. static unsigned bus_widths[] = {

  10. MMC_BUS_WIDTH_8, MMC_BUS_WIDTH_4, MMC_BUS_WIDTH_1

  11. };

  12. unsigned idx, bus_width = 0;

  13. int err = 0;

  14. host = card->host;

  15. if ((card->csd.mmca_vsn < CSD_SPEC_VER_4) || !(host->caps & (MMC_CAP_4_BIT_DATA | MMC_CAP_8_BIT_DATA)))

  16. // 首先判断emmc是否支持,以及host是否支持,只能往4bit或者8bit去设置

  17. goto out;

  18.  
  19. if (host->caps & MMC_CAP_8_BIT_DATA)

  20. idx = 0;

  21. else

  22. idx = 1;

  23.  
  24. for (; idx < ARRAY_SIZE(bus_widths); idx++) { // 先尝试以SDR对应的总线宽度模式去设置,从8bit-》4bit-》1bit的方式去设置

  25. bus_width = bus_widths[idx];

  26. if (bus_width == MMC_BUS_WIDTH_1)

  27. ddr = 0; /* no DDR for 1-bit width */

  28. err = mmc_select_powerclass(card, ext_csd_bits[idx][0], ext_csd);

  29.  
  30. 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总线宽度

  31. if (!err) {

  32. mmc_set_bus_width(host, bus_width); // 设置host的总线宽度

  33. }

  34. }

  35.  
  36. if (!err && ddr) { // 如果是DDR模式,再设置相应的总线宽度为ddr模式

  37. err = mmc_select_powerclass(card, ext_csd_bits[idx][1], ext_csd);

  38. err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL, EXT_CSD_BUS_WIDTH, ext_csd_bits[idx][1], card->ext_csd.generic_cmd6_time);

  39. // 重新设置emmc的总线宽度模式为ddr模式

  40. }

  41.  
  42. out:

  43. return err;

  44. }

4、信号电压的设置

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

 

 
  1. #define EXT_CSD_CARD_TYPE_26 (1<<0) /* Card can run at 26MHz */

  2. #define EXT_CSD_CARD_TYPE_52 (1<<1) /* Card can run at 52MHz */

  3. #define EXT_CSD_CARD_TYPE_MASK 0xFF /* Mask out reserved bits */

  4. #define EXT_CSD_CARD_TYPE_DDR_1_8V (1<<2) /* Card can run at 52MHz */ /* DDR mode @1.8V or 3V I/O */

  5. #define EXT_CSD_CARD_TYPE_DDR_1_2V (1<<3) /* Card can run at 52MHz */ /* DDR mode @1.2V I/O */

  6. #define EXT_CSD_CARD_TYPE_DDR_52 (EXT_CSD_CARD_TYPE_DDR_1_8V | EXT_CSD_CARD_TYPE_DDR_1_2V)

  7. #define EXT_CSD_CARD_TYPE_SDR_1_8V (1<<4) /* Card can run at 200MHz */

  8. #define EXT_CSD_CARD_TYPE_SDR_1_2V (1<<5) /* Card can run at 200MHz */ /* SDR mode @1.2V I/O */

  9. #define EXT_CSD_CARD_TYPE_HS200 (EXT_CSD_CARD_TYPE_SDR_1_8V | EXT_CSD_CARD_TYPE_SDR_1_2V)

  10. #define EXT_CSD_CARD_TYPE_HS400_1_8V (1<<6) /* Card can run at 200MHz */ /* DDR mode @1.8V I/O */

  11. #define EXT_CSD_CARD_TYPE_HS400_1_2V (1<<7) /* Card can run at 200MHz */ /* DDR mode @1.2V I/O */

  12. #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
  • 1
  • 如何设置emmc的信号电压? 
    似乎不需要另外通知emmc信号电压要发生切换了(sd card则是需要的)

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

 
  1. #define MMC_CAP2_HS400_1_8V (1 << 22) /* can support */

  2. #define MMC_CAP2_HS400_1_2V (1 << 23) /* can support */

  3. #define MMC_CAP2_HS400 (MMC_CAP2_HS400_1_8V | MMC_CAP2_HS400_1_2V)

  4. #define MMC_CAP2_HS200_1_8V_SDR (1 << 5) /* can support */

  5. #define MMC_CAP2_HS200_1_2V_SDR (1 << 6) /* can support */

  6. #define MMC_CAP2_HS200 (MMC_CAP2_HS200_1_8V_SDR | MMC_CAP2_HS200_1_2V_SDR)

  7. #define MMC_CAP_1_8V_DDR (1 << 11) /* can support */ /* DDR mode at 1.8V */

  8. #define MMC_CAP_1_2V_DDR (1 << 12) /* can support */ /* DDR mode at 1.2V */

  9. #define MMC_CAP_HSDDR (MMC_CAP_1_8V_DDR | MMC_CAP_1_2V_DDR)

  10. // 补充,似乎HS SDR虽然支持1.8V和1.2V,但是代码上并没有做相应的判断

  11. eg:host->caps2 & MMC_CAP2_HS200_1_2V_SDR

  • 如何设置host输出的信号电压 
    调用__mmc_set_signal_voltage来对host输出的信号电压进行设置。 
    使用示例如下:
 
  1. #define MMC_SIGNAL_VOLTAGE_330 0

  2. #define MMC_SIGNAL_VOLTAGE_180 1

  3. #define MMC_SIGNAL_VOLTAGE_120 2

  4. __mmc_set_signal_voltage(host, MMC_SIGNAL_VOLTAGE_120)

  5. __mmc_set_signal_voltage(host, MMC_SIGNAL_VOLTAGE_180)

三、总线设置整体代码

1、总线模式选择

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

 
  1. static int mmc_select_bus_speed(struct mmc_card *card, u8 *ext_csd)

  2. {

  3. int err = 0;

  4.  
  5. BUG_ON(!card);

  6. if (!mmc_select_hs400_strobe(card, ext_csd)) // 先尝试切换到hs400模式,并设置strobe

  7. goto out;

  8. if (!mmc_select_hs400(card, ext_csd)) // 尝试切换到hs400模式

  9. goto out;

  10. if (!mmc_select_hs200(card, ext_csd)) // 尝试切换到hs200模式

  11. goto out;

  12. if (!mmc_select_hsddr(card, ext_csd)) // 尝试切换到hsddr模式

  13. goto out;

  14. if (!mmc_select_hs(card, ext_csd)) // 尝试切换到hs模式

  15. goto out;

  16.  
  17. // 上述都失败的情况下,还是处于legacy模式

  18. mmc_set_clock(card->host, card->csd.max_dtr);

  19. err = mmc_select_bus_width(card, 0, ext_csd);

  20.  
  21. out:

  22. return err;

  23. }

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

2、mmc_select_hs

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

 
  1. static int mmc_select_hs(struct mmc_card *card, u8 *ext_csd)

  2. {

  3. int err = 0;

  4. struct mmc_host *host;

  5.  
  6. host = card->host;

  7. // hs模式:host——》MMC_CAP_MMC_HIGHSPEED

  8. // hs模式:emmc——》EXT_CSD_CARD_TYPE_52

  9. if (!(host->caps & MMC_CAP_MMC_HIGHSPEED) || !(card->ext_csd.card_type & EXT_CSD_CARD_TYPE_52)) {

  10. err = -EOPNOTSUPP;

  11. goto out;

  12. }

  13.  
  14. // 切换emmc的总线速度模式

  15. err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL, EXT_CSD_HS_TIMING, 1, card->ext_csd.generic_cmd6_time);

  16.  
  17. if (err && err != -EBADMSG)

  18. goto out;

  19. // 切换成功,后续emmc都使用hs模式

  20. mmc_card_set_highspeed(card);

  21. // 切换host的总线速度模式(时序)

  22. mmc_set_timing(host, MMC_TIMING_MMC_HS);

  23. // 设置时钟频率

  24. mmc_set_clock(host, MMC_HIGH_52_MAX_DTR);

  25. // 切换总线宽度

  26. err = mmc_select_bus_width(card, 0, ext_csd);

  27.  
  28. out:

  29. if (err && err != -EOPNOTSUPP)

  30. pr_warning("%s: Switch to HighSpeed mode failed (err:%d)\n",

  31. mmc_hostname(host), err);

  32. return err;

  33. }

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

3、mmc_select_hsddr

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

 
  1. static int mmc_select_hsddr(struct mmc_card *card, u8 *ext_csd)

  2. {

  3. int ddr = 0, err = 0;

  4. struct mmc_host *host;

  5.  
  6. host = card->host;

  7.  
  8. if (!(host->caps & MMC_CAP_HSDDR) || // 判断host是否支持hsddr模式

  9. !(card->ext_csd.card_type & EXT_CSD_CARD_TYPE_DDR_52)) { // 判断emmc是否支持hsddr模式

  10. err = -EOPNOTSUPP;

  11. goto out;

  12. }

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

  15. if (err)

  16. goto out;

  17. mmc_card_clr_highspeed(card);

  18.  
  19. if ((card->ext_csd.card_type & EXT_CSD_CARD_TYPE_DDR_1_8V)

  20. && ((host->caps & (MMC_CAP_1_8V_DDR | MMC_CAP_UHS_DDR50))

  21. == (MMC_CAP_1_8V_DDR | MMC_CAP_UHS_DDR50)))

  22. ddr = MMC_1_8V_DDR_MODE; // 判断是否要设置成ddr 1.8V模式

  23. else if ((card->ext_csd.card_type & EXT_CSD_CARD_TYPE_DDR_1_2V)

  24. && ((host->caps & (MMC_CAP_1_2V_DDR | MMC_CAP_UHS_DDR50))

  25. == (MMC_CAP_1_2V_DDR | MMC_CAP_UHS_DDR50)))

  26. ddr = MMC_1_2V_DDR_MODE; // 判断是否要设置成ddr 1.2V模式

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

  29. if (err)

  30. goto out;

  31.  
  32. if (host->ios.bus_width == MMC_BUS_WIDTH_1) { // hsddr模式不支持1bit的总线宽度

  33. pr_err("%s: failed to switch to wide bus\n",

  34. mmc_hostname(host));

  35. goto out;

  36. }

  37.  
  38. if (ddr == MMC_1_2V_DDR_MODE) {

  39. err = __mmc_set_signal_voltage(host, MMC_SIGNAL_VOLTAGE_120); // 切换信号电压为1.2V

  40. if (err)

  41. goto out;

  42. }

  43. mmc_card_set_ddr_mode(card);

  44. mmc_set_timing(host, MMC_TIMING_UHS_DDR50); // 设置host的总线速度模式为MMC_TIMING_UHS_DDR50

  45. mmc_set_bus_width(host, host->ios.bus_width); // 设置host的总线宽度,其实在mmc_select_bus_width已经设置过一次了。

  46.  
  47. out:

  48. if (err && err != -EOPNOTSUPP)

  49. pr_warning("%s: Switch to HighSpeed DDR mode failed (err:%d)\n",

  50. mmc_hostname(host), err);

  51. return err;

  52. }

4、mmc_select_hs200

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

 
  1. static int mmc_select_hs200(struct mmc_card *card, u8 *ext_csd)

  2. {

  3. int err = 0;

  4. struct mmc_host *host;

  5.  
  6. host = card->host;

  7.  
  8. if (!(host->caps2 & MMC_CAP2_HS200) || // 判断host是否支持hs200模式

  9. !(card->ext_csd.card_type & EXT_CSD_CARD_TYPE_HS200)) { // 判断emmc是否支持hs200模式

  10. err = -EOPNOTSUPP;

  11. goto out;

  12. }

  13.  
  14. // HS200只支持1.2V或者1.8V的信号电压

  15. if (card->ext_csd.card_type & EXT_CSD_CARD_TYPE_SDR_1_2V && // 判断emmc是否支持hs200_1.2V模式

  16. host->caps2 & MMC_CAP2_HS200_1_2V_SDR) // 判断host是否支持hs200_1.2V模式

  17. if (__mmc_set_signal_voltage(host, MMC_SIGNAL_VOLTAGE_120)) // 如果是的话切换信号电压到1.2V

  18. err = __mmc_set_signal_voltage(host, MMC_SIGNAL_VOLTAGE_180); // 否则,切换信号电压到1.8V

  19. // 这里很不能理解,如果不支持1.2V的话,连1.8V的信号电压都不用设置了???

  20. // 根据实际结果来看,确实hs200模式下如果没有设置1.2V的话,确实也就没有设置成1.8V了,而是直接使用3V的信号电压

  21.  
  22. // 注意如下顺序:

  23. /*

  24. * For devices supporting HS200 mode, the bus width has

  25. * to be set before executing the tuning function. If

  26. * set before tuning, then device will respond with CRC

  27. * errors for responses on CMD line. So for HS200 the

  28. * sequence will be

  29. * 1. set bus width 4bit / 8 bit (1 bit not supported) // 先设置总线宽度,hs200不支持1bit的总线宽度

  30. * 2. switch to HS200 mode // 切换emmc和host的总线速度模式为hs200模式

  31. * 3. set the clock to > 52Mhz <=200MHz and // 设置时钟,200MHZ以内

  32. * 4. execute tuning for HS200 // hs200需要进行tuning操作获取一个合适的采样点

  33. */

  34. err = mmc_select_bus_width(card, 0, ext_csd); // 设置总线宽度

  35.  
  36. /* switch to HS200 mode if bus width set successfully */

  37. err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL, EXT_CSD_HS_TIMING, 2, 0); // 切换emmc的总线速度模式

  38.  
  39. /*

  40. * When HS200 activation is performed as part of HS400 selection

  41. * set the timing appropriately

  42. */

  43. if (mmc_card_hs400(card))

  44. mmc_set_timing(host, MMC_TIMING_MMC_HS400);

  45. else

  46. mmc_set_timing(host, MMC_TIMING_MMC_HS200); // 设置host的总线速度模式(时序)

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

  49.  
  50. if (host->ops->execute_tuning) { // 进行tuning操作

  51. mmc_host_clk_hold(host);

  52. err = host->ops->execute_tuning(host,

  53. MMC_SEND_TUNING_BLOCK_HS200);

  54. mmc_host_clk_release(host);

  55. }

  56. if (err) {

  57. pr_warning("%s: tuning execution failed\n",

  58. mmc_hostname(host));

  59. goto out;

  60. }

  61. mmc_card_set_hs200(card); // 设置mmc_card状态为MMC_STATE_HIGHSPEED_200

  62.  
  63. out:

  64. if (err && err != -EOPNOTSUPP)

  65. pr_warning("%s: Switch to HS200 mode failed (err:%d)\n",

  66. mmc_hostname(host), err);

  67. return err;

  68. }

5、mmc_select_hs400

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

 
  1. static int mmc_select_hs400(struct mmc_card *card, u8 *ext_csd)

  2. {

  3. int err = 0;

  4. struct mmc_host *host;

  5.  
  6. host = card->host;

  7.  
  8. if (!(host->caps2 & MMC_CAP2_HS400) || // 判断host是否支持hs400模式

  9. !(card->ext_csd.card_type & EXT_CSD_CARD_TYPE_HS400)) { // 判断emmc是否支持hs400模式

  10. err = -EOPNOTSUPP;

  11. goto out;

  12. }

  13.  
  14. // 根据协议emmc 5.0,不能直接切换到hs400模式,而是需要结果如下步骤

  15. /*

  16. * eMMC5.0 spec doesn't allow switching to HS400 mode from

  17. * HS200 mode directly. Hence follow these steps to switch

  18. * to HS400 mode:

  19. * Enable HS200 mode

  20. * Enable HighSpeed mode (The clk should be low enough

  21. * to enable HighSpeed mode) - HS_TIMING is 0x1

  22. * Enable DDR mode (Set bus width to 8-bit DDR)

  23. * Enable HS400 mode (Set HS_TIMING to 0x3 and change

  24. * frequency to <= 200MHz)

  25. * Perform tuning if required

  26. */

  27. mmc_card_set_hs400(card);

  28. err = mmc_select_hs200(card, ext_csd); // 先设置总线速度模式为hs200模式

  29. mmc_card_clr_hs200(card);

  30.  
  31. if ((card->ext_csd.card_type & EXT_CSD_CARD_TYPE_HS400_1_2V) // 判断emmc是否支持hs400_1.2v模式

  32. && (host->caps2 & MMC_CAP2_HS400_1_2V)) // 判断host是否支持hs400_1.2v模式

  33. if (__mmc_set_signal_voltage(host, MMC_SIGNAL_VOLTAGE_120)) // 如果是的话切换信号电压到1.2V

  34. err = __mmc_set_signal_voltage(host, MMC_SIGNAL_VOLTAGE_180); // 否则,切换信号电压到1.8V

  35. // 这里的疑问和前面说的一样

  36.  
  37. /*

  38. * Lower the clock and adjust the timing to be able

  39. * to switch to HighSpeed mode

  40. */

  41. mmc_set_timing(host, MMC_TIMING_LEGACY); // 设置host总线速度模式为legacy模式

  42. mmc_set_clock(host, MMC_HIGH_26_MAX_DTR); // 设置时钟为legacy的最大频率

  43.  
  44. /* Switch to 8-bit HighSpeed DDR mode */

  45. err = mmc_select_hsddr(card, ext_csd); // 设置总线速度模式为hsddr模式

  46. mmc_card_clr_ddr_mode(card);

  47.  
  48.  
  49. /* Switch to HS400 mode if bus width set successfully */

  50. err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,

  51. EXT_CSD_HS_TIMING, 3, 0); // 设置card的总线速度模式为hs400模式

  52. mmc_set_timing(host, MMC_TIMING_MMC_HS400); // 设置host的总线速度模式为hs400模式

  53. mmc_set_clock(host, MMC_HS400_MAX_DTR); // 设置时钟频率

  54.  
  55. if (host->ops->execute_tuning) {

  56. mmc_host_clk_hold(host);

  57. err = host->ops->execute_tuning(host, MMC_SEND_TUNING_BLOCK_HS400); // 执行tuning操作

  58. mmc_host_clk_release(host);

  59. }

  60. mmc_card_set_hs400(card); // 设置mmc_card状态为MMC_STATE_HIGHSPEED_400

  61.  
  62. out:

  63. return err;

  • 1
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值