From owner-svn-src-all@FreeBSD.ORG Mon Jan 12 02:42:34 2015 Return-Path: Delivered-To: svn-src-all@freebsd.org Received: from mx1.freebsd.org (mx1.freebsd.org [8.8.178.115]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by hub.freebsd.org (Postfix) with ESMTPS id ED3143A1; Mon, 12 Jan 2015 02:42:34 +0000 (UTC) Received: from svn.freebsd.org (svn.freebsd.org [IPv6:2001:1900:2254:2068::e6a:0]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (Client did not present a certificate) by mx1.freebsd.org (Postfix) with ESMTPS id CF83926E; Mon, 12 Jan 2015 02:42:34 +0000 (UTC) Received: from svn.freebsd.org ([127.0.1.70]) by svn.freebsd.org (8.14.9/8.14.9) with ESMTP id t0C2gY3p006295; Mon, 12 Jan 2015 02:42:34 GMT (envelope-from ian@FreeBSD.org) Received: (from ian@localhost) by svn.freebsd.org (8.14.9/8.14.9/Submit) id t0C2gYcp006294; Mon, 12 Jan 2015 02:42:34 GMT (envelope-from ian@FreeBSD.org) Message-Id: <201501120242.t0C2gYcp006294@svn.freebsd.org> X-Authentication-Warning: svn.freebsd.org: ian set sender to ian@FreeBSD.org using -f From: Ian Lepore Date: Mon, 12 Jan 2015 02:42:34 +0000 (UTC) To: src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-head@freebsd.org Subject: svn commit: r277038 - head/sys/arm/broadcom/bcm2835 X-SVN-Group: head MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit X-BeenThere: svn-src-all@freebsd.org X-Mailman-Version: 2.1.18-1 Precedence: list List-Id: "SVN commit messages for the entire src tree \(except for " user" and " projects" \)" List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Mon, 12 Jan 2015 02:42:35 -0000 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