From owner-p4-projects@FreeBSD.ORG Wed Aug 2 11:55:17 2006 Return-Path: X-Original-To: p4-projects@freebsd.org Delivered-To: p4-projects@freebsd.org Received: by hub.freebsd.org (Postfix, from userid 32767) id 1E81416A4E0; Wed, 2 Aug 2006 11:55:17 +0000 (UTC) X-Original-To: perforce@FreeBSD.org Delivered-To: perforce@FreeBSD.org Received: from mx1.FreeBSD.org (mx1.freebsd.org [216.136.204.125]) by hub.freebsd.org (Postfix) with ESMTP id B5DFA16A4DF for ; Wed, 2 Aug 2006 11:55:16 +0000 (UTC) (envelope-from gonzo@FreeBSD.org) Received: from repoman.freebsd.org (repoman.freebsd.org [216.136.204.115]) by mx1.FreeBSD.org (Postfix) with ESMTP id 2E3E043D46 for ; Wed, 2 Aug 2006 11:55:16 +0000 (GMT) (envelope-from gonzo@FreeBSD.org) Received: from repoman.freebsd.org (localhost [127.0.0.1]) by repoman.freebsd.org (8.13.6/8.13.6) with ESMTP id k72BtGjM041281 for ; Wed, 2 Aug 2006 11:55:16 GMT (envelope-from gonzo@FreeBSD.org) Received: (from perforce@localhost) by repoman.freebsd.org (8.13.6/8.13.4/Submit) id k72BtFQ5041263 for perforce@freebsd.org; Wed, 2 Aug 2006 11:55:15 GMT (envelope-from gonzo@FreeBSD.org) Date: Wed, 2 Aug 2006 11:55:15 GMT Message-Id: <200608021155.k72BtFQ5041263@repoman.freebsd.org> X-Authentication-Warning: repoman.freebsd.org: perforce set sender to gonzo@FreeBSD.org using -f From: Oleksandr Tymoshenko To: Perforce Change Reviews Cc: Subject: PERFORCE change 103017 for review X-BeenThere: p4-projects@freebsd.org X-Mailman-Version: 2.1.5 Precedence: list List-Id: p4 projects tree changes List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Wed, 02 Aug 2006 11:55:17 -0000 http://perforce.freebsd.org/chv.cgi?CH=103017 Change 103017 by gonzo@gonzo_hq on 2006/08/02 11:54:44 o busdma functions added. Borrowed from NetBSD and FreeBSD/arm. Some calls are stubs, some - semiready (debug required). These functions are required for ATA stuff. Affected files ... .. //depot/projects/mips2/src/sys/mips/mips/busdma_machdep.c#2 edit Differences ... ==== //depot/projects/mips2/src/sys/mips/mips/busdma_machdep.c#2 (text+ko) ==== @@ -25,5 +25,649 @@ * */ +/*- + * Copyright (c) 1997, 1998, 2001 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Jason R. Thorpe of the Numerical Aerospace Simulation Facility, + * NASA Ames Research Center. + * + * 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 by the NetBSD + * Foundation, Inc. and its contributors. + * 4. Neither the name of The NetBSD Foundation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``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 THE FOUNDATION OR CONTRIBUTORS + * 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. + */ + +/* $NetBSD: bus_dma.c,v 1.17 2006/03/01 12:38:11 yamt Exp $ */ + #include __FBSDID("$FreeBSD$"); + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include +#include +#include + +struct bus_dma_tag { + bus_dma_tag_t parent; + bus_size_t alignment; + bus_size_t boundary; + bus_addr_t lowaddr; + bus_addr_t highaddr; + bus_dma_filter_t *filter; + void *filterarg; + bus_size_t maxsize; + u_int nsegments; + bus_size_t maxsegsz; + int flags; + int ref_count; + int map_count; + bus_dma_lock_t *lockfunc; + void *lockfuncarg; + /* XXX: machine-dependent fields */ + vm_offset_t _physbase; + vm_offset_t _wbase; + vm_offset_t _wsize; +}; + +#define DMAMAP_LINEAR 0x1 +#define DMAMAP_MBUF 0x2 +#define DMAMAP_UIO 0x4 +#define DMAMAP_ALLOCATED 0x10 +#define DMAMAP_TYPE_MASK (DMAMAP_LINEAR|DMAMAP_MBUF|DMAMAP_UIO) +#define DMAMAP_COHERENT 0x8 +struct bus_dmamap { + bus_dma_tag_t dmat; + int flags; + void *buffer; + void *origbuffer; + void *allocbuffer; + TAILQ_ENTRY(bus_dmamap) freelist; + int len; +}; + +static TAILQ_HEAD(,bus_dmamap) dmamap_freelist = + TAILQ_HEAD_INITIALIZER(dmamap_freelist); + +#define BUSDMA_STATIC_MAPS 500 +static struct bus_dmamap map_pool[BUSDMA_STATIC_MAPS]; + +static struct mtx busdma_mtx; + +MTX_SYSINIT(busdma_mtx, &busdma_mtx, "busdma lock", MTX_DEF); + +static void +mips_dmamap_freelist_init(void *dummy) +{ + int i; + + for (i = 0; i < BUSDMA_STATIC_MAPS; i++) + TAILQ_INSERT_HEAD(&dmamap_freelist, &map_pool[i], freelist); +} + +SYSINIT(busdma, SI_SUB_VM, SI_ORDER_ANY, mips_dmamap_freelist_init, NULL); + + + +/* + * Check to see if the specified page is in an allowed DMA range. + */ + +static __inline int +bus_dmamap_load_buffer(bus_dma_tag_t dmat, bus_dma_segment_t *segs, + bus_dmamap_t map, void *buf, bus_size_t buflen, struct pmap *pmap, + int flags, vm_offset_t *lastaddrp, int *segp); + +/* + * Convenience function for manipulating driver locks from busdma (during + * busdma_swi, for example). Drivers that don't provide their own locks + * should specify &Giant to dmat->lockfuncarg. Drivers that use their own + * non-mutex locking scheme don't have to use this at all. + */ +void +busdma_lock_mutex(void *arg, bus_dma_lock_op_t op) +{ + struct mtx *dmtx; + + dmtx = (struct mtx *)arg; + switch (op) { + case BUS_DMA_LOCK: + mtx_lock(dmtx); + break; + case BUS_DMA_UNLOCK: + mtx_unlock(dmtx); + break; + default: + panic("Unknown operation 0x%x for busdma_lock_mutex!", op); + } +} + +/* + * dflt_lock should never get called. It gets put into the dma tag when + * lockfunc == NULL, which is only valid if the maps that are associated + * with the tag are meant to never be defered. + * XXX Should have a way to identify which driver is responsible here. + */ +static void +dflt_lock(void *arg, bus_dma_lock_op_t op) +{ +#ifdef INVARIANTS + panic("driver error: busdma dflt_lock called"); +#else + printf("DRIVER_ERROR: busdma dflt_lock called\n"); +#endif +} + +static __inline bus_dmamap_t +_busdma_alloc_dmamap(void) +{ + bus_dmamap_t map; + + mtx_lock(&busdma_mtx); + map = TAILQ_FIRST(&dmamap_freelist); + if (map) + TAILQ_REMOVE(&dmamap_freelist, map, freelist); + mtx_unlock(&busdma_mtx); + if (!map) { + map = malloc(sizeof(*map), M_DEVBUF, M_NOWAIT); + if (map) + map->flags = DMAMAP_ALLOCATED; + } else + map->flags = 0; + return (map); +} + +static __inline void +_busdma_free_dmamap(bus_dmamap_t map) +{ + if (map->flags & DMAMAP_ALLOCATED) + free(map, M_DEVBUF); + else { + mtx_lock(&busdma_mtx); + TAILQ_INSERT_HEAD(&dmamap_freelist, map, freelist); + mtx_unlock(&busdma_mtx); + } +} + +int +bus_dma_tag_create(bus_dma_tag_t parent, bus_size_t alignment, + bus_size_t boundary, bus_addr_t lowaddr, + bus_addr_t highaddr, bus_dma_filter_t *filter, + void *filterarg, bus_size_t maxsize, int nsegments, + bus_size_t maxsegsz, int flags, bus_dma_lock_t *lockfunc, + void *lockfuncarg, bus_dma_tag_t *dmat) +{ + bus_dma_tag_t newtag; + int error = 0; + + /* Basic sanity checking */ + if (boundary != 0 && boundary < maxsegsz) + maxsegsz = boundary; + + /* Return a NULL tag on failure */ + *dmat = NULL; + + newtag = (bus_dma_tag_t)malloc(sizeof(*newtag), M_DEVBUF, + M_ZERO | M_NOWAIT); + if (newtag == NULL) { + CTR4(KTR_BUSDMA, "%s returned tag %p tag flags 0x%x error %d", + __func__, newtag, 0, error); + return (ENOMEM); + } + + newtag->parent = parent; + newtag->alignment = alignment; + newtag->boundary = boundary; + newtag->lowaddr = trunc_page((vm_paddr_t)lowaddr) + (PAGE_SIZE - 1); + newtag->highaddr = trunc_page((vm_paddr_t)highaddr) + + (PAGE_SIZE - 1); + newtag->filter = filter; + newtag->filterarg = filterarg; + newtag->maxsize = maxsize; + newtag->nsegments = nsegments; + newtag->maxsegsz = maxsegsz; + newtag->flags = flags; + newtag->ref_count = 1; /* Count ourself */ + newtag->map_count = 0; + newtag->_wbase = 0; + newtag->_physbase = 0; + newtag->_wsize = 64 * 1024 * 1024; + if (lockfunc != NULL) { + newtag->lockfunc = lockfunc; + newtag->lockfuncarg = lockfuncarg; + } else { + newtag->lockfunc = dflt_lock; + newtag->lockfuncarg = NULL; + } + + /* Take into account any restrictions imposed by our parent tag */ + if (parent != NULL) { + newtag->lowaddr = MIN(parent->lowaddr, newtag->lowaddr); + newtag->highaddr = MAX(parent->highaddr, newtag->highaddr); + if (newtag->boundary == 0) + newtag->boundary = parent->boundary; + else if (parent->boundary != 0) + newtag->boundary = MIN(parent->boundary, + newtag->boundary); + if (newtag->filter == NULL) { + /* + * Short circuit looking at our parent directly + * since we have encapsulated all of its information + */ + newtag->filter = parent->filter; + newtag->filterarg = parent->filterarg; + newtag->parent = parent->parent; + } + if (newtag->parent != NULL) + atomic_add_int(&parent->ref_count, 1); + } + + if (error != 0) { + free(newtag, M_DEVBUF); + } else { + *dmat = newtag; + } + CTR4(KTR_BUSDMA, "%s returned tag %p tag flags 0x%x error %d", + __func__, newtag, (newtag != NULL ? newtag->flags : 0), error); + return (error); + +} + +int +bus_dma_tag_destroy(bus_dma_tag_t dmat) +{ +#ifdef KTR + bus_dma_tag_t dmat_copy = dmat; +#endif + + if (dmat != NULL) { + + if (dmat->map_count != 0) + return (EBUSY); + + while (dmat != NULL) { + bus_dma_tag_t parent; + + parent = dmat->parent; + atomic_subtract_int(&dmat->ref_count, 1); + if (dmat->ref_count == 0) { + free(dmat, M_DEVBUF); + /* + * Last reference count, so + * release our reference + * count on our parent. + */ + dmat = parent; + } else + dmat = NULL; + } + } + CTR2(KTR_BUSDMA, "%s tag %p", __func__, dmat_copy); + + return (0); +} + +/* + * Allocate a handle for mapping from kva/uva/physical + * address space into bus device space. + */ +int +bus_dmamap_create(bus_dma_tag_t dmat, int flags, bus_dmamap_t *mapp) +{ + bus_dmamap_t newmap; +#ifdef KTR + int error = 0; +#endif + + newmap = _busdma_alloc_dmamap(); + if (newmap == NULL) { + CTR3(KTR_BUSDMA, "%s: tag %p error %d", __func__, dmat, ENOMEM); + return (ENOMEM); + } + *mapp = newmap; + newmap->dmat = dmat; + dmat->map_count++; + + CTR4(KTR_BUSDMA, "%s: tag %p tag flags 0x%x error %d", + __func__, dmat, dmat->flags, error); + + return (0); + +} + +/* + * Destroy a handle for mapping from kva/uva/physical + * address space into bus device space. + */ +int +bus_dmamap_destroy(bus_dma_tag_t dmat, bus_dmamap_t map) +{ + _busdma_free_dmamap(map); + dmat->map_count--; + CTR2(KTR_BUSDMA, "%s: tag %p error 0", __func__, dmat); + return (0); +} + +/* + * Allocate a piece of memory that can be efficiently mapped into + * bus device space based on the constraints lited in the dma tag. + * A dmamap to for use with dmamap_load is also allocated. + */ +int +bus_dmamem_alloc(bus_dma_tag_t dmat, void** vaddr, int flags, + bus_dmamap_t *mapp) +{ + bus_dmamap_t newmap = NULL; + + int mflags; + + if (flags & BUS_DMA_NOWAIT) + mflags = M_NOWAIT; + else + mflags = M_WAITOK; + if (flags & BUS_DMA_ZERO) + mflags |= M_ZERO; + + newmap = _busdma_alloc_dmamap(); + if (newmap == NULL) { + CTR4(KTR_BUSDMA, "%s: tag %p tag flags 0x%x error %d", + __func__, dmat, dmat->flags, ENOMEM); + return (ENOMEM); + } + dmat->map_count++; + *mapp = newmap; + newmap->dmat = dmat; + + if (dmat->maxsize <= PAGE_SIZE) { + *vaddr = malloc(dmat->maxsize, M_DEVBUF, mflags); + } else { + /* + * XXX Use Contigmalloc until it is merged into this facility + * and handles multi-seg allocations. Nobody is doing + * multi-seg allocations yet though. + */ + *vaddr = contigmalloc(dmat->maxsize, M_DEVBUF, mflags, + 0ul, dmat->lowaddr, dmat->alignment? dmat->alignment : 1ul, + dmat->boundary); + } + if (*vaddr == NULL) { + if (newmap != NULL) { + _busdma_free_dmamap(newmap); + dmat->map_count--; + } + *mapp = NULL; + return (ENOMEM); + } + if (flags & BUS_DMA_COHERENT) { + /* XXXMIPS: check this later + void *tmpaddr = arm_remap_nocache( + (void *)((vm_offset_t)*vaddr &~ PAGE_MASK), + dmat->maxsize + ((vm_offset_t)*vaddr & PAGE_MASK)); + */ + void *tmpaddr = (void *)*vaddr; + + if (tmpaddr) { + tmpaddr = (void *)((vm_offset_t)(tmpaddr) + + ((vm_offset_t)*vaddr & PAGE_MASK)); + newmap->origbuffer = *vaddr; + newmap->allocbuffer = tmpaddr; + mips_dcache_wbinv_range((vm_offset_t)*vaddr, + dmat->maxsize); + *vaddr = tmpaddr; + } else + newmap->origbuffer = newmap->allocbuffer = NULL; + } else + newmap->origbuffer = newmap->allocbuffer = NULL; + return (0); + +} + +/* + * Free a piece of memory and it's allocated dmamap, that was allocated + * via bus_dmamem_alloc. Make the same choice for free/contigfree. + */ +void +bus_dmamem_free(bus_dma_tag_t dmat, void *vaddr, bus_dmamap_t map) +{ + if (map->allocbuffer) { + KASSERT(map->allocbuffer == vaddr, + ("Trying to freeing the wrong DMA buffer")); + vaddr = map->origbuffer; + /* XXXMIPS: arm_unmap_nocache(map->allocbuffer, dmat->maxsize); */ + } + if (dmat->maxsize <= PAGE_SIZE) + free(vaddr, M_DEVBUF); + else { + contigfree(vaddr, dmat->maxsize, M_DEVBUF); + } + dmat->map_count--; + _busdma_free_dmamap(map); + CTR3(KTR_BUSDMA, "%s: tag %p flags 0x%x", __func__, dmat, dmat->flags); + +} + +/* + * Utility function to load a linear buffer. lastaddrp holds state + * between invocations (for multiple-buffer loads). segp contains + * the starting segment on entrance, and the ending segment on exit. + * first indicates if this is the first invocation of this function. + */ +static __inline int +bus_dmamap_load_buffer(bus_dma_tag_t dmat, bus_dma_segment_t *segs, + bus_dmamap_t map, void *buf, bus_size_t buflen, struct pmap *pmap, + int flags, vm_offset_t *lastaddrp, int *segp) +{ + bus_size_t sgsize; + bus_size_t bmask; + vm_offset_t curaddr, lastaddr; + vm_offset_t vaddr = (vm_offset_t)buf; + int seg; + int error = 0; + + lastaddr = *lastaddrp; + bmask = ~(dmat->boundary - 1); + + for (seg = *segp; buflen > 0 ; ) { + /* + * Get the physical address for this segment. + */ + curaddr = pmap_extract(pmap, vaddr); + + /* + * If we're beyond the current DMA window, indicate + * that and try to fall back onto something else. + */ + /* XXXMIPS: check for range validity */ + if (curaddr < dmat->_physbase || + curaddr >= (dmat->_physbase + dmat->_wsize)) + return (EINVAL); +#if 0 + printf("dma: addr 0x%08lx -> 0x%08lx\n", curaddr, + (curaddr - t->_physbase) + t->_wbase); +#endif + /* + * In a valid DMA range. Translate the physical + * memory address to an address in the DMA window. + */ + curaddr = (curaddr - dmat->_physbase) + dmat->_wbase; + + + /* + * Compute the segment size, and adjust counts. + */ + sgsize = PAGE_SIZE - ((u_long)curaddr & PAGE_MASK); + if (buflen < sgsize) + sgsize = buflen; + + /* + * Insert chunk into a segment, coalescing with + * the previous segment if possible. + */ + if (seg >= 0 && curaddr == lastaddr && + (segs[seg].ds_len + sgsize) <= dmat->maxsegsz && + (dmat->boundary == 0 || + (segs[seg].ds_addr & bmask) == + (curaddr & bmask))) { + segs[seg].ds_len += sgsize; + goto segdone; + } else { + if (++seg >= dmat->nsegments) + break; + segs[seg].ds_addr = curaddr; + segs[seg].ds_len = sgsize; + } + if (error) + break; +segdone: + lastaddr = curaddr + sgsize; + vaddr += sgsize; + buflen -= sgsize; + } + + *segp = seg; + *lastaddrp = lastaddr; + + /* + * Did we fit? + */ + if (buflen != 0) + error = EFBIG; + + return error; +} + +/* + * Map the buffer buf into bus space using the dmamap map. + */ +int +bus_dmamap_load(bus_dma_tag_t dmat, bus_dmamap_t map, void *buf, + bus_size_t buflen, bus_dmamap_callback_t *callback, + void *callback_arg, int flags) +{ + vm_offset_t lastaddr = 0; + int error, nsegs = -1; +#ifdef __CC_SUPPORTS_DYNAMIC_ARRAY_INIT + bus_dma_segment_t dm_segments[dmat->nsegments]; +#else + bus_dma_segment_t dm_segments[BUS_DMAMAP_NSEGS]; +#endif + + KASSERT(dmat != NULL, ("dmatag is NULL")); + KASSERT(map != NULL, ("dmamap is NULL")); + map->flags &= ~DMAMAP_TYPE_MASK; + map->flags |= DMAMAP_LINEAR|DMAMAP_COHERENT; + map->buffer = buf; + map->len = buflen; + error = bus_dmamap_load_buffer(dmat, + dm_segments, map, buf, buflen, kernel_pmap, + flags, &lastaddr, &nsegs); + if (error) + (*callback)(callback_arg, NULL, 0, error); + else + (*callback)(callback_arg, dm_segments, nsegs + 1, error); + + CTR5(KTR_BUSDMA, "%s: tag %p tag flags 0x%x error %d nsegs %d", + __func__, dmat, dmat->flags, nsegs + 1, error); + + return (0); + +} + +/* + * Like bus_dmamap_load(), but for mbufs. + */ +int +bus_dmamap_load_mbuf(bus_dma_tag_t dmat, bus_dmamap_t map, struct mbuf *m0, + bus_dmamap_callback2_t *callback, void *callback_arg, + int flags) +{ + panic("Unimplemented %s at %s:%d\n", __func__, __FILE__, __LINE__); + return (0); +} + +int +bus_dmamap_load_mbuf_sg(bus_dma_tag_t dmat, bus_dmamap_t map, + struct mbuf *m0, bus_dma_segment_t *segs, int *nsegs, + int flags) +{ + int error = 0; + panic("Unimplemented %s at %s:%d\n", __func__, __FILE__, __LINE__); + return (error); +} + +/* + * Like bus_dmamap_load(), but for uios. + */ +int +bus_dmamap_load_uio(bus_dma_tag_t dmat, bus_dmamap_t map, struct uio *uio, + bus_dmamap_callback2_t *callback, void *callback_arg, + int flags) +{ + panic("Unimplemented %s at %s:%d\n", __func__, __FILE__, __LINE__); + return (0); +} + +/* + * Release the mapping held by map. + */ +void +_bus_dmamap_unload(bus_dma_tag_t dmat, bus_dmamap_t map) +{ + panic("Unimplemented %s at %s:%d\n", __func__, __FILE__, __LINE__); + return; +} + + + +static __inline void +bus_dmamap_sync_buf(void *buf, int len, bus_dmasync_op_t op) +{ + panic("Unimplemented %s at %s:%d\n", __func__, __FILE__, __LINE__); +} + +void +_bus_dmamap_sync(bus_dma_tag_t dmat, bus_dmamap_t map, bus_dmasync_op_t op) +{ + panic("Unimplemented %s at %s:%d\n", __func__, __FILE__, __LINE__); +}