[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[Xen-devel] [PATCH v6 1/6] xen/arm: IRQ: Store IRQ type in arch_irq_desc



For now, ARM uses different IRQ functions to setup an interrupt handler. This
is a bit annoying for common driver because we have to add idefery when
an IRQ is setup (see ns16550_init_postirq for an example).

To avoid to completely fork the IRQ management code, we can introduce a field
to store the IRQ type (e.g level/edge ...).

This patch also adds platform_get_irq which will retrieve the IRQ from the
device tree and setup correctly the IRQ type.

In order to use this solution, we have to move init_IRQ earlier for the boot
CPU. It's fine because the code only depends on percpu.

Signed-off-by: Julien Grall <julien.grall@xxxxxxxxxx>

---
    Changes in v6:
        - Introduce DT_IRQ_TYPE_INVALID to initialize IRQ type
        - Introduce local_irqs_type to avoid use boot CPU PPIs type
        config. Will be usefull for hotplug
        - Export irq_set_type and restrict for SPIs
        - Split IRQ set type for PPIs in another function

    Changes in v5:
        - Update comment in init_local_irq_data
        - Don't set desc.arch.type on boot cpu in init_local_irq_data
        - Use new macro boot_cpu instead of percpu(myvar, 0)

    Changes in v4:
        - Add an ASSERT to check if irq_set_type hasn't failed for PPI
        on other CPU than 0
        - platform_get_irq return -1 in case of error.

    Changes in v3:
        - irqflags is unsigned long not unsigned int
        - fix comment
        - don't need to set IRQ type when NONE is used
        (already set at startup).

    Changes in v2:
        - Patch added
---
 xen/arch/arm/gic.c            |   25 +++++----
 xen/arch/arm/irq.c            |  125 ++++++++++++++++++++++++++++++++++++++---
 xen/arch/arm/setup.c          |    3 +-
 xen/include/asm-arm/gic.h     |    5 +-
 xen/include/asm-arm/irq.h     |    6 ++
 xen/include/xen/device_tree.h |    3 +
 6 files changed, 146 insertions(+), 21 deletions(-)

diff --git a/xen/arch/arm/gic.c b/xen/arch/arm/gic.c
index 577d85b..13eeb33 100644
--- a/xen/arch/arm/gic.c
+++ b/xen/arch/arm/gic.c
@@ -216,14 +216,21 @@ static hw_irq_controller gic_guest_irq_type = {
 /*
  * - needs to be called with a valid cpu_mask, ie each cpu in the mask has
  * already called gic_cpu_init
+ * - desc.lock must be held
+ * - arch.type must be valid (i.e != DT_IRQ_TYPE_INVALID)
  */
-static void gic_set_irq_properties(unsigned int irq, bool_t level,
+static void gic_set_irq_properties(struct irq_desc *desc,
                                    const cpumask_t *cpu_mask,
                                    unsigned int priority)
 {
     volatile unsigned char *bytereg;
     uint32_t cfg, edgebit;
     unsigned int mask;
+    unsigned int irq = desc->irq;
+    unsigned int type = desc->arch.type;
+
+    ASSERT(type != DT_IRQ_TYPE_INVALID);
+    ASSERT(spin_is_locked(&desc->lock));
 
     spin_lock(&gic.lock);
 
@@ -232,9 +239,9 @@ static void gic_set_irq_properties(unsigned int irq, bool_t 
level,
     /* Set edge / level */
     cfg = GICD[GICD_ICFGR + irq / 16];
     edgebit = 2u << (2 * (irq % 16));
-    if ( level )
+    if ( type & DT_IRQ_TYPE_LEVEL_MASK )
         cfg &= ~edgebit;
-    else
+    else if ( type & DT_IRQ_TYPE_EDGE_BOTH )
         cfg |= edgebit;
     GICD[GICD_ICFGR + irq / 16] = cfg;
 
@@ -252,8 +259,8 @@ static void gic_set_irq_properties(unsigned int irq, bool_t 
level,
 /* Program the GIC to route an interrupt to the host (i.e. Xen)
  * - needs to be called with desc.lock held
  */
-void gic_route_irq_to_xen(struct irq_desc *desc, bool_t level,
-                          const cpumask_t *cpu_mask, unsigned int priority)
+void gic_route_irq_to_xen(struct irq_desc *desc, const cpumask_t *cpu_mask,
+                          unsigned int priority)
 {
     ASSERT(priority <= 0xff);     /* Only 8 bits of priority */
     ASSERT(desc->irq < gic.lines);/* Can't route interrupts that don't exist */
@@ -262,15 +269,14 @@ void gic_route_irq_to_xen(struct irq_desc *desc, bool_t 
level,
 
     desc->handler = &gic_host_irq_type;
 
-    gic_set_irq_properties(desc->irq, level, cpu_mask, priority);
+    gic_set_irq_properties(desc, cpu_mask, priority);
 }
 
 /* Program the GIC to route an interrupt to a guest
  *   - desc.lock must be held
  */
 void gic_route_irq_to_guest(struct domain *d, struct irq_desc *desc,
-                            bool_t level, const cpumask_t *cpu_mask,
-                            unsigned int priority)
+                            const cpumask_t *cpu_mask, unsigned int priority)
 {
     struct pending_irq *p;
     ASSERT(spin_is_locked(&desc->lock));
@@ -278,8 +284,7 @@ void gic_route_irq_to_guest(struct domain *d, struct 
irq_desc *desc,
     desc->handler = &gic_guest_irq_type;
     desc->status |= IRQ_GUEST;
 
-    gic_set_irq_properties(desc->irq, level, cpumask_of(smp_processor_id()),
-                           GIC_PRI_IRQ);
+    gic_set_irq_properties(desc, cpumask_of(smp_processor_id()), GIC_PRI_IRQ);
 
     /* TODO: do not assume delivery to vcpu0 */
     p = irq_to_pending(d->vcpu[0], desc->irq);
diff --git a/xen/arch/arm/irq.c b/xen/arch/arm/irq.c
index 44696e7..0b67089 100644
--- a/xen/arch/arm/irq.c
+++ b/xen/arch/arm/irq.c
@@ -27,6 +27,9 @@
 
 #include <asm/gic.h>
 
+static unsigned int local_irqs_type[NR_LOCAL_IRQS];
+static DEFINE_SPINLOCK(local_irqs_type_lock);
+
 static void ack_none(struct irq_desc *irq)
 {
     printk("unexpected IRQ trap at irq %02x\n", irq->irq);
@@ -55,6 +58,7 @@ irq_desc_t *__irq_to_desc(int irq)
 
 int __init arch_init_one_irq_desc(struct irq_desc *desc)
 {
+    desc->arch.type = DT_IRQ_TYPE_INVALID;
     return 0;
 }
 
@@ -77,18 +81,37 @@ static int __cpuinit init_local_irq_data(void)
 {
     int irq;
 
+    spin_lock(&local_irqs_type_lock);
+
     for (irq = 0; irq < NR_LOCAL_IRQS; irq++) {
         struct irq_desc *desc = irq_to_desc(irq);
         init_one_irq_desc(desc);
         desc->irq = irq;
         desc->action  = NULL;
+
+        /* PPIs are included in local_irqs, we copy the IRQ type from
+         * local_irqs_type when bringing up local IRQ for this CPU in
+         * order to pick up any configuration done before this CPU came
+         * up. For interrupts configured after this point this is done in
+         * irq_set_type.
+         */
+        desc->arch.type = local_irqs_type[irq];
     }
 
+    spin_unlock(&local_irqs_type_lock);
+
     return 0;
 }
 
 void __init init_IRQ(void)
 {
+    int irq;
+
+    spin_lock(&local_irqs_type_lock);
+    for ( irq = 0; irq < NR_LOCAL_IRQS; irq++ )
+        local_irqs_type[irq] = DT_IRQ_TYPE_INVALID;
+    spin_unlock(&local_irqs_type_lock);
+
     BUG_ON(init_local_irq_data() < 0);
     BUG_ON(init_irq_data() < 0);
 }
@@ -275,9 +298,6 @@ int setup_dt_irq(const struct dt_irq *irq, struct irqaction 
*new)
     /* First time the IRQ is setup */
     if ( disabled )
     {
-        bool_t level;
-
-        level = dt_irq_is_level_triggered(irq);
         /* It's fine to use smp_processor_id() because:
          * For PPI: irq_desc is banked
          * For SPI: we don't care for now which CPU will receive the
@@ -285,7 +305,8 @@ int setup_dt_irq(const struct dt_irq *irq, struct irqaction 
*new)
          * TODO: Handle case where SPI is setup on different CPU than
          * the targeted CPU and the priority.
          */
-        gic_route_irq_to_xen(desc, level, cpumask_of(smp_processor_id()),
+        desc->arch.type = irq->type;
+        gic_route_irq_to_xen(desc, cpumask_of(smp_processor_id()),
                              GIC_PRI_IRQ);
         desc->handler->startup(desc);
     }
@@ -303,7 +324,6 @@ int route_dt_irq_to_guest(struct domain *d, const struct 
dt_irq *irq,
     struct irq_desc *desc = irq_to_desc(irq->irq);
     unsigned long flags;
     int retval = 0;
-    bool_t level;
 
     action = xmalloc(struct irqaction);
     if (!action)
@@ -341,8 +361,8 @@ int route_dt_irq_to_guest(struct domain *d, const struct 
dt_irq *irq,
     if ( retval )
         goto out;
 
-    level = dt_irq_is_level_triggered(irq);
-    gic_route_irq_to_guest(d, desc, level, cpumask_of(smp_processor_id()),
+    desc->arch.type = irq->type;
+    gic_route_irq_to_guest(d, desc, cpumask_of(smp_processor_id()),
                            GIC_PRI_IRQ);
     spin_unlock_irqrestore(&desc->lock, flags);
     return 0;
@@ -383,6 +403,97 @@ void pirq_set_affinity(struct domain *d, int pirq, const 
cpumask_t *mask)
     BUG();
 }
 
+static bool_t irq_check_type(unsigned int curr, unsigned new)
+{
+    return (curr == DT_IRQ_TYPE_INVALID || curr == new );
+}
+
+int irq_set_type(unsigned int spi, unsigned int type)
+{
+    unsigned long flags;
+    struct irq_desc *desc = irq_to_desc(spi);
+    int ret = -EBUSY;
+
+    /* This function should not be used for other than SPIs */
+    if ( spi < NR_LOCAL_IRQS )
+        return -EINVAL;
+
+    spin_lock_irqsave(&desc->lock, flags);
+
+    if ( !irq_check_type(desc->arch.type, type) )
+        goto err;
+
+    desc->arch.type = type;
+
+    ret = 0;
+
+err:
+    spin_unlock_irqrestore(&desc->lock, flags);
+    return ret;
+}
+
+static int irq_local_set_type(unsigned int irq, unsigned int type)
+{
+    unsigned int cpu;
+    unsigned int old_type;
+    unsigned long flags;
+    int ret = -EBUSY;
+    struct irq_desc *desc;
+
+    ASSERT(irq < NR_LOCAL_IRQS);
+
+    spin_lock(&local_irqs_type_lock);
+
+    old_type = local_irqs_type[irq];
+
+    if ( !irq_check_type(old_type, type) )
+        goto unlock;
+
+    ret = 0;
+    /* We don't need to reconfigure if the type is correctly set */
+    if ( old_type == type )
+        goto unlock;
+
+    local_irqs_type[irq] = type;
+
+    for_each_cpu( cpu, &cpu_online_map )
+    {
+        desc = &per_cpu(local_irq_desc, cpu)[irq];
+        spin_lock_irqsave(&desc->lock, flags);
+        desc->arch.type = type;
+        spin_unlock_irqrestore(&desc->lock, flags);
+    }
+
+unlock:
+    spin_unlock(&local_irqs_type_lock);
+    return ret;
+}
+
+int platform_get_irq(const struct dt_device_node *device, int index)
+{
+    struct dt_irq dt_irq;
+    unsigned int type, irq;
+    int res;
+
+    res = dt_device_get_irq(device, index, &dt_irq);
+    if ( res )
+        return -1;
+
+    irq = dt_irq.irq;
+    type = dt_irq.type;
+
+    /* Setup the IRQ type */
+    if ( irq < NR_LOCAL_IRQS )
+        res = irq_local_set_type(irq, type);
+    else
+        res = irq_set_type(irq, type);
+
+    if ( res )
+            return -1;
+
+    return irq;
+}
+
 /*
  * Local variables:
  * mode: C
diff --git a/xen/arch/arm/setup.c b/xen/arch/arm/setup.c
index dec5950..40ba26e 100644
--- a/xen/arch/arm/setup.c
+++ b/xen/arch/arm/setup.c
@@ -688,6 +688,8 @@ void __init start_xen(unsigned long boot_phys_offset,
     dt_unflatten_host_device_tree();
     dt_irq_xlate = gic_irq_xlate;
 
+    init_IRQ();
+
     dt_uart_init();
     console_init_preirq();
 
@@ -717,7 +719,6 @@ void __init start_xen(unsigned long boot_phys_offset,
 
     tasklet_subsys_init();
 
-    init_IRQ();
 
     xsm_dt_init();
 
diff --git a/xen/include/asm-arm/gic.h b/xen/include/asm-arm/gic.h
index b750b17..80f8dd2 100644
--- a/xen/include/asm-arm/gic.h
+++ b/xen/include/asm-arm/gic.h
@@ -168,11 +168,10 @@ extern void vgic_clear_pending_irqs(struct vcpu *v);
 extern struct pending_irq *irq_to_pending(struct vcpu *v, unsigned int irq);
 
 /* Program the GIC to route an interrupt */
-extern void gic_route_irq_to_xen(struct irq_desc *desc, bool_t level,
-                                 const cpumask_t *cpu_mask,
+extern void gic_route_irq_to_xen(struct irq_desc *desc, const cpumask_t 
*cpu_mask,
                                  unsigned int priority);
 extern void gic_route_irq_to_guest(struct domain *, struct irq_desc *desc,
-                                   bool_t level, const cpumask_t *cpu_mask,
+                                   const cpumask_t *cpu_mask,
                                    unsigned int priority);
 
 extern void gic_inject(void);
diff --git a/xen/include/asm-arm/irq.h b/xen/include/asm-arm/irq.h
index b52c26f..e791ed1 100644
--- a/xen/include/asm-arm/irq.h
+++ b/xen/include/asm-arm/irq.h
@@ -16,6 +16,7 @@ struct arch_pirq
 
 struct arch_irq_desc {
     int eoi_cpu;
+    unsigned int type;
 };
 
 #define NR_LOCAL_IRQS  32
@@ -47,6 +48,11 @@ int setup_dt_irq(const struct dt_irq *irq, struct irqaction 
*new);
 int route_dt_irq_to_guest(struct domain *d, const struct dt_irq *irq,
                           const char *devname);
 
+/* Set IRQ type for an SPI */
+int irq_set_type(unsigned int spi, unsigned int type);
+
+int platform_get_irq(const struct dt_device_node *device, int index);
+
 #endif /* _ASM_HW_IRQ_H */
 /*
  * Local variables:
diff --git a/xen/include/xen/device_tree.h b/xen/include/xen/device_tree.h
index 5542958..e677da9 100644
--- a/xen/include/xen/device_tree.h
+++ b/xen/include/xen/device_tree.h
@@ -131,6 +131,7 @@ struct dt_phandle_args {
  * DT_IRQ_TYPE_LEVEL_LOW       - low level triggered
  * DT_IRQ_TYPE_LEVEL_MASK      - Mask to filter out the level bits
  * DT_IRQ_TYPE_SENSE_MASK      - Mask for all the above bits
+ * DT_IRQ_TYPE_INVALID         - Use to initialize the type
  */
 #define DT_IRQ_TYPE_NONE           0x00000000
 #define DT_IRQ_TYPE_EDGE_RISING    0x00000001
@@ -143,6 +144,8 @@ struct dt_phandle_args {
     (DT_IRQ_TYPE_LEVEL_LOW | DT_IRQ_TYPE_LEVEL_HIGH)
 #define DT_IRQ_TYPE_SENSE_MASK     0x0000000f
 
+#define DT_IRQ_TYPE_INVALID        0x00000010
+
 /**
  * dt_irq - describe an IRQ in the device tree
  * @irq: IRQ number
-- 
1.7.10.4


_______________________________________________
Xen-devel mailing list
Xen-devel@xxxxxxxxxxxxx
http://lists.xen.org/xen-devel


 


Rackspace

Lists.xenproject.org is hosted with RackSpace, monitoring our
servers 24x7x365 and backed by RackSpace's Fanatical Support®.