]> git.kernelconcepts.de Git - karo-tx-linux.git/blobdiff - kernel/irq/irqdomain.c
genirq/debugfs: Remove redundant NULL pointer check
[karo-tx-linux.git] / kernel / irq / irqdomain.c
index 31805f237396bdfb6f4d72a906c0dcb957b6ceaf..ed47688b8e7981b24323153ec763a19505e2451a 100644 (file)
@@ -26,39 +26,69 @@ static struct irq_domain *irq_default_domain;
 static void irq_domain_check_hierarchy(struct irq_domain *domain);
 
 struct irqchip_fwid {
-       struct fwnode_handle fwnode;
-       char *name;
+       struct fwnode_handle    fwnode;
+       unsigned int            type;
+       char                    *name;
        void *data;
 };
 
+#ifdef CONFIG_GENERIC_IRQ_DEBUGFS
+static void debugfs_add_domain_dir(struct irq_domain *d);
+static void debugfs_remove_domain_dir(struct irq_domain *d);
+#else
+static inline void debugfs_add_domain_dir(struct irq_domain *d) { }
+static inline void debugfs_remove_domain_dir(struct irq_domain *d) { }
+#endif
+
 /**
  * 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
@@ -97,26 +127,82 @@ struct irq_domain *__irq_domain_add(struct fwnode_handle *fwnode, int size,
                                    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;
        irq_domain_check_hierarchy(domain);
 
        mutex_lock(&irq_domain_mutex);
+       debugfs_add_domain_dir(domain);
        list_add(&domain->link, &irq_domain_list);
        mutex_unlock(&irq_domain_mutex);
 
@@ -136,6 +222,7 @@ EXPORT_SYMBOL_GPL(__irq_domain_add);
 void irq_domain_remove(struct irq_domain *domain)
 {
        mutex_lock(&irq_domain_mutex);
+       debugfs_remove_domain_dir(domain);
 
        WARN_ON(!radix_tree_empty(&domain->revmap_tree));
 
@@ -152,10 +239,43 @@ void irq_domain_remove(struct irq_domain *domain)
        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);
 
+void irq_domain_update_bus_token(struct irq_domain *domain,
+                                enum irq_domain_bus_token bus_token)
+{
+       char *name;
+
+       if (domain->bus_token == bus_token)
+               return;
+
+       mutex_lock(&irq_domain_mutex);
+
+       domain->bus_token = bus_token;
+
+       name = kasprintf(GFP_KERNEL, "%s-%d", domain->name, bus_token);
+       if (!name) {
+               mutex_unlock(&irq_domain_mutex);
+               return;
+       }
+
+       debugfs_remove_domain_dir(domain);
+
+       if (domain->flags & IRQ_DOMAIN_NAME_ALLOCATED)
+               kfree(domain->name);
+       else
+               domain->flags |= IRQ_DOMAIN_NAME_ALLOCATED;
+
+       domain->name = name;
+       debugfs_add_domain_dir(domain);
+
+       mutex_unlock(&irq_domain_mutex);
+}
+
 /**
  * irq_domain_add_simple() - Register an irq_domain and optionally map a range of irqs
  * @of_node: pointer to interrupt controller's device tree node.
@@ -344,6 +464,7 @@ void irq_domain_disassociate(struct irq_domain *domain, unsigned int irq)
 
        irq_data->domain = NULL;
        irq_data->hwirq = 0;
+       domain->mapcount--;
 
        /* Clear reverse map for this hwirq */
        if (hwirq < domain->revmap_size) {
@@ -395,6 +516,7 @@ int irq_domain_associate(struct irq_domain *domain, unsigned int virq,
                        domain->name = irq_data->chip->name;
        }
 
+       domain->mapcount++;
        if (hwirq < domain->revmap_size) {
                domain->linear_revmap[hwirq] = virq;
        } else {
@@ -746,13 +868,54 @@ unsigned int irq_find_mapping(struct irq_domain *domain,
 EXPORT_SYMBOL_GPL(irq_find_mapping);
 
 #ifdef CONFIG_IRQ_DOMAIN_DEBUG
+static void virq_debug_show_one(struct seq_file *m, struct irq_desc *desc)
+{
+       struct irq_domain *domain;
+       struct irq_data *data;
+
+       domain = desc->irq_data.domain;
+       data = &desc->irq_data;
+
+       while (domain) {
+               unsigned int irq = data->irq;
+               unsigned long hwirq = data->hwirq;
+               struct irq_chip *chip;
+               bool direct;
+
+               if (data == &desc->irq_data)
+                       seq_printf(m, "%5d  ", irq);
+               else
+                       seq_printf(m, "%5d+ ", irq);
+               seq_printf(m, "0x%05lx  ", hwirq);
+
+               chip = irq_data_get_irq_chip(data);
+               seq_printf(m, "%-15s  ", (chip && chip->name) ? chip->name : "none");
+
+               seq_printf(m, data ? "0x%p  " : "  %p  ",
+                          irq_data_get_irq_chip_data(data));
+
+               seq_printf(m, "   %c    ", (desc->action && desc->action->handler) ? '*' : ' ');
+               direct = (irq == hwirq) && (irq < domain->revmap_direct_max_irq);
+               seq_printf(m, "%6s%-8s  ",
+                          (hwirq < domain->revmap_size) ? "LINEAR" : "RADIX",
+                          direct ? "(DIRECT)" : "");
+               seq_printf(m, "%s\n", domain->name);
+#ifdef CONFIG_IRQ_DOMAIN_HIERARCHY
+               domain = domain->parent;
+               data = data->parent_data;
+#else
+               domain = NULL;
+#endif
+       }
+}
+
 static int virq_debug_show(struct seq_file *m, void *private)
 {
        unsigned long flags;
        struct irq_desc *desc;
        struct irq_domain *domain;
        struct radix_tree_iter iter;
-       void *data, **slot;
+       void **slot;
        int i;
 
        seq_printf(m, " %-16s  %-6s  %-10s  %-10s  %s\n",
@@ -760,15 +923,26 @@ static int virq_debug_show(struct seq_file *m, void *private)
        mutex_lock(&irq_domain_mutex);
        list_for_each_entry(domain, &irq_domain_list, link) {
                struct device_node *of_node;
+               const char *name;
+
                int count = 0;
+
                of_node = irq_domain_get_of_node(domain);
+               if (of_node)
+                       name = of_node_full_name(of_node);
+               else if (is_fwnode_irqchip(domain->fwnode))
+                       name = container_of(domain->fwnode, struct irqchip_fwid,
+                                           fwnode)->name;
+               else
+                       name = "";
+
                radix_tree_for_each_slot(slot, &domain->revmap_tree, &iter, 0)
                        count++;
                seq_printf(m, "%c%-16s  %6u  %10u  %10u  %s\n",
                           domain == irq_default_domain ? '*' : ' ', domain->name,
                           domain->revmap_size + count, domain->revmap_size,
                           domain->revmap_direct_max_irq,
-                          of_node ? of_node_full_name(of_node) : "");
+                          name);
        }
        mutex_unlock(&irq_domain_mutex);
 
@@ -782,30 +956,7 @@ static int virq_debug_show(struct seq_file *m, void *private)
                        continue;
 
                raw_spin_lock_irqsave(&desc->lock, flags);
-               domain = desc->irq_data.domain;
-
-               if (domain) {
-                       struct irq_chip *chip;
-                       int hwirq = desc->irq_data.hwirq;
-                       bool direct;
-
-                       seq_printf(m, "%5d  ", i);
-                       seq_printf(m, "0x%05x  ", hwirq);
-
-                       chip = irq_desc_get_chip(desc);
-                       seq_printf(m, "%-15s  ", (chip && chip->name) ? chip->name : "none");
-
-                       data = irq_desc_get_chip_data(desc);
-                       seq_printf(m, data ? "0x%p  " : "  %p  ", data);
-
-                       seq_printf(m, "   %c    ", (desc->action && desc->action->handler) ? '*' : ' ');
-                       direct = (i == hwirq) && (i < domain->revmap_direct_max_irq);
-                       seq_printf(m, "%6s%-8s  ",
-                                  (hwirq < domain->revmap_size) ? "LINEAR" : "RADIX",
-                                  direct ? "(DIRECT)" : "");
-                       seq_printf(m, "%s\n", desc->irq_data.domain->name);
-               }
-
+               virq_debug_show_one(m, desc);
                raw_spin_unlock_irqrestore(&desc->lock, flags);
        }
 
@@ -973,6 +1124,7 @@ static void irq_domain_insert_irq(int virq)
                struct irq_domain *domain = data->domain;
                irq_hw_number_t hwirq = data->hwirq;
 
+               domain->mapcount++;
                if (hwirq < domain->revmap_size) {
                        domain->linear_revmap[hwirq] = virq;
                } else {
@@ -1002,6 +1154,7 @@ static void irq_domain_remove_irq(int virq)
                struct irq_domain *domain = data->domain;
                irq_hw_number_t hwirq = data->hwirq;
 
+               domain->mapcount--;
                if (hwirq < domain->revmap_size) {
                        domain->linear_revmap[hwirq] = 0;
                } else {
@@ -1189,43 +1342,18 @@ void irq_domain_free_irqs_top(struct irq_domain *domain, unsigned int virq,
        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,
+static void irq_domain_free_irqs_hierarchy(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);
-       }
 }
 
-int irq_domain_alloc_irqs_recursive(struct irq_domain *domain,
+int irq_domain_alloc_irqs_hierarchy(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)
-               return ret;
-
-       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;
+       return domain->ops->alloc(domain, irq_base, nr_irqs, arg);
 }
 
 /**
@@ -1286,7 +1414,7 @@ int __irq_domain_alloc_irqs(struct irq_domain *domain, int irq_base,
        }
 
        mutex_lock(&irq_domain_mutex);
-       ret = irq_domain_alloc_irqs_recursive(domain, virq, nr_irqs, arg);
+       ret = irq_domain_alloc_irqs_hierarchy(domain, virq, nr_irqs, arg);
        if (ret < 0) {
                mutex_unlock(&irq_domain_mutex);
                goto out_free_irq_data;
@@ -1321,7 +1449,7 @@ void irq_domain_free_irqs(unsigned int virq, unsigned int nr_irqs)
        mutex_lock(&irq_domain_mutex);
        for (i = 0; i < nr_irqs; i++)
                irq_domain_remove_irq(virq + i);
-       irq_domain_free_irqs_recursive(data->domain, virq, nr_irqs);
+       irq_domain_free_irqs_hierarchy(data->domain, virq, nr_irqs);
        mutex_unlock(&irq_domain_mutex);
 
        irq_domain_free_irq_data(virq, nr_irqs);
@@ -1341,15 +1469,11 @@ 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;
+       if (!domain->parent)
+               return -ENOSYS;
 
-       domain = domain->parent;
-       if (domain)
-               return irq_domain_alloc_irqs_recursive(domain, irq_base,
-                                                      nr_irqs, arg);
-       return -ENOSYS;
+       return irq_domain_alloc_irqs_hierarchy(domain->parent, irq_base,
+                                              nr_irqs, arg);
 }
 EXPORT_SYMBOL_GPL(irq_domain_alloc_irqs_parent);
 
@@ -1364,10 +1488,10 @@ EXPORT_SYMBOL_GPL(irq_domain_alloc_irqs_parent);
 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);
+       if (!domain->parent)
+               return;
+
+       irq_domain_free_irqs_hierarchy(domain->parent, irq_base, nr_irqs);
 }
 EXPORT_SYMBOL_GPL(irq_domain_free_irqs_parent);
 
@@ -1487,3 +1611,77 @@ static void irq_domain_check_hierarchy(struct irq_domain *domain)
 {
 }
 #endif /* CONFIG_IRQ_DOMAIN_HIERARCHY */
+
+#ifdef CONFIG_GENERIC_IRQ_DEBUGFS
+static struct dentry *domain_dir;
+
+static void
+irq_domain_debug_show_one(struct seq_file *m, struct irq_domain *d, int ind)
+{
+       seq_printf(m, "%*sname:   %s\n", ind, "", d->name);
+       seq_printf(m, "%*ssize:   %u\n", ind + 1, "",
+                  d->revmap_size + d->revmap_direct_max_irq);
+       seq_printf(m, "%*smapped: %u\n", ind + 1, "", d->mapcount);
+       seq_printf(m, "%*sflags:  0x%08x\n", ind +1 , "", d->flags);
+#ifdef CONFIG_IRQ_DOMAIN_HIERARCHY
+       if (!d->parent)
+               return;
+       seq_printf(m, "%*sparent: %s\n", ind + 1, "", d->parent->name);
+       irq_domain_debug_show_one(m, d->parent, ind + 4);
+#endif
+}
+
+static int irq_domain_debug_show(struct seq_file *m, void *p)
+{
+       struct irq_domain *d = m->private;
+
+       /* Default domain? Might be NULL */
+       if (!d) {
+               if (!irq_default_domain)
+                       return 0;
+               d = irq_default_domain;
+       }
+       irq_domain_debug_show_one(m, d, 0);
+       return 0;
+}
+
+static int irq_domain_debug_open(struct inode *inode, struct file *file)
+{
+       return single_open(file, irq_domain_debug_show, inode->i_private);
+}
+
+static const struct file_operations dfs_domain_ops = {
+       .open           = irq_domain_debug_open,
+       .read           = seq_read,
+       .llseek         = seq_lseek,
+       .release        = single_release,
+};
+
+static void debugfs_add_domain_dir(struct irq_domain *d)
+{
+       if (!d->name || !domain_dir || d->debugfs_file)
+               return;
+       d->debugfs_file = debugfs_create_file(d->name, 0444, domain_dir, d,
+                                             &dfs_domain_ops);
+}
+
+static void debugfs_remove_domain_dir(struct irq_domain *d)
+{
+       debugfs_remove(d->debugfs_file);
+}
+
+void __init irq_domain_debugfs_init(struct dentry *root)
+{
+       struct irq_domain *d;
+
+       domain_dir = debugfs_create_dir("domains", root);
+       if (!domain_dir)
+               return;
+
+       debugfs_create_file("default", 0444, domain_dir, NULL, &dfs_domain_ops);
+       mutex_lock(&irq_domain_mutex);
+       list_for_each_entry(d, &irq_domain_list, link)
+               debugfs_add_domain_dir(d);
+       mutex_unlock(&irq_domain_mutex);
+}
+#endif