mirror of https://lore.kernel.org/lkml/
 help / color / mirror / Atom feed
From: Alexander Usyskin <alexander.usyskin@intel.com>
To: Arnd Bergmann <arnd@arndb.de>,
	 Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Cc: linux-kernel@vger.kernel.org,
	Menachem Adin <menachem.adin@intel.com>,
	 Andrzej Kacprowski <andrzej.kacprowski@linux.intel.com>,
	 Karol Wachowski <karol.wachowski@linux.intel.com>,
	 Alexander Usyskin <alexander.usyskin@intel.com>,
	 Reuven Abliyev <reuvenab@gmail.com>
Subject: [PATCH char-misc-next v2 4/4] issei: add driver to driver interface
Date: Sun, 18 Oct 2026 12:30:55 +0300	[thread overview]
Message-ID: <20261018-issei_pm_and_itf-v2-4-7a07452e3606@intel.com> (raw)
In-Reply-To: <20261018-issei_pm_and_itf-v2-0-7a07452e3606@intel.com>

Add new interface that enables communication with ISSEI device
from the kernel modules.
First intended consumer is an NPU driver.

Co-developed-by: Reuven Abliyev <reuvenab@gmail.com>
Signed-off-by: Reuven Abliyev <reuvenab@gmail.com>
Co-developed-by: Andrzej Kacprowski <andrzej.kacprowski@linux.intel.com>
Signed-off-by: Andrzej Kacprowski <andrzej.kacprowski@linux.intel.com>
Signed-off-by: Alexander Usyskin <alexander.usyskin@intel.com>
---
 MAINTAINERS                       |   1 +
 drivers/misc/issei/Makefile       |   1 +
 drivers/misc/issei/cdev.c         |  12 +-
 drivers/misc/issei/issei_device.c | 269 ++++++++++++++++++++++++++++++++++++++
 include/linux/issei_device.h      |  31 +++++
 5 files changed, 308 insertions(+), 6 deletions(-)

diff --git a/MAINTAINERS b/MAINTAINERS
index c548092c6a78..e16f6a0bc186 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -13506,6 +13506,7 @@ S:	Supported
 F:	Documentation/ABI/testing/sysfs-class-issei
 F:	Documentation/driver-api/issei/issei.rst
 F:	drivers/misc/issei/
+F:	include/linux/issei_device.h
 F:	include/uapi/linux/issei.h
 
 INTEL SGX
diff --git a/drivers/misc/issei/Makefile b/drivers/misc/issei/Makefile
index 8fac360c3342..b689784357f4 100644
--- a/drivers/misc/issei/Makefile
+++ b/drivers/misc/issei/Makefile
@@ -8,6 +8,7 @@ issei-objs += dma.o
 issei-objs += fw_client.o
 issei-objs += host_client.o
 issei-objs += ham.o
+issei-objs += issei_device.o
 issei-objs += main.o
 
 obj-$(CONFIG_INTEL_SSEI_HW_HECI) += issei-heci.o
diff --git a/drivers/misc/issei/cdev.c b/drivers/misc/issei/cdev.c
index b7d65e2d0813..f5a3ebbf8740 100644
--- a/drivers/misc/issei/cdev.c
+++ b/drivers/misc/issei/cdev.c
@@ -272,14 +272,14 @@ static const struct file_operations issei_fops = {
 	.poll = issei_poll,
 };
 
-static void issei_device_release(struct device *dev)
+static void issei_dev_release(struct device *dev)
 {
 	kfree(dev_get_drvdata(dev));
 }
 
