2009-03-27 16:25:49 +03:00
/*
2013-08-27 12:48:29 +04:00
* Copyright ( C ) 2007 - 2013 Michal Simek < monstr @ monstr . eu >
* Copyright ( C ) 2012 - 2013 Xilinx , Inc .
2009-03-27 16:25:49 +03:00
* Copyright ( C ) 2007 - 2009 PetaLogix
* Copyright ( C ) 2006 Atmark Techno , Inc .
*
* This file is subject to the terms and conditions of the GNU General Public
* License . See the file " COPYING " in the main directory of this archive
* for more details .
*/
2012-01-27 01:10:13 +04:00
# include <linux/irqdomain.h>
2009-03-27 16:25:49 +03:00
# include <linux/irq.h>
2015-07-08 00:13:15 +03:00
# include <linux/irqchip.h>
2016-11-14 15:13:49 +03:00
# include <linux/irqchip/chained_irq.h>
2013-08-27 12:49:00 +04:00
# include <linux/of_address.h>
2009-03-27 16:25:49 +03:00
# include <linux/io.h>
2016-11-14 15:13:47 +03:00
# include <linux/jump_label.h>
2009-07-29 16:08:40 +04:00
# include <linux/bug.h>
2016-11-14 15:13:49 +03:00
# include <linux/of_irq.h>
2009-03-27 16:25:49 +03:00
/* No one else should require these constants, so define them locally here. */
# define ISR 0x00 /* Interrupt Status Register */
# define IPR 0x04 /* Interrupt Pending Register */
# define IER 0x08 /* Interrupt Enable Register */
# define IAR 0x0c /* Interrupt Acknowledge Register */
# define SIE 0x10 /* Set Interrupt Enable bits */
# define CIE 0x14 /* Clear Interrupt Enable bits */
# define IVR 0x18 /* Interrupt Vector Register */
# define MER 0x1c /* Master Enable Register */
# define MER_ME (1<<0)
# define MER_HIE (1<<1)
2022-03-04 10:51:29 +03:00
# define SPURIOUS_IRQ (-1U)
2016-11-14 15:13:47 +03:00
static DEFINE_STATIC_KEY_FALSE ( xintc_is_be ) ;
2014-02-24 17:56:32 +04:00
2016-11-14 15:13:47 +03:00
struct xintc_irq_chip {
void __iomem * base ;
struct irq_domain * root_domain ;
u32 intr_mask ;
2020-03-17 15:55:57 +03:00
u32 nr_irq ;
2016-11-14 15:13:47 +03:00
} ;
2014-02-24 17:56:32 +04:00
2020-03-17 15:55:57 +03:00
static struct xintc_irq_chip * primary_intc ;
2014-02-24 17:56:32 +04:00
2020-03-17 15:55:57 +03:00
static void xintc_write ( struct xintc_irq_chip * irqc , int reg , u32 data )
2014-02-24 17:56:32 +04:00
{
2016-11-14 15:13:47 +03:00
if ( static_branch_unlikely ( & xintc_is_be ) )
2020-03-17 15:55:57 +03:00
iowrite32be ( data , irqc - > base + reg ) ;
2016-11-14 15:13:47 +03:00
else
2020-03-17 15:55:57 +03:00
iowrite32 ( data , irqc - > base + reg ) ;
2014-02-24 17:56:32 +04:00
}
2020-03-17 15:55:57 +03:00
static u32 xintc_read ( struct xintc_irq_chip * irqc , int reg )
2014-02-24 17:56:32 +04:00
{
2016-11-14 15:13:47 +03:00
if ( static_branch_unlikely ( & xintc_is_be ) )
2020-03-17 15:55:57 +03:00
return ioread32be ( irqc - > base + reg ) ;
2016-11-14 15:13:47 +03:00
else
2020-03-17 15:55:57 +03:00
return ioread32 ( irqc - > base + reg ) ;
2014-02-24 17:56:32 +04:00
}
2011-02-06 22:36:30 +03:00
static void intc_enable_or_unmask ( struct irq_data * d )
2009-03-27 16:25:49 +03:00
{
2020-03-17 15:55:57 +03:00
struct xintc_irq_chip * irqc = irq_data_get_irq_chip_data ( d ) ;
unsigned long mask = BIT ( d - > hwirq ) ;
2011-12-09 13:45:20 +04:00
2016-11-14 15:13:46 +03:00
pr_debug ( " irq-xilinx: enable_or_unmask: %ld \n " , d - > hwirq ) ;
2009-11-17 17:43:39 +03:00
/* ack level irqs because they can't be acked during
* ack function since the handle_level_irq function
* acks the irq before calling the interrupt handler
*/
2011-03-24 16:52:04 +03:00
if ( irqd_is_level_type ( d ) )
2020-03-17 15:55:57 +03:00
xintc_write ( irqc , IAR , mask ) ;
2012-11-05 14:51:13 +04:00
2020-03-17 15:55:57 +03:00
xintc_write ( irqc , SIE , mask ) ;
2009-03-27 16:25:49 +03:00
}
2011-02-06 22:36:30 +03:00
static void intc_disable_or_mask ( struct irq_data * d )
2009-03-27 16:25:49 +03:00
{
2020-03-17 15:55:57 +03:00
struct xintc_irq_chip * irqc = irq_data_get_irq_chip_data ( d ) ;
2016-11-14 15:13:46 +03:00
pr_debug ( " irq-xilinx: disable: %ld \n " , d - > hwirq ) ;
2020-03-17 15:55:57 +03:00
xintc_write ( irqc , CIE , BIT ( d - > hwirq ) ) ;
2009-03-27 16:25:49 +03:00
}
2011-02-06 22:36:30 +03:00
static void intc_ack ( struct irq_data * d )
2009-03-27 16:25:49 +03:00
{
2020-03-17 15:55:57 +03:00
struct xintc_irq_chip * irqc = irq_data_get_irq_chip_data ( d ) ;
2016-11-14 15:13:46 +03:00
pr_debug ( " irq-xilinx: ack: %ld \n " , d - > hwirq ) ;
2020-03-17 15:55:57 +03:00
xintc_write ( irqc , IAR , BIT ( d - > hwirq ) ) ;
2009-03-27 16:25:49 +03:00
}
2011-02-06 22:36:30 +03:00
static void intc_mask_ack ( struct irq_data * d )
2009-03-27 16:25:49 +03:00
{
2020-03-17 15:55:57 +03:00
struct xintc_irq_chip * irqc = irq_data_get_irq_chip_data ( d ) ;
unsigned long mask = BIT ( d - > hwirq ) ;
2011-12-09 13:45:20 +04:00
2016-11-14 15:13:46 +03:00
pr_debug ( " irq-xilinx: disable_and_ack: %ld \n " , d - > hwirq ) ;
2020-03-17 15:55:57 +03:00
xintc_write ( irqc , CIE , mask ) ;
xintc_write ( irqc , IAR , mask ) ;
2009-03-27 16:25:49 +03:00
}
static struct irq_chip intc_dev = {
. name = " Xilinx INTC " ,
2011-02-06 22:36:30 +03:00
. irq_unmask = intc_enable_or_unmask ,
. irq_mask = intc_disable_or_mask ,
. irq_ack = intc_ack ,
. irq_mask_ack = intc_mask_ack ,
2009-03-27 16:25:49 +03:00
} ;
2012-12-13 20:30:05 +04:00
static int xintc_map ( struct irq_domain * d , unsigned int irq , irq_hw_number_t hw )
2012-01-27 01:10:13 +04:00
{
2020-03-17 15:55:57 +03:00
struct xintc_irq_chip * irqc = d - > host_data ;
if ( irqc - > intr_mask & BIT ( hw ) ) {
2012-01-27 01:10:13 +04:00
irq_set_chip_and_handler_name ( irq , & intc_dev ,
2020-03-17 15:55:57 +03:00
handle_edge_irq , " edge " ) ;
2012-01-27 01:10:13 +04:00
irq_clear_status_flags ( irq , IRQ_LEVEL ) ;
} else {
irq_set_chip_and_handler_name ( irq , & intc_dev ,
2020-03-17 15:55:57 +03:00
handle_level_irq , " level " ) ;
2012-01-27 01:10:13 +04:00
irq_set_status_flags ( irq , IRQ_LEVEL ) ;
}
2020-03-17 15:55:57 +03:00
irq_set_chip_data ( irq , irqc ) ;
2012-01-27 01:10:13 +04:00
return 0 ;
}
static const struct irq_domain_ops xintc_irq_domain_ops = {
. xlate = irq_domain_xlate_onetwocell ,
. map = xintc_map ,
} ;
2016-11-14 15:13:49 +03:00
static void xil_intc_irq_handler ( struct irq_desc * desc )
{
struct irq_chip * chip = irq_desc_get_chip ( desc ) ;
2020-03-17 15:55:57 +03:00
struct xintc_irq_chip * irqc ;
2016-11-14 15:13:49 +03:00
2020-03-17 15:55:57 +03:00
irqc = irq_data_get_irq_handler_data ( & desc - > irq_data ) ;
2016-11-14 15:13:49 +03:00
chained_irq_enter ( chip , desc ) ;
do {
2021-05-04 19:42:18 +03:00
u32 hwirq = xintc_read ( irqc , IVR ) ;
if ( hwirq = = - 1U )
2016-11-14 15:13:49 +03:00
break ;
2021-05-04 19:42:18 +03:00
generic_handle_domain_irq ( irqc - > root_domain , hwirq ) ;
2016-11-14 15:13:49 +03:00
} while ( true ) ;
chained_irq_exit ( chip , desc ) ;
}
2022-03-04 10:51:29 +03:00
static void xil_intc_handle_irq ( struct pt_regs * regs )
{
u32 hwirq ;
do {
hwirq = xintc_read ( primary_intc , IVR ) ;
if ( unlikely ( hwirq = = SPURIOUS_IRQ ) )
break ;
generic_handle_domain_irq ( primary_intc - > root_domain , hwirq ) ;
} while ( true ) ;
}
2013-08-27 12:49:00 +04:00
static int __init xilinx_intc_of_init ( struct device_node * intc ,
struct device_node * parent )
2009-03-27 16:25:49 +03:00
{
2016-11-14 15:13:47 +03:00
struct xintc_irq_chip * irqc ;
2020-03-17 15:55:57 +03:00
int ret , irq ;
2016-11-14 15:13:47 +03:00
irqc = kzalloc ( sizeof ( * irqc ) , GFP_KERNEL ) ;
if ( ! irqc )
return - ENOMEM ;
irqc - > base = of_iomap ( intc , 0 ) ;
BUG_ON ( ! irqc - > base ) ;
2013-08-27 12:49:00 +04:00
2020-03-17 15:55:57 +03:00
ret = of_property_read_u32 ( intc , " xlnx,num-intr-inputs " , & irqc - > nr_irq ) ;
2013-08-27 12:49:00 +04:00
if ( ret < 0 ) {
2016-11-14 15:13:46 +03:00
pr_err ( " irq-xilinx: unable to read xlnx,num-intr-inputs \n " ) ;
2020-03-17 15:55:57 +03:00
goto error ;
2013-08-27 12:49:00 +04:00
}
2016-11-14 15:13:47 +03:00
ret = of_property_read_u32 ( intc , " xlnx,kind-of-intr " , & irqc - > intr_mask ) ;
2013-08-27 12:49:00 +04:00
if ( ret < 0 ) {
2016-11-14 15:13:50 +03:00
pr_warn ( " irq-xilinx: unable to read xlnx,kind-of-intr \n " ) ;
irqc - > intr_mask = 0 ;
2013-08-27 12:49:00 +04:00
}
2009-03-27 16:25:49 +03:00
2020-03-17 15:55:57 +03:00
if ( irqc - > intr_mask > > irqc - > nr_irq )
2016-11-14 15:13:46 +03:00
pr_warn ( " irq-xilinx: mismatch in kind-of-intr param \n " ) ;
2009-03-27 16:25:49 +03:00
2017-07-19 00:43:10 +03:00
pr_info ( " irq-xilinx: %pOF: num_irq=%d, edge=0x%x \n " ,
2020-03-17 15:55:57 +03:00
intc , irqc - > nr_irq , irqc - > intr_mask ) ;
2009-03-27 16:25:49 +03:00
2014-02-24 17:56:32 +04:00
2009-03-27 16:25:49 +03:00
/*
* Disable all external interrupts until they are
2021-03-22 06:21:30 +03:00
* explicitly requested .
2009-03-27 16:25:49 +03:00
*/
2020-03-17 15:55:57 +03:00
xintc_write ( irqc , IER , 0 ) ;
2009-03-27 16:25:49 +03:00
/* Acknowledge any pending interrupts just in case. */
2020-03-17 15:55:57 +03:00
xintc_write ( irqc , IAR , 0xffffffff ) ;
2009-03-27 16:25:49 +03:00
/* Turn on the Master Enable. */
2020-03-17 15:55:57 +03:00
xintc_write ( irqc , MER , MER_HIE | MER_ME ) ;
if ( xintc_read ( irqc , MER ) ! = ( MER_HIE | MER_ME ) ) {
2016-11-14 15:13:47 +03:00
static_branch_enable ( & xintc_is_be ) ;
2020-03-17 15:55:57 +03:00
xintc_write ( irqc , MER , MER_HIE | MER_ME ) ;
2014-02-24 17:56:32 +04:00
}
2009-03-27 16:25:49 +03:00
2020-03-17 15:55:57 +03:00
irqc - > root_domain = irq_domain_add_linear ( intc , irqc - > nr_irq ,
2016-11-14 15:13:47 +03:00
& xintc_irq_domain_ops , irqc ) ;
if ( ! irqc - > root_domain ) {
pr_err ( " irq-xilinx: Unable to create IRQ domain \n " ) ;
2020-03-17 15:55:58 +03:00
ret = - EINVAL ;
2020-03-17 15:55:57 +03:00
goto error ;
2016-11-14 15:13:47 +03:00
}
2013-03-17 13:48:56 +04:00
2016-11-14 15:13:49 +03:00
if ( parent ) {
irq = irq_of_parse_and_map ( intc , 0 ) ;
if ( irq ) {
irq_set_chained_handler_and_data ( irq ,
xil_intc_irq_handler ,
irqc ) ;
} else {
pr_err ( " irq-xilinx: interrupts property not in DT \n " ) ;
ret = - EINVAL ;
2020-03-17 15:55:57 +03:00
goto error ;
2016-11-14 15:13:49 +03:00
}
} else {
2020-03-17 15:55:57 +03:00
primary_intc = irqc ;
2020-03-30 12:41:58 +03:00
irq_set_default_host ( primary_intc - > root_domain ) ;
2022-03-04 10:51:29 +03:00
set_handle_irq ( xil_intc_handle_irq ) ;
2016-11-14 15:13:49 +03:00
}
2013-08-27 12:49:00 +04:00
return 0 ;
2016-11-14 15:13:47 +03:00
2020-03-17 15:55:57 +03:00
error :
iounmap ( irqc - > base ) ;
2016-11-14 15:13:47 +03:00
kfree ( irqc ) ;
return ret ;
2009-03-27 16:25:49 +03:00
}
2013-08-27 12:49:00 +04:00
2016-11-14 15:13:51 +03:00
IRQCHIP_DECLARE ( xilinx_intc_xps , " xlnx,xps-intc-1.00.a " , xilinx_intc_of_init ) ;
IRQCHIP_DECLARE ( xilinx_intc_opb , " xlnx,opb-intc-1.00.c " , xilinx_intc_of_init ) ;