18c2ecf20Sopenharmony_ci/* 28c2ecf20Sopenharmony_ci * Symmetric Multi Processing (SMP) support for Armada XP 38c2ecf20Sopenharmony_ci * 48c2ecf20Sopenharmony_ci * Copyright (C) 2012 Marvell 58c2ecf20Sopenharmony_ci * 68c2ecf20Sopenharmony_ci * Lior Amsalem <alior@marvell.com> 78c2ecf20Sopenharmony_ci * Yehuda Yitschak <yehuday@marvell.com> 88c2ecf20Sopenharmony_ci * Gregory CLEMENT <gregory.clement@free-electrons.com> 98c2ecf20Sopenharmony_ci * Thomas Petazzoni <thomas.petazzoni@free-electrons.com> 108c2ecf20Sopenharmony_ci * 118c2ecf20Sopenharmony_ci * This file is licensed under the terms of the GNU General Public 128c2ecf20Sopenharmony_ci * License version 2. This program is licensed "as is" without any 138c2ecf20Sopenharmony_ci * warranty of any kind, whether express or implied. 148c2ecf20Sopenharmony_ci * 158c2ecf20Sopenharmony_ci * The Armada XP SoC has 4 ARMv7 PJ4B CPUs running in full HW coherency 168c2ecf20Sopenharmony_ci * This file implements the routines for preparing the SMP infrastructure 178c2ecf20Sopenharmony_ci * and waking up the secondary CPUs 188c2ecf20Sopenharmony_ci */ 198c2ecf20Sopenharmony_ci 208c2ecf20Sopenharmony_ci#include <linux/init.h> 218c2ecf20Sopenharmony_ci#include <linux/smp.h> 228c2ecf20Sopenharmony_ci#include <linux/clk.h> 238c2ecf20Sopenharmony_ci#include <linux/of.h> 248c2ecf20Sopenharmony_ci#include <linux/of_address.h> 258c2ecf20Sopenharmony_ci#include <linux/mbus.h> 268c2ecf20Sopenharmony_ci#include <asm/cacheflush.h> 278c2ecf20Sopenharmony_ci#include <asm/smp_plat.h> 288c2ecf20Sopenharmony_ci#include "common.h" 298c2ecf20Sopenharmony_ci#include "armada-370-xp.h" 308c2ecf20Sopenharmony_ci#include "pmsu.h" 318c2ecf20Sopenharmony_ci#include "coherency.h" 328c2ecf20Sopenharmony_ci 338c2ecf20Sopenharmony_ci#define ARMADA_XP_MAX_CPUS 4 348c2ecf20Sopenharmony_ci 358c2ecf20Sopenharmony_ci#define AXP_BOOTROM_BASE 0xfff00000 368c2ecf20Sopenharmony_ci#define AXP_BOOTROM_SIZE 0x100000 378c2ecf20Sopenharmony_ci 388c2ecf20Sopenharmony_cistatic struct clk *boot_cpu_clk; 398c2ecf20Sopenharmony_ci 408c2ecf20Sopenharmony_cistatic struct clk *get_cpu_clk(int cpu) 418c2ecf20Sopenharmony_ci{ 428c2ecf20Sopenharmony_ci struct clk *cpu_clk; 438c2ecf20Sopenharmony_ci struct device_node *np = of_get_cpu_node(cpu, NULL); 448c2ecf20Sopenharmony_ci 458c2ecf20Sopenharmony_ci if (WARN(!np, "missing cpu node\n")) 468c2ecf20Sopenharmony_ci return NULL; 478c2ecf20Sopenharmony_ci cpu_clk = of_clk_get(np, 0); 488c2ecf20Sopenharmony_ci if (WARN_ON(IS_ERR(cpu_clk))) 498c2ecf20Sopenharmony_ci return NULL; 508c2ecf20Sopenharmony_ci return cpu_clk; 518c2ecf20Sopenharmony_ci} 528c2ecf20Sopenharmony_ci 538c2ecf20Sopenharmony_cistatic int armada_xp_boot_secondary(unsigned int cpu, struct task_struct *idle) 548c2ecf20Sopenharmony_ci{ 558c2ecf20Sopenharmony_ci int ret, hw_cpu; 568c2ecf20Sopenharmony_ci 578c2ecf20Sopenharmony_ci pr_info("Booting CPU %d\n", cpu); 588c2ecf20Sopenharmony_ci 598c2ecf20Sopenharmony_ci hw_cpu = cpu_logical_map(cpu); 608c2ecf20Sopenharmony_ci mvebu_pmsu_set_cpu_boot_addr(hw_cpu, armada_xp_secondary_startup); 618c2ecf20Sopenharmony_ci 628c2ecf20Sopenharmony_ci /* 638c2ecf20Sopenharmony_ci * This is needed to wake up CPUs in the offline state after 648c2ecf20Sopenharmony_ci * using CPU hotplug. 658c2ecf20Sopenharmony_ci */ 668c2ecf20Sopenharmony_ci arch_send_wakeup_ipi_mask(cpumask_of(cpu)); 678c2ecf20Sopenharmony_ci 688c2ecf20Sopenharmony_ci /* 698c2ecf20Sopenharmony_ci * This is needed to take secondary CPUs out of reset on the 708c2ecf20Sopenharmony_ci * initial boot. 718c2ecf20Sopenharmony_ci */ 728c2ecf20Sopenharmony_ci ret = mvebu_cpu_reset_deassert(hw_cpu); 738c2ecf20Sopenharmony_ci if (ret) { 748c2ecf20Sopenharmony_ci pr_warn("unable to boot CPU: %d\n", ret); 758c2ecf20Sopenharmony_ci return ret; 768c2ecf20Sopenharmony_ci } 778c2ecf20Sopenharmony_ci 788c2ecf20Sopenharmony_ci return 0; 798c2ecf20Sopenharmony_ci} 808c2ecf20Sopenharmony_ci 818c2ecf20Sopenharmony_ci/* 828c2ecf20Sopenharmony_ci * When a CPU is brought back online, either through CPU hotplug, or 838c2ecf20Sopenharmony_ci * because of the boot of a kexec'ed kernel, the PMSU configuration 848c2ecf20Sopenharmony_ci * for this CPU might be in the deep idle state, preventing this CPU 858c2ecf20Sopenharmony_ci * from receiving interrupts. Here, we therefore take out the current 868c2ecf20Sopenharmony_ci * CPU from this state, which was entered by armada_xp_cpu_die() 878c2ecf20Sopenharmony_ci * below. 888c2ecf20Sopenharmony_ci */ 898c2ecf20Sopenharmony_cistatic void armada_xp_secondary_init(unsigned int cpu) 908c2ecf20Sopenharmony_ci{ 918c2ecf20Sopenharmony_ci mvebu_v7_pmsu_idle_exit(); 928c2ecf20Sopenharmony_ci} 938c2ecf20Sopenharmony_ci 948c2ecf20Sopenharmony_cistatic void __init armada_xp_smp_init_cpus(void) 958c2ecf20Sopenharmony_ci{ 968c2ecf20Sopenharmony_ci unsigned int ncores = num_possible_cpus(); 978c2ecf20Sopenharmony_ci 988c2ecf20Sopenharmony_ci if (ncores == 0 || ncores > ARMADA_XP_MAX_CPUS) 998c2ecf20Sopenharmony_ci panic("Invalid number of CPUs in DT\n"); 1008c2ecf20Sopenharmony_ci} 1018c2ecf20Sopenharmony_ci 1028c2ecf20Sopenharmony_cistatic int armada_xp_sync_secondary_clk(unsigned int cpu) 1038c2ecf20Sopenharmony_ci{ 1048c2ecf20Sopenharmony_ci struct clk *cpu_clk = get_cpu_clk(cpu); 1058c2ecf20Sopenharmony_ci 1068c2ecf20Sopenharmony_ci if (!cpu_clk || !boot_cpu_clk) 1078c2ecf20Sopenharmony_ci return 0; 1088c2ecf20Sopenharmony_ci 1098c2ecf20Sopenharmony_ci clk_prepare_enable(cpu_clk); 1108c2ecf20Sopenharmony_ci clk_set_rate(cpu_clk, clk_get_rate(boot_cpu_clk)); 1118c2ecf20Sopenharmony_ci 1128c2ecf20Sopenharmony_ci return 0; 1138c2ecf20Sopenharmony_ci} 1148c2ecf20Sopenharmony_ci 1158c2ecf20Sopenharmony_cistatic void __init armada_xp_smp_prepare_cpus(unsigned int max_cpus) 1168c2ecf20Sopenharmony_ci{ 1178c2ecf20Sopenharmony_ci struct device_node *node; 1188c2ecf20Sopenharmony_ci struct resource res; 1198c2ecf20Sopenharmony_ci int err; 1208c2ecf20Sopenharmony_ci 1218c2ecf20Sopenharmony_ci flush_cache_all(); 1228c2ecf20Sopenharmony_ci set_cpu_coherent(); 1238c2ecf20Sopenharmony_ci 1248c2ecf20Sopenharmony_ci boot_cpu_clk = get_cpu_clk(smp_processor_id()); 1258c2ecf20Sopenharmony_ci if (boot_cpu_clk) { 1268c2ecf20Sopenharmony_ci clk_prepare_enable(boot_cpu_clk); 1278c2ecf20Sopenharmony_ci cpuhp_setup_state_nocalls(CPUHP_AP_ARM_MVEBU_SYNC_CLOCKS, 1288c2ecf20Sopenharmony_ci "arm/mvebu/sync_clocks:online", 1298c2ecf20Sopenharmony_ci armada_xp_sync_secondary_clk, NULL); 1308c2ecf20Sopenharmony_ci } 1318c2ecf20Sopenharmony_ci 1328c2ecf20Sopenharmony_ci /* 1338c2ecf20Sopenharmony_ci * In order to boot the secondary CPUs we need to ensure 1348c2ecf20Sopenharmony_ci * the bootROM is mapped at the correct address. 1358c2ecf20Sopenharmony_ci */ 1368c2ecf20Sopenharmony_ci node = of_find_compatible_node(NULL, NULL, "marvell,bootrom"); 1378c2ecf20Sopenharmony_ci if (!node) 1388c2ecf20Sopenharmony_ci panic("Cannot find 'marvell,bootrom' compatible node"); 1398c2ecf20Sopenharmony_ci 1408c2ecf20Sopenharmony_ci err = of_address_to_resource(node, 0, &res); 1418c2ecf20Sopenharmony_ci of_node_put(node); 1428c2ecf20Sopenharmony_ci if (err < 0) 1438c2ecf20Sopenharmony_ci panic("Cannot get 'bootrom' node address"); 1448c2ecf20Sopenharmony_ci 1458c2ecf20Sopenharmony_ci if (res.start != AXP_BOOTROM_BASE || 1468c2ecf20Sopenharmony_ci resource_size(&res) != AXP_BOOTROM_SIZE) 1478c2ecf20Sopenharmony_ci panic("The address for the BootROM is incorrect"); 1488c2ecf20Sopenharmony_ci} 1498c2ecf20Sopenharmony_ci 1508c2ecf20Sopenharmony_ci#ifdef CONFIG_HOTPLUG_CPU 1518c2ecf20Sopenharmony_cistatic void armada_xp_cpu_die(unsigned int cpu) 1528c2ecf20Sopenharmony_ci{ 1538c2ecf20Sopenharmony_ci /* 1548c2ecf20Sopenharmony_ci * CPU hotplug is implemented by putting offline CPUs into the 1558c2ecf20Sopenharmony_ci * deep idle sleep state. 1568c2ecf20Sopenharmony_ci */ 1578c2ecf20Sopenharmony_ci armada_370_xp_pmsu_idle_enter(true); 1588c2ecf20Sopenharmony_ci} 1598c2ecf20Sopenharmony_ci 1608c2ecf20Sopenharmony_ci/* 1618c2ecf20Sopenharmony_ci * We need a dummy function, so that platform_can_cpu_hotplug() knows 1628c2ecf20Sopenharmony_ci * we support CPU hotplug. However, the function does not need to do 1638c2ecf20Sopenharmony_ci * anything, because CPUs going offline can enter the deep idle state 1648c2ecf20Sopenharmony_ci * by themselves, without any help from a still alive CPU. 1658c2ecf20Sopenharmony_ci */ 1668c2ecf20Sopenharmony_cistatic int armada_xp_cpu_kill(unsigned int cpu) 1678c2ecf20Sopenharmony_ci{ 1688c2ecf20Sopenharmony_ci return 1; 1698c2ecf20Sopenharmony_ci} 1708c2ecf20Sopenharmony_ci#endif 1718c2ecf20Sopenharmony_ci 1728c2ecf20Sopenharmony_ciconst struct smp_operations armada_xp_smp_ops __initconst = { 1738c2ecf20Sopenharmony_ci .smp_init_cpus = armada_xp_smp_init_cpus, 1748c2ecf20Sopenharmony_ci .smp_prepare_cpus = armada_xp_smp_prepare_cpus, 1758c2ecf20Sopenharmony_ci .smp_boot_secondary = armada_xp_boot_secondary, 1768c2ecf20Sopenharmony_ci .smp_secondary_init = armada_xp_secondary_init, 1778c2ecf20Sopenharmony_ci#ifdef CONFIG_HOTPLUG_CPU 1788c2ecf20Sopenharmony_ci .cpu_die = armada_xp_cpu_die, 1798c2ecf20Sopenharmony_ci .cpu_kill = armada_xp_cpu_kill, 1808c2ecf20Sopenharmony_ci#endif 1818c2ecf20Sopenharmony_ci}; 1828c2ecf20Sopenharmony_ci 1838c2ecf20Sopenharmony_ciCPU_METHOD_OF_DECLARE(armada_xp_smp, "marvell,armada-xp-smp", 1848c2ecf20Sopenharmony_ci &armada_xp_smp_ops); 1858c2ecf20Sopenharmony_ci 1868c2ecf20Sopenharmony_ci#define MV98DX3236_CPU_RESUME_CTRL_REG 0x08 1878c2ecf20Sopenharmony_ci#define MV98DX3236_CPU_RESUME_ADDR_REG 0x04 1888c2ecf20Sopenharmony_ci 1898c2ecf20Sopenharmony_cistatic const struct of_device_id of_mv98dx3236_resume_table[] = { 1908c2ecf20Sopenharmony_ci { 1918c2ecf20Sopenharmony_ci .compatible = "marvell,98dx3336-resume-ctrl", 1928c2ecf20Sopenharmony_ci }, 1938c2ecf20Sopenharmony_ci { /* end of list */ }, 1948c2ecf20Sopenharmony_ci}; 1958c2ecf20Sopenharmony_ci 1968c2ecf20Sopenharmony_cistatic int mv98dx3236_resume_set_cpu_boot_addr(int hw_cpu, void *boot_addr) 1978c2ecf20Sopenharmony_ci{ 1988c2ecf20Sopenharmony_ci struct device_node *np; 1998c2ecf20Sopenharmony_ci void __iomem *base; 2008c2ecf20Sopenharmony_ci WARN_ON(hw_cpu != 1); 2018c2ecf20Sopenharmony_ci 2028c2ecf20Sopenharmony_ci np = of_find_matching_node(NULL, of_mv98dx3236_resume_table); 2038c2ecf20Sopenharmony_ci if (!np) 2048c2ecf20Sopenharmony_ci return -ENODEV; 2058c2ecf20Sopenharmony_ci 2068c2ecf20Sopenharmony_ci base = of_io_request_and_map(np, 0, of_node_full_name(np)); 2078c2ecf20Sopenharmony_ci of_node_put(np); 2088c2ecf20Sopenharmony_ci if (IS_ERR(base)) 2098c2ecf20Sopenharmony_ci return PTR_ERR(base); 2108c2ecf20Sopenharmony_ci 2118c2ecf20Sopenharmony_ci writel(0, base + MV98DX3236_CPU_RESUME_CTRL_REG); 2128c2ecf20Sopenharmony_ci writel(__pa_symbol(boot_addr), base + MV98DX3236_CPU_RESUME_ADDR_REG); 2138c2ecf20Sopenharmony_ci 2148c2ecf20Sopenharmony_ci iounmap(base); 2158c2ecf20Sopenharmony_ci 2168c2ecf20Sopenharmony_ci return 0; 2178c2ecf20Sopenharmony_ci} 2188c2ecf20Sopenharmony_ci 2198c2ecf20Sopenharmony_cistatic int mv98dx3236_boot_secondary(unsigned int cpu, struct task_struct *idle) 2208c2ecf20Sopenharmony_ci{ 2218c2ecf20Sopenharmony_ci int ret, hw_cpu; 2228c2ecf20Sopenharmony_ci 2238c2ecf20Sopenharmony_ci hw_cpu = cpu_logical_map(cpu); 2248c2ecf20Sopenharmony_ci mv98dx3236_resume_set_cpu_boot_addr(hw_cpu, 2258c2ecf20Sopenharmony_ci armada_xp_secondary_startup); 2268c2ecf20Sopenharmony_ci 2278c2ecf20Sopenharmony_ci /* 2288c2ecf20Sopenharmony_ci * This is needed to wake up CPUs in the offline state after 2298c2ecf20Sopenharmony_ci * using CPU hotplug. 2308c2ecf20Sopenharmony_ci */ 2318c2ecf20Sopenharmony_ci arch_send_wakeup_ipi_mask(cpumask_of(cpu)); 2328c2ecf20Sopenharmony_ci 2338c2ecf20Sopenharmony_ci /* 2348c2ecf20Sopenharmony_ci * This is needed to take secondary CPUs out of reset on the 2358c2ecf20Sopenharmony_ci * initial boot. 2368c2ecf20Sopenharmony_ci */ 2378c2ecf20Sopenharmony_ci ret = mvebu_cpu_reset_deassert(hw_cpu); 2388c2ecf20Sopenharmony_ci if (ret) { 2398c2ecf20Sopenharmony_ci pr_warn("unable to boot CPU: %d\n", ret); 2408c2ecf20Sopenharmony_ci return ret; 2418c2ecf20Sopenharmony_ci } 2428c2ecf20Sopenharmony_ci 2438c2ecf20Sopenharmony_ci return 0; 2448c2ecf20Sopenharmony_ci} 2458c2ecf20Sopenharmony_ci 2468c2ecf20Sopenharmony_cistatic const struct smp_operations mv98dx3236_smp_ops __initconst = { 2478c2ecf20Sopenharmony_ci .smp_init_cpus = armada_xp_smp_init_cpus, 2488c2ecf20Sopenharmony_ci .smp_prepare_cpus = armada_xp_smp_prepare_cpus, 2498c2ecf20Sopenharmony_ci .smp_boot_secondary = mv98dx3236_boot_secondary, 2508c2ecf20Sopenharmony_ci .smp_secondary_init = armada_xp_secondary_init, 2518c2ecf20Sopenharmony_ci#ifdef CONFIG_HOTPLUG_CPU 2528c2ecf20Sopenharmony_ci .cpu_die = armada_xp_cpu_die, 2538c2ecf20Sopenharmony_ci .cpu_kill = armada_xp_cpu_kill, 2548c2ecf20Sopenharmony_ci#endif 2558c2ecf20Sopenharmony_ci}; 2568c2ecf20Sopenharmony_ci 2578c2ecf20Sopenharmony_ciCPU_METHOD_OF_DECLARE(mv98dx3236_smp, "marvell,98dx3236-smp", 2588c2ecf20Sopenharmony_ci &mv98dx3236_smp_ops); 259