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:
parent
e927c2b2e6
commit
b7382e099d
Notes:
svn2git
2020-12-20 02:59:44 +00:00
svn path=/head/; revision=183409
@ -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
|
||||
|
282
sys/powerpc/powermac/ata_dbdma.c
Normal file
282
sys/powerpc/powermac/ata_dbdma.c
Normal 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);
|
||||
}
|
||||
|
55
sys/powerpc/powermac/ata_dbdma.h
Normal file
55
sys/powerpc/powermac/ata_dbdma.h
Normal 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 */
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user