2008-02-14 05:34:17 +03:00
/*
* Copyright 2006 - 2008 , IBM Corporation .
*
* This program is free software ; you can redistribute it and / or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation ; either version
* 2 of the License , or ( at your option ) any later version .
*/
# undef DEBUG
2006-06-19 22:33:16 +04:00
# include <linux/types.h>
# include <linux/kernel.h>
# include <linux/smp.h>
2007-04-23 23:35:47 +04:00
# include <linux/reboot.h>
2006-06-19 22:33:16 +04:00
# include <asm/reg.h>
# include <asm/io.h>
# include <asm/prom.h>
2008-06-12 13:14:36 +04:00
# include <asm/kexec.h>
2006-06-19 22:33:16 +04:00
# include <asm/machdep.h>
2007-04-23 23:35:47 +04:00
# include <asm/rtas.h>
2007-10-04 09:40:42 +04:00
# include <asm/cell-regs.h>
2006-06-19 22:33:16 +04:00
# include "ras.h"
static void dump_fir ( int cpu )
{
struct cbe_pmd_regs __iomem * pregs = cbe_get_cpu_pmd_regs ( cpu ) ;
struct cbe_iic_regs __iomem * iregs = cbe_get_cpu_iic_regs ( cpu ) ;
if ( pregs = = NULL )
return ;
/* Todo: do some nicer parsing of bits and based on them go down
* to other sub - units FIRs and not only IIC
*/
printk ( KERN_ERR " Global Checkstop FIR : 0x%016lx \n " ,
in_be64 ( & pregs - > checkstop_fir ) ) ;
printk ( KERN_ERR " Global Recoverable FIR : 0x%016lx \n " ,
in_be64 ( & pregs - > checkstop_fir ) ) ;
printk ( KERN_ERR " Global MachineCheck FIR : 0x%016lx \n " ,
in_be64 ( & pregs - > spec_att_mchk_fir ) ) ;
if ( iregs = = NULL )
return ;
printk ( KERN_ERR " IOC FIR : 0x%016lx \n " ,
in_be64 ( & iregs - > ioc_fir ) ) ;
}
void cbe_system_error_exception ( struct pt_regs * regs )
{
int cpu = smp_processor_id ( ) ;
printk ( KERN_ERR " System Error Interrupt on CPU %d ! \n " , cpu ) ;
dump_fir ( cpu ) ;
dump_stack ( ) ;
}
void cbe_maintenance_exception ( struct pt_regs * regs )
{
int cpu = smp_processor_id ( ) ;
/*
* Nothing implemented for the maintenance interrupt at this point
*/
printk ( KERN_ERR " Unhandled Maintenance interrupt on CPU %d ! \n " , cpu ) ;
dump_stack ( ) ;
}
void cbe_thermal_exception ( struct pt_regs * regs )
{
int cpu = smp_processor_id ( ) ;
/*
* Nothing implemented for the thermal interrupt at this point
*/
printk ( KERN_ERR " Unhandled Thermal interrupt on CPU %d ! \n " , cpu ) ;
dump_stack ( ) ;
}
static int cbe_machine_check_handler ( struct pt_regs * regs )
{
int cpu = smp_processor_id ( ) ;
printk ( KERN_ERR " Machine Check Interrupt on CPU %d ! \n " , cpu ) ;
dump_fir ( cpu ) ;
/* No recovery from this code now, lets continue */
return 0 ;
}
2007-04-23 23:35:47 +04:00
struct ptcal_area {
struct list_head list ;
int nid ;
int order ;
struct page * pages ;
} ;
static LIST_HEAD ( ptcal_list ) ;
static int ptcal_start_tok , ptcal_stop_tok ;
static int __init cbe_ptcal_enable_on_node ( int nid , int order )
{
struct ptcal_area * area ;
int ret = - ENOMEM ;
unsigned long addr ;
# ifdef CONFIG_CRASH_DUMP
rtas_call ( ptcal_stop_tok , 1 , 1 , NULL , nid ) ;
# endif
area = kmalloc ( sizeof ( * area ) , GFP_KERNEL ) ;
if ( ! area )
goto out_err ;
area - > nid = nid ;
area - > order = order ;
area - > pages = alloc_pages_node ( area - > nid , GFP_KERNEL , area - > order ) ;
if ( ! area - > pages )
goto out_free_area ;
addr = __pa ( page_address ( area - > pages ) ) ;
ret = - EIO ;
if ( rtas_call ( ptcal_start_tok , 3 , 1 , NULL , area - > nid ,
( unsigned int ) ( addr > > 32 ) ,
( unsigned int ) ( addr & 0xffffffff ) ) ) {
printk ( KERN_ERR " %s: error enabling PTCAL on node %d! \n " ,
2008-03-29 00:21:07 +03:00
__func__ , nid ) ;
2007-04-23 23:35:47 +04:00
goto out_free_pages ;
}
list_add ( & area - > list , & ptcal_list ) ;
return 0 ;
out_free_pages :
__free_pages ( area - > pages , area - > order ) ;
out_free_area :
kfree ( area ) ;
out_err :
return ret ;
}
static int __init cbe_ptcal_enable ( void )
{
const u32 * size ;
struct device_node * np ;
int order , found_mic = 0 ;
np = of_find_node_by_path ( " /rtas " ) ;
if ( ! np )
return - ENODEV ;
2007-04-29 10:29:08 +04:00
size = of_get_property ( np , " ibm,cbe-ptcal-size " , NULL ) ;
2007-04-23 23:35:47 +04:00
if ( ! size )
return - ENODEV ;
2008-03-29 00:21:07 +03:00
pr_debug ( " %s: enabling PTCAL, size = 0x%x \n " , __func__ , * size ) ;
2007-04-23 23:35:47 +04:00
order = get_order ( * size ) ;
of_node_put ( np ) ;
/* support for malta device trees, with be@/mic@ nodes */
for_each_node_by_type ( np , " mic-tm " ) {
cbe_ptcal_enable_on_node ( of_node_to_nid ( np ) , order ) ;
found_mic = 1 ;
}
if ( found_mic )
return 0 ;
/* support for older device tree - use cpu nodes */
for_each_node_by_type ( np , " cpu " ) {
2007-04-29 10:29:08 +04:00
const u32 * nid = of_get_property ( np , " node-id " , NULL ) ;
2007-04-23 23:35:47 +04:00
if ( ! nid ) {
printk ( KERN_ERR " %s: node %s is missing node-id? \n " ,
2008-03-29 00:21:07 +03:00
__func__ , np - > full_name ) ;
2007-04-23 23:35:47 +04:00
continue ;
}
cbe_ptcal_enable_on_node ( * nid , order ) ;
found_mic = 1 ;
}
return found_mic ? 0 : - ENODEV ;
}
static int cbe_ptcal_disable ( void )
{
struct ptcal_area * area , * tmp ;
int ret = 0 ;
2008-03-29 00:21:07 +03:00
pr_debug ( " %s: disabling PTCAL \n " , __func__ ) ;
2007-04-23 23:35:47 +04:00
list_for_each_entry_safe ( area , tmp , & ptcal_list , list ) {
/* disable ptcal on this node */
if ( rtas_call ( ptcal_stop_tok , 1 , 1 , NULL , area - > nid ) ) {
printk ( KERN_ERR " %s: error disabling PTCAL "
2008-03-29 00:21:07 +03:00
" on node %d! \n " , __func__ ,
2007-04-23 23:35:47 +04:00
area - > nid ) ;
ret = - EIO ;
continue ;
}
/* ensure we can access the PTCAL area */
memset ( page_address ( area - > pages ) , 0 ,
1 < < ( area - > order + PAGE_SHIFT ) ) ;
/* clean up */
list_del ( & area - > list ) ;
__free_pages ( area - > pages , area - > order ) ;
kfree ( area ) ;
}
return ret ;
}
static int cbe_ptcal_notify_reboot ( struct notifier_block * nb ,
unsigned long code , void * data )
{
return cbe_ptcal_disable ( ) ;
}
2008-06-12 13:14:36 +04:00
static void cbe_ptcal_crash_shutdown ( void )
{
cbe_ptcal_disable ( ) ;
}
2007-04-23 23:35:47 +04:00
static struct notifier_block cbe_ptcal_reboot_notifier = {
. notifier_call = cbe_ptcal_notify_reboot
} ;
int __init cbe_ptcal_init ( void )
{
int ret ;
ptcal_start_tok = rtas_token ( " ibm,cbe-start-ptcal " ) ;
ptcal_stop_tok = rtas_token ( " ibm,cbe-stop-ptcal " ) ;
if ( ptcal_start_tok = = RTAS_UNKNOWN_SERVICE
| | ptcal_stop_tok = = RTAS_UNKNOWN_SERVICE )
return - ENODEV ;
ret = register_reboot_notifier ( & cbe_ptcal_reboot_notifier ) ;
2008-06-12 13:14:36 +04:00
if ( ret )
goto out1 ;
ret = crash_shutdown_register ( & cbe_ptcal_crash_shutdown ) ;
if ( ret )
goto out2 ;
2007-04-23 23:35:47 +04:00
return cbe_ptcal_enable ( ) ;
2008-06-12 13:14:36 +04:00
out2 :
unregister_reboot_notifier ( & cbe_ptcal_reboot_notifier ) ;
out1 :
printk ( KERN_ERR " Can't disable PTCAL, so not enabling \n " ) ;
return ret ;
2007-04-23 23:35:47 +04:00
}
arch_initcall ( cbe_ptcal_init ) ;
2006-06-19 22:33:16 +04:00
void __init cbe_ras_init ( void )
{
unsigned long hid0 ;
/*
* Enable System Error & thermal interrupts and wakeup conditions
*/
hid0 = mfspr ( SPRN_HID0 ) ;
hid0 | = HID0_CBE_THERM_INT_EN | HID0_CBE_THERM_WAKEUP |
HID0_CBE_SYSERR_INT_EN | HID0_CBE_SYSERR_WAKEUP ;
mtspr ( SPRN_HID0 , hid0 ) ;
mb ( ) ;
/*
* Install machine check handler . Leave setting of precise mode to
* what the firmware did for now
*/
ppc_md . machine_check_exception = cbe_machine_check_handler ;
mb ( ) ;
/*
* For now , we assume that IOC_FIR is already set to forward some
* error conditions to the System Error handler . If that is not true
* then it will have to be fixed up here .
*/
}