blob: 6328d9350f04f38dde7933204fa7159e2227e28a [file] [log] [blame]
Grant Likelycc79ca62012-02-16 01:37:49 -07001#include <linux/debugfs.h>
2#include <linux/hardirq.h>
3#include <linux/interrupt.h>
Grant Likely08a543a2011-07-26 03:19:06 -06004#include <linux/irq.h>
Grant Likelycc79ca62012-02-16 01:37:49 -07005#include <linux/irqdesc.h>
Grant Likely08a543a2011-07-26 03:19:06 -06006#include <linux/irqdomain.h>
7#include <linux/module.h>
8#include <linux/mutex.h>
9#include <linux/of.h>
Grant Likely7e713302011-07-26 03:19:06 -060010#include <linux/of_address.h>
Grant Likelycc79ca62012-02-16 01:37:49 -070011#include <linux/seq_file.h>
Grant Likely7e713302011-07-26 03:19:06 -060012#include <linux/slab.h>
Grant Likelycc79ca62012-02-16 01:37:49 -070013#include <linux/smp.h>
14#include <linux/fs.h>
Grant Likely08a543a2011-07-26 03:19:06 -060015
Grant Likely1bc04f22012-02-14 14:06:55 -070016#define IRQ_DOMAIN_MAP_LEGACY 0 /* driver allocated fixed range of irqs.
17 * ie. legacy 8259, gets irqs 1..15 */
Grant Likelya8db8cf2012-02-14 14:06:54 -070018#define IRQ_DOMAIN_MAP_NOMAP 1 /* no fast reverse mapping */
19#define IRQ_DOMAIN_MAP_LINEAR 2 /* linear map of interrupts */
20#define IRQ_DOMAIN_MAP_TREE 3 /* radix tree */
21
Grant Likely08a543a2011-07-26 03:19:06 -060022static LIST_HEAD(irq_domain_list);
23static DEFINE_MUTEX(irq_domain_mutex);
24
Grant Likelycc79ca62012-02-16 01:37:49 -070025static DEFINE_MUTEX(revmap_trees_mutex);
26static unsigned int irq_virq_count = NR_IRQS;
Grant Likely68700652012-02-14 14:06:53 -070027static struct irq_domain *irq_default_domain;
Grant Likelycc79ca62012-02-16 01:37:49 -070028
Grant Likely68700652012-02-14 14:06:53 -070029static int default_irq_domain_match(struct irq_domain *d, struct device_node *np)
Grant Likelycc79ca62012-02-16 01:37:49 -070030{
Grant Likely68700652012-02-14 14:06:53 -070031 return d->of_node != NULL && d->of_node == np;
Grant Likelycc79ca62012-02-16 01:37:49 -070032}
33
34/**
Grant Likelya8db8cf2012-02-14 14:06:54 -070035 * irq_domain_alloc() - Allocate a new irq_domain data structure
Grant Likelycc79ca62012-02-16 01:37:49 -070036 * @of_node: optional device-tree node of the interrupt controller
37 * @revmap_type: type of reverse mapping to use
Grant Likely68700652012-02-14 14:06:53 -070038 * @ops: map/unmap domain callbacks
Grant Likelya8db8cf2012-02-14 14:06:54 -070039 * @host_data: Controller private data pointer
Grant Likelycc79ca62012-02-16 01:37:49 -070040 *
Grant Likelya8db8cf2012-02-14 14:06:54 -070041 * Allocates and initialize and irq_domain structure. Caller is expected to
42 * register allocated irq_domain with irq_domain_register(). Returns pointer
43 * to IRQ domain, or NULL on failure.
Grant Likelycc79ca62012-02-16 01:37:49 -070044 */
Grant Likelya8db8cf2012-02-14 14:06:54 -070045static struct irq_domain *irq_domain_alloc(struct device_node *of_node,
46 unsigned int revmap_type,
47 struct irq_domain_ops *ops,
48 void *host_data)
Grant Likelycc79ca62012-02-16 01:37:49 -070049{
Grant Likelya8db8cf2012-02-14 14:06:54 -070050 struct irq_domain *domain;
Grant Likelycc79ca62012-02-16 01:37:49 -070051
Grant Likelya8db8cf2012-02-14 14:06:54 -070052 domain = kzalloc(sizeof(*domain), GFP_KERNEL);
53 if (WARN_ON(!domain))
Grant Likelycc79ca62012-02-16 01:37:49 -070054 return NULL;
55
56 /* Fill structure */
Grant Likely68700652012-02-14 14:06:53 -070057 domain->revmap_type = revmap_type;
Grant Likely68700652012-02-14 14:06:53 -070058 domain->ops = ops;
Grant Likelya8db8cf2012-02-14 14:06:54 -070059 domain->host_data = host_data;
Grant Likely68700652012-02-14 14:06:53 -070060 domain->of_node = of_node_get(of_node);
Grant Likelycc79ca62012-02-16 01:37:49 -070061
Grant Likely68700652012-02-14 14:06:53 -070062 if (domain->ops->match == NULL)
63 domain->ops->match = default_irq_domain_match;
Grant Likelycc79ca62012-02-16 01:37:49 -070064
Grant Likelya8db8cf2012-02-14 14:06:54 -070065 return domain;
66}
67
68static void irq_domain_add(struct irq_domain *domain)
69{
70 mutex_lock(&irq_domain_mutex);
71 list_add(&domain->link, &irq_domain_list);
72 mutex_unlock(&irq_domain_mutex);
73 pr_debug("irq: Allocated domain of type %d @0x%p\n",
74 domain->revmap_type, domain);
75}
76
Grant Likely1bc04f22012-02-14 14:06:55 -070077static unsigned int irq_domain_legacy_revmap(struct irq_domain *domain,
78 irq_hw_number_t hwirq)
79{
80 irq_hw_number_t first_hwirq = domain->revmap_data.legacy.first_hwirq;
81 int size = domain->revmap_data.legacy.size;
82
83 if (WARN_ON(hwirq < first_hwirq || hwirq >= first_hwirq + size))
84 return 0;
85 return hwirq - first_hwirq + domain->revmap_data.legacy.first_irq;
86}
87
Grant Likelya8db8cf2012-02-14 14:06:54 -070088/**
89 * irq_domain_add_legacy() - Allocate and register a legacy revmap irq_domain.
90 * @of_node: pointer to interrupt controller's device tree node.
Grant Likely1bc04f22012-02-14 14:06:55 -070091 * @size: total number of irqs in legacy mapping
92 * @first_irq: first number of irq block assigned to the domain
93 * @first_hwirq: first hwirq number to use for the translation. Should normally
94 * be '0', but a positive integer can be used if the effective
95 * hwirqs numbering does not begin at zero.
Grant Likelya8db8cf2012-02-14 14:06:54 -070096 * @ops: map/unmap domain callbacks
97 * @host_data: Controller private data pointer
98 *
99 * Note: the map() callback will be called before this function returns
100 * for all legacy interrupts except 0 (which is always the invalid irq for
101 * a legacy controller).
102 */
103struct irq_domain *irq_domain_add_legacy(struct device_node *of_node,
Grant Likely1bc04f22012-02-14 14:06:55 -0700104 unsigned int size,
105 unsigned int first_irq,
106 irq_hw_number_t first_hwirq,
Grant Likelya8db8cf2012-02-14 14:06:54 -0700107 struct irq_domain_ops *ops,
108 void *host_data)
109{
Grant Likely1bc04f22012-02-14 14:06:55 -0700110 struct irq_domain *domain;
Grant Likelya8db8cf2012-02-14 14:06:54 -0700111 unsigned int i;
112
113 domain = irq_domain_alloc(of_node, IRQ_DOMAIN_MAP_LEGACY, ops, host_data);
114 if (!domain)
115 return NULL;
116
Grant Likely1bc04f22012-02-14 14:06:55 -0700117 domain->revmap_data.legacy.first_irq = first_irq;
118 domain->revmap_data.legacy.first_hwirq = first_hwirq;
119 domain->revmap_data.legacy.size = size;
120
Grant Likelycc79ca62012-02-16 01:37:49 -0700121 mutex_lock(&irq_domain_mutex);
Grant Likely1bc04f22012-02-14 14:06:55 -0700122 /* Verify that all the irqs are available */
123 for (i = 0; i < size; i++) {
124 int irq = first_irq + i;
125 struct irq_data *irq_data = irq_get_irq_data(irq);
126
127 if (WARN_ON(!irq_data || irq_data->domain)) {
Grant Likelya8db8cf2012-02-14 14:06:54 -0700128 mutex_unlock(&irq_domain_mutex);
129 of_node_put(domain->of_node);
130 kfree(domain);
131 return NULL;
Grant Likelycc79ca62012-02-16 01:37:49 -0700132 }
133 }
Grant Likely1bc04f22012-02-14 14:06:55 -0700134
135 /* Claim all of the irqs before registering a legacy domain */
136 for (i = 0; i < size; i++) {
137 struct irq_data *irq_data = irq_get_irq_data(first_irq + i);
138 irq_data->hwirq = first_hwirq + i;
139 irq_data->domain = domain;
140 }
Grant Likelycc79ca62012-02-16 01:37:49 -0700141 mutex_unlock(&irq_domain_mutex);
142
Grant Likely1bc04f22012-02-14 14:06:55 -0700143 for (i = 0; i < size; i++) {
144 int irq = first_irq + i;
145 int hwirq = first_hwirq + i;
146
147 /* IRQ0 gets ignored */
148 if (!irq)
149 continue;
Grant Likelycc79ca62012-02-16 01:37:49 -0700150
Grant Likelya8db8cf2012-02-14 14:06:54 -0700151 /* Legacy flags are left to default at this point,
152 * one can then use irq_create_mapping() to
153 * explicitly change them
154 */
Grant Likely1bc04f22012-02-14 14:06:55 -0700155 ops->map(domain, irq, hwirq);
Grant Likelycc79ca62012-02-16 01:37:49 -0700156
Grant Likelya8db8cf2012-02-14 14:06:54 -0700157 /* Clear norequest flags */
Grant Likely1bc04f22012-02-14 14:06:55 -0700158 irq_clear_status_flags(irq, IRQ_NOREQUEST);
Grant Likelycc79ca62012-02-16 01:37:49 -0700159 }
Grant Likely1bc04f22012-02-14 14:06:55 -0700160
161 irq_domain_add(domain);
Grant Likelya8db8cf2012-02-14 14:06:54 -0700162 return domain;
163}
Grant Likelycc79ca62012-02-16 01:37:49 -0700164
Grant Likelya8db8cf2012-02-14 14:06:54 -0700165/**
166 * irq_domain_add_linear() - Allocate and register a legacy revmap irq_domain.
167 * @of_node: pointer to interrupt controller's device tree node.
168 * @ops: map/unmap domain callbacks
169 * @host_data: Controller private data pointer
170 */
171struct irq_domain *irq_domain_add_linear(struct device_node *of_node,
172 unsigned int size,
173 struct irq_domain_ops *ops,
174 void *host_data)
175{
176 struct irq_domain *domain;
177 unsigned int *revmap;
Grant Likelycc79ca62012-02-16 01:37:49 -0700178
Grant Likelya8db8cf2012-02-14 14:06:54 -0700179 revmap = kzalloc(sizeof(*revmap) * size, GFP_KERNEL);
180 if (WARN_ON(!revmap))
181 return NULL;
182
183 domain = irq_domain_alloc(of_node, IRQ_DOMAIN_MAP_LINEAR, ops, host_data);
184 if (!domain) {
185 kfree(revmap);
186 return NULL;
187 }
188 domain->revmap_data.linear.size = size;
189 domain->revmap_data.linear.revmap = revmap;
190 irq_domain_add(domain);
191 return domain;
192}
193
194struct irq_domain *irq_domain_add_nomap(struct device_node *of_node,
195 struct irq_domain_ops *ops,
196 void *host_data)
197{
198 struct irq_domain *domain = irq_domain_alloc(of_node,
199 IRQ_DOMAIN_MAP_NOMAP, ops, host_data);
200 if (domain)
201 irq_domain_add(domain);
202 return domain;
203}
204
205/**
206 * irq_domain_add_tree()
207 * @of_node: pointer to interrupt controller's device tree node.
208 * @ops: map/unmap domain callbacks
209 *
210 * Note: The radix tree will be allocated later during boot automatically
211 * (the reverse mapping will use the slow path until that happens).
212 */
213struct irq_domain *irq_domain_add_tree(struct device_node *of_node,
214 struct irq_domain_ops *ops,
215 void *host_data)
216{
217 struct irq_domain *domain = irq_domain_alloc(of_node,
218 IRQ_DOMAIN_MAP_TREE, ops, host_data);
219 if (domain) {
220 INIT_RADIX_TREE(&domain->revmap_data.tree, GFP_KERNEL);
221 irq_domain_add(domain);
222 }
Grant Likely68700652012-02-14 14:06:53 -0700223 return domain;
Grant Likelycc79ca62012-02-16 01:37:49 -0700224}
225
226/**
227 * irq_find_host() - Locates a domain for a given device node
228 * @node: device-tree node of the interrupt controller
229 */
230struct irq_domain *irq_find_host(struct device_node *node)
231{
232 struct irq_domain *h, *found = NULL;
233
234 /* We might want to match the legacy controller last since
235 * it might potentially be set to match all interrupts in
236 * the absence of a device node. This isn't a problem so far
237 * yet though...
238 */
239 mutex_lock(&irq_domain_mutex);
240 list_for_each_entry(h, &irq_domain_list, link)
241 if (h->ops->match(h, node)) {
242 found = h;
243 break;
244 }
245 mutex_unlock(&irq_domain_mutex);
246 return found;
247}
248EXPORT_SYMBOL_GPL(irq_find_host);
249
250/**
251 * irq_set_default_host() - Set a "default" irq domain
Grant Likely68700652012-02-14 14:06:53 -0700252 * @domain: default domain pointer
Grant Likelycc79ca62012-02-16 01:37:49 -0700253 *
254 * For convenience, it's possible to set a "default" domain that will be used
255 * whenever NULL is passed to irq_create_mapping(). It makes life easier for
256 * platforms that want to manipulate a few hard coded interrupt numbers that
257 * aren't properly represented in the device-tree.
258 */
Grant Likely68700652012-02-14 14:06:53 -0700259void irq_set_default_host(struct irq_domain *domain)
Grant Likelycc79ca62012-02-16 01:37:49 -0700260{
Grant Likely68700652012-02-14 14:06:53 -0700261 pr_debug("irq: Default domain set to @0x%p\n", domain);
Grant Likelycc79ca62012-02-16 01:37:49 -0700262
Grant Likely68700652012-02-14 14:06:53 -0700263 irq_default_domain = domain;
Grant Likelycc79ca62012-02-16 01:37:49 -0700264}
265
266/**
267 * irq_set_virq_count() - Set the maximum number of linux irqs
268 * @count: number of linux irqs, capped with NR_IRQS
269 *
270 * This is mainly for use by platforms like iSeries who want to program
271 * the virtual irq number in the controller to avoid the reverse mapping
272 */
273void irq_set_virq_count(unsigned int count)
274{
275 pr_debug("irq: Trying to set virq count to %d\n", count);
276
277 BUG_ON(count < NUM_ISA_INTERRUPTS);
278 if (count < NR_IRQS)
279 irq_virq_count = count;
280}
281
Grant Likely68700652012-02-14 14:06:53 -0700282static int irq_setup_virq(struct irq_domain *domain, unsigned int virq,
Grant Likelycc79ca62012-02-16 01:37:49 -0700283 irq_hw_number_t hwirq)
284{
285 struct irq_data *irq_data = irq_get_irq_data(virq);
286
287 irq_data->hwirq = hwirq;
Grant Likely68700652012-02-14 14:06:53 -0700288 irq_data->domain = domain;
289 if (domain->ops->map(domain, virq, hwirq)) {
Grant Likelycc79ca62012-02-16 01:37:49 -0700290 pr_debug("irq: -> mapping failed, freeing\n");
291 irq_data->domain = NULL;
292 irq_data->hwirq = 0;
293 return -1;
294 }
295
296 irq_clear_status_flags(virq, IRQ_NOREQUEST);
297
298 return 0;
299}
300
301/**
302 * irq_create_direct_mapping() - Allocate an irq for direct mapping
Grant Likely68700652012-02-14 14:06:53 -0700303 * @domain: domain to allocate the irq for or NULL for default domain
Grant Likelycc79ca62012-02-16 01:37:49 -0700304 *
305 * This routine is used for irq controllers which can choose the hardware
306 * interrupt numbers they generate. In such a case it's simplest to use
307 * the linux irq as the hardware interrupt number.
308 */
Grant Likely68700652012-02-14 14:06:53 -0700309unsigned int irq_create_direct_mapping(struct irq_domain *domain)
Grant Likelycc79ca62012-02-16 01:37:49 -0700310{
311 unsigned int virq;
312
Grant Likely68700652012-02-14 14:06:53 -0700313 if (domain == NULL)
314 domain = irq_default_domain;
Grant Likelycc79ca62012-02-16 01:37:49 -0700315
Grant Likely68700652012-02-14 14:06:53 -0700316 BUG_ON(domain == NULL);
317 WARN_ON(domain->revmap_type != IRQ_DOMAIN_MAP_NOMAP);
Grant Likelycc79ca62012-02-16 01:37:49 -0700318
319 virq = irq_alloc_desc_from(1, 0);
Grant Likely03848372012-02-14 14:06:52 -0700320 if (!virq) {
Grant Likelycc79ca62012-02-16 01:37:49 -0700321 pr_debug("irq: create_direct virq allocation failed\n");
Grant Likely03848372012-02-14 14:06:52 -0700322 return 0;
Grant Likelycc79ca62012-02-16 01:37:49 -0700323 }
324 if (virq >= irq_virq_count) {
325 pr_err("ERROR: no free irqs available below %i maximum\n",
326 irq_virq_count);
327 irq_free_desc(virq);
328 return 0;
329 }
330
331 pr_debug("irq: create_direct obtained virq %d\n", virq);
332
Grant Likely68700652012-02-14 14:06:53 -0700333 if (irq_setup_virq(domain, virq, virq)) {
Grant Likelycc79ca62012-02-16 01:37:49 -0700334 irq_free_desc(virq);
Grant Likely03848372012-02-14 14:06:52 -0700335 return 0;
Grant Likelycc79ca62012-02-16 01:37:49 -0700336 }
337
338 return virq;
339}
340
341/**
342 * irq_create_mapping() - Map a hardware interrupt into linux irq space
Grant Likely68700652012-02-14 14:06:53 -0700343 * @domain: domain owning this hardware interrupt or NULL for default domain
344 * @hwirq: hardware irq number in that domain space
Grant Likelycc79ca62012-02-16 01:37:49 -0700345 *
346 * Only one mapping per hardware interrupt is permitted. Returns a linux
347 * irq number.
348 * If the sense/trigger is to be specified, set_irq_type() should be called
349 * on the number returned from that call.
350 */
Grant Likely68700652012-02-14 14:06:53 -0700351unsigned int irq_create_mapping(struct irq_domain *domain,
Grant Likelycc79ca62012-02-16 01:37:49 -0700352 irq_hw_number_t hwirq)
353{
354 unsigned int virq, hint;
355
Grant Likely68700652012-02-14 14:06:53 -0700356 pr_debug("irq: irq_create_mapping(0x%p, 0x%lx)\n", domain, hwirq);
Grant Likelycc79ca62012-02-16 01:37:49 -0700357
Grant Likely68700652012-02-14 14:06:53 -0700358 /* Look for default domain if nececssary */
359 if (domain == NULL)
360 domain = irq_default_domain;
361 if (domain == NULL) {
Grant Likelycc79ca62012-02-16 01:37:49 -0700362 printk(KERN_WARNING "irq_create_mapping called for"
Grant Likely68700652012-02-14 14:06:53 -0700363 " NULL domain, hwirq=%lx\n", hwirq);
Grant Likelycc79ca62012-02-16 01:37:49 -0700364 WARN_ON(1);
Grant Likely03848372012-02-14 14:06:52 -0700365 return 0;
Grant Likelycc79ca62012-02-16 01:37:49 -0700366 }
Grant Likely68700652012-02-14 14:06:53 -0700367 pr_debug("irq: -> using domain @%p\n", domain);
Grant Likelycc79ca62012-02-16 01:37:49 -0700368
369 /* Check if mapping already exists */
Grant Likely68700652012-02-14 14:06:53 -0700370 virq = irq_find_mapping(domain, hwirq);
Grant Likely03848372012-02-14 14:06:52 -0700371 if (virq) {
Grant Likelycc79ca62012-02-16 01:37:49 -0700372 pr_debug("irq: -> existing mapping on virq %d\n", virq);
373 return virq;
374 }
375
376 /* Get a virtual interrupt number */
Grant Likely1bc04f22012-02-14 14:06:55 -0700377 if (domain->revmap_type == IRQ_DOMAIN_MAP_LEGACY)
378 return irq_domain_legacy_revmap(domain, hwirq);
379
380 /* Allocate a virtual interrupt number */
381 hint = hwirq % irq_virq_count;
382 if (hint == 0)
383 hint++;
384 virq = irq_alloc_desc_from(hint, 0);
385 if (!virq)
386 virq = irq_alloc_desc_from(1, 0);
387 if (!virq) {
388 pr_debug("irq: -> virq allocation failed\n");
389 return 0;
Grant Likelycc79ca62012-02-16 01:37:49 -0700390 }
391
Grant Likely68700652012-02-14 14:06:53 -0700392 if (irq_setup_virq(domain, virq, hwirq)) {
393 if (domain->revmap_type != IRQ_DOMAIN_MAP_LEGACY)
Grant Likelycc79ca62012-02-16 01:37:49 -0700394 irq_free_desc(virq);
Grant Likely03848372012-02-14 14:06:52 -0700395 return 0;
Grant Likelycc79ca62012-02-16 01:37:49 -0700396 }
397
Grant Likely68700652012-02-14 14:06:53 -0700398 pr_debug("irq: irq %lu on domain %s mapped to virtual irq %u\n",
399 hwirq, domain->of_node ? domain->of_node->full_name : "null", virq);
Grant Likelycc79ca62012-02-16 01:37:49 -0700400
401 return virq;
402}
403EXPORT_SYMBOL_GPL(irq_create_mapping);
404
405unsigned int irq_create_of_mapping(struct device_node *controller,
406 const u32 *intspec, unsigned int intsize)
407{
Grant Likely68700652012-02-14 14:06:53 -0700408 struct irq_domain *domain;
Grant Likelycc79ca62012-02-16 01:37:49 -0700409 irq_hw_number_t hwirq;
410 unsigned int type = IRQ_TYPE_NONE;
411 unsigned int virq;
412
Grant Likely68700652012-02-14 14:06:53 -0700413 domain = controller ? irq_find_host(controller) : irq_default_domain;
414 if (!domain) {
415 printk(KERN_WARNING "irq: no irq domain found for %s !\n",
Grant Likelycc79ca62012-02-16 01:37:49 -0700416 controller->full_name);
Grant Likely03848372012-02-14 14:06:52 -0700417 return 0;
Grant Likelycc79ca62012-02-16 01:37:49 -0700418 }
419
Grant Likely68700652012-02-14 14:06:53 -0700420 /* If domain has no translation, then we assume interrupt line */
421 if (domain->ops->xlate == NULL)
Grant Likelycc79ca62012-02-16 01:37:49 -0700422 hwirq = intspec[0];
423 else {
Grant Likely68700652012-02-14 14:06:53 -0700424 if (domain->ops->xlate(domain, controller, intspec, intsize,
Grant Likelycc79ca62012-02-16 01:37:49 -0700425 &hwirq, &type))
Grant Likely03848372012-02-14 14:06:52 -0700426 return 0;
Grant Likelycc79ca62012-02-16 01:37:49 -0700427 }
428
429 /* Create mapping */
Grant Likely68700652012-02-14 14:06:53 -0700430 virq = irq_create_mapping(domain, hwirq);
Grant Likely03848372012-02-14 14:06:52 -0700431 if (!virq)
Grant Likelycc79ca62012-02-16 01:37:49 -0700432 return virq;
433
434 /* Set type if specified and different than the current one */
435 if (type != IRQ_TYPE_NONE &&
436 type != (irqd_get_trigger_type(irq_get_irq_data(virq))))
437 irq_set_irq_type(virq, type);
438 return virq;
439}
440EXPORT_SYMBOL_GPL(irq_create_of_mapping);
441
442/**
443 * irq_dispose_mapping() - Unmap an interrupt
444 * @virq: linux irq number of the interrupt to unmap
445 */
446void irq_dispose_mapping(unsigned int virq)
447{
448 struct irq_data *irq_data = irq_get_irq_data(virq);
Grant Likely68700652012-02-14 14:06:53 -0700449 struct irq_domain *domain;
Grant Likelycc79ca62012-02-16 01:37:49 -0700450 irq_hw_number_t hwirq;
451
Grant Likely03848372012-02-14 14:06:52 -0700452 if (!virq || !irq_data)
Grant Likelycc79ca62012-02-16 01:37:49 -0700453 return;
454
Grant Likely68700652012-02-14 14:06:53 -0700455 domain = irq_data->domain;
456 if (WARN_ON(domain == NULL))
Grant Likelycc79ca62012-02-16 01:37:49 -0700457 return;
458
459 /* Never unmap legacy interrupts */
Grant Likely68700652012-02-14 14:06:53 -0700460 if (domain->revmap_type == IRQ_DOMAIN_MAP_LEGACY)
Grant Likelycc79ca62012-02-16 01:37:49 -0700461 return;
462
463 irq_set_status_flags(virq, IRQ_NOREQUEST);
464
465 /* remove chip and handler */
466 irq_set_chip_and_handler(virq, NULL, NULL);
467
468 /* Make sure it's completed */
469 synchronize_irq(virq);
470
471 /* Tell the PIC about it */
Grant Likely68700652012-02-14 14:06:53 -0700472 if (domain->ops->unmap)
473 domain->ops->unmap(domain, virq);
Grant Likelycc79ca62012-02-16 01:37:49 -0700474 smp_mb();
475
476 /* Clear reverse map */
477 hwirq = irq_data->hwirq;
Grant Likely68700652012-02-14 14:06:53 -0700478 switch(domain->revmap_type) {
Grant Likelycc79ca62012-02-16 01:37:49 -0700479 case IRQ_DOMAIN_MAP_LINEAR:
Grant Likely68700652012-02-14 14:06:53 -0700480 if (hwirq < domain->revmap_data.linear.size)
481 domain->revmap_data.linear.revmap[hwirq] = 0;
Grant Likelycc79ca62012-02-16 01:37:49 -0700482 break;
483 case IRQ_DOMAIN_MAP_TREE:
484 mutex_lock(&revmap_trees_mutex);
Grant Likely68700652012-02-14 14:06:53 -0700485 radix_tree_delete(&domain->revmap_data.tree, hwirq);
Grant Likelycc79ca62012-02-16 01:37:49 -0700486 mutex_unlock(&revmap_trees_mutex);
487 break;
488 }
489
Grant Likelycc79ca62012-02-16 01:37:49 -0700490 irq_free_desc(virq);
491}
492EXPORT_SYMBOL_GPL(irq_dispose_mapping);
493
494/**
495 * irq_find_mapping() - Find a linux irq from an hw irq number.
Grant Likely68700652012-02-14 14:06:53 -0700496 * @domain: domain owning this hardware interrupt
497 * @hwirq: hardware irq number in that domain space
Grant Likelycc79ca62012-02-16 01:37:49 -0700498 *
499 * This is a slow path, for use by generic code. It's expected that an
500 * irq controller implementation directly calls the appropriate low level
501 * mapping function.
502 */
Grant Likely68700652012-02-14 14:06:53 -0700503unsigned int irq_find_mapping(struct irq_domain *domain,
Grant Likelycc79ca62012-02-16 01:37:49 -0700504 irq_hw_number_t hwirq)
505{
506 unsigned int i;
507 unsigned int hint = hwirq % irq_virq_count;
508
Grant Likely68700652012-02-14 14:06:53 -0700509 /* Look for default domain if nececssary */
510 if (domain == NULL)
511 domain = irq_default_domain;
512 if (domain == NULL)
Grant Likely03848372012-02-14 14:06:52 -0700513 return 0;
Grant Likelycc79ca62012-02-16 01:37:49 -0700514
515 /* legacy -> bail early */
Grant Likely68700652012-02-14 14:06:53 -0700516 if (domain->revmap_type == IRQ_DOMAIN_MAP_LEGACY)
Grant Likely1bc04f22012-02-14 14:06:55 -0700517 return irq_domain_legacy_revmap(domain, hwirq);
Grant Likelycc79ca62012-02-16 01:37:49 -0700518
519 /* Slow path does a linear search of the map */
520 if (hint == 0)
521 hint = 1;
522 i = hint;
523 do {
524 struct irq_data *data = irq_get_irq_data(i);
Grant Likely68700652012-02-14 14:06:53 -0700525 if (data && (data->domain == domain) && (data->hwirq == hwirq))
Grant Likelycc79ca62012-02-16 01:37:49 -0700526 return i;
527 i++;
528 if (i >= irq_virq_count)
529 i = 1;
530 } while(i != hint);
Grant Likely03848372012-02-14 14:06:52 -0700531 return 0;
Grant Likelycc79ca62012-02-16 01:37:49 -0700532}
533EXPORT_SYMBOL_GPL(irq_find_mapping);
534
535/**
536 * irq_radix_revmap_lookup() - Find a linux irq from a hw irq number.
Grant Likely68700652012-02-14 14:06:53 -0700537 * @domain: domain owning this hardware interrupt
538 * @hwirq: hardware irq number in that domain space
Grant Likelycc79ca62012-02-16 01:37:49 -0700539 *
540 * This is a fast path, for use by irq controller code that uses radix tree
541 * revmaps
542 */
Grant Likely68700652012-02-14 14:06:53 -0700543unsigned int irq_radix_revmap_lookup(struct irq_domain *domain,
Grant Likelycc79ca62012-02-16 01:37:49 -0700544 irq_hw_number_t hwirq)
545{
546 struct irq_data *irq_data;
547
Grant Likely68700652012-02-14 14:06:53 -0700548 if (WARN_ON_ONCE(domain->revmap_type != IRQ_DOMAIN_MAP_TREE))
549 return irq_find_mapping(domain, hwirq);
Grant Likelycc79ca62012-02-16 01:37:49 -0700550
551 /*
552 * Freeing an irq can delete nodes along the path to
553 * do the lookup via call_rcu.
554 */
555 rcu_read_lock();
Grant Likely68700652012-02-14 14:06:53 -0700556 irq_data = radix_tree_lookup(&domain->revmap_data.tree, hwirq);
Grant Likelycc79ca62012-02-16 01:37:49 -0700557 rcu_read_unlock();
558
559 /*
560 * If found in radix tree, then fine.
561 * Else fallback to linear lookup - this should not happen in practice
562 * as it means that we failed to insert the node in the radix tree.
563 */
Grant Likely68700652012-02-14 14:06:53 -0700564 return irq_data ? irq_data->irq : irq_find_mapping(domain, hwirq);
Grant Likelycc79ca62012-02-16 01:37:49 -0700565}
566
567/**
568 * irq_radix_revmap_insert() - Insert a hw irq to linux irq number mapping.
Grant Likely68700652012-02-14 14:06:53 -0700569 * @domain: domain owning this hardware interrupt
Grant Likelycc79ca62012-02-16 01:37:49 -0700570 * @virq: linux irq number
Grant Likely68700652012-02-14 14:06:53 -0700571 * @hwirq: hardware irq number in that domain space
Grant Likelycc79ca62012-02-16 01:37:49 -0700572 *
573 * This is for use by irq controllers that use a radix tree reverse
574 * mapping for fast lookup.
575 */
Grant Likely68700652012-02-14 14:06:53 -0700576void irq_radix_revmap_insert(struct irq_domain *domain, unsigned int virq,
Grant Likelycc79ca62012-02-16 01:37:49 -0700577 irq_hw_number_t hwirq)
578{
579 struct irq_data *irq_data = irq_get_irq_data(virq);
580
Grant Likely68700652012-02-14 14:06:53 -0700581 if (WARN_ON(domain->revmap_type != IRQ_DOMAIN_MAP_TREE))
Grant Likelycc79ca62012-02-16 01:37:49 -0700582 return;
583
Grant Likely03848372012-02-14 14:06:52 -0700584 if (virq) {
Grant Likelycc79ca62012-02-16 01:37:49 -0700585 mutex_lock(&revmap_trees_mutex);
Grant Likely68700652012-02-14 14:06:53 -0700586 radix_tree_insert(&domain->revmap_data.tree, hwirq, irq_data);
Grant Likelycc79ca62012-02-16 01:37:49 -0700587 mutex_unlock(&revmap_trees_mutex);
588 }
589}
590
591/**
592 * irq_linear_revmap() - Find a linux irq from a hw irq number.
Grant Likely68700652012-02-14 14:06:53 -0700593 * @domain: domain owning this hardware interrupt
594 * @hwirq: hardware irq number in that domain space
Grant Likelycc79ca62012-02-16 01:37:49 -0700595 *
596 * This is a fast path, for use by irq controller code that uses linear
597 * revmaps. It does fallback to the slow path if the revmap doesn't exist
598 * yet and will create the revmap entry with appropriate locking
599 */
Grant Likely68700652012-02-14 14:06:53 -0700600unsigned int irq_linear_revmap(struct irq_domain *domain,
Grant Likelycc79ca62012-02-16 01:37:49 -0700601 irq_hw_number_t hwirq)
602{
603 unsigned int *revmap;
604
Grant Likely68700652012-02-14 14:06:53 -0700605 if (WARN_ON_ONCE(domain->revmap_type != IRQ_DOMAIN_MAP_LINEAR))
606 return irq_find_mapping(domain, hwirq);
Grant Likelycc79ca62012-02-16 01:37:49 -0700607
608 /* Check revmap bounds */
Grant Likely68700652012-02-14 14:06:53 -0700609 if (unlikely(hwirq >= domain->revmap_data.linear.size))
610 return irq_find_mapping(domain, hwirq);
Grant Likelycc79ca62012-02-16 01:37:49 -0700611
612 /* Check if revmap was allocated */
Grant Likely68700652012-02-14 14:06:53 -0700613 revmap = domain->revmap_data.linear.revmap;
Grant Likelycc79ca62012-02-16 01:37:49 -0700614 if (unlikely(revmap == NULL))
Grant Likely68700652012-02-14 14:06:53 -0700615 return irq_find_mapping(domain, hwirq);
Grant Likelycc79ca62012-02-16 01:37:49 -0700616
617 /* Fill up revmap with slow path if no mapping found */
Grant Likely03848372012-02-14 14:06:52 -0700618 if (unlikely(!revmap[hwirq]))
Grant Likely68700652012-02-14 14:06:53 -0700619 revmap[hwirq] = irq_find_mapping(domain, hwirq);
Grant Likelycc79ca62012-02-16 01:37:49 -0700620
621 return revmap[hwirq];
622}
623
624#ifdef CONFIG_VIRQ_DEBUG
625static int virq_debug_show(struct seq_file *m, void *private)
626{
627 unsigned long flags;
628 struct irq_desc *desc;
629 const char *p;
630 static const char none[] = "none";
631 void *data;
632 int i;
633
634 seq_printf(m, "%-5s %-7s %-15s %-18s %s\n", "virq", "hwirq",
Grant Likely68700652012-02-14 14:06:53 -0700635 "chip name", "chip data", "domain name");
Grant Likelycc79ca62012-02-16 01:37:49 -0700636
637 for (i = 1; i < nr_irqs; i++) {
638 desc = irq_to_desc(i);
639 if (!desc)
640 continue;
641
642 raw_spin_lock_irqsave(&desc->lock, flags);
643
644 if (desc->action && desc->action->handler) {
645 struct irq_chip *chip;
646
647 seq_printf(m, "%5d ", i);
648 seq_printf(m, "0x%05lx ", desc->irq_data.hwirq);
649
650 chip = irq_desc_get_chip(desc);
651 if (chip && chip->name)
652 p = chip->name;
653 else
654 p = none;
655 seq_printf(m, "%-15s ", p);
656
657 data = irq_desc_get_chip_data(desc);
658 seq_printf(m, "0x%16p ", data);
659
660 if (desc->irq_data.domain->of_node)
661 p = desc->irq_data.domain->of_node->full_name;
662 else
663 p = none;
664 seq_printf(m, "%s\n", p);
665 }
666
667 raw_spin_unlock_irqrestore(&desc->lock, flags);
668 }
669
670 return 0;
671}
672
673static int virq_debug_open(struct inode *inode, struct file *file)
674{
675 return single_open(file, virq_debug_show, inode->i_private);
676}
677
678static const struct file_operations virq_debug_fops = {
679 .open = virq_debug_open,
680 .read = seq_read,
681 .llseek = seq_lseek,
682 .release = single_release,
683};
684
685static int __init irq_debugfs_init(void)
686{
687 if (debugfs_create_file("virq_mapping", S_IRUGO, powerpc_debugfs_root,
688 NULL, &virq_debug_fops) == NULL)
689 return -ENOMEM;
690
691 return 0;
692}
693__initcall(irq_debugfs_init);
694#endif /* CONFIG_VIRQ_DEBUG */
695
Grant Likely75294952012-02-14 14:06:57 -0700696int irq_domain_simple_map(struct irq_domain *d, unsigned int irq,
697 irq_hw_number_t hwirq)
Grant Likely08a543a2011-07-26 03:19:06 -0600698{
Grant Likely75294952012-02-14 14:06:57 -0700699 return 0;
Grant Likely08a543a2011-07-26 03:19:06 -0600700}
701
Grant Likely7bb69ba2012-02-14 14:06:48 -0700702int irq_domain_simple_xlate(struct irq_domain *d,
Grant Likely7e713302011-07-26 03:19:06 -0600703 struct device_node *controller,
704 const u32 *intspec, unsigned int intsize,
705 unsigned long *out_hwirq, unsigned int *out_type)
706{
707 if (d->of_node != controller)
708 return -EINVAL;
709 if (intsize < 1)
710 return -EINVAL;
Grant Likely7e713302011-07-26 03:19:06 -0600711 *out_hwirq = intspec[0];
712 *out_type = IRQ_TYPE_NONE;
713 if (intsize > 1)
714 *out_type = intspec[1] & IRQ_TYPE_SENSE_MASK;
715 return 0;
716}
717
Grant Likely75294952012-02-14 14:06:57 -0700718struct irq_domain_ops irq_domain_simple_ops = {
719 .map = irq_domain_simple_map,
720 .xlate = irq_domain_simple_xlate,
721};
722EXPORT_SYMBOL_GPL(irq_domain_simple_ops);
723
724#ifdef CONFIG_OF_IRQ
Grant Likely7e713302011-07-26 03:19:06 -0600725void irq_domain_generate_simple(const struct of_device_id *match,
726 u64 phys_base, unsigned int irq_start)
727{
728 struct device_node *node;
Grant Likelye1964c52012-02-14 14:06:48 -0700729 pr_debug("looking for phys_base=%llx, irq_start=%i\n",
Grant Likely7e713302011-07-26 03:19:06 -0600730 (unsigned long long) phys_base, (int) irq_start);
731 node = of_find_matching_node_by_address(NULL, match, phys_base);
732 if (node)
Grant Likely6b783f72012-01-10 17:09:30 -0700733 irq_domain_add_legacy(node, 32, irq_start, 0,
734 &irq_domain_simple_ops, NULL);
Grant Likely7e713302011-07-26 03:19:06 -0600735}
736EXPORT_SYMBOL_GPL(irq_domain_generate_simple);
Grant Likely75294952012-02-14 14:06:57 -0700737#endif