pci: Resurrect COMPAT_OLDPCI; mainly to unbreak LINT building
authorSepherosa Ziehau <sephe@dragonflybsd.org>
Sun, 5 Jul 2009 13:08:30 +0000 (21:08 +0800)
committerSepherosa Ziehau <sephe@dragonflybsd.org>
Wed, 8 Jul 2009 10:50:42 +0000 (18:50 +0800)
sys/bus/pci/pci.c
sys/bus/pci/pci_compat.c [new file with mode: 0644]
sys/bus/pci/pcireg.h
sys/bus/pci/pcivar.h
sys/conf/files

index a63728b..1aa2cf5 100644 (file)
@@ -30,6 +30,7 @@
 #include <sys/cdefs.h>
 
 #include "opt_bus.h"
+#include "opt_compat_oldpci.h"
 
 #include <sys/param.h>
 #include <sys/systm.h>
@@ -441,11 +442,17 @@ pci_hdrtypedata(device_t pcib, int b, int s, int f, pcicfgregs *cfg)
                break;
        case 1:
                cfg->nummaps        = PCI_MAXMAPS_1;
+#ifdef COMPAT_OLDPCI
+               cfg->secondarybus   = REG(PCIR_SECBUS_1, 1);
+#endif
                break;
        case 2:
                cfg->subvendor      = REG(PCIR_SUBVEND_2, 2);
                cfg->subdevice      = REG(PCIR_SUBDEV_2, 2);
                cfg->nummaps        = PCI_MAXMAPS_2;
+#ifdef COMPAT_OLDPCI
+               cfg->secondarybus   = REG(PCIR_SECBUS_2, 1);
+#endif
                break;
        }
 #undef REG
@@ -4190,3 +4197,52 @@ pci_cfg_save(device_t dev, struct pci_devinfo *dinfo, int setstate)
        if (pci_get_powerstate(dev) != PCI_POWERSTATE_D3)
                pci_set_powerstate(dev, PCI_POWERSTATE_D3);
 }
