2023-01-11 12:32:45 +03:00
// SPDX-License-Identifier: GPL-2.0-only
// Copyright (c) 2022 Nuvoton Technology Corporation
# include <linux/debugfs.h>
# include <linux/iopoll.h>
2023-07-14 20:44:34 +03:00
# include <linux/of.h>
# include <linux/platform_device.h>
2023-01-11 12:32:45 +03:00
# include <linux/regmap.h>
# include "edac_module.h"
# define EDAC_MOD_NAME "npcm-edac"
# define EDAC_MSG_SIZE 256
/* chip serials */
# define NPCM7XX_CHIP BIT(0)
# define NPCM8XX_CHIP BIT(1)
/* syndrome values */
# define UE_SYNDROME 0x03
/* error injection */
# define ERROR_TYPE_CORRECTABLE 0
# define ERROR_TYPE_UNCORRECTABLE 1
# define ERROR_LOCATION_DATA 0
# define ERROR_LOCATION_CHECKCODE 1
# define ERROR_BIT_DATA_MAX 63
# define ERROR_BIT_CHECKCODE_MAX 7
static char data_synd [ ] = {
0xf4 , 0xf1 , 0xec , 0xea , 0xe9 , 0xe6 , 0xe5 , 0xe3 ,
0xdc , 0xda , 0xd9 , 0xd6 , 0xd5 , 0xd3 , 0xce , 0xcb ,
0xb5 , 0xb0 , 0xad , 0xab , 0xa8 , 0xa7 , 0xa4 , 0xa2 ,
0x9d , 0x9b , 0x98 , 0x97 , 0x94 , 0x92 , 0x8f , 0x8a ,
0x75 , 0x70 , 0x6d , 0x6b , 0x68 , 0x67 , 0x64 , 0x62 ,
0x5e , 0x5b , 0x58 , 0x57 , 0x54 , 0x52 , 0x4f , 0x4a ,
0x34 , 0x31 , 0x2c , 0x2a , 0x29 , 0x26 , 0x25 , 0x23 ,
0x1c , 0x1a , 0x19 , 0x16 , 0x15 , 0x13 , 0x0e , 0x0b
} ;
static struct regmap * npcm_regmap ;
struct npcm_platform_data {
/* chip serials */
int chip ;
/* memory controller registers */
u32 ctl_ecc_en ;
u32 ctl_int_status ;
u32 ctl_int_ack ;
u32 ctl_int_mask_master ;
u32 ctl_int_mask_ecc ;
u32 ctl_ce_addr_l ;
u32 ctl_ce_addr_h ;
u32 ctl_ce_data_l ;
u32 ctl_ce_data_h ;
u32 ctl_ce_synd ;
u32 ctl_ue_addr_l ;
u32 ctl_ue_addr_h ;
u32 ctl_ue_data_l ;
u32 ctl_ue_data_h ;
u32 ctl_ue_synd ;
u32 ctl_source_id ;
u32 ctl_controller_busy ;
u32 ctl_xor_check_bits ;
/* masks and shifts */
u32 ecc_en_mask ;
u32 int_status_ce_mask ;
u32 int_status_ue_mask ;
u32 int_ack_ce_mask ;
u32 int_ack_ue_mask ;
u32 int_mask_master_non_ecc_mask ;
u32 int_mask_master_global_mask ;
u32 int_mask_ecc_non_event_mask ;
u32 ce_addr_h_mask ;
u32 ce_synd_mask ;
u32 ce_synd_shift ;
u32 ue_addr_h_mask ;
u32 ue_synd_mask ;
u32 ue_synd_shift ;
u32 source_id_ce_mask ;
u32 source_id_ce_shift ;
u32 source_id_ue_mask ;
u32 source_id_ue_shift ;
u32 controller_busy_mask ;
u32 xor_check_bits_mask ;
u32 xor_check_bits_shift ;
u32 writeback_en_mask ;
u32 fwc_mask ;
} ;
struct priv_data {
void __iomem * reg ;
char message [ EDAC_MSG_SIZE ] ;
const struct npcm_platform_data * pdata ;
/* error injection */
struct dentry * debugfs ;
u8 error_type ;
u8 location ;
u8 bit ;
} ;
static void handle_ce ( struct mem_ctl_info * mci )
{
struct priv_data * priv = mci - > pvt_info ;
const struct npcm_platform_data * pdata ;
u32 val_h = 0 , val_l , id , synd ;
u64 addr = 0 , data = 0 ;
pdata = priv - > pdata ;
regmap_read ( npcm_regmap , pdata - > ctl_ce_addr_l , & val_l ) ;
if ( pdata - > chip = = NPCM8XX_CHIP ) {
regmap_read ( npcm_regmap , pdata - > ctl_ce_addr_h , & val_h ) ;
val_h & = pdata - > ce_addr_h_mask ;
}
addr = ( ( addr | val_h ) < < 32 ) | val_l ;
regmap_read ( npcm_regmap , pdata - > ctl_ce_data_l , & val_l ) ;
if ( pdata - > chip = = NPCM8XX_CHIP )
regmap_read ( npcm_regmap , pdata - > ctl_ce_data_h , & val_h ) ;
data = ( ( data | val_h ) < < 32 ) | val_l ;
regmap_read ( npcm_regmap , pdata - > ctl_source_id , & id ) ;
id = ( id & pdata - > source_id_ce_mask ) > > pdata - > source_id_ce_shift ;
regmap_read ( npcm_regmap , pdata - > ctl_ce_synd , & synd ) ;
synd = ( synd & pdata - > ce_synd_mask ) > > pdata - > ce_synd_shift ;
snprintf ( priv - > message , EDAC_MSG_SIZE ,
" addr = 0x%llx, data = 0x%llx, id = 0x%x " , addr , data , id ) ;
edac_mc_handle_error ( HW_EVENT_ERR_CORRECTED , mci , 1 , addr > > PAGE_SHIFT ,
addr & ~ PAGE_MASK , synd , 0 , 0 , - 1 , priv - > message , " " ) ;
}
static void handle_ue ( struct mem_ctl_info * mci )
{
struct priv_data * priv = mci - > pvt_info ;
const struct npcm_platform_data * pdata ;
u32 val_h = 0 , val_l , id , synd ;
u64 addr = 0 , data = 0 ;
pdata = priv - > pdata ;
regmap_read ( npcm_regmap , pdata - > ctl_ue_addr_l , & val_l ) ;
if ( pdata - > chip = = NPCM8XX_CHIP ) {
regmap_read ( npcm_regmap , pdata - > ctl_ue_addr_h , & val_h ) ;
val_h & = pdata - > ue_addr_h_mask ;
}
addr = ( ( addr | val_h ) < < 32 ) | val_l ;
regmap_read ( npcm_regmap , pdata - > ctl_ue_data_l , & val_l ) ;
if ( pdata - > chip = = NPCM8XX_CHIP )
regmap_read ( npcm_regmap , pdata - > ctl_ue_data_h , & val_h ) ;
data = ( ( data | val_h ) < < 32 ) | val_l ;
regmap_read ( npcm_regmap , pdata - > ctl_source_id , & id ) ;
id = ( id & pdata - > source_id_ue_mask ) > > pdata - > source_id_ue_shift ;
regmap_read ( npcm_regmap , pdata - > ctl_ue_synd , & synd ) ;
synd = ( synd & pdata - > ue_synd_mask ) > > pdata - > ue_synd_shift ;
snprintf ( priv - > message , EDAC_MSG_SIZE ,
" addr = 0x%llx, data = 0x%llx, id = 0x%x " , addr , data , id ) ;
edac_mc_handle_error ( HW_EVENT_ERR_UNCORRECTED , mci , 1 , addr > > PAGE_SHIFT ,
addr & ~ PAGE_MASK , synd , 0 , 0 , - 1 , priv - > message , " " ) ;
}
static irqreturn_t edac_ecc_isr ( int irq , void * dev_id )
{
const struct npcm_platform_data * pdata ;
struct mem_ctl_info * mci = dev_id ;
u32 status ;
pdata = ( ( struct priv_data * ) mci - > pvt_info ) - > pdata ;
regmap_read ( npcm_regmap , pdata - > ctl_int_status , & status ) ;
if ( status & pdata - > int_status_ce_mask ) {
handle_ce ( mci ) ;
/* acknowledge the CE interrupt */
regmap_write ( npcm_regmap , pdata - > ctl_int_ack ,
pdata - > int_ack_ce_mask ) ;
return IRQ_HANDLED ;
} else if ( status & pdata - > int_status_ue_mask ) {
handle_ue ( mci ) ;
/* acknowledge the UE interrupt */
regmap_write ( npcm_regmap , pdata - > ctl_int_ack ,
pdata - > int_ack_ue_mask ) ;
return IRQ_HANDLED ;
}
WARN_ON_ONCE ( 1 ) ;
return IRQ_NONE ;
}
static ssize_t force_ecc_error ( struct file * file , const char __user * data ,
size_t count , loff_t * ppos )
{
struct device * dev = file - > private_data ;
struct mem_ctl_info * mci = to_mci ( dev ) ;
struct priv_data * priv = mci - > pvt_info ;
const struct npcm_platform_data * pdata ;
u32 val , syndrome ;
int ret ;
pdata = priv - > pdata ;
edac_printk ( KERN_INFO , EDAC_MOD_NAME ,
" force an ECC error, type = %d, location = %d, bit = %d \n " ,
priv - > error_type , priv - > location , priv - > bit ) ;
/* ensure no pending writes */
ret = regmap_read_poll_timeout ( npcm_regmap , pdata - > ctl_controller_busy ,
val , ! ( val & pdata - > controller_busy_mask ) ,
1000 , 10000 ) ;
if ( ret ) {
edac_printk ( KERN_INFO , EDAC_MOD_NAME ,
" wait pending writes timeout \n " ) ;
return count ;
}
regmap_read ( npcm_regmap , pdata - > ctl_xor_check_bits , & val ) ;
val & = ~ pdata - > xor_check_bits_mask ;
/* write syndrome to XOR_CHECK_BITS */
if ( priv - > error_type = = ERROR_TYPE_CORRECTABLE ) {
if ( priv - > location = = ERROR_LOCATION_DATA & &
priv - > bit > ERROR_BIT_DATA_MAX ) {
edac_printk ( KERN_INFO , EDAC_MOD_NAME ,
" data bit should not exceed %d (%d) \n " ,
ERROR_BIT_DATA_MAX , priv - > bit ) ;
return count ;
}
if ( priv - > location = = ERROR_LOCATION_CHECKCODE & &
priv - > bit > ERROR_BIT_CHECKCODE_MAX ) {
edac_printk ( KERN_INFO , EDAC_MOD_NAME ,
" checkcode bit should not exceed %d (%d) \n " ,
ERROR_BIT_CHECKCODE_MAX , priv - > bit ) ;
return count ;
}
syndrome = priv - > location ? 1 < < priv - > bit
: data_synd [ priv - > bit ] ;
regmap_write ( npcm_regmap , pdata - > ctl_xor_check_bits ,
val | ( syndrome < < pdata - > xor_check_bits_shift ) |
pdata - > writeback_en_mask ) ;
} else if ( priv - > error_type = = ERROR_TYPE_UNCORRECTABLE ) {
regmap_write ( npcm_regmap , pdata - > ctl_xor_check_bits ,
val | ( UE_SYNDROME < < pdata - > xor_check_bits_shift ) ) ;
}
/* force write check */
regmap_update_bits ( npcm_regmap , pdata - > ctl_xor_check_bits ,
pdata - > fwc_mask , pdata - > fwc_mask ) ;
return count ;
}
static const struct file_operations force_ecc_error_fops = {
. open = simple_open ,
. write = force_ecc_error ,
. llseek = generic_file_llseek ,
} ;
/*
* Setup debugfs for error injection .
*
* Nodes :
* error_type - 0 : CE , 1 : UE
* location - 0 : data , 1 : checkcode
* bit - 0 ~ 63 for data and 0 ~ 7 for checkcode
* force_ecc_error - trigger
*
* Examples :
* 1. Inject a correctable error ( CE ) at checkcode bit 7.
* ~ # echo 0 > / sys / kernel / debug / edac / npcm - edac / error_type
* ~ # echo 1 > / sys / kernel / debug / edac / npcm - edac / location
* ~ # echo 7 > / sys / kernel / debug / edac / npcm - edac / bit
* ~ # echo 1 > / sys / kernel / debug / edac / npcm - edac / force_ecc_error
*
* 2. Inject an uncorrectable error ( UE ) .
* ~ # echo 1 > / sys / kernel / debug / edac / npcm - edac / error_type
* ~ # echo 1 > / sys / kernel / debug / edac / npcm - edac / force_ecc_error
*/
static void setup_debugfs ( struct mem_ctl_info * mci )
{
struct priv_data * priv = mci - > pvt_info ;
priv - > debugfs = edac_debugfs_create_dir ( mci - > mod_name ) ;
if ( ! priv - > debugfs )
return ;
edac_debugfs_create_x8 ( " error_type " , 0644 , priv - > debugfs , & priv - > error_type ) ;
edac_debugfs_create_x8 ( " location " , 0644 , priv - > debugfs , & priv - > location ) ;
edac_debugfs_create_x8 ( " bit " , 0644 , priv - > debugfs , & priv - > bit ) ;
edac_debugfs_create_file ( " force_ecc_error " , 0200 , priv - > debugfs ,
& mci - > dev , & force_ecc_error_fops ) ;
}
static int setup_irq ( struct mem_ctl_info * mci , struct platform_device * pdev )
{
const struct npcm_platform_data * pdata ;
int ret , irq ;
pdata = ( ( struct priv_data * ) mci - > pvt_info ) - > pdata ;
irq = platform_get_irq ( pdev , 0 ) ;
if ( irq < 0 ) {
edac_printk ( KERN_ERR , EDAC_MOD_NAME , " IRQ not defined in DTS \n " ) ;
return irq ;
}
ret = devm_request_irq ( & pdev - > dev , irq , edac_ecc_isr , 0 ,
dev_name ( & pdev - > dev ) , mci ) ;
if ( ret < 0 ) {
edac_printk ( KERN_ERR , EDAC_MOD_NAME , " failed to request IRQ \n " ) ;
return ret ;
}
/* enable the functional group of ECC and mask the others */
regmap_write ( npcm_regmap , pdata - > ctl_int_mask_master ,
pdata - > int_mask_master_non_ecc_mask ) ;
if ( pdata - > chip = = NPCM8XX_CHIP )
regmap_write ( npcm_regmap , pdata - > ctl_int_mask_ecc ,
pdata - > int_mask_ecc_non_event_mask ) ;
return 0 ;
}
static const struct regmap_config npcm_regmap_cfg = {
. reg_bits = 32 ,
. reg_stride = 4 ,
. val_bits = 32 ,
} ;
static int edac_probe ( struct platform_device * pdev )
{
const struct npcm_platform_data * pdata ;
struct device * dev = & pdev - > dev ;
struct edac_mc_layer layers [ 1 ] ;
struct mem_ctl_info * mci ;
struct priv_data * priv ;
void __iomem * reg ;
u32 val ;
int rc ;
reg = devm_platform_ioremap_resource ( pdev , 0 ) ;
if ( IS_ERR ( reg ) )
return PTR_ERR ( reg ) ;
npcm_regmap = devm_regmap_init_mmio ( dev , reg , & npcm_regmap_cfg ) ;
if ( IS_ERR ( npcm_regmap ) )
return PTR_ERR ( npcm_regmap ) ;
pdata = of_device_get_match_data ( dev ) ;
if ( ! pdata )
return - EINVAL ;
/* bail out if ECC is not enabled */
regmap_read ( npcm_regmap , pdata - > ctl_ecc_en , & val ) ;
if ( ! ( val & pdata - > ecc_en_mask ) ) {
edac_printk ( KERN_ERR , EDAC_MOD_NAME , " ECC is not enabled \n " ) ;
return - EPERM ;
}
edac_op_state = EDAC_OPSTATE_INT ;
layers [ 0 ] . type = EDAC_MC_LAYER_ALL_MEM ;
layers [ 0 ] . size = 1 ;
mci = edac_mc_alloc ( 0 , ARRAY_SIZE ( layers ) , layers ,
sizeof ( struct priv_data ) ) ;
if ( ! mci )
return - ENOMEM ;
mci - > pdev = & pdev - > dev ;
priv = mci - > pvt_info ;
priv - > reg = reg ;
priv - > pdata = pdata ;
platform_set_drvdata ( pdev , mci ) ;
mci - > mtype_cap = MEM_FLAG_DDR4 ;
mci - > edac_ctl_cap = EDAC_FLAG_SECDED ;
mci - > scrub_cap = SCRUB_FLAG_HW_SRC ;
mci - > scrub_mode = SCRUB_HW_SRC ;
mci - > edac_cap = EDAC_FLAG_SECDED ;
mci - > ctl_name = " npcm_ddr_controller " ;
mci - > dev_name = dev_name ( & pdev - > dev ) ;
mci - > mod_name = EDAC_MOD_NAME ;
mci - > ctl_page_to_phys = NULL ;
rc = setup_irq ( mci , pdev ) ;
if ( rc )
goto free_edac_mc ;
rc = edac_mc_add_mc ( mci ) ;
if ( rc )
goto free_edac_mc ;
if ( IS_ENABLED ( CONFIG_EDAC_DEBUG ) & & pdata - > chip = = NPCM8XX_CHIP )
setup_debugfs ( mci ) ;
return rc ;
free_edac_mc :
edac_mc_free ( mci ) ;
return rc ;
}
static int edac_remove ( struct platform_device * pdev )
{
struct mem_ctl_info * mci = platform_get_drvdata ( pdev ) ;
struct priv_data * priv = mci - > pvt_info ;
const struct npcm_platform_data * pdata ;
pdata = priv - > pdata ;
if ( IS_ENABLED ( CONFIG_EDAC_DEBUG ) & & pdata - > chip = = NPCM8XX_CHIP )
edac_debugfs_remove_recursive ( priv - > debugfs ) ;
edac_mc_del_mc ( & pdev - > dev ) ;
edac_mc_free ( mci ) ;
regmap_write ( npcm_regmap , pdata - > ctl_int_mask_master ,
pdata - > int_mask_master_global_mask ) ;
regmap_update_bits ( npcm_regmap , pdata - > ctl_ecc_en , pdata - > ecc_en_mask , 0 ) ;
return 0 ;
}
static const struct npcm_platform_data npcm750_edac = {
. chip = NPCM7XX_CHIP ,
/* memory controller registers */
. ctl_ecc_en = 0x174 ,
. ctl_int_status = 0x1d0 ,
. ctl_int_ack = 0x1d4 ,
. ctl_int_mask_master = 0x1d8 ,
. ctl_ce_addr_l = 0x188 ,
. ctl_ce_data_l = 0x190 ,
. ctl_ce_synd = 0x18c ,
. ctl_ue_addr_l = 0x17c ,
. ctl_ue_data_l = 0x184 ,
. ctl_ue_synd = 0x180 ,
. ctl_source_id = 0x194 ,
/* masks and shifts */
. ecc_en_mask = BIT ( 24 ) ,
. int_status_ce_mask = GENMASK ( 4 , 3 ) ,
. int_status_ue_mask = GENMASK ( 6 , 5 ) ,
. int_ack_ce_mask = GENMASK ( 4 , 3 ) ,
. int_ack_ue_mask = GENMASK ( 6 , 5 ) ,
. int_mask_master_non_ecc_mask = GENMASK ( 30 , 7 ) | GENMASK ( 2 , 0 ) ,
. int_mask_master_global_mask = BIT ( 31 ) ,
. ce_synd_mask = GENMASK ( 6 , 0 ) ,
. ce_synd_shift = 0 ,
. ue_synd_mask = GENMASK ( 6 , 0 ) ,
. ue_synd_shift = 0 ,
. source_id_ce_mask = GENMASK ( 29 , 16 ) ,
. source_id_ce_shift = 16 ,
. source_id_ue_mask = GENMASK ( 13 , 0 ) ,
. source_id_ue_shift = 0 ,
} ;
static const struct npcm_platform_data npcm845_edac = {
. chip = NPCM8XX_CHIP ,
/* memory controller registers */
. ctl_ecc_en = 0x16c ,
. ctl_int_status = 0x228 ,
. ctl_int_ack = 0x244 ,
. ctl_int_mask_master = 0x220 ,
. ctl_int_mask_ecc = 0x260 ,
. ctl_ce_addr_l = 0x18c ,
. ctl_ce_addr_h = 0x190 ,
. ctl_ce_data_l = 0x194 ,
. ctl_ce_data_h = 0x198 ,
. ctl_ce_synd = 0x190 ,
. ctl_ue_addr_l = 0x17c ,
. ctl_ue_addr_h = 0x180 ,
. ctl_ue_data_l = 0x184 ,
. ctl_ue_data_h = 0x188 ,
. ctl_ue_synd = 0x180 ,
. ctl_source_id = 0x19c ,
. ctl_controller_busy = 0x20c ,
. ctl_xor_check_bits = 0x174 ,
/* masks and shifts */
. ecc_en_mask = GENMASK ( 17 , 16 ) ,
. int_status_ce_mask = GENMASK ( 1 , 0 ) ,
. int_status_ue_mask = GENMASK ( 3 , 2 ) ,
. int_ack_ce_mask = GENMASK ( 1 , 0 ) ,
. int_ack_ue_mask = GENMASK ( 3 , 2 ) ,
. int_mask_master_non_ecc_mask = GENMASK ( 30 , 3 ) | GENMASK ( 1 , 0 ) ,
. int_mask_master_global_mask = BIT ( 31 ) ,
. int_mask_ecc_non_event_mask = GENMASK ( 8 , 4 ) ,
. ce_addr_h_mask = GENMASK ( 1 , 0 ) ,
. ce_synd_mask = GENMASK ( 15 , 8 ) ,
. ce_synd_shift = 8 ,
. ue_addr_h_mask = GENMASK ( 1 , 0 ) ,
. ue_synd_mask = GENMASK ( 15 , 8 ) ,
. ue_synd_shift = 8 ,
. source_id_ce_mask = GENMASK ( 29 , 16 ) ,
. source_id_ce_shift = 16 ,
. source_id_ue_mask = GENMASK ( 13 , 0 ) ,
. source_id_ue_shift = 0 ,
. controller_busy_mask = BIT ( 0 ) ,
. xor_check_bits_mask = GENMASK ( 23 , 16 ) ,
. xor_check_bits_shift = 16 ,
. writeback_en_mask = BIT ( 24 ) ,
. fwc_mask = BIT ( 8 ) ,
} ;
static const struct of_device_id npcm_edac_of_match [ ] = {
{
. compatible = " nuvoton,npcm750-memory-controller " ,
. data = & npcm750_edac
} ,
{
. compatible = " nuvoton,npcm845-memory-controller " ,
. data = & npcm845_edac
} ,
{ } ,
} ;
MODULE_DEVICE_TABLE ( of , npcm_edac_of_match ) ;
static struct platform_driver npcm_edac_driver = {
. driver = {
. name = " npcm-edac " ,
. of_match_table = npcm_edac_of_match ,
} ,
. probe = edac_probe ,
. remove = edac_remove ,
} ;
module_platform_driver ( npcm_edac_driver ) ;
MODULE_AUTHOR ( " Medad CChien <medadyoung@gmail.com> " ) ;
MODULE_AUTHOR ( " Marvin Lin <kflin@nuvoton.com> " ) ;
MODULE_DESCRIPTION ( " Nuvoton NPCM EDAC Driver " ) ;
MODULE_LICENSE ( " GPL " ) ;