From 6951547ba59a8efc0cd28f2cdffb30eab7eb6a03 Mon Sep 17 00:00:00 2001 From: Sepherosa Ziehau Date: Sun, 5 Jul 2009 21:08:30 +0800 Subject: [PATCH] pci: Resurrect COMPAT_OLDPCI; mainly to unbreak LINT building --- sys/bus/pci/pci.c | 56 +++++++ sys/bus/pci/pci_compat.c | 319 +++++++++++++++++++++++++++++++++++++++ sys/bus/pci/pcireg.h | 29 ++++ sys/bus/pci/pcivar.h | 65 ++++++++ sys/conf/files | 1 + 5 files changed, 470 insertions(+) create mode 100644 sys/bus/pci/pci_compat.c diff --git a/sys/bus/pci/pci.c b/sys/bus/pci/pci.c index a63728be1a..1aa2cf5ba0 100644 --- a/sys/bus/pci/pci.c +++ b/sys/bus/pci/pci.c @@ -30,6 +30,7 @@ #include #include "opt_bus.h" +#include "opt_compat_oldpci.h" #include #include @@ -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 index 0000000000..c66eb09736 --- /dev/null +++ b/sys/bus/pci/pci_compat.c @@ -0,0 +1,319 @@ +/* + * Copyright (c) 1997, Stefan Esser + * 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 +#include +#include +#include + +#include +#include + +#include +#include +#include +#include + +#include +#include "pcireg.h" +#include "pcivar.h" +#include + +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; +} diff --git a/sys/bus/pci/pcireg.h b/sys/bus/pci/pcireg.h index 3a99d0891c..a6e0fcd8e3 100644 --- a/sys/bus/pci/pcireg.h +++ b/sys/bus/pci/pcireg.h @@ -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_ */ diff --git a/sys/bus/pci/pcivar.h b/sys/bus/pci/pcivar.h index 75251e8cf2..73711f133f 100644 --- a/sys/bus/pci/pcivar.h +++ b/sys/bus/pci/pcivar.h @@ -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_ */ diff --git a/sys/conf/files b/sys/conf/files index d06e79ec40..5d2ab7768b 100644 --- a/sys/conf/files +++ b/sys/conf/files @@ -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 -- 2.41.0