+
+#ifdef COMPAT_OLDPCI
+
+/*
+ * Locate the parent of a PCI device by scanning the PCI devlist
+ * and return the entry for the parent.
+ * For devices on PCI Bus 0 (the host bus), this is the PCI Host.
+ * For devices on secondary PCI busses, this is that bus' PCI-PCI Bridge.
+ */
+pcicfgregs *
+pci_devlist_get_parent(pcicfgregs *cfg)
+{
+       struct devlist *devlist_head;
+       struct pci_devinfo *dinfo;
+       pcicfgregs *bridge_cfg;
+       int i;
+
+       dinfo = STAILQ_FIRST(devlist_head = &pci_devq);
+
+       /* If the device is on PCI bus 0, look for the host */
+       if (cfg->bus == 0) {
+               for (i = 0; (dinfo != NULL) && (i < pci_numdevs);
+               dinfo = STAILQ_NEXT(dinfo, pci_links), i++) {
+                       bridge_cfg = &dinfo->cfg;
+                       if (bridge_cfg->baseclass == PCIC_BRIDGE
+                               && bridge_cfg->subclass == PCIS_BRIDGE_HOST
+                               && bridge_cfg->bus == cfg->bus) {
+                               return bridge_cfg;
+                       }
+               }
+       }
+
+       /* If the device is not on PCI bus 0, look for the PCI-PCI bridge */
+       if (cfg->bus > 0) {
+               for (i = 0; (dinfo != NULL) && (i < pci_numdevs);
+               dinfo = STAILQ_NEXT(dinfo, pci_links), i++) {
+                       bridge_cfg = &dinfo->cfg;
+                       if (bridge_cfg->baseclass == PCIC_BRIDGE
+                               && bridge_cfg->subclass == PCIS_BRIDGE_PCI
+                               && bridge_cfg->secondarybus == cfg->bus) {
+                               return bridge_cfg;
+                       }
+               }
+       }
+
+       return NULL; 
+}
+
+#endif /* COMPAT_OLDPCI */
diff --git a/sys/bus/pci/pci_compat.c b/sys/bus/pci/pci_compat.c
new file mode 100644 (file)
index 0000000..c66eb09
--- /dev/null
@@ -0,0 +1,319 @@
+/*
+ * Copyright (c) 1997, Stefan Esser <se@freebsd.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 unmodified, 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 ``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: src/sys/pci/pci_compat.c,v 1.35.2.1 2001/10/14 21:14:14 luigi Exp $
+ * $DragonFly: src/sys/bus/pci/pci_compat.c,v 1.14 2007/05/13 18:33:56 swildner Exp $
+ *
+ */
+
+#include "opt_bus.h"
+
+/* for compatibility to FreeBSD-2.2 and 3.x versions of PCI code */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/malloc.h>
+#include <sys/module.h>
+
+#include <vm/vm.h>
+#include <vm/pmap.h>
+
+#include <sys/bus.h>
+#include <sys/rman.h>
+#include <machine/smp.h>
+#include <sys/interrupt.h>
+
+#include <sys/pciio.h>
+#include "pcireg.h"
+#include "pcivar.h"
+#include <bus/pci/pci_cfgreg.h>
+
+struct pci_compat_driver {
+       driver_t driver;
+       struct pci_device *dvp;
+};
+
+/* ------------------------------------------------------------------------- */
+
+u_long
+pci_conf_read(pcici_t cfg, u_long reg)
+{
+       return (pci_read_config(cfg->dev, reg, 4));
+}
+
+void
+pci_conf_write(pcici_t cfg, u_long reg, u_long data)
+{
+       pci_write_config(cfg->dev, reg, data, 4);
+}
+
+void
+pci_cfgwrite(pcicfgregs *cfg, int reg, int data, int bytes)
+{
+       pci_cfgregwrite(cfg->bus, cfg->slot, cfg->func, reg, data, bytes);
+}
+
+int
+pci_map_port(pcici_t cfg, u_long reg, pci_port_t* pa)
+{
+       int rid;
+       struct resource *res;
+
+       rid = reg;
+       res = bus_alloc_resource(cfg->dev, SYS_RES_IOPORT, &rid,
+                                0, ~0, 1, RF_ACTIVE);
+       if (res) {
+               *pa = rman_get_start(res);
+               return (1);
+       }
+       return (0);
+}
+
+int
+pci_map_mem(pcici_t cfg, u_long reg, vm_offset_t* va, vm_offset_t* pa)
+{
+       int rid;
+       struct resource *res;
+
+       rid = reg;
+       res = bus_alloc_resource(cfg->dev, SYS_RES_MEMORY, &rid,
+                                0, ~0, 1, RF_ACTIVE);
+       if (res) {
+               *pa = rman_get_start(res);
+               *va = (vm_offset_t) rman_get_virtual(res);
+               return (1);
+       }
+       return (0);
+}
+
+int
+pci_map_int(pcici_t cfg, pci_inthand_t *handler, void *arg)
+{
+       return (pci_map_int_right(cfg, handler, arg, 0));
+}
+
+int
+pci_map_int_right(pcici_t cfg, pci_inthand_t *handler, void *arg, u_int intflags)
+{
+       int error;
+#ifdef APIC_IO
+       int nextpin, muxcnt;
+#endif
+       if (cfg->intpin != 0) {
+               int irq = cfg->intline;
+               int rid = 0;
+               struct resource *res;
+               int flags = 0;
+               int resflags = RF_SHAREABLE|RF_ACTIVE;
+               void *ih;
+
+               if (intflags & INTR_FAST)
+                       flags |= INTR_FAST;
+               if (intflags & INTR_EXCL)
+                       resflags &= ~RF_SHAREABLE;
+
+               res = bus_alloc_resource(cfg->dev, SYS_RES_IRQ, &rid,
+                                        irq, irq, 1, resflags);
+               if (!res) {
+                       kprintf("pci_map_int: can't allocate interrupt\n");
+                       return 0;
+               }
+
+               /*
+                * This is ugly. Translate the mask into an interrupt type.
+                */
+
+               error = BUS_SETUP_INTR(device_get_parent(cfg->dev), cfg->dev,
+                                      res, flags, handler, arg, &ih, NULL);
+               if (error != 0)
+                       return 0;
+
+#ifdef NEW_BUS_PCI
+               /*
+                * XXX this apic stuff looks totally busted.  It should
+                * move to the nexus code which actually registers the
+                * interrupt.
+                */
+#endif
+
+#ifdef APIC_IO
+               nextpin = next_apic_irq(irq);
+               
+               if (nextpin < 0)
+                       return 1;
+
+               /* 
+                * Attempt handling of some broken mp tables.
+                *
+                * It's OK to yell (since the mp tables are broken).
+                * 
+                * Hanging in the boot is not OK
+                */
+
+               muxcnt = 2;
+               nextpin = next_apic_irq(nextpin);
+               while (muxcnt < 5 && nextpin >= 0) {
+                       muxcnt++;
+                       nextpin = next_apic_irq(nextpin);
+               }
+               if (muxcnt >= 5) {
+                       kprintf("bogus MP table, more than 4 IO APIC pins connected to the same PCI device or ISA/EISA interrupt\n");
+                       return 0;
+               }
+               
+               kprintf("bogus MP table, %d IO APIC pins connected to the same PCI device or ISA/EISA interrupt\n", muxcnt);
+
+               nextpin = next_apic_irq(irq);
+               while (nextpin >= 0) {
+                       rid = 0;
+                       res = bus_alloc_resource(cfg->dev, SYS_RES_IRQ, &rid,
+                                                nextpin, nextpin, 1,
+                                                resflags);
+                       if (!res) {
+                               kprintf("pci_map_int: can't allocate extra interrupt\n");
+                               return 0;
+                       }
+                       error = BUS_SETUP_INTR(device_get_parent(cfg->dev),
+                                              cfg->dev, res, flags,
+                                              handler, arg, &ih, NULL);
+                       if (error != 0) {
+                               kprintf("pci_map_int: BUS_SETUP_INTR failed\n");
+                               return 0;
+                       }
+                       kprintf("Registered extra interrupt handler for int %d (in addition to int %d)\n", nextpin, irq);
+                       nextpin = next_apic_irq(nextpin);
+               }
+#endif
+       }
+       return (1);
+}
+
+int
+pci_unmap_int(pcici_t cfg)
+{
+       return (0); /* not supported, yet, since cfg doesn't know about idesc */
+}
+
+pcici_t
+pci_get_parent_from_tag(pcici_t tag)
+{
+       return (pcici_t)pci_devlist_get_parent(tag);
+}
+
+int
+pci_get_bus_from_tag(pcici_t tag)
+{
+       return tag->bus;
+}
+
+/*
+ * A simple driver to wrap the old pci driver mechanism for back-compat.
+ */
+
+static int
+pci_compat_probe(device_t dev)
+{
+       struct pci_device *dvp;
+       struct pci_devinfo *dinfo;
+       pcicfgregs *cfg;
+       const char *name;
+       int error;
+       
+       dinfo = device_get_ivars(dev);
+       cfg = &dinfo->cfg;
+       dvp = ((struct pci_compat_driver *)device_get_driver(dev))->dvp;
+
+       /*
+        * Do the wrapped probe.
+        */
+       error = ENXIO;
+       if (dvp && dvp->pd_probe) {
+               name = dvp->pd_probe(cfg, (cfg->device << 16) + cfg->vendor);
+               if (name) {
+                       device_set_desc_copy(dev, name);
+                       /* Allow newbus drivers to match "better" */
+                       error = -200;
+               }
+       }
+
+       return error;
+}
+
+static int
+pci_compat_attach(device_t dev)
+{
+       struct pci_device *dvp;
+       struct pci_devinfo *dinfo;
+       pcicfgregs *cfg;
+       int unit;
+
+       dinfo = device_get_ivars(dev);
+       cfg = &dinfo->cfg;
+       dvp = ((struct pci_compat_driver *)device_get_driver(dev))->dvp;
+
+       unit = device_get_unit(dev);
+       if (unit > *dvp->pd_count)
+               *dvp->pd_count = unit;
+       if (dvp->pd_attach)
+               dvp->pd_attach(cfg, unit);
+       device_printf(dev, "driver is using old-style compatibility shims\n");
+       return 0;
+}
+
+static device_method_t pci_compat_methods[] = {
+       /* Device interface */
+       DEVMETHOD(device_probe,         pci_compat_probe),
+       DEVMETHOD(device_attach,        pci_compat_attach),
+
+       { 0, 0 }
+};
+
+/*
+ * Create a new style driver around each old pci driver.
+ */
+int
+compat_pci_handler(module_t mod, int type, void *data)
+{
+       struct pci_device *dvp = (struct pci_device *)data;
+       struct pci_compat_driver *driver;
+       devclass_t pci_devclass = devclass_find("pci");
+
+       switch (type) {
+       case MOD_LOAD:
+               driver = kmalloc(sizeof(struct pci_compat_driver), M_DEVBUF, M_WAITOK | M_ZERO);
+               driver->driver.name = dvp->pd_name;
+               driver->driver.methods = pci_compat_methods;
+               driver->driver.size = sizeof(struct pci_devinfo *);
+               driver->dvp = dvp;
+               devclass_add_driver(pci_devclass, (driver_t *)driver);
+               break;
+       case MOD_UNLOAD:
+               kprintf("%s: module unload not supported!\n", dvp->pd_name);
+               return EOPNOTSUPP;
+       default:
+               break;
+       }
+       return 0;
+}
index 3a99d08..a6e0fcd 100644 (file)
@@ -675,4 +675,33 @@ typedef u_int32_t pcireg_t;             /* ~typical configuration space */
 #define PCIE_HPINTR_PRSN_DETECT                0x0008  /* Presence detect intr */
 #define PCIE_HPINTR_CMD_COMPL          0x0010  /* Command completed intr */
 
