Add the Linux client driver for the Network Packet ACcelerator (NPAC).
The driver communicates with the NPAC host driver via RPMsg,
allowing offloading of ethernet-related packet processing tasks.

The driver provides the basic framework for receiving offload commands
via IOCTL and communicating with the NPAC hardware accelerator.

Signed-off-by: Jaspinder Budhal <[email protected]>
---
 drivers/accel/Kconfig         |   1 +
 drivers/accel/Makefile        |   1 +
 drivers/accel/npac/Kconfig    |  13 ++
 drivers/accel/npac/Makefile   |   3 +
 drivers/accel/npac/npac_drv.c | 218 ++++++++++++++++++++++++++++++++++
 5 files changed, 236 insertions(+)
 create mode 100644 drivers/accel/npac/Kconfig
 create mode 100644 drivers/accel/npac/Makefile
 create mode 100644 drivers/accel/npac/npac_drv.c

diff --git a/drivers/accel/Kconfig b/drivers/accel/Kconfig
index bdf48ccafcf2..a6d8c1ebfcf1 100644
--- a/drivers/accel/Kconfig
+++ b/drivers/accel/Kconfig
@@ -28,6 +28,7 @@ source "drivers/accel/amdxdna/Kconfig"
 source "drivers/accel/ethosu/Kconfig"
 source "drivers/accel/habanalabs/Kconfig"
 source "drivers/accel/ivpu/Kconfig"
+source "drivers/accel/npac/Kconfig"
 source "drivers/accel/qaic/Kconfig"
 source "drivers/accel/rocket/Kconfig"
 
diff --git a/drivers/accel/Makefile b/drivers/accel/Makefile
index 1d3a7251b950..0df7b9f2ae18 100644
--- a/drivers/accel/Makefile
+++ b/drivers/accel/Makefile
@@ -4,5 +4,6 @@ obj-$(CONFIG_DRM_ACCEL_AMDXDNA)         += amdxdna/
 obj-$(CONFIG_DRM_ACCEL_ARM_ETHOSU)     += ethosu/
 obj-$(CONFIG_DRM_ACCEL_HABANALABS)     += habanalabs/
 obj-$(CONFIG_DRM_ACCEL_IVPU)           += ivpu/
+obj-$(CONFIG_DRM_ACCEL_NPAC_CLIENT)    += npac/
 obj-$(CONFIG_DRM_ACCEL_QAIC)           += qaic/
 obj-$(CONFIG_DRM_ACCEL_ROCKET)         += rocket/
