[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index] [Xen-devel] [PATCH v3 07/16] xen/arm: segregate and split GIC low level functionality
From: Vijaya Kumar K <Vijaya.Kumar@xxxxxxxxxxxxxxxxxx> GIC driver contains both generic and hardware specific low level functionality in gic.c file. With this patch, low level functionality is moved to separate file gic-v2.c and generic code is kept in gic.c file Callbacks are registered by low level driver with generic driver and are called whereever required. The locking mechanism is now kept in generic code as is and hence it is upto generic driver to take proper locks before calling low level driver callbacks. This helps to separate generic and hardware functionality and implement future hardware version drivers. Signed-off-by: Vijaya Kumar K <Vijaya.Kumar@xxxxxxxxxxxxxxxxxx> --- xen/arch/arm/Makefile | 2 +- xen/arch/arm/gic-v2.c | 532 +++++++++++++++++++++++++++++++++++++ xen/arch/arm/gic.c | 423 +++++------------------------ xen/include/asm-arm/gic.h | 86 ++++++ xen/include/asm-arm/gic_v2_defs.h | 16 +- 5 files changed, 689 insertions(+), 370 deletions(-) diff --git a/xen/arch/arm/Makefile b/xen/arch/arm/Makefile index 63e0460..969ee52 100644 --- a/xen/arch/arm/Makefile +++ b/xen/arch/arm/Makefile @@ -10,7 +10,7 @@ obj-y += vpsci.o obj-y += domctl.o obj-y += sysctl.o obj-y += domain_build.o -obj-y += gic.o +obj-y += gic.o gic-v2.o obj-y += io.o obj-y += irq.o obj-y += kernel.o diff --git a/xen/arch/arm/gic-v2.c b/xen/arch/arm/gic-v2.c new file mode 100644 index 0000000..0c0fa86 --- /dev/null +++ b/xen/arch/arm/gic-v2.c @@ -0,0 +1,532 @@ +/* + * xen/arch/arm/gic-v2.c + * + * ARM Generic Interrupt Controller support v2 + * + * Tim Deegan <tim@xxxxxxx> + * Copyright (c) 2011 Citrix Systems. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include <xen/config.h> +#include <xen/lib.h> +#include <xen/init.h> +#include <xen/mm.h> +#include <xen/irq.h> +#include <xen/sched.h> +#include <xen/errno.h> +#include <xen/serial.h> +#include <xen/softirq.h> +#include <xen/list.h> +#include <xen/device_tree.h> +#include <asm/p2m.h> +#include <asm/domain.h> +#include <asm/platform.h> + +#include <asm/gic_v2_defs.h> +#include <asm/gic.h> + +/* Access to the GIC Distributor registers through the fixmap */ +#define GICD ((volatile uint32_t *) FIXMAP_ADDR(FIXMAP_GICD)) +#define GICC ((volatile uint32_t *) FIXMAP_ADDR(FIXMAP_GICC1)) +#define GICH ((volatile uint32_t *) FIXMAP_ADDR(FIXMAP_GICH)) + +/* Global state */ +static struct { + paddr_t dbase; /* Address of distributor registers */ + paddr_t cbase; /* Address of CPU interface registers */ + paddr_t hbase; /* Address of virtual interface registers */ + paddr_t vbase; /* Address of virtual cpu interface registers */ + unsigned int lines; /* Number of interrupts (SPIs + PPIs + SGIs) */ + struct dt_irq maintenance; /* IRQ maintenance */ + unsigned int cpus; +} gic; + +static struct gic_hw_operations gic_ops; + +static uint8_t nr_lrs; + +/* The GIC mapping of CPU interfaces does not necessarily match the + * logical CPU numbering. Let's use mapping as returned by the GIC + * itself + */ +static DEFINE_PER_CPU(u8, gic_cpu_id); + +/* Maximum cpu interface per GIC */ +#define NR_GIC_CPU_IF 8 + +static unsigned int gicv2_cpu_mask(const cpumask_t *cpumask) +{ + unsigned int cpu; + unsigned int mask = 0; + cpumask_t possible_mask; + + cpumask_and(&possible_mask, cpumask, &cpu_possible_map); + for_each_cpu(cpu, &possible_mask) + { + ASSERT(cpu < NR_GIC_CPU_IF); + mask |= per_cpu(gic_cpu_id, cpu); + } + + return mask; +} + +static void gicv2_save_state(struct vcpu *v) +{ + int i; + + /* No need for spinlocks here because interrupts are disabled around + * this call and it only accesses struct vcpu fields that cannot be + * accessed simultaneously by another pCPU. + */ + for ( i = 0; i < nr_lrs; i++ ) + v->arch.gic_lr[i] = GICH[GICH_LR + i]; + v->arch.gic_apr = GICH[GICH_APR]; + v->arch.gic_vmcr = GICH[GICH_VMCR]; + /* Disable until next VCPU scheduled */ + GICH[GICH_HCR] = 0; +} + +static void gicv2_restore_state(struct vcpu *v) +{ + int i; + + for ( i = 0; i < nr_lrs; i++ ) + GICH[GICH_LR + i] = v->arch.gic_lr[i]; + GICH[GICH_APR] = v->arch.gic_apr; + GICH[GICH_VMCR] = v->arch.gic_vmcr; + GICH[GICH_HCR] = GICH_HCR_EN; +} + +static void gicv2_dump_state(struct vcpu *v) +{ + int i; + if ( v == current ) + { + for ( i = 0; i < nr_lrs; i++ ) + printk(" HW_LR[%d]=%x\n", i, GICH[GICH_LR + i]); + } else { + for ( i = 0; i < nr_lrs; i++ ) + printk(" VCPU_LR[%d]=%x\n", i, v->arch.gic_lr[i]); + } +} + +static void gicv2_enable_irq(struct irq_desc *irqd) +{ + int irq = irqd->irq; + /* Enable routing */ + GICD[GICD_ISENABLER + irq / 32] = (1u << (irq % 32)); +} + +static void gicv2_disable_irq(struct irq_desc *irqd) +{ + int irq = irqd->irq; + /* Disable routing */ + GICD[GICD_ICENABLER + irq / 32] = (1u << (irq % 32)); +} + +static void gicv2_eoi_irq(struct irq_desc *irqd) +{ + int irq = irqd->irq; + /* Lower the priority */ + GICC[GICC_EOIR] = irq; +} + +static void gicv2_dir_irq(int irq) +{ + /* Deactivate */ + GICC[GICC_DIR] = irq; +} + +static unsigned int gicv2_ack_irq(void) +{ + return (GICC[GICC_IAR] & GICC_IA_IRQ); +} + +/* + * - needs to be called with gic_lock held + * - needs to be called with a valid cpu_mask, ie each cpu in the mask has + * already called gic_cpu_init + */ +static void gicv2_set_irq_properties(unsigned int irq, bool_t level, + const cpumask_t *cpu_mask, + unsigned int priority) +{ + volatile unsigned char *bytereg; + uint32_t cfg, edgebit; + unsigned int mask = gicv2_cpu_mask(cpu_mask); + + /* Set edge / level */ + cfg = GICD[GICD_ICFGR + irq / 16]; + edgebit = 2u << (2 * (irq % 16)); + if ( level ) + cfg &= ~edgebit; + else + cfg |= edgebit; + GICD[GICD_ICFGR + irq / 16] = cfg; + + /* Set target CPU mask (RAZ/WI on uniprocessor) */ + bytereg = (unsigned char *) (GICD + GICD_ITARGETSR); + bytereg[irq] = mask; + + /* Set priority */ + bytereg = (unsigned char *) (GICD + GICD_IPRIORITYR); + bytereg[irq] = priority; + +} + +static void __init gicv2_dist_init(void) +{ + uint32_t type; + uint32_t cpumask; + int i; + + cpumask = GICD[GICD_ITARGETSR] & 0xff; + cpumask |= cpumask << 8; + cpumask |= cpumask << 16; + + /* Disable the distributor */ + GICD[GICD_CTLR] = 0; + + type = GICD[GICD_TYPER]; + gic.lines = 32 * ((type & GICD_TYPE_LINES) + 1); + gic.cpus = 1 + ((type & GICD_TYPE_CPUS) >> 5); + printk("GIC: %d lines, %d cpu%s%s (IID %8.8x).\n", + gic.lines, gic.cpus, (gic.cpus == 1) ? "" : "s", + (type & GICD_TYPE_SEC) ? ", secure" : "", + GICD[GICD_IIDR]); + + gic_ops.nr_lines = gic.lines; + /* Default all global IRQs to level, active low */ + for ( i = 32; i < gic.lines; i += 16 ) + GICD[GICD_ICFGR + i / 16] = 0x0; + + /* Route all global IRQs to this CPU */ + for ( i = 32; i < gic.lines; i += 4 ) + GICD[GICD_ITARGETSR + i / 4] = cpumask; + + /* Default priority for global interrupts */ + for ( i = 32; i < gic.lines; i += 4 ) + GICD[GICD_IPRIORITYR + i / 4] = + GIC_PRI_IRQ<<24 | GIC_PRI_IRQ<<16 | GIC_PRI_IRQ<<8 | GIC_PRI_IRQ; + + /* Disable all global interrupts */ + for ( i = 32; i < gic.lines; i += 32 ) + GICD[GICD_ICENABLER + i / 32] = (uint32_t)~0ul; + + /* Turn on the distributor */ + GICD[GICD_CTLR] = GICD_CTL_ENABLE; +} + +static void __cpuinit gicv2_cpu_init(void) +{ + int i; + + this_cpu(gic_cpu_id) = GICD[GICD_ITARGETSR] & 0xff; + + /* The first 32 interrupts (PPI and SGI) are banked per-cpu, so + * even though they are controlled with GICD registers, they must + * be set up here with the other per-cpu state. */ + GICD[GICD_ICENABLER] = 0xffff0000; /* Disable all PPI */ + GICD[GICD_ISENABLER] = 0x0000ffff; /* Enable all SGI */ + /* Set SGI priorities */ + for ( i = 0; i < 16; i += 4 ) + GICD[GICD_IPRIORITYR + i / 4] = + GIC_PRI_IPI<<24 | GIC_PRI_IPI<<16 | GIC_PRI_IPI<<8 | GIC_PRI_IPI; + /* Set PPI priorities */ + for ( i = 16; i < 32; i += 4 ) + GICD[GICD_IPRIORITYR + i / 4] = + GIC_PRI_IRQ<<24 | GIC_PRI_IRQ<<16 | GIC_PRI_IRQ<<8 | GIC_PRI_IRQ; + + /* Local settings: interface controller */ + GICC[GICC_PMR] = 0xff; /* Don't mask by priority */ + GICC[GICC_BPR] = 0; /* Finest granularity of priority */ + GICC[GICC_CTLR] = GICC_CTL_ENABLE|GICC_CTL_EOI; /* Turn on delivery */ +} + +static void gicv2_cpu_disable(void) +{ + GICC[GICC_CTLR] = 0; +} + +static void __cpuinit gicv2_hyp_init(void) +{ + uint32_t vtr; + + vtr = GICH[GICH_VTR]; + nr_lrs = (vtr & GICH_VTR_NRLRGS) + 1; + gic_ops.nr_lrs = nr_lrs; + + GICH[GICH_MISR] = GICH_MISR_EOI; + update_cpu_lr_mask(); +} + +static void __cpuinit gicv2_hyp_disable(void) +{ + GICH[GICH_HCR] = 0; +} + +/* Set up the GIC */ +void __init gicv2_init(void) +{ + static const struct dt_device_match gic_ids[] __initconst = + { + DT_MATCH_GIC, + { /* sentinel */ }, + }; + struct dt_device_node *node; + int res; + + node = dt_find_interrupt_controller(gic_ids); + if ( !node ) + panic("Unable to find compatible GIC in the device tree"); + + dt_device_set_used_by(node, DOMID_XEN); + + res = dt_device_get_address(node, 0, &gic.dbase, NULL); + if ( res || !gic.dbase || (gic.dbase & ~PAGE_MASK) ) + panic("GIC: Cannot find a valid address for the distributor"); + + res = dt_device_get_address(node, 1, &gic.cbase, NULL); + if ( res || !gic.cbase || (gic.cbase & ~PAGE_MASK) ) + panic("GIC: Cannot find a valid address for the CPU"); + + res = dt_device_get_address(node, 2, &gic.hbase, NULL); + if ( res || !gic.hbase || (gic.hbase & ~PAGE_MASK) ) + panic("GIC: Cannot find a valid address for the hypervisor"); + + res = dt_device_get_address(node, 3, &gic.vbase, NULL); + if ( res || !gic.vbase || (gic.vbase & ~PAGE_MASK) ) + panic("GIC: Cannot find a valid address for the virtual CPU"); + + res = dt_device_get_irq(node, 0, &gic.maintenance); + if ( res ) + panic("GIC: Cannot find the maintenance IRQ"); + + /* Set the GIC as the primary interrupt controller */ + dt_interrupt_controller = node; + + /* TODO: Add check on distributor, cpu size */ + + printk("GIC initialization:\n" + " gic_dist_addr=%"PRIpaddr"\n" + " gic_cpu_addr=%"PRIpaddr"\n" + " gic_hyp_addr=%"PRIpaddr"\n" + " gic_vcpu_addr=%"PRIpaddr"\n" + " gic_maintenance_irq=%u\n", + gic.dbase, gic.cbase, gic.hbase, gic.vbase, + gic.maintenance.irq); + + if ( (gic.dbase & ~PAGE_MASK) || (gic.cbase & ~PAGE_MASK) || + (gic.hbase & ~PAGE_MASK) || (gic.vbase & ~PAGE_MASK) ) + panic("GIC interfaces not page aligned"); + + set_fixmap(FIXMAP_GICD, gic.dbase >> PAGE_SHIFT, DEV_SHARED); + BUILD_BUG_ON(FIXMAP_ADDR(FIXMAP_GICC1) != + FIXMAP_ADDR(FIXMAP_GICC2)-PAGE_SIZE); + set_fixmap(FIXMAP_GICC1, gic.cbase >> PAGE_SHIFT, DEV_SHARED); + if ( platform_has_quirk(PLATFORM_QUIRK_GIC_64K_STRIDE) ) + set_fixmap(FIXMAP_GICC2, (gic.cbase >> PAGE_SHIFT) + 0x10, DEV_SHARED); + else + set_fixmap(FIXMAP_GICC2, (gic.cbase >> PAGE_SHIFT) + 0x1, DEV_SHARED); + set_fixmap(FIXMAP_GICH, gic.hbase >> PAGE_SHIFT, DEV_SHARED); + + /* Global settings: interrupt distributor */ + + gicv2_dist_init(); + gicv2_cpu_init(); + gicv2_hyp_init(); + + gic_ops.hw_version = GIC_V2; + register_gic_ops(&gic_ops); +} + +static void gicv2_secondary_cpu_init(void) +{ + gicv2_cpu_init(); + gicv2_hyp_init(); +} + +static struct dt_irq * gicv2_maintenance_irq(void) +{ + return &gic.maintenance; +} + +static void gicv2_send_sgi(const cpumask_t *online_mask, enum gic_sgi sgi, uint8_t irqmode) +{ + unsigned int mask = 0; + + switch(irqmode) + { + case SGI_TARGET_OTHERS: + GICD[GICD_SGIR] = GICD_SGI_TARGET_OTHERS | sgi; + break; + case SGI_TARGET_SELF: + GICD[GICD_SGIR] = GICD_SGI_TARGET_SELF | sgi; + break; + case SGI_TARGET_LIST: + mask = gicv2_cpu_mask(online_mask); + GICD[GICD_SGIR] = GICD_SGI_TARGET_LIST | + (mask<<GICD_SGI_TARGET_SHIFT) | sgi; + break; + default: + gdprintk(XENLOG_WARNING, "Wrong sgi irq mode for sgi %x\n", sgi); + } +} + +/* Shut down the per-CPU GIC interface */ +static void gicv2_disable_interface(void) +{ + gicv2_cpu_disable(); + gicv2_hyp_disable(); +} + +static void gicv2_update_lr(int lr, struct pending_irq *p, unsigned int state) +{ + int maintenance_int = GICH_LR_MAINTENANCE_IRQ; + + BUG_ON(lr >= nr_lrs); + BUG_ON(lr < 0); + BUG_ON(state & ~(GICH_LR_STATE_MASK<<GICH_LR_STATE_SHIFT)); + + GICH[GICH_LR + lr] = ((state & 0x3) << GICH_LR_STATE_SHIFT) | + maintenance_int | + ((p->priority >> 3) << GICH_LR_PRIORITY_SHIFT) | + ((p->irq & GICH_LR_VIRTUAL_MASK) << GICH_LR_VIRTUAL_SHIFT); +} + +static void gicv2_clear_lr(int lr) +{ + GICH[GICH_LR + lr] = 0; +} + +static int gicv_v2_init(struct domain *d) +{ + int ret; + + /* + * Domain 0 gets the hardware address. + * Guests get the virtual platform layout. + */ + if ( d->domain_id == 0 ) + { + d->arch.vgic.dbase = gic.dbase; + d->arch.vgic.cbase = gic.cbase; + } + else + { + d->arch.vgic.dbase = GUEST_GICD_BASE; + d->arch.vgic.cbase = GUEST_GICC_BASE; + } + + d->arch.vgic.nr_lines = 0; + + /* + * Map the gic virtual cpu interface in the gic cpu interface + * region of the guest. + * + * The second page is always mapped at +4K irrespective of the + * GIC_64K_STRIDE quirk. The DTB passed to the guest reflects this. + */ + ret = map_mmio_regions(d, d->arch.vgic.cbase, + d->arch.vgic.cbase + PAGE_SIZE - 1, + gic.vbase); + if ( ret ) + return ret; + + if ( !platform_has_quirk(PLATFORM_QUIRK_GIC_64K_STRIDE) ) + ret = map_mmio_regions(d, d->arch.vgic.cbase + PAGE_SIZE, + d->arch.vgic.cbase + (2 * PAGE_SIZE) - 1, + gic.vbase + PAGE_SIZE); + else + ret = map_mmio_regions(d, d->arch.vgic.cbase + PAGE_SIZE, + d->arch.vgic.cbase + (2 * PAGE_SIZE) - 1, + gic.vbase + 16*PAGE_SIZE); + + return ret; + +} + +static void gicv2_read_lr(int lr, struct gic_lr *lr_reg) +{ + uint32_t lrv; + + lrv = GICH[GICH_LR + lr]; + lr_reg->pirq = (lrv >> GICH_LR_PHYSICAL_SHIFT) & GICH_LR_PHYSICAL_MASK; + lr_reg->virq = (lrv >> GICH_LR_VIRTUAL_SHIFT) & GICH_LR_VIRTUAL_MASK; + lr_reg->priority = (lrv >> GICH_LR_PRIORITY_SHIFT) & GICH_LR_PRIORITY_MASK; + lr_reg->state = (lrv >> GICH_LR_STATE_SHIFT) & GICH_LR_STATE_MASK; + lr_reg->hw_status = (lrv >> GICH_LR_HW_SHIFT) & GICH_LR_HW_MASK; + lr_reg->grp = (lrv >> GICH_LR_GRP_SHIFT) & GICH_LR_GRP_MASK; +} + +static void gicv2_write_lr(int lr, struct gic_lr *lr_reg) +{ + uint32_t lrv = 0; + lrv = ( ((lr_reg->pirq & GICH_LR_PHYSICAL_MASK) << GICH_LR_PHYSICAL_SHIFT) | + ((lr_reg->virq & GICH_LR_VIRTUAL_MASK) << GICH_LR_VIRTUAL_SHIFT) | + ((uint32_t)(lr_reg->priority & GICH_LR_PRIORITY_MASK) << GICH_LR_PRIORITY_SHIFT) | + ((uint32_t)(lr_reg->state & GICH_LR_STATE_MASK) << GICH_LR_STATE_SHIFT) | + ((uint32_t)(lr_reg->hw_status & GICH_LR_HW_MASK) << GICH_LR_HW_SHIFT) | + ((uint32_t)(lr_reg->grp & GICH_LR_GRP_MASK) << GICH_LR_GRP_SHIFT) ); + + GICH[GICH_LR + lr] = lrv; +} + +static void gicv2_hcr_status(uint32_t flag, bool_t status) +{ + if ( status ) + GICH[GICH_HCR] |= flag; + else + GICH[GICH_HCR] &= ~flag; +} + +static unsigned int gicv2_read_vmcr_priority(void) +{ + return (GICH[GICH_VMCR] >> GICH_VMCR_PRIORITY_SHIFT) & GICH_VMCR_PRIORITY_MASK; +} + +static hw_irq_controller irq_ops = { + .enable = gicv2_enable_irq, + .disable = gicv2_disable_irq, + .end = gicv2_eoi_irq, +}; + +static struct gic_hw_operations gic_ops = { + .secondary_init = gicv2_secondary_cpu_init, + .get_maintenance_irq = gicv2_maintenance_irq, + .save_state = gicv2_save_state, + .restore_state = gicv2_restore_state, + .dump_state = gicv2_dump_state, + .gicv_setup = gicv_v2_init, + .gic_irq_ops = &irq_ops, + .deactivate_irq = gicv2_dir_irq, + .ack_irq = gicv2_ack_irq, + .set_irq_property = gicv2_set_irq_properties, + .send_sgi = gicv2_send_sgi, + .disable_interface = gicv2_disable_interface, + .update_lr = gicv2_update_lr, + .update_hcr_status = gicv2_hcr_status, + .clear_lr = gicv2_clear_lr, + .read_lr = gicv2_read_lr, + .write_lr = gicv2_write_lr, + .read_vmcr_priority = gicv2_read_vmcr_priority, +}; + +/* + * Local variables: + * mode: C + * c-file-style: "BSD" + * c-basic-offset: 4 + * indent-tabs-mode: nil + * End: + */ diff --git a/xen/arch/arm/gic.c b/xen/arch/arm/gic.c index 7c9a408..ce21ef6 100644 --- a/xen/arch/arm/gic.c +++ b/xen/arch/arm/gic.c @@ -32,65 +32,39 @@ #include <asm/domain.h> #include <asm/platform.h> -#include <asm/gic_v2_defs.h> #include <asm/gic.h> static spinlock_t gic_lock; - -/* Access to the GIC Distributor registers through the fixmap */ -#define GICD ((volatile uint32_t *) FIXMAP_ADDR(FIXMAP_GICD)) -#define GICC ((volatile uint32_t *) FIXMAP_ADDR(FIXMAP_GICC1)) -#define GICH ((volatile uint32_t *) FIXMAP_ADDR(FIXMAP_GICH)) static void gic_restore_pending_irqs(struct vcpu *v); -/* Global state */ -static struct { - paddr_t dbase; /* Address of distributor registers */ - paddr_t cbase; /* Address of CPU interface registers */ - paddr_t hbase; /* Address of virtual interface registers */ - paddr_t vbase; /* Address of virtual cpu interface registers */ - unsigned int lines; /* Number of interrupts (SPIs + PPIs + SGIs) */ - struct dt_irq maintenance; /* IRQ maintenance */ - unsigned int cpus; -} gic; - static irq_desc_t irq_desc[NR_IRQS]; static DEFINE_PER_CPU(irq_desc_t[NR_LOCAL_IRQS], local_irq_desc); static DEFINE_PER_CPU(uint64_t, lr_mask); -static uint8_t nr_lrs; -#define lr_all_full() (this_cpu(lr_mask) == ((1 << nr_lrs) - 1)) - -/* The GIC mapping of CPU interfaces does not necessarily match the - * logical CPU numbering. Let's use mapping as returned by the GIC - * itself - */ -static DEFINE_PER_CPU(u8, gic_cpu_id); - -/* Maximum cpu interface per GIC */ -#define NR_GIC_CPU_IF 8 +#define lr_all_full() (this_cpu(lr_mask) == ((1 << gic_hw_ops->nr_lrs) - 1)) static void gic_clear_one_lr(struct vcpu *v, int i); -static unsigned int gic_cpu_mask(const cpumask_t *cpumask) +static const struct gic_hw_operations *gic_hw_ops; + +void register_gic_ops(const struct gic_hw_operations *ops) { - unsigned int cpu; - unsigned int mask = 0; - cpumask_t possible_mask; + gic_hw_ops = ops; +} - cpumask_and(&possible_mask, cpumask, &cpu_possible_map); - for_each_cpu(cpu, &possible_mask) - { - ASSERT(cpu < NR_GIC_CPU_IF); - mask |= per_cpu(gic_cpu_id, cpu); - } +void update_cpu_lr_mask(void) +{ + this_cpu(lr_mask) = 0ULL; +} - return mask; +int gic_hw_version(void) +{ + return gic_hw_ops->hw_version; } unsigned int gic_number_lines(void) { - return gic.lines; + return gic_hw_ops->nr_lines; } irq_desc_t *__irq_to_desc(int irq) @@ -101,37 +75,29 @@ irq_desc_t *__irq_to_desc(int irq) void gic_save_state(struct vcpu *v) { - int i; ASSERT(!local_irq_is_enabled()); + if ( is_idle_vcpu(v) ) + return; /* No need for spinlocks here because interrupts are disabled around * this call and it only accesses struct vcpu fields that cannot be * accessed simultaneously by another pCPU. */ - for ( i=0; i<nr_lrs; i++) - v->arch.gic_lr[i] = GICH[GICH_LR + i]; v->arch.lr_mask = this_cpu(lr_mask); - v->arch.gic_apr = GICH[GICH_APR]; - v->arch.gic_vmcr = GICH[GICH_VMCR]; - /* Disable until next VCPU scheduled */ - GICH[GICH_HCR] = 0; + gic_hw_ops->save_state(v); isb(); } void gic_restore_state(struct vcpu *v) { - int i; ASSERT(!local_irq_is_enabled()); if ( is_idle_vcpu(v) ) return; this_cpu(lr_mask) = v->arch.lr_mask; - for ( i=0; i<nr_lrs; i++) - GICH[GICH_LR + i] = v->arch.gic_lr[i]; - GICH[GICH_APR] = v->arch.gic_apr; - GICH[GICH_VMCR] = v->arch.gic_vmcr; - GICH[GICH_HCR] = GICH_HCR_EN; + gic_hw_ops->restore_state(v); + isb(); gic_restore_pending_irqs(v); @@ -139,7 +105,6 @@ void gic_restore_state(struct vcpu *v) static void gic_irq_enable(struct irq_desc *desc) { - int irq = desc->irq; unsigned long flags; spin_lock_irqsave(&desc->lock, flags); @@ -147,20 +112,19 @@ static void gic_irq_enable(struct irq_desc *desc) desc->status &= ~IRQ_DISABLED; dsb(); /* Enable routing */ - GICD[GICD_ISENABLER + irq / 32] = (1u << (irq % 32)); + gic_hw_ops->gic_irq_ops->enable(desc); spin_unlock(&gic_lock); spin_unlock_irqrestore(&desc->lock, flags); } static void gic_irq_disable(struct irq_desc *desc) { - int irq = desc->irq; unsigned long flags; spin_lock_irqsave(&desc->lock, flags); spin_lock(&gic_lock); /* Disable routing */ - GICD[GICD_ICENABLER + irq / 32] = (1u << (irq % 32)); + gic_hw_ops->gic_irq_ops->disable(desc); desc->status |= IRQ_DISABLED; spin_unlock(&gic_lock); spin_unlock_irqrestore(&desc->lock, flags); @@ -186,16 +150,15 @@ static void gic_host_irq_end(struct irq_desc *desc) { int irq = desc->irq; /* Lower the priority */ - GICC[GICC_EOIR] = irq; + gic_hw_ops->gic_irq_ops->end(desc); /* Deactivate */ - GICC[GICC_DIR] = irq; + gic_hw_ops->deactivate_irq(irq); } static void gic_guest_irq_end(struct irq_desc *desc) { - int irq = desc->irq; /* Lower the priority of the IRQ */ - GICC[GICC_EOIR] = irq; + gic_hw_ops->gic_irq_ops->end(desc); /* Deactivation happens in maintenance interrupt / via GICV */ } @@ -215,6 +178,7 @@ static hw_irq_controller gic_host_irq_type = { .end = gic_host_irq_end, .set_affinity = gic_irq_set_affinity, }; + static hw_irq_controller gic_guest_irq_type = { .typename = "gic", .startup = gic_irq_startup, @@ -235,27 +199,7 @@ static void gic_set_irq_properties(unsigned int irq, bool_t level, const cpumask_t *cpu_mask, unsigned int priority) { - volatile unsigned char *bytereg; - uint32_t cfg, edgebit; - unsigned int mask = gic_cpu_mask(cpu_mask); - - /* Set edge / level */ - cfg = GICD[GICD_ICFGR + irq / 16]; - edgebit = 2u << (2 * (irq % 16)); - if ( level ) - cfg &= ~edgebit; - else - cfg |= edgebit; - GICD[GICD_ICFGR + irq / 16] = cfg; - - /* Set target CPU mask (RAZ/WI on uniprocessor) */ - bytereg = (unsigned char *) (GICD + GICD_ITARGETSR); - bytereg[irq] = mask; - - /* Set priority */ - bytereg = (unsigned char *) (GICD + GICD_IPRIORITYR); - bytereg[irq] = priority; - + return gic_hw_ops->set_irq_property(irq, level, cpu_mask, priority); } /* Program the GIC to route an interrupt */ @@ -266,7 +210,7 @@ static int gic_route_irq(unsigned int irq, bool_t level, unsigned long flags; ASSERT(priority <= 0xff); /* Only 8 bits of priority */ - ASSERT(irq < gic.lines); /* Can't route interrupts that don't exist */ + ASSERT(irq < gic_number_lines()); /* Can't route interrupts that don't exist */ if ( desc->action != NULL ) return -EBUSY; @@ -297,95 +241,6 @@ void gic_route_dt_irq(const struct dt_irq *irq, const cpumask_t *cpu_mask, gic_route_irq(irq->irq, level, cpu_mask, priority); } -static void __init gic_dist_init(void) -{ - uint32_t type; - uint32_t cpumask; - int i; - - cpumask = GICD[GICD_ITARGETSR] & 0xff; - cpumask |= cpumask << 8; - cpumask |= cpumask << 16; - - /* Disable the distributor */ - GICD[GICD_CTLR] = 0; - - type = GICD[GICD_TYPER]; - gic.lines = 32 * ((type & GICD_TYPE_LINES) + 1); - gic.cpus = 1 + ((type & GICD_TYPE_CPUS) >> 5); - printk("GIC: %d lines, %d cpu%s%s (IID %8.8x).\n", - gic.lines, gic.cpus, (gic.cpus == 1) ? "" : "s", - (type & GICD_TYPE_SEC) ? ", secure" : "", - GICD[GICD_IIDR]); - - /* Default all global IRQs to level, active low */ - for ( i = 32; i < gic.lines; i += 16 ) - GICD[GICD_ICFGR + i / 16] = 0x0; - - /* Route all global IRQs to this CPU */ - for ( i = 32; i < gic.lines; i += 4 ) - GICD[GICD_ITARGETSR + i / 4] = cpumask; - - /* Default priority for global interrupts */ - for ( i = 32; i < gic.lines; i += 4 ) - GICD[GICD_IPRIORITYR + i / 4] = - GIC_PRI_IRQ<<24 | GIC_PRI_IRQ<<16 | GIC_PRI_IRQ<<8 | GIC_PRI_IRQ; - - /* Disable all global interrupts */ - for ( i = 32; i < gic.lines; i += 32 ) - GICD[GICD_ICENABLER + i / 32] = (uint32_t)~0ul; - - /* Turn on the distributor */ - GICD[GICD_CTLR] = GICD_CTL_ENABLE; -} - -static void __cpuinit gic_cpu_init(void) -{ - int i; - - this_cpu(gic_cpu_id) = GICD[GICD_ITARGETSR] & 0xff; - - /* The first 32 interrupts (PPI and SGI) are banked per-cpu, so - * even though they are controlled with GICD registers, they must - * be set up here with the other per-cpu state. */ - GICD[GICD_ICENABLER] = 0xffff0000; /* Disable all PPI */ - GICD[GICD_ISENABLER] = 0x0000ffff; /* Enable all SGI */ - /* Set SGI priorities */ - for (i = 0; i < 16; i += 4) - GICD[GICD_IPRIORITYR + i / 4] = - GIC_PRI_IPI<<24 | GIC_PRI_IPI<<16 | GIC_PRI_IPI<<8 | GIC_PRI_IPI; - /* Set PPI priorities */ - for (i = 16; i < 32; i += 4) - GICD[GICD_IPRIORITYR + i / 4] = - GIC_PRI_IRQ<<24 | GIC_PRI_IRQ<<16 | GIC_PRI_IRQ<<8 | GIC_PRI_IRQ; - - /* Local settings: interface controller */ - GICC[GICC_PMR] = 0xff; /* Don't mask by priority */ - GICC[GICC_BPR] = 0; /* Finest granularity of priority */ - GICC[GICC_CTLR] = GICC_CTL_ENABLE|GICC_CTL_EOI; /* Turn on delivery */ -} - -static void gic_cpu_disable(void) -{ - GICC[GICC_CTLR] = 0; -} - -static void __cpuinit gic_hyp_init(void) -{ - uint32_t vtr; - - vtr = GICH[GICH_VTR]; - nr_lrs = (vtr & GICH_VTR_NRLRGS) + 1; - - GICH[GICH_MISR] = GICH_MISR_EOI; - this_cpu(lr_mask) = 0ULL; -} - -static void __cpuinit gic_hyp_disable(void) -{ - GICH[GICH_HCR] = 0; -} - int gic_irq_xlate(const u32 *intspec, unsigned int intsize, unsigned int *out_hwirq, unsigned int *out_type) @@ -409,99 +264,8 @@ int gic_irq_xlate(const u32 *intspec, unsigned int intsize, /* Set up the GIC */ void __init gic_init(void) { - static const struct dt_device_match gic_ids[] __initconst = - { - DT_MATCH_GIC, - { /* sentinel */ }, - }; - struct dt_device_node *node; - int res; - - node = dt_find_interrupt_controller(gic_ids); - if ( !node ) - panic("Unable to find compatible GIC in the device tree"); - - dt_device_set_used_by(node, DOMID_XEN); - - res = dt_device_get_address(node, 0, &gic.dbase, NULL); - if ( res || !gic.dbase || (gic.dbase & ~PAGE_MASK) ) - panic("GIC: Cannot find a valid address for the distributor"); - - res = dt_device_get_address(node, 1, &gic.cbase, NULL); - if ( res || !gic.cbase || (gic.cbase & ~PAGE_MASK) ) - panic("GIC: Cannot find a valid address for the CPU"); - - res = dt_device_get_address(node, 2, &gic.hbase, NULL); - if ( res || !gic.hbase || (gic.hbase & ~PAGE_MASK) ) - panic("GIC: Cannot find a valid address for the hypervisor"); - - res = dt_device_get_address(node, 3, &gic.vbase, NULL); - if ( res || !gic.vbase || (gic.vbase & ~PAGE_MASK) ) - panic("GIC: Cannot find a valid address for the virtual CPU"); - - res = dt_device_get_irq(node, 0, &gic.maintenance); - if ( res ) - panic("GIC: Cannot find the maintenance IRQ"); - - /* Set the GIC as the primary interrupt controller */ - dt_interrupt_controller = node; - - /* TODO: Add check on distributor, cpu size */ - - printk("GIC initialization:\n" - " gic_dist_addr=%"PRIpaddr"\n" - " gic_cpu_addr=%"PRIpaddr"\n" - " gic_hyp_addr=%"PRIpaddr"\n" - " gic_vcpu_addr=%"PRIpaddr"\n" - " gic_maintenance_irq=%u\n", - gic.dbase, gic.cbase, gic.hbase, gic.vbase, - gic.maintenance.irq); - - if ( (gic.dbase & ~PAGE_MASK) || (gic.cbase & ~PAGE_MASK) || - (gic.hbase & ~PAGE_MASK) || (gic.vbase & ~PAGE_MASK) ) - panic("GIC interfaces not page aligned"); - - set_fixmap(FIXMAP_GICD, gic.dbase >> PAGE_SHIFT, DEV_SHARED); - BUILD_BUG_ON(FIXMAP_ADDR(FIXMAP_GICC1) != - FIXMAP_ADDR(FIXMAP_GICC2)-PAGE_SIZE); - set_fixmap(FIXMAP_GICC1, gic.cbase >> PAGE_SHIFT, DEV_SHARED); - if ( platform_has_quirk(PLATFORM_QUIRK_GIC_64K_STRIDE) ) - set_fixmap(FIXMAP_GICC2, (gic.cbase >> PAGE_SHIFT) + 0x10, DEV_SHARED); - else - set_fixmap(FIXMAP_GICC2, (gic.cbase >> PAGE_SHIFT) + 0x1, DEV_SHARED); - set_fixmap(FIXMAP_GICH, gic.hbase >> PAGE_SHIFT, DEV_SHARED); - - /* Global settings: interrupt distributor */ + gicv2_init(); spin_lock_init(&gic_lock); - spin_lock(&gic_lock); - - gic_dist_init(); - gic_cpu_init(); - gic_hyp_init(); - - spin_unlock(&gic_lock); -} - -void send_sgi(const cpumask_t *online_mask, enum gic_sgi sgi, uint8_t irqmode) -{ - unsigned int mask = 0; - - switch(irqmode) - { - case SGI_TARGET_OTHERS: - GICD[GICD_SGIR] = GICD_SGI_TARGET_OTHERS | sgi; - break; - case SGI_TARGET_SELF: - GICD[GICD_SGIR] = GICD_SGI_TARGET_SELF | sgi; - break; - case SGI_TARGET_LIST: - mask = gic_cpu_mask(online_mask); - GICD[GICD_SGIR] = GICD_SGI_TARGET_LIST | - (mask<<GICD_SGI_TARGET_SHIFT) | sgi; - break; - default: - gdprintk(XENLOG_WARNING, "Wrong sgi irq mode for sgi %x\n", sgi); - } } void send_SGI_mask(const cpumask_t *cpumask, enum gic_sgi sgi) @@ -514,12 +278,11 @@ void send_SGI_mask(const cpumask_t *cpumask, enum gic_sgi sgi) dsb(); - send_sgi(&online_mask, sgi, SGI_TARGET_LIST); + gic_hw_ops->send_sgi(&online_mask, sgi, SGI_TARGET_LIST); } void send_SGI_one(unsigned int cpu, enum gic_sgi sgi) { - ASSERT(cpu < NR_GIC_CPU_IF); /* Targets bitmap only supports 8 CPUs */ send_SGI_mask(cpumask_of(cpu), sgi); } @@ -528,8 +291,8 @@ void send_SGI_self(enum gic_sgi sgi) ASSERT(sgi < 16); /* There are only 16 SGIs */ dsb(); - - send_sgi(cpumask_of(smp_processor_id()), sgi, SGI_TARGET_SELF); + + gic_hw_ops->send_sgi(cpumask_of(smp_processor_id()), sgi, SGI_TARGET_SELF); } void send_SGI_allbutself(enum gic_sgi sgi) @@ -540,7 +303,7 @@ void send_SGI_allbutself(enum gic_sgi sgi) cpumask_andnot(&all_others_mask, &cpu_possible_map, cpumask_of(smp_processor_id())); dsb(); - send_sgi(&all_others_mask, sgi, SGI_TARGET_OTHERS); + gic_hw_ops->send_sgi(&all_others_mask, sgi, SGI_TARGET_OTHERS); } void smp_send_state_dump(unsigned int cpu) @@ -552,8 +315,7 @@ void smp_send_state_dump(unsigned int cpu) void __cpuinit gic_init_secondary_cpu(void) { spin_lock(&gic_lock); - gic_cpu_init(); - gic_hyp_init(); + gic_hw_ops->secondary_init(); spin_unlock(&gic_lock); } @@ -563,15 +325,14 @@ void gic_disable_cpu(void) ASSERT(!local_irq_is_enabled()); spin_lock(&gic_lock); - gic_cpu_disable(); - gic_hyp_disable(); + gic_hw_ops->disable_interface(); spin_unlock(&gic_lock); } void gic_route_ppis(void) { /* GIC maintenance */ - gic_route_dt_irq(&gic.maintenance, cpumask_of(smp_processor_id()), + gic_route_dt_irq(gic_hw_ops->get_maintenance_irq(), cpumask_of(smp_processor_id()), GIC_PRI_IRQ); /* Route timer interrupt */ route_timer_interrupt(); @@ -646,22 +407,11 @@ int __init setup_dt_irq(const struct dt_irq *irq, struct irqaction *new) return rc; } -static inline void gic_set_lr(int lr, struct pending_irq *p, +static void gic_set_lr(int lr, struct pending_irq *p, unsigned int state) { - uint32_t lr_reg; - ASSERT(!local_irq_is_enabled()); - BUG_ON(lr >= nr_lrs); - BUG_ON(lr < 0); - BUG_ON(state & ~(GICH_LR_STATE_MASK<<GICH_LR_STATE_SHIFT)); - - lr_reg = state | ((p->priority >> 3) << GICH_LR_PRIORITY_SHIFT) | - ((p->irq & GICH_LR_VIRTUAL_MASK) << GICH_LR_VIRTUAL_SHIFT); - if ( p->desc != NULL ) - lr_reg |= GICH_LR_HW | (p->desc->irq << GICH_LR_PHYSICAL_SHIFT); - - GICH[GICH_LR + lr] = lr_reg; + gic_hw_ops->update_lr(lr, p, state); set_bit(GIC_IRQ_GUEST_VISIBLE, &p->status); clear_bit(GIC_IRQ_GUEST_PENDING, &p->status); @@ -704,6 +454,7 @@ void gic_raise_guest_irq(struct vcpu *v, unsigned int virtual_irq, { int i; struct pending_irq *n = irq_to_pending(v, virtual_irq); + unsigned int nr_lrs = gic_hw_ops->nr_lrs; ASSERT(spin_is_locked(&v->arch.vgic.lock)); @@ -730,27 +481,30 @@ void gic_raise_guest_irq(struct vcpu *v, unsigned int virtual_irq, static void gic_clear_one_lr(struct vcpu *v, int i) { struct pending_irq *p; - uint32_t lr; int irq; + struct gic_lr lr_val; ASSERT(spin_is_locked(&v->arch.vgic.lock)); ASSERT(!local_irq_is_enabled()); - lr = GICH[GICH_LR + i]; - irq = (lr >> GICH_LR_VIRTUAL_SHIFT) & GICH_LR_VIRTUAL_MASK; + gic_hw_ops->read_lr(i, &lr_val); + irq = lr_val.virq; p = irq_to_pending(v, irq); - if ( lr & GICH_LR_ACTIVE ) + if ( lr_val.state & GICH_LR_ACTIVE ) { set_bit(GIC_IRQ_GUEST_ACTIVE, &p->status); /* HW interrupts cannot be ACTIVE and PENDING */ if ( p->desc == NULL && test_bit(GIC_IRQ_GUEST_ENABLED, &p->status) && test_and_clear_bit(GIC_IRQ_GUEST_PENDING, &p->status) ) - GICH[GICH_LR + i] = lr | GICH_LR_PENDING; - } else if ( lr & GICH_LR_PENDING ) { + { + lr_val.state |= GICH_LR_PENDING; + gic_hw_ops->write_lr(i, &lr_val); + } + } else if ( lr_val.state & GICH_LR_PENDING ) { clear_bit(GIC_IRQ_GUEST_PENDING, &p->status); } else { - GICH[GICH_LR + i] = 0; + gic_hw_ops->clear_lr(i); clear_bit(i, &this_cpu(lr_mask)); if ( p->desc != NULL ) @@ -771,6 +525,7 @@ void gic_clear_lrs(struct vcpu *v) { int i = 0; unsigned long flags; + unsigned int nr_lrs = gic_hw_ops->nr_lrs; spin_lock_irqsave(&v->arch.vgic.lock, flags); @@ -785,10 +540,12 @@ void gic_clear_lrs(struct vcpu *v) static void gic_restore_pending_irqs(struct vcpu *v) { - int i = 0, lrs = nr_lrs; + int i = 0, lrs; struct pending_irq *p, *t, *p_r; unsigned long flags; + unsigned int nr_lrs = gic_hw_ops->nr_lrs; + lrs = nr_lrs; if ( list_empty(&v->arch.vgic.lr_pending) ) return; @@ -845,13 +602,15 @@ void gic_clear_pending_irqs(struct vcpu *v) int gic_events_need_delivery(void) { - int mask_priority, lrs = nr_lrs; + int mask_priority, lrs; int max_priority = 0xff, active_priority = 0xff; struct vcpu *v = current; struct pending_irq *p; unsigned long flags; + unsigned int nr_lrs = gic_hw_ops->nr_lrs; + lrs = nr_lrs; - mask_priority = (GICH[GICH_VMCR] >> GICH_VMCR_PRIORITY_SHIFT) & GICH_VMCR_PRIORITY_MASK; + mask_priority = gic_hw_ops->read_vmcr_priority(); spin_lock_irqsave(&v->arch.vgic.lock, flags); @@ -888,9 +647,9 @@ void gic_inject(void) gic_restore_pending_irqs(current); if ( !list_empty(¤t->arch.vgic.lr_pending) && lr_all_full() ) - GICH[GICH_HCR] |= GICH_HCR_UIE; + gic_hw_ops->update_hcr_status(GICH_HCR_UIE, 1); else - GICH[GICH_HCR] &= ~GICH_HCR_UIE; + gic_hw_ops->update_hcr_status(GICH_HCR_UIE, 0); } int gic_route_irq_to_guest(struct domain *d, const struct dt_irq *irq, @@ -941,7 +700,8 @@ out: static void do_sgi(struct cpu_user_regs *regs, enum gic_sgi sgi) { /* Lower the priority */ - GICC[GICC_EOIR] = sgi; + struct irq_desc *desc = irq_to_desc(sgi); + gic_hw_ops->gic_irq_ops->end(desc); switch (sgi) { @@ -960,19 +720,16 @@ static void do_sgi(struct cpu_user_regs *regs, enum gic_sgi sgi) } /* Deactivate */ - GICC[GICC_DIR] = sgi; + gic_hw_ops->deactivate_irq(sgi); } /* Accept an interrupt from the GIC and dispatch its handler */ void gic_interrupt(struct cpu_user_regs *regs, int is_fiq) { - uint32_t intack; unsigned int irq; - do { - intack = GICC[GICC_IAR]; - irq = intack & GICC_IA_IRQ; + irq = gic_hw_ops->ack_irq(); if ( likely(irq >= 16 && irq < 1021) ) { @@ -994,49 +751,7 @@ void gic_interrupt(struct cpu_user_regs *regs, int is_fiq) int gicv_setup(struct domain *d) { - int ret; - - /* - * Domain 0 gets the hardware address. - * Guests get the virtual platform layout. - */ - if ( d->domain_id == 0 ) - { - d->arch.vgic.dbase = gic.dbase; - d->arch.vgic.cbase = gic.cbase; - } - else - { - d->arch.vgic.dbase = GUEST_GICD_BASE; - d->arch.vgic.cbase = GUEST_GICC_BASE; - } - - d->arch.vgic.nr_lines = 0; - - /* - * Map the gic virtual cpu interface in the gic cpu interface - * region of the guest. - * - * The second page is always mapped at +4K irrespective of the - * GIC_64K_STRIDE quirk. The DTB passed to the guest reflects this. - */ - ret = map_mmio_regions(d, d->arch.vgic.cbase, - d->arch.vgic.cbase + PAGE_SIZE - 1, - gic.vbase); - if (ret) - return ret; - - if ( !platform_has_quirk(PLATFORM_QUIRK_GIC_64K_STRIDE) ) - ret = map_mmio_regions(d, d->arch.vgic.cbase + PAGE_SIZE, - d->arch.vgic.cbase + (2 * PAGE_SIZE) - 1, - gic.vbase + PAGE_SIZE); - else - ret = map_mmio_regions(d, d->arch.vgic.cbase + PAGE_SIZE, - d->arch.vgic.cbase + (2 * PAGE_SIZE) - 1, - gic.vbase + 16*PAGE_SIZE); - - return ret; - + return gic_hw_ops->gicv_setup(d); } static void maintenance_interrupt(int irq, void *dev_id, struct cpu_user_regs *regs) @@ -1051,18 +766,10 @@ static void maintenance_interrupt(int irq, void *dev_id, struct cpu_user_regs *r void gic_dump_info(struct vcpu *v) { - int i; struct pending_irq *p; printk("GICH_LRs (vcpu %d) mask=%"PRIx64"\n", v->vcpu_id, v->arch.lr_mask); - if ( v == current ) - { - for ( i = 0; i < nr_lrs; i++ ) - printk(" HW_LR[%d]=%x\n", i, GICH[GICH_LR + i]); - } else { - for ( i = 0; i < nr_lrs; i++ ) - printk(" VCPU_LR[%d]=%x\n", i, v->arch.gic_lr[i]); - } + gic_hw_ops->dump_state(v); list_for_each_entry ( p, &v->arch.vgic.inflight_irqs, inflight ) { @@ -1078,7 +785,7 @@ void gic_dump_info(struct vcpu *v) void __cpuinit init_maintenance_interrupt(void) { - request_dt_irq(&gic.maintenance, maintenance_interrupt, + request_dt_irq(gic_hw_ops->get_maintenance_irq(), maintenance_interrupt, "irq-maintenance", NULL); } diff --git a/xen/include/asm-arm/gic.h b/xen/include/asm-arm/gic.h index eba41ee..2387e38 100644 --- a/xen/include/asm-arm/gic.h +++ b/xen/include/asm-arm/gic.h @@ -43,12 +43,41 @@ #define SGI_TARGET_OTHERS 1 #define SGI_TARGET_SELF 2 +#define GICH_LR_PENDING 1 +#define GICH_LR_ACTIVE 2 + +#define GICH_HCR_EN (1 << 0) +#define GICH_HCR_UIE (1 << 1) +#define GICH_HCR_LRENPIE (1 << 2) +#define GICH_HCR_NPIE (1 << 3) +#define GICH_HCR_VGRP0EIE (1 << 4) +#define GICH_HCR_VGRP0DIE (1 << 5) +#define GICH_HCR_VGRP1EIE (1 << 6) +#define GICH_HCR_VGRP1DIE (1 << 7) + #ifndef __ASSEMBLY__ #include <xen/device_tree.h> +#include <xen/irq.h> #define DT_MATCH_GIC DT_MATCH_COMPATIBLE("arm,cortex-a15-gic"), \ DT_MATCH_COMPATIBLE("arm,cortex-a7-gic") +/* + * Decode LR register content and populate below struct. + * The LR register format is different for GIC HW version + */ +struct gic_lr { + uint32_t pirq; + uint32_t virq; + uint8_t priority; + uint8_t state; + uint8_t hw_status; + uint8_t grp; +}; + +extern int gic_hw_version(void); +extern void gicv2_init(void); + extern int domain_vgic_init(struct domain *d); extern void domain_vgic_free(struct domain *d); @@ -114,6 +143,63 @@ int gic_irq_xlate(const u32 *intspec, unsigned int intsize, unsigned int *out_hwirq, unsigned int *out_type); void gic_clear_lrs(struct vcpu *v); +enum gic_version { + GIC_V2 = 2, +}; + +struct gic_hw_operations { + /* GIC version */ + enum gic_version hw_version; + /* Number of GIC lines supported */ + unsigned int nr_lines; + /* Number of LR registers */ + unsigned int nr_lrs; + /* Maintenance irq is derived from dt node. Fetch from gic driver */ + struct dt_irq * (*get_maintenance_irq)(void); + /* Save GIC registers */ + void (*save_state)(struct vcpu *); + /* Restore GIC registers */ + void (*restore_state)(struct vcpu *); + /* Dump GIC LR register information */ + void (*dump_state)(struct vcpu *); + /* Map MMIO region of GIC and get GIC address information */ + int (*gicv_setup)(struct domain *); + + /* hw_irq_controller for enable/disable/eoi irq */ + hw_irq_controller *gic_irq_ops; + + /* Deactivate/reduce priority of irq */ + void (*deactivate_irq)(int); + /* Ack IRQ and return irq id */ + unsigned int (*ack_irq)(void); + /* Set IRQ property */ + void (*set_irq_property)(unsigned int irq, bool_t level, + const cpumask_t *cpu_mask, + unsigned int priority); + /* Send SGI */ + void (*send_sgi)(const cpumask_t *online_mask, + enum gic_sgi sgi, uint8_t irqmode); + /* Disable CPU physical and virtual interfaces */ + void (*disable_interface)(void); + /* Update LR with state and priority */ + void (*update_lr)(int lr, struct pending_irq *, unsigned int state); + /* Update HCR status register */ + void (*update_hcr_status)(uint32_t flag, bool_t set); + /* Clear LR register */ + void (*clear_lr)(int lr); + /* Read LR register and populate gic_lr structure */ + void (*read_lr)(int lr, struct gic_lr *); + /* Write LR register from gic_lr structure */ + void (*write_lr)(int lr, struct gic_lr *); + /* Read VMCR priority */ + unsigned int (*read_vmcr_priority)(void); + /* Secondary CPU init */ + void (*secondary_init)(void); +}; + +void register_gic_ops(const struct gic_hw_operations *ops); +extern void update_cpu_lr_mask(void); + #endif /* __ASSEMBLY__ */ #endif diff --git a/xen/include/asm-arm/gic_v2_defs.h b/xen/include/asm-arm/gic_v2_defs.h index 033d826..cb47edd 100644 --- a/xen/include/asm-arm/gic_v2_defs.h +++ b/xen/include/asm-arm/gic_v2_defs.h @@ -96,15 +96,6 @@ #define GICC_IA_CPU_MASK 0x1c00 #define GICC_IA_CPU_SHIFT 10 -#define GICH_HCR_EN (1 << 0) -#define GICH_HCR_UIE (1 << 1) -#define GICH_HCR_LRENPIE (1 << 2) -#define GICH_HCR_NPIE (1 << 3) -#define GICH_HCR_VGRP0EIE (1 << 4) -#define GICH_HCR_VGRP0DIE (1 << 5) -#define GICH_HCR_VGRP1EIE (1 << 6) -#define GICH_HCR_VGRP1DIE (1 << 7) - #define GICH_MISR_EOI (1 << 0) #define GICH_MISR_U (1 << 1) #define GICH_MISR_LRENP (1 << 2) @@ -121,9 +112,12 @@ #define GICH_LR_STATE_MASK 0x3 #define GICH_LR_STATE_SHIFT 28 #define GICH_LR_PRIORITY_SHIFT 23 +#define GICH_LR_PRIORITY_MASK 0x1f +#define GICH_LR_HW_SHIFT 31 +#define GICH_LR_HW_MASK 0x1 +#define GICH_LR_GRP_SHIFT 30 +#define GICH_LR_GRP_MASK 0x1 #define GICH_LR_MAINTENANCE_IRQ (1<<19) -#define GICH_LR_PENDING (1<<28) -#define GICH_LR_ACTIVE (1<<29) #define GICH_LR_GRP1 (1<<30) #define GICH_LR_HW (1<<31) #define GICH_LR_CPUID_SHIFT 9 -- 1.7.9.5 _______________________________________________ Xen-devel mailing list Xen-devel@xxxxxxxxxxxxx http://lists.xen.org/xen-devel
|
Lists.xenproject.org is hosted with RackSpace, monitoring our |