summaryrefslogtreecommitdiffstats
path: root/sys/powerpc/powermac
diff options
context:
space:
mode:
authormarcel <marcel@FreeBSD.org>2008-06-07 21:56:48 +0000
committermarcel <marcel@FreeBSD.org>2008-06-07 21:56:48 +0000
commit7cc97ffd512dc3393fb06812a6b553e964bcfcf7 (patch)
treeab91a0daf175a60aa416e7582cf93a77cfa63b58 /sys/powerpc/powermac
parent87278ff58a1872ef2002ec481db68eebce6f5884 (diff)
downloadFreeBSD-src-7cc97ffd512dc3393fb06812a6b553e964bcfcf7.zip
FreeBSD-src-7cc97ffd512dc3393fb06812a6b553e964bcfcf7.tar.gz
Add support for Apple's Descriptor-Based DMA (DBDMA) engine. The DMA
engine is usful to various existing drivers, such as ata(4) and scc(4), and is used bhy the soon to be added bm(4). Submitted by: Nathan Whitehorn
Diffstat (limited to 'sys/powerpc/powermac')
-rw-r--r--sys/powerpc/powermac/dbdma.c331
-rw-r--r--sys/powerpc/powermac/dbdmavar.h102
2 files changed, 433 insertions, 0 deletions
diff --git a/sys/powerpc/powermac/dbdma.c b/sys/powerpc/powermac/dbdma.c
new file mode 100644
index 0000000..71bf417
--- /dev/null
+++ b/sys/powerpc/powermac/dbdma.c
@@ -0,0 +1,331 @@
+/*-
+ * Copyright (c) 2008 Nathan Whitehorn
+ * All rights reserved
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/kernel.h>
+#include <sys/malloc.h>
+#include <sys/module.h>
+#include <sys/endian.h>
+#include <sys/bus.h>
+#include <machine/bus.h>
+#include <machine/dbdma.h>
+#include <sys/rman.h>
+
+#include "dbdmavar.h"
+
+MALLOC_DEFINE(M_DBDMA, "dbdma", "DBDMA Command List");
+
+static uint32_t dbdma_read_reg(dbdma_channel_t *, u_int);
+static void dbdma_write_reg(dbdma_channel_t *, u_int, uint32_t);
+static void dbdma_phys_callback(void *, bus_dma_segment_t *, int, int);
+
+static void
+dbdma_phys_callback(void *chan, bus_dma_segment_t *segs, int nsegs, int error)
+{
+ dbdma_channel_t *channel = (dbdma_channel_t *)(chan);
+
+ channel->sc_slots_pa = segs[0].ds_addr;
+ dbdma_write_reg(channel, CHAN_CMDPTR, channel->sc_slots_pa);
+}
+
+int
+dbdma_allocate_channel(struct resource *dbdma_regs, bus_dma_tag_t parent_dma,
+ int slots, dbdma_channel_t **chan)
+{
+ int error = 0;
+ dbdma_channel_t *channel;
+
+ channel = *chan = malloc(sizeof(struct dbdma_channel), M_DBDMA,
+ M_WAITOK | M_ZERO);
+
+ channel->sc_bt = rman_get_bustag(dbdma_regs);
+ channel->sc_bh = rman_get_bushandle(dbdma_regs);
+ dbdma_stop(channel);
+
+ channel->sc_slots_pa = 0;
+
+ error = bus_dma_tag_create(parent_dma, 16, 0, BUS_SPACE_MAXADDR_32BIT,
+ BUS_SPACE_MAXADDR, NULL, NULL, PAGE_SIZE, 1, PAGE_SIZE, 0, NULL,
+ NULL, &(channel->sc_dmatag));
+
+ error = bus_dmamem_alloc(channel->sc_dmatag,
+ (void **)&channel->sc_slots, BUS_DMA_WAITOK | BUS_DMA_ZERO,
+ &channel->sc_dmamap);
+
+ error = bus_dmamap_load(channel->sc_dmatag, channel->sc_dmamap,
+ channel->sc_slots, PAGE_SIZE, dbdma_phys_callback, channel, 0);
+
+ channel->sc_nslots = slots;
+
+ return (error);
+}
+
+int
+dbdma_resize_channel(dbdma_channel_t *chan, int newslots)
+{
+
+ if (newslots > (PAGE_SIZE / 16))
+ return (-1);
+
+ chan->sc_nslots = newslots;
+ return (0);
+}
+
+int
+dbdma_free_channel(dbdma_channel_t *chan)
+{
+
+ dbdma_stop(chan);
+
+ bus_dmamem_free(chan->sc_dmatag, chan->sc_slots, chan->sc_dmamap);
+ bus_dma_tag_destroy(chan->sc_dmatag);
+
+ free(chan, M_DBDMA);
+
+ return (0);
+}
+
+uint16_t
+dbdma_get_cmd_status(dbdma_channel_t *chan, int slot)
+{
+
+ bus_dmamap_sync(chan->sc_dmatag, chan->sc_dmamap, BUS_DMASYNC_POSTREAD);
+
+ /*
+ * I really did mean to swap resCount and xferStatus here, to
+ * account for the quad-word little endian fields.
+ */
+ return (le16toh(chan->sc_slots[slot].resCount));
+}
+
+uint16_t
+dbdma_get_residuals(dbdma_channel_t *chan, int slot)
+{
+
+ bus_dmamap_sync(chan->sc_dmatag, chan->sc_dmamap, BUS_DMASYNC_POSTREAD);
+
+ return (le16toh(chan->sc_slots[slot].xferStatus));
+}
+
+void
+dbdma_reset(dbdma_channel_t *chan)
+{
+
+ dbdma_stop(chan);
+ dbdma_set_current_cmd(chan, 0);
+ dbdma_run(chan);
+}
+
+void
+dbdma_run(dbdma_channel_t *chan)
+{
+ uint32_t control_reg;
+
+ control_reg = DBDMA_STATUS_RUN | DBDMA_STATUS_PAUSE |
+ DBDMA_STATUS_WAKE | DBDMA_STATUS_DEAD;
+ control_reg <<= 16;
+ control_reg |= DBDMA_STATUS_RUN;
+ dbdma_write_reg(chan, CHAN_CONTROL_REG, control_reg);
+}
+
+void
+dbdma_pause(dbdma_channel_t *chan)
+{
+ uint32_t control_reg;
+
+ control_reg = DBDMA_STATUS_PAUSE;
+ control_reg <<= 16;
+ control_reg |= DBDMA_STATUS_PAUSE;
+ dbdma_write_reg(chan, CHAN_CONTROL_REG, control_reg);
+}
+
+void
+dbdma_wake(dbdma_channel_t *chan)
+{
+ uint32_t control_reg;
+
+ control_reg = DBDMA_STATUS_WAKE | DBDMA_STATUS_PAUSE |
+ DBDMA_STATUS_RUN | DBDMA_STATUS_DEAD;
+ control_reg <<= 16;
+ control_reg |= DBDMA_STATUS_WAKE | DBDMA_STATUS_RUN;
+ dbdma_write_reg(chan, CHAN_CONTROL_REG, control_reg);
+}
+
+void
+dbdma_stop(dbdma_channel_t *chan)
+{
+ uint32_t control_reg;
+
+ control_reg = DBDMA_STATUS_RUN;
+ control_reg <<= 16;
+ dbdma_write_reg(chan, CHAN_CONTROL_REG, control_reg);
+
+ while (dbdma_read_reg(chan, CHAN_STATUS_REG) & DBDMA_STATUS_ACTIVE)
+ DELAY(5);
+}
+
+void
+dbdma_set_current_cmd(dbdma_channel_t *chan, int slot)
+{
+ uint32_t cmd;
+
+ cmd = chan->sc_slots_pa + slot * 16;
+ dbdma_write_reg(chan, CHAN_CMDPTR, cmd);
+}
+
+uint16_t
+dbdma_get_chan_status(dbdma_channel_t *chan)
+{
+ uint32_t status_reg;
+
+ status_reg = dbdma_read_reg(chan, CHAN_STATUS_REG);
+ return (status_reg & 0x0000ffff);
+}
+
+uint8_t
+dbdma_get_chan_device_status(dbdma_channel_t *chan)
+{
+
+ return (dbdma_get_chan_status(chan) & 0x00ff);
+}
+
+void
+dbdma_set_interrupt_selector(dbdma_channel_t *chan, uint8_t mask, uint8_t val)
+{
+ uint32_t intr_select;
+
+ intr_select = mask;
+ intr_select <<= 16;
+ intr_select |= val;
+ dbdma_write_reg(chan, CHAN_INTR_SELECT, intr_select);
+}
+
+void
+dbdma_set_branch_selector(dbdma_channel_t *chan, uint8_t mask, uint8_t val)
+{
+ uint32_t br_select;
+
+ br_select = mask;
+ br_select <<= 16;
+ br_select |= val;
+ dbdma_write_reg(chan, CHAN_BRANCH_SELECT, br_select);
+}
+
+void
+dbdma_set_wait_selector(dbdma_channel_t *chan, uint8_t mask, uint8_t val)
+{
+ uint32_t wait_select;
+
+ wait_select = mask;
+ wait_select <<= 16;
+ wait_select |= val;
+ dbdma_write_reg(chan, CHAN_WAIT_SELECT, wait_select);
+}
+
+void
+dbdma_insert_command(dbdma_channel_t *chan, int slot, int command, int stream,
+ bus_addr_t data, size_t count, uint8_t interrupt, uint8_t branch,
+ uint8_t wait, uint32_t branch_slot)
+{
+ struct dbdma_command cmd;
+ uint32_t *flip;
+
+ cmd.cmd = command;
+ cmd.key = stream;
+ cmd.intr = interrupt;
+ cmd.branch = branch;
+ cmd.wait = wait;
+
+ cmd.reqCount = count;
+ cmd.address = (uint32_t)(data);
+ if (command != DBDMA_STORE_QUAD && command != DBDMA_LOAD_QUAD)
+ cmd.cmdDep = chan->sc_slots_pa + branch_slot * 16;
+ else
+ cmd.cmdDep = branch_slot;
+
+ cmd.resCount = 0;
+ cmd.xferStatus = 0;
+
+ /*
+ * Move quadwords to little-endian. God only knows why
+ * Apple thought this was a good idea.
+ */
+ flip = (uint32_t *)(&cmd);
+ flip[0] = htole32(flip[0]);
+ flip[1] = htole32(flip[1]);
+ flip[2] = htole32(flip[2]);
+
+ chan->sc_slots[slot] = cmd;
+}
+
+void
+dbdma_insert_stop(dbdma_channel_t *chan, int slot)
+{
+
+ dbdma_insert_command(chan, slot, DBDMA_STOP, 0, 0, 0, DBDMA_NEVER,
+ DBDMA_NEVER, DBDMA_NEVER, 0);
+}
+
+void
+dbdma_insert_nop(dbdma_channel_t *chan, int slot)
+{
+
+ dbdma_insert_command(chan, slot, DBDMA_NOP, 0, 0, 0, DBDMA_NEVER,
+ DBDMA_NEVER, DBDMA_NEVER, 0);
+}
+
+void
+dbdma_insert_branch(dbdma_channel_t *chan, int slot, int to_slot)
+{
+
+ dbdma_insert_command(chan, slot, DBDMA_NOP, 0, 0, 0, DBDMA_NEVER,
+ DBDMA_ALWAYS, DBDMA_NEVER, to_slot);
+}
+
+void
+dbdma_sync_commands(dbdma_channel_t *chan, bus_dmasync_op_t op)
+{
+
+ bus_dmamap_sync(chan->sc_dmatag, chan->sc_dmamap, op);
+}
+
+static uint32_t
+dbdma_read_reg(dbdma_channel_t *chan, u_int offset)
+{
+
+ return (bus_space_read_4(chan->sc_bt, chan->sc_bh, offset));
+}
+
+static void
+dbdma_write_reg(dbdma_channel_t *chan, u_int offset, uint32_t val)
+{
+
+ bus_space_write_4(chan->sc_bt, chan->sc_bh, offset, val);
+}
diff --git a/sys/powerpc/powermac/dbdmavar.h b/sys/powerpc/powermac/dbdmavar.h
new file mode 100644
index 0000000..c69da13
--- /dev/null
+++ b/sys/powerpc/powermac/dbdmavar.h
@@ -0,0 +1,102 @@
+/*-
+ * Copyright (c) 2008 Nathan Whitehorn
+ * All rights reserved
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef _POWERPC_POWERMAC_DBDMAVAR_H_
+#define _POWERPC_POWERMAC_DBDMAVAR_H_
+
+struct dbdma_command {
+ uint8_t cmd:4; /* DBDMA command */
+
+ uint8_t _resd1:1;
+ uint8_t key:3; /* Stream number, or 6 for KEY_SYSTEM */
+ uint8_t _resd2:2;
+
+ /* Interrupt, branch, and wait flags */
+ uint8_t intr:2;
+ uint8_t branch:2;
+ uint8_t wait:2;
+
+ uint16_t reqCount; /* Bytes to transfer */
+
+ uint32_t address; /* 32-bit system physical address */
+ uint32_t cmdDep; /* Branch address or quad word to load/store */
+
+ uint16_t xferStatus; /* Contents of channel status after completion */
+ uint16_t resCount; /* Number of residual bytes outstanding */
+};
+
+struct dbdma_channel {
+ bus_space_tag_t sc_bt;
+ bus_space_handle_t sc_bh;
+
+ struct dbdma_command *sc_slots;
+ int sc_nslots;
+ bus_addr_t sc_slots_pa;
+
+ bus_dma_tag_t sc_dmatag;
+ bus_dmamap_t sc_dmamap;
+};
+
+
+/*
+ DBDMA registers are found at 0x8000 + n*0x100 in the macio register space,
+ and are laid out as follows within each block:
+
+ Address: Description: Length (bytes):
+ 0x000 Channel Control 4
+ 0x004 Channel Status 4
+ 0x00C Command Phys Addr 4
+ 0x010 Interrupt Select 4
+ 0x014 Branch Select 4
+ 0x018 Wait Select 4
+*/
+
+#define CHAN_CONTROL_REG 0x00
+#define CHAN_STATUS_REG 0x04
+#define CHAN_CMDPTR 0x0C
+#define CHAN_INTR_SELECT 0x10
+#define CHAN_BRANCH_SELECT 0x14
+#define CHAN_WAIT_SELECT 0x18
+
+/* Channel control is the write channel to channel status, the upper 16 bits
+ are a mask of which bytes to change */
+
+/* Status bits 0-7 are device dependent status bits */
+
+/*
+ The Interrupt/Branch/Wait Select triggers the corresponding condition bits
+ in the event that (select.mask & device dependent status) == select.value
+
+ They are defined a follows:
+ Byte 1: Reserved
+ Byte 2: Mask
+ Byte 3: Reserved
+ Byte 4: Value
+*/
+
+#endif /* _POWERPC_POWERMAC_DBDMAVAR_H_ */
OpenPOWER on IntegriCloud