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

Stefan Roese sr at denx.de
Fri Feb 18 15:15:31 CET 2022


On 2/17/22 19:50, Pali Rohár wrote:
> 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>

Reviewed-by: Stefan Roese <sr at denx.de>

Thanks,
Stefan

> ---
>   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;
> +}

Viele Grüße,
Stefan Roese

-- 
DENX Software Engineering GmbH,      Managing Director: Wolfgang Denk
HRB 165235 Munich, Office: Kirchenstr.5, D-82194 Groebenzell, Germany
Phone: (+49)-8142-66989-51 Fax: (+49)-8142-66989-80 Email: sr at denx.de


More information about the U-Boot mailing list