diff options
author | Michael Brown <mcb30@ipxe.org> | 2022-09-19 17:47:57 +0100 |
---|---|---|
committer | Michael Brown <mcb30@ipxe.org> | 2022-09-19 17:47:57 +0100 |
commit | 3aa6b79c8d9dd0b4126c966aa83114dd0b9b4120 (patch) | |
tree | 99cb3ae4867260a4f9746f861551811e757c32be | |
parent | 649176cd608e74ce54d20488a0618b4c6d8be71d (diff) | |
download | ipxe-3aa6b79c8d9dd0b4126c966aa83114dd0b9b4120.zip ipxe-3aa6b79c8d9dd0b4126c966aa83114dd0b9b4120.tar.gz ipxe-3aa6b79c8d9dd0b4126c966aa83114dd0b9b4120.tar.bz2 |
[pci] Add minimal PCI bridge driver
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>
-rw-r--r-- | src/drivers/bus/pcibridge.c | 132 | ||||
-rw-r--r-- | src/include/ipxe/errfile.h | 1 | ||||
-rw-r--r-- | src/include/ipxe/pci.h | 15 | ||||
-rw-r--r-- | src/include/ipxe/pcibridge.h | 43 |
4 files changed, 191 insertions, 0 deletions
diff --git a/src/drivers/bus/pcibridge.c b/src/drivers/bus/pcibridge.c new file mode 100644 index 0000000..d2763fa --- /dev/null +++ b/src/drivers/bus/pcibridge.c @@ -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, +}; diff --git a/src/include/ipxe/errfile.h b/src/include/ipxe/errfile.h index 2b8adbf..1fca747 100644 --- a/src/include/ipxe/errfile.h +++ b/src/include/ipxe/errfile.h @@ -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 ) diff --git a/src/include/ipxe/pci.h b/src/include/ipxe/pci.h index c91baad..637b20d 100644 --- a/src/include/ipxe/pci.h +++ b/src/include/ipxe/pci.h @@ -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 index 0000000..c57a810 --- /dev/null +++ b/src/include/ipxe/pcibridge.h @@ -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 */ |