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