Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-only
0002 /*
0003  *
0004  * Copyright (C) 2013 ARM Limited
0005  *
0006  * Author: Will Deacon <will.deacon@arm.com>
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     /* Fail early if we don't have CPU_OFF support */
0058     if (!psci_ops.cpu_off)
0059         return -EOPNOTSUPP;
0060 
0061     /* Trusted OS will deny CPU_OFF */
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      * There are no known implementations of PSCI actually using the
0072      * power state field, pass a sensible default for now.
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      * cpu_kill could race with cpu_die and we can
0089      * potentially end up declaring this cpu undead
0090      * while it is dying. So, try again a few times.
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