2012-10-17 00:39:09 +02: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 .
*
* 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/io.h>
# include <linux/edac.h>
2012-11-15 13:58:59 -08:00
# include <asm/octeon/octeon.h>
# include <asm/octeon/cvmx-lmcx-defs.h>
2012-10-17 00:39:09 +02:00
# include "edac_core.h"
# include "edac_module.h"
2012-11-15 13:58:59 -08:00
# define OCTEON_MAX_MC 4
2012-10-17 00:39:09 +02:00
2012-11-15 13:58:59 -08:00
static void octeon_lmc_edac_poll ( struct mem_ctl_info * mci )
2012-10-17 00:39:09 +02:00
{
2012-11-15 13:58:59 -08:00
union cvmx_lmcx_mem_cfg0 cfg0 ;
bool do_clear = false ;
2012-10-17 00:39:09 +02:00
char msg [ 64 ] ;
2012-11-15 13:58:59 -08:00
cfg0 . u64 = cvmx_read_csr ( CVMX_LMCX_MEM_CFG0 ( mci - > mc_idx ) ) ;
if ( cfg0 . s . sec_err | | cfg0 . s . ded_err ) {
union cvmx_lmcx_fadr fadr ;
fadr . u64 = cvmx_read_csr ( CVMX_LMCX_FADR ( mci - > mc_idx ) ) ;
snprintf ( msg , sizeof ( msg ) ,
" DIMM %d rank %d bank %d row %d col %d " ,
fadr . cn30xx . fdimm , fadr . cn30xx . fbunk ,
fadr . cn30xx . fbank , fadr . cn30xx . frow , fadr . cn30xx . fcol ) ;
2012-10-17 00:39:09 +02:00
}
2012-11-15 13:58:59 -08:00
if ( cfg0 . s . sec_err ) {
edac_mc_handle_error ( HW_EVENT_ERR_CORRECTED , mci , 1 , 0 , 0 , 0 ,
- 1 , - 1 , - 1 , msg , " " ) ;
cfg0 . s . sec_err = - 1 ; /* Done, re-arm */
do_clear = true ;
2012-10-17 00:39:09 +02:00
}
2012-11-15 13:58:59 -08:00
if ( cfg0 . s . ded_err ) {
edac_mc_handle_error ( HW_EVENT_ERR_UNCORRECTED , mci , 1 , 0 , 0 , 0 ,
- 1 , - 1 , - 1 , msg , " " ) ;
cfg0 . s . ded_err = - 1 ; /* Done, re-arm */
do_clear = true ;
}
if ( do_clear )
cvmx_write_csr ( CVMX_LMCX_MEM_CFG0 ( mci - > mc_idx ) , cfg0 . u64 ) ;
2012-10-17 00:39:09 +02:00
}
2012-11-15 13:58:59 -08:00
static void octeon_lmc_edac_poll_o2 ( struct mem_ctl_info * mci )
2012-10-17 00:39:09 +02:00
{
2012-11-15 13:58:59 -08:00
union cvmx_lmcx_int int_reg ;
bool do_clear = false ;
char msg [ 64 ] ;
2012-10-17 00:39:09 +02:00
2012-11-15 13:58:59 -08:00
int_reg . u64 = cvmx_read_csr ( CVMX_LMCX_INT ( mci - > mc_idx ) ) ;
if ( int_reg . s . sec_err | | int_reg . s . ded_err ) {
union cvmx_lmcx_fadr fadr ;
fadr . u64 = cvmx_read_csr ( CVMX_LMCX_FADR ( mci - > mc_idx ) ) ;
snprintf ( msg , sizeof ( msg ) ,
" DIMM %d rank %d bank %d row %d col %d " ,
fadr . cn61xx . fdimm , fadr . cn61xx . fbunk ,
fadr . cn61xx . fbank , fadr . cn61xx . frow , fadr . cn61xx . fcol ) ;
}
2012-10-17 00:39:09 +02:00
2012-11-15 13:58:59 -08:00
if ( int_reg . s . sec_err ) {
edac_mc_handle_error ( HW_EVENT_ERR_CORRECTED , mci , 1 , 0 , 0 , 0 ,
- 1 , - 1 , - 1 , msg , " " ) ;
int_reg . s . sec_err = - 1 ; /* Done, re-arm */
do_clear = true ;
2012-10-17 00:39:09 +02:00
}
2012-11-15 13:58:59 -08:00
if ( int_reg . s . ded_err ) {
edac_mc_handle_error ( HW_EVENT_ERR_UNCORRECTED , mci , 1 , 0 , 0 , 0 ,
- 1 , - 1 , - 1 , msg , " " ) ;
int_reg . s . ded_err = - 1 ; /* Done, re-arm */
do_clear = true ;
}
if ( do_clear )
cvmx_write_csr ( CVMX_LMCX_INT ( mci - > mc_idx ) , int_reg . u64 ) ;
}
2012-10-17 00:39:09 +02:00
2012-12-21 13:23:51 -08:00
static int octeon_lmc_edac_probe ( struct platform_device * pdev )
2012-11-15 13:58:59 -08:00
{
struct mem_ctl_info * mci ;
struct edac_mc_layer layers [ 1 ] ;
int mc = pdev - > id ;
layers [ 0 ] . type = EDAC_MC_LAYER_CHANNEL ;
layers [ 0 ] . size = 1 ;
layers [ 0 ] . is_virt_csrow = false ;
if ( OCTEON_IS_MODEL ( OCTEON_FAM_1_PLUS ) ) {
union cvmx_lmcx_mem_cfg0 cfg0 ;
cfg0 . u64 = cvmx_read_csr ( CVMX_LMCX_MEM_CFG0 ( 0 ) ) ;
if ( ! cfg0 . s . ecc_ena ) {
dev_info ( & pdev - > dev , " Disabled (ECC not enabled) \n " ) ;
return 0 ;
}
mci = edac_mc_alloc ( mc , ARRAY_SIZE ( layers ) , layers , 0 ) ;
if ( ! mci )
return - ENXIO ;
mci - > pdev = & pdev - > dev ;
mci - > dev_name = dev_name ( & pdev - > dev ) ;
mci - > mod_name = " octeon-lmc " ;
mci - > ctl_name = " octeon-lmc-err " ;
mci - > edac_check = octeon_lmc_edac_poll ;
if ( edac_mc_add_mc ( mci ) ) {
dev_err ( & pdev - > dev , " edac_mc_add_mc() failed \n " ) ;
edac_mc_free ( mci ) ;
return - ENXIO ;
}
cfg0 . u64 = cvmx_read_csr ( CVMX_LMCX_MEM_CFG0 ( mc ) ) ;
cfg0 . s . intr_ded_ena = 0 ; /* We poll */
cfg0 . s . intr_sec_ena = 0 ;
cvmx_write_csr ( CVMX_LMCX_MEM_CFG0 ( mc ) , cfg0 . u64 ) ;
} else {
/* OCTEON II */
union cvmx_lmcx_int_en en ;
union cvmx_lmcx_config config ;
config . u64 = cvmx_read_csr ( CVMX_LMCX_CONFIG ( 0 ) ) ;
if ( ! config . s . ecc_ena ) {
dev_info ( & pdev - > dev , " Disabled (ECC not enabled) \n " ) ;
return 0 ;
}
mci = edac_mc_alloc ( mc , ARRAY_SIZE ( layers ) , layers , 0 ) ;
if ( ! mci )
return - ENXIO ;
mci - > pdev = & pdev - > dev ;
mci - > dev_name = dev_name ( & pdev - > dev ) ;
mci - > mod_name = " octeon-lmc " ;
mci - > ctl_name = " co_lmc_err " ;
mci - > edac_check = octeon_lmc_edac_poll_o2 ;
if ( edac_mc_add_mc ( mci ) ) {
dev_err ( & pdev - > dev , " edac_mc_add_mc() failed \n " ) ;
edac_mc_free ( mci ) ;
return - ENXIO ;
}
en . u64 = cvmx_read_csr ( CVMX_LMCX_MEM_CFG0 ( mc ) ) ;
en . s . intr_ded_ena = 0 ; /* We poll */
en . s . intr_sec_ena = 0 ;
cvmx_write_csr ( CVMX_LMCX_MEM_CFG0 ( mc ) , en . u64 ) ;
}
platform_set_drvdata ( pdev , mci ) ;
2012-10-17 00:39:09 +02:00
return 0 ;
}
2012-11-15 13:58:59 -08:00
static int octeon_lmc_edac_remove ( struct platform_device * pdev )
2012-10-17 00:39:09 +02:00
{
struct mem_ctl_info * mci = platform_get_drvdata ( pdev ) ;
edac_mc_del_mc ( & pdev - > dev ) ;
edac_mc_free ( mci ) ;
return 0 ;
}
2012-11-15 13:58:59 -08:00
static struct platform_driver octeon_lmc_edac_driver = {
. probe = octeon_lmc_edac_probe ,
. remove = octeon_lmc_edac_remove ,
2012-10-17 00:39:09 +02:00
. driver = {
2012-11-15 13:58:59 -08:00
. name = " octeon_lmc_edac " ,
2012-10-17 00:39:09 +02:00
}
} ;
2012-11-15 13:58:59 -08:00
module_platform_driver ( octeon_lmc_edac_driver ) ;
2012-10-17 00:39:09 +02:00
MODULE_LICENSE ( " GPL " ) ;
MODULE_AUTHOR ( " Ralf Baechle <ralf@linux-mips.org> " ) ;