Merge branch 'irq/core' into irq/urgent
[platform/kernel/linux-starfive.git] / kernel / irq / irqdomain.c
index 798a904..aa5b7ee 100644 (file)
@@ -25,6 +25,9 @@ static DEFINE_MUTEX(irq_domain_mutex);
 
 static struct irq_domain *irq_default_domain;
 
+static int irq_domain_alloc_irqs_locked(struct irq_domain *domain, int irq_base,
+                                       unsigned int nr_irqs, int node, void *arg,
+                                       bool realloc, const struct irq_affinity_desc *affinity);
 static void irq_domain_check_hierarchy(struct irq_domain *domain);
 
 struct irqchip_fwid {
@@ -123,23 +126,12 @@ void irq_domain_free_fwnode(struct fwnode_handle *fwnode)
 }
 EXPORT_SYMBOL_GPL(irq_domain_free_fwnode);
 
-/**
- * __irq_domain_add() - Allocate a new irq_domain data structure
- * @fwnode: firmware node for the interrupt controller
- * @size: Size of linear map; 0 for radix mapping only
- * @hwirq_max: Maximum number of interrupts supported by controller
- * @direct_max: Maximum value of direct maps; Use ~0 for no limit; 0 for no
- *              direct mapping
- * @ops: domain callbacks
- * @host_data: Controller private data pointer
- *
- * Allocates and initializes an irq_domain structure.
- * Returns pointer to IRQ domain, or NULL on failure.
- */
-struct irq_domain *__irq_domain_add(struct fwnode_handle *fwnode, unsigned int size,
-                                   irq_hw_number_t hwirq_max, int direct_max,
-                                   const struct irq_domain_ops *ops,
-                                   void *host_data)
+static struct irq_domain *__irq_domain_create(struct fwnode_handle *fwnode,
+                                             unsigned int size,
+                                             irq_hw_number_t hwirq_max,
+                                             int direct_max,
+                                             const struct irq_domain_ops *ops,
+                                             void *host_data)
 {
        struct irqchip_fwid *fwid;
        struct irq_domain *domain;
@@ -214,25 +206,66 @@ struct irq_domain *__irq_domain_add(struct fwnode_handle *fwnode, unsigned int s
 
        /* Fill structure */
        INIT_RADIX_TREE(&domain->revmap_tree, GFP_KERNEL);
-       mutex_init(&domain->revmap_mutex);
        domain->ops = ops;
        domain->host_data = host_data;
        domain->hwirq_max = hwirq_max;
 
-       if (direct_max) {
+       if (direct_max)
                domain->flags |= IRQ_DOMAIN_FLAG_NO_MAP;
-       }
 
        domain->revmap_size = size;
 
+       /*
+        * Hierarchical domains use the domain lock of the root domain
+        * (innermost domain).
+        *
+        * For non-hierarchical domains (as for root domains), the root
+        * pointer is set to the domain itself so that &domain->root->mutex
+        * always points to the right lock.
+        */
+       mutex_init(&domain->mutex);
+       domain->root = domain;
+
        irq_domain_check_hierarchy(domain);
 
+       return domain;
+}
+
+static void __irq_domain_publish(struct irq_domain *domain)
+{
        mutex_lock(&irq_domain_mutex);
        debugfs_add_domain_dir(domain);
        list_add(&domain->link, &irq_domain_list);
        mutex_unlock(&irq_domain_mutex);
 
        pr_debug("Added domain %s\n", domain->name);
+}
+
+/**
+ * __irq_domain_add() - Allocate a new irq_domain data structure
+ * @fwnode: firmware node for the interrupt controller
+ * @size: Size of linear map; 0 for radix mapping only
+ * @hwirq_max: Maximum number of interrupts supported by controller
+ * @direct_max: Maximum value of direct maps; Use ~0 for no limit; 0 for no
+ *              direct mapping
+ * @ops: domain callbacks
+ * @host_data: Controller private data pointer
+ *
+ * Allocates and initializes an irq_domain structure.
+ * Returns pointer to IRQ domain, or NULL on failure.
+ */
+struct irq_domain *__irq_domain_add(struct fwnode_handle *fwnode, unsigned int size,
+                                   irq_hw_number_t hwirq_max, int direct_max,
+                                   const struct irq_domain_ops *ops,
+                                   void *host_data)
+{
+       struct irq_domain *domain;
+
+       domain = __irq_domain_create(fwnode, size, hwirq_max, direct_max,
+                                    ops, host_data);
+       if (domain)
+               __irq_domain_publish(domain);
+
        return domain;
 }
 EXPORT_SYMBOL_GPL(__irq_domain_add);
@@ -502,30 +535,34 @@ static bool irq_domain_is_nomap(struct irq_domain *domain)
 static void irq_domain_clear_mapping(struct irq_domain *domain,
                                     irq_hw_number_t hwirq)
 {
+       lockdep_assert_held(&domain->root->mutex);
+
        if (irq_domain_is_nomap(domain))
                return;
 
-       mutex_lock(&domain->revmap_mutex);
        if (hwirq < domain->revmap_size)
                rcu_assign_pointer(domain->revmap[hwirq], NULL);
        else
                radix_tree_delete(&domain->revmap_tree, hwirq);
-       mutex_unlock(&domain->revmap_mutex);
 }
 
 static void irq_domain_set_mapping(struct irq_domain *domain,
                                   irq_hw_number_t hwirq,
                                   struct irq_data *irq_data)
 {
+       /*
+        * This also makes sure that all domains point to the same root when
+        * called from irq_domain_insert_irq() for each domain in a hierarchy.
+        */
+       lockdep_assert_held(&domain->root->mutex);
+
        if (irq_domain_is_nomap(domain))
                return;
 
-       mutex_lock(&domain->revmap_mutex);
        if (hwirq < domain->revmap_size)
                rcu_assign_pointer(domain->revmap[hwirq], irq_data);
        else
                radix_tree_insert(&domain->revmap_tree, hwirq, irq_data);
-       mutex_unlock(&domain->revmap_mutex);
 }
 
 static void irq_domain_disassociate(struct irq_domain *domain, unsigned int irq)
@@ -538,6 +575,9 @@ static void irq_domain_disassociate(struct irq_domain *domain, unsigned int irq)
                return;
 
        hwirq = irq_data->hwirq;
+
+       mutex_lock(&domain->root->mutex);
+
        irq_set_status_flags(irq, IRQ_NOREQUEST);
 
        /* remove chip and handler */
@@ -557,10 +597,12 @@ static void irq_domain_disassociate(struct irq_domain *domain, unsigned int irq)
 
        /* Clear reverse map for this hwirq */
        irq_domain_clear_mapping(domain, hwirq);
+
+       mutex_unlock(&domain->root->mutex);
 }
 
