[RFC PATCH 1/5] firmware: imx: ele: Add API functions for OCOTP fuse access

Frieder Schrempf frieder at fris.de
Wed Apr 16 07:26:20 PDT 2025


From: Frieder Schrempf <frieder.schrempf at kontron.de>

The ELE S400 API provides read and write access to the OCOTP fuse
registers. This adds the necessary API functions imx_se_read_fuse()
and imx_se_write_fuse() to be used by other drivers such as the
OCOTP S400 NVMEM driver.

This is ported from the downstream vendor kernel.

Signed-off-by: Frieder Schrempf <frieder.schrempf at kontron.de>
---
 drivers/firmware/imx/ele_base_msg.c | 122 ++++++++++++++++++++++++++++
 drivers/firmware/imx/ele_base_msg.h |   8 ++
 include/linux/firmware/imx/se_api.h |   3 +
 3 files changed, 133 insertions(+)

diff --git a/drivers/firmware/imx/ele_base_msg.c b/drivers/firmware/imx/ele_base_msg.c
index bed1a0459d8d..0dfd4d2fef5a 100644
--- a/drivers/firmware/imx/ele_base_msg.c
+++ b/drivers/firmware/imx/ele_base_msg.c
@@ -311,3 +311,125 @@ int ele_debug_dump(struct se_if_priv *priv)
 
 	return ret;
 }
+
+static int ele_read_fuse(struct se_if_priv *priv, uint16_t fuse_id, u32 *value)
+{
+	struct se_api_msg *tx_msg __free(kfree) = NULL;
+	struct se_api_msg *rx_msg __free(kfree) = NULL;
+	int rx_msg_sz = ELE_READ_FUSE_RSP_MSG_SZ;
+	int ret = 0;
+
+	if (!priv)
+		return -EINVAL;
+
+	tx_msg = kzalloc(ELE_READ_FUSE_REQ_MSG_SZ, GFP_KERNEL);
+	if (!tx_msg)
+		return -ENOMEM;
+
+	rx_msg = kzalloc(rx_msg_sz, GFP_KERNEL);
+	if (!rx_msg)
+		return -ENOMEM;
+
+	ret = se_fill_cmd_msg_hdr(priv, (struct se_msg_hdr *)&tx_msg->header,
+				  ELE_READ_FUSE_REQ, ELE_READ_FUSE_REQ_MSG_SZ,
+				  true);
+	if (ret)
+		return ret;
+
+	tx_msg->data[0] = fuse_id;
+
+	ret = ele_msg_send_rcv(priv->priv_dev_ctx, tx_msg,
+			       ELE_READ_FUSE_REQ_MSG_SZ, rx_msg, rx_msg_sz);
+	if (ret < 0)
+		return ret;
+
+	ret = se_val_rsp_hdr_n_status(priv, rx_msg, ELE_READ_FUSE_REQ,
+				      rx_msg_sz, true);
+	if (ret)
+		return ret;
+
+	*value = rx_msg->data[1];
+
+	return 0;
+}
+
+/**
+ * imx_se_read_fuse() - API to request SE-FW to read the fuse(s) value.
+ * @void *se_if_data: refs to data attached to the se interface.
+ * @uint16_t fuse_id: Fuse identifier to read.
+ * @u32 *value: unsigned integer array to store the fuse values.
+ *
+ * Secure enclave like EdgeLock Enclave, manages the fuse. This API
+ * requests the FW to read the fuses. FW responds with the read
+ * values.
+ *
+ * Context:
+ *
+ * Return value:
+ *   0,   means success.
+ *   < 0, means failure.
+ */
+int imx_se_read_fuse(void *se_if_data, uint16_t fuse_id, u32 *value)
+{
+	return ele_read_fuse((struct se_if_priv *)se_if_data, fuse_id, value);
+}
+EXPORT_SYMBOL_GPL(imx_se_read_fuse);
+
+static int ele_write_fuse(struct se_if_priv *priv, uint16_t fuse_id, u32 value)
+{
+	struct se_api_msg *tx_msg __free(kfree) = NULL;
+	struct se_api_msg *rx_msg __free(kfree) = NULL;
+	int ret = 0;
+
+	if (!priv)
+		return -EINVAL;
+
+	tx_msg = kzalloc(ELE_WRITE_FUSE_REQ_MSG_SZ, GFP_KERNEL);
+	if (!tx_msg)
+		return -ENOMEM;
+
+	rx_msg = kzalloc(ELE_WRITE_FUSE_RSP_MSG_SZ, GFP_KERNEL);
+	if (!rx_msg)
+		return -ENOMEM;
+
+	ret = se_fill_cmd_msg_hdr(priv, (struct se_msg_hdr *)&tx_msg->header,
+				  ELE_WRITE_FUSE, ELE_WRITE_FUSE_REQ_MSG_SZ,
+				  true);
+	if (ret)
+		return ret;
+
+	tx_msg->data[0] = (32 << 16) | (fuse_id << 5);
+	tx_msg->data[1] = value;
+
+	ret = ele_msg_send_rcv(priv->priv_dev_ctx, tx_msg,
+			       ELE_WRITE_FUSE_REQ_MSG_SZ, rx_msg,
+			       ELE_WRITE_FUSE_RSP_MSG_SZ);
+	if (ret < 0)
+		return ret;
+
+	ret = se_val_rsp_hdr_n_status(priv, rx_msg, ELE_WRITE_FUSE,
+				      ELE_WRITE_FUSE_RSP_MSG_SZ, true);
+
+	return ret;
+}
+
+/**
+ * imx_se_write_fuse() - API to request SE-FW to write to fuses.
+ * @void *se_if_data: refs to data attached to the se interface.
+ * @uint16_t fuse_id: Fuse identifier to write to.
+ * @u32 value: unsigned integer value that to be written to the fuse.
+ *
+ * Secure enclave like EdgeLock Enclave, manages the fuse. This API
+ * requests the FW to write the fuse with the given value.
+ *
+ * Context:
+ *
+ * Return value:
+ *   0,   means success.
+ *   < 0, means failure.
+ */
+int imx_se_write_fuse(void *se_if_data, uint16_t fuse_id, u32 value)
+{
+	return ele_write_fuse((struct se_if_priv *)se_if_data, fuse_id, value);
+}
+EXPORT_SYMBOL_GPL(imx_se_write_fuse);
diff --git a/drivers/firmware/imx/ele_base_msg.h b/drivers/firmware/imx/ele_base_msg.h
index dd89412f485e..22f553cdbc33 100644
--- a/drivers/firmware/imx/ele_base_msg.h
+++ b/drivers/firmware/imx/ele_base_msg.h
@@ -19,6 +19,14 @@
 #define ELE_GET_INFO_REQ_MSG_SZ		0x10
 #define ELE_GET_INFO_RSP_MSG_SZ		0x08
 
