NXP: SFP driver support for NXP SoC

NXP Security Fuse Processor is used to read and write
fuses.
- Fuses once written, are cannot be un-done.
- Used as trust anchor for monotonic counter,
  different platform keys etc.

Signed-off-by: Udit Agarwal <udit.agarwal@nxp.com>
Signed-off-by: Ruchika Gupta <ruchika.gupta@nxp.com>
Signed-off-by: Pankaj Gupta <pankaj.gupta@nxp.com>
Change-Id: I347e806dd87078150fbbbfc28355bb44d9eacb9c
diff --git a/drivers/nxp/sfp/sfp.c b/drivers/nxp/sfp/sfp.c
new file mode 100644
index 0000000..e06c6b9
--- /dev/null
+++ b/drivers/nxp/sfp/sfp.c
@@ -0,0 +1,167 @@
+/*
+ * Copyright 2021 NXP
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ */
+
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <caam.h>
+#include <common/debug.h>
+#include <drivers/delay_timer.h>
+#include <sfp.h>
+#include <sfp_error_codes.h>
+
+static uintptr_t g_nxp_sfp_addr;
+static uint32_t srk_hash[SRK_HASH_SIZE/sizeof(uint32_t)]
+					__aligned(CACHE_WRITEBACK_GRANULE);
+
+void sfp_init(uintptr_t nxp_sfp_addr)
+{
+	g_nxp_sfp_addr = nxp_sfp_addr;
+}
+
+uintptr_t get_sfp_addr(void)
+{
+	return g_nxp_sfp_addr;
+}
+
+uint32_t *get_sfp_srk_hash(void)
+{
+	struct sfp_ccsr_regs_t *sfp_ccsr_regs =
+			(void *) (g_nxp_sfp_addr + SFP_FUSE_REGS_OFFSET);
+	int i = 0;
+
+	/* Add comparison of hash with SFP hash here */
+	for (i = 0; i < SRK_HASH_SIZE/sizeof(uint32_t); i++)
+		srk_hash[i] =
+			mmio_read_32((uintptr_t)&sfp_ccsr_regs->srk_hash[i]);
+
+	return srk_hash;
+}
+
+void set_sfp_wr_disable(void)
+{
+	/*
+	 * Mark SFP Write Disable and Write Disable Lock
+	 * Bit to prevent write to SFP fuses like
+	 * OUID's, Key Revocation fuse etc
+	 */
+	void *sfpcr = (void *)(g_nxp_sfp_addr + SFP_SFPCR_OFFSET);
+	uint32_t sfpcr_val;
+
+	sfpcr_val = sfp_read32(sfpcr);
+	sfpcr_val |= (SFP_SFPCR_WD | SFP_SFPCR_WDL);
+	sfp_write32(sfpcr, sfpcr_val);
+}
+
+int sfp_program_fuses(void)
+{
+	uint32_t ingr;
+	uint32_t sfp_cmd_status = 0U;
+	int ret = 0;
+
+	/* Program SFP fuses from mirror registers */
+	sfp_write32((void *)(g_nxp_sfp_addr + SFP_INGR_OFFSET),
+		    SFP_INGR_PROGFB_CMD);
+
+	/* Wait until fuse programming is successful */
+	do {
+		ingr = sfp_read32(g_nxp_sfp_addr + SFP_INGR_OFFSET);
+	} while (ingr & SFP_INGR_PROGFB_CMD);
+
+	/* Check for SFP fuse programming error */
+	sfp_cmd_status = sfp_read32(g_nxp_sfp_addr + SFP_INGR_OFFSET)
+			 & SFP_INGR_ERROR_MASK;
+
+	if (sfp_cmd_status != 0U) {
+		return ERROR_PROGFB_CMD;
+	}
+
+	return ret;
+}
+
+uint32_t sfp_read_oem_uid(uint8_t oem_uid)
+{
+	uint32_t val = 0U;
+	struct sfp_ccsr_regs_t *sfp_ccsr_regs = (void *)(g_nxp_sfp_addr
+							+ SFP_FUSE_REGS_OFFSET);
+
+	if (oem_uid > MAX_OEM_UID) {
+		ERROR("Invalid OEM UID received.\n");
+		return ERROR_OEMUID_WRITE;
+	}
+
+	val = sfp_read32(&sfp_ccsr_regs->oem_uid[oem_uid]);
+
+	return val;
+}
+
+/*
+ * return val:  0 - No update required.
+ *              1 - successful update done.
+ *              ERROR_OEMUID_WRITE - Invalid OEM UID
+ */
+uint32_t sfp_write_oem_uid(uint8_t oem_uid, uint32_t sfp_val)
+{
+	uint32_t val = 0U;
+	struct sfp_ccsr_regs_t *sfp_ccsr_regs = (void *)(g_nxp_sfp_addr
+							+ SFP_FUSE_REGS_OFFSET);
+
+	val = sfp_read_oem_uid(oem_uid);
+
+	if (val == ERROR_OEMUID_WRITE) {
+		return ERROR_OEMUID_WRITE;
+	}
+
+	/* Counter already set. No need to do anything */
+	if ((val & sfp_val) != 0U) {
+		return 0U;
+	}
+
+	val |= sfp_val;
+
+	INFO("SFP Value is %x for setting sfp_val = %d\n", val, sfp_val);
+
+	sfp_write32(&sfp_ccsr_regs->oem_uid[oem_uid], val);
+
+	return 1U;
+}
+
+int sfp_check_its(void)
+{
+	struct sfp_ccsr_regs_t *sfp_ccsr_regs = (void *)(g_nxp_sfp_addr
+							+ SFP_FUSE_REGS_OFFSET);
+
+	if ((sfp_read32(&sfp_ccsr_regs->ospr) & OSPR_ITS_MASK) != 0) {
+		return 1;
+	} else {
+		return 0;
+	}
+}
+
+int sfp_check_oem_wp(void)
+{
+	struct sfp_ccsr_regs_t *sfp_ccsr_regs = (void *)(g_nxp_sfp_addr
+							+ SFP_FUSE_REGS_OFFSET);
+
+	if ((sfp_read32(&sfp_ccsr_regs->ospr) & OSPR_WP_MASK) != 0) {
+		return 1;
+	} else {
+		return 0;
+	}
+}
+
+/* This function returns ospr's key_revoc values.*/
+uint32_t get_key_revoc(void)
+{
+	struct sfp_ccsr_regs_t *sfp_ccsr_regs = (void *)(g_nxp_sfp_addr
+							+ SFP_FUSE_REGS_OFFSET);
+
+	return (sfp_read32(&sfp_ccsr_regs->ospr) & OSPR_KEY_REVOC_MASK) >>
+						OSPR_KEY_REVOC_SHIFT;
+}