123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307 |
- /*
- * Shared interrupt handling code for IPR and INTC2 types of IRQs.
- *
- * Copyright (C) 2007, 2008 Magnus Damm
- * Copyright (C) 2009, 2010 Paul Mundt
- *
- * This file is subject to the terms and conditions of the GNU General Public
- * License. See the file "COPYING" in the main directory of this archive
- * for more details.
- */
- #include <linux/init.h>
- #include <linux/irq.h>
- #include <linux/spinlock.h>
- #include "internals.h"
- static unsigned long ack_handle[INTC_NR_IRQS];
- static intc_enum __init intc_grp_id(struct intc_desc *desc,
- intc_enum enum_id)
- {
- struct intc_group *g = desc->hw.groups;
- unsigned int i, j;
- for (i = 0; g && enum_id && i < desc->hw.nr_groups; i++) {
- g = desc->hw.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_mask_data(struct intc_desc *desc,
- struct intc_desc_int *d,
- intc_enum enum_id,
- unsigned int *reg_idx,
- unsigned int *fld_idx)
- {
- struct intc_mask_reg *mr = desc->hw.mask_regs;
- unsigned int fn, mode;
- unsigned long reg_e, reg_d;
- while (mr && enum_id && *reg_idx < desc->hw.nr_mask_regs) {
- mr = desc->hw.mask_regs + *reg_idx;
- for (; *fld_idx < ARRAY_SIZE(mr->enum_ids); (*fld_idx)++) {
- if (mr->enum_ids[*fld_idx] != enum_id)
- continue;
- if (mr->set_reg && mr->clr_reg) {
- fn = REG_FN_WRITE_BASE;
- mode = MODE_DUAL_REG;
- reg_e = mr->clr_reg;
- reg_d = mr->set_reg;
- } else {
- fn = REG_FN_MODIFY_BASE;
- if (mr->set_reg) {
- mode = MODE_ENABLE_REG;
- reg_e = mr->set_reg;
- reg_d = mr->set_reg;
- } else {
- mode = MODE_MASK_REG;
- reg_e = mr->clr_reg;
- reg_d = mr->clr_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) - *fld_idx);
- }
- *fld_idx = 0;
- (*reg_idx)++;
- }
- return 0;
- }
- unsigned int __init
- intc_get_mask_handle(struct intc_desc *desc, struct intc_desc_int *d,
- intc_enum enum_id, int do_grps)
- {
- unsigned int i = 0;
- unsigned int j = 0;
- unsigned int ret;
- ret = _intc_mask_data(desc, d, enum_id, &i, &j);
- if (ret)
- return ret;
- if (do_grps)
- return intc_get_mask_handle(desc, d, intc_grp_id(desc, enum_id), 0);
- return 0;
- }
- static unsigned int __init _intc_prio_data(struct intc_desc *desc,
- struct intc_desc_int *d,
- intc_enum enum_id,
- unsigned int *reg_idx,
- unsigned int *fld_idx)
- {
- struct intc_prio_reg *pr = desc->hw.prio_regs;
- unsigned int fn, n, mode, bit;
- unsigned long reg_e, reg_d;
- while (pr && enum_id && *reg_idx < desc->hw.nr_prio_regs) {
- pr = desc->hw.prio_regs + *reg_idx;
- for (; *fld_idx < ARRAY_SIZE(pr->enum_ids); (*fld_idx)++) {
- if (pr->enum_ids[*fld_idx] != enum_id)
- continue;
- if (pr->set_reg && pr->clr_reg) {
- fn = REG_FN_WRITE_BASE;
- mode = MODE_PCLR_REG;
- reg_e = pr->set_reg;
- reg_d = pr->clr_reg;
- } else {
- fn = REG_FN_MODIFY_BASE;
- mode = MODE_PRIO_REG;
- if (!pr->set_reg)
- BUG();
- reg_e = pr->set_reg;
- reg_d = pr->set_reg;
- }
- fn += (pr->reg_width >> 3) - 1;
- n = *fld_idx + 1;
- BUG_ON(n * pr->field_width > pr->reg_width);
- bit = pr->reg_width - (n * pr->field_width);
- return _INTC_MK(fn, mode,
- intc_get_reg(d, reg_e),
- intc_get_reg(d, reg_d),
- pr->field_width, bit);
- }
- *fld_idx = 0;
- (*reg_idx)++;
- }
- return 0;
- }
- unsigned int __init
- intc_get_prio_handle(struct intc_desc *desc, struct intc_desc_int *d,
- intc_enum enum_id, int do_grps)
- {
- unsigned int i = 0;
- unsigned int j = 0;
- unsigned int ret;
- ret = _intc_prio_data(desc, d, enum_id, &i, &j);
- if (ret)
- return ret;
- if (do_grps)
- return intc_get_prio_handle(desc, d, intc_grp_id(desc, enum_id), 0);
- return 0;
- }
- static unsigned int intc_ack_data(struct intc_desc *desc,
- struct intc_desc_int *d, intc_enum enum_id)
- {
- struct intc_mask_reg *mr = desc->hw.ack_regs;
- unsigned int i, j, fn, mode;
- unsigned long reg_e, reg_d;
- for (i = 0; mr && enum_id && i < desc->hw.nr_ack_regs; i++) {
- mr = desc->hw.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;
- }
- static void intc_enable_disable(struct intc_desc_int *d,
- unsigned long handle, int do_enable)
- {
- unsigned long addr;
- unsigned int cpu;
- unsigned long (*fn)(unsigned long, unsigned long,
- unsigned long (*)(unsigned long, unsigned long,
- unsigned long),
- unsigned int);
- if (do_enable) {
- for (cpu = 0; cpu < SMP_NR(d, _INTC_ADDR_E(handle)); cpu++) {
- addr = INTC_REG(d, _INTC_ADDR_E(handle), cpu);
- fn = intc_enable_noprio_fns[_INTC_MODE(handle)];
- fn(addr, handle, intc_reg_fns[_INTC_FN(handle)], 0);
- }
- } else {
- for (cpu = 0; cpu < SMP_NR(d, _INTC_ADDR_D(handle)); cpu++) {
- addr = INTC_REG(d, _INTC_ADDR_D(handle), cpu);
- fn = intc_disable_fns[_INTC_MODE(handle)];
- fn(addr, handle, intc_reg_fns[_INTC_FN(handle)], 0);
- }
- }
- }
- void __init intc_enable_disable_enum(struct intc_desc *desc,
- struct intc_desc_int *d,
- intc_enum enum_id, int enable)
- {
- unsigned int i, j, data;
- /* go through and enable/disable all mask bits */
- i = j = 0;
- do {
- data = _intc_mask_data(desc, d, enum_id, &i, &j);
- if (data)
- intc_enable_disable(d, data, enable);
- j++;
- } while (data);
- /* go through and enable/disable all priority fields */
- i = j = 0;
- do {
- data = _intc_prio_data(desc, d, enum_id, &i, &j);
- if (data)
- intc_enable_disable(d, data, enable);
- j++;
- } while (data);
- }
- unsigned int __init
- intc_get_sense_handle(struct intc_desc *desc, struct intc_desc_int *d,
- intc_enum enum_id)
- {
- struct intc_sense_reg *sr = desc->hw.sense_regs;
- unsigned int i, j, fn, bit;
- for (i = 0; sr && enum_id && i < desc->hw.nr_sense_regs; i++) {
- sr = desc->hw.sense_regs + i;
- for (j = 0; j < ARRAY_SIZE(sr->enum_ids); j++) {
- if (sr->enum_ids[j] != enum_id)
- continue;
- fn = REG_FN_MODIFY_BASE;
- fn += (sr->reg_width >> 3) - 1;
- BUG_ON((j + 1) * sr->field_width > sr->reg_width);
- bit = sr->reg_width - ((j + 1) * sr->field_width);
- return _INTC_MK(fn, 0, intc_get_reg(d, sr->reg),
- 0, sr->field_width, bit);
- }
- }
- return 0;
- }
- void intc_set_ack_handle(unsigned int irq, struct intc_desc *desc,
- struct intc_desc_int *d, intc_enum id)
- {
- unsigned long flags;
- /*
- * Nothing to do for this IRQ.
- */
- if (!desc->hw.ack_regs)
- return;
- raw_spin_lock_irqsave(&intc_big_lock, flags);
- ack_handle[irq] = intc_ack_data(desc, d, id);
- raw_spin_unlock_irqrestore(&intc_big_lock, flags);
- }
- unsigned long intc_get_ack_handle(unsigned int irq)
- {
- return ack_handle[irq];
- }
|