[sd card] SD card初始化时的总线设置

==============================================================
零、在sd card初始化过程中,对于bus主要设置以下东西
1、时钟,输出频率
2、工作电压(注意和信号电压区分开来)
    如下范围:
   (1) MMC_VDD_165_195        0x00000080    /* VDD voltage 1.65 - 1.95 */ bit7
   (2) MMC_VDD_20_21        0x00000100    /* VDD voltage 2.0 ~ 2.1 */ bit8
   (3) MMC_VDD_21_22        0x00000200    /* VDD voltage 2.1 ~ 2.2 */ bit9
   (4) MMC_VDD_22_23        0x00000400    /* VDD voltage 2.2 ~ 2.3 */ bit10
   (5) MMC_VDD_23_24        0x00000800    /* VDD voltage 2.3 ~ 2.4 */ bit11
   (6) MMC_VDD_24_25        0x00001000    /* VDD voltage 2.4 ~ 2.5 */ bit12
   (7) MMC_VDD_25_26        0x00002000    /* VDD voltage 2.5 ~ 2.6 */ bit13
   (8) MMC_VDD_26_27        0x00004000    /* VDD voltage 2.6 ~ 2.7 */ bit14
   (9) MMC_VDD_27_28        0x00008000    /* VDD voltage 2.7 ~ 2.8 */ bit15
   (10) MMC_VDD_28_29        0x00010000    /* VDD voltage 2.8 ~ 2.9 */ bit16
   (11) MMC_VDD_29_30        0x00020000    /* VDD voltage 2.9 ~ 3.0 */ bit17
   (12) MMC_VDD_30_31        0x00040000    /* VDD voltage 3.0 ~ 3.1 */ bit18
   (13) MMC_VDD_31_32        0x00080000    /* VDD voltage 3.1 ~ 3.2 */ bit19
   (14) MMC_VDD_32_33        0x00100000    /* VDD voltage 3.2 ~ 3.3 */ bit20
   (15) MMC_VDD_33_34        0x00200000    /* VDD voltage 3.3 ~ 3.4 */ bit21
   (16) MMC_VDD_34_35        0x00400000    /* VDD voltage 3.4 ~ 3.5 */ bit22
   (17) MMC_VDD_35_36        0x00800000    /* VDD voltage 3.5 ~ 3.6 */ bit23
3、信号电压(IO电压)
    如下范围:
   (1)MMC_SIGNAL_VOLTAGE_330 0 (SD card刚上电的时候使用)
   (1)MMC_SIGNAL_VOLTAGE_180 1
4、总线模式
   (1)MMC_BUSMODE_OPENDRAIN——》开漏模式
    (2)MMC_BUSMODE_PUSHPULL——》上拉模式
5、总线速度模式
     (1)  Default Speed mode(DS) 3.3V signaling , Frequency up to 25 MHz, up to 12.5 MB/sec (SD card刚上电的时候使用)
     (2)  High Speed mode(HS) 3.3V signaling , Frequency up to 50 MHz, up to 25 MB/sec 
     (3)  SDR12: 1.8V signaling , Frequency up to 25 MHz, up to 12.5MB/sec 
     (4)  SDR25: 1.8V signaling,  Frequency up to 50 MHz, up to 25MB/sec 
     (5)  SDR50: 1.8V signaling , Frequency up to 100 MHz, up to 50MB/sec 
     (6)  SDR104: 1.8V signaling , Frequency up to 208 MHz, up to 104MB/sec 
     (7)  DDR50: 1.8V signaling , Frequency up to 50 MHz, sampled on both clock edges, up to   50MB/sec 
 
6、总线宽度
   (1) 一线, MMC_BUS_WIDTH_1(SD card刚上电的时候使用)
   (2) 四线, MMC_BUS_WIDTH_4

==============================================================
一、时钟的设置?
时钟是由host输出给card的,因此只需要为host设置时钟即可。
注意:并不是说处于什么总线速度模式下就需要设置什么样的频率,而是只需要比其频率小都是可以的。
例如,总线速度模式DS模式下,其最大频率是25MHz,但是host只要输出不超过这个值的频率都可以被处理,刚上电之后,400KHz的时钟频率就是工作在这种速度模式之下。

