18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Copyright (c) 2013-2014 Linaro Ltd. 48c2ecf20Sopenharmony_ci * Copyright (c) 2013-2014 Hisilicon Limited. 58c2ecf20Sopenharmony_ci */ 68c2ecf20Sopenharmony_ci#include <linux/init.h> 78c2ecf20Sopenharmony_ci#include <linux/smp.h> 88c2ecf20Sopenharmony_ci#include <linux/delay.h> 98c2ecf20Sopenharmony_ci#include <linux/io.h> 108c2ecf20Sopenharmony_ci#include <linux/memblock.h> 118c2ecf20Sopenharmony_ci#include <linux/of_address.h> 128c2ecf20Sopenharmony_ci 138c2ecf20Sopenharmony_ci#include <asm/cputype.h> 148c2ecf20Sopenharmony_ci#include <asm/cp15.h> 158c2ecf20Sopenharmony_ci#include <asm/cacheflush.h> 168c2ecf20Sopenharmony_ci#include <asm/smp.h> 178c2ecf20Sopenharmony_ci#include <asm/smp_plat.h> 188c2ecf20Sopenharmony_ci 198c2ecf20Sopenharmony_ci#include "core.h" 208c2ecf20Sopenharmony_ci 218c2ecf20Sopenharmony_ci/* bits definition in SC_CPU_RESET_REQ[x]/SC_CPU_RESET_DREQ[x] 228c2ecf20Sopenharmony_ci * 1 -- unreset; 0 -- reset 238c2ecf20Sopenharmony_ci */ 248c2ecf20Sopenharmony_ci#define CORE_RESET_BIT(x) (1 << x) 258c2ecf20Sopenharmony_ci#define NEON_RESET_BIT(x) (1 << (x + 4)) 268c2ecf20Sopenharmony_ci#define CORE_DEBUG_RESET_BIT(x) (1 << (x + 9)) 278c2ecf20Sopenharmony_ci#define CLUSTER_L2_RESET_BIT (1 << 8) 288c2ecf20Sopenharmony_ci#define CLUSTER_DEBUG_RESET_BIT (1 << 13) 298c2ecf20Sopenharmony_ci 308c2ecf20Sopenharmony_ci/* 318c2ecf20Sopenharmony_ci * bits definition in SC_CPU_RESET_STATUS[x] 328c2ecf20Sopenharmony_ci * 1 -- reset status; 0 -- unreset status 338c2ecf20Sopenharmony_ci */ 348c2ecf20Sopenharmony_ci#define CORE_RESET_STATUS(x) (1 << x) 358c2ecf20Sopenharmony_ci#define NEON_RESET_STATUS(x) (1 << (x + 4)) 368c2ecf20Sopenharmony_ci#define CORE_DEBUG_RESET_STATUS(x) (1 << (x + 9)) 378c2ecf20Sopenharmony_ci#define CLUSTER_L2_RESET_STATUS (1 << 8) 388c2ecf20Sopenharmony_ci#define CLUSTER_DEBUG_RESET_STATUS (1 << 13) 398c2ecf20Sopenharmony_ci#define CORE_WFI_STATUS(x) (1 << (x + 16)) 408c2ecf20Sopenharmony_ci#define CORE_WFE_STATUS(x) (1 << (x + 20)) 418c2ecf20Sopenharmony_ci#define CORE_DEBUG_ACK(x) (1 << (x + 24)) 428c2ecf20Sopenharmony_ci 438c2ecf20Sopenharmony_ci#define SC_CPU_RESET_REQ(x) (0x520 + (x << 3)) /* reset */ 448c2ecf20Sopenharmony_ci#define SC_CPU_RESET_DREQ(x) (0x524 + (x << 3)) /* unreset */ 458c2ecf20Sopenharmony_ci#define SC_CPU_RESET_STATUS(x) (0x1520 + (x << 3)) 468c2ecf20Sopenharmony_ci 478c2ecf20Sopenharmony_ci#define FAB_SF_MODE 0x0c 488c2ecf20Sopenharmony_ci#define FAB_SF_INVLD 0x10 498c2ecf20Sopenharmony_ci 508c2ecf20Sopenharmony_ci/* bits definition in FB_SF_INVLD */ 518c2ecf20Sopenharmony_ci#define FB_SF_INVLD_START (1 << 8) 528c2ecf20Sopenharmony_ci 538c2ecf20Sopenharmony_ci#define HIP04_MAX_CLUSTERS 4 548c2ecf20Sopenharmony_ci#define HIP04_MAX_CPUS_PER_CLUSTER 4 558c2ecf20Sopenharmony_ci 568c2ecf20Sopenharmony_ci#define POLL_MSEC 10 578c2ecf20Sopenharmony_ci#define TIMEOUT_MSEC 1000 588c2ecf20Sopenharmony_ci 598c2ecf20Sopenharmony_cistatic void __iomem *sysctrl, *fabric; 608c2ecf20Sopenharmony_cistatic int hip04_cpu_table[HIP04_MAX_CLUSTERS][HIP04_MAX_CPUS_PER_CLUSTER]; 618c2ecf20Sopenharmony_cistatic DEFINE_SPINLOCK(boot_lock); 628c2ecf20Sopenharmony_cistatic u32 fabric_phys_addr; 638c2ecf20Sopenharmony_ci/* 648c2ecf20Sopenharmony_ci * [0]: bootwrapper physical address 658c2ecf20Sopenharmony_ci * [1]: bootwrapper size 668c2ecf20Sopenharmony_ci * [2]: relocation address 678c2ecf20Sopenharmony_ci * [3]: relocation size 688c2ecf20Sopenharmony_ci */ 698c2ecf20Sopenharmony_cistatic u32 hip04_boot_method[4]; 708c2ecf20Sopenharmony_ci 718c2ecf20Sopenharmony_cistatic bool hip04_cluster_is_down(unsigned int cluster) 728c2ecf20Sopenharmony_ci{ 738c2ecf20Sopenharmony_ci int i; 748c2ecf20Sopenharmony_ci 758c2ecf20Sopenharmony_ci for (i = 0; i < HIP04_MAX_CPUS_PER_CLUSTER; i++) 768c2ecf20Sopenharmony_ci if (hip04_cpu_table[cluster][i]) 778c2ecf20Sopenharmony_ci return false; 788c2ecf20Sopenharmony_ci return true; 798c2ecf20Sopenharmony_ci} 808c2ecf20Sopenharmony_ci 818c2ecf20Sopenharmony_cistatic void hip04_set_snoop_filter(unsigned int cluster, unsigned int on) 828c2ecf20Sopenharmony_ci{ 838c2ecf20Sopenharmony_ci unsigned long data; 848c2ecf20Sopenharmony_ci 858c2ecf20Sopenharmony_ci if (!fabric) 868c2ecf20Sopenharmony_ci BUG(); 878c2ecf20Sopenharmony_ci data = readl_relaxed(fabric + FAB_SF_MODE); 888c2ecf20Sopenharmony_ci if (on) 898c2ecf20Sopenharmony_ci data |= 1 << cluster; 908c2ecf20Sopenharmony_ci else 918c2ecf20Sopenharmony_ci data &= ~(1 << cluster); 928c2ecf20Sopenharmony_ci writel_relaxed(data, fabric + FAB_SF_MODE); 938c2ecf20Sopenharmony_ci do { 948c2ecf20Sopenharmony_ci cpu_relax(); 958c2ecf20Sopenharmony_ci } while (data != readl_relaxed(fabric + FAB_SF_MODE)); 968c2ecf20Sopenharmony_ci} 978c2ecf20Sopenharmony_ci 988c2ecf20Sopenharmony_cistatic int hip04_boot_secondary(unsigned int l_cpu, struct task_struct *idle) 998c2ecf20Sopenharmony_ci{ 1008c2ecf20Sopenharmony_ci unsigned int mpidr, cpu, cluster; 1018c2ecf20Sopenharmony_ci unsigned long data; 1028c2ecf20Sopenharmony_ci void __iomem *sys_dreq, *sys_status; 1038c2ecf20Sopenharmony_ci 1048c2ecf20Sopenharmony_ci mpidr = cpu_logical_map(l_cpu); 1058c2ecf20Sopenharmony_ci cpu = MPIDR_AFFINITY_LEVEL(mpidr, 0); 1068c2ecf20Sopenharmony_ci cluster = MPIDR_AFFINITY_LEVEL(mpidr, 1); 1078c2ecf20Sopenharmony_ci 1088c2ecf20Sopenharmony_ci if (!sysctrl) 1098c2ecf20Sopenharmony_ci return -ENODEV; 1108c2ecf20Sopenharmony_ci if (cluster >= HIP04_MAX_CLUSTERS || cpu >= HIP04_MAX_CPUS_PER_CLUSTER) 1118c2ecf20Sopenharmony_ci return -EINVAL; 1128c2ecf20Sopenharmony_ci 1138c2ecf20Sopenharmony_ci spin_lock_irq(&boot_lock); 1148c2ecf20Sopenharmony_ci 1158c2ecf20Sopenharmony_ci if (hip04_cpu_table[cluster][cpu]) 1168c2ecf20Sopenharmony_ci goto out; 1178c2ecf20Sopenharmony_ci 1188c2ecf20Sopenharmony_ci sys_dreq = sysctrl + SC_CPU_RESET_DREQ(cluster); 1198c2ecf20Sopenharmony_ci sys_status = sysctrl + SC_CPU_RESET_STATUS(cluster); 1208c2ecf20Sopenharmony_ci if (hip04_cluster_is_down(cluster)) { 1218c2ecf20Sopenharmony_ci data = CLUSTER_DEBUG_RESET_BIT; 1228c2ecf20Sopenharmony_ci writel_relaxed(data, sys_dreq); 1238c2ecf20Sopenharmony_ci do { 1248c2ecf20Sopenharmony_ci cpu_relax(); 1258c2ecf20Sopenharmony_ci data = readl_relaxed(sys_status); 1268c2ecf20Sopenharmony_ci } while (data & CLUSTER_DEBUG_RESET_STATUS); 1278c2ecf20Sopenharmony_ci hip04_set_snoop_filter(cluster, 1); 1288c2ecf20Sopenharmony_ci } 1298c2ecf20Sopenharmony_ci 1308c2ecf20Sopenharmony_ci data = CORE_RESET_BIT(cpu) | NEON_RESET_BIT(cpu) | \ 1318c2ecf20Sopenharmony_ci CORE_DEBUG_RESET_BIT(cpu); 1328c2ecf20Sopenharmony_ci writel_relaxed(data, sys_dreq); 1338c2ecf20Sopenharmony_ci do { 1348c2ecf20Sopenharmony_ci cpu_relax(); 1358c2ecf20Sopenharmony_ci } while (data == readl_relaxed(sys_status)); 1368c2ecf20Sopenharmony_ci 1378c2ecf20Sopenharmony_ci /* 1388c2ecf20Sopenharmony_ci * We may fail to power up core again without this delay. 1398c2ecf20Sopenharmony_ci * It's not mentioned in document. It's found by test. 1408c2ecf20Sopenharmony_ci */ 1418c2ecf20Sopenharmony_ci udelay(20); 1428c2ecf20Sopenharmony_ci 1438c2ecf20Sopenharmony_ci arch_send_wakeup_ipi_mask(cpumask_of(l_cpu)); 1448c2ecf20Sopenharmony_ci 1458c2ecf20Sopenharmony_ciout: 1468c2ecf20Sopenharmony_ci hip04_cpu_table[cluster][cpu]++; 1478c2ecf20Sopenharmony_ci spin_unlock_irq(&boot_lock); 1488c2ecf20Sopenharmony_ci 1498c2ecf20Sopenharmony_ci return 0; 1508c2ecf20Sopenharmony_ci} 1518c2ecf20Sopenharmony_ci 1528c2ecf20Sopenharmony_ci#ifdef CONFIG_HOTPLUG_CPU 1538c2ecf20Sopenharmony_cistatic void hip04_cpu_die(unsigned int l_cpu) 1548c2ecf20Sopenharmony_ci{ 1558c2ecf20Sopenharmony_ci unsigned int mpidr, cpu, cluster; 1568c2ecf20Sopenharmony_ci bool last_man; 1578c2ecf20Sopenharmony_ci 1588c2ecf20Sopenharmony_ci mpidr = cpu_logical_map(l_cpu); 1598c2ecf20Sopenharmony_ci cpu = MPIDR_AFFINITY_LEVEL(mpidr, 0); 1608c2ecf20Sopenharmony_ci cluster = MPIDR_AFFINITY_LEVEL(mpidr, 1); 1618c2ecf20Sopenharmony_ci 1628c2ecf20Sopenharmony_ci spin_lock(&boot_lock); 1638c2ecf20Sopenharmony_ci hip04_cpu_table[cluster][cpu]--; 1648c2ecf20Sopenharmony_ci if (hip04_cpu_table[cluster][cpu] == 1) { 1658c2ecf20Sopenharmony_ci /* A power_up request went ahead of us. */ 1668c2ecf20Sopenharmony_ci spin_unlock(&boot_lock); 1678c2ecf20Sopenharmony_ci return; 1688c2ecf20Sopenharmony_ci } else if (hip04_cpu_table[cluster][cpu] > 1) { 1698c2ecf20Sopenharmony_ci pr_err("Cluster %d CPU%d boots multiple times\n", cluster, cpu); 1708c2ecf20Sopenharmony_ci BUG(); 1718c2ecf20Sopenharmony_ci } 1728c2ecf20Sopenharmony_ci 1738c2ecf20Sopenharmony_ci last_man = hip04_cluster_is_down(cluster); 1748c2ecf20Sopenharmony_ci spin_unlock(&boot_lock); 1758c2ecf20Sopenharmony_ci if (last_man) { 1768c2ecf20Sopenharmony_ci /* Since it's Cortex A15, disable L2 prefetching. */ 1778c2ecf20Sopenharmony_ci asm volatile( 1788c2ecf20Sopenharmony_ci "mcr p15, 1, %0, c15, c0, 3 \n\t" 1798c2ecf20Sopenharmony_ci "isb \n\t" 1808c2ecf20Sopenharmony_ci "dsb " 1818c2ecf20Sopenharmony_ci : : "r" (0x400) ); 1828c2ecf20Sopenharmony_ci v7_exit_coherency_flush(all); 1838c2ecf20Sopenharmony_ci } else { 1848c2ecf20Sopenharmony_ci v7_exit_coherency_flush(louis); 1858c2ecf20Sopenharmony_ci } 1868c2ecf20Sopenharmony_ci 1878c2ecf20Sopenharmony_ci for (;;) 1888c2ecf20Sopenharmony_ci wfi(); 1898c2ecf20Sopenharmony_ci} 1908c2ecf20Sopenharmony_ci 1918c2ecf20Sopenharmony_cistatic int hip04_cpu_kill(unsigned int l_cpu) 1928c2ecf20Sopenharmony_ci{ 1938c2ecf20Sopenharmony_ci unsigned int mpidr, cpu, cluster; 1948c2ecf20Sopenharmony_ci unsigned int data, tries, count; 1958c2ecf20Sopenharmony_ci 1968c2ecf20Sopenharmony_ci mpidr = cpu_logical_map(l_cpu); 1978c2ecf20Sopenharmony_ci cpu = MPIDR_AFFINITY_LEVEL(mpidr, 0); 1988c2ecf20Sopenharmony_ci cluster = MPIDR_AFFINITY_LEVEL(mpidr, 1); 1998c2ecf20Sopenharmony_ci BUG_ON(cluster >= HIP04_MAX_CLUSTERS || 2008c2ecf20Sopenharmony_ci cpu >= HIP04_MAX_CPUS_PER_CLUSTER); 2018c2ecf20Sopenharmony_ci 2028c2ecf20Sopenharmony_ci count = TIMEOUT_MSEC / POLL_MSEC; 2038c2ecf20Sopenharmony_ci spin_lock_irq(&boot_lock); 2048c2ecf20Sopenharmony_ci for (tries = 0; tries < count; tries++) { 2058c2ecf20Sopenharmony_ci if (hip04_cpu_table[cluster][cpu]) 2068c2ecf20Sopenharmony_ci goto err; 2078c2ecf20Sopenharmony_ci cpu_relax(); 2088c2ecf20Sopenharmony_ci data = readl_relaxed(sysctrl + SC_CPU_RESET_STATUS(cluster)); 2098c2ecf20Sopenharmony_ci if (data & CORE_WFI_STATUS(cpu)) 2108c2ecf20Sopenharmony_ci break; 2118c2ecf20Sopenharmony_ci spin_unlock_irq(&boot_lock); 2128c2ecf20Sopenharmony_ci /* Wait for clean L2 when the whole cluster is down. */ 2138c2ecf20Sopenharmony_ci msleep(POLL_MSEC); 2148c2ecf20Sopenharmony_ci spin_lock_irq(&boot_lock); 2158c2ecf20Sopenharmony_ci } 2168c2ecf20Sopenharmony_ci if (tries >= count) 2178c2ecf20Sopenharmony_ci goto err; 2188c2ecf20Sopenharmony_ci data = CORE_RESET_BIT(cpu) | NEON_RESET_BIT(cpu) | \ 2198c2ecf20Sopenharmony_ci CORE_DEBUG_RESET_BIT(cpu); 2208c2ecf20Sopenharmony_ci writel_relaxed(data, sysctrl + SC_CPU_RESET_REQ(cluster)); 2218c2ecf20Sopenharmony_ci for (tries = 0; tries < count; tries++) { 2228c2ecf20Sopenharmony_ci cpu_relax(); 2238c2ecf20Sopenharmony_ci data = readl_relaxed(sysctrl + SC_CPU_RESET_STATUS(cluster)); 2248c2ecf20Sopenharmony_ci if (data & CORE_RESET_STATUS(cpu)) 2258c2ecf20Sopenharmony_ci break; 2268c2ecf20Sopenharmony_ci } 2278c2ecf20Sopenharmony_ci if (tries >= count) 2288c2ecf20Sopenharmony_ci goto err; 2298c2ecf20Sopenharmony_ci if (hip04_cluster_is_down(cluster)) 2308c2ecf20Sopenharmony_ci hip04_set_snoop_filter(cluster, 0); 2318c2ecf20Sopenharmony_ci spin_unlock_irq(&boot_lock); 2328c2ecf20Sopenharmony_ci return 1; 2338c2ecf20Sopenharmony_cierr: 2348c2ecf20Sopenharmony_ci spin_unlock_irq(&boot_lock); 2358c2ecf20Sopenharmony_ci return 0; 2368c2ecf20Sopenharmony_ci} 2378c2ecf20Sopenharmony_ci#endif 2388c2ecf20Sopenharmony_ci 2398c2ecf20Sopenharmony_cistatic const struct smp_operations hip04_smp_ops __initconst = { 2408c2ecf20Sopenharmony_ci .smp_boot_secondary = hip04_boot_secondary, 2418c2ecf20Sopenharmony_ci#ifdef CONFIG_HOTPLUG_CPU 2428c2ecf20Sopenharmony_ci .cpu_die = hip04_cpu_die, 2438c2ecf20Sopenharmony_ci .cpu_kill = hip04_cpu_kill, 2448c2ecf20Sopenharmony_ci#endif 2458c2ecf20Sopenharmony_ci}; 2468c2ecf20Sopenharmony_ci 2478c2ecf20Sopenharmony_cistatic bool __init hip04_cpu_table_init(void) 2488c2ecf20Sopenharmony_ci{ 2498c2ecf20Sopenharmony_ci unsigned int mpidr, cpu, cluster; 2508c2ecf20Sopenharmony_ci 2518c2ecf20Sopenharmony_ci mpidr = read_cpuid_mpidr(); 2528c2ecf20Sopenharmony_ci cpu = MPIDR_AFFINITY_LEVEL(mpidr, 0); 2538c2ecf20Sopenharmony_ci cluster = MPIDR_AFFINITY_LEVEL(mpidr, 1); 2548c2ecf20Sopenharmony_ci 2558c2ecf20Sopenharmony_ci if (cluster >= HIP04_MAX_CLUSTERS || 2568c2ecf20Sopenharmony_ci cpu >= HIP04_MAX_CPUS_PER_CLUSTER) { 2578c2ecf20Sopenharmony_ci pr_err("%s: boot CPU is out of bound!\n", __func__); 2588c2ecf20Sopenharmony_ci return false; 2598c2ecf20Sopenharmony_ci } 2608c2ecf20Sopenharmony_ci hip04_set_snoop_filter(cluster, 1); 2618c2ecf20Sopenharmony_ci hip04_cpu_table[cluster][cpu] = 1; 2628c2ecf20Sopenharmony_ci return true; 2638c2ecf20Sopenharmony_ci} 2648c2ecf20Sopenharmony_ci 2658c2ecf20Sopenharmony_cistatic int __init hip04_smp_init(void) 2668c2ecf20Sopenharmony_ci{ 2678c2ecf20Sopenharmony_ci struct device_node *np, *np_sctl, *np_fab; 2688c2ecf20Sopenharmony_ci struct resource fab_res; 2698c2ecf20Sopenharmony_ci void __iomem *relocation; 2708c2ecf20Sopenharmony_ci int ret = -ENODEV; 2718c2ecf20Sopenharmony_ci 2728c2ecf20Sopenharmony_ci np = of_find_compatible_node(NULL, NULL, "hisilicon,hip04-bootwrapper"); 2738c2ecf20Sopenharmony_ci if (!np) 2748c2ecf20Sopenharmony_ci goto err; 2758c2ecf20Sopenharmony_ci ret = of_property_read_u32_array(np, "boot-method", 2768c2ecf20Sopenharmony_ci &hip04_boot_method[0], 4); 2778c2ecf20Sopenharmony_ci if (ret) 2788c2ecf20Sopenharmony_ci goto err; 2798c2ecf20Sopenharmony_ci 2808c2ecf20Sopenharmony_ci ret = -ENODEV; 2818c2ecf20Sopenharmony_ci np_sctl = of_find_compatible_node(NULL, NULL, "hisilicon,sysctrl"); 2828c2ecf20Sopenharmony_ci if (!np_sctl) 2838c2ecf20Sopenharmony_ci goto err; 2848c2ecf20Sopenharmony_ci np_fab = of_find_compatible_node(NULL, NULL, "hisilicon,hip04-fabric"); 2858c2ecf20Sopenharmony_ci if (!np_fab) 2868c2ecf20Sopenharmony_ci goto err; 2878c2ecf20Sopenharmony_ci 2888c2ecf20Sopenharmony_ci ret = memblock_reserve(hip04_boot_method[0], hip04_boot_method[1]); 2898c2ecf20Sopenharmony_ci if (ret) 2908c2ecf20Sopenharmony_ci goto err; 2918c2ecf20Sopenharmony_ci 2928c2ecf20Sopenharmony_ci relocation = ioremap(hip04_boot_method[2], hip04_boot_method[3]); 2938c2ecf20Sopenharmony_ci if (!relocation) { 2948c2ecf20Sopenharmony_ci pr_err("failed to map relocation space\n"); 2958c2ecf20Sopenharmony_ci ret = -ENOMEM; 2968c2ecf20Sopenharmony_ci goto err_reloc; 2978c2ecf20Sopenharmony_ci } 2988c2ecf20Sopenharmony_ci sysctrl = of_iomap(np_sctl, 0); 2998c2ecf20Sopenharmony_ci if (!sysctrl) { 3008c2ecf20Sopenharmony_ci pr_err("failed to get sysctrl base\n"); 3018c2ecf20Sopenharmony_ci ret = -ENOMEM; 3028c2ecf20Sopenharmony_ci goto err_sysctrl; 3038c2ecf20Sopenharmony_ci } 3048c2ecf20Sopenharmony_ci ret = of_address_to_resource(np_fab, 0, &fab_res); 3058c2ecf20Sopenharmony_ci if (ret) { 3068c2ecf20Sopenharmony_ci pr_err("failed to get fabric base phys\n"); 3078c2ecf20Sopenharmony_ci goto err_fabric; 3088c2ecf20Sopenharmony_ci } 3098c2ecf20Sopenharmony_ci fabric_phys_addr = fab_res.start; 3108c2ecf20Sopenharmony_ci sync_cache_w(&fabric_phys_addr); 3118c2ecf20Sopenharmony_ci fabric = of_iomap(np_fab, 0); 3128c2ecf20Sopenharmony_ci if (!fabric) { 3138c2ecf20Sopenharmony_ci pr_err("failed to get fabric base\n"); 3148c2ecf20Sopenharmony_ci ret = -ENOMEM; 3158c2ecf20Sopenharmony_ci goto err_fabric; 3168c2ecf20Sopenharmony_ci } 3178c2ecf20Sopenharmony_ci 3188c2ecf20Sopenharmony_ci if (!hip04_cpu_table_init()) { 3198c2ecf20Sopenharmony_ci ret = -EINVAL; 3208c2ecf20Sopenharmony_ci goto err_table; 3218c2ecf20Sopenharmony_ci } 3228c2ecf20Sopenharmony_ci 3238c2ecf20Sopenharmony_ci /* 3248c2ecf20Sopenharmony_ci * Fill the instruction address that is used after secondary core 3258c2ecf20Sopenharmony_ci * out of reset. 3268c2ecf20Sopenharmony_ci */ 3278c2ecf20Sopenharmony_ci writel_relaxed(hip04_boot_method[0], relocation); 3288c2ecf20Sopenharmony_ci writel_relaxed(0xa5a5a5a5, relocation + 4); /* magic number */ 3298c2ecf20Sopenharmony_ci writel_relaxed(__pa_symbol(secondary_startup), relocation + 8); 3308c2ecf20Sopenharmony_ci writel_relaxed(0, relocation + 12); 3318c2ecf20Sopenharmony_ci iounmap(relocation); 3328c2ecf20Sopenharmony_ci 3338c2ecf20Sopenharmony_ci smp_set_ops(&hip04_smp_ops); 3348c2ecf20Sopenharmony_ci return ret; 3358c2ecf20Sopenharmony_cierr_table: 3368c2ecf20Sopenharmony_ci iounmap(fabric); 3378c2ecf20Sopenharmony_cierr_fabric: 3388c2ecf20Sopenharmony_ci iounmap(sysctrl); 3398c2ecf20Sopenharmony_cierr_sysctrl: 3408c2ecf20Sopenharmony_ci iounmap(relocation); 3418c2ecf20Sopenharmony_cierr_reloc: 3428c2ecf20Sopenharmony_ci memblock_free(hip04_boot_method[0], hip04_boot_method[1]); 3438c2ecf20Sopenharmony_cierr: 3448c2ecf20Sopenharmony_ci return ret; 3458c2ecf20Sopenharmony_ci} 3468c2ecf20Sopenharmony_ciearly_initcall(hip04_smp_init); 347