irqdomain.c 22.4 KB
Newer Older
1 2 3
#include <linux/debugfs.h>
#include <linux/hardirq.h>
#include <linux/interrupt.h>
4
#include <linux/irq.h>
5
#include <linux/irqdesc.h>
6 7 8 9
#include <linux/irqdomain.h>
#include <linux/module.h>
#include <linux/mutex.h>
#include <linux/of.h>
10
#include <linux/of_address.h>
11
#include <linux/seq_file.h>
12
#include <linux/slab.h>
13 14
#include <linux/smp.h>
#include <linux/fs.h>
15

16 17 18 19 20
#define IRQ_DOMAIN_MAP_LEGACY 0 /* legacy 8259, gets irqs 1..15 */
#define IRQ_DOMAIN_MAP_NOMAP 1 /* no fast reverse mapping */
#define IRQ_DOMAIN_MAP_LINEAR 2 /* linear map of interrupts */
#define IRQ_DOMAIN_MAP_TREE 3 /* radix tree */

21 22 23
static LIST_HEAD(irq_domain_list);
static DEFINE_MUTEX(irq_domain_mutex);

24 25 26
#ifdef CONFIG_PPC
static DEFINE_MUTEX(revmap_trees_mutex);
static unsigned int irq_virq_count = NR_IRQS;
27
static struct irq_domain *irq_default_domain;
28

29
static int default_irq_domain_match(struct irq_domain *d, struct device_node *np)
30
{
31
	return d->of_node != NULL && d->of_node == np;
32 33 34
}

/**
35
 * irq_domain_alloc() - Allocate a new irq_domain data structure
36 37
 * @of_node: optional device-tree node of the interrupt controller
 * @revmap_type: type of reverse mapping to use
38
 * @ops: map/unmap domain callbacks
39
 * @host_data: Controller private data pointer
40
 *
41 42 43
 * Allocates and initialize and irq_domain structure.  Caller is expected to
 * register allocated irq_domain with irq_domain_register().  Returns pointer
 * to IRQ domain, or NULL on failure.
44
 */
45 46 47 48
static struct irq_domain *irq_domain_alloc(struct device_node *of_node,
					   unsigned int revmap_type,
					   struct irq_domain_ops *ops,
					   void *host_data)
49
{
50
	struct irq_domain *domain;
51

52 53
	domain = kzalloc(sizeof(*domain), GFP_KERNEL);
	if (WARN_ON(!domain))
54 55 56
		return NULL;

	/* Fill structure */
57 58
	domain->revmap_type = revmap_type;
	domain->ops = ops;
59
	domain->host_data = host_data;
60
	domain->of_node = of_node_get(of_node);
61

62 63
	if (domain->ops->match == NULL)
		domain->ops->match = default_irq_domain_match;
64

65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97
	return domain;
}

static void irq_domain_add(struct irq_domain *domain)
{
	mutex_lock(&irq_domain_mutex);
	list_add(&domain->link, &irq_domain_list);
	mutex_unlock(&irq_domain_mutex);
	pr_debug("irq: Allocated domain of type %d @0x%p\n",
		 domain->revmap_type, domain);
}

/**
 * irq_domain_add_legacy() - Allocate and register a legacy revmap irq_domain.
 * @of_node: pointer to interrupt controller's device tree node.
 * @ops: map/unmap domain callbacks
 * @host_data: Controller private data pointer
 *
 * Note: the map() callback will be called before this function returns
 * for all legacy interrupts except 0 (which is always the invalid irq for
 * a legacy controller).
 */
struct irq_domain *irq_domain_add_legacy(struct device_node *of_node,
					 struct irq_domain_ops *ops,
					 void *host_data)
{
	struct irq_domain *domain, *h;
	unsigned int i;

	domain = irq_domain_alloc(of_node, IRQ_DOMAIN_MAP_LEGACY, ops, host_data);
	if (!domain)
		return NULL;

98 99
	mutex_lock(&irq_domain_mutex);
	/* Make sure only one legacy controller can be created */
100 101 102 103 104 105
	list_for_each_entry(h, &irq_domain_list, link) {
		if (WARN_ON(h->revmap_type == IRQ_DOMAIN_MAP_LEGACY)) {
			mutex_unlock(&irq_domain_mutex);
			of_node_put(domain->of_node);
			kfree(domain);
			return NULL;
106 107
		}
	}
108
	list_add(&domain->link, &irq_domain_list);
109 110
	mutex_unlock(&irq_domain_mutex);

111 112 113 114 115 116 117 118 119 120 121 122 123 124
	/* setup us as the domain for all legacy interrupts */
	for (i = 1; i < NUM_ISA_INTERRUPTS; i++) {
		struct irq_data *irq_data = irq_get_irq_data(i);
		irq_data->hwirq = i;
		irq_data->domain = domain;

		/* Legacy flags are left to default at this point,
		 * one can then use irq_create_mapping() to
		 * explicitly change them
		 */
		ops->map(domain, i, i);

		/* Clear norequest flags */
		irq_clear_status_flags(i, IRQ_NOREQUEST);
125
	}
126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141
	return domain;
}

