ARM: oxnas: Add OX820 SMP support

The Oxford Semiconductor OX820 is a ARM11MPcore based SoC sharing some
features with the OX810 earlier SoC.
This patch adds the core to wake up the second core.

Clarifications about Copyrights dates :
- hotplug.c was taken from an old versatile code by Ma Haijun and left verbatim
- headsmp.S was taken from an old versatile code and adapted by Ma Haijun
- platsmp.c is a mix from versatile code, Ma Haijun code and my code for DT
Hence the 2002/2003 ARM Coryrights and 2013 Ma Haijun Copyrights.

Reviewed-by: Arnd Bergmann <arnd@arndb.de>
Signed-off-by: Neil Armstrong <narmstrong@baylibre.com>
This commit is contained in:
Neil Armstrong 2016-09-09 12:23:11 +02:00
parent 1001354ca3
commit af76e806b5
4 changed files with 239 additions and 0 deletions

View file

@ -0,0 +1,2 @@
obj-$(CONFIG_SMP) += platsmp.o headsmp.o
obj-$(CONFIG_HOTPLUG_CPU) += hotplug.o

View file

@ -0,0 +1,26 @@
/*
* Copyright (C) 2013 Ma Haijun <mahaijuns@gmail.com>
* 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
/*
* OX820 specific entry point for secondary CPUs.
*/
ENTRY(ox820_secondary_startup)
mov r4, #0
/* invalidate both caches and branch target cache */
mcr p15, 0, r4, c7, c7, 0
/*
* we've been released from the holding pen: secondary_stack
* should now contain the SVC stack for this core
*/
b secondary_startup

View file

@ -0,0 +1,109 @@
/*
* 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/cp15.h>
#include <asm/smp_plat.h>
static inline void cpu_enter_lowpower(void)
{
unsigned int v;
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, #0x20\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)
: "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, #0x20\n"
" mcr p15, 0, %0, c1, c0, 1\n"
: "=&r" (v)
: "Ir" (CR_C)
: "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_logical_map(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)++;
}
}
/*
* platform-specific code to shutdown a CPU
*
* Called with IRQs disabled
*/
void ox820_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);
}

View file

@ -0,0 +1,102 @@
/*
* Copyright (C) 2016 Neil Armstrong <narmstrong@baylibre.com>
* Copyright (C) 2013 Ma Haijun <mahaijuns@gmail.com>
* 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/io.h>
#include <linux/delay.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <asm/cacheflush.h>
#include <asm/cp15.h>
#include <asm/smp_plat.h>
#include <asm/smp_scu.h>
extern void ox820_secondary_startup(void);
extern void ox820_cpu_die(unsigned int cpu);
static void __iomem *cpu_ctrl;
static void __iomem *gic_cpu_ctrl;
#define HOLDINGPEN_CPU_OFFSET 0xc8
#define HOLDINGPEN_LOCATION_OFFSET 0xc4
#define GIC_NCPU_OFFSET(cpu) (0x100 + (cpu)*0x100)
#define GIC_CPU_CTRL 0x00
#define GIC_CPU_CTRL_ENABLE 1
int __init ox820_boot_secondary(unsigned int cpu, struct task_struct *idle)
{
/*
* Write the address of secondary startup into the
* system-wide flags register. The BootMonitor waits
* until it receives a soft interrupt, and then the
* secondary CPU branches to this address.
*/
writel(virt_to_phys(ox820_secondary_startup),
cpu_ctrl + HOLDINGPEN_LOCATION_OFFSET);
writel(cpu, cpu_ctrl + HOLDINGPEN_CPU_OFFSET);
/*
* Enable GIC cpu interface in CPU Interface Control Register
*/
writel(GIC_CPU_CTRL_ENABLE,
gic_cpu_ctrl + GIC_NCPU_OFFSET(cpu) + GIC_CPU_CTRL);
/*
* 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.
*/
arch_send_wakeup_ipi_mask(cpumask_of(cpu));
return 0;
}
static void __init ox820_smp_prepare_cpus(unsigned int max_cpus)
{
struct device_node *np;
void __iomem *scu_base;
np = of_find_compatible_node(NULL, NULL, "arm,arm11mp-scu");
scu_base = of_iomap(np, 0);
of_node_put(np);
if (!scu_base)
return;
/* Remap CPU Interrupt Interface Registers */
np = of_find_compatible_node(NULL, NULL, "arm,arm11mp-gic");
gic_cpu_ctrl = of_iomap(np, 1);
of_node_put(np);
if (!gic_cpu_ctrl)
goto unmap_scu;
np = of_find_compatible_node(NULL, NULL, "oxsemi,ox820-sys-ctrl");
cpu_ctrl = of_iomap(np, 0);
of_node_put(np);
if (!cpu_ctrl)
goto unmap_scu;
scu_enable(scu_base);
flush_cache_all();
unmap_scu:
iounmap(scu_base);
}
static const struct smp_operations ox820_smp_ops __initconst = {
.smp_prepare_cpus = ox820_smp_prepare_cpus,
.smp_boot_secondary = ox820_boot_secondary,
#ifdef CONFIG_HOTPLUG_CPU
.cpu_die = ox820_cpu_die,
#endif
};
CPU_METHOD_OF_DECLARE(ox820_smp, "oxsemi,ox820-smp", &ox820_smp_ops);