18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * Copyright (C) 2002 ARM Ltd.
48c2ecf20Sopenharmony_ci * Copyright (C) 2008 STMicroelctronics.
58c2ecf20Sopenharmony_ci * Copyright (C) 2009 ST-Ericsson.
68c2ecf20Sopenharmony_ci * Author: Srinidhi Kasagar <srinidhi.kasagar@stericsson.com>
78c2ecf20Sopenharmony_ci *
88c2ecf20Sopenharmony_ci * This file is based on arm realview platform
98c2ecf20Sopenharmony_ci */
108c2ecf20Sopenharmony_ci#include <linux/init.h>
118c2ecf20Sopenharmony_ci#include <linux/errno.h>
128c2ecf20Sopenharmony_ci#include <linux/delay.h>
138c2ecf20Sopenharmony_ci#include <linux/device.h>
148c2ecf20Sopenharmony_ci#include <linux/smp.h>
158c2ecf20Sopenharmony_ci#include <linux/io.h>
168c2ecf20Sopenharmony_ci#include <linux/of.h>
178c2ecf20Sopenharmony_ci#include <linux/of_address.h>
188c2ecf20Sopenharmony_ci
198c2ecf20Sopenharmony_ci#include <asm/cacheflush.h>
208c2ecf20Sopenharmony_ci#include <asm/smp_plat.h>
218c2ecf20Sopenharmony_ci#include <asm/smp_scu.h>
228c2ecf20Sopenharmony_ci
238c2ecf20Sopenharmony_ci#include "db8500-regs.h"
248c2ecf20Sopenharmony_ci
258c2ecf20Sopenharmony_ci/* Magic triggers in backup RAM */
268c2ecf20Sopenharmony_ci#define UX500_CPU1_JUMPADDR_OFFSET 0x1FF4
278c2ecf20Sopenharmony_ci#define UX500_CPU1_WAKEMAGIC_OFFSET 0x1FF0
288c2ecf20Sopenharmony_ci
298c2ecf20Sopenharmony_cistatic void __iomem *backupram;
308c2ecf20Sopenharmony_ci
318c2ecf20Sopenharmony_cistatic void __init ux500_smp_prepare_cpus(unsigned int max_cpus)
328c2ecf20Sopenharmony_ci{
338c2ecf20Sopenharmony_ci	struct device_node *np;
348c2ecf20Sopenharmony_ci	static void __iomem *scu_base;
358c2ecf20Sopenharmony_ci	unsigned int ncores;
368c2ecf20Sopenharmony_ci	int i;
378c2ecf20Sopenharmony_ci
388c2ecf20Sopenharmony_ci	np = of_find_compatible_node(NULL, NULL, "ste,dbx500-backupram");
398c2ecf20Sopenharmony_ci	if (!np) {
408c2ecf20Sopenharmony_ci		pr_err("No backupram base address\n");
418c2ecf20Sopenharmony_ci		return;
428c2ecf20Sopenharmony_ci	}
438c2ecf20Sopenharmony_ci	backupram = of_iomap(np, 0);
448c2ecf20Sopenharmony_ci	of_node_put(np);
458c2ecf20Sopenharmony_ci	if (!backupram) {
468c2ecf20Sopenharmony_ci		pr_err("No backupram remap\n");
478c2ecf20Sopenharmony_ci		return;
488c2ecf20Sopenharmony_ci	}
498c2ecf20Sopenharmony_ci
508c2ecf20Sopenharmony_ci	np = of_find_compatible_node(NULL, NULL, "arm,cortex-a9-scu");
518c2ecf20Sopenharmony_ci	if (!np) {
528c2ecf20Sopenharmony_ci		pr_err("No SCU base address\n");
538c2ecf20Sopenharmony_ci		return;
548c2ecf20Sopenharmony_ci	}
558c2ecf20Sopenharmony_ci	scu_base = of_iomap(np, 0);
568c2ecf20Sopenharmony_ci	of_node_put(np);
578c2ecf20Sopenharmony_ci	if (!scu_base) {
588c2ecf20Sopenharmony_ci		pr_err("No SCU remap\n");
598c2ecf20Sopenharmony_ci		return;
608c2ecf20Sopenharmony_ci	}
618c2ecf20Sopenharmony_ci
628c2ecf20Sopenharmony_ci	scu_enable(scu_base);
638c2ecf20Sopenharmony_ci	ncores = scu_get_core_count(scu_base);
648c2ecf20Sopenharmony_ci	for (i = 0; i < ncores; i++)
658c2ecf20Sopenharmony_ci		set_cpu_possible(i, true);
668c2ecf20Sopenharmony_ci	iounmap(scu_base);
678c2ecf20Sopenharmony_ci}
688c2ecf20Sopenharmony_ci
698c2ecf20Sopenharmony_cistatic int ux500_boot_secondary(unsigned int cpu, struct task_struct *idle)
708c2ecf20Sopenharmony_ci{
718c2ecf20Sopenharmony_ci	/*
728c2ecf20Sopenharmony_ci	 * write the address of secondary startup into the backup ram register
738c2ecf20Sopenharmony_ci	 * at offset 0x1FF4, then write the magic number 0xA1FEED01 to the
748c2ecf20Sopenharmony_ci	 * backup ram register at offset 0x1FF0, which is what boot rom code
758c2ecf20Sopenharmony_ci	 * is waiting for. This will wake up the secondary core from WFE.
768c2ecf20Sopenharmony_ci	 */
778c2ecf20Sopenharmony_ci	writel(__pa_symbol(secondary_startup),
788c2ecf20Sopenharmony_ci	       backupram + UX500_CPU1_JUMPADDR_OFFSET);
798c2ecf20Sopenharmony_ci	writel(0xA1FEED01,
808c2ecf20Sopenharmony_ci	       backupram + UX500_CPU1_WAKEMAGIC_OFFSET);
818c2ecf20Sopenharmony_ci
828c2ecf20Sopenharmony_ci	/* make sure write buffer is drained */
838c2ecf20Sopenharmony_ci	mb();
848c2ecf20Sopenharmony_ci	arch_send_wakeup_ipi_mask(cpumask_of(cpu));
858c2ecf20Sopenharmony_ci	return 0;
868c2ecf20Sopenharmony_ci}
878c2ecf20Sopenharmony_ci
888c2ecf20Sopenharmony_ci#ifdef CONFIG_HOTPLUG_CPU
898c2ecf20Sopenharmony_civoid ux500_cpu_die(unsigned int cpu)
908c2ecf20Sopenharmony_ci{
918c2ecf20Sopenharmony_ci	wfi();
928c2ecf20Sopenharmony_ci}
938c2ecf20Sopenharmony_ci#endif
948c2ecf20Sopenharmony_ci
958c2ecf20Sopenharmony_cistatic const struct smp_operations ux500_smp_ops __initconst = {
968c2ecf20Sopenharmony_ci	.smp_prepare_cpus	= ux500_smp_prepare_cpus,
978c2ecf20Sopenharmony_ci	.smp_boot_secondary	= ux500_boot_secondary,
988c2ecf20Sopenharmony_ci#ifdef CONFIG_HOTPLUG_CPU
998c2ecf20Sopenharmony_ci	.cpu_die		= ux500_cpu_die,
1008c2ecf20Sopenharmony_ci#endif
1018c2ecf20Sopenharmony_ci};
1028c2ecf20Sopenharmony_ciCPU_METHOD_OF_DECLARE(ux500_smp, "ste,dbx500-smp", &ux500_smp_ops);
103