ata-dma.c revision 111188
145095Ssos/*- 2111188Ssos * Copyright (c) 1998 - 2003 S�ren Schmidt <sos@FreeBSD.org> 345095Ssos * All rights reserved. 445095Ssos * 545095Ssos * Redistribution and use in source and binary forms, with or without 645095Ssos * modification, are permitted provided that the following conditions 745095Ssos * are met: 845095Ssos * 1. Redistributions of source code must retain the above copyright 945095Ssos * notice, this list of conditions and the following disclaimer, 1045095Ssos * without modification, immediately at the beginning of the file. 1145095Ssos * 2. Redistributions in binary form must reproduce the above copyright 1245095Ssos * notice, this list of conditions and the following disclaimer in the 1345095Ssos * documentation and/or other materials provided with the distribution. 1445095Ssos * 3. The name of the author may not be used to endorse or promote products 1545095Ssos * derived from this software without specific prior written permission. 1645095Ssos * 1745095Ssos * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 1845095Ssos * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 1945095Ssos * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 2045095Ssos * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 2145095Ssos * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 2245095Ssos * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 2345095Ssos * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 2445095Ssos * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 2545095Ssos * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 2645095Ssos * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 2745095Ssos * 2850477Speter * $FreeBSD: head/sys/dev/ata/ata-dma.c 111188 2003-02-20 20:02:32Z sos $ 2945095Ssos */ 3045095Ssos 3145095Ssos#include <sys/param.h> 3245095Ssos#include <sys/systm.h> 3374302Ssos#include <sys/ata.h> 34111188Ssos#include <sys/kernel.h> 3595533Smike#include <sys/endian.h> 3645095Ssos#include <sys/malloc.h> 3745798Ssos#include <sys/bus.h> 3845095Ssos#include <pci/pcivar.h> 3966106Ssos#include <machine/bus.h> 4072106Ssos#include <sys/rman.h> 4145095Ssos#include <dev/ata/ata-all.h> 42111188Ssos#include <dev/ata/ata-pci.h> 4345095Ssos 4452067Ssos/* prototypes */ 45111188Ssosstatic void ata_dmasetupc_cb(void *, bus_dma_segment_t *, int, int); 46111188Ssosstatic int ata_dmaalloc(struct ata_device *); 47111188Ssosstatic void ata_dmafree(struct ata_device *); 48111188Ssosstatic void ata_dmacreate(struct ata_channel *); 49111188Ssosstatic void ata_dmadestroy(struct ata_channel *); 5093882Ssosstatic void ata_dmasetupd_cb(void *, bus_dma_segment_t *, int, int); 51111188Ssosstatic int ata_dmasetup(struct ata_device *, caddr_t, int32_t); 52111188Ssos/* 53111188Ssosstatic int ata_dmastart(struct ata_device *, caddr_t, int32_t, int); 54111188Ssosstatic int ata_dmastop(struct ata_device *); 55111188Ssos*/ 56111188Ssosstatic int ata_dmastatus(struct ata_channel *); 5752067Ssos 58111188Ssos/* local vars */ 59111188Ssosstatic MALLOC_DEFINE(M_ATADMA, "ATA DMA", "ATA driver DMA"); 60111188Ssos 6152067Ssos/* misc defines */ 62111188Ssos#define MAXSEGSZ PAGE_SIZE 63111188Ssos#define MAXTABSZ PAGE_SIZE 64111188Ssos#define MAXCTLDMASZ (2 * (MAXTABSZ + MAXPHYS)) 6545720Speter 6693882Ssosstruct ata_dc_cb_args { 6793882Ssos bus_addr_t maddr; 6893882Ssos int error; 6993882Ssos}; 7093882Ssos 71111188Ssosint 72111188Ssosata_dmainit(struct ata_channel *ch) 73111188Ssos{ 74111188Ssos if (!(ch->dma = 75111188Ssos malloc(sizeof(struct ata_dma_funcs), M_ATADMA, M_NOWAIT | M_ZERO))) 76111188Ssos return ENOMEM; 77111188Ssos ch->dma->create = ata_dmacreate; 78111188Ssos ch->dma->destroy = ata_dmadestroy; 79111188Ssos ch->dma->alloc = ata_dmaalloc; 80111188Ssos ch->dma->free = ata_dmafree; 81111188Ssos ch->dma->setup = ata_dmasetup; 82111188Ssos ch->dma->start = ata_dmastart; 83111188Ssos ch->dma->stop = ata_dmastop; 84111188Ssos ch->dma->status = ata_dmastatus; 85111188Ssos ch->dma->alignment = 2; 86111188Ssos return 0; 87111188Ssos} 88111188Ssos 89111188Ssos 9093882Ssosstatic void 9193882Ssosata_dmasetupc_cb(void *xsc, bus_dma_segment_t *segs, int nsegs, int error) 9266070Ssos{ 9393882Ssos struct ata_dc_cb_args *cba = (struct ata_dc_cb_args *)xsc; 9466070Ssos 9593882Ssos if (!(cba->error = error)) 9693882Ssos cba->maddr = segs[0].ds_addr; 9793882Ssos} 9893882Ssos 99111188Ssosstatic int 10093882Ssosata_dmaalloc(struct ata_device *atadev) 10193882Ssos{ 10293882Ssos struct ata_channel *ch; 10393882Ssos struct ata_dc_cb_args ccba; 10493882Ssos struct ata_dmastate *ds; 10593882Ssos int error; 10693882Ssos 10793882Ssos ch = atadev->channel; 10893882Ssos ds = &atadev->dmastate; 10993882Ssos if (!ds->cdmatag) { 11093882Ssos if ((error = bus_dma_tag_create(ch->dmatag, 1, PAGE_SIZE, 11193882Ssos BUS_SPACE_MAXADDR_32BIT, 11293882Ssos BUS_SPACE_MAXADDR, NULL, NULL, 11393882Ssos MAXTABSZ, 1, MAXTABSZ, 11493882Ssos BUS_DMA_ALLOCNOW, &ds->cdmatag))) 11593882Ssos return error; 11693882Ssos } 11793882Ssos if (!ds->ddmatag) { 118111188Ssos if ((error = bus_dma_tag_create(ch->dmatag, ch->dma->alignment, 0, 11993882Ssos BUS_SPACE_MAXADDR_32BIT, 12093882Ssos BUS_SPACE_MAXADDR, NULL, NULL, 12193882Ssos MAXPHYS, ATA_DMA_ENTRIES, MAXSEGSZ, 12293882Ssos BUS_DMA_ALLOCNOW, &ds->ddmatag))) 12393882Ssos return error; 12493882Ssos } 12593882Ssos if (!ds->mdmatab) { 12693882Ssos if ((error = bus_dmamem_alloc(ds->cdmatag, (void **)&ds->dmatab, 0, 12793882Ssos &ds->cdmamap))) 12893882Ssos return error; 12993882Ssos 13093882Ssos if ((error = bus_dmamap_load(ds->cdmatag, ds->cdmamap, ds->dmatab, 13193882Ssos MAXTABSZ, ata_dmasetupc_cb, &ccba, 13293882Ssos 0)) != 0 || ccba.error != 0) { 13393882Ssos bus_dmamem_free(ds->cdmatag, ds->dmatab, ds->cdmamap); 13493882Ssos return error; 13566070Ssos } 13693882Ssos ds->mdmatab = ccba.maddr; 13766070Ssos } 13893882Ssos if (!ds->ddmamap) { 13993882Ssos if ((error = bus_dmamap_create(ds->ddmatag, 0, &ds->ddmamap)) != 0) 14093882Ssos return error; 14193882Ssos } 14293882Ssos return 0; 14366070Ssos} 14466070Ssos 145111188Ssosstatic void 14693882Ssosata_dmafree(struct ata_device *atadev) 14745095Ssos{ 14893882Ssos struct ata_dmastate *ds; 14993882Ssos 15093882Ssos ds = &atadev->dmastate; 15193882Ssos if (ds->mdmatab) { 15293882Ssos bus_dmamap_unload(ds->cdmatag, ds->cdmamap); 15393882Ssos bus_dmamem_free(ds->cdmatag, ds->dmatab, ds->cdmamap); 15493882Ssos ds->mdmatab = 0; 15593882Ssos ds->cdmamap = NULL; 15693882Ssos ds->dmatab = NULL; 15793882Ssos } 15893882Ssos if (ds->ddmamap) { 15993882Ssos bus_dmamap_destroy(ds->ddmatag, ds->ddmamap); 16093882Ssos ds->ddmamap = NULL; 16193882Ssos } 16293882Ssos if (ds->cdmatag) { 16393882Ssos bus_dma_tag_destroy(ds->cdmatag); 16493882Ssos ds->cdmatag = NULL; 16593882Ssos } 16693882Ssos if (ds->ddmatag) { 16793882Ssos bus_dma_tag_destroy(ds->ddmatag); 16893882Ssos ds->ddmatag = NULL; 16993882Ssos } 17093882Ssos} 17193882Ssos 17293882Ssosstatic void 173111188Ssosata_dmacreate(struct ata_channel *ch) 17493882Ssos{ 17593882Ssos 176111188Ssos if (!ch->dmatag) { 17793882Ssos if (bus_dma_tag_create(NULL, 1, 0, 17893882Ssos BUS_SPACE_MAXADDR_32BIT, BUS_SPACE_MAXADDR, 17993882Ssos NULL, NULL, MAXCTLDMASZ, ATA_DMA_ENTRIES, 180111188Ssos BUS_SPACE_MAXSIZE_32BIT, 0, &ch->dmatag)) { 181111188Ssos printf("DMA tag allocation failed, disabling DMA\n"); 18293882Ssos } 18393882Ssos } 18493882Ssos} 18593882Ssos 186111188Ssosstatic void 187111188Ssosata_dmadestroy(struct ata_channel *ch) 18893882Ssos{ 18945095Ssos 190111188Ssos if (ch->dmatag) { 191111188Ssos bus_dma_tag_destroy(ch->dmatag); 192111188Ssos ch->dmatag = NULL; 19352067Ssos } 19445095Ssos} 19545095Ssos 19693882Ssosstruct ata_dmasetup_data_cb_args { 19793882Ssos struct ata_dmaentry *dmatab; 19893882Ssos int error; 19993882Ssos}; 20093882Ssos 20193882Ssosstatic void 20293882Ssosata_dmasetupd_cb(void *xsc, bus_dma_segment_t *segs, int nsegs, int error) 20393882Ssos{ 20493882Ssos struct ata_dmasetup_data_cb_args *cba = 20593882Ssos (struct ata_dmasetup_data_cb_args *)xsc; 20693882Ssos bus_size_t cnt; 20793882Ssos u_int32_t lastcount; 20893882Ssos int i, j; 20993882Ssos 21093882Ssos cba->error = error; 21193882Ssos if (error != 0) 21293882Ssos return; 21393882Ssos lastcount = j = 0; 21493882Ssos for (i = 0; i < nsegs; i++) { 21593882Ssos /* 21693882Ssos * A maximum segment size was specified for bus_dma_tag_create, but 21793882Ssos * some busdma code does not seem to honor this, so fix up if needed. 21893882Ssos */ 21993882Ssos for (cnt = 0; cnt < segs[i].ds_len; cnt += MAXSEGSZ, j++) { 22093882Ssos cba->dmatab[j].base = htole32(segs[i].ds_addr + cnt); 22193882Ssos lastcount = ulmin(segs[i].ds_len - cnt, MAXSEGSZ) & 0xffff; 22293882Ssos cba->dmatab[j].count = htole32(lastcount); 22393882Ssos } 22493882Ssos } 22593882Ssos cba->dmatab[j - 1].count = htole32(lastcount | ATA_DMA_EOT); 22693882Ssos} 22793882Ssos 228111188Ssosstatic int 22993882Ssosata_dmasetup(struct ata_device *atadev, caddr_t data, int32_t count) 23045095Ssos{ 23193882Ssos struct ata_channel *ch = atadev->channel; 23245095Ssos 233111188Ssos if (((uintptr_t)data & (ch->dma->alignment - 1)) || 234111188Ssos (count & (ch->dma->alignment - 1))) { 23593882Ssos ata_prtdev(atadev, "non aligned DMA transfer attempted\n"); 23645095Ssos return -1; 23766070Ssos } 23845095Ssos 23945095Ssos if (!count) { 24093882Ssos ata_prtdev(atadev, "zero length DMA transfer attempted\n"); 24145095Ssos return -1; 24245095Ssos } 24395010Ssos return 0; 24495010Ssos} 24545095Ssos 24695010Ssosint 24795010Ssosata_dmastart(struct ata_device *atadev, caddr_t data, int32_t count, int dir) 24895010Ssos{ 24995010Ssos struct ata_channel *ch = atadev->channel; 25095010Ssos struct ata_dmastate *ds = &atadev->dmastate; 25195010Ssos struct ata_dmasetup_data_cb_args cba; 25295010Ssos 25395010Ssos if (ds->flags & ATA_DS_ACTIVE) 25495010Ssos panic("ata_dmasetup: transfer active on this device!"); 25595010Ssos 25693882Ssos cba.dmatab = ds->dmatab; 25795010Ssos bus_dmamap_sync(ds->cdmatag, ds->cdmamap, BUS_DMASYNC_PREWRITE); 25893882Ssos if (bus_dmamap_load(ds->ddmatag, ds->ddmamap, data, count, 25993882Ssos ata_dmasetupd_cb, &cba, 0) || cba.error) 26095010Ssos return -1; 26193882Ssos 26295010Ssos bus_dmamap_sync(ds->cdmatag, ds->cdmamap, BUS_DMASYNC_POSTWRITE); 26393882Ssos bus_dmamap_sync(ds->ddmatag, ds->ddmamap, dir ? BUS_DMASYNC_PREREAD : 26493882Ssos BUS_DMASYNC_PREWRITE); 26595010Ssos 26695010Ssos ch->flags |= ATA_DMA_ACTIVE; 267108931Ssos ds->flags = dir ? (ATA_DS_ACTIVE | ATA_DS_READ) : ATA_DS_ACTIVE; 26895010Ssos 26993882Ssos ATA_OUTL(ch->r_bmio, ATA_BMDTP_PORT, ds->mdmatab); 27090215Ssos ATA_OUTB(ch->r_bmio, ATA_BMCMD_PORT, dir ? ATA_BMCMD_WRITE_READ : 0); 27190215Ssos ATA_OUTB(ch->r_bmio, ATA_BMSTAT_PORT, 272108931Ssos (ATA_INB(ch->r_bmio, ATA_BMSTAT_PORT) | 273108931Ssos (ATA_BMSTAT_INTERRUPT | ATA_BMSTAT_ERROR))); 27490215Ssos ATA_OUTB(ch->r_bmio, ATA_BMCMD_PORT, 275108931Ssos ATA_INB(ch->r_bmio, ATA_BMCMD_PORT) | ATA_BMCMD_START_STOP); 27695010Ssos return 0; 27745095Ssos} 27845095Ssos 27966070Ssosint 280111188Ssosata_dmastop(struct ata_device *atadev) 28145095Ssos{ 282108931Ssos struct ata_channel *ch = atadev->channel; 283108931Ssos struct ata_dmastate *ds = &atadev->dmastate; 28472106Ssos int error; 28572106Ssos 286109529Ssos error = ATA_INB(ch->r_bmio, ATA_BMSTAT_PORT); 287109529Ssos ATA_OUTB(ch->r_bmio, ATA_BMCMD_PORT, 288109529Ssos ATA_INB(ch->r_bmio, ATA_BMCMD_PORT) & ~ATA_BMCMD_START_STOP); 289109529Ssos ATA_OUTB(ch->r_bmio, ATA_BMSTAT_PORT,ATA_BMSTAT_INTERRUPT|ATA_BMSTAT_ERROR); 290109529Ssos 29193882Ssos bus_dmamap_sync(ds->ddmatag, ds->ddmamap, (ds->flags & ATA_DS_READ) != 0 ? 29293882Ssos BUS_DMASYNC_POSTREAD : BUS_DMASYNC_POSTWRITE); 29393882Ssos bus_dmamap_unload(ds->ddmatag, ds->ddmamap); 294109529Ssos 29595010Ssos ch->flags &= ~ATA_DMA_ACTIVE; 29693882Ssos ds->flags = 0; 29795010Ssos return (error & ATA_BMSTAT_MASK); 29845095Ssos} 29945095Ssos 300111188Ssosstatic int 30190215Ssosata_dmastatus(struct ata_channel *ch) 30245095Ssos{ 30390215Ssos return ATA_INB(ch->r_bmio, ATA_BMSTAT_PORT) & ATA_BMSTAT_MASK; 30445095Ssos} 305