2012-10-17 02:39:09 +04:00
/*
* This file is subject to the terms and conditions of the GNU General Public
* License . See the file " COPYING " in the main directory of this archive
* for more details .
*
2012-11-16 01:58:59 +04:00
* Copyright ( C ) 2012 Cavium , Inc .
*
2012-10-17 02:39:09 +04:00
* Copyright ( C ) 2009 Wind River Systems ,
* written by Ralf Baechle < ralf @ linux - mips . org >
*/
# include <linux/module.h>
# include <linux/init.h>
# include <linux/slab.h>
# include <linux/interrupt.h>
# include <linux/io.h>
# include <linux/edac.h>
# include "edac_module.h"
# include <asm/octeon/cvmx.h>
# include <asm/mipsregs.h>
extern int register_co_cache_error_notifier ( struct notifier_block * nb ) ;
extern int unregister_co_cache_error_notifier ( struct notifier_block * nb ) ;
extern unsigned long long cache_err_dcache [ NR_CPUS ] ;
2012-11-16 01:58:59 +04:00
struct co_cache_error {
struct notifier_block notifier ;
struct edac_device_ctl_info * ed ;
} ;
2012-10-17 02:39:09 +04:00
2012-11-16 01:58:59 +04:00
/**
2012-10-17 02:39:09 +04:00
* EDAC CPU cache error callback
*
2012-11-16 01:58:59 +04:00
* @ event : non - zero if unrecoverable .
2012-10-17 02:39:09 +04:00
*/
static int co_cache_error_event ( struct notifier_block * this ,
unsigned long event , void * ptr )
{
2012-11-16 01:58:59 +04:00
struct co_cache_error * p = container_of ( this , struct co_cache_error ,
notifier ) ;
2012-10-17 02:39:09 +04:00
unsigned int core = cvmx_get_core_num ( ) ;
unsigned int cpu = smp_processor_id ( ) ;
2012-11-16 01:58:59 +04:00
u64 icache_err = read_octeon_c0_icacheerr ( ) ;
u64 dcache_err ;
if ( event ) {
dcache_err = cache_err_dcache [ core ] ;
cache_err_dcache [ core ] = 0 ;
} else {
dcache_err = read_octeon_c0_dcacheerr ( ) ;
}
2012-10-17 02:39:09 +04:00
if ( icache_err & 1 ) {
2012-11-16 01:58:59 +04:00
edac_device_printk ( p - > ed , KERN_ERR ,
" CacheErr (Icache):%llx, core %d/cpu %d, cp0_errorepc == %lx \n " ,
( unsigned long long ) icache_err , core , cpu ,
read_c0_errorepc ( ) ) ;
2012-10-17 02:39:09 +04:00
write_octeon_c0_icacheerr ( 0 ) ;
2012-11-16 01:58:59 +04:00
edac_device_handle_ce ( p - > ed , cpu , 1 , " icache " ) ;
2012-10-17 02:39:09 +04:00
}
2012-11-16 01:58:59 +04:00
if ( dcache_err & 1 ) {
edac_device_printk ( p - > ed , KERN_ERR ,
" CacheErr (Dcache):%llx, core %d/cpu %d, cp0_errorepc == %lx \n " ,
( unsigned long long ) dcache_err , core , cpu ,
read_c0_errorepc ( ) ) ;
if ( event )
edac_device_handle_ue ( p - > ed , cpu , 0 , " dcache " ) ;
else
edac_device_handle_ce ( p - > ed , cpu , 0 , " dcache " ) ;
/* Clear the error indication */
2015-07-01 13:38:52 +03:00
if ( OCTEON_IS_OCTEON2 ( ) )
2012-11-16 01:58:59 +04:00
write_octeon_c0_dcacheerr ( 1 ) ;
else
write_octeon_c0_dcacheerr ( 0 ) ;
2012-10-17 02:39:09 +04:00
}
2012-11-16 01:58:59 +04:00
return NOTIFY_STOP ;
2012-10-17 02:39:09 +04:00
}
2012-12-22 01:23:51 +04:00
static int co_cache_error_probe ( struct platform_device * pdev )
2012-10-17 02:39:09 +04:00
{
2012-11-16 01:58:59 +04:00
struct co_cache_error * p = devm_kzalloc ( & pdev - > dev , sizeof ( * p ) ,
GFP_KERNEL ) ;
if ( ! p )
return - ENOMEM ;
p - > notifier . notifier_call = co_cache_error_event ;
platform_set_drvdata ( pdev , p ) ;
p - > ed = edac_device_alloc_ctl_info ( 0 , " cpu " , num_possible_cpus ( ) ,
" cache " , 2 , 0 , NULL , 0 ,
edac_device_alloc_index ( ) ) ;
if ( ! p - > ed )
goto err ;
2012-10-17 02:39:09 +04:00
2012-11-16 01:58:59 +04:00
p - > ed - > dev = & pdev - > dev ;
2012-10-17 02:39:09 +04:00
2012-11-16 01:58:59 +04:00
p - > ed - > dev_name = dev_name ( & pdev - > dev ) ;
2012-10-17 02:39:09 +04:00
2012-11-16 01:58:59 +04:00
p - > ed - > mod_name = " octeon-cpu " ;
p - > ed - > ctl_name = " cache " ;
2012-10-17 02:39:09 +04:00
2012-11-16 01:58:59 +04:00
if ( edac_device_add_device ( p - > ed ) ) {
2012-10-17 02:39:09 +04:00
pr_err ( " %s: edac_device_add_device() failed \n " , __func__ ) ;
2012-11-16 01:58:59 +04:00
goto err1 ;
2012-10-17 02:39:09 +04:00
}
2012-11-16 01:58:59 +04:00
register_co_cache_error_notifier ( & p - > notifier ) ;
2012-10-17 02:39:09 +04:00
return 0 ;
2012-11-16 01:58:59 +04:00
err1 :
edac_device_free_ctl_info ( p - > ed ) ;
2012-10-17 02:39:09 +04:00
err :
2012-11-16 01:58:59 +04:00
return - ENXIO ;
2012-10-17 02:39:09 +04:00
}
static int co_cache_error_remove ( struct platform_device * pdev )
{
2012-11-16 01:58:59 +04:00
struct co_cache_error * p = platform_get_drvdata ( pdev ) ;
2012-10-17 02:39:09 +04:00
2012-11-16 01:58:59 +04:00
unregister_co_cache_error_notifier ( & p - > notifier ) ;
2012-10-17 02:39:09 +04:00
edac_device_del_device ( & pdev - > dev ) ;
2012-11-16 01:58:59 +04:00
edac_device_free_ctl_info ( p - > ed ) ;
2012-10-17 02:39:09 +04:00
return 0 ;
}
static struct platform_driver co_cache_error_driver = {
. probe = co_cache_error_probe ,
. remove = co_cache_error_remove ,
. driver = {
2012-11-16 01:58:59 +04:00
. name = " octeon_pc_edac " ,
2012-10-17 02:39:09 +04:00
}
} ;
2012-11-16 01:58:59 +04:00
module_platform_driver ( co_cache_error_driver ) ;
2012-10-17 02:39:09 +04:00
MODULE_LICENSE ( " GPL " ) ;
MODULE_AUTHOR ( " Ralf Baechle <ralf@linux-mips.org> " ) ;