/**
 * irq_domain_add_linear() - Allocate and register a legacy revmap irq_domain.
 * @of_node: pointer to interrupt controller's device tree node.
 * @ops: map/unmap domain callbacks
 * @host_data: Controller private data pointer
 */
struct irq_domain *irq_domain_add_linear(struct device_node *of_node,
					 unsigned int size,
					 struct irq_domain_ops *ops,
					 void *host_data)
{
	struct irq_domain *domain;
	unsigned int *revmap;
142

143 144 145
	revmap = kzalloc(sizeof(*revmap) * size, GFP_KERNEL);
	if (WARN_ON(!revmap))
		return NULL;
146

147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186
	domain = irq_domain_alloc(of_node, IRQ_DOMAIN_MAP_LINEAR, ops, host_data);
	if (!domain) {
		kfree(revmap);
		return NULL;
	}
	domain->revmap_data.linear.size = size;
	domain->revmap_data.linear.revmap = revmap;
	irq_domain_add(domain);
	return domain;
}

struct irq_domain *irq_domain_add_nomap(struct device_node *of_node,
					 struct irq_domain_ops *ops,
					 void *host_data)
{
	struct irq_domain *domain = irq_domain_alloc(of_node,
					IRQ_DOMAIN_MAP_NOMAP, ops, host_data);
	if (domain)
		irq_domain_add(domain);
	return domain;
}

/**
 * irq_domain_add_tree()
 * @of_node: pointer to interrupt controller's device tree node.
 * @ops: map/unmap domain callbacks
 *
 * Note: The radix tree will be allocated later during boot automatically
 * (the reverse mapping will use the slow path until that happens).
 */
struct irq_domain *irq_domain_add_tree(struct device_node *of_node,
					 struct irq_domain_ops *ops,
					 void *host_data)
{
	struct irq_domain *domain = irq_domain_alloc(of_node,
					IRQ_DOMAIN_MAP_TREE, ops, host_data);
	if (domain) {
		INIT_RADIX_TREE(&domain->revmap_data.tree, GFP_KERNEL);
		irq_domain_add(domain);
	}
187
	return domain;
188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215
}

/**
 * irq_find_host() - Locates a domain for a given device node
 * @node: device-tree node of the interrupt controller
 */
struct irq_domain *irq_find_host(struct device_node *node)
{
	struct irq_domain *h, *found = NULL;

	/* 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...
	 */
	mutex_lock(&irq_domain_mutex);
	list_for_each_entry(h, &irq_domain_list, link)
		if (h->ops->match(h, node)) {
			found = h;
			break;
		}
	mutex_unlock(&irq_domain_mutex);
	return found;
}
EXPORT_SYMBOL_GPL(irq_find_host);

/**
 * irq_set_default_host() - Set a "default" irq domain
216
 * @domain: default domain pointer
217 218 219 220 221 222
 *
 * For convenience, it's possible to set a "default" domain that will be used
 * whenever NULL is passed to irq_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.
 */
223
void irq_set_default_host(struct irq_domain *domain)
224
{
225
	pr_debug("irq: Default domain set to @0x%p\n", domain);
226

227
	irq_default_domain = domain;
228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245
}

/**
 * irq_set_virq_count() - Set the maximum number of linux irqs
 * @count: number of linux irqs, capped with NR_IRQS
 *
 * This is mainly for use by platforms like iSeries who want to program
 * the virtual irq number in the controller to avoid the reverse mapping
 */
void irq_set_virq_count(unsigned int count)
{
	pr_debug("irq: Trying to set virq count to %d\n", count);

	BUG_ON(count < NUM_ISA_INTERRUPTS);
	if (count < NR_IRQS)
		irq_virq_count = count;
}

