/* vim: set ts=4 sw=4 fenc=cp936 et fdm=marker: */ /* * mmc driver * * ChangeLog: * 2022/03/29: surenyi926 add */ #include #include #include "mmcDevice.h" #include "mmcPlatform.h" #include "mmc.h" /* MMC_BLKSIZE */ /* {{{: mmc devices */ static struct mmc_device *__mmc_dev_list = NULL, **__mmc_dev_tail = &__mmc_dev_list; /* }}} */ /* {{{: driver api */ int mmcAddDevice(struct mmc_device *dev) { struct mmc_device *mmc; if ((dev == NULL) || (mmcLock() != 0)) { return -1; } /* redundancy ? */ mmc = __mmc_dev_list; while (mmc) { if (strcmp(mmc_device_name(mmc), mmc_device_name(dev)) == 0) { mmcPrintf("%s is in the mmc subsystem\r\n", mmc_device_name(dev)); mmcUnlock(); return 1; } mmc = mmc->next; } dev->next = NULL; *__mmc_dev_tail = dev; __mmc_dev_tail = &dev->next; mmcUnlock(); return 0; } void mmcSetDSR(struct mmc_device *dev, uint16_t dsr) { dev->dsr = dsr; } int mmcSetCapacity(struct mmc_device *mmc, int part_num) { if ((mmc->quirks & MMC_QUIRK_CAPACITY) == MMC_QUIRK_CAPACITY) { return 0; } switch (part_num) { case 0: mmc->capacity = mmc->capacity_user; break; case 1: case 2: mmc->capacity = mmc->capacity_boot; break; case 3: mmc->capacity = mmc->capacity_rpmb; break; case 4: case 5: case 6: case 7: mmc->capacity = mmc->capacity_gp[part_num - 4]; break; default: return -1; } return 0; } int mmcSendStatus(struct mmc_device *mmc, int timeout) { struct mmc_cmd cmd; int err, retries = 5; /* CMD13: SEND_STATUS */ cmd.cmdidx = 13; cmd.resp_type = MMC_RSP_R1; cmd.cmdarg = mmc->rca << 16; while (1) { err = mmc_send_command(mmc, &cmd, NULL); if (!err) { if ((cmd.response[0] & MMC_STATUS_RDY_FOR_DATA) && ((cmd.response[0] & MMC_STATUS_CURR_STATE) != MMC_STATE_PRG)) break; if (cmd.response[0] & MMC_STATUS_MASK) { mmcPrintf("Status Error: 0x%08x\r\n", cmd.response[0]); return -70; } } else if (--retries < 0) return err; if (timeout-- <= 0) break; mmcDelayUs(1000); } if (timeout <= 0) { mmcPrintf("Timeout waiting card ready\r\n"); return -110; } return 0; } static const uint8_t tuning_blk_pattern_4bit[] = { 0xff, 0x0f, 0xff, 0x00, 0xff, 0xcc, 0xc3, 0xcc, 0xc3, 0x3c, 0xcc, 0xff, 0xfe, 0xff, 0xfe, 0xef, 0xff, 0xdf, 0xff, 0xdd, 0xff, 0xfb, 0xff, 0xfb, 0xbf, 0xff, 0x7f, 0xff, 0x77, 0xf7, 0xbd, 0xef, 0xff, 0xf0, 0xff, 0xf0, 0x0f, 0xfc, 0xcc, 0x3c, 0xcc, 0x33, 0xcc, 0xcf, 0xff, 0xef, 0xff, 0xee, 0xff, 0xfd, 0xff, 0xfd, 0xdf, 0xff, 0xbf, 0xff, 0xbb, 0xff, 0xf7, 0xff, 0xf7, 0x7f, 0x7b, 0xde, }; static const uint8_t tuning_blk_pattern_8bit[] = { 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, }; int mmcSendTuning(struct mmc_device *mmc, unsigned int opcode) { struct mmc_cmd cmd; struct mmc_data data; const uint8_t *tuning_block_pattern; uint8_t data_buf[128]; int size, err; if (mmc->bus_width == 8) { tuning_block_pattern = tuning_blk_pattern_8bit; size = sizeof(tuning_blk_pattern_8bit); } else if (mmc->bus_width == 4) { tuning_block_pattern = tuning_blk_pattern_4bit; size = sizeof(tuning_blk_pattern_4bit); } else { return -22; } /* CMD21: SEND_TUNNING_BLOCK */ cmd.cmdidx = opcode; cmd.cmdarg = 0; cmd.resp_type = MMC_RSP_R1; data.dest = (void *)data_buf; data.blocks = 1; data.blocksize = size; data.flags = MMC_DATA_READ; err = mmc_send_command(mmc, &cmd, &data); if (err) return err; if (memcmp(data_buf, tuning_block_pattern, size)) { return -5; } return 0; } void mmcForEachDevice(int (*func)(struct mmc_device *, void *arg), void *arg) { struct mmc_device *cur, *next; if (!func) { return; } if (mmcLock() != 0) { return; } cur = __mmc_dev_list; while (cur) { next = cur->next; if (func(cur, arg)) { break; } cur = next; } mmcUnlock(); } void mmcPrintInfo(struct mmc_device *mmc) { int i; double cap; mmcPrintf(" Device: %s\r\n", mmc->name); mmcPrintf(" Manufacturer ID: %x\r\n", mmc->cid[0] >> 24); mmcPrintf(" OEM: %x\r\n", (mmc->cid[0] >> 8) & 0xffff); mmcPrintf(" Name: %c%c%c%c%c[%02x-%02x-%02x-%02x-%02x]\r\n", mmc->cid[0] & 0xff, (mmc->cid[1] >> 24), (mmc->cid[1] >> 16) & 0xff, (mmc->cid[1] >> 8) & 0xff, mmc->cid[1] & 0xff, mmc->cid[0] & 0xff, (mmc->cid[1] >> 24), (mmc->cid[1] >> 16) & 0xff, (mmc->cid[1] >> 8) & 0xff, mmc->cid[1] & 0xff); mmcPrintf(" Rd Block Len: %d\r\n", mmc->read_bl_len); mmcPrintf(" MMC version: %d.%d", EXTRACT_SDMMC_MAJOR_VERSION(mmc->version), EXTRACT_SDMMC_MINOR_VERSION(mmc->version)); if (EXTRACT_SDMMC_CHANGE_VERSION(mmc->version) != 0) mmcPrintf(".%d", EXTRACT_SDMMC_CHANGE_VERSION(mmc->version)); mmcPrintf("\r\n"); cap = 1.0 * mmc->capacity / 1024.0 / 1024.0 / 1024.0; mmcPrintf(" High Capacity: %s\r\n", ((mmc->ocr & OCR_HCS) == OCR_HCS) ? "Yes" : "No"); mmcPrintf(" Capacity: %.2lfGB\r\n", cap); mmcPrintf(" Bus Width: %d-bit%s\r\n", mmc->bus_width, mmc->ddr_mode ? " DDR" : ""); mmcPrintf(" Erase Group Size: %llu\r\n", (((uint64_t)mmc->erase_grp_size) << 9)); if (mmc->version >= MMC_VERSION_4_41) { int has_enh = (mmc->part_support & ENHNCD_SUPPORT) != 0; int usr_enh = has_enh && (mmc->part_attr & EXT_CSD_ENH_USR); mmcPrintf(" User Capacity: %llu %s ", mmc->capacity_user, usr_enh ? " ENH" : ""); if (mmc->wr_rel_set & EXT_CSD_WR_DATA_REL_USR) mmcPrintf(" WRREL\r\n"); else mmcPrintf("\r\n"); if (usr_enh) { mmcPrintf("User Enhanced Start: %llu\r\n", mmc->enh_user_start); mmcPrintf(" User Enhanced Size: %llu\r\n", mmc->enh_user_size); } mmcPrintf("Boot Capacity: %llu %s\r\n", mmc->capacity_boot, has_enh ? " ENH" : ""); mmcPrintf("RPMB Capacity: %llu %s\r\n", mmc->capacity_rpmb, has_enh ? " ENH" : ""); for (i = 0; i < ARRAY_SIZE(mmc->capacity_gp); i++) { int is_enh = has_enh && (mmc->part_attr & EXT_CSD_ENH_GP(i)); if (mmc->capacity_gp[i]) { mmcPrintf("GP%i Capacity: %llu%s", i+1, mmc->capacity_gp[i], is_enh ? " ENH" : ""); if (mmc->wr_rel_set & EXT_CSD_WR_DATA_REL_GP(i)) mmcPrintf(" WRREL\r\n"); else mmcPrintf("\r\n"); } } } mmcPrintf("\r\n"); mmcPrintf("CID: %08x-%08x-%08x-%08x\r\n", mmc->cid[0], mmc->cid[1], mmc->cid[2], mmc->cid[3]); } static inline int goto_idle(struct mmc_device *mmc) { struct mmc_cmd cmd; /* CMD0: put mmc device to idle mode*/ cmd.cmdidx = 0; /* CMD0 */ cmd.cmdarg = 0; cmd.resp_type = MMC_RSP_NONE; return mmc_send_command(mmc, &cmd, NULL); } int mmcGoIdle(struct mmc_device *mmc) { return goto_idle(mmc); } /* }}} */ /* {{{: interal functions */ static int send_op_cond(struct mmc_device *mmc) { struct mmc_cmd cmd; int err; /* CMD1: set operations conditions */ cmd.cmdidx = 1; cmd.resp_type = MMC_RSP_R3; cmd.cmdarg = mmc->voltages & OCR_VOLTAGE_MASK; if (mmc->hcs_mode) { cmd.cmdarg |= OCR_HCS; } err = mmc_send_command(mmc, &cmd, NULL); if (err) return err; mmc->ocr = cmd.response[0]; return 0; } static int mmc_send_ext_csd(struct mmc_device *mmc, uint8_t *ext_csd) { struct mmc_cmd cmd; struct mmc_data data; /* CMD8: Get the Card Status Register */ cmd.cmdidx = 8; cmd.resp_type = MMC_RSP_R1; cmd.cmdarg = 0; data.dest = (char *)ext_csd; data.blocks = 1; data.blocksize = MMC_BLKSIZE; data.flags = MMC_DATA_READ; return mmc_send_command(mmc, &cmd, &data); } static int __mmc_switch(struct mmc_device *mmc, uint8_t index, uint8_t value, int send_status) { struct mmc_cmd cmd; int timeout = 1000; int retries = 3; int ret; /* CMD6: switch */ cmd.cmdidx = 6; cmd.resp_type = MMC_RSP_R1b; cmd.cmdarg = (MMC_SWITCH_MODE_WRITE_BYTE << 24) | (index << 16) | (value << 8); /* mmcPrintf("switch (6): %x\r\n", cmd.cmdarg); */ while (retries > 0) { ret = mmc_send_command(mmc, &cmd, NULL); if (ret) { retries--; continue; } if (!send_status) { mmcDelayUs(50000); return 0; } /* Waiting for the ready status */ return mmcSendStatus(mmc, timeout); } /* mmcPrintf("%s[%d]: %d\r\n", __func__, __LINE__, ret); */ return ret; } static int mmc_switch(struct mmc_device *mmc, uint8_t index, uint8_t value) { return __mmc_switch(mmc, index, value, 1); } static int mmc_startup_v4(struct mmc_device *mmc) { int err, i; uint64_t capacity; int has_parts = 0; int part_completed; uint8_t *ext_csd; static const uint32_t mmc_versions[] = { MMC_VERSION_4, MMC_VERSION_4_1, MMC_VERSION_4_2, MMC_VERSION_4_3, MMC_VERSION_4_4, MMC_VERSION_4_41, MMC_VERSION_4_5, MMC_VERSION_5_0, MMC_VERSION_5_1 }; if (mmc->version < MMC_VERSION_4) return 0; /* check ext_csd version and capacity */ err = mmc_send_ext_csd(mmc, mmc->ext_csd); if (err) goto error; ext_csd = mmc->ext_csd; #if 0 for (i = 0; i < sizeof mmc->ext_csd; ++i) { mmcPrintf("%02x%s", mmc->ext_csd[i], (i + 1) % 16 == 0 ? "\r\n" : " "); } mmcPrintf("\r\n"); #endif if (ext_csd[EXT_CSD_REV] >= ARRAY_SIZE(mmc_versions)) { /* mmcPrintf("EXT_CSD_REV: %d\r\n", ext_csd[EXT_CSD_REV]); */ return -22; } mmc->version = mmc_versions[ext_csd[EXT_CSD_REV]]; if (mmc->version >= MMC_VERSION_4_2) { /* * According to the JEDEC Standard, the value of * ext_csd's capacity is valid if the value is more * than 2GB */ capacity = ext_csd[EXT_CSD_SEC_CNT] << 0 | ext_csd[EXT_CSD_SEC_CNT + 1] << 8 | ext_csd[EXT_CSD_SEC_CNT + 2] << 16 | ext_csd[EXT_CSD_SEC_CNT + 3] << 24; capacity *= MMC_BLKSIZE; if ((capacity >> 20) > 2 * 1024) mmc->capacity_user = capacity; } /* The partition data may be non-zero but it is only * effective if PARTITION_SETTING_COMPLETED is set in * EXT_CSD, so ignore any data if this bit is not set, * except for enabling the high-capacity group size * definition (see below). */ part_completed = !!(ext_csd[EXT_CSD_PARTITION_SETTING] & EXT_CSD_PARTITION_SETTING_COMPLETED); /* store the partition info of emmc */ mmc->part_support = ext_csd[EXT_CSD_PARTITIONING_SUPPORT]; if ((ext_csd[EXT_CSD_PARTITIONING_SUPPORT] & PART_SUPPORT) || ext_csd[EXT_CSD_BOOT_MULT]) mmc->part_config = ext_csd[EXT_CSD_PART_CONF]; if (part_completed && (ext_csd[EXT_CSD_PARTITIONING_SUPPORT] & ENHNCD_SUPPORT)) mmc->part_attr = ext_csd[EXT_CSD_PARTITIONS_ATTRIBUTE]; mmc->capacity_boot = ext_csd[EXT_CSD_BOOT_MULT] << 17; mmc->capacity_rpmb = ext_csd[EXT_CSD_RPMB_MULT] << 17; for (i = 0; i < 4; i++) { int idx = EXT_CSD_GP_SIZE_MULT + i * 3; uint32_t mult = (ext_csd[idx + 2] << 16) + (ext_csd[idx + 1] << 8) + ext_csd[idx]; if (mult) has_parts = 1; if (!part_completed) continue; mmc->capacity_gp[i] = mult; mmc->capacity_gp[i] *= ext_csd[EXT_CSD_HC_ERASE_GRP_SIZE]; mmc->capacity_gp[i] *= ext_csd[EXT_CSD_HC_WP_GRP_SIZE]; mmc->capacity_gp[i] <<= 19; } if (part_completed) { mmc->enh_user_size = (ext_csd[EXT_CSD_ENH_SIZE_MULT + 2] << 16) + (ext_csd[EXT_CSD_ENH_SIZE_MULT + 1] << 8) + ext_csd[EXT_CSD_ENH_SIZE_MULT]; mmc->enh_user_size *= ext_csd[EXT_CSD_HC_ERASE_GRP_SIZE]; mmc->enh_user_size *= ext_csd[EXT_CSD_HC_WP_GRP_SIZE]; mmc->enh_user_size <<= 19; mmc->enh_user_start = (ext_csd[EXT_CSD_ENH_START_ADDR + 3] << 24) + (ext_csd[EXT_CSD_ENH_START_ADDR + 2] << 16) + (ext_csd[EXT_CSD_ENH_START_ADDR + 1] << 8) + ext_csd[EXT_CSD_ENH_START_ADDR]; if ((mmc->ocr & OCR_HCS) == OCR_HCS) mmc->enh_user_start <<= 9; } /* * Host needs to enable ERASE_GRP_DEF bit if device is * partitioned. This bit will be lost every time after a reset * or power off. This will affect erase size. */ if (part_completed) has_parts = 1; if ((ext_csd[EXT_CSD_PARTITIONING_SUPPORT] & PART_SUPPORT) && (ext_csd[EXT_CSD_PARTITIONS_ATTRIBUTE] & PART_ENH_ATTRIB)) has_parts = 1; if (has_parts) { err = mmc_switch(mmc, EXT_CSD_ERASE_GROUP_DEF, 1); if (err) goto error; ext_csd[EXT_CSD_ERASE_GROUP_DEF] = 1; } if (ext_csd[EXT_CSD_ERASE_GROUP_DEF] & 0x01) { /* Read out group size from ext_csd */ mmc->erase_grp_size = ext_csd[EXT_CSD_HC_ERASE_GRP_SIZE] * 1024; /* * if high capacity and partition setting completed * SEC_COUNT is valid even if it is smaller than 2 GiB * JEDEC Standard JESD84-B45, 6.2.4 */ if (((mmc->ocr & OCR_HCS) == OCR_HCS) && part_completed) { capacity = (ext_csd[EXT_CSD_SEC_CNT]) | (ext_csd[EXT_CSD_SEC_CNT + 1] << 8) | (ext_csd[EXT_CSD_SEC_CNT + 2] << 16) | (ext_csd[EXT_CSD_SEC_CNT + 3] << 24); capacity *= MMC_BLKSIZE; mmc->capacity_user = capacity; } } else { /* Calculate the group size from the csd value. */ int erase_gsz, erase_gmul; erase_gsz = (mmc->csd[2] & 0x00007c00) >> 10; erase_gmul = (mmc->csd[2] & 0x000003e0) >> 5; mmc->erase_grp_size = (erase_gsz + 1) * (erase_gmul + 1); } mmc->wr_rel_set = ext_csd[EXT_CSD_WR_REL_SET]; return 0; error: return err; } const char *mmc_mode_name(enum bus_mode mode) { static const char *const names[] = { [MMC_LEGACY] = "MMC legacy", [SD_LEGACY] = "SD Legacy", [MMC_HS] = "MMC High Speed (26MHz)", [SD_HS] = "SD High Speed (50MHz)", [UHS_SDR12] = "UHS SDR12 (25MHz)", [UHS_SDR25] = "UHS SDR25 (50MHz)", [UHS_SDR50] = "UHS SDR50 (100MHz)", [UHS_SDR104] = "UHS SDR104 (208MHz)", [UHS_DDR50] = "UHS DDR50 (50MHz)", [MMC_HS_52] = "MMC High Speed (52MHz)", [MMC_DDR_52] = "MMC DDR52 (52MHz)", [MMC_HS_200] = "HS200 (200MHz)", [MMC_HS_400] = "HS400 (200MHz)", }; if (mode >= MMC_MODES_END) return "Unknown mode"; else return names[mode]; } static uint32_t mmc_mode2freq(struct mmc_device *mmc, enum bus_mode mode) { static const int freqs[] = { [MMC_LEGACY] = 25000000, [SD_LEGACY] = 25000000, [MMC_HS] = 26000000, [SD_HS] = 50000000, [MMC_HS_52] = 52000000, [MMC_DDR_52] = 52000000, [UHS_SDR12] = 25000000, [UHS_SDR25] = 50000000, [UHS_SDR50] = 100000000, [UHS_DDR50] = 50000000, [UHS_SDR104] = 208000000, [MMC_HS_200] = 200000000, [MMC_HS_400] = 200000000, }; if (mode == MMC_LEGACY) return mmc->legacy_speed; if (mode >= MMC_MODES_END) return 0; else return freqs[mode]; } static inline int mmc_is_mode_ddr(enum bus_mode mode) { if (mode == MMC_DDR_52) return 1; else if (mode == UHS_DDR50) return 1; else if (mode == MMC_HS_400) return 1; else return 0; } static int mmc_select_mode(struct mmc_device *mmc, enum bus_mode mode) { mmc->selected_mode = mode; mmc->tran_speed = mmc_mode2freq(mmc, mode); mmc->ddr_mode = mmc_is_mode_ddr(mode); mmcPrintf("%s: selecting mode %s\r\n", mmc_device_name(mmc), mmc_mode_name(mode)); /* , mmc->tran_speed / 1000000); */ return 0; } static int mmc_set_card_speed(struct mmc_device *mmc, enum bus_mode mode, int hsdowngrade) { int err; int speed_bits; uint8_t test_csd[MMC_BLKSIZE]; switch (mode) { case MMC_HS: case MMC_HS_52: case MMC_DDR_52: speed_bits = EXT_CSD_TIMING_HS; break; case MMC_HS_200: speed_bits = EXT_CSD_TIMING_HS200; break; case MMC_HS_400: speed_bits = EXT_CSD_TIMING_HS400; break; case MMC_LEGACY: speed_bits = EXT_CSD_TIMING_LEGACY; break; default: return -22; } err = __mmc_switch(mmc, EXT_CSD_HS_TIMING, speed_bits, !hsdowngrade); if (err) return err; /* * In case the eMMC is in HS200/HS400 mode and we are downgrading * to HS mode, the card clock are still running much faster than * the supported HS mode clock, so we can not reliably read out * Extended CSD. Reconfigure the controller to run at HS mode. */ if (hsdowngrade) { mmc_select_mode(mmc, MMC_HS); mmc_set_clock(mmc, mmc_mode2freq(mmc, MMC_HS), 1); } if ((mode == MMC_HS) || (mode == MMC_HS_52)) { /* Now check to see that it worked */ err = mmc_send_ext_csd(mmc, test_csd); if (err) return err; /* No high-speed support */ if (!test_csd[EXT_CSD_HS_TIMING]) return -524; } return 0; } static int mmc_select_hs400(struct mmc_device *mmc) { int err; /* Set timing to HS200 for tuning */ err = mmc_set_card_speed(mmc, MMC_HS_200, 0); if (err) return err; /* configure the bus mode (host) */ mmc_select_mode(mmc, MMC_HS_200); mmc_set_clock(mmc, mmc->tran_speed, 1); /* execute tuning if needed */ err = mmc_execute_tuning(mmc, 21); /* CMD21 */ if (err) { mmcPrintf("tuning failed\r\n"); return err; } /* Set back to HS */ mmc_set_card_speed(mmc, MMC_HS, 1); err = mmc_switch(mmc, EXT_CSD_BUS_WIDTH, EXT_CSD_BUS_WIDTH_8 | EXT_CSD_DDR_FLAG); if (err) return err; err = mmc_set_card_speed(mmc, MMC_HS_400, 0); if (err) return err; mmc_select_mode(mmc, MMC_HS_400); err = mmc_set_clock(mmc, mmc->tran_speed, 1); if (err) return err; return 0; } static inline int bus_width(uint32_t cap) { if (cap == MMC_MODE_8BIT) return 8; if (cap == MMC_MODE_4BIT) return 4; if (cap == MMC_MODE_1BIT) return 1; mmcPrintf("invalid bus witdh capability 0x%x\r\n", cap); return 0; } /* * read the compare the part of ext csd that is constant. * This can be used to check that the transfer is working * as expected. */ static int mmc_read_and_compare_ext_csd(struct mmc_device *mmc) { int err; const uint8_t *ext_csd = mmc->ext_csd; uint8_t test_csd[MMC_BLKSIZE]; if (mmc->version < MMC_VERSION_4) return 0; err = mmc_send_ext_csd(mmc, test_csd); if (err) return err; /* Only compare read only fields */ if (ext_csd[EXT_CSD_PARTITIONING_SUPPORT] == test_csd[EXT_CSD_PARTITIONING_SUPPORT] && ext_csd[EXT_CSD_HC_WP_GRP_SIZE] == test_csd[EXT_CSD_HC_WP_GRP_SIZE] && ext_csd[EXT_CSD_REV] == test_csd[EXT_CSD_REV] && ext_csd[EXT_CSD_HC_ERASE_GRP_SIZE] == test_csd[EXT_CSD_HC_ERASE_GRP_SIZE] && memcmp(&ext_csd[EXT_CSD_SEC_CNT], &test_csd[EXT_CSD_SEC_CNT], 4) == 0) return 0; return -74; } struct mode_width_tuning { enum bus_mode mode; uint32_t widths; uint32_t tuning; }; static const struct mode_width_tuning mmc_modes_by_pref[] = { { .mode = MMC_HS_400, .widths = MMC_MODE_8BIT, .tuning = 21, /* CMD21 */ }, { .mode = MMC_HS_200, .widths = MMC_MODE_8BIT | MMC_MODE_4BIT, .tuning = 21, /* CMD21 */ }, { .mode = MMC_DDR_52, .widths = MMC_MODE_8BIT | MMC_MODE_4BIT, }, { .mode = MMC_HS_52, .widths = MMC_MODE_8BIT | MMC_MODE_4BIT | MMC_MODE_1BIT, }, { .mode = MMC_HS, .widths = MMC_MODE_8BIT | MMC_MODE_4BIT | MMC_MODE_1BIT, }, { .mode = MMC_LEGACY, .widths = MMC_MODE_8BIT | MMC_MODE_4BIT | MMC_MODE_1BIT, } }; #define for_each_mmc_mode_by_pref(caps, mwt) \ for (mwt = mmc_modes_by_pref;\ mwt < mmc_modes_by_pref + ARRAY_SIZE(mmc_modes_by_pref);\ mwt++) \ if (caps & MMC_CAP(mwt->mode)) static const struct ext_csd_bus_width { uint32_t cap; int is_ddr; uint32_t ext_csd_bits; } ext_csd_bus_width[] = { {MMC_MODE_8BIT, 1, EXT_CSD_DDR_BUS_WIDTH_8}, {MMC_MODE_4BIT, 1, EXT_CSD_DDR_BUS_WIDTH_4}, {MMC_MODE_8BIT, 0, EXT_CSD_BUS_WIDTH_8}, {MMC_MODE_4BIT, 0, EXT_CSD_BUS_WIDTH_4}, {MMC_MODE_1BIT, 0, EXT_CSD_BUS_WIDTH_1}, }; #define for_each_supported_width(caps, ddr, ecbv) \ for (ecbv = ext_csd_bus_width;\ ecbv < ext_csd_bus_width + ARRAY_SIZE(ext_csd_bus_width);\ ecbv++) \ if ((ddr == ecbv->is_ddr) && (caps & ecbv->cap)) static int mmc_select_mode_and_width(struct mmc_device *mmc, uint32_t card_caps) { int err; const struct mode_width_tuning *mwt; const struct ext_csd_bus_width *ecbw; /* Restrict card's capabilities by what the host can do */ card_caps &= mmc->host_caps; /* mmcPrintf("card_caps: %x\r\n", card_caps); */ /* Only version 4 of MMC supports wider bus widths */ if (mmc->version < MMC_VERSION_4) return 0; /* * In case the eMMC is in HS200/HS400 mode, downgrade to HS mode * before doing anything else, since a transition from either of * the HS200/HS400 mode directly to legacy mode is not supported. */ if (mmc->selected_mode == MMC_HS_200 || mmc->selected_mode == MMC_HS_400) mmc_set_card_speed(mmc, MMC_HS, 1); else mmc_set_clock(mmc, mmc->legacy_speed, 1); for_each_mmc_mode_by_pref(card_caps, mwt) { for_each_supported_width(card_caps & mwt->widths, mmc_is_mode_ddr(mwt->mode), ecbw) { /* configure the bus width (card + host) */ err = mmc_switch(mmc, EXT_CSD_BUS_WIDTH, ecbw->ext_csd_bits & ~EXT_CSD_DDR_FLAG); /* mmcPrintf("%s[%d]: %d\r\n", __func__, __LINE__, err); */ if (err) goto error; mmc_set_bus_width(mmc, bus_width(ecbw->cap)); if (mwt->mode == MMC_HS_400) { err = mmc_select_hs400(mmc); if (err) { mmcPrintf("Select HS400 failed %d\r\n", err); goto error; } } else { /* configure the bus speed (card) */ err = mmc_set_card_speed(mmc, mwt->mode, 0); if (err) goto error; /* * configure the bus width AND the ddr mode * (card). The host side will be taken care * of in the next step */ if (ecbw->ext_csd_bits & EXT_CSD_DDR_FLAG) { err = mmc_switch(mmc, EXT_CSD_BUS_WIDTH, ecbw->ext_csd_bits); if (err) goto error; } /* configure the bus mode (host) */ mmc_select_mode(mmc, mwt->mode); mmc_set_clock(mmc, mmc->tran_speed, 1); /* execute tuning if needed */ if (mwt->tuning) { err = mmc_execute_tuning(mmc, mwt->tuning); if (err) { mmcPrintf("%s: retraining tuning parameters.\r\n", mmc_device_name(mmc)); goto error; } /* tunning success, skip ext_csd checking. */ if (mmc->quirks & MMC_QUIRK_TUNNING) { return 0; } } } /* do a transfer to check the configuration */ err = mmc_read_and_compare_ext_csd(mmc); if (!err) return 0; error: /* if an error occured, revert to a safer bus mode */ mmc_switch(mmc, EXT_CSD_BUS_WIDTH, EXT_CSD_BUS_WIDTH_1); mmc_select_mode(mmc, MMC_LEGACY); mmc_set_bus_width(mmc, 1); mmc_set_clock(mmc, mmc->tran_speed, 1); } } /* mmcPrintf("%s: fallback to legacy mode\r\n", __func__); */ return -524; } static int mmc_get_capabilities(struct mmc_device *mmc) { uint8_t *ext_csd = mmc->ext_csd; char cardtype; mmc->card_caps |= MMC_MODE_1BIT | MMC_CAP(MMC_LEGACY); /* Only version 4 supports high-speed */ if (mmc->version < MMC_VERSION_4) return 0; mmc->card_caps |= MMC_MODE_4BIT | MMC_MODE_8BIT; cardtype = ext_csd[EXT_CSD_CARD_TYPE]; mmc->cardtype = cardtype; if (cardtype & (EXT_CSD_CARD_TYPE_HS200_1_2V | EXT_CSD_CARD_TYPE_HS200_1_8V)) { mmc->card_caps |= MMC_MODE_HS200; } if (cardtype & (EXT_CSD_CARD_TYPE_HS400_1_2V | EXT_CSD_CARD_TYPE_HS400_1_8V)) { mmc->card_caps |= MMC_MODE_HS400; } if (cardtype & EXT_CSD_CARD_TYPE_52) { if (cardtype & EXT_CSD_CARD_TYPE_DDR_52) mmc->card_caps |= MMC_MODE_DDR_52MHz; mmc->card_caps |= MMC_MODE_HS_52MHz; } if (cardtype & EXT_CSD_CARD_TYPE_26) mmc->card_caps |= MMC_MODE_HS; return 0; } /* frequency bases */ /* divided by 10 to be nice to platforms without floating point */ static const int fbase[] = { 10000, 100000, 1000000, 10000000, }; /* Multiplier values for TRAN_SPEED. Multiplied by 10 to be nice * to platforms without floating point. */ static const uint8_t multipliers[] = { 0, /* reserved */ 10, 12, 13, 15, 20, 25, 30, 35, 40, 45, 50, 55, 60, 70, 80, }; static int startup(struct mmc_device *mmc) { struct mmc_cmd cmd; int i, err, version; uint64_t cmult, csize; uint32_t mult, freq; /* send CMD2, put card to Indentify mode */ /* Put the Card in Identify Mode */ cmd.cmdidx = 2; cmd.resp_type = MMC_RSP_R2; cmd.cmdarg = 0; err = mmc_send_command(mmc, &cmd, NULL); if (err && (mmc->quirks & MMC_QUIRK_RETRY_SEND_CID)) { int retries = 4; /* * It has been seen that SEND_CID may fail on the first * attempt, let's try a few more time */ do { mmcDelayUs(50000); err = mmc_send_command(mmc, &cmd, NULL); if (!err) break; } while (retries--); } if (err) return -1; memcpy(mmc->cid, cmd.response, MMC_CID_LEN); /* sizeof (cmd.response) == 16 */ /* CMD3: set the Relative Address. */ cmd.cmdidx = 3; cmd.cmdarg = mmc->rca << 16; cmd.resp_type = MMC_RSP_R1; err = mmc_send_command(mmc, &cmd, NULL); if (err) return -2; /* CMD9: Get the Card-Specific Data (*/ cmd.cmdidx = 9; cmd.resp_type = MMC_RSP_R2; cmd.cmdarg = mmc->rca << 16; err = mmc_send_command(mmc, &cmd, NULL); if (err) return err; for (i = 0; i < 4; ++i) { mmc->csd[i] = cmd.response[i]; } if (mmc->version == MMC_VERSION_UNKNOWN) { version = (cmd.response[0] >> 26) & 0xf; /* mmcPrintf("%s: mmc version %d\r\n", mmc_device_name(mmc), version); */ switch (version) { case 0: mmc->version = MMC_VERSION_1_2; break; case 1: mmc->version = MMC_VERSION_1_4; break; case 2: mmc->version = MMC_VERSION_2_2; break; case 3: mmc->version = MMC_VERSION_3; break; case 4: mmc->version = MMC_VERSION_4; break; case 6: mmc->version = MMC_VERSION_5_1; break; default: mmc->version = MMC_VERSION_1_2; break; } } /* divide frequency by 10, since the mults are 10x bigger */ freq = fbase[(cmd.response[0] & 0x7)]; mult = multipliers[((cmd.response[0] >> 3) & 0xf)]; mmc->legacy_speed = freq * mult; mmc_select_mode(mmc, MMC_LEGACY); mmc->dsr_imp = ((cmd.response[1] >> 12) & 0x1); mmc->read_bl_len = 1 << ((cmd.response[1] >> 16) & 0xf); mmc->write_bl_len = 1 << ((cmd.response[3] >> 22) & 0xf); if ((mmc->ocr & OCR_HCS) == OCR_HCS) { /* high capacity */ csize = (mmc->csd[1] & 0x3f) << 16 | (mmc->csd[2] & 0xffff0000) >> 16; cmult = 8; } else { csize = (mmc->csd[1] & 0x3ff) << 2 | (mmc->csd[2] & 0xc0000000) >> 30; cmult = (mmc->csd[2] & 0x00038000) >> 15; } mmc->capacity_user = (csize + 1) << (cmult + 2); mmc->capacity_user *= mmc->read_bl_len; mmc->capacity_boot = 0; mmc->capacity_rpmb = 0; for (i = 0; i < 4; i++) mmc->capacity_gp[i] = 0; if (mmc->read_bl_len > MMC_BLKSIZE) mmc->read_bl_len = MMC_BLKSIZE; if (mmc->write_bl_len > MMC_BLKSIZE) mmc->write_bl_len = MMC_BLKSIZE; if (mmc->dsr_imp && (0xffffffff != mmc->dsr)) { /* device output driver */ cmd.cmdidx = 4; /* CMD4: set dsr */ cmd.cmdarg = ((mmc->dsr & 0xffff) << 16) | 0xffff; cmd.resp_type = MMC_RSP_NONE; if (mmc_send_command(mmc, &cmd, NULL)) mmcPrintf("%s: SET_DSR failed\r\n", mmc_device_name(mmc)); else { mmcPrintf("%s: dsr_imp enable, dsr(0x%04x)\r\n", mmc_device_name(mmc), mmc->dsr); } } /* CMD7: Select the card, and put it into Transfer Mode */ cmd.cmdidx = 7; cmd.resp_type = MMC_RSP_R1; cmd.cmdarg = mmc->rca << 16; err = mmc_send_command(mmc, &cmd, NULL); if (err) return -3; mmc->erase_grp_size = 1; mmc->part_config = MMCPART_NOAVAILABLE; mmc_startup_v4(mmc); mmcSetCapacity(mmc, 0); mmc_get_capabilities(mmc); err = mmc_select_mode_and_width(mmc, mmc->card_caps); if (err) { return -4; } /* switch to trans state */ mmc_start_xfer(mmc); return 0; } static int do_init(struct mmc_device *mmc) { int count = 100; if (goto_idle(mmc)) { return -1; } mmcDelayUs(10000); if (mmc->quirks & MMC_QUIRK_RETRY_IDLE) { goto_idle(mmc); mmcDelayUs(10000); } do { if (send_op_cond(mmc)) { goto skip; } if (mmc->ocr & OCR_NOT_BUSY) { break; } skip: mmcDelayUs(50000); } while (count-- > 0); if (!(mmc->ocr & OCR_NOT_BUSY)) { /* timeout */ return -3; } mmc->rca = 1; if (startup(mmc)) return -4; mmcPrintf("%s: capacity 0x%llx\r\n", mmc_device_name(mmc), mmc->capacity); return 0; } /* }}} */ /* {{{: user level api */ int mmcBlocksXfer(size_t off , size_t blocks, void *buffer, int (*rwio)(struct mmc_device *, size_t, size_t, void *)) { struct mmc_device *mmc; int err; size_t blk_save, nblks, lba = 0, lba_end; uint8_t *bp = buffer; if (mmcLock() != 0) { return -1; } blk_save = blocks; for (mmc = __mmc_dev_list; (mmc != NULL) && (blocks > 0); mmc = mmc->next) { lba_end = lba + mmc->capacity / MMC_BLKSIZE; if ((off >= lba) && (off < lba_end)) { nblks = lba_end - off; if (nblks > blocks) nblks = blocks; if ((err = rwio(mmc, off - lba, nblks, bp)) != 0) { mmcPrintf("%s: read off %d blks %d failed\r\n", mmc_device_name(mmc), off, nblks); mmcUnlock(); return err; } off += nblks; blocks -= nblks; bp += nblks * MMC_BLKSIZE; } lba = lba_end; } mmcUnlock(); return (blk_save - blocks); } int mmcDeviceStart(struct mmc_device *mmc) { int nret; mmc->dsr_imp = 0; mmc->dsr = 0xffffffff; nret = mmc_hwinit(mmc); if (nret > 0) { /* already initialized */ return 0; } if (nret < 0) { mmc->capacity = 0; mmcPrintf("mmc(%s): hwinit failed: %d\r\n", mmc_device_name(mmc), nret); return -1; } goto_idle(mmc); mmcDelayUs(100000); nret = do_init(mmc); if (nret) { mmcPrintf("mmc(%s): init failed %d\r\n", mmc_device_name(mmc), nret); mmc->capacity = 0; return -2; } return 0; } void mmcDeviceRemoveAll() { struct mmc_device *mmc; uint32_t caps_filtered; if (mmcLock() != 0) { return; } for (mmc = __mmc_dev_list; mmc != NULL; mmc = mmc->next) { caps_filtered = mmc->card_caps & ~(MMC_CAP(MMC_HS_200) | MMC_CAP(MMC_HS_400)); mmc_select_mode_and_width(mmc, caps_filtered); if (mmc->driver && mmc->driver->free) mmc->driver->free(mmc); } __mmc_dev_list = NULL; __mmc_dev_tail = &__mmc_dev_list; mmcUnlock(); } /* }}} */