2005-04-17 02:20:36 +04:00
/*
* drivers / pci / setup - res . c
*
* Extruded from code written by
* Dave Rusling ( david . rusling @ reo . mts . dec . com )
* David Mosberger ( davidm @ cs . arizona . edu )
* David Miller ( davem @ redhat . com )
*
* Support routines for initializing a PCI subsystem .
*/
/* fixed for multiple pci buses, 1999 Andrea Arcangeli <andrea@suse.de> */
/*
* Nov 2000 , Ivan Kokshaysky < ink @ jurassic . park . msu . ru >
* Resource sorting
*/
# include <linux/init.h>
# include <linux/kernel.h>
# include <linux/pci.h>
# include <linux/errno.h>
# include <linux/ioport.h>
# include <linux/cache.h>
# include <linux/slab.h>
# include "pci.h"
2008-11-21 21:38:52 +03:00
void pci_update_resource ( struct pci_dev * dev , int resno )
2005-04-17 02:20:36 +04:00
{
struct pci_bus_region region ;
u32 new , check , mask ;
int reg ;
2008-11-21 21:41:27 +03:00
enum pci_bar_type type ;
2008-11-21 21:38:52 +03:00
struct resource * res = dev - > resource + resno ;
2005-04-17 02:20:36 +04:00
2006-12-20 00:12:08 +03:00
/*
* Ignore resources for unimplemented BARs and unused resource slots
* for 64 bit BARs .
*/
2005-08-07 13:49:59 +04:00
if ( ! res - > flags )
return ;
2006-12-20 00:12:08 +03:00
/*
* Ignore non - moveable resources . This might be legacy resources for
* which no functional BAR register exists or another important
2008-06-13 20:52:11 +04:00
* system resource we shouldn ' t move around .
2006-12-20 00:12:08 +03:00
*/
if ( res - > flags & IORESOURCE_PCI_FIXED )
return ;
2005-04-17 02:20:36 +04:00
pcibios_resource_to_bus ( dev , & region , res ) ;
2008-10-20 08:07:37 +04:00
dev_dbg ( & dev - > dev , " BAR %d: got res %pR bus [%#llx-%#llx] "
" flags %#lx \n " , resno , res ,
2007-12-10 09:32:16 +03:00
( unsigned long long ) region . start ,
( unsigned long long ) region . end ,
2008-06-13 20:52:11 +04:00
( unsigned long ) res - > flags ) ;
2005-04-17 02:20:36 +04:00
new = region . start | ( res - > flags & PCI_REGION_FLAG_MASK ) ;
if ( res - > flags & IORESOURCE_IO )
mask = ( u32 ) PCI_BASE_ADDRESS_IO_MASK ;
else
mask = ( u32 ) PCI_BASE_ADDRESS_MEM_MASK ;
2008-11-21 21:41:27 +03:00
reg = pci_resource_bar ( dev , resno , & type ) ;
if ( ! reg )
return ;
if ( type ! = pci_bar_unknown ) {
2005-08-26 21:49:22 +04:00
if ( ! ( res - > flags & IORESOURCE_ROM_ENABLE ) )
return ;
new | = PCI_ROM_ADDRESS_ENABLE ;
2005-04-17 02:20:36 +04:00
}
pci_write_config_dword ( dev , reg , new ) ;
pci_read_config_dword ( dev , reg , & check ) ;
if ( ( new ^ check ) & mask ) {
2008-06-13 20:52:11 +04:00
dev_err ( & dev - > dev , " BAR %d: error updating (%#08x != %#08x) \n " ,
resno , new , check ) ;
2005-04-17 02:20:36 +04:00
}
if ( ( new & ( PCI_BASE_ADDRESS_SPACE | PCI_BASE_ADDRESS_MEM_TYPE_MASK ) ) = =
( PCI_BASE_ADDRESS_SPACE_MEMORY | PCI_BASE_ADDRESS_MEM_TYPE_64 ) ) {
2005-08-07 13:49:59 +04:00
new = region . start > > 16 > > 16 ;
2005-04-17 02:20:36 +04:00
pci_write_config_dword ( dev , reg + 4 , new ) ;
pci_read_config_dword ( dev , reg + 4 , & check ) ;
if ( check ! = new ) {
2008-06-13 20:52:11 +04:00
dev_err ( & dev - > dev , " BAR %d: error updating "
" (high %#08x != %#08x) \n " , resno , new , check ) ;
2005-04-17 02:20:36 +04:00
}
}
res - > flags & = ~ IORESOURCE_UNSET ;
2008-06-13 20:52:11 +04:00
dev_dbg ( & dev - > dev , " BAR %d: moved to bus [%#llx-%#llx] flags %#lx \n " ,
resno , ( unsigned long long ) region . start ,
( unsigned long long ) region . end , res - > flags ) ;
2005-04-17 02:20:36 +04:00
}
2007-03-27 09:53:30 +04:00
int pci_claim_resource ( struct pci_dev * dev , int resource )
2005-04-17 02:20:36 +04:00
{
struct resource * res = & dev - > resource [ resource ] ;
2009-06-18 00:33:33 +04:00
struct resource * root ;
2005-04-17 02:20:36 +04:00
int err ;
2009-06-18 00:33:33 +04:00
root = pci_find_parent_resource ( dev , res ) ;
2005-04-17 02:20:36 +04:00
err = - EINVAL ;
if ( root ! = NULL )
2009-08-03 01:04:19 +04:00
err = request_resource ( root , res ) ;
2005-04-17 02:20:36 +04:00
if ( err ) {
2009-08-03 01:04:19 +04:00
const char * dtype = resource < PCI_BRIDGE_RESOURCES ? " device " : " bridge " ;
2008-10-20 08:07:37 +04:00
dev_err ( & dev - > dev , " BAR %d: %s of %s %pR \n " ,
2008-06-13 20:52:11 +04:00
resource ,
root ? " address space collision on " :
" no parent found for " ,
2008-10-20 08:07:37 +04:00
dtype , res ) ;
2005-04-17 02:20:36 +04:00
}
return err ;
}
2009-07-01 08:45:44 +04:00
EXPORT_SYMBOL ( pci_claim_resource ) ;
2005-04-17 02:20:36 +04:00
2009-03-16 11:13:39 +03:00
# ifdef CONFIG_PCI_QUIRKS
void pci_disable_bridge_window ( struct pci_dev * dev )
{
dev_dbg ( & dev - > dev , " Disabling bridge window. \n " ) ;
/* MMIO Base/Limit */
pci_write_config_dword ( dev , PCI_MEMORY_BASE , 0x0000fff0 ) ;
/* Prefetchable MMIO Base/Limit */
pci_write_config_dword ( dev , PCI_PREF_LIMIT_UPPER32 , 0 ) ;
pci_write_config_dword ( dev , PCI_PREF_MEMORY_BASE , 0x0000fff0 ) ;
pci_write_config_dword ( dev , PCI_PREF_BASE_UPPER32 , 0xffffffff ) ;
}
# endif /* CONFIG_PCI_QUIRKS */
2009-04-24 07:49:25 +04:00
static int __pci_assign_resource ( struct pci_bus * bus , struct pci_dev * dev ,
int resno )
2005-04-17 02:20:36 +04:00
{
struct resource * res = dev - > resource + resno ;
2006-06-13 04:06:02 +04:00
resource_size_t size , min , align ;
2005-04-17 02:20:36 +04:00
int ret ;
2008-10-13 15:24:28 +04:00
size = resource_size ( res ) ;
2005-04-17 02:20:36 +04:00
min = ( res - > flags & IORESOURCE_IO ) ? PCIBIOS_MIN_IO : PCIBIOS_MIN_MEM ;
2009-08-29 00:00:06 +04:00
align = pci_resource_alignment ( dev , res ) ;
2005-04-17 02:20:36 +04:00
/* First, try exact prefetching match.. */
ret = pci_bus_alloc_resource ( bus , res , size , align , min ,
IORESOURCE_PREFETCH ,
pcibios_align_resource , dev ) ;
if ( ret < 0 & & ( res - > flags & IORESOURCE_PREFETCH ) ) {
/*
* That failed .
*
* But a prefetching area can handle a non - prefetching
* window ( it will just not perform as well ) .
*/
ret = pci_bus_alloc_resource ( bus , res , size , align , min , 0 ,
pcibios_align_resource , dev ) ;
}
2009-04-24 07:49:25 +04:00
if ( ! ret ) {
PCI: clean up resource alignment management
Done per Linus' request and suggestions. Linus has explained that
better than I'll be able to explain:
On Thu, Mar 27, 2008 at 10:12:10AM -0700, Linus Torvalds wrote:
> Actually, before we go any further, there might be a less intrusive
> alternative: add just a couple of flags to the resource flags field (we
> still have something like 8 unused bits on 32-bit), and use those to
> implement a generic "resource_alignment()" routine.
>
> Two flags would do it:
>
> - IORESOURCE_SIZEALIGN: size indicates alignment (regular PCI device
> resources)
>
> - IORESOURCE_STARTALIGN: start field is alignment (PCI bus resources
> during probing)
>
> and then the case of both flags zero (or both bits set) would actually be
> "invalid", and we would also clear the IORESOURCE_STARTALIGN flag when we
> actually allocate the resource (so that we don't use the "start" field as
> alignment incorrectly when it no longer indicates alignment).
>
> That wouldn't be totally generic, but it would have the nice property of
> automatically at least add sanity checking for that whole "res->start has
> the odd meaning of 'alignment' during probing" and remove the need for a
> new field, and it would allow us to have a generic "resource_alignment()"
> routine that just gets a resource pointer.
Besides, I removed IORESOURCE_BUS_HAS_VGA flag which was unused for ages.
Signed-off-by: Ivan Kokshaysky <ink@jurassic.park.msu.ru>
Cc: Linus Torvalds <torvalds@linux-foundation.org>
Cc: Gary Hade <garyhade@us.ibm.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
2008-03-30 19:50:14 +04:00
res - > flags & = ~ IORESOURCE_STARTALIGN ;
if ( resno < PCI_BRIDGE_RESOURCES )
2008-11-21 21:38:52 +03:00
pci_update_resource ( dev , resno ) ;
2005-04-17 02:20:36 +04:00
}
return ret ;
}
2009-04-24 07:49:25 +04:00
int pci_assign_resource ( struct pci_dev * dev , int resno )
{
struct resource * res = dev - > resource + resno ;
resource_size_t align ;
struct pci_bus * bus ;
int ret ;
2009-08-29 00:00:06 +04:00
align = pci_resource_alignment ( dev , res ) ;
2009-04-24 07:49:25 +04:00
if ( ! align ) {
dev_info ( & dev - > dev , " BAR %d: can't allocate resource (bogus "
" alignment) %pR flags %#lx \n " ,
resno , res , res - > flags ) ;
return - EINVAL ;
}
bus = dev - > bus ;
while ( ( ret = __pci_assign_resource ( bus , dev , resno ) ) ) {
if ( bus - > parent & & bus - > self - > transparent )
bus = bus - > parent ;
else
bus = NULL ;
if ( bus )
continue ;
break ;
}
if ( ret )
dev_info ( & dev - > dev , " BAR %d: can't allocate %s resource %pR \n " ,
resno , res - > flags & IORESOURCE_IO ? " I/O " : " mem " , res ) ;
return ret ;
}
2005-04-17 02:20:36 +04:00
/* Sort resources by alignment */
2007-03-27 09:53:30 +04:00
void pdev_sort_resources ( struct pci_dev * dev , struct resource_list * head )
2005-04-17 02:20:36 +04:00
{
int i ;
for ( i = 0 ; i < PCI_NUM_RESOURCES ; i + + ) {
struct resource * r ;
struct resource_list * list , * tmp ;
2006-06-13 04:06:02 +04:00
resource_size_t r_align ;
2005-04-17 02:20:36 +04:00
r = & dev - > resource [ i ] ;
2006-12-20 00:12:08 +03:00
if ( r - > flags & IORESOURCE_PCI_FIXED )
continue ;
2005-04-17 02:20:36 +04:00
if ( ! ( r - > flags ) | | r - > parent )
continue ;
PCI: clean up resource alignment management
Done per Linus' request and suggestions. Linus has explained that
better than I'll be able to explain:
On Thu, Mar 27, 2008 at 10:12:10AM -0700, Linus Torvalds wrote:
> Actually, before we go any further, there might be a less intrusive
> alternative: add just a couple of flags to the resource flags field (we
> still have something like 8 unused bits on 32-bit), and use those to
> implement a generic "resource_alignment()" routine.
>
> Two flags would do it:
>
> - IORESOURCE_SIZEALIGN: size indicates alignment (regular PCI device
> resources)
>
> - IORESOURCE_STARTALIGN: start field is alignment (PCI bus resources
> during probing)
>
> and then the case of both flags zero (or both bits set) would actually be
> "invalid", and we would also clear the IORESOURCE_STARTALIGN flag when we
> actually allocate the resource (so that we don't use the "start" field as
> alignment incorrectly when it no longer indicates alignment).
>
> That wouldn't be totally generic, but it would have the nice property of
> automatically at least add sanity checking for that whole "res->start has
> the odd meaning of 'alignment' during probing" and remove the need for a
> new field, and it would allow us to have a generic "resource_alignment()"
> routine that just gets a resource pointer.
Besides, I removed IORESOURCE_BUS_HAS_VGA flag which was unused for ages.
Signed-off-by: Ivan Kokshaysky <ink@jurassic.park.msu.ru>
Cc: Linus Torvalds <torvalds@linux-foundation.org>
Cc: Gary Hade <garyhade@us.ibm.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
2008-03-30 19:50:14 +04:00
2009-08-29 00:00:06 +04:00
r_align = pci_resource_alignment ( dev , r ) ;
2005-04-17 02:20:36 +04:00
if ( ! r_align ) {
2008-06-13 20:52:11 +04:00
dev_warn ( & dev - > dev , " BAR %d: bogus alignment "
2008-10-20 08:07:37 +04:00
" %pR flags %#lx \n " ,
i , r , r - > flags ) ;
2005-04-17 02:20:36 +04:00
continue ;
}
for ( list = head ; ; list = list - > next ) {
2006-06-13 04:06:02 +04:00
resource_size_t align = 0 ;
2005-04-17 02:20:36 +04:00
struct resource_list * ln = list - > next ;
PCI: clean up resource alignment management
Done per Linus' request and suggestions. Linus has explained that
better than I'll be able to explain:
On Thu, Mar 27, 2008 at 10:12:10AM -0700, Linus Torvalds wrote:
> Actually, before we go any further, there might be a less intrusive
> alternative: add just a couple of flags to the resource flags field (we
> still have something like 8 unused bits on 32-bit), and use those to
> implement a generic "resource_alignment()" routine.
>
> Two flags would do it:
>
> - IORESOURCE_SIZEALIGN: size indicates alignment (regular PCI device
> resources)
>
> - IORESOURCE_STARTALIGN: start field is alignment (PCI bus resources
> during probing)
>
> and then the case of both flags zero (or both bits set) would actually be
> "invalid", and we would also clear the IORESOURCE_STARTALIGN flag when we
> actually allocate the resource (so that we don't use the "start" field as
> alignment incorrectly when it no longer indicates alignment).
>
> That wouldn't be totally generic, but it would have the nice property of
> automatically at least add sanity checking for that whole "res->start has
> the odd meaning of 'alignment' during probing" and remove the need for a
> new field, and it would allow us to have a generic "resource_alignment()"
> routine that just gets a resource pointer.
Besides, I removed IORESOURCE_BUS_HAS_VGA flag which was unused for ages.
Signed-off-by: Ivan Kokshaysky <ink@jurassic.park.msu.ru>
Cc: Linus Torvalds <torvalds@linux-foundation.org>
Cc: Gary Hade <garyhade@us.ibm.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
2008-03-30 19:50:14 +04:00
if ( ln )
2009-08-29 00:00:06 +04:00
align = pci_resource_alignment ( ln - > dev , ln - > res ) ;
PCI: clean up resource alignment management
Done per Linus' request and suggestions. Linus has explained that
better than I'll be able to explain:
On Thu, Mar 27, 2008 at 10:12:10AM -0700, Linus Torvalds wrote:
> Actually, before we go any further, there might be a less intrusive
> alternative: add just a couple of flags to the resource flags field (we
> still have something like 8 unused bits on 32-bit), and use those to
> implement a generic "resource_alignment()" routine.
>
> Two flags would do it:
>
> - IORESOURCE_SIZEALIGN: size indicates alignment (regular PCI device
> resources)
>
> - IORESOURCE_STARTALIGN: start field is alignment (PCI bus resources
> during probing)
>
> and then the case of both flags zero (or both bits set) would actually be
> "invalid", and we would also clear the IORESOURCE_STARTALIGN flag when we
> actually allocate the resource (so that we don't use the "start" field as
> alignment incorrectly when it no longer indicates alignment).
>
> That wouldn't be totally generic, but it would have the nice property of
> automatically at least add sanity checking for that whole "res->start has
> the odd meaning of 'alignment' during probing" and remove the need for a
> new field, and it would allow us to have a generic "resource_alignment()"
> routine that just gets a resource pointer.
Besides, I removed IORESOURCE_BUS_HAS_VGA flag which was unused for ages.
Signed-off-by: Ivan Kokshaysky <ink@jurassic.park.msu.ru>
Cc: Linus Torvalds <torvalds@linux-foundation.org>
Cc: Gary Hade <garyhade@us.ibm.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
2008-03-30 19:50:14 +04:00
2005-04-17 02:20:36 +04:00
if ( r_align > align ) {
tmp = kmalloc ( sizeof ( * tmp ) , GFP_KERNEL ) ;
if ( ! tmp )
panic ( " pdev_sort_resources(): "
" kmalloc() failed! \n " ) ;
tmp - > next = ln ;
tmp - > res = r ;
tmp - > dev = dev ;
list - > next = tmp ;
break ;
}
}
}
}
2008-03-04 21:56:47 +03:00
int pci_enable_resources ( struct pci_dev * dev , int mask )
{
u16 cmd , old_cmd ;
int i ;
struct resource * r ;
pci_read_config_word ( dev , PCI_COMMAND , & cmd ) ;
old_cmd = cmd ;
for ( i = 0 ; i < PCI_NUM_RESOURCES ; i + + ) {
if ( ! ( mask & ( 1 < < i ) ) )
continue ;
r = & dev - > resource [ i ] ;
if ( ! ( r - > flags & ( IORESOURCE_IO | IORESOURCE_MEM ) ) )
continue ;
if ( ( i = = PCI_ROM_RESOURCE ) & &
( ! ( r - > flags & IORESOURCE_ROM_ENABLE ) ) )
continue ;
if ( ! r - > parent ) {
dev_err ( & dev - > dev , " device not available because of "
2008-10-20 08:07:37 +04:00
" BAR %d %pR collisions \n " , i , r ) ;
2008-03-04 21:56:47 +03:00
return - EINVAL ;
}
if ( r - > flags & IORESOURCE_IO )
cmd | = PCI_COMMAND_IO ;
if ( r - > flags & IORESOURCE_MEM )
cmd | = PCI_COMMAND_MEMORY ;
}
if ( cmd ! = old_cmd ) {
dev_info ( & dev - > dev , " enabling device (%04x -> %04x) \n " ,
old_cmd , cmd ) ;
pci_write_config_word ( dev , PCI_COMMAND , cmd ) ;
}
return 0 ;
}