473 lines
12 KiB
C
473 lines
12 KiB
C
|
/*-
|
||
|
* Copyright (c) 2001 Brian Somers <brian@Awfulhak.org>
|
||
|
* All rights reserved.
|
||
|
*
|
||
|
* Redistribution and use in source and binary forms, with or without
|
||
|
* modification, are permitted provided that the following conditions
|
||
|
* are met:
|
||
|
* 1. Redistributions of source code must retain the above copyright
|
||
|
* notice, this list of conditions and the following disclaimer.
|
||
|
* 2. Redistributions in binary form must reproduce the above copyright
|
||
|
* notice, this list of conditions and the following disclaimer in the
|
||
|
* documentation and/or other materials provided with the distribution.
|
||
|
*
|
||
|
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
|
||
|
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||
|
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
|
||
|
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||
|
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||
|
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||
|
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||
|
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||
|
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||
|
* SUCH DAMAGE.
|
||
|
*
|
||
|
* $FreeBSD$
|
||
|
*/
|
||
|
|
||
|
/*-
|
||
|
* TODO:
|
||
|
* Figure out how to make the non-Xi boards use memory addresses other
|
||
|
* than 0xd0000 !!!
|
||
|
*/
|
||
|
|
||
|
#include <sys/param.h>
|
||
|
|
||
|
#include <sys/systm.h>
|
||
|
#include <sys/kernel.h>
|
||
|
#include <sys/tty.h>
|
||
|
#include <sys/syslog.h>
|
||
|
#include <sys/bus.h>
|
||
|
#include <machine/bus.h>
|
||
|
#include <sys/rman.h>
|
||
|
#include <machine/resource.h>
|
||
|
#include <vm/vm.h>
|
||
|
#include <vm/pmap.h>
|
||
|
|
||
|
#include <digi/digireg.h>
|
||
|
#include <digi/digiio.h>
|
||
|
#include <digi/digi.h>
|
||
|
|
||
|
/* Valid i/o addresses are any of these with either 0 or 4 added */
|
||
|
static u_long digi_validio[] = {
|
||
|
0x100, 0x110, 0x120, 0x200, 0x220, 0x300, 0x320
|
||
|
};
|
||
|
#define DIGI_NVALIDIO (sizeof(digi_validio) / sizeof(digi_validio[0]))
|
||
|
#define IO_SIZE 0x04
|
||
|
|
||
|
static u_long digi_validmem[] = {
|
||
|
0x80000, 0x88000, 0x90000, 0x98000, 0xa0000, 0xa8000, 0xb0000, 0xb8000,
|
||
|
0xc0000, 0xc8000, 0xd0000, 0xd8000, 0xe0000, 0xe8000, 0xf0000, 0xf8000,
|
||
|
0xf0000000, 0xf1000000, 0xf2000000, 0xf3000000, 0xf4000000, 0xf5000000,
|
||
|
0xf6000000, 0xf7000000, 0xf8000000, 0xf9000000, 0xfa000000, 0xfb000000,
|
||
|
0xfc000000, 0xfd000000, 0xfe000000, 0xff000000
|
||
|
};
|
||
|
#define DIGI_NVALIDMEM (sizeof(digi_validmem) / sizeof(digi_validmem[0]))
|
||
|
|
||
|
static u_char *
|
||
|
digi_isa_setwin(struct digi_softc *sc, unsigned int addr)
|
||
|
{
|
||
|
outb(sc->wport, sc->window = FEPWIN | (addr >> sc->win_bits));
|
||
|
return (sc->vmem + (addr % sc->win_size));
|
||
|
}
|
||
|
|
||
|
static u_char *
|
||
|
digi_xi_setwin(struct digi_softc *sc, unsigned int addr)
|
||
|
{
|
||
|
outb(sc->wport, sc->window = FEPMEM);
|
||
|
return (sc->vmem + addr);
|
||
|
}
|
||
|
|
||
|
static void
|
||
|
digi_isa_hidewin(struct digi_softc *sc)
|
||
|
{
|
||
|
outb(sc->wport, sc->window = 0);
|
||
|
/* outb(sc->port, 0); */
|
||
|
}
|
||
|
|
||
|
static void
|
||
|
digi_isa_towin(struct digi_softc *sc, int win)
|
||
|
{
|
||
|
outb(sc->wport, sc->window = win);
|
||
|
}
|
||
|
|
||
|
static void
|
||
|
digi_xi_towin(struct digi_softc *sc, int win)
|
||
|
{
|
||
|
outb(sc->wport, sc->window = FEPMEM);
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* sc->port should be set and its resource allocated.
|
||
|
*/
|
||
|
static int
|
||
|
digi_isa_check(struct digi_softc *sc)
|
||
|
{
|
||
|
int i, ident;
|
||
|
|
||
|
sc->name = NULL;
|
||
|
|
||
|
/* Invasive probe - reset the card */
|
||
|
outb(sc->port, FEPRST);
|
||
|
for (i = 1000; DELAY(1), i >= 0; i--)
|
||
|
if ((inb(sc->port) & FEPMASK) == FEPRST)
|
||
|
break;
|
||
|
if (i == -1)
|
||
|
return (NULL);
|
||
|
DLOG(DIGIDB_INIT, (sc->dev, "got reset after %d us\n", 1000 - i));
|
||
|
|
||
|
ident = inb(sc->port);
|
||
|
|
||
|
/*
|
||
|
* NOTE, this probe is all wrong. I haven't got the data sheets !
|
||
|
*/
|
||
|
|
||
|
DLOG(DIGIDB_INIT, (sc->dev, "board type is 0x%x\n", ident));
|
||
|
if (ident & 0x1) {
|
||
|
switch (ident) {
|
||
|
case 0x05:
|
||
|
case 0x15:
|
||
|
case 0x25:
|
||
|
case 0x35:
|
||
|
sc->model = PCXI;
|
||
|
sc->csigs = &digi_xixe_signals;
|
||
|
switch (ident & 0x30) {
|
||
|
case 0:
|
||
|
sc->name = "Digiboard PC/Xi 64K";
|
||
|
sc->mem_seg = 0xf000;
|
||
|
sc->win_size = 0x10000;
|
||
|
sc->win_bits = 16;
|
||
|
break;
|
||
|
case 0x10:
|
||
|
sc->name = "Digiboard PC/Xi 128K";
|
||
|
sc->mem_seg = 0xE000;
|
||
|
sc->win_size = 0x20000;
|
||
|
sc->win_bits = 17;
|
||
|
break;
|
||
|
case 0x20:
|
||
|
sc->name = "Digiboard PC/Xi 256K";
|
||
|
sc->mem_seg = 0xC000;
|
||
|
sc->win_size = 0x40000;
|
||
|
sc->win_bits = 18;
|
||
|
break;
|
||
|
case 0x30:
|
||
|
sc->name = "Digiboard PC/Xi 512K";
|
||
|
sc->mem_seg = 0x8000;
|
||
|
sc->win_size = 0x80000;
|
||
|
sc->win_bits = 19;
|
||
|
break;
|
||
|
}
|
||
|
sc->wport = sc->port;
|
||
|
sc->module = "Xe";
|
||
|
|
||
|
sc->setwin = digi_xi_setwin;
|
||
|
sc->hidewin = digi_isa_hidewin;
|
||
|
sc->towin = digi_xi_towin;
|
||
|
break;
|
||
|
|
||
|
case 0xf5:
|
||
|
sc->name = "Digiboard PC/Xem";
|
||
|
sc->model = PCXEM;
|
||
|
sc->csigs = &digi_normal_signals;
|
||
|
sc->win_size = 0x8000;
|
||
|
sc->win_bits = 15;
|
||
|
sc->wport = sc->port + 1;
|
||
|
sc->module = "Xem";
|
||
|
|
||
|
sc->setwin = digi_isa_setwin;
|
||
|
sc->hidewin = digi_isa_hidewin;
|
||
|
sc->towin = digi_isa_towin;
|
||
|
break;
|
||
|
}
|
||
|
} else {
|
||
|
outb(sc->port, 1);
|
||
|
ident = inb(sc->port);
|
||
|
|
||
|
if (ident & 0x1) {
|
||
|
device_printf(sc->dev, "PC/Xm is unsupported\n");
|
||
|
return (NULL);
|
||
|
}
|
||
|
|
||
|
sc->mem_seg = 0xf000;
|
||
|
|
||
|
if (!(ident & 0xc0)) {
|
||
|
sc->name = "Digiboard PC/Xe 64K";
|
||
|
sc->model = PCXE;
|
||
|
sc->csigs = &digi_xixe_signals;
|
||
|
sc->win_size = 0x10000;
|
||
|
sc->win_bits = 16;
|
||
|
sc->wport = sc->port;
|
||
|
} else {
|
||
|
sc->name = "Digiboard PC/Xe 64/8K (windowed)";
|
||
|
sc->model = PCXEVE;
|
||
|
sc->csigs = &digi_normal_signals;
|
||
|
sc->win_size = 0x2000;
|
||
|
sc->win_bits = 13;
|
||
|
sc->wport = sc->port + 1;
|
||
|
}
|
||
|
sc->module = "Xe";
|
||
|
|
||
|
sc->setwin = digi_isa_setwin;
|
||
|
sc->hidewin = digi_isa_hidewin;
|
||
|
sc->towin = digi_isa_towin;
|
||
|
}
|
||
|
|
||
|
return (sc->name != NULL);
|
||
|
}
|
||
|
|
||
|
static int
|
||
|
digi_isa_probe(device_t dev)
|
||
|
{
|
||
|
struct digi_softc *sc = device_get_softc(dev);
|
||
|
int i;
|
||
|
|
||
|
KASSERT(sc, ("digi%d: softc not allocated in digi_isa_probe\n",
|
||
|
device_get_unit(dev)));
|
||
|
|
||
|
bzero(sc, sizeof(*sc));
|
||
|
sc->status = DIGI_STATUS_NOTINIT;
|
||
|
sc->dev = dev;
|
||
|
sc->res.unit = device_get_unit(dev);
|
||
|
if (sc->res.unit >= 16) {
|
||
|
/* Don't overflow our control mask */
|
||
|
device_printf(dev, "At most 16 digiboards may be used\n");
|
||
|
return (ENXIO);
|
||
|
}
|
||
|
DLOG(DIGIDB_INIT, (sc->dev, "probing on isa bus\n"));
|
||
|
|
||
|
/* Check that we've got a valid i/o address */
|
||
|
if ((sc->port = bus_get_resource_start(dev, SYS_RES_IOPORT, 0)) == 0) {
|
||
|
DLOG(DIGIDB_INIT, (sc->dev, "io address not given\n"));
|
||
|
return (ENXIO);
|
||
|
}
|
||
|
for (i = 0; i < DIGI_NVALIDIO; i++)
|
||
|
if (sc->port == digi_validio[i] ||
|
||
|
sc->port == digi_validio[i] + 4)
|
||
|
break;
|
||
|
if (i == DIGI_NVALIDIO) {
|
||
|
device_printf(dev, "0x%03x: Invalid i/o address\n", sc->port);
|
||
|
return (ENXIO);
|
||
|
}
|
||
|
|
||
|
/* Ditto for our memory address */
|
||
|
if ((sc->pmem = bus_get_resource_start(dev, SYS_RES_MEMORY, 0)) == 0)
|
||
|
return (ENXIO);
|
||
|
for (i = 0; i < DIGI_NVALIDMEM; i++)
|
||
|
if (sc->pmem == digi_validmem[i])
|
||
|
break;
|
||
|
if (i == DIGI_NVALIDMEM) {
|
||
|
device_printf(dev, "0x%lx: Invalid memory address\n", sc->pmem);
|
||
|
return (ENXIO);
|
||
|
}
|
||
|
if ((sc->pmem & 0xfffffful) != sc->pmem) {
|
||
|
device_printf(dev, "0x%lx: Memory address not supported\n",
|
||
|
sc->pmem);
|
||
|
return (ENXIO);
|
||
|
}
|
||
|
sc->vmem = (u_char *)sc->pmem;
|
||
|
|
||
|
DLOG(DIGIDB_INIT, (sc->dev, "isa? port 0x%03x mem 0x%lx\n",
|
||
|
sc->port, sc->pmem));
|
||
|
|
||
|
/* Temporarily map our io ports */
|
||
|
sc->res.iorid = 0;
|
||
|
sc->res.io = bus_alloc_resource(dev, SYS_RES_IOPORT, &sc->res.iorid,
|
||
|
0ul, ~0ul, IO_SIZE, RF_ACTIVE);
|
||
|
if (sc->res.io == NULL)
|
||
|
return (ENXIO);
|
||
|
|
||
|
/* Check the type of card and get internal memory characteristics */
|
||
|
if (!digi_isa_check(sc)) {
|
||
|
bus_release_resource(dev, SYS_RES_IOPORT, sc->res.iorid,
|
||
|
sc->res.io);
|
||
|
return (ENXIO);
|
||
|
}
|
||
|
|
||
|
/* Temporarily map our memory */
|
||
|
sc->res.mrid = 0;
|
||
|
sc->res.mem = bus_alloc_resource(dev, SYS_RES_MEMORY, &sc->res.mrid,
|
||
|
0ul, ~0ul, sc->win_size, RF_ALLOCATED);
|
||
|
if (sc->res.mem == NULL) {
|
||
|
device_printf(dev, "0x%lx: Memory range is in use\n", sc->pmem);
|
||
|
bus_release_resource(dev, SYS_RES_IOPORT, sc->res.iorid,
|
||
|
sc->res.io);
|
||
|
return (ENXIO);
|
||
|
}
|
||
|
|
||
|
outb(sc->port, FEPCLR); /* drop RESET */
|
||
|
sc->hidewin(sc); /* set initial sc->window */
|
||
|
|
||
|
bus_release_resource(dev, SYS_RES_MEMORY, sc->res.mrid, sc->res.mem);
|
||
|
bus_release_resource(dev, SYS_RES_IOPORT, sc->res.iorid, sc->res.io);
|
||
|
|
||
|
/* Let digi_isa_attach() know what we've found */
|
||
|
bus_set_resource(dev, SYS_RES_IOPORT, 0, sc->port, IO_SIZE);
|
||
|
bus_set_resource(dev, SYS_RES_MEMORY, 0, sc->pmem, sc->win_size);
|
||
|
|
||
|
DLOG(DIGIDB_INIT, (sc->dev, "Probe returns -10\n"));
|
||
|
|
||
|
return (-10); /* Other drivers are preferred for now */
|
||
|
}
|
||
|
|
||
|
static int
|
||
|
digi_isa_attach(device_t dev)
|
||
|
{
|
||
|
struct digi_softc *sc = device_get_softc(dev);
|
||
|
int i, t, res;
|
||
|
u_char *ptr;
|
||
|
int reset;
|
||
|
u_long msize, iosize;
|
||
|
long scport;
|
||
|
|
||
|
KASSERT(sc, ("digi%d: softc not allocated in digi_isa_attach\n",
|
||
|
device_get_unit(dev)));
|
||
|
|
||
|
res = ENXIO;
|
||
|
bzero(sc, sizeof(*sc));
|
||
|
sc->status = DIGI_STATUS_NOTINIT;
|
||
|
sc->dev = dev;
|
||
|
sc->res.unit = device_get_unit(dev);
|
||
|
DLOG(DIGIDB_INIT, (sc->dev, "attaching\n"));
|
||
|
|
||
|
bus_get_resource(dev, SYS_RES_IOPORT, 0, &scport, &iosize);
|
||
|
bus_get_resource(dev, SYS_RES_MEMORY, 0, &sc->pmem, &msize);
|
||
|
sc->port = scport;
|
||
|
/* sc->altpin = !!(device_get_flags(dev) & DGBFLAG_ALTPIN); */
|
||
|
|
||
|
/* Allocate resources (verified in digi_isa_probe()) */
|
||
|
sc->res.iorid = 0;
|
||
|
sc->res.io = bus_alloc_resource(dev, SYS_RES_IOPORT, &sc->res.iorid,
|
||
|
0ul, ~0ul, iosize, RF_ACTIVE);
|
||
|
if (sc->res.io == NULL)
|
||
|
return (ENXIO);
|
||
|
|
||
|
/* Check the type of card and get internal memory characteristics */
|
||
|
DLOG(DIGIDB_INIT, (sc->dev, "Checking card type\n"));
|
||
|
if (!digi_isa_check(sc))
|
||
|
goto failed;
|
||
|
|
||
|
callout_handle_init(&sc->callout);
|
||
|
callout_handle_init(&sc->inttest);
|
||
|
|
||
|
sc->res.mrid = 0;
|
||
|
sc->res.mem = bus_alloc_resource(dev, SYS_RES_MEMORY, &sc->res.mrid,
|
||
|
0ul, ~0ul, msize, RF_ACTIVE);
|
||
|
if (sc->res.mem == NULL) {
|
||
|
device_printf(dev, "0x%lx: Memory range is in use\n", sc->pmem);
|
||
|
sc->hidewin(sc);
|
||
|
goto failed;
|
||
|
}
|
||
|
|
||
|
/* map memory */
|
||
|
sc->vmem = pmap_mapdev(sc->pmem, msize);
|
||
|
|
||
|
DLOG(DIGIDB_INIT, (sc->dev, "internal memory segment 0x%x\n",
|
||
|
sc->mem_seg));
|
||
|
|
||
|
/* Start by resetting the card */
|
||
|
reset = FEPRST;
|
||
|
if (sc->model == PCXI)
|
||
|
reset |= FEPMEM;
|
||
|
|
||
|
outb(sc->port, reset);
|
||
|
for (i = 10; DELAY(1), i >= 0; i--) {
|
||
|
if ((inb(sc->port) & FEPMASK) == reset)
|
||
|
break;
|
||
|
tsleep(sc, PUSER | PCATCH, "digirst0", hz / 5);
|
||
|
}
|
||
|
if (i == -1) {
|
||
|
device_printf(dev, "1st reset failed\n");
|
||
|
sc->hidewin(sc);
|
||
|
goto failed;
|
||
|
}
|
||
|
DLOG(DIGIDB_INIT, (sc->dev, "got reset after %d us\n",
|
||
|
(10 - i) * (hz / 5)));
|
||
|
|
||
|
if (sc->model != PCXI) {
|
||
|
t = (sc->pmem >> 8) & 0xffe0;
|
||
|
if (sc->model == PCXEVE)
|
||
|
t |= 0x10; /* enable windowing */
|
||
|
outb(sc->port + 2, t & 0xff);
|
||
|
outb(sc->port + 3, t >> 8);
|
||
|
}
|
||
|
|
||
|
if (sc->model == PCXI || sc->model == PCXE) {
|
||
|
outb(sc->port, FEPRST | FEPMEM);
|
||
|
for (i = 10000; DELAY(1), i >= 0; i--)
|
||
|
if ((inb(sc->port) & FEPMASK) != FEPRST)
|
||
|
break;
|
||
|
if (i == -1) {
|
||
|
device_printf(dev, "2nd reset failed (0x%02x)\n",
|
||
|
inb(sc->port));
|
||
|
sc->hidewin(sc);
|
||
|
goto failed;
|
||
|
}
|
||
|
DLOG(DIGIDB_INIT, (sc->dev, "got memory after %d us\n",
|
||
|
10000 - i));
|
||
|
}
|
||
|
|
||
|
DLOG(DIGIDB_INIT, (sc->dev, "short memory test\n"));
|
||
|
ptr = sc->setwin(sc, BOTWIN);
|
||
|
vD(ptr) = 0xA55A3CC3;
|
||
|
if (vD(ptr) != 0xA55A3CC3) {
|
||
|
device_printf(dev, "1st memory test failed\n");
|
||
|
sc->hidewin(sc);
|
||
|
goto failed;
|
||
|
}
|
||
|
DLOG(DIGIDB_INIT, (sc->dev, "1st memory test ok\n"));
|
||
|
|
||
|
ptr = sc->setwin(sc, TOPWIN);
|
||
|
vD(ptr) = 0x5AA5C33C;
|
||
|
if (vD(ptr) != 0x5AA5C33C) {
|
||
|
device_printf(dev, "2nd memory test failed\n");
|
||
|
sc->hidewin(sc);
|
||
|
goto failed;
|
||
|
}
|
||
|
DLOG(DIGIDB_INIT, (sc->dev, "2nd memory test ok\n"));
|
||
|
|
||
|
ptr = sc->setwin(sc, BIOSCODE + ((0xf000 - sc->mem_seg) << 4));
|
||
|
vD(ptr) = 0x5AA5C33C;
|
||
|
if (vD(ptr) != 0x5AA5C33C) {
|
||
|
device_printf(dev, "3rd (BIOS) memory test failed\n");
|
||
|
sc->hidewin(sc);
|
||
|
goto failed;
|
||
|
}
|
||
|
DLOG(DIGIDB_INIT, (sc->dev, "3rd memory test ok\n"));
|
||
|
|
||
|
if ((res = digi_attach(sc)) == 0)
|
||
|
return (0);
|
||
|
|
||
|
failed:
|
||
|
if (sc->res.mem != NULL) {
|
||
|
bus_release_resource(dev, SYS_RES_MEMORY, sc->res.mrid,
|
||
|
sc->res.mem);
|
||
|
sc->res.mem = NULL;
|
||
|
}
|
||
|
if (sc->res.io != NULL) {
|
||
|
bus_release_resource(dev, SYS_RES_IOPORT, sc->res.iorid,
|
||
|
sc->res.io);
|
||
|
sc->res.io = NULL;
|
||
|
}
|
||
|
|
||
|
return (res);
|
||
|
}
|
||
|
|
||
|
static device_method_t digi_isa_methods[] = {
|
||
|
/* Device interface */
|
||
|
DEVMETHOD(device_probe, digi_isa_probe),
|
||
|
DEVMETHOD(device_attach, digi_isa_attach),
|
||
|
DEVMETHOD(device_detach, digi_detach),
|
||
|
DEVMETHOD(device_shutdown, digi_shutdown),
|
||
|
{0, 0}
|
||
|
};
|
||
|
|
||
|
static driver_t digi_isa_drv = {
|
||
|
"digi",
|
||
|
digi_isa_methods,
|
||
|
sizeof(struct digi_softc),
|
||
|
};
|
||
|
|
||
|
|
||
|
DRIVER_MODULE(digi, isa, digi_isa_drv, digi_devclass, digi_modhandler, 0);
|