| Grant Likely | 08a543a | 2011-07-26 03:19:06 -0600 | [diff] [blame] | 1 | #include <linux/irq.h> | 
 | 2 | #include <linux/irqdomain.h> | 
 | 3 | #include <linux/module.h> | 
 | 4 | #include <linux/mutex.h> | 
 | 5 | #include <linux/of.h> | 
| Grant Likely | 7e71330 | 2011-07-26 03:19:06 -0600 | [diff] [blame] | 6 | #include <linux/of_address.h> | 
 | 7 | #include <linux/slab.h> | 
| Grant Likely | 08a543a | 2011-07-26 03:19:06 -0600 | [diff] [blame] | 8 |  | 
 | 9 | static LIST_HEAD(irq_domain_list); | 
 | 10 | static DEFINE_MUTEX(irq_domain_mutex); | 
 | 11 |  | 
 | 12 | /** | 
 | 13 |  * irq_domain_add() - Register an irq_domain | 
 | 14 |  * @domain: ptr to initialized irq_domain structure | 
 | 15 |  * | 
 | 16 |  * Registers an irq_domain structure.  The irq_domain must at a minimum be | 
 | 17 |  * initialized with an ops structure pointer, and either a ->to_irq hook or | 
 | 18 |  * a valid irq_base value.  Everything else is optional. | 
 | 19 |  */ | 
 | 20 | void irq_domain_add(struct irq_domain *domain) | 
 | 21 | { | 
 | 22 | 	struct irq_data *d; | 
| Rob Herring | 6d27430 | 2011-09-30 10:48:38 -0500 | [diff] [blame] | 23 | 	int hwirq, irq; | 
| Grant Likely | 08a543a | 2011-07-26 03:19:06 -0600 | [diff] [blame] | 24 |  | 
 | 25 | 	/* | 
 | 26 | 	 * This assumes that the irq_domain owner has already allocated | 
 | 27 | 	 * the irq_descs.  This block will be removed when support for dynamic | 
 | 28 | 	 * allocation of irq_descs is added to irq_domain. | 
 | 29 | 	 */ | 
| Rob Herring | 6d27430 | 2011-09-30 10:48:38 -0500 | [diff] [blame] | 30 | 	irq_domain_for_each_irq(domain, hwirq, irq) { | 
 | 31 | 		d = irq_get_irq_data(irq); | 
| Rob Herring | eef24af | 2011-09-14 11:31:37 -0500 | [diff] [blame] | 32 | 		if (!d) { | 
 | 33 | 			WARN(1, "error: assigning domain to non existant irq_desc"); | 
 | 34 | 			return; | 
 | 35 | 		} | 
 | 36 | 		if (d->domain) { | 
| Grant Likely | 08a543a | 2011-07-26 03:19:06 -0600 | [diff] [blame] | 37 | 			/* things are broken; just report, don't clean up */ | 
 | 38 | 			WARN(1, "error: irq_desc already assigned to a domain"); | 
 | 39 | 			return; | 
 | 40 | 		} | 
 | 41 | 		d->domain = domain; | 
 | 42 | 		d->hwirq = hwirq; | 
 | 43 | 	} | 
 | 44 |  | 
 | 45 | 	mutex_lock(&irq_domain_mutex); | 
 | 46 | 	list_add(&domain->list, &irq_domain_list); | 
 | 47 | 	mutex_unlock(&irq_domain_mutex); | 
 | 48 | } | 
 | 49 |  | 
 | 50 | /** | 
 | 51 |  * irq_domain_del() - Unregister an irq_domain | 
 | 52 |  * @domain: ptr to registered irq_domain. | 
 | 53 |  */ | 
 | 54 | void irq_domain_del(struct irq_domain *domain) | 
 | 55 | { | 
 | 56 | 	struct irq_data *d; | 
| Rob Herring | 6d27430 | 2011-09-30 10:48:38 -0500 | [diff] [blame] | 57 | 	int hwirq, irq; | 
| Grant Likely | 08a543a | 2011-07-26 03:19:06 -0600 | [diff] [blame] | 58 |  | 
 | 59 | 	mutex_lock(&irq_domain_mutex); | 
 | 60 | 	list_del(&domain->list); | 
 | 61 | 	mutex_unlock(&irq_domain_mutex); | 
 | 62 |  | 
 | 63 | 	/* Clear the irq_domain assignments */ | 
| Rob Herring | 6d27430 | 2011-09-30 10:48:38 -0500 | [diff] [blame] | 64 | 	irq_domain_for_each_irq(domain, hwirq, irq) { | 
 | 65 | 		d = irq_get_irq_data(irq); | 
| Grant Likely | 08a543a | 2011-07-26 03:19:06 -0600 | [diff] [blame] | 66 | 		d->domain = NULL; | 
 | 67 | 	} | 
 | 68 | } | 
 | 69 |  | 
 | 70 | #if defined(CONFIG_OF_IRQ) | 
 | 71 | /** | 
 | 72 |  * irq_create_of_mapping() - Map a linux irq number from a DT interrupt spec | 
 | 73 |  * | 
 | 74 |  * Used by the device tree interrupt mapping code to translate a device tree | 
 | 75 |  * interrupt specifier to a valid linux irq number.  Returns either a valid | 
 | 76 |  * linux IRQ number or 0. | 
 | 77 |  * | 
 | 78 |  * When the caller no longer need the irq number returned by this function it | 
 | 79 |  * should arrange to call irq_dispose_mapping(). | 
 | 80 |  */ | 
 | 81 | unsigned int irq_create_of_mapping(struct device_node *controller, | 
 | 82 | 				   const u32 *intspec, unsigned int intsize) | 
 | 83 | { | 
 | 84 | 	struct irq_domain *domain; | 
 | 85 | 	unsigned long hwirq; | 
 | 86 | 	unsigned int irq, type; | 
 | 87 | 	int rc = -EINVAL; | 
 | 88 |  | 
 | 89 | 	/* Find a domain which can translate the irq spec */ | 
 | 90 | 	mutex_lock(&irq_domain_mutex); | 
 | 91 | 	list_for_each_entry(domain, &irq_domain_list, list) { | 
 | 92 | 		if (!domain->ops->dt_translate) | 
 | 93 | 			continue; | 
 | 94 | 		rc = domain->ops->dt_translate(domain, controller, | 
 | 95 | 					intspec, intsize, &hwirq, &type); | 
 | 96 | 		if (rc == 0) | 
 | 97 | 			break; | 
 | 98 | 	} | 
 | 99 | 	mutex_unlock(&irq_domain_mutex); | 
 | 100 |  | 
 | 101 | 	if (rc != 0) | 
 | 102 | 		return 0; | 
 | 103 |  | 
 | 104 | 	irq = irq_domain_to_irq(domain, hwirq); | 
 | 105 | 	if (type != IRQ_TYPE_NONE) | 
 | 106 | 		irq_set_irq_type(irq, type); | 
 | 107 | 	pr_debug("%s: mapped hwirq=%i to irq=%i, flags=%x\n", | 
 | 108 | 		 controller->full_name, (int)hwirq, irq, type); | 
 | 109 | 	return irq; | 
 | 110 | } | 
 | 111 | EXPORT_SYMBOL_GPL(irq_create_of_mapping); | 
 | 112 |  | 
 | 113 | /** | 
 | 114 |  * irq_dispose_mapping() - Discard a mapping created by irq_create_of_mapping() | 
 | 115 |  * @irq: linux irq number to be discarded | 
 | 116 |  * | 
 | 117 |  * Calling this function indicates the caller no longer needs a reference to | 
 | 118 |  * the linux irq number returned by a prior call to irq_create_of_mapping(). | 
 | 119 |  */ | 
 | 120 | void irq_dispose_mapping(unsigned int irq) | 
 | 121 | { | 
 | 122 | 	/* | 
 | 123 | 	 * nothing yet; will be filled when support for dynamic allocation of | 
 | 124 | 	 * irq_descs is added to irq_domain | 
 | 125 | 	 */ | 
 | 126 | } | 
 | 127 | EXPORT_SYMBOL_GPL(irq_dispose_mapping); | 
