0001
0002
0003
0004
0005
0006
0007
0008
0009 #include <linux/init.h>
0010 #include <linux/errno.h>
0011 #include <linux/delay.h>
0012 #include <linux/device.h>
0013 #include <linux/of.h>
0014 #include <linux/of_address.h>
0015 #include <linux/smp.h>
0016 #include <linux/io.h>
0017 #include <linux/qcom_scm.h>
0018
0019 #include <asm/smp_plat.h>
0020
0021
0022 #define VDD_SC1_ARRAY_CLAMP_GFS_CTL 0x35a0
0023 #define SCSS_CPU1CORE_RESET 0x2d80
0024 #define SCSS_DBG_STATUS_CORE_PWRDUP 0x2e64
0025
0026 #define APCS_CPU_PWR_CTL 0x04
0027 #define PLL_CLAMP BIT(8)
0028 #define CORE_PWRD_UP BIT(7)
0029 #define COREPOR_RST BIT(5)
0030 #define CORE_RST BIT(4)
0031 #define L2DT_SLP BIT(3)
0032 #define CORE_MEM_CLAMP BIT(1)
0033 #define CLAMP BIT(0)
0034
0035 #define APC_PWR_GATE_CTL 0x14
0036 #define BHS_CNT_SHIFT 24
0037 #define LDO_PWR_DWN_SHIFT 16
0038 #define LDO_BYP_SHIFT 8
0039 #define BHS_SEG_SHIFT 1
0040 #define BHS_EN BIT(0)
0041
0042 #define APCS_SAW2_VCTL 0x14
0043 #define APCS_SAW2_2_VCTL 0x1c
0044
0045 extern void secondary_startup_arm(void);
0046
0047 #ifdef CONFIG_HOTPLUG_CPU
0048 static void qcom_cpu_die(unsigned int cpu)
0049 {
0050 wfi();
0051 }
0052 #endif
0053
0054 static int scss_release_secondary(unsigned int cpu)
0055 {
0056 struct device_node *node;
0057 void __iomem *base;
0058
0059 node = of_find_compatible_node(NULL, NULL, "qcom,gcc-msm8660");
0060 if (!node) {
0061 pr_err("%s: can't find node\n", __func__);
0062 return -ENXIO;
0063 }
0064
0065 base = of_iomap(node, 0);
0066 of_node_put(node);
0067 if (!base)
0068 return -ENOMEM;
0069
0070 writel_relaxed(0, base + VDD_SC1_ARRAY_CLAMP_GFS_CTL);
0071 writel_relaxed(0, base + SCSS_CPU1CORE_RESET);
0072 writel_relaxed(3, base + SCSS_DBG_STATUS_CORE_PWRDUP);
0073 mb();
0074 iounmap(base);
0075
0076 return 0;
0077 }
0078
0079 static int cortex_a7_release_secondary(unsigned int cpu)
0080 {
0081 int ret = 0;
0082 void __iomem *reg;
0083 struct device_node *cpu_node, *acc_node;
0084 u32 reg_val;
0085
0086 cpu_node = of_get_cpu_node(cpu, NULL);
0087 if (!cpu_node)
0088 return -ENODEV;
0089
0090 acc_node = of_parse_phandle(cpu_node, "qcom,acc", 0);
0091 if (!acc_node) {
0092 ret = -ENODEV;
0093 goto out_acc;
0094 }
0095
0096 reg = of_iomap(acc_node, 0);
0097 if (!reg) {
0098 ret = -ENOMEM;
0099 goto out_acc_map;
0100 }
0101
0102
0103 reg_val = CORE_RST | COREPOR_RST | CLAMP | CORE_MEM_CLAMP;
0104 writel(reg_val, reg + APCS_CPU_PWR_CTL);
0105
0106
0107 writel(BHS_EN | (0x10 << BHS_CNT_SHIFT), reg + APC_PWR_GATE_CTL);
0108
0109 udelay(2);
0110
0111 reg_val &= ~CORE_MEM_CLAMP;
0112 writel(reg_val, reg + APCS_CPU_PWR_CTL);
0113 reg_val |= L2DT_SLP;
0114 writel(reg_val, reg + APCS_CPU_PWR_CTL);
0115 udelay(2);
0116
0117 reg_val = (reg_val | BIT(17)) & ~CLAMP;
0118 writel(reg_val, reg + APCS_CPU_PWR_CTL);
0119 udelay(2);
0120
0121
0122 reg_val &= ~(CORE_RST | COREPOR_RST);
0123 writel(reg_val, reg + APCS_CPU_PWR_CTL);
0124 reg_val |= CORE_PWRD_UP;
0125 writel(reg_val, reg + APCS_CPU_PWR_CTL);
0126
0127 iounmap(reg);
0128 out_acc_map:
0129 of_node_put(acc_node);
0130 out_acc:
0131 of_node_put(cpu_node);
0132 return ret;
0133 }
0134
0135 static int kpssv1_release_secondary(unsigned int cpu)
0136 {
0137 int ret = 0;
0138 void __iomem *reg, *saw_reg;
0139 struct device_node *cpu_node, *acc_node, *saw_node;
0140 u32 val;
0141
0142 cpu_node = of_get_cpu_node(cpu, NULL);
0143 if (!cpu_node)
0144 return -ENODEV;
0145
0146 acc_node = of_parse_phandle(cpu_node, "qcom,acc", 0);
0147 if (!acc_node) {
0148 ret = -ENODEV;
0149 goto out_acc;
0150 }
0151
0152 saw_node = of_parse_phandle(cpu_node, "qcom,saw", 0);
0153 if (!saw_node) {
0154 ret = -ENODEV;
0155 goto out_saw;
0156 }
0157
0158 reg = of_iomap(acc_node, 0);
0159 if (!reg) {
0160 ret = -ENOMEM;
0161 goto out_acc_map;
0162 }
0163
0164 saw_reg = of_iomap(saw_node, 0);
0165 if (!saw_reg) {
0166 ret = -ENOMEM;
0167 goto out_saw_map;
0168 }
0169
0170
0171 writel_relaxed(0xA4, saw_reg + APCS_SAW2_VCTL);
0172 mb();
0173 udelay(512);
0174
0175
0176 val = PLL_CLAMP | L2DT_SLP | CLAMP;
0177 writel_relaxed(val, reg + APCS_CPU_PWR_CTL);
0178 val &= ~L2DT_SLP;
0179 writel_relaxed(val, reg + APCS_CPU_PWR_CTL);
0180 mb();
0181 ndelay(300);
0182
0183 val |= COREPOR_RST;
0184 writel_relaxed(val, reg + APCS_CPU_PWR_CTL);
0185 mb();
0186 udelay(2);
0187
0188 val &= ~CLAMP;
0189 writel_relaxed(val, reg + APCS_CPU_PWR_CTL);
0190 mb();
0191 udelay(2);
0192
0193 val &= ~COREPOR_RST;
0194 writel_relaxed(val, reg + APCS_CPU_PWR_CTL);
0195 mb();
0196 udelay(100);
0197
0198 val |= CORE_PWRD_UP;
0199 writel_relaxed(val, reg + APCS_CPU_PWR_CTL);
0200 mb();
0201
0202 iounmap(saw_reg);
0203 out_saw_map:
0204 iounmap(reg);
0205 out_acc_map:
0206 of_node_put(saw_node);
0207 out_saw:
0208 of_node_put(acc_node);
0209 out_acc:
0210 of_node_put(cpu_node);
0211 return ret;
0212 }
0213
0214 static int kpssv2_release_secondary(unsigned int cpu)
0215 {
0216 void __iomem *reg;
0217 struct device_node *cpu_node, *l2_node, *acc_node, *saw_node;
0218 void __iomem *l2_saw_base;
0219 unsigned reg_val;
0220 int ret;
0221
0222 cpu_node = of_get_cpu_node(cpu, NULL);
0223 if (!cpu_node)
0224 return -ENODEV;
0225
0226 acc_node = of_parse_phandle(cpu_node, "qcom,acc", 0);
0227 if (!acc_node) {
0228 ret = -ENODEV;
0229 goto out_acc;
0230 }
0231
0232 l2_node = of_parse_phandle(cpu_node, "next-level-cache", 0);
0233 if (!l2_node) {
0234 ret = -ENODEV;
0235 goto out_l2;
0236 }
0237
0238 saw_node = of_parse_phandle(l2_node, "qcom,saw", 0);
0239 if (!saw_node) {
0240 ret = -ENODEV;
0241 goto out_saw;
0242 }
0243
0244 reg = of_iomap(acc_node, 0);
0245 if (!reg) {
0246 ret = -ENOMEM;
0247 goto out_map;
0248 }
0249
0250 l2_saw_base = of_iomap(saw_node, 0);
0251 if (!l2_saw_base) {
0252 ret = -ENOMEM;
0253 goto out_saw_map;
0254 }
0255
0256
0257 reg_val = (64 << BHS_CNT_SHIFT) | (0x3f << LDO_PWR_DWN_SHIFT) | BHS_EN;
0258 writel_relaxed(reg_val, reg + APC_PWR_GATE_CTL);
0259 mb();
0260
0261 udelay(1);
0262
0263
0264 reg_val |= 0x3f << BHS_SEG_SHIFT;
0265 writel_relaxed(reg_val, reg + APC_PWR_GATE_CTL);
0266 mb();
0267
0268 udelay(1);
0269
0270
0271 reg_val |= 0x3f << LDO_BYP_SHIFT;
0272 writel_relaxed(reg_val, reg + APC_PWR_GATE_CTL);
0273
0274
0275 writel_relaxed(0x10003, l2_saw_base + APCS_SAW2_2_VCTL);
0276 mb();
0277 udelay(50);
0278
0279 reg_val = COREPOR_RST | CLAMP;
0280 writel_relaxed(reg_val, reg + APCS_CPU_PWR_CTL);
0281 mb();
0282 udelay(2);
0283
0284 reg_val &= ~CLAMP;
0285 writel_relaxed(reg_val, reg + APCS_CPU_PWR_CTL);
0286 mb();
0287 udelay(2);
0288
0289 reg_val &= ~COREPOR_RST;
0290 writel_relaxed(reg_val, reg + APCS_CPU_PWR_CTL);
0291 mb();
0292
0293 reg_val |= CORE_PWRD_UP;
0294 writel_relaxed(reg_val, reg + APCS_CPU_PWR_CTL);
0295 mb();
0296
0297 ret = 0;
0298
0299 iounmap(l2_saw_base);
0300 out_saw_map:
0301 iounmap(reg);
0302 out_map:
0303 of_node_put(saw_node);
0304 out_saw:
0305 of_node_put(l2_node);
0306 out_l2:
0307 of_node_put(acc_node);
0308 out_acc:
0309 of_node_put(cpu_node);
0310
0311 return ret;
0312 }
0313
0314 static DEFINE_PER_CPU(int, cold_boot_done);
0315
0316 static int qcom_boot_secondary(unsigned int cpu, int (*func)(unsigned int))
0317 {
0318 int ret = 0;
0319
0320 if (!per_cpu(cold_boot_done, cpu)) {
0321 ret = func(cpu);
0322 if (!ret)
0323 per_cpu(cold_boot_done, cpu) = true;
0324 }
0325
0326
0327
0328
0329
0330
0331 arch_send_wakeup_ipi_mask(cpumask_of(cpu));
0332
0333 return ret;
0334 }
0335
0336 static int msm8660_boot_secondary(unsigned int cpu, struct task_struct *idle)
0337 {
0338 return qcom_boot_secondary(cpu, scss_release_secondary);
0339 }
0340
0341 static int cortex_a7_boot_secondary(unsigned int cpu, struct task_struct *idle)
0342 {
0343 return qcom_boot_secondary(cpu, cortex_a7_release_secondary);
0344 }
0345
0346 static int kpssv1_boot_secondary(unsigned int cpu, struct task_struct *idle)
0347 {
0348 return qcom_boot_secondary(cpu, kpssv1_release_secondary);
0349 }
0350
0351 static int kpssv2_boot_secondary(unsigned int cpu, struct task_struct *idle)
0352 {
0353 return qcom_boot_secondary(cpu, kpssv2_release_secondary);
0354 }
0355
0356 static void __init qcom_smp_prepare_cpus(unsigned int max_cpus)
0357 {
0358 int cpu;
0359
0360 if (qcom_scm_set_cold_boot_addr(secondary_startup_arm)) {
0361 for_each_present_cpu(cpu) {
0362 if (cpu == smp_processor_id())
0363 continue;
0364 set_cpu_present(cpu, false);
0365 }
0366 pr_warn("Failed to set CPU boot address, disabling SMP\n");
0367 }
0368 }
0369
0370 static const struct smp_operations smp_msm8660_ops __initconst = {
0371 .smp_prepare_cpus = qcom_smp_prepare_cpus,
0372 .smp_boot_secondary = msm8660_boot_secondary,
0373 #ifdef CONFIG_HOTPLUG_CPU
0374 .cpu_die = qcom_cpu_die,
0375 #endif
0376 };
0377 CPU_METHOD_OF_DECLARE(qcom_smp, "qcom,gcc-msm8660", &smp_msm8660_ops);
0378
0379 static const struct smp_operations qcom_smp_cortex_a7_ops __initconst = {
0380 .smp_prepare_cpus = qcom_smp_prepare_cpus,
0381 .smp_boot_secondary = cortex_a7_boot_secondary,
0382 #ifdef CONFIG_HOTPLUG_CPU
0383 .cpu_die = qcom_cpu_die,
0384 #endif
0385 };
0386 CPU_METHOD_OF_DECLARE(qcom_smp_msm8226, "qcom,msm8226-smp", &qcom_smp_cortex_a7_ops);
0387 CPU_METHOD_OF_DECLARE(qcom_smp_msm8909, "qcom,msm8909-smp", &qcom_smp_cortex_a7_ops);
0388 CPU_METHOD_OF_DECLARE(qcom_smp_msm8916, "qcom,msm8916-smp", &qcom_smp_cortex_a7_ops);
0389
0390 static const struct smp_operations qcom_smp_kpssv1_ops __initconst = {
0391 .smp_prepare_cpus = qcom_smp_prepare_cpus,
0392 .smp_boot_secondary = kpssv1_boot_secondary,
0393 #ifdef CONFIG_HOTPLUG_CPU
0394 .cpu_die = qcom_cpu_die,
0395 #endif
0396 };
0397 CPU_METHOD_OF_DECLARE(qcom_smp_kpssv1, "qcom,kpss-acc-v1", &qcom_smp_kpssv1_ops);
0398
0399 static const struct smp_operations qcom_smp_kpssv2_ops __initconst = {
0400 .smp_prepare_cpus = qcom_smp_prepare_cpus,
0401 .smp_boot_secondary = kpssv2_boot_secondary,
0402 #ifdef CONFIG_HOTPLUG_CPU
0403 .cpu_die = qcom_cpu_die,
0404 #endif
0405 };
0406 CPU_METHOD_OF_DECLARE(qcom_smp_kpssv2, "qcom,kpss-acc-v2", &qcom_smp_kpssv2_ops);