diff options
Diffstat (limited to 'src/drivers/bus/pci.c')
| -rw-r--r-- | src/drivers/bus/pci.c | 97 |
1 files changed, 97 insertions, 0 deletions
diff --git a/src/drivers/bus/pci.c b/src/drivers/bus/pci.c index 92b389641..30163300a 100644 --- a/src/drivers/bus/pci.c +++ b/src/drivers/bus/pci.c @@ -25,6 +25,7 @@ */ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); +FILE_SECBOOT ( PERMITTED ); #include <stdint.h> #include <stdlib.h> @@ -105,6 +106,92 @@ unsigned long pci_bar_start ( struct pci_device *pci, unsigned int reg ) { } /** + * Set the start of a PCI BAR + * + * @v pci PCI device + * @v reg PCI register number + * @v start BAR start address + */ +void pci_bar_set ( struct pci_device *pci, unsigned int reg, + unsigned long start ) { + unsigned int type; + uint32_t low; + uint32_t high; + uint16_t cmd; + + /* Save the original command register and disable decoding */ + pci_read_config_word ( pci, PCI_COMMAND, &cmd ); + pci_write_config_word ( pci, PCI_COMMAND, + ( cmd & ~( PCI_COMMAND_MEM | + PCI_COMMAND_IO ) ) ); + + /* Check for a 64-bit BAR */ + pci_read_config_dword ( pci, reg, &low ); + type = ( low & ( PCI_BASE_ADDRESS_SPACE_IO | + PCI_BASE_ADDRESS_MEM_TYPE_MASK ) ); + + /* Write low 32 bits */ + low = start; + pci_write_config_dword ( pci, reg, low ); + + /* Write high 32 bits, if applicable */ + if ( type == PCI_BASE_ADDRESS_MEM_TYPE_64 ) { + if ( sizeof ( unsigned long ) > sizeof ( uint32_t ) ) { + high = ( ( ( uint64_t ) start ) >> 32 ); + } else { + high = 0; + } + pci_write_config_dword ( pci, reg + 4, high ); + } + + /* Restore the original command register */ + pci_write_config_word ( pci, PCI_COMMAND, cmd ); +} + +/** + * Get the size of a PCI BAR + * + * @v pci PCI device + * @v reg PCI register number + * @ret size BAR size + * + * Most drivers should not need to call this function. It is not + * necessary to map the whole PCI BAR, only the portion that will be + * used for register access. Since register offsets are almost always + * fixed by hardware design, the length of the mapped portion will + * almost always be a compile-time constant. + */ +unsigned long pci_bar_size ( struct pci_device *pci, unsigned int reg ) { + unsigned long start; + unsigned long size; + uint16_t cmd; + + /* Save the original command register and disable decoding */ + pci_read_config_word ( pci, PCI_COMMAND, &cmd ); + pci_write_config_word ( pci, PCI_COMMAND, + ( cmd & ~( PCI_COMMAND_MEM | + PCI_COMMAND_IO ) ) ); + + /* Save the original start address */ + start = pci_bar_start ( pci, reg ); + + /* Set all possible bits */ + pci_bar_set ( pci, reg, -1UL ); + + /* Determine size by finding lowest set bit */ + size = pci_bar_start ( pci, reg ); + size &= ( -size ); + + /* Restore the original start address */ + pci_bar_set ( pci, reg, start ); + + /* Restore the original command register */ + pci_write_config_word ( pci, PCI_COMMAND, cmd ); + + return size; +} + +/** * Read membase and ioaddr for a PCI device * * @v pci PCI device @@ -374,6 +461,10 @@ static int pcibus_probe ( struct root_device *rootdev ) { if ( ( rc = pci_find_next ( pci, &busdevfn ) ) != 0 ) break; + /* Skip automatic probing if prohibited */ + if ( ! pci_can_probe ( pci ) ) + continue; + /* Look for a driver */ if ( ( rc = pci_find_driver ( pci ) ) != 0 ) { DBGC ( pci, PCI_FMT " (%04x:%04x class %06x) has no " @@ -434,3 +525,9 @@ struct root_device pci_root_device __root_device = { .dev = { .name = "PCI" }, .driver = &pci_root_driver, }; + +/* Drag in objects via pcibus_probe() */ +REQUIRING_SYMBOL ( pcibus_probe ); + +/* Drag in PCI configuration */ +REQUIRE_OBJECT ( config_pci ); |
