[PATCH 11/15] misc: nnpi: Create comm channel from app to device

From: Guy Zadicario
Date: Wed May 12 2021 - 03:12:24 EST


Establish bi-directional communication channels between user-mode
processes and NNP-I devices. Each command channel object ("channel")
holds a queue of messages from a single user-mode connection to a
single NNP-I device, as well as a ring-buffer to hold response messages
from this NNP-I device back to the user-mode process.

Messages to the NNP-I device are put by the channel into a command
queue. Response messages coming back from the device are being routed
to the intended channel's ring-buffer, where they are consumed by this
channel's user-mode connection. Routing of messages to and from the
device is done based on a channel's 10-bit unique id, which is included
in the messages.

The interface for consuming responses from the ring-buffer and writing
command messages into the msg_scheduler's command queue will be added
in a future patch. This patch only adds the channel creation code and
response message routing to the targeted channel.

When creating a "command channel", the user should give an open file
descriptor to the /dev/nnpi_host device. This file descriptor
associates the channel with a particular "nnp_user" object. The channel
can only reference host resources created by that "user".

Signed-off-by: Guy Zadicario <guy.zadicario@xxxxxxxxx>
Reviewed-by: Alexander Shishkin <alexander.shishkin@xxxxxxxxxxxxxxx>
---
drivers/misc/intel-nnpi/Makefile | 2 +-
drivers/misc/intel-nnpi/cmd_chan.c | 314 +++++++++++++++++++++
drivers/misc/intel-nnpi/cmd_chan.h | 72 +++++
drivers/misc/intel-nnpi/device.c | 113 +++++++-
drivers/misc/intel-nnpi/device.h | 17 +-
drivers/misc/intel-nnpi/ipc_include/ipc_protocol.h | 3 +
6 files changed, 516 insertions(+), 5 deletions(-)
create mode 100644 drivers/misc/intel-nnpi/cmd_chan.c
create mode 100644 drivers/misc/intel-nnpi/cmd_chan.h

diff --git a/drivers/misc/intel-nnpi/Makefile b/drivers/misc/intel-nnpi/Makefile
index e46c89f..b3bab2a 100644
--- a/drivers/misc/intel-nnpi/Makefile
+++ b/drivers/misc/intel-nnpi/Makefile
@@ -6,7 +6,7 @@
obj-$(CONFIG_INTEL_NNPI) := intel_nnpi.o intel_nnpi_pcie.o

intel_nnpi-y := device.o msg_scheduler.o hostres.o host_chardev.o nnp_user.o \
- bootimage.o
+ bootimage.o cmd_chan.o

intel_nnpi_pcie-y := nnp_pcie.o

