2019-05-28 09:57:11 -07:00
// SPDX-License-Identifier: GPL-2.0-only
2005-04-16 15:20:36 -07:00
/*
2005-02-01 20:18:59 +00:00
* Copyright ( C ) 1999 , 2000 , 2004 MIPS Technologies , Inc .
* All rights reserved .
* Authors : Carsten Langgaard < carstenl @ mips . com >
* Maciej W . Rozycki < macro @ mips . com >
2005-04-16 15:20:36 -07:00
*
* MIPS boards specific PCI support .
*/
# include <linux/types.h>
# include <linux/pci.h>
# include <linux/kernel.h>
# include <asm/mips-boards/bonito64.h>
2013-01-22 12:59:30 +01:00
# define PCI_ACCESS_READ 0
2005-04-16 15:20:36 -07:00
# define PCI_ACCESS_WRITE 1
2007-06-06 14:52:38 +08:00
# define CFG_SPACE_REG(offset) (void *)CKSEG1ADDR(_pcictrl_bonito_pcicfg + (offset))
# define ID_SEL_BEGIN 10
# define MAX_DEV_NUM (31 - ID_SEL_BEGIN)
2005-04-16 15:20:36 -07:00
static int bonito64_pcibios_config_access ( unsigned char access_type ,
struct pci_bus * bus ,
unsigned int devfn , int where ,
u32 * data )
{
2007-06-06 14:52:38 +08:00
u32 busnum = bus - > number ;
u32 addr , type ;
2005-04-16 15:20:36 -07:00
u32 dummy ;
2007-06-06 14:52:38 +08:00
void * addrp ;
int device = PCI_SLOT ( devfn ) ;
int function = PCI_FUNC ( devfn ) ;
int reg = where & ~ 3 ;
2005-04-16 15:20:36 -07:00
if ( busnum = = 0 ) {
2007-06-06 14:52:38 +08:00
/* Type 0 configuration for onboard PCI bus */
if ( device > MAX_DEV_NUM )
return - 1 ;
2005-04-16 15:20:36 -07:00
2007-06-06 14:52:38 +08:00
addr = ( 1 < < ( device + ID_SEL_BEGIN ) ) | ( function < < 8 ) | reg ;
type = 0 ;
2005-04-16 15:20:36 -07:00
} else {
2007-06-06 14:52:38 +08:00
/* Type 1 configuration for offboard PCI bus */
addr = ( busnum < < 16 ) | ( device < < 11 ) | ( function < < 8 ) | reg ;
type = 0x10000 ;
2005-04-16 15:20:36 -07:00
}
2007-06-06 14:52:38 +08:00
/* Clear aborts */
BONITO_PCICMD | = BONITO_PCICMD_MABORT_CLR | BONITO_PCICMD_MTABORT_CLR ;
BONITO_PCIMAP_CFG = ( addr > > 16 ) | type ;
2005-04-16 15:20:36 -07:00
/* Flush Bonito register block */
dummy = BONITO_PCIMAP_CFG ;
2007-06-06 14:52:38 +08:00
mmiowb ( ) ;
2005-04-16 15:20:36 -07:00
2007-06-06 14:52:38 +08:00
addrp = CFG_SPACE_REG ( addr & 0xffff ) ;
2005-04-16 15:20:36 -07:00
if ( access_type = = PCI_ACCESS_WRITE ) {
2007-06-06 14:52:38 +08:00
writel ( cpu_to_le32 ( * data ) , addrp ) ;
2005-04-16 15:20:36 -07:00
/* Wait till done */
while ( BONITO_PCIMSTAT & 0xF ) ;
} else {
2007-06-06 14:52:38 +08:00
* data = le32_to_cpu ( readl ( addrp ) ) ;
2005-04-16 15:20:36 -07:00
}
/* Detect Master/Target abort */
if ( BONITO_PCICMD & ( BONITO_PCICMD_MABORT_CLR |
BONITO_PCICMD_MTABORT_CLR ) ) {
/* Error occurred */
/* Clear bits */
BONITO_PCICMD | = ( BONITO_PCICMD_MABORT_CLR |
BONITO_PCICMD_MTABORT_CLR ) ;
return - 1 ;
}
return 0 ;
2007-06-06 14:52:38 +08:00
2005-04-16 15:20:36 -07:00
}
/*
* We can ' t address 8 and 16 bit words directly . Instead we have to
* read / write a 32 bit word and mask / modify the data we actually want .
*/
static int bonito64_pcibios_read ( struct pci_bus * bus , unsigned int devfn ,
int where , int size , u32 * val )
{
u32 data = 0 ;
if ( ( size = = 2 ) & & ( where & 1 ) )
return PCIBIOS_BAD_REGISTER_NUMBER ;
else if ( ( size = = 4 ) & & ( where & 3 ) )
return PCIBIOS_BAD_REGISTER_NUMBER ;
if ( bonito64_pcibios_config_access ( PCI_ACCESS_READ , bus , devfn , where ,
& data ) )
return - 1 ;
if ( size = = 1 )
* val = ( data > > ( ( where & 3 ) < < 3 ) ) & 0xff ;
else if ( size = = 2 )
* val = ( data > > ( ( where & 3 ) < < 3 ) ) & 0xffff ;
else
* val = data ;
return PCIBIOS_SUCCESSFUL ;
}
static int bonito64_pcibios_write ( struct pci_bus * bus , unsigned int devfn ,
int where , int size , u32 val )
{
u32 data = 0 ;
if ( ( size = = 2 ) & & ( where & 1 ) )
return PCIBIOS_BAD_REGISTER_NUMBER ;
else if ( ( size = = 4 ) & & ( where & 3 ) )
return PCIBIOS_BAD_REGISTER_NUMBER ;
if ( size = = 4 )
data = val ;
else {
if ( bonito64_pcibios_config_access ( PCI_ACCESS_READ , bus , devfn ,
2013-01-22 12:59:30 +01:00
where , & data ) )
2005-04-16 15:20:36 -07:00
return - 1 ;
if ( size = = 1 )
data = ( data & ~ ( 0xff < < ( ( where & 3 ) < < 3 ) ) ) |
( val < < ( ( where & 3 ) < < 3 ) ) ;
else if ( size = = 2 )
data = ( data & ~ ( 0xffff < < ( ( where & 3 ) < < 3 ) ) ) |
( val < < ( ( where & 3 ) < < 3 ) ) ;
}
if ( bonito64_pcibios_config_access ( PCI_ACCESS_WRITE , bus , devfn , where ,
& data ) )
return - 1 ;
return PCIBIOS_SUCCESSFUL ;
}
struct pci_ops bonito64_pci_ops = {
. read = bonito64_pcibios_read ,
. write = bonito64_pcibios_write
} ;