[PATCH 4/6] dt: generalize irq_of_create_mapping()

From: Grant Likely
Date: Thu Apr 28 2011 - 16:07:24 EST


This patch creates a common implementation of irq_of_create_mapping()
and factors out the interrupt domain translation code from powerpc to
make it available for all architectures.

It creates a new structure, struct of_irq_domain, which can be
embedded into the private data structure of an interrupt controller.
Any interrupt controller can call of_irq_domain_add() to register a
structure with a .map() hook that can translate irq specifiers from
the device tree into linux irq numbers.

The patch also modifies the powerpc irq_host to embed the
of_irq_domain structure and use the new common infrastructure for
registering domains. This separates the reverse mapping and irq
allocation infrastructure of irq_host from the domain registration
infrastructure. I elected to split the functionality this way for
several reasons. First, with the major irq cleanup done by Thomas
Gleixner, dynamic allocation of irqs can be handled gracefully with
irq_alloc_desc*() and interrupt controllers may not need or want an
irq_host layer to manage it for them. For example, the new
irq_chip_generic() already has a method for mapping between hwirq and
Linux irq.

Second, the irq_host code currently has a lot of complexity to handle
all the different reverse mapping types from a single structure. The
radix mapping in particular has a lot of support code, but only one
user. I didn't want to bring that complexity into the common code
as-is. Instead, I'd prefer to create a separate encapsulating
structure for each revmap, and each type would have a separate .map
hook.

For now, the mapping code remains powerpc-specific and further
generalization will happen in subsequent patches.

Signed-off-by: Grant Likely <grant.likely@xxxxxxxxxxxx>
---
arch/microblaze/kernel/irq.c | 7 --
arch/mips/kernel/prom.c | 14 ----
arch/powerpc/include/asm/irq.h | 21 +++--
arch/powerpc/include/asm/irqhost.h | 4 -
arch/powerpc/kernel/irq.c | 99 ++++++++++++++-----------
arch/powerpc/platforms/cell/axon_msi.c | 12 ++-
arch/powerpc/platforms/cell/spider-pic.c | 8 +-
arch/powerpc/sysdev/fsl_msi.c | 2 -
arch/powerpc/sysdev/i8259.c | 2 -
arch/powerpc/sysdev/ipic.c | 2 -
arch/powerpc/sysdev/mpic.c | 4 +
arch/powerpc/sysdev/mpic_msi.c | 2 -
arch/powerpc/sysdev/mpic_pasemi_msi.c | 4 +
arch/powerpc/sysdev/qe_lib/qe_ic.c | 2 -
arch/x86/include/asm/irq_controller.h | 12 ---
arch/x86/include/asm/prom.h | 1
arch/x86/kernel/devicetree.c | 77 ++++----------------
drivers/of/irq.c | 118 ++++++++++++++++++++++++++++++
include/linux/of_irq.h | 31 ++++++++
19 files changed, 248 insertions(+), 174 deletions(-)
delete mode 100644 arch/x86/include/asm/irq_controller.h

diff --git a/arch/microblaze/kernel/irq.c b/arch/microblaze/kernel/irq.c
index ce7ac84..59bb560 100644
--- a/arch/microblaze/kernel/irq.c
+++ b/arch/microblaze/kernel/irq.c
@@ -54,10 +54,3 @@ unsigned int irq_create_mapping(struct irq_host *host, irq_hw_number_t hwirq)
return hwirq;
}
EXPORT_SYMBOL_GPL(irq_create_mapping);
-
-unsigned int irq_create_of_mapping(struct device_node *controller,
- const u32 *intspec, unsigned int intsize)
-{
- return intspec[0];
-}
-EXPORT_SYMBOL_GPL(irq_create_of_mapping);
diff --git a/arch/mips/kernel/prom.c b/arch/mips/kernel/prom.c
index a19811e9..0b82f98 100644
--- a/arch/mips/kernel/prom.c
+++ b/arch/mips/kernel/prom.c
@@ -60,20 +60,6 @@ void __init early_init_dt_setup_initrd_arch(unsigned long start,
}
#endif

