summaryrefslogtreecommitdiffstats
path: root/qemu/roms/ipxe/src/drivers/bus/pciextra.c
blob: c4417e0cbbc613085ba4715d480a75fd9faf8bbf (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
FILE_LICENCE ( GPL2_OR_LATER );

#include <stdint.h>
#include <ipxe/pci.h>

/**
 * 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, id;
	uint8_t hdr_type;
	int ttl = 48;

	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 & 0x7F ) {
	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;
	}
	while ( ttl-- && pos >= 0x40 ) {
		pos &= ~3;
		pci_read_config_byte ( pci, pos + PCI_CAP_LIST_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_LIST_NEXT, &pos );
	}
	return 0;
}

/**
 * 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;
}