|
@@ -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);
|
|
|
|
|
|
/*
|