|  | @@ -0,0 +1,144 @@
 | 
		
	
		
			
			|  | 1 | +#include "etherboot.h"
 | 
		
	
		
			
			|  | 2 | +#include "pci.h"
 | 
		
	
		
			
			|  | 3 | +
 | 
		
	
		
			
			|  | 4 | +#undef DBG
 | 
		
	
		
			
			|  | 5 | +#ifdef DEBUG_PCI
 | 
		
	
		
			
			|  | 6 | +#define DBG(...) printf ( __VA_ARGS__ )
 | 
		
	
		
			
			|  | 7 | +#else
 | 
		
	
		
			
			|  | 8 | +#define DBG(...)
 | 
		
	
		
			
			|  | 9 | +#endif
 | 
		
	
		
			
			|  | 10 | +
 | 
		
	
		
			
			|  | 11 | +
 | 
		
	
		
			
			|  | 12 | +/*
 | 
		
	
		
			
			|  | 13 | + * Set device to be a busmaster in case BIOS neglected to do so.  Also
 | 
		
	
		
			
			|  | 14 | + * adjust PCI latency timer to a reasonable value, 32.
 | 
		
	
		
			
			|  | 15 | + */
 | 
		
	
		
			
			|  | 16 | +void adjust_pci_device ( struct pci_device *dev ) {
 | 
		
	
		
			
			|  | 17 | +	unsigned short	new_command, pci_command;
 | 
		
	
		
			
			|  | 18 | +	unsigned char	pci_latency;
 | 
		
	
		
			
			|  | 19 | +
 | 
		
	
		
			
			|  | 20 | +	pci_read_config_word ( dev, PCI_COMMAND, &pci_command );
 | 
		
	
		
			
			|  | 21 | +	new_command = pci_command | PCI_COMMAND_MASTER | PCI_COMMAND_IO;
 | 
		
	
		
			
			|  | 22 | +	if ( pci_command != new_command ) {
 | 
		
	
		
			
			|  | 23 | +		DBG ( "The PCI BIOS has not enabled this device!\n"
 | 
		
	
		
			
			|  | 24 | +		      "Updating PCI command %hX->%hX. bus %hhX dev_fn %hhX\n",
 | 
		
	
		
			
			|  | 25 | +		      pci_command, new_command, p->bus, p->devfn );
 | 
		
	
		
			
			|  | 26 | +		pci_write_config_word ( dev, PCI_COMMAND, new_command );
 | 
		
	
		
			
			|  | 27 | +	}
 | 
		
	
		
			
			|  | 28 | +	pci_read_config_byte ( dev, PCI_LATENCY_TIMER, &pci_latency);
 | 
		
	
		
			
			|  | 29 | +	if ( pci_latency < 32 ) {
 | 
		
	
		
			
			|  | 30 | +		DBG ( "PCI latency timer (CFLT) is unreasonably low at %d. "
 | 
		
	
		
			
			|  | 31 | +		      "Setting to 32 clocks.\n", pci_latency );
 | 
		
	
		
			
			|  | 32 | +		pci_write_config_byte ( dev, PCI_LATENCY_TIMER, 32);
 | 
		
	
		
			
			|  | 33 | +	}
 | 
		
	
		
			
			|  | 34 | +}
 | 
		
	
		
			
			|  | 35 | +
 | 
		
	
		
			
			|  | 36 | +/*
 | 
		
	
		
			
			|  | 37 | + * Find the start of a pci resource.
 | 
		
	
		
			
			|  | 38 | + */
 | 
		
	
		
			
			|  | 39 | +unsigned long pci_bar_start ( struct pci_device *dev, unsigned int index ) {
 | 
		
	
		
			
			|  | 40 | +	uint32_t lo, hi;
 | 
		
	
		
			
			|  | 41 | +	unsigned long bar;
 | 
		
	
		
			
			|  | 42 | +
 | 
		
	
		
			
			|  | 43 | +	pci_read_config_dword ( dev, index, &lo );
 | 
		
	
		
			
			|  | 44 | +	if ( lo & PCI_BASE_ADDRESS_SPACE_IO ) {
 | 
		
	
		
			
			|  | 45 | +		bar = lo & PCI_BASE_ADDRESS_IO_MASK;
 | 
		
	
		
			
			|  | 46 | +	} else {
 | 
		
	
		
			
			|  | 47 | +		bar = 0;
 | 
		
	
		
			
			|  | 48 | +		if ( ( lo & PCI_BASE_ADDRESS_MEM_TYPE_MASK ) ==
 | 
		
	
		
			
			|  | 49 | +		     PCI_BASE_ADDRESS_MEM_TYPE_64) {
 | 
		
	
		
			
			|  | 50 | +			pci_read_config_dword ( dev, index + 4, &hi );
 | 
		
	
		
			
			|  | 51 | +			if ( hi ) {
 | 
		
	
		
			
			|  | 52 | +#if ULONG_MAX > 0xffffffff
 | 
		
	
		
			
			|  | 53 | +					bar = hi;
 | 
		
	
		
			
			|  | 54 | +					bar <<= 32;
 | 
		
	
		
			
			|  | 55 | +#else
 | 
		
	
		
			
			|  | 56 | +					printf ( "Unhandled 64bit BAR\n" );
 | 
		
	
		
			
			|  | 57 | +					return -1UL;
 | 
		
	
		
			
			|  | 58 | +#endif
 | 
		
	
		
			
			|  | 59 | +			}
 | 
		
	
		
			
			|  | 60 | +		}
 | 
		
	
		
			
			|  | 61 | +		bar |= lo & PCI_BASE_ADDRESS_MEM_MASK;
 | 
		
	
		
			
			|  | 62 | +	}
 | 
		
	
		
			
			|  | 63 | +	return bar + pcibios_bus_base ( dev->bus );
 | 
		
	
		
			
			|  | 64 | +}
 | 
		
	
		
			
			|  | 65 | +
 | 
		
	
		
			
			|  | 66 | +/*
 | 
		
	
		
			
			|  | 67 | + * Find the size of a pci resource.
 | 
		
	
		
			
			|  | 68 | + */
 | 
		
	
		
			
			|  | 69 | +unsigned long pci_bar_size ( struct pci_device *dev, unsigned int bar ) {
 | 
		
	
		
			
			|  | 70 | +	uint32_t start, size;
 | 
		
	
		
			
			|  | 71 | +
 | 
		
	
		
			
			|  | 72 | +	/* Save the original bar */
 | 
		
	
		
			
			|  | 73 | +	pci_read_config_dword ( dev, bar, &start );
 | 
		
	
		
			
			|  | 74 | +	/* Compute which bits can be set */
 | 
		
	
		
			
			|  | 75 | +	pci_write_config_dword ( dev, bar, ~0 );
 | 
		
	
		
			
			|  | 76 | +	pci_read_config_dword ( dev, bar, &size );
 | 
		
	
		
			
			|  | 77 | +	/* Restore the original size */
 | 
		
	
		
			
			|  | 78 | +	pci_write_config_dword ( dev, bar, start );
 | 
		
	
		
			
			|  | 79 | +	/* Find the significant bits */
 | 
		
	
		
			
			|  | 80 | +	if ( start & PCI_BASE_ADDRESS_SPACE_IO ) {
 | 
		
	
		
			
			|  | 81 | +		size &= PCI_BASE_ADDRESS_IO_MASK;
 | 
		
	
		
			
			|  | 82 | +	} else {
 | 
		
	
		
			
			|  | 83 | +		size &= PCI_BASE_ADDRESS_MEM_MASK;
 | 
		
	
		
			
			|  | 84 | +	}
 | 
		
	
		
			
			|  | 85 | +	/* Find the lowest bit set */
 | 
		
	
		
			
			|  | 86 | +	size = size & ~( size - 1 );
 | 
		
	
		
			
			|  | 87 | +	return size;
 | 
		
	
		
			
			|  | 88 | +}
 | 
		
	
		
			
			|  | 89 | +
 | 
		
	
		
			
			|  | 90 | +/**
 | 
		
	
		
			
			|  | 91 | + * pci_find_capability - query for devices' capabilities 
 | 
		
	
		
			
			|  | 92 | + * @dev: PCI device to query
 | 
		
	
		
			
			|  | 93 | + * @cap: capability code
 | 
		
	
		
			
			|  | 94 | + *
 | 
		
	
		
			
			|  | 95 | + * Tell if a device supports a given PCI capability.
 | 
		
	
		
			
			|  | 96 | + * Returns the address of the requested capability structure within the
 | 
		
	
		
			
			|  | 97 | + * device's PCI configuration space or 0 in case the device does not
 | 
		
	
		
			
			|  | 98 | + * support it.  Possible values for @cap:
 | 
		
	
		
			
			|  | 99 | + *
 | 
		
	
		
			
			|  | 100 | + *  %PCI_CAP_ID_PM           Power Management 
 | 
		
	
		
			
			|  | 101 | + *
 | 
		
	
		
			
			|  | 102 | + *  %PCI_CAP_ID_AGP          Accelerated Graphics Port 
 | 
		
	
		
			
			|  | 103 | + *
 | 
		
	
		
			
			|  | 104 | + *  %PCI_CAP_ID_VPD          Vital Product Data 
 | 
		
	
		
			
			|  | 105 | + *
 | 
		
	
		
			
			|  | 106 | + *  %PCI_CAP_ID_SLOTID       Slot Identification 
 | 
		
	
		
			
			|  | 107 | + *
 | 
		
	
		
			
			|  | 108 | + *  %PCI_CAP_ID_MSI          Message Signalled Interrupts
 | 
		
	
		
			
			|  | 109 | + *
 | 
		
	
		
			
			|  | 110 | + *  %PCI_CAP_ID_CHSWP        CompactPCI HotSwap 
 | 
		
	
		
			
			|  | 111 | + */
 | 
		
	
		
			
			|  | 112 | +int pci_find_capability ( struct pci_device *dev, int cap ) {
 | 
		
	
		
			
			|  | 113 | +	uint16_t status;
 | 
		
	
		
			
			|  | 114 | +	uint8_t pos, id;
 | 
		
	
		
			
			|  | 115 | +	uint8_t hdr_type;
 | 
		
	
		
			
			|  | 116 | +	int ttl = 48;
 | 
		
	
		
			
			|  | 117 | +
 | 
		
	
		
			
			|  | 118 | +	pci_read_config_word ( dev, PCI_STATUS, &status );
 | 
		
	
		
			
			|  | 119 | +	if ( ! ( status & PCI_STATUS_CAP_LIST ) )
 | 
		
	
		
			
			|  | 120 | +		return 0;
 | 
		
	
		
			
			|  | 121 | +
 | 
		
	
		
			
			|  | 122 | +	pci_read_config_byte ( dev, PCI_HEADER_TYPE, &hdr_type );
 | 
		
	
		
			
			|  | 123 | +	switch ( hdr_type & 0x7F ) {
 | 
		
	
		
			
			|  | 124 | +	case PCI_HEADER_TYPE_NORMAL:
 | 
		
	
		
			
			|  | 125 | +	case PCI_HEADER_TYPE_BRIDGE:
 | 
		
	
		
			
			|  | 126 | +	default:
 | 
		
	
		
			
			|  | 127 | +		pci_read_config_byte ( dev, PCI_CAPABILITY_LIST, &pos );
 | 
		
	
		
			
			|  | 128 | +		break;
 | 
		
	
		
			
			|  | 129 | +	case PCI_HEADER_TYPE_CARDBUS:
 | 
		
	
		
			
			|  | 130 | +		pci_read_config_byte ( dev, PCI_CB_CAPABILITY_LIST, &pos );
 | 
		
	
		
			
			|  | 131 | +		break;
 | 
		
	
		
			
			|  | 132 | +	}
 | 
		
	
		
			
			|  | 133 | +	while ( ttl-- && pos >= 0x40 ) {
 | 
		
	
		
			
			|  | 134 | +		pos &= ~3;
 | 
		
	
		
			
			|  | 135 | +		pci_read_config_byte ( dev, pos + PCI_CAP_LIST_ID, &id );
 | 
		
	
		
			
			|  | 136 | +		DBG ( "Capability: %d\n", id );
 | 
		
	
		
			
			|  | 137 | +		if ( id == 0xff )
 | 
		
	
		
			
			|  | 138 | +			break;
 | 
		
	
		
			
			|  | 139 | +		if ( id == cap )
 | 
		
	
		
			
			|  | 140 | +			return pos;
 | 
		
	
		
			
			|  | 141 | +		pci_read_config_byte ( dev, pos + PCI_CAP_LIST_NEXT, &pos );
 | 
		
	
		
			
			|  | 142 | +	}
 | 
		
	
		
			
			|  | 143 | +	return 0;
 | 
		
	
		
			
			|  | 144 | +}
 |