in irq-pic32-evic.c [211:317]
static int __init pic32_of_init(struct device_node *node,
struct device_node *parent)
{
struct irq_chip_generic *gc;
struct evic_chip_data *priv;
unsigned int clr = IRQ_NOREQUEST | IRQ_NOPROBE | IRQ_NOAUTOEN;
int nchips, ret;
int i;
nchips = DIV_ROUND_UP(NR_IRQS, 32);
evic_base = of_iomap(node, 0);
if (!evic_base)
return -ENOMEM;
priv = kcalloc(nchips, sizeof(*priv), GFP_KERNEL);
if (!priv) {
ret = -ENOMEM;
goto err_iounmap;
}
evic_irq_domain = irq_domain_add_linear(node, nchips * 32,
&pic32_irq_domain_ops,
priv);
if (!evic_irq_domain) {
ret = -ENOMEM;
goto err_free_priv;
}
/*
* The PIC32 EVIC has a linear list of irqs and the type of each
* irq is determined by the hardware peripheral the EVIC is arbitrating.
* These irq types are defined in the datasheet as "persistent" and
* "non-persistent" which are mapped here to level and edge
* respectively. To manage the different flow handler requirements of
* each irq type, different chip_types are used.
*/
ret = irq_alloc_domain_generic_chips(evic_irq_domain, 32, 2,
"evic-level", handle_level_irq,
clr, 0, 0);
if (ret)
goto err_domain_remove;
board_bind_eic_interrupt = &pic32_bind_evic_interrupt;
for (i = 0; i < nchips; i++) {
u32 ifsclr = PIC32_CLR(REG_IFS_OFFSET + (i * 0x10));
u32 iec = REG_IEC_OFFSET + (i * 0x10);
gc = irq_get_domain_generic_chip(evic_irq_domain, i * 32);
gc->reg_base = evic_base;
gc->unused = 0;
/*
* Level/persistent interrupts have a special requirement that
* the condition generating the interrupt be cleared before the
* interrupt flag (ifs) can be cleared. chip.irq_eoi is used to
* complete the interrupt with an ack.
*/
gc->chip_types[0].type = IRQ_TYPE_LEVEL_MASK;
gc->chip_types[0].handler = handle_fasteoi_irq;
gc->chip_types[0].regs.ack = ifsclr;
gc->chip_types[0].regs.mask = iec;
gc->chip_types[0].chip.name = "evic-level";
gc->chip_types[0].chip.irq_eoi = irq_gc_ack_set_bit;
gc->chip_types[0].chip.irq_mask = irq_gc_mask_clr_bit;
gc->chip_types[0].chip.irq_unmask = irq_gc_mask_set_bit;
gc->chip_types[0].chip.flags = IRQCHIP_SKIP_SET_WAKE;
/* Edge interrupts */
gc->chip_types[1].type = IRQ_TYPE_EDGE_BOTH;
gc->chip_types[1].handler = handle_edge_irq;
gc->chip_types[1].regs.ack = ifsclr;
gc->chip_types[1].regs.mask = iec;
gc->chip_types[1].chip.name = "evic-edge";
gc->chip_types[1].chip.irq_ack = irq_gc_ack_set_bit;
gc->chip_types[1].chip.irq_mask = irq_gc_mask_clr_bit;
gc->chip_types[1].chip.irq_unmask = irq_gc_mask_set_bit;
gc->chip_types[1].chip.irq_set_type = pic32_set_type_edge;
gc->chip_types[1].chip.flags = IRQCHIP_SKIP_SET_WAKE;
gc->private = &priv[i];
}
irq_set_default_host(evic_irq_domain);
/*
* External interrupts have software configurable edge polarity. These
* interrupts are defined in DT allowing polarity to be configured only
* for these interrupts when requested.
*/
pic32_ext_irq_of_init(evic_irq_domain);
return 0;
err_domain_remove:
irq_domain_remove(evic_irq_domain);
err_free_priv:
kfree(priv);
err_iounmap:
iounmap(evic_base);
return ret;
}