-int irq_domain_associate(struct irq_domain *domain, unsigned int virq,
-                        irq_hw_number_t hwirq)
+static int irq_domain_associate_locked(struct irq_domain *domain, unsigned int virq,
+                                      irq_hw_number_t hwirq)
 {
        struct irq_data *irq_data = irq_get_irq_data(virq);
        int ret;
@@ -573,7 +615,6 @@ int irq_domain_associate(struct irq_domain *domain, unsigned int virq,
        if (WARN(irq_data->domain, "error: virq%i is already associated", virq))
                return -EINVAL;
 
-       mutex_lock(&irq_domain_mutex);
        irq_data->hwirq = hwirq;
        irq_data->domain = domain;
        if (domain->ops->map) {
@@ -590,23 +631,29 @@ int irq_domain_associate(struct irq_domain *domain, unsigned int virq,
                        }
                        irq_data->domain = NULL;
                        irq_data->hwirq = 0;
-                       mutex_unlock(&irq_domain_mutex);
                        return ret;
                }
-
-               /* If not already assigned, give the domain the chip's name */
-               if (!domain->name && irq_data->chip)
-                       domain->name = irq_data->chip->name;
        }
 
        domain->mapcount++;
        irq_domain_set_mapping(domain, hwirq, irq_data);
-       mutex_unlock(&irq_domain_mutex);
 
        irq_clear_status_flags(virq, IRQ_NOREQUEST);
 
        return 0;
 }
