lists.openwall.net   lists  /  announce  owl-users  owl-dev  john-users  john-dev  passwdqc-users  yescrypt  popa3d-users  /  oss-security  kernel-hardening  musl  sabotage  tlsify  passwords  /  crypt-dev  xvendor  /  Bugtraq  Full-Disclosure  linux-kernel  linux-netdev  linux-ext4  linux-hardening  linux-cve-announce  PHC 
Open Source and information security mailing list archives
 
Hash Suite: Windows password security audit tool. GUI, reports in PDF.
[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-ID: <20250919195132.1088515-2-xiangrongl@nvidia.com>
Date: Fri, 19 Sep 2025 19:51:30 +0000
From: Ron Li <xiangrongl@...dia.com>
To: <hdegoede@...hat.com>, <ilpo.jarvinen@...ux.intel.com>,
	<vadimp@...dia.com>, <alok.a.tiwari@...cle.com>, <kblaiech@...dia.com>,
	<davthompson@...dia.com>
CC: <platform-driver-x86@...r.kernel.org>, <linux-kernel@...r.kernel.org>,
	<linux-crypto@...r.kernel.org>, Ron Li <xiangrongl@...dia.com>
Subject: [PATCH v3 1/3] platform/mellanox/mlxbf_pka: Add core BlueField PKA platform driver

Add the initial mlxbf_pka driver to support the BlueField DPU Public Key
Acceleration (PKA) hardware. The PKA provides a simple, complete framework
for crypto public key hardware offload. It supports direct access to the
public key hardware resources from the user space, and makes available
several arithmetic operations: some basic operations (e.g., addition and
multiplication), some complex operations (e.g., modular exponentiation and
modular inversion), and high-level operations such as RSA, Diffie-Hellman,
Elliptic Curve Cryptography, and the Federal Digital Signature Algorithm
(DSA as documented in FIPS-186) public-private key systems.

This patch wires up the platform driver and the device/shim layer:

- ACPI matching for BF1/BF2/BF3 (MLNXBF10/20/51)
- Probe flow to verify boot status, discover EIP154, Window RAM, alt Window RAM,
  and CSR resources
- Register a PKA "shim" via the device layer, which maps resources and tracks
  per-shim state
- Provide shim lifecycle helpers (register/unregister)

This driver is placed under drivers/platform/mellanox as it exposes platform
resources and does not provide in-kernel crypto offload.

Reviewed-by: David Thompson <davthompson@...dia.com>
Reviewed-by: Khalil Blaiech <kblaiech@...dia.com>
Signed-off-by: Ron Li <xiangrongl@...dia.com>
---
 .../ABI/testing/sysfs-platform-mellanox-pka   |  16 +
 MAINTAINERS                                   |   8 +
 drivers/platform/mellanox/Kconfig             |  10 +
 drivers/platform/mellanox/Makefile            |   1 +
 drivers/platform/mellanox/mlxbf_pka/Makefile  |  10 +
 .../mellanox/mlxbf_pka/mlxbf_pka_dev.c        | 395 +++++++++++++++++
 .../mellanox/mlxbf_pka/mlxbf_pka_dev.h        | 298 +++++++++++++
 .../mellanox/mlxbf_pka/mlxbf_pka_drv.c        | 413 ++++++++++++++++++
 8 files changed, 1151 insertions(+)
 create mode 100644 Documentation/ABI/testing/sysfs-platform-mellanox-pka
 create mode 100644 drivers/platform/mellanox/mlxbf_pka/Makefile
 create mode 100644 drivers/platform/mellanox/mlxbf_pka/mlxbf_pka_dev.c
 create mode 100644 drivers/platform/mellanox/mlxbf_pka/mlxbf_pka_dev.h
 create mode 100644 drivers/platform/mellanox/mlxbf_pka/mlxbf_pka_drv.c

diff --git a/Documentation/ABI/testing/sysfs-platform-mellanox-pka b/Documentation/ABI/testing/sysfs-platform-mellanox-pka
new file mode 100644
index 000000000000..cf8dd292c781
--- /dev/null
+++ b/Documentation/ABI/testing/sysfs-platform-mellanox-pka
@@ -0,0 +1,16 @@
+What:		/sys/devices/platform/<pka-device-id>/<pka-ring-device-id>
+Date:		Oct 2025
+KernelVersion:	6.18
+Contact:	"Ron Li <xiangrongl@...dia.com>"
+Description:
+		The mlxbf_pka driver to support the BlueField SoC Public Key Acceleration (PKA)
+		hardware. It supports direct access to the public key hardware resources from the
+		user space.
+
+		There are three PKA device IDs that support different BlueField product:
+
+		===========  ==============================================
+		BlueField-1  MLNXBF10:xx, where xx ranges from '00' to '03'
+		BlueField-2  MLNXBF20:xx, where xx ranges from '00' to '07'
+		BlueField-3  MLNXBF51:xx, where xx ranges from '00' to '17'
+		===========  ==============================================
diff --git a/MAINTAINERS b/MAINTAINERS
index f6206963efbf..64b995b16d1a 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -15755,6 +15755,14 @@ L:	linux-i2c@...r.kernel.org
 S:	Supported
 F:	drivers/i2c/busses/i2c-mlxbf.c
 
+MELLANOX BLUEFIELD PKA DRIVER
+M:	Ron Li <xiangrongl@...dia.com>
+M:	Khalil Blaiech <kblaiech@...dia.com>
+L:	platform-driver-x86@...r.kernel.org
+S:	Supported
+F:	Documentation/ABI/testing/sysfs-platform-mellanox-pka
+F:	drivers/platform/mellanox/mlxbf_pka/mlxbf_pka_*
+
 MELLANOX ETHERNET DRIVER (mlx4_en)
 M:	Tariq Toukan <tariqt@...dia.com>
 L:	netdev@...r.kernel.org
diff --git a/drivers/platform/mellanox/Kconfig b/drivers/platform/mellanox/Kconfig
index e3afbe62c7f6..b09681c137e6 100644
--- a/drivers/platform/mellanox/Kconfig
+++ b/drivers/platform/mellanox/Kconfig
@@ -121,4 +121,14 @@ config NVSW_SN2201
 	  C3338R which is one of the Denverton product families.
 	  System equipped with Nvidia®Spectrum-1 32x100GbE Ethernet switch.
 
+config MLXBF_PKA
+        tristate "Mellanox BlueField Public Key Accelerator driver"
+        depends on ARM64 && ACPI
+        help
+          If you say yes to this option, support will be included for the
+          Public Key Accelerator device on Mellanox BlueField SoCs.
+
+          This driver can also be built as a module. If so, the module will
+          be called pka-mlxbf.
+
 endif # MELLANOX_PLATFORM
diff --git a/drivers/platform/mellanox/Makefile b/drivers/platform/mellanox/Makefile
index e86723b44c2e..a6535959cde4 100644
--- a/drivers/platform/mellanox/Makefile
+++ b/drivers/platform/mellanox/Makefile
@@ -5,6 +5,7 @@
 #
 obj-$(CONFIG_MLX_PLATFORM)	+= mlx-platform.o
 obj-$(CONFIG_MLXBF_BOOTCTL)	+= mlxbf-bootctl.o
+obj-$(CONFIG_MLXBF_PKA)		+= mlxbf_pka/
 obj-$(CONFIG_MLXBF_PMC)		+= mlxbf-pmc.o
 obj-$(CONFIG_MLXBF_TMFIFO)	+= mlxbf-tmfifo.o
 obj-$(CONFIG_MLXREG_DPU)	+= mlxreg-dpu.o
diff --git a/drivers/platform/mellanox/mlxbf_pka/Makefile b/drivers/platform/mellanox/mlxbf_pka/Makefile
new file mode 100644
index 000000000000..67465a63edb8
--- /dev/null
+++ b/drivers/platform/mellanox/mlxbf_pka/Makefile
@@ -0,0 +1,10 @@
+# SPDX-License-Identifier: GPL-2.0-only OR BSD-3-Clause
+# SPDX-FileCopyrightText: Copyright (c) 2025 NVIDIA CORPORATION. All rights reserved.
+#
+# Mellanox BlueField PKA Driver
+#
+
+obj-$(CONFIG_MLXBF_PKA) += mlxbf-pka.o
+
+mlxbf-pka-objs := mlxbf_pka_drv.o
+mlxbf-pka-objs += mlxbf_pka_dev.o
diff --git a/drivers/platform/mellanox/mlxbf_pka/mlxbf_pka_dev.c b/drivers/platform/mellanox/mlxbf_pka/mlxbf_pka_dev.c
new file mode 100644
index 000000000000..0a5db1be6eaa
--- /dev/null
+++ b/drivers/platform/mellanox/mlxbf_pka/mlxbf_pka_dev.c
@@ -0,0 +1,395 @@
+// SPDX-License-Identifier: GPL-2.0-only OR BSD-3-Clause
+// SPDX-FileCopyrightText: Copyright (c) 2025 NVIDIA CORPORATION. All rights reserved.
+
+#include <linux/bitfield.h>
+#include <linux/compiler.h>
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/io.h>
+#include <linux/iopoll.h>
+#include <linux/ioport.h>
+#include <linux/kernel.h>
+#include <linux/math.h>
+#include <linux/slab.h>
+#include <linux/string.h>
+#include <linux/time.h>
+#include <linux/timex.h>
+#include <linux/types.h>
+
+#include "mlxbf_pka_dev.h"
+
+struct mlxbf_pka_dev_gbl_config_t mlxbf_pka_gbl_config;
+
+/* Global PKA shim resource info table. */
+static struct mlxbf_pka_dev_gbl_shim_res_info_t mlxbf_pka_gbl_res_tbl[MLXBF_PKA_MAX_NUM_IO_BLOCKS];
+
+/* Add the resource to the global resource table. */
+static int mlxbf_pka_dev_add_resource(struct mlxbf_pka_dev_res_t *res_ptr, u32 shim_idx)
+{
+	u8 res_cnt;
+
+	res_cnt = mlxbf_pka_gbl_res_tbl[shim_idx].res_cnt;
+	if (res_cnt >= MLXBF_PKA_DEV_SHIM_RES_CNT)
+		return -ENOMEM;
+
+	mlxbf_pka_gbl_res_tbl[shim_idx].res_tbl[res_cnt] = res_ptr;
+	mlxbf_pka_gbl_res_tbl[shim_idx].res_cnt++;
+
+	return 0;
+}
+
+/* Remove the resource from the global resource table. */
+static int mlxbf_pka_dev_put_resource(struct mlxbf_pka_dev_res_t *res, u32 shim_idx)
+{
+	struct mlxbf_pka_dev_res_t *res_ptr;
+	u8 res_idx;
+
+	for (res_idx = 0; res_idx < MLXBF_PKA_DEV_SHIM_RES_CNT; res_idx++) {
+		res_ptr = mlxbf_pka_gbl_res_tbl[shim_idx].res_tbl[res_idx];
+		if (!res_ptr || strcmp(res_ptr->name, res->name))
+			continue;
+
+		mlxbf_pka_gbl_res_tbl[shim_idx].res_tbl[res_idx] = NULL;
+		mlxbf_pka_gbl_res_tbl[shim_idx].res_cnt--;
+		break;
+	}
+
+	/*
+	 * Check whether the resource shares the same memory map; If so, the memory
+	 * map shouldn't be released.
+	 */
+	for (res_idx = 0; res_idx < MLXBF_PKA_DEV_SHIM_RES_CNT; res_idx++) {
+		res_ptr = mlxbf_pka_gbl_res_tbl[shim_idx].res_tbl[res_idx];
+		if (res_ptr && res_ptr->base == res->base)
+			return -EBUSY;
+	}
+
+	return 0;
+}
+
+static void __iomem *mlxbf_pka_dev_get_resource_ioaddr(u64 res_base, u32 shim_idx)
+{
+	struct mlxbf_pka_dev_res_t *res_ptr;
+	u8 res_cnt, res_idx;
+
+	res_cnt = mlxbf_pka_gbl_res_tbl[shim_idx].res_cnt;
+	if (!res_cnt)
+		return NULL;
+
+	for (res_idx = 0; res_idx < res_cnt; res_idx++) {
+		res_ptr = mlxbf_pka_gbl_res_tbl[shim_idx].res_tbl[res_idx];
+		if (res_ptr->base == res_base)
+			return res_ptr->ioaddr;
+	}
+
+	return NULL;
+}
+
+/* Set PKA device resource config and map io memory if needed. */
+static int mlxbf_pka_dev_set_resource_config(struct device *dev,
+					     struct mlxbf_pka_dev_shim_s *shim,
+					     struct mlxbf_pka_dev_res_t *res_ptr,
+					     u64 res_base,
+					     u64 res_size,
+					     u64 res_type,
+					     char *res_name)
+{
+	if (res_ptr->status == MLXBF_PKA_DEV_RES_STATUS_MAPPED)
+		return -EPERM;
+
+	switch (res_type) {
+	case MLXBF_PKA_DEV_RES_TYPE_REG:
+		res_ptr->base = res_base;
+		break;
+	case MLXBF_PKA_DEV_RES_TYPE_MEM:
+		res_ptr->base = shim->mem_res.eip154_base + res_base;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	res_ptr->size = res_size;
+	res_ptr->type = res_type;
+	res_ptr->name = res_name;
+	res_ptr->status = MLXBF_PKA_DEV_RES_STATUS_UNMAPPED;
+	res_ptr->ioaddr = mlxbf_pka_dev_get_resource_ioaddr(res_ptr->base, shim->shim_id);
+	if (!res_ptr->ioaddr) {
+		if (!devm_request_mem_region(dev, res_ptr->base, res_ptr->size, res_ptr->name)) {
+			dev_err(dev, "failed to get io memory region\n");
+			return -EPERM;
+		}
+
+		res_ptr->ioaddr = devm_ioremap(dev, res_ptr->base, res_ptr->size);
+		if (!res_ptr->ioaddr) {
+			dev_err(dev, "unable to map io memory into CPU space\n");
+			return -ENOMEM;
+		}
+	}
+
+	res_ptr->status = MLXBF_PKA_DEV_RES_STATUS_MAPPED;
+
+	if (!res_ptr->ioaddr || mlxbf_pka_dev_add_resource(res_ptr, shim->shim_id)) {
+		dev_err(dev, "unable to map io memory\n");
+		return -ENOMEM;
+	}
+
+	return 0;
+}
+
+/* Unset PKA device resource config - unmap io memory if needed. */
+void mlxbf_pka_dev_unset_resource_config(struct device *dev,
+					 struct mlxbf_pka_dev_shim_s *shim,
+					 struct mlxbf_pka_dev_res_t *res_ptr)
+{
+	if (res_ptr->status != MLXBF_PKA_DEV_RES_STATUS_MAPPED)
+		return;
+
+	if (!res_ptr->ioaddr)
+		return;
+
+	if (-EBUSY == mlxbf_pka_dev_put_resource(res_ptr, shim->shim_id))
+		return;
+
+	dev_dbg(dev, "PKA device resource released\n");
+	res_ptr->status = MLXBF_PKA_DEV_RES_STATUS_UNMAPPED;
+}
+
+/*
+ * mlxbf_pka_dev_create_shim - Create shim.
+ *
+ * Set shim parameters and configure shim resources.
+ *
+ * Return: 0 on success, a negative error code on failure.
+ */
+static int mlxbf_pka_dev_create_shim(struct device *dev,
+				     struct mlxbf_pka_dev_shim_s *shim,
+				     u32 shim_id,
+				     u8 split,
+				     struct mlxbf_pka_dev_mem_res *mem_res)
+{
+	u64 reg_base;
+	u64 reg_size;
+	int ret;
+
+	if (shim->status == MLXBF_PKA_SHIM_STATUS_CREATED)
+		return 0;
+
+	if (shim->status != MLXBF_PKA_SHIM_STATUS_UNDEFINED) {
+		dev_err(dev, "PKA device must be undefined\n");
+		return -EPERM;
+	}
+
+	if (shim_id > MLXBF_PKA_MAX_NUM_IO_BLOCKS - 1) {
+		dev_err(dev, "invalid shim identifier\n");
+		return -EINVAL;
+	}
+
+	shim->shim_id = shim_id;
+	shim->mem_res = *mem_res;
+
+	if (split)
+		shim->window_ram_split = MLXBF_PKA_SHIM_WINDOW_RAM_SPLIT_ENABLED;
+	else
+		shim->window_ram_split = MLXBF_PKA_SHIM_WINDOW_RAM_SPLIT_DISABLED;
+
+	/* Set PKA device Buffer RAM config. */
+	ret = mlxbf_pka_dev_set_resource_config(dev,
+						shim,
+						&shim->resources.buffer_ram,
+						MLXBF_PKA_BUFFER_RAM_BASE,
+						MLXBF_PKA_BUFFER_RAM_SIZE,
+						MLXBF_PKA_DEV_RES_TYPE_MEM,
+						"MLXBF_PKA_BUFFER_RAM");
+	if (ret) {
+		dev_err(dev, "unable to set Buffer RAM config\n");
+		return ret;
+	}
+
+	/* Set PKA device Master Controller register. */
+	reg_size = PAGE_SIZE;
+	reg_base = mlxbf_pka_dev_get_register_base(shim->mem_res.eip154_base,
+						   MLXBF_PKA_MASTER_SEQ_CTRL_ADDR);
+	ret = mlxbf_pka_dev_set_resource_config(dev,
+						shim,
+						&shim->resources.master_seq_ctrl,
+						reg_base,
+						reg_size,
+						MLXBF_PKA_DEV_RES_TYPE_REG,
+						"MLXBF_PKA_MASTER_SEQ_CTRL");
+	if (ret) {
+		dev_err(dev, "unable to set Master Controller register config\n");
+		return ret;
+	}
+
+	/* Set PKA device AIC registers. */
+	reg_size = PAGE_SIZE;
+	reg_base = mlxbf_pka_dev_get_register_base(shim->mem_res.eip154_base,
+						   MLXBF_PKA_AIC_POL_CTRL_ADDR);
+	ret = mlxbf_pka_dev_set_resource_config(dev,
+						shim,
+						&shim->resources.aic_csr,
+						reg_base,
+						reg_size,
+						MLXBF_PKA_DEV_RES_TYPE_REG,
+						"MLXBF_PKA_AIC_CSR");
+	if (ret) {
+		dev_err(dev, "unable to set AIC registers config\n");
+		return ret;
+	}
+
+	shim->status = MLXBF_PKA_SHIM_STATUS_CREATED;
+
+	return ret;
+}
+
+/* Delete shim and unset shim resources. */
+static int mlxbf_pka_dev_delete_shim(struct device *dev, struct mlxbf_pka_dev_shim_s *shim)
+{
+	struct mlxbf_pka_dev_res_t *res_master_seq_ctrl, *res_aic_csr;
+	struct mlxbf_pka_dev_res_t *res_buffer_ram;
+
+	dev_dbg(dev, "PKA device delete shim\n");
+
+	if (shim->status == MLXBF_PKA_SHIM_STATUS_UNDEFINED)
+		return 0;
+
+	if (shim->status != MLXBF_PKA_SHIM_STATUS_FINALIZED &&
+	    shim->status != MLXBF_PKA_SHIM_STATUS_CREATED) {
+		dev_err(dev, "PKA device status must be finalized\n");
+		return -EPERM;
+	}
+
+	res_buffer_ram = &shim->resources.buffer_ram;
+	res_master_seq_ctrl = &shim->resources.master_seq_ctrl;
+	res_aic_csr = &shim->resources.aic_csr;
+
+	mlxbf_pka_dev_unset_resource_config(dev, shim, res_buffer_ram);
+	mlxbf_pka_dev_unset_resource_config(dev, shim, res_master_seq_ctrl);
+	mlxbf_pka_dev_unset_resource_config(dev, shim, res_aic_csr);
+
+	shim->status = MLXBF_PKA_SHIM_STATUS_UNDEFINED;
+
+	return 0;
+}
+
+/*
+ * Initialize PKA IO block referred to as shim. It configures shim's
+ * parameters and prepares resources by mapping corresponding memory. The
+ * function also configures shim registers and loads firmware to shim
+ * internal rams. The struct mlxbf_pka_dev_shim_s passed as input is also
+ * an output. It returns 0 on success, a negative error code on failure.
+ */
+static int mlxbf_pka_dev_init_shim(struct device *dev, struct mlxbf_pka_dev_shim_s *shim)
+{
+	int ret;
+
+	if (shim->status != MLXBF_PKA_SHIM_STATUS_CREATED) {
+		dev_err(dev, "PKA device must be created\n");
+		return -EPERM;
+	}
+
+	ret = devm_mutex_init(dev, &shim->mutex);
+	if (ret)
+		return ret;
+
+	shim->status = MLXBF_PKA_SHIM_STATUS_INITIALIZED;
+
+	return ret;
+}
+
+/* Release a given shim. */
+static int mlxbf_pka_dev_release_shim(struct device *dev, struct mlxbf_pka_dev_shim_s *shim)
+{
+	int ret = 0;
+
+	if (shim->status != MLXBF_PKA_SHIM_STATUS_INITIALIZED &&
+	    shim->status != MLXBF_PKA_SHIM_STATUS_STOPPED) {
+		dev_err(dev, "PKA device must be initialized or stopped\n");
+		return -EPERM;
+	}
+
+	shim->status = MLXBF_PKA_SHIM_STATUS_FINALIZED;
+
+	return ret;
+}
+
+/* Return the shim associated with the given identifier. */
+struct mlxbf_pka_dev_shim_s *mlxbf_pka_dev_get_shim(u32 shim_id)
+{
+	return mlxbf_pka_gbl_config.dev_shims[shim_id];
+}
+
+int mlxbf_pka_dev_register_shim(struct device *dev,
+				u32 shim_id,
+				struct mlxbf_pka_dev_mem_res *mem_res,
+				struct mlxbf_pka_dev_shim_s **shim)
+{
+	struct mlxbf_pka_dev_shim_s *shim_ptr;
+	u8 split;
+	int ret;
+
+	if (!shim)
+		return -EINVAL;
+
+	dev_dbg(dev, "register shim id=%u\n", shim_id);
+
+	shim_ptr = devm_kzalloc(dev, sizeof(*shim_ptr), GFP_KERNEL);
+	if (!shim_ptr)
+		return -ENOMEM;
+
+	/*
+	 * Shim state MUST be set to undefined before calling
+	 * 'mlxbf_pka_dev_create_shim' function.
+	 */
+	shim_ptr->status = MLXBF_PKA_SHIM_STATUS_UNDEFINED;
+
+	/* Set the Window RAM user mode. */
+	split = MLXBF_PKA_SPLIT_WINDOW_RAM_MODE;
+
+	/* Create PKA shim. */
+	ret = mlxbf_pka_dev_create_shim(dev, shim_ptr, shim_id, split, mem_res);
+	if (ret) {
+		dev_err(dev, "failed to create shim %u\n", shim_id);
+		mlxbf_pka_dev_delete_shim(dev, shim_ptr);
+		goto exit_create_shim;
+	}
+
+	/* Initialize PKA shim. */
+	ret = mlxbf_pka_dev_init_shim(dev, shim_ptr);
+	if (ret) {
+		dev_err(dev, "failed to init shim %u\n", shim_id);
+		goto exit_init_shim;
+	}
+
+	mlxbf_pka_gbl_config.dev_shims[shim_ptr->shim_id] = shim_ptr;
+	mlxbf_pka_gbl_config.dev_shims_cnt += 1;
+
+	*shim = shim_ptr;
+	return 0;
+
+exit_init_shim:
+	mlxbf_pka_dev_release_shim(dev, shim_ptr);
+
+exit_create_shim:
+	mlxbf_pka_dev_delete_shim(dev, shim_ptr);
+	return ret;
+}
+
+int mlxbf_pka_dev_unregister_shim(struct device *dev, struct mlxbf_pka_dev_shim_s *shim)
+{
+	int ret;
+
+	if (!shim)
+		return -EINVAL;
+
+	mlxbf_pka_gbl_config.dev_shims[shim->shim_id] = NULL;
+	mlxbf_pka_gbl_config.dev_shims_cnt -= 1;
+
+	/* Release shim. */
+	ret = mlxbf_pka_dev_release_shim(dev, shim);
+	if (ret)
+		return ret;
+
+	/* Delete shim. */
+	return mlxbf_pka_dev_delete_shim(dev, shim);
+}
diff --git a/drivers/platform/mellanox/mlxbf_pka/mlxbf_pka_dev.h b/drivers/platform/mellanox/mlxbf_pka/mlxbf_pka_dev.h
new file mode 100644
index 000000000000..df51202f79bd
--- /dev/null
+++ b/drivers/platform/mellanox/mlxbf_pka/mlxbf_pka_dev.h
@@ -0,0 +1,298 @@
+/* SPDX-License-Identifier: GPL-2.0-only OR BSD-3-Clause */
+/* SPDX-FileCopyrightText: Copyright (c) 2025 NVIDIA CORPORATION. All rights reserved. */
+
+#ifndef __MLXBF_PKA_DEV_H__
+#define __MLXBF_PKA_DEV_H__
+
+/*
+ * @file
+ *
+ * API to handle the PKA EIP-154 I/O block (shim). It provides functions and
+ * data structures to initialize and configure the PKA shim. It's the "southband
+ * interface" for communication with PKA hardware resources.
+ */
+
+#include <linux/bitfield.h>
+#include <linux/bits.h>
+#include <linux/ioctl.h>
+#include <linux/mutex.h>
+#include <linux/sizes.h>
+#include <linux/types.h>
+#include <linux/units.h>
+
+#define MASTER_CONTROLLER_OUT_OF_RESET 0
+
+/* PKA address related definitions. */
+
+/*
+ * Global Control Space CSR addresses/offsets. These are accessed from the ARM
+ * as 8 byte reads/writes. However only the bottom 32 bits are implemented.
+ */
+#define MLXBF_PKA_CLK_FORCE_ADDR 0x11C80
+
+/*
+ * Advanced Interrupt Controller CSR addresses/offsets. These are accessed from
+ * the ARM as 8 byte reads/writes. However only the bottom 32 bits are
+ * implemented.
+ */
+#define MLXBF_PKA_AIC_POL_CTRL_ADDR 0x11E00
+
+/*
+ * Control register address/offset. This is accessed from the ARM using 8 byte
+ * reads/writes. However only the bottom 32 bits are implemented.
+ */
+#define MLXBF_PKA_MASTER_SEQ_CTRL_ADDR 0x27F90
+
+/* PKA buffer RAM */
+#define MLXBF_PKA_BUFFER_RAM_BASE 0x00000
+#define MLXBF_PKA_BUFFER_RAM_SIZE SZ_16K
+
+/*
+ * PKA Buffer RAM offsets. These are NOT real CSR's but instead are specific
+ * offset/addresses within the EIP154 MLXBF_PKA_BUFFER_RAM.
+ */
+
+/* Alternate Window RAM size. */
+#define MLXBF_PKA_WINDOW_RAM_REGION_SIZE SZ_16K
+
+/* PKA configuration related definitions. */
+
+/* The maximum number of PKA shims referred to as IO blocks. */
+#define MLXBF_PKA_MAX_NUM_IO_BLOCKS 24
+
+/*
+ * PKA Window RAM parameters.
+ * Define whether to split window RAM during PKA device creation phase.
+ */
+#define MLXBF_PKA_SPLIT_WINDOW_RAM_MODE 0
+
+/* Defines for window RAM partition, valid for 16K memory. */
+#define MLXBF_PKA_WINDOW_RAM_DATA_MEM_SIZE	0x3800 /* 14KB. */
+
+/* Window RAM/Alternate window RAM offset mask for BF1 and BF2. */
+#define MLXBF_PKA_WINDOW_RAM_OFFSET_BF1_BF2_MASK (GENMASK(17, 16) | GENMASK(22, 20))
+
+/* Window RAM/Alternate window RAM offset mask for BF3. */
+#define MLXBF_PKA_WINDOW_RAM_OFFSET_BF3_MASK GENMASK(18, 16)
+
+/*
+ * PKA Master Sequencer Control/Status Register.
+ * Writing '1' to bit [31] puts the Master controller Sequencer in a reset
+ * state. Resetting the Sequencer (in order to load other firmware) should
+ * only be done when the EIP-154 is not performing any operations.
+ */
+#define MLXBF_PKA_MASTER_SEQ_CTRL_RESET BIT(31)
+/*
+ * Writing '1' to bit [30] will reset all Command and Result counters. This bit
+ * is write-only and self clearing and can only be set if the 'Reset' bit [31]
+ * is '1'.
+ */
+#define MLXBF_PKA_MASTER_SEQ_CTRL_CLEAR_COUNTERS BIT(30)
+
+/**
+ * struct mlxbf_pka_dev_res_t - Device resource structure
+ * @ioaddr: The (iore)mapped version of addr, driver internal use
+ * @base: Base address of the device's resource
+ * @size: Size of IO
+ * @type: Type of resource addr points to
+ * @status: Status of the resource
+ * @name: Name of the resource
+ */
+struct mlxbf_pka_dev_res_t {
+	void __iomem *ioaddr;
+	u64 base;
+	u64 size;
+	u8 type;
+	s8 status;
+	char *name;
+};
+
+/* Defines for mlxbf_pka_dev_res->type. */
+#define MLXBF_PKA_DEV_RES_TYPE_MEM	1 /* Resource type is memory. */
+#define MLXBF_PKA_DEV_RES_TYPE_REG	2 /* Resource type is register. */
+
+/* Defines for mlxbf_pka_dev_res->status. */
+#define MLXBF_PKA_DEV_RES_STATUS_MAPPED		1 /* The resource is (iore)mapped. */
+#define MLXBF_PKA_DEV_RES_STATUS_UNMAPPED	-1 /* The resource is unmapped. */
+
+/**
+ * struct mlxbf_pka_dev_shim_res_t - PKA Shim resources structure
+ * @buffer_ram: Buffer RAM
+ * @master_seq_ctrl: Master sequencer controller CSR
+ * @aic_csr: Interrupt controller CSRs
+ */
+struct mlxbf_pka_dev_shim_res_t {
+	struct mlxbf_pka_dev_res_t buffer_ram;
+	struct mlxbf_pka_dev_res_t master_seq_ctrl;
+	struct mlxbf_pka_dev_res_t aic_csr;
+};
+
+/* Number of PKA device resources. */
+#define MLXBF_PKA_DEV_SHIM_RES_CNT 6
+
+/* Platform global shim resource information. */
+struct mlxbf_pka_dev_gbl_shim_res_info_t {
+	struct mlxbf_pka_dev_res_t *res_tbl[MLXBF_PKA_DEV_SHIM_RES_CNT];
+	u8 res_cnt;
+};
+
+/**
+ * struct mlxbf_pka_dev_mem_res - PKA device memory resources
+ * @eip154_base: Base address for EIP154 mmio registers
+ * @eip154_size: EIP154 mmio register region size
+ * @wndw_ram_off_mask: Common offset mask for alt window RAM and window RAM
+ * @wndw_ram_base: Base address for window RAM
+ * @wndw_ram_size: Window RAM region size
+ * @alt_wndw_ram_0_base: Base address for alternate window RAM 0
+ * @alt_wndw_ram_1_base: Base address for alternate window RAM 1
+ * @alt_wndw_ram_2_base: Base address for alternate window RAM 2
+ * @alt_wndw_ram_3_base: Base address for alternate window RAM 3
+ * @alt_wndw_ram_size: Alternate window RAM regions size
+ * @csr_base: Base address for CSR registers
+ * @csr_size: CSR area size
+ */
+struct mlxbf_pka_dev_mem_res {
+	u64 eip154_base;
+	u64 eip154_size;
+
+	u64 wndw_ram_off_mask;
+	u64 wndw_ram_base;
+	u64 wndw_ram_size;
+
+	u64 alt_wndw_ram_0_base;
+	u64 alt_wndw_ram_1_base;
+	u64 alt_wndw_ram_2_base;
+	u64 alt_wndw_ram_3_base;
+	u64 alt_wndw_ram_size;
+
+	u64 csr_base;
+	u64 csr_size;
+};
+
+/**
+ * struct mlxbf_pka_dev_shim_s - PKA Shim structure
+ * @mem_res: Memory resources
+ * @shim_id: Shim identifier
+ * @resources: Shim resources
+ * @window_ram_split: If non-zero, the split window RAM scheme is used
+ * @status: Status of the shim
+ * @mutex: Mutex lock for sharing shim
+ */
+struct mlxbf_pka_dev_shim_s {
+	struct mlxbf_pka_dev_mem_res mem_res;
+	u32 shim_id;
+	struct mlxbf_pka_dev_shim_res_t resources;
+	u8 window_ram_split;
+	s8 status;
+	struct mutex mutex;
+};
+
+/* Defines for mlxbf_pka_dev_shim->status. */
+#define MLXBF_PKA_SHIM_STATUS_UNDEFINED		-1
+#define MLXBF_PKA_SHIM_STATUS_CREATED		1
+#define MLXBF_PKA_SHIM_STATUS_INITIALIZED	2
+#define MLXBF_PKA_SHIM_STATUS_RUNNING		3
+#define MLXBF_PKA_SHIM_STATUS_STOPPED		4
+#define MLXBF_PKA_SHIM_STATUS_FINALIZED		5
+
+/* Defines for mlxbf_pka_dev_shim->window_ram_split. */
+
+/* Window RAM is split into 4 * 16KB blocks. */
+#define MLXBF_PKA_SHIM_WINDOW_RAM_SPLIT_ENABLED 1
+/* Window RAM is not split and occupies 64KB. */
+#define MLXBF_PKA_SHIM_WINDOW_RAM_SPLIT_DISABLED 2
+
+/**
+ * struct mlxbf_pka_dev_gbl_config_t - Platform global configuration structure
+ * @dev_shims_cnt: Number of registered PKA shims
+ * @dev_shims: Table of registered PKA shims
+ */
+struct mlxbf_pka_dev_gbl_config_t {
+	u32 dev_shims_cnt;
+	struct mlxbf_pka_dev_shim_s *dev_shims[MLXBF_PKA_MAX_NUM_IO_BLOCKS];
+};
+
+extern struct mlxbf_pka_dev_gbl_config_t mlxbf_pka_gbl_config;
+
+/*
+ * Processor speed in hertz, used in routines which might be called very early
+ * in boot.
+ */
+static inline u64 mlxbf_pka_early_cpu_speed(void)
+{
+	/*
+	 * Initial guess at our CPU speed.  We set this to be larger than any
+	 * possible real speed, so that any calculated delays will be too long,
+	 * rather than too short.
+	 *
+	 * CPU Freq for High/Bin Chip - 1.255GHz.
+	 */
+	return 1255 * HZ_PER_MHZ;
+}
+
+/* Start a PKA device timer. */
+static inline u64 mlxbf_pka_dev_timer_start_msec(u32 msec)
+{
+	u64 cur_time = get_cycles();
+
+	return cur_time + mlxbf_pka_early_cpu_speed() * msec / MSEC_PER_SEC;
+}
+
+/* Test a PKA device timer for completion. */
+static inline bool mlxbf_pka_dev_timer_done(u64 timer)
+{
+	return get_cycles() >= timer;
+}
+
+/* Return register base address. */
+static inline u64 mlxbf_pka_dev_get_register_base(u64 base, u64 reg_addr)
+{
+	return (base + reg_addr) & PAGE_MASK;
+}
+
+/* Return register offset. */
+static inline u64 mlxbf_pka_dev_get_register_offset(u64 base, u64 reg_addr)
+{
+	return (base + reg_addr) & ~PAGE_MASK;
+}
+
+/* Return word offset within io memory. */
+static inline u64 mlxbf_pka_dev_get_word_offset(u64 mem_base, u64 word_addr, u64 mem_size)
+{
+	return (mem_base + word_addr) & (mem_size - 1);
+}
+
+static inline u64 mlxbf_pka_dev_io_read(void __iomem *mem_ptr, u64 mem_off)
+{
+	return readq_relaxed(mem_ptr + mem_off);
+}
+
+static inline void mlxbf_pka_dev_io_write(void __iomem *mem_ptr, u64 mem_off, u64 value)
+{
+	writeq_relaxed(value, mem_ptr + mem_off);
+}
+
+/*
+ * Shim getter for mlxbf_pka_dev_gbl_config_t structure which holds all system
+ * global configuration. This configuration is shared and common to kernel
+ * device driver associated with PKA hardware.
+ */
+struct mlxbf_pka_dev_shim_s *mlxbf_pka_dev_get_shim(u32 shim_id);
+
+void mlxbf_pka_dev_unset_resource_config(struct device *dev,
+					 struct mlxbf_pka_dev_shim_s *shim,
+					 struct mlxbf_pka_dev_res_t *res_ptr);
+
+/*
+ * Register PKA IO block. This function initializes a shim and configures its
+ * related resources, and returns the error code.
+ */
+int mlxbf_pka_dev_register_shim(struct device *dev,
+				u32 shim_id,
+				struct mlxbf_pka_dev_mem_res *mem_res,
+				struct mlxbf_pka_dev_shim_s **shim);
+
+/* Unregister PKA IO block. */
+int mlxbf_pka_dev_unregister_shim(struct device *dev, struct mlxbf_pka_dev_shim_s *shim);
+
+#endif /* __MLXBF_PKA_DEV_H__ */
diff --git a/drivers/platform/mellanox/mlxbf_pka/mlxbf_pka_drv.c b/drivers/platform/mellanox/mlxbf_pka/mlxbf_pka_drv.c
new file mode 100644
index 000000000000..42bfe30fbe49
--- /dev/null
+++ b/drivers/platform/mellanox/mlxbf_pka/mlxbf_pka_drv.c
@@ -0,0 +1,413 @@
+// SPDX-License-Identifier: GPL-2.0-only OR BSD-3-Clause
+// SPDX-FileCopyrightText: Copyright (c) 2025 NVIDIA CORPORATION. All rights reserved.
+
+#include <linux/acpi.h>
+#include <linux/bitfield.h>
+#include <linux/cdev.h>
+#include <linux/cleanup.h>
+#include <linux/device.h>
+#include <linux/idr.h>
+#include <linux/interrupt.h>
+#include <linux/iommu.h>
+#include <linux/ioport.h>
+#include <linux/kdev_t.h>
+#include <linux/kernel.h>
+#include <linux/miscdevice.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/types.h>
+#include <linux/uaccess.h>
+
+#include "mlxbf_pka_dev.h"
+
+#define MLXBF_PKA_DRIVER_DESCRIPTION		"BlueField PKA driver"
+
+#define MLXBF_PKA_DEVICE_ACPIHID_BF1		"MLNXBF10"
+
+#define MLXBF_PKA_DEVICE_ACPIHID_BF2		"MLNXBF20"
+
+#define MLXBF_PKA_DEVICE_ACPIHID_BF3		"MLNXBF51"
+
+#define MLXBF_PKA_DEVICE_ACCESS_MODE	0666
+#define MLXBF_PKA_DEVICE_RES_CNT	7
+#define MLXBF_PKA_DEVICE_NAME_MAX	14
+
+enum mlxbf_pka_mem_res_idx {
+	MLXBF_PKA_ACPI_EIP154_IDX = 0,
+	MLXBF_PKA_ACPI_WNDW_RAM_IDX,
+	MLXBF_PKA_ACPI_ALT_WNDW_RAM_0_IDX,
+	MLXBF_PKA_ACPI_ALT_WNDW_RAM_1_IDX,
+	MLXBF_PKA_ACPI_ALT_WNDW_RAM_2_IDX,
+	MLXBF_PKA_ACPI_ALT_WNDW_RAM_3_IDX,
+	MLXBF_PKA_ACPI_CSR_IDX
+};
+
+enum mlxbf_pka_plat_type {
+	/* Platform type Bluefield-1. */
+	MLXBF_PKA_PLAT_TYPE_BF1 = 0,
+	/* Platform type Bluefield-2. */
+	MLXBF_PKA_PLAT_TYPE_BF2,
+	/* Platform type Bluefield-3. */
+	MLXBF_PKA_PLAT_TYPE_BF3
+};
+
+struct mlxbf_pka_drv_plat_info {
+	enum mlxbf_pka_plat_type type;
+	u64 wndw_ram_off_mask;
+};
+
+static const struct mlxbf_pka_drv_plat_info mlxbf_pka_bf1_info = {
+	.type = MLXBF_PKA_PLAT_TYPE_BF1,
+	.wndw_ram_off_mask = MLXBF_PKA_WINDOW_RAM_OFFSET_BF1_BF2_MASK,
+};
+
+static const struct mlxbf_pka_drv_plat_info mlxbf_pka_bf2_info = {
+	.type = MLXBF_PKA_PLAT_TYPE_BF2,
+	.wndw_ram_off_mask = MLXBF_PKA_WINDOW_RAM_OFFSET_BF1_BF2_MASK,
+};
+
+static const struct mlxbf_pka_drv_plat_info mlxbf_pka_bf3_info = {
+	.type = MLXBF_PKA_PLAT_TYPE_BF3,
+	.wndw_ram_off_mask = MLXBF_PKA_WINDOW_RAM_OFFSET_BF3_MASK,
+};
+
+static DEFINE_MUTEX(mlxbf_pka_drv_lock);
+
+static u32 mlxbf_pka_device_cnt;
+
+static const char mlxbf_pka_acpihid_bf1[] = MLXBF_PKA_DEVICE_ACPIHID_BF1;
+
+static const char mlxbf_pka_acpihid_bf2[] = MLXBF_PKA_DEVICE_ACPIHID_BF2;
+
+static const char mlxbf_pka_acpihid_bf3[] = MLXBF_PKA_DEVICE_ACPIHID_BF3;
+
+static const struct acpi_device_id mlxbf_pka_drv_acpi_ids[] = {
+	{ MLXBF_PKA_DEVICE_ACPIHID_BF1, (kernel_ulong_t)&mlxbf_pka_bf1_info, 0, 0 },
+	{ MLXBF_PKA_DEVICE_ACPIHID_BF2, (kernel_ulong_t)&mlxbf_pka_bf2_info, 0, 0 },
+	{ MLXBF_PKA_DEVICE_ACPIHID_BF3, (kernel_ulong_t)&mlxbf_pka_bf3_info, 0, 0 },
+	{},
+};
+
+struct mlxbf_pka_info {
+	/* The device this info struct belongs to. */
+	struct device *dev;
+	/* Device name. */
+	const char *name;
+	/* Device ACPI HID. */
+	const char *acpihid;
+	/* Device flags. */
+	u8 flag;
+	struct module *module;
+	/* Optional private data. */
+	void *priv;
+};
+
+/* Defines for mlxbf_pka_info->flags. */
+#define MLXBF_PKA_DRIVER_FLAG_DEVICE 2
+
+struct mlxbf_pka_platdata {
+	struct platform_device *pdev;
+	struct mlxbf_pka_info *info;
+	/* Generic spinlock. */
+	spinlock_t lock;
+};
+
+#define MLXBF_PKA_DRIVER_DEV_MAX MLXBF_PKA_MAX_NUM_IO_BLOCKS
+
+struct mlxbf_pka_device {
+	struct mlxbf_pka_info *info;
+	struct device *device;
+	u32 device_id;
+	struct resource *resource[MLXBF_PKA_DEVICE_RES_CNT];
+	struct mlxbf_pka_dev_shim_s *shim;
+};
+
+static int mlxbf_pka_drv_verify_bootup_status(struct device *dev)
+{
+	const char *bootup_status;
+	int ret;
+
+	ret = device_property_read_string(dev, "bootup_done", &bootup_status);
+	if (ret < 0) {
+		dev_err(dev, "failed to read bootup_done property\n");
+		return ret;
+	}
+
+	if (strcmp(bootup_status, "true")) {
+		dev_err(dev, "device bootup required\n");
+		return -ENODEV;
+	}
+
+	return 0;
+}
+
+static void mlxbf_pka_drv_get_mem_res(struct mlxbf_pka_device *mlxbf_pka_dev,
+				      struct mlxbf_pka_dev_mem_res *mem_res,
+				      u64 wndw_ram_off_mask)
+{
+	enum mlxbf_pka_mem_res_idx acpi_mem_idx;
+
+	acpi_mem_idx = MLXBF_PKA_ACPI_EIP154_IDX;
+	mem_res->wndw_ram_off_mask = wndw_ram_off_mask;
+
+	/* PKA EIP154 MMIO base address. */
+	mem_res->eip154_base = mlxbf_pka_dev->resource[acpi_mem_idx]->start;
+	mem_res->eip154_size = resource_size(mlxbf_pka_dev->resource[acpi_mem_idx]);
+	acpi_mem_idx++;
+
+	/* PKA window RAM base address. */
+	mem_res->wndw_ram_base = mlxbf_pka_dev->resource[acpi_mem_idx]->start;
+	mem_res->wndw_ram_size = resource_size(mlxbf_pka_dev->resource[acpi_mem_idx]);
+	acpi_mem_idx++;
+
+	/*
+	 * PKA alternate window RAM base address.
+	 * Note: the size of all the alt window RAM is the same, depicted by
+	 * 'alt_wndw_ram_size' variable. All alt window RAM resources are read
+	 * here even though not all of them are used currently.
+	 */
+	mem_res->alt_wndw_ram_0_base = mlxbf_pka_dev->resource[acpi_mem_idx]->start;
+	mem_res->alt_wndw_ram_size = resource_size(mlxbf_pka_dev->resource[acpi_mem_idx]);
+
+	if (mem_res->alt_wndw_ram_size != MLXBF_PKA_WINDOW_RAM_REGION_SIZE)
+		dev_warn(mlxbf_pka_dev->device, "alternate Window RAM size from ACPI is wrong.\n");
+
+	acpi_mem_idx++;
+
+	mem_res->alt_wndw_ram_1_base = mlxbf_pka_dev->resource[acpi_mem_idx]->start;
+	acpi_mem_idx++;
+
+	mem_res->alt_wndw_ram_2_base = mlxbf_pka_dev->resource[acpi_mem_idx]->start;
+	acpi_mem_idx++;
+
+	mem_res->alt_wndw_ram_3_base = mlxbf_pka_dev->resource[acpi_mem_idx]->start;
+	acpi_mem_idx++;
+
+	/* PKA CSR base address. */
+	mem_res->csr_base = mlxbf_pka_dev->resource[acpi_mem_idx]->start;
+	mem_res->csr_size = resource_size(mlxbf_pka_dev->resource[acpi_mem_idx]);
+}
+
+/*
+ * Note: this function must be serialized because it calls
+ * 'mlxbf_pka_dev_register_shim' which manipulates common counters for the
+ * PKA devices.
+ */
+static int mlxbf_pka_drv_register_device(struct mlxbf_pka_device *mlxbf_pka_dev,
+					 u64 wndw_ram_off_mask)
+{
+	struct mlxbf_pka_dev_mem_res mem_res;
+	u32 mlxbf_pka_shim_id;
+	int ret;
+
+	/* Assert that the driver lock is held for serialization */
+	lockdep_assert_held(&mlxbf_pka_drv_lock);
+
+	mlxbf_pka_shim_id = mlxbf_pka_dev->device_id;
+
+	mlxbf_pka_drv_get_mem_res(mlxbf_pka_dev, &mem_res, wndw_ram_off_mask);
+
+	ret = mlxbf_pka_dev_register_shim(mlxbf_pka_dev->device,
+					  mlxbf_pka_shim_id,
+					  &mem_res,
+					  &mlxbf_pka_dev->shim);
+	if (ret) {
+		dev_dbg(mlxbf_pka_dev->device, "failed to register shim\n");
+		return ret;
+	}
+
+	return 0;
+}
+
+static int mlxbf_pka_drv_unregister_device(struct mlxbf_pka_device *mlxbf_pka_dev)
+{
+	if (!mlxbf_pka_dev || !mlxbf_pka_dev->shim)
+		return -EINVAL;
+
+	dev_dbg(mlxbf_pka_dev->device, "unregister device shim\n");
+	return mlxbf_pka_dev_unregister_shim(mlxbf_pka_dev->device, mlxbf_pka_dev->shim);
+}
+
+static int mlxbf_pka_drv_probe_device(struct mlxbf_pka_info *info)
+{
+	struct mlxbf_pka_drv_plat_info *plat_info;
+	enum mlxbf_pka_mem_res_idx acpi_mem_idx;
+	struct mlxbf_pka_device *mlxbf_pka_dev;
+	const struct acpi_device_id *aid;
+	struct platform_device *pdev;
+	u64 wndw_ram_off_mask;
+	struct device *dev;
+	int ret;
+
+	if (!info)
+		return -EINVAL;
+
+	dev = info->dev;
+	pdev = to_platform_device(dev);
+
+	mlxbf_pka_dev = devm_kzalloc(dev, sizeof(*mlxbf_pka_dev), GFP_KERNEL);
+	if (!mlxbf_pka_dev)
+		return -ENOMEM;
+
+	scoped_guard(mutex, &mlxbf_pka_drv_lock) {
+		mlxbf_pka_device_cnt += 1;
+		if (mlxbf_pka_device_cnt > MLXBF_PKA_DRIVER_DEV_MAX) {
+			dev_dbg(dev, "cannot support %u devices\n", mlxbf_pka_device_cnt);
+			return -ENOSPC;
+		}
+		mlxbf_pka_dev->device_id = mlxbf_pka_device_cnt - 1;
+	}
+
+	mlxbf_pka_dev->info = info;
+	mlxbf_pka_dev->device = dev;
+	info->flag = MLXBF_PKA_DRIVER_FLAG_DEVICE;
+
+	for (acpi_mem_idx = MLXBF_PKA_ACPI_EIP154_IDX;
+	     acpi_mem_idx < MLXBF_PKA_DEVICE_RES_CNT;
+	     acpi_mem_idx++) {
+		mlxbf_pka_dev->resource[acpi_mem_idx] = platform_get_resource(pdev,
+									      IORESOURCE_MEM,
+									      acpi_mem_idx);
+	}
+
+	/* Verify PKA bootup status. */
+	ret = mlxbf_pka_drv_verify_bootup_status(dev);
+	if (ret)
+		return ret;
+
+	/* Window RAM offset mask is platform dependent. */
+	aid = acpi_match_device(mlxbf_pka_drv_acpi_ids, dev);
+	if (!aid)
+		return -ENODEV;
+
+	plat_info = (struct mlxbf_pka_drv_plat_info *)aid->driver_data;
+	if (!plat_info) {
+		dev_err(dev, "missing platform data\n");
+		return -EINVAL;
+	}
+
+	wndw_ram_off_mask = plat_info->wndw_ram_off_mask;
+
+	scoped_guard(mutex, &mlxbf_pka_drv_lock) {
+		ret = mlxbf_pka_drv_register_device(mlxbf_pka_dev, wndw_ram_off_mask);
+		if (ret) {
+			dev_dbg(dev, "failed to register shim\n");
+			return ret;
+		}
+	}
+
+	info->priv = mlxbf_pka_dev;
+
+	return 0;
+}
+
+static void mlxbf_pka_drv_remove_device(struct platform_device *pdev)
+{
+	struct mlxbf_pka_platdata *priv = platform_get_drvdata(pdev);
+	struct mlxbf_pka_info *info = priv->info;
+	struct mlxbf_pka_device *mlxbf_pka_dev = (struct mlxbf_pka_device *)info->priv;
+
+	if (!mlxbf_pka_dev)
+		return;
+
+	mlxbf_pka_drv_unregister_device(mlxbf_pka_dev);
+}
+
+static int mlxbf_pka_drv_acpi_probe(struct platform_device *pdev, struct mlxbf_pka_info *info)
+{
+	struct device *dev = &pdev->dev;
+	struct acpi_device *adev;
+	int ret;
+
+	if (acpi_disabled)
+		return -ENOENT;
+
+	adev = ACPI_COMPANION(dev);
+	if (!adev) {
+		dev_dbg(dev, "ACPI companion device not found for %s\n", pdev->name);
+		return -ENODEV;
+	}
+
+	info->acpihid = acpi_device_hid(adev);
+	if (WARN_ON(!info->acpihid))
+		return -EINVAL;
+
+	if (!strcmp(info->acpihid, mlxbf_pka_acpihid_bf1) ||
+	    !strcmp(info->acpihid, mlxbf_pka_acpihid_bf2) ||
+	    !strcmp(info->acpihid, mlxbf_pka_acpihid_bf3)) {
+		ret = mlxbf_pka_drv_probe_device(info);
+		if (ret) {
+			dev_dbg(dev, "failed to register device\n");
+			return ret;
+		}
+		dev_info(dev, "device probed\n");
+	}
+
+	return 0;
+}
+
+static int mlxbf_pka_drv_probe(struct platform_device *pdev)
+{
+	struct mlxbf_pka_platdata *priv;
+	struct device *dev = &pdev->dev;
+	struct mlxbf_pka_info *info;
+	int ret;
+
+	priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
+	if (!priv)
+		return -ENOMEM;
+
+	spin_lock_init(&priv->lock);
+	priv->pdev = pdev;
+
+	info = devm_kzalloc(dev, sizeof(*info), GFP_KERNEL);
+	if (!info)
+		return -ENOMEM;
+
+	info->name = pdev->name;
+	info->module = THIS_MODULE;
+	info->dev = dev;
+	priv->info = info;
+
+	platform_set_drvdata(pdev, priv);
+
+	ret = mlxbf_pka_drv_acpi_probe(pdev, info);
+	if (ret) {
+		dev_dbg(dev, "unknown device\n");
+		return ret;
+	}
+
+	return ret;
+}
+
+static void mlxbf_pka_drv_remove(struct platform_device *pdev)
+{
+	struct mlxbf_pka_platdata *priv = platform_get_drvdata(pdev);
+	struct mlxbf_pka_info *info = priv->info;
+
+	if (info->flag == MLXBF_PKA_DRIVER_FLAG_DEVICE) {
+		dev_info(&pdev->dev, "remove PKA device\n");
+		mlxbf_pka_drv_remove_device(pdev);
+	}
+}
+
+MODULE_DEVICE_TABLE(acpi, mlxbf_pka_drv_acpi_ids);
+
+static struct platform_driver mlxbf_pka_drv = {
+	.driver = {
+		   .name = KBUILD_MODNAME,
+		   .acpi_match_table = ACPI_PTR(mlxbf_pka_drv_acpi_ids),
+		  },
+	.probe = mlxbf_pka_drv_probe,
+	.remove = mlxbf_pka_drv_remove,
+};
+
+module_platform_driver(mlxbf_pka_drv);
+MODULE_DESCRIPTION(MLXBF_PKA_DRIVER_DESCRIPTION);
+MODULE_AUTHOR("Ron Li <xiangrongl@...dia.com>");
+MODULE_AUTHOR("Khalil Blaiech <kblaiech@...dia.com>");
+MODULE_AUTHOR("Mahantesh Salimath <mahantesh@...dia.com>");
+MODULE_AUTHOR("Shih-Yi Chen <shihyic@...dia.com>");
+MODULE_LICENSE("Dual BSD/GPL");
-- 
2.34.1


Powered by blists - more mailing lists

Powered by Openwall GNU/*/Linux Powered by OpenVZ