mirror of https://lore.kernel.org/lkml/
 help / color / mirror / Atom feed
* [PATCH char-misc-next 0/4] issei: runtime power management, dma and in-kernel interface
@ 2026-10-15  5:28 Alexander Usyskin
  2026-10-15  5:28 ` [PATCH char-misc-next 1/4] issei: save power_down flag while processing event Alexander Usyskin
                   ` (3 more replies)
  0 siblings, 4 replies; 8+ messages in thread
From: Alexander Usyskin @ 2026-10-15  5:28 UTC (permalink / raw)
  To: Arnd Bergmann, Greg Kroah-Hartman
  Cc: linux-kernel, Menachem Adin, Andrzej Kacprowski, Karol Wachowski,
	Alexander Usyskin, Reuven Abliyev

Pack of additions for issei driver:

1. Support for runtime power management
2. Support for DMA allocation on request from client
3. In-kernel interface that allows kernel drivers to communicate directly
   with issei.
4. Fix for power down flow

Signed-off-by: Alexander Usyskin <alexander.usyskin@intel.com>
---
Alexander Usyskin (4):
      issei: save power_down flag while processing event
      issei: add runtime pm
      issei: host_client: add dma allocation support
      issei: add driver to driver interface

 MAINTAINERS                       |   1 +
 drivers/misc/issei/Makefile       |   1 +
 drivers/misc/issei/cdev.c         |  12 +-
 drivers/misc/issei/host_client.c  |  70 ++++++++++
 drivers/misc/issei/host_client.h  |  12 ++
 drivers/misc/issei/hw_heci.c      |  21 ++-
 drivers/misc/issei/issei_dev.h    |   3 +
 drivers/misc/issei/issei_device.c | 268 ++++++++++++++++++++++++++++++++++++++
 drivers/misc/issei/main.c         | 233 ++++++++++++++++++---------------
 drivers/misc/issei/pci_heci.c     |  21 +++
 include/linux/issei_device.h      |  84 ++++++++++++
 11 files changed, 612 insertions(+), 114 deletions(-)
---
base-commit: 79e5797d243a466138f641932f81dc69f796060c
change-id: 20261015-issei_pm_and_itf-d1911cd73ec0

Best regards,
-- 
Alexander Usyskin <alexander.usyskin@intel.com>


^ permalink raw reply	[flat|nested] 8+ messages in thread

* [PATCH char-misc-next 1/4] issei: save power_down flag while processing event
  2026-10-15  5:28 [PATCH char-misc-next 0/4] issei: runtime power management, dma and in-kernel interface Alexander Usyskin
@ 2026-10-15  5:28 ` Alexander Usyskin
  2026-10-15  5:28 ` [PATCH char-misc-next 2/4] issei: add runtime pm Alexander Usyskin
                   ` (2 subsequent siblings)
  3 siblings, 0 replies; 8+ messages in thread
From: Alexander Usyskin @ 2026-10-15  5:28 UTC (permalink / raw)
  To: Arnd Bergmann, Greg Kroah-Hartman
  Cc: linux-kernel, Menachem Adin, Andrzej Kacprowski, Karol Wachowski,
	Alexander Usyskin

Change in power_down flag may confuse event thread.
Save flag status at the first available moment and
work according to saved value.

Signed-off-by: Alexander Usyskin <alexander.usyskin@intel.com>
---
 drivers/misc/issei/main.c | 22 ++++++++++++----------
 1 file changed, 12 insertions(+), 10 deletions(-)

diff --git a/drivers/misc/issei/main.c b/drivers/misc/issei/main.c
index 5987fb340250..8aaf2d47392b 100644
--- a/drivers/misc/issei/main.c
+++ b/drivers/misc/issei/main.c
@@ -24,7 +24,7 @@ static void issei_rst_state_set(struct issei_device *idev, enum issei_rst_state
 		wake_up(&idev->wait_rst_state);
 }
 
-static int issei_reset(struct issei_device *idev)
+static int issei_reset(struct issei_device *idev, bool power_down)
 {
 	int ret;
 
@@ -34,14 +34,14 @@ static int issei_reset(struct issei_device *idev)
 	issei_fw_cl_remove_all(idev);
 	/* No need to check for overflow here, the counter is used only for info */
 	idev->all_reset_count++;
-	ret = idev->ops->hw_reset(idev, !idev->power_down);
+	ret = idev->ops->hw_reset(idev, !power_down);
 	issei_dmam_setup(idev);
 	if (ret) {
 		dev_err(&idev->dev, "hw_reset failed ret = %d\n", ret);
 		return ret;
 	}
 
-	if (idev->power_down) {
+	if (power_down) {
 		dev_dbg(&idev->dev, "powering down: end of reset\n");
 		issei_rst_state_set(idev, ISSEI_RST_STATE_DISABLED);
 		return -ENODEV;
@@ -87,24 +87,26 @@ static int issei_process_thread(void *_dev)
 {
 	long timeout, old_timeout = MAX_SCHEDULE_TIMEOUT;
 	struct issei_device *idev = _dev;
+	bool power_down;
 	int ret;
 
 	while (!kthread_should_stop()) {
 		dev_dbg(&idev->dev, "process_work in %d\n", idev->rst_state);
+		WRITE_ONCE(idev->has_data, false);
+		power_down = idev->power_down; /* to save state */
 		if (!idev->ops->hw_is_ready(idev) && idev->rst_state > ISSEI_RST_STATE_HW_READY) {
-			if (!idev->power_down)
+			if (!power_down)
 				dev_dbg(&idev->dev, "HW not ready, resetting\n");
 			idev->rst_state = ISSEI_RST_STATE_INIT;
 		}
-		if (idev->power_down)
+		if (power_down)
 			idev->rst_state = ISSEI_RST_STATE_INIT;
-		WRITE_ONCE(idev->has_data, false);
 		dev_dbg(&idev->dev, "reset_step in %d\n", idev->rst_state);
 		timeout = MAX_SCHEDULE_TIMEOUT;
 		ret = 0;
 		switch (idev->rst_state) {
 		case ISSEI_RST_STATE_DISABLED:
-			if (idev->power_down) {
+			if (power_down) {
 				dev_dbg(&idev->dev, "Interrupt in power down?\n");
 				break;
 			}
@@ -115,7 +117,7 @@ static int issei_process_thread(void *_dev)
 			idev->ops->irq_clear(idev);
 			idev->ops->irq_sync(idev);
 
-			if (!idev->power_down) {
+			if (!power_down) {
 				idev->reset_count++;
 				if (idev->reset_count > ISSEI_MAX_CONSEC_RESET) {
 					dev_err(&idev->dev, "reset: reached maximal consecutive resets: disabling the device\n");
@@ -124,8 +126,8 @@ static int issei_process_thread(void *_dev)
 				}
 			}
 
-			ret = issei_reset(idev);
-			if (idev->power_down) {
+			ret = issei_reset(idev, power_down);
+			if (power_down) {
 				dev_dbg(&idev->dev, "Powering down\n");
 				return 0;
 			}

-- 
2.53.0


^ permalink raw reply	[flat|nested] 8+ messages in thread

* [PATCH char-misc-next 2/4] issei: add runtime pm
  2026-10-15  5:28 [PATCH char-misc-next 0/4] issei: runtime power management, dma and in-kernel interface Alexander Usyskin
  2026-10-15  5:28 ` [PATCH char-misc-next 1/4] issei: save power_down flag while processing event Alexander Usyskin
