mirror of
https://github.com/torvalds/linux.git
synced 2026-08-01 12:11:59 +02:00
gunyah: pops: Porting Gunyah Guest Power-Operations driver to Pineapple
Add gunyah guest power-operations driver for Pineapple.
This is just snapshot of kalama's gunyah power-operations driver
from 'commit 5beb98404269 ("Merge "i3c: i3c-master-msm-geni: Fix
IBI for invalid device OR handler"")'from msm-5.15 branch.
Change-Id: I87f37b3462d1655d5db051ab3ae861d2345c0c2f
Signed-off-by: Peng Yang <quic_penyan@quicinc.com>
This commit is contained in:
parent
381ac781d4
commit
4919ab2b83
|
|
@ -78,4 +78,15 @@ config GH_MEM_NOTIFIER
|
|||
machines, and register callbacks that get invoked only when
|
||||
notifications pertaining to their memory resources arrive.
|
||||
|
||||
config GH_GUEST_POPS
|
||||
tristate "Gunyah Guest Power-Operations driver"
|
||||
depends on GH_RM_DRV
|
||||
help
|
||||
The driver runs on guest VMs and is majorly responsible for coordinating
|
||||
the guest's power operations. This includes, setting the VM's status,
|
||||
such as OS_STATUS_* or APPS_STATUS_*. It also acts as an input power key
|
||||
driver. That is, it listens to the shutdown requests, and when it receives
|
||||
one, it'll emulate a power key press action by sending an input
|
||||
notification to user-space.
|
||||
|
||||
endif
|
||||
|
|
|
|||
|
|
@ -6,3 +6,4 @@ obj-$(CONFIG_GH_RM_DRV) += gh_rm_drv.o
|
|||
gh_rm_drv-y += gh_rm_core.o gh_rm_iface.o
|
||||
obj-$(CONFIG_GH_IRQ_LEND) += gh_irq_lend.o
|
||||
obj-$(CONFIG_GH_MEM_NOTIFIER) += gh_mem_notifier.o
|
||||
obj-$(CONFIG_GH_GUEST_POPS) += gh_guest_pops.o
|
||||
|
|
|
|||
227
drivers/virt/gunyah/gh_guest_pops.c
Normal file
227
drivers/virt/gunyah/gh_guest_pops.c
Normal file
|
|
@ -0,0 +1,227 @@
|
|||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
/*
|
||||
* Copyright (c) 2021, The Linux Foundation. All rights reserved.
|
||||
*/
|
||||
|
||||
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/input.h>
|
||||
#include <linux/notifier.h>
|
||||
|
||||
#include <linux/gunyah/gh_rm_drv.h>
|
||||
|
||||
#define GH_GUEST_POPS_POFF_BUTTON_HOLD_SHUTDOWN_DELAY_MS 1000
|
||||
#define GH_GUEST_POPS_POFF_BUTTON_HOLD_RESTART_DELAY_MS 500
|
||||
|
||||
static struct input_dev *gh_vm_poff_input;
|
||||
static struct notifier_block rm_nb;
|
||||
|
||||
static struct kobj_type gh_guest_kobj_type = {
|
||||
.sysfs_ops = &kobj_sysfs_ops,
|
||||
};
|
||||
static struct kobject gh_guest_kobj;
|
||||
|
||||
static int gh_guest_pops_handle_stop_shutdown(u32 stop_reason)
|
||||
{
|
||||
/* Emulate a KEY_POWER event to notify user-space of a shutdown */
|
||||
pr_info("Sending KEY_POWER event\n");
|
||||
|
||||
input_report_key(gh_vm_poff_input, KEY_POWER, 1);
|
||||
input_sync(gh_vm_poff_input);
|
||||
|
||||
switch (stop_reason) {
|
||||
case GH_VM_STOP_SHUTDOWN:
|
||||
msleep(GH_GUEST_POPS_POFF_BUTTON_HOLD_SHUTDOWN_DELAY_MS);
|
||||
break;
|
||||
case GH_VM_STOP_RESTART:
|
||||
msleep(GH_GUEST_POPS_POFF_BUTTON_HOLD_RESTART_DELAY_MS);
|
||||
break;
|
||||
}
|
||||
|
||||
input_report_key(gh_vm_poff_input, KEY_POWER, 0);
|
||||
input_sync(gh_vm_poff_input);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int gh_guest_pops_handle_stop_crash(void)
|
||||
{
|
||||
panic("Panic requested by Primary-VM\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
gh_guest_pops_vm_shutdown(struct gh_rm_notif_vm_shutdown_payload *vm_shutdown)
|
||||
{
|
||||
switch (vm_shutdown->stop_reason) {
|
||||
case GH_VM_STOP_SHUTDOWN:
|
||||
return gh_guest_pops_handle_stop_shutdown(vm_shutdown->stop_reason);
|
||||
case GH_VM_STOP_CRASH:
|
||||
return gh_guest_pops_handle_stop_crash();
|
||||
case GH_VM_STOP_RESTART:
|
||||
return gh_guest_pops_handle_stop_shutdown(vm_shutdown->stop_reason);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int gh_guest_pops_rm_notifer_fn(struct notifier_block *nb,
|
||||
unsigned long cmd, void *data)
|
||||
{
|
||||
switch (cmd) {
|
||||
case GH_RM_NOTIF_VM_SHUTDOWN:
|
||||
return gh_guest_pops_vm_shutdown(data);
|
||||
}
|
||||
|
||||
return NOTIFY_DONE;
|
||||
}
|
||||
|
||||
static int __init gh_guest_pops_init_poff(void)
|
||||
{
|
||||
int ret;
|
||||
|
||||
gh_vm_poff_input = input_allocate_device();
|
||||
if (!gh_vm_poff_input)
|
||||
return -ENOMEM;
|
||||
|
||||
input_set_capability(gh_vm_poff_input, EV_KEY, KEY_POWER);
|
||||
|
||||
ret = input_register_device(gh_vm_poff_input);
|
||||
if (ret)
|
||||
goto fail_register;
|
||||
|
||||
rm_nb.notifier_call = gh_guest_pops_rm_notifer_fn;
|
||||
ret = gh_rm_register_notifier(&rm_nb);
|
||||
if (ret)
|
||||
goto fail_init;
|
||||
|
||||
return 0;
|
||||
|
||||
fail_init:
|
||||
input_unregister_device(gh_vm_poff_input);
|
||||
fail_register:
|
||||
input_free_device(gh_vm_poff_input);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static ssize_t gh_guest_set_app_status(struct kobject *kobj,
|
||||
struct kobj_attribute *attr,
|
||||
const char *buf,
|
||||
size_t count)
|
||||
{
|
||||
int ret = 0;
|
||||
u16 app_status;
|
||||
|
||||
ret = kstrtou16(buf, 0, &app_status);
|
||||
if (ret)
|
||||
return -EINVAL;
|
||||
|
||||
if (app_status != GH_RM_APP_STATUS_TUI_SERVICE_BOOT)
|
||||
return -EINVAL;
|
||||
|
||||
ret = gh_rm_vm_set_app_status(app_status);
|
||||
if (ret) {
|
||||
pr_err("Failed to set the APP status\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
static struct kobj_attribute gh_guest_attribute =
|
||||
__ATTR(app_status, 0220, NULL, gh_guest_set_app_status);
|
||||
|
||||
static struct attribute *attrs[] = {
|
||||
&gh_guest_attribute.attr,
|
||||
NULL,
|
||||
};
|
||||
|
||||
static struct attribute_group attr_group = {
|
||||
.attrs = attrs,
|
||||
};
|
||||
|
||||
static int gh_guest_sysfs_init(void)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
ret = kobject_init_and_add(&gh_guest_kobj, &gh_guest_kobj_type,
|
||||
kernel_kobj, "%s", "vm_set_status");
|
||||
if (ret) {
|
||||
pr_err("sysfs create and add failed\n");
|
||||
ret = -ENOMEM;
|
||||
goto error_return;
|
||||
}
|
||||
|
||||
ret = sysfs_create_group(&gh_guest_kobj, &attr_group);
|
||||
if (ret) {
|
||||
pr_err("sysfs create group failed %d\n", ret);
|
||||
goto error_return;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
error_return:
|
||||
if (kobject_name(&gh_guest_kobj) != NULL) {
|
||||
kobject_del(&gh_guest_kobj);
|
||||
kobject_put(&gh_guest_kobj);
|
||||
memset(&gh_guest_kobj, 0, sizeof(gh_guest_kobj));
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void gh_guest_sysfs_cleanup(void)
|
||||
{
|
||||
sysfs_remove_group(&gh_guest_kobj, &attr_group);
|
||||
kobject_del(&gh_guest_kobj);
|
||||
kobject_put(&gh_guest_kobj);
|
||||
memset(&gh_guest_kobj, 0, sizeof(gh_guest_kobj));
|
||||
}
|
||||
|
||||
static void gh_guest_pops_exit_poff(void)
|
||||
{
|
||||
gh_rm_unregister_notifier(&rm_nb);
|
||||
|
||||
input_unregister_device(gh_vm_poff_input);
|
||||
input_free_device(gh_vm_poff_input);
|
||||
}
|
||||
|
||||
static int __init gh_guest_pops_init(void)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = gh_guest_pops_init_poff();
|
||||
if (ret) {
|
||||
pr_err("Failed to initialize guest poweroff driver\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
ret = gh_guest_sysfs_init();
|
||||
if (ret) {
|
||||
pr_err("Failed to init sysfs of guest_pops\n");
|
||||
gh_guest_pops_exit_poff();
|
||||
return 0;
|
||||
}
|
||||
|
||||
ret = gh_rm_vm_set_os_status(GH_RM_OS_STATUS_BOOT);
|
||||
if (ret) {
|
||||
pr_err("Failed to set the OS status\n");
|
||||
gh_guest_pops_exit_poff();
|
||||
gh_guest_sysfs_cleanup();
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
module_init(gh_guest_pops_init);
|
||||
|
||||
static void __exit gh_guest_pops_exit(void)
|
||||
{
|
||||
gh_guest_pops_exit_poff();
|
||||
gh_guest_sysfs_cleanup();
|
||||
}
|
||||
module_exit(gh_guest_pops_exit);
|
||||
|
||||
MODULE_LICENSE("GPL");
|
||||
Loading…
Reference in New Issue
Block a user