+/* for compatibility to FreeBSD-2.2 and 3.x versions of PCI code */
+
+#if defined(_KERNEL) && !defined(KLD_MODULE)
+#include "opt_compat_oldpci.h"
 #endif
+
+#ifdef COMPAT_OLDPCI
+
+#define PCI_ID_REG             0x00
+#define PCI_COMMAND_STATUS_REG 0x04
+#define        PCI_COMMAND_IO_ENABLE           0x00000001
+#define        PCI_COMMAND_MEM_ENABLE          0x00000002
+#define PCI_CLASS_REG          0x08
+#define PCI_CLASS_MASK                 0xff000000
+#define PCI_SUBCLASS_MASK              0x00ff0000
+#define        PCI_REVISION_MASK               0x000000ff
+#define PCI_CLASS_PREHISTORIC          0x00000000
+#define PCI_SUBCLASS_PREHISTORIC_VGA           0x00010000
+#define PCI_CLASS_MASS_STORAGE         0x01000000
+#define PCI_CLASS_DISPLAY              0x03000000
+#define PCI_SUBCLASS_DISPLAY_VGA               0x00000000
+#define PCI_CLASS_BRIDGE               0x06000000
+#define PCI_MAP_REG_START      0x10
+#define        PCI_MAP_REG_END         0x28
+#define        PCI_MAP_IO                      0x00000001
+#define        PCI_INTERRUPT_REG       0x3c
+
+#endif /* COMPAT_OLDPCI */
+
+#endif /* _PCIREG_H_ */
index 75251e8..73711f1 100644 (file)
@@ -173,6 +173,12 @@ typedef struct pcicfg {
     uint8_t    slot;           /* config space slot address */
     uint8_t    func;           /* config space function number */
 
+#ifdef COMPAT_OLDPCI
+    uint8_t    secondarybus;   /* bus on secondary side of bridge, if any */
+#else
+    uint8_t    dummy;
+#endif
+
     struct pcicfg_pp pp;       /* pci power management */
     struct pcicfg_vpd vpd;     /* pci vital product data */
     struct pcicfg_msi msi;     /* pci msi */
@@ -524,4 +530,63 @@ STAILQ_HEAD(devlist, pci_devinfo);
 extern struct devlist  pci_devq;
 extern uint32_t        pci_generation;
 
+/* for compatibility to FreeBSD-2.2 and 3.x versions of PCI code */
+
+#if defined(_KERNEL) && !defined(KLD_MODULE)
+#include "opt_compat_oldpci.h"
+#endif
+
+#ifdef COMPAT_OLDPCI
+/* all this is going some day */
+
+typedef pcicfgregs *pcici_t;
+typedef unsigned pcidi_t;
+typedef void pci_inthand_t(void *arg);
+
+#define pci_max_burst_len (3)
+
+/* just copied from old PCI code for now ... */
+
+struct pci_device {
+    char*    pd_name;
+    const char*  (*pd_probe ) (pcici_t tag, pcidi_t type);
+    void   (*pd_attach) (pcici_t tag, int     unit);
+    u_long  *pd_count;
+    int    (*pd_shutdown) (int, int);
+};
+
+#ifdef __i386__
+typedef u_short pci_port_t;
+#else
+typedef u_int pci_port_t;
+#endif
+
+u_long pci_conf_read (pcici_t tag, u_long reg);
+void pci_conf_write (pcici_t tag, u_long reg, u_long data);
+int pci_map_port (pcici_t tag, u_long reg, pci_port_t* pa);
+int pci_map_mem (pcici_t tag, u_long reg, vm_offset_t* va, vm_offset_t* pa);
+int pci_map_int (pcici_t tag, pci_inthand_t *handler, void *arg);
+int pci_map_int_right(pcici_t cfg, pci_inthand_t *handler, void *arg,
+                     u_int flags);
+int pci_unmap_int (pcici_t tag);
+
+void pci_cfgwrite (pcicfgregs *cfg, int reg, int data, int bytes);
+
+pcici_t pci_get_parent_from_tag(pcici_t tag);
+int     pci_get_bus_from_tag(pcici_t tag);
+
+pcicfgregs *pci_devlist_get_parent(pcicfgregs *cfg);
+
+struct module;
+int compat_pci_handler (struct module *, int, void *);
+#define COMPAT_PCI_DRIVER(name, pcidata)                               \
+static moduledata_t name##_mod = {                                     \
+       #name,                                                          \
+       compat_pci_handler,                                             \
+       &pcidata                                                        \
+};                                                                     \
+DECLARE_MODULE(name, name##_mod, SI_SUB_DRIVERS, SI_ORDER_ANY)
+
+#endif /* COMPAT_OLDPCI */
+
 #endif /* _PCIVAR_H_ */
index d06e79e..5d2ab77 100644 (file)
@@ -1341,6 +1341,7 @@ bus/pci/pci_pci.c         optional pci
 bus/pci/pci_user.c             optional pci
 bus/pci/pcib_if.m              optional pci
 bus/pci/vga_pci.c              optional pci
+bus/pci/pci_compat.c           optional pci compat_oldpci
 dev/powermng/i386/alpm/alpm.c  optional alpm
 kern/kern_posix4_mib.c         standard
 kern/kern_p1003_1b.c           standard