@ 2026-10-15  5:28 ` Alexander Usyskin
  2026-10-15  5:28 ` [PATCH char-misc-next 3/4] issei: host_client: add dma allocation support Alexander Usyskin
  2026-10-15  5:28 ` [PATCH char-misc-next 4/4] issei: add driver to driver interface Alexander Usyskin
  3 siblings, 0 replies; 8+ messages in thread
From: Alexander Usyskin @ 2026-10-15  5:28 UTC (permalink / raw)
  To: Arnd Bergmann, Greg Kroah-Hartman
  Cc: linux-kernel, Menachem Adin, Andrzej Kacprowski, Karol Wachowski,
	Alexander Usyskin

Add support for runtime power management.
ISSE communication is based on send-receive transaction.
Allow autosuspend with 60 seconds grace as firmware required
to answer inside this timeframe.

Signed-off-by: Alexander Usyskin <alexander.usyskin@intel.com>
---
 drivers/misc/issei/host_client.c |   4 +
 drivers/misc/issei/hw_heci.c     |  21 +++-
 drivers/misc/issei/main.c        | 229 +++++++++++++++++++++------------------
 drivers/misc/issei/pci_heci.c    |  21 ++++
 4 files changed, 168 insertions(+), 107 deletions(-)

diff --git a/drivers/misc/issei/host_client.c b/drivers/misc/issei/host_client.c
index 8f36d1c319ec..d16b5835c619 100644
--- a/drivers/misc/issei/host_client.c
+++ b/drivers/misc/issei/host_client.c
@@ -7,6 +7,7 @@
 #include <linux/kobject.h>
 #include <linux/list.h>
 #include <linux/overflow.h>
+#include <linux/pm_runtime.h>
 #include <linux/slab.h>
 #include <linux/types.h>
 #include <linux/wait.h>
@@ -73,6 +74,7 @@ static void __issei_cl_clean_all_wbuf(struct issei_device *idev, struct issei_ho
 		}
 	}
 	cl->write_in_progress = false;
+	pm_runtime_put_autosuspend(idev->parent);
 	/* synchronized under host client mutex */
 	if (waitqueue_active(&cl->write_wait))
 		wake_up_interruptible(&cl->write_wait);
@@ -309,6 +311,7 @@ ssize_t issei_cl_write(struct issei_host_client *cl, const u8 *buf, size_t buf_s
 	list_add_tail(&wbuf->list, &idev->write_queue);
 	cl->write_in_progress = true;
 	cl_dbg(idev, cl, "Write queued %zu bytes\n", buf_size);
+	pm_runtime_get(idev->parent);
 
 	issei_poke_process_thread(idev);
 
@@ -353,6 +356,7 @@ int issei_cl_write_from_queue(struct issei_device *idev)
 	if (ret >= 0)
 		idev->ops->irq_write_generate(idev);
 	cl->write_in_progress = false;
+	pm_runtime_put_autosuspend(idev->parent);
 	/* synchronized under host client mutex */
 	if (waitqueue_active(&cl->write_wait))
 		wake_up_interruptible(&cl->write_wait);
diff --git a/drivers/misc/issei/hw_heci.c b/drivers/misc/issei/hw_heci.c
index 501774aa95ff..1e39b285d4db 100644
--- a/drivers/misc/issei/hw_heci.c
+++ b/drivers/misc/issei/hw_heci.c
@@ -8,6 +8,7 @@
 #include <linux/err.h>
 #include <linux/io.h>
 #include <linux/pci.h>
+#include <linux/pm_runtime.h>
 #include <linux/sizes.h>
 #include <linux/spinlock.h>
 #include <linux/types.h>
@@ -501,12 +502,20 @@ irqreturn_t issei_heci_irq_quick_handler(int irq, void *dev_id)
 {
 	struct issei_device *idev = dev_id;
 	struct issei_heci_hw *hw = to_heci_hw(idev);
+	irqreturn_t ret;
+	int active;
 	u32 reg;
 
-	reg = heci_hcsr_read(idev);
-	if (!heci_irq_src(reg))
+	active = pm_runtime_get_if_active(idev->parent);
+	if (!active)
 		return IRQ_NONE;
 
+	reg = heci_hcsr_read(idev);
+	if (!heci_irq_src(reg)) {
+		ret = IRQ_NONE;
+		goto out;
+	}
+
 	scoped_guard(spinlock_irqsave, &hw->access_lock) {
 		reg = heci_hcsr_read(idev);
 		heci_irq_clear(idev, reg);
@@ -519,7 +528,13 @@ irqreturn_t issei_heci_irq_quick_handler(int irq, void *dev_id)
 
 	issei_poke_process_thread(idev);
 
-	return IRQ_HANDLED;
+	ret = IRQ_HANDLED;
+
+out:
+	if (active > 0)
+		pm_runtime_put_autosuspend(idev->parent);
+
+	return ret;
 }
 
 static const struct hw_heci_cfg hw_heci_pch_cfg = {
diff --git a/drivers/misc/issei/main.c b/drivers/misc/issei/main.c
index 8aaf2d47392b..c0b8606b17b3 100644
--- a/drivers/misc/issei/main.c
+++ b/drivers/misc/issei/main.c
@@ -7,6 +7,7 @@
 #include <linux/export.h>
 #include <linux/jiffies.h>
 #include <linux/kthread.h>
+#include <linux/pm_runtime.h>
 #include <linux/slab.h>
 #include <linux/wait.h>
 
@@ -83,132 +84,152 @@ static int issei_process_write_msg(struct issei_device *idev)
 	return issei_cl_write_from_queue(idev);
 }
 
-static int issei_process_thread(void *_dev)
+static bool issei_process_thread_worker(struct issei_device *idev, bool power_down,
+					long *timeout, long *old_timeout)
 {
-	long timeout, old_timeout = MAX_SCHEDULE_TIMEOUT;
-	struct issei_device *idev = _dev;
-	bool power_down;
 	int ret;
 
-	while (!kthread_should_stop()) {
-		dev_dbg(&idev->dev, "process_work in %d\n", idev->rst_state);
-		WRITE_ONCE(idev->has_data, false);
-		power_down = idev->power_down; /* to save state */
-		if (!idev->ops->hw_is_ready(idev) && idev->rst_state > ISSEI_RST_STATE_HW_READY) {
-			if (!power_down)
-				dev_dbg(&idev->dev, "HW not ready, resetting\n");
-			idev->rst_state = ISSEI_RST_STATE_INIT;
+	PM_RUNTIME_ACQUIRE_AUTOSUSPEND(idev->parent, pm);
+	ret = PM_RUNTIME_ACQUIRE_ERR(&pm);
+	if (ret) {
+		dev_err(&idev->dev, "Failed to acquire pm %d\n", ret);
+		idev->rst_state = ISSEI_RST_STATE_INIT;
+		return false;
+	}
+
+	if (!idev->ops->hw_is_ready(idev) && idev->rst_state > ISSEI_RST_STATE_HW_READY) {
+		if (!power_down)
+			dev_dbg(&idev->dev, "HW not ready, resetting\n");
+		idev->rst_state = ISSEI_RST_STATE_INIT;
+	}
+	if (power_down)
+		idev->rst_state = ISSEI_RST_STATE_INIT;
+	dev_dbg(&idev->dev, "reset_step in %d\n", idev->rst_state);
+	*timeout = MAX_SCHEDULE_TIMEOUT;
+	ret = 0;
+	switch (idev->rst_state) {
+	case ISSEI_RST_STATE_DISABLED:
+		if (power_down) {
+			dev_dbg(&idev->dev, "Interrupt in power down?\n");
+			break;
 		}
-		if (power_down)
-			idev->rst_state = ISSEI_RST_STATE_INIT;
-		dev_dbg(&idev->dev, "reset_step in %d\n", idev->rst_state);
-		timeout = MAX_SCHEDULE_TIMEOUT;
-		ret = 0;
-		switch (idev->rst_state) {
-		case ISSEI_RST_STATE_DISABLED:
-			if (power_down) {
-				dev_dbg(&idev->dev, "Interrupt in power down?\n");
+		idev->rst_state = ISSEI_RST_STATE_INIT;
+		idev->reset_count = 0;
+		fallthrough;
+
+	case ISSEI_RST_STATE_INIT:
+		idev->ops->irq_clear(idev);
+		idev->ops->irq_sync(idev);
+
+		if (!power_down) {
+			if (!idev->reset_count)
+				pm_runtime_get_noresume(idev->parent);
+			idev->reset_count++;
+			if (idev->reset_count > ISSEI_MAX_CONSEC_RESET) {
+				dev_err(&idev->dev, "reset: reached maximal consecutive resets: disabling the device\n");
+				issei_rst_state_set(idev, ISSEI_RST_STATE_DISABLED);
+				pm_runtime_put_noidle(idev->parent);
 				break;
 			}
-			idev->rst_state = ISSEI_RST_STATE_INIT;
-			fallthrough;
-
-		case ISSEI_RST_STATE_INIT:
-			idev->ops->irq_clear(idev);
-			idev->ops->irq_sync(idev);
-
-			if (!power_down) {
-				idev->reset_count++;
-				if (idev->reset_count > ISSEI_MAX_CONSEC_RESET) {
-					dev_err(&idev->dev, "reset: reached maximal consecutive resets: disabling the device\n");
-					issei_rst_state_set(idev, ISSEI_RST_STATE_DISABLED);
-					break;
-				}
-			}
-
-			ret = issei_reset(idev, power_down);
-			if (power_down) {
-				dev_dbg(&idev->dev, "Powering down\n");
-				return 0;
-			}
-			if (ret)
-				break;
+		}
 
-			idev->rst_state = ISSEI_RST_STATE_HW_READY;
-			timeout = msecs_to_jiffies(ISSEI_RST_HW_READY_TIMEOUT_MSEC);
+		ret = issei_reset(idev, power_down);
+		if (power_down) {
+			dev_dbg(&idev->dev, "Powering down\n");
+			return true;
+		}
+		if (ret)
 			break;
 
-		case ISSEI_RST_STATE_HW_READY:
-			if (!idev->ops->hw_is_ready(idev)) {
-				dev_dbg(&idev->dev, "HW is not ready?\n");
-				timeout = old_timeout;
-				break;
-			}
-
-			dev_dbg(&idev->dev, "HW is ready\n");
-			idev->ops->hw_reset_release(idev);
-			idev->ops->host_set_ready(idev);
-			ret = idev->ops->setup_message_send(idev);
-			if (ret)
-				break;
+		idev->rst_state = ISSEI_RST_STATE_HW_READY;
+		*timeout = msecs_to_jiffies(ISSEI_RST_HW_READY_TIMEOUT_MSEC);
+		break;
 
-			idev->rst_state = ISSEI_RST_STATE_SETUP;
-			timeout = msecs_to_jiffies(ISSEI_RST_STEP_TIMEOUT_MSEC);
+	case ISSEI_RST_STATE_HW_READY:
+		if (!idev->ops->hw_is_ready(idev)) {
+			dev_dbg(&idev->dev, "HW is not ready?\n");
+			*timeout = *old_timeout;
 			break;
+		}
 
-		case ISSEI_RST_STATE_SETUP:
-			ret = idev->ops->setup_message_recv(idev);
-			if (ret) {
-				if (ret == -ENODATA) {
-					ret = 0;
-					timeout = old_timeout;
-				}
-			} else {
-				timeout = msecs_to_jiffies(ISSEI_RST_STEP_TIMEOUT_MSEC);
-				ret = issei_ham_send_start_req(idev);
-				idev->rst_state = ISSEI_RST_STATE_START;
-			}
+		dev_dbg(&idev->dev, "HW is ready\n");
+		idev->ops->hw_reset_release(idev);
+		idev->ops->host_set_ready(idev);
+		ret = idev->ops->setup_message_send(idev);
+		if (ret)
 			break;
 
-		case ISSEI_RST_STATE_START:
-			ret = issei_process_read_msg(idev);
-			if (!ret) {
-				timeout = msecs_to_jiffies(ISSEI_RST_STEP_TIMEOUT_MSEC);
-				idev->rst_state = ISSEI_RST_STATE_CLIENT_ENUM;
-			} else if (ret == -ENODATA) {
+		idev->rst_state = ISSEI_RST_STATE_SETUP;
+		*timeout = msecs_to_jiffies(ISSEI_RST_STEP_TIMEOUT_MSEC);
+		break;
+
+	case ISSEI_RST_STATE_SETUP:
+		ret = idev->ops->setup_message_recv(idev);
+		if (ret) {
+			if (ret == -ENODATA) {
 				ret = 0;
-				timeout = old_timeout;
+				*timeout = *old_timeout;
 			}
-			break;
+		} else {
+			*timeout = msecs_to_jiffies(ISSEI_RST_STEP_TIMEOUT_MSEC);
+			ret = issei_ham_send_start_req(idev);
+			idev->rst_state = ISSEI_RST_STATE_START;
+		}
+		break;
+
+	case ISSEI_RST_STATE_START:
+		ret = issei_process_read_msg(idev);
+		if (!ret) {
+			*timeout = msecs_to_jiffies(ISSEI_RST_STEP_TIMEOUT_MSEC);
+			idev->rst_state = ISSEI_RST_STATE_CLIENT_ENUM;
+		} else if (ret == -ENODATA) {
+			ret = 0;
+			*timeout = *old_timeout;
+		}
+		break;
 
-		case ISSEI_RST_STATE_CLIENT_ENUM:
-			ret = issei_process_read_msg(idev);
-			if (ret) {
-				if (ret == -ENODATA) {
-					ret = 0;
-					timeout = old_timeout;
-				}
-			} else {
-				idev->reset_count = 0;
-				idev->rst_state = ISSEI_RST_STATE_DONE;
-				dev_dbg(&idev->dev, "Reset finished successfully\n");
+	case ISSEI_RST_STATE_CLIENT_ENUM:
+		ret = issei_process_read_msg(idev);
+		if (ret) {
+			if (ret == -ENODATA) {
+				ret = 0;
+				*timeout = *old_timeout;
 			}
+		} else {
+			idev->reset_count = 0;
+			issei_rst_state_set(idev, ISSEI_RST_STATE_DONE);
+			pm_runtime_put_autosuspend(idev->parent);
+			dev_dbg(&idev->dev, "Reset finished successfully\n");
+		}
+		break;
+
+	case ISSEI_RST_STATE_DONE:
+		ret = issei_process_read_msg(idev);
+		if (ret != 0 && ret != -ENODATA)
 			break;
 
-		case ISSEI_RST_STATE_DONE:
-			ret = issei_process_read_msg(idev);
-			if (ret != 0 && ret != -ENODATA)
-				break;
+		ret = issei_process_write_msg(idev);
+		break;
+	}
 
-			ret = issei_process_write_msg(idev);
-			break;
-		}
+	if (ret) {
+		dev_warn(&idev->dev, "Process failed ret = %d\n", ret);
+		idev->rst_state = ISSEI_RST_STATE_INIT;
+	}
+	return false;
+}
 
-		if (ret) {
-			dev_warn(&idev->dev, "Process failed ret = %d\n", ret);
-			idev->rst_state = ISSEI_RST_STATE_INIT;
-			continue;
-		}
+static int issei_process_thread(void *_dev)
+{
+	long timeout, old_timeout = MAX_SCHEDULE_TIMEOUT;
+	struct issei_device *idev = _dev;
+
+	while (!kthread_should_stop()) {
+		dev_dbg(&idev->dev, "process_work in %d\n", idev->rst_state);
+		WRITE_ONCE(idev->has_data, false);
+
+		if (issei_process_thread_worker(idev, idev->power_down, &timeout, &old_timeout))
+			return 0;
 
 		/*
 		 * Every thread that has data to process sets the 'has_data' flag and
diff --git a/drivers/misc/issei/pci_heci.c b/drivers/misc/issei/pci_heci.c
index 86c567a4bf0f..c58b51b3aad3 100644
--- a/drivers/misc/issei/pci_heci.c
+++ b/drivers/misc/issei/pci_heci.c
@@ -8,11 +8,15 @@
 #include <linux/module.h>
 #include <linux/pci.h>
 #include <linux/pm.h>
+#include <linux/pm_runtime.h>
+#include <linux/time.h>
 
 #include "cdev.h"
 #include "hw_heci.h"
 #include "hw_heci_regs.h"
 
+#define HW_HECI_RPM_TIMEOUT_MSEC (60 * MSEC_PER_SEC)
+
 static int issei_heci_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
 {
 	struct device *dev = &pdev->dev;
@@ -70,6 +74,18 @@ static int issei_heci_probe(struct pci_dev *pdev, const struct pci_device_id *en
 		goto free_irq;
 	}
 
+	pm_runtime_use_autosuspend(dev);
+	pm_runtime_set_autosuspend_delay(dev, HW_HECI_RPM_TIMEOUT_MSEC);
+
+	/*
+	 * ISSEI requires to resume from runtime suspend mode
+	 * in order to perform link reset flow upon system suspend.
+	 */
+	dev_pm_set_driver_flags(dev, DPM_FLAG_NO_DIRECT_COMPLETE);
+
+	pm_runtime_put_noidle(dev);
+	pm_runtime_allow(dev);
+
 	return 0;
 
 free_irq:
@@ -86,6 +102,11 @@ static void issei_heci_shutdown(struct pci_dev *pdev)
 {
 	struct issei_device *idev = pci_get_drvdata(pdev);
 	struct issei_heci_hw *hw = to_heci_hw(idev);
+	struct device *dev = &pdev->dev;
+
+	pm_runtime_dont_use_autosuspend(dev);
+	pm_runtime_forbid(dev);
+	pm_runtime_get_noresume(dev);
 
 	issei_stop(idev);
 

-- 
2.53.0


^ permalink raw reply	[flat|nested] 8+ messages in thread

* [PATCH char-misc-next 3/4] issei: host_client: add dma allocation support
  2026-10-15  5:28 [PATCH char-misc-next 0/4] issei: runtime power management, dma and in-kernel interface Alexander Usyskin
  2026-10-15  5:28 ` [PATCH char-misc-next 1/4] issei: save power_down flag while processing event Alexander Usyskin
  2026-10-15  5:28 ` [PATCH char-misc-next 2/4] issei: add runtime pm Alexander Usyskin
@ 2026-10-15  5:28 ` Alexander Usyskin
  2026-10-15  5:28 ` [PATCH char-misc-next 4/4] issei: add driver to driver interface Alexander Usyskin
  3 siblings, 0 replies; 8+ messages in thread
From: Alexander Usyskin @ 2026-10-15  5:28 UTC (permalink / raw)
  To: Arnd Bergmann, Greg Kroah-Hartman
  Cc: linux-kernel, Menachem Adin, Andrzej Kacprowski, Karol Wachowski,
	Alexander Usyskin

Add functions to allow dedicated DMA buffer allocated on current
hardware device per host client.

Signed-off-by: Alexander Usyskin <alexander.usyskin@intel.com>
---
 drivers/misc/issei/host_client.c | 66 ++++++++++++++++++++++++++++++++++++++++
 drivers/misc/issei/host_client.h | 12 ++++++++
 drivers/misc/issei/issei_dev.h   |  3 ++
 3 files changed, 81 insertions(+)

diff --git a/drivers/misc/issei/host_client.c b/drivers/misc/issei/host_client.c
index d16b5835c619..751296e3dc25 100644
--- a/drivers/misc/issei/host_client.c
+++ b/drivers/misc/issei/host_client.c
@@ -2,6 +2,7 @@
 /* Copyright (C) 2023-2026 Intel Corporation */
 #include <linux/cleanup.h>
 #include <linux/dev_printk.h>
+#include <linux/dma-mapping.h>
 #include <linux/err.h>
 #include <linux/fs.h>
 #include <linux/kobject.h>
@@ -112,6 +113,17 @@ static void __issei_cl_disconnect(struct issei_device *idev, struct issei_host_c
 	cl_dbg(idev, cl, "Disconnected\n");
 }
 
+static void __issei_cl_dma_unmap(struct issei_host_client *cl)
+{
+	if (!cl->dma_size)
+		return;
+
+	dma_free_coherent(cl->idev->parent, cl->dma_size, cl->dma_vaddr, cl->dma_daddr);
+	cl->dma_size = 0;
+	cl->dma_vaddr = NULL;
+	cl->dma_daddr = 0;
+}
+
 static void __issei_cl_init(struct issei_host_client *cl, struct issei_device *idev,
 			    u16 id, struct file *fp)
 {
@@ -185,6 +197,8 @@ void issei_cl_remove(struct issei_host_client *cl)
 
 	__issei_cl_disconnect(idev, cl);
 
+	__issei_cl_dma_unmap(cl);
+
 	cl_dbg(idev, cl, "Removed\n");
 	kfree(cl);
 }
@@ -521,3 +535,55 @@ void issei_cl_clean_all_wbuf(struct issei_host_client *cl)
 
 	__issei_cl_clean_all_wbuf(idev, cl);
 }
+
+/**
+ * issei_cl_dma_map - allocate DMA mapping for client
+ *
+ * @cl: host client
+ * @size: memory size to allocate
+ * @daddr: pointer for buffer DMA address
+ * @vaddr: pointer for buffer virtual address
+ *
+ * Return: 0 on success, < 0 on error
+ */
+int issei_cl_dma_map(struct issei_host_client *cl, size_t size,
+		     dma_addr_t *daddr, void **vaddr)
+{
+	struct issei_device *idev = cl->idev;
+
+	if (size == 0 || size > ISSEI_HOST_DMA_MAX_SIZE) {
+		cl_err(idev, cl, "The size is out of bounds.\n");
+		return -EINVAL;
+	}
+
+	guard(mutex)(&idev->client_lock);
+
+	if (cl->dma_size) {
+		cl_err(idev, cl, "DMA already allocated.\n");
+		return -EALREADY;
+	}
+
+	cl->dma_vaddr = dma_alloc_coherent(cl->idev->parent, size, &cl->dma_daddr, GFP_KERNEL);
+	if (!cl->dma_vaddr)
+		return -ENOMEM;
+
+	cl->dma_size = size;
+
+	*daddr = cl->dma_daddr;
+	*vaddr = cl->dma_vaddr;
+	return 0;
+}
+
+/**
+ * issei_cl_dma_unmap - deallocate DMA mapping for client
+ *
+ * @cl: host client
+ */
+void issei_cl_dma_unmap(struct issei_host_client *cl)
+{
+	struct issei_device *idev = cl->idev;
+
+	guard(mutex)(&idev->client_lock);
+
+	__issei_cl_dma_unmap(cl);
+}
diff --git a/drivers/misc/issei/host_client.h b/drivers/misc/issei/host_client.h
index 05e2f4ade9c3..987cee609676 100644
--- a/drivers/misc/issei/host_client.h
+++ b/drivers/misc/issei/host_client.h
@@ -38,6 +38,10 @@ enum issei_host_client_state {
  * @read_wait: waitqueue for read object
  * @read_data: received data pointer
  * @read_data_size: received data size
+ *
+ * @dma_vaddr: allocated DMA buffer virtual address
+ * @dma_daddr: allocated DMA buffer DMA address
+ * @dma_size: allocated DMA buffer size, zero if no buffer allocated
  */
 struct issei_host_client {
 	struct list_head list;
@@ -54,6 +58,10 @@ struct issei_host_client {
 	wait_queue_head_t read_wait;
 	u8 *read_data;
 	size_t read_data_size;
+
+	void *dma_vaddr;
+	dma_addr_t dma_daddr;
+	size_t dma_size;
 };
 
 struct issei_host_client *issei_cl_create(struct issei_device *idev, struct file *fp);
@@ -72,4 +80,8 @@ int issei_cl_check_read(struct issei_host_client *cl);
 int issei_cl_check_write(struct issei_host_client *cl);
 void issei_cl_clean_all_wbuf(struct issei_host_client *cl);
 
+int issei_cl_dma_map(struct issei_host_client *cl, size_t size,
+		     dma_addr_t *daddr, void **vaddr);
+void issei_cl_dma_unmap(struct issei_host_client *cl);
+
 #endif /* ISSEI_HOST_CLIENT_H_ */
diff --git a/drivers/misc/issei/issei_dev.h b/drivers/misc/issei/issei_dev.h
index b47c4a7c2da4..244dc62ad85f 100644
--- a/drivers/misc/issei/issei_dev.h
+++ b/drivers/misc/issei/issei_dev.h
@@ -6,6 +6,7 @@
 #include <linux/device.h>
 #include <linux/mutex.h>
 #include <linux/sched.h>
+#include <linux/sizes.h>
 #include <linux/time64.h>
 #include <linux/types.h>
 #include <linux/wait.h>
@@ -31,6 +32,8 @@ extern struct class *issei_class;
 #define ISSEI_STOP_TIMEOUT_MSEC         500
 #define ISSEI_WRITE_TIMEOUT_MSEC        (MSEC_PER_SEC)
 
+#define ISSEI_HOST_DMA_MAX_SIZE	        SZ_64M
+
 /**
  * struct issei_write_buf - write buffer object
  * @list: linked list pointer

-- 
2.53.0


^ permalink raw reply	[flat|nested] 8+ messages in thread

* [PATCH char-misc-next 4/4] issei: add driver to driver interface
  2026-10-15  5:28 [PATCH char-misc-next 0/4] issei: runtime power management, dma and in-kernel interface Alexander Usyskin
                   ` (2 preceding siblings ...)
  2026-10-15  5:28 ` [PATCH char-misc-next 3/4] issei: host_client: add dma allocation support Alexander Usyskin
@ 2026-10-15  5:28 ` Alexander Usyskin
  2026-10-15  6:29   ` Arnd Bergmann
  3 siblings, 1 reply; 8+ messages in thread
From: Alexander Usyskin @ 2026-10-15  5:28 UTC (permalink / raw)
  To: Arnd Bergmann, Greg Kroah-Hartman
  Cc: linux-kernel, Menachem Adin, Andrzej Kacprowski, Karol Wachowski,
	Alexander Usyskin, Reuven Abliyev

Add new interface that enables communication with ISSEI device
from the kernel modules.
First intended consumer is an NPU (NPU7+) 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 | 268 ++++++++++++++++++++++++++++++++++++++
 include/linux/issei_device.h      |  84 ++++++++++++
 5 files changed, 360 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..a28f2adef9d2
--- /dev/null
+++ b/drivers/misc/issei/issei_device.c
@@ -0,0 +1,268 @@
+// 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_CONNECT_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;
+
+	if (idev->rst_state != ISSEI_RST_STATE_DONE) {
+		wait_event_interruptible_timeout(idev->wait_rst_state,
+						 idev->rst_state == ISSEI_RST_STATE_DONE,
+						 msecs_to_jiffies(ISSEI_DEVICE_CONNECT_TO_MS));
+		if (idev->rst_state != ISSEI_RST_STATE_DONE) {
+			dev_dbg(&idev->dev, "Device is in transition\n");
+			return -ENODEV;
+		}
+	}
+	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) {
+			issei_cl_clean_all_wbuf(cl);
+			if (wret == 0) /* timed out */
+				return -ETIMEDOUT;
+			if (signal_pending(current))
+				return -EINTR;
+			return -ERESTARTSYS;
+		}
+	} 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) {
+			if (ret == 0) /* timeout */
+				return -ETIMEDOUT;
+			if (signal_pending(current))
+				return -EINTR;
+			return -ERESTARTSYS;
+		}
+		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..a177b7ebdf11
--- /dev/null
+++ b/include/linux/issei_device.h
@@ -0,0 +1,84 @@
+/* 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;
+
+#if IS_ENABLED(CONFIG_INTEL_SSEI)
+
+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)
+
+#else
+
+void *issei_device_open(int (*match)(struct device *, const void *), const void *data)
+{
+	return ERR_PTR(-EOPNOTSUPP);
+}
+
+void issei_device_release(void *ctx)
+{
+}
+
+int issei_device_connect(void *ctx, const uuid_t *client_uuid, struct issei_client *conn)
+{
+	return -EOPNOTSUPP;
+}
+
+int issei_device_disconnect(void *ctx)
+{
+	return -EOPNOTSUPP;
+}
+
+ssize_t issei_device_write(void *ctx, const u8 *ubuf, size_t length, unsigned long timeout)
+{
+	return -EOPNOTSUPP;
+}
+
+ssize_t issei_device_read(void *ctx, u8 *ubuf, size_t length, unsigned long timeout);
+{
+	return -EOPNOTSUPP;
+}
+
+int issei_device_dma_map(void *ctx, size_t size, dma_addr_t *daddr, void **vaddr);
+{
+	return -EOPNOTSUPP;
+}
+
+void issei_device_dma_unmap(void *ctx);
+{
+}
+
+int issei_device_register_interface(struct class_interface *intf)
+{
+	return -EOPNOTSUPP;
+}
+
+void issei_device_unregister_interface(struct class_interface *intf)
+{
+}
+
+#endif /* CONFIG_INTEL_SSEI */
+
+#endif /* _ISSEI_DEVICE_H_ */

-- 
2.53.0


^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: [PATCH char-misc-next 4/4] issei: add driver to driver interface
  2026-10-15  5:28 ` [PATCH char-misc-next 4/4] issei: add driver to driver interface Alexander Usyskin
