2010-03-15 23:04:46 -07:00
/*
2011-05-01 14:10:10 -07:00
* Copyright ( C ) 2011 Google , Inc .
2010-03-15 23:04:46 -07:00
*
* Author :
2011-05-01 14:10:10 -07:00
* Colin Cross < ccross @ android . com >
2010-03-15 23:04:46 -07:00
*
2013-04-03 19:31:45 +08:00
* Copyright ( C ) 2010 , 2013 , NVIDIA Corporation
2010-04-05 20:30:59 -07:00
*
2010-03-15 23:04:46 -07:00
* This software is licensed under the terms of the GNU General Public
* License version 2 , as published by the Free Software Foundation , and
* may be copied , distributed , and modified under those terms .
*
* 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 .
*
*/
2013-07-19 17:25:24 +08:00
# include <linux/cpu_pm.h>
2010-03-15 23:04:46 -07:00
# include <linux/interrupt.h>
# include <linux/io.h>
2012-12-27 13:10:24 -06:00
# include <linux/irqchip/arm-gic.h>
2014-07-11 09:44:49 +02:00
# include <linux/irq.h>
# include <linux/kernel.h>
# include <linux/of_address.h>
# include <linux/of.h>
2013-04-03 19:31:45 +08:00
# include <linux/syscore_ops.h>
2010-03-15 23:04:46 -07:00
# include "board.h"
2012-10-04 14:24:09 -06:00
# include "iomap.h"
2010-03-15 23:04:46 -07:00
2011-05-01 15:26:51 -07:00
# define ICTLR_CPU_IEP_VFIQ 0x08
# define ICTLR_CPU_IEP_FIR 0x14
# define ICTLR_CPU_IEP_FIR_SET 0x18
# define ICTLR_CPU_IEP_FIR_CLR 0x1c
# define ICTLR_CPU_IER 0x20
# define ICTLR_CPU_IER_SET 0x24
# define ICTLR_CPU_IER_CLR 0x28
# define ICTLR_CPU_IEP_CLASS 0x2C
# define ICTLR_COP_IER 0x30
# define ICTLR_COP_IER_SET 0x34
# define ICTLR_COP_IER_CLR 0x38
# define ICTLR_COP_IEP_CLASS 0x3c
# define FIRST_LEGACY_IRQ 32
2013-04-03 19:31:45 +08:00
# define TEGRA_MAX_NUM_ICTLRS 5
2011-05-01 15:26:51 -07:00
2013-01-15 22:10:26 +00:00
# define SGI_MASK 0xFFFF
2012-01-05 03:31:45 +00:00
static int num_ictlrs ;
2011-05-01 15:26:51 -07:00
static void __iomem * ictlr_reg_base [ ] = {
IO_ADDRESS ( TEGRA_PRIMARY_ICTLR_BASE ) ,
IO_ADDRESS ( TEGRA_SECONDARY_ICTLR_BASE ) ,
IO_ADDRESS ( TEGRA_TERTIARY_ICTLR_BASE ) ,
IO_ADDRESS ( TEGRA_QUATERNARY_ICTLR_BASE ) ,
2012-01-05 03:31:45 +00:00
IO_ADDRESS ( TEGRA_QUINARY_ICTLR_BASE ) ,
2011-05-01 15:26:51 -07:00
} ;
2013-04-03 19:31:45 +08:00
# ifdef CONFIG_PM_SLEEP
static u32 cop_ier [ TEGRA_MAX_NUM_ICTLRS ] ;
static u32 cop_iep [ TEGRA_MAX_NUM_ICTLRS ] ;
static u32 cpu_ier [ TEGRA_MAX_NUM_ICTLRS ] ;
static u32 cpu_iep [ TEGRA_MAX_NUM_ICTLRS ] ;
static u32 ictlr_wake_mask [ TEGRA_MAX_NUM_ICTLRS ] ;
2013-07-19 17:25:24 +08:00
static void __iomem * tegra_gic_cpu_base ;
2013-04-03 19:31:45 +08:00
# endif
2013-01-15 22:10:26 +00:00
bool tegra_pending_sgi ( void )
{
u32 pending_set ;
void __iomem * distbase = IO_ADDRESS ( TEGRA_ARM_INT_DIST_BASE ) ;
pending_set = readl_relaxed ( distbase + GIC_DIST_PENDING_SET ) ;
if ( pending_set & SGI_MASK )
return true ;
return false ;
}
2011-05-01 15:26:51 -07:00
static inline void tegra_irq_write_mask ( unsigned int irq , unsigned long reg )
{
void __iomem * base ;
u32 mask ;
BUG_ON ( irq < FIRST_LEGACY_IRQ | |
2012-01-05 03:31:45 +00:00
irq > = FIRST_LEGACY_IRQ + num_ictlrs * 32 ) ;
2011-05-01 15:26:51 -07:00
base = ictlr_reg_base [ ( irq - FIRST_LEGACY_IRQ ) / 32 ] ;
mask = BIT ( ( irq - FIRST_LEGACY_IRQ ) % 32 ) ;
__raw_writel ( mask , base + reg ) ;
}
2010-11-28 22:23:55 -08:00
static void tegra_mask ( struct irq_data * d )
{
2014-11-26 17:55:31 +00:00
if ( d - > hwirq < FIRST_LEGACY_IRQ )
2011-05-01 15:26:51 -07:00
return ;
2014-11-26 17:55:31 +00:00
tegra_irq_write_mask ( d - > hwirq , ICTLR_CPU_IER_CLR ) ;
2010-11-28 22:23:55 -08:00
}
2010-04-05 20:30:59 -07:00
2010-11-28 22:23:55 -08:00
static void tegra_unmask ( struct irq_data * d )
2010-04-05 20:30:59 -07:00
{
2014-11-26 17:55:31 +00:00
if ( d - > hwirq < FIRST_LEGACY_IRQ )
2011-05-01 15:26:51 -07:00
return ;
2014-11-26 17:55:31 +00:00
tegra_irq_write_mask ( d - > hwirq , ICTLR_CPU_IER_SET ) ;
2010-04-05 20:30:59 -07:00
}
2011-02-09 22:17:17 -08:00
static void tegra_ack ( struct irq_data * d )
{
2014-11-26 17:55:31 +00:00
if ( d - > hwirq < FIRST_LEGACY_IRQ )
2011-05-01 15:26:51 -07:00
return ;
2014-11-26 17:55:31 +00:00
tegra_irq_write_mask ( d - > hwirq , ICTLR_CPU_IEP_FIR_CLR ) ;
2011-02-09 22:17:17 -08:00
}
2011-05-01 15:27:34 -07:00
static void tegra_eoi ( struct irq_data * d )
{
2014-11-26 17:55:31 +00:00
if ( d - > hwirq < FIRST_LEGACY_IRQ )
2011-05-01 15:27:34 -07:00
return ;
2014-11-26 17:55:31 +00:00
tegra_irq_write_mask ( d - > hwirq , ICTLR_CPU_IEP_FIR_CLR ) ;
2011-05-01 15:27:34 -07:00
}
2011-02-09 22:17:17 -08:00
static int tegra_retrigger ( struct irq_data * d )
{
2014-11-26 17:55:31 +00:00
if ( d - > hwirq < FIRST_LEGACY_IRQ )
2011-05-01 14:10:10 -07:00
return 0 ;
2014-11-26 17:55:31 +00:00
tegra_irq_write_mask ( d - > hwirq , ICTLR_CPU_IEP_FIR_SET ) ;
2011-05-01 15:26:51 -07:00
2011-02-09 22:17:17 -08:00
return 1 ;
}
2013-04-03 19:31:45 +08:00
# ifdef CONFIG_PM_SLEEP
static int tegra_set_wake ( struct irq_data * d , unsigned int enable )
{
2014-11-26 17:55:31 +00:00
u32 irq = d - > hwirq ;
2013-04-03 19:31:45 +08:00
u32 index , mask ;
if ( irq < FIRST_LEGACY_IRQ | |
irq > = FIRST_LEGACY_IRQ + num_ictlrs * 32 )
return - EINVAL ;
index = ( ( irq - FIRST_LEGACY_IRQ ) / 32 ) ;
mask = BIT ( ( irq - FIRST_LEGACY_IRQ ) % 32 ) ;
if ( enable )
ictlr_wake_mask [ index ] | = mask ;
else
ictlr_wake_mask [ index ] & = ~ mask ;
return 0 ;
}
static int tegra_legacy_irq_suspend ( void )
{
unsigned long flags ;
int i ;
local_irq_save ( flags ) ;
for ( i = 0 ; i < num_ictlrs ; i + + ) {
void __iomem * ictlr = ictlr_reg_base [ i ] ;
/* Save interrupt state */
cpu_ier [ i ] = readl_relaxed ( ictlr + ICTLR_CPU_IER ) ;
cpu_iep [ i ] = readl_relaxed ( ictlr + ICTLR_CPU_IEP_CLASS ) ;
cop_ier [ i ] = readl_relaxed ( ictlr + ICTLR_COP_IER ) ;
cop_iep [ i ] = readl_relaxed ( ictlr + ICTLR_COP_IEP_CLASS ) ;
/* Disable COP interrupts */
writel_relaxed ( ~ 0ul , ictlr + ICTLR_COP_IER_CLR ) ;
/* Disable CPU interrupts */
writel_relaxed ( ~ 0ul , ictlr + ICTLR_CPU_IER_CLR ) ;
/* Enable the wakeup sources of ictlr */
writel_relaxed ( ictlr_wake_mask [ i ] , ictlr + ICTLR_CPU_IER_SET ) ;
}
local_irq_restore ( flags ) ;
return 0 ;
}
static void tegra_legacy_irq_resume ( void )
{
unsigned long flags ;
int i ;
local_irq_save ( flags ) ;
for ( i = 0 ; i < num_ictlrs ; i + + ) {
void __iomem * ictlr = ictlr_reg_base [ i ] ;
writel_relaxed ( cpu_iep [ i ] , ictlr + ICTLR_CPU_IEP_CLASS ) ;
writel_relaxed ( ~ 0ul , ictlr + ICTLR_CPU_IER_CLR ) ;
writel_relaxed ( cpu_ier [ i ] , ictlr + ICTLR_CPU_IER_SET ) ;
writel_relaxed ( cop_iep [ i ] , ictlr + ICTLR_COP_IEP_CLASS ) ;
writel_relaxed ( ~ 0ul , ictlr + ICTLR_COP_IER_CLR ) ;
writel_relaxed ( cop_ier [ i ] , ictlr + ICTLR_COP_IER_SET ) ;
}
local_irq_restore ( flags ) ;
}
static struct syscore_ops tegra_legacy_irq_syscore_ops = {
. suspend = tegra_legacy_irq_suspend ,
. resume = tegra_legacy_irq_resume ,
} ;
int tegra_legacy_irq_syscore_init ( void )
{
register_syscore_ops ( & tegra_legacy_irq_syscore_ops ) ;
return 0 ;
}
2013-07-19 17:25:24 +08:00
static int tegra_gic_notifier ( struct notifier_block * self ,
unsigned long cmd , void * v )
{
switch ( cmd ) {
case CPU_PM_ENTER :
writel_relaxed ( 0x1E0 , tegra_gic_cpu_base + GIC_CPU_CTRL ) ;
break ;
}
return NOTIFY_OK ;
}
static struct notifier_block tegra_gic_notifier_block = {
. notifier_call = tegra_gic_notifier ,
} ;
static const struct of_device_id tegra114_dt_gic_match [ ] __initconst = {
{ . compatible = " arm,cortex-a15-gic " } ,
{ }
} ;
static void tegra114_gic_cpu_pm_registration ( void )
{
struct device_node * dn ;
dn = of_find_matching_node ( NULL , tegra114_dt_gic_match ) ;
if ( ! dn )
return ;
tegra_gic_cpu_base = of_iomap ( dn , 1 ) ;
cpu_pm_register_notifier ( & tegra_gic_notifier_block ) ;
}
2013-04-03 19:31:45 +08:00
# else
# define tegra_set_wake NULL
2013-07-19 17:25:24 +08:00
static void tegra114_gic_cpu_pm_registration ( void ) { }
2013-04-03 19:31:45 +08:00
# endif
2010-03-15 23:04:46 -07:00
void __init tegra_init_irq ( void )
{
2011-05-01 15:26:51 -07:00
int i ;
2012-01-05 03:31:45 +00:00
void __iomem * distbase ;
distbase = IO_ADDRESS ( TEGRA_ARM_INT_DIST_BASE ) ;
num_ictlrs = readl_relaxed ( distbase + GIC_DIST_CTR ) & 0x1f ;
if ( num_ictlrs > ARRAY_SIZE ( ictlr_reg_base ) ) {
WARN ( 1 , " Too many (%d) interrupt controllers found. Maximum is %d. " ,
num_ictlrs , ARRAY_SIZE ( ictlr_reg_base ) ) ;
num_ictlrs = ARRAY_SIZE ( ictlr_reg_base ) ;
}
2011-05-01 15:26:51 -07:00
2012-01-05 03:31:45 +00:00
for ( i = 0 ; i < num_ictlrs ; i + + ) {
2011-05-01 15:26:51 -07:00
void __iomem * ictlr = ictlr_reg_base [ i ] ;
writel ( ~ 0 , ictlr + ICTLR_CPU_IER_CLR ) ;
writel ( 0 , ictlr + ICTLR_CPU_IEP_CLASS ) ;
}
2010-04-05 20:30:59 -07:00
2011-05-01 14:10:10 -07:00
gic_arch_extn . irq_ack = tegra_ack ;
2011-05-01 15:27:34 -07:00
gic_arch_extn . irq_eoi = tegra_eoi ;
2011-05-01 14:10:10 -07:00
gic_arch_extn . irq_mask = tegra_mask ;
gic_arch_extn . irq_unmask = tegra_unmask ;
gic_arch_extn . irq_retrigger = tegra_retrigger ;
2013-04-03 19:31:45 +08:00
gic_arch_extn . irq_set_wake = tegra_set_wake ;
gic_arch_extn . flags = IRQCHIP_MASK_ON_SUSPEND ;
2011-05-01 14:10:10 -07:00
2013-07-19 17:25:24 +08:00
tegra114_gic_cpu_pm_registration ( ) ;
2010-04-05 20:30:59 -07:00
}