* [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®