summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMagnus Damm <damm@igel.co.jp>2007-08-17 00:50:44 +0900
committerPaul Mundt <lethal@linux-sh.org>2007-09-21 11:57:50 +0900
commit3d37d94e5aab669f5a492bb3cda67bbbbbca50b8 (patch)
tree7066d6e40e2da4b9fdc9b169909ae65d88068df1
parent5c37e025352b993d8726b0207ff2270b2f2bc7d6 (diff)
sh: intc - primary priority masking fixes
This patch contains various intc fixes for problems reported by Markus Brunner on the linuxsh-dev mailing list: http://marc.info/?l=linuxsh-dev&m=118701948224991&w=1 Apart from added comments, the fixes are: - add intc_set_priority() function prototype to hw_irq.h - fix off-by-one error in intc_set_priority() - make sure _INTC_WIDTH() is set for primary priority masking Big thanks to Markus for finding these problems. Version two fixes a compile error and an inverted primary check. Signed-off-by: Magnus Damm <damm@igel.co.jp> Acked-by: Markus Brunner <super.firetwister@gmail.com> Signed-off-by: Paul Mundt <lethal@linux-sh.org>
-rw-r--r--arch/sh/kernel/cpu/irq/intc.c33
-rw-r--r--include/asm-sh/hw_irq.h1
2 files changed, 29 insertions, 5 deletions
diff --git a/arch/sh/kernel/cpu/irq/intc.c b/arch/sh/kernel/cpu/irq/intc.c
index d609a8ccd456..481871fee1a8 100644
--- a/arch/sh/kernel/cpu/irq/intc.c
+++ b/arch/sh/kernel/cpu/irq/intc.c
@@ -206,6 +206,18 @@ static struct intc_handle_int *intc_find_irq(struct intc_handle_int *hp,
{
int i;
+ /* this doesn't scale well, but...
+ *
+ * this function should only be used for cerain uncommon
+ * operations such as intc_set_priority() and intc_set_sense()
+ * and in those rare cases performance doesn't matter that much.
+ * keeping the memory footprint low is more important.
+ *
+ * one rather simple way to speed this up and still keep the
+ * memory footprint down is to make sure the array is sorted
+ * and then perform a bisect to lookup the irq.
+ */
+
for (i = 0; i < nr_hp; i++) {
if ((hp + i)->irq != irq)
continue;
@@ -226,7 +238,7 @@ int intc_set_priority(unsigned int irq, unsigned int prio)
ihp = intc_find_irq(d->prio, d->nr_prio, irq);
if (ihp) {
- if (prio >= ((1 << _INTC_WIDTH(ihp->handle)) - 1))
+ if (prio >= (1 << _INTC_WIDTH(ihp->handle)))
return -EINVAL;
intc_prio_level[irq] = prio;
@@ -237,7 +249,7 @@ int intc_set_priority(unsigned int irq, unsigned int prio)
* priority level will be set during next enable()
*/
- if (ihp->handle)
+ if (_INTC_FN(ihp->handle) != REG_FN_ERR)
_intc_enable(irq, ihp->handle);
}
return 0;
@@ -457,6 +469,7 @@ static void __init intc_register_irq(struct intc_desc *desc,
intc_enum enum_id,
unsigned int irq)
{
+ struct intc_handle_int *hp;
unsigned int data[2], primary;
/* Prefer single interrupt source bitmap over other combinations:
@@ -495,9 +508,19 @@ static void __init intc_register_irq(struct intc_desc *desc,
/* add irq to d->prio list if priority is available */
if (data[1]) {
- (d->prio + d->nr_prio)->irq = irq;
- if (!primary) /* only secondary priority can access regs */
- (d->prio + d->nr_prio)->handle = data[1];
+ hp = d->prio + d->nr_prio;
+ hp->irq = irq;
+ hp->handle = data[1];
+
+ if (primary) {
+ /*
+ * only secondary priority should access registers, so
+ * set _INTC_FN(h) = REG_FN_ERR for intc_set_priority()
+ */
+
+ hp->handle &= ~_INTC_MK(0x0f, 0, 0, 0, 0, 0);
+ hp->handle |= _INTC_MK(REG_FN_ERR, 0, 0, 0, 0, 0);
+ }
d->nr_prio++;
}
diff --git a/include/asm-sh/hw_irq.h b/include/asm-sh/hw_irq.h
index 0c016e949931..f9c90670e147 100644
--- a/include/asm-sh/hw_irq.h
+++ b/include/asm-sh/hw_irq.h
@@ -90,6 +90,7 @@ struct intc_desc symbol __initdata = { \
}
void __init register_intc_controller(struct intc_desc *desc);
+int intc_set_priority(unsigned int irq, unsigned int prio);
void __init plat_irq_setup(void);