summaryrefslogtreecommitdiffstats
path: root/sys/powerpc
diff options
context:
space:
mode:
authornwhitehorn <nwhitehorn@FreeBSD.org>2008-09-27 15:13:44 +0000
committernwhitehorn <nwhitehorn@FreeBSD.org>2008-09-27 15:13:44 +0000
commitb44c49966f0d0e48d8a28a146a784adad65dec09 (patch)
tree41b77e96c58ea8523f7b9d1fe61659a92a72ac9b /sys/powerpc
parent4d3e47c7710ae08c97176cdb8639726fc74ff1c7 (diff)
downloadFreeBSD-src-b44c49966f0d0e48d8a28a146a784adad65dec09.zip
FreeBSD-src-b44c49966f0d0e48d8a28a146a784adad65dec09.tar.gz
Add DMA support for Apple built-in ATA controllers.
Tested by: grehan, marcotrillo@gmail.com MFC after: 1 month
Diffstat (limited to 'sys/powerpc')
-rw-r--r--sys/powerpc/powermac/ata_dbdma.c282
-rw-r--r--sys/powerpc/powermac/ata_dbdma.h55
-rw-r--r--sys/powerpc/powermac/ata_kauai.c267
-rw-r--r--sys/powerpc/powermac/ata_macio.c225
4 files changed, 795 insertions, 34 deletions
diff --git a/sys/powerpc/powermac/ata_dbdma.c b/sys/powerpc/powermac/ata_dbdma.c
new file mode 100644
index 0000000..fe53255
--- /dev/null
+++ b/sys/powerpc/powermac/ata_dbdma.c
@@ -0,0 +1,282 @@
+/*-
+ * Copyright 2008 by 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.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 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$
+ */
+
+/*
+ * Common routines for the DMA engine on both the Apple Kauai and MacIO
+ * ATA controllers.
+ */
+
+#include "opt_ata.h"
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/kernel.h>
+#include <sys/module.h>
+#include <sys/bus.h>
+#include <sys/malloc.h>
+#include <sys/sema.h>
+#include <sys/taskqueue.h>
+#include <vm/uma.h>
+#include <machine/stdarg.h>
+#include <machine/resource.h>
+#include <machine/bus.h>
+#include <sys/rman.h>
+#include <sys/ata.h>
+#include <dev/ata/ata-all.h>
+#include <dev/ata/ata-pci.h>
+#include <ata_if.h>
+
+#include "ata_dbdma.h"
+
+struct ata_dbdma_dmaload_args {
+ struct ata_dbdma_channel *sc;
+
+ int write;
+ int nsegs;
+};
+
+static void
+ata_dbdma_setprd(void *xarg, bus_dma_segment_t *segs, int nsegs, int error)
+{
+ struct ata_dbdma_dmaload_args *arg = xarg;
+ struct ata_dbdma_channel *sc = arg->sc;
+ int branch_type, command;
+ int prev_stop;
+ int i;
+
+ mtx_lock(&sc->dbdma_mtx);
+
+ prev_stop = sc->next_dma_slot-1;
+ if (prev_stop < 0)
+ prev_stop = 0xff;
+
+ for (i = 0; i < nsegs; i++) {
+ /* Loop back to the beginning if this is our last slot */
+ if (sc->next_dma_slot == 0xff)
+ branch_type = DBDMA_ALWAYS;
+ else
+ branch_type = DBDMA_NEVER;
+
+ if (arg->write) {
+ command = (i + 1 < nsegs) ? DBDMA_OUTPUT_MORE :
+ DBDMA_OUTPUT_LAST;
+ } else {
+ command = (i + 1 < nsegs) ? DBDMA_INPUT_MORE :
+ DBDMA_INPUT_LAST;
+ }
+
+ dbdma_insert_command(sc->dbdma, sc->next_dma_slot++,
+ command, 0, segs[i].ds_addr, segs[i].ds_len,
+ DBDMA_NEVER, branch_type, DBDMA_NEVER, 0);
+
+ if (branch_type == DBDMA_ALWAYS)
+ sc->next_dma_slot = 0;
+ }
+
+ /* We have a corner case where the STOP command is the last slot,
+ * but you can't branch in STOP commands. So add a NOP branch here
+ * and the STOP in slot 0. */
+
+ if (sc->next_dma_slot == 0xff) {
+ dbdma_insert_branch(sc->dbdma, sc->next_dma_slot, 0);
+ sc->next_dma_slot = 0;
+ }
+
+#if 0
+ dbdma_insert_command(sc->dbdma, sc->next_dma_slot++,
+ DBDMA_NOP, 0, 0, 0, DBDMA_ALWAYS, DBDMA_NEVER, DBDMA_NEVER, 0);
+#endif
+ dbdma_insert_stop(sc->dbdma, sc->next_dma_slot++);
+ dbdma_insert_nop(sc->dbdma, prev_stop);
+
+ dbdma_sync_commands(sc->dbdma, BUS_DMASYNC_PREWRITE);
+
+ mtx_unlock(&sc->dbdma_mtx);
+
+ arg->nsegs = nsegs;
+}
+
+static int
+ata_dbdma_status(device_t dev)
+{
+ struct ata_dbdma_channel *sc = device_get_softc(dev);
+ struct ata_channel *ch = device_get_softc(dev);
+
+ if (sc->sc_ch.dma.flags & ATA_DMA_ACTIVE) {
+ return (!(dbdma_get_chan_status(sc->dbdma) &
+ DBDMA_STATUS_ACTIVE));
+ }
+
+ if (ATA_IDX_INB(ch, ATA_ALTSTAT) & ATA_S_BUSY) {
+ DELAY(100);
+ if (ATA_IDX_INB(ch, ATA_ALTSTAT) & ATA_S_BUSY)
+ return 0;
+ }
+ return 1;
+}
+
+static int
+ata_dbdma_start(struct ata_request *request)
+{
+ struct ata_dbdma_channel *sc = device_get_softc(request->parent);
+
+ sc->sc_ch.dma.flags |= ATA_DMA_ACTIVE;
+ dbdma_wake(sc->dbdma);
+ return 0;
+}
+
+static void
+ata_dbdma_reset(device_t dev)
+{
+ struct ata_dbdma_channel *sc = device_get_softc(dev);
+
+ mtx_lock(&sc->dbdma_mtx);
+
+ dbdma_stop(sc->dbdma);
+ dbdma_insert_stop(sc->dbdma, 0);
+ sc->next_dma_slot=1;
+ dbdma_set_current_cmd(sc->dbdma, 0);
+
+ sc->sc_ch.dma.flags &= ~ATA_DMA_ACTIVE;
+
+ mtx_unlock(&sc->dbdma_mtx);
+}
+
+static int
+ata_dbdma_stop(struct ata_request *request)
+{
+ struct ata_dbdma_channel *sc = device_get_softc(request->parent);
+
+ uint16_t status;
+
+ status = dbdma_get_chan_status(sc->dbdma);
+
+ dbdma_pause(sc->dbdma);
+ sc->sc_ch.dma.flags &= ~ATA_DMA_ACTIVE;
+
+ if (status & DBDMA_STATUS_DEAD) {
+ device_printf(request->parent,"DBDMA dead, resetting "
+ "channel...\n");
+ ata_dbdma_reset(request->parent);
+ return ATA_S_ERROR;
+ }
+
+ if (!(status & DBDMA_STATUS_RUN)) {
+ device_printf(request->parent,"DBDMA confused, stop called "
+ "when channel is not running!\n");
+ return ATA_S_ERROR;
+ }
+
+ if (status & DBDMA_STATUS_ACTIVE) {
+ device_printf(request->parent,"DBDMA channel stopped "
+ "prematurely\n");
+ return ATA_S_ERROR;
+ }
+ return 0;
+}
+
+static int
+ata_dbdma_load(struct ata_request *request, void *addr, int *entries)
+{
+ struct ata_channel *ch = device_get_softc(request->parent);
+ struct ata_device *atadev = device_get_softc(request->dev);
+ struct ata_dbdma_dmaload_args args;
+
+ int error;
+
+ args.sc = device_get_softc(request->parent);
+ args.write = !(request->flags & ATA_R_READ);
+
+ if (!request->bytecount) {
+ device_printf(request->dev,
+ "FAILURE - zero length DMA transfer attempted\n");
+ return EIO;
+ }
+ if (((uintptr_t)(request->data) & (ch->dma.alignment - 1)) ||
+ (request->bytecount & (ch->dma.alignment - 1))) {
+ device_printf(request->dev,
+ "FAILURE - non aligned DMA transfer attempted\n");
+ return EIO;
+ }
+ if (request->bytecount > ch->dma.max_iosize) {
+ device_printf(request->dev,
+ "FAILURE - oversized DMA transfer attempt %d > %d\n",
+ request->bytecount, ch->dma.max_iosize);
+ return EIO;
+ }
+
+ request->dma = &ch->dma.slot[atadev->unit];
+
+ if ((error = bus_dmamap_load(request->dma->data_tag,
+ request->dma->data_map, request->data, request->bytecount,
+ &ata_dbdma_setprd, &args, BUS_DMA_NOWAIT))) {
+ device_printf(request->dev, "FAILURE - load data\n");
+ goto error;
+ }
+
+ if (entries)
+ *entries = args.nsegs;
+
+ bus_dmamap_sync(request->dma->sg_tag, request->dma->sg_map,
+ BUS_DMASYNC_PREWRITE);
+ bus_dmamap_sync(request->dma->data_tag, request->dma->data_map,
+ (request->flags & ATA_R_READ) ?
+ BUS_DMASYNC_PREREAD : BUS_DMASYNC_PREWRITE);
+
+ return 0;
+
+error:
+ ch->dma.unload(request);
+ return EIO;
+}
+
+void
+ata_dbdma_dmainit(device_t dev)
+{
+ struct ata_dbdma_channel *sc = device_get_softc(dev);
+ int error;
+
+ error = dbdma_allocate_channel(sc->dbdma_regs, sc->dbdma_offset,
+ bus_get_dma_tag(dev), 256, &sc->dbdma);
+
+ dbdma_set_wait_selector(sc->dbdma,1 << 7, 1 << 7);
+
+ dbdma_insert_stop(sc->dbdma,0);
+ sc->next_dma_slot=1;
+
+ ata_dmainit(dev);
+ sc->sc_ch.dma.start = ata_dbdma_start;
+ sc->sc_ch.dma.stop = ata_dbdma_stop;
+ sc->sc_ch.dma.load = ata_dbdma_load;
+ sc->sc_ch.dma.reset = ata_dbdma_reset;
+
+ sc->sc_ch.hw.status = ata_dbdma_status;
+
+ mtx_init(&sc->dbdma_mtx, "ATA DBDMA", NULL, MTX_DEF);
+}
+
diff --git a/sys/powerpc/powermac/ata_dbdma.h b/sys/powerpc/powermac/ata_dbdma.h
new file mode 100644
index 0000000..61d0687
--- /dev/null
+++ b/sys/powerpc/powermac/ata_dbdma.h
@@ -0,0 +1,55 @@
+/*-
+ * Copyright 2008 by 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.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 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 ATA_DBDMA_H
+#define ATA_DBDMA_H
+
+#include <sys/param.h>
+#include <sys/lock.h>
+#include <sys/mutex.h>
+
+#include <machine/dbdma.h>
+
+struct ata_dbdma_channel {
+ struct ata_channel sc_ch;
+
+ int dbdma_rid;
+ struct resource *dbdma_regs;
+ u_int dbdma_offset;
+
+ dbdma_channel_t *dbdma;
+ int next_dma_slot;
+
+ struct mtx dbdma_mtx;
+};
+
+void ata_dbdma_dmainit(device_t dev);
+
+#endif /* ATA_DBDMA_H */
+
diff --git a/sys/powerpc/powermac/ata_kauai.c b/sys/powerpc/powermac/ata_kauai.c
index 624f142..0582251 100644
--- a/sys/powerpc/powermac/ata_kauai.c
+++ b/sys/powerpc/powermac/ata_kauai.c
@@ -50,11 +50,16 @@ __FBSDID("$FreeBSD$");
#include <ata_if.h>
#include <dev/ofw/openfirm.h>
+#include <powerpc/ofw/ofw_pci.h>
+#include <machine/intr_machdep.h>
#include <dev/pci/pcivar.h>
#include <dev/pci/pcireg.h>
+#include "ata_dbdma.h"
+
#define ATA_KAUAI_REGOFFSET 0x2000
+#define ATA_KAUAI_DBDMAOFFSET 0x1000
/*
* Offset to alt-control register from base
@@ -67,15 +72,26 @@ __FBSDID("$FreeBSD$");
#define ATA_KAUAI_REGGAP 16
/*
+ * PIO and DMA access registers
+ */
+#define PIO_CONFIG_REG (ATA_KAUAI_REGOFFSET + 0x200)
+#define UDMA_CONFIG_REG (ATA_KAUAI_REGOFFSET + 0x210)
+#define DMA_IRQ_REG (ATA_KAUAI_REGOFFSET + 0x300)
+
+#define USE_DBDMA_IRQ 0
+
+/*
* Define the kauai pci bus attachment.
*/
static int ata_kauai_probe(device_t dev);
+static int ata_kauai_attach(device_t dev);
static void ata_kauai_setmode(device_t parent, device_t dev);
+static int ata_kauai_begin_transaction(struct ata_request *request);
static device_method_t ata_kauai_methods[] = {
/* Device interface */
DEVMETHOD(device_probe, ata_kauai_probe),
- DEVMETHOD(device_attach, ata_attach),
+ DEVMETHOD(device_attach, ata_kauai_attach),
DEVMETHOD(device_detach, bus_generic_detach),
DEVMETHOD(device_shutdown, bus_generic_shutdown),
DEVMETHOD(device_suspend, bus_generic_suspend),
@@ -86,10 +102,22 @@ static device_method_t ata_kauai_methods[] = {
{ 0, 0 }
};
+struct ata_kauai_softc {
+ struct ata_dbdma_channel sc_ch;
+
+ struct resource *sc_memr;
+
+ int shasta;
+
+ uint32_t udmaconf[2];
+ uint32_t wdmaconf[2];
+ uint32_t pioconf[2];
+};
+
static driver_t ata_kauai_driver = {
"ata",
ata_kauai_methods,
- sizeof(struct ata_channel),
+ sizeof(struct ata_kauai_softc),
};
DRIVER_MODULE(ata, pci, ata_kauai_driver, ata_devclass, 0, 0);
@@ -105,17 +133,71 @@ static struct kauai_pci_dev {
{ 0x0033106b, "Uninorth2 Kauai ATA Controller" },
{ 0x003b106b, "Intrepid Kauai ATA Controller" },
{ 0x0043106b, "K2 Kauai ATA Controller" },
+ { 0x0050106b, "Shasta Kauai ATA Controller" },
{ 0x0069106b, "Intrepid-2 Kauai ATA Controller" },
{ 0, NULL }
};
+/*
+ * IDE transfer timings
+ */
+#define KAUAI_PIO_MASK 0xff000fff
+#define KAUAI_DMA_MASK 0x00fff000
+#define KAUAI_UDMA_MASK 0x0000ffff
+
+static const u_int pio_timing_kauai[] = {
+ 0x08000a92, /* PIO0 */
+ 0x0800060f, /* PIO1 */
+ 0x0800038b, /* PIO2 */
+ 0x05000249, /* PIO3 */
+ 0x04000148 /* PIO4 */
+};
+static const u_int pio_timing_shasta[] = {
+ 0x0a000c97, /* PIO0 */
+ 0x07000712, /* PIO1 */
+ 0x040003cd, /* PIO2 */
+ 0x0400028b, /* PIO3 */
+ 0x0400010a /* PIO4 */
+};
+
+static const u_int dma_timing_kauai[] = {
+ 0x00618000, /* WDMA0 */
+ 0x00209000, /* WDMA1 */
+ 0x00148000 /* WDMA2 */
+};
+static const u_int dma_timing_shasta[] = {
+ 0x00820800, /* WDMA0 */
+ 0x0028b000, /* WDMA1 */
+ 0x001ca000 /* WDMA2 */
+};
+
+static const u_int udma_timing_kauai[] = {
+ 0x000070c1, /* UDMA0 */
+ 0x00005d81, /* UDMA1 */
+ 0x00004a61, /* UDMA2 */
+ 0x00003a51, /* UDMA3 */
+ 0x00002a31, /* UDMA4 */
+ 0x00002921 /* UDMA5 */
+};
+static const u_int udma_timing_shasta[] = {
+ 0x00035901, /* UDMA0 */
+ 0x000348b1, /* UDMA1 */
+ 0x00033881, /* UDMA2 */
+ 0x00033861, /* UDMA3 */
+ 0x00033841, /* UDMA4 */
+ 0x00033031, /* UDMA5 */
+ 0x00033021 /* UDMA6 */
+};
+
static int
ata_kauai_probe(device_t dev)
{
struct ata_channel *ch;
- struct resource *mem;
+ struct ata_kauai_softc *sc;
u_long startp, countp;
u_int32_t devid;
+ phandle_t node;
+ char *compatstring = NULL;
int i, found, rid, status;
found = 0;
@@ -130,6 +212,18 @@ ata_kauai_probe(device_t dev)
if (!found)
return (ENXIO);
+ node = ofw_pci_find_node(dev);
+ sc = device_get_softc(dev);
+ bzero(sc, sizeof(struct ata_kauai_softc));
+ ch = &sc->sc_ch.sc_ch;
+
+ OF_getprop_alloc(node, "compatible", 1, (void **)&compatstring);
+ if (strcmp(compatstring,"shasta-ata") == 0)
+ sc->shasta = 1;
+
+ free(compatstring, M_OFWPROP);
+
+
/*
* This device seems to ignore writes to the interrupt
* config register, resulting in interrupt resources
@@ -140,33 +234,42 @@ ata_kauai_probe(device_t dev)
*/
status = bus_get_resource(dev, SYS_RES_IRQ, 0, &startp, &countp);
if (status == ENOENT) {
- int irq;
+ int *irq;
+ phandle_t iparent;
+ int icells, nintr, i;
/*
- * Aargh, hideous hack until ofw pci intr routine is
- * exported
+ * Horrible hack to handle Kauai devices that have their IRQs
+ * set up in an utterly wrong way
*/
- irq = 39; /* XXX */
- bus_set_resource(dev, SYS_RES_IRQ, 0, irq, 1);
+ if (!sc->shasta)
+ bus_set_resource(dev, SYS_RES_IRQ, 0, 39, 1);
/*
- * Sanity check...
+ * For the rest of the interrupts, and the main Shasta
+ * interrupt, get the IRQs from firmware.
*/
- status = bus_get_resource(dev, SYS_RES_IRQ, 0, &startp,
- &countp);
- if (status == ENOENT ||
- startp != 39) {
- printf("kauai irq not set!\n");
- return (ENXIO);
+ if (OF_getprop(node, "interrupt-parent", &iparent,
+ sizeof(iparent)) == sizeof(iparent)) {
+ OF_getprop(iparent, "#interrupt-cells", &icells,
+ sizeof(icells)) ;
}
+
+ nintr = OF_getprop_alloc(node, "interrupts", sizeof(*irq),
+ (void **)&irq);
+
+ for (i = 0; i < nintr; i += icells)
+ bus_set_resource(dev, SYS_RES_IRQ,
+ i/icells + !sc->shasta, irq[i], 1);
+
+ free(irq, M_OFWPROP);
}
- ch = device_get_softc(dev);
- bzero(ch, sizeof(struct ata_channel));
rid = PCIR_BARS;
- mem = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, RF_ACTIVE);
- if (mem == NULL) {
+ sc->sc_memr = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid,
+ RF_ACTIVE);
+ if (sc->sc_memr == NULL) {
device_printf(dev, "could not allocate memory\n");
return (ENXIO);
}
@@ -175,10 +278,10 @@ ata_kauai_probe(device_t dev)
* Set up the resource vectors
*/
for (i = ATA_DATA; i <= ATA_COMMAND; i++) {
- ch->r_io[i].res = mem;
+ ch->r_io[i].res = sc->sc_memr;
ch->r_io[i].offset = i*ATA_KAUAI_REGGAP + ATA_KAUAI_REGOFFSET;
}
- ch->r_io[ATA_CONTROL].res = mem;
+ ch->r_io[ATA_CONTROL].res = sc->sc_memr;
ch->r_io[ATA_CONTROL].offset = ATA_KAUAI_ALTOFFSET;
ata_default_registers(dev);
@@ -189,11 +292,129 @@ ata_kauai_probe(device_t dev)
return (ata_probe(dev));
}
+#if USE_DBDMA_IRQ
+static int
+ata_kauai_dma_interrupt(struct ata_kauai_softc *sc)
+{
+ /* Clear the DMA interrupt bits */
+
+ bus_write_4(sc->sc_memr, DMA_IRQ_REG, 0x80000000);
+
+ return ata_interrupt(sc);
+}
+#endif
+
+static int
+ata_kauai_attach(device_t dev)
+{
+ struct ata_kauai_softc *sc = device_get_softc(dev);
+#if USE_DBDMA_IRQ
+ int dbdma_irq_rid = 1;
+ struct resource *dbdma_irq;
+ void *cookie;
+#endif
+
+ pci_enable_busmaster(dev);
+
+ /* Init DMA engine */
+
+ sc->sc_ch.dbdma_rid = 1;
+ sc->sc_ch.dbdma_regs = sc->sc_memr;
+ sc->sc_ch.dbdma_offset = ATA_KAUAI_DBDMAOFFSET;
+
+ ata_dbdma_dmainit(dev);
+
+#if USE_DBDMA_IRQ
+ /* Bind to DBDMA interrupt as well */
+ if ((dbdma_irq = bus_alloc_resource_any(dev, SYS_RES_IRQ,
+ &dbdma_irq_rid, RF_SHAREABLE | RF_ACTIVE)) != NULL) {
+ bus_setup_intr(dev, dbdma_irq, ATA_INTR_FLAGS, NULL,
+ (driver_intr_t *)ata_kauai_dma_interrupt, sc,&cookie);
+ }
+#endif
+
+ /* Set up initial mode */
+ if (sc->shasta)
+ sc->pioconf[0] = sc->pioconf[1] = pio_timing_shasta[4];
+ else
+ sc->pioconf[0] = sc->pioconf[1] = pio_timing_kauai[4];
+
+ sc->udmaconf[0] = sc->udmaconf[1] = 0;
+ sc->wdmaconf[0] = sc->wdmaconf[1] = 0;
+
+ bus_write_4(sc->sc_memr, PIO_CONFIG_REG, sc->pioconf[0]);
+
+ /* Magic FCR value from Apple */
+ bus_write_4(sc->sc_memr, 0, 0x00000007);
+
+ /* Set begin_transaction */
+ sc->sc_ch.sc_ch.hw.begin_transaction = ata_kauai_begin_transaction;
+
+ return ata_attach(dev);
+}
+
static void
ata_kauai_setmode(device_t parent, device_t dev)
{
struct ata_device *atadev = device_get_softc(dev);
+ struct ata_kauai_softc *sc = device_get_softc(parent);
+ uint32_t mode;
+
+ mode = ata_limit_mode(dev,atadev->mode,
+ (sc->shasta) ? ATA_UDMA6 : ATA_UDMA5);
+
+ if (ata_controlcmd(dev, ATA_SETFEATURES, ATA_SF_SETXFER, 0, mode))
+ return;
+
+ atadev->mode = mode;
+
+ if (sc->shasta) {
+ switch (mode & ATA_DMA_MASK) {
+ case ATA_UDMA0:
+ sc->udmaconf[atadev->unit]
+ = udma_timing_shasta[mode & ATA_MODE_MASK];
+ break;
+ case ATA_WDMA0:
+ sc->udmaconf[atadev->unit] = 0;
+ sc->wdmaconf[atadev->unit]
+ = dma_timing_shasta[mode & ATA_MODE_MASK];
+ break;
+ default:
+ sc->pioconf[atadev->unit]
+ = pio_timing_shasta[(mode & ATA_MODE_MASK) -
+ ATA_PIO0];
+ break;
+ }
+ } else {
+ switch (mode & ATA_DMA_MASK) {
+ case ATA_UDMA0:
+ sc->udmaconf[atadev->unit]
+ = udma_timing_kauai[mode & ATA_MODE_MASK];
+ break;
+ case ATA_WDMA0:
+ sc->udmaconf[atadev->unit] = 0;
+ sc->wdmaconf[atadev->unit]
+ = dma_timing_kauai[mode & ATA_MODE_MASK];
+ break;
+ default:
+ sc->pioconf[atadev->unit]
+ = pio_timing_kauai[(mode & ATA_MODE_MASK)
+ - ATA_PIO0];
+ break;
+ }
+ }
+}
+
+static int
+ata_kauai_begin_transaction(struct ata_request *request)
+{
+ struct ata_device *atadev = device_get_softc(request->dev);
+ struct ata_kauai_softc *sc = device_get_softc(request->parent);
- /* TODO bang kauai speed register */
- atadev->mode = ATA_PIO;
+ bus_write_4(sc->sc_memr, UDMA_CONFIG_REG, sc->udmaconf[atadev->unit]);
+ bus_write_4(sc->sc_memr, PIO_CONFIG_REG,
+ sc->wdmaconf[atadev->unit] | sc->pioconf[atadev->unit]);
+
+ return ata_begin_transaction(request);
}
+
diff --git a/sys/powerpc/powermac/ata_macio.c b/sys/powerpc/powermac/ata_macio.c
index f582a5c..5d1e211 100644
--- a/sys/powerpc/powermac/ata_macio.c
+++ b/sys/powerpc/powermac/ata_macio.c
@@ -50,6 +50,8 @@
#include <dev/ofw/ofw_bus.h>
+#include "ata_dbdma.h"
+
/*
* Offset to control registers from base
*/
@@ -61,25 +63,84 @@
#define ATA_MACIO_REGGAP 16
/*
+ * Whether or not to bind to the DBDMA IRQ
+ */
+#define USE_DBDMA_IRQ 0
+
+/*
+ * Timing register
+ */
+#define ATA_MACIO_TIMINGREG 0x200
+
+#define ATA_TIME_TO_TICK(rev,time) howmany(time, (rev == 4) ? 15 : 30)
+#define PIO_REC_OFFSET 4
+#define PIO_REC_MIN 1
+#define PIO_ACT_MIN 1
+#define DMA_REC_OFFSET 1
+#define DMA_REC_MIN 1
+#define DMA_ACT_MIN 1
+
+struct ide_timings {
+ int cycle; /* minimum cycle time [ns] */
+ int active; /* minimum command active time [ns] */
+};
+
+struct ide_timings pio_timings[5] = {
+ { 600, 180 }, /* PIO 0 */
+ { 390, 150 }, /* PIO 1 */
+ { 240, 105 }, /* PIO 2 */
+ { 180, 90 }, /* PIO 3 */
+ { 120, 75 } /* PIO 4 */
+};
+
+static const struct ide_timings dma_timings[3] = {
+ { 480, 240 }, /* WDMA 0 */
+ { 165, 90 }, /* WDMA 1 */
+ { 120, 75 } /* WDMA 2 */
+};
+
+static const struct ide_timings udma_timings[5] = {
+ { 120, 180 }, /* UDMA 0 */
+ { 90, 150 }, /* UDMA 1 */
+ { 60, 120 }, /* UDMA 2 */
+ { 45, 90 }, /* UDMA 3 */
+ { 30, 90 } /* UDMA 4 */
+};
+
+/*
* Define the macio ata bus attachment.
*/
static int ata_macio_probe(device_t dev);
static void ata_macio_setmode(device_t parent, device_t dev);
+static int ata_macio_attach(device_t dev);
+static int ata_macio_begin_transaction(struct ata_request *request);
static device_method_t ata_macio_methods[] = {
/* Device interface */
DEVMETHOD(device_probe, ata_macio_probe),
- DEVMETHOD(device_attach, ata_attach),
+ DEVMETHOD(device_attach, ata_macio_attach),
/* ATA interface */
DEVMETHOD(ata_setmode, ata_macio_setmode),
{ 0, 0 }
};
+struct ata_macio_softc {
+ struct ata_dbdma_channel sc_ch;
+
+ int rev;
+ int max_mode;
+ struct resource *sc_mem;
+
+ uint32_t udmaconf[2];
+ uint32_t wdmaconf[2];
+ uint32_t pioconf[2];
+};
+
static driver_t ata_macio_driver = {
"ata",
ata_macio_methods,
- sizeof(struct ata_channel),
+ sizeof(struct ata_macio_softc),
};
DRIVER_MODULE(ata, macio, ata_macio_driver, ata_devclass, 0, 0);
@@ -89,20 +150,31 @@ static int
ata_macio_probe(device_t dev)
{
const char *type = ofw_bus_get_type(dev);
+ const char *name = ofw_bus_get_name(dev);
+ struct ata_macio_softc *sc;
struct ata_channel *ch;
- struct resource *mem;
int rid, i;
if (strcmp(type, "ata") != 0 &&
strcmp(type, "ide") != 0)
return (ENXIO);
- ch = device_get_softc(dev);
- bzero(ch, sizeof(struct ata_channel));
+ sc = device_get_softc(dev);
+ bzero(sc, sizeof(struct ata_macio_softc));
+ ch = &sc->sc_ch.sc_ch;
+
+ if (strcmp(name,"ata-4") == 0) {
+ sc->rev = 4;
+ sc->max_mode = ATA_UDMA4;
+ } else {
+ sc->rev = 3;
+ sc->max_mode = ATA_WDMA2;
+ }
rid = 0;
- mem = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, RF_ACTIVE);
- if (mem == NULL) {
+ sc->sc_mem = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid,
+ RF_ACTIVE);
+ if (sc->sc_mem == NULL) {
device_printf(dev, "could not allocate memory\n");
return (ENXIO);
}
@@ -111,10 +183,10 @@ ata_macio_probe(device_t dev)
* Set up the resource vectors
*/
for (i = ATA_DATA; i <= ATA_COMMAND; i++) {
- ch->r_io[i].res = mem;
+ ch->r_io[i].res = sc->sc_mem;
ch->r_io[i].offset = i * ATA_MACIO_REGGAP;
}
- ch->r_io[ATA_CONTROL].res = mem;
+ ch->r_io[ATA_CONTROL].res = sc->sc_mem;
ch->r_io[ATA_CONTROL].offset = ATA_MACIO_ALTOFFSET;
ata_default_registers(dev);
@@ -125,12 +197,143 @@ ata_macio_probe(device_t dev)
return (ata_probe(dev));
}
+static int
+ata_macio_attach(device_t dev)
+{
+ struct ata_macio_softc *sc = device_get_softc(dev);
+ uint32_t timingreg;
+
+#if USE_DBDMA_IRQ
+ int dbdma_irq_rid = 1;
+ struct resource *dbdma_irq;
+ void *cookie;
+#endif
+
+ /* Init DMA engine */
+
+ sc->sc_ch.dbdma_rid = 1;
+ sc->sc_ch.dbdma_regs = bus_alloc_resource_any(dev, SYS_RES_MEMORY,
+ &sc->sc_ch.dbdma_rid, RF_ACTIVE);
+
+ ata_dbdma_dmainit(dev);
+
+ /* Configure initial timings */
+ timingreg = bus_read_4(sc->sc_mem, ATA_MACIO_TIMINGREG);
+ if (sc->rev == 4) {
+ sc->udmaconf[0] = sc->udmaconf[1] = timingreg & 0x1ff00000;
+ sc->wdmaconf[0] = sc->wdmaconf[1] = timingreg & 0x001ffc00;
+ sc->pioconf[0] = sc->pioconf[1] = timingreg & 0x000003ff;
+ } else {
+ sc->udmaconf[0] = sc->udmaconf[1] = 0;
+ sc->wdmaconf[0] = sc->wdmaconf[1] = timingreg & 0xfffff800;
+ sc->pioconf[0] = sc->pioconf[1] = timingreg & 0x000007ff;
+ }
+
+#if USE_DBDMA_IRQ
+ /* Bind to DBDMA interrupt as well */
+
+ if ((dbdma_irq = bus_alloc_resource_any(dev, SYS_RES_IRQ,
+ &dbdma_irq_rid, RF_SHAREABLE | RF_ACTIVE)) != NULL) {
+ bus_setup_intr(dev, dbdma_irq, ATA_INTR_FLAGS, NULL,
+ (driver_intr_t *)ata_interrupt, sc,&cookie);
+ }
+#endif
+
+ /* Set begin_transaction */
+ sc->sc_ch.sc_ch.hw.begin_transaction = ata_macio_begin_transaction;
+
+ return ata_attach(dev);
+}
+
static void
ata_macio_setmode(device_t parent, device_t dev)
{
struct ata_device *atadev = device_get_softc(dev);
+ struct ata_macio_softc *sc = device_get_softc(parent);
+ int mode = atadev->mode;
+
+ int min_cycle = 0, min_active = 0;
+ int cycle_tick = 0, act_tick = 0, inact_tick = 0, half_tick;
+
+ mode = ata_limit_mode(dev, mode, sc->max_mode);
+
+ if (ata_controlcmd(dev, ATA_SETFEATURES, ATA_SF_SETXFER, 0, mode))
+ return;
+
+ atadev->mode = mode;
+
+ if ((mode & ATA_DMA_MASK) == ATA_UDMA0) {
+ min_cycle = udma_timings[mode & ATA_MODE_MASK].cycle;
+ min_active = udma_timings[mode & ATA_MODE_MASK].active;
+
+ cycle_tick = ATA_TIME_TO_TICK(sc->rev,min_cycle);
+ act_tick = ATA_TIME_TO_TICK(sc->rev,min_active);
+
+ /* mask: 0x1ff00000 */
+ sc->udmaconf[atadev->unit] =
+ (cycle_tick << 21) | (act_tick << 25) | 0x100000;
+ } else if ((mode & ATA_DMA_MASK) == ATA_WDMA0) {
+ min_cycle = dma_timings[mode & ATA_MODE_MASK].cycle;
+ min_active = dma_timings[mode & ATA_MODE_MASK].active;
+
+ cycle_tick = ATA_TIME_TO_TICK(sc->rev,min_cycle);
+ act_tick = ATA_TIME_TO_TICK(sc->rev,min_active);
+
+ if (sc->rev == 4) {
+ inact_tick = cycle_tick - act_tick;
+ /* mask: 0x001ffc00 */
+ sc->wdmaconf[atadev->unit] =
+ (act_tick << 10) | (inact_tick << 15);
+ } else {
+ inact_tick = cycle_tick - act_tick - DMA_REC_OFFSET;
+ if (inact_tick < DMA_REC_MIN)
+ inact_tick = DMA_REC_MIN;
+ half_tick = 0; /* XXX */
+
+ /* mask: 0xfffff800 */
+ sc->wdmaconf[atadev->unit] = (half_tick << 21)
+ | (inact_tick << 16) | (act_tick << 11);
+ }
+ } else {
+ min_cycle =
+ pio_timings[(mode & ATA_MODE_MASK) - ATA_PIO0].cycle;
+ min_active =
+ pio_timings[(mode & ATA_MODE_MASK) - ATA_PIO0].active;
+
+ cycle_tick = ATA_TIME_TO_TICK(sc->rev,min_cycle);
+ act_tick = ATA_TIME_TO_TICK(sc->rev,min_active);
+
+ if (sc->rev == 4) {
+ inact_tick = cycle_tick - act_tick;
+
+ /* mask: 0x000003ff */
+ sc->pioconf[atadev->unit] =
+ (inact_tick << 5) | act_tick;
+ } else {
+ if (act_tick < PIO_ACT_MIN)
+ act_tick = PIO_ACT_MIN;
+
+ inact_tick = cycle_tick - act_tick - PIO_REC_OFFSET;
+ if (inact_tick < PIO_REC_MIN)
+ inact_tick = PIO_REC_MIN;
+
+ /* mask: 0x000007ff */
+ sc->pioconf[atadev->unit] =
+ (inact_tick << 5) | act_tick;
+ }
+ }
+}
+
+static int
+ata_macio_begin_transaction(struct ata_request *request)
+{
+ struct ata_device *atadev = device_get_softc(request->dev);
+ struct ata_macio_softc *sc = device_get_softc(request->parent);
+
+ bus_write_4(sc->sc_mem, ATA_MACIO_TIMINGREG,
+ sc->udmaconf[atadev->unit] | sc->wdmaconf[atadev->unit]
+ | sc->pioconf[atadev->unit]);
- /* TODO bang macio speed register */
- atadev->mode = ATA_PIO;
+ return ata_begin_transaction(request);
}
OpenPOWER on IntegriCloud