From: David Mercado <david.merc...@windriver.com> Adds ability to dynamically enable or diesable a CPU on a running system.
Signed-off-by: David Mercado <david.merc...@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 _______________________________________________ linux-yocto mailing list linux-yocto@yoctoproject.org https://lists.yoctoproject.org/listinfo/linux-yocto