[linux-yocto] [PATCH 03/78] LSI axm55xx: Add CPU Hotplug support

Paul Butler butler.paul at gmail.com
Tue Nov 19 20:22:39 PST 2013


From: David Mercado <david.mercado at windriver.com>

Adds ability to dynamically enable or diesable a CPU on a running system.

Signed-off-by: David Mercado <david.mercado at windriver.com>
---
 arch/arm/mach-axxia/axxia-gic.c | 73 ++++++++++++++++++++++-------------------
 arch/arm/mach-axxia/hotplug.c   | 52 ++++++++++++++++++++---------
 arch/arm/mach-axxia/platsmp.c   |  5 +++
 3 files changed, 80 insertions(+), 50 deletions(-)

diff --git a/arch/arm/mach-axxia/axxia-gic.c b/arch/arm/mach-axxia/axxia-gic.c
index 20d04ea..2c2dfba 100644
--- a/arch/arm/mach-axxia/axxia-gic.c
+++ b/arch/arm/mach-axxia/axxia-gic.c
@@ -155,8 +155,12 @@ static void gic_mask_irq(struct irq_data *d)
 	if (irqid >= 1020)
 		return;
 
+	/* Don't mess with the AXM IPIs. */
+	if ((irqid >= IPI0_CPU0) && (irqid < MAX_AXM_IPI_NUM))
+		return;
+
 	/* Deal with PPI interrupts directly. */
