2019-06-04 11:11:33 +03:00
// SPDX-License-Identifier: GPL-2.0-only
2006-11-11 09:25:12 +03:00
/*
2008-04-24 13:21:10 +04:00
* Support PCI IO workaround
*
2006-11-11 09:25:12 +03:00
* Copyright ( C ) 2006 Benjamin Herrenschmidt < benh @ kernel . crashing . org >
* IBM , Corp .
2008-04-24 13:21:10 +04:00
* ( C ) Copyright 2007 - 2008 TOSHIBA CORPORATION
2006-11-11 09:25:12 +03:00
*/
# undef DEBUG
# include <linux/kernel.h>
2017-02-04 02:16:44 +03:00
# include <linux/sched/mm.h> /* for init_mm */
2020-06-09 07:32:42 +03:00
# include <linux/pgtable.h>
2008-04-24 13:21:10 +04:00
2006-11-11 09:25:12 +03:00
# include <asm/io.h>
# include <asm/machdep.h>
# include <asm/ppc-pci.h>
2011-04-12 01:25:01 +04:00
# include <asm/io-workarounds.h>
2017-07-27 09:24:53 +03:00
# include <asm/pte-walk.h>
2006-11-11 09:25:12 +03:00
2008-04-24 13:21:10 +04:00
# define IOWA_MAX_BUS 8
2006-11-11 09:25:12 +03:00
2008-04-24 13:21:10 +04:00
static struct iowa_bus iowa_busses [ IOWA_MAX_BUS ] ;
static unsigned int iowa_bus_count ;
2006-11-11 09:25:12 +03:00
2008-04-24 13:21:10 +04:00
static struct iowa_bus * iowa_pci_find ( unsigned long vaddr , unsigned long paddr )
{
int i , j ;
struct resource * res ;
unsigned long vstart , vend ;
2006-11-11 09:25:12 +03:00
2008-04-24 13:21:10 +04:00
for ( i = 0 ; i < iowa_bus_count ; i + + ) {
struct iowa_bus * bus = & iowa_busses [ i ] ;
struct pci_controller * phb = bus - > phb ;
2006-11-11 09:25:12 +03:00
2008-04-24 13:21:10 +04:00
if ( vaddr ) {
vstart = ( unsigned long ) phb - > io_base_virt ;
vend = vstart + phb - > pci_io_size - 1 ;
if ( ( vaddr > = vstart ) & & ( vaddr < = vend ) )
return bus ;
}
if ( paddr )
for ( j = 0 ; j < 3 ; j + + ) {
res = & phb - > mem_resources [ j ] ;
if ( paddr > = res - > start & & paddr < = res - > end )
return bus ;
}
2006-11-11 09:25:12 +03:00
}
2008-04-24 13:21:10 +04:00
2006-11-11 09:25:12 +03:00
return NULL ;
}
2013-07-15 07:03:08 +04:00
# ifdef CONFIG_PPC_INDIRECT_MMIO
2008-04-24 13:21:10 +04:00
struct iowa_bus * iowa_mem_find_bus ( const PCI_IO_ADDR addr )
2006-11-11 09:25:12 +03:00
{
2013-06-20 13:00:18 +04:00
unsigned hugepage_shift ;
2008-04-24 13:21:10 +04:00
struct iowa_bus * bus ;
2006-11-11 09:25:12 +03:00
int token ;
token = PCI_GET_ADDR_TOKEN ( addr ) ;
2008-04-24 13:21:10 +04:00
if ( token & & token < = iowa_bus_count )
bus = & iowa_busses [ token - 1 ] ;
2006-11-11 09:25:12 +03:00
else {
unsigned long vaddr , paddr ;
pte_t * ptep ;
vaddr = ( unsigned long ) PCI_FIX_ADDR ( addr ) ;
2008-04-24 13:21:10 +04:00
if ( vaddr < PHB_IO_BASE | | vaddr > = PHB_IO_END )
return NULL ;
2015-03-30 08:11:03 +03:00
/*
* We won ' t find huge pages here ( iomem ) . Also can ' t hit
* a page table free due to init_mm
*/
2017-07-27 09:24:53 +03:00
ptep = find_init_mm_pte ( vaddr , & hugepage_shift ) ;
2006-11-11 09:25:12 +03:00
if ( ptep = = NULL )
paddr = 0 ;
2013-06-20 13:00:18 +04:00
else {
WARN_ON ( hugepage_shift ) ;
2006-11-11 09:25:12 +03:00
paddr = pte_pfn ( * ptep ) < < PAGE_SHIFT ;
2013-06-20 13:00:18 +04:00
}
2008-04-24 13:21:10 +04:00
bus = iowa_pci_find ( vaddr , paddr ) ;
2006-11-11 09:25:12 +03:00
if ( bus = = NULL )
2008-04-24 13:21:10 +04:00
return NULL ;
2006-11-11 09:25:12 +03:00
}
2008-04-24 13:21:10 +04:00
return bus ;
2006-11-11 09:25:12 +03:00
}
2013-07-15 07:03:08 +04:00
# else /* CONFIG_PPC_INDIRECT_MMIO */
struct iowa_bus * iowa_mem_find_bus ( const PCI_IO_ADDR addr )
{
return NULL ;
}
# endif /* !CONFIG_PPC_INDIRECT_MMIO */
2006-11-11 09:25:12 +03:00
2013-07-15 07:03:08 +04:00
# ifdef CONFIG_PPC_INDIRECT_PIO
2008-04-24 13:21:10 +04:00
struct iowa_bus * iowa_pio_find_bus ( unsigned long port )
2006-11-11 09:25:12 +03:00
{
2008-04-24 13:21:10 +04:00
unsigned long vaddr = ( unsigned long ) pci_io_base + port ;
return iowa_pci_find ( vaddr , 0 ) ;
2006-11-11 09:25:12 +03:00
}
2013-07-15 07:03:08 +04:00
# else
struct iowa_bus * iowa_pio_find_bus ( unsigned long port )
{
return NULL ;
}
# endif
2006-11-11 09:25:12 +03:00
2008-04-24 13:21:10 +04:00
# define DEF_PCI_AC_RET(name, ret, at, al, space, aa) \
static ret iowa_ # # name at \
{ \
struct iowa_bus * bus ; \
bus = iowa_ # # space # # _find_bus ( aa ) ; \
if ( bus & & bus - > ops & & bus - > ops - > name ) \
return bus - > ops - > name al ; \
return __do_ # # name al ; \
2006-11-11 09:25:12 +03:00
}
2008-04-24 13:21:10 +04:00
# define DEF_PCI_AC_NORET(name, at, al, space, aa) \
static void iowa_ # # name at \
{ \
struct iowa_bus * bus ; \
bus = iowa_ # # space # # _find_bus ( aa ) ; \
if ( bus & & bus - > ops & & bus - > ops - > name ) { \
bus - > ops - > name al ; \
return ; \
} \
__do_ # # name al ; \
2006-11-11 09:25:12 +03:00
}
2008-04-24 13:21:10 +04:00
# include <asm/io-defs.h>
2006-11-11 09:25:12 +03:00
2008-04-24 13:21:10 +04:00
# undef DEF_PCI_AC_RET
# undef DEF_PCI_AC_NORET
2006-11-11 09:25:12 +03:00
2012-12-22 02:04:10 +04:00
static const struct ppc_pci_io iowa_pci_io = {
2006-11-11 09:25:12 +03:00
2008-04-24 13:21:10 +04:00
# define DEF_PCI_AC_RET(name, ret, at, al, space, aa) .name = iowa_##name,
# define DEF_PCI_AC_NORET(name, at, al, space, aa) .name = iowa_##name,
2006-11-11 09:25:12 +03:00
2008-04-24 13:21:10 +04:00
# include <asm/io-defs.h>
2006-11-11 09:25:12 +03:00
2008-04-24 13:21:10 +04:00
# undef DEF_PCI_AC_RET
# undef DEF_PCI_AC_NORET
2006-11-11 09:25:12 +03:00
2008-04-24 13:21:10 +04:00
} ;
2006-11-11 09:25:12 +03:00
2013-07-15 07:03:08 +04:00
# ifdef CONFIG_PPC_INDIRECT_MMIO
2019-08-20 17:07:13 +03:00
void __iomem * iowa_ioremap ( phys_addr_t addr , unsigned long size ,
pgprot_t prot , void * caller )
2006-11-11 09:25:12 +03:00
{
2008-04-24 13:21:10 +04:00
struct iowa_bus * bus ;
2018-10-09 16:51:45 +03:00
void __iomem * res = __ioremap_caller ( addr , size , prot , caller ) ;
2006-11-11 09:25:12 +03:00
int busno ;
2009-01-06 17:03:44 +03:00
bus = iowa_pci_find ( 0 , ( unsigned long ) addr ) ;
2006-11-11 09:25:12 +03:00
if ( bus ! = NULL ) {
2008-04-24 13:21:10 +04:00
busno = bus - iowa_busses ;
2006-11-11 09:25:12 +03:00
PCI_SET_ADDR_TOKEN ( res , busno + 1 ) ;
}
return res ;
}
2013-07-15 07:03:08 +04:00
# endif /* !CONFIG_PPC_INDIRECT_MMIO */
2006-11-11 09:25:12 +03:00
2019-08-20 17:07:13 +03:00
bool io_workaround_inited ;
2011-04-12 01:25:02 +04:00
/* Enable IO workaround */
2012-12-22 02:04:10 +04:00
static void io_workaround_init ( void )
2011-04-12 01:25:02 +04:00
{
if ( io_workaround_inited )
return ;
ppc_pci_io = iowa_pci_io ;
2019-08-20 17:07:13 +03:00
io_workaround_inited = true ;
2011-04-12 01:25:02 +04:00
}
/* Register new bus to support workaround */
2012-12-22 02:04:10 +04:00
void iowa_register_bus ( struct pci_controller * phb , struct ppc_pci_io * ops ,
int ( * initfunc ) ( struct iowa_bus * , void * ) , void * data )
2006-11-11 09:25:12 +03:00
{
2008-04-24 13:21:10 +04:00
struct iowa_bus * bus ;
2007-12-10 06:33:21 +03:00
struct device_node * np = phb - > dn ;
2006-11-11 09:25:12 +03:00
2011-04-12 01:25:02 +04:00
io_workaround_init ( ) ;
2008-04-24 13:21:10 +04:00
if ( iowa_bus_count > = IOWA_MAX_BUS ) {
pr_err ( " IOWA:Too many pci bridges, "
2017-08-21 18:16:47 +03:00
" workarounds disabled for %pOF \n " , np ) ;
2006-11-11 09:25:12 +03:00
return ;
}
2008-04-24 13:21:10 +04:00
bus = & iowa_busses [ iowa_bus_count ] ;
bus - > phb = phb ;
bus - > ops = ops ;
2011-04-12 01:25:02 +04:00
bus - > private = data ;
2006-11-11 09:25:12 +03:00
2008-04-24 13:21:10 +04:00
if ( initfunc )
if ( ( * initfunc ) ( bus , data ) )
return ;
2006-11-11 09:25:12 +03:00
2008-04-24 13:21:10 +04:00
iowa_bus_count + + ;
2006-11-11 09:25:12 +03:00
2017-08-21 18:16:47 +03:00
pr_debug ( " IOWA:[%d]Add bus, %pOF. \n " , iowa_bus_count - 1 , np ) ;
2006-11-11 09:25:12 +03:00
}