162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Copyright (C) 2014 Marvell Technology Group Ltd. 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Antoine Ténart <antoine.tenart@free-electrons.com> 662306a36Sopenharmony_ci */ 762306a36Sopenharmony_ci 862306a36Sopenharmony_ci#include <linux/io.h> 962306a36Sopenharmony_ci#include <linux/delay.h> 1062306a36Sopenharmony_ci#include <linux/of.h> 1162306a36Sopenharmony_ci#include <linux/of_address.h> 1262306a36Sopenharmony_ci 1362306a36Sopenharmony_ci#include <asm/cacheflush.h> 1462306a36Sopenharmony_ci#include <asm/cp15.h> 1562306a36Sopenharmony_ci#include <asm/page.h> 1662306a36Sopenharmony_ci#include <asm/smp_plat.h> 1762306a36Sopenharmony_ci#include <asm/smp_scu.h> 1862306a36Sopenharmony_ci 1962306a36Sopenharmony_ci/* 2062306a36Sopenharmony_ci * There are two reset registers, one with self-clearing (SC) 2162306a36Sopenharmony_ci * reset and one with non-self-clearing reset (NON_SC). 2262306a36Sopenharmony_ci */ 2362306a36Sopenharmony_ci#define CPU_RESET_SC 0x00 2462306a36Sopenharmony_ci#define CPU_RESET_NON_SC 0x20 2562306a36Sopenharmony_ci 2662306a36Sopenharmony_ci#define RESET_VECT 0x00 2762306a36Sopenharmony_ci#define SW_RESET_ADDR 0x94 2862306a36Sopenharmony_ci 2962306a36Sopenharmony_ciextern u32 boot_inst; 3062306a36Sopenharmony_ci 3162306a36Sopenharmony_cistatic void __iomem *cpu_ctrl; 3262306a36Sopenharmony_ci 3362306a36Sopenharmony_cistatic inline void berlin_perform_reset_cpu(unsigned int cpu) 3462306a36Sopenharmony_ci{ 3562306a36Sopenharmony_ci u32 val; 3662306a36Sopenharmony_ci 3762306a36Sopenharmony_ci val = readl(cpu_ctrl + CPU_RESET_NON_SC); 3862306a36Sopenharmony_ci val &= ~BIT(cpu_logical_map(cpu)); 3962306a36Sopenharmony_ci writel(val, cpu_ctrl + CPU_RESET_NON_SC); 4062306a36Sopenharmony_ci val |= BIT(cpu_logical_map(cpu)); 4162306a36Sopenharmony_ci writel(val, cpu_ctrl + CPU_RESET_NON_SC); 4262306a36Sopenharmony_ci} 4362306a36Sopenharmony_ci 4462306a36Sopenharmony_cistatic int berlin_boot_secondary(unsigned int cpu, struct task_struct *idle) 4562306a36Sopenharmony_ci{ 4662306a36Sopenharmony_ci if (!cpu_ctrl) 4762306a36Sopenharmony_ci return -EFAULT; 4862306a36Sopenharmony_ci 4962306a36Sopenharmony_ci /* 5062306a36Sopenharmony_ci * Reset the CPU, making it to execute the instruction in the reset 5162306a36Sopenharmony_ci * exception vector. 5262306a36Sopenharmony_ci */ 5362306a36Sopenharmony_ci berlin_perform_reset_cpu(cpu); 5462306a36Sopenharmony_ci 5562306a36Sopenharmony_ci return 0; 5662306a36Sopenharmony_ci} 5762306a36Sopenharmony_ci 5862306a36Sopenharmony_cistatic void __init berlin_smp_prepare_cpus(unsigned int max_cpus) 5962306a36Sopenharmony_ci{ 6062306a36Sopenharmony_ci struct device_node *np; 6162306a36Sopenharmony_ci void __iomem *scu_base; 6262306a36Sopenharmony_ci void __iomem *vectors_base; 6362306a36Sopenharmony_ci 6462306a36Sopenharmony_ci np = of_find_compatible_node(NULL, NULL, "arm,cortex-a9-scu"); 6562306a36Sopenharmony_ci scu_base = of_iomap(np, 0); 6662306a36Sopenharmony_ci of_node_put(np); 6762306a36Sopenharmony_ci if (!scu_base) 6862306a36Sopenharmony_ci return; 6962306a36Sopenharmony_ci 7062306a36Sopenharmony_ci np = of_find_compatible_node(NULL, NULL, "marvell,berlin-cpu-ctrl"); 7162306a36Sopenharmony_ci cpu_ctrl = of_iomap(np, 0); 7262306a36Sopenharmony_ci of_node_put(np); 7362306a36Sopenharmony_ci if (!cpu_ctrl) 7462306a36Sopenharmony_ci goto unmap_scu; 7562306a36Sopenharmony_ci 7662306a36Sopenharmony_ci vectors_base = ioremap(VECTORS_BASE, SZ_32K); 7762306a36Sopenharmony_ci if (!vectors_base) 7862306a36Sopenharmony_ci goto unmap_scu; 7962306a36Sopenharmony_ci 8062306a36Sopenharmony_ci scu_enable(scu_base); 8162306a36Sopenharmony_ci 8262306a36Sopenharmony_ci /* 8362306a36Sopenharmony_ci * Write the first instruction the CPU will execute after being reset 8462306a36Sopenharmony_ci * in the reset exception vector. 8562306a36Sopenharmony_ci */ 8662306a36Sopenharmony_ci writel(boot_inst, vectors_base + RESET_VECT); 8762306a36Sopenharmony_ci 8862306a36Sopenharmony_ci /* 8962306a36Sopenharmony_ci * Write the secondary startup address into the SW reset address 9062306a36Sopenharmony_ci * vector. This is used by boot_inst. 9162306a36Sopenharmony_ci */ 9262306a36Sopenharmony_ci writel(__pa_symbol(secondary_startup), vectors_base + SW_RESET_ADDR); 9362306a36Sopenharmony_ci 9462306a36Sopenharmony_ci iounmap(vectors_base); 9562306a36Sopenharmony_ciunmap_scu: 9662306a36Sopenharmony_ci iounmap(scu_base); 9762306a36Sopenharmony_ci} 9862306a36Sopenharmony_ci 9962306a36Sopenharmony_ci#ifdef CONFIG_HOTPLUG_CPU 10062306a36Sopenharmony_cistatic void berlin_cpu_die(unsigned int cpu) 10162306a36Sopenharmony_ci{ 10262306a36Sopenharmony_ci v7_exit_coherency_flush(louis); 10362306a36Sopenharmony_ci while (1) 10462306a36Sopenharmony_ci cpu_do_idle(); 10562306a36Sopenharmony_ci} 10662306a36Sopenharmony_ci 10762306a36Sopenharmony_cistatic int berlin_cpu_kill(unsigned int cpu) 10862306a36Sopenharmony_ci{ 10962306a36Sopenharmony_ci u32 val; 11062306a36Sopenharmony_ci 11162306a36Sopenharmony_ci val = readl(cpu_ctrl + CPU_RESET_NON_SC); 11262306a36Sopenharmony_ci val &= ~BIT(cpu_logical_map(cpu)); 11362306a36Sopenharmony_ci writel(val, cpu_ctrl + CPU_RESET_NON_SC); 11462306a36Sopenharmony_ci 11562306a36Sopenharmony_ci return 1; 11662306a36Sopenharmony_ci} 11762306a36Sopenharmony_ci#endif 11862306a36Sopenharmony_ci 11962306a36Sopenharmony_cistatic const struct smp_operations berlin_smp_ops __initconst = { 12062306a36Sopenharmony_ci .smp_prepare_cpus = berlin_smp_prepare_cpus, 12162306a36Sopenharmony_ci .smp_boot_secondary = berlin_boot_secondary, 12262306a36Sopenharmony_ci#ifdef CONFIG_HOTPLUG_CPU 12362306a36Sopenharmony_ci .cpu_die = berlin_cpu_die, 12462306a36Sopenharmony_ci .cpu_kill = berlin_cpu_kill, 12562306a36Sopenharmony_ci#endif 12662306a36Sopenharmony_ci}; 12762306a36Sopenharmony_ciCPU_METHOD_OF_DECLARE(berlin_smp, "marvell,berlin-smp", &berlin_smp_ops); 128