[PATCH] CNS3xxx: add SMP support
Russell King - ARM Linux
linux at arm.linux.org.uk
Mon Jul 18 16:09:28 EDT 2011
On Mon, Jul 18, 2011 at 09:55:01PM +0200, Imre Kaloz wrote:
> diff --git a/arch/arm/mach-cns3xxx/Kconfig b/arch/arm/mach-cns3xxx/Kconfig
> index 29b13f2..a13f401 100644
> --- a/arch/arm/mach-cns3xxx/Kconfig
> +++ b/arch/arm/mach-cns3xxx/Kconfig
> @@ -3,6 +3,7 @@ menu "CNS3XXX platform type"
>
> config MACH_CNS3420VB
> bool "Support for CNS3420 Validation Board"
> + select HAVE_ARM_SCU if SMP
This is not required:
config SMP
select HAVE_ARM_SCU if !ARCH_MSM_SCORPIONMP
So you get the ARM SCU if you enable SMP anyway (if you're not scorpionmp).
> diff --git a/arch/arm/mach-cns3xxx/headsmp.S b/arch/arm/mach-cns3xxx/headsmp.S
> new file mode 100644
> index 0000000..456fd67
> --- /dev/null
> +++ b/arch/arm/mach-cns3xxx/headsmp.S
> @@ -0,0 +1,42 @@
> +/*
> + * linux/arch/arm/mach-cns3xxx/headsmp.S
> + *
> + * Cloned from linux/arch/arm/plat-versatile/headsmp.S
> + *
> + * Copyright (c) 2003 ARM Limited
> + * All Rights Reserved
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License version 2 as
> + * published by the Free Software Foundation.
> + */
> +#include <linux/linkage.h>
> +#include <linux/init.h>
> +
> + __INIT
This should (in all likely) be __CPUINIT so that secondary CPUs can
re-enter this on hot-plug, or if you boot with max_cpus= lower than
the number of CPUs you have and you later hotplug them in.
> diff --git a/arch/arm/mach-cns3xxx/hotplug.c b/arch/arm/mach-cns3xxx/hotplug.c
> new file mode 100644
> index 0000000..be0d499
> --- /dev/null
> +++ b/arch/arm/mach-cns3xxx/hotplug.c
> @@ -0,0 +1,130 @@
> +/* linux arch/arm/mach-cns3xxx/hotplug.c
> + *
> + * Cloned from linux/arch/arm/mach-realview/hotplug.c
> + *
> + * Copyright (C) 2002 ARM Ltd.
> + * All Rights Reserved
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License version 2 as
> + * published by the Free Software Foundation.
> +*/
> +
> +#include <linux/kernel.h>
> +#include <linux/errno.h>
> +#include <linux/smp.h>
> +
> +#include <asm/cacheflush.h>
> +
> +extern volatile int pen_release;
> +
> +static inline void cpu_enter_lowpower(void)
> +{
> + unsigned int v;
> +
> + 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");
> +}
> +
> +static inline void cpu_leave_lowpower(void)
> +{
> + unsigned int v;
> +
> + asm volatile(
> + "mrc p15, 0, %0, c1, c0, 0\n"
> + " orr %0, %0, %1\n"
> + " mcr p15, 0, %0, c1, c0, 0\n"
> + " mrc p15, 0, %0, c1, c0, 1\n"
> + " orr %0, %0, %2\n"
> + " mcr p15, 0, %0, c1, c0, 1\n"
> + : "=&r" (v)
> + : "Ir" (CR_C), "Ir" (0x40)
> + : "cc");
> +}
> +
> +static inline void platform_do_lowpower(unsigned int cpu, int *spurious)
> +{
> + /*
> + * 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
> + * code will have already disabled interrupts
> + */
> + for (;;) {
> + /*
> + * here's the WFI
> + */
> + asm(".word 0xe320f003\n"
> + :
> + :
> + : "memory", "cc");
> +
> + if (pen_release == cpu) {
> + /*
> + * OK, proper wakeup, we're done
> + */
> + break;
> + }
> +
> + /*
> + * Getting here, means that we have come out of WFI without
> + * having been woken up - this shouldn't happen
> + *
> + * Just note it happening - when we're woken, we can report
> + * its occurrence.
> + */
> + (*spurious)++;
> + }
> +}
> +
> +int platform_cpu_kill(unsigned int cpu)
> +{
> + return 1;
> +}
> +
> +/*
> + * platform-specific code to shutdown a CPU
> + *
> + * Called with IRQs disabled
> + */
> +void platform_cpu_die(unsigned int cpu)
> +{
> + int spurious = 0;
> +
> + /*
> + * we're ready for shutdown now, so do it
> + */
> + cpu_enter_lowpower();
> + platform_do_lowpower(cpu, &spurious);
> +
> + /*
> + * bring this CPU back into the world of cache
> + * coherency, and then restore interrupts
> + */
> + cpu_leave_lowpower();
> +
> + if (spurious)
> + pr_warn("CPU%u: %u spurious wakeup calls\n", cpu, spurious);
> +}
> +
> +int platform_cpu_disable(unsigned int cpu)
> +{
> + /*
> + * we don't allow CPU 0 to be shutdown (it is still too special
> + * e.g. clock tick interrupts)
> + */
> + return cpu == 0 ? -EPERM : 0;
> +}
Have you thought about whether that's the best method? Can you put a
secondary CPU back into reset or power down? If so, it may be a good
idea to do that, rather than just copying the Realview code.
> +static void __iomem *scu_base_addr(void)
> +{
> + return (void __iomem *)(CNS3XXX_TC11MP_SCU_BASE_VIRT);
> +}
If it's at a fixed address then don't do this. Use a macro directly.
Even better - make sure your macros like CNS3XXX_TC11MP_SCU_BASE_VIRT
are correctly typed in the first place and avoid littering code with
casts.
> + /*
> + * Send the secondary CPU a soft interrupt, thereby causing
> + * the boot monitor to read the system wide flags register,
> + * and branch to the address found there.
> + */
> + gic_raise_softirq(cpumask_of(cpu), 1);
Will IPI0 do? If so, please use IPI0.
> +void __init platform_smp_prepare_cpus(unsigned int max_cpus)
> +{
> + int i;
> +
> + /*
> + * Initialise the present map, which describes the set of CPUs
> + * actually populated at the present time.
> + */
> + for (i = 0; i < max_cpus; i++)
> + set_cpu_present(i, true);
> +
> + scu_enable(scu_base_addr());
> +
> + /*
> + * Write the address of secondary startup into the
> + * system-wide flags register. The boot monitor waits
> + * until it receives a soft interrupt, and then the
> + * secondary CPU branches to this address.
> + */
> + __raw_writel(virt_to_phys(cns3xxx_secondary_startup),
> + (void __iomem *)(CNS3XXX_MISC_BASE_VIRT + 0x0600));
Best move this into your boot_secondary() function so that hotplug works
after boot.
> +}
The above will change at the next merge window - if your CPUs are 0..N
then there'll be no need to touch the cpu present array. So the only
thing left should be the call to scu_enable() here.
More information about the linux-arm-kernel
mailing list