246
static int irq_setup_virq(struct irq_domain *domain, unsigned int virq,
247 248 249 250 251
			    irq_hw_number_t hwirq)
{
	struct irq_data *irq_data = irq_get_irq_data(virq);

	irq_data->hwirq = hwirq;
252 253
	irq_data->domain = domain;
	if (domain->ops->map(domain, virq, hwirq)) {
254 255 256 257 258 259 260 261 262 263 264 265 266
		pr_debug("irq: -> mapping failed, freeing\n");
		irq_data->domain = NULL;
		irq_data->hwirq = 0;
		return -1;
	}

	irq_clear_status_flags(virq, IRQ_NOREQUEST);

	return 0;
}

/**
 * irq_create_direct_mapping() - Allocate an irq for direct mapping
267
 * @domain: domain to allocate the irq for or NULL for default domain
268 269 270 271 272
 *
 * This routine is used for irq controllers which can choose the hardware
 * interrupt numbers they generate. In such a case it's simplest to use
 * the linux irq as the hardware interrupt number.
 */
273
unsigned int irq_create_direct_mapping(struct irq_domain *domain)
274 275 276
{
	unsigned int virq;

277 278
	if (domain == NULL)
		domain = irq_default_domain;
279

280 281
	BUG_ON(domain == NULL);
	WARN_ON(domain->revmap_type != IRQ_DOMAIN_MAP_NOMAP);
282 283

	virq = irq_alloc_desc_from(1, 0);
284
	if (!virq) {
285
		pr_debug("irq: create_direct virq allocation failed\n");
286
		return 0;
287 288 289 290 291 292 293 294 295 296
	}
	if (virq >= irq_virq_count) {
		pr_err("ERROR: no free irqs available below %i maximum\n",
			irq_virq_count);
		irq_free_desc(virq);
		return 0;
	}

	pr_debug("irq: create_direct obtained virq %d\n", virq);

297
	if (irq_setup_virq(domain, virq, virq)) {
298
		irq_free_desc(virq);
299
		return 0;
300 301 302 303 304 305 306
	}

	return virq;
}

/**
 * irq_create_mapping() - Map a hardware interrupt into linux irq space
307 308
 * @domain: domain owning this hardware interrupt or NULL for default domain
 * @hwirq: hardware irq number in that domain space
309 310 311 312 313 314
 *
 * Only one mapping per hardware interrupt is permitted. Returns a linux
 * irq number.
 * If the sense/trigger is to be specified, set_irq_type() should be called
 * on the number returned from that call.
 */
315
unsigned int irq_create_mapping(struct irq_domain *domain,
316 317 318 319
				irq_hw_number_t hwirq)
{
	unsigned int virq, hint;

320
	pr_debug("irq: irq_create_mapping(0x%p, 0x%lx)\n", domain, hwirq);
321

322 323 324 325
	/* Look for default domain if nececssary */
	if (domain == NULL)
		domain = irq_default_domain;
	if (domain == NULL) {
326
		printk(KERN_WARNING "irq_create_mapping called for"
327
		       " NULL domain, hwirq=%lx\n", hwirq);
328
		WARN_ON(1);
329
		return 0;
330
	}
331
	pr_debug("irq: -> using domain @%p\n", domain);
332 333

	/* Check if mapping already exists */
334
	virq = irq_find_mapping(domain, hwirq);
335
	if (virq) {
336 337 338 339 340
		pr_debug("irq: -> existing mapping on virq %d\n", virq);
		return virq;
	}

	/* Get a virtual interrupt number */
341
	if (domain->revmap_type == IRQ_DOMAIN_MAP_LEGACY) {
342 343 344
		/* Handle legacy */
		virq = (unsigned int)hwirq;
		if (virq == 0 || virq >= NUM_ISA_INTERRUPTS)
345
			return 0;
346 347 348 349 350 351 352 353 354
		return virq;
	} else {
		/* Allocate a virtual interrupt number */
		hint = hwirq % irq_virq_count;
		if (hint == 0)
			hint++;
		virq = irq_alloc_desc_from(hint, 0);
		if (!virq)
			virq = irq_alloc_desc_from(1, 0);
355
		if (!virq) {
356
			pr_debug("irq: -> virq allocation failed\n");
357
			return 0;
358 359 360
		}
	}

361 362
	if (irq_setup_virq(domain, virq, hwirq)) {
		if (domain->revmap_type != IRQ_DOMAIN_MAP_LEGACY)
363
			irq_free_desc(virq);
364
		return 0;
365 366
	}

367 368
	pr_debug("irq: irq %lu on domain %s mapped to virtual irq %u\n",
		hwirq, domain->of_node ? domain->of_node->full_name : "null", virq);
369 370 371 372 373 374 375 376

	return virq;
}
EXPORT_SYMBOL_GPL(irq_create_mapping);

