162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Copyright (c) 2013 Linaro Ltd.
462306a36Sopenharmony_ci * Copyright (c) 2013 HiSilicon Limited.
562306a36Sopenharmony_ci */
662306a36Sopenharmony_ci
762306a36Sopenharmony_ci#include <linux/cpu.h>
862306a36Sopenharmony_ci#include <linux/delay.h>
962306a36Sopenharmony_ci#include <linux/io.h>
1062306a36Sopenharmony_ci#include <linux/of_address.h>
1162306a36Sopenharmony_ci#include <asm/cacheflush.h>
1262306a36Sopenharmony_ci#include <asm/smp_plat.h>
1362306a36Sopenharmony_ci#include "core.h"
1462306a36Sopenharmony_ci
1562306a36Sopenharmony_ci/* Sysctrl registers in Hi3620 SoC */
1662306a36Sopenharmony_ci#define SCISOEN				0xc0
1762306a36Sopenharmony_ci#define SCISODIS			0xc4
1862306a36Sopenharmony_ci#define SCPERPWREN			0xd0
1962306a36Sopenharmony_ci#define SCPERPWRDIS			0xd4
2062306a36Sopenharmony_ci#define SCCPUCOREEN			0xf4
2162306a36Sopenharmony_ci#define SCCPUCOREDIS			0xf8
2262306a36Sopenharmony_ci#define SCPERCTRL0			0x200
2362306a36Sopenharmony_ci#define SCCPURSTEN			0x410
2462306a36Sopenharmony_ci#define SCCPURSTDIS			0x414
2562306a36Sopenharmony_ci
2662306a36Sopenharmony_ci/*
2762306a36Sopenharmony_ci * bit definition in SCISOEN/SCPERPWREN/...
2862306a36Sopenharmony_ci *
2962306a36Sopenharmony_ci * CPU2_ISO_CTRL	(1 << 5)
3062306a36Sopenharmony_ci * CPU3_ISO_CTRL	(1 << 6)
3162306a36Sopenharmony_ci * ...
3262306a36Sopenharmony_ci */
3362306a36Sopenharmony_ci#define CPU2_ISO_CTRL			(1 << 5)
3462306a36Sopenharmony_ci
3562306a36Sopenharmony_ci/*
3662306a36Sopenharmony_ci * bit definition in SCPERCTRL0
3762306a36Sopenharmony_ci *
3862306a36Sopenharmony_ci * CPU0_WFI_MASK_CFG	(1 << 28)
3962306a36Sopenharmony_ci * CPU1_WFI_MASK_CFG	(1 << 29)
4062306a36Sopenharmony_ci * ...
4162306a36Sopenharmony_ci */
4262306a36Sopenharmony_ci#define CPU0_WFI_MASK_CFG		(1 << 28)
4362306a36Sopenharmony_ci
4462306a36Sopenharmony_ci/*
4562306a36Sopenharmony_ci * bit definition in SCCPURSTEN/...
4662306a36Sopenharmony_ci *
4762306a36Sopenharmony_ci * CPU0_SRST_REQ_EN	(1 << 0)
4862306a36Sopenharmony_ci * CPU1_SRST_REQ_EN	(1 << 1)
4962306a36Sopenharmony_ci * ...
5062306a36Sopenharmony_ci */
5162306a36Sopenharmony_ci#define CPU0_HPM_SRST_REQ_EN		(1 << 22)
5262306a36Sopenharmony_ci#define CPU0_DBG_SRST_REQ_EN		(1 << 12)
5362306a36Sopenharmony_ci#define CPU0_NEON_SRST_REQ_EN		(1 << 4)
5462306a36Sopenharmony_ci#define CPU0_SRST_REQ_EN		(1 << 0)
5562306a36Sopenharmony_ci
5662306a36Sopenharmony_ci#define HIX5HD2_PERI_CRG20		0x50
5762306a36Sopenharmony_ci#define CRG20_CPU1_RESET		(1 << 17)
5862306a36Sopenharmony_ci
5962306a36Sopenharmony_ci#define HIX5HD2_PERI_PMC0		0x1000
6062306a36Sopenharmony_ci#define PMC0_CPU1_WAIT_MTCOMS_ACK	(1 << 8)
6162306a36Sopenharmony_ci#define PMC0_CPU1_PMC_ENABLE		(1 << 7)
6262306a36Sopenharmony_ci#define PMC0_CPU1_POWERDOWN		(1 << 3)
6362306a36Sopenharmony_ci
6462306a36Sopenharmony_ci#define HIP01_PERI9                    0x50
6562306a36Sopenharmony_ci#define PERI9_CPU1_RESET               (1 << 1)
6662306a36Sopenharmony_ci
6762306a36Sopenharmony_cienum {
6862306a36Sopenharmony_ci	HI3620_CTRL,
6962306a36Sopenharmony_ci	ERROR_CTRL,
7062306a36Sopenharmony_ci};
7162306a36Sopenharmony_ci
7262306a36Sopenharmony_cistatic void __iomem *ctrl_base;
7362306a36Sopenharmony_cistatic int id;
7462306a36Sopenharmony_ci
7562306a36Sopenharmony_cistatic void set_cpu_hi3620(int cpu, bool enable)
7662306a36Sopenharmony_ci{
7762306a36Sopenharmony_ci	u32 val = 0;
7862306a36Sopenharmony_ci
7962306a36Sopenharmony_ci	if (enable) {
8062306a36Sopenharmony_ci		/* MTCMOS set */
8162306a36Sopenharmony_ci		if ((cpu == 2) || (cpu == 3))
8262306a36Sopenharmony_ci			writel_relaxed(CPU2_ISO_CTRL << (cpu - 2),
8362306a36Sopenharmony_ci				       ctrl_base + SCPERPWREN);
8462306a36Sopenharmony_ci		udelay(100);
8562306a36Sopenharmony_ci
8662306a36Sopenharmony_ci		/* Enable core */
8762306a36Sopenharmony_ci		writel_relaxed(0x01 << cpu, ctrl_base + SCCPUCOREEN);
8862306a36Sopenharmony_ci
8962306a36Sopenharmony_ci		/* unreset */
9062306a36Sopenharmony_ci		val = CPU0_DBG_SRST_REQ_EN | CPU0_NEON_SRST_REQ_EN
9162306a36Sopenharmony_ci			| CPU0_SRST_REQ_EN;
9262306a36Sopenharmony_ci		writel_relaxed(val << cpu, ctrl_base + SCCPURSTDIS);
9362306a36Sopenharmony_ci		/* reset */
9462306a36Sopenharmony_ci		val |= CPU0_HPM_SRST_REQ_EN;
9562306a36Sopenharmony_ci		writel_relaxed(val << cpu, ctrl_base + SCCPURSTEN);
9662306a36Sopenharmony_ci
9762306a36Sopenharmony_ci		/* ISO disable */
9862306a36Sopenharmony_ci		if ((cpu == 2) || (cpu == 3))
9962306a36Sopenharmony_ci			writel_relaxed(CPU2_ISO_CTRL << (cpu - 2),
10062306a36Sopenharmony_ci				       ctrl_base + SCISODIS);
10162306a36Sopenharmony_ci		udelay(1);
10262306a36Sopenharmony_ci
10362306a36Sopenharmony_ci		/* WFI Mask */
10462306a36Sopenharmony_ci		val = readl_relaxed(ctrl_base + SCPERCTRL0);
10562306a36Sopenharmony_ci		val &= ~(CPU0_WFI_MASK_CFG << cpu);
10662306a36Sopenharmony_ci		writel_relaxed(val, ctrl_base + SCPERCTRL0);
10762306a36Sopenharmony_ci
10862306a36Sopenharmony_ci		/* Unreset */
10962306a36Sopenharmony_ci		val = CPU0_DBG_SRST_REQ_EN | CPU0_NEON_SRST_REQ_EN
11062306a36Sopenharmony_ci			| CPU0_SRST_REQ_EN | CPU0_HPM_SRST_REQ_EN;
11162306a36Sopenharmony_ci		writel_relaxed(val << cpu, ctrl_base + SCCPURSTDIS);
11262306a36Sopenharmony_ci	} else {
11362306a36Sopenharmony_ci		/* wfi mask */
11462306a36Sopenharmony_ci		val = readl_relaxed(ctrl_base + SCPERCTRL0);
11562306a36Sopenharmony_ci		val |= (CPU0_WFI_MASK_CFG << cpu);
11662306a36Sopenharmony_ci		writel_relaxed(val, ctrl_base + SCPERCTRL0);
11762306a36Sopenharmony_ci
11862306a36Sopenharmony_ci		/* disable core*/
11962306a36Sopenharmony_ci		writel_relaxed(0x01 << cpu, ctrl_base + SCCPUCOREDIS);
12062306a36Sopenharmony_ci
12162306a36Sopenharmony_ci		if ((cpu == 2) || (cpu == 3)) {
12262306a36Sopenharmony_ci			/* iso enable */
12362306a36Sopenharmony_ci			writel_relaxed(CPU2_ISO_CTRL << (cpu - 2),
12462306a36Sopenharmony_ci				       ctrl_base + SCISOEN);
12562306a36Sopenharmony_ci			udelay(1);
12662306a36Sopenharmony_ci		}
12762306a36Sopenharmony_ci
12862306a36Sopenharmony_ci		/* reset */
12962306a36Sopenharmony_ci		val = CPU0_DBG_SRST_REQ_EN | CPU0_NEON_SRST_REQ_EN
13062306a36Sopenharmony_ci			| CPU0_SRST_REQ_EN | CPU0_HPM_SRST_REQ_EN;
13162306a36Sopenharmony_ci		writel_relaxed(val << cpu, ctrl_base + SCCPURSTEN);
13262306a36Sopenharmony_ci
13362306a36Sopenharmony_ci		if ((cpu == 2) || (cpu == 3)) {
13462306a36Sopenharmony_ci			/* MTCMOS unset */
13562306a36Sopenharmony_ci			writel_relaxed(CPU2_ISO_CTRL << (cpu - 2),
13662306a36Sopenharmony_ci				       ctrl_base + SCPERPWRDIS);
13762306a36Sopenharmony_ci			udelay(100);
13862306a36Sopenharmony_ci		}
13962306a36Sopenharmony_ci	}
14062306a36Sopenharmony_ci}
14162306a36Sopenharmony_ci
14262306a36Sopenharmony_cistatic int hi3xxx_hotplug_init(void)
14362306a36Sopenharmony_ci{
14462306a36Sopenharmony_ci	struct device_node *node;
14562306a36Sopenharmony_ci
14662306a36Sopenharmony_ci	node = of_find_compatible_node(NULL, NULL, "hisilicon,sysctrl");
14762306a36Sopenharmony_ci	if (!node) {
14862306a36Sopenharmony_ci		id = ERROR_CTRL;
14962306a36Sopenharmony_ci		return -ENOENT;
15062306a36Sopenharmony_ci	}
15162306a36Sopenharmony_ci
15262306a36Sopenharmony_ci	ctrl_base = of_iomap(node, 0);
15362306a36Sopenharmony_ci	of_node_put(node);
15462306a36Sopenharmony_ci	if (!ctrl_base) {
15562306a36Sopenharmony_ci		id = ERROR_CTRL;
15662306a36Sopenharmony_ci		return -ENOMEM;
15762306a36Sopenharmony_ci	}
15862306a36Sopenharmony_ci
15962306a36Sopenharmony_ci	id = HI3620_CTRL;
16062306a36Sopenharmony_ci	return 0;
16162306a36Sopenharmony_ci}
16262306a36Sopenharmony_ci
16362306a36Sopenharmony_civoid hi3xxx_set_cpu(int cpu, bool enable)
16462306a36Sopenharmony_ci{
16562306a36Sopenharmony_ci	if (!ctrl_base) {
16662306a36Sopenharmony_ci		if (hi3xxx_hotplug_init() < 0)
16762306a36Sopenharmony_ci			return;
16862306a36Sopenharmony_ci	}
16962306a36Sopenharmony_ci
17062306a36Sopenharmony_ci	if (id == HI3620_CTRL)
17162306a36Sopenharmony_ci		set_cpu_hi3620(cpu, enable);
17262306a36Sopenharmony_ci}
17362306a36Sopenharmony_ci
17462306a36Sopenharmony_cistatic bool hix5hd2_hotplug_init(void)
17562306a36Sopenharmony_ci{
17662306a36Sopenharmony_ci	struct device_node *np;
17762306a36Sopenharmony_ci
17862306a36Sopenharmony_ci	np = of_find_compatible_node(NULL, NULL, "hisilicon,cpuctrl");
17962306a36Sopenharmony_ci	if (!np)
18062306a36Sopenharmony_ci		return false;
18162306a36Sopenharmony_ci
18262306a36Sopenharmony_ci	ctrl_base = of_iomap(np, 0);
18362306a36Sopenharmony_ci	of_node_put(np);
18462306a36Sopenharmony_ci	if (!ctrl_base)
18562306a36Sopenharmony_ci		return false;
18662306a36Sopenharmony_ci
18762306a36Sopenharmony_ci	return true;
18862306a36Sopenharmony_ci}
18962306a36Sopenharmony_ci
19062306a36Sopenharmony_civoid hix5hd2_set_cpu(int cpu, bool enable)
19162306a36Sopenharmony_ci{
19262306a36Sopenharmony_ci	u32 val = 0;
19362306a36Sopenharmony_ci
19462306a36Sopenharmony_ci	if (!ctrl_base)
19562306a36Sopenharmony_ci		if (!hix5hd2_hotplug_init())
19662306a36Sopenharmony_ci			BUG();
19762306a36Sopenharmony_ci
19862306a36Sopenharmony_ci	if (enable) {
19962306a36Sopenharmony_ci		/* power on cpu1 */
20062306a36Sopenharmony_ci		val = readl_relaxed(ctrl_base + HIX5HD2_PERI_PMC0);
20162306a36Sopenharmony_ci		val &= ~(PMC0_CPU1_WAIT_MTCOMS_ACK | PMC0_CPU1_POWERDOWN);
20262306a36Sopenharmony_ci		val |= PMC0_CPU1_PMC_ENABLE;
20362306a36Sopenharmony_ci		writel_relaxed(val, ctrl_base + HIX5HD2_PERI_PMC0);
20462306a36Sopenharmony_ci		/* unreset */
20562306a36Sopenharmony_ci		val = readl_relaxed(ctrl_base + HIX5HD2_PERI_CRG20);
20662306a36Sopenharmony_ci		val &= ~CRG20_CPU1_RESET;
20762306a36Sopenharmony_ci		writel_relaxed(val, ctrl_base + HIX5HD2_PERI_CRG20);
20862306a36Sopenharmony_ci	} else {
20962306a36Sopenharmony_ci		/* power down cpu1 */
21062306a36Sopenharmony_ci		val = readl_relaxed(ctrl_base + HIX5HD2_PERI_PMC0);
21162306a36Sopenharmony_ci		val |= PMC0_CPU1_PMC_ENABLE | PMC0_CPU1_POWERDOWN;
21262306a36Sopenharmony_ci		val &= ~PMC0_CPU1_WAIT_MTCOMS_ACK;
21362306a36Sopenharmony_ci		writel_relaxed(val, ctrl_base + HIX5HD2_PERI_PMC0);
21462306a36Sopenharmony_ci
21562306a36Sopenharmony_ci		/* reset */
21662306a36Sopenharmony_ci		val = readl_relaxed(ctrl_base + HIX5HD2_PERI_CRG20);
21762306a36Sopenharmony_ci		val |= CRG20_CPU1_RESET;
21862306a36Sopenharmony_ci		writel_relaxed(val, ctrl_base + HIX5HD2_PERI_CRG20);
21962306a36Sopenharmony_ci	}
22062306a36Sopenharmony_ci}
22162306a36Sopenharmony_ci
22262306a36Sopenharmony_civoid hip01_set_cpu(int cpu, bool enable)
22362306a36Sopenharmony_ci{
22462306a36Sopenharmony_ci	unsigned int temp;
22562306a36Sopenharmony_ci	struct device_node *np;
22662306a36Sopenharmony_ci
22762306a36Sopenharmony_ci	if (!ctrl_base) {
22862306a36Sopenharmony_ci		np = of_find_compatible_node(NULL, NULL, "hisilicon,hip01-sysctrl");
22962306a36Sopenharmony_ci		BUG_ON(!np);
23062306a36Sopenharmony_ci		ctrl_base = of_iomap(np, 0);
23162306a36Sopenharmony_ci		of_node_put(np);
23262306a36Sopenharmony_ci		BUG_ON(!ctrl_base);
23362306a36Sopenharmony_ci	}
23462306a36Sopenharmony_ci
23562306a36Sopenharmony_ci	if (enable) {
23662306a36Sopenharmony_ci		/* reset on CPU1  */
23762306a36Sopenharmony_ci		temp = readl_relaxed(ctrl_base + HIP01_PERI9);
23862306a36Sopenharmony_ci		temp |= PERI9_CPU1_RESET;
23962306a36Sopenharmony_ci		writel_relaxed(temp, ctrl_base + HIP01_PERI9);
24062306a36Sopenharmony_ci
24162306a36Sopenharmony_ci		udelay(50);
24262306a36Sopenharmony_ci
24362306a36Sopenharmony_ci		/* unreset on CPU1 */
24462306a36Sopenharmony_ci		temp = readl_relaxed(ctrl_base + HIP01_PERI9);
24562306a36Sopenharmony_ci		temp &= ~PERI9_CPU1_RESET;
24662306a36Sopenharmony_ci		writel_relaxed(temp, ctrl_base + HIP01_PERI9);
24762306a36Sopenharmony_ci	}
24862306a36Sopenharmony_ci}
24962306a36Sopenharmony_ci
25062306a36Sopenharmony_cistatic inline void cpu_enter_lowpower(void)
25162306a36Sopenharmony_ci{
25262306a36Sopenharmony_ci	unsigned int v;
25362306a36Sopenharmony_ci
25462306a36Sopenharmony_ci	flush_cache_all();
25562306a36Sopenharmony_ci
25662306a36Sopenharmony_ci	/*
25762306a36Sopenharmony_ci	 * Turn off coherency and L1 D-cache
25862306a36Sopenharmony_ci	 */
25962306a36Sopenharmony_ci	asm volatile(
26062306a36Sopenharmony_ci	"	mrc	p15, 0, %0, c1, c0, 1\n"
26162306a36Sopenharmony_ci	"	bic	%0, %0, #0x40\n"
26262306a36Sopenharmony_ci	"	mcr	p15, 0, %0, c1, c0, 1\n"
26362306a36Sopenharmony_ci	"	mrc	p15, 0, %0, c1, c0, 0\n"
26462306a36Sopenharmony_ci	"	bic	%0, %0, #0x04\n"
26562306a36Sopenharmony_ci	"	mcr	p15, 0, %0, c1, c0, 0\n"
26662306a36Sopenharmony_ci	  : "=&r" (v)
26762306a36Sopenharmony_ci	  : "r" (0)
26862306a36Sopenharmony_ci	  : "cc");
26962306a36Sopenharmony_ci}
27062306a36Sopenharmony_ci
27162306a36Sopenharmony_ci#ifdef CONFIG_HOTPLUG_CPU
27262306a36Sopenharmony_civoid hi3xxx_cpu_die(unsigned int cpu)
27362306a36Sopenharmony_ci{
27462306a36Sopenharmony_ci	cpu_enter_lowpower();
27562306a36Sopenharmony_ci	hi3xxx_set_cpu_jump(cpu, phys_to_virt(0));
27662306a36Sopenharmony_ci	cpu_do_idle();
27762306a36Sopenharmony_ci
27862306a36Sopenharmony_ci	/* We should have never returned from idle */
27962306a36Sopenharmony_ci	panic("cpu %d unexpectedly exit from shutdown\n", cpu);
28062306a36Sopenharmony_ci}
28162306a36Sopenharmony_ci
28262306a36Sopenharmony_ciint hi3xxx_cpu_kill(unsigned int cpu)
28362306a36Sopenharmony_ci{
28462306a36Sopenharmony_ci	unsigned long timeout = jiffies + msecs_to_jiffies(50);
28562306a36Sopenharmony_ci
28662306a36Sopenharmony_ci	while (hi3xxx_get_cpu_jump(cpu))
28762306a36Sopenharmony_ci		if (time_after(jiffies, timeout))
28862306a36Sopenharmony_ci			return 0;
28962306a36Sopenharmony_ci	hi3xxx_set_cpu(cpu, false);
29062306a36Sopenharmony_ci	return 1;
29162306a36Sopenharmony_ci}
29262306a36Sopenharmony_ci
29362306a36Sopenharmony_civoid hix5hd2_cpu_die(unsigned int cpu)
29462306a36Sopenharmony_ci{
29562306a36Sopenharmony_ci	flush_cache_all();
29662306a36Sopenharmony_ci	hix5hd2_set_cpu(cpu, false);
29762306a36Sopenharmony_ci}
29862306a36Sopenharmony_ci#endif
299