ARM: hi3xxx: add smp support

Enable SMP support on hi3xxx platform

Signed-off-by: Zhangfei Gao <zhangfei.gao@linaro.org>
Tested-by: Zhang Mingjun <zhang.mingjun@linaro.org>
Tested-by: Li Xin <li.xin@linaro.org>
Signed-off-by: Haojian Zhuang <haojian.zhuang@linaro.org>
[khilman: fix checkpatch errors]
Signed-off-by: Kevin Hilman <khilman@linaro.org>
This commit is contained in:
Kevin Hilman 2013-12-17 16:23:49 -08:00
parent 524b7df9fd
commit a9434e96d9
7 changed files with 204 additions and 3 deletions

View File

@ -4,3 +4,29 @@ Hisilicon Platforms Device Tree Bindings
Hi4511 Board
Required root node properties:
- compatible = "hisilicon,hi3620-hi4511";
Hisilicon system controller
Required properties:
- compatible : "hisilicon,sysctrl"
- reg : Register address and size
Optional properties:
- smp-offset : offset in sysctrl for notifying slave cpu booting
cpu 1, reg;
cpu 2, reg + 0x4;
cpu 3, reg + 0x8;
If reg value is not zero, cpun exit wfi and go
- resume-offset : offset in sysctrl for notifying cpu0 when resume
- reboot-offset : offset in sysctrl for system reboot
Example:
/* for Hi3620 */
sysctrl: system-controller@fc802000 {
compatible = "hisilicon,sysctrl";
reg = <0xfc802000 0x1000>;
smp-offset = <0x31c>;
resume-offset = <0x308>;
reboot-offset = <0x4>;
};

View File

