Message ID | 20180321163235.12529-5-andre.przywara@linaro.org |
---|---|
State | New |
Headers | show |
Series | New VGIC(-v2) implementation | expand |
On Wed, 21 Mar 2018, Andre Przywara wrote: > To synchronize level triggered interrupts which are mapped into a guest, > we need to update the virtual line level at certain points in time. > For a hardware mapped interrupt the GIC is the only place where we can > easily access this information. > Implement a gic_hw_operations member to return the pending state of a > particular interrupt. Due to hardware limitations this only works for > private interrupts of the current CPU, so there is no CPU field in the > prototype. > This adds gicv2/3_peek_irq() helper functions, to read a bit in a bitmap > spread over several MMIO registers. > > Signed-off-by: Andre Przywara <andre.przywara@linaro.org> > Reviewed-by: Julien Grall <julien.grall@arm.com> Acked-by: Stefano Stabellini <sstabellini@kernel.org> > --- > Changelog v2 ... v3: > - introduce gicv[23]_peek_irq() (moved from patch before) > > xen/arch/arm/gic-v2.c | 15 +++++++++++++++ > xen/arch/arm/gic-v3.c | 19 +++++++++++++++++++ > xen/include/asm-arm/gic.h | 11 +++++++++++ > 3 files changed, 45 insertions(+) > > diff --git a/xen/arch/arm/gic-v2.c b/xen/arch/arm/gic-v2.c > index d1f1578c05..b440a45e8e 100644 > --- a/xen/arch/arm/gic-v2.c > +++ b/xen/arch/arm/gic-v2.c > @@ -243,6 +243,15 @@ static void gicv2_poke_irq(struct irq_desc *irqd, uint32_t offset) > writel_gicd(1U << (irqd->irq % 32), offset + (irqd->irq / 32) * 4); > } > > +static bool gicv2_peek_irq(struct irq_desc *irqd, uint32_t offset) > +{ > + uint32_t reg; > + > + reg = readl_gicd(offset + (irqd->irq / 32) * 4) & (1U << (irqd->irq % 32)); > + > + return reg; > +} > + > static void gicv2_set_active_state(struct irq_desc *irqd, bool active) > { > ASSERT(spin_is_locked(&irqd->lock)); > @@ -580,6 +589,11 @@ static unsigned int gicv2_read_apr(int apr_reg) > return readl_gich(GICH_APR); > } > > +static bool gicv2_read_pending_state(struct irq_desc *irqd) > +{ > + return gicv2_peek_irq(irqd, GICD_ISPENDR); > +} > + > static void gicv2_irq_enable(struct irq_desc *desc) > { > unsigned long flags; > @@ -1325,6 +1339,7 @@ const static struct gic_hw_operations gicv2_ops = { > .write_lr = gicv2_write_lr, > .read_vmcr_priority = gicv2_read_vmcr_priority, > .read_apr = gicv2_read_apr, > + .read_pending_state = gicv2_read_pending_state, > .make_hwdom_dt_node = gicv2_make_hwdom_dt_node, > .make_hwdom_madt = gicv2_make_hwdom_madt, > .get_hwdom_extra_madt_size = gicv2_get_hwdom_extra_madt_size, > diff --git a/xen/arch/arm/gic-v3.c b/xen/arch/arm/gic-v3.c > index f244d51661..5c9a783968 100644 > --- a/xen/arch/arm/gic-v3.c > +++ b/xen/arch/arm/gic-v3.c > @@ -444,6 +444,19 @@ static void gicv3_poke_irq(struct irq_desc *irqd, u32 offset, bool wait_for_rwp) > gicv3_wait_for_rwp(irqd->irq); > } > > +static bool gicv3_peek_irq(struct irq_desc *irqd, u32 offset) > +{ > + void __iomem *base; > + unsigned int irq = irqd->irq; > + > + if ( irq >= NR_GIC_LOCAL_IRQS) > + base = GICD + (irq / 32) * 4; > + else > + base = GICD_RDIST_SGI_BASE; > + > + return !!(readl(base + offset) & (1U << (irq % 32))); > +} > + > static void gicv3_unmask_irq(struct irq_desc *irqd) > { > gicv3_poke_irq(irqd, GICD_ISENABLER, false); > @@ -1144,6 +1157,11 @@ static unsigned int gicv3_read_apr(int apr_reg) > } > } > > +static bool gicv3_read_pending_state(struct irq_desc *irqd) > +{ > + return gicv3_peek_irq(irqd, GICD_ISPENDR); > +} > + > static void gicv3_irq_enable(struct irq_desc *desc) > { > unsigned long flags; > @@ -1812,6 +1830,7 @@ static const struct gic_hw_operations gicv3_ops = { > .write_lr = gicv3_write_lr, > .read_vmcr_priority = gicv3_read_vmcr_priority, > .read_apr = gicv3_read_apr, > + .read_pending_state = gicv3_read_pending_state, > .secondary_init = gicv3_secondary_cpu_init, > .make_hwdom_dt_node = gicv3_make_hwdom_dt_node, > .make_hwdom_madt = gicv3_make_hwdom_madt, > diff --git a/xen/include/asm-arm/gic.h b/xen/include/asm-arm/gic.h > index 2aca243ac3..58b910fe6a 100644 > --- a/xen/include/asm-arm/gic.h > +++ b/xen/include/asm-arm/gic.h > @@ -373,6 +373,8 @@ struct gic_hw_operations { > unsigned int (*read_vmcr_priority)(void); > /* Read APRn register */ > unsigned int (*read_apr)(int apr_reg); > + /* Query the pending state of an interrupt at the distributor level. */ > + bool (*read_pending_state)(struct irq_desc *irqd); > /* Secondary CPU init */ > int (*secondary_init)(void); > /* Create GIC node for the hardware domain */ > @@ -417,6 +419,15 @@ static inline void gic_set_pending_state(struct irq_desc *irqd, bool state) > gic_hw_ops->set_pending_state(irqd, state); > } > > +/* > + * Read the pending state of an interrupt from the distributor. > + * For private IRQs this only works for those of the current CPU. > + */ > +static inline bool gic_read_pending_state(struct irq_desc *irqd) > +{ > + return gic_hw_ops->read_pending_state(irqd); > +} > + > void register_gic_ops(const struct gic_hw_operations *ops); > int gic_make_hwdom_dt_node(const struct domain *d, > const struct dt_device_node *gic, > -- > 2.14.1 >
diff --git a/xen/arch/arm/gic-v2.c b/xen/arch/arm/gic-v2.c index d1f1578c05..b440a45e8e 100644 --- a/xen/arch/arm/gic-v2.c +++ b/xen/arch/arm/gic-v2.c @@ -243,6 +243,15 @@ static void gicv2_poke_irq(struct irq_desc *irqd, uint32_t offset) writel_gicd(1U << (irqd->irq % 32), offset + (irqd->irq / 32) * 4); } +static bool gicv2_peek_irq(struct irq_desc *irqd, uint32_t offset) +{ + uint32_t reg; + + reg = readl_gicd(offset + (irqd->irq / 32) * 4) & (1U << (irqd->irq % 32)); + + return reg; +} + static void gicv2_set_active_state(struct irq_desc *irqd, bool active) { ASSERT(spin_is_locked(&irqd->lock)); @@ -580,6 +589,11 @@ static unsigned int gicv2_read_apr(int apr_reg) return readl_gich(GICH_APR); } +static bool gicv2_read_pending_state(struct irq_desc *irqd) +{ + return gicv2_peek_irq(irqd, GICD_ISPENDR); +} + static void gicv2_irq_enable(struct irq_desc *desc) { unsigned long flags; @@ -1325,6 +1339,7 @@ const static struct gic_hw_operations gicv2_ops = { .write_lr = gicv2_write_lr, .read_vmcr_priority = gicv2_read_vmcr_priority, .read_apr = gicv2_read_apr, + .read_pending_state = gicv2_read_pending_state, .make_hwdom_dt_node = gicv2_make_hwdom_dt_node, .make_hwdom_madt = gicv2_make_hwdom_madt, .get_hwdom_extra_madt_size = gicv2_get_hwdom_extra_madt_size, diff --git a/xen/arch/arm/gic-v3.c b/xen/arch/arm/gic-v3.c index f244d51661..5c9a783968 100644 --- a/xen/arch/arm/gic-v3.c +++ b/xen/arch/arm/gic-v3.c @@ -444,6 +444,19 @@ static void gicv3_poke_irq(struct irq_desc *irqd, u32 offset, bool wait_for_rwp) gicv3_wait_for_rwp(irqd->irq); } +static bool gicv3_peek_irq(struct irq_desc *irqd, u32 offset) +{ + void __iomem *base; + unsigned int irq = irqd->irq; + + if ( irq >= NR_GIC_LOCAL_IRQS) + base = GICD + (irq / 32) * 4; + else + base = GICD_RDIST_SGI_BASE; + + return !!(readl(base + offset) & (1U << (irq % 32))); +} + static void gicv3_unmask_irq(struct irq_desc *irqd) { gicv3_poke_irq(irqd, GICD_ISENABLER, false); @@ -1144,6 +1157,11 @@ static unsigned int gicv3_read_apr(int apr_reg) } } +static bool gicv3_read_pending_state(struct irq_desc *irqd) +{ + return gicv3_peek_irq(irqd, GICD_ISPENDR); +} + static void gicv3_irq_enable(struct irq_desc *desc) { unsigned long flags; @@ -1812,6 +1830,7 @@ static const struct gic_hw_operations gicv3_ops = { .write_lr = gicv3_write_lr, .read_vmcr_priority = gicv3_read_vmcr_priority, .read_apr = gicv3_read_apr, + .read_pending_state = gicv3_read_pending_state, .secondary_init = gicv3_secondary_cpu_init, .make_hwdom_dt_node = gicv3_make_hwdom_dt_node, .make_hwdom_madt = gicv3_make_hwdom_madt, diff --git a/xen/include/asm-arm/gic.h b/xen/include/asm-arm/gic.h index 2aca243ac3..58b910fe6a 100644 --- a/xen/include/asm-arm/gic.h +++ b/xen/include/asm-arm/gic.h @@ -373,6 +373,8 @@ struct gic_hw_operations { unsigned int (*read_vmcr_priority)(void); /* Read APRn register */ unsigned int (*read_apr)(int apr_reg); + /* Query the pending state of an interrupt at the distributor level. */ + bool (*read_pending_state)(struct irq_desc *irqd); /* Secondary CPU init */ int (*secondary_init)(void); /* Create GIC node for the hardware domain */ @@ -417,6 +419,15 @@ static inline void gic_set_pending_state(struct irq_desc *irqd, bool state) gic_hw_ops->set_pending_state(irqd, state); } +/* + * Read the pending state of an interrupt from the distributor. + * For private IRQs this only works for those of the current CPU. + */ +static inline bool gic_read_pending_state(struct irq_desc *irqd) +{ + return gic_hw_ops->read_pending_state(irqd); +} + void register_gic_ops(const struct gic_hw_operations *ops); int gic_make_hwdom_dt_node(const struct domain *d, const struct dt_device_node *gic,