[PATCH u-boot-mvebu v2 1/5] arm: mvebu: a37xx: Add support for reading NB and SB fuse OTP value

Pali Rohár pali at kernel.org
Thu Feb 17 19:50:42 CET 2022


Implement reading NB and SB fuses of Armada 37xx SOC via U-Boot fuse API.

Banks 0-43 are reserved for accessing Security OTP (not implemented yet).
Bank 44 is used for accessing North Bridge OTP (69 bits via words 0-2).
Bank 45 is used for accessing South Bridge OTP (97 bits via words 0-3).

Write support is not implemented yet because it looks like that both North
and South Bridge OTPs are already burned in factory with some data. The
meaning of some bits of North Bridge is documented in WTMI source code.
The meaning of bits in South Bridge is unknown.

Signed-off-by: Pali Rohár <pali at kernel.org>
Reviewed-by: Marek Behún <marek.behun at nic.cz>
---
 arch/arm/mach-mvebu/Kconfig             |   1 +
 arch/arm/mach-mvebu/Makefile            |   3 +
 arch/arm/mach-mvebu/armada3700/Makefile |   1 +
 arch/arm/mach-mvebu/armada3700/efuse.c  | 136 ++++++++++++++++++++++++
 4 files changed, 141 insertions(+)
 create mode 100644 arch/arm/mach-mvebu/armada3700/efuse.c

diff --git a/arch/arm/mach-mvebu/Kconfig b/arch/arm/mach-mvebu/Kconfig
index d23cc0c760f1..659c4fe2380a 100644
--- a/arch/arm/mach-mvebu/Kconfig
+++ b/arch/arm/mach-mvebu/Kconfig
@@ -44,6 +44,7 @@ config ARMADA_XP
 config ARMADA_3700
 	bool
 	select ARM64
+	select HAVE_MVEBU_EFUSE
 
 # Armada 7K and 8K are very similar - use only one Kconfig symbol for both
 config ARMADA_8K
diff --git a/arch/arm/mach-mvebu/Makefile b/arch/arm/mach-mvebu/Makefile
index a5a20877dda6..1b451889d242 100644
--- a/arch/arm/mach-mvebu/Makefile
+++ b/arch/arm/mach-mvebu/Makefile
@@ -27,7 +27,10 @@ obj-$(CONFIG_ARMADA_375) += ../../../drivers/ddr/marvell/axp/xor.o
 obj-$(CONFIG_ARMADA_38X) += ../../../drivers/ddr/marvell/a38x/xor.o
 obj-$(CONFIG_ARMADA_XP) += ../../../drivers/ddr/marvell/axp/xor.o
 obj-$(CONFIG_ARMADA_MSYS) += ../../../drivers/ddr/marvell/axp/xor.o
+
+ifdef CONFIG_ARMADA_38X
 obj-$(CONFIG_MVEBU_EFUSE) += efuse.o
+endif
 
 extra-y += kwbimage.cfg
 
diff --git a/arch/arm/mach-mvebu/armada3700/Makefile b/arch/arm/mach-mvebu/armada3700/Makefile
index 031b3e854e36..cd74726cc778 100644
--- a/arch/arm/mach-mvebu/armada3700/Makefile
+++ b/arch/arm/mach-mvebu/armada3700/Makefile
@@ -3,3 +3,4 @@
 # Copyright (C) 2016 Stefan Roese <sr at denx.de>
 
 obj-y = cpu.o
