176d05dc695b06c4e987bb8078f78032441e1430cGreg HartmanFILE_LICENCE ( GPL2_OR_LATER );
276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman#include <stdint.h>
476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman#include <gpxe/pci.h>
576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman/**
776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * Look for a PCI capability
876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman *
976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * @v pci		PCI device to query
1076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * @v cap		Capability code
1176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * @ret address		Address of capability, or 0 if not found
1276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman *
1376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * Determine whether or not a device supports a given PCI capability.
1476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * Returns the address of the requested capability structure within
1576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * the device's PCI configuration space, or 0 if the device does not
1676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * support it.
1776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman */
1876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartmanint pci_find_capability ( struct pci_device *pci, int cap ) {
1976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	uint16_t status;
2076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	uint8_t pos, id;
2176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	uint8_t hdr_type;
2276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	int ttl = 48;
2376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
2476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	pci_read_config_word ( pci, PCI_STATUS, &status );
2576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	if ( ! ( status & PCI_STATUS_CAP_LIST ) )
2676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		return 0;
2776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
2876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	pci_read_config_byte ( pci, PCI_HEADER_TYPE, &hdr_type );
2976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	switch ( hdr_type & 0x7F ) {
3076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	case PCI_HEADER_TYPE_NORMAL:
3176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	case PCI_HEADER_TYPE_BRIDGE:
3276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	default:
3376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		pci_read_config_byte ( pci, PCI_CAPABILITY_LIST, &pos );
3476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		break;
3576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	case PCI_HEADER_TYPE_CARDBUS:
3676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		pci_read_config_byte ( pci, PCI_CB_CAPABILITY_LIST, &pos );
3776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		break;
3876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	}
3976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	while ( ttl-- && pos >= 0x40 ) {
4076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		pos &= ~3;
4176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		pci_read_config_byte ( pci, pos + PCI_CAP_LIST_ID, &id );
4276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		DBG ( "PCI Capability: %d\n", id );
4376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		if ( id == 0xff )
4476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman			break;
4576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		if ( id == cap )
4676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman			return pos;
4776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		pci_read_config_byte ( pci, pos + PCI_CAP_LIST_NEXT, &pos );
4876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	}
4976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	return 0;
5076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman}
5176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
5276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman/**
5376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * Find the size of a PCI BAR
5476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman *
5576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * @v pci		PCI device
5676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * @v reg		PCI register number
5776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * @ret size		BAR size
5876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman *
5976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * It should not be necessary for any Etherboot code to call this
6076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * function.
6176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman */
6276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartmanunsigned long pci_bar_size ( struct pci_device *pci, unsigned int reg ) {
6376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	uint16_t cmd;
6476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	uint32_t start, size;
6576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
6676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	/* Save the original command register */
6776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	pci_read_config_word ( pci, PCI_COMMAND, &cmd );
6876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	/* Save the original bar */
6976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	pci_read_config_dword ( pci, reg, &start );
7076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	/* Compute which bits can be set */
7176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	pci_write_config_dword ( pci, reg, ~0 );
7276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	pci_read_config_dword ( pci, reg, &size );
7376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	/* Restore the original size */
7476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	pci_write_config_dword ( pci, reg, start );
7576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	/* Find the significant bits */
7676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	/* Restore the original command register. This reenables decoding. */
7776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	pci_write_config_word ( pci, PCI_COMMAND, cmd );
7876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	if ( start & PCI_BASE_ADDRESS_SPACE_IO ) {
7976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		size &= PCI_BASE_ADDRESS_IO_MASK;
8076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	} else {
8176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		size &= PCI_BASE_ADDRESS_MEM_MASK;
8276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	}
8376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	/* Find the lowest bit set */
8476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	size = size & ~( size - 1 );
8576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	return size;
8676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman}
87