2007-12-02 15:00:32 +03:00
/*
* ip28 - berr . c : Bus error handling .
*
* Copyright ( C ) 2002 , 2003 Ladislav Michl ( ladis @ linux - mips . org )
* Copyright ( C ) 2005 Peter Fuerst ( pf @ net . alphadv . de ) - IP28
*/
# include <linux/init.h>
# include <linux/kernel.h>
# include <linux/sched.h>
# include <linux/seq_file.h>
# include <asm/addrspace.h>
# include <asm/system.h>
# include <asm/traps.h>
# include <asm/branch.h>
# include <asm/irq_regs.h>
# include <asm/sgi/mc.h>
# include <asm/sgi/hpc3.h>
# include <asm/sgi/ioc.h>
# include <asm/sgi/ip22.h>
# include <asm/r4kcache.h>
# include <asm/uaccess.h>
# include <asm/bootinfo.h>
static unsigned int count_be_is_fixup ;
static unsigned int count_be_handler ;
static unsigned int count_be_interrupt ;
static int debug_be_interrupt ;
static unsigned int cpu_err_stat ; /* Status reg for CPU */
static unsigned int gio_err_stat ; /* Status reg for GIO */
static unsigned int cpu_err_addr ; /* Error address reg for CPU */
static unsigned int gio_err_addr ; /* Error address reg for GIO */
static unsigned int extio_stat ;
static unsigned int hpc3_berr_stat ; /* Bus error interrupt status */
struct hpc3_stat {
unsigned long addr ;
unsigned int ctrl ;
unsigned int cbp ;
unsigned int ndptr ;
} ;
static struct {
struct hpc3_stat pbdma [ 8 ] ;
struct hpc3_stat scsi [ 2 ] ;
struct hpc3_stat ethrx , ethtx ;
} hpc3 ;
static struct {
unsigned long err_addr ;
struct {
u32 lo ;
u32 hi ;
} tags [ 1 ] [ 2 ] , tagd [ 4 ] [ 2 ] , tagi [ 4 ] [ 2 ] ; /* Way 0/1 */
} cache_tags ;
static inline void save_cache_tags ( unsigned busaddr )
{
unsigned long addr = CAC_BASE | busaddr ;
int i ;
cache_tags . err_addr = addr ;
/*
* Starting with a bus - address , save secondary cache ( indexed by
* PA [ 23. .18 : 7. .6 ] ) tags first .
*/
addr & = ~ 1L ;
# define tag cache_tags.tags[0]
cache_op ( Index_Load_Tag_S , addr ) ;
tag [ 0 ] . lo = read_c0_taglo ( ) ; /* PA[35:18], VA[13:12] */
tag [ 0 ] . hi = read_c0_taghi ( ) ; /* PA[39:36] */
cache_op ( Index_Load_Tag_S , addr | 1L ) ;
tag [ 1 ] . lo = read_c0_taglo ( ) ; /* PA[35:18], VA[13:12] */
tag [ 1 ] . hi = read_c0_taghi ( ) ; /* PA[39:36] */
# undef tag
/*
* Save all primary data cache ( indexed by VA [ 13 : 5 ] ) tags which
* might fit to this bus - address , knowing that VA [ 11 : 0 ] = = PA [ 11 : 0 ] .
* Saving all tags and evaluating them later is easier and safer
* than relying on VA [ 13 : 12 ] from the secondary cache tags to pick
* matching primary tags here already .
*/
addr & = ( 0xffL < < 56 ) | ( ( 1 < < 12 ) - 1 ) ;
# define tag cache_tags.tagd[i]
for ( i = 0 ; i < 4 ; + + i , addr + = ( 1 < < 12 ) ) {
cache_op ( Index_Load_Tag_D , addr ) ;
tag [ 0 ] . lo = read_c0_taglo ( ) ; /* PA[35:12] */
tag [ 0 ] . hi = read_c0_taghi ( ) ; /* PA[39:36] */
cache_op ( Index_Load_Tag_D , addr | 1L ) ;
tag [ 1 ] . lo = read_c0_taglo ( ) ; /* PA[35:12] */
tag [ 1 ] . hi = read_c0_taghi ( ) ; /* PA[39:36] */
}
# undef tag
/*
* Save primary instruction cache ( indexed by VA [ 13 : 6 ] ) tags
* the same way .
*/
addr & = ( 0xffL < < 56 ) | ( ( 1 < < 12 ) - 1 ) ;
# define tag cache_tags.tagi[i]
for ( i = 0 ; i < 4 ; + + i , addr + = ( 1 < < 12 ) ) {
cache_op ( Index_Load_Tag_I , addr ) ;
tag [ 0 ] . lo = read_c0_taglo ( ) ; /* PA[35:12] */
tag [ 0 ] . hi = read_c0_taghi ( ) ; /* PA[39:36] */
cache_op ( Index_Load_Tag_I , addr | 1L ) ;
tag [ 1 ] . lo = read_c0_taglo ( ) ; /* PA[35:12] */
tag [ 1 ] . hi = read_c0_taghi ( ) ; /* PA[39:36] */
}
# undef tag
}
# define GIO_ERRMASK 0xff00
# define CPU_ERRMASK 0x3f00
static void save_and_clear_buserr ( void )
{
int i ;
/* save status registers */
cpu_err_addr = sgimc - > cerr ;
cpu_err_stat = sgimc - > cstat ;
gio_err_addr = sgimc - > gerr ;
gio_err_stat = sgimc - > gstat ;
extio_stat = sgioc - > extio ;
hpc3_berr_stat = hpc3c0 - > bestat ;
hpc3 . scsi [ 0 ] . addr = ( unsigned long ) & hpc3c0 - > scsi_chan0 ;
hpc3 . scsi [ 0 ] . ctrl = hpc3c0 - > scsi_chan0 . ctrl ; /* HPC3_SCTRL_ACTIVE ? */
hpc3 . scsi [ 0 ] . cbp = hpc3c0 - > scsi_chan0 . cbptr ;
hpc3 . scsi [ 0 ] . ndptr = hpc3c0 - > scsi_chan0 . ndptr ;
hpc3 . scsi [ 1 ] . addr = ( unsigned long ) & hpc3c0 - > scsi_chan1 ;
hpc3 . scsi [ 1 ] . ctrl = hpc3c0 - > scsi_chan1 . ctrl ; /* HPC3_SCTRL_ACTIVE ? */
hpc3 . scsi [ 1 ] . cbp = hpc3c0 - > scsi_chan1 . cbptr ;
hpc3 . scsi [ 1 ] . ndptr = hpc3c0 - > scsi_chan1 . ndptr ;
hpc3 . ethrx . addr = ( unsigned long ) & hpc3c0 - > ethregs . rx_cbptr ;
hpc3 . ethrx . ctrl = hpc3c0 - > ethregs . rx_ctrl ; /* HPC3_ERXCTRL_ACTIVE ? */
hpc3 . ethrx . cbp = hpc3c0 - > ethregs . rx_cbptr ;
hpc3 . ethrx . ndptr = hpc3c0 - > ethregs . rx_ndptr ;
hpc3 . ethtx . addr = ( unsigned long ) & hpc3c0 - > ethregs . tx_cbptr ;
hpc3 . ethtx . ctrl = hpc3c0 - > ethregs . tx_ctrl ; /* HPC3_ETXCTRL_ACTIVE ? */
hpc3 . ethtx . cbp = hpc3c0 - > ethregs . tx_cbptr ;
hpc3 . ethtx . ndptr = hpc3c0 - > ethregs . tx_ndptr ;
for ( i = 0 ; i < 8 ; + + i ) {
/* HPC3_PDMACTRL_ISACT ? */
hpc3 . pbdma [ i ] . addr = ( unsigned long ) & hpc3c0 - > pbdma [ i ] ;
hpc3 . pbdma [ i ] . ctrl = hpc3c0 - > pbdma [ i ] . pbdma_ctrl ;
hpc3 . pbdma [ i ] . cbp = hpc3c0 - > pbdma [ i ] . pbdma_bptr ;
hpc3 . pbdma [ i ] . ndptr = hpc3c0 - > pbdma [ i ] . pbdma_dptr ;
}
i = 0 ;
if ( gio_err_stat & CPU_ERRMASK )
i = gio_err_addr ;
if ( cpu_err_stat & CPU_ERRMASK )
i = cpu_err_addr ;
save_cache_tags ( i ) ;
sgimc - > cstat = sgimc - > gstat = 0 ;
}
static void print_cache_tags ( void )
{
u32 scb , scw ;
int i ;
printk ( KERN_ERR " Cache tags @ %08x: \n " , ( unsigned ) cache_tags . err_addr ) ;
/* PA[31:12] shifted to PTag0 (PA[35:12]) format */
scw = ( cache_tags . err_addr > > 4 ) & 0x0fffff00 ;
scb = cache_tags . err_addr & ( ( 1 < < 12 ) - 1 ) & ~ ( ( 1 < < 5 ) - 1 ) ;
for ( i = 0 ; i < 4 ; + + i ) { /* for each possible VA[13:12] value */
if ( ( cache_tags . tagd [ i ] [ 0 ] . lo & 0x0fffff00 ) ! = scw & &
( cache_tags . tagd [ i ] [ 1 ] . lo & 0x0fffff00 ) ! = scw )
continue ;
printk ( KERN_ERR
" D: 0: %08x %08x, 1: %08x %08x (VA[13:5] %04x) \n " ,
cache_tags . tagd [ i ] [ 0 ] . hi , cache_tags . tagd [ i ] [ 0 ] . lo ,
cache_tags . tagd [ i ] [ 1 ] . hi , cache_tags . tagd [ i ] [ 1 ] . lo ,
scb | ( 1 < < 12 ) * i ) ;
}
scb = cache_tags . err_addr & ( ( 1 < < 12 ) - 1 ) & ~ ( ( 1 < < 6 ) - 1 ) ;
for ( i = 0 ; i < 4 ; + + i ) { /* for each possible VA[13:12] value */
if ( ( cache_tags . tagi [ i ] [ 0 ] . lo & 0x0fffff00 ) ! = scw & &
( cache_tags . tagi [ i ] [ 1 ] . lo & 0x0fffff00 ) ! = scw )
continue ;
printk ( KERN_ERR
" I: 0: %08x %08x, 1: %08x %08x (VA[13:6] %04x) \n " ,
cache_tags . tagi [ i ] [ 0 ] . hi , cache_tags . tagi [ i ] [ 0 ] . lo ,
cache_tags . tagi [ i ] [ 1 ] . hi , cache_tags . tagi [ i ] [ 1 ] . lo ,
scb | ( 1 < < 12 ) * i ) ;
}
i = read_c0_config ( ) ;
scb = i & ( 1 < < 13 ) ? 7 : 6 ; /* scblksize = 2^[7..6] */
scw = ( ( i > > 16 ) & 7 ) + 19 - 1 ; /* scwaysize = 2^[24..19] / 2 */
i = ( ( 1 < < scw ) - 1 ) & ~ ( ( 1 < < scb ) - 1 ) ;
printk ( KERN_ERR " S: 0: %08x %08x, 1: %08x %08x (PA[%u:%u] %05x) \n " ,
cache_tags . tags [ 0 ] [ 0 ] . hi , cache_tags . tags [ 0 ] [ 0 ] . lo ,
cache_tags . tags [ 0 ] [ 1 ] . hi , cache_tags . tags [ 0 ] [ 1 ] . lo ,
scw - 1 , scb , i & ( unsigned ) cache_tags . err_addr ) ;
}
static inline const char * cause_excode_text ( int cause )
{
static const char * txt [ 32 ] =
{ " Interrupt " ,
" TLB modification " ,
" TLB (load or instruction fetch) " ,
" TLB (store) " ,
" Address error (load or instruction fetch) " ,
" Address error (store) " ,
" Bus error (instruction fetch) " ,
" Bus error (data: load or store) " ,
" Syscall " ,
" Breakpoint " ,
" Reserved instruction " ,
" Coprocessor unusable " ,
" Arithmetic Overflow " ,
" Trap " ,
" 14 " ,
" Floating-Point " ,
" 16 " , " 17 " , " 18 " , " 19 " , " 20 " , " 21 " , " 22 " ,
" Watch Hi/Lo " ,
" 24 " , " 25 " , " 26 " , " 27 " , " 28 " , " 29 " , " 30 " , " 31 " ,
} ;
return txt [ ( cause & 0x7c ) > > 2 ] ;
}
static void print_buserr ( const struct pt_regs * regs )
{
const int field = 2 * sizeof ( unsigned long ) ;
int error = 0 ;
if ( extio_stat & EXTIO_MC_BUSERR ) {
printk ( KERN_ERR " MC Bus Error \n " ) ;
error | = 1 ;
}
if ( extio_stat & EXTIO_HPC3_BUSERR ) {
printk ( KERN_ERR " HPC3 Bus Error 0x%x:<id=0x%x,%s,lane=0x%x> \n " ,
hpc3_berr_stat ,
( hpc3_berr_stat & HPC3_BESTAT_PIDMASK ) > >
HPC3_BESTAT_PIDSHIFT ,
( hpc3_berr_stat & HPC3_BESTAT_CTYPE ) ? " PIO " : " DMA " ,
hpc3_berr_stat & HPC3_BESTAT_BLMASK ) ;
error | = 2 ;
}
if ( extio_stat & EXTIO_EISA_BUSERR ) {
printk ( KERN_ERR " EISA Bus Error \n " ) ;
error | = 4 ;
}
if ( cpu_err_stat & CPU_ERRMASK ) {
printk ( KERN_ERR " CPU error 0x%x<%s%s%s%s%s%s> @ 0x%08x \n " ,
cpu_err_stat ,
cpu_err_stat & SGIMC_CSTAT_RD ? " RD " : " " ,
cpu_err_stat & SGIMC_CSTAT_PAR ? " PAR " : " " ,
cpu_err_stat & SGIMC_CSTAT_ADDR ? " ADDR " : " " ,
cpu_err_stat & SGIMC_CSTAT_SYSAD_PAR ? " SYSAD " : " " ,
cpu_err_stat & SGIMC_CSTAT_SYSCMD_PAR ? " SYSCMD " : " " ,
cpu_err_stat & SGIMC_CSTAT_BAD_DATA ? " BAD_DATA " : " " ,
cpu_err_addr ) ;
error | = 8 ;
}
if ( gio_err_stat & GIO_ERRMASK ) {
printk ( KERN_ERR " GIO error 0x%x:<%s%s%s%s%s%s%s%s> @ 0x%08x \n " ,
gio_err_stat ,
gio_err_stat & SGIMC_GSTAT_RD ? " RD " : " " ,
gio_err_stat & SGIMC_GSTAT_WR ? " WR " : " " ,
gio_err_stat & SGIMC_GSTAT_TIME ? " TIME " : " " ,
gio_err_stat & SGIMC_GSTAT_PROM ? " PROM " : " " ,
gio_err_stat & SGIMC_GSTAT_ADDR ? " ADDR " : " " ,
gio_err_stat & SGIMC_GSTAT_BC ? " BC " : " " ,
gio_err_stat & SGIMC_GSTAT_PIO_RD ? " PIO_RD " : " " ,
gio_err_stat & SGIMC_GSTAT_PIO_WR ? " PIO_WR " : " " ,
gio_err_addr ) ;
error | = 16 ;
}
if ( ! error )
printk ( KERN_ERR " MC: Hmm, didn't find any error condition. \n " ) ;
else {
printk ( KERN_ERR " CP0: config %08x, "
" MC: cpuctrl0/1: %08x/%05x, giopar: %04x \n "
" MC: cpu/gio_memacc: %08x/%05x, memcfg0/1: %08x/%08x \n " ,
read_c0_config ( ) ,
sgimc - > cpuctrl0 , sgimc - > cpuctrl0 , sgimc - > giopar ,
sgimc - > cmacc , sgimc - > gmacc ,
sgimc - > mconfig0 , sgimc - > mconfig1 ) ;
print_cache_tags ( ) ;
}
printk ( KERN_ALERT " %s, epc == %0*lx, ra == %0*lx \n " ,
cause_excode_text ( regs - > cp0_cause ) ,
field , regs - > cp0_epc , field , regs - > regs [ 31 ] ) ;
}
/*
* Check , whether MC ' s ( virtual ) DMA address caused the bus error .
* See " Virtual DMA Specification " , Draft 1.5 , Feb 13 1992 , SGI
*/
static int addr_is_ram ( unsigned long addr , unsigned sz )
{
int i ;
for ( i = 0 ; i < boot_mem_map . nr_map ; i + + ) {
unsigned long a = boot_mem_map . map [ i ] . addr ;
if ( a < = addr & & addr + sz < = a + boot_mem_map . map [ i ] . size )
return 1 ;
}
return 0 ;
}
static int check_microtlb ( u32 hi , u32 lo , unsigned long vaddr )
{
/* This is likely rather similar to correct code ;-) */
vaddr & = 0x7fffffff ; /* Doc. states that top bit is ignored */
/* If tlb-entry is valid and VPN-high (bits [30:21] ?) matches... */
if ( ( lo & 2 ) & & ( vaddr > > 21 ) = = ( ( hi < < 1 ) > > 22 ) ) {
u32 ctl = sgimc - > dma_ctrl ;
if ( ctl & 1 ) {
unsigned int pgsz = ( ctl & 2 ) ? 14 : 12 ; /* 16k:4k */
/* PTEIndex is VPN-low (bits [22:14]/[20:12] ?) */
unsigned long pte = ( lo > > 6 ) < < 12 ; /* PTEBase */
pte + = 8 * ( ( vaddr > > pgsz ) & 0x1ff ) ;
if ( addr_is_ram ( pte , 8 ) ) {
/*
* Note : Since DMA hardware does look up
* translation on its own , this PTE * must *
* match the TLB / EntryLo - register format !
*/
unsigned long a = * ( unsigned long * )
PHYS_TO_XKSEG_UNCACHED ( pte ) ;
a = ( a & 0x3f ) < < 6 ; /* PFN */
a + = vaddr & ( ( 1 < < pgsz ) - 1 ) ;
return ( cpu_err_addr = = a ) ;
}
}
}
return 0 ;
}
static int check_vdma_memaddr ( void )
{
if ( cpu_err_stat & CPU_ERRMASK ) {
u32 a = sgimc - > maddronly ;
if ( ! ( sgimc - > dma_ctrl & 0x100 ) ) /* Xlate-bit clear ? */
return ( cpu_err_addr = = a ) ;
if ( check_microtlb ( sgimc - > dtlb_hi0 , sgimc - > dtlb_lo0 , a ) | |
check_microtlb ( sgimc - > dtlb_hi1 , sgimc - > dtlb_lo1 , a ) | |
check_microtlb ( sgimc - > dtlb_hi2 , sgimc - > dtlb_lo2 , a ) | |
check_microtlb ( sgimc - > dtlb_hi3 , sgimc - > dtlb_lo3 , a ) )
return 1 ;
}
return 0 ;
}
static int check_vdma_gioaddr ( void )
{
if ( gio_err_stat & GIO_ERRMASK ) {
u32 a = sgimc - > gio_dma_trans ;
a = ( sgimc - > gmaddronly & ~ a ) | ( sgimc - > gio_dma_sbits & a ) ;
return ( gio_err_addr = = a ) ;
}
return 0 ;
}
/*
* MC sends an interrupt whenever bus or parity errors occur . In addition ,
* if the error happened during a CPU read , it also asserts the bus error
* pin on the R4K . Code in bus error handler save the MC bus error registers
* and then clear the interrupt when this happens .
*/
static int ip28_be_interrupt ( const struct pt_regs * regs )
{
int i ;
save_and_clear_buserr ( ) ;
/*
* Try to find out , whether we got here by a mispredicted speculative
* load / store operation . If so , it ' s not fatal , we can go on .
*/
/* Any cause other than "Interrupt" (ExcCode 0) is fatal. */
if ( regs - > cp0_cause & CAUSEF_EXCCODE )
goto mips_be_fatal ;
/* Any cause other than "Bus error interrupt" (IP6) is weird. */
if ( ( regs - > cp0_cause & CAUSEF_IP6 ) ! = CAUSEF_IP6 )
goto mips_be_fatal ;
if ( extio_stat & ( EXTIO_HPC3_BUSERR | EXTIO_EISA_BUSERR ) )
goto mips_be_fatal ;
/* Any state other than "Memory bus error" is fatal. */
if ( cpu_err_stat & CPU_ERRMASK & ~ SGIMC_CSTAT_ADDR )
goto mips_be_fatal ;
/* GIO errors other than timeouts are fatal */
if ( gio_err_stat & GIO_ERRMASK & ~ SGIMC_GSTAT_TIME )
goto mips_be_fatal ;
/*
* Now we have an asynchronous bus error , speculatively or DMA caused .
* Need to search all DMA descriptors for the error address .
*/
2008-07-16 20:25:40 +04:00
for ( i = 0 ; i < sizeof ( hpc3 ) / sizeof ( struct hpc3_stat ) ; + + i ) {
2007-12-02 15:00:32 +03:00
struct hpc3_stat * hp = ( struct hpc3_stat * ) & hpc3 + i ;
if ( ( cpu_err_stat & CPU_ERRMASK ) & &
( cpu_err_addr = = hp - > ndptr | | cpu_err_addr = = hp - > cbp ) )
break ;
if ( ( gio_err_stat & GIO_ERRMASK ) & &
( gio_err_addr = = hp - > ndptr | | gio_err_addr = = hp - > cbp ) )
break ;
}
2008-07-16 20:25:40 +04:00
if ( i < sizeof ( hpc3 ) / sizeof ( struct hpc3_stat ) ) {
2007-12-02 15:00:32 +03:00
struct hpc3_stat * hp = ( struct hpc3_stat * ) & hpc3 + i ;
printk ( KERN_ERR " at DMA addresses: HPC3 @ %08lx: "
" ctl %08x, ndp %08x, cbp %08x \n " ,
CPHYSADDR ( hp - > addr ) , hp - > ctrl , hp - > ndptr , hp - > cbp ) ;
goto mips_be_fatal ;
}
/* Check MC's virtual DMA stuff. */
if ( check_vdma_memaddr ( ) ) {
printk ( KERN_ERR " at GIO DMA: mem address 0x%08x. \n " ,
sgimc - > maddronly ) ;
goto mips_be_fatal ;
}
if ( check_vdma_gioaddr ( ) ) {
printk ( KERN_ERR " at GIO DMA: gio address 0x%08x. \n " ,
sgimc - > gmaddronly ) ;
goto mips_be_fatal ;
}
/* A speculative bus error... */
if ( debug_be_interrupt ) {
print_buserr ( regs ) ;
printk ( KERN_ERR " discarded! \n " ) ;
}
return MIPS_BE_DISCARD ;
mips_be_fatal :
print_buserr ( regs ) ;
return MIPS_BE_FATAL ;
}
void ip22_be_interrupt ( int irq )
{
const struct pt_regs * regs = get_irq_regs ( ) ;
count_be_interrupt + + ;
if ( ip28_be_interrupt ( regs ) ! = MIPS_BE_DISCARD ) {
/* Assume it would be too dangerous to continue ... */
die_if_kernel ( " Oops " , regs ) ;
force_sig ( SIGBUS , current ) ;
} else if ( debug_be_interrupt )
show_regs ( ( struct pt_regs * ) regs ) ;
}
static int ip28_be_handler ( struct pt_regs * regs , int is_fixup )
{
/*
* We arrive here only in the unusual case of do_be ( ) invocation ,
* i . e . by a bus error exception without a bus error interrupt .
*/
if ( is_fixup ) {
count_be_is_fixup + + ;
save_and_clear_buserr ( ) ;
return MIPS_BE_FIXUP ;
}
count_be_handler + + ;
return ip28_be_interrupt ( regs ) ;
}
void __init ip22_be_init ( void )
{
board_be_handler = ip28_be_handler ;
}
int ip28_show_be_info ( struct seq_file * m )
{
seq_printf ( m , " IP28 be fixups \t \t : %u \n " , count_be_is_fixup ) ;
seq_printf ( m , " IP28 be interrupts \t : %u \n " , count_be_interrupt ) ;
seq_printf ( m , " IP28 be handler \t \t : %u \n " , count_be_handler ) ;
return 0 ;
}
static int __init debug_be_setup ( char * str )
{
debug_be_interrupt + + ;
return 1 ;
}
__setup ( " ip28_debug_be " , debug_be_setup ) ;