+obj-$(CONFIG_MVEBU_EFUSE) += efuse.o
diff --git a/arch/arm/mach-mvebu/armada3700/efuse.c b/arch/arm/mach-mvebu/armada3700/efuse.c
new file mode 100644
index 000000000000..03778f17ea49
--- /dev/null
+++ b/arch/arm/mach-mvebu/armada3700/efuse.c
@@ -0,0 +1,136 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * (C) 2017 Marvell International Ltd.
+ * (C) 2021 Pali Rohár <pali at kernel.org>
+ */
+
+#include <config.h>
+#include <common.h>
+#include <asm/io.h>
+#include <linux/delay.h>
+#include <mach/soc.h>
+
+#define OTP_NB_REG_BASE		((void __iomem *)MVEBU_REGISTER(0x12600))
+#define OTP_SB_REG_BASE		((void __iomem *)MVEBU_REGISTER(0x1A200))
+
+#define OTP_CONTROL_OFF		0x00
+#define   OTP_MODE_BIT		BIT(15)
+#define   OTP_RPTR_RST_BIT	BIT(14)
+#define   OTP_POR_B_BIT		BIT(13)
+#define   OTP_PRDT_BIT		BIT(3)
+#define OTP_READ_PORT_OFF	0x04
+#define OTP_READ_POINTER_OFF	0x08
+#define   OTP_PTR_INC_BIT	BIT(8)
+
+static void otp_read_parallel(void __iomem *base, u32 *data, u32 count)
+{
+	u32 regval;
+
+	/* 1. Clear OTP_MODE_NB to parallel mode */
+	regval = readl(base + OTP_CONTROL_OFF);
+	regval &= ~OTP_MODE_BIT;
+	writel(regval, base + OTP_CONTROL_OFF);
+
+	/* 2. Set OTP_POR_B_NB enter normal operation */
+	regval = readl(base + OTP_CONTROL_OFF);
+	regval |= OTP_POR_B_BIT;
+	writel(regval, base + OTP_CONTROL_OFF);
+
+	/* 3. Set OTP_PTR_INC_NB to auto-increment pointer after each read */
+	regval = readl(base + OTP_READ_POINTER_OFF);
+	regval |= OTP_PTR_INC_BIT;
+	writel(regval, base + OTP_READ_POINTER_OFF);
+
+	/* 4. Set OTP_RPTR_RST_NB, then clear the same field */
+	regval = readl(base + OTP_CONTROL_OFF);
+	regval |= OTP_RPTR_RST_BIT;
+	writel(regval, base + OTP_CONTROL_OFF);
+
+	regval = readl(base + OTP_CONTROL_OFF);
+	regval &= ~OTP_RPTR_RST_BIT;
+	writel(regval, base + OTP_CONTROL_OFF);
+
+	/* 5. Toggle OTP_PRDT_NB
+	 * a. Set OTP_PRDT_NB to 1.
+	 * b. Clear OTP_PRDT_NB to 0.
+	 * c. Wait for a minimum of 100 ns.
+	 * d. Set OTP_PRDT_NB to 1
+	 */
+	regval = readl(base + OTP_CONTROL_OFF);
+	regval |= OTP_PRDT_BIT;
+	writel(regval, base + OTP_CONTROL_OFF);
+
+	regval = readl(base + OTP_CONTROL_OFF);
+	regval &= ~OTP_PRDT_BIT;
+	writel(regval, base + OTP_CONTROL_OFF);
+
+	ndelay(100);
+
+	regval = readl(base + OTP_CONTROL_OFF);
+	regval |= OTP_PRDT_BIT;
+	writel(regval, base + OTP_CONTROL_OFF);
+
+	while (count-- > 0) {
+		/* 6. Read the content of OTP 32-bits at a time */
+		ndelay(100000);
+		*(data++) = readl(base + OTP_READ_PORT_OFF);
+	}
+}
+
+/*
+ * Banks 0-43 are used for accessing Security OTP (44 rows with 67 bits via 44 banks and words 0-2)
+ * Bank 44 is used for accessing North Bridge OTP (69 bits via words 0-2)
+ * Bank 45 is used for accessing South Bridge OTP (97 bits via words 0-3)
+ */
+
+#define RWTM_ROWS	44
+#define RWTM_MAX_BANK	(RWTM_ROWS - 1)
+#define RWTM_ROW_WORDS	3
+#define OTP_NB_BANK	RWTM_ROWS
+#define OTP_NB_WORDS	3
+#define OTP_SB_BANK	(RWTM_ROWS + 1)
+#define OTP_SB_WORDS	4
+
+int fuse_read(u32 bank, u32 word, u32 *val)
+{
+	if (bank <= RWTM_MAX_BANK) {
+		if (word >= RWTM_ROW_WORDS)
+			return -EINVAL;
+		/* TODO: not implemented yet */
+		return -ENOSYS;
+	} else if (bank == OTP_NB_BANK) {
+		u32 data[OTP_NB_WORDS];
+		if (word >= OTP_NB_WORDS)
+			return -EINVAL;
+		otp_read_parallel(OTP_NB_REG_BASE, data, OTP_NB_WORDS);
+		*val = data[word];
+		return 0;
+	} else if (bank == OTP_SB_BANK) {
+		u32 data[OTP_SB_WORDS];
+		if (word >= OTP_SB_WORDS)
+			return -EINVAL;
+		otp_read_parallel(OTP_SB_REG_BASE, data, OTP_SB_WORDS);
+		*val = data[word];
+		return 0;
+	} else {
+		return -EINVAL;
+	}
+}
+
+int fuse_prog(u32 bank, u32 word, u32 val)
+{
+	/* TODO: not implemented yet */
+	return -ENOSYS;
+}
+
+int fuse_sense(u32 bank, u32 word, u32 *val)
+{
+	/* not supported */
+	return -ENOSYS;
+}
+
+int fuse_override(u32 bank, u32 word, u32 val)
+{
+	/* not supported */
+	return -ENOSYS;
+}
-- 
2.20.1



More information about the U-Boot mailing list