arm: mvebu: a37xx: Add support for reading NB and SB fuse OTP value
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@kernel.org>
Reviewed-by: Marek Behún <marek.behun@nic.cz>
Reviewed-by: Stefan Roese <sr@denx.de>
diff --git a/arch/arm/mach-mvebu/Kconfig b/arch/arm/mach-mvebu/Kconfig
index 21d9db2..ccdb624 100644
--- a/arch/arm/mach-mvebu/Kconfig
+++ b/arch/arm/mach-mvebu/Kconfig
@@ -44,6 +44,7 @@
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 a5a2087..1b45188 100644
--- a/arch/arm/mach-mvebu/Makefile
+++ b/arch/arm/mach-mvebu/Makefile
@@ -27,7 +27,10 @@
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 031b3e8..cd74726 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@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 0000000..03778f1
--- /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@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;
+}