summaryrefslogtreecommitdiffstats
path: root/sys/dev/ieee488
diff options
context:
space:
mode:
authorjoerg <joerg@FreeBSD.org>2010-01-23 07:54:06 +0000
committerjoerg <joerg@FreeBSD.org>2010-01-23 07:54:06 +0000
commit5d7561656a2ec6b5511fb174aaef518d2e64eebe (patch)
tree5b5fdbcfdfb289369bab6078ccc092fd5c52c21d /sys/dev/ieee488
parent164ec789c7d4a9e86f802c8f3bfaa46ddb4f0813 (diff)
downloadFreeBSD-src-5d7561656a2ec6b5511fb174aaef518d2e64eebe.zip
FreeBSD-src-5d7561656a2ec6b5511fb174aaef518d2e64eebe.tar.gz
Overhaul of the pcii driver:
. Properly allocate all IO space resources. These cards scatter their IO addresses over a range of 0x1600 bytes, and they require an additional address for "special interrupt handling". . Implement the "special interrupt handling" per the GPIB-PCIIA Technical Reference Manual; this was apparently not declared for the clone card this driver has been originally implemented for, but it turned out to be needed for both, an original NI brand PCII/PCIIA card as well as the Axiom AX5488 clone. . Add some diagnostic messages for various resource allocation etc. failures during probe. . Add some comments about the structure of the IO address space that is used by these cards. MFC after: 1 day
Diffstat (limited to 'sys/dev/ieee488')
-rw-r--r--sys/dev/ieee488/pcii.c102
-rw-r--r--sys/dev/ieee488/upd7210.c41
-rw-r--r--sys/dev/ieee488/upd7210.h3
3 files changed, 120 insertions, 26 deletions
diff --git a/sys/dev/ieee488/pcii.c b/sys/dev/ieee488/pcii.c
index 7e5e23e..fdff61e 100644
--- a/sys/dev/ieee488/pcii.c
+++ b/sys/dev/ieee488/pcii.c
@@ -1,5 +1,6 @@
/*-
* Copyright (c) 2005 Poul-Henning Kamp <phk@FreeBSD.org>
+ * Copyright (c) 2010 Joerg Wunsch <joerg@FreeBSD.org>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -33,6 +34,8 @@
*
* Tested and known working:
* "B&C Microsystems PC488A-0"
+ * "National Instruments GPIB-PCII/PCIIA" (in PCIIa mode)
+ * "Axiom AX5488"
*
*/
@@ -56,7 +59,7 @@ __FBSDID("$FreeBSD$");
struct pcii_softc {
int foo;
- struct resource *res[3];
+ struct resource *res[11];
void *intr_handler;
struct upd7210 upd7210;
};
@@ -79,6 +82,14 @@ static struct resource_spec pcii_res_spec[] = {
{ SYS_RES_IRQ, 0, RF_ACTIVE | RF_SHAREABLE},
{ SYS_RES_DRQ, 0, RF_ACTIVE | RF_SHAREABLE | RF_OPTIONAL},
{ SYS_RES_IOPORT, 0, RF_ACTIVE},
+ { SYS_RES_IOPORT, 1, RF_ACTIVE},
+ { SYS_RES_IOPORT, 2, RF_ACTIVE},
+ { SYS_RES_IOPORT, 3, RF_ACTIVE},
+ { SYS_RES_IOPORT, 4, RF_ACTIVE},
+ { SYS_RES_IOPORT, 5, RF_ACTIVE},
+ { SYS_RES_IOPORT, 6, RF_ACTIVE},
+ { SYS_RES_IOPORT, 7, RF_ACTIVE},
+ { SYS_RES_IOPORT, 8, RF_ACTIVE | RF_SHAREABLE},
{ -1, 0, 0 }
};
@@ -92,7 +103,7 @@ static int
pcii_probe(device_t dev)
{
int rid, i, j;
- u_long start, count;
+ u_long start, count, addr;
int error = 0;
struct pcii_softc *sc;
@@ -102,30 +113,89 @@ pcii_probe(device_t dev)
rid = 0;
if (bus_get_resource(dev, SYS_RES_IOPORT, rid, &start, &count) != 0)
return ENXIO;
- if ((start & 0x3ff) != 0x2e1)
+ /*
+ * The PCIIA decodes a fixed pattern of 0x2e1 for the lower 10
+ * address bits A0 ... A9. Bits A10 through A12 are used by
+ * the µPD7210 register select lines. This makes the
+ * individual 7210 register being 0x400 bytes apart in the ISA
+ * bus address space. Address bits A13 and A14 are compared
+ * to a DIP switch setting on the card, allowing for up to 4
+ * different cards being installed (at base addresses 0x2e1,
+ * 0x22e1, 0x42e1, and 0x62e1, respectively). A15 has been
+ * used to select an optional on-board time-of-day clock chip
+ * (MM58167A) on the original PCIIA rather than the µPD7210
+ * (which is not implemented on later boards). The
+ * documentation states the respective addresses for that chip
+ * should be handled as reserved addresses, which we don't do
+ * (right now). Finally, the IO addresses 0x2f0 ... 0x2f7 for
+ * a "special interrupt handling feature" (re-enable
+ * interrupts so the IRQ can be shared).
+ *
+ * Usually, the user will only set the base address in the
+ * device hints, so we handle the rest here.
+ *
+ * (Source: GPIB-PCIIA Technical Reference Manual, September
+ * 1989 Edition, National Instruments.)
+ */
+ if ((start & 0x3ff) != 0x2e1) {
+ printf("pcii_probe: PCIIA base address 0x%lx not "
+ "0x2e1/0x22e1/0x42e1/0x62e1\n",
+ start);
return (ENXIO);
- count = 1;
- if (bus_set_resource(dev, SYS_RES_IOPORT, rid, start, count) != 0)
+ }
+
+ for (rid = 0, addr = start; rid < 8; rid++, addr += 0x400) {
+ if (bus_set_resource(dev, SYS_RES_IOPORT, rid, addr, 1) != 0) {
+ printf("pcii_probe: could not set IO port 0x%lx\n",
+ addr);
+ return (ENXIO);
+ }
+ }
+ if (bus_get_resource(dev, SYS_RES_IRQ, 0, &start, &count) != 0) {
+ printf("pcii_probe: cannot obtain IRQ level\n");
+ return ENXIO;
+ }
+ if (start > 7) {
+ printf("pcii_probe: IRQ level %lu too high\n", start);
return ENXIO;
+ }
+
+ if (bus_set_resource(dev, SYS_RES_IOPORT, 8, 0x2f0 + start, 1) != 0) {
+ printf("pcii_probe: could not set IO port 0x%3lx\n",
+ 0x2f0 + start);
+ return (ENXIO);
+ }
+
error = bus_alloc_resources(dev, pcii_res_spec, sc->res);
- if (error)
+ if (error) {
+ printf("pcii_probe: Could not allocate resources\n");
return (error);
+ }
error = ENXIO;
+ /*
+ * Perform some basic tests on the µPD7210 registers. At
+ * least *some* register must read different from 0x00 or
+ * 0xff.
+ */
for (i = 0; i < 8; i++) {
- j = bus_read_1(sc->res[2], i * 0x400);
+ j = bus_read_1(sc->res[2 + i], 0);
if (j != 0x00 && j != 0xff)
error = 0;
}
+ /* SPSR/SPMR read/write test */
if (!error) {
- bus_write_1(sc->res[2], 3 * 0x400, 0x55);
- if (bus_read_1(sc->res[2], 3 * 0x400) != 0x55)
+ bus_write_1(sc->res[2 + 3], 0, 0x55);
+ if (bus_read_1(sc->res[2 + 3], 0) != 0x55)
error = ENXIO;
}
if (!error) {
- bus_write_1(sc->res[2], 3 * 0x400, 0xaa);
- if (bus_read_1(sc->res[2], 3 * 0x400) != 0xaa)
+ bus_write_1(sc->res[2 + 3], 0, 0xaa);
+ if (bus_read_1(sc->res[2 + 3], 0) != 0xaa)
error = ENXIO;
}
+ if (error)
+ printf("pcii_probe: probe failure\n");
+
bus_release_resources(dev, pcii_res_spec, sc->res);
return (error);
}
@@ -134,6 +204,7 @@ static int
pcii_attach(device_t dev)
{
struct pcii_softc *sc;
+ u_long start, count;
int unit;
int rid;
int error = 0;
@@ -144,6 +215,11 @@ pcii_attach(device_t dev)
device_set_desc(dev, "PCII IEEE-4888 controller");
+ if (bus_get_resource(dev, SYS_RES_IRQ, 0, &start, &count) != 0) {
+ printf("pcii_attach: cannot obtain IRQ number\n");
+ return ENXIO;
+ }
+
error = bus_alloc_resources(dev, pcii_res_spec, sc->res);
if (error)
return (error);
@@ -157,9 +233,9 @@ pcii_attach(device_t dev)
}
for (rid = 0; rid < 8; rid++) {
- sc->upd7210.reg_res[rid] = sc->res[2];
- sc->upd7210.reg_offset[rid] = 0x400 * rid;
+ sc->upd7210.reg_res[rid] = sc->res[2 + rid];
}
+ sc->upd7210.irq_clear_res = sc->res[10];
if (sc->res[1] == NULL)
sc->upd7210.dmachan = -1;
diff --git a/sys/dev/ieee488/upd7210.c b/sys/dev/ieee488/upd7210.c
index 27359e2..2c63f2f 100644
--- a/sys/dev/ieee488/upd7210.c
+++ b/sys/dev/ieee488/upd7210.c
@@ -1,5 +1,6 @@
/*-
* Copyright (c) 2005 Poul-Henning Kamp <phk@FreeBSD.org>
+ * Copyright (c) 2010 Joerg Wunsch <joerg@FreeBSD.org>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -71,7 +72,7 @@ upd7210_rd(struct upd7210 *u, enum upd7210_rreg reg)
{
u_int r;
- r = bus_read_1(u->reg_res[reg], u->reg_offset[reg]);
+ r = bus_read_1(u->reg_res[reg], 0);
u->rreg[reg] = r;
return (r);
}
@@ -80,7 +81,7 @@ void
upd7210_wr(struct upd7210 *u, enum upd7210_wreg reg, u_int val)
{
- bus_write_1(u->reg_res[reg], u->reg_offset[reg], val);
+ bus_write_1(u->reg_res[reg], 0, val);
u->wreg[reg] = val;
if (reg == AUXMR)
u->wreg[8 + (val >> 5)] = val & 0x1f;
@@ -96,19 +97,35 @@ upd7210intr(void *arg)
mtx_lock(&u->mutex);
isr1 = upd7210_rd(u, ISR1);
isr2 = upd7210_rd(u, ISR2);
- if (u->busy == 0 || u->irq == NULL || !u->irq(u, 1)) {
+ if (isr1 != 0 || isr2 != 0) {
+ if (u->busy == 0 || u->irq == NULL || !u->irq(u, 1)) {
#if 0
- printf("upd7210intr [%02x %02x %02x",
- upd7210_rd(u, DIR), isr1, isr2);
- printf(" %02x %02x %02x %02x %02x] ",
- upd7210_rd(u, SPSR),
- upd7210_rd(u, ADSR),
- upd7210_rd(u, CPTR),
- upd7210_rd(u, ADR0),
+ printf("upd7210intr [%02x %02x %02x",
+ upd7210_rd(u, DIR), isr1, isr2);
+ printf(" %02x %02x %02x %02x %02x] ",
+ upd7210_rd(u, SPSR),
+ upd7210_rd(u, ADSR),
+ upd7210_rd(u, CPTR),
+ upd7210_rd(u, ADR0),
upd7210_rd(u, ADR1));
- upd7210_print_isr(isr1, isr2);
- printf("\n");
+ upd7210_print_isr(isr1, isr2);
+ printf("\n");
#endif
+ }
+ /*
+ * "special interrupt handling"
+ *
+ * In order to implement shared IRQs, the original
+ * PCIIa uses IO locations 0x2f0 + (IRQ#) as an output
+ * location. If an ISR for a particular card has
+ * detected this card triggered the IRQ, it must reset
+ * the card's IRQ by writing (anything) to that IO
+ * location.
+ *
+ * Some clones apparently don't implement this
+ * feature, but National Instrument cards do.
+ */
+ bus_write_1(u->irq_clear_res, 0, 42);
}
mtx_unlock(&u->mutex);
}
diff --git a/sys/dev/ieee488/upd7210.h b/sys/dev/ieee488/upd7210.h
index da1032c..fccb13e 100644
--- a/sys/dev/ieee488/upd7210.h
+++ b/sys/dev/ieee488/upd7210.h
@@ -1,5 +1,6 @@
/*-
* Copyright (c) 2005 Poul-Henning Kamp <phk@FreeBSD.org>
+ * Copyright (c) 2010 Joerg Wunsch <joerg@FreeBSD.org>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -48,7 +49,7 @@ typedef int upd7210_irq_t(struct upd7210 *, int);
struct upd7210 {
struct resource *reg_res[8];
- u_int reg_offset[8];
+ struct resource *irq_clear_res;
int dmachan;
int unit;
OpenPOWER on IntegriCloud