2010-10-05 22:10:30 +09:00
/*
* Shared interrupt handling code for IPR and INTC2 types of IRQs .
*
* Copyright ( C ) 2007 , 2008 Magnus Damm
2012-01-24 17:41:55 +09:00
* Copyright ( C ) 2009 - 2012 Paul Mundt
2010-10-05 22:10:30 +09:00
*
* Based on intc2 . c and ipr . c
*
* Copyright ( C ) 1999 Niibe Yutaka & Takeshi Yaegashi
* Copyright ( C ) 2000 Kazumoto Kojima
* Copyright ( C ) 2001 David J . Mckay ( david . mckay @ st . com )
* Copyright ( C ) 2003 Takashi Kusuda < kusuda - takashi @ hitachi - ul . co . jp >
* Copyright ( C ) 2005 , 2006 Paul Mundt
*
* 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 .
*/
# define pr_fmt(fmt) "intc: " fmt
# include <linux/init.h>
# include <linux/irq.h>
# include <linux/io.h>
# include <linux/slab.h>
2011-07-31 17:40:26 -04:00
# include <linux/stat.h>
2010-10-05 22:10:30 +09:00
# include <linux/interrupt.h>
# include <linux/sh_intc.h>
2012-08-01 17:13:46 +09:00
# include <linux/irqdomain.h>
2011-12-21 15:09:52 -08:00
# include <linux/device.h>
2011-03-22 20:19:28 +00:00
# include <linux/syscore_ops.h>
2010-10-05 22:10:30 +09:00
# include <linux/list.h>
# include <linux/spinlock.h>
# include <linux/radix-tree.h>
2011-07-31 19:20:02 -04:00
# include <linux/export.h>
2012-01-24 17:41:55 +09:00
# include <linux/sort.h>
2010-10-05 22:10:30 +09:00
# include "internals.h"
LIST_HEAD ( intc_list ) ;
DEFINE_RAW_SPINLOCK ( intc_big_lock ) ;
2012-01-24 14:07:18 +09:00
static unsigned int nr_intc_controllers ;
2010-10-05 22:10:30 +09:00
/*
* Default priority level
* - this needs to be at least 2 for 5 - bit priorities on 7780
*/
static unsigned int default_prio_level = 2 ; /* 2 - 16 */
2012-01-17 13:10:25 -06:00
static unsigned int intc_prio_level [ INTC_NR_IRQS ] ; /* for now */
2010-10-05 22:10:30 +09:00
unsigned int intc_get_dfl_prio_level ( void )
{
return default_prio_level ;
}
unsigned int intc_get_prio_level ( unsigned int irq )
{
return intc_prio_level [ irq ] ;
}
void intc_set_prio_level ( unsigned int irq , unsigned int level )
{
unsigned long flags ;
raw_spin_lock_irqsave ( & intc_big_lock , flags ) ;
intc_prio_level [ irq ] = level ;
raw_spin_unlock_irqrestore ( & intc_big_lock , flags ) ;
}
2015-09-14 10:42:37 +02:00
static void intc_redirect_irq ( struct irq_desc * desc )
2010-10-05 22:10:30 +09:00
{
2015-07-13 20:51:25 +00:00
generic_handle_irq ( ( unsigned int ) irq_desc_get_handler_data ( desc ) ) ;
2010-10-05 22:10:30 +09:00
}
static void __init intc_register_irq ( struct intc_desc * desc ,
struct intc_desc_int * d ,
intc_enum enum_id ,
unsigned int irq )
{
struct intc_handle_int * hp ;
2010-10-27 15:42:10 +09:00
struct irq_data * irq_data ;
2010-10-05 22:10:30 +09:00
unsigned int data [ 2 ] , primary ;
unsigned long flags ;
raw_spin_lock_irqsave ( & intc_big_lock , flags ) ;
radix_tree_insert ( & d - > tree , enum_id , intc_irq_xlate_get ( irq ) ) ;
raw_spin_unlock_irqrestore ( & intc_big_lock , flags ) ;
/*
* Prefer single interrupt source bitmap over other combinations :
*
* 1. bitmap , single interrupt source
* 2. priority , single interrupt source
* 3. bitmap , multiple interrupt sources ( groups )
* 4. priority , multiple interrupt sources ( groups )
*/
data [ 0 ] = intc_get_mask_handle ( desc , d , enum_id , 0 ) ;
data [ 1 ] = intc_get_prio_handle ( desc , d , enum_id , 0 ) ;
primary = 0 ;
if ( ! data [ 0 ] & & data [ 1 ] )
primary = 1 ;
if ( ! data [ 0 ] & & ! data [ 1 ] )
pr_warning ( " missing unique irq mask for irq %d (vect 0x%04x) \n " ,
irq , irq2evt ( irq ) ) ;
data [ 0 ] = data [ 0 ] ? data [ 0 ] : intc_get_mask_handle ( desc , d , enum_id , 1 ) ;
data [ 1 ] = data [ 1 ] ? data [ 1 ] : intc_get_prio_handle ( desc , d , enum_id , 1 ) ;
if ( ! data [ primary ] )
primary ^ = 1 ;
BUG_ON ( ! data [ primary ] ) ; /* must have primary masking method */
2010-10-27 15:42:10 +09:00
irq_data = irq_get_irq_data ( irq ) ;
2010-10-05 22:10:30 +09:00
disable_irq_nosync ( irq ) ;
2011-03-24 16:31:17 +01:00
irq_set_chip_and_handler_name ( irq , & d - > chip , handle_level_irq ,
" level " ) ;
irq_set_chip_data ( irq , ( void * ) data [ primary ] ) ;
2010-10-05 22:10:30 +09:00
/*
* set priority level
*/
intc_set_prio_level ( irq , intc_get_dfl_prio_level ( ) ) ;
/* enable secondary masking method if present */
if ( data [ ! primary ] )
2010-10-27 15:42:10 +09:00
_intc_enable ( irq_data , data [ ! primary ] ) ;
2010-10-05 22:10:30 +09:00
/* add irq to d->prio list if priority is available */
if ( data [ 1 ] ) {
hp = d - > prio + d - > nr_prio ;
hp - > irq = irq ;
hp - > handle = data [ 1 ] ;
if ( primary ) {
/*
* only secondary priority should access registers , so
* set _INTC_FN ( h ) = REG_FN_ERR for intc_set_priority ( )
*/
hp - > handle & = ~ _INTC_MK ( 0x0f , 0 , 0 , 0 , 0 , 0 ) ;
hp - > handle | = _INTC_MK ( REG_FN_ERR , 0 , 0 , 0 , 0 , 0 ) ;
}
d - > nr_prio + + ;
}
/* add irq to d->sense list if sense is available */
data [ 0 ] = intc_get_sense_handle ( desc , d , enum_id ) ;
if ( data [ 0 ] ) {
( d - > sense + d - > nr_sense ) - > irq = irq ;
( d - > sense + d - > nr_sense ) - > handle = data [ 0 ] ;
d - > nr_sense + + ;
}
/* irq should be disabled by default */
2010-10-27 15:42:10 +09:00
d - > chip . irq_mask ( irq_data ) ;
2010-10-05 22:10:30 +09:00
intc_set_ack_handle ( irq , desc , d , enum_id ) ;
intc_set_dist_handle ( irq , desc , d , enum_id ) ;
activate_irq ( irq ) ;
}
static unsigned int __init save_reg ( struct intc_desc_int * d ,
unsigned int cnt ,
unsigned long value ,
unsigned int smp )
{
if ( value ) {
value = intc_phys_to_virt ( d , value ) ;
d - > reg [ cnt ] = value ;
# ifdef CONFIG_SMP
d - > smp [ cnt ] = smp ;
# endif
return 1 ;
}
return 0 ;
}
int __init register_intc_controller ( struct intc_desc * desc )
{
unsigned int i , k , smp ;
struct intc_hw_desc * hw = & desc - > hw ;
struct intc_desc_int * d ;
struct resource * res ;
pr_info ( " Registered controller '%s' with %u IRQs \n " ,
desc - > name , hw - > nr_vectors ) ;
d = kzalloc ( sizeof ( * d ) , GFP_NOWAIT ) ;
if ( ! d )
goto err0 ;
INIT_LIST_HEAD ( & d - > list ) ;
list_add_tail ( & d - > list , & intc_list ) ;
raw_spin_lock_init ( & d - > lock ) ;
2010-12-24 19:38:37 +09:00
INIT_RADIX_TREE ( & d - > tree , GFP_ATOMIC ) ;
2010-10-05 22:10:30 +09:00
d - > index = nr_intc_controllers ;
if ( desc - > num_resources ) {
d - > nr_windows = desc - > num_resources ;
d - > window = kzalloc ( d - > nr_windows * sizeof ( * d - > window ) ,
GFP_NOWAIT ) ;
if ( ! d - > window )
goto err1 ;
for ( k = 0 ; k < d - > nr_windows ; k + + ) {
res = desc - > resource + k ;
WARN_ON ( resource_type ( res ) ! = IORESOURCE_MEM ) ;
d - > window [ k ] . phys = res - > start ;
d - > window [ k ] . size = resource_size ( res ) ;
d - > window [ k ] . virt = ioremap_nocache ( res - > start ,
resource_size ( res ) ) ;
if ( ! d - > window [ k ] . virt )
goto err2 ;
}
}
d - > nr_reg = hw - > mask_regs ? hw - > nr_mask_regs * 2 : 0 ;
# ifdef CONFIG_INTC_BALANCING
if ( d - > nr_reg )
d - > nr_reg + = hw - > nr_mask_regs ;
# endif
d - > nr_reg + = hw - > prio_regs ? hw - > nr_prio_regs * 2 : 0 ;
d - > nr_reg + = hw - > sense_regs ? hw - > nr_sense_regs : 0 ;
d - > nr_reg + = hw - > ack_regs ? hw - > nr_ack_regs : 0 ;
d - > nr_reg + = hw - > subgroups ? hw - > nr_subgroups : 0 ;
d - > reg = kzalloc ( d - > nr_reg * sizeof ( * d - > reg ) , GFP_NOWAIT ) ;
if ( ! d - > reg )
goto err2 ;
# ifdef CONFIG_SMP
d - > smp = kzalloc ( d - > nr_reg * sizeof ( * d - > smp ) , GFP_NOWAIT ) ;
if ( ! d - > smp )
goto err3 ;
# endif
k = 0 ;
if ( hw - > mask_regs ) {
for ( i = 0 ; i < hw - > nr_mask_regs ; i + + ) {
smp = IS_SMP ( hw - > mask_regs [ i ] ) ;
k + = save_reg ( d , k , hw - > mask_regs [ i ] . set_reg , smp ) ;
k + = save_reg ( d , k , hw - > mask_regs [ i ] . clr_reg , smp ) ;
# ifdef CONFIG_INTC_BALANCING
k + = save_reg ( d , k , hw - > mask_regs [ i ] . dist_reg , 0 ) ;
# endif
}
}
if ( hw - > prio_regs ) {
d - > prio = kzalloc ( hw - > nr_vectors * sizeof ( * d - > prio ) ,
GFP_NOWAIT ) ;
if ( ! d - > prio )
goto err4 ;
for ( i = 0 ; i < hw - > nr_prio_regs ; i + + ) {
smp = IS_SMP ( hw - > prio_regs [ i ] ) ;
k + = save_reg ( d , k , hw - > prio_regs [ i ] . set_reg , smp ) ;
k + = save_reg ( d , k , hw - > prio_regs [ i ] . clr_reg , smp ) ;
}
2012-01-24 17:41:55 +09:00
sort ( d - > prio , hw - > nr_prio_regs , sizeof ( * d - > prio ) ,
intc_handle_int_cmp , NULL ) ;
2010-10-05 22:10:30 +09:00
}
if ( hw - > sense_regs ) {
d - > sense = kzalloc ( hw - > nr_vectors * sizeof ( * d - > sense ) ,
GFP_NOWAIT ) ;
if ( ! d - > sense )
goto err5 ;
for ( i = 0 ; i < hw - > nr_sense_regs ; i + + )
k + = save_reg ( d , k , hw - > sense_regs [ i ] . reg , 0 ) ;
2012-01-24 17:41:55 +09:00
sort ( d - > sense , hw - > nr_sense_regs , sizeof ( * d - > sense ) ,
intc_handle_int_cmp , NULL ) ;
2010-10-05 22:10:30 +09:00
}
if ( hw - > subgroups )
for ( i = 0 ; i < hw - > nr_subgroups ; i + + )
if ( hw - > subgroups [ i ] . reg )
k + = save_reg ( d , k , hw - > subgroups [ i ] . reg , 0 ) ;
memcpy ( & d - > chip , & intc_irq_chip , sizeof ( struct irq_chip ) ) ;
d - > chip . name = desc - > name ;
if ( hw - > ack_regs )
for ( i = 0 ; i < hw - > nr_ack_regs ; i + + )
k + = save_reg ( d , k , hw - > ack_regs [ i ] . set_reg , 0 ) ;
else
2010-10-27 15:42:10 +09:00
d - > chip . irq_mask_ack = d - > chip . irq_disable ;
2010-10-05 22:10:30 +09:00
/* disable bits matching force_disable before registering irqs */
if ( desc - > force_disable )
intc_enable_disable_enum ( desc , d , desc - > force_disable , 0 ) ;
/* disable bits matching force_enable before registering irqs */
if ( desc - > force_enable )
intc_enable_disable_enum ( desc , d , desc - > force_enable , 0 ) ;
BUG_ON ( k > 256 ) ; /* _INTC_ADDR_E() and _INTC_ADDR_D() are 8 bits */
2012-08-01 17:13:46 +09:00
intc_irq_domain_init ( d , hw ) ;
2010-10-05 22:10:30 +09:00
/* register the vectors one by one */
for ( i = 0 ; i < hw - > nr_vectors ; i + + ) {
struct intc_vect * vect = hw - > vectors + i ;
unsigned int irq = evt2irq ( vect - > vect ) ;
2010-10-12 02:03:09 +09:00
int res ;
2010-10-05 22:10:30 +09:00
if ( ! vect - > enum_id )
continue ;
2012-08-01 17:13:46 +09:00
res = irq_create_identity_mapping ( d - > domain , irq ) ;
if ( unlikely ( res ) ) {
2012-08-09 12:59:40 +09:00
if ( res = = - EEXIST ) {
res = irq_domain_associate ( d - > domain , irq , irq ) ;
if ( unlikely ( res ) ) {
pr_err ( " domain association failure \n " ) ;
continue ;
}
} else {
pr_err ( " can't identity map IRQ %d \n " , irq ) ;
continue ;
}
2010-10-05 22:10:30 +09:00
}
intc_irq_xlate_set ( irq , vect - > enum_id , d ) ;
intc_register_irq ( desc , d , vect - > enum_id , irq ) ;
for ( k = i + 1 ; k < hw - > nr_vectors ; k + + ) {
struct intc_vect * vect2 = hw - > vectors + k ;
unsigned int irq2 = evt2irq ( vect2 - > vect ) ;
if ( vect - > enum_id ! = vect2 - > enum_id )
continue ;
/*
* In the case of multi - evt handling and sparse
* IRQ support , each vector still needs to have
* its own backing irq_desc .
*/
2012-08-01 17:13:46 +09:00
res = irq_create_identity_mapping ( d - > domain , irq2 ) ;
if ( unlikely ( res ) ) {
2012-08-09 12:59:40 +09:00
if ( res = = - EEXIST ) {
res = irq_domain_associate ( d - > domain ,
2012-08-20 14:51:50 +09:00
irq2 , irq2 ) ;
2012-08-09 12:59:40 +09:00
if ( unlikely ( res ) ) {
pr_err ( " domain association "
" failure \n " ) ;
continue ;
}
} else {
pr_err ( " can't identity map IRQ %d \n " ,
irq ) ;
continue ;
}
2010-10-05 22:10:30 +09:00
}
vect2 - > enum_id = 0 ;
/* redirect this interrupts to the first one */
2011-03-24 16:31:17 +01:00
irq_set_chip ( irq2 , & dummy_irq_chip ) ;
sh/intc: Fix race in installing chained IRQ handler
Fix a race where a pending interrupt could be received and the handler
called before the handler's data has been setup, by converting to
irq_set_chained_handler_and_data().
Search and conversion was done with coccinelle:
@@
expression E1, E2, E3;
@@
(
-if (irq_set_chained_handler(E1, E3) != 0)
- BUG();
|
-irq_set_chained_handler(E1, E3);
)
-irq_set_handler_data(E1, E2);
+irq_set_chained_handler_and_data(E1, E3, E2);
@@
expression E1, E2, E3;
@@
(
-if (irq_set_chained_handler(E1, E3) != 0)
- BUG();
...
|
-irq_set_chained_handler(E1, E3);
...
)
-irq_set_handler_data(E1, E2);
+irq_set_chained_handler_and_data(E1, E3, E2);
Reported-by: Russell King <rmk+kernel@arm.linux.org.uk>
Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
Cc: Julia Lawall <Julia.Lawall@lip6.fr>
Cc: Simon Horman <horms@verge.net.au>
Cc: Magnus Damm <magnus.damm@gmail.com>
Cc: linux-sh@vger.kernel.org
2015-06-21 20:16:21 +02:00
irq_set_chained_handler_and_data ( irq2 ,
intc_redirect_irq ,
( void * ) irq ) ;
2010-10-05 22:10:30 +09:00
}
}
intc_subgroup_init ( desc , d ) ;
/* enable bits matching force_enable after registering irqs */
if ( desc - > force_enable )
intc_enable_disable_enum ( desc , d , desc - > force_enable , 1 ) ;
2011-12-23 01:23:30 +01:00
d - > skip_suspend = desc - > skip_syscore_suspend ;
2010-10-05 22:10:30 +09:00
nr_intc_controllers + + ;
return 0 ;
err5 :
kfree ( d - > prio ) ;
err4 :
# ifdef CONFIG_SMP
kfree ( d - > smp ) ;
err3 :
# endif
kfree ( d - > reg ) ;
err2 :
for ( k = 0 ; k < d - > nr_windows ; k + + )
if ( d - > window [ k ] . virt )
iounmap ( d - > window [ k ] . virt ) ;
kfree ( d - > window ) ;
err1 :
kfree ( d ) ;
err0 :
pr_err ( " unable to allocate INTC memory \n " ) ;
return - ENOMEM ;
}
2011-03-22 20:19:28 +00:00
static int intc_suspend ( void )
2010-10-05 22:10:30 +09:00
{
struct intc_desc_int * d ;
2011-03-22 20:19:28 +00:00
list_for_each_entry ( d , & intc_list , list ) {
int irq ;
2010-10-05 22:10:30 +09:00
2011-12-23 01:23:30 +01:00
if ( d - > skip_suspend )
continue ;
2011-03-22 20:19:28 +00:00
/* enable wakeup irqs belonging to this intc controller */
for_each_active_irq ( irq ) {
struct irq_data * data ;
struct irq_chip * chip ;
2010-10-05 22:10:30 +09:00
2011-03-22 20:19:28 +00:00
data = irq_get_irq_data ( irq ) ;
chip = irq_data_get_irq_chip ( data ) ;
if ( chip ! = & d - > chip )
continue ;
2011-03-24 14:47:46 +01:00
if ( irqd_is_wakeup_set ( data ) )
2011-03-22 20:19:28 +00:00
chip - > irq_enable ( data ) ;
}
}
return 0 ;
}
2010-10-05 22:10:30 +09:00
2011-03-22 20:19:28 +00:00
static void intc_resume ( void )
2010-10-05 22:10:30 +09:00
{
struct intc_desc_int * d ;
2011-03-22 20:19:28 +00:00
list_for_each_entry ( d , & intc_list , list ) {
int irq ;
2010-10-05 22:10:30 +09:00
2011-12-23 01:23:30 +01:00
if ( d - > skip_suspend )
continue ;
2010-10-28 11:36:31 +09:00
for_each_active_irq ( irq ) {
2011-03-22 20:19:28 +00:00
struct irq_data * data ;
struct irq_chip * chip ;
2010-10-27 15:42:10 +09:00
data = irq_get_irq_data ( irq ) ;
chip = irq_data_get_irq_chip ( data ) ;
2010-10-05 22:10:30 +09:00
/*
* This will catch the redirect and VIRQ cases
* due to the dummy_irq_chip being inserted .
*/
2010-10-27 15:42:10 +09:00
if ( chip ! = & d - > chip )
2010-10-05 22:10:30 +09:00
continue ;
2011-03-24 14:47:46 +01:00
if ( irqd_irq_disabled ( data ) )
2010-10-27 15:42:10 +09:00
chip - > irq_disable ( data ) ;
2010-10-05 22:10:30 +09:00
else
2010-10-27 15:42:10 +09:00
chip - > irq_enable ( data ) ;
2010-10-05 22:10:30 +09:00
}
}
}
2011-03-22 20:19:28 +00:00
struct syscore_ops intc_syscore_ops = {
. suspend = intc_suspend ,
. resume = intc_resume ,
} ;
2010-10-05 22:10:30 +09:00
2011-12-21 15:09:52 -08:00
struct bus_type intc_subsys = {
2010-10-05 22:10:30 +09:00
. name = " intc " ,
2011-12-21 15:09:52 -08:00
. dev_name = " intc " ,
2010-10-05 22:10:30 +09:00
} ;
2011-03-22 20:19:28 +00:00
static ssize_t
2011-12-21 15:09:52 -08:00
show_intc_name ( struct device * dev , struct device_attribute * attr , char * buf )
2011-03-22 20:19:28 +00:00
{
struct intc_desc_int * d ;
2011-12-21 15:09:52 -08:00
d = container_of ( dev , struct intc_desc_int , dev ) ;
2011-03-22 20:19:28 +00:00
return sprintf ( buf , " %s \n " , d - > chip . name ) ;
}
2011-12-21 15:09:52 -08:00
static DEVICE_ATTR ( name , S_IRUGO , show_intc_name , NULL ) ;
2011-03-22 20:19:28 +00:00
2011-12-21 15:09:52 -08:00
static int __init register_intc_devs ( void )
2010-10-05 22:10:30 +09:00
{
struct intc_desc_int * d ;
int error ;
2011-03-22 20:19:28 +00:00
register_syscore_ops ( & intc_syscore_ops ) ;
2011-12-21 15:09:52 -08:00
error = subsys_system_register ( & intc_subsys , NULL ) ;
2010-10-05 22:10:30 +09:00
if ( ! error ) {
list_for_each_entry ( d , & intc_list , list ) {
2011-12-21 15:09:52 -08:00
d - > dev . id = d - > index ;
d - > dev . bus = & intc_subsys ;
error = device_register ( & d - > dev ) ;
2010-10-05 22:10:30 +09:00
if ( error = = 0 )
2011-12-21 15:09:52 -08:00
error = device_create_file ( & d - > dev ,
& dev_attr_name ) ;
2010-10-05 22:10:30 +09:00
if ( error )
break ;
}
}
if ( error )
2011-12-21 15:09:52 -08:00
pr_err ( " device registration error \n " ) ;
2010-10-05 22:10:30 +09:00
return error ;
}
2011-12-21 15:09:52 -08:00
device_initcall ( register_intc_devs ) ;