[linux-yocto] [PATCH 03/89] LSI axm55xx: Add CPU Hotplug support
Paul Butler
butler.paul at gmail.com
Sun Oct 27 12:32:28 PDT 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.3.4
More information about the linux-yocto
mailing list