mirror of https://github.com/ipxe/ipxe.git
142 lines
4.0 KiB
C
142 lines
4.0 KiB
C
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
|
|
|
|
#include <stdint.h>
|
|
#include <ipxe/timer.h>
|
|
#include <ipxe/pci.h>
|
|
#include <ipxe/pcibackup.h>
|
|
|
|
static int pci_find_capability_common ( struct pci_device *pci,
|
|
uint8_t pos, int cap ) {
|
|
uint8_t id;
|
|
int ttl = 48;
|
|
|
|
while ( ttl-- && pos >= 0x40 ) {
|
|
pos &= ~3;
|
|
pci_read_config_byte ( pci, pos + PCI_CAP_ID, &id );
|
|
DBG ( "PCI Capability: %d\n", id );
|
|
if ( id == 0xff )
|
|
break;
|
|
if ( id == cap )
|
|
return pos;
|
|
pci_read_config_byte ( pci, pos + PCI_CAP_NEXT, &pos );
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* Look for a PCI capability
|
|
*
|
|
* @v pci PCI device to query
|
|
* @v cap Capability code
|
|
* @ret address Address of capability, or 0 if not found
|
|
*
|
|
* Determine whether or not a device supports a given PCI capability.
|
|
* Returns the address of the requested capability structure within
|
|
* the device's PCI configuration space, or 0 if the device does not
|
|
* support it.
|
|
*/
|
|
int pci_find_capability ( struct pci_device *pci, int cap ) {
|
|
uint16_t status;
|
|
uint8_t pos;
|
|
uint8_t hdr_type;
|
|
|
|
pci_read_config_word ( pci, PCI_STATUS, &status );
|
|
if ( ! ( status & PCI_STATUS_CAP_LIST ) )
|
|
return 0;
|
|
|
|
pci_read_config_byte ( pci, PCI_HEADER_TYPE, &hdr_type );
|
|
switch ( hdr_type & PCI_HEADER_TYPE_MASK ) {
|
|
case PCI_HEADER_TYPE_NORMAL:
|
|
case PCI_HEADER_TYPE_BRIDGE:
|
|
default:
|
|
pci_read_config_byte ( pci, PCI_CAPABILITY_LIST, &pos );
|
|
break;
|
|
case PCI_HEADER_TYPE_CARDBUS:
|
|
pci_read_config_byte ( pci, PCI_CB_CAPABILITY_LIST, &pos );
|
|
break;
|
|
}
|
|
return pci_find_capability_common ( pci, pos, cap );
|
|
}
|
|
|
|
/**
|
|
* Look for another PCI capability
|
|
*
|
|
* @v pci PCI device to query
|
|
* @v pos Address of the current capability
|
|
* @v cap Capability code
|
|
* @ret address Address of capability, or 0 if not found
|
|
*
|
|
* Determine whether or not a device supports a given PCI capability
|
|
* starting the search at a given address within the device's PCI
|
|
* configuration space. Returns the address of the next capability
|
|
* structure within the device's PCI configuration space, or 0 if the
|
|
* device does not support another such capability.
|
|
*/
|
|
int pci_find_next_capability ( struct pci_device *pci, int pos, int cap ) {
|
|
uint8_t new_pos;
|
|
|
|
pci_read_config_byte ( pci, pos + PCI_CAP_NEXT, &new_pos );
|
|
return pci_find_capability_common ( pci, new_pos, cap );
|
|
}
|
|
|
|
/**
|
|
* Find the size of a PCI BAR
|
|
*
|
|
* @v pci PCI device
|
|
* @v reg PCI register number
|
|
* @ret size BAR size
|
|
*
|
|
* It should not be necessary for any Etherboot code to call this
|
|
* function.
|
|
*/
|
|
unsigned long pci_bar_size ( struct pci_device *pci, unsigned int reg ) {
|
|
uint16_t cmd;
|
|
uint32_t start, size;
|
|
|
|
/* Save the original command register */
|
|
pci_read_config_word ( pci, PCI_COMMAND, &cmd );
|
|
/* Save the original bar */
|
|
pci_read_config_dword ( pci, reg, &start );
|
|
/* Compute which bits can be set */
|
|
pci_write_config_dword ( pci, reg, ~0 );
|
|
pci_read_config_dword ( pci, reg, &size );
|
|
/* Restore the original size */
|
|
pci_write_config_dword ( pci, reg, start );
|
|
/* Find the significant bits */
|
|
/* Restore the original command register. This reenables decoding. */
|
|
pci_write_config_word ( pci, PCI_COMMAND, cmd );
|
|
if ( start & PCI_BASE_ADDRESS_SPACE_IO ) {
|
|
size &= ~PCI_BASE_ADDRESS_IO_MASK;
|
|
} else {
|
|
size &= ~PCI_BASE_ADDRESS_MEM_MASK;
|
|
}
|
|
/* Find the lowest bit set */
|
|
size = size & ~( size - 1 );
|
|
return size;
|
|
}
|
|
|
|
/**
|
|
* Perform PCI Express function-level reset (FLR)
|
|
*
|
|
* @v pci PCI device
|
|
* @v exp PCI Express Capability address
|
|
*/
|
|
void pci_reset ( struct pci_device *pci, unsigned int exp ) {
|
|
struct pci_config_backup backup;
|
|
uint16_t control;
|
|
|
|
/* Back up configuration space */
|
|
pci_backup ( pci, &backup, PCI_CONFIG_BACKUP_STANDARD, NULL );
|
|
|
|
/* Perform a PCIe function-level reset */
|
|
pci_read_config_word ( pci, ( exp + PCI_EXP_DEVCTL ), &control );
|
|
control |= PCI_EXP_DEVCTL_FLR;
|
|
pci_write_config_word ( pci, ( exp + PCI_EXP_DEVCTL ), control );
|
|
|
|
/* Allow time for reset to complete */
|
|
mdelay ( PCI_EXP_FLR_DELAY_MS );
|
|
|
|
/* Restore configuration */
|
|
pci_restore ( pci, &backup, PCI_CONFIG_BACKUP_STANDARD, NULL );
|
|
}
|