Page MenuHomeFreeBSD

D59125.diff
No OneTemporary

D59125.diff

diff --git a/sys/arm/ti/ti_sdhci.c b/sys/arm/ti/ti_sdhci.c
--- a/sys/arm/ti/ti_sdhci.c
+++ b/sys/arm/ti/ti_sdhci.c
@@ -49,6 +49,7 @@
#include <dev/clk/clk.h>
#include <dev/ofw/ofw_bus.h>
#include <dev/ofw/ofw_bus_subr.h>
+#include <dev/ofw/ofw_subr.h>
#include <dev/mmc/bridge.h>
#include <dev/mmc/mmcreg.h>
@@ -61,9 +62,13 @@
#include <machine/bus.h>
#include <machine/resource.h>
#include <machine/intr.h>
+#include <arm/ti/ti_edma3.h>
#include "opt_mmccam.h"
+#define TI_SDHCI_DMA_MAXSEGS 40
+#define TI_SDHCI_DMA_PARAM_BASE 64
+
struct ti_sdhci_softc {
device_t dev;
struct sdhci_fdt_gpio * gpio;
@@ -79,8 +84,54 @@
boolean_t disable_highspeed;
boolean_t force_card_present;
boolean_t disable_readonly;
+
+ /* EDMA3 offload of the data phase; see ti_sdhci_dma_setup(). */
+ bus_dma_tag_t dma_tag;
+ bus_dmamap_t dma_map;
+ void *dma_buf;
+ bus_addr_t dma_physaddr;
+ uint32_t dma_fifo_paddr;
+ int dma_mode;
+ unsigned int dma_ch;
+ unsigned int dma_ch_rx;
+ unsigned int dma_ch_tx;
+ size_t dma_len;
+ bool dma_armed;
+ bool dma_write;
+ uint64_t dma_xfers;
+ uint64_t dma_fallbacks;
+ uint64_t dma_timeouts;
+ uint64_t dma_missed;
+ uint64_t dma_aborts;
+ /* Direct mapping of the caller's buffer, one PaRAM set per segment. */
+ bus_dma_tag_t dma_xtag;
+ bus_dmamap_t dma_xmap;
+ bus_dma_segment_t dma_segs[TI_SDHCI_DMA_MAXSEGS];
+ int dma_nsegs;
+ int dma_direct;
+ unsigned int dma_param_base;
+ bool dma_loaded;
+ uint64_t dma_directs;
+ uint64_t dma_bounces;
+ uint64_t dma_fb_off;
+ uint64_t dma_fb_align;
+ uint64_t dma_fb_load;
+ uint64_t dma_fb_seg;
+ int dma_maxsegs;
+ struct intr_config_hook dma_hook;
};
+static bool ti_sdhci_dma_arm(struct ti_sdhci_softc *sc);
+static void ti_sdhci_dma_setup(struct ti_sdhci_softc *sc);
+static void ti_sdhci_dma_setup_hook(void *arg);
+static void ti_sdhci_dma_load(void *arg, bus_dma_segment_t *segs,
+ int nsegs, int error);
+static void ti_sdhci_dma_param(struct ti_sdhci_softc *sc,
+ struct ti_edma3cc_param_set *ps, unsigned int ch, bus_addr_t mem,
+ size_t blksz, size_t nblk, bool write, bool last, uint16_t link);
+
+static bool ti_sdhci_edma3_inited;
+
/*
* Table of supported FDT compat strings.
*
@@ -124,6 +175,19 @@
#define MMCHS_SD_CAPA_VS30 (1 << 25)
#define MMCHS_SD_CAPA_VS33 (1 << 24)
+/*
+ * Size of the bounce buffer, and the cap on a data phase either way:
+ * anything larger is left to PIO. The direct path does not use the
+ * buffer but shares the cap.
+ */
+#define TI_SDHCI_DMA_BUFSZ (128 * 1024)
+#define TI_SDHCI_DMA_TIMEOUT 10000 /* microseconds */
+
+/* Values for the dma_mode tunable. */
+#define TI_SDHCI_DMA_OFF 0
+#define TI_SDHCI_DMA_READ 1
+#define TI_SDHCI_DMA_READ_WRITE 2
+
/* Forward declarations, CAM-relataed */
// static void ti_sdhci_cam_poll(struct cam_sim *);
// static void ti_sdhci_cam_action(struct cam_sim *, union ccb *);
@@ -310,6 +374,8 @@
} else if (off == SDHCI_COMMAND_FLAGS) {
sc->cmd_and_mode = (sc->cmd_and_mode & 0x0000ffff) |
((uint32_t)val << 16);
+ if (ti_sdhci_dma_arm(sc))
+ sc->cmd_and_mode |= SDHCI_TRNS_DMA;
WR4(sc, SDHCI_TRANSFER_MODE, sc->cmd_and_mode);
return;
}
@@ -637,7 +703,16 @@
sc->slot.quirks |= SDHCI_QUIRK_WAIT_WHILE_BUSY;
/*
- * DMA is not really broken, I just haven't implemented it yet.
+ * SD_CAPA advertises SDMA, but the OCP master interface is not
+ * implemented here: SD_CON[20] DMA_MnS is documented as unavailable
+ * on this device (AM335x TRM SPRUH73Q 18.5.1.5). sdhci(4) drives DMA
+ * by making the controller master the bus, which this controller
+ * cannot do, so the quirk stays set.
+ *
+ * The data phase is offloaded instead through the platform transfer
+ * hooks, which sdhci(4) reaches from its PIO path: EDMA3 drives the
+ * controller's SDTXEVT/SDRXEVT slave request lines while the
+ * controller remains a slave. See ti_sdhci_will_handle_transfer().
*/
sc->slot.quirks |= SDHCI_QUIRK_BROKEN_DMA;
@@ -679,6 +754,11 @@
if (OF_hasprop(node, "non-removable"))
sc->force_card_present = true;
+ sc->dma_hook.ich_func = ti_sdhci_dma_setup_hook;
+ sc->dma_hook.ich_arg = sc;
+ if (config_intrhook_establish(&sc->dma_hook) != 0)
+ device_printf(dev, "cannot establish DMA hook\n");
+
bus_identify_children(dev);
bus_attach_children(dev);
@@ -711,6 +791,455 @@
return (ENXIO);
}
+static void
+ti_sdhci_dma_getaddr(void *arg, bus_dma_segment_t *segs, int nsegs, int error)
+{
+ if (error != 0 || nsegs != 1)
+ return;
+ *(bus_addr_t *)arg = segs[0].ds_addr;
+}
+
+/*
+ * Arm EDMA3 for the data phase of the command about to be issued. The
+ * controller raises one request per block. SD_CMD[0] DE has to be set by the
+ * write that issues the command, so this runs from the SDHCI_COMMAND_FLAGS
+ * case of ti_sdhci_write_2().
+ */
+static bool
+ti_sdhci_dma_arm(struct ti_sdhci_softc *sc)
+{
+ struct ti_edma3cc_param_set ps;
+ struct sdhci_slot *slot = &sc->slot;
+ struct mmc_data *data;
+ uint32_t blkreg;
+ unsigned int ch;
+ size_t blksz, len, nblk;
+ bool direct, write;
+ unsigned int set;
+ uint16_t link;
+ int i;
+
+ if (sc->dma_mode == TI_SDHCI_DMA_OFF || sc->dma_buf == NULL)
+ return (false);
+ if (slot->curcmd == NULL)
+ return (false);
+ data = slot->curcmd->data;
+ if (data == NULL || data->data == NULL)
+ return (false);
+
+ if (sc->dma_armed) {
+ /* The previous data phase was never finished; take it back. */
+ ti_edma3_clear_completion(sc->dma_ch);
+ ti_edma3_clear_event_missed(sc->dma_ch);
+ sc->dma_armed = false;
+ sc->dma_aborts++;
+ }
+
+ write = (data->flags & MMC_DATA_WRITE) != 0;
+ if (write && sc->dma_mode < TI_SDHCI_DMA_READ_WRITE)
+ return (false);
+
+ /* Use what sdhci programmed; a count of zero means one block. */
+ blkreg = RD4(sc, SDHCI_BLOCK_SIZE);
+ blksz = blkreg & 0xfff;
+ nblk = (blkreg >> 16) & 0xffff;
+ if (nblk == 0)
+ nblk = 1;
+ len = blksz * nblk;
+ if (blksz == 0 || (blksz % 4) != 0 || len > TI_SDHCI_DMA_BUFSZ ||
+ len != data->len) {
+ sc->dma_fallbacks++;
+ return (false);
+ }
+
+ ch = write ? sc->dma_ch_tx : sc->dma_ch_rx;
+
+ ti_edma3_clear_completion(ch);
+ ti_edma3_clear_event_missed(ch);
+
+ /*
+ * Decide whether the caller's buffer can go to EDMA3 as it stands.
+ * If it can, each physically contiguous run becomes one PaRAM set,
+ * linked into a chain the channel controller walks unaided.
+ * Otherwise the data phase goes through the bounce buffer.
+ *
+ * Two conditions have to hold. The buffer must start on a 64 byte
+ * cache line, because bus_dmamap_sync() maintains whole lines and a
+ * shared leading line would be invalidated along with it. And every
+ * segment must be a whole number of blocks: the controller raises one
+ * request per block, and a PaRAM set is not reloaded until its events
+ * are exhausted, so a block cannot span two sets.
+ */
+ direct = false;
+ if (sc->dma_direct == 0)
+ sc->dma_fb_off++;
+ else if (((uintptr_t)data->data & 0x3f) != 0)
+ sc->dma_fb_align++;
+ else if (bus_dmamap_load(sc->dma_xtag, sc->dma_xmap, data->data, len,
+ ti_sdhci_dma_load, sc, BUS_DMA_NOWAIT) != 0)
+ sc->dma_fb_load++;
+ else {
+ direct = sc->dma_nsegs > 0;
+ for (i = 0; i < sc->dma_nsegs; i++) {
+ if ((sc->dma_segs[i].ds_len % blksz) != 0)
+ direct = false;
+ }
+ if (sc->dma_nsegs > sc->dma_maxsegs)
+ sc->dma_maxsegs = sc->dma_nsegs;
+ if (!direct) {
+ sc->dma_fb_seg++;
+ bus_dmamap_unload(sc->dma_xtag, sc->dma_xmap);
+ }
+ }
+
+ if (direct) {
+ for (i = 0; i < sc->dma_nsegs; i++) {
+ set = (i == 0) ? ch : sc->dma_param_base + i - 1;
+ link = (i + 1 < sc->dma_nsegs) ?
+ ti_edma3_param_link(sc->dma_param_base + i) :
+ 0xffff;
+ ti_sdhci_dma_param(sc, &ps, ch,
+ sc->dma_segs[i].ds_addr, blksz,
+ sc->dma_segs[i].ds_len / blksz, write,
+ i + 1 == sc->dma_nsegs, link);
+ ti_edma3_param_write(set, &ps);
+ }
+ bus_dmamap_sync(sc->dma_xtag, sc->dma_xmap,
+ write ? BUS_DMASYNC_PREWRITE : BUS_DMASYNC_PREREAD);
+ sc->dma_loaded = true;
+ sc->dma_directs++;
+ } else {
+ if (write)
+ memcpy(sc->dma_buf, data->data, len);
+ ti_sdhci_dma_param(sc, &ps, ch, sc->dma_physaddr, blksz, nblk,
+ write, true, 0xffff);
+ ti_edma3_param_write(ch, &ps);
+ bus_dmamap_sync(sc->dma_tag, sc->dma_map,
+ write ? BUS_DMASYNC_PREWRITE : BUS_DMASYNC_PREREAD);
+ sc->dma_bounces++;
+ }
+
+ if (ti_edma3_enable_transfer_event(ch) != 0) {
+ sc->dma_fallbacks++;
+ return (false);
+ }
+
+ sc->dma_ch = ch;
+ sc->dma_len = len;
+ sc->dma_write = write;
+ sc->dma_armed = true;
+
+ /*
+ * With DE set the controller reports only command and transfer
+ * complete, so claim the data phase; no buffer-ready interrupt
+ * will arrive.
+ */
+ slot->flags |= PLATFORM_DATA_STARTED;
+
+ return (true);
+}
+
+static int
+ti_sdhci_will_handle_transfer(device_t dev, struct sdhci_slot *slot)
+{
+ struct ti_sdhci_softc *sc = device_get_softc(dev);
+
+ /* Claim it only if EDMA3 has the data, otherwise sdhci runs PIO. */
+ return (sc->dma_armed ? 1 : 0);
+}
+
+static void
+ti_sdhci_start_transfer(device_t dev, struct sdhci_slot *slot,
+ uint32_t *intmask)
+{
+ /* EDMA3 was started when the command was issued; nothing to do. */
+}
+
+static void
+ti_sdhci_finish_transfer(device_t dev, struct sdhci_slot *slot)
+{
+ struct ti_sdhci_softc *sc = device_get_softc(dev);
+ struct mmc_data *data;
+ int i;
+
+ if (!sc->dma_armed) {
+ sdhci_finish_data(slot);
+ return;
+ }
+
+ /*
+ * The card can report transfer complete before EDMA3 has written the
+ * last block's tail, so wait for the channel. This runs in the sdhci
+ * interrupt handler and cannot sleep, so the wait is a bounded spin of
+ * 10 ms, after which the command is failed rather than hung.
+ *
+ * In practice it does not spin. Instrumented over 3612 data phases,
+ * completion was already posted on the first check every time: EDMA3
+ * drains the buffer as blocks arrive and finishes before the card
+ * reports transfer complete. The loop covers the case where that
+ * ordering does not hold, not the common path.
+ */
+ for (i = 0; i < TI_SDHCI_DMA_TIMEOUT; i++) {
+ if (ti_edma3_transfer_complete(sc->dma_ch))
+ break;
+ DELAY(1);
+ }
+ if (i == TI_SDHCI_DMA_TIMEOUT) {
+ sc->dma_timeouts++;
+ slot->curcmd->error = MMC_ERR_TIMEOUT;
+ } else if (ti_edma3_event_missed(sc->dma_ch)) {
+ sc->dma_missed++;
+ slot->curcmd->error = MMC_ERR_FAILED;
+ }
+ ti_edma3_clear_completion(sc->dma_ch);
+ ti_edma3_clear_event_missed(sc->dma_ch);
+
+ data = slot->curcmd->data;
+ if (sc->dma_loaded) {
+ bus_dmamap_sync(sc->dma_xtag, sc->dma_xmap,
+ sc->dma_write ? BUS_DMASYNC_POSTWRITE :
+ BUS_DMASYNC_POSTREAD);
+ bus_dmamap_unload(sc->dma_xtag, sc->dma_xmap);
+ sc->dma_loaded = false;
+ } else if (sc->dma_write)
+ bus_dmamap_sync(sc->dma_tag, sc->dma_map,
+ BUS_DMASYNC_POSTWRITE);
+ else {
+ bus_dmamap_sync(sc->dma_tag, sc->dma_map, BUS_DMASYNC_POSTREAD);
+ if (slot->curcmd->error == 0 && data != NULL)
+ memcpy(data->data, sc->dma_buf, sc->dma_len);
+ }
+
+ sc->dma_armed = false;
+ sc->dma_xfers++;
+ sdhci_finish_data(slot);
+}
+
+/*
+ * The request lines are fixed per instance and the device tree has no dma
+ * bindings for these nodes, so identify the instance by register base.
+ */
+static void
+ti_sdhci_dma_setup(struct ti_sdhci_softc *sc)
+{
+ device_t dev = sc->dev;
+ bus_addr_t base;
+ bus_size_t bsize;
+ struct sysctl_ctx_list *ctx;
+ struct sysctl_oid_list *tree;
+ int err;
+
+ if (!ti_edma3_available()) {
+ device_printf(dev,
+ "EDMA3 unavailable; data transfers stay in PIO\n");
+ return;
+ }
+
+ if (ofw_reg_to_paddr(ofw_bus_get_node(dev), 0, &base,
+ &bsize, NULL) != 0) {
+ device_printf(dev,
+ "cannot translate registers to a physical address\n");
+ return;
+ }
+
+ switch (base) {
+ case 0x48060000:
+ sc->dma_ch_tx = TI_EDMA3_EVENT_SDTXEVT0;
+ sc->dma_ch_rx = TI_EDMA3_EVENT_SDRXEVT0;
+ break;
+ case 0x481d8000:
+ sc->dma_ch_tx = TI_EDMA3_EVENT_SDTXEVT1;
+ sc->dma_ch_rx = TI_EDMA3_EVENT_SDRXEVT1;
+ break;
+ default:
+ device_printf(dev,
+ "no EDMA3 request lines for base %#jx\n",
+ (uintmax_t)base);
+ return;
+ }
+
+ sc->dma_fifo_paddr = base + sc->sdhci_reg_off + SDHCI_BUFFER;
+
+ err = bus_dma_tag_create(bus_get_dma_tag(dev), 4, 0,
+ BUS_SPACE_MAXADDR_32BIT, BUS_SPACE_MAXADDR, NULL, NULL,
+ TI_SDHCI_DMA_BUFSZ, 1, TI_SDHCI_DMA_BUFSZ, 0, NULL, NULL,
+ &sc->dma_tag);
+ if (err != 0) {
+ device_printf(dev, "cannot create DMA tag: %d\n", err);
+ return;
+ }
+ /*
+ * Not BUS_DMA_COHERENT: that maps the buffer uncacheable, which costs a
+ * separate DRAM access for every byte read back out of it.
+ */
+ err = bus_dmamem_alloc(sc->dma_tag, &sc->dma_buf,
+ BUS_DMA_NOWAIT, &sc->dma_map);
+ if (err != 0) {
+ device_printf(dev, "cannot allocate DMA buffer: %d\n", err);
+ sc->dma_buf = NULL;
+ return;
+ }
+ err = bus_dmamap_load(sc->dma_tag, sc->dma_map, sc->dma_buf,
+ TI_SDHCI_DMA_BUFSZ, ti_sdhci_dma_getaddr, &sc->dma_physaddr,
+ BUS_DMA_NOWAIT);
+ if (err != 0 || sc->dma_physaddr == 0) {
+ device_printf(dev, "cannot load DMA buffer: %d\n", err);
+ sc->dma_buf = NULL;
+ return;
+ }
+
+
+ /*
+ * A second tag and map for the scattered caller buffer, and a
+ * private run of PaRAM sets above the 64 that belong to the
+ * channels.
+ */
+ err = bus_dma_tag_create(bus_get_dma_tag(dev), 4, 0,
+ BUS_SPACE_MAXADDR_32BIT, BUS_SPACE_MAXADDR, NULL, NULL,
+ TI_SDHCI_DMA_BUFSZ, TI_SDHCI_DMA_MAXSEGS, TI_SDHCI_DMA_BUFSZ, 0,
+ NULL, NULL, &sc->dma_xtag);
+ if (err == 0)
+ err = bus_dmamap_create(sc->dma_xtag, 0, &sc->dma_xmap);
+ if (err != 0) {
+ device_printf(dev,
+ "no direct DMA mapping, bouncing instead: %d\n", err);
+ sc->dma_xtag = NULL;
+ } else {
+ sc->dma_param_base = TI_SDHCI_DMA_PARAM_BASE +
+ device_get_unit(dev) * TI_SDHCI_DMA_MAXSEGS;
+ sc->dma_direct = 1;
+ }
+
+ if (!ti_sdhci_edma3_inited) {
+ ti_edma3_init(0);
+ ti_sdhci_edma3_inited = true;
+ }
+ if (ti_edma3_request_dma_ch(sc->dma_ch_rx, sc->dma_ch_rx, 0) != 0 ||
+ ti_edma3_request_dma_ch(sc->dma_ch_tx, sc->dma_ch_tx, 0) != 0) {
+ device_printf(dev, "cannot request EDMA3 channels\n");
+ sc->dma_buf = NULL;
+ return;
+ }
+
+ /* dma_mode selects none, reads, or reads and writes. */
+ sc->dma_mode = TI_SDHCI_DMA_READ_WRITE;
+ ctx = device_get_sysctl_ctx(dev);
+ tree = SYSCTL_CHILDREN(device_get_sysctl_tree(dev));
+ SYSCTL_ADD_INT(ctx, tree, OID_AUTO, "dma_mode", CTLFLAG_RW,
+ &sc->dma_mode, 0,
+ "EDMA3 data transfers: 0 none, 1 reads, 2 reads and writes");
+ SYSCTL_ADD_U64(ctx, tree, OID_AUTO, "dma_xfers", CTLFLAG_RD,
+ &sc->dma_xfers, 0, "data phases moved by EDMA3");
+ SYSCTL_ADD_U64(ctx, tree, OID_AUTO, "dma_fallbacks", CTLFLAG_RD,
+ &sc->dma_fallbacks, 0, "data phases left to PIO");
+ SYSCTL_ADD_U64(ctx, tree, OID_AUTO, "dma_timeouts", CTLFLAG_RD,
+ &sc->dma_timeouts, 0, "EDMA3 transfers that did not complete");
+ SYSCTL_ADD_U64(ctx, tree, OID_AUTO, "dma_missed", CTLFLAG_RD,
+ &sc->dma_missed, 0, "EDMA3 events missed by the channel");
+ SYSCTL_ADD_U64(ctx, tree, OID_AUTO, "dma_aborts", CTLFLAG_RD,
+ &sc->dma_aborts, 0, "armed transfers dropped by a reset");
+ SYSCTL_ADD_INT(ctx, tree, OID_AUTO, "dma_direct", CTLFLAG_RW,
+ &sc->dma_direct, 0,
+ "map the caller's buffer instead of copying through one buffer");
+ SYSCTL_ADD_U64(ctx, tree, OID_AUTO, "dma_directs", CTLFLAG_RD,
+ &sc->dma_directs, 0, "data phases mapped in place");
+ SYSCTL_ADD_U64(ctx, tree, OID_AUTO, "dma_bounces", CTLFLAG_RD,
+ &sc->dma_bounces, 0, "data phases copied through the buffer");
+ SYSCTL_ADD_U64(ctx, tree, OID_AUTO, "dma_fb_align", CTLFLAG_RD,
+ &sc->dma_fb_align, 0, "fallbacks: buffer not cache line aligned");
+ SYSCTL_ADD_U64(ctx, tree, OID_AUTO, "dma_fb_load", CTLFLAG_RD,
+ &sc->dma_fb_load, 0, "fallbacks: could not map the buffer");
+ SYSCTL_ADD_U64(ctx, tree, OID_AUTO, "dma_fb_seg", CTLFLAG_RD,
+ &sc->dma_fb_seg, 0, "fallbacks: segment was not whole blocks");
+ SYSCTL_ADD_U64(ctx, tree, OID_AUTO, "dma_fb_off", CTLFLAG_RD,
+ &sc->dma_fb_off, 0, "fallbacks: direct mapping turned off");
+ SYSCTL_ADD_INT(ctx, tree, OID_AUTO, "dma_maxsegs", CTLFLAG_RD,
+ &sc->dma_maxsegs, 0, "most segments seen in one transfer");
+
+ sc->slot.opt |= SDHCI_PLATFORM_TRANSFER;
+ device_printf(dev, "EDMA3 channels %u/%u for data transfers\n",
+ sc->dma_ch_rx, sc->dma_ch_tx);
+}
+
+
+/* ti_edma3 attaches after this driver, so claim the channels from a hook. */
+static void
+ti_sdhci_dma_setup_hook(void *arg)
+{
+ struct ti_sdhci_softc *sc = arg;
+
+ config_intrhook_disestablish(&sc->dma_hook);
+ ti_sdhci_dma_setup(sc);
+}
+
+
+/*
+ * sdhci resets the slot from its timeout callout without finishing the data
+ * phase, so release an armed transfer here or the channel stays claimed.
+ */
+static void
+ti_sdhci_reset(device_t dev, struct sdhci_slot *slot, uint8_t mask)
+{
+ struct ti_sdhci_softc *sc = device_get_softc(dev);
+
+ if ((mask & (SDHCI_RESET_DATA | SDHCI_RESET_ALL)) != 0 &&
+ sc->dma_armed) {
+ ti_edma3_clear_completion(sc->dma_ch);
+ ti_edma3_clear_event_missed(sc->dma_ch);
+ sc->dma_armed = false;
+ if (sc->dma_loaded) {
+ bus_dmamap_unload(sc->dma_xtag, sc->dma_xmap);
+ sc->dma_loaded = false;
+ }
+ slot->flags &= ~PLATFORM_DATA_STARTED;
+ sc->dma_aborts++;
+ }
+
+ sdhci_generic_reset(dev, slot, mask);
+}
+
+static void
+ti_sdhci_dma_load(void *arg, bus_dma_segment_t *segs, int nsegs, int error)
+{
+ struct ti_sdhci_softc *sc = arg;
+
+ sc->dma_nsegs = 0;
+ if (error != 0 || nsegs < 1 || nsegs > TI_SDHCI_DMA_MAXSEGS)
+ return;
+ memcpy(sc->dma_segs, segs, nsegs * sizeof(*segs));
+ sc->dma_nsegs = nsegs;
+}
+
+/*
+ * One PaRAM set covering nblk blocks of a contiguous run. Only the last set
+ * in a chain raises the completion interrupt.
+ */
+static void
+ti_sdhci_dma_param(struct ti_sdhci_softc *sc, struct ti_edma3cc_param_set *ps,
+ unsigned int ch, bus_addr_t mem, size_t blksz, size_t nblk, bool write,
+ bool last, uint16_t link)
+{
+ memset(ps, 0, sizeof(*ps));
+ ps->opt.tcc = ch;
+ ps->opt.tcinten = last ? 1 : 0;
+ ps->opt.syncdim = 1; /* AB-sync: one event, one block. */
+ ps->acnt = 4;
+ ps->bcnt = blksz / 4;
+ ps->ccnt = nblk;
+ ps->link = link;
+ if (write) {
+ ps->src = mem;
+ ps->dst = sc->dma_fifo_paddr;
+ ps->srcbidx = 4;
+ ps->srccidx = blksz;
+ } else {
+ ps->src = sc->dma_fifo_paddr;
+ ps->dst = mem;
+ ps->dstbidx = 4;
+ ps->dstcidx = blksz;
+ }
+}
+
static device_method_t ti_sdhci_methods[] = {
/* Device interface */
DEVMETHOD(device_probe, ti_sdhci_probe),
@@ -739,6 +1268,12 @@
DEVMETHOD(sdhci_write_multi_4, ti_sdhci_write_multi_4),
DEVMETHOD(sdhci_get_card_present, ti_sdhci_get_card_present),
+ /* Data phase offloaded to EDMA3. */
+ DEVMETHOD(sdhci_reset, ti_sdhci_reset),
+ DEVMETHOD(sdhci_platform_will_handle, ti_sdhci_will_handle_transfer),
+ DEVMETHOD(sdhci_platform_start_transfer, ti_sdhci_start_transfer),
+ DEVMETHOD(sdhci_platform_finish_transfer, ti_sdhci_finish_transfer),
+
DEVMETHOD_END
};

File Metadata

Mime Type
text/plain
Expires
Wed, Aug 26, 6:22 AM (10 h, 54 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
37185583
Default Alt Text
D59125.diff (18 KB)

Event Timeline