diff --git a/components/drivers/include/drivers/mmcsd_card.h b/components/drivers/include/drivers/mmcsd_card.h index 1da7ac921a..13173f1413 100644 --- a/components/drivers/include/drivers/mmcsd_card.h +++ b/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 */ diff --git a/components/drivers/include/drivers/mmcsd_cmd.h b/components/drivers/include/drivers/mmcsd_cmd.h index d470942815..06212c5162 100644 --- a/components/drivers/include/drivers/mmcsd_cmd.h +++ b/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 */ diff --git a/components/drivers/include/drivers/mmcsd_host.h b/components/drivers/include/drivers/mmcsd_host.h index 405adacb53..73c1b971d8 100644 --- a/components/drivers/include/drivers/mmcsd_host.h +++ b/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 */ diff --git a/components/drivers/include/drivers/sd.h b/components/drivers/include/drivers/sd.h index 45e27aa8a7..4b7b093600 100644 --- a/components/drivers/include/drivers/sd.h +++ b/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); diff --git a/components/drivers/sdio/sd.c b/components/drivers/sdio/sd.c index cebb86ee43..222c1fd95f 100644 --- a/components/drivers/sdio/sd.c +++ b/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 @@ -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,31 +755,8 @@ static rt_int32_t mmcsd_sd_init_card(struct rt_mmcsd_host *host, goto err1; } - /* - * change SD card to high-speed, only SD2.0 spec - */ - err = mmcsd_switch(card); - if (err) - goto err1; - - /* set bus speed */ - max_data_rate = (unsigned int)-1; - - if (card->flags & CARD_FLAG_HIGHSPEED) - { - if (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) - { - 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)) + 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) @@ -628,6 +764,41 @@ static rt_int32_t mmcsd_sd_init_card(struct rt_mmcsd_host *host, 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 the highest supported speed + */ + err = mmcsd_switch(card); + if (err) + goto err1; + + /* set bus speed */ + max_data_rate = (unsigned int)-1; + if (max_data_rate < card->hs_max_data_rate) + { + max_data_rate = card->hs_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); host->card = card; @@ -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); /*