diff options
author | Magnus Damm <damm@igel.co.jp> | 2007-07-20 12:09:29 +0900 |
---|---|---|
committer | Paul Mundt <lethal@linux-sh.org> | 2007-07-20 12:18:21 +0900 |
commit | 680c45981ae2b4029878806d76aa17bb62d3c674 (patch) | |
tree | 57d0f10573fb577d1330f15ac6177666fe3cfb25 /arch/sh | |
parent | d0afa579698f33a65bc5c21d3d667dbb46f9e440 (diff) |
sh: intc - improve group support
This patch improves intc group support, ie it makes it possible to
group interrupts together and mask / unmask the entire group. This
also works with priorities, so setting a priority for an entire group
is also possible. This patch is needed to properly support certain
processors such as the 7780.
Fixes for NULL pointers in DECLARE_INTC_DESC() are also included.
Signed-off-by: Magnus Damm <damm@igel.co.jp>
Signed-off-by: Paul Mundt <lethal@linux-sh.org>
Diffstat (limited to 'arch/sh')
-rw-r--r-- | arch/sh/kernel/cpu/irq/intc.c | 93 |
1 files changed, 73 insertions, 20 deletions
diff --git a/arch/sh/kernel/cpu/irq/intc.c b/arch/sh/kernel/cpu/irq/intc.c index 626b4d8d793..9345a7130e9 100644 --- a/arch/sh/kernel/cpu/irq/intc.c +++ b/arch/sh/kernel/cpu/irq/intc.c @@ -244,13 +244,33 @@ static unsigned int __init intc_find_prio_handler(unsigned int width) return REG_FN_ERROR; } +static intc_enum __init intc_grp_id(struct intc_desc *desc, intc_enum enum_id) +{ + struct intc_group *g = desc->groups; + unsigned int i, j; + + for (i = 0; g && enum_id && i < desc->nr_groups; i++) { + g = desc->groups + i; + + for (j = 0; g->enum_ids[j]; j++) { + if (g->enum_ids[j] != enum_id) + continue; + + return g->enum_id; + } + } + + return 0; +} + static unsigned int __init intc_prio_value(struct intc_desc *desc, - intc_enum enum_id) + intc_enum enum_id, int do_grps) { + struct intc_prio *p = desc->priorities; unsigned int i; - for (i = 0; i < desc->nr_priorities; i++) { - struct intc_prio *p = desc->priorities + i; + for (i = 0; p && enum_id && i < desc->nr_priorities; i++) { + p = desc->priorities + i; if (p->enum_id != enum_id) continue; @@ -258,16 +278,24 @@ static unsigned int __init intc_prio_value(struct intc_desc *desc, return p->priority; } - return 1; /* default to the lowest priority if no priority is set */ + if (do_grps) + return intc_prio_value(desc, intc_grp_id(desc, enum_id), 0); + + /* default to the lowest priority possible if no priority is set + * - this needs to be at least 2 for 5-bit priorities on 7780 + */ + + return 2; } static unsigned int __init intc_mask_data(struct intc_desc *desc, - intc_enum enum_id) + intc_enum enum_id, int do_grps) { + struct intc_mask_reg *mr = desc->mask_regs; unsigned int i, j, fn; - for (i = 0; i < desc->nr_mask_regs; i++) { - struct intc_mask_reg *mr = desc->mask_regs + i; + for (i = 0; mr && enum_id && i < desc->nr_mask_regs; i++) { + mr = desc->mask_regs + i; for (j = 0; j < ARRAY_SIZE(mr->enum_ids); j++) { if (mr->enum_ids[j] != enum_id) @@ -281,16 +309,20 @@ static unsigned int __init intc_mask_data(struct intc_desc *desc, } } + if (do_grps) + return intc_mask_data(desc, intc_grp_id(desc, enum_id), 0); + return 0; } static unsigned int __init intc_prio_data(struct intc_desc *desc, - intc_enum enum_id) + intc_enum enum_id, int do_grps) { + struct intc_prio_reg *pr = desc->prio_regs; unsigned int i, j, fn, bit, prio; - for (i = 0; i < desc->nr_prio_regs; i++) { - struct intc_prio_reg *pr = desc->prio_regs + i; + for (i = 0; pr && enum_id && i < desc->nr_prio_regs; i++) { + pr = desc->prio_regs + i; for (j = 0; j < ARRAY_SIZE(pr->enum_ids); j++) { if (pr->enum_ids[j] != enum_id) @@ -300,7 +332,7 @@ static unsigned int __init intc_prio_data(struct intc_desc *desc, if (fn == REG_FN_ERROR) return 0; - prio = intc_prio_value(desc, enum_id); + prio = intc_prio_value(desc, enum_id, 1); bit = pr->reg_width - ((j + 1) * pr->field_width); BUG_ON(bit < 0); @@ -309,27 +341,48 @@ static unsigned int __init intc_prio_data(struct intc_desc *desc, } } + if (do_grps) + return intc_prio_data(desc, intc_grp_id(desc, enum_id), 0); + return 0; } static void __init intc_register_irq(struct intc_desc *desc, intc_enum enum_id, unsigned int irq) { - unsigned int mask_data = intc_mask_data(desc, enum_id); - unsigned int prio_data = intc_prio_data(desc, enum_id); - unsigned int data = mask_data ? mask_data : prio_data; + unsigned int data[2], primary; + + /* Prefer single interrupt source bitmap over other combinations: + * 1. bitmap, single interrupt source + * 2. priority, single interrupt source + * 3. bitmap, multiple interrupt sources (groups) + * 4. priority, multiple interrupt sources (groups) + */ - BUG_ON(!data); + data[0] = intc_mask_data(desc, enum_id, 0); + data[1] = intc_prio_data(desc, enum_id, 0); + + primary = 0; + if (!data[0] && data[1]) + primary = 1; + + data[0] = data[0] ? data[0] : intc_mask_data(desc, enum_id, 1); + data[1] = data[1] ? data[1] : intc_prio_data(desc, enum_id, 1); + + if (!data[primary]) + primary ^= 1; + + BUG_ON(!data[primary]); /* must have primary masking method */ disable_irq_nosync(irq); set_irq_chip_and_handler_name(irq, &desc->chip, handle_level_irq, "level"); - set_irq_chip_data(irq, (void *)data); - - /* set priority */ + set_irq_chip_data(irq, (void *)data[primary]); - if (prio_data) - intc_reg_fns[_INTC_FN(prio_data)].enable(desc, prio_data); + /* enable secondary masking method if present */ + if (data[!primary]) + intc_reg_fns[_INTC_FN(data[!primary])].enable(desc, + data[!primary]); /* irq should be disabled by default */ desc->chip.mask(irq); |