+#define ELE_WRITE_FUSE			0xD6
+#define ELE_WRITE_FUSE_REQ_MSG_SZ	12
+#define ELE_WRITE_FUSE_RSP_MSG_SZ	12
+
+#define ELE_READ_FUSE_REQ		0x97
+#define ELE_READ_FUSE_REQ_MSG_SZ	0x08
+#define ELE_READ_FUSE_RSP_MSG_SZ	0x0C
+
 #define MAX_UID_SIZE                     (16)
 #define DEV_GETINFO_ROM_PATCH_SHA_SZ     (32)
 #define DEV_GETINFO_FW_SHA_SZ            (32)
diff --git a/include/linux/firmware/imx/se_api.h b/include/linux/firmware/imx/se_api.h
index b1c4c9115d7b..9503b9363593 100644
--- a/include/linux/firmware/imx/se_api.h
+++ b/include/linux/firmware/imx/se_api.h
@@ -11,4 +11,7 @@
 #define SOC_ID_OF_IMX8ULP		0x084d
 #define SOC_ID_OF_IMX93			0x9300
 
+int imx_se_write_fuse(void *se_if_data, uint16_t fuse_id, u32 value);
+int imx_se_read_fuse(void *se_if_data, uint16_t fuse_id, u32 *value);
+
 #endif /* __SE_API_H__ */
-- 
2.49.0




More information about the linux-arm-kernel mailing list