unsigned int irq_create_of_mapping(struct device_node *controller,
				   const u32 *intspec, unsigned int intsize)
{
377
	struct irq_domain *domain;
378 379 380 381
	irq_hw_number_t hwirq;
	unsigned int type = IRQ_TYPE_NONE;
	unsigned int virq;

382 383 384
	domain = controller ? irq_find_host(controller) : irq_default_domain;
	if (!domain) {
		printk(KERN_WARNING "irq: no irq domain found for %s !\n",
385
		       controller->full_name);
386
		return 0;
387 388
	}

389 390
	/* If domain has no translation, then we assume interrupt line */
	if (domain->ops->xlate == NULL)
391 392
		hwirq = intspec[0];
	else {
393
		if (domain->ops->xlate(domain, controller, intspec, intsize,
394
				     &hwirq, &type))
395
			return 0;
396 397 398
	}

	/* Create mapping */
399
	virq = irq_create_mapping(domain, hwirq);
400
	if (!virq)
401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417
		return virq;

	/* Set type if specified and different than the current one */
	if (type != IRQ_TYPE_NONE &&
	    type != (irqd_get_trigger_type(irq_get_irq_data(virq))))
		irq_set_irq_type(virq, type);
	return virq;
}
EXPORT_SYMBOL_GPL(irq_create_of_mapping);

/**
 * irq_dispose_mapping() - Unmap an interrupt
 * @virq: linux irq number of the interrupt to unmap
 */
void irq_dispose_mapping(unsigned int virq)
{
	struct irq_data *irq_data = irq_get_irq_data(virq);
418
	struct irq_domain *domain;
419 420
	irq_hw_number_t hwirq;

421
	if (!virq || !irq_data)
422 423
		return;

424 425
	domain = irq_data->domain;
	if (WARN_ON(domain == NULL))
426 427 428
		return;

	/* Never unmap legacy interrupts */
429
	if (domain->revmap_type == IRQ_DOMAIN_MAP_LEGACY)
430 431 432 433 434 435 436 437 438 439 440
		return;

	irq_set_status_flags(virq, IRQ_NOREQUEST);

	/* remove chip and handler */
	irq_set_chip_and_handler(virq, NULL, NULL);

	/* Make sure it's completed */
	synchronize_irq(virq);

	/* Tell the PIC about it */
441 442
	if (domain->ops->unmap)
		domain->ops->unmap(domain, virq);
443 444 445 446
	smp_mb();

	/* Clear reverse map */
	hwirq = irq_data->hwirq;
447
	switch(domain->revmap_type) {
448
	case IRQ_DOMAIN_MAP_LINEAR:
449 450
		if (hwirq < domain->revmap_data.linear.size)
			domain->revmap_data.linear.revmap[hwirq] = 0;
451 452 453
		break;
	case IRQ_DOMAIN_MAP_TREE:
		mutex_lock(&revmap_trees_mutex);
454
		radix_tree_delete(&domain->revmap_data.tree, hwirq);
455 456 457 458 459 460 461 462 463 464
		mutex_unlock(&revmap_trees_mutex);
		break;
	}

	irq_free_desc(virq);
}
EXPORT_SYMBOL_GPL(irq_dispose_mapping);

/**
 * irq_find_mapping() - Find a linux irq from an hw irq number.
465 466
 * @domain: domain owning this hardware interrupt
 * @hwirq: hardware irq number in that domain space
467 468 469 470 471
 *
 * This is a slow path, for use by generic code. It's expected that an
 * irq controller implementation directly calls the appropriate low level
 * mapping function.
 */
