MLK-18732 imx8: Add fuse driver for read/write OTP memory
authorYe Li <ye.li@nxp.com>
Wed, 17 May 2017 07:06:15 +0000 (02:06 -0500)
committerYe Li <ye.li@nxp.com>
Fri, 24 May 2019 08:47:53 +0000 (01:47 -0700)
Implement a fuse driver to access OTP memory. When CONFIG_SMC_FUSE is set,
the driver uses SIP call to ATF to read/write OTP. When CONFIG_SMC_FUSE is
not set, the driver wraps the SCFW API and only provide interfaces
to read the OTP.

Since there is no concept of fuse bank on i.MX8. Need set "bank" parameter to 0
when using the fuse command.

Signed-off-by: Ye Li <ye.li@nxp.com>
Signed-off-by: Teo Hall <teo.hall@nxp.com>
arch/arm/include/asm/mach-imx/sys_proto.h
arch/arm/mach-imx/imx8/Kconfig
arch/arm/mach-imx/imx8/Makefile
arch/arm/mach-imx/imx8/fuse.c [new file with mode: 0644]
arch/arm/mach-imx/sip.c

index 4de1c16..c204698 100644 (file)
@@ -134,4 +134,6 @@ int mxs_wait_mask_clr(struct mxs_register_32 *reg, u32 mask, u32 timeout);
 
 unsigned long call_imx_sip(unsigned long id, unsigned long reg0,
                           unsigned long reg1, unsigned long reg2, unsigned long reg3);
+unsigned long call_imx_sip_ret2(unsigned long id, unsigned long reg0,
+                                unsigned long *reg1, unsigned long reg2, unsigned long reg3);
 #endif
index f76a139..44c2ffd 100644 (file)
@@ -18,6 +18,9 @@ config IMX8QXP
 config SYS_SOC
        default "imx8"
 
+config SMC_FUSE
+        bool "Call fuse commands through SMC"
+
 choice
        prompt "i.MX8 board select"
        optional
index 31ad169..8ac9e84 100644 (file)
@@ -4,4 +4,4 @@
 # SPDX-License-Identifier:     GPL-2.0+
 #
 
-obj-y += cpu.o iomux.o
+obj-y += cpu.o iomux.o fuse.o
diff --git a/arch/arm/mach-imx/imx8/fuse.c b/arch/arm/mach-imx/imx8/fuse.c
new file mode 100644 (file)
index 0000000..a91f0e6
--- /dev/null
@@ -0,0 +1,108 @@
+/*
+ * Copyright 2017-2019 NXP
+ *
+ * SPDX-License-Identifier:    GPL-2.0+
+ *
+ */
+
+#include <common.h>
+#include <errno.h>
+#include <asm/io.h>
+#include <console.h>
+#include <fuse.h>
+#include <asm/arch/sci/sci.h>
+#include <asm/arch/sys_proto.h>
+
+DECLARE_GLOBAL_DATA_PTR;
+
+
+#define FSL_ECC_WORD_START_1    0x10
+#define FSL_ECC_WORD_END_1      0x10F
+
+#ifdef CONFIG_IMX8QM
+#define FSL_ECC_WORD_START_2    0x1A0
+#define FSL_ECC_WORD_END_2      0x1FF
+#endif
+
+#ifdef CONFIG_IMX8QXP
+#define FSL_ECC_WORD_START_2    0x220
+#define FSL_ECC_WORD_END_2      0x31F
+
+#define FSL_QXP_FUSE_GAP_START  0x110
+#define FSL_QXP_FUSE_GAP_END    0x21F
+#endif
+
+#define FSL_SIP_OTP_READ             0xc200000A
+#define FSL_SIP_OTP_WRITE            0xc200000B
+
+int fuse_read(u32 bank, u32 word, u32 *val)
+{
+       return fuse_sense(bank, word, val);
+}
+
+int fuse_sense(u32 bank, u32 word, u32 *val)
+{
+       if (bank != 0) {
+               printf("Invalid bank argument, ONLY bank 0 is supported\n");
+               return -EINVAL;
+       }
+#if defined(CONFIG_SMC_FUSE)
+       unsigned long ret, value;
+       ret = call_imx_sip_ret2(FSL_SIP_OTP_READ, (unsigned long)word,
+               &value, 0, 0);
+       *val = (u32)value;
+       return ret;
+#else
+       sc_err_t err;
+
+       err = sc_misc_otp_fuse_read(-1, word, val);
+       if (err != SC_ERR_NONE) {
+               printf("fuse read error: %d\n", err);
+               return -EIO;
+       }
+
+       return 0;
+#endif
+}
+
+int fuse_prog(u32 bank, u32 word, u32 val)
+{
+       if (bank != 0) {
+               printf("Invalid bank argument, ONLY bank 0 is supported\n");
+               return -EINVAL;
+       }
+#ifdef CONFIG_IMX8QXP
+       if ((word >= FSL_QXP_FUSE_GAP_START) && (word <= FSL_QXP_FUSE_GAP_END)) {
+               printf("Invalid word argument for this SoC\n");
+               return -EINVAL;
+       }
+#endif
+
+    if (((word >= FSL_ECC_WORD_START_1) && (word <= FSL_ECC_WORD_END_1)) ||
+               ((word >= FSL_ECC_WORD_START_2) && (word <= FSL_ECC_WORD_END_2)))
+       {
+             puts("Warning: Words in this index range have ECC protection and\n"
+                  "can only be programmed once per word. Individual bit operations will\n"
+                  "be rejected after the first one. \n"
+                  "\n\n Really program this word? <y/N> \n");
+
+             if(!confirm_yesno()) {
+                 puts("Word programming aborted\n");
+                 return -EPERM;
+             }
+       }
+
+#if defined(CONFIG_SMC_FUSE)
+       return call_imx_sip(FSL_SIP_OTP_WRITE, (unsigned long)word,\
+               (unsigned long)val, 0, 0);
+#else
+       printf("Program fuse to i.MX8 in u-boot is forbidden\n");
+       return -EPERM;
+#endif
+}
+
+int fuse_override(u32 bank, u32 word, u32 val)
+{
+       printf("Override fuse to i.MX8 in u-boot is forbidden\n");
+       return -EPERM;
+}
index 8654be9..3fec3ec 100644 (file)
@@ -21,3 +21,20 @@ unsigned long call_imx_sip(unsigned long id, unsigned long reg0,
 
        return regs.regs[0];
 }
+
+/* do an SMC call to return 2 registers by having reg1 passed in by reference */
+unsigned long call_imx_sip_ret2(unsigned long id, unsigned long reg0, unsigned long *reg1, unsigned long reg2, unsigned long reg3)
+{
+       struct pt_regs regs;
+
+       regs.regs[0] = id;
+       regs.regs[1] = reg0;
+       regs.regs[2] = *reg1;
+       regs.regs[3] = reg2;
+       regs.regs[4] = reg3;
+
+       smc_call(&regs);
+
+    *reg1 = regs.regs[1];
+       return regs.regs[0];
+}