diff --git a/drivers/misc/intel-nnpi/cmd_chan.c b/drivers/misc/intel-nnpi/cmd_chan.c
new file mode 100644
index 0000000..b5518e0
--- /dev/null
+++ b/drivers/misc/intel-nnpi/cmd_chan.c
@@ -0,0 +1,314 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/* Copyright (C) 2019-2021 Intel Corporation */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/anon_inodes.h>
+#include <linux/dev_printk.h>
+#include <linux/file.h>
+#include <linux/minmax.h>
+#include <linux/slab.h>
+
+#include "cmd_chan.h"
+#include "host_chardev.h"
+#include "ipc_protocol.h"
+#include "nnp_user.h"
+
+#define RESPQ_INIT_BUF_SIZE SZ_2K /* must be power of 2 */
+#define RESPQ_MAX_BUF_SIZE SZ_1M /* must be power of 2 */
+
+static inline int respq_free_bytes(struct nnp_chan *chan)
+{
+ return CIRC_SPACE(chan->respq.head, chan->respq.tail, chan->respq_size);
+}
+
+static inline void respq_push(struct nnp_chan *chan, void *buf, int count)
+{
+ char *dst = chan->respq.buf + chan->respq.head;
+ int t = CIRC_SPACE_TO_END(chan->respq.head, chan->respq.tail,
+ chan->respq_size);
+
+ if (t >= count) {
+ memcpy(dst, buf, count);
+ } else {
+ memcpy(dst, buf, t);
+ memcpy(chan->respq.buf, (u8 *)buf + t, count - t);
+ }
+ chan->respq.head = (chan->respq.head + count) & (chan->respq_size - 1);
+}
+
+static inline void respq_pop(struct nnp_chan *chan, void *buf, int count)
+{
+ char *src = chan->respq.buf + chan->respq.tail;
+ int t = CIRC_CNT_TO_END(chan->respq.head, chan->respq.tail,
+ chan->respq_size);
+
+ if (t >= count) {
+ memcpy(buf, src, count);
+ } else {
+ memcpy(buf, src, t);
+ memcpy((u8 *)buf + t, chan->respq.buf, count - t);
+ }
+ chan->respq.tail = (chan->respq.tail + count) & (chan->respq_size - 1);
+}
+
+/**
+ * nnpdev_chan_create() - creates a command channel object
+ * @nnpdev: the device
+ * @host_fd: opened file descriptor to "/dev/nnpi_host"
+ * @min_id: minimum range for allocating ipc channel id for that channel
+ * @max_id: maximum range for allocating ipc channel id for that channel
+ * @get_device_events: true if this channel needs to receive device-level
+ * responses (not originated to specific channel).
+ *
+ * This function create a "command channel" and assign it a unique id within
+ * the range [@min_id..@max_id]. channels in id range [0, 255] are assumed to be
+ * used for inference related operations and have slightly special semantics.
+ *
+ * Return: pointer to created channel or error.
+ */
+struct nnp_chan *nnpdev_chan_create(struct nnp_device *nnpdev, int host_fd,
+ unsigned int min_id, unsigned int max_id,
+ bool get_device_events)
+{
+ struct nnp_chan *cmd_chan;
+ int chan_id;
+ int ret;
+ unsigned int max_proto_id = BIT(NNP_IPC_CHANNEL_BITS) - 1;
+
+ if (min_id > max_proto_id)
+ return ERR_PTR(-EINVAL);
+ if (max_id > max_proto_id)
+ max_id = max_proto_id;
+ if (max_id < min_id)
+ return ERR_PTR(-EINVAL);
+
+ ret = ida_simple_get(&nnpdev->cmd_chan_ida, min_id, max_id + 1,
+ GFP_KERNEL);
+ if (ret < 0)
+ return ERR_PTR(ret);
+ chan_id = ret;
+
+ cmd_chan = kzalloc(sizeof(*cmd_chan), GFP_KERNEL);
+ if (!cmd_chan) {
+ ret = -ENOMEM;
+ goto err_ida;
+ }
+
+ cmd_chan->respq_buf = kmalloc(RESPQ_INIT_BUF_SIZE, GFP_KERNEL);
+ if (!cmd_chan->respq_buf) {
+ ret = -ENOMEM;
+ goto err_alloc;
+ }
+ cmd_chan->respq_size = RESPQ_INIT_BUF_SIZE;
+ cmd_chan->respq.buf = cmd_chan->respq_buf;
+ spin_lock_init(&cmd_chan->respq_lock);
+
+ cmd_chan->host_file = nnp_host_file_get(host_fd);
+ if (!cmd_chan->host_file) {
+ ret = -EINVAL;
+ goto err_respq;
+ }
+
+ cmd_chan->cmdq = nnp_msched_queue_create(nnpdev->cmdq_sched);
+ if (!cmd_chan->cmdq) {
+ ret = -ENOMEM;
+ goto err_file_get;
+ }
+
+ kref_init(&cmd_chan->ref);
+ cmd_chan->chan_id = chan_id;
+ cmd_chan->nnpdev = nnpdev;
+ cmd_chan->get_device_events = get_device_events;
+
+ cmd_chan->nnp_user = cmd_chan->host_file->private_data;
+ nnp_user_get(cmd_chan->nnp_user);
+
+ init_waitqueue_head(&cmd_chan->resp_waitq);
+ mutex_init(&cmd_chan->dev_mutex);
+
+ /*
+ * Add channel to the channel hash
+ */
+ spin_lock(&nnpdev->lock);
+ hash_add(nnpdev->cmd_chan_hash, &cmd_chan->hash_node, cmd_chan->chan_id);
+
+ spin_unlock(&nnpdev->lock);
+
+ return cmd_chan;
+
+err_file_get:
+ fput(cmd_chan->host_file);
+err_respq:
+ kfree(cmd_chan->respq_buf);
+err_alloc:
+ kfree(cmd_chan);
+err_ida:
+ ida_simple_remove(&nnpdev->cmd_chan_ida, chan_id);
+ return ERR_PTR(ret);
+}
+
+static void nnp_chan_release(struct kref *kref)
+{
+ struct nnp_chan *cmd_chan = container_of(kref, struct nnp_chan, ref);
+
+ nnp_chan_disconnect(cmd_chan);
+
+ nnp_user_put(cmd_chan->nnp_user);
+
+ kfree(cmd_chan->respq_buf);
+ kfree(cmd_chan);
+}
+
+void nnp_chan_get(struct nnp_chan *cmd_chan)
+{
+ kref_get(&cmd_chan->ref);
+}
+
+void nnp_chan_put(struct nnp_chan *cmd_chan)
+{
+ kref_put(&cmd_chan->ref, nnp_chan_release);
+}
+
+/**
+ * nnp_chan_disconnect() - disconnect the channel from the NNP-I device object
+ * @cmd_chan: the command channel object
+ *
+ * This function is called when the channel is released or the NNP-I device is
+ * being removed. It disconnect the channel from the nnp_device object.
+ * A disconnected channel can no longer become connected again and cannot
+ * be used to communicate with any device.
+ */
+void nnp_chan_disconnect(struct nnp_chan *cmd_chan)
+{
+ struct nnp_device *nnpdev;
+
+ mutex_lock(&cmd_chan->dev_mutex);
+ if (!cmd_chan->nnpdev) {
+ mutex_unlock(&cmd_chan->dev_mutex);
+ return;
+ }
+
+ nnpdev = cmd_chan->nnpdev;
+ cmd_chan->nnpdev = NULL;
+ spin_lock(&nnpdev->lock);
+ hash_del(&cmd_chan->hash_node);
+ spin_unlock(&nnpdev->lock);
+ mutex_unlock(&cmd_chan->dev_mutex);
+
+ nnp_msched_queue_sync(cmd_chan->cmdq);
+ nnp_msched_queue_destroy(cmd_chan->cmdq);
+
+ ida_simple_remove(&nnpdev->cmd_chan_ida, cmd_chan->chan_id);
+}
+
+static int resize_respq(struct nnp_chan *cmd_chan)
+{
+ unsigned int avail_size;
+ unsigned int new_size;
+ char *new_buf;
+
+ new_size = min_t(unsigned int, cmd_chan->respq_size * 2, RESPQ_MAX_BUF_SIZE);
+
+ /* do not try to resize if already in maximum size */
+ if (new_size == cmd_chan->respq_size)
+ return -ENOMEM;
+
+ new_buf = kmalloc(new_size, GFP_KERNEL);
+ if (!new_buf)
+ return -ENOMEM;
+
+ /* copy data from old to new ring buffer */
+ spin_lock(&cmd_chan->respq_lock);
+ avail_size = CIRC_CNT(cmd_chan->respq.head, cmd_chan->respq.tail,
+ cmd_chan->respq_size);
+ if (avail_size > 0)
+ respq_pop(cmd_chan, new_buf, avail_size);
+ kfree(cmd_chan->respq_buf);
+ cmd_chan->respq_buf = new_buf;
+ cmd_chan->respq_size = new_size;
+ cmd_chan->respq.buf = cmd_chan->respq_buf;
+ cmd_chan->respq.tail = 0;
+ cmd_chan->respq.head = avail_size;
+ spin_unlock(&cmd_chan->respq_lock);
+ dev_dbg(cmd_chan->nnpdev->dev, "channel respq resized to %d\n", new_size);
+
+ return 0;
+}
+
+/**
+ * try_add_response() - adds a response message to respq if enough space exist
+ * @cmd_chan: the command channel object
+ * @hw_msg: response message arrived from device
+ * @size: size in bytes of the response
+ *
+ * Return: zero on success, -ENOSPC if message does not fit
+ */
+static int try_add_response(struct nnp_chan *cmd_chan, u64 *hw_msg, u32 size)
+{
+ spin_lock(&cmd_chan->respq_lock);
+
+ /* Would the response fit in the buffer? */
+ if (respq_free_bytes(cmd_chan) < size + sizeof(size)) {
+ spin_unlock(&cmd_chan->respq_lock);
+ return -ENOSPC;
+ }
+
+ /* push the response message to the ring buffer */
+ respq_push(cmd_chan, &size, sizeof(size));
+ respq_push(cmd_chan, hw_msg, size);
+
+ spin_unlock(&cmd_chan->respq_lock);
+
+ wake_up_all(&cmd_chan->resp_waitq);
+
+ return 0;
+}
+
+/**
+ * nnp_chan_add_response() - adds a response message targeting this channel
+ * @cmd_chan: the command channel object
+ * @hw_msg: response message arrived from device
+ * @size: size in bytes of the response
+ *
+ * This function is being called when a response arrived from the NNP-I card
+ * which targets to a specific command channel object.
+ * The function puts the response message in a ring buffer and will later be
+ * consumed by user space through a call to read(2) on the channel' file
+ * descriptor.
+ *
+ * Return: error code or zero on success.
+ */
+int nnp_chan_add_response(struct nnp_chan *cmd_chan, u64 *hw_msg, u32 size)
+{
+ int ret;
+
+retry:
+ ret = try_add_response(cmd_chan, hw_msg, size);
+ if (ret == -ENOSPC) {
+ /*
+ * This should *rarely* happen in normal system
+ * operation since the ring-buffer is big enough.
+ * We will get here only if the user application sleeps
+ * for a *very* long time without draining the responses.
+ * Try to resize the response buffer when it does
+ * happen, but up to a maximum value.
+ * If resize failed, we have no choice but to lose the
+ * response. Only the application that uses that channel
+ * will get affected.
+ */
+ ret = resize_respq(cmd_chan);
+ if (!ret)
+ goto retry;
+ }
+
+ if (ret) {
+ if (!cmd_chan->resp_lost)
+ dev_err_ratelimited(cmd_chan->nnpdev->dev,
+ "Response queue full for channel %d losing response!\n",
+ cmd_chan->chan_id);
+ cmd_chan->resp_lost++;
+ }
+
+ return ret;
+}
diff --git a/drivers/misc/intel-nnpi/cmd_chan.h b/drivers/misc/intel-nnpi/cmd_chan.h
new file mode 100644
index 0000000..8cb1a5e
--- /dev/null
+++ b/drivers/misc/intel-nnpi/cmd_chan.h
@@ -0,0 +1,72 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/* Copyright (C) 2019-2021 Intel Corporation */
+
+#ifndef NNPDRV_CMD_CHAN_H
+#define NNPDRV_CMD_CHAN_H
+
+#include <linux/circ_buf.h>
+#include <linux/hashtable.h>
+#include <linux/kref.h>
+#include <linux/list.h>
+#include <linux/mutex.h>
+#include <linux/spinlock.h>
+
+#include "device.h"
+
+/**
+ * struct nnp_chan - structure object for user<->device communication
+ * @ref: refcount for this object
+ * @nnpdev: the device this channel is connected to. May be NULL after device
+ * disconnects (on device removal or reset).
+ * @chan_id: the ipc channel id for this channel
+ * @hash_node: node to include this object in list of channels
+ * hash is in (cmd_chan_hash in nnp_device).
+ * @get_device_events: true if device-level events received from card should
+ * be sent over this channel to user.
+ * @cmdq: message queue added to msg_scheduler, for user commands to be sent
+ * to the device.
+ * @host_file: reference to opened "/dev/nnpi_host" object which defines the
+ * nnp_user object this channel connects to.
+ * @nnp_user: the nnp_user this channel belongs to.
+ * the channel can reference host resources created by this
+ * nnp_user object.
+ * @dev_mutex: protects @nnpdev
+ * @resp_waitq: waitqueue used for waiting for response messages be available.
+ * @respq: circular buffer object that receive response messages from device.
+ * @respq_lock: protects @respq
+ * @respq_buf: buffer space allocated for circular response buffer.
+ * @respq_size: current allocated size of circular response buffer.
+ * @resp_lost: number of response messages lost due to response buffer full.
+ */
+struct nnp_chan {
+ struct kref ref;
+ struct nnp_device *nnpdev;
+ u16 chan_id;
+ struct hlist_node hash_node;
+ bool get_device_events;
+
+ struct nnp_msched_queue *cmdq;
+ struct file *host_file;
+ struct nnp_user_info *nnp_user;
+
+ struct mutex dev_mutex;
+ wait_queue_head_t resp_waitq;
+
+ struct circ_buf respq;
+ spinlock_t respq_lock;
+ char *respq_buf;
+ unsigned int respq_size;
+ unsigned int resp_lost;
+};
+
+struct nnp_chan *nnpdev_chan_create(struct nnp_device *nnpdev, int host_fd,
+ unsigned int min_id, unsigned int max_id,
+ bool get_device_events);
+
+void nnp_chan_get(struct nnp_chan *cmd_chan);
+void nnp_chan_put(struct nnp_chan *cmd_chan);
+void nnp_chan_disconnect(struct nnp_chan *cmd_chan);
+
+int nnp_chan_add_response(struct nnp_chan *cmd_chan, u64 *hw_msg, u32 size);
+
+#endif
diff --git a/drivers/misc/intel-nnpi/device.c b/drivers/misc/intel-nnpi/device.c
index 7bcab563..3902876 100644
--- a/drivers/misc/intel-nnpi/device.c
+++ b/drivers/misc/intel-nnpi/device.c
@@ -11,6 +11,7 @@
#include <linux/printk.h>