-	if (irqid > 16 && irqid < 32) {
+	if ((irqid > 16) && (irqid < 32)) {
 		_gic_mask_irq(d);
 		return;
 	}
@@ -203,8 +207,12 @@ static void gic_unmask_irq(struct irq_data *d)
 	if (irqid >= 1020)
 		return;
 
+	/* Don't mess with the AXM IPIs. */
+	if ((irqid >= IPI0_CPU0) && (irqid < MAX_AXM_IPI_NUM))
+		return;
+
 	/* Deal with PPI interrupts directly. */
-	if (irqid > 15 && irqid < 32) {
+	if ((irqid > 15) && (irqid < 32)) {
 		_gic_unmask_irq(d);
 		return;
 	}
@@ -253,18 +261,6 @@ static int _gic_set_type(struct irq_data *d, unsigned int type)
 	bool enabled = false;
 	u32 val;
 
-	/* Interrupt configuration for SGIs can't be changed. */
-	if (gicirq < 16)
-		return -EINVAL;
-
-	/* Interrupt configuration for the AXM IPIs can't be changed. */
-	if ((gicirq >= IPI0_CPU0) && (gicirq < MAX_AXM_IPI_NUM))
-		return -EINVAL;
-
-	/* We only support two interrupt trigger types. */
-	if (type != IRQ_TYPE_LEVEL_HIGH && type != IRQ_TYPE_EDGE_RISING)
-		return -EINVAL;
-
 	raw_spin_lock(&irq_controller_lock);
 
 	val = readl_relaxed(base + GIC_DIST_CONFIG + confoff);
@@ -315,9 +311,22 @@ static int gic_set_type(struct irq_data *d, unsigned int type)
 {
 #ifdef CONFIG_SMP
 	int i, nr_cluster_ids = ((nr_cpu_ids-1) / 4) + 1;
+	unsigned int gicirq = gic_irq(d);
 	u32 pcpu = cpu_logical_map(smp_processor_id());
 	struct gic_set_type_wrapper_struct data;
 
+	/* Interrupt configuration for SGIs can't be changed. */
+	if (gicirq < 16)
+		return -EINVAL;
+
+	/* Interrupt configuration for the AXM IPIs can't be changed. */
+	if ((gicirq >= IPI0_CPU0) && (gicirq < MAX_AXM_IPI_NUM))
+		return -EINVAL;
+
+	/* We only support two interrupt trigger types. */
+	if (type != IRQ_TYPE_LEVEL_HIGH && type != IRQ_TYPE_EDGE_RISING)
+		return -EINVAL;
+
 	/*
 	 * Duplicate IRQ type settings across all clusters. Run
 	 * directly for this cluster, use IPI for all others.
@@ -410,15 +419,26 @@ static int gic_set_affinity(struct irq_data *d,
 	if (irqid >= 1020)
 		return -EINVAL;
 
-	data.d = d;
-	data.mask_val = mask_val;
-	data.disable = false;
+	/* Interrupt affinity for the AXM IPIs can't be changed. */
+	if ((irqid >= IPI0_CPU0) && (irqid < MAX_AXM_IPI_NUM))
+		return IRQ_SET_MASK_OK;
+
+	/*
+	 * If the new IRQ affinity is the same as current, then
+	 * there's no need to update anything.
+	 */
+	if (cpu == irq_cpuid[irqid])
+		return IRQ_SET_MASK_OK;
 
 	/*
 	 * If the new physical cpu assignment falls within the same
 	 * cluster as the cpu we're currently running on, set the IRQ
 	 * affinity directly. Otherwise, use the IPI mechanism.
 	 */
+	data.d = d;
+	data.mask_val = mask_val;
+	data.disable = false;
+
 	if ((cpu_logical_map(cpu) / 4) == (pcpu / 4)) {
 		_gic_set_affinity(&data);
 	} else {
@@ -475,7 +495,6 @@ asmlinkage void __exception_irq_entry axxia_gic_handle_irq(struct pt_regs *regs)
 {
 	u32 irqstat, irqnr;
 	u32 ipinum = 0;
-	u32 mask, offset;
 	struct gic_chip_data *gic = &gic_data;
 	void __iomem *cpu_base = gic_data_cpu_base(gic);
 
@@ -540,7 +559,7 @@ asmlinkage void __exception_irq_entry axxia_gic_handle_irq(struct pt_regs *regs)
 				break;
 			}
 
-			if (ipinum) {
+			if (ipinum > 1) { /* Ignore IPI_WAKEUP (1) */
 				/*
 				 * Write the original irq number to the
 				 * EOI register to acknowledge the IRQ.
@@ -548,18 +567,6 @@ asmlinkage void __exception_irq_entry axxia_gic_handle_irq(struct pt_regs *regs)
 				 * is really a SPI interrupt, not a SGI.
 				 */
 				writel_relaxed(irqnr, cpu_base + GIC_CPU_EOI);
-
-				/*
-				 * Unlike the GIC softirqs, the Axxia IPI
-				 * interrupts do not remain enabled after
-				 * firing. Re-enable the interrupt here.
-				 */
-				mask = 1 << (irqnr % 32);
-				offset = 4 * (irqnr / 32);
-				writel_relaxed(mask,
-					gic_data_dist_base(&gic_data)
-					+ GIC_DIST_ENABLE_SET + offset);
-
 #ifdef CONFIG_SMP
 				/* Do the normal IPI handling. */
 				handle_IPI(ipinum, regs);
@@ -668,8 +675,7 @@ static void __cpuinit gic_dist_init(struct gic_chip_data *gic)
 	 */
 	for (i = IPI0_CPU0; i < MAX_AXM_IPI_NUM; i++) {
 		cpumask_8 = 1 << ((i - IPI0_CPU0) % 4);
-		writeb_relaxed(cpumask_8,
-			base + GIC_DIST_TARGET + (4 * (i / 4)) + i % 4);
+		writeb_relaxed(cpumask_8, base + GIC_DIST_TARGET + i);
 	}
 
 	/*
@@ -849,7 +855,6 @@ static int _gic_notifier(struct notifier_block *self,
 			 unsigned long cmd, void *v)
 {
 	int i;
-
 	switch (cmd) {
 	case CPU_PM_ENTER:
 		gic_cpu_save();
diff --git a/arch/arm/mach-axxia/hotplug.c b/arch/arm/mach-axxia/hotplug.c
index 9ecd64d..d919bff 100644
--- a/arch/arm/mach-axxia/hotplug.c
+++ b/arch/arm/mach-axxia/hotplug.c
@@ -18,26 +18,33 @@
 
 extern volatile int pen_release;
 
-static inline void cpu_enter_lowpower(void)
+static inline void cpu_enter_lowpower_a15(void)
 {
 	unsigned int v;
 
+	asm volatile(
+	"       mrc     p15, 0, %0, c1, c0, 0\n"
+	"       bic     %0, %0, %1\n"
+	"       mcr     p15, 0, %0, c1, c0, 0\n"
+	: "=&r" (v)
+	: "Ir" (CR_C)
+	: "cc");
+
 	flush_cache_all();
+
 	asm volatile(
-		"mcr	p15, 0, %1, c7, c5, 0\n"
-	"	mcr	p15, 0, %1, c7, c10, 4\n"
 	/*
-	 * Turn off coherency
-	 */
-	"	mrc	p15, 0, %0, c1, c0, 1\n"
-	"	bic	%0, %0, %3\n"
-	"	mcr	p15, 0, %0, c1, c0, 1\n"
-	"	mrc	p15, 0, %0, c1, c0, 0\n"
-	"	bic	%0, %0, %2\n"
-	"	mcr	p15, 0, %0, c1, c0, 0\n"
-	  : "=&r" (v)
-	  : "r" (0), "Ir" (CR_C), "Ir" (0x40)
-	  : "cc");
+	* Turn off coherency
+	*/
+	"       mrc     p15, 0, %0, c1, c0, 1\n"
+	"       bic     %0, %0, %1\n"
+	"       mcr     p15, 0, %0, c1, c0, 1\n"
+	: "=&r" (v)
+	: "Ir" (0x40)
+	: "cc");
+
+	isb();
+	dsb();
 }
 
 static inline void cpu_leave_lowpower(void)
@@ -58,6 +65,8 @@ static inline void cpu_leave_lowpower(void)
 
 static void __ref platform_do_lowpower(unsigned int cpu, int *spurious)
 {
+	int phys_cpu, cluster;
+
 	/*
 	 * there is no power-control hardware on this platform, so all
 	 * we can do is put the core into WFI; this is safe as the calling
@@ -66,7 +75,18 @@ static void __ref platform_do_lowpower(unsigned int cpu, int *spurious)
 	for (;;) {
 		wfi();
 
-		if (pen_release == cpu_logical_map(cpu)) {
+		/*
+		 * Convert the "cpu" variable to be compatible with the
+		 * ARM MPIDR register format (CLUSTERID and CPUID):
+		 *
+		 * Bits:   |11 10 9 8|7 6 5 4 3 2|1 0
+		 *         | CLUSTER | Reserved  |CPU
+		 */
+		phys_cpu = cpu_logical_map(cpu);
+		cluster = (phys_cpu / 4) << 8;
+		phys_cpu = cluster + (phys_cpu % 4);
+
+		if (pen_release == phys_cpu) {
 			/*
 			 * OK, proper wakeup, we're done
 			 */
@@ -101,7 +121,7 @@ void platform_cpu_die(unsigned int cpu)
 	/*
 	 * we're ready for shutdown now, so do it
 	 */
-	cpu_enter_lowpower();
+	cpu_enter_lowpower_a15();
 	platform_do_lowpower(cpu, &spurious);
 
 	/*
diff --git a/arch/arm/mach-axxia/platsmp.c b/arch/arm/mach-axxia/platsmp.c
index 3b202b9..4ba2e7a 100644
--- a/arch/arm/mach-axxia/platsmp.c
+++ b/arch/arm/mach-axxia/platsmp.c
@@ -107,6 +107,11 @@ int __cpuinit boot_secondary(unsigned int cpu, struct task_struct *idle)
 	/* Release the specified core */
 	write_pen_release(phys_cpu);
 
+#ifdef CONFIG_HOTPLUG_CPU
+	/* Send a wakeup IPI to get the idled cpu out of WFI state */
+	axxia_gic_raise_softirq(cpumask_of(cpu), 1);
+#endif
+
 	/* Wait for so long, then give up if nothing happens ... */
 	timeout = jiffies + (1 * HZ);
 	while (time_before(jiffies, timeout)) {
-- 
1.8.4.3



More information about the linux-yocto mailing list