18c2ecf20Sopenharmony_ci/* 28c2ecf20Sopenharmony_ci * Broadcom STB CPU SMP and hotplug support for ARM 38c2ecf20Sopenharmony_ci * 48c2ecf20Sopenharmony_ci * Copyright (C) 2013-2014 Broadcom Corporation 58c2ecf20Sopenharmony_ci * 68c2ecf20Sopenharmony_ci * This program is free software; you can redistribute it and/or 78c2ecf20Sopenharmony_ci * modify it under the terms of the GNU General Public License as 88c2ecf20Sopenharmony_ci * published by the Free Software Foundation version 2. 98c2ecf20Sopenharmony_ci * 108c2ecf20Sopenharmony_ci * This program is distributed "as is" WITHOUT ANY WARRANTY of any 118c2ecf20Sopenharmony_ci * kind, whether express or implied; without even the implied warranty 128c2ecf20Sopenharmony_ci * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 138c2ecf20Sopenharmony_ci * GNU General Public License for more details. 148c2ecf20Sopenharmony_ci */ 158c2ecf20Sopenharmony_ci 168c2ecf20Sopenharmony_ci#include <linux/delay.h> 178c2ecf20Sopenharmony_ci#include <linux/errno.h> 188c2ecf20Sopenharmony_ci#include <linux/init.h> 198c2ecf20Sopenharmony_ci#include <linux/io.h> 208c2ecf20Sopenharmony_ci#include <linux/jiffies.h> 218c2ecf20Sopenharmony_ci#include <linux/of_address.h> 228c2ecf20Sopenharmony_ci#include <linux/of_platform.h> 238c2ecf20Sopenharmony_ci#include <linux/printk.h> 248c2ecf20Sopenharmony_ci#include <linux/regmap.h> 258c2ecf20Sopenharmony_ci#include <linux/smp.h> 268c2ecf20Sopenharmony_ci#include <linux/mfd/syscon.h> 278c2ecf20Sopenharmony_ci 288c2ecf20Sopenharmony_ci#include <asm/cacheflush.h> 298c2ecf20Sopenharmony_ci#include <asm/cp15.h> 308c2ecf20Sopenharmony_ci#include <asm/mach-types.h> 318c2ecf20Sopenharmony_ci#include <asm/smp_plat.h> 328c2ecf20Sopenharmony_ci 338c2ecf20Sopenharmony_cienum { 348c2ecf20Sopenharmony_ci ZONE_MAN_CLKEN_MASK = BIT(0), 358c2ecf20Sopenharmony_ci ZONE_MAN_RESET_CNTL_MASK = BIT(1), 368c2ecf20Sopenharmony_ci ZONE_MAN_MEM_PWR_MASK = BIT(4), 378c2ecf20Sopenharmony_ci ZONE_RESERVED_1_MASK = BIT(5), 388c2ecf20Sopenharmony_ci ZONE_MAN_ISO_CNTL_MASK = BIT(6), 398c2ecf20Sopenharmony_ci ZONE_MANUAL_CONTROL_MASK = BIT(7), 408c2ecf20Sopenharmony_ci ZONE_PWR_DN_REQ_MASK = BIT(9), 418c2ecf20Sopenharmony_ci ZONE_PWR_UP_REQ_MASK = BIT(10), 428c2ecf20Sopenharmony_ci ZONE_BLK_RST_ASSERT_MASK = BIT(12), 438c2ecf20Sopenharmony_ci ZONE_PWR_OFF_STATE_MASK = BIT(25), 448c2ecf20Sopenharmony_ci ZONE_PWR_ON_STATE_MASK = BIT(26), 458c2ecf20Sopenharmony_ci ZONE_DPG_PWR_STATE_MASK = BIT(28), 468c2ecf20Sopenharmony_ci ZONE_MEM_PWR_STATE_MASK = BIT(29), 478c2ecf20Sopenharmony_ci ZONE_RESET_STATE_MASK = BIT(31), 488c2ecf20Sopenharmony_ci CPU0_PWR_ZONE_CTRL_REG = 1, 498c2ecf20Sopenharmony_ci CPU_RESET_CONFIG_REG = 2, 508c2ecf20Sopenharmony_ci}; 518c2ecf20Sopenharmony_ci 528c2ecf20Sopenharmony_cistatic void __iomem *cpubiuctrl_block; 538c2ecf20Sopenharmony_cistatic void __iomem *hif_cont_block; 548c2ecf20Sopenharmony_cistatic u32 cpu0_pwr_zone_ctrl_reg; 558c2ecf20Sopenharmony_cistatic u32 cpu_rst_cfg_reg; 568c2ecf20Sopenharmony_cistatic u32 hif_cont_reg; 578c2ecf20Sopenharmony_ci 588c2ecf20Sopenharmony_ci#ifdef CONFIG_HOTPLUG_CPU 598c2ecf20Sopenharmony_ci/* 608c2ecf20Sopenharmony_ci * We must quiesce a dying CPU before it can be killed by the boot CPU. Because 618c2ecf20Sopenharmony_ci * one or more cache may be disabled, we must flush to ensure coherency. We 628c2ecf20Sopenharmony_ci * cannot use traditionl completion structures or spinlocks as they rely on 638c2ecf20Sopenharmony_ci * coherency. 648c2ecf20Sopenharmony_ci */ 658c2ecf20Sopenharmony_cistatic DEFINE_PER_CPU_ALIGNED(int, per_cpu_sw_state); 668c2ecf20Sopenharmony_ci 678c2ecf20Sopenharmony_cistatic int per_cpu_sw_state_rd(u32 cpu) 688c2ecf20Sopenharmony_ci{ 698c2ecf20Sopenharmony_ci sync_cache_r(SHIFT_PERCPU_PTR(&per_cpu_sw_state, per_cpu_offset(cpu))); 708c2ecf20Sopenharmony_ci return per_cpu(per_cpu_sw_state, cpu); 718c2ecf20Sopenharmony_ci} 728c2ecf20Sopenharmony_ci 738c2ecf20Sopenharmony_cistatic void per_cpu_sw_state_wr(u32 cpu, int val) 748c2ecf20Sopenharmony_ci{ 758c2ecf20Sopenharmony_ci dmb(); 768c2ecf20Sopenharmony_ci per_cpu(per_cpu_sw_state, cpu) = val; 778c2ecf20Sopenharmony_ci sync_cache_w(SHIFT_PERCPU_PTR(&per_cpu_sw_state, per_cpu_offset(cpu))); 788c2ecf20Sopenharmony_ci} 798c2ecf20Sopenharmony_ci#else 808c2ecf20Sopenharmony_cistatic inline void per_cpu_sw_state_wr(u32 cpu, int val) { } 818c2ecf20Sopenharmony_ci#endif 828c2ecf20Sopenharmony_ci 838c2ecf20Sopenharmony_cistatic void __iomem *pwr_ctrl_get_base(u32 cpu) 848c2ecf20Sopenharmony_ci{ 858c2ecf20Sopenharmony_ci void __iomem *base = cpubiuctrl_block + cpu0_pwr_zone_ctrl_reg; 868c2ecf20Sopenharmony_ci base += (cpu_logical_map(cpu) * 4); 878c2ecf20Sopenharmony_ci return base; 888c2ecf20Sopenharmony_ci} 898c2ecf20Sopenharmony_ci 908c2ecf20Sopenharmony_cistatic u32 pwr_ctrl_rd(u32 cpu) 918c2ecf20Sopenharmony_ci{ 928c2ecf20Sopenharmony_ci void __iomem *base = pwr_ctrl_get_base(cpu); 938c2ecf20Sopenharmony_ci return readl_relaxed(base); 948c2ecf20Sopenharmony_ci} 958c2ecf20Sopenharmony_ci 968c2ecf20Sopenharmony_cistatic void pwr_ctrl_set(unsigned int cpu, u32 val, u32 mask) 978c2ecf20Sopenharmony_ci{ 988c2ecf20Sopenharmony_ci void __iomem *base = pwr_ctrl_get_base(cpu); 998c2ecf20Sopenharmony_ci writel((readl(base) & mask) | val, base); 1008c2ecf20Sopenharmony_ci} 1018c2ecf20Sopenharmony_ci 1028c2ecf20Sopenharmony_cistatic void pwr_ctrl_clr(unsigned int cpu, u32 val, u32 mask) 1038c2ecf20Sopenharmony_ci{ 1048c2ecf20Sopenharmony_ci void __iomem *base = pwr_ctrl_get_base(cpu); 1058c2ecf20Sopenharmony_ci writel((readl(base) & mask) & ~val, base); 1068c2ecf20Sopenharmony_ci} 1078c2ecf20Sopenharmony_ci 1088c2ecf20Sopenharmony_ci#define POLL_TMOUT_MS 500 1098c2ecf20Sopenharmony_cistatic int pwr_ctrl_wait_tmout(unsigned int cpu, u32 set, u32 mask) 1108c2ecf20Sopenharmony_ci{ 1118c2ecf20Sopenharmony_ci const unsigned long timeo = jiffies + msecs_to_jiffies(POLL_TMOUT_MS); 1128c2ecf20Sopenharmony_ci u32 tmp; 1138c2ecf20Sopenharmony_ci 1148c2ecf20Sopenharmony_ci do { 1158c2ecf20Sopenharmony_ci tmp = pwr_ctrl_rd(cpu) & mask; 1168c2ecf20Sopenharmony_ci if (!set == !tmp) 1178c2ecf20Sopenharmony_ci return 0; 1188c2ecf20Sopenharmony_ci } while (time_before(jiffies, timeo)); 1198c2ecf20Sopenharmony_ci 1208c2ecf20Sopenharmony_ci tmp = pwr_ctrl_rd(cpu) & mask; 1218c2ecf20Sopenharmony_ci if (!set == !tmp) 1228c2ecf20Sopenharmony_ci return 0; 1238c2ecf20Sopenharmony_ci 1248c2ecf20Sopenharmony_ci return -ETIMEDOUT; 1258c2ecf20Sopenharmony_ci} 1268c2ecf20Sopenharmony_ci 1278c2ecf20Sopenharmony_cistatic void cpu_rst_cfg_set(u32 cpu, int set) 1288c2ecf20Sopenharmony_ci{ 1298c2ecf20Sopenharmony_ci u32 val; 1308c2ecf20Sopenharmony_ci val = readl_relaxed(cpubiuctrl_block + cpu_rst_cfg_reg); 1318c2ecf20Sopenharmony_ci if (set) 1328c2ecf20Sopenharmony_ci val |= BIT(cpu_logical_map(cpu)); 1338c2ecf20Sopenharmony_ci else 1348c2ecf20Sopenharmony_ci val &= ~BIT(cpu_logical_map(cpu)); 1358c2ecf20Sopenharmony_ci writel_relaxed(val, cpubiuctrl_block + cpu_rst_cfg_reg); 1368c2ecf20Sopenharmony_ci} 1378c2ecf20Sopenharmony_ci 1388c2ecf20Sopenharmony_cistatic void cpu_set_boot_addr(u32 cpu, unsigned long boot_addr) 1398c2ecf20Sopenharmony_ci{ 1408c2ecf20Sopenharmony_ci const int reg_ofs = cpu_logical_map(cpu) * 8; 1418c2ecf20Sopenharmony_ci writel_relaxed(0, hif_cont_block + hif_cont_reg + reg_ofs); 1428c2ecf20Sopenharmony_ci writel_relaxed(boot_addr, hif_cont_block + hif_cont_reg + 4 + reg_ofs); 1438c2ecf20Sopenharmony_ci} 1448c2ecf20Sopenharmony_ci 1458c2ecf20Sopenharmony_cistatic void brcmstb_cpu_boot(u32 cpu) 1468c2ecf20Sopenharmony_ci{ 1478c2ecf20Sopenharmony_ci /* Mark this CPU as "up" */ 1488c2ecf20Sopenharmony_ci per_cpu_sw_state_wr(cpu, 1); 1498c2ecf20Sopenharmony_ci 1508c2ecf20Sopenharmony_ci /* 1518c2ecf20Sopenharmony_ci * Set the reset vector to point to the secondary_startup 1528c2ecf20Sopenharmony_ci * routine 1538c2ecf20Sopenharmony_ci */ 1548c2ecf20Sopenharmony_ci cpu_set_boot_addr(cpu, __pa_symbol(secondary_startup)); 1558c2ecf20Sopenharmony_ci 1568c2ecf20Sopenharmony_ci /* Unhalt the cpu */ 1578c2ecf20Sopenharmony_ci cpu_rst_cfg_set(cpu, 0); 1588c2ecf20Sopenharmony_ci} 1598c2ecf20Sopenharmony_ci 1608c2ecf20Sopenharmony_cistatic void brcmstb_cpu_power_on(u32 cpu) 1618c2ecf20Sopenharmony_ci{ 1628c2ecf20Sopenharmony_ci /* 1638c2ecf20Sopenharmony_ci * The secondary cores power was cut, so we must go through 1648c2ecf20Sopenharmony_ci * power-on initialization. 1658c2ecf20Sopenharmony_ci */ 1668c2ecf20Sopenharmony_ci pwr_ctrl_set(cpu, ZONE_MAN_ISO_CNTL_MASK, 0xffffff00); 1678c2ecf20Sopenharmony_ci pwr_ctrl_set(cpu, ZONE_MANUAL_CONTROL_MASK, -1); 1688c2ecf20Sopenharmony_ci pwr_ctrl_set(cpu, ZONE_RESERVED_1_MASK, -1); 1698c2ecf20Sopenharmony_ci 1708c2ecf20Sopenharmony_ci pwr_ctrl_set(cpu, ZONE_MAN_MEM_PWR_MASK, -1); 1718c2ecf20Sopenharmony_ci 1728c2ecf20Sopenharmony_ci if (pwr_ctrl_wait_tmout(cpu, 1, ZONE_MEM_PWR_STATE_MASK)) 1738c2ecf20Sopenharmony_ci panic("ZONE_MEM_PWR_STATE_MASK set timeout"); 1748c2ecf20Sopenharmony_ci 1758c2ecf20Sopenharmony_ci pwr_ctrl_set(cpu, ZONE_MAN_CLKEN_MASK, -1); 1768c2ecf20Sopenharmony_ci 1778c2ecf20Sopenharmony_ci if (pwr_ctrl_wait_tmout(cpu, 1, ZONE_DPG_PWR_STATE_MASK)) 1788c2ecf20Sopenharmony_ci panic("ZONE_DPG_PWR_STATE_MASK set timeout"); 1798c2ecf20Sopenharmony_ci 1808c2ecf20Sopenharmony_ci pwr_ctrl_clr(cpu, ZONE_MAN_ISO_CNTL_MASK, -1); 1818c2ecf20Sopenharmony_ci pwr_ctrl_set(cpu, ZONE_MAN_RESET_CNTL_MASK, -1); 1828c2ecf20Sopenharmony_ci} 1838c2ecf20Sopenharmony_ci 1848c2ecf20Sopenharmony_cistatic int brcmstb_cpu_get_power_state(u32 cpu) 1858c2ecf20Sopenharmony_ci{ 1868c2ecf20Sopenharmony_ci int tmp = pwr_ctrl_rd(cpu); 1878c2ecf20Sopenharmony_ci return (tmp & ZONE_RESET_STATE_MASK) ? 0 : 1; 1888c2ecf20Sopenharmony_ci} 1898c2ecf20Sopenharmony_ci 1908c2ecf20Sopenharmony_ci#ifdef CONFIG_HOTPLUG_CPU 1918c2ecf20Sopenharmony_ci 1928c2ecf20Sopenharmony_cistatic void brcmstb_cpu_die(u32 cpu) 1938c2ecf20Sopenharmony_ci{ 1948c2ecf20Sopenharmony_ci v7_exit_coherency_flush(all); 1958c2ecf20Sopenharmony_ci 1968c2ecf20Sopenharmony_ci per_cpu_sw_state_wr(cpu, 0); 1978c2ecf20Sopenharmony_ci 1988c2ecf20Sopenharmony_ci /* Sit and wait to die */ 1998c2ecf20Sopenharmony_ci wfi(); 2008c2ecf20Sopenharmony_ci 2018c2ecf20Sopenharmony_ci /* We should never get here... */ 2028c2ecf20Sopenharmony_ci while (1) 2038c2ecf20Sopenharmony_ci ; 2048c2ecf20Sopenharmony_ci} 2058c2ecf20Sopenharmony_ci 2068c2ecf20Sopenharmony_cistatic int brcmstb_cpu_kill(u32 cpu) 2078c2ecf20Sopenharmony_ci{ 2088c2ecf20Sopenharmony_ci /* 2098c2ecf20Sopenharmony_ci * Ordinarily, the hardware forbids power-down of CPU0 (which is good 2108c2ecf20Sopenharmony_ci * because it is the boot CPU), but this is not true when using BPCM 2118c2ecf20Sopenharmony_ci * manual mode. Consequently, we must avoid turning off CPU0 here to 2128c2ecf20Sopenharmony_ci * ensure that TI2C master reset will work. 2138c2ecf20Sopenharmony_ci */ 2148c2ecf20Sopenharmony_ci if (cpu == 0) { 2158c2ecf20Sopenharmony_ci pr_warn("SMP: refusing to power off CPU0\n"); 2168c2ecf20Sopenharmony_ci return 1; 2178c2ecf20Sopenharmony_ci } 2188c2ecf20Sopenharmony_ci 2198c2ecf20Sopenharmony_ci while (per_cpu_sw_state_rd(cpu)) 2208c2ecf20Sopenharmony_ci ; 2218c2ecf20Sopenharmony_ci 2228c2ecf20Sopenharmony_ci pwr_ctrl_set(cpu, ZONE_MANUAL_CONTROL_MASK, -1); 2238c2ecf20Sopenharmony_ci pwr_ctrl_clr(cpu, ZONE_MAN_RESET_CNTL_MASK, -1); 2248c2ecf20Sopenharmony_ci pwr_ctrl_clr(cpu, ZONE_MAN_CLKEN_MASK, -1); 2258c2ecf20Sopenharmony_ci pwr_ctrl_set(cpu, ZONE_MAN_ISO_CNTL_MASK, -1); 2268c2ecf20Sopenharmony_ci pwr_ctrl_clr(cpu, ZONE_MAN_MEM_PWR_MASK, -1); 2278c2ecf20Sopenharmony_ci 2288c2ecf20Sopenharmony_ci if (pwr_ctrl_wait_tmout(cpu, 0, ZONE_MEM_PWR_STATE_MASK)) 2298c2ecf20Sopenharmony_ci panic("ZONE_MEM_PWR_STATE_MASK clear timeout"); 2308c2ecf20Sopenharmony_ci 2318c2ecf20Sopenharmony_ci pwr_ctrl_clr(cpu, ZONE_RESERVED_1_MASK, -1); 2328c2ecf20Sopenharmony_ci 2338c2ecf20Sopenharmony_ci if (pwr_ctrl_wait_tmout(cpu, 0, ZONE_DPG_PWR_STATE_MASK)) 2348c2ecf20Sopenharmony_ci panic("ZONE_DPG_PWR_STATE_MASK clear timeout"); 2358c2ecf20Sopenharmony_ci 2368c2ecf20Sopenharmony_ci /* Flush pipeline before resetting CPU */ 2378c2ecf20Sopenharmony_ci mb(); 2388c2ecf20Sopenharmony_ci 2398c2ecf20Sopenharmony_ci /* Assert reset on the CPU */ 2408c2ecf20Sopenharmony_ci cpu_rst_cfg_set(cpu, 1); 2418c2ecf20Sopenharmony_ci 2428c2ecf20Sopenharmony_ci return 1; 2438c2ecf20Sopenharmony_ci} 2448c2ecf20Sopenharmony_ci 2458c2ecf20Sopenharmony_ci#endif /* CONFIG_HOTPLUG_CPU */ 2468c2ecf20Sopenharmony_ci 2478c2ecf20Sopenharmony_cistatic int __init setup_hifcpubiuctrl_regs(struct device_node *np) 2488c2ecf20Sopenharmony_ci{ 2498c2ecf20Sopenharmony_ci int rc = 0; 2508c2ecf20Sopenharmony_ci char *name; 2518c2ecf20Sopenharmony_ci struct device_node *syscon_np = NULL; 2528c2ecf20Sopenharmony_ci 2538c2ecf20Sopenharmony_ci name = "syscon-cpu"; 2548c2ecf20Sopenharmony_ci 2558c2ecf20Sopenharmony_ci syscon_np = of_parse_phandle(np, name, 0); 2568c2ecf20Sopenharmony_ci if (!syscon_np) { 2578c2ecf20Sopenharmony_ci pr_err("can't find phandle %s\n", name); 2588c2ecf20Sopenharmony_ci rc = -EINVAL; 2598c2ecf20Sopenharmony_ci goto cleanup; 2608c2ecf20Sopenharmony_ci } 2618c2ecf20Sopenharmony_ci 2628c2ecf20Sopenharmony_ci cpubiuctrl_block = of_iomap(syscon_np, 0); 2638c2ecf20Sopenharmony_ci if (!cpubiuctrl_block) { 2648c2ecf20Sopenharmony_ci pr_err("iomap failed for cpubiuctrl_block\n"); 2658c2ecf20Sopenharmony_ci rc = -EINVAL; 2668c2ecf20Sopenharmony_ci goto cleanup; 2678c2ecf20Sopenharmony_ci } 2688c2ecf20Sopenharmony_ci 2698c2ecf20Sopenharmony_ci rc = of_property_read_u32_index(np, name, CPU0_PWR_ZONE_CTRL_REG, 2708c2ecf20Sopenharmony_ci &cpu0_pwr_zone_ctrl_reg); 2718c2ecf20Sopenharmony_ci if (rc) { 2728c2ecf20Sopenharmony_ci pr_err("failed to read 1st entry from %s property (%d)\n", name, 2738c2ecf20Sopenharmony_ci rc); 2748c2ecf20Sopenharmony_ci rc = -EINVAL; 2758c2ecf20Sopenharmony_ci goto cleanup; 2768c2ecf20Sopenharmony_ci } 2778c2ecf20Sopenharmony_ci 2788c2ecf20Sopenharmony_ci rc = of_property_read_u32_index(np, name, CPU_RESET_CONFIG_REG, 2798c2ecf20Sopenharmony_ci &cpu_rst_cfg_reg); 2808c2ecf20Sopenharmony_ci if (rc) { 2818c2ecf20Sopenharmony_ci pr_err("failed to read 2nd entry from %s property (%d)\n", name, 2828c2ecf20Sopenharmony_ci rc); 2838c2ecf20Sopenharmony_ci rc = -EINVAL; 2848c2ecf20Sopenharmony_ci goto cleanup; 2858c2ecf20Sopenharmony_ci } 2868c2ecf20Sopenharmony_ci 2878c2ecf20Sopenharmony_cicleanup: 2888c2ecf20Sopenharmony_ci of_node_put(syscon_np); 2898c2ecf20Sopenharmony_ci return rc; 2908c2ecf20Sopenharmony_ci} 2918c2ecf20Sopenharmony_ci 2928c2ecf20Sopenharmony_cistatic int __init setup_hifcont_regs(struct device_node *np) 2938c2ecf20Sopenharmony_ci{ 2948c2ecf20Sopenharmony_ci int rc = 0; 2958c2ecf20Sopenharmony_ci char *name; 2968c2ecf20Sopenharmony_ci struct device_node *syscon_np = NULL; 2978c2ecf20Sopenharmony_ci 2988c2ecf20Sopenharmony_ci name = "syscon-cont"; 2998c2ecf20Sopenharmony_ci 3008c2ecf20Sopenharmony_ci syscon_np = of_parse_phandle(np, name, 0); 3018c2ecf20Sopenharmony_ci if (!syscon_np) { 3028c2ecf20Sopenharmony_ci pr_err("can't find phandle %s\n", name); 3038c2ecf20Sopenharmony_ci rc = -EINVAL; 3048c2ecf20Sopenharmony_ci goto cleanup; 3058c2ecf20Sopenharmony_ci } 3068c2ecf20Sopenharmony_ci 3078c2ecf20Sopenharmony_ci hif_cont_block = of_iomap(syscon_np, 0); 3088c2ecf20Sopenharmony_ci if (!hif_cont_block) { 3098c2ecf20Sopenharmony_ci pr_err("iomap failed for hif_cont_block\n"); 3108c2ecf20Sopenharmony_ci rc = -EINVAL; 3118c2ecf20Sopenharmony_ci goto cleanup; 3128c2ecf20Sopenharmony_ci } 3138c2ecf20Sopenharmony_ci 3148c2ecf20Sopenharmony_ci /* Offset is at top of hif_cont_block */ 3158c2ecf20Sopenharmony_ci hif_cont_reg = 0; 3168c2ecf20Sopenharmony_ci 3178c2ecf20Sopenharmony_cicleanup: 3188c2ecf20Sopenharmony_ci of_node_put(syscon_np); 3198c2ecf20Sopenharmony_ci return rc; 3208c2ecf20Sopenharmony_ci} 3218c2ecf20Sopenharmony_ci 3228c2ecf20Sopenharmony_cistatic void __init brcmstb_cpu_ctrl_setup(unsigned int max_cpus) 3238c2ecf20Sopenharmony_ci{ 3248c2ecf20Sopenharmony_ci int rc; 3258c2ecf20Sopenharmony_ci struct device_node *np; 3268c2ecf20Sopenharmony_ci char *name; 3278c2ecf20Sopenharmony_ci 3288c2ecf20Sopenharmony_ci name = "brcm,brcmstb-smpboot"; 3298c2ecf20Sopenharmony_ci np = of_find_compatible_node(NULL, NULL, name); 3308c2ecf20Sopenharmony_ci if (!np) { 3318c2ecf20Sopenharmony_ci pr_err("can't find compatible node %s\n", name); 3328c2ecf20Sopenharmony_ci return; 3338c2ecf20Sopenharmony_ci } 3348c2ecf20Sopenharmony_ci 3358c2ecf20Sopenharmony_ci rc = setup_hifcpubiuctrl_regs(np); 3368c2ecf20Sopenharmony_ci if (rc) 3378c2ecf20Sopenharmony_ci goto out_put_node; 3388c2ecf20Sopenharmony_ci 3398c2ecf20Sopenharmony_ci rc = setup_hifcont_regs(np); 3408c2ecf20Sopenharmony_ci if (rc) 3418c2ecf20Sopenharmony_ci goto out_put_node; 3428c2ecf20Sopenharmony_ci 3438c2ecf20Sopenharmony_ciout_put_node: 3448c2ecf20Sopenharmony_ci of_node_put(np); 3458c2ecf20Sopenharmony_ci} 3468c2ecf20Sopenharmony_ci 3478c2ecf20Sopenharmony_cistatic int brcmstb_boot_secondary(unsigned int cpu, struct task_struct *idle) 3488c2ecf20Sopenharmony_ci{ 3498c2ecf20Sopenharmony_ci /* Missing the brcm,brcmstb-smpboot DT node? */ 3508c2ecf20Sopenharmony_ci if (!cpubiuctrl_block || !hif_cont_block) 3518c2ecf20Sopenharmony_ci return -ENODEV; 3528c2ecf20Sopenharmony_ci 3538c2ecf20Sopenharmony_ci /* Bring up power to the core if necessary */ 3548c2ecf20Sopenharmony_ci if (brcmstb_cpu_get_power_state(cpu) == 0) 3558c2ecf20Sopenharmony_ci brcmstb_cpu_power_on(cpu); 3568c2ecf20Sopenharmony_ci 3578c2ecf20Sopenharmony_ci brcmstb_cpu_boot(cpu); 3588c2ecf20Sopenharmony_ci 3598c2ecf20Sopenharmony_ci return 0; 3608c2ecf20Sopenharmony_ci} 3618c2ecf20Sopenharmony_ci 3628c2ecf20Sopenharmony_cistatic const struct smp_operations brcmstb_smp_ops __initconst = { 3638c2ecf20Sopenharmony_ci .smp_prepare_cpus = brcmstb_cpu_ctrl_setup, 3648c2ecf20Sopenharmony_ci .smp_boot_secondary = brcmstb_boot_secondary, 3658c2ecf20Sopenharmony_ci#ifdef CONFIG_HOTPLUG_CPU 3668c2ecf20Sopenharmony_ci .cpu_kill = brcmstb_cpu_kill, 3678c2ecf20Sopenharmony_ci .cpu_die = brcmstb_cpu_die, 3688c2ecf20Sopenharmony_ci#endif 3698c2ecf20Sopenharmony_ci}; 3708c2ecf20Sopenharmony_ci 3718c2ecf20Sopenharmony_ciCPU_METHOD_OF_DECLARE(brcmstb_smp, "brcm,brahma-b15", &brcmstb_smp_ops); 372