mirror of
https://github.com/torvalds/linux.git
synced 2026-07-31 19:47:08 +02:00
gunyah: gh_ctrl: Porting gh_ctrl driver to Pineapple
Add gh_ctrl driver for Pineapple.
This is just snapshot of kalama's gh_ctrl driver from
'commit 5beb98404269 ("Merge "i3c: i3c-master-msm-geni: Fix
IBI for invalid device OR handler"")'from msm-5.15 branch.
Change-Id: Ia3aeefb7abaf6bed1d7057ce4866d4cf92d3c8cd
Signed-off-by: Peng Yang <quic_penyan@quicinc.com>
This commit is contained in:
parent
233918f39e
commit
8a2ba9a2c0
|
|
@ -52,6 +52,16 @@ config GH_VIRT_WATCHDOG
|
|||
actions such as setting the bark/bite time and also petting the
|
||||
watchdog in the hypervisor.
|
||||
|
||||
config GH_CTRL
|
||||
tristate "Create Gunyah entries under /sys/hypervisor"
|
||||
depends on SYSFS
|
||||
select SYS_HYPERVISOR
|
||||
help
|
||||
Create entries under /sys/hypervisor for the Gunyah hypervisor.
|
||||
The driver also provides a facility for controlling
|
||||
hypervisor debug features.
|
||||
See Documentation/ABI/testing/sysfs-hypervisor-gunyah for more details.
|
||||
|
||||
config GH_DBL
|
||||
tristate "Gunyah Doorbell driver"
|
||||
help
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
# SPDX-License-Identifier: GPL-2.0-only
|
||||
obj-$(CONFIG_GH_VIRT_WATCHDOG)+= gh_virt_wdt.o
|
||||
obj-$(CONFIG_GH_CTRL) += gh_ctrl.o
|
||||
obj-$(CONFIG_GH_DBL) += gh_dbl.o
|
||||
obj-$(CONFIG_GH_MSGQ) += gh_msgq.o
|
||||
obj-$(CONFIG_GH_RM_DRV) += gh_rm_drv.o
|
||||
|
|
|
|||
268
drivers/virt/gunyah/gh_ctrl.c
Normal file
268
drivers/virt/gunyah/gh_ctrl.c
Normal file
|
|
@ -0,0 +1,268 @@
|
|||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
/*
|
||||
* Copyright (c) 2020-2021, The Linux Foundation. All rights reserved.
|
||||
*/
|
||||
|
||||
#define pr_fmt(fmt) "gunyah: " fmt
|
||||
|
||||
#include <linux/arm-smccc.h>
|
||||
#include <linux/debugfs.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/kobject.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/printk.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/gunyah/gh_errno.h>
|
||||
#include "hcall_ctrl.h"
|
||||
|
||||
#define QC_HYP_SMCCC_CALL_UID \
|
||||
ARM_SMCCC_CALL_VAL(ARM_SMCCC_FAST_CALL, ARM_SMCCC_SMC_32, \
|
||||
ARM_SMCCC_OWNER_VENDOR_HYP, 0xff01)
|
||||
#define QC_HYP_SMCCC_REVISION \
|
||||
ARM_SMCCC_CALL_VAL(ARM_SMCCC_FAST_CALL, ARM_SMCCC_SMC_32, \
|
||||
ARM_SMCCC_OWNER_VENDOR_HYP, 0xff03)
|
||||
|
||||
#define QC_HYP_UID0 0x19bd54bd
|
||||
#define QC_HYP_UID1 0x0b37571b
|
||||
#define QC_HYP_UID2 0x946f609b
|
||||
#define QC_HYP_UID3 0x54539de6
|
||||
|
||||
#define GH_API_INFO_API_VERSION(x) (((x) >> 0) & 0x3fff)
|
||||
#define GH_API_INFO_BIG_ENDIAN(x) (((x) >> 14) & 1)
|
||||
#define GH_API_INFO_IS_64BIT(x) (((x) >> 15) & 1)
|
||||
#define GH_API_INFO_VARIANT(x) (((x) >> 56) & 0xff)
|
||||
|
||||
#define GH_IDENTIFY_PARTITION_CSPACE(x) (((x) >> 0) & 1)
|
||||
#define GH_IDENTIFY_DOORBELL(x) (((x) >> 1) & 1)
|
||||
#define GH_IDENTIFY_MSGQUEUE(x) (((x) >> 2) & 1)
|
||||
#define GH_IDENTIFY_VIC(x) (((x) >> 3) & 1)
|
||||
#define GH_IDENTIFY_VPM(x) (((x) >> 4) & 1)
|
||||
#define GH_IDENTIFY_VCPU(x) (((x) >> 5) & 1)
|
||||
#define GH_IDENTIFY_MEMEXTENT(x) (((x) >> 6) & 1)
|
||||
#define GH_IDENTIFY_TRACE_CTRL(x) (((x) >> 7) & 1)
|
||||
#define GH_IDENTIFY_ROOTVM_CHANNEL(x) (((x) >> 16) & 1)
|
||||
#define GH_IDENTIFY_SCHEDULER(x) (((x) >> 28) & 0xf)
|
||||
|
||||
static bool qc_hyp_calls;
|
||||
static struct gh_hcall_hyp_identify_resp gunyah_api;
|
||||
|
||||
static ssize_t type_show(struct kobject *kobj, struct kobj_attribute *attr,
|
||||
char *buffer)
|
||||
{
|
||||
return scnprintf(buffer, PAGE_SIZE, "gunyah\n");
|
||||
}
|
||||
static struct kobj_attribute type_attr = __ATTR_RO(type);
|
||||
|
||||
static ssize_t api_show(struct kobject *kobj, struct kobj_attribute *attr,
|
||||
char *buffer)
|
||||
{
|
||||
return scnprintf(buffer, PAGE_SIZE, "%d\n",
|
||||
(int)GH_API_INFO_API_VERSION(gunyah_api.api_info));
|
||||
}
|
||||
static struct kobj_attribute api_attr = __ATTR_RO(api);
|
||||
|
||||
static ssize_t variant_show(struct kobject *kobj, struct kobj_attribute *attr,
|
||||
char *buffer)
|
||||
{
|
||||
return scnprintf(buffer, PAGE_SIZE, "%d\n",
|
||||
(int)GH_API_INFO_VARIANT(gunyah_api.api_info));
|
||||
}
|
||||
static struct kobj_attribute variant_attr = __ATTR_RO(variant);
|
||||
|
||||
static struct attribute *version_attrs[] = { &api_attr.attr,
|
||||
&variant_attr.attr, NULL };
|
||||
|
||||
static const struct attribute_group version_group = {
|
||||
.name = "version",
|
||||
.attrs = version_attrs,
|
||||
};
|
||||
|
||||
static int __init gh_sysfs_register(void)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = sysfs_create_file(hypervisor_kobj, &type_attr.attr);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return sysfs_create_group(hypervisor_kobj, &version_group);
|
||||
}
|
||||
|
||||
static void __exit gh_sysfs_unregister(void)
|
||||
{
|
||||
sysfs_remove_file(hypervisor_kobj, &type_attr.attr);
|
||||
sysfs_remove_group(hypervisor_kobj, &version_group);
|
||||
}
|
||||
|
||||
#if defined(CONFIG_DEBUG_FS)
|
||||
|
||||
#define QC_HYP_SMCCC_UART_DISABLE \
|
||||
ARM_SMCCC_CALL_VAL(ARM_SMCCC_FAST_CALL, ARM_SMCCC_SMC_32, \
|
||||
ARM_SMCCC_OWNER_VENDOR_HYP, 0x0)
|
||||
#define QC_HYP_SMCCC_UART_ENABLE \
|
||||
ARM_SMCCC_CALL_VAL(ARM_SMCCC_FAST_CALL, ARM_SMCCC_SMC_32, \
|
||||
ARM_SMCCC_OWNER_VENDOR_HYP, 0xc)
|
||||
#define ENABLE 1
|
||||
#define DISABLE 0
|
||||
|
||||
static struct dentry *gh_dbgfs_dir;
|
||||
static int hyp_uart_enable;
|
||||
|
||||
static void gh_control_hyp_uart(int val)
|
||||
{
|
||||
switch (val) {
|
||||
case ENABLE:
|
||||
if (!hyp_uart_enable) {
|
||||
hyp_uart_enable = val;
|
||||
pr_info("Gunyah: enabling HYP UART\n");
|
||||
arm_smccc_1_1_smc(QC_HYP_SMCCC_UART_ENABLE, NULL);
|
||||
} else {
|
||||
pr_info("Gunyah: HYP UART already enabled\n");
|
||||
}
|
||||
break;
|
||||
case DISABLE:
|
||||
if (hyp_uart_enable) {
|
||||
hyp_uart_enable = val;
|
||||
pr_info("Gunyah: disabling HYP UART\n");
|
||||
arm_smccc_1_1_smc(QC_HYP_SMCCC_UART_DISABLE, NULL);
|
||||
} else {
|
||||
pr_info("Gunyah: HYP UART already disabled\n");
|
||||
}
|
||||
break;
|
||||
default:
|
||||
pr_info("Gunyah: supported values disable(0)/enable(1)\n");
|
||||
}
|
||||
}
|
||||
|
||||
static int gh_dbgfs_trace_class_set(void *data, u64 val)
|
||||
{
|
||||
return gh_remap_error(gh_hcall_trace_update_class_flags(val, 0, NULL));
|
||||
}
|
||||
|
||||
static int gh_dbgfs_trace_class_clear(void *data, u64 val)
|
||||
{
|
||||
return gh_remap_error(gh_hcall_trace_update_class_flags(0, val, NULL));
|
||||
}
|
||||
|
||||
static int gh_dbgfs_trace_class_get(void *data, u64 *val)
|
||||
{
|
||||
*val = 0;
|
||||
return gh_remap_error(gh_hcall_trace_update_class_flags(0, 0, val));
|
||||
}
|
||||
|
||||
static int gh_dbgfs_hyp_uart_set(void *data, u64 val)
|
||||
{
|
||||
gh_control_hyp_uart(val);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int gh_dbgfs_hyp_uart_get(void *data, u64 *val)
|
||||
{
|
||||
*val = hyp_uart_enable;
|
||||
return 0;
|
||||
}
|
||||
|
||||
DEFINE_DEBUGFS_ATTRIBUTE(gh_dbgfs_trace_class_set_fops,
|
||||
gh_dbgfs_trace_class_get,
|
||||
gh_dbgfs_trace_class_set,
|
||||
"0x%llx\n");
|
||||
|
||||
DEFINE_DEBUGFS_ATTRIBUTE(gh_dbgfs_trace_class_clear_fops,
|
||||
gh_dbgfs_trace_class_get,
|
||||
gh_dbgfs_trace_class_clear,
|
||||
"0x%llx\n");
|
||||
|
||||
DEFINE_DEBUGFS_ATTRIBUTE(gh_dbgfs_hyp_uart_ctrl_fops,
|
||||
gh_dbgfs_hyp_uart_get,
|
||||
gh_dbgfs_hyp_uart_set,
|
||||
"0x%llx\n");
|
||||
|
||||
static int __init gh_dbgfs_register(void)
|
||||
{
|
||||
struct dentry *dentry;
|
||||
|
||||
gh_dbgfs_dir = debugfs_create_dir("gunyah", NULL);
|
||||
if (IS_ERR_OR_NULL(gh_dbgfs_dir))
|
||||
return PTR_ERR(gh_dbgfs_dir);
|
||||
|
||||
if (GH_IDENTIFY_TRACE_CTRL(gunyah_api.flags[0])) {
|
||||
dentry = debugfs_create_file("trace_set", 0600, gh_dbgfs_dir,
|
||||
NULL, &gh_dbgfs_trace_class_set_fops);
|
||||
if (IS_ERR(dentry))
|
||||
return PTR_ERR(dentry);
|
||||
|
||||
dentry = debugfs_create_file("trace_clear", 0600, gh_dbgfs_dir,
|
||||
NULL, &gh_dbgfs_trace_class_clear_fops);
|
||||
if (IS_ERR(dentry))
|
||||
return PTR_ERR(dentry);
|
||||
|
||||
dentry = debugfs_create_file("hyp_uart_ctrl", 0600, gh_dbgfs_dir,
|
||||
NULL, &gh_dbgfs_hyp_uart_ctrl_fops);
|
||||
if (IS_ERR(dentry))
|
||||
return PTR_ERR(dentry);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void __exit gh_dbgfs_unregister(void)
|
||||
{
|
||||
debugfs_remove_recursive(gh_dbgfs_dir);
|
||||
}
|
||||
#else /* !defined (CONFIG_DEBUG_FS) */
|
||||
static inline int gh_dbgfs_register(void) { return 0; }
|
||||
static inline int gh_dbgfs_unregister(void) { return 0; }
|
||||
#endif
|
||||
|
||||
static int __init gh_ctrl_init(void)
|
||||
{
|
||||
int ret;
|
||||
struct device_node *hyp;
|
||||
struct arm_smccc_res res;
|
||||
|
||||
hyp = of_find_node_by_path("/hypervisor");
|
||||
|
||||
if (!hyp || (!of_device_is_compatible(hyp, "qcom,gunyah-hypervisor") &&
|
||||
!of_device_is_compatible(hyp, "qcom,haven-hypervisor"))) {
|
||||
pr_err("gunyah-hypervisor or haven-hypervisor node not present\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
(void)gh_hcall_hyp_identify(&gunyah_api);
|
||||
|
||||
if (GH_API_INFO_API_VERSION(gunyah_api.api_info) != 1) {
|
||||
pr_err("unknown version\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Check for ARM SMCCC VENDOR_HYP service calls by UID. */
|
||||
arm_smccc_1_1_smc(QC_HYP_SMCCC_CALL_UID, &res);
|
||||
if ((res.a0 == QC_HYP_UID0) && (res.a1 == QC_HYP_UID1) &&
|
||||
(res.a2 == QC_HYP_UID2) && (res.a3 == QC_HYP_UID3))
|
||||
qc_hyp_calls = true;
|
||||
|
||||
if (qc_hyp_calls) {
|
||||
ret = gh_sysfs_register();
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = gh_dbgfs_register();
|
||||
if (ret)
|
||||
pr_warn("failed to register dbgfs: %d\n", ret);
|
||||
} else {
|
||||
pr_info("Gunyah: no QC HYP interface detected\n");
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
module_init(gh_ctrl_init);
|
||||
|
||||
static void __exit gh_ctrl_exit(void)
|
||||
{
|
||||
gh_sysfs_unregister();
|
||||
gh_dbgfs_unregister();
|
||||
}
|
||||
module_exit(gh_ctrl_exit);
|
||||
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_DESCRIPTION("Qualcomm Technologies, Inc. Gunyah Hypervisor Control Driver");
|
||||
Loading…
Reference in New Issue
Block a user