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/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-2-Clause-FreeBSD
    3  *
    4  * Copyright (c) 2008 Nathan Whitehorn
    5  * All rights reserved
    6  *
    7  * Redistribution and use in source and binary forms, with or without
    8  * modification, are permitted provided that the following conditions
    9  * are met:
   10  * 1. Redistributions of source code must retain the above copyright
   11  *    notice, this list of conditions and the following disclaimer.
   12  * 2. Redistributions in binary form must reproduce the above copyright
   13  *    notice, this list of conditions and the following disclaimer in the
   14  *    documentation and/or other materials provided with the distribution.
   15  *
   16  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
   17  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
   18  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
   19  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
   20  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
   21  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
   22  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
   23  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
   24  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
   25  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
   26  * SUCH DAMAGE.
   27  */
   28 
   29 #include <sys/cdefs.h>
   30 __FBSDID("$FreeBSD$");
   31 
   32 #include <sys/param.h>
   33 #include <sys/systm.h>
   34 #include <sys/kernel.h>
   35 #include <sys/malloc.h>
   36 #include <sys/module.h>
   37 #include <sys/endian.h>
   38 #include <sys/bus.h>
   39 #include <machine/bus.h>
   40 #include <machine/dbdma.h>
   41 #include <sys/rman.h>
   42 
   43 #include "dbdmavar.h"
   44 
   45 static MALLOC_DEFINE(M_DBDMA, "dbdma", "DBDMA Command List");
   46 
   47 static uint32_t dbdma_read_reg(dbdma_channel_t *, u_int);
   48 static void dbdma_write_reg(dbdma_channel_t *, u_int, uint32_t);
   49 static void dbdma_phys_callback(void *, bus_dma_segment_t *, int, int);
   50 
   51 static void
   52 dbdma_phys_callback(void *chan, bus_dma_segment_t *segs, int nsegs, int error)
   53 {
   54         dbdma_channel_t *channel = (dbdma_channel_t *)(chan);
   55 
   56         channel->sc_slots_pa = segs[0].ds_addr;
   57         dbdma_write_reg(channel, CHAN_CMDPTR, channel->sc_slots_pa);
   58 }
   59 
   60 int
   61 dbdma_allocate_channel(struct resource *dbdma_regs, u_int offset,
   62     bus_dma_tag_t parent_dma, int slots, dbdma_channel_t **chan)
   63 {
   64         int error = 0;
   65         dbdma_channel_t *channel;
   66 
   67         channel = *chan = malloc(sizeof(struct dbdma_channel), M_DBDMA, 
   68             M_WAITOK | M_ZERO);
   69 
   70         channel->sc_regs = dbdma_regs;
   71         channel->sc_off = offset;
   72         dbdma_stop(channel);
   73 
   74         channel->sc_slots_pa = 0;
   75 
   76         error = bus_dma_tag_create(parent_dma, 16, 0, BUS_SPACE_MAXADDR_32BIT,
   77             BUS_SPACE_MAXADDR, NULL, NULL, PAGE_SIZE, 1, PAGE_SIZE, 0, NULL,
   78             NULL, &(channel->sc_dmatag));
   79 
   80         error = bus_dmamem_alloc(channel->sc_dmatag,
   81             (void **)&channel->sc_slots, BUS_DMA_WAITOK | BUS_DMA_ZERO,
   82             &channel->sc_dmamap);
   83 
   84         error = bus_dmamap_load(channel->sc_dmatag, channel->sc_dmamap,
   85             channel->sc_slots, PAGE_SIZE, dbdma_phys_callback, channel, 0);
   86 
   87         dbdma_write_reg(channel, CHAN_CMDPTR_HI, 0);
   88 
   89         channel->sc_nslots = slots;
   90 
   91         return (error);
   92 }
   93 
   94 int
   95 dbdma_resize_channel(dbdma_channel_t *chan, int newslots)
   96 {
   97 
   98         if (newslots > (PAGE_SIZE / sizeof(struct dbdma_command)))
   99                 return (-1);
  100 
  101         chan->sc_nslots = newslots;
  102         return (0);
  103 }
  104 
  105 int
  106 dbdma_free_channel(dbdma_channel_t *chan)
  107 {
  108 
  109         dbdma_stop(chan);
  110 
  111         bus_dmamem_free(chan->sc_dmatag, chan->sc_slots, chan->sc_dmamap);
  112         bus_dma_tag_destroy(chan->sc_dmatag);
  113 
  114         free(chan, M_DBDMA);
  115 
  116         return (0);
  117 }
  118 
  119 uint16_t
  120 dbdma_get_cmd_status(dbdma_channel_t *chan, int slot)
  121 {
  122 
  123         bus_dmamap_sync(chan->sc_dmatag, chan->sc_dmamap, BUS_DMASYNC_POSTREAD);
  124 
  125         /*
  126          * I really did mean to swap resCount and xferStatus here, to
  127          * account for the quad-word little endian fields.
  128          */
  129         return (le16toh(chan->sc_slots[slot].resCount));
  130 }
  131 
  132 void
  133 dbdma_clear_cmd_status(dbdma_channel_t *chan, int slot)
  134 {
  135         /* See endian note above */
  136         chan->sc_slots[slot].resCount = 0;
  137 }
  138 
  139 uint16_t
  140 dbdma_get_residuals(dbdma_channel_t *chan, int slot)
  141 {
  142 
  143         bus_dmamap_sync(chan->sc_dmatag, chan->sc_dmamap, BUS_DMASYNC_POSTREAD);
  144 
  145         return (le16toh(chan->sc_slots[slot].xferStatus));
  146 }
  147 
  148 void
  149 dbdma_reset(dbdma_channel_t *chan)
  150 {
  151 
  152         dbdma_stop(chan);
  153         dbdma_set_current_cmd(chan, 0);
  154         dbdma_run(chan);
  155 }
  156 
  157 void
  158 dbdma_run(dbdma_channel_t *chan)
  159 {
  160         uint32_t control_reg;
  161 
  162         control_reg = DBDMA_STATUS_RUN | DBDMA_STATUS_PAUSE |
  163             DBDMA_STATUS_WAKE | DBDMA_STATUS_DEAD;
  164         control_reg <<= DBDMA_REG_MASK_SHIFT;
  165 
  166         control_reg |= DBDMA_STATUS_RUN;
  167         dbdma_write_reg(chan, CHAN_CONTROL_REG, control_reg);
  168 }
  169 
  170 void
  171 dbdma_pause(dbdma_channel_t *chan)
  172 {
  173         uint32_t control_reg;
  174 
  175         control_reg = DBDMA_STATUS_PAUSE;
  176         control_reg <<= DBDMA_REG_MASK_SHIFT;
  177 
  178         control_reg |= DBDMA_STATUS_PAUSE;
  179         dbdma_write_reg(chan, CHAN_CONTROL_REG, control_reg);
  180 }
  181 
  182 void
  183 dbdma_wake(dbdma_channel_t *chan)
  184 {
  185         uint32_t control_reg;
  186 
  187         control_reg = DBDMA_STATUS_WAKE | DBDMA_STATUS_PAUSE |
  188             DBDMA_STATUS_RUN | DBDMA_STATUS_DEAD;
  189         control_reg <<= DBDMA_REG_MASK_SHIFT;
  190 
  191         control_reg |= DBDMA_STATUS_WAKE | DBDMA_STATUS_RUN;
  192         dbdma_write_reg(chan, CHAN_CONTROL_REG, control_reg);
  193 }
  194 
  195 void
  196 dbdma_stop(dbdma_channel_t *chan)
  197 {
  198         uint32_t control_reg;
  199 
  200         control_reg = DBDMA_STATUS_RUN;
  201         control_reg <<= DBDMA_REG_MASK_SHIFT;
  202 
  203         dbdma_write_reg(chan, CHAN_CONTROL_REG, control_reg);
  204 
  205         while (dbdma_read_reg(chan, CHAN_STATUS_REG) & DBDMA_STATUS_ACTIVE)
  206                 DELAY(5);
  207 }
  208 
  209 void
  210 dbdma_set_current_cmd(dbdma_channel_t *chan, int slot)
  211 {
  212         uint32_t cmd;
  213 
  214         cmd = chan->sc_slots_pa + slot * sizeof(struct dbdma_command);
  215         dbdma_write_reg(chan, CHAN_CMDPTR, cmd);
  216 }
  217 
  218 uint16_t
  219 dbdma_get_chan_status(dbdma_channel_t *chan)
  220 {
  221         uint32_t status_reg;
  222 
  223         status_reg = dbdma_read_reg(chan, CHAN_STATUS_REG);
  224         return (status_reg & 0x0000ffff);
  225 }
  226 
  227 uint8_t
  228 dbdma_get_device_status(dbdma_channel_t *chan)
  229 {
  230         return (dbdma_get_chan_status(chan) & 0x00ff);
  231 }
  232 
  233 void
  234 dbdma_set_device_status(dbdma_channel_t *chan, uint8_t mask, uint8_t value)
  235 {
  236         uint32_t control_reg;
  237 
  238         control_reg = mask;
  239         control_reg <<= DBDMA_REG_MASK_SHIFT;
  240         control_reg |= value;
  241 
  242         dbdma_write_reg(chan, CHAN_CONTROL_REG, control_reg);
  243 }
  244 
  245 void
  246 dbdma_set_interrupt_selector(dbdma_channel_t *chan, uint8_t mask, uint8_t val)
  247 {
  248         uint32_t intr_select;
  249 
  250         intr_select = mask;
  251         intr_select <<= DBDMA_REG_MASK_SHIFT;
  252 
  253         intr_select |= val;
  254         dbdma_write_reg(chan, CHAN_INTR_SELECT, intr_select);
  255 }
  256 
  257 void
  258 dbdma_set_branch_selector(dbdma_channel_t *chan, uint8_t mask, uint8_t val)
  259 {
  260         uint32_t br_select;
  261 
  262         br_select = mask;
  263         br_select <<= DBDMA_REG_MASK_SHIFT;
  264 
  265         br_select |= val;
  266         dbdma_write_reg(chan, CHAN_BRANCH_SELECT, br_select);
  267 }
  268 
  269 void
  270 dbdma_set_wait_selector(dbdma_channel_t *chan, uint8_t mask, uint8_t val)
  271 {
  272         uint32_t wait_select;
  273 
  274         wait_select = mask;
  275         wait_select <<= DBDMA_REG_MASK_SHIFT;
  276         wait_select |= val;
  277         dbdma_write_reg(chan, CHAN_WAIT_SELECT, wait_select);
  278 }
  279 
  280 void
  281 dbdma_insert_command(dbdma_channel_t *chan, int slot, int command, int stream,
  282     bus_addr_t data, size_t count, uint8_t interrupt, uint8_t branch,
  283     uint8_t wait, uint32_t branch_slot)
  284 {
  285         struct dbdma_command cmd;
  286         uint32_t *flip;
  287 
  288         cmd.cmd = command;
  289         cmd.key = stream;
  290         cmd.intr = interrupt;
  291         cmd.branch = branch;
  292         cmd.wait = wait;
  293 
  294         cmd.reqCount = count;
  295         cmd.address = (uint32_t)(data);
  296         if (command != DBDMA_STORE_QUAD && command != DBDMA_LOAD_QUAD)
  297                 cmd.cmdDep = chan->sc_slots_pa + 
  298                     branch_slot * sizeof(struct dbdma_command);
  299         else
  300                 cmd.cmdDep = branch_slot;
  301 
  302         cmd.resCount = 0;
  303         cmd.xferStatus = 0;
  304 
  305         /*
  306          * Move quadwords to little-endian. God only knows why
  307          * Apple thought this was a good idea.
  308          */
  309         flip = (uint32_t *)(&cmd);
  310         flip[0] = htole32(flip[0]);
  311         flip[1] = htole32(flip[1]);
  312         flip[2] = htole32(flip[2]);
  313 
  314         chan->sc_slots[slot] = cmd;
  315 }
  316 
  317 void
  318 dbdma_insert_stop(dbdma_channel_t *chan, int slot)
  319 {
  320 
  321         dbdma_insert_command(chan, slot, DBDMA_STOP, 0, 0, 0, DBDMA_NEVER,
  322             DBDMA_NEVER, DBDMA_NEVER, 0);
  323 }
  324 
  325 void
  326 dbdma_insert_nop(dbdma_channel_t *chan, int slot)
  327 {
  328 
  329         dbdma_insert_command(chan, slot, DBDMA_NOP, 0, 0, 0, DBDMA_NEVER,
  330             DBDMA_NEVER, DBDMA_NEVER, 0);
  331 }
  332 
  333 void
  334 dbdma_insert_branch(dbdma_channel_t *chan, int slot, int to_slot)
  335 {
  336 
  337         dbdma_insert_command(chan, slot, DBDMA_NOP, 0, 0, 0, DBDMA_NEVER,
  338             DBDMA_ALWAYS, DBDMA_NEVER, to_slot);
  339 }
  340 
  341 void
  342 dbdma_sync_commands(dbdma_channel_t *chan, bus_dmasync_op_t op)
  343 {
  344 
  345         bus_dmamap_sync(chan->sc_dmatag, chan->sc_dmamap, op);
  346 }
  347 
  348 void
  349 dbdma_save_state(dbdma_channel_t *chan)
  350 {
  351 
  352         chan->sc_saved_regs[0] = dbdma_read_reg(chan, CHAN_CMDPTR);
  353         chan->sc_saved_regs[1] = dbdma_read_reg(chan, CHAN_CMDPTR_HI);
  354         chan->sc_saved_regs[2] = dbdma_read_reg(chan, CHAN_INTR_SELECT);
  355         chan->sc_saved_regs[3] = dbdma_read_reg(chan, CHAN_BRANCH_SELECT);
  356         chan->sc_saved_regs[4] = dbdma_read_reg(chan, CHAN_WAIT_SELECT);
  357 
  358         dbdma_stop(chan);
  359 }
  360 
  361 void
  362 dbdma_restore_state(dbdma_channel_t *chan)
  363 {
  364 
  365         dbdma_wake(chan);
  366         dbdma_write_reg(chan, CHAN_CMDPTR, chan->sc_saved_regs[0]);
  367         dbdma_write_reg(chan, CHAN_CMDPTR_HI, chan->sc_saved_regs[1]);
  368         dbdma_write_reg(chan, CHAN_INTR_SELECT, chan->sc_saved_regs[2]);
  369         dbdma_write_reg(chan, CHAN_BRANCH_SELECT, chan->sc_saved_regs[3]);
  370         dbdma_write_reg(chan, CHAN_WAIT_SELECT, chan->sc_saved_regs[4]);
  371 }
  372 
  373 static uint32_t
  374 dbdma_read_reg(dbdma_channel_t *chan, u_int offset)
  375 {
  376 
  377         return (bus_read_4(chan->sc_regs, chan->sc_off + offset));
  378 }
  379 
  380 static void
  381 dbdma_write_reg(dbdma_channel_t *chan, u_int offset, uint32_t val)
  382 {
  383 
  384         bus_write_4(chan->sc_regs, chan->sc_off + offset, val);
  385 }

Cache object: 3fb56750b842f8f6366398b5e32c32df


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