ata_dbdma.c revision 249213
1183409Snwhitehorn/*- 2183409Snwhitehorn * Copyright 2008 by Nathan Whitehorn. All rights reserved. 3183409Snwhitehorn * 4183409Snwhitehorn * Redistribution and use in source and binary forms, with or without 5183409Snwhitehorn * modification, are permitted provided that the following conditions 6183409Snwhitehorn * are met: 7183409Snwhitehorn * 1. Redistributions of source code must retain the above copyright 8183409Snwhitehorn * notice, this list of conditions and the following disclaimer. 9183409Snwhitehorn * 2. Redistributions in binary form must reproduce the above copyright 10183409Snwhitehorn * notice, this list of conditions and the following disclaimer in the 11183409Snwhitehorn * documentation and/or other materials provided with the distribution. 12183409Snwhitehorn * 3. The name of the author may not be used to endorse or promote products 13183409Snwhitehorn * derived from this software without specific prior written permission. 14183409Snwhitehorn * 15183409Snwhitehorn * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 16183409Snwhitehorn * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 17183409Snwhitehorn * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 18183409Snwhitehorn * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 19183409Snwhitehorn * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 20183409Snwhitehorn * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 21183409Snwhitehorn * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED 22183409Snwhitehorn * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 23183409Snwhitehorn * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 24183409Snwhitehorn * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 25183409Snwhitehorn * SUCH DAMAGE. 26183409Snwhitehorn */ 27183409Snwhitehorn 28249213Smarius#include <sys/cdefs.h> 29249213Smarius__FBSDID("* $FreeBSD: head/sys/powerpc/powermac/ata_dbdma.c 249213 2013-04-06 19:12:49Z marius $"); 30249213Smarius 31183409Snwhitehorn/* 32183409Snwhitehorn * Common routines for the DMA engine on both the Apple Kauai and MacIO 33183409Snwhitehorn * ATA controllers. 34183409Snwhitehorn */ 35183409Snwhitehorn 36183409Snwhitehorn#include <sys/param.h> 37183409Snwhitehorn#include <sys/systm.h> 38183409Snwhitehorn#include <sys/kernel.h> 39183409Snwhitehorn#include <sys/module.h> 40183409Snwhitehorn#include <sys/bus.h> 41183409Snwhitehorn#include <sys/malloc.h> 42183409Snwhitehorn#include <sys/sema.h> 43183409Snwhitehorn#include <sys/taskqueue.h> 44183409Snwhitehorn#include <vm/uma.h> 45183409Snwhitehorn#include <machine/stdarg.h> 46183409Snwhitehorn#include <machine/resource.h> 47183409Snwhitehorn#include <machine/bus.h> 48183409Snwhitehorn#include <sys/rman.h> 49183409Snwhitehorn#include <sys/ata.h> 50183409Snwhitehorn#include <dev/ata/ata-all.h> 51183409Snwhitehorn#include <dev/ata/ata-pci.h> 52183409Snwhitehorn#include <ata_if.h> 53183409Snwhitehorn 54183409Snwhitehorn#include "ata_dbdma.h" 55183409Snwhitehorn 56183409Snwhitehornstruct ata_dbdma_dmaload_args { 57183409Snwhitehorn struct ata_dbdma_channel *sc; 58183409Snwhitehorn 59183409Snwhitehorn int write; 60183409Snwhitehorn int nsegs; 61183409Snwhitehorn}; 62183409Snwhitehorn 63183409Snwhitehornstatic void 64183409Snwhitehornata_dbdma_setprd(void *xarg, bus_dma_segment_t *segs, int nsegs, int error) 65183409Snwhitehorn{ 66183409Snwhitehorn struct ata_dbdma_dmaload_args *arg = xarg; 67183409Snwhitehorn struct ata_dbdma_channel *sc = arg->sc; 68183409Snwhitehorn int branch_type, command; 69183409Snwhitehorn int prev_stop; 70183409Snwhitehorn int i; 71183409Snwhitehorn 72183409Snwhitehorn mtx_lock(&sc->dbdma_mtx); 73183409Snwhitehorn 74183409Snwhitehorn prev_stop = sc->next_dma_slot-1; 75183409Snwhitehorn if (prev_stop < 0) 76183409Snwhitehorn prev_stop = 0xff; 77183409Snwhitehorn 78183409Snwhitehorn for (i = 0; i < nsegs; i++) { 79183409Snwhitehorn /* Loop back to the beginning if this is our last slot */ 80183409Snwhitehorn if (sc->next_dma_slot == 0xff) 81183409Snwhitehorn branch_type = DBDMA_ALWAYS; 82183409Snwhitehorn else 83183409Snwhitehorn branch_type = DBDMA_NEVER; 84183409Snwhitehorn 85183409Snwhitehorn if (arg->write) { 86183409Snwhitehorn command = (i + 1 < nsegs) ? DBDMA_OUTPUT_MORE : 87183409Snwhitehorn DBDMA_OUTPUT_LAST; 88183409Snwhitehorn } else { 89183409Snwhitehorn command = (i + 1 < nsegs) ? DBDMA_INPUT_MORE : 90183409Snwhitehorn DBDMA_INPUT_LAST; 91183409Snwhitehorn } 92183409Snwhitehorn 93183409Snwhitehorn dbdma_insert_command(sc->dbdma, sc->next_dma_slot++, 94183409Snwhitehorn command, 0, segs[i].ds_addr, segs[i].ds_len, 95183409Snwhitehorn DBDMA_NEVER, branch_type, DBDMA_NEVER, 0); 96183409Snwhitehorn 97183409Snwhitehorn if (branch_type == DBDMA_ALWAYS) 98183409Snwhitehorn sc->next_dma_slot = 0; 99183409Snwhitehorn } 100183409Snwhitehorn 101183409Snwhitehorn /* We have a corner case where the STOP command is the last slot, 102183409Snwhitehorn * but you can't branch in STOP commands. So add a NOP branch here 103183409Snwhitehorn * and the STOP in slot 0. */ 104183409Snwhitehorn 105183409Snwhitehorn if (sc->next_dma_slot == 0xff) { 106183409Snwhitehorn dbdma_insert_branch(sc->dbdma, sc->next_dma_slot, 0); 107183409Snwhitehorn sc->next_dma_slot = 0; 108183409Snwhitehorn } 109183409Snwhitehorn 110183409Snwhitehorn#if 0 111183409Snwhitehorn dbdma_insert_command(sc->dbdma, sc->next_dma_slot++, 112183409Snwhitehorn DBDMA_NOP, 0, 0, 0, DBDMA_ALWAYS, DBDMA_NEVER, DBDMA_NEVER, 0); 113183409Snwhitehorn#endif 114183409Snwhitehorn dbdma_insert_stop(sc->dbdma, sc->next_dma_slot++); 115183409Snwhitehorn dbdma_insert_nop(sc->dbdma, prev_stop); 116183409Snwhitehorn 117183409Snwhitehorn dbdma_sync_commands(sc->dbdma, BUS_DMASYNC_PREWRITE); 118183409Snwhitehorn 119183409Snwhitehorn mtx_unlock(&sc->dbdma_mtx); 120183409Snwhitehorn 121183409Snwhitehorn arg->nsegs = nsegs; 122183409Snwhitehorn} 123183409Snwhitehorn 124183409Snwhitehornstatic int 125183409Snwhitehornata_dbdma_status(device_t dev) 126183409Snwhitehorn{ 127183409Snwhitehorn struct ata_dbdma_channel *sc = device_get_softc(dev); 128183409Snwhitehorn struct ata_channel *ch = device_get_softc(dev); 129183409Snwhitehorn 130183409Snwhitehorn if (sc->sc_ch.dma.flags & ATA_DMA_ACTIVE) { 131183409Snwhitehorn return (!(dbdma_get_chan_status(sc->dbdma) & 132183409Snwhitehorn DBDMA_STATUS_ACTIVE)); 133183409Snwhitehorn } 134183409Snwhitehorn 135183409Snwhitehorn if (ATA_IDX_INB(ch, ATA_ALTSTAT) & ATA_S_BUSY) { 136183409Snwhitehorn DELAY(100); 137183409Snwhitehorn if (ATA_IDX_INB(ch, ATA_ALTSTAT) & ATA_S_BUSY) 138183409Snwhitehorn return 0; 139183409Snwhitehorn } 140183409Snwhitehorn return 1; 141183409Snwhitehorn} 142183409Snwhitehorn 143183409Snwhitehornstatic int 144183409Snwhitehornata_dbdma_start(struct ata_request *request) 145183409Snwhitehorn{ 146183409Snwhitehorn struct ata_dbdma_channel *sc = device_get_softc(request->parent); 147183409Snwhitehorn 148183409Snwhitehorn sc->sc_ch.dma.flags |= ATA_DMA_ACTIVE; 149183409Snwhitehorn dbdma_wake(sc->dbdma); 150183409Snwhitehorn return 0; 151183409Snwhitehorn} 152183409Snwhitehorn 153183409Snwhitehornstatic void 154183409Snwhitehornata_dbdma_reset(device_t dev) 155183409Snwhitehorn{ 156183409Snwhitehorn struct ata_dbdma_channel *sc = device_get_softc(dev); 157183409Snwhitehorn 158183409Snwhitehorn mtx_lock(&sc->dbdma_mtx); 159183409Snwhitehorn 160183409Snwhitehorn dbdma_stop(sc->dbdma); 161183409Snwhitehorn dbdma_insert_stop(sc->dbdma, 0); 162183409Snwhitehorn sc->next_dma_slot=1; 163183409Snwhitehorn dbdma_set_current_cmd(sc->dbdma, 0); 164183409Snwhitehorn 165183409Snwhitehorn sc->sc_ch.dma.flags &= ~ATA_DMA_ACTIVE; 166183409Snwhitehorn 167183409Snwhitehorn mtx_unlock(&sc->dbdma_mtx); 168183409Snwhitehorn} 169183409Snwhitehorn 170183409Snwhitehornstatic int 171183409Snwhitehornata_dbdma_stop(struct ata_request *request) 172183409Snwhitehorn{ 173183409Snwhitehorn struct ata_dbdma_channel *sc = device_get_softc(request->parent); 174183409Snwhitehorn 175183409Snwhitehorn uint16_t status; 176183409Snwhitehorn 177183409Snwhitehorn status = dbdma_get_chan_status(sc->dbdma); 178183409Snwhitehorn 179183409Snwhitehorn dbdma_pause(sc->dbdma); 180183409Snwhitehorn sc->sc_ch.dma.flags &= ~ATA_DMA_ACTIVE; 181183409Snwhitehorn 182183409Snwhitehorn if (status & DBDMA_STATUS_DEAD) { 183183409Snwhitehorn device_printf(request->parent,"DBDMA dead, resetting " 184183409Snwhitehorn "channel...\n"); 185183409Snwhitehorn ata_dbdma_reset(request->parent); 186183409Snwhitehorn return ATA_S_ERROR; 187183409Snwhitehorn } 188183409Snwhitehorn 189183409Snwhitehorn if (!(status & DBDMA_STATUS_RUN)) { 190183409Snwhitehorn device_printf(request->parent,"DBDMA confused, stop called " 191183409Snwhitehorn "when channel is not running!\n"); 192183409Snwhitehorn return ATA_S_ERROR; 193183409Snwhitehorn } 194183409Snwhitehorn 195183409Snwhitehorn if (status & DBDMA_STATUS_ACTIVE) { 196183409Snwhitehorn device_printf(request->parent,"DBDMA channel stopped " 197183409Snwhitehorn "prematurely\n"); 198183409Snwhitehorn return ATA_S_ERROR; 199183409Snwhitehorn } 200183409Snwhitehorn return 0; 201183409Snwhitehorn} 202183409Snwhitehorn 203183409Snwhitehornstatic int 204183409Snwhitehornata_dbdma_load(struct ata_request *request, void *addr, int *entries) 205183409Snwhitehorn{ 206183409Snwhitehorn struct ata_channel *ch = device_get_softc(request->parent); 207183409Snwhitehorn struct ata_dbdma_dmaload_args args; 208183409Snwhitehorn 209183409Snwhitehorn int error; 210183409Snwhitehorn 211183409Snwhitehorn args.sc = device_get_softc(request->parent); 212183409Snwhitehorn args.write = !(request->flags & ATA_R_READ); 213183409Snwhitehorn 214183409Snwhitehorn if (!request->bytecount) { 215183409Snwhitehorn device_printf(request->dev, 216183409Snwhitehorn "FAILURE - zero length DMA transfer attempted\n"); 217183409Snwhitehorn return EIO; 218183409Snwhitehorn } 219183409Snwhitehorn if (((uintptr_t)(request->data) & (ch->dma.alignment - 1)) || 220183409Snwhitehorn (request->bytecount & (ch->dma.alignment - 1))) { 221183409Snwhitehorn device_printf(request->dev, 222183409Snwhitehorn "FAILURE - non aligned DMA transfer attempted\n"); 223183409Snwhitehorn return EIO; 224183409Snwhitehorn } 225183409Snwhitehorn if (request->bytecount > ch->dma.max_iosize) { 226183409Snwhitehorn device_printf(request->dev, 227183409Snwhitehorn "FAILURE - oversized DMA transfer attempt %d > %d\n", 228183409Snwhitehorn request->bytecount, ch->dma.max_iosize); 229183409Snwhitehorn return EIO; 230183409Snwhitehorn } 231183409Snwhitehorn 232200171Smav request->dma = &ch->dma.slot[0]; 233183409Snwhitehorn 234183409Snwhitehorn if ((error = bus_dmamap_load(request->dma->data_tag, 235183409Snwhitehorn request->dma->data_map, request->data, request->bytecount, 236183409Snwhitehorn &ata_dbdma_setprd, &args, BUS_DMA_NOWAIT))) { 237183409Snwhitehorn device_printf(request->dev, "FAILURE - load data\n"); 238183409Snwhitehorn goto error; 239183409Snwhitehorn } 240183409Snwhitehorn 241183409Snwhitehorn if (entries) 242183409Snwhitehorn *entries = args.nsegs; 243183409Snwhitehorn 244183409Snwhitehorn bus_dmamap_sync(request->dma->sg_tag, request->dma->sg_map, 245183409Snwhitehorn BUS_DMASYNC_PREWRITE); 246183409Snwhitehorn bus_dmamap_sync(request->dma->data_tag, request->dma->data_map, 247183409Snwhitehorn (request->flags & ATA_R_READ) ? 248183409Snwhitehorn BUS_DMASYNC_PREREAD : BUS_DMASYNC_PREWRITE); 249183409Snwhitehorn 250183409Snwhitehorn return 0; 251183409Snwhitehorn 252183409Snwhitehornerror: 253183409Snwhitehorn ch->dma.unload(request); 254183409Snwhitehorn return EIO; 255183409Snwhitehorn} 256183409Snwhitehorn 257183409Snwhitehornvoid 258183409Snwhitehornata_dbdma_dmainit(device_t dev) 259183409Snwhitehorn{ 260183409Snwhitehorn struct ata_dbdma_channel *sc = device_get_softc(dev); 261183409Snwhitehorn int error; 262183409Snwhitehorn 263183409Snwhitehorn error = dbdma_allocate_channel(sc->dbdma_regs, sc->dbdma_offset, 264183409Snwhitehorn bus_get_dma_tag(dev), 256, &sc->dbdma); 265183409Snwhitehorn 266183409Snwhitehorn dbdma_set_wait_selector(sc->dbdma,1 << 7, 1 << 7); 267183409Snwhitehorn 268183409Snwhitehorn dbdma_insert_stop(sc->dbdma,0); 269183409Snwhitehorn sc->next_dma_slot=1; 270183409Snwhitehorn 271183409Snwhitehorn sc->sc_ch.dma.start = ata_dbdma_start; 272183409Snwhitehorn sc->sc_ch.dma.stop = ata_dbdma_stop; 273183409Snwhitehorn sc->sc_ch.dma.load = ata_dbdma_load; 274183409Snwhitehorn sc->sc_ch.dma.reset = ata_dbdma_reset; 275183409Snwhitehorn 276184429Snwhitehorn /* 277184429Snwhitehorn * DBDMA's field for transfer size is 16 bits. This will overflow 278184429Snwhitehorn * if we try to do a 64K transfer, so stop short of 64K. 279184429Snwhitehorn */ 280184429Snwhitehorn sc->sc_ch.dma.segsize = 126 * DEV_BSIZE; 281216083Smarius ata_dmainit(dev); 282184429Snwhitehorn 283183409Snwhitehorn sc->sc_ch.hw.status = ata_dbdma_status; 284183409Snwhitehorn 285183409Snwhitehorn mtx_init(&sc->dbdma_mtx, "ATA DBDMA", NULL, MTX_DEF); 286183409Snwhitehorn} 287