busdma_machdep.c revision 99657
178342Sbenno/* 299657Sbenno * Copyright (c) 2002 Peter Grehan 399657Sbenno * Copyright (c) 1997, 1998 Justin T. Gibbs. 478342Sbenno * All rights reserved. 578342Sbenno * 678342Sbenno * Redistribution and use in source and binary forms, with or without 778342Sbenno * modification, are permitted provided that the following conditions 878342Sbenno * are met: 978342Sbenno * 1. Redistributions of source code must retain the above copyright 1099657Sbenno * notice, this list of conditions, and the following disclaimer, 1199657Sbenno * without modification, immediately at the beginning of the file. 1299657Sbenno * 2. The name of the author may not be used to endorse or promote products 1399657Sbenno * derived from this software without specific prior written permission. 1478342Sbenno * 1599657Sbenno * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 1699657Sbenno * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 1799657Sbenno * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 1899657Sbenno * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR 1999657Sbenno * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 2099657Sbenno * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 2199657Sbenno * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 2299657Sbenno * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 2399657Sbenno * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 2499657Sbenno * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 2599657Sbenno * SUCH DAMAGE. 2699657Sbenno * 2799657Sbenno * From i386/busdma_machdep.c,v 1.26 2002/04/19 22:58:09 alfred 2878342Sbenno */ 2978342Sbenno 3078342Sbenno#ifndef lint 3178342Sbennostatic const char rcsid[] = 3278342Sbenno "$FreeBSD: head/sys/powerpc/powerpc/busdma_machdep.c 99657 2002-07-09 12:47:14Z benno $"; 3378342Sbenno#endif /* not lint */ 3478342Sbenno 3599657Sbenno/* 3699657Sbenno * MacPPC bus dma support routines 3799657Sbenno */ 3878342Sbenno 3999657Sbenno#include <sys/param.h> 4099657Sbenno#include <sys/systm.h> 4199657Sbenno#include <sys/malloc.h> 4299657Sbenno#include <sys/bus.h> 4399657Sbenno#include <sys/interrupt.h> 4499657Sbenno#include <sys/lock.h> 4599657Sbenno#include <sys/proc.h> 4699657Sbenno#include <sys/mutex.h> 4799657Sbenno 4899657Sbenno#include <vm/vm.h> 4999657Sbenno#include <vm/vm_page.h> 5099657Sbenno 5199657Sbenno#include <machine/bus.h> 5299657Sbenno 5399657Sbennostruct bus_dma_tag { 5499657Sbenno bus_dma_tag_t parent; 5599657Sbenno bus_size_t alignment; 5699657Sbenno bus_size_t boundary; 5799657Sbenno bus_addr_t lowaddr; 5899657Sbenno bus_addr_t highaddr; 5999657Sbenno bus_dma_filter_t *filter; 6099657Sbenno void *filterarg; 6199657Sbenno bus_size_t maxsize; 6299657Sbenno u_int nsegments; 6399657Sbenno bus_size_t maxsegsz; 6499657Sbenno int flags; 6599657Sbenno int ref_count; 6699657Sbenno int map_count; 6799657Sbenno}; 6899657Sbenno 6999657Sbennostruct bus_dmamap { 7099657Sbenno bus_dma_tag_t dmat; 7199657Sbenno void *buf; /* unmapped buffer pointer */ 7299657Sbenno bus_size_t buflen; /* unmapped buffer length */ 7399657Sbenno bus_dmamap_callback_t *callback; 7499657Sbenno void *callback_arg; 7599657Sbenno}; 7699657Sbenno 7799657Sbenno/* 7899657Sbenno * Allocate a device specific dma_tag. 7999657Sbenno */ 8099657Sbennoint 8199657Sbennobus_dma_tag_create(bus_dma_tag_t parent, bus_size_t alignment, 8299657Sbenno bus_size_t boundary, bus_addr_t lowaddr, 8399657Sbenno bus_addr_t highaddr, bus_dma_filter_t *filter, 8499657Sbenno void *filterarg, bus_size_t maxsize, int nsegments, 8599657Sbenno bus_size_t maxsegsz, int flags, bus_dma_tag_t *dmat) 8699657Sbenno{ 8799657Sbenno bus_dma_tag_t newtag; 8899657Sbenno int error = 0; 8999657Sbenno 9099657Sbenno /* Return a NULL tag on failure */ 9199657Sbenno *dmat = NULL; 9299657Sbenno 9399657Sbenno newtag = (bus_dma_tag_t)malloc(sizeof(*newtag), M_DEVBUF, M_NOWAIT); 9499657Sbenno if (newtag == NULL) 9599657Sbenno return (ENOMEM); 9699657Sbenno 9799657Sbenno newtag->parent = parent; 9899657Sbenno newtag->alignment = alignment; 9999657Sbenno newtag->boundary = boundary; 10099657Sbenno newtag->lowaddr = trunc_page((vm_offset_t)lowaddr) + (PAGE_SIZE - 1); 10199657Sbenno newtag->highaddr = trunc_page((vm_offset_t)highaddr) + (PAGE_SIZE - 1); 10299657Sbenno newtag->filter = filter; 10399657Sbenno newtag->filterarg = filterarg; 10499657Sbenno newtag->maxsize = maxsize; 10599657Sbenno newtag->nsegments = nsegments; 10699657Sbenno newtag->maxsegsz = maxsegsz; 10799657Sbenno newtag->flags = flags; 10899657Sbenno newtag->ref_count = 1; /* Count ourself */ 10999657Sbenno newtag->map_count = 0; 11099657Sbenno 11199657Sbenno /* 11299657Sbenno * Take into account any restrictions imposed by our parent tag 11399657Sbenno */ 11499657Sbenno if (parent != NULL) { 11599657Sbenno newtag->lowaddr = min(parent->lowaddr, newtag->lowaddr); 11699657Sbenno newtag->highaddr = max(parent->highaddr, newtag->highaddr); 11799657Sbenno 11899657Sbenno /* 11999657Sbenno * XXX Not really correct??? Probably need to honor boundary 12099657Sbenno * all the way up the inheritence chain. 12199657Sbenno */ 12299657Sbenno newtag->boundary = max(parent->boundary, newtag->boundary); 12399657Sbenno if (newtag->filter == NULL) { 12499657Sbenno /* 12599657Sbenno * Short circuit looking at our parent directly 12699657Sbenno * since we have encapsulated all of its information 12799657Sbenno */ 12899657Sbenno newtag->filter = parent->filter; 12999657Sbenno newtag->filterarg = parent->filterarg; 13099657Sbenno newtag->parent = parent->parent; 13199657Sbenno } 13299657Sbenno if (newtag->parent != NULL) { 13399657Sbenno parent->ref_count++; 13499657Sbenno } 13599657Sbenno } 13699657Sbenno 13799657Sbenno *dmat = newtag; 13899657Sbenno return (error); 13999657Sbenno} 14099657Sbenno 14199657Sbennoint 14299657Sbennobus_dma_tag_destroy(bus_dma_tag_t dmat) 14399657Sbenno{ 14499657Sbenno if (dmat != NULL) { 14599657Sbenno 14699657Sbenno if (dmat->map_count != 0) 14799657Sbenno return (EBUSY); 14899657Sbenno 14999657Sbenno while (dmat != NULL) { 15099657Sbenno bus_dma_tag_t parent; 15199657Sbenno 15299657Sbenno parent = dmat->parent; 15399657Sbenno dmat->ref_count--; 15499657Sbenno if (dmat->ref_count == 0) { 15599657Sbenno free(dmat, M_DEVBUF); 15699657Sbenno /* 15799657Sbenno * Last reference count, so 15899657Sbenno * release our reference 15999657Sbenno * count on our parent. 16099657Sbenno */ 16199657Sbenno dmat = parent; 16299657Sbenno } else 16399657Sbenno dmat = NULL; 16499657Sbenno } 16599657Sbenno } 16699657Sbenno return (0); 16799657Sbenno} 16899657Sbenno 16999657Sbenno/* 17099657Sbenno * Allocate a handle for mapping from kva/uva/physical 17199657Sbenno * address space into bus device space. 17299657Sbenno */ 17399657Sbennoint 17499657Sbennobus_dmamap_create(bus_dma_tag_t dmat, int flags, bus_dmamap_t *mapp) 17599657Sbenno{ 17699657Sbenno *mapp = NULL; 17799657Sbenno dmat->map_count++; 17899657Sbenno 17999657Sbenno return (0); 18099657Sbenno} 18199657Sbenno 18299657Sbenno/* 18399657Sbenno * Destroy a handle for mapping from kva/uva/physical 18499657Sbenno * address space into bus device space. 18599657Sbenno */ 18699657Sbennoint 18799657Sbennobus_dmamap_destroy(bus_dma_tag_t dmat, bus_dmamap_t map) 18899657Sbenno{ 18999657Sbenno if (map != NULL) { 19099657Sbenno panic("dmamap_destroy: NULL?\n"); 19199657Sbenno } 19299657Sbenno dmat->map_count--; 19399657Sbenno return (0); 19499657Sbenno} 19599657Sbenno 19699657Sbenno/* 19799657Sbenno * Allocate a piece of memory that can be efficiently mapped into 19899657Sbenno * bus device space based on the constraints lited in the dma tag. 19999657Sbenno * A dmamap to for use with dmamap_load is also allocated. 20099657Sbenno */ 20199657Sbennoint 20299657Sbennobus_dmamem_alloc(bus_dma_tag_t dmat, void** vaddr, int flags, 20399657Sbenno bus_dmamap_t *mapp) 20499657Sbenno{ 20599657Sbenno *mapp = NULL; 20699657Sbenno 20799657Sbenno if (dmat->maxsize <= PAGE_SIZE) { 20899657Sbenno *vaddr = malloc(dmat->maxsize, M_DEVBUF, 20999657Sbenno (flags & BUS_DMA_NOWAIT) ? M_NOWAIT : M_WAITOK); 21099657Sbenno } else { 21199657Sbenno /* 21299657Sbenno * XXX Use Contigmalloc until it is merged into this facility 21399657Sbenno * and handles multi-seg allocations. Nobody is doing 21499657Sbenno * multi-seg allocations yet though. 21599657Sbenno */ 21699657Sbenno *vaddr = contigmalloc(dmat->maxsize, M_DEVBUF, 21799657Sbenno (flags & BUS_DMA_NOWAIT) ? M_NOWAIT : M_WAITOK, 21899657Sbenno 0ul, dmat->lowaddr, dmat->alignment? dmat->alignment : 1ul, 21999657Sbenno dmat->boundary); 22099657Sbenno } 22199657Sbenno 22299657Sbenno if (*vaddr == NULL) 22399657Sbenno return (ENOMEM); 22499657Sbenno 22599657Sbenno return (0); 22699657Sbenno} 22799657Sbenno 22899657Sbenno/* 22999657Sbenno * Free a piece of memory and it's allociated dmamap, that was allocated 23099657Sbenno * via bus_dmamem_alloc. Make the same choice for free/contigfree. 23199657Sbenno */ 23278342Sbennovoid 23399657Sbennobus_dmamem_free(bus_dma_tag_t dmat, void *vaddr, bus_dmamap_t map) 23478342Sbenno{ 23599657Sbenno if (map != NULL) 23699657Sbenno panic("bus_dmamem_free: Invalid map freed\n"); 23799657Sbenno if (dmat->maxsize <= PAGE_SIZE) 23899657Sbenno free(vaddr, M_DEVBUF); 23999657Sbenno else 24099657Sbenno contigfree(vaddr, dmat->maxsize, M_DEVBUF); 24199657Sbenno} 24278342Sbenno 24399657Sbenno/* 24499657Sbenno * Map the buffer buf into bus space using the dmamap map. 24599657Sbenno */ 24699657Sbennoint 24799657Sbennobus_dmamap_load(bus_dma_tag_t dmat, bus_dmamap_t map, void *buf, 24899657Sbenno bus_size_t buflen, bus_dmamap_callback_t *callback, 24999657Sbenno void *callback_arg, int flags) 25099657Sbenno{ 25199657Sbenno vm_offset_t vaddr; 25299657Sbenno vm_offset_t paddr; 25399657Sbenno#ifdef __GNUC__ 25499657Sbenno bus_dma_segment_t dm_segments[dmat->nsegments]; 25599657Sbenno#else 25699657Sbenno bus_dma_segment_t dm_segments[BUS_DMAMAP_NSEGS]; 25799657Sbenno#endif 25899657Sbenno bus_dma_segment_t *sg; 25999657Sbenno int seg; 26099657Sbenno int error = 0; 26199657Sbenno vm_offset_t nextpaddr; 26299657Sbenno 26399657Sbenno if (map != NULL) 26499657Sbenno panic("bus_dmamap_load: Invalid map\n"); 26599657Sbenno 26699657Sbenno vaddr = (vm_offset_t)buf; 26799657Sbenno sg = &dm_segments[0]; 26899657Sbenno seg = 1; 26999657Sbenno sg->ds_len = 0; 27099657Sbenno nextpaddr = 0; 27199657Sbenno 27299657Sbenno do { 27399657Sbenno bus_size_t size; 27499657Sbenno 27599657Sbenno paddr = pmap_kextract(vaddr); 27699657Sbenno size = PAGE_SIZE - (paddr & PAGE_MASK); 27799657Sbenno if (size > buflen) 27899657Sbenno size = buflen; 27999657Sbenno 28099657Sbenno if (sg->ds_len == 0) { 28199657Sbenno sg->ds_addr = paddr; 28299657Sbenno sg->ds_len = size; 28399657Sbenno } else if (paddr == nextpaddr) { 28499657Sbenno sg->ds_len += size; 28599657Sbenno } else { 28699657Sbenno /* Go to the next segment */ 28799657Sbenno sg++; 28899657Sbenno seg++; 28999657Sbenno if (seg > dmat->nsegments) 29099657Sbenno break; 29199657Sbenno sg->ds_addr = paddr; 29299657Sbenno sg->ds_len = size; 29399657Sbenno } 29499657Sbenno vaddr += size; 29599657Sbenno nextpaddr = paddr + size; 29699657Sbenno buflen -= size; 29799657Sbenno 29899657Sbenno } while (buflen > 0); 29999657Sbenno 30099657Sbenno if (buflen != 0) { 30199657Sbenno printf("bus_dmamap_load: Too many segs! buf_len = 0x%lx\n", 30299657Sbenno (u_long)buflen); 30399657Sbenno error = EFBIG; 30499657Sbenno } 30599657Sbenno 30699657Sbenno (*callback)(callback_arg, dm_segments, seg, error); 30799657Sbenno 30899657Sbenno return (0); 30978342Sbenno} 31099657Sbenno 31199657Sbenno/* 31299657Sbenno * Release the mapping held by map. 31399657Sbenno */ 31499657Sbennovoid 31599657Sbennobus_dmamap_unload(bus_dma_tag_t dmat, bus_dmamap_t map) 31699657Sbenno{} 31799657Sbenno 31899657Sbennovoid 31999657Sbennobus_dmamap_sync(bus_dma_tag_t dmat, bus_dmamap_t map, bus_dmasync_op_t op) 32099657Sbenno{} 32199657Sbenno 32299657Sbenno 32399657Sbenno 32499657Sbenno 32599657Sbenno 326