\ No newline at end of file
diff --git a/drivers/accel/npac/Kconfig b/drivers/accel/npac/Kconfig
new file mode 100644
index 000000000000..a9543ac2c7aa
--- /dev/null
+++ b/drivers/accel/npac/Kconfig
@@ -0,0 +1,13 @@
+config DRM_ACCEL_NPAC_CLIENT
+       tristate "NPAC Client Driver"
+       depends on DRM_ACCEL
+       depends on RPMSG
+       help
+         This driver is the NPAC (Network Packet ACcelerator) client,
+         communicating with the NPAC host driver running on a remote
+         processor over RPMsg. The driver registers with the DRM accelerator
+         framework and exposes /dev/accel/accel* as the userspace interface
+         for ACL rule offload operations.
+         If you have a device with NPAC and wish to use it with this
+         driver, say M to compile this driver as a module, or Y to
+         compile it into the kernel.
diff --git a/drivers/accel/npac/Makefile b/drivers/accel/npac/Makefile
new file mode 100644
index 000000000000..89f4773d7041
--- /dev/null
+++ b/drivers/accel/npac/Makefile
@@ -0,0 +1,3 @@
+# SPDX-License-Identifier: GPL-2.0-only
+
+obj-$(CONFIG_DRM_ACCEL_NPAC_CLIENT) += npac_drv.o
diff --git a/drivers/accel/npac/npac_drv.c b/drivers/accel/npac/npac_drv.c
new file mode 100644
index 000000000000..7516382ba74b
--- /dev/null
+++ b/drivers/accel/npac/npac_drv.c
@@ -0,0 +1,218 @@
+// SPDX-License-Identifier: GPL-2.0-only OR MIT
+/*
+ * Copyright (C) 2026 Texas Instruments Incorporated - https://www.ti.com/
+ */
+
+#include <drm/drm_accel.h>
+#include <drm/drm_drv.h>
+#include <drm/drm_file.h>
+#include <drm/drm_gem.h>
+#include <drm/drm_ioctl.h>
+#include <linux/completion.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/rpmsg.h>
+
+#define NPAC_RPMSG_SERVICE_NAME        "ti.npac"
+#define NPAC_ACK_TIMEOUT_MS    1000
+
+/**
+ * enum npac_acl_cmd - IOCTL command numbers for NPAC ACL operations.
+ */
+enum npac_acl_cmd {
+       /** @NPAC_ACL_ADD_RULE: Add an ACL rule. */
+       NPAC_ACL_ADD_RULE,
+
+       /** @NPAC_ACL_DELETE_RULE: Delete an ACL rule. */
+       NPAC_ACL_DELETE_RULE,
+};
+
+/**
+ * enum npac_acl_match_type - Match field selector for an ACL rule.
+ */
+enum npac_acl_match_type {
+       /** @NPAC_ACL_MATCH_SRC_IPV4: Match on source IPv4 address. */
+       NPAC_ACL_MATCH_SRC_IPV4 = 1,
+
+       /** @NPAC_ACL_MATCH_DST_IPV4: Match on destination IPv4 address. */
+       NPAC_ACL_MATCH_DST_IPV4,
+
+       /** @NPAC_ACL_MATCH_SRC_MAC: Match on source MAC address. */
+       NPAC_ACL_MATCH_SRC_MAC,
+
+       /** @NPAC_ACL_MATCH_DST_MAC: Match on destination MAC address. */
+       NPAC_ACL_MATCH_DST_MAC,
+};
+
+/**
+ * enum npac_acl_verdict - Action to apply when an ACL rule matches.
+ */
+enum npac_acl_verdict {
+       /** @NPAC_ACL_VERDICT_ALLOW: Accept the matched packet. */
+       NPAC_ACL_VERDICT_ALLOW = 1,
+
+       /** @NPAC_ACL_VERDICT_DROP: Drop the matched packet. */
+       NPAC_ACL_VERDICT_DROP,
+};
+
+/* Sized for the largest match field: a 6-byte MAC address. */
+#define NPAC_ACL_MATCH_VALUE_MAX       6
+
+struct npac_acl_offload_rule {
+       __u32 match_type;
+       __u32 verdict;
+       __u8  match_value[NPAC_ACL_MATCH_VALUE_MAX];
+};
+
+#define DRM_IOCTL_NPAC_ACL_ADD_RULE \
+       DRM_IOWR(DRM_COMMAND_BASE + NPAC_ACL_ADD_RULE, struct 
npac_acl_offload_rule)
+
+#define DRM_IOCTL_NPAC_ACL_DELETE_RULE \
+       DRM_IOWR(DRM_COMMAND_BASE + NPAC_ACL_DELETE_RULE, struct 
npac_acl_offload_rule)
+
+struct npac_device {
+       struct drm_device drm;
+       struct rpmsg_device *rpdev;
+       struct mutex req_lock; /* lock for request */
+       struct completion ack;
+};
+
+static inline struct npac_device *drm_to_npac_device(struct drm_device *dev)
+{
+       return container_of(dev, struct npac_device, drm);
+}
+
+static int npac_add_rule_ioctl(struct drm_device *dev, void *data,
+                              struct drm_file *file)
+{
+       struct npac_device *npac = drm_to_npac_device(dev);
+       struct npac_acl_offload_rule *rule = data;
+       int ret;
+
+       dev_dbg(dev->dev, "add rule ioctl received\n");
+
+       mutex_lock(&npac->req_lock);
+       reinit_completion(&npac->ack);
+       ret = rpmsg_send(npac->rpdev->ept, rule, sizeof(*rule));
+       if (ret) {
+               dev_err(dev->dev, "rpmsg_send failed: %d\n", ret);
+               mutex_unlock(&npac->req_lock);
+               return ret;
+       }
+       if (!wait_for_completion_timeout(&npac->ack,
+                                        
msecs_to_jiffies(NPAC_ACK_TIMEOUT_MS))) {
+               dev_err(dev->dev, "ack timed out\n");
+               mutex_unlock(&npac->req_lock);
+               return -ETIMEDOUT;
+       }
+       mutex_unlock(&npac->req_lock);
+       return 0;
+}
+
+static int npac_delete_rule_ioctl(struct drm_device *dev, void *data,
+                                 struct drm_file *file)
+{
+       struct npac_device *npac = drm_to_npac_device(dev);
+       struct npac_acl_offload_rule *rule = data;
+       int ret;
+
+       dev_dbg(dev->dev, "delete rule ioctl received\n");
+
+       mutex_lock(&npac->req_lock);
+       reinit_completion(&npac->ack);
+       ret = rpmsg_send(npac->rpdev->ept, rule, sizeof(*rule));
+       if (ret) {
+               dev_err(dev->dev, "rpmsg_send failed: %d\n", ret);
+               mutex_unlock(&npac->req_lock);
+               return ret;
+       }
+       if (!wait_for_completion_timeout(&npac->ack,
+                                        
msecs_to_jiffies(NPAC_ACK_TIMEOUT_MS))) {
+               dev_err(dev->dev, "ack timed out\n");
+               mutex_unlock(&npac->req_lock);
+               return -ETIMEDOUT;
+       }
+       mutex_unlock(&npac->req_lock);
+       return 0;
+}
+
+static const struct drm_ioctl_desc npac_drm_ioctls[] = {
+       DRM_IOCTL_DEF_DRV(NPAC_ACL_ADD_RULE,    npac_add_rule_ioctl,    
DRM_ROOT_ONLY),
+       DRM_IOCTL_DEF_DRV(NPAC_ACL_DELETE_RULE, npac_delete_rule_ioctl, 
DRM_ROOT_ONLY),
+};
+
+DEFINE_DRM_ACCEL_FOPS(npac_fops);
+
+static const struct drm_driver npac_drm_driver = {
+       .driver_features        = DRIVER_COMPUTE_ACCEL,
+       .ioctls                 = npac_drm_ioctls,
+       .num_ioctls             = ARRAY_SIZE(npac_drm_ioctls),
+       .fops                   = &npac_fops,
+       .name                   = "npac",
+       .desc                   = "NPAC Client Driver",
+};
+
+static int npac_cb(struct rpmsg_device *rpdev, void *data, int len,
+                  void *priv, u32 src)
+{
+       struct npac_device *npac = dev_get_drvdata(&rpdev->dev);
+
+       dev_dbg(&rpdev->dev, "ack received\n");
+       complete(&npac->ack);
+       return 0;
+}
+
+static int npac_probe(struct rpmsg_device *rpdev)
+{
+       struct npac_device *npac;
+       int ret;
+
+       npac = devm_drm_dev_alloc(&rpdev->dev, &npac_drm_driver,
+                                 struct npac_device, drm);
+       if (IS_ERR(npac))
+               return PTR_ERR(npac);
+
+       npac->rpdev = rpdev;
+       mutex_init(&npac->req_lock);
+       init_completion(&npac->ack);
+       dev_set_drvdata(&rpdev->dev, npac);
+
+       ret = drm_dev_register(&npac->drm, 0);
+       if (ret) {
+               dev_err(&rpdev->dev, "Failed to register DRM accel device: 
%d\n", ret);
+               return ret;
+       }
+
+       dev_info(&rpdev->dev, "NPAC Client driver probed\n");
+       return 0;
+}
+
+static void npac_remove(struct rpmsg_device *rpdev)
+{
+       struct npac_device *npac = dev_get_drvdata(&rpdev->dev);
+
+       drm_dev_unplug(&npac->drm);
+
+       dev_info(&rpdev->dev, "NPAC Client Driver removed\n");
+}
+
+static struct rpmsg_device_id npac_id_table[] = {
+       { .name = NPAC_RPMSG_SERVICE_NAME },
+       {},
+};
+MODULE_DEVICE_TABLE(rpmsg, npac_id_table);
+
+static struct rpmsg_driver npac_driver = {
+       .drv.name       = KBUILD_MODNAME,
+       .id_table       = npac_id_table,
+       .probe          = npac_probe,
+       .callback       = npac_cb,
+       .remove         = npac_remove,
+};
+
+module_rpmsg_driver(npac_driver);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("NPAC Client Driver");
+MODULE_AUTHOR("Jaspinder Budhal <[email protected]>");
-- 
2.34.1

Reply via email to