git: 8727c174b0fe - main - dwmmc: Multiple busdma fixes.
Michal Meloun
mmel at FreeBSD.org
Wed Feb 3 20:32:37 UTC 2021
The branch main has been updated by mmel:
URL: https://cgit.FreeBSD.org/src/commit/?id=8727c174b0fe44766bb7ea765dac6d5f82818103
commit 8727c174b0fe44766bb7ea765dac6d5f82818103
Author: Michal Meloun <mmel at FreeBSD.org>
AuthorDate: 2021-01-21 14:06:19 +0000
Commit: Michal Meloun <mmel at FreeBSD.org>
CommitDate: 2021-02-03 20:15:11 +0000
dwmmc: Multiple busdma fixes.
- limit maximum segment size to 2048 bytes. Although dwmmc supports a buffer
fragment with a maximum length of 4095 bytes, use the nearest lower power
of two as the maximum fragment size. Otherwise, busdma create excessive
buffer fragments.
- fix off by one error in computation of the maximum data transfer length.
- in addition, reserve two DMA descriptors that can be used by busdma
bouncing. The beginning or end of the buffer can be misaligned.
- Don’t ignore errors passed to bus_dmamap_load() callback function.
- In theory, a DMA engine may be running at time when next dma descriptor is
constructed. Create a full DMA descriptor before OWN bit is set.
MFC after: 2 weeks
---
sys/dev/mmc/host/dwmmc.c | 47 ++++++++++++++++++++++++++++++++---------------
1 file changed, 32 insertions(+), 15 deletions(-)
diff --git a/sys/dev/mmc/host/dwmmc.c b/sys/dev/mmc/host/dwmmc.c
index 11521257ee0a..b31bb0d4e68b 100644
--- a/sys/dev/mmc/host/dwmmc.c
+++ b/sys/dev/mmc/host/dwmmc.c
@@ -107,8 +107,7 @@ __FBSDID("$FreeBSD$");
#define CARD_INIT_DONE 0x04
#define DWMMC_DATA_ERR_FLAGS (SDMMC_INTMASK_DRT | SDMMC_INTMASK_DCRC \
- |SDMMC_INTMASK_HTO | SDMMC_INTMASK_SBE \
- |SDMMC_INTMASK_EBE)
+ |SDMMC_INTMASK_SBE | SDMMC_INTMASK_EBE)
#define DWMMC_CMD_ERR_FLAGS (SDMMC_INTMASK_RTO | SDMMC_INTMASK_RCRC \
|SDMMC_INTMASK_RE)
#define DWMMC_ERR_FLAGS (DWMMC_DATA_ERR_FLAGS | DWMMC_CMD_ERR_FLAGS \
@@ -134,7 +133,16 @@ struct idmac_desc {
#define IDMAC_DESC_SEGS (PAGE_SIZE / (sizeof(struct idmac_desc)))
#define IDMAC_DESC_SIZE (sizeof(struct idmac_desc) * IDMAC_DESC_SEGS)
#define DEF_MSIZE 0x2 /* Burst size of multiple transaction */
-#define IDMAC_MAX_SIZE 4096
+/*
+ * Size field in DMA descriptor is 13 bits long (up to 4095 bytes),
+ * but must be a multiple of the data bus size.Additionally, we must ensure
+ * that bus_dmamap_load() doesn't additionally fragments buffer (because it
+ * is processed with page size granularity). Thus limit fragment size to half
+ * of page.
+ * XXX switch descriptor format to array and use second buffer pointer for
+ * second half of page
+ */
+#define IDMAC_MAX_SIZE 2048
static void dwmmc_next_operation(struct dwmmc_softc *);
static int dwmmc_setup_bus(struct dwmmc_softc *, int);
@@ -165,8 +173,11 @@ static void
dwmmc_get1paddr(void *arg, bus_dma_segment_t *segs, int nsegs, int error)
{
+ if (nsegs != 1)
+ panic("%s: nsegs != 1 (%d)\n", __func__, nsegs);
if (error != 0)
- return;
+ panic("%s: error != 0 (%d)\n", __func__, error);
+
*(bus_addr_t *)arg = segs[0].ds_addr;
}
@@ -176,15 +187,13 @@ dwmmc_ring_setup(void *arg, bus_dma_segment_t *segs, int nsegs, int error)
struct dwmmc_softc *sc;
int idx;
- if (error != 0)
- return;
-
sc = arg;
-
dprintf("nsegs %d seg0len %lu\n", nsegs, segs[0].ds_len);
+ if (error != 0)
+ panic("%s: error != 0 (%d)\n", __func__, error);
for (idx = 0; idx < nsegs; idx++) {
- sc->desc_ring[idx].des0 = (DES0_OWN | DES0_DIC | DES0_CH);
+ sc->desc_ring[idx].des0 = DES0_DIC | DES0_CH;
sc->desc_ring[idx].des1 = segs[idx].ds_len & DES1_BS1_MASK;
sc->desc_ring[idx].des2 = segs[idx].ds_addr;
@@ -195,6 +204,8 @@ dwmmc_ring_setup(void *arg, bus_dma_segment_t *segs, int nsegs, int error)
sc->desc_ring[idx].des0 &= ~(DES0_DIC | DES0_CH);
sc->desc_ring[idx].des0 |= DES0_LD;
}
+ wmb();
+ sc->desc_ring[idx].des0 |= DES0_OWN;
}
}
@@ -277,7 +288,7 @@ dma_setup(struct dwmmc_softc *sc)
error = bus_dma_tag_create(
bus_get_dma_tag(sc->dev), /* Parent tag. */
- CACHE_LINE_SIZE, 0, /* alignment, boundary */
+ 8, 0, /* alignment, boundary */
BUS_SPACE_MAXADDR_32BIT, /* lowaddr */
BUS_SPACE_MAXADDR, /* highaddr */
NULL, NULL, /* filter, filterarg */
@@ -786,7 +797,7 @@ dwmmc_attach(device_t dev)
fail:
mtx_unlock(&sc->sim_mtx);
#endif
- /*
+ /*
* Schedule a card detection as we won't get an interrupt
* if the card is inserted when we attach
*/
@@ -900,8 +911,8 @@ dwmmc_update_ios(device_t brdev, device_t reqdev)
sc = device_get_softc(brdev);
ios = &sc->host.ios;
- dprintf("Setting up clk %u bus_width %d\n",
- ios->clock, ios->bus_width);
+ dprintf("Setting up clk %u bus_width %d, timming: %d\n",
+ ios->clock, ios->bus_width, ios->timing);
if (ios->bus_width == bus_width_8)
WRITE4(sc, SDMMC_CTYPE, SDMMC_CTYPE_8BIT);
@@ -985,7 +996,7 @@ dma_prepare(struct dwmmc_softc *sc, struct mmc_command *cmd)
reg = READ4(sc, SDMMC_INTMASK);
reg &= ~(SDMMC_INTMASK_TXDR | SDMMC_INTMASK_RXDR);
WRITE4(sc, SDMMC_INTMASK, reg);
-
+ dprintf("%s: bus_dmamap_load size: %zu\n", __func__, data->len);
err = bus_dmamap_load(sc->buf_tag, sc->buf_map,
data->data, data->len, dwmmc_ring_setup,
sc, BUS_DMA_NOWAIT);
@@ -1358,7 +1369,13 @@ dwmmc_read_ivar(device_t bus, device_t child, int which, uintptr_t *result)
*(int *)result = sc->host.caps;
break;
case MMCBR_IVAR_MAX_DATA:
- *(int *)result = (IDMAC_MAX_SIZE * IDMAC_DESC_SEGS) / MMC_SECTOR_SIZE;
+ /*
+ * Busdma may bounce buffers, so we must reserve 2 descriptors
+ * (on start and on end) for bounced fragments.
+ *
+ */
+ *(int *)result = (IDMAC_MAX_SIZE * IDMAC_DESC_SEGS) /
+ MMC_SECTOR_SIZE - 3;
break;
case MMCBR_IVAR_TIMING:
*(int *)result = sc->host.ios.timing;
More information about the dev-commits-src-all
mailing list