18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Copyright (C) 2002 ARM Ltd. 48c2ecf20Sopenharmony_ci * All Rights Reserved 58c2ecf20Sopenharmony_ci * Copyright (c) 2010, Code Aurora Forum. All rights reserved. 68c2ecf20Sopenharmony_ci * Copyright (c) 2014 The Linux Foundation. All rights reserved. 78c2ecf20Sopenharmony_ci */ 88c2ecf20Sopenharmony_ci 98c2ecf20Sopenharmony_ci#include <linux/init.h> 108c2ecf20Sopenharmony_ci#include <linux/errno.h> 118c2ecf20Sopenharmony_ci#include <linux/delay.h> 128c2ecf20Sopenharmony_ci#include <linux/device.h> 138c2ecf20Sopenharmony_ci#include <linux/of.h> 148c2ecf20Sopenharmony_ci#include <linux/of_address.h> 158c2ecf20Sopenharmony_ci#include <linux/smp.h> 168c2ecf20Sopenharmony_ci#include <linux/io.h> 178c2ecf20Sopenharmony_ci#include <linux/qcom_scm.h> 188c2ecf20Sopenharmony_ci 198c2ecf20Sopenharmony_ci#include <asm/smp_plat.h> 208c2ecf20Sopenharmony_ci 218c2ecf20Sopenharmony_ci 228c2ecf20Sopenharmony_ci#define VDD_SC1_ARRAY_CLAMP_GFS_CTL 0x35a0 238c2ecf20Sopenharmony_ci#define SCSS_CPU1CORE_RESET 0x2d80 248c2ecf20Sopenharmony_ci#define SCSS_DBG_STATUS_CORE_PWRDUP 0x2e64 258c2ecf20Sopenharmony_ci 268c2ecf20Sopenharmony_ci#define APCS_CPU_PWR_CTL 0x04 278c2ecf20Sopenharmony_ci#define PLL_CLAMP BIT(8) 288c2ecf20Sopenharmony_ci#define CORE_PWRD_UP BIT(7) 298c2ecf20Sopenharmony_ci#define COREPOR_RST BIT(5) 308c2ecf20Sopenharmony_ci#define CORE_RST BIT(4) 318c2ecf20Sopenharmony_ci#define L2DT_SLP BIT(3) 328c2ecf20Sopenharmony_ci#define CLAMP BIT(0) 338c2ecf20Sopenharmony_ci 348c2ecf20Sopenharmony_ci#define APC_PWR_GATE_CTL 0x14 358c2ecf20Sopenharmony_ci#define BHS_CNT_SHIFT 24 368c2ecf20Sopenharmony_ci#define LDO_PWR_DWN_SHIFT 16 378c2ecf20Sopenharmony_ci#define LDO_BYP_SHIFT 8 388c2ecf20Sopenharmony_ci#define BHS_SEG_SHIFT 1 398c2ecf20Sopenharmony_ci#define BHS_EN BIT(0) 408c2ecf20Sopenharmony_ci 418c2ecf20Sopenharmony_ci#define APCS_SAW2_VCTL 0x14 428c2ecf20Sopenharmony_ci#define APCS_SAW2_2_VCTL 0x1c 438c2ecf20Sopenharmony_ci 448c2ecf20Sopenharmony_ciextern void secondary_startup_arm(void); 458c2ecf20Sopenharmony_ci 468c2ecf20Sopenharmony_ci#ifdef CONFIG_HOTPLUG_CPU 478c2ecf20Sopenharmony_cistatic void qcom_cpu_die(unsigned int cpu) 488c2ecf20Sopenharmony_ci{ 498c2ecf20Sopenharmony_ci wfi(); 508c2ecf20Sopenharmony_ci} 518c2ecf20Sopenharmony_ci#endif 528c2ecf20Sopenharmony_ci 538c2ecf20Sopenharmony_cistatic int scss_release_secondary(unsigned int cpu) 548c2ecf20Sopenharmony_ci{ 558c2ecf20Sopenharmony_ci struct device_node *node; 568c2ecf20Sopenharmony_ci void __iomem *base; 578c2ecf20Sopenharmony_ci 588c2ecf20Sopenharmony_ci node = of_find_compatible_node(NULL, NULL, "qcom,gcc-msm8660"); 598c2ecf20Sopenharmony_ci if (!node) { 608c2ecf20Sopenharmony_ci pr_err("%s: can't find node\n", __func__); 618c2ecf20Sopenharmony_ci return -ENXIO; 628c2ecf20Sopenharmony_ci } 638c2ecf20Sopenharmony_ci 648c2ecf20Sopenharmony_ci base = of_iomap(node, 0); 658c2ecf20Sopenharmony_ci of_node_put(node); 668c2ecf20Sopenharmony_ci if (!base) 678c2ecf20Sopenharmony_ci return -ENOMEM; 688c2ecf20Sopenharmony_ci 698c2ecf20Sopenharmony_ci writel_relaxed(0, base + VDD_SC1_ARRAY_CLAMP_GFS_CTL); 708c2ecf20Sopenharmony_ci writel_relaxed(0, base + SCSS_CPU1CORE_RESET); 718c2ecf20Sopenharmony_ci writel_relaxed(3, base + SCSS_DBG_STATUS_CORE_PWRDUP); 728c2ecf20Sopenharmony_ci mb(); 738c2ecf20Sopenharmony_ci iounmap(base); 748c2ecf20Sopenharmony_ci 758c2ecf20Sopenharmony_ci return 0; 768c2ecf20Sopenharmony_ci} 778c2ecf20Sopenharmony_ci 788c2ecf20Sopenharmony_cistatic int kpssv1_release_secondary(unsigned int cpu) 798c2ecf20Sopenharmony_ci{ 808c2ecf20Sopenharmony_ci int ret = 0; 818c2ecf20Sopenharmony_ci void __iomem *reg, *saw_reg; 828c2ecf20Sopenharmony_ci struct device_node *cpu_node, *acc_node, *saw_node; 838c2ecf20Sopenharmony_ci u32 val; 848c2ecf20Sopenharmony_ci 858c2ecf20Sopenharmony_ci cpu_node = of_get_cpu_node(cpu, NULL); 868c2ecf20Sopenharmony_ci if (!cpu_node) 878c2ecf20Sopenharmony_ci return -ENODEV; 888c2ecf20Sopenharmony_ci 898c2ecf20Sopenharmony_ci acc_node = of_parse_phandle(cpu_node, "qcom,acc", 0); 908c2ecf20Sopenharmony_ci if (!acc_node) { 918c2ecf20Sopenharmony_ci ret = -ENODEV; 928c2ecf20Sopenharmony_ci goto out_acc; 938c2ecf20Sopenharmony_ci } 948c2ecf20Sopenharmony_ci 958c2ecf20Sopenharmony_ci saw_node = of_parse_phandle(cpu_node, "qcom,saw", 0); 968c2ecf20Sopenharmony_ci if (!saw_node) { 978c2ecf20Sopenharmony_ci ret = -ENODEV; 988c2ecf20Sopenharmony_ci goto out_saw; 998c2ecf20Sopenharmony_ci } 1008c2ecf20Sopenharmony_ci 1018c2ecf20Sopenharmony_ci reg = of_iomap(acc_node, 0); 1028c2ecf20Sopenharmony_ci if (!reg) { 1038c2ecf20Sopenharmony_ci ret = -ENOMEM; 1048c2ecf20Sopenharmony_ci goto out_acc_map; 1058c2ecf20Sopenharmony_ci } 1068c2ecf20Sopenharmony_ci 1078c2ecf20Sopenharmony_ci saw_reg = of_iomap(saw_node, 0); 1088c2ecf20Sopenharmony_ci if (!saw_reg) { 1098c2ecf20Sopenharmony_ci ret = -ENOMEM; 1108c2ecf20Sopenharmony_ci goto out_saw_map; 1118c2ecf20Sopenharmony_ci } 1128c2ecf20Sopenharmony_ci 1138c2ecf20Sopenharmony_ci /* Turn on CPU rail */ 1148c2ecf20Sopenharmony_ci writel_relaxed(0xA4, saw_reg + APCS_SAW2_VCTL); 1158c2ecf20Sopenharmony_ci mb(); 1168c2ecf20Sopenharmony_ci udelay(512); 1178c2ecf20Sopenharmony_ci 1188c2ecf20Sopenharmony_ci /* Krait bring-up sequence */ 1198c2ecf20Sopenharmony_ci val = PLL_CLAMP | L2DT_SLP | CLAMP; 1208c2ecf20Sopenharmony_ci writel_relaxed(val, reg + APCS_CPU_PWR_CTL); 1218c2ecf20Sopenharmony_ci val &= ~L2DT_SLP; 1228c2ecf20Sopenharmony_ci writel_relaxed(val, reg + APCS_CPU_PWR_CTL); 1238c2ecf20Sopenharmony_ci mb(); 1248c2ecf20Sopenharmony_ci ndelay(300); 1258c2ecf20Sopenharmony_ci 1268c2ecf20Sopenharmony_ci val |= COREPOR_RST; 1278c2ecf20Sopenharmony_ci writel_relaxed(val, reg + APCS_CPU_PWR_CTL); 1288c2ecf20Sopenharmony_ci mb(); 1298c2ecf20Sopenharmony_ci udelay(2); 1308c2ecf20Sopenharmony_ci 1318c2ecf20Sopenharmony_ci val &= ~CLAMP; 1328c2ecf20Sopenharmony_ci writel_relaxed(val, reg + APCS_CPU_PWR_CTL); 1338c2ecf20Sopenharmony_ci mb(); 1348c2ecf20Sopenharmony_ci udelay(2); 1358c2ecf20Sopenharmony_ci 1368c2ecf20Sopenharmony_ci val &= ~COREPOR_RST; 1378c2ecf20Sopenharmony_ci writel_relaxed(val, reg + APCS_CPU_PWR_CTL); 1388c2ecf20Sopenharmony_ci mb(); 1398c2ecf20Sopenharmony_ci udelay(100); 1408c2ecf20Sopenharmony_ci 1418c2ecf20Sopenharmony_ci val |= CORE_PWRD_UP; 1428c2ecf20Sopenharmony_ci writel_relaxed(val, reg + APCS_CPU_PWR_CTL); 1438c2ecf20Sopenharmony_ci mb(); 1448c2ecf20Sopenharmony_ci 1458c2ecf20Sopenharmony_ci iounmap(saw_reg); 1468c2ecf20Sopenharmony_ciout_saw_map: 1478c2ecf20Sopenharmony_ci iounmap(reg); 1488c2ecf20Sopenharmony_ciout_acc_map: 1498c2ecf20Sopenharmony_ci of_node_put(saw_node); 1508c2ecf20Sopenharmony_ciout_saw: 1518c2ecf20Sopenharmony_ci of_node_put(acc_node); 1528c2ecf20Sopenharmony_ciout_acc: 1538c2ecf20Sopenharmony_ci of_node_put(cpu_node); 1548c2ecf20Sopenharmony_ci return ret; 1558c2ecf20Sopenharmony_ci} 1568c2ecf20Sopenharmony_ci 1578c2ecf20Sopenharmony_cistatic int kpssv2_release_secondary(unsigned int cpu) 1588c2ecf20Sopenharmony_ci{ 1598c2ecf20Sopenharmony_ci void __iomem *reg; 1608c2ecf20Sopenharmony_ci struct device_node *cpu_node, *l2_node, *acc_node, *saw_node; 1618c2ecf20Sopenharmony_ci void __iomem *l2_saw_base; 1628c2ecf20Sopenharmony_ci unsigned reg_val; 1638c2ecf20Sopenharmony_ci int ret; 1648c2ecf20Sopenharmony_ci 1658c2ecf20Sopenharmony_ci cpu_node = of_get_cpu_node(cpu, NULL); 1668c2ecf20Sopenharmony_ci if (!cpu_node) 1678c2ecf20Sopenharmony_ci return -ENODEV; 1688c2ecf20Sopenharmony_ci 1698c2ecf20Sopenharmony_ci acc_node = of_parse_phandle(cpu_node, "qcom,acc", 0); 1708c2ecf20Sopenharmony_ci if (!acc_node) { 1718c2ecf20Sopenharmony_ci ret = -ENODEV; 1728c2ecf20Sopenharmony_ci goto out_acc; 1738c2ecf20Sopenharmony_ci } 1748c2ecf20Sopenharmony_ci 1758c2ecf20Sopenharmony_ci l2_node = of_parse_phandle(cpu_node, "next-level-cache", 0); 1768c2ecf20Sopenharmony_ci if (!l2_node) { 1778c2ecf20Sopenharmony_ci ret = -ENODEV; 1788c2ecf20Sopenharmony_ci goto out_l2; 1798c2ecf20Sopenharmony_ci } 1808c2ecf20Sopenharmony_ci 1818c2ecf20Sopenharmony_ci saw_node = of_parse_phandle(l2_node, "qcom,saw", 0); 1828c2ecf20Sopenharmony_ci if (!saw_node) { 1838c2ecf20Sopenharmony_ci ret = -ENODEV; 1848c2ecf20Sopenharmony_ci goto out_saw; 1858c2ecf20Sopenharmony_ci } 1868c2ecf20Sopenharmony_ci 1878c2ecf20Sopenharmony_ci reg = of_iomap(acc_node, 0); 1888c2ecf20Sopenharmony_ci if (!reg) { 1898c2ecf20Sopenharmony_ci ret = -ENOMEM; 1908c2ecf20Sopenharmony_ci goto out_map; 1918c2ecf20Sopenharmony_ci } 1928c2ecf20Sopenharmony_ci 1938c2ecf20Sopenharmony_ci l2_saw_base = of_iomap(saw_node, 0); 1948c2ecf20Sopenharmony_ci if (!l2_saw_base) { 1958c2ecf20Sopenharmony_ci ret = -ENOMEM; 1968c2ecf20Sopenharmony_ci goto out_saw_map; 1978c2ecf20Sopenharmony_ci } 1988c2ecf20Sopenharmony_ci 1998c2ecf20Sopenharmony_ci /* Turn on the BHS, turn off LDO Bypass and power down LDO */ 2008c2ecf20Sopenharmony_ci reg_val = (64 << BHS_CNT_SHIFT) | (0x3f << LDO_PWR_DWN_SHIFT) | BHS_EN; 2018c2ecf20Sopenharmony_ci writel_relaxed(reg_val, reg + APC_PWR_GATE_CTL); 2028c2ecf20Sopenharmony_ci mb(); 2038c2ecf20Sopenharmony_ci /* wait for the BHS to settle */ 2048c2ecf20Sopenharmony_ci udelay(1); 2058c2ecf20Sopenharmony_ci 2068c2ecf20Sopenharmony_ci /* Turn on BHS segments */ 2078c2ecf20Sopenharmony_ci reg_val |= 0x3f << BHS_SEG_SHIFT; 2088c2ecf20Sopenharmony_ci writel_relaxed(reg_val, reg + APC_PWR_GATE_CTL); 2098c2ecf20Sopenharmony_ci mb(); 2108c2ecf20Sopenharmony_ci /* wait for the BHS to settle */ 2118c2ecf20Sopenharmony_ci udelay(1); 2128c2ecf20Sopenharmony_ci 2138c2ecf20Sopenharmony_ci /* Finally turn on the bypass so that BHS supplies power */ 2148c2ecf20Sopenharmony_ci reg_val |= 0x3f << LDO_BYP_SHIFT; 2158c2ecf20Sopenharmony_ci writel_relaxed(reg_val, reg + APC_PWR_GATE_CTL); 2168c2ecf20Sopenharmony_ci 2178c2ecf20Sopenharmony_ci /* enable max phases */ 2188c2ecf20Sopenharmony_ci writel_relaxed(0x10003, l2_saw_base + APCS_SAW2_2_VCTL); 2198c2ecf20Sopenharmony_ci mb(); 2208c2ecf20Sopenharmony_ci udelay(50); 2218c2ecf20Sopenharmony_ci 2228c2ecf20Sopenharmony_ci reg_val = COREPOR_RST | CLAMP; 2238c2ecf20Sopenharmony_ci writel_relaxed(reg_val, reg + APCS_CPU_PWR_CTL); 2248c2ecf20Sopenharmony_ci mb(); 2258c2ecf20Sopenharmony_ci udelay(2); 2268c2ecf20Sopenharmony_ci 2278c2ecf20Sopenharmony_ci reg_val &= ~CLAMP; 2288c2ecf20Sopenharmony_ci writel_relaxed(reg_val, reg + APCS_CPU_PWR_CTL); 2298c2ecf20Sopenharmony_ci mb(); 2308c2ecf20Sopenharmony_ci udelay(2); 2318c2ecf20Sopenharmony_ci 2328c2ecf20Sopenharmony_ci reg_val &= ~COREPOR_RST; 2338c2ecf20Sopenharmony_ci writel_relaxed(reg_val, reg + APCS_CPU_PWR_CTL); 2348c2ecf20Sopenharmony_ci mb(); 2358c2ecf20Sopenharmony_ci 2368c2ecf20Sopenharmony_ci reg_val |= CORE_PWRD_UP; 2378c2ecf20Sopenharmony_ci writel_relaxed(reg_val, reg + APCS_CPU_PWR_CTL); 2388c2ecf20Sopenharmony_ci mb(); 2398c2ecf20Sopenharmony_ci 2408c2ecf20Sopenharmony_ci ret = 0; 2418c2ecf20Sopenharmony_ci 2428c2ecf20Sopenharmony_ci iounmap(l2_saw_base); 2438c2ecf20Sopenharmony_ciout_saw_map: 2448c2ecf20Sopenharmony_ci iounmap(reg); 2458c2ecf20Sopenharmony_ciout_map: 2468c2ecf20Sopenharmony_ci of_node_put(saw_node); 2478c2ecf20Sopenharmony_ciout_saw: 2488c2ecf20Sopenharmony_ci of_node_put(l2_node); 2498c2ecf20Sopenharmony_ciout_l2: 2508c2ecf20Sopenharmony_ci of_node_put(acc_node); 2518c2ecf20Sopenharmony_ciout_acc: 2528c2ecf20Sopenharmony_ci of_node_put(cpu_node); 2538c2ecf20Sopenharmony_ci 2548c2ecf20Sopenharmony_ci return ret; 2558c2ecf20Sopenharmony_ci} 2568c2ecf20Sopenharmony_ci 2578c2ecf20Sopenharmony_cistatic DEFINE_PER_CPU(int, cold_boot_done); 2588c2ecf20Sopenharmony_ci 2598c2ecf20Sopenharmony_cistatic int qcom_boot_secondary(unsigned int cpu, int (*func)(unsigned int)) 2608c2ecf20Sopenharmony_ci{ 2618c2ecf20Sopenharmony_ci int ret = 0; 2628c2ecf20Sopenharmony_ci 2638c2ecf20Sopenharmony_ci if (!per_cpu(cold_boot_done, cpu)) { 2648c2ecf20Sopenharmony_ci ret = func(cpu); 2658c2ecf20Sopenharmony_ci if (!ret) 2668c2ecf20Sopenharmony_ci per_cpu(cold_boot_done, cpu) = true; 2678c2ecf20Sopenharmony_ci } 2688c2ecf20Sopenharmony_ci 2698c2ecf20Sopenharmony_ci /* 2708c2ecf20Sopenharmony_ci * Send the secondary CPU a soft interrupt, thereby causing 2718c2ecf20Sopenharmony_ci * the boot monitor to read the system wide flags register, 2728c2ecf20Sopenharmony_ci * and branch to the address found there. 2738c2ecf20Sopenharmony_ci */ 2748c2ecf20Sopenharmony_ci arch_send_wakeup_ipi_mask(cpumask_of(cpu)); 2758c2ecf20Sopenharmony_ci 2768c2ecf20Sopenharmony_ci return ret; 2778c2ecf20Sopenharmony_ci} 2788c2ecf20Sopenharmony_ci 2798c2ecf20Sopenharmony_cistatic int msm8660_boot_secondary(unsigned int cpu, struct task_struct *idle) 2808c2ecf20Sopenharmony_ci{ 2818c2ecf20Sopenharmony_ci return qcom_boot_secondary(cpu, scss_release_secondary); 2828c2ecf20Sopenharmony_ci} 2838c2ecf20Sopenharmony_ci 2848c2ecf20Sopenharmony_cistatic int kpssv1_boot_secondary(unsigned int cpu, struct task_struct *idle) 2858c2ecf20Sopenharmony_ci{ 2868c2ecf20Sopenharmony_ci return qcom_boot_secondary(cpu, kpssv1_release_secondary); 2878c2ecf20Sopenharmony_ci} 2888c2ecf20Sopenharmony_ci 2898c2ecf20Sopenharmony_cistatic int kpssv2_boot_secondary(unsigned int cpu, struct task_struct *idle) 2908c2ecf20Sopenharmony_ci{ 2918c2ecf20Sopenharmony_ci return qcom_boot_secondary(cpu, kpssv2_release_secondary); 2928c2ecf20Sopenharmony_ci} 2938c2ecf20Sopenharmony_ci 2948c2ecf20Sopenharmony_cistatic void __init qcom_smp_prepare_cpus(unsigned int max_cpus) 2958c2ecf20Sopenharmony_ci{ 2968c2ecf20Sopenharmony_ci int cpu; 2978c2ecf20Sopenharmony_ci 2988c2ecf20Sopenharmony_ci if (qcom_scm_set_cold_boot_addr(secondary_startup_arm, 2998c2ecf20Sopenharmony_ci cpu_present_mask)) { 3008c2ecf20Sopenharmony_ci for_each_present_cpu(cpu) { 3018c2ecf20Sopenharmony_ci if (cpu == smp_processor_id()) 3028c2ecf20Sopenharmony_ci continue; 3038c2ecf20Sopenharmony_ci set_cpu_present(cpu, false); 3048c2ecf20Sopenharmony_ci } 3058c2ecf20Sopenharmony_ci pr_warn("Failed to set CPU boot address, disabling SMP\n"); 3068c2ecf20Sopenharmony_ci } 3078c2ecf20Sopenharmony_ci} 3088c2ecf20Sopenharmony_ci 3098c2ecf20Sopenharmony_cistatic const struct smp_operations smp_msm8660_ops __initconst = { 3108c2ecf20Sopenharmony_ci .smp_prepare_cpus = qcom_smp_prepare_cpus, 3118c2ecf20Sopenharmony_ci .smp_boot_secondary = msm8660_boot_secondary, 3128c2ecf20Sopenharmony_ci#ifdef CONFIG_HOTPLUG_CPU 3138c2ecf20Sopenharmony_ci .cpu_die = qcom_cpu_die, 3148c2ecf20Sopenharmony_ci#endif 3158c2ecf20Sopenharmony_ci}; 3168c2ecf20Sopenharmony_ciCPU_METHOD_OF_DECLARE(qcom_smp, "qcom,gcc-msm8660", &smp_msm8660_ops); 3178c2ecf20Sopenharmony_ci 3188c2ecf20Sopenharmony_cistatic const struct smp_operations qcom_smp_kpssv1_ops __initconst = { 3198c2ecf20Sopenharmony_ci .smp_prepare_cpus = qcom_smp_prepare_cpus, 3208c2ecf20Sopenharmony_ci .smp_boot_secondary = kpssv1_boot_secondary, 3218c2ecf20Sopenharmony_ci#ifdef CONFIG_HOTPLUG_CPU 3228c2ecf20Sopenharmony_ci .cpu_die = qcom_cpu_die, 3238c2ecf20Sopenharmony_ci#endif 3248c2ecf20Sopenharmony_ci}; 3258c2ecf20Sopenharmony_ciCPU_METHOD_OF_DECLARE(qcom_smp_kpssv1, "qcom,kpss-acc-v1", &qcom_smp_kpssv1_ops); 3268c2ecf20Sopenharmony_ci 3278c2ecf20Sopenharmony_cistatic const struct smp_operations qcom_smp_kpssv2_ops __initconst = { 3288c2ecf20Sopenharmony_ci .smp_prepare_cpus = qcom_smp_prepare_cpus, 3298c2ecf20Sopenharmony_ci .smp_boot_secondary = kpssv2_boot_secondary, 3308c2ecf20Sopenharmony_ci#ifdef CONFIG_HOTPLUG_CPU 3318c2ecf20Sopenharmony_ci .cpu_die = qcom_cpu_die, 3328c2ecf20Sopenharmony_ci#endif 3338c2ecf20Sopenharmony_ci}; 3348c2ecf20Sopenharmony_ciCPU_METHOD_OF_DECLARE(qcom_smp_kpssv2, "qcom,kpss-acc-v2", &qcom_smp_kpssv2_ops); 335