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 #include <linux/spinlock.h> 27 28 #include <asm/cacheflush.h> 29 #include <asm/cp15.h> 30 #include <asm/mach-types.h> 31 #include <asm/smp_plat.h> 32 33 #include "brcmstb.h" 34 35 enum { 36 ZONE_MAN_CLKEN_MASK = BIT(0), 37 ZONE_MAN_RESET_CNTL_MASK = BIT(1), 38 ZONE_MAN_MEM_PWR_MASK = BIT(4), 39 ZONE_RESERVED_1_MASK = BIT(5), 40 ZONE_MAN_ISO_CNTL_MASK = BIT(6), 41 ZONE_MANUAL_CONTROL_MASK = BIT(7), 42 ZONE_PWR_DN_REQ_MASK = BIT(9), 43 ZONE_PWR_UP_REQ_MASK = BIT(10), 44 ZONE_BLK_RST_ASSERT_MASK = BIT(12), 45 ZONE_PWR_OFF_STATE_MASK = BIT(25), 46 ZONE_PWR_ON_STATE_MASK = BIT(26), 47 ZONE_DPG_PWR_STATE_MASK = BIT(28), 48 ZONE_MEM_PWR_STATE_MASK = BIT(29), 49 ZONE_RESET_STATE_MASK = BIT(31), 50 CPU0_PWR_ZONE_CTRL_REG = 1, 51 CPU_RESET_CONFIG_REG = 2, 52 }; 53 54 static void __iomem *cpubiuctrl_block; 55 static void __iomem *hif_cont_block; 56 static u32 cpu0_pwr_zone_ctrl_reg; 57 static u32 cpu_rst_cfg_reg; 58 static u32 hif_cont_reg; 59 60 #ifdef CONFIG_HOTPLUG_CPU 61 static DEFINE_PER_CPU_ALIGNED(int, per_cpu_sw_state); 62 63 static int per_cpu_sw_state_rd(u32 cpu) 64 { 65 sync_cache_r(SHIFT_PERCPU_PTR(&per_cpu_sw_state, per_cpu_offset(cpu))); 66 return per_cpu(per_cpu_sw_state, cpu); 67 } 68 69 static void per_cpu_sw_state_wr(u32 cpu, int val) 70 { 71 per_cpu(per_cpu_sw_state, cpu) = val; 72 dmb(); 73 sync_cache_w(SHIFT_PERCPU_PTR(&per_cpu_sw_state, per_cpu_offset(cpu))); 74 dsb_sev(); 75 } 76 #else 77 static inline void per_cpu_sw_state_wr(u32 cpu, int val) { } 78 #endif 79 80 static void __iomem *pwr_ctrl_get_base(u32 cpu) 81 { 82 void __iomem *base = cpubiuctrl_block + cpu0_pwr_zone_ctrl_reg; 83 base += (cpu_logical_map(cpu) * 4); 84 return base; 85 } 86 87 static u32 pwr_ctrl_rd(u32 cpu) 88 { 89 void __iomem *base = pwr_ctrl_get_base(cpu); 90 return readl_relaxed(base); 91 } 92 93 static void pwr_ctrl_wr(u32 cpu, u32 val) 94 { 95 void __iomem *base = pwr_ctrl_get_base(cpu); 96 writel(val, base); 97 } 98 99 static void cpu_rst_cfg_set(u32 cpu, int set) 100 { 101 u32 val; 102 val = readl_relaxed(cpubiuctrl_block + cpu_rst_cfg_reg); 103 if (set) 104 val |= BIT(cpu_logical_map(cpu)); 105 else 106 val &= ~BIT(cpu_logical_map(cpu)); 107 writel_relaxed(val, cpubiuctrl_block + cpu_rst_cfg_reg); 108 } 109 110 static void cpu_set_boot_addr(u32 cpu, unsigned long boot_addr) 111 { 112 const int reg_ofs = cpu_logical_map(cpu) * 8; 113 writel_relaxed(0, hif_cont_block + hif_cont_reg + reg_ofs); 114 writel_relaxed(boot_addr, hif_cont_block + hif_cont_reg + 4 + reg_ofs); 115 } 116 117 static void brcmstb_cpu_boot(u32 cpu) 118 { 119 pr_info("SMP: Booting CPU%d...\n", cpu); 120 121 /* 122 * set the reset vector to point to the secondary_startup 123 * routine 124 */ 125 cpu_set_boot_addr(cpu, virt_to_phys(brcmstb_secondary_startup)); 126 127 /* unhalt the cpu */ 128 cpu_rst_cfg_set(cpu, 0); 129 } 130 131 static void brcmstb_cpu_power_on(u32 cpu) 132 { 133 /* 134 * The secondary cores power was cut, so we must go through 135 * power-on initialization. 136 */ 137 u32 tmp; 138 139 pr_info("SMP: Powering up CPU%d...\n", cpu); 140 141 /* Request zone power up */ 142 pwr_ctrl_wr(cpu, ZONE_PWR_UP_REQ_MASK); 143 144 /* Wait for the power up FSM to complete */ 145 do { 146 tmp = pwr_ctrl_rd(cpu); 147 } while (!(tmp & ZONE_PWR_ON_STATE_MASK)); 148 149 per_cpu_sw_state_wr(cpu, 1); 150 } 151 152 static int brcmstb_cpu_get_power_state(u32 cpu) 153 { 154 int tmp = pwr_ctrl_rd(cpu); 155 return (tmp & ZONE_RESET_STATE_MASK) ? 0 : 1; 156 } 157 158 #ifdef CONFIG_HOTPLUG_CPU 159 160 static void brcmstb_cpu_die(u32 cpu) 161 { 162 v7_exit_coherency_flush(all); 163 164 /* Prevent all interrupts from reaching this CPU. */ 165 arch_local_irq_disable(); 166 167 /* 168 * Final full barrier to ensure everything before this instruction has 169 * quiesced. 170 */ 171 isb(); 172 dsb(); 173 174 per_cpu_sw_state_wr(cpu, 0); 175 176 /* Sit and wait to die */ 177 wfi(); 178 179 /* We should never get here... */ 180 panic("Spurious interrupt on CPU %d received!\n", cpu); 181 } 182 183 static int brcmstb_cpu_kill(u32 cpu) 184 { 185 u32 tmp; 186 187 pr_info("SMP: Powering down CPU%d...\n", cpu); 188 189 while (per_cpu_sw_state_rd(cpu)) 190 ; 191 192 /* Program zone reset */ 193 pwr_ctrl_wr(cpu, ZONE_RESET_STATE_MASK | ZONE_BLK_RST_ASSERT_MASK | 194 ZONE_PWR_DN_REQ_MASK); 195 196 /* Verify zone reset */ 197 tmp = pwr_ctrl_rd(cpu); 198 if (!(tmp & ZONE_RESET_STATE_MASK)) 199 pr_err("%s: Zone reset bit for CPU %d not asserted!\n", 200 __func__, cpu); 201 202 /* Wait for power down */ 203 do { 204 tmp = pwr_ctrl_rd(cpu); 205 } while (!(tmp & ZONE_PWR_OFF_STATE_MASK)); 206 207 /* Settle-time from Broadcom-internal DVT reference code */ 208 udelay(7); 209 210 /* Assert reset on the CPU */ 211 cpu_rst_cfg_set(cpu, 1); 212 213 return 1; 214 } 215 216 #endif /* CONFIG_HOTPLUG_CPU */ 217 218 static int __init setup_hifcpubiuctrl_regs(struct device_node *np) 219 { 220 int rc = 0; 221 char *name; 222 struct device_node *syscon_np = NULL; 223 224 name = "syscon-cpu"; 225 226 syscon_np = of_parse_phandle(np, name, 0); 227 if (!syscon_np) { 228 pr_err("can't find phandle %s\n", name); 229 rc = -EINVAL; 230 goto cleanup; 231 } 232 233 cpubiuctrl_block = of_iomap(syscon_np, 0); 234 if (!cpubiuctrl_block) { 235 pr_err("iomap failed for cpubiuctrl_block\n"); 236 rc = -EINVAL; 237 goto cleanup; 238 } 239 240 rc = of_property_read_u32_index(np, name, CPU0_PWR_ZONE_CTRL_REG, 241 &cpu0_pwr_zone_ctrl_reg); 242 if (rc) { 243 pr_err("failed to read 1st entry from %s property (%d)\n", name, 244 rc); 245 rc = -EINVAL; 246 goto cleanup; 247 } 248 249 rc = of_property_read_u32_index(np, name, CPU_RESET_CONFIG_REG, 250 &cpu_rst_cfg_reg); 251 if (rc) { 252 pr_err("failed to read 2nd entry from %s property (%d)\n", name, 253 rc); 254 rc = -EINVAL; 255 goto cleanup; 256 } 257 258 cleanup: 259 if (syscon_np) 260 of_node_put(syscon_np); 261 262 return rc; 263 } 264 265 static int __init setup_hifcont_regs(struct device_node *np) 266 { 267 int rc = 0; 268 char *name; 269 struct device_node *syscon_np = NULL; 270 271 name = "syscon-cont"; 272 273 syscon_np = of_parse_phandle(np, name, 0); 274 if (!syscon_np) { 275 pr_err("can't find phandle %s\n", name); 276 rc = -EINVAL; 277 goto cleanup; 278 } 279 280 hif_cont_block = of_iomap(syscon_np, 0); 281 if (!hif_cont_block) { 282 pr_err("iomap failed for hif_cont_block\n"); 283 rc = -EINVAL; 284 goto cleanup; 285 } 286 287 /* offset is at top of hif_cont_block */ 288 hif_cont_reg = 0; 289 290 cleanup: 291 if (syscon_np) 292 of_node_put(syscon_np); 293 294 return rc; 295 } 296 297 static void __init brcmstb_cpu_ctrl_setup(unsigned int max_cpus) 298 { 299 int rc; 300 struct device_node *np; 301 char *name; 302 303 name = "brcm,brcmstb-smpboot"; 304 np = of_find_compatible_node(NULL, NULL, name); 305 if (!np) { 306 pr_err("can't find compatible node %s\n", name); 307 return; 308 } 309 310 rc = setup_hifcpubiuctrl_regs(np); 311 if (rc) 312 return; 313 314 rc = setup_hifcont_regs(np); 315 if (rc) 316 return; 317 } 318 319 static DEFINE_SPINLOCK(boot_lock); 320 321 static void brcmstb_secondary_init(unsigned int cpu) 322 { 323 /* 324 * Synchronise with the boot thread. 325 */ 326 spin_lock(&boot_lock); 327 spin_unlock(&boot_lock); 328 } 329 330 static int brcmstb_boot_secondary(unsigned int cpu, struct task_struct *idle) 331 { 332 /* 333 * set synchronisation state between this boot processor 334 * and the secondary one 335 */ 336 spin_lock(&boot_lock); 337 338 /* Bring up power to the core if necessary */ 339 if (brcmstb_cpu_get_power_state(cpu) == 0) 340 brcmstb_cpu_power_on(cpu); 341 342 brcmstb_cpu_boot(cpu); 343 344 /* 345 * now the secondary core is starting up let it run its 346 * calibrations, then wait for it to finish 347 */ 348 spin_unlock(&boot_lock); 349 350 return 0; 351 } 352 353 static struct smp_operations brcmstb_smp_ops __initdata = { 354 .smp_prepare_cpus = brcmstb_cpu_ctrl_setup, 355 .smp_secondary_init = brcmstb_secondary_init, 356 .smp_boot_secondary = brcmstb_boot_secondary, 357 #ifdef CONFIG_HOTPLUG_CPU 358 .cpu_kill = brcmstb_cpu_kill, 359 .cpu_die = brcmstb_cpu_die, 360 #endif 361 }; 362 363 CPU_METHOD_OF_DECLARE(brcmstb_smp, "brcm,brahma-b15", &brcmstb_smp_ops); 364