]> git.kernelconcepts.de Git - karo-tx-linux.git/commitdiff
Merge branch 'linus' into irq/core
authorThomas Gleixner <tglx@linutronix.de>
Tue, 20 Jun 2017 20:08:32 +0000 (22:08 +0200)
committerThomas Gleixner <tglx@linutronix.de>
Tue, 20 Jun 2017 20:08:32 +0000 (22:08 +0200)
Get upstream changes so pending patches won't conflict.

Documentation/IRQ-domain.txt
include/linux/irq.h
kernel/irq/chip.c
kernel/irq/irqdesc.c
kernel/irq/irqdomain.c
kernel/irq/manage.c
kernel/irq/msi.c

index 82001a25a14bd7478c8aace88bc063ab3d0b87bf..1f246eb25ca546acdbb9d38cd6ad5b7628fa66cd 100644 (file)
@@ -231,5 +231,42 @@ needs to:
 4) No need to implement irq_domain_ops.map and irq_domain_ops.unmap,
    they are unused with hierarchy irq_domain.
 
-Hierarchy irq_domain may also be used to support other architectures,
-such as ARM, ARM64 etc.
+Hierarchy irq_domain is in no way x86 specific, and is heavily used to
+support other architectures, such as ARM, ARM64 etc.
+
+=== Debugging ===
+
+If you switch on CONFIG_IRQ_DOMAIN_DEBUG (which depends on
+CONFIG_IRQ_DOMAIN and CONFIG_DEBUG_FS), you will find a new file in
+your debugfs mount point, called irq_domain_mapping. This file
+contains a live snapshot of all the IRQ domains in the system:
+
+ name              mapped  linear-max  direct-max  devtree-node
+ pl061                  8           8           0  /smb/gpio@e0080000
+ pl061                  8           8           0  /smb/gpio@e1050000
+ pMSI                   0           0           0  /interrupt-controller@e1101000/v2m@e0080000
+ MSI                   37           0           0  /interrupt-controller@e1101000/v2m@e0080000
+ GICv2m                37           0           0  /interrupt-controller@e1101000/v2m@e0080000
+ GICv2                448         448           0  /interrupt-controller@e1101000
+
+it also iterates over the interrupts to display their mapping in the
+domains, and makes the domain stacking visible:
+
+
+irq    hwirq    chip name        chip data           active  type            domain
+    1  0x00019  GICv2            0xffff00000916bfd8     *    LINEAR          GICv2
+    2  0x0001d  GICv2            0xffff00000916bfd8          LINEAR          GICv2
+    3  0x0001e  GICv2            0xffff00000916bfd8     *    LINEAR          GICv2
+    4  0x0001b  GICv2            0xffff00000916bfd8     *    LINEAR          GICv2
+    5  0x0001a  GICv2            0xffff00000916bfd8          LINEAR          GICv2
+[...]
+   96  0x81808  MSI              0x          (null)           RADIX          MSI
+   96+ 0x00063  GICv2m           0xffff8003ee116980           RADIX          GICv2m
+   96+ 0x00063  GICv2            0xffff00000916bfd8          LINEAR          GICv2
+   97  0x08800  MSI              0x          (null)     *     RADIX          MSI
+   97+ 0x00064  GICv2m           0xffff8003ee116980     *     RADIX          GICv2m
+   97+ 0x00064  GICv2            0xffff00000916bfd8     *    LINEAR          GICv2
+
+Here, interrupts 1-5 are only using a single domain, while 96 and 97
+are build out of a stack of three domain, each level performing a
+particular function.
index f887351aa80e787ddde2883f215cded0d18b6274..94d1ad6ffdd4f08198f3787f02f9a843e4981ef5 100644 (file)
@@ -216,6 +216,7 @@ enum {
        IRQD_WAKEUP_ARMED               = (1 << 19),
        IRQD_FORWARDED_TO_VCPU          = (1 << 20),
        IRQD_AFFINITY_MANAGED           = (1 << 21),
+       IRQD_IRQ_STARTED                = (1 << 22),
 };
 
 #define __irqd_to_state(d) ACCESS_PRIVATE((d)->common, state_use_accessors)
@@ -329,6 +330,11 @@ static inline void irqd_clr_activated(struct irq_data *d)
        __irqd_to_state(d) &= ~IRQD_ACTIVATED;
 }
 
+static inline bool irqd_is_started(struct irq_data *d)
+{
+       return __irqd_to_state(d) & IRQD_IRQ_STARTED;
+}
+
 #undef __irqd_to_state
 
 static inline irq_hw_number_t irqd_to_hwirq(struct irq_data *d)