#include "bootimage.h"
+#include "cmd_chan.h"
#include "device.h"
#include "host_chardev.h"
#include "msg_scheduler.h"
@@ -61,6 +62,9 @@ static void process_query_version_reply(struct work_struct *work)
u32 protocol_version;
u32 card_boot_state;
u32 val;
+ u64 chan_resp_op_size;
+ u64 chan_cmd_op_size;
+ int i;

query_version_work =
container_of(work, struct query_version_work, work);
@@ -70,6 +74,15 @@ static void process_query_version_reply(struct work_struct *work)
card_boot_state = FIELD_GET(NNP_CARD_BOOT_STATE_MASK,
nnpdev->card_doorbell_val);

+ chan_resp_op_size = query_version_work->chan_resp_op_size;
+ chan_cmd_op_size = query_version_work->chan_cmd_op_size;
+ for (i = 0; i < NNP_IPC_NUM_USER_OPS; i++) {
+ nnpdev->ipc_chan_resp_op_size[i] = chan_resp_op_size & 0x3;
+ chan_resp_op_size >>= 2;
+ nnpdev->ipc_chan_cmd_op_size[i] = chan_cmd_op_size & 0x3;
+ chan_cmd_op_size >>= 2;
+ }
+
nnpdev->protocol_version =
query_version_work->protocol_version;
nnpdev->chan_protocol_version =
@@ -167,6 +180,38 @@ static int handle_bios_protocol(struct nnp_device *nnpdev, const u64 *msgbuf,
return msg_qwords;
}

