2019-05-27 08:55:08 +02:00
// SPDX-License-Identifier: GPL-2.0-or-later
2009-12-03 22:36:41 +02:00
/*
2010-04-22 16:28:42 +03:00
* Copyright ( C ) 2004 - 2010 Freescale Semiconductor , Inc . All Rights Reserved .
2009-12-03 22:36:41 +02:00
*/
# include <linux/init.h>
# include <linux/device.h>
# include <linux/errno.h>
# include <linux/io.h>
2016-06-01 22:07:32 +03:00
# include <linux/irqchip.h>
2012-06-13 10:20:58 +08:00
# include <linux/irqdomain.h>
# include <linux/of.h>
2014-05-19 20:19:06 +08:00
# include <linux/of_address.h>
2009-12-03 22:36:41 +02:00
# include <asm/mach/irq.h>
2011-11-03 17:31:26 +08:00
# include <asm/exception.h>
2009-12-03 22:36:41 +02:00
2012-09-13 21:01:00 +08:00
# include "common.h"
2012-09-14 14:14:45 +08:00
# include "hardware.h"
2010-12-06 11:37:38 +00:00
# include "irq-common.h"
2009-12-03 22:36:41 +02:00
/*
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* TZIC Registers *
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
*/
# define TZIC_INTCNTL 0x0000 /* Control register */
# define TZIC_INTTYPE 0x0004 /* Controller Type register */
# define TZIC_IMPID 0x0008 /* Distributor Implementer Identification */
# define TZIC_PRIOMASK 0x000C /* Priority Mask Reg */
# define TZIC_SYNCCTRL 0x0010 /* Synchronizer Control register */
# define TZIC_DSMINT 0x0014 /* DSM interrupt Holdoffregister */
# define TZIC_INTSEC0(i) (0x0080 + ((i) << 2)) /* Interrupt Security Reg 0 */
# define TZIC_ENSET0(i) (0x0100 + ((i) << 2)) /* Enable Set Reg 0 */
# define TZIC_ENCLEAR0(i) (0x0180 + ((i) << 2)) /* Enable Clear Reg 0 */
# define TZIC_SRCSET0 0x0200 /* Source Set Register 0 */
# define TZIC_SRCCLAR0 0x0280 /* Source Clear Register 0 */
# define TZIC_PRIORITY0 0x0400 /* Priority Register 0 */
# define TZIC_PND0 0x0D00 /* Pending Register 0 */
2011-09-20 14:28:39 +02:00
# define TZIC_HIPND(i) (0x0D80+ ((i) << 2)) /* High Priority Pending Register */
2009-12-03 22:36:41 +02:00
# define TZIC_WAKEUP0(i) (0x0E00 + ((i) << 2)) /* Wakeup Config Register */
# define TZIC_SWINT 0x0F00 /* Software Interrupt Rigger Register */
# define TZIC_ID0 0x0FD0 /* Indentification Register 0 */
2013-03-25 11:03:45 -03:00
static void __iomem * tzic_base ;
2012-06-13 10:20:58 +08:00
static struct irq_domain * domain ;
2009-12-03 22:36:41 +02:00
2011-05-10 18:15:25 +02:00
# define TZIC_NUM_IRQS 128
2010-12-06 11:37:38 +00:00
# ifdef CONFIG_FIQ
2016-06-19 09:55:53 +03:00
static int tzic_set_irq_fiq ( unsigned int hwirq , unsigned int type )
2010-12-06 11:37:38 +00:00
{
unsigned int index , mask , value ;
2016-06-19 09:55:53 +03:00
index = hwirq > > 5 ;
2010-12-06 11:37:38 +00:00
if ( unlikely ( index > = 4 ) )
return - EINVAL ;
2016-06-19 09:55:53 +03:00
mask = 1U < < ( hwirq & 0x1F ) ;
2010-12-06 11:37:38 +00:00
2016-01-27 17:59:35 +01:00
value = imx_readl ( tzic_base + TZIC_INTSEC0 ( index ) ) | mask ;
2010-12-06 11:37:38 +00:00
if ( type )
value & = ~ mask ;
2016-01-27 17:59:35 +01:00
imx_writel ( value , tzic_base + TZIC_INTSEC0 ( index ) ) ;
2010-12-06 11:37:38 +00:00
return 0 ;
}
2011-06-07 13:59:14 +08:00
# else
# define tzic_set_irq_fiq NULL
2010-12-06 11:37:38 +00:00
# endif
2011-10-09 17:42:15 +08:00
# ifdef CONFIG_PM
static void tzic_irq_suspend ( struct irq_data * d )
{
struct irq_chip_generic * gc = irq_data_get_irq_chip_data ( d ) ;
2012-06-13 10:20:58 +08:00
int idx = d - > hwirq > > 5 ;
2011-10-09 17:42:15 +08:00
2016-01-27 17:59:35 +01:00
imx_writel ( gc - > wake_active , tzic_base + TZIC_WAKEUP0 ( idx ) ) ;
2011-10-09 17:42:15 +08:00
}
static void tzic_irq_resume ( struct irq_data * d )
{
2012-06-13 10:20:58 +08:00
int idx = d - > hwirq > > 5 ;
2011-10-09 17:42:15 +08:00
2016-01-27 17:59:35 +01:00
imx_writel ( imx_readl ( tzic_base + TZIC_ENSET0 ( idx ) ) ,
tzic_base + TZIC_WAKEUP0 ( idx ) ) ;
2011-10-09 17:42:15 +08:00
}
# else
# define tzic_irq_suspend NULL
# define tzic_irq_resume NULL
# endif
2009-12-03 22:36:41 +02:00
2011-09-22 17:40:08 +08:00
static struct mxc_extra_irq tzic_extra_irq = {
# ifdef CONFIG_FIQ
. set_irq_fiq = tzic_set_irq_fiq ,
# endif
} ;
2012-06-13 10:20:58 +08:00
static __init void tzic_init_gc ( int idx , unsigned int irq_start )
2009-12-03 22:36:41 +02:00
{
2011-06-07 13:59:14 +08:00
struct irq_chip_generic * gc ;
struct irq_chip_type * ct ;
gc = irq_alloc_generic_chip ( " tzic " , 1 , irq_start , tzic_base ,
handle_level_irq ) ;
2011-09-22 17:40:08 +08:00
gc - > private = & tzic_extra_irq ;
2011-06-07 13:59:14 +08:00
gc - > wake_enabled = IRQ_MSK ( 32 ) ;
ct = gc - > chip_types ;
ct - > chip . irq_mask = irq_gc_mask_disable_reg ;
ct - > chip . irq_unmask = irq_gc_unmask_enable_reg ;
ct - > chip . irq_set_wake = irq_gc_set_wake ;
2011-10-09 17:42:15 +08:00
ct - > chip . irq_suspend = tzic_irq_suspend ;
ct - > chip . irq_resume = tzic_irq_resume ;
2011-06-07 13:59:14 +08:00
ct - > regs . disable = TZIC_ENCLEAR0 ( idx ) ;
ct - > regs . enable = TZIC_ENSET0 ( idx ) ;
irq_setup_generic_chip ( gc , IRQ_MSK ( 32 ) , 0 , IRQ_NOREQUEST , 0 ) ;
2009-12-03 22:36:41 +02:00
}
2014-05-11 11:35:57 +04:00
static void __exception_irq_entry tzic_handle_irq ( struct pt_regs * regs )
2011-09-20 14:28:39 +02:00
{
u32 stat ;
int i , irqofs , handled ;
do {
handled = 0 ;
for ( i = 0 ; i < 4 ; i + + ) {
2016-01-27 17:59:35 +01:00
stat = imx_readl ( tzic_base + TZIC_HIPND ( i ) ) &
imx_readl ( tzic_base + TZIC_INTSEC0 ( i ) ) ;
2011-09-20 14:28:39 +02:00
while ( stat ) {
handled = 1 ;
irqofs = fls ( stat ) - 1 ;
2014-08-26 11:03:38 +01:00
handle_domain_irq ( domain , irqofs + i * 32 , regs ) ;
2011-09-20 14:28:39 +02:00
stat & = ~ ( 1 < < irqofs ) ;
}
}
} while ( handled ) ;
}
2009-12-03 22:36:41 +02:00
/*
* This function initializes the TZIC hardware and disables all the
* interrupts . It registers the interrupt enable and disable functions
* to the kernel for each interrupt source .
*/
2016-06-01 22:07:32 +03:00
static int __init tzic_init_dt ( struct device_node * np , struct device_node * p )
2009-12-03 22:36:41 +02:00
{
2012-06-13 10:20:58 +08:00
int irq_base ;
2009-12-03 22:36:41 +02:00
int i ;
2014-05-19 20:19:06 +08:00
tzic_base = of_iomap ( np , 0 ) ;
WARN_ON ( ! tzic_base ) ;
2009-12-03 22:36:41 +02:00
/* put the TZIC into the reset value with
* all interrupts disabled
*/
2016-01-27 17:59:35 +01:00
i = imx_readl ( tzic_base + TZIC_INTCNTL ) ;
2009-12-03 22:36:41 +02:00
2016-01-27 17:59:35 +01:00
imx_writel ( 0x80010001 , tzic_base + TZIC_INTCNTL ) ;
imx_writel ( 0x1f , tzic_base + TZIC_PRIOMASK ) ;
imx_writel ( 0x02 , tzic_base + TZIC_SYNCCTRL ) ;
2009-12-03 22:36:41 +02:00
for ( i = 0 ; i < 4 ; i + + )
2016-01-27 17:59:35 +01:00
imx_writel ( 0xFFFFFFFF , tzic_base + TZIC_INTSEC0 ( i ) ) ;
2009-12-03 22:36:41 +02:00
/* disable all interrupts */
for ( i = 0 ; i < 4 ; i + + )
2016-01-27 17:59:35 +01:00
imx_writel ( 0xFFFFFFFF , tzic_base + TZIC_ENCLEAR0 ( i ) ) ;
2009-12-03 22:36:41 +02:00
/* all IRQ no FIQ Warning :: No selection */
2012-06-13 10:20:58 +08:00
irq_base = irq_alloc_descs ( - 1 , 0 , TZIC_NUM_IRQS , numa_node_id ( ) ) ;
WARN_ON ( irq_base < 0 ) ;
domain = irq_domain_add_legacy ( np , TZIC_NUM_IRQS , irq_base , 0 ,
& irq_domain_simple_ops , NULL ) ;
WARN_ON ( ! domain ) ;
for ( i = 0 ; i < 4 ; i + + , irq_base + = 32 )
tzic_init_gc ( i , irq_base ) ;
2010-12-06 11:37:38 +00:00
2014-05-11 11:35:57 +04:00
set_handle_irq ( tzic_handle_irq ) ;
2010-12-06 11:37:38 +00:00
# ifdef CONFIG_FIQ
/* Initialize FIQ */
2012-06-28 14:42:08 +08:00
init_FIQ ( FIQ_START ) ;
2010-12-06 11:37:38 +00:00
# endif
2009-12-03 22:36:41 +02:00
pr_info ( " TrustZone Interrupt Controller (TZIC) initialized \n " ) ;
2016-06-01 22:07:32 +03:00
return 0 ;
2009-12-03 22:36:41 +02:00
}
2016-06-01 22:07:32 +03:00
IRQCHIP_DECLARE ( tzic , " fsl,tzic " , tzic_init_dt ) ;
2009-12-03 22:36:41 +02:00
/**
* tzic_enable_wake ( ) - enable wakeup interrupt
*
* @ return 0 if successful ; non - zero otherwise
2012-05-21 17:50:25 -05:00
*
* This function provides an interrupt synchronization point that is required
* by tzic enabled platforms before entering imx specific low power modes ( ie ,
* those low power modes beyond the WAIT_CLOCKED basic ARM WFI only mode ) .
2009-12-03 22:36:41 +02:00
*/
2011-10-09 17:42:15 +08:00
int tzic_enable_wake ( void )
2009-12-03 22:36:41 +02:00
{
2011-10-09 17:42:15 +08:00
unsigned int i ;
2009-12-03 22:36:41 +02:00
2016-01-27 17:59:35 +01:00
imx_writel ( 1 , tzic_base + TZIC_DSMINT ) ;
if ( unlikely ( imx_readl ( tzic_base + TZIC_DSMINT ) = = 0 ) )
2009-12-03 22:36:41 +02:00
return - EAGAIN ;
2011-10-09 17:42:15 +08:00
for ( i = 0 ; i < 4 ; i + + )
2016-01-27 17:59:35 +01:00
imx_writel ( imx_readl ( tzic_base + TZIC_ENSET0 ( i ) ) ,
tzic_base + TZIC_WAKEUP0 ( i ) ) ;
2009-12-03 22:36:41 +02:00
return 0 ;
}