2019-02-14 17:52:28 +03:00
// SPDX-License-Identifier: GPL-2.0-only
//
// Author: Steve Chen <schen@mvista.com>
// Copyright (C) 2008-2009, MontaVista Software, Inc. <source@mvista.com>
// Author: Bartosz Golaszewski <bgolaszewski@baylibre.com>
// Copyright (C) 2019, Texas Instruments
//
// TI Common Platform Interrupt Controller (cp_intc) driver
2009-03-11 18:49:05 +03:00
2012-05-30 14:18:57 +04:00
# include <linux/export.h>
2009-03-11 18:49:05 +03:00
# include <linux/init.h>
# include <linux/irq.h>
2016-03-01 01:33:26 +03:00
# include <linux/irqchip.h>
2019-02-14 17:52:23 +03:00
# include <linux/irqchip/irq-davinci-cp-intc.h>
2012-05-30 14:18:57 +04:00
# include <linux/irqdomain.h>
2009-03-11 18:49:05 +03:00
# include <linux/io.h>
2012-05-30 14:18:58 +04:00
# include <linux/of.h>
# include <linux/of_address.h>
# include <linux/of_irq.h>
2009-03-11 18:49:05 +03:00
2019-02-14 17:51:58 +03:00
# include <asm/exception.h>
2019-02-14 17:52:17 +03:00
2019-02-14 17:52:21 +03:00
# define DAVINCI_CP_INTC_CTRL 0x04
2019-02-14 17:52:22 +03:00
# define DAVINCI_CP_INTC_HOST_CTRL 0x0c
2019-02-14 17:52:21 +03:00
# define DAVINCI_CP_INTC_GLOBAL_ENABLE 0x10
# define DAVINCI_CP_INTC_SYS_STAT_IDX_CLR 0x24
# define DAVINCI_CP_INTC_SYS_ENABLE_IDX_SET 0x28
2019-02-14 17:52:22 +03:00
# define DAVINCI_CP_INTC_SYS_ENABLE_IDX_CLR 0x2c
2019-02-14 17:52:21 +03:00
# define DAVINCI_CP_INTC_HOST_ENABLE_IDX_SET 0x34
# define DAVINCI_CP_INTC_HOST_ENABLE_IDX_CLR 0x38
# define DAVINCI_CP_INTC_PRIO_IDX 0x80
# define DAVINCI_CP_INTC_SYS_STAT_CLR(n) (0x0280 + (n << 2))
# define DAVINCI_CP_INTC_SYS_ENABLE_CLR(n) (0x0380 + (n << 2))
# define DAVINCI_CP_INTC_CHAN_MAP(n) (0x0400 + (n << 2))
2019-02-14 17:52:22 +03:00
# define DAVINCI_CP_INTC_SYS_POLARITY(n) (0x0d00 + (n << 2))
# define DAVINCI_CP_INTC_SYS_TYPE(n) (0x0d80 + (n << 2))
2019-02-14 17:52:21 +03:00
# define DAVINCI_CP_INTC_HOST_ENABLE(n) (0x1500 + (n << 2))
2019-02-14 17:51:58 +03:00
# define DAVINCI_CP_INTC_PRI_INDX_MASK GENMASK(9, 0)
# define DAVINCI_CP_INTC_GPIR_NONE BIT(31)
2019-02-14 17:52:21 +03:00
static void __iomem * davinci_cp_intc_base ;
static struct irq_domain * davinci_cp_intc_irq_domain ;
2019-02-14 17:52:00 +03:00
2019-02-14 17:52:21 +03:00
static inline unsigned int davinci_cp_intc_read ( unsigned int offset )
2009-03-11 18:49:05 +03:00
{
2019-02-14 17:52:27 +03:00
return readl_relaxed ( davinci_cp_intc_base + offset ) ;
2009-03-11 18:49:05 +03:00
}
2019-02-14 17:52:21 +03:00
static inline void davinci_cp_intc_write ( unsigned long value ,
unsigned int offset )
2009-03-11 18:49:05 +03:00
{
2019-02-14 17:52:27 +03:00
writel_relaxed ( value , davinci_cp_intc_base + offset ) ;
2009-03-11 18:49:05 +03:00
}
2019-02-14 17:52:21 +03:00
static void davinci_cp_intc_ack_irq ( struct irq_data * d )
2009-03-11 18:49:05 +03:00
{
2019-02-14 17:52:21 +03:00
davinci_cp_intc_write ( d - > hwirq , DAVINCI_CP_INTC_SYS_STAT_IDX_CLR ) ;
2009-03-11 18:49:05 +03:00
}
2019-02-14 17:52:21 +03:00
static void davinci_cp_intc_mask_irq ( struct irq_data * d )
2009-03-11 18:49:05 +03:00
{
/* XXX don't know why we need to disable nIRQ here... */
2019-02-14 17:52:21 +03:00
davinci_cp_intc_write ( 1 , DAVINCI_CP_INTC_HOST_ENABLE_IDX_CLR ) ;
davinci_cp_intc_write ( d - > hwirq , DAVINCI_CP_INTC_SYS_ENABLE_IDX_CLR ) ;
davinci_cp_intc_write ( 1 , DAVINCI_CP_INTC_HOST_ENABLE_IDX_SET ) ;
2009-03-11 18:49:05 +03:00
}
2019-02-14 17:52:21 +03:00
static void davinci_cp_intc_unmask_irq ( struct irq_data * d )
2009-03-11 18:49:05 +03:00
{
2019-02-14 17:52:21 +03:00
davinci_cp_intc_write ( d - > hwirq , DAVINCI_CP_INTC_SYS_ENABLE_IDX_SET ) ;
2009-03-11 18:49:05 +03:00
}
2019-02-14 17:52:21 +03:00
static int davinci_cp_intc_set_irq_type ( struct irq_data * d ,
unsigned int flow_type )
2009-03-11 18:49:05 +03:00
{
2019-02-14 17:52:25 +03:00
unsigned int reg , mask , polarity , type ;
reg = BIT_WORD ( d - > hwirq ) ;
mask = BIT_MASK ( d - > hwirq ) ;
polarity = davinci_cp_intc_read ( DAVINCI_CP_INTC_SYS_POLARITY ( reg ) ) ;
type = davinci_cp_intc_read ( DAVINCI_CP_INTC_SYS_TYPE ( reg ) ) ;
2009-03-11 18:49:05 +03:00
switch ( flow_type ) {
case IRQ_TYPE_EDGE_RISING :
polarity | = mask ;
type | = mask ;
break ;
case IRQ_TYPE_EDGE_FALLING :
polarity & = ~ mask ;
type | = mask ;
break ;
case IRQ_TYPE_LEVEL_HIGH :
polarity | = mask ;
type & = ~ mask ;
break ;
case IRQ_TYPE_LEVEL_LOW :
polarity & = ~ mask ;
type & = ~ mask ;
break ;
default :
return - EINVAL ;
}
2019-02-14 17:52:21 +03:00
davinci_cp_intc_write ( polarity , DAVINCI_CP_INTC_SYS_POLARITY ( reg ) ) ;
davinci_cp_intc_write ( type , DAVINCI_CP_INTC_SYS_TYPE ( reg ) ) ;
2009-03-11 18:49:05 +03:00
return 0 ;
}
2019-02-14 17:52:21 +03:00
static struct irq_chip davinci_cp_intc_irq_chip = {
2009-03-11 18:49:05 +03:00
. name = " cp_intc " ,
2019-02-14 17:52:21 +03:00
. irq_ack = davinci_cp_intc_ack_irq ,
. irq_mask = davinci_cp_intc_mask_irq ,
. irq_unmask = davinci_cp_intc_unmask_irq ,
. irq_set_type = davinci_cp_intc_set_irq_type ,
2015-08-01 18:33:56 +03:00
. flags = IRQCHIP_SKIP_SET_WAKE ,
2009-03-11 18:49:05 +03:00
} ;
2019-02-14 17:51:58 +03:00
static asmlinkage void __exception_irq_entry
2019-02-14 17:52:21 +03:00
davinci_cp_intc_handle_irq ( struct pt_regs * regs )
2019-02-14 17:51:58 +03:00
{
int gpir , irqnr , none ;
/*
* The interrupt number is in first ten bits . The NONE field set to 1
* indicates a spurious irq .
*/
2019-02-14 17:52:21 +03:00
gpir = davinci_cp_intc_read ( DAVINCI_CP_INTC_PRIO_IDX ) ;
2019-02-14 17:51:58 +03:00
irqnr = gpir & DAVINCI_CP_INTC_PRI_INDX_MASK ;
none = gpir & DAVINCI_CP_INTC_GPIR_NONE ;
if ( unlikely ( none ) ) {
pr_err_once ( " %s: spurious irq! \n " , __func__ ) ;
return ;
}
2021-10-20 22:23:09 +03:00
generic_handle_domain_irq ( davinci_cp_intc_irq_domain , irqnr ) ;
2019-02-14 17:51:58 +03:00
}
2019-02-14 17:52:21 +03:00
static int davinci_cp_intc_host_map ( struct irq_domain * h , unsigned int virq ,
2012-05-30 14:18:57 +04:00
irq_hw_number_t hw )
{
pr_debug ( " cp_intc_host_map(%d, 0x%lx) \n " , virq , hw ) ;
2019-02-14 17:52:21 +03:00
irq_set_chip ( virq , & davinci_cp_intc_irq_chip ) ;
2015-07-27 23:55:13 +03:00
irq_set_probe ( virq ) ;
2012-05-30 14:18:57 +04:00
irq_set_handler ( virq , handle_edge_irq ) ;
2019-02-14 17:52:25 +03:00
2012-05-30 14:18:57 +04:00
return 0 ;
}
2019-02-14 17:52:21 +03:00
static const struct irq_domain_ops davinci_cp_intc_irq_domain_ops = {
. map = davinci_cp_intc_host_map ,
2012-05-30 14:18:57 +04:00
. xlate = irq_domain_xlate_onetwocell ,
} ;
2019-02-14 17:52:23 +03:00
static int __init
davinci_cp_intc_do_init ( const struct davinci_cp_intc_config * config ,
struct device_node * node )
2009-03-11 18:49:05 +03:00
{
2019-02-14 17:52:23 +03:00
unsigned int num_regs = BITS_TO_LONGS ( config - > num_irqs ) ;
int offset , irq_base ;
2019-02-14 17:52:24 +03:00
void __iomem * req ;
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 - EBUSY ;
}
2019-02-14 17:52:23 +03:00
davinci_cp_intc_base = ioremap ( config - > reg . start ,
resource_size ( & config - > reg ) ) ;
2019-02-14 17:52:26 +03:00
if ( ! davinci_cp_intc_base ) {
pr_err ( " %s: unable to ioremap register range \n " , __func__ ) ;
2012-05-30 14:18:57 +04:00
return - EINVAL ;
2019-02-14 17:52:26 +03:00
}
2009-03-11 18:49:05 +03:00
2019-02-14 17:52:21 +03:00
davinci_cp_intc_write ( 0 , DAVINCI_CP_INTC_GLOBAL_ENABLE ) ;
2009-03-11 18:49:05 +03:00
/* Disable all host interrupts */
2019-02-14 17:52:21 +03:00
davinci_cp_intc_write ( 0 , DAVINCI_CP_INTC_HOST_ENABLE ( 0 ) ) ;
2009-03-11 18:49:05 +03:00
/* Disable system interrupts */
2019-02-14 17:52:23 +03:00
for ( offset = 0 ; offset < num_regs ; offset + + )
davinci_cp_intc_write ( ~ 0 ,
DAVINCI_CP_INTC_SYS_ENABLE_CLR ( offset ) ) ;
2009-03-11 18:49:05 +03:00
/* Set to normal mode, no nesting, no priority hold */
2019-02-14 17:52:21 +03:00
davinci_cp_intc_write ( 0 , DAVINCI_CP_INTC_CTRL ) ;
davinci_cp_intc_write ( 0 , DAVINCI_CP_INTC_HOST_CTRL ) ;
2009-03-11 18:49:05 +03:00
/* Clear system interrupt status */
2019-02-14 17:52:23 +03:00
for ( offset = 0 ; offset < num_regs ; offset + + )
davinci_cp_intc_write ( ~ 0 ,
DAVINCI_CP_INTC_SYS_STAT_CLR ( offset ) ) ;
2009-03-11 18:49:05 +03:00
/* Enable nIRQ (what about nFIQ?) */
2019-02-14 17:52:21 +03:00
davinci_cp_intc_write ( 1 , DAVINCI_CP_INTC_HOST_ENABLE_IDX_SET ) ;
2009-03-11 18:49:05 +03:00
2019-02-14 17:52:23 +03:00
/* Default all priorities to channel 7. */
num_regs = ( config - > num_irqs + 3 ) > > 2 ; /* 4 channels per register */
for ( offset = 0 ; offset < num_regs ; offset + + )
davinci_cp_intc_write ( 0x07070707 ,
DAVINCI_CP_INTC_CHAN_MAP ( offset ) ) ;
2009-03-11 18:49:05 +03:00
2019-02-14 17:52:23 +03:00
irq_base = irq_alloc_descs ( - 1 , 0 , config - > num_irqs , 0 ) ;
2012-05-30 14:18:57 +04:00
if ( irq_base < 0 ) {
2019-02-14 17:52:26 +03:00
pr_err ( " %s: unable to allocate interrupt descriptors: %d \n " ,
__func__ , irq_base ) ;
return irq_base ;
2012-05-30 14:18:57 +04:00
}
2019-02-14 17:52:21 +03:00
davinci_cp_intc_irq_domain = irq_domain_add_legacy (
2019-02-14 17:52:23 +03:00
node , config - > num_irqs , irq_base , 0 ,
2019-02-14 17:52:21 +03:00
& davinci_cp_intc_irq_domain_ops , NULL ) ;
2012-05-30 14:18:57 +04:00
2019-02-14 17:52:21 +03:00
if ( ! davinci_cp_intc_irq_domain ) {
2019-02-14 17:52:26 +03:00
pr_err ( " %s: unable to create an interrupt domain \n " , __func__ ) ;
2012-05-30 14:18:57 +04:00
return - EINVAL ;
2009-03-11 18:49:05 +03:00
}
2019-02-14 17:52:21 +03:00
set_handle_irq ( davinci_cp_intc_handle_irq ) ;
2019-02-14 17:51:58 +03:00
2009-03-11 18:49:05 +03:00
/* Enable global interrupt */
2019-02-14 17:52:21 +03:00
davinci_cp_intc_write ( 1 , DAVINCI_CP_INTC_GLOBAL_ENABLE ) ;
2012-05-30 14:18:57 +04:00
return 0 ;
}
2019-02-14 17:52:23 +03:00
int __init davinci_cp_intc_init ( const struct davinci_cp_intc_config * config )
2012-05-30 14:18:57 +04:00
{
2019-02-14 17:52:23 +03:00
return davinci_cp_intc_do_init ( config , NULL ) ;
2009-03-11 18:49:05 +03:00
}
2016-03-01 01:33:26 +03:00
2019-02-14 17:52:23 +03:00
static int __init davinci_cp_intc_of_init ( struct device_node * node ,
struct device_node * parent )
{
struct davinci_cp_intc_config config = { } ;
int ret ;
ret = of_address_to_resource ( node , 0 , & config . reg ) ;
if ( ret ) {
pr_err ( " %s: unable to get the register range from device-tree \n " ,
__func__ ) ;
return ret ;
}
ret = of_property_read_u32 ( node , " ti,intc-size " , & config . num_irqs ) ;
if ( ret ) {
pr_err ( " %s: unable to read the 'ti,intc-size' property \n " ,
__func__ ) ;
return ret ;
}
return davinci_cp_intc_do_init ( & config , node ) ;
}
2019-02-14 17:52:21 +03:00
IRQCHIP_DECLARE ( cp_intc , " ti,cp-intc " , davinci_cp_intc_of_init ) ;