Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
[pci] Modularise PCI device support
Some operating environments require (or at least prefer) that we do
not perform our own PCI bus scan, but deal only with specified
devices.  Modularise the PCI core to allow for this.

Signed-off-by: Michael Brown <mcb30@ipxe.org>
  • Loading branch information
mcb30 committed Feb 17, 2011
1 parent 5bde349 commit 4f4c214
Show file tree
Hide file tree
Showing 2 changed files with 107 additions and 47 deletions.
126 changes: 79 additions & 47 deletions src/drivers/bus/pci.c
Expand Up @@ -164,23 +164,54 @@ void adjust_pci_device ( struct pci_device *pci ) {
}

/**
* Probe a PCI device
* Read PCI device configuration
*
* @v pci PCI device
* @ret rc Return status code
*/
int pci_read_config ( struct pci_device *pci ) {
uint32_t tmp;

/* Check for physical device presence */
pci_read_config_dword ( pci, PCI_VENDOR_ID, &tmp );
if ( ( tmp == 0xffffffff ) || ( tmp == 0 ) )
return -ENODEV;

/* Populate struct pci_device */
pci->vendor = ( tmp & 0xffff );
pci->device = ( tmp >> 16 );
pci_read_config_dword ( pci, PCI_REVISION, &tmp );
pci->class = ( tmp >> 8 );
pci_read_config_byte ( pci, PCI_INTERRUPT_LINE, &pci->irq );
pci_read_bases ( pci );

/* Initialise generic device component */
snprintf ( pci->dev.name, sizeof ( pci->dev.name ),
"PCI%02x:%02x.%x", PCI_BUS ( pci->busdevfn ),
PCI_SLOT ( pci->busdevfn ), PCI_FUNC ( pci->busdevfn ) );
pci->dev.desc.bus_type = BUS_TYPE_PCI;
pci->dev.desc.location = pci->busdevfn;
pci->dev.desc.vendor = pci->vendor;
pci->dev.desc.device = pci->device;
pci->dev.desc.class = pci->class;
pci->dev.desc.ioaddr = pci->ioaddr;
pci->dev.desc.irq = pci->irq;
INIT_LIST_HEAD ( &pci->dev.siblings );
INIT_LIST_HEAD ( &pci->dev.children );

return 0;
}

