2012-05-10 11:42:30 +04:00
/*
* Tegra20 Memory Controller
*
* Copyright ( c ) 2012 , NVIDIA CORPORATION . All rights reserved .
*
* This program is free software ; you can redistribute it and / or modify it
* under the terms and conditions of the GNU General Public License ,
* version 2 , as published by the Free Software Foundation .
*
* This program is distributed in the hope 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 . ,
* 51 Franklin St - Fifth Floor , Boston , MA 02110 - 1301 USA .
*/
2013-01-21 14:09:08 +04:00
# include <linux/err.h>
2012-05-10 11:42:30 +04:00
# include <linux/kernel.h>
# include <linux/module.h>
# include <linux/ratelimit.h>
# include <linux/platform_device.h>
# include <linux/interrupt.h>
# include <linux/io.h>
# define DRV_NAME "tegra20-mc"
# define MC_INTSTATUS 0x0
# define MC_INTMASK 0x4
# define MC_INT_ERR_SHIFT 6
# define MC_INT_ERR_MASK (0x1f << MC_INT_ERR_SHIFT)
# define MC_INT_DECERR_EMEM BIT(MC_INT_ERR_SHIFT)
# define MC_INT_INVALID_GART_PAGE BIT(MC_INT_ERR_SHIFT + 1)
# define MC_INT_SECURITY_VIOLATION BIT(MC_INT_ERR_SHIFT + 2)
# define MC_INT_ARBITRATION_EMEM BIT(MC_INT_ERR_SHIFT + 3)
# define MC_GART_ERROR_REQ 0x30
# define MC_DECERR_EMEM_OTHERS_STATUS 0x58
# define MC_SECURITY_VIOLATION_STATUS 0x74
# define SECURITY_VIOLATION_TYPE BIT(30) /* 0=TRUSTZONE, 1=CARVEOUT */
# define MC_CLIENT_ID_MASK 0x3f
# define NUM_MC_REG_BANKS 2
struct tegra20_mc {
void __iomem * regs [ NUM_MC_REG_BANKS ] ;
struct device * dev ;
} ;
static inline u32 mc_readl ( struct tegra20_mc * mc , u32 offs )
{
2012-05-11 14:04:44 +04:00
u32 val = 0 ;
2012-05-10 11:42:30 +04:00
if ( offs < 0x24 )
2012-05-11 14:04:44 +04:00
val = readl ( mc - > regs [ 0 ] + offs ) ;
2012-09-20 05:32:53 +04:00
else if ( offs < 0x400 )
2012-05-11 14:04:44 +04:00
val = readl ( mc - > regs [ 1 ] + offs - 0x3c ) ;
return val ;
2012-05-10 11:42:30 +04:00
}
static inline void mc_writel ( struct tegra20_mc * mc , u32 val , u32 offs )
{
2012-09-20 05:32:53 +04:00
if ( offs < 0x24 )
2012-05-10 11:42:30 +04:00
writel ( val , mc - > regs [ 0 ] + offs ) ;
2012-09-20 05:32:53 +04:00
else if ( offs < 0x400 )
2012-05-10 11:42:30 +04:00
writel ( val , mc - > regs [ 1 ] + offs - 0x3c ) ;
}
static const char * const tegra20_mc_client [ ] = {
" cbr_display0a " ,
" cbr_display0ab " ,
" cbr_display0b " ,
" cbr_display0bb " ,
" cbr_display0c " ,
" cbr_display0cb " ,
" cbr_display1b " ,
" cbr_display1bb " ,
" cbr_eppup " ,
" cbr_g2pr " ,
" cbr_g2sr " ,
" cbr_mpeunifbr " ,
" cbr_viruv " ,
" csr_avpcarm7r " ,
" csr_displayhc " ,
" csr_displayhcb " ,
" csr_fdcdrd " ,
" csr_g2dr " ,
" csr_host1xdmar " ,
" csr_host1xr " ,
" csr_idxsrd " ,
" csr_mpcorer " ,
" csr_mpe_ipred " ,
" csr_mpeamemrd " ,
" csr_mpecsrd " ,
" csr_ppcsahbdmar " ,
" csr_ppcsahbslvr " ,
" csr_texsrd " ,
" csr_vdebsevr " ,
" csr_vdember " ,
" csr_vdemcer " ,
" csr_vdetper " ,
" cbw_eppu " ,
" cbw_eppv " ,
" cbw_eppy " ,
" cbw_mpeunifbw " ,
" cbw_viwsb " ,
" cbw_viwu " ,
" cbw_viwv " ,
" cbw_viwy " ,
" ccw_g2dw " ,
" csw_avpcarm7w " ,
" csw_fdcdwr " ,
" csw_host1xw " ,
" csw_ispw " ,
" csw_mpcorew " ,
" csw_mpecswr " ,
" csw_ppcsahbdmaw " ,
" csw_ppcsahbslvw " ,
" csw_vdebsevw " ,
" csw_vdembew " ,
" csw_vdetpmw " ,
} ;
static void tegra20_mc_decode ( struct tegra20_mc * mc , int n )
{
u32 addr , req ;
const char * client = " Unknown " ;
int idx , cid ;
const struct reg_info {
u32 offset ;
u32 write_bit ; /* 0=READ, 1=WRITE */
int cid_shift ;
char * message ;
} reg [ ] = {
{
. offset = MC_DECERR_EMEM_OTHERS_STATUS ,
. write_bit = 31 ,
. message = " MC_DECERR " ,
} ,
{
. offset = MC_GART_ERROR_REQ ,
. cid_shift = 1 ,
. message = " MC_GART_ERR " ,
} ,
{
. offset = MC_SECURITY_VIOLATION_STATUS ,
. write_bit = 31 ,
. message = " MC_SECURITY_ERR " ,
} ,
} ;
idx = n - MC_INT_ERR_SHIFT ;
if ( ( idx < 0 ) | | ( idx > = ARRAY_SIZE ( reg ) ) ) {
2012-05-14 14:07:03 +04:00
dev_err_ratelimited ( mc - > dev , " Unknown interrupt status %08lx \n " ,
BIT ( n ) ) ;
2012-05-10 11:42:30 +04:00
return ;
}
req = mc_readl ( mc , reg [ idx ] . offset ) ;
cid = ( req > > reg [ idx ] . cid_shift ) & MC_CLIENT_ID_MASK ;
if ( cid < ARRAY_SIZE ( tegra20_mc_client ) )
client = tegra20_mc_client [ cid ] ;
addr = mc_readl ( mc , reg [ idx ] . offset + sizeof ( u32 ) ) ;
2012-05-14 14:07:03 +04:00
dev_err_ratelimited ( mc - > dev , " %s (0x%08x): 0x%08x %s (%s %s) \n " ,
2012-05-10 11:42:30 +04:00
reg [ idx ] . message , req , addr , client ,
( req & BIT ( reg [ idx ] . write_bit ) ) ? " write " : " read " ,
( reg [ idx ] . offset = = MC_SECURITY_VIOLATION_STATUS ) ?
( ( req & SECURITY_VIOLATION_TYPE ) ?
" carveout " : " trustzone " ) : " " ) ;
}
2012-12-22 03:06:41 +04:00
static const struct of_device_id tegra20_mc_of_match [ ] = {
2012-05-10 11:42:30 +04:00
{ . compatible = " nvidia,tegra20-mc " , } ,
{ } ,
} ;
static irqreturn_t tegra20_mc_isr ( int irq , void * data )
{
u32 stat , mask , bit ;
struct tegra20_mc * mc = data ;
stat = mc_readl ( mc , MC_INTSTATUS ) ;
mask = mc_readl ( mc , MC_INTMASK ) ;
mask & = stat ;
if ( ! mask )
return IRQ_NONE ;
2013-06-11 14:11:18 +04:00
while ( ( bit = ffs ( mask ) ) ! = 0 ) {
2012-05-10 11:42:30 +04:00
tegra20_mc_decode ( mc , bit - 1 ) ;
2013-06-11 14:11:18 +04:00
mask & = ~ BIT ( bit - 1 ) ;
}
2012-05-10 11:42:30 +04:00
mc_writel ( mc , stat , MC_INTSTATUS ) ;
return IRQ_HANDLED ;
}
2012-12-22 03:06:41 +04:00
static int tegra20_mc_probe ( struct platform_device * pdev )
2012-05-10 11:42:30 +04:00
{
struct resource * irq ;
struct tegra20_mc * mc ;
int i , err ;
u32 intmask ;
mc = devm_kzalloc ( & pdev - > dev , sizeof ( * mc ) , GFP_KERNEL ) ;
if ( ! mc )
return - ENOMEM ;
mc - > dev = & pdev - > dev ;
for ( i = 0 ; i < ARRAY_SIZE ( mc - > regs ) ; i + + ) {
struct resource * res ;
res = platform_get_resource ( pdev , IORESOURCE_MEM , i ) ;
2013-01-21 14:09:08 +04:00
mc - > regs [ i ] = devm_ioremap_resource ( & pdev - > dev , res ) ;
if ( IS_ERR ( mc - > regs [ i ] ) )
return PTR_ERR ( mc - > regs [ i ] ) ;
2012-05-10 11:42:30 +04:00
}
irq = platform_get_resource ( pdev , IORESOURCE_IRQ , 0 ) ;
if ( ! irq )
return - ENODEV ;
err = devm_request_irq ( & pdev - > dev , irq - > start , tegra20_mc_isr ,
IRQF_SHARED , dev_name ( & pdev - > dev ) , mc ) ;
if ( err )
return - ENODEV ;
platform_set_drvdata ( pdev , mc ) ;
intmask = MC_INT_INVALID_GART_PAGE |
MC_INT_DECERR_EMEM | MC_INT_SECURITY_VIOLATION ;
mc_writel ( mc , intmask , MC_INTMASK ) ;
return 0 ;
}
static struct platform_driver tegra20_mc_driver = {
. probe = tegra20_mc_probe ,
. driver = {
. name = DRV_NAME ,
. of_match_table = tegra20_mc_of_match ,
} ,
} ;
module_platform_driver ( tegra20_mc_driver ) ;
MODULE_AUTHOR ( " Hiroshi DOYU <hdoyu@nvidia.com> " ) ;
MODULE_DESCRIPTION ( " Tegra20 MC driver " ) ;
MODULE_LICENSE ( " GPL v2 " ) ;
MODULE_ALIAS ( " platform: " DRV_NAME ) ;