diff options
author | Jiang Liu <jiang.liu@linux.intel.com> | 2014-11-15 09:24:01 -0500 |
---|---|---|
committer | Thomas Gleixner <tglx@linutronix.de> | 2014-11-23 07:01:46 -0500 |
commit | 36d727310cb9f85efb5ac089ffb1797e7c3538e1 (patch) | |
tree | e58639a6b10598f89db6adc66b03145ab1841f45 | |
parent | 1b5377087cb4e68d719a875120894fddfbcbf0f9 (diff) |
irqdomain: Implement a method to automatically call parent domains alloc/free
Add a flags to irq_domain.flags to control whether the irqdomain core
should automatically call parent irqdomain's alloc/free callbacks. It
help to reduce hierarchy irqdomains users' code size.
Signed-off-by: Jiang Liu <jiang.liu@linux.intel.com>
Cc: Tony Luck <tony.luck@intel.com>
Cc: linux-arm-kernel@lists.infradead.org
Cc: Bjorn Helgaas <bhelgaas@google.com>
Cc: Grant Likely <grant.likely@linaro.org>
Cc: Marc Zyngier <marc.zyngier@arm.com>
Cc: Yijing Wang <wangyijing@huawei.com>
Cc: Yingjoe Chen <yingjoe.chen@mediatek.com>
Cc: Borislav Petkov <bp@alien8.de>
Cc: Benjamin Herrenschmidt <benh@kernel.crashing.org>
Cc: Matthias Brugger <matthias.bgg@gmail.com>
Link: http://lkml.kernel.org/r/1416061447-9472-4-git-send-email-jiang.liu@linux.intel.com
Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
-rw-r--r-- | include/linux/irqdomain.h | 24 | ||||
-rw-r--r-- | kernel/irq/irqdomain.c | 82 |
2 files changed, 89 insertions, 17 deletions
diff --git a/include/linux/irqdomain.h b/include/linux/irqdomain.h index 7aca1adb68a1..dd2709bdad56 100644 --- a/include/linux/irqdomain.h +++ b/include/linux/irqdomain.h | |||
@@ -134,6 +134,9 @@ enum { | |||
134 | /* Irq domain is hierarchical */ | 134 | /* Irq domain is hierarchical */ |
135 | IRQ_DOMAIN_FLAG_HIERARCHY = (1 << 0), | 135 | IRQ_DOMAIN_FLAG_HIERARCHY = (1 << 0), |
136 | 136 | ||
137 | /* Core calls alloc/free recursive through the domain hierarchy. */ | ||
138 | IRQ_DOMAIN_FLAG_AUTO_RECURSIVE = (1 << 1), | ||
139 | |||
137 | /* | 140 | /* |
138 | * Flags starting from IRQ_DOMAIN_FLAG_NONCORE are reserved | 141 | * Flags starting from IRQ_DOMAIN_FLAG_NONCORE are reserved |
139 | * for implementation specific purposes and ignored by the | 142 | * for implementation specific purposes and ignored by the |
@@ -285,22 +288,13 @@ extern void irq_domain_free_irqs_common(struct irq_domain *domain, | |||
285 | extern void irq_domain_free_irqs_top(struct irq_domain *domain, | 288 | extern void irq_domain_free_irqs_top(struct irq_domain *domain, |
286 | unsigned int virq, unsigned int nr_irqs); | 289 | unsigned int virq, unsigned int nr_irqs); |
287 | 290 | ||
288 | static inline int irq_domain_alloc_irqs_parent(struct irq_domain *domain, | 291 | extern int irq_domain_alloc_irqs_parent(struct irq_domain *domain, |
289 | unsigned int irq_base, | 292 | unsigned int irq_base, |
290 | unsigned int nr_irqs, void *arg) | 293 | unsigned int nr_irqs, void *arg); |
291 | { | ||
292 | if (domain->parent && domain->parent->ops->alloc) | ||
293 | return domain->parent->ops->alloc(domain->parent, irq_base, | ||
294 | nr_irqs, arg); | ||
295 | return -ENOSYS; | ||
296 | } | ||
297 | 294 | ||
298 | static inline void irq_domain_free_irqs_parent(struct irq_domain *domain, | 295 | extern void irq_domain_free_irqs_parent(struct irq_domain *domain, |
299 | unsigned int irq_base, unsigned int nr_irqs) | 296 | unsigned int irq_base, |
300 | { | 297 | unsigned int nr_irqs); |
301 | if (domain->parent && domain->parent->ops->free) | ||
302 | domain->parent->ops->free(domain->parent, irq_base, nr_irqs); | ||
303 | } | ||
304 | 298 | ||
305 | static inline bool irq_domain_is_hierarchy(struct irq_domain *domain) | 299 | static inline bool irq_domain_is_hierarchy(struct irq_domain *domain) |
306 | { | 300 | { |
diff --git a/kernel/irq/irqdomain.c b/kernel/irq/irqdomain.c index 4e62832ace82..9c88db7056d4 100644 --- a/kernel/irq/irqdomain.c +++ b/kernel/irq/irqdomain.c | |||
@@ -960,6 +960,43 @@ void irq_domain_free_irqs_top(struct irq_domain *domain, unsigned int virq, | |||
960 | irq_domain_free_irqs_common(domain, virq, nr_irqs); | 960 | irq_domain_free_irqs_common(domain, virq, nr_irqs); |
961 | } | 961 | } |
962 | 962 | ||
963 | static bool irq_domain_is_auto_recursive(struct irq_domain *domain) | ||
964 | { | ||
965 | return domain->flags & IRQ_DOMAIN_FLAG_AUTO_RECURSIVE; | ||
966 | } | ||
967 | |||
968 | static void irq_domain_free_irqs_recursive(struct irq_domain *domain, | ||
969 | unsigned int irq_base, | ||
970 | unsigned int nr_irqs) | ||
971 | { | ||
972 | domain->ops->free(domain, irq_base, nr_irqs); | ||
973 | if (irq_domain_is_auto_recursive(domain)) { | ||
974 | BUG_ON(!domain->parent); | ||
975 | irq_domain_free_irqs_recursive(domain->parent, irq_base, | ||
976 | nr_irqs); | ||
977 | } | ||
978 | } | ||
979 | |||
980 | static int irq_domain_alloc_irqs_recursive(struct irq_domain *domain, | ||
981 | unsigned int irq_base, | ||
982 | unsigned int nr_irqs, void *arg) | ||
983 | { | ||
984 | int ret = 0; | ||
985 | struct irq_domain *parent = domain->parent; | ||
986 | bool recursive = irq_domain_is_auto_recursive(domain); | ||
987 | |||
988 | BUG_ON(recursive && !parent); | ||
989 | if (recursive) | ||
990 | ret = irq_domain_alloc_irqs_recursive(parent, irq_base, | ||
991 | nr_irqs, arg); | ||
992 | if (ret >= 0) | ||
993 | ret = domain->ops->alloc(domain, irq_base, nr_irqs, arg); | ||
994 | if (ret < 0 && recursive) | ||
995 | irq_domain_free_irqs_recursive(parent, irq_base, nr_irqs); | ||
996 | |||
997 | return ret; | ||
998 | } | ||
999 | |||
963 | /** | 1000 | /** |
964 | * __irq_domain_alloc_irqs - Allocate IRQs from domain | 1001 | * __irq_domain_alloc_irqs - Allocate IRQs from domain |
965 | * @domain: domain to allocate from | 1002 | * @domain: domain to allocate from |
@@ -1016,7 +1053,7 @@ int __irq_domain_alloc_irqs(struct irq_domain *domain, int irq_base, | |||
1016 | } | 1053 | } |
1017 | 1054 | ||
1018 | mutex_lock(&irq_domain_mutex); | 1055 | mutex_lock(&irq_domain_mutex); |
1019 | ret = domain->ops->alloc(domain, virq, nr_irqs, arg); | 1056 | ret = irq_domain_alloc_irqs_recursive(domain, virq, nr_irqs, arg); |
1020 | if (ret < 0) { | 1057 | if (ret < 0) { |
1021 | mutex_unlock(&irq_domain_mutex); | 1058 | mutex_unlock(&irq_domain_mutex); |
1022 | goto out_free_irq_data; | 1059 | goto out_free_irq_data; |
@@ -1051,7 +1088,7 @@ void irq_domain_free_irqs(unsigned int virq, unsigned int nr_irqs) | |||
1051 | mutex_lock(&irq_domain_mutex); | 1088 | mutex_lock(&irq_domain_mutex); |
1052 | for (i = 0; i < nr_irqs; i++) | 1089 | for (i = 0; i < nr_irqs; i++) |
1053 | irq_domain_remove_irq(virq + i); | 1090 | irq_domain_remove_irq(virq + i); |
1054 | data->domain->ops->free(data->domain, virq, nr_irqs); | 1091 | irq_domain_free_irqs_recursive(data->domain, virq, nr_irqs); |
1055 | mutex_unlock(&irq_domain_mutex); | 1092 | mutex_unlock(&irq_domain_mutex); |
1056 | 1093 | ||
1057 | irq_domain_free_irq_data(virq, nr_irqs); | 1094 | irq_domain_free_irq_data(virq, nr_irqs); |
@@ -1059,6 +1096,47 @@ void irq_domain_free_irqs(unsigned int virq, unsigned int nr_irqs) | |||
1059 | } | 1096 | } |
1060 | 1097 | ||
1061 | /** | 1098 | /** |
1099 | * irq_domain_alloc_irqs_parent - Allocate interrupts from parent domain | ||
1100 | * @irq_base: Base IRQ number | ||
1101 | * @nr_irqs: Number of IRQs to allocate | ||
1102 | * @arg: Allocation data (arch/domain specific) | ||
1103 | * | ||
1104 | * Check whether the domain has been setup recursive. If not allocate | ||
1105 | * through the parent domain. | ||
1106 | */ | ||
1107 | int irq_domain_alloc_irqs_parent(struct irq_domain *domain, | ||
1108 | unsigned int irq_base, unsigned int nr_irqs, | ||
1109 | void *arg) | ||
1110 | { | ||
1111 | /* irq_domain_alloc_irqs_recursive() has called parent's alloc() */ | ||
1112 | if (irq_domain_is_auto_recursive(domain)) | ||
1113 | return 0; | ||
1114 | |||
1115 | domain = domain->parent; | ||
1116 | if (domain) | ||
1117 | return irq_domain_alloc_irqs_recursive(domain, irq_base, | ||
1118 | nr_irqs, arg); | ||
1119 | return -ENOSYS; | ||
1120 | } | ||
1121 | |||
1122 | /** | ||
1123 | * irq_domain_free_irqs_parent - Free interrupts from parent domain | ||
1124 | * @irq_base: Base IRQ number | ||
1125 | * @nr_irqs: Number of IRQs to free | ||
1126 | * | ||
1127 | * Check whether the domain has been setup recursive. If not free | ||
1128 | * through the parent domain. | ||
1129 | */ | ||
1130 | void irq_domain_free_irqs_parent(struct irq_domain *domain, | ||
1131 | unsigned int irq_base, unsigned int nr_irqs) | ||
1132 | { | ||
1133 | /* irq_domain_free_irqs_recursive() will call parent's free */ | ||
1134 | if (!irq_domain_is_auto_recursive(domain) && domain->parent) | ||
1135 | irq_domain_free_irqs_recursive(domain->parent, irq_base, | ||
1136 | nr_irqs); | ||
1137 | } | ||
1138 | |||
1139 | /** | ||
1062 | * irq_domain_activate_irq - Call domain_ops->activate recursively to activate | 1140 | * irq_domain_activate_irq - Call domain_ops->activate recursively to activate |
1063 | * interrupt | 1141 | * interrupt |
1064 | * @irq_data: outermost irq_data associated with interrupt | 1142 | * @irq_data: outermost irq_data associated with interrupt |