1/* 2 * Copyright (C) 2002 ARM Ltd. 3 * All Rights Reserved 4 * Copyright (c) 2010, Code Aurora Forum. All rights reserved. 5 * Copyright (c) 2014 The Linux Foundation. All rights reserved. 6 * 7 * This program is free software; you can redistribute it and/or modify 8 * it under the terms of the GNU General Public License version 2 as 9 * published by the Free Software Foundation. 10 */ 11 12#include <linux/init.h> 13#include <linux/errno.h> 14#include <linux/delay.h> 15#include <linux/device.h> 16#include <linux/of.h> 17#include <linux/of_address.h> 18#include <linux/smp.h> 19#include <linux/io.h> 20#include <linux/qcom_scm.h> 21 22#include <asm/smp_plat.h> 23 24 25#define VDD_SC1_ARRAY_CLAMP_GFS_CTL 0x35a0 26#define SCSS_CPU1CORE_RESET 0x2d80 27#define SCSS_DBG_STATUS_CORE_PWRDUP 0x2e64 28 29#define APCS_CPU_PWR_CTL 0x04 30#define PLL_CLAMP BIT(8) 31#define CORE_PWRD_UP BIT(7) 32#define COREPOR_RST BIT(5) 33#define CORE_RST BIT(4) 34#define L2DT_SLP BIT(3) 35#define CLAMP BIT(0) 36 37#define APC_PWR_GATE_CTL 0x14 38#define BHS_CNT_SHIFT 24 39#define LDO_PWR_DWN_SHIFT 16 40#define LDO_BYP_SHIFT 8 41#define BHS_SEG_SHIFT 1 42#define BHS_EN BIT(0) 43 44#define APCS_SAW2_VCTL 0x14 45#define APCS_SAW2_2_VCTL 0x1c 46 47extern void secondary_startup_arm(void); 48 49static DEFINE_SPINLOCK(boot_lock); 50 51#ifdef CONFIG_HOTPLUG_CPU 52static void __ref qcom_cpu_die(unsigned int cpu) 53{ 54 wfi(); 55} 56#endif 57 58static void qcom_secondary_init(unsigned int cpu) 59{ 60 /* 61 * Synchronise with the boot thread. 62 */ 63 spin_lock(&boot_lock); 64 spin_unlock(&boot_lock); 65} 66 67static int scss_release_secondary(unsigned int cpu) 68{ 69 struct device_node *node; 70 void __iomem *base; 71 72 node = of_find_compatible_node(NULL, NULL, "qcom,gcc-msm8660"); 73 if (!node) { 74 pr_err("%s: can't find node\n", __func__); 75 return -ENXIO; 76 } 77 78 base = of_iomap(node, 0); 79 of_node_put(node); 80 if (!base) 81 return -ENOMEM; 82 83 writel_relaxed(0, base + VDD_SC1_ARRAY_CLAMP_GFS_CTL); 84 writel_relaxed(0, base + SCSS_CPU1CORE_RESET); 85 writel_relaxed(3, base + SCSS_DBG_STATUS_CORE_PWRDUP); 86 mb(); 87 iounmap(base); 88 89 return 0; 90} 91 92static int kpssv1_release_secondary(unsigned int cpu) 93{ 94 int ret = 0; 95 void __iomem *reg, *saw_reg; 96 struct device_node *cpu_node, *acc_node, *saw_node; 97 u32 val; 98 99 cpu_node = of_get_cpu_node(cpu, NULL); 100 if (!cpu_node) 101 return -ENODEV; 102 103 acc_node = of_parse_phandle(cpu_node, "qcom,acc", 0); 104 if (!acc_node) { 105 ret = -ENODEV; 106 goto out_acc; 107 } 108 109 saw_node = of_parse_phandle(cpu_node, "qcom,saw", 0); 110 if (!saw_node) { 111 ret = -ENODEV; 112 goto out_saw; 113 } 114 115 reg = of_iomap(acc_node, 0); 116 if (!reg) { 117 ret = -ENOMEM; 118 goto out_acc_map; 119 } 120 121 saw_reg = of_iomap(saw_node, 0); 122 if (!saw_reg) { 123 ret = -ENOMEM; 124 goto out_saw_map; 125 } 126 127 /* Turn on CPU rail */ 128 writel_relaxed(0xA4, saw_reg + APCS_SAW2_VCTL); 129 mb(); 130 udelay(512); 131 132 /* Krait bring-up sequence */ 133 val = PLL_CLAMP | L2DT_SLP | CLAMP; 134 writel_relaxed(val, reg + APCS_CPU_PWR_CTL); 135 val &= ~L2DT_SLP; 136 writel_relaxed(val, reg + APCS_CPU_PWR_CTL); 137 mb(); 138 ndelay(300); 139 140 val |= COREPOR_RST; 141 writel_relaxed(val, reg + APCS_CPU_PWR_CTL); 142 mb(); 143 udelay(2); 144 145 val &= ~CLAMP; 146 writel_relaxed(val, reg + APCS_CPU_PWR_CTL); 147 mb(); 148 udelay(2); 149 150 val &= ~COREPOR_RST; 151 writel_relaxed(val, reg + APCS_CPU_PWR_CTL); 152 mb(); 153 udelay(100); 154 155 val |= CORE_PWRD_UP; 156 writel_relaxed(val, reg + APCS_CPU_PWR_CTL); 157 mb(); 158 159 iounmap(saw_reg); 160out_saw_map: 161 iounmap(reg); 162out_acc_map: 163 of_node_put(saw_node); 164out_saw: 165 of_node_put(acc_node); 166out_acc: 167 of_node_put(cpu_node); 168 return ret; 169} 170 171static int kpssv2_release_secondary(unsigned int cpu) 172{ 173 void __iomem *reg; 174 struct device_node *cpu_node, *l2_node, *acc_node, *saw_node; 175 void __iomem *l2_saw_base; 176 unsigned reg_val; 177 int ret; 178 179 cpu_node = of_get_cpu_node(cpu, NULL); 180 if (!cpu_node) 181 return -ENODEV; 182 183 acc_node = of_parse_phandle(cpu_node, "qcom,acc", 0); 184 if (!acc_node) { 185 ret = -ENODEV; 186 goto out_acc; 187 } 188 189 l2_node = of_parse_phandle(cpu_node, "next-level-cache", 0); 190 if (!l2_node) { 191 ret = -ENODEV; 192 goto out_l2; 193 } 194 195 saw_node = of_parse_phandle(l2_node, "qcom,saw", 0); 196 if (!saw_node) { 197 ret = -ENODEV; 198 goto out_saw; 199 } 200 201 reg = of_iomap(acc_node, 0); 202 if (!reg) { 203 ret = -ENOMEM; 204 goto out_map; 205 } 206 207 l2_saw_base = of_iomap(saw_node, 0); 208 if (!l2_saw_base) { 209 ret = -ENOMEM; 210 goto out_saw_map; 211 } 212 213 /* Turn on the BHS, turn off LDO Bypass and power down LDO */ 214 reg_val = (64 << BHS_CNT_SHIFT) | (0x3f << LDO_PWR_DWN_SHIFT) | BHS_EN; 215 writel_relaxed(reg_val, reg + APC_PWR_GATE_CTL); 216 mb(); 217 /* wait for the BHS to settle */ 218 udelay(1); 219 220 /* Turn on BHS segments */ 221 reg_val |= 0x3f << BHS_SEG_SHIFT; 222 writel_relaxed(reg_val, reg + APC_PWR_GATE_CTL); 223 mb(); 224 /* wait for the BHS to settle */ 225 udelay(1); 226 227 /* Finally turn on the bypass so that BHS supplies power */ 228 reg_val |= 0x3f << LDO_BYP_SHIFT; 229 writel_relaxed(reg_val, reg + APC_PWR_GATE_CTL); 230 231 /* enable max phases */ 232 writel_relaxed(0x10003, l2_saw_base + APCS_SAW2_2_VCTL); 233 mb(); 234 udelay(50); 235 236 reg_val = COREPOR_RST | CLAMP; 237 writel_relaxed(reg_val, reg + APCS_CPU_PWR_CTL); 238 mb(); 239 udelay(2); 240 241 reg_val &= ~CLAMP; 242 writel_relaxed(reg_val, reg + APCS_CPU_PWR_CTL); 243 mb(); 244 udelay(2); 245 246 reg_val &= ~COREPOR_RST; 247 writel_relaxed(reg_val, reg + APCS_CPU_PWR_CTL); 248 mb(); 249 250 reg_val |= CORE_PWRD_UP; 251 writel_relaxed(reg_val, reg + APCS_CPU_PWR_CTL); 252 mb(); 253 254 ret = 0; 255 256 iounmap(l2_saw_base); 257out_saw_map: 258 iounmap(reg); 259out_map: 260 of_node_put(saw_node); 261out_saw: 262 of_node_put(l2_node); 263out_l2: 264 of_node_put(acc_node); 265out_acc: 266 of_node_put(cpu_node); 267 268 return ret; 269} 270 271static DEFINE_PER_CPU(int, cold_boot_done); 272 273static int qcom_boot_secondary(unsigned int cpu, int (*func)(unsigned int)) 274{ 275 int ret = 0; 276 277 if (!per_cpu(cold_boot_done, cpu)) { 278 ret = func(cpu); 279 if (!ret) 280 per_cpu(cold_boot_done, cpu) = true; 281 } 282 283 /* 284 * set synchronisation state between this boot processor 285 * and the secondary one 286 */ 287 spin_lock(&boot_lock); 288 289 /* 290 * Send the secondary CPU a soft interrupt, thereby causing 291 * the boot monitor to read the system wide flags register, 292 * and branch to the address found there. 293 */ 294 arch_send_wakeup_ipi_mask(cpumask_of(cpu)); 295 296 /* 297 * now the secondary core is starting up let it run its 298 * calibrations, then wait for it to finish 299 */ 300 spin_unlock(&boot_lock); 301 302 return ret; 303} 304 305static int msm8660_boot_secondary(unsigned int cpu, struct task_struct *idle) 306{ 307 return qcom_boot_secondary(cpu, scss_release_secondary); 308} 309 310static int kpssv1_boot_secondary(unsigned int cpu, struct task_struct *idle) 311{ 312 return qcom_boot_secondary(cpu, kpssv1_release_secondary); 313} 314 315static int kpssv2_boot_secondary(unsigned int cpu, struct task_struct *idle) 316{ 317 return qcom_boot_secondary(cpu, kpssv2_release_secondary); 318} 319 320static void __init qcom_smp_prepare_cpus(unsigned int max_cpus) 321{ 322 int cpu; 323 324 if (qcom_scm_set_cold_boot_addr(secondary_startup_arm, 325 cpu_present_mask)) { 326 for_each_present_cpu(cpu) { 327 if (cpu == smp_processor_id()) 328 continue; 329 set_cpu_present(cpu, false); 330 } 331 pr_warn("Failed to set CPU boot address, disabling SMP\n"); 332 } 333} 334 335static struct smp_operations smp_msm8660_ops __initdata = { 336 .smp_prepare_cpus = qcom_smp_prepare_cpus, 337 .smp_secondary_init = qcom_secondary_init, 338 .smp_boot_secondary = msm8660_boot_secondary, 339#ifdef CONFIG_HOTPLUG_CPU 340 .cpu_die = qcom_cpu_die, 341#endif 342}; 343CPU_METHOD_OF_DECLARE(qcom_smp, "qcom,gcc-msm8660", &smp_msm8660_ops); 344 345static struct smp_operations qcom_smp_kpssv1_ops __initdata = { 346 .smp_prepare_cpus = qcom_smp_prepare_cpus, 347 .smp_secondary_init = qcom_secondary_init, 348 .smp_boot_secondary = kpssv1_boot_secondary, 349#ifdef CONFIG_HOTPLUG_CPU 350 .cpu_die = qcom_cpu_die, 351#endif 352}; 353CPU_METHOD_OF_DECLARE(qcom_smp_kpssv1, "qcom,kpss-acc-v1", &qcom_smp_kpssv1_ops); 354 355static struct smp_operations qcom_smp_kpssv2_ops __initdata = { 356 .smp_prepare_cpus = qcom_smp_prepare_cpus, 357 .smp_secondary_init = qcom_secondary_init, 358 .smp_boot_secondary = kpssv2_boot_secondary, 359#ifdef CONFIG_HOTPLUG_CPU 360 .cpu_die = qcom_cpu_die, 361#endif 362}; 363CPU_METHOD_OF_DECLARE(qcom_smp_kpssv2, "qcom,kpss-acc-v2", &qcom_smp_kpssv2_ops); 364