2005-04-16 15:20:36 -07:00
/*
* mmconfig . c - Low - level direct PCI config space access via MMCONFIG
2007-02-02 19:48:22 +03:00
*
2005-04-16 15:20:36 -07:00
* This is an 64 bit optimized version that always keeps the full mmconfig
* space mapped . This allows lockless config space operation .
*/
# include <linux/pci.h>
# include <linux/init.h>
2005-06-23 17:35:56 -07:00
# include <linux/acpi.h>
2005-12-12 22:17:11 -08:00
# include <linux/bitmap.h>
2006-04-07 19:49:30 +02:00
# include <asm/e820.h>
2005-04-16 15:20:36 -07:00
# include "pci.h"
/* Static virtual mapping of the MMCONFIG aperture */
2005-06-23 17:35:56 -07:00
struct mmcfg_virt {
2007-02-02 19:48:22 +03:00
struct acpi_mcfg_allocation * cfg ;
2005-12-15 09:17:44 +00:00
char __iomem * virt ;
2005-06-23 17:35:56 -07:00
} ;
static struct mmcfg_virt * pci_mmcfg_virt ;
2005-04-16 15:20:36 -07:00
2005-12-15 09:17:44 +00:00
static char __iomem * get_virt ( unsigned int seg , unsigned bus )
2005-04-16 15:20:36 -07:00
{
2007-02-02 19:48:22 +03:00
struct acpi_mcfg_allocation * cfg ;
2007-02-13 13:26:20 +01:00
int cfg_num ;
2005-06-23 17:35:56 -07:00
2007-02-13 13:26:20 +01:00
for ( cfg_num = 0 ; cfg_num < pci_mmcfg_config_num ; cfg_num + + ) {
2005-06-23 17:35:56 -07:00
cfg = pci_mmcfg_virt [ cfg_num ] . cfg ;
2007-02-13 13:26:20 +01:00
if ( cfg - > pci_segment = = seg & &
( cfg - > start_bus_number < = bus ) & &
2005-06-23 17:35:56 -07:00
( cfg - > end_bus_number > = bus ) )
return pci_mmcfg_virt [ cfg_num ] . virt ;
}
2006-01-27 02:03:50 +01:00
/* Fall back to type 0 */
2006-02-03 20:28:01 -05:00
return NULL ;
2005-06-23 17:35:56 -07:00
}
2005-12-15 09:17:44 +00:00
static char __iomem * pci_dev_base ( unsigned int seg , unsigned int bus , unsigned int devfn )
2005-06-23 17:35:56 -07:00
{
2005-12-15 09:17:44 +00:00
char __iomem * addr ;
2007-02-13 13:26:20 +01:00
if ( seg = = 0 & & bus < PCI_MMCFG_MAX_CHECK_BUS & &
test_bit ( 32 * bus + PCI_SLOT ( devfn ) , pci_mmcfg_fallback_slots ) )
2005-12-12 22:17:11 -08:00
return NULL ;
addr = get_virt ( seg , bus ) ;
2005-12-12 22:17:10 -08:00
if ( ! addr )
return NULL ;
return addr + ( ( bus < < 20 ) | ( devfn < < 12 ) ) ;
2005-04-16 15:20:36 -07:00
}
static int pci_mmcfg_read ( unsigned int seg , unsigned int bus ,
unsigned int devfn , int reg , int len , u32 * value )
{
2005-12-15 09:17:44 +00:00
char __iomem * addr ;
2005-04-16 15:20:36 -07:00
2005-12-12 22:17:10 -08:00
/* Why do we have this when nobody checks it. How about a BUG()!? -AK */
2006-04-11 12:54:48 +02:00
if ( unlikely ( ( 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
2005-12-12 22:17:10 -08:00
addr = pci_dev_base ( seg , bus , devfn ) ;
if ( ! addr )
return pci_conf1_read ( seg , bus , devfn , reg , len , value ) ;
2005-04-16 15:20:36 -07:00
switch ( len ) {
case 1 :
2007-08-10 22:30:59 +02:00
* value = mmio_config_readb ( addr + reg ) ;
2005-04-16 15:20:36 -07:00
break ;
case 2 :
2007-08-10 22:30:59 +02:00
* value = mmio_config_readw ( addr + reg ) ;
2005-04-16 15:20:36 -07:00
break ;
case 4 :
2007-08-10 22:30:59 +02:00
* value = mmio_config_readl ( addr + reg ) ;
2005-04-16 15:20:36 -07:00
break ;
}
return 0 ;
}
static int pci_mmcfg_write ( unsigned int seg , unsigned int bus ,
unsigned int devfn , int reg , int len , u32 value )
{
2005-12-15 09:17:44 +00:00
char __iomem * addr ;
2005-04-16 15:20:36 -07:00
2005-12-12 22:17:10 -08:00
/* Why do we have this when nobody checks it. How about a BUG()!? -AK */
2005-04-16 15:20:36 -07:00
if ( unlikely ( ( bus > 255 ) | | ( devfn > 255 ) | | ( reg > 4095 ) ) )
return - EINVAL ;
2005-12-12 22:17:10 -08:00
addr = pci_dev_base ( seg , bus , devfn ) ;
if ( ! addr )
return pci_conf1_write ( seg , bus , devfn , reg , len , value ) ;
2005-04-16 15:20:36 -07:00
switch ( len ) {
case 1 :
2007-08-10 22:30:59 +02:00
mmio_config_writeb ( addr + reg , value ) ;
2005-04-16 15:20:36 -07:00
break ;
case 2 :
2007-08-10 22:30:59 +02:00
mmio_config_writew ( addr + reg , value ) ;
2005-04-16 15:20:36 -07:00
break ;
case 4 :
2007-08-10 22:30:59 +02:00
mmio_config_writel ( addr + reg , value ) ;
2005-04-16 15:20:36 -07:00
break ;
}
return 0 ;
}
static struct pci_raw_ops pci_mmcfg = {
. read = pci_mmcfg_read ,
. write = pci_mmcfg_write ,
} ;
2007-02-13 13:26:20 +01:00
static void __iomem * __init mcfg_ioremap ( struct acpi_mcfg_allocation * cfg )
{
void __iomem * addr ;
u32 size ;
size = ( cfg - > end_bus_number + 1 ) < < 20 ;
addr = ioremap_nocache ( cfg - > address , size ) ;
if ( addr ) {
printk ( KERN_INFO " PCI: Using MMCONFIG at %Lx - %Lx \n " ,
cfg - > address , cfg - > address + size - 1 ) ;
}
return addr ;
}
2007-02-13 13:26:20 +01:00
int __init pci_mmcfg_arch_reachable ( unsigned int seg , unsigned int bus ,
unsigned int devfn )
{
return pci_dev_base ( seg , bus , devfn ) ! = NULL ;
}
2007-02-13 13:26:20 +01:00
int __init pci_mmcfg_arch_init ( void )
2005-04-16 15:20:36 -07:00
{
2005-06-23 17:35:56 -07:00
int i ;
2007-02-13 13:26:20 +01:00
pci_mmcfg_virt = kmalloc ( sizeof ( * pci_mmcfg_virt ) *
pci_mmcfg_config_num , GFP_KERNEL ) ;
2005-06-23 17:35:56 -07:00
if ( pci_mmcfg_virt = = NULL ) {
2006-10-20 14:45:33 -07:00
printk ( KERN_ERR " PCI: Can not allocate memory for mmconfig structures \n " ) ;
2007-02-13 13:26:20 +01:00
return 0 ;
2005-06-23 17:35:56 -07:00
}
2007-02-13 13:26:20 +01:00
2005-06-23 17:35:56 -07:00
for ( i = 0 ; i < pci_mmcfg_config_num ; + + i ) {
pci_mmcfg_virt [ i ] . cfg = & pci_mmcfg_config [ i ] ;
2007-02-13 13:26:20 +01:00
pci_mmcfg_virt [ i ] . virt = mcfg_ioremap ( & pci_mmcfg_config [ i ] ) ;
2005-06-23 17:35:56 -07:00
if ( ! pci_mmcfg_virt [ i ] . virt ) {
2006-10-20 14:45:33 -07:00
printk ( KERN_ERR " PCI: Cannot map mmconfig aperture for "
" segment %d \n " ,
2007-02-02 19:48:22 +03:00
pci_mmcfg_config [ i ] . pci_segment ) ;
2007-02-13 13:26:20 +01:00
return 0 ;
2005-06-23 17:35:56 -07:00
}
}
2005-04-16 15:20:36 -07:00
raw_pci_ops = & pci_mmcfg ;
2007-02-13 13:26:20 +01:00
return 1 ;
2005-04-16 15:20:36 -07:00
}