2011-03-07 17:05:20 +03:00
/*
* OMAP3XXX L3 Interconnect Driver
*
* Copyright ( C ) 2011 Texas Corporation
* Felipe Balbi < balbi @ ti . com >
* 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
*/
# include <linux/kernel.h>
# include <linux/slab.h>
# include <linux/platform_device.h>
# include <linux/interrupt.h>
# include <linux/io.h>
# include "omap_l3_smx.h"
static inline u64 omap3_l3_readll ( void __iomem * base , u16 reg )
{
return __raw_readll ( base + reg ) ;
}
static inline void omap3_l3_writell ( void __iomem * base , u16 reg , u64 value )
{
__raw_writell ( value , base + reg ) ;
}
static inline enum omap3_l3_code omap3_l3_decode_error_code ( u64 error )
{
return ( error & 0x0f000000 ) > > L3_ERROR_LOG_CODE ;
}
static inline u32 omap3_l3_decode_addr ( u64 error_addr )
{
return error_addr & 0xffffffff ;
}
static inline unsigned omap3_l3_decode_cmd ( u64 error )
{
return ( error & 0x07 ) > > L3_ERROR_LOG_CMD ;
}
static inline enum omap3_l3_initiator_id omap3_l3_decode_initid ( u64 error )
{
return ( error & 0xff00 ) > > L3_ERROR_LOG_INITID ;
}
static inline unsigned omap3_l3_decode_req_info ( u64 error )
{
return ( error > > 32 ) & 0xffff ;
}
static char * omap3_l3_code_string ( u8 code )
{
switch ( code ) {
case OMAP_L3_CODE_NOERROR :
return " No Error " ;
case OMAP_L3_CODE_UNSUP_CMD :
return " Unsupported Command " ;
case OMAP_L3_CODE_ADDR_HOLE :
return " Address Hole " ;
case OMAP_L3_CODE_PROTECT_VIOLATION :
return " Protection Violation " ;
case OMAP_L3_CODE_IN_BAND_ERR :
return " In-band Error " ;
case OMAP_L3_CODE_REQ_TOUT_NOT_ACCEPT :
return " Request Timeout Not Accepted " ;
case OMAP_L3_CODE_REQ_TOUT_NO_RESP :
return " Request Timeout, no response " ;
default :
return " UNKNOWN error " ;
}
}
static char * omap3_l3_initiator_string ( u8 initid )
{
switch ( initid ) {
case OMAP_L3_LCD :
return " LCD " ;
case OMAP_L3_SAD2D :
return " SAD2D " ;
case OMAP_L3_IA_MPU_SS_1 :
case OMAP_L3_IA_MPU_SS_2 :
case OMAP_L3_IA_MPU_SS_3 :
case OMAP_L3_IA_MPU_SS_4 :
case OMAP_L3_IA_MPU_SS_5 :
return " MPU " ;
case OMAP_L3_IA_IVA_SS_1 :
case OMAP_L3_IA_IVA_SS_2 :
case OMAP_L3_IA_IVA_SS_3 :
return " IVA_SS " ;
case OMAP_L3_IA_IVA_SS_DMA_1 :
case OMAP_L3_IA_IVA_SS_DMA_2 :
case OMAP_L3_IA_IVA_SS_DMA_3 :
case OMAP_L3_IA_IVA_SS_DMA_4 :
case OMAP_L3_IA_IVA_SS_DMA_5 :
case OMAP_L3_IA_IVA_SS_DMA_6 :
return " IVA_SS_DMA " ;
case OMAP_L3_IA_SGX :
return " SGX " ;
case OMAP_L3_IA_CAM_1 :
case OMAP_L3_IA_CAM_2 :
case OMAP_L3_IA_CAM_3 :
return " CAM " ;
case OMAP_L3_IA_DAP :
return " DAP " ;
case OMAP_L3_SDMA_WR_1 :
case OMAP_L3_SDMA_WR_2 :
return " SDMA_WR " ;
case OMAP_L3_SDMA_RD_1 :
case OMAP_L3_SDMA_RD_2 :
case OMAP_L3_SDMA_RD_3 :
case OMAP_L3_SDMA_RD_4 :
return " SDMA_RD " ;
case OMAP_L3_USBOTG :
return " USB_OTG " ;
case OMAP_L3_USBHOST :
return " USB_HOST " ;
default :
return " UNKNOWN Initiator " ;
}
}
/**
* omap3_l3_block_irq - handles a register block ' s irq
* @ l3 : struct omap3_l3 *
* @ base : register block base address
* @ error : L3_ERROR_LOG register of our block
*
* Called in hard - irq context . Caller should take care of locking
*
* OMAP36xx TRM gives , on page 2001 , Figure 9 - 10 , the Typical Error
* Analysis Sequence , we are following that sequence here , please
* refer to that Figure for more information on the subject .
*/
static irqreturn_t omap3_l3_block_irq ( struct omap3_l3 * l3 ,
u64 error , int error_addr )
{
u8 code = omap3_l3_decode_error_code ( error ) ;
u8 initid = omap3_l3_decode_initid ( error ) ;
u8 multi = error & L3_ERROR_LOG_MULTI ;
u32 address = omap3_l3_decode_addr ( error_addr ) ;
WARN ( true , " %s Error seen by %s %s at address %x \n " ,
omap3_l3_code_string ( code ) ,
omap3_l3_initiator_string ( initid ) ,
multi ? " Multiple Errors " : " " ,
address ) ;
return IRQ_HANDLED ;
}
static irqreturn_t omap3_l3_app_irq ( int irq , void * _l3 )
{
struct omap3_l3 * l3 = _l3 ;
u64 status , clear ;
u64 error ;
u64 error_addr ;
u64 err_source = 0 ;
void __iomem * base ;
int int_type ;
irqreturn_t ret = IRQ_NONE ;
if ( irq = = l3 - > app_irq )
int_type = L3_APPLICATION_ERROR ;
else
int_type = L3_DEBUG_ERROR ;
if ( ! int_type ) {
status = omap3_l3_readll ( l3 - > rt , L3_SI_FLAG_STATUS_0 ) ;
/*
* if we have a timeout error , there ' s nothing we can
* do besides rebooting the board . So let ' s BUG on any
* of such errors and handle the others . timeout error
* is severe and not expected to occur .
*/
BUG_ON ( status & L3_STATUS_0_TIMEOUT_MASK ) ;
} else {
status = omap3_l3_readll ( l3 - > rt , L3_SI_FLAG_STATUS_1 ) ;
/* No timeout error for debug sources */
}
base = ( ( l3 - > rt ) + ( * ( omap3_l3_bases [ int_type ] + err_source ) ) ) ;
/* identify the error source */
for ( err_source = 0 ; ! ( status & ( 1 < < err_source ) ) ; err_source + + )
;
error = omap3_l3_readll ( base , L3_ERROR_LOG ) ;
if ( error ) {
error_addr = omap3_l3_readll ( base , L3_ERROR_LOG_ADDR ) ;
ret | = omap3_l3_block_irq ( l3 , error , error_addr ) ;
}
/* Clear the status register */
clear = ( ( L3_AGENT_STATUS_CLEAR_IA < < int_type ) |
( L3_AGENT_STATUS_CLEAR_TA ) ) ;
omap3_l3_writell ( base , L3_AGENT_STATUS , clear ) ;
/* clear the error log register */
omap3_l3_writell ( base , L3_ERROR_LOG , error ) ;
return ret ;
}
static int __init omap3_l3_probe ( struct platform_device * pdev )
{
struct omap3_l3 * l3 ;
struct resource * res ;
int ret ;
l3 = kzalloc ( sizeof ( * l3 ) , GFP_KERNEL ) ;
2011-04-18 20:39:41 +04:00
if ( ! l3 )
return - ENOMEM ;
2011-03-07 17:05:20 +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 \n " ) ;
ret = - ENODEV ;
2011-04-18 20:39:41 +04:00
goto err0 ;
2011-03-07 17:05:20 +03:00
}
l3 - > rt = ioremap ( res - > start , resource_size ( res ) ) ;
if ( ! ( l3 - > rt ) ) {
dev_err ( & pdev - > dev , " ioremap failed \n " ) ;
ret = - ENOMEM ;
2011-04-18 20:39:41 +04:00
goto err0 ;
2011-03-07 17:05:20 +03:00
}
2011-03-19 02:53:20 +03:00
l3 - > debug_irq = platform_get_irq ( pdev , 0 ) ;
ret = request_irq ( l3 - > debug_irq , omap3_l3_app_irq ,
2011-03-07 17:05:20 +03:00
IRQF_DISABLED | IRQF_TRIGGER_RISING ,
" l3-debug-irq " , l3 ) ;
if ( ret ) {
dev_err ( & pdev - > dev , " couldn't request debug irq \n " ) ;
2011-04-18 20:39:41 +04:00
goto err1 ;
2011-03-07 17:05:20 +03:00
}
2011-03-19 02:53:20 +03:00
l3 - > app_irq = platform_get_irq ( pdev , 1 ) ;
ret = request_irq ( l3 - > app_irq , omap3_l3_app_irq ,
2011-03-07 17:05:20 +03:00
IRQF_DISABLED | IRQF_TRIGGER_RISING ,
" l3-app-irq " , l3 ) ;
if ( ret ) {
dev_err ( & pdev - > dev , " couldn't request app irq \n " ) ;
2011-04-18 20:39:41 +04:00
goto err2 ;
2011-03-07 17:05:20 +03:00
}
2011-04-18 20:39:41 +04:00
return 0 ;
2011-03-07 17:05:20 +03:00
err2 :
2011-04-18 20:39:41 +04:00
free_irq ( l3 - > debug_irq , l3 ) ;
2011-03-07 17:05:20 +03:00
err1 :
2011-04-18 20:39:41 +04:00
iounmap ( l3 - > rt ) ;
2011-03-07 17:05:20 +03:00
err0 :
2011-04-18 20:39:41 +04:00
kfree ( l3 ) ;
2011-03-07 17:05:20 +03:00
return ret ;
}
static int __exit omap3_l3_remove ( struct platform_device * pdev )
{
struct omap3_l3 * l3 = platform_get_drvdata ( pdev ) ;
free_irq ( l3 - > app_irq , l3 ) ;
free_irq ( l3 - > debug_irq , l3 ) ;
iounmap ( l3 - > rt ) ;
kfree ( l3 ) ;
return 0 ;
}
static struct platform_driver omap3_l3_driver = {
. remove = __exit_p ( omap3_l3_remove ) ,
. driver = {
. name = " omap_l3_smx " ,
} ,
} ;
static int __init omap3_l3_init ( void )
{
return platform_driver_probe ( & omap3_l3_driver , omap3_l3_probe ) ;
}
postcore_initcall_sync ( omap3_l3_init ) ;
static void __exit omap3_l3_exit ( void )
{
platform_driver_unregister ( & omap3_l3_driver ) ;
}
module_exit ( omap3_l3_exit ) ;