162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Broadcom BCM63138 DSL SoCs SMP support code
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * Copyright (C) 2015, Broadcom Corporation
662306a36Sopenharmony_ci */
762306a36Sopenharmony_ci
862306a36Sopenharmony_ci#include <linux/delay.h>
962306a36Sopenharmony_ci#include <linux/init.h>
1062306a36Sopenharmony_ci#include <linux/smp.h>
1162306a36Sopenharmony_ci#include <linux/io.h>
1262306a36Sopenharmony_ci#include <linux/of.h>
1362306a36Sopenharmony_ci#include <linux/of_address.h>
1462306a36Sopenharmony_ci
1562306a36Sopenharmony_ci#include <asm/cacheflush.h>
1662306a36Sopenharmony_ci#include <asm/smp_scu.h>
1762306a36Sopenharmony_ci#include <asm/smp_plat.h>
1862306a36Sopenharmony_ci#include <asm/vfp.h>
1962306a36Sopenharmony_ci
2062306a36Sopenharmony_ci#include "bcm63xx_smp.h"
2162306a36Sopenharmony_ci
2262306a36Sopenharmony_ci/* Size of mapped Cortex A9 SCU address space */
2362306a36Sopenharmony_ci#define CORTEX_A9_SCU_SIZE	0x58
2462306a36Sopenharmony_ci
2562306a36Sopenharmony_ci/*
2662306a36Sopenharmony_ci * Enable the Cortex A9 Snoop Control Unit
2762306a36Sopenharmony_ci *
2862306a36Sopenharmony_ci * By the time this is called we already know there are multiple
2962306a36Sopenharmony_ci * cores present.  We assume we're running on a Cortex A9 processor,
3062306a36Sopenharmony_ci * so any trouble getting the base address register or getting the
3162306a36Sopenharmony_ci * SCU base is a problem.
3262306a36Sopenharmony_ci *
3362306a36Sopenharmony_ci * Return 0 if successful or an error code otherwise.
3462306a36Sopenharmony_ci */
3562306a36Sopenharmony_cistatic int __init scu_a9_enable(void)
3662306a36Sopenharmony_ci{
3762306a36Sopenharmony_ci	unsigned long config_base;
3862306a36Sopenharmony_ci	void __iomem *scu_base;
3962306a36Sopenharmony_ci	unsigned int i, ncores;
4062306a36Sopenharmony_ci
4162306a36Sopenharmony_ci	if (!scu_a9_has_base()) {
4262306a36Sopenharmony_ci		pr_err("no configuration base address register!\n");
4362306a36Sopenharmony_ci		return -ENXIO;
4462306a36Sopenharmony_ci	}
4562306a36Sopenharmony_ci
4662306a36Sopenharmony_ci	/* Config base address register value is zero for uniprocessor */
4762306a36Sopenharmony_ci	config_base = scu_a9_get_base();
4862306a36Sopenharmony_ci	if (!config_base) {
4962306a36Sopenharmony_ci		pr_err("hardware reports only one core\n");
5062306a36Sopenharmony_ci		return -ENOENT;
5162306a36Sopenharmony_ci	}
5262306a36Sopenharmony_ci
5362306a36Sopenharmony_ci	scu_base = ioremap((phys_addr_t)config_base, CORTEX_A9_SCU_SIZE);
5462306a36Sopenharmony_ci	if (!scu_base) {
5562306a36Sopenharmony_ci		pr_err("failed to remap config base (%lu/%u) for SCU\n",
5662306a36Sopenharmony_ci			config_base, CORTEX_A9_SCU_SIZE);
5762306a36Sopenharmony_ci		return -ENOMEM;
5862306a36Sopenharmony_ci	}
5962306a36Sopenharmony_ci
6062306a36Sopenharmony_ci	scu_enable(scu_base);
6162306a36Sopenharmony_ci
6262306a36Sopenharmony_ci	ncores = scu_base ? scu_get_core_count(scu_base) : 1;
6362306a36Sopenharmony_ci
6462306a36Sopenharmony_ci	if (ncores > nr_cpu_ids) {
6562306a36Sopenharmony_ci		pr_warn("SMP: %u cores greater than maximum (%u), clipping\n",
6662306a36Sopenharmony_ci				ncores, nr_cpu_ids);
6762306a36Sopenharmony_ci		ncores = nr_cpu_ids;
6862306a36Sopenharmony_ci	}
6962306a36Sopenharmony_ci
7062306a36Sopenharmony_ci	/* The BCM63138 SoC has two Cortex-A9 CPUs, CPU0 features a complete
7162306a36Sopenharmony_ci	 * and fully functional VFP unit that can be used, but CPU1 does not.
7262306a36Sopenharmony_ci	 * Since we will not be able to trap kernel-mode NEON to force
7362306a36Sopenharmony_ci	 * migration to CPU0, just do not advertise VFP support at all.
7462306a36Sopenharmony_ci	 *
7562306a36Sopenharmony_ci	 * This will make vfp_init bail out and do not attempt to use VFP at
7662306a36Sopenharmony_ci	 * all, for kernel-mode NEON, we do not want to introduce any
7762306a36Sopenharmony_ci	 * conditionals in hot-paths, so we just restrict the system to UP.
7862306a36Sopenharmony_ci	 */
7962306a36Sopenharmony_ci#ifdef CONFIG_VFP
8062306a36Sopenharmony_ci	if (ncores > 1) {
8162306a36Sopenharmony_ci		pr_warn("SMP: secondary CPUs lack VFP unit, disabling VFP\n");
8262306a36Sopenharmony_ci		vfp_disable();
8362306a36Sopenharmony_ci
8462306a36Sopenharmony_ci#ifdef CONFIG_KERNEL_MODE_NEON
8562306a36Sopenharmony_ci		WARN(1, "SMP: kernel-mode NEON enabled, restricting to UP\n");
8662306a36Sopenharmony_ci		ncores = 1;
8762306a36Sopenharmony_ci#endif
8862306a36Sopenharmony_ci	}
8962306a36Sopenharmony_ci#endif
9062306a36Sopenharmony_ci
9162306a36Sopenharmony_ci	for (i = 0; i < ncores; i++)
9262306a36Sopenharmony_ci		set_cpu_possible(i, true);
9362306a36Sopenharmony_ci
9462306a36Sopenharmony_ci	iounmap(scu_base);	/* That's the last we'll need of this */
9562306a36Sopenharmony_ci
9662306a36Sopenharmony_ci	return 0;
9762306a36Sopenharmony_ci}
9862306a36Sopenharmony_ci
9962306a36Sopenharmony_cistatic const struct of_device_id bcm63138_bootlut_ids[] = {
10062306a36Sopenharmony_ci	{ .compatible = "brcm,bcm63138-bootlut", },
10162306a36Sopenharmony_ci	{ /* sentinel */ },
10262306a36Sopenharmony_ci};
10362306a36Sopenharmony_ci
10462306a36Sopenharmony_ci#define BOOTLUT_RESET_VECT	0x20
10562306a36Sopenharmony_ci
10662306a36Sopenharmony_cistatic int bcm63138_smp_boot_secondary(unsigned int cpu,
10762306a36Sopenharmony_ci				       struct task_struct *idle)
10862306a36Sopenharmony_ci{
10962306a36Sopenharmony_ci	void __iomem *bootlut_base;
11062306a36Sopenharmony_ci	struct device_node *dn;
11162306a36Sopenharmony_ci	int ret = 0;
11262306a36Sopenharmony_ci	u32 val;
11362306a36Sopenharmony_ci
11462306a36Sopenharmony_ci	dn = of_find_matching_node(NULL, bcm63138_bootlut_ids);
11562306a36Sopenharmony_ci	if (!dn) {
11662306a36Sopenharmony_ci		pr_err("SMP: unable to find bcm63138 boot LUT node\n");
11762306a36Sopenharmony_ci		return -ENODEV;
11862306a36Sopenharmony_ci	}
11962306a36Sopenharmony_ci
12062306a36Sopenharmony_ci	bootlut_base = of_iomap(dn, 0);
12162306a36Sopenharmony_ci	of_node_put(dn);
12262306a36Sopenharmony_ci
12362306a36Sopenharmony_ci	if (!bootlut_base) {
12462306a36Sopenharmony_ci		pr_err("SMP: unable to remap boot LUT base register\n");
12562306a36Sopenharmony_ci		return -ENOMEM;
12662306a36Sopenharmony_ci	}
12762306a36Sopenharmony_ci
12862306a36Sopenharmony_ci	/* Locate the secondary CPU node */
12962306a36Sopenharmony_ci	dn = of_get_cpu_node(cpu, NULL);
13062306a36Sopenharmony_ci	if (!dn) {
13162306a36Sopenharmony_ci		pr_err("SMP: failed to locate secondary CPU%d node\n", cpu);
13262306a36Sopenharmony_ci		ret = -ENODEV;
13362306a36Sopenharmony_ci		goto out;
13462306a36Sopenharmony_ci	}
13562306a36Sopenharmony_ci
13662306a36Sopenharmony_ci	/* Write the secondary init routine to the BootLUT reset vector */
13762306a36Sopenharmony_ci	val = __pa_symbol(secondary_startup);
13862306a36Sopenharmony_ci	writel_relaxed(val, bootlut_base + BOOTLUT_RESET_VECT);
13962306a36Sopenharmony_ci
14062306a36Sopenharmony_ci	/* Power up the core, will jump straight to its reset vector when we
14162306a36Sopenharmony_ci	 * return
14262306a36Sopenharmony_ci	 */
14362306a36Sopenharmony_ci	ret = bcm63xx_pmb_power_on_cpu(dn);
14462306a36Sopenharmony_ci	of_node_put(dn);
14562306a36Sopenharmony_ci
14662306a36Sopenharmony_ciout:
14762306a36Sopenharmony_ci	iounmap(bootlut_base);
14862306a36Sopenharmony_ci
14962306a36Sopenharmony_ci	return ret;
15062306a36Sopenharmony_ci}
15162306a36Sopenharmony_ci
15262306a36Sopenharmony_cistatic void __init bcm63138_smp_prepare_cpus(unsigned int max_cpus)
15362306a36Sopenharmony_ci{
15462306a36Sopenharmony_ci	int ret;
15562306a36Sopenharmony_ci
15662306a36Sopenharmony_ci	ret = scu_a9_enable();
15762306a36Sopenharmony_ci	if (ret) {
15862306a36Sopenharmony_ci		pr_warn("SMP: Cortex-A9 SCU setup failed\n");
15962306a36Sopenharmony_ci		return;
16062306a36Sopenharmony_ci	}
16162306a36Sopenharmony_ci}
16262306a36Sopenharmony_ci
16362306a36Sopenharmony_cistatic const struct smp_operations bcm63138_smp_ops __initconst = {
16462306a36Sopenharmony_ci	.smp_prepare_cpus	= bcm63138_smp_prepare_cpus,
16562306a36Sopenharmony_ci	.smp_boot_secondary	= bcm63138_smp_boot_secondary,
16662306a36Sopenharmony_ci};
16762306a36Sopenharmony_ci
16862306a36Sopenharmony_ciCPU_METHOD_OF_DECLARE(bcm63138_smp, "brcm,bcm63138", &bcm63138_smp_ops);
169