[ 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  -  FREEBSD8  -  FREEBSD7  -  FREEBSD72  -  FREEBSD71  -  FREEBSD70  -  FREEBSD6  -  FREEBSD64  -  FREEBSD63  -  FREEBSD62  -  FREEBSD61  -  FREEBSD60  -  FREEBSD5  -  FREEBSD55  -  FREEBSD54  -  FREEBSD53  -  FREEBSD52  -  FREEBSD51  -  FREEBSD50  -  FREEBSD4  -  FREEBSD3  -  FREEBSD22  -  linux-2.6  -  linux-2.4.22  -  MK83  -  MK84  -  PLAN9  -  DFBSD  -  NETBSD  -  NETBSD5  -  NETBSD4  -  NETBSD3  -  NETBSD20  -  OPENBSD  -  xnu-517  -  xnu-792  -  xnu-792.6.70  -  xnu-1228  -  xnu-1456.1.26  -  OPENSOLARIS  -  minix-3-1-1  -  FREEBSD-LIBC  -  FREEBSD7-LIBC  -  FREEBSD6-LIBC  -  GLIBC27 
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  * $FreeBSD: head/sys/powerpc/powermac/ata_dbdma.c 200171 2009-12-06 00:10:13Z mav $
   28  */
   29 
   30 /*
   31  * Common routines for the DMA engine on both the Apple Kauai and MacIO
   32  * ATA controllers.
   33  */
   34 
   35 #include "opt_ata.h"
   36 #include <sys/param.h>
   37 #include <sys/systm.h>
   38 #include <sys/kernel.h>
   39 #include <sys/module.h>
   40 #include <sys/bus.h>
   41 #include <sys/malloc.h>
   42 #include <sys/sema.h>
   43 #include <sys/taskqueue.h>
   44 #include <vm/uma.h>
   45 #include <machine/stdarg.h>
   46 #include <machine/resource.h>
   47 #include <machine/bus.h>
   48 #include <sys/rman.h>
   49 #include <sys/ata.h>
   50 #include <dev/ata/ata-all.h>
   51 #include <dev/ata/ata-pci.h>
   52 #include <ata_if.h>
   53 
   54 #include "ata_dbdma.h"
   55 
   56 struct ata_dbdma_dmaload_args {
   57         struct ata_dbdma_channel *sc;
   58 
   59         int write;
   60         int nsegs;
   61 };
   62 
   63 static void
   64 ata_dbdma_setprd(void *xarg, bus_dma_segment_t *segs, int nsegs, int error)
   65 {
   66         struct ata_dbdma_dmaload_args *arg = xarg;
   67         struct ata_dbdma_channel *sc = arg->sc;
   68         int branch_type, command;
   69         int prev_stop;
   70         int i;
   71 
   72         mtx_lock(&sc->dbdma_mtx);
   73 
   74         prev_stop = sc->next_dma_slot-1;
   75         if (prev_stop < 0)
   76                 prev_stop = 0xff;
   77 
   78         for (i = 0; i < nsegs; i++) {
   79                 /* Loop back to the beginning if this is our last slot */
   80                 if (sc->next_dma_slot == 0xff)
   81                         branch_type = DBDMA_ALWAYS;
   82                 else
   83                         branch_type = DBDMA_NEVER;
   84 
   85                 if (arg->write) {
   86                         command = (i + 1 < nsegs) ? DBDMA_OUTPUT_MORE : 
   87                             DBDMA_OUTPUT_LAST;
   88                 } else {
   89                         command = (i + 1 < nsegs) ? DBDMA_INPUT_MORE : 
   90                             DBDMA_INPUT_LAST;
   91                 }
   92 
   93                 dbdma_insert_command(sc->dbdma, sc->next_dma_slot++,
   94                     command, 0, segs[i].ds_addr, segs[i].ds_len,
   95                     DBDMA_NEVER, branch_type, DBDMA_NEVER, 0);
   96 
   97                 if (branch_type == DBDMA_ALWAYS)
   98                         sc->next_dma_slot = 0;
   99         }
  100 
  101         /* We have a corner case where the STOP command is the last slot,
  102          * but you can't branch in STOP commands. So add a NOP branch here
  103          * and the STOP in slot 0. */
  104 
  105         if (sc->next_dma_slot == 0xff) {
  106                 dbdma_insert_branch(sc->dbdma, sc->next_dma_slot, 0);
  107                 sc->next_dma_slot = 0;
  108         }
  109 
  110 #if 0
  111         dbdma_insert_command(sc->dbdma, sc->next_dma_slot++,
  112             DBDMA_NOP, 0, 0, 0, DBDMA_ALWAYS, DBDMA_NEVER, DBDMA_NEVER, 0);
  113 #endif
  114         dbdma_insert_stop(sc->dbdma, sc->next_dma_slot++);
  115         dbdma_insert_nop(sc->dbdma, prev_stop);
  116 
  117         dbdma_sync_commands(sc->dbdma, BUS_DMASYNC_PREWRITE);
  118 
  119         mtx_unlock(&sc->dbdma_mtx);
  120 
  121         arg->nsegs = nsegs;
  122 }
  123 
  124 static int
  125 ata_dbdma_status(device_t dev)
  126 {
  127         struct ata_dbdma_channel *sc = device_get_softc(dev);
  128         struct ata_channel *ch = device_get_softc(dev);
  129 
  130         if (sc->sc_ch.dma.flags & ATA_DMA_ACTIVE) {
  131                 return (!(dbdma_get_chan_status(sc->dbdma) & 
  132                     DBDMA_STATUS_ACTIVE));
  133         }
  134 
  135         if (ATA_IDX_INB(ch, ATA_ALTSTAT) & ATA_S_BUSY) {
  136                 DELAY(100);
  137                 if (ATA_IDX_INB(ch, ATA_ALTSTAT) & ATA_S_BUSY)
  138                         return 0;
  139         }
  140         return 1;
  141 }
  142 
  143 static int
  144 ata_dbdma_start(struct ata_request *request)
  145 {
  146         struct ata_dbdma_channel *sc = device_get_softc(request->parent);
  147 
  148         sc->sc_ch.dma.flags |= ATA_DMA_ACTIVE;
  149         dbdma_wake(sc->dbdma);
  150         return 0;
  151 }
  152 
  153 static void
  154 ata_dbdma_reset(device_t dev)
  155 {
  156         struct ata_dbdma_channel *sc = device_get_softc(dev);
  157 
  158         mtx_lock(&sc->dbdma_mtx);
  159 
  160         dbdma_stop(sc->dbdma);
  161         dbdma_insert_stop(sc->dbdma, 0);
  162         sc->next_dma_slot=1;
  163         dbdma_set_current_cmd(sc->dbdma, 0);
  164 
  165         sc->sc_ch.dma.flags &= ~ATA_DMA_ACTIVE;
  166 
  167         mtx_unlock(&sc->dbdma_mtx);
  168 }
  169 
  170 static int
  171 ata_dbdma_stop(struct ata_request *request)
  172 {
  173         struct ata_dbdma_channel *sc = device_get_softc(request->parent);
  174 
  175         uint16_t status;
  176         
  177         status = dbdma_get_chan_status(sc->dbdma);
  178 
  179         dbdma_pause(sc->dbdma);
  180         sc->sc_ch.dma.flags &= ~ATA_DMA_ACTIVE;
  181 
  182         if (status & DBDMA_STATUS_DEAD) {
  183                 device_printf(request->parent,"DBDMA dead, resetting "
  184                     "channel...\n");
  185                 ata_dbdma_reset(request->parent);
  186                 return ATA_S_ERROR;
  187         }
  188 
  189         if (!(status & DBDMA_STATUS_RUN)) {
  190                 device_printf(request->parent,"DBDMA confused, stop called "
  191                     "when channel is not running!\n");
  192                 return ATA_S_ERROR;
  193         }
  194 
  195         if (status & DBDMA_STATUS_ACTIVE) {
  196                 device_printf(request->parent,"DBDMA channel stopped "
  197                     "prematurely\n");
  198                 return ATA_S_ERROR;
  199         }
  200         return 0;
  201 }
  202 
  203 static int
  204 ata_dbdma_load(struct ata_request *request, void *addr, int *entries)
  205 {
  206         struct ata_channel *ch = device_get_softc(request->parent);
  207         struct ata_dbdma_dmaload_args args;
  208 
  209         int error;
  210 
  211         args.sc = device_get_softc(request->parent);
  212         args.write = !(request->flags & ATA_R_READ);
  213 
  214         if (!request->bytecount) {
  215                 device_printf(request->dev,
  216                     "FAILURE - zero length DMA transfer attempted\n");
  217                 return EIO;
  218         }
  219         if (((uintptr_t)(request->data) & (ch->dma.alignment - 1)) ||
  220             (request->bytecount & (ch->dma.alignment - 1))) {
  221                 device_printf(request->dev,
  222                     "FAILURE - non aligned DMA transfer attempted\n");
  223                 return EIO;
  224         }
  225         if (request->bytecount > ch->dma.max_iosize) {
  226                 device_printf(request->dev,
  227                     "FAILURE - oversized DMA transfer attempt %d > %d\n",
  228                     request->bytecount, ch->dma.max_iosize);
  229                 return EIO;
  230         }
  231 
  232         request->dma = &ch->dma.slot[0];
  233 
  234         if ((error = bus_dmamap_load(request->dma->data_tag, 
  235             request->dma->data_map, request->data, request->bytecount,
  236             &ata_dbdma_setprd, &args, BUS_DMA_NOWAIT))) {
  237                 device_printf(request->dev, "FAILURE - load data\n");
  238                 goto error;
  239         }
  240 
  241         if (entries)
  242                 *entries = args.nsegs;
  243 
  244         bus_dmamap_sync(request->dma->sg_tag, request->dma->sg_map,
  245             BUS_DMASYNC_PREWRITE);
  246         bus_dmamap_sync(request->dma->data_tag, request->dma->data_map,
  247             (request->flags & ATA_R_READ) ?
  248             BUS_DMASYNC_PREREAD : BUS_DMASYNC_PREWRITE);
  249 
  250         return 0;
  251 
  252 error:
  253         ch->dma.unload(request);
  254         return EIO;
  255 }
  256 
  257 void
  258 ata_dbdma_dmainit(device_t dev)
  259 {
  260         struct ata_dbdma_channel *sc = device_get_softc(dev);
  261         int error;
  262 
  263         error = dbdma_allocate_channel(sc->dbdma_regs, sc->dbdma_offset,
  264             bus_get_dma_tag(dev), 256, &sc->dbdma);
  265 
  266         dbdma_set_wait_selector(sc->dbdma,1 << 7, 1 << 7);
  267 
  268         dbdma_insert_stop(sc->dbdma,0);
  269         sc->next_dma_slot=1;
  270 
  271         ata_dmainit(dev);
  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 
  283         sc->sc_ch.hw.status = ata_dbdma_status;
  284 
  285         mtx_init(&sc->dbdma_mtx, "ATA DBDMA", NULL, MTX_DEF);
  286 }
  287 

Cache object: eb2ab2360f108c22464a74840317d40a


[ 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.