| Grant Likely | 7e71330 | 2011-07-26 03:19:06 -0600 | [diff] [blame] | 128 |  | 
 | 129 | int irq_domain_simple_dt_translate(struct irq_domain *d, | 
 | 130 | 			    struct device_node *controller, | 
 | 131 | 			    const u32 *intspec, unsigned int intsize, | 
 | 132 | 			    unsigned long *out_hwirq, unsigned int *out_type) | 
 | 133 | { | 
 | 134 | 	if (d->of_node != controller) | 
 | 135 | 		return -EINVAL; | 
 | 136 | 	if (intsize < 1) | 
 | 137 | 		return -EINVAL; | 
 | 138 |  | 
 | 139 | 	*out_hwirq = intspec[0]; | 
 | 140 | 	*out_type = IRQ_TYPE_NONE; | 
 | 141 | 	if (intsize > 1) | 
 | 142 | 		*out_type = intspec[1] & IRQ_TYPE_SENSE_MASK; | 
 | 143 | 	return 0; | 
 | 144 | } | 
 | 145 |  | 
 | 146 | struct irq_domain_ops irq_domain_simple_ops = { | 
 | 147 | 	.dt_translate = irq_domain_simple_dt_translate, | 
 | 148 | }; | 
 | 149 | EXPORT_SYMBOL_GPL(irq_domain_simple_ops); | 
 | 150 |  | 
 | 151 | /** | 
 | 152 |  * irq_domain_create_simple() - Set up a 'simple' translation range | 
 | 153 |  */ | 
 | 154 | void irq_domain_add_simple(struct device_node *controller, int irq_base) | 
 | 155 | { | 
 | 156 | 	struct irq_domain *domain; | 
 | 157 |  | 
 | 158 | 	domain = kzalloc(sizeof(*domain), GFP_KERNEL); | 
 | 159 | 	if (!domain) { | 
 | 160 | 		WARN_ON(1); | 
 | 161 | 		return; | 
 | 162 | 	} | 
 | 163 |  | 
 | 164 | 	domain->irq_base = irq_base; | 
 | 165 | 	domain->of_node = of_node_get(controller); | 
 | 166 | 	domain->ops = &irq_domain_simple_ops; | 
 | 167 | 	irq_domain_add(domain); | 
 | 168 | } | 
 | 169 | EXPORT_SYMBOL_GPL(irq_domain_add_simple); | 
 | 170 |  | 
 | 171 | void irq_domain_generate_simple(const struct of_device_id *match, | 
 | 172 | 				u64 phys_base, unsigned int irq_start) | 
 | 173 | { | 
 | 174 | 	struct device_node *node; | 
 | 175 | 	pr_info("looking for phys_base=%llx, irq_start=%i\n", | 
 | 176 | 		(unsigned long long) phys_base, (int) irq_start); | 
 | 177 | 	node = of_find_matching_node_by_address(NULL, match, phys_base); | 
 | 178 | 	if (node) | 
 | 179 | 		irq_domain_add_simple(node, irq_start); | 
 | 180 | 	else | 
 | 181 | 		pr_info("no node found\n"); | 
 | 182 | } | 
 | 183 | EXPORT_SYMBOL_GPL(irq_domain_generate_simple); | 
| Grant Likely | 08a543a | 2011-07-26 03:19:06 -0600 | [diff] [blame] | 184 | #endif /* CONFIG_OF_IRQ */ |