@ 2026-10-15  6:29   ` Arnd Bergmann
  2026-10-15 12:47     ` Usyskin, Alexander
  0 siblings, 1 reply; 8+ messages in thread
From: Arnd Bergmann @ 2026-10-15  6:29 UTC (permalink / raw)
  To: Alexander Usyskin, Greg Kroah-Hartman
  Cc: linux-kernel, Menachem Adin, Andrzej Kacprowski, Karol Wachowski,
	Reuven Abliyev

On Thu, Oct 15, 2026, at 07:28, Alexander Usyskin wrote:

> +	if (idev->rst_state != ISSEI_RST_STATE_DONE) {
> +		wait_event_interruptible_timeout(idev->wait_rst_state,
> +						 idev->rst_state == ISSEI_RST_STATE_DONE,
> +						 msecs_to_jiffies(ISSEI_DEVICE_CONNECT_TO_MS));
> +		if (idev->rst_state != ISSEI_RST_STATE_DONE) {
> +			dev_dbg(&idev->dev, "Device is in transition\n");
> +			return -ENODEV;
> +		}


> +		wret = wait_event_interruptible_timeout(cl->write_wait,
> +							issei_cl_check_write(cl) != 1,
> +							msecs_to_jiffies(timeout));
> +		if (wret <= 0) {
> +			issei_cl_clean_all_wbuf(cl);
> +			if (wret == 0) /* timed out */
> +				return -ETIMEDOUT;
> +			if (signal_pending(current))
> +				return -EINTR;
> +			return -ERESTARTSYS;

The wait logic is inconsistent here, the first one ignores the timeout
and pending signal, the second one flips -ERESTARTSYS to -EINTR and
everything else to -ERESTARTSYS, which is also questionable.

> +#if IS_ENABLED(CONFIG_INTEL_SSEI)
> +
> +void *issei_device_open(int (*match)(struct device *, const void *), 
> const void *data);
> +void issei_device_release(void *ctx);
...
> +#else
> +
> +void *issei_device_open(int (*match)(struct device *, const void *), 
> const void *data)
> +{
> +	return ERR_PTR(-EOPNOTSUPP);
> +}

What is the intended use case for !CONFIG_INTEL_SSEI in the caller,
and what do you expect to happen when CONFIG_INTEL_SSEI=m but the
caller is configured as built-in?

      Arnd

^ permalink raw reply	[flat|nested] 8+ messages in thread

* RE: [PATCH char-misc-next 4/4] issei: add driver to driver interface
  2026-10-15  6:29   ` Arnd Bergmann
@ 2026-10-15 12:47     ` Usyskin, Alexander
  2026-10-15 12:58       ` Arnd Bergmann
  0 siblings, 1 reply; 8+ messages in thread
From: Usyskin, Alexander @ 2026-10-15 12:47 UTC (permalink / raw)
  To: Arnd Bergmann, Greg Kroah-Hartman
  Cc: linux-kernel, Adin, Menachem, Andrzej Kacprowski,
	Karol Wachowski, Reuven Abliyev

> Subject: Re: [PATCH char-misc-next 4/4] issei: add driver to driver interface
> 
> On Thu, Oct 15, 2026, at 07:28, Alexander Usyskin wrote:
> 
> > +	if (idev->rst_state != ISSEI_RST_STATE_DONE) {
> > +		wait_event_interruptible_timeout(idev->wait_rst_state,
> > +						 idev->rst_state ==
> ISSEI_RST_STATE_DONE,
> > +
> msecs_to_jiffies(ISSEI_DEVICE_CONNECT_TO_MS));
> > +		if (idev->rst_state != ISSEI_RST_STATE_DONE) {
> > +			dev_dbg(&idev->dev, "Device is in transition\n");
> > +			return -ENODEV;
> > +		}
> 
> 
> > +		wret = wait_event_interruptible_timeout(cl->write_wait,
> > +
> 	issei_cl_check_write(cl) != 1,
> > +
> 	msecs_to_jiffies(timeout));
> > +		if (wret <= 0) {
> > +			issei_cl_clean_all_wbuf(cl);
> > +			if (wret == 0) /* timed out */
> > +				return -ETIMEDOUT;
> > +			if (signal_pending(current))
> > +				return -EINTR;
> > +			return -ERESTARTSYS;
> 
> The wait logic is inconsistent here, the first one ignores the timeout
> and pending signal, the second one flips -ERESTARTSYS to -EINTR and
> everything else to -ERESTARTSYS, which is also questionable.
> 

First wait ignores timeout as it only interesting in ISSEI_RST_STATE_DONE.
If after timeout the state is ISSEI_RST_STATE_DONE - we can continue in flow.
About signal - you are right - better to add check and specified return code.

Second wait was converted from wait_event_interruptible and I've missed
difference in return values - will fix this.

> > +#if IS_ENABLED(CONFIG_INTEL_SSEI)
> > +
> > +void *issei_device_open(int (*match)(struct device *, const void *),
> > const void *data);
> > +void issei_device_release(void *ctx);
> ...
> > +#else
> > +
> > +void *issei_device_open(int (*match)(struct device *, const void *),
> > const void *data)
> > +{
> > +	return ERR_PTR(-EOPNOTSUPP);
> > +}
> 
> What is the intended use case for !CONFIG_INTEL_SSEI in the caller,
> and what do you expect to happen when CONFIG_INTEL_SSEI=m but the
> caller is configured as built-in?
> 

For CONFIG_INTEL_SSEI=m #if IS_ENABLED(CONFIG_INTEL_SSEI) will be true, I hope.
Caller anyway should wait for device with issei_device_register_interface() and
it will receive devices as soon as module will be loaded.

>       Arnd

- - 
Thanks,
Sasha



^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: [PATCH char-misc-next 4/4] issei: add driver to driver interface
  2026-10-15 12:47     ` Usyskin, Alexander
@ 2026-10-15 12:58       ` Arnd Bergmann
  0 siblings, 0 replies; 8+ messages in thread
From: Arnd Bergmann @ 2026-10-15 12:58 UTC (permalink / raw)
  To: Alexander Usyskin, Greg Kroah-Hartman
  Cc: linux-kernel, Menachem Adin, Andrzej Kacprowski, Karol Wachowski,
	Reuven Abliyev

On Thu, Oct 15, 2026, at 14:47, Usyskin, Alexander wrote:
>> Subject: Re: [PATCH char-misc-next 4/4] issei: add driver to driver interface
>> 
>> On Thu, Oct 15, 2026, at 07:28, Alexander Usyskin wrote:
>> 
>> > +	if (idev->rst_state != ISSEI_RST_STATE_DONE) {
>> > +		wait_event_interruptible_timeout(idev->wait_rst_state,
>> > +						 idev->rst_state ==
>> ISSEI_RST_STATE_DONE,
>> > +
>> msecs_to_jiffies(ISSEI_DEVICE_CONNECT_TO_MS));
>> > +		if (idev->rst_state != ISSEI_RST_STATE_DONE) {
>> > +			dev_dbg(&idev->dev, "Device is in transition\n");
>> > +			return -ENODEV;
>> > +		}
>> 
>> 
>> The wait logic is inconsistent here, the first one ignores the timeout
>> and pending signal, the second one flips -ERESTARTSYS to -EINTR and
>> everything else to -ERESTARTSYS, which is also questionable.
>> 
>
> First wait ignores timeout as it only interesting in ISSEI_RST_STATE_DONE.
> If after timeout the state is ISSEI_RST_STATE_DONE - we can continue in flow.

But if it times out, it won't be in that state unless you hit
a tiny race, for consistency I would always check the return
code, not check the condition again.

>> > +#if IS_ENABLED(CONFIG_INTEL_SSEI)
>> > +
>> > +void *issei_device_open(int (*match)(struct device *, const void *),
>> > const void *data);
>> > +void issei_device_release(void *ctx);
>> ...
>> > +#else
>> > +
>> > +void *issei_device_open(int (*match)(struct device *, const void *),
>> > const void *data)
>> > +{
>> > +	return ERR_PTR(-EOPNOTSUPP);
>> > +}
>> 
>> What is the intended use case for !CONFIG_INTEL_SSEI in the caller,
>> and what do you expect to happen when CONFIG_INTEL_SSEI=m but the
>> caller is configured as built-in?
>> 
>
> For CONFIG_INTEL_SSEI=m #if IS_ENABLED(CONFIG_INTEL_SSEI) will be true, I hope.

Yes

> Caller anyway should wait for device with issei_device_register_interface() and
> it will receive devices as soon as module will be loaded.

If that configuration is allowed, then calling any of the exported
functions causes a link error. Your only options really are:

a) prevent the broken config using Kconfig
   'depends on INTEL_SSEI || !INTEL_SSEI' for every caller to force
   the caller to e a module instead of built-in driver.

b) remove the stub helpers and use a simpler 'depends on INTEL_SSEI'

       Arnd

^ permalink raw reply	[flat|nested] 8+ messages in thread

end of thread, other threads:[~2026-10-15 12:58 UTC | newest]

Thread overview: 8+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2026-10-15  5:28 [PATCH char-misc-next 0/4] issei: runtime power management, dma and in-kernel interface Alexander Usyskin
2026-10-15  5:28 ` [PATCH char-misc-next 1/4] issei: save power_down flag while processing event Alexander Usyskin
2026-10-15  5:28 ` [PATCH char-misc-next 2/4] issei: add runtime pm Alexander Usyskin
2026-10-15  5:28 ` [PATCH char-misc-next 3/4] issei: host_client: add dma allocation support Alexander Usyskin
2026-10-15  5:28 ` [PATCH char-misc-next 4/4] issei: add driver to driver interface Alexander Usyskin
2026-10-15  6:29   ` Arnd Bergmann
2026-10-15 12:47     ` Usyskin, Alexander
2026-10-15 12:58       ` Arnd Bergmann

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®