2011-04-03 11:42:53 +02:00
/*
* Library implementing the most common irq chip callback functions
*
* Copyright ( C ) 2011 , Thomas Gleixner
*/
# include <linux/io.h>
# include <linux/irq.h>
# include <linux/slab.h>
2011-05-26 16:00:52 -04:00
# include <linux/export.h>
2011-04-03 11:42:53 +02:00
# include <linux/interrupt.h>
# include <linux/kernel_stat.h>
2011-04-15 22:36:08 +02:00
# include <linux/syscore_ops.h>
2011-04-03 11:42:53 +02:00
# include "internals.h"
2011-04-15 22:36:08 +02:00
static LIST_HEAD ( gc_list ) ;
static DEFINE_RAW_SPINLOCK ( gc_lock ) ;
2011-04-03 11:42:53 +02:00
/**
* irq_gc_noop - NOOP function
* @ d : irq_data
*/
void irq_gc_noop ( struct irq_data * d )
{
}
/**
* irq_gc_mask_disable_reg - Mask chip via disable register
* @ d : irq_data
*
* Chip has separate enable / disable registers instead of a single mask
* register .
*/
void irq_gc_mask_disable_reg ( struct irq_data * d )
{
struct irq_chip_generic * gc = irq_data_get_irq_chip_data ( d ) ;
2013-05-06 14:30:17 +00:00
struct irq_chip_type * ct = irq_data_get_chip_type ( d ) ;
2013-05-06 14:30:22 +00:00
u32 mask = d - > mask ;
2011-04-03 11:42:53 +02:00
irq_gc_lock ( gc ) ;
2013-05-06 14:30:17 +00:00
irq_reg_writel ( mask , gc - > reg_base + ct - > regs . disable ) ;
2013-05-06 14:30:19 +00:00
* ct - > mask_cache & = ~ mask ;
2011-04-03 11:42:53 +02:00
irq_gc_unlock ( gc ) ;
}
/**
* irq_gc_mask_set_mask_bit - Mask chip via setting bit in mask register
* @ d : irq_data
*
* Chip has a single mask register . Values of this register are cached
* and protected by gc - > lock
*/
void irq_gc_mask_set_bit ( struct irq_data * d )
{
struct irq_chip_generic * gc = irq_data_get_irq_chip_data ( d ) ;
2013-05-06 14:30:17 +00:00
struct irq_chip_type * ct = irq_data_get_chip_type ( d ) ;
2013-05-06 14:30:22 +00:00
u32 mask = d - > mask ;
2011-04-03 11:42:53 +02:00
irq_gc_lock ( gc ) ;
2013-05-06 14:30:19 +00:00
* ct - > mask_cache | = mask ;
irq_reg_writel ( * ct - > mask_cache , gc - > reg_base + ct - > regs . mask ) ;
2011-04-03 11:42:53 +02:00
irq_gc_unlock ( gc ) ;
}
/**
* irq_gc_mask_set_mask_bit - Mask chip via clearing bit in mask register
* @ d : irq_data
*
* Chip has a single mask register . Values of this register are cached
* and protected by gc - > lock
*/
void irq_gc_mask_clr_bit ( struct irq_data * d )
{
struct irq_chip_generic * gc = irq_data_get_irq_chip_data ( d ) ;
2013-05-06 14:30:17 +00:00
struct irq_chip_type * ct = irq_data_get_chip_type ( d ) ;
2013-05-06 14:30:22 +00:00
u32 mask = d - > mask ;
2011-04-03 11:42:53 +02:00
irq_gc_lock ( gc ) ;
2013-05-06 14:30:19 +00:00
* ct - > mask_cache & = ~ mask ;
irq_reg_writel ( * ct - > mask_cache , gc - > reg_base + ct - > regs . mask ) ;
2011-04-03 11:42:53 +02:00
irq_gc_unlock ( gc ) ;
}
/**
* irq_gc_unmask_enable_reg - Unmask chip via enable register
* @ d : irq_data
*
* Chip has separate enable / disable registers instead of a single mask
* register .
*/
void irq_gc_unmask_enable_reg ( struct irq_data * d )
{
struct irq_chip_generic * gc = irq_data_get_irq_chip_data ( d ) ;
2013-05-06 14:30:17 +00:00
struct irq_chip_type * ct = irq_data_get_chip_type ( d ) ;
2013-05-06 14:30:22 +00:00
u32 mask = d - > mask ;
2011-04-03 11:42:53 +02:00
irq_gc_lock ( gc ) ;
2013-05-06 14:30:17 +00:00
irq_reg_writel ( mask , gc - > reg_base + ct - > regs . enable ) ;
2013-05-06 14:30:19 +00:00
* ct - > mask_cache | = mask ;
2011-04-03 11:42:53 +02:00
irq_gc_unlock ( gc ) ;
}
/**
2011-07-06 12:41:31 -04:00
* irq_gc_ack_set_bit - Ack pending interrupt via setting bit
2011-04-03 11:42:53 +02:00
* @ d : irq_data
*/
2011-07-06 12:41:31 -04:00
void irq_gc_ack_set_bit ( struct irq_data * d )
2011-04-03 11:42:53 +02:00
{
struct irq_chip_generic * gc = irq_data_get_irq_chip_data ( d ) ;
2013-05-06 14:30:17 +00:00
struct irq_chip_type * ct = irq_data_get_chip_type ( d ) ;
2013-05-06 14:30:22 +00:00
u32 mask = d - > mask ;
2011-04-03 11:42:53 +02:00
irq_gc_lock ( gc ) ;
2013-05-06 14:30:17 +00:00
irq_reg_writel ( mask , gc - > reg_base + ct - > regs . ack ) ;
2011-04-03 11:42:53 +02:00
irq_gc_unlock ( gc ) ;
}
2011-07-06 12:41:31 -04:00
/**
* irq_gc_ack_clr_bit - Ack pending interrupt via clearing bit
* @ d : irq_data
*/
void irq_gc_ack_clr_bit ( struct irq_data * d )
{
struct irq_chip_generic * gc = irq_data_get_irq_chip_data ( d ) ;
2013-05-06 14:30:17 +00:00
struct irq_chip_type * ct = irq_data_get_chip_type ( d ) ;
2013-05-06 14:30:22 +00:00
u32 mask = ~ d - > mask ;
2011-07-06 12:41:31 -04:00
irq_gc_lock ( gc ) ;
2013-05-06 14:30:17 +00:00
irq_reg_writel ( mask , gc - > reg_base + ct - > regs . ack ) ;
2011-07-06 12:41:31 -04:00
irq_gc_unlock ( gc ) ;
}
2011-04-03 11:42:53 +02:00
/**
* irq_gc_mask_disable_reg_and_ack - Mask and ack pending interrupt
* @ d : irq_data
*/
void irq_gc_mask_disable_reg_and_ack ( struct irq_data * d )
{
struct irq_chip_generic * gc = irq_data_get_irq_chip_data ( d ) ;
2013-05-06 14:30:17 +00:00
struct irq_chip_type * ct = irq_data_get_chip_type ( d ) ;
2013-05-06 14:30:22 +00:00
u32 mask = d - > mask ;
2011-04-03 11:42:53 +02:00
irq_gc_lock ( gc ) ;
2013-05-06 14:30:17 +00:00
irq_reg_writel ( mask , gc - > reg_base + ct - > regs . mask ) ;
irq_reg_writel ( mask , gc - > reg_base + ct - > regs . ack ) ;
2011-04-03 11:42:53 +02:00
irq_gc_unlock ( gc ) ;
}
/**
* irq_gc_eoi - EOI interrupt
* @ d : irq_data
*/
void irq_gc_eoi ( struct irq_data * d )
{
struct irq_chip_generic * gc = irq_data_get_irq_chip_data ( d ) ;
2013-05-06 14:30:17 +00:00
struct irq_chip_type * ct = irq_data_get_chip_type ( d ) ;
2013-05-06 14:30:22 +00:00
u32 mask = d - > mask ;
2011-04-03 11:42:53 +02:00
irq_gc_lock ( gc ) ;
2013-05-06 14:30:17 +00:00
irq_reg_writel ( mask , gc - > reg_base + ct - > regs . eoi ) ;
2011-04-03 11:42:53 +02:00
irq_gc_unlock ( gc ) ;
}
/**
* irq_gc_set_wake - Set / clr wake bit for an interrupt
* @ d : irq_data
*
* For chips where the wake from suspend functionality is not
* configured in a separate register and the wakeup active state is
* just stored in a bitmask .
*/
int irq_gc_set_wake ( struct irq_data * d , unsigned int on )
{
struct irq_chip_generic * gc = irq_data_get_irq_chip_data ( d ) ;
2013-05-06 14:30:22 +00:00
u32 mask = d - > mask ;
2011-04-03 11:42:53 +02:00
if ( ! ( mask & gc - > wake_enabled ) )
return - EINVAL ;
irq_gc_lock ( gc ) ;
if ( on )
gc - > wake_active | = mask ;
else
gc - > wake_active & = ~ mask ;
irq_gc_unlock ( gc ) ;
return 0 ;
}
/**
* irq_alloc_generic_chip - Allocate a generic chip and initialize it
* @ name : Name of the irq chip
* @ num_ct : Number of irq_chip_type instances associated with this
* @ irq_base : Interrupt base nr for this chip
* @ reg_base : Register base address ( virtual )
* @ handler : Default flow handler associated with this chip
*
* Returns an initialized irq_chip_generic structure . The chip defaults
* to the primary ( index 0 ) irq_chip_type and @ handler
*/
struct irq_chip_generic *
irq_alloc_generic_chip ( const char * name , int num_ct , unsigned int irq_base ,
void __iomem * reg_base , irq_flow_handler_t handler )
{
struct irq_chip_generic * gc ;
unsigned long sz = sizeof ( * gc ) + num_ct * sizeof ( struct irq_chip_type ) ;
gc = kzalloc ( sz , GFP_KERNEL ) ;
if ( gc ) {
raw_spin_lock_init ( & gc - > lock ) ;
gc - > num_ct = num_ct ;
gc - > irq_base = irq_base ;
gc - > reg_base = reg_base ;
gc - > chip_types - > chip . name = name ;
gc - > chip_types - > handler = handler ;
}
return gc ;
}
2011-10-17 11:08:46 +09:00
EXPORT_SYMBOL_GPL ( irq_alloc_generic_chip ) ;
2011-04-03 11:42:53 +02:00
/*
* Separate lockdep class for interrupt chip which can nest irq_desc
* lock .
*/
static struct lock_class_key irq_nested_lock_class ;
/**
* irq_setup_generic_chip - Setup a range of interrupts with a generic chip
* @ gc : Generic irq chip holding all data
* @ msk : Bitmask holding the irqs to initialize relative to gc - > irq_base
* @ flags : Flags for initialization
* @ clr : IRQ_ * bits to clear
* @ set : IRQ_ * bits to set
*
* Set up max . 32 interrupts starting from gc - > irq_base . Note , this
* initializes all interrupts to the primary irq_chip_type and its
* associated handler .
*/
void irq_setup_generic_chip ( struct irq_chip_generic * gc , u32 msk ,
enum irq_gc_flags flags , unsigned int clr ,
unsigned int set )
{
struct irq_chip_type * ct = gc - > chip_types ;
2013-05-06 14:30:24 +00:00
struct irq_chip * chip = & ct - > chip ;
2011-04-03 11:42:53 +02:00
unsigned int i ;
2013-05-06 14:30:21 +00:00
u32 * mskptr = & gc - > mask_cache , mskreg = ct - > regs . mask ;
2011-04-03 11:42:53 +02:00
2011-04-15 22:36:08 +02:00
raw_spin_lock ( & gc_lock ) ;
list_add_tail ( & gc - > list , & gc_list ) ;
raw_spin_unlock ( & gc_lock ) ;
2013-05-06 14:30:21 +00:00
for ( i = 0 ; i < gc - > num_ct ; i + + ) {
if ( flags & IRQ_GC_MASK_CACHE_PER_TYPE ) {
mskptr = & ct [ i ] . mask_cache_priv ;
mskreg = ct [ i ] . regs . mask ;
}
ct [ i ] . mask_cache = mskptr ;
if ( flags & IRQ_GC_INIT_MASK_CACHE )
* mskptr = irq_reg_readl ( gc - > reg_base + mskreg ) ;
}
2013-05-06 14:30:19 +00:00
2011-04-03 11:42:53 +02:00
for ( i = gc - > irq_base ; msk ; msk > > = 1 , i + + ) {
2011-07-21 15:29:14 +09:00
if ( ! ( msk & 0x01 ) )
2011-04-03 11:42:53 +02:00
continue ;
if ( flags & IRQ_GC_INIT_NESTED_LOCK )
irq_set_lockdep_class ( i , & irq_nested_lock_class ) ;
2013-05-06 14:30:22 +00:00
if ( ! ( flags & IRQ_GC_NO_MASK ) ) {
struct irq_data * d = irq_get_irq_data ( i ) ;
2013-05-06 14:30:24 +00:00
if ( chip - > irq_calc_mask )
chip - > irq_calc_mask ( d ) ;
else
d - > mask = 1 < < ( i - gc - > irq_base ) ;
2013-05-06 14:30:22 +00:00
}
2013-05-06 14:30:24 +00:00
irq_set_chip_and_handler ( i , chip , ct - > handler ) ;
2011-04-03 11:42:53 +02:00
irq_set_chip_data ( i , gc ) ;
irq_modify_status ( i , clr , set ) ;
}
gc - > irq_cnt = i - gc - > irq_base ;
}
2011-10-17 11:08:46 +09:00
EXPORT_SYMBOL_GPL ( irq_setup_generic_chip ) ;
2011-04-03 11:42:53 +02:00
/**
* irq_setup_alt_chip - Switch to alternative chip
* @ d : irq_data for this interrupt
* @ type Flow type to be initialized
*
* Only to be called from chip - > irq_set_type ( ) callbacks .
*/
int irq_setup_alt_chip ( struct irq_data * d , unsigned int type )
{
struct irq_chip_generic * gc = irq_data_get_irq_chip_data ( d ) ;
struct irq_chip_type * ct = gc - > chip_types ;
unsigned int i ;
for ( i = 0 ; i < gc - > num_ct ; i + + , ct + + ) {
if ( ct - > type & type ) {
d - > chip = & ct - > chip ;
irq_data_to_desc ( d ) - > handle_irq = ct - > handler ;
return 0 ;
}
}
return - EINVAL ;
}
2011-10-17 11:08:46 +09:00
EXPORT_SYMBOL_GPL ( irq_setup_alt_chip ) ;
2011-04-15 22:36:08 +02:00
/**
* irq_remove_generic_chip - Remove a chip
* @ gc : Generic irq chip holding all data
* @ msk : Bitmask holding the irqs to initialize relative to gc - > irq_base
* @ clr : IRQ_ * bits to clear
* @ set : IRQ_ * bits to set
*
* Remove up to 32 interrupts starting from gc - > irq_base .
*/
void irq_remove_generic_chip ( struct irq_chip_generic * gc , u32 msk ,
unsigned int clr , unsigned int set )
{
unsigned int i = gc - > irq_base ;
raw_spin_lock ( & gc_lock ) ;
list_del ( & gc - > list ) ;
raw_spin_unlock ( & gc_lock ) ;
for ( ; msk ; msk > > = 1 , i + + ) {
2011-07-21 15:29:14 +09:00
if ( ! ( msk & 0x01 ) )
2011-04-15 22:36:08 +02:00
continue ;
/* Remove handler first. That will mask the irq line */
irq_set_handler ( i , NULL ) ;
irq_set_chip ( i , & no_irq_chip ) ;
irq_set_chip_data ( i , NULL ) ;
irq_modify_status ( i , clr , set ) ;
}
}
2011-10-17 11:08:46 +09:00
EXPORT_SYMBOL_GPL ( irq_remove_generic_chip ) ;
2011-04-15 22:36:08 +02:00
# ifdef CONFIG_PM
static int irq_gc_suspend ( void )
{
struct irq_chip_generic * gc ;
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 ) ) ;
}
return 0 ;
}
static void irq_gc_resume ( void )
{
struct irq_chip_generic * gc ;
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 ) ) ;
}
}
# else
# define irq_gc_suspend NULL
# define irq_gc_resume NULL
# endif
static void irq_gc_shutdown ( void )
{
struct irq_chip_generic * gc ;
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 ) ) ;
}
}
static struct syscore_ops irq_gc_syscore_ops = {
. suspend = irq_gc_suspend ,
. resume = irq_gc_resume ,
. shutdown = irq_gc_shutdown ,
} ;
static int __init irq_gc_init_ops ( void )
{
register_syscore_ops ( & irq_gc_syscore_ops ) ;
return 0 ;
}
device_initcall ( irq_gc_init_ops ) ;