162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Copyright 2011 Freescale Semiconductor, Inc. 462306a36Sopenharmony_ci * Copyright 2011 Linaro Ltd. 562306a36Sopenharmony_ci */ 662306a36Sopenharmony_ci 762306a36Sopenharmony_ci#include <linux/init.h> 862306a36Sopenharmony_ci#include <linux/of_address.h> 962306a36Sopenharmony_ci#include <linux/of.h> 1062306a36Sopenharmony_ci#include <linux/smp.h> 1162306a36Sopenharmony_ci 1262306a36Sopenharmony_ci#include <asm/cacheflush.h> 1362306a36Sopenharmony_ci#include <asm/page.h> 1462306a36Sopenharmony_ci#include <asm/smp_scu.h> 1562306a36Sopenharmony_ci#include <asm/mach/map.h> 1662306a36Sopenharmony_ci 1762306a36Sopenharmony_ci#include "common.h" 1862306a36Sopenharmony_ci#include "hardware.h" 1962306a36Sopenharmony_ci 2062306a36Sopenharmony_ciu32 g_diag_reg; 2162306a36Sopenharmony_cistatic void __iomem *scu_base; 2262306a36Sopenharmony_ci 2362306a36Sopenharmony_cistatic struct map_desc scu_io_desc __initdata = { 2462306a36Sopenharmony_ci /* .virtual and .pfn are run-time assigned */ 2562306a36Sopenharmony_ci .length = SZ_4K, 2662306a36Sopenharmony_ci .type = MT_DEVICE, 2762306a36Sopenharmony_ci}; 2862306a36Sopenharmony_ci 2962306a36Sopenharmony_civoid __init imx_scu_map_io(void) 3062306a36Sopenharmony_ci{ 3162306a36Sopenharmony_ci unsigned long base; 3262306a36Sopenharmony_ci 3362306a36Sopenharmony_ci /* Get SCU base */ 3462306a36Sopenharmony_ci asm("mrc p15, 4, %0, c15, c0, 0" : "=r" (base)); 3562306a36Sopenharmony_ci 3662306a36Sopenharmony_ci scu_io_desc.virtual = IMX_IO_P2V(base); 3762306a36Sopenharmony_ci scu_io_desc.pfn = __phys_to_pfn(base); 3862306a36Sopenharmony_ci iotable_init(&scu_io_desc, 1); 3962306a36Sopenharmony_ci 4062306a36Sopenharmony_ci scu_base = IMX_IO_ADDRESS(base); 4162306a36Sopenharmony_ci} 4262306a36Sopenharmony_ci 4362306a36Sopenharmony_cistatic int imx_boot_secondary(unsigned int cpu, struct task_struct *idle) 4462306a36Sopenharmony_ci{ 4562306a36Sopenharmony_ci imx_set_cpu_jump(cpu, v7_secondary_startup); 4662306a36Sopenharmony_ci imx_enable_cpu(cpu, true); 4762306a36Sopenharmony_ci return 0; 4862306a36Sopenharmony_ci} 4962306a36Sopenharmony_ci 5062306a36Sopenharmony_ci/* 5162306a36Sopenharmony_ci * Initialise the CPU possible map early - this describes the CPUs 5262306a36Sopenharmony_ci * which may be present or become present in the system. 5362306a36Sopenharmony_ci */ 5462306a36Sopenharmony_cistatic void __init imx_smp_init_cpus(void) 5562306a36Sopenharmony_ci{ 5662306a36Sopenharmony_ci int i, ncores; 5762306a36Sopenharmony_ci 5862306a36Sopenharmony_ci ncores = scu_get_core_count(scu_base); 5962306a36Sopenharmony_ci 6062306a36Sopenharmony_ci for (i = ncores; i < NR_CPUS; i++) 6162306a36Sopenharmony_ci set_cpu_possible(i, false); 6262306a36Sopenharmony_ci} 6362306a36Sopenharmony_ci 6462306a36Sopenharmony_civoid imx_smp_prepare(void) 6562306a36Sopenharmony_ci{ 6662306a36Sopenharmony_ci scu_enable(scu_base); 6762306a36Sopenharmony_ci} 6862306a36Sopenharmony_ci 6962306a36Sopenharmony_cistatic void __init imx_smp_prepare_cpus(unsigned int max_cpus) 7062306a36Sopenharmony_ci{ 7162306a36Sopenharmony_ci imx_smp_prepare(); 7262306a36Sopenharmony_ci 7362306a36Sopenharmony_ci /* 7462306a36Sopenharmony_ci * The diagnostic register holds the errata bits. Mostly bootloader 7562306a36Sopenharmony_ci * does not bring up secondary cores, so that when errata bits are set 7662306a36Sopenharmony_ci * in bootloader, they are set only for boot cpu. But on a SMP 7762306a36Sopenharmony_ci * configuration, it should be equally done on every single core. 7862306a36Sopenharmony_ci * Read the register from boot cpu here, and will replicate it into 7962306a36Sopenharmony_ci * secondary cores when booting them. 8062306a36Sopenharmony_ci */ 8162306a36Sopenharmony_ci asm("mrc p15, 0, %0, c15, c0, 1" : "=r" (g_diag_reg) : : "cc"); 8262306a36Sopenharmony_ci sync_cache_w(&g_diag_reg); 8362306a36Sopenharmony_ci} 8462306a36Sopenharmony_ci 8562306a36Sopenharmony_ciconst struct smp_operations imx_smp_ops __initconst = { 8662306a36Sopenharmony_ci .smp_init_cpus = imx_smp_init_cpus, 8762306a36Sopenharmony_ci .smp_prepare_cpus = imx_smp_prepare_cpus, 8862306a36Sopenharmony_ci .smp_boot_secondary = imx_boot_secondary, 8962306a36Sopenharmony_ci#ifdef CONFIG_HOTPLUG_CPU 9062306a36Sopenharmony_ci .cpu_die = imx_cpu_die, 9162306a36Sopenharmony_ci .cpu_kill = imx_cpu_kill, 9262306a36Sopenharmony_ci#endif 9362306a36Sopenharmony_ci}; 9462306a36Sopenharmony_ci 9562306a36Sopenharmony_ci/* 9662306a36Sopenharmony_ci * Initialise the CPU possible map early - this describes the CPUs 9762306a36Sopenharmony_ci * which may be present or become present in the system. 9862306a36Sopenharmony_ci */ 9962306a36Sopenharmony_cistatic void __init imx7_smp_init_cpus(void) 10062306a36Sopenharmony_ci{ 10162306a36Sopenharmony_ci struct device_node *np; 10262306a36Sopenharmony_ci int i, ncores = 0; 10362306a36Sopenharmony_ci 10462306a36Sopenharmony_ci /* The iMX7D SCU does not report core count, get it from DT */ 10562306a36Sopenharmony_ci for_each_of_cpu_node(np) 10662306a36Sopenharmony_ci ncores++; 10762306a36Sopenharmony_ci 10862306a36Sopenharmony_ci for (i = ncores; i < NR_CPUS; i++) 10962306a36Sopenharmony_ci set_cpu_possible(i, false); 11062306a36Sopenharmony_ci} 11162306a36Sopenharmony_ci 11262306a36Sopenharmony_ciconst struct smp_operations imx7_smp_ops __initconst = { 11362306a36Sopenharmony_ci .smp_init_cpus = imx7_smp_init_cpus, 11462306a36Sopenharmony_ci .smp_boot_secondary = imx_boot_secondary, 11562306a36Sopenharmony_ci#ifdef CONFIG_HOTPLUG_CPU 11662306a36Sopenharmony_ci .cpu_die = imx_cpu_die, 11762306a36Sopenharmony_ci .cpu_kill = imx_cpu_kill, 11862306a36Sopenharmony_ci#endif 11962306a36Sopenharmony_ci}; 12062306a36Sopenharmony_ci 12162306a36Sopenharmony_ci#define DCFG_CCSR_SCRATCHRW1 0x200 12262306a36Sopenharmony_ci 12362306a36Sopenharmony_cistatic int ls1021a_boot_secondary(unsigned int cpu, struct task_struct *idle) 12462306a36Sopenharmony_ci{ 12562306a36Sopenharmony_ci arch_send_wakeup_ipi_mask(cpumask_of(cpu)); 12662306a36Sopenharmony_ci 12762306a36Sopenharmony_ci return 0; 12862306a36Sopenharmony_ci} 12962306a36Sopenharmony_ci 13062306a36Sopenharmony_cistatic void __init ls1021a_smp_prepare_cpus(unsigned int max_cpus) 13162306a36Sopenharmony_ci{ 13262306a36Sopenharmony_ci struct device_node *np; 13362306a36Sopenharmony_ci void __iomem *dcfg_base; 13462306a36Sopenharmony_ci unsigned long paddr; 13562306a36Sopenharmony_ci 13662306a36Sopenharmony_ci np = of_find_compatible_node(NULL, NULL, "fsl,ls1021a-dcfg"); 13762306a36Sopenharmony_ci dcfg_base = of_iomap(np, 0); 13862306a36Sopenharmony_ci of_node_put(np); 13962306a36Sopenharmony_ci BUG_ON(!dcfg_base); 14062306a36Sopenharmony_ci 14162306a36Sopenharmony_ci paddr = __pa_symbol(secondary_startup); 14262306a36Sopenharmony_ci writel_relaxed(cpu_to_be32(paddr), dcfg_base + DCFG_CCSR_SCRATCHRW1); 14362306a36Sopenharmony_ci 14462306a36Sopenharmony_ci iounmap(dcfg_base); 14562306a36Sopenharmony_ci} 14662306a36Sopenharmony_ci 14762306a36Sopenharmony_ciconst struct smp_operations ls1021a_smp_ops __initconst = { 14862306a36Sopenharmony_ci .smp_prepare_cpus = ls1021a_smp_prepare_cpus, 14962306a36Sopenharmony_ci .smp_boot_secondary = ls1021a_boot_secondary, 15062306a36Sopenharmony_ci}; 151