2011-03-07 18:23:10 +03:00
/*
2014-04-11 22:09:36 +04:00
* OMAP L3 Interconnect error handling driver
2011-08-24 18:37:45 +04:00
*
2014-04-11 22:15:43 +04:00
* Copyright ( C ) 2011 - 2014 Texas Instruments Incorporated - http : //www.ti.com/
2011-08-24 18:37:45 +04:00
* Santosh Shilimkar < santosh . shilimkar @ ti . com >
* Sricharan < r . sricharan @ ti . com >
*
* This program is free software ; you can redistribute it and / or modify
2014-04-11 22:15:43 +04:00
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation .
2011-08-24 18:37:45 +04:00
*
2014-04-11 22:15:43 +04:00
* This program is distributed " as is " WITHOUT ANY WARRANTY of any
* kind , whether express or implied ; without even the implied warranty
* of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
2011-08-24 18:37:45 +04:00
* GNU General Public License for more details .
*/
2011-11-02 05:40:11 +04:00
# include <linux/module.h>
2011-03-07 18:23:10 +03:00
# include <linux/init.h>
# include <linux/io.h>
# include <linux/platform_device.h>
# include <linux/interrupt.h>
# include <linux/kernel.h>
# include <linux/slab.h>
# include "omap_l3_noc.h"
/*
* Interrupt Handler for L3 error detection .
* 1 ) Identify the L3 clockdomain partition to which the error belongs to .
* 2 ) Identify the slave where the error information is logged
* 3 ) Print the logged information .
* 4 ) Add dump stack to provide kernel trace .
*
* Two Types of errors :
* 1 ) Custom errors in L3 :
* Target like DMM / FW / EMIF generates SRESP = ERR error
* 2 ) Standard L3 error :
* - Unsupported CMD .
* L3 tries to access target while it is idle
* - OCP disconnect .
* - Address hole error :
* If DSS / ISS / FDIF / USBHOSTFS access a target where they
* do not have connectivity , the error is logged in
* their default target which is DMM2 .
*
* On High Secure devices , firewall errors are possible and those
* can be trapped as well . But the trapping is implemented as part
* secure software and hence need not be implemented here .
*/
static irqreturn_t l3_interrupt_handler ( int irq , void * _l3 )
{
2014-04-11 22:09:36 +04:00
struct omap_l3 * l3 = _l3 ;
2011-09-07 15:55:16 +04:00
int inttype , i , k ;
2011-03-07 18:23:10 +03:00
int err_src = 0 ;
2011-09-07 15:55:16 +04:00
u32 std_err_main , err_reg , clear , masterid ;
2011-08-23 11:28:48 +04:00
void __iomem * base , * l3_targ_base ;
2014-04-11 20:21:47 +04:00
void __iomem * l3_targ_stderr , * l3_targ_slvofslsb , * l3_targ_mstaddr ;
2011-09-07 15:55:16 +04:00
char * target_name , * master_name = " UN IDENTIFIED " ;
2011-03-07 18:23:10 +03:00
/* Get the Type of interrupt */
2011-04-18 20:39:42 +04:00
inttype = irq = = l3 - > app_irq ? L3_APPLICATION_ERROR : L3_DEBUG_ERROR ;
2011-03-07 18:23:10 +03:00
for ( i = 0 ; i < L3_MODULES ; i + + ) {
/*
* Read the regerr register of the clock domain
* to determine the source
*/
2011-08-23 11:28:48 +04:00
base = l3 - > l3_base [ i ] ;
2014-04-11 20:21:47 +04:00
err_reg = readl_relaxed ( base + l3_flagmux [ i ] +
L3_FLAGMUX_REGERR0 + ( inttype < < 3 ) ) ;
2011-03-07 18:23:10 +03:00
/* Get the corresponding error and analyse */
if ( err_reg ) {
/* Identify the source from control status register */
2011-08-24 17:41:39 +04:00
err_src = __ffs ( err_reg ) ;
2011-03-07 18:23:10 +03:00
/* Read the stderrlog_main_source from clk domain */
2014-04-11 20:24:42 +04:00
l3_targ_base = base + l3_targ [ i ] [ err_src ] ;
2014-04-11 20:21:47 +04:00
l3_targ_stderr = l3_targ_base + L3_TARG_STDERRLOG_MAIN ;
l3_targ_slvofslsb = l3_targ_base +
L3_TARG_STDERRLOG_SLVOFSLSB ;
l3_targ_mstaddr = l3_targ_base +
L3_TARG_STDERRLOG_MSTADDR ;
std_err_main = readl_relaxed ( l3_targ_stderr ) ;
masterid = readl_relaxed ( l3_targ_mstaddr ) ;
2011-03-07 18:23:10 +03:00
2011-04-18 20:39:42 +04:00
switch ( std_err_main & CUSTOM_ERROR ) {
2011-03-07 18:23:10 +03:00
case STANDARD_ERROR :
2011-09-07 15:55:16 +04:00
target_name =
2011-08-24 17:41:39 +04:00
l3_targ_inst_name [ i ] [ err_src ] ;
2011-09-07 15:55:16 +04:00
WARN ( true , " L3 standard error: TARGET:%s at address 0x%x \n " ,
target_name ,
2014-04-11 20:21:47 +04:00
readl_relaxed ( l3_targ_slvofslsb ) ) ;
2011-03-07 18:23:10 +03:00
/* clear the std error log*/
clear = std_err_main | CLEAR_STDERR_LOG ;
2014-04-11 20:21:47 +04:00
writel_relaxed ( clear , l3_targ_stderr ) ;
2011-03-07 18:23:10 +03:00
break ;
case CUSTOM_ERROR :
2011-09-07 15:55:16 +04:00
target_name =
2011-08-24 17:41:39 +04:00
l3_targ_inst_name [ i ] [ err_src ] ;
2011-09-07 15:55:16 +04:00
for ( k = 0 ; k < NUM_OF_L3_MASTERS ; k + + ) {
if ( masterid = = l3_masters [ k ] . id )
master_name =
l3_masters [ k ] . name ;
}
WARN ( true , " L3 custom error: MASTER:%s TARGET:%s \n " ,
master_name , target_name ) ;
2011-03-07 18:23:10 +03:00
/* clear the std error log*/
clear = std_err_main | CLEAR_STDERR_LOG ;
2014-04-11 20:21:47 +04:00
writel_relaxed ( clear , l3_targ_stderr ) ;
2011-03-07 18:23:10 +03:00
break ;
default :
/* Nothing to be handled here as of now */
break ;
}
/* Error found so break the for loop */
break ;
}
}
return IRQ_HANDLED ;
}
2014-04-11 22:09:36 +04:00
static int omap_l3_probe ( struct platform_device * pdev )
2011-03-07 18:23:10 +03:00
{
2014-04-11 22:09:36 +04:00
static struct omap_l3 * l3 ;
2014-04-01 17:23:47 +04:00
int ret , i ;
2011-03-07 18:23:10 +03:00
2014-04-01 17:23:46 +04:00
l3 = devm_kzalloc ( & pdev - > dev , sizeof ( * l3 ) , GFP_KERNEL ) ;
2011-03-07 18:23:10 +03:00
if ( ! l3 )
2011-04-18 20:39:41 +04:00
return - ENOMEM ;
2011-03-07 18:23:10 +03:00
2014-04-11 21:04:01 +04:00
l3 - > dev = & pdev - > dev ;
2011-03-07 18:23:10 +03:00
platform_set_drvdata ( pdev , l3 ) ;
2014-04-01 17:23:47 +04:00
/* Get mem resources */
for ( i = 0 ; i < L3_MODULES ; i + + ) {
struct resource * res = platform_get_resource ( pdev ,
IORESOURCE_MEM , i ) ;
2011-03-07 18:23:10 +03:00
2014-04-01 17:23:47 +04:00
l3 - > l3_base [ i ] = devm_ioremap_resource ( & pdev - > dev , res ) ;
if ( IS_ERR ( l3 - > l3_base [ i ] ) ) {
2014-04-11 21:04:01 +04:00
dev_err ( l3 - > dev , " ioremap %d failed \n " , i ) ;
2014-04-01 17:23:47 +04:00
return PTR_ERR ( l3 - > l3_base [ i ] ) ;
}
2011-03-07 18:23:10 +03:00
}
/*
* Setup interrupt Handlers
*/
2011-08-29 16:12:23 +04:00
l3 - > debug_irq = platform_get_irq ( pdev , 0 ) ;
2014-04-11 21:04:01 +04:00
ret = devm_request_irq ( l3 - > dev , l3 - > debug_irq , l3_interrupt_handler ,
2014-04-01 17:23:48 +04:00
IRQF_DISABLED , " l3-dbg-irq " , l3 ) ;
2011-03-07 18:23:10 +03:00
if ( ret ) {
2014-04-11 21:04:01 +04:00
dev_err ( l3 - > dev , " request_irq failed for %d \n " ,
2014-04-01 17:23:50 +04:00
l3 - > debug_irq ) ;
2014-04-01 17:23:47 +04:00
return ret ;
2011-03-07 18:23:10 +03:00
}
2011-08-29 16:12:23 +04:00
l3 - > app_irq = platform_get_irq ( pdev , 1 ) ;
2014-04-11 21:04:01 +04:00
ret = devm_request_irq ( l3 - > dev , l3 - > app_irq , l3_interrupt_handler ,
2014-04-01 17:23:48 +04:00
IRQF_DISABLED , " l3-app-irq " , l3 ) ;
if ( ret )
2014-04-11 21:04:01 +04:00
dev_err ( l3 - > dev , " request_irq failed for %d \n " , l3 - > app_irq ) ;
2011-04-18 20:39:41 +04:00
2011-03-07 18:23:10 +03:00
return ret ;
}
2011-08-12 15:52:50 +04:00
# if defined(CONFIG_OF)
static const struct of_device_id l3_noc_match [ ] = {
{ . compatible = " ti,omap4-l3-noc " , } ,
{ } ,
2011-11-24 02:45:37 +04:00
} ;
2011-08-12 15:52:50 +04:00
MODULE_DEVICE_TABLE ( of , l3_noc_match ) ;
# else
# define l3_noc_match NULL
# endif
2014-04-11 22:09:36 +04:00
static struct platform_driver omap_l3_driver = {
. probe = omap_l3_probe ,
2011-08-12 15:52:50 +04:00
. driver = {
. name = " omap_l3_noc " ,
. owner = THIS_MODULE ,
. of_match_table = l3_noc_match ,
2011-03-07 18:23:10 +03:00
} ,
} ;
2014-04-11 22:09:36 +04:00
static int __init omap_l3_init ( void )
2011-03-07 18:23:10 +03:00
{
2014-04-11 22:09:36 +04:00
return platform_driver_register ( & omap_l3_driver ) ;
2011-03-07 18:23:10 +03:00
}
2014-04-11 22:09:36 +04:00
postcore_initcall_sync ( omap_l3_init ) ;
2011-03-07 18:23:10 +03:00
2014-04-11 22:09:36 +04:00
static void __exit omap_l3_exit ( void )
2011-03-07 18:23:10 +03:00
{
2014-04-11 22:09:36 +04:00
platform_driver_unregister ( & omap_l3_driver ) ;
2011-03-07 18:23:10 +03:00
}
2014-04-11 22:09:36 +04:00
module_exit ( omap_l3_exit ) ;