2005-04-17 02:20:36 +04:00
/*
* drivers / pci / setup - bus . 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 .
*/
/*
* Nov 2000 , Ivan Kokshaysky < ink @ jurassic . park . msu . ru >
* PCI - PCI bridges cleanup , sorted resource allocation .
* Feb 2002 , Ivan Kokshaysky < ink @ jurassic . park . msu . ru >
* Converted to allocation in 3 passes , which gives
* tighter packing . Prefetchable range support .
*/
# include <linux/init.h>
# include <linux/kernel.h>
# include <linux/module.h>
# include <linux/pci.h>
# include <linux/errno.h>
# include <linux/ioport.h>
# include <linux/cache.h>
# include <linux/slab.h>
2009-08-29 00:00:06 +04:00
# include "pci.h"
2005-04-17 02:20:36 +04:00
2009-02-18 21:44:29 +03:00
static void pbus_assign_resources_sorted ( const struct pci_bus * bus )
2005-04-17 02:20:36 +04:00
{
struct pci_dev * dev ;
struct resource * res ;
struct resource_list head , * list , * tmp ;
int idx ;
head . next = NULL ;
list_for_each_entry ( dev , & bus - > devices , bus_list ) {
u16 class = dev - > class > > 8 ;
2006-10-04 13:15:34 +04:00
/* Don't touch classless devices or host bridges or ioapics. */
2005-04-17 02:20:36 +04:00
if ( class = = PCI_CLASS_NOT_DEFINED | |
2006-09-12 21:21:44 +04:00
class = = PCI_CLASS_BRIDGE_HOST )
2005-04-17 02:20:36 +04:00
continue ;
2006-10-04 13:15:34 +04:00
/* Don't touch ioapic devices already enabled by firmware */
2006-09-12 21:21:44 +04:00
if ( class = = PCI_CLASS_SYSTEM_PIC ) {
2006-10-04 13:15:34 +04:00
u16 command ;
pci_read_config_word ( dev , PCI_COMMAND , & command ) ;
if ( command & ( PCI_COMMAND_IO | PCI_COMMAND_MEMORY ) )
2006-09-12 21:21:44 +04:00
continue ;
}
2005-04-17 02:20:36 +04:00
pdev_sort_resources ( dev , & head ) ;
}
for ( list = head . next ; list ; ) {
res = list - > res ;
idx = res - & list - > dev - > resource [ 0 ] ;
2005-04-28 11:25:50 +04:00
if ( pci_assign_resource ( list - > dev , idx ) ) {
res - > start = 0 ;
2005-07-07 03:07:56 +04:00
res - > end = 0 ;
2005-04-28 11:25:50 +04:00
res - > flags = 0 ;
}
2005-04-17 02:20:36 +04:00
tmp = list ;
list = list - > next ;
kfree ( tmp ) ;
}
}
2005-09-10 00:03:23 +04:00
void pci_setup_cardbus ( struct pci_bus * bus )
2005-04-17 02:20:36 +04:00
{
struct pci_dev * bridge = bus - > self ;
2009-10-27 22:26:47 +03:00
struct resource * res ;
2005-04-17 02:20:36 +04:00
struct pci_bus_region region ;
2009-11-04 20:32:57 +03:00
dev_info ( & bridge - > dev , " CardBus bridge to [bus %02x-%02x] \n " ,
bus - > secondary , bus - > subordinate ) ;
2005-04-17 02:20:36 +04:00
2009-10-27 22:26:47 +03:00
res = bus - > resource [ 0 ] ;
pcibios_resource_to_bus ( bridge , & region , res ) ;
if ( res - > flags & IORESOURCE_IO ) {
2005-04-17 02:20:36 +04:00
/*
* The IO resource is allocated a range twice as large as it
* would normally need . This allows us to set both IO regs .
*/
2009-10-27 22:26:47 +03:00
dev_info ( & bridge - > dev , " bridge window %pR \n " , res ) ;
2005-04-17 02:20:36 +04:00
pci_write_config_dword ( bridge , PCI_CB_IO_BASE_0 ,
region . start ) ;
pci_write_config_dword ( bridge , PCI_CB_IO_LIMIT_0 ,
region . end ) ;
}
2009-10-27 22:26:47 +03:00
res = bus - > resource [ 1 ] ;
pcibios_resource_to_bus ( bridge , & region , res ) ;
if ( res - > flags & IORESOURCE_IO ) {
dev_info ( & bridge - > dev , " bridge window %pR \n " , res ) ;
2005-04-17 02:20:36 +04:00
pci_write_config_dword ( bridge , PCI_CB_IO_BASE_1 ,
region . start ) ;
pci_write_config_dword ( bridge , PCI_CB_IO_LIMIT_1 ,
region . end ) ;
}
2009-10-27 22:26:47 +03:00
res = bus - > resource [ 2 ] ;
pcibios_resource_to_bus ( bridge , & region , res ) ;
if ( res - > flags & IORESOURCE_MEM ) {
dev_info ( & bridge - > dev , " bridge window %pR \n " , res ) ;
2005-04-17 02:20:36 +04:00
pci_write_config_dword ( bridge , PCI_CB_MEMORY_BASE_0 ,
region . start ) ;
pci_write_config_dword ( bridge , PCI_CB_MEMORY_LIMIT_0 ,
region . end ) ;
}
2009-10-27 22:26:47 +03:00
res = bus - > resource [ 3 ] ;
pcibios_resource_to_bus ( bridge , & region , res ) ;
if ( res - > flags & IORESOURCE_MEM ) {
dev_info ( & bridge - > dev , " bridge window %pR \n " , res ) ;
2005-04-17 02:20:36 +04:00
pci_write_config_dword ( bridge , PCI_CB_MEMORY_BASE_1 ,
region . start ) ;
pci_write_config_dword ( bridge , PCI_CB_MEMORY_LIMIT_1 ,
region . end ) ;
}
}
2005-09-10 00:03:23 +04:00
EXPORT_SYMBOL ( pci_setup_cardbus ) ;
2005-04-17 02:20:36 +04:00
/* Initialize bridges with base/limit values we have collected.
PCI - to - PCI Bridge Architecture Specification rev . 1.1 ( 1998 )
requires that if there is no I / O ports or memory behind the
bridge , corresponding range must be turned off by writing base
value greater than limit to the bridge ' s base / limit registers .
Note : care must be taken when updating I / O base / limit registers
of bridges which support 32 - bit I / O . This update requires two
config space writes , so it ' s quite possible that an I / O window of
the bridge will have some undesirable address ( e . g . 0 ) after the
first write . Ditto 64 - bit prefetchable MMIO . */
2009-12-23 02:02:21 +03:00
static void pci_setup_bridge_io ( struct pci_bus * bus )
2005-04-17 02:20:36 +04:00
{
struct pci_dev * bridge = bus - > self ;
2009-10-27 22:26:47 +03:00
struct resource * res ;
2005-04-17 02:20:36 +04:00
struct pci_bus_region region ;
2009-12-23 02:02:21 +03:00
u32 l , io_upper16 ;
2005-04-17 02:20:36 +04:00
/* Set up the top and bottom of the PCI I/O segment for this bus. */
2009-10-27 22:26:47 +03:00
res = bus - > resource [ 0 ] ;
pcibios_resource_to_bus ( bridge , & region , res ) ;
if ( res - > flags & IORESOURCE_IO ) {
2005-04-17 02:20:36 +04:00
pci_read_config_dword ( bridge , PCI_IO_BASE , & l ) ;
l & = 0xffff0000 ;
l | = ( region . start > > 8 ) & 0x00f0 ;
l | = region . end & 0xf000 ;
/* Set up upper 16 bits of I/O base/limit. */
io_upper16 = ( region . end & 0xffff0000 ) | ( region . start > > 16 ) ;
2009-10-27 22:26:47 +03:00
dev_info ( & bridge - > dev , " bridge window %pR \n " , res ) ;
2009-12-23 02:02:21 +03:00
} else {
2005-04-17 02:20:36 +04:00
/* Clear upper 16 bits of I/O base/limit. */
io_upper16 = 0 ;
l = 0x00f0 ;
2009-10-27 22:26:47 +03:00
dev_info ( & bridge - > dev , " bridge window [io disabled] \n " ) ;
2005-04-17 02:20:36 +04:00
}
/* Temporarily disable the I/O range before updating PCI_IO_BASE. */
pci_write_config_dword ( bridge , PCI_IO_BASE_UPPER16 , 0x0000ffff ) ;
/* Update lower 16 bits of I/O base/limit. */
pci_write_config_dword ( bridge , PCI_IO_BASE , l ) ;
/* Update upper 16 bits of I/O base/limit. */
pci_write_config_dword ( bridge , PCI_IO_BASE_UPPER16 , io_upper16 ) ;
2009-12-23 02:02:21 +03:00
}
static void pci_setup_bridge_mmio ( struct pci_bus * bus )
{
struct pci_dev * bridge = bus - > self ;
struct resource * res ;
struct pci_bus_region region ;
u32 l ;
2005-04-17 02:20:36 +04:00
2009-12-23 02:02:21 +03:00
/* Set up the top and bottom of the PCI Memory segment for this bus. */
2009-10-27 22:26:47 +03:00
res = bus - > resource [ 1 ] ;
pcibios_resource_to_bus ( bridge , & region , res ) ;
if ( res - > flags & IORESOURCE_MEM ) {
2005-04-17 02:20:36 +04:00
l = ( region . start > > 16 ) & 0xfff0 ;
l | = region . end & 0xfff00000 ;
2009-10-27 22:26:47 +03:00
dev_info ( & bridge - > dev , " bridge window %pR \n " , res ) ;
2009-12-23 02:02:21 +03:00
} else {
2005-04-17 02:20:36 +04:00
l = 0x0000fff0 ;
2009-10-27 22:26:47 +03:00
dev_info ( & bridge - > dev , " bridge window [mem disabled] \n " ) ;
2005-04-17 02:20:36 +04:00
}
pci_write_config_dword ( bridge , PCI_MEMORY_BASE , l ) ;
2009-12-23 02:02:21 +03:00
}
static void pci_setup_bridge_mmio_pref ( struct pci_bus * bus )
{
struct pci_dev * bridge = bus - > self ;
struct resource * res ;
struct pci_bus_region region ;
u32 l , bu , lu ;
2005-04-17 02:20:36 +04:00
/* Clear out the upper 32 bits of PREF limit.
If PCI_PREF_BASE_UPPER32 was non - zero , this temporarily
disables PREF range , which is ok . */
pci_write_config_dword ( bridge , PCI_PREF_LIMIT_UPPER32 , 0 ) ;
/* Set up PREF base/limit. */
2007-12-10 09:32:15 +03:00
bu = lu = 0 ;
2009-10-27 22:26:47 +03:00
res = bus - > resource [ 2 ] ;
pcibios_resource_to_bus ( bridge , & region , res ) ;
if ( res - > flags & IORESOURCE_PREFETCH ) {
2005-04-17 02:20:36 +04:00
l = ( region . start > > 16 ) & 0xfff0 ;
l | = region . end & 0xfff00000 ;
2009-10-27 22:26:47 +03:00
if ( res - > flags & IORESOURCE_MEM_64 ) {
2009-04-24 07:48:32 +04:00
bu = upper_32_bits ( region . start ) ;
lu = upper_32_bits ( region . end ) ;
}
2009-10-27 22:26:47 +03:00
dev_info ( & bridge - > dev , " bridge window %pR \n " , res ) ;
2009-12-23 02:02:21 +03:00
} else {
2005-04-17 02:20:36 +04:00
l = 0x0000fff0 ;
2009-10-27 22:26:47 +03:00
dev_info ( & bridge - > dev , " bridge window [mem pref disabled] \n " ) ;
2005-04-17 02:20:36 +04:00
}
pci_write_config_dword ( bridge , PCI_PREF_MEMORY_BASE , l ) ;
2009-12-01 00:51:44 +03:00
/* Set the upper 32 bits of PREF base & limit. */
pci_write_config_dword ( bridge , PCI_PREF_BASE_UPPER32 , bu ) ;
pci_write_config_dword ( bridge , PCI_PREF_LIMIT_UPPER32 , lu ) ;
2009-12-23 02:02:21 +03:00
}
static void __pci_setup_bridge ( struct pci_bus * bus , unsigned long type )
{
struct pci_dev * bridge = bus - > self ;
if ( pci_is_enabled ( bridge ) )
return ;
dev_info ( & bridge - > dev , " PCI bridge to [bus %02x-%02x] \n " ,
bus - > secondary , bus - > subordinate ) ;
if ( type & IORESOURCE_IO )
pci_setup_bridge_io ( bus ) ;
if ( type & IORESOURCE_MEM )
pci_setup_bridge_mmio ( bus ) ;
if ( type & IORESOURCE_PREFETCH )
pci_setup_bridge_mmio_pref ( bus ) ;
2005-04-17 02:20:36 +04:00
pci_write_config_word ( bridge , PCI_BRIDGE_CONTROL , bus - > bridge_ctl ) ;
}
2009-12-23 02:02:21 +03:00
static void pci_setup_bridge ( struct pci_bus * bus )
{
unsigned long type = IORESOURCE_IO | IORESOURCE_MEM |
IORESOURCE_PREFETCH ;
__pci_setup_bridge ( bus , type ) ;
}
2005-04-17 02:20:36 +04:00
/* Check whether the bridge supports optional I/O and
prefetchable memory ranges . If not , the respective
base / limit registers must be read - only and read as 0. */
2007-03-27 09:53:30 +04:00
static void pci_bridge_check_ranges ( struct pci_bus * bus )
2005-04-17 02:20:36 +04:00
{
u16 io ;
u32 pmem ;
struct pci_dev * bridge = bus - > self ;
struct resource * b_res ;
b_res = & bridge - > resource [ PCI_BRIDGE_RESOURCES ] ;
b_res [ 1 ] . flags | = IORESOURCE_MEM ;
pci_read_config_word ( bridge , PCI_IO_BASE , & io ) ;
if ( ! io ) {
pci_write_config_word ( bridge , PCI_IO_BASE , 0xf0f0 ) ;
pci_read_config_word ( bridge , PCI_IO_BASE , & io ) ;
pci_write_config_word ( bridge , PCI_IO_BASE , 0x0 ) ;
}
if ( io )
b_res [ 0 ] . flags | = IORESOURCE_IO ;
/* DECchip 21050 pass 2 errata: the bridge may miss an address
disconnect boundary by one PCI data phase .
Workaround : do not use prefetching on this device . */
if ( bridge - > vendor = = PCI_VENDOR_ID_DEC & & bridge - > device = = 0x0001 )
return ;
pci_read_config_dword ( bridge , PCI_PREF_MEMORY_BASE , & pmem ) ;
if ( ! pmem ) {
pci_write_config_dword ( bridge , PCI_PREF_MEMORY_BASE ,
0xfff0fff0 ) ;
pci_read_config_dword ( bridge , PCI_PREF_MEMORY_BASE , & pmem ) ;
pci_write_config_dword ( bridge , PCI_PREF_MEMORY_BASE , 0x0 ) ;
}
2009-04-24 07:48:32 +04:00
if ( pmem ) {
2005-04-17 02:20:36 +04:00
b_res [ 2 ] . flags | = IORESOURCE_MEM | IORESOURCE_PREFETCH ;
2009-04-24 07:48:32 +04:00
if ( ( pmem & PCI_PREF_RANGE_TYPE_MASK ) = = PCI_PREF_RANGE_TYPE_64 )
b_res [ 2 ] . flags | = IORESOURCE_MEM_64 ;
}
/* double check if bridge does support 64 bit pref */
if ( b_res [ 2 ] . flags & IORESOURCE_MEM_64 ) {
u32 mem_base_hi , tmp ;
pci_read_config_dword ( bridge , PCI_PREF_BASE_UPPER32 ,
& mem_base_hi ) ;
pci_write_config_dword ( bridge , PCI_PREF_BASE_UPPER32 ,
0xffffffff ) ;
pci_read_config_dword ( bridge , PCI_PREF_BASE_UPPER32 , & tmp ) ;
if ( ! tmp )
b_res [ 2 ] . flags & = ~ IORESOURCE_MEM_64 ;
pci_write_config_dword ( bridge , PCI_PREF_BASE_UPPER32 ,
mem_base_hi ) ;
}
2005-04-17 02:20:36 +04:00
}
/* Helper function for sizing routines: find first available
bus resource of a given type . Note : we intentionally skip
the bus resources which have already been assigned ( that is ,
have non - NULL parent resource ) . */
2007-03-27 09:53:30 +04:00
static struct resource * find_free_bus_resource ( struct pci_bus * bus , unsigned long type )
2005-04-17 02:20:36 +04:00
{
int i ;
struct resource * r ;
unsigned long type_mask = IORESOURCE_IO | IORESOURCE_MEM |
IORESOURCE_PREFETCH ;
for ( i = 0 ; i < PCI_BUS_NUM_RESOURCES ; i + + ) {
r = bus - > resource [ i ] ;
2005-06-15 18:59:27 +04:00
if ( r = = & ioport_resource | | r = = & iomem_resource )
continue ;
2009-10-27 19:39:18 +03:00
if ( r & & ( r - > flags & type_mask ) = = type & & ! r - > parent )
return r ;
2005-04-17 02:20:36 +04:00
}
return NULL ;
}
/* Sizing the IO windows of the PCI-PCI bridge is trivial,
since these windows have 4 K granularity and the IO ranges
of non - bridge PCI devices are limited to 256 bytes .
We must be careful with the ISA aliasing though . */
2009-09-10 01:09:24 +04:00
static void pbus_size_io ( struct pci_bus * bus , resource_size_t min_size )
2005-04-17 02:20:36 +04:00
{
struct pci_dev * dev ;
struct resource * b_res = find_free_bus_resource ( bus , IORESOURCE_IO ) ;
unsigned long size = 0 , size1 = 0 ;
if ( ! b_res )
return ;
list_for_each_entry ( dev , & bus - > devices , bus_list ) {
int i ;
for ( i = 0 ; i < PCI_NUM_RESOURCES ; i + + ) {
struct resource * r = & dev - > resource [ i ] ;
unsigned long r_size ;
if ( r - > parent | | ! ( r - > flags & IORESOURCE_IO ) )
continue ;
2008-10-13 15:24:28 +04:00
r_size = resource_size ( r ) ;
2005-04-17 02:20:36 +04:00
if ( r_size < 0x400 )
/* Might be re-aligned for ISA */
size + = r_size ;
else
size1 + = r_size ;
}
}
2009-09-10 01:09:24 +04:00
if ( size < min_size )
size = min_size ;
2005-04-17 02:20:36 +04:00
/* To be fixed in 2.5: we should have sort of HAVE_ISA
flag in the struct pci_bus . */
# if defined(CONFIG_ISA) || defined(CONFIG_EISA)
size = ( size & 0xff ) + ( ( size & ~ 0xffUL ) < < 2 ) ;
# endif
2007-07-09 22:55:51 +04:00
size = ALIGN ( size + size1 , 4096 ) ;
2005-04-17 02:20:36 +04:00
if ( ! size ) {
2009-11-04 20:32:57 +03:00
if ( b_res - > start | | b_res - > end )
dev_info ( & bus - > self - > dev , " disabling bridge window "
" %pR to [bus %02x-%02x] (unused) \n " , b_res ,
bus - > secondary , bus - > subordinate ) ;
2005-04-17 02:20:36 +04:00
b_res - > flags = 0 ;
return ;
}
/* Alignment of the IO window is always 4K */
b_res - > start = 4096 ;
b_res - > end = b_res - > start + size - 1 ;
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
b_res - > flags | = IORESOURCE_STARTALIGN ;
2005-04-17 02:20:36 +04:00
}
/* Calculate the size of the bus and minimal alignment which
guarantees that all child resources fit in this size . */
2009-09-10 01:09:24 +04:00
static int pbus_size_mem ( struct pci_bus * bus , unsigned long mask ,
unsigned long type , resource_size_t min_size )
2005-04-17 02:20:36 +04:00
{
struct pci_dev * dev ;
2007-12-10 09:32:15 +03:00
resource_size_t min_align , align , size ;
resource_size_t aligns [ 12 ] ; /* Alignments from 1Mb to 2Gb */
2005-04-17 02:20:36 +04:00
int order , max_order ;
struct resource * b_res = find_free_bus_resource ( bus , type ) ;
2009-04-24 07:48:32 +04:00
unsigned int mem64_mask = 0 ;
2005-04-17 02:20:36 +04:00
if ( ! b_res )
return 0 ;
memset ( aligns , 0 , sizeof ( aligns ) ) ;
max_order = 0 ;
size = 0 ;
2009-04-24 07:48:32 +04:00
mem64_mask = b_res - > flags & IORESOURCE_MEM_64 ;
b_res - > flags & = ~ IORESOURCE_MEM_64 ;
2005-04-17 02:20:36 +04:00
list_for_each_entry ( dev , & bus - > devices , bus_list ) {
int i ;
2009-04-24 07:48:32 +04:00
2005-04-17 02:20:36 +04:00
for ( i = 0 ; i < PCI_NUM_RESOURCES ; i + + ) {
struct resource * r = & dev - > resource [ i ] ;
2007-12-10 09:32:15 +03:00
resource_size_t r_size ;
2005-04-17 02:20:36 +04:00
if ( r - > parent | | ( r - > flags & mask ) ! = type )
continue ;
2008-10-13 15:24:28 +04:00
r_size = resource_size ( r ) ;
2005-04-17 02:20:36 +04:00
/* For bridges size != alignment */
2009-08-29 00:00:06 +04:00
align = pci_resource_alignment ( dev , r ) ;
2005-04-17 02:20:36 +04:00
order = __ffs ( align ) - 20 ;
if ( order > 11 ) {
2009-11-04 20:32:57 +03:00
dev_warn ( & dev - > dev , " disabling BAR %d: %pR "
" (bad alignment %#llx) \n " , i , r ,
( unsigned long long ) align ) ;
2005-04-17 02:20:36 +04:00
r - > flags = 0 ;
continue ;
}
size + = r_size ;
if ( order < 0 )
order = 0 ;
/* Exclude ranges with size > align from
calculation of the alignment . */
if ( r_size = = align )
aligns [ order ] + = align ;
if ( order > max_order )
max_order = order ;
2009-04-24 07:48:32 +04:00
mem64_mask & = r - > flags & IORESOURCE_MEM_64 ;
2005-04-17 02:20:36 +04:00
}
}
2009-09-10 01:09:24 +04:00
if ( size < min_size )
size = min_size ;
2005-04-17 02:20:36 +04:00
align = 0 ;
min_align = 0 ;
for ( order = 0 ; order < = max_order ; order + + ) {
2008-09-11 12:31:50 +04:00
resource_size_t align1 = 1 ;
align1 < < = ( order + 20 ) ;
2005-04-17 02:20:36 +04:00
if ( ! align )
min_align = align1 ;
2007-07-09 22:55:51 +04:00
else if ( ALIGN ( align + min_align , min_align ) < align1 )
2005-04-17 02:20:36 +04:00
min_align = align1 > > 1 ;
align + = aligns [ order ] ;
}
2007-07-09 22:55:51 +04:00
size = ALIGN ( size , min_align ) ;
2005-04-17 02:20:36 +04:00
if ( ! size ) {
2009-11-04 20:32:57 +03:00
if ( b_res - > start | | b_res - > end )
dev_info ( & bus - > self - > dev , " disabling bridge window "
" %pR to [bus %02x-%02x] (unused) \n " , b_res ,
bus - > secondary , bus - > subordinate ) ;
2005-04-17 02:20:36 +04:00
b_res - > flags = 0 ;
return 1 ;
}
b_res - > start = min_align ;
b_res - > end = size + min_align - 1 ;
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
b_res - > flags | = IORESOURCE_STARTALIGN ;
2009-04-24 07:48:32 +04:00
b_res - > flags | = mem64_mask ;
2005-04-17 02:20:36 +04:00
return 1 ;
}
2008-04-19 00:53:56 +04:00
static void pci_bus_size_cardbus ( struct pci_bus * bus )
2005-04-17 02:20:36 +04:00
{
struct pci_dev * bridge = bus - > self ;
struct resource * b_res = & bridge - > resource [ PCI_BRIDGE_RESOURCES ] ;
u16 ctrl ;
/*
* Reserve some resources for CardBus . We reserve
* a fixed amount of bus space for CardBus bridges .
*/
2008-04-23 05:16:30 +04:00
b_res [ 0 ] . start = 0 ;
b_res [ 0 ] . end = pci_cardbus_io_size - 1 ;
b_res [ 0 ] . flags | = IORESOURCE_IO | IORESOURCE_SIZEALIGN ;
2005-04-17 02:20:36 +04:00
2008-04-23 05:16:30 +04:00
b_res [ 1 ] . start = 0 ;
b_res [ 1 ] . end = pci_cardbus_io_size - 1 ;
b_res [ 1 ] . flags | = IORESOURCE_IO | IORESOURCE_SIZEALIGN ;
2005-04-17 02:20:36 +04:00
/*
* Check whether prefetchable memory is supported
* by this bridge .
*/
pci_read_config_word ( bridge , PCI_CB_BRIDGE_CONTROL , & ctrl ) ;
if ( ! ( ctrl & PCI_CB_BRIDGE_CTL_PREFETCH_MEM0 ) ) {
ctrl | = PCI_CB_BRIDGE_CTL_PREFETCH_MEM0 ;
pci_write_config_word ( bridge , PCI_CB_BRIDGE_CONTROL , ctrl ) ;
pci_read_config_word ( bridge , PCI_CB_BRIDGE_CONTROL , & ctrl ) ;
}
/*
* If we have prefetchable memory support , allocate
* two regions . Otherwise , allocate one region of
* twice the size .
*/
if ( ctrl & PCI_CB_BRIDGE_CTL_PREFETCH_MEM0 ) {
2008-04-23 05:16:30 +04:00
b_res [ 2 ] . start = 0 ;
b_res [ 2 ] . end = pci_cardbus_mem_size - 1 ;
b_res [ 2 ] . flags | = IORESOURCE_MEM | IORESOURCE_PREFETCH | IORESOURCE_SIZEALIGN ;
2005-04-17 02:20:36 +04:00
2008-04-23 05:16:30 +04:00
b_res [ 3 ] . start = 0 ;
b_res [ 3 ] . end = pci_cardbus_mem_size - 1 ;
b_res [ 3 ] . flags | = IORESOURCE_MEM | IORESOURCE_SIZEALIGN ;
2005-04-17 02:20:36 +04:00
} else {
2008-04-23 05:16:30 +04:00
b_res [ 3 ] . start = 0 ;
b_res [ 3 ] . end = pci_cardbus_mem_size * 2 - 1 ;
b_res [ 3 ] . flags | = IORESOURCE_MEM | IORESOURCE_SIZEALIGN ;
2005-04-17 02:20:36 +04:00
}
}
2008-02-03 00:33:43 +03:00
void __ref pci_bus_size_bridges ( struct pci_bus * bus )
2005-04-17 02:20:36 +04:00
{
struct pci_dev * dev ;
unsigned long mask , prefmask ;
2009-09-10 01:09:24 +04:00
resource_size_t min_mem_size = 0 , min_io_size = 0 ;
2005-04-17 02:20:36 +04:00
list_for_each_entry ( dev , & bus - > devices , bus_list ) {
struct pci_bus * b = dev - > subordinate ;
if ( ! b )
continue ;
switch ( dev - > class > > 8 ) {
case PCI_CLASS_BRIDGE_CARDBUS :
pci_bus_size_cardbus ( b ) ;
break ;
case PCI_CLASS_BRIDGE_PCI :
default :
pci_bus_size_bridges ( b ) ;
break ;
}
}
/* The root bus? */
if ( ! bus - > self )
return ;
switch ( bus - > self - > class > > 8 ) {
case PCI_CLASS_BRIDGE_CARDBUS :
/* don't size cardbuses yet. */
break ;
case PCI_CLASS_BRIDGE_PCI :
pci_bridge_check_ranges ( bus ) ;
2009-09-10 01:09:24 +04:00
if ( bus - > self - > is_hotplug_bridge ) {
min_io_size = pci_hotplug_io_size ;
min_mem_size = pci_hotplug_mem_size ;
}
2005-04-17 02:20:36 +04:00
default :
2009-09-10 01:09:24 +04:00
pbus_size_io ( bus , min_io_size ) ;
2005-04-17 02:20:36 +04:00
/* If the bridge supports prefetchable range, size it
separately . If it doesn ' t , or its prefetchable window
has already been allocated by arch code , try
non - prefetchable range for both types of PCI memory
resources . */
mask = IORESOURCE_MEM ;
prefmask = IORESOURCE_MEM | IORESOURCE_PREFETCH ;
2009-09-10 01:09:24 +04:00
if ( pbus_size_mem ( bus , prefmask , prefmask , min_mem_size ) )
2005-04-17 02:20:36 +04:00
mask = prefmask ; /* Success, size non-prefetch only. */
2009-09-10 01:09:24 +04:00
else
min_mem_size + = min_mem_size ;
pbus_size_mem ( bus , mask , IORESOURCE_MEM , min_mem_size ) ;
2005-04-17 02:20:36 +04:00
break ;
}
}
EXPORT_SYMBOL ( pci_bus_size_bridges ) ;
2009-02-18 21:44:29 +03:00
void __ref pci_bus_assign_resources ( const struct pci_bus * bus )
2005-04-17 02:20:36 +04:00
{
struct pci_bus * b ;
struct pci_dev * dev ;
pbus_assign_resources_sorted ( bus ) ;
list_for_each_entry ( dev , & bus - > devices , bus_list ) {
b = dev - > subordinate ;
if ( ! b )
continue ;
pci_bus_assign_resources ( b ) ;
switch ( dev - > class > > 8 ) {
case PCI_CLASS_BRIDGE_PCI :
pci_setup_bridge ( b ) ;
break ;
case PCI_CLASS_BRIDGE_CARDBUS :
pci_setup_cardbus ( b ) ;
break ;
default :
2008-06-13 20:52:11 +04:00
dev_info ( & dev - > dev , " not setting up bridge for bus "
" %04x:%02x \n " , pci_domain_nr ( b ) , b - > number ) ;
2005-04-17 02:20:36 +04:00
break ;
}
}
}
EXPORT_SYMBOL ( pci_bus_assign_resources ) ;
2008-06-23 22:33:06 +04:00
static void pci_bus_dump_res ( struct pci_bus * bus )
{
int i ;
for ( i = 0 ; i < PCI_BUS_NUM_RESOURCES ; i + + ) {
struct resource * res = bus - > resource [ i ] ;
2009-12-23 02:02:24 +03:00
if ( ! res | | ! res - > end | | ! res - > flags )
2008-06-23 22:33:06 +04:00
continue ;
2009-10-27 22:26:47 +03:00
dev_printk ( KERN_DEBUG , & bus - > dev , " resource %d %pR \n " , i , res ) ;
2008-06-23 22:33:06 +04:00
}
}
static void pci_bus_dump_resources ( struct pci_bus * bus )
{
struct pci_bus * b ;
struct pci_dev * dev ;
pci_bus_dump_res ( bus ) ;
list_for_each_entry ( dev , & bus - > devices , bus_list ) {
b = dev - > subordinate ;
if ( ! b )
continue ;
pci_bus_dump_resources ( b ) ;
}
}
2005-04-17 02:20:36 +04:00
void __init
pci_assign_unassigned_resources ( void )
{
struct pci_bus * bus ;
/* Depth first, calculate sizes and alignments of all
subordinate buses . */
list_for_each_entry ( bus , & pci_root_buses , node ) {
pci_bus_size_bridges ( bus ) ;
}
/* Depth last, allocate resources and update the hardware. */
list_for_each_entry ( bus , & pci_root_buses , node ) {
pci_bus_assign_resources ( bus ) ;
pci_enable_bridges ( bus ) ;
}
2008-06-23 22:33:06 +04:00
/* dump the resource on buses */
list_for_each_entry ( bus , & pci_root_buses , node ) {
pci_bus_dump_resources ( bus ) ;
}
2005-04-17 02:20:36 +04:00
}