2007-07-10 01:06:53 +04:00
/*
2008-07-05 12:02:54 +04:00
* Copyright 2004 - 2007 Freescale Semiconductor , Inc . All Rights Reserved .
* Copyright 2008 Juergen Beisert , kernel @ pengutronix . de
*
* 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 . , 51 Franklin Street , Fifth Floor , Boston ,
* MA 02110 - 1301 , USA .
2007-07-10 01:06:53 +04:00
*/
2008-11-14 13:01:39 +03:00
# include <linux/module.h>
2008-07-05 12:02:54 +04:00
# include <linux/irq.h>
2012-06-13 06:55:46 +04:00
# include <linux/irqdomain.h>
2008-09-06 15:10:45 +04:00
# include <linux/io.h>
2012-06-13 06:55:46 +04:00
# include <linux/of.h>
2008-11-14 13:01:39 +03:00
# include <asm/mach/irq.h>
2011-11-03 13:31:26 +04:00
# include <asm/exception.h>
2007-07-10 01:06:53 +04:00
2012-09-13 17:01:00 +04:00
# include "common.h"
2012-09-14 10:14:45 +04:00
# include "hardware.h"
2010-12-06 14:37:38 +03:00
# include "irq-common.h"
2009-02-18 22:59:04 +03:00
# define AVIC_INTCNTL 0x00 /* int control reg */
# define AVIC_NIMASK 0x04 /* int mask reg */
# define AVIC_INTENNUM 0x08 /* int enable number reg */
# define AVIC_INTDISNUM 0x0C /* int disable number reg */
# define AVIC_INTENABLEH 0x10 /* int enable reg high */
# define AVIC_INTENABLEL 0x14 /* int enable reg low */
# define AVIC_INTTYPEH 0x18 /* int type reg high */
# define AVIC_INTTYPEL 0x1C /* int type reg low */
# define AVIC_NIPRIORITY(x) (0x20 + 4 * (7 - (x))) /* int priority */
# define AVIC_NIVECSR 0x40 /* norm int vector/status */
# define AVIC_FIVECSR 0x44 /* fast int vector/status */
# define AVIC_INTSRCH 0x48 /* int source reg high */
# define AVIC_INTSRCL 0x4C /* int source reg low */
# define AVIC_INTFRCH 0x50 /* int force reg high */
# define AVIC_INTFRCL 0x54 /* int force reg low */
# define AVIC_NIPNDH 0x58 /* norm int pending high */
# define AVIC_NIPNDL 0x5C /* norm int pending low */
# define AVIC_FIPNDH 0x60 /* fast int pending high */
# define AVIC_FIPNDL 0x64 /* fast int pending low */
2011-05-10 20:16:10 +04:00
# define AVIC_NUM_IRQS 64
2009-05-25 12:50:52 +04:00
void __iomem * avic_base ;
2012-06-13 06:55:46 +04:00
static struct irq_domain * domain ;
2008-07-05 12:02:54 +04:00
2011-09-22 13:40:08 +04:00
static u32 avic_saved_mask_reg [ 2 ] ;
2009-04-08 17:17:50 +04:00
# ifdef CONFIG_MXC_IRQ_PRIOR
2010-12-06 14:37:38 +03:00
static int avic_irq_set_priority ( unsigned char irq , unsigned char prio )
{
2012-06-13 06:55:46 +04:00
struct irq_data * d = irq_get_irq_data ( irq ) ;
2008-09-09 13:29:41 +04:00
unsigned int temp ;
unsigned int mask = 0x0F < < irq % 8 * 4 ;
2012-06-13 06:55:46 +04:00
irq = d - > hwirq ;
2011-05-10 20:16:10 +04:00
if ( irq > = AVIC_NUM_IRQS )
2012-02-27 02:09:40 +04:00
return - EINVAL ;
2008-09-09 13:29:41 +04:00
2009-02-18 22:59:04 +03:00
temp = __raw_readl ( avic_base + AVIC_NIPRIORITY ( irq / 8 ) ) ;
2008-09-09 13:29:41 +04:00
temp & = ~ mask ;
temp | = prio & mask ;
2009-02-18 22:59:04 +03:00
__raw_writel ( temp , avic_base + AVIC_NIPRIORITY ( irq / 8 ) ) ;
2009-04-08 17:17:50 +04:00
return 0 ;
2008-09-09 13:29:41 +04:00
}
2010-12-06 14:37:38 +03:00
# endif
2008-09-09 13:29:41 +04:00
2008-11-14 13:01:39 +03:00
# ifdef CONFIG_FIQ
2010-12-06 14:37:38 +03:00
static int avic_set_irq_fiq ( unsigned int irq , unsigned int type )
2008-11-14 13:01:39 +03:00
{
2012-06-13 06:55:46 +04:00
struct irq_data * d = irq_get_irq_data ( irq ) ;
2008-11-14 13:01:39 +03:00
unsigned int irqt ;
2012-06-13 06:55:46 +04:00
irq = d - > hwirq ;
2011-05-10 20:16:10 +04:00
if ( irq > = AVIC_NUM_IRQS )
2008-11-14 13:01:39 +03:00
return - EINVAL ;
2011-05-10 20:16:10 +04:00
if ( irq < AVIC_NUM_IRQS / 2 ) {
2009-02-18 22:59:04 +03:00
irqt = __raw_readl ( avic_base + AVIC_INTTYPEL ) & ~ ( 1 < < irq ) ;
__raw_writel ( irqt | ( ! ! type < < irq ) , avic_base + AVIC_INTTYPEL ) ;
2008-11-14 13:01:39 +03:00
} else {
2011-05-10 20:16:10 +04:00
irq - = AVIC_NUM_IRQS / 2 ;
2009-02-18 22:59:04 +03:00
irqt = __raw_readl ( avic_base + AVIC_INTTYPEH ) & ~ ( 1 < < irq ) ;
__raw_writel ( irqt | ( ! ! type < < irq ) , avic_base + AVIC_INTTYPEH ) ;
2008-11-14 13:01:39 +03:00
}
return 0 ;
}
# endif /* CONFIG_FIQ */
2007-07-10 01:06:53 +04:00
2011-09-22 13:40:08 +04:00
static struct mxc_extra_irq avic_extra_irq = {
2010-12-06 14:37:38 +03:00
# ifdef CONFIG_MXC_IRQ_PRIOR
. set_priority = avic_irq_set_priority ,
# endif
# ifdef CONFIG_FIQ
. set_irq_fiq = avic_set_irq_fiq ,
# endif
2007-07-10 01:06:53 +04:00
} ;
2011-09-22 13:40:08 +04:00
# ifdef CONFIG_PM
static void avic_irq_suspend ( struct irq_data * d )
{
struct irq_chip_generic * gc = irq_data_get_irq_chip_data ( d ) ;
struct irq_chip_type * ct = gc - > chip_types ;
2012-06-13 06:55:46 +04:00
int idx = d - > hwirq > > 5 ;
2011-09-22 13:40:08 +04:00
avic_saved_mask_reg [ idx ] = __raw_readl ( avic_base + ct - > regs . mask ) ;
__raw_writel ( gc - > wake_active , avic_base + ct - > regs . mask ) ;
}
static void avic_irq_resume ( struct irq_data * d )
{
struct irq_chip_generic * gc = irq_data_get_irq_chip_data ( d ) ;
struct irq_chip_type * ct = gc - > chip_types ;
2012-06-13 06:55:46 +04:00
int idx = d - > hwirq > > 5 ;
2011-09-22 13:40:08 +04:00
__raw_writel ( avic_saved_mask_reg [ idx ] , avic_base + ct - > regs . mask ) ;
}
# else
# define avic_irq_suspend NULL
# define avic_irq_resume NULL
# endif
2012-06-13 06:55:46 +04:00
static __init void avic_init_gc ( int idx , unsigned int irq_start )
2011-09-22 13:40:08 +04:00
{
struct irq_chip_generic * gc ;
struct irq_chip_type * ct ;
gc = irq_alloc_generic_chip ( " mxc-avic " , 1 , irq_start , avic_base ,
handle_level_irq ) ;
gc - > private = & avic_extra_irq ;
gc - > wake_enabled = IRQ_MSK ( 32 ) ;
ct = gc - > chip_types ;
ct - > chip . irq_mask = irq_gc_mask_clr_bit ;
ct - > chip . irq_unmask = irq_gc_mask_set_bit ;
ct - > chip . irq_ack = irq_gc_mask_clr_bit ;
ct - > chip . irq_set_wake = irq_gc_set_wake ;
ct - > chip . irq_suspend = avic_irq_suspend ;
ct - > chip . irq_resume = avic_irq_resume ;
ct - > regs . mask = ! idx ? AVIC_INTENABLEL : AVIC_INTENABLEH ;
ct - > regs . ack = ct - > regs . mask ;
irq_setup_generic_chip ( gc , IRQ_MSK ( 32 ) , 0 , IRQ_NOREQUEST , 0 ) ;
}
2011-09-20 16:28:17 +04:00
asmlinkage void __exception_irq_entry avic_handle_irq ( struct pt_regs * regs )
{
u32 nivector ;
do {
nivector = __raw_readl ( avic_base + AVIC_NIVECSR ) > > 16 ;
if ( nivector = = 0xffff )
break ;
2012-06-13 06:55:46 +04:00
handle_IRQ ( irq_find_mapping ( domain , nivector ) , regs ) ;
2011-09-20 16:28:17 +04:00
} while ( 1 ) ;
}
2008-03-28 13:02:13 +03:00
/*
2007-07-10 01:06:53 +04:00
* This function initializes the AVIC hardware and disables all the
* interrupts . It registers the interrupt enable and disable functions
* to the kernel for each interrupt source .
*/
2009-05-25 19:36:19 +04:00
void __init mxc_init_irq ( void __iomem * irqbase )
2007-07-10 01:06:53 +04:00
{
2012-06-13 06:55:46 +04:00
struct device_node * np ;
int irq_base ;
2007-07-10 01:06:53 +04:00
int i ;
2009-05-25 19:36:19 +04:00
avic_base = irqbase ;
2009-02-18 22:59:04 +03:00
2007-07-10 01:06:53 +04:00
/* put the AVIC into the reset value with
* all interrupts disabled
*/
2009-02-18 22:59:04 +03:00
__raw_writel ( 0 , avic_base + AVIC_INTCNTL ) ;
__raw_writel ( 0x1f , avic_base + AVIC_NIMASK ) ;
2007-07-10 01:06:53 +04:00
/* disable all interrupts */
2009-02-18 22:59:04 +03:00
__raw_writel ( 0 , avic_base + AVIC_INTENABLEH ) ;
__raw_writel ( 0 , avic_base + AVIC_INTENABLEL ) ;
2007-07-10 01:06:53 +04:00
/* all IRQ no FIQ */
2009-02-18 22:59:04 +03:00
__raw_writel ( 0 , avic_base + AVIC_INTTYPEH ) ;
__raw_writel ( 0 , avic_base + AVIC_INTTYPEL ) ;
2011-09-22 13:40:08 +04:00
2012-06-13 06:55:46 +04:00
irq_base = irq_alloc_descs ( - 1 , 0 , AVIC_NUM_IRQS , numa_node_id ( ) ) ;
WARN_ON ( irq_base < 0 ) ;
np = of_find_compatible_node ( NULL , NULL , " fsl,avic " ) ;
domain = irq_domain_add_legacy ( np , AVIC_NUM_IRQS , irq_base , 0 ,
& irq_domain_simple_ops , NULL ) ;
WARN_ON ( ! domain ) ;
for ( i = 0 ; i < AVIC_NUM_IRQS / 32 ; i + + , irq_base + = 32 )
avic_init_gc ( i , irq_base ) ;
2007-07-10 01:06:53 +04:00
2008-09-09 13:29:41 +04:00
/* Set default priority value (0) for all IRQ's */
for ( i = 0 ; i < 8 ; i + + )
2009-02-18 22:59:04 +03:00
__raw_writel ( 0 , avic_base + AVIC_NIPRIORITY ( i ) ) ;
2007-07-10 01:06:53 +04:00
2008-11-14 13:01:39 +03:00
# ifdef CONFIG_FIQ
/* Initialize FIQ */
2012-06-28 10:42:08 +04:00
init_FIQ ( FIQ_START ) ;
2008-11-14 13:01:39 +03:00
# endif
2007-07-10 01:06:53 +04:00
printk ( KERN_INFO " MXC IRQ initialized \n " ) ;
}