162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Copyright 2010-2011 Calxeda, Inc.
462306a36Sopenharmony_ci * Copyright 2012 Pavel Machek <pavel@denx.de>
562306a36Sopenharmony_ci * Based on platsmp.c, Copyright (C) 2002 ARM Ltd.
662306a36Sopenharmony_ci * Copyright (C) 2012 Altera Corporation
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
1962306a36Sopenharmony_ci#include "core.h"
2062306a36Sopenharmony_ci
2162306a36Sopenharmony_cistatic int socfpga_boot_secondary(unsigned int cpu, struct task_struct *idle)
2262306a36Sopenharmony_ci{
2362306a36Sopenharmony_ci	int trampoline_size = secondary_trampoline_end - secondary_trampoline;
2462306a36Sopenharmony_ci
2562306a36Sopenharmony_ci	if (socfpga_cpu1start_addr) {
2662306a36Sopenharmony_ci		/* This will put CPU #1 into reset. */
2762306a36Sopenharmony_ci		writel(RSTMGR_MPUMODRST_CPU1,
2862306a36Sopenharmony_ci		       rst_manager_base_addr + SOCFPGA_RSTMGR_MODMPURST);
2962306a36Sopenharmony_ci
3062306a36Sopenharmony_ci		memcpy(phys_to_virt(0), secondary_trampoline, trampoline_size);
3162306a36Sopenharmony_ci
3262306a36Sopenharmony_ci		writel(__pa_symbol(secondary_startup),
3362306a36Sopenharmony_ci		       sys_manager_base_addr + (socfpga_cpu1start_addr & 0x000000ff));
3462306a36Sopenharmony_ci
3562306a36Sopenharmony_ci		flush_cache_all();
3662306a36Sopenharmony_ci		smp_wmb();
3762306a36Sopenharmony_ci		outer_clean_range(0, trampoline_size);
3862306a36Sopenharmony_ci
3962306a36Sopenharmony_ci		/* This will release CPU #1 out of reset. */
4062306a36Sopenharmony_ci		writel(0, rst_manager_base_addr + SOCFPGA_RSTMGR_MODMPURST);
4162306a36Sopenharmony_ci	}
4262306a36Sopenharmony_ci
4362306a36Sopenharmony_ci	return 0;
4462306a36Sopenharmony_ci}
4562306a36Sopenharmony_ci
4662306a36Sopenharmony_cistatic int socfpga_a10_boot_secondary(unsigned int cpu, struct task_struct *idle)
4762306a36Sopenharmony_ci{
4862306a36Sopenharmony_ci	int trampoline_size = secondary_trampoline_end - secondary_trampoline;
4962306a36Sopenharmony_ci
5062306a36Sopenharmony_ci	if (socfpga_cpu1start_addr) {
5162306a36Sopenharmony_ci		writel(RSTMGR_MPUMODRST_CPU1, rst_manager_base_addr +
5262306a36Sopenharmony_ci		       SOCFPGA_A10_RSTMGR_MODMPURST);
5362306a36Sopenharmony_ci		memcpy(phys_to_virt(0), secondary_trampoline, trampoline_size);
5462306a36Sopenharmony_ci
5562306a36Sopenharmony_ci		writel(__pa_symbol(secondary_startup),
5662306a36Sopenharmony_ci		       sys_manager_base_addr + (socfpga_cpu1start_addr & 0x00000fff));
5762306a36Sopenharmony_ci
5862306a36Sopenharmony_ci		flush_cache_all();
5962306a36Sopenharmony_ci		smp_wmb();
6062306a36Sopenharmony_ci		outer_clean_range(0, trampoline_size);
6162306a36Sopenharmony_ci
6262306a36Sopenharmony_ci		/* This will release CPU #1 out of reset. */
6362306a36Sopenharmony_ci		writel(0, rst_manager_base_addr + SOCFPGA_A10_RSTMGR_MODMPURST);
6462306a36Sopenharmony_ci	}
6562306a36Sopenharmony_ci
6662306a36Sopenharmony_ci	return 0;
6762306a36Sopenharmony_ci}
6862306a36Sopenharmony_ci
6962306a36Sopenharmony_cistatic void __init socfpga_smp_prepare_cpus(unsigned int max_cpus)
7062306a36Sopenharmony_ci{
7162306a36Sopenharmony_ci	struct device_node *np;
7262306a36Sopenharmony_ci	void __iomem *socfpga_scu_base_addr;
7362306a36Sopenharmony_ci
7462306a36Sopenharmony_ci	np = of_find_compatible_node(NULL, NULL, "arm,cortex-a9-scu");
7562306a36Sopenharmony_ci	if (!np) {
7662306a36Sopenharmony_ci		pr_err("%s: missing scu\n", __func__);
7762306a36Sopenharmony_ci		return;
7862306a36Sopenharmony_ci	}
7962306a36Sopenharmony_ci
8062306a36Sopenharmony_ci	socfpga_scu_base_addr = of_iomap(np, 0);
8162306a36Sopenharmony_ci	if (!socfpga_scu_base_addr)
8262306a36Sopenharmony_ci		return;
8362306a36Sopenharmony_ci	scu_enable(socfpga_scu_base_addr);
8462306a36Sopenharmony_ci}
8562306a36Sopenharmony_ci
8662306a36Sopenharmony_ci#ifdef CONFIG_HOTPLUG_CPU
8762306a36Sopenharmony_ci/*
8862306a36Sopenharmony_ci * platform-specific code to shutdown a CPU
8962306a36Sopenharmony_ci *
9062306a36Sopenharmony_ci * Called with IRQs disabled
9162306a36Sopenharmony_ci */
9262306a36Sopenharmony_cistatic void socfpga_cpu_die(unsigned int cpu)
9362306a36Sopenharmony_ci{
9462306a36Sopenharmony_ci	/* Do WFI. If we wake up early, go back into WFI */
9562306a36Sopenharmony_ci	while (1)
9662306a36Sopenharmony_ci		cpu_do_idle();
9762306a36Sopenharmony_ci}
9862306a36Sopenharmony_ci
9962306a36Sopenharmony_ci/*
10062306a36Sopenharmony_ci * We need a dummy function so that platform_can_cpu_hotplug() knows
10162306a36Sopenharmony_ci * we support CPU hotplug. However, the function does not need to do
10262306a36Sopenharmony_ci * anything, because CPUs going offline just do WFI. We could reset
10362306a36Sopenharmony_ci * the CPUs but it would increase power consumption.
10462306a36Sopenharmony_ci */
10562306a36Sopenharmony_cistatic int socfpga_cpu_kill(unsigned int cpu)
10662306a36Sopenharmony_ci{
10762306a36Sopenharmony_ci	return 1;
10862306a36Sopenharmony_ci}
10962306a36Sopenharmony_ci#endif
11062306a36Sopenharmony_ci
11162306a36Sopenharmony_cistatic const struct smp_operations socfpga_smp_ops __initconst = {
11262306a36Sopenharmony_ci	.smp_prepare_cpus	= socfpga_smp_prepare_cpus,
11362306a36Sopenharmony_ci	.smp_boot_secondary	= socfpga_boot_secondary,
11462306a36Sopenharmony_ci#ifdef CONFIG_HOTPLUG_CPU
11562306a36Sopenharmony_ci	.cpu_die		= socfpga_cpu_die,
11662306a36Sopenharmony_ci	.cpu_kill		= socfpga_cpu_kill,
11762306a36Sopenharmony_ci#endif
11862306a36Sopenharmony_ci};
11962306a36Sopenharmony_ci
12062306a36Sopenharmony_cistatic const struct smp_operations socfpga_a10_smp_ops __initconst = {
12162306a36Sopenharmony_ci	.smp_prepare_cpus	= socfpga_smp_prepare_cpus,
12262306a36Sopenharmony_ci	.smp_boot_secondary	= socfpga_a10_boot_secondary,
12362306a36Sopenharmony_ci#ifdef CONFIG_HOTPLUG_CPU
12462306a36Sopenharmony_ci	.cpu_die		= socfpga_cpu_die,
12562306a36Sopenharmony_ci	.cpu_kill		= socfpga_cpu_kill,
12662306a36Sopenharmony_ci#endif
12762306a36Sopenharmony_ci};
12862306a36Sopenharmony_ci
12962306a36Sopenharmony_ciCPU_METHOD_OF_DECLARE(socfpga_smp, "altr,socfpga-smp", &socfpga_smp_ops);
13062306a36Sopenharmony_ciCPU_METHOD_OF_DECLARE(socfpga_a10_smp, "altr,socfpga-a10-smp", &socfpga_a10_smp_ops);
131