18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Copyright (c) 2013 Linaro Ltd. 48c2ecf20Sopenharmony_ci * Copyright (c) 2013 Hisilicon Limited. 58c2ecf20Sopenharmony_ci * Based on arch/arm/mach-vexpress/platsmp.c, Copyright (C) 2002 ARM Ltd. 68c2ecf20Sopenharmony_ci */ 78c2ecf20Sopenharmony_ci#include <linux/smp.h> 88c2ecf20Sopenharmony_ci#include <linux/io.h> 98c2ecf20Sopenharmony_ci#include <linux/of_address.h> 108c2ecf20Sopenharmony_ci#include <linux/delay.h> 118c2ecf20Sopenharmony_ci 128c2ecf20Sopenharmony_ci#include <asm/cacheflush.h> 138c2ecf20Sopenharmony_ci#include <asm/smp_plat.h> 148c2ecf20Sopenharmony_ci#include <asm/smp_scu.h> 158c2ecf20Sopenharmony_ci#include <asm/mach/map.h> 168c2ecf20Sopenharmony_ci 178c2ecf20Sopenharmony_ci#include "core.h" 188c2ecf20Sopenharmony_ci 198c2ecf20Sopenharmony_ci#define HIX5HD2_BOOT_ADDRESS 0xffff0000 208c2ecf20Sopenharmony_ci 218c2ecf20Sopenharmony_cistatic void __iomem *ctrl_base; 228c2ecf20Sopenharmony_ci 238c2ecf20Sopenharmony_civoid hi3xxx_set_cpu_jump(int cpu, void *jump_addr) 248c2ecf20Sopenharmony_ci{ 258c2ecf20Sopenharmony_ci cpu = cpu_logical_map(cpu); 268c2ecf20Sopenharmony_ci if (!cpu || !ctrl_base) 278c2ecf20Sopenharmony_ci return; 288c2ecf20Sopenharmony_ci writel_relaxed(__pa_symbol(jump_addr), ctrl_base + ((cpu - 1) << 2)); 298c2ecf20Sopenharmony_ci} 308c2ecf20Sopenharmony_ci 318c2ecf20Sopenharmony_ciint hi3xxx_get_cpu_jump(int cpu) 328c2ecf20Sopenharmony_ci{ 338c2ecf20Sopenharmony_ci cpu = cpu_logical_map(cpu); 348c2ecf20Sopenharmony_ci if (!cpu || !ctrl_base) 358c2ecf20Sopenharmony_ci return 0; 368c2ecf20Sopenharmony_ci return readl_relaxed(ctrl_base + ((cpu - 1) << 2)); 378c2ecf20Sopenharmony_ci} 388c2ecf20Sopenharmony_ci 398c2ecf20Sopenharmony_cistatic void __init hisi_enable_scu_a9(void) 408c2ecf20Sopenharmony_ci{ 418c2ecf20Sopenharmony_ci unsigned long base = 0; 428c2ecf20Sopenharmony_ci void __iomem *scu_base = NULL; 438c2ecf20Sopenharmony_ci 448c2ecf20Sopenharmony_ci if (scu_a9_has_base()) { 458c2ecf20Sopenharmony_ci base = scu_a9_get_base(); 468c2ecf20Sopenharmony_ci scu_base = ioremap(base, SZ_4K); 478c2ecf20Sopenharmony_ci if (!scu_base) { 488c2ecf20Sopenharmony_ci pr_err("ioremap(scu_base) failed\n"); 498c2ecf20Sopenharmony_ci return; 508c2ecf20Sopenharmony_ci } 518c2ecf20Sopenharmony_ci scu_enable(scu_base); 528c2ecf20Sopenharmony_ci iounmap(scu_base); 538c2ecf20Sopenharmony_ci } 548c2ecf20Sopenharmony_ci} 558c2ecf20Sopenharmony_ci 568c2ecf20Sopenharmony_cistatic void __init hi3xxx_smp_prepare_cpus(unsigned int max_cpus) 578c2ecf20Sopenharmony_ci{ 588c2ecf20Sopenharmony_ci struct device_node *np = NULL; 598c2ecf20Sopenharmony_ci u32 offset = 0; 608c2ecf20Sopenharmony_ci 618c2ecf20Sopenharmony_ci hisi_enable_scu_a9(); 628c2ecf20Sopenharmony_ci if (!ctrl_base) { 638c2ecf20Sopenharmony_ci np = of_find_compatible_node(NULL, NULL, "hisilicon,sysctrl"); 648c2ecf20Sopenharmony_ci if (!np) { 658c2ecf20Sopenharmony_ci pr_err("failed to find hisilicon,sysctrl node\n"); 668c2ecf20Sopenharmony_ci return; 678c2ecf20Sopenharmony_ci } 688c2ecf20Sopenharmony_ci ctrl_base = of_iomap(np, 0); 698c2ecf20Sopenharmony_ci if (!ctrl_base) { 708c2ecf20Sopenharmony_ci of_node_put(np); 718c2ecf20Sopenharmony_ci pr_err("failed to map address\n"); 728c2ecf20Sopenharmony_ci return; 738c2ecf20Sopenharmony_ci } 748c2ecf20Sopenharmony_ci if (of_property_read_u32(np, "smp-offset", &offset) < 0) { 758c2ecf20Sopenharmony_ci of_node_put(np); 768c2ecf20Sopenharmony_ci pr_err("failed to find smp-offset property\n"); 778c2ecf20Sopenharmony_ci return; 788c2ecf20Sopenharmony_ci } 798c2ecf20Sopenharmony_ci ctrl_base += offset; 808c2ecf20Sopenharmony_ci of_node_put(np); 818c2ecf20Sopenharmony_ci } 828c2ecf20Sopenharmony_ci} 838c2ecf20Sopenharmony_ci 848c2ecf20Sopenharmony_cistatic int hi3xxx_boot_secondary(unsigned int cpu, struct task_struct *idle) 858c2ecf20Sopenharmony_ci{ 868c2ecf20Sopenharmony_ci hi3xxx_set_cpu(cpu, true); 878c2ecf20Sopenharmony_ci hi3xxx_set_cpu_jump(cpu, secondary_startup); 888c2ecf20Sopenharmony_ci arch_send_wakeup_ipi_mask(cpumask_of(cpu)); 898c2ecf20Sopenharmony_ci return 0; 908c2ecf20Sopenharmony_ci} 918c2ecf20Sopenharmony_ci 928c2ecf20Sopenharmony_cistatic const struct smp_operations hi3xxx_smp_ops __initconst = { 938c2ecf20Sopenharmony_ci .smp_prepare_cpus = hi3xxx_smp_prepare_cpus, 948c2ecf20Sopenharmony_ci .smp_boot_secondary = hi3xxx_boot_secondary, 958c2ecf20Sopenharmony_ci#ifdef CONFIG_HOTPLUG_CPU 968c2ecf20Sopenharmony_ci .cpu_die = hi3xxx_cpu_die, 978c2ecf20Sopenharmony_ci .cpu_kill = hi3xxx_cpu_kill, 988c2ecf20Sopenharmony_ci#endif 998c2ecf20Sopenharmony_ci}; 1008c2ecf20Sopenharmony_ci 1018c2ecf20Sopenharmony_cistatic void __init hisi_common_smp_prepare_cpus(unsigned int max_cpus) 1028c2ecf20Sopenharmony_ci{ 1038c2ecf20Sopenharmony_ci hisi_enable_scu_a9(); 1048c2ecf20Sopenharmony_ci} 1058c2ecf20Sopenharmony_ci 1068c2ecf20Sopenharmony_cistatic void hix5hd2_set_scu_boot_addr(phys_addr_t start_addr, phys_addr_t jump_addr) 1078c2ecf20Sopenharmony_ci{ 1088c2ecf20Sopenharmony_ci void __iomem *virt; 1098c2ecf20Sopenharmony_ci 1108c2ecf20Sopenharmony_ci virt = ioremap(start_addr, PAGE_SIZE); 1118c2ecf20Sopenharmony_ci 1128c2ecf20Sopenharmony_ci writel_relaxed(0xe51ff004, virt); /* ldr pc, [pc, #-4] */ 1138c2ecf20Sopenharmony_ci writel_relaxed(jump_addr, virt + 4); /* pc jump phy address */ 1148c2ecf20Sopenharmony_ci iounmap(virt); 1158c2ecf20Sopenharmony_ci} 1168c2ecf20Sopenharmony_ci 1178c2ecf20Sopenharmony_cistatic int hix5hd2_boot_secondary(unsigned int cpu, struct task_struct *idle) 1188c2ecf20Sopenharmony_ci{ 1198c2ecf20Sopenharmony_ci phys_addr_t jumpaddr; 1208c2ecf20Sopenharmony_ci 1218c2ecf20Sopenharmony_ci jumpaddr = __pa_symbol(secondary_startup); 1228c2ecf20Sopenharmony_ci hix5hd2_set_scu_boot_addr(HIX5HD2_BOOT_ADDRESS, jumpaddr); 1238c2ecf20Sopenharmony_ci hix5hd2_set_cpu(cpu, true); 1248c2ecf20Sopenharmony_ci arch_send_wakeup_ipi_mask(cpumask_of(cpu)); 1258c2ecf20Sopenharmony_ci return 0; 1268c2ecf20Sopenharmony_ci} 1278c2ecf20Sopenharmony_ci 1288c2ecf20Sopenharmony_ci 1298c2ecf20Sopenharmony_cistatic const struct smp_operations hix5hd2_smp_ops __initconst = { 1308c2ecf20Sopenharmony_ci .smp_prepare_cpus = hisi_common_smp_prepare_cpus, 1318c2ecf20Sopenharmony_ci .smp_boot_secondary = hix5hd2_boot_secondary, 1328c2ecf20Sopenharmony_ci#ifdef CONFIG_HOTPLUG_CPU 1338c2ecf20Sopenharmony_ci .cpu_die = hix5hd2_cpu_die, 1348c2ecf20Sopenharmony_ci#endif 1358c2ecf20Sopenharmony_ci}; 1368c2ecf20Sopenharmony_ci 1378c2ecf20Sopenharmony_ci 1388c2ecf20Sopenharmony_ci#define SC_SCTL_REMAP_CLR 0x00000100 1398c2ecf20Sopenharmony_ci#define HIP01_BOOT_ADDRESS 0x80000000 1408c2ecf20Sopenharmony_ci#define REG_SC_CTRL 0x000 1418c2ecf20Sopenharmony_ci 1428c2ecf20Sopenharmony_cistatic void hip01_set_boot_addr(phys_addr_t start_addr, phys_addr_t jump_addr) 1438c2ecf20Sopenharmony_ci{ 1448c2ecf20Sopenharmony_ci void __iomem *virt; 1458c2ecf20Sopenharmony_ci 1468c2ecf20Sopenharmony_ci virt = phys_to_virt(start_addr); 1478c2ecf20Sopenharmony_ci 1488c2ecf20Sopenharmony_ci writel_relaxed(0xe51ff004, virt); 1498c2ecf20Sopenharmony_ci writel_relaxed(jump_addr, virt + 4); 1508c2ecf20Sopenharmony_ci} 1518c2ecf20Sopenharmony_ci 1528c2ecf20Sopenharmony_cistatic int hip01_boot_secondary(unsigned int cpu, struct task_struct *idle) 1538c2ecf20Sopenharmony_ci{ 1548c2ecf20Sopenharmony_ci phys_addr_t jumpaddr; 1558c2ecf20Sopenharmony_ci unsigned int remap_reg_value = 0; 1568c2ecf20Sopenharmony_ci struct device_node *node; 1578c2ecf20Sopenharmony_ci 1588c2ecf20Sopenharmony_ci 1598c2ecf20Sopenharmony_ci jumpaddr = __pa_symbol(secondary_startup); 1608c2ecf20Sopenharmony_ci hip01_set_boot_addr(HIP01_BOOT_ADDRESS, jumpaddr); 1618c2ecf20Sopenharmony_ci 1628c2ecf20Sopenharmony_ci node = of_find_compatible_node(NULL, NULL, "hisilicon,hip01-sysctrl"); 1638c2ecf20Sopenharmony_ci if (WARN_ON(!node)) 1648c2ecf20Sopenharmony_ci return -1; 1658c2ecf20Sopenharmony_ci ctrl_base = of_iomap(node, 0); 1668c2ecf20Sopenharmony_ci of_node_put(node); 1678c2ecf20Sopenharmony_ci 1688c2ecf20Sopenharmony_ci /* set the secondary core boot from DDR */ 1698c2ecf20Sopenharmony_ci remap_reg_value = readl_relaxed(ctrl_base + REG_SC_CTRL); 1708c2ecf20Sopenharmony_ci barrier(); 1718c2ecf20Sopenharmony_ci remap_reg_value |= SC_SCTL_REMAP_CLR; 1728c2ecf20Sopenharmony_ci barrier(); 1738c2ecf20Sopenharmony_ci writel_relaxed(remap_reg_value, ctrl_base + REG_SC_CTRL); 1748c2ecf20Sopenharmony_ci 1758c2ecf20Sopenharmony_ci hip01_set_cpu(cpu, true); 1768c2ecf20Sopenharmony_ci 1778c2ecf20Sopenharmony_ci return 0; 1788c2ecf20Sopenharmony_ci} 1798c2ecf20Sopenharmony_ci 1808c2ecf20Sopenharmony_cistatic const struct smp_operations hip01_smp_ops __initconst = { 1818c2ecf20Sopenharmony_ci .smp_prepare_cpus = hisi_common_smp_prepare_cpus, 1828c2ecf20Sopenharmony_ci .smp_boot_secondary = hip01_boot_secondary, 1838c2ecf20Sopenharmony_ci}; 1848c2ecf20Sopenharmony_ci 1858c2ecf20Sopenharmony_ciCPU_METHOD_OF_DECLARE(hi3xxx_smp, "hisilicon,hi3620-smp", &hi3xxx_smp_ops); 1868c2ecf20Sopenharmony_ciCPU_METHOD_OF_DECLARE(hix5hd2_smp, "hisilicon,hix5hd2-smp", &hix5hd2_smp_ops); 1878c2ecf20Sopenharmony_ciCPU_METHOD_OF_DECLARE(hip01_smp, "hisilicon,hip01-smp", &hip01_smp_ops); 188