2005-04-17 02:20:36 +04:00
/*
* Low - Level PCI Access for i386 machines
*
* Copyright 1993 , 1994 Drew Eckhardt
* Visionary Computing
* ( Unix and Linux consulting and custom programming )
* Drew @ Colorado . EDU
* + 1 ( 303 ) 786 - 7975
*
* Drew ' s work was sponsored by :
* iX Multiuser Multitasking Magazine
* Hannover , Germany
* hm @ ix . de
*
* Copyright 1997 - - 2000 Martin Mares < mj @ ucw . cz >
*
* For more information , please consult the following manuals ( look at
* http : //www.pcisig.com/ for how to get them):
*
* PCI BIOS Specification
* PCI Local Bus Specification
* PCI to PCI Bridge Specification
* PCI System Design Guide
*
*/
# include <linux/types.h>
# include <linux/kernel.h>
2011-05-26 20:22:53 +04:00
# include <linux/export.h>
2005-04-17 02:20:36 +04:00
# include <linux/pci.h>
# include <linux/init.h>
# include <linux/ioport.h>
# include <linux/errno.h>
2008-03-19 03:00:19 +03:00
# include <linux/bootmem.h>
# include <asm/pat.h>
2008-08-29 00:52:25 +04:00
# include <asm/e820.h>
2008-12-27 16:02:28 +03:00
# include <asm/pci_x86.h>
2009-07-10 20:36:20 +04:00
# include <asm/io_apic.h>
2005-04-17 02:20:36 +04:00
2011-11-21 22:54:13 +04:00
/*
* This list of dynamic mappings is for temporarily maintaining
* original BIOS BAR addresses for possible reinstatement .
*/
struct pcibios_fwaddrmap {
struct list_head list ;
struct pci_dev * dev ;
resource_size_t fw_addr [ DEVICE_COUNT_RESOURCE ] ;
} ;
static LIST_HEAD ( pcibios_fwaddrmappings ) ;
static DEFINE_SPINLOCK ( pcibios_fwaddrmap_lock ) ;
2012-11-04 08:39:28 +04:00
static bool pcibios_fw_addr_done ;
2011-11-21 22:54:13 +04:00
/* Must be called with 'pcibios_fwaddrmap_lock' lock held. */
static struct pcibios_fwaddrmap * pcibios_fwaddrmap_lookup ( struct pci_dev * dev )
{
struct pcibios_fwaddrmap * map ;
2012-05-16 03:01:09 +04:00
WARN_ON_SMP ( ! spin_is_locked ( & pcibios_fwaddrmap_lock ) ) ;
2012-03-02 23:45:01 +04:00
2011-11-21 22:54:13 +04:00
list_for_each_entry ( map , & pcibios_fwaddrmappings , list )
if ( map - > dev = = dev )
return map ;
return NULL ;
}
static void
pcibios_save_fw_addr ( struct pci_dev * dev , int idx , resource_size_t fw_addr )
{
unsigned long flags ;
struct pcibios_fwaddrmap * map ;
2012-11-04 08:39:28 +04:00
if ( pcibios_fw_addr_done )
return ;
2011-11-21 22:54:13 +04:00
spin_lock_irqsave ( & pcibios_fwaddrmap_lock , flags ) ;
map = pcibios_fwaddrmap_lookup ( dev ) ;
if ( ! map ) {
spin_unlock_irqrestore ( & pcibios_fwaddrmap_lock , flags ) ;
map = kzalloc ( sizeof ( * map ) , GFP_KERNEL ) ;
if ( ! map )
return ;
map - > dev = pci_dev_get ( dev ) ;
map - > fw_addr [ idx ] = fw_addr ;
INIT_LIST_HEAD ( & map - > list ) ;
spin_lock_irqsave ( & pcibios_fwaddrmap_lock , flags ) ;
list_add_tail ( & map - > list , & pcibios_fwaddrmappings ) ;
} else
map - > fw_addr [ idx ] = fw_addr ;
spin_unlock_irqrestore ( & pcibios_fwaddrmap_lock , flags ) ;
}
resource_size_t pcibios_retrieve_fw_addr ( struct pci_dev * dev , int idx )
{
unsigned long flags ;
struct pcibios_fwaddrmap * map ;
resource_size_t fw_addr = 0 ;
2012-11-04 08:39:28 +04:00
if ( pcibios_fw_addr_done )
return 0 ;
2011-11-21 22:54:13 +04:00
spin_lock_irqsave ( & pcibios_fwaddrmap_lock , flags ) ;
map = pcibios_fwaddrmap_lookup ( dev ) ;
if ( map )
fw_addr = map - > fw_addr [ idx ] ;
spin_unlock_irqrestore ( & pcibios_fwaddrmap_lock , flags ) ;
return fw_addr ;
}
2012-11-04 08:39:28 +04:00
static void __init pcibios_fw_addr_list_del ( void )
2011-11-21 22:54:13 +04:00
{
unsigned long flags ;
struct pcibios_fwaddrmap * entry , * next ;
spin_lock_irqsave ( & pcibios_fwaddrmap_lock , flags ) ;
list_for_each_entry_safe ( entry , next , & pcibios_fwaddrmappings , list ) {
list_del ( & entry - > list ) ;
pci_dev_put ( entry - > dev ) ;
kfree ( entry ) ;
}
spin_unlock_irqrestore ( & pcibios_fwaddrmap_lock , flags ) ;
2012-11-04 08:39:28 +04:00
pcibios_fw_addr_done = true ;
2011-11-21 22:54:13 +04:00
}
2007-10-04 02:56:14 +04:00
static int
skip_isa_ioresource_align ( struct pci_dev * dev ) {
if ( ( pci_probe & PCI_CAN_SKIP_ISA_ALIGN ) & &
2007-10-09 03:24:16 +04:00
! ( dev - > bus - > bridge_ctl & PCI_BRIDGE_CTL_ISA ) )
2007-10-04 02:56:14 +04:00
return 1 ;
return 0 ;
}
2005-04-17 02:20:36 +04:00
/*
* We need to avoid collisions with ` mirrored ' VGA ports
* and other strange ISA hardware , so we always want the
* addresses to be allocated in the 0x000 - 0x0ff region
* modulo 0x400 .
*
* Why ? Because some silly external IO cards only decode
* the low 10 bits of the IO address . The 0x00 - 0xff region
* is reserved for motherboard devices that decode all 16
* bits , so it ' s ok to allocate at , say , 0x2800 - 0x28ff ,
* but we want to try to avoid allocating at 0x2900 - 0x2bff
* which might have be mirrored at 0x0100 - 0x03ff . .
*/
2010-01-01 19:40:49 +03:00
resource_size_t
2010-01-01 19:40:50 +03:00
pcibios_align_resource ( void * data , const struct resource * res ,
2006-06-13 04:06:02 +04:00
resource_size_t size , resource_size_t align )
2005-04-17 02:20:36 +04:00
{
2007-10-04 02:56:14 +04:00
struct pci_dev * dev = data ;
2010-12-16 20:38:31 +03:00
resource_size_t start = res - > start ;
2007-10-04 02:56:14 +04:00
2005-04-17 02:20:36 +04:00
if ( res - > flags & IORESOURCE_IO ) {
2010-12-16 20:38:31 +03:00
if ( skip_isa_ioresource_align ( dev ) )
return start ;
if ( start & 0x300 )
start = ( start + 0x3ff ) & ~ 0x3ff ;
2005-04-17 02:20:36 +04:00
}
2010-01-01 19:40:49 +03:00
return start ;
2005-04-17 02:20:36 +04:00
}
2007-10-29 11:06:10 +03:00
EXPORT_SYMBOL ( pcibios_align_resource ) ;
2005-04-17 02:20:36 +04:00
/*
* Handle resources of PCI devices . If the world were perfect , we could
* just allocate all the resource regions and do nothing more . It isn ' t .
* On the other hand , we cannot just re - allocate all devices , as it would
* require us to know lots of host bridge internals . So we attempt to
* keep as much of the original configuration as possible , but tweak it
* when it ' s found to be wrong .
*
* Known BIOS problems we have to work around :
* - I / O or memory regions not configured
* - regions configured , but not enabled in the command register
* - bogus I / O addresses above 64 K used
* - expansion ROMs left enabled ( this may sound harmless , but given
* the fact the PCI specs explicitly allow address decoders to be
* shared between expansion ROMs and other resource regions , it ' s
* at least dangerous )
2010-06-04 00:43:03 +04:00
* - bad resource sizes or overlaps with other regions
2005-04-17 02:20:36 +04:00
*
* Our solution :
* ( 1 ) Allocate resources for all buses behind PCI - to - PCI bridges .
* This gives us fixed barriers on where we can allocate .
* ( 2 ) Allocate resources for all enabled devices . If there is
* a collision , just mark the resource as unallocated . Also
* disable expansion ROMs during this step .
* ( 3 ) Try to allocate resources for disabled devices . If the
* resources were assigned correctly , everything goes well ,
* if they weren ' t , they won ' t disturb allocation of other
* resources .
* ( 4 ) Assign new addresses to resources which were either
* not configured at all or misconfigured . If explicitly
* requested by the user , configure expansion ROM address
* as well .
*/
2012-11-04 08:39:29 +04:00
static void pcibios_allocate_bridge_resources ( struct pci_dev * dev )
2005-04-17 02:20:36 +04:00
{
int idx ;
2009-06-18 00:33:35 +04:00
struct resource * r ;
2005-04-17 02:20:36 +04:00
2012-11-04 08:39:24 +04:00
for ( idx = PCI_BRIDGE_RESOURCES ; idx < PCI_NUM_RESOURCES ; idx + + ) {
r = & dev - > resource [ idx ] ;
if ( ! r - > flags )
continue ;
if ( ! r - > start | | pci_claim_resource ( dev , idx ) < 0 ) {
/*
* Something is wrong with the region .
* Invalidate the resource to prevent
* child resource allocations in this
* range .
*/
r - > start = r - > end = 0 ;
r - > flags = 0 ;
}
}
}
2012-11-04 08:39:29 +04:00
static void pcibios_allocate_bus_resources ( struct pci_bus * bus )
2012-11-04 08:39:24 +04:00
{
2012-11-04 08:39:26 +04:00
struct pci_bus * child ;
2012-11-04 08:39:24 +04:00
2005-04-17 02:20:36 +04:00
/* Depth-First Search on bus tree */
2012-11-04 08:39:26 +04:00
if ( bus - > self )
pcibios_allocate_bridge_resources ( bus - > self ) ;
list_for_each_entry ( child , & bus - > children , node )
pcibios_allocate_bus_resources ( child ) ;
2005-04-17 02:20:36 +04:00
}
2009-11-25 05:05:12 +03:00
struct pci_check_idx_range {
int start ;
int end ;
} ;
2012-11-04 08:39:29 +04:00
static void pcibios_allocate_dev_resources ( struct pci_dev * dev , int pass )
2005-04-17 02:20:36 +04:00
{
2009-11-25 05:05:12 +03:00
int idx , disabled , i ;
2005-04-17 02:20:36 +04:00
u16 command ;
2009-06-18 00:33:35 +04:00
struct resource * r ;
2005-04-17 02:20:36 +04:00
2009-11-25 05:05:12 +03:00
struct pci_check_idx_range idx_range [ ] = {
{ PCI_STD_RESOURCES , PCI_STD_RESOURCE_END } ,
# ifdef CONFIG_PCI_IOV
{ PCI_IOV_RESOURCES , PCI_IOV_RESOURCE_END } ,
# endif
} ;
2012-11-04 08:39:25 +04:00
pci_read_config_word ( dev , PCI_COMMAND , & command ) ;
for ( i = 0 ; i < ARRAY_SIZE ( idx_range ) ; i + + )
2009-11-25 05:05:12 +03:00
for ( idx = idx_range [ i ] . start ; idx < = idx_range [ i ] . end ; idx + + ) {
2005-04-17 02:20:36 +04:00
r = & dev - > resource [ idx ] ;
2012-11-04 08:39:25 +04:00
if ( r - > parent ) /* Already allocated */
2005-04-17 02:20:36 +04:00
continue ;
2012-11-04 08:39:25 +04:00
if ( ! r - > start ) /* Address not assigned at all */
2005-04-17 02:20:36 +04:00
continue ;
if ( r - > flags & IORESOURCE_IO )
disabled = ! ( command & PCI_COMMAND_IO ) ;
else
disabled = ! ( command & PCI_COMMAND_MEMORY ) ;
if ( pass = = disabled ) {
2009-10-27 22:26:47 +03:00
dev_dbg ( & dev - > dev ,
2009-11-04 20:32:57 +03:00
" BAR %d: reserving %pr (d=%d, p=%d) \n " ,
2009-10-27 22:26:47 +03:00
idx , r , disabled , pass ) ;
2009-06-18 00:33:35 +04:00
if ( pci_claim_resource ( dev , idx ) < 0 ) {
2005-04-17 02:20:36 +04:00
/* We'll assign a new address later */
2011-11-21 22:54:19 +04:00
pcibios_save_fw_addr ( dev ,
idx , r - > start ) ;
2005-04-17 02:20:36 +04:00
r - > end - = r - > start ;
r - > start = 0 ;
}
}
}
2012-11-04 08:39:25 +04:00
if ( ! pass ) {
r = & dev - > resource [ PCI_ROM_RESOURCE ] ;
if ( r - > flags & IORESOURCE_ROM_ENABLE ) {
/* Turn the ROM off, leave the resource region,
* but keep it unregistered . */
u32 reg ;
dev_dbg ( & dev - > dev , " disabling ROM %pR \n " , r ) ;
r - > flags & = ~ IORESOURCE_ROM_ENABLE ;
pci_read_config_dword ( dev , dev - > rom_base_reg , & reg ) ;
pci_write_config_dword ( dev , dev - > rom_base_reg ,
2006-10-17 21:17:58 +04:00
reg & ~ PCI_ROM_ADDRESS_ENABLE ) ;
2005-04-17 02:20:36 +04:00
}
}
}
2012-11-04 08:39:29 +04:00
static void pcibios_allocate_resources ( struct pci_bus * bus , int pass )
2012-11-04 08:39:25 +04:00
{
2012-11-04 08:39:26 +04:00
struct pci_dev * dev ;
struct pci_bus * child ;
2012-11-04 08:39:25 +04:00
2012-11-04 08:39:26 +04:00
list_for_each_entry ( dev , & bus - > devices , bus_list ) {
2012-11-04 08:39:25 +04:00
pcibios_allocate_dev_resources ( dev , pass ) ;
2012-11-04 08:39:26 +04:00
child = dev - > subordinate ;
if ( child )
pcibios_allocate_resources ( child , pass ) ;
}
2012-11-04 08:39:25 +04:00
}
2012-11-04 08:39:29 +04:00
static void pcibios_allocate_dev_rom_resource ( struct pci_dev * dev )
2005-04-17 02:20:36 +04:00
{
2009-06-18 00:33:35 +04:00
struct resource * r ;
2005-04-17 02:20:36 +04:00
2012-11-04 08:39:27 +04:00
/*
* Try to use BIOS settings for ROMs , otherwise let
* pci_assign_unassigned_resources ( ) allocate the new
* addresses .
*/
r = & dev - > resource [ PCI_ROM_RESOURCE ] ;
if ( ! r - > flags | | ! r - > start )
return ;
if ( pci_claim_resource ( dev , PCI_ROM_RESOURCE ) < 0 ) {
r - > end - = r - > start ;
r - > start = 0 ;
2005-04-17 02:20:36 +04:00
}
2012-11-04 08:39:27 +04:00
}
2012-11-04 08:39:29 +04:00
static void pcibios_allocate_rom_resources ( struct pci_bus * bus )
2012-11-04 08:39:27 +04:00
{
struct pci_dev * dev ;
struct pci_bus * child ;
list_for_each_entry ( dev , & bus - > devices , bus_list ) {
pcibios_allocate_dev_rom_resource ( dev ) ;
child = dev - > subordinate ;
if ( child )
pcibios_allocate_rom_resources ( child ) ;
}
}
static int __init pcibios_assign_resources ( void )
{
struct pci_bus * bus ;
if ( ! ( pci_probe & PCI_ASSIGN_ROMS ) )
list_for_each_entry ( bus , & pci_root_buses , node )
pcibios_allocate_rom_resources ( bus ) ;
2005-08-30 18:48:52 +04:00
pci_assign_unassigned_resources ( ) ;
2011-11-21 22:54:19 +04:00
pcibios_fw_addr_list_del ( ) ;
2005-08-30 18:48:52 +04:00
2005-04-17 02:20:36 +04:00
return 0 ;
}
2012-11-04 08:39:30 +04:00
void pcibios_resource_survey_bus ( struct pci_bus * bus )
{
dev_printk ( KERN_DEBUG , & bus - > dev , " Allocating resources \n " ) ;
pcibios_allocate_bus_resources ( bus ) ;
pcibios_allocate_resources ( bus , 0 ) ;
pcibios_allocate_resources ( bus , 1 ) ;
if ( ! ( pci_probe & PCI_ASSIGN_ROMS ) )
pcibios_allocate_rom_resources ( bus ) ;
}
2005-04-17 02:20:36 +04:00
void __init pcibios_resource_survey ( void )
{
2012-11-04 08:39:26 +04:00
struct pci_bus * bus ;
2005-04-17 02:20:36 +04:00
DBG ( " PCI: Allocating resources \n " ) ;
2012-11-04 08:39:26 +04:00
list_for_each_entry ( bus , & pci_root_buses , node )
pcibios_allocate_bus_resources ( bus ) ;
list_for_each_entry ( bus , & pci_root_buses , node )
pcibios_allocate_resources ( bus , 0 ) ;
list_for_each_entry ( bus , & pci_root_buses , node )
pcibios_allocate_resources ( bus , 1 ) ;
2008-08-29 10:09:23 +04:00
e820_reserve_resources_late ( ) ;
2009-07-10 20:36:20 +04:00
/*
* Insert the IO APIC resources after PCI initialization has
2011-03-17 22:24:16 +03:00
* occurred to handle IO APICS that are mapped in on a BAR in
2009-07-10 20:36:20 +04:00
* PCI space , but before trying to assign unassigned pci res .
*/
ioapic_insert_resources ( ) ;
2005-04-17 02:20:36 +04:00
}
/**
* called in fs_initcall ( one below subsys_initcall ) ,
* give a chance for motherboard reserve resources
*/
fs_initcall ( pcibios_assign_resources ) ;
2009-09-27 22:29:37 +04:00
static const struct vm_operations_struct pci_mmap_ops = {
2008-07-24 08:27:07 +04:00
. access = generic_access_phys ,
2008-03-19 03:00:19 +03:00
} ;
2005-04-17 02:20:36 +04:00
int pci_mmap_page_range ( struct pci_dev * dev , struct vm_area_struct * vma ,
enum pci_mmap_state mmap_state , int write_combine )
{
unsigned long prot ;
/* I/O space cannot be accessed via normal processor loads and
* stores on this platform .
*/
if ( mmap_state = = pci_mmap_io )
return - EINVAL ;
prot = pgprot_val ( vma - > vm_page_prot ) ;
2009-10-27 00:21:32 +03:00
/*
* Return error if pat is not enabled and write_combine is requested .
* Caller can followup with UC MINUS request and add a WC mtrr if there
* is a free mtrr slot .
*/
if ( ! pat_enabled & & write_combine )
return - EINVAL ;
2008-06-10 18:06:21 +04:00
if ( pat_enabled & & write_combine )
2008-03-19 03:00:19 +03:00
prot | = _PAGE_CACHE_WC ;
2008-06-10 18:06:21 +04:00
else if ( pat_enabled | | boot_cpu_data . x86 > 3 )
2008-04-26 04:07:22 +04:00
/*
* ioremap ( ) and ioremap_nocache ( ) defaults to UC MINUS for now .
* To avoid attribute conflicts , request UC MINUS here
2011-03-17 22:24:16 +03:00
* as well .
2008-04-26 04:07:22 +04:00
*/
prot | = _PAGE_CACHE_UC_MINUS ;
2008-03-19 03:00:19 +03:00
2010-03-18 21:31:30 +03:00
prot | = _PAGE_IOMAP ; /* creating a mapping for IO */
2005-04-17 02:20:36 +04:00
vma - > vm_page_prot = __pgprot ( prot ) ;
2005-07-31 12:51:45 +04:00
if ( io_remap_pfn_range ( vma , vma - > vm_start , vma - > vm_pgoff ,
vma - > vm_end - vma - > vm_start ,
vma - > vm_page_prot ) )
2005-04-17 02:20:36 +04:00
return - EAGAIN ;
2008-03-19 03:00:19 +03:00
vma - > vm_ops = & pci_mmap_ops ;
2005-04-17 02:20:36 +04:00
return 0 ;
}