diff options
Diffstat (limited to 'arch/sh/kernel/cpu/irq/intc.c')
-rw-r--r-- | arch/sh/kernel/cpu/irq/intc.c | 79 |
1 files changed, 76 insertions, 3 deletions
diff --git a/arch/sh/kernel/cpu/irq/intc.c b/arch/sh/kernel/cpu/irq/intc.c index 84806b2027f..df3695406d8 100644 --- a/arch/sh/kernel/cpu/irq/intc.c +++ b/arch/sh/kernel/cpu/irq/intc.c @@ -1,7 +1,7 @@ /* * Shared interrupt handling code for IPR and INTC2 types of IRQs. * - * Copyright (C) 2007 Magnus Damm + * Copyright (C) 2007, 2008 Magnus Damm * * Based on intc2.c and ipr.c * @@ -62,6 +62,9 @@ struct intc_desc_int { #endif static unsigned int intc_prio_level[NR_IRQS]; /* for now */ +#ifdef CONFIG_CPU_SH3 +static unsigned long ack_handle[NR_IRQS]; +#endif static inline struct intc_desc_int *get_intc_desc(unsigned int irq) { @@ -219,6 +222,25 @@ static void intc_disable(unsigned int irq) } } +#ifdef CONFIG_CPU_SH3 +static void intc_mask_ack(unsigned int irq) +{ + struct intc_desc_int *d = get_intc_desc(irq); + unsigned long handle = ack_handle[irq]; + unsigned long addr; + + intc_disable(irq); + + /* read register and write zero only to the assocaited bit */ + + if (handle) { + addr = INTC_REG(d, _INTC_ADDR_D(handle), 0); + ctrl_inb(addr); + ctrl_outb(0x3f ^ set_field(0, 1, handle), addr); + } +} +#endif + static struct intc_handle_int *intc_find_irq(struct intc_handle_int *hp, unsigned int nr_hp, unsigned int irq) @@ -430,6 +452,40 @@ static unsigned int __init intc_prio_data(struct intc_desc *desc, return 0; } +#ifdef CONFIG_CPU_SH3 +static unsigned int __init intc_ack_data(struct intc_desc *desc, + struct intc_desc_int *d, + intc_enum enum_id) +{ + struct intc_mask_reg *mr = desc->ack_regs; + unsigned int i, j, fn, mode; + unsigned long reg_e, reg_d; + + for (i = 0; mr && enum_id && i < desc->nr_ack_regs; i++) { + mr = desc->ack_regs + i; + + for (j = 0; j < ARRAY_SIZE(mr->enum_ids); j++) { + if (mr->enum_ids[j] != enum_id) + continue; + + fn = REG_FN_MODIFY_BASE; + mode = MODE_ENABLE_REG; + reg_e = mr->set_reg; + reg_d = mr->set_reg; + + fn += (mr->reg_width >> 3) - 1; + return _INTC_MK(fn, mode, + intc_get_reg(d, reg_e), + intc_get_reg(d, reg_d), + 1, + (mr->reg_width - 1) - j); + } + } + + return 0; +} +#endif + static unsigned int __init intc_sense_data(struct intc_desc *desc, struct intc_desc_int *d, intc_enum enum_id) @@ -530,6 +586,11 @@ static void __init intc_register_irq(struct intc_desc *desc, /* irq should be disabled by default */ d->chip.mask(irq); + +#ifdef CONFIG_CPU_SH3 + if (desc->ack_regs) + ack_handle[irq] = intc_ack_data(desc, d, enum_id); +#endif } static unsigned int __init save_reg(struct intc_desc_int *d, @@ -560,6 +621,9 @@ void __init register_intc_controller(struct intc_desc *desc) d->nr_reg += desc->prio_regs ? desc->nr_prio_regs * 2 : 0; d->nr_reg += desc->sense_regs ? desc->nr_sense_regs : 0; +#ifdef CONFIG_CPU_SH3 + d->nr_reg += desc->ack_regs ? desc->nr_ack_regs : 0; +#endif d->reg = alloc_bootmem(d->nr_reg * sizeof(*d->reg)); #ifdef CONFIG_SMP d->smp = alloc_bootmem(d->nr_reg * sizeof(*d->smp)); @@ -592,14 +656,23 @@ void __init register_intc_controller(struct intc_desc *desc) } } - BUG_ON(k > 256); /* _INTC_ADDR_E() and _INTC_ADDR_D() are 8 bits */ - d->chip.name = desc->name; d->chip.mask = intc_disable; d->chip.unmask = intc_enable; d->chip.mask_ack = intc_disable; d->chip.set_type = intc_set_sense; +#ifdef CONFIG_CPU_SH3 + if (desc->ack_regs) { + for (i = 0; i < desc->nr_ack_regs; i++) + k += save_reg(d, k, desc->ack_regs[i].set_reg, 0); + + d->chip.mask_ack = intc_mask_ack; + } +#endif + + BUG_ON(k > 256); /* _INTC_ADDR_E() and _INTC_ADDR_D() are 8 bits */ + for (i = 0; i < desc->nr_vectors; i++) { struct intc_vect *vect = desc->vectors + i; |