-/*
- * irq_create_of_mapping - Hook to resolve OF irq specifier into a Linux irq#
- *
- * Currently the mapping mechanism is trivial; simple flat hwirq numbers are
- * mapped 1:1 onto Linux irq numbers. Cascaded irq controllers are not
- * supported.
- */
-unsigned int irq_create_of_mapping(struct device_node *controller,
- const u32 *intspec, unsigned int intsize)
-{
- return intspec[0];
-}
-EXPORT_SYMBOL_GPL(irq_create_of_mapping);
-
void __init early_init_devtree(void *params)
{
/* Setup flat device-tree pointer */
diff --git a/arch/powerpc/include/asm/irq.h b/arch/powerpc/include/asm/irq.h
index a44be93..ccefc8c 100644
--- a/arch/powerpc/include/asm/irq.h
+++ b/arch/powerpc/include/asm/irq.h
@@ -55,11 +55,18 @@ typedef unsigned long irq_hw_number_t;
* model). It's the host callbacks that are responsible for setting the
* irq_chip on a given irq_desc after it's been mapped.
*
+ * irq_host builds upon the of_irq_domain code in drivers/of/irq.c.
+ * of_irq_domain provides all of the translation hooks for registering irq
+ * controllers. irq_host add mapping infrastructure to and from hardware
+ * irq numbers. IRQ controllers that don't need the mapping infrastructure
+ * can use irq_domain directly.
+ *
* The host code and data structures are fairly agnostic to the fact that
* we use an open firmware device-tree. We do have references to struct
- * device_node in two places: in irq_find_host() to find the host matching
- * a given interrupt controller node, and of course as an argument to its
- * counterpart host->ops->match() callback. However, those are treated as
+ * device_node in two places: in of_irq_domain_find() to find the host matching
+ * a given interrupt controller node (which is actually common of_irq_domain
+ * code), and of course as an argument to its counterpart host->ops->match()
+ * and host->domain->match() callbacks. However, those are treated as
* generic pointers by the core and the fact that it's actually a device-node
* pointer is purely a convention between callers and implementation. This
* code could thus be used on other architectures by replacing those two
@@ -137,14 +144,6 @@ extern struct irq_host *irq_alloc_host(struct device_node *of_node,
struct irq_host_ops *ops,
irq_hw_number_t inval_irq);

-
-/**
- * irq_find_host - Locates a host for a given device node
- * @node: device-tree node of the interrupt controller
- */
-extern struct irq_host *irq_find_host(struct device_node *node);
-
-
/**
* irq_set_default_host - Set a "default" host
* @host: default host pointer
diff --git a/arch/powerpc/include/asm/irqhost.h b/arch/powerpc/include/asm/irqhost.h
index 958e6c1..a97a513 100644
--- a/arch/powerpc/include/asm/irqhost.h
+++ b/arch/powerpc/include/asm/irqhost.h
@@ -8,6 +8,7 @@

struct irq_host {
struct list_head link;
+ struct of_irq_domain domain;

/* type of reverse mapping technique */
unsigned int revmap_type;
@@ -21,9 +22,6 @@ struct irq_host {
struct irq_host_ops *ops;
void *host_data;
irq_hw_number_t inval_irq;
-
- /* Optional device node pointer */
- struct device_node *of_node;
};

#endif /* _ASM_POWERPC_IRQHOST_H */
diff --git a/arch/powerpc/kernel/irq.c b/arch/powerpc/kernel/irq.c
index b961b19..9300e1c 100644
--- a/arch/powerpc/kernel/irq.c
+++ b/arch/powerpc/kernel/irq.c
@@ -514,11 +514,40 @@ struct irq_host *virq_to_host(unsigned int virq)
}
EXPORT_SYMBOL_GPL(virq_to_host);

-static int default_irq_host_match(struct irq_host *h, struct device_node *np)
+/**
+ * irq_host_domain_match() - irq_domain hook to call irq_host match ops.
+ *
+ * This functions gets set as the irq domain match function for irq_host
+ * instances *if* the ->ops->match() hook is populated. If ->match() is
+ * not populated, then the default irq_domain matching behaviour is used
+ * instead.
+ */
+static bool irq_host_domain_match(struct of_irq_domain *domain,
+ struct device_node *controller)
{
- return h->of_node != NULL && h->of_node == np;
+ struct irq_host *host = container_of(domain, struct irq_host, domain);
+ return host->ops->match(host, controller);
}

