ata-dma.c revision 119404
110099Sjkh/*- 213884Sache * Copyright (c) 1998 - 2003 S�ren Schmidt <sos@FreeBSD.org> 310099Sjkh * All rights reserved. 413832Sache * 513884Sache * Redistribution and use in source and binary forms, with or without 613985Sache * modification, are permitted provided that the following conditions 713985Sache * are met: 813985Sache * 1. Redistributions of source code must retain the above copyright 913985Sache * notice, this list of conditions and the following disclaimer, 1013985Sache * without modification, immediately at the beginning of the file. 1113985Sache * 2. Redistributions in binary form must reproduce the above copyright 1213985Sache * notice, this list of conditions and the following disclaimer in the 1313985Sache * documentation and/or other materials provided with the distribution. 1413985Sache * 3. The name of the author may not be used to endorse or promote products 1513985Sache * derived from this software without specific prior written permission. 1613985Sache * 1713985Sache * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 1813985Sache * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 1910099Sjkh * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 2013985Sache * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 2129103Scharnier * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 2229103Scharnier * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 2350479Speter * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 2429103Scharnier * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 2529103Scharnier * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 2677168Skris * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 2777168Skris * 2877168Skris * $FreeBSD: head/sys/dev/ata/ata-dma.c 119404 2003-08-24 09:22:26Z sos $ 2977168Skris */ 3013985Sache 3129103Scharnier#include <sys/param.h> 3229103Scharnier#include <sys/systm.h> 3377168Skris#include <sys/ata.h> 3469793Sobrien#include <sys/kernel.h> 3510099Sjkh#include <sys/endian.h> 3610099Sjkh#include <sys/malloc.h> 3710099Sjkh#include <sys/lock.h> 3810099Sjkh#include <sys/mutex.h> 3977168Skris#include <sys/taskqueue.h> 4010099Sjkh#include <sys/bus.h> 4113884Sache#include <machine/bus.h> 4210099Sjkh#include <sys/rman.h> 4313985Sache#include <dev/pci/pcivar.h> 4413985Sache#include <dev/ata/ata-all.h> 4513985Sache#include <dev/ata/ata-pci.h> 4613985Sache 4713985Sache/* prototypes */ 4813985Sachestatic void ata_dmasetupc_cb(void *, bus_dma_segment_t *, int, int); 4910099Sjkhstatic int ata_dmaalloc(struct ata_channel *); 5013985Sachestatic void ata_dmafree(struct ata_channel *); 5113985Sachestatic void ata_dmasetupd_cb(void *, bus_dma_segment_t *, int, int); 5213985Sachestatic int ata_dmasetup(struct ata_device *, caddr_t, int32_t); 5313985Sache 5413985Sache/* local vars */ 5513985Sachestatic MALLOC_DEFINE(M_ATADMA, "ATA DMA", "ATA driver DMA"); 5613985Sache 5713985Sache/* misc defines */ 5813985Sache#define MAXSEGSZ PAGE_SIZE 5913985Sache#define MAXTABSZ PAGE_SIZE 6013985Sache#define MAXCTLDMASZ (2 * (MAXTABSZ + MAXPHYS)) 6113985Sache 6213985Sachestruct ata_dc_cb_args { 6313985Sache bus_addr_t maddr; 6413985Sache int error; 6513985Sache}; 6613985Sache 6713985Sacheint 6813985Sacheata_dmainit(struct ata_channel *ch) 6913985Sache{ 7013985Sache if (!(ch->dma = 7113985Sache malloc(sizeof(struct ata_dma), M_ATADMA, M_NOWAIT | M_ZERO))) 7263091Sjoe return ENOMEM; 7377168Skris ch->dma->alloc = ata_dmaalloc; 7477168Skris ch->dma->free = ata_dmafree; 7532782Sjmz ch->dma->setup = ata_dmasetup; 7632782Sjmz ch->dma->start = ata_dmastart; 7732782Sjmz ch->dma->stop = ata_dmastop; 7813985Sache ch->dma->alignment = 2; 7910099Sjkh ch->dma->max_iosize = 64*1024; 8010099Sjkh return 0; 8187568Smikeh} 8287568Smikeh 8387568Smikeh 8410099Sjkhstatic void 8513985Sacheata_dmasetupc_cb(void *xsc, bus_dma_segment_t *segs, int nsegs, int error) 8613985Sache{ 8713985Sache struct ata_dc_cb_args *cba = (struct ata_dc_cb_args *)xsc; 8813985Sache 8913985Sache if (!(cba->error = error)) 9013985Sache cba->maddr = segs[0].ds_addr; 9177168Skris} 9213985Sache 9313985Sachestatic int 9413985Sacheata_dmaalloc(struct ata_channel *ch) 9513985Sache{ 9613985Sache struct ata_dc_cb_args ccba; 9777168Skris int error; 9813985Sache 9913985Sache if (!ch->dma->dmatag) { 10013985Sache if (bus_dma_tag_create(NULL, 1, 0, 10113985Sache BUS_SPACE_MAXADDR_32BIT, BUS_SPACE_MAXADDR, 10232782Sjmz NULL, NULL, MAXCTLDMASZ, ATA_DMA_ENTRIES, 10313985Sache BUS_SPACE_MAXSIZE_32BIT, 0, NULL, NULL, 10413985Sache &ch->dma->dmatag)) { 10563091Sjoe ata_printf(ch, -1, 10687568Smikeh "WARNING - DMA tag allocation failed, disabling DMA\n"); 10710099Sjkh } 10810099Sjkh } 10913985Sache if (!ch->dma->cdmatag) { 11010099Sjkh if ((error = bus_dma_tag_create(ch->dma->dmatag, 1, PAGE_SIZE, 11113985Sache BUS_SPACE_MAXADDR_32BIT, 11213985Sache BUS_SPACE_MAXADDR, NULL, NULL, 11313985Sache MAXTABSZ, 1, MAXTABSZ, 11413985Sache BUS_DMA_ALLOCNOW, NULL, NULL, 11510099Sjkh &ch->dma->cdmatag))) 11613985Sache return error; 11713985Sache } 11813985Sache if (!ch->dma->ddmatag) { 11913985Sache if ((error = bus_dma_tag_create(ch->dma->dmatag, ch->dma->alignment, 0, 12013985Sache BUS_SPACE_MAXADDR_32BIT, 12113985Sache BUS_SPACE_MAXADDR, NULL, NULL, 12213985Sache MAXPHYS, ATA_DMA_ENTRIES, MAXSEGSZ, 12377168Skris BUS_DMA_ALLOCNOW, NULL, NULL, 12413985Sache &ch->dma->ddmatag))) 12513985Sache return error; 12663091Sjoe } 12713985Sache if (!ch->dma->mdmatab) { 12813985Sache if ((error = bus_dmamem_alloc(ch->dma->cdmatag, 12913985Sache (void **)&ch->dma->dmatab, 0, 13013985Sache &ch->dma->cdmamap))) 13113985Sache return error; 13213985Sache 13313985Sache if ((error = bus_dmamap_load(ch->dma->cdmatag, ch->dma->cdmamap, 13413985Sache ch->dma->dmatab, MAXTABSZ, 13513985Sache ata_dmasetupc_cb, &ccba, 0)) != 0 || 13687568Smikeh ccba.error != 0) { 13787568Smikeh bus_dmamem_free(ch->dma->cdmatag, ch->dma->dmatab,ch->dma->cdmamap); 13887568Smikeh return error; 13987568Smikeh } 14087568Smikeh ch->dma->mdmatab = ccba.maddr; 14187568Smikeh } 14210099Sjkh if (!ch->dma->ddmamap) { 14310099Sjkh if ((error = bus_dmamap_create(ch->dma->ddmatag, 0, 14410099Sjkh &ch->dma->ddmamap)) != 0) 14510099Sjkh return error; 14687568Smikeh } 14787568Smikeh return 0; 14813985Sache} 14910099Sjkh 15010099Sjkhstatic void 15110099Sjkhata_dmafree(struct ata_channel *ch) 15210099Sjkh{ 15313985Sache if (ch->dma->mdmatab) { 15413985Sache bus_dmamap_unload(ch->dma->cdmatag, ch->dma->cdmamap); 15513985Sache bus_dmamem_free(ch->dma->cdmatag, ch->dma->dmatab, ch->dma->cdmamap); 15613985Sache ch->dma->mdmatab = 0; 15713985Sache ch->dma->cdmamap = NULL; 15813985Sache ch->dma->dmatab = NULL; 15913985Sache } 16013985Sache if (ch->dma->ddmamap) { 16110099Sjkh bus_dmamap_destroy(ch->dma->ddmatag, ch->dma->ddmamap); 16210099Sjkh ch->dma->ddmamap = NULL; 16310099Sjkh } 16410099Sjkh if (ch->dma->cdmatag) { 16513985Sache bus_dma_tag_destroy(ch->dma->cdmatag); 16613985Sache ch->dma->cdmatag = NULL; 16710099Sjkh } 16810099Sjkh if (ch->dma->ddmatag) { 16910099Sjkh bus_dma_tag_destroy(ch->dma->ddmatag); 17010099Sjkh ch->dma->ddmatag = NULL; 17143479Sbillf } 17210099Sjkh if (ch->dma->dmatag) { 17310099Sjkh bus_dma_tag_destroy(ch->dma->dmatag); 17410099Sjkh ch->dma->dmatag = NULL; 17587568Smikeh } 17671122Sjoe} 17771122Sjoe 17871122Sjoestruct ata_dmasetup_data_cb_args { 17971122Sjoe struct ata_dmaentry *dmatab; 18071122Sjoe int error; 18171122Sjoe}; 18271122Sjoe 18371122Sjoestatic void 18471122Sjoeata_dmasetupd_cb(void *xsc, bus_dma_segment_t *segs, int nsegs, int error) 18571122Sjoe{ 18671122Sjoe struct ata_dmasetup_data_cb_args *cba = 18710099Sjkh (struct ata_dmasetup_data_cb_args *)xsc; 18810099Sjkh bus_size_t cnt; 18910099Sjkh u_int32_t lastcount; 19010099Sjkh int i, j; 19110099Sjkh 19210099Sjkh cba->error = error; 19310099Sjkh if (error != 0) 19410099Sjkh return; 19510099Sjkh lastcount = j = 0; 19610099Sjkh for (i = 0; i < nsegs; i++) { 19710099Sjkh /* 19810099Sjkh * A maximum segment size was specified for bus_dma_tag_create, but 19910099Sjkh * some busdma code does not seem to honor this, so fix up if needed. 20010099Sjkh */ 20110099Sjkh for (cnt = 0; cnt < segs[i].ds_len; cnt += MAXSEGSZ, j++) { 20210099Sjkh cba->dmatab[j].base = htole32(segs[i].ds_addr + cnt); 20310099Sjkh lastcount = ulmin(segs[i].ds_len - cnt, MAXSEGSZ) & 0xffff; 20410099Sjkh cba->dmatab[j].count = htole32(lastcount); 20510099Sjkh } 20610099Sjkh } 20710099Sjkh cba->dmatab[j - 1].count = htole32(lastcount | ATA_DMA_EOT); 20810099Sjkh} 20910099Sjkh 21010099Sjkhstatic int 21110099Sjkhata_dmasetup(struct ata_device *atadev, caddr_t data, int32_t count) 21210099Sjkh{ 21310099Sjkh struct ata_channel *ch = atadev->channel; 21413985Sache 21510099Sjkh if (((uintptr_t)data & (ch->dma->alignment - 1)) || 21610099Sjkh (count & (ch->dma->alignment - 1))) { 21710099Sjkh ata_prtdev(atadev, "FAILURE - non aligned DMA transfer attempted\n"); 21870149Sdes return -1; 21970149Sdes } 22070149Sdes if (!count) { 22175324Sjoe ata_prtdev(atadev, "FAILURE - zero length DMA transfer attempted\n"); 22275324Sjoe return -1; 22375324Sjoe } 22475324Sjoe if (count > ch->dma->max_iosize) { 22575324Sjoe ata_prtdev(atadev, 22675324Sjoe "FAILURE - oversized DMA transfer attempted %d > %d\n", 22775324Sjoe count, ch->dma->max_iosize); 22875324Sjoe return -1; 22975324Sjoe } 23070149Sdes return 0; 23113985Sache} 23229103Scharnier 23310099Sjkhint 23410099Sjkhata_dmastart(struct ata_channel *ch, caddr_t data, int32_t count, int dir) 23510099Sjkh{ 23610099Sjkh struct ata_dmasetup_data_cb_args cba; 23710099Sjkh 23810099Sjkh if (ch->dma->flags & ATA_DMA_ACTIVE) 23913985Sache panic("ata_dmasetup: transfer active on this device!"); 24010099Sjkh 24113985Sache cba.dmatab = ch->dma->dmatab; 24210099Sjkh bus_dmamap_sync(ch->dma->cdmatag, ch->dma->cdmamap, BUS_DMASYNC_PREWRITE); 24310099Sjkh if (bus_dmamap_load(ch->dma->ddmatag, ch->dma->ddmamap, data, count, 24413985Sache ata_dmasetupd_cb, &cba, 0) || cba.error) 24510099Sjkh return -1; 24610099Sjkh 24713985Sache bus_dmamap_sync(ch->dma->ddmatag, ch->dma->ddmamap, 24810099Sjkh dir ? BUS_DMASYNC_PREREAD : BUS_DMASYNC_PREWRITE); 24910099Sjkh 25010099Sjkh ch->dma->flags = dir ? (ATA_DMA_ACTIVE | ATA_DMA_READ) : ATA_DMA_ACTIVE; 25110099Sjkh return 0; 25210099Sjkh} 25313985Sache 25410099Sjkhint 25510099Sjkhata_dmastop(struct ata_channel *ch) 25610099Sjkh{ 25710099Sjkh bus_dmamap_sync(ch->dma->cdmatag, ch->dma->cdmamap, BUS_DMASYNC_POSTWRITE); 25813985Sache 25910099Sjkh bus_dmamap_sync(ch->dma->ddmatag, ch->dma->ddmamap, 26013985Sache (ch->dma->flags & ATA_DMA_READ) != 0 ? 26110099Sjkh BUS_DMASYNC_POSTREAD : BUS_DMASYNC_POSTWRITE); 26210099Sjkh bus_dmamap_unload(ch->dma->ddmatag, ch->dma->ddmamap); 26310099Sjkh 26410099Sjkh ch->dma->flags = 0; 26510099Sjkh return 0; 26610099Sjkh} 26710099Sjkh