diff options
Diffstat (limited to 'drivers/irqchip/irq-gic.c')
| -rw-r--r-- | drivers/irqchip/irq-gic.c | 274 | 
1 files changed, 246 insertions, 28 deletions
diff --git a/drivers/irqchip/irq-gic.c b/drivers/irqchip/irq-gic.c index d0e948084ea..7c131cf7cc1 100644 --- a/drivers/irqchip/irq-gic.c +++ b/drivers/irqchip/irq-gic.c @@ -42,6 +42,7 @@  #include <linux/irqchip/chained_irq.h>  #include <linux/irqchip/arm-gic.h> +#include <asm/cputype.h>  #include <asm/irq.h>  #include <asm/exception.h>  #include <asm/smp_plat.h> @@ -50,7 +51,7 @@  union gic_base {  	void __iomem *common_base; -	void __percpu __iomem **percpu_base; +	void __percpu * __iomem *percpu_base;  };  struct gic_chip_data { @@ -246,17 +247,20 @@ static int gic_set_affinity(struct irq_data *d, const struct cpumask *mask_val,  			    bool force)  {  	void __iomem *reg = gic_dist_base(d) + GIC_DIST_TARGET + (gic_irq(d) & ~3); -	unsigned int shift = (gic_irq(d) % 4) * 8; -	unsigned int cpu = cpumask_any_and(mask_val, cpu_online_mask); +	unsigned int cpu, shift = (gic_irq(d) % 4) * 8;  	u32 val, mask, bit; +	if (!force) +		cpu = cpumask_any_and(mask_val, cpu_online_mask); +	else +		cpu = cpumask_first(mask_val); +  	if (cpu >= NR_GIC_CPU_IF || cpu >= nr_cpu_ids)  		return -EINVAL; +	raw_spin_lock(&irq_controller_lock);  	mask = 0xff << shift;  	bit = gic_cpu_map[cpu] << shift; - -	raw_spin_lock(&irq_controller_lock);  	val = readl_relaxed(reg) & ~mask;  	writel_relaxed(val | bit, reg);  	raw_spin_unlock(&irq_controller_lock); @@ -280,7 +284,7 @@ static int gic_set_wake(struct irq_data *d, unsigned int on)  #define gic_set_wake	NULL  #endif -static asmlinkage void __exception_irq_entry gic_handle_irq(struct pt_regs *regs) +static void __exception_irq_entry gic_handle_irq(struct pt_regs *regs)  {  	u32 irqstat, irqnr;  	struct gic_chip_data *gic = &gic_data[0]; @@ -288,7 +292,7 @@ static asmlinkage void __exception_irq_entry gic_handle_irq(struct pt_regs *regs  	do {  		irqstat = readl_relaxed(cpu_base + GIC_CPU_INTACK); -		irqnr = irqstat & ~0x1c00; +		irqnr = irqstat & GICC_IAR_INT_ID_MASK;  		if (likely(irqnr > 15 && irqnr < 1021)) {  			irqnr = irq_find_mapping(gic->domain, irqnr); @@ -649,10 +653,12 @@ static void __init gic_pm_init(struct gic_chip_data *gic)  #endif  #ifdef CONFIG_SMP -void gic_raise_softirq(const struct cpumask *mask, unsigned int irq) +static void gic_raise_softirq(const struct cpumask *mask, unsigned int irq)  {  	int cpu; -	unsigned long map = 0; +	unsigned long flags, map = 0; + +	raw_spin_lock_irqsave(&irq_controller_lock, flags);  	/* Convert our logical CPU mask into a physical one. */  	for_each_cpu(cpu, mask) @@ -660,13 +666,155 @@ void gic_raise_softirq(const struct cpumask *mask, unsigned int irq)  	/*  	 * Ensure that stores to Normal memory are visible to the -	 * other CPUs before issuing the IPI. +	 * other CPUs before they observe us issuing the IPI.  	 */ -	dsb(); +	dmb(ishst);  	/* this always happens on GIC0 */  	writel_relaxed(map << 16 | irq, gic_data_dist_base(&gic_data[0]) + GIC_DIST_SOFTINT); + +	raw_spin_unlock_irqrestore(&irq_controller_lock, flags); +} +#endif + +#ifdef CONFIG_BL_SWITCHER +/* + * gic_send_sgi - send a SGI directly to given CPU interface number + * + * cpu_id: the ID for the destination CPU interface + * irq: the IPI number to send a SGI for + */ +void gic_send_sgi(unsigned int cpu_id, unsigned int irq) +{ +	BUG_ON(cpu_id >= NR_GIC_CPU_IF); +	cpu_id = 1 << cpu_id; +	/* this always happens on GIC0 */ +	writel_relaxed((cpu_id << 16) | irq, gic_data_dist_base(&gic_data[0]) + GIC_DIST_SOFTINT); +} + +/* + * gic_get_cpu_id - get the CPU interface ID for the specified CPU + * + * @cpu: the logical CPU number to get the GIC ID for. + * + * Return the CPU interface ID for the given logical CPU number, + * or -1 if the CPU number is too large or the interface ID is + * unknown (more than one bit set). + */ +int gic_get_cpu_id(unsigned int cpu) +{ +	unsigned int cpu_bit; + +	if (cpu >= NR_GIC_CPU_IF) +		return -1; +	cpu_bit = gic_cpu_map[cpu]; +	if (cpu_bit & (cpu_bit - 1)) +		return -1; +	return __ffs(cpu_bit); +} + +/* + * gic_migrate_target - migrate IRQs to another CPU interface + * + * @new_cpu_id: the CPU target ID to migrate IRQs to + * + * Migrate all peripheral interrupts with a target matching the current CPU + * to the interface corresponding to @new_cpu_id.  The CPU interface mapping + * is also updated.  Targets to other CPU interfaces are unchanged. + * This must be called with IRQs locally disabled. + */ +void gic_migrate_target(unsigned int new_cpu_id) +{ +	unsigned int cur_cpu_id, gic_irqs, gic_nr = 0; +	void __iomem *dist_base; +	int i, ror_val, cpu = smp_processor_id(); +	u32 val, cur_target_mask, active_mask; + +	if (gic_nr >= MAX_GIC_NR) +		BUG(); + +	dist_base = gic_data_dist_base(&gic_data[gic_nr]); +	if (!dist_base) +		return; +	gic_irqs = gic_data[gic_nr].gic_irqs; + +	cur_cpu_id = __ffs(gic_cpu_map[cpu]); +	cur_target_mask = 0x01010101 << cur_cpu_id; +	ror_val = (cur_cpu_id - new_cpu_id) & 31; + +	raw_spin_lock(&irq_controller_lock); + +	/* Update the target interface for this logical CPU */ +	gic_cpu_map[cpu] = 1 << new_cpu_id; + +	/* +	 * Find all the peripheral interrupts targetting the current +	 * CPU interface and migrate them to the new CPU interface. +	 * We skip DIST_TARGET 0 to 7 as they are read-only. +	 */ +	for (i = 8; i < DIV_ROUND_UP(gic_irqs, 4); i++) { +		val = readl_relaxed(dist_base + GIC_DIST_TARGET + i * 4); +		active_mask = val & cur_target_mask; +		if (active_mask) { +			val &= ~active_mask; +			val |= ror32(active_mask, ror_val); +			writel_relaxed(val, dist_base + GIC_DIST_TARGET + i*4); +		} +	} + +	raw_spin_unlock(&irq_controller_lock); + +	/* +	 * Now let's migrate and clear any potential SGIs that might be +	 * pending for us (cur_cpu_id).  Since GIC_DIST_SGI_PENDING_SET +	 * is a banked register, we can only forward the SGI using +	 * GIC_DIST_SOFTINT.  The original SGI source is lost but Linux +	 * doesn't use that information anyway. +	 * +	 * For the same reason we do not adjust SGI source information +	 * for previously sent SGIs by us to other CPUs either. +	 */ +	for (i = 0; i < 16; i += 4) { +		int j; +		val = readl_relaxed(dist_base + GIC_DIST_SGI_PENDING_SET + i); +		if (!val) +			continue; +		writel_relaxed(val, dist_base + GIC_DIST_SGI_PENDING_CLEAR + i); +		for (j = i; j < i + 4; j++) { +			if (val & 0xff) +				writel_relaxed((1 << (new_cpu_id + 16)) | j, +						dist_base + GIC_DIST_SOFTINT); +			val >>= 8; +		} +	}  } + +/* + * gic_get_sgir_physaddr - get the physical address for the SGI register + * + * REturn the physical address of the SGI register to be used + * by some early assembly code when the kernel is not yet available. + */ +static unsigned long gic_dist_physaddr; + +unsigned long gic_get_sgir_physaddr(void) +{ +	if (!gic_dist_physaddr) +		return 0; +	return gic_dist_physaddr + GIC_DIST_SOFTINT; +} + +void __init gic_init_physaddr(struct device_node *node) +{ +	struct resource res; +	if (of_address_to_resource(node, 0, &res) == 0) { +		gic_dist_physaddr = res.start; +		pr_info("GIC physical location is %#lx\n", gic_dist_physaddr); +	} +} + +#else +#define gic_init_physaddr(node)  do { } while (0)  #endif  static int gic_irq_domain_map(struct irq_domain *d, unsigned int irq, @@ -681,16 +829,25 @@ static int gic_irq_domain_map(struct irq_domain *d, unsigned int irq,  		irq_set_chip_and_handler(irq, &gic_chip,  					 handle_fasteoi_irq);  		set_irq_flags(irq, IRQF_VALID | IRQF_PROBE); + +		gic_routable_irq_domain_ops->map(d, irq, hw);  	}  	irq_set_chip_data(irq, d->host_data);  	return 0;  } +static void gic_irq_domain_unmap(struct irq_domain *d, unsigned int irq) +{ +	gic_routable_irq_domain_ops->unmap(d, irq); +} +  static int gic_irq_domain_xlate(struct irq_domain *d,  				struct device_node *controller,  				const u32 *intspec, unsigned int intsize,  				unsigned long *out_hwirq, unsigned int *out_type)  { +	unsigned long ret = 0; +  	if (d->of_node != controller)  		return -EINVAL;  	if (intsize < 3) @@ -700,11 +857,20 @@ static int gic_irq_domain_xlate(struct irq_domain *d,  	*out_hwirq = intspec[1] + 16;  	/* For SPIs, we need to add 16 more to get the GIC irq ID number */ -	if (!intspec[0]) -		*out_hwirq += 16; +	if (!intspec[0]) { +		ret = gic_routable_irq_domain_ops->xlate(d, controller, +							 intspec, +							 intsize, +							 out_hwirq, +							 out_type); + +		if (IS_ERR_VALUE(ret)) +			return ret; +	}  	*out_type = intspec[2] & IRQ_TYPE_SENSE_MASK; -	return 0; + +	return ret;  }  #ifdef CONFIG_SMP @@ -726,11 +892,43 @@ static struct notifier_block gic_cpu_notifier = {  };  #endif -const struct irq_domain_ops gic_irq_domain_ops = { +static const struct irq_domain_ops gic_irq_domain_ops = {  	.map = gic_irq_domain_map, +	.unmap = gic_irq_domain_unmap,  	.xlate = gic_irq_domain_xlate,  }; +/* Default functions for routable irq domain */ +static int gic_routable_irq_domain_map(struct irq_domain *d, unsigned int irq, +			      irq_hw_number_t hw) +{ +	return 0; +} + +static void gic_routable_irq_domain_unmap(struct irq_domain *d, +					  unsigned int irq) +{ +} + +static int gic_routable_irq_domain_xlate(struct irq_domain *d, +				struct device_node *controller, +				const u32 *intspec, unsigned int intsize, +				unsigned long *out_hwirq, +				unsigned int *out_type) +{ +	*out_hwirq += 16; +	return 0; +} + +const struct irq_domain_ops gic_default_routable_irq_domain_ops = { +	.map = gic_routable_irq_domain_map, +	.unmap = gic_routable_irq_domain_unmap, +	.xlate = gic_routable_irq_domain_xlate, +}; + +const struct irq_domain_ops *gic_routable_irq_domain_ops = +					&gic_default_routable_irq_domain_ops; +  void __init gic_init_bases(unsigned int gic_nr, int irq_start,  			   void __iomem *dist_base, void __iomem *cpu_base,  			   u32 percpu_offset, struct device_node *node) @@ -738,6 +936,7 @@ void __init gic_init_bases(unsigned int gic_nr, int irq_start,  	irq_hw_number_t hwirq_base;  	struct gic_chip_data *gic;  	int gic_irqs, irq_base, i; +	int nr_routable_irqs;  	BUG_ON(gic_nr >= MAX_GIC_NR); @@ -756,7 +955,9 @@ void __init gic_init_bases(unsigned int gic_nr, int irq_start,  		}  		for_each_possible_cpu(cpu) { -			unsigned long offset = percpu_offset * cpu_logical_map(cpu); +			u32 mpidr = cpu_logical_map(cpu); +			u32 core_id = MPIDR_AFFINITY_LEVEL(mpidr, 0); +			unsigned long offset = percpu_offset * core_id;  			*per_cpu_ptr(gic->dist_base.percpu_base, cpu) = dist_base + offset;  			*per_cpu_ptr(gic->cpu_base.percpu_base, cpu) = cpu_base + offset;  		} @@ -803,23 +1004,35 @@ 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 */ -	irq_base = irq_alloc_descs(irq_start, 16, gic_irqs, numa_node_id()); -	if (IS_ERR_VALUE(irq_base)) { -		WARN(1, "Cannot allocate irq_descs @ IRQ%d, assuming pre-allocated\n", -		     irq_start); -		irq_base = irq_start; + +	if (of_property_read_u32(node, "arm,routable-irqs", +				 &nr_routable_irqs)) { +		irq_base = irq_alloc_descs(irq_start, 16, gic_irqs, +					   numa_node_id()); +		if (IS_ERR_VALUE(irq_base)) { +			WARN(1, "Cannot allocate irq_descs @ IRQ%d, assuming pre-allocated\n", +			     irq_start); +			irq_base = irq_start; +		} + +		gic->domain = irq_domain_add_legacy(node, 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_legacy(node, gic_irqs, irq_base, -				    hwirq_base, &gic_irq_domain_ops, gic); +  	if (WARN_ON(!gic->domain))  		return; +	if (gic_nr == 0) {  #ifdef CONFIG_SMP -	set_smp_cross_call(gic_raise_softirq); -	register_cpu_notifier(&gic_cpu_notifier); +		set_smp_cross_call(gic_raise_softirq); +		register_cpu_notifier(&gic_cpu_notifier);  #endif - -	set_handle_irq(gic_handle_irq); +		set_handle_irq(gic_handle_irq); +	}  	gic_chip.flags |= gic_arch_extn.flags;  	gic_dist_init(gic); @@ -830,7 +1043,8 @@ void __init gic_init_bases(unsigned int gic_nr, int irq_start,  #ifdef CONFIG_OF  static int gic_cnt __initdata; -int __init gic_of_init(struct device_node *node, struct device_node *parent) +static int __init +gic_of_init(struct device_node *node, struct device_node *parent)  {  	void __iomem *cpu_base;  	void __iomem *dist_base; @@ -850,6 +1064,8 @@ int __init gic_of_init(struct device_node *node, struct device_node *parent)  		percpu_offset = 0;  	gic_init_bases(gic_cnt, -1, dist_base, cpu_base, percpu_offset, node); +	if (!gic_cnt) +		gic_init_physaddr(node);  	if (parent) {  		irq = irq_of_parse_and_map(node, 0); @@ -858,8 +1074,10 @@ int __init gic_of_init(struct device_node *node, struct device_node *parent)  	gic_cnt++;  	return 0;  } +IRQCHIP_DECLARE(gic_400, "arm,gic-400", gic_of_init);  IRQCHIP_DECLARE(cortex_a15_gic, "arm,cortex-a15-gic", gic_of_init);  IRQCHIP_DECLARE(cortex_a9_gic, "arm,cortex-a9-gic", gic_of_init); +IRQCHIP_DECLARE(cortex_a7_gic, "arm,cortex-a7-gic", gic_of_init);  IRQCHIP_DECLARE(msm_8660_qgic, "qcom,msm-8660-qgic", gic_of_init);  IRQCHIP_DECLARE(msm_qgic2, "qcom,msm-qgic2", gic_of_init);  | 
