summaryrefslogtreecommitdiffstats
path: root/arch/arm/mach-imx/ele.c
diff options
context:
space:
mode:
Diffstat (limited to 'arch/arm/mach-imx/ele.c')
-rw-r--r--arch/arm/mach-imx/ele.c704
1 files changed, 704 insertions, 0 deletions
diff --git a/arch/arm/mach-imx/ele.c b/arch/arm/mach-imx/ele.c
new file mode 100644
index 0000000000..48e8749b31
--- /dev/null
+++ b/arch/arm/mach-imx/ele.c
@@ -0,0 +1,704 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright 2020-2022 NXP
+ */
+#define pr_fmt(fmt) "ele: " fmt
+
+#include <common.h>
+#include <io.h>
+#include <mach/imx/ele.h>
+#include <mach/imx/imx9-regs.h>
+#include <linux/iopoll.h>
+#include <firmware.h>
+#include <linux/bitfield.h>
+
+#define MU_SR_TE0_MASK BIT(0)
+#define MU_SR_RF0_MASK BIT(0)
+#define MU_TR_COUNT 8
+#define MU_RR_COUNT 4
+
+struct mu_type {
+ u32 ver;
+ u32 par;
+ u32 cr;
+ u32 sr;
+ u32 reserved0[60];
+ u32 fcr;
+ u32 fsr;
+ u32 reserved1[2];
+ u32 gier;
+ u32 gcr;
+ u32 gsr;
+ u32 reserved2;
+ u32 tcr;
+ u32 tsr;
+ u32 rcr;
+ u32 rsr;
+ u32 reserved3[52];
+ u32 tr[16];
+ u32 reserved4[16];
+ u32 rr[16];
+ u32 reserved5[14];
+ u32 mu_attr;
+};
+
+static int mu_hal_sendmsg(void __iomem *base, u32 reg_index, u32 msg)
+{
+ struct mu_type *mu_base = base;
+ u32 mask = MU_SR_TE0_MASK << reg_index;
+ u32 val;
+ int ret;
+
+ /* Wait TX register to be empty. */
+ ret = readl_poll_timeout(&mu_base->tsr, val, val & mask, 10000);
+ if (ret < 0) {
+ pr_debug("%s timeout\n", __func__);
+ return -ETIMEDOUT;
+ }
+
+ writel(msg, &mu_base->tr[reg_index]);
+
+ return 0;
+}
+
+static int mu_hal_receivemsg(void __iomem *base, u32 reg_index, u32 *msg)
+{
+ struct mu_type *mu_base = base;
+ u32 mask = MU_SR_RF0_MASK << reg_index;
+ u32 val;
+ int ret;
+
+ /* Wait RX register to be full. */
+ ret = readl_poll_timeout(&mu_base->rsr, val, val & mask, 10000000);
+ if (ret < 0)
+ return -ETIMEDOUT;
+
+ *msg = readl(&mu_base->rr[reg_index]);
+
+ return 0;
+}
+
+static int mu_read(void __iomem *base, struct ele_msg *msg)
+{
+ int ret, i;
+
+ /* Read first word */
+ ret = mu_hal_receivemsg(base, 0, (u32 *)msg);
+ if (ret)
+ return ret;
+
+ /* Read remaining words */
+ for (i = 1; i < msg->size; i++) {
+ ret = mu_hal_receivemsg(base, i % MU_RR_COUNT, &msg->data[i - 1]);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+
+static int mu_write(void __iomem *base, struct ele_msg *msg)
+{
+ int ret, i;
+
+ /* Write first word */
+ ret = mu_hal_sendmsg(base, 0, *((u32 *)msg));
+ if (ret)
+ return ret;
+
+ /* Write remaining words */
+ for (i = 1; i < msg->size; i++) {
+ ret = mu_hal_sendmsg(base, i % MU_TR_COUNT, msg->data[i - 1]);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+
+static int imx9_s3mua_call(struct ele_msg *msg)
+{
+ void __iomem *s3mua = IOMEM(MX9_S3MUA_BASE_ADDR);
+ u32 result;
+ int ret;
+
+ ret = mu_write(s3mua, msg);
+ if (ret)
+ return ret;
+
+ ret = mu_read(s3mua, msg);
+ if (ret)
+ return ret;
+
+ result = msg->data[0];
+ if ((result & 0xff) == 0xd6)
+ return 0;
+
+ return -EIO;
+}
+
+int ele_call(struct ele_msg *msg)
+{
+ return imx9_s3mua_call(msg);
+}
+
+int ele_get_info(struct ele_get_info_data *info)
+{
+ struct ele_msg msg = {
+ .version = ELE_VERSION,
+ .tag = ELE_CMD_TAG,
+ .size = 4,
+ .command = ELE_GET_INFO_REQ,
+ .data = {
+ upper_32_bits((unsigned long)info),
+ lower_32_bits((unsigned long)info),
+ sizeof(struct ele_get_info_data),
+ },
+ };
+ int ret;
+
+ ret = ele_call(&msg);
+ if (ret)
+ pr_err("Could not get ELE info: ret %d, response 0x%x\n",
+ ret, msg.data[0]);
+
+ return ret;
+}
+
+static int ele_get_start_trng(void)
+{
+ struct ele_msg msg = {
+ .version = ELE_VERSION,
+ .tag = ELE_CMD_TAG,
+ .size = 1,
+ .command = ELE_START_RNG,
+ };
+ int ret;
+
+ ret = ele_call(&msg);
+ if (ret)
+ pr_err("Could not start TRNG, response 0x%x\n", msg.data[0]);
+
+ return ret;
+}
+
+int imx93_ele_load_fw(void *bl33)
+{
+ struct ele_get_info_data info = {};
+ struct ele_msg msg = {
+ .version = ELE_VERSION,
+ .tag = ELE_CMD_TAG,
+ .size = 4,
+ .command = ELE_FW_AUTH_REQ,
+ };
+ void *firmware;
+ int size, ret;
+ int rev = 0;
+
+ ele_get_info(&info);
+
+ rev = FIELD_GET(ELE_INFO_SOC_REV, info.soc);
+
+ switch (rev) {
+ case 0xa0:
+ get_builtin_firmware_ext(mx93a0_ahab_container_img, bl33, &firmware, &size);
+ break;
+ case 0xa1:
+ get_builtin_firmware_ext(mx93a1_ahab_container_img, bl33, &firmware, &size);
+ break;
+ default:
+ pr_err("Unknown unhandled SoC revision %2x\n", rev);
+ return -EINVAL;
+ }
+
+ /* Address of the container header */
+ msg.data[0] = lower_32_bits((unsigned long)firmware);
+ /* Actual address of the container header */
+ msg.data[2] = lower_32_bits((unsigned long)firmware);
+
+ ret = ele_call(&msg);
+ if (ret)
+ pr_err("Could not start ELE firmware: ret %d, response 0x%x\n",
+ ret, msg.data[0]);
+
+ if (rev >= 0xa1)
+ ele_get_start_trng();
+
+ return 0;
+}
+
+/*
+ * ele_read_common_fuse - read a fuse
+ * @fuse_id: The fuse to read (in 32bit word number)
+ * fuse_word: The value read from the fuse
+ * @response: on return contains the response from ELE
+ *
+ * This reads the shadow value of the fuse @fuse_id.
+ *
+ * Return: 0 when the ELE call succeeds, negative error code otherwise
+ */
+int ele_read_common_fuse(u16 fuse_id, u32 *fuse_word, u32 *response)
+{
+ struct ele_msg msg;
+ int ret;
+
+ msg.version = ELE_VERSION;
+ msg.tag = ELE_CMD_TAG;
+ msg.size = 2;
+ msg.command = ELE_READ_FUSE_REQ;
+ msg.data[0] = fuse_id;
+
+ ret = imx9_s3mua_call(&msg);
+
+ if (response)
+ *response = msg.data[0];
+
+ *fuse_word = msg.data[1];
+
+ return ret;
+}
+
+/*
+ * ele_read_shadow_fuse - read a fuse
+ * @fuse_id: The fuse to read (in 32bit word number)
+ * fuse_word: The value read from the fuse
+ * @response: on return contains the response from ELE
+ *
+ * This reads the shadow value of the fuse @fuse_id.
+ *
+ * Return: 0 when the ELE call succeeds, negative error code otherwise
+ */
+int ele_read_shadow_fuse(u16 fuse_id, u32 *fuse_word, u32 *response)
+{
+ struct ele_msg msg;
+ int ret;
+
+ msg.version = ELE_VERSION;
+ msg.tag = ELE_CMD_TAG;
+ msg.size = 2;
+ msg.command = ELE_READ_SHADOW_REQ;
+ msg.data[0] = fuse_id;
+
+ ret = imx9_s3mua_call(&msg);
+
+ if (response)
+ *response = msg.data[0];
+
+ *fuse_word = msg.data[1];
+
+ return ret;
+}
+
+/*
+ * ele_write_fuse - write a fuse
+ * @fuse_id: The fuse to write to (in 32bit word number)
+ * @fuse_val: The value to write to the fuse
+ * @lock: lock fuse after writing
+ * @response: on return contains the response from ELE
+ *
+ * This writes the 32bit given in @fuse_val to the fuses at @fuse_id. This is
+ * a permanent change, be careful.
+ *
+ * Return: 0 when the ELE call succeeds, negative error code otherwise
+ */
+int ele_write_fuse(u16 fuse_id, u32 fuse_val, bool lock, u32 *response)
+{
+ struct ele_msg msg;
+ int ret;
+
+ msg.version = ELE_VERSION;
+ msg.tag = ELE_CMD_TAG;
+ msg.size = 3;
+ msg.command = ELE_WRITE_FUSE_REQ;
+ msg.data[0] = (32 << 16) | (fuse_id << 5);
+
+ if (lock)
+ msg.data[0] |= (1 << 31);
+
+ msg.data[1] = fuse_val;
+
+ ret = imx9_s3mua_call(&msg);
+
+ if (response)
+ *response = msg.data[0];
+
+ return ret;
+}
+
+/*
+ * ele_write_shadow_fuse - write a fuse
+ * @fuse_id: The fuse to write to
+ * @fuse_val: The value to write to the fuse
+ * @lock: lock fuse after writing
+ * @response: on return contains the response from ELE
+ *
+ * This writes the 32bit given in @fuse_val to the fuses at @fuse_id. This is
+ * a permanent change, be careful.
+ *
+ * Return: 0 when the ELE call succeeds, negative error code otherwise
+ */
+int ele_write_shadow_fuse(u16 fuse_id, u32 fuse_val, u32 *response)
+{
+ struct ele_msg msg;
+ int ret;
+
+ msg.version = ELE_VERSION;
+ msg.tag = ELE_CMD_TAG;
+ msg.size = 3;
+ msg.command = ELE_WRITE_SHADOW_REQ;
+ msg.data[0] = fuse_id;
+
+ msg.data[1] = fuse_val;
+
+ ret = imx9_s3mua_call(&msg);
+
+ if (response)
+ *response = msg.data[0];
+
+ return ret;
+}
+
+/*
+ * ele_forward_lifecycle - forward lifecycle
+ * @lc: The lifecycle value to forward to
+ * @response: on return contains the response from ELE
+ *
+ * This changes the chip's lifecycle value. Mainly useful to forward to
+ * from ELE_LIFECYCLE_OEM_OPEN to ELE_LIFECYCLE_OEM_CLOSED. When doing
+ * this the SoC will only boot authenticated images. Make sure the correct
+ * SRK has been fused beforehand, otherwise you brick your board.
+ *
+ * Return: 0 when the ELE call succeeds, negative error code otherwise
+ */
+int ele_forward_lifecycle(enum ele_lifecycle lc, u32 *response)
+{
+ struct ele_msg msg;
+ int ret;
+
+ msg.version = ELE_VERSION;
+ msg.tag = ELE_CMD_TAG;
+ msg.size = 2;
+ msg.command = ELE_FWD_LIFECYCLE_UP_REQ;
+ msg.data[0] = lc;
+
+ ret = imx9_s3mua_call(&msg);
+
+ if (response)
+ *response = msg.data[0];
+
+ return ret;
+}
+
+/*
+ * ele_authenticate_container - authenticate a container image
+ * @addr: the address of the container
+ * @response: on return contains the response from ELE
+ *
+ * This authenticates a container with the ELE. On return the result
+ * of the authentication will be encoded in @response
+ *
+ * Return: 0 when the ELE call succeeds, negative error code otherwise
+ */
+int ele_authenticate_container(unsigned long addr, u32 *response)
+{
+ struct ele_msg msg;
+ int ret;
+
+ msg.version = ELE_VERSION;
+ msg.tag = ELE_CMD_TAG;
+ msg.size = 3;
+ msg.command = ELE_OEM_CNTN_AUTH_REQ;
+ msg.data[0] = upper_32_bits(addr);
+ msg.data[1] = lower_32_bits(addr);
+
+ ret = imx9_s3mua_call(&msg);
+
+ if (response)
+ *response = msg.data[0];
+
+ return ret;
+}
+
+/*
+ * ele_release_container - release a container image
+ * @response: on return contains the response from ELE
+ *
+ * This releases a container image. Must be called when done with an
+ * image previously authenticated with ele_authenticate_container()
+ *
+ * Return: 0 when the ELE call succeeds, negative error code otherwise
+ */
+int ele_release_container(u32 *response)
+{
+ struct ele_msg msg;
+ int ret;
+
+ msg.version = ELE_VERSION;
+ msg.tag = ELE_CMD_TAG;
+ msg.size = 1;
+ msg.command = ELE_RELEASE_CONTAINER_REQ;
+
+ ret = imx9_s3mua_call(&msg);
+
+ if (response)
+ *response = msg.data[0];
+
+ return ret;
+}
+
+int ele_release_rdc(u8 core_id, u8 xrdc, u32 *response)
+{
+ struct ele_msg msg;
+ int ret;
+
+ msg.version = ELE_VERSION;
+ msg.tag = ELE_CMD_TAG;
+ msg.size = 2;
+ msg.command = ELE_RELEASE_RDC_REQ;
+
+ switch (xrdc) {
+ case 0:
+ msg.data[0] = (0x74 << 8) | core_id;
+ break;
+ case 1:
+ msg.data[0] = (0x78 << 8) | core_id;
+ break;
+ case 2:
+ msg.data[0] = (0x82 << 8) | core_id;
+ break;
+ case 3:
+ msg.data[0] = (0x86 << 8) | core_id;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ ret = ele_call(&msg);
+ if (ret)
+ pr_err("%s: ret %d, core id %u, response 0x%x\n",
+ __func__, ret, core_id, msg.data[0]);
+
+ if (response)
+ *response = msg.data[0];
+
+ return ret;
+}
+
+struct ele_str {
+ u8 id;
+ const char *str;
+};
+
+static struct ele_str ele_ind[] = {
+ { .id = ELE_ROM_PING_FAILURE_IND, .str = "ELE_ROM_PING_FAILURE" },
+ { .id = ELE_FW_PING_FAILURE_IND, .str = "ELE_FW_PING_FAILURE" },
+ { .id = ELE_BAD_SIGNATURE_FAILURE_IND, .str = "ELE_BAD_SIGNATURE_FAILURE" },
+ { .id = ELE_BAD_HASH_FAILURE_IND, .str = "ELE_BAD_HASH_FAILURE" },
+ { .id = ELE_INVALID_LIFECYCLE_IND, .str = "ELE_INVALID_LIFECYCLE" },
+ { .id = ELE_PERMISSION_DENIED_FAILURE_IND, .str = "ELE_PERMISSION_DENIED_FAILURE" },
+ { .id = ELE_INVALID_MESSAGE_FAILURE_IND, .str = "ELE_INVALID_MESSAGE_FAILURE" },
+ { .id = ELE_BAD_VALUE_FAILURE_IND, .str = "ELE_BAD_VALUE_FAILURE" },
+ { .id = ELE_BAD_FUSE_ID_FAILURE_IND, .str = "ELE_BAD_FUSE_ID_FAILURE" },
+ { .id = ELE_BAD_CONTAINER_FAILURE_IND, .str = "ELE_BAD_CONTAINER_FAILURE" },
+ { .id = ELE_BAD_VERSION_FAILURE_IND, .str = "ELE_BAD_VERSION_FAILURE" },
+ { .id = ELE_INVALID_KEY_FAILURE_IND, .str = "ELE_INVALID_KEY_FAILURE" },
+ { .id = ELE_BAD_KEY_HASH_FAILURE_IND, .str = "ELE_BAD_KEY_HASH_FAILURE" },
+ { .id = ELE_NO_VALID_CONTAINER_FAILURE_IND, .str = "ELE_NO_VALID_CONTAINER_FAILURE" },
+ { .id = ELE_BAD_CERTIFICATE_FAILURE_IND, .str = "ELE_BAD_CERTIFICATE_FAILURE" },
+ { .id = ELE_BAD_UID_FAILURE_IND, .str = "ELE_BAD_UID_FAILURE" },
+ { .id = ELE_BAD_MONOTONIC_COUNTER_FAILURE_IND, .str = "ELE_BAD_MONOTONIC_COUNTER_FAILURE" },
+ { .id = ELE_MUST_SIGNED_FAILURE_IND, .str = "ELE_MUST_SIGNED_FAILURE" },
+ { .id = ELE_NO_AUTHENTICATION_FAILURE_IND, .str = "ELE_NO_AUTHENTICATION_FAILURE" },
+ { .id = ELE_BAD_SRK_SET_FAILURE_IND, .str = "ELE_BAD_SRK_SET_FAILURE" },
+ { .id = ELE_UNALIGNED_PAYLOAD_FAILURE_IND, .str = "ELE_UNALIGNED_PAYLOAD_FAILURE" },
+ { .id = ELE_WRONG_SIZE_FAILURE_IND, .str = "ELE_WRONG_SIZE_FAILURE" },
+ { .id = ELE_ENCRYPTION_FAILURE_IND, .str = "ELE_ENCRYPTION_FAILURE" },
+ { .id = ELE_DECRYPTION_FAILURE_IND, .str = "ELE_DECRYPTION_FAILURE" },
+ { .id = ELE_OTP_PROGFAIL_FAILURE_IND, .str = "ELE_OTP_PROGFAIL_FAILURE" },
+ { .id = ELE_OTP_LOCKED_FAILURE_IND, .str = "ELE_OTP_LOCKED_FAILURE" },
+ { .id = ELE_OTP_INVALID_IDX_FAILURE_IND, .str = "ELE_OTP_INVALID_IDX_FAILURE" },
+ { .id = ELE_TIME_OUT_FAILURE_IND, .str = "ELE_TIME_OUT_FAILURE" },
+ { .id = ELE_BAD_PAYLOAD_FAILURE_IND, .str = "ELE_BAD_PAYLOAD_FAILURE" },
+ { .id = ELE_WRONG_ADDRESS_FAILURE_IND, .str = "ELE_WRONG_ADDRESS_FAILURE" },
+ { .id = ELE_DMA_FAILURE_IND, .str = "ELE_DMA_FAILURE" },
+ { .id = ELE_DISABLED_FEATURE_FAILURE_IND, .str = "ELE_DISABLED_FEATURE_FAILURE" },
+ { .id = ELE_MUST_ATTEST_FAILURE_IND, .str = "ELE_MUST_ATTEST_FAILURE" },
+ { .id = ELE_RNG_NOT_STARTED_FAILURE_IND, .str = "ELE_RNG_NOT_STARTED_FAILURE" },
+ { .id = ELE_CRC_ERROR_IND, .str = "ELE_CRC_ERROR" },
+ { .id = ELE_AUTH_SKIPPED_OR_FAILED_FAILURE_IND, .str = "ELE_AUTH_SKIPPED_OR_FAILED_FAILURE" },
+ { .id = ELE_INCONSISTENT_PAR_FAILURE_IND, .str = "ELE_INCONSISTENT_PAR_FAILURE" },
+ { .id = ELE_RNG_INST_FAILURE_FAILURE_IND, .str = "ELE_RNG_INST_FAILURE_FAILURE" },
+ { .id = ELE_LOCKED_REG_FAILURE_IND, .str = "ELE_LOCKED_REG_FAILURE" },
+ { .id = ELE_BAD_ID_FAILURE_IND, .str = "ELE_BAD_ID_FAILURE" },
+ { .id = ELE_INVALID_OPERATION_FAILURE_IND, .str = "ELE_INVALID_OPERATION_FAILURE" },
+ { .id = ELE_NON_SECURE_STATE_FAILURE_IND, .str = "ELE_NON_SECURE_STATE_FAILURE" },
+ { .id = ELE_MSG_TRUNCATED_IND, .str = "ELE_MSG_TRUNCATED" },
+ { .id = ELE_BAD_IMAGE_NUM_FAILURE_IND, .str = "ELE_BAD_IMAGE_NUM_FAILURE" },
+ { .id = ELE_BAD_IMAGE_ADDR_FAILURE_IND, .str = "ELE_BAD_IMAGE_ADDR_FAILURE" },
+ { .id = ELE_BAD_IMAGE_PARAM_FAILURE_IND, .str = "ELE_BAD_IMAGE_PARAM_FAILURE" },
+ { .id = ELE_BAD_IMAGE_TYPE_FAILURE_IND, .str = "ELE_BAD_IMAGE_TYPE_FAILURE" },
+ { .id = ELE_CORRUPTED_SRK_FAILURE_IND, .str = "ELE_CORRUPTED_SRK_FAILURE" },
+ { .id = ELE_OUT_OF_MEMORY_IND, .str = "ELE_OUT_OF_MEMORY" },
+ { .id = ELE_CSTM_FAILURE_IND, .str = "ELE_CSTM_FAILURE" },
+ { .id = ELE_OLD_VERSION_FAILURE_IND, .str = "ELE_OLD_VERSION_FAILURE" },
+ { .id = ELE_WRONG_BOOT_MODE_FAILURE_IND, .str = "ELE_WRONG_BOOT_MODE_FAILURE" },
+ { .id = ELE_APC_ALREADY_ENABLED_FAILURE_IND, .str = "ELE_APC_ALREADY_ENABLED_FAILURE" },
+ { .id = ELE_RTC_ALREADY_ENABLED_FAILURE_IND, .str = "ELE_RTC_ALREADY_ENABLED_FAILURE" },
+ { .id = ELE_ABORT_IND, .str = "ELE_ABORT" },
+};
+
+static struct ele_str ele_ipc[] = {
+ { .id = ELE_IPC_MU_RTD, .str = "MU RTD" },
+ { .id = ELE_IPC_MU_APD, .str = "MU APD" },
+};
+
+static struct ele_str ele_command[] = {
+ { .id = ELE_PING_REQ, .str = "ELE_PING" },
+ { .id = ELE_FW_AUTH_REQ, .str = "ELE_FW_AUTH" },
+ { .id = ELE_RESTART_RST_TIMER_REQ, .str = "ELE_RESTART_RST_TIMER" },
+ { .id = ELE_DUMP_DEBUG_BUFFER_REQ, .str = "ELE_DUMP_DEBUG_BUFFER" },
+ { .id = ELE_OEM_CNTN_AUTH_REQ, .str = "ELE_OEM_CNTN_AUTH" },
+ { .id = ELE_VERIFY_IMAGE_REQ, .str = "ELE_VERIFY_IMAGE" },
+ { .id = ELE_RELEASE_CONTAINER_REQ, .str = "ELE_RELEASE_CONTAINER" },
+ { .id = ELE_WRITE_SECURE_FUSE_REQ, .str = "ELE_WRITE_SECURE_FUSE" },
+ { .id = ELE_FWD_LIFECYCLE_UP_REQ, .str = "ELE_FWD_LIFECYCLE_UP" },
+ { .id = ELE_READ_FUSE_REQ, .str = "ELE_READ_FUSE" },
+ { .id = ELE_GET_FW_VERSION_REQ, .str = "ELE_GET_FW_VERSION" },
+ { .id = ELE_RET_LIFECYCLE_UP_REQ, .str = "ELE_RET_LIFECYCLE_UP" },
+ { .id = ELE_GET_EVENTS_REQ, .str = "ELE_GET_EVENTS" },
+ { .id = ELE_ENABLE_PATCH_REQ, .str = "ELE_ENABLE_PATCH" },
+ { .id = ELE_RELEASE_RDC_REQ, .str = "ELE_RELEASE_RDC" },
+ { .id = ELE_GET_FW_STATUS_REQ, .str = "ELE_GET_FW_STATUS" },
+ { .id = ELE_ENABLE_OTFAD_REQ, .str = "ELE_ENABLE_OTFAD" },
+ { .id = ELE_RESET_REQ, .str = "ELE_RESET" },
+ { .id = ELE_UPDATE_OTP_CLKDIV_REQ, .str = "ELE_UPDATE_OTP_CLKDIV" },
+ { .id = ELE_POWER_DOWN_REQ, .str = "ELE_POWER_DOWN" },
+ { .id = ELE_ENABLE_APC_REQ, .str = "ELE_ENABLE_APC" },
+ { .id = ELE_ENABLE_RTC_REQ, .str = "ELE_ENABLE_RTC" },
+ { .id = ELE_DEEP_POWER_DOWN_REQ, .str = "ELE_DEEP_POWER_DOWN" },
+ { .id = ELE_STOP_RST_TIMER_REQ, .str = "ELE_STOP_RST_TIMER" },
+ { .id = ELE_WRITE_FUSE_REQ, .str = "ELE_WRITE_FUSE" },
+ { .id = ELE_RELEASE_CAAM_REQ, .str = "ELE_RELEASE_CAAM" },
+ { .id = ELE_RESET_A35_CTX_REQ, .str = "ELE_RESET_A35_CTX" },
+ { .id = ELE_MOVE_TO_UNSECURED_REQ, .str = "ELE_MOVE_TO_UNSECURED" },
+ { .id = ELE_GET_INFO_REQ, .str = "ELE_GET_INFO" },
+ { .id = ELE_ATTEST_REQ, .str = "ELE_ATTEST" },
+ { .id = ELE_RELEASE_PATCH_REQ, .str = "ELE_RELEASE_PATCH" },
+ { .id = ELE_OTP_SEQ_SWITH_REQ, .str = "ELE_OTP_SEQ_SWITH" },
+};
+
+static struct ele_str ele_status[] = {
+ { .id = ELE_SUCCESS_IND, .str = "ELE_SUCCESS" },
+ { .id = ELE_FAILURE_IND, .str = "ELE_FAILURE" },
+};
+
+static const struct ele_str *get_idx(struct ele_str *str, int size, int id)
+{
+ u32 i;
+
+ for (i = 0; i < size; i++) {
+ if (str[i].id == id)
+ return &str[i];
+ }
+
+ return NULL;
+}
+
+#define ELE_EVENT_IPC GENMASK(31, 24)
+#define ELE_EVENT_COMMAND GENMASK(23, 16)
+#define ELE_EVENT_IND GENMASK(15, 8)
+#define ELE_EVENT_STATUS GENMASK(7, 0)
+
+static void display_event(u32 event)
+{
+ int ipc = FIELD_GET(ELE_EVENT_IPC, event);
+ int command = FIELD_GET(ELE_EVENT_COMMAND, event);
+ int ind = FIELD_GET(ELE_EVENT_IND, event);
+ int status = FIELD_GET(ELE_EVENT_STATUS, event);
+ const struct ele_str *ipc_str = get_idx(ARRAY_AND_SIZE(ele_ipc), ipc);
+ const struct ele_str *command_str = get_idx(ARRAY_AND_SIZE(ele_command), command);
+ const struct ele_str *ind_str = get_idx(ARRAY_AND_SIZE(ele_ind), ind);
+ const struct ele_str *status_str = get_idx(ARRAY_AND_SIZE(ele_status), status);
+
+ pr_info("Event 0x%08x:\n", event);
+ pr_info(" IPC = %s (0x%02x)\n", ipc_str ? ipc_str->str : "INVALID", ipc);
+ pr_info(" CMD = %s (0x%02x)\n", command_str ? command_str->str : "INVALID", command);
+ pr_info(" IND = %s (0x%02x)\n", ind_str ? ind_str->str : "INVALID", ind);
+ pr_info(" STA = %s (0x%02x)\n", status_str ? status_str->str : "INVALID", status);
+}
+
+#define AHAB_MAX_EVENTS 8
+
+static int ahab_get_events(u32 *events)
+{
+ struct ele_msg msg;
+ int ret, i = 0;
+ u32 n_events;
+
+ msg.version = ELE_VERSION;
+ msg.tag = ELE_CMD_TAG;
+ msg.size = 1;
+ msg.command = ELE_GET_EVENTS_REQ;
+
+ ret = imx9_s3mua_call(&msg);
+ if (ret) {
+ pr_err("%s: ret %d, response 0x%x\n", __func__, ret, msg.data[0]);
+
+ return ret;
+ }
+
+ n_events = msg.data[1] & 0xffff;
+
+ if (n_events > AHAB_MAX_EVENTS)
+ n_events = AHAB_MAX_EVENTS;
+
+ for (; i < n_events; i++)
+ events[i] = msg.data[i + 2];
+
+ return n_events;
+}
+
+unsigned int imx93_ahab_read_lifecycle(void)
+{
+ return readl(MX9_OCOTP_BASE_ADDR + 0x41c) & 0x3ff;
+}
+
+static const char *ele_life_cycle(u32 lc)
+{
+ switch (lc) {
+ case ELE_LIFECYCLE_BLANK: return "BLANK";
+ case ELE_LIFECYCLE_FAB: return "FAB";
+ case ELE_LIFECYCLE_NXP_PROVISIONED: return "NXP Provisioned";
+ case ELE_LIFECYCLE_OEM_OPEN: return "OEM Open";
+ case ELE_LIFECYCLE_OEM_CLOSED: return "OEM closed";
+ case ELE_LIFECYCLE_FIELD_RETURN_OEM: return "Field Return OEM";
+ case ELE_LIFECYCLE_FIELD_RETURN_NXP: return "Field Return NXP";
+ case ELE_LIFECYCLE_OEM_LOCKED: return "OEM Locked";
+ case ELE_LIFECYCLE_BRICKED: return "BRICKED";
+ default: return "Unknown";
+ }
+}
+
+int ele_print_events(void)
+{
+ unsigned int lc;
+ u32 events[AHAB_MAX_EVENTS];
+ int i, ret;
+
+ lc = imx93_ahab_read_lifecycle();
+ pr_info("Current lifecycle: %s\n", ele_life_cycle(lc));
+
+ ret = ahab_get_events(events);
+ if (ret < 0)
+ return ret;
+
+ if (!ret) {
+ pr_info("No Events Found!\n");
+ return 0;
+ }
+
+ for (i = 0; i < ret; i++)
+ display_event(events[i]);
+
+ return 0;
+}