mirror of
https://github.com/torvalds/linux.git
synced 2024-12-29 06:12:08 +00:00
genirq: Implement a generic interrupt chip
Implement a generic interrupt chip, which is configurable and is able to handle the most common irq chip implementations. Signed-off-by: Thomas Gleixner <tglx@linutronix.de> Cc: linux-arm-kernel@lists.infradead.org Tested-by: H Hartley Sweeten <hsweeten@visionengravers.com> Tested-by: Tony Lindgren <tony@atomide.com> Tested-by; Kevin Hilman <khilman@ti.com>
This commit is contained in:
parent
7f1b1244e1
commit
7d82806247
@ -568,6 +568,141 @@ static inline int irq_reserve_irq(unsigned int irq)
|
||||
return irq_reserve_irqs(irq, 1);
|
||||
}
|
||||
|
||||
#ifndef irq_reg_writel
|
||||
# define irq_reg_writel(val, addr) writel(val, addr)
|
||||
#endif
|
||||
#ifndef irq_reg_readl
|
||||
# define irq_reg_readl(addr) readl(addr)
|
||||
#endif
|
||||
|
||||
/**
|
||||
* struct irq_chip_regs - register offsets for struct irq_gci
|
||||
* @enable: Enable register offset to reg_base
|
||||
* @disable: Disable register offset to reg_base
|
||||
* @mask: Mask register offset to reg_base
|
||||
* @ack: Ack register offset to reg_base
|
||||
* @eoi: Eoi register offset to reg_base
|
||||
* @type: Type configuration register offset to reg_base
|
||||
* @polarity: Polarity configuration register offset to reg_base
|
||||
*/
|
||||
struct irq_chip_regs {
|
||||
unsigned long enable;
|
||||
unsigned long disable;
|
||||
unsigned long mask;
|
||||
unsigned long ack;
|
||||
unsigned long eoi;
|
||||
unsigned long type;
|
||||
unsigned long polarity;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct irq_chip_type - Generic interrupt chip instance for a flow type
|
||||
* @chip: The real interrupt chip which provides the callbacks
|
||||
* @regs: Register offsets for this chip
|
||||
* @handler: Flow handler associated with this chip
|
||||
* @type: Chip can handle these flow types
|
||||
*
|
||||
* A irq_generic_chip can have several instances of irq_chip_type when
|
||||
* it requires different functions and register offsets for different
|
||||
* flow types.
|
||||
*/
|
||||
struct irq_chip_type {
|
||||
struct irq_chip chip;
|
||||
struct irq_chip_regs regs;
|
||||
irq_flow_handler_t handler;
|
||||
u32 type;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct irq_chip_generic - Generic irq chip data structure
|
||||
* @lock: Lock to protect register and cache data access
|
||||
* @reg_base: Register base address (virtual)
|
||||
* @irq_base: Interrupt base nr for this chip
|
||||
* @irq_cnt: Number of interrupts handled by this chip
|
||||
* @mask_cache: Cached mask register
|
||||
* @type_cache: Cached type register
|
||||
* @polarity_cache: Cached polarity register
|
||||
* @wake_enabled: Interrupt can wakeup from suspend
|
||||
* @wake_active: Interrupt is marked as an wakeup from suspend source
|
||||
* @num_ct: Number of available irq_chip_type instances (usually 1)
|
||||
* @private: Private data for non generic chip callbacks
|
||||
* @chip_types: Array of interrupt irq_chip_types
|
||||
*
|
||||
* Note, that irq_chip_generic can have multiple irq_chip_type
|
||||
* implementations which can be associated to a particular irq line of
|
||||
* an irq_chip_generic instance. That allows to share and protect
|
||||
* state in an irq_chip_generic instance when we need to implement
|
||||
* different flow mechanisms (level/edge) for it.
|
||||
*/
|
||||
struct irq_chip_generic {
|
||||
raw_spinlock_t lock;
|
||||
void __iomem *reg_base;
|
||||
unsigned int irq_base;
|
||||
unsigned int irq_cnt;
|
||||
u32 mask_cache;
|
||||
u32 type_cache;
|
||||
u32 polarity_cache;
|
||||
u32 wake_enabled;
|
||||
u32 wake_active;
|
||||
unsigned int num_ct;
|
||||
void *private;
|
||||
struct irq_chip_type chip_types[0];
|
||||
};
|
||||
|
||||
/**
|
||||
* enum irq_gc_flags - Initialization flags for generic irq chips
|
||||
* @IRQ_GC_INIT_MASK_CACHE: Initialize the mask_cache by reading mask reg
|
||||
* @IRQ_GC_INIT_NESTED_LOCK: Set the lock class of the irqs to nested for
|
||||
* irq chips which need to call irq_set_wake() on
|
||||
* the parent irq. Usually GPIO implementations
|
||||
*/
|
||||
enum irq_gc_flags {
|
||||
IRQ_GC_INIT_MASK_CACHE = 1 << 0,
|
||||
IRQ_GC_INIT_NESTED_LOCK = 1 << 1,
|
||||
};
|
||||
|
||||
/* Generic chip callback functions */
|
||||
void irq_gc_noop(struct irq_data *d);
|
||||
void irq_gc_mask_disable_reg(struct irq_data *d);
|
||||
void irq_gc_mask_set_bit(struct irq_data *d);
|
||||
void irq_gc_mask_clr_bit(struct irq_data *d);
|
||||
void irq_gc_unmask_enable_reg(struct irq_data *d);
|
||||
void irq_gc_ack(struct irq_data *d);
|
||||
void irq_gc_mask_disable_reg_and_ack(struct irq_data *d);
|
||||
void irq_gc_eoi(struct irq_data *d);
|
||||
int irq_gc_set_wake(struct irq_data *d, unsigned int on);
|
||||
|
||||
/* Setup functions for irq_chip_generic */
|
||||
struct irq_chip_generic *
|
||||
irq_alloc_generic_chip(const char *name, int nr_ct, unsigned int irq_base,
|
||||
void __iomem *reg_base, irq_flow_handler_t handler);
|
||||
void irq_setup_generic_chip(struct irq_chip_generic *gc, u32 msk,
|
||||
enum irq_gc_flags flags, unsigned int clr,
|
||||
unsigned int set);
|
||||
int irq_setup_alt_chip(struct irq_data *d, unsigned int type);
|
||||
|
||||
static inline struct irq_chip_type *irq_data_get_chip_type(struct irq_data *d)
|
||||
{
|
||||
return container_of(d->chip, struct irq_chip_type, chip);
|
||||
}
|
||||
|
||||
#define IRQ_MSK(n) (u32)((n) < 32 ? ((1 << (n)) - 1) : UINT_MAX)
|
||||
|
||||
#ifdef CONFIG_SMP
|
||||
static inline void irq_gc_lock(struct irq_chip_generic *gc)
|
||||
{
|
||||
raw_spin_lock(&gc->lock);
|
||||
}
|
||||
|
||||
static inline void irq_gc_unlock(struct irq_chip_generic *gc)
|
||||
{
|
||||
raw_spin_unlock(&gc->lock);
|
||||
}
|
||||
#else
|
||||
static inline void irq_gc_lock(struct irq_chip_generic *gc) { }
|
||||
static inline void irq_gc_unlock(struct irq_chip_generic *gc) { }
|
||||
#endif
|
||||
|
||||
#endif /* CONFIG_GENERIC_HARDIRQS */
|
||||
|
||||
#endif /* !CONFIG_S390 */
|
||||
|
@ -1,5 +1,6 @@
|
||||
|
||||
obj-y := irqdesc.o handle.o manage.o spurious.o resend.o chip.o dummychip.o devres.o
|
||||
obj-y += generic-chip.o
|
||||
obj-$(CONFIG_GENERIC_IRQ_PROBE) += autoprobe.o
|
||||
obj-$(CONFIG_PROC_FS) += proc.o
|
||||
obj-$(CONFIG_GENERIC_PENDING_IRQ) += migration.o
|
||||
|
261
kernel/irq/generic-chip.c
Normal file
261
kernel/irq/generic-chip.c
Normal file
@ -0,0 +1,261 @@
|
||||
/*
|
||||
* 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 "internals.h"
|
||||
|
||||
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;
|
||||
|
||||
/* 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;
|
||||
}
|
Loading…
Reference in New Issue
Block a user