162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Copyright (c) 2013-2014 Linaro Ltd. 462306a36Sopenharmony_ci * Copyright (c) 2013-2014 HiSilicon Limited. 562306a36Sopenharmony_ci */ 662306a36Sopenharmony_ci#include <linux/init.h> 762306a36Sopenharmony_ci#include <linux/smp.h> 862306a36Sopenharmony_ci#include <linux/delay.h> 962306a36Sopenharmony_ci#include <linux/io.h> 1062306a36Sopenharmony_ci#include <linux/memblock.h> 1162306a36Sopenharmony_ci#include <linux/of_address.h> 1262306a36Sopenharmony_ci 1362306a36Sopenharmony_ci#include <asm/cputype.h> 1462306a36Sopenharmony_ci#include <asm/cp15.h> 1562306a36Sopenharmony_ci#include <asm/cacheflush.h> 1662306a36Sopenharmony_ci#include <asm/smp.h> 1762306a36Sopenharmony_ci#include <asm/smp_plat.h> 1862306a36Sopenharmony_ci 1962306a36Sopenharmony_ci#include "core.h" 2062306a36Sopenharmony_ci 2162306a36Sopenharmony_ci/* bits definition in SC_CPU_RESET_REQ[x]/SC_CPU_RESET_DREQ[x] 2262306a36Sopenharmony_ci * 1 -- unreset; 0 -- reset 2362306a36Sopenharmony_ci */ 2462306a36Sopenharmony_ci#define CORE_RESET_BIT(x) (1 << x) 2562306a36Sopenharmony_ci#define NEON_RESET_BIT(x) (1 << (x + 4)) 2662306a36Sopenharmony_ci#define CORE_DEBUG_RESET_BIT(x) (1 << (x + 9)) 2762306a36Sopenharmony_ci#define CLUSTER_L2_RESET_BIT (1 << 8) 2862306a36Sopenharmony_ci#define CLUSTER_DEBUG_RESET_BIT (1 << 13) 2962306a36Sopenharmony_ci 3062306a36Sopenharmony_ci/* 3162306a36Sopenharmony_ci * bits definition in SC_CPU_RESET_STATUS[x] 3262306a36Sopenharmony_ci * 1 -- reset status; 0 -- unreset status 3362306a36Sopenharmony_ci */ 3462306a36Sopenharmony_ci#define CORE_RESET_STATUS(x) (1 << x) 3562306a36Sopenharmony_ci#define NEON_RESET_STATUS(x) (1 << (x + 4)) 3662306a36Sopenharmony_ci#define CORE_DEBUG_RESET_STATUS(x) (1 << (x + 9)) 3762306a36Sopenharmony_ci#define CLUSTER_L2_RESET_STATUS (1 << 8) 3862306a36Sopenharmony_ci#define CLUSTER_DEBUG_RESET_STATUS (1 << 13) 3962306a36Sopenharmony_ci#define CORE_WFI_STATUS(x) (1 << (x + 16)) 4062306a36Sopenharmony_ci#define CORE_WFE_STATUS(x) (1 << (x + 20)) 4162306a36Sopenharmony_ci#define CORE_DEBUG_ACK(x) (1 << (x + 24)) 4262306a36Sopenharmony_ci 4362306a36Sopenharmony_ci#define SC_CPU_RESET_REQ(x) (0x520 + (x << 3)) /* reset */ 4462306a36Sopenharmony_ci#define SC_CPU_RESET_DREQ(x) (0x524 + (x << 3)) /* unreset */ 4562306a36Sopenharmony_ci#define SC_CPU_RESET_STATUS(x) (0x1520 + (x << 3)) 4662306a36Sopenharmony_ci 4762306a36Sopenharmony_ci#define FAB_SF_MODE 0x0c 4862306a36Sopenharmony_ci#define FAB_SF_INVLD 0x10 4962306a36Sopenharmony_ci 5062306a36Sopenharmony_ci/* bits definition in FB_SF_INVLD */ 5162306a36Sopenharmony_ci#define FB_SF_INVLD_START (1 << 8) 5262306a36Sopenharmony_ci 5362306a36Sopenharmony_ci#define HIP04_MAX_CLUSTERS 4 5462306a36Sopenharmony_ci#define HIP04_MAX_CPUS_PER_CLUSTER 4 5562306a36Sopenharmony_ci 5662306a36Sopenharmony_ci#define POLL_MSEC 10 5762306a36Sopenharmony_ci#define TIMEOUT_MSEC 1000 5862306a36Sopenharmony_ci 5962306a36Sopenharmony_cistatic void __iomem *sysctrl, *fabric; 6062306a36Sopenharmony_cistatic int hip04_cpu_table[HIP04_MAX_CLUSTERS][HIP04_MAX_CPUS_PER_CLUSTER]; 6162306a36Sopenharmony_cistatic DEFINE_SPINLOCK(boot_lock); 6262306a36Sopenharmony_cistatic u32 fabric_phys_addr; 6362306a36Sopenharmony_ci/* 6462306a36Sopenharmony_ci * [0]: bootwrapper physical address 6562306a36Sopenharmony_ci * [1]: bootwrapper size 6662306a36Sopenharmony_ci * [2]: relocation address 6762306a36Sopenharmony_ci * [3]: relocation size 6862306a36Sopenharmony_ci */ 6962306a36Sopenharmony_cistatic u32 hip04_boot_method[4]; 7062306a36Sopenharmony_ci 7162306a36Sopenharmony_cistatic bool hip04_cluster_is_down(unsigned int cluster) 7262306a36Sopenharmony_ci{ 7362306a36Sopenharmony_ci int i; 7462306a36Sopenharmony_ci 7562306a36Sopenharmony_ci for (i = 0; i < HIP04_MAX_CPUS_PER_CLUSTER; i++) 7662306a36Sopenharmony_ci if (hip04_cpu_table[cluster][i]) 7762306a36Sopenharmony_ci return false; 7862306a36Sopenharmony_ci return true; 7962306a36Sopenharmony_ci} 8062306a36Sopenharmony_ci 8162306a36Sopenharmony_cistatic void hip04_set_snoop_filter(unsigned int cluster, unsigned int on) 8262306a36Sopenharmony_ci{ 8362306a36Sopenharmony_ci unsigned long data; 8462306a36Sopenharmony_ci 8562306a36Sopenharmony_ci if (!fabric) 8662306a36Sopenharmony_ci BUG(); 8762306a36Sopenharmony_ci data = readl_relaxed(fabric + FAB_SF_MODE); 8862306a36Sopenharmony_ci if (on) 8962306a36Sopenharmony_ci data |= 1 << cluster; 9062306a36Sopenharmony_ci else 9162306a36Sopenharmony_ci data &= ~(1 << cluster); 9262306a36Sopenharmony_ci writel_relaxed(data, fabric + FAB_SF_MODE); 9362306a36Sopenharmony_ci do { 9462306a36Sopenharmony_ci cpu_relax(); 9562306a36Sopenharmony_ci } while (data != readl_relaxed(fabric + FAB_SF_MODE)); 9662306a36Sopenharmony_ci} 9762306a36Sopenharmony_ci 9862306a36Sopenharmony_cistatic int hip04_boot_secondary(unsigned int l_cpu, struct task_struct *idle) 9962306a36Sopenharmony_ci{ 10062306a36Sopenharmony_ci unsigned int mpidr, cpu, cluster; 10162306a36Sopenharmony_ci unsigned long data; 10262306a36Sopenharmony_ci void __iomem *sys_dreq, *sys_status; 10362306a36Sopenharmony_ci 10462306a36Sopenharmony_ci mpidr = cpu_logical_map(l_cpu); 10562306a36Sopenharmony_ci cpu = MPIDR_AFFINITY_LEVEL(mpidr, 0); 10662306a36Sopenharmony_ci cluster = MPIDR_AFFINITY_LEVEL(mpidr, 1); 10762306a36Sopenharmony_ci 10862306a36Sopenharmony_ci if (!sysctrl) 10962306a36Sopenharmony_ci return -ENODEV; 11062306a36Sopenharmony_ci if (cluster >= HIP04_MAX_CLUSTERS || cpu >= HIP04_MAX_CPUS_PER_CLUSTER) 11162306a36Sopenharmony_ci return -EINVAL; 11262306a36Sopenharmony_ci 11362306a36Sopenharmony_ci spin_lock_irq(&boot_lock); 11462306a36Sopenharmony_ci 11562306a36Sopenharmony_ci if (hip04_cpu_table[cluster][cpu]) 11662306a36Sopenharmony_ci goto out; 11762306a36Sopenharmony_ci 11862306a36Sopenharmony_ci sys_dreq = sysctrl + SC_CPU_RESET_DREQ(cluster); 11962306a36Sopenharmony_ci sys_status = sysctrl + SC_CPU_RESET_STATUS(cluster); 12062306a36Sopenharmony_ci if (hip04_cluster_is_down(cluster)) { 12162306a36Sopenharmony_ci data = CLUSTER_DEBUG_RESET_BIT; 12262306a36Sopenharmony_ci writel_relaxed(data, sys_dreq); 12362306a36Sopenharmony_ci do { 12462306a36Sopenharmony_ci cpu_relax(); 12562306a36Sopenharmony_ci data = readl_relaxed(sys_status); 12662306a36Sopenharmony_ci } while (data & CLUSTER_DEBUG_RESET_STATUS); 12762306a36Sopenharmony_ci hip04_set_snoop_filter(cluster, 1); 12862306a36Sopenharmony_ci } 12962306a36Sopenharmony_ci 13062306a36Sopenharmony_ci data = CORE_RESET_BIT(cpu) | NEON_RESET_BIT(cpu) | \ 13162306a36Sopenharmony_ci CORE_DEBUG_RESET_BIT(cpu); 13262306a36Sopenharmony_ci writel_relaxed(data, sys_dreq); 13362306a36Sopenharmony_ci do { 13462306a36Sopenharmony_ci cpu_relax(); 13562306a36Sopenharmony_ci } while (data == readl_relaxed(sys_status)); 13662306a36Sopenharmony_ci 13762306a36Sopenharmony_ci /* 13862306a36Sopenharmony_ci * We may fail to power up core again without this delay. 13962306a36Sopenharmony_ci * It's not mentioned in document. It's found by test. 14062306a36Sopenharmony_ci */ 14162306a36Sopenharmony_ci udelay(20); 14262306a36Sopenharmony_ci 14362306a36Sopenharmony_ci arch_send_wakeup_ipi_mask(cpumask_of(l_cpu)); 14462306a36Sopenharmony_ci 14562306a36Sopenharmony_ciout: 14662306a36Sopenharmony_ci hip04_cpu_table[cluster][cpu]++; 14762306a36Sopenharmony_ci spin_unlock_irq(&boot_lock); 14862306a36Sopenharmony_ci 14962306a36Sopenharmony_ci return 0; 15062306a36Sopenharmony_ci} 15162306a36Sopenharmony_ci 15262306a36Sopenharmony_ci#ifdef CONFIG_HOTPLUG_CPU 15362306a36Sopenharmony_cistatic void hip04_cpu_die(unsigned int l_cpu) 15462306a36Sopenharmony_ci{ 15562306a36Sopenharmony_ci unsigned int mpidr, cpu, cluster; 15662306a36Sopenharmony_ci bool last_man; 15762306a36Sopenharmony_ci 15862306a36Sopenharmony_ci mpidr = cpu_logical_map(l_cpu); 15962306a36Sopenharmony_ci cpu = MPIDR_AFFINITY_LEVEL(mpidr, 0); 16062306a36Sopenharmony_ci cluster = MPIDR_AFFINITY_LEVEL(mpidr, 1); 16162306a36Sopenharmony_ci 16262306a36Sopenharmony_ci spin_lock(&boot_lock); 16362306a36Sopenharmony_ci hip04_cpu_table[cluster][cpu]--; 16462306a36Sopenharmony_ci if (hip04_cpu_table[cluster][cpu] == 1) { 16562306a36Sopenharmony_ci /* A power_up request went ahead of us. */ 16662306a36Sopenharmony_ci spin_unlock(&boot_lock); 16762306a36Sopenharmony_ci return; 16862306a36Sopenharmony_ci } else if (hip04_cpu_table[cluster][cpu] > 1) { 16962306a36Sopenharmony_ci pr_err("Cluster %d CPU%d boots multiple times\n", cluster, cpu); 17062306a36Sopenharmony_ci BUG(); 17162306a36Sopenharmony_ci } 17262306a36Sopenharmony_ci 17362306a36Sopenharmony_ci last_man = hip04_cluster_is_down(cluster); 17462306a36Sopenharmony_ci spin_unlock(&boot_lock); 17562306a36Sopenharmony_ci if (last_man) { 17662306a36Sopenharmony_ci /* Since it's Cortex A15, disable L2 prefetching. */ 17762306a36Sopenharmony_ci asm volatile( 17862306a36Sopenharmony_ci "mcr p15, 1, %0, c15, c0, 3 \n\t" 17962306a36Sopenharmony_ci "isb \n\t" 18062306a36Sopenharmony_ci "dsb " 18162306a36Sopenharmony_ci : : "r" (0x400) ); 18262306a36Sopenharmony_ci v7_exit_coherency_flush(all); 18362306a36Sopenharmony_ci } else { 18462306a36Sopenharmony_ci v7_exit_coherency_flush(louis); 18562306a36Sopenharmony_ci } 18662306a36Sopenharmony_ci 18762306a36Sopenharmony_ci for (;;) 18862306a36Sopenharmony_ci wfi(); 18962306a36Sopenharmony_ci} 19062306a36Sopenharmony_ci 19162306a36Sopenharmony_cistatic int hip04_cpu_kill(unsigned int l_cpu) 19262306a36Sopenharmony_ci{ 19362306a36Sopenharmony_ci unsigned int mpidr, cpu, cluster; 19462306a36Sopenharmony_ci unsigned int data, tries, count; 19562306a36Sopenharmony_ci 19662306a36Sopenharmony_ci mpidr = cpu_logical_map(l_cpu); 19762306a36Sopenharmony_ci cpu = MPIDR_AFFINITY_LEVEL(mpidr, 0); 19862306a36Sopenharmony_ci cluster = MPIDR_AFFINITY_LEVEL(mpidr, 1); 19962306a36Sopenharmony_ci BUG_ON(cluster >= HIP04_MAX_CLUSTERS || 20062306a36Sopenharmony_ci cpu >= HIP04_MAX_CPUS_PER_CLUSTER); 20162306a36Sopenharmony_ci 20262306a36Sopenharmony_ci count = TIMEOUT_MSEC / POLL_MSEC; 20362306a36Sopenharmony_ci spin_lock_irq(&boot_lock); 20462306a36Sopenharmony_ci for (tries = 0; tries < count; tries++) { 20562306a36Sopenharmony_ci if (hip04_cpu_table[cluster][cpu]) 20662306a36Sopenharmony_ci goto err; 20762306a36Sopenharmony_ci cpu_relax(); 20862306a36Sopenharmony_ci data = readl_relaxed(sysctrl + SC_CPU_RESET_STATUS(cluster)); 20962306a36Sopenharmony_ci if (data & CORE_WFI_STATUS(cpu)) 21062306a36Sopenharmony_ci break; 21162306a36Sopenharmony_ci spin_unlock_irq(&boot_lock); 21262306a36Sopenharmony_ci /* Wait for clean L2 when the whole cluster is down. */ 21362306a36Sopenharmony_ci msleep(POLL_MSEC); 21462306a36Sopenharmony_ci spin_lock_irq(&boot_lock); 21562306a36Sopenharmony_ci } 21662306a36Sopenharmony_ci if (tries >= count) 21762306a36Sopenharmony_ci goto err; 21862306a36Sopenharmony_ci data = CORE_RESET_BIT(cpu) | NEON_RESET_BIT(cpu) | \ 21962306a36Sopenharmony_ci CORE_DEBUG_RESET_BIT(cpu); 22062306a36Sopenharmony_ci writel_relaxed(data, sysctrl + SC_CPU_RESET_REQ(cluster)); 22162306a36Sopenharmony_ci for (tries = 0; tries < count; tries++) { 22262306a36Sopenharmony_ci cpu_relax(); 22362306a36Sopenharmony_ci data = readl_relaxed(sysctrl + SC_CPU_RESET_STATUS(cluster)); 22462306a36Sopenharmony_ci if (data & CORE_RESET_STATUS(cpu)) 22562306a36Sopenharmony_ci break; 22662306a36Sopenharmony_ci } 22762306a36Sopenharmony_ci if (tries >= count) 22862306a36Sopenharmony_ci goto err; 22962306a36Sopenharmony_ci if (hip04_cluster_is_down(cluster)) 23062306a36Sopenharmony_ci hip04_set_snoop_filter(cluster, 0); 23162306a36Sopenharmony_ci spin_unlock_irq(&boot_lock); 23262306a36Sopenharmony_ci return 1; 23362306a36Sopenharmony_cierr: 23462306a36Sopenharmony_ci spin_unlock_irq(&boot_lock); 23562306a36Sopenharmony_ci return 0; 23662306a36Sopenharmony_ci} 23762306a36Sopenharmony_ci#endif 23862306a36Sopenharmony_ci 23962306a36Sopenharmony_cistatic const struct smp_operations hip04_smp_ops __initconst = { 24062306a36Sopenharmony_ci .smp_boot_secondary = hip04_boot_secondary, 24162306a36Sopenharmony_ci#ifdef CONFIG_HOTPLUG_CPU 24262306a36Sopenharmony_ci .cpu_die = hip04_cpu_die, 24362306a36Sopenharmony_ci .cpu_kill = hip04_cpu_kill, 24462306a36Sopenharmony_ci#endif 24562306a36Sopenharmony_ci}; 24662306a36Sopenharmony_ci 24762306a36Sopenharmony_cistatic bool __init hip04_cpu_table_init(void) 24862306a36Sopenharmony_ci{ 24962306a36Sopenharmony_ci unsigned int mpidr, cpu, cluster; 25062306a36Sopenharmony_ci 25162306a36Sopenharmony_ci mpidr = read_cpuid_mpidr(); 25262306a36Sopenharmony_ci cpu = MPIDR_AFFINITY_LEVEL(mpidr, 0); 25362306a36Sopenharmony_ci cluster = MPIDR_AFFINITY_LEVEL(mpidr, 1); 25462306a36Sopenharmony_ci 25562306a36Sopenharmony_ci if (cluster >= HIP04_MAX_CLUSTERS || 25662306a36Sopenharmony_ci cpu >= HIP04_MAX_CPUS_PER_CLUSTER) { 25762306a36Sopenharmony_ci pr_err("%s: boot CPU is out of bound!\n", __func__); 25862306a36Sopenharmony_ci return false; 25962306a36Sopenharmony_ci } 26062306a36Sopenharmony_ci hip04_set_snoop_filter(cluster, 1); 26162306a36Sopenharmony_ci hip04_cpu_table[cluster][cpu] = 1; 26262306a36Sopenharmony_ci return true; 26362306a36Sopenharmony_ci} 26462306a36Sopenharmony_ci 26562306a36Sopenharmony_cistatic int __init hip04_smp_init(void) 26662306a36Sopenharmony_ci{ 26762306a36Sopenharmony_ci struct device_node *np, *np_sctl, *np_fab; 26862306a36Sopenharmony_ci struct resource fab_res; 26962306a36Sopenharmony_ci void __iomem *relocation; 27062306a36Sopenharmony_ci int ret = -ENODEV; 27162306a36Sopenharmony_ci 27262306a36Sopenharmony_ci np = of_find_compatible_node(NULL, NULL, "hisilicon,hip04-bootwrapper"); 27362306a36Sopenharmony_ci if (!np) 27462306a36Sopenharmony_ci goto err; 27562306a36Sopenharmony_ci ret = of_property_read_u32_array(np, "boot-method", 27662306a36Sopenharmony_ci &hip04_boot_method[0], 4); 27762306a36Sopenharmony_ci if (ret) 27862306a36Sopenharmony_ci goto err; 27962306a36Sopenharmony_ci 28062306a36Sopenharmony_ci ret = -ENODEV; 28162306a36Sopenharmony_ci np_sctl = of_find_compatible_node(NULL, NULL, "hisilicon,sysctrl"); 28262306a36Sopenharmony_ci if (!np_sctl) 28362306a36Sopenharmony_ci goto err; 28462306a36Sopenharmony_ci np_fab = of_find_compatible_node(NULL, NULL, "hisilicon,hip04-fabric"); 28562306a36Sopenharmony_ci if (!np_fab) 28662306a36Sopenharmony_ci goto err; 28762306a36Sopenharmony_ci 28862306a36Sopenharmony_ci ret = memblock_reserve(hip04_boot_method[0], hip04_boot_method[1]); 28962306a36Sopenharmony_ci if (ret) 29062306a36Sopenharmony_ci goto err; 29162306a36Sopenharmony_ci 29262306a36Sopenharmony_ci relocation = ioremap(hip04_boot_method[2], hip04_boot_method[3]); 29362306a36Sopenharmony_ci if (!relocation) { 29462306a36Sopenharmony_ci pr_err("failed to map relocation space\n"); 29562306a36Sopenharmony_ci ret = -ENOMEM; 29662306a36Sopenharmony_ci goto err_reloc; 29762306a36Sopenharmony_ci } 29862306a36Sopenharmony_ci sysctrl = of_iomap(np_sctl, 0); 29962306a36Sopenharmony_ci if (!sysctrl) { 30062306a36Sopenharmony_ci pr_err("failed to get sysctrl base\n"); 30162306a36Sopenharmony_ci ret = -ENOMEM; 30262306a36Sopenharmony_ci goto err_sysctrl; 30362306a36Sopenharmony_ci } 30462306a36Sopenharmony_ci ret = of_address_to_resource(np_fab, 0, &fab_res); 30562306a36Sopenharmony_ci if (ret) { 30662306a36Sopenharmony_ci pr_err("failed to get fabric base phys\n"); 30762306a36Sopenharmony_ci goto err_fabric; 30862306a36Sopenharmony_ci } 30962306a36Sopenharmony_ci fabric_phys_addr = fab_res.start; 31062306a36Sopenharmony_ci sync_cache_w(&fabric_phys_addr); 31162306a36Sopenharmony_ci fabric = of_iomap(np_fab, 0); 31262306a36Sopenharmony_ci if (!fabric) { 31362306a36Sopenharmony_ci pr_err("failed to get fabric base\n"); 31462306a36Sopenharmony_ci ret = -ENOMEM; 31562306a36Sopenharmony_ci goto err_fabric; 31662306a36Sopenharmony_ci } 31762306a36Sopenharmony_ci 31862306a36Sopenharmony_ci if (!hip04_cpu_table_init()) { 31962306a36Sopenharmony_ci ret = -EINVAL; 32062306a36Sopenharmony_ci goto err_table; 32162306a36Sopenharmony_ci } 32262306a36Sopenharmony_ci 32362306a36Sopenharmony_ci /* 32462306a36Sopenharmony_ci * Fill the instruction address that is used after secondary core 32562306a36Sopenharmony_ci * out of reset. 32662306a36Sopenharmony_ci */ 32762306a36Sopenharmony_ci writel_relaxed(hip04_boot_method[0], relocation); 32862306a36Sopenharmony_ci writel_relaxed(0xa5a5a5a5, relocation + 4); /* magic number */ 32962306a36Sopenharmony_ci writel_relaxed(__pa_symbol(secondary_startup), relocation + 8); 33062306a36Sopenharmony_ci writel_relaxed(0, relocation + 12); 33162306a36Sopenharmony_ci iounmap(relocation); 33262306a36Sopenharmony_ci 33362306a36Sopenharmony_ci smp_set_ops(&hip04_smp_ops); 33462306a36Sopenharmony_ci return ret; 33562306a36Sopenharmony_cierr_table: 33662306a36Sopenharmony_ci iounmap(fabric); 33762306a36Sopenharmony_cierr_fabric: 33862306a36Sopenharmony_ci iounmap(sysctrl); 33962306a36Sopenharmony_cierr_sysctrl: 34062306a36Sopenharmony_ci iounmap(relocation); 34162306a36Sopenharmony_cierr_reloc: 34262306a36Sopenharmony_ci memblock_phys_free(hip04_boot_method[0], hip04_boot_method[1]); 34362306a36Sopenharmony_cierr: 34462306a36Sopenharmony_ci return ret; 34562306a36Sopenharmony_ci} 34662306a36Sopenharmony_ciearly_initcall(hip04_smp_init); 347