mirror of
https://github.com/xcat2/xNBA.git
synced 2025-01-05 11:05:07 +00:00
Strip down i386 PCI configuration space I/O to the bare minimum. A
typical build will now include 880 bytes of PCI support code, compared to 2327 bytes in Etherboot 5.4. (There is a slight cost of around 5 extra bytes per access to a non-constant config space address; this should be an overall win. Driver-specific accesses will usually be to constant addresses, for which there is no additional cost.)
This commit is contained in:
parent
15ee09ed10
commit
6b6096d28b
@ -1,451 +0,0 @@
|
||||
/*
|
||||
** 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 <gpxe/init.h>
|
||||
#include <gpxe/pci.h>
|
||||
#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 ), <other output registers>
|
||||
* : "S" ( entry_point ), <other input registers> );
|
||||
* "=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;
|
||||
}
|
111
src/arch/i386/core/pcibios.c
Normal file
111
src/arch/i386/core/pcibios.c
Normal file
@ -0,0 +1,111 @@
|
||||
/*
|
||||
* Copyright (C) 2006 Michael Brown <mbrown@fensystems.co.uk>.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License as
|
||||
* published by the Free Software Foundation; either version 2 of the
|
||||
* License, or any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
#include <stdint.h>
|
||||
#include <gpxe/pci.h>
|
||||
#include <pcibios.h>
|
||||
#include <realmode.h>
|
||||
|
||||
/** @file
|
||||
*
|
||||
* PCI configuration space access via PCI BIOS
|
||||
*
|
||||
*/
|
||||
|
||||
/**
|
||||
* Determine maximum PCI bus number within system
|
||||
*
|
||||
* @ret max_bus Maximum bus number
|
||||
*/
|
||||
int pcibios_max_bus ( void ) {
|
||||
int discard_a;
|
||||
uint8_t max_bus;
|
||||
|
||||
REAL_EXEC ( rm_pcibios_check,
|
||||
"stc\n\t"
|
||||
"int $0x1a\n\t"
|
||||
"jnc 1f\n\t"
|
||||
"xorw %%cx, %%cx\n\t"
|
||||
"\n1:\n\t",
|
||||
2,
|
||||
OUT_CONSTRAINTS ( "=a" ( discard_a ), "=c" ( max_bus ) ),
|
||||
IN_CONSTRAINTS ( "a" ( PCIBIOS_INSTALLATION_CHECK >> 16 )),
|
||||
CLOBBER ( "ebx", "edx", "edi" ) );
|
||||
|
||||
return max_bus;
|
||||
}
|
||||
|
||||
/**
|
||||
* Read configuration space via PCI BIOS
|
||||
*
|
||||
* @v pci PCI device
|
||||
* @v command PCI BIOS command
|
||||
* @v value Value read
|
||||
* @ret rc Return status code
|
||||
*/
|
||||
int pcibios_read ( struct pci_device *pci, uint32_t command, uint32_t *value ){
|
||||
int discard_b, discard_D;
|
||||
int status;
|
||||
|
||||
REAL_EXEC ( rm_pcibios_read,
|
||||
"stc\n\t"
|
||||
"int $0x1a\n\t"
|
||||
"jnc 1f\n\t"
|
||||
"xorl %%eax, %%eax\n\t"
|
||||
"decl %%eax\n\t"
|
||||
"movl %%eax, %%ecx\n\t"
|
||||
"\n1:\n\t",
|
||||
4,
|
||||
OUT_CONSTRAINTS ( "=a" ( status ), "=b" ( discard_b ),
|
||||
"=c" ( *value ), "=D" ( discard_D ) ),
|
||||
IN_CONSTRAINTS ( "a" ( command >> 16 ),
|
||||
"b" ( ( pci->bus << 8 ) | pci->devfn ),
|
||||
"D" ( command ) ),
|
||||
CLOBBER ( "edx" ) );
|
||||
|
||||
return ( ( status >> 8 ) & 0xff );
|
||||
}
|
||||
|
||||
/**
|
||||
* Write configuration space via PCI BIOS
|
||||
*
|
||||
* @v pci PCI device
|
||||
* @v command PCI BIOS command
|
||||
* @v value Value to be written
|
||||
* @ret rc Return status code
|
||||
*/
|
||||
int pcibios_write ( struct pci_device *pci, uint32_t command, uint32_t value ){
|
||||
int discard_b, discard_c, discard_D;
|
||||
int status;
|
||||
|
||||
REAL_EXEC ( rm_pcibios_write,
|
||||
"stc\n\t"
|
||||
"int $0x1a\n\t"
|
||||
"jnc 1f\n\t"
|
||||
"movb $0xff, %%ah\n\t"
|
||||
"\n1:\n\t",
|
||||
4,
|
||||
OUT_CONSTRAINTS ( "=a" ( status ), "=b" ( discard_b ),
|
||||
"=c" ( discard_c ), "=D" ( discard_D ) ),
|
||||
IN_CONSTRAINTS ( "a" ( command >> 16 ),
|
||||
"b" ( ( pci->bus << 8 ) | pci->devfn ),
|
||||
"c" ( value ), "D" ( command ) ),
|
||||
CLOBBER ( "edx" ) );
|
||||
|
||||
return ( ( status >> 8 ) & 0xff );
|
||||
}
|
38
src/arch/i386/core/pcidirect.c
Normal file
38
src/arch/i386/core/pcidirect.c
Normal file
@ -0,0 +1,38 @@
|
||||
/*
|
||||
* Copyright (C) 2006 Michael Brown <mbrown@fensystems.co.uk>.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License as
|
||||
* published by the Free Software Foundation; either version 2 of the
|
||||
* License, or any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
#include <gpxe/pci.h>
|
||||
#include <pcidirect.h>
|
||||
|
||||
/** @file
|
||||
*
|
||||
* PCI configuration space access via Type 1 accesses
|
||||
*
|
||||
*/
|
||||
|
||||
/**
|
||||
* Prepare for Type 1 PCI configuration space access
|
||||
*
|
||||
* @v pci PCI device
|
||||
* @v where Location within PCI configuration space
|
||||
*/
|
||||
void pcidirect_prepare ( struct pci_device *pci, int where ) {
|
||||
outl ( ( 0x80000000 | ( pci->bus << 16 ) | ( pci->devfn << 8 ) |
|
||||
( where & ~3 ) ), PCIDIRECT_CONFIG_ADDRESS );
|
||||
}
|
||||
|
@ -1,20 +1,35 @@
|
||||
#ifndef PCI_IO_H
|
||||
#define PCI_IO_H
|
||||
#ifndef _PCI_IO_H
|
||||
#define _PCI_IO_H
|
||||
|
||||
/* %ah */
|
||||
#define PCIBIOS_PCI_FUNCTION_ID ( 0xb1 )
|
||||
/* %al */
|
||||
#define PCIBIOS_PCI_BIOS_PRESENT ( 0x01 )
|
||||
#define PCIBIOS_FIND_PCI_DEVICE ( 0x02 )
|
||||
#define PCIBIOS_FIND_PCI_CLASS_CODE ( 0x03 )
|
||||
#define PCIBIOS_GENERATE_SPECIAL_CYCLE ( 0x06 )
|
||||
#define PCIBIOS_READ_CONFIG_BYTE ( 0x08 )
|
||||
#define PCIBIOS_READ_CONFIG_WORD ( 0x09 )
|
||||
#define PCIBIOS_READ_CONFIG_DWORD ( 0x0a )
|
||||
#define PCIBIOS_WRITE_CONFIG_BYTE ( 0x0b )
|
||||
#define PCIBIOS_WRITE_CONFIG_WORD ( 0x0c )
|
||||
#define PCIBIOS_WRITE_CONFIG_DWORD ( 0x0d )
|
||||
#define PCIBIOS_GET_IRQ_ROUTING_OPTIONS ( 0x0e )
|
||||
#define PCIBIOS_SET_PCI_IRQ ( 0x0f )
|
||||
#include <pcibios.h>
|
||||
#include <pcidirect.h>
|
||||
|
||||
#endif /* PCI_IO_H */
|
||||
/** @file
|
||||
*
|
||||
* i386 PCI configuration space access
|
||||
*
|
||||
* We have two methods of PCI configuration space access: the PCI BIOS
|
||||
* and direct Type 1 accesses. Selecting between them is via the
|
||||
* compile-time switch -DCONFIG_PCI_DIRECT.
|
||||
*
|
||||
*/
|
||||
|
||||
#if CONFIG_PCI_DIRECT
|
||||
#define pci_max_bus pcidirect_max_bus
|
||||
#define pci_read_config_byte pcidirect_read_config_byte
|
||||
#define pci_read_config_word pcidirect_read_config_word
|
||||
#define pci_read_config_dword pcidirect_read_config_dword
|
||||
#define pci_write_config_byte pcidirect_write_config_byte
|
||||
#define pci_write_config_word pcidirect_write_config_word
|
||||
#define pci_write_config_dword pcidirect_write_config_dword
|
||||
#else /* CONFIG_PCI_DIRECT */
|
||||
#define pci_max_bus pcibios_max_bus
|
||||
#define pci_read_config_byte pcibios_read_config_byte
|
||||
#define pci_read_config_word pcibios_read_config_word
|
||||
#define pci_read_config_dword pcibios_read_config_dword
|
||||
#define pci_write_config_byte pcibios_write_config_byte
|
||||
#define pci_write_config_word pcibios_write_config_word
|
||||
#define pci_write_config_dword pcibios_write_config_dword
|
||||
#endif /* CONFIG_PCI_DIRECT */
|
||||
|
||||
#endif /* _PCI_IO_H */
|
||||
|
122
src/arch/i386/include/pcibios.h
Normal file
122
src/arch/i386/include/pcibios.h
Normal file
@ -0,0 +1,122 @@
|
||||
#ifndef _PCIBIOS_H
|
||||
#define _PCIBIOS_H
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
/** @file
|
||||
*
|
||||
* PCI configuration space access via PCI BIOS
|
||||
*
|
||||
*/
|
||||
|
||||
struct pci_device;
|
||||
|
||||
#define PCIBIOS_INSTALLATION_CHECK 0xb1010000
|
||||
#define PCIBIOS_READ_CONFIG_BYTE 0xb1080000
|
||||
#define PCIBIOS_READ_CONFIG_WORD 0xb1090000
|
||||
#define PCIBIOS_READ_CONFIG_DWORD 0xb10a0000
|
||||
#define PCIBIOS_WRITE_CONFIG_BYTE 0xb10b0000
|
||||
#define PCIBIOS_WRITE_CONFIG_WORD 0xb10c0000
|
||||
#define PCIBIOS_WRITE_CONFIG_DWORD 0xb10d0000
|
||||
|
||||
extern int pcibios_max_bus ( void );
|
||||
extern int pcibios_read ( struct pci_device *pci, uint32_t command,
|
||||
uint32_t *value );
|
||||
extern int pcibios_write ( struct pci_device *pci, uint32_t command,
|
||||
uint32_t value );
|
||||
|
||||
/**
|
||||
* Read byte from PCI configuration space via PCI BIOS
|
||||
*
|
||||
* @v pci PCI device
|
||||
* @v where Location within PCI configuration space
|
||||
* @v value Value read
|
||||
* @ret rc Return status code
|
||||
*/
|
||||
static inline __attribute__ (( always_inline )) int
|
||||
pcibios_read_config_byte ( struct pci_device *pci, unsigned int where,
|
||||
uint8_t *value ) {
|
||||
uint32_t tmp;
|
||||
int rc;
|
||||
|
||||
rc = pcibios_read ( pci, PCIBIOS_READ_CONFIG_BYTE | where, &tmp );
|
||||
*value = tmp;
|
||||
return rc;
|
||||
}
|
||||
|
||||
/**
|
||||
* Read word from PCI configuration space via PCI BIOS
|
||||
*
|
||||
* @v pci PCI device
|
||||
* @v where Location within PCI configuration space
|
||||
* @v value Value read
|
||||
* @ret rc Return status code
|
||||
*/
|
||||
static inline __attribute__ (( always_inline )) int
|
||||
pcibios_read_config_word ( struct pci_device *pci, unsigned int where,
|
||||
uint16_t *value ) {
|
||||
uint32_t tmp;
|
||||
int rc;
|
||||
|
||||
rc = pcibios_read ( pci, PCIBIOS_READ_CONFIG_WORD | where, &tmp );
|
||||
*value = tmp;
|
||||
return rc;
|
||||
}
|
||||
|
||||
/**
|
||||
* Read dword from PCI configuration space via PCI BIOS
|
||||
*
|
||||
* @v pci PCI device
|
||||
* @v where Location within PCI configuration space
|
||||
* @v value Value read
|
||||
* @ret rc Return status code
|
||||
*/
|
||||
static inline __attribute__ (( always_inline )) int
|
||||
pcibios_read_config_dword ( struct pci_device *pci, unsigned int where,
|
||||
uint32_t *value ) {
|
||||
return pcibios_read ( pci, PCIBIOS_READ_CONFIG_DWORD | where, value );
|
||||
}
|
||||
|
||||
/**
|
||||
* Write byte to PCI configuration space via PCI BIOS
|
||||
*
|
||||
* @v pci PCI device
|
||||
* @v where Location within PCI configuration space
|
||||
* @v value Value to be written
|
||||
* @ret rc Return status code
|
||||
*/
|
||||
static inline __attribute__ (( always_inline )) int
|
||||
pcibios_write_config_byte ( struct pci_device *pci, unsigned int where,
|
||||
uint8_t value ) {
|
||||
return pcibios_write ( pci, PCIBIOS_WRITE_CONFIG_BYTE | where, value );
|
||||
}
|
||||
|
||||
/**
|
||||
* Write word to PCI configuration space via PCI BIOS
|
||||
*
|
||||
* @v pci PCI device
|
||||
* @v where Location within PCI configuration space
|
||||
* @v value Value to be written
|
||||
* @ret rc Return status code
|
||||
*/
|
||||
static inline __attribute__ (( always_inline )) int
|
||||
pcibios_write_config_word ( struct pci_device *pci, unsigned int where,
|
||||
uint16_t value ) {
|
||||
return pcibios_write ( pci, PCIBIOS_WRITE_CONFIG_BYTE | where, value );
|
||||
}
|
||||
|
||||
/**
|
||||
* Write dword to PCI configuration space via PCI BIOS
|
||||
*
|
||||
* @v pci PCI device
|
||||
* @v where Location within PCI configuration space
|
||||
* @v value Value to be written
|
||||
* @ret rc Return status code
|
||||
*/
|
||||
static inline __attribute__ (( always_inline )) int
|
||||
pcibios_write_config_dword ( struct pci_device *pci, unsigned int where,
|
||||
uint32_t value ) {
|
||||
return pcibios_write ( pci, PCIBIOS_WRITE_CONFIG_BYTE | where, value );
|
||||
}
|
||||
|
||||
#endif /* _PCIBIOS_H */
|
126
src/arch/i386/include/pcidirect.h
Normal file
126
src/arch/i386/include/pcidirect.h
Normal file
@ -0,0 +1,126 @@
|
||||
#ifndef _PCIDIRECT_H
|
||||
#define _PCIDIRECT_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include <io.h>
|
||||
|
||||
/** @file
|
||||
*
|
||||
* PCI configuration space access via Type 1 accesses
|
||||
*
|
||||
*/
|
||||
|
||||
#define PCIDIRECT_CONFIG_ADDRESS 0xcf8
|
||||
#define PCIDIRECT_CONFIG_DATA 0xcfc
|
||||
|
||||
struct pci_device;
|
||||
|
||||
extern void pcidirect_prepare ( struct pci_device *pci, int where );
|
||||
|
||||
/**
|
||||
* Determine maximum PCI bus number within system
|
||||
*
|
||||
* @ret max_bus Maximum bus number
|
||||
*/
|
||||
static inline int pcidirect_max_bus ( void ) {
|
||||
/* No way to work this out via Type 1 accesses */
|
||||
return 0xff;
|
||||
}
|
||||
|
||||
/**
|
||||
* Read byte from PCI configuration space via Type 1 access
|
||||
*
|
||||
* @v pci PCI device
|
||||
* @v where Location within PCI configuration space
|
||||
* @v value Value read
|
||||
* @ret rc Return status code
|
||||
*/
|
||||
static inline __attribute__ (( always_inline )) int
|
||||
pcidirect_read_config_byte ( struct pci_device *pci, unsigned int where,
|
||||
uint8_t *value ) {
|
||||
pcidirect_prepare ( pci, where );
|
||||
*value = inb ( PCIDIRECT_CONFIG_DATA + ( where & 3 ) );
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Read word from PCI configuration space via Type 1 access
|
||||
*
|
||||
* @v pci PCI device
|
||||
* @v where Location within PCI configuration space
|
||||
* @v value Value read
|
||||
* @ret rc Return status code
|
||||
*/
|
||||
static inline __attribute__ (( always_inline )) int
|
||||
pcidirect_read_config_word ( struct pci_device *pci, unsigned int where,
|
||||
uint16_t *value ) {
|
||||
pcidirect_prepare ( pci, where );
|
||||
*value = inw ( PCIDIRECT_CONFIG_DATA + ( where & 2 ) );
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Read dword from PCI configuration space via Type 1 access
|
||||
*
|
||||
* @v pci PCI device
|
||||
* @v where Location within PCI configuration space
|
||||
* @v value Value read
|
||||
* @ret rc Return status code
|
||||
*/
|
||||
static inline __attribute__ (( always_inline )) int
|
||||
pcidirect_read_config_dword ( struct pci_device *pci, unsigned int where,
|
||||
uint32_t *value ) {
|
||||
pcidirect_prepare ( pci, where );
|
||||
*value = inl ( PCIDIRECT_CONFIG_DATA + where );
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Write byte to PCI configuration space via Type 1 access
|
||||
*
|
||||
* @v pci PCI device
|
||||
* @v where Location within PCI configuration space
|
||||
* @v value Value to be written
|
||||
* @ret rc Return status code
|
||||
*/
|
||||
static inline __attribute__ (( always_inline )) int
|
||||
pcidirect_write_config_byte ( struct pci_device *pci, unsigned int where,
|
||||
uint8_t value ) {
|
||||
pcidirect_prepare ( pci, where );
|
||||
outb ( value, PCIDIRECT_CONFIG_DATA + ( where & 3 ) );
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Write word to PCI configuration space via Type 1 access
|
||||
*
|
||||
* @v pci PCI device
|
||||
* @v where Location within PCI configuration space
|
||||
* @v value Value to be written
|
||||
* @ret rc Return status code
|
||||
*/
|
||||
static inline __attribute__ (( always_inline )) int
|
||||
pcidirect_write_config_word ( struct pci_device *pci, unsigned int where,
|
||||
uint16_t value ) {
|
||||
pcidirect_prepare ( pci, where );
|
||||
outb ( value, PCIDIRECT_CONFIG_DATA + ( where & 2 ) );
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Write dword to PCI configuration space via Type 1 access
|
||||
*
|
||||
* @v pci PCI device
|
||||
* @v where Location within PCI configuration space
|
||||
* @v value Value to be written
|
||||
* @ret rc Return status code
|
||||
*/
|
||||
static inline __attribute__ (( always_inline )) int
|
||||
pcidirect_write_config_dword ( struct pci_device *pci, unsigned int where,
|
||||
uint32_t value ) {
|
||||
pcidirect_prepare ( pci, where );
|
||||
outb ( value, PCIDIRECT_CONFIG_DATA + where );
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif /* _PCIDIRECT_H */
|
@ -38,15 +38,6 @@ static struct pci_driver pci_drivers_end[0] __table_end ( pci_drivers );
|
||||
|
||||
static void pcibus_remove ( struct root_device *rootdev );
|
||||
|
||||
/**
|
||||
* Maximum PCI bus number
|
||||
*
|
||||
* Architecture-specific code may know how many buses we have, in
|
||||
* which case it can overwrite this value.
|
||||
*
|
||||
*/
|
||||
unsigned int pci_max_bus = 0xff;
|
||||
|
||||
/**
|
||||
* Read PCI BAR
|
||||
*
|
||||
@ -242,13 +233,15 @@ static void unregister_pcidev ( struct pci_device *pci ) {
|
||||
*/
|
||||
static int pcibus_probe ( struct root_device *rootdev ) {
|
||||
struct pci_device *pci = NULL;
|
||||
unsigned int max_bus;
|
||||
unsigned int bus;
|
||||
unsigned int devfn;
|
||||
uint8_t hdrtype;
|
||||
uint8_t hdrtype = 0;
|
||||
uint32_t tmp;
|
||||
int rc;
|
||||
|
||||
for ( bus = 0 ; bus <= pci_max_bus ; bus++ ) {
|
||||
max_bus = pci_max_bus();
|
||||
for ( bus = 0 ; bus <= max_bus ; bus++ ) {
|
||||
for ( devfn = 0 ; devfn <= 0xff ; devfn++ ) {
|
||||
|
||||
/* Allocate struct pci_device */
|
||||
|
@ -19,6 +19,7 @@
|
||||
#include <stdint.h>
|
||||
#include <gpxe/device.h>
|
||||
#include <gpxe/tables.h>
|
||||
#include <pci_io.h>
|
||||
#include "pci_ids.h"
|
||||
|
||||
/*
|
||||
@ -318,19 +319,6 @@ struct pci_driver {
|
||||
.name = _name, \
|
||||
}
|
||||
|
||||
extern unsigned int pci_max_bus;
|
||||
extern int pci_read_config_byte ( struct pci_device *pci, unsigned int where,
|
||||
uint8_t *value );
|
||||
extern int pci_write_config_byte ( struct pci_device *pci, unsigned int where,
|
||||
uint8_t value );
|
||||
extern int pci_read_config_word ( struct pci_device *pci, unsigned int where,
|
||||
uint16_t *value );
|
||||
extern int pci_write_config_word ( struct pci_device *pci, unsigned int where,
|
||||
uint16_t value );
|
||||
extern int pci_read_config_dword ( struct pci_device *pci, unsigned int where,
|
||||
uint32_t *value );
|
||||
extern int pci_write_config_dword ( struct pci_device *pci, unsigned int where,
|
||||
uint32_t value );
|
||||
extern void adjust_pci_device ( struct pci_device *pci );
|
||||
extern unsigned long pci_bar_start ( struct pci_device *pci,
|
||||
unsigned int reg );
|
||||
|
Loading…
Reference in New Issue
Block a user