diff options
Diffstat (limited to 'arch/arm/common/gic.c')
| -rw-r--r-- | arch/arm/common/gic.c | 164 | 
1 files changed, 141 insertions, 23 deletions
diff --git a/arch/arm/common/gic.c b/arch/arm/common/gic.c index 0e6ae470c94..a1feb6b4f9f 100644 --- a/arch/arm/common/gic.c +++ b/arch/arm/common/gic.c @@ -40,13 +40,36 @@  #include <linux/slab.h>  #include <asm/irq.h> +#include <asm/exception.h>  #include <asm/mach/irq.h>  #include <asm/hardware/gic.h> -static DEFINE_RAW_SPINLOCK(irq_controller_lock); +union gic_base { +	void __iomem *common_base; +	void __percpu __iomem **percpu_base; +}; + +struct gic_chip_data { +	unsigned int irq_offset; +	union gic_base dist_base; +	union gic_base cpu_base; +#ifdef CONFIG_CPU_PM +	u32 saved_spi_enable[DIV_ROUND_UP(1020, 32)]; +	u32 saved_spi_conf[DIV_ROUND_UP(1020, 16)]; +	u32 saved_spi_target[DIV_ROUND_UP(1020, 4)]; +	u32 __percpu *saved_ppi_enable; +	u32 __percpu *saved_ppi_conf; +#endif +#ifdef CONFIG_IRQ_DOMAIN +	struct irq_domain domain; +#endif +	unsigned int gic_irqs; +#ifdef CONFIG_GIC_NON_BANKED +	void __iomem *(*get_base)(union gic_base *); +#endif +}; -/* Address of GIC 0 CPU interface */ -void __iomem *gic_cpu_base_addr __read_mostly; +static DEFINE_RAW_SPINLOCK(irq_controller_lock);  /*   * Supported arch specific GIC irq extension. @@ -67,16 +90,48 @@ struct irq_chip gic_arch_extn = {  static struct gic_chip_data gic_data[MAX_GIC_NR] __read_mostly; +#ifdef CONFIG_GIC_NON_BANKED +static void __iomem *gic_get_percpu_base(union gic_base *base) +{ +	return *__this_cpu_ptr(base->percpu_base); +} + +static void __iomem *gic_get_common_base(union gic_base *base) +{ +	return base->common_base; +} + +static inline void __iomem *gic_data_dist_base(struct gic_chip_data *data) +{ +	return data->get_base(&data->dist_base); +} + +static inline void __iomem *gic_data_cpu_base(struct gic_chip_data *data) +{ +	return data->get_base(&data->cpu_base); +} + +static inline void gic_set_base_accessor(struct gic_chip_data *data, +					 void __iomem *(*f)(union gic_base *)) +{ +	data->get_base = f; +} +#else +#define gic_data_dist_base(d)	((d)->dist_base.common_base) +#define gic_data_cpu_base(d)	((d)->cpu_base.common_base) +#define gic_set_base_accessor(d,f) +#endif +  static inline void __iomem *gic_dist_base(struct irq_data *d)  {  	struct gic_chip_data *gic_data = irq_data_get_irq_chip_data(d); -	return gic_data->dist_base; +	return gic_data_dist_base(gic_data);  }  static inline void __iomem *gic_cpu_base(struct irq_data *d)  {  	struct gic_chip_data *gic_data = irq_data_get_irq_chip_data(d); -	return gic_data->cpu_base; +	return gic_data_cpu_base(gic_data);  }  static inline unsigned int gic_irq(struct irq_data *d) @@ -215,6 +270,32 @@ static int gic_set_wake(struct irq_data *d, unsigned int on)  #define gic_set_wake	NULL  #endif +asmlinkage void __exception_irq_entry gic_handle_irq(struct pt_regs *regs) +{ +	u32 irqstat, irqnr; +	struct gic_chip_data *gic = &gic_data[0]; +	void __iomem *cpu_base = gic_data_cpu_base(gic); + +	do { +		irqstat = readl_relaxed(cpu_base + GIC_CPU_INTACK); +		irqnr = irqstat & ~0x1c00; + +		if (likely(irqnr > 15 && irqnr < 1021)) { +			irqnr = irq_domain_to_irq(&gic->domain, irqnr); +			handle_IRQ(irqnr, regs); +			continue; +		} +		if (irqnr < 16) { +			writel_relaxed(irqstat, cpu_base + GIC_CPU_EOI); +#ifdef CONFIG_SMP +			handle_IPI(irqnr, regs); +#endif +			continue; +		} +		break; +	} while (1); +} +  static void gic_handle_cascade_irq(unsigned int irq, struct irq_desc *desc)  {  	struct gic_chip_data *chip_data = irq_get_handler_data(irq); @@ -225,7 +306,7 @@ static void gic_handle_cascade_irq(unsigned int irq, struct irq_desc *desc)  	chained_irq_enter(chip, desc);  	raw_spin_lock(&irq_controller_lock); -	status = readl_relaxed(chip_data->cpu_base + GIC_CPU_INTACK); +	status = readl_relaxed(gic_data_cpu_base(chip_data) + GIC_CPU_INTACK);  	raw_spin_unlock(&irq_controller_lock);  	gic_irq = (status & 0x3ff); @@ -270,7 +351,7 @@ static void __init gic_dist_init(struct gic_chip_data *gic)  	u32 cpumask;  	unsigned int gic_irqs = gic->gic_irqs;  	struct irq_domain *domain = &gic->domain; -	void __iomem *base = gic->dist_base; +	void __iomem *base = gic_data_dist_base(gic);  	u32 cpu = 0;  #ifdef CONFIG_SMP @@ -330,8 +411,8 @@ static void __init gic_dist_init(struct gic_chip_data *gic)  static void __cpuinit gic_cpu_init(struct gic_chip_data *gic)  { -	void __iomem *dist_base = gic->dist_base; -	void __iomem *base = gic->cpu_base; +	void __iomem *dist_base = gic_data_dist_base(gic); +	void __iomem *base = gic_data_cpu_base(gic);  	int i;  	/* @@ -368,7 +449,7 @@ static void gic_dist_save(unsigned int gic_nr)  		BUG();  	gic_irqs = gic_data[gic_nr].gic_irqs; -	dist_base = gic_data[gic_nr].dist_base; +	dist_base = gic_data_dist_base(&gic_data[gic_nr]);  	if (!dist_base)  		return; @@ -403,7 +484,7 @@ static void gic_dist_restore(unsigned int gic_nr)  		BUG();  	gic_irqs = gic_data[gic_nr].gic_irqs; -	dist_base = gic_data[gic_nr].dist_base; +	dist_base = gic_data_dist_base(&gic_data[gic_nr]);  	if (!dist_base)  		return; @@ -439,8 +520,8 @@ static void gic_cpu_save(unsigned int gic_nr)  	if (gic_nr >= MAX_GIC_NR)  		BUG(); -	dist_base = gic_data[gic_nr].dist_base; -	cpu_base = gic_data[gic_nr].cpu_base; +	dist_base = gic_data_dist_base(&gic_data[gic_nr]); +	cpu_base = gic_data_cpu_base(&gic_data[gic_nr]);  	if (!dist_base || !cpu_base)  		return; @@ -465,8 +546,8 @@ static void gic_cpu_restore(unsigned int gic_nr)  	if (gic_nr >= MAX_GIC_NR)  		BUG(); -	dist_base = gic_data[gic_nr].dist_base; -	cpu_base = gic_data[gic_nr].cpu_base; +	dist_base = gic_data_dist_base(&gic_data[gic_nr]); +	cpu_base = gic_data_cpu_base(&gic_data[gic_nr]);  	if (!dist_base || !cpu_base)  		return; @@ -491,6 +572,11 @@ static int gic_notifier(struct notifier_block *self, unsigned long cmd,	void *v)  	int i;  	for (i = 0; i < MAX_GIC_NR; i++) { +#ifdef CONFIG_GIC_NON_BANKED +		/* Skip over unused GICs */ +		if (!gic_data[i].get_base) +			continue; +#endif  		switch (cmd) {  		case CPU_PM_ENTER:  			gic_cpu_save(i); @@ -563,8 +649,9 @@ const struct irq_domain_ops gic_irq_domain_ops = {  #endif  }; -void __init gic_init(unsigned int gic_nr, int irq_start, -	void __iomem *dist_base, void __iomem *cpu_base) +void __init gic_init_bases(unsigned int gic_nr, int irq_start, +			   void __iomem *dist_base, void __iomem *cpu_base, +			   u32 percpu_offset)  {  	struct gic_chip_data *gic;  	struct irq_domain *domain; @@ -574,15 +661,42 @@ void __init gic_init(unsigned int gic_nr, int irq_start,  	gic = &gic_data[gic_nr];  	domain = &gic->domain; -	gic->dist_base = dist_base; -	gic->cpu_base = cpu_base; +#ifdef CONFIG_GIC_NON_BANKED +	if (percpu_offset) { /* Frankein-GIC without banked registers... */ +		unsigned int cpu; + +		gic->dist_base.percpu_base = alloc_percpu(void __iomem *); +		gic->cpu_base.percpu_base = alloc_percpu(void __iomem *); +		if (WARN_ON(!gic->dist_base.percpu_base || +			    !gic->cpu_base.percpu_base)) { +			free_percpu(gic->dist_base.percpu_base); +			free_percpu(gic->cpu_base.percpu_base); +			return; +		} + +		for_each_possible_cpu(cpu) { +			unsigned long offset = percpu_offset * cpu_logical_map(cpu); +			*per_cpu_ptr(gic->dist_base.percpu_base, cpu) = dist_base + offset; +			*per_cpu_ptr(gic->cpu_base.percpu_base, cpu) = cpu_base + offset; +		} + +		gic_set_base_accessor(gic, gic_get_percpu_base); +	} else +#endif +	{			/* Normal, sane GIC... */ +		WARN(percpu_offset, +		     "GIC_NON_BANKED not enabled, ignoring %08x offset!", +		     percpu_offset); +		gic->dist_base.common_base = dist_base; +		gic->cpu_base.common_base = cpu_base; +		gic_set_base_accessor(gic, gic_get_common_base); +	}  	/*  	 * For primary GICs, skip over SGIs.  	 * For secondary GICs, skip over PPIs, too.  	 */  	if (gic_nr == 0) { -		gic_cpu_base_addr = cpu_base;  		domain->hwirq_base = 16;  		if (irq_start > 0)  			irq_start = (irq_start & ~31) + 16; @@ -593,7 +707,7 @@ void __init gic_init(unsigned int gic_nr, int irq_start,  	 * Find out how many interrupts are supported.  	 * The GIC only supports up to 1020 interrupt sources.  	 */ -	gic_irqs = readl_relaxed(dist_base + GIC_DIST_CTR) & 0x1f; +	gic_irqs = readl_relaxed(gic_data_dist_base(gic) + GIC_DIST_CTR) & 0x1f;  	gic_irqs = (gic_irqs + 1) * 32;  	if (gic_irqs > 1020)  		gic_irqs = 1020; @@ -641,7 +755,7 @@ void gic_raise_softirq(const struct cpumask *mask, unsigned int irq)  	dsb();  	/* this always happens on GIC0 */ -	writel_relaxed(map << 16 | irq, gic_data[0].dist_base + GIC_DIST_SOFTINT); +	writel_relaxed(map << 16 | irq, gic_data_dist_base(&gic_data[0]) + GIC_DIST_SOFTINT);  }  #endif @@ -652,6 +766,7 @@ int __init gic_of_init(struct device_node *node, struct device_node *parent)  {  	void __iomem *cpu_base;  	void __iomem *dist_base; +	u32 percpu_offset;  	int irq;  	struct irq_domain *domain = &gic_data[gic_cnt].domain; @@ -664,9 +779,12 @@ int __init gic_of_init(struct device_node *node, struct device_node *parent)  	cpu_base = of_iomap(node, 1);  	WARN(!cpu_base, "unable to map gic cpu registers\n"); +	if (of_property_read_u32(node, "cpu-offset", &percpu_offset)) +		percpu_offset = 0; +  	domain->of_node = of_node_get(node); -	gic_init(gic_cnt, -1, dist_base, cpu_base); +	gic_init_bases(gic_cnt, -1, dist_base, cpu_base, percpu_offset);  	if (parent) {  		irq = irq_of_parse_and_map(node, 0);  |