aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorPaul Mundt <lethal@linux-sh.org>2012-05-19 02:11:41 -0400
committerGrant Likely <grant.likely@secretlab.ca>2012-05-19 14:32:35 -0400
commit58ee99ada293b5ed971a023304fcfbc1a0ccdb1c (patch)
tree29faab64364615dda0cb373ee7dbd483ff89ce9c
parent36be50515fe2aef61533b516fa2576a2c7fe7664 (diff)
irqdomain: Support removal of IRQ domains.
Now that IRQ domains are being used by modules it's necessary to support removing them, too. This adds a new irq_domain_remove() routine for doing the bulk of the heavy lifting. It's left as an exercise to the caller to ensure all mappings have been appropriatey disposed of before attempting to remove the domain. Signed-off-by: Paul Mundt <lethal@linux-sh.org> Signed-off-by: Grant Likely <grant.likely@secretlab.ca>
-rw-r--r--include/linux/irqdomain.h4
-rw-r--r--kernel/irq/irqdomain.c61
2 files changed, 62 insertions, 3 deletions
diff --git a/include/linux/irqdomain.h b/include/linux/irqdomain.h
index c65740d76e66..a796dbf80b67 100644
--- a/include/linux/irqdomain.h
+++ b/include/linux/irqdomain.h
@@ -141,10 +141,12 @@ static inline struct irq_domain *irq_domain_add_legacy_isa(
141 return irq_domain_add_legacy(of_node, NUM_ISA_INTERRUPTS, 0, 0, ops, 141 return irq_domain_add_legacy(of_node, NUM_ISA_INTERRUPTS, 0, 0, ops,
142 host_data); 142 host_data);
143} 143}
144
145extern void irq_domain_remove(struct irq_domain *host);
146
144extern struct irq_domain *irq_find_host(struct device_node *node); 147extern struct irq_domain *irq_find_host(struct device_node *node);
145extern void irq_set_default_host(struct irq_domain *host); 148extern void irq_set_default_host(struct irq_domain *host);
146 149
147
148extern unsigned int irq_create_mapping(struct irq_domain *host, 150extern unsigned int irq_create_mapping(struct irq_domain *host,
149 irq_hw_number_t hwirq); 151 irq_hw_number_t hwirq);
150extern void irq_dispose_mapping(unsigned int virq); 152extern void irq_dispose_mapping(unsigned int virq);
diff --git a/kernel/irq/irqdomain.c b/kernel/irq/irqdomain.c
index 0e0ba5f840b2..9cae0b2f509f 100644
--- a/kernel/irq/irqdomain.c
+++ b/kernel/irq/irqdomain.c
@@ -56,6 +56,12 @@ static struct irq_domain *irq_domain_alloc(struct device_node *of_node,
56 return domain; 56 return domain;
57} 57}
58 58
59static void irq_domain_free(struct irq_domain *domain)
60{
61 of_node_put(domain->of_node);
62 kfree(domain);
63}
64
59static void irq_domain_add(struct irq_domain *domain) 65static void irq_domain_add(struct irq_domain *domain)
60{ 66{
61 mutex_lock(&irq_domain_mutex); 67 mutex_lock(&irq_domain_mutex);
@@ -65,6 +71,58 @@ static void irq_domain_add(struct irq_domain *domain)
65 domain->revmap_type, domain); 71 domain->revmap_type, domain);
66} 72}
67 73
74/**
75 * irq_domain_remove() - Remove an irq domain.
76 * @domain: domain to remove
77 *
78 * This routine is used to remove an irq domain. The caller must ensure
79 * that all mappings within the domain have been disposed of prior to
80 * use, depending on the revmap type.
81 */
82void irq_domain_remove(struct irq_domain *domain)
83{
84 mutex_lock(&irq_domain_mutex);
85
86 switch (domain->revmap_type) {
87 case IRQ_DOMAIN_MAP_LEGACY:
88 /*
89 * Legacy domains don't manage their own irq_desc
90 * allocations, we expect the caller to handle irq_desc
91 * freeing on their own.
92 */
93 break;
94 case IRQ_DOMAIN_MAP_TREE:
95 /*
96 * radix_tree_delete() takes care of destroying the root
97 * node when all entries are removed. Shout if there are
98 * any mappings left.
99 */
100 WARN_ON(domain->revmap_data.tree.height);
101 break;
102 case IRQ_DOMAIN_MAP_LINEAR:
103 kfree(domain->revmap_data.linear.revmap);
104 domain->revmap_data.linear.size = 0;
105 break;
106 case IRQ_DOMAIN_MAP_NOMAP:
107 break;
108 }
109
110 list_del(&domain->link);
111
112 /*
113 * If the going away domain is the default one, reset it.
114 */
115 if (unlikely(irq_default_domain == domain))
116 irq_set_default_host(NULL);
117
118 mutex_unlock(&irq_domain_mutex);
119
120 pr_debug("irq: Removed domain of type %d @0x%p\n",
121 domain->revmap_type, domain);
122
123 irq_domain_free(domain);
124}
125
68static unsigned int irq_domain_legacy_revmap(struct irq_domain *domain, 126static unsigned int irq_domain_legacy_revmap(struct irq_domain *domain,
69 irq_hw_number_t hwirq) 127 irq_hw_number_t hwirq)
70{ 128{
@@ -117,8 +175,7 @@ struct irq_domain *irq_domain_add_legacy(struct device_node *of_node,
117 175
118 if (WARN_ON(!irq_data || irq_data->domain)) { 176 if (WARN_ON(!irq_data || irq_data->domain)) {
119 mutex_unlock(&irq_domain_mutex); 177 mutex_unlock(&irq_domain_mutex);
120 of_node_put(domain->of_node); 178 irq_domain_free(domain);
121 kfree(domain);
122 return NULL; 179 return NULL;
123 } 180 }
124 } 181 }