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 47 extern void secondary_startup_arm(void); 48 49 #ifdef CONFIG_HOTPLUG_CPU 50 static void qcom_cpu_die(unsigned int cpu) 51 { 52 wfi(); 53 } 54 #endif 55 56 static int scss_release_secondary(unsigned int cpu) 57 { 58 struct device_node *node; 59 void __iomem *base; 60 61 node = of_find_compatible_node(NULL, NULL, "qcom,gcc-msm8660"); 62 if (!node) { 63 pr_err("%s: can't find node\n", __func__); 64 return -ENXIO; 65 } 66 67 base = of_iomap(node, 0); 68 of_node_put(node); 69 if (!base) 70 return -ENOMEM; 71 72 writel_relaxed(0, base + VDD_SC1_ARRAY_CLAMP_GFS_CTL); 73 writel_relaxed(0, base + SCSS_CPU1CORE_RESET); 74 writel_relaxed(3, base + SCSS_DBG_STATUS_CORE_PWRDUP); 75 mb(); 76 iounmap(base); 77 78 return 0; 79 } 80 81 static int kpssv1_release_secondary(unsigned int cpu) 82 { 83 int ret = 0; 84 void __iomem *reg, *saw_reg; 85 struct device_node *cpu_node, *acc_node, *saw_node; 86 u32 val; 87 88 cpu_node = of_get_cpu_node(cpu, NULL); 89 if (!cpu_node) 90 return -ENODEV; 91 92 acc_node = of_parse_phandle(cpu_node, "qcom,acc", 0); 93 if (!acc_node) { 94 ret = -ENODEV; 95 goto out_acc; 96 } 97 98 saw_node = of_parse_phandle(cpu_node, "qcom,saw", 0); 99 if (!saw_node) { 100 ret = -ENODEV; 101 goto out_saw; 102 } 103 104 reg = of_iomap(acc_node, 0); 105 if (!reg) { 106 ret = -ENOMEM; 107 goto out_acc_map; 108 } 109 110 saw_reg = of_iomap(saw_node, 0); 111 if (!saw_reg) { 112 ret = -ENOMEM; 113 goto out_saw_map; 114 } 115 116 /* Turn on CPU rail */ 117 writel_relaxed(0xA4, saw_reg + APCS_SAW2_VCTL); 118 mb(); 119 udelay(512); 120 121 /* Krait bring-up sequence */ 122 val = PLL_CLAMP | L2DT_SLP | CLAMP; 123 writel_relaxed(val, reg + APCS_CPU_PWR_CTL); 124 val &= ~L2DT_SLP; 125 writel_relaxed(val, reg + APCS_CPU_PWR_CTL); 126 mb(); 127 ndelay(300); 128 129 val |= COREPOR_RST; 130 writel_relaxed(val, reg + APCS_CPU_PWR_CTL); 131 mb(); 132 udelay(2); 133 134 val &= ~CLAMP; 135 writel_relaxed(val, reg + APCS_CPU_PWR_CTL); 136 mb(); 137 udelay(2); 138 139 val &= ~COREPOR_RST; 140 writel_relaxed(val, reg + APCS_CPU_PWR_CTL); 141 mb(); 142 udelay(100); 143 144 val |= CORE_PWRD_UP; 145 writel_relaxed(val, reg + APCS_CPU_PWR_CTL); 146 mb(); 147 148 iounmap(saw_reg); 149 out_saw_map: 150 iounmap(reg); 151 out_acc_map: 152 of_node_put(saw_node); 153 out_saw: 154 of_node_put(acc_node); 155 out_acc: 156 of_node_put(cpu_node); 157 return ret; 158 } 159 160 static int kpssv2_release_secondary(unsigned int cpu) 161 { 162 void __iomem *reg; 163 struct device_node *cpu_node, *l2_node, *acc_node, *saw_node; 164 void __iomem *l2_saw_base; 165 unsigned reg_val; 166 int ret; 167 168 cpu_node = of_get_cpu_node(cpu, NULL); 169 if (!cpu_node) 170 return -ENODEV; 171 172 acc_node = of_parse_phandle(cpu_node, "qcom,acc", 0); 173 if (!acc_node) { 174 ret = -ENODEV; 175 goto out_acc; 176 } 177 178 l2_node = of_parse_phandle(cpu_node, "next-level-cache", 0); 179 if (!l2_node) { 180 ret = -ENODEV; 181 goto out_l2; 182 } 183 184 saw_node = of_parse_phandle(l2_node, "qcom,saw", 0); 185 if (!saw_node) { 186 ret = -ENODEV; 187 goto out_saw; 188 } 189 190 reg = of_iomap(acc_node, 0); 191 if (!reg) { 192 ret = -ENOMEM; 193 goto out_map; 194 } 195 196 l2_saw_base = of_iomap(saw_node, 0); 197 if (!l2_saw_base) { 198 ret = -ENOMEM; 199 goto out_saw_map; 200 } 201 202 /* Turn on the BHS, turn off LDO Bypass and power down LDO */ 203 reg_val = (64 << BHS_CNT_SHIFT) | (0x3f << LDO_PWR_DWN_SHIFT) | BHS_EN; 204 writel_relaxed(reg_val, reg + APC_PWR_GATE_CTL); 205 mb(); 206 /* wait for the BHS to settle */ 207 udelay(1); 208 209 /* Turn on BHS segments */ 210 reg_val |= 0x3f << BHS_SEG_SHIFT; 211 writel_relaxed(reg_val, reg + APC_PWR_GATE_CTL); 212 mb(); 213 /* wait for the BHS to settle */ 214 udelay(1); 215 216 /* Finally turn on the bypass so that BHS supplies power */ 217 reg_val |= 0x3f << LDO_BYP_SHIFT; 218 writel_relaxed(reg_val, reg + APC_PWR_GATE_CTL); 219 220 /* enable max phases */ 221 writel_relaxed(0x10003, l2_saw_base + APCS_SAW2_2_VCTL); 222 mb(); 223 udelay(50); 224 225 reg_val = COREPOR_RST | CLAMP; 226 writel_relaxed(reg_val, reg + APCS_CPU_PWR_CTL); 227 mb(); 228 udelay(2); 229 230 reg_val &= ~CLAMP; 231 writel_relaxed(reg_val, reg + APCS_CPU_PWR_CTL); 232 mb(); 233 udelay(2); 234 235 reg_val &= ~COREPOR_RST; 236 writel_relaxed(reg_val, reg + APCS_CPU_PWR_CTL); 237 mb(); 238 239 reg_val |= CORE_PWRD_UP; 240 writel_relaxed(reg_val, reg + APCS_CPU_PWR_CTL); 241 mb(); 242 243 ret = 0; 244 245 iounmap(l2_saw_base); 246 out_saw_map: 247 iounmap(reg); 248 out_map: 249 of_node_put(saw_node); 250 out_saw: 251 of_node_put(l2_node); 252 out_l2: 253 of_node_put(acc_node); 254 out_acc: 255 of_node_put(cpu_node); 256 257 return ret; 258 } 259 260 static DEFINE_PER_CPU(int, cold_boot_done); 261 262 static int qcom_boot_secondary(unsigned int cpu, int (*func)(unsigned int)) 263 { 264 int ret = 0; 265 266 if (!per_cpu(cold_boot_done, cpu)) { 267 ret = func(cpu); 268 if (!ret) 269 per_cpu(cold_boot_done, cpu) = true; 270 } 271 272 /* 273 * Send the secondary CPU a soft interrupt, thereby causing 274 * the boot monitor to read the system wide flags register, 275 * and branch to the address found there. 276 */ 277 arch_send_wakeup_ipi_mask(cpumask_of(cpu)); 278 279 return ret; 280 } 281 282 static int msm8660_boot_secondary(unsigned int cpu, struct task_struct *idle) 283 { 284 return qcom_boot_secondary(cpu, scss_release_secondary); 285 } 286 287 static int kpssv1_boot_secondary(unsigned int cpu, struct task_struct *idle) 288 { 289 return qcom_boot_secondary(cpu, kpssv1_release_secondary); 290 } 291 292 static int kpssv2_boot_secondary(unsigned int cpu, struct task_struct *idle) 293 { 294 return qcom_boot_secondary(cpu, kpssv2_release_secondary); 295 } 296 297 static void __init qcom_smp_prepare_cpus(unsigned int max_cpus) 298 { 299 int cpu; 300 301 if (qcom_scm_set_cold_boot_addr(secondary_startup_arm, 302 cpu_present_mask)) { 303 for_each_present_cpu(cpu) { 304 if (cpu == smp_processor_id()) 305 continue; 306 set_cpu_present(cpu, false); 307 } 308 pr_warn("Failed to set CPU boot address, disabling SMP\n"); 309 } 310 } 311 312 static const struct smp_operations smp_msm8660_ops __initconst = { 313 .smp_prepare_cpus = qcom_smp_prepare_cpus, 314 .smp_boot_secondary = msm8660_boot_secondary, 315 #ifdef CONFIG_HOTPLUG_CPU 316 .cpu_die = qcom_cpu_die, 317 #endif 318 }; 319 CPU_METHOD_OF_DECLARE(qcom_smp, "qcom,gcc-msm8660", &smp_msm8660_ops); 320 321 static const struct smp_operations qcom_smp_kpssv1_ops __initconst = { 322 .smp_prepare_cpus = qcom_smp_prepare_cpus, 323 .smp_boot_secondary = kpssv1_boot_secondary, 324 #ifdef CONFIG_HOTPLUG_CPU 325 .cpu_die = qcom_cpu_die, 326 #endif 327 }; 328 CPU_METHOD_OF_DECLARE(qcom_smp_kpssv1, "qcom,kpss-acc-v1", &qcom_smp_kpssv1_ops); 329 330 static const struct smp_operations qcom_smp_kpssv2_ops __initconst = { 331 .smp_prepare_cpus = qcom_smp_prepare_cpus, 332 .smp_boot_secondary = kpssv2_boot_secondary, 333 #ifdef CONFIG_HOTPLUG_CPU 334 .cpu_die = qcom_cpu_die, 335 #endif 336 }; 337 CPU_METHOD_OF_DECLARE(qcom_smp_kpssv2, "qcom,kpss-acc-v2", &qcom_smp_kpssv2_ops); 338