Browse Source

[components][drivers][sd] add uhs-i mode support to sd driver

- added SDR50, SDR104 and DDR50 support to SD driver

Signed-off-by: Fan YANG <fan.yang@hpmicro.com>
Fan YANG 1 year ago
parent
commit
2cc2743fc7

+ 49 - 0
components/drivers/include/drivers/mmcsd_card.h

@@ -7,6 +7,7 @@
  * Date           Author        Notes
  * 2011-07-25     weety         first version
  * 2024-05-24     HPMicro       add HS400 support
+ * 2024-05-26     HPMicro       add UHS-I support for SD card
  */
 
 #ifndef __MMCSD_CARD_H__
@@ -84,6 +85,51 @@ struct rt_sdio_cccr {
 
 };
 
+/*
+ * SD Status
+ */
+union rt_sd_status {
+    rt_uint32_t status_words[16];
+    struct {
+        rt_uint32_t reserved[12];
+        rt_uint64_t : 8;
+        rt_uint64_t uhs_au_size: 4;
+        rt_uint64_t uhs_speed_grade: 4;
+        rt_uint64_t erase_offset: 2;
+        rt_uint64_t erase_timeout: 6;
+        rt_uint64_t erase_size: 16;
+        rt_uint64_t : 4;
+        rt_uint64_t au_size: 4;
+        rt_uint64_t performance_move: 8;
+        rt_uint64_t speed_class: 8;
+
+        rt_uint32_t size_of_protected_area;
+
+        rt_uint32_t sd_card_type: 16;
+        rt_uint32_t : 6;
+        rt_uint32_t : 7;
+        rt_uint32_t secured_mode: 1;
+        rt_uint32_t data_bus_width: 2;
+    };
+};
+
+/*
+ * SD Speed Class
+ */
+#define SD_SPEED_CLASS_0    0
+#define SD_SPEED_CLASS_2    1
+#define SD_SPEED_CLASS_4    2
+#define SD_SPEED_CLASS_6    3
+#define SD_SPEED_CLASS_10   4
+
+/*
+ * UHS Speed Grade
+ */
+#define UHS_SPEED_GRADE_0   0
+#define UHS_SPEED_GRADE_1   1
+#define UHS_SPEED_GRADE_3   3
+
+
 struct rt_sdio_cis {
     rt_uint16_t     manufacturer;
     rt_uint16_t     product;
@@ -161,6 +207,9 @@ struct rt_mmcsd_card {
 #define CARD_FLAG_HIGHSPEED_DDR  (1 << 3)   /* HIGH SPEED DDR */
 #define CARD_FLAG_HS200      (1 << 4)   /* BUS SPEED 200MHz */
 #define CARD_FLAG_HS400      (1 << 5)   /* BUS SPEED 400MHz */
+#define CARD_FLAG_SDR50      (1 << 6)   /* BUS SPEED 100MHz */
+#define CARD_FLAG_SDR104     (1 << 7)   /* BUS SPEED 200MHz */
+#define CARD_FLAG_DDR50      (1 << 8)   /* DDR50, works on 1.8V only */
     struct rt_sd_scr    scr;
     struct rt_mmcsd_csd csd;
     rt_uint32_t     hs_max_data_rate;  /* max data transfer rate in high speed mode */

+ 3 - 2
components/drivers/include/drivers/mmcsd_cmd.h

@@ -5,7 +5,8 @@
  *
  * Change Logs:
  * Date           Author        Notes
- * 2011-07-25     weety     first version
+ * 2011-07-25     weety         first version
+ * 2024-05-26     HPMicro       add VOLTAGE_SWITCH definition
  */
 
 #ifndef __CMD_H__
@@ -26,7 +27,7 @@ extern "C" {
 #define SEND_EXT_CSD          8   /* adtc                    R1  */
 #define SEND_CSD              9   /* ac   [31:16] RCA        R2  */
 #define SEND_CID             10   /* ac   [31:16] RCA        R2  */
-#define READ_DAT_UNTIL_STOP  11   /* adtc [31:0] dadr        R1  */
+#define VOLTAGE_SWITCH       11   /* ac   [31:0]             R1  */
 #define STOP_TRANSMISSION    12   /* ac                      R1b */
 #define SEND_STATUS          13   /* ac   [31:16] RCA        R1  */
 #define GO_INACTIVE_STATE    15   /* ac   [31:16] RCA            */

+ 6 - 0
components/drivers/include/drivers/mmcsd_host.h

@@ -7,6 +7,7 @@
  * Date           Author        Notes
  * 2011-07-25     weety         first version
  * 2024-05-25     HPMicro       add HS400 support
+ * 2024-05-26     HPMicro       add UHS-I support
  */
 
 #ifndef __HOST_H__
@@ -87,6 +88,7 @@ struct rt_mmcsd_host_ops
     rt_int32_t (*get_card_status)(struct rt_mmcsd_host *host);
     void (*enable_sdio_irq)(struct rt_mmcsd_host *host, rt_int32_t en);
     rt_int32_t (*execute_tuning)(struct rt_mmcsd_host *host, rt_int32_t opcode);
+    rt_int32_t (*switch_uhs_voltage)(struct rt_mmcsd_host *host);
 };
 
 struct rt_mmcsd_host
@@ -115,6 +117,7 @@ struct rt_mmcsd_host
 #define VDD_33_34       (1 << 21)   /* VDD voltage 3.3 ~ 3.4 */
 #define VDD_34_35       (1 << 22)   /* VDD voltage 3.4 ~ 3.5 */
 #define VDD_35_36       (1 << 23)   /* VDD voltage 3.5 ~ 3.6 */
+#define OCR_S18R        (1 << 24)   /* Switch to 1V8 Request */
     rt_uint32_t  flags; /* define device capabilities */
 #define MMCSD_BUSWIDTH_4    (1 << 0)
 #define MMCSD_BUSWIDTH_8    (1 << 1)
@@ -135,6 +138,9 @@ struct rt_mmcsd_host
 #define MMCSD_SUP_HS400_1V2  (1 << 13)
 #define MMCSD_SUP_HS400      (MMCSD_SUP_HS400_1V2 | MMCSD_SUP_HS400_1V8) /* hs400 ddr */
 #define MMCSD_SUP_ENH_DS     (1 << 14)
+#define MMCSD_SUP_SDR50      (1 << 15)
+#define MMCSD_SUP_SDR104     (1 << 16)
+#define MMCSD_SUP_DDR50      (1 << 17)
 
     rt_uint32_t max_seg_size;   /* maximum size of one dma segment */
     rt_uint32_t max_dma_segs;   /* maximum number of dma segments in one request */

+ 13 - 1
components/drivers/include/drivers/sd.h

@@ -1,11 +1,12 @@
 /*
- * Copyright (c) 2006-2023, RT-Thread Development Team
+ * Copyright (c) 2006-2024, RT-Thread Development Team
  *
  * SPDX-License-Identifier: Apache-2.0
  *
  * Change Logs:
  * Date           Author        Notes
  * 2011-07-25     weety         first version
+ * 2024-05-26     HPMicro       Add UHS-I support
  */
 
 #ifndef __SD_H__
@@ -18,6 +19,17 @@
 extern "C" {
 #endif
 
+
+/*
+ *  SWITCH_FUNC timing
+ */
+#define SD_SWITCH_FUNC_TIMING_DEFAULT 0
+#define SD_SWITCH_FUNC_TIMING_HS      1
+#define SD_SWITCH_FUNC_TIMING_SDR50   2
+#define SD_SWITCH_FUNC_TIMING_SDR104  3
+#define SD_SWITCH_FUNC_TIMING_DDR50   4
+
+
 rt_err_t mmcsd_send_if_cond(struct rt_mmcsd_host *host, rt_uint32_t ocr);
 rt_err_t mmcsd_send_app_op_cond(struct rt_mmcsd_host *host, rt_uint32_t ocr, rt_uint32_t *rocr);
 

+ 195 - 32
components/drivers/sdio/sd.c

@@ -1,11 +1,12 @@
 /*
- * Copyright (c) 2006-2023, RT-Thread Development Team
+ * Copyright (c) 2006-2024, RT-Thread Development Team
  *
  * SPDX-License-Identifier: Apache-2.0
  *
  * Change Logs:
  * Date           Author        Notes
- * 2011-07-25     weety     first version
+ * 2011-07-25     weety         first version
+ * 2024-05-26     HPMicro       add UHS-I support
  */
 
 #include <drivers/mmcsd_core.h>
@@ -203,7 +204,25 @@ static rt_int32_t mmcsd_switch(struct rt_mmcsd_card *card)
     rt_memset(&cmd, 0, sizeof(struct rt_mmcsd_cmd));
 
     cmd.cmd_code = SD_SWITCH;
-    cmd.arg = 0x80FFFFF1;
+
+    rt_uint32_t switch_func_timing;
+    if ((card->flags & CARD_FLAG_SDR104) && (card->host->flags & MMCSD_SUP_SDR104))
+    {
+        switch_func_timing = SD_SWITCH_FUNC_TIMING_SDR104;
+    }
+    else if ((card->flags & CARD_FLAG_SDR50) && (card->host->flags & MMCSD_SUP_SDR50))
+    {
+        switch_func_timing = SD_SWITCH_FUNC_TIMING_SDR50;
+    }
+    else if ((card->flags & CARD_FLAG_DDR50) && (card->host->flags & MMCSD_SUP_DDR50))
+    {
+        switch_func_timing = SD_SWITCH_FUNC_TIMING_DDR50;
+    }
+    else
+    {
+        switch_func_timing = SD_SWITCH_FUNC_TIMING_HS;
+    }
+    cmd.arg = 0x80FFFFF0 | switch_func_timing;
     cmd.flags = RESP_R1 | CMD_ADTC;
 
     rt_memset(&data, 0, sizeof(struct rt_mmcsd_data));
@@ -227,13 +246,60 @@ static rt_int32_t mmcsd_switch(struct rt_mmcsd_card *card)
         goto err1;
     }
 
-    if ((buf[16] & 0xF) != 1)
+    if ((buf[16] & 0xF) != switch_func_timing)
     {
-        LOG_I("switching card to high speed failed!");
+        LOG_E("switching card to timing mode %d failed!", switch_func_timing);
         goto err;
     }
 
-    card->flags |= CARD_FLAG_HIGHSPEED;
+    switch(switch_func_timing)
+    {
+    case SD_SWITCH_FUNC_TIMING_SDR104:
+        card->flags |= CARD_FLAG_SDR104;
+        break;
+    case SD_SWITCH_FUNC_TIMING_SDR50:
+        card->flags |= CARD_FLAG_SDR50;
+        break;
+    case SD_SWITCH_FUNC_TIMING_DDR50:
+        card->flags |= CARD_FLAG_DDR50;
+        break;
+    case SD_SWITCH_FUNC_TIMING_HS:
+        card->flags |= CARD_FLAG_HIGHSPEED;
+        break;
+    default:
+        /* Default speed */
+        break;
+    }
+
+    card->max_data_rate = 50000000;
+    if (switch_func_timing == SD_SWITCH_FUNC_TIMING_SDR104)
+    {
+        LOG_I("sd: switch to SDR104 mode\n");
+        mmcsd_set_timing(card->host, MMCSD_TIMING_UHS_SDR104);
+        mmcsd_set_clock(card->host, 208000000);
+        err = mmcsd_excute_tuning(card);
+        card->max_data_rate = 208000000;
+    }
+    else if (switch_func_timing == SD_SWITCH_FUNC_TIMING_SDR50)
+    {
+        LOG_I("sd: switch to SDR50 mode\n");
+        mmcsd_set_timing(card->host, MMCSD_TIMING_UHS_SDR50);
+        mmcsd_set_clock(card->host, 100000000);
+        err = mmcsd_excute_tuning(card);
+        card->max_data_rate = 10000000;
+    }
+    else if (switch_func_timing == SD_SWITCH_FUNC_TIMING_DDR50)
+    {
+        LOG_I("sd: switch to DDR50 mode\n");
+        mmcsd_set_timing(card->host, MMCSD_TIMING_UHS_DDR50);
+        mmcsd_set_clock(card->host, 50000000);
+    }
+    else
+    {
+        LOG_I("sd: switch to High Speed / SDR25 mode \n");
+        mmcsd_set_timing(card->host, MMCSD_TIMING_SD_HS);
+        mmcsd_set_clock(card->host, 50000000);
+    }
 
 err:
     rt_free(buf);
@@ -511,6 +577,82 @@ rt_int32_t mmcsd_get_scr(struct rt_mmcsd_card *card, rt_uint32_t *scr)
     return 0;
 }
 
+static rt_err_t mmcsd_read_sd_status(struct rt_mmcsd_card *card, rt_uint32_t *sd_status)
+{
+    rt_int32_t err;
+    struct rt_mmcsd_req req;
+    struct rt_mmcsd_cmd cmd;
+    struct rt_mmcsd_data data;
+
+    err = mmcsd_app_cmd(card->host, card);
+    if (err)
+        return err;
+
+    rt_memset(&req, 0, sizeof(struct rt_mmcsd_req));
+    rt_memset(&cmd, 0, sizeof(struct rt_mmcsd_cmd));
+    rt_memset(&data, 0, sizeof(struct rt_mmcsd_data));
+
+    req.cmd = &cmd;
+    req.data = &data;
+
+    cmd.cmd_code = SEND_STATUS;
+    cmd.arg = 0;
+    cmd.flags = RESP_SPI_R1 | RESP_R1 | CMD_ADTC;
+
+    data.blksize = 64;
+    data.blks = 1;
+    data.flags = DATA_DIR_READ;
+    data.buf = sd_status;
+
+    mmcsd_set_data_timeout(&data, card);
+
+    mmcsd_send_request(card->host, &req);
+
+    if (cmd.err)
+        return cmd.err;
+    if (data.err)
+        return data.err;
+
+    /* Convert endian */
+    for (uint32_t i=0; i < 8; i++)
+    {
+        uint32_t tmp = sd_status[i];
+        sd_status[i] = sd_status[15 - i];
+        sd_status[15 - i] = tmp;
+    }
+    for (uint32_t i=0; i < 16; i++)
+    {
+        sd_status[i] = be32_to_cpu(sd_status[i]);
+    }
+
+
+    return 0;
+}
+
+static rt_err_t sd_switch_voltage(struct rt_mmcsd_host *host)
+{
+    rt_err_t err;
+    struct rt_mmcsd_cmd cmd = { 0 };
+
+    cmd.cmd_code = VOLTAGE_SWITCH;
+    cmd.arg = 0;
+    cmd.flags = RESP_R1 | CMD_AC;
+
+    err = mmcsd_send_cmd(host, &cmd, 0);
+    if (err)
+        return err;
+
+    return RT_EOK;
+}
+
+static rt_err_t sd_switch_uhs_voltage(struct rt_mmcsd_host *host)
+{
+    if (host->ops->switch_uhs_voltage != RT_NULL)
+    {
+        return host->ops->switch_uhs_voltage(host);
+    }
+    return -ENOSYS;
+}
 
 static rt_int32_t mmcsd_sd_init_card(struct rt_mmcsd_host *host,
                                      rt_uint32_t           ocr)
@@ -532,10 +674,27 @@ static rt_int32_t mmcsd_sd_init_card(struct rt_mmcsd_host *host,
     if (!err)
         ocr |= 1 << 30;
 
-    err = mmcsd_send_app_op_cond(host, ocr, RT_NULL);
+    /* Switch to UHS voltage if both Host and the Card support this feature */
+    if (((host->valid_ocr & VDD_165_195) != 0) && (host->ops->switch_uhs_voltage != RT_NULL))
+    {
+        ocr |= OCR_S18R;
+    }
+    err = mmcsd_send_app_op_cond(host, ocr, &ocr);
     if (err)
         goto err2;
 
+    /* Select voltage */
+    if (ocr & OCR_S18R)
+    {
+        ocr = VDD_165_195;
+        err = sd_switch_voltage(host);
+        if (err)
+           goto err2;
+        err = sd_switch_uhs_voltage(host);
+        if (err)
+            goto err2;
+    }
+
     if (controller_is_spi(host))
         err = mmcsd_get_cid(host, resp);
     else
@@ -596,8 +755,33 @@ static rt_int32_t mmcsd_sd_init_card(struct rt_mmcsd_host *host,
             goto err1;
     }
 
+    /*switch bus width*/
+    if ((host->flags & MMCSD_BUSWIDTH_4) && (card->scr.sd_bus_widths & SD_SCR_BUS_WIDTH_4))
+    {
+        err = mmcsd_app_set_bus_width(card, MMCSD_BUS_WIDTH_4);
+        if (err)
+            goto err1;
+
+        mmcsd_set_bus_width(host, MMCSD_BUS_WIDTH_4);
+    }
+    mmcsd_set_timing(host, MMCSD_TIMING_LEGACY);
+    mmcsd_set_clock(host, 25000000);
+
+    /* Read and decode SD Status and check whether UHS mode is supported */
+    union rt_sd_status sd_status;
+    err = mmcsd_read_sd_status(card, sd_status.status_words);
+    if (err)
+        goto err1;
+    if ((sd_status.uhs_speed_grade > 0) && (ocr & VDD_165_195))
+    {
+        /* Assume the card supports all UHS-I modes because we cannot find any mainstreaming card
+         * that can support only part of the following modes.
+         */
+        card->flags |= CARD_FLAG_SDR50 | CARD_FLAG_SDR104 | CARD_FLAG_DDR50;
+    }
+
     /*
-     * change SD card to high-speed, only SD2.0 spec
+     * change SD card to the highest supported speed
      */
     err = mmcsd_switch(card);
     if (err)
@@ -605,30 +789,17 @@ static rt_int32_t mmcsd_sd_init_card(struct rt_mmcsd_host *host,
 
     /* set bus speed */
     max_data_rate = (unsigned int)-1;
-
-    if (card->flags & CARD_FLAG_HIGHSPEED)
+    if (max_data_rate < card->hs_max_data_rate)
     {
-        if (max_data_rate > card->hs_max_data_rate)
-            max_data_rate = card->hs_max_data_rate;
+        max_data_rate = card->hs_max_data_rate;
     }
-    else if (max_data_rate > card->max_data_rate)
+    if (max_data_rate < card->max_data_rate)
     {
         max_data_rate = card->max_data_rate;
     }
 
     mmcsd_set_clock(host, max_data_rate);
 
-    /*switch bus width*/
-    if ((host->flags & MMCSD_BUSWIDTH_4) &&
-        (card->scr.sd_bus_widths & SD_SCR_BUS_WIDTH_4))
-    {
-        err = mmcsd_app_set_bus_width(card, MMCSD_BUS_WIDTH_4);
-        if (err)
-            goto err1;
-
-        mmcsd_set_bus_width(host, MMCSD_BUS_WIDTH_4);
-    }
-
     host->card = card;
 
     return 0;
@@ -659,14 +830,6 @@ rt_int32_t init_sd(struct rt_mmcsd_host *host, rt_uint32_t ocr)
             goto _err;
     }
 
-    if (ocr & VDD_165_195)
-    {
-        LOG_I(" SD card claims to support the "
-               "incompletely defined 'low voltage range'. This "
-               "will be ignored.");
-        ocr &= ~VDD_165_195;
-    }
-
     current_ocr = mmcsd_select_voltage(host, ocr);
 
     /*