linux/drivers/firmware/psci.c
<<
>>
Prefs
   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) 2015 ARM Limited
  12 */
  13
  14#define pr_fmt(fmt) "psci: " fmt
  15
  16#include <linux/arm-smccc.h>
  17#include <linux/errno.h>
  18#include <linux/linkage.h>
  19#include <linux/of.h>
  20#include <linux/pm.h>
  21#include <linux/printk.h>
  22#include <linux/psci.h>
  23#include <linux/reboot.h>
  24#include <linux/suspend.h>
  25
  26#include <uapi/linux/psci.h>
  27
  28#include <asm/cputype.h>
  29#include <asm/system_misc.h>
  30#include <asm/smp_plat.h>
  31#include <asm/suspend.h>
  32
  33/*
  34 * While a 64-bit OS can make calls with SMC32 calling conventions, for some
  35 * calls it is necessary to use SMC64 to pass or return 64-bit values.
  36 * For such calls PSCI_FN_NATIVE(version, name) will choose the appropriate
  37 * (native-width) function ID.
  38 */
  39#ifdef CONFIG_64BIT
  40#define PSCI_FN_NATIVE(version, name)   PSCI_##version##_FN64_##name
  41#else
  42#define PSCI_FN_NATIVE(version, name)   PSCI_##version##_FN_##name
  43#endif
  44
  45/*
  46 * The CPU any Trusted OS is resident on. The trusted OS may reject CPU_OFF
  47 * calls to its resident CPU, so we must avoid issuing those. We never migrate
  48 * a Trusted OS even if it claims to be capable of migration -- doing so will
  49 * require cooperation with a Trusted OS driver.
  50 */
  51static int resident_cpu = -1;
  52
  53bool psci_tos_resident_on(int cpu)
  54{
  55        return cpu == resident_cpu;
  56}
  57
  58struct psci_operations psci_ops;
  59
  60typedef unsigned long (psci_fn)(unsigned long, unsigned long,
  61                                unsigned long, unsigned long);
  62static psci_fn *invoke_psci_fn;
  63
  64enum psci_function {
  65        PSCI_FN_CPU_SUSPEND,
  66        PSCI_FN_CPU_ON,
  67        PSCI_FN_CPU_OFF,
  68        PSCI_FN_MIGRATE,
  69        PSCI_FN_MAX,
  70};
  71
  72static u32 psci_function_id[PSCI_FN_MAX];
  73
  74#define PSCI_0_2_POWER_STATE_MASK               \
  75                                (PSCI_0_2_POWER_STATE_ID_MASK | \
  76                                PSCI_0_2_POWER_STATE_TYPE_MASK | \
  77                                PSCI_0_2_POWER_STATE_AFFL_MASK)
  78
  79#define PSCI_1_0_EXT_POWER_STATE_MASK           \
  80                                (PSCI_1_0_EXT_POWER_STATE_ID_MASK | \
  81                                PSCI_1_0_EXT_POWER_STATE_TYPE_MASK)
  82
  83static u32 psci_cpu_suspend_feature;
  84
  85static inline bool psci_has_ext_power_state(void)
  86{
  87        return psci_cpu_suspend_feature &
  88                                PSCI_1_0_FEATURES_CPU_SUSPEND_PF_MASK;
  89}
  90
  91bool psci_power_state_loses_context(u32 state)
  92{
  93        const u32 mask = psci_has_ext_power_state() ?
  94                                        PSCI_1_0_EXT_POWER_STATE_TYPE_MASK :
  95                                        PSCI_0_2_POWER_STATE_TYPE_MASK;
  96
  97        return state & mask;
  98}
  99
 100bool psci_power_state_is_valid(u32 state)
 101{
 102        const u32 valid_mask = psci_has_ext_power_state() ?
 103                               PSCI_1_0_EXT_POWER_STATE_MASK :
 104                               PSCI_0_2_POWER_STATE_MASK;
 105
 106        return !(state & ~valid_mask);
 107}
 108
 109static unsigned long __invoke_psci_fn_hvc(unsigned long function_id,
 110                        unsigned long arg0, unsigned long arg1,
 111                        unsigned long arg2)
 112{
 113        struct arm_smccc_res res;
 114
 115        arm_smccc_hvc(function_id, arg0, arg1, arg2, 0, 0, 0, 0, &res);
 116        return res.a0;
 117}
 118
 119static unsigned long __invoke_psci_fn_smc(unsigned long function_id,
 120                        unsigned long arg0, unsigned long arg1,
 121                        unsigned long arg2)
 122{
 123        struct arm_smccc_res res;
 124
 125        arm_smccc_smc(function_id, arg0, arg1, arg2, 0, 0, 0, 0, &res);
 126        return res.a0;
 127}
 128
 129static int psci_to_linux_errno(int errno)
 130{
 131        switch (errno) {
 132        case PSCI_RET_SUCCESS:
 133                return 0;
 134        case PSCI_RET_NOT_SUPPORTED:
 135                return -EOPNOTSUPP;
 136        case PSCI_RET_INVALID_PARAMS:
 137        case PSCI_RET_INVALID_ADDRESS:
 138                return -EINVAL;
 139        case PSCI_RET_DENIED:
 140                return -EPERM;
 141        };
 142
 143        return -EINVAL;
 144}
 145
 146static u32 psci_get_version(void)
 147{
 148        return invoke_psci_fn(PSCI_0_2_FN_PSCI_VERSION, 0, 0, 0);
 149}
 150
 151static int psci_cpu_suspend(u32 state, unsigned long entry_point)
 152{
 153        int err;
 154        u32 fn;
 155
 156        fn = psci_function_id[PSCI_FN_CPU_SUSPEND];
 157        err = invoke_psci_fn(fn, state, entry_point, 0);
 158        return psci_to_linux_errno(err);
 159}
 160
 161static int psci_cpu_off(u32 state)
 162{
 163        int err;
 164        u32 fn;
 165
 166        fn = psci_function_id[PSCI_FN_CPU_OFF];
 167        err = invoke_psci_fn(fn, state, 0, 0);
 168        return psci_to_linux_errno(err);
 169}
 170
 171static int psci_cpu_on(unsigned long cpuid, unsigned long entry_point)
 172{
 173        int err;
 174        u32 fn;
 175
 176        fn = psci_function_id[PSCI_FN_CPU_ON];
 177        err = invoke_psci_fn(fn, cpuid, entry_point, 0);
 178        return psci_to_linux_errno(err);
 179}
 180
 181static int psci_migrate(unsigned long cpuid)
 182{
 183        int err;
 184        u32 fn;
 185
 186        fn = psci_function_id[PSCI_FN_MIGRATE];
 187        err = invoke_psci_fn(fn, cpuid, 0, 0);
 188        return psci_to_linux_errno(err);
 189}
 190
 191static int psci_affinity_info(unsigned long target_affinity,
 192                unsigned long lowest_affinity_level)
 193{
 194        return invoke_psci_fn(PSCI_FN_NATIVE(0_2, AFFINITY_INFO),
 195                              target_affinity, lowest_affinity_level, 0);
 196}
 197
 198static int psci_migrate_info_type(void)
 199{
 200        return invoke_psci_fn(PSCI_0_2_FN_MIGRATE_INFO_TYPE, 0, 0, 0);
 201}
 202
 203static unsigned long psci_migrate_info_up_cpu(void)
 204{
 205        return invoke_psci_fn(PSCI_FN_NATIVE(0_2, MIGRATE_INFO_UP_CPU),
 206                              0, 0, 0);
 207}
 208
 209static int get_set_conduit_method(struct device_node *np)
 210{
 211        const char *method;
 212
 213        pr_info("probing for conduit method from DT.\n");
 214
 215        if (of_property_read_string(np, "method", &method)) {
 216                pr_warn("missing \"method\" property\n");
 217                return -ENXIO;
 218        }
 219
 220        if (!strcmp("hvc", method)) {
 221                invoke_psci_fn = __invoke_psci_fn_hvc;
 222        } else if (!strcmp("smc", method)) {
 223                invoke_psci_fn = __invoke_psci_fn_smc;
 224        } else {
 225                pr_warn("invalid \"method\" property: %s\n", method);
 226                return -EINVAL;
 227        }
 228        return 0;
 229}
 230
 231static void psci_sys_reset(enum reboot_mode reboot_mode, const char *cmd)
 232{
 233        invoke_psci_fn(PSCI_0_2_FN_SYSTEM_RESET, 0, 0, 0);
 234}
 235
 236static void psci_sys_poweroff(void)
 237{
 238        invoke_psci_fn(PSCI_0_2_FN_SYSTEM_OFF, 0, 0, 0);
 239}
 240
 241static int __init psci_features(u32 psci_func_id)
 242{
 243        return invoke_psci_fn(PSCI_1_0_FN_PSCI_FEATURES,
 244                              psci_func_id, 0, 0);
 245}
 246
 247static int psci_system_suspend(unsigned long unused)
 248{
 249        return invoke_psci_fn(PSCI_FN_NATIVE(1_0, SYSTEM_SUSPEND),
 250                              virt_to_phys(cpu_resume), 0, 0);
 251}
 252
 253static int psci_system_suspend_enter(suspend_state_t state)
 254{
 255        return cpu_suspend(0, psci_system_suspend);
 256}
 257
 258static const struct platform_suspend_ops psci_suspend_ops = {
 259        .valid          = suspend_valid_only_mem,
 260        .enter          = psci_system_suspend_enter,
 261};
 262
 263static void __init psci_init_system_suspend(void)
 264{
 265        int ret;
 266
 267        if (!IS_ENABLED(CONFIG_SUSPEND))
 268                return;
 269
 270        ret = psci_features(PSCI_FN_NATIVE(1_0, SYSTEM_SUSPEND));
 271
 272        if (ret != PSCI_RET_NOT_SUPPORTED)
 273                suspend_set_ops(&psci_suspend_ops);
 274}
 275
 276static void __init psci_init_cpu_suspend(void)
 277{
 278        int feature = psci_features(psci_function_id[PSCI_FN_CPU_SUSPEND]);
 279
 280        if (feature != PSCI_RET_NOT_SUPPORTED)
 281                psci_cpu_suspend_feature = feature;
 282}
 283
 284/*
 285 * Detect the presence of a resident Trusted OS which may cause CPU_OFF to
 286 * return DENIED (which would be fatal).
 287 */
 288static void __init psci_init_migrate(void)
 289{
 290        unsigned long cpuid;
 291        int type, cpu = -1;
 292
 293        type = psci_ops.migrate_info_type();
 294
 295        if (type == PSCI_0_2_TOS_MP) {
 296                pr_info("Trusted OS migration not required\n");
 297                return;
 298        }
 299
 300        if (type == PSCI_RET_NOT_SUPPORTED) {
 301                pr_info("MIGRATE_INFO_TYPE not supported.\n");
 302                return;
 303        }
 304
 305        if (type != PSCI_0_2_TOS_UP_MIGRATE &&
 306            type != PSCI_0_2_TOS_UP_NO_MIGRATE) {
 307                pr_err("MIGRATE_INFO_TYPE returned unknown type (%d)\n", type);
 308                return;
 309        }
 310
 311        cpuid = psci_migrate_info_up_cpu();
 312        if (cpuid & ~MPIDR_HWID_BITMASK) {
 313                pr_warn("MIGRATE_INFO_UP_CPU reported invalid physical ID (0x%lx)\n",
 314                        cpuid);
 315                return;
 316        }
 317
 318        cpu = get_logical_index(cpuid);
 319        resident_cpu = cpu >= 0 ? cpu : -1;
 320
 321        pr_info("Trusted OS resident on physical CPU 0x%lx\n", cpuid);
 322}
 323
 324static void __init psci_0_2_set_functions(void)
 325{
 326        pr_info("Using standard PSCI v0.2 function IDs\n");
 327        psci_function_id[PSCI_FN_CPU_SUSPEND] =
 328                                        PSCI_FN_NATIVE(0_2, CPU_SUSPEND);
 329        psci_ops.cpu_suspend = psci_cpu_suspend;
 330
 331        psci_function_id[PSCI_FN_CPU_OFF] = PSCI_0_2_FN_CPU_OFF;
 332        psci_ops.cpu_off = psci_cpu_off;
 333
 334        psci_function_id[PSCI_FN_CPU_ON] = PSCI_FN_NATIVE(0_2, CPU_ON);
 335        psci_ops.cpu_on = psci_cpu_on;
 336
 337        psci_function_id[PSCI_FN_MIGRATE] = PSCI_FN_NATIVE(0_2, MIGRATE);
 338        psci_ops.migrate = psci_migrate;
 339
 340        psci_ops.affinity_info = psci_affinity_info;
 341
 342        psci_ops.migrate_info_type = psci_migrate_info_type;
 343
 344        arm_pm_restart = psci_sys_reset;
 345
 346        pm_power_off = psci_sys_poweroff;
 347}
 348
 349/*
 350 * Probe function for PSCI firmware versions >= 0.2
 351 */
 352static int __init psci_probe(void)
 353{
 354        u32 ver = psci_get_version();
 355
 356        pr_info("PSCIv%d.%d detected in firmware.\n",
 357                        PSCI_VERSION_MAJOR(ver),
 358                        PSCI_VERSION_MINOR(ver));
 359
 360        if (PSCI_VERSION_MAJOR(ver) == 0 && PSCI_VERSION_MINOR(ver) < 2) {
 361                pr_err("Conflicting PSCI version detected.\n");
 362                return -EINVAL;
 363        }
 364
 365        psci_0_2_set_functions();
 366
 367        psci_init_migrate();
 368
 369        if (PSCI_VERSION_MAJOR(ver) >= 1) {
 370                psci_init_cpu_suspend();
 371                psci_init_system_suspend();
 372        }
 373
 374        return 0;
 375}
 376
 377typedef int (*psci_initcall_t)(const struct device_node *);
 378
 379/*
 380 * PSCI init function for PSCI versions >=0.2
 381 *
 382 * Probe based on PSCI PSCI_VERSION function
 383 */
 384static int __init psci_0_2_init(struct device_node *np)
 385{
 386        int err;
 387
 388        err = get_set_conduit_method(np);
 389
 390        if (err)
 391                goto out_put_node;
 392        /*
 393         * Starting with v0.2, the PSCI specification introduced a call
 394         * (PSCI_VERSION) that allows probing the firmware version, so
 395         * that PSCI function IDs and version specific initialization
 396         * can be carried out according to the specific version reported
 397         * by firmware
 398         */
 399        err = psci_probe();
 400
 401out_put_node:
 402        of_node_put(np);
 403        return err;
 404}
 405
 406/*
 407 * PSCI < v0.2 get PSCI Function IDs via DT.
 408 */
 409static int __init psci_0_1_init(struct device_node *np)
 410{
 411        u32 id;
 412        int err;
 413
 414        err = get_set_conduit_method(np);
 415
 416        if (err)
 417                goto out_put_node;
 418
 419        pr_info("Using PSCI v0.1 Function IDs from DT\n");
 420
 421        if (!of_property_read_u32(np, "cpu_suspend", &id)) {
 422                psci_function_id[PSCI_FN_CPU_SUSPEND] = id;
 423                psci_ops.cpu_suspend = psci_cpu_suspend;
 424        }
 425
 426        if (!of_property_read_u32(np, "cpu_off", &id)) {
 427                psci_function_id[PSCI_FN_CPU_OFF] = id;
 428                psci_ops.cpu_off = psci_cpu_off;
 429        }
 430
 431        if (!of_property_read_u32(np, "cpu_on", &id)) {
 432                psci_function_id[PSCI_FN_CPU_ON] = id;
 433                psci_ops.cpu_on = psci_cpu_on;
 434        }
 435
 436        if (!of_property_read_u32(np, "migrate", &id)) {
 437                psci_function_id[PSCI_FN_MIGRATE] = id;
 438                psci_ops.migrate = psci_migrate;
 439        }
 440
 441out_put_node:
 442        of_node_put(np);
 443        return err;
 444}
 445
 446static const struct of_device_id const psci_of_match[] __initconst = {
 447        { .compatible = "arm,psci",     .data = psci_0_1_init},
 448        { .compatible = "arm,psci-0.2", .data = psci_0_2_init},
 449        { .compatible = "arm,psci-1.0", .data = psci_0_2_init},
 450        {},
 451};
 452
 453int __init psci_dt_init(void)
 454{
 455        struct device_node *np;
 456        const struct of_device_id *matched_np;
 457        psci_initcall_t init_fn;
 458
 459        np = of_find_matching_node_and_match(NULL, psci_of_match, &matched_np);
 460
 461        if (!np)
 462                return -ENODEV;
 463
 464        init_fn = (psci_initcall_t)matched_np->data;
 465        return init_fn(np);
 466}
 467
 468#ifdef CONFIG_ACPI
 469/*
 470 * We use PSCI 0.2+ when ACPI is deployed on ARM64 and it's
 471 * explicitly clarified in SBBR
 472 */
 473int __init psci_acpi_init(void)
 474{
 475        if (!acpi_psci_present()) {
 476                pr_info("is not implemented in ACPI.\n");
 477                return -EOPNOTSUPP;
 478        }
 479
 480        pr_info("probing for conduit method from ACPI.\n");
 481
 482        if (acpi_psci_use_hvc())
 483                invoke_psci_fn = __invoke_psci_fn_hvc;
 484        else
 485                invoke_psci_fn = __invoke_psci_fn_smc;
 486
 487        return psci_probe();
 488}
 489#endif
 490