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).
*/