Skip site navigation (1)Skip section navigation (2)
Date:      Mon, 12 Jan 2015 02:42:34 +0000 (UTC)
From:      Ian Lepore <ian@FreeBSD.org>
To:        src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-head@freebsd.org
Subject:   svn commit: r277038 - head/sys/arm/broadcom/bcm2835
Message-ID:  <201501120242.t0C2gYcp006294@svn.freebsd.org>

next in thread | raw e-mail | index | archive | help
Author: ian
Date: Mon Jan 12 02:42:33 2015
New Revision: 277038
URL: https://svnweb.freebsd.org/changeset/base/277038

Log:
  Handle dma mappings with more than one segment for rpi sdhci.
  
  The driver inherently does dma in 512 byte chunks, but it's possible that
  such a buffer can span two physically discontiguous pages (such as when
  a userland program does IO on the raw /dev/mmcsdN devices).  Now the driver
  can handle a buffer that's split across two pages.
  
  It could in theory handle any number of segments now, but as long as IO is
  being done in 512 byte blocks it will never need more than two.

Modified:
  head/sys/arm/broadcom/bcm2835/bcm2835_sdhci.c

Modified: head/sys/arm/broadcom/bcm2835/bcm2835_sdhci.c
==============================================================================
--- head/sys/arm/broadcom/bcm2835/bcm2835_sdhci.c	Mon Jan 12 02:29:23 2015	(r277037)
+++ head/sys/arm/broadcom/bcm2835/bcm2835_sdhci.c	Mon Jan 12 02:42:33 2015	(r277038)
@@ -73,6 +73,7 @@ __FBSDID("$FreeBSD$");
 #define	BCM2835_DEFAULT_SDHCI_FREQ	50
 
 #define	BCM_SDHCI_BUFFER_SIZE		512
+#define	NUM_DMA_SEGS			2
 
 #ifdef DEBUG
 #define dprintf(fmt, args...) do { printf("%s(): ", __func__);   \
