Source-Changes-HG archive
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index][Old Index]
[src/trunk]: src/sys/arch/arm/omap AM335x EDMA3 has an alignment restriction ...
details: https://anonhg.NetBSD.org/src/rev/0a8664b0311f
branches: trunk
changeset: 811498:0a8664b0311f
user: jmcneill <jmcneill%NetBSD.org@localhost>
date: Sun Nov 01 23:33:05 2015 +0000
description:
AM335x EDMA3 has an alignment restriction for both SAM and DAM in constant
addressing mode. In these cases, the physical address must be aligned to
256-bits. To handle this, pre-allocate a MAXPHYS sized bounce buffer and
use it for unaligned transfers.
Fixes "edma errint!" problem mentioned in port-arm/50288.
diffstat:
sys/arch/arm/omap/omap3_sdhc.c | 126 ++++++++++++++++++++++++++++++++++++----
1 files changed, 112 insertions(+), 14 deletions(-)
diffs (218 lines):
diff -r 222de70e458c -r 0a8664b0311f sys/arch/arm/omap/omap3_sdhc.c
--- a/sys/arch/arm/omap/omap3_sdhc.c Sun Nov 01 22:41:24 2015 +0000
+++ b/sys/arch/arm/omap/omap3_sdhc.c Sun Nov 01 23:33:05 2015 +0000
@@ -1,4 +1,4 @@
-/* $NetBSD: omap3_sdhc.c,v 1.17 2015/07/31 15:00:07 jmcneill Exp $ */
+/* $NetBSD: omap3_sdhc.c,v 1.18 2015/11/01 23:33:05 jmcneill Exp $ */
/*-
* Copyright (c) 2011 The NetBSD Foundation, Inc.
* All rights reserved.
@@ -29,7 +29,7 @@
*/
#include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: omap3_sdhc.c,v 1.17 2015/07/31 15:00:07 jmcneill Exp $");
+__KERNEL_RCSID(0, "$NetBSD: omap3_sdhc.c,v 1.18 2015/11/01 23:33:05 jmcneill Exp $");
#include "opt_omap.h"
#include "edma.h"
@@ -105,13 +105,17 @@
kcondvar_t sc_edma_cv;
bus_addr_t sc_edma_fifo;
bool sc_edma_pending;
+ bus_dmamap_t sc_edma_dmamap;
+ bus_dma_segment_t sc_edma_segs[1];
+ void *sc_edma_bbuf;
#endif
};
#if NEDMA > 0
-static void obiosdhc_edma_init(struct obiosdhc_softc *, unsigned int);
+static int obiosdhc_edma_init(struct obiosdhc_softc *, unsigned int);
static int obiosdhc_edma_xfer_data(struct sdhc_softc *, struct sdmmc_command *);
static void obiosdhc_edma_done(void *);
+static int obiosdhc_edma_transfer(struct sdhc_softc *, struct sdmmc_command *);
#endif
#ifdef TI_AM335X
@@ -229,10 +233,12 @@
#if NEDMA > 0
if (oa->obio_edmabase != -1) {
+ if (obiosdhc_edma_init(sc, oa->obio_edmabase) != 0)
+ goto no_dma;
+
cv_init(&sc->sc_edma_cv, "sdhcedma");
sc->sc_edma_fifo = oa->obio_addr +
OMAP3_SDMMC_SDHC_OFFSET + SDHC_DATA;
- obiosdhc_edma_init(sc, oa->obio_edmabase);
sc->sc.sc_flags |= SDHC_FLAG_USE_DMA;
sc->sc.sc_flags |= SDHC_FLAG_EXTERNAL_DMA;
sc->sc.sc_flags |= SDHC_FLAG_EXTDMA_DMAEN;
@@ -240,6 +246,7 @@
sc->sc.sc_vendor_transfer_data_dma = obiosdhc_edma_xfer_data;
transfer_mode = "EDMA";
}
+no_dma:
#endif
aprint_naive("\n");
@@ -432,10 +439,10 @@
}
#if NEDMA > 0
-static void
+static int
obiosdhc_edma_init(struct obiosdhc_softc *sc, unsigned int edmabase)
{
- int i;
+ int i, error, rseg;
/* Request tx and rx DMA channels */
sc->sc_edma_tx = edma_channel_alloc(EDMA_TYPE_DMA, edmabase + 0,
@@ -459,19 +466,99 @@
KASSERT(sc->sc_edma_param_rx[i] != 0xffff);
}
- return;
+ /* Setup bounce buffer */
+ error = bus_dmamem_alloc(sc->sc.sc_dmat, MAXPHYS, 32, MAXPHYS,
+ sc->sc_edma_segs, 1, &rseg, BUS_DMA_WAITOK);
+ if (error) {
+ aprint_error_dev(sc->sc.sc_dev,
+ "couldn't allocate dmamem: %d\n", error);
+ return error;
+ }
+ KASSERT(rseg == 1);
+ error = bus_dmamem_map(sc->sc.sc_dmat, sc->sc_edma_segs, rseg, MAXPHYS,
+ &sc->sc_edma_bbuf, BUS_DMA_WAITOK);
+ if (error) {
+ aprint_error_dev(sc->sc.sc_dev, "couldn't map dmamem: %d\n",
+ error);
+ return error;
+ }
+ error = bus_dmamap_create(sc->sc.sc_dmat, MAXPHYS, 1, MAXPHYS, 0,
+ BUS_DMA_WAITOK, &sc->sc_edma_dmamap);
+ if (error) {
+ aprint_error_dev(sc->sc.sc_dev, "couldn't create dmamap: %d\n",
+ error);
+ return error;
+ }
+
+ return error;
}
static int
obiosdhc_edma_xfer_data(struct sdhc_softc *sdhc_sc, struct sdmmc_command *cmd)
{
struct obiosdhc_softc *sc = device_private(sdhc_sc->sc_dev);
+ const bus_dmamap_t map = cmd->c_dmamap;
+ int seg, error;
+ bool bounce;
+
+ for (bounce = false, seg = 0; seg < cmd->c_dmamap->dm_nsegs; seg++) {
+ if ((cmd->c_dmamap->dm_segs[seg].ds_addr & 0x1f) != 0) {
+ bounce = true;
+ break;
+ }
+ }
+
+ if (bounce) {
+ error = bus_dmamap_load(sc->sc.sc_dmat, sc->sc_edma_dmamap,
+ sc->sc_edma_bbuf, MAXPHYS, NULL, BUS_DMA_WAITOK);
+ if (error) {
+ device_printf(sc->sc.sc_dev,
+ "[bounce] bus_dmamap_load failed: %d\n", error);
+ return error;
+ }
+ if (ISSET(cmd->c_flags, SCF_CMD_READ)) {
+ bus_dmamap_sync(sc->sc.sc_dmat, sc->sc_edma_dmamap, 0,
+ MAXPHYS, BUS_DMASYNC_PREREAD);
+ } else {
+ memcpy(sc->sc_edma_bbuf, cmd->c_data, cmd->c_datalen);
+ bus_dmamap_sync(sc->sc.sc_dmat, sc->sc_edma_dmamap, 0,
+ MAXPHYS, BUS_DMASYNC_PREWRITE);
+ }
+
+ cmd->c_dmamap = sc->sc_edma_dmamap;
+ }
+
+ error = obiosdhc_edma_transfer(sdhc_sc, cmd);
+
+ if (bounce) {
+ if (ISSET(cmd->c_flags, SCF_CMD_READ)) {
+ bus_dmamap_sync(sc->sc.sc_dmat, sc->sc_edma_dmamap, 0,
+ MAXPHYS, BUS_DMASYNC_POSTREAD);
+ } else {
+ bus_dmamap_sync(sc->sc.sc_dmat, sc->sc_edma_dmamap, 0,
+ MAXPHYS, BUS_DMASYNC_POSTWRITE);
+ }
+ bus_dmamap_unload(sc->sc.sc_dmat, sc->sc_edma_dmamap);
+ if (ISSET(cmd->c_flags, SCF_CMD_READ) && error == 0) {
+ memcpy(cmd->c_data, sc->sc_edma_bbuf, cmd->c_datalen);
+ }
+
+ cmd->c_dmamap = map;
+ }
+
+ return error;
+}
+
+static int
+obiosdhc_edma_transfer(struct sdhc_softc *sdhc_sc, struct sdmmc_command *cmd)
+{
+ struct obiosdhc_softc *sc = device_private(sdhc_sc->sc_dev);
kmutex_t *plock = sdhc_host_lock(sc->sc_hosts[0]);
struct edma_channel *edma;
uint16_t *edma_param;
struct edma_param ep;
size_t seg;
- int error;
+ int error, resid = cmd->c_datalen;
int blksize = MIN(cmd->c_datalen, cmd->c_blklen);
KASSERT(mutex_owned(plock));
@@ -489,6 +576,13 @@
}
for (seg = 0; seg < cmd->c_dmamap->dm_nsegs; seg++) {
+ KASSERT(resid > 0);
+ const int xferlen = min(resid,
+ cmd->c_dmamap->dm_segs[seg].ds_len);
+ KASSERT(xferlen == cmd->c_dmamap->dm_segs[seg].ds_len ||
+ seg == cmd->c_dmamap->dm_nsegs - 1);
+ resid -= xferlen;
+ KASSERT((xferlen & 0x3) == 0);
ep.ep_opt = __SHIFTIN(2, EDMA_PARAM_OPT_FWID) /* 32-bit */;
ep.ep_opt |= __SHIFTIN(edma_channel_index(edma),
EDMA_PARAM_OPT_TCC);
@@ -508,7 +602,13 @@
ep.ep_dst = sc->sc_edma_fifo;
}
- KASSERT(cmd->c_dmamap->dm_segs[seg].ds_len <= 65536 * 4);
+ KASSERT(xferlen <= 65536 * 4);
+
+ /*
+ * In constant addressing mode, the address must be aligned
+ * to 256-bits.
+ */
+ KASSERT((cmd->c_dmamap->dm_segs[seg].ds_addr & 0x1f) == 0);
/*
* For unknown reason, the A-DMA transfers never completes for
@@ -517,11 +617,9 @@
*/
ep.ep_bcntrld = 0; /* not used for AB-synchronous mode */
ep.ep_opt |= EDMA_PARAM_OPT_SYNCDIM;
- ep.ep_acnt = min(cmd->c_dmamap->dm_segs[seg].ds_len, 64);
- ep.ep_bcnt = min(cmd->c_dmamap->dm_segs[seg].ds_len, blksize) /
- ep.ep_acnt;
- ep.ep_ccnt = cmd->c_dmamap->dm_segs[seg].ds_len /
- (ep.ep_acnt * ep.ep_bcnt);
+ ep.ep_acnt = min(xferlen, 64);
+ ep.ep_bcnt = min(xferlen, blksize) / ep.ep_acnt;
+ ep.ep_ccnt = xferlen / (ep.ep_acnt * ep.ep_bcnt);
ep.ep_srcbidx = ep.ep_dstbidx = 0;
ep.ep_srccidx = ep.ep_dstcidx = 0;
if (ISSET(cmd->c_flags, SCF_CMD_READ)) {
Home |
Main Index |
Thread Index |
Old Index