Skip to content

Commit

Permalink
genirq: Mirror irq trigger type bits in irq_data.state
Browse files Browse the repository at this point in the history
That's the data structure chip functions get provided. Also allow them
to signal the core code that they updated the flags in irq_data.state
by returning IRQ_SET_MASK_OK_NOCOPY. The default is unchanged.

The type bits should be accessed via:

val = irqd_get_trigger_type(irqdata);
and
irqd_set_trigger_type(irqdata, val);

Coders who access them directly will be tracked down and slapped with
stinking trouts.

Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
  • Loading branch information
Thomas Gleixner committed Feb 19, 2011
1 parent 2bdd105 commit 876dbd4
Show file tree
Hide file tree
Showing 5 changed files with 87 additions and 20 deletions.
26 changes: 25 additions & 1 deletion include/linux/irq.h
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,9 @@ typedef void (*irq_flow_handler_t)(unsigned int irq,
#define IRQ_TYPE_EDGE_BOTH (IRQ_TYPE_EDGE_FALLING | IRQ_TYPE_EDGE_RISING)
#define IRQ_TYPE_LEVEL_HIGH 0x00000004 /* Level high type */
#define IRQ_TYPE_LEVEL_LOW 0x00000008 /* Level low type */
#define IRQ_TYPE_LEVEL_MASK (IRQ_TYPE_LEVEL_LOW | IRQ_TYPE_LEVEL_HIGH)
#define IRQ_TYPE_SENSE_MASK 0x0000000f /* Mask of the above */

#define IRQ_TYPE_PROBE 0x00000010 /* Probing in progress */

/* Internal flags */
Expand Down Expand Up @@ -131,17 +133,20 @@ struct irq_data {
/*
* Bit masks for irq_data.state
*
* IRQD_TRIGGER_MASK - Mask for the trigger type bits
* IRQD_SETAFFINITY_PENDING - Affinity setting is pending
* IRQD_NO_BALANCING - Balancing disabled for this IRQ
* IRQD_PER_CPU - Interrupt is per cpu
* IRQD_AFFINITY_SET - Interrupt affinity was set
* IRQD_LEVEL - Interrupt is level triggered
*/
enum {
/* Bit 0 - 7 reserved for TYPE will use later */
IRQD_TRIGGER_MASK = 0xf,
IRQD_SETAFFINITY_PENDING = (1 << 8),
IRQD_NO_BALANCING = (1 << 10),
IRQD_PER_CPU = (1 << 11),
IRQD_AFFINITY_SET = (1 << 12),
IRQD_LEVEL = (1 << 13),
};

static inline bool irqd_is_setaffinity_pending(struct irq_data *d)
Expand All @@ -164,6 +169,25 @@ static inline bool irqd_affinity_was_set(struct irq_data *d)
return d->state_use_accessors & IRQD_AFFINITY_SET;
}

static inline u32 irqd_get_trigger_type(struct irq_data *d)
{
return d->state_use_accessors & IRQD_TRIGGER_MASK;
}

/*
* Must only be called inside irq_chip.irq_set_type() functions.
*/
static inline void irqd_set_trigger_type(struct irq_data *d, u32 type)
{
d->state_use_accessors &= ~IRQD_TRIGGER_MASK;
d->state_use_accessors |= type & IRQD_TRIGGER_MASK;
}

static inline bool irqd_is_level_type(struct irq_data *d)
{
return d->state_use_accessors & IRQD_LEVEL;
}

/**
* struct irq_chip - hardware interrupt chip descriptor
*
Expand Down
5 changes: 4 additions & 1 deletion kernel/irq/chip.c
Original file line number Diff line number Diff line change
Expand Up @@ -710,11 +710,14 @@ void irq_modify_status(unsigned int irq, unsigned long clr, unsigned long set)

irq_settings_clr_and_set(desc, clr, set);

irqd_clear(&desc->irq_data, IRQD_NO_BALANCING | IRQD_PER_CPU);
irqd_clear(&desc->irq_data, IRQD_NO_BALANCING | IRQD_PER_CPU |
IRQD_TRIGGER_MASK | IRQD_LEVEL);
if (irq_settings_has_no_balance_set(desc))
irqd_set(&desc->irq_data, IRQD_NO_BALANCING);
if (irq_settings_is_per_cpu(desc))
irqd_set(&desc->irq_data, IRQD_PER_CPU);

irqd_set(&desc->irq_data, irq_settings_get_trigger_mask(desc));

raw_spin_unlock_irqrestore(&desc->lock, flags);
}
44 changes: 27 additions & 17 deletions kernel/irq/manage.c
Original file line number Diff line number Diff line change
Expand Up @@ -567,23 +567,32 @@ int __irq_set_trigger(struct irq_desc *desc, unsigned int irq,
return 0;
}

flags &= IRQ_TYPE_SENSE_MASK;
/* caller masked out all except trigger mode flags */
ret = chip->irq_set_type(&desc->irq_data, flags);

if (ret)
pr_err("setting trigger mode %lu for irq %u failed (%pF)\n",
flags, irq, chip->irq_set_type);
else {
if (flags & (IRQ_TYPE_LEVEL_LOW | IRQ_TYPE_LEVEL_HIGH))
flags |= IRQ_LEVEL;
/* note that IRQF_TRIGGER_MASK == IRQ_TYPE_SENSE_MASK */
desc->status &= ~(IRQ_LEVEL | IRQ_TYPE_SENSE_MASK);
desc->status |= flags;
switch (ret) {
case IRQ_SET_MASK_OK:
irqd_clear(&desc->irq_data, IRQD_TRIGGER_MASK);
irqd_set(&desc->irq_data, flags);

case IRQ_SET_MASK_OK_NOCOPY:
flags = irqd_get_trigger_type(&desc->irq_data);
irq_settings_set_trigger_mask(desc, flags);
irqd_clear(&desc->irq_data, IRQD_LEVEL);
irq_settings_clr_level(desc);
if (flags & IRQ_TYPE_LEVEL_MASK) {
irq_settings_set_level(desc);
irqd_set(&desc->irq_data, IRQD_LEVEL);
}

if (chip != desc->irq_data.chip)
irq_chip_set_defaults(desc->irq_data.chip);
return 0;
default:
pr_err("setting trigger mode %lu for irq %u failed (%pF)\n",
flags, irq, chip->irq_set_type);
}

return ret;
}

Expand Down Expand Up @@ -923,13 +932,14 @@ __setup_irq(unsigned int irq, struct irq_desc *desc, struct irqaction *new)
/* Set default affinity mask once everything is setup */
setup_affinity(irq, desc, mask);

} else if ((new->flags & IRQF_TRIGGER_MASK)
&& (new->flags & IRQF_TRIGGER_MASK)
!= (desc->status & IRQ_TYPE_SENSE_MASK)) {
/* hope the handler works with the actual trigger mode... */
pr_warning("IRQ %d uses trigger mode %d; requested %d\n",
irq, (int)(desc->status & IRQ_TYPE_SENSE_MASK),
(int)(new->flags & IRQF_TRIGGER_MASK));
} else if (new->flags & IRQF_TRIGGER_MASK) {
unsigned int nmsk = new->flags & IRQF_TRIGGER_MASK;
unsigned int omsk = irq_settings_get_trigger_mask(desc);

if (nmsk != omsk)
/* hope the handler works with current trigger mode */
pr_warning("IRQ %d uses trigger mode %u; requested %u\n",
irq, nmsk, omsk);
}

