2015-01-06 20:43:47 +03:00
/*
* Synopsys DDR ECC Driver
* This driver is based on ppc4xx_edac . c drivers
*
* Copyright ( C ) 2012 - 2014 Xilinx , Inc .
*
* 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 .
*
* This program is distributed in the hope that it will be useful ,
* but WITHOUT ANY WARRANTY ; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
* GNU General Public License for more details .
*
* 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
*/
# include <linux/edac.h>
# include <linux/module.h>
# include <linux/platform_device.h>
2016-10-29 20:16:34 +03:00
# include "edac_module.h"
2015-01-06 20:43:47 +03:00
/* Number of cs_rows needed per memory controller */
# define SYNPS_EDAC_NR_CSROWS 1
/* Number of channels per memory controller */
# define SYNPS_EDAC_NR_CHANS 1
/* Granularity of reported error in bytes */
# define SYNPS_EDAC_ERR_GRAIN 1
# define SYNPS_EDAC_MSG_SIZE 256
# define SYNPS_EDAC_MOD_STRING "synps_edac"
# define SYNPS_EDAC_MOD_VER "1"
/* Synopsys DDR memory controller registers that are relevant to ECC */
# define CTRL_OFST 0x0
# define T_ZQ_OFST 0xA4
/* ECC control register */
# define ECC_CTRL_OFST 0xC4
/* ECC log register */
# define CE_LOG_OFST 0xC8
/* ECC address register */
# define CE_ADDR_OFST 0xCC
/* ECC data[31:0] register */
# define CE_DATA_31_0_OFST 0xD0
/* Uncorrectable error info registers */
# define UE_LOG_OFST 0xDC
# define UE_ADDR_OFST 0xE0
# define UE_DATA_31_0_OFST 0xE4
# define STAT_OFST 0xF0
# define SCRUB_OFST 0xF4
/* Control register bit field definitions */
# define CTRL_BW_MASK 0xC
# define CTRL_BW_SHIFT 2
# define DDRCTL_WDTH_16 1
# define DDRCTL_WDTH_32 0
/* ZQ register bit field definitions */
# define T_ZQ_DDRMODE_MASK 0x2
/* ECC control register bit field definitions */
# define ECC_CTRL_CLR_CE_ERR 0x2
# define ECC_CTRL_CLR_UE_ERR 0x1
/* ECC correctable/uncorrectable error log register definitions */
# define LOG_VALID 0x1
# define CE_LOG_BITPOS_MASK 0xFE
# define CE_LOG_BITPOS_SHIFT 1
/* ECC correctable/uncorrectable error address register definitions */
# define ADDR_COL_MASK 0xFFF
# define ADDR_ROW_MASK 0xFFFF000
# define ADDR_ROW_SHIFT 12
# define ADDR_BANK_MASK 0x70000000
# define ADDR_BANK_SHIFT 28
/* ECC statistic register definitions */
# define STAT_UECNT_MASK 0xFF
# define STAT_CECNT_MASK 0xFF00
# define STAT_CECNT_SHIFT 8
/* ECC scrub register definitions */
# define SCRUB_MODE_MASK 0x7
# define SCRUB_MODE_SECDED 0x4
/**
* struct ecc_error_info - ECC error log information
* @ row : Row number
* @ col : Column number
* @ bank : Bank number
* @ bitpos : Bit position
* @ data : Data causing the error
*/
struct ecc_error_info {
u32 row ;
u32 col ;
u32 bank ;
u32 bitpos ;
u32 data ;
} ;
/**
* struct synps_ecc_status - ECC status information to report
* @ ce_cnt : Correctable error count
* @ ue_cnt : Uncorrectable error count
* @ ceinfo : Correctable error log information
* @ ueinfo : Uncorrectable error log information
*/
struct synps_ecc_status {
u32 ce_cnt ;
u32 ue_cnt ;
struct ecc_error_info ceinfo ;
struct ecc_error_info ueinfo ;
} ;
/**
* struct synps_edac_priv - DDR memory controller private instance data
* @ baseaddr : Base address of the DDR controller
* @ message : Buffer for framing the event specific info
* @ stat : ECC status information
* @ ce_cnt : Correctable Error count
* @ ue_cnt : Uncorrectable Error count
*/
struct synps_edac_priv {
void __iomem * baseaddr ;
char message [ SYNPS_EDAC_MSG_SIZE ] ;
struct synps_ecc_status stat ;
u32 ce_cnt ;
u32 ue_cnt ;
} ;
/**
* synps_edac_geterror_info - Get the current ecc error info
* @ base : Pointer to the base address of the ddr memory controller
* @ p : Pointer to the synopsys ecc status structure
*
* Determines there is any ecc error or not
*
* Return : one if there is no error otherwise returns zero
*/
static int synps_edac_geterror_info ( void __iomem * base ,
struct synps_ecc_status * p )
{
u32 regval , clearval = 0 ;
regval = readl ( base + STAT_OFST ) ;
if ( ! regval )
return 1 ;
p - > ce_cnt = ( regval & STAT_CECNT_MASK ) > > STAT_CECNT_SHIFT ;
p - > ue_cnt = regval & STAT_UECNT_MASK ;
regval = readl ( base + CE_LOG_OFST ) ;
if ( ! ( p - > ce_cnt & & ( regval & LOG_VALID ) ) )
goto ue_err ;
p - > ceinfo . bitpos = ( regval & CE_LOG_BITPOS_MASK ) > > CE_LOG_BITPOS_SHIFT ;
regval = readl ( base + CE_ADDR_OFST ) ;
p - > ceinfo . row = ( regval & ADDR_ROW_MASK ) > > ADDR_ROW_SHIFT ;
p - > ceinfo . col = regval & ADDR_COL_MASK ;
p - > ceinfo . bank = ( regval & ADDR_BANK_MASK ) > > ADDR_BANK_SHIFT ;
p - > ceinfo . data = readl ( base + CE_DATA_31_0_OFST ) ;
edac_dbg ( 3 , " ce bit position: %d data: %d \n " , p - > ceinfo . bitpos ,
p - > ceinfo . data ) ;
clearval = ECC_CTRL_CLR_CE_ERR ;
ue_err :
regval = readl ( base + UE_LOG_OFST ) ;
if ( ! ( p - > ue_cnt & & ( regval & LOG_VALID ) ) )
goto out ;
regval = readl ( base + UE_ADDR_OFST ) ;
p - > ueinfo . row = ( regval & ADDR_ROW_MASK ) > > ADDR_ROW_SHIFT ;
p - > ueinfo . col = regval & ADDR_COL_MASK ;
p - > ueinfo . bank = ( regval & ADDR_BANK_MASK ) > > ADDR_BANK_SHIFT ;
p - > ueinfo . data = readl ( base + UE_DATA_31_0_OFST ) ;
clearval | = ECC_CTRL_CLR_UE_ERR ;
out :
writel ( clearval , base + ECC_CTRL_OFST ) ;
writel ( 0x0 , base + ECC_CTRL_OFST ) ;
return 0 ;
}
/**
* synps_edac_handle_error - Handle controller error types CE and UE
* @ mci : Pointer to the edac memory controller instance
* @ p : Pointer to the synopsys ecc status structure
*
* Handles the controller ECC correctable and un correctable error .
*/
static void synps_edac_handle_error ( struct mem_ctl_info * mci ,
struct synps_ecc_status * p )
{
struct synps_edac_priv * priv = mci - > pvt_info ;
struct ecc_error_info * pinf ;
if ( p - > ce_cnt ) {
pinf = & p - > ceinfo ;
snprintf ( priv - > message , SYNPS_EDAC_MSG_SIZE ,
" DDR ECC error type :%s Row %d Bank %d Col %d " ,
" CE " , pinf - > row , pinf - > bank , pinf - > col ) ;
edac_mc_handle_error ( HW_EVENT_ERR_CORRECTED , mci ,
p - > ce_cnt , 0 , 0 , 0 , 0 , 0 , - 1 ,
priv - > message , " " ) ;
}
if ( p - > ue_cnt ) {
pinf = & p - > ueinfo ;
snprintf ( priv - > message , SYNPS_EDAC_MSG_SIZE ,
" DDR ECC error type :%s Row %d Bank %d Col %d " ,
" UE " , pinf - > row , pinf - > bank , pinf - > col ) ;
edac_mc_handle_error ( HW_EVENT_ERR_UNCORRECTED , mci ,
p - > ue_cnt , 0 , 0 , 0 , 0 , 0 , - 1 ,
priv - > message , " " ) ;
}
memset ( p , 0 , sizeof ( * p ) ) ;
}
/**
* synps_edac_check - Check controller for ECC errors
* @ mci : Pointer to the edac memory controller instance
*
* Used to check and post ECC errors . Called by the polling thread
*/
static void synps_edac_check ( struct mem_ctl_info * mci )
{
struct synps_edac_priv * priv = mci - > pvt_info ;
int status ;
status = synps_edac_geterror_info ( priv - > baseaddr , & priv - > stat ) ;
if ( status )
return ;
priv - > ce_cnt + = priv - > stat . ce_cnt ;
priv - > ue_cnt + = priv - > stat . ue_cnt ;
synps_edac_handle_error ( mci , & priv - > stat ) ;
edac_dbg ( 3 , " Total error count ce %d ue %d \n " ,
priv - > ce_cnt , priv - > ue_cnt ) ;
}
/**
* synps_edac_get_dtype - Return the controller memory width
* @ base : Pointer to the ddr memory controller base address
*
* Get the EDAC device type width appropriate for the current controller
* configuration .
*
* Return : a device type width enumeration .
*/
static enum dev_type synps_edac_get_dtype ( const void __iomem * base )
{
enum dev_type dt ;
u32 width ;
width = readl ( base + CTRL_OFST ) ;
width = ( width & CTRL_BW_MASK ) > > CTRL_BW_SHIFT ;
switch ( width ) {
case DDRCTL_WDTH_16 :
dt = DEV_X2 ;
break ;
case DDRCTL_WDTH_32 :
dt = DEV_X4 ;
break ;
default :
dt = DEV_UNKNOWN ;
}
return dt ;
}
/**
* synps_edac_get_eccstate - Return the controller ecc enable / disable status
* @ base : Pointer to the ddr memory controller base address
*
* Get the ECC enable / disable status for the controller
*
* Return : a ecc status boolean i . e true / false - enabled / disabled .
*/
static bool synps_edac_get_eccstate ( void __iomem * base )
{
enum dev_type dt ;
u32 ecctype ;
bool state = false ;
dt = synps_edac_get_dtype ( base ) ;
if ( dt = = DEV_UNKNOWN )
return state ;
ecctype = readl ( base + SCRUB_OFST ) & SCRUB_MODE_MASK ;
if ( ( ecctype = = SCRUB_MODE_SECDED ) & & ( dt = = DEV_X2 ) )
state = true ;
return state ;
}
/**
* synps_edac_get_memsize - reads the size of the attached memory device
*
* Return : the memory size in bytes
*/
static u32 synps_edac_get_memsize ( void )
{
struct sysinfo inf ;
si_meminfo ( & inf ) ;
return inf . totalram * inf . mem_unit ;
}
/**
* synps_edac_get_mtype - Returns controller memory type
* @ base : pointer to the synopsys ecc status structure
*
* Get the EDAC memory type appropriate for the current controller
* configuration .
*
* Return : a memory type enumeration .
*/
static enum mem_type synps_edac_get_mtype ( const void __iomem * base )
{
enum mem_type mt ;
u32 memtype ;
memtype = readl ( base + T_ZQ_OFST ) ;
if ( memtype & T_ZQ_DDRMODE_MASK )
mt = MEM_DDR3 ;
else
mt = MEM_DDR2 ;
return mt ;
}
/**
* synps_edac_init_csrows - Initialize the cs row data
* @ mci : Pointer to the edac memory controller instance
*
* Initializes the chip select rows associated with the EDAC memory
* controller instance
*
* Return : Unconditionally 0.
*/
static int synps_edac_init_csrows ( struct mem_ctl_info * mci )
{
struct csrow_info * csi ;
struct dimm_info * dimm ;
struct synps_edac_priv * priv = mci - > pvt_info ;
u32 size ;
int row , j ;
for ( row = 0 ; row < mci - > nr_csrows ; row + + ) {
csi = mci - > csrows [ row ] ;
size = synps_edac_get_memsize ( ) ;
for ( j = 0 ; j < csi - > nr_channels ; j + + ) {
dimm = csi - > channels [ j ] - > dimm ;
dimm - > edac_mode = EDAC_FLAG_SECDED ;
dimm - > mtype = synps_edac_get_mtype ( priv - > baseaddr ) ;
dimm - > nr_pages = ( size > > PAGE_SHIFT ) / csi - > nr_channels ;
dimm - > grain = SYNPS_EDAC_ERR_GRAIN ;
dimm - > dtype = synps_edac_get_dtype ( priv - > baseaddr ) ;
}
}
return 0 ;
}
/**
* synps_edac_mc_init - Initialize driver instance
* @ mci : Pointer to the edac memory controller instance
* @ pdev : Pointer to the platform_device struct
*
* Performs initialization of the EDAC memory controller instance and
* related driver - private data associated with the memory controller the
* instance is bound to .
*
* Return : Always zero .
*/
static int synps_edac_mc_init ( struct mem_ctl_info * mci ,
struct platform_device * pdev )
{
int status ;
struct synps_edac_priv * priv ;
mci - > pdev = & pdev - > dev ;
priv = mci - > pvt_info ;
platform_set_drvdata ( pdev , mci ) ;
/* Initialize controller capabilities and configuration */
mci - > mtype_cap = MEM_FLAG_DDR3 | MEM_FLAG_DDR2 ;
mci - > edac_ctl_cap = EDAC_FLAG_NONE | EDAC_FLAG_SECDED ;
mci - > scrub_cap = SCRUB_HW_SRC ;
mci - > scrub_mode = SCRUB_NONE ;
mci - > edac_cap = EDAC_FLAG_SECDED ;
mci - > ctl_name = " synps_ddr_controller " ;
mci - > dev_name = SYNPS_EDAC_MOD_STRING ;
mci - > mod_name = SYNPS_EDAC_MOD_VER ;
mci - > mod_ver = " 1 " ;
edac_op_state = EDAC_OPSTATE_POLL ;
mci - > edac_check = synps_edac_check ;
mci - > ctl_page_to_phys = NULL ;
status = synps_edac_init_csrows ( mci ) ;
return status ;
}
/**
* synps_edac_mc_probe - Check controller and bind driver
* @ pdev : Pointer to the platform_device struct
*
* Probes a specific controller instance for binding with the driver .
*
* Return : 0 if the controller instance was successfully bound to the
* driver ; otherwise , < 0 on error .
*/
static int synps_edac_mc_probe ( struct platform_device * pdev )
{
struct mem_ctl_info * mci ;
struct edac_mc_layer layers [ 2 ] ;
struct synps_edac_priv * priv ;
int rc ;
struct resource * res ;
void __iomem * baseaddr ;
res = platform_get_resource ( pdev , IORESOURCE_MEM , 0 ) ;
baseaddr = devm_ioremap_resource ( & pdev - > dev , res ) ;
if ( IS_ERR ( baseaddr ) )
return PTR_ERR ( baseaddr ) ;
if ( ! synps_edac_get_eccstate ( baseaddr ) ) {
edac_printk ( KERN_INFO , EDAC_MC , " ECC not enabled \n " ) ;
return - ENXIO ;
}
layers [ 0 ] . type = EDAC_MC_LAYER_CHIP_SELECT ;
layers [ 0 ] . size = SYNPS_EDAC_NR_CSROWS ;
layers [ 0 ] . is_virt_csrow = true ;
layers [ 1 ] . type = EDAC_MC_LAYER_CHANNEL ;
layers [ 1 ] . size = SYNPS_EDAC_NR_CHANS ;
layers [ 1 ] . is_virt_csrow = false ;
mci = edac_mc_alloc ( 0 , ARRAY_SIZE ( layers ) , layers ,
sizeof ( struct synps_edac_priv ) ) ;
if ( ! mci ) {
edac_printk ( KERN_ERR , EDAC_MC ,
" Failed memory allocation for mc instance \n " ) ;
return - ENOMEM ;
}
priv = mci - > pvt_info ;
priv - > baseaddr = baseaddr ;
rc = synps_edac_mc_init ( mci , pdev ) ;
if ( rc ) {
edac_printk ( KERN_ERR , EDAC_MC ,
" Failed to initialize instance \n " ) ;
goto free_edac_mc ;
}
rc = edac_mc_add_mc ( mci ) ;
if ( rc ) {
edac_printk ( KERN_ERR , EDAC_MC ,
" Failed to register with EDAC core \n " ) ;
goto free_edac_mc ;
}
/*
* Start capturing the correctable and uncorrectable errors . A write of
* 0 starts the counters .
*/
writel ( 0x0 , baseaddr + ECC_CTRL_OFST ) ;
return rc ;
free_edac_mc :
edac_mc_free ( mci ) ;
return rc ;
}
/**
* synps_edac_mc_remove - Unbind driver from controller
* @ pdev : Pointer to the platform_device struct
*
* Return : Unconditionally 0
*/
static int synps_edac_mc_remove ( struct platform_device * pdev )
{
struct mem_ctl_info * mci = platform_get_drvdata ( pdev ) ;
edac_mc_del_mc ( & pdev - > dev ) ;
edac_mc_free ( mci ) ;
return 0 ;
}
2015-03-16 22:54:41 +03:00
static const struct of_device_id synps_edac_match [ ] = {
2015-01-06 20:43:47 +03:00
{ . compatible = " xlnx,zynq-ddrc-a05 " , } ,
{ /* end of table */ }
} ;
MODULE_DEVICE_TABLE ( of , synps_edac_match ) ;
static struct platform_driver synps_edac_mc_driver = {
. driver = {
. name = " synopsys-edac " ,
. of_match_table = synps_edac_match ,
} ,
. probe = synps_edac_mc_probe ,
. remove = synps_edac_mc_remove ,
} ;
module_platform_driver ( synps_edac_mc_driver ) ;
MODULE_AUTHOR ( " Xilinx Inc " ) ;
MODULE_DESCRIPTION ( " Synopsys DDR ECC driver " ) ;
MODULE_LICENSE ( " GPL v2 " ) ;