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