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