1/* $NetBSD: fwdma.c,v 1.14 2010/04/29 06:56:00 kiyohara Exp $ */ 2/*- 3 * Copyright (c) 2003 4 * Hidetoshi Shimokawa. All rights reserved. 5 * 6 * Redistribution and use in source and binary forms, with or without 7 * modification, are permitted provided that the following conditions 8 * are met: 9 * 1. Redistributions of source code must retain the above copyright 10 * notice, this list of conditions and the following disclaimer. 11 * 2. Redistributions in binary form must reproduce the above copyright 12 * notice, this list of conditions and the following disclaimer in the 13 * documentation and/or other materials provided with the distribution. 14 * 3. All advertising materials mentioning features or use of this software 15 * must display the following acknowledgement: 16 * 17 * This product includes software developed by Hidetoshi Shimokawa. 18 * 19 * 4. Neither the name of the author nor the names of its contributors 20 * may be used to endorse or promote products derived from this software 21 * without specific prior written permission. 22 * 23 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 24 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 25 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 26 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 27 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 28 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 29 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 30 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 31 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 32 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 33 * SUCH DAMAGE. 34 * 35 */ 36 37#include <sys/cdefs.h> 38__KERNEL_RCSID(0, "$NetBSD: fwdma.c,v 1.14 2010/04/29 06:56:00 kiyohara Exp $"); 39#if defined(__FreeBSD__) 40__FBSDID("$FreeBSD: src/sys/dev/firewire/fwdma.c,v 1.9 2007/06/06 14:31:36 simokawa Exp $"); 41#endif 42 43#include <sys/param.h> 44#include <sys/bus.h> 45#include <sys/device.h> 46#include <sys/systm.h> 47#include <sys/types.h> 48#include <sys/kernel.h> 49#include <sys/malloc.h> 50#include <sys/select.h> 51 52#include <machine/vmparam.h> 53 54#include <dev/ieee1394/firewire.h> 55#include <dev/ieee1394/firewirereg.h> 56#include <dev/ieee1394/fwdma.h> 57 58#define BUS_SPACE_MAXSIZE_32BIT 0xffffffff 59 60 61void * 62fwdma_malloc(device_t dev, bus_dma_tag_t dmat, bus_dmamap_t *dmamap, 63 bus_size_t size, int alignment, int flags) 64{ 65 bus_dma_segment_t segs; 66 int nsegs; 67 int err; 68 void *v_addr; 69 70 err = bus_dmamem_alloc(dmat, size, alignment, 0, &segs, 1, 71 &nsegs, flags); 72 if (err) { 73 aprint_error_dev(dev, "DMA memory allocation failed %d\n", err); 74 return NULL; 75 } 76 77 err = bus_dmamem_map(dmat, &segs, nsegs, size, &v_addr, flags); 78 if (err) { 79 aprint_error_dev(dev, "DMA memory map failed %d\n", err); 80 bus_dmamem_free(dmat, &segs, nsegs); 81 return NULL; 82 } 83 84 if (*dmamap == NULL) { 85 err = bus_dmamap_create(dmat, size, nsegs, 86 BUS_SPACE_MAXSIZE_32BIT, 0, flags, dmamap); 87 if (err) { 88 aprint_error_dev(dev, 89 "DMA map create failed %d\n", err); 90 bus_dmamem_unmap(dmat, v_addr, size); 91 bus_dmamem_free(dmat, &segs, nsegs); 92 return NULL; 93 } 94 } 95 96 err = bus_dmamap_load(dmat, *dmamap, v_addr, size, NULL, flags); 97 if (err != 0) { 98 aprint_error_dev(dev, "DMA map load failed %d\n", err); 99 bus_dmamap_destroy(dmat, *dmamap); 100 bus_dmamem_unmap(dmat, v_addr, size); 101 bus_dmamem_free(dmat, &segs, nsegs); 102 return NULL; 103 } 104 105 return v_addr; 106} 107 108void 109fwdma_free(bus_dma_tag_t dmat, bus_dmamap_t dmamap, void *vaddr) 110{ 111 112 bus_dmamap_unload(dmat, dmamap); 113 bus_dmamem_unmap(dmat, vaddr, dmamap->dm_mapsize); 114 bus_dmamem_free(dmat, dmamap->dm_segs, dmamap->dm_nsegs); 115 bus_dmamap_destroy(dmat, dmamap); 116} 117 118 119void * 120fwdma_alloc_setup(device_t dev, bus_dma_tag_t dmat, bus_size_t size, 121 struct fwdma_alloc *dma, int alignment, int flags) 122{ 123 124 dma->v_addr = 125 fwdma_malloc(dev, dmat, &dma->dma_map, size, alignment, flags); 126 if (dma->v_addr != NULL) { 127 dma->dma_tag = dmat; 128 dma->bus_addr = dma->dma_map->dm_segs[0].ds_addr; 129 } 130 return dma->v_addr; 131} 132 133/* 134 * Allocate multisegment dma buffers 135 * each segment size is eqaul to ssize except last segment. 136 */ 137struct fwdma_alloc_multi * 138fwdma_malloc_multiseg(struct firewire_comm *fc, int alignment, int esize, int n, 139 int flags) 140{ 141 struct fwdma_alloc_multi *am; 142 struct fwdma_seg *seg; 143 bus_size_t ssize; 144 size_t size; 145 int nseg; 146 147 if (esize > PAGE_SIZE) { 148 /* round up to PAGE_SIZE */ 149 esize = ssize = roundup2(esize, PAGE_SIZE); 150 nseg = n; 151 } else { 152 /* allocate PAGE_SIZE segment for small elements */ 153 ssize = rounddown(PAGE_SIZE, esize); 154 nseg = howmany(n, ssize / esize); 155 } 156 size = sizeof(struct fwdma_alloc_multi) + 157 sizeof(struct fwdma_seg) * nseg; 158 am = (struct fwdma_alloc_multi *)malloc(size, M_FW, M_WAITOK | M_ZERO); 159 if (am == NULL) { 160 aprint_error_dev(fc->dev, "malloc failed\n"); 161 return NULL; 162 } 163 am->ssize = ssize; 164 am->esize = esize; 165 am->nseg = 0; 166 am->dma_tag = fc->dmat; 167 168 for (seg = am->seg; nseg--; seg++) { 169 seg->v_addr = fwdma_malloc(fc->dev, am->dma_tag, &seg->dma_map, 170 ssize, alignment, flags); 171 if (seg->v_addr == NULL) { 172 aprint_error_dev(fc->dev, "malloc_size failed %d\n", 173 am->nseg); 174 fwdma_free_multiseg(am); 175 return NULL; 176 } 177 seg->bus_addr = seg->dma_map->dm_segs[0].ds_addr; 178 am->nseg++; 179 } 180 return am; 181} 182 183void 184fwdma_free_multiseg(struct fwdma_alloc_multi *am) 185{ 186 struct fwdma_seg *seg; 187 188 for (seg = am->seg; am->nseg--; seg++) 189 fwdma_free(am->dma_tag, seg->dma_map, seg->v_addr); 190 free(am, M_FW); 191} 192