162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Copyright (C) 2014-2015 Broadcom Corporation
462306a36Sopenharmony_ci * Copyright 2014 Linaro Limited
562306a36Sopenharmony_ci */
662306a36Sopenharmony_ci
762306a36Sopenharmony_ci#include <linux/cpumask.h>
862306a36Sopenharmony_ci#include <linux/delay.h>
962306a36Sopenharmony_ci#include <linux/errno.h>
1062306a36Sopenharmony_ci#include <linux/init.h>
1162306a36Sopenharmony_ci#include <linux/io.h>
1262306a36Sopenharmony_ci#include <linux/irqchip/irq-bcm2836.h>
1362306a36Sopenharmony_ci#include <linux/jiffies.h>
1462306a36Sopenharmony_ci#include <linux/of.h>
1562306a36Sopenharmony_ci#include <linux/of_address.h>
1662306a36Sopenharmony_ci#include <linux/sched.h>
1762306a36Sopenharmony_ci#include <linux/sched/clock.h>
1862306a36Sopenharmony_ci#include <linux/smp.h>
1962306a36Sopenharmony_ci
2062306a36Sopenharmony_ci#include <asm/cacheflush.h>
2162306a36Sopenharmony_ci#include <asm/smp.h>
2262306a36Sopenharmony_ci#include <asm/smp_plat.h>
2362306a36Sopenharmony_ci#include <asm/smp_scu.h>
2462306a36Sopenharmony_ci
2562306a36Sopenharmony_ci#include "platsmp.h"
2662306a36Sopenharmony_ci
2762306a36Sopenharmony_ci/* Size of mapped Cortex A9 SCU address space */
2862306a36Sopenharmony_ci#define CORTEX_A9_SCU_SIZE	0x58
2962306a36Sopenharmony_ci
3062306a36Sopenharmony_ci#define SECONDARY_TIMEOUT_NS	NSEC_PER_MSEC	/* 1 msec (in nanoseconds) */
3162306a36Sopenharmony_ci#define BOOT_ADDR_CPUID_MASK	0x3
3262306a36Sopenharmony_ci
3362306a36Sopenharmony_ci/* Name of device node property defining secondary boot register location */
3462306a36Sopenharmony_ci#define OF_SECONDARY_BOOT	"secondary-boot-reg"
3562306a36Sopenharmony_ci#define MPIDR_CPUID_BITMASK	0x3
3662306a36Sopenharmony_ci
3762306a36Sopenharmony_ci/*
3862306a36Sopenharmony_ci * Enable the Cortex A9 Snoop Control Unit
3962306a36Sopenharmony_ci *
4062306a36Sopenharmony_ci * By the time this is called we already know there are multiple
4162306a36Sopenharmony_ci * cores present.  We assume we're running on a Cortex A9 processor,
4262306a36Sopenharmony_ci * so any trouble getting the base address register or getting the
4362306a36Sopenharmony_ci * SCU base is a problem.
4462306a36Sopenharmony_ci *
4562306a36Sopenharmony_ci * Return 0 if successful or an error code otherwise.
4662306a36Sopenharmony_ci */
4762306a36Sopenharmony_cistatic int __init scu_a9_enable(void)
4862306a36Sopenharmony_ci{
4962306a36Sopenharmony_ci	unsigned long config_base;
5062306a36Sopenharmony_ci	void __iomem *scu_base;
5162306a36Sopenharmony_ci
5262306a36Sopenharmony_ci	if (!scu_a9_has_base()) {
5362306a36Sopenharmony_ci		pr_err("no configuration base address register!\n");
5462306a36Sopenharmony_ci		return -ENXIO;
5562306a36Sopenharmony_ci	}
5662306a36Sopenharmony_ci
5762306a36Sopenharmony_ci	/* Config base address register value is zero for uniprocessor */
5862306a36Sopenharmony_ci	config_base = scu_a9_get_base();
5962306a36Sopenharmony_ci	if (!config_base) {
6062306a36Sopenharmony_ci		pr_err("hardware reports only one core\n");
6162306a36Sopenharmony_ci		return -ENOENT;
6262306a36Sopenharmony_ci	}
6362306a36Sopenharmony_ci
6462306a36Sopenharmony_ci	scu_base = ioremap((phys_addr_t)config_base, CORTEX_A9_SCU_SIZE);
6562306a36Sopenharmony_ci	if (!scu_base) {
6662306a36Sopenharmony_ci		pr_err("failed to remap config base (%lu/%u) for SCU\n",
6762306a36Sopenharmony_ci			config_base, CORTEX_A9_SCU_SIZE);
6862306a36Sopenharmony_ci		return -ENOMEM;
6962306a36Sopenharmony_ci	}
7062306a36Sopenharmony_ci
7162306a36Sopenharmony_ci	scu_enable(scu_base);
7262306a36Sopenharmony_ci
7362306a36Sopenharmony_ci	iounmap(scu_base);	/* That's the last we'll need of this */
7462306a36Sopenharmony_ci
7562306a36Sopenharmony_ci	return 0;
7662306a36Sopenharmony_ci}
7762306a36Sopenharmony_ci
7862306a36Sopenharmony_cistatic u32 secondary_boot_addr_for(unsigned int cpu)
7962306a36Sopenharmony_ci{
8062306a36Sopenharmony_ci	u32 secondary_boot_addr = 0;
8162306a36Sopenharmony_ci	struct device_node *cpu_node = of_get_cpu_node(cpu, NULL);
8262306a36Sopenharmony_ci
8362306a36Sopenharmony_ci        if (!cpu_node) {
8462306a36Sopenharmony_ci		pr_err("Failed to find device tree node for CPU%u\n", cpu);
8562306a36Sopenharmony_ci		return 0;
8662306a36Sopenharmony_ci	}
8762306a36Sopenharmony_ci
8862306a36Sopenharmony_ci	if (of_property_read_u32(cpu_node,
8962306a36Sopenharmony_ci				 OF_SECONDARY_BOOT,
9062306a36Sopenharmony_ci				 &secondary_boot_addr))
9162306a36Sopenharmony_ci		pr_err("required secondary boot register not specified for CPU%u\n",
9262306a36Sopenharmony_ci			cpu);
9362306a36Sopenharmony_ci
9462306a36Sopenharmony_ci	of_node_put(cpu_node);
9562306a36Sopenharmony_ci
9662306a36Sopenharmony_ci	return secondary_boot_addr;
9762306a36Sopenharmony_ci}
9862306a36Sopenharmony_ci
9962306a36Sopenharmony_cistatic int nsp_write_lut(unsigned int cpu)
10062306a36Sopenharmony_ci{
10162306a36Sopenharmony_ci	void __iomem *sku_rom_lut;
10262306a36Sopenharmony_ci	phys_addr_t secondary_startup_phy;
10362306a36Sopenharmony_ci	const u32 secondary_boot_addr = secondary_boot_addr_for(cpu);
10462306a36Sopenharmony_ci
10562306a36Sopenharmony_ci	if (!secondary_boot_addr)
10662306a36Sopenharmony_ci		return -EINVAL;
10762306a36Sopenharmony_ci
10862306a36Sopenharmony_ci	sku_rom_lut = ioremap((phys_addr_t)secondary_boot_addr,
10962306a36Sopenharmony_ci				      sizeof(phys_addr_t));
11062306a36Sopenharmony_ci	if (!sku_rom_lut) {
11162306a36Sopenharmony_ci		pr_warn("unable to ioremap SKU-ROM LUT register for cpu %u\n", cpu);
11262306a36Sopenharmony_ci		return -ENOMEM;
11362306a36Sopenharmony_ci	}
11462306a36Sopenharmony_ci
11562306a36Sopenharmony_ci	secondary_startup_phy = __pa_symbol(secondary_startup);
11662306a36Sopenharmony_ci	BUG_ON(secondary_startup_phy > (phys_addr_t)U32_MAX);
11762306a36Sopenharmony_ci
11862306a36Sopenharmony_ci	writel_relaxed(secondary_startup_phy, sku_rom_lut);
11962306a36Sopenharmony_ci
12062306a36Sopenharmony_ci	/* Ensure the write is visible to the secondary core */
12162306a36Sopenharmony_ci	smp_wmb();
12262306a36Sopenharmony_ci
12362306a36Sopenharmony_ci	iounmap(sku_rom_lut);
12462306a36Sopenharmony_ci
12562306a36Sopenharmony_ci	return 0;
12662306a36Sopenharmony_ci}
12762306a36Sopenharmony_ci
12862306a36Sopenharmony_cistatic void __init bcm_smp_prepare_cpus(unsigned int max_cpus)
12962306a36Sopenharmony_ci{
13062306a36Sopenharmony_ci	const cpumask_t only_cpu_0 = { CPU_BITS_CPU0 };
13162306a36Sopenharmony_ci
13262306a36Sopenharmony_ci	/* Enable the SCU on Cortex A9 based SoCs */
13362306a36Sopenharmony_ci	if (scu_a9_enable()) {
13462306a36Sopenharmony_ci		/* Update the CPU present map to reflect uniprocessor mode */
13562306a36Sopenharmony_ci		pr_warn("failed to enable A9 SCU - disabling SMP\n");
13662306a36Sopenharmony_ci		init_cpu_present(&only_cpu_0);
13762306a36Sopenharmony_ci	}
13862306a36Sopenharmony_ci}
13962306a36Sopenharmony_ci
14062306a36Sopenharmony_ci/*
14162306a36Sopenharmony_ci * The ROM code has the secondary cores looping, waiting for an event.
14262306a36Sopenharmony_ci * When an event occurs each core examines the bottom two bits of the
14362306a36Sopenharmony_ci * secondary boot register.  When a core finds those bits contain its
14462306a36Sopenharmony_ci * own core id, it performs initialization, including computing its boot
14562306a36Sopenharmony_ci * address by clearing the boot register value's bottom two bits.  The
14662306a36Sopenharmony_ci * core signals that it is beginning its execution by writing its boot
14762306a36Sopenharmony_ci * address back to the secondary boot register, and finally jumps to
14862306a36Sopenharmony_ci * that address.
14962306a36Sopenharmony_ci *
15062306a36Sopenharmony_ci * So to start a core executing we need to:
15162306a36Sopenharmony_ci * - Encode the (hardware) CPU id with the bottom bits of the secondary
15262306a36Sopenharmony_ci *   start address.
15362306a36Sopenharmony_ci * - Write that value into the secondary boot register.
15462306a36Sopenharmony_ci * - Generate an event to wake up the secondary CPU(s).
15562306a36Sopenharmony_ci * - Wait for the secondary boot register to be re-written, which
15662306a36Sopenharmony_ci *   indicates the secondary core has started.
15762306a36Sopenharmony_ci */
15862306a36Sopenharmony_cistatic int kona_boot_secondary(unsigned int cpu, struct task_struct *idle)
15962306a36Sopenharmony_ci{
16062306a36Sopenharmony_ci	void __iomem *boot_reg;
16162306a36Sopenharmony_ci	phys_addr_t boot_func;
16262306a36Sopenharmony_ci	u64 start_clock;
16362306a36Sopenharmony_ci	u32 cpu_id;
16462306a36Sopenharmony_ci	u32 boot_val;
16562306a36Sopenharmony_ci	bool timeout = false;
16662306a36Sopenharmony_ci	const u32 secondary_boot_addr = secondary_boot_addr_for(cpu);
16762306a36Sopenharmony_ci
16862306a36Sopenharmony_ci	cpu_id = cpu_logical_map(cpu);
16962306a36Sopenharmony_ci	if (cpu_id & ~BOOT_ADDR_CPUID_MASK) {
17062306a36Sopenharmony_ci		pr_err("bad cpu id (%u > %u)\n", cpu_id, BOOT_ADDR_CPUID_MASK);
17162306a36Sopenharmony_ci		return -EINVAL;
17262306a36Sopenharmony_ci	}
17362306a36Sopenharmony_ci
17462306a36Sopenharmony_ci	if (!secondary_boot_addr)
17562306a36Sopenharmony_ci		return -EINVAL;
17662306a36Sopenharmony_ci
17762306a36Sopenharmony_ci	boot_reg = ioremap((phys_addr_t)secondary_boot_addr,
17862306a36Sopenharmony_ci				   sizeof(phys_addr_t));
17962306a36Sopenharmony_ci	if (!boot_reg) {
18062306a36Sopenharmony_ci		pr_err("unable to map boot register for cpu %u\n", cpu_id);
18162306a36Sopenharmony_ci		return -ENOMEM;
18262306a36Sopenharmony_ci	}
18362306a36Sopenharmony_ci
18462306a36Sopenharmony_ci	/*
18562306a36Sopenharmony_ci	 * Secondary cores will start in secondary_startup(),
18662306a36Sopenharmony_ci	 * defined in "arch/arm/kernel/head.S"
18762306a36Sopenharmony_ci	 */
18862306a36Sopenharmony_ci	boot_func = __pa_symbol(secondary_startup);
18962306a36Sopenharmony_ci	BUG_ON(boot_func & BOOT_ADDR_CPUID_MASK);
19062306a36Sopenharmony_ci	BUG_ON(boot_func > (phys_addr_t)U32_MAX);
19162306a36Sopenharmony_ci
19262306a36Sopenharmony_ci	/* The core to start is encoded in the low bits */
19362306a36Sopenharmony_ci	boot_val = (u32)boot_func | cpu_id;
19462306a36Sopenharmony_ci	writel_relaxed(boot_val, boot_reg);
19562306a36Sopenharmony_ci
19662306a36Sopenharmony_ci	sev();
19762306a36Sopenharmony_ci
19862306a36Sopenharmony_ci	/* The low bits will be cleared once the core has started */
19962306a36Sopenharmony_ci	start_clock = local_clock();
20062306a36Sopenharmony_ci	while (!timeout && readl_relaxed(boot_reg) == boot_val)
20162306a36Sopenharmony_ci		timeout = local_clock() - start_clock > SECONDARY_TIMEOUT_NS;
20262306a36Sopenharmony_ci
20362306a36Sopenharmony_ci	iounmap(boot_reg);
20462306a36Sopenharmony_ci
20562306a36Sopenharmony_ci	if (!timeout)
20662306a36Sopenharmony_ci		return 0;
20762306a36Sopenharmony_ci
20862306a36Sopenharmony_ci	pr_err("timeout waiting for cpu %u to start\n", cpu_id);
20962306a36Sopenharmony_ci
21062306a36Sopenharmony_ci	return -ENXIO;
21162306a36Sopenharmony_ci}
21262306a36Sopenharmony_ci
21362306a36Sopenharmony_ci/* Cluster Dormant Control command to bring CPU into a running state */
21462306a36Sopenharmony_ci#define CDC_CMD			6
21562306a36Sopenharmony_ci#define CDC_CMD_OFFSET		0
21662306a36Sopenharmony_ci#define CDC_CMD_REG(cpu)	(CDC_CMD_OFFSET + 4*(cpu))
21762306a36Sopenharmony_ci
21862306a36Sopenharmony_ci/*
21962306a36Sopenharmony_ci * BCM23550 has a Cluster Dormant Control block that keeps the core in
22062306a36Sopenharmony_ci * idle state. A command needs to be sent to the block to bring the CPU
22162306a36Sopenharmony_ci * into running state.
22262306a36Sopenharmony_ci */
22362306a36Sopenharmony_cistatic int bcm23550_boot_secondary(unsigned int cpu, struct task_struct *idle)
22462306a36Sopenharmony_ci{
22562306a36Sopenharmony_ci	void __iomem *cdc_base;
22662306a36Sopenharmony_ci	struct device_node *dn;
22762306a36Sopenharmony_ci	char *name;
22862306a36Sopenharmony_ci	int ret;
22962306a36Sopenharmony_ci
23062306a36Sopenharmony_ci	/* Make sure a CDC node exists before booting the
23162306a36Sopenharmony_ci	 * secondary core.
23262306a36Sopenharmony_ci	 */
23362306a36Sopenharmony_ci	name = "brcm,bcm23550-cdc";
23462306a36Sopenharmony_ci	dn = of_find_compatible_node(NULL, NULL, name);
23562306a36Sopenharmony_ci	if (!dn) {
23662306a36Sopenharmony_ci		pr_err("unable to find cdc node\n");
23762306a36Sopenharmony_ci		return -ENODEV;
23862306a36Sopenharmony_ci	}
23962306a36Sopenharmony_ci
24062306a36Sopenharmony_ci	cdc_base = of_iomap(dn, 0);
24162306a36Sopenharmony_ci	of_node_put(dn);
24262306a36Sopenharmony_ci
24362306a36Sopenharmony_ci	if (!cdc_base) {
24462306a36Sopenharmony_ci		pr_err("unable to remap cdc base register\n");
24562306a36Sopenharmony_ci		return -ENOMEM;
24662306a36Sopenharmony_ci	}
24762306a36Sopenharmony_ci
24862306a36Sopenharmony_ci	/* Boot the secondary core */
24962306a36Sopenharmony_ci	ret = kona_boot_secondary(cpu, idle);
25062306a36Sopenharmony_ci	if (ret)
25162306a36Sopenharmony_ci		goto out;
25262306a36Sopenharmony_ci
25362306a36Sopenharmony_ci	/* Bring this CPU to RUN state so that nIRQ nFIQ
25462306a36Sopenharmony_ci	 * signals are unblocked.
25562306a36Sopenharmony_ci	 */
25662306a36Sopenharmony_ci	writel_relaxed(CDC_CMD, cdc_base + CDC_CMD_REG(cpu));
25762306a36Sopenharmony_ci
25862306a36Sopenharmony_ciout:
25962306a36Sopenharmony_ci	iounmap(cdc_base);
26062306a36Sopenharmony_ci
26162306a36Sopenharmony_ci	return ret;
26262306a36Sopenharmony_ci}
26362306a36Sopenharmony_ci
26462306a36Sopenharmony_cistatic int nsp_boot_secondary(unsigned int cpu, struct task_struct *idle)
26562306a36Sopenharmony_ci{
26662306a36Sopenharmony_ci	int ret;
26762306a36Sopenharmony_ci
26862306a36Sopenharmony_ci	/*
26962306a36Sopenharmony_ci	 * After wake up, secondary core branches to the startup
27062306a36Sopenharmony_ci	 * address programmed at SKU ROM LUT location.
27162306a36Sopenharmony_ci	 */
27262306a36Sopenharmony_ci	ret = nsp_write_lut(cpu);
27362306a36Sopenharmony_ci	if (ret) {
27462306a36Sopenharmony_ci		pr_err("unable to write startup addr to SKU ROM LUT\n");
27562306a36Sopenharmony_ci		goto out;
27662306a36Sopenharmony_ci	}
27762306a36Sopenharmony_ci
27862306a36Sopenharmony_ci	/* Send a CPU wakeup interrupt to the secondary core */
27962306a36Sopenharmony_ci	arch_send_wakeup_ipi_mask(cpumask_of(cpu));
28062306a36Sopenharmony_ci
28162306a36Sopenharmony_ciout:
28262306a36Sopenharmony_ci	return ret;
28362306a36Sopenharmony_ci}
28462306a36Sopenharmony_ci
28562306a36Sopenharmony_cistatic int bcm2836_boot_secondary(unsigned int cpu, struct task_struct *idle)
28662306a36Sopenharmony_ci{
28762306a36Sopenharmony_ci	void __iomem *intc_base;
28862306a36Sopenharmony_ci	struct device_node *dn;
28962306a36Sopenharmony_ci	char *name;
29062306a36Sopenharmony_ci
29162306a36Sopenharmony_ci	name = "brcm,bcm2836-l1-intc";
29262306a36Sopenharmony_ci	dn = of_find_compatible_node(NULL, NULL, name);
29362306a36Sopenharmony_ci	if (!dn) {
29462306a36Sopenharmony_ci		pr_err("unable to find intc node\n");
29562306a36Sopenharmony_ci		return -ENODEV;
29662306a36Sopenharmony_ci	}
29762306a36Sopenharmony_ci
29862306a36Sopenharmony_ci	intc_base = of_iomap(dn, 0);
29962306a36Sopenharmony_ci	of_node_put(dn);
30062306a36Sopenharmony_ci
30162306a36Sopenharmony_ci	if (!intc_base) {
30262306a36Sopenharmony_ci		pr_err("unable to remap intc base register\n");
30362306a36Sopenharmony_ci		return -ENOMEM;
30462306a36Sopenharmony_ci	}
30562306a36Sopenharmony_ci
30662306a36Sopenharmony_ci	writel(virt_to_phys(secondary_startup),
30762306a36Sopenharmony_ci	       intc_base + LOCAL_MAILBOX3_SET0 + 16 * cpu);
30862306a36Sopenharmony_ci
30962306a36Sopenharmony_ci	dsb(sy);
31062306a36Sopenharmony_ci	sev();
31162306a36Sopenharmony_ci
31262306a36Sopenharmony_ci	iounmap(intc_base);
31362306a36Sopenharmony_ci
31462306a36Sopenharmony_ci	return 0;
31562306a36Sopenharmony_ci}
31662306a36Sopenharmony_ci
31762306a36Sopenharmony_cistatic const struct smp_operations kona_smp_ops __initconst = {
31862306a36Sopenharmony_ci	.smp_prepare_cpus	= bcm_smp_prepare_cpus,
31962306a36Sopenharmony_ci	.smp_boot_secondary	= kona_boot_secondary,
32062306a36Sopenharmony_ci};
32162306a36Sopenharmony_ciCPU_METHOD_OF_DECLARE(bcm_smp_bcm281xx, "brcm,bcm11351-cpu-method",
32262306a36Sopenharmony_ci			&kona_smp_ops);
32362306a36Sopenharmony_ci
32462306a36Sopenharmony_cistatic const struct smp_operations bcm23550_smp_ops __initconst = {
32562306a36Sopenharmony_ci	.smp_boot_secondary	= bcm23550_boot_secondary,
32662306a36Sopenharmony_ci};
32762306a36Sopenharmony_ciCPU_METHOD_OF_DECLARE(bcm_smp_bcm23550, "brcm,bcm23550",
32862306a36Sopenharmony_ci			&bcm23550_smp_ops);
32962306a36Sopenharmony_ci
33062306a36Sopenharmony_cistatic const struct smp_operations nsp_smp_ops __initconst = {
33162306a36Sopenharmony_ci	.smp_prepare_cpus	= bcm_smp_prepare_cpus,
33262306a36Sopenharmony_ci	.smp_boot_secondary	= nsp_boot_secondary,
33362306a36Sopenharmony_ci};
33462306a36Sopenharmony_ciCPU_METHOD_OF_DECLARE(bcm_smp_nsp, "brcm,bcm-nsp-smp", &nsp_smp_ops);
33562306a36Sopenharmony_ci
33662306a36Sopenharmony_ciconst struct smp_operations bcm2836_smp_ops __initconst = {
33762306a36Sopenharmony_ci	.smp_boot_secondary	= bcm2836_boot_secondary,
33862306a36Sopenharmony_ci};
33962306a36Sopenharmony_ciCPU_METHOD_OF_DECLARE(bcm_smp_bcm2836, "brcm,bcm2836-smp", &bcm2836_smp_ops);
340