2011-03-07 18:23:10 +03:00
/*
2011-08-24 18:37:45 +04:00
* OMAP4XXX L3 Interconnect error handling driver
*
* Copyright ( C ) 2011 Texas Corporation
* Santosh Shilimkar < santosh . shilimkar @ ti . com >
* Sricharan < r . sricharan @ ti . com >
*
* 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 .
*
* You should have received a copy of the GNU General Public License
* along with this program ; if not , write to the Free Software
* Foundation , Inc . , 59 Temple Place , Suite 330 , Boston , MA 02111 - 1307
* USA
*/
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>
2012-08-28 04:43:01 +04:00
# include <plat/hardware.h>
2011-03-07 18:23:10 +03:00
# 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 )
{
2011-08-24 18:37:45 +04:00
struct omap4_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 ;
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 ] ;
err_reg = __raw_readl ( base + l3_flagmux [ i ] +
2011-08-24 17:41:39 +04:00
+ 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 */
2011-08-24 17:41:39 +04:00
l3_targ_base = base + * ( l3_targ [ i ] + err_src ) ;
2011-08-23 11:28:48 +04:00
std_err_main = __raw_readl ( l3_targ_base +
2011-08-24 17:41:39 +04:00
L3_TARG_STDERRLOG_MAIN ) ;
2011-09-07 15:55:16 +04:00
masterid = __raw_readl ( l3_targ_base +
L3_TARG_STDERRLOG_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 ,
2011-08-23 11:28:48 +04:00
__raw_readl ( l3_targ_base +
2011-08-24 17:41:39 +04:00
L3_TARG_STDERRLOG_SLVOFSLSB ) ) ;
2011-03-07 18:23:10 +03:00
/* clear the std error log*/
clear = std_err_main | CLEAR_STDERR_LOG ;
2011-08-24 17:41:39 +04:00
writel ( clear , l3_targ_base +
L3_TARG_STDERRLOG_MAIN ) ;
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 ;
2011-08-24 17:41:39 +04:00
writel ( clear , l3_targ_base +
L3_TARG_STDERRLOG_MAIN ) ;
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 ;
}
2011-08-12 15:52:50 +04:00
static int __devinit omap4_l3_probe ( struct platform_device * pdev )
2011-03-07 18:23:10 +03:00
{
2011-08-24 18:37:45 +04:00
static struct omap4_l3 * l3 ;
2011-08-24 17:41:39 +04:00
struct resource * res ;
2011-08-29 16:12:23 +04:00
int ret ;
2011-03-07 18:23:10 +03:00
l3 = kzalloc ( sizeof ( * l3 ) , GFP_KERNEL ) ;
if ( ! l3 )
2011-04-18 20:39:41 +04:00
return - ENOMEM ;
2011-03-07 18:23:10 +03:00
platform_set_drvdata ( pdev , l3 ) ;
res = platform_get_resource ( pdev , IORESOURCE_MEM , 0 ) ;
if ( ! res ) {
dev_err ( & pdev - > dev , " couldn't find resource 0 \n " ) ;
ret = - ENODEV ;
2011-04-18 20:39:41 +04:00
goto err0 ;
2011-03-07 18:23:10 +03:00
}
l3 - > l3_base [ 0 ] = ioremap ( res - > start , resource_size ( res ) ) ;
2011-04-18 20:39:42 +04:00
if ( ! l3 - > l3_base [ 0 ] ) {
2011-03-07 18:23:10 +03:00
dev_err ( & pdev - > dev , " ioremap failed \n " ) ;
ret = - ENOMEM ;
2011-04-18 20:39:41 +04:00
goto err0 ;
2011-03-07 18:23:10 +03:00
}
res = platform_get_resource ( pdev , IORESOURCE_MEM , 1 ) ;
if ( ! res ) {
dev_err ( & pdev - > dev , " couldn't find resource 1 \n " ) ;
ret = - ENODEV ;
2011-04-18 20:39:41 +04:00
goto err1 ;
2011-03-07 18:23:10 +03:00
}
l3 - > l3_base [ 1 ] = ioremap ( res - > start , resource_size ( res ) ) ;
2011-04-18 20:39:42 +04:00
if ( ! l3 - > l3_base [ 1 ] ) {
2011-03-07 18:23:10 +03:00
dev_err ( & pdev - > dev , " ioremap failed \n " ) ;
ret = - ENOMEM ;
2011-04-18 20:39:41 +04:00
goto err1 ;
2011-03-07 18:23:10 +03:00
}
res = platform_get_resource ( pdev , IORESOURCE_MEM , 2 ) ;
if ( ! res ) {
dev_err ( & pdev - > dev , " couldn't find resource 2 \n " ) ;
ret = - ENODEV ;
2011-04-18 20:39:41 +04:00
goto err2 ;
2011-03-07 18:23:10 +03:00
}
l3 - > l3_base [ 2 ] = ioremap ( res - > start , resource_size ( res ) ) ;
2011-04-18 20:39:42 +04:00
if ( ! l3 - > l3_base [ 2 ] ) {
2011-03-07 18:23:10 +03:00
dev_err ( & pdev - > dev , " ioremap failed \n " ) ;
ret = - ENOMEM ;
2011-04-18 20:39:41 +04:00
goto err2 ;
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 ) ;
ret = request_irq ( l3 - > debug_irq ,
2011-03-07 18:23:10 +03:00
l3_interrupt_handler ,
IRQF_DISABLED , " l3-dbg-irq " , l3 ) ;
if ( ret ) {
pr_crit ( " L3: request_irq failed to register for 0x%x \n " ,
2012-08-28 04:43:01 +04:00
9 + OMAP44XX_IRQ_GIC_START ) ;
2011-04-18 20:39:41 +04:00
goto err3 ;
2011-03-07 18:23:10 +03:00
}
2011-08-29 16:12:23 +04:00
l3 - > app_irq = platform_get_irq ( pdev , 1 ) ;
ret = request_irq ( l3 - > app_irq ,
2011-03-07 18:23:10 +03:00
l3_interrupt_handler ,
IRQF_DISABLED , " l3-app-irq " , l3 ) ;
if ( ret ) {
pr_crit ( " L3: request_irq failed to register for 0x%x \n " ,
2012-08-28 04:43:01 +04:00
10 + OMAP44XX_IRQ_GIC_START ) ;
2011-04-18 20:39:41 +04:00
goto err4 ;
2011-03-07 18:23:10 +03:00
}
2011-04-18 20:39:41 +04:00
return 0 ;
2011-03-07 18:23:10 +03:00
err4 :
2011-04-18 20:39:41 +04:00
free_irq ( l3 - > debug_irq , l3 ) ;
2011-03-07 18:23:10 +03:00
err3 :
2011-04-18 20:39:41 +04:00
iounmap ( l3 - > l3_base [ 2 ] ) ;
2011-03-07 18:23:10 +03:00
err2 :
2011-04-18 20:39:41 +04:00
iounmap ( l3 - > l3_base [ 1 ] ) ;
2011-03-07 18:23:10 +03:00
err1 :
2011-04-18 20:39:41 +04:00
iounmap ( l3 - > l3_base [ 0 ] ) ;
2011-03-07 18:23:10 +03:00
err0 :
2011-04-18 20:39:41 +04:00
kfree ( l3 ) ;
2011-03-07 18:23:10 +03:00
return ret ;
}
2011-08-12 15:52:50 +04:00
static int __devexit omap4_l3_remove ( struct platform_device * pdev )
2011-03-07 18:23:10 +03:00
{
2011-08-24 18:37:45 +04:00
struct omap4_l3 * l3 = platform_get_drvdata ( pdev ) ;
2011-03-07 18:23:10 +03:00
free_irq ( l3 - > app_irq , l3 ) ;
free_irq ( l3 - > debug_irq , l3 ) ;
iounmap ( l3 - > l3_base [ 0 ] ) ;
iounmap ( l3 - > l3_base [ 1 ] ) ;
iounmap ( l3 - > l3_base [ 2 ] ) ;
kfree ( l3 ) ;
return 0 ;
}
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
2011-03-07 18:23:10 +03:00
static struct platform_driver omap4_l3_driver = {
2011-08-12 15:52:50 +04:00
. probe = omap4_l3_probe ,
. remove = __devexit_p ( omap4_l3_remove ) ,
. driver = {
. name = " omap_l3_noc " ,
. owner = THIS_MODULE ,
. of_match_table = l3_noc_match ,
2011-03-07 18:23:10 +03:00
} ,
} ;
static int __init omap4_l3_init ( void )
{
2011-08-12 15:52:50 +04:00
return platform_driver_register ( & omap4_l3_driver ) ;
2011-03-07 18:23:10 +03:00
}
postcore_initcall_sync ( omap4_l3_init ) ;
static void __exit omap4_l3_exit ( void )
{
platform_driver_unregister ( & omap4_l3_driver ) ;
}
module_exit ( omap4_l3_exit ) ;