Commit 4f8413a3 authored by Marc Zyngier's avatar Marc Zyngier

genirq: Track whether the trigger type has been set

When requesting a shared interrupt, we assume that the firmware
support code (DT or ACPI) has called irqd_set_trigger_type
already, so that we can retrieve it and check that the requester
is being reasonnable.

Unfortunately, we still have non-DT, non-ACPI systems around,
and these guys won't call irqd_set_trigger_type before requesting
the interrupt. The consequence is that we fail the request that
would have worked before.

We can either chase all these use cases (boring), or address it
in core code (easier). Let's have a per-irq_desc flag that
indicates whether irqd_set_trigger_type has been called, and
let's just check it when checking for a shared interrupt.
If it hasn't been set, just take whatever the interrupt
requester asks.

Fixes: 382bd4de ("genirq: Use irqd_get_trigger_type to compare the trigger type for shared IRQs")
Cc: stable@vger.kernel.org
Reported-and-tested-by: default avatarPetr Cvek <petrcvekcz@gmail.com>
Signed-off-by: default avatarMarc Zyngier <marc.zyngier@arm.com>
parent 666740fd
...@@ -210,6 +210,7 @@ struct irq_data { ...@@ -210,6 +210,7 @@ struct irq_data {
* IRQD_MANAGED_SHUTDOWN - Interrupt was shutdown due to empty affinity * IRQD_MANAGED_SHUTDOWN - Interrupt was shutdown due to empty affinity
* mask. Applies only to affinity managed irqs. * mask. Applies only to affinity managed irqs.
* IRQD_SINGLE_TARGET - IRQ allows only a single affinity target * IRQD_SINGLE_TARGET - IRQ allows only a single affinity target
* IRQD_DEFAULT_TRIGGER_SET - Expected trigger already been set
*/ */
enum { enum {
IRQD_TRIGGER_MASK = 0xf, IRQD_TRIGGER_MASK = 0xf,
...@@ -230,6 +231,7 @@ enum { ...@@ -230,6 +231,7 @@ enum {
IRQD_IRQ_STARTED = (1 << 22), IRQD_IRQ_STARTED = (1 << 22),
IRQD_MANAGED_SHUTDOWN = (1 << 23), IRQD_MANAGED_SHUTDOWN = (1 << 23),
IRQD_SINGLE_TARGET = (1 << 24), IRQD_SINGLE_TARGET = (1 << 24),
IRQD_DEFAULT_TRIGGER_SET = (1 << 25),
}; };
#define __irqd_to_state(d) ACCESS_PRIVATE((d)->common, state_use_accessors) #define __irqd_to_state(d) ACCESS_PRIVATE((d)->common, state_use_accessors)
...@@ -259,18 +261,25 @@ static inline void irqd_mark_affinity_was_set(struct irq_data *d) ...@@ -259,18 +261,25 @@ static inline void irqd_mark_affinity_was_set(struct irq_data *d)
__irqd_to_state(d) |= IRQD_AFFINITY_SET; __irqd_to_state(d) |= IRQD_AFFINITY_SET;
} }
static inline bool irqd_trigger_type_was_set(struct irq_data *d)
{
return __irqd_to_state(d) & IRQD_DEFAULT_TRIGGER_SET;
}
static inline u32 irqd_get_trigger_type(struct irq_data *d) static inline u32 irqd_get_trigger_type(struct irq_data *d)
{ {
return __irqd_to_state(d) & IRQD_TRIGGER_MASK; return __irqd_to_state(d) & IRQD_TRIGGER_MASK;
} }
/* /*
* Must only be called inside irq_chip.irq_set_type() functions. * Must only be called inside irq_chip.irq_set_type() functions or
* from the DT/ACPI setup code.
*/ */
static inline void irqd_set_trigger_type(struct irq_data *d, u32 type) static inline void irqd_set_trigger_type(struct irq_data *d, u32 type)
{ {
__irqd_to_state(d) &= ~IRQD_TRIGGER_MASK; __irqd_to_state(d) &= ~IRQD_TRIGGER_MASK;
__irqd_to_state(d) |= type & IRQD_TRIGGER_MASK; __irqd_to_state(d) |= type & IRQD_TRIGGER_MASK;
__irqd_to_state(d) |= IRQD_DEFAULT_TRIGGER_SET;
} }
static inline bool irqd_is_level_type(struct irq_data *d) static inline bool irqd_is_level_type(struct irq_data *d)
......
...@@ -1228,7 +1228,18 @@ __setup_irq(unsigned int irq, struct irq_desc *desc, struct irqaction *new) ...@@ -1228,7 +1228,18 @@ __setup_irq(unsigned int irq, struct irq_desc *desc, struct irqaction *new)
* set the trigger type must match. Also all must * set the trigger type must match. Also all must
* agree on ONESHOT. * agree on ONESHOT.
*/ */
unsigned int oldtype = irqd_get_trigger_type(&desc->irq_data); unsigned int oldtype;
/*
* If nobody did set the configuration before, inherit
* the one provided by the requester.
*/
if (irqd_trigger_type_was_set(&desc->irq_data)) {
oldtype = irqd_get_trigger_type(&desc->irq_data);
} else {
oldtype = new->flags & IRQF_TRIGGER_MASK;
irqd_set_trigger_type(&desc->irq_data, oldtype);
}
if (!((old->flags & new->flags) & IRQF_SHARED) || if (!((old->flags & new->flags) & IRQF_SHARED) ||
(oldtype != (new->flags & IRQF_TRIGGER_MASK)) || (oldtype != (new->flags & IRQF_TRIGGER_MASK)) ||
......
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