+struct nnp_chan *nnpdev_find_channel(struct nnp_device *nnpdev, u16 chan_id)
+{
+ struct nnp_chan *cmd_chan;
+
+ spin_lock(&nnpdev->lock);
+ hash_for_each_possible(nnpdev->cmd_chan_hash, cmd_chan, hash_node, chan_id)
+ if (cmd_chan->chan_id == chan_id) {
+ nnp_chan_get(cmd_chan);
+ spin_unlock(&nnpdev->lock);
+ return cmd_chan;
+ }
+ spin_unlock(&nnpdev->lock);
+
+ return NULL;
+}
+
+static void disconnect_all_channels(struct nnp_device *nnpdev)
+{
+ struct nnp_chan *cmd_chan;
+ int i;
+
+restart:
+ spin_lock(&nnpdev->lock);
+ hash_for_each(nnpdev->cmd_chan_hash, i, cmd_chan, hash_node) {
+ spin_unlock(&nnpdev->lock);
+ nnp_chan_disconnect(cmd_chan);
+ nnp_chan_put(cmd_chan);
+ goto restart;
+ }
+ spin_unlock(&nnpdev->lock);
+}
+
typedef int (*response_handler)(struct nnp_device *nnpdev, const u64 *msgbuf,
int avail_qwords);

