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 <p...@kernel.org> Reviewed-by: Marek Behún <marek.be...@nic.cz> Reviewed-by: Stefan Roese <s...@denx.de> --- 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 7d487f270b51..81ba1d9f6423 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 <s...@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 <p...@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