2019-05-27 09:55:05 +03:00
// SPDX-License-Identifier: GPL-2.0-or-later
2010-12-23 15:11:21 +03:00
/*
* arch / arm / mach - vt8500 / irq . c
*
2012-08-03 13:00:06 +04:00
* Copyright ( C ) 2012 Tony Prisk < linux @ prisktech . co . nz >
2010-12-23 15:11:21 +03:00
* Copyright ( C ) 2010 Alexey Charkov < alchark @ gmail . com >
*/
2012-08-03 13:00:06 +04:00
/*
* This file is copied and modified from the original irq . c provided by
* Alexey Charkov . Minor changes have been made for Device Tree Support .
*/
# include <linux/slab.h>
2010-12-23 15:11:21 +03:00
# include <linux/io.h>
# include <linux/irq.h>
2015-07-08 00:11:46 +03:00
# include <linux/irqchip.h>
2012-08-03 13:00:06 +04:00
# include <linux/irqdomain.h>
2010-12-23 15:11:21 +03:00
# include <linux/interrupt.h>
2012-08-03 13:00:06 +04:00
# include <linux/bitops.h>
# include <linux/of.h>
# include <linux/of_irq.h>
# include <linux/of_address.h>
2010-12-23 15:11:21 +03:00
# include <asm/irq.h>
2012-10-10 11:59:32 +04:00
# include <asm/exception.h>
2013-03-24 05:12:25 +04:00
# include <asm/mach/irq.h>
2012-08-03 13:00:06 +04:00
# define VT8500_ICPC_IRQ 0x20
# define VT8500_ICPC_FIQ 0x24
# define VT8500_ICDC 0x40 /* Destination Control 64*u32 */
# define VT8500_ICIS 0x80 /* Interrupt status, 16*u32 */
/* ICPC */
# define ICPC_MASK 0x3F
# define ICPC_ROTATE BIT(6)
/* IC_DCTR */
# define ICDC_IRQ 0x00
# define ICDC_FIQ 0x01
# define ICDC_DSS0 0x02
# define ICDC_DSS1 0x03
# define ICDC_DSS2 0x04
# define ICDC_DSS3 0x05
# define ICDC_DSS4 0x06
# define ICDC_DSS5 0x07
# define VT8500_INT_DISABLE 0
# define VT8500_INT_ENABLE BIT(3)
# define VT8500_TRIGGER_HIGH 0
# define VT8500_TRIGGER_RISING BIT(5)
# define VT8500_TRIGGER_FALLING BIT(6)
2010-12-23 15:11:21 +03:00
# define VT8500_EDGE ( VT8500_TRIGGER_RISING \
| VT8500_TRIGGER_FALLING )
2012-10-10 11:59:32 +04:00
/* vt8500 has 1 intc, wm8505 and wm8650 have 2 */
# define VT8500_INTC_MAX 2
2012-08-03 13:00:06 +04:00
2012-10-10 11:59:32 +04:00
struct vt8500_irq_data {
void __iomem * base ; /* IO Memory base address */
struct irq_domain * domain ; /* Domain for this controller */
2012-08-03 13:00:06 +04:00
} ;
2010-12-23 15:11:21 +03:00
2012-10-10 11:59:32 +04:00
/* Global variable for accessing io-mem addresses */
static struct vt8500_irq_data intc [ VT8500_INTC_MAX ] ;
static u32 active_cnt = 0 ;
2011-06-28 12:53:20 +04:00
static void vt8500_irq_mask ( struct irq_data * d )
2010-12-23 15:11:21 +03:00
{
2012-10-10 11:59:32 +04:00
struct vt8500_irq_data * priv = d - > domain - > host_data ;
2012-08-03 13:00:06 +04:00
void __iomem * base = priv - > base ;
2012-10-10 11:59:32 +04:00
void __iomem * stat_reg = base + VT8500_ICIS + ( d - > hwirq < 32 ? 0 : 4 ) ;
u8 edge , dctr ;
u32 status ;
2010-12-23 15:11:21 +03:00
2012-08-03 13:00:06 +04:00
edge = readb ( base + VT8500_ICDC + d - > hwirq ) & VT8500_EDGE ;
2010-12-23 15:11:21 +03:00
if ( edge ) {
2012-10-10 11:59:32 +04:00
status = readl ( stat_reg ) ;
2010-12-23 15:11:21 +03:00
2012-08-03 13:00:06 +04:00
status | = ( 1 < < ( d - > hwirq & 0x1f ) ) ;
2010-12-23 15:11:21 +03:00
writel ( status , stat_reg ) ;
} else {
2012-10-10 11:59:32 +04:00
dctr = readb ( base + VT8500_ICDC + d - > hwirq ) ;
2010-12-23 15:11:21 +03:00
dctr & = ~ VT8500_INT_ENABLE ;
2012-08-03 13:00:06 +04:00
writeb ( dctr , base + VT8500_ICDC + d - > hwirq ) ;
2010-12-23 15:11:21 +03:00
}
}
2011-06-28 12:53:20 +04:00
static void vt8500_irq_unmask ( struct irq_data * d )
2010-12-23 15:11:21 +03:00
{
2012-10-10 11:59:32 +04:00
struct vt8500_irq_data * priv = d - > domain - > host_data ;
2012-08-03 13:00:06 +04:00
void __iomem * base = priv - > base ;
2010-12-23 15:11:21 +03:00
u8 dctr ;
2012-08-03 13:00:06 +04:00
dctr = readb ( base + VT8500_ICDC + d - > hwirq ) ;
2010-12-23 15:11:21 +03:00
dctr | = VT8500_INT_ENABLE ;
2012-08-03 13:00:06 +04:00
writeb ( dctr , base + VT8500_ICDC + d - > hwirq ) ;
2010-12-23 15:11:21 +03:00
}
2011-06-28 12:53:20 +04:00
static int vt8500_irq_set_type ( struct irq_data * d , unsigned int flow_type )
2010-12-23 15:11:21 +03:00
{
2012-10-10 11:59:32 +04:00
struct vt8500_irq_data * priv = d - > domain - > host_data ;
2012-08-03 13:00:06 +04:00
void __iomem * base = priv - > base ;
2010-12-23 15:11:21 +03:00
u8 dctr ;
2012-08-03 13:00:06 +04:00
dctr = readb ( base + VT8500_ICDC + d - > hwirq ) ;
2010-12-23 15:11:21 +03:00
dctr & = ~ VT8500_EDGE ;
switch ( flow_type ) {
case IRQF_TRIGGER_LOW :
return - EINVAL ;
case IRQF_TRIGGER_HIGH :
dctr | = VT8500_TRIGGER_HIGH ;
2015-06-23 16:52:41 +03:00
irq_set_handler_locked ( d , handle_level_irq ) ;
2010-12-23 15:11:21 +03:00
break ;
case IRQF_TRIGGER_FALLING :
dctr | = VT8500_TRIGGER_FALLING ;
2015-06-23 16:52:41 +03:00
irq_set_handler_locked ( d , handle_edge_irq ) ;
2010-12-23 15:11:21 +03:00
break ;
case IRQF_TRIGGER_RISING :
dctr | = VT8500_TRIGGER_RISING ;
2015-06-23 16:52:41 +03:00
irq_set_handler_locked ( d , handle_edge_irq ) ;
2010-12-23 15:11:21 +03:00
break ;
}
2012-08-03 13:00:06 +04:00
writeb ( dctr , base + VT8500_ICDC + d - > hwirq ) ;
2010-12-23 15:11:21 +03:00
return 0 ;
}
static struct irq_chip vt8500_irq_chip = {
2011-06-28 12:53:20 +04:00
. name = " vt8500 " ,
. irq_ack = vt8500_irq_mask ,
. irq_mask = vt8500_irq_mask ,
. irq_unmask = vt8500_irq_unmask ,
. irq_set_type = vt8500_irq_set_type ,
2010-12-23 15:11:21 +03:00
} ;
2012-08-03 13:00:06 +04:00
static void __init vt8500_init_irq_hw ( void __iomem * base )
2010-12-23 15:11:21 +03:00
{
2012-10-10 11:59:32 +04:00
u32 i ;
2010-12-23 15:11:21 +03:00
2012-08-03 13:00:06 +04:00
/* Enable rotating priority for IRQ */
writel ( ICPC_ROTATE , base + VT8500_ICPC_IRQ ) ;
writel ( 0x00 , base + VT8500_ICPC_FIQ ) ;
2010-12-23 15:11:21 +03:00
2012-10-10 11:59:32 +04:00
/* Disable all interrupts and route them to IRQ */
for ( i = 0 ; i < 64 ; i + + )
writeb ( VT8500_INT_DISABLE | ICDC_IRQ , base + VT8500_ICDC + i ) ;
2012-08-03 13:00:06 +04:00
}
2010-12-23 15:11:21 +03:00
2012-08-03 13:00:06 +04:00
static int vt8500_irq_map ( struct irq_domain * h , unsigned int virq ,
irq_hw_number_t hw )
{
irq_set_chip_and_handler ( virq , & vt8500_irq_chip , handle_level_irq ) ;
2010-12-23 15:11:21 +03:00
2012-08-03 13:00:06 +04:00
return 0 ;
2010-12-23 15:11:21 +03:00
}
2015-04-27 15:54:24 +03:00
static const struct irq_domain_ops vt8500_irq_domain_ops = {
2012-08-03 13:00:06 +04:00
. map = vt8500_irq_map ,
. xlate = irq_domain_xlate_onecell ,
} ;
2014-03-05 04:40:30 +04:00
static void __exception_irq_entry vt8500_handle_irq ( struct pt_regs * regs )
2012-10-10 11:59:32 +04:00
{
u32 stat , i ;
2014-08-26 14:03:31 +04:00
int irqnr ;
2012-10-10 11:59:32 +04:00
void __iomem * base ;
/* Loop through each active controller */
for ( i = 0 ; i < active_cnt ; i + + ) {
base = intc [ i ] . base ;
irqnr = readl_relaxed ( base ) & 0x3F ;
/*
Highest Priority register default = 63 , so check that this
is a real interrupt by checking the status register
*/
if ( irqnr = = 63 ) {
stat = readl_relaxed ( base + VT8500_ICIS + 4 ) ;
if ( ! ( stat & BIT ( 31 ) ) )
continue ;
}
2021-10-20 22:23:09 +03:00
generic_handle_domain_irq ( intc [ i ] . domain , irqnr ) ;
2012-10-10 11:59:32 +04:00
}
}
2013-07-05 07:33:49 +04:00
static int __init vt8500_irq_init ( struct device_node * node ,
struct device_node * parent )
2010-12-23 15:11:21 +03:00
{
2012-08-03 13:00:06 +04:00
int irq , i ;
struct device_node * np = node ;
2012-10-10 11:59:32 +04:00
if ( active_cnt = = VT8500_INTC_MAX ) {
pr_err ( " %s: Interrupt controllers > VT8500_INTC_MAX \n " ,
__func__ ) ;
goto out ;
}
intc [ active_cnt ] . base = of_iomap ( np , 0 ) ;
intc [ active_cnt ] . domain = irq_domain_add_linear ( node , 64 ,
& vt8500_irq_domain_ops , & intc [ active_cnt ] ) ;
2012-08-03 13:00:06 +04:00
2012-10-10 11:59:32 +04:00
if ( ! intc [ active_cnt ] . base ) {
pr_err ( " %s: Unable to map IO memory \n " , __func__ ) ;
goto out ;
}
if ( ! intc [ active_cnt ] . domain ) {
pr_err ( " %s: Unable to add irq domain! \n " , __func__ ) ;
goto out ;
}
2012-08-03 13:00:06 +04:00
2013-03-24 05:12:25 +04:00
set_handle_irq ( vt8500_handle_irq ) ;
2012-10-10 11:59:32 +04:00
vt8500_init_irq_hw ( intc [ active_cnt ] . base ) ;
2012-08-03 13:00:06 +04:00
2012-10-10 11:59:32 +04:00
pr_info ( " vt8500-irq: Added interrupt controller \n " ) ;
2010-12-23 15:11:21 +03:00
2012-10-10 11:59:32 +04:00
active_cnt + + ;
2012-08-03 13:00:06 +04:00
/* check if this is a slaved controller */
if ( of_irq_count ( np ) ! = 0 ) {
/* check that we have the correct number of interrupts */
if ( of_irq_count ( np ) ! = 8 ) {
2012-10-10 11:59:32 +04:00
pr_err ( " %s: Incorrect IRQ map for slaved controller \n " ,
2012-08-03 13:00:06 +04:00
__func__ ) ;
return - EINVAL ;
2010-12-23 15:11:21 +03:00
}
2012-08-03 13:00:06 +04:00
for ( i = 0 ; i < 8 ; i + + ) {
irq = irq_of_parse_and_map ( np , i ) ;
enable_irq ( irq ) ;
}
pr_info ( " vt8500-irq: Enabled slave->parent interrupts \n " ) ;
2010-12-23 15:11:21 +03:00
}
2012-10-10 11:59:32 +04:00
out :
2012-08-03 13:00:06 +04:00
return 0 ;
2010-12-23 15:11:21 +03:00
}
2012-08-03 13:00:06 +04:00
2013-03-24 05:12:25 +04:00
IRQCHIP_DECLARE ( vt8500_irq , " via,vt8500-intc " , vt8500_irq_init ) ;