Am Mon, Feb 22, 2021 at 08:10:21PM +0100 schrieb Patrick Wildt:
> 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
Anyone want to give this a try? It's basically relevant for all
ARM machines with eMMC ('soldered SD cards'), and they should work
as well as before.
> 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).
> */