@@ -175,6 +220,50 @@ typedef int (*response_handler)(struct nnp_device *nnpdev, const u64 *msgbuf,
[NNP_IPC_C2H_OP_BIOS_PROTOCOL] = handle_bios_protocol
};

+static int dispatch_chan_message(struct nnp_device *nnpdev, u64 *hw_msg,
+ unsigned int size)
+{
+ int op_code = FIELD_GET(NNP_C2H_CHAN_MSG_OP_MASK, hw_msg[0]);
+ int chan_id = FIELD_GET(NNP_C2H_CHAN_MSG_CHAN_ID_MASK, hw_msg[0]);
+ struct nnp_chan *chan;
+ int msg_size;
+
+ if (op_code < NNP_IPC_MIN_USER_OP ||
+ op_code > NNP_IPC_MAX_USER_OP) {
+ /* Should not happen! */
+ dev_err(nnpdev->dev,
+ "chan response opcode out-of-range received %d (0x%llx)\n",
+ op_code, *hw_msg);
+ return -EINVAL;
+ }
+
+ msg_size = nnpdev->ipc_chan_resp_op_size[op_code - NNP_IPC_MIN_USER_OP];
+ if (msg_size == 0) {
+ /* Should not happen! */
+ dev_err(nnpdev->dev,
+ "Unknown response chan opcode received %d (0x%llx)\n",
+ op_code, *hw_msg);
+ return -EINVAL;
+ }
+
+ /* Check for partial message */
+ if (size < msg_size)
+ return -ENOSPC;
+
+ chan = nnpdev_find_channel(nnpdev, chan_id);
+ if (!chan) {
+ dev_err(nnpdev->dev,
+ "Got response for invalid channel chan_id=%d 0x%llx\n",
+ chan_id, *hw_msg);
+ return msg_size;
+ }
+
+ nnp_chan_add_response(chan, hw_msg, msg_size * 8);
+ nnp_chan_put(chan);
+
+ return msg_size;
+}
+
/**
* nnpdev_process_messages() - process response messages from nnpi device
* @nnpdev: The nnp device
@@ -234,10 +323,18 @@ void nnpdev_process_messages(struct nnp_device *nnpdev, u64 *hw_msg,
int op_code = FIELD_GET(NNP_C2H_OP_MASK, msg[j]);
response_handler handler;

- /* opcodes above OP_BIOS_PROTOCOL are not yet supported */
+ /* opcodes above OP_BIOS_PROTOCOL are routed to a channel */
if (op_code > NNP_IPC_C2H_OP_BIOS_PROTOCOL) {
- fatal_protocol_error = true;
- break;
+ msg_size = dispatch_chan_message(nnpdev, &msg[j],
+ nof_msg - j);
+ if (msg_size < 0) {
+ if (msg_size != -ENOSPC)
+ fatal_protocol_error = true;
+ break;
+ }
+
+ j += msg_size;
+ continue;
}

