Asking for guidance in writing IRQ controller driver

Mason slash.tmp at free.fr
Wed May 17 09:54:36 PDT 2017


On 16/05/2017 13:43, Mason wrote:

> It seems clear that I'll need to define an irq_domain.
> 
> I have used irq_domain_create_linear() in the past.
> 
> But I also see other people use irq_domain_create_hierarchy()
> which calls irq_domain_create_linear() but also sets
> domain->parent
> 
> But now I'm left wondering how the code that doesn't set
> domain->parent works... How does the irq framework know
> how to "tie" this new domain into the mix... Probably by
> walking the DT? So setting the parent is not required then?

I have written some code to manage level interrupts.
(Copied below for illustration purposes)

I have some lingering doubts. I would appreciate it if someone
could shed some light.

1) Is it correct to call irq_domain_set_info() in the .map callback?

2) Passing handle_level_irq to irq_domain_set_info() means this
domain can only handle level interrupts, right?

3) If I need to handle also edge interrupts, would I need a separate
domain?

4) How does the core decide which domain to pass to the .map callback?
Is that the role of the .select callback?

5) If I need to route edge and level interrupts, do I need to use
2 separate output lines to the GIC? Or maybe even 4 output lines
(edge rising/falling, level high/low) ?

6) What is the symmetric irq_domain_set_info() to call in .unmap ?





#include <linux/of_irq.h>
#include <linux/of_address.h>
#include <linux/irqchip.h>
#include <linux/irqchip/chained_irq.h>

#define IRQ_MAX 128

struct tango_intc {
	DECLARE_BITMAP(enabled, IRQ_MAX);
	spinlock_t lock;
	void __iomem *base;
	void __iomem *status;
	struct irq_domain *dom;
};

static void tango_isr(struct irq_desc *desc)
{
	unsigned int pos, virq;
	struct irq_chip *chip = irq_desc_get_chip(desc);
	struct tango_intc *intc = irq_desc_get_handler_data(desc);
	DECLARE_BITMAP(status, IRQ_MAX);

	chained_irq_enter(chip, desc);

	memcpy_fromio(status, intc->status, IRQ_MAX / BITS_PER_BYTE);
	bitmap_and(status, status, intc->enabled, IRQ_MAX);
	for_each_set_bit(pos, status, IRQ_MAX) {
		virq = irq_find_mapping(intc->dom, pos);
		generic_handle_irq(virq);
	}

	chained_irq_exit(chip, desc);
}

static void tango_mask(struct irq_data *data)
{
	unsigned long flags;
	struct tango_intc *intc = data->chip_data;

	spin_lock_irqsave(&intc->lock, flags);
	__clear_bit(data->hwirq, intc->enabled);
	writel_relaxed(0, intc->base + data->hwirq * 4);
	spin_unlock_irqrestore(&intc->lock, flags);
}

static void tango_unmask(struct irq_data *data)
{
	unsigned long flags;
	struct tango_intc *intc = data->chip_data;

	spin_lock_irqsave(&intc->lock, flags);
	__set_bit(data->hwirq, intc->enabled);
	writel_relaxed(BIT(31), intc->base + data->hwirq * 4);
	spin_unlock_irqrestore(&intc->lock, flags);
}

static struct irq_chip tango_chip = {
	.name		= "tango",
	.irq_mask	= tango_mask,
	.irq_unmask	= tango_unmask,
	//.irq_set_type
};

static int tango_map(struct irq_domain *dom, unsigned int virq, irq_hw_number_t hw)
{
	struct tango_intc *intc = dom->host_data;
	printk("%s: dom=%p virq=%u hwirq=%lu\n", __func__, dom, virq, hw);
	//dump_stack();
	/*** GROS DOUTE ICI ***/
	irq_domain_set_info(dom, virq, hw, &tango_chip, intc,
			handle_level_irq, NULL, NULL);

	return 0;
}

static void tango_unmap(struct irq_domain *d, unsigned int virq)
{
	printk("%s: dom=%p virq=%u\n", __func__, d, virq);
	dump_stack();
}

struct irq_domain_ops dom_ops =
{
	.map = tango_map,
	.unmap = tango_unmap,
};

static int __init tango_irq_init(struct device_node *node, struct device_node *parent)
{
	int i;
	int virq;
	struct irq_domain *irq_dom;
	void __iomem *base;
	struct tango_intc *intc = kzalloc(sizeof(*intc), GFP_KERNEL);

	base = of_iomap(node, 0);
	if (!base)
		panic("%s: of_iomap failed", node->name);

	virq = irq_of_parse_and_map(node, 0);
	if (!virq)
		panic("%s: Failed to map IRQ\n", node->name);

	irq_dom = irq_domain_add_linear(node, IRQ_MAX, &dom_ops, intc);

	irq_set_chained_handler_and_data(virq, tango_isr, intc);

	spin_lock_init(&intc->lock);
	intc->base = base;
	intc->status = base + 0x420;
	intc->dom = irq_dom;

	return 0;
}
IRQCHIP_DECLARE(tango_intc, "sigma,rev2-intc", tango_irq_init);



More information about the linux-arm-kernel mailing list