Skip to content

Commit

Permalink
sh: intc - improve group support
Browse files Browse the repository at this point in the history
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>
  • Loading branch information
Magnus Damm authored and Paul Mundt committed Jul 20, 2007
1 parent d0afa57 commit 680c459
Showing 1 changed file with 73 additions and 20 deletions.
93 changes: 73 additions & 20 deletions arch/sh/kernel/cpu/irq/intc.c
Original file line number Diff line number Diff line change
Expand Up @@ -244,30 +244,58 @@ 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;

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)
Expand All @@ -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)
Expand All @@ -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);
Expand All @@ -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);
Expand Down

0 comments on commit 680c459

Please sign in to comment.