Currently, ADMA will temporarily store data into a local bounce buffer
when transferring it. This will produce unneeded copies of the data and
limit us to the bounce buffer size for each step.
This patch now maps the requested DMA address and passes this buffer
directly to sdbus_{read,write}_data. This allows to pass much larger
buffers down to increase the performance. sdbus_{read,write}_data is
already able to handle arbitrary length and alignments, so we do not
need to ensure this.
Signed-off-by: Christian Speich <c.speich@avm.de>
---
hw/sd/sdhci.c | 102 +++++++++++++++++++++++++++++++---------------------------
1 file changed, 55 insertions(+), 47 deletions(-)
diff --git a/hw/sd/sdhci.c b/hw/sd/sdhci.c
index 3c897e54b721075a3ebd215e027fb73a65ff39b2..94ba23a8da990e69fd59c039e4fdd25b98929dfd 100644
--- a/hw/sd/sdhci.c
+++ b/hw/sd/sdhci.c
@@ -774,7 +774,7 @@ static void get_adma_description(SDHCIState *s, ADMADescr *dscr)
static void sdhci_do_adma(SDHCIState *s)
{
- unsigned int begin, length;
+ unsigned int length;
const uint16_t block_size = s->blksize & BLOCK_SIZE_MASK;
const MemTxAttrs attrs = { .memory = true };
ADMADescr dscr = {};
@@ -816,66 +816,74 @@ static void sdhci_do_adma(SDHCIState *s)
if (s->trnmod & SDHC_TRNS_READ) {
s->prnsts |= SDHC_DOING_READ;
while (length) {
- if (s->data_count == 0) {
- sdbus_read_data(&s->sdbus, s->fifo_buffer, block_size);
- }
- begin = s->data_count;
- if ((length + begin) < block_size) {
- s->data_count = length + begin;
- length = 0;
- } else {
- s->data_count = block_size;
- length -= block_size - begin;
- }
- res = dma_memory_write(s->dma_as, dscr.addr,
- &s->fifo_buffer[begin],
- s->data_count - begin,
- attrs);
- if (res != MEMTX_OK) {
+ dma_addr_t dma_len = length;
+
+ void *buf = dma_memory_map(s->dma_as, dscr.addr, &dma_len,
+ DMA_DIRECTION_FROM_DEVICE,
+ attrs);
+
+ if (buf == NULL) {
+ res = MEMTX_ERROR;
break;
+ } else {
+ res = MEMTX_OK;
}
- dscr.addr += s->data_count - begin;
- if (s->data_count == block_size) {
- s->data_count = 0;
- if (s->trnmod & SDHC_TRNS_BLK_CNT_EN) {
- s->blkcnt--;
- if (s->blkcnt == 0) {
- break;
- }
+
+ sdbus_read_data(&s->sdbus, buf, dma_len);
+ length -= dma_len;
+ dscr.addr += dma_len;
+
+ dma_memory_unmap(s->dma_as, buf, dma_len,
+ DMA_DIRECTION_FROM_DEVICE, dma_len);
+
+ if (s->trnmod & SDHC_TRNS_BLK_CNT_EN) {
+ size_t transfered = s->data_count + dma_len;
+
+ s->blkcnt -= transfered / block_size;
+ s->data_count = transfered % block_size;
+
+ if (s->blkcnt == 0) {
+ s->data_count = 0;
+ break;
}
}
}
} else {
s->prnsts |= SDHC_DOING_WRITE;
while (length) {
- begin = s->data_count;
- if ((length + begin) < block_size) {
- s->data_count = length + begin;
- length = 0;
- } else {
- s->data_count = block_size;
- length -= block_size - begin;
- }
- res = dma_memory_read(s->dma_as, dscr.addr,
- &s->fifo_buffer[begin],
- s->data_count - begin,
- attrs);
- if (res != MEMTX_OK) {
+ dma_addr_t dma_len = length;
+
+ void *buf = dma_memory_map(s->dma_as, dscr.addr, &dma_len,
+ DMA_DIRECTION_TO_DEVICE, attrs);
+
+ if (buf == NULL) {
+ res = MEMTX_ERROR;
break;
+ } else {
+ res = MEMTX_OK;
}
- dscr.addr += s->data_count - begin;
- if (s->data_count == block_size) {
- sdbus_write_data(&s->sdbus, s->fifo_buffer, block_size);
- s->data_count = 0;
- if (s->trnmod & SDHC_TRNS_BLK_CNT_EN) {
- s->blkcnt--;
- if (s->blkcnt == 0) {
- break;
- }
+
+ sdbus_write_data(&s->sdbus, buf, dma_len);
+ length -= dma_len;
+ dscr.addr += dma_len;
+
+ dma_memory_unmap(s->dma_as, buf, dma_len,
+ DMA_DIRECTION_TO_DEVICE, dma_len);
+
+ if (s->trnmod & SDHC_TRNS_BLK_CNT_EN) {
+ size_t transfered = s->data_count + dma_len;
+
+ s->blkcnt -= transfered / block_size;
+ s->data_count = transfered % block_size;
+
+ if (s->blkcnt == 0) {
+ s->data_count = 0;
+ break;
}
}
}
}
+
if (res != MEMTX_OK) {
s->data_count = 0;
if (s->errintstsen & SDHC_EISEN_ADMAERR) {
--
2.43.0
© 2016 - 2025 Red Hat, Inc.