1、card支持的最大时钟频率
最大时钟频率取决于总线速度模式,
#define HIGH_SPEED_MAX_DTR 50000000    // HS模式的最大频率是50MHz,3.3V
#define UHS_SDR104_MAX_DTR 208000000    // SDR104模式的最大频率是208MHz,1.8V
#define UHS_SDR50_MAX_DTR 100000000    // SDR50模式的最大频率是100MHz,1.8V
#define UHS_DDR50_MAX_DTR 50000000    // DDR50模式的最大频率是50MHz,1.8V
#define UHS_SDR25_MAX_DTR UHS_DDR50_MAX_DTR    // SDR模式的最大频率是50MHz,1.8V
#define UHS_SDR12_MAX_DTR 25000000    // HS模式的最大频率是25MHz,1.8V
DS模式的最大频率是25Mhz,3.3V,但是在代码里面并没有定义出来??
(1)对于uhs来说,会在设置总线速度模式的时候,把最大频率设置到card->sw_caps.uhs_max_dtr中
static int sd_set_bus_speed_mode(struct mmc_card *card, u8 *status)
case UHS_SDR104_BUS_SPEED:
card->sw_caps.uhs_max_dtr = UHS_SDR104_MAX_DTR;
case UHS_DDR50_BUS_SPEED:
card->sw_caps.uhs_max_dtr = UHS_DDR50_MAX_DTR;
case UHS_SDR50_BUS_SPEED:
card->sw_caps.uhs_max_dtr = UHS_SDR50_MAX_DTR;
case UHS_SDR25_BUS_SPEED:
card->sw_caps.uhs_max_dtr = UHS_SDR25_MAX_DTR;
case UHS_SDR12_BUS_SPEED:
card->sw_caps.uhs_max_dtr = UHS_SDR12_MAX_DTR;
(2)对于HS模式的card来说,最大频率会设置到card->sw_caps.hs_max_dtr中
        card->sw_caps.hs_max_dtr = HIGH_SPEED_MAX_DTR;
(3)对于DS模式的card来说,最大频率会设置到card->csd.max_dtr中,
        是从card的CSD寄存器中读取出来的:
 
         对于现在的SD card来说,这个值一般是0x32。也就是2.5*10=25MHz
        csd->max_dtr   = tran_exp[e] * tran_mant[m];


2、host支持的最大频率和最小频率的说明
对于sdhci host来说,需要host driver提供struct sdhci_ops中的get_min_clock和get_max_clock方法来获取最大频率和最小频率。
然后设置到mmc_host中去
host->max_clk = host->ops->get_max_clock(host);    // 由底层host driver提供
mmc->f_max = host->max_clk;
mmc->f_min = host->ops->get_min_clock(host);    // 由底层host driver提供

