0001
0002
0003
0004
0005
0006
0007
0008
0009 #define pr_fmt(fmt) "psci: " fmt
0010
0011 #include <linux/init.h>
0012 #include <linux/of.h>
0013 #include <linux/smp.h>
0014 #include <linux/delay.h>
0015 #include <linux/psci.h>
0016 #include <linux/mm.h>
0017
0018 #include <uapi/linux/psci.h>
0019
0020 #include <asm/cpu_ops.h>
0021 #include <asm/errno.h>
0022 #include <asm/smp_plat.h>
0023
0024 static int __init cpu_psci_cpu_init(unsigned int cpu)
0025 {
0026 return 0;
0027 }
0028
0029 static int __init cpu_psci_cpu_prepare(unsigned int cpu)
0030 {
0031 if (!psci_ops.cpu_on) {
0032 pr_err("no cpu_on method, not booting CPU%d\n", cpu);
0033 return -ENODEV;
0034 }
0035
0036 return 0;
0037 }
0038
0039 static int cpu_psci_cpu_boot(unsigned int cpu)
0040 {
0041 phys_addr_t pa_secondary_entry = __pa_symbol(function_nocfi(secondary_entry));
0042 int err = psci_ops.cpu_on(cpu_logical_map(cpu), pa_secondary_entry);
0043 if (err)
0044 pr_err("failed to boot CPU%d (%d)\n", cpu, err);
0045
0046 return err;
0047 }
0048
0049 #ifdef CONFIG_HOTPLUG_CPU
0050 static bool cpu_psci_cpu_can_disable(unsigned int cpu)
0051 {
0052 return !psci_tos_resident_on(cpu);
0053 }
0054
0055 static int cpu_psci_cpu_disable(unsigned int cpu)
0056 {
0057
0058 if (!psci_ops.cpu_off)
0059 return -EOPNOTSUPP;
0060
0061
0062 if (psci_tos_resident_on(cpu))
0063 return -EPERM;
0064
0065 return 0;
0066 }
0067
0068 static void cpu_psci_cpu_die(unsigned int cpu)
0069 {
0070
0071
0072
0073
0074 u32 state = PSCI_POWER_STATE_TYPE_POWER_DOWN <<
0075 PSCI_0_2_POWER_STATE_TYPE_SHIFT;
0076
0077 psci_ops.cpu_off(state);
0078 }
0079
0080 static int cpu_psci_cpu_kill(unsigned int cpu)
0081 {
0082 int err;
0083 unsigned long start, end;
0084
0085 if (!psci_ops.affinity_info)
0086 return 0;
0087
0088
0089
0090
0091
0092
0093 start = jiffies;
0094 end = start + msecs_to_jiffies(100);
0095 do {
0096 err = psci_ops.affinity_info(cpu_logical_map(cpu), 0);
0097 if (err == PSCI_0_2_AFFINITY_LEVEL_OFF) {
0098 pr_info("CPU%d killed (polled %d ms)\n", cpu,
0099 jiffies_to_msecs(jiffies - start));
0100 return 0;
0101 }
0102
0103 usleep_range(100, 1000);
0104 } while (time_before(jiffies, end));
0105
0106 pr_warn("CPU%d may not have shut down cleanly (AFFINITY_INFO reports %d)\n",
0107 cpu, err);
0108 return -ETIMEDOUT;
0109 }
0110 #endif
0111
0112 const struct cpu_operations cpu_psci_ops = {
0113 .name = "psci",
0114 .cpu_init = cpu_psci_cpu_init,
0115 .cpu_prepare = cpu_psci_cpu_prepare,
0116 .cpu_boot = cpu_psci_cpu_boot,
0117 #ifdef CONFIG_HOTPLUG_CPU
0118 .cpu_can_disable = cpu_psci_cpu_can_disable,
0119 .cpu_disable = cpu_psci_cpu_disable,
0120 .cpu_die = cpu_psci_cpu_die,
0121 .cpu_kill = cpu_psci_cpu_kill,
0122 #endif
0123 };
0124