2005-04-17 02:20:36 +04:00
/*
* direct . c - Low - level direct PCI config space access
*/
# include <linux/pci.h>
# include <linux/init.h>
2006-04-07 21:49:36 +04:00
# include <linux/dmi.h>
2005-04-17 02:20:36 +04:00
# include "pci.h"
/*
2007-09-03 12:17:39 +04:00
* Functions for accessing PCI base ( first 256 bytes ) and extended
* ( 4096 bytes per PCI function ) configuration space with type 1
* accesses .
2005-04-17 02:20:36 +04:00
*/
# define PCI_CONF1_ADDRESS(bus, devfn, reg) \
2007-09-03 12:17:39 +04:00
( 0x80000000 | ( ( reg & 0xF00 ) < < 16 ) | ( bus < < 16 ) \
| ( devfn < < 8 ) | ( reg & 0xFC ) )
2005-04-17 02:20:36 +04:00
2008-02-10 17:45:28 +03:00
static int pci_conf1_read ( unsigned int seg , unsigned int bus ,
2005-04-17 02:20:36 +04:00
unsigned int devfn , int reg , int len , u32 * value )
{
unsigned long flags ;
2007-09-03 12:17:39 +04:00
if ( ( bus > 255 ) | | ( devfn > 255 ) | | ( reg > 4095 ) ) {
2006-04-07 21:50:15 +04:00
* value = - 1 ;
2005-04-17 02:20:36 +04:00
return - EINVAL ;
2006-04-07 21:50:15 +04:00
}
2005-04-17 02:20:36 +04:00
spin_lock_irqsave ( & pci_config_lock , flags ) ;
outl ( PCI_CONF1_ADDRESS ( bus , devfn , reg ) , 0xCF8 ) ;
switch ( len ) {
case 1 :
* value = inb ( 0xCFC + ( reg & 3 ) ) ;
break ;
case 2 :
* value = inw ( 0xCFC + ( reg & 2 ) ) ;
break ;
case 4 :
* value = inl ( 0xCFC ) ;
break ;
}
spin_unlock_irqrestore ( & pci_config_lock , flags ) ;
return 0 ;
}
2008-02-10 17:45:28 +03:00
static int pci_conf1_write ( unsigned int seg , unsigned int bus ,
2005-04-17 02:20:36 +04:00
unsigned int devfn , int reg , int len , u32 value )
{
unsigned long flags ;
2007-09-03 12:17:39 +04:00
if ( ( bus > 255 ) | | ( devfn > 255 ) | | ( reg > 4095 ) )
2005-04-17 02:20:36 +04:00
return - EINVAL ;
spin_lock_irqsave ( & pci_config_lock , flags ) ;
outl ( PCI_CONF1_ADDRESS ( bus , devfn , reg ) , 0xCF8 ) ;
switch ( len ) {
case 1 :
outb ( ( u8 ) value , 0xCFC + ( reg & 3 ) ) ;
break ;
case 2 :
outw ( ( u16 ) value , 0xCFC + ( reg & 2 ) ) ;
break ;
case 4 :
outl ( ( u32 ) value , 0xCFC ) ;
break ;
}
spin_unlock_irqrestore ( & pci_config_lock , flags ) ;
return 0 ;
}
# undef PCI_CONF1_ADDRESS
struct pci_raw_ops pci_direct_conf1 = {
. read = pci_conf1_read ,
. write = pci_conf1_write ,
} ;
/*
* Functions for accessing PCI configuration space with type 2 accesses
*/
# define PCI_CONF2_ADDRESS(dev, reg) (u16)(0xC000 | (dev << 8) | reg)
static int pci_conf2_read ( unsigned int seg , unsigned int bus ,
unsigned int devfn , int reg , int len , u32 * value )
{
unsigned long flags ;
int dev , fn ;
2006-04-11 14:54:48 +04:00
if ( ( bus > 255 ) | | ( devfn > 255 ) | | ( reg > 255 ) ) {
* value = - 1 ;
2005-04-17 02:20:36 +04:00
return - EINVAL ;
2006-04-11 14:54:48 +04:00
}
2005-04-17 02:20:36 +04:00
dev = PCI_SLOT ( devfn ) ;
fn = PCI_FUNC ( devfn ) ;
if ( dev & 0x10 )
return PCIBIOS_DEVICE_NOT_FOUND ;
spin_lock_irqsave ( & pci_config_lock , flags ) ;
outb ( ( u8 ) ( 0xF0 | ( fn < < 1 ) ) , 0xCF8 ) ;
outb ( ( u8 ) bus , 0xCFA ) ;
switch ( len ) {
case 1 :
* value = inb ( PCI_CONF2_ADDRESS ( dev , reg ) ) ;
break ;
case 2 :
* value = inw ( PCI_CONF2_ADDRESS ( dev , reg ) ) ;
break ;
case 4 :
* value = inl ( PCI_CONF2_ADDRESS ( dev , reg ) ) ;
break ;
}
outb ( 0 , 0xCF8 ) ;
spin_unlock_irqrestore ( & pci_config_lock , flags ) ;
return 0 ;
}
static int pci_conf2_write ( unsigned int seg , unsigned int bus ,
unsigned int devfn , int reg , int len , u32 value )
{
unsigned long flags ;
int dev , fn ;
if ( ( bus > 255 ) | | ( devfn > 255 ) | | ( reg > 255 ) )
return - EINVAL ;
dev = PCI_SLOT ( devfn ) ;
fn = PCI_FUNC ( devfn ) ;
if ( dev & 0x10 )
return PCIBIOS_DEVICE_NOT_FOUND ;
spin_lock_irqsave ( & pci_config_lock , flags ) ;
outb ( ( u8 ) ( 0xF0 | ( fn < < 1 ) ) , 0xCF8 ) ;
outb ( ( u8 ) bus , 0xCFA ) ;
switch ( len ) {
case 1 :
outb ( ( u8 ) value , PCI_CONF2_ADDRESS ( dev , reg ) ) ;
break ;
case 2 :
outw ( ( u16 ) value , PCI_CONF2_ADDRESS ( dev , reg ) ) ;
break ;
case 4 :
outl ( ( u32 ) value , PCI_CONF2_ADDRESS ( dev , reg ) ) ;
break ;
}
outb ( 0 , 0xCF8 ) ;
spin_unlock_irqrestore ( & pci_config_lock , flags ) ;
return 0 ;
}
# undef PCI_CONF2_ADDRESS
static struct pci_raw_ops pci_direct_conf2 = {
. read = pci_conf2_read ,
. write = pci_conf2_write ,
} ;
/*
* Before we decide to use direct hardware access mechanisms , we try to do some
* trivial checks to ensure it at least _seems_ to be working - - we just test
* whether bus 00 contains a host bridge ( this is similar to checking
* techniques used in XFree86 , but ours should be more reliable since we
* attempt to make use of direct access hints provided by the PCI BIOS ) .
*
* This should be close to trivial , but it isn ' t , because there are buggy
* chipsets ( yes , you guessed it , by Intel and Compaq ) that have no class ID .
*/
static int __init pci_sanity_check ( struct pci_raw_ops * o )
{
u32 x = 0 ;
int devfn ;
if ( pci_probe & PCI_NO_CHECKS )
return 1 ;
2006-04-07 21:49:36 +04:00
/* Assume Type 1 works for newer systems.
This handles machines that don ' t have anything on PCI Bus 0. */
if ( dmi_get_year ( DMI_BIOS_DATE ) > = 2001 )
return 1 ;
2005-04-17 02:20:36 +04:00
for ( devfn = 0 ; devfn < 0x100 ; devfn + + ) {
if ( o - > read ( 0 , 0 , devfn , PCI_CLASS_DEVICE , 2 , & x ) )
continue ;
if ( x = = PCI_CLASS_BRIDGE_HOST | | x = = PCI_CLASS_DISPLAY_VGA )
return 1 ;
if ( o - > read ( 0 , 0 , devfn , PCI_VENDOR_ID , 2 , & x ) )
continue ;
if ( x = = PCI_VENDOR_ID_INTEL | | x = = PCI_VENDOR_ID_COMPAQ )
return 1 ;
}
2005-11-24 02:45:09 +03:00
DBG ( KERN_WARNING " PCI: Sanity check failed \n " ) ;
2005-04-17 02:20:36 +04:00
return 0 ;
}
static int __init pci_check_type1 ( void )
{
unsigned long flags ;
unsigned int tmp ;
int works = 0 ;
local_irq_save ( flags ) ;
outb ( 0x01 , 0xCFB ) ;
tmp = inl ( 0xCF8 ) ;
outl ( 0x80000000 , 0xCF8 ) ;
if ( inl ( 0xCF8 ) = = 0x80000000 & & pci_sanity_check ( & pci_direct_conf1 ) ) {
works = 1 ;
}
outl ( tmp , 0xCF8 ) ;
local_irq_restore ( flags ) ;
return works ;
}
static int __init pci_check_type2 ( void )
{
unsigned long flags ;
int works = 0 ;
local_irq_save ( flags ) ;
outb ( 0x00 , 0xCFB ) ;
outb ( 0x00 , 0xCF8 ) ;
outb ( 0x00 , 0xCFA ) ;
if ( inb ( 0xCF8 ) = = 0x00 & & inb ( 0xCFA ) = = 0x00 & &
pci_sanity_check ( & pci_direct_conf2 ) ) {
works = 1 ;
}
local_irq_restore ( flags ) ;
return works ;
}
2006-09-26 12:52:40 +04:00
void __init pci_direct_init ( int type )
{
2006-10-05 20:47:22 +04:00
if ( type = = 0 )
return ;
2008-02-29 10:56:50 +03:00
printk ( KERN_INFO " PCI: Using configuration type %d for base access \n " ,
type ) ;
2007-09-03 12:17:39 +04:00
if ( type = = 1 ) {
2006-09-26 12:52:40 +04:00
raw_pci_ops = & pci_direct_conf1 ;
2008-06-12 22:19:23 +04:00
if ( raw_pci_ext_ops )
return ;
if ( ! ( pci_probe & PCI_HAS_IO_ECS ) )
return ;
printk ( KERN_INFO " PCI: Using configuration type 1 "
" for extended access \n " ) ;
raw_pci_ext_ops = & pci_direct_conf1 ;
return ;
2007-09-03 12:17:39 +04:00
}
2008-06-12 22:19:23 +04:00
raw_pci_ops = & pci_direct_conf2 ;
2006-09-26 12:52:40 +04:00
}
int __init pci_direct_probe ( void )
2005-04-17 02:20:36 +04:00
{
struct resource * region , * region2 ;
if ( ( pci_probe & PCI_PROBE_CONF1 ) = = 0 )
goto type2 ;
region = request_region ( 0xCF8 , 8 , " PCI conf1 " ) ;
if ( ! region )
goto type2 ;
2008-02-29 10:56:50 +03:00
if ( pci_check_type1 ( ) ) {
raw_pci_ops = & pci_direct_conf1 ;
2006-09-26 12:52:40 +04:00
return 1 ;
2008-02-29 10:56:50 +03:00
}
2005-04-17 02:20:36 +04:00
release_resource ( region ) ;
type2 :
if ( ( pci_probe & PCI_PROBE_CONF2 ) = = 0 )
2006-09-26 12:52:40 +04:00
return 0 ;
2005-04-17 02:20:36 +04:00
region = request_region ( 0xCF8 , 4 , " PCI conf2 " ) ;
if ( ! region )
2006-09-26 12:52:40 +04:00
return 0 ;
2005-04-17 02:20:36 +04:00
region2 = request_region ( 0xC000 , 0x1000 , " PCI conf2 " ) ;
if ( ! region2 )
goto fail2 ;
if ( pci_check_type2 ( ) ) {
raw_pci_ops = & pci_direct_conf2 ;
2006-09-26 12:52:40 +04:00
return 2 ;
2005-04-17 02:20:36 +04:00
}
release_resource ( region2 ) ;
fail2 :
release_resource ( region ) ;
2006-09-26 12:52:40 +04:00
return 0 ;
2005-04-17 02:20:36 +04:00
}