Date: Sat, 23 Jan 2010 07:54:07 +0000 (UTC) From: Joerg Wunsch <joerg@FreeBSD.org> To: src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-head@freebsd.org Subject: svn commit: r202870 - head/sys/dev/ieee488 Message-ID: <201001230754.o0N7s70b040987@svn.freebsd.org>
next in thread | raw e-mail | index | archive | help
Author: joerg Date: Sat Jan 23 07:54:06 2010 New Revision: 202870 URL: http://svn.freebsd.org/changeset/base/202870 Log: 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 Modified: head/sys/dev/ieee488/pcii.c head/sys/dev/ieee488/upd7210.c head/sys/dev/ieee488/upd7210.h Modified: head/sys/dev/ieee488/pcii.c ============================================================================== --- head/sys/dev/ieee488/pcii.c Sat Jan 23 07:52:44 2010 (r202869) +++ head/sys/dev/ieee488/pcii.c Sat Jan 23 07:54:06 2010 (r202870) @@ -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_spe { 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; Modified: head/sys/dev/ieee488/upd7210.c ============================================================================== --- head/sys/dev/ieee488/upd7210.c Sat Jan 23 07:52:44 2010 (r202869) +++ head/sys/dev/ieee488/upd7210.c Sat Jan 23 07:54:06 2010 (r202870) @@ -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 upd72 { 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); } Modified: head/sys/dev/ieee488/upd7210.h ============================================================================== --- head/sys/dev/ieee488/upd7210.h Sat Jan 23 07:52:44 2010 (r202869) +++ head/sys/dev/ieee488/upd7210.h Sat Jan 23 07:54:06 2010 (r202870) @@ -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 struct upd7210 { struct resource *reg_res[8]; - u_int reg_offset[8]; + struct resource *irq_clear_res; int dmachan; int unit;
Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?201001230754.o0N7s70b040987>