From d8de224e26eac82193dea10b4d143e0093a7f609 Mon Sep 17 00:00:00 2001 From: Ye Li Date: Wed, 17 May 2017 02:06:15 -0500 Subject: [PATCH] MLK-18732 imx8: Add fuse driver for read/write OTP memory 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 Signed-off-by: Teo Hall --- arch/arm/include/asm/mach-imx/sys_proto.h | 2 + arch/arm/mach-imx/imx8/Kconfig | 3 + arch/arm/mach-imx/imx8/Makefile | 2 +- arch/arm/mach-imx/imx8/fuse.c | 108 ++++++++++++++++++++++ arch/arm/mach-imx/sip.c | 17 ++++ 5 files changed, 131 insertions(+), 1 deletion(-) create mode 100644 arch/arm/mach-imx/imx8/fuse.c diff --git a/arch/arm/include/asm/mach-imx/sys_proto.h b/arch/arm/include/asm/mach-imx/sys_proto.h index 4de1c169a2..c204698dc3 100644 --- a/arch/arm/include/asm/mach-imx/sys_proto.h +++ b/arch/arm/include/asm/mach-imx/sys_proto.h @@ -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 diff --git a/arch/arm/mach-imx/imx8/Kconfig b/arch/arm/mach-imx/imx8/Kconfig index f76a139684..44c2ffd2ee 100644 --- a/arch/arm/mach-imx/imx8/Kconfig +++ b/arch/arm/mach-imx/imx8/Kconfig @@ -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 diff --git a/arch/arm/mach-imx/imx8/Makefile b/arch/arm/mach-imx/imx8/Makefile index 31ad169ccf..8ac9e8480b 100644 --- a/arch/arm/mach-imx/imx8/Makefile +++ b/arch/arm/mach-imx/imx8/Makefile @@ -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 index 0000000000..a91f0e62e6 --- /dev/null +++ b/arch/arm/mach-imx/imx8/fuse.c @@ -0,0 +1,108 @@ +/* + * Copyright 2017-2019 NXP + * + * SPDX-License-Identifier: GPL-2.0+ + * + */ + +#include +#include +#include +#include +#include +#include +#include + +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? \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; +} diff --git a/arch/arm/mach-imx/sip.c b/arch/arm/mach-imx/sip.c index 8654be9b63..3fec3eca83 100644 --- a/arch/arm/mach-imx/sip.c +++ b/arch/arm/mach-imx/sip.c @@ -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(®s); + + *reg1 = regs.regs[1]; + return regs.regs[0]; +} -- 2.17.1