/*
* - 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);
/* 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;
/* 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 */
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));
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);
#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);
int __init arch_init_one_irq_desc(struct irq_desc *desc)
{
+ desc->arch.type = DT_IRQ_TYPE_INVALID;
return 0;
}
{
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);
}
/* 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
* 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);
}
struct irq_desc *desc = irq_to_desc(irq->irq);
unsigned long flags;
int retval = 0;
- bool_t level;
action = xmalloc(struct irqaction);
if (!action)
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;
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