[PATCH v2 04/11] mci: sdhci: honor BROKEN_ADMA_ZEROLEN_DESC / NO_ENDATTR_IN_NOPDESC quirks
Johannes Schneider <[email protected]> Sat, 4 Jul 2026 12:26:21 +0000
| Newsgroups | org.infradead.lists.barebox |
|---|---|
| Message-ID | <[email protected]> |
The ADMA2 table builder always emitted a 64 KiB chunk as a length-0 descriptor (the 16-bit length field encodes 65536 as 0) and terminated the table with a separate NOP-END descriptor. Some controllers mishandle these forms; Linux gates the workarounds behind SDHCI_QUIRK_BROKEN_ADMA_ZEROLEN_DESC (cap the length below 64 KiB so it is never 0) and SDHCI_QUIRK_NO_ENDATTR_IN_NOPDESC (mark END on the last transfer descriptor instead of a trailing NOP-END). Add both quirks to the shared SDHCI layer and honor them in the table builder. Controllers that set neither keep the previous behaviour exactly, so this is a no-op for everyone until a host driver opts in. Assisted-by: Claude Opus 4.8 (1M context) <[email protected]> Assisted-by: GitHub Copilot CLI (gpt-5.5) Signed-off-by: Johannes Schneider <[email protected]> --- Notes: v2: - No functional change; rebased on next. drivers/mci/sdhci.c | 35 ++++++++++++++++++++++++----------- drivers/mci/sdhci.h | 4 ++++ 2 files changed, 28 insertions(+), 11 deletions(-) diff --git a/drivers/mci/sdhci.c b/drivers/mci/sdhci.c index ccd5d4b83c..8ed8a59342 100644 --- a/drivers/mci/sdhci.c +++ b/drivers/mci/sdhci.c @@ -646,31 +646,44 @@ static int sdhci_adma_write_desc(struct sdhci *host, void **desc, static int sdhci_adma_build_table(struct sdhci *host, dma_addr_t addr, unsigned int len) { + unsigned int max_len = SDHCI_ADMA2_MAX_LEN; void *desc = host->adma_table; int ret; + /* + * Some controllers (e.g. the i.MX uSDHC) cannot handle a length-0 + * descriptor, which the 16-bit length field uses to encode the full + * SDHCI_ADMA2_MAX_LEN. Cap the chunk just below that so the length is + * never 0. + */ + if (host->quirks & SDHCI_QUIRK_BROKEN_ADMA_ZEROLEN_DESC) + max_len = SDHCI_ADMA2_MAX_LEN - 4; + while (len) { - unsigned int chunk = min_t(unsigned int, len, - SDHCI_ADMA2_MAX_LEN); + unsigned int chunk = min_t(unsigned int, len, max_len); + unsigned int attr = ADMA2_TRAN_VALID; + len -= chunk; /* - * The length field is 16-bit; a length of 0 encodes - * SDHCI_ADMA2_MAX_LEN bytes per the SD Host Controller - * specification. + * Controllers that ignore the END attribute in a trailing NOP + * descriptor want END on the last transfer descriptor instead + * (SDHCI_QUIRK_NO_ENDATTR_IN_NOPDESC). */ - ret = sdhci_adma_write_desc(host, &desc, addr, chunk & 0xffff, - ADMA2_TRAN_VALID); + if (!len && (host->quirks & SDHCI_QUIRK_NO_ENDATTR_IN_NOPDESC)) + attr |= ADMA2_END; + + ret = sdhci_adma_write_desc(host, &desc, addr, chunk & 0xffff, attr); if (ret) return ret; addr += chunk; - len -= chunk; } - /* Append a terminating descriptor (nop, end, valid). */ - ret = sdhci_adma_write_desc(host, &desc, 0, 0, ADMA2_NOP_END_VALID); + if (host->quirks & SDHCI_QUIRK_NO_ENDATTR_IN_NOPDESC) + return 0; - return ret; + /* Append a terminating descriptor (nop, end, valid). */ + return sdhci_adma_write_desc(host, &desc, 0, 0, ADMA2_NOP_END_VALID); } void sdhci_setup_data_dma(struct sdhci *sdhci, struct mci_data *data, diff --git a/drivers/mci/sdhci.h b/drivers/mci/sdhci.h index a3b8e65b55..2a7f9be575 100644 --- a/drivers/mci/sdhci.h +++ b/drivers/mci/sdhci.h @@ -341,6 +341,10 @@ struct sdhci { bool v4_mode; /* Host Version 4 Enable */ unsigned int quirks; +/* Controller ignores the END attribute in a NOP ADMA2 descriptor */ +#define SDHCI_QUIRK_NO_ENDATTR_IN_NOPDESC BIT(6) +/* Controller cannot handle a length-0 (== 64 KiB) ADMA2 descriptor */ +#define SDHCI_QUIRK_BROKEN_ADMA_ZEROLEN_DESC BIT(11) #define SDHCI_QUIRK_MISSING_CAPS BIT(27) unsigned int quirks2; /* The system physically doesn't support 1.8v, even if the host does */ -- 2.43.0