From: Bin Meng <[email protected]> SDHCI version 4 moves the SDMA system address from the legacy 32-bit register at offset 0x00 to the address pair at offsets 0x58 and 0x5c. At present QEMU always uses the legacy register, so guest SDHCI driver with version 4 mode enabled uses an incorrect DMA address.
Select the address register from Host Version 4 Enable and honor 64-bit Addressing when reading and advancing it. Keep the existing offset 0x00 behavior when version 4 mode is disabled. Signed-off-by: Bin Meng <[email protected]> Message-ID: <[email protected]> [PMD: Simplified extracting the previous trivial 2 commits] Reviewed-by: Philippe Mathieu-Daudé <[email protected]> Signed-off-by: Philippe Mathieu-Daudé <[email protected]> --- hw/sd/sdhci-internal.h | 1 + hw/sd/sdhci.c | 32 +++++++++++++++++++++++++++++--- 2 files changed, 30 insertions(+), 3 deletions(-) diff --git a/hw/sd/sdhci-internal.h b/hw/sd/sdhci-internal.h index 4aeed120bf1..2116995dcca 100644 --- a/hw/sd/sdhci-internal.h +++ b/hw/sd/sdhci-internal.h @@ -201,6 +201,7 @@ FIELD(SDHC_HOSTCTL2, UHS_II_ENA, 8, 1); /* since v4 */ FIELD(SDHC_HOSTCTL2, ADMA2_LENGTH, 10, 1); /* since v4 */ FIELD(SDHC_HOSTCTL2, CMD23_ENA, 11, 1); /* since v4 */ FIELD(SDHC_HOSTCTL2, VERSION4, 12, 1); /* since v4 */ +FIELD(SDHC_HOSTCTL2, ADDRESSING_64, 13, 1); /* since v4 */ FIELD(SDHC_HOSTCTL2, ASYNC_INT, 14, 1); FIELD(SDHC_HOSTCTL2, PRESET_ENA, 15, 1); diff --git a/hw/sd/sdhci.c b/hw/sd/sdhci.c index 93e1687dbe5..3fba3970d4f 100644 --- a/hw/sd/sdhci.c +++ b/hw/sd/sdhci.c @@ -598,14 +598,39 @@ static void sdhci_write_dataport(SDHCIState *s, uint32_t value, unsigned size) * Single DMA data transfer */ +static bool sdhci_version4_enabled(SDHCIState *s) +{ + return FIELD_EX32(s->hostctl2, SDHC_HOSTCTL2, VERSION4); +} + +static bool sdhci_64bit_addressing_enabled(SDHCIState *s) +{ + return FIELD_EX32(s->hostctl2, SDHC_HOSTCTL2, ADDRESSING_64); +} + static uint64_t sdhci_sdma_address(SDHCIState *s) { + if (!sdhci_version4_enabled(s)) { return s->sdmasysad; + } + + return sdhci_64bit_addressing_enabled(s) ? + s->admasysaddr : (uint32_t)s->admasysaddr; } static void sdhci_advance_sdma_address(SDHCIState *s, uint32_t bytes) { + if (!sdhci_version4_enabled(s)) { s->sdmasysad += bytes; + } else if (sdhci_64bit_addressing_enabled(s)) { + s->admasysaddr += bytes; + } else { + uint32_t address = s->admasysaddr; + + address += bytes; + s->admasysaddr = (s->admasysaddr & 0xffffffff00000000ULL) | + address; + } } /* Multi block SDMA transfer */ @@ -1380,10 +1405,11 @@ sdhci_write(void *opaque, hwaddr offset, uint64_t val, unsigned size) MASKED_WRITE(s->acmd12errsts, mask, value & UINT16_MAX); if (s->uhs_mode < UHS_I) { /* - * VERSION4 is writable even without UHS-I. Preserve all other - * Host Control 2 bits when UHS-I is not supported. + * Version 4 fields are writable even without UHS-I. Preserve all + * other Host Control 2 bits when UHS-I is not supported. */ - uint16_t independent = R_SDHC_HOSTCTL2_VERSION4_MASK; + uint16_t independent = R_SDHC_HOSTCTL2_VERSION4_MASK | + R_SDHC_HOSTCTL2_ADDRESSING_64_MASK; hostctl2_mask |= ~independent; hostctl2_value &= independent; -- 2.53.0
