diff options
Diffstat (limited to 'arch/arm/mach-hisi/hotplug.c')
| -rw-r--r-- | arch/arm/mach-hisi/hotplug.c | 202 | 
1 files changed, 202 insertions, 0 deletions
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  | 
