[pci] Save and restore PCI command register
[people/cooldavid/gpxe.git] / src / drivers / bus / pciextra.c
1 FILE_LICENCE ( GPL2_OR_LATER );
2
3 #include <stdint.h>
4 #include <gpxe/pci.h>
5
6 /**
7  * Look for a PCI capability
8  *
9  * @v pci               PCI device to query
10  * @v cap               Capability code
11  * @ret address         Address of capability, or 0 if not found
12  *
13  * Determine whether or not a device supports a given PCI capability.
14  * Returns the address of the requested capability structure within
15  * the device's PCI configuration space, or 0 if the device does not
16  * support it.
17  */
18 int pci_find_capability ( struct pci_device *pci, int cap ) {
19         uint16_t status;
20         uint8_t pos, id;
21         uint8_t hdr_type;
22         int ttl = 48;
23
24         pci_read_config_word ( pci, PCI_STATUS, &status );
25         if ( ! ( status & PCI_STATUS_CAP_LIST ) )
26                 return 0;
27
28         pci_read_config_byte ( pci, PCI_HEADER_TYPE, &hdr_type );
29         switch ( hdr_type & 0x7F ) {
30         case PCI_HEADER_TYPE_NORMAL:
31         case PCI_HEADER_TYPE_BRIDGE:
32         default:
33                 pci_read_config_byte ( pci, PCI_CAPABILITY_LIST, &pos );
34                 break;
35         case PCI_HEADER_TYPE_CARDBUS:
36                 pci_read_config_byte ( pci, PCI_CB_CAPABILITY_LIST, &pos );
37                 break;
38         }
39         while ( ttl-- && pos >= 0x40 ) {
40                 pos &= ~3;
41                 pci_read_config_byte ( pci, pos + PCI_CAP_LIST_ID, &id );
42                 DBG ( "PCI Capability: %d\n", id );
43                 if ( id == 0xff )
44                         break;
45                 if ( id == cap )
46                         return pos;
47                 pci_read_config_byte ( pci, pos + PCI_CAP_LIST_NEXT, &pos );
48         }
49         return 0;
50 }
51
52 /**
53  * Find the size of a PCI BAR
54  *
55  * @v pci               PCI device
56  * @v reg               PCI register number
57  * @ret size            BAR size
58  *
59  * It should not be necessary for any Etherboot code to call this
60  * function.
61  */
62 unsigned long pci_bar_size ( struct pci_device *pci, unsigned int reg ) {
63         uint16_t cmd;
64         uint32_t start, size;
65
66         /* Save the original command register */
67         pci_read_config_word ( pci, PCI_COMMAND, &cmd );
68         /* Save the original bar */
69         pci_read_config_dword ( pci, reg, &start );
70         /* Compute which bits can be set */
71         pci_write_config_dword ( pci, reg, ~0 );
72         pci_read_config_dword ( pci, reg, &size );
73         /* Restore the original size */
74         pci_write_config_dword ( pci, reg, start );
75         /* Find the significant bits */
76         /* Restore the original command register. This reenables decoding. */
77         pci_write_config_word ( pci, PCI_COMMAND, cmd );
78         if ( start & PCI_BASE_ADDRESS_SPACE_IO ) {
79                 size &= PCI_BASE_ADDRESS_IO_MASK;
80         } else {
81                 size &= PCI_BASE_ADDRESS_MEM_MASK;
82         }
83         /* Find the lowest bit set */
84         size = size & ~( size - 1 );
85         return size;
86 }