@@ -97,10 +98,6 @@ TUNABLE_INT("hw.bcm2835.sdhci.min_freq",
 TUNABLE_INT("hw.bcm2835.sdhci.hs", &bcm2835_sdhci_hs);
 TUNABLE_INT("hw.bcm2835.sdhci.pio_mode", &bcm2835_sdhci_pio_mode);
 
-struct bcm_sdhci_dmamap_arg {
-	bus_addr_t		sc_dma_busaddr;
-};
-
 struct bcm_sdhci_softc {
 	device_t		sc_dev;
 	struct mtx		sc_mtx;
@@ -125,9 +122,10 @@ struct bcm_sdhci_softc {
 	bus_dmamap_t		sc_dma_map;
 	vm_paddr_t		sc_sdhci_buffer_phys;
 	uint32_t		cmd_and_mode;
-	bus_addr_t		dmamap_seg_addrs[1];
-	bus_size_t		dmamap_seg_sizes[1];
+	bus_addr_t		dmamap_seg_addrs[NUM_DMA_SEGS];
+	bus_size_t		dmamap_seg_sizes[NUM_DMA_SEGS];
 	int			dmamap_seg_count;
+	int			dmamap_seg_index;
 	int			dmamap_status;
 };
 
@@ -255,7 +253,7 @@ bcm_sdhci_attach(device_t dev)
 	err = bus_dma_tag_create(bus_get_dma_tag(dev),
 	    1, 0, BUS_SPACE_MAXADDR_32BIT,
 	    BUS_SPACE_MAXADDR, NULL, NULL,
-	    BCM_SDHCI_BUFFER_SIZE, 1, BCM_SDHCI_BUFFER_SIZE,
+	    BCM_SDHCI_BUFFER_SIZE, NUM_DMA_SEGS, BCM_SDHCI_BUFFER_SIZE,
 	    BUS_DMA_ALLOCNOW, NULL, NULL,
 	    &sc->sc_dma_tag);
 
@@ -428,18 +426,79 @@ bcm_sdhci_min_freq(device_t dev, struct 
 }
 
 static void
+bcm_sdhci_start_dma_seg(struct bcm_sdhci_softc *sc)
+{
+	struct sdhci_slot *slot;
+	vm_paddr_t pdst, psrc;
+	int err, idx, len, sync_op;
+
+	slot = &sc->sc_slot;
+	idx = sc->dmamap_seg_index++;
+	len = sc->dmamap_seg_sizes[idx];
+	slot->offset += len;
+
+	if (slot->curcmd->data->flags & MMC_DATA_READ) {
+		bcm_dma_setup_src(sc->sc_dma_ch, BCM_DMA_DREQ_EMMC,
+		    BCM_DMA_SAME_ADDR, BCM_DMA_32BIT); 
+		bcm_dma_setup_dst(sc->sc_dma_ch, BCM_DMA_DREQ_NONE,
+		    BCM_DMA_INC_ADDR,
+		    (len & 0xf) ? BCM_DMA_32BIT : BCM_DMA_128BIT);
+		psrc = sc->sc_sdhci_buffer_phys;
+		pdst = sc->dmamap_seg_addrs[idx];
+		sync_op = BUS_DMASYNC_PREREAD;
+	} else {
+		bcm_dma_setup_src(sc->sc_dma_ch, BCM_DMA_DREQ_NONE,
+		    BCM_DMA_INC_ADDR,
+		    (len & 0xf) ? BCM_DMA_32BIT : BCM_DMA_128BIT);
+		bcm_dma_setup_dst(sc->sc_dma_ch, BCM_DMA_DREQ_EMMC,
+		    BCM_DMA_SAME_ADDR, BCM_DMA_32BIT);
+		psrc = sc->dmamap_seg_addrs[idx];
+		pdst = sc->sc_sdhci_buffer_phys;
+		sync_op = BUS_DMASYNC_PREWRITE;
+	}
+
+	/*
+	 * When starting a new DMA operation do the busdma sync operation, and
+	 * disable SDCHI data interrrupts because we'll be driven by DMA
+	 * interrupts (or SDHCI error interrupts) until the IO is done.
+	 */
+	if (idx == 0) {
+		bus_dmamap_sync(sc->sc_dma_tag, sc->sc_dma_map, sync_op);
+		slot->intmask &= ~(SDHCI_INT_DATA_AVAIL | 
+		    SDHCI_INT_SPACE_AVAIL | SDHCI_INT_DATA_END);
+		bcm_sdhci_write_4(sc->sc_dev, &sc->sc_slot, SDHCI_SIGNAL_ENABLE,
+		    slot->intmask);
+	}
+
+	/*
+	 * Start the DMA transfer.  Only programming errors (like failing to
+	 * allocate a channel) cause a non-zero return from bcm_dma_start().
+	 */
+	err = bcm_dma_start(sc->sc_dma_ch, psrc, pdst, len);
+	KASSERT((err == 0), ("bcm2835_sdhci: failed DMA start"));
+}
+
+static void
 bcm_sdhci_dma_intr(int ch, void *arg)
 {
 	struct bcm_sdhci_softc *sc = (struct bcm_sdhci_softc *)arg;
 	struct sdhci_slot *slot = &sc->sc_slot;
 	uint32_t reg, mask;
-	vm_paddr_t pdst, psrc;
-	size_t len;
 	int left, sync_op;
 
 	mtx_lock(&slot->mtx);
 
-	len = bcm_dma_length(sc->sc_dma_ch);
+	/*
+	 * If there are more segments for the current dma, start the next one.
+	 * Otherwise unload the dma map and decide what to do next based on the
+	 * status of the sdhci controller and whether there's more data left.
+	 */
+	if (sc->dmamap_seg_index < sc->dmamap_seg_count) {
+		bcm_sdhci_start_dma_seg(sc);
+		mtx_unlock(&slot->mtx);
+		return;
+	}
+
 	if (slot->curcmd->data->flags & MMC_DATA_READ) {
 		sync_op = BUS_DMASYNC_POSTREAD;
 		mask = SDHCI_INT_DATA_AVAIL;
@@ -450,8 +509,8 @@ bcm_sdhci_dma_intr(int ch, void *arg)
 	bus_dmamap_sync(sc->sc_dma_tag, sc->sc_dma_map, sync_op);
 	bus_dmamap_unload(sc->sc_dma_tag, sc->sc_dma_map);
 
-	slot->offset += len;
-	sc->sc_dma_inuse = 0;
+	sc->dmamap_seg_count = 0;
+	sc->dmamap_seg_index = 0;
 
 	left = min(BCM_SDHCI_BUFFER_SIZE,
 	    slot->curcmd->data->len - slot->offset);
@@ -475,7 +534,6 @@ bcm_sdhci_dma_intr(int ch, void *arg)
 	else {
 		/* already available? */
 		if (reg & mask) {
-			sc->sc_dma_inuse = 1;
 
 			/* ACK for DATA_AVAIL or SPACE_AVAIL */
 			bcm_sdhci_write_4(slot->bus, slot,
@@ -489,24 +547,7 @@ bcm_sdhci_dma_intr(int ch, void *arg)
 				slot->curcmd->error = MMC_ERR_NO_MEMORY;
 				sdhci_finish_data(slot);
 			} else {
-				if (slot->curcmd->data->flags & MMC_DATA_READ) {
-					psrc = sc->sc_sdhci_buffer_phys;
-					pdst = sc->dmamap_seg_addrs[0];
-					sync_op = BUS_DMASYNC_PREREAD;
-				} else {
-					psrc = sc->dmamap_seg_addrs[0];
-					pdst = sc->sc_sdhci_buffer_phys;
-					sync_op = BUS_DMASYNC_PREWRITE;
-				}
-				bus_dmamap_sync(sc->sc_dma_tag, sc->sc_dma_map,
-				    sync_op);
-				if (bcm_dma_start(sc->sc_dma_ch, psrc, pdst,
-				    left)) {
-					device_printf(sc->sc_dev, 
-					    "failed DMA start\n");
-					slot->curcmd->error = MMC_ERR_FAILED;
-					sdhci_finish_data(slot);
-				}
+				bcm_sdhci_start_dma_seg(sc);
 			}
 		} else {
 			/* wait for next data by INT */
@@ -528,7 +569,7 @@ bcm_sdhci_read_dma(device_t dev, struct 
 	struct bcm_sdhci_softc *sc = device_get_softc(slot->bus);
 	size_t left;
 
-	if (sc->sc_dma_inuse) {
+	if (sc->dmamap_seg_count != 0) {
 		device_printf(sc->sc_dev, "DMA in use\n");
 		return;
 	}
@@ -547,25 +588,8 @@ bcm_sdhci_read_dma(device_t dev, struct 
 		return;
 	}
 
-	bus_dmamap_sync(sc->sc_dma_tag, sc->sc_dma_map,
-	    BUS_DMASYNC_PREREAD);
-
-	bcm_dma_setup_src(sc->sc_dma_ch, BCM_DMA_DREQ_EMMC,
-	    BCM_DMA_SAME_ADDR, BCM_DMA_32BIT); 
-	bcm_dma_setup_dst(sc->sc_dma_ch, BCM_DMA_DREQ_NONE,
-	    BCM_DMA_INC_ADDR,
-	    (left & 0xf) ? BCM_DMA_32BIT : BCM_DMA_128BIT);
-
-	/* Disable INT */
-	slot->intmask &= ~(SDHCI_INT_DATA_AVAIL | SDHCI_INT_SPACE_AVAIL | SDHCI_INT_DATA_END);
-	bcm_sdhci_write_4(dev, slot, SDHCI_SIGNAL_ENABLE, slot->intmask);
-
-	sc->sc_dma_inuse = 1;
-
 	/* DMA start */
-	if (bcm_dma_start(sc->sc_dma_ch, sc->sc_sdhci_buffer_phys,
-	    sc->dmamap_seg_addrs[0], left) != 0)
-		device_printf(sc->sc_dev, "failed DMA start\n");
+	bcm_sdhci_start_dma_seg(sc);
 }
 
 static void
@@ -574,7 +598,7 @@ bcm_sdhci_write_dma(device_t dev, struct
 	struct bcm_sdhci_softc *sc = device_get_softc(slot->bus);
 	size_t left;
 
-	if (sc->sc_dma_inuse) {
+	if (sc->dmamap_seg_count != 0) {
 		device_printf(sc->sc_dev, "DMA in use\n");
 		return;
 	}
@@ -593,25 +617,8 @@ bcm_sdhci_write_dma(device_t dev, struct
 		return;
 	}
 
-	bcm_dma_setup_src(sc->sc_dma_ch, BCM_DMA_DREQ_NONE,
-	    BCM_DMA_INC_ADDR,
-	    (left & 0xf) ? BCM_DMA_32BIT : BCM_DMA_128BIT);
-	bcm_dma_setup_dst(sc->sc_dma_ch, BCM_DMA_DREQ_EMMC,
-	    BCM_DMA_SAME_ADDR, BCM_DMA_32BIT);
-
-	bus_dmamap_sync(sc->sc_dma_tag, sc->sc_dma_map,
-	    BUS_DMASYNC_PREWRITE);
-
-	/* Disable INT */
-	slot->intmask &= ~(SDHCI_INT_DATA_AVAIL | SDHCI_INT_SPACE_AVAIL | SDHCI_INT_DATA_END);
-	bcm_sdhci_write_4(dev, slot, SDHCI_SIGNAL_ENABLE, slot->intmask);
-
-	sc->sc_dma_inuse = 1;
-
 	/* DMA start */
-	if (bcm_dma_start(sc->sc_dma_ch, sc->dmamap_seg_addrs[0],
-	    sc->sc_sdhci_buffer_phys, left) != 0)
-		device_printf(sc->sc_dev, "failed DMA start\n");
+	bcm_sdhci_start_dma_seg(sc);
 }
 
 static int



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?201501120242.t0C2gYcp006294>