diff options
Diffstat (limited to 'arch/arm/mach-hisi')
| -rw-r--r-- | arch/arm/mach-hisi/Kconfig | 12 | ||||
| -rw-r--r-- | arch/arm/mach-hisi/Makefile | 6 | ||||
| -rw-r--r-- | arch/arm/mach-hisi/core.h | 15 | ||||
| -rw-r--r-- | arch/arm/mach-hisi/hisilicon.c | 90 | ||||
| -rw-r--r-- | arch/arm/mach-hisi/hotplug.c | 202 | ||||
| -rw-r--r-- | arch/arm/mach-hisi/platsmp.c | 89 | 
6 files changed, 414 insertions, 0 deletions
diff --git a/arch/arm/mach-hisi/Kconfig b/arch/arm/mach-hisi/Kconfig new file mode 100644 index 00000000000..feee4dbb076 --- /dev/null +++ b/arch/arm/mach-hisi/Kconfig @@ -0,0 +1,12 @@ +config ARCH_HI3xxx +	bool "Hisilicon Hi36xx/Hi37xx family" if ARCH_MULTI_V7 +	select ARM_AMBA +	select ARM_GIC +	select ARM_TIMER_SP804 +	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 diff --git a/arch/arm/mach-hisi/Makefile b/arch/arm/mach-hisi/Makefile new file mode 100644 index 00000000000..2ae1b59267c --- /dev/null +++ b/arch/arm/mach-hisi/Makefile @@ -0,0 +1,6 @@ +# +# Makefile for Hisilicon processors family +# + +obj-y	+= hisilicon.o +obj-$(CONFIG_SMP)		+= platsmp.o hotplug.o diff --git a/arch/arm/mach-hisi/core.h b/arch/arm/mach-hisi/core.h new file mode 100644 index 00000000000..af23ec20453 --- /dev/null +++ b/arch/arm/mach-hisi/core.h @@ -0,0 +1,15 @@ +#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; + +extern void hi3xxx_cpu_die(unsigned int cpu); +extern int hi3xxx_cpu_kill(unsigned int cpu); +extern void hi3xxx_set_cpu(int cpu, bool enable); + +#endif diff --git a/arch/arm/mach-hisi/hisilicon.c b/arch/arm/mach-hisi/hisilicon.c new file mode 100644 index 00000000000..741faf3e710 --- /dev/null +++ b/arch/arm/mach-hisi/hisilicon.c @@ -0,0 +1,90 @@ +/* + * (Hisilicon's SoC based) flattened device tree enabled machine + * + * Copyright (c) 2012-2013 Hisilicon Ltd. + * Copyright (c) 2012-2013 Linaro Ltd. + * + * Author: Haojian Zhuang <haojian.zhuang@linaro.org> + * + * 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/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. + * + * Without this table, system could also work. The cost is some virtual address + * spaces wasted since ioremap() may be called multi times for the same + * IO space. + */ +static struct map_desc hi3620_io_desc[] __initdata = { +	{ +		/* sysctrl */ +		.pfn		= __phys_to_pfn(HI3620_SYSCTRL_PHYS_BASE), +		.virtual	= HI3620_SYSCTRL_VIRT_BASE, +		.length		= 0x1000, +		.type		= MT_DEVICE, +	}, +}; + +static void __init hi3620_map_io(void) +{ +	debug_ll_io_init(); +	iotable_init(hi3620_io_desc, ARRAY_SIZE(hi3620_io_desc)); +} + +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, +}; + +DT_MACHINE_START(HI3620, "Hisilicon Hi3620 (Flattened Device Tree)") +	.map_io		= hi3620_map_io, +	.dt_compat	= hi3xxx_compat, +	.smp		= smp_ops(hi3xxx_smp_ops), +	.restart	= hi3xxx_restart, +MACHINE_END diff --git a/arch/arm/mach-hisi/hotplug.c b/arch/arm/mach-hisi/hotplug.c new file mode 100644 index 00000000000..abd441b0c60 --- /dev/null +++ b/arch/arm/mach-hisi/hotplug.c @@ -0,0 +1,202 @@ +/* + * Copyright (c) 2013 Linaro Ltd. + * Copyright (c) 2013 Hisilicon Limited. + * + * 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/cpu.h> +#include <linux/delay.h> +#include <linux/io.h> +#include <linux/of_address.h> +#include <linux/of_platform.h> +#include <asm/cacheflush.h> +#include <asm/smp_plat.h> +#include "core.h" + +/* Sysctrl registers in Hi3620 SoC */ +#define SCISOEN				0xc0 +#define SCISODIS			0xc4 +#define SCPERPWREN			0xd0 +#define SCPERPWRDIS			0xd4 +#define SCCPUCOREEN			0xf4 +#define SCCPUCOREDIS			0xf8 +#define SCPERCTRL0			0x200 +#define SCCPURSTEN			0x410 +#define SCCPURSTDIS			0x414 + +/* + * bit definition in SCISOEN/SCPERPWREN/... + * + * CPU2_ISO_CTRL	(1 << 5) + * CPU3_ISO_CTRL	(1 << 6) + * ... + */ +#define CPU2_ISO_CTRL			(1 << 5) + +/* + * bit definition in SCPERCTRL0 + * + * CPU0_WFI_MASK_CFG	(1 << 28) + * CPU1_WFI_MASK_CFG	(1 << 29) + * ... + */ +#define CPU0_WFI_MASK_CFG		(1 << 28) + +/* + * bit definition in SCCPURSTEN/... + * + * CPU0_SRST_REQ_EN	(1 << 0) + * CPU1_SRST_REQ_EN	(1 << 1) + * ... + */ +#define CPU0_HPM_SRST_REQ_EN		(1 << 22) +#define CPU0_DBG_SRST_REQ_EN		(1 << 12) +#define CPU0_NEON_SRST_REQ_EN		(1 << 4) +#define CPU0_SRST_REQ_EN		(1 << 0) + +enum { +	HI3620_CTRL, +	ERROR_CTRL, +}; + +static void __iomem *ctrl_base; +static int id; + +static void set_cpu_hi3620(int cpu, bool enable) +{ +	u32 val = 0; + +	if (enable) { +		/* MTCMOS set */ +		if ((cpu == 2) || (cpu == 3)) +			writel_relaxed(CPU2_ISO_CTRL << (cpu - 2), +				       ctrl_base + SCPERPWREN); +		udelay(100); + +		/* Enable core */ +		writel_relaxed(0x01 << cpu, ctrl_base + SCCPUCOREEN); + +		/* unreset */ +		val = CPU0_DBG_SRST_REQ_EN | CPU0_NEON_SRST_REQ_EN +			| CPU0_SRST_REQ_EN; +		writel_relaxed(val << cpu, ctrl_base + SCCPURSTDIS); +		/* reset */ +		val |= CPU0_HPM_SRST_REQ_EN; +		writel_relaxed(val << cpu, ctrl_base + SCCPURSTEN); + +		/* ISO disable */ +		if ((cpu == 2) || (cpu == 3)) +			writel_relaxed(CPU2_ISO_CTRL << (cpu - 2), +				       ctrl_base + SCISODIS); +		udelay(1); + +		/* WFI Mask */ +		val = readl_relaxed(ctrl_base + SCPERCTRL0); +		val &= ~(CPU0_WFI_MASK_CFG << cpu); +		writel_relaxed(val, ctrl_base + SCPERCTRL0); + +		/* Unreset */ +		val = CPU0_DBG_SRST_REQ_EN | CPU0_NEON_SRST_REQ_EN +			| CPU0_SRST_REQ_EN | CPU0_HPM_SRST_REQ_EN; +		writel_relaxed(val << cpu, ctrl_base + SCCPURSTDIS); +	} else { +		/* wfi mask */ +		val = readl_relaxed(ctrl_base + SCPERCTRL0); +		val |= (CPU0_WFI_MASK_CFG << cpu); +		writel_relaxed(val, ctrl_base + SCPERCTRL0); + +		/* disable core*/ +		writel_relaxed(0x01 << cpu, ctrl_base + SCCPUCOREDIS); + +		if ((cpu == 2) || (cpu == 3)) { +			/* iso enable */ +			writel_relaxed(CPU2_ISO_CTRL << (cpu - 2), +				       ctrl_base + SCISOEN); +			udelay(1); +		} + +		/* reset */ +		val = CPU0_DBG_SRST_REQ_EN | CPU0_NEON_SRST_REQ_EN +			| CPU0_SRST_REQ_EN | CPU0_HPM_SRST_REQ_EN; +		writel_relaxed(val << cpu, ctrl_base + SCCPURSTEN); + +		if ((cpu == 2) || (cpu == 3)) { +			/* MTCMOS unset */ +			writel_relaxed(CPU2_ISO_CTRL << (cpu - 2), +				       ctrl_base + SCPERPWRDIS); +			udelay(100); +		} +	} +} + +static int hi3xxx_hotplug_init(void) +{ +	struct device_node *node; + +	node = of_find_compatible_node(NULL, NULL, "hisilicon,sysctrl"); +	if (node) { +		ctrl_base = of_iomap(node, 0); +		id = HI3620_CTRL; +		return 0; +	} +	id = ERROR_CTRL; +	return -ENOENT; +} + +void hi3xxx_set_cpu(int cpu, bool enable) +{ +	if (!ctrl_base) { +		if (hi3xxx_hotplug_init() < 0) +			return; +	} + +	if (id == HI3620_CTRL) +		set_cpu_hi3620(cpu, enable); +} + +static inline void cpu_enter_lowpower(void) +{ +	unsigned int v; + +	flush_cache_all(); + +	/* +	 * Turn off coherency and L1 D-cache +	 */ +	asm volatile( +	"	mrc	p15, 0, %0, c1, c0, 1\n" +	"	bic	%0, %0, #0x40\n" +	"	mcr	p15, 0, %0, c1, c0, 1\n" +	"	mrc	p15, 0, %0, c1, c0, 0\n" +	"	bic	%0, %0, #0x04\n" +	"	mcr	p15, 0, %0, c1, c0, 0\n" +	  : "=&r" (v) +	  : "r" (0) +	  : "cc"); +} + +#ifdef CONFIG_HOTPLUG_CPU +void hi3xxx_cpu_die(unsigned int cpu) +{ +	cpu_enter_lowpower(); +	hi3xxx_set_cpu_jump(cpu, phys_to_virt(0)); +	cpu_do_idle(); + +	/* We should have never returned from idle */ +	panic("cpu %d unexpectedly exit from shutdown\n", cpu); +} + +int hi3xxx_cpu_kill(unsigned int cpu) +{ +	unsigned long timeout = jiffies + msecs_to_jiffies(50); + +	while (hi3xxx_get_cpu_jump(cpu)) +		if (time_after(jiffies, timeout)) +			return 0; +	hi3xxx_set_cpu(cpu, false); +	return 1; +} +#endif diff --git a/arch/arm/mach-hisi/platsmp.c b/arch/arm/mach-hisi/platsmp.c new file mode 100644 index 00000000000..471f1ee3be2 --- /dev/null +++ b/arch/arm/mach-hisi/platsmp.c @@ -0,0 +1,89 @@ +/* + * 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(cpu, true); +	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, +#ifdef CONFIG_HOTPLUG_CPU +	.cpu_die		= hi3xxx_cpu_die, +	.cpu_kill		= hi3xxx_cpu_kill, +#endif +};  | 
