|
@@ -960,6 +960,43 @@ void irq_domain_free_irqs_top(struct irq_domain *domain, unsigned int virq,
|
|
irq_domain_free_irqs_common(domain, virq, nr_irqs);
|
|
irq_domain_free_irqs_common(domain, virq, nr_irqs);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+static bool irq_domain_is_auto_recursive(struct irq_domain *domain)
|
|
|
|
+{
|
|
|
|
+ return domain->flags & IRQ_DOMAIN_FLAG_AUTO_RECURSIVE;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static void irq_domain_free_irqs_recursive(struct irq_domain *domain,
|
|
|
|
+ unsigned int irq_base,
|
|
|
|
+ unsigned int nr_irqs)
|
|
|
|
+{
|
|
|
|
+ domain->ops->free(domain, irq_base, nr_irqs);
|
|
|
|
+ if (irq_domain_is_auto_recursive(domain)) {
|
|
|
|
+ BUG_ON(!domain->parent);
|
|
|
|
+ irq_domain_free_irqs_recursive(domain->parent, irq_base,
|
|
|
|
+ nr_irqs);
|
|
|
|
+ }
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static int irq_domain_alloc_irqs_recursive(struct irq_domain *domain,
|
|
|
|
+ unsigned int irq_base,
|
|
|
|
+ unsigned int nr_irqs, void *arg)
|
|
|
|
+{
|
|
|
|
+ int ret = 0;
|
|
|
|
+ struct irq_domain *parent = domain->parent;
|
|
|
|
+ bool recursive = irq_domain_is_auto_recursive(domain);
|
|
|
|
+
|
|
|
|
+ BUG_ON(recursive && !parent);
|
|
|
|
+ if (recursive)
|
|
|
|
+ ret = irq_domain_alloc_irqs_recursive(parent, irq_base,
|
|
|
|
+ nr_irqs, arg);
|
|
|
|
+ if (ret >= 0)
|
|
|
|
+ ret = domain->ops->alloc(domain, irq_base, nr_irqs, arg);
|
|
|
|
+ if (ret < 0 && recursive)
|
|
|
|
+ irq_domain_free_irqs_recursive(parent, irq_base, nr_irqs);
|
|
|
|
+
|
|
|
|
+ return ret;
|
|
|
|
+}
|
|
|
|
+
|
|
/**
|
|
/**
|
|
* __irq_domain_alloc_irqs - Allocate IRQs from domain
|
|
* __irq_domain_alloc_irqs - Allocate IRQs from domain
|
|
* @domain: domain to allocate from
|
|
* @domain: domain to allocate from
|
|
@@ -1016,7 +1053,7 @@ int __irq_domain_alloc_irqs(struct irq_domain *domain, int irq_base,
|
|
}
|
|
}
|
|
|
|
|
|
mutex_lock(&irq_domain_mutex);
|
|
mutex_lock(&irq_domain_mutex);
|
|
- ret = domain->ops->alloc(domain, virq, nr_irqs, arg);
|
|
|
|
|
|
+ ret = irq_domain_alloc_irqs_recursive(domain, virq, nr_irqs, arg);
|
|
if (ret < 0) {
|
|
if (ret < 0) {
|
|
mutex_unlock(&irq_domain_mutex);
|
|
mutex_unlock(&irq_domain_mutex);
|
|
goto out_free_irq_data;
|
|
goto out_free_irq_data;
|
|
@@ -1051,13 +1088,54 @@ void irq_domain_free_irqs(unsigned int virq, unsigned int nr_irqs)
|
|
mutex_lock(&irq_domain_mutex);
|
|
mutex_lock(&irq_domain_mutex);
|
|
for (i = 0; i < nr_irqs; i++)
|
|
for (i = 0; i < nr_irqs; i++)
|
|
irq_domain_remove_irq(virq + i);
|
|
irq_domain_remove_irq(virq + i);
|
|
- data->domain->ops->free(data->domain, virq, nr_irqs);
|
|
|
|
|
|
+ irq_domain_free_irqs_recursive(data->domain, virq, nr_irqs);
|
|
mutex_unlock(&irq_domain_mutex);
|
|
mutex_unlock(&irq_domain_mutex);
|
|
|
|
|
|
irq_domain_free_irq_data(virq, nr_irqs);
|
|
irq_domain_free_irq_data(virq, nr_irqs);
|
|
irq_free_descs(virq, nr_irqs);
|
|
irq_free_descs(virq, nr_irqs);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+/**
|
|
|
|
+ * irq_domain_alloc_irqs_parent - Allocate interrupts from parent domain
|
|
|
|
+ * @irq_base: Base IRQ number
|
|
|
|
+ * @nr_irqs: Number of IRQs to allocate
|
|
|
|
+ * @arg: Allocation data (arch/domain specific)
|
|
|
|
+ *
|
|
|
|
+ * Check whether the domain has been setup recursive. If not allocate
|
|
|
|
+ * through the parent domain.
|
|
|
|
+ */
|
|
|
|
+int irq_domain_alloc_irqs_parent(struct irq_domain *domain,
|
|
|
|
+ unsigned int irq_base, unsigned int nr_irqs,
|
|
|
|
+ void *arg)
|
|
|
|
+{
|
|
|
|
+ /* irq_domain_alloc_irqs_recursive() has called parent's alloc() */
|
|
|
|
+ if (irq_domain_is_auto_recursive(domain))
|
|
|
|
+ return 0;
|
|
|
|
+
|
|
|
|
+ domain = domain->parent;
|
|
|
|
+ if (domain)
|
|
|
|
+ return irq_domain_alloc_irqs_recursive(domain, irq_base,
|
|
|
|
+ nr_irqs, arg);
|
|
|
|
+ return -ENOSYS;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+/**
|
|
|
|
+ * irq_domain_free_irqs_parent - Free interrupts from parent domain
|
|
|
|
+ * @irq_base: Base IRQ number
|
|
|
|
+ * @nr_irqs: Number of IRQs to free
|
|
|
|
+ *
|
|
|
|
+ * Check whether the domain has been setup recursive. If not free
|
|
|
|
+ * through the parent domain.
|
|
|
|
+ */
|
|
|
|
+void irq_domain_free_irqs_parent(struct irq_domain *domain,
|
|
|
|
+ unsigned int irq_base, unsigned int nr_irqs)
|
|
|
|
+{
|
|
|
|
+ /* irq_domain_free_irqs_recursive() will call parent's free */
|
|
|
|
+ if (!irq_domain_is_auto_recursive(domain) && domain->parent)
|
|
|
|
+ irq_domain_free_irqs_recursive(domain->parent, irq_base,
|
|
|
|
+ nr_irqs);
|
|
|
|
+}
|
|
|
|
+
|
|
/**
|
|
/**
|
|
* irq_domain_activate_irq - Call domain_ops->activate recursively to activate
|
|
* irq_domain_activate_irq - Call domain_ops->activate recursively to activate
|
|
* interrupt
|
|
* interrupt
|