18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * Broadcom BCM63138 DSL SoCs SMP support code
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * Copyright (C) 2015, Broadcom Corporation
68c2ecf20Sopenharmony_ci */
78c2ecf20Sopenharmony_ci
88c2ecf20Sopenharmony_ci#include <linux/delay.h>
98c2ecf20Sopenharmony_ci#include <linux/init.h>
108c2ecf20Sopenharmony_ci#include <linux/smp.h>
118c2ecf20Sopenharmony_ci#include <linux/io.h>
128c2ecf20Sopenharmony_ci#include <linux/of.h>
138c2ecf20Sopenharmony_ci#include <linux/of_address.h>
148c2ecf20Sopenharmony_ci
158c2ecf20Sopenharmony_ci#include <asm/cacheflush.h>
168c2ecf20Sopenharmony_ci#include <asm/smp_scu.h>
178c2ecf20Sopenharmony_ci#include <asm/smp_plat.h>
188c2ecf20Sopenharmony_ci#include <asm/vfp.h>
198c2ecf20Sopenharmony_ci
208c2ecf20Sopenharmony_ci#include "bcm63xx_smp.h"
218c2ecf20Sopenharmony_ci
228c2ecf20Sopenharmony_ci/* Size of mapped Cortex A9 SCU address space */
238c2ecf20Sopenharmony_ci#define CORTEX_A9_SCU_SIZE	0x58
248c2ecf20Sopenharmony_ci
258c2ecf20Sopenharmony_ci/*
268c2ecf20Sopenharmony_ci * Enable the Cortex A9 Snoop Control Unit
278c2ecf20Sopenharmony_ci *
288c2ecf20Sopenharmony_ci * By the time this is called we already know there are multiple
298c2ecf20Sopenharmony_ci * cores present.  We assume we're running on a Cortex A9 processor,
308c2ecf20Sopenharmony_ci * so any trouble getting the base address register or getting the
318c2ecf20Sopenharmony_ci * SCU base is a problem.
328c2ecf20Sopenharmony_ci *
338c2ecf20Sopenharmony_ci * Return 0 if successful or an error code otherwise.
348c2ecf20Sopenharmony_ci */
358c2ecf20Sopenharmony_cistatic int __init scu_a9_enable(void)
368c2ecf20Sopenharmony_ci{
378c2ecf20Sopenharmony_ci	unsigned long config_base;
388c2ecf20Sopenharmony_ci	void __iomem *scu_base;
398c2ecf20Sopenharmony_ci	unsigned int i, ncores;
408c2ecf20Sopenharmony_ci
418c2ecf20Sopenharmony_ci	if (!scu_a9_has_base()) {
428c2ecf20Sopenharmony_ci		pr_err("no configuration base address register!\n");
438c2ecf20Sopenharmony_ci		return -ENXIO;
448c2ecf20Sopenharmony_ci	}
458c2ecf20Sopenharmony_ci
468c2ecf20Sopenharmony_ci	/* Config base address register value is zero for uniprocessor */
478c2ecf20Sopenharmony_ci	config_base = scu_a9_get_base();
488c2ecf20Sopenharmony_ci	if (!config_base) {
498c2ecf20Sopenharmony_ci		pr_err("hardware reports only one core\n");
508c2ecf20Sopenharmony_ci		return -ENOENT;
518c2ecf20Sopenharmony_ci	}
528c2ecf20Sopenharmony_ci
538c2ecf20Sopenharmony_ci	scu_base = ioremap((phys_addr_t)config_base, CORTEX_A9_SCU_SIZE);
548c2ecf20Sopenharmony_ci	if (!scu_base) {
558c2ecf20Sopenharmony_ci		pr_err("failed to remap config base (%lu/%u) for SCU\n",
568c2ecf20Sopenharmony_ci			config_base, CORTEX_A9_SCU_SIZE);
578c2ecf20Sopenharmony_ci		return -ENOMEM;
588c2ecf20Sopenharmony_ci	}
598c2ecf20Sopenharmony_ci
608c2ecf20Sopenharmony_ci	scu_enable(scu_base);
618c2ecf20Sopenharmony_ci
628c2ecf20Sopenharmony_ci	ncores = scu_base ? scu_get_core_count(scu_base) : 1;
638c2ecf20Sopenharmony_ci
648c2ecf20Sopenharmony_ci	if (ncores > nr_cpu_ids) {
658c2ecf20Sopenharmony_ci		pr_warn("SMP: %u cores greater than maximum (%u), clipping\n",
668c2ecf20Sopenharmony_ci				ncores, nr_cpu_ids);
678c2ecf20Sopenharmony_ci		ncores = nr_cpu_ids;
688c2ecf20Sopenharmony_ci	}
698c2ecf20Sopenharmony_ci
708c2ecf20Sopenharmony_ci	/* The BCM63138 SoC has two Cortex-A9 CPUs, CPU0 features a complete
718c2ecf20Sopenharmony_ci	 * and fully functional VFP unit that can be used, but CPU1 does not.
728c2ecf20Sopenharmony_ci	 * Since we will not be able to trap kernel-mode NEON to force
738c2ecf20Sopenharmony_ci	 * migration to CPU0, just do not advertise VFP support at all.
748c2ecf20Sopenharmony_ci	 *
758c2ecf20Sopenharmony_ci	 * This will make vfp_init bail out and do not attempt to use VFP at
768c2ecf20Sopenharmony_ci	 * all, for kernel-mode NEON, we do not want to introduce any
778c2ecf20Sopenharmony_ci	 * conditionals in hot-paths, so we just restrict the system to UP.
788c2ecf20Sopenharmony_ci	 */
798c2ecf20Sopenharmony_ci#ifdef CONFIG_VFP
808c2ecf20Sopenharmony_ci	if (ncores > 1) {
818c2ecf20Sopenharmony_ci		pr_warn("SMP: secondary CPUs lack VFP unit, disabling VFP\n");
828c2ecf20Sopenharmony_ci		vfp_disable();
838c2ecf20Sopenharmony_ci
848c2ecf20Sopenharmony_ci#ifdef CONFIG_KERNEL_MODE_NEON
858c2ecf20Sopenharmony_ci		WARN(1, "SMP: kernel-mode NEON enabled, restricting to UP\n");
868c2ecf20Sopenharmony_ci		ncores = 1;
878c2ecf20Sopenharmony_ci#endif
888c2ecf20Sopenharmony_ci	}
898c2ecf20Sopenharmony_ci#endif
908c2ecf20Sopenharmony_ci
918c2ecf20Sopenharmony_ci	for (i = 0; i < ncores; i++)
928c2ecf20Sopenharmony_ci		set_cpu_possible(i, true);
938c2ecf20Sopenharmony_ci
948c2ecf20Sopenharmony_ci	iounmap(scu_base);	/* That's the last we'll need of this */
958c2ecf20Sopenharmony_ci
968c2ecf20Sopenharmony_ci	return 0;
978c2ecf20Sopenharmony_ci}
988c2ecf20Sopenharmony_ci
998c2ecf20Sopenharmony_cistatic const struct of_device_id bcm63138_bootlut_ids[] = {
1008c2ecf20Sopenharmony_ci	{ .compatible = "brcm,bcm63138-bootlut", },
1018c2ecf20Sopenharmony_ci	{ /* sentinel */ },
1028c2ecf20Sopenharmony_ci};
1038c2ecf20Sopenharmony_ci
1048c2ecf20Sopenharmony_ci#define BOOTLUT_RESET_VECT	0x20
1058c2ecf20Sopenharmony_ci
1068c2ecf20Sopenharmony_cistatic int bcm63138_smp_boot_secondary(unsigned int cpu,
1078c2ecf20Sopenharmony_ci				       struct task_struct *idle)
1088c2ecf20Sopenharmony_ci{
1098c2ecf20Sopenharmony_ci	void __iomem *bootlut_base;
1108c2ecf20Sopenharmony_ci	struct device_node *dn;
1118c2ecf20Sopenharmony_ci	int ret = 0;
1128c2ecf20Sopenharmony_ci	u32 val;
1138c2ecf20Sopenharmony_ci
1148c2ecf20Sopenharmony_ci	dn = of_find_matching_node(NULL, bcm63138_bootlut_ids);
1158c2ecf20Sopenharmony_ci	if (!dn) {
1168c2ecf20Sopenharmony_ci		pr_err("SMP: unable to find bcm63138 boot LUT node\n");
1178c2ecf20Sopenharmony_ci		return -ENODEV;
1188c2ecf20Sopenharmony_ci	}
1198c2ecf20Sopenharmony_ci
1208c2ecf20Sopenharmony_ci	bootlut_base = of_iomap(dn, 0);
1218c2ecf20Sopenharmony_ci	of_node_put(dn);
1228c2ecf20Sopenharmony_ci
1238c2ecf20Sopenharmony_ci	if (!bootlut_base) {
1248c2ecf20Sopenharmony_ci		pr_err("SMP: unable to remap boot LUT base register\n");
1258c2ecf20Sopenharmony_ci		return -ENOMEM;
1268c2ecf20Sopenharmony_ci	}
1278c2ecf20Sopenharmony_ci
1288c2ecf20Sopenharmony_ci	/* Locate the secondary CPU node */
1298c2ecf20Sopenharmony_ci	dn = of_get_cpu_node(cpu, NULL);
1308c2ecf20Sopenharmony_ci	if (!dn) {
1318c2ecf20Sopenharmony_ci		pr_err("SMP: failed to locate secondary CPU%d node\n", cpu);
1328c2ecf20Sopenharmony_ci		ret = -ENODEV;
1338c2ecf20Sopenharmony_ci		goto out;
1348c2ecf20Sopenharmony_ci	}
1358c2ecf20Sopenharmony_ci
1368c2ecf20Sopenharmony_ci	/* Write the secondary init routine to the BootLUT reset vector */
1378c2ecf20Sopenharmony_ci	val = __pa_symbol(secondary_startup);
1388c2ecf20Sopenharmony_ci	writel_relaxed(val, bootlut_base + BOOTLUT_RESET_VECT);
1398c2ecf20Sopenharmony_ci
1408c2ecf20Sopenharmony_ci	/* Power up the core, will jump straight to its reset vector when we
1418c2ecf20Sopenharmony_ci	 * return
1428c2ecf20Sopenharmony_ci	 */
1438c2ecf20Sopenharmony_ci	ret = bcm63xx_pmb_power_on_cpu(dn);
1448c2ecf20Sopenharmony_ci	of_node_put(dn);
1458c2ecf20Sopenharmony_ci	if (ret)
1468c2ecf20Sopenharmony_ci		goto out;
1478c2ecf20Sopenharmony_ciout:
1488c2ecf20Sopenharmony_ci	iounmap(bootlut_base);
1498c2ecf20Sopenharmony_ci
1508c2ecf20Sopenharmony_ci	return ret;
1518c2ecf20Sopenharmony_ci}
1528c2ecf20Sopenharmony_ci
1538c2ecf20Sopenharmony_cistatic void __init bcm63138_smp_prepare_cpus(unsigned int max_cpus)
1548c2ecf20Sopenharmony_ci{
1558c2ecf20Sopenharmony_ci	int ret;
1568c2ecf20Sopenharmony_ci
1578c2ecf20Sopenharmony_ci	ret = scu_a9_enable();
1588c2ecf20Sopenharmony_ci	if (ret) {
1598c2ecf20Sopenharmony_ci		pr_warn("SMP: Cortex-A9 SCU setup failed\n");
1608c2ecf20Sopenharmony_ci		return;
1618c2ecf20Sopenharmony_ci	}
1628c2ecf20Sopenharmony_ci}
1638c2ecf20Sopenharmony_ci
1648c2ecf20Sopenharmony_cistatic const struct smp_operations bcm63138_smp_ops __initconst = {
1658c2ecf20Sopenharmony_ci	.smp_prepare_cpus	= bcm63138_smp_prepare_cpus,
1668c2ecf20Sopenharmony_ci	.smp_boot_secondary	= bcm63138_smp_boot_secondary,
1678c2ecf20Sopenharmony_ci};
1688c2ecf20Sopenharmony_ci
1698c2ecf20Sopenharmony_ciCPU_METHOD_OF_DECLARE(bcm63138_smp, "brcm,bcm63138", &bcm63138_smp_ops);
170