/*  * Copyright (c) 2016, The Linux Foundation. All rights reserved.  *  * Permission to use, copy, modify, and/or distribute this software for any  * purpose with or without fee is hereby granted, provided that the above  * copyright notice and this permission notice appear in all copies.  *  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ /* IPQ ARMv8 CPU Operations * Based on arch/arm64/kernel/smp_spin_table.c */ #include #include #include #include #include #include #include #include #include #include #include #include #include extern void a53ss_unclamp_cpu(void __iomem *reg); static DEFINE_SPINLOCK(boot_lock); static DEFINE_PER_CPU(int, cold_boot_done); static int a53ss_release_secondary(unsigned int cpu) { int ret = 0; struct device_node *cpu_node, *acc_node; void __iomem *reg; cpu_node = of_get_cpu_node(cpu, NULL); if (!cpu_node) return -ENODEV; acc_node = of_parse_phandle(cpu_node, "qcom,acc", 0); if (!acc_node) { ret = -ENODEV; goto err_cpu_node; } reg = of_iomap(acc_node, 0); if (!reg) { ret = -ENOMEM; goto err_acc_node; } a53ss_unclamp_cpu(reg); per_cpu(cold_boot_done, cpu) = true; /* Secondary CPU-N is now alive */ iounmap(reg); err_acc_node: of_node_put(acc_node); err_cpu_node: of_node_put(cpu_node); return ret; } static void qca_wake_cpu(unsigned int cpu) { spin_lock(&boot_lock); arch_send_wakeup_ipi_mask(cpumask_of(cpu)); spin_unlock(&boot_lock); } static int qca_boot_cpu(unsigned int cpu, int (*func)(unsigned int)) { int ret = 0; if (!per_cpu(cold_boot_done, cpu)) { ret = func(cpu); } qca_wake_cpu(cpu); return ret; } static int __init qca_cpu_init(unsigned int cpu) { return 0; } static int __init qca_cpu_prepare(unsigned int cpu) { if (qcom_scm_set_cold_boot_addr(secondary_startup, cpu_present_mask)) { set_cpu_present(cpu, false); pr_warn("Failed to set CPU boot address, disabling SMP\n"); return -ENOSYS; } return 0; } static int a53ss_cpu_boot(unsigned int cpu) { return qca_boot_cpu(cpu, a53ss_release_secondary); } void qca_cpu_postboot(void) { } #ifdef CONFIG_HOTPLUG_CPU static void a53ss_wfi_cpu_die(unsigned int cpu) { } #endif struct cpu_operations smp_a53ss_ops = { .name = "qcom,arm-cortex-acc", .cpu_init = qca_cpu_init, .cpu_prepare = qca_cpu_prepare, .cpu_boot = a53ss_cpu_boot, .cpu_postboot = qca_cpu_postboot, #ifdef CONFIG_HOTPLUG_CPU .cpu_die = a53ss_wfi_cpu_die, #endif }; CPU_METHOD_OF_DECLARE(qcom_smp_a53ss, "qcom,arm-cortex-acc", &smp_a53ss_ops);