1 /* $NetBSD: fwdma.c,v 1.11 2007/12/11 11:34:08 lukem 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.11 2007/12/11 11:34:08 lukem 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 #if defined(__FreeBSD__) 44 #include <sys/param.h> 45 #include <sys/systm.h> 46 #include <sys/types.h> 47 #include <sys/kernel.h> 48 #include <sys/conf.h> 49 #include <sys/malloc.h> 50 #if defined(__FreeBSD__) && __FreeBSD_version >= 501102 51 #include <sys/lock.h> 52 #include <sys/mutex.h> 53 #endif 54 55 #include <sys/bus.h> 56 #include <sys/bus.h> 57 58 #ifdef __DragonFly__ 59 #include <bus/firewire/fw_port.h> 60 #include <bus/firewire/firewire.h> 61 #include <bus/firewire/firewirereg.h> 62 #include <bus/firewire/fwdma.h> 63 #else 64 #include <dev/firewire/fw_port.h> 65 #include <dev/firewire/firewire.h> 66 #include <dev/firewire/firewirereg.h> 67 #include <dev/firewire/fwdma.h> 68 #endif 69 #elif defined(__NetBSD__) 70 #include <sys/param.h> 71 #include <sys/device.h> 72 #include <sys/systm.h> 73 #include <sys/types.h> 74 #include <sys/kernel.h> 75 #include <sys/malloc.h> 76 77 #include <sys/bus.h> 78 79 #include <dev/ieee1394/fw_port.h> 80 #include <dev/ieee1394/firewire.h> 81 #include <dev/ieee1394/firewirereg.h> 82 #include <dev/ieee1394/fwdma.h> 83 #endif 84 85 static void 86 fwdma_map_cb(void *arg, bus_dma_segment_t *segs, int nseg, int error) 87 { 88 bus_addr_t *baddr; 89 90 if (error) 91 printf("fwdma_map_cb: error=%d\n", error); 92 baddr = (bus_addr_t *)arg; 93 *baddr = segs->ds_addr; 94 } 95 96 void * 97 fwdma_malloc(struct firewire_comm *fc, int alignment, bus_size_t size, 98 struct fwdma_alloc *dma, int flag) 99 { 100 int err; 101 102 dma->v_addr = NULL; 103 err = fw_bus_dma_tag_create( 104 /*parent*/ fc->dmat, 105 /*alignment*/ alignment, 106 /*boundary*/ 0, 107 /*lowaddr*/ BUS_SPACE_MAXADDR_32BIT, 108 /*highaddr*/ BUS_SPACE_MAXADDR, 109 /*filter*/NULL, /*filterarg*/NULL, 110 /*maxsize*/ size, 111 /*nsegments*/ 1, 112 /*maxsegsz*/ BUS_SPACE_MAXSIZE_32BIT, 113 /*flags*/ BUS_DMA_ALLOCNOW, 114 /*lockfunc*/busdma_lock_mutex, 115 /*lockarg*/FW_GMTX(fc), 116 &dma->fw_dma_tag); 117 if (err) { 118 printf("fwdma_malloc: failed(1)\n"); 119 return(NULL); 120 } 121 122 err = fw_bus_dmamem_alloc(dma->fw_dma_tag, &dma->v_addr, 123 flag, &dma->dma_map); 124 if (err) { 125 printf("fwdma_malloc: failed(2)\n"); 126 fw_bus_dma_tag_destroy(dma->fw_dma_tag); 127 return(NULL); 128 } 129 130 err = fw_bus_dmamap_load(dma->fw_dma_tag, dma->dma_map, dma->v_addr, 131 size, fwdma_map_cb, &dma->bus_addr, flag); 132 if (err != 0) { 133 printf("fwdma_malloc: failed(3)\n"); 134 fw_bus_dmamem_free(dma->fw_dma_tag, dma->v_addr, dma->dma_map); 135 fw_bus_dma_tag_destroy(dma->fw_dma_tag); 136 return(NULL); 137 } 138 139 return(dma->v_addr); 140 } 141 142 void 143 fwdma_free(struct firewire_comm *fc, struct fwdma_alloc *dma) 144 { 145 fw_bus_dmamap_unload(dma->fw_dma_tag, dma->dma_map); 146 fw_bus_dmamem_free(dma->fw_dma_tag, dma->v_addr, dma->dma_map); 147 fw_bus_dma_tag_destroy(dma->fw_dma_tag); 148 } 149 150 151 void * 152 fwdma_malloc_size(fw_bus_dma_tag_t dmat, bus_dmamap_t *dmamap, 153 bus_size_t size, bus_addr_t *bus_addr, int flag) 154 { 155 void *v_addr; 156 157 if (fw_bus_dmamem_alloc(dmat, &v_addr, flag, dmamap)) { 158 printf("fwdma_malloc_size: failed(1)\n"); 159 return(NULL); 160 } 161 if (fw_bus_dmamap_load(dmat, *dmamap, v_addr, size, 162 fwdma_map_cb, bus_addr, flag)) { 163 printf("fwdma_malloc_size: failed(2)\n"); 164 fw_bus_dmamem_free(dmat, v_addr, *dmamap); 165 return(NULL); 166 } 167 return(v_addr); 168 } 169 170 void 171 fwdma_free_size(fw_bus_dma_tag_t dmat, bus_dmamap_t dmamap, void *vaddr, 172 bus_size_t size) 173 { 174 fw_bus_dmamap_unload(dmat, dmamap); 175 fw_bus_dmamem_free(dmat, vaddr, dmamap); 176 } 177 178 /* 179 * Allocate multisegment dma buffers 180 * each segment size is eqaul to ssize except last segment. 181 */ 182 struct fwdma_alloc_multi * 183 fwdma_malloc_multiseg(struct firewire_comm *fc, int alignment, 184 int esize, int n, int flag) 185 { 186 struct fwdma_alloc_multi *am; 187 struct fwdma_seg *seg; 188 bus_size_t ssize; 189 int nseg, size; 190 191 if (esize > PAGE_SIZE) { 192 /* round up to PAGE_SIZE */ 193 esize = ssize = roundup2(esize, PAGE_SIZE); 194 nseg = n; 195 } else { 196 /* allocate PAGE_SIZE segment for small elements */ 197 ssize = rounddown(PAGE_SIZE, esize); 198 nseg = howmany(n, ssize / esize); 199 } 200 size = sizeof (struct fwdma_alloc_multi) + 201 sizeof (struct fwdma_seg) * nseg; 202 am = (struct fwdma_alloc_multi *)malloc(size, M_FW, M_WAITOK); 203 if (am == NULL) { 204 printf("fwdma_malloc_multiseg: malloc failed\n"); 205 return(NULL); 206 } 207 memset(am, 0, size); 208 am->ssize = ssize; 209 am->esize = esize; 210 am->nseg = 0; 211 if (fw_bus_dma_tag_create( 212 /*parent*/ fc->dmat, 213 /*alignment*/ alignment, 214 /*boundary*/ 0, 215 /*lowaddr*/ BUS_SPACE_MAXADDR_32BIT, 216 /*highaddr*/ BUS_SPACE_MAXADDR, 217 /*filter*/NULL, /*filterarg*/NULL, 218 /*maxsize*/ ssize, 219 /*nsegments*/ 1, 220 /*maxsegsz*/ BUS_SPACE_MAXSIZE_32BIT, 221 /*flags*/ BUS_DMA_ALLOCNOW, 222 /*lockfunc*/busdma_lock_mutex, 223 /*lockarg*/FW_GMTX(fc), 224 &am->fw_dma_tag)) { 225 printf("fwdma_malloc_multiseg: tag_create failed\n"); 226 free(am, M_FW); 227 return(NULL); 228 } 229 230 #if 0 231 #if defined(__DragonFly__) || __FreeBSD_version < 500000 232 printf("malloc_multi: ssize=%d nseg=%d\n", ssize, nseg); 233 #else 234 printf("malloc_multi: ssize=%td nseg=%d\n", ssize, nseg); 235 #endif 236 #endif 237 for (seg = &am->seg[0]; nseg --; seg ++) { 238 seg->v_addr = fwdma_malloc_size(am->fw_dma_tag, &seg->dma_map, 239 ssize, &seg->bus_addr, flag); 240 if (seg->v_addr == NULL) { 241 printf("fwdma_malloc_multi: malloc_size failed %d\n", 242 am->nseg); 243 fwdma_free_multiseg(am); 244 return(NULL); 245 } 246 am->nseg++; 247 } 248 return(am); 249 } 250 251 void 252 fwdma_free_multiseg(struct fwdma_alloc_multi *am) 253 { 254 struct fwdma_seg *seg; 255 256 for (seg = &am->seg[0]; am->nseg --; seg ++) { 257 fwdma_free_size(am->fw_dma_tag, seg->dma_map, 258 seg->v_addr, am->ssize); 259 } 260 fw_bus_dma_tag_destroy(am->fw_dma_tag); 261 free(am, M_FW); 262 } 263