@@ -1378,6 +1378,14 @@ static sd_rsp_type_t sd_normal_command(SDState *sd, SDRequest req)
}
break;
+ case 21: /* CMD21: mmc SEND TUNING_BLOCK */
+ if (sd->emmc && (sd->state == sd_transfer_state)) {
+ sd->state = sd_sendingdata_state;
+ sd->data_offset = 0;
+ return sd_r1;
+ }
+ break;
+
case 23: /* CMD23: SET_BLOCK_COUNT */
if (sd->spec_version < SD_PHY_SPECv3_01_VERS) {
break;
@@ -2112,6 +2120,30 @@ static const uint8_t sd_tuning_block_pattern[SD_TUNING_BLOCK_SIZE] = {
0xbb, 0xff, 0xf7, 0xff, 0xf7, 0x7f, 0x7b, 0xde,
};
+#define EXCSD_BUS_WIDTH_OFFSET 183
+#define BUS_WIDTH_8_MASK 0x4
+#define BUS_WIDTH_4_MASK 0x2
+#define MMC_TUNING_BLOCK_SIZE 128
+
+static const uint8_t mmc_tunning_block_pattern[128] = {
+ 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00,
+ 0xff, 0xff, 0xcc, 0xcc, 0xcc, 0x33, 0xcc, 0xcc,
+ 0xcc, 0x33, 0x33, 0xcc, 0xcc, 0xcc, 0xff, 0xff,
+ 0xff, 0xee, 0xff, 0xff, 0xff, 0xee, 0xee, 0xff,
+ 0xff, 0xff, 0xdd, 0xff, 0xff, 0xff, 0xdd, 0xdd,
+ 0xff, 0xff, 0xff, 0xbb, 0xff, 0xff, 0xff, 0xbb,
+ 0xbb, 0xff, 0xff, 0xff, 0x77, 0xff, 0xff, 0xff,
+ 0x77, 0x77, 0xff, 0x77, 0xbb, 0xdd, 0xee, 0xff,
+ 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00,
+ 0x00, 0xff, 0xff, 0xcc, 0xcc, 0xcc, 0x33, 0xcc,
+ 0xcc, 0xcc, 0x33, 0x33, 0xcc, 0xcc, 0xcc, 0xff,
+ 0xff, 0xff, 0xee, 0xff, 0xff, 0xff, 0xee, 0xee,
+ 0xff, 0xff, 0xff, 0xdd, 0xff, 0xff, 0xff, 0xdd,
+ 0xdd, 0xff, 0xff, 0xff, 0xbb, 0xff, 0xff, 0xff,
+ 0xbb, 0xbb, 0xff, 0xff, 0xff, 0x77, 0xff, 0xff,
+ 0xff, 0x77, 0x77, 0xff, 0x77, 0xbb, 0xdd, 0xee,
+};
+
uint8_t sd_read_byte(SDState *sd)
{
/* TODO: Append CRCs */
@@ -2204,6 +2236,21 @@ uint8_t sd_read_byte(SDState *sd)
ret = sd_tuning_block_pattern[sd->data_offset++];
break;
+ case 21: /* CMD21: SEND_TUNNING_BLOCK (MMC) */
+ if (sd->data_offset >= MMC_TUNING_BLOCK_SIZE - 1) {
+ sd->state = sd_transfer_state;
+ }
+ if (sd->ext_csd[EXCSD_BUS_WIDTH_OFFSET] & BUS_WIDTH_8_MASK) {
+ ret = mmc_tunning_block_pattern[sd->data_offset++];
+ } else {
+ /* Return LSB Nibbles of two byte from the 8bit tuning block
+ * for 4bit mode
+ */
+ ret = mmc_tunning_block_pattern[sd->data_offset++] & 0x0F;
+ ret |= (mmc_tunning_block_pattern[sd->data_offset++] & 0x0F) << 4;
+ }
+ break;
+
case 22: /* ACMD22: SEND_NUM_WR_BLOCKS */
ret = sd->data[sd->data_offset ++];