summaryrefslogtreecommitdiffstats
path: root/src/drivers/bus/pci.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/drivers/bus/pci.c')
-rw-r--r--src/drivers/bus/pci.c97
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 );