1 /* 2 * This program is free software; you can redistribute it and/or modify 3 * it under the terms of the GNU General Public License version 2 as 4 * published by the Free Software Foundation. 5 * 6 * This program is distributed in the hope that it will be useful, 7 * but WITHOUT ANY WARRANTY; without even the implied warranty of 8 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 9 * GNU General Public License for more details. 10 * 11 * Copyright (C) 2013 ARM Limited 12 * 13 * Author: Will Deacon <will.deacon@arm.com> 14 */ 15 16 #define pr_fmt(fmt) "psci: " fmt 17 18 #include <linux/init.h> 19 #include <linux/of.h> 20 #include <linux/smp.h> 21 #include <linux/delay.h> 22 #include <linux/psci.h> 23 #include <linux/mm.h> 24 25 #include <uapi/linux/psci.h> 26 27 #include <asm/cpu_ops.h> 28 #include <asm/errno.h> 29 #include <asm/smp_plat.h> 30 31 static int __init cpu_psci_cpu_init(unsigned int cpu) 32 { 33 return 0; 34 } 35 36 static int __init cpu_psci_cpu_prepare(unsigned int cpu) 37 { 38 if (!psci_ops.cpu_on) { 39 pr_err("no cpu_on method, not booting CPU%d\n", cpu); 40 return -ENODEV; 41 } 42 43 return 0; 44 } 45 46 static int cpu_psci_cpu_boot(unsigned int cpu) 47 { 48 int err = psci_ops.cpu_on(cpu_logical_map(cpu), __pa_symbol(secondary_entry)); 49 if (err) 50 pr_err("failed to boot CPU%d (%d)\n", cpu, err); 51 52 return err; 53 } 54 55 #ifdef CONFIG_HOTPLUG_CPU 56 static int cpu_psci_cpu_disable(unsigned int cpu) 57 { 58 /* Fail early if we don't have CPU_OFF support */ 59 if (!psci_ops.cpu_off) 60 return -EOPNOTSUPP; 61 62 /* Trusted OS will deny CPU_OFF */ 63 if (psci_tos_resident_on(cpu)) 64 return -EPERM; 65 66 return 0; 67 } 68 69 static void cpu_psci_cpu_die(unsigned int cpu) 70 { 71 int ret; 72 /* 73 * There are no known implementations of PSCI actually using the 74 * power state field, pass a sensible default for now. 75 */ 76 u32 state = PSCI_POWER_STATE_TYPE_POWER_DOWN << 77 PSCI_0_2_POWER_STATE_TYPE_SHIFT; 78 79 ret = psci_ops.cpu_off(state); 80 81 pr_crit("unable to power off CPU%u (%d)\n", cpu, ret); 82 } 83 84 static int cpu_psci_cpu_kill(unsigned int cpu) 85 { 86 int err, i; 87 88 if (!psci_ops.affinity_info) 89 return 0; 90 /* 91 * cpu_kill could race with cpu_die and we can 92 * potentially end up declaring this cpu undead 93 * while it is dying. So, try again a few times. 94 */ 95 96 for (i = 0; i < 10; i++) { 97 err = psci_ops.affinity_info(cpu_logical_map(cpu), 0); 98 if (err == PSCI_0_2_AFFINITY_LEVEL_OFF) { 99 pr_info("CPU%d killed.\n", cpu); 100 return 0; 101 } 102 103 msleep(10); 104 pr_info("Retrying again to check for CPU kill\n"); 105 } 106 107 pr_warn("CPU%d may not have shut down cleanly (AFFINITY_INFO reports %d)\n", 108 cpu, err); 109 return -ETIMEDOUT; 110 } 111 #endif 112 113 const struct cpu_operations cpu_psci_ops = { 114 .name = "psci", 115 #ifdef CONFIG_CPU_IDLE 116 .cpu_init_idle = psci_cpu_init_idle, 117 .cpu_suspend = psci_cpu_suspend_enter, 118 #endif 119 .cpu_init = cpu_psci_cpu_init, 120 .cpu_prepare = cpu_psci_cpu_prepare, 121 .cpu_boot = cpu_psci_cpu_boot, 122 #ifdef CONFIG_HOTPLUG_CPU 123 .cpu_disable = cpu_psci_cpu_disable, 124 .cpu_die = cpu_psci_cpu_die, 125 .cpu_kill = cpu_psci_cpu_kill, 126 #endif 127 }; 128 129