summaryrefslogtreecommitdiffstats
path: root/drivers/misc/issei/ham.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/misc/issei/ham.c')
-rw-r--r--drivers/misc/issei/ham.c169
1 files changed, 169 insertions, 0 deletions
diff --git a/drivers/misc/issei/ham.c b/drivers/misc/issei/ham.c
new file mode 100644
index 000000000..674d9733d
--- /dev/null
+++ b/drivers/misc/issei/ham.c
@@ -0,0 +1,169 @@
+// SPDX-License-Identifier: GPL-2.0
+/* Copyright (C) 2023-2026 Intel Corporation */
+#include <linux/dev_printk.h>
+#include <linux/errno.h>
+#include <linux/slab.h>
+#include <linux/string.h>
+
+#include "dma.h"
+#include "fw_client.h"
+#include "ham.h"
+#include "hw_msg.h"
+#include "issei_dev.h"
+
+static int __issei_ham_send_msg(struct issei_device *idev, u32 length, void *buf)
+{
+ struct issei_dma_data data = { };
+ int ret;
+
+ data.length = length;
+ data.buf = buf;
+ ret = issei_dma_write(idev, &data);
+ if (ret)
+ return ret;
+ return idev->ops->irq_write_generate(idev);
+}
+
+/**
+ * issei_ham_send_start_req - send start request to firmware
+ * @idev: issei device object
+ *
+ * Return: 0 on success, <0 on failures
+ */
+int issei_ham_send_start_req(struct issei_device *idev)
+{
+ struct ham_start_message_req req;
+
+ req.header.cmd = HAM_BUS_CMD_START_REQ;
+ req.supported_version = ISSEI_SUPPORTED_PROTOCOL_VER;
+ req.heci_capabilities_length = 0;
+
+ return __issei_ham_send_msg(idev, sizeof(req), &req);
+}
+
+/**
+ * issei_ham_send_clients_req - send clients request to firmware
+ * @idev: issei device object
+ *
+ * Return: 0 on success, <0 on failures
+ */
+int issei_ham_send_clients_req(struct issei_device *idev)
+{
+ struct ham_get_clients_req req;
+
+ req.header.cmd = HAM_BUS_CMD_CLIENT_REQ;
+
+ return __issei_ham_send_msg(idev, sizeof(req), &req);
+}
+
+static int issei_ham_start_rsp(struct issei_device *idev, const u8 *buf, size_t length)
+{
+ struct ham_start_message_res *res = (struct ham_start_message_res *)buf;
+ int ret;
+
+ if (idev->rst_state != ISSEI_RST_STATE_START) {
+ dev_err(&idev->dev, "Wrong state %d != %d\n",
+ idev->rst_state, ISSEI_RST_STATE_START);
+ return -EPROTO;
+ }
+
+ if (length < sizeof(*res)) {
+ dev_err(&idev->dev, "Small start response size %zu < %zu\n",
+ length, sizeof(*res));
+ return -EPROTO;
+ }
+
+ if (length - sizeof(*res) != res->heci_capabilities_length) {
+ dev_err(&idev->dev, "Wrong start response size %zu != %u\n",
+ length - sizeof(*res), res->heci_capabilities_length);
+ return -EPROTO;
+ }
+
+ memcpy(idev->fw_version, res->fw_version, sizeof(idev->fw_version));
+ idev->fw_protocol_ver = res->supported_version;
+ dev_dbg(&idev->dev, "FW protocol: %u FW version %u.%u.%u.%u", idev->fw_protocol_ver,
+ idev->fw_version[0], idev->fw_version[1],
+ idev->fw_version[2], idev->fw_version[3]);
+
+ ret = issei_ham_send_clients_req(idev);
+ if (ret == -EBUSY)
+ ret = 0;
+
+ return ret;
+}
+
+static int issei_ham_client_rsp(struct issei_device *idev, const u8 *buf, size_t length)
+{
+ struct ham_get_clients_res *res = (struct ham_get_clients_res *)buf;
+ struct ham_client_properties *client;
+
+ if (idev->rst_state != ISSEI_RST_STATE_CLIENT_ENUM) {
+ dev_err(&idev->dev, "Wrong state %d != %d\n",
+ idev->rst_state, ISSEI_RST_STATE_CLIENT_ENUM);
+ return -EPROTO;
+ }
+
+ if (length < sizeof(*res)) {
+ dev_err(&idev->dev, "Small response size %zu < %zu\n", length, sizeof(*res));
+ return -EPROTO;
+ }
+
+ if (length - sizeof(*res) != res->client_count * sizeof(struct ham_client_properties)) {
+ dev_err(&idev->dev, "Wrong response size %zu < %zu\n",
+ length - sizeof(*res),
+ res->client_count * sizeof(struct ham_client_properties));
+ return -EPROTO;
+ }
+
+ guard(mutex)(&idev->client_lock);
+
+ for (size_t i = 0; i < res->client_count; i++) {
+ client = &res->clients_props[i];
+ dev_dbg(&idev->dev, "client: id = %u ver = %u uuid = %pUb mtu = %u flags = %u",
+ client->client_number, client->protocol_ver, &client->client_uuid,
+ client->client_mtu, client->flags);
+ issei_fw_cl_create(idev, client->client_number, client->protocol_ver,
+ &client->client_uuid, client->client_mtu, client->flags);
+ }
+ return 0;
+}
+
+static int __issei_ham_process_ham_rsp(struct issei_device *idev, const u8 *buf, size_t length)
+{
+ struct ham_bus_message *hdr = (struct ham_bus_message *)buf;
+
+ if (length < sizeof(*hdr)) {
+ dev_err(&idev->dev, "Small bus message size %zu < %zu\n",
+ length, sizeof(*hdr));
+ return -EPROTO;
+ }
+
+ switch (hdr->cmd) {
+ case HAM_BUS_CMD_START_RSP:
+ return issei_ham_start_rsp(idev, buf, length);
+
+ case HAM_BUS_CMD_CLIENT_RSP:
+ return issei_ham_client_rsp(idev, buf, length);
+
+ default:
+ dev_err(&idev->dev, "Unexpected command 0x%x", hdr->cmd);
+ return -EPROTO;
+ }
+}
+
+/**
+ * issei_ham_process_ham_rsp - process response from firmware and release buffer
+ * @idev: issei device object
+ * @buf: response buffer
+ * @length: response buffer length
+ *
+ * Return: 0 on success, <0 on failures
+ */
+int issei_ham_process_ham_rsp(struct issei_device *idev, const u8 *buf, size_t length)
+{
+ int ret;
+
+ ret = __issei_ham_process_ham_rsp(idev, buf, length);
+ kfree(buf);
+ return ret;
+}