Merge branch 'irq-core-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/linux-2.6-tip
* 'irq-core-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/linux-2.6-tip: irq: Export functions to allow modular irq drivers genirq: Uninline and sanity check generic_handle_irq() genirq: Remove pointless ifdefs genirq: Make generic irq chip depend on CONFIG_GENERIC_IRQ_CHIP genirq: Add chip suspend and resume callbacks genirq: Implement a generic interrupt chip genirq: Support per-IRQ thread disabling. genirq: irq_desc: Document preflow_handler and affinity_hint genirq: Update DocBook comments genirq: Forgotten updates/deletions after removal of compat code
This commit is contained in:
@@ -48,6 +48,10 @@ config IRQ_PREFLOW_FASTEOI
|
||||
config IRQ_EDGE_EOI_HANDLER
|
||||
bool
|
||||
|
||||
# Generic configurable interrupt chip implementation
|
||||
config GENERIC_IRQ_CHIP
|
||||
bool
|
||||
|
||||
# Support forced irq threading
|
||||
config IRQ_FORCED_THREADING
|
||||
bool
|
||||
|
@@ -1,5 +1,6 @@
|
||||
|
||||
obj-y := irqdesc.o handle.o manage.o spurious.o resend.o chip.o dummychip.o devres.o
|
||||
obj-$(CONFIG_GENERIC_IRQ_CHIP) += generic-chip.o
|
||||
obj-$(CONFIG_GENERIC_IRQ_PROBE) += autoprobe.o
|
||||
obj-$(CONFIG_PROC_FS) += proc.o
|
||||
obj-$(CONFIG_GENERIC_PENDING_IRQ) += migration.o
|
||||
|
@@ -310,6 +310,7 @@ handle_simple_irq(unsigned int irq, struct irq_desc *desc)
|
||||
out_unlock:
|
||||
raw_spin_unlock(&desc->lock);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(handle_simple_irq);
|
||||
|
||||
/**
|
||||
* handle_level_irq - Level type irq handler
|
||||
@@ -573,6 +574,7 @@ __irq_set_handler(unsigned int irq, irq_flow_handler_t handle, int is_chained,
|
||||
if (handle != handle_bad_irq && is_chained) {
|
||||
irq_settings_set_noprobe(desc);
|
||||
irq_settings_set_norequest(desc);
|
||||
irq_settings_set_nothread(desc);
|
||||
irq_startup(desc);
|
||||
}
|
||||
out:
|
||||
@@ -612,6 +614,7 @@ void irq_modify_status(unsigned int irq, unsigned long clr, unsigned long set)
|
||||
|
||||
irq_put_desc_unlock(desc, flags);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(irq_modify_status);
|
||||
|
||||
/**
|
||||
* irq_cpu_online - Invoke all irq_cpu_online functions.
|
||||
|
@@ -27,6 +27,7 @@ static inline void print_irq_desc(unsigned int irq, struct irq_desc *desc)
|
||||
P(IRQ_PER_CPU);
|
||||
P(IRQ_NOPROBE);
|
||||
P(IRQ_NOREQUEST);
|
||||
P(IRQ_NOTHREAD);
|
||||
P(IRQ_NOAUTOEN);
|
||||
|
||||
PS(IRQS_AUTODETECT);
|
||||
|
354
kernel/irq/generic-chip.c
Normal file
354
kernel/irq/generic-chip.c
Normal file
@@ -0,0 +1,354 @@
|
||||
/*
|
||||
* 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>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/kernel_stat.h>
|
||||
#include <linux/syscore_ops.h>
|
||||
|
||||
#include "internals.h"
|
||||
|
||||
static LIST_HEAD(gc_list);
|
||||
static DEFINE_RAW_SPINLOCK(gc_lock);
|
||||
|
||||
static inline struct irq_chip_regs *cur_regs(struct irq_data *d)
|
||||
{
|
||||
return &container_of(d->chip, struct irq_chip_type, chip)->regs;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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);
|
||||
u32 mask = 1 << (d->irq - gc->irq_base);
|
||||
|
||||
irq_gc_lock(gc);
|
||||
irq_reg_writel(mask, gc->reg_base + cur_regs(d)->disable);
|
||||
gc->mask_cache &= ~mask;
|
||||
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);
|
||||
u32 mask = 1 << (d->irq - gc->irq_base);
|
||||
|
||||
irq_gc_lock(gc);
|
||||
gc->mask_cache |= mask;
|
||||
irq_reg_writel(gc->mask_cache, gc->reg_base + cur_regs(d)->mask);
|
||||
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);
|
||||
u32 mask = 1 << (d->irq - gc->irq_base);
|
||||
|
||||
irq_gc_lock(gc);
|
||||
gc->mask_cache &= ~mask;
|
||||
irq_reg_writel(gc->mask_cache, gc->reg_base + cur_regs(d)->mask);
|
||||
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);
|
||||
u32 mask = 1 << (d->irq - gc->irq_base);
|
||||
|
||||
irq_gc_lock(gc);
|
||||
irq_reg_writel(mask, gc->reg_base + cur_regs(d)->enable);
|
||||
gc->mask_cache |= mask;
|
||||
irq_gc_unlock(gc);
|
||||
}
|
||||
|
||||
/**
|
||||
* irq_gc_ack - Ack pending interrupt
|
||||
* @d: irq_data
|
||||
*/
|
||||
void irq_gc_ack(struct irq_data *d)
|
||||
{
|
||||
struct irq_chip_generic *gc = irq_data_get_irq_chip_data(d);
|
||||
u32 mask = 1 << (d->irq - gc->irq_base);
|
||||
|
||||
irq_gc_lock(gc);
|
||||
irq_reg_writel(mask, gc->reg_base + cur_regs(d)->ack);
|
||||
irq_gc_unlock(gc);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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);
|
||||
u32 mask = 1 << (d->irq - gc->irq_base);
|
||||
|
||||
irq_gc_lock(gc);
|
||||
irq_reg_writel(mask, gc->reg_base + cur_regs(d)->mask);
|
||||
irq_reg_writel(mask, gc->reg_base + cur_regs(d)->ack);
|
||||
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);
|
||||
u32 mask = 1 << (d->irq - gc->irq_base);
|
||||
|
||||
irq_gc_lock(gc);
|
||||
irq_reg_writel(mask, gc->reg_base + cur_regs(d)->eoi);
|
||||
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);
|
||||
u32 mask = 1 << (d->irq - gc->irq_base);
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
/*
|
||||
* 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;
|
||||
unsigned int i;
|
||||
|
||||
raw_spin_lock(&gc_lock);
|
||||
list_add_tail(&gc->list, &gc_list);
|
||||
raw_spin_unlock(&gc_lock);
|
||||
|
||||
/* Init mask cache ? */
|
||||
if (flags & IRQ_GC_INIT_MASK_CACHE)
|
||||
gc->mask_cache = irq_reg_readl(gc->reg_base + ct->regs.mask);
|
||||
|
||||
for (i = gc->irq_base; msk; msk >>= 1, i++) {
|
||||
if (!msk & 0x01)
|
||||
continue;
|
||||
|
||||
if (flags & IRQ_GC_INIT_NESTED_LOCK)
|
||||
irq_set_lockdep_class(i, &irq_nested_lock_class);
|
||||
|
||||
irq_set_chip_and_handler(i, &ct->chip, ct->handler);
|
||||
irq_set_chip_data(i, gc);
|
||||
irq_modify_status(i, clr, set);
|
||||
}
|
||||
gc->irq_cnt = i - gc->irq_base;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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++) {
|
||||
if (!msk & 0x01)
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
#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);
|
@@ -22,7 +22,7 @@
|
||||
*/
|
||||
static struct lock_class_key irq_desc_lock_class;
|
||||
|
||||
#if defined(CONFIG_SMP) && defined(CONFIG_GENERIC_HARDIRQS)
|
||||
#if defined(CONFIG_SMP)
|
||||
static void __init init_irq_default_affinity(void)
|
||||
{
|
||||
alloc_cpumask_var(&irq_default_affinity, GFP_NOWAIT);
|
||||
@@ -290,6 +290,22 @@ static int irq_expand_nr_irqs(unsigned int nr)
|
||||
|
||||
#endif /* !CONFIG_SPARSE_IRQ */
|
||||
|
||||
/**
|
||||
* generic_handle_irq - Invoke the handler for a particular irq
|
||||
* @irq: The irq number to handle
|
||||
*
|
||||
*/
|
||||
int generic_handle_irq(unsigned int irq)
|
||||
{
|
||||
struct irq_desc *desc = irq_to_desc(irq);
|
||||
|
||||
if (!desc)
|
||||
return -EINVAL;
|
||||
generic_handle_irq_desc(irq, desc);
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(generic_handle_irq);
|
||||
|
||||
/* Dynamic interrupt handling */
|
||||
|
||||
/**
|
||||
@@ -311,6 +327,7 @@ void irq_free_descs(unsigned int from, unsigned int cnt)
|
||||
bitmap_clear(allocated_irqs, from, cnt);
|
||||
mutex_unlock(&sparse_irq_lock);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(irq_free_descs);
|
||||
|
||||
/**
|
||||
* irq_alloc_descs - allocate and initialize a range of irq descriptors
|
||||
@@ -351,6 +368,7 @@ err:
|
||||
mutex_unlock(&sparse_irq_lock);
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(irq_alloc_descs);
|
||||
|
||||
/**
|
||||
* irq_reserve_irqs - mark irqs allocated
|
||||
@@ -430,7 +448,6 @@ unsigned int kstat_irqs_cpu(unsigned int irq, int cpu)
|
||||
*per_cpu_ptr(desc->kstat_irqs, cpu) : 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_GENERIC_HARDIRQS
|
||||
unsigned int kstat_irqs(unsigned int irq)
|
||||
{
|
||||
struct irq_desc *desc = irq_to_desc(irq);
|
||||
@@ -443,4 +460,3 @@ unsigned int kstat_irqs(unsigned int irq)
|
||||
sum += *per_cpu_ptr(desc->kstat_irqs, cpu);
|
||||
return sum;
|
||||
}
|
||||
#endif /* CONFIG_GENERIC_HARDIRQS */
|
||||
|
@@ -900,7 +900,8 @@ __setup_irq(unsigned int irq, struct irq_desc *desc, struct irqaction *new)
|
||||
*/
|
||||
new->handler = irq_nested_primary_handler;
|
||||
} else {
|
||||
irq_setup_forced_threading(new);
|
||||
if (irq_settings_can_thread(desc))
|
||||
irq_setup_forced_threading(new);
|
||||
}
|
||||
|
||||
/*
|
||||
|
@@ -8,6 +8,7 @@ enum {
|
||||
_IRQ_LEVEL = IRQ_LEVEL,
|
||||
_IRQ_NOPROBE = IRQ_NOPROBE,
|
||||
_IRQ_NOREQUEST = IRQ_NOREQUEST,
|
||||
_IRQ_NOTHREAD = IRQ_NOTHREAD,
|
||||
_IRQ_NOAUTOEN = IRQ_NOAUTOEN,
|
||||
_IRQ_MOVE_PCNTXT = IRQ_MOVE_PCNTXT,
|
||||
_IRQ_NO_BALANCING = IRQ_NO_BALANCING,
|
||||
@@ -20,6 +21,7 @@ enum {
|
||||
#define IRQ_LEVEL GOT_YOU_MORON
|
||||
#define IRQ_NOPROBE GOT_YOU_MORON
|
||||
#define IRQ_NOREQUEST GOT_YOU_MORON
|
||||
#define IRQ_NOTHREAD GOT_YOU_MORON
|
||||
#define IRQ_NOAUTOEN GOT_YOU_MORON
|
||||
#define IRQ_NESTED_THREAD GOT_YOU_MORON
|
||||
#undef IRQF_MODIFY_MASK
|
||||
@@ -94,6 +96,21 @@ static inline void irq_settings_set_norequest(struct irq_desc *desc)
|
||||
desc->status_use_accessors |= _IRQ_NOREQUEST;
|
||||
}
|
||||
|
||||
static inline bool irq_settings_can_thread(struct irq_desc *desc)
|
||||
{
|
||||
return !(desc->status_use_accessors & _IRQ_NOTHREAD);
|
||||
}
|
||||
|
||||
static inline void irq_settings_clr_nothread(struct irq_desc *desc)
|
||||
{
|
||||
desc->status_use_accessors &= ~_IRQ_NOTHREAD;
|
||||
}
|
||||
|
||||
static inline void irq_settings_set_nothread(struct irq_desc *desc)
|
||||
{
|
||||
desc->status_use_accessors |= _IRQ_NOTHREAD;
|
||||
}
|
||||
|
||||
static inline bool irq_settings_can_probe(struct irq_desc *desc)
|
||||
{
|
||||
return !(desc->status_use_accessors & _IRQ_NOPROBE);
|
||||
|
Reference in New Issue
Block a user