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 <p...@freebsd.org>
+ * Copyright (c) 2010 Joerg Wunsch <jo...@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 <p...@freebsd.org>
+ * Copyright (c) 2010 Joerg Wunsch <jo...@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 <p...@freebsd.org>
+ * Copyright (c) 2010 Joerg Wunsch <jo...@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;
 
_______________________________________________
svn-src-all@freebsd.org mailing list
http://lists.freebsd.org/mailman/listinfo/svn-src-all
To unsubscribe, send any mail to "svn-src-all-unsubscr...@freebsd.org"

Reply via email to