2005-10-10 16:25:26 +04:00
/*
* Support for indirect PCI bridges .
*
* Copyright ( C ) 1998 Gabriel Paubert .
*
* This program is free software ; you can redistribute it and / or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation ; either version
* 2 of the License , or ( at your option ) any later version .
*/
# include <linux/kernel.h>
# include <linux/pci.h>
# include <linux/delay.h>
# include <linux/string.h>
# include <linux/init.h>
# include <asm/io.h>
# include <asm/prom.h>
# include <asm/pci-bridge.h>
# include <asm/machdep.h>
static int
indirect_read_config ( struct pci_bus * bus , unsigned int devfn , int offset ,
int len , u32 * val )
{
2009-04-30 07:10:07 +04:00
struct pci_controller * hose = pci_bus_to_host ( bus ) ;
2005-10-10 16:25:26 +04:00
volatile void __iomem * cfg_data ;
u8 cfg_type = 0 ;
2007-06-26 00:19:48 +04:00
u32 bus_no , reg ;
2005-10-10 16:25:26 +04:00
2007-07-11 22:22:41 +04:00
if ( hose - > indirect_type & PPC_INDIRECT_TYPE_NO_PCIE_LINK ) {
if ( bus - > number ! = hose - > first_busno )
return PCIBIOS_DEVICE_NOT_FOUND ;
if ( devfn ! = 0 )
return PCIBIOS_DEVICE_NOT_FOUND ;
}
2005-10-10 16:25:26 +04:00
if ( ppc_md . pci_exclude_device )
2007-06-22 09:23:57 +04:00
if ( ppc_md . pci_exclude_device ( hose , bus - > number , devfn ) )
2005-10-10 16:25:26 +04:00
return PCIBIOS_DEVICE_NOT_FOUND ;
2007-07-11 22:22:41 +04:00
2007-06-26 00:19:48 +04:00
if ( hose - > indirect_type & PPC_INDIRECT_TYPE_SET_CFG_TYPE )
2005-10-10 16:25:26 +04:00
if ( bus - > number ! = hose - > first_busno )
cfg_type = 1 ;
2007-06-25 22:09:42 +04:00
bus_no = ( bus - > number = = hose - > first_busno ) ?
2007-06-25 22:32:48 +04:00
hose - > self_busno : bus - > number ;
2007-06-25 22:09:42 +04:00
2007-06-26 00:19:48 +04:00
if ( hose - > indirect_type & PPC_INDIRECT_TYPE_EXT_REG )
reg = ( ( offset & 0xf00 ) < < 16 ) | ( offset & 0xfc ) ;
else
reg = offset & 0xfc ;
2007-07-20 01:07:35 +04:00
if ( hose - > indirect_type & PPC_INDIRECT_TYPE_BIG_ENDIAN )
out_be32 ( hose - > cfg_addr , ( 0x80000000 | ( bus_no < < 16 ) |
( devfn < < 8 ) | reg | cfg_type ) ) ;
else
out_le32 ( hose - > cfg_addr , ( 0x80000000 | ( bus_no < < 16 ) |
( devfn < < 8 ) | reg | cfg_type ) ) ;
2005-10-10 16:25:26 +04:00
/*
* Note : the caller has already checked that offset is
* suitably aligned and that len is 1 , 2 or 4.
*/
cfg_data = hose - > cfg_data + ( offset & 3 ) ;
switch ( len ) {
case 1 :
* val = in_8 ( cfg_data ) ;
break ;
case 2 :
* val = in_le16 ( cfg_data ) ;
break ;
default :
* val = in_le32 ( cfg_data ) ;
break ;
}
return PCIBIOS_SUCCESSFUL ;
}
static int
indirect_write_config ( struct pci_bus * bus , unsigned int devfn , int offset ,
int len , u32 val )
{
2009-04-30 07:10:07 +04:00
struct pci_controller * hose = pci_bus_to_host ( bus ) ;
2005-10-10 16:25:26 +04:00
volatile void __iomem * cfg_data ;
u8 cfg_type = 0 ;
2007-06-26 00:19:48 +04:00
u32 bus_no , reg ;
2005-10-10 16:25:26 +04:00
2007-07-11 22:22:41 +04:00
if ( hose - > indirect_type & PPC_INDIRECT_TYPE_NO_PCIE_LINK ) {
if ( bus - > number ! = hose - > first_busno )
return PCIBIOS_DEVICE_NOT_FOUND ;
if ( devfn ! = 0 )
return PCIBIOS_DEVICE_NOT_FOUND ;
}
2005-10-10 16:25:26 +04:00
if ( ppc_md . pci_exclude_device )
2007-06-22 09:23:57 +04:00
if ( ppc_md . pci_exclude_device ( hose , bus - > number , devfn ) )
2005-10-10 16:25:26 +04:00
return PCIBIOS_DEVICE_NOT_FOUND ;
2007-06-26 00:19:48 +04:00
if ( hose - > indirect_type & PPC_INDIRECT_TYPE_SET_CFG_TYPE )
2005-10-10 16:25:26 +04:00
if ( bus - > number ! = hose - > first_busno )
cfg_type = 1 ;
2007-06-25 22:09:42 +04:00
bus_no = ( bus - > number = = hose - > first_busno ) ?
2007-06-25 22:32:48 +04:00
hose - > self_busno : bus - > number ;
2007-06-25 22:09:42 +04:00
2007-06-26 00:19:48 +04:00
if ( hose - > indirect_type & PPC_INDIRECT_TYPE_EXT_REG )
reg = ( ( offset & 0xf00 ) < < 16 ) | ( offset & 0xfc ) ;
else
reg = offset & 0xfc ;
2007-07-20 01:07:35 +04:00
if ( hose - > indirect_type & PPC_INDIRECT_TYPE_BIG_ENDIAN )
out_be32 ( hose - > cfg_addr , ( 0x80000000 | ( bus_no < < 16 ) |
( devfn < < 8 ) | reg | cfg_type ) ) ;
else
out_le32 ( hose - > cfg_addr , ( 0x80000000 | ( bus_no < < 16 ) |
( devfn < < 8 ) | reg | cfg_type ) ) ;
2005-10-10 16:25:26 +04:00
2011-03-31 05:57:33 +04:00
/* suppress setting of PCI_PRIMARY_BUS */
2007-06-26 21:12:55 +04:00
if ( hose - > indirect_type & PPC_INDIRECT_TYPE_SURPRESS_PRIMARY_BUS )
if ( ( offset = = PCI_PRIMARY_BUS ) & &
( bus - > number = = hose - > first_busno ) )
val & = 0xffffff00 ;
2008-06-18 03:01:38 +04:00
/* Workaround for PCI_28 Errata in 440EPx/GRx */
if ( ( hose - > indirect_type & PPC_INDIRECT_TYPE_BROKEN_MRM ) & &
offset = = PCI_CACHE_LINE_SIZE ) {
val = 0 ;
}
2005-10-10 16:25:26 +04:00
/*
* Note : the caller has already checked that offset is
* suitably aligned and that len is 1 , 2 or 4.
*/
cfg_data = hose - > cfg_data + ( offset & 3 ) ;
switch ( len ) {
case 1 :
out_8 ( cfg_data , val ) ;
break ;
case 2 :
out_le16 ( cfg_data , val ) ;
break ;
default :
out_le32 ( cfg_data , val ) ;
break ;
}
return PCIBIOS_SUCCESSFUL ;
}
static struct pci_ops indirect_pci_ops =
{
2007-08-09 23:18:45 +04:00
. read = indirect_read_config ,
. write = indirect_write_config ,
2005-10-10 16:25:26 +04:00
} ;
void __init
2007-10-08 16:51:24 +04:00
setup_indirect_pci ( struct pci_controller * hose ,
resource_size_t cfg_addr ,
resource_size_t cfg_data , u32 flags )
2005-10-10 16:25:26 +04:00
{
2007-10-08 16:51:24 +04:00
resource_size_t base = cfg_addr & PAGE_MASK ;
2007-07-20 00:44:52 +04:00
void __iomem * mbase ;
2005-10-10 16:25:26 +04:00
mbase = ioremap ( base , PAGE_SIZE ) ;
2007-07-20 00:44:52 +04:00
hose - > cfg_addr = mbase + ( cfg_addr & ~ PAGE_MASK ) ;
2005-10-10 16:25:26 +04:00
if ( ( cfg_data & PAGE_MASK ) ! = base )
mbase = ioremap ( cfg_data & PAGE_MASK , PAGE_SIZE ) ;
2007-07-20 00:44:52 +04:00
hose - > cfg_data = mbase + ( cfg_data & ~ PAGE_MASK ) ;
hose - > ops = & indirect_pci_ops ;
2007-07-25 09:29:53 +04:00
hose - > indirect_type = flags ;
2005-10-10 16:25:26 +04:00
}