-static void issei_device_init(struct issei_device *idev, struct device *parent,
-			      const struct issei_dma_length *dma_length,
-			      const struct issei_hw_ops *ops)
+static void issei_dev_init(struct issei_device *idev, struct device *parent,
+			   const struct issei_dma_length *dma_length,
+			   const struct issei_hw_ops *ops)
 {
 	idev->parent = parent;
 	idev->power_down = false;
@@ -322,7 +322,7 @@ struct issei_device *issei_register(size_t hw_size, struct device *parent,
 	if (!idev)
 		return ERR_PTR(-ENOMEM);
 
-	issei_device_init(idev, parent, dma_length, ops);
+	issei_dev_init(idev, parent, dma_length, ops);
 
 	ret = xa_alloc(&issei_minor_xa, &minor, idev, XA_LIMIT(0, ISSEI_MAX_DEVS), GFP_KERNEL);
 	if (ret < 0) {
@@ -339,7 +339,7 @@ struct issei_device *issei_register(size_t hw_size, struct device *parent,
 	idev->dev.class = issei_class;
 	idev->dev.parent = parent;
 	idev->dev.groups = issei_groups;
-	idev->dev.release = issei_device_release;
+	idev->dev.release = issei_dev_release;
 	dev_set_drvdata(&idev->dev, idev);
 
 	idev->cdev = cdev_alloc();
diff --git a/drivers/misc/issei/issei_device.c b/drivers/misc/issei/issei_device.c
new file mode 100644
index 000000000000..a399ffc39455
--- /dev/null
+++ b/drivers/misc/issei/issei_device.c
@@ -0,0 +1,269 @@
+// SPDX-License-Identifier: GPL-2.0
+/* Copyright (C) 2025-2026 Intel Corporation */
+#include <linux/cleanup.h>
+#include <linux/device.h>
+#include <linux/jiffies.h>
+#include <linux/err.h>
+#include <linux/export.h>
+#include <linux/sched/signal.h>
+#include <linux/slab.h>
+#include <linux/string.h>
+#include <linux/types.h>
+#include <linux/uuid.h>
+#include <linux/wait.h>
+
+#include <linux/issei.h>
+#include <linux/issei_device.h>
+
+#include "issei_dev.h"
+#include "host_client.h"
+
+#define ISSEI_DEVICE_CNT_TO_MS 200
+
+/**
+ * issei_device_open - open issei device context.
+ * @match: pointer to callback that should return non-zero when matching device is found
+ * @data: user data to pass to match callback
+ *
+ * Return: pointer to opaque context on success, ERR_PTR on error
+ */
+void *issei_device_open(int (*match)(struct device *, const void *), const void *data)
+{
+	struct issei_host_client *cl;
+	struct issei_device *idev;
+	struct device *dev;
+
+	dev = class_find_device(issei_class, NULL, data, match);
+	if (!dev)
+		return ERR_PTR(-ENODEV);
+
+	idev = dev_get_drvdata(dev);
+
+	cl = issei_cl_create(idev, NULL);
+	if (IS_ERR(cl))
+		put_device(dev);
+
+	return cl;
+}
+EXPORT_SYMBOL_GPL(issei_device_open);
+
+/**
+ * issei_device_release - release issei device context.
+ * @ctx: opaque context
+ */
+void issei_device_release(void *ctx)
+{
+	struct issei_host_client *cl = ctx;
+	struct issei_device *idev = cl->idev;
+
+	issei_cl_remove(cl);
+	put_device(&idev->dev);
+}
+EXPORT_SYMBOL_GPL(issei_device_release);
+
+/**
+ * issei_device_connect - connect to the firmware client.
+ * @ctx: opaque context
+ * @client_uuid: UUID of firmware client to connect
+ * @conn: pointer for client data to fill on successful connection
+ *
+ * Return: 0 on success, <0 on error
+ */
+int issei_device_connect(void *ctx, const uuid_t *client_uuid, struct issei_client *conn)
+{
+	struct issei_host_client *cl = ctx;
+	struct issei_device *idev = cl->idev;
+	long ret;
+
+	if (idev->rst_state != ISSEI_RST_STATE_DONE) {
+		ret = wait_event_interruptible_timeout(idev->wait_rst_state,
+						       idev->rst_state == ISSEI_RST_STATE_DONE,
+						       msecs_to_jiffies(ISSEI_DEVICE_CNT_TO_MS));
+		if (ret == 0) /* timeout */
+			ret = -ETIMEDOUT;
+		if (ret < 0) {
+			dev_dbg(&idev->dev, "Device is in transition ret = %ld\n", ret);
+			return ret;
+		}
+	}
+	return issei_cl_connect(cl, client_uuid, &conn->max_msg_length,
+				&conn->protocol_version, &conn->flags);
+}
+EXPORT_SYMBOL_GPL(issei_device_connect);
+
+/**
+ * issei_device_disconnect - disconnect from the firmware client.
+ * @ctx: opaque context
+ *
+ * Return: 0 on success, <0 on error
+ */
+int issei_device_disconnect(void *ctx)
+{
+	struct issei_host_client *cl = ctx;
+	struct issei_device *idev = cl->idev;
+
+	if (idev->rst_state != ISSEI_RST_STATE_DONE) {
+		dev_dbg(&idev->dev, "Device is in transition\n");
+		return -ENODEV;
+	}
+
+	return issei_cl_disconnect(cl);
+}
+EXPORT_SYMBOL_GPL(issei_device_disconnect);
+
+/**
+ * issei_device_write - send data to the firmware client.
+ * @ctx: opaque context
+ * @ubuf: data to send
+ * @length: data length
+ * @timeout: operation timeout in milliseconds
+ *
+ * Return: >=0 data length on success, <0 on error
+ */
+ssize_t issei_device_write(void *ctx, const u8 *ubuf, size_t length, unsigned long timeout)
+{
+	struct issei_host_client *cl = ctx;
+	struct issei_device *idev = cl->idev;
+	ssize_t ret;
+	long wret;
+
+	if (idev->rst_state != ISSEI_RST_STATE_DONE) {
+		dev_dbg(&idev->dev, "Device is in transition\n");
+		return -EBUSY;
+	}
+
+	if (!length)
+		return 0;
+
+	/* sanity check */
+	if (length > idev->dma.length.h2f) {
+		dev_dbg(&idev->dev, "Write is too big %zu > %zu\n", length,
+			idev->dma.length.h2f);
+		return -EFBIG;
+	}
+
+	u8 *buf __free(kfree) = kmemdup(ubuf, length, GFP_KERNEL);
+	if (!buf)
+		return -ENOMEM;
+
+	do {
+		ret = issei_cl_write(cl, buf, length);
+		if (ret < 0 && ret != -EAGAIN)
+			return ret;
+		/* buf is consumed by issei_cl_write on success */
+		if (ret >= 0)
+			retain_and_null_ptr(buf);
+		wret = wait_event_interruptible_timeout(cl->write_wait,
+							issei_cl_check_write(cl) != 1,
+							msecs_to_jiffies(timeout));
+		if (wret == 0) /* timeout */
+			wret = -ETIMEDOUT;
+		if (wret < 0) {
+			issei_cl_clean_all_wbuf(cl);
+			dev_dbg(&idev->dev, "Failed writing data ret = %ld\n", ret);
+			return wret;
+		}
+	} while (ret == -EAGAIN);
+
+	return ret;
+}
+EXPORT_SYMBOL_GPL(issei_device_write);
+
+/**
+ * issei_device_read - receive data from the firmware client.
+ * @ctx: opaque context
+ * @ubuf: buffer for data
+ * @length: buffer length
+ * @timeout: operation timeout in milliseconds
+ *
+ * Return: >=0 data length on success, <0 on error
+ */
+ssize_t issei_device_read(void *ctx, u8 *ubuf, size_t length, unsigned long timeout)
+{
+	struct issei_host_client *cl = ctx;
+	struct issei_device *idev = cl->idev;
+	u8 *data = NULL;
+	ssize_t ret;
+
+	if (idev->rst_state != ISSEI_RST_STATE_DONE) {
+		dev_dbg(&idev->dev, "Device is in transition\n");
+		return -EBUSY;
+	}
+
+	if (!length)
+		return 0;
+
+	/* sanity check */
+	if (length > idev->dma.length.f2h) {
+		dev_dbg(&idev->dev, "Read is too big %zu > %zu\n", length,
+			idev->dma.length.f2h);
+		return -EFBIG;
+	}
+
+	ret = issei_cl_read(cl, &data, length);
+	if (ret < 0) {
+		if (ret != -ENOENT)
+			return ret;
+
+		ret = wait_event_interruptible_timeout(cl->read_wait,
+						       issei_cl_check_read(cl) != 0,
+						       timeout);
+		if (ret == 0) /* timeout */
+			ret = -ETIMEDOUT;
+		if (ret < 0) {
+			dev_dbg(&idev->dev, "Failed reading data ret = %zd\n", ret);
+			return ret;
+		}
+		ret = issei_cl_read(cl, &data, length);
+		if (ret < 0)
+			return ret;
+	}
+
+	memcpy(ubuf, data, ret);
+	kfree(data);
+	return ret;
+}
+EXPORT_SYMBOL_GPL(issei_device_read);
+
+/**
+ * issei_device_dma_map - allocate DMA buffer on parent device.
+ * @ctx: opaque context
+ * @size: buffer size
+ * @daddr: pointer for buffer DMA address
+ * @vaddr: pointer for buffer virtual address
+ *
+ * Return: 0 on success, <0 on error
+ */
+int issei_device_dma_map(void *ctx, size_t size, dma_addr_t *daddr, void **vaddr)
+{
+	struct issei_host_client *cl = ctx;
+
+	return issei_cl_dma_map(cl, size, daddr, vaddr);
+}
+EXPORT_SYMBOL_GPL(issei_device_dma_map);
+
+/**
+ * issei_device_dma_unmap - deallocate DMA buffer.
+ * @ctx: opaque context
+ */
+void issei_device_dma_unmap(void *ctx)
+{
+	struct issei_host_client *cl = ctx;
+
+	issei_cl_dma_unmap(cl);
+}
+EXPORT_SYMBOL_GPL(issei_device_dma_unmap);
+
+/**
+ * issei_device_register_interface - register class interface for issei class.
+ * @intf: interface structure
+ *
+ * Return: 0 on success, <0 on error
+ */
+int issei_device_register_interface(struct class_interface *intf)
+{
+	intf->class = issei_class;
+
+	return class_interface_register(intf);
+}
+EXPORT_SYMBOL_GPL(issei_device_register_interface);
diff --git a/include/linux/issei_device.h b/include/linux/issei_device.h
new file mode 100644
index 000000000000..2775aa1c1ed8
--- /dev/null
+++ b/include/linux/issei_device.h
@@ -0,0 +1,31 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/* Copyright (C) 2025-2026 Intel Corporation */
+#ifndef _ISSEI_DEVICE_H_
+#define _ISSEI_DEVICE_H_
+
+#include <linux/issei.h>
+#include <linux/types.h>
+#include <linux/uuid.h>
+
+/* add MODULE_IMPORT_NS("INTEL_SSEI") to use these APIs */
+
+struct class_interface;
+struct device;
+
+void *issei_device_open(int (*match)(struct device *, const void *), const void *data);
+void issei_device_release(void *ctx);
+
+int issei_device_connect(void *ctx, const uuid_t *client_uuid, struct issei_client *conn);
+int issei_device_disconnect(void *ctx);
+
+ssize_t issei_device_write(void *ctx, const u8 *ubuf, size_t length, unsigned long timeout);
+ssize_t issei_device_read(void *ctx, u8 *ubuf, size_t length, unsigned long timeout);
+
+int issei_device_dma_map(void *ctx, size_t size, dma_addr_t *daddr, void **vaddr);
+void issei_device_dma_unmap(void *ctx);
+
+int issei_device_register_interface(struct class_interface *intf);
+#define issei_device_unregister_interface(intf) \
+	class_interface_unregister(intf)
+
+#endif /* _ISSEI_DEVICE_H_ */

-- 
2.53.0


  parent reply	other threads:[~2026-10-18  9:31 UTC|newest]

Thread overview: 7+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-10-18  9:30 [PATCH char-misc-next v2 0/4] issei: runtime power management, dma and in-kernel interface Alexander Usyskin
2026-10-18  9:30 ` [PATCH char-misc-next v2 1/4] issei: save power_down flag while processing event Alexander Usyskin
2026-10-18  9:30 ` [PATCH char-misc-next v2 2/4] issei: add runtime pm Alexander Usyskin
2026-10-18  9:30 ` [PATCH char-misc-next v2 3/4] issei: host_client: add dma allocation support Alexander Usyskin
2026-10-18  9:30 ` Alexander Usyskin [this message]
2026-10-19  4:17   ` [PATCH char-misc-next v2 4/4] issei: add driver to driver interface kernel test robot
2026-10-19  6:19   ` kernel test robot

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20261018-issei_pm_and_itf-v2-4-7a07452e3606@intel.com \
    --to=alexander.usyskin@intel.com \
    --cc=andrzej.kacprowski@linux.intel.com \
    --cc=arnd@arndb.de \
    --cc=gregkh@linuxfoundation.org \
    --cc=karol.wachowski@linux.intel.com \
    --cc=linux-kernel@vger.kernel.org \
    --cc=menachem.adin@intel.com \
    --cc=reuvenab@gmail.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox

all inboxes | Powered by JetHome®