175a6faf6SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only 29cdc9991SHaojian Zhuang /* 39cdc9991SHaojian Zhuang * Copyright (c) 2013-2014 Linaro Ltd. 4*c1ce9d80SHao Fang * Copyright (c) 2013-2014 HiSilicon Limited. 59cdc9991SHaojian Zhuang */ 6905cdf9dSNicolas Pitre #include <linux/init.h> 7905cdf9dSNicolas Pitre #include <linux/smp.h> 89cdc9991SHaojian Zhuang #include <linux/delay.h> 99cdc9991SHaojian Zhuang #include <linux/io.h> 109cdc9991SHaojian Zhuang #include <linux/memblock.h> 119cdc9991SHaojian Zhuang #include <linux/of_address.h> 129cdc9991SHaojian Zhuang 139cdc9991SHaojian Zhuang #include <asm/cputype.h> 149cdc9991SHaojian Zhuang #include <asm/cp15.h> 15905cdf9dSNicolas Pitre #include <asm/cacheflush.h> 16905cdf9dSNicolas Pitre #include <asm/smp.h> 17905cdf9dSNicolas Pitre #include <asm/smp_plat.h> 189cdc9991SHaojian Zhuang 199cdc9991SHaojian Zhuang #include "core.h" 209cdc9991SHaojian Zhuang 219cdc9991SHaojian Zhuang /* bits definition in SC_CPU_RESET_REQ[x]/SC_CPU_RESET_DREQ[x] 229cdc9991SHaojian Zhuang * 1 -- unreset; 0 -- reset 239cdc9991SHaojian Zhuang */ 249cdc9991SHaojian Zhuang #define CORE_RESET_BIT(x) (1 << x) 259cdc9991SHaojian Zhuang #define NEON_RESET_BIT(x) (1 << (x + 4)) 269cdc9991SHaojian Zhuang #define CORE_DEBUG_RESET_BIT(x) (1 << (x + 9)) 279cdc9991SHaojian Zhuang #define CLUSTER_L2_RESET_BIT (1 << 8) 289cdc9991SHaojian Zhuang #define CLUSTER_DEBUG_RESET_BIT (1 << 13) 299cdc9991SHaojian Zhuang 309cdc9991SHaojian Zhuang /* 319cdc9991SHaojian Zhuang * bits definition in SC_CPU_RESET_STATUS[x] 329cdc9991SHaojian Zhuang * 1 -- reset status; 0 -- unreset status 339cdc9991SHaojian Zhuang */ 349cdc9991SHaojian Zhuang #define CORE_RESET_STATUS(x) (1 << x) 359cdc9991SHaojian Zhuang #define NEON_RESET_STATUS(x) (1 << (x + 4)) 369cdc9991SHaojian Zhuang #define CORE_DEBUG_RESET_STATUS(x) (1 << (x + 9)) 379cdc9991SHaojian Zhuang #define CLUSTER_L2_RESET_STATUS (1 << 8) 389cdc9991SHaojian Zhuang #define CLUSTER_DEBUG_RESET_STATUS (1 << 13) 399cdc9991SHaojian Zhuang #define CORE_WFI_STATUS(x) (1 << (x + 16)) 409cdc9991SHaojian Zhuang #define CORE_WFE_STATUS(x) (1 << (x + 20)) 419cdc9991SHaojian Zhuang #define CORE_DEBUG_ACK(x) (1 << (x + 24)) 429cdc9991SHaojian Zhuang 439cdc9991SHaojian Zhuang #define SC_CPU_RESET_REQ(x) (0x520 + (x << 3)) /* reset */ 449cdc9991SHaojian Zhuang #define SC_CPU_RESET_DREQ(x) (0x524 + (x << 3)) /* unreset */ 459cdc9991SHaojian Zhuang #define SC_CPU_RESET_STATUS(x) (0x1520 + (x << 3)) 469cdc9991SHaojian Zhuang 479cdc9991SHaojian Zhuang #define FAB_SF_MODE 0x0c 489cdc9991SHaojian Zhuang #define FAB_SF_INVLD 0x10 499cdc9991SHaojian Zhuang 509cdc9991SHaojian Zhuang /* bits definition in FB_SF_INVLD */ 519cdc9991SHaojian Zhuang #define FB_SF_INVLD_START (1 << 8) 529cdc9991SHaojian Zhuang 539cdc9991SHaojian Zhuang #define HIP04_MAX_CLUSTERS 4 549cdc9991SHaojian Zhuang #define HIP04_MAX_CPUS_PER_CLUSTER 4 559cdc9991SHaojian Zhuang 569cdc9991SHaojian Zhuang #define POLL_MSEC 10 579cdc9991SHaojian Zhuang #define TIMEOUT_MSEC 1000 589cdc9991SHaojian Zhuang 599cdc9991SHaojian Zhuang static void __iomem *sysctrl, *fabric; 609cdc9991SHaojian Zhuang static int hip04_cpu_table[HIP04_MAX_CLUSTERS][HIP04_MAX_CPUS_PER_CLUSTER]; 619cdc9991SHaojian Zhuang static DEFINE_SPINLOCK(boot_lock); 629cdc9991SHaojian Zhuang static u32 fabric_phys_addr; 639cdc9991SHaojian Zhuang /* 649cdc9991SHaojian Zhuang * [0]: bootwrapper physical address 659cdc9991SHaojian Zhuang * [1]: bootwrapper size 669cdc9991SHaojian Zhuang * [2]: relocation address 679cdc9991SHaojian Zhuang * [3]: relocation size 689cdc9991SHaojian Zhuang */ 699cdc9991SHaojian Zhuang static u32 hip04_boot_method[4]; 709cdc9991SHaojian Zhuang 719cdc9991SHaojian Zhuang static bool hip04_cluster_is_down(unsigned int cluster) 729cdc9991SHaojian Zhuang { 739cdc9991SHaojian Zhuang int i; 749cdc9991SHaojian Zhuang 759cdc9991SHaojian Zhuang for (i = 0; i < HIP04_MAX_CPUS_PER_CLUSTER; i++) 769cdc9991SHaojian Zhuang if (hip04_cpu_table[cluster][i]) 779cdc9991SHaojian Zhuang return false; 789cdc9991SHaojian Zhuang return true; 799cdc9991SHaojian Zhuang } 809cdc9991SHaojian Zhuang 819cdc9991SHaojian Zhuang static void hip04_set_snoop_filter(unsigned int cluster, unsigned int on) 829cdc9991SHaojian Zhuang { 839cdc9991SHaojian Zhuang unsigned long data; 849cdc9991SHaojian Zhuang 859cdc9991SHaojian Zhuang if (!fabric) 869cdc9991SHaojian Zhuang BUG(); 879cdc9991SHaojian Zhuang data = readl_relaxed(fabric + FAB_SF_MODE); 889cdc9991SHaojian Zhuang if (on) 899cdc9991SHaojian Zhuang data |= 1 << cluster; 909cdc9991SHaojian Zhuang else 919cdc9991SHaojian Zhuang data &= ~(1 << cluster); 929cdc9991SHaojian Zhuang writel_relaxed(data, fabric + FAB_SF_MODE); 939cdc9991SHaojian Zhuang do { 949cdc9991SHaojian Zhuang cpu_relax(); 959cdc9991SHaojian Zhuang } while (data != readl_relaxed(fabric + FAB_SF_MODE)); 969cdc9991SHaojian Zhuang } 979cdc9991SHaojian Zhuang 98905cdf9dSNicolas Pitre static int hip04_boot_secondary(unsigned int l_cpu, struct task_struct *idle) 999cdc9991SHaojian Zhuang { 100905cdf9dSNicolas Pitre unsigned int mpidr, cpu, cluster; 1019cdc9991SHaojian Zhuang unsigned long data; 1029cdc9991SHaojian Zhuang void __iomem *sys_dreq, *sys_status; 1039cdc9991SHaojian Zhuang 104905cdf9dSNicolas Pitre mpidr = cpu_logical_map(l_cpu); 105905cdf9dSNicolas Pitre cpu = MPIDR_AFFINITY_LEVEL(mpidr, 0); 106905cdf9dSNicolas Pitre cluster = MPIDR_AFFINITY_LEVEL(mpidr, 1); 107905cdf9dSNicolas Pitre 1089cdc9991SHaojian Zhuang if (!sysctrl) 1099cdc9991SHaojian Zhuang return -ENODEV; 1109cdc9991SHaojian Zhuang if (cluster >= HIP04_MAX_CLUSTERS || cpu >= HIP04_MAX_CPUS_PER_CLUSTER) 1119cdc9991SHaojian Zhuang return -EINVAL; 1129cdc9991SHaojian Zhuang 1139cdc9991SHaojian Zhuang spin_lock_irq(&boot_lock); 1149cdc9991SHaojian Zhuang 1159cdc9991SHaojian Zhuang if (hip04_cpu_table[cluster][cpu]) 1169cdc9991SHaojian Zhuang goto out; 1179cdc9991SHaojian Zhuang 1189cdc9991SHaojian Zhuang sys_dreq = sysctrl + SC_CPU_RESET_DREQ(cluster); 1199cdc9991SHaojian Zhuang sys_status = sysctrl + SC_CPU_RESET_STATUS(cluster); 1209cdc9991SHaojian Zhuang if (hip04_cluster_is_down(cluster)) { 1219cdc9991SHaojian Zhuang data = CLUSTER_DEBUG_RESET_BIT; 1229cdc9991SHaojian Zhuang writel_relaxed(data, sys_dreq); 1239cdc9991SHaojian Zhuang do { 1249cdc9991SHaojian Zhuang cpu_relax(); 1259cdc9991SHaojian Zhuang data = readl_relaxed(sys_status); 1269cdc9991SHaojian Zhuang } while (data & CLUSTER_DEBUG_RESET_STATUS); 127905cdf9dSNicolas Pitre hip04_set_snoop_filter(cluster, 1); 1289cdc9991SHaojian Zhuang } 1299cdc9991SHaojian Zhuang 1309cdc9991SHaojian Zhuang data = CORE_RESET_BIT(cpu) | NEON_RESET_BIT(cpu) | \ 1319cdc9991SHaojian Zhuang CORE_DEBUG_RESET_BIT(cpu); 1329cdc9991SHaojian Zhuang writel_relaxed(data, sys_dreq); 1339cdc9991SHaojian Zhuang do { 1349cdc9991SHaojian Zhuang cpu_relax(); 1359cdc9991SHaojian Zhuang } while (data == readl_relaxed(sys_status)); 136905cdf9dSNicolas Pitre 1379cdc9991SHaojian Zhuang /* 1389cdc9991SHaojian Zhuang * We may fail to power up core again without this delay. 1399cdc9991SHaojian Zhuang * It's not mentioned in document. It's found by test. 1409cdc9991SHaojian Zhuang */ 1419cdc9991SHaojian Zhuang udelay(20); 142905cdf9dSNicolas Pitre 143905cdf9dSNicolas Pitre arch_send_wakeup_ipi_mask(cpumask_of(l_cpu)); 144905cdf9dSNicolas Pitre 1459cdc9991SHaojian Zhuang out: 1469cdc9991SHaojian Zhuang hip04_cpu_table[cluster][cpu]++; 1479cdc9991SHaojian Zhuang spin_unlock_irq(&boot_lock); 1489cdc9991SHaojian Zhuang 1499cdc9991SHaojian Zhuang return 0; 1509cdc9991SHaojian Zhuang } 1519cdc9991SHaojian Zhuang 1524c9e0f76SArnd Bergmann #ifdef CONFIG_HOTPLUG_CPU 153905cdf9dSNicolas Pitre static void hip04_cpu_die(unsigned int l_cpu) 1549cdc9991SHaojian Zhuang { 1559cdc9991SHaojian Zhuang unsigned int mpidr, cpu, cluster; 156905cdf9dSNicolas Pitre bool last_man; 1579cdc9991SHaojian Zhuang 158905cdf9dSNicolas Pitre mpidr = cpu_logical_map(l_cpu); 1599cdc9991SHaojian Zhuang cpu = MPIDR_AFFINITY_LEVEL(mpidr, 0); 1609cdc9991SHaojian Zhuang cluster = MPIDR_AFFINITY_LEVEL(mpidr, 1); 1619cdc9991SHaojian Zhuang 1629cdc9991SHaojian Zhuang spin_lock(&boot_lock); 1639cdc9991SHaojian Zhuang hip04_cpu_table[cluster][cpu]--; 1649cdc9991SHaojian Zhuang if (hip04_cpu_table[cluster][cpu] == 1) { 1659cdc9991SHaojian Zhuang /* A power_up request went ahead of us. */ 166905cdf9dSNicolas Pitre spin_unlock(&boot_lock); 167905cdf9dSNicolas Pitre return; 1689cdc9991SHaojian Zhuang } else if (hip04_cpu_table[cluster][cpu] > 1) { 1699cdc9991SHaojian Zhuang pr_err("Cluster %d CPU%d boots multiple times\n", cluster, cpu); 1709cdc9991SHaojian Zhuang BUG(); 1719cdc9991SHaojian Zhuang } 1729cdc9991SHaojian Zhuang 1739cdc9991SHaojian Zhuang last_man = hip04_cluster_is_down(cluster); 1749cdc9991SHaojian Zhuang spin_unlock(&boot_lock); 175905cdf9dSNicolas Pitre if (last_man) { 1769cdc9991SHaojian Zhuang /* Since it's Cortex A15, disable L2 prefetching. */ 1779cdc9991SHaojian Zhuang asm volatile( 1789cdc9991SHaojian Zhuang "mcr p15, 1, %0, c15, c0, 3 \n\t" 1799cdc9991SHaojian Zhuang "isb \n\t" 1809cdc9991SHaojian Zhuang "dsb " 1819cdc9991SHaojian Zhuang : : "r" (0x400) ); 1829cdc9991SHaojian Zhuang v7_exit_coherency_flush(all); 1839cdc9991SHaojian Zhuang } else { 1849cdc9991SHaojian Zhuang v7_exit_coherency_flush(louis); 1859cdc9991SHaojian Zhuang } 1869cdc9991SHaojian Zhuang 187905cdf9dSNicolas Pitre for (;;) 1889cdc9991SHaojian Zhuang wfi(); 1899cdc9991SHaojian Zhuang } 1909cdc9991SHaojian Zhuang 191905cdf9dSNicolas Pitre static int hip04_cpu_kill(unsigned int l_cpu) 1929cdc9991SHaojian Zhuang { 193905cdf9dSNicolas Pitre unsigned int mpidr, cpu, cluster; 1949cdc9991SHaojian Zhuang unsigned int data, tries, count; 1959cdc9991SHaojian Zhuang 196905cdf9dSNicolas Pitre mpidr = cpu_logical_map(l_cpu); 197905cdf9dSNicolas Pitre cpu = MPIDR_AFFINITY_LEVEL(mpidr, 0); 198905cdf9dSNicolas Pitre cluster = MPIDR_AFFINITY_LEVEL(mpidr, 1); 1999cdc9991SHaojian Zhuang BUG_ON(cluster >= HIP04_MAX_CLUSTERS || 2009cdc9991SHaojian Zhuang cpu >= HIP04_MAX_CPUS_PER_CLUSTER); 2019cdc9991SHaojian Zhuang 2029cdc9991SHaojian Zhuang count = TIMEOUT_MSEC / POLL_MSEC; 2039cdc9991SHaojian Zhuang spin_lock_irq(&boot_lock); 2049cdc9991SHaojian Zhuang for (tries = 0; tries < count; tries++) { 205905cdf9dSNicolas Pitre if (hip04_cpu_table[cluster][cpu]) 2069cdc9991SHaojian Zhuang goto err; 2079cdc9991SHaojian Zhuang cpu_relax(); 2089cdc9991SHaojian Zhuang data = readl_relaxed(sysctrl + SC_CPU_RESET_STATUS(cluster)); 2099cdc9991SHaojian Zhuang if (data & CORE_WFI_STATUS(cpu)) 2109cdc9991SHaojian Zhuang break; 2119cdc9991SHaojian Zhuang spin_unlock_irq(&boot_lock); 2129cdc9991SHaojian Zhuang /* Wait for clean L2 when the whole cluster is down. */ 2139cdc9991SHaojian Zhuang msleep(POLL_MSEC); 2149cdc9991SHaojian Zhuang spin_lock_irq(&boot_lock); 2159cdc9991SHaojian Zhuang } 2169cdc9991SHaojian Zhuang if (tries >= count) 2179cdc9991SHaojian Zhuang goto err; 2189cdc9991SHaojian Zhuang data = CORE_RESET_BIT(cpu) | NEON_RESET_BIT(cpu) | \ 2199cdc9991SHaojian Zhuang CORE_DEBUG_RESET_BIT(cpu); 2209cdc9991SHaojian Zhuang writel_relaxed(data, sysctrl + SC_CPU_RESET_REQ(cluster)); 2219cdc9991SHaojian Zhuang for (tries = 0; tries < count; tries++) { 2229cdc9991SHaojian Zhuang cpu_relax(); 2239cdc9991SHaojian Zhuang data = readl_relaxed(sysctrl + SC_CPU_RESET_STATUS(cluster)); 2249cdc9991SHaojian Zhuang if (data & CORE_RESET_STATUS(cpu)) 2259cdc9991SHaojian Zhuang break; 2269cdc9991SHaojian Zhuang } 2279cdc9991SHaojian Zhuang if (tries >= count) 2289cdc9991SHaojian Zhuang goto err; 229905cdf9dSNicolas Pitre if (hip04_cluster_is_down(cluster)) 230905cdf9dSNicolas Pitre hip04_set_snoop_filter(cluster, 0); 2319cdc9991SHaojian Zhuang spin_unlock_irq(&boot_lock); 232905cdf9dSNicolas Pitre return 1; 2339cdc9991SHaojian Zhuang err: 2349cdc9991SHaojian Zhuang spin_unlock_irq(&boot_lock); 235905cdf9dSNicolas Pitre return 0; 2369cdc9991SHaojian Zhuang } 2374c9e0f76SArnd Bergmann #endif 2389cdc9991SHaojian Zhuang 23975305275SMasahiro Yamada static const struct smp_operations hip04_smp_ops __initconst = { 240905cdf9dSNicolas Pitre .smp_boot_secondary = hip04_boot_secondary, 2414c9e0f76SArnd Bergmann #ifdef CONFIG_HOTPLUG_CPU 242905cdf9dSNicolas Pitre .cpu_die = hip04_cpu_die, 243905cdf9dSNicolas Pitre .cpu_kill = hip04_cpu_kill, 2444c9e0f76SArnd Bergmann #endif 2459cdc9991SHaojian Zhuang }; 2469cdc9991SHaojian Zhuang 2479cdc9991SHaojian Zhuang static bool __init hip04_cpu_table_init(void) 2489cdc9991SHaojian Zhuang { 2499cdc9991SHaojian Zhuang unsigned int mpidr, cpu, cluster; 2509cdc9991SHaojian Zhuang 2519cdc9991SHaojian Zhuang mpidr = read_cpuid_mpidr(); 2529cdc9991SHaojian Zhuang cpu = MPIDR_AFFINITY_LEVEL(mpidr, 0); 2539cdc9991SHaojian Zhuang cluster = MPIDR_AFFINITY_LEVEL(mpidr, 1); 2549cdc9991SHaojian Zhuang 2559cdc9991SHaojian Zhuang if (cluster >= HIP04_MAX_CLUSTERS || 2569cdc9991SHaojian Zhuang cpu >= HIP04_MAX_CPUS_PER_CLUSTER) { 2579cdc9991SHaojian Zhuang pr_err("%s: boot CPU is out of bound!\n", __func__); 2589cdc9991SHaojian Zhuang return false; 2599cdc9991SHaojian Zhuang } 2609cdc9991SHaojian Zhuang hip04_set_snoop_filter(cluster, 1); 2619cdc9991SHaojian Zhuang hip04_cpu_table[cluster][cpu] = 1; 2629cdc9991SHaojian Zhuang return true; 2639cdc9991SHaojian Zhuang } 2649cdc9991SHaojian Zhuang 265905cdf9dSNicolas Pitre static int __init hip04_smp_init(void) 2669cdc9991SHaojian Zhuang { 2679cdc9991SHaojian Zhuang struct device_node *np, *np_sctl, *np_fab; 2689cdc9991SHaojian Zhuang struct resource fab_res; 2699cdc9991SHaojian Zhuang void __iomem *relocation; 2709cdc9991SHaojian Zhuang int ret = -ENODEV; 2719cdc9991SHaojian Zhuang 2729cdc9991SHaojian Zhuang np = of_find_compatible_node(NULL, NULL, "hisilicon,hip04-bootwrapper"); 2739cdc9991SHaojian Zhuang if (!np) 2749cdc9991SHaojian Zhuang goto err; 2759cdc9991SHaojian Zhuang ret = of_property_read_u32_array(np, "boot-method", 2769cdc9991SHaojian Zhuang &hip04_boot_method[0], 4); 2779cdc9991SHaojian Zhuang if (ret) 2789cdc9991SHaojian Zhuang goto err; 279f147140cSWei Yongjun 280f147140cSWei Yongjun ret = -ENODEV; 2819cdc9991SHaojian Zhuang np_sctl = of_find_compatible_node(NULL, NULL, "hisilicon,sysctrl"); 2829cdc9991SHaojian Zhuang if (!np_sctl) 2839cdc9991SHaojian Zhuang goto err; 2849cdc9991SHaojian Zhuang np_fab = of_find_compatible_node(NULL, NULL, "hisilicon,hip04-fabric"); 2859cdc9991SHaojian Zhuang if (!np_fab) 2869cdc9991SHaojian Zhuang goto err; 2879cdc9991SHaojian Zhuang 2889cdc9991SHaojian Zhuang ret = memblock_reserve(hip04_boot_method[0], hip04_boot_method[1]); 2899cdc9991SHaojian Zhuang if (ret) 2909cdc9991SHaojian Zhuang goto err; 2919cdc9991SHaojian Zhuang 2929cdc9991SHaojian Zhuang relocation = ioremap(hip04_boot_method[2], hip04_boot_method[3]); 2939cdc9991SHaojian Zhuang if (!relocation) { 2949cdc9991SHaojian Zhuang pr_err("failed to map relocation space\n"); 2959cdc9991SHaojian Zhuang ret = -ENOMEM; 2969cdc9991SHaojian Zhuang goto err_reloc; 2979cdc9991SHaojian Zhuang } 2989cdc9991SHaojian Zhuang sysctrl = of_iomap(np_sctl, 0); 2999cdc9991SHaojian Zhuang if (!sysctrl) { 3009cdc9991SHaojian Zhuang pr_err("failed to get sysctrl base\n"); 3019cdc9991SHaojian Zhuang ret = -ENOMEM; 3029cdc9991SHaojian Zhuang goto err_sysctrl; 3039cdc9991SHaojian Zhuang } 3049cdc9991SHaojian Zhuang ret = of_address_to_resource(np_fab, 0, &fab_res); 3059cdc9991SHaojian Zhuang if (ret) { 3069cdc9991SHaojian Zhuang pr_err("failed to get fabric base phys\n"); 3079cdc9991SHaojian Zhuang goto err_fabric; 3089cdc9991SHaojian Zhuang } 3099cdc9991SHaojian Zhuang fabric_phys_addr = fab_res.start; 3109cdc9991SHaojian Zhuang sync_cache_w(&fabric_phys_addr); 3119cdc9991SHaojian Zhuang fabric = of_iomap(np_fab, 0); 3129cdc9991SHaojian Zhuang if (!fabric) { 3139cdc9991SHaojian Zhuang pr_err("failed to get fabric base\n"); 3149cdc9991SHaojian Zhuang ret = -ENOMEM; 3159cdc9991SHaojian Zhuang goto err_fabric; 3169cdc9991SHaojian Zhuang } 3179cdc9991SHaojian Zhuang 3189cdc9991SHaojian Zhuang if (!hip04_cpu_table_init()) { 3199cdc9991SHaojian Zhuang ret = -EINVAL; 3209cdc9991SHaojian Zhuang goto err_table; 3219cdc9991SHaojian Zhuang } 3229cdc9991SHaojian Zhuang 3239cdc9991SHaojian Zhuang /* 3249cdc9991SHaojian Zhuang * Fill the instruction address that is used after secondary core 3259cdc9991SHaojian Zhuang * out of reset. 3269cdc9991SHaojian Zhuang */ 3279cdc9991SHaojian Zhuang writel_relaxed(hip04_boot_method[0], relocation); 3289cdc9991SHaojian Zhuang writel_relaxed(0xa5a5a5a5, relocation + 4); /* magic number */ 32964fc2a94SFlorian Fainelli writel_relaxed(__pa_symbol(secondary_startup), relocation + 8); 3309cdc9991SHaojian Zhuang writel_relaxed(0, relocation + 12); 3319cdc9991SHaojian Zhuang iounmap(relocation); 3329cdc9991SHaojian Zhuang 333905cdf9dSNicolas Pitre smp_set_ops(&hip04_smp_ops); 3349cdc9991SHaojian Zhuang return ret; 3359cdc9991SHaojian Zhuang err_table: 3369cdc9991SHaojian Zhuang iounmap(fabric); 3379cdc9991SHaojian Zhuang err_fabric: 3389cdc9991SHaojian Zhuang iounmap(sysctrl); 3399cdc9991SHaojian Zhuang err_sysctrl: 3409cdc9991SHaojian Zhuang iounmap(relocation); 3419cdc9991SHaojian Zhuang err_reloc: 3429cdc9991SHaojian Zhuang memblock_free(hip04_boot_method[0], hip04_boot_method[1]); 3439cdc9991SHaojian Zhuang err: 3449cdc9991SHaojian Zhuang return ret; 3459cdc9991SHaojian Zhuang } 346905cdf9dSNicolas Pitre early_initcall(hip04_smp_init); 347