Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

genirq/irqdomain: Allow irq domain aliasing

It is not uncommon (at least with the ARM stuff) to have a piece
of hardware that implements different flavours of "interrupts".
A typical example of this is the GICv3 ITS, which implements
standard PCI/MSI support, but also some form of "generic MSI".

So far, the PCI/MSI domain is registered using the ITS device_node,
so that irq_find_host can return it. On the contrary, the raw MSI
domain is not registered with an device_node, making it impossible
to be looked up by another subsystem (obviously, using the same
device_node twice would only result in confusion, as it is not
defined which one irq_find_host would return).

A solution to this is to "type" domains that may be aliasing, and
to be able to lookup an device_node that matches a given type.
For this, we introduce irq_find_matching_host() as a superset
of irq_find_host:

struct irq_domain *irq_find_matching_host(struct device_node *node,
enum irq_domain_bus_token bus_token);

where bus_token is the "type" we want to match the domain against
(so far, only DOMAIN_BUS_ANY is defined). This result in some
moderately invasive changes on the PPC side (which is the only
user of the .match method).

This has otherwise no functionnal change.

Reviewed-by: Hanjun Guo <hanjun.guo@linaro.org>
Signed-off-by: Marc Zyngier <marc.zyngier@arm.com>
Cc: <linux-arm-kernel@lists.infradead.org>
Cc: Yijing Wang <wangyijing@huawei.com>
Cc: Ma Jun <majun258@huawei.com>
Cc: Lorenzo Pieralisi <lorenzo.pieralisi@arm.com>
Cc: Duc Dang <dhdang@apm.com>
Cc: Bjorn Helgaas <bhelgaas@google.com>
Cc: Jiang Liu <jiang.liu@linux.intel.com>
Cc: Jason Cooper <jason@lakedaemon.net>
Link: http://lkml.kernel.org/r/1438091186-10244-2-git-send-email-marc.zyngier@arm.com
Signed-off-by: Thomas Gleixner <tglx@linutronix.de>

authored by

Marc Zyngier and committed by
Thomas Gleixner
ad3aedfb 8505a81b

