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:
Peng Yang 2022-07-03 03:06:29 -07:00 committed by Gerrit - the friendly Code Review server
parent 381ac781d4
commit 4919ab2b83
3 changed files with 239 additions and 0 deletions

View File

@ -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

View File

@ -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

View 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");