···80668066F: include/linux/power_supply.h80678067F: drivers/power/8068806880698069+POWER STATE COORDINATION INTERFACE (PSCI)80708070+M: Mark Rutland <mark.rutland@arm.com>80718071+M: Lorenzo Pieralisi <lorenzo.pieralisi@arm.com>80728072+L: linux-arm-kernel@lists.infradead.org80738073+S: Maintained80748074+F: drivers/firmware/psci.c80758075+F: include/linux/psci.h80768076+F: include/uapi/linux/psci.h80778077+80698078PNP SUPPORT80708079M: "Rafael J. Wysocki" <rafael.j.wysocki@intel.com>80718080S: Maintained
+1
arch/arm/Kconfig
···14961496config ARM_PSCI14971497 bool "Support for the ARM Power State Coordination Interface (PSCI)"14981498 depends on CPU_V714991499+ select ARM_PSCI_FW14991500 help15001501 Say Y here if you want Linux to communicate with system firmware15011502 implementing the PSCI specification for CPU-centric power
-23
arch/arm/include/asm/psci.h
···1414#ifndef __ASM_ARM_PSCI_H1515#define __ASM_ARM_PSCI_H16161717-#define PSCI_POWER_STATE_TYPE_STANDBY 01818-#define PSCI_POWER_STATE_TYPE_POWER_DOWN 11919-2020-struct psci_power_state {2121- u16 id;2222- u8 type;2323- u8 affinity_level;2424-};2525-2626-struct psci_operations {2727- int (*cpu_suspend)(struct psci_power_state state,2828- unsigned long entry_point);2929- int (*cpu_off)(struct psci_power_state state);3030- int (*cpu_on)(unsigned long cpuid, unsigned long entry_point);3131- int (*migrate)(unsigned long cpuid);3232- int (*affinity_info)(unsigned long target_affinity,3333- unsigned long lowest_affinity_level);3434- int (*migrate_info_type)(void);3535-};3636-3737-extern struct psci_operations psci_ops;3817extern struct smp_operations psci_smp_ops;39184019#ifdef CONFIG_ARM_PSCI4141-int psci_init(void);4220bool psci_smp_available(void);4321#else4444-static inline int psci_init(void) { return 0; }4522static inline bool psci_smp_available(void) { return false; }4623#endif4724
···11-/*22- * This program is free software; you can redistribute it and/or modify33- * it under the terms of the GNU General Public License version 2 as44- * published by the Free Software Foundation.55- *66- * This program is distributed in the hope that it will be useful,77- * but WITHOUT ANY WARRANTY; without even the implied warranty of88- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the99- * GNU General Public License for more details.1010- *1111- * Copyright (C) 2012 ARM Limited1212- *1313- * Author: Will Deacon <will.deacon@arm.com>1414- */1515-1616-#define pr_fmt(fmt) "psci: " fmt1717-1818-#include <linux/init.h>1919-#include <linux/of.h>2020-#include <linux/reboot.h>2121-#include <linux/pm.h>2222-#include <uapi/linux/psci.h>2323-2424-#include <asm/compiler.h>2525-#include <asm/errno.h>2626-#include <asm/psci.h>2727-#include <asm/system_misc.h>2828-2929-struct psci_operations psci_ops;3030-3131-static int (*invoke_psci_fn)(u32, u32, u32, u32);3232-typedef int (*psci_initcall_t)(const struct device_node *);3333-3434-asmlinkage int __invoke_psci_fn_hvc(u32, u32, u32, u32);3535-asmlinkage int __invoke_psci_fn_smc(u32, u32, u32, u32);3636-3737-enum psci_function {3838- PSCI_FN_CPU_SUSPEND,3939- PSCI_FN_CPU_ON,4040- PSCI_FN_CPU_OFF,4141- PSCI_FN_MIGRATE,4242- PSCI_FN_AFFINITY_INFO,4343- PSCI_FN_MIGRATE_INFO_TYPE,4444- PSCI_FN_MAX,4545-};4646-4747-static u32 psci_function_id[PSCI_FN_MAX];4848-4949-static int psci_to_linux_errno(int errno)5050-{5151- switch (errno) {5252- case PSCI_RET_SUCCESS:5353- return 0;5454- case PSCI_RET_NOT_SUPPORTED:5555- return -EOPNOTSUPP;5656- case PSCI_RET_INVALID_PARAMS:5757- return -EINVAL;5858- case PSCI_RET_DENIED:5959- return -EPERM;6060- };6161-6262- return -EINVAL;6363-}6464-6565-static u32 psci_power_state_pack(struct psci_power_state state)6666-{6767- return ((state.id << PSCI_0_2_POWER_STATE_ID_SHIFT)6868- & PSCI_0_2_POWER_STATE_ID_MASK) |6969- ((state.type << PSCI_0_2_POWER_STATE_TYPE_SHIFT)7070- & PSCI_0_2_POWER_STATE_TYPE_MASK) |7171- ((state.affinity_level << PSCI_0_2_POWER_STATE_AFFL_SHIFT)7272- & PSCI_0_2_POWER_STATE_AFFL_MASK);7373-}7474-7575-static int psci_get_version(void)7676-{7777- int err;7878-7979- err = invoke_psci_fn(PSCI_0_2_FN_PSCI_VERSION, 0, 0, 0);8080- return err;8181-}8282-8383-static int psci_cpu_suspend(struct psci_power_state state,8484- unsigned long entry_point)8585-{8686- int err;8787- u32 fn, power_state;8888-8989- fn = psci_function_id[PSCI_FN_CPU_SUSPEND];9090- power_state = psci_power_state_pack(state);9191- err = invoke_psci_fn(fn, power_state, entry_point, 0);9292- return psci_to_linux_errno(err);9393-}9494-9595-static int psci_cpu_off(struct psci_power_state state)9696-{9797- int err;9898- u32 fn, power_state;9999-100100- fn = psci_function_id[PSCI_FN_CPU_OFF];101101- power_state = psci_power_state_pack(state);102102- err = invoke_psci_fn(fn, power_state, 0, 0);103103- return psci_to_linux_errno(err);104104-}105105-106106-static int psci_cpu_on(unsigned long cpuid, unsigned long entry_point)107107-{108108- int err;109109- u32 fn;110110-111111- fn = psci_function_id[PSCI_FN_CPU_ON];112112- err = invoke_psci_fn(fn, cpuid, entry_point, 0);113113- return psci_to_linux_errno(err);114114-}115115-116116-static int psci_migrate(unsigned long cpuid)117117-{118118- int err;119119- u32 fn;120120-121121- fn = psci_function_id[PSCI_FN_MIGRATE];122122- err = invoke_psci_fn(fn, cpuid, 0, 0);123123- return psci_to_linux_errno(err);124124-}125125-126126-static int psci_affinity_info(unsigned long target_affinity,127127- unsigned long lowest_affinity_level)128128-{129129- int err;130130- u32 fn;131131-132132- fn = psci_function_id[PSCI_FN_AFFINITY_INFO];133133- err = invoke_psci_fn(fn, target_affinity, lowest_affinity_level, 0);134134- return err;135135-}136136-137137-static int psci_migrate_info_type(void)138138-{139139- int err;140140- u32 fn;141141-142142- fn = psci_function_id[PSCI_FN_MIGRATE_INFO_TYPE];143143- err = invoke_psci_fn(fn, 0, 0, 0);144144- return err;145145-}146146-147147-static int get_set_conduit_method(struct device_node *np)148148-{149149- const char *method;150150-151151- pr_info("probing for conduit method from DT.\n");152152-153153- if (of_property_read_string(np, "method", &method)) {154154- pr_warn("missing \"method\" property\n");155155- return -ENXIO;156156- }157157-158158- if (!strcmp("hvc", method)) {159159- invoke_psci_fn = __invoke_psci_fn_hvc;160160- } else if (!strcmp("smc", method)) {161161- invoke_psci_fn = __invoke_psci_fn_smc;162162- } else {163163- pr_warn("invalid \"method\" property: %s\n", method);164164- return -EINVAL;165165- }166166- return 0;167167-}168168-169169-static void psci_sys_reset(enum reboot_mode reboot_mode, const char *cmd)170170-{171171- invoke_psci_fn(PSCI_0_2_FN_SYSTEM_RESET, 0, 0, 0);172172-}173173-174174-static void psci_sys_poweroff(void)175175-{176176- invoke_psci_fn(PSCI_0_2_FN_SYSTEM_OFF, 0, 0, 0);177177-}178178-179179-/*180180- * PSCI Function IDs for v0.2+ are well defined so use181181- * standard values.182182- */183183-static int psci_0_2_init(struct device_node *np)184184-{185185- int err, ver;186186-187187- err = get_set_conduit_method(np);188188-189189- if (err)190190- goto out_put_node;191191-192192- ver = psci_get_version();193193-194194- if (ver == PSCI_RET_NOT_SUPPORTED) {195195- /* PSCI v0.2 mandates implementation of PSCI_ID_VERSION. */196196- pr_err("PSCI firmware does not comply with the v0.2 spec.\n");197197- err = -EOPNOTSUPP;198198- goto out_put_node;199199- } else {200200- pr_info("PSCIv%d.%d detected in firmware.\n",201201- PSCI_VERSION_MAJOR(ver),202202- PSCI_VERSION_MINOR(ver));203203-204204- if (PSCI_VERSION_MAJOR(ver) == 0 &&205205- PSCI_VERSION_MINOR(ver) < 2) {206206- err = -EINVAL;207207- pr_err("Conflicting PSCI version detected.\n");208208- goto out_put_node;209209- }210210- }211211-212212- pr_info("Using standard PSCI v0.2 function IDs\n");213213- psci_function_id[PSCI_FN_CPU_SUSPEND] = PSCI_0_2_FN_CPU_SUSPEND;214214- psci_ops.cpu_suspend = psci_cpu_suspend;215215-216216- psci_function_id[PSCI_FN_CPU_OFF] = PSCI_0_2_FN_CPU_OFF;217217- psci_ops.cpu_off = psci_cpu_off;218218-219219- psci_function_id[PSCI_FN_CPU_ON] = PSCI_0_2_FN_CPU_ON;220220- psci_ops.cpu_on = psci_cpu_on;221221-222222- psci_function_id[PSCI_FN_MIGRATE] = PSCI_0_2_FN_MIGRATE;223223- psci_ops.migrate = psci_migrate;224224-225225- psci_function_id[PSCI_FN_AFFINITY_INFO] = PSCI_0_2_FN_AFFINITY_INFO;226226- psci_ops.affinity_info = psci_affinity_info;227227-228228- psci_function_id[PSCI_FN_MIGRATE_INFO_TYPE] =229229- PSCI_0_2_FN_MIGRATE_INFO_TYPE;230230- psci_ops.migrate_info_type = psci_migrate_info_type;231231-232232- arm_pm_restart = psci_sys_reset;233233-234234- pm_power_off = psci_sys_poweroff;235235-236236-out_put_node:237237- of_node_put(np);238238- return err;239239-}240240-241241-/*242242- * PSCI < v0.2 get PSCI Function IDs via DT.243243- */244244-static int psci_0_1_init(struct device_node *np)245245-{246246- u32 id;247247- int err;248248-249249- err = get_set_conduit_method(np);250250-251251- if (err)252252- goto out_put_node;253253-254254- pr_info("Using PSCI v0.1 Function IDs from DT\n");255255-256256- if (!of_property_read_u32(np, "cpu_suspend", &id)) {257257- psci_function_id[PSCI_FN_CPU_SUSPEND] = id;258258- psci_ops.cpu_suspend = psci_cpu_suspend;259259- }260260-261261- if (!of_property_read_u32(np, "cpu_off", &id)) {262262- psci_function_id[PSCI_FN_CPU_OFF] = id;263263- psci_ops.cpu_off = psci_cpu_off;264264- }265265-266266- if (!of_property_read_u32(np, "cpu_on", &id)) {267267- psci_function_id[PSCI_FN_CPU_ON] = id;268268- psci_ops.cpu_on = psci_cpu_on;269269- }270270-271271- if (!of_property_read_u32(np, "migrate", &id)) {272272- psci_function_id[PSCI_FN_MIGRATE] = id;273273- psci_ops.migrate = psci_migrate;274274- }275275-276276-out_put_node:277277- of_node_put(np);278278- return err;279279-}280280-281281-static const struct of_device_id psci_of_match[] __initconst = {282282- { .compatible = "arm,psci", .data = psci_0_1_init},283283- { .compatible = "arm,psci-0.2", .data = psci_0_2_init},284284- {},285285-};286286-287287-int __init psci_init(void)288288-{289289- struct device_node *np;290290- const struct of_device_id *matched_np;291291- psci_initcall_t init_fn;292292-293293- np = of_find_matching_node_and_match(NULL, psci_of_match, &matched_np);294294- if (!np)295295- return -ENODEV;296296-297297- init_fn = (psci_initcall_t)matched_np->data;298298- return init_fn(np);299299-}
+23-8
arch/arm/kernel/psci_smp.c
···1717#include <linux/smp.h>1818#include <linux/of.h>1919#include <linux/delay.h>2020+#include <linux/psci.h>2121+2022#include <uapi/linux/psci.h>21232224#include <asm/psci.h>···5351{5452 if (psci_ops.cpu_on)5553 return psci_ops.cpu_on(cpu_logical_map(cpu),5656- __pa(secondary_startup));5454+ virt_to_idmap(&secondary_startup));5755 return -ENODEV;5856}59576058#ifdef CONFIG_HOTPLUG_CPU5959+int psci_cpu_disable(unsigned int cpu)6060+{6161+ /* Fail early if we don't have CPU_OFF support */6262+ if (!psci_ops.cpu_off)6363+ return -EOPNOTSUPP;6464+6565+ /* Trusted OS will deny CPU_OFF */6666+ if (psci_tos_resident_on(cpu))6767+ return -EPERM;6868+6969+ return 0;7070+}7171+6172void __ref psci_cpu_die(unsigned int cpu)6273{6363- const struct psci_power_state ps = {6464- .type = PSCI_POWER_STATE_TYPE_POWER_DOWN,6565- };7474+ u32 state = PSCI_POWER_STATE_TYPE_POWER_DOWN <<7575+ PSCI_0_2_POWER_STATE_TYPE_SHIFT;66766767- if (psci_ops.cpu_off)6868- psci_ops.cpu_off(ps);7777+ if (psci_ops.cpu_off)7878+ psci_ops.cpu_off(state);69797070- /* We should never return */7171- panic("psci: cpu %d failed to shutdown\n", cpu);8080+ /* We should never return */8181+ panic("psci: cpu %d failed to shutdown\n", cpu);7282}73837484int __ref psci_cpu_kill(unsigned int cpu)···123109struct smp_operations __initdata psci_smp_ops = {124110 .smp_boot_secondary = psci_boot_secondary,125111#ifdef CONFIG_HOTPLUG_CPU112112+ .cpu_disable = psci_cpu_disable,126113 .cpu_die = psci_cpu_die,127114 .cpu_kill = psci_cpu_kill,128115#endif
···2020 select ARM_GIC_V2M if PCI_MSI2121 select ARM_GIC_V32222 select ARM_GIC_V3_ITS if PCI_MSI2323+ select ARM_PSCI_FW2324 select BUILDTIME_EXTABLE_SORT2425 select CLONE_BACKWARDS2526 select COMMON_CLK
+2-2
arch/arm64/include/asm/acpi.h
···1212#ifndef _ASM_ACPI_H1313#define _ASM_ACPI_H14141515-#include <linux/mm.h>1615#include <linux/irqchip/arm-gic-acpi.h>1616+#include <linux/mm.h>1717+#include <linux/psci.h>17181819#include <asm/cputype.h>1919-#include <asm/psci.h>2020#include <asm/smp_plat.h>21212222/* Macros for consistency checks of the GICC subtable of MADT */
-28
arch/arm64/include/asm/psci.h
···11-/*22- * This program is free software; you can redistribute it and/or modify33- * it under the terms of the GNU General Public License version 2 as44- * published by the Free Software Foundation.55- *66- * This program is distributed in the hope that it will be useful,77- * but WITHOUT ANY WARRANTY; without even the implied warranty of88- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the99- * GNU General Public License for more details.1010- *1111- * Copyright (C) 2013 ARM Limited1212- */1313-1414-#ifndef __ASM_PSCI_H1515-#define __ASM_PSCI_H1616-1717-int __init psci_dt_init(void);1818-1919-#ifdef CONFIG_ACPI2020-int __init psci_acpi_init(void);2121-bool __init acpi_psci_present(void);2222-bool __init acpi_psci_use_hvc(void);2323-#else2424-static inline int psci_acpi_init(void) { return 0; }2525-static inline bool acpi_psci_present(void) { return false; }2626-#endif2727-2828-#endif /* __ASM_PSCI_H */
+2-359
arch/arm64/kernel/psci.c
···1818#include <linux/init.h>1919#include <linux/of.h>2020#include <linux/smp.h>2121-#include <linux/reboot.h>2222-#include <linux/pm.h>2321#include <linux/delay.h>2222+#include <linux/psci.h>2423#include <linux/slab.h>2424+2525#include <uapi/linux/psci.h>26262727#include <asm/compiler.h>2828-#include <asm/cputype.h>2928#include <asm/cpu_ops.h>3029#include <asm/errno.h>3131-#include <asm/psci.h>3230#include <asm/smp_plat.h>3331#include <asm/suspend.h>3434-#include <asm/system_misc.h>3535-3636-#define PSCI_POWER_STATE_TYPE_STANDBY 03737-#define PSCI_POWER_STATE_TYPE_POWER_DOWN 138323933static bool psci_power_state_loses_context(u32 state)4034{···4450 return !(state & ~valid_mask);4551}46524747-/*4848- * The CPU any Trusted OS is resident on. The trusted OS may reject CPU_OFF4949- * calls to its resident CPU, so we must avoid issuing those. We never migrate5050- * a Trusted OS even if it claims to be capable of migration -- doing so will5151- * require cooperation with a Trusted OS driver.5252- */5353-static int resident_cpu = -1;5454-5555-struct psci_operations {5656- int (*cpu_suspend)(u32 state, unsigned long entry_point);5757- int (*cpu_off)(u32 state);5858- int (*cpu_on)(unsigned long cpuid, unsigned long entry_point);5959- int (*migrate)(unsigned long cpuid);6060- int (*affinity_info)(unsigned long target_affinity,6161- unsigned long lowest_affinity_level);6262- int (*migrate_info_type)(void);6363-};6464-6565-static struct psci_operations psci_ops;6666-6767-typedef unsigned long (psci_fn)(unsigned long, unsigned long,6868- unsigned long, unsigned long);6969-asmlinkage psci_fn __invoke_psci_fn_hvc;7070-asmlinkage psci_fn __invoke_psci_fn_smc;7171-static psci_fn *invoke_psci_fn;7272-7373-enum psci_function {7474- PSCI_FN_CPU_SUSPEND,7575- PSCI_FN_CPU_ON,7676- PSCI_FN_CPU_OFF,7777- PSCI_FN_MIGRATE,7878- PSCI_FN_MAX,7979-};8080-8153static DEFINE_PER_CPU_READ_MOSTLY(u32 *, psci_power_state);8282-8383-static u32 psci_function_id[PSCI_FN_MAX];8484-8585-static int psci_to_linux_errno(int errno)8686-{8787- switch (errno) {8888- case PSCI_RET_SUCCESS:8989- return 0;9090- case PSCI_RET_NOT_SUPPORTED:9191- return -EOPNOTSUPP;9292- case PSCI_RET_INVALID_PARAMS:9393- return -EINVAL;9494- case PSCI_RET_DENIED:9595- return -EPERM;9696- };9797-9898- return -EINVAL;9999-}100100-101101-static u32 psci_get_version(void)102102-{103103- return invoke_psci_fn(PSCI_0_2_FN_PSCI_VERSION, 0, 0, 0);104104-}105105-106106-static int psci_cpu_suspend(u32 state, unsigned long entry_point)107107-{108108- int err;109109- u32 fn;110110-111111- fn = psci_function_id[PSCI_FN_CPU_SUSPEND];112112- err = invoke_psci_fn(fn, state, entry_point, 0);113113- return psci_to_linux_errno(err);114114-}115115-116116-static int psci_cpu_off(u32 state)117117-{118118- int err;119119- u32 fn;120120-121121- fn = psci_function_id[PSCI_FN_CPU_OFF];122122- err = invoke_psci_fn(fn, state, 0, 0);123123- return psci_to_linux_errno(err);124124-}125125-126126-static int psci_cpu_on(unsigned long cpuid, unsigned long entry_point)127127-{128128- int err;129129- u32 fn;130130-131131- fn = psci_function_id[PSCI_FN_CPU_ON];132132- err = invoke_psci_fn(fn, cpuid, entry_point, 0);133133- return psci_to_linux_errno(err);134134-}135135-136136-static int psci_migrate(unsigned long cpuid)137137-{138138- int err;139139- u32 fn;140140-141141- fn = psci_function_id[PSCI_FN_MIGRATE];142142- err = invoke_psci_fn(fn, cpuid, 0, 0);143143- return psci_to_linux_errno(err);144144-}145145-146146-static int psci_affinity_info(unsigned long target_affinity,147147- unsigned long lowest_affinity_level)148148-{149149- return invoke_psci_fn(PSCI_0_2_FN64_AFFINITY_INFO, target_affinity,150150- lowest_affinity_level, 0);151151-}152152-153153-static int psci_migrate_info_type(void)154154-{155155- return invoke_psci_fn(PSCI_0_2_FN_MIGRATE_INFO_TYPE, 0, 0, 0);156156-}157157-158158-static unsigned long psci_migrate_info_up_cpu(void)159159-{160160- return invoke_psci_fn(PSCI_0_2_FN64_MIGRATE_INFO_UP_CPU, 0, 0, 0);161161-}1625416355static int __maybe_unused cpu_psci_cpu_init_idle(unsigned int cpu)16456{···110230 return ret;111231}112232113113-static int get_set_conduit_method(struct device_node *np)114114-{115115- const char *method;116116-117117- pr_info("probing for conduit method from DT.\n");118118-119119- if (of_property_read_string(np, "method", &method)) {120120- pr_warn("missing \"method\" property\n");121121- return -ENXIO;122122- }123123-124124- if (!strcmp("hvc", method)) {125125- invoke_psci_fn = __invoke_psci_fn_hvc;126126- } else if (!strcmp("smc", method)) {127127- invoke_psci_fn = __invoke_psci_fn_smc;128128- } else {129129- pr_warn("invalid \"method\" property: %s\n", method);130130- return -EINVAL;131131- }132132- return 0;133133-}134134-135135-static void psci_sys_reset(enum reboot_mode reboot_mode, const char *cmd)136136-{137137- invoke_psci_fn(PSCI_0_2_FN_SYSTEM_RESET, 0, 0, 0);138138-}139139-140140-static void psci_sys_poweroff(void)141141-{142142- invoke_psci_fn(PSCI_0_2_FN_SYSTEM_OFF, 0, 0, 0);143143-}144144-145145-/*146146- * Detect the presence of a resident Trusted OS which may cause CPU_OFF to147147- * return DENIED (which would be fatal).148148- */149149-static void __init psci_init_migrate(void)150150-{151151- unsigned long cpuid;152152- int type, cpu;153153-154154- type = psci_ops.migrate_info_type();155155-156156- if (type == PSCI_0_2_TOS_MP) {157157- pr_info("Trusted OS migration not required\n");158158- return;159159- }160160-161161- if (type == PSCI_RET_NOT_SUPPORTED) {162162- pr_info("MIGRATE_INFO_TYPE not supported.\n");163163- return;164164- }165165-166166- if (type != PSCI_0_2_TOS_UP_MIGRATE &&167167- type != PSCI_0_2_TOS_UP_NO_MIGRATE) {168168- pr_err("MIGRATE_INFO_TYPE returned unknown type (%d)\n", type);169169- return;170170- }171171-172172- cpuid = psci_migrate_info_up_cpu();173173- if (cpuid & ~MPIDR_HWID_BITMASK) {174174- pr_warn("MIGRATE_INFO_UP_CPU reported invalid physical ID (0x%lx)\n",175175- cpuid);176176- return;177177- }178178-179179- cpu = get_logical_index(cpuid);180180- resident_cpu = cpu >= 0 ? cpu : -1;181181-182182- pr_info("Trusted OS resident on physical CPU 0x%lx\n", cpuid);183183-}184184-185185-static void __init psci_0_2_set_functions(void)186186-{187187- pr_info("Using standard PSCI v0.2 function IDs\n");188188- psci_function_id[PSCI_FN_CPU_SUSPEND] = PSCI_0_2_FN64_CPU_SUSPEND;189189- psci_ops.cpu_suspend = psci_cpu_suspend;190190-191191- psci_function_id[PSCI_FN_CPU_OFF] = PSCI_0_2_FN_CPU_OFF;192192- psci_ops.cpu_off = psci_cpu_off;193193-194194- psci_function_id[PSCI_FN_CPU_ON] = PSCI_0_2_FN64_CPU_ON;195195- psci_ops.cpu_on = psci_cpu_on;196196-197197- psci_function_id[PSCI_FN_MIGRATE] = PSCI_0_2_FN64_MIGRATE;198198- psci_ops.migrate = psci_migrate;199199-200200- psci_ops.affinity_info = psci_affinity_info;201201-202202- psci_ops.migrate_info_type = psci_migrate_info_type;203203-204204- arm_pm_restart = psci_sys_reset;205205-206206- pm_power_off = psci_sys_poweroff;207207-}208208-209209-/*210210- * Probe function for PSCI firmware versions >= 0.2211211- */212212-static int __init psci_probe(void)213213-{214214- u32 ver = psci_get_version();215215-216216- pr_info("PSCIv%d.%d detected in firmware.\n",217217- PSCI_VERSION_MAJOR(ver),218218- PSCI_VERSION_MINOR(ver));219219-220220- if (PSCI_VERSION_MAJOR(ver) == 0 && PSCI_VERSION_MINOR(ver) < 2) {221221- pr_err("Conflicting PSCI version detected.\n");222222- return -EINVAL;223223- }224224-225225- psci_0_2_set_functions();226226-227227- psci_init_migrate();228228-229229- return 0;230230-}231231-232232-typedef int (*psci_initcall_t)(const struct device_node *);233233-234234-/*235235- * PSCI init function for PSCI versions >=0.2236236- *237237- * Probe based on PSCI PSCI_VERSION function238238- */239239-static int __init psci_0_2_init(struct device_node *np)240240-{241241- int err;242242-243243- err = get_set_conduit_method(np);244244-245245- if (err)246246- goto out_put_node;247247- /*248248- * Starting with v0.2, the PSCI specification introduced a call249249- * (PSCI_VERSION) that allows probing the firmware version, so250250- * that PSCI function IDs and version specific initialization251251- * can be carried out according to the specific version reported252252- * by firmware253253- */254254- err = psci_probe();255255-256256-out_put_node:257257- of_node_put(np);258258- return err;259259-}260260-261261-/*262262- * PSCI < v0.2 get PSCI Function IDs via DT.263263- */264264-static int __init psci_0_1_init(struct device_node *np)265265-{266266- u32 id;267267- int err;268268-269269- err = get_set_conduit_method(np);270270-271271- if (err)272272- goto out_put_node;273273-274274- pr_info("Using PSCI v0.1 Function IDs from DT\n");275275-276276- if (!of_property_read_u32(np, "cpu_suspend", &id)) {277277- psci_function_id[PSCI_FN_CPU_SUSPEND] = id;278278- psci_ops.cpu_suspend = psci_cpu_suspend;279279- }280280-281281- if (!of_property_read_u32(np, "cpu_off", &id)) {282282- psci_function_id[PSCI_FN_CPU_OFF] = id;283283- psci_ops.cpu_off = psci_cpu_off;284284- }285285-286286- if (!of_property_read_u32(np, "cpu_on", &id)) {287287- psci_function_id[PSCI_FN_CPU_ON] = id;288288- psci_ops.cpu_on = psci_cpu_on;289289- }290290-291291- if (!of_property_read_u32(np, "migrate", &id)) {292292- psci_function_id[PSCI_FN_MIGRATE] = id;293293- psci_ops.migrate = psci_migrate;294294- }295295-296296-out_put_node:297297- of_node_put(np);298298- return err;299299-}300300-301301-static const struct of_device_id psci_of_match[] __initconst = {302302- { .compatible = "arm,psci", .data = psci_0_1_init},303303- { .compatible = "arm,psci-0.2", .data = psci_0_2_init},304304- {},305305-};306306-307307-int __init psci_dt_init(void)308308-{309309- struct device_node *np;310310- const struct of_device_id *matched_np;311311- psci_initcall_t init_fn;312312-313313- np = of_find_matching_node_and_match(NULL, psci_of_match, &matched_np);314314-315315- if (!np)316316- return -ENODEV;317317-318318- init_fn = (psci_initcall_t)matched_np->data;319319- return init_fn(np);320320-}321321-322322-#ifdef CONFIG_ACPI323323-/*324324- * We use PSCI 0.2+ when ACPI is deployed on ARM64 and it's325325- * explicitly clarified in SBBR326326- */327327-int __init psci_acpi_init(void)328328-{329329- if (!acpi_psci_present()) {330330- pr_info("is not implemented in ACPI.\n");331331- return -EOPNOTSUPP;332332- }333333-334334- pr_info("probing for conduit method from ACPI.\n");335335-336336- if (acpi_psci_use_hvc())337337- invoke_psci_fn = __invoke_psci_fn_hvc;338338- else339339- invoke_psci_fn = __invoke_psci_fn_smc;340340-341341- return psci_probe();342342-}343343-#endif344344-345233#ifdef CONFIG_SMP346234347235static int __init cpu_psci_cpu_init(unsigned int cpu)···137489}138490139491#ifdef CONFIG_HOTPLUG_CPU140140-static bool psci_tos_resident_on(int cpu)141141-{142142- return cpu == resident_cpu;143143-}144144-145492static int cpu_psci_cpu_disable(unsigned int cpu)146493{147494 /* Fail early if we don't have CPU_OFF support */
···5566menu "Firmware Drivers"7788+config ARM_PSCI_FW99+ bool1010+811config EDD912 tristate "BIOS Enhanced Disk Drive calls determine boot disk"1013 depends on X86
+1
drivers/firmware/Makefile
···11#22# Makefile for the linux kernel.33#44+obj-$(CONFIG_ARM_PSCI_FW) += psci.o45obj-$(CONFIG_DMI) += dmi_scan.o56obj-$(CONFIG_DMI_SYSFS) += dmi-sysfs.o67obj-$(CONFIG_EDD) += edd.o
+382
drivers/firmware/psci.c
···11+/*22+ * This program is free software; you can redistribute it and/or modify33+ * it under the terms of the GNU General Public License version 2 as44+ * published by the Free Software Foundation.55+ *66+ * This program is distributed in the hope that it will be useful,77+ * but WITHOUT ANY WARRANTY; without even the implied warranty of88+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the99+ * GNU General Public License for more details.1010+ *1111+ * Copyright (C) 2015 ARM Limited1212+ */1313+1414+#define pr_fmt(fmt) "psci: " fmt1515+1616+#include <linux/errno.h>1717+#include <linux/linkage.h>1818+#include <linux/of.h>1919+#include <linux/pm.h>2020+#include <linux/printk.h>2121+#include <linux/psci.h>2222+#include <linux/reboot.h>2323+2424+#include <uapi/linux/psci.h>2525+2626+#include <asm/cputype.h>2727+#include <asm/system_misc.h>2828+#include <asm/smp_plat.h>2929+3030+/*3131+ * While a 64-bit OS can make calls with SMC32 calling conventions, for some3232+ * calls it is necessary to use SMC64 to pass or return 64-bit values. For such3333+ * calls PSCI_0_2_FN_NATIVE(x) will choose the appropriate (native-width)3434+ * function ID.3535+ */3636+#ifdef CONFIG_64BIT3737+#define PSCI_0_2_FN_NATIVE(name) PSCI_0_2_FN64_##name3838+#else3939+#define PSCI_0_2_FN_NATIVE(name) PSCI_0_2_FN_##name4040+#endif4141+4242+/*4343+ * The CPU any Trusted OS is resident on. The trusted OS may reject CPU_OFF4444+ * calls to its resident CPU, so we must avoid issuing those. We never migrate4545+ * a Trusted OS even if it claims to be capable of migration -- doing so will4646+ * require cooperation with a Trusted OS driver.4747+ */4848+static int resident_cpu = -1;4949+5050+bool psci_tos_resident_on(int cpu)5151+{5252+ return cpu == resident_cpu;5353+}5454+5555+struct psci_operations psci_ops;5656+5757+typedef unsigned long (psci_fn)(unsigned long, unsigned long,5858+ unsigned long, unsigned long);5959+asmlinkage psci_fn __invoke_psci_fn_hvc;6060+asmlinkage psci_fn __invoke_psci_fn_smc;6161+static psci_fn *invoke_psci_fn;6262+6363+enum psci_function {6464+ PSCI_FN_CPU_SUSPEND,6565+ PSCI_FN_CPU_ON,6666+ PSCI_FN_CPU_OFF,6767+ PSCI_FN_MIGRATE,6868+ PSCI_FN_MAX,6969+};7070+7171+static u32 psci_function_id[PSCI_FN_MAX];7272+7373+static int psci_to_linux_errno(int errno)7474+{7575+ switch (errno) {7676+ case PSCI_RET_SUCCESS:7777+ return 0;7878+ case PSCI_RET_NOT_SUPPORTED:7979+ return -EOPNOTSUPP;8080+ case PSCI_RET_INVALID_PARAMS:8181+ return -EINVAL;8282+ case PSCI_RET_DENIED:8383+ return -EPERM;8484+ };8585+8686+ return -EINVAL;8787+}8888+8989+static u32 psci_get_version(void)9090+{9191+ return invoke_psci_fn(PSCI_0_2_FN_PSCI_VERSION, 0, 0, 0);9292+}9393+9494+static int psci_cpu_suspend(u32 state, unsigned long entry_point)9595+{9696+ int err;9797+ u32 fn;9898+9999+ fn = psci_function_id[PSCI_FN_CPU_SUSPEND];100100+ err = invoke_psci_fn(fn, state, entry_point, 0);101101+ return psci_to_linux_errno(err);102102+}103103+104104+static int psci_cpu_off(u32 state)105105+{106106+ int err;107107+ u32 fn;108108+109109+ fn = psci_function_id[PSCI_FN_CPU_OFF];110110+ err = invoke_psci_fn(fn, state, 0, 0);111111+ return psci_to_linux_errno(err);112112+}113113+114114+static int psci_cpu_on(unsigned long cpuid, unsigned long entry_point)115115+{116116+ int err;117117+ u32 fn;118118+119119+ fn = psci_function_id[PSCI_FN_CPU_ON];120120+ err = invoke_psci_fn(fn, cpuid, entry_point, 0);121121+ return psci_to_linux_errno(err);122122+}123123+124124+static int psci_migrate(unsigned long cpuid)125125+{126126+ int err;127127+ u32 fn;128128+129129+ fn = psci_function_id[PSCI_FN_MIGRATE];130130+ err = invoke_psci_fn(fn, cpuid, 0, 0);131131+ return psci_to_linux_errno(err);132132+}133133+134134+static int psci_affinity_info(unsigned long target_affinity,135135+ unsigned long lowest_affinity_level)136136+{137137+ return invoke_psci_fn(PSCI_0_2_FN_NATIVE(AFFINITY_INFO),138138+ target_affinity, lowest_affinity_level, 0);139139+}140140+141141+static int psci_migrate_info_type(void)142142+{143143+ return invoke_psci_fn(PSCI_0_2_FN_MIGRATE_INFO_TYPE, 0, 0, 0);144144+}145145+146146+static unsigned long psci_migrate_info_up_cpu(void)147147+{148148+ return invoke_psci_fn(PSCI_0_2_FN_NATIVE(MIGRATE_INFO_UP_CPU),149149+ 0, 0, 0);150150+}151151+152152+static int get_set_conduit_method(struct device_node *np)153153+{154154+ const char *method;155155+156156+ pr_info("probing for conduit method from DT.\n");157157+158158+ if (of_property_read_string(np, "method", &method)) {159159+ pr_warn("missing \"method\" property\n");160160+ return -ENXIO;161161+ }162162+163163+ if (!strcmp("hvc", method)) {164164+ invoke_psci_fn = __invoke_psci_fn_hvc;165165+ } else if (!strcmp("smc", method)) {166166+ invoke_psci_fn = __invoke_psci_fn_smc;167167+ } else {168168+ pr_warn("invalid \"method\" property: %s\n", method);169169+ return -EINVAL;170170+ }171171+ return 0;172172+}173173+174174+static void psci_sys_reset(enum reboot_mode reboot_mode, const char *cmd)175175+{176176+ invoke_psci_fn(PSCI_0_2_FN_SYSTEM_RESET, 0, 0, 0);177177+}178178+179179+static void psci_sys_poweroff(void)180180+{181181+ invoke_psci_fn(PSCI_0_2_FN_SYSTEM_OFF, 0, 0, 0);182182+}183183+184184+/*185185+ * Detect the presence of a resident Trusted OS which may cause CPU_OFF to186186+ * return DENIED (which would be fatal).187187+ */188188+static void __init psci_init_migrate(void)189189+{190190+ unsigned long cpuid;191191+ int type, cpu = -1;192192+193193+ type = psci_ops.migrate_info_type();194194+195195+ if (type == PSCI_0_2_TOS_MP) {196196+ pr_info("Trusted OS migration not required\n");197197+ return;198198+ }199199+200200+ if (type == PSCI_RET_NOT_SUPPORTED) {201201+ pr_info("MIGRATE_INFO_TYPE not supported.\n");202202+ return;203203+ }204204+205205+ if (type != PSCI_0_2_TOS_UP_MIGRATE &&206206+ type != PSCI_0_2_TOS_UP_NO_MIGRATE) {207207+ pr_err("MIGRATE_INFO_TYPE returned unknown type (%d)\n", type);208208+ return;209209+ }210210+211211+ cpuid = psci_migrate_info_up_cpu();212212+ if (cpuid & ~MPIDR_HWID_BITMASK) {213213+ pr_warn("MIGRATE_INFO_UP_CPU reported invalid physical ID (0x%lx)\n",214214+ cpuid);215215+ return;216216+ }217217+218218+ cpu = get_logical_index(cpuid);219219+ resident_cpu = cpu >= 0 ? cpu : -1;220220+221221+ pr_info("Trusted OS resident on physical CPU 0x%lx\n", cpuid);222222+}223223+224224+static void __init psci_0_2_set_functions(void)225225+{226226+ pr_info("Using standard PSCI v0.2 function IDs\n");227227+ psci_function_id[PSCI_FN_CPU_SUSPEND] = PSCI_0_2_FN_NATIVE(CPU_SUSPEND);228228+ psci_ops.cpu_suspend = psci_cpu_suspend;229229+230230+ psci_function_id[PSCI_FN_CPU_OFF] = PSCI_0_2_FN_CPU_OFF;231231+ psci_ops.cpu_off = psci_cpu_off;232232+233233+ psci_function_id[PSCI_FN_CPU_ON] = PSCI_0_2_FN_NATIVE(CPU_ON);234234+ psci_ops.cpu_on = psci_cpu_on;235235+236236+ psci_function_id[PSCI_FN_MIGRATE] = PSCI_0_2_FN_NATIVE(MIGRATE);237237+ psci_ops.migrate = psci_migrate;238238+239239+ psci_ops.affinity_info = psci_affinity_info;240240+241241+ psci_ops.migrate_info_type = psci_migrate_info_type;242242+243243+ arm_pm_restart = psci_sys_reset;244244+245245+ pm_power_off = psci_sys_poweroff;246246+}247247+248248+/*249249+ * Probe function for PSCI firmware versions >= 0.2250250+ */251251+static int __init psci_probe(void)252252+{253253+ u32 ver = psci_get_version();254254+255255+ pr_info("PSCIv%d.%d detected in firmware.\n",256256+ PSCI_VERSION_MAJOR(ver),257257+ PSCI_VERSION_MINOR(ver));258258+259259+ if (PSCI_VERSION_MAJOR(ver) == 0 && PSCI_VERSION_MINOR(ver) < 2) {260260+ pr_err("Conflicting PSCI version detected.\n");261261+ return -EINVAL;262262+ }263263+264264+ psci_0_2_set_functions();265265+266266+ psci_init_migrate();267267+268268+ return 0;269269+}270270+271271+typedef int (*psci_initcall_t)(const struct device_node *);272272+273273+/*274274+ * PSCI init function for PSCI versions >=0.2275275+ *276276+ * Probe based on PSCI PSCI_VERSION function277277+ */278278+static int __init psci_0_2_init(struct device_node *np)279279+{280280+ int err;281281+282282+ err = get_set_conduit_method(np);283283+284284+ if (err)285285+ goto out_put_node;286286+ /*287287+ * Starting with v0.2, the PSCI specification introduced a call288288+ * (PSCI_VERSION) that allows probing the firmware version, so289289+ * that PSCI function IDs and version specific initialization290290+ * can be carried out according to the specific version reported291291+ * by firmware292292+ */293293+ err = psci_probe();294294+295295+out_put_node:296296+ of_node_put(np);297297+ return err;298298+}299299+300300+/*301301+ * PSCI < v0.2 get PSCI Function IDs via DT.302302+ */303303+static int __init psci_0_1_init(struct device_node *np)304304+{305305+ u32 id;306306+ int err;307307+308308+ err = get_set_conduit_method(np);309309+310310+ if (err)311311+ goto out_put_node;312312+313313+ pr_info("Using PSCI v0.1 Function IDs from DT\n");314314+315315+ if (!of_property_read_u32(np, "cpu_suspend", &id)) {316316+ psci_function_id[PSCI_FN_CPU_SUSPEND] = id;317317+ psci_ops.cpu_suspend = psci_cpu_suspend;318318+ }319319+320320+ if (!of_property_read_u32(np, "cpu_off", &id)) {321321+ psci_function_id[PSCI_FN_CPU_OFF] = id;322322+ psci_ops.cpu_off = psci_cpu_off;323323+ }324324+325325+ if (!of_property_read_u32(np, "cpu_on", &id)) {326326+ psci_function_id[PSCI_FN_CPU_ON] = id;327327+ psci_ops.cpu_on = psci_cpu_on;328328+ }329329+330330+ if (!of_property_read_u32(np, "migrate", &id)) {331331+ psci_function_id[PSCI_FN_MIGRATE] = id;332332+ psci_ops.migrate = psci_migrate;333333+ }334334+335335+out_put_node:336336+ of_node_put(np);337337+ return err;338338+}339339+340340+static const struct of_device_id psci_of_match[] __initconst = {341341+ { .compatible = "arm,psci", .data = psci_0_1_init},342342+ { .compatible = "arm,psci-0.2", .data = psci_0_2_init},343343+ {},344344+};345345+346346+int __init psci_dt_init(void)347347+{348348+ struct device_node *np;349349+ const struct of_device_id *matched_np;350350+ psci_initcall_t init_fn;351351+352352+ np = of_find_matching_node_and_match(NULL, psci_of_match, &matched_np);353353+354354+ if (!np)355355+ return -ENODEV;356356+357357+ init_fn = (psci_initcall_t)matched_np->data;358358+ return init_fn(np);359359+}360360+361361+#ifdef CONFIG_ACPI362362+/*363363+ * We use PSCI 0.2+ when ACPI is deployed on ARM64 and it's364364+ * explicitly clarified in SBBR365365+ */366366+int __init psci_acpi_init(void)367367+{368368+ if (!acpi_psci_present()) {369369+ pr_info("is not implemented in ACPI.\n");370370+ return -EOPNOTSUPP;371371+ }372372+373373+ pr_info("probing for conduit method from ACPI.\n");374374+375375+ if (acpi_psci_use_hvc())376376+ invoke_psci_fn = __invoke_psci_fn_hvc;377377+ else378378+ invoke_psci_fn = __invoke_psci_fn_smc;379379+380380+ return psci_probe();381381+}382382+#endif
+52
include/linux/psci.h
···11+/*22+ * This program is free software; you can redistribute it and/or modify33+ * it under the terms of the GNU General Public License version 2 as44+ * published by the Free Software Foundation.55+ *66+ * This program is distributed in the hope that it will be useful,77+ * but WITHOUT ANY WARRANTY; without even the implied warranty of88+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the99+ * GNU General Public License for more details.1010+ *1111+ * Copyright (C) 2015 ARM Limited1212+ */1313+1414+#ifndef __LINUX_PSCI_H1515+#define __LINUX_PSCI_H1616+1717+#include <linux/init.h>1818+#include <linux/types.h>1919+2020+#define PSCI_POWER_STATE_TYPE_STANDBY 02121+#define PSCI_POWER_STATE_TYPE_POWER_DOWN 12222+2323+bool psci_tos_resident_on(int cpu);2424+2525+struct psci_operations {2626+ int (*cpu_suspend)(u32 state, unsigned long entry_point);2727+ int (*cpu_off)(u32 state);2828+ int (*cpu_on)(unsigned long cpuid, unsigned long entry_point);2929+ int (*migrate)(unsigned long cpuid);3030+ int (*affinity_info)(unsigned long target_affinity,3131+ unsigned long lowest_affinity_level);3232+ int (*migrate_info_type)(void);3333+};3434+3535+extern struct psci_operations psci_ops;3636+3737+#if defined(CONFIG_ARM_PSCI_FW)3838+int __init psci_dt_init(void);3939+#else4040+static inline int psci_dt_init(void) { return 0; }4141+#endif4242+4343+#if defined(CONFIG_ARM_PSCI_FW) && defined(CONFIG_ACPI)4444+int __init psci_acpi_init(void);4545+bool __init acpi_psci_present(void);4646+bool __init acpi_psci_use_hvc(void);4747+#else4848+static inline int psci_acpi_init(void) { return 0; }4949+static inline bool acpi_psci_present(void) { return false; }5050+#endif5151+5252+#endif /* __LINUX_PSCI_H */