472
unsigned int irq_find_mapping(struct irq_domain *domain,
473 474 475 476 477
			      irq_hw_number_t hwirq)
{
	unsigned int i;
	unsigned int hint = hwirq % irq_virq_count;

478 479 480 481
	/* Look for default domain if nececssary */
	if (domain == NULL)
		domain = irq_default_domain;
	if (domain == NULL)
482
		return 0;
483 484

	/* legacy -> bail early */
485
	if (domain->revmap_type == IRQ_DOMAIN_MAP_LEGACY)
486 487 488 489 490 491 492 493
		return hwirq;

	/* Slow path does a linear search of the map */
	if (hint == 0)
		hint = 1;
	i = hint;
	do {
		struct irq_data *data = irq_get_irq_data(i);
494
		if (data && (data->domain == domain) && (data->hwirq == hwirq))
495 496 497 498 499
			return i;
		i++;
		if (i >= irq_virq_count)
			i = 1;
	} while(i != hint);
500
	return 0;
501 502 503 504 505
}
EXPORT_SYMBOL_GPL(irq_find_mapping);

/**
 * irq_radix_revmap_lookup() - Find a linux irq from a hw irq number.
506 507
 * @domain: domain owning this hardware interrupt
 * @hwirq: hardware irq number in that domain space
508 509 510 511
 *
 * This is a fast path, for use by irq controller code that uses radix tree
 * revmaps
 */
512
unsigned int irq_radix_revmap_lookup(struct irq_domain *domain,
513 514 515 516
				     irq_hw_number_t hwirq)
{
	struct irq_data *irq_data;

517 518
	if (WARN_ON_ONCE(domain->revmap_type != IRQ_DOMAIN_MAP_TREE))
		return irq_find_mapping(domain, hwirq);
519 520 521 522 523 524

	/*
	 * Freeing an irq can delete nodes along the path to
	 * do the lookup via call_rcu.
	 */
	rcu_read_lock();
525
	irq_data = radix_tree_lookup(&domain->revmap_data.tree, hwirq);
526 527 528 529 530 531 532
	rcu_read_unlock();

	/*
	 * If found in radix tree, then fine.
	 * Else fallback to linear lookup - this should not happen in practice
	 * as it means that we failed to insert the node in the radix tree.
	 */
533
	return irq_data ? irq_data->irq : irq_find_mapping(domain, hwirq);
534 535 536 537
}

/**
 * irq_radix_revmap_insert() - Insert a hw irq to linux irq number mapping.
538
 * @domain: domain owning this hardware interrupt
539
 * @virq: linux irq number
540
 * @hwirq: hardware irq number in that domain space
541 542 543 544
 *
 * This is for use by irq controllers that use a radix tree reverse
 * mapping for fast lookup.
 */
545
void irq_radix_revmap_insert(struct irq_domain *domain, unsigned int virq,
546 547 548 549
			     irq_hw_number_t hwirq)
{
	struct irq_data *irq_data = irq_get_irq_data(virq);

550
	if (WARN_ON(domain->revmap_type != IRQ_DOMAIN_MAP_TREE))
551 552
		return;

553
	if (virq) {
554
		mutex_lock(&revmap_trees_mutex);
555
		radix_tree_insert(&domain->revmap_data.tree, hwirq, irq_data);
556 557 558 559 560 561
		mutex_unlock(&revmap_trees_mutex);
	}
}

/**
 * irq_linear_revmap() - Find a linux irq from a hw irq number.
562 563
 * @domain: domain owning this hardware interrupt
 * @hwirq: hardware irq number in that domain space
564 565 566 567 568
 *
 * This is a fast path, for use by irq controller code that uses linear
 * revmaps. It does fallback to the slow path if the revmap doesn't exist
 * yet and will create the revmap entry with appropriate locking
 */
569
unsigned int irq_linear_revmap(struct irq_domain *domain,
570 571 572 573
			       irq_hw_number_t hwirq)
{
	unsigned int *revmap;

574 575
	if (WARN_ON_ONCE(domain->revmap_type != IRQ_DOMAIN_MAP_LINEAR))
		return irq_find_mapping(domain, hwirq);
576 577

	/* Check revmap bounds */
578 579
	if (unlikely(hwirq >= domain->revmap_data.linear.size))
		return irq_find_mapping(domain, hwirq);
580 581

	/* Check if revmap was allocated */
582
	revmap = domain->revmap_data.linear.revmap;
583
	if (unlikely(revmap == NULL))
584
		return irq_find_mapping(domain, hwirq);
585 586

	/* Fill up revmap with slow path if no mapping found */
587
	if (unlikely(!revmap[hwirq]))
588
		revmap[hwirq] = irq_find_mapping(domain, hwirq);
589 590 591 592 593 594 595 596 597 598 599 600 601 602 603

	return revmap[hwirq];
}