+static unsigned int irq_host_domain_map(struct of_irq_domain *domain,
+ struct device_node *controller,
+ const u32 *intspec,
+ unsigned int intsize);
+
+/**
+ * irq_alloc_host() - Allocate and register an irq_host
+ * @of_node: Device node of the irq controller; this is used mainly as an
+ * anonymouns context pointer.
+ * @revmap_type: One of IRQ_HOST_MAP_* defined in arch/powerpc/include/asm/irq.h
+ * Defines the type of reverse map to be used by the irq_host.
+ * @revmap_arg: Currently only used by the IRQ_HOST_MAP_LINEAR which uses it
+ * to define the size of the reverse map.
+ * @ops: irq_host ops structure for match/map/unmap/remap/xlate operations.
+ * @inval_irq: Value used by irq controller to indicate an invalid irq.
+ *
+ * irq_host implements mapping between hardware irq numbers and the linux
+ * virq number space. This function allocates and registers an irq_host.
+ */
struct irq_host *irq_alloc_host(struct device_node *of_node,
unsigned int revmap_type,
unsigned int revmap_arg,
@@ -542,10 +571,10 @@ struct irq_host *irq_alloc_host(struct device_node *of_node,
host->revmap_type = revmap_type;
host->inval_irq = inval_irq;
host->ops = ops;
- host->of_node = of_node_get(of_node);
-
- if (host->ops->match == NULL)
- host->ops->match = default_irq_host_match;
+ host->domain.controller = of_node_get(of_node);
+ host->domain.map = irq_host_domain_map;
+ if (host->ops->match != NULL)
+ host->domain.match = irq_host_domain_match;

raw_spin_lock_irqsave(&irq_big_lock, flags);

@@ -561,7 +590,7 @@ struct irq_host *irq_alloc_host(struct device_node *of_node,
* instead of the current cruft
*/
if (mem_init_done) {
- of_node_put(host->of_node);
+ of_node_put(host->domain.controller);
kfree(host);
}
return NULL;
@@ -572,6 +601,8 @@ struct irq_host *irq_alloc_host(struct device_node *of_node,
list_add(&host->link, &irq_hosts);
raw_spin_unlock_irqrestore(&irq_big_lock, flags);

+ of_irq_domain_add(&host->domain);
+
/* Additional setups per revmap type */
switch(revmap_type) {
case IRQ_HOST_MAP_LEGACY:
@@ -611,32 +642,12 @@ struct irq_host *irq_alloc_host(struct device_node *of_node,
return host;
}

-struct irq_host *irq_find_host(struct device_node *node)
-{
- struct irq_host *h, *found = NULL;
- unsigned long flags;
-
- /* We might want to match the legacy controller last since
- * it might potentially be set to match all interrupts in
- * the absence of a device node. This isn't a problem so far
- * yet though...
- */
- raw_spin_lock_irqsave(&irq_big_lock, flags);
- list_for_each_entry(h, &irq_hosts, link)
- if (h->ops->match(h, node)) {
- found = h;
- break;
- }
- raw_spin_unlock_irqrestore(&irq_big_lock, flags);
- return found;
-}
-EXPORT_SYMBOL_GPL(irq_find_host);
-
void irq_set_default_host(struct irq_host *host)
{
pr_debug("irq: Default host set to @0x%p\n", host);

irq_default_host = host;
+ of_irq_set_default_domain(&host->domain);
}

void irq_set_virq_count(unsigned int count)
@@ -757,30 +768,29 @@ unsigned int irq_create_mapping(struct irq_host *host,
return NO_IRQ;

printk(KERN_DEBUG "irq: irq %lu on host %s mapped to virtual irq %u\n",
- hwirq, host->of_node ? host->of_node->full_name : "null", virq);
+ hwirq, host->domain.controller ? host->domain.controller->full_name : "null", virq);

return virq;
}
EXPORT_SYMBOL_GPL(irq_create_mapping);

-unsigned int irq_create_of_mapping(struct device_node *controller,
- const u32 *intspec, unsigned int intsize)
+/**
+ * irq_host_domain_map() - Map device tree irq to linux irq number
+ * This hook implements all of the powerpc 'irq_host' behaviour, which means
+ * - calling the ->ops->xlate hook to get the hardware irq number,
+ * - calling of_create_mapping to translate/allocate a linux virq number
+ * - calling irq_set_irq_type() if necessary
+ */
+static unsigned int irq_host_domain_map(struct of_irq_domain *domain,
+ struct device_node *controller,
+ const u32 *intspec,
+ unsigned int intsize)
{
- struct irq_host *host;
+ struct irq_host *host = container_of(domain, struct irq_host, domain);
irq_hw_number_t hwirq;
unsigned int type = IRQ_TYPE_NONE;
unsigned int virq;

- if (controller == NULL)
- host = irq_default_host;
- else
- host = irq_find_host(controller);
- if (host == NULL) {
- printk(KERN_WARNING "irq: no irq host found for %s !\n",
- controller->full_name);
- return NO_IRQ;
- }
-
/* If host has no translation, then we assume interrupt line */
if (host->ops->xlate == NULL)
hwirq = intspec[0];
@@ -801,7 +811,6 @@ unsigned int irq_create_of_mapping(struct device_node *controller,
irq_set_irq_type(virq, type);
return virq;
}
-EXPORT_SYMBOL_GPL(irq_create_of_mapping);

void irq_dispose_mapping(unsigned int virq)
{
@@ -1128,8 +1137,8 @@ static int virq_debug_show(struct seq_file *m, void *private)
p = none;
seq_printf(m, "%-15s ", p);

- if (irq_map[i].host && irq_map[i].host->of_node)
- p = irq_map[i].host->of_node->full_name;
+ if (irq_map[i].host && irq_map[i].host->domain.controller)
+ p = irq_map[i].host->domain.controller->full_name;
else
p = none;
seq_printf(m, "%s\n", p);
diff --git a/arch/powerpc/platforms/cell/axon_msi.c b/arch/powerpc/platforms/cell/axon_msi.c
index e1469ae..f125673 100644
--- a/arch/powerpc/platforms/cell/axon_msi.c
+++ b/arch/powerpc/platforms/cell/axon_msi.c
@@ -152,7 +152,7 @@ static void axon_msi_cascade(unsigned int irq, struct irq_desc *desc)

static struct axon_msic *find_msi_translator(struct pci_dev *dev)
{
- struct irq_host *irq_host;
+ struct of_irq_domain *irq_domain;
struct device_node *dn, *tmp;
const phandle *ph;
struct axon_msic *msic = NULL;
@@ -184,14 +184,14 @@ static struct axon_msic *find_msi_translator(struct pci_dev *dev)
goto out_error;
}

- irq_host = irq_find_host(dn);
- if (!irq_host) {
+ irq_domain = of_irq_domain_find(dn);
+ if (!irq_domain) {
dev_dbg(&dev->dev, "axon_msi: no irq_host found for node %s\n",
dn->full_name);
goto out_error;
}

- msic = irq_host->host_data;
+ msic = irq_domain->priv;

out_error:
of_node_put(dn);
@@ -336,7 +336,7 @@ static void axon_msi_shutdown(struct platform_device *device)
u32 tmp;

pr_devel("axon_msi: disabling %s\n",
- msic->irq_host->of_node->full_name);
+ msic->irq_host->domain.controller->full_name);
tmp = dcr_read(msic->dcr_host, MSIC_CTRL_REG);
tmp &= ~MSIC_CTRL_ENABLE & ~MSIC_CTRL_IRQ_ENABLE;
msic_dcr_write(msic, MSIC_CTRL_REG, tmp);
@@ -399,7 +399,7 @@ static int axon_msi_probe(struct platform_device *device)
goto out_free_fifo;
}

- msic->irq_host->host_data = msic;
+ msic->irq_host->domain.priv = msic;

irq_set_handler_data(virq, msic);
irq_set_chained_handler(virq, axon_msi_cascade);
diff --git a/arch/powerpc/platforms/cell/spider-pic.c b/arch/powerpc/platforms/cell/spider-pic.c
index 73a5494..5bf36ab 100644
--- a/arch/powerpc/platforms/cell/spider-pic.c
+++ b/arch/powerpc/platforms/cell/spider-pic.c
@@ -236,18 +236,20 @@ static unsigned int __init spider_find_cascade_and_node(struct spider_pic *pic)
* tree in case the device-tree is ever fixed
*/
struct of_irq oirq;
- if (of_irq_map_one(pic->host->of_node, 0, &oirq) == 0) {
+ if (of_irq_map_one(pic->host->domain.controller, 0, &oirq) == 0) {
virq = irq_create_of_mapping(oirq.controller, oirq.specifier,
oirq.size);
return virq;
}

/* Now do the horrible hacks */
- tmp = of_get_property(pic->host->of_node, "#interrupt-cells", NULL);
+ tmp = of_get_property(pic->host->domain.controller,
+ "#interrupt-cells", NULL);
if (tmp == NULL)
return NO_IRQ;
intsize = *tmp;
- imap = of_get_property(pic->host->of_node, "interrupt-map", &imaplen);
+ imap = of_get_property(pic->host->domain.controller,
+ "interrupt-map", &imaplen);
if (imap == NULL || imaplen < (intsize + 1))
return NO_IRQ;
iic = of_find_node_by_phandle(imap[intsize]);
diff --git a/arch/powerpc/sysdev/fsl_msi.c b/arch/powerpc/sysdev/fsl_msi.c
index 2c11b3e..b45a25a 100644
--- a/arch/powerpc/sysdev/fsl_msi.c
+++ b/arch/powerpc/sysdev/fsl_msi.c
@@ -82,7 +82,7 @@ static int fsl_msi_init_allocator(struct fsl_msi *msi_data)
int rc;

rc = msi_bitmap_alloc(&msi_data->bitmap, NR_MSI_IRQS,
- msi_data->irqhost->of_node);
+ msi_data->irqhost->domain.controller);
if (rc)
return rc;

diff --git a/arch/powerpc/sysdev/i8259.c b/arch/powerpc/sysdev/i8259.c
index 30869f0..a6f78fb 100644
--- a/arch/powerpc/sysdev/i8259.c
+++ b/arch/powerpc/sysdev/i8259.c
@@ -166,7 +166,7 @@ static struct resource pic_edgectrl_iores = {

static int i8259_host_match(struct irq_host *h, struct device_node *node)
{
- return h->of_node == NULL || h->of_node == node;
+ return h->domain.controller == NULL || h->domain.controller == node;
}

static int i8259_host_map(struct irq_host *h, unsigned int virq,
diff --git a/arch/powerpc/sysdev/ipic.c b/arch/powerpc/sysdev/ipic.c
index fc3751f..5805c7b 100644
--- a/arch/powerpc/sysdev/ipic.c
+++ b/arch/powerpc/sysdev/ipic.c
@@ -676,7 +676,7 @@ static struct irq_chip ipic_edge_irq_chip = {
static int ipic_host_match(struct irq_host *h, struct device_node *node)
{
/* Exact match, unless ipic node is NULL */
- return h->of_node == NULL || h->of_node == node;
+ return h->domain.controller == NULL || h->domain.controller == node;
}

static int ipic_host_map(struct irq_host *h, unsigned int virq,
diff --git a/arch/powerpc/sysdev/mpic.c b/arch/powerpc/sysdev/mpic.c
index 6e9e594..cafc364 100644
--- a/arch/powerpc/sysdev/mpic.c
+++ b/arch/powerpc/sysdev/mpic.c
@@ -956,7 +956,7 @@ static struct irq_chip mpic_irq_ht_chip = {
static int mpic_host_match(struct irq_host *h, struct device_node *node)
{
/* Exact match, unless mpic node is NULL */
- return h->of_node == NULL || h->of_node == node;
+ return h->domain.controller == NULL || h->domain.controller == node;
}

static int mpic_host_map(struct irq_host *h, unsigned int virq,
@@ -1296,7 +1296,7 @@ void __init mpic_assign_isu(struct mpic *mpic, unsigned int isu_num,

BUG_ON(isu_num >= MPIC_MAX_ISU);

- mpic_map(mpic, mpic->irqhost->of_node,
+ mpic_map(mpic, mpic->irqhost->domain.controller,
paddr, &mpic->isus[isu_num], 0,
MPIC_INFO(IRQ_STRIDE) * mpic->isu_size);

diff --git a/arch/powerpc/sysdev/mpic_msi.c b/arch/powerpc/sysdev/mpic_msi.c
index 50176ed..ddf79c7 100644
--- a/arch/powerpc/sysdev/mpic_msi.c
+++ b/arch/powerpc/sysdev/mpic_msi.c
@@ -85,7 +85,7 @@ int mpic_msi_init_allocator(struct mpic *mpic)
int rc;

rc = msi_bitmap_alloc(&mpic->msi_bitmap, mpic->irq_count,
- mpic->irqhost->of_node);
+ mpic->irqhost->domain.controller);
if (rc)
return rc;

diff --git a/arch/powerpc/sysdev/mpic_pasemi_msi.c b/arch/powerpc/sysdev/mpic_pasemi_msi.c
index 6b11a89..857be51 100644
--- a/arch/powerpc/sysdev/mpic_pasemi_msi.c
+++ b/arch/powerpc/sysdev/mpic_pasemi_msi.c
@@ -153,8 +153,8 @@ int mpic_pasemi_msi_init(struct mpic *mpic)
{
int rc;

- if (!mpic->irqhost->of_node ||
- !of_device_is_compatible(mpic->irqhost->of_node,
+ if (!mpic->irqhost->domain.controller ||
+ !of_device_is_compatible(mpic->irqhost->domain.controller,
"pasemi,pwrficient-openpic"))
return -ENODEV;

diff --git a/arch/powerpc/sysdev/qe_lib/qe_ic.c b/arch/powerpc/sysdev/qe_lib/qe_ic.c
index 9dd7746..c2ccafa 100644
--- a/arch/powerpc/sysdev/qe_lib/qe_ic.c
+++ b/arch/powerpc/sysdev/qe_lib/qe_ic.c
@@ -250,7 +250,7 @@ static struct irq_chip qe_ic_irq_chip = {
static int qe_ic_host_match(struct irq_host *h, struct device_node *node)
{
/* Exact match, unless qe_ic node is NULL */
- return h->of_node == NULL || h->of_node == node;
+ return h->domain.controller == NULL || h->domain.controller == node;
}

static int qe_ic_host_map(struct irq_host *h, unsigned int virq,
diff --git a/arch/x86/include/asm/irq_controller.h b/arch/x86/include/asm/irq_controller.h
deleted file mode 100644
index 423bbbd..0000000
--- a/arch/x86/include/asm/irq_controller.h
+++ /dev/null
@@ -1,12 +0,0 @@
-#ifndef __IRQ_CONTROLLER__
-#define __IRQ_CONTROLLER__
-
-struct irq_domain {
- int (*xlate)(struct irq_domain *h, const u32 *intspec, u32 intsize,
- u32 *out_hwirq, u32 *out_type);
- void *priv;
- struct device_node *controller;
- struct list_head l;
-};
-
-#endif
diff --git a/arch/x86/include/asm/prom.h b/arch/x86/include/asm/prom.h
index 971e0b4..eb9d5ab 100644
--- a/arch/x86/include/asm/prom.h
+++ b/arch/x86/include/asm/prom.h
@@ -21,7 +21,6 @@
#include <asm/irq.h>
#include <asm/atomic.h>
#include <asm/setup.h>
-#include <asm/irq_controller.h>

#ifdef CONFIG_OF
extern int of_ioapic;
diff --git a/arch/x86/kernel/devicetree.c b/arch/x86/kernel/devicetree.c
index 706a9fb..651e724 100644
--- a/arch/x86/kernel/devicetree.c
+++ b/arch/x86/kernel/devicetree.c
@@ -15,64 +15,14 @@
#include <linux/of_pci.h>

#include <asm/hpet.h>
-#include <asm/irq_controller.h>
#include <asm/apic.h>
#include <asm/pci_x86.h>

__initdata u64 initial_dtb;
char __initdata cmd_line[COMMAND_LINE_SIZE];
-static LIST_HEAD(irq_domains);
-static DEFINE_RAW_SPINLOCK(big_irq_lock);

int __initdata of_ioapic;

-#ifdef CONFIG_X86_IO_APIC
-static void add_interrupt_host(struct irq_domain *ih)
-{
- unsigned long flags;
-
- raw_spin_lock_irqsave(&big_irq_lock, flags);
- list_add(&ih->l, &irq_domains);
- raw_spin_unlock_irqrestore(&big_irq_lock, flags);
-}
-#endif
-
-static struct irq_domain *get_ih_from_node(struct device_node *controller)
-{
- struct irq_domain *ih, *found = NULL;
- unsigned long flags;
-
- raw_spin_lock_irqsave(&big_irq_lock, flags);
- list_for_each_entry(ih, &irq_domains, l) {
- if (ih->controller == controller) {
- found = ih;
- break;
- }
- }
- raw_spin_unlock_irqrestore(&big_irq_lock, flags);
- return found;
-}
-
-unsigned int irq_create_of_mapping(struct device_node *controller,
- const u32 *intspec, unsigned int intsize)
-{
- struct irq_domain *ih;
- u32 virq, type;
- int ret;
-
- ih = get_ih_from_node(controller);
- if (!ih)
- return 0;
- ret = ih->xlate(ih, intspec, intsize, &virq, &type);
- if (ret)
- return 0;
- if (type == IRQ_TYPE_NONE)
- return virq;
- irq_set_irq_type(virq, type);
- return virq;
-}
-EXPORT_SYMBOL_GPL(irq_create_of_mapping);
-
unsigned long pci_address_to_pio(phys_addr_t address)
{
/*
@@ -366,32 +316,33 @@ static struct of_ioapic_type of_ioapic_type[] =
},
};

-static int ioapic_xlate(struct irq_domain *id, const u32 *intspec, u32 intsize,
- u32 *out_hwirq, u32 *out_type)
+static unsigned int ioapic_of_irq_map(struct of_irq_domain *id,
+ struct device_node *np,
+ const u32 *intspec, u32 intsize)
{
struct io_apic_irq_attr attr;
struct of_ioapic_type *it;
- u32 line, idx, type;
+ u32 line, idx, type, hwirq;

if (intsize < 2)
- return -EINVAL;
+ return 0;

line = *intspec;
idx = (u32) id->priv;
- *out_hwirq = line + mp_gsi_routing[idx].gsi_base;
+ hwirq = line + mp_gsi_routing[idx].gsi_base;

intspec++;
type = *intspec;

if (type >= ARRAY_SIZE(of_ioapic_type))
- return -EINVAL;
+ return 0;

it = of_ioapic_type + type;
- *out_type = it->out_type;
-
set_io_apic_irq_attr(&attr, idx, line, it->trigger, it->polarity);

- return io_apic_setup_irq_pin(*out_hwirq, cpu_to_node(0), &attr);
+ if (io_apic_setup_irq_pin(hwirq, cpu_to_node(0), &attr))
+ return 0;
+ return hwirq;
}

static void __init ioapic_add_ofnode(struct device_node *np)
@@ -408,14 +359,14 @@ static void __init ioapic_add_ofnode(struct device_node *np)

for (i = 0; i < nr_ioapics; i++) {
if (r.start == mp_ioapics[i].apicaddr) {
- struct irq_domain *id;
+ struct of_irq_domain *id;

id = kzalloc(sizeof(*id), GFP_KERNEL);
BUG_ON(!id);
- id->controller = np;
- id->xlate = ioapic_xlate;
+ id->controller = of_node_get(np);
+ id->map = ioapic_of_irq_map;
id->priv = (void *)i;
- add_interrupt_host(id);
+ of_irq_domain_add(id);
return;
}
}
diff --git a/drivers/of/irq.c b/drivers/of/irq.c
index 75b0d3c..6da0964 100644
--- a/drivers/of/irq.c
+++ b/drivers/of/irq.c
@@ -19,6 +19,7 @@
*/

#include <linux/errno.h>
+#include <linux/list.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/of_irq.h>
@@ -29,6 +30,123 @@
#define NO_IRQ 0
#endif

+/*
+ * Device Tree IRQ domains
+ *
+ * IRQ domains provide translation from device tree irq controller nodes to
+ * linux IRQ numbers. IRQ controllers register an irq_domain with a .map()
+ * hook that performs everything needed to decode and configure a device
+ * tree specified interrupt.
+ */
+static LIST_HEAD(of_irq_domains);
+static DEFINE_RAW_SPINLOCK(of_irq_lock);
+static struct of_irq_domain *of_irq_default_domain;
+
+/**
+ * of_irq_domain_default_match() - Return true if the controller pointers match
+ *
+ * Default match behaviour for of_irq_domains. If the device tree node pointer
+ * matches the value stored in the domain structure, then return true.
+ */
+static bool of_irq_domain_default_match(struct of_irq_domain *domain,
+ struct device_node *controller)
+{
+ return domain->controller == controller;
+}
+
+/**
+ * of_irq_domain_add() - Register a device tree irq domain
+ * @domain: pointer to domain structure to be registered.
+ *
+ * Adds an of_irq_domain to the global list of domains.
+ */
+void of_irq_domain_add(struct of_irq_domain *domain)
+{
+ unsigned long flags;
+
+ if (!domain->match)
+ domain->match = of_irq_domain_default_match;
+ if (!domain->map) {
+ WARN_ON(1);
+ return;
+ }
+
+ raw_spin_lock_irqsave(&of_irq_lock, flags);
+ list_add(&domain->list, &of_irq_domains);
+ raw_spin_unlock_irqrestore(&of_irq_lock, flags);
+}
+
+/**
+ * of_irq_domain_find() - Find the domain that handles a given device tree node
+ *
+ * Returns the pointer to an of_irq_domain capable of translating irq specifiers
+ * for the given irq controller device tree node. Returns NULL if a suitable
+ * domain could not be found.
+ */
+struct of_irq_domain *of_irq_domain_find(struct device_node *controller)
+{
+ struct of_irq_domain *domain, *found = NULL;
+ unsigned long flags;
+
+ raw_spin_lock_irqsave(&of_irq_lock, flags);
+ list_for_each_entry(domain, &of_irq_domains, list) {
+ if (domain->match(domain, controller)) {
+ found = domain;
+ break;
+ }
+ }
+ raw_spin_unlock_irqrestore(&of_irq_lock, flags);
+ return found;
+}
+
+/**
+ * of_irq_set_default_domain() - Set a "default" host
+ * @domain: default domain pointer
+ *
+ * For convenience, it's possible to set a "default" host that will be used
+ * whenever NULL is passed to irq_of_create_mapping(). It makes life easier
+ * for platforms that want to manipulate a few hard coded interrupt numbers
+ * that aren't properly represented in the device-tree.
+ */
+void of_irq_set_default_domain(struct of_irq_domain *domain)
+{
+ pr_debug("irq: Default host set to @0x%p\n", domain);
+ of_irq_default_domain = domain;
+}
+
+/**
+ * irq_create_of_mapping() - Map a linux irq # from a device tree specifier
+ * @controller - interrupt-controller node in the device tree
+ * @intspec - array of interrupt specifier data. Points to an array of u32
+ * values. Data is *cpu-native* endian u32 values.
+ * @intsize - size of intspec array.
+ *
+ * Given an interrupt controller node pointer and an interrupt specifier, this
+ * function looks up the linux irq number.
+ */
+unsigned int irq_create_of_mapping(struct device_node *controller,
+ const u32 *intspec, unsigned int intsize)
+{
+ struct of_irq_domain *domain;
+
+ domain = of_irq_domain_find(controller);
+ if (!domain)
+ domain = of_irq_default_domain;
+ if (!domain) {
+ pr_warn("error: no irq host found for %s !\n",
+ controller->full_name);
+#if defined(CONFIG_MIPS) || defined(CONFIG_MICROBLAZE)
+ /* FIXME: make Microblaze and MIPS register irq domains */
+ return intspec[0];
+#else /* defined(CONFIG_MIPS) || defined(CONFIG_MICROBLAZE) */
+ return NO_IRQ;
+#endif /* defined(CONFIG_MIPS) || defined(CONFIG_MICROBLAZE) */
+ }
+
+ return domain->map(domain, controller, intspec, intsize);
+}
+EXPORT_SYMBOL_GPL(irq_create_of_mapping);
+
/**
* irq_of_parse_and_map - Parse and map an interrupt into linux virq space
* @device: Device node of the device whose interrupt is to be mapped
diff --git a/include/linux/of_irq.h b/include/linux/of_irq.h
index 109e013..511dbc3 100644
--- a/include/linux/of_irq.h
+++ b/include/linux/of_irq.h
@@ -33,6 +33,37 @@ struct of_irq {
u32 specifier[OF_MAX_IRQ_SPEC]; /* Specifier copy */
};

+/**
+ * struct of_irq_domain - Translation domain from device tree to linux irq
+ * @list: Linked list node entry
+ * @match: (optional) Called to determine if the passed device_node
+ * interrupt-controller can be translated by this irq domain.
+ * Returns 'true' if it can.
+ * @decode: Translation callback; returns virq, or NO_IRQ if this irq
+ * domain cannot translate it.
+ * @controller: (optional) pointer to OF node. By default, if
+ * 'match' is not set, then this of_irq_domain will only
+ * be used if the device tree node passed in matches the
+ * controller pointer.
+ * @priv: Private data pointer, not touched by core of_irq_domain code.
+ */
+struct of_irq_domain {
+ struct list_head list;
+ bool (*match)(struct of_irq_domain *d, struct device_node *np);
+ unsigned int (*map)(struct of_irq_domain *d, struct device_node *np,
+ const u32 *intspec, u32 intsize);
+ struct device_node *controller;
+ void *priv;
+};
+
+/**
+ * of_irq_domain_add() - Add a device tree interrupt translation domain
+ * @domain: interrupt domain to add.
+ */
+extern void of_irq_domain_add(struct of_irq_domain *domain);
+extern void of_irq_set_default_domain(struct of_irq_domain *host);
+extern struct of_irq_domain *of_irq_domain_find(struct device_node *controller);
+
/*
* Workarounds only applied to 32bit powermac machines
*/

--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/