1*c942fddfSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later 2d850f3e5SMartin Blumenstingl /* 3d850f3e5SMartin Blumenstingl * Copyright (C) 2015 Carlo Caione <carlo@endlessm.com> 4d850f3e5SMartin Blumenstingl * Copyright (C) 2017 Martin Blumenstingl <martin.blumenstingl@googlemail.com> 5d850f3e5SMartin Blumenstingl */ 6d850f3e5SMartin Blumenstingl 7d850f3e5SMartin Blumenstingl #include <linux/delay.h> 8d850f3e5SMartin Blumenstingl #include <linux/init.h> 9d850f3e5SMartin Blumenstingl #include <linux/io.h> 10d850f3e5SMartin Blumenstingl #include <linux/of.h> 11d850f3e5SMartin Blumenstingl #include <linux/of_address.h> 12d850f3e5SMartin Blumenstingl #include <linux/regmap.h> 13d850f3e5SMartin Blumenstingl #include <linux/reset.h> 14d850f3e5SMartin Blumenstingl #include <linux/smp.h> 15d850f3e5SMartin Blumenstingl #include <linux/mfd/syscon.h> 16d850f3e5SMartin Blumenstingl 17d850f3e5SMartin Blumenstingl #include <asm/cacheflush.h> 18d850f3e5SMartin Blumenstingl #include <asm/cp15.h> 19d850f3e5SMartin Blumenstingl #include <asm/smp_scu.h> 20d850f3e5SMartin Blumenstingl #include <asm/smp_plat.h> 21d850f3e5SMartin Blumenstingl 22d850f3e5SMartin Blumenstingl #define MESON_SMP_SRAM_CPU_CTRL_REG (0x00) 23d850f3e5SMartin Blumenstingl #define MESON_SMP_SRAM_CPU_CTRL_ADDR_REG(c) (0x04 + ((c - 1) << 2)) 24d850f3e5SMartin Blumenstingl 25d850f3e5SMartin Blumenstingl #define MESON_CPU_AO_RTI_PWR_A9_CNTL0 (0x00) 26d850f3e5SMartin Blumenstingl #define MESON_CPU_AO_RTI_PWR_A9_CNTL1 (0x04) 27d850f3e5SMartin Blumenstingl #define MESON_CPU_AO_RTI_PWR_A9_MEM_PD0 (0x14) 28d850f3e5SMartin Blumenstingl 29d850f3e5SMartin Blumenstingl #define MESON_CPU_PWR_A9_CNTL0_M(c) (0x03 << ((c * 2) + 16)) 30d850f3e5SMartin Blumenstingl #define MESON_CPU_PWR_A9_CNTL1_M(c) (0x03 << ((c + 1) << 1)) 31d850f3e5SMartin Blumenstingl #define MESON_CPU_PWR_A9_MEM_PD0_M(c) (0x0f << (32 - (c * 4))) 32d850f3e5SMartin Blumenstingl #define MESON_CPU_PWR_A9_CNTL1_ST(c) (0x01 << (c + 16)) 33d850f3e5SMartin Blumenstingl 34d850f3e5SMartin Blumenstingl static void __iomem *sram_base; 35d850f3e5SMartin Blumenstingl static void __iomem *scu_base; 36d850f3e5SMartin Blumenstingl static struct regmap *pmu; 37d850f3e5SMartin Blumenstingl 38d850f3e5SMartin Blumenstingl static struct reset_control *meson_smp_get_core_reset(int cpu) 39d850f3e5SMartin Blumenstingl { 40d850f3e5SMartin Blumenstingl struct device_node *np = of_get_cpu_node(cpu, 0); 41d850f3e5SMartin Blumenstingl 42d850f3e5SMartin Blumenstingl return of_reset_control_get_exclusive(np, NULL); 43d850f3e5SMartin Blumenstingl } 44d850f3e5SMartin Blumenstingl 45d850f3e5SMartin Blumenstingl static void meson_smp_set_cpu_ctrl(int cpu, bool on_off) 46d850f3e5SMartin Blumenstingl { 47d850f3e5SMartin Blumenstingl u32 val = readl(sram_base + MESON_SMP_SRAM_CPU_CTRL_REG); 48d850f3e5SMartin Blumenstingl 49d850f3e5SMartin Blumenstingl if (on_off) 50d850f3e5SMartin Blumenstingl val |= BIT(cpu); 51d850f3e5SMartin Blumenstingl else 52d850f3e5SMartin Blumenstingl val &= ~BIT(cpu); 53d850f3e5SMartin Blumenstingl 54d850f3e5SMartin Blumenstingl /* keep bit 0 always enabled */ 55d850f3e5SMartin Blumenstingl val |= BIT(0); 56d850f3e5SMartin Blumenstingl 57d850f3e5SMartin Blumenstingl writel(val, sram_base + MESON_SMP_SRAM_CPU_CTRL_REG); 58d850f3e5SMartin Blumenstingl } 59d850f3e5SMartin Blumenstingl 60d850f3e5SMartin Blumenstingl static void __init meson_smp_prepare_cpus(const char *scu_compatible, 61d850f3e5SMartin Blumenstingl const char *pmu_compatible, 62d850f3e5SMartin Blumenstingl const char *sram_compatible) 63d850f3e5SMartin Blumenstingl { 64d850f3e5SMartin Blumenstingl static struct device_node *node; 65d850f3e5SMartin Blumenstingl 66d850f3e5SMartin Blumenstingl /* SMP SRAM */ 67d850f3e5SMartin Blumenstingl node = of_find_compatible_node(NULL, NULL, sram_compatible); 68d850f3e5SMartin Blumenstingl if (!node) { 69d850f3e5SMartin Blumenstingl pr_err("Missing SRAM node\n"); 70d850f3e5SMartin Blumenstingl return; 71d850f3e5SMartin Blumenstingl } 72d850f3e5SMartin Blumenstingl 73d850f3e5SMartin Blumenstingl sram_base = of_iomap(node, 0); 74d850f3e5SMartin Blumenstingl if (!sram_base) { 75d850f3e5SMartin Blumenstingl pr_err("Couldn't map SRAM registers\n"); 76d850f3e5SMartin Blumenstingl return; 77d850f3e5SMartin Blumenstingl } 78d850f3e5SMartin Blumenstingl 79d850f3e5SMartin Blumenstingl /* PMU */ 80d850f3e5SMartin Blumenstingl pmu = syscon_regmap_lookup_by_compatible(pmu_compatible); 81d850f3e5SMartin Blumenstingl if (IS_ERR(pmu)) { 82d850f3e5SMartin Blumenstingl pr_err("Couldn't map PMU registers\n"); 83d850f3e5SMartin Blumenstingl return; 84d850f3e5SMartin Blumenstingl } 85d850f3e5SMartin Blumenstingl 86d850f3e5SMartin Blumenstingl /* SCU */ 87d850f3e5SMartin Blumenstingl node = of_find_compatible_node(NULL, NULL, scu_compatible); 88d850f3e5SMartin Blumenstingl if (!node) { 89d850f3e5SMartin Blumenstingl pr_err("Missing SCU node\n"); 90d850f3e5SMartin Blumenstingl return; 91d850f3e5SMartin Blumenstingl } 92d850f3e5SMartin Blumenstingl 93d850f3e5SMartin Blumenstingl scu_base = of_iomap(node, 0); 94d850f3e5SMartin Blumenstingl if (!scu_base) { 950f0e290aSColin Ian King pr_err("Couldn't map SCU registers\n"); 96d850f3e5SMartin Blumenstingl return; 97d850f3e5SMartin Blumenstingl } 98d850f3e5SMartin Blumenstingl 99d850f3e5SMartin Blumenstingl scu_enable(scu_base); 100d850f3e5SMartin Blumenstingl } 101d850f3e5SMartin Blumenstingl 102d850f3e5SMartin Blumenstingl static void __init meson8b_smp_prepare_cpus(unsigned int max_cpus) 103d850f3e5SMartin Blumenstingl { 104d850f3e5SMartin Blumenstingl meson_smp_prepare_cpus("arm,cortex-a5-scu", "amlogic,meson8b-pmu", 105d850f3e5SMartin Blumenstingl "amlogic,meson8b-smp-sram"); 106d850f3e5SMartin Blumenstingl } 107d850f3e5SMartin Blumenstingl 108d850f3e5SMartin Blumenstingl static void __init meson8_smp_prepare_cpus(unsigned int max_cpus) 109d850f3e5SMartin Blumenstingl { 110d850f3e5SMartin Blumenstingl meson_smp_prepare_cpus("arm,cortex-a9-scu", "amlogic,meson8-pmu", 111d850f3e5SMartin Blumenstingl "amlogic,meson8-smp-sram"); 112d850f3e5SMartin Blumenstingl } 113d850f3e5SMartin Blumenstingl 114d850f3e5SMartin Blumenstingl static void meson_smp_begin_secondary_boot(unsigned int cpu) 115d850f3e5SMartin Blumenstingl { 116d850f3e5SMartin Blumenstingl /* 117d850f3e5SMartin Blumenstingl * Set the entry point before powering on the CPU through the SCU. This 118d850f3e5SMartin Blumenstingl * is needed if the CPU is in "warm" state (= after rebooting the 119d850f3e5SMartin Blumenstingl * system without power-cycling, or when taking the CPU offline and 120d850f3e5SMartin Blumenstingl * then taking it online again. 121d850f3e5SMartin Blumenstingl */ 122d850f3e5SMartin Blumenstingl writel(__pa_symbol(secondary_startup), 123d850f3e5SMartin Blumenstingl sram_base + MESON_SMP_SRAM_CPU_CTRL_ADDR_REG(cpu)); 124d850f3e5SMartin Blumenstingl 125d850f3e5SMartin Blumenstingl /* 126d850f3e5SMartin Blumenstingl * SCU Power on CPU (needs to be done before starting the CPU, 127d850f3e5SMartin Blumenstingl * otherwise the secondary CPU will not start). 128d850f3e5SMartin Blumenstingl */ 129d850f3e5SMartin Blumenstingl scu_cpu_power_enable(scu_base, cpu); 130d850f3e5SMartin Blumenstingl } 131d850f3e5SMartin Blumenstingl 132d850f3e5SMartin Blumenstingl static int meson_smp_finalize_secondary_boot(unsigned int cpu) 133d850f3e5SMartin Blumenstingl { 134d850f3e5SMartin Blumenstingl unsigned long timeout; 135d850f3e5SMartin Blumenstingl 136d850f3e5SMartin Blumenstingl timeout = jiffies + (10 * HZ); 137d850f3e5SMartin Blumenstingl while (readl(sram_base + MESON_SMP_SRAM_CPU_CTRL_ADDR_REG(cpu))) { 138d850f3e5SMartin Blumenstingl if (!time_before(jiffies, timeout)) { 139d850f3e5SMartin Blumenstingl pr_err("Timeout while waiting for CPU%d status\n", 140d850f3e5SMartin Blumenstingl cpu); 141d850f3e5SMartin Blumenstingl return -ETIMEDOUT; 142d850f3e5SMartin Blumenstingl } 143d850f3e5SMartin Blumenstingl } 144d850f3e5SMartin Blumenstingl 145d850f3e5SMartin Blumenstingl writel(__pa_symbol(secondary_startup), 146d850f3e5SMartin Blumenstingl sram_base + MESON_SMP_SRAM_CPU_CTRL_ADDR_REG(cpu)); 147d850f3e5SMartin Blumenstingl 148d850f3e5SMartin Blumenstingl meson_smp_set_cpu_ctrl(cpu, true); 149d850f3e5SMartin Blumenstingl 150d850f3e5SMartin Blumenstingl return 0; 151d850f3e5SMartin Blumenstingl } 152d850f3e5SMartin Blumenstingl 153d850f3e5SMartin Blumenstingl static int meson8_smp_boot_secondary(unsigned int cpu, 154d850f3e5SMartin Blumenstingl struct task_struct *idle) 155d850f3e5SMartin Blumenstingl { 156d850f3e5SMartin Blumenstingl struct reset_control *rstc; 157d850f3e5SMartin Blumenstingl int ret; 158d850f3e5SMartin Blumenstingl 159d850f3e5SMartin Blumenstingl rstc = meson_smp_get_core_reset(cpu); 160d850f3e5SMartin Blumenstingl if (IS_ERR(rstc)) { 161d850f3e5SMartin Blumenstingl pr_err("Couldn't get the reset controller for CPU%d\n", cpu); 162d850f3e5SMartin Blumenstingl return PTR_ERR(rstc); 163d850f3e5SMartin Blumenstingl } 164d850f3e5SMartin Blumenstingl 165d850f3e5SMartin Blumenstingl meson_smp_begin_secondary_boot(cpu); 166d850f3e5SMartin Blumenstingl 167d850f3e5SMartin Blumenstingl /* Reset enable */ 168d850f3e5SMartin Blumenstingl ret = reset_control_assert(rstc); 169d850f3e5SMartin Blumenstingl if (ret) { 170d850f3e5SMartin Blumenstingl pr_err("Failed to assert CPU%d reset\n", cpu); 171d850f3e5SMartin Blumenstingl goto out; 172d850f3e5SMartin Blumenstingl } 173d850f3e5SMartin Blumenstingl 174d850f3e5SMartin Blumenstingl /* CPU power ON */ 175d850f3e5SMartin Blumenstingl ret = regmap_update_bits(pmu, MESON_CPU_AO_RTI_PWR_A9_CNTL1, 176d850f3e5SMartin Blumenstingl MESON_CPU_PWR_A9_CNTL1_M(cpu), 0); 177d850f3e5SMartin Blumenstingl if (ret < 0) { 178d850f3e5SMartin Blumenstingl pr_err("Couldn't wake up CPU%d\n", cpu); 179d850f3e5SMartin Blumenstingl goto out; 180d850f3e5SMartin Blumenstingl } 181d850f3e5SMartin Blumenstingl 182d850f3e5SMartin Blumenstingl udelay(10); 183d850f3e5SMartin Blumenstingl 184d850f3e5SMartin Blumenstingl /* Isolation disable */ 185d850f3e5SMartin Blumenstingl ret = regmap_update_bits(pmu, MESON_CPU_AO_RTI_PWR_A9_CNTL0, BIT(cpu), 186d850f3e5SMartin Blumenstingl 0); 187d850f3e5SMartin Blumenstingl if (ret < 0) { 188d850f3e5SMartin Blumenstingl pr_err("Error when disabling isolation of CPU%d\n", cpu); 189d850f3e5SMartin Blumenstingl goto out; 190d850f3e5SMartin Blumenstingl } 191d850f3e5SMartin Blumenstingl 192d850f3e5SMartin Blumenstingl /* Reset disable */ 193d850f3e5SMartin Blumenstingl ret = reset_control_deassert(rstc); 194d850f3e5SMartin Blumenstingl if (ret) { 195d850f3e5SMartin Blumenstingl pr_err("Failed to de-assert CPU%d reset\n", cpu); 196d850f3e5SMartin Blumenstingl goto out; 197d850f3e5SMartin Blumenstingl } 198d850f3e5SMartin Blumenstingl 199d850f3e5SMartin Blumenstingl ret = meson_smp_finalize_secondary_boot(cpu); 200d850f3e5SMartin Blumenstingl if (ret) 201d850f3e5SMartin Blumenstingl goto out; 202d850f3e5SMartin Blumenstingl 203d850f3e5SMartin Blumenstingl out: 204d850f3e5SMartin Blumenstingl reset_control_put(rstc); 205d850f3e5SMartin Blumenstingl 206d850f3e5SMartin Blumenstingl return 0; 207d850f3e5SMartin Blumenstingl } 208d850f3e5SMartin Blumenstingl 209d850f3e5SMartin Blumenstingl static int meson8b_smp_boot_secondary(unsigned int cpu, 210d850f3e5SMartin Blumenstingl struct task_struct *idle) 211d850f3e5SMartin Blumenstingl { 212d850f3e5SMartin Blumenstingl struct reset_control *rstc; 213d850f3e5SMartin Blumenstingl int ret; 214d850f3e5SMartin Blumenstingl u32 val; 215d850f3e5SMartin Blumenstingl 216d850f3e5SMartin Blumenstingl rstc = meson_smp_get_core_reset(cpu); 217d850f3e5SMartin Blumenstingl if (IS_ERR(rstc)) { 218d850f3e5SMartin Blumenstingl pr_err("Couldn't get the reset controller for CPU%d\n", cpu); 219d850f3e5SMartin Blumenstingl return PTR_ERR(rstc); 220d850f3e5SMartin Blumenstingl } 221d850f3e5SMartin Blumenstingl 222d850f3e5SMartin Blumenstingl meson_smp_begin_secondary_boot(cpu); 223d850f3e5SMartin Blumenstingl 224d850f3e5SMartin Blumenstingl /* CPU power UP */ 225d850f3e5SMartin Blumenstingl ret = regmap_update_bits(pmu, MESON_CPU_AO_RTI_PWR_A9_CNTL0, 226d850f3e5SMartin Blumenstingl MESON_CPU_PWR_A9_CNTL0_M(cpu), 0); 227d850f3e5SMartin Blumenstingl if (ret < 0) { 228d850f3e5SMartin Blumenstingl pr_err("Couldn't power up CPU%d\n", cpu); 229d850f3e5SMartin Blumenstingl goto out; 230d850f3e5SMartin Blumenstingl } 231d850f3e5SMartin Blumenstingl 232d850f3e5SMartin Blumenstingl udelay(5); 233d850f3e5SMartin Blumenstingl 234d850f3e5SMartin Blumenstingl /* Reset enable */ 235d850f3e5SMartin Blumenstingl ret = reset_control_assert(rstc); 236d850f3e5SMartin Blumenstingl if (ret) { 237d850f3e5SMartin Blumenstingl pr_err("Failed to assert CPU%d reset\n", cpu); 238d850f3e5SMartin Blumenstingl goto out; 239d850f3e5SMartin Blumenstingl } 240d850f3e5SMartin Blumenstingl 241d850f3e5SMartin Blumenstingl /* Memory power UP */ 242d850f3e5SMartin Blumenstingl ret = regmap_update_bits(pmu, MESON_CPU_AO_RTI_PWR_A9_MEM_PD0, 243d850f3e5SMartin Blumenstingl MESON_CPU_PWR_A9_MEM_PD0_M(cpu), 0); 244d850f3e5SMartin Blumenstingl if (ret < 0) { 245d850f3e5SMartin Blumenstingl pr_err("Couldn't power up the memory for CPU%d\n", cpu); 246d850f3e5SMartin Blumenstingl goto out; 247d850f3e5SMartin Blumenstingl } 248d850f3e5SMartin Blumenstingl 249d850f3e5SMartin Blumenstingl /* Wake up CPU */ 250d850f3e5SMartin Blumenstingl ret = regmap_update_bits(pmu, MESON_CPU_AO_RTI_PWR_A9_CNTL1, 251d850f3e5SMartin Blumenstingl MESON_CPU_PWR_A9_CNTL1_M(cpu), 0); 252d850f3e5SMartin Blumenstingl if (ret < 0) { 253d850f3e5SMartin Blumenstingl pr_err("Couldn't wake up CPU%d\n", cpu); 254d850f3e5SMartin Blumenstingl goto out; 255d850f3e5SMartin Blumenstingl } 256d850f3e5SMartin Blumenstingl 257d850f3e5SMartin Blumenstingl udelay(10); 258d850f3e5SMartin Blumenstingl 259d850f3e5SMartin Blumenstingl ret = regmap_read_poll_timeout(pmu, MESON_CPU_AO_RTI_PWR_A9_CNTL1, val, 260d850f3e5SMartin Blumenstingl val & MESON_CPU_PWR_A9_CNTL1_ST(cpu), 261d850f3e5SMartin Blumenstingl 10, 10000); 262d850f3e5SMartin Blumenstingl if (ret) { 263d850f3e5SMartin Blumenstingl pr_err("Timeout while polling PMU for CPU%d status\n", cpu); 264d850f3e5SMartin Blumenstingl goto out; 265d850f3e5SMartin Blumenstingl } 266d850f3e5SMartin Blumenstingl 267d850f3e5SMartin Blumenstingl /* Isolation disable */ 268d850f3e5SMartin Blumenstingl ret = regmap_update_bits(pmu, MESON_CPU_AO_RTI_PWR_A9_CNTL0, BIT(cpu), 269d850f3e5SMartin Blumenstingl 0); 270d850f3e5SMartin Blumenstingl if (ret < 0) { 271d850f3e5SMartin Blumenstingl pr_err("Error when disabling isolation of CPU%d\n", cpu); 272d850f3e5SMartin Blumenstingl goto out; 273d850f3e5SMartin Blumenstingl } 274d850f3e5SMartin Blumenstingl 275d850f3e5SMartin Blumenstingl /* Reset disable */ 276d850f3e5SMartin Blumenstingl ret = reset_control_deassert(rstc); 277d850f3e5SMartin Blumenstingl if (ret) { 278d850f3e5SMartin Blumenstingl pr_err("Failed to de-assert CPU%d reset\n", cpu); 279d850f3e5SMartin Blumenstingl goto out; 280d850f3e5SMartin Blumenstingl } 281d850f3e5SMartin Blumenstingl 282d850f3e5SMartin Blumenstingl ret = meson_smp_finalize_secondary_boot(cpu); 283d850f3e5SMartin Blumenstingl if (ret) 284d850f3e5SMartin Blumenstingl goto out; 285d850f3e5SMartin Blumenstingl 286d850f3e5SMartin Blumenstingl out: 287d850f3e5SMartin Blumenstingl reset_control_put(rstc); 288d850f3e5SMartin Blumenstingl 289d850f3e5SMartin Blumenstingl return 0; 290d850f3e5SMartin Blumenstingl } 291d850f3e5SMartin Blumenstingl 292d850f3e5SMartin Blumenstingl #ifdef CONFIG_HOTPLUG_CPU 293d850f3e5SMartin Blumenstingl static void meson8_smp_cpu_die(unsigned int cpu) 294d850f3e5SMartin Blumenstingl { 295d850f3e5SMartin Blumenstingl meson_smp_set_cpu_ctrl(cpu, false); 296d850f3e5SMartin Blumenstingl 297d850f3e5SMartin Blumenstingl v7_exit_coherency_flush(louis); 298d850f3e5SMartin Blumenstingl 299d850f3e5SMartin Blumenstingl scu_power_mode(scu_base, SCU_PM_POWEROFF); 300d850f3e5SMartin Blumenstingl 301d850f3e5SMartin Blumenstingl dsb(); 302d850f3e5SMartin Blumenstingl wfi(); 303d850f3e5SMartin Blumenstingl 304d850f3e5SMartin Blumenstingl /* we should never get here */ 305d850f3e5SMartin Blumenstingl WARN_ON(1); 306d850f3e5SMartin Blumenstingl } 307d850f3e5SMartin Blumenstingl 308d850f3e5SMartin Blumenstingl static int meson8_smp_cpu_kill(unsigned int cpu) 309d850f3e5SMartin Blumenstingl { 310d850f3e5SMartin Blumenstingl int ret, power_mode; 311d850f3e5SMartin Blumenstingl unsigned long timeout; 312d850f3e5SMartin Blumenstingl 313d850f3e5SMartin Blumenstingl timeout = jiffies + (50 * HZ); 314d850f3e5SMartin Blumenstingl do { 315d850f3e5SMartin Blumenstingl power_mode = scu_get_cpu_power_mode(scu_base, cpu); 316d850f3e5SMartin Blumenstingl 317d850f3e5SMartin Blumenstingl if (power_mode == SCU_PM_POWEROFF) 318d850f3e5SMartin Blumenstingl break; 319d850f3e5SMartin Blumenstingl 320d850f3e5SMartin Blumenstingl usleep_range(10000, 15000); 321d850f3e5SMartin Blumenstingl } while (time_before(jiffies, timeout)); 322d850f3e5SMartin Blumenstingl 323d850f3e5SMartin Blumenstingl if (power_mode != SCU_PM_POWEROFF) { 324d850f3e5SMartin Blumenstingl pr_err("Error while waiting for SCU power-off on CPU%d\n", 325d850f3e5SMartin Blumenstingl cpu); 326d850f3e5SMartin Blumenstingl return -ETIMEDOUT; 327d850f3e5SMartin Blumenstingl } 328d850f3e5SMartin Blumenstingl 329d850f3e5SMartin Blumenstingl msleep(30); 330d850f3e5SMartin Blumenstingl 331d850f3e5SMartin Blumenstingl /* Isolation enable */ 332d850f3e5SMartin Blumenstingl ret = regmap_update_bits(pmu, MESON_CPU_AO_RTI_PWR_A9_CNTL0, BIT(cpu), 333d850f3e5SMartin Blumenstingl 0x3); 334d850f3e5SMartin Blumenstingl if (ret < 0) { 335d850f3e5SMartin Blumenstingl pr_err("Error when enabling isolation for CPU%d\n", cpu); 336d850f3e5SMartin Blumenstingl return ret; 337d850f3e5SMartin Blumenstingl } 338d850f3e5SMartin Blumenstingl 339d850f3e5SMartin Blumenstingl udelay(10); 340d850f3e5SMartin Blumenstingl 341d850f3e5SMartin Blumenstingl /* CPU power OFF */ 342d850f3e5SMartin Blumenstingl ret = regmap_update_bits(pmu, MESON_CPU_AO_RTI_PWR_A9_CNTL1, 343d850f3e5SMartin Blumenstingl MESON_CPU_PWR_A9_CNTL1_M(cpu), 0x3); 344d850f3e5SMartin Blumenstingl if (ret < 0) { 345d850f3e5SMartin Blumenstingl pr_err("Couldn't change sleep status of CPU%d\n", cpu); 346d850f3e5SMartin Blumenstingl return ret; 347d850f3e5SMartin Blumenstingl } 348d850f3e5SMartin Blumenstingl 349d850f3e5SMartin Blumenstingl return 1; 350d850f3e5SMartin Blumenstingl } 351d850f3e5SMartin Blumenstingl 352d850f3e5SMartin Blumenstingl static int meson8b_smp_cpu_kill(unsigned int cpu) 353d850f3e5SMartin Blumenstingl { 354d850f3e5SMartin Blumenstingl int ret, power_mode, count = 5000; 355d850f3e5SMartin Blumenstingl 356d850f3e5SMartin Blumenstingl do { 357d850f3e5SMartin Blumenstingl power_mode = scu_get_cpu_power_mode(scu_base, cpu); 358d850f3e5SMartin Blumenstingl 359d850f3e5SMartin Blumenstingl if (power_mode == SCU_PM_POWEROFF) 360d850f3e5SMartin Blumenstingl break; 361d850f3e5SMartin Blumenstingl 362d850f3e5SMartin Blumenstingl udelay(10); 363d850f3e5SMartin Blumenstingl } while (++count); 364d850f3e5SMartin Blumenstingl 365d850f3e5SMartin Blumenstingl if (power_mode != SCU_PM_POWEROFF) { 366d850f3e5SMartin Blumenstingl pr_err("Error while waiting for SCU power-off on CPU%d\n", 367d850f3e5SMartin Blumenstingl cpu); 368d850f3e5SMartin Blumenstingl return -ETIMEDOUT; 369d850f3e5SMartin Blumenstingl } 370d850f3e5SMartin Blumenstingl 371d850f3e5SMartin Blumenstingl udelay(10); 372d850f3e5SMartin Blumenstingl 373d850f3e5SMartin Blumenstingl /* CPU power DOWN */ 374d850f3e5SMartin Blumenstingl ret = regmap_update_bits(pmu, MESON_CPU_AO_RTI_PWR_A9_CNTL0, 375d850f3e5SMartin Blumenstingl MESON_CPU_PWR_A9_CNTL0_M(cpu), 0x3); 376d850f3e5SMartin Blumenstingl if (ret < 0) { 377d850f3e5SMartin Blumenstingl pr_err("Couldn't power down CPU%d\n", cpu); 378d850f3e5SMartin Blumenstingl return ret; 379d850f3e5SMartin Blumenstingl } 380d850f3e5SMartin Blumenstingl 381d850f3e5SMartin Blumenstingl /* Isolation enable */ 382d850f3e5SMartin Blumenstingl ret = regmap_update_bits(pmu, MESON_CPU_AO_RTI_PWR_A9_CNTL0, BIT(cpu), 383d850f3e5SMartin Blumenstingl 0x3); 384d850f3e5SMartin Blumenstingl if (ret < 0) { 385d850f3e5SMartin Blumenstingl pr_err("Error when enabling isolation for CPU%d\n", cpu); 386d850f3e5SMartin Blumenstingl return ret; 387d850f3e5SMartin Blumenstingl } 388d850f3e5SMartin Blumenstingl 389d850f3e5SMartin Blumenstingl udelay(10); 390d850f3e5SMartin Blumenstingl 391d850f3e5SMartin Blumenstingl /* Sleep status */ 392d850f3e5SMartin Blumenstingl ret = regmap_update_bits(pmu, MESON_CPU_AO_RTI_PWR_A9_CNTL1, 393d850f3e5SMartin Blumenstingl MESON_CPU_PWR_A9_CNTL1_M(cpu), 0x3); 394d850f3e5SMartin Blumenstingl if (ret < 0) { 395d850f3e5SMartin Blumenstingl pr_err("Couldn't change sleep status of CPU%d\n", cpu); 396d850f3e5SMartin Blumenstingl return ret; 397d850f3e5SMartin Blumenstingl } 398d850f3e5SMartin Blumenstingl 399d850f3e5SMartin Blumenstingl /* Memory power DOWN */ 400d850f3e5SMartin Blumenstingl ret = regmap_update_bits(pmu, MESON_CPU_AO_RTI_PWR_A9_MEM_PD0, 401d850f3e5SMartin Blumenstingl MESON_CPU_PWR_A9_MEM_PD0_M(cpu), 0xf); 402d850f3e5SMartin Blumenstingl if (ret < 0) { 403d850f3e5SMartin Blumenstingl pr_err("Couldn't power down the memory of CPU%d\n", cpu); 404d850f3e5SMartin Blumenstingl return ret; 405d850f3e5SMartin Blumenstingl } 406d850f3e5SMartin Blumenstingl 407d850f3e5SMartin Blumenstingl return 1; 408d850f3e5SMartin Blumenstingl } 409d850f3e5SMartin Blumenstingl #endif 410d850f3e5SMartin Blumenstingl 411d850f3e5SMartin Blumenstingl static struct smp_operations meson8_smp_ops __initdata = { 412d850f3e5SMartin Blumenstingl .smp_prepare_cpus = meson8_smp_prepare_cpus, 413d850f3e5SMartin Blumenstingl .smp_boot_secondary = meson8_smp_boot_secondary, 414d850f3e5SMartin Blumenstingl #ifdef CONFIG_HOTPLUG_CPU 415d850f3e5SMartin Blumenstingl .cpu_die = meson8_smp_cpu_die, 416d850f3e5SMartin Blumenstingl .cpu_kill = meson8_smp_cpu_kill, 417d850f3e5SMartin Blumenstingl #endif 418d850f3e5SMartin Blumenstingl }; 419d850f3e5SMartin Blumenstingl 420d850f3e5SMartin Blumenstingl static struct smp_operations meson8b_smp_ops __initdata = { 421d850f3e5SMartin Blumenstingl .smp_prepare_cpus = meson8b_smp_prepare_cpus, 422d850f3e5SMartin Blumenstingl .smp_boot_secondary = meson8b_smp_boot_secondary, 423d850f3e5SMartin Blumenstingl #ifdef CONFIG_HOTPLUG_CPU 424d850f3e5SMartin Blumenstingl .cpu_die = meson8_smp_cpu_die, 425d850f3e5SMartin Blumenstingl .cpu_kill = meson8b_smp_cpu_kill, 426d850f3e5SMartin Blumenstingl #endif 427d850f3e5SMartin Blumenstingl }; 428d850f3e5SMartin Blumenstingl 429d850f3e5SMartin Blumenstingl CPU_METHOD_OF_DECLARE(meson8_smp, "amlogic,meson8-smp", &meson8_smp_ops); 430d850f3e5SMartin Blumenstingl CPU_METHOD_OF_DECLARE(meson8b_smp, "amlogic,meson8b-smp", &meson8b_smp_ops); 431