2019-05-27 08:55:01 +02:00
// SPDX-License-Identifier: GPL-2.0-or-later
2005-04-16 15:20:36 -07:00
/*
* Bus error event handling code for systems equipped with ECC
* handling logic , i . e . DECstation / DECsystem 5000 / 200 ( KN02 ) ,
* 5000 / 240 ( KN03 ) , 5000 / 260 ( KN05 ) and DECsystem 5900 ( KN03 ) ,
* 5900 / 260 ( KN05 ) systems .
*
2005-06-22 20:56:26 +00:00
* Copyright ( c ) 2003 , 2005 Maciej W . Rozycki
2005-04-16 15:20:36 -07:00
*/
# include <linux/init.h>
2005-06-22 20:56:26 +00:00
# include <linux/interrupt.h>
2005-04-16 15:20:36 -07:00
# include <linux/kernel.h>
# include <linux/sched.h>
# include <linux/types.h>
# include <asm/addrspace.h>
# include <asm/bootinfo.h>
# include <asm/cpu.h>
2014-03-31 23:51:30 +01:00
# include <asm/cpu-type.h>
2006-10-09 00:00:31 +01:00
# include <asm/irq_regs.h>
2005-04-16 15:20:36 -07:00
# include <asm/processor.h>
2006-12-06 11:50:23 +00:00
# include <asm/ptrace.h>
2005-04-16 15:20:36 -07:00
# include <asm/traps.h>
# include <asm/dec/ecc.h>
# include <asm/dec/kn02.h>
# include <asm/dec/kn03.h>
# include <asm/dec/kn05.h>
static volatile u32 * kn0x_erraddr ;
static volatile u32 * kn0x_chksyn ;
static inline void dec_ecc_be_ack ( void )
{
* kn0x_erraddr = 0 ; /* any write clears the IRQ */
iob ( ) ;
}
static int dec_ecc_be_backend ( struct pt_regs * regs , int is_fixup , int invoker )
{
static const char excstr [ ] = " exception " ;
static const char intstr [ ] = " interrupt " ;
static const char cpustr [ ] = " CPU " ;
static const char dmastr [ ] = " DMA " ;
static const char readstr [ ] = " read " ;
static const char mreadstr [ ] = " memory read " ;
static const char writestr [ ] = " write " ;
static const char mwritstr [ ] = " partial memory write " ;
static const char timestr [ ] = " timeout " ;
static const char overstr [ ] = " overrun " ;
static const char eccstr [ ] = " ECC error " ;
const char * kind , * agent , * cycle , * event ;
const char * status = " " , * xbit = " " , * fmt = " " ;
2005-06-22 20:56:26 +00:00
unsigned long address ;
2005-04-16 15:20:36 -07:00
u16 syn = 0 , sngl ;
int i = 0 ;
u32 erraddr = * kn0x_erraddr ;
u32 chksyn = * kn0x_chksyn ;
int action = MIPS_BE_FATAL ;
2005-06-22 20:56:26 +00:00
/* For non-ECC ack ASAP, so that any subsequent errors get caught. */
2005-04-16 15:20:36 -07:00
if ( ( erraddr & ( KN0X_EAR_VALID | KN0X_EAR_ECCERR ) ) = = KN0X_EAR_VALID )
dec_ecc_be_ack ( ) ;
kind = invoker ? intstr : excstr ;
if ( ! ( erraddr & KN0X_EAR_VALID ) ) {
/* No idea what happened. */
2005-06-22 20:56:26 +00:00
printk ( KERN_ALERT " Unidentified bus error %s \n " , kind ) ;
2005-04-16 15:20:36 -07:00
return action ;
}
agent = ( erraddr & KN0X_EAR_CPU ) ? cpustr : dmastr ;
if ( erraddr & KN0X_EAR_ECCERR ) {
/* An ECC error on a CPU or DMA transaction. */
cycle = ( erraddr & KN0X_EAR_WRITE ) ? mwritstr : mreadstr ;
event = eccstr ;
} else {
/* A CPU timeout or a DMA overrun. */
cycle = ( erraddr & KN0X_EAR_WRITE ) ? writestr : readstr ;
event = ( erraddr & KN0X_EAR_CPU ) ? timestr : overstr ;
}
address = erraddr & KN0X_EAR_ADDRESS ;
/* For ECC errors on reads adjust for MT pipelining. */
if ( ( erraddr & ( KN0X_EAR_WRITE | KN0X_EAR_ECCERR ) ) = = KN0X_EAR_ECCERR )
address = ( address & ~ 0xfffLL ) | ( ( address - 5 ) & 0xfffLL ) ;
address < < = 2 ;
/* Only CPU errors are fixable. */
if ( erraddr & KN0X_EAR_CPU & & is_fixup )
action = MIPS_BE_FIXUP ;
if ( erraddr & KN0X_EAR_ECCERR ) {
static const u8 data_sbit [ 32 ] = {
0x4f , 0x4a , 0x52 , 0x54 , 0x57 , 0x58 , 0x5b , 0x5d ,
0x23 , 0x25 , 0x26 , 0x29 , 0x2a , 0x2c , 0x31 , 0x34 ,
0x0e , 0x0b , 0x13 , 0x15 , 0x16 , 0x19 , 0x1a , 0x1c ,
0x62 , 0x64 , 0x67 , 0x68 , 0x6b , 0x6d , 0x70 , 0x75 ,
} ;
static const u8 data_mbit [ 25 ] = {
0x07 , 0x0d , 0x1f ,
0x2f , 0x32 , 0x37 , 0x38 , 0x3b , 0x3d , 0x3e ,
0x43 , 0x45 , 0x46 , 0x49 , 0x4c , 0x51 , 0x5e ,
0x61 , 0x6e , 0x73 , 0x76 , 0x79 , 0x7a , 0x7c , 0x7f ,
} ;
static const char sbestr [ ] = " corrected single " ;
static const char dbestr [ ] = " uncorrectable double " ;
static const char mbestr [ ] = " uncorrectable multiple " ;
if ( ! ( address & 0x4 ) )
syn = chksyn ; /* Low bank. */
else
syn = chksyn > > 16 ; /* High bank. */
if ( ! ( syn & KN0X_ESR_VLDLO ) ) {
/* Ack now, no rewrite will happen. */
dec_ecc_be_ack ( ) ;
2005-06-22 20:56:26 +00:00
fmt = KERN_ALERT " %s " " invalid \n " ;
2005-04-16 15:20:36 -07:00
} else {
sngl = syn & KN0X_ESR_SNGLO ;
syn & = KN0X_ESR_SYNLO ;
/*
* Multibit errors may be tagged incorrectly ;
* check the syndrome explicitly .
*/
for ( i = 0 ; i < 25 ; i + + )
if ( syn = = data_mbit [ i ] )
break ;
if ( i < 25 ) {
status = mbestr ;
} else if ( ! sngl ) {
status = dbestr ;
} else {
2005-06-16 20:30:54 +00:00
volatile u32 * ptr =
( void * ) CKSEG1ADDR ( address ) ;
2005-04-16 15:20:36 -07:00
* ptr = * ptr ; /* Rewrite. */
iob ( ) ;
status = sbestr ;
action = MIPS_BE_DISCARD ;
}
/* Ack now, now we've rewritten (or not). */
dec_ecc_be_ack ( ) ;
if ( syn & & syn = = ( syn & - syn ) ) {
if ( syn = = 0x01 ) {
fmt = KERN_ALERT " %s "
" %#04x -- %s bit error "
2005-06-22 20:56:26 +00:00
" at check bit C%s \n " ;
2005-04-16 15:20:36 -07:00
xbit = " X " ;
} else {
fmt = KERN_ALERT " %s "
" %#04x -- %s bit error "
2005-06-22 20:56:26 +00:00
" at check bit C%s%u \n " ;
2005-04-16 15:20:36 -07:00
}
i = syn > > 2 ;
} else {
for ( i = 0 ; i < 32 ; i + + )
if ( syn = = data_sbit [ i ] )
break ;
if ( i < 32 )
fmt = KERN_ALERT " %s "
" %#04x -- %s bit error "
2005-06-22 20:56:26 +00:00
" at data bit D%s%u \n " ;
2005-04-16 15:20:36 -07:00
else
fmt = KERN_ALERT " %s "
2005-06-22 20:56:26 +00:00
" %#04x -- %s bit error \n " ;
2005-04-16 15:20:36 -07:00
}
}
}
if ( action ! = MIPS_BE_FIXUP )
2005-06-22 20:56:26 +00:00
printk ( KERN_ALERT " Bus error %s: %s %s %s at %#010lx \n " ,
2005-04-16 15:20:36 -07:00
kind , agent , cycle , event , address ) ;
if ( action ! = MIPS_BE_FIXUP & & erraddr & KN0X_EAR_ECCERR )
printk ( fmt , " ECC syndrome " , syn , status , xbit , i ) ;
return action ;
}
int dec_ecc_be_handler ( struct pt_regs * regs , int is_fixup )
{
return dec_ecc_be_backend ( regs , is_fixup , 0 ) ;
}
2006-10-09 00:00:31 +01:00
irqreturn_t dec_ecc_be_interrupt ( int irq , void * dev_id )
2005-04-16 15:20:36 -07:00
{
2006-10-09 00:00:31 +01:00
struct pt_regs * regs = get_irq_regs ( ) ;
2005-04-16 15:20:36 -07:00
int action = dec_ecc_be_backend ( regs , 0 , 1 ) ;
if ( action = = MIPS_BE_DISCARD )
2005-06-13 19:53:38 +00:00
return IRQ_HANDLED ;
2005-04-16 15:20:36 -07:00
/*
2005-06-22 20:56:26 +00:00
* FIXME : Find the affected processes and kill them , otherwise
* we must die .
2005-04-16 15:20:36 -07:00
*
* The interrupt is asynchronously delivered thus EPC and RA
* may be irrelevant , but are printed for a reference .
*/
printk ( KERN_ALERT " Fatal bus interrupt, epc == %08lx, ra == %08lx \n " ,
regs - > cp0_epc , regs - > regs [ 31 ] ) ;
die ( " Unrecoverable bus error " , regs ) ;
}
/*
* Initialization differs a bit between KN02 and KN03 / KN05 , so we
* need two variants . Once set up , all systems can be handled the
* same way .
*/
static inline void dec_kn02_be_init ( void )
{
2005-07-01 16:10:40 +00:00
volatile u32 * csr = ( void * ) CKSEG1ADDR ( KN02_SLOT_BASE + KN02_CSR ) ;
2005-04-16 15:20:36 -07:00
2005-07-01 16:10:40 +00:00
kn0x_erraddr = ( void * ) CKSEG1ADDR ( KN02_SLOT_BASE + KN02_ERRADDR ) ;
kn0x_chksyn = ( void * ) CKSEG1ADDR ( KN02_SLOT_BASE + KN02_CHKSYN ) ;
2005-04-16 15:20:36 -07:00
/* Preset write-only bits of the Control Register cache. */
2005-06-22 20:56:26 +00:00
cached_kn02_csr = * csr | KN02_CSR_LEDS ;
2005-04-16 15:20:36 -07:00
/* Set normal ECC detection and generation. */
cached_kn02_csr & = ~ ( KN02_CSR_DIAGCHK | KN02_CSR_DIAGGEN ) ;
/* Enable ECC correction. */
cached_kn02_csr | = KN02_CSR_CORRECT ;
* csr = cached_kn02_csr ;
iob ( ) ;
}
static inline void dec_kn03_be_init ( void )
{
2005-07-01 16:10:40 +00:00
volatile u32 * mcr = ( void * ) CKSEG1ADDR ( KN03_SLOT_BASE + IOASIC_MCR ) ;
volatile u32 * mbcs = ( void * ) CKSEG1ADDR ( KN4K_SLOT_BASE + KN4K_MB_CSR ) ;
2005-04-16 15:20:36 -07:00
2005-07-01 16:10:40 +00:00
kn0x_erraddr = ( void * ) CKSEG1ADDR ( KN03_SLOT_BASE + IOASIC_ERRADDR ) ;
kn0x_chksyn = ( void * ) CKSEG1ADDR ( KN03_SLOT_BASE + IOASIC_CHKSYN ) ;
2005-09-03 15:56:17 -07:00
2005-04-16 15:20:36 -07:00
/*
* Set normal ECC detection and generation , enable ECC correction .
* For KN05 we also need to make sure EE ( ? ) is enabled in the MB .
* Otherwise DBE / IBE exceptions would be masked but bus error
* interrupts would still arrive , resulting in an inevitable crash
* if get_dbe ( ) triggers one .
*/
* mcr = ( * mcr & ~ ( KN03_MCR_DIAGCHK | KN03_MCR_DIAGGEN ) ) |
KN03_MCR_CORRECT ;
2007-10-11 23:46:15 +01:00
if ( current_cpu_type ( ) = = CPU_R4400SC )
2005-06-22 20:56:26 +00:00
* mbcs | = KN4K_MB_CSR_EE ;
2005-04-16 15:20:36 -07:00
fast_iob ( ) ;
}
void __init dec_ecc_be_init ( void )
{
if ( mips_machtype = = MACH_DS5000_200 )
dec_kn02_be_init ( ) ;
else
dec_kn03_be_init ( ) ;
/* Clear any leftover errors from the firmware. */
dec_ecc_be_ack ( ) ;
}