+58 -19
+2 -1
arch/powerpc/platforms/512x/mpc5121_ads_cpld.c
··· 123 123 } 124 124 125 125 static int 126 - cpld_pic_host_match(struct irq_domain *h, struct device_node *node) 126 + cpld_pic_host_match(struct irq_domain *h, struct device_node *node, 127 + enum irq_domain_bus_token bus_token) 127 128 { 128 129 return cpld_pic_node == node; 129 130 }
+2 -1
arch/powerpc/platforms/cell/interrupt.c
··· 222 222 #endif /* CONFIG_SMP */ 223 223 224 224 225 - static int iic_host_match(struct irq_domain *h, struct device_node *node) 225 + static int iic_host_match(struct irq_domain *h, struct device_node *node, 226 + enum irq_domain_bus_token bus_token) 226 227 { 227 228 return of_device_is_compatible(node, 228 229 "IBM,CBEA-Internal-Interrupt-Controller");
+2 -1
arch/powerpc/platforms/embedded6xx/flipper-pic.c
··· 108 108 return 0; 109 109 } 110 110 111 - static int flipper_pic_match(struct irq_domain *h, struct device_node *np) 111 + static int flipper_pic_match(struct irq_domain *h, struct device_node *np, 112 + enum irq_domain_bus_token bus_token) 112 113 { 113 114 return 1; 114 115 }
+2 -1
arch/powerpc/platforms/powermac/pic.c
··· 268 268 .name = "cascade", 269 269 }; 270 270 271 - static int pmac_pic_host_match(struct irq_domain *h, struct device_node *node) 271 + static int pmac_pic_host_match(struct irq_domain *h, struct device_node *node, 272 + enum irq_domain_bus_token bus_token) 272 273 { 273 274 /* We match all, we don't always have a node anyway */ 274 275 return 1;
+2 -1
arch/powerpc/platforms/powernv/opal-irqchip.c
··· 134 134 opal_handle_events(be64_to_cpu(last_outstanding_events)); 135 135 } 136 136 137 - static int opal_event_match(struct irq_domain *h, struct device_node *node) 137 + static int opal_event_match(struct irq_domain *h, struct device_node *node, 138 + enum irq_domain_bus_token bus_token) 138 139 { 139 140 return h->of_node == node; 140 141 }
+2 -1
arch/powerpc/platforms/ps3/interrupt.c
··· 678 678 return 0; 679 679 } 680 680 681 - static int ps3_host_match(struct irq_domain *h, struct device_node *np) 681 + static int ps3_host_match(struct irq_domain *h, struct device_node *np, 682 + enum irq_domain_bus_token bus_token) 682 683 { 683 684 /* Match all */ 684 685 return 1;
+2 -1
arch/powerpc/sysdev/ehv_pic.c
··· 177 177 return irq_linear_revmap(global_ehv_pic->irqhost, irq); 178 178 } 179 179 180 - static int ehv_pic_host_match(struct irq_domain *h, struct device_node *node) 180 + static int ehv_pic_host_match(struct irq_domain *h, struct device_node *node, 181 + enum irq_domain_bus_token bus_token) 181 182 { 182 183 /* Exact match, unless ehv_pic node is NULL */ 183 184 return h->of_node == NULL || h->of_node == node;
+2 -1
arch/powerpc/sysdev/i8259.c
··· 162 162 .flags = IORESOURCE_BUSY, 163 163 }; 164 164 165 - static int i8259_host_match(struct irq_domain *h, struct device_node *node) 165 + static int i8259_host_match(struct irq_domain *h, struct device_node *node, 166 + enum irq_domain_bus_token bus_token) 166 167 { 167 168 return h->of_node == NULL || h->of_node == node; 168 169 }
+2 -1
arch/powerpc/sysdev/ipic.c
··· 671 671 .irq_set_type = ipic_set_irq_type, 672 672 }; 673 673 674 - static int ipic_host_match(struct irq_domain *h, struct device_node *node) 674 + static int ipic_host_match(struct irq_domain *h, struct device_node *node, 675 + enum irq_domain_bus_token bus_token) 675 676 { 676 677 /* Exact match, unless ipic node is NULL */ 677 678 return h->of_node == NULL || h->of_node == node;
+2 -1
arch/powerpc/sysdev/mpic.c
··· 1007 1007 #endif /* CONFIG_MPIC_U3_HT_IRQS */ 1008 1008 1009 1009 1010 - static int mpic_host_match(struct irq_domain *h, struct device_node *node) 1010 + static int mpic_host_match(struct irq_domain *h, struct device_node *node, 1011 + enum irq_domain_bus_token bus_token) 1011 1012 { 1012 1013 /* Exact match, unless mpic node is NULL */ 1013 1014 return h->of_node == NULL || h->of_node == node;
+2 -1
arch/powerpc/sysdev/qe_lib/qe_ic.c
··· 244 244 .irq_mask_ack = qe_ic_mask_irq, 245 245 }; 246 246 247 - static int qe_ic_host_match(struct irq_domain *h, struct device_node *node) 247 + static int qe_ic_host_match(struct irq_domain *h, struct device_node *node, 248 + enum irq_domain_bus_token bus_token) 248 249 { 249 250 /* Exact match, unless qe_ic node is NULL */ 250 251 return h->of_node == NULL || h->of_node == node;
+2 -1
arch/powerpc/sysdev/xics/xics-common.c
··· 298 298 } 299 299 #endif /* CONFIG_SMP */ 300 300 301 - static int xics_host_match(struct irq_domain *h, struct device_node *node) 301 + static int xics_host_match(struct irq_domain *h, struct device_node *node, 302 + enum irq_domain_bus_token bus_token) 302 303 { 303 304 struct ics *ics; 304 305
+21 -2
include/linux/irqdomain.h
··· 45 45 /* Number of irqs reserved for a legacy isa controller */ 46 46 #define NUM_ISA_INTERRUPTS 16 47 47 48 + /* 49 + * Should several domains have the same device node, but serve 50 + * different purposes (for example one domain is for PCI/MSI, and the 51 + * other for wired IRQs), they can be distinguished using a 52 + * bus-specific token. Most domains are expected to only carry 53 + * DOMAIN_BUS_ANY. 54 + */ 55 + enum irq_domain_bus_token { 56 + DOMAIN_BUS_ANY = 0, 57 + }; 58 + 48 59 /** 49 60 * struct irq_domain_ops - Methods for irq_domain objects 50 61 * @match: Match an interrupt controller device node to a host, returns ··· 72 61 * to setup the irq_desc when returning from map(). 73 62 */ 74 63 struct irq_domain_ops { 75 - int (*match)(struct irq_domain *d, struct device_node *node); 64 + int (*match)(struct irq_domain *d, struct device_node *node, 65 + enum irq_domain_bus_token bus_token); 76 66 int (*map)(struct irq_domain *d, unsigned int virq, irq_hw_number_t hw); 77 67 void (*unmap)(struct irq_domain *d, unsigned int virq); 78 68 int (*xlate)(struct irq_domain *d, struct device_node *node, ··· 128 116 129 117 /* Optional data */ 130 118 struct device_node *of_node; 119 + enum irq_domain_bus_token bus_token; 131 120 struct irq_domain_chip_generic *gc; 132 121 #ifdef CONFIG_IRQ_DOMAIN_HIERARCHY 133 122 struct irq_domain *parent; ··· 174 161 irq_hw_number_t first_hwirq, 175 162 const struct irq_domain_ops *ops, 176 163 void *host_data); 177 - extern struct irq_domain *irq_find_host(struct device_node *node); 164 + extern struct irq_domain *irq_find_matching_host(struct device_node *node, 165 + enum irq_domain_bus_token bus_token); 178 166 extern void irq_set_default_host(struct irq_domain *host); 167 + 168 + static inline struct irq_domain *irq_find_host(struct device_node *node) 169 + { 170 + return irq_find_matching_host(node, DOMAIN_BUS_ANY); 171 + } 179 172 180 173 /** 181 174 * irq_domain_add_linear() - Allocate and register a linear revmap irq_domain.
+13 -5
kernel/irq/irqdomain.c
··· 187 187 EXPORT_SYMBOL_GPL(irq_domain_add_legacy); 188 188 189 189 /** 190 - * irq_find_host() - Locates a domain for a given device node 190 + * irq_find_matching_host() - Locates a domain for a given device node 191 191 * @node: device-tree node of the interrupt controller 192 + * @bus_token: domain-specific data 192 193 */ 193 - struct irq_domain *irq_find_host(struct device_node *node) 194 + struct irq_domain *irq_find_matching_host(struct device_node *node, 195 + enum irq_domain_bus_token bus_token) 194 196 { 195 197 struct irq_domain *h, *found = NULL; 196 198 int rc; ··· 201 199 * it might potentially be set to match all interrupts in 202 200 * the absence of a device node. This isn't a problem so far 203 201 * yet though... 202 + * 203 + * bus_token == DOMAIN_BUS_ANY matches any domain, any other 204 + * values must generate an exact match for the domain to be 205 + * selected. 204 206 */ 205 207 mutex_lock(&irq_domain_mutex); 206 208 list_for_each_entry(h, &irq_domain_list, link) { 207 209 if (h->ops->match) 208 - rc = h->ops->match(h, node); 210 + rc = h->ops->match(h, node, bus_token); 209 211 else 210 - rc = (h->of_node != NULL) && (h->of_node == node); 212 + rc = ((h->of_node != NULL) && (h->of_node == node) && 213 + ((bus_token == DOMAIN_BUS_ANY) || 214 + (h->bus_token == bus_token))); 211 215 212 216 if (rc) { 213 217 found = h; ··· 223 215 mutex_unlock(&irq_domain_mutex); 224 216 return found; 225 217 } 226 - EXPORT_SYMBOL_GPL(irq_find_host); 218 + EXPORT_SYMBOL_GPL(irq_find_matching_host); 227 219 228 220 /** 229 221 * irq_set_default_host() - Set a "default" irq domain