]> git.dujemihanovic.xyz Git - u-boot.git/commitdiff
arm: mvebu: a37xx: Add support for reading NB and SB fuse OTP value
authorPali Rohár <pali@kernel.org>
Wed, 23 Feb 2022 13:15:45 +0000 (14:15 +0100)
committerStefan Roese <sr@denx.de>
Thu, 21 Apr 2022 10:31:36 +0000 (12:31 +0200)
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>
arch/arm/mach-mvebu/Kconfig
arch/arm/mach-mvebu/Makefile
arch/arm/mach-mvebu/armada3700/Makefile
arch/arm/mach-mvebu/armada3700/efuse.c [new file with mode: 0644]

index 21d9db2638d681e7337184573a44bb4410babcb5..ccdb624ba7d171e0f2616d36bd64e1a016262c35 100644 (file)
@@ -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
index a5a20877dda6f742c71b305df4037610afa4c6ce..1b451889d242440ca9e5f44d4fbc99045588ce35 100644 (file)
@@ -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
 
index 031b3e854e360e49095471207ef810dc2ec6dc74..cd74726cc778278d3c5eb278aa9e8cdeffd0b47f 100644 (file)
@@ -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 (file)
index 0000000..03778f1
--- /dev/null
@@ -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;
+}