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

Reply via email to