[RFC v3 4/9] irqchip: gic: Introduce shadow irqs for FIQ

Peter De Schrijver pdeschrijver at nvidia.com
Fri Jun 6 00:46:14 PDT 2014


On Thu, Jun 05, 2014 at 11:53:09AM +0200, Daniel Thompson wrote:
> This patch registers two virqs for each interrupt source it supports.
> Using multiple virqs allows the GIC driver to automatically modify the group
> register, allowing the new virqs to be used as argument to enable_fiq().
> This also allows FIQ resources to be described in the device tree's
> interrupt list using a special flag (currently 0x80).
> 
> Both these aspects combine and allow a driver to deploy a FIQ handler
> without any machine specific knowledge; it can be used effectively on
> multi-arch kernels.
> 
> Signed-off-by: Daniel Thompson <daniel.thompson at linaro.org>
> Cc: Thomas Gleixner <tglx at linutronix.de>
> Cc: Jason Cooper <jason at lakedaemon.net>
> Cc: Nicolas Pitre <nicolas.pitre at linaro.org>
> Cc: Christoffer Dall <christoffer.dall at linaro.org>
> Cc: Sricharan R <r.sricharan at ti.com>
> ---
>  drivers/irqchip/irq-gic.c | 62 ++++++++++++++++++++++++++++++++++++++++++-----
>  1 file changed, 56 insertions(+), 6 deletions(-)
> 
> diff --git a/drivers/irqchip/irq-gic.c b/drivers/irqchip/irq-gic.c
> index aa8efe4..9a4712d 100644
> --- a/drivers/irqchip/irq-gic.c
> +++ b/drivers/irqchip/irq-gic.c
> @@ -42,12 +42,17 @@
>  #include <linux/irqchip/chained_irq.h>
>  #include <linux/irqchip/arm-gic.h>
>  
> +#ifdef CONFIG_FIQ
> +#include <asm/fiq.h>
> +#endif
>  #include <asm/irq.h>
>  #include <asm/exception.h>
>  #include <asm/smp_plat.h>
>  
>  #include "irqchip.h"
>  
> +#define GIC_INTSPEC_IRQ_IS_FIQ (1 << 7)
> +
>  union gic_base {
>  	void __iomem *common_base;
>  	void __percpu * __iomem *percpu_base;
> @@ -65,6 +70,7 @@ struct gic_chip_data {
>  #endif
>  	struct irq_domain *domain;
>  	unsigned int gic_irqs;
> +	unsigned int fiq_shadow_offset;
>  #ifdef CONFIG_GIC_NON_BANKED
>  	void __iomem *(*get_base)(union gic_base *);
>  #endif
> @@ -143,11 +149,34 @@ static inline void __iomem *gic_cpu_base(struct irq_data *d)
>  	return gic_data_cpu_base(gic_data);
>  }
>  
> +static inline bool gic_is_fiq(struct irq_data *d)
> +{
> +	struct gic_chip_data *gic_data = irq_data_get_irq_chip_data(d);
> +	return d->hwirq > gic_data->gic_irqs;
> +}
> +
>  static inline unsigned int gic_irq(struct irq_data *d)
>  {
> +	struct gic_chip_data *gic_data = irq_data_get_irq_chip_data(d);
> +	if (gic_is_fiq(d))
> +		return d->hwirq - gic_data->fiq_shadow_offset;
>  	return d->hwirq;
>  }
>  
> +static void gic_set_group_irq(struct irq_data *d, int group)
> +{
> +	unsigned int reg = gic_irq(d) / 32 * 4;
> +	u32 mask = 1 << (gic_irq(d) % 32);
> +	u32 val;
> +
> +	val = readl_relaxed(gic_dist_base(d) + GIC_DIST_IGROUP + reg);
> +	if (group)
> +		val |= mask;
> +	else
> +		val &= ~mask;
> +	writel_relaxed(val, gic_dist_base(d) + GIC_DIST_IGROUP + reg);
> +}
> +
>  /*
>   * Routines to acknowledge, disable and enable interrupts
>   */
> @@ -159,6 +188,8 @@ static void gic_mask_irq(struct irq_data *d)
>  	writel_relaxed(mask, gic_dist_base(d) + GIC_DIST_ENABLE_CLEAR + (gic_irq(d) / 32) * 4);
>  	if (gic_arch_extn.irq_mask)
>  		gic_arch_extn.irq_mask(d);
> +	if (gic_is_fiq(d))
> +		gic_set_group_irq(d, 1);
>  	raw_spin_unlock(&irq_controller_lock);
>  }
>  
> @@ -167,6 +198,8 @@ static void gic_unmask_irq(struct irq_data *d)
>  	u32 mask = 1 << (gic_irq(d) % 32);
>  
>  	raw_spin_lock(&irq_controller_lock);
> +	if (gic_is_fiq(d))
> +		gic_set_group_irq(d, 0);
>  	if (gic_arch_extn.irq_unmask)
>  		gic_arch_extn.irq_unmask(d);
>  	writel_relaxed(mask, gic_dist_base(d) + GIC_DIST_ENABLE_SET + (gic_irq(d) / 32) * 4);
> @@ -940,7 +973,12 @@ static int gic_routable_irq_domain_xlate(struct irq_domain *d,
>  				unsigned long *out_hwirq,
>  				unsigned int *out_type)
>  {
> +	struct gic_chip_data *gic_data = d->host_data;
>  	*out_hwirq += 16;
> +
> +	if (intspec[2] & GIC_INTSPEC_IRQ_IS_FIQ)
> +		*out_hwirq += gic_data->fiq_shadow_offset;
> +
>  	return 0;
>  }
>  
> @@ -1026,10 +1064,11 @@ void __init gic_init_bases(unsigned int gic_nr, int irq_start,
>  	gic->gic_irqs = gic_irqs;
>  
>  	gic_irqs -= hwirq_base; /* calculate # of irqs to allocate */
> +	gic->fiq_shadow_offset = gic_irqs;
>  
>  	if (of_property_read_u32(node, "arm,routable-irqs",
>  				 &nr_routable_irqs)) {
> -		irq_base = irq_alloc_descs(irq_start, 16, gic_irqs,
> +		irq_base = irq_alloc_descs(irq_start, 16, 2 * gic_irqs,
>  					   numa_node_id());
>  		if (IS_ERR_VALUE(irq_base)) {
>  			WARN(1, "Cannot allocate irq_descs @ IRQ%d, assuming pre-allocated\n",
> @@ -1037,17 +1076,28 @@ void __init gic_init_bases(unsigned int gic_nr, int irq_start,
>  			irq_base = irq_start;
>  		}
>  
> -		gic->domain = irq_domain_add_legacy(node, gic_irqs, irq_base,
> -					hwirq_base, &gic_irq_domain_ops, gic);
> +		gic->domain =
> +		    irq_domain_add_legacy(node, 2 * gic_irqs, irq_base,
> +					  hwirq_base, &gic_irq_domain_ops, gic);
>  	} else {
> -		gic->domain = irq_domain_add_linear(node, nr_routable_irqs,
> -						    &gic_irq_domain_ops,
> -						    gic);
> +		gic->domain = irq_domain_add_linear(node, 2 * nr_routable_irqs,
> +						    &gic_irq_domain_ops, gic);
>  	}
>  
>  	if (WARN_ON(!gic->domain))
>  		return;
>  
> +#ifdef CONFIG_FIQ
> +	/* FIQ can only be supported on platforms without an extended irq_eoi
> +	 * method (otherwise we take a lock during irq_eoi handling).
> +	 */
> +	if (!gic_arch_extn.irq_eoi)
> +		fiq_add_mapping(
> +		    irq_linear_revmap(gic->domain, hwirq_base),
> +		    irq_linear_revmap(gic->domain, hwirq_base + gic_irqs),
> +		    gic_irqs);
> +#endif

This is rather unfortunate. On Tegra for example we don't need a lock for the
irq_eoi because the eoi ack can be handled with a single write to the
appropriate irq ack register.

Cheers,

Peter.



More information about the linux-arm-kernel mailing list