static int __init pic32_of_init()

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;
}