]> git.ipfire.org Git - thirdparty/ipxe.git/commitdiff
[pci] Add minimal PCI bridge driver
authorMichael Brown <mcb30@ipxe.org>
Mon, 19 Sep 2022 16:47:57 +0000 (17:47 +0100)
committerMichael Brown <mcb30@ipxe.org>
Mon, 19 Sep 2022 16:47:57 +0000 (17:47 +0100)
Add a minimal driver for PCI bridges that can be used to locate the
bridge to which a PCI device is attached.

Signed-off-by: Michael Brown <mcb30@ipxe.org>
src/drivers/bus/pcibridge.c [new file with mode: 0644]
src/include/ipxe/errfile.h
src/include/ipxe/pci.h
src/include/ipxe/pcibridge.h [new file with mode: 0644]

diff --git a/src/drivers/bus/pcibridge.c b/src/drivers/bus/pcibridge.c
new file mode 100644 (file)
index 0000000..d2763fa
--- /dev/null
@@ -0,0 +1,132 @@
+/*
+ * Copyright (C) 2022 Michael Brown <mbrown@fensystems.co.uk>.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ *
+ * You can also choose to distribute this program under the terms of
+ * the Unmodified Binary Distribution Licence (as given in the file
+ * COPYING.UBDL), provided that you have satisfied its requirements.
+ */
+
+FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+
+#include <stddef.h>
+#include <stdint.h>
+#include <errno.h>
+#include <byteswap.h>
+#include <ipxe/pci.h>
+#include <ipxe/pcibridge.h>
+
+/** @file
+ *
+ * PCI-to-PCI bridge
+ *
+ */
+
+/** List of all PCI bridges */
+static LIST_HEAD ( pcibridges );
+
+/**
+ * Find bridge attached to a PCI device
+ *
+ * @v pci              PCI device
+ * @ret bridge         PCI bridge, or NULL
+ */
+struct pci_bridge * pcibridge_find ( struct pci_device *pci ) {
+       unsigned int bus = PCI_BUS ( pci->busdevfn );
+       struct pci_bridge *bridge;
+
+       /* Find matching bridge */
+       list_for_each_entry ( bridge, &pcibridges, list ) {
+               if ( bus == bridge->secondary )
+                       return bridge;
+       }
+
+       return NULL;
+}
+
+/**
+ * Probe PCI device
+ *
+ * @v pci              PCI device
+ * @ret rc             Return status code
+ */
+static int pcibridge_probe ( struct pci_device *pci ) {
+       struct pci_bridge *bridge;
+       uint16_t base;
+       uint16_t limit;
+       int rc;
+
+       /* Allocate and initialise structure */
+       bridge = zalloc ( sizeof ( *bridge ) );
+       if ( ! bridge ) {
+               rc = -ENOMEM;
+               goto err_alloc;
+       }
+       bridge->pci = pci;
+
+       /* Read configuration */
+       pci_read_config_dword ( pci, PCI_PRIMARY, &bridge->buses );
+       cpu_to_le32s ( &buses );
+       pci_read_config_word ( pci, PCI_MEM_BASE, &base );
+       bridge->membase = ( ( base & ~PCI_MEM_MASK ) << 16 );
+       pci_read_config_word ( pci, PCI_MEM_LIMIT, &limit );
+       bridge->memlimit = ( ( ( ( limit | PCI_MEM_MASK ) + 1 ) << 16 ) - 1 );
+       DBGC ( bridge, "BRIDGE " PCI_FMT " bus %02x to [%02x,%02x) mem "
+              "[%08x,%08x)\n", PCI_ARGS ( pci ), bridge->primary,
+              bridge->secondary, bridge->subordinate, bridge->membase,
+              bridge->memlimit );
+
+       /* Add to list of PCI bridges */
+       list_add ( &bridge->list, &pcibridges );
+
+       pci_set_drvdata ( pci, bridge );
+       return 0;
+
+       free ( bridge );
+ err_alloc:
+       return rc;
+}
+
+/**
+ * Remove PCI device
+ *
+ * @v pci              PCI device
+ */
+static void pcibridge_remove ( struct pci_device *pci ) {
+       struct pci_bridge *bridge = pci_get_drvdata ( pci );
+
+       /* Remove from list of bridges */
+       list_del ( &bridge->list );
+
+       /* Free device */
+       free ( bridge );
+}
+
+/** Bridge PCI device IDs */
+static struct pci_device_id pcibridge_ids[] = {
+       PCI_ROM ( 0xffff, 0xffff, "bridge", "Bridge", 0 ),
+};
+
+/** Bridge PCI driver */
+struct pci_driver pcibridge_driver __pci_driver = {
+       .ids = pcibridge_ids,
+       .id_count = ( sizeof ( pcibridge_ids ) / sizeof ( pcibridge_ids[0] ) ),
+       .class = PCI_CLASS_ID ( PCI_CLASS_BRIDGE, PCI_CLASS_BRIDGE_PCI,
+                               PCI_ANY_ID ),
+       .probe = pcibridge_probe,
+       .remove = pcibridge_remove,
+};
index 2b8adbfd6e333fab0033b17d01ebb57b45504506..1fca74742524636307608f40257d87bebb438e4e 100644 (file)
@@ -217,6 +217,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
 #define ERRFILE_rdc                 ( ERRFILE_DRIVER | 0x00d10000 )
 #define ERRFILE_ice                 ( ERRFILE_DRIVER | 0x00d20000 )
 #define ERRFILE_ecam                ( ERRFILE_DRIVER | 0x00d30000 )
