Version 4 SDMA selects its system address from the registers also named as the ADMA System Address registers. Updating those registers does not resume a transfer that stopped at the configured SDMA buffer boundary.
Resume 32-bit addressing after an access completes the lower 32 bits of the ADMA System Address register. For 64-bit addressing, wait until an access completes the upper 32 bits so software can update the low half before the high half without starting from a mixed address. Signed-off-by: Bin Meng <[email protected]> --- hw/sd/sdhci.c | 32 ++++++++++++++++++++++++++------ 1 file changed, 26 insertions(+), 6 deletions(-) diff --git a/hw/sd/sdhci.c b/hw/sd/sdhci.c index 00a4b9b0e4..8d85e1551c 100644 --- a/hw/sd/sdhci.c +++ b/hw/sd/sdhci.c @@ -786,6 +786,12 @@ static bool sdhci_sdma_transfer_active(SDHCIState *s) SDHC_DMA_TYPE(s->hostctl1) == SDHC_CTRL_SDMA; } +static void sdhci_resume_sdma_transfer(SDHCIState *s) +{ + s->sdma_boundary_paused = false; + sdhci_sdma_transfer(s); +} + typedef struct ADMADescr { hwaddr addr; uint16_t length; @@ -1257,14 +1263,15 @@ sdhci_write(void *opaque, hwaddr offset, uint64_t val, unsigned size) switch (offset & ~0x3) { case SDHC_SYSAD: - if (!TRANSFERRING_DATA(s->prnsts) || s->sdma_boundary_paused) { + if (!TRANSFERRING_DATA(s->prnsts) || + (!sdhci_version4_enabled(s) && s->sdma_boundary_paused)) { s->sdmasysad = (s->sdmasysad & mask) | value; MASKED_WRITE(s->sdmasysad, mask, value); - /* Writing to last byte of sdmasysad might trigger transfer */ - if (!(mask & 0xFF000000) && s->blkcnt && - (s->blksize & BLOCK_SIZE_MASK) && - SDHC_DMA_TYPE(s->hostctl1) == SDHC_CTRL_SDMA) { - sdhci_sdma_transfer(s); + /* A completed selected-address write resumes stopped SDMA */ + if (!sdhci_version4_enabled(s) && !(mask & 0xff000000) && + s->sdma_boundary_paused && + sdhci_sdma_transfer_active(s)) { + sdhci_resume_sdma_transfer(s); } } break; @@ -1401,10 +1408,23 @@ sdhci_write(void *opaque, hwaddr offset, uint64_t val, unsigned size) case SDHC_ADMASYSADDR: s->admasysaddr = (s->admasysaddr & (0xFFFFFFFF00000000ULL | (uint64_t)mask)) | (uint64_t)value; + if (sdhci_version4_enabled(s) && + !sdhci_64bit_addressing_enabled(s) && + !(mask & 0xff000000) && s->sdma_boundary_paused && + sdhci_sdma_transfer_active(s)) { + sdhci_resume_sdma_transfer(s); + } break; case SDHC_ADMASYSADDR + 4: s->admasysaddr = (s->admasysaddr & (0x00000000FFFFFFFFULL | ((uint64_t)mask << 32))) | ((uint64_t)value << 32); + /* A completed selected-address write resumes boundary-stopped SDMA */ + if (sdhci_version4_enabled(s) && + sdhci_64bit_addressing_enabled(s) && + !(mask & 0xff000000) && s->sdma_boundary_paused && + sdhci_sdma_transfer_active(s)) { + sdhci_resume_sdma_transfer(s); + } break; case SDHC_FEAER: s->acmd12errsts |= value; -- 2.34.1
