@@ -8,6 +8,15 @@ menu "Firmware Drivers"
source "drivers/firmware/arm_scmi/Kconfig"
+config ARM_SCMI_VIRTIO_BACKEND
+ tristate "SCMI Message Protocol Virtio Backend device"
+ depends on ARM || ARM64 || COMPILE_TEST
+ help
+ ARM System Control and Management Interface (SCMI) protocol
+ platform device for SCMI Virtio transport. This component
+ provides platform side implementation of SCMI protocols
+ for clients communication over Virtio transport.
+
config ARM_SCPI_PROTOCOL
tristate "ARM System Control and Power Interface (SCPI) Message Protocol"
depends on ARM || ARM64 || COMPILE_TEST
@@ -12,6 +12,7 @@ scmi-module-objs := $(scmi-bus-y) $(scmi-driver-y) $(scmi-protocols-y) \
$(scmi-transport-y)
obj-$(CONFIG_ARM_SCMI_PROTOCOL) += scmi-module.o
obj-$(CONFIG_ARM_SCMI_POWER_DOMAIN) += scmi_pm_domain.o
+obj-y += virtio_backend/
ifeq ($(CONFIG_THUMB2_KERNEL)$(CONFIG_CC_IS_CLANG),yy)
# The use of R7 in the SMCCC conflicts with the compiler's use of R7 as a frame
@@ -16,18 +16,6 @@
#define SCMI_BASE_NUM_SOURCES 1
#define SCMI_BASE_MAX_CMD_ERR_COUNT 1024
-enum scmi_base_protocol_cmd {
- BASE_DISCOVER_VENDOR = 0x3,
- BASE_DISCOVER_SUB_VENDOR = 0x4,
- BASE_DISCOVER_IMPLEMENT_VERSION = 0x5,
- BASE_DISCOVER_LIST_PROTOCOLS = 0x6,
- BASE_DISCOVER_AGENT = 0x7,
- BASE_NOTIFY_ERRORS = 0x8,
- BASE_SET_DEVICE_PERMISSIONS = 0x9,
- BASE_SET_PROTOCOL_PERMISSIONS = 0xa,
- BASE_RESET_AGENT_CONFIGURATION = 0xb,
-};
-
struct scmi_msg_resp_base_attributes {
u8 num_protocols;
u8 num_agents;
@@ -39,6 +39,18 @@ enum scmi_common_cmd {
PROTOCOL_MESSAGE_ATTRIBUTES = 0x2,
};
+enum scmi_base_protocol_cmd {
+ BASE_DISCOVER_VENDOR = 0x3,
+ BASE_DISCOVER_SUB_VENDOR = 0x4,
+ BASE_DISCOVER_IMPLEMENT_VERSION = 0x5,
+ BASE_DISCOVER_LIST_PROTOCOLS = 0x6,
+ BASE_DISCOVER_AGENT = 0x7,
+ BASE_NOTIFY_ERRORS = 0x8,
+ BASE_SET_DEVICE_PERMISSIONS = 0x9,
+ BASE_SET_PROTOCOL_PERMISSIONS = 0xa,
+ BASE_RESET_AGENT_CONFIGURATION = 0xb,
+};
+
/**
* struct scmi_msg_resp_prot_version - Response for a message
*
@@ -68,6 +80,9 @@ struct scmi_msg_resp_prot_version {
#define MSG_TOKEN_ID_MASK GENMASK(27, 18)
#define MSG_XTRACT_TOKEN(hdr) FIELD_GET(MSG_TOKEN_ID_MASK, (hdr))
#define MSG_TOKEN_MAX (MSG_XTRACT_TOKEN(MSG_TOKEN_ID_MASK) + 1)
+#define MSG_HDR_SZ 4
+#define MSG_STATUS_SZ 4
+
/*
* Size of @pending_xfers hashtable included in @scmi_xfers_info; ideally, in
new file mode 100644
@@ -0,0 +1,5 @@
+# SPDX-License-Identifier: GPL-2.0-only
+scmi-vio-backend-y = backend.o client_handle.o
+scmi-vio-backend-protocols-y = base.o
+scmi-vio-backend-objs := $(scmi-vio-backend-y) $(scmi-vio-backend-protocols-y)
+obj-$(CONFIG_ARM_SCMI_VIRTIO_BACKEND) += scmi-vio-backend.o
new file mode 100644
@@ -0,0 +1,516 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (c) 2022 Qualcomm Innovation Center, Inc. All rights reserved.
+ */
+
+#define pr_fmt(fmt) "SCMI Virtio BE: " fmt
+
+#include <linux/device.h>
+#include <linux/export.h>
+#include <linux/idr.h>
+#include <linux/module.h>
+#include <linux/of_device.h>
+#include <linux/scmi_protocol.h>
+#include <linux/scmi_vio_backend.h>
+#include <linux/slab.h>
+#include <linux/bitfield.h>
+
+#include "common.h"
+#include "backend.h"
+#include "client_handle.h"
+#include "../common.h"
+#include "backend_protocol.h"
+
+
+/**
+ * scmi_vio_client_priv - Structure respreseting client specific
+ * private data maintained by backend.
+ *
+ * @backend_protocol_map: IDR used by Virtio backend, to maintain per protocol
+ * private data. This map is initialized with protocol data,
+ * on scmi_vio_be_open() call during client channel setup.
+ * The per protocol pointer is initialized by each registered
+ * backend protocol, in its open() call, and can be used to
+ * reference client specific bookkeeping information, which
+ * is maintained by that protocol. This can be used to release
+ * client specific protocol resources during close call for
+ * it.
+ */
+struct scmi_vio_client_priv {
+ struct idr backend_protocol_map;
+};
+
+struct scmi_vio_be_info {
+ struct device *dev;
+ struct idr registered_protocols;
+ struct idr active_protocols;
+ struct rw_semaphore active_protocols_rwsem;
+};
+
+static struct scmi_vio_be_info scmi_vio_be_info;
+
+void *scmi_vio_get_protocol_priv(
+ const struct scmi_vio_protocol_h *__maybe_unused prot_h,
+ const struct scmi_vio_client_h *client_h)
+{
+ return scmi_vio_get_client_priv(client_h);
+}
+
+void scmi_vio_set_protocol_priv(
+ const struct scmi_vio_protocol_h *__maybe_unused prot_h,
+ const struct scmi_vio_client_h *client_h,
+ void *priv)
+{
+ scmi_vio_set_client_priv(client_h, priv);
+}
+
+int scmi_vio_protocol_register(const struct scmi_vio_protocol *proto)
+{
+ int ret;
+
+ if (!proto) {
+ pr_err("Invalid protocol\n");
+ return -EINVAL;
+ }
+
+ if (!proto->prot_init_fn || !proto->prot_exit_fn) {
+ pr_err("Missing protocol init/exit fn %#x\n", proto->id);
+ return -EINVAL;
+ }
+
+ ret = idr_alloc(&scmi_vio_be_info.registered_protocols, (void *)proto,
+ proto->id, proto->id + 1, GFP_ATOMIC);
+ if (ret != proto->id) {
+ pr_err(
+ "Idr allocation failed for %#x - err %d\n",
+ proto->id, ret);
+ return ret;
+ }
+
+ pr_err("Registered Protocol %#x\n", proto->id);
+
+ return 0;
+}
+
+void scmi_vio_protocol_unregister(const struct scmi_vio_protocol *proto)
+{
+ idr_remove(&scmi_vio_be_info.registered_protocols, proto->id);
+ pr_err("Unregistered Protocol %#x\n", proto->id);
+}
+
+int scmi_vio_implemented_protocols(
+ const struct scmi_vio_protocol_h *__maybe_unused prot_h,
+ const struct scmi_vio_client_h *__maybe_unused client_h,
+ u8 *imp_protocols, int *imp_proto_num)
+{
+ const struct scmi_vio_protocol_h *protocol_h;
+ const struct scmi_vio_protocol_h *base_protocol_h;
+ int active_protocols_num = 0;
+ int proto_num = *imp_proto_num;
+ unsigned int id = 0;
+ int ret = 0;
+
+ base_protocol_h = idr_find(&scmi_vio_be_info.active_protocols,
+ SCMI_PROTOCOL_BASE);
+ if (prot_h != base_protocol_h)
+ return -EINVAL;
+
+ /*
+ * SCMI agents, identified by client_h, may not have access to
+ * all protocols?
+ */
+ idr_for_each_entry(&scmi_vio_be_info.active_protocols, protocol_h, id) {
+ if (id == SCMI_PROTOCOL_BASE)
+ continue;
+ if (active_protocols_num >= proto_num) {
+ pr_err("Number of active protocols %d > max num: %d\n",
+ (active_protocols_num + 1), proto_num);
+ ret = -ENOMEM;
+ goto imp_protocols_exit;
+ }
+ imp_protocols[active_protocols_num] = id;
+ active_protocols_num++;
+ }
+imp_protocols_exit:
+ *imp_proto_num = active_protocols_num;
+ return ret;
+}
+
+static int scmi_vio_protocol_init(struct device *dev, u8 protocol_id)
+{
+ int ret = 0, ret2 = 0;
+ struct scmi_vio_protocol *protocol_info;
+ struct scmi_vio_protocol_h *protocol_h;
+
+ down_write(&scmi_vio_be_info.active_protocols_rwsem);
+ protocol_info = idr_find(&scmi_vio_be_info.registered_protocols, protocol_id);
+ if (!protocol_info) {
+ pr_err("Protocol %#x not registered; protocol init failed\n",
+ protocol_id);
+ ret = -EINVAL;
+ goto out_protocol_release_rwsem;
+ }
+
+ protocol_h = kzalloc(sizeof(*protocol_h), GFP_KERNEL);
+ if (!protocol_h) {
+ ret = -ENOMEM;
+ goto out_protocol_release_rwsem;
+ }
+
+ protocol_h->dev = dev;
+ ret = protocol_info->prot_init_fn(protocol_h);
+ if (ret) {
+ pr_err("Protocol %#x init function returned: %d\n",
+ protocol_id, ret);
+ kfree(protocol_h);
+ protocol_h = NULL;
+ goto out_protocol_release_rwsem;
+ }
+
+ /* Register this protocol in active protocols list */
+ ret = idr_alloc(&scmi_vio_be_info.active_protocols,
+ (void *)protocol_h,
+ protocol_id, protocol_id + 1, GFP_ATOMIC);
+ if (ret != protocol_id) {
+ pr_err(
+ "Allocation failed for active protocol %#x - err %d\n",
+ protocol_id, ret);
+ ret2 = protocol_info->prot_exit_fn(protocol_h);
+ if (ret2)
+ pr_err("Protocol %#x exit function returned: %d\n",
+ protocol_id, ret2);
+ kfree(protocol_h);
+ protocol_h = NULL;
+ goto out_protocol_release_rwsem;
+ }
+
+ ret = 0;
+out_protocol_release_rwsem:
+ up_write(&scmi_vio_be_info.active_protocols_rwsem);
+ return ret;
+}
+
+static int scmi_vio_protocol_exit(struct device *dev)
+{
+ int ret = 0;
+ unsigned int id = 0;
+ struct scmi_vio_protocol *protocol_info;
+ const struct scmi_vio_protocol_h *protocol_h;
+
+ down_write(&scmi_vio_be_info.active_protocols_rwsem);
+ idr_for_each_entry(&scmi_vio_be_info.active_protocols, protocol_h, id) {
+ protocol_info = idr_find(&scmi_vio_be_info.registered_protocols, id);
+ ret = protocol_info->prot_exit_fn(protocol_h);
+ if (ret) {
+ pr_err("Protocol %#x exit function returned: %d\n",
+ id, ret);
+ }
+ kfree(protocol_h);
+ idr_remove(&scmi_vio_be_info.active_protocols, id);
+ }
+ up_write(&scmi_vio_be_info.active_protocols_rwsem);
+
+ return ret;
+}
+
+int scmi_vio_be_open(const struct scmi_vio_client_h *client_h)
+{
+ int tmp_id, ret = 0, close_ret = 0;
+ const struct scmi_vio_protocol_h *protocol_h;
+ struct scmi_vio_protocol *vio_be_protocol;
+ unsigned int id = 0;
+ struct scmi_vio_client_h *proto_client_h;
+ struct scmi_vio_client_priv *client_priv;
+ u8 open_protocols[MAX_PROTOCOLS_IMP];
+ u8 open_protocols_num = 0;
+
+ client_priv = kzalloc(sizeof(*client_priv), GFP_KERNEL);
+
+ if (!client_priv)
+ return -ENOMEM;
+
+ idr_init(&client_priv->backend_protocol_map);
+
+ down_read(&scmi_vio_be_info.active_protocols_rwsem);
+ idr_for_each_entry(&scmi_vio_be_info.active_protocols,
+ protocol_h, id) {
+ vio_be_protocol = idr_find(&scmi_vio_be_info.registered_protocols, id);
+ proto_client_h = scmi_vio_get_client_h(client_h);
+ if (!proto_client_h) {
+ ret = -ENOMEM;
+ goto vio_proto_open_fail;
+ }
+ ret = vio_be_protocol->prot_ops->open(proto_client_h);
+ if (ret) {
+ pr_err("->open() failed for id: %d ret: %d\n",
+ id, ret);
+ goto vio_proto_open_fail;
+ }
+ tmp_id = idr_alloc(&client_priv->backend_protocol_map,
+ (void *)proto_client_h,
+ id, id + 1, GFP_ATOMIC);
+ if (tmp_id != id) {
+ pr_err("Failed to allocate client handle for %#x\n",
+ id);
+ close_ret = vio_be_protocol->prot_ops->close(
+ proto_client_h);
+ if (close_ret) {
+ pr_err("->close() failed for id: %d ret: %d\n",
+ id, close_ret);
+ }
+ scmi_vio_put_client_h(proto_client_h);
+ ret = -EINVAL;
+ goto vio_proto_open_fail;
+ }
+ open_protocols[open_protocols_num++] = (u8)id;
+ }
+ up_read(&scmi_vio_be_info.active_protocols_rwsem);
+ scmi_vio_set_client_priv(client_h, client_priv);
+
+ return 0;
+
+vio_proto_open_fail:
+ for ( ; open_protocols_num > 0; open_protocols_num--) {
+ id = open_protocols[open_protocols_num-1];
+ vio_be_protocol = idr_find(&scmi_vio_be_info.registered_protocols, id);
+ proto_client_h = idr_find(&client_priv->backend_protocol_map, id);
+ close_ret = vio_be_protocol->prot_ops->close(proto_client_h);
+ WARN(close_ret, "->close() failed for id: %d ret: %d\n",
+ id, close_ret);
+ scmi_vio_put_client_h(proto_client_h);
+ }
+
+ up_read(&scmi_vio_be_info.active_protocols_rwsem);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(scmi_vio_be_open);
+
+int scmi_vio_be_close(const struct scmi_vio_client_h *client_h)
+{
+ const struct scmi_vio_protocol_h *protocol_h;
+ struct scmi_vio_protocol *vio_be_protocol;
+ unsigned int id = 0;
+ struct scmi_vio_client_priv *client_priv =
+ scmi_vio_get_client_priv(client_h);
+ struct scmi_vio_client_h *proto_client_h;
+ int ret = 0, close_ret = 0;
+
+ if (!client_priv) {
+ pr_err("->close() failed: priv data not available\n");
+ return -EINVAL;
+ }
+
+ down_read(&scmi_vio_be_info.active_protocols_rwsem);
+ idr_for_each_entry(&scmi_vio_be_info.active_protocols,
+ protocol_h, id) {
+ vio_be_protocol = idr_find(&scmi_vio_be_info.registered_protocols, id);
+ proto_client_h = idr_find(&client_priv->backend_protocol_map, id);
+ /* We might have failed to alloc idr, in scmi_vio_be_open() */
+ if (!proto_client_h)
+ continue;
+ close_ret = vio_be_protocol->prot_ops->close(proto_client_h);
+ WARN(close_ret, "->close() failed for id: %d ret: %d\n",
+ id, close_ret);
+ /* Return first failure return code */
+ ret = ret ? : close_ret;
+ scmi_vio_put_client_h(proto_client_h);
+ }
+ up_read(&scmi_vio_be_info.active_protocols_rwsem);
+
+ idr_destroy(&client_priv->backend_protocol_map);
+ kfree(client_priv);
+ scmi_vio_set_client_priv(client_h, NULL);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(scmi_vio_be_close);
+
+int scmi_vio_be_request(const struct scmi_vio_client_h *client_h,
+ const struct scmi_vio_be_msg *req,
+ struct scmi_vio_be_msg *resp)
+{ int ret = 0;
+ struct scmi_vio_be_msg_hdr msg_header;
+ u32 msg;
+ struct scmi_vio_be_payload_buf req_payload, resp_payload;
+ struct scmi_vio_protocol *vio_be_protocol;
+ const struct scmi_vio_protocol_h *protocol_h;
+ struct scmi_vio_client_priv *client_priv =
+ scmi_vio_get_client_priv(client_h);
+ struct scmi_vio_client_h *proto_client_h;
+ int scmi_error = 0;
+
+ /* Unpack message header */
+ if (!req || req->msg_sz < MSG_HDR_SZ ||
+ !resp || resp->msg_sz <
+ (MSG_HDR_SZ + MSG_STATUS_SZ)) {
+ pr_err("Invalid equest/response message size\n");
+ return -EINVAL;
+ }
+
+ down_read(&scmi_vio_be_info.active_protocols_rwsem);
+ msg = le32_to_cpu(*(__le32 *)req->msg_payld);
+ msg_header.protocol_id = MSG_XTRACT_PROT_ID(msg);
+ msg_header.msg_id = MSG_XTRACT_ID(msg);
+ msg_header.type = MSG_XTRACT_TYPE(msg);
+ msg_header.seq = MSG_XTRACT_TOKEN(msg);
+
+ *(__le32 *)resp->msg_payld = cpu_to_le32(msg);
+
+ resp_payload.payload =
+ (void *)((unsigned long)(uintptr_t)resp->msg_payld
+ + MSG_HDR_SZ);
+
+ if (!client_priv) {
+ pr_err("Private map not initialized\n");
+ scmi_error = SCMI_VIO_BE_NOT_FOUND;
+ goto protocol_find_fail;
+ }
+
+ protocol_h = idr_find(&scmi_vio_be_info.active_protocols,
+ msg_header.protocol_id);
+ if (unlikely(!protocol_h)) {
+ pr_err("Invalid protocol id in request header :%#x\n",
+ msg_header.protocol_id);
+ scmi_error = SCMI_VIO_BE_NOT_FOUND;
+ goto protocol_find_fail;
+ }
+
+ proto_client_h = idr_find(&client_priv->backend_protocol_map,
+ msg_header.protocol_id);
+ if (!proto_client_h) {
+ pr_err("Frontend handle not present for protocol : %#x\n",
+ msg_header.protocol_id);
+ scmi_error = SCMI_VIO_BE_NOT_FOUND;
+ goto protocol_find_fail;
+ }
+
+ vio_be_protocol = idr_find(&scmi_vio_be_info.registered_protocols,
+ msg_header.protocol_id);
+ if (!vio_be_protocol) {
+ pr_err("Protocol %#x is not registered\n",
+ msg_header.protocol_id);
+ scmi_error = SCMI_VIO_BE_NOT_FOUND;
+ goto protocol_find_fail;
+ }
+
+ req_payload.payload =
+ (void *)((unsigned long)(uintptr_t)req->msg_payld +
+ MSG_HDR_SZ);
+ req_payload.payload_size = req->msg_sz - MSG_HDR_SZ;
+
+ resp_payload.payload_size = resp->msg_sz - MSG_HDR_SZ;
+
+ ret = vio_be_protocol->prot_ops->msg_handle(
+ proto_client_h, &msg_header, &req_payload, &resp_payload);
+ if (ret) {
+ pr_err("Protocol %#x ->msg_handle() failed err: %d\n",
+ msg_header.protocol_id, ret);
+ scmi_error = scmi_vio_linux_err_remap(ret);
+ goto protocol_find_fail;
+ }
+
+ up_read(&scmi_vio_be_info.active_protocols_rwsem);
+
+ resp->msg_sz = MSG_HDR_SZ + resp_payload.payload_size;
+
+ return 0;
+
+protocol_find_fail:
+ up_read(&scmi_vio_be_info.active_protocols_rwsem);
+ *((__le32 *)(resp_payload.payload)) = cpu_to_le32(scmi_error);
+ resp->msg_sz = MSG_HDR_SZ + MSG_STATUS_SZ;
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(scmi_vio_be_request);
+
+static int scmi_vio_be_probe(struct platform_device *pdev)
+{
+ int ret;
+ struct device *dev = &pdev->dev;
+ struct device_node *child, *np = dev->of_node;
+
+ scmi_vio_be_info.dev = dev;
+
+ platform_set_drvdata(pdev, &scmi_vio_be_info);
+
+ /*
+ * Base protocol is always implemented. So init it.
+ */
+ ret = scmi_vio_protocol_init(dev, SCMI_PROTOCOL_BASE);
+ if (ret) {
+ dev_err(dev, "Base protocol init failed with err: %d\n", ret);
+ return -EPROBE_DEFER;
+ }
+
+ for_each_available_child_of_node(np, child) {
+ u32 prot_id;
+
+ if (of_property_read_u32(child, "reg", &prot_id))
+ continue;
+
+ if (!FIELD_FIT(MSG_PROTOCOL_ID_MASK, prot_id)) {
+ dev_err(dev, "Out of range protocol %d\n", prot_id);
+ continue;
+ }
+
+ ret = scmi_vio_protocol_init(dev, prot_id);
+ if (ret == -EPROBE_DEFER)
+ goto vio_be_protocol_exit;
+ }
+
+ return 0;
+
+vio_be_protocol_exit:
+ scmi_vio_protocol_exit(dev);
+
+ return ret;
+}
+
+static int scmi_vio_be_remove(struct platform_device *pdev)
+{
+ scmi_vio_protocol_exit(&pdev->dev);
+
+ return 0;
+}
+
+static const struct of_device_id scmi_vio_be_of_match[] = {
+ { .compatible = "arm,scmi-vio-backend" },
+ { },
+};
+
+MODULE_DEVICE_TABLE(of, scmi_vio_be_of_match);
+
+static struct platform_driver scmi_vio_be_driver = {
+ .driver = {
+ .name = "arm-scmi-virtio-backend",
+ .of_match_table = scmi_vio_be_of_match,
+ },
+ .probe = scmi_vio_be_probe,
+ .remove = scmi_vio_be_remove,
+};
+
+static int __init scmi_vio_be_init(void)
+{
+ idr_init(&scmi_vio_be_info.registered_protocols);
+ idr_init(&scmi_vio_be_info.active_protocols);
+ init_rwsem(&scmi_vio_be_info.active_protocols_rwsem);
+ scmi_vio_base_register();
+
+ return platform_driver_register(&scmi_vio_be_driver);
+}
+module_init(scmi_vio_be_init);
+
+static void __exit scmi_vio_be_exit(void)
+{
+ scmi_vio_base_unregister();
+ idr_destroy(&scmi_vio_be_info.active_protocols);
+ idr_destroy(&scmi_vio_be_info.registered_protocols);
+ platform_driver_unregister(&scmi_vio_be_driver);
+}
+module_exit(scmi_vio_be_exit);
+
+MODULE_ALIAS("platform: scmi-vio-backend");
+MODULE_DESCRIPTION("ARM SCMI protocol Virtio Backend driver");
+MODULE_LICENSE("GPL");
new file mode 100644
@@ -0,0 +1,20 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (c) 2022 Qualcomm Innovation Center, Inc. All rights reserved.
+ */
+#ifndef _SCMI_VIO_BE_H
+#define _SCMI_VIO_BE_H
+
+void *scmi_vio_get_client_priv(
+ const struct scmi_vio_client_h *client_h);
+
+void scmi_vio_set_client_priv(
+ const struct scmi_vio_client_h *client_h,
+ void *priv);
+
+#define DECLARE_SCMI_VIO_REGISTER_UNREGISTER(protocol) \
+ int __init scmi_vio_##protocol##_register(void); \
+ void __exit scmi_vio_##protocol##_unregister(void)
+
+DECLARE_SCMI_VIO_REGISTER_UNREGISTER(base);
+#endif /* _SCMI_VIO_BE_H */
new file mode 100644
@@ -0,0 +1,93 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (c) 2022 Qualcomm Innovation Center, Inc. All rights reserved.
+ */
+
+#ifndef _SCMI_VIO_BE_PROTOCOL_H
+#define _SCMI_VIO_BE_PROTOCOL_H
+
+#include <linux/bitfield.h>
+
+#define SCMI_VIO_BE_SUCCESS 0
+#define SCMI_VIO_BE_NOT_SUPPORTED -1
+#define SCMI_VIO_BE_INVALID_PARAMETERS -2
+#define SCMI_VIO_BE_DENIED -3
+#define SCMI_VIO_BE_NOT_FOUND -4
+#define SCMI_VIO_BE_OUT_OF_RANGE -5
+#define SCMI_VIO_BE_BUSY -6
+#define SCMI_VIO_BE_COMMS_ERROR -7
+#define SCMI_VIO_BE_GENERIC_ERROR -8
+#define SCMI_VIO_BE_HARDWARE_ERROR -9
+#define SCMI_VIO_BE_PROTOCOL_ERROR -10
+
+#define SCMI_VIO_BE_VER_MINOR_MASK GENMASK(15, 0)
+#define SCMI_VIO_BE_VER_MAJOR_MASK GENMASK(31, 16)
+
+#define SCMI_VIO_BE_PROTO_MAJOR_VERSION(val) \
+ ((u32)FIELD_PREP(SCMI_VIO_BE_VER_MAJOR_MASK, (val)))
+#define SCMI_VIO_BE_PROTO_MINOR_VERSION(val) \
+ ((u32)FIELD_PREP(SCMI_VIO_BE_VER_MINOR_MASK, (val)))
+
+#define SCMI_VIO_BE_PROTO_VERSION(major, minor) \
+ (SCMI_VIO_BE_PROTO_MAJOR_VERSION((major)) | \
+ SCMI_VIO_BE_PROTO_MINOR_VERSION((minor)))
+
+static int __maybe_unused scmi_vio_linux_err_remap(const int errno)
+{
+ switch (errno) {
+ case 0:
+ return SCMI_VIO_BE_SUCCESS;
+ case -EOPNOTSUPP:
+ return SCMI_VIO_BE_NOT_SUPPORTED;
+ case -EINVAL:
+ return SCMI_VIO_BE_INVALID_PARAMETERS;
+ case -EACCES:
+ return SCMI_VIO_BE_DENIED;
+ case -ENOENT:
+ return SCMI_VIO_BE_NOT_FOUND;
+ case -ERANGE:
+ return SCMI_VIO_BE_OUT_OF_RANGE;
+ case -EBUSY:
+ return SCMI_VIO_BE_BUSY;
+ case -ECOMM:
+ return SCMI_VIO_BE_COMMS_ERROR;
+ case -EIO:
+ return SCMI_VIO_BE_GENERIC_ERROR;
+ case -EREMOTEIO:
+ return SCMI_VIO_BE_HARDWARE_ERROR;
+ case -EPROTO:
+ return SCMI_VIO_BE_PROTOCOL_ERROR;
+ default:
+ return SCMI_VIO_BE_GENERIC_ERROR;
+ }
+}
+
+int scmi_vio_protocol_register(const struct scmi_vio_protocol *proto);
+void scmi_vio_protocol_unregister(const struct scmi_vio_protocol *proto);
+
+void *scmi_vio_get_protocol_priv(
+ const struct scmi_vio_protocol_h *__maybe_unused prot_handle,
+ const struct scmi_vio_client_h *client_h);
+void scmi_vio_set_protocol_priv(
+ const struct scmi_vio_protocol_h *__maybe_unused prot_handle,
+ const struct scmi_vio_client_h *client_h,
+ void *priv);
+
+int scmi_vio_implemented_protocols(
+ const struct scmi_vio_protocol_h *__maybe_unused prot_handle,
+ const struct scmi_vio_client_h *__maybe_unused client_h,
+ u8 *imp_protocols, int *imp_proto_num);
+
+#define DEFINE_SCMI_VIO_PROT_REG_UNREG(name, proto) \
+static const struct scmi_vio_protocol *__this_proto = &(proto); \
+ \
+int __init scmi_vio_##name##_register(void) \
+{ \
+ return scmi_vio_protocol_register(__this_proto); \
+} \
+ \
+void __exit scmi_vio_##name##_unregister(void) \
+{ \
+ scmi_vio_protocol_unregister(__this_proto); \
+}
+#endif /* _SCMI_VIO_BE_PROTOCOL_H */
new file mode 100644
@@ -0,0 +1,474 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (c) 2022 Qualcomm Innovation Center, Inc. All rights reserved.
+ */
+
+#include <linux/device.h>
+#include <linux/export.h>
+#include <linux/module.h>
+#include <linux/of_device.h>
+#include <linux/scmi_protocol.h>
+#include <linux/scmi_vio_backend.h>
+#include <linux/slab.h>
+#include <linux/bitfield.h>
+#include <linux/byteorder/generic.h>
+
+#include "common.h"
+#include "backend.h"
+#include "client_handle.h"
+
+#include "../common.h"
+#include "backend_protocol.h"
+
+#define SCMI_VIO_BASE_NUM_AGENTS 1
+#define SCMI_VIO_BASE_AGENT_MASK GENMASK(15, 8)
+#define SCMI_VIO_BASE_NUM_AGENT(num) \
+ ((u32)FIELD_PREP(SCMI_VIO_BASE_AGENT_MASK, (num)))
+#define SCMI_VIO_BASE_NUM_PROT_MASK GENMASK(7, 0)
+#define SCMI_VIO_BASE_NUM_PROT(num) \
+ ((u32)FIELD_PREP(SCMI_VIO_BASE_NUM_PROT_MASK, (num)))
+#define SCMI_VIO_BASE_NUM_AGENT_PROTOCOL(num) \
+ (SCMI_VIO_BASE_NUM_AGENT(SCMI_VIO_BASE_NUM_AGENTS) | \
+ SCMI_VIO_BASE_NUM_PROT(num))
+
+#define SCMI_VIO_BASE_MAX_VENDORID_STR 16
+#define SCMI_VIO_BASE_IMP_VER 1
+
+/*
+ * Number of protocols returned in single call to
+ * DISCOVER_LIST_PROTOCOLS.
+ */
+#define SCMI_VIO_BASE_PROTO_LIST_LEN 8
+
+struct scmi_vio_base_info {
+ const struct scmi_vio_protocol_h *prot_h;
+};
+
+static struct scmi_vio_base_info *scmi_vio_base_proto_info;
+
+struct scmi_vio_base_client_info {
+ const struct scmi_vio_client_h *client_h;
+ u8 *implemented_protocols;
+ int implemented_proto_num;
+};
+
+struct scmi_vio_base_version_resp {
+ __le32 status;
+ __le32 version;
+} __packed;
+
+struct scmi_vio_base_attr_resp {
+ __le32 status;
+ __le32 attributes;
+} __packed;
+
+struct scmi_vio_base_msg_attr_resp {
+ __le32 status;
+ __le32 attributes;
+} __packed;
+
+struct scmi_vio_base_imp_version_resp {
+ __le32 status;
+ __le32 imp_version;
+} __packed;
+
+struct scmi_vio_base_list_protocols_resp {
+ __le32 num_protocols;
+ u8 *protocols;
+} __packed;
+
+static int scmi_vio_base_open(
+ const struct scmi_vio_client_h *client_h)
+{
+ struct scmi_vio_base_client_info *base_client_info =
+ kzalloc(sizeof(*base_client_info),
+ GFP_KERNEL);
+ if (!base_client_info)
+ return -ENOMEM;
+ base_client_info->client_h = client_h;
+ base_client_info->implemented_protocols = kcalloc(
+ sizeof(u8), MAX_PROTOCOLS_IMP,
+ GFP_KERNEL);
+ if (!base_client_info->implemented_protocols)
+ return -ENOMEM;
+ base_client_info->implemented_proto_num = MAX_PROTOCOLS_IMP;
+ scmi_vio_implemented_protocols(
+ scmi_vio_base_proto_info->prot_h,
+ client_h, base_client_info->implemented_protocols,
+ &base_client_info->implemented_proto_num);
+ scmi_vio_set_protocol_priv(
+ scmi_vio_base_proto_info->prot_h,
+ client_h, base_client_info);
+
+ return 0;
+}
+
+static s32 scmi_vio_base_version_get(u32 *version)
+{
+ *version = SCMI_VIO_BE_PROTO_VERSION(2, 0);
+ return SCMI_VIO_BE_SUCCESS;
+}
+
+static s32 scmi_vio_base_attributes_get(int num_protocols, u32 *attributes)
+{
+ *attributes = SCMI_VIO_BASE_NUM_AGENT_PROTOCOL(num_protocols);
+ return SCMI_VIO_BE_SUCCESS;
+}
+
+static s32 scmi_vio_base_msg_attibutes_get(u32 msg_id, u32 *attributes)
+{
+ *attributes = 0;
+ switch (msg_id) {
+ case PROTOCOL_VERSION:
+ case PROTOCOL_ATTRIBUTES:
+ case PROTOCOL_MESSAGE_ATTRIBUTES:
+ case BASE_DISCOVER_VENDOR:
+ case BASE_DISCOVER_IMPLEMENT_VERSION:
+ case BASE_DISCOVER_LIST_PROTOCOLS:
+ return SCMI_VIO_BE_SUCCESS;
+ case BASE_DISCOVER_AGENT:
+ case BASE_NOTIFY_ERRORS:
+ case BASE_SET_DEVICE_PERMISSIONS:
+ case BASE_SET_PROTOCOL_PERMISSIONS:
+ case BASE_RESET_AGENT_CONFIGURATION:
+ return SCMI_VIO_BE_NOT_FOUND;
+ default:
+ return SCMI_VIO_BE_NOT_FOUND;
+ }
+}
+
+static s32 scmi_vio_base_vendor_get(char *vendor_id)
+{
+ char vendor[SCMI_VIO_BASE_MAX_VENDORID_STR] = "SCMI-VIO-BE";
+
+ strscpy(vendor_id, vendor, SCMI_VIO_BASE_MAX_VENDORID_STR);
+ return SCMI_VIO_BE_SUCCESS;
+}
+
+static s32 scmi_vio_base_imp_version_get(u32 *imp_version)
+{
+ *imp_version = SCMI_VIO_BASE_IMP_VER;
+ return SCMI_VIO_BE_SUCCESS;
+}
+
+static s32 scmi_vio_protocol_list_get(
+ const u8 *implemented_protocols, const int num_protocols_imp,
+ u32 skip, u32 *num_protocols,
+ u8 *protocols, int protocols_len)
+{
+ int i = 0;
+
+ if (skip >= num_protocols_imp) {
+ *num_protocols = 0;
+ return SCMI_VIO_BE_INVALID_PARAMETERS;
+ }
+ while (i < protocols_len && skip < num_protocols_imp) {
+ protocols[i] = implemented_protocols[skip];
+ i++;
+ skip++;
+ }
+ *num_protocols = i;
+ return SCMI_VIO_BE_SUCCESS;
+}
+
+static int debug_resp_size(
+ struct scmi_vio_be_payload_buf *resp_payload_buf,
+ u8 msg_id, size_t required_size)
+{
+ if (resp_payload_buf->payload_size < required_size) {
+ pr_err("Invalid response buffer size : %#zx required: %#zx for protocol: %#x msg: %#x\n",
+ resp_payload_buf->payload_size, required_size,
+ SCMI_PROTOCOL_BASE, msg_id);
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static int handle_protocol_ver(
+ struct scmi_vio_be_payload_buf *resp_payload_buf)
+{
+ int err;
+ s32 ret;
+ u32 version = 0;
+ size_t msg_resp_size;
+ struct scmi_vio_base_version_resp *ver_response;
+
+ msg_resp_size = sizeof(version) + sizeof(ret);
+ err = debug_resp_size(resp_payload_buf, PROTOCOL_VERSION,
+ msg_resp_size);
+ if (err)
+ return err;
+ ret = scmi_vio_base_version_get(&version);
+ ver_response = (struct scmi_vio_base_version_resp *)resp_payload_buf->payload;
+ ver_response->status = cpu_to_le32(ret);
+ ver_response->version = cpu_to_le32(version);
+ resp_payload_buf->payload_size = msg_resp_size;
+
+ return 0;
+}
+
+static int handle_protocol_attrs(int num_protocols,
+ struct scmi_vio_be_payload_buf *resp_payload_buf)
+{
+ int err;
+ s32 ret;
+ u32 attributes = 0;
+ size_t msg_resp_size;
+ struct scmi_vio_base_attr_resp *attr_response;
+
+ msg_resp_size = sizeof(attributes) + sizeof(ret);
+ err = debug_resp_size(resp_payload_buf, PROTOCOL_ATTRIBUTES,
+ msg_resp_size);
+ if (err)
+ return err;
+ ret = scmi_vio_base_attributes_get(num_protocols, &attributes);
+ attr_response = (struct scmi_vio_base_attr_resp *)resp_payload_buf->payload;
+ attr_response->status = cpu_to_le32(ret);
+ attr_response->attributes = cpu_to_le32(attributes);
+ resp_payload_buf->payload_size = msg_resp_size;
+
+ return 0;
+}
+
+static int handle_msg_attrs(
+ const struct scmi_vio_be_payload_buf *req_payload_buf,
+ struct scmi_vio_be_payload_buf *resp_payload_buf)
+{
+ int err;
+ s32 ret;
+ u32 req_msg_id = 0;
+ size_t msg_resp_size;
+ u32 msg_attributes = 0;
+ struct scmi_vio_base_msg_attr_resp *msg_attr_response;
+
+ msg_resp_size = sizeof(msg_attributes) + sizeof(ret);
+ err = debug_resp_size(
+ resp_payload_buf, PROTOCOL_MESSAGE_ATTRIBUTES,
+ msg_resp_size);
+ if (err)
+ return err;
+ if (req_payload_buf->payload_size < sizeof(req_msg_id)) {
+ pr_err("Invalid request buffer size : %#zx required: %#zx for protocol: %#x msg: %#x\n",
+ req_payload_buf->payload_size, sizeof(req_msg_id),
+ SCMI_PROTOCOL_BASE, PROTOCOL_MESSAGE_ATTRIBUTES);
+ return -EINVAL;
+ }
+ req_msg_id = le32_to_cpu(*((__le32 *)req_payload_buf->payload));
+ ret = scmi_vio_base_msg_attibutes_get(req_msg_id, &msg_attributes);
+ msg_attr_response =
+ (struct scmi_vio_base_msg_attr_resp *)resp_payload_buf->payload;
+ msg_attr_response->status = cpu_to_le32(ret);
+ msg_attr_response->attributes = cpu_to_le32(msg_attributes);
+ resp_payload_buf->payload_size = msg_resp_size;
+
+ return 0;
+}
+
+static int handle_discover_vendor(
+ struct scmi_vio_be_payload_buf *resp_payload_buf)
+{
+ int err;
+ s32 ret;
+ size_t vendor_id_sz;
+ size_t msg_resp_size;
+ void *vendor_payload;
+ char vendor[SCMI_VIO_BASE_MAX_VENDORID_STR];
+
+ msg_resp_size = (size_t)SCMI_VIO_BASE_MAX_VENDORID_STR +
+ sizeof(ret);
+ err = debug_resp_size(
+ resp_payload_buf, BASE_DISCOVER_VENDOR,
+ msg_resp_size);
+ ret = scmi_vio_base_vendor_get(vendor);
+ WARN_ON(strlen(vendor) >= SCMI_VIO_BASE_MAX_VENDORID_STR);
+ vendor_id_sz = (size_t)min_t(size_t, (size_t)(strlen(vendor) + 1),
+ (size_t)SCMI_VIO_BASE_MAX_VENDORID_STR);
+ *(__le32 *)resp_payload_buf->payload = cpu_to_le32(ret);
+ vendor_payload =
+ (void *)((unsigned long)(uintptr_t)resp_payload_buf->payload +
+ (unsigned long)sizeof(ret));
+ memcpy((u8 *)vendor_payload, vendor, vendor_id_sz);
+ resp_payload_buf->payload_size = vendor_id_sz + sizeof(ret);
+
+ return 0;
+}
+
+static int handle_discover_imp_ver(
+ struct scmi_vio_be_payload_buf *resp_payload_buf)
+{
+ int err;
+ s32 ret;
+ u32 imp_version = 0;
+ size_t msg_resp_size;
+ struct scmi_vio_base_imp_version_resp *imp_ver_response;
+
+ msg_resp_size = sizeof(imp_version) + sizeof(ret);
+ err = debug_resp_size(resp_payload_buf,
+ BASE_DISCOVER_IMPLEMENT_VERSION,
+ msg_resp_size);
+ if (err)
+ return err;
+ ret = scmi_vio_base_imp_version_get(&imp_version);
+ imp_ver_response =
+ (struct scmi_vio_base_imp_version_resp *)resp_payload_buf->payload;
+ imp_ver_response->status = cpu_to_le32(ret);
+ imp_ver_response->imp_version = cpu_to_le32(imp_version);
+ resp_payload_buf->payload_size = msg_resp_size;
+
+ return 0;
+}
+
+static int handle_discover_list_protocols(
+ const struct scmi_vio_be_payload_buf *req_payload_buf,
+ struct scmi_vio_be_payload_buf *resp_payload_buf,
+ u8 *implemented_protocols, int num_protocols_imp)
+{
+ int err;
+ s32 ret;
+ u32 num_protocols = 0;
+ size_t msg_resp_size;
+ u32 protocol_skip_count;
+ void *protocol_list_payload;
+ void *protocol_num_payload;
+ u8 proto_list[SCMI_VIO_BASE_PROTO_LIST_LEN];
+
+ msg_resp_size = sizeof(u8) * SCMI_VIO_BASE_PROTO_LIST_LEN +
+ sizeof(ret);
+ err = debug_resp_size(
+ resp_payload_buf, BASE_DISCOVER_LIST_PROTOCOLS,
+ msg_resp_size);
+ if (err)
+ return err;
+ if (req_payload_buf->payload_size < sizeof(protocol_skip_count)) {
+ pr_err("Invalid request buffer size : %#zx required: %#zx for protocol: %#x msg: %#x\n",
+ req_payload_buf->payload_size, sizeof(protocol_skip_count),
+ SCMI_PROTOCOL_BASE, BASE_DISCOVER_LIST_PROTOCOLS);
+ return -EINVAL;
+ }
+ protocol_skip_count =
+ le32_to_cpu((*(__le32 *)req_payload_buf->payload));
+ ret = scmi_vio_protocol_list_get(
+ implemented_protocols, num_protocols_imp,
+ protocol_skip_count, &num_protocols,
+ proto_list, SCMI_VIO_BASE_PROTO_LIST_LEN);
+ *(__le32 *)resp_payload_buf->payload = cpu_to_le32(ret);
+ protocol_num_payload =
+ (void *)((unsigned long)(uintptr_t)resp_payload_buf->payload +
+ (unsigned long)sizeof(ret));
+ *(__le32 *)protocol_num_payload = cpu_to_le32(num_protocols);
+ if (num_protocols > 0) {
+ pr_info("%s: num_protocols:%d\n", __func__, num_protocols);
+ protocol_list_payload =
+ (void *)((unsigned long)(uintptr_t)resp_payload_buf->payload +
+ (unsigned long)sizeof(ret) + (unsigned long)sizeof(num_protocols));
+ memcpy((u8 *)protocol_list_payload, proto_list, num_protocols);
+ }
+ resp_payload_buf->payload_size =
+ num_protocols * sizeof(u8) + sizeof(num_protocols) + sizeof(ret);
+
+ return 0;
+}
+
+static int scmi_vio_base_msg_handle(
+ const struct scmi_vio_client_h *client_h,
+ const struct scmi_vio_be_msg_hdr *msg_hdr,
+ const struct scmi_vio_be_payload_buf *req_payload_buf,
+ struct scmi_vio_be_payload_buf *resp_payload_buf)
+{
+ int err;
+ size_t msg_resp_size;
+ struct scmi_vio_base_client_info *base_client_info;
+
+ base_client_info = (struct scmi_vio_base_client_info *)
+ scmi_vio_get_protocol_priv(
+ scmi_vio_base_proto_info->prot_h,
+ client_h);
+ WARN_ON(client_h != base_client_info->client_h);
+
+ WARN_ON(msg_hdr->protocol_id != SCMI_PROTOCOL_BASE);
+ switch (msg_hdr->msg_id) {
+ case PROTOCOL_VERSION:
+ return handle_protocol_ver(resp_payload_buf);
+ case PROTOCOL_ATTRIBUTES:
+ return handle_protocol_attrs(
+ base_client_info->implemented_proto_num,
+ resp_payload_buf);
+ case PROTOCOL_MESSAGE_ATTRIBUTES:
+ return handle_msg_attrs(req_payload_buf, resp_payload_buf);
+ case BASE_DISCOVER_VENDOR:
+ return handle_discover_vendor(resp_payload_buf);
+ case BASE_DISCOVER_IMPLEMENT_VERSION:
+ return handle_discover_imp_ver(resp_payload_buf);
+ case BASE_DISCOVER_LIST_PROTOCOLS:
+ return handle_discover_list_protocols(
+ req_payload_buf, resp_payload_buf,
+ base_client_info->implemented_protocols,
+ base_client_info->implemented_proto_num);
+ default:
+ pr_err("Msg id %#x not supported for base protocol\n",
+ msg_hdr->msg_id);
+ msg_resp_size = sizeof(u32);
+ err = debug_resp_size(resp_payload_buf, msg_hdr->msg_id,
+ msg_resp_size);
+ if (err)
+ return err;
+ *(__le32 *)resp_payload_buf->payload =
+ cpu_to_le32(SCMI_VIO_BE_NOT_FOUND);
+ resp_payload_buf->payload_size = sizeof(u32);
+ break;
+ }
+
+ return 0;
+}
+
+
+static int scmi_vio_base_close(
+ const struct scmi_vio_client_h *client_h)
+{
+ struct scmi_vio_base_client_info *base_client_info;
+
+ base_client_info = (struct scmi_vio_base_client_info *)
+ scmi_vio_get_protocol_priv(
+ scmi_vio_base_proto_info->prot_h,
+ client_h);
+ WARN_ON(client_h != base_client_info->client_h);
+ kfree(base_client_info->implemented_protocols);
+ kfree(base_client_info);
+ return 0;
+}
+
+const struct scmi_vio_protocol_ops scmi_vio_base_ops = {
+ .open = scmi_vio_base_open,
+ .msg_handle = scmi_vio_base_msg_handle,
+ .close = scmi_vio_base_close,
+};
+
+int scmi_vio_base_init_fn(
+ const struct scmi_vio_protocol_h *prot_h)
+{
+ scmi_vio_base_proto_info = kzalloc(
+ sizeof(*scmi_vio_base_proto_info),
+ GFP_KERNEL);
+ if (!scmi_vio_base_proto_info)
+ return -ENOMEM;
+ scmi_vio_base_proto_info->prot_h = prot_h;
+ return 0;
+}
+
+int scmi_vio_base_exit_fn(
+ const struct scmi_vio_protocol_h *prot_h)
+{
+ kfree(scmi_vio_base_proto_info);
+ scmi_vio_base_proto_info = NULL;
+
+ return 0;
+}
+
+const struct scmi_vio_protocol scmi_vio_base_protocol = {
+ .id = SCMI_PROTOCOL_BASE,
+ .prot_init_fn = scmi_vio_base_init_fn,
+ .prot_exit_fn = scmi_vio_base_exit_fn,
+ .prot_ops = &scmi_vio_base_ops,
+};
+
+DEFINE_SCMI_VIO_PROT_REG_UNREG(base, scmi_vio_base_protocol);
new file mode 100644
@@ -0,0 +1,71 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (c) 2022 Qualcomm Innovation Center, Inc. All rights reserved.
+ */
+
+
+#include <linux/slab.h>
+#include <linux/scmi_vio_backend.h>
+
+#define to_client_info(clienth) \
+ container_of((clienth), struct scmi_vio_client_info, \
+ client_h)
+
+/*
+ * scmi_vio_client_info - Structure respreseting information
+ * associated with a client handle.
+ *
+ * @client_h: Opaque handle used as agent/client idenitifier.
+ * @priv: Private data mainted by SCMI Virtio Backend, for
+ * an agent/client.
+ * This pointer can be used by backend, to maintain,
+ * per protocol private information. This pointer is
+ * typically populated by SCMI backend, on open() call
+ * by client, by calling scmi_vio_set_client_priv().
+ */
+struct scmi_vio_client_info {
+ struct scmi_vio_client_h client_h;
+ void *priv;
+};
+
+struct scmi_vio_client_h *scmi_vio_get_client_h(
+ const void *handle)
+{
+ struct scmi_vio_client_info *client_info =
+ kzalloc(sizeof(*client_info), GFP_KERNEL);
+
+ if (!client_info)
+ return NULL;
+ client_info->client_h.handle = handle;
+ return &client_info->client_h;
+}
+EXPORT_SYMBOL_GPL(scmi_vio_get_client_h);
+
+void scmi_vio_put_client_h(
+ const struct scmi_vio_client_h *client_h)
+{
+ struct scmi_vio_client_info *client_info =
+ to_client_info(client_h);
+
+ kfree(client_info);
+}
+EXPORT_SYMBOL_GPL(scmi_vio_put_client_h);
+
+void *scmi_vio_get_client_priv(
+ const struct scmi_vio_client_h *client_h)
+{
+ struct scmi_vio_client_info *client_info =
+ to_client_info(client_h);
+
+ return client_info->priv;
+}
+
+void scmi_vio_set_client_priv(
+ const struct scmi_vio_client_h *client_h,
+ void *priv)
+{
+ struct scmi_vio_client_info *client_info =
+ to_client_info(client_h);
+
+ client_info->priv = priv;
+}
new file mode 100644
@@ -0,0 +1,24 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (c) 2022 Qualcomm Innovation Center, Inc. All rights reserved.
+ */
+#ifndef _SCMI_VIO_BE_CLIENT_HANDLE_H
+#define _SCMI_VIO_BE_CLIENT_HANDLE_H
+
+/**
+ * scmi_vio_get_client_h: This function in used by callers
+ * to construct a unique client handle, enclosing the client handle.
+ */
+struct scmi_vio_client_h *scmi_vio_get_client_h(
+ const void *handle);
+
+
+/**
+ * scmi_vio_put_client_h: This function in used by callers
+ * to release the client handle, and any private data associated
+ * with it.
+ */
+void scmi_vio_put_client_h(
+ const struct scmi_vio_client_h *client_h);
+
+#endif /*_SCMI_VIO_BE_CLIENT_HANDLE_H */
new file mode 100644
@@ -0,0 +1,53 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (c) 2022 Qualcomm Innovation Center, Inc. All rights reserved.
+ */
+
+#ifndef _SCMI_VIO_BE_COMMON_H
+#define _SCMI_VIO_BE_COMMON_H
+
+struct scmi_vio_protocol_h {
+ struct device *dev;
+};
+
+struct scmi_vio_be_payload_buf {
+ void *payload;
+ size_t payload_size;
+};
+
+struct scmi_vio_be_msg_hdr {
+ u8 msg_id;
+ u8 protocol_id;
+ u8 type;
+ u16 seq;
+};
+
+/**
+ * struct scmi_vio_protocol_ops - operations
+ * supported by SCMI Virtio backend protocol drivers.
+ *
+ * @open: Notify protocol driver, about new client.
+ * @close: Notify protocol driver, about exiting client.
+ * @msg_handle: Unparcel a request and parcel response, to be sent over
+ * client channels.
+ */
+struct scmi_vio_protocol_ops {
+ int (*open)(const struct scmi_vio_client_h *client_h);
+ int (*msg_handle)(const struct scmi_vio_client_h *client_h,
+ const struct scmi_vio_be_msg_hdr *msg_hdr,
+ const struct scmi_vio_be_payload_buf *req_payload_buf,
+ struct scmi_vio_be_payload_buf *resp_payload_buf);
+ int (*close)(const struct scmi_vio_client_h *client_h);
+};
+
+typedef int (*scmi_vio_prot_init_fn_t)(const struct scmi_vio_protocol_h *);
+typedef int (*scmi_vio_prot_exit_fn_t)(const struct scmi_vio_protocol_h *);
+
+struct scmi_vio_protocol {
+ const u8 id;
+ const scmi_vio_prot_init_fn_t prot_init_fn;
+ const scmi_vio_prot_exit_fn_t prot_exit_fn;
+ const struct scmi_vio_protocol_ops *prot_ops;
+};
+
+#endif /* _SCMI_VIO_BE_COMMON_H */
new file mode 100644
@@ -0,0 +1,31 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (c) 2021-2022 Qualcomm Innovation Center, Inc. All rights reserved.
+ */
+
+#ifndef _LINUX_SCMI_VIO_BACKEND_H
+#define _LINUX_SCMI_VIO_BACKEND_H
+
+#include <linux/idr.h>
+
+/**
+ * scmi_vio_client_h : Structure encapsulating a unique
+ * handle, identifying the client connection to SCMI
+ * Virtio backend.
+ */
+struct scmi_vio_client_h {
+ const void *handle;
+};
+
+struct scmi_vio_be_msg {
+ struct scmi_msg_payld *msg_payld;
+ size_t msg_sz;
+};
+
+int scmi_vio_be_open(const struct scmi_vio_client_h *client_h);
+int scmi_vio_be_close(const struct scmi_vio_client_h *client_h);
+int scmi_vio_be_request(const struct scmi_vio_client_h *client_h,
+ const struct scmi_vio_be_msg *req,
+ struct scmi_vio_be_msg *resp);
+
+#endif /* _LINUX_SCMI_VIO_BACKEND_H */