+#define ERRFILE_pcibridge           ( ERRFILE_DRIVER | 0x00d40000 )
 
 #define ERRFILE_aoe                    ( ERRFILE_NET | 0x00000000 )
 #define ERRFILE_arp                    ( ERRFILE_NET | 0x00010000 )
index c91baaddd69274281c1c757706dc4a49f5c05a2d..637b20d60f8b5652db55139a3501a7180fad3ceb 100644 (file)
@@ -127,6 +127,10 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
 /** Network controller */
 #define PCI_CLASS_NETWORK      0x02
 
+/** Bridge device */
+#define PCI_CLASS_BRIDGE       0x06
+#define PCI_CLASS_BRIDGE_PCI           0x04    /**< PCI-to-PCI bridge */
+
 /** Serial bus controller */
 #define PCI_CLASS_SERIAL       0x0c
 #define PCI_CLASS_SERIAL_USB           0x03    /**< USB controller */
@@ -135,9 +139,20 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
 #define PCI_CLASS_SERIAL_USB_EHCI       0x20   /**< ECHI USB controller */
 #define PCI_CLASS_SERIAL_USB_XHCI       0x30   /**< xHCI USB controller */
 
+/** Primary bus number */
+#define PCI_PRIMARY            0x18
+
+/** Secondary bus number */
+#define PCI_SECONDARY          0x19
+
 /** Subordinate bus number */
 #define PCI_SUBORDINATE                0x1a
 
+/** Memory base and limit */
+#define PCI_MEM_BASE           0x20
+#define PCI_MEM_LIMIT          0x22
+#define PCI_MEM_MASK                   0x000f
+
 /** Construct PCI class
  *
  * @v base             Base class (or PCI_ANY_ID)
diff --git a/src/include/ipxe/pcibridge.h b/src/include/ipxe/pcibridge.h
new file mode 100644 (file)
index 0000000..c57a810
--- /dev/null
@@ -0,0 +1,43 @@
+#ifndef _IPXE_PCIBRIDGE_H
+#define _IPXE_PCIBRIDGE_H
+
+/** @file
+ *
+ * PCI-to-PCI bridge
+ *
+ */
+
+FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+
+#include <stdint.h>
+#include <ipxe/list.h>
+#include <ipxe/pci.h>
+
+/** A PCI-to-PCI bridge */
+struct pci_bridge {
+       /** PCI device */
+       struct pci_device *pci;
+       /** Bridge numbers */
+       union {
+               /** Raw dword */
+               uint32_t buses;
+               struct {
+                       /** Primary bus */
+                       uint8_t primary;
+                       /** Secondary bus */
+                       uint8_t secondary;
+                       /** Subordinate bus */
+                       uint8_t subordinate;
+               } __attribute__ (( packed ));
+       };
+       /** Memory base */
+       uint32_t membase;
+       /** Memory limit */
+       uint32_t memlimit;
+       /** List of bridges */
+       struct list_head list;
+};
+
+extern struct pci_bridge * pcibridge_find ( struct pci_device *pci );
+
+#endif /* _IPXE_PCIBRIDGE_H */