summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJoel Sherrill <joel@rtems.org>2016-03-03 10:36:24 -0600
committerJoel Sherrill <joel@rtems.org>2016-03-10 10:30:25 -0600
commitf770fcb3688f4db8aabb13964dfcbe28ef6fbd02 (patch)
tree298c432cb62516bb77f9a022e28837ba953e440a
parentpc386/configure.ac: Delete incorrect and unused AM_CONDITIONAL (diff)
downloadrtems-f770fcb3688f4db8aabb13964dfcbe28ef6fbd02.tar.bz2
Add shared PCI support and enhance pc386 to support non-legacy PCI configuration space
This patch fundamentally results from enhancements to the pc386 BSP to support systems which do **NOT** have the legacy PCI BIOS. The patch adds support for detecting when legacy PCI BIOS is not present and then using IO space to access to PCI Configuration Space. This resulted in dynamically selected between two implementations of PCI and refactoring out the shared methods. This patch adds shared implementations of pci_bus_count() and pci_find_device(). Subsequent patches will remove implementations of these methods in other BSPs where possible.
-rw-r--r--c/src/lib/libbsp/Makefile.am3
-rw-r--r--c/src/lib/libbsp/i386/Makefile.am1
-rw-r--r--c/src/lib/libbsp/i386/pc386/Makefile.am3
-rw-r--r--c/src/lib/libbsp/i386/pc386/include/bsp.h7
-rw-r--r--c/src/lib/libbsp/i386/pc386/startup/bspstart.c29
-rw-r--r--c/src/lib/libbsp/i386/shared/pci/pci_io.c198
-rw-r--r--c/src/lib/libbsp/i386/shared/pci/pcibios.c149
-rw-r--r--c/src/lib/libbsp/shared/pci/pci_bus_count.c67
-rw-r--r--c/src/lib/libbsp/shared/pci/pci_find_device.c274
9 files changed, 596 insertions, 135 deletions
diff --git a/c/src/lib/libbsp/Makefile.am b/c/src/lib/libbsp/Makefile.am
index 16c276f7b0..c54fdcb7d6 100644
--- a/c/src/lib/libbsp/Makefile.am
+++ b/c/src/lib/libbsp/Makefile.am
@@ -29,6 +29,9 @@ EXTRA_DIST += shared/include/coverhd.h
EXTRA_DIST += shared/gdbstub/rtems-stub-glue.c
EXTRA_DIST += shared/include/bootcard.h
+EXTRA_DIST += shared/pci/pci_bus_count.c
+EXTRA_DIST += shared/pci/pci_find_device.c
+
include_bspdir = $(includedir)/bsp
include_bsp_HEADERS =
diff --git a/c/src/lib/libbsp/i386/Makefile.am b/c/src/lib/libbsp/i386/Makefile.am
index f10b764067..3b6df65b1d 100644
--- a/c/src/lib/libbsp/i386/Makefile.am
+++ b/c/src/lib/libbsp/i386/Makefile.am
@@ -22,6 +22,7 @@ EXTRA_DIST += shared/irq/irq_init.c
# shared/pci
EXTRA_DIST += shared/pci/pcibios.c
+EXTRA_DIST += shared/pci/pci_io.c
include $(top_srcdir)/../../../automake/subdirs.am
include $(top_srcdir)/../../../automake/local.am
diff --git a/c/src/lib/libbsp/i386/pc386/Makefile.am b/c/src/lib/libbsp/i386/pc386/Makefile.am
index a9c645bb67..fc73afdbe9 100644
--- a/c/src/lib/libbsp/i386/pc386/Makefile.am
+++ b/c/src/lib/libbsp/i386/pc386/Makefile.am
@@ -141,6 +141,9 @@ libbsp_a_SOURCES += ../../shared/gnatinstallhandler.c
# pci
libbsp_a_SOURCES += ../../i386/shared/pci/pcibios.c
+libbsp_a_SOURCES += ../../i386/shared/pci/pci_io.c
+libbsp_a_SOURCES += ../../shared/pci/pci_bus_count.c
+libbsp_a_SOURCES += ../../shared/pci/pci_find_device.c
include_HEADERS += ../../i386/shared/comm/uart.h
# startup
diff --git a/c/src/lib/libbsp/i386/pc386/include/bsp.h b/c/src/lib/libbsp/i386/pc386/include/bsp.h
index 3d29e3ec18..5778c75696 100644
--- a/c/src/lib/libbsp/i386/pc386/include/bsp.h
+++ b/c/src/lib/libbsp/i386/pc386/include/bsp.h
@@ -59,6 +59,7 @@
#include <rtems/clockdrv.h>
#include <rtems/score/cpu.h>
#include <rtems/bspIo.h>
+#include <rtems/pci.h>
#ifdef __cplusplus
extern "C" {
@@ -275,6 +276,12 @@ uint32_t BSP_irq_count_dump(FILE *f);
void raw_idt_notify(void);
void C_dispatch_isr(int vector);
+/*
+ * PCI Support Methods
+ */
+const pci_config_access_functions *pci_bios_initialize(void);
+const pci_config_access_functions *pci_io_initialize(void);
+
#ifdef __cplusplus
}
#endif
diff --git a/c/src/lib/libbsp/i386/pc386/startup/bspstart.c b/c/src/lib/libbsp/i386/pc386/startup/bspstart.c
index ac871f0565..f74300e971 100644
--- a/c/src/lib/libbsp/i386/pc386/startup/bspstart.c
+++ b/c/src/lib/libbsp/i386/pc386/startup/bspstart.c
@@ -34,17 +34,37 @@
#include <libcpu/cpuModel.h>
/*
+ * PCI Bus Configuration
+ */
+rtems_pci_config_t BSP_pci_configuration = {
+ (volatile unsigned char*)0,
+ (volatile unsigned char*)0,
+ NULL
+};
+
+/*
* Helper to initialize the PCI Bus
*/
static void bsp_pci_initialize_helper(void)
{
#if (BSP_IS_EDISON == 0)
- int pci_init_retval;
+ const pci_config_access_functions *pci_accessors;
- pci_init_retval = pci_initialize();
- if (pci_init_retval != PCIB_ERR_SUCCESS) {
- printk("PCI bus: could not initialize PCI BIOS interface\n");
+ pci_accessors = pci_bios_initialize();
+ if (pci_accessors != NULL) {
+ printk("PCI bus: using PCI BIOS interface\n");
+ BSP_pci_configuration.pci_functions = pci_accessors;
+ return;
}
+
+ pci_accessors = pci_io_initialize();
+ if (pci_accessors != NULL) {
+ printk("PCI bus: using PCI I/O interface\n");
+ BSP_pci_configuration.pci_functions = pci_accessors;
+ return;
+ }
+
+ printk("PCI bus: could not initialize PCI BIOS interface\n");
#endif
}
@@ -65,7 +85,6 @@ static void bsp_start_default( void )
*edison_wd = 0x11f8;
#endif
-
/*
* Calibrate variable for 1ms-loop (see timer.c)
*/
diff --git a/c/src/lib/libbsp/i386/shared/pci/pci_io.c b/c/src/lib/libbsp/i386/shared/pci/pci_io.c
new file mode 100644
index 0000000000..5947c04388
--- /dev/null
+++ b/c/src/lib/libbsp/i386/shared/pci/pci_io.c
@@ -0,0 +1,198 @@
+#include <rtems.h>
+#include <rtems/pci.h>
+#include <bsp.h>
+
+static int pci_io_initialized = 0;
+
+/*
+ * Forward reference. Initialized at bottom.
+ */
+static const pci_config_access_functions pci_io_indirect_functions;
+
+/*
+ * Detects presense of PCI Configuration is in I/O space. If so, return
+ * pointer to accessor methods.
+ *
+ * NOTE: TBD to determine if (a) PCI Bus exists and (b) this is the
+ * access method.
+ */
+const pci_config_access_functions *pci_io_initialize(void)
+{
+ pci_io_initialized = 1;
+
+ printk( "PCI I/O Support Initialized\n" );
+
+ return &pci_io_indirect_functions;
+}
+
+/*
+ * Build PCI Address
+ */
+static inline uint32_t pci_io_build_address(
+ uint16_t bus,
+ uint16_t slot,
+ uint16_t function,
+ uint16_t offset
+)
+{
+ uint32_t bus_u32 = (uint32_t)bus;
+ uint32_t slot_u32 = (uint32_t)slot;
+ uint32_t function_u32 = (uint32_t)function;
+ uint32_t address;
+
+ /*
+ * create configuration address as per figure at
+ * http://wiki.osdev.org/PCI#Configuration_Space_Access_Mechanism_.231
+ */
+ address = (uint32_t) 0x80000000; /* Bit 31 - Enable Bit */
+ /* Bits 30-24 - Reserved */
+ address |= bus_u32 << 16; /* Bits 23-16 - Bus Number */
+ address |= slot_u32 << 11; /* Bits 15-11 - Device/Slot Number */
+ address |= function_u32 << 8; /* Bits 10-8 - Function Number */
+ address |= offset & 0xfc; /* Bits 7-2 - Offset/Register Number */
+ /* Bits 1-0 - Reserved 0 */
+ return address;
+}
+
+static int BSP_pci_read_config_byte(
+ unsigned char bus,
+ unsigned char slot,
+ unsigned char function,
+ unsigned char offset,
+ unsigned char *value
+)
+{
+ uint32_t address;
+ uint32_t tmp;
+
+ address = pci_io_build_address( bus, slot, function, offset );
+
+ /* write out the address */
+ outport_long(0xCF8, address);
+
+ /* read in the data */
+ inport_long(0xCFC, tmp);
+
+ /* (offset & 3) * 8) = 0 will choose the first byte of the 32 bits register */
+ *value = (uint16_t)(tmp >> ((offset & 3) * 8)) & 0xff;
+ return PCIBIOS_SUCCESSFUL;
+}
+
+static int BSP_pci_read_config_word(
+ unsigned char bus,
+ unsigned char slot,
+ unsigned char function,
+ unsigned char offset,
+ unsigned short *value
+)
+{
+ uint32_t address;
+ uint32_t tmp;
+
+ address = pci_io_build_address( bus, slot, function, offset );
+
+ /* write out the address */
+ outport_long(0xCF8, address);
+
+ /* read in the data */
+ inport_long(0xCFC, tmp);
+
+ /* (offset & 2) * 8) = 0 will choose the first word of the 32 bits register */
+ *value = (uint16_t)(tmp >> ((offset & 2) * 8)) & 0xffff;
+ return PCIBIOS_SUCCESSFUL;
+}
+
+static int BSP_pci_read_config_dword(
+ unsigned char bus,
+ unsigned char slot,
+ unsigned char function,
+ unsigned char offset,
+ uint32_t *value
+)
+{
+ uint32_t address;
+ uint32_t tmp;
+
+ address = pci_io_build_address( bus, slot, function, offset );
+
+ /* write out the address */
+ outport_long(0xCF8, address);
+
+ /* read in the data */
+ inport_long(0xCFC, tmp);
+
+ *value = tmp;
+ return PCIBIOS_SUCCESSFUL;
+}
+
+static int BSP_pci_write_config_byte(
+ unsigned char bus,
+ unsigned char slot,
+ unsigned char function,
+ unsigned char offset,
+ unsigned char value
+)
+{
+ uint32_t address;
+
+ address = pci_io_build_address( bus, slot, function, offset );
+
+ /* write out the address */
+ outport_long(0xCF8, address);
+
+ /* read in the data */
+ outport_byte(0xCFC, value);
+
+ return PCIBIOS_SUCCESSFUL;
+}
+
+static int BSP_pci_write_config_word(
+ unsigned char bus,
+ unsigned char slot,
+ unsigned char function,
+ unsigned char offset,
+ unsigned short value
+)
+{
+ uint32_t address;
+
+ address = pci_io_build_address( bus, slot, function, offset );
+
+ /* write out the address */
+ outport_long(0xCF8, address);
+
+ /* read in the data */
+ outport_word(0xCFC, value);
+
+ return PCIBIOS_SUCCESSFUL;
+}
+
+static int BSP_pci_write_config_dword(
+ unsigned char bus,
+ unsigned char slot,
+ unsigned char function,
+ unsigned char offset,
+ uint32_t value
+)
+{
+ uint32_t address;
+
+ address = pci_io_build_address( bus, slot, function, offset );
+
+ /* write out the address */
+ outport_long(0xCF8, address);
+
+ /* read in the data */
+ outport_long(0xCFC, value);
+
+ return PCIBIOS_SUCCESSFUL;
+}
+
+static const pci_config_access_functions pci_io_indirect_functions = {
+ BSP_pci_read_config_byte,
+ BSP_pci_read_config_word,
+ BSP_pci_read_config_dword,
+ BSP_pci_write_config_byte,
+ BSP_pci_write_config_word,
+ BSP_pci_write_config_dword
+};
diff --git a/c/src/lib/libbsp/i386/shared/pci/pcibios.c b/c/src/lib/libbsp/i386/shared/pci/pcibios.c
index 7dc53a125e..e74649413b 100644
--- a/c/src/lib/libbsp/i386/shared/pci/pcibios.c
+++ b/c/src/lib/libbsp/i386/shared/pci/pcibios.c
@@ -40,16 +40,23 @@ static int pcib_convert_err(int err);
#define PCIB_DEVSIG_BUS(x) (((x)>>8) &0xff)
#define PCIB_DEVSIG_DEV(x) (((x)>>3) & 0x1f)
#define PCIB_DEVSIG_FUNC(x) ((x) & 0x7)
+
/*
- * Detects presense of PCI BIOS, returns
- * error code
+ * Forward reference. Initialized at bottom.
*/
-int
-pci_initialize(void)
+static const pci_config_access_functions pci_bios_indirect_functions;
+
+/* prototype before defining */
+const pci_config_access_functions *pci_bios_initialize(void);
+
+/*
+ * Detects presense of PCI BIOS, returns pointer to accessor methods.
+ */
+const pci_config_access_functions *pci_bios_initialize(void)
{
unsigned char *ucp;
- unsigned char sum;
- int i;
+ unsigned char sum;
+ int i;
pcibInitialized = 0;
@@ -80,7 +87,7 @@ pci_initialize(void)
if (ucp >= (unsigned char *)0xFFFFF) {
/* BIOS-32 not found */
- return PCIB_ERR_NOTPRESENT;
+ return NULL;
}
/* BIOS-32 found, let us find PCI BIOS */
@@ -102,7 +109,7 @@ pci_initialize(void)
if ((pcibExchg[0] & 0xff) != 0) {
/* Not found */
- return PCIB_ERR_NOTPRESENT;
+ return NULL;
}
/* Found PCI entry point */
@@ -125,130 +132,18 @@ pci_initialize(void)
if ((pcibExchg[0] & 0xff00) != 0) {
/* Not found */
- return PCIB_ERR_NOTPRESENT;
+ return NULL;
}
if (pcibExchg[3] != 0x20494350) {
/* Signature does not match */
- return PCIB_ERR_NOTPRESENT;
+ return NULL;
}
/* Success */
-
pcibInitialized = 1;
- return PCIB_ERR_SUCCESS;
-}
-
-/*
- * Find specified device and return its signature: combination
- * of bus number, device number and function number
- */
-static int
-pcib_find_by_devid(int vendorId, int devId, int idx, int *sig)
-{
- if (!pcibInitialized) {
- return PCIB_ERR_UNINITIALIZED;
- }
-
- pcibExchg[0] = pcibEntry;
- pcibExchg[1] = vendorId;
- pcibExchg[2] = devId;
- pcibExchg[3] = idx;
-
- __asm__ (" pusha");
- __asm__ (" movl pcibExchg, %edi");
- __asm__ (" movb $0xb1, %ah");
- __asm__ (" movb $0x02, %al");
- __asm__ (" movl pcibExchg+4, %edx");
- __asm__ (" movl pcibExchg+8, %ecx");
- __asm__ (" movl pcibExchg+12, %esi");
- __asm__ (" pushl %cs");
- __asm__ (" call *%edi");
- __asm__ (" movl %eax, pcibExchg");
- __asm__ (" movl %ebx, pcibExchg+4");
- __asm__ (" popa");
-
- *sig = pcibExchg[1] & 0xffff;
- return pcib_convert_err((pcibExchg[0] >> 8) & 0xff);
-}
-
-int
-pci_find_device(
- unsigned short vendorid,
- unsigned short deviceid,
- int instance,
- int *pbus,
- int *pdev,
- int *pfun
-)
-{
- int status;
- int sig = 0;
-
- status = pcib_find_by_devid( vendorid, deviceid, instance, &sig );
-
- *pbus = PCIB_DEVSIG_BUS(sig);
- *pdev = PCIB_DEVSIG_DEV(sig);
- *pfun = PCIB_DEVSIG_FUNC(sig);
- return status ? -1 : 0;
-}
-
-static uint8_t ucBusCount = 0xff;
-
-unsigned char
-pci_bus_count(void)
-{
- if ( ucBusCount == 0xff ) {
- unsigned char bus;
- unsigned char dev;
- unsigned char fun;
- unsigned char nfn;
- unsigned char hd = 0;
- uint32_t d = 0;
-
- ucBusCount = 0;
-
- for (bus=0; bus< 0xff; bus++) {
- for (dev=0; dev<PCI_MAX_DEVICES; dev++) {
- pci_read_config_dword(bus, dev, fun, PCI_VENDOR_ID, &d);
-
- if ( -1 == d ) {
- continue;
- }
-
- pci_read_config_byte(bus, dev, fun, PCI_HEADER_TYPE, &hd);
- nfn = (hd & 0x80) ? PCI_MAX_FUNCTIONS : 1;
-
- for ( fun=0; fun<nfn; fun++ ) {
-
- pci_read_config_dword(bus, dev, fun, PCI_VENDOR_ID, &d);
- if ( -1 == d )
- continue;
-
- pci_read_config_dword(bus, dev, fun, PCI_CLASS_REVISION, &d);
-
- if ( (d >> 16) == PCI_CLASS_BRIDGE_PCI ) {
- pci_read_config_byte(bus, dev, fun, PCI_SUBORDINATE_BUS, &hd);
-
- if ( hd > ucBusCount )
- ucBusCount = hd;
- }
-
- }
- }
- }
-
- if ( ucBusCount == 0 ) {
- printk("pci_bus_count() found 0 busses, assuming 1\n");
- ucBusCount = 1;
- } else if ( ucBusCount == 0xff ) {
- printk("pci_bus_count() found 0xff busses, assuming 1\n");
- ucBusCount = 1;
- }
- }
-
- return ucBusCount;
+ return &pci_bios_indirect_functions;
}
/*
@@ -568,7 +463,7 @@ BSP_pci_write_config_dword(
return PCIBIOS_SUCCESSFUL;
}
-const pci_config_access_functions pci_indirect_functions = {
+static const pci_config_access_functions pci_bios_indirect_functions = {
BSP_pci_read_config_byte,
BSP_pci_read_config_word,
BSP_pci_read_config_dword,
@@ -576,9 +471,3 @@ const pci_config_access_functions pci_indirect_functions = {
BSP_pci_write_config_word,
BSP_pci_write_config_dword
};
-
-rtems_pci_config_t BSP_pci_configuration = {
- (volatile unsigned char*)0,
- (volatile unsigned char*)0,
- &pci_indirect_functions
-};
diff --git a/c/src/lib/libbsp/shared/pci/pci_bus_count.c b/c/src/lib/libbsp/shared/pci/pci_bus_count.c
new file mode 100644
index 0000000000..d0c7c80f54
--- /dev/null
+++ b/c/src/lib/libbsp/shared/pci/pci_bus_count.c
@@ -0,0 +1,67 @@
+/*
+ * This software is Copyright (C) 1998 by T.sqware - all rights limited
+ * It is provided in to the public domain "as is", can be freely modified
+ * as far as this copyight notice is kept unchanged, but does not imply
+ * an endorsement by T.sqware of the product in which it is included.
+ */
+
+#include <rtems.h>
+#include <rtems/pci.h>
+
+static uint8_t pci_number_of_buses = 0xff;
+
+unsigned char pci_bus_count(void)
+{
+ if ( pci_number_of_buses != 0xff ) {
+ return pci_number_of_buses;
+ }
+
+ uint8_t bus;
+ uint8_t device;
+ uint8_t function;
+ uint8_t number_of_functions;
+ uint8_t header = 0;
+ uint8_t buses = 0;
+ uint32_t vendor = 0;
+ uint32_t class_rev = 0;
+
+ pci_number_of_buses = 0;
+
+ for (bus=0; bus < 0xff; bus++) {
+ for (device=0; device < PCI_MAX_DEVICES; device++) {
+
+ pci_read_config_dword(bus, device, 0, PCI_VENDOR_ID, &vendor);
+ if ( vendor == -1 ) {
+ continue;
+ }
+
+ pci_read_config_byte(bus, device, 0, PCI_HEADER_TYPE, &header);
+ number_of_functions = (header & 0x80) ? PCI_MAX_FUNCTIONS : 1;
+
+ for ( function=0; function < number_of_functions; function++ ) {
+ pci_read_config_dword(bus, device, function, PCI_VENDOR_ID, &vendor);
+ if ( vendor == -1 ) {
+ continue;
+ }
+
+ pci_read_config_dword(bus, device, function, PCI_CLASS_REVISION, &class_rev);
+ if ( (class_rev >> 16) == PCI_CLASS_BRIDGE_PCI ) {
+ pci_read_config_byte(bus, device, function, PCI_SUBORDINATE_BUS, &buses);
+ if ( buses > pci_number_of_buses ) {
+ pci_number_of_buses = buses;
+ }
+ }
+ }
+ }
+ }
+
+ if ( pci_number_of_buses == 0 ) {
+ printk("pci_bus_count() found 0 busses, assuming 1\n");
+ pci_number_of_buses = 1;
+ } else if ( pci_number_of_buses == 0xff ) {
+ printk("pci_bus_count() found 0xff busses, assuming 1\n");
+ pci_number_of_buses = 1;
+ }
+
+ return pci_number_of_buses;
+}
diff --git a/c/src/lib/libbsp/shared/pci/pci_find_device.c b/c/src/lib/libbsp/shared/pci/pci_find_device.c
new file mode 100644
index 0000000000..1feddcc574
--- /dev/null
+++ b/c/src/lib/libbsp/shared/pci/pci_find_device.c
@@ -0,0 +1,274 @@
+/**
+ * @file
+ *
+ * This file implements a BSP independent version of pci_find_device().
+ */
+
+/*
+ * The software in this file was based upon the pci_find_device()
+ * implementation provided by Till Straumann under the following terms.
+ * That implementation had been copied to multiple BSPs. This implementation
+ * is BSP independent and follows RTEMS Project coding guidelines.
+ *
+ * COPYRIGHT (c) 2016.
+ * On-Line Applications Research Corporation (OAR).
+ *
+ * Authorship
+ * ----------
+ * This software was created by
+ * Till Straumann <strauman@slac.stanford.edu>, 2001,
+ * Stanford Linear Accelerator Center, Stanford University.
+ *
+ * Acknowledgement of sponsorship
+ * ------------------------------
+ * This software was produced by
+ * the Stanford Linear Accelerator Center, Stanford University,
+ * under Contract DE-AC03-76SFO0515 with the Department of Energy.
+ *
+ * Government disclaimer of liability
+ * ----------------------------------
+ * Neither the United States nor the United States Department of Energy,
+ * nor any of their employees, makes any warranty, express or implied, or
+ * assumes any legal liability or responsibility for the accuracy,
+ * completeness, or usefulness of any data, apparatus, product, or process
+ * disclosed, or represents that its use would not infringe privately owned
+ * rights.
+ *
+ * Stanford disclaimer of liability
+ * --------------------------------
+ * Stanford University makes no representations or warranties, express or
+ * implied, nor assumes any liability for the use of this software.
+ *
+ * Stanford disclaimer of copyright
+ * --------------------------------
+ * Stanford University, owner of the copyright, hereby disclaims its
+ * copyright and all other rights in this software. Hence, anyone may
+ * freely use it for any purpose without restriction.
+ *
+ * Maintenance of notices
+ * ----------------------
+ * In the interest of clarity regarding the origin and status of this
+ * SLAC software, this and all the preceding Stanford University notices
+ * are to remain affixed to any copy or derivative of this software made
+ * or distributed by the recipient and are to be affixed to any copy of
+ * software made or distributed by the recipient that contains a copy or
+ * derivative of this software.
+ *
+ * ------------------ SLAC Software Notices, Set 4 OTT.002a, 2004 FEB 03
+ */
+
+
+#include <rtems/pci.h>
+#include <rtems/bspIo.h>
+#include <inttypes.h>
+#include <stdio.h>
+
+/*
+ * Public methods from this file
+ */
+void pci_dump(FILE *f);
+
+/*
+ * These are used to construct the handle returned by pci_find_device()
+ * but the caller does not need to know how to decode them.
+ */
+#define PCIB_DEVSIG_BUS(x) (((x)>>8) &0xff)
+#define PCIB_DEVSIG_DEV(x) (((x)>>3) & 0x1f)
+#define PCIB_DEVSIG_FUNC(x) ((x) & 0x7)
+#define PCIB_DEVSIG_MAKE(b,d,f) ((b<<8)|(d<<3)|(f))
+
+/*
+ * Function pointer to helper function called during bus iteration.
+ */
+typedef int (*pci_scan_helper_t)(
+ int bus,
+ int dev,
+ int fun,
+ void *uarg
+);
+
+/*
+ *
+ */
+static uint32_t pci_scan(
+ uint32_t handle,
+ pci_scan_helper_t helper,
+ void *uarg
+);
+
+typedef struct {
+ uint16_t vendor_id;
+ uint16_t device_id;
+ int instance;
+ uint8_t bus;
+ uint8_t device;
+ uint8_t function;
+} pci_scan_arg_t;
+
+static int find_dev_helper(
+ int bus,
+ int device,
+ int function,
+ void *uarg
+)
+{
+ pci_scan_arg_t *scan = uarg;
+ uint16_t vendor_tmp;
+ uint16_t device_tmp;
+
+ pci_read_config_word(bus, device, function, PCI_VENDOR_ID, &vendor_tmp);
+ if (scan->vendor_id == vendor_tmp) {
+ pci_read_config_word(bus, device, function, PCI_DEVICE_ID, &device_tmp);
+ if (scan->device_id == device_tmp && scan->instance-- == 0) {
+ scan->bus = bus;
+ scan->device = device;
+ scan->function = function;
+
+ return 1;
+ }
+ }
+ return 0;
+}
+
+int pci_find_device(
+ uint16_t vendorid,
+ uint16_t deviceid,
+ int instance,
+ int *bus,
+ int *device,
+ int *function
+)
+{
+ pci_scan_arg_t scan;
+
+ scan.instance = instance;
+ scan.vendor_id = vendorid;
+ scan.device_id = deviceid;
+
+ if ( pci_scan(0, find_dev_helper, (void*)&scan) != 0 ) {
+ *bus = scan.bus;
+ *device = scan.device;
+ *function = scan.function;
+ return 0;
+ }
+ return -1;
+}
+
+static int dump_dev_helper(
+ int bus,
+ int device,
+ int function,
+ void *arg
+)
+{
+ uint16_t vendor_id;
+ uint16_t device_id;
+ uint16_t cmd;
+ uint16_t status;
+ uint32_t base0;
+ uint32_t base1;
+ uint8_t irq_pin;
+ uint8_t int_line;
+ FILE *fp = arg;
+
+ pci_read_config_word (bus, device, function, PCI_VENDOR_ID, &vendor_id);
+ pci_read_config_word (bus, device, function, PCI_DEVICE_ID, &device_id);
+ pci_read_config_word (bus, device, function, PCI_COMMAND, &cmd);
+ pci_read_config_word (bus, device, function, PCI_STATUS, &status);
+ pci_read_config_dword(bus, device, function, PCI_BASE_ADDRESS_0, &base0);
+ pci_read_config_dword(bus, device, function, PCI_BASE_ADDRESS_1, &base1);
+ pci_read_config_byte (bus, device, function, PCI_INTERRUPT_PIN, &irq_pin);
+ pci_read_config_byte (bus, device, function, PCI_INTERRUPT_LINE, &int_line);
+
+ fprintf(
+ fp,
+ "%3d:0x%02x:%d 0x%04x:0x%04x 0x%04x 0x%04x 0x%08" PRIx32 " 0x%08" PRIx32 " %d %3d(0x%02x)\n",
+ bus,
+ device,
+ function,
+ vendor_id,
+ device_id,
+ cmd,
+ status,
+ base0,
+ base1,
+ irq_pin,
+ int_line,
+ int_line
+ );
+ return 0;
+}
+
+void pci_dump(
+ FILE *fp
+)
+{
+ if ( !fp )
+ fp = stdout;
+ fprintf(
+ fp,
+ "BUS:SLOT:FUN VENDOR:DEV_ID CMD STAT BASE_ADDR0 BASE_ADDR1 INTn IRQ_LINE\n"
+ );
+ pci_scan(0, dump_dev_helper, fp);
+}
+
+static uint32_t pci_scan(
+ uint32_t handle,
+ pci_scan_helper_t helper,
+ void *arg
+)
+{
+ uint32_t vendor;
+ uint8_t bus;
+ uint8_t dev;
+ uint8_t fun;
+ uint8_t hd;
+
+ bus = PCIB_DEVSIG_BUS( (unsigned long)handle );
+ dev = PCIB_DEVSIG_DEV( (unsigned long)handle );
+ fun = PCIB_DEVSIG_FUNC( (unsigned long)handle );
+
+ hd = fun > 0 ? PCI_MAX_FUNCTIONS : 1;
+
+ for (; bus<pci_bus_count(); bus++, dev=0) {
+ for (; dev<PCI_MAX_DEVICES; dev++, fun=0) {
+ for (; fun<hd; fun++) {
+ /*
+ * The last devfn id/slot is special; must skip it
+ */
+ if ((PCI_MAX_DEVICES-1 == dev) && (PCI_MAX_FUNCTIONS-1 == fun) )
+ break;
+
+ (void)pci_read_config_dword(bus, dev, 0, PCI_VENDOR_ID, &vendor);
+ if (PCI_INVALID_VENDORDEVICEID == vendor)
+ continue;
+
+ if ( fun == 0 ) {
+ pci_read_config_byte(bus,dev, 0, PCI_HEADER_TYPE, &hd);
+ hd = (hd & PCI_HEADER_TYPE_MULTI_FUNCTION ? PCI_MAX_FUNCTIONS : 1);
+ }
+
+ (void)pci_read_config_dword(bus, dev, fun, PCI_VENDOR_ID, &vendor);
+ if (PCI_INVALID_VENDORDEVICEID == vendor)
+ continue;
+ #ifdef PCI_DEBUG
+ fprintf(
+ stderr,
+ "pci_scan: found 0x%08" PRIx32 " at %d/x%02x/%d\n", vendor, bus, dev, fun
+ );
+ #endif
+ if ( (*helper)(bus, dev, fun, arg) > 0 ) {
+ if ( ++fun >= hd ) {
+ fun = 0;
+ if ( ++dev >= PCI_MAX_DEVICES ) {
+ dev = 0;
+ bus++;
+ }
+ }
+ return PCIB_DEVSIG_MAKE(bus,dev,fun);
+ }
+ }
+ }
+ }
+ return 0;
+}