@ -7,6 +7,7 @@
# include <linux/irq.h>
# include <linux/slab.h>
# include <linux/export.h>
# include <linux/irqdomain.h>
# include <linux/interrupt.h>
# include <linux/kernel_stat.h>
# include <linux/syscore_ops.h>
@ -244,12 +245,156 @@ irq_gc_init_mask_cache(struct irq_chip_generic *gc, enum irq_gc_flags flags)
}
}
/**
* irq_alloc_domain_generic_chip - Allocate generic chips for an irq domain
* @ d : irq domain for which to allocate chips
* @ irqs_per_chip : Number of interrupts each chip handles
* @ num_ct : Number of irq_chip_type instances associated with this
* @ name : Name of the irq chip
* @ handler : Default flow handler associated with these chips
* @ clr : IRQ_ * bits to clear in the mapping function
* @ set : IRQ_ * bits to set in the mapping function
*/
int irq_alloc_domain_generic_chips ( struct irq_domain * d , int irqs_per_chip ,
int num_ct , const char * name ,
irq_flow_handler_t handler ,
unsigned int clr , unsigned int set ,
enum irq_gc_flags gcflags )
{
struct irq_domain_chip_generic * dgc ;
struct irq_chip_generic * gc ;
int numchips , sz , i ;
unsigned long flags ;
void * tmp ;
if ( d - > gc )
return - EBUSY ;
if ( d - > revmap_type ! = IRQ_DOMAIN_MAP_LINEAR )
return - EINVAL ;
numchips = d - > revmap_data . linear . size / irqs_per_chip ;
if ( ! numchips )
return - EINVAL ;
/* Allocate a pointer, generic chip and chiptypes for each chip */
sz = sizeof ( * dgc ) + numchips * sizeof ( gc ) ;
sz + = numchips * ( sizeof ( * gc ) + num_ct * sizeof ( struct irq_chip_type ) ) ;
tmp = dgc = kzalloc ( sz , GFP_KERNEL ) ;
if ( ! dgc )
return - ENOMEM ;
dgc - > irqs_per_chip = irqs_per_chip ;
dgc - > num_chips = numchips ;
dgc - > irq_flags_to_set = set ;
dgc - > irq_flags_to_clear = clr ;
dgc - > gc_flags = gcflags ;
d - > gc = dgc ;
/* Calc pointer to the first generic chip */
tmp + = sizeof ( * dgc ) + numchips * sizeof ( gc ) ;
for ( i = 0 ; i < numchips ; i + + ) {
/* Store the pointer to the generic chip */
dgc - > gc [ i ] = gc = tmp ;
irq_init_generic_chip ( gc , name , num_ct , i * irqs_per_chip ,
NULL , handler ) ;
gc - > domain = d ;
raw_spin_lock_irqsave ( & gc_lock , flags ) ;
list_add_tail ( & gc - > list , & gc_list ) ;
raw_spin_unlock_irqrestore ( & gc_lock , flags ) ;
/* Calc pointer to the next generic chip */
tmp + = sizeof ( * gc ) + num_ct * sizeof ( struct irq_chip_type ) ;
}
return 0 ;
}
EXPORT_SYMBOL_GPL ( irq_alloc_domain_generic_chips ) ;
/**
* irq_get_domain_generic_chip - Get a pointer to the generic chip of a hw_irq
* @ d : irq domain pointer
* @ hw_irq : Hardware interrupt number
*/
struct irq_chip_generic *
irq_get_domain_generic_chip ( struct irq_domain * d , unsigned int hw_irq )
{
struct irq_domain_chip_generic * dgc = d - > gc ;
int idx ;
if ( ! dgc )
return NULL ;
idx = hw_irq / dgc - > irqs_per_chip ;
if ( idx > = dgc - > num_chips )
return NULL ;
return dgc - > gc [ idx ] ;
}
EXPORT_SYMBOL_GPL ( irq_get_domain_generic_chip ) ;
/*
* Separate lockdep class for interrupt chip which can nest irq_desc
* lock .
*/
static struct lock_class_key irq_nested_lock_class ;
/**
* irq_map_generic_chip - Map a generic chip for an irq domain
*/
static int irq_map_generic_chip ( struct irq_domain * d , unsigned int virq ,
irq_hw_number_t hw_irq )
{
struct irq_data * data = irq_get_irq_data ( virq ) ;
struct irq_domain_chip_generic * dgc = d - > gc ;
struct irq_chip_generic * gc ;
struct irq_chip_type * ct ;
struct irq_chip * chip ;
unsigned long flags ;
int idx ;
if ( ! d - > gc )
return - ENODEV ;
idx = hw_irq / dgc - > irqs_per_chip ;
if ( idx > = dgc - > num_chips )
return - EINVAL ;
gc = dgc - > gc [ idx ] ;
idx = hw_irq % dgc - > irqs_per_chip ;
if ( test_bit ( idx , & gc - > installed ) )
return - EBUSY ;
ct = gc - > chip_types ;
chip = & ct - > chip ;
/* We only init the cache for the first mapping of a generic chip */
if ( ! gc - > installed ) {
raw_spin_lock_irqsave ( & gc - > lock , flags ) ;
irq_gc_init_mask_cache ( gc , dgc - > gc_flags ) ;
raw_spin_unlock_irqrestore ( & gc - > lock , flags ) ;
}
/* Mark the interrupt as installed */
set_bit ( idx , & gc - > installed ) ;
if ( dgc - > gc_flags & IRQ_GC_INIT_NESTED_LOCK )
irq_set_lockdep_class ( virq , & irq_nested_lock_class ) ;
if ( chip - > irq_calc_mask )
chip - > irq_calc_mask ( data ) ;
else
data - > mask = 1 < < idx ;
irq_set_chip_and_handler ( virq , chip , ct - > handler ) ;
irq_set_chip_data ( virq , gc ) ;
irq_modify_status ( virq , dgc - > irq_flags_to_clear , dgc - > irq_flags_to_set ) ;
return 0 ;
}
struct irq_domain_ops irq_generic_chip_ops = {
. map = irq_map_generic_chip ,
. xlate = irq_domain_xlate_onetwocell ,
} ;
EXPORT_SYMBOL_GPL ( irq_generic_chip_ops ) ;
/**
* irq_setup_generic_chip - Setup a range of interrupts with a generic chip
* @ gc : Generic irq chip holding all data
@ -354,6 +499,24 @@ void irq_remove_generic_chip(struct irq_chip_generic *gc, u32 msk,
}
EXPORT_SYMBOL_GPL ( irq_remove_generic_chip ) ;
static struct irq_data * irq_gc_get_irq_data ( struct irq_chip_generic * gc )
{
unsigned int virq ;
if ( ! gc - > domain )
return irq_get_irq_data ( gc - > irq_base ) ;
/*
* We don ' t know which of the irqs has been actually
* installed . Use the first one .
*/
if ( ! gc - > installed )
return NULL ;
virq = irq_find_mapping ( gc - > domain , gc - > irq_base + __ffs ( gc - > installed ) ) ;
return virq ? irq_get_irq_data ( virq ) : NULL ;
}
# ifdef CONFIG_PM
static int irq_gc_suspend ( void )
{
@ -362,8 +525,12 @@ static int irq_gc_suspend(void)
list_for_each_entry ( gc , & gc_list , list ) {
struct irq_chip_type * ct = gc - > chip_types ;
if ( ct - > chip . irq_suspend )
ct - > chip . irq_suspend ( irq_get_irq_data ( gc - > irq_base ) ) ;
if ( ct - > chip . irq_suspend ) {
struct irq_data * data = irq_gc_get_irq_data ( gc ) ;
if ( data )
ct - > chip . irq_suspend ( data ) ;
}
}
return 0 ;
}
@ -375,8 +542,12 @@ static void irq_gc_resume(void)
list_for_each_entry ( gc , & gc_list , list ) {
struct irq_chip_type * ct = gc - > chip_types ;
if ( ct - > chip . irq_resume )
ct - > chip . irq_resume ( irq_get_irq_data ( gc - > irq_base ) ) ;
if ( ct - > chip . irq_resume ) {
struct irq_data * data = irq_gc_get_irq_data ( gc ) ;
if ( data )
ct - > chip . irq_resume ( data ) ;
}
}
}
# else
@ -391,8 +562,12 @@ static void irq_gc_shutdown(void)
list_for_each_entry ( gc , & gc_list , list ) {
struct irq_chip_type * ct = gc - > chip_types ;
if ( ct - > chip . irq_pm_shutdown )
ct - > chip . irq_pm_shutdown ( irq_get_irq_data ( gc - > irq_base ) ) ;
if ( ct - > chip . irq_pm_shutdown ) {
struct irq_data * data = irq_gc_get_irq_data ( gc ) ;
if ( data )
ct - > chip . irq_pm_shutdown ( data ) ;
}
}
}