2005-04-16 15:20:36 -07:00
/*
* direct . c - Low - level direct PCI config space access
*/
# include <linux/pci.h>
# include <linux/init.h>
2006-04-07 19:49:36 +02:00
# include <linux/dmi.h>
2008-12-27 18:32:28 +05:30
# include <asm/pci_x86.h>
2005-04-16 15:20:36 -07:00
/*
2007-09-03 10:17:39 +02:00
* Functions for accessing PCI base ( first 256 bytes ) and extended
* ( 4096 bytes per PCI function ) configuration space with type 1
* accesses .
2005-04-16 15:20:36 -07:00
*/
# define PCI_CONF1_ADDRESS(bus, devfn, reg) \
2007-09-03 10:17:39 +02:00
( 0x80000000 | ( ( reg & 0xF00 ) < < 16 ) | ( bus < < 16 ) \
| ( devfn < < 8 ) | ( reg & 0xFC ) )
2005-04-16 15:20:36 -07:00
2008-02-10 09:45:28 -05:00
static int pci_conf1_read ( unsigned int seg , unsigned int bus ,
2005-04-16 15:20:36 -07:00
unsigned int devfn , int reg , int len , u32 * value )
{
unsigned long flags ;
x86/PCI: config space accessor functions should not ignore the segment argument
Without this change, the majority of the raw PCI config space access
functions silently ignore a non-zero segment argument, which is
certainly wrong.
Apart from pci_direct_conf1, all other non-MMCFG access methods get
used only for non-extended accesses (i.e. assigned to raw_pci_ops
only). Consequently, with the way raw_pci_{read,write}() work, it would
be a coding error to call these functions with a non-zero segment (with
the current call flow this cannot happen afaict).
The access method 1 accessor, as it can be used for extended accesses
(on AMD systems) instead gets checks added for the passed in segment to
be zero. This would be the case when on such a system having multiple
PCI segments (don't know whether any exist in practice) MMCFG for some
reason is not usable, and method 1 gets selected for doing extended
accesses. Rather than accessing the wrong device's config space, the
function will now error out.
v2: Convert BUG_ON() to WARN_ON(), and extend description as per Ingo's
request.
Signed-off-by: Jan Beulich <jbeulich@novell.com>
Reviewed-by: Ingo Molnar <mingo@elte.hu>
Signed-off-by: Jesse Barnes <jbarnes@virtuousgeek.org>
2011-07-22 08:13:05 +01:00
if ( seg | | ( bus > 255 ) | | ( devfn > 255 ) | | ( reg > 4095 ) ) {
2006-04-07 19:50:15 +02:00
* value = - 1 ;
2005-04-16 15:20:36 -07:00
return - EINVAL ;
2006-04-07 19:50:15 +02:00
}
2005-04-16 15:20:36 -07:00
2010-02-17 14:35:25 +00:00
raw_spin_lock_irqsave ( & pci_config_lock , flags ) ;
2005-04-16 15:20:36 -07:00
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 ;
}
2010-02-17 14:35:25 +00:00
raw_spin_unlock_irqrestore ( & pci_config_lock , flags ) ;
2005-04-16 15:20:36 -07:00
return 0 ;
}
2008-02-10 09:45:28 -05:00
static int pci_conf1_write ( unsigned int seg , unsigned int bus ,
2005-04-16 15:20:36 -07:00
unsigned int devfn , int reg , int len , u32 value )
{
unsigned long flags ;
x86/PCI: config space accessor functions should not ignore the segment argument
Without this change, the majority of the raw PCI config space access
functions silently ignore a non-zero segment argument, which is
certainly wrong.
Apart from pci_direct_conf1, all other non-MMCFG access methods get
used only for non-extended accesses (i.e. assigned to raw_pci_ops
only). Consequently, with the way raw_pci_{read,write}() work, it would
be a coding error to call these functions with a non-zero segment (with
the current call flow this cannot happen afaict).
The access method 1 accessor, as it can be used for extended accesses
(on AMD systems) instead gets checks added for the passed in segment to
be zero. This would be the case when on such a system having multiple
PCI segments (don't know whether any exist in practice) MMCFG for some
reason is not usable, and method 1 gets selected for doing extended
accesses. Rather than accessing the wrong device's config space, the
function will now error out.
v2: Convert BUG_ON() to WARN_ON(), and extend description as per Ingo's
request.
Signed-off-by: Jan Beulich <jbeulich@novell.com>
Reviewed-by: Ingo Molnar <mingo@elte.hu>
Signed-off-by: Jesse Barnes <jbarnes@virtuousgeek.org>
2011-07-22 08:13:05 +01:00
if ( seg | | ( bus > 255 ) | | ( devfn > 255 ) | | ( reg > 4095 ) )
2005-04-16 15:20:36 -07:00
return - EINVAL ;
2010-02-17 14:35:25 +00:00
raw_spin_lock_irqsave ( & pci_config_lock , flags ) ;
2005-04-16 15:20:36 -07:00
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 ;
}
2010-02-17 14:35:25 +00:00
raw_spin_unlock_irqrestore ( & pci_config_lock , flags ) ;
2005-04-16 15:20:36 -07:00
return 0 ;
}
# undef PCI_CONF1_ADDRESS
2011-09-15 08:58:51 +01:00
const struct pci_raw_ops pci_direct_conf1 = {
2005-04-16 15:20:36 -07:00
. 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 ;
x86/PCI: config space accessor functions should not ignore the segment argument
Without this change, the majority of the raw PCI config space access
functions silently ignore a non-zero segment argument, which is
certainly wrong.
Apart from pci_direct_conf1, all other non-MMCFG access methods get
used only for non-extended accesses (i.e. assigned to raw_pci_ops
only). Consequently, with the way raw_pci_{read,write}() work, it would
be a coding error to call these functions with a non-zero segment (with
the current call flow this cannot happen afaict).
The access method 1 accessor, as it can be used for extended accesses
(on AMD systems) instead gets checks added for the passed in segment to
be zero. This would be the case when on such a system having multiple
PCI segments (don't know whether any exist in practice) MMCFG for some
reason is not usable, and method 1 gets selected for doing extended
accesses. Rather than accessing the wrong device's config space, the
function will now error out.
v2: Convert BUG_ON() to WARN_ON(), and extend description as per Ingo's
request.
Signed-off-by: Jan Beulich <jbeulich@novell.com>
Reviewed-by: Ingo Molnar <mingo@elte.hu>
Signed-off-by: Jesse Barnes <jbarnes@virtuousgeek.org>
2011-07-22 08:13:05 +01:00
WARN_ON ( seg ) ;
2006-04-11 12:54:48 +02:00
if ( ( bus > 255 ) | | ( devfn > 255 ) | | ( reg > 255 ) ) {
* value = - 1 ;
2005-04-16 15:20:36 -07:00
return - EINVAL ;
2006-04-11 12:54:48 +02:00
}
2005-04-16 15:20:36 -07:00
dev = PCI_SLOT ( devfn ) ;
fn = PCI_FUNC ( devfn ) ;
if ( dev & 0x10 )
return PCIBIOS_DEVICE_NOT_FOUND ;
2010-02-17 14:35:25 +00:00
raw_spin_lock_irqsave ( & pci_config_lock , flags ) ;
2005-04-16 15:20:36 -07:00
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 ) ;
2010-02-17 14:35:25 +00:00
raw_spin_unlock_irqrestore ( & pci_config_lock , flags ) ;
2005-04-16 15:20:36 -07:00
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 ;
x86/PCI: config space accessor functions should not ignore the segment argument
Without this change, the majority of the raw PCI config space access
functions silently ignore a non-zero segment argument, which is
certainly wrong.
Apart from pci_direct_conf1, all other non-MMCFG access methods get
used only for non-extended accesses (i.e. assigned to raw_pci_ops
only). Consequently, with the way raw_pci_{read,write}() work, it would
be a coding error to call these functions with a non-zero segment (with
the current call flow this cannot happen afaict).
The access method 1 accessor, as it can be used for extended accesses
(on AMD systems) instead gets checks added for the passed in segment to
be zero. This would be the case when on such a system having multiple
PCI segments (don't know whether any exist in practice) MMCFG for some
reason is not usable, and method 1 gets selected for doing extended
accesses. Rather than accessing the wrong device's config space, the
function will now error out.
v2: Convert BUG_ON() to WARN_ON(), and extend description as per Ingo's
request.
Signed-off-by: Jan Beulich <jbeulich@novell.com>
Reviewed-by: Ingo Molnar <mingo@elte.hu>
Signed-off-by: Jesse Barnes <jbarnes@virtuousgeek.org>
2011-07-22 08:13:05 +01:00
WARN_ON ( seg ) ;
2005-04-16 15:20:36 -07:00
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 ;
2010-02-17 14:35:25 +00:00
raw_spin_lock_irqsave ( & pci_config_lock , flags ) ;
2005-04-16 15:20:36 -07:00
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 ) ;
2010-02-17 14:35:25 +00:00
raw_spin_unlock_irqrestore ( & pci_config_lock , flags ) ;
2005-04-16 15:20:36 -07:00
return 0 ;
}
# undef PCI_CONF2_ADDRESS
2011-09-15 08:58:51 +01:00
static const struct pci_raw_ops pci_direct_conf2 = {
2005-04-16 15:20:36 -07:00
. 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 .
*/
2011-09-15 08:58:51 +01:00
static int __init pci_sanity_check ( const struct pci_raw_ops * o )
2005-04-16 15:20:36 -07:00
{
u32 x = 0 ;
2009-08-16 21:02:36 +09:00
int year , devfn ;
2005-04-16 15:20:36 -07:00
if ( pci_probe & PCI_NO_CHECKS )
return 1 ;
2006-04-07 19:49:36 +02:00
/* Assume Type 1 works for newer systems.
This handles machines that don ' t have anything on PCI Bus 0. */
2009-08-16 21:02:36 +09:00
dmi_get_date ( DMI_BIOS_DATE , & year , NULL , NULL ) ;
if ( year > = 2001 )
2006-04-07 19:49:36 +02:00
return 1 ;
2005-04-16 15:20:36 -07: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-23 15:45:09 -08:00
DBG ( KERN_WARNING " PCI: Sanity check failed \n " ) ;
2005-04-16 15:20:36 -07: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 10:52:40 +02:00
void __init pci_direct_init ( int type )
{
2006-10-05 18:47:22 +02:00
if ( type = = 0 )
return ;
2008-02-28 23:56:50 -08:00
printk ( KERN_INFO " PCI: Using configuration type %d for base access \n " ,
type ) ;
2007-09-03 10:17:39 +02:00
if ( type = = 1 ) {
2006-09-26 10:52:40 +02:00
raw_pci_ops = & pci_direct_conf1 ;
2008-06-12 20:19:23 +02: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 10:17:39 +02:00
}
2008-06-12 20:19:23 +02:00
raw_pci_ops = & pci_direct_conf2 ;
2006-09-26 10:52:40 +02:00
}
int __init pci_direct_probe ( void )
2005-04-16 15:20:36 -07:00
{
if ( ( pci_probe & PCI_PROBE_CONF1 ) = = 0 )
goto type2 ;
2011-02-13 13:12:11 +01:00
if ( ! request_region ( 0xCF8 , 8 , " PCI conf1 " ) )
2005-04-16 15:20:36 -07:00
goto type2 ;
2008-02-28 23:56:50 -08:00
if ( pci_check_type1 ( ) ) {
raw_pci_ops = & pci_direct_conf1 ;
2008-11-11 16:19:48 -08:00
port_cf9_safe = true ;
2006-09-26 10:52:40 +02:00
return 1 ;
2008-02-28 23:56:50 -08:00
}
2011-02-13 13:12:11 +01:00
release_region ( 0xCF8 , 8 ) ;
2005-04-16 15:20:36 -07:00
type2 :
if ( ( pci_probe & PCI_PROBE_CONF2 ) = = 0 )
2006-09-26 10:52:40 +02:00
return 0 ;
2011-02-13 13:12:11 +01:00
if ( ! request_region ( 0xCF8 , 4 , " PCI conf2 " ) )
2006-09-26 10:52:40 +02:00
return 0 ;
2011-02-13 13:12:11 +01:00
if ( ! request_region ( 0xC000 , 0x1000 , " PCI conf2 " ) )
2005-04-16 15:20:36 -07:00
goto fail2 ;
if ( pci_check_type2 ( ) ) {
raw_pci_ops = & pci_direct_conf2 ;
2008-11-11 16:19:48 -08:00
port_cf9_safe = true ;
2006-09-26 10:52:40 +02:00
return 2 ;
2005-04-16 15:20:36 -07:00
}
2011-02-13 13:12:11 +01:00
release_region ( 0xC000 , 0x1000 ) ;
2005-04-16 15:20:36 -07:00
fail2 :
2011-02-13 13:12:11 +01:00
release_region ( 0xCF8 , 4 ) ;
2006-09-26 10:52:40 +02:00
return 0 ;
2005-04-16 15:20:36 -07:00
}