162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Copyright (C) 2002 ARM Ltd.
462306a36Sopenharmony_ci * Copyright (C) 2008 STMicroelctronics.
562306a36Sopenharmony_ci * Copyright (C) 2009 ST-Ericsson.
662306a36Sopenharmony_ci * Author: Srinidhi Kasagar <srinidhi.kasagar@stericsson.com>
762306a36Sopenharmony_ci *
862306a36Sopenharmony_ci * This file is based on arm realview platform
962306a36Sopenharmony_ci */
1062306a36Sopenharmony_ci#include <linux/init.h>
1162306a36Sopenharmony_ci#include <linux/errno.h>
1262306a36Sopenharmony_ci#include <linux/delay.h>
1362306a36Sopenharmony_ci#include <linux/device.h>
1462306a36Sopenharmony_ci#include <linux/smp.h>
1562306a36Sopenharmony_ci#include <linux/io.h>
1662306a36Sopenharmony_ci#include <linux/of.h>
1762306a36Sopenharmony_ci#include <linux/of_address.h>
1862306a36Sopenharmony_ci
1962306a36Sopenharmony_ci#include <asm/cacheflush.h>
2062306a36Sopenharmony_ci#include <asm/smp_plat.h>
2162306a36Sopenharmony_ci#include <asm/smp_scu.h>
2262306a36Sopenharmony_ci
2362306a36Sopenharmony_ci/* Magic triggers in backup RAM */
2462306a36Sopenharmony_ci#define UX500_CPU1_JUMPADDR_OFFSET 0x1FF4
2562306a36Sopenharmony_ci#define UX500_CPU1_WAKEMAGIC_OFFSET 0x1FF0
2662306a36Sopenharmony_ci
2762306a36Sopenharmony_cistatic void __iomem *backupram;
2862306a36Sopenharmony_ci
2962306a36Sopenharmony_cistatic void __init ux500_smp_prepare_cpus(unsigned int max_cpus)
3062306a36Sopenharmony_ci{
3162306a36Sopenharmony_ci	struct device_node *np;
3262306a36Sopenharmony_ci	static void __iomem *scu_base;
3362306a36Sopenharmony_ci	unsigned int ncores;
3462306a36Sopenharmony_ci	int i;
3562306a36Sopenharmony_ci
3662306a36Sopenharmony_ci	np = of_find_compatible_node(NULL, NULL, "ste,dbx500-backupram");
3762306a36Sopenharmony_ci	if (!np) {
3862306a36Sopenharmony_ci		pr_err("No backupram base address\n");
3962306a36Sopenharmony_ci		return;
4062306a36Sopenharmony_ci	}
4162306a36Sopenharmony_ci	backupram = of_iomap(np, 0);
4262306a36Sopenharmony_ci	of_node_put(np);
4362306a36Sopenharmony_ci	if (!backupram) {
4462306a36Sopenharmony_ci		pr_err("No backupram remap\n");
4562306a36Sopenharmony_ci		return;
4662306a36Sopenharmony_ci	}
4762306a36Sopenharmony_ci
4862306a36Sopenharmony_ci	np = of_find_compatible_node(NULL, NULL, "arm,cortex-a9-scu");
4962306a36Sopenharmony_ci	if (!np) {
5062306a36Sopenharmony_ci		pr_err("No SCU base address\n");
5162306a36Sopenharmony_ci		return;
5262306a36Sopenharmony_ci	}
5362306a36Sopenharmony_ci	scu_base = of_iomap(np, 0);
5462306a36Sopenharmony_ci	of_node_put(np);
5562306a36Sopenharmony_ci	if (!scu_base) {
5662306a36Sopenharmony_ci		pr_err("No SCU remap\n");
5762306a36Sopenharmony_ci		return;
5862306a36Sopenharmony_ci	}
5962306a36Sopenharmony_ci
6062306a36Sopenharmony_ci	scu_enable(scu_base);
6162306a36Sopenharmony_ci	ncores = scu_get_core_count(scu_base);
6262306a36Sopenharmony_ci	for (i = 0; i < ncores; i++)
6362306a36Sopenharmony_ci		set_cpu_possible(i, true);
6462306a36Sopenharmony_ci	iounmap(scu_base);
6562306a36Sopenharmony_ci}
6662306a36Sopenharmony_ci
6762306a36Sopenharmony_cistatic int ux500_boot_secondary(unsigned int cpu, struct task_struct *idle)
6862306a36Sopenharmony_ci{
6962306a36Sopenharmony_ci	/*
7062306a36Sopenharmony_ci	 * write the address of secondary startup into the backup ram register
7162306a36Sopenharmony_ci	 * at offset 0x1FF4, then write the magic number 0xA1FEED01 to the
7262306a36Sopenharmony_ci	 * backup ram register at offset 0x1FF0, which is what boot rom code
7362306a36Sopenharmony_ci	 * is waiting for. This will wake up the secondary core from WFE.
7462306a36Sopenharmony_ci	 */
7562306a36Sopenharmony_ci	writel(__pa_symbol(secondary_startup),
7662306a36Sopenharmony_ci	       backupram + UX500_CPU1_JUMPADDR_OFFSET);
7762306a36Sopenharmony_ci	writel(0xA1FEED01,
7862306a36Sopenharmony_ci	       backupram + UX500_CPU1_WAKEMAGIC_OFFSET);
7962306a36Sopenharmony_ci
8062306a36Sopenharmony_ci	/* make sure write buffer is drained */
8162306a36Sopenharmony_ci	mb();
8262306a36Sopenharmony_ci	arch_send_wakeup_ipi_mask(cpumask_of(cpu));
8362306a36Sopenharmony_ci	return 0;
8462306a36Sopenharmony_ci}
8562306a36Sopenharmony_ci
8662306a36Sopenharmony_ci#ifdef CONFIG_HOTPLUG_CPU
8762306a36Sopenharmony_cistatic void ux500_cpu_die(unsigned int cpu)
8862306a36Sopenharmony_ci{
8962306a36Sopenharmony_ci	wfi();
9062306a36Sopenharmony_ci}
9162306a36Sopenharmony_ci#endif
9262306a36Sopenharmony_ci
9362306a36Sopenharmony_cistatic const struct smp_operations ux500_smp_ops __initconst = {
9462306a36Sopenharmony_ci	.smp_prepare_cpus	= ux500_smp_prepare_cpus,
9562306a36Sopenharmony_ci	.smp_boot_secondary	= ux500_boot_secondary,
9662306a36Sopenharmony_ci#ifdef CONFIG_HOTPLUG_CPU
9762306a36Sopenharmony_ci	.cpu_die		= ux500_cpu_die,
9862306a36Sopenharmony_ci#endif
9962306a36Sopenharmony_ci};
10062306a36Sopenharmony_ciCPU_METHOD_OF_DECLARE(ux500_smp, "ste,dbx500-smp", &ux500_smp_ops);
101