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

Re: [Xen-devel] [PATCH v5.99.1 RFC 1/4] xen/arm: Duplicate gic-v2.c file to support hip04 platform version



On Wed, 25 Feb 2015, Frediano Ziglio wrote:
> HiSilison Hip04 platform use a slightly different version
> 
> Signed-off-by: Frediano Ziglio <frediano.ziglio@xxxxxxxxxx>

I think that this is preferable to the previous approach of modifying
the existing gic-v2 driver, after all the hip04 interrupt controller is
not a gicv2.



>  xen/arch/arm/Makefile    |   2 +-
>  xen/arch/arm/gic-hip04.c | 788 
> +++++++++++++++++++++++++++++++++++++++++++++++
>  2 files changed, 789 insertions(+), 1 deletion(-)
>  create mode 100644 xen/arch/arm/gic-hip04.c

I am concerned about the increased size of the Xen binary as a result of
the introduction of this driver.

I think we should disable the build of all drivers in Xen by default,
except for the ARM standard compliant ones (for aarch64 the SBSA is a
nice summary of what is considered compliant), to keep the size of the
binary small.

Could you please introduce a Xen build time option in
xen/arch/arm/Rules.mk, called HAS_NON_STANDARD_DRIVERS, that by default
is n, and gate the build of gic-hip04.c on it?



> diff --git a/xen/arch/arm/Makefile b/xen/arch/arm/Makefile
> index 41aba2e..6fb8ba9 100644
> --- a/xen/arch/arm/Makefile
> +++ b/xen/arch/arm/Makefile
> @@ -11,7 +11,7 @@ obj-y += vpsci.o
>  obj-y += domctl.o
>  obj-y += sysctl.o
>  obj-y += domain_build.o
> -obj-y += gic.o gic-v2.o
> +obj-y += gic.o gic-v2.o gic-hip04.o
>  obj-$(CONFIG_ARM_64) += gic-v3.o
>  obj-y += io.o
>  obj-y += irq.o
> diff --git a/xen/arch/arm/gic-hip04.c b/xen/arch/arm/gic-hip04.c
> new file mode 100644
> index 0000000..a401e3f
> --- /dev/null
> +++ b/xen/arch/arm/gic-hip04.c
> @@ -0,0 +1,788 @@
> +/*
> + * xen/arch/arm/gic-v2.c

gic-hip04.c


> + * ARM Generic Interrupt Controller support v2

This is not an ARM Generic Interrupt Controller 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/softirq.h>
> +#include <xen/list.h>
> +#include <xen/device_tree.h>
> +#include <xen/libfdt/libfdt.h>
> +#include <asm/p2m.h>
> +#include <asm/domain.h>
> +#include <asm/platform.h>
> +#include <asm/device.h>
> +
> +#include <asm/io.h>
> +#include <asm/gic.h>
> +
> +/*
> + * LR register definitions are GIC v2 specific.
> + * Moved these definitions from header file to here
> + */
> +#define GICH_V2_LR_VIRTUAL_MASK    0x3ff
> +#define GICH_V2_LR_VIRTUAL_SHIFT   0
> +#define GICH_V2_LR_PHYSICAL_MASK   0x3ff
> +#define GICH_V2_LR_PHYSICAL_SHIFT  10
> +#define GICH_V2_LR_STATE_MASK      0x3
> +#define GICH_V2_LR_STATE_SHIFT     28
> +#define GICH_V2_LR_PRIORITY_SHIFT  23
> +#define GICH_V2_LR_PRIORITY_MASK   0x1f
> +#define GICH_V2_LR_HW_SHIFT        31
> +#define GICH_V2_LR_HW_MASK         0x1
> +#define GICH_V2_LR_GRP_SHIFT       30
> +#define GICH_V2_LR_GRP_MASK        0x1
> +#define GICH_V2_LR_MAINTENANCE_IRQ (1<<19)
> +#define GICH_V2_LR_GRP1            (1<<30)
> +#define GICH_V2_LR_HW              (1<<31)
> +#define GICH_V2_LR_CPUID_SHIFT     9
> +#define GICH_V2_VTR_NRLRGS         0x3f
> +
> +#define GICH_V2_VMCR_PRIORITY_MASK   0x1f
> +#define GICH_V2_VMCR_PRIORITY_SHIFT  27