/* dispatch the message request */
@@ -492,6 +589,11 @@ int nnpdev_init(struct nnp_device *nnpdev, struct device *dev,
nnpdev->ops = ops;
nnpdev->protocol_version = 0;

+ nnpdev->protocol_version = 0;
+
+ ida_init(&nnpdev->cmd_chan_ida);
+ hash_init(nnpdev->cmd_chan_hash);
+
nnpdev->cmdq_sched = nnp_msched_create(nnpdev);
if (!nnpdev->cmdq_sched) {
ret = -ENOMEM;
@@ -668,10 +770,15 @@ void nnpdev_destroy(struct nnp_device *nnpdev)

destroy_workqueue(nnpdev->wq);

+ disconnect_all_channels(nnpdev);
dma_free_coherent(nnpdev->dev, NNP_PAGE_SIZE, nnpdev->bios_system_info,
nnpdev->bios_system_info_dma_addr);

nnp_msched_destroy(nnpdev->cmdq_sched);
+ /*
+ * nnpdev->cmd_chan_ida is empty after disconnect_all_channels,
+ * ida_destroy is not needed
+ */
ida_simple_remove(&dev_ida, nnpdev->id);
}
EXPORT_SYMBOL(nnpdev_destroy);
diff --git a/drivers/misc/intel-nnpi/device.h b/drivers/misc/intel-nnpi/device.h
index 63bc54d..9b6383e 100644
--- a/drivers/misc/intel-nnpi/device.h
+++ b/drivers/misc/intel-nnpi/device.h
@@ -4,6 +4,8 @@
#ifndef _NNPDRV_DEVICE_H
#define _NNPDRV_DEVICE_H

+#include <linux/hashtable.h>
+#include <linux/idr.h>
#include <linux/spinlock.h>
#include <linux/workqueue.h>

@@ -70,11 +72,13 @@ struct query_version_work {
* @cmdq: input queue to @cmdq_sched used to schedule driver internal commands
* to be sent to the device.
* @wq: singlethread workqueue for processing device's response messages.
- * @lock: protects accesses to @state
+ * @lock: protects accesses to @state and @cmd_chan_hash
* @is_recovery_bios: true if device has booted from the recovery bios flash
* @boot_image_loaded: true if boot image load has started
* @response_buf: buffer of device response messages arrived from "pci" layer.
* @response_num_msgs: number of qwords available in @response_buf
+ * @cmd_chan_ida: allocate channel ids to be used in ipc protocol.
+ * @cmd_chan_hash: maps command channel id to its struct pointer.
* @bios_system_info_dma_addr: dma page allocated for bios system info.
* @bios_system_info: virtual pointer to bios system info page
* @bios_version_str: the device's started bios version string
@@ -87,6 +91,9 @@ struct query_version_work {
* @boot_image: boot image object used to boot the card
* @query_version_work: work struct used to schedule processing of version
* reply response message arrived from card.
+ * @ipc_chan_resp_op_size: holds response size for each possible channel
+ * response.
+ * @ipc_chan_cmd_op_size: holds command size for each possible channel command.
*/
struct nnp_device {
const struct nnp_device_ops *ops;
@@ -104,6 +111,9 @@ struct nnp_device {
u64 response_buf[NNP_DEVICE_RESPONSE_BUFFER_LEN];
unsigned int response_num_msgs;

+ struct ida cmd_chan_ida;
+ DECLARE_HASHTABLE(cmd_chan_hash, 6);
+
dma_addr_t bios_system_info_dma_addr;
struct nnp_c2h_system_info *bios_system_info;
char bios_version_str[NNP_BIOS_VERSION_LEN];
@@ -117,6 +127,9 @@ struct nnp_device {
struct image_info boot_image;

struct query_version_work query_version_work;
+
+ u8 ipc_chan_resp_op_size[NNP_IPC_NUM_USER_OPS];
+ u8 ipc_chan_cmd_op_size[NNP_IPC_NUM_USER_OPS];
};

/**
@@ -151,4 +164,6 @@ void nnpdev_process_messages(struct nnp_device *nnpdev, u64 *hw_msg,
*/
void nnpdev_set_boot_state(struct nnp_device *nnpdev, u32 mask);

+struct nnp_chan *nnpdev_find_channel(struct nnp_device *nnpdev, u16 chan_id);
+
#endif
diff --git a/drivers/misc/intel-nnpi/ipc_include/ipc_protocol.h b/drivers/misc/intel-nnpi/ipc_include/ipc_protocol.h
index 59b4a79..037c362 100644
--- a/drivers/misc/intel-nnpi/ipc_include/ipc_protocol.h
+++ b/drivers/misc/intel-nnpi/ipc_include/ipc_protocol.h
@@ -11,6 +11,9 @@

#define IPC_OP_MAX BIT(6)
#define NNP_IPC_OPCODE_MASK GENMASK(5, 0)
+#define NNP_IPC_MIN_USER_OP 32
+#define NNP_IPC_MAX_USER_OP 63
+#define NNP_IPC_NUM_USER_OPS (NNP_IPC_MAX_USER_OP - NNP_IPC_MIN_USER_OP + 1)

#define NNP_MSG_SIZE(msg) (sizeof(msg) / sizeof(__le64))

--
1.8.3.1

---------------------------------------------------------------------
Intel Israel (74) Limited

This e-mail and any attachments may contain confidential material for
the sole use of the intended recipient(s). Any review or distribution
by others is strictly prohibited. If you are not the intended
recipient, please contact the sender and delete all copies.