2019-02-14 17:52:04 +03:00
// SPDX-License-Identifier: GPL-2.0-or-later
//
// Copyright (C) 2006, 2019 Texas Instruments.
//
// Interrupt handler for DaVinci boards.
2007-04-30 22:37:19 +04:00
# include <linux/kernel.h>
# include <linux/init.h>
# include <linux/interrupt.h>
# include <linux/irq.h>
2019-02-14 17:52:11 +03:00
# include <linux/irqchip/irq-davinci-aintc.h>
2008-09-06 15:10:45 +04:00
# include <linux/io.h>
2019-02-14 17:51:57 +03:00
# include <linux/irqdomain.h>
2007-04-30 22:37:19 +04:00
2019-02-14 17:51:58 +03:00
# include <asm/exception.h>
2007-04-30 22:37:19 +04:00
2019-02-14 17:52:07 +03:00
# define DAVINCI_AINTC_FIQ_REG0 0x00
# define DAVINCI_AINTC_FIQ_REG1 0x04
# define DAVINCI_AINTC_IRQ_REG0 0x08
# define DAVINCI_AINTC_IRQ_REG1 0x0c
# define DAVINCI_AINTC_IRQ_IRQENTRY 0x14
# define DAVINCI_AINTC_IRQ_ENT_REG0 0x18
# define DAVINCI_AINTC_IRQ_ENT_REG1 0x1c
# define DAVINCI_AINTC_IRQ_INCTL_REG 0x20
# define DAVINCI_AINTC_IRQ_EABASE_REG 0x24
# define DAVINCI_AINTC_IRQ_INTPRI0_REG 0x30
# define DAVINCI_AINTC_IRQ_INTPRI7_REG 0x4c
2019-02-14 17:52:06 +03:00
static void __iomem * davinci_aintc_base ;
static struct irq_domain * davinci_aintc_irq_domain ;
static inline void davinci_aintc_writel ( unsigned long value , int offset )
2007-04-30 22:37:19 +04:00
{
2019-02-14 17:52:08 +03:00
writel_relaxed ( value , davinci_aintc_base + offset ) ;
2007-04-30 22:37:19 +04:00
}
2019-02-14 17:52:06 +03:00
static inline unsigned long davinci_aintc_readl ( int offset )
2019-02-14 17:51:58 +03:00
{
2019-02-14 17:52:06 +03:00
return readl_relaxed ( davinci_aintc_base + offset ) ;
2019-02-14 17:51:58 +03:00
}
2011-04-15 13:19:57 +04:00
static __init void
2019-02-14 17:52:06 +03:00
davinci_aintc_setup_gc ( void __iomem * base ,
unsigned int irq_start , unsigned int num )
2007-04-30 22:37:19 +04:00
{
2011-04-15 13:19:57 +04:00
struct irq_chip_generic * gc ;
struct irq_chip_type * ct ;
2019-02-14 17:52:06 +03:00
gc = irq_get_domain_generic_chip ( davinci_aintc_irq_domain , irq_start ) ;
2019-02-14 17:51:57 +03:00
gc - > reg_base = base ;
gc - > irq_base = irq_start ;
2011-07-17 09:39:35 +04:00
2011-04-15 13:19:57 +04:00
ct = gc - > chip_types ;
2011-07-06 20:41:31 +04:00
ct - > chip . irq_ack = irq_gc_ack_set_bit ;
2011-04-15 13:19:57 +04:00
ct - > chip . irq_mask = irq_gc_mask_clr_bit ;
ct - > chip . irq_unmask = irq_gc_mask_set_bit ;
2019-02-14 17:52:06 +03:00
ct - > regs . ack = DAVINCI_AINTC_IRQ_REG0 ;
ct - > regs . mask = DAVINCI_AINTC_IRQ_ENT_REG0 ;
2011-04-15 13:19:57 +04:00
irq_setup_generic_chip ( gc , IRQ_MSK ( num ) , IRQ_GC_INIT_MASK_CACHE ,
IRQ_NOREQUEST | IRQ_NOPROBE , 0 ) ;
2007-04-30 22:37:19 +04:00
}
2019-02-14 17:51:58 +03:00
static asmlinkage void __exception_irq_entry
2019-02-14 17:52:06 +03:00
davinci_aintc_handle_irq ( struct pt_regs * regs )
2019-02-14 17:51:58 +03:00
{
2019-02-14 17:52:06 +03:00
int irqnr = davinci_aintc_readl ( DAVINCI_AINTC_IRQ_IRQENTRY ) ;
2019-02-14 17:51:58 +03:00
/*
* Use the formula for entry vector index generation from section
* 8.3 .3 of the manual .
*/
irqnr > > = 2 ;
irqnr - = 1 ;
2021-10-20 22:23:09 +03:00
generic_handle_domain_irq ( davinci_aintc_irq_domain , irqnr ) ;
2019-02-14 17:51:58 +03:00
}
2007-04-30 22:37:19 +04:00
/* ARM Interrupt Controller Initialization */
2019-02-14 17:52:11 +03:00
void __init davinci_aintc_init ( const struct davinci_aintc_config * config )
2007-04-30 22:37:19 +04:00
{
2019-02-14 17:52:11 +03:00
unsigned int irq_off , reg_off , prio , shift ;
2019-02-14 17:52:13 +03:00
void __iomem * req ;
2019-02-14 17:51:57 +03:00
int ret , irq_base ;
2019-02-14 17:52:11 +03:00
const u8 * prios ;
2007-04-30 22:37:19 +04:00
2019-02-14 17:52:13 +03:00
req = request_mem_region ( config - > reg . start ,
resource_size ( & config - > reg ) ,
" davinci-cp-intc " ) ;
if ( ! req ) {
pr_err ( " %s: register range busy \n " , __func__ ) ;
return ;
}
2019-02-14 17:52:11 +03:00
davinci_aintc_base = ioremap ( config - > reg . start ,
resource_size ( & config - > reg ) ) ;
2019-02-14 17:52:12 +03:00
if ( ! davinci_aintc_base ) {
pr_err ( " %s: unable to ioremap register range \n " , __func__ ) ;
2010-05-08 01:06:37 +04:00
return ;
2019-02-14 17:52:12 +03:00
}
2010-05-08 01:06:37 +04:00
2007-04-30 22:37:19 +04:00
/* Clear all interrupt requests */
2019-02-14 17:52:06 +03:00
davinci_aintc_writel ( ~ 0x0 , DAVINCI_AINTC_FIQ_REG0 ) ;
davinci_aintc_writel ( ~ 0x0 , DAVINCI_AINTC_FIQ_REG1 ) ;
davinci_aintc_writel ( ~ 0x0 , DAVINCI_AINTC_IRQ_REG0 ) ;
davinci_aintc_writel ( ~ 0x0 , DAVINCI_AINTC_IRQ_REG1 ) ;
2007-04-30 22:37:19 +04:00
/* Disable all interrupts */
2019-02-14 17:52:06 +03:00
davinci_aintc_writel ( 0x0 , DAVINCI_AINTC_IRQ_ENT_REG0 ) ;
davinci_aintc_writel ( 0x0 , DAVINCI_AINTC_IRQ_ENT_REG1 ) ;
2007-04-30 22:37:19 +04:00
/* Interrupts disabled immediately, IRQ entry reflects all */
2019-02-14 17:52:06 +03:00
davinci_aintc_writel ( 0x0 , DAVINCI_AINTC_IRQ_INCTL_REG ) ;
2007-04-30 22:37:19 +04:00
/* we don't use the hardware vector table, just its entry addresses */
2019-02-14 17:52:06 +03:00
davinci_aintc_writel ( 0 , DAVINCI_AINTC_IRQ_EABASE_REG ) ;
2007-04-30 22:37:19 +04:00
/* Clear all interrupt requests */
2019-02-14 17:52:06 +03:00
davinci_aintc_writel ( ~ 0x0 , DAVINCI_AINTC_FIQ_REG0 ) ;
davinci_aintc_writel ( ~ 0x0 , DAVINCI_AINTC_FIQ_REG1 ) ;
davinci_aintc_writel ( ~ 0x0 , DAVINCI_AINTC_IRQ_REG0 ) ;
davinci_aintc_writel ( ~ 0x0 , DAVINCI_AINTC_IRQ_REG1 ) ;
2007-04-30 22:37:19 +04:00
2019-02-14 17:52:11 +03:00
prios = config - > prios ;
for ( reg_off = DAVINCI_AINTC_IRQ_INTPRI0_REG ;
reg_off < = DAVINCI_AINTC_IRQ_INTPRI7_REG ; reg_off + = 4 ) {
for ( shift = 0 , prio = 0 ; shift < 32 ; shift + = 4 , prios + + )
prio | = ( * prios & 0x07 ) < < shift ;
davinci_aintc_writel ( prio , reg_off ) ;
2007-04-30 22:37:19 +04:00
}
2019-02-14 17:52:11 +03:00
irq_base = irq_alloc_descs ( - 1 , 0 , config - > num_irqs , 0 ) ;
2019-02-14 17:52:12 +03:00
if ( irq_base < 0 ) {
pr_err ( " %s: unable to allocate interrupt descriptors: %d \n " ,
__func__ , irq_base ) ;
2019-02-14 17:51:57 +03:00
return ;
2019-02-14 17:52:12 +03:00
}
2019-02-14 17:51:57 +03:00
2019-02-14 17:52:06 +03:00
davinci_aintc_irq_domain = irq_domain_add_legacy ( NULL ,
2019-02-14 17:52:11 +03:00
config - > num_irqs , irq_base , 0 ,
& irq_domain_simple_ops , NULL ) ;
2019-02-14 17:52:12 +03:00
if ( ! davinci_aintc_irq_domain ) {
pr_err ( " %s: unable to create interrupt domain \n " , __func__ ) ;
2019-02-14 17:51:57 +03:00
return ;
2019-02-14 17:52:12 +03:00
}
2019-02-14 17:51:57 +03:00
2019-02-14 17:52:06 +03:00
ret = irq_alloc_domain_generic_chips ( davinci_aintc_irq_domain , 32 , 1 ,
" AINTC " , handle_edge_irq ,
IRQ_NOREQUEST | IRQ_NOPROBE , 0 , 0 ) ;
2019-02-14 17:52:12 +03:00
if ( ret ) {
pr_err ( " %s: unable to allocate generic irq chips for domain \n " ,
__func__ ) ;
2019-02-14 17:51:57 +03:00
return ;
2019-02-14 17:52:12 +03:00
}
2019-02-14 17:51:57 +03:00
2019-02-14 17:52:11 +03:00
for ( irq_off = 0 , reg_off = 0 ;
irq_off < config - > num_irqs ;
irq_off + = 32 , reg_off + = 0x04 )
davinci_aintc_setup_gc ( davinci_aintc_base + reg_off ,
irq_base + irq_off , 32 ) ;
2011-04-15 13:19:57 +04:00
2019-02-14 17:52:06 +03:00
set_handle_irq ( davinci_aintc_handle_irq ) ;
2007-04-30 22:37:19 +04:00
}