Page Menu
Home
FreeBSD
Search
Configure Global Search
Log In
Files
F167886248
D59125.diff
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Flag For Later
Award Token
Size
18 KB
Referenced Files
None
Subscribers
None
D59125.diff
View Options
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
Details
Attached
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)
Attached To
Mode
D59125: ti_sdhci: offload the MMC data phase to EDMA3
Attached
Detach File
Event Timeline
Log In to Comment