#ifdef CONFIG_VIRQ_DEBUG
static int virq_debug_show(struct seq_file *m, void *private)
{
	unsigned long flags;
	struct irq_desc *desc;
	const char *p;
	static const char none[] = "none";
	void *data;
	int i;

	seq_printf(m, "%-5s  %-7s  %-15s  %-18s  %s\n", "virq", "hwirq",
604
		      "chip name", "chip data", "domain name");
605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666

	for (i = 1; i < nr_irqs; i++) {
		desc = irq_to_desc(i);
		if (!desc)
			continue;

		raw_spin_lock_irqsave(&desc->lock, flags);

		if (desc->action && desc->action->handler) {
			struct irq_chip *chip;

			seq_printf(m, "%5d  ", i);
			seq_printf(m, "0x%05lx  ", desc->irq_data.hwirq);

			chip = irq_desc_get_chip(desc);
			if (chip && chip->name)
				p = chip->name;
			else
				p = none;
			seq_printf(m, "%-15s  ", p);

			data = irq_desc_get_chip_data(desc);
			seq_printf(m, "0x%16p  ", data);

			if (desc->irq_data.domain->of_node)
				p = desc->irq_data.domain->of_node->full_name;
			else
				p = none;
			seq_printf(m, "%s\n", p);
		}

		raw_spin_unlock_irqrestore(&desc->lock, flags);
	}

	return 0;
}

static int virq_debug_open(struct inode *inode, struct file *file)
{
	return single_open(file, virq_debug_show, inode->i_private);
}

static const struct file_operations virq_debug_fops = {
	.open = virq_debug_open,
	.read = seq_read,
	.llseek = seq_lseek,
	.release = single_release,
};

static int __init irq_debugfs_init(void)
{
	if (debugfs_create_file("virq_mapping", S_IRUGO, powerpc_debugfs_root,
				 NULL, &virq_debug_fops) == NULL)
		return -ENOMEM;

	return 0;
}
__initcall(irq_debugfs_init);
#endif /* CONFIG_VIRQ_DEBUG */

#else /* CONFIG_PPC */

667 668 669 670 671 672 673 674 675 676 677
/**
 * irq_domain_add() - Register an irq_domain
 * @domain: ptr to initialized irq_domain structure
 *
 * Registers an irq_domain structure.  The irq_domain must at a minimum be
 * initialized with an ops structure pointer, and either a ->to_irq hook or
 * a valid irq_base value.  Everything else is optional.
 */
void irq_domain_add(struct irq_domain *domain)
{
	struct irq_data *d;
678
	int hwirq, irq;
679 680 681 682 683 684

	/*
	 * This assumes that the irq_domain owner has already allocated
	 * the irq_descs.  This block will be removed when support for dynamic
	 * allocation of irq_descs is added to irq_domain.
	 */
685 686
	irq_domain_for_each_irq(domain, hwirq, irq) {
		d = irq_get_irq_data(irq);
687 688 689 690 691
		if (!d) {
			WARN(1, "error: assigning domain to non existant irq_desc");
			return;
		}
		if (d->domain) {
692 693 694 695 696 697 698 699 700
			/* things are broken; just report, don't clean up */
			WARN(1, "error: irq_desc already assigned to a domain");
			return;
		}
		d->domain = domain;
		d->hwirq = hwirq;
	}

	mutex_lock(&irq_domain_mutex);
701
	list_add(&domain->link, &irq_domain_list);
702 703 704 705 706 707 708 709 710 711
	mutex_unlock(&irq_domain_mutex);
}

/**
 * irq_domain_del() - Unregister an irq_domain
 * @domain: ptr to registered irq_domain.
 */
void irq_domain_del(struct irq_domain *domain)
{
	struct irq_data *d;
712
	int hwirq, irq;
713 714

	mutex_lock(&irq_domain_mutex);
715
	list_del(&domain->link);
716 717 718
	mutex_unlock(&irq_domain_mutex);

	/* Clear the irq_domain assignments */
719 720
	irq_domain_for_each_irq(domain, hwirq, irq) {
		d = irq_get_irq_data(irq);
721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745
		d->domain = NULL;
	}
}

