Commit d90a7e86 authored by Linus Torvalds's avatar Linus Torvalds

Merge branch 'irq-threaded-for-linus' of...

Merge branch 'irq-threaded-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/linux-2.6-tip

* 'irq-threaded-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/linux-2.6-tip:
  genirq: Do not mask oneshot edge type interrupts
  genirq: Support nested threaded irq handling
  genirq: Add buslock support
  genirq: Add oneshot support
parents 12a49961 4dbc9ca2
...@@ -50,6 +50,9 @@ ...@@ -50,6 +50,9 @@
* IRQF_IRQPOLL - Interrupt is used for polling (only the interrupt that is * IRQF_IRQPOLL - Interrupt is used for polling (only the interrupt that is
* registered first in an shared interrupt is considered for * registered first in an shared interrupt is considered for
* performance reasons) * performance reasons)
* IRQF_ONESHOT - Interrupt is not reenabled after the hardirq handler finished.
* Used by threaded interrupts which need to keep the
* irq line disabled until the threaded handler has been run.
*/ */
#define IRQF_DISABLED 0x00000020 #define IRQF_DISABLED 0x00000020
#define IRQF_SAMPLE_RANDOM 0x00000040 #define IRQF_SAMPLE_RANDOM 0x00000040
...@@ -59,6 +62,7 @@ ...@@ -59,6 +62,7 @@
#define IRQF_PERCPU 0x00000400 #define IRQF_PERCPU 0x00000400
#define IRQF_NOBALANCING 0x00000800 #define IRQF_NOBALANCING 0x00000800
#define IRQF_IRQPOLL 0x00001000 #define IRQF_IRQPOLL 0x00001000
#define IRQF_ONESHOT 0x00002000
/* /*
* Bits used by threaded handlers: * Bits used by threaded handlers:
......
...@@ -69,6 +69,8 @@ typedef void (*irq_flow_handler_t)(unsigned int irq, ...@@ -69,6 +69,8 @@ typedef void (*irq_flow_handler_t)(unsigned int irq,
#define IRQ_MOVE_PCNTXT 0x01000000 /* IRQ migration from process context */ #define IRQ_MOVE_PCNTXT 0x01000000 /* IRQ migration from process context */
#define IRQ_AFFINITY_SET 0x02000000 /* IRQ affinity was set from userspace*/ #define IRQ_AFFINITY_SET 0x02000000 /* IRQ affinity was set from userspace*/
#define IRQ_SUSPENDED 0x04000000 /* IRQ has gone through suspend sequence */ #define IRQ_SUSPENDED 0x04000000 /* IRQ has gone through suspend sequence */
#define IRQ_ONESHOT 0x08000000 /* IRQ is not unmasked after hardirq */
#define IRQ_NESTED_THREAD 0x10000000 /* IRQ is nested into another, no own handler thread */
#ifdef CONFIG_IRQ_PER_CPU #ifdef CONFIG_IRQ_PER_CPU
# define CHECK_IRQ_PER_CPU(var) ((var) & IRQ_PER_CPU) # define CHECK_IRQ_PER_CPU(var) ((var) & IRQ_PER_CPU)
...@@ -100,6 +102,9 @@ struct msi_desc; ...@@ -100,6 +102,9 @@ struct msi_desc;
* @set_type: set the flow type (IRQ_TYPE_LEVEL/etc.) of an IRQ * @set_type: set the flow type (IRQ_TYPE_LEVEL/etc.) of an IRQ
* @set_wake: enable/disable power-management wake-on of an IRQ * @set_wake: enable/disable power-management wake-on of an IRQ
* *
* @bus_lock: function to lock access to slow bus (i2c) chips
* @bus_sync_unlock: function to sync and unlock slow bus (i2c) chips
*
* @release: release function solely used by UML * @release: release function solely used by UML
* @typename: obsoleted by name, kept as migration helper * @typename: obsoleted by name, kept as migration helper
*/ */
...@@ -123,6 +128,9 @@ struct irq_chip { ...@@ -123,6 +128,9 @@ struct irq_chip {
int (*set_type)(unsigned int irq, unsigned int flow_type); int (*set_type)(unsigned int irq, unsigned int flow_type);
int (*set_wake)(unsigned int irq, unsigned int on); int (*set_wake)(unsigned int irq, unsigned int on);
void (*bus_lock)(unsigned int irq);
void (*bus_sync_unlock)(unsigned int irq);
/* Currently used only by UML, might disappear one day.*/ /* Currently used only by UML, might disappear one day.*/
#ifdef CONFIG_IRQ_RELEASE_METHOD #ifdef CONFIG_IRQ_RELEASE_METHOD
void (*release)(unsigned int irq, void *dev_id); void (*release)(unsigned int irq, void *dev_id);
...@@ -373,6 +381,8 @@ set_irq_chained_handler(unsigned int irq, ...@@ -373,6 +381,8 @@ set_irq_chained_handler(unsigned int irq,
__set_irq_handler(irq, handle, 1, NULL); __set_irq_handler(irq, handle, 1, NULL);
} }
extern void set_irq_nested_thread(unsigned int irq, int nest);
extern void set_irq_noprobe(unsigned int irq); extern void set_irq_noprobe(unsigned int irq);
extern void set_irq_probe(unsigned int irq); extern void set_irq_probe(unsigned int irq);
......
...@@ -222,6 +222,34 @@ int set_irq_chip_data(unsigned int irq, void *data) ...@@ -222,6 +222,34 @@ int set_irq_chip_data(unsigned int irq, void *data)
} }
EXPORT_SYMBOL(set_irq_chip_data); EXPORT_SYMBOL(set_irq_chip_data);
/**
* set_irq_nested_thread - Set/Reset the IRQ_NESTED_THREAD flag of an irq
*
* @irq: Interrupt number
* @nest: 0 to clear / 1 to set the IRQ_NESTED_THREAD flag
*
* The IRQ_NESTED_THREAD flag indicates that on
* request_threaded_irq() no separate interrupt thread should be
* created for the irq as the handler are called nested in the
* context of a demultiplexing interrupt handler thread.
*/
void set_irq_nested_thread(unsigned int irq, int nest)
{
struct irq_desc *desc = irq_to_desc(irq);
unsigned long flags;
if (!desc)
return;
spin_lock_irqsave(&desc->lock, flags);
if (nest)
desc->status |= IRQ_NESTED_THREAD;
else
desc->status &= ~IRQ_NESTED_THREAD;
spin_unlock_irqrestore(&desc->lock, flags);
}
EXPORT_SYMBOL_GPL(set_irq_nested_thread);
/* /*
* default enable function * default enable function
*/ */
...@@ -299,6 +327,45 @@ static inline void mask_ack_irq(struct irq_desc *desc, int irq) ...@@ -299,6 +327,45 @@ static inline void mask_ack_irq(struct irq_desc *desc, int irq)
} }
} }
/*
* handle_nested_irq - Handle a nested irq from a irq thread
* @irq: the interrupt number
*
* Handle interrupts which are nested into a threaded interrupt
* handler. The handler function is called inside the calling
* threads context.
*/
void handle_nested_irq(unsigned int irq)
{
struct irq_desc *desc = irq_to_desc(irq);
struct irqaction *action;
irqreturn_t action_ret;
might_sleep();
spin_lock_irq(&desc->lock);
kstat_incr_irqs_this_cpu(irq, desc);
action = desc->action;
if (unlikely(!action || (desc->status & IRQ_DISABLED)))
goto out_unlock;
desc->status |= IRQ_INPROGRESS;
spin_unlock_irq(&desc->lock);
action_ret = action->thread_fn(action->irq, action->dev_id);
if (!noirqdebug)
note_interrupt(irq, desc, action_ret);
spin_lock_irq(&desc->lock);
desc->status &= ~IRQ_INPROGRESS;
out_unlock:
spin_unlock_irq(&desc->lock);
}
EXPORT_SYMBOL_GPL(handle_nested_irq);
/** /**
* handle_simple_irq - Simple and software-decoded IRQs. * handle_simple_irq - Simple and software-decoded IRQs.
* @irq: the interrupt number * @irq: the interrupt number
...@@ -382,7 +449,10 @@ handle_level_irq(unsigned int irq, struct irq_desc *desc) ...@@ -382,7 +449,10 @@ handle_level_irq(unsigned int irq, struct irq_desc *desc)
spin_lock(&desc->lock); spin_lock(&desc->lock);
desc->status &= ~IRQ_INPROGRESS; desc->status &= ~IRQ_INPROGRESS;
if (!(desc->status & IRQ_DISABLED) && desc->chip->unmask)
if (unlikely(desc->status & IRQ_ONESHOT))
desc->status |= IRQ_MASKED;
else if (!(desc->status & IRQ_DISABLED) && desc->chip->unmask)
desc->chip->unmask(irq); desc->chip->unmask(irq);
out_unlock: out_unlock:
spin_unlock(&desc->lock); spin_unlock(&desc->lock);
...@@ -572,6 +642,7 @@ __set_irq_handler(unsigned int irq, irq_flow_handler_t handle, int is_chained, ...@@ -572,6 +642,7 @@ __set_irq_handler(unsigned int irq, irq_flow_handler_t handle, int is_chained,
desc->chip = &dummy_irq_chip; desc->chip = &dummy_irq_chip;
} }
chip_bus_lock(irq, desc);
spin_lock_irqsave(&desc->lock, flags); spin_lock_irqsave(&desc->lock, flags);
/* Uninstall? */ /* Uninstall? */
...@@ -591,6 +662,7 @@ __set_irq_handler(unsigned int irq, irq_flow_handler_t handle, int is_chained, ...@@ -591,6 +662,7 @@ __set_irq_handler(unsigned int irq, irq_flow_handler_t handle, int is_chained,
desc->chip->startup(irq); desc->chip->startup(irq);
} }
spin_unlock_irqrestore(&desc->lock, flags); spin_unlock_irqrestore(&desc->lock, flags);
chip_bus_sync_unlock(irq, desc);
} }
EXPORT_SYMBOL_GPL(__set_irq_handler); EXPORT_SYMBOL_GPL(__set_irq_handler);
......
...@@ -44,6 +44,19 @@ extern int irq_select_affinity_usr(unsigned int irq); ...@@ -44,6 +44,19 @@ extern int irq_select_affinity_usr(unsigned int irq);
extern void irq_set_thread_affinity(struct irq_desc *desc); extern void irq_set_thread_affinity(struct irq_desc *desc);
/* Inline functions for support of irq chips on slow busses */
static inline void chip_bus_lock(unsigned int irq, struct irq_desc *desc)
{
if (unlikely(desc->chip->bus_lock))
desc->chip->bus_lock(irq);
}
static inline void chip_bus_sync_unlock(unsigned int irq, struct irq_desc *desc)
{
if (unlikely(desc->chip->bus_sync_unlock))
desc->chip->bus_sync_unlock(irq);
}
/* /*
* Debugging printout: * Debugging printout:
*/ */
......
...@@ -230,9 +230,11 @@ void disable_irq_nosync(unsigned int irq) ...@@ -230,9 +230,11 @@ void disable_irq_nosync(unsigned int irq)
if (!desc) if (!desc)
return; return;
chip_bus_lock(irq, desc);
spin_lock_irqsave(&desc->lock, flags); spin_lock_irqsave(&desc->lock, flags);
__disable_irq(desc, irq, false); __disable_irq(desc, irq, false);
spin_unlock_irqrestore(&desc->lock, flags); spin_unlock_irqrestore(&desc->lock, flags);
chip_bus_sync_unlock(irq, desc);
} }
EXPORT_SYMBOL(disable_irq_nosync); EXPORT_SYMBOL(disable_irq_nosync);
...@@ -294,7 +296,8 @@ void __enable_irq(struct irq_desc *desc, unsigned int irq, bool resume) ...@@ -294,7 +296,8 @@ void __enable_irq(struct irq_desc *desc, unsigned int irq, bool resume)
* matches the last disable, processing of interrupts on this * matches the last disable, processing of interrupts on this
* IRQ line is re-enabled. * IRQ line is re-enabled.
* *
* This function may be called from IRQ context. * This function may be called from IRQ context only when
* desc->chip->bus_lock and desc->chip->bus_sync_unlock are NULL !
*/ */
void enable_irq(unsigned int irq) void enable_irq(unsigned int irq)
{ {
...@@ -304,9 +307,11 @@ void enable_irq(unsigned int irq) ...@@ -304,9 +307,11 @@ void enable_irq(unsigned int irq)
if (!desc) if (!desc)
return; return;
chip_bus_lock(irq, desc);
spin_lock_irqsave(&desc->lock, flags); spin_lock_irqsave(&desc->lock, flags);
__enable_irq(desc, irq, false); __enable_irq(desc, irq, false);
spin_unlock_irqrestore(&desc->lock, flags); spin_unlock_irqrestore(&desc->lock, flags);
chip_bus_sync_unlock(irq, desc);
} }
EXPORT_SYMBOL(enable_irq); EXPORT_SYMBOL(enable_irq);
...@@ -436,6 +441,26 @@ int __irq_set_trigger(struct irq_desc *desc, unsigned int irq, ...@@ -436,6 +441,26 @@ int __irq_set_trigger(struct irq_desc *desc, unsigned int irq,
return ret; return ret;
} }
/*
* Default primary interrupt handler for threaded interrupts. Is
* assigned as primary handler when request_threaded_irq is called
* with handler == NULL. Useful for oneshot interrupts.
*/
static irqreturn_t irq_default_primary_handler(int irq, void *dev_id)
{
return IRQ_WAKE_THREAD;
}
/*
* Primary handler for nested threaded interrupts. Should never be
* called.
*/
static irqreturn_t irq_nested_primary_handler(int irq, void *dev_id)
{
WARN(1, "Primary handler called for nested irq %d\n", irq);
return IRQ_NONE;
}
static int irq_wait_for_interrupt(struct irqaction *action) static int irq_wait_for_interrupt(struct irqaction *action)
{ {
while (!kthread_should_stop()) { while (!kthread_should_stop()) {
...@@ -451,6 +476,23 @@ static int irq_wait_for_interrupt(struct irqaction *action) ...@@ -451,6 +476,23 @@ static int irq_wait_for_interrupt(struct irqaction *action)
return -1; return -1;
} }
/*
* Oneshot interrupts keep the irq line masked until the threaded
* handler finished. unmask if the interrupt has not been disabled and
* is marked MASKED.
*/
static void irq_finalize_oneshot(unsigned int irq, struct irq_desc *desc)
{
chip_bus_lock(irq, desc);
spin_lock_irq(&desc->lock);
if (!(desc->status & IRQ_DISABLED) && (desc->status & IRQ_MASKED)) {
desc->status &= ~IRQ_MASKED;
desc->chip->unmask(irq);
}
spin_unlock_irq(&desc->lock);
chip_bus_sync_unlock(irq, desc);
}
#ifdef CONFIG_SMP #ifdef CONFIG_SMP
/* /*
* Check whether we need to change the affinity of the interrupt thread. * Check whether we need to change the affinity of the interrupt thread.
...@@ -492,7 +534,7 @@ static int irq_thread(void *data) ...@@ -492,7 +534,7 @@ static int irq_thread(void *data)
struct sched_param param = { .sched_priority = MAX_USER_RT_PRIO/2, }; struct sched_param param = { .sched_priority = MAX_USER_RT_PRIO/2, };
struct irqaction *action = data; struct irqaction *action = data;
struct irq_desc *desc = irq_to_desc(action->irq); struct irq_desc *desc = irq_to_desc(action->irq);
int wake; int wake, oneshot = desc->status & IRQ_ONESHOT;
sched_setscheduler(current, SCHED_FIFO, &param); sched_setscheduler(current, SCHED_FIFO, &param);
current->irqaction = action; current->irqaction = action;
...@@ -518,6 +560,9 @@ static int irq_thread(void *data) ...@@ -518,6 +560,9 @@ static int irq_thread(void *data)
spin_unlock_irq(&desc->lock); spin_unlock_irq(&desc->lock);
action->thread_fn(action->irq, action->dev_id); action->thread_fn(action->irq, action->dev_id);
if (oneshot)
irq_finalize_oneshot(action->irq, desc);
} }
wake = atomic_dec_and_test(&desc->threads_active); wake = atomic_dec_and_test(&desc->threads_active);
...@@ -565,7 +610,7 @@ __setup_irq(unsigned int irq, struct irq_desc *desc, struct irqaction *new) ...@@ -565,7 +610,7 @@ __setup_irq(unsigned int irq, struct irq_desc *desc, struct irqaction *new)
struct irqaction *old, **old_ptr; struct irqaction *old, **old_ptr;
const char *old_name = NULL; const char *old_name = NULL;
unsigned long flags; unsigned long flags;
int shared = 0; int nested, shared = 0;
int ret; int ret;
if (!desc) if (!desc)
...@@ -590,10 +635,32 @@ __setup_irq(unsigned int irq, struct irq_desc *desc, struct irqaction *new) ...@@ -590,10 +635,32 @@ __setup_irq(unsigned int irq, struct irq_desc *desc, struct irqaction *new)
rand_initialize_irq(irq); rand_initialize_irq(irq);
} }
/* Oneshot interrupts are not allowed with shared */
if ((new->flags & IRQF_ONESHOT) && (new->flags & IRQF_SHARED))
return -EINVAL;
/*
* Check whether the interrupt nests into another interrupt
* thread.
*/
nested = desc->status & IRQ_NESTED_THREAD;
if (nested) {
if (!new->thread_fn)
return -EINVAL;
/*
* Replace the primary handler which was provided from
* the driver for non nested interrupt handling by the
* dummy function which warns when called.
*/
new->handler = irq_nested_primary_handler;
}
/* /*
* Threaded handler ? * Create a handler thread when a thread function is supplied
* and the interrupt does not nest into another interrupt
* thread.
*/ */
if (new->thread_fn) { if (new->thread_fn && !nested) {
struct task_struct *t; struct task_struct *t;
t = kthread_create(irq_thread, new, "irq/%d-%s", irq, t = kthread_create(irq_thread, new, "irq/%d-%s", irq,
...@@ -662,9 +729,12 @@ __setup_irq(unsigned int irq, struct irq_desc *desc, struct irqaction *new) ...@@ -662,9 +729,12 @@ __setup_irq(unsigned int irq, struct irq_desc *desc, struct irqaction *new)
desc->status |= IRQ_PER_CPU; desc->status |= IRQ_PER_CPU;
#endif #endif
desc->status &= ~(IRQ_AUTODETECT | IRQ_WAITING | desc->status &= ~(IRQ_AUTODETECT | IRQ_WAITING | IRQ_ONESHOT |
IRQ_INPROGRESS | IRQ_SPURIOUS_DISABLED); IRQ_INPROGRESS | IRQ_SPURIOUS_DISABLED);
if (new->flags & IRQF_ONESHOT)
desc->status |= IRQ_ONESHOT;
if (!(desc->status & IRQ_NOAUTOEN)) { if (!(desc->status & IRQ_NOAUTOEN)) {
desc->depth = 0; desc->depth = 0;
desc->status &= ~IRQ_DISABLED; desc->status &= ~IRQ_DISABLED;
...@@ -875,7 +945,14 @@ EXPORT_SYMBOL_GPL(remove_irq); ...@@ -875,7 +945,14 @@ EXPORT_SYMBOL_GPL(remove_irq);
*/ */
void free_irq(unsigned int irq, void *dev_id) void free_irq(unsigned int irq, void *dev_id)
{ {
struct irq_desc *desc = irq_to_desc(irq);
if (!desc)
return;
chip_bus_lock(irq, desc);
kfree(__free_irq(irq, dev_id)); kfree(__free_irq(irq, dev_id));
chip_bus_sync_unlock(irq, desc);
} }
EXPORT_SYMBOL(free_irq); EXPORT_SYMBOL(free_irq);
...@@ -884,6 +961,8 @@ EXPORT_SYMBOL(free_irq); ...@@ -884,6 +961,8 @@ EXPORT_SYMBOL(free_irq);
* @irq: Interrupt line to allocate * @irq: Interrupt line to allocate
* @handler: Function to be called when the IRQ occurs. * @handler: Function to be called when the IRQ occurs.
* Primary handler for threaded interrupts * Primary handler for threaded interrupts
* If NULL and thread_fn != NULL the default
* primary handler is installed
* @thread_fn: Function called from the irq handler thread * @thread_fn: Function called from the irq handler thread
* If NULL, no irq thread is created * If NULL, no irq thread is created
* @irqflags: Interrupt type flags * @irqflags: Interrupt type flags
...@@ -963,8 +1042,12 @@ int request_threaded_irq(unsigned int irq, irq_handler_t handler, ...@@ -963,8 +1042,12 @@ int request_threaded_irq(unsigned int irq, irq_handler_t handler,
if (desc->status & IRQ_NOREQUEST) if (desc->status & IRQ_NOREQUEST)
return -EINVAL; return -EINVAL;
if (!handler)
return -EINVAL; if (!handler) {
if (!thread_fn)
return -EINVAL;
handler = irq_default_primary_handler;
}
action = kzalloc(sizeof(struct irqaction), GFP_KERNEL); action = kzalloc(sizeof(struct irqaction), GFP_KERNEL);
if (!action) if (!action)
...@@ -976,7 +1059,10 @@ int request_threaded_irq(unsigned int irq, irq_handler_t handler, ...@@ -976,7 +1059,10 @@ int request_threaded_irq(unsigned int irq, irq_handler_t handler,
action->name = devname; action->name = devname;
action->dev_id = dev_id; action->dev_id = dev_id;
chip_bus_lock(irq, desc);
retval = __setup_irq(irq, desc, action); retval = __setup_irq(irq, desc, action);
chip_bus_sync_unlock(irq, desc);
if (retval) if (retval)
kfree(action); kfree(action);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment