The Design and Implementation of the FreeBSD Operating System, Second Edition
Now available: The Design and Implementation of the FreeBSD Operating System (Second Edition)


[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ] [ list types ] [ track identifier ]

FreeBSD/Linux Kernel Cross Reference
sys/powerpc/powermac/ata_dbdma.c

Version: -  FREEBSD  -  FREEBSD-13-STABLE  -  FREEBSD-13-0  -  FREEBSD-12-STABLE  -  FREEBSD-12-0  -  FREEBSD-11-STABLE  -  FREEBSD-11-0  -  FREEBSD-10-STABLE  -  FREEBSD-10-0  -  FREEBSD-9-STABLE  -  FREEBSD-9-0  -  FREEBSD-8-STABLE  -  FREEBSD-8-0  -  FREEBSD-7-STABLE  -  FREEBSD-7-0  -  FREEBSD-6-STABLE  -  FREEBSD-6-0  -  FREEBSD-5-STABLE  -  FREEBSD-5-0  -  FREEBSD-4-STABLE  -  FREEBSD-3-STABLE  -  FREEBSD22  -  l41  -  OPENBSD  -  linux-2.6  -  MK84  -  PLAN9  -  xnu-8792 
SearchContext: -  none  -  3  -  10 

    1 /*-
    2  * Copyright 2008 by Nathan Whitehorn. All rights reserved.
    3  *
    4  * Redistribution and use in source and binary forms, with or without
    5  * modification, are permitted provided that the following conditions
    6  * are met:
    7  * 1. Redistributions of source code must retain the above copyright
    8  *    notice, this list of conditions and the following disclaimer.
    9  * 2. Redistributions in binary form must reproduce the above copyright
   10  *    notice, this list of conditions and the following disclaimer in the
   11  *    documentation and/or other materials provided with the distribution.
   12  * 3. The name of the author may not be used to endorse or promote products
   13  *    derived from this software without specific prior written permission.
   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,
   20  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
   21  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
   22  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
   23  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
   24  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
   25  * SUCH DAMAGE.
   26  */
   27 
   28 #include <sys/cdefs.h>
   29 __FBSDID("* $FreeBSD$");
   30 
   31 /*
   32  * Common routines for the DMA engine on both the Apple Kauai and MacIO
   33  * ATA controllers.
   34  */
   35 
   36 #include "opt_ata.h"
   37 #include <sys/param.h>
   38 #include <sys/systm.h>
   39 #include <sys/kernel.h>
   40 #include <sys/module.h>
   41 #include <sys/bus.h>
   42 #include <sys/malloc.h>
   43 #include <sys/sema.h>
   44 #include <sys/taskqueue.h>
   45 #include <vm/uma.h>
   46 #include <machine/stdarg.h>
   47 #include <machine/resource.h>
   48 #include <machine/bus.h>
   49 #include <sys/rman.h>
   50 #include <sys/ata.h>
   51 #include <dev/ata/ata-all.h>
   52 #include <dev/ata/ata-pci.h>
   53 #include <ata_if.h>
   54 
   55 #include "ata_dbdma.h"
   56 
   57 struct ata_dbdma_dmaload_args {
   58         struct ata_dbdma_channel *sc;
   59 
   60         int write;
   61         int nsegs;
   62 };
   63 
   64 static void
   65 ata_dbdma_setprd(void *xarg, bus_dma_segment_t *segs, int nsegs, int error)
   66 {
   67         struct ata_dbdma_dmaload_args *arg = xarg;
   68         struct ata_dbdma_channel *sc = arg->sc;
   69         int branch_type, command;
   70         int prev_stop;
   71         int i;
   72 
   73         mtx_lock(&sc->dbdma_mtx);
   74 
   75         prev_stop = sc->next_dma_slot-1;
   76         if (prev_stop < 0)
   77                 prev_stop = 0xff;
   78 
   79         for (i = 0; i < nsegs; i++) {
   80                 /* Loop back to the beginning if this is our last slot */
   81                 if (sc->next_dma_slot == 0xff)
   82                         branch_type = DBDMA_ALWAYS;
   83                 else
   84                         branch_type = DBDMA_NEVER;
   85 
   86                 if (arg->write) {
   87                         command = (i + 1 < nsegs) ? DBDMA_OUTPUT_MORE : 
   88                             DBDMA_OUTPUT_LAST;
   89                 } else {
   90                         command = (i + 1 < nsegs) ? DBDMA_INPUT_MORE : 
   91                             DBDMA_INPUT_LAST;
   92                 }
   93 
   94                 dbdma_insert_command(sc->dbdma, sc->next_dma_slot++,
   95                     command, 0, segs[i].ds_addr, segs[i].ds_len,
   96                     DBDMA_NEVER, branch_type, DBDMA_NEVER, 0);
   97 
   98                 if (branch_type == DBDMA_ALWAYS)
   99                         sc->next_dma_slot = 0;
  100         }
  101 
  102         /* We have a corner case where the STOP command is the last slot,
  103          * but you can't branch in STOP commands. So add a NOP branch here
  104          * and the STOP in slot 0. */
  105 
  106         if (sc->next_dma_slot == 0xff) {
  107                 dbdma_insert_branch(sc->dbdma, sc->next_dma_slot, 0);
  108                 sc->next_dma_slot = 0;
  109         }
  110 
  111 #if 0
  112         dbdma_insert_command(sc->dbdma, sc->next_dma_slot++,
  113             DBDMA_NOP, 0, 0, 0, DBDMA_ALWAYS, DBDMA_NEVER, DBDMA_NEVER, 0);
  114 #endif
  115         dbdma_insert_stop(sc->dbdma, sc->next_dma_slot++);
  116         dbdma_insert_nop(sc->dbdma, prev_stop);
  117 
  118         dbdma_sync_commands(sc->dbdma, BUS_DMASYNC_PREWRITE);
  119 
  120         mtx_unlock(&sc->dbdma_mtx);
  121 
  122         arg->nsegs = nsegs;
  123 }
  124 
  125 static int
  126 ata_dbdma_status(device_t dev)
  127 {
  128         struct ata_dbdma_channel *sc = device_get_softc(dev);
  129         struct ata_channel *ch = device_get_softc(dev);
  130 
  131         if (sc->sc_ch.dma.flags & ATA_DMA_ACTIVE) {
  132                 return (!(dbdma_get_chan_status(sc->dbdma) & 
  133                     DBDMA_STATUS_ACTIVE));
  134         }
  135 
  136         if (ATA_IDX_INB(ch, ATA_ALTSTAT) & ATA_S_BUSY) {
  137                 DELAY(100);
  138                 if (ATA_IDX_INB(ch, ATA_ALTSTAT) & ATA_S_BUSY)
  139                         return 0;
  140         }
  141         return 1;
  142 }
  143 
  144 static int
  145 ata_dbdma_start(struct ata_request *request)
  146 {
  147         struct ata_dbdma_channel *sc = device_get_softc(request->parent);
  148 
  149         sc->sc_ch.dma.flags |= ATA_DMA_ACTIVE;
  150         dbdma_wake(sc->dbdma);
  151         return 0;
  152 }
  153 
  154 static void
  155 ata_dbdma_reset(device_t dev)
  156 {
  157         struct ata_dbdma_channel *sc = device_get_softc(dev);
  158 
  159         mtx_lock(&sc->dbdma_mtx);
  160 
  161         dbdma_stop(sc->dbdma);
  162         dbdma_insert_stop(sc->dbdma, 0);
  163         sc->next_dma_slot=1;
  164         dbdma_set_current_cmd(sc->dbdma, 0);
  165 
  166         sc->sc_ch.dma.flags &= ~ATA_DMA_ACTIVE;
  167 
  168         mtx_unlock(&sc->dbdma_mtx);
  169 }
  170 
  171 static int
  172 ata_dbdma_stop(struct ata_request *request)
  173 {
  174         struct ata_dbdma_channel *sc = device_get_softc(request->parent);
  175 
  176         uint16_t status;
  177         
  178         status = dbdma_get_chan_status(sc->dbdma);
  179 
  180         dbdma_pause(sc->dbdma);
  181         sc->sc_ch.dma.flags &= ~ATA_DMA_ACTIVE;
  182 
  183         if (status & DBDMA_STATUS_DEAD) {
  184                 device_printf(request->parent,"DBDMA dead, resetting "
  185                     "channel...\n");
  186                 ata_dbdma_reset(request->parent);
  187                 return ATA_S_ERROR;
  188         }
  189 
  190         if (!(status & DBDMA_STATUS_RUN)) {
  191                 device_printf(request->parent,"DBDMA confused, stop called "
  192                     "when channel is not running!\n");
  193                 return ATA_S_ERROR;
  194         }
  195 
  196         if (status & DBDMA_STATUS_ACTIVE) {
  197                 device_printf(request->parent,"DBDMA channel stopped "
  198                     "prematurely\n");
  199                 return ATA_S_ERROR;
  200         }
  201         return 0;
  202 }
  203 
  204 static int
  205 ata_dbdma_load(struct ata_request *request, void *addr, int *entries)
  206 {
  207         struct ata_channel *ch = device_get_softc(request->parent);
  208         struct ata_dbdma_dmaload_args args;
  209 
  210         int error;
  211 
  212         args.sc = device_get_softc(request->parent);
  213         args.write = !(request->flags & ATA_R_READ);
  214 
  215         if (!request->bytecount) {
  216                 device_printf(request->dev,
  217                     "FAILURE - zero length DMA transfer attempted\n");
  218                 return EIO;
  219         }
  220         if (((uintptr_t)(request->data) & (ch->dma.alignment - 1)) ||
  221             (request->bytecount & (ch->dma.alignment - 1))) {
  222                 device_printf(request->dev,
  223                     "FAILURE - non aligned DMA transfer attempted\n");
  224                 return EIO;
  225         }
  226         if (request->bytecount > ch->dma.max_iosize) {
  227                 device_printf(request->dev,
  228                     "FAILURE - oversized DMA transfer attempt %d > %d\n",
  229                     request->bytecount, ch->dma.max_iosize);
  230                 return EIO;
  231         }
  232 
  233         request->dma = &ch->dma.slot[0];
  234 
  235         if ((error = bus_dmamap_load(request->dma->data_tag, 
  236             request->dma->data_map, request->data, request->bytecount,
  237             &ata_dbdma_setprd, &args, BUS_DMA_NOWAIT))) {
  238                 device_printf(request->dev, "FAILURE - load data\n");
  239                 goto error;
  240         }
  241 
  242         if (entries)
  243                 *entries = args.nsegs;
  244 
  245         bus_dmamap_sync(request->dma->sg_tag, request->dma->sg_map,
  246             BUS_DMASYNC_PREWRITE);
  247         bus_dmamap_sync(request->dma->data_tag, request->dma->data_map,
  248             (request->flags & ATA_R_READ) ?
  249             BUS_DMASYNC_PREREAD : BUS_DMASYNC_PREWRITE);
  250 
  251         return 0;
  252 
  253 error:
  254         ch->dma.unload(request);
  255         return EIO;
  256 }
  257 
  258 void
  259 ata_dbdma_dmainit(device_t dev)
  260 {
  261         struct ata_dbdma_channel *sc = device_get_softc(dev);
  262         int error;
  263 
  264         error = dbdma_allocate_channel(sc->dbdma_regs, sc->dbdma_offset,
  265             bus_get_dma_tag(dev), 256, &sc->dbdma);
  266 
  267         dbdma_set_wait_selector(sc->dbdma,1 << 7, 1 << 7);
  268 
  269         dbdma_insert_stop(sc->dbdma,0);
  270         sc->next_dma_slot=1;
  271 
  272         sc->sc_ch.dma.start = ata_dbdma_start;
  273         sc->sc_ch.dma.stop = ata_dbdma_stop;
  274         sc->sc_ch.dma.load = ata_dbdma_load;
  275         sc->sc_ch.dma.reset = ata_dbdma_reset;
  276 
  277         /*
  278          * DBDMA's field for transfer size is 16 bits. This will overflow
  279          * if we try to do a 64K transfer, so stop short of 64K.
  280          */
  281         sc->sc_ch.dma.segsize = 126 * DEV_BSIZE;
  282         ata_dmainit(dev);
  283 
  284         sc->sc_ch.hw.status = ata_dbdma_status;
  285 
  286         mtx_init(&sc->dbdma_mtx, "ATA DBDMA", NULL, MTX_DEF);
  287 }

Cache object: 69f091ba420b5489c8f187d4c7a44724


[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ] [ list types ] [ track identifier ]


This page is part of the FreeBSD/Linux Linux Kernel Cross-Reference, and was automatically generated using a modified version of the LXR engine.