/* ** Support for NE2000 PCI clones added David Monro June 1997 ** Generalised to other NICs by Ken Yap July 1997 ** ** Most of this is taken from: ** ** /usr/src/linux/drivers/pci/pci.c ** /usr/src/linux/include/linux/pci.h ** /usr/src/linux/arch/i386/bios32.c ** /usr/src/linux/include/linux/bios32.h ** /usr/src/linux/drivers/net/ne.c */ #include "etherboot.h" #include #include #include "pci_io.h" #ifdef KEEP_IT_REAL #include "realmode.h" #endif /* Macros for direct PCI access */ #define CONFIG_ADDRESS 0xcf8 #define CONFIG_DATA 0xcfc #define CONFIG_CMD( pci, where ) \ ( 0x80000000 | ( pci->bus << 16 ) | ( pci->devfn << 8 ) | \ ( where & ~3 ) ) /* Signatures for PCI BIOS */ #define BIOS_SIG(a,b,c,d) ( ( a<<0 ) + ( b<<8 ) + ( c<<16 ) + ( d<<24 ) ) #define PRINT_BIOS_SIG(x) ( (x) & 0xff ), ( ( (x)>>8 ) & 0xff ), \ ( ( (x)>>16 ) & 0xff ),( ( (x)>>24 ) & 0xff ) #define BIOS32_SIGNATURE BIOS_SIG ( '_', '3', '2', '_' ) #define PCI_SIGNATURE BIOS_SIG ( 'P', 'C', 'I', ' ' ) #define PCI_SERVICE BIOS_SIG ( '$', 'P', 'C', 'I' ) /* BIOS32 structure as found in PCI BIOS ROM */ struct bios32 { unsigned long signature; /* _32_ */ unsigned long entry; /* 32 bit physical address */ unsigned char revision; /* Revision level, 0 */ unsigned char length; /* Length in paragraphs */ unsigned char checksum; /* Should byte sum to zero */ unsigned char reserved[5]; /* Must be zero */ }; /* Values returned by BIOS32 service directory */ #define BIOS32_SERVICE_PRESENT 0x00 #define BIOS32_SERVICE_NOT_PRESENT 0x80 #define CF ( 1 << 0 ) /* PCI BIOS entry point */ #ifndef KEEP_IT_REAL static unsigned long pcibios32_entry; #endif static int have_pcibios; /* Macro for calling a 32-bit entry point with flat physical * addresses. Use in a statement such as * __asm__ ( FLAT_FAR_CALL_ESI, * : "=S" ( discard, or real output ), * : "S" ( entry_point ), ); * "=S" *must* be specified as an output, otherwise the compiler will * assume that it remains unaltered. */ #define FLAT_FAR_CALL_ESI "call _virt_to_phys\n\t" \ "pushl %%cs\n\t" \ "call *%%esi\n\t" \ "cli\n\t" \ "cld\n\t" \ "call _phys_to_virt\n\t" /* * Functions for accessing PCI configuration space directly with type * 1 accesses. * */ static inline int pcidirect_read_config_byte ( struct pci_device *pci, unsigned int where, uint8_t *value ) { outl ( CONFIG_CMD ( pci, where ), CONFIG_ADDRESS ); *value = inb ( CONFIG_DATA + ( where & 3 ) ); return 0; } static inline int pcidirect_read_config_word ( struct pci_device *pci, unsigned int where, uint16_t *value ) { outl ( CONFIG_CMD ( pci, where ), CONFIG_ADDRESS ); *value = inw ( CONFIG_DATA + ( where & 2 ) ); return 0; } static inline int pcidirect_read_config_dword ( struct pci_device *pci, unsigned int where, uint32_t *value ) { outl ( CONFIG_CMD ( pci, where ), CONFIG_ADDRESS ); *value = inl ( CONFIG_DATA ); return 0; } static inline int pcidirect_write_config_byte ( struct pci_device *pci, unsigned int where, uint8_t value ) { outl ( CONFIG_CMD ( pci, where ), CONFIG_ADDRESS ); outb ( value, CONFIG_DATA + ( where & 3 ) ); return 0; } static inline int pcidirect_write_config_word ( struct pci_device *pci, unsigned int where, uint16_t value ) { outl ( CONFIG_CMD ( pci, where ), CONFIG_ADDRESS ); outw ( value, CONFIG_DATA + ( where & 2 ) ); return 0; } static inline int pcidirect_write_config_dword ( struct pci_device *pci, unsigned int where, uint32_t value ) { outl ( CONFIG_CMD ( pci, where ), CONFIG_ADDRESS ); outl ( value, CONFIG_DATA ); return 0; } /* * Functions for accessing PCI configuration space directly via the * PCI BIOS. * * Under -DKEEP_IT_REAL, we use INT 1A, otherwise we use the BIOS32 * interface. */ #ifdef KEEP_IT_REAL static void find_pcibios16 ( void ) { uint16_t present; uint32_t signature; uint16_t flags; uint16_t revision; uint8_t max_bus; /* PCI BIOS installation check */ REAL_EXEC ( rm_pcibios_check, "int $0x1a\n\t" "pushfw\n\t" "popw %%si\n\t", 5, OUT_CONSTRAINTS ( "=a" ( present ), "=b" ( revision ), "=c" ( max_bus ), "=d" ( signature ), "=S" ( flags ) ), IN_CONSTRAINTS ( "a" ( ( PCIBIOS_PCI_FUNCTION_ID << 8 ) + PCIBIOS_PCI_BIOS_PRESENT ) ), CLOBBER ( "edi", "ebp" ) ); if ( ( flags & CF ) || ( ( present >> 8 ) != 0 ) || ( signature != PCI_SIGNATURE ) ) { DBG ( "PCI BIOS installation check failed\n" ); return; } /* We have a PCI BIOS */ DBG ( "Found 16-bit PCI BIOS interface with %d buses\n", max_bus + 1 ); have_pcibios = 1; pci_max_bus = max_bus; return; } INIT_FN ( INIT_PCIBIOS, find_pcibios16, NULL, NULL ); #define pcibios16_read_write( command, pci, where, value ) \ ( { \ uint32_t discard_b, discard_D; \ uint16_t ret; \ \ REAL_EXEC ( 999, /* need a local label */ \ "int $0x1a\n\t" \ "jc 1f\n\t" \ "xorw %%ax, %%ax\n\t" \ "\n1:\n\t", \ 5, \ OUT_CONSTRAINTS ( "=a" ( ret ), \ "=b" ( discard_b ), \ "=c" ( value ), \ "=D" ( discard_D ) ), \ IN_CONSTRAINTS ( "a" ( command + \ ( PCIBIOS_PCI_FUNCTION_ID << 8 ) ), \ "b" ( pci->busdevfn ), \ "c" ( value ), \ "D" ( where ) ), \ CLOBBER ( "edx", "esi", "ebp" ) ); \ \ ( ret >> 8 ); \ } ) #define pcibios_read_write pcibios16_read_write #else /* KEEP_IT_REAL */ /* * Locate the BIOS32 service directory by scanning for a valid BIOS32 * structure * */ static struct bios32 * find_bios32 ( void ) { uint32_t address; /* * Follow the standard procedure for locating the BIOS32 Service * directory by scanning the permissible address range from * 0xe0000 through 0xfffff for a valid BIOS32 structure. * */ for ( address = 0xe0000 ; address < 0xffff0 ; address += 16 ) { struct bios32 * candidate = phys_to_virt ( address ); unsigned int length, i; unsigned char sum; if ( candidate->signature != BIOS32_SIGNATURE ) continue; length = candidate->length * 16; if ( ! length ) continue; for ( sum = 0, i = 0 ; i < length ; i++ ) sum += ( ( char * ) candidate ) [i]; if ( sum != 0 ) continue; if ( candidate->revision != 0 ) { DBG ( "unsupported BIOS32 revision %d at %#x\n", candidate->revision, address ); continue; } DBG ( "BIOS32 Service Directory structure at %#x\n", address ); return candidate; } return NULL; } /* * Look up a service in the BIOS32 service directory * */ static unsigned long find_bios32_service ( struct bios32 * bios32, unsigned long service ) { uint8_t return_code; uint32_t address; uint32_t length; uint32_t entry; uint32_t discard; __asm__ ( FLAT_FAR_CALL_ESI : "=a" ( return_code ), "=b" ( address ), "=c" ( length ), "=d" ( entry ), "=S" ( discard ) : "a" ( service ), "b" ( 0 ), "S" ( bios32->entry ) : "edi", "ebp" ); switch ( return_code ) { case BIOS32_SERVICE_PRESENT: DBG ( "BIOS32 service %c%c%c%c present at %#x\n", PRINT_BIOS_SIG ( service ), ( address + entry ) ); return ( address + entry ); case BIOS32_SERVICE_NOT_PRESENT: DBG ( "BIOS32 service %c%c%c%c : not present\n", PRINT_BIOS_SIG ( service ) ); return 0; default: /* Shouldn't happen */ DBG ( "BIOS32 returned %#x for service %c%c%c%c!\n", return_code, PRINT_BIOS_SIG ( service ) ); return 0; } } /* * Find the 32-bit PCI BIOS interface, if present. * */ static void find_pcibios32 ( void ) { struct bios32 *bios32; uint32_t signature; uint16_t present; uint32_t flags; uint16_t revision; uint8_t max_bus; /* Locate BIOS32 service directory */ bios32 = find_bios32 (); if ( ! bios32 ) { DBG ( "No BIOS32\n" ); return; } /* Locate PCI BIOS service */ pcibios32_entry = find_bios32_service ( bios32, PCI_SERVICE ); if ( ! pcibios32_entry ) { DBG ( "No PCI BIOS\n" ); return; } /* PCI BIOS installation check */ __asm__ ( FLAT_FAR_CALL_ESI "pushfl\n\t" "popl %%esi\n\t" : "=a" ( present ), "=b" ( revision ), "=c" ( max_bus ), "=d" ( signature ), "=S" ( flags ) : "a" ( ( PCIBIOS_PCI_FUNCTION_ID << 8 ) + PCIBIOS_PCI_BIOS_PRESENT ), "S" ( pcibios32_entry ) : "edi", "ebp" ); if ( ( flags & CF ) || ( ( present >> 8 ) != 0 ) || ( signature != PCI_SIGNATURE ) ) { DBG ( "PCI BIOS installation check failed\n" ); return; } /* We have a PCI BIOS */ DBG ( "Found 32-bit PCI BIOS interface at %#x with %d bus(es)\n", pcibios32_entry, max_bus + 1 ); have_pcibios = 1; pci_max_bus = max_bus; return; } INIT_FN ( INIT_PCIBIOS, find_pcibios32, NULL, NULL ); #define pcibios32_read_write( command, pci, where, value ) \ ( { \ uint32_t discard_b, discard_D, discard_S; \ uint16_t ret; \ \ __asm__ ( FLAT_FAR_CALL_ESI \ "jc 1f\n\t" \ "xorl %%eax, %%eax\n\t" \ "\n1:\n\t" \ : "=a" ( ret ), "=b" ( discard_b ), \ "=c" ( value ), \ "=S" ( discard_S ), "=D" ( discard_D ) \ : "a" ( ( PCIBIOS_PCI_FUNCTION_ID << 8 ) \ + command ), \ "b" ( ( pci->bus << 8 ) | pci->devfn ), \ "c" ( value ), "D" ( where ), \ "S" ( pcibios32_entry ) \ : "edx", "ebp" ); \ \ ( ret >> 8 ); \ } ) #define pcibios_read_write pcibios32_read_write #endif /* KEEP_IT_REAL */ static inline int pcibios_read_config_byte ( struct pci_device *pci, unsigned int where, uint8_t *value ) { return pcibios_read_write ( PCIBIOS_READ_CONFIG_BYTE, pci, where, *value ); } static inline int pcibios_read_config_word ( struct pci_device *pci, unsigned int where, uint16_t *value ) { return pcibios_read_write ( PCIBIOS_READ_CONFIG_WORD, pci, where, *value ); } static inline int pcibios_read_config_dword ( struct pci_device *pci, unsigned int where, uint32_t *value ) { return pcibios_read_write ( PCIBIOS_READ_CONFIG_DWORD, pci, where, *value ); } static inline int pcibios_write_config_byte ( struct pci_device *pci, unsigned int where, uint8_t value ) { return pcibios_read_write ( PCIBIOS_WRITE_CONFIG_BYTE, pci, where, value ); } static inline int pcibios_write_config_word ( struct pci_device *pci, unsigned int where, uint16_t value ) { return pcibios_read_write ( PCIBIOS_WRITE_CONFIG_WORD, pci, where, value ); } static inline int pcibios_write_config_dword ( struct pci_device *pci, unsigned int where, uint32_t value ) { return pcibios_read_write ( PCIBIOS_WRITE_CONFIG_DWORD, pci, where, value ); } /* * Functions for accessing PCI configuration space via the PCI BIOS if * present, otherwise directly via type 1 accesses. * */ int pci_read_config_byte ( struct pci_device *pci, unsigned int where, uint8_t *value ) { return have_pcibios ? pcibios_read_config_byte ( pci, where, value ) : pcidirect_read_config_byte ( pci, where, value ); } int pci_read_config_word ( struct pci_device *pci, unsigned int where, uint16_t *value ) { return have_pcibios ? pcibios_read_config_word ( pci, where, value ) : pcidirect_read_config_word ( pci, where, value ); } int pci_read_config_dword ( struct pci_device *pci, unsigned int where, uint32_t *value ) { return have_pcibios ? pcibios_read_config_dword ( pci, where, value ) : pcidirect_read_config_dword ( pci, where, value ); } int pci_write_config_byte ( struct pci_device *pci, unsigned int where, uint8_t value ) { return have_pcibios ? pcibios_write_config_byte ( pci, where, value ) : pcidirect_write_config_byte ( pci, where, value ); } int pci_write_config_word ( struct pci_device *pci, unsigned int where, uint16_t value ) { return have_pcibios ? pcibios_write_config_word ( pci, where, value ) : pcidirect_write_config_word ( pci, where, value ); } int pci_write_config_dword ( struct pci_device *pci, unsigned int where, uint32_t value ) { return have_pcibios ? pcibios_write_config_dword ( pci, where, value ) : pcidirect_write_config_dword ( pci, where, value ); } unsigned long pci_bus_base ( struct pci_device *pci __unused ) { /* architecturally this must be 0 */ return 0; }