^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1) // SPDX-License-Identifier: GPL-2.0
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 2) /*
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 3) * Copyright: (C) 2018 Socionext Inc.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 4) * Copyright: (C) 2015 Linaro Ltd.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 5) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 6)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 7) #include <linux/cpu_pm.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 8) #include <linux/irqchip/arm-gic.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 9) #include <linux/of_address.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 10) #include <linux/suspend.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 11)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 12) #include <asm/cacheflush.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 13) #include <asm/cp15.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 14) #include <asm/idmap.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 15) #include <asm/smp_plat.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 16) #include <asm/suspend.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 17)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 18) #define M10V_MAX_CPU 4
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 19) #define KERNEL_UNBOOT_FLAG 0x12345678
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 20)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 21) static void __iomem *m10v_smp_base;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 22)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 23) static int m10v_boot_secondary(unsigned int l_cpu, struct task_struct *idle)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 24) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 25) unsigned int mpidr, cpu, cluster;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 26)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 27) if (!m10v_smp_base)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 28) return -ENXIO;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 29)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 30) mpidr = cpu_logical_map(l_cpu);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 31) cpu = MPIDR_AFFINITY_LEVEL(mpidr, 0);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 32) cluster = MPIDR_AFFINITY_LEVEL(mpidr, 1);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 33)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 34) if (cpu >= M10V_MAX_CPU)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 35) return -EINVAL;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 36)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 37) pr_info("%s: cpu %u l_cpu %u cluster %u\n",
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 38) __func__, cpu, l_cpu, cluster);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 39)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 40) writel(__pa_symbol(secondary_startup), m10v_smp_base + cpu * 4);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 41) arch_send_wakeup_ipi_mask(cpumask_of(l_cpu));
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 42)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 43) return 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 44) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 45)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 46) static void m10v_smp_init(unsigned int max_cpus)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 47) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 48) unsigned int mpidr, cpu, cluster;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 49) struct device_node *np;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 50)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 51) np = of_find_compatible_node(NULL, NULL, "socionext,milbeaut-smp-sram");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 52) if (!np)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 53) return;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 54)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 55) m10v_smp_base = of_iomap(np, 0);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 56) if (!m10v_smp_base)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 57) return;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 58)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 59) mpidr = read_cpuid_mpidr();
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 60) cpu = MPIDR_AFFINITY_LEVEL(mpidr, 0);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 61) cluster = MPIDR_AFFINITY_LEVEL(mpidr, 1);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 62) pr_info("MCPM boot on cpu_%u cluster_%u\n", cpu, cluster);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 63)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 64) for (cpu = 0; cpu < M10V_MAX_CPU; cpu++)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 65) writel(KERNEL_UNBOOT_FLAG, m10v_smp_base + cpu * 4);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 66) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 67)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 68) #ifdef CONFIG_HOTPLUG_CPU
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 69) static void m10v_cpu_die(unsigned int l_cpu)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 70) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 71) gic_cpu_if_down(0);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 72) v7_exit_coherency_flush(louis);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 73) wfi();
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 74) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 75)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 76) static int m10v_cpu_kill(unsigned int l_cpu)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 77) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 78) unsigned int mpidr, cpu;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 79)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 80) mpidr = cpu_logical_map(l_cpu);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 81) cpu = MPIDR_AFFINITY_LEVEL(mpidr, 0);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 82)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 83) writel(KERNEL_UNBOOT_FLAG, m10v_smp_base + cpu * 4);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 84)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 85) return 1;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 86) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 87) #endif
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 88)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 89) static struct smp_operations m10v_smp_ops __initdata = {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 90) .smp_prepare_cpus = m10v_smp_init,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 91) .smp_boot_secondary = m10v_boot_secondary,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 92) #ifdef CONFIG_HOTPLUG_CPU
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 93) .cpu_die = m10v_cpu_die,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 94) .cpu_kill = m10v_cpu_kill,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 95) #endif
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 96) };
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 97) CPU_METHOD_OF_DECLARE(m10v_smp, "socionext,milbeaut-m10v-smp", &m10v_smp_ops);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 98)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 99) static int m10v_pm_valid(suspend_state_t state)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 100) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 101) return (state == PM_SUSPEND_STANDBY) || (state == PM_SUSPEND_MEM);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 102) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 103)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 104) typedef void (*phys_reset_t)(unsigned long);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 105) static phys_reset_t phys_reset;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 106)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 107) static int m10v_die(unsigned long arg)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 108) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 109) setup_mm_for_reboot();
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 110) asm("wfi");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 111) /* Boot just like a secondary */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 112) phys_reset = (phys_reset_t)(unsigned long)virt_to_phys(cpu_reset);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 113) phys_reset(virt_to_phys(cpu_resume));
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 114)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 115) return 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 116) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 117)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 118) static int m10v_pm_enter(suspend_state_t state)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 119) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 120) switch (state) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 121) case PM_SUSPEND_STANDBY:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 122) asm("wfi");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 123) break;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 124) case PM_SUSPEND_MEM:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 125) cpu_pm_enter();
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 126) cpu_suspend(0, m10v_die);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 127) cpu_pm_exit();
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 128) break;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 129) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 130) return 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 131) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 132)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 133) static const struct platform_suspend_ops m10v_pm_ops = {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 134) .valid = m10v_pm_valid,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 135) .enter = m10v_pm_enter,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 136) };
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 137)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 138) struct clk *m10v_clclk_register(struct device *cpu_dev);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 139)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 140) static int __init m10v_pm_init(void)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 141) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 142) if (of_machine_is_compatible("socionext,milbeaut-evb"))
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 143) suspend_set_ops(&m10v_pm_ops);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 144)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 145) return 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 146) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 147) late_initcall(m10v_pm_init);