diff --git a/src/sdhci.c b/src/sdhci.c index 42af1fe713..059bc030bd 100644 --- a/src/sdhci.c +++ b/src/sdhci.c @@ -1699,6 +1699,10 @@ static int sdhci_transfer(int dir, uint32_t cmd_index, uint32_t block_addr, if (reg & buf_ready_flag) { uint32_t i, xfer_sz = (sz > SDHCI_BLOCK_SIZE) ? SDHCI_BLOCK_SIZE : sz; + /* BWR is W1C: ack before refill or lose the next assertion. */ + if (dir == SDHCI_DIR_WRITE) { + SDHCI_REG_SET(SDHCI_SRS12, SDHCI_SRS12_BWR); + } for (i = 0; i < xfer_sz; i += 4) { if (dir == SDHCI_DIR_READ) { #ifdef SDHCI_PIO_WRITE_NONCACHED_ALIAS @@ -1745,8 +1749,7 @@ static int sdhci_transfer(int dir, uint32_t cmd_index, uint32_t block_addr, * (the CM4 microSD path, which drives the same EMMC2 block). * For multi-block READ: clear BRR by writing 1 to it (W1C), * then the outer loop waits for BRR to be set again when the - * next block's data is available. - * For WRITE: BWR auto-clears when buffer full, don't touch. */ + * next block's data is available. */ if (sz > 0 && dir == SDHCI_DIR_READ) { SDHCI_REG_SET(SDHCI_SRS12, SDHCI_SRS12_BRR); } diff --git a/tools/unit-tests/unit-sdhci-disk-unaligned.c b/tools/unit-tests/unit-sdhci-disk-unaligned.c index d7dd4b377c..c48af4ecab 100644 --- a/tools/unit-tests/unit-sdhci-disk-unaligned.c +++ b/tools/unit-tests/unit-sdhci-disk-unaligned.c @@ -77,8 +77,25 @@ void sdhci_reg_write(uint32_t offset, uint32_t val) if (offset == SDHCI_SRS08 && xfer.active && !xfer.is_read) { uint32_t pos = xfer.block_addr * SDHCI_BLOCK_SIZE + (xfer.word_index * 4); + /* Beginning a block while BWR is still asserted means the driver + * refilled without acknowledging it. Real hardware overruns and + * stalls there; fail the transfer so the test reports it. */ + if ((xfer.word_index % xfer.word_count) == 0 && + (mock_regs[SDHCI_SRS12 / sizeof(uint32_t)] & SDHCI_SRS12_BWR)) { + mock_regs[SDHCI_SRS12 / sizeof(uint32_t)] |= SDHCI_SRS12_EINT; + } memcpy(&mock_disk[pos], &val, sizeof(val)); xfer.word_index++; + /* BWR is raised once per block, not latched for the whole transfer: + * re-assert it when the block just taken has drained and another is + * still expected. */ + if ((xfer.word_index % xfer.word_count) == 0) { + uint32_t blocks = mock_regs[SDHCI_SRS01 / sizeof(uint32_t)] >> + SDHCI_SRS01_BCCT_SHIFT; + if ((xfer.word_index / xfer.word_count) < blocks) { + mock_regs[SDHCI_SRS12 / sizeof(uint32_t)] |= SDHCI_SRS12_BWR; + } + } return; }