new->irq = irq;
Expand Down
2 changes: 1 addition & 1 deletion kernel/irq/resend.c
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ void check_irq_resend(struct irq_desc *desc, unsigned int irq)
* interrupts are resent by hardware when they are still
* active.
*/
if (desc->status & IRQ_LEVEL)
if (irq_settings_is_level(desc))
return;
if (desc->istate & IRQS_REPLAY)
return;
Expand Down
30 changes: 30 additions & 0 deletions kernel/irq/settings.h
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
enum {
_IRQ_DEFAULT_INIT_FLAGS = IRQ_DEFAULT_INIT_FLAGS,
_IRQ_PER_CPU = IRQ_PER_CPU,
_IRQ_LEVEL = IRQ_LEVEL,
_IRQ_NO_BALANCING = IRQ_NO_BALANCING,
_IRQF_MODIFY_MASK = IRQF_MODIFY_MASK,
};
Expand All @@ -31,6 +32,8 @@ enum {
#define IRQ_NO_BALANCING GOT_YOU_MORON
#undef IRQ_AFFINITY_SET
#define IRQ_AFFINITY_SET GOT_YOU_MORON
#undef IRQ_LEVEL
#define IRQ_LEVEL GOT_YOU_MORON
#undef IRQF_MODIFY_MASK
#define IRQF_MODIFY_MASK GOT_YOU_MORON

Expand Down Expand Up @@ -60,3 +63,30 @@ static inline bool irq_settings_has_no_balance_set(struct irq_desc *desc)
{
return desc->status & _IRQ_NO_BALANCING;
}

static inline u32 irq_settings_get_trigger_mask(struct irq_desc *desc)
{
return desc->status & IRQ_TYPE_SENSE_MASK;
}

static inline void
irq_settings_set_trigger_mask(struct irq_desc *desc, u32 mask)
{
desc->status &= ~IRQ_TYPE_SENSE_MASK;
desc->status |= mask & IRQ_TYPE_SENSE_MASK;
}

static inline bool irq_settings_is_level(struct irq_desc *desc)
{
return desc->status & _IRQ_LEVEL;
}

static inline void irq_settings_clr_level(struct irq_desc *desc)
{
desc->status &= ~_IRQ_LEVEL;
}

static inline void irq_settings_set_level(struct irq_desc *desc)
{
desc->status |= _IRQ_LEVEL;
}

0 comments on commit 876dbd4

Please sign in to comment.