Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-only
0002 /*
0003  *
0004  * Copyright (C) 2012 ARM Limited
0005  *
0006  * Author: Will Deacon <will.deacon@arm.com>
0007  */
0008 
0009 #include <linux/init.h>
0010 #include <linux/smp.h>
0011 #include <linux/of.h>
0012 #include <linux/delay.h>
0013 #include <linux/psci.h>
0014 
0015 #include <uapi/linux/psci.h>
0016 
0017 #include <asm/psci.h>
0018 #include <asm/smp_plat.h>
0019 
0020 /*
0021  * psci_smp assumes that the following is true about PSCI:
0022  *
0023  * cpu_suspend   Suspend the execution on a CPU
0024  * @state        we don't currently describe affinity levels, so just pass 0.
0025  * @entry_point  the first instruction to be executed on return
0026  * returns 0  success, < 0 on failure
0027  *
0028  * cpu_off       Power down a CPU
0029  * @state        we don't currently describe affinity levels, so just pass 0.
0030  * no return on successful call
0031  *
0032  * cpu_on        Power up a CPU
0033  * @cpuid        cpuid of target CPU, as from MPIDR
0034  * @entry_point  the first instruction to be executed on return
0035  * returns 0  success, < 0 on failure
0036  *
0037  * migrate       Migrate the context to a different CPU
0038  * @cpuid        cpuid of target CPU, as from MPIDR
0039  * returns 0  success, < 0 on failure
0040  *
0041  */
0042 
0043 extern void secondary_startup(void);
0044 
0045 static int psci_boot_secondary(unsigned int cpu, struct task_struct *idle)
0046 {
0047     if (psci_ops.cpu_on)
0048         return psci_ops.cpu_on(cpu_logical_map(cpu),
0049                     virt_to_idmap(&secondary_startup));
0050     return -ENODEV;
0051 }
0052 
0053 #ifdef CONFIG_HOTPLUG_CPU
0054 static int psci_cpu_disable(unsigned int cpu)
0055 {
0056     /* Fail early if we don't have CPU_OFF support */
0057     if (!psci_ops.cpu_off)
0058         return -EOPNOTSUPP;
0059 
0060     /* Trusted OS will deny CPU_OFF */
0061     if (psci_tos_resident_on(cpu))
0062         return -EPERM;
0063 
0064     return 0;
0065 }
0066 
0067 static void psci_cpu_die(unsigned int cpu)
0068 {
0069     u32 state = PSCI_POWER_STATE_TYPE_POWER_DOWN <<
0070             PSCI_0_2_POWER_STATE_TYPE_SHIFT;
0071 
0072     if (psci_ops.cpu_off)
0073         psci_ops.cpu_off(state);
0074 
0075     /* We should never return */
0076     panic("psci: cpu %d failed to shutdown\n", cpu);
0077 }
0078 
0079 static int psci_cpu_kill(unsigned int cpu)
0080 {
0081     int err, i;
0082 
0083     if (!psci_ops.affinity_info)
0084         return 1;
0085     /*
0086      * cpu_kill could race with cpu_die and we can
0087      * potentially end up declaring this cpu undead
0088      * while it is dying. So, try again a few times.
0089      */
0090 
0091     for (i = 0; i < 10; i++) {
0092         err = psci_ops.affinity_info(cpu_logical_map(cpu), 0);
0093         if (err == PSCI_0_2_AFFINITY_LEVEL_OFF) {
0094             pr_info("CPU%d killed.\n", cpu);
0095             return 1;
0096         }
0097 
0098         msleep(10);
0099         pr_info("Retrying again to check for CPU kill\n");
0100     }
0101 
0102     pr_warn("CPU%d may not have shut down cleanly (AFFINITY_INFO reports %d)\n",
0103             cpu, err);
0104     /* Make platform_cpu_kill() fail. */
0105     return 0;
0106 }
0107 
0108 #endif
0109 
0110 bool __init psci_smp_available(void)
0111 {
0112     /* is cpu_on available at least? */
0113     return (psci_ops.cpu_on != NULL);
0114 }
0115 
0116 const struct smp_operations psci_smp_ops __initconst = {
0117     .smp_boot_secondary = psci_boot_secondary,
0118 #ifdef CONFIG_HOTPLUG_CPU
0119     .cpu_disable        = psci_cpu_disable,
0120     .cpu_die        = psci_cpu_die,
0121     .cpu_kill       = psci_cpu_kill,
0122 #endif
0123 };