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>
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>
2009-07-29 16:08:40 +04:00
# include <linux/bug.h>
2009-03-27 16:25:49 +03:00
2013-08-27 12:49:00 +04:00
# include "../../drivers/irqchip/irqchip.h"
2009-03-27 16:25:49 +03:00
2013-08-27 12:49:00 +04:00
static void __iomem * intc_baseaddr ;
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)
2014-02-24 17:56:32 +04:00
static unsigned int ( * read_fn ) ( void __iomem * ) ;
static void ( * write_fn ) ( u32 , void __iomem * ) ;
static void intc_write32 ( u32 val , void __iomem * addr )
{
iowrite32 ( val , addr ) ;
}
static unsigned int intc_read32 ( void __iomem * addr )
{
return ioread32 ( addr ) ;
}
static void intc_write32_be ( u32 val , void __iomem * addr )
{
iowrite32be ( val , addr ) ;
}
static unsigned int intc_read32_be ( void __iomem * addr )
{
return ioread32be ( addr ) ;
}
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
{
2011-12-09 13:45:20 +04:00
unsigned long mask = 1 < < d - > hwirq ;
pr_debug ( " 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 ) )
2014-02-24 17:56:32 +04:00
write_fn ( mask , intc_baseaddr + IAR ) ;
2012-11-05 14:51:13 +04:00
2014-02-24 17:56:32 +04:00
write_fn ( mask , intc_baseaddr + SIE ) ;
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
{
2011-12-09 13:45:20 +04:00
pr_debug ( " disable: %ld \n " , d - > hwirq ) ;
2014-02-24 17:56:32 +04:00
write_fn ( 1 < < d - > hwirq , intc_baseaddr + CIE ) ;
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
{
2011-12-09 13:45:20 +04:00
pr_debug ( " ack: %ld \n " , d - > hwirq ) ;
2014-02-24 17:56:32 +04:00
write_fn ( 1 < < d - > hwirq , intc_baseaddr + IAR ) ;
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
{
2011-12-09 13:45:20 +04:00
unsigned long mask = 1 < < d - > hwirq ;
pr_debug ( " disable_and_ack: %ld \n " , d - > hwirq ) ;
2014-02-24 17:56:32 +04:00
write_fn ( mask , intc_baseaddr + CIE ) ;
write_fn ( mask , intc_baseaddr + IAR ) ;
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-01-27 01:10:13 +04:00
static struct irq_domain * root_domain ;
unsigned int get_irq ( void )
2009-03-27 16:25:49 +03:00
{
2012-01-27 01:10:13 +04:00
unsigned int hwirq , irq = - 1 ;
2009-03-27 16:25:49 +03:00
2014-02-24 17:56:32 +04:00
hwirq = read_fn ( intc_baseaddr + IVR ) ;
2012-01-27 01:10:13 +04:00
if ( hwirq ! = - 1U )
irq = irq_find_mapping ( root_domain , hwirq ) ;
pr_debug ( " get_irq: hwirq=%d, irq=%d \n " , hwirq , irq ) ;
2009-03-27 16:25:49 +03:00
return irq ;
}
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
{
u32 intr_mask = ( u32 ) d - > host_data ;
if ( intr_mask & ( 1 < < hw ) ) {
irq_set_chip_and_handler_name ( irq , & intc_dev ,
handle_edge_irq , " edge " ) ;
irq_clear_status_flags ( irq , IRQ_LEVEL ) ;
} else {
irq_set_chip_and_handler_name ( irq , & intc_dev ,
handle_level_irq , " level " ) ;
irq_set_status_flags ( irq , IRQ_LEVEL ) ;
}
return 0 ;
}
static const struct irq_domain_ops xintc_irq_domain_ops = {
. xlate = irq_domain_xlate_onetwocell ,
. map = xintc_map ,
} ;
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
{
2012-01-27 01:10:13 +04:00
u32 nr_irq , intr_mask ;
2013-08-27 12:49:00 +04:00
int ret ;
2009-03-27 16:25:49 +03:00
2013-08-27 12:49:00 +04:00
intc_baseaddr = of_iomap ( intc , 0 ) ;
BUG_ON ( ! intc_baseaddr ) ;
ret = of_property_read_u32 ( intc , " xlnx,num-intr-inputs " , & nr_irq ) ;
if ( ret < 0 ) {
pr_err ( " %s: unable to read xlnx,num-intr-inputs \n " , __func__ ) ;
return - EINVAL ;
}
ret = of_property_read_u32 ( intc , " xlnx,kind-of-intr " , & intr_mask ) ;
if ( ret < 0 ) {
pr_err ( " %s: unable to read xlnx,kind-of-intr \n " , __func__ ) ;
return - EINVAL ;
}
2009-03-27 16:25:49 +03:00
2011-12-09 15:26:55 +04:00
if ( intr_mask > ( u32 ) ( ( 1ULL < < nr_irq ) - 1 ) )
2012-12-27 13:40:38 +04:00
pr_info ( " ERROR: Mismatch in kind-of-intr param \n " ) ;
2009-03-27 16:25:49 +03:00
2013-08-27 12:49:00 +04:00
pr_info ( " %s: num_irq=%d, edge=0x%x \n " ,
intc - > full_name , nr_irq , intr_mask ) ;
2009-03-27 16:25:49 +03:00
2014-02-24 17:56:32 +04:00
write_fn = intc_write32 ;
read_fn = intc_read32 ;
2009-03-27 16:25:49 +03:00
/*
* Disable all external interrupts until they are
* explicity requested .
*/
2014-02-24 17:56:32 +04:00
write_fn ( 0 , intc_baseaddr + IER ) ;
2009-03-27 16:25:49 +03:00
/* Acknowledge any pending interrupts just in case. */
2014-02-24 17:56:32 +04:00
write_fn ( 0xffffffff , intc_baseaddr + IAR ) ;
2009-03-27 16:25:49 +03:00
/* Turn on the Master Enable. */
2014-02-24 17:56:32 +04:00
write_fn ( MER_HIE | MER_ME , intc_baseaddr + MER ) ;
if ( ! ( read_fn ( intc_baseaddr + MER ) & ( MER_HIE | MER_ME ) ) ) {
write_fn = intc_write32_be ;
read_fn = intc_read32_be ;
write_fn ( MER_HIE | MER_ME , intc_baseaddr + MER ) ;
}
2009-03-27 16:25:49 +03:00
2012-01-27 01:10:13 +04:00
/* Yeah, okay, casting the intr_mask to a void* is butt-ugly, but I'm
* lazy and Michal can clean it up to something nicer when he tests
* and commits this patch . ~ ~ gcl */
root_domain = irq_domain_add_linear ( intc , nr_irq , & xintc_irq_domain_ops ,
( void * ) intr_mask ) ;
2013-03-17 13:48:56 +04:00
irq_set_default_host ( root_domain ) ;
2013-08-27 12:49:00 +04:00
return 0 ;
2009-03-27 16:25:49 +03:00
}
2013-08-27 12:49:00 +04:00
IRQCHIP_DECLARE ( xilinx_intc , " xlnx,xps-intc-1.00.a " , xilinx_intc_of_init ) ;