Hi, it seems like some eMMCs are not capable of doing 8-bit operation, even if the controller supports it. I was questioning our drivers first, but it looks like it's the same on Linux. In the case that 8-bit doesn't work, they seem to fall back to lower values to make that HW work.
This diff implements a mechanism that tries 8-bit, if available, then 4-bit and in the end falls back to 1-bit. This makes my HW work, but I would like to have this tested by a broader audience. Apparently there's a "bus test" command, but it isn't implemented on all host controllers. Hence I simply try to read the EXT_CSD to make sure the transfer works. For testing, a print like printf("%s: using %u-bit width\n", DEVNAME(sc), width); could be added at line 928. What could possible regressions be? The width could become smaller then previously. This would reduce the read/write transfer speed. Also it's possible that eMMCs are not recognized/initialized anymore. What could possible improvements be? eMMCs that previously didn't work now work, with at least 1-bit or 4-bit wide transfers. Please note that this only works for eMMCs. SD cards are *not* using this code path. SD cards have a different initialization code path. Please report any changes or non-changes. If nothing changes, that's perfect. Patrick diff --git a/sys/dev/sdmmc/sdmmc_mem.c b/sys/dev/sdmmc/sdmmc_mem.c index 59bcb1b4a11..5856b9bb1b3 100644 --- a/sys/dev/sdmmc/sdmmc_mem.c +++ b/sys/dev/sdmmc/sdmmc_mem.c @@ -56,6 +56,8 @@ int sdmmc_mem_signal_voltage(struct sdmmc_softc *, int); int sdmmc_mem_sd_init(struct sdmmc_softc *, struct sdmmc_function *); int sdmmc_mem_mmc_init(struct sdmmc_softc *, struct sdmmc_function *); +int sdmmc_mem_mmc_select_bus_width(struct sdmmc_softc *, + struct sdmmc_function *, int); int sdmmc_mem_single_read_block(struct sdmmc_function *, int, u_char *, size_t); int sdmmc_mem_read_block_subr(struct sdmmc_function *, bus_dmamap_t, @@ -908,31 +910,20 @@ sdmmc_mem_mmc_init(struct sdmmc_softc *sc, struct sdmmc_function *sf) ext_csd[EXT_CSD_CARD_TYPE]); } - if (ISSET(sc->sc_caps, SMC_CAPS_8BIT_MODE)) { + if (ISSET(sc->sc_caps, SMC_CAPS_8BIT_MODE) && + sdmmc_mem_mmc_select_bus_width(sc, sf, 8) == 0) width = 8; - value = EXT_CSD_BUS_WIDTH_8; - } else if (ISSET(sc->sc_caps, SMC_CAPS_4BIT_MODE)) { + else if (ISSET(sc->sc_caps, SMC_CAPS_4BIT_MODE) && + sdmmc_mem_mmc_select_bus_width(sc, sf, 4) == 0) width = 4; - value = EXT_CSD_BUS_WIDTH_4; - } else { - width = 1; - value = EXT_CSD_BUS_WIDTH_1; - } - - if (width != 1) { - error = sdmmc_mem_mmc_switch(sf, EXT_CSD_CMD_SET_NORMAL, - EXT_CSD_BUS_WIDTH, value); - if (error == 0) - error = sdmmc_chip_bus_width(sc->sct, - sc->sch, width); - else { + else { + error = sdmmc_mem_mmc_select_bus_width(sc, sf, 1); + if (error != 0) { DPRINTF(("%s: can't change bus width" " (%d bit)\n", DEVNAME(sc), width)); return error; } - - /* XXXX: need bus test? (using by CMD14 & CMD19) */ - sdmmc_delay(10000); + width = 1; } if (timing != SDMMC_TIMING_LEGACY) { @@ -1041,6 +1032,59 @@ sdmmc_mem_mmc_init(struct sdmmc_softc *sc, struct sdmmc_function *sf) return error; } +int +sdmmc_mem_mmc_select_bus_width(struct sdmmc_softc *sc, struct sdmmc_function *sf, + int width) +{ + u_int8_t ext_csd[512]; + int error, value; + + switch (width) { + case 8: + value = EXT_CSD_BUS_WIDTH_8; + break; + case 4: + value = EXT_CSD_BUS_WIDTH_4; + break; + case 1: + value = EXT_CSD_BUS_WIDTH_1; + break; + default: + printf("%s: invalid bus width\n", DEVNAME(sc)); + return EINVAL; + } + + error = sdmmc_mem_mmc_switch(sf, EXT_CSD_CMD_SET_NORMAL, + EXT_CSD_BUS_WIDTH, value); + if (error != 0) { + DPRINTF(("%s: can't change card bus width" + " (%d bit)\n", DEVNAME(sc), width)); + return error; + } + + error = sdmmc_chip_bus_width(sc->sct, + sc->sch, width); + if (error != 0) { + DPRINTF(("%s: can't change host bus width" + " (%d bit)\n", DEVNAME(sc), width)); + return error; + } + + /* XXX: need bus test? (using by CMD14 & CMD19) */ + sdmmc_delay(10000); + + /* XXX: read EXT_CSD again, alternative to bus test */ + error = sdmmc_mem_send_cxd_data(sc, + MMC_SEND_EXT_CSD, ext_csd, sizeof(ext_csd)); + if (error != 0) { + DPRINTF(("%s: can't read EXT_CSD with bus width" + " (%d bit)\n", DEVNAME(sc), width)); + return error; + } + + return error; +} + /* * Get or set the card's memory OCR value (SD or MMC). */