1
0
mirror of https://git.FreeBSD.org/src.git synced 2025-01-21 15:45:02 +00:00

Add DMA support for Apple built-in ATA controllers.

Tested by:	grehan, marcotrillo@gmail.com
MFC after:	1 month
This commit is contained in:
Nathan Whitehorn 2008-09-27 15:13:44 +00:00
parent e927c2b2e6
commit b7382e099d
Notes: svn2git 2020-12-20 02:59:44 +00:00
svn path=/head/; revision=183409
5 changed files with 796 additions and 34 deletions

View File

@ -109,6 +109,7 @@ powerpc/ofw/ofw_pcib_pci.c optional pci aim
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

View File

@ -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);
}

View File

@ -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 */

View File

@ -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
@ -66,16 +71,27 @@ __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;
/* 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);
}

View File

@ -50,6 +50,8 @@
#include <dev/ofw/ofw_bus.h>
#include "ata_dbdma.h"
/*
* Offset to control registers from base
*/
@ -60,26 +62,85 @@
*/
#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;
/* 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);
}