On Thu, 2011-04-28 at 14:02 -0600, Grant Likely wrote: > 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.
I think you are going the wrong way around. First thing first, is to make the irq domain / mapping API generic without the OF bits. IE. move the IRQ domain generically, get rid of irq_map by putting the domain ptr & hw numbers in the irq desc/data etc... Then you can move over the OF specific bits which are optional and orthogonal to a large extent. Cheers, Ben. > 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.lik...@secretlab.ca> > --- > 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 > */ _______________________________________________ Linuxppc-dev mailing list Linuxppc-dev@lists.ozlabs.org https://lists.ozlabs.org/listinfo/linuxppc-dev