2019-05-19 16:51:31 +03:00
// SPDX-License-Identifier: GPL-2.0-or-later
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
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>
2021-02-05 04:38:46 +03:00
# include <linux/irqchip.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>
2018-02-28 00:29:15 +03:00
# include <linux/of_address.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
2018-02-28 00:29:15 +03:00
/* low power interrupt mask registers */
# define MX25_CCM_LPIMR0 0x68
# define MX25_CCM_LPIMR1 0x6C
2013-03-25 16:20:40 +04:00
static void __iomem * avic_base ;
2018-02-28 00:29:15 +03:00
static void __iomem * mx25_ccm_base ;
2012-06-13 06:55:46 +04:00
static struct irq_domain * domain ;
2008-07-05 12:02:54 +04:00
2008-11-14 13:01:39 +03:00
# ifdef CONFIG_FIQ
2016-06-19 09:55:53 +03:00
static int avic_set_irq_fiq ( unsigned int hwirq , unsigned int type )
2008-11-14 13:01:39 +03:00
{
unsigned int irqt ;
2016-06-19 09:55:53 +03:00
if ( hwirq > = AVIC_NUM_IRQS )
2008-11-14 13:01:39 +03:00
return - EINVAL ;
2016-06-19 09:55:53 +03:00
if ( hwirq < AVIC_NUM_IRQS / 2 ) {
irqt = imx_readl ( avic_base + AVIC_INTTYPEL ) & ~ ( 1 < < hwirq ) ;
imx_writel ( irqt | ( ! ! type < < hwirq ) , avic_base + AVIC_INTTYPEL ) ;
2008-11-14 13:01:39 +03:00
} else {
2016-06-19 09:55:53 +03:00
hwirq - = AVIC_NUM_IRQS / 2 ;
irqt = imx_readl ( avic_base + AVIC_INTTYPEH ) & ~ ( 1 < < hwirq ) ;
imx_writel ( irqt | ( ! ! type < < hwirq ) , 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_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
2013-02-05 21:36:16 +04:00
static u32 avic_saved_mask_reg [ 2 ] ;
2011-09-22 13:40:08 +04:00
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
2016-01-27 19:59:35 +03:00
avic_saved_mask_reg [ idx ] = imx_readl ( avic_base + ct - > regs . mask ) ;
imx_writel ( gc - > wake_active , avic_base + ct - > regs . mask ) ;
2018-02-28 00:29:15 +03:00
if ( mx25_ccm_base ) {
u8 offs = d - > hwirq < AVIC_NUM_IRQS / 2 ?
MX25_CCM_LPIMR0 : MX25_CCM_LPIMR1 ;
/*
* The interrupts which are still enabled will be used as wakeup
* sources . Allow those interrupts in low - power mode .
* The LPIMR registers use 0 to allow an interrupt , the AVIC
* registers use 1.
*/
imx_writel ( ~ gc - > wake_active , mx25_ccm_base + offs ) ;
}
2011-09-22 13:40:08 +04:00
}
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
2016-01-27 19:59:35 +03:00
imx_writel ( avic_saved_mask_reg [ idx ] , avic_base + ct - > regs . mask ) ;
2018-02-28 00:29:15 +03:00
if ( mx25_ccm_base ) {
u8 offs = d - > hwirq < AVIC_NUM_IRQS / 2 ?
MX25_CCM_LPIMR0 : MX25_CCM_LPIMR1 ;
imx_writel ( 0xffffffff , mx25_ccm_base + offs ) ;
}
2011-09-22 13:40:08 +04:00
}
# 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 ) ;
}
2014-05-11 11:35:57 +04:00
static void __exception_irq_entry avic_handle_irq ( struct pt_regs * regs )
2011-09-20 16:28:17 +04:00
{
u32 nivector ;
do {
2016-01-27 19:59:35 +03:00
nivector = imx_readl ( avic_base + AVIC_NIVECSR ) > > 16 ;
2011-09-20 16:28:17 +04:00
if ( nivector = = 0xffff )
break ;
2021-10-20 22:23:09 +03:00
generic_handle_domain_irq ( domain , nivector ) ;
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 .
*/
2021-02-05 04:38:46 +03:00
static 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
2018-02-28 00:29:15 +03:00
np = of_find_compatible_node ( NULL , NULL , " fsl,imx25-ccm " ) ;
mx25_ccm_base = of_iomap ( np , 0 ) ;
if ( mx25_ccm_base ) {
/*
* By default , we mask all interrupts . We set the actual mask
* before we go into low - power mode .
*/
imx_writel ( 0xffffffff , mx25_ccm_base + MX25_CCM_LPIMR0 ) ;
imx_writel ( 0xffffffff , mx25_ccm_base + MX25_CCM_LPIMR1 ) ;
}
2007-07-10 01:06:53 +04:00
/* put the AVIC into the reset value with
* all interrupts disabled
*/
2016-01-27 19:59:35 +03:00
imx_writel ( 0 , avic_base + AVIC_INTCNTL ) ;
imx_writel ( 0x1f , avic_base + AVIC_NIMASK ) ;
2007-07-10 01:06:53 +04:00
/* disable all interrupts */
2016-01-27 19:59:35 +03:00
imx_writel ( 0 , avic_base + AVIC_INTENABLEH ) ;
imx_writel ( 0 , avic_base + AVIC_INTENABLEL ) ;
2007-07-10 01:06:53 +04:00
/* all IRQ no FIQ */
2016-01-27 19:59:35 +03:00
imx_writel ( 0 , avic_base + AVIC_INTTYPEH ) ;
imx_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 + + )
2016-01-27 19:59:35 +03:00
imx_writel ( 0 , avic_base + AVIC_NIPRIORITY ( i ) ) ;
2007-07-10 01:06:53 +04:00
2014-05-11 11:35:57 +04:00
set_handle_irq ( avic_handle_irq ) ;
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 " ) ;
}
2021-02-05 04:38:46 +03:00
static int __init imx_avic_init ( struct device_node * node ,
struct device_node * parent )
{
void __iomem * avic_base ;
avic_base = of_iomap ( node , 0 ) ;
BUG_ON ( ! avic_base ) ;
mxc_init_irq ( avic_base ) ;
return 0 ;
}
IRQCHIP_DECLARE ( imx_avic , " fsl,avic " , imx_avic_init ) ;