From efe71a8aac2b1a091fdb9a2e3cba3d802e3dd069 Mon Sep 17 00:00:00 2001 From: thorpej Date: Thu, 25 Jul 2002 15:00:48 +0000 Subject: [PATCH] Add support for DMA to/from the on-chip devices of the i80321 (no PCI window translation). XXX This would be better done by overhauling the shared ARM bus_dma code. --- sys/arch/arm/xscale/files.i80321 | 4 +- sys/arch/arm/xscale/i80321.c | 3 +- sys/arch/arm/xscale/i80321_local_dma.c | 347 +++++++++++++++++++++++++ sys/arch/arm/xscale/i80321var.h | 7 +- 4 files changed, 358 insertions(+), 3 deletions(-) create mode 100644 sys/arch/arm/xscale/i80321_local_dma.c diff --git a/sys/arch/arm/xscale/files.i80321 b/sys/arch/arm/xscale/files.i80321 index 2c444518d742..07227605c0a8 100644 --- a/sys/arch/arm/xscale/files.i80321 +++ b/sys/arch/arm/xscale/files.i80321 @@ -1,4 +1,4 @@ -# $NetBSD: files.i80321,v 1.2 2002/04/12 19:02:30 thorpej Exp $ +# $NetBSD: files.i80321,v 1.3 2002/07/25 15:00:48 thorpej Exp $ # # Configuration info for Intel i80321 XScale I/O Processor support # @@ -14,3 +14,5 @@ file arch/arm/xscale/i80321.c iopxs file arch/arm/xscale/i80321_pci.c iopxs file arch/arm/xscale/i80321_pci_dma.c iopxs file arch/arm/xscale/i80321_space.c iopxs + +file arch/arm/xscale/i80321_local_dma.c iopxs diff --git a/sys/arch/arm/xscale/i80321.c b/sys/arch/arm/xscale/i80321.c index 8f044d61c4a5..4c0f48f01ae3 100644 --- a/sys/arch/arm/xscale/i80321.c +++ b/sys/arch/arm/xscale/i80321.c @@ -1,4 +1,4 @@ -/* $NetBSD: i80321.c,v 1.2 2002/05/16 01:01:34 thorpej Exp $ */ +/* $NetBSD: i80321.c,v 1.3 2002/07/25 15:00:48 thorpej Exp $ */ /* * Copyright (c) 2002 Wasabi Systems, Inc. @@ -187,6 +187,7 @@ i80321_attach(struct i80321_softc *sc) i80321_mem_bs_init(&sc->sc_pci_memt, sc); i80321_pci_dma_init(&sc->sc_pci_dmat, sc); i80321_pci_init(&sc->sc_pci_chipset, sc); + i80321_local_dma_init(&sc->sc_local_dmat, sc); /* * Attach the PCI bus. diff --git a/sys/arch/arm/xscale/i80321_local_dma.c b/sys/arch/arm/xscale/i80321_local_dma.c new file mode 100644 index 000000000000..71f4052adfad --- /dev/null +++ b/sys/arch/arm/xscale/i80321_local_dma.c @@ -0,0 +1,347 @@ +/* $NetBSD: i80321_local_dma.c,v 1.1 2002/07/25 15:00:49 thorpej Exp $ */ + +/* + * Copyright (c) 2001, 2002 Wasabi Systems, Inc. + * All rights reserved. + * + * Written by Jason R. Thorpe for Wasabi Systems, Inc. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed for the NetBSD Project by + * Wasabi Systems, Inc. + * 4. The name of Wasabi Systems, Inc. may not be used to endorse + * or promote products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED BY WASABI SYSTEMS, INC. ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL WASABI SYSTEMS, INC + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * Local DMA support for i80321 I/O Processor chip. + */ + +#include +#include +#include +#include +#include + +#include + +#define _ARM32_BUS_DMA_PRIVATE +#include + +#include +#include + +int i80321_local_dmamap_load(bus_dma_tag_t, bus_dmamap_t, void *, + bus_size_t, struct proc *, int); +int i80321_local_dmamap_load_mbuf(bus_dma_tag_t, bus_dmamap_t, + struct mbuf *, int); +int i80321_local_dmamap_load_uio(bus_dma_tag_t, bus_dmamap_t, + struct uio *, int); +int i80321_local_dmamap_load_raw(bus_dma_tag_t, bus_dmamap_t, + bus_dma_segment_t *, int, bus_size_t, int); + +int i80321_local_dmamem_alloc(bus_dma_tag_t, bus_size_t, + bus_size_t, bus_size_t, bus_dma_segment_t *, int, int *, int); + +void +i80321_local_dma_init(bus_dma_tag_t dmat, void *cookie) +{ + + dmat->_ranges = NULL; + dmat->_nranges = 0; + + dmat->_dmamap_create = _bus_dmamap_create; + dmat->_dmamap_destroy = _bus_dmamap_destroy; + dmat->_dmamap_load = i80321_local_dmamap_load; + dmat->_dmamap_load_mbuf = i80321_local_dmamap_load_mbuf; + dmat->_dmamap_load_uio = i80321_local_dmamap_load_uio; + dmat->_dmamap_load_raw = i80321_local_dmamap_load_raw; + dmat->_dmamap_unload = _bus_dmamap_unload; + dmat->_dmamap_sync = _bus_dmamap_sync; + + dmat->_dmamem_alloc = i80321_local_dmamem_alloc; + dmat->_dmamem_free = _bus_dmamem_free; + dmat->_dmamem_map = _bus_dmamem_map; + dmat->_dmamem_unmap = _bus_dmamem_unmap; + dmat->_dmamem_mmap = _bus_dmamem_mmap; +} + +/* + * i80321_local_dmamap_load_buffer: + * + * Utility function to load a linear buffer. lastaddrp holds state + * between invocations (for multiple-buffer loads). segp contains + * the starting segment on entry, and the ending segment on exit. + * first indicates if this is the first invocation of this function. + */ +static int +i80321_local_dmamap_load_buffer(bus_dma_tag_t t, bus_dmamap_t map, + void *buf, bus_size_t buflen, struct proc *p, int flags, + vaddr_t *lastaddrp, int *segp, int first) +{ + bus_size_t sgsize; + bus_addr_t curaddr, lastaddr, baddr, bmask; + vaddr_t vaddr = (vaddr_t) buf; + int seg; + struct pmap *pmap; + + if (p != NULL) + pmap = p->p_vmspace->vm_map.pmap; + else + pmap = pmap_kernel(); + + lastaddr = *lastaddrp; + bmask = ~(map->_dm_boundary - 1); + + for (seg = *segp; buflen > 0; ) { + /* Get the physical address for this segment. */ + (void) pmap_extract(pmap, (vaddr_t) vaddr, &curaddr); + + /* Compute the segment size, and adjust counts. */ + sgsize = PAGE_SIZE - (vaddr & PAGE_MASK); + if (buflen < sgsize) + sgsize = buflen; + + /* Make sure we don't cross any boundaries. */ + if (map->_dm_boundary > 0) { + baddr = (curaddr + map->_dm_boundary) & bmask; + if (sgsize > (baddr - curaddr)) + sgsize = (baddr - curaddr); + } + + /* + * Insert chunk into a segment, coalescing with + * the previous segment if possible. + */ + if (first) { + map->dm_segs[seg].ds_addr = curaddr; + map->dm_segs[seg].ds_len = sgsize; + map->dm_segs[seg]._ds_vaddr = vaddr; + first = 0; + } else { + if (curaddr == lastaddr && + (map->dm_segs[seg].ds_len + sgsize) <= + map->_dm_maxsegsz && + (map->_dm_boundary == 0 || + (map->dm_segs[seg].ds_addr & bmask) == + (curaddr & bmask))) + map->dm_segs[seg].ds_len += sgsize; + else { + if (++seg >= map->_dm_segcnt) + break; + map->dm_segs[seg].ds_addr = curaddr; + map->dm_segs[seg].ds_len = sgsize; + map->dm_segs[seg]._ds_vaddr = vaddr; + } + } + + lastaddr = curaddr + sgsize; + vaddr += sgsize; + buflen -= sgsize; + } + + *segp = seg; + *lastaddrp = lastaddr; + + /* Did we fit? */ + if (buflen != 0) + return (EFBIG); /* XXX better return value here? */ + return (0); +} + +/* + * bus_dmamap_load: + * + * Load a DMA map with a linear buffer. + */ +int +i80321_local_dmamap_load(bus_dma_tag_t t, bus_dmamap_t map, void *buf, + bus_size_t buflen, struct proc *p, int flags) +{ + vaddr_t lastaddr; + int seg, error; + + /* + * Make sure that on error condition we return "no valid mappings". + */ + map->dm_mapsize = 0; + map->dm_nsegs = 0; + + if (buflen > map->_dm_size) + return (EINVAL); + + seg = 0; + error = i80321_local_dmamap_load_buffer(t, map, buf, buflen, p, + flags, &lastaddr, &seg, 1); + if (error == 0) { + map->dm_mapsize = buflen; + map->dm_nsegs = seg + 1; + map->_dm_proc = p; + } + + return (error); +} + +/* + * bus_dmamap_load_mbuf: + * + * Load a DMA map with an mbuf chain. + */ +int +i80321_local_dmamap_load_mbuf(bus_dma_tag_t t, bus_dmamap_t map, + struct mbuf *m0, int flags) +{ + vaddr_t lastaddr; + int seg, error, first; + struct mbuf *m; + + /* + * Make sure that on error condition we return "no valid mappings". + */ + map->dm_mapsize = 0; + map->dm_nsegs = 0; + +#ifdef DIAGNOSTIC + if ((m0->m_flags & M_PKTHDR) == 0) + panic("i80321_local_bus_dmamap_load_mbuf: no packet header"); +#endif + + if (m0->m_pkthdr.len > map->_dm_size) + return (EINVAL); + + first = 1; + seg = 0; + error = 0; + for (m = m0; m != NULL && error == 0; m = m->m_next) { + error = i80321_local_dmamap_load_buffer(t, map, m->m_data, + m->m_len, NULL, flags, &lastaddr, &seg, first); + first = 0; + } + if (error == 0) { + map->dm_mapsize = m0->m_pkthdr.len; + map->dm_nsegs = seg + 1; + map->_dm_proc = NULL; /* always kernel */ + } + + return (error); +} + +/* + * bus_dmamap_load_uio: + * + * Load a DMA map with a uio. + */ +int +i80321_local_dmamap_load_uio(bus_dma_tag_t t, bus_dmamap_t map, + struct uio *uio, int flags) +{ + vaddr_t lastaddr; + int seg, i, error, first; + bus_size_t minlen, resid; + struct proc *p = NULL; + struct iovec *iov; + caddr_t addr; + + /* + * Make sure that on error condition we return "no valid mappings". + */ + map->dm_mapsize = 0; + map->dm_nsegs = 0; + + resid = uio->uio_resid; + iov = uio->uio_iov; + + if (uio->uio_segflg == UIO_USERSPACE) { + p = uio->uio_procp; +#ifdef DIAGNOSTIC + if (p == NULL) + panic("i80321_local_dmamap_load_uio: USERSPACE but " + "no proc"); +#endif + } + + first = 1; + seg = 0; + error = 0; + for (i = 0; i < uio->uio_iovcnt && resid != 0 && error == 0; i++) { + /* + * Now at the first iovec to load. Load each iovec + * until we have exhausted the residual count. + */ + minlen = resid < iov[i].iov_len ? resid : iov[i].iov_len; + addr = (caddr_t)iov[i].iov_base; + + error = i80321_local_dmamap_load_buffer(t, map, addr, minlen, + p, flags, &lastaddr, &seg, first); + first = 0; + + resid -= minlen; + } + if (error == 0) { + map->dm_mapsize = uio->uio_resid; + map->dm_nsegs = seg + 1; + map->_dm_proc = p; + } + + return (error); +} + +/* + * bus_dmamap_load_raw: + * + * Load a DMA map with raw memory allocated with + * bus_dmamem_alloc(). + */ +int +i80321_local_dmamap_load_raw(bus_dma_tag_t t, bus_dmamap_t map, + bus_dma_segment_t *segs, int nsegs, bus_size_t size, int flags) +{ + + panic("i80321_local_dmamap_load_raw: not implemented"); +} + +/* + * bus_dmamem_alloc: + * + * Allocate DMA-safe memory. + */ +int +i80321_local_dmamem_alloc(bus_dma_tag_t t, bus_size_t size, + bus_size_t alignment, bus_size_t boundary, + bus_dma_segment_t *segs, int nsegs, int *rsegs, int flags) +{ + struct i80321_softc *sc = (void *) t->_ranges; + extern paddr_t physical_start, physical_end; + int error; + + if (sc->sc_iwin[2].iwin_size == 0) + return (ENOMEM); + + error = _bus_dmamem_alloc_range(t, size, alignment, boundary, + segs, nsegs, rsegs, flags, trunc_page(physical_start), + trunc_page(physical_end)); + + return (error); +} diff --git a/sys/arch/arm/xscale/i80321var.h b/sys/arch/arm/xscale/i80321var.h index 3f134590d1bb..2597cc35db1a 100644 --- a/sys/arch/arm/xscale/i80321var.h +++ b/sys/arch/arm/xscale/i80321var.h @@ -1,4 +1,4 @@ -/* $NetBSD: i80321var.h,v 1.1 2002/03/27 21:45:48 thorpej Exp $ */ +/* $NetBSD: i80321var.h,v 1.2 2002/07/25 15:00:49 thorpej Exp $ */ /* * Copyright (c) 2002 Wasabi Systems, Inc. @@ -132,6 +132,9 @@ struct i80321_softc { /* GPIO state */ uint8_t sc_gpio_dir; /* GPIO pin direction (1 == output) */ uint8_t sc_gpio_val; /* GPIO output pin value */ + + /* DMA tag for local devices. */ + struct arm32_bus_dma_tag sc_local_dmat; }; extern struct bus_space i80321_bs_tag; @@ -153,6 +156,8 @@ void i80321_bs_init(bus_space_tag_t, void *); void i80321_io_bs_init(bus_space_tag_t, void *); void i80321_mem_bs_init(bus_space_tag_t, void *); +void i80321_local_dma_init(bus_dma_tag_t, void *); + void i80321_pci_dma_init(bus_dma_tag_t, void *); void i80321_pci_init(pci_chipset_tag_t, void *);