2019-05-29 16:57:47 -07:00
// SPDX-License-Identifier: GPL-2.0-only
2006-09-06 14:42:08 -05:00
/*
* Copyright ( C ) 2006 PA Semi , Inc
*
* Authors : Kip Walker , PA Semi
* Olof Johansson , PA Semi
*
* Maintained by : Olof Johansson < olof @ lixom . net >
*
* Based on arch / powerpc / platforms / maple / pci . c
*/
# include <linux/kernel.h>
2022-03-08 20:20:25 +01:00
# include <linux/of_address.h>
2006-09-06 14:42:08 -05:00
# include <linux/pci.h>
# include <asm/pci-bridge.h>
2018-08-19 21:26:28 +01:00
# include <asm/isa-bridge.h>
2006-09-06 14:42:08 -05:00
# include <asm/machdep.h>
# include <asm/ppc-pci.h>
2015-03-31 16:00:52 +11:00
# include "pasemi.h"
2006-09-06 14:42:08 -05:00
# define PA_PXP_CFA(bus, devfn, off) (((bus) << 20) | ((devfn) << 12) | (off))
2007-04-16 16:26:34 +10:00
static inline int pa_pxp_offset_valid ( u8 bus , u8 devfn , int offset )
{
/* Device 0 Function 0 is special: It's config space spans function 1 as
* well , so allow larger offset . It ' s really a two - function device but the
* second function does not probe .
*/
if ( bus = = 0 & & devfn = = 0 )
return offset < 8192 ;
else
return offset < 4096 ;
}
2006-09-06 14:42:08 -05:00
2006-10-09 16:23:09 +01:00
static void volatile __iomem * pa_pxp_cfg_addr ( struct pci_controller * hose ,
2006-09-06 14:42:08 -05:00
u8 bus , u8 devfn , int offset )
{
2006-10-09 16:23:09 +01:00
return hose - > cfg_data + PA_PXP_CFA ( bus , devfn , offset ) ;
2006-09-06 14:42:08 -05:00
}
2007-09-05 12:08:50 +10:00
static inline int is_root_port ( int busno , int devfn )
{
return ( ( busno = = 0 ) & & ( PCI_FUNC ( devfn ) < 4 ) & &
( ( PCI_SLOT ( devfn ) = = 16 ) | | ( PCI_SLOT ( devfn ) = = 17 ) ) ) ;
}
static inline int is_5945_reg ( int reg )
{
return ( ( ( reg > = 0x18 ) & & ( reg < 0x34 ) ) | |
( ( reg > = 0x158 ) & & ( reg < 0x178 ) ) ) ;
}
static int workaround_5945 ( struct pci_bus * bus , unsigned int devfn ,
int offset , int len , u32 * val )
{
struct pci_controller * hose ;
void volatile __iomem * addr , * dummy ;
int byte ;
u32 tmp ;
if ( ! is_root_port ( bus - > number , devfn ) | | ! is_5945_reg ( offset ) )
return 0 ;
hose = pci_bus_to_host ( bus ) ;
addr = pa_pxp_cfg_addr ( hose , bus - > number , devfn , offset & ~ 0x3 ) ;
byte = offset & 0x3 ;
/* Workaround bug 5945: write 0 to a dummy register before reading,
* and write back what we read . We must read / write the full 32 - bit
* contents so we need to shift and mask by hand .
*/
dummy = pa_pxp_cfg_addr ( hose , bus - > number , devfn , 0x10 ) ;
out_le32 ( dummy , 0 ) ;
tmp = in_le32 ( addr ) ;
out_le32 ( addr , tmp ) ;
switch ( len ) {
case 1 :
* val = ( tmp > > ( 8 * byte ) ) & 0xff ;
break ;
case 2 :
if ( byte = = 0 )
* val = tmp & 0xffff ;
else
* val = ( tmp > > 16 ) & 0xffff ;
break ;
default :
* val = tmp ;
break ;
}
return 1 ;
}
2018-08-19 21:21:55 +01:00
# ifdef CONFIG_PPC_PASEMI_NEMO
# define PXP_ERR_CFG_REG 0x4
# define PXP_IGNORE_PCIE_ERRORS 0x800
# define SB600_BUS 5
static void sb600_set_flag ( int bus )
{
static void __iomem * iob_mapbase = NULL ;
struct resource res ;
struct device_node * dn ;
int err ;
if ( iob_mapbase = = NULL ) {
dn = of_find_compatible_node ( NULL , " isa " , " pasemi,1682m-iob " ) ;
if ( ! dn ) {
pr_crit ( " NEMO SB600 missing iob node \n " ) ;
return ;
}
err = of_address_to_resource ( dn , 0 , & res ) ;
of_node_put ( dn ) ;
if ( err ) {
pr_crit ( " NEMO SB600 missing resource \n " ) ;
return ;
}
pr_info ( " NEMO SB600 IOB base %08llx \n " , res . start ) ;
iob_mapbase = ioremap ( res . start + 0x100 , 0x94 ) ;
}
if ( iob_mapbase ! = NULL ) {
if ( bus = = SB600_BUS ) {
/*
* This is the SB600 ' s bus , tell the PCI - e root port
* to allow non - zero devices to enumerate .
*/
out_le32 ( iob_mapbase + PXP_ERR_CFG_REG , in_le32 ( iob_mapbase + PXP_ERR_CFG_REG ) | PXP_IGNORE_PCIE_ERRORS ) ;
} else {
/*
* Only scan device 0 on other busses
*/
out_le32 ( iob_mapbase + PXP_ERR_CFG_REG , in_le32 ( iob_mapbase + PXP_ERR_CFG_REG ) & ~ PXP_IGNORE_PCIE_ERRORS ) ;
}
}
}
# else
static void sb600_set_flag ( int bus )
{
}
# endif
2006-09-06 14:42:08 -05:00
static int pa_pxp_read_config ( struct pci_bus * bus , unsigned int devfn ,
int offset , int len , u32 * val )
{
struct pci_controller * hose ;
2006-10-09 16:23:09 +01:00
void volatile __iomem * addr ;
2006-09-06 14:42:08 -05:00
hose = pci_bus_to_host ( bus ) ;
if ( ! hose )
return PCIBIOS_DEVICE_NOT_FOUND ;
2007-04-16 16:26:34 +10:00
if ( ! pa_pxp_offset_valid ( bus - > number , devfn , offset ) )
2006-09-06 14:42:08 -05:00
return PCIBIOS_BAD_REGISTER_NUMBER ;
2007-09-05 12:08:50 +10:00
if ( workaround_5945 ( bus , devfn , offset , len , val ) )
return PCIBIOS_SUCCESSFUL ;
2006-09-06 14:42:08 -05:00
addr = pa_pxp_cfg_addr ( hose , bus - > number , devfn , offset ) ;
2018-08-19 21:26:28 +01:00
sb600_set_flag ( bus - > number ) ;
2006-09-06 14:42:08 -05:00
/*
* Note : the caller has already checked that offset is
* suitably aligned and that len is 1 , 2 or 4.
*/
switch ( len ) {
case 1 :
2006-10-09 16:23:09 +01:00
* val = in_8 ( addr ) ;
2006-09-06 14:42:08 -05:00
break ;
case 2 :
2006-10-09 16:23:09 +01:00
* val = in_le16 ( addr ) ;
2006-09-06 14:42:08 -05:00
break ;
default :
2006-10-09 16:23:09 +01:00
* val = in_le32 ( addr ) ;
2006-09-06 14:42:08 -05:00
break ;
}
return PCIBIOS_SUCCESSFUL ;
}
static int pa_pxp_write_config ( struct pci_bus * bus , unsigned int devfn ,
int offset , int len , u32 val )
{
struct pci_controller * hose ;
2006-10-09 16:23:09 +01:00
void volatile __iomem * addr ;
2006-09-06 14:42:08 -05:00
hose = pci_bus_to_host ( bus ) ;
if ( ! hose )
return PCIBIOS_DEVICE_NOT_FOUND ;
2007-04-16 16:26:34 +10:00
if ( ! pa_pxp_offset_valid ( bus - > number , devfn , offset ) )
2006-09-06 14:42:08 -05:00
return PCIBIOS_BAD_REGISTER_NUMBER ;
addr = pa_pxp_cfg_addr ( hose , bus - > number , devfn , offset ) ;
2018-08-19 21:26:28 +01:00
sb600_set_flag ( bus - > number ) ;
2006-09-06 14:42:08 -05:00
/*
* Note : the caller has already checked that offset is
* suitably aligned and that len is 1 , 2 or 4.
*/
switch ( len ) {
case 1 :
2006-10-09 16:23:09 +01:00
out_8 ( addr , val ) ;
2006-09-06 14:42:08 -05:00
break ;
case 2 :
2006-10-09 16:23:09 +01:00
out_le16 ( addr , val ) ;
2006-09-06 14:42:08 -05:00
break ;
default :
2006-10-09 16:23:09 +01:00
out_le32 ( addr , val ) ;
2006-09-06 14:42:08 -05:00
break ;
}
return PCIBIOS_SUCCESSFUL ;
}
static struct pci_ops pa_pxp_ops = {
2007-08-10 05:18:40 +10:00
. read = pa_pxp_read_config ,
. write = pa_pxp_write_config ,
2006-09-06 14:42:08 -05:00
} ;
static void __init setup_pa_pxp ( struct pci_controller * hose )
{
hose - > ops = & pa_pxp_ops ;
hose - > cfg_data = ioremap ( 0xe0000000 , 0x10000000 ) ;
}
2007-06-18 01:06:54 +02:00
static int __init pas_add_bridge ( struct device_node * dev )
2006-09-06 14:42:08 -05:00
{
struct pci_controller * hose ;
2017-08-21 10:16:47 -05:00
pr_debug ( " Adding PCI host bridge %pOF \n " , dev ) ;
2006-09-06 14:42:08 -05:00
hose = pcibios_alloc_controller ( dev ) ;
if ( ! hose )
return - ENOMEM ;
hose - > first_busno = 0 ;
hose - > last_busno = 0xff ;
2015-03-31 16:00:52 +11:00
hose - > controller_ops = pasemi_pci_controller_ops ;
2006-09-06 14:42:08 -05:00
setup_pa_pxp ( hose ) ;
2018-08-03 21:15:10 +10:00
pr_info ( " Found PA-PXP PCI host bridge. \n " ) ;
2006-09-06 14:42:08 -05:00
/* Interpret the "ranges" property */
pci_process_bridge_OF_ranges ( hose , dev , 1 ) ;
2018-08-19 21:26:28 +01:00
/*
* Scan for an isa bridge . This is needed to find the SB600 on the nemo
* and does nothing on machines without one .
*/
isa_bridge_find_early ( hose ) ;
2006-09-06 14:42:08 -05:00
return 0 ;
}
void __init pas_pci_init ( void )
{
struct device_node * np , * root ;
2018-07-25 21:55:18 +01:00
int res ;
2006-09-06 14:42:08 -05:00
root = of_find_node_by_path ( " / " ) ;
if ( ! root ) {
2018-08-03 21:15:10 +10:00
pr_crit ( " pas_pci_init: can't find root of device tree \n " ) ;
2006-09-06 14:42:08 -05:00
return ;
}
2017-12-06 12:03:52 +01:00
pci_set_flags ( PCI_SCAN_ALL_PCIE_DEVS ) ;
2018-07-25 21:55:18 +01:00
np = of_find_compatible_node ( root , NULL , " pasemi,rootbus " ) ;
if ( np ) {
res = pas_add_bridge ( np ) ;
of_node_put ( np ) ;
}
2006-09-06 14:42:08 -05:00
}
2007-09-05 12:08:30 +10:00
2021-12-16 17:00:24 -05:00
void __iomem * __init pasemi_pci_getcfgaddr ( struct pci_dev * dev , int offset )
2007-09-05 12:08:30 +10:00
{
struct pci_controller * hose ;
2007-09-05 12:08:50 +10:00
hose = pci_bus_to_host ( dev - > bus ) ;
2007-09-05 12:08:30 +10:00
2007-09-05 12:08:50 +10:00
return ( void __iomem * ) pa_pxp_cfg_addr ( hose , dev - > bus - > number , dev - > devfn , offset ) ;
2007-09-05 12:08:30 +10:00
}
2015-03-31 16:00:52 +11:00
struct pci_controller_ops pasemi_pci_controller_ops ;