1 /* 2 * Broadcom STB CPU SMP and hotplug support for ARM 3 * 4 * Copyright (C) 2013-2014 Broadcom Corporation 5 * 6 * This program is free software; you can redistribute it and/or 7 * modify it under the terms of the GNU General Public License as 8 * published by the Free Software Foundation version 2. 9 * 10 * This program is distributed "as is" WITHOUT ANY WARRANTY of any 11 * kind, whether express or implied; without even the implied warranty 12 * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 * GNU General Public License for more details. 14 */ 15 16 #include <linux/delay.h> 17 #include <linux/errno.h> 18 #include <linux/init.h> 19 #include <linux/io.h> 20 #include <linux/of_address.h> 21 #include <linux/of_platform.h> 22 #include <linux/printk.h> 23 #include <linux/regmap.h> 24 #include <linux/smp.h> 25 #include <linux/mfd/syscon.h> 26 27 #include <asm/cacheflush.h> 28 #include <asm/cp15.h> 29 #include <asm/mach-types.h> 30 #include <asm/smp_plat.h> 31 32 #include "brcmstb.h" 33 34 enum { 35 ZONE_MAN_CLKEN_MASK = BIT(0), 36 ZONE_MAN_RESET_CNTL_MASK = BIT(1), 37 ZONE_MAN_MEM_PWR_MASK = BIT(4), 38 ZONE_RESERVED_1_MASK = BIT(5), 39 ZONE_MAN_ISO_CNTL_MASK = BIT(6), 40 ZONE_MANUAL_CONTROL_MASK = BIT(7), 41 ZONE_PWR_DN_REQ_MASK = BIT(9), 42 ZONE_PWR_UP_REQ_MASK = BIT(10), 43 ZONE_BLK_RST_ASSERT_MASK = BIT(12), 44 ZONE_PWR_OFF_STATE_MASK = BIT(25), 45 ZONE_PWR_ON_STATE_MASK = BIT(26), 46 ZONE_DPG_PWR_STATE_MASK = BIT(28), 47 ZONE_MEM_PWR_STATE_MASK = BIT(29), 48 ZONE_RESET_STATE_MASK = BIT(31), 49 CPU0_PWR_ZONE_CTRL_REG = 1, 50 CPU_RESET_CONFIG_REG = 2, 51 }; 52 53 static void __iomem *cpubiuctrl_block; 54 static void __iomem *hif_cont_block; 55 static u32 cpu0_pwr_zone_ctrl_reg; 56 static u32 cpu_rst_cfg_reg; 57 static u32 hif_cont_reg; 58 59 #ifdef CONFIG_HOTPLUG_CPU 60 /* 61 * We must quiesce a dying CPU before it can be killed by the boot CPU. Because 62 * one or more cache may be disabled, we must flush to ensure coherency. We 63 * cannot use traditionl completion structures or spinlocks as they rely on 64 * coherency. 65 */ 66 static DEFINE_PER_CPU_ALIGNED(int, per_cpu_sw_state); 67 68 static int per_cpu_sw_state_rd(u32 cpu) 69 { 70 sync_cache_r(SHIFT_PERCPU_PTR(&per_cpu_sw_state, per_cpu_offset(cpu))); 71 return per_cpu(per_cpu_sw_state, cpu); 72 } 73 74 static void per_cpu_sw_state_wr(u32 cpu, int val) 75 { 76 dmb(); 77 per_cpu(per_cpu_sw_state, cpu) = val; 78 sync_cache_w(SHIFT_PERCPU_PTR(&per_cpu_sw_state, per_cpu_offset(cpu))); 79 } 80 #else 81 static inline void per_cpu_sw_state_wr(u32 cpu, int val) { } 82 #endif 83 84 static void __iomem *pwr_ctrl_get_base(u32 cpu) 85 { 86 void __iomem *base = cpubiuctrl_block + cpu0_pwr_zone_ctrl_reg; 87 base += (cpu_logical_map(cpu) * 4); 88 return base; 89 } 90 91 static u32 pwr_ctrl_rd(u32 cpu) 92 { 93 void __iomem *base = pwr_ctrl_get_base(cpu); 94 return readl_relaxed(base); 95 } 96 97 static void pwr_ctrl_wr(u32 cpu, u32 val) 98 { 99 void __iomem *base = pwr_ctrl_get_base(cpu); 100 writel(val, base); 101 } 102 103 static void cpu_rst_cfg_set(u32 cpu, int set) 104 { 105 u32 val; 106 val = readl_relaxed(cpubiuctrl_block + cpu_rst_cfg_reg); 107 if (set) 108 val |= BIT(cpu_logical_map(cpu)); 109 else 110 val &= ~BIT(cpu_logical_map(cpu)); 111 writel_relaxed(val, cpubiuctrl_block + cpu_rst_cfg_reg); 112 } 113 114 static void cpu_set_boot_addr(u32 cpu, unsigned long boot_addr) 115 { 116 const int reg_ofs = cpu_logical_map(cpu) * 8; 117 writel_relaxed(0, hif_cont_block + hif_cont_reg + reg_ofs); 118 writel_relaxed(boot_addr, hif_cont_block + hif_cont_reg + 4 + reg_ofs); 119 } 120 121 static void brcmstb_cpu_boot(u32 cpu) 122 { 123 /* Mark this CPU as "up" */ 124 per_cpu_sw_state_wr(cpu, 1); 125 126 /* 127 * Set the reset vector to point to the secondary_startup 128 * routine 129 */ 130 cpu_set_boot_addr(cpu, virt_to_phys(brcmstb_secondary_startup)); 131 132 /* Unhalt the cpu */ 133 cpu_rst_cfg_set(cpu, 0); 134 } 135 136 static void brcmstb_cpu_power_on(u32 cpu) 137 { 138 /* 139 * The secondary cores power was cut, so we must go through 140 * power-on initialization. 141 */ 142 u32 tmp; 143 144 /* Request zone power up */ 145 pwr_ctrl_wr(cpu, ZONE_PWR_UP_REQ_MASK); 146 147 /* Wait for the power up FSM to complete */ 148 do { 149 tmp = pwr_ctrl_rd(cpu); 150 } while (!(tmp & ZONE_PWR_ON_STATE_MASK)); 151 } 152 153 static int brcmstb_cpu_get_power_state(u32 cpu) 154 { 155 int tmp = pwr_ctrl_rd(cpu); 156 return (tmp & ZONE_RESET_STATE_MASK) ? 0 : 1; 157 } 158 159 #ifdef CONFIG_HOTPLUG_CPU 160 161 static void brcmstb_cpu_die(u32 cpu) 162 { 163 v7_exit_coherency_flush(all); 164 165 per_cpu_sw_state_wr(cpu, 0); 166 167 /* Sit and wait to die */ 168 wfi(); 169 170 /* We should never get here... */ 171 while (1) 172 ; 173 } 174 175 static int brcmstb_cpu_kill(u32 cpu) 176 { 177 u32 tmp; 178 179 while (per_cpu_sw_state_rd(cpu)) 180 ; 181 182 /* Program zone reset */ 183 pwr_ctrl_wr(cpu, ZONE_RESET_STATE_MASK | ZONE_BLK_RST_ASSERT_MASK | 184 ZONE_PWR_DN_REQ_MASK); 185 186 /* Verify zone reset */ 187 tmp = pwr_ctrl_rd(cpu); 188 if (!(tmp & ZONE_RESET_STATE_MASK)) 189 pr_err("%s: Zone reset bit for CPU %d not asserted!\n", 190 __func__, cpu); 191 192 /* Wait for power down */ 193 do { 194 tmp = pwr_ctrl_rd(cpu); 195 } while (!(tmp & ZONE_PWR_OFF_STATE_MASK)); 196 197 /* Flush pipeline before resetting CPU */ 198 mb(); 199 200 /* Assert reset on the CPU */ 201 cpu_rst_cfg_set(cpu, 1); 202 203 return 1; 204 } 205 206 #endif /* CONFIG_HOTPLUG_CPU */ 207 208 static int __init setup_hifcpubiuctrl_regs(struct device_node *np) 209 { 210 int rc = 0; 211 char *name; 212 struct device_node *syscon_np = NULL; 213 214 name = "syscon-cpu"; 215 216 syscon_np = of_parse_phandle(np, name, 0); 217 if (!syscon_np) { 218 pr_err("can't find phandle %s\n", name); 219 rc = -EINVAL; 220 goto cleanup; 221 } 222 223 cpubiuctrl_block = of_iomap(syscon_np, 0); 224 if (!cpubiuctrl_block) { 225 pr_err("iomap failed for cpubiuctrl_block\n"); 226 rc = -EINVAL; 227 goto cleanup; 228 } 229 230 rc = of_property_read_u32_index(np, name, CPU0_PWR_ZONE_CTRL_REG, 231 &cpu0_pwr_zone_ctrl_reg); 232 if (rc) { 233 pr_err("failed to read 1st entry from %s property (%d)\n", name, 234 rc); 235 rc = -EINVAL; 236 goto cleanup; 237 } 238 239 rc = of_property_read_u32_index(np, name, CPU_RESET_CONFIG_REG, 240 &cpu_rst_cfg_reg); 241 if (rc) { 242 pr_err("failed to read 2nd entry from %s property (%d)\n", name, 243 rc); 244 rc = -EINVAL; 245 goto cleanup; 246 } 247 248 cleanup: 249 of_node_put(syscon_np); 250 return rc; 251 } 252 253 static int __init setup_hifcont_regs(struct device_node *np) 254 { 255 int rc = 0; 256 char *name; 257 struct device_node *syscon_np = NULL; 258 259 name = "syscon-cont"; 260 261 syscon_np = of_parse_phandle(np, name, 0); 262 if (!syscon_np) { 263 pr_err("can't find phandle %s\n", name); 264 rc = -EINVAL; 265 goto cleanup; 266 } 267 268 hif_cont_block = of_iomap(syscon_np, 0); 269 if (!hif_cont_block) { 270 pr_err("iomap failed for hif_cont_block\n"); 271 rc = -EINVAL; 272 goto cleanup; 273 } 274 275 /* Offset is at top of hif_cont_block */ 276 hif_cont_reg = 0; 277 278 cleanup: 279 of_node_put(syscon_np); 280 return rc; 281 } 282 283 static void __init brcmstb_cpu_ctrl_setup(unsigned int max_cpus) 284 { 285 int rc; 286 struct device_node *np; 287 char *name; 288 289 name = "brcm,brcmstb-smpboot"; 290 np = of_find_compatible_node(NULL, NULL, name); 291 if (!np) { 292 pr_err("can't find compatible node %s\n", name); 293 return; 294 } 295 296 rc = setup_hifcpubiuctrl_regs(np); 297 if (rc) 298 return; 299 300 rc = setup_hifcont_regs(np); 301 if (rc) 302 return; 303 } 304 305 static int brcmstb_boot_secondary(unsigned int cpu, struct task_struct *idle) 306 { 307 /* Missing the brcm,brcmstb-smpboot DT node? */ 308 if (!cpubiuctrl_block || !hif_cont_block) 309 return -ENODEV; 310 311 /* Bring up power to the core if necessary */ 312 if (brcmstb_cpu_get_power_state(cpu) == 0) 313 brcmstb_cpu_power_on(cpu); 314 315 brcmstb_cpu_boot(cpu); 316 317 return 0; 318 } 319 320 static struct smp_operations brcmstb_smp_ops __initdata = { 321 .smp_prepare_cpus = brcmstb_cpu_ctrl_setup, 322 .smp_boot_secondary = brcmstb_boot_secondary, 323 #ifdef CONFIG_HOTPLUG_CPU 324 .cpu_kill = brcmstb_cpu_kill, 325 .cpu_die = brcmstb_cpu_die, 326 #endif 327 }; 328 329 CPU_METHOD_OF_DECLARE(brcmstb_smp, "brcm,brahma-b15", &brcmstb_smp_ops); 330