162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Copyright 2011 Freescale Semiconductor, Inc.
462306a36Sopenharmony_ci * Copyright 2011 Linaro Ltd.
562306a36Sopenharmony_ci */
662306a36Sopenharmony_ci
762306a36Sopenharmony_ci#include <linux/init.h>
862306a36Sopenharmony_ci#include <linux/of_address.h>
962306a36Sopenharmony_ci#include <linux/of.h>
1062306a36Sopenharmony_ci#include <linux/smp.h>
1162306a36Sopenharmony_ci
1262306a36Sopenharmony_ci#include <asm/cacheflush.h>
1362306a36Sopenharmony_ci#include <asm/page.h>
1462306a36Sopenharmony_ci#include <asm/smp_scu.h>
1562306a36Sopenharmony_ci#include <asm/mach/map.h>
1662306a36Sopenharmony_ci
1762306a36Sopenharmony_ci#include "common.h"
1862306a36Sopenharmony_ci#include "hardware.h"
1962306a36Sopenharmony_ci
2062306a36Sopenharmony_ciu32 g_diag_reg;
2162306a36Sopenharmony_cistatic void __iomem *scu_base;
2262306a36Sopenharmony_ci
2362306a36Sopenharmony_cistatic struct map_desc scu_io_desc __initdata = {
2462306a36Sopenharmony_ci	/* .virtual and .pfn are run-time assigned */
2562306a36Sopenharmony_ci	.length		= SZ_4K,
2662306a36Sopenharmony_ci	.type		= MT_DEVICE,
2762306a36Sopenharmony_ci};
2862306a36Sopenharmony_ci
2962306a36Sopenharmony_civoid __init imx_scu_map_io(void)
3062306a36Sopenharmony_ci{
3162306a36Sopenharmony_ci	unsigned long base;
3262306a36Sopenharmony_ci
3362306a36Sopenharmony_ci	/* Get SCU base */
3462306a36Sopenharmony_ci	asm("mrc p15, 4, %0, c15, c0, 0" : "=r" (base));
3562306a36Sopenharmony_ci
3662306a36Sopenharmony_ci	scu_io_desc.virtual = IMX_IO_P2V(base);
3762306a36Sopenharmony_ci	scu_io_desc.pfn = __phys_to_pfn(base);
3862306a36Sopenharmony_ci	iotable_init(&scu_io_desc, 1);
3962306a36Sopenharmony_ci
4062306a36Sopenharmony_ci	scu_base = IMX_IO_ADDRESS(base);
4162306a36Sopenharmony_ci}
4262306a36Sopenharmony_ci
4362306a36Sopenharmony_cistatic int imx_boot_secondary(unsigned int cpu, struct task_struct *idle)
4462306a36Sopenharmony_ci{
4562306a36Sopenharmony_ci	imx_set_cpu_jump(cpu, v7_secondary_startup);
4662306a36Sopenharmony_ci	imx_enable_cpu(cpu, true);
4762306a36Sopenharmony_ci	return 0;
4862306a36Sopenharmony_ci}
4962306a36Sopenharmony_ci
5062306a36Sopenharmony_ci/*
5162306a36Sopenharmony_ci * Initialise the CPU possible map early - this describes the CPUs
5262306a36Sopenharmony_ci * which may be present or become present in the system.
5362306a36Sopenharmony_ci */
5462306a36Sopenharmony_cistatic void __init imx_smp_init_cpus(void)
5562306a36Sopenharmony_ci{
5662306a36Sopenharmony_ci	int i, ncores;
5762306a36Sopenharmony_ci
5862306a36Sopenharmony_ci	ncores = scu_get_core_count(scu_base);
5962306a36Sopenharmony_ci
6062306a36Sopenharmony_ci	for (i = ncores; i < NR_CPUS; i++)
6162306a36Sopenharmony_ci		set_cpu_possible(i, false);
6262306a36Sopenharmony_ci}
6362306a36Sopenharmony_ci
6462306a36Sopenharmony_civoid imx_smp_prepare(void)
6562306a36Sopenharmony_ci{
6662306a36Sopenharmony_ci	scu_enable(scu_base);
6762306a36Sopenharmony_ci}
6862306a36Sopenharmony_ci
6962306a36Sopenharmony_cistatic void __init imx_smp_prepare_cpus(unsigned int max_cpus)
7062306a36Sopenharmony_ci{
7162306a36Sopenharmony_ci	imx_smp_prepare();
7262306a36Sopenharmony_ci
7362306a36Sopenharmony_ci	/*
7462306a36Sopenharmony_ci	 * The diagnostic register holds the errata bits.  Mostly bootloader
7562306a36Sopenharmony_ci	 * does not bring up secondary cores, so that when errata bits are set
7662306a36Sopenharmony_ci	 * in bootloader, they are set only for boot cpu.  But on a SMP
7762306a36Sopenharmony_ci	 * configuration, it should be equally done on every single core.
7862306a36Sopenharmony_ci	 * Read the register from boot cpu here, and will replicate it into
7962306a36Sopenharmony_ci	 * secondary cores when booting them.
8062306a36Sopenharmony_ci	 */
8162306a36Sopenharmony_ci	asm("mrc p15, 0, %0, c15, c0, 1" : "=r" (g_diag_reg) : : "cc");
8262306a36Sopenharmony_ci	sync_cache_w(&g_diag_reg);
8362306a36Sopenharmony_ci}
8462306a36Sopenharmony_ci
8562306a36Sopenharmony_ciconst struct smp_operations imx_smp_ops __initconst = {
8662306a36Sopenharmony_ci	.smp_init_cpus		= imx_smp_init_cpus,
8762306a36Sopenharmony_ci	.smp_prepare_cpus	= imx_smp_prepare_cpus,
8862306a36Sopenharmony_ci	.smp_boot_secondary	= imx_boot_secondary,
8962306a36Sopenharmony_ci#ifdef CONFIG_HOTPLUG_CPU
9062306a36Sopenharmony_ci	.cpu_die		= imx_cpu_die,
9162306a36Sopenharmony_ci	.cpu_kill		= imx_cpu_kill,
9262306a36Sopenharmony_ci#endif
9362306a36Sopenharmony_ci};
9462306a36Sopenharmony_ci
9562306a36Sopenharmony_ci/*
9662306a36Sopenharmony_ci * Initialise the CPU possible map early - this describes the CPUs
9762306a36Sopenharmony_ci * which may be present or become present in the system.
9862306a36Sopenharmony_ci */
9962306a36Sopenharmony_cistatic void __init imx7_smp_init_cpus(void)
10062306a36Sopenharmony_ci{
10162306a36Sopenharmony_ci	struct device_node *np;
10262306a36Sopenharmony_ci	int i, ncores = 0;
10362306a36Sopenharmony_ci
10462306a36Sopenharmony_ci	/* The iMX7D SCU does not report core count, get it from DT */
10562306a36Sopenharmony_ci	for_each_of_cpu_node(np)
10662306a36Sopenharmony_ci		ncores++;
10762306a36Sopenharmony_ci
10862306a36Sopenharmony_ci	for (i = ncores; i < NR_CPUS; i++)
10962306a36Sopenharmony_ci		set_cpu_possible(i, false);
11062306a36Sopenharmony_ci}
11162306a36Sopenharmony_ci
11262306a36Sopenharmony_ciconst struct smp_operations imx7_smp_ops __initconst = {
11362306a36Sopenharmony_ci	.smp_init_cpus		= imx7_smp_init_cpus,
11462306a36Sopenharmony_ci	.smp_boot_secondary	= imx_boot_secondary,
11562306a36Sopenharmony_ci#ifdef CONFIG_HOTPLUG_CPU
11662306a36Sopenharmony_ci	.cpu_die		= imx_cpu_die,
11762306a36Sopenharmony_ci	.cpu_kill		= imx_cpu_kill,
11862306a36Sopenharmony_ci#endif
11962306a36Sopenharmony_ci};
12062306a36Sopenharmony_ci
12162306a36Sopenharmony_ci#define DCFG_CCSR_SCRATCHRW1	0x200
12262306a36Sopenharmony_ci
12362306a36Sopenharmony_cistatic int ls1021a_boot_secondary(unsigned int cpu, struct task_struct *idle)
12462306a36Sopenharmony_ci{
12562306a36Sopenharmony_ci	arch_send_wakeup_ipi_mask(cpumask_of(cpu));
12662306a36Sopenharmony_ci
12762306a36Sopenharmony_ci	return 0;
12862306a36Sopenharmony_ci}
12962306a36Sopenharmony_ci
13062306a36Sopenharmony_cistatic void __init ls1021a_smp_prepare_cpus(unsigned int max_cpus)
13162306a36Sopenharmony_ci{
13262306a36Sopenharmony_ci	struct device_node *np;
13362306a36Sopenharmony_ci	void __iomem *dcfg_base;
13462306a36Sopenharmony_ci	unsigned long paddr;
13562306a36Sopenharmony_ci
13662306a36Sopenharmony_ci	np = of_find_compatible_node(NULL, NULL, "fsl,ls1021a-dcfg");
13762306a36Sopenharmony_ci	dcfg_base = of_iomap(np, 0);
13862306a36Sopenharmony_ci	of_node_put(np);
13962306a36Sopenharmony_ci	BUG_ON(!dcfg_base);
14062306a36Sopenharmony_ci
14162306a36Sopenharmony_ci	paddr = __pa_symbol(secondary_startup);
14262306a36Sopenharmony_ci	writel_relaxed(cpu_to_be32(paddr), dcfg_base + DCFG_CCSR_SCRATCHRW1);
14362306a36Sopenharmony_ci
14462306a36Sopenharmony_ci	iounmap(dcfg_base);
14562306a36Sopenharmony_ci}
14662306a36Sopenharmony_ci
14762306a36Sopenharmony_ciconst struct smp_operations ls1021a_smp_ops __initconst = {
14862306a36Sopenharmony_ci	.smp_prepare_cpus	= ls1021a_smp_prepare_cpus,
14962306a36Sopenharmony_ci	.smp_boot_secondary	= ls1021a_boot_secondary,
15062306a36Sopenharmony_ci};
151