2005-04-17 02:20:36 +04:00
/*
* mmconfig . c - Low - level direct PCI config space access via MMCONFIG
2007-02-02 19:48:22 +03:00
*
2005-04-17 02:20:36 +04: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-24 04:35:56 +04:00
# include <linux/acpi.h>
2005-12-13 09:17:11 +03:00
# include <linux/bitmap.h>
2006-04-07 21:49:30 +04:00
# include <asm/e820.h>
2008-12-27 16:02:28 +03:00
# include <asm/pci_x86.h>
2005-04-17 02:20:36 +04:00
2009-11-14 03:34:59 +03:00
# define PREFIX "PCI: "
2005-12-15 12:17:44 +03:00
static char __iomem * pci_dev_base ( unsigned int seg , unsigned int bus , unsigned int devfn )
2005-06-24 04:35:56 +04:00
{
2009-11-14 03:35:04 +03:00
struct pci_mmcfg_region * cfg = pci_mmconfig_lookup ( seg , bus ) ;
2008-01-15 01:31:09 +03:00
2009-11-14 03:35:04 +03:00
if ( cfg & & cfg - > virt )
return cfg - > virt + ( PCI_MMCFG_BUS_OFFSET ( bus ) | ( devfn < < 12 ) ) ;
return NULL ;
2005-04-17 02:20:36 +04:00
}
static int pci_mmcfg_read ( unsigned int seg , unsigned int bus ,
unsigned int devfn , int reg , int len , u32 * value )
{
2005-12-15 12:17:44 +03:00
char __iomem * addr ;
2005-04-17 02:20:36 +04:00
2005-12-13 09:17:10 +03:00
/* Why do we have this when nobody checks it. How about a BUG()!? -AK */
2006-04-11 14:54:48 +04:00
if ( unlikely ( ( bus > 255 ) | | ( devfn > 255 ) | | ( reg > 4095 ) ) ) {
2008-01-15 01:31:09 +03:00
err : * 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
2005-12-13 09:17:10 +03:00
addr = pci_dev_base ( seg , bus , devfn ) ;
if ( ! addr )
2008-01-15 01:31:09 +03:00
goto err ;
2005-12-13 09:17:10 +03:00
2005-04-17 02:20:36 +04:00
switch ( len ) {
case 1 :
2007-08-11 00:30:59 +04:00
* value = mmio_config_readb ( addr + reg ) ;
2005-04-17 02:20:36 +04:00
break ;
case 2 :
2007-08-11 00:30:59 +04:00
* value = mmio_config_readw ( addr + reg ) ;
2005-04-17 02:20:36 +04:00
break ;
case 4 :
2007-08-11 00:30:59 +04:00
* value = mmio_config_readl ( addr + reg ) ;
2005-04-17 02:20:36 +04: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 12:17:44 +03:00
char __iomem * addr ;
2005-04-17 02:20:36 +04:00
2005-12-13 09:17:10 +03:00
/* Why do we have this when nobody checks it. How about a BUG()!? -AK */
2005-04-17 02:20:36 +04:00
if ( unlikely ( ( bus > 255 ) | | ( devfn > 255 ) | | ( reg > 4095 ) ) )
return - EINVAL ;
2005-12-13 09:17:10 +03:00
addr = pci_dev_base ( seg , bus , devfn ) ;
if ( ! addr )
2008-01-15 01:31:09 +03:00
return - EINVAL ;
2005-12-13 09:17:10 +03:00
2005-04-17 02:20:36 +04:00
switch ( len ) {
case 1 :
2007-08-11 00:30:59 +04:00
mmio_config_writeb ( addr + reg , value ) ;
2005-04-17 02:20:36 +04:00
break ;
case 2 :
2007-08-11 00:30:59 +04:00
mmio_config_writew ( addr + reg , value ) ;
2005-04-17 02:20:36 +04:00
break ;
case 4 :
2007-08-11 00:30:59 +04:00
mmio_config_writel ( addr + reg , value ) ;
2005-04-17 02:20:36 +04:00
break ;
}
return 0 ;
}
2011-09-15 11:58:51 +04:00
static const struct pci_raw_ops pci_mmcfg = {
2005-04-17 02:20:36 +04:00
. read = pci_mmcfg_read ,
. write = pci_mmcfg_write ,
} ;
2009-11-14 03:34:13 +03:00
static void __iomem * __init mcfg_ioremap ( struct pci_mmcfg_region * cfg )
2007-02-13 15:26:20 +03:00
{
void __iomem * addr ;
2009-03-20 06:55:35 +03:00
u64 start , size ;
2009-11-14 03:34:08 +03:00
int num_buses ;
2009-03-20 06:55:35 +03:00
2009-11-14 03:34:18 +03:00
start = cfg - > address + PCI_MMCFG_BUS_OFFSET ( cfg - > start_bus ) ;
num_buses = cfg - > end_bus - cfg - > start_bus + 1 ;
2009-11-14 03:34:08 +03:00
size = PCI_MMCFG_BUS_OFFSET ( num_buses ) ;
2009-03-20 06:55:35 +03:00
addr = ioremap_nocache ( start , size ) ;
2009-11-14 03:34:59 +03:00
if ( addr )
2009-11-14 03:34:18 +03:00
addr - = PCI_MMCFG_BUS_OFFSET ( cfg - > start_bus ) ;
2007-02-13 15:26:20 +03:00
return addr ;
}
2007-02-13 15:26:20 +03:00
int __init pci_mmcfg_arch_init ( void )
2005-04-17 02:20:36 +04:00
{
2009-11-14 03:34:39 +03:00
struct pci_mmcfg_region * cfg ;
2007-02-13 15:26:20 +03:00
2009-11-14 03:34:49 +03:00
list_for_each_entry ( cfg , & pci_mmcfg_list , list ) {
2009-11-14 03:34:39 +03:00
cfg - > virt = mcfg_ioremap ( cfg ) ;
if ( ! cfg - > virt ) {
2009-11-14 03:34:59 +03:00
printk ( KERN_ERR PREFIX " can't map MMCONFIG at %pR \n " ,
& cfg - > res ) ;
2008-02-15 12:28:41 +03:00
pci_mmcfg_arch_free ( ) ;
2007-02-13 15:26:20 +03:00
return 0 ;
2005-06-24 04:35:56 +04:00
}
}
2008-02-10 17:45:28 +03:00
raw_pci_ext_ops = & pci_mmcfg ;
2007-02-13 15:26:20 +03:00
return 1 ;
2005-04-17 02:20:36 +04:00
}
2008-02-15 12:28:41 +03:00
void __init pci_mmcfg_arch_free ( void )
{
2009-11-14 03:34:39 +03:00
struct pci_mmcfg_region * cfg ;
2008-02-15 12:28:41 +03:00
2009-11-14 03:34:49 +03:00
list_for_each_entry ( cfg , & pci_mmcfg_list , list ) {
2009-11-14 03:34:39 +03:00
if ( cfg - > virt ) {
iounmap ( cfg - > virt + PCI_MMCFG_BUS_OFFSET ( cfg - > start_bus ) ) ;
cfg - > virt = NULL ;
2008-02-15 12:28:41 +03:00
}
}
}