@@ -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);
@@ -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_validate_new_type(unsigned int curr, unsigned new)
+{
+ return (curr == DT_IRQ_TYPE_INVALID || curr == new );
+}
+
+int irq_set_spi_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_validate_new_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_validate_new_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_spi_type(irq, type);
+
+ if ( res )
+ return -1;
+
+ return irq;
+}
+
/*
* Local variables:
* mode: 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();
@@ -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);
@@ -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_spi_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:
@@ -135,6 +135,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
@@ -147,6 +148,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
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@linaro.org> --- Changes in v7: - Rename irq_check_type into irq_validate_new_type - Rename irq_set_type into irq_set_spi_type 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(-)