summaryrefslogtreecommitdiff
path: root/sys/dev/pci/pci_subr.c
diff options
context:
space:
mode:
Diffstat (limited to 'sys/dev/pci/pci_subr.c')
-rw-r--r--sys/dev/pci/pci_subr.c312
1 files changed, 312 insertions, 0 deletions
diff --git a/sys/dev/pci/pci_subr.c b/sys/dev/pci/pci_subr.c
new file mode 100644
index 00000000000..0b1eb5408fb
--- /dev/null
+++ b/sys/dev/pci/pci_subr.c
@@ -0,0 +1,312 @@
+/* $NetBSD: pci_subr.c,v 1.12 1995/08/16 04:54:50 cgd Exp $ */
+
+/*
+ * Copyright (c) 1995 Christopher G. Demetriou. All rights reserved.
+ * Copyright (c) 1994 Charles Hannum. 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, 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.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by Charles Hannum.
+ * 4. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * 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.
+ */
+
+/*
+ * PCI autoconfiguration support functions.
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/device.h>
+
+#include <dev/pci/pcireg.h>
+#include <dev/pci/pcivar.h>
+#ifdef PCIVERBOSE
+#include <dev/pci/pcidevs.h>
+#endif
+
+int
+pciprint(aux, pci)
+ void *aux;
+ char *pci;
+{
+ register struct pci_attach_args *pa = aux;
+
+ printf(" bus %d device %d", pa->pa_bus, pa->pa_device);
+ return (UNCONF);
+}
+
+int
+pcisubmatch(parent, match, aux)
+ struct device *parent;
+ void *match, *aux;
+{
+ struct cfdata *cf = match;
+ struct pci_attach_args *pa = aux;
+
+ if (cf->cf_loc[0] != -1 && cf->cf_loc[0] != pa->pa_bus)
+ return 0;
+ if (cf->cf_loc[1] != -1 && cf->cf_loc[1] != pa->pa_device)
+ return 0;
+ return ((*cf->cf_driver->cd_match)(parent, match, aux));
+}
+
+/*
+ * Try to find and attach the PCI device at the give bus and device number.
+ * Return 1 if successful, 0 if unsuccessful.
+ */
+int
+pci_attach_subdev(pcidev, bus, device)
+ struct device *pcidev;
+ int bus, device;
+{
+ pcitag_t tag;
+ pcireg_t id, class;
+ struct pci_attach_args pa;
+ struct cfdata *cf;
+ int supported;
+ char devinfo[256];
+
+ tag = pci_make_tag(bus, device, 0);
+ id = pci_conf_read(tag, PCI_ID_REG);
+ if (id == 0 || id == 0xffffffff)
+ return (0);
+ class = pci_conf_read(tag, PCI_CLASS_REG);
+
+ pa.pa_bus = bus;
+ pa.pa_device = device;
+ pa.pa_tag = tag;
+ pa.pa_id = id;
+ pa.pa_class = class;
+
+#if defined(PCIVERBOSE) && 0 /* _too_ verbose */
+ pci_devinfo(id, class, devinfo, NULL);
+ printf("%s bus %d device %d: %s\n", pcidev->dv_xname, bus,
+ device, devinfo);
+#endif /* _too_ verbose */
+
+ if ((cf = config_search(pcisubmatch, pcidev, &pa)) != NULL)
+ config_attach(pcidev, cf, &pa, pciprint);
+ else {
+ pci_devinfo(id, class, devinfo, &supported);
+ printf("%s bus %d device %d: %s not %s\n", pcidev->dv_xname,
+ bus, device, devinfo,
+ supported ? "configured" : "supported");
+ return (0);
+ }
+
+ return (1);
+}
+
+/*
+ * Descriptions of known PCI classes and subclasses.
+ *
+ * Subclasses are described in the same way as classes, but have a
+ * NULL subclass pointer.
+ */
+struct pci_class {
+ char *name;
+ int val; /* as wide as pci_{,sub}class_t */
+ struct pci_class *subclasses;
+};
+
+struct pci_class pci_subclass_prehistoric[] = {
+ { "miscellaneous", PCI_SUBCLASS_PREHISTORIC_MISC, },
+ { "VGA", PCI_SUBCLASS_PREHISTORIC_VGA, },
+ { 0 }
+};
+
+struct pci_class pci_subclass_mass_storage[] = {
+ { "SCSI", PCI_SUBCLASS_MASS_STORAGE_SCSI, },
+ { "IDE", PCI_SUBCLASS_MASS_STORAGE_IDE, },
+ { "floppy", PCI_SUBCLASS_MASS_STORAGE_FLOPPY, },
+ { "IPI", PCI_SUBCLASS_MASS_STORAGE_IPI, },
+ { "miscellaneous", PCI_SUBCLASS_MASS_STORAGE_MISC, },
+ { 0 },
+};
+
+struct pci_class pci_subclass_network[] = {
+ { "ethernet", PCI_SUBCLASS_NETWORK_ETHERNET, },
+ { "token ring", PCI_SUBCLASS_NETWORK_TOKENRING, },
+ { "FDDI", PCI_SUBCLASS_NETWORK_FDDI, },
+ { "miscellaneous", PCI_SUBCLASS_NETWORK_MISC, },
+ { 0 },
+};
+
+struct pci_class pci_subclass_display[] = {
+ { "VGA", PCI_SUBCLASS_DISPLAY_VGA, },
+ { "XGA", PCI_SUBCLASS_DISPLAY_XGA, },
+ { "miscellaneous", PCI_SUBCLASS_DISPLAY_MISC, },
+ { 0 },
+};
+
+struct pci_class pci_subclass_multimedia[] = {
+ { "video", PCI_SUBCLASS_MULTIMEDIA_VIDEO, },
+ { "audio", PCI_SUBCLASS_MULTIMEDIA_AUDIO, },
+ { "miscellaneous", PCI_SUBCLASS_MULTIMEDIA_MISC, },
+ { 0 },
+};
+
+struct pci_class pci_subclass_memory[] = {
+ { "RAM", PCI_SUBCLASS_MEMORY_RAM, },
+ { "flash", PCI_SUBCLASS_MEMORY_FLASH, },
+ { "miscellaneous", PCI_SUBCLASS_MEMORY_MISC, },
+ { 0 },
+};
+
+struct pci_class pci_subclass_bridge[] = {
+ { "host", PCI_SUBCLASS_BRIDGE_HOST, },
+ { "ISA", PCI_SUBCLASS_BRIDGE_ISA, },
+ { "EISA", PCI_SUBCLASS_BRIDGE_EISA, },
+ { "MicroChannel", PCI_SUBCLASS_BRIDGE_MC, },
+ { "PCI", PCI_SUBCLASS_BRIDGE_PCI, },
+ { "PCMCIA", PCI_SUBCLASS_BRIDGE_PCMCIA, },
+ { "miscellaneous", PCI_SUBCLASS_BRIDGE_MISC, },
+ { 0 },
+};
+
+struct pci_class pci_class[] = {
+ { "prehistoric", PCI_CLASS_PREHISTORIC,
+ pci_subclass_prehistoric, },
+ { "mass storage", PCI_CLASS_MASS_STORAGE,
+ pci_subclass_mass_storage, },
+ { "network", PCI_CLASS_NETWORK,
+ pci_subclass_network, },
+ { "display", PCI_CLASS_DISPLAY,
+ pci_subclass_display, },
+ { "multimedia", PCI_CLASS_MULTIMEDIA,
+ pci_subclass_multimedia, },
+ { "memory", PCI_CLASS_MEMORY,
+ pci_subclass_memory, },
+ { "bridge", PCI_CLASS_BRIDGE,
+ pci_subclass_bridge, },
+ { "undefined", PCI_CLASS_UNDEFINED,
+ 0, },
+ { 0 },
+};
+
+#ifdef PCIVERBOSE
+/*
+ * Descriptions of of known vendors and devices ("products").
+ */
+struct pci_knowndev {
+ pci_vendor_id_t vendor;
+ pci_product_id_t product;
+ int flags;
+ char *vendorname, *productname;
+};
+#define PCI_KNOWNDEV_UNSUPP 0x01 /* unsupported device */
+#define PCI_KNOWNDEV_NOPROD 0x02 /* match on vendor only */
+
+#include <dev/pci/pcidevs_data.h>
+#endif /* PCIVERBOSE */
+
+void
+pci_devinfo(id_reg, class_reg, cp, supp)
+ pcireg_t id_reg, class_reg;
+ char *cp;
+ int *supp;
+{
+ pci_vendor_id_t vendor;
+ pci_product_id_t product;
+ pci_class_t class;
+ pci_subclass_t subclass;
+ pci_interface_t interface;
+ pci_revision_t revision;
+ char *vendor_namep, *product_namep;
+ struct pci_class *classp, *subclassp;
+#ifdef PCIVERBOSE
+ struct pci_knowndev *kdp;
+#endif
+
+ vendor = PCI_VENDOR(id_reg);
+ product = PCI_PRODUCT(id_reg);
+
+ class = PCI_CLASS(class_reg);
+ subclass = PCI_SUBCLASS(class_reg);
+ interface = PCI_INTERFACE(class_reg);
+ revision = PCI_REVISION(class_reg);
+
+#ifdef PCIVERBOSE
+ kdp = pci_knowndevs;
+ while (kdp->vendorname != NULL) { /* all have vendor name */
+ if (kdp->vendor == vendor && (kdp->product == product ||
+ (kdp->flags & PCI_KNOWNDEV_NOPROD) != 0))
+ break;
+ kdp++;
+ }
+ if (kdp->vendorname == NULL) {
+ vendor_namep = product_namep = NULL;
+ if (supp != NULL)
+ *supp = 0;
+ } else {
+ vendor_namep = kdp->vendorname;
+ product_namep = (kdp->flags & PCI_KNOWNDEV_NOPROD) == 0 ?
+ kdp->productname : NULL;
+ if (supp != NULL)
+ *supp = (kdp->flags & PCI_KNOWNDEV_UNSUPP) == 0;
+ }
+#else /* PCIVERBOSE */
+ vendor_namep = product_namep = NULL;
+ if (supp != NULL)
+ *supp = 1; /* always say 'not configured' */
+#endif /* PCIVERBOSE */
+
+ classp = pci_class;
+ while (classp->name != NULL) {
+ if (class == classp->val)
+ break;
+ classp++;
+ }
+
+ subclassp = (classp->name != NULL) ? classp->subclasses : NULL;
+ while (subclassp && subclassp->name != NULL) {
+ if (subclass == subclassp->val)
+ break;
+ subclassp++;
+ }
+
+ if (vendor_namep == NULL)
+ cp += sprintf(cp, "unknown vendor/product: 0x%04x/0x%04x",
+ vendor, product);
+ else if (product_namep != NULL)
+ cp += sprintf(cp, "%s %s", vendor_namep, product_namep);
+ else
+ cp += sprintf(cp, "vendor: %s, unknown product: 0x%x",
+ vendor_namep, product);
+ cp += sprintf(cp, " (");
+ if (classp->name == NULL)
+ cp += sprintf(cp, "unknown class/subclass: 0x%02x/0x%02x",
+ class, subclass);
+ else {
+ cp += sprintf(cp, "class: %s, ", classp->name);
+ if (subclassp == NULL || subclassp->name == NULL)
+ cp += sprintf(cp, "unknown subclass: 0x%02x",
+ subclass);
+ else
+ cp += sprintf(cp, "subclass: %s", subclassp->name);
+ }
+#if 0 /* not very useful */
+ cp += sprintf(cp, ", interface: 0x%02x", interface);
+#endif
+ cp += sprintf(cp, ", revision: 0x%02x)", revision);
+}