Index: powerpc/powermac/ata_dbdma.c =================================================================== --- powerpc/powermac/ata_dbdma.c (revision 0) +++ powerpc/powermac/ata_dbdma.c (revision 0) @@ -0,0 +1,280 @@ +/*- + * 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: head/sys/powerpc/powermac/ata_dbdma.c 145772 2005-05-01 13:11:29Z grehan $ + */ + +/* + * Mac-io ATA controller + */ +#include "opt_ata.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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); +} + Property changes on: powerpc/powermac/ata_dbdma.c ___________________________________________________________________ Added: svn:mergeinfo Index: powerpc/powermac/ata_dbdma.h =================================================================== --- powerpc/powermac/ata_dbdma.h (revision 0) +++ powerpc/powermac/ata_dbdma.h (revision 0) @@ -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: head/sys/powerpc/powermac/ata_macio.c 145772 2005-05-01 13:11:29Z grehan $ + */ + +#ifndef ATA_DBDMA_H +#define ATA_DBDMA_H + +#include +#include +#include + +#include + +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 */ + Property changes on: powerpc/powermac/ata_dbdma.h ___________________________________________________________________ Added: svn:mergeinfo Index: powerpc/powermac/ata_kauai.c =================================================================== --- powerpc/powermac/ata_kauai.c (revision 183319) +++ powerpc/powermac/ata_kauai.c (working copy) @@ -50,11 +50,16 @@ #include #include +#include +#include #include #include +#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 @@ #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 @@ { 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 @@ { 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 @@ 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 @@ */ 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 @@ * 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 @@ 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; - /* TODO bang kauai speed register */ - atadev->mode = ATA_PIO; + 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); + + 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); +} + Index: powerpc/powermac/ata_macio.c =================================================================== --- powerpc/powermac/ata_macio.c (revision 183319) +++ powerpc/powermac/ata_macio.c (working copy) @@ -50,6 +50,8 @@ #include +#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 @@ 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 @@ * 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 @@ 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; - /* TODO bang macio speed register */ - atadev->mode = ATA_PIO; + 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]); + + return ata_begin_transaction(request); +} + Index: conf/files.powerpc =================================================================== --- conf/files.powerpc (revision 183319) +++ conf/files.powerpc (working copy) @@ -109,6 +115,7 @@ powerpc/ofw/ofw_syscons.c optional sc aim powerpc/powermac/ata_kauai.c optional powermac ata powerpc/powermac/ata_macio.c optional powermac ata +powerpc/powermac/ata_dbdma.c optional powermac ata powerpc/powermac/dbdma.c optional powermac pci powerpc/powermac/grackle.c optional powermac pci powerpc/powermac/hrowpic.c optional powermac pci