/* Irq domain implements MSI remapping */
        IRQ_DOMAIN_FLAG_MSI_REMAP       = (1 << 5),
 
+       /* Irq domain name was allocated in __irq_domain_add() */
+       IRQ_DOMAIN_NAME_ALLOCATED       = (1 << 6),
+
        /*
         * Flags starting from IRQ_DOMAIN_FLAG_NONCORE are reserved
         * for implementation specific purposes and ignored by the
 }
 
 #ifdef CONFIG_IRQ_DOMAIN
-struct fwnode_handle *irq_domain_alloc_fwnode(void *data);
+struct fwnode_handle *__irq_domain_alloc_fwnode(unsigned int type, int id,
+                                               const char *name, void *data);
+
+enum {
+       IRQCHIP_FWNODE_REAL,
+       IRQCHIP_FWNODE_NAMED,
+       IRQCHIP_FWNODE_NAMED_ID,
+};
+
+static inline
+struct fwnode_handle *irq_domain_alloc_named_fwnode(const char *name)
+{
+       return __irq_domain_alloc_fwnode(IRQCHIP_FWNODE_NAMED, 0, name, NULL);
+}
+
+static inline
+struct fwnode_handle *irq_domain_alloc_named_id_fwnode(const char *name, int id)
+{
+       return __irq_domain_alloc_fwnode(IRQCHIP_FWNODE_NAMED_ID, id, name,
+                                        NULL);
+}
+
+static inline struct fwnode_handle *irq_domain_alloc_fwnode(void *data)
+{
+       return __irq_domain_alloc_fwnode(IRQCHIP_FWNODE_REAL, 0, NULL, data);
+}
+
 void irq_domain_free_fwnode(struct fwnode_handle *fwnode);
 struct irq_domain *__irq_domain_add(struct fwnode_handle *fwnode, int size,
                                    irq_hw_number_t hwirq_max, int direct_max,
 
 static void irq_domain_check_hierarchy(struct irq_domain *domain);
 
 struct irqchip_fwid {
-       struct fwnode_handle fwnode;
-       char *name;
-       void *data;
+       struct fwnode_handle    fwnode;
+       unsigned int            type;
+       char                    *name;
+       void                    *data;
 };
 
 /**
  * irq_domain_alloc_fwnode - Allocate a fwnode_handle suitable for
  *                           identifying an irq domain
- * @data: optional user-provided data
+ * @type:      Type of irqchip_fwnode. See linux/irqdomain.h
+ * @name:      Optional user provided domain name
+ * @id:                Optional user provided id if name != NULL
+ * @data:      Optional user-provided data
  *
- * Allocate a struct device_node, and return a poiner to the embedded
+ * Allocate a struct irqchip_fwid, and return a poiner to the embedded
  * fwnode_handle (or NULL on failure).
+ *
+ * Note: The types IRQCHIP_FWNODE_NAMED and IRQCHIP_FWNODE_NAMED_ID are
+ * solely to transport name information to irqdomain creation code. The
+ * node is not stored. For other types the pointer is kept in the irq
+ * domain struct.
  */
-struct fwnode_handle *irq_domain_alloc_fwnode(void *data)
+struct fwnode_handle *__irq_domain_alloc_fwnode(unsigned int type, int id,
+                                               const char *name, void *data)
 {
        struct irqchip_fwid *fwid;
-       char *name;
+       char *n;
 
        fwid = kzalloc(sizeof(*fwid), GFP_KERNEL);
-       name = kasprintf(GFP_KERNEL, "irqchip@%p", data);
 
-       if (!fwid || !name) {
+       switch (type) {
+       case IRQCHIP_FWNODE_NAMED:
+               n = kasprintf(GFP_KERNEL, "%s", name);
+               break;
+       case IRQCHIP_FWNODE_NAMED_ID:
+               n = kasprintf(GFP_KERNEL, "%s-%d", name, id);
+               break;
+       default:
+               n = kasprintf(GFP_KERNEL, "irqchip@%p", data);
+               break;
+       }
+
+       if (!fwid || !n) {
                kfree(fwid);
-               kfree(name);
+               kfree(n);
                return NULL;
        }
 
-       fwid->name = name;
+       fwid->type = type;
+       fwid->name = n;
        fwid->data = data;
        fwid->fwnode.type = FWNODE_IRQCHIP;
        return &fwid->fwnode;
 }
-EXPORT_SYMBOL_GPL(irq_domain_alloc_fwnode);
+EXPORT_SYMBOL_GPL(__irq_domain_alloc_fwnode);
 
 /**
  * irq_domain_free_fwnode - Free a non-OF-backed fwnode_handle
                                    void *host_data)
 {
        struct device_node *of_node = to_of_node(fwnode);
+       struct irqchip_fwid *fwid;
        struct irq_domain *domain;
 
+       static atomic_t unknown_domains;
+
        domain = kzalloc_node(sizeof(*domain) + (sizeof(unsigned int) * size),
                              GFP_KERNEL, of_node_to_nid(of_node));
        if (WARN_ON(!domain))
                return NULL;
 
+       if (fwnode && is_fwnode_irqchip(fwnode)) {
+               fwid = container_of(fwnode, struct irqchip_fwid, fwnode);
+
+               switch (fwid->type) {
+               case IRQCHIP_FWNODE_NAMED:
+               case IRQCHIP_FWNODE_NAMED_ID:
+                       domain->name = kstrdup(fwid->name, GFP_KERNEL);
+                       if (!domain->name) {
+                               kfree(domain);
+                               return NULL;
+                       }
+                       domain->flags |= IRQ_DOMAIN_NAME_ALLOCATED;
+                       break;
+               default:
+                       domain->fwnode = fwnode;
+                       domain->name = fwid->name;
+                       break;
+               }
+       } else if (of_node) {
+               char *name;
+
+               /*
+                * DT paths contain '/', which debugfs is legitimately
+                * unhappy about. Replace them with ':', which does
+                * the trick and is not as offensive as '\'...
+                */
+               name = kstrdup(of_node_full_name(of_node), GFP_KERNEL);
+               if (!name) {
+                       kfree(domain);
+                       return NULL;
+               }
+
+               strreplace(name, '/', ':');
+
+               domain->name = name;
+               domain->fwnode = fwnode;
+               domain->flags |= IRQ_DOMAIN_NAME_ALLOCATED;
+       }
+
+       if (!domain->name) {
+               if (fwnode) {
+                       pr_err("Invalid fwnode type (%d) for irqdomain\n",
+                              fwnode->type);
+               }
+               domain->name = kasprintf(GFP_KERNEL, "unknown-%d",
+                                        atomic_inc_return(&unknown_domains));
+               if (!domain->name) {
+                       kfree(domain);
+                       return NULL;
+               }
+               domain->flags |= IRQ_DOMAIN_NAME_ALLOCATED;
+       }
+
        of_node_get(of_node);
 
        /* Fill structure */
        INIT_RADIX_TREE(&domain->revmap_tree, GFP_KERNEL);
        domain->ops = ops;
        domain->host_data = host_data;
-       domain->fwnode = fwnode;
        domain->hwirq_max = hwirq_max;
        domain->revmap_size = size;
        domain->revmap_direct_max_irq = direct_max;
        pr_debug("Removed domain %s\n", domain->name);
 
        of_node_put(irq_domain_get_of_node(domain));
+       if (domain->flags & IRQ_DOMAIN_NAME_ALLOCATED)
+               kfree(domain->name);
        kfree(domain);
 }
 EXPORT_SYMBOL_GPL(irq_domain_remove);