@ -39,6 +39,27 @@
reg = <0x0>;
next-level-cache = <&L2>;
};
cpu@1 {
compatible = "arm,cortex-a9";
device_type = "cpu";
reg = <1>;
next-level-cache = <&L2>;
};
cpu@2 {
compatible = "arm,cortex-a9";
device_type = "cpu";
reg = <2>;
next-level-cache = <&L2>;
};
cpu@3 {
compatible = "arm,cortex-a9";
device_type = "cpu";
reg = <3>;
next-level-cache = <&L2>;
};
};
amba {
@ -65,6 +86,17 @@
reg = <0x1000 0x1000>, <0x100 0x100>;
};
sysctrl: system-controller@802000 {
compatible = "hisilicon,sysctrl";
reg = <0x802000 0x1000>;
#address-cells = <1>;
#size-cells = <0>;
smp-offset = <0x31c>;
resume-offset = <0x308>;
reboot-offset = <0x4>;
};
dual_timer0: dual_timer@800000 {
compatible = "arm,sp804", "arm,primecell";
reg = <0x800000 0x1000>;
@ -115,6 +147,12 @@
status = "disabled";
};
timer5: timer@600 {
compatible = "arm,cortex-a9-twd-timer";
reg = <0x600 0x20>;
interrupts = <1 13 0xf01>;
};
uart0: uart@b00000 {
compatible = "arm,pl011", "arm,primecell";
reg = <0xb00000 0x1000>;

View File

@ -7,7 +7,11 @@ config ARCH_HI3xxx
select CACHE_L2X0
select CLKSRC_OF
select GENERIC_CLOCKEVENTS
select HAVE_ARM_SCU
select HAVE_ARM_TWD
select HAVE_SMP
select PINCTRL
select PINCTRL_SINGLE
select SMP
help
Support for Hisilicon Hi36xx/Hi37xx processor family

View File

@ -3,3 +3,4 @@
#
obj-y += hi3xxx.o
obj-$(CONFIG_SMP) += platsmp.o

View File

@ -0,0 +1,11 @@
#ifndef __HISILICON_CORE_H
#define __HISILICON_CORE_H
#include <linux/reboot.h>
extern void hi3xxx_set_cpu_jump(int cpu, void *jump_addr);
extern int hi3xxx_get_cpu_jump(int cpu);
extern void secondary_startup(void);
extern struct smp_operations hi3xxx_smp_ops;
#endif

View File

@ -1,4 +1,4 @@
5/*
/*
* (Hisilicon's Hi36xx/Hi37xx SoC based) flattened device tree enabled machine
*
* Copyright (c) 2012-2013 Hisilicon Ltd.
@ -14,11 +14,19 @@
#include <linux/clk-provider.h>
#include <linux/clocksource.h>
#include <linux/irqchip.h>
#include <linux/of_address.h>
#include <linux/of_platform.h>
#include <asm/proc-fns.h>
#include <asm/mach/arch.h>
#include <asm/mach/map.h>
#include "core.h"
#define HI3620_SYSCTRL_PHYS_BASE 0xfc802000
#define HI3620_SYSCTRL_VIRT_BASE 0xfe802000
/*
* This table is only for optimization. Since ioremap() could always share
* the same mapping if it's defined as static IO mapping.
@ -29,8 +37,9 @@
*/
static struct map_desc hi3620_io_desc[] __initdata = {
{
.pfn = __phys_to_pfn(0xfc802000),
.virtual = 0xfe802000,
/* sysctrl */
.pfn = __phys_to_pfn(HI3620_SYSCTRL_PHYS_BASE),
.virtual = HI3620_SYSCTRL_VIRT_BASE,
.length = 0x1000,
.type = MT_DEVICE,
},
@ -48,6 +57,32 @@ static void __init hi3xxx_timer_init(void)
clocksource_of_init();
}
static void hi3xxx_restart(enum reboot_mode mode, const char *cmd)
{
struct device_node *np;
void __iomem *base;
int offset;
np = of_find_compatible_node(NULL, NULL, "hisilicon,sysctrl");
if (!np) {
pr_err("failed to find hisilicon,sysctrl node\n");
return;
}
base = of_iomap(np, 0);
if (!base) {
pr_err("failed to map address in hisilicon,sysctrl node\n");
return;
}
if (of_property_read_u32(np, "reboot-offset", &offset) < 0) {
pr_err("failed to find reboot-offset property\n");
return;
}
writel_relaxed(0xdeadbeef, base + offset);
while (1)
cpu_do_idle();
}
static const char *hi3xxx_compat[] __initconst = {
"hisilicon,hi3620-hi4511",
NULL,
@ -57,4 +92,6 @@ DT_MACHINE_START(HI3620, "Hisilicon Hi3620 (Flattened Device Tree)")
.map_io = hi3620_map_io,
.init_time = hi3xxx_timer_init,
.dt_compat = hi3xxx_compat,
.smp = smp_ops(hi3xxx_smp_ops),
.restart = hi3xxx_restart,
MACHINE_END

View File

@ -0,0 +1,84 @@
/*
* Copyright (c) 2013 Linaro Ltd.
* Copyright (c) 2013 Hisilicon Limited.
* Based on arch/arm/mach-vexpress/platsmp.c, Copyright (C) 2002 ARM Ltd.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*/
#include <linux/smp.h>
#include <linux/io.h>
#include <linux/of_address.h>
#include <asm/cacheflush.h>
#include <asm/smp_plat.h>
#include <asm/smp_scu.h>
#include "core.h"
static void __iomem *ctrl_base;
void hi3xxx_set_cpu_jump(int cpu, void *jump_addr)
{
cpu = cpu_logical_map(cpu);
if (!cpu || !ctrl_base)
return;
writel_relaxed(virt_to_phys(jump_addr), ctrl_base + ((cpu - 1) << 2));
}
int hi3xxx_get_cpu_jump(int cpu)
{
cpu = cpu_logical_map(cpu);
if (!cpu || !ctrl_base)
return 0;
return readl_relaxed(ctrl_base + ((cpu - 1) << 2));
}
static void __init hi3xxx_smp_prepare_cpus(unsigned int max_cpus)
{
struct device_node *np = NULL;
unsigned long base = 0;
u32 offset = 0;
void __iomem *scu_base = NULL;
if (scu_a9_has_base()) {
base = scu_a9_get_base();
scu_base = ioremap(base, SZ_4K);
if (!scu_base) {
pr_err("ioremap(scu_base) failed\n");
return;
}
scu_enable(scu_base);
iounmap(scu_base);
}
if (!ctrl_base) {
np = of_find_compatible_node(NULL, NULL, "hisilicon,sysctrl");
if (!np) {
pr_err("failed to find hisilicon,sysctrl node\n");
return;
}
ctrl_base = of_iomap(np, 0);
if (!ctrl_base) {
pr_err("failed to map address\n");
return;
}
if (of_property_read_u32(np, "smp-offset", &offset) < 0) {
pr_err("failed to find smp-offset property\n");
return;
}
ctrl_base += offset;
}
}
static int hi3xxx_boot_secondary(unsigned int cpu, struct task_struct *idle)
{
hi3xxx_set_cpu_jump(cpu, secondary_startup);
arch_send_wakeup_ipi_mask(cpumask_of(cpu));
return 0;
}
struct smp_operations hi3xxx_smp_ops __initdata = {
.smp_prepare_cpus = hi3xxx_smp_prepare_cpus,
.smp_boot_secondary = hi3xxx_boot_secondary,
};