#if defined(CONFIG_OF_IRQ)
/**
 * irq_create_of_mapping() - Map a linux irq number from a DT interrupt spec
 *
 * Used by the device tree interrupt mapping code to translate a device tree
 * interrupt specifier to a valid linux irq number.  Returns either a valid
 * linux IRQ number or 0.
 *
 * When the caller no longer need the irq number returned by this function it
 * should arrange to call irq_dispose_mapping().
 */
unsigned int irq_create_of_mapping(struct device_node *controller,
				   const u32 *intspec, unsigned int intsize)
{
	struct irq_domain *domain;
	unsigned long hwirq;
	unsigned int irq, type;
	int rc = -EINVAL;

	/* Find a domain which can translate the irq spec */
	mutex_lock(&irq_domain_mutex);
746 747
	list_for_each_entry(domain, &irq_domain_list, link) {
		if (!domain->ops->xlate)
748
			continue;
749
		rc = domain->ops->xlate(domain, controller,
750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782
					intspec, intsize, &hwirq, &type);
		if (rc == 0)
			break;
	}
	mutex_unlock(&irq_domain_mutex);

	if (rc != 0)
		return 0;

	irq = irq_domain_to_irq(domain, hwirq);
	if (type != IRQ_TYPE_NONE)
		irq_set_irq_type(irq, type);
	pr_debug("%s: mapped hwirq=%i to irq=%i, flags=%x\n",
		 controller->full_name, (int)hwirq, irq, type);
	return irq;
}
EXPORT_SYMBOL_GPL(irq_create_of_mapping);

/**
 * irq_dispose_mapping() - Discard a mapping created by irq_create_of_mapping()
 * @irq: linux irq number to be discarded
 *
 * Calling this function indicates the caller no longer needs a reference to
 * the linux irq number returned by a prior call to irq_create_of_mapping().
 */
void irq_dispose_mapping(unsigned int irq)
{
	/*
	 * nothing yet; will be filled when support for dynamic allocation of
	 * irq_descs is added to irq_domain
	 */
}
EXPORT_SYMBOL_GPL(irq_dispose_mapping);
783

784
int irq_domain_simple_xlate(struct irq_domain *d,
785 786 787 788 789 790 791 792
			    struct device_node *controller,
			    const u32 *intspec, unsigned int intsize,
			    unsigned long *out_hwirq, unsigned int *out_type)
{
	if (d->of_node != controller)
		return -EINVAL;
	if (intsize < 1)
		return -EINVAL;
793 794 795
	if (d->nr_irq && ((intspec[0] < d->hwirq_base) ||
	    (intspec[0] >= d->hwirq_base + d->nr_irq)))
		return -EINVAL;
796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827

	*out_hwirq = intspec[0];
	*out_type = IRQ_TYPE_NONE;
	if (intsize > 1)
		*out_type = intspec[1] & IRQ_TYPE_SENSE_MASK;
	return 0;
}

/**
 * irq_domain_create_simple() - Set up a 'simple' translation range
 */
void irq_domain_add_simple(struct device_node *controller, int irq_base)
{
	struct irq_domain *domain;

	domain = kzalloc(sizeof(*domain), GFP_KERNEL);
	if (!domain) {
		WARN_ON(1);
		return;
	}

	domain->irq_base = irq_base;
	domain->of_node = of_node_get(controller);
	domain->ops = &irq_domain_simple_ops;
	irq_domain_add(domain);
}
EXPORT_SYMBOL_GPL(irq_domain_add_simple);

void irq_domain_generate_simple(const struct of_device_id *match,
				u64 phys_base, unsigned int irq_start)
{
	struct device_node *node;
G
Grant Likely 已提交
828
	pr_debug("looking for phys_base=%llx, irq_start=%i\n",
829 830 831 832 833 834
		(unsigned long long) phys_base, (int) irq_start);
	node = of_find_matching_node_by_address(NULL, match, phys_base);
	if (node)
		irq_domain_add_simple(node, irq_start);
}
EXPORT_SYMBOL_GPL(irq_domain_generate_simple);
835
#endif /* CONFIG_OF_IRQ */
836 837 838

struct irq_domain_ops irq_domain_simple_ops = {
#ifdef CONFIG_OF_IRQ
839
	.xlate = irq_domain_simple_xlate,
840 841 842
#endif /* CONFIG_OF_IRQ */
};
EXPORT_SYMBOL_GPL(irq_domain_simple_ops);
843 844

#endif /* !CONFIG_PPC */