Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions drivers/firmware/psci/Kconfig
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
# SPDX-License-Identifier: GPL-2.0-only
config ARM_PSCI_FW
bool
select POWER_RESET
select REBOOT_MODE

config ARM_PSCI_CHECKER
bool "ARM PSCI checker"
Expand Down
92 changes: 91 additions & 1 deletion drivers/firmware/psci/psci.c
Original file line number Diff line number Diff line change
Expand Up @@ -8,15 +8,18 @@

#include <linux/acpi.h>
#include <linux/arm-smccc.h>
#include <linux/bitops.h>
#include <linux/cpuidle.h>
#include <linux/debugfs.h>
#include <linux/errno.h>
#include <linux/linkage.h>
#include <linux/of.h>
#include <linux/panic_notifier.h>
#include <linux/pm.h>
#include <linux/printk.h>
#include <linux/psci.h>
#include <linux/reboot.h>
#include <linux/reboot-mode.h>
#include <linux/slab.h>
#include <linux/suspend.h>

Expand Down Expand Up @@ -51,6 +54,24 @@ static int resident_cpu = -1;
struct psci_operations psci_ops;
static enum arm_smccc_conduit psci_conduit = SMCCC_CONDUIT_NONE;

struct psci_vendor_sysreset2 {
u32 reset_type;
u32 cookie;
bool valid;
};

static struct psci_vendor_sysreset2 vendor_reset;

static int psci_panic_event(struct notifier_block *nb, unsigned long v, void *p)
{
vendor_reset.valid = false;
return NOTIFY_DONE;
}

static struct notifier_block psci_panic_block = {
.notifier_call = psci_panic_event
};

bool psci_tos_resident_on(int cpu)
{
return cpu == resident_cpu;
Expand Down Expand Up @@ -309,7 +330,10 @@ static int get_set_conduit_method(const struct device_node *np)
static int psci_sys_reset(struct notifier_block *nb, unsigned long action,
void *data)
{
if ((reboot_mode == REBOOT_WARM || reboot_mode == REBOOT_SOFT) &&
if (vendor_reset.valid && psci_system_reset2_supported) {
invoke_psci_fn(PSCI_FN_NATIVE(1_1, SYSTEM_RESET2), vendor_reset.reset_type,
vendor_reset.cookie, 0);
} else if ((reboot_mode == REBOOT_WARM || reboot_mode == REBOOT_SOFT) &&
psci_system_reset2_supported) {
/*
* reset_type[31] = 0 (architectural)
Expand Down Expand Up @@ -557,6 +581,72 @@ static const struct platform_suspend_ops psci_suspend_ops = {
.begin = psci_system_suspend_begin,
};

static int psci_set_vendor_sys_reset2(struct reboot_mode_driver *reboot, u64 magic)
{
u32 magic_32;

if (psci_system_reset2_supported) {
magic_32 = magic & GENMASK(31, 0);
vendor_reset.reset_type = PSCI_1_1_RESET_TYPE_VENDOR_START | magic_32;
vendor_reset.cookie = (magic >> 32) & GENMASK(31, 0);
vendor_reset.valid = true;
}

return NOTIFY_DONE;
}

static int __init psci_init_vendor_reset(void)
{
struct reboot_mode_driver *reboot;
struct device_node *psci_np;
struct device_node *np;
int ret;

if (!psci_system_reset2_supported)
return -EINVAL;

psci_np = of_find_compatible_node(NULL, NULL, "arm,psci-1.0");
if (!psci_np)
return -ENODEV;

np = of_find_node_by_name(psci_np, "reboot-mode");
if (!np) {
of_node_put(psci_np);
return -ENODEV;
}

ret = atomic_notifier_chain_register(&panic_notifier_list, &psci_panic_block);
if (ret)
goto err_notifier;

reboot = kzalloc(sizeof(*reboot), GFP_KERNEL);
if (!reboot) {
ret = -ENOMEM;
goto err_kzalloc;
}

reboot->write = psci_set_vendor_sys_reset2;
reboot->name = "psci";

ret = reboot_mode_register(reboot, of_fwnode_handle(np));
if (ret)
goto err_register;

of_node_put(psci_np);
of_node_put(np);
return 0;

err_register:
kfree(reboot);
err_kzalloc:
atomic_notifier_chain_unregister(&panic_notifier_list, &psci_panic_block);
err_notifier:
of_node_put(psci_np);
of_node_put(np);
return ret;
}
late_initcall(psci_init_vendor_reset)

static void __init psci_init_system_reset2(void)
{
int ret;
Expand Down
20 changes: 18 additions & 2 deletions drivers/power/reset/reboot-mode.c
Original file line number Diff line number Diff line change
Expand Up @@ -119,8 +119,16 @@ static int reboot_mode_create_device(struct reboot_mode_driver *reboot)
struct reboot_mode_sysfs_data *priv;
struct mode_info *sysfs_info;
struct mode_info *info;
const char *dev_name;
int ret;

dev_name = reboot->name;
if (!dev_name) {
if (!reboot->dev || !reboot->dev->driver)
return -EINVAL;
dev_name = reboot->dev->driver->name;
}

priv = kzalloc_obj(*priv, GFP_KERNEL);
if (!priv)
return -ENOMEM;
Expand All @@ -146,7 +154,7 @@ static int reboot_mode_create_device(struct reboot_mode_driver *reboot)

priv->reboot_mode_device = device_create(&reboot_mode_class, NULL, 0,
(void *)priv, "%s",
reboot->dev->driver->name);
dev_name);
if (IS_ERR(priv->reboot_mode_device)) {
ret = PTR_ERR(priv->reboot_mode_device);
goto error;
Expand Down Expand Up @@ -237,8 +245,16 @@ static inline void reboot_mode_unregister_device(struct reboot_mode_driver *rebo
{
struct reboot_mode_sysfs_data *priv;
struct device *reboot_mode_device;
const char *dev_name;

dev_name = reboot->name;
if (!dev_name) {
if (!reboot->dev || !reboot->dev->driver)
return;
dev_name = reboot->dev->driver->name;
}

reboot_mode_device = class_find_device(&reboot_mode_class, NULL, reboot->dev->driver->name,
reboot_mode_device = class_find_device(&reboot_mode_class, NULL, dev_name,
reboot_mode_match_by_name);

if (!reboot_mode_device)
Expand Down
1 change: 1 addition & 0 deletions include/linux/reboot-mode.h
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

struct reboot_mode_driver {
struct device *dev;
const char *name;
struct list_head head;
int (*write)(struct reboot_mode_driver *reboot, unsigned int magic);
struct notifier_block reboot_notifier;
Expand Down