reboot-offset = <0x4>;
};
+-----------------------------------------------------------------------
+Hisilicon CPU controller
+
+Required properties:
+- compatible : "hisilicon,cpuctrl"
+- reg : Register address and size
+
+The clock registers and power registers of secondary cores are defined
+in CPU controller, especially in HIX5HD2 SoC.
+
+-----------------------------------------------------------------------
PCTRL: Peripheral misc control register
Required Properties:
menu "Hisilicon platform type"
config ARCH_HI3xxx
- bool "Hisilicon Hi36xx/Hi37xx family" if ARCH_MULTI_V7
+ bool "Hisilicon Hi36xx family" if ARCH_MULTI_V7
select CACHE_L2X0
select HAVE_ARM_SCU if SMP
select HAVE_ARM_TWD if SMP
select PINCTRL
select PINCTRL_SINGLE
help
- Support for Hisilicon Hi36xx/Hi37xx processor family
+ Support for Hisilicon Hi36xx SoC family
+config ARCH_HIX5HD2
+ bool "Hisilicon X5HD2 family" if ARCH_MULTI_V7
+ select CACHE_L2X0
+ select HAVE_ARM_SCU if SMP
+ select HAVE_ARM_TWD if SMP
+ select PINCTRL
+ select PINCTRL_SINGLE
+ help
+ Support for Hisilicon HIX5HD2 SoC family
endmenu
endif
#
obj-y += hisilicon.o
-obj-$(CONFIG_SMP) += platsmp.o hotplug.o
+obj-$(CONFIG_SMP) += platsmp.o hotplug.o headsmp.o
extern int hi3xxx_cpu_kill(unsigned int cpu);
extern void hi3xxx_set_cpu(int cpu, bool enable);
+extern void hix5hd2_secondary_startup(void);
+extern struct smp_operations hix5hd2_smp_ops;
+extern void hix5hd2_set_cpu(int cpu, bool enable);
+extern void hix5hd2_cpu_die(unsigned int cpu);
+
#endif
--- /dev/null
+/*
+ * Copyright (c) 2014 Hisilicon Limited.
+ * Copyright (c) 2014 Linaro Ltd.
+ *
+ * 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>
+
+ __CPUINIT
+
+ENTRY(hix5hd2_secondary_startup)
+ bl v7_invalidate_l1
+ b secondary_startup
.smp = smp_ops(hi3xxx_smp_ops),
.restart = hi3xxx_restart,
MACHINE_END
+
+static const char *hix5hd2_compat[] __initconst = {
+ "hisilicon,hix5hd2",
+ NULL,
+};
+
+DT_MACHINE_START(HIX5HD2_DT, "Hisilicon HIX5HD2 (Flattened Device Tree)")
+ .dt_compat = hix5hd2_compat,
+ .init_late = hi3xxx_init_late,
+ .smp = smp_ops(hix5hd2_smp_ops),
+ .restart = hi3xxx_restart,
+MACHINE_END
#define CPU0_NEON_SRST_REQ_EN (1 << 4)
#define CPU0_SRST_REQ_EN (1 << 0)
+#define HIX5HD2_PERI_CRG20 0x50
+#define CRG20_CPU1_RESET (1 << 17)
+
+#define HIX5HD2_PERI_PMC0 0x1000
+#define PMC0_CPU1_WAIT_MTCOMS_ACK (1 << 8)
+#define PMC0_CPU1_PMC_ENABLE (1 << 7)
+#define PMC0_CPU1_POWERDOWN (1 << 3)
+
enum {
HI3620_CTRL,
ERROR_CTRL,
set_cpu_hi3620(cpu, enable);
}
+static bool hix5hd2_hotplug_init(void)
+{
+ struct device_node *np;
+
+ np = of_find_compatible_node(NULL, NULL, "hisilicon,cpuctrl");
+ if (np) {
+ ctrl_base = of_iomap(np, 0);
+ return true;
+ }
+ return false;
+}
+
+void hix5hd2_set_cpu(int cpu, bool enable)
+{
+ u32 val = 0;
+
+ if (!ctrl_base)
+ if (!hix5hd2_hotplug_init())
+ BUG();
+
+ if (enable) {
+ /* power on cpu1 */
+ val = readl_relaxed(ctrl_base + HIX5HD2_PERI_PMC0);
+ val &= ~(PMC0_CPU1_WAIT_MTCOMS_ACK | PMC0_CPU1_POWERDOWN);
+ val |= PMC0_CPU1_PMC_ENABLE;
+ writel_relaxed(val, ctrl_base + HIX5HD2_PERI_PMC0);
+ /* unreset */
+ val = readl_relaxed(ctrl_base + HIX5HD2_PERI_CRG20);
+ val &= ~CRG20_CPU1_RESET;
+ writel_relaxed(val, ctrl_base + HIX5HD2_PERI_CRG20);
+ } else {
+ /* power down cpu1 */
+ val = readl_relaxed(ctrl_base + HIX5HD2_PERI_PMC0);
+ val |= PMC0_CPU1_PMC_ENABLE | PMC0_CPU1_POWERDOWN;
+ val &= ~PMC0_CPU1_WAIT_MTCOMS_ACK;
+ writel_relaxed(val, ctrl_base + HIX5HD2_PERI_PMC0);
+
+ /* reset */
+ val = readl_relaxed(ctrl_base + HIX5HD2_PERI_CRG20);
+ val |= CRG20_CPU1_RESET;
+ writel_relaxed(val, ctrl_base + HIX5HD2_PERI_CRG20);
+ }
+}
+
static inline void cpu_enter_lowpower(void)
{
unsigned int v;
hi3xxx_set_cpu(cpu, false);
return 1;
}
+
+void hix5hd2_cpu_die(unsigned int cpu)
+{
+ flush_cache_all();
+ hix5hd2_set_cpu(cpu, false);
+}
#endif
#include "core.h"
+#define HIX5HD2_BOOT_ADDRESS 0xffff0000
+
static void __iomem *ctrl_base;
void hi3xxx_set_cpu_jump(int cpu, void *jump_addr)
return readl_relaxed(ctrl_base + ((cpu - 1) << 2));
}
-static void __init hi3xxx_smp_prepare_cpus(unsigned int max_cpus)
+static void __init hisi_enable_scu_a9(void)
{
- struct device_node *np = NULL;
unsigned long base = 0;
- u32 offset = 0;
void __iomem *scu_base = NULL;
if (scu_a9_has_base()) {
scu_enable(scu_base);
iounmap(scu_base);
}
+}
+
+static void __init hi3xxx_smp_prepare_cpus(unsigned int max_cpus)
+{
+ struct device_node *np = NULL;
+ u32 offset = 0;
+
+ hisi_enable_scu_a9();
if (!ctrl_base) {
np = of_find_compatible_node(NULL, NULL, "hisilicon,sysctrl");
if (!np) {
.cpu_kill = hi3xxx_cpu_kill,
#endif
};
+
+static void __init hix5hd2_smp_prepare_cpus(unsigned int max_cpus)
+{
+ hisi_enable_scu_a9();
+}
+
+void hix5hd2_set_scu_boot_addr(phys_addr_t start_addr, phys_addr_t jump_addr)
+{
+ void __iomem *virt;
+
+ virt = ioremap(start_addr, PAGE_SIZE);
+
+ writel_relaxed(0xe51ff004, virt); /* ldr pc, [rc, #-4] */
+ writel_relaxed(jump_addr, virt + 4); /* pc jump phy address */
+ iounmap(virt);
+}
+
+static int hix5hd2_boot_secondary(unsigned int cpu, struct task_struct *idle)
+{
+ phys_addr_t jumpaddr;
+
+ jumpaddr = virt_to_phys(hix5hd2_secondary_startup);
+ hix5hd2_set_scu_boot_addr(HIX5HD2_BOOT_ADDRESS, jumpaddr);
+ hix5hd2_set_cpu(cpu, true);
+ arch_send_wakeup_ipi_mask(cpumask_of(cpu));
+ return 0;
+}
+
+
+struct smp_operations hix5hd2_smp_ops __initdata = {
+ .smp_prepare_cpus = hix5hd2_smp_prepare_cpus,
+ .smp_boot_secondary = hix5hd2_boot_secondary,
+#ifdef CONFIG_HOTPLUG_CPU
+ .cpu_die = hix5hd2_cpu_die,
+#endif
+};