busdma_machdep.c revision 99657
138514Sdfr/* 238514Sdfr * Copyright (c) 2002 Peter Grehan 338514Sdfr * Copyright (c) 1997, 1998 Justin T. Gibbs. 438514Sdfr * All rights reserved. 538514Sdfr * 638514Sdfr * Redistribution and use in source and binary forms, with or without 738514Sdfr * modification, are permitted provided that the following conditions 838514Sdfr * are met: 938514Sdfr * 1. Redistributions of source code must retain the above copyright 1038514Sdfr * notice, this list of conditions, and the following disclaimer, 1138514Sdfr * without modification, immediately at the beginning of the file. 1238514Sdfr * 2. The name of the author may not be used to endorse or promote products 1338514Sdfr * derived from this software without specific prior written permission. 1438514Sdfr * 1538514Sdfr * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 1638514Sdfr * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 1738514Sdfr * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 1838514Sdfr * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR 1938514Sdfr * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 2038514Sdfr * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 2138514Sdfr * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 2238514Sdfr * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 2338514Sdfr * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 2438514Sdfr * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 2538514Sdfr * SUCH DAMAGE. 2650477Speter * 2738514Sdfr * From i386/busdma_machdep.c,v 1.26 2002/04/19 22:58:09 alfred 2838514Sdfr */ 2938514Sdfr 3038514Sdfr#ifndef lint 3138514Sdfrstatic const char rcsid[] = 3238514Sdfr "$FreeBSD: head/sys/powerpc/powerpc/busdma_machdep.c 99657 2002-07-09 12:47:14Z benno $"; 3338514Sdfr#endif /* not lint */ 3438514Sdfr 3538514Sdfr/* 3638514Sdfr * MacPPC bus dma support routines 3738514Sdfr */ 3838514Sdfr 3938514Sdfr#include <sys/param.h> 4039071Sdfr#include <sys/systm.h> 4139071Sdfr#include <sys/malloc.h> 4254655Seivind#include <sys/bus.h> 4339071Sdfr#include <sys/interrupt.h> 4452128Speter#include <sys/lock.h> 4539071Sdfr#include <sys/proc.h> 4639071Sdfr#include <sys/mutex.h> 4739071Sdfr 4852128Speter#include <vm/vm.h> 4939071Sdfr#include <vm/vm_page.h> 5039071Sdfr 5139071Sdfr#include <machine/bus.h> 5240156Speter 5338514Sdfrstruct bus_dma_tag { 5438514Sdfr bus_dma_tag_t parent; 5543301Sdillon bus_size_t alignment; 5643309Sdillon bus_size_t boundary; 5738514Sdfr bus_addr_t lowaddr; 5843301Sdillon bus_addr_t highaddr; 5938514Sdfr bus_dma_filter_t *filter; 6040156Speter void *filterarg; 6140156Speter bus_size_t maxsize; 6238514Sdfr u_int nsegments; 6338514Sdfr bus_size_t maxsegsz; 6440156Speter int flags; 6538514Sdfr int ref_count; 6638514Sdfr int map_count; 6738514Sdfr}; 6838514Sdfr 6938514Sdfrstruct bus_dmamap { 7038514Sdfr bus_dma_tag_t dmat; 7140156Speter void *buf; /* unmapped buffer pointer */ 7238514Sdfr bus_size_t buflen; /* unmapped buffer length */ 7338514Sdfr bus_dmamap_callback_t *callback; 7440156Speter void *callback_arg; 7540156Speter}; 7640156Speter 7740156Speter/* 7840156Speter * Allocate a device specific dma_tag. 7940156Speter */ 8038514Sdfrint 8139071Sdfrbus_dma_tag_create(bus_dma_tag_t parent, bus_size_t alignment, 8239071Sdfr bus_size_t boundary, bus_addr_t lowaddr, 8339071Sdfr bus_addr_t highaddr, bus_dma_filter_t *filter, 8439071Sdfr void *filterarg, bus_size_t maxsize, int nsegments, 8539071Sdfr bus_size_t maxsegsz, int flags, bus_dma_tag_t *dmat) 8638514Sdfr{ 8738514Sdfr bus_dma_tag_t newtag; 8838514Sdfr int error = 0; 8938514Sdfr 9038514Sdfr /* Return a NULL tag on failure */ 9138514Sdfr *dmat = NULL; 9240254Speter 9339071Sdfr newtag = (bus_dma_tag_t)malloc(sizeof(*newtag), M_DEVBUF, M_NOWAIT); 9439071Sdfr if (newtag == NULL) 9539071Sdfr return (ENOMEM); 9639071Sdfr 9739071Sdfr newtag->parent = parent; 9839071Sdfr newtag->alignment = alignment; 9939071Sdfr newtag->boundary = boundary; 10039071Sdfr newtag->lowaddr = trunc_page((vm_offset_t)lowaddr) + (PAGE_SIZE - 1); 10139071Sdfr newtag->highaddr = trunc_page((vm_offset_t)highaddr) + (PAGE_SIZE - 1); 10239071Sdfr newtag->filter = filter; 10340254Speter newtag->filterarg = filterarg; 10440254Speter newtag->maxsize = maxsize; 10540254Speter newtag->nsegments = nsegments; 10640254Speter newtag->maxsegsz = maxsegsz; 10740254Speter newtag->flags = flags; 10840292Speter newtag->ref_count = 1; /* Count ourself */ 10940292Speter newtag->map_count = 0; 11038514Sdfr 11138514Sdfr /* 11238514Sdfr * Take into account any restrictions imposed by our parent tag 11338514Sdfr */ 11438514Sdfr if (parent != NULL) { 11540254Speter newtag->lowaddr = min(parent->lowaddr, newtag->lowaddr); 11638514Sdfr newtag->highaddr = max(parent->highaddr, newtag->highaddr); 11738514Sdfr 11838514Sdfr /* 11938514Sdfr * XXX Not really correct??? Probably need to honor boundary 12038514Sdfr * all the way up the inheritence chain. 12138514Sdfr */ 12238514Sdfr newtag->boundary = max(parent->boundary, newtag->boundary); 12338514Sdfr if (newtag->filter == NULL) { 12438514Sdfr /* 12540156Speter * Short circuit looking at our parent directly 12640156Speter * since we have encapsulated all of its information 12740156Speter */ 12840156Speter newtag->filter = parent->filter; 12940156Speter newtag->filterarg = parent->filterarg; 13040156Speter newtag->parent = parent->parent; 13138514Sdfr } 13238514Sdfr if (newtag->parent != NULL) { 13338514Sdfr parent->ref_count++; 13438514Sdfr } 13538514Sdfr } 13638514Sdfr 13738514Sdfr *dmat = newtag; 13840156Speter return (error); 13940156Speter} 14038514Sdfr 14138514Sdfrint 14238514Sdfrbus_dma_tag_destroy(bus_dma_tag_t dmat) 14338514Sdfr{ 14440397Speter if (dmat != NULL) { 14538514Sdfr 14638514Sdfr if (dmat->map_count != 0) 14739071Sdfr return (EBUSY); 14839071Sdfr 14939071Sdfr while (dmat != NULL) { 15038514Sdfr bus_dma_tag_t parent; 15140156Speter 15240156Speter parent = dmat->parent; 15340156Speter dmat->ref_count--; 15440156Speter if (dmat->ref_count == 0) { 15540156Speter free(dmat, M_DEVBUF); 15640156Speter /* 15740156Speter * Last reference count, so 15838514Sdfr * release our reference 15938514Sdfr * count on our parent. 16038514Sdfr */ 16146694Speter dmat = parent; 16250275Sbde } else 16340156Speter dmat = NULL; 16440156Speter } 16540254Speter } 16640156Speter return (0); 16740156Speter} 16840156Speter 16940156Speter/* 17040156Speter * Allocate a handle for mapping from kva/uva/physical 17140156Speter * address space into bus device space. 17240156Speter */ 17340254Speterint 17438514Sdfrbus_dmamap_create(bus_dma_tag_t dmat, int flags, bus_dmamap_t *mapp) 17543185Sdfr{ 17638514Sdfr *mapp = NULL; 17740156Speter dmat->map_count++; 17838514Sdfr 17938514Sdfr return (0); 18040156Speter} 18138514Sdfr 18238514Sdfr/* 18340254Speter * Destroy a handle for mapping from kva/uva/physical 18440254Speter * address space into bus device space. 18540254Speter */ 18640254Speterint 18740254Speterbus_dmamap_destroy(bus_dma_tag_t dmat, bus_dmamap_t map) 18840254Speter{ 18940254Speter if (map != NULL) { 19040254Speter panic("dmamap_destroy: NULL?\n"); 19140254Speter } 19240254Speter dmat->map_count--; 19340292Speter return (0); 19440292Speter} 19540254Speter 19640254Speter/* 19740254Speter * Allocate a piece of memory that can be efficiently mapped into 19840254Speter * bus device space based on the constraints lited in the dma tag. 19940254Speter * A dmamap to for use with dmamap_load is also allocated. 20040254Speter */ 20140254Speterint 20240254Speterbus_dmamem_alloc(bus_dma_tag_t dmat, void** vaddr, int flags, 20340254Speter bus_dmamap_t *mapp) 20440254Speter{ 20540254Speter *mapp = NULL; 20640254Speter 20740254Speter if (dmat->maxsize <= PAGE_SIZE) { 20840254Speter *vaddr = malloc(dmat->maxsize, M_DEVBUF, 20940254Speter (flags & BUS_DMA_NOWAIT) ? M_NOWAIT : M_WAITOK); 21040254Speter } else { 21140254Speter /* 21240254Speter * XXX Use Contigmalloc until it is merged into this facility 21340254Speter * and handles multi-seg allocations. Nobody is doing 21440254Speter * multi-seg allocations yet though. 21540254Speter */ 21640254Speter *vaddr = contigmalloc(dmat->maxsize, M_DEVBUF, 21740254Speter (flags & BUS_DMA_NOWAIT) ? M_NOWAIT : M_WAITOK, 21840254Speter 0ul, dmat->lowaddr, dmat->alignment? dmat->alignment : 1ul, 21940254Speter dmat->boundary); 22040254Speter } 22140254Speter 22240254Speter if (*vaddr == NULL) 22340254Speter return (ENOMEM); 22440254Speter 22540254Speter return (0); 22640254Speter} 22740254Speter 22840254Speter/* 22940254Speter * Free a piece of memory and it's allociated dmamap, that was allocated 23040254Speter * via bus_dmamem_alloc. Make the same choice for free/contigfree. 23140254Speter */ 23240254Spetervoid 23340254Speterbus_dmamem_free(bus_dma_tag_t dmat, void *vaddr, bus_dmamap_t map) 23440254Speter{ 23538514Sdfr if (map != NULL) 23638514Sdfr panic("bus_dmamem_free: Invalid map freed\n"); 23738514Sdfr if (dmat->maxsize <= PAGE_SIZE) 23839071Sdfr free(vaddr, M_DEVBUF); 23939071Sdfr else 24038514Sdfr contigfree(vaddr, dmat->maxsize, M_DEVBUF); 24138514Sdfr} 24238514Sdfr 24338514Sdfr/* 24438514Sdfr * Map the buffer buf into bus space using the dmamap map. 24538514Sdfr */ 24638514Sdfrint 24738514Sdfrbus_dmamap_load(bus_dma_tag_t dmat, bus_dmamap_t map, void *buf, 24838514Sdfr bus_size_t buflen, bus_dmamap_callback_t *callback, 24938514Sdfr void *callback_arg, int flags) 25038514Sdfr{ 25138514Sdfr vm_offset_t vaddr; 25238514Sdfr vm_offset_t paddr; 25338514Sdfr#ifdef __GNUC__ 25438514Sdfr bus_dma_segment_t dm_segments[dmat->nsegments]; 25539071Sdfr#else 25638514Sdfr bus_dma_segment_t dm_segments[BUS_DMAMAP_NSEGS]; 25740254Speter#endif 25840254Speter bus_dma_segment_t *sg; 25940254Speter int seg; 26038514Sdfr int error = 0; 26139071Sdfr vm_offset_t nextpaddr; 26238514Sdfr 26338514Sdfr if (map != NULL) 26438514Sdfr panic("bus_dmamap_load: Invalid map\n"); 26538514Sdfr 26639071Sdfr vaddr = (vm_offset_t)buf; 26739071Sdfr sg = &dm_segments[0]; 26839071Sdfr seg = 1; 26939071Sdfr sg->ds_len = 0; 27039071Sdfr nextpaddr = 0; 27139071Sdfr 27239071Sdfr do { 27339071Sdfr bus_size_t size; 27439071Sdfr 27539071Sdfr paddr = pmap_kextract(vaddr); 27639071Sdfr size = PAGE_SIZE - (paddr & PAGE_MASK); 27739071Sdfr if (size > buflen) 27839071Sdfr size = buflen; 27939071Sdfr 28039071Sdfr if (sg->ds_len == 0) { 28139071Sdfr sg->ds_addr = paddr; 28239071Sdfr sg->ds_len = size; 28339071Sdfr } else if (paddr == nextpaddr) { 28439071Sdfr sg->ds_len += size; 28539071Sdfr } else { 28639071Sdfr /* Go to the next segment */ 28739071Sdfr sg++; 28839071Sdfr seg++; 28939071Sdfr if (seg > dmat->nsegments) 29039071Sdfr break; 29139071Sdfr sg->ds_addr = paddr; 29239071Sdfr sg->ds_len = size; 29339071Sdfr } 29439071Sdfr vaddr += size; 29539071Sdfr nextpaddr = paddr + size; 29639071Sdfr buflen -= size; 29739071Sdfr 29839071Sdfr } while (buflen > 0); 29939071Sdfr 30039071Sdfr if (buflen != 0) { 30138514Sdfr printf("bus_dmamap_load: Too many segs! buf_len = 0x%lx\n", 30238514Sdfr (u_long)buflen); 30339071Sdfr error = EFBIG; 30439071Sdfr } 30539071Sdfr 30639071Sdfr (*callback)(callback_arg, dm_segments, seg, error); 30739071Sdfr 30839071Sdfr return (0); 30939071Sdfr} 31039071Sdfr 31140254Speter/* 31240254Speter * Release the mapping held by map. 31340254Speter */ 31440254Spetervoid 31540254Speterbus_dmamap_unload(bus_dma_tag_t dmat, bus_dmamap_t map) 31638514Sdfr{} 31738514Sdfr 31838514Sdfrvoid 31939071Sdfrbus_dmamap_sync(bus_dma_tag_t dmat, bus_dmamap_t map, bus_dmasync_op_t op) 32039071Sdfr{} 32139071Sdfr 32239071Sdfr 32339071Sdfr 32439071Sdfr 32538514Sdfr 32640156Speter