2014-07-03 03:52:11 +04:00
/*
* CoreNet Coherency Fabric error reporting
*
* Copyright 2014 Freescale Semiconductor 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 .
*/
# include <linux/interrupt.h>
# include <linux/io.h>
# include <linux/irq.h>
# include <linux/module.h>
# include <linux/of.h>
# include <linux/of_address.h>
# include <linux/of_device.h>
# include <linux/of_irq.h>
# include <linux/platform_device.h>
enum ccf_version {
CCF1 ,
CCF2 ,
} ;
struct ccf_info {
enum ccf_version version ;
int err_reg_offs ;
2014-12-11 03:56:22 +03:00
bool has_brr ;
2014-07-03 03:52:11 +04:00
} ;
static const struct ccf_info ccf1_info = {
. version = CCF1 ,
. err_reg_offs = 0xa00 ,
2014-12-11 03:56:22 +03:00
. has_brr = false ,
2014-07-03 03:52:11 +04:00
} ;
static const struct ccf_info ccf2_info = {
. version = CCF2 ,
. err_reg_offs = 0xe40 ,
2014-12-11 03:56:22 +03:00
. has_brr = true ,
2014-07-03 03:52:11 +04:00
} ;
2014-12-11 03:56:22 +03:00
/*
* This register is present but not documented , with different values for
* IP_ID , on other chips with fsl , corenet2 - cf such as t4240 and b4860 .
*/
# define CCF_BRR 0xbf8
# define CCF_BRR_IPID 0xffff0000
# define CCF_BRR_IPID_T1040 0x09310000
2014-07-03 03:52:11 +04:00
static const struct of_device_id ccf_matches [ ] = {
{
. compatible = " fsl,corenet1-cf " ,
. data = & ccf1_info ,
} ,
{
. compatible = " fsl,corenet2-cf " ,
. data = & ccf2_info ,
} ,
{ }
} ;
2015-10-04 19:23:48 +03:00
MODULE_DEVICE_TABLE ( of , ccf_matches ) ;
2014-07-03 03:52:11 +04:00
struct ccf_err_regs {
u32 errdet ; /* 0x00 Error Detect Register */
/* 0x04 Error Enable (ccf1)/Disable (ccf2) Register */
u32 errdis ;
/* 0x08 Error Interrupt Enable Register (ccf2 only) */
u32 errinten ;
u32 cecar ; /* 0x0c Error Capture Attribute Register */
u32 cecaddrh ; /* 0x10 Error Capture Address High */
u32 cecaddrl ; /* 0x14 Error Capture Address Low */
u32 cecar2 ; /* 0x18 Error Capture Attribute Register 2 */
} ;
/* LAE/CV also valid for errdis and errinten */
# define ERRDET_LAE (1 << 0) /* Local Access Error */
# define ERRDET_CV (1 << 1) /* Coherency Violation */
2014-12-11 03:56:22 +03:00
# define ERRDET_UTID (1 << 2) /* Unavailable Target ID (t1040) */
# define ERRDET_MCST (1 << 3) /* Multicast Stash (t1040) */
2014-07-03 03:52:11 +04:00
# define ERRDET_CTYPE_SHIFT 26 /* Capture Type (ccf2 only) */
# define ERRDET_CTYPE_MASK (0x1f << ERRDET_CTYPE_SHIFT)
# define ERRDET_CAP (1 << 31) /* Capture Valid (ccf2 only) */
# define CECAR_VAL (1 << 0) /* Valid (ccf1 only) */
# define CECAR_UVT (1 << 15) /* Unavailable target ID (ccf1) */
# define CECAR_SRCID_SHIFT_CCF1 24
# define CECAR_SRCID_MASK_CCF1 (0xff << CECAR_SRCID_SHIFT_CCF1)
# define CECAR_SRCID_SHIFT_CCF2 18
# define CECAR_SRCID_MASK_CCF2 (0xff << CECAR_SRCID_SHIFT_CCF2)
# define CECADDRH_ADDRH 0xff
struct ccf_private {
const struct ccf_info * info ;
struct device * dev ;
void __iomem * regs ;
struct ccf_err_regs __iomem * err_regs ;
2014-12-11 03:56:22 +03:00
bool t1040 ;
2014-07-03 03:52:11 +04:00
} ;
static irqreturn_t ccf_irq ( int irq , void * dev_id )
{
struct ccf_private * ccf = dev_id ;
static DEFINE_RATELIMIT_STATE ( ratelimit , DEFAULT_RATELIMIT_INTERVAL ,
DEFAULT_RATELIMIT_BURST ) ;
u32 errdet , cecar , cecar2 ;
u64 addr ;
u32 src_id ;
bool uvt = false ;
bool cap_valid = false ;
errdet = ioread32be ( & ccf - > err_regs - > errdet ) ;
cecar = ioread32be ( & ccf - > err_regs - > cecar ) ;
cecar2 = ioread32be ( & ccf - > err_regs - > cecar2 ) ;
addr = ioread32be ( & ccf - > err_regs - > cecaddrl ) ;
addr | = ( ( u64 ) ( ioread32be ( & ccf - > err_regs - > cecaddrh ) &
CECADDRH_ADDRH ) ) < < 32 ;
if ( ! __ratelimit ( & ratelimit ) )
goto out ;
switch ( ccf - > info - > version ) {
case CCF1 :
if ( cecar & CECAR_VAL ) {
if ( cecar & CECAR_UVT )
uvt = true ;
src_id = ( cecar & CECAR_SRCID_MASK_CCF1 ) > >
CECAR_SRCID_SHIFT_CCF1 ;
cap_valid = true ;
}
break ;
case CCF2 :
if ( errdet & ERRDET_CAP ) {
src_id = ( cecar & CECAR_SRCID_MASK_CCF2 ) > >
CECAR_SRCID_SHIFT_CCF2 ;
cap_valid = true ;
}
break ;
}
dev_crit ( ccf - > dev , " errdet 0x%08x cecar 0x%08x cecar2 0x%08x \n " ,
errdet , cecar , cecar2 ) ;
if ( errdet & ERRDET_LAE ) {
if ( uvt )
dev_crit ( ccf - > dev , " LAW Unavailable Target ID \n " ) ;
else
dev_crit ( ccf - > dev , " Local Access Window Error \n " ) ;
}
if ( errdet & ERRDET_CV )
dev_crit ( ccf - > dev , " Coherency Violation \n " ) ;
2014-12-11 03:56:22 +03:00
if ( errdet & ERRDET_UTID )
dev_crit ( ccf - > dev , " Unavailable Target ID \n " ) ;
if ( errdet & ERRDET_MCST )
dev_crit ( ccf - > dev , " Multicast Stash \n " ) ;
2014-07-03 03:52:11 +04:00
if ( cap_valid ) {
dev_crit ( ccf - > dev , " address 0x%09llx, src id 0x%x \n " ,
addr , src_id ) ;
}
out :
iowrite32be ( errdet , & ccf - > err_regs - > errdet ) ;
return errdet ? IRQ_HANDLED : IRQ_NONE ;
}
static int ccf_probe ( struct platform_device * pdev )
{
struct ccf_private * ccf ;
struct resource * r ;
const struct of_device_id * match ;
2014-12-11 03:56:22 +03:00
u32 errinten ;
2014-07-03 03:52:11 +04:00
int ret , irq ;
match = of_match_device ( ccf_matches , & pdev - > dev ) ;
if ( WARN_ON ( ! match ) )
return - ENODEV ;
ccf = devm_kzalloc ( & pdev - > dev , sizeof ( * ccf ) , GFP_KERNEL ) ;
if ( ! ccf )
return - ENOMEM ;
r = platform_get_resource ( pdev , IORESOURCE_MEM , 0 ) ;
if ( ! r ) {
dev_err ( & pdev - > dev , " %s: no mem resource \n " , __func__ ) ;
return - ENXIO ;
}
ccf - > regs = devm_ioremap_resource ( & pdev - > dev , r ) ;
if ( IS_ERR ( ccf - > regs ) ) {
dev_err ( & pdev - > dev , " %s: can't map mem resource \n " , __func__ ) ;
return PTR_ERR ( ccf - > regs ) ;
}
ccf - > dev = & pdev - > dev ;
ccf - > info = match - > data ;
ccf - > err_regs = ccf - > regs + ccf - > info - > err_reg_offs ;
2014-12-11 03:56:22 +03:00
if ( ccf - > info - > has_brr ) {
u32 brr = ioread32be ( ccf - > regs + CCF_BRR ) ;
if ( ( brr & CCF_BRR_IPID ) = = CCF_BRR_IPID_T1040 )
ccf - > t1040 = true ;
}
2014-07-03 03:52:11 +04:00
dev_set_drvdata ( & pdev - > dev , ccf ) ;
irq = platform_get_irq ( pdev , 0 ) ;
if ( ! irq ) {
dev_err ( & pdev - > dev , " %s: no irq \n " , __func__ ) ;
return - ENXIO ;
}
ret = devm_request_irq ( & pdev - > dev , irq , ccf_irq , 0 , pdev - > name , ccf ) ;
if ( ret ) {
dev_err ( & pdev - > dev , " %s: can't request irq \n " , __func__ ) ;
return ret ;
}
2014-12-11 03:56:22 +03:00
errinten = ERRDET_LAE | ERRDET_CV ;
if ( ccf - > t1040 )
errinten | = ERRDET_UTID | ERRDET_MCST ;
2014-07-03 03:52:11 +04:00
switch ( ccf - > info - > version ) {
case CCF1 :
/* On CCF1 this register enables rather than disables. */
2014-12-11 03:56:22 +03:00
iowrite32be ( errinten , & ccf - > err_regs - > errdis ) ;
2014-07-03 03:52:11 +04:00
break ;
case CCF2 :
iowrite32be ( 0 , & ccf - > err_regs - > errdis ) ;
2014-12-11 03:56:22 +03:00
iowrite32be ( errinten , & ccf - > err_regs - > errinten ) ;
2014-07-03 03:52:11 +04:00
break ;
}
return 0 ;
}
static int ccf_remove ( struct platform_device * pdev )
{
struct ccf_private * ccf = dev_get_drvdata ( & pdev - > dev ) ;
switch ( ccf - > info - > version ) {
case CCF1 :
iowrite32be ( 0 , & ccf - > err_regs - > errdis ) ;
break ;
case CCF2 :
/*
* We clear errdis on ccf1 because that ' s the only way to
* disable interrupts , but on ccf2 there ' s no need to disable
* detection .
*/
iowrite32be ( 0 , & ccf - > err_regs - > errinten ) ;
break ;
}
return 0 ;
}
static struct platform_driver ccf_driver = {
. driver = {
. name = KBUILD_MODNAME ,
. of_match_table = ccf_matches ,
} ,
. probe = ccf_probe ,
. remove = ccf_remove ,
} ;
module_platform_driver ( ccf_driver ) ;
MODULE_LICENSE ( " GPL " ) ;
MODULE_AUTHOR ( " Freescale Semiconductor " ) ;
MODULE_DESCRIPTION ( " Freescale CoreNet Coherency Fabric error reporting " ) ;