[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-ID: <20250919195132.1088515-4-xiangrongl@nvidia.com>
Date: Fri, 19 Sep 2025 19:51:32 +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 3/3] platform/mellanox/mlxbf_pka: Add TRNG support and hwrng integration
Add True Random Number Generator support to the BlueField PKA driver and
wire it into the HWRNG core to feed kernel entropy (/dev/hwrng).
- Enable TRNG clock and configure hardware
- Run TRNG health tests (monobit/run/poker) and a sanity check
- Implement shutdown-overflow recovery and FRO detune/reenable
- Initialize TRNG during shim bring-up and prefetch initial output
- Provide read path from TRNG output registers
- Register/unregister with the HWRNG core
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 | 3 +-
drivers/platform/mellanox/mlxbf_pka/Makefile | 1 +
.../mellanox/mlxbf_pka/mlxbf_pka_dev.c | 48 +-
.../mellanox/mlxbf_pka/mlxbf_pka_dev.h | 6 +
.../mellanox/mlxbf_pka/mlxbf_pka_drv.c | 75 ++
.../mellanox/mlxbf_pka/mlxbf_pka_trng.c | 874 ++++++++++++++++++
.../mellanox/mlxbf_pka/mlxbf_pka_trng.h | 152 +++
include/uapi/linux/mlxbf-pka.h | 19 +
8 files changed, 1176 insertions(+), 2 deletions(-)
create mode 100644 drivers/platform/mellanox/mlxbf_pka/mlxbf_pka_trng.c
create mode 100644 drivers/platform/mellanox/mlxbf_pka/mlxbf_pka_trng.h
diff --git a/Documentation/ABI/testing/sysfs-platform-mellanox-pka b/Documentation/ABI/testing/sysfs-platform-mellanox-pka
index e7d331bca8fb..d45575dc0d77 100644
--- a/Documentation/ABI/testing/sysfs-platform-mellanox-pka
+++ b/Documentation/ABI/testing/sysfs-platform-mellanox-pka
@@ -30,5 +30,6 @@ Description:
- release(): close the PKA ring device specified by the device ID, and release the
related RAM regions.
- unlocked_ioctl(): make PKA related system calls, including getting ring device or
- RAM region information, clearing PKA ring counter.
+ RAM region information, clearing PKA ring counter and getting random bytes from
+ the TRNG module from the PKA device.
- mmap(): map the PKA ring device to the virtual memory region.
diff --git a/drivers/platform/mellanox/mlxbf_pka/Makefile b/drivers/platform/mellanox/mlxbf_pka/Makefile
index 6e536794d339..4f6df4de9cb7 100644
--- a/drivers/platform/mellanox/mlxbf_pka/Makefile
+++ b/drivers/platform/mellanox/mlxbf_pka/Makefile
@@ -9,3 +9,4 @@ obj-$(CONFIG_MLXBF_PKA) += mlxbf-pka.o
mlxbf-pka-objs := mlxbf_pka_drv.o
mlxbf-pka-objs += mlxbf_pka_dev.o
mlxbf-pka-objs += mlxbf_pka_ring.o
+mlxbf-pka-objs += mlxbf_pka_trng.o
\ No newline at end of file
diff --git a/drivers/platform/mellanox/mlxbf_pka/mlxbf_pka_dev.c b/drivers/platform/mellanox/mlxbf_pka/mlxbf_pka_dev.c
index 12df11dd1eee..f649ae9ef967 100644
--- a/drivers/platform/mellanox/mlxbf_pka/mlxbf_pka_dev.c
+++ b/drivers/platform/mellanox/mlxbf_pka/mlxbf_pka_dev.c
@@ -18,6 +18,7 @@
#include "mlxbf_pka_dev.h"
#include "mlxbf_pka_ring.h"
+#include "mlxbf_pka_trng.h"
struct mlxbf_pka_dev_gbl_config_t mlxbf_pka_gbl_config;
@@ -250,6 +251,22 @@ static int mlxbf_pka_dev_create_shim(struct device *dev,
return ret;
}
+ /* Set PKA device TRNG registers. */
+ reg_size = PAGE_SIZE;
+ reg_base = mlxbf_pka_dev_get_register_base(shim->mem_res.eip154_base,
+ MLXBF_PKA_TRNG_OUTPUT_0_ADDR);
+ ret = mlxbf_pka_dev_set_resource_config(dev,
+ shim,
+ &shim->resources.trng_csr,
+ reg_base,
+ reg_size,
+ MLXBF_PKA_DEV_RES_TYPE_REG,
+ "MLXBF_PKA_TRNG_CSR");
+ if (ret) {
+ dev_err(dev, "unable to setup the TRNG\n");
+ return ret;
+ }
+
shim->status = MLXBF_PKA_SHIM_STATUS_CREATED;
return ret;
@@ -258,7 +275,7 @@ static int mlxbf_pka_dev_create_shim(struct device *dev,
/* 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_master_seq_ctrl, *res_aic_csr, *res_trng_csr;
struct mlxbf_pka_dev_res_t *res_buffer_ram;
dev_dbg(dev, "PKA device delete shim\n");
@@ -275,10 +292,12 @@ static int mlxbf_pka_dev_delete_shim(struct device *dev, struct mlxbf_pka_dev_sh
res_buffer_ram = &shim->resources.buffer_ram;
res_master_seq_ctrl = &shim->resources.master_seq_ctrl;
res_aic_csr = &shim->resources.aic_csr;
+ res_trng_csr = &shim->resources.trng_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);
+ mlxbf_pka_dev_unset_resource_config(dev, shim, res_trng_csr);
shim->status = MLXBF_PKA_SHIM_STATUS_UNDEFINED;
@@ -294,7 +313,9 @@ static int mlxbf_pka_dev_delete_shim(struct device *dev, struct mlxbf_pka_dev_sh
*/
static int mlxbf_pka_dev_init_shim(struct device *dev, struct mlxbf_pka_dev_shim_s *shim)
{
+ u32 data[MLXBF_PKA_TRNG_OUTPUT_CNT];
int ret;
+ u8 i;
if (shim->status != MLXBF_PKA_SHIM_STATUS_CREATED) {
dev_err(dev, "PKA device must be created\n");
@@ -311,6 +332,31 @@ static int mlxbf_pka_dev_init_shim(struct device *dev, struct mlxbf_pka_dev_shim
return ret;
}
+ shim->trng_enabled = MLXBF_PKA_SHIM_TRNG_ENABLED;
+ shim->trng_err_cycle = 0;
+
+ /* Configure the TRNG. */
+ ret = mlxbf_pka_dev_config_trng_drbg(dev,
+ &shim->resources.aic_csr,
+ &shim->resources.trng_csr);
+
+ /*
+ * Pull out data from the content of the TRNG buffer RAM and start the
+ * regeneration of new numbers; read and drop 512 words. The read must
+ * be done over the 4 TRNG_OUTPUT_X registers at a time.
+ */
+ for (i = 0; i < MLXBF_PKA_TRNG_NUM_OF_FOUR_WORD; i++)
+ mlxbf_pka_dev_trng_read(dev, shim, data, sizeof(data));
+
+ if (ret) {
+ /*
+ * Keep running without TRNG since it does not hurt, but notify
+ * users.
+ */
+ dev_err(dev, "failed to configure TRNG\n");
+ shim->trng_enabled = MLXBF_PKA_SHIM_TRNG_DISABLED;
+ }
+
ret = devm_mutex_init(dev, &shim->mutex);
if (ret)
return ret;
diff --git a/drivers/platform/mellanox/mlxbf_pka/mlxbf_pka_dev.h b/drivers/platform/mellanox/mlxbf_pka/mlxbf_pka_dev.h
index 12512850cf79..ba0bbc87c832 100644
--- a/drivers/platform/mellanox/mlxbf_pka/mlxbf_pka_dev.h
+++ b/drivers/platform/mellanox/mlxbf_pka/mlxbf_pka_dev.h
@@ -128,11 +128,13 @@ struct mlxbf_pka_dev_res_t {
* @buffer_ram: Buffer RAM
* @master_seq_ctrl: Master sequencer controller CSR
* @aic_csr: Interrupt controller CSRs
+ * @trng_csr: TRNG module 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;
+ struct mlxbf_pka_dev_res_t trng_csr;
};
/* Number of PKA device resources. */
@@ -180,6 +182,7 @@ struct mlxbf_pka_dev_mem_res {
/**
* struct mlxbf_pka_dev_shim_s - PKA Shim structure
* @mem_res: Memory resources
+ * @trng_err_cycle: TRNG error cycle
* @shim_id: Shim identifier
* @rings_num: Number of supported rings (hardware specific)
* @rings: Pointer to rings which belong to the shim
@@ -188,11 +191,13 @@ struct mlxbf_pka_dev_mem_res {
* @resources: Shim resources
* @window_ram_split: If non-zero, the split window RAM scheme is used
* @busy_ring_num: Number of active rings (rings in busy state)
+ * @trng_enabled: Whether the TRNG engine is enabled
* @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;
+ u64 trng_err_cycle;
u32 shim_id;
u32 rings_num;
struct mlxbf_pka_dev_ring_t **rings;
@@ -201,6 +206,7 @@ struct mlxbf_pka_dev_shim_s {
struct mlxbf_pka_dev_shim_res_t resources;
u8 window_ram_split;
u32 busy_ring_num;
+ u8 trng_enabled;
s8 status;
struct mutex mutex;
};
diff --git a/drivers/platform/mellanox/mlxbf_pka/mlxbf_pka_drv.c b/drivers/platform/mellanox/mlxbf_pka/mlxbf_pka_drv.c
index a009437e4a48..a1ae007ba903 100644
--- a/drivers/platform/mellanox/mlxbf_pka/mlxbf_pka_drv.c
+++ b/drivers/platform/mellanox/mlxbf_pka/mlxbf_pka_drv.c
@@ -6,6 +6,7 @@
#include <linux/cdev.h>
#include <linux/cleanup.h>
#include <linux/device.h>
+#include <linux/hw_random.h>
#include <linux/idr.h>
#include <linux/interrupt.h>
#include <linux/iommu.h>
@@ -22,6 +23,7 @@
#include "mlxbf_pka_dev.h"
#include "mlxbf_pka_ring.h"
+#include "mlxbf_pka_trng.h"
#define MLXBF_PKA_DRIVER_DESCRIPTION "BlueField PKA driver"
@@ -187,6 +189,7 @@ struct mlxbf_pka_device {
u32 device_id;
struct resource *resource[MLXBF_PKA_DEVICE_RES_CNT];
struct mlxbf_pka_dev_shim_s *shim;
+ struct hwrng rng;
};
static int mlxbf_pka_drv_verify_bootup_status(struct device *dev)
@@ -435,6 +438,44 @@ static long mlxbf_pka_drv_ring_ioctl(void *device_data, unsigned int cmd, unsign
} else if (cmd == MLXBF_PKA_CLEAR_RING_COUNTERS) {
return mlxbf_pka_dev_clear_ring_counters(ring_dev->ring);
+ } else if (cmd == MLXBF_PKA_GET_RANDOM_BYTES) {
+ struct mlxbf_pka_dev_trng_info trng_data;
+ struct mlxbf_pka_dev_shim_s *shim;
+ bool trng_present;
+ u32 byte_cnt;
+ int ret;
+
+ shim = ring_dev->ring->shim;
+ ret = copy_from_user(&trng_data,
+ (void __user *)(arg),
+ sizeof(struct mlxbf_pka_dev_trng_info));
+ if (ret) {
+ dev_dbg(ring_dev->device, "failed to copy user request.\n");
+ return -EFAULT;
+ }
+
+ /*
+ * Need byte count which is multiple of 4 as required by the
+ * mlxbf_pka_dev_trng_read() interface.
+ */
+ byte_cnt = round_up(trng_data.count, MLXBF_PKA_TRNG_OUTPUT_CNT);
+
+ u32 *data __free(kfree) = kzalloc(byte_cnt, GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+
+ trng_present = mlxbf_pka_dev_has_trng(shim);
+ if (!trng_present)
+ return -EAGAIN;
+
+ ret = mlxbf_pka_dev_trng_read(ring_dev->device, shim, data, byte_cnt);
+ if (ret) {
+ dev_dbg(ring_dev->device, "TRNG failed %d\n", ret);
+ return ret;
+ }
+
+ ret = copy_to_user((void __user *)(trng_data.data), data, trng_data.count);
+ return ret ? -EFAULT : 0;
}
return -ENOTTY;
@@ -659,6 +700,23 @@ static void mlxbf_pka_drv_unregister_ring_device(struct mlxbf_pka_ring_device *r
mlxbf_pka_dev_unregister_ring(ring_dev->device, ring_dev->ring);
}
+static int mlxbf_pka_drv_rng_read(struct hwrng *rng, void *data, size_t max, bool wait)
+{
+ struct mlxbf_pka_device *mlxbf_pka_dev = container_of(rng, struct mlxbf_pka_device, rng);
+ u32 *buffer = data;
+ int ret;
+
+ ret = mlxbf_pka_dev_trng_read(mlxbf_pka_dev->device, mlxbf_pka_dev->shim, buffer, max);
+ if (ret) {
+ dev_dbg(mlxbf_pka_dev->device,
+ "%s: failed to read random bytes ret=%d",
+ rng->name, ret);
+ return 0;
+ }
+
+ return max;
+}
+
static int mlxbf_pka_drv_probe_device(struct mlxbf_pka_info *info)
{
struct mlxbf_pka_drv_plat_info *plat_info;
@@ -667,6 +725,7 @@ static int mlxbf_pka_drv_probe_device(struct mlxbf_pka_info *info)
const struct acpi_device_id *aid;
struct platform_device *pdev;
u64 wndw_ram_off_mask;
+ struct hwrng *trng;
struct device *dev;
int ret;
@@ -727,6 +786,19 @@ static int mlxbf_pka_drv_probe_device(struct mlxbf_pka_info *info)
}
}
+ /* Setup the TRNG if needed. */
+ if (mlxbf_pka_dev_has_trng(mlxbf_pka_dev->shim)) {
+ trng = &mlxbf_pka_dev->rng;
+ trng->name = pdev->name;
+ trng->read = mlxbf_pka_drv_rng_read;
+
+ ret = hwrng_register(&mlxbf_pka_dev->rng);
+ if (ret) {
+ dev_err(dev, "failed to register trng\n");
+ return ret;
+ }
+ }
+
info->priv = mlxbf_pka_dev;
return 0;
@@ -741,6 +813,9 @@ static void mlxbf_pka_drv_remove_device(struct platform_device *pdev)
if (!mlxbf_pka_dev)
return;
+ if (mlxbf_pka_dev_has_trng(mlxbf_pka_dev->shim))
+ hwrng_unregister(&mlxbf_pka_dev->rng);
+
mlxbf_pka_drv_unregister_device(mlxbf_pka_dev);
}
diff --git a/drivers/platform/mellanox/mlxbf_pka/mlxbf_pka_trng.c b/drivers/platform/mellanox/mlxbf_pka/mlxbf_pka_trng.c
new file mode 100644
index 000000000000..0a0007fb9e31
--- /dev/null
+++ b/drivers/platform/mellanox/mlxbf_pka/mlxbf_pka_trng.c
@@ -0,0 +1,874 @@
+// SPDX-License-Identifier: GPL-2.0-only OR BSD-3-Clause
+// SPDX-FileCopyrightText: Copyright (c) 2025 NVIDIA CORPORATION. All rights reserved.
+
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/iopoll.h>
+
+#include "mlxbf_pka_dev.h"
+#include "mlxbf_pka_ring.h"
+#include "mlxbf_pka_trng.h"
+
+/* Personalization string "NVIDIA-MELLANOX-BLUEFIELD-TRUE_RANDOM_NUMBER_GEN". */
+static u32 mlxbf_pka_trng_drbg_ps_str[] = {
+ 0x4e564944, 0x49412d4d, 0x454c4c41, 0x4e4f582d,
+ 0x424c5545, 0x4649454c, 0x442d5452, 0x55455f52,
+ 0x414e444f, 0x4d5f4e55, 0x4d424552, 0x5f47454e
+};
+
+/* Personalization string for DRBG test. */
+static u32 mlxbf_pka_trng_drbg_test_ps_str[] = {
+ 0x64299d83, 0xc34d7098, 0x5bd1f51d, 0xddccfdc1,
+ 0xdd0455b7, 0x166279e5, 0x0974cb1b, 0x2f2cd100,
+ 0x59a5060a, 0xca79940d, 0xd4e29a40, 0x56b7b779
+};
+
+/* First Entropy string for DRBG test. */
+static u32 mlxbf_pka_trng_drbg_test_etpy_str1[] = {
+ 0xaa6bbcab, 0xef45e339, 0x136ca1e7, 0xbce1c881,
+ 0x9fa37b09, 0x63b53667, 0xb36e0053, 0xa202ed81,
+ 0x4650d90d, 0x8eed6127, 0x666f2402, 0x0dfd3af9
+};
+
+/* Second Entropy string for DRBG test. */
+static u32 mlxbf_pka_trng_drbg_test_etpy_str2[] = {
+ 0x35c1b7a1, 0x0154c52b, 0xd5777390, 0x226a4fdb,
+ 0x5f16080d, 0x06b68369, 0xd0c93d00, 0x3336e27f,
+ 0x1abf2c37, 0xe6ab006c, 0xa4adc6e1, 0x8e1907a2
+};
+
+/* Known answer for DRBG test. */
+static u32 mlxbf_pka_trng_drbg_test_output[] = {
+ 0xb663b9f1, 0x24943e13, 0x80f7dce5, 0xaba1a16f
+};
+
+/* Known answer for poker test. */
+static u64 poker_test_exp_cnt[] = {
+ 0x20f42bf4, 0xaf415f4, 0xf4f4fff4, 0xfff4f4f4
+};
+
+static int mlxbf_pka_dev_config_trng_clk(struct device *dev,
+ struct mlxbf_pka_dev_res_t *aic_csr_ptr)
+{
+ u32 trng_clk_en = 0;
+ void __iomem *csr_reg_ptr;
+ u64 csr_reg_base;
+ u64 csr_reg_off;
+ u64 timer;
+
+ if (aic_csr_ptr->status != MLXBF_PKA_DEV_RES_STATUS_MAPPED ||
+ aic_csr_ptr->type != MLXBF_PKA_DEV_RES_TYPE_REG)
+ return -EPERM;
+
+ dev_dbg(dev, "turn on TRNG clock\n");
+
+ csr_reg_base = aic_csr_ptr->base;
+ csr_reg_ptr = aic_csr_ptr->ioaddr;
+
+ /*
+ * Enable the TRNG clock in MLXBF_PKA_CLK_FORCE. In general, this
+ * register should be left in its default state of all zeroes. Only
+ * when the TRNG is directly controlled via the Host slave interface,
+ * the engine needs to be turned on using the 'trng_clk_on' bit in
+ * this register. In case the TRNG is controlled via internal firmware,
+ * this is not required.
+ */
+ csr_reg_off = mlxbf_pka_dev_get_register_offset(csr_reg_base, MLXBF_PKA_CLK_FORCE_ADDR);
+ mlxbf_pka_dev_io_write(csr_reg_ptr, csr_reg_off, MLXBF_PKA_CLK_FORCE_TRNG_ON);
+ /*
+ * Check whether the system clock for TRNG engine is enabled. The clock
+ * MUST be running to provide access to the TRNG.
+ */
+ timer = mlxbf_pka_dev_timer_start_msec(100);
+ while (!trng_clk_en) {
+ trng_clk_en |= mlxbf_pka_dev_io_read(csr_reg_ptr, csr_reg_off)
+ & MLXBF_PKA_CLK_FORCE_TRNG_ON;
+ if (mlxbf_pka_dev_timer_done(timer)) {
+ dev_dbg(dev, "failed to enable TRNG clock\n");
+ return -ETIMEDOUT;
+ }
+ }
+ dev_dbg(dev, "trng_clk_on is enabled\n");
+
+ return 0;
+}
+
+static bool mlxbf_pka_dev_trng_wait_test_ready(struct device *dev,
+ void __iomem *csr_reg_ptr,
+ u64 csr_reg_base)
+{
+ u64 csr_reg_off, timer, csr_reg_val, test_ready = 0;
+
+ csr_reg_off = mlxbf_pka_dev_get_register_offset(csr_reg_base, MLXBF_PKA_TRNG_STATUS_ADDR);
+ timer = mlxbf_pka_dev_timer_start_msec(MSEC_PER_SEC);
+
+ while (!test_ready) {
+ csr_reg_val = mlxbf_pka_dev_io_read(csr_reg_ptr, csr_reg_off);
+ test_ready = csr_reg_val & MLXBF_PKA_TRNG_STATUS_TEST_READY;
+
+ if (mlxbf_pka_dev_timer_done(timer)) {
+ dev_dbg(dev, "TRNG test ready timer done, 0x%llx\n", csr_reg_val);
+ return false;
+ }
+ }
+
+ return true;
+}
+
+static int mlxbf_pka_dev_trng_enable_test(struct device *dev,
+ void __iomem *csr_reg_ptr,
+ u64 csr_reg_base, u32 test)
+{
+ u64 csr_reg_val, csr_reg_off;
+ int ret;
+
+ /*
+ * Set the 'test_mode' bit in the TRNG_CONTROL register and the
+ * 'test_known_noise' bit in the TRNG_TEST register - this will
+ * immediately set the 'test_ready' bit (in the TRNG_STATUS register)
+ * to indicate that data can be written. It will also reset the
+ * 'monobit test', 'run test' and 'poker test' circuits to their
+ * initial states. Note that the TRNG need not be enabled for this
+ * test.
+ */
+ csr_reg_off = mlxbf_pka_dev_get_register_offset(csr_reg_base, MLXBF_PKA_TRNG_CONTROL_ADDR);
+ csr_reg_val = mlxbf_pka_dev_io_read(csr_reg_ptr, csr_reg_off);
+ csr_reg_off = mlxbf_pka_dev_get_register_offset(csr_reg_base, MLXBF_PKA_TRNG_CONTROL_ADDR);
+
+ mlxbf_pka_dev_io_write(csr_reg_ptr, csr_reg_off,
+ csr_reg_val | MLXBF_PKA_TRNG_CONTROL_TEST_MODE);
+ csr_reg_off = mlxbf_pka_dev_get_register_offset(csr_reg_base, MLXBF_PKA_TRNG_TEST_ADDR);
+ mlxbf_pka_dev_io_write(csr_reg_ptr, csr_reg_off, test);
+ /* Wait until the 'test_ready' bit is set. */
+ csr_reg_off = mlxbf_pka_dev_get_register_offset(csr_reg_base, MLXBF_PKA_TRNG_STATUS_ADDR);
+ ret = read_poll_timeout(mlxbf_pka_dev_io_read, csr_reg_val,
+ csr_reg_val & MLXBF_PKA_TRNG_STATUS_TEST_READY,
+ USEC_PER_MSEC / 100, USEC_PER_SEC, false,
+ csr_reg_ptr, csr_reg_off);
+ if (ret) {
+ dev_err(dev, "timeout waiting for test ready\n");
+ return -ETIMEDOUT;
+ }
+
+ /*
+ * Check whether the 'monobit test', 'run test' and 'poker test'
+ * are reset.
+ */
+ if (csr_reg_val & MLXBF_PKA_TRNG_STATUS_FAIL_MODES) {
+ dev_err(dev, "test bits aren't reset, TRNG_STATUS:0x%llx\n", csr_reg_val);
+ return -EIO;
+ }
+
+ /*
+ * Set 'stall_run_poker' bit to allow inspecting the state of the
+ * result counters which would otherwise be reset immediately for the
+ * next 20,000 bits block to test.
+ */
+ csr_reg_off = mlxbf_pka_dev_get_register_offset(csr_reg_base, MLXBF_PKA_TRNG_ALARMCNT_ADDR);
+ csr_reg_val = mlxbf_pka_dev_io_read(csr_reg_ptr, csr_reg_off);
+ mlxbf_pka_dev_io_write(csr_reg_ptr,
+ csr_reg_off,
+ csr_reg_val | MLXBF_PKA_TRNG_ALARMCNT_STALL_RUN_POKER);
+
+ return 0;
+}
+
+static int mlxbf_pka_dev_trng_test_circuits(struct device *dev,
+ void __iomem *csr_reg_ptr,
+ u64 csr_reg_base,
+ u64 datal, u64 datah,
+ int count, u8 add_half,
+ u64 *monobit_fail_cnt,
+ u64 *run_fail_cnt,
+ u64 *poker_fail_cnt)
+{
+ u64 status, csr_reg_off;
+ unsigned int test_idx;
+ int ret;
+
+ if (!monobit_fail_cnt || !run_fail_cnt || !poker_fail_cnt)
+ return -EINVAL;
+
+ for (test_idx = 0; test_idx < count; test_idx++) {
+ csr_reg_off = mlxbf_pka_dev_get_register_offset(csr_reg_base,
+ MLXBF_PKA_TRNG_RAW_L_ADDR);
+ mlxbf_pka_dev_io_write(csr_reg_ptr, csr_reg_off, datal);
+
+ if (!add_half || test_idx < count - 1) {
+ csr_reg_off = mlxbf_pka_dev_get_register_offset(csr_reg_base,
+ MLXBF_PKA_TRNG_RAW_H_ADDR);
+ mlxbf_pka_dev_io_write(csr_reg_ptr, csr_reg_off, datah);
+ }
+
+ /*
+ * Wait until the 'test_ready' bit in the TRNG_STATUS register
+ * becomes '1' again, signalling readiness for the next 64 bits
+ * of test data. At this point, the previous test data has been
+ * handled so the counter states can be inspected.
+ */
+ csr_reg_off = mlxbf_pka_dev_get_register_offset(csr_reg_base,
+ MLXBF_PKA_TRNG_STATUS_ADDR);
+ ret = read_poll_timeout(mlxbf_pka_dev_io_read, status,
+ status & MLXBF_PKA_TRNG_STATUS_TEST_READY,
+ USEC_PER_MSEC / 100, USEC_PER_SEC, false,
+ csr_reg_ptr, csr_reg_off);
+ if (ret) {
+ dev_err(dev, "timeout waiting for test ready in circuits\n");
+ return -ETIMEDOUT;
+ }
+
+ /* Check test status bits. */
+ csr_reg_off = mlxbf_pka_dev_get_register_offset(csr_reg_base,
+ MLXBF_PKA_TRNG_INTACK_ADDR);
+ if (status & MLXBF_PKA_TRNG_STATUS_MONOBIT_FAIL) {
+ mlxbf_pka_dev_io_write(csr_reg_ptr, csr_reg_off,
+ MLXBF_PKA_TRNG_STATUS_MONOBIT_FAIL);
+ *monobit_fail_cnt += 1;
+ } else if (status & MLXBF_PKA_TRNG_STATUS_RUN_FAIL) {
+ mlxbf_pka_dev_io_write(csr_reg_ptr, csr_reg_off,
+ MLXBF_PKA_TRNG_STATUS_RUN_FAIL);
+ *run_fail_cnt += 1;
+ } else if (status & MLXBF_PKA_TRNG_STATUS_POKER_FAIL) {
+ mlxbf_pka_dev_io_write(csr_reg_ptr, csr_reg_off,
+ MLXBF_PKA_TRNG_STATUS_POKER_FAIL);
+ *poker_fail_cnt += 1;
+ }
+ }
+
+ return *monobit_fail_cnt || *poker_fail_cnt || *run_fail_cnt ? -EIO : 0;
+}
+
+static void mlxbf_pka_dev_trng_disable_test(struct device *dev,
+ void __iomem *csr_reg_ptr,
+ u64 csr_reg_base)
+{
+ u64 status, val, csr_reg_off;
+
+ /*
+ * When done, clear the 'test_known_noise' bit in the TRNG_TEST
+ * register (will immediately clear the 'test_ready' bit in the
+ * TRNG_STATUS register and reset the 'monobit test', 'run test'
+ * and 'poker test' circuits) and clear the 'test_mode' bit in the
+ * TRNG_CONTROL register.
+ */
+ csr_reg_off = mlxbf_pka_dev_get_register_offset(csr_reg_base, MLXBF_PKA_TRNG_TEST_ADDR);
+ mlxbf_pka_dev_io_write(csr_reg_ptr, csr_reg_off, 0);
+
+ csr_reg_off = mlxbf_pka_dev_get_register_offset(csr_reg_base, MLXBF_PKA_TRNG_STATUS_ADDR);
+ status = mlxbf_pka_dev_io_read(csr_reg_ptr, csr_reg_off);
+
+ if (status & MLXBF_PKA_TRNG_STATUS_TEST_READY)
+ dev_info(dev, "test ready bit is still set\n");
+
+ if (status & MLXBF_PKA_TRNG_STATUS_FAIL_MODES)
+ dev_info(dev, "test bits are still set, TRNG_STATUS:0x%llx\n", status);
+
+ csr_reg_off = mlxbf_pka_dev_get_register_offset(csr_reg_base, MLXBF_PKA_TRNG_CONTROL_ADDR);
+ val = mlxbf_pka_dev_io_read(csr_reg_ptr, csr_reg_off);
+ mlxbf_pka_dev_io_write(csr_reg_ptr, csr_reg_off, val & ~MLXBF_PKA_TRNG_STATUS_TEST_READY);
+
+ csr_reg_off = mlxbf_pka_dev_get_register_offset(csr_reg_base, MLXBF_PKA_TRNG_ALARMCNT_ADDR);
+ val = mlxbf_pka_dev_io_read(csr_reg_ptr, csr_reg_off);
+ mlxbf_pka_dev_io_write(csr_reg_ptr,
+ csr_reg_off,
+ val & ~MLXBF_PKA_TRNG_ALARMCNT_STALL_RUN_POKER);
+}
+
+static int mlxbf_pka_dev_trng_test_known_answer_basic(struct device *dev,
+ void __iomem *csr_reg_ptr,
+ u64 csr_reg_base)
+{
+ u64 poker_cnt[MLXBF_PKA_TRNG_POKER_TEST_CNT];
+ u64 monobit_fail_cnt = 0;
+ u64 poker_fail_cnt = 0;
+ u64 run_fail_cnt = 0;
+ u64 monobit_cnt;
+ u64 csr_reg_off;
+ int cnt_idx;
+ int cnt_off;
+ int ret;
+
+ dev_dbg(dev, "run known-answer test circuits\n");
+
+ ret = mlxbf_pka_dev_trng_enable_test(dev, csr_reg_ptr, csr_reg_base,
+ MLXBF_PKA_TRNG_TEST_KNOWN_NOISE);
+ if (ret)
+ return ret;
+
+ ret = mlxbf_pka_dev_trng_test_circuits(dev,
+ csr_reg_ptr,
+ csr_reg_base,
+ MLXBF_PKA_TRNG_TEST_DATAL_BASIC_1,
+ MLXBF_PKA_TRNG_TEST_DATAH_BASIC_1,
+ MLXBF_PKA_TRNG_TEST_COUNT_BASIC_1,
+ MLXBF_PKA_TRNG_TEST_HALF_NO,
+ &monobit_fail_cnt,
+ &run_fail_cnt,
+ &poker_fail_cnt);
+
+ ret |= mlxbf_pka_dev_trng_test_circuits(dev,
+ csr_reg_ptr,
+ csr_reg_base,
+ MLXBF_PKA_TRNG_TEST_DATAL_BASIC_2,
+ MLXBF_PKA_TRNG_TEST_DATAH_BASIC_2,
+ MLXBF_PKA_TRNG_TEST_COUNT_BASIC_2,
+ MLXBF_PKA_TRNG_TEST_HALF_ADD,
+ &monobit_fail_cnt,
+ &run_fail_cnt,
+ &poker_fail_cnt);
+
+ dev_dbg(dev, "monobit_fail_cnt : 0x%llx\n", monobit_fail_cnt);
+ dev_dbg(dev, "poker_fail_cnt : 0x%llx\n", poker_fail_cnt);
+ dev_dbg(dev, "run_fail_cnt : 0x%llx\n", run_fail_cnt);
+
+ for (cnt_idx = 0, cnt_off = 0;
+ cnt_idx < MLXBF_PKA_TRNG_POKER_TEST_CNT;
+ cnt_idx++, cnt_off += 8) {
+ csr_reg_off = mlxbf_pka_dev_get_register_offset(csr_reg_base,
+ MLXBF_PKA_TRNG_POKER_3_0_ADDR +
+ cnt_off);
+ poker_cnt[cnt_idx] = mlxbf_pka_dev_io_read(csr_reg_ptr, csr_reg_off);
+ }
+
+ csr_reg_off = mlxbf_pka_dev_get_register_offset(csr_reg_base,
+ MLXBF_PKA_TRNG_MONOBITCNT_ADDR);
+ monobit_cnt = mlxbf_pka_dev_io_read(csr_reg_ptr, csr_reg_off);
+
+ if (ret)
+ goto exit;
+
+ if (memcmp(poker_cnt, poker_test_exp_cnt, sizeof(poker_test_exp_cnt))) {
+ dev_dbg(dev, "invalid poker counters!\n");
+ ret = -EIO;
+ goto exit;
+ }
+
+ if (monobit_cnt != MLXBF_PKA_TRNG_MONOBITCNT_SUM) {
+ dev_dbg(dev, "invalid sum of squares!\n");
+ ret = -EIO;
+ goto exit;
+ }
+
+exit:
+ mlxbf_pka_dev_trng_disable_test(dev, csr_reg_ptr, csr_reg_base);
+ return ret;
+}
+
+static int mlxbf_pka_dev_trng_test_known_answer_poker_fail(struct device *dev,
+ void __iomem *csr_reg_ptr,
+ u64 csr_reg_base)
+{
+ u64 monobit_fail_cnt = 0;
+ u64 poker_fail_cnt = 0;
+ u64 run_fail_cnt = 0;
+
+ dev_dbg(dev, "run known-answer test circuits (poker fail)\n");
+
+ mlxbf_pka_dev_trng_enable_test(dev, csr_reg_ptr, csr_reg_base,
+ MLXBF_PKA_TRNG_TEST_KNOWN_NOISE);
+
+ /*
+ * Ignore the return value here as it is expected that poker test
+ * should fail. Check failure counts thereafter to assert only poker
+ * test has failed.
+ */
+ mlxbf_pka_dev_trng_test_circuits(dev,
+ csr_reg_ptr,
+ csr_reg_base,
+ MLXBF_PKA_TRNG_TEST_DATAL_POKER,
+ MLXBF_PKA_TRNG_TEST_DATAH_POKER,
+ MLXBF_PKA_TRNG_TEST_COUNT_POKER,
+ MLXBF_PKA_TRNG_TEST_HALF_NO,
+ &monobit_fail_cnt,
+ &run_fail_cnt,
+ &poker_fail_cnt);
+
+ dev_dbg(dev, "monobit_fail_cnt : 0x%llx\n", monobit_fail_cnt);
+ dev_dbg(dev, "poker_fail_cnt : 0x%llx\n", poker_fail_cnt);
+ dev_dbg(dev, "run_fail_cnt : 0x%llx\n", run_fail_cnt);
+
+ mlxbf_pka_dev_trng_disable_test(dev, csr_reg_ptr, csr_reg_base);
+
+ return poker_fail_cnt && !run_fail_cnt && !monobit_fail_cnt ? 0 : -EIO;
+}
+
+static int mlxbf_pka_dev_trng_test_unknown_answer(struct device *dev,
+ void __iomem *csr_reg_ptr,
+ u64 csr_reg_base)
+{
+ u64 datal = 0, datah = 0, csr_reg_off;
+ int ret = 0, test_idx;
+
+ dev_dbg(dev, "run unknown-answer self test\n");
+
+ /* First reset, the RAW registers. */
+ csr_reg_off = mlxbf_pka_dev_get_register_offset(csr_reg_base, MLXBF_PKA_TRNG_RAW_L_ADDR);
+ mlxbf_pka_dev_io_write(csr_reg_ptr, csr_reg_off, 0);
+
+ csr_reg_off = mlxbf_pka_dev_get_register_offset(csr_reg_base, MLXBF_PKA_TRNG_RAW_H_ADDR);
+ mlxbf_pka_dev_io_write(csr_reg_ptr, csr_reg_off, 0);
+
+ /*
+ * There is a small probability for this test to fail. So run the test
+ * 10 times, if it succeeds once then assume that the test passed.
+ */
+ for (test_idx = 0; test_idx < 10; test_idx++) {
+ mlxbf_pka_dev_trng_enable_test(dev, csr_reg_ptr, csr_reg_base,
+ MLXBF_PKA_TRNG_TEST_NOISE);
+
+ csr_reg_off = mlxbf_pka_dev_get_register_offset(csr_reg_base,
+ MLXBF_PKA_TRNG_RAW_L_ADDR);
+ datal = mlxbf_pka_dev_io_read(csr_reg_ptr, csr_reg_off);
+
+ csr_reg_off = mlxbf_pka_dev_get_register_offset(csr_reg_base,
+ MLXBF_PKA_TRNG_RAW_H_ADDR);
+ datah = mlxbf_pka_dev_io_read(csr_reg_ptr, csr_reg_off);
+
+ dev_dbg(dev, "datal=0x%llx\n", datal);
+ dev_dbg(dev, "datah=0x%llx\n", datah);
+
+ mlxbf_pka_dev_trng_disable_test(dev, csr_reg_ptr, csr_reg_base);
+
+ if (!datah && !datal)
+ ret = -EIO;
+ else
+ return 0;
+ }
+ return ret;
+}
+
+/* Test TRNG. */
+static int mlxbf_pka_dev_test_trng(struct device *dev, void __iomem *csr_reg_ptr, u64 csr_reg_base)
+{
+ int ret;
+
+ ret = mlxbf_pka_dev_trng_test_known_answer_basic(dev, csr_reg_ptr, csr_reg_base);
+ if (ret)
+ return ret;
+
+ ret = mlxbf_pka_dev_trng_test_known_answer_poker_fail(dev, csr_reg_ptr, csr_reg_base);
+ if (ret)
+ return ret;
+
+ ret = mlxbf_pka_dev_trng_test_unknown_answer(dev, csr_reg_ptr, csr_reg_base);
+ if (ret)
+ return ret;
+
+ return ret;
+}
+
+static void mlxbf_pka_dev_trng_write_ps_ai_str(void __iomem *csr_reg_ptr,
+ u64 csr_reg_base,
+ u32 input_str[])
+{
+ u64 csr_reg_off;
+ u8 i;
+
+ for (i = 0; i < MLXBF_PKA_TRNG_PS_AI_REG_COUNT; i++) {
+ csr_reg_off = mlxbf_pka_dev_get_register_offset(csr_reg_base,
+ MLXBF_PKA_TRNG_PS_AI_0_ADDR + i *
+ MLXBF_PKA_TRNG_OUTPUT_REG_OFFSET);
+
+ mlxbf_pka_dev_io_write(csr_reg_ptr, csr_reg_off, input_str[i]);
+ }
+}
+
+static void mlxbf_pka_dev_trng_drbg_generate(void __iomem *csr_reg_ptr, u64 csr_reg_base)
+{
+ u64 csr_reg_off;
+
+ csr_reg_off = mlxbf_pka_dev_get_register_offset(csr_reg_base, MLXBF_PKA_TRNG_CONTROL_ADDR);
+ mlxbf_pka_dev_io_write(csr_reg_ptr, csr_reg_off, MLXBF_PKA_TRNG_CONTROL_REQ_DATA);
+}
+
+static int mlxbf_pka_dev_test_trng_drbg(struct device *dev,
+ void __iomem *csr_reg_ptr,
+ u64 csr_reg_base)
+{
+ u64 csr_reg_off, csr_reg_val;
+ u8 i;
+
+ /* Make sure the engine is idle. */
+ csr_reg_off = mlxbf_pka_dev_get_register_offset(csr_reg_base, MLXBF_PKA_TRNG_CONTROL_ADDR);
+ mlxbf_pka_dev_io_write(csr_reg_ptr, csr_reg_off, 0);
+
+ /* Enable DRBG, TRNG need not be enabled for this test. */
+ csr_reg_off = mlxbf_pka_dev_get_register_offset(csr_reg_base, MLXBF_PKA_TRNG_CONTROL_ADDR);
+ mlxbf_pka_dev_io_write(csr_reg_ptr, csr_reg_off, MLXBF_PKA_TRNG_CONTROL_DRBG_ENABLE);
+
+ /* Set 'test_sp_800_90' bit in the TRNG_TEST register. */
+ csr_reg_off = mlxbf_pka_dev_get_register_offset(csr_reg_base, MLXBF_PKA_TRNG_TEST_ADDR);
+ mlxbf_pka_dev_io_write(csr_reg_ptr, csr_reg_off, MLXBF_PKA_TRNG_TEST_DRBG);
+
+ /* Wait for 'test_ready' bit to be set. */
+ if (!mlxbf_pka_dev_trng_wait_test_ready(dev, csr_reg_ptr, csr_reg_base))
+ return -ETIMEDOUT;
+
+ /* 'Instantiate' function. */
+ mlxbf_pka_dev_trng_write_ps_ai_str(csr_reg_ptr,
+ csr_reg_base,
+ mlxbf_pka_trng_drbg_test_ps_str);
+ if (!mlxbf_pka_dev_trng_wait_test_ready(dev, csr_reg_ptr, csr_reg_base))
+ return -ETIMEDOUT;
+
+ /* 'Generate' function. */
+ mlxbf_pka_dev_trng_write_ps_ai_str(csr_reg_ptr,
+ csr_reg_base,
+ mlxbf_pka_trng_drbg_test_etpy_str1);
+ if (!mlxbf_pka_dev_trng_wait_test_ready(dev, csr_reg_ptr, csr_reg_base))
+ return -ETIMEDOUT;
+
+ /*
+ * A standard NIST SP 800-90A DRBG known-answer test discards the
+ * result of the first 'Generate' function and only checks the result
+ * of the second 'Generate' function. Hence 'Generate' is performed
+ * again.
+ */
+
+ /* 'Generate' function. */
+ mlxbf_pka_dev_trng_write_ps_ai_str(csr_reg_ptr,
+ csr_reg_base,
+ mlxbf_pka_trng_drbg_test_etpy_str2);
+ if (!mlxbf_pka_dev_trng_wait_test_ready(dev, csr_reg_ptr, csr_reg_base))
+ return -ETIMEDOUT;
+
+ /* Check output registers. */
+ for (i = 0; i < MLXBF_PKA_TRNG_OUTPUT_CNT; i++) {
+ csr_reg_off =
+ mlxbf_pka_dev_get_register_offset(csr_reg_base,
+ MLXBF_PKA_TRNG_OUTPUT_0_ADDR +
+ (i * MLXBF_PKA_TRNG_OUTPUT_REG_OFFSET));
+
+ csr_reg_val = mlxbf_pka_dev_io_read(csr_reg_ptr, csr_reg_off);
+
+ if ((u32)csr_reg_val != mlxbf_pka_trng_drbg_test_output[i]) {
+ dev_dbg(dev, "DRBG known answer test failed: output register:%d, 0x%x\n",
+ i, (u32)csr_reg_val);
+ return -EIO;
+ }
+ }
+
+ /* Clear 'test_sp_800_90' bit in the TRNG_TEST register. */
+ csr_reg_off = mlxbf_pka_dev_get_register_offset(csr_reg_base, MLXBF_PKA_TRNG_TEST_ADDR);
+ mlxbf_pka_dev_io_write(csr_reg_ptr, csr_reg_off, 0);
+
+ return 0;
+}
+
+static bool mlxbf_pka_dev_trng_shutdown_oflo(struct mlxbf_pka_dev_res_t *trng_csr_ptr,
+ u64 *err_cycle)
+{
+ u64 curr_cycle_cnt, fro_stopped_mask, fro_enabled_mask;
+ u64 csr_reg_base, csr_reg_off, csr_reg_value;
+ void __iomem *csr_reg_ptr;
+
+ csr_reg_base = trng_csr_ptr->base;
+ csr_reg_ptr = trng_csr_ptr->ioaddr;
+
+ csr_reg_off = mlxbf_pka_dev_get_register_offset(csr_reg_base, MLXBF_PKA_TRNG_STATUS_ADDR);
+ csr_reg_value = mlxbf_pka_dev_io_read(csr_reg_ptr, csr_reg_off);
+
+ if (csr_reg_value & MLXBF_PKA_TRNG_STATUS_SHUTDOWN_OFLO) {
+ curr_cycle_cnt = get_cycles();
+ /*
+ * See if any FROs were shut down. If they were, toggle bits in
+ * the FRO detune register and reenable the FROs.
+ */
+ csr_reg_off = mlxbf_pka_dev_get_register_offset(csr_reg_base,
+ MLXBF_PKA_TRNG_ALARMSTOP_ADDR);
+ fro_stopped_mask = mlxbf_pka_dev_io_read(csr_reg_ptr, csr_reg_off);
+ if (fro_stopped_mask) {
+ csr_reg_off =
+ mlxbf_pka_dev_get_register_offset(csr_reg_base,
+ MLXBF_PKA_TRNG_FROENABLE_ADDR);
+ fro_enabled_mask = mlxbf_pka_dev_io_read(csr_reg_ptr, csr_reg_off);
+ csr_reg_off =
+ mlxbf_pka_dev_get_register_offset(csr_reg_base,
+ MLXBF_PKA_TRNG_FRODETUNE_ADDR);
+ mlxbf_pka_dev_io_write(csr_reg_ptr, csr_reg_off, fro_stopped_mask);
+
+ csr_reg_off =
+ mlxbf_pka_dev_get_register_offset(csr_reg_base,
+ MLXBF_PKA_TRNG_FROENABLE_ADDR);
+ mlxbf_pka_dev_io_write(csr_reg_ptr, csr_reg_off,
+ fro_stopped_mask | fro_enabled_mask);
+ }
+
+ /* Reset the error. */
+ csr_reg_off = mlxbf_pka_dev_get_register_offset(csr_reg_base,
+ MLXBF_PKA_TRNG_ALARMMASK_ADDR);
+ mlxbf_pka_dev_io_write(csr_reg_ptr, csr_reg_off, 0);
+
+ csr_reg_off = mlxbf_pka_dev_get_register_offset(csr_reg_base,
+ MLXBF_PKA_TRNG_ALARMSTOP_ADDR);
+ mlxbf_pka_dev_io_write(csr_reg_ptr, csr_reg_off, 0);
+
+ csr_reg_off = mlxbf_pka_dev_get_register_offset(csr_reg_base,
+ MLXBF_PKA_TRNG_INTACK_ADDR);
+ mlxbf_pka_dev_io_write(csr_reg_ptr,
+ csr_reg_off,
+ MLXBF_PKA_TRNG_STATUS_SHUTDOWN_OFLO);
+
+ /*
+ * If this error occurs again within about a second, the hardware
+ * is malfunctioning. Disable the trng and return an error.
+ */
+ if (*err_cycle &&
+ (curr_cycle_cnt - *err_cycle < MLXBF_PKA_TRNG_TEST_ERR_CYCLE_MAX)) {
+ csr_reg_off =
+ mlxbf_pka_dev_get_register_offset(csr_reg_base,
+ MLXBF_PKA_TRNG_CONTROL_ADDR);
+ csr_reg_value = mlxbf_pka_dev_io_read(csr_reg_ptr, csr_reg_off);
+ csr_reg_value &= ~MLXBF_PKA_TRNG_CONTROL;
+ mlxbf_pka_dev_io_write(csr_reg_ptr, csr_reg_off, csr_reg_value);
+ return false;
+ }
+
+ *err_cycle = curr_cycle_cnt;
+ }
+
+ return true;
+}
+
+static int mlxbf_pka_dev_trng_drbg_reseed(struct device *dev,
+ void __iomem *csr_reg_ptr,
+ u64 csr_reg_base)
+{
+ u64 csr_reg_off;
+
+ csr_reg_off = mlxbf_pka_dev_get_register_offset(csr_reg_base, MLXBF_PKA_TRNG_CONTROL_ADDR);
+ mlxbf_pka_dev_io_write(csr_reg_ptr, csr_reg_off, MLXBF_PKA_TRNG_CONTROL_DRBG_RESEED);
+
+ if (!mlxbf_pka_dev_trng_wait_test_ready(dev, csr_reg_ptr, csr_reg_base))
+ return -ETIMEDOUT;
+
+ /* Write personalization string. */
+ mlxbf_pka_dev_trng_write_ps_ai_str(csr_reg_ptr, csr_reg_base, mlxbf_pka_trng_drbg_ps_str);
+
+ return 0;
+}
+
+/* Configure the TRNG. */
+int mlxbf_pka_dev_config_trng_drbg(struct device *dev,
+ struct mlxbf_pka_dev_res_t *aic_csr_ptr,
+ struct mlxbf_pka_dev_res_t *trng_csr_ptr)
+{
+ u64 csr_reg_base, csr_reg_off;
+ void __iomem *csr_reg_ptr;
+ int ret;
+
+ if (trng_csr_ptr->status != MLXBF_PKA_DEV_RES_STATUS_MAPPED ||
+ trng_csr_ptr->type != MLXBF_PKA_DEV_RES_TYPE_REG)
+ return -EPERM;
+
+ dev_dbg(dev, "starting up the TRNG\n");
+
+ ret = mlxbf_pka_dev_config_trng_clk(dev, aic_csr_ptr);
+ if (ret)
+ return ret;
+
+ csr_reg_base = trng_csr_ptr->base;
+ csr_reg_ptr = trng_csr_ptr->ioaddr;
+
+ /*
+ * Perform NIST known-answer tests on the complete SP 800-90A DRBG
+ * without BC_DF functionality.
+ */
+ ret = mlxbf_pka_dev_test_trng_drbg(dev, csr_reg_ptr, csr_reg_base);
+ if (ret)
+ return ret;
+
+ /* Starting up the TRNG with a DRBG. */
+
+ /* Make sure the engine is idle. */
+ csr_reg_off = mlxbf_pka_dev_get_register_offset(csr_reg_base, MLXBF_PKA_TRNG_CONTROL_ADDR);
+ mlxbf_pka_dev_io_write(csr_reg_ptr, csr_reg_off, 0);
+
+ /* Disable all FROs initially. */
+ csr_reg_off =
+ mlxbf_pka_dev_get_register_offset(csr_reg_base, MLXBF_PKA_TRNG_FROENABLE_ADDR);
+ mlxbf_pka_dev_io_write(csr_reg_ptr, csr_reg_off, 0);
+ csr_reg_off =
+ mlxbf_pka_dev_get_register_offset(csr_reg_base, MLXBF_PKA_TRNG_FRODETUNE_ADDR);
+ mlxbf_pka_dev_io_write(csr_reg_ptr, csr_reg_off, 0);
+
+ /*
+ * Write all configuration values in the TRNG_CONFIG and TRNG_ALARMCNT,
+ * write zeroes to the TRNG_ALARMMASK and TRNG_ALARMSTOP registers.
+ */
+ csr_reg_off = mlxbf_pka_dev_get_register_offset(csr_reg_base, MLXBF_PKA_TRNG_CONFIG_ADDR);
+ mlxbf_pka_dev_io_write(csr_reg_ptr, csr_reg_off, MLXBF_PKA_TRNG_CONFIG);
+ csr_reg_off = mlxbf_pka_dev_get_register_offset(csr_reg_base, MLXBF_PKA_TRNG_ALARMCNT_ADDR);
+ mlxbf_pka_dev_io_write(csr_reg_ptr, csr_reg_off, MLXBF_PKA_TRNG_ALARMCNT);
+
+ csr_reg_off =
+ mlxbf_pka_dev_get_register_offset(csr_reg_base, MLXBF_PKA_TRNG_ALARMMASK_ADDR);
+ mlxbf_pka_dev_io_write(csr_reg_ptr, csr_reg_off, 0);
+ csr_reg_off =
+ mlxbf_pka_dev_get_register_offset(csr_reg_base, MLXBF_PKA_TRNG_ALARMSTOP_ADDR);
+ mlxbf_pka_dev_io_write(csr_reg_ptr, csr_reg_off, 0);
+
+ /*
+ * Enable all FROs in the TRNG_FROENABLE register. Note that this can
+ * only be done after clearing the TRNG_ALARMSTOP register.
+ */
+ csr_reg_off =
+ mlxbf_pka_dev_get_register_offset(csr_reg_base, MLXBF_PKA_TRNG_FROENABLE_ADDR);
+ mlxbf_pka_dev_io_write(csr_reg_ptr, csr_reg_off, MLXBF_PKA_TRNG_FROENABLE);
+
+ /*
+ * Optionally, write 'Personalization string' of up to 384 bits in
+ * TRNG_PS_AI_xxx registers.
+ * The contents of these registers will be XOR-ed into the output of the
+ * SHA-256 'Conditioning Function' to be used as seed value for the
+ * actual DRBG.
+ */
+ mlxbf_pka_dev_trng_write_ps_ai_str(csr_reg_ptr, csr_reg_base, mlxbf_pka_trng_drbg_ps_str);
+
+ /*
+ * Run TRNG tests after configuring TRNG.
+ * NOTE: TRNG need not be enabled to carry out these tests.
+ */
+ ret = mlxbf_pka_dev_test_trng(dev, csr_reg_ptr, csr_reg_base);
+ if (ret)
+ return ret;
+
+ /*
+ * Start the actual engine by setting the 'enable_trng' and 'drbg_en'
+ * bit in the TRNG_CONTROL register (also a nice point to set the
+ * interrupt mask bits).
+ */
+ csr_reg_off = mlxbf_pka_dev_get_register_offset(csr_reg_base, MLXBF_PKA_TRNG_CONTROL_ADDR);
+ mlxbf_pka_dev_io_write(csr_reg_ptr, csr_reg_off, MLXBF_PKA_TRNG_CONTROL_DRBG);
+
+ /*
+ * The engine is now ready to handle the first 'Generate' request using
+ * the 'request_data' bit of the TRNG_CONTROL register. The first output
+ * for these requests will take a while, as Noise Source and
+ * Conditioning Function must first generate seed entropy for the DRBG.
+ *
+ * Optionally, when buffer RAM is configured: Set a data available
+ * interrupt threshold using the 'load_thresh' and 'blocks_thresh'
+ * fields of the TRNG_INTACK register. This allows delaying the data
+ * available interrupt until the indicated number of 128-bit words a
+ * available in the buffer RAM.
+ *
+ * Start the actual 'Generate' operation using the 'request_data' and
+ * 'data_blocks' fields of the TRNG_CONTROL register.
+ */
+ mlxbf_pka_dev_trng_drbg_generate(csr_reg_ptr, csr_reg_base);
+
+ /* Delay 200 ms. */
+ mdelay(200);
+
+ return 0;
+}
+
+/* Read from DRBG enabled TRNG. */
+int mlxbf_pka_dev_trng_read(struct device *dev,
+ struct mlxbf_pka_dev_shim_s *shim,
+ u32 *data, u32 cnt)
+{
+ u64 csr_reg_base, csr_reg_off, csr_reg_value, timer;
+ struct mlxbf_pka_dev_res_t *trng_csr_ptr;
+ u8 output_idx, trng_ready = 0;
+ u32 data_idx, word_cnt;
+ void __iomem *csr_reg_ptr;
+ int ret = 0;
+
+ if (!shim || !data || (cnt % MLXBF_PKA_TRNG_OUTPUT_CNT != 0))
+ return -EINVAL;
+
+ if (!cnt)
+ return ret;
+
+ guard(mutex)(&shim->mutex);
+
+ trng_csr_ptr = &shim->resources.trng_csr;
+
+ if (trng_csr_ptr->status != MLXBF_PKA_DEV_RES_STATUS_MAPPED ||
+ trng_csr_ptr->type != MLXBF_PKA_DEV_RES_TYPE_REG)
+ return -EPERM;
+
+ csr_reg_base = trng_csr_ptr->base;
+ csr_reg_ptr = trng_csr_ptr->ioaddr;
+
+ if (!mlxbf_pka_dev_trng_shutdown_oflo(trng_csr_ptr, &shim->trng_err_cycle))
+ return -EWOULDBLOCK;
+
+ word_cnt = cnt >> ilog2(sizeof(u32));
+
+ for (data_idx = 0; data_idx < word_cnt; data_idx++) {
+ output_idx = data_idx % MLXBF_PKA_TRNG_OUTPUT_CNT;
+
+ /* Tell the hardware to advance. */
+ if (!output_idx) {
+ csr_reg_off = mlxbf_pka_dev_get_register_offset(csr_reg_base,
+ MLXBF_PKA_TRNG_INTACK_ADDR);
+ mlxbf_pka_dev_io_write(csr_reg_ptr, csr_reg_off,
+ MLXBF_PKA_TRNG_STATUS_READY);
+ trng_ready = 0;
+
+ /*
+ * Check if 'data_blocks' field is zero in TRNG_CONTROL
+ * register. If it is zero, need to issue a 'Reseed and
+ * Generate' request for DRBG enabled TRNG.
+ */
+ csr_reg_off =
+ mlxbf_pka_dev_get_register_offset(csr_reg_base,
+ MLXBF_PKA_TRNG_CONTROL_ADDR);
+ csr_reg_value = mlxbf_pka_dev_io_read(csr_reg_ptr, csr_reg_off);
+
+ if (!((u32)csr_reg_value & MLXBF_PKA_TRNG_DRBG_DATA_BLOCK_MASK)) {
+ /* Issue reseed. */
+ ret = mlxbf_pka_dev_trng_drbg_reseed(dev,
+ csr_reg_ptr,
+ csr_reg_base);
+ if (ret)
+ return -EBUSY;
+
+ /* Issue generate request. */
+ mlxbf_pka_dev_trng_drbg_generate(csr_reg_ptr, csr_reg_base);
+ }
+ }
+
+ /*
+ * Wait until a data word is available in the TRNG_OUTPUT_X
+ * registers, using the interrupt and/or 'ready' status bit
+ * in the TRNG_STATUS register. The only way this would hang
+ * is if the TRNG is never initialized. This function cannot
+ * be called if that happened.
+ */
+ timer = mlxbf_pka_dev_timer_start_msec(1000);
+ csr_reg_off =
+ mlxbf_pka_dev_get_register_offset(csr_reg_base, MLXBF_PKA_TRNG_STATUS_ADDR);
+ while (!trng_ready) {
+ csr_reg_value = mlxbf_pka_dev_io_read(csr_reg_ptr, csr_reg_off);
+ trng_ready = csr_reg_value & MLXBF_PKA_TRNG_STATUS_READY;
+
+ if (mlxbf_pka_dev_timer_done(timer)) {
+ dev_dbg(dev, "shim %u got error obtaining random number\n",
+ shim->shim_id);
+ return -EBUSY;
+ }
+ }
+
+ /* Read the registers. */
+ csr_reg_off =
+ mlxbf_pka_dev_get_register_offset(csr_reg_base,
+ MLXBF_PKA_TRNG_OUTPUT_0_ADDR +
+ (output_idx * MLXBF_PKA_TRNG_OUTPUT_REG_OFFSET));
+ csr_reg_value = mlxbf_pka_dev_io_read(csr_reg_ptr, csr_reg_off);
+ data[data_idx] = (u32)csr_reg_value;
+ }
+
+ return ret;
+}
+
+bool mlxbf_pka_dev_has_trng(struct mlxbf_pka_dev_shim_s *shim)
+{
+ if (!shim)
+ return false;
+
+ return shim->trng_enabled == MLXBF_PKA_SHIM_TRNG_ENABLED;
+}
diff --git a/drivers/platform/mellanox/mlxbf_pka/mlxbf_pka_trng.h b/drivers/platform/mellanox/mlxbf_pka/mlxbf_pka_trng.h
new file mode 100644
index 000000000000..97ed6045781e
--- /dev/null
+++ b/drivers/platform/mellanox/mlxbf_pka/mlxbf_pka_trng.h
@@ -0,0 +1,152 @@
+/* SPDX-License-Identifier: GPL-2.0-only OR BSD-3-Clause */
+/* SPDX-FileCopyrightText: Copyright (c) 2025 NVIDIA CORPORATION. All rights reserved. */
+
+#ifndef __MLXBF_PKA_TRNG_H__
+#define __MLXBF_PKA_TRNG_H__
+
+/*
+ * The True Random Number Generator 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_TRNG_OUTPUT_0_ADDR 0x12000
+#define MLXBF_PKA_TRNG_STATUS_ADDR 0x12020
+#define MLXBF_PKA_TRNG_INTACK_ADDR MLXBF_PKA_TRNG_STATUS_ADDR
+#define MLXBF_PKA_TRNG_CONTROL_ADDR 0x12028
+#define MLXBF_PKA_TRNG_CONFIG_ADDR 0x12030
+#define MLXBF_PKA_TRNG_ALARMCNT_ADDR 0x12038
+#define MLXBF_PKA_TRNG_FROENABLE_ADDR 0x12040
+#define MLXBF_PKA_TRNG_FRODETUNE_ADDR 0x12048
+#define MLXBF_PKA_TRNG_ALARMMASK_ADDR 0x12050
+#define MLXBF_PKA_TRNG_ALARMSTOP_ADDR 0x12058
+#define MLXBF_PKA_TRNG_TEST_ADDR 0x120E0
+#define MLXBF_PKA_TRNG_RAW_L_ADDR 0x12060
+#define MLXBF_PKA_TRNG_RAW_H_ADDR 0x12068
+#define MLXBF_PKA_TRNG_MONOBITCNT_ADDR 0x120B8
+#define MLXBF_PKA_TRNG_POKER_3_0_ADDR 0x120C0
+#define MLXBF_PKA_TRNG_PS_AI_0_ADDR 0x12080
+
+/*
+ * 'trng_clk_on' mask for PKA Clock Switch Forcing Register. Turn on the TRNG
+ * clock. When the TRNG is controlled via the host slave interface, this engine
+ * needs to be turned on by setting bit 11.
+ */
+#define MLXBF_PKA_CLK_FORCE_TRNG_ON BIT(11)
+
+/* Number of TRNG output registers. */
+#define MLXBF_PKA_TRNG_OUTPUT_CNT 4
+
+/* Number of TRNG poker test counts. */
+#define MLXBF_PKA_TRNG_POKER_TEST_CNT 4
+
+/* TRNG configuration. */
+#define MLXBF_PKA_TRNG_CONFIG 0x00020008
+/* TRNG Alarm Counter Register value. */
+#define MLXBF_PKA_TRNG_ALARMCNT 0x000200ff
+/* TRNG FRO Enable Register value. */
+#define MLXBF_PKA_TRNG_FROENABLE 0x00ffffff
+/*
+ * TRNG Control Register value. Set bit 10 to start the EIP-76 (i.e. TRNG
+ * engine), gathering entropy from the Free Running Oscillators (FROs).
+ */
+#define MLXBF_PKA_TRNG_CONTROL 0x00000400
+
+/* TRNG Control bit. */
+#define MLXBF_PKA_TRNG_CONTROL_TEST_MODE BIT(8)
+
+/*
+ * TRNG Control Register value. Set bit 10 and 12 to start the EIP-76 (i.e.
+ * TRNG engine) with DRBG enabled, gathering entropy from the FROs.
+ */
+#define MLXBF_PKA_TRNG_CONTROL_DRBG 0x00001400
+
+/*
+ * DRBG enabled TRNG 'request_data' value. REQ_DATA (in accordance with
+ * DATA_BLOCK_MASK) requests 256 blocks of 128-bit random output. 4095 blocks
+ * is the maximum number that can be requested for the TRNG (with DRBG)
+ * configuration on Bluefield platforms.
+ */
+#define MLXBF_PKA_TRNG_CONTROL_REQ_DATA 0x10010000
+
+/* Mask for 'Data Block' in TRNG Control Register. */
+#define MLXBF_PKA_TRNG_DRBG_DATA_BLOCK_MASK GENMASK(31, 20)
+
+/* Set bit 12 of TRNG Control Register to enable DRBG functionality. */
+#define MLXBF_PKA_TRNG_CONTROL_DRBG_ENABLE BIT(12)
+
+/* Set bit 7 (i.e. 'test_sp_800_90 DRBG' bit) in the TRNG Test Register. */
+#define MLXBF_PKA_TRNG_TEST_DRBG BIT(7)
+
+/* Number of Personalization String/Additional Input Registers. */
+#define MLXBF_PKA_TRNG_PS_AI_REG_COUNT 12
+
+/* Offset bytes of Personalization String/Additional Input Registers. */
+#define MLXBF_PKA_TRNG_OUTPUT_REG_OFFSET 0x8
+
+/* Maximum TRNG test error cycle, about one second. */
+#define MLXBF_PKA_TRNG_TEST_ERR_CYCLE_MAX (1000 * 1000 * 1000)
+
+/* DRBG Reseed enable. */
+#define MLXBF_PKA_TRNG_CONTROL_DRBG_RESEED BIT(15)
+
+/* TRNG Status bits. */
+#define MLXBF_PKA_TRNG_STATUS_READY BIT(0)
+#define MLXBF_PKA_TRNG_STATUS_SHUTDOWN_OFLO BIT(1)
+#define MLXBF_PKA_TRNG_STATUS_TEST_READY BIT(8)
+#define MLXBF_PKA_TRNG_STATUS_MONOBIT_FAIL BIT(7)
+#define MLXBF_PKA_TRNG_STATUS_RUN_FAIL BIT(4)
+#define MLXBF_PKA_TRNG_STATUS_POKER_FAIL BIT(6)
+
+#define MLXBF_PKA_TRNG_STATUS_FAIL_MODES (MLXBF_PKA_TRNG_STATUS_MONOBIT_FAIL | \
+ MLXBF_PKA_TRNG_STATUS_RUN_FAIL | \
+ MLXBF_PKA_TRNG_STATUS_POKER_FAIL)
+
+/* TRNG Alarm Counter bits. */
+#define MLXBF_PKA_TRNG_ALARMCNT_STALL_RUN_POKER BIT(15)
+
+/* TRNG Test bits. */
+#define MLXBF_PKA_TRNG_TEST_KNOWN_NOISE BIT(5)
+#define MLXBF_PKA_TRNG_TEST_NOISE BIT(13)
+
+/* TRNG Test constants*/
+#define MLXBF_PKA_TRNG_MONOBITCNT_SUM 9978
+
+#define MLXBF_PKA_TRNG_TEST_HALF_ADD 1
+#define MLXBF_PKA_TRNG_TEST_HALF_NO 0
+
+#define MLXBF_PKA_TRNG_TEST_DATAL_BASIC_1 0x11111333
+#define MLXBF_PKA_TRNG_TEST_DATAH_BASIC_1 0x3555779f
+#define MLXBF_PKA_TRNG_TEST_COUNT_BASIC_1 11
+
+#define MLXBF_PKA_TRNG_TEST_DATAL_BASIC_2 0x01234567
+#define MLXBF_PKA_TRNG_TEST_DATAH_BASIC_2 0x89abcdef
+#define MLXBF_PKA_TRNG_TEST_COUNT_BASIC_2 302
+
+#define MLXBF_PKA_TRNG_TEST_DATAL_POKER 0xffffffff
+#define MLXBF_PKA_TRNG_TEST_DATAH_POKER 0xffffffff
+#define MLXBF_PKA_TRNG_TEST_COUNT_POKER 11
+
+#define MLXBF_PKA_TRNG_NUM_OF_FOUR_WORD 128
+
+/* Defines for mlxbf_pka_dev_shim->trng_enabled. */
+#define MLXBF_PKA_SHIM_TRNG_ENABLED 1
+#define MLXBF_PKA_SHIM_TRNG_DISABLED 0
+
+/* Configure the TRNG. */
+int mlxbf_pka_dev_config_trng_drbg(struct device *dev,
+ struct mlxbf_pka_dev_res_t *aic_csr_ptr,
+ struct mlxbf_pka_dev_res_t *trng_csr_ptr);
+
+/*
+ * Read data from the TRNG. Drivers can fill up to 'cnt' bytes of data into the
+ * buffer 'data'. The buffer 'data' is aligned for any type and 'cnt' is a
+ * multiple of 4.
+ */
+int mlxbf_pka_dev_trng_read(struct device *dev,
+ struct mlxbf_pka_dev_shim_s *shim,
+ u32 *data, u32 cnt);
+
+/* Return true if the TRNG engine is enabled, false if not. */
+bool mlxbf_pka_dev_has_trng(struct mlxbf_pka_dev_shim_s *shim);
+
+#endif /* __MLXBF_PKA_TRNG_H__ */
diff --git a/include/uapi/linux/mlxbf-pka.h b/include/uapi/linux/mlxbf-pka.h
index 9da6c1e5044b..2f2b11c2dacd 100644
--- a/include/uapi/linux/mlxbf-pka.h
+++ b/include/uapi/linux/mlxbf-pka.h
@@ -90,4 +90,23 @@ struct mlxbf_pka_dev_hw_ring_info {
*/
#define MLXBF_PKA_CLEAR_RING_COUNTERS _IO(MLXBF_PKA_IOC_TYPE, 0x2)
+/**
+ * struct mlxbf_pka_dev_trng_info - TRNG information
+ * @count: Number of random bytes in the buffer or length of the buffer
+ * @data: Data buffer to hold the random bytes
+ *
+ * MLXBF_PKA_GET_RANDOM_BYTES:
+ * _IOWR(MLXBF_PKA_IOC_TYPE, 0x3, mlxbf_pka_dev_trng_info).
+ *
+ * Get random bytes from True Random Number Generator(TRNG).
+ *
+ * Return: 0 on success, -errno on failure.
+ */
+struct mlxbf_pka_dev_trng_info {
+ __u32 count;
+ __u8 *data;
+};
+
+#define MLXBF_PKA_GET_RANDOM_BYTES _IOWR(MLXBF_PKA_IOC_TYPE, 0x3, struct mlxbf_pka_dev_trng_info)
+
#endif /* _UAPI_LINUX_MLXBF_PKA_H */
--
2.34.1
Powered by blists - more mailing lists