Please rename all constants, definitions and function names to something
else to make it clear that this driver is not for a gicv2.
Replacing gicv2 with hip04-gic would be a good start.


> +/* Global state */
> +static struct {
> +    paddr_t dbase;            /* Address of distributor registers */
> +    void __iomem * map_dbase; /* IO mapped Address of distributor registers 
> */
> +    paddr_t cbase;            /* Address of CPU interface registers */
> +    void __iomem * map_cbase[2]; /* IO mapped Address of CPU interface 
> registers */
> +    paddr_t hbase;            /* Address of virtual interface registers */
> +    void __iomem * map_hbase; /* IO Address of virtual interface registers */
> +    paddr_t vbase;            /* Address of virtual cpu interface registers 
> */
> +    spinlock_t lock;
> +} gicv2;
> +
> +static struct gic_info gicv2_info;
> +
> +/* 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 inline void writeb_gicd(uint8_t val, unsigned int offset)
> +{
> +    writeb_relaxed(val, gicv2.map_dbase + offset);
> +}
> +
> +static inline void writel_gicd(uint32_t val, unsigned int offset)
> +{
> +    writel_relaxed(val, gicv2.map_dbase + offset);
> +}
> +
> +static inline uint32_t readl_gicd(unsigned int offset)
> +{
> +    return readl_relaxed(gicv2.map_dbase + offset);
> +}
> +
> +static inline void writel_gicc(uint32_t val, unsigned int offset)
> +{
> +    unsigned int page = offset >> PAGE_SHIFT;
> +    offset &= ~PAGE_MASK;
> +    writel_relaxed(val, gicv2.map_cbase[page] + offset);
> +}
> +
> +static inline uint32_t readl_gicc(unsigned int offset)
> +{
> +    unsigned int page = offset >> PAGE_SHIFT;
> +    offset &= ~PAGE_MASK;
> +    return readl_relaxed(gicv2.map_cbase[page] + offset);
> +}
> +
> +static inline void writel_gich(uint32_t val, unsigned int offset)
> +{
> +    writel_relaxed(val, gicv2.map_hbase + offset);
> +}
> +
> +static inline uint32_t readl_gich(int unsigned offset)
> +{
> +    return readl_relaxed(gicv2.map_hbase + offset);
> +}
> +
> +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 < gicv2_info.nr_lrs; i++ )
> +        v->arch.gic.v2.lr[i] = readl_gich(GICH_LR + i * 4);
> +
> +    v->arch.gic.v2.apr = readl_gich(GICH_APR);
> +    v->arch.gic.v2.vmcr = readl_gich(GICH_VMCR);
> +    /* Disable until next VCPU scheduled */
> +    writel_gich(0, GICH_HCR);
> +}
> +
> +static void gicv2_restore_state(const struct vcpu *v)
> +{
> +    int i;
> +
> +    for ( i = 0; i < gicv2_info.nr_lrs; i++ )
> +        writel_gich(v->arch.gic.v2.lr[i], GICH_LR + i * 4);
> +
> +    writel_gich(v->arch.gic.v2.apr, GICH_APR);
> +    writel_gich(v->arch.gic.v2.vmcr, GICH_VMCR);
> +    writel_gich(GICH_HCR_EN, GICH_HCR);
> +}
> +
> +static void gicv2_dump_state(const struct vcpu *v)
> +{
> +    int i;
> +
> +    if ( v == current )
> +    {
> +        for ( i = 0; i < gicv2_info.nr_lrs; i++ )
> +            printk("   HW_LR[%d]=%x\n", i,
> +                   readl_gich(GICH_LR + i * 4));
> +    }
> +    else
> +    {
> +        for ( i = 0; i < gicv2_info.nr_lrs; i++ )
> +            printk("   VCPU_LR[%d]=%x\n", i, v->arch.gic.v2.lr[i]);
> +    }
> +}
> +
> +static void gicv2_eoi_irq(struct irq_desc *irqd)
> +{
> +    int irq = irqd->irq;
> +    /* Lower the priority */
> +    writel_gicc(irq, GICC_EOIR);
> +}
> +
> +static void gicv2_dir_irq(struct irq_desc *irqd)
> +{
> +    /* Deactivate */
> +    writel_gicc(irqd->irq, GICC_DIR);
> +}
> +
> +static unsigned int gicv2_read_irq(void)
> +{
> +    return (readl_gicc(GICC_IAR) & GICC_IA_IRQ);
> +}
> +
> +/*
> + * 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(struct irq_desc *desc,
> +                                   const cpumask_t *cpu_mask,
> +                                   unsigned int priority)
> +{
> +    uint32_t cfg, edgebit;
> +    unsigned int mask = gicv2_cpu_mask(cpu_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(&gicv2.lock);
> +    /* Set edge / level */
> +    cfg = readl_gicd(GICD_ICFGR + (irq / 16) * 4);
> +    edgebit = 2u << (2 * (irq % 16));
> +    if ( type & DT_IRQ_TYPE_LEVEL_MASK )
> +        cfg &= ~edgebit;
> +    else if ( type & DT_IRQ_TYPE_EDGE_BOTH )
> +        cfg |= edgebit;
> +    writel_gicd(cfg, GICD_ICFGR + (irq / 16) * 4);
> +
> +    /* Set target CPU mask (RAZ/WI on uniprocessor) */
> +    writeb_gicd(mask, GICD_ITARGETSR + irq);
> +    /* Set priority */
> +    writeb_gicd(priority, GICD_IPRIORITYR + irq);
> +
> +    spin_unlock(&gicv2.lock);
> +}
> +
> +static void __init gicv2_dist_init(void)
> +{
> +    uint32_t type;
> +    uint32_t cpumask;
> +    uint32_t gic_cpus;
> +    int i;
> +
> +    cpumask = readl_gicd(GICD_ITARGETSR) & 0xff;
> +    cpumask |= cpumask << 8;
> +    cpumask |= cpumask << 16;
> +
> +    /* Disable the distributor */
> +    writel_gicd(0, GICD_CTLR);
> +
> +    type = readl_gicd(GICD_TYPER);
> +    gicv2_info.nr_lines = 32 * ((type & GICD_TYPE_LINES) + 1);
> +    gic_cpus = 1 + ((type & GICD_TYPE_CPUS) >> 5);
> +    printk("GICv2: %d lines, %d cpu%s%s (IID %8.8x).\n",
> +           gicv2_info.nr_lines, gic_cpus, (gic_cpus == 1) ? "" : "s",
> +           (type & GICD_TYPE_SEC) ? ", secure" : "",
> +           readl_gicd(GICD_IIDR));
> +
> +    /* Default all global IRQs to level, active low */
> +    for ( i = 32; i < gicv2_info.nr_lines; i += 16 )
> +        writel_gicd(0x0, GICD_ICFGR + (i / 16) * 4);
> +
> +    /* Route all global IRQs to this CPU */
> +    for ( i = 32; i < gicv2_info.nr_lines; i += 4 )
> +        writel_gicd(cpumask, GICD_ITARGETSR + (i / 4) * 4);
> +
> +    /* Default priority for global interrupts */
> +    for ( i = 32; i < gicv2_info.nr_lines; i += 4 )
> +        writel_gicd(GIC_PRI_IRQ << 24 | GIC_PRI_IRQ << 16 |
> +                    GIC_PRI_IRQ << 8 | GIC_PRI_IRQ,
> +                    GICD_IPRIORITYR + (i / 4) * 4);
> +
> +    /* Disable all global interrupts */
> +    for ( i = 32; i < gicv2_info.nr_lines; i += 32 )
> +        writel_gicd(~0x0, GICD_ICENABLER + (i / 32) * 4);
> +
> +    /* Turn on the distributor */
> +    writel_gicd(GICD_CTL_ENABLE, GICD_CTLR);
> +}
> +
> +static void __cpuinit gicv2_cpu_init(void)
> +{
> +    int i;
> +
> +    this_cpu(gic_cpu_id) = readl_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. */
> +    writel_gicd(0xffff0000, GICD_ICENABLER); /* Disable all PPI */
> +    writel_gicd(0x0000ffff, GICD_ISENABLER); /* Enable all SGI */
> +
> +    /* Set SGI priorities */
> +    for ( i = 0; i < 16; i += 4 )
> +        writel_gicd(GIC_PRI_IPI << 24 | GIC_PRI_IPI << 16 |
> +                    GIC_PRI_IPI << 8 | GIC_PRI_IPI,
> +                    GICD_IPRIORITYR + (i / 4) * 4);
> +
> +    /* Set PPI priorities */
> +    for ( i = 16; i < 32; i += 4 )
> +        writel_gicd(GIC_PRI_IRQ << 24 | GIC_PRI_IRQ << 16 |
> +                    GIC_PRI_IRQ << 8 | GIC_PRI_IRQ,
> +                    GICD_IPRIORITYR + (i / 4) * 4);
> +
> +    /* Local settings: interface controller */
> +    /* Don't mask by priority */
> +    writel_gicc(0xff, GICC_PMR);
> +    /* Finest granularity of priority */
> +    writel_gicc(0x0, GICC_BPR);
> +    /* Turn on delivery */
> +    writel_gicc(GICC_CTL_ENABLE|GICC_CTL_EOI, GICC_CTLR);
> +}
> +
> +static void gicv2_cpu_disable(void)
> +{
> +    writel_gicc(0x0, GICC_CTLR);
> +}
> +
> +static void __cpuinit gicv2_hyp_init(void)
> +{
> +    uint32_t vtr;
> +    uint8_t nr_lrs;
> +
> +    vtr = readl_gich(GICH_VTR);
> +    nr_lrs  = (vtr & GICH_V2_VTR_NRLRGS) + 1;
> +    gicv2_info.nr_lrs = nr_lrs;
> +
> +    writel_gich(GICH_MISR_EOI, GICH_MISR);
> +}
> +
> +static void __cpuinit gicv2_hyp_disable(void)
> +{
> +    writel_gich(0, GICH_HCR);
> +}
> +
> +static int gicv2_secondary_cpu_init(void)
> +{
> +    spin_lock(&gicv2.lock);
> +
> +    gicv2_cpu_init();
> +    gicv2_hyp_init();
> +
> +    spin_unlock(&gicv2.lock);
> +
> +    return 0;
> +}
> +
> +static void gicv2_send_SGI(enum gic_sgi sgi, enum gic_sgi_mode irqmode,
> +                           const cpumask_t *cpu_mask)
> +{
> +    unsigned int mask = 0;
> +    cpumask_t online_mask;
> +
> +    switch ( irqmode )
> +    {
> +    case SGI_TARGET_OTHERS:
> +        writel_gicd(GICD_SGI_TARGET_OTHERS | sgi, GICD_SGIR);
> +        break;
> +    case SGI_TARGET_SELF:
> +        writel_gicd(GICD_SGI_TARGET_SELF | sgi, GICD_SGIR);
> +        break;
> +    case SGI_TARGET_LIST:
> +        cpumask_and(&online_mask, cpu_mask, &cpu_online_map);
> +        mask = gicv2_cpu_mask(&online_mask);
> +        writel_gicd(GICD_SGI_TARGET_LIST |
> +                    (mask << GICD_SGI_TARGET_SHIFT) | sgi,
> +                    GICD_SGIR);
> +        break;
> +    default:
> +        BUG();
> +    }
> +}
> +
> +/* Shut down the per-CPU GIC interface */
> +static void gicv2_disable_interface(void)
> +{
> +    spin_lock(&gicv2.lock);
> +    gicv2_cpu_disable();
> +    gicv2_hyp_disable();
> +    spin_unlock(&gicv2.lock);
> +}
> +
> +static void gicv2_update_lr(int lr, const struct pending_irq *p,
> +                            unsigned int state)
> +{
> +    uint32_t lr_reg;
> +
> +    BUG_ON(lr >= gicv2_info.nr_lrs);
> +    BUG_ON(lr < 0);
> +
> +    lr_reg = (((state & GICH_V2_LR_STATE_MASK) << GICH_V2_LR_STATE_SHIFT)  |
> +              ((GIC_PRI_TO_GUEST(p->priority) & GICH_V2_LR_PRIORITY_MASK)
> +                                             << GICH_V2_LR_PRIORITY_SHIFT) |
> +              ((p->irq & GICH_V2_LR_VIRTUAL_MASK) << 
> GICH_V2_LR_VIRTUAL_SHIFT));
> +
> +    if ( p->desc != NULL )
> +    {
> +        if ( platform_has_quirk(PLATFORM_QUIRK_GUEST_PIRQ_NEED_EOI) )
> +            lr_reg |= GICH_V2_LR_MAINTENANCE_IRQ;
> +        else
> +            lr_reg |= GICH_V2_LR_HW | ((p->desc->irq & 
> GICH_V2_LR_PHYSICAL_MASK )
> +                            << GICH_V2_LR_PHYSICAL_SHIFT);
> +    }
> +
> +    writel_gich(lr_reg, GICH_LR + lr * 4);
> +}
> +
> +static void gicv2_clear_lr(int lr)
> +{
> +    writel_gich(0, GICH_LR + lr * 4);
> +}
> +
> +static int gicv2v_setup(struct domain *d)
> +{
> +    int ret;
> +
> +    /*
> +     * The hardware domain gets the hardware address.
> +     * Guests get the virtual platform layout.
> +     */
> +    if ( is_hardware_domain(d) )
> +    {
> +        d->arch.vgic.dbase = gicv2.dbase;
> +        d->arch.vgic.cbase = gicv2.cbase;
> +    }
> +    else
> +    {
> +        d->arch.vgic.dbase = GUEST_GICD_BASE;
> +        d->arch.vgic.cbase = GUEST_GICC_BASE;
> +    }
> +
> +    /*
> +     * 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, paddr_to_pfn(d->arch.vgic.cbase), 1,
> +                            paddr_to_pfn(gicv2.vbase));
> +    if ( ret )
> +        return ret;
> +
> +    if ( !platform_has_quirk(PLATFORM_QUIRK_GIC_64K_STRIDE) )
> +        ret = map_mmio_regions(d, paddr_to_pfn(d->arch.vgic.cbase + 
> PAGE_SIZE),
> +                               2, paddr_to_pfn(gicv2.vbase + PAGE_SIZE));
> +    else
> +        ret = map_mmio_regions(d, paddr_to_pfn(d->arch.vgic.cbase + 
> PAGE_SIZE),
> +                               2, paddr_to_pfn(gicv2.vbase + 16*PAGE_SIZE));
> +
> +    return ret;
> +}
> +
> +static void gicv2_read_lr(int lr, struct gic_lr *lr_reg)
> +{
> +    uint32_t lrv;
> +
> +    lrv          = readl_gich(GICH_LR + lr * 4);
> +    lr_reg->pirq = (lrv >> GICH_V2_LR_PHYSICAL_SHIFT) & 
> GICH_V2_LR_PHYSICAL_MASK;
> +    lr_reg->virq = (lrv >> GICH_V2_LR_VIRTUAL_SHIFT) & 
> GICH_V2_LR_VIRTUAL_MASK;
> +    lr_reg->priority = (lrv >> GICH_V2_LR_PRIORITY_SHIFT) & 
> GICH_V2_LR_PRIORITY_MASK;
> +    lr_reg->state     = (lrv >> GICH_V2_LR_STATE_SHIFT) & 
> GICH_V2_LR_STATE_MASK;
> +    lr_reg->hw_status = (lrv >> GICH_V2_LR_HW_SHIFT) & GICH_V2_LR_HW_MASK;
> +    lr_reg->grp       = (lrv >> GICH_V2_LR_GRP_SHIFT) & GICH_V2_LR_GRP_MASK;
> +}
> +
> +static void gicv2_write_lr(int lr, const struct gic_lr *lr_reg)
> +{
> +    uint32_t lrv = 0;
> +
> +    lrv = ( ((lr_reg->pirq & GICH_V2_LR_PHYSICAL_MASK) << 
> GICH_V2_LR_PHYSICAL_SHIFT) |
> +          ((lr_reg->virq & GICH_V2_LR_VIRTUAL_MASK) << 
> GICH_V2_LR_VIRTUAL_SHIFT)   |
> +          ((uint32_t)(lr_reg->priority & GICH_V2_LR_PRIORITY_MASK)
> +                                      << GICH_V2_LR_PRIORITY_SHIFT) |
> +          ((uint32_t)(lr_reg->state & GICH_V2_LR_STATE_MASK)
> +                                   << GICH_V2_LR_STATE_SHIFT) |
> +          ((uint32_t)(lr_reg->hw_status & GICH_V2_LR_HW_MASK)
> +                                       << GICH_V2_LR_HW_SHIFT)  |
> +          ((uint32_t)(lr_reg->grp & GICH_V2_LR_GRP_MASK) << 
> GICH_V2_LR_GRP_SHIFT) );
> +
> +    writel_gich(lrv, GICH_LR + lr * 4);
> +}
> +
> +static void gicv2_hcr_status(uint32_t flag, bool_t status)
> +{
> +    uint32_t hcr = readl_gich(GICH_HCR);
> +
> +    if ( status )
> +        hcr |= flag;
> +    else
> +        hcr &= (~flag);
> +
> +    writel_gich(hcr, GICH_HCR);
> +}
> +
> +static unsigned int gicv2_read_vmcr_priority(void)
> +{
> +   return ((readl_gich(GICH_VMCR) >> GICH_V2_VMCR_PRIORITY_SHIFT)
> +           & GICH_V2_VMCR_PRIORITY_MASK);
> +}
> +
> +static unsigned int gicv2_read_apr(int apr_reg)
> +{
> +   return readl_gich(GICH_APR);
> +}
> +
> +static void gicv2_irq_enable(struct irq_desc *desc)
> +{
> +    unsigned long flags;
> +    int irq = desc->irq;
> +
> +    ASSERT(spin_is_locked(&desc->lock));
> +
> +    spin_lock_irqsave(&gicv2.lock, flags);
> +    clear_bit(_IRQ_DISABLED, &desc->status);
> +    dsb(sy);
> +    /* Enable routing */
> +    writel_gicd((1u << (irq % 32)), GICD_ISENABLER + (irq / 32) * 4);
> +    spin_unlock_irqrestore(&gicv2.lock, flags);
> +}
> +
> +static void gicv2_irq_disable(struct irq_desc *desc)
> +{
> +    unsigned long flags;
> +    int irq = desc->irq;
> +
> +    ASSERT(spin_is_locked(&desc->lock));
> +
> +    spin_lock_irqsave(&gicv2.lock, flags);
> +    /* Disable routing */
> +    writel_gicd(1u << (irq % 32), GICD_ICENABLER + (irq / 32) * 4);
> +    set_bit(_IRQ_DISABLED, &desc->status);
> +    spin_unlock_irqrestore(&gicv2.lock, flags);
> +}
> +
> +static unsigned int gicv2_irq_startup(struct irq_desc *desc)
> +{
> +    gicv2_irq_enable(desc);
> +
> +    return 0;
> +}
> +
> +static void gicv2_irq_shutdown(struct irq_desc *desc)
> +{
> +    gicv2_irq_disable(desc);
> +}
> +
> +static void gicv2_irq_ack(struct irq_desc *desc)
> +{
> +    /* No ACK -- reading IAR has done this for us */
> +}
> +
> +static void gicv2_host_irq_end(struct irq_desc *desc)
> +{
> +    /* Lower the priority */
> +    gicv2_eoi_irq(desc);
> +    /* Deactivate */
> +    gicv2_dir_irq(desc);
> +}
> +
> +static void gicv2_guest_irq_end(struct irq_desc *desc)
> +{
> +    /* Lower the priority of the IRQ */
> +    gicv2_eoi_irq(desc);
> +    /* Deactivation happens in maintenance interrupt / via GICV */
> +}
> +
> +static void gicv2_irq_set_affinity(struct irq_desc *desc, const cpumask_t 
> *cpu_mask)
> +{
> +    unsigned int mask;
> +
> +    ASSERT(!cpumask_empty(cpu_mask));
> +
> +    spin_lock(&gicv2.lock);
> +
> +    mask = gicv2_cpu_mask(cpu_mask);
> +
> +    /* Set target CPU mask (RAZ/WI on uniprocessor) */
> +    writeb_gicd(mask, GICD_ITARGETSR + desc->irq);
> +
> +    spin_unlock(&gicv2.lock);
> +}
> +
> +static int gicv2_make_dt_node(const struct domain *d,
> +                              const struct dt_device_node *node, void *fdt)
> +{
> +    const struct dt_device_node *gic = dt_interrupt_controller;
> +    const void *compatible = NULL;
> +    u32 len;
> +    const __be32 *regs;
> +    int res = 0;
> +
> +    compatible = dt_get_property(gic, "compatible", &len);
> +    if ( !compatible )
> +    {
> +        dprintk(XENLOG_ERR, "Can't find compatible property for the gic 
> node\n");
> +        return -FDT_ERR_XEN(ENOENT);
> +    }
> +
> +    res = fdt_begin_node(fdt, "interrupt-controller");
> +    if ( res )
> +        return res;
> +
> +    res = fdt_property(fdt, "compatible", compatible, len);
> +    if ( res )
> +        return res;
> +
> +    res = fdt_property_cell(fdt, "#interrupt-cells", 3);
> +    if ( res )
> +        return res;
> +
> +    res = fdt_property(fdt, "interrupt-controller", NULL, 0);
> +
> +    if ( res )
> +        return res;
> +
> +    /*
> +     * DTB provides up to 4 regions to handle virtualization
> +     * however dom0 just needs GICC and GICD provided by Xen.
> +     */
> +    regs = dt_get_property(gic, "reg", &len);
> +    if ( !regs )
> +    {
> +        dprintk(XENLOG_ERR, "Can't find reg property for the gic node\n");
> +        return -FDT_ERR_XEN(ENOENT);
> +    }
> +
> +    len = dt_cells_to_size(dt_n_addr_cells(node) + dt_n_size_cells(node));
> +    len *= 2;
> +
> +    res = fdt_property(fdt, "reg", regs, len);
> +
> +    return res;
> +}
> +
> +/* XXX different for level vs edge */
> +static hw_irq_controller gicv2_host_irq_type = {
> +    .typename     = "gic-v2",
> +    .startup      = gicv2_irq_startup,
> +    .shutdown     = gicv2_irq_shutdown,
> +    .enable       = gicv2_irq_enable,
> +    .disable      = gicv2_irq_disable,
> +    .ack          = gicv2_irq_ack,
> +    .end          = gicv2_host_irq_end,
> +    .set_affinity = gicv2_irq_set_affinity,
> +};
> +
> +static hw_irq_controller gicv2_guest_irq_type = {
> +    .typename     = "gic-v2",
> +    .startup      = gicv2_irq_startup,
> +    .shutdown     = gicv2_irq_shutdown,
> +    .enable       = gicv2_irq_enable,
> +    .disable      = gicv2_irq_disable,
> +    .ack          = gicv2_irq_ack,
> +    .end          = gicv2_guest_irq_end,
> +    .set_affinity = gicv2_irq_set_affinity,
> +};
> +
> +const static struct gic_hw_operations gicv2_ops = {
> +    .info                = &gicv2_info,
> +    .secondary_init      = gicv2_secondary_cpu_init,
> +    .save_state          = gicv2_save_state,
> +    .restore_state       = gicv2_restore_state,
> +    .dump_state          = gicv2_dump_state,
> +    .gicv_setup          = gicv2v_setup,
> +    .gic_host_irq_type   = &gicv2_host_irq_type,
> +    .gic_guest_irq_type  = &gicv2_guest_irq_type,
> +    .eoi_irq             = gicv2_eoi_irq,
> +    .deactivate_irq      = gicv2_dir_irq,
> +    .read_irq            = gicv2_read_irq,
> +    .set_irq_properties  = 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,
> +    .read_apr            = gicv2_read_apr,
> +    .make_dt_node        = gicv2_make_dt_node,
> +};
> +
> +/* Set up the GIC */
> +static int __init gicv2_init(struct dt_device_node *node, const void *data)
> +{
> +    int res;
> +
> +    dt_device_set_used_by(node, DOMID_XEN);
> +
> +    res = dt_device_get_address(node, 0, &gicv2.dbase, NULL);
> +    if ( res || !gicv2.dbase || (gicv2.dbase & ~PAGE_MASK) )
> +        panic("GICv2: Cannot find a valid address for the distributor");
> +
> +    res = dt_device_get_address(node, 1, &gicv2.cbase, NULL);
> +    if ( res || !gicv2.cbase || (gicv2.cbase & ~PAGE_MASK) )
> +        panic("GICv2: Cannot find a valid address for the CPU");
> +
> +    res = dt_device_get_address(node, 2, &gicv2.hbase, NULL);
> +    if ( res || !gicv2.hbase || (gicv2.hbase & ~PAGE_MASK) )
> +        panic("GICv2: Cannot find a valid address for the hypervisor");
> +
> +    res = dt_device_get_address(node, 3, &gicv2.vbase, NULL);
> +    if ( res || !gicv2.vbase || (gicv2.vbase & ~PAGE_MASK) )
> +        panic("GICv2: Cannot find a valid address for the virtual CPU");
> +
> +    res = platform_get_irq(node, 0);
> +    if ( res < 0 )
> +        panic("GICv2: Cannot find the maintenance IRQ");
> +    gicv2_info.maintenance_irq = res;
> +
> +    /* Set the GIC as the primary interrupt controller */
> +    dt_interrupt_controller = node;
> +
> +    /* TODO: Add check on distributor, cpu size */
> +
> +    printk("GICv2 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",
> +              gicv2.dbase, gicv2.cbase, gicv2.hbase, gicv2.vbase,
> +              gicv2_info.maintenance_irq);
> +
> +    if ( (gicv2.dbase & ~PAGE_MASK) || (gicv2.cbase & ~PAGE_MASK) ||
> +         (gicv2.hbase & ~PAGE_MASK) || (gicv2.vbase & ~PAGE_MASK) )
> +        panic("GICv2 interfaces not page aligned");
> +
> +    gicv2.map_dbase = ioremap_nocache(gicv2.dbase, PAGE_SIZE);
> +    if ( !gicv2.map_dbase )
> +        panic("GICv2: Failed to ioremap for GIC distributor\n");
> +
> +    gicv2.map_cbase[0] = ioremap_nocache(gicv2.cbase, PAGE_SIZE);
> +
> +    if ( platform_has_quirk(PLATFORM_QUIRK_GIC_64K_STRIDE) )
> +        gicv2.map_cbase[1] = ioremap_nocache(gicv2.cbase + PAGE_SIZE * 0x10,
> +                                           PAGE_SIZE);
> +    else
> +        gicv2.map_cbase[1] = ioremap_nocache(gicv2.cbase + PAGE_SIZE, 
> PAGE_SIZE);
> +
> +    if ( !gicv2.map_cbase[0] || !gicv2.map_cbase[1] )
> +        panic("GICv2: Failed to ioremap for GIC CPU interface\n");
> +
> +    gicv2.map_hbase = ioremap_nocache(gicv2.hbase, PAGE_SIZE);
> +    if ( !gicv2.map_hbase )
> +        panic("GICv2: Failed to ioremap for GIC Virtual interface\n");
> +
> +    /* Global settings: interrupt distributor */
> +    spin_lock_init(&gicv2.lock);
> +    spin_lock(&gicv2.lock);
> +
> +    gicv2_dist_init();
> +    gicv2_cpu_init();
> +    gicv2_hyp_init();
> +
> +    spin_unlock(&gicv2.lock);
> +
> +    gicv2_info.hw_version = GIC_V2;
> +    register_gic_ops(&gicv2_ops);
> +
> +    return 0;
> +}
> +
> +static const char * const gicv2_dt_compat[] __initconst =
> +{
> +    DT_COMPAT_GIC_CORTEX_A15,
> +    DT_COMPAT_GIC_CORTEX_A7,
> +    DT_COMPAT_GIC_400,
> +    NULL
> +};
> +
> +DT_DEVICE_START(gicv2, "GICv2:", DEVICE_GIC)
> +        .compatible = gicv2_dt_compat,
> +        .init = gicv2_init,
> +DT_DEVICE_END
> +
> +/*
> + * Local variables:
> + * mode: C
> + * c-file-style: "BSD"
> + * c-basic-offset: 4
> + * indent-tabs-mode: nil
> + * End:
> + */
> -- 
> 1.9.1
> 
> 
> 
> _______________________________________________
> Xen-devel mailing list
> Xen-devel@xxxxxxxxxxxxx
> http://lists.xen.org/xen-devel
> 

_______________________________________________
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®.