/**
* Find driver for PCI device
*
* Searches for a driver for the PCI device. If a driver is found,
* its probe() routine is called.
* @v pci PCI device
* @ret rc Return status code
*/
static int pci_probe ( struct pci_device *pci ) {
int pci_find_driver ( struct pci_device *pci ) {
struct pci_driver *driver;
struct pci_device_id *id;
unsigned int i;
int rc;

DBGC ( pci, PCI_FMT " is %04x:%04x mem %lx io %lx irq %d\n",
PCI_ARGS ( pci ), pci->vendor, pci->device, pci->membase,
pci->ioaddr, pci->irq );

for_each_table_entry ( driver, PCI_DRIVERS ) {
for ( i = 0 ; i < driver->id_count ; i++ ) {
Expand All @@ -191,29 +222,45 @@ static int pci_probe ( struct pci_device *pci ) {
if ( ( id->device != PCI_ANY_ID ) &&
( id->device != pci->device ) )
continue;
pci->driver = driver;
pci->id = id;
DBGC ( pci, "...using driver %s\n", pci->id->name );
if ( ( rc = driver->probe ( pci ) ) != 0 ) {
DBGC ( pci, "......probe failed: %s\n",
strerror ( rc ) );
continue;
}
DBGC ( pci, PCI_FMT " added\n", PCI_ARGS ( pci ) );
pci_set_driver ( pci, driver, id );
return 0;
}
}
return -ENOENT;
}

DBGC ( pci, "...no driver found\n" );
return -ENOTTY;
/**
* Probe a PCI device
*
* @v pci PCI device
* @ret rc Return status code
*
* Searches for a driver for the PCI device. If a driver is found,
* its probe() routine is called.
*/
int pci_probe ( struct pci_device *pci ) {
int rc;

DBGC ( pci, PCI_FMT " (%04x:%04x) has driver \"%s\"\n",
PCI_ARGS ( pci ), pci->vendor, pci->device, pci->id->name );
DBGC ( pci, PCI_FMT " has mem %lx io %lx irq %d\n",
PCI_ARGS ( pci ), pci->membase, pci->ioaddr, pci->irq );

if ( ( rc = pci->driver->probe ( pci ) ) != 0 ) {
DBGC ( pci, PCI_FMT " probe failed: %s\n",
PCI_ARGS ( pci ), strerror ( rc ) );
return rc;
}

return 0;
}

/**
* Remove a PCI device
*
* @v pci PCI device
*/
static void pci_remove ( struct pci_device *pci ) {
void pci_remove ( struct pci_device *pci ) {
pci->driver->remove ( pci );
DBGC ( pci, PCI_FMT " removed\n", PCI_ARGS ( pci ) );
}
Expand All @@ -231,7 +278,6 @@ static int pcibus_probe ( struct root_device *rootdev ) {
unsigned int num_bus;
unsigned int busdevfn;
uint8_t hdrtype = 0;
uint32_t tmp;
int rc;

num_bus = pci_num_bus();
Expand All @@ -246,7 +292,7 @@ static int pcibus_probe ( struct root_device *rootdev ) {
goto err;
}
memset ( pci, 0, sizeof ( *pci ) );
pci->busdevfn = busdevfn;
pci_init ( pci, busdevfn );

/* Skip all but the first function on
* non-multifunction cards
Expand All @@ -258,37 +304,23 @@ static int pcibus_probe ( struct root_device *rootdev ) {
continue;
}

/* Check for physical device presence */
pci_read_config_dword ( pci, PCI_VENDOR_ID, &tmp );
if ( ( tmp == 0xffffffff ) || ( tmp == 0 ) )
/* Read device configuration */
if ( ( rc = pci_read_config ( pci ) ) != 0 )
continue;

/* Populate struct pci_device */
pci->vendor = ( tmp & 0xffff );
pci->device = ( tmp >> 16 );
pci_read_config_dword ( pci, PCI_REVISION, &tmp );
pci->class = ( tmp >> 8 );
pci_read_config_byte ( pci, PCI_INTERRUPT_LINE,
&pci->irq );
pci_read_bases ( pci );

/* Look for a driver */
if ( ( rc = pci_find_driver ( pci ) ) != 0 ) {
DBGC ( pci, PCI_FMT " (%04x:%04x) has no driver\n",
PCI_ARGS ( pci ), pci->vendor, pci->device );
continue;
}

/* Add to device hierarchy */
snprintf ( pci->dev.name, sizeof ( pci->dev.name ),
"PCI%02x:%02x.%x", PCI_BUS ( busdevfn ),
PCI_SLOT ( busdevfn ), PCI_FUNC ( busdevfn ) );
pci->dev.desc.bus_type = BUS_TYPE_PCI;
pci->dev.desc.location = pci->busdevfn;
pci->dev.desc.vendor = pci->vendor;
pci->dev.desc.device = pci->device;
pci->dev.desc.class = pci->class;
pci->dev.desc.ioaddr = pci->ioaddr;
pci->dev.desc.irq = pci->irq;
pci->dev.parent = &rootdev->dev;
list_add ( &pci->dev.siblings, &rootdev->dev.children);
INIT_LIST_HEAD ( &pci->dev.children );

/* Look for a driver */
if ( pci_probe ( pci ) == 0 ) {
if ( ( rc = pci_probe ( pci ) ) == 0 ) {
/* pcidev registered, we can drop our ref */
pci = NULL;
} else {
Expand Down
28 changes: 28 additions & 0 deletions src/include/ipxe/pci.h
Expand Up @@ -381,9 +381,37 @@ struct pci_driver {
extern void adjust_pci_device ( struct pci_device *pci );
extern unsigned long pci_bar_start ( struct pci_device *pci,
unsigned int reg );
extern int pci_read_config ( struct pci_device *pci );
extern int pci_find_driver ( struct pci_device *pci );
extern int pci_probe ( struct pci_device *pci );
extern void pci_remove ( struct pci_device *pci );
extern int pci_find_capability ( struct pci_device *pci, int capability );
extern unsigned long pci_bar_size ( struct pci_device *pci, unsigned int reg );

/**
* Initialise PCI device
*
* @v pci PCI device
* @v busdevfn PCI bus:dev.fn address
*/
static inline void pci_init ( struct pci_device *pci, unsigned int busdevfn ) {
pci->busdevfn = busdevfn;
}

/**
* Set PCI driver
*
* @v pci PCI device
* @v driver PCI driver
* @v id PCI device ID
*/
static inline void pci_set_driver ( struct pci_device *pci,
struct pci_driver *driver,
struct pci_device_id *id ) {
pci->driver = driver;
pci->id = id;
}

/**
* Set PCI driver-private data
*
Expand Down

0 comments on commit 4f4c214

Please sign in to comment.