18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * Copyright 2014 Linaro Ltd.
48c2ecf20Sopenharmony_ci * Copyright (C) 2014 ZTE Corporation.
58c2ecf20Sopenharmony_ci */
68c2ecf20Sopenharmony_ci
78c2ecf20Sopenharmony_ci#include <linux/delay.h>
88c2ecf20Sopenharmony_ci#include <linux/errno.h>
98c2ecf20Sopenharmony_ci#include <linux/init.h>
108c2ecf20Sopenharmony_ci#include <linux/io.h>
118c2ecf20Sopenharmony_ci#include <linux/jiffies.h>
128c2ecf20Sopenharmony_ci#include <linux/of.h>
138c2ecf20Sopenharmony_ci#include <linux/of_address.h>
148c2ecf20Sopenharmony_ci#include <linux/smp.h>
158c2ecf20Sopenharmony_ci
168c2ecf20Sopenharmony_ci#include <asm/cacheflush.h>
178c2ecf20Sopenharmony_ci#include <asm/cp15.h>
188c2ecf20Sopenharmony_ci#include <asm/fncpy.h>
198c2ecf20Sopenharmony_ci#include <asm/proc-fns.h>
208c2ecf20Sopenharmony_ci#include <asm/smp_scu.h>
218c2ecf20Sopenharmony_ci#include <asm/smp_plat.h>
228c2ecf20Sopenharmony_ci
238c2ecf20Sopenharmony_ci#include "core.h"
248c2ecf20Sopenharmony_ci
258c2ecf20Sopenharmony_ci#define AON_SYS_CTRL_RESERVED1		0xa8
268c2ecf20Sopenharmony_ci
278c2ecf20Sopenharmony_ci#define BUS_MATRIX_REMAP_CONFIG		0x00
288c2ecf20Sopenharmony_ci
298c2ecf20Sopenharmony_ci#define PCU_CPU0_CTRL			0x00
308c2ecf20Sopenharmony_ci#define PCU_CPU1_CTRL			0x04
318c2ecf20Sopenharmony_ci#define PCU_CPU1_ST			0x0c
328c2ecf20Sopenharmony_ci#define PCU_GLOBAL_CTRL			0x14
338c2ecf20Sopenharmony_ci#define PCU_EXPEND_CONTROL		0x34
348c2ecf20Sopenharmony_ci
358c2ecf20Sopenharmony_ci#define ZX_IRAM_BASE			0x00200000
368c2ecf20Sopenharmony_ci
378c2ecf20Sopenharmony_cistatic void __iomem *pcu_base;
388c2ecf20Sopenharmony_cistatic void __iomem *matrix_base;
398c2ecf20Sopenharmony_cistatic void __iomem *scu_base;
408c2ecf20Sopenharmony_ci
418c2ecf20Sopenharmony_civoid __init zx_smp_prepare_cpus(unsigned int max_cpus)
428c2ecf20Sopenharmony_ci{
438c2ecf20Sopenharmony_ci	struct device_node *np;
448c2ecf20Sopenharmony_ci	unsigned long base = 0;
458c2ecf20Sopenharmony_ci	void __iomem *aonsysctrl_base;
468c2ecf20Sopenharmony_ci	void __iomem *sys_iram;
478c2ecf20Sopenharmony_ci
488c2ecf20Sopenharmony_ci	base = scu_a9_get_base();
498c2ecf20Sopenharmony_ci	scu_base = ioremap(base, SZ_256);
508c2ecf20Sopenharmony_ci	if (!scu_base) {
518c2ecf20Sopenharmony_ci		pr_err("%s: failed to map scu\n", __func__);
528c2ecf20Sopenharmony_ci		return;
538c2ecf20Sopenharmony_ci	}
548c2ecf20Sopenharmony_ci
558c2ecf20Sopenharmony_ci	scu_enable(scu_base);
568c2ecf20Sopenharmony_ci
578c2ecf20Sopenharmony_ci	np = of_find_compatible_node(NULL, NULL, "zte,sysctrl");
588c2ecf20Sopenharmony_ci	if (!np) {
598c2ecf20Sopenharmony_ci		pr_err("%s: failed to find sysctrl node\n", __func__);
608c2ecf20Sopenharmony_ci		return;
618c2ecf20Sopenharmony_ci	}
628c2ecf20Sopenharmony_ci
638c2ecf20Sopenharmony_ci	aonsysctrl_base = of_iomap(np, 0);
648c2ecf20Sopenharmony_ci	if (!aonsysctrl_base) {
658c2ecf20Sopenharmony_ci		pr_err("%s: failed to map aonsysctrl\n", __func__);
668c2ecf20Sopenharmony_ci		of_node_put(np);
678c2ecf20Sopenharmony_ci		return;
688c2ecf20Sopenharmony_ci	}
698c2ecf20Sopenharmony_ci
708c2ecf20Sopenharmony_ci	/*
718c2ecf20Sopenharmony_ci	 * Write the address of secondary startup into the
728c2ecf20Sopenharmony_ci	 * system-wide flags register. The BootMonitor waits
738c2ecf20Sopenharmony_ci	 * until it receives a soft interrupt, and then the
748c2ecf20Sopenharmony_ci	 * secondary CPU branches to this address.
758c2ecf20Sopenharmony_ci	 */
768c2ecf20Sopenharmony_ci	__raw_writel(__pa_symbol(zx_secondary_startup),
778c2ecf20Sopenharmony_ci		     aonsysctrl_base + AON_SYS_CTRL_RESERVED1);
788c2ecf20Sopenharmony_ci
798c2ecf20Sopenharmony_ci	iounmap(aonsysctrl_base);
808c2ecf20Sopenharmony_ci	of_node_put(np);
818c2ecf20Sopenharmony_ci
828c2ecf20Sopenharmony_ci	np = of_find_compatible_node(NULL, NULL, "zte,zx296702-pcu");
838c2ecf20Sopenharmony_ci	pcu_base = of_iomap(np, 0);
848c2ecf20Sopenharmony_ci	of_node_put(np);
858c2ecf20Sopenharmony_ci	WARN_ON(!pcu_base);
868c2ecf20Sopenharmony_ci
878c2ecf20Sopenharmony_ci	np = of_find_compatible_node(NULL, NULL, "zte,zx-bus-matrix");
888c2ecf20Sopenharmony_ci	matrix_base = of_iomap(np, 0);
898c2ecf20Sopenharmony_ci	of_node_put(np);
908c2ecf20Sopenharmony_ci	WARN_ON(!matrix_base);
918c2ecf20Sopenharmony_ci
928c2ecf20Sopenharmony_ci	/* Map the first 4 KB IRAM for suspend usage */
938c2ecf20Sopenharmony_ci	sys_iram = __arm_ioremap_exec(ZX_IRAM_BASE, PAGE_SIZE, false);
948c2ecf20Sopenharmony_ci	zx_secondary_startup_pa = __pa_symbol(zx_secondary_startup);
958c2ecf20Sopenharmony_ci	fncpy(sys_iram, &zx_resume_jump, zx_suspend_iram_sz);
968c2ecf20Sopenharmony_ci}
978c2ecf20Sopenharmony_ci
988c2ecf20Sopenharmony_cistatic int zx_boot_secondary(unsigned int cpu, struct task_struct *idle)
998c2ecf20Sopenharmony_ci{
1008c2ecf20Sopenharmony_ci	static bool first_boot = true;
1018c2ecf20Sopenharmony_ci
1028c2ecf20Sopenharmony_ci	if (first_boot) {
1038c2ecf20Sopenharmony_ci		arch_send_wakeup_ipi_mask(cpumask_of(cpu));
1048c2ecf20Sopenharmony_ci		first_boot = false;
1058c2ecf20Sopenharmony_ci		return 0;
1068c2ecf20Sopenharmony_ci	}
1078c2ecf20Sopenharmony_ci
1088c2ecf20Sopenharmony_ci	/* Swap the base address mapping between IRAM and IROM */
1098c2ecf20Sopenharmony_ci	writel_relaxed(0x1, matrix_base + BUS_MATRIX_REMAP_CONFIG);
1108c2ecf20Sopenharmony_ci
1118c2ecf20Sopenharmony_ci	/* Power on CPU1 */
1128c2ecf20Sopenharmony_ci	writel_relaxed(0x0, pcu_base + PCU_CPU1_CTRL);
1138c2ecf20Sopenharmony_ci
1148c2ecf20Sopenharmony_ci	/* Wait for power on ack */
1158c2ecf20Sopenharmony_ci	while (readl_relaxed(pcu_base + PCU_CPU1_ST) & 0x4)
1168c2ecf20Sopenharmony_ci		cpu_relax();
1178c2ecf20Sopenharmony_ci
1188c2ecf20Sopenharmony_ci	/* Swap back the mapping of IRAM and IROM */
1198c2ecf20Sopenharmony_ci	writel_relaxed(0x0, matrix_base + BUS_MATRIX_REMAP_CONFIG);
1208c2ecf20Sopenharmony_ci
1218c2ecf20Sopenharmony_ci	return 0;
1228c2ecf20Sopenharmony_ci}
1238c2ecf20Sopenharmony_ci
1248c2ecf20Sopenharmony_ci#ifdef CONFIG_HOTPLUG_CPU
1258c2ecf20Sopenharmony_cistatic inline void cpu_enter_lowpower(void)
1268c2ecf20Sopenharmony_ci{
1278c2ecf20Sopenharmony_ci	unsigned int v;
1288c2ecf20Sopenharmony_ci
1298c2ecf20Sopenharmony_ci	asm volatile(
1308c2ecf20Sopenharmony_ci		"mcr	p15, 0, %1, c7, c5, 0\n"
1318c2ecf20Sopenharmony_ci	"	mcr	p15, 0, %1, c7, c10, 4\n"
1328c2ecf20Sopenharmony_ci	/*
1338c2ecf20Sopenharmony_ci	 * Turn off coherency
1348c2ecf20Sopenharmony_ci	 */
1358c2ecf20Sopenharmony_ci	"	mrc	p15, 0, %0, c1, c0, 1\n"
1368c2ecf20Sopenharmony_ci	"	bic	%0, %0, %3\n"
1378c2ecf20Sopenharmony_ci	"	mcr	p15, 0, %0, c1, c0, 1\n"
1388c2ecf20Sopenharmony_ci	"	mrc	p15, 0, %0, c1, c0, 0\n"
1398c2ecf20Sopenharmony_ci	"	bic	%0, %0, %2\n"
1408c2ecf20Sopenharmony_ci	"	mcr	p15, 0, %0, c1, c0, 0\n"
1418c2ecf20Sopenharmony_ci	  : "=&r" (v)
1428c2ecf20Sopenharmony_ci	  : "r" (0), "Ir" (CR_C), "Ir" (0x40)
1438c2ecf20Sopenharmony_ci	  : "cc");
1448c2ecf20Sopenharmony_ci}
1458c2ecf20Sopenharmony_ci
1468c2ecf20Sopenharmony_cistatic int zx_cpu_kill(unsigned int cpu)
1478c2ecf20Sopenharmony_ci{
1488c2ecf20Sopenharmony_ci	unsigned long timeout = jiffies + msecs_to_jiffies(2000);
1498c2ecf20Sopenharmony_ci
1508c2ecf20Sopenharmony_ci	writel_relaxed(0x2, pcu_base + PCU_CPU1_CTRL);
1518c2ecf20Sopenharmony_ci
1528c2ecf20Sopenharmony_ci	while ((readl_relaxed(pcu_base + PCU_CPU1_ST) & 0x3) != 0x0) {
1538c2ecf20Sopenharmony_ci		if (time_after(jiffies, timeout)) {
1548c2ecf20Sopenharmony_ci			pr_err("*** cpu1 poweroff timeout\n");
1558c2ecf20Sopenharmony_ci			break;
1568c2ecf20Sopenharmony_ci		}
1578c2ecf20Sopenharmony_ci	}
1588c2ecf20Sopenharmony_ci	return 1;
1598c2ecf20Sopenharmony_ci}
1608c2ecf20Sopenharmony_ci
1618c2ecf20Sopenharmony_cistatic void zx_cpu_die(unsigned int cpu)
1628c2ecf20Sopenharmony_ci{
1638c2ecf20Sopenharmony_ci	scu_power_mode(scu_base, SCU_PM_POWEROFF);
1648c2ecf20Sopenharmony_ci	cpu_enter_lowpower();
1658c2ecf20Sopenharmony_ci
1668c2ecf20Sopenharmony_ci	while (1)
1678c2ecf20Sopenharmony_ci		cpu_do_idle();
1688c2ecf20Sopenharmony_ci}
1698c2ecf20Sopenharmony_ci#endif
1708c2ecf20Sopenharmony_ci
1718c2ecf20Sopenharmony_cistatic void zx_secondary_init(unsigned int cpu)
1728c2ecf20Sopenharmony_ci{
1738c2ecf20Sopenharmony_ci	scu_power_mode(scu_base, SCU_PM_NORMAL);
1748c2ecf20Sopenharmony_ci}
1758c2ecf20Sopenharmony_ci
1768c2ecf20Sopenharmony_cistatic const struct smp_operations zx_smp_ops __initconst = {
1778c2ecf20Sopenharmony_ci	.smp_prepare_cpus	= zx_smp_prepare_cpus,
1788c2ecf20Sopenharmony_ci	.smp_secondary_init	= zx_secondary_init,
1798c2ecf20Sopenharmony_ci	.smp_boot_secondary	= zx_boot_secondary,
1808c2ecf20Sopenharmony_ci#ifdef CONFIG_HOTPLUG_CPU
1818c2ecf20Sopenharmony_ci	.cpu_kill		= zx_cpu_kill,
1828c2ecf20Sopenharmony_ci	.cpu_die		= zx_cpu_die,
1838c2ecf20Sopenharmony_ci#endif
1848c2ecf20Sopenharmony_ci};
1858c2ecf20Sopenharmony_ci
1868c2ecf20Sopenharmony_ciCPU_METHOD_OF_DECLARE(zx_smp, "zte,zx296702-smp", &zx_smp_ops);
187