3、如何设置host的时钟频率
使用mmc_set_clock函数进行设置:
对于UHS-I来说,mmc_sd_init_uhs_card——》sd_set_bus_speed_mode——》mmc_set_clock(card->host, card->sw_caps.uhs_max_dtr);
对于HS或者DS来说,mmc_set_clock(host, mmc_sd_get_max_clock(card));
void mmc_set_clock(struct mmc_host *host, unsigned int hz)
{
mmc_host_clk_hold(host);
__mmc_set_clock(host, hz);
mmc_host_clk_release(host);
}
static void __mmc_set_clock(struct mmc_host *host, unsigned int hz)
{
WARN_ON(hz < host->f_min);
if (hz > host->f_max)
hz = host->f_max;
host->ios.clock = hz;    // 将要设置的频率定义到ios.clock中去
mmc_set_ios(host);
}
对于sdhci host来说,对应mmc_set_ios中设置时钟的实现如下:
static void sdhci_do_set_ios(struct sdhci_host *host, struct mmc_ios *ios)
{
if (ios->clock) {
sdhci_set_clock(host, ios->clock);
}
对于没有使用sdhci标准的host,会调用host driver的struct sdhci_ops的set_clock方法来设置时钟频率。
static void sdhci_set_clock(struct sdhci_host *host, unsigned int clock)
{
if (host->ops->set_clock) {
host->ops->set_clock(host, clock);    // 由底层host driver提供


=====================================================================
二、工作电压电压的设置?
首先必须搞清楚工作电压和信号电压的区别。
sdhci标准中,默认使用两个regulater,
vmmc——》工作电压,是指使sd card工作起来的电压,可能的范围有2.7-3.6V
vqmmc——》信号电压,也就是IO(CMD线、DAT线)上的电压,只有1.8V(UHScard)以及3.3V(实际上达不到3.3V的时候也是可以正常使用的,看spec吧)

工作电压同样也是只需要在host端设置。但是必须知道card所支持的工作电压范围。

1、如何获取host端支持的电压?
对应sdhci类host来说,会从 Capabilities Register读取属性,从属性中的[bit26-24]来获取host支持的电压。

[    6.009797] mmc0: no vqmmc regulator found     // 信号电压,IO电压
[    6.013189] mmc0: no vmmc regulator found        // 工作电压
从log上看msm8916的regulator并不是设置为vmmc或者vqmmc来给sdhci core进行管理的。

 sdhci_add_host——》
if (caps[0] & SDHCI_CAN_VDD_330) {
ocr_avail |= MMC_VDD_32_33 | MMC_VDD_33_34;
}
if (caps[0] & SDHCI_CAN_VDD_300) {
ocr_avail |= MMC_VDD_29_30 | MMC_VDD_30_31;
}
if (caps[0] & SDHCI_CAN_VDD_180) {
ocr_avail |= MMC_VDD_165_195;
}
mmc->ocr_avail  = ocr_avail;
mmc->ocr_avail_sdio = ocr_avail;
if (host->ocr_avail_sdio)
mmc->ocr_avail_sdio &= host->ocr_avail_sdio;
mmc->ocr_avail_sd  = ocr_avail;
if (host->ocr_avail_sd)
mmc->ocr_avail_sd &= host->ocr_avail_sd;
else /* normal SD controllers don't support 1.8V */
mmc->ocr_avail_sd &= ~MMC_VDD_165_195;    // 这里又把1.8V去掉了。。
mmc->ocr_avail_mmc = ocr_avail;

#define  SDHCI_CAN_VDD_330 0x01000000
#define  SDHCI_CAN_VDD_300 0x02000000
#define  SDHCI_CAN_VDD_180 0x04000000
对于msm8916来说sdhci2的caps[0]=0x262dc8b2,只满足SDHCI_CAN_VDD_300 & SDHCI_CAN_VDD_180    ,说明msm8916的sdhci2只支持3.0V左右的情况。
也就是值设置 MMC_VDD_29_30 | MMC_VDD_30_31,bit17、bit18.相应ocr_avail_sd等于 0x60000


#define MMC_VDD_165_195        0x00000080    /* VDD voltage 1.65 - 1.95 */ bit7
#define MMC_VDD_20_21        0x00000100    /* VDD voltage 2.0 ~ 2.1 */ bit8
#define MMC_VDD_21_22        0x00000200    /* VDD voltage 2.1 ~ 2.2 */ bit9
#define MMC_VDD_22_23        0x00000400    /* VDD voltage 2.2 ~ 2.3 */ bit10
#define MMC_VDD_23_24        0x00000800    /* VDD voltage 2.3 ~ 2.4 */ bit11
#define MMC_VDD_24_25        0x00001000    /* VDD voltage 2.4 ~ 2.5 */ bit12
#define MMC_VDD_25_26        0x00002000    /* VDD voltage 2.5 ~ 2.6 */ bit13
#define MMC_VDD_26_27        0x00004000    /* VDD voltage 2.6 ~ 2.7 */ bit14
#define MMC_VDD_27_28        0x00008000    /* VDD voltage 2.7 ~ 2.8 */ bit15
#define MMC_VDD_28_29        0x00010000    /* VDD voltage 2.8 ~ 2.9 */ bit16
#define MMC_VDD_29_30        0x00020000    /* VDD voltage 2.9 ~ 3.0 */ bit17
#define MMC_VDD_30_31        0x00040000    /* VDD voltage 3.0 ~ 3.1 */ bit18
#define MMC_VDD_31_32        0x00080000    /* VDD voltage 3.1 ~ 3.2 */ bit19
#define MMC_VDD_32_33        0x00100000    /* VDD voltage 3.2 ~ 3.3 */ bit20
#define MMC_VDD_33_34        0x00200000    /* VDD voltage 3.3 ~ 3.4 */ bit21
#define MMC_VDD_34_35        0x00400000    /* VDD voltage 3.4 ~ 3.5 */ bit22
#define MMC_VDD_35_36        0x00800000    /* VDD voltage 3.5 ~ 3.6 */ bit23

这些位都是和ocr寄存器中的电压值的位域是对应的。这样就知道了host支持的电压了。



2、如何获取card支持的电压的读取?
mmc_attach_sd——》mmc_send_app_op_cond(host, 0, &ocr);发送ACMD41命令获取 ocr寄存器
eg:[    9.907888] mmc1: req done (CMD41): 0: 00ff8000 00000000 00000000 00000000
00ff8000 就是ocr寄存器的值。
参照下表:
0xff8000,说明[bit23:15]都为1,也就是支持2.7-3.6V之间的电压。
 


3、如何选择一个合适的电压(双方都支持的最低电压,节省功耗)
mmc_select_voltage(host, ocr);        
ocr表示card的ocr寄存器的值,也就表示支持的电压域
host->ocr_avail则表示host支持的电压域。
这个函数用于选择一个双方都支持的最低电压。
u32 mmc_select_voltage(struct mmc_host *host, u32 ocr)
{
int bit;
ocr &= host->ocr_avail;
// ocr=0xff8000, host->ocr_avail=host->ocr_avail=0x60000,这里会选中bit17
bit = ffs(ocr);
if (bit) {
bit -= 1;
ocr &= 3 << bit;     // 将0b11右移bit,表示当前要使用的电压位域
mmc_host_clk_hold(host);
host->ios.vdd = bit;
mmc_set_ios(host);    // 调用 mmc_set_ios来设置host的io电压
mmc_host_clk_release(host);
return ocr;
}
 eg:
[    6.300332] mmc1: ================== ocr=0xff8000, host->ocr_avail=host->ocr_avail=0x60000
[    6.300332]  mmc1: the ocr choose bit = 18(bit位从1开始计数的)
host的值是0x60000,bit17和bit18为1,所以对应电压为vdd 2.9-3.1V

4、host的工作电压的设置?
调用mmc_select_voltage进行工作电压的设置。
host->ocr = mmc_select_voltage(host, ocr);
在sdhci的pwr中断触发的时候由host driver进行设置。
对于8916来说,对应代码在sdhci_msm_pwr_irq中对regulator进行设置。


=====================================================================
三、信号电压(IO电压)的设置?

首先必须搞清楚工作电压和信号电压的区别。
sdhci标准中,默认使用两个regulater,
vmmc——》工作电压,是指使sd card工作起来的电压,可能的范围有2.7-3.6V
vqmmc——》信号电压,也就是IO(CMD线、DAT线)上的电压,只有1.8V(UHScard)以及3.3V(实际上达不到3.3V的时候也是可以正常使用的,看spec吧)

信号电压虽然也是由host进行设置,但是card 必须知道当前时候的信号电压是3.3V或者1.8V才能对信号进行正确的处理.
刚上电的情况下,信号电压是3.3V。

1、如何知道host是否支持1.8V的信号电压?
因为UHS都要求IO电压为1.8V。因此只需要host支持UHS模式,自然也就说明能支持1.8V的信号电压,通过mmc_host_uhs(host)来进行判断。

2、如何知道card是否支持切换到1.8V的信号电压?
同样通过ACMD41读取的ocr寄存器来进行判断。当host支持1.8V的信号电压的时候,在ACMD41的参数的bit24(S18R)设置为1,告诉card“ 我这个host是支持1.8V的信号电压的,你必须也告诉我你是否支持切换到1.8V的信号电压 ”,card返回的ocr寄存器如下:
 bit24: The  card  indicates  S18A=0,  which  means  that  voltage  switch  is  not  allowed  and  the  host   needs  to  use  current  signaling  level.    S18A=1  means  that  voltage  switch  is  allowed  and  host  issues  CMD11 to invoke voltage switch sequence. 
ocr的bit24就是card用来告诉host是否支持切换到1.8V。
如果busy为1(说明SD card已经初始化完成了),检查bit24(S18A),为0说明card不允许切换到1.8V,为1说明card允许切换到1.8V


3、通知card准备切换信号电压到1.8V
通过CMD11 通知card准备切换信号电压到1.8V
cmd.opcode = SD_SWITCH_VOLTAGE;    // CMD11
cmd.arg = 0;
cmd.flags = MMC_RSP_R1 | MMC_CMD_AC;
mmc_host_clk_hold(host);
err = mmc_wait_for_cmd(host, &cmd, 0);
 
4、host如何切换信号电压为1.8V
需要调用__mmc_set_signal_voltage来进行1.8V和3.3V返回的切换
linux定义了3中信号电压
#define MMC_SIGNAL_VOLTAGE_330 0
#define MMC_SIGNAL_VOLTAGE_180 1
#define MMC_SIGNAL_VOLTAGE_120 2    // 对于emmc来说
__mmc_set_signal_voltage中的参数也只能选择这上面的其中一个了
int __mmc_set_signal_voltage(struct mmc_host *host, int signal_voltage)
{
host->ios.signal_voltage = signal_voltage;
if (host->ops->start_signal_voltage_switch) {
mmc_host_clk_hold(host);
err = host->ops->start_signal_voltage_switch(host, &host->ios);    // 会调用到sdhci的sdhci_start_signal_voltage_switch
mmc_host_clk_release(host);
}
}

5、sd core切换信号电压整体说明
if (!mmc_host_is_spi(host) && rocr &&
   ((*rocr & 0x41000000) == 0x41000000)) {
err = mmc_set_signal_voltage(host, MMC_SIGNAL_VOLTAGE_180);
首先判断busy位和S18A位是否为1,如果为1就说明card已经准备好并且支持切换到1.8V,此时调用 mmc_set_signal_voltage进行切换。
int mmc_set_signal_voltage(struct mmc_host *host, int signal_voltage)
{
cmd.opcode = SD_SWITCH_VOLTAGE;
cmd.arg = 0;
cmd.flags = MMC_RSP_R1 | MMC_CMD_AC;
mmc_host_clk_hold(host);
err = mmc_wait_for_cmd(host, &cmd, 0);    // 先发送CMD11通知card准备切换信号电压到1.8V了
if (__mmc_set_signal_voltage(host, signal_voltage)) {    // 调用 __mmc_set_signal_voltage来切换host输出的信号到1.8V

======================================================================
四、总线模式的设置?
1、有两种总线模式
开漏模式(MMC_BUSMODE_OPENDRAIN)和上拉模式(MMC_BUSMODE_PUSHPULL)
但是SD card不管是处于card identification mode或者是data transfer mode,其总线模式都是MMC_BUSMODE_OPENDRAIN。
为什么和emmc不一样,emmc在data tranfer mode下要求处于上拉模式?????!


=====================================================================
五、总线速度模式的设置?

--------------------------------------------------------------------------------
为了方便理解,先不考虑UHS的情况,只考虑SD的速度模式有DS模式和HS模式,
DS(Default speed),最大总线速度12.5MB/s,最大支持时钟25MHz,电压3.3V
HS(Default speed),最大总线速度25MB/s,最大支持时钟50MHz,电压3.3V
SD卡上电默认的模式就是DS模式,也就是只需要尝试将其切换到HS模式即可
    After  power  up,  the  SD  memory  card  is  in  the  default  speed  mode,  and  by  using  Switch  Function 
    command (CMD6), the Version 1.10 and higher SD  memory card can be placed in High-Speed mode. 
    The High-Speed function is a function in the access mode group (see Table 4-11). Supporting High-
    Speed mode is optional.  
1、card总线速度模式的设置
不考虑uhs的情况下,会尝试切换到HS模式。
可以通过CMD6进行切换。mode设置为1(set function),group设置为0(access mode),value设置为1(HS的function是1
mmc_sd_switch_hs-》mmc_sd_switch(card, 1, 0, 1, status);
具体参考协议中的CMD6说明

2、host总线速度模式的设置
设置host的总线速度模式也就是设置host的时序。可以通过mmc_set_ios进行设置
host的总线时序有如下类型:
#define MMC_TIMING_LEGACY 0   ——》(对应DS模式)
#define MMC_TIMING_SD_HS 2   ——》(对应HS模式)
#define MMC_TIMING_UHS_SDR12 3
#define MMC_TIMING_UHS_SDR25 4
#define MMC_TIMING_UHS_SDR50 5
#define MMC_TIMING_UHS_SDR104 6
#define MMC_TIMING_UHS_DDR50 7

void mmc_sd_go_highspeed(struct mmc_card *card)
{
mmc_card_set_highspeed(card);——》((c)->state |= MMC_STATE_HIGHSPEED)
mmc_set_timing(card->host, MMC_TIMING_SD_HS);——》host->ios.timing = timing;          mmc_set_ios(host);
}
对于sdhci host,会去设置0x28寄存器的bit2,
对应代码sdhci.c中
static void sdhci_do_set_ios(struct sdhci_host *host, struct mmc_ios *ios)
{
        ctrl = sdhci_readb(host, SDHCI_HOST_CONTROL);    // #define SDHCI_HOST_CONTROL 0x28
if ((ios->timing == MMC_TIMING_SD_HS ||
     ios->timing == MMC_TIMING_MMC_HS)
    && !(host->quirks & SDHCI_QUIRK_NO_HISPD_BIT))
ctrl |= SDHCI_CTRL_HISPD;        // #define  SDHCI_CTRL_HISPD 0x04  ,SPD表示speed
else
ctrl &= ~SDHCI_CTRL_HISPD;
}
 
-------------------------------------------------------------------------------------
考虑UFS的情况,有如下模式
     (1)  Default Speed mode(DS)3.3V signaling, Frequency up to 25 MHz, up to 12.5 MB/sec 
     (2)  High Speed mode(HS)3.3V signaling, Frequency up to 50 MHz, up to 25 MB/sec 
     (3)  SDR12: 1.8V signaling, Frequency up to 25 MHz, up to 12.5MB/sec 
     (4)  SDR25: 1.8V signaling, Frequency up to 50 MHz, up to 25MB/sec 
     (5)  SDR50: 1.8V signaling, Frequency up to 100 MHz, up to 50MB/sec 
     (6)  SDR104: 1.8V signaling, Frequency up to 208 MHz, up to 104MB/sec 
     (7)  DDR50: 1.8V signaling, Frequency up to 50 MHz, sampled on both clock edges, up to   50MB/sec 
SD card上电之后同样是处于DS模式,对于SD card的操作只需要将其切换到其他模式。
1、如何获取card支持的总线速度模式
首先要根据switch属性,也就是CMD6读出来的数据,来获取sd card支持的速度模式card->sw_caps.sd3_bus_mode
mmc_read_switch——》mmc_sd_switch(card, 0, 0, 0, status);     card->sw_caps.sd3_bus_mode = status[13];
从SD3.0协议上可以看到对应
 host读到的顺序是反过来的,也就是(512-400)/8=112/8=14.所以对应在status[13]的位置上
对应bit支持的速度模式可以参考如下代码
#define SD_MODE_HIGH_SPEED (1 << HIGH_SPEED_BUS_SPEED)
#define SD_MODE_UHS_SDR12 (1 << UHS_SDR12_BUS_SPEED)
#define SD_MODE_UHS_SDR25 (1 << UHS_SDR25_BUS_SPEED)
#define SD_MODE_UHS_SDR50 (1 << UHS_SDR50_BUS_SPEED)
#define SD_MODE_UHS_SDR104 (1 << UHS_SDR104_BUS_SPEED)
#define SD_MODE_UHS_DDR50 (1 << UHS_DDR50_BUS_SPEED)
这里注意一下,DS和SDR12频率是一样的,差异在于SRD12的电压是1.8V。同样,HS和SDR25频率是一样的,差异在于SDR25的电压是1.8V。
所以在对于SD的速度模式来说他们是一致的
2、如何设置card的总线速度模式
可以通过CMD6进行切换。mode设置为1(set function),group设置为0(access mode),value设置为对应速度模式的function(HS的function是1
sd_set_bus_speed_mode-》 mmc_sd_switch(card, 1, 0, card->sd_bus_speed, status);   
card->sd_bus_speed对应如下内容
#define UHS_SDR12_BUS_SPEED 0
#define HIGH_SPEED_BUS_SPEED 1
#define UHS_SDR25_BUS_SPEED 1    // 和HIGH_SPEED_BUS_SPEED一致
#define UHS_SDR50_BUS_SPEED 2
#define UHS_SDR104_BUS_SPEED 3
#define UHS_DDR50_BUS_SPEED 4
具体参考协议中的CMD6说明

3、如何获取host支持的总线速度模式
以sdhci为例,从SDHCI_CAPABILITIES_1寄存器中读取(0x44)
(注意,协议中是从0x40中开始定义,但是代码中读的是0x44的部分,所以这里偏移32bit)
#define  SDHCI_SUPPORT_SDR50 0x00000001
#define  SDHCI_SUPPORT_SDR104 0x00000002
#define  SDHCI_SUPPORT_DDR50 0x00000004
另外,还需要根据host是否支持1.8V来进行判断,最终设置到mmc->caps中去
if (host->quirks2 & SDHCI_QUIRK2_NO_1_8_V)
caps[1] &= ~(SDHCI_SUPPORT_SDR104 | SDHCI_SUPPORT_SDR50 |
       SDHCI_SUPPORT_DDR50);
/* Any UHS-I mode in caps implies SDR12 and SDR25 support. */
if (caps[1] & (SDHCI_SUPPORT_SDR104 | SDHCI_SUPPORT_SDR50 |
       SDHCI_SUPPORT_DDR50))
mmc->caps |= MMC_CAP_UHS_SDR12 | MMC_CAP_UHS_SDR25;
/* SDR104 supports also implies SDR50 support */
if (caps[1] & SDHCI_SUPPORT_SDR104)
mmc->caps |= MMC_CAP_UHS_SDR104 | MMC_CAP_UHS_SDR50;
else if (caps[1] & SDHCI_SUPPORT_SDR50)
mmc->caps |= MMC_CAP_UHS_SDR50;
if (caps[1] & SDHCI_SUPPORT_DDR50)
mmc->caps |= MMC_CAP_UHS_DDR50;

 
4、如何设置host总线速度模式
设置host的总线速度模式也就是设置host的时序。可以通过mmc_set_ios进行设置
host的总线时序有如下类型(和SD card相关的):
#define MMC_TIMING_LEGACY 0   ——》对应DS模式
#define MMC_TIMING_SD_HS 2      ——》对应HS模式
#define MMC_TIMING_UHS_SDR12 3
#define MMC_TIMING_UHS_SDR25 4
#define MMC_TIMING_UHS_SDR50 5
#define MMC_TIMING_UHS_SDR104 6
#define MMC_TIMING_UHS_DDR50 7
static int sd_set_bus_speed_mode(struct mmc_card *card, u8 *status)
{
timing = MMC_TIMING_UHS_SDR104;
timing = MMC_TIMING_UHS_DDR50;
timing = MMC_TIMING_UHS_SDR50;
timing = MMC_TIMING_UHS_SDR25;
timing = MMC_TIMING_UHS_SDR12;
mmc_set_timing(card->host, timing);    // 同样是使用 mmc_set_timing进行设置
mmc_set_timing——》host->ios.timing = timing;  mmc_set_ios(host);
uhs模式下,对于sdhci host来说
除了要设置SDHCI_HOST_CONTROL(0x28)——》SDHCI_CTRL_HISPD,bit2(大于25M的timing)
 
还需要设置SDHCI_HOST_CONTROL2(0x3E)——》SDHCI_CTRL_UHS_MASK,bit2-0
 对应代码sdhci.c中
if (host->ops->set_uhs_signaling)
host->ops->set_uhs_signaling(host, ios->timing);
else {
ctrl_2 = sdhci_readw(host, SDHCI_HOST_CONTROL2);
/* Select Bus Speed Mode for host */
ctrl_2 &= ~SDHCI_CTRL_UHS_MASK;
if (ios->timing == MMC_TIMING_MMC_HS400)
ctrl_2 |= SDHCI_CTRL_HS_SDR200;
else if (ios->timing == MMC_TIMING_MMC_HS200)
ctrl_2 |= SDHCI_CTRL_HS_SDR200;
else if (ios->timing == MMC_TIMING_UHS_SDR12)
ctrl_2 |= SDHCI_CTRL_UHS_SDR12;
else if (ios->timing == MMC_TIMING_UHS_SDR25)
ctrl_2 |= SDHCI_CTRL_UHS_SDR25;
else if (ios->timing == MMC_TIMING_UHS_SDR50)
ctrl_2 |= SDHCI_CTRL_UHS_SDR50;
else if (ios->timing == MMC_TIMING_UHS_SDR104)
ctrl_2 |= SDHCI_CTRL_UHS_SDR104;
else if (ios->timing == MMC_TIMING_UHS_DDR50)
ctrl_2 |= SDHCI_CTRL_UHS_DDR50;
sdhci_writew(host, ctrl_2, SDHCI_HOST_CONTROL2);
}

5、sd core选择一个合适总线速度模式
先根据card是否支持1.8V来判断card是否支持UHS模式。这个可以通过ocr寄存器的SD_ROCR_S18A位(bit24)来进行判断。
如果支持1.8V,则会从UHS模式中选择一个host和card都支持的最优的总线模式。
SD_MODE_UHS_SDR104 > SD_MODE_UHS_DDR50 > SD_MODE_UHS_SDR50 > SD_MODE_UHS_SDR25 > UHS_SDR12_BUS_SPEED > MMC_TIMING_SD_HS
否则直接尝试切换到HS模式。
if (rocr & SD_ROCR_S18A) {
err = mmc_sd_init_uhs_card(card);
if (err)
goto free_card;
mmc_card_set_uhs(card);
} else {
err = mmc_sd_switch_hs(card);
if (err > 0)
mmc_sd_go_highspeed(card);        // 前面说明过了
通过 mmc_sd_init_uhs_card来从uhs中选择一个合适的速度模式,并设置到card和host中
mmc_sd_init_uhs_card
——》sd_update_bus_speed_mode // 选择一个合适的速度模式
if ((card->host->caps & MMC_CAP_UHS_SDR104) &&
    (card->sw_caps.sd3_bus_mode & SD_MODE_UHS_SDR104) &&
    (card->host->f_max > UHS_SDR104_MIN_DTR)) {
card->sd_bus_speed = UHS_SDR104_BUS_SPEED;
} else if ((card->host->caps & MMC_CAP_UHS_DDR50) &&
   (card->sw_caps.sd3_bus_mode & SD_MODE_UHS_DDR50) &&
    (card->host->f_max > UHS_DDR50_MIN_DTR)) {
card->sd_bus_speed = UHS_DDR50_BUS_SPEED;
——》sd_set_bus_speed_mode // 设置到card和host中,调用sd_set_bus_speed_mode和mmc_set_timing


=====================================================================
六、总线宽度的设置?
注意点:
刚上电的情况下,SD CARD处于单线模式,也就是只有DAT[0]线可以进行数据传输。
UHS-I只能使用在四线模式下。host可以通过ACMD6将总线宽度设置为四线。

1、如何获取card支持的总线宽度?
ACMD51可以读取到SCR寄存器(sd configuration register),通过SCR寄存器bit[51-48]可以读取到当前card支持的总线宽度
 
 scr->bus_widths = UNSTUFF_BITS(resp, 48, 4);

2、如何设置card的总线宽度
SD_APP_SET_BUS_WIDTH(ACMD6)命令用于设置card的总线宽度当参数为SD_BUS_WIDTH_1(0)时,设置为总线宽度为单线模式,当参数为SD_BUS_WIDTH_4(2)时,设置为四线模式。
mmc_app_set_bus_width则会发送 SD_APP_SET_BUS_WIDTH指令。
eg:mmc_app_set_bus_width(card, MMC_BUS_WIDTH_4);

3、如何获取host支持的总线宽度
对于sdhci host来说,只需要其host controller没有定义sdhci的quirks中的SDHCI_QUIRK_FORCE_1_BIT_DATA,那么都会默认支持四线模式的。
sdhci_add_host
if (!(host->quirks & SDHCI_QUIRK_FORCE_1_BIT_DATA))
mmc->caps |= MMC_CAP_4_BIT_DATA;
注意,对于emmc而言,如果host支持八线模式,那么就应该在host driver中自己定义 mmc->caps |= MMC_CAP_8_BIT_DATA属性,sdhci并不会去将其设置为八线模式。

4、如何设置host的总线宽度
mmc_set_bus_width用来设置host的总线宽度
mmc_set_bus_width(host, MMC_BUS_WIDTH_4);
mmc_set_bus_width——》host->ios.bus_width = width; mmc_set_ios(host);
对于sdhci host来说,需要去设置SDHCI_HOST_CONTROL寄存器(0x28)的bit1(四线模式)或者bit5(八线模式)
 
 注意不要同时选择。
sdhci的设置总线宽度的代码如下:
ctrl = sdhci_readb(host, SDHCI_HOST_CONTROL);
if (ios->bus_width == MMC_BUS_WIDTH_8) {
ctrl &= ~SDHCI_CTRL_4BITBUS;
if (host->version >= SDHCI_SPEC_300)
ctrl |= SDHCI_CTRL_8BITBUS;
} else {
if (host->version >= SDHCI_SPEC_300)
ctrl &= ~SDHCI_CTRL_8BITBUS;
if (ios->bus_width == MMC_BUS_WIDTH_4)
ctrl |= SDHCI_CTRL_4BITBUS;
else
ctrl &= ~SDHCI_CTRL_4BITBUS;
}
sdhci_writeb(host, ctrl, SDHCI_HOST_CONTROL);

5、sd core设置总线宽度
需要先判断host和card是否都支持四线模式,然后再设置sdcard的总线宽度模式,再设置host的总线宽度模式。
if ((card->host->caps & MMC_CAP_4_BIT_DATA) &&    // 判断host是否支持四线模式
    (card->scr.bus_widths & SD_SCR_BUS_WIDTH_4)) {    // 判断card是否支持四线模式
err = mmc_app_set_bus_width(card, MMC_BUS_WIDTH_4);    // 发送ACMD6设置card的总线宽度为四线模式
if (err)
goto out;
mmc_set_bus_width(card->host, MMC_BUS_WIDTH_4);    // 设置host的总线宽度为四线模式
}




猜你喜欢

转载自blog.csdn.net/ooonebook/article/details/60467901
sd