index c94da688ee9b30ff9e746372b20a0c30f4c8cf5f..bc1331f84fb5f11d5845ec191f89fe6fd62ad82c 100644 (file)
@@ -185,37 +185,64 @@ static void irq_state_set_masked(struct irq_desc *desc)
        irqd_set(&desc->irq_data, IRQD_IRQ_MASKED);
 }
 
+static void irq_state_clr_started(struct irq_desc *desc)
+{
+       irqd_clear(&desc->irq_data, IRQD_IRQ_STARTED);
+}
+
+static void irq_state_set_started(struct irq_desc *desc)
+{
+       irqd_set(&desc->irq_data, IRQD_IRQ_STARTED);
+}
+
 int irq_startup(struct irq_desc *desc, bool resend)
 {
        int ret = 0;
 
-       irq_state_clr_disabled(desc);
        desc->depth = 0;
 
-       irq_domain_activate_irq(&desc->irq_data);
-       if (desc->irq_data.chip->irq_startup) {
-               ret = desc->irq_data.chip->irq_startup(&desc->irq_data);
-               irq_state_clr_masked(desc);
-       } else {
+       if (irqd_is_started(&desc->irq_data)) {
                irq_enable(desc);
+       } else {
+               irq_domain_activate_irq(&desc->irq_data);
+               if (desc->irq_data.chip->irq_startup) {
+                       ret = desc->irq_data.chip->irq_startup(&desc->irq_data);
+                       irq_state_clr_disabled(desc);
+                       irq_state_clr_masked(desc);
+               } else {
+                       irq_enable(desc);
+               }
+               irq_state_set_started(desc);
        }
+
        if (resend)
                check_irq_resend(desc);
+
        return ret;
 }
 
+static void __irq_disable(struct irq_desc *desc, bool mask);
+
 void irq_shutdown(struct irq_desc *desc)
 {
-       irq_state_set_disabled(desc);
-       desc->depth = 1;
-       if (desc->irq_data.chip->irq_shutdown)
-               desc->irq_data.chip->irq_shutdown(&desc->irq_data);
-       else if (desc->irq_data.chip->irq_disable)
-               desc->irq_data.chip->irq_disable(&desc->irq_data);
-       else
-               desc->irq_data.chip->irq_mask(&desc->irq_data);
+       if (irqd_is_started(&desc->irq_data)) {
+               desc->depth = 1;
+               if (desc->irq_data.chip->irq_shutdown) {
+                       desc->irq_data.chip->irq_shutdown(&desc->irq_data);
+                       irq_state_set_disabled(desc);
+                       irq_state_set_masked(desc);
+               } else {
+                       __irq_disable(desc, true);
+               }
+               irq_state_clr_started(desc);
+       }
+       /*
+        * This must be called even if the interrupt was never started up,
+        * because the activation can happen before the interrupt is
+        * available for request/startup. It has it's own state tracking so
+        * it's safe to call it unconditionally.
+        */
        irq_domain_deactivate_irq(&desc->irq_data);
-       irq_state_set_masked(desc);
 }
 
 void irq_enable(struct irq_desc *desc)
@@ -228,6 +255,17 @@ void irq_enable(struct irq_desc *desc)
        irq_state_clr_masked(desc);
 }
 
+static void __irq_disable(struct irq_desc *desc, bool mask)
+{
+       irq_state_set_disabled(desc);
+       if (desc->irq_data.chip->irq_disable) {
+               desc->irq_data.chip->irq_disable(&desc->irq_data);
+               irq_state_set_masked(desc);
+       } else if (mask) {
+               mask_irq(desc);
+       }
+}
+
 /**
  * irq_disable - Mark interrupt disabled
  * @desc:      irq descriptor which should be disabled
@@ -250,13 +288,7 @@ void irq_enable(struct irq_desc *desc)
  */
 void irq_disable(struct irq_desc *desc)
 {
-       irq_state_set_disabled(desc);
-       if (desc->irq_data.chip->irq_disable) {
-               desc->irq_data.chip->irq_disable(&desc->irq_data);
-               irq_state_set_masked(desc);
-       } else if (irq_settings_disable_unlazy(desc)) {
-               mask_irq(desc);
-       }
+       __irq_disable(desc, irq_settings_disable_unlazy(desc));
 }
 
 void irq_percpu_enable(struct irq_desc *desc, unsigned int cpu)
@@ -903,6 +935,13 @@ void irq_modify_status(unsigned int irq, unsigned long clr, unsigned long set)
 
        if (!desc)
                return;
