2010-03-16 09:04:46 +03:00
/*
2011-05-02 01:10:10 +04:00
* Copyright ( C ) 2011 Google , Inc .
2010-03-16 09:04:46 +03:00
*
* Author :
2011-05-02 01:10:10 +04:00
* Colin Cross < ccross @ android . com >
2010-03-16 09:04:46 +03:00
*
2010-04-06 07:30:59 +04:00
* Copyright ( C ) 2010 , NVIDIA Corporation
*
2010-03-16 09:04:46 +03: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 .
*
*/
# include <linux/kernel.h>
# include <linux/interrupt.h>
# include <linux/irq.h>
# include <linux/io.h>
2011-11-30 05:29:19 +04:00
# include <linux/of.h>
2010-03-16 09:04:46 +03:00
# include <asm/hardware/gic.h>
# include <mach/iomap.h>
# include "board.h"
2011-05-02 02:26:51 +04: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
2012-01-05 07:31:45 +04:00
static int num_ictlrs ;
2011-05-02 02:26:51 +04: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 07:31:45 +04:00
IO_ADDRESS ( TEGRA_QUINARY_ICTLR_BASE ) ,
2011-05-02 02:26:51 +04: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 07:31:45 +04:00
irq > = FIRST_LEGACY_IRQ + num_ictlrs * 32 ) ;
2011-05-02 02:26:51 +04:00
base = ictlr_reg_base [ ( irq - FIRST_LEGACY_IRQ ) / 32 ] ;
mask = BIT ( ( irq - FIRST_LEGACY_IRQ ) % 32 ) ;
__raw_writel ( mask , base + reg ) ;
}
2010-11-29 09:23:55 +03:00
static void tegra_mask ( struct irq_data * d )
{
2011-05-02 02:26:51 +04:00
if ( d - > irq < FIRST_LEGACY_IRQ )
return ;
tegra_irq_write_mask ( d - > irq , ICTLR_CPU_IER_CLR ) ;
2010-11-29 09:23:55 +03:00
}
2010-04-06 07:30:59 +04:00
2010-11-29 09:23:55 +03:00
static void tegra_unmask ( struct irq_data * d )
2010-04-06 07:30:59 +04:00
{
2011-05-02 02:26:51 +04:00
if ( d - > irq < FIRST_LEGACY_IRQ )
return ;
tegra_irq_write_mask ( d - > irq , ICTLR_CPU_IER_SET ) ;
2010-04-06 07:30:59 +04:00
}
2011-02-10 09:17:17 +03:00
static void tegra_ack ( struct irq_data * d )
{
2011-05-02 02:26:51 +04:00
if ( d - > irq < FIRST_LEGACY_IRQ )
return ;
tegra_irq_write_mask ( d - > irq , ICTLR_CPU_IEP_FIR_CLR ) ;
2011-02-10 09:17:17 +03:00
}
2011-05-02 02:27:34 +04:00
static void tegra_eoi ( struct irq_data * d )
{
if ( d - > irq < FIRST_LEGACY_IRQ )
return ;
tegra_irq_write_mask ( d - > irq , ICTLR_CPU_IEP_FIR_CLR ) ;
}
2011-02-10 09:17:17 +03:00
static int tegra_retrigger ( struct irq_data * d )
{
2011-05-02 02:26:51 +04:00
if ( d - > irq < FIRST_LEGACY_IRQ )
2011-05-02 01:10:10 +04:00
return 0 ;
2011-05-02 02:26:51 +04:00
tegra_irq_write_mask ( d - > irq , ICTLR_CPU_IEP_FIR_SET ) ;
2011-02-10 09:17:17 +03:00
return 1 ;
}
2010-03-16 09:04:46 +03:00
void __init tegra_init_irq ( void )
{
2011-05-02 02:26:51 +04:00
int i ;
2012-01-05 07:31:45 +04: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-02 02:26:51 +04:00
2012-01-05 07:31:45 +04:00
for ( i = 0 ; i < num_ictlrs ; i + + ) {
2011-05-02 02:26:51 +04:00
void __iomem * ictlr = ictlr_reg_base [ i ] ;
writel ( ~ 0 , ictlr + ICTLR_CPU_IER_CLR ) ;
writel ( 0 , ictlr + ICTLR_CPU_IEP_CLASS ) ;
}
2010-04-06 07:30:59 +04:00
2011-05-02 01:10:10 +04:00
gic_arch_extn . irq_ack = tegra_ack ;
2011-05-02 02:27:34 +04:00
gic_arch_extn . irq_eoi = tegra_eoi ;
2011-05-02 01:10:10 +04:00
gic_arch_extn . irq_mask = tegra_mask ;
gic_arch_extn . irq_unmask = tegra_unmask ;
gic_arch_extn . irq_retrigger = tegra_retrigger ;
2011-11-30 05:29:19 +04:00
/*
* Check if there is a devicetree present , since the GIC will be
* initialized elsewhere under DT .
*/
if ( ! of_have_populated_dt ( ) )
2012-01-05 07:31:45 +04:00
gic_init ( 0 , 29 , distbase ,
2011-11-30 05:29:19 +04:00
IO_ADDRESS ( TEGRA_ARM_PERIF_BASE + 0x100 ) ) ;
2010-04-06 07:30:59 +04:00
}