ata-dma.c revision 178114
1/*- 2 * Copyright (c) 1998 - 2008 S�ren Schmidt <sos@FreeBSD.org> 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 1. Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer, 10 * without modification, immediately at the beginning of the file. 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 * 15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 16 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 17 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 18 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 19 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 20 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 21 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 22 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 23 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 24 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 25 */ 26 27#include <sys/cdefs.h> 28__FBSDID("$FreeBSD: head/sys/dev/ata/ata-dma.c 178114 2008-04-11 11:30:27Z sos $"); 29 30#include <sys/param.h> 31#include <sys/systm.h> 32#include <sys/ata.h> 33#include <sys/kernel.h> 34#include <sys/endian.h> 35#include <sys/malloc.h> 36#include <sys/lock.h> 37#include <sys/sema.h> 38#include <sys/taskqueue.h> 39#include <vm/uma.h> 40#include <sys/bus.h> 41#include <machine/bus.h> 42#include <sys/rman.h> 43#include <dev/pci/pcivar.h> 44#include <dev/ata/ata-all.h> 45#include <dev/ata/ata-pci.h> 46 47/* prototypes */ 48static void ata_dmaalloc(device_t dev); 49static void ata_dmafree(device_t dev); 50static void ata_dmasetprd(void *xsc, bus_dma_segment_t *segs, int nsegs, int error); 51static int ata_dmaload(struct ata_request *request, void *addr, int *nsegs); 52static int ata_dmaunload(struct ata_request *request); 53 54/* local vars */ 55static MALLOC_DEFINE(M_ATADMA, "ata_dma", "ATA driver DMA"); 56 57/* misc defines */ 58#define MAXTABSZ PAGE_SIZE 59#define MAXWSPCSZ PAGE_SIZE*2 60 61struct ata_dc_cb_args { 62 bus_addr_t maddr; 63 int error; 64}; 65 66void 67ata_dmainit(device_t dev) 68{ 69 struct ata_channel *ch = device_get_softc(dev); 70 71 ch->dma.alloc = ata_dmaalloc; 72 ch->dma.free = ata_dmafree; 73 ch->dma.setprd = ata_dmasetprd; 74 ch->dma.load = ata_dmaload; 75 ch->dma.unload = ata_dmaunload; 76 ch->dma.alignment = 2; 77 ch->dma.boundary = 65536; 78 ch->dma.segsize = 63536; 79 ch->dma.max_iosize = 128 * DEV_BSIZE; 80 ch->dma.max_address = BUS_SPACE_MAXADDR_32BIT; 81} 82 83static void 84ata_dmasetupc_cb(void *xsc, bus_dma_segment_t *segs, int nsegs, int error) 85{ 86 struct ata_dc_cb_args *dcba = (struct ata_dc_cb_args *)xsc; 87 88 if (!(dcba->error = error)) 89 dcba->maddr = segs[0].ds_addr; 90} 91 92static void 93ata_dmaalloc(device_t dev) 94{ 95 struct ata_channel *ch = device_get_softc(dev); 96 struct ata_dc_cb_args dcba; 97 98 if (bus_dma_tag_create(bus_get_dma_tag(dev), ch->dma.alignment, 0, 99 ch->dma.max_address, BUS_SPACE_MAXADDR, 100 NULL, NULL, ch->dma.max_iosize, 101 ATA_DMA_ENTRIES, ch->dma.segsize, 102 0, NULL, NULL, &ch->dma.dmatag)) 103 goto error; 104 105 if (bus_dma_tag_create(ch->dma.dmatag, PAGE_SIZE, 64 * 1024, 106 ch->dma.max_address, BUS_SPACE_MAXADDR, 107 NULL, NULL, MAXWSPCSZ, 1, MAXWSPCSZ, 108 0, NULL, NULL, &ch->dma.work_tag)) 109 goto error; 110 111 if (bus_dmamem_alloc(ch->dma.work_tag, (void **)&ch->dma.work, 0, 112 &ch->dma.work_map)) 113 goto error; 114 115 if (bus_dmamap_load(ch->dma.work_tag, ch->dma.work_map ,ch->dma.work, 116 MAXWSPCSZ, ata_dmasetupc_cb, &dcba, BUS_DMA_NOWAIT) || 117 dcba.error) { 118 bus_dmamem_free(ch->dma.work_tag, ch->dma.work, ch->dma.work_map); 119 goto error; 120 } 121 ch->dma.work_bus = dcba.maddr; 122 123 return; 124 125error: 126 device_printf(dev, "WARNING - DMA allocation failed, disabling DMA\n"); 127 ata_dmafree(dev); 128} 129 130static void 131ata_dmafree(device_t dev) 132{ 133 struct ata_channel *ch = device_get_softc(dev); 134 135 if (ch->dma.work_bus) { 136 bus_dmamap_unload(ch->dma.work_tag, ch->dma.work_map); 137 bus_dmamem_free(ch->dma.work_tag, ch->dma.work, ch->dma.work_map); 138 ch->dma.work_bus = 0; 139 ch->dma.work_map = NULL; 140 ch->dma.work = NULL; 141 } 142 if (ch->dma.work_tag) { 143 bus_dma_tag_destroy(ch->dma.work_tag); 144 ch->dma.work_tag = NULL; 145 } 146 if (ch->dma.dmatag) { 147 bus_dma_tag_destroy(ch->dma.dmatag); 148 ch->dma.dmatag = NULL; 149 } 150} 151 152static void 153ata_dmasetprd(void *xsc, bus_dma_segment_t *segs, int nsegs, int error) 154{ 155 struct ata_dmasetprd_args *args = xsc; 156 struct ata_dma_prdentry *prd = args->dmatab; 157 int i; 158 159 if ((args->error = error)) 160 return; 161 162 for (i = 0; i < nsegs; i++) { 163 prd[i].addr = htole32(segs[i].ds_addr); 164 prd[i].count = htole32(segs[i].ds_len); 165 } 166 prd[i - 1].count |= htole32(ATA_DMA_EOT); 167 KASSERT(nsegs <= ATA_DMA_ENTRIES, ("too many DMA segment entries\n")); 168 args->nsegs = nsegs; 169} 170 171static int 172ata_dmaload(struct ata_request *request, void *addr, int *entries) 173{ 174 struct ata_channel *ch = device_get_softc(request->parent); 175 struct ata_dc_cb_args dcba; 176 struct ata_dmasetprd_args dspa; 177 int error; 178 179 ATA_DEBUG_RQ(request, "dmaload"); 180 181 if (request->dma.cur_iosize) { 182 device_printf(request->dev, 183 "FAILURE - already active DMA on this device\n"); 184 return EIO; 185 } 186 if (!request->bytecount) { 187 device_printf(request->dev, 188 "FAILURE - zero length DMA transfer attempted\n"); 189 return EIO; 190 } 191 if (((uintptr_t)(request->data) & (ch->dma.alignment - 1)) || 192 (request->bytecount & (ch->dma.alignment - 1))) { 193 device_printf(request->dev, 194 "FAILURE - non aligned DMA transfer attempted\n"); 195 return EIO; 196 } 197 if (request->bytecount > ch->dma.max_iosize) { 198 device_printf(request->dev, 199 "FAILURE - oversized DMA transfer attempt %d > %d\n", 200 request->bytecount, ch->dma.max_iosize); 201 return EIO; 202 } 203 204 if (bus_dmamap_load(request->dma.sg_tag, request->dma.sg_map, 205 request->dma.sg, MAXTABSZ, 206 ata_dmasetupc_cb, &dcba, BUS_DMA_NOWAIT) || dcba.error){ 207 bus_dmamem_free(request->dma.sg_tag, 208 request->dma.sg, request->dma.sg_map); 209 device_printf(request->dev, "FAILURE - load sg\n"); 210 goto error; 211 } 212 request->dma.sg_bus = dcba.maddr; 213 214 if (bus_dmamap_create(request->dma.data_tag, 0, &request->dma.data_map)) { 215 device_printf(request->dev, "FAILURE - create data_map\n"); 216 goto error; 217 } 218 219 if (addr) 220 dspa.dmatab = addr; 221 else 222 dspa.dmatab = request->dma.sg; 223 224 if ((error = bus_dmamap_load(request->dma.data_tag, request->dma.data_map, 225 request->data, request->bytecount, 226 ch->dma.setprd, &dspa, BUS_DMA_NOWAIT)) || 227 (error = dspa.error)) { 228 device_printf(request->dev, "FAILURE - load data\n"); 229 goto error; 230 } 231 232 if (entries) 233 *entries = dspa.nsegs; 234 235 bus_dmamap_sync(request->dma.sg_tag, request->dma.sg_map, 236 BUS_DMASYNC_PREWRITE); 237 238 bus_dmamap_sync(request->dma.data_tag, request->dma.data_map, 239 (request->flags & ATA_R_READ) ? 240 BUS_DMASYNC_PREREAD : BUS_DMASYNC_PREWRITE); 241 242 request->dma.cur_iosize = request->bytecount; 243 244 return 0; 245 246error: 247 ata_dmaunload(request); 248 return EIO; 249} 250 251int 252ata_dmaunload(struct ata_request *request) 253{ 254 ATA_DEBUG_RQ(request, "dmaunload"); 255 256 if (request->dma.cur_iosize) { 257 bus_dmamap_sync(request->dma.sg_tag, request->dma.sg_map, 258 BUS_DMASYNC_POSTWRITE); 259 260 bus_dmamap_sync(request->dma.data_tag, request->dma.data_map, 261 (request->flags & ATA_R_READ) ? 262 BUS_DMASYNC_POSTREAD : BUS_DMASYNC_POSTWRITE); 263 bus_dmamap_unload(request->dma.data_tag, request->dma.data_map); 264 265 request->dma.cur_iosize = 0; 266 } 267 268 if (request->dma.data_map) { 269 bus_dmamap_destroy(request->dma.data_tag, request->dma.data_map); 270 request->dma.data_map = NULL; 271 } 272 273 if (request->dma.sg_bus) { 274 bus_dmamap_unload(request->dma.sg_tag, request->dma.sg_map); 275 bus_dmamem_free(request->dma.sg_tag, request->dma.sg, 276 request->dma.sg_map); 277 request->dma.sg = NULL; 278 request->dma.sg_bus = 0; 279 request->dma.sg_map = NULL; 280 } 281 282 return 0; 283} 284