+
+       /*
+        * Warn when a driver sets the no autoenable flag on an already
+        * active interrupt.
+        */
+       WARN_ON_ONCE(!desc->depth && (set & _IRQ_NOAUTOEN));
+
        irq_settings_clr_and_set(desc, clr, set);
 
        irqd_clear(&desc->irq_data, IRQD_NO_BALANCING | IRQD_PER_CPU |
index 00bb0aeea1d0b52ae5983aac8f7b039913bc9d45..09abce2ea8f0dab2651eb0a260795057357bc18d 100644 (file)
@@ -480,7 +480,8 @@ int __init early_irq_init(void)
 
        /* Let arch update nr_irqs and return the nr of preallocated irqs */
        initcnt = arch_probe_nr_irqs();
-       printk(KERN_INFO "NR_IRQS:%d nr_irqs:%d %d\n", NR_IRQS, nr_irqs, initcnt);
+       printk(KERN_INFO "NR_IRQS: %d, nr_irqs: %d, preallocated irqs: %d\n",
+              NR_IRQS, nr_irqs, initcnt);
 
        if (WARN_ON(nr_irqs > IRQ_BITMAP_BITS))
                nr_irqs = IRQ_BITMAP_BITS;
@@ -516,7 +517,7 @@ int __init early_irq_init(void)
 
        init_irq_default_affinity();
 
-       printk(KERN_INFO "NR_IRQS:%d\n", NR_IRQS);
+       printk(KERN_INFO "NR_IRQS: %d\n", NR_IRQS);
 
        desc = irq_desc;
        count = ARRAY_SIZE(irq_desc);
index 31805f237396bdfb6f4d72a906c0dcb957b6ceaf..70b9da72018ba0163d7df463f6847621e56636be 100644 (file)
@@ -746,13 +746,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 +801,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 +834,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);
        }
 
index 425170d4439be5926a63ef9be50c71c1dd5878a9..4c34696ca57587593ab4364205383d840d21d6d8 100644 (file)
@@ -533,9 +533,15 @@ void __enable_irq(struct irq_desc *desc)
                        goto err_out;
                /* Prevent probing on this irq: */
                irq_settings_set_noprobe(desc);
-               irq_enable(desc);
-               check_irq_resend(desc);
-               /* fall-through */
+               /*
+                * Call irq_startup() not irq_enable() here because the
+                * interrupt might be marked NOAUTOEN. So irq_startup()
+                * needs to be invoked when it gets enabled the first
+                * time. If it was already started up, then irq_startup()
+                * will invoke irq_enable() under the hood.
+                */
+               irq_startup(desc, true);
+               break;
        }
        default:
                desc->depth--;
@@ -1330,11 +1336,19 @@ __setup_irq(unsigned int irq, struct irq_desc *desc, struct irqaction *new)
                if (new->flags & IRQF_ONESHOT)
                        desc->istate |= IRQS_ONESHOT;
 
-               if (irq_settings_can_autoenable(desc))
+               if (irq_settings_can_autoenable(desc)) {
                        irq_startup(desc, true);
-               else
+               } else {
+                       /*
+                        * Shared interrupts do not go well with disabling
+                        * auto enable. The sharing interrupt might request
+                        * it while it's still disabled and then wait for
+                        * interrupts forever.
+                        */
+                       WARN_ON_ONCE(new->flags & IRQF_SHARED);
                        /* Undo nested disables: */
                        desc->depth = 1;
+               }
 
                /* Exclude IRQ from balancing if requested */
                if (new->flags & IRQF_NOBALANCING) {
index ddc2f5427f75c57e09bc2e5ba9d655fa35c6e288..fe4d48ec5bc4a708c42246bbe3b16e437c4683ec 100644 (file)
@@ -265,13 +265,19 @@ struct irq_domain *msi_create_irq_domain(struct fwnode_handle *fwnode,
                                         struct msi_domain_info *info,
                                         struct irq_domain *parent)
 {
+       struct irq_domain *domain;
+
        if (info->flags & MSI_FLAG_USE_DEF_DOM_OPS)
                msi_domain_update_dom_ops(info);
        if (info->flags & MSI_FLAG_USE_DEF_CHIP_OPS)
                msi_domain_update_chip_ops(info);
 
-       return irq_domain_create_hierarchy(parent, IRQ_DOMAIN_FLAG_MSI, 0,
-                                          fwnode, &msi_domain_ops, info);
+       domain = irq_domain_create_hierarchy(parent, IRQ_DOMAIN_FLAG_MSI, 0,
+                                            fwnode, &msi_domain_ops, info);
+       if (domain && info->chip && info->chip->name)
+               domain->name = info->chip->name;
+
+       return domain;
 }
 
 int msi_domain_prepare_irqs(struct irq_domain *domain, struct device *dev,