19cdc9991SHaojian Zhuang /* 29cdc9991SHaojian Zhuang * Copyright (c) 2013-2014 Linaro Ltd. 39cdc9991SHaojian Zhuang * Copyright (c) 2013-2014 Hisilicon Limited. 49cdc9991SHaojian Zhuang * 59cdc9991SHaojian Zhuang * This program is free software; you can redistribute it and/or modify it 69cdc9991SHaojian Zhuang * under the terms and conditions of the GNU General Public License, 79cdc9991SHaojian Zhuang * version 2, as published by the Free Software Foundation. 89cdc9991SHaojian Zhuang */ 9905cdf9dSNicolas Pitre #include <linux/init.h> 10905cdf9dSNicolas Pitre #include <linux/smp.h> 119cdc9991SHaojian Zhuang #include <linux/delay.h> 129cdc9991SHaojian Zhuang #include <linux/io.h> 139cdc9991SHaojian Zhuang #include <linux/memblock.h> 149cdc9991SHaojian Zhuang #include <linux/of_address.h> 159cdc9991SHaojian Zhuang 169cdc9991SHaojian Zhuang #include <asm/cputype.h> 179cdc9991SHaojian Zhuang #include <asm/cp15.h> 18905cdf9dSNicolas Pitre #include <asm/cacheflush.h> 19905cdf9dSNicolas Pitre #include <asm/smp.h> 20905cdf9dSNicolas Pitre #include <asm/smp_plat.h> 219cdc9991SHaojian Zhuang 229cdc9991SHaojian Zhuang #include "core.h" 239cdc9991SHaojian Zhuang 249cdc9991SHaojian Zhuang /* bits definition in SC_CPU_RESET_REQ[x]/SC_CPU_RESET_DREQ[x] 259cdc9991SHaojian Zhuang * 1 -- unreset; 0 -- reset 269cdc9991SHaojian Zhuang */ 279cdc9991SHaojian Zhuang #define CORE_RESET_BIT(x) (1 << x) 289cdc9991SHaojian Zhuang #define NEON_RESET_BIT(x) (1 << (x + 4)) 299cdc9991SHaojian Zhuang #define CORE_DEBUG_RESET_BIT(x) (1 << (x + 9)) 309cdc9991SHaojian Zhuang #define CLUSTER_L2_RESET_BIT (1 << 8) 319cdc9991SHaojian Zhuang #define CLUSTER_DEBUG_RESET_BIT (1 << 13) 329cdc9991SHaojian Zhuang 339cdc9991SHaojian Zhuang /* 349cdc9991SHaojian Zhuang * bits definition in SC_CPU_RESET_STATUS[x] 359cdc9991SHaojian Zhuang * 1 -- reset status; 0 -- unreset status 369cdc9991SHaojian Zhuang */ 379cdc9991SHaojian Zhuang #define CORE_RESET_STATUS(x) (1 << x) 389cdc9991SHaojian Zhuang #define NEON_RESET_STATUS(x) (1 << (x + 4)) 399cdc9991SHaojian Zhuang #define CORE_DEBUG_RESET_STATUS(x) (1 << (x + 9)) 409cdc9991SHaojian Zhuang #define CLUSTER_L2_RESET_STATUS (1 << 8) 419cdc9991SHaojian Zhuang #define CLUSTER_DEBUG_RESET_STATUS (1 << 13) 429cdc9991SHaojian Zhuang #define CORE_WFI_STATUS(x) (1 << (x + 16)) 439cdc9991SHaojian Zhuang #define CORE_WFE_STATUS(x) (1 << (x + 20)) 449cdc9991SHaojian Zhuang #define CORE_DEBUG_ACK(x) (1 << (x + 24)) 459cdc9991SHaojian Zhuang 469cdc9991SHaojian Zhuang #define SC_CPU_RESET_REQ(x) (0x520 + (x << 3)) /* reset */ 479cdc9991SHaojian Zhuang #define SC_CPU_RESET_DREQ(x) (0x524 + (x << 3)) /* unreset */ 489cdc9991SHaojian Zhuang #define SC_CPU_RESET_STATUS(x) (0x1520 + (x << 3)) 499cdc9991SHaojian Zhuang 509cdc9991SHaojian Zhuang #define FAB_SF_MODE 0x0c 519cdc9991SHaojian Zhuang #define FAB_SF_INVLD 0x10 529cdc9991SHaojian Zhuang 539cdc9991SHaojian Zhuang /* bits definition in FB_SF_INVLD */ 549cdc9991SHaojian Zhuang #define FB_SF_INVLD_START (1 << 8) 559cdc9991SHaojian Zhuang 569cdc9991SHaojian Zhuang #define HIP04_MAX_CLUSTERS 4 579cdc9991SHaojian Zhuang #define HIP04_MAX_CPUS_PER_CLUSTER 4 589cdc9991SHaojian Zhuang 599cdc9991SHaojian Zhuang #define POLL_MSEC 10 609cdc9991SHaojian Zhuang #define TIMEOUT_MSEC 1000 619cdc9991SHaojian Zhuang 629cdc9991SHaojian Zhuang static void __iomem *sysctrl, *fabric; 639cdc9991SHaojian Zhuang static int hip04_cpu_table[HIP04_MAX_CLUSTERS][HIP04_MAX_CPUS_PER_CLUSTER]; 649cdc9991SHaojian Zhuang static DEFINE_SPINLOCK(boot_lock); 659cdc9991SHaojian Zhuang static u32 fabric_phys_addr; 669cdc9991SHaojian Zhuang /* 679cdc9991SHaojian Zhuang * [0]: bootwrapper physical address 689cdc9991SHaojian Zhuang * [1]: bootwrapper size 699cdc9991SHaojian Zhuang * [2]: relocation address 709cdc9991SHaojian Zhuang * [3]: relocation size 719cdc9991SHaojian Zhuang */ 729cdc9991SHaojian Zhuang static u32 hip04_boot_method[4]; 739cdc9991SHaojian Zhuang 749cdc9991SHaojian Zhuang static bool hip04_cluster_is_down(unsigned int cluster) 759cdc9991SHaojian Zhuang { 769cdc9991SHaojian Zhuang int i; 779cdc9991SHaojian Zhuang 789cdc9991SHaojian Zhuang for (i = 0; i < HIP04_MAX_CPUS_PER_CLUSTER; i++) 799cdc9991SHaojian Zhuang if (hip04_cpu_table[cluster][i]) 809cdc9991SHaojian Zhuang return false; 819cdc9991SHaojian Zhuang return true; 829cdc9991SHaojian Zhuang } 839cdc9991SHaojian Zhuang 849cdc9991SHaojian Zhuang static void hip04_set_snoop_filter(unsigned int cluster, unsigned int on) 859cdc9991SHaojian Zhuang { 869cdc9991SHaojian Zhuang unsigned long data; 879cdc9991SHaojian Zhuang 889cdc9991SHaojian Zhuang if (!fabric) 899cdc9991SHaojian Zhuang BUG(); 909cdc9991SHaojian Zhuang data = readl_relaxed(fabric + FAB_SF_MODE); 919cdc9991SHaojian Zhuang if (on) 929cdc9991SHaojian Zhuang data |= 1 << cluster; 939cdc9991SHaojian Zhuang else 949cdc9991SHaojian Zhuang data &= ~(1 << cluster); 959cdc9991SHaojian Zhuang writel_relaxed(data, fabric + FAB_SF_MODE); 969cdc9991SHaojian Zhuang do { 979cdc9991SHaojian Zhuang cpu_relax(); 989cdc9991SHaojian Zhuang } while (data != readl_relaxed(fabric + FAB_SF_MODE)); 999cdc9991SHaojian Zhuang } 1009cdc9991SHaojian Zhuang 101905cdf9dSNicolas Pitre static int hip04_boot_secondary(unsigned int l_cpu, struct task_struct *idle) 1029cdc9991SHaojian Zhuang { 103905cdf9dSNicolas Pitre unsigned int mpidr, cpu, cluster; 1049cdc9991SHaojian Zhuang unsigned long data; 1059cdc9991SHaojian Zhuang void __iomem *sys_dreq, *sys_status; 1069cdc9991SHaojian Zhuang 107905cdf9dSNicolas Pitre mpidr = cpu_logical_map(l_cpu); 108905cdf9dSNicolas Pitre cpu = MPIDR_AFFINITY_LEVEL(mpidr, 0); 109905cdf9dSNicolas Pitre cluster = MPIDR_AFFINITY_LEVEL(mpidr, 1); 110905cdf9dSNicolas Pitre 1119cdc9991SHaojian Zhuang if (!sysctrl) 1129cdc9991SHaojian Zhuang return -ENODEV; 1139cdc9991SHaojian Zhuang if (cluster >= HIP04_MAX_CLUSTERS || cpu >= HIP04_MAX_CPUS_PER_CLUSTER) 1149cdc9991SHaojian Zhuang return -EINVAL; 1159cdc9991SHaojian Zhuang 1169cdc9991SHaojian Zhuang spin_lock_irq(&boot_lock); 1179cdc9991SHaojian Zhuang 1189cdc9991SHaojian Zhuang if (hip04_cpu_table[cluster][cpu]) 1199cdc9991SHaojian Zhuang goto out; 1209cdc9991SHaojian Zhuang 1219cdc9991SHaojian Zhuang sys_dreq = sysctrl + SC_CPU_RESET_DREQ(cluster); 1229cdc9991SHaojian Zhuang sys_status = sysctrl + SC_CPU_RESET_STATUS(cluster); 1239cdc9991SHaojian Zhuang if (hip04_cluster_is_down(cluster)) { 1249cdc9991SHaojian Zhuang data = CLUSTER_DEBUG_RESET_BIT; 1259cdc9991SHaojian Zhuang writel_relaxed(data, sys_dreq); 1269cdc9991SHaojian Zhuang do { 1279cdc9991SHaojian Zhuang cpu_relax(); 1289cdc9991SHaojian Zhuang data = readl_relaxed(sys_status); 1299cdc9991SHaojian Zhuang } while (data & CLUSTER_DEBUG_RESET_STATUS); 130905cdf9dSNicolas Pitre hip04_set_snoop_filter(cluster, 1); 1319cdc9991SHaojian Zhuang } 1329cdc9991SHaojian Zhuang 1339cdc9991SHaojian Zhuang data = CORE_RESET_BIT(cpu) | NEON_RESET_BIT(cpu) | \ 1349cdc9991SHaojian Zhuang CORE_DEBUG_RESET_BIT(cpu); 1359cdc9991SHaojian Zhuang writel_relaxed(data, sys_dreq); 1369cdc9991SHaojian Zhuang do { 1379cdc9991SHaojian Zhuang cpu_relax(); 1389cdc9991SHaojian Zhuang } while (data == readl_relaxed(sys_status)); 139905cdf9dSNicolas Pitre 1409cdc9991SHaojian Zhuang /* 1419cdc9991SHaojian Zhuang * We may fail to power up core again without this delay. 1429cdc9991SHaojian Zhuang * It's not mentioned in document. It's found by test. 1439cdc9991SHaojian Zhuang */ 1449cdc9991SHaojian Zhuang udelay(20); 145905cdf9dSNicolas Pitre 146905cdf9dSNicolas Pitre arch_send_wakeup_ipi_mask(cpumask_of(l_cpu)); 147905cdf9dSNicolas Pitre 1489cdc9991SHaojian Zhuang out: 1499cdc9991SHaojian Zhuang hip04_cpu_table[cluster][cpu]++; 1509cdc9991SHaojian Zhuang spin_unlock_irq(&boot_lock); 1519cdc9991SHaojian Zhuang 1529cdc9991SHaojian Zhuang return 0; 1539cdc9991SHaojian Zhuang } 1549cdc9991SHaojian Zhuang 1554c9e0f76SArnd Bergmann #ifdef CONFIG_HOTPLUG_CPU 156905cdf9dSNicolas Pitre static void hip04_cpu_die(unsigned int l_cpu) 1579cdc9991SHaojian Zhuang { 1589cdc9991SHaojian Zhuang unsigned int mpidr, cpu, cluster; 159905cdf9dSNicolas Pitre bool last_man; 1609cdc9991SHaojian Zhuang 161905cdf9dSNicolas Pitre mpidr = cpu_logical_map(l_cpu); 1629cdc9991SHaojian Zhuang cpu = MPIDR_AFFINITY_LEVEL(mpidr, 0); 1639cdc9991SHaojian Zhuang cluster = MPIDR_AFFINITY_LEVEL(mpidr, 1); 1649cdc9991SHaojian Zhuang 1659cdc9991SHaojian Zhuang spin_lock(&boot_lock); 1669cdc9991SHaojian Zhuang hip04_cpu_table[cluster][cpu]--; 1679cdc9991SHaojian Zhuang if (hip04_cpu_table[cluster][cpu] == 1) { 1689cdc9991SHaojian Zhuang /* A power_up request went ahead of us. */ 169905cdf9dSNicolas Pitre spin_unlock(&boot_lock); 170905cdf9dSNicolas Pitre return; 1719cdc9991SHaojian Zhuang } else if (hip04_cpu_table[cluster][cpu] > 1) { 1729cdc9991SHaojian Zhuang pr_err("Cluster %d CPU%d boots multiple times\n", cluster, cpu); 1739cdc9991SHaojian Zhuang BUG(); 1749cdc9991SHaojian Zhuang } 1759cdc9991SHaojian Zhuang 1769cdc9991SHaojian Zhuang last_man = hip04_cluster_is_down(cluster); 1779cdc9991SHaojian Zhuang spin_unlock(&boot_lock); 178905cdf9dSNicolas Pitre if (last_man) { 1799cdc9991SHaojian Zhuang /* Since it's Cortex A15, disable L2 prefetching. */ 1809cdc9991SHaojian Zhuang asm volatile( 1819cdc9991SHaojian Zhuang "mcr p15, 1, %0, c15, c0, 3 \n\t" 1829cdc9991SHaojian Zhuang "isb \n\t" 1839cdc9991SHaojian Zhuang "dsb " 1849cdc9991SHaojian Zhuang : : "r" (0x400) ); 1859cdc9991SHaojian Zhuang v7_exit_coherency_flush(all); 1869cdc9991SHaojian Zhuang } else { 1879cdc9991SHaojian Zhuang v7_exit_coherency_flush(louis); 1889cdc9991SHaojian Zhuang } 1899cdc9991SHaojian Zhuang 190905cdf9dSNicolas Pitre for (;;) 1919cdc9991SHaojian Zhuang wfi(); 1929cdc9991SHaojian Zhuang } 1939cdc9991SHaojian Zhuang 194905cdf9dSNicolas Pitre static int hip04_cpu_kill(unsigned int l_cpu) 1959cdc9991SHaojian Zhuang { 196905cdf9dSNicolas Pitre unsigned int mpidr, cpu, cluster; 1979cdc9991SHaojian Zhuang unsigned int data, tries, count; 1989cdc9991SHaojian Zhuang 199905cdf9dSNicolas Pitre mpidr = cpu_logical_map(l_cpu); 200905cdf9dSNicolas Pitre cpu = MPIDR_AFFINITY_LEVEL(mpidr, 0); 201905cdf9dSNicolas Pitre cluster = MPIDR_AFFINITY_LEVEL(mpidr, 1); 2029cdc9991SHaojian Zhuang BUG_ON(cluster >= HIP04_MAX_CLUSTERS || 2039cdc9991SHaojian Zhuang cpu >= HIP04_MAX_CPUS_PER_CLUSTER); 2049cdc9991SHaojian Zhuang 2059cdc9991SHaojian Zhuang count = TIMEOUT_MSEC / POLL_MSEC; 2069cdc9991SHaojian Zhuang spin_lock_irq(&boot_lock); 2079cdc9991SHaojian Zhuang for (tries = 0; tries < count; tries++) { 208905cdf9dSNicolas Pitre if (hip04_cpu_table[cluster][cpu]) 2099cdc9991SHaojian Zhuang goto err; 2109cdc9991SHaojian Zhuang cpu_relax(); 2119cdc9991SHaojian Zhuang data = readl_relaxed(sysctrl + SC_CPU_RESET_STATUS(cluster)); 2129cdc9991SHaojian Zhuang if (data & CORE_WFI_STATUS(cpu)) 2139cdc9991SHaojian Zhuang break; 2149cdc9991SHaojian Zhuang spin_unlock_irq(&boot_lock); 2159cdc9991SHaojian Zhuang /* Wait for clean L2 when the whole cluster is down. */ 2169cdc9991SHaojian Zhuang msleep(POLL_MSEC); 2179cdc9991SHaojian Zhuang spin_lock_irq(&boot_lock); 2189cdc9991SHaojian Zhuang } 2199cdc9991SHaojian Zhuang if (tries >= count) 2209cdc9991SHaojian Zhuang goto err; 2219cdc9991SHaojian Zhuang data = CORE_RESET_BIT(cpu) | NEON_RESET_BIT(cpu) | \ 2229cdc9991SHaojian Zhuang CORE_DEBUG_RESET_BIT(cpu); 2239cdc9991SHaojian Zhuang writel_relaxed(data, sysctrl + SC_CPU_RESET_REQ(cluster)); 2249cdc9991SHaojian Zhuang for (tries = 0; tries < count; tries++) { 2259cdc9991SHaojian Zhuang cpu_relax(); 2269cdc9991SHaojian Zhuang data = readl_relaxed(sysctrl + SC_CPU_RESET_STATUS(cluster)); 2279cdc9991SHaojian Zhuang if (data & CORE_RESET_STATUS(cpu)) 2289cdc9991SHaojian Zhuang break; 2299cdc9991SHaojian Zhuang } 2309cdc9991SHaojian Zhuang if (tries >= count) 2319cdc9991SHaojian Zhuang goto err; 232905cdf9dSNicolas Pitre if (hip04_cluster_is_down(cluster)) 233905cdf9dSNicolas Pitre hip04_set_snoop_filter(cluster, 0); 2349cdc9991SHaojian Zhuang spin_unlock_irq(&boot_lock); 235905cdf9dSNicolas Pitre return 1; 2369cdc9991SHaojian Zhuang err: 2379cdc9991SHaojian Zhuang spin_unlock_irq(&boot_lock); 238905cdf9dSNicolas Pitre return 0; 2399cdc9991SHaojian Zhuang } 2404c9e0f76SArnd Bergmann #endif 2419cdc9991SHaojian Zhuang 242*75305275SMasahiro Yamada static const struct smp_operations hip04_smp_ops __initconst = { 243905cdf9dSNicolas Pitre .smp_boot_secondary = hip04_boot_secondary, 2444c9e0f76SArnd Bergmann #ifdef CONFIG_HOTPLUG_CPU 245905cdf9dSNicolas Pitre .cpu_die = hip04_cpu_die, 246905cdf9dSNicolas Pitre .cpu_kill = hip04_cpu_kill, 2474c9e0f76SArnd Bergmann #endif 2489cdc9991SHaojian Zhuang }; 2499cdc9991SHaojian Zhuang 2509cdc9991SHaojian Zhuang static bool __init hip04_cpu_table_init(void) 2519cdc9991SHaojian Zhuang { 2529cdc9991SHaojian Zhuang unsigned int mpidr, cpu, cluster; 2539cdc9991SHaojian Zhuang 2549cdc9991SHaojian Zhuang mpidr = read_cpuid_mpidr(); 2559cdc9991SHaojian Zhuang cpu = MPIDR_AFFINITY_LEVEL(mpidr, 0); 2569cdc9991SHaojian Zhuang cluster = MPIDR_AFFINITY_LEVEL(mpidr, 1); 2579cdc9991SHaojian Zhuang 2589cdc9991SHaojian Zhuang if (cluster >= HIP04_MAX_CLUSTERS || 2599cdc9991SHaojian Zhuang cpu >= HIP04_MAX_CPUS_PER_CLUSTER) { 2609cdc9991SHaojian Zhuang pr_err("%s: boot CPU is out of bound!\n", __func__); 2619cdc9991SHaojian Zhuang return false; 2629cdc9991SHaojian Zhuang } 2639cdc9991SHaojian Zhuang hip04_set_snoop_filter(cluster, 1); 2649cdc9991SHaojian Zhuang hip04_cpu_table[cluster][cpu] = 1; 2659cdc9991SHaojian Zhuang return true; 2669cdc9991SHaojian Zhuang } 2679cdc9991SHaojian Zhuang 268905cdf9dSNicolas Pitre static int __init hip04_smp_init(void) 2699cdc9991SHaojian Zhuang { 2709cdc9991SHaojian Zhuang struct device_node *np, *np_sctl, *np_fab; 2719cdc9991SHaojian Zhuang struct resource fab_res; 2729cdc9991SHaojian Zhuang void __iomem *relocation; 2739cdc9991SHaojian Zhuang int ret = -ENODEV; 2749cdc9991SHaojian Zhuang 2759cdc9991SHaojian Zhuang np = of_find_compatible_node(NULL, NULL, "hisilicon,hip04-bootwrapper"); 2769cdc9991SHaojian Zhuang if (!np) 2779cdc9991SHaojian Zhuang goto err; 2789cdc9991SHaojian Zhuang ret = of_property_read_u32_array(np, "boot-method", 2799cdc9991SHaojian Zhuang &hip04_boot_method[0], 4); 2809cdc9991SHaojian Zhuang if (ret) 2819cdc9991SHaojian Zhuang goto err; 2829cdc9991SHaojian Zhuang np_sctl = of_find_compatible_node(NULL, NULL, "hisilicon,sysctrl"); 2839cdc9991SHaojian Zhuang if (!np_sctl) 2849cdc9991SHaojian Zhuang goto err; 2859cdc9991SHaojian Zhuang np_fab = of_find_compatible_node(NULL, NULL, "hisilicon,hip04-fabric"); 2869cdc9991SHaojian Zhuang if (!np_fab) 2879cdc9991SHaojian Zhuang goto err; 2889cdc9991SHaojian Zhuang 2899cdc9991SHaojian Zhuang ret = memblock_reserve(hip04_boot_method[0], hip04_boot_method[1]); 2909cdc9991SHaojian Zhuang if (ret) 2919cdc9991SHaojian Zhuang goto err; 2929cdc9991SHaojian Zhuang 2939cdc9991SHaojian Zhuang relocation = ioremap(hip04_boot_method[2], hip04_boot_method[3]); 2949cdc9991SHaojian Zhuang if (!relocation) { 2959cdc9991SHaojian Zhuang pr_err("failed to map relocation space\n"); 2969cdc9991SHaojian Zhuang ret = -ENOMEM; 2979cdc9991SHaojian Zhuang goto err_reloc; 2989cdc9991SHaojian Zhuang } 2999cdc9991SHaojian Zhuang sysctrl = of_iomap(np_sctl, 0); 3009cdc9991SHaojian Zhuang if (!sysctrl) { 3019cdc9991SHaojian Zhuang pr_err("failed to get sysctrl base\n"); 3029cdc9991SHaojian Zhuang ret = -ENOMEM; 3039cdc9991SHaojian Zhuang goto err_sysctrl; 3049cdc9991SHaojian Zhuang } 3059cdc9991SHaojian Zhuang ret = of_address_to_resource(np_fab, 0, &fab_res); 3069cdc9991SHaojian Zhuang if (ret) { 3079cdc9991SHaojian Zhuang pr_err("failed to get fabric base phys\n"); 3089cdc9991SHaojian Zhuang goto err_fabric; 3099cdc9991SHaojian Zhuang } 3109cdc9991SHaojian Zhuang fabric_phys_addr = fab_res.start; 3119cdc9991SHaojian Zhuang sync_cache_w(&fabric_phys_addr); 3129cdc9991SHaojian Zhuang fabric = of_iomap(np_fab, 0); 3139cdc9991SHaojian Zhuang if (!fabric) { 3149cdc9991SHaojian Zhuang pr_err("failed to get fabric base\n"); 3159cdc9991SHaojian Zhuang ret = -ENOMEM; 3169cdc9991SHaojian Zhuang goto err_fabric; 3179cdc9991SHaojian Zhuang } 3189cdc9991SHaojian Zhuang 3199cdc9991SHaojian Zhuang if (!hip04_cpu_table_init()) { 3209cdc9991SHaojian Zhuang ret = -EINVAL; 3219cdc9991SHaojian Zhuang goto err_table; 3229cdc9991SHaojian Zhuang } 3239cdc9991SHaojian Zhuang 3249cdc9991SHaojian Zhuang /* 3259cdc9991SHaojian Zhuang * Fill the instruction address that is used after secondary core 3269cdc9991SHaojian Zhuang * out of reset. 3279cdc9991SHaojian Zhuang */ 3289cdc9991SHaojian Zhuang writel_relaxed(hip04_boot_method[0], relocation); 3299cdc9991SHaojian Zhuang writel_relaxed(0xa5a5a5a5, relocation + 4); /* magic number */ 330905cdf9dSNicolas Pitre writel_relaxed(virt_to_phys(secondary_startup), relocation + 8); 3319cdc9991SHaojian Zhuang writel_relaxed(0, relocation + 12); 3329cdc9991SHaojian Zhuang iounmap(relocation); 3339cdc9991SHaojian Zhuang 334905cdf9dSNicolas Pitre smp_set_ops(&hip04_smp_ops); 3359cdc9991SHaojian Zhuang return ret; 3369cdc9991SHaojian Zhuang err_table: 3379cdc9991SHaojian Zhuang iounmap(fabric); 3389cdc9991SHaojian Zhuang err_fabric: 3399cdc9991SHaojian Zhuang iounmap(sysctrl); 3409cdc9991SHaojian Zhuang err_sysctrl: 3419cdc9991SHaojian Zhuang iounmap(relocation); 3429cdc9991SHaojian Zhuang err_reloc: 3439cdc9991SHaojian Zhuang memblock_free(hip04_boot_method[0], hip04_boot_method[1]); 3449cdc9991SHaojian Zhuang err: 3459cdc9991SHaojian Zhuang return ret; 3469cdc9991SHaojian Zhuang } 347905cdf9dSNicolas Pitre early_initcall(hip04_smp_init); 348