+
+int irq_domain_associate(struct irq_domain *domain, unsigned int virq,
+                        irq_hw_number_t hwirq)
+{
+       int ret;
+
+       mutex_lock(&domain->root->mutex);
+       ret = irq_domain_associate_locked(domain, virq, hwirq);
+       mutex_unlock(&domain->root->mutex);
+
+       return ret;
+}
 EXPORT_SYMBOL_GPL(irq_domain_associate);
 
 void irq_domain_associate_many(struct irq_domain *domain, unsigned int irq_base,
@@ -619,9 +666,8 @@ void irq_domain_associate_many(struct irq_domain *domain, unsigned int irq_base,
        pr_debug("%s(%s, irqbase=%i, hwbase=%i, count=%i)\n", __func__,
                of_node_full_name(of_node), irq_base, (int)hwirq_base, count);
 
-       for (i = 0; i < count; i++) {
+       for (i = 0; i < count; i++)
                irq_domain_associate(domain, irq_base + i, hwirq_base + i);
-       }
 }
 EXPORT_SYMBOL_GPL(irq_domain_associate_many);
 
@@ -668,6 +714,34 @@ unsigned int irq_create_direct_mapping(struct irq_domain *domain)
 EXPORT_SYMBOL_GPL(irq_create_direct_mapping);
 #endif
 
+static unsigned int irq_create_mapping_affinity_locked(struct irq_domain *domain,
+                                                      irq_hw_number_t hwirq,
+                                                      const struct irq_affinity_desc *affinity)
+{
+       struct device_node *of_node = irq_domain_get_of_node(domain);
+       int virq;
+
+       pr_debug("irq_create_mapping(0x%p, 0x%lx)\n", domain, hwirq);
+
+       /* Allocate a virtual interrupt number */
+       virq = irq_domain_alloc_descs(-1, 1, hwirq, of_node_to_nid(of_node),
+                                     affinity);
+       if (virq <= 0) {
+               pr_debug("-> virq allocation failed\n");
+               return 0;
+       }
+
+       if (irq_domain_associate_locked(domain, virq, hwirq)) {
+               irq_free_desc(virq);
+               return 0;
+       }
+
+       pr_debug("irq %lu on domain %s mapped to virtual irq %u\n",
+               hwirq, of_node_full_name(of_node), virq);
+
+       return virq;
+}
+
 /**
  * irq_create_mapping_affinity() - Map a hardware interrupt into linux irq space
  * @domain: domain owning this hardware interrupt or NULL for default domain
@@ -680,14 +754,11 @@ EXPORT_SYMBOL_GPL(irq_create_direct_mapping);
  * on the number returned from that call.
  */
 unsigned int irq_create_mapping_affinity(struct irq_domain *domain,
-                                      irq_hw_number_t hwirq,
-                                      const struct irq_affinity_desc *affinity)
+                                        irq_hw_number_t hwirq,
+                                        const struct irq_affinity_desc *affinity)
 {
-       struct device_node *of_node;
        int virq;
 
-       pr_debug("irq_create_mapping(0x%p, 0x%lx)\n", domain, hwirq);
-
        /* Look for default domain if necessary */
        if (domain == NULL)
                domain = irq_default_domain;
@@ -695,32 +766,19 @@ unsigned int irq_create_mapping_affinity(struct irq_domain *domain,
                WARN(1, "%s(, %lx) called with NULL domain\n", __func__, hwirq);
                return 0;
        }
-       pr_debug("-> using domain @%p\n", domain);
 
-       of_node = irq_domain_get_of_node(domain);
+       mutex_lock(&domain->root->mutex);
 
        /* Check if mapping already exists */
        virq = irq_find_mapping(domain, hwirq);
        if (virq) {
-               pr_debug("-> existing mapping on virq %d\n", virq);
-               return virq;
-       }
-
-       /* Allocate a virtual interrupt number */
-       virq = irq_domain_alloc_descs(-1, 1, hwirq, of_node_to_nid(of_node),
-                                     affinity);
-       if (virq <= 0) {
-               pr_debug("-> virq allocation failed\n");
-               return 0;
-       }
-
-       if (irq_domain_associate(domain, virq, hwirq)) {
-               irq_free_desc(virq);
-               return 0;
+               pr_debug("existing mapping on virq %d\n", virq);
+               goto out;
        }
 
-       pr_debug("irq %lu on domain %s mapped to virtual irq %u\n",
-               hwirq, of_node_full_name(of_node), virq);
+       virq = irq_create_mapping_affinity_locked(domain, hwirq, affinity);
+out:
+       mutex_unlock(&domain->root->mutex);
 
        return virq;
 }
@@ -789,6 +847,8 @@ unsigned int irq_create_fwspec_mapping(struct irq_fwspec *fwspec)
        if (WARN_ON(type & ~IRQ_TYPE_SENSE_MASK))
                type &= IRQ_TYPE_SENSE_MASK;
 
+       mutex_lock(&domain->root->mutex);
+
        /*
         * If we've already configured this interrupt,
         * don't do it again, or hell will break loose.
@@ -801,7 +861,7 @@ unsigned int irq_create_fwspec_mapping(struct irq_fwspec *fwspec)
                 * interrupt number.
                 */
                if (type == IRQ_TYPE_NONE || type == irq_get_trigger_type(virq))
-                       return virq;
+                       goto out;
 
                /*
                 * If the trigger type has not been set yet, then set
@@ -809,40 +869,45 @@ unsigned int irq_create_fwspec_mapping(struct irq_fwspec *fwspec)
                 */
                if (irq_get_trigger_type(virq) == IRQ_TYPE_NONE) {
                        irq_data = irq_get_irq_data(virq);
-                       if (!irq_data)
-                               return 0;
+                       if (!irq_data) {
+                               virq = 0;
+                               goto out;
+                       }
 
                        irqd_set_trigger_type(irq_data, type);
-                       return virq;
+                       goto out;
                }
 
                pr_warn("type mismatch, failed to map hwirq-%lu for %s!\n",
                        hwirq, of_node_full_name(to_of_node(fwspec->fwnode)));
-               return 0;
+               virq = 0;
+               goto out;
        }
 
        if (irq_domain_is_hierarchy(domain)) {
-               virq = irq_domain_alloc_irqs(domain, 1, NUMA_NO_NODE, fwspec);
-               if (virq <= 0)
-                       return 0;
+               virq = irq_domain_alloc_irqs_locked(domain, -1, 1, NUMA_NO_NODE,
+                                                   fwspec, false, NULL);
+               if (virq <= 0) {
+                       virq = 0;
+                       goto out;
+               }
        } else {
                /* Create mapping */
-               virq = irq_create_mapping(domain, hwirq);
+               virq = irq_create_mapping_affinity_locked(domain, hwirq, NULL);
                if (!virq)
-                       return virq;
+                       goto out;
        }
 
        irq_data = irq_get_irq_data(virq);
-       if (!irq_data) {
-               if (irq_domain_is_hierarchy(domain))
-                       irq_domain_free_irqs(virq, 1);
-               else
-                       irq_dispose_mapping(virq);
-               return 0;
+       if (WARN_ON(!irq_data)) {
+               virq = 0;
+               goto out;
        }
 
        /* Store trigger type */
        irqd_set_trigger_type(irq_data, type);
+out:
+       mutex_unlock(&domain->root->mutex);
 
        return virq;
 }
@@ -1102,12 +1167,16 @@ struct irq_domain *irq_domain_create_hierarchy(struct irq_domain *parent,
        struct irq_domain *domain;
 
        if (size)
-               domain = irq_domain_create_linear(fwnode, size, ops, host_data);
+               domain = __irq_domain_create(fwnode, size, size, 0, ops, host_data);
        else
-               domain = irq_domain_create_tree(fwnode, ops, host_data);
+               domain = __irq_domain_create(fwnode, 0, ~0, 0, ops, host_data);
+
        if (domain) {
+               domain->root = parent->root;
                domain->parent = parent;
                domain->flags |= flags;
+
+               __irq_domain_publish(domain);
        }
 
        return domain;
@@ -1123,10 +1192,6 @@ static void irq_domain_insert_irq(int virq)
 
                domain->mapcount++;
                irq_domain_set_mapping(domain, data->hwirq, data);
-
-               /* If not already assigned, give the domain the chip's name */
-               if (!domain->name && data->chip)
-                       domain->name = data->chip->name;
        }
 
        irq_clear_status_flags(virq, IRQ_NOREQUEST);
@@ -1426,40 +1491,12 @@ int irq_domain_alloc_irqs_hierarchy(struct irq_domain *domain,
        return domain->ops->alloc(domain, irq_base, nr_irqs, arg);
 }
 
-/**
- * __irq_domain_alloc_irqs - Allocate IRQs from domain
- * @domain:    domain to allocate from
- * @irq_base:  allocate specified IRQ number if irq_base >= 0
- * @nr_irqs:   number of IRQs to allocate
- * @node:      NUMA node id for memory allocation
- * @arg:       domain specific argument
- * @realloc:   IRQ descriptors have already been allocated if true
- * @affinity:  Optional irq affinity mask for multiqueue devices
- *
- * Allocate IRQ numbers and initialized all data structures to support
- * hierarchy IRQ domains.
- * Parameter @realloc is mainly to support legacy IRQs.
- * Returns error code or allocated IRQ number
- *
- * The whole process to setup an IRQ has been split into two steps.
- * The first step, __irq_domain_alloc_irqs(), is to allocate IRQ
- * descriptor and required hardware resources. The second step,
- * irq_domain_activate_irq(), is to program the hardware with preallocated
- * resources. In this way, it's easier to rollback when failing to
- * allocate resources.
- */
-int __irq_domain_alloc_irqs(struct irq_domain *domain, int irq_base,
-                           unsigned int nr_irqs, int node, void *arg,
-                           bool realloc, const struct irq_affinity_desc *affinity)
+static int irq_domain_alloc_irqs_locked(struct irq_domain *domain, int irq_base,
+                                       unsigned int nr_irqs, int node, void *arg,
+                                       bool realloc, const struct irq_affinity_desc *affinity)
 {
        int i, ret, virq;
 
-       if (domain == NULL) {
-               domain = irq_default_domain;
-               if (WARN(!domain, "domain is NULL; cannot allocate IRQ\n"))
-                       return -EINVAL;
-       }
-
        if (realloc && irq_base >= 0) {
                virq = irq_base;
        } else {
@@ -1478,24 +1515,18 @@ int __irq_domain_alloc_irqs(struct irq_domain *domain, int irq_base,
                goto out_free_desc;
        }
 
-       mutex_lock(&irq_domain_mutex);
        ret = irq_domain_alloc_irqs_hierarchy(domain, virq, nr_irqs, arg);
-       if (ret < 0) {
-               mutex_unlock(&irq_domain_mutex);
+       if (ret < 0)
                goto out_free_irq_data;
-       }
 
        for (i = 0; i < nr_irqs; i++) {
                ret = irq_domain_trim_hierarchy(virq + i);
-               if (ret) {
-                       mutex_unlock(&irq_domain_mutex);
+               if (ret)
                        goto out_free_irq_data;
-               }
        }
-       
+
        for (i = 0; i < nr_irqs; i++)
                irq_domain_insert_irq(virq + i);
-       mutex_unlock(&irq_domain_mutex);
 
        return virq;
 
@@ -1505,6 +1536,48 @@ out_free_desc:
        irq_free_descs(virq, nr_irqs);
        return ret;
 }
+
+/**
+ * __irq_domain_alloc_irqs - Allocate IRQs from domain
+ * @domain:    domain to allocate from
+ * @irq_base:  allocate specified IRQ number if irq_base >= 0
+ * @nr_irqs:   number of IRQs to allocate
+ * @node:      NUMA node id for memory allocation
+ * @arg:       domain specific argument
+ * @realloc:   IRQ descriptors have already been allocated if true
+ * @affinity:  Optional irq affinity mask for multiqueue devices
+ *
+ * Allocate IRQ numbers and initialized all data structures to support
+ * hierarchy IRQ domains.
+ * Parameter @realloc is mainly to support legacy IRQs.
+ * Returns error code or allocated IRQ number
+ *
+ * The whole process to setup an IRQ has been split into two steps.
+ * The first step, __irq_domain_alloc_irqs(), is to allocate IRQ
+ * descriptor and required hardware resources. The second step,
+ * irq_domain_activate_irq(), is to program the hardware with preallocated
+ * resources. In this way, it's easier to rollback when failing to
+ * allocate resources.
+ */
+int __irq_domain_alloc_irqs(struct irq_domain *domain, int irq_base,
+                           unsigned int nr_irqs, int node, void *arg,
+                           bool realloc, const struct irq_affinity_desc *affinity)
+{
+       int ret;
+
+       if (domain == NULL) {
+               domain = irq_default_domain;
+               if (WARN(!domain, "domain is NULL; cannot allocate IRQ\n"))
+                       return -EINVAL;
+       }
+
+       mutex_lock(&domain->root->mutex);
+       ret = irq_domain_alloc_irqs_locked(domain, irq_base, nr_irqs, node, arg,
+                                          realloc, affinity);
+       mutex_unlock(&domain->root->mutex);
+
+       return ret;
+}
 EXPORT_SYMBOL_GPL(__irq_domain_alloc_irqs);
 
 /* The irq_data was moved, fix the revmap to refer to the new location */
@@ -1512,11 +1585,12 @@ static void irq_domain_fix_revmap(struct irq_data *d)
 {
        void __rcu **slot;
 
+       lockdep_assert_held(&d->domain->root->mutex);
+
        if (irq_domain_is_nomap(d->domain))
                return;
 
        /* Fix up the revmap. */
-       mutex_lock(&d->domain->revmap_mutex);
        if (d->hwirq < d->domain->revmap_size) {
                /* Not using radix tree */
                rcu_assign_pointer(d->domain->revmap[d->hwirq], d);
@@ -1525,7 +1599,6 @@ static void irq_domain_fix_revmap(struct irq_data *d)
                if (slot)
                        radix_tree_replace_slot(&d->domain->revmap_tree, slot, d);
        }
-       mutex_unlock(&d->domain->revmap_mutex);
 }
 
 /**
@@ -1541,8 +1614,8 @@ static void irq_domain_fix_revmap(struct irq_data *d)
  */
 int irq_domain_push_irq(struct irq_domain *domain, int virq, void *arg)
 {
-       struct irq_data *child_irq_data;
-       struct irq_data *root_irq_data = irq_get_irq_data(virq);
+       struct irq_data *irq_data = irq_get_irq_data(virq);
+       struct irq_data *parent_irq_data;
        struct irq_desc *desc;
        int rv = 0;
 
@@ -1567,47 +1640,46 @@ int irq_domain_push_irq(struct irq_domain *domain, int virq, void *arg)
        if (WARN_ON(!irq_domain_is_hierarchy(domain)))
                return -EINVAL;
 
-       if (!root_irq_data)
+       if (!irq_data)
                return -EINVAL;
 
-       if (domain->parent != root_irq_data->domain)
+       if (domain->parent != irq_data->domain)
                return -EINVAL;
 
-       child_irq_data = kzalloc_node(sizeof(*child_irq_data), GFP_KERNEL,
-                                     irq_data_get_node(root_irq_data));
-       if (!child_irq_data)
+       parent_irq_data = kzalloc_node(sizeof(*parent_irq_data), GFP_KERNEL,
+                                      irq_data_get_node(irq_data));
+       if (!parent_irq_data)
                return -ENOMEM;
 
-       mutex_lock(&irq_domain_mutex);
+       mutex_lock(&domain->root->mutex);
 
        /* Copy the original irq_data. */
-       *child_irq_data = *root_irq_data;
+       *parent_irq_data = *irq_data;
 
        /*
-        * Overwrite the root_irq_data, which is embedded in struct
-        * irq_desc, with values for this domain.
+        * Overwrite the irq_data, which is embedded in struct irq_desc, with
+        * values for this domain.
         */
-       root_irq_data->parent_data = child_irq_data;
-       root_irq_data->domain = domain;
-       root_irq_data->mask = 0;
-       root_irq_data->hwirq = 0;
-       root_irq_data->chip = NULL;
-       root_irq_data->chip_data = NULL;
+       irq_data->parent_data = parent_irq_data;
+       irq_data->domain = domain;
+       irq_data->mask = 0;
+       irq_data->hwirq = 0;
+       irq_data->chip = NULL;
+       irq_data->chip_data = NULL;
 
        /* May (probably does) set hwirq, chip, etc. */
        rv = irq_domain_alloc_irqs_hierarchy(domain, virq, 1, arg);
        if (rv) {
                /* Restore the original irq_data. */
-               *root_irq_data = *child_irq_data;
-               kfree(child_irq_data);
+               *irq_data = *parent_irq_data;
+               kfree(parent_irq_data);
                goto error;
        }
 
-       irq_domain_fix_revmap(child_irq_data);
-       irq_domain_set_mapping(domain, root_irq_data->hwirq, root_irq_data);
-
+       irq_domain_fix_revmap(parent_irq_data);
+       irq_domain_set_mapping(domain, irq_data->hwirq, irq_data);
 error:
-       mutex_unlock(&irq_domain_mutex);
+       mutex_unlock(&domain->root->mutex);
 
        return rv;
 }
@@ -1623,8 +1695,8 @@ EXPORT_SYMBOL_GPL(irq_domain_push_irq);
  */
 int irq_domain_pop_irq(struct irq_domain *domain, int virq)
 {
-       struct irq_data *root_irq_data = irq_get_irq_data(virq);
-       struct irq_data *child_irq_data;
+       struct irq_data *irq_data = irq_get_irq_data(virq);
+       struct irq_data *parent_irq_data;
        struct irq_data *tmp_irq_data;
        struct irq_desc *desc;
 
@@ -1646,37 +1718,37 @@ int irq_domain_pop_irq(struct irq_domain *domain, int virq)
        if (domain == NULL)
                return -EINVAL;
 
-       if (!root_irq_data)
+       if (!irq_data)
                return -EINVAL;
 
        tmp_irq_data = irq_domain_get_irq_data(domain, virq);
 
        /* We can only "pop" if this domain is at the top of the list */
-       if (WARN_ON(root_irq_data != tmp_irq_data))
+       if (WARN_ON(irq_data != tmp_irq_data))
                return -EINVAL;
 
-       if (WARN_ON(root_irq_data->domain != domain))
+       if (WARN_ON(irq_data->domain != domain))
                return -EINVAL;
 
-       child_irq_data = root_irq_data->parent_data;
-       if (WARN_ON(!child_irq_data))
+       parent_irq_data = irq_data->parent_data;
+       if (WARN_ON(!parent_irq_data))
                return -EINVAL;
 
-       mutex_lock(&irq_domain_mutex);
+       mutex_lock(&domain->root->mutex);
 
-       root_irq_data->parent_data = NULL;
+       irq_data->parent_data = NULL;
 
-       irq_domain_clear_mapping(domain, root_irq_data->hwirq);
+       irq_domain_clear_mapping(domain, irq_data->hwirq);
        irq_domain_free_irqs_hierarchy(domain, virq, 1);
 
        /* Restore the original irq_data. */
-       *root_irq_data = *child_irq_data;
+       *irq_data = *parent_irq_data;
 
-       irq_domain_fix_revmap(root_irq_data);
+       irq_domain_fix_revmap(irq_data);
 
-       mutex_unlock(&irq_domain_mutex);
+       mutex_unlock(&domain->root->mutex);
 
-       kfree(child_irq_data);
+       kfree(parent_irq_data);
 
        return 0;
 }
@@ -1690,17 +1762,20 @@ EXPORT_SYMBOL_GPL(irq_domain_pop_irq);
 void irq_domain_free_irqs(unsigned int virq, unsigned int nr_irqs)
 {
        struct irq_data *data = irq_get_irq_data(virq);
+       struct irq_domain *domain;
        int i;
 
        if (WARN(!data || !data->domain || !data->domain->ops->free,
                 "NULL pointer, cannot free irq\n"))
                return;
 
-       mutex_lock(&irq_domain_mutex);
+       domain = data->domain;
+
+       mutex_lock(&domain->root->mutex);
        for (i = 0; i < nr_irqs; i++)
                irq_domain_remove_irq(virq + i);
-       irq_domain_free_irqs_hierarchy(data->domain, virq, nr_irqs);
-       mutex_unlock(&irq_domain_mutex);
+       irq_domain_free_irqs_hierarchy(domain, virq, nr_irqs);
+       mutex_unlock(&domain->root->mutex);
 
        irq_domain_free_irq_data(virq, nr_irqs);
        irq_free_descs(virq, nr_irqs);
@@ -1865,6 +1940,13 @@ void irq_domain_set_info(struct irq_domain *domain, unsigned int virq,
        irq_set_handler_data(virq, handler_data);
 }
 
+static int irq_domain_alloc_irqs_locked(struct irq_domain *domain, int irq_base,
+                                       unsigned int nr_irqs, int node, void *arg,
+                                       bool realloc, const struct irq_affinity_desc *affinity)
+{
+       return -EINVAL;
+}
+
 static void irq_domain_check_hierarchy(struct irq_domain *domain)
 {
 }