diff mbox series

[U-Boot,v7,7/9] riscv: sifive: fu540: Setup ethaddr env variable using OTP

Message ID 20190624040212.3726-8-anup.patel@wdc.com
State Superseded
Delegated to: Andes
Headers show
Series Update SiFive Unleashed Drivers | expand

Commit Message

Anup Patel June 24, 2019, 4:03 a.m. UTC
This patch extends SiFive FU540 board support to setup ethaddr
env variable based on board serialnum read from OTP.

Signed-off-by: Anup Patel <anup.patel@wdc.com>
---
 board/sifive/fu540/fu540.c     | 122 +++++++++++++++++++++++++++++++++
 configs/sifive_fu540_defconfig |   1 +
 2 files changed, 123 insertions(+)

Comments

Bin Meng June 24, 2019, 5:03 a.m. UTC | #1
On Mon, Jun 24, 2019 at 12:03 PM Anup Patel <Anup.Patel@wdc.com> wrote:
>
> This patch extends SiFive FU540 board support to setup ethaddr
> env variable based on board serialnum read from OTP.
>
> Signed-off-by: Anup Patel <anup.patel@wdc.com>
> ---
>  board/sifive/fu540/fu540.c     | 122 +++++++++++++++++++++++++++++++++
>  configs/sifive_fu540_defconfig |   1 +
>  2 files changed, 123 insertions(+)
>

Reviewed-by: Bin Meng <bmeng.cn@gmail.com>
Troy Benjegerdes July 10, 2019, 5:15 p.m. UTC | #2
> On Jun 23, 2019, at 11:03 PM, Anup Patel <Anup.Patel@wdc.com> wrote:
> 
> This patch extends SiFive FU540 board support to setup ethaddr
> env variable based on board serialnum read from OTP.
> 
> Signed-off-by: Anup Patel <anup.patel@wdc.com>
> ---
> board/sifive/fu540/fu540.c     | 122 +++++++++++++++++++++++++++++++++
> configs/sifive_fu540_defconfig |   1 +
> 2 files changed, 123 insertions(+)
> 
> diff --git a/board/sifive/fu540/fu540.c b/board/sifive/fu540/fu540.c
> index 5adc4a3d4a..11daf1a75a 100644
> --- a/board/sifive/fu540/fu540.c
> +++ b/board/sifive/fu540/fu540.c
> @@ -8,6 +8,128 @@
> 
> #include <common.h>
> #include <dm.h>
> +#include <linux/delay.h>
> +#include <linux/io.h>
> +
> +#ifdef CONFIG_MISC_INIT_R
> +
> +#define FU540_OTP_BASE_ADDR			0x10070000
> +
> +struct fu540_otp_regs {
> +	u32 pa;     /* Address input */
> +	u32 paio;   /* Program address input */
> +	u32 pas;    /* Program redundancy cell selection input */
> +	u32 pce;    /* OTP Macro enable input */
> +	u32 pclk;   /* Clock input */
> +	u32 pdin;   /* Write data input */
> +	u32 pdout;  /* Read data output */
> +	u32 pdstb;  /* Deep standby mode enable input (active low) */
> +	u32 pprog;  /* Program mode enable input */
> +	u32 ptc;    /* Test column enable input */
> +	u32 ptm;    /* Test mode enable input */
> +	u32 ptm_rep;/* Repair function test mode enable input */
> +	u32 ptr;    /* Test row enable input */
> +	u32 ptrim;  /* Repair function enable input */
> +	u32 pwe;    /* Write enable input (defines program cycle) */
> +} __packed;
> +
> +#define BYTES_PER_FUSE				4
> +#define NUM_FUSES				0x1000
> +
> +static int fu540_otp_read(int offset, void *buf, int size)
> +{
> +	struct fu540_otp_regs *regs = (void __iomem *)FU540_OTP_BASE_ADDR;
> +	unsigned int i;
> +	int fuseidx = offset / BYTES_PER_FUSE;
> +	int fusecount = size / BYTES_PER_FUSE;
> +	u32 fusebuf[fusecount];
> +
> +	/* check bounds */
> +	if (offset < 0 || size < 0)
> +		return -EINVAL;
> +	if (fuseidx >= NUM_FUSES)
> +		return -EINVAL;
> +	if ((fuseidx + fusecount) > NUM_FUSES)
> +		return -EINVAL;
> +
> +	/* init OTP */
> +	writel(0x01, &regs->pdstb); /* wake up from stand-by */
> +	writel(0x01, &regs->ptrim); /* enable repair function */
> +	writel(0x01, &regs->pce);   /* enable input */
> +
> +	/* read all requested fuses */
> +	for (i = 0; i < fusecount; i++, fuseidx++) {
> +		writel(fuseidx, &regs->pa);
> +
> +		/* cycle clock to read */
> +		writel(0x01, &regs->pclk);
> +		mdelay(1);
> +		writel(0x00, &regs->pclk);
> +		mdelay(1);
> +
> +		/* read the value */
> +		fusebuf[i] = readl(&regs->pdout);
> +	}
> +
> +	/* shut down */
> +	writel(0, &regs->pce);
> +	writel(0, &regs->ptrim);
> +	writel(0, &regs->pdstb);
> +
> +	/* copy out */
> +	memcpy(buf, fusebuf, size);
> +
> +	return 0;
> +}
> +
> +static u32 fu540_read_serialnum(void)
> +{
> +	int ret;
> +	u32 serial[2] = {0};
> +
> +	for (int i = 0xfe * 4; i > 0; i -= 8) {
> +		ret = fu540_otp_read(i, serial, sizeof(serial));
> +		if (ret) {
> +			printf("%s: error reading from OTP\n", __func__);
> +			break;
> +		}
> +		if (serial[0] == ~serial[1])
> +			return serial[0];
> +	}
> +
> +	return 0;
> +}

Please take a look at the DM-enabled SiFive OTP driver submitted by Joey Hewitt at
https://github.com/sifive/HiFive_U-Boot/commit/6d842765de142b61f847852da7a9ce0d081d770c

This Joey's version also sets the ‘serial#’ environment variable, while this patch only sets ‘ethaddr'

> +
> +static void fu540_setup_macaddr(u32 serialnum)
> +{
> +	/* Default MAC address */
> +	unsigned char mac[6] = { 0x70, 0xb3, 0xd5, 0x92, 0xf0, 0x00 };
> +
> +	/*
> +	 * We derive our board MAC address by ORing last three bytes
> +	 * of board serial number to above default MAC address.
> +	 *
> +	 * This logic of deriving board MAC address is taken from
> +	 * SiFive FSBL and is kept unchanged.
> +	 */
> +	mac[5] |= (serialnum >>  0) & 0xff;
> +	mac[4] |= (serialnum >>  8) & 0xff;
> +	mac[3] |= (serialnum >> 16) & 0xff;
> +
> +	/* Update environment variable */
> +	eth_env_set_enetaddr("ethaddr", mac);
> +}
> +
> +int misc_init_r(void)
> +{
> +	/* Set ethaddr environment variable if not set */
> +	if (!env_get("ethaddr"))
> +		fu540_setup_macaddr(fu540_read_serialnum());
> +
> +	return 0;
> +}
> +
> +#endif
> 
> int board_init(void)
> {
> diff --git a/configs/sifive_fu540_defconfig b/configs/sifive_fu540_defconfig
> index f78412398e..f19203745e 100644
> --- a/configs/sifive_fu540_defconfig
> +++ b/configs/sifive_fu540_defconfig
> @@ -7,4 +7,5 @@ CONFIG_DISTRO_DEFAULTS=y
> CONFIG_FIT=y
> CONFIG_DISPLAY_CPUINFO=y
> CONFIG_DISPLAY_BOARDINFO=y
> +CONFIG_MISC_INIT_R=y
> CONFIG_OF_PRIOR_STAGE=y
> -- 
> 2.17.1
>
Alistair Francis July 10, 2019, 11:47 p.m. UTC | #3
On Wed, 2019-07-10 at 12:15 -0500, Troy Benjegerdes wrote:
> > On Jun 23, 2019, at 11:03 PM, Anup Patel <Anup.Patel@wdc.com>
> > wrote:
> > 
> > This patch extends SiFive FU540 board support to setup ethaddr
> > env variable based on board serialnum read from OTP.
> > 
> > Signed-off-by: Anup Patel <anup.patel@wdc.com>
> > ---
> > board/sifive/fu540/fu540.c     | 122
> > +++++++++++++++++++++++++++++++++
> > configs/sifive_fu540_defconfig |   1 +
> > 2 files changed, 123 insertions(+)
> > 
> > diff --git a/board/sifive/fu540/fu540.c
> > b/board/sifive/fu540/fu540.c
> > index 5adc4a3d4a..11daf1a75a 100644
> > --- a/board/sifive/fu540/fu540.c
> > +++ b/board/sifive/fu540/fu540.c
> > @@ -8,6 +8,128 @@
> > 
> > #include <common.h>
> > #include <dm.h>
> > +#include <linux/delay.h>
> > +#include <linux/io.h>
> > +
> > +#ifdef CONFIG_MISC_INIT_R
> > +
> > +#define FU540_OTP_BASE_ADDR			0x10070000
> > +
> > +struct fu540_otp_regs {
> > +	u32 pa;     /* Address input */
> > +	u32 paio;   /* Program address input */
> > +	u32 pas;    /* Program redundancy cell selection input */
> > +	u32 pce;    /* OTP Macro enable input */
> > +	u32 pclk;   /* Clock input */
> > +	u32 pdin;   /* Write data input */
> > +	u32 pdout;  /* Read data output */
> > +	u32 pdstb;  /* Deep standby mode enable input (active low) */
> > +	u32 pprog;  /* Program mode enable input */
> > +	u32 ptc;    /* Test column enable input */
> > +	u32 ptm;    /* Test mode enable input */
> > +	u32 ptm_rep;/* Repair function test mode enable input */
> > +	u32 ptr;    /* Test row enable input */
> > +	u32 ptrim;  /* Repair function enable input */
> > +	u32 pwe;    /* Write enable input (defines program cycle) */
> > +} __packed;
> > +
> > +#define BYTES_PER_FUSE				4
> > +#define NUM_FUSES				0x1000
> > +
> > +static int fu540_otp_read(int offset, void *buf, int size)
> > +{
> > +	struct fu540_otp_regs *regs = (void __iomem
> > *)FU540_OTP_BASE_ADDR;
> > +	unsigned int i;
> > +	int fuseidx = offset / BYTES_PER_FUSE;
> > +	int fusecount = size / BYTES_PER_FUSE;
> > +	u32 fusebuf[fusecount];
> > +
> > +	/* check bounds */
> > +	if (offset < 0 || size < 0)
> > +		return -EINVAL;
> > +	if (fuseidx >= NUM_FUSES)
> > +		return -EINVAL;
> > +	if ((fuseidx + fusecount) > NUM_FUSES)
> > +		return -EINVAL;
> > +
> > +	/* init OTP */
> > +	writel(0x01, &regs->pdstb); /* wake up from stand-by */
> > +	writel(0x01, &regs->ptrim); /* enable repair function */
> > +	writel(0x01, &regs->pce);   /* enable input */
> > +
> > +	/* read all requested fuses */
> > +	for (i = 0; i < fusecount; i++, fuseidx++) {
> > +		writel(fuseidx, &regs->pa);
> > +
> > +		/* cycle clock to read */
> > +		writel(0x01, &regs->pclk);
> > +		mdelay(1);
> > +		writel(0x00, &regs->pclk);
> > +		mdelay(1);
> > +
> > +		/* read the value */
> > +		fusebuf[i] = readl(&regs->pdout);
> > +	}
> > +
> > +	/* shut down */
> > +	writel(0, &regs->pce);
> > +	writel(0, &regs->ptrim);
> > +	writel(0, &regs->pdstb);
> > +
> > +	/* copy out */
> > +	memcpy(buf, fusebuf, size);
> > +
> > +	return 0;
> > +}
> > +
> > +static u32 fu540_read_serialnum(void)
> > +{
> > +	int ret;
> > +	u32 serial[2] = {0};
> > +
> > +	for (int i = 0xfe * 4; i > 0; i -= 8) {
> > +		ret = fu540_otp_read(i, serial, sizeof(serial));
> > +		if (ret) {
> > +			printf("%s: error reading from OTP\n",
> > __func__);
> > +			break;
> > +		}
> > +		if (serial[0] == ~serial[1])
> > +			return serial[0];
> > +	}
> > +
> > +	return 0;
> > +}
> 
> Please take a look at the DM-enabled SiFive OTP driver submitted by
> Joey Hewitt at
> https://github.com/sifive/HiFive_U-Boot/commit/6d842765de142b61f847852da7a9ce0d081d770c
> 
> This Joey's version also sets the ‘serial#’ environment variable,
> while this patch only sets ‘ethaddr'

Cool! Anup's patch seem to be reviewed and ready to merge, but
your/Joey's patch can just be rebased on top when it is sent upstream.

Alistair

> 
> > +
> > +static void fu540_setup_macaddr(u32 serialnum)
> > +{
> > +	/* Default MAC address */
> > +	unsigned char mac[6] = { 0x70, 0xb3, 0xd5, 0x92, 0xf0, 0x00 };
> > +
> > +	/*
> > +	 * We derive our board MAC address by ORing last three bytes
> > +	 * of board serial number to above default MAC address.
> > +	 *
> > +	 * This logic of deriving board MAC address is taken from
> > +	 * SiFive FSBL and is kept unchanged.
> > +	 */
> > +	mac[5] |= (serialnum >>  0) & 0xff;
> > +	mac[4] |= (serialnum >>  8) & 0xff;
> > +	mac[3] |= (serialnum >> 16) & 0xff;
> > +
> > +	/* Update environment variable */
> > +	eth_env_set_enetaddr("ethaddr", mac);
> > +}
> > +
> > +int misc_init_r(void)
> > +{
> > +	/* Set ethaddr environment variable if not set */
> > +	if (!env_get("ethaddr"))
> > +		fu540_setup_macaddr(fu540_read_serialnum());
> > +
> > +	return 0;
> > +}
> > +
> > +#endif
> > 
> > int board_init(void)
> > {
> > diff --git a/configs/sifive_fu540_defconfig
> > b/configs/sifive_fu540_defconfig
> > index f78412398e..f19203745e 100644
> > --- a/configs/sifive_fu540_defconfig
> > +++ b/configs/sifive_fu540_defconfig
> > @@ -7,4 +7,5 @@ CONFIG_DISTRO_DEFAULTS=y
> > CONFIG_FIT=y
> > CONFIG_DISPLAY_CPUINFO=y
> > CONFIG_DISPLAY_BOARDINFO=y
> > +CONFIG_MISC_INIT_R=y
> > CONFIG_OF_PRIOR_STAGE=y
> > -- 
> > 2.17.1
> >
Anup Patel July 11, 2019, 4:28 a.m. UTC | #4
> -----Original Message-----
> From: Troy Benjegerdes <troy.benjegerdes@sifive.com>
> Sent: Wednesday, July 10, 2019 10:45 PM
> To: Anup Patel <Anup.Patel@wdc.com>; Sagar Karandikar
> <sagar.karandikar@sifive.com>; Joey Hewitt <joey@joeyhewitt.com>
> Cc: Rick Chen <rick@andestech.com>; Bin Meng <bmeng.cn@gmail.com>;
> Lukas Auer <lukas.auer@aisec.fraunhofer.de>; Simon Glass
> <sjg@chromium.org>; Ramon Fried <rfried.dev@gmail.com>; Joe
> Hershberger <joe.hershberger@ni.com>; Palmer Dabbelt
> <palmer@sifive.com>; Paul Walmsley <paul.walmsley@sifive.com>; Atish
> Patra <Atish.Patra@wdc.com>; Alistair Francis <Alistair.Francis@wdc.com>;
> U-Boot Mailing List <u-boot@lists.denx.de>
> Subject: Re: [PATCH v7 7/9] riscv: sifive: fu540: Setup ethaddr env variable
> using OTP
> 
> 
> 
> > On Jun 23, 2019, at 11:03 PM, Anup Patel <Anup.Patel@wdc.com> wrote:
> >
> > This patch extends SiFive FU540 board support to setup ethaddr env
> > variable based on board serialnum read from OTP.
> >
> > Signed-off-by: Anup Patel <anup.patel@wdc.com>
> > ---
> > board/sifive/fu540/fu540.c     | 122
> +++++++++++++++++++++++++++++++++
> > configs/sifive_fu540_defconfig |   1 +
> > 2 files changed, 123 insertions(+)
> >
> > diff --git a/board/sifive/fu540/fu540.c b/board/sifive/fu540/fu540.c
> > index 5adc4a3d4a..11daf1a75a 100644
> > --- a/board/sifive/fu540/fu540.c
> > +++ b/board/sifive/fu540/fu540.c
> > @@ -8,6 +8,128 @@
> >
> > #include <common.h>
> > #include <dm.h>
> > +#include <linux/delay.h>
> > +#include <linux/io.h>
> > +
> > +#ifdef CONFIG_MISC_INIT_R
> > +
> > +#define FU540_OTP_BASE_ADDR			0x10070000
> > +
> > +struct fu540_otp_regs {
> > +	u32 pa;     /* Address input */
> > +	u32 paio;   /* Program address input */
> > +	u32 pas;    /* Program redundancy cell selection input */
> > +	u32 pce;    /* OTP Macro enable input */
> > +	u32 pclk;   /* Clock input */
> > +	u32 pdin;   /* Write data input */
> > +	u32 pdout;  /* Read data output */
> > +	u32 pdstb;  /* Deep standby mode enable input (active low) */
> > +	u32 pprog;  /* Program mode enable input */
> > +	u32 ptc;    /* Test column enable input */
> > +	u32 ptm;    /* Test mode enable input */
> > +	u32 ptm_rep;/* Repair function test mode enable input */
> > +	u32 ptr;    /* Test row enable input */
> > +	u32 ptrim;  /* Repair function enable input */
> > +	u32 pwe;    /* Write enable input (defines program cycle) */
> > +} __packed;
> > +
> > +#define BYTES_PER_FUSE				4
> > +#define NUM_FUSES				0x1000
> > +
> > +static int fu540_otp_read(int offset, void *buf, int size) {
> > +	struct fu540_otp_regs *regs = (void __iomem
> *)FU540_OTP_BASE_ADDR;
> > +	unsigned int i;
> > +	int fuseidx = offset / BYTES_PER_FUSE;
> > +	int fusecount = size / BYTES_PER_FUSE;
> > +	u32 fusebuf[fusecount];
> > +
> > +	/* check bounds */
> > +	if (offset < 0 || size < 0)
> > +		return -EINVAL;
> > +	if (fuseidx >= NUM_FUSES)
> > +		return -EINVAL;
> > +	if ((fuseidx + fusecount) > NUM_FUSES)
> > +		return -EINVAL;
> > +
> > +	/* init OTP */
> > +	writel(0x01, &regs->pdstb); /* wake up from stand-by */
> > +	writel(0x01, &regs->ptrim); /* enable repair function */
> > +	writel(0x01, &regs->pce);   /* enable input */
> > +
> > +	/* read all requested fuses */
> > +	for (i = 0; i < fusecount; i++, fuseidx++) {
> > +		writel(fuseidx, &regs->pa);
> > +
> > +		/* cycle clock to read */
> > +		writel(0x01, &regs->pclk);
> > +		mdelay(1);
> > +		writel(0x00, &regs->pclk);
> > +		mdelay(1);
> > +
> > +		/* read the value */
> > +		fusebuf[i] = readl(&regs->pdout);
> > +	}
> > +
> > +	/* shut down */
> > +	writel(0, &regs->pce);
> > +	writel(0, &regs->ptrim);
> > +	writel(0, &regs->pdstb);
> > +
> > +	/* copy out */
> > +	memcpy(buf, fusebuf, size);
> > +
> > +	return 0;
> > +}
> > +
> > +static u32 fu540_read_serialnum(void) {
> > +	int ret;
> > +	u32 serial[2] = {0};
> > +
> > +	for (int i = 0xfe * 4; i > 0; i -= 8) {
> > +		ret = fu540_otp_read(i, serial, sizeof(serial));
> > +		if (ret) {
> > +			printf("%s: error reading from OTP\n", __func__);
> > +			break;
> > +		}
> > +		if (serial[0] == ~serial[1])
> > +			return serial[0];
> > +	}
> > +
> > +	return 0;
> > +}
> 
> Please take a look at the DM-enabled SiFive OTP driver submitted by Joey
> Hewitt at https://github.com/sifive/HiFive_U-
> Boot/commit/6d842765de142b61f847852da7a9ce0d081d770c
> 
> This Joey's version also sets the ‘serial#’ environment variable, while this
> patch only sets ‘ethaddr'

I am not sure if "serial#" environment variable is a standard U-Boot
way of advertising serial number of underlying Host.

Where is this used?

Regards,
Anup
Ramon Fried July 11, 2019, 10:19 a.m. UTC | #5
On July 11, 2019 7:28:59 AM GMT+03:00, Anup Patel <Anup.Patel@wdc.com> wrote:
>
>
>> -----Original Message-----
>> From: Troy Benjegerdes <troy.benjegerdes@sifive.com>
>> Sent: Wednesday, July 10, 2019 10:45 PM
>> To: Anup Patel <Anup.Patel@wdc.com>; Sagar Karandikar
>> <sagar.karandikar@sifive.com>; Joey Hewitt <joey@joeyhewitt.com>
>> Cc: Rick Chen <rick@andestech.com>; Bin Meng <bmeng.cn@gmail.com>;
>> Lukas Auer <lukas.auer@aisec.fraunhofer.de>; Simon Glass
>> <sjg@chromium.org>; Ramon Fried <rfried.dev@gmail.com>; Joe
>> Hershberger <joe.hershberger@ni.com>; Palmer Dabbelt
>> <palmer@sifive.com>; Paul Walmsley <paul.walmsley@sifive.com>; Atish
>> Patra <Atish.Patra@wdc.com>; Alistair Francis
><Alistair.Francis@wdc.com>;
>> U-Boot Mailing List <u-boot@lists.denx.de>
>> Subject: Re: [PATCH v7 7/9] riscv: sifive: fu540: Setup ethaddr env
>variable
>> using OTP
>> 
>> 
>> 
>> > On Jun 23, 2019, at 11:03 PM, Anup Patel <Anup.Patel@wdc.com>
>wrote:
>> >
>> > This patch extends SiFive FU540 board support to setup ethaddr env
>> > variable based on board serialnum read from OTP.
>> >
>> > Signed-off-by: Anup Patel <anup.patel@wdc.com>
>> > ---
>> > board/sifive/fu540/fu540.c     | 122
>> +++++++++++++++++++++++++++++++++
>> > configs/sifive_fu540_defconfig |   1 +
>> > 2 files changed, 123 insertions(+)
>> >
>> > diff --git a/board/sifive/fu540/fu540.c
>b/board/sifive/fu540/fu540.c
>> > index 5adc4a3d4a..11daf1a75a 100644
>> > --- a/board/sifive/fu540/fu540.c
>> > +++ b/board/sifive/fu540/fu540.c
>> > @@ -8,6 +8,128 @@
>> >
>> > #include <common.h>
>> > #include <dm.h>
>> > +#include <linux/delay.h>
>> > +#include <linux/io.h>
>> > +
>> > +#ifdef CONFIG_MISC_INIT_R
>> > +
>> > +#define FU540_OTP_BASE_ADDR			0x10070000
>> > +
>> > +struct fu540_otp_regs {
>> > +	u32 pa;     /* Address input */
>> > +	u32 paio;   /* Program address input */
>> > +	u32 pas;    /* Program redundancy cell selection input */
>> > +	u32 pce;    /* OTP Macro enable input */
>> > +	u32 pclk;   /* Clock input */
>> > +	u32 pdin;   /* Write data input */
>> > +	u32 pdout;  /* Read data output */
>> > +	u32 pdstb;  /* Deep standby mode enable input (active low) */
>> > +	u32 pprog;  /* Program mode enable input */
>> > +	u32 ptc;    /* Test column enable input */
>> > +	u32 ptm;    /* Test mode enable input */
>> > +	u32 ptm_rep;/* Repair function test mode enable input */
>> > +	u32 ptr;    /* Test row enable input */
>> > +	u32 ptrim;  /* Repair function enable input */
>> > +	u32 pwe;    /* Write enable input (defines program cycle) */
>> > +} __packed;
>> > +
>> > +#define BYTES_PER_FUSE				4
>> > +#define NUM_FUSES				0x1000
>> > +
>> > +static int fu540_otp_read(int offset, void *buf, int size) {
>> > +	struct fu540_otp_regs *regs = (void __iomem
>> *)FU540_OTP_BASE_ADDR;
>> > +	unsigned int i;
>> > +	int fuseidx = offset / BYTES_PER_FUSE;
>> > +	int fusecount = size / BYTES_PER_FUSE;
>> > +	u32 fusebuf[fusecount];
>> > +
>> > +	/* check bounds */
>> > +	if (offset < 0 || size < 0)
>> > +		return -EINVAL;
>> > +	if (fuseidx >= NUM_FUSES)
>> > +		return -EINVAL;
>> > +	if ((fuseidx + fusecount) > NUM_FUSES)
>> > +		return -EINVAL;
>> > +
>> > +	/* init OTP */
>> > +	writel(0x01, &regs->pdstb); /* wake up from stand-by */
>> > +	writel(0x01, &regs->ptrim); /* enable repair function */
>> > +	writel(0x01, &regs->pce);   /* enable input */
>> > +
>> > +	/* read all requested fuses */
>> > +	for (i = 0; i < fusecount; i++, fuseidx++) {
>> > +		writel(fuseidx, &regs->pa);
>> > +
>> > +		/* cycle clock to read */
>> > +		writel(0x01, &regs->pclk);
>> > +		mdelay(1);
>> > +		writel(0x00, &regs->pclk);
>> > +		mdelay(1);
>> > +
>> > +		/* read the value */
>> > +		fusebuf[i] = readl(&regs->pdout);
>> > +	}
>> > +
>> > +	/* shut down */
>> > +	writel(0, &regs->pce);
>> > +	writel(0, &regs->ptrim);
>> > +	writel(0, &regs->pdstb);
>> > +
>> > +	/* copy out */
>> > +	memcpy(buf, fusebuf, size);
>> > +
>> > +	return 0;
>> > +}
>> > +
>> > +static u32 fu540_read_serialnum(void) {
>> > +	int ret;
>> > +	u32 serial[2] = {0};
>> > +
>> > +	for (int i = 0xfe * 4; i > 0; i -= 8) {
>> > +		ret = fu540_otp_read(i, serial, sizeof(serial));
>> > +		if (ret) {
>> > +			printf("%s: error reading from OTP\n", __func__);
>> > +			break;
>> > +		}
>> > +		if (serial[0] == ~serial[1])
>> > +			return serial[0];
>> > +	}
>> > +
>> > +	return 0;
>> > +}
>> 
>> Please take a look at the DM-enabled SiFive OTP driver submitted by
>Joey
>> Hewitt at https://github.com/sifive/HiFive_U-
>> Boot/commit/6d842765de142b61f847852da7a9ce0d081d770c
>> 
>> This Joey's version also sets the ‘serial#’ environment variable,
>while this
>> patch only sets ‘ethaddr'
>
>I am not sure if "serial#" environment variable is a standard U-Boot
>way of advertising serial number of underlying Host.
it's standard. 
>
>Where is this used?
looks at fastboot for instance. it uses this env for the USB serial. 

Thanks.,
Ramon 
>
>Regards,
>Anup
Anup Patel July 12, 2019, 8:39 a.m. UTC | #6
On Thu, Jul 11, 2019 at 3:49 PM Ramon Fried <rfried.dev@gmail.com> wrote:
>
>
>
> On July 11, 2019 7:28:59 AM GMT+03:00, Anup Patel <Anup.Patel@wdc.com> wrote:
> >
> >
> >> -----Original Message-----
> >> From: Troy Benjegerdes <troy.benjegerdes@sifive.com>
> >> Sent: Wednesday, July 10, 2019 10:45 PM
> >> To: Anup Patel <Anup.Patel@wdc.com>; Sagar Karandikar
> >> <sagar.karandikar@sifive.com>; Joey Hewitt <joey@joeyhewitt.com>
> >> Cc: Rick Chen <rick@andestech.com>; Bin Meng <bmeng.cn@gmail.com>;
> >> Lukas Auer <lukas.auer@aisec.fraunhofer.de>; Simon Glass
> >> <sjg@chromium.org>; Ramon Fried <rfried.dev@gmail.com>; Joe
> >> Hershberger <joe.hershberger@ni.com>; Palmer Dabbelt
> >> <palmer@sifive.com>; Paul Walmsley <paul.walmsley@sifive.com>; Atish
> >> Patra <Atish.Patra@wdc.com>; Alistair Francis
> ><Alistair.Francis@wdc.com>;
> >> U-Boot Mailing List <u-boot@lists.denx.de>
> >> Subject: Re: [PATCH v7 7/9] riscv: sifive: fu540: Setup ethaddr env
> >variable
> >> using OTP
> >>
> >>
> >>
> >> > On Jun 23, 2019, at 11:03 PM, Anup Patel <Anup.Patel@wdc.com>
> >wrote:
> >> >
> >> > This patch extends SiFive FU540 board support to setup ethaddr env
> >> > variable based on board serialnum read from OTP.
> >> >
> >> > Signed-off-by: Anup Patel <anup.patel@wdc.com>
> >> > ---
> >> > board/sifive/fu540/fu540.c     | 122
> >> +++++++++++++++++++++++++++++++++
> >> > configs/sifive_fu540_defconfig |   1 +
> >> > 2 files changed, 123 insertions(+)
> >> >
> >> > diff --git a/board/sifive/fu540/fu540.c
> >b/board/sifive/fu540/fu540.c
> >> > index 5adc4a3d4a..11daf1a75a 100644
> >> > --- a/board/sifive/fu540/fu540.c
> >> > +++ b/board/sifive/fu540/fu540.c
> >> > @@ -8,6 +8,128 @@
> >> >
> >> > #include <common.h>
> >> > #include <dm.h>
> >> > +#include <linux/delay.h>
> >> > +#include <linux/io.h>
> >> > +
> >> > +#ifdef CONFIG_MISC_INIT_R
> >> > +
> >> > +#define FU540_OTP_BASE_ADDR                       0x10070000
> >> > +
> >> > +struct fu540_otp_regs {
> >> > +  u32 pa;     /* Address input */
> >> > +  u32 paio;   /* Program address input */
> >> > +  u32 pas;    /* Program redundancy cell selection input */
> >> > +  u32 pce;    /* OTP Macro enable input */
> >> > +  u32 pclk;   /* Clock input */
> >> > +  u32 pdin;   /* Write data input */
> >> > +  u32 pdout;  /* Read data output */
> >> > +  u32 pdstb;  /* Deep standby mode enable input (active low) */
> >> > +  u32 pprog;  /* Program mode enable input */
> >> > +  u32 ptc;    /* Test column enable input */
> >> > +  u32 ptm;    /* Test mode enable input */
> >> > +  u32 ptm_rep;/* Repair function test mode enable input */
> >> > +  u32 ptr;    /* Test row enable input */
> >> > +  u32 ptrim;  /* Repair function enable input */
> >> > +  u32 pwe;    /* Write enable input (defines program cycle) */
> >> > +} __packed;
> >> > +
> >> > +#define BYTES_PER_FUSE                            4
> >> > +#define NUM_FUSES                         0x1000
> >> > +
> >> > +static int fu540_otp_read(int offset, void *buf, int size) {
> >> > +  struct fu540_otp_regs *regs = (void __iomem
> >> *)FU540_OTP_BASE_ADDR;
> >> > +  unsigned int i;
> >> > +  int fuseidx = offset / BYTES_PER_FUSE;
> >> > +  int fusecount = size / BYTES_PER_FUSE;
> >> > +  u32 fusebuf[fusecount];
> >> > +
> >> > +  /* check bounds */
> >> > +  if (offset < 0 || size < 0)
> >> > +          return -EINVAL;
> >> > +  if (fuseidx >= NUM_FUSES)
> >> > +          return -EINVAL;
> >> > +  if ((fuseidx + fusecount) > NUM_FUSES)
> >> > +          return -EINVAL;
> >> > +
> >> > +  /* init OTP */
> >> > +  writel(0x01, &regs->pdstb); /* wake up from stand-by */
> >> > +  writel(0x01, &regs->ptrim); /* enable repair function */
> >> > +  writel(0x01, &regs->pce);   /* enable input */
> >> > +
> >> > +  /* read all requested fuses */
> >> > +  for (i = 0; i < fusecount; i++, fuseidx++) {
> >> > +          writel(fuseidx, &regs->pa);
> >> > +
> >> > +          /* cycle clock to read */
> >> > +          writel(0x01, &regs->pclk);
> >> > +          mdelay(1);
> >> > +          writel(0x00, &regs->pclk);
> >> > +          mdelay(1);
> >> > +
> >> > +          /* read the value */
> >> > +          fusebuf[i] = readl(&regs->pdout);
> >> > +  }
> >> > +
> >> > +  /* shut down */
> >> > +  writel(0, &regs->pce);
> >> > +  writel(0, &regs->ptrim);
> >> > +  writel(0, &regs->pdstb);
> >> > +
> >> > +  /* copy out */
> >> > +  memcpy(buf, fusebuf, size);
> >> > +
> >> > +  return 0;
> >> > +}
> >> > +
> >> > +static u32 fu540_read_serialnum(void) {
> >> > +  int ret;
> >> > +  u32 serial[2] = {0};
> >> > +
> >> > +  for (int i = 0xfe * 4; i > 0; i -= 8) {
> >> > +          ret = fu540_otp_read(i, serial, sizeof(serial));
> >> > +          if (ret) {
> >> > +                  printf("%s: error reading from OTP\n", __func__);
> >> > +                  break;
> >> > +          }
> >> > +          if (serial[0] == ~serial[1])
> >> > +                  return serial[0];
> >> > +  }
> >> > +
> >> > +  return 0;
> >> > +}
> >>
> >> Please take a look at the DM-enabled SiFive OTP driver submitted by
> >Joey
> >> Hewitt at https://github.com/sifive/HiFive_U-
> >> Boot/commit/6d842765de142b61f847852da7a9ce0d081d770c
> >>
> >> This Joey's version also sets the ‘serial#’ environment variable,
> >while this
> >> patch only sets ‘ethaddr'
> >
> >I am not sure if "serial#" environment variable is a standard U-Boot
> >way of advertising serial number of underlying Host.
> it's standard.
> >
> >Where is this used?
> looks at fastboot for instance. it uses this env for the USB serial.

Sure, this can be added as a separate patch. It's not related to what
this patch is trying to do.

Regards,
Anup
Ramon Fried July 15, 2019, 7:07 a.m. UTC | #7
On July 11, 2019 7:28:59 AM GMT+03:00, Anup Patel <Anup.Patel@wdc.com> wrote:
>
>
>> -----Original Message-----
>> From: Troy Benjegerdes <troy.benjegerdes@sifive.com>
>> Sent: Wednesday, July 10, 2019 10:45 PM
>> To: Anup Patel <Anup.Patel@wdc.com>; Sagar Karandikar
>> <sagar.karandikar@sifive.com>; Joey Hewitt <joey@joeyhewitt.com>
>> Cc: Rick Chen <rick@andestech.com>; Bin Meng <bmeng.cn@gmail.com>;
>> Lukas Auer <lukas.auer@aisec.fraunhofer.de>; Simon Glass
>> <sjg@chromium.org>; Ramon Fried <rfried.dev@gmail.com>; Joe
>> Hershberger <joe.hershberger@ni.com>; Palmer Dabbelt
>> <palmer@sifive.com>; Paul Walmsley <paul.walmsley@sifive.com>; Atish
>> Patra <Atish.Patra@wdc.com>; Alistair Francis
> <Alistair.Francis@wdc.com>;
>> U-Boot Mailing List <u-boot@lists.denx.de>
>> Subject: Re: [PATCH v7 7/9] riscv: sifive: fu540: Setup ethaddr env
> variable
>> using OTP
>>
>>
>>
>> > On Jun 23, 2019, at 11:03 PM, Anup Patel <Anup.Patel@wdc.com>
> wrote:
>> >
>> > This patch extends SiFive FU540 board support to setup ethaddr env
>> > variable based on board serialnum read from OTP.
>> >
>> > Signed-off-by: Anup Patel <anup.patel@wdc.com>
>> > ---
>> > board/sifive/fu540/fu540.c | 122
>> +++++++++++++++++++++++++++++++++
>> > configs/sifive_fu540_defconfig | 1 +
>> > 2 files changed, 123 insertions(+)
>> >
>> > diff --git a/board/sifive/fu540/fu540.c
> b/board/sifive/fu540/fu540.c
>> > index 5adc4a3d4a..11daf1a75a 100644
>> > --- a/board/sifive/fu540/fu540.c
>> > +++ b/board/sifive/fu540/fu540.c
>> > @@ -8,6 +8,128 @@
>> >
>> > #include <common.h>
>> > #include <dm.h>
>> > +#include <linux/delay.h>
>> > +#include <linux/io.h>
>> > +
>> > +#ifdef CONFIG_MISC_INIT_R
>> > +
>> > +#define FU540_OTP_BASE_ADDR 0x10070000
>> > +
>> > +struct fu540_otp_regs {
>> > + u32 pa; /* Address input */
>> > + u32 paio; /* Program address input */
>> > + u32 pas; /* Program redundancy cell selection input */
>> > + u32 pce; /* OTP Macro enable input */
>> > + u32 pclk; /* Clock input */
>> > + u32 pdin; /* Write data input */
>> > + u32 pdout; /* Read data output */
>> > + u32 pdstb; /* Deep standby mode enable input (active low) */
>> > + u32 pprog; /* Program mode enable input */
>> > + u32 ptc; /* Test column enable input */
>> > + u32 ptm; /* Test mode enable input */
>> > + u32 ptm_rep;/* Repair function test mode enable input */
>> > + u32 ptr; /* Test row enable input */
>> > + u32 ptrim; /* Repair function enable input */
>> > + u32 pwe; /* Write enable input (defines program cycle) */
>> > +} __packed;
>> > +
>> > +#define BYTES_PER_FUSE 4
>> > +#define NUM_FUSES 0x1000
>> > +
>> > +static int fu540_otp_read(int offset, void *buf, int size) {
>> > + struct fu540_otp_regs *regs = (void __iomem
>> *)FU540_OTP_BASE_ADDR;
>> > + unsigned int i;
>> > + int fuseidx = offset / BYTES_PER_FUSE;
>> > + int fusecount = size / BYTES_PER_FUSE;
>> > + u32 fusebuf[fusecount];
>> > +
>> > + /* check bounds */
>> > + if (offset < 0 || size < 0)
>> > + return -EINVAL;
>> > + if (fuseidx >= NUM_FUSES)
>> > + return -EINVAL;
>> > + if ((fuseidx + fusecount) > NUM_FUSES)
>> > + return -EINVAL;
>> > +
>> > + /* init OTP */
>> > + writel(0x01, &regs->pdstb); /* wake up from stand-by */
>> > + writel(0x01, &regs->ptrim); /* enable repair function */
>> > + writel(0x01, &regs->pce); /* enable input */
>> > +
>> > + /* read all requested fuses */
>> > + for (i = 0; i < fusecount; i++, fuseidx++) {
>> > + writel(fuseidx, &regs->pa);
>> > +
>> > + /* cycle clock to read */
>> > + writel(0x01, &regs->pclk);
>> > + mdelay(1);
>> > + writel(0x00, &regs->pclk);
>> > + mdelay(1);
>> > +
>> > + /* read the value */
>> > + fusebuf[i] = readl(&regs->pdout);
>> > + }
>> > +
>> > + /* shut down */
>> > + writel(0, &regs->pce);
>> > + writel(0, &regs->ptrim);
>> > + writel(0, &regs->pdstb);
>> > +
>> > + /* copy out */
>> > + memcpy(buf, fusebuf, size);
>> > +
>> > + return 0;
>> > +}
>> > +
>> > +static u32 fu540_read_serialnum(void) {
>> > + int ret;
>> > + u32 serial[2] = {0};
>> > +
>> > + for (int i = 0xfe * 4; i > 0; i -= 8) {
>> > + ret = fu540_otp_read(i, serial, sizeof(serial));
>> > + if (ret) {
>> > + printf("%s: error reading from OTP\n", __func__);
>> > + break;
>> > + }
>> > + if (serial[0] == ~serial[1])
>> > + return serial[0];
>> > + }
>> > +
>> > + return 0;
>> > +}
>>
>> Please take a look at the DM-enabled SiFive OTP driver submitted by
> Joey
>> Hewitt at https://github.com/sifive/HiFive_U-
>> Boot/commit/6d842765de142b61f847852da7a9ce0d081d770c
>>
>> This Joey's version also sets the ‘serial#’ environment variable,
> while this
>> patch only sets ‘ethaddr'
>
> I am not sure if "serial#" environment variable is a standard U-Boot
> way of advertising serial number of underlying Host.
it's standard.
>
> Where is this used?
looks at fastboot for instance. it uses this env for the USB serial.
Thanks.,
Ramon
>
> Regards,
> Anup
Joey Hewitt July 20, 2019, 12:42 a.m. UTC | #8
On 7/10/19 10:28 PM, Anup Patel wrote:
> 
> 
>> -----Original Message-----
>> From: Troy Benjegerdes <troy.benjegerdes@sifive.com>
>> Sent: Wednesday, July 10, 2019 10:45 PM
>> To: Anup Patel <Anup.Patel@wdc.com>; Sagar Karandikar
>> <sagar.karandikar@sifive.com>; Joey Hewitt <joey@joeyhewitt.com>
>> Cc: Rick Chen <rick@andestech.com>; Bin Meng <bmeng.cn@gmail.com>;
>> Lukas Auer <lukas.auer@aisec.fraunhofer.de>; Simon Glass
>> <sjg@chromium.org>; Ramon Fried <rfried.dev@gmail.com>; Joe
>> Hershberger <joe.hershberger@ni.com>; Palmer Dabbelt
>> <palmer@sifive.com>; Paul Walmsley <paul.walmsley@sifive.com>; Atish
>> Patra <Atish.Patra@wdc.com>; Alistair Francis <Alistair.Francis@wdc.com>;
>> U-Boot Mailing List <u-boot@lists.denx.de>
>> Subject: Re: [PATCH v7 7/9] riscv: sifive: fu540: Setup ethaddr env variable
>> using OTP
>>
>>
>>
>>> On Jun 23, 2019, at 11:03 PM, Anup Patel <Anup.Patel@wdc.com> wrote:
>>>
>>> This patch extends SiFive FU540 board support to setup ethaddr env
>>> variable based on board serialnum read from OTP.
>>>
>>> Signed-off-by: Anup Patel <anup.patel@wdc.com>
>>> ---
>>> board/sifive/fu540/fu540.c     | 122
>> +++++++++++++++++++++++++++++++++
>>> configs/sifive_fu540_defconfig |   1 +
>>> 2 files changed, 123 insertions(+)
>>>
>>> diff --git a/board/sifive/fu540/fu540.c b/board/sifive/fu540/fu540.c
>>> index 5adc4a3d4a..11daf1a75a 100644
>>> --- a/board/sifive/fu540/fu540.c
>>> +++ b/board/sifive/fu540/fu540.c
>>> @@ -8,6 +8,128 @@
>>>
>>> #include <common.h>
>>> #include <dm.h>
>>> +#include <linux/delay.h>
>>> +#include <linux/io.h>
>>> +
>>> +#ifdef CONFIG_MISC_INIT_R
>>> +
>>> +#define FU540_OTP_BASE_ADDR			0x10070000
>>> +
>>> +struct fu540_otp_regs {
>>> +	u32 pa;     /* Address input */
>>> +	u32 paio;   /* Program address input */
>>> +	u32 pas;    /* Program redundancy cell selection input */
>>> +	u32 pce;    /* OTP Macro enable input */
>>> +	u32 pclk;   /* Clock input */
>>> +	u32 pdin;   /* Write data input */
>>> +	u32 pdout;  /* Read data output */
>>> +	u32 pdstb;  /* Deep standby mode enable input (active low) */
>>> +	u32 pprog;  /* Program mode enable input */
>>> +	u32 ptc;    /* Test column enable input */
>>> +	u32 ptm;    /* Test mode enable input */
>>> +	u32 ptm_rep;/* Repair function test mode enable input */
>>> +	u32 ptr;    /* Test row enable input */
>>> +	u32 ptrim;  /* Repair function enable input */
>>> +	u32 pwe;    /* Write enable input (defines program cycle) */
>>> +} __packed;
>>> +
>>> +#define BYTES_PER_FUSE				4
>>> +#define NUM_FUSES				0x1000
>>> +
>>> +static int fu540_otp_read(int offset, void *buf, int size) {
>>> +	struct fu540_otp_regs *regs = (void __iomem
>> *)FU540_OTP_BASE_ADDR;
>>> +	unsigned int i;
>>> +	int fuseidx = offset / BYTES_PER_FUSE;
>>> +	int fusecount = size / BYTES_PER_FUSE;
>>> +	u32 fusebuf[fusecount];
>>> +
>>> +	/* check bounds */
>>> +	if (offset < 0 || size < 0)
>>> +		return -EINVAL;
>>> +	if (fuseidx >= NUM_FUSES)
>>> +		return -EINVAL;
>>> +	if ((fuseidx + fusecount) > NUM_FUSES)
>>> +		return -EINVAL;
>>> +
>>> +	/* init OTP */
>>> +	writel(0x01, &regs->pdstb); /* wake up from stand-by */
>>> +	writel(0x01, &regs->ptrim); /* enable repair function */
>>> +	writel(0x01, &regs->pce);   /* enable input */
>>> +
>>> +	/* read all requested fuses */
>>> +	for (i = 0; i < fusecount; i++, fuseidx++) {
>>> +		writel(fuseidx, &regs->pa);
>>> +
>>> +		/* cycle clock to read */
>>> +		writel(0x01, &regs->pclk);
>>> +		mdelay(1);
>>> +		writel(0x00, &regs->pclk);
>>> +		mdelay(1);
>>> +
>>> +		/* read the value */
>>> +		fusebuf[i] = readl(&regs->pdout);
>>> +	}
>>> +
>>> +	/* shut down */
>>> +	writel(0, &regs->pce);
>>> +	writel(0, &regs->ptrim);
>>> +	writel(0, &regs->pdstb);
>>> +
>>> +	/* copy out */
>>> +	memcpy(buf, fusebuf, size);
>>> +
>>> +	return 0;
>>> +}
>>> +
>>> +static u32 fu540_read_serialnum(void) {
>>> +	int ret;
>>> +	u32 serial[2] = {0};
>>> +
>>> +	for (int i = 0xfe * 4; i > 0; i -= 8) {
>>> +		ret = fu540_otp_read(i, serial, sizeof(serial));
>>> +		if (ret) {
>>> +			printf("%s: error reading from OTP\n", __func__);
>>> +			break;
>>> +		}
>>> +		if (serial[0] == ~serial[1])
>>> +			return serial[0];
>>> +	}
>>> +
>>> +	return 0;
>>> +}
>>
>> Please take a look at the DM-enabled SiFive OTP driver submitted by Joey
>> Hewitt at https://github.com/sifive/HiFive_U-
>> Boot/commit/6d842765de142b61f847852da7a9ce0d081d770c
>>
>> This Joey's version also sets the ‘serial#’ environment variable, while this
>> patch only sets ‘ethaddr'
> 
> I am not sure if "serial#" environment variable is a standard U-Boot
> way of advertising serial number of underlying Host.
> 
> Where is this used?
> 
> Regards,
> Anup
> 

The serial# var is inserted into the device tree.
https://github.com/u-boot/u-boot/blob/0de815356474912ef5bef9a69f0327a5a93bb2c2/common/fdt_support.c#L199

See also
https://github.com/torvalds/linux/commit/13dd92bb4599b5655cafe1f2c0365396a096b94a

I don't recall if it had any particularly special meaning on the kernels 
I tested with, but it seems to be standard and could be useful to userspace.

-Joey
Joe Hershberger July 23, 2019, 2:58 a.m. UTC | #9
On Mon, Jul 22, 2019 at 12:46 PM Joey Hewitt <joey.hewitt@gmail.com> wrote:
>
> On 7/10/19 10:28 PM, Anup Patel wrote:

[ ... ]

> >> Please take a look at the DM-enabled SiFive OTP driver submitted by Joey
> >> Hewitt at https://github.com/sifive/HiFive_U-
> >> Boot/commit/6d842765de142b61f847852da7a9ce0d081d770c
> >>
> >> This Joey's version also sets the ‘serial#’ environment variable, while this
> >> patch only sets ‘ethaddr'
> >
> > I am not sure if "serial#" environment variable is a standard U-Boot
> > way of advertising serial number of underlying Host.
> >
> > Where is this used?
> >
> > Regards,
> > Anup
> >
>
> The serial# var is inserted into the device tree.
> https://github.com/u-boot/u-boot/blob/0de815356474912ef5bef9a69f0327a5a93bb2c2/common/fdt_support.c#L199
>
> See also
> https://github.com/torvalds/linux/commit/13dd92bb4599b5655cafe1f2c0365396a096b94a
>
> I don't recall if it had any particularly special meaning on the kernels
> I tested with, but it seems to be standard and could be useful to userspace.

You can find the so-called "built in" variables here:
https://gitlab.denx.de/u-boot/u-boot/blob/master/include/env_flags.h

It is fairly common for a board.c to set the serial# variable.

-Joe
diff mbox series

Patch

diff --git a/board/sifive/fu540/fu540.c b/board/sifive/fu540/fu540.c
index 5adc4a3d4a..11daf1a75a 100644
--- a/board/sifive/fu540/fu540.c
+++ b/board/sifive/fu540/fu540.c
@@ -8,6 +8,128 @@ 
 
 #include <common.h>
 #include <dm.h>
+#include <linux/delay.h>
+#include <linux/io.h>
+
+#ifdef CONFIG_MISC_INIT_R
+
+#define FU540_OTP_BASE_ADDR			0x10070000
+
+struct fu540_otp_regs {
+	u32 pa;     /* Address input */
+	u32 paio;   /* Program address input */
+	u32 pas;    /* Program redundancy cell selection input */
+	u32 pce;    /* OTP Macro enable input */
+	u32 pclk;   /* Clock input */
+	u32 pdin;   /* Write data input */
+	u32 pdout;  /* Read data output */
+	u32 pdstb;  /* Deep standby mode enable input (active low) */
+	u32 pprog;  /* Program mode enable input */
+	u32 ptc;    /* Test column enable input */
+	u32 ptm;    /* Test mode enable input */
+	u32 ptm_rep;/* Repair function test mode enable input */
+	u32 ptr;    /* Test row enable input */
+	u32 ptrim;  /* Repair function enable input */
+	u32 pwe;    /* Write enable input (defines program cycle) */
+} __packed;
+
+#define BYTES_PER_FUSE				4
+#define NUM_FUSES				0x1000
+
+static int fu540_otp_read(int offset, void *buf, int size)
+{
+	struct fu540_otp_regs *regs = (void __iomem *)FU540_OTP_BASE_ADDR;
+	unsigned int i;
+	int fuseidx = offset / BYTES_PER_FUSE;
+	int fusecount = size / BYTES_PER_FUSE;
+	u32 fusebuf[fusecount];
+
+	/* check bounds */
+	if (offset < 0 || size < 0)
+		return -EINVAL;
+	if (fuseidx >= NUM_FUSES)
+		return -EINVAL;
+	if ((fuseidx + fusecount) > NUM_FUSES)
+		return -EINVAL;
+
+	/* init OTP */
+	writel(0x01, &regs->pdstb); /* wake up from stand-by */
+	writel(0x01, &regs->ptrim); /* enable repair function */
+	writel(0x01, &regs->pce);   /* enable input */
+
+	/* read all requested fuses */
+	for (i = 0; i < fusecount; i++, fuseidx++) {
+		writel(fuseidx, &regs->pa);
+
+		/* cycle clock to read */
+		writel(0x01, &regs->pclk);
+		mdelay(1);
+		writel(0x00, &regs->pclk);
+		mdelay(1);
+
+		/* read the value */
+		fusebuf[i] = readl(&regs->pdout);
+	}
+
+	/* shut down */
+	writel(0, &regs->pce);
+	writel(0, &regs->ptrim);
+	writel(0, &regs->pdstb);
+
+	/* copy out */
+	memcpy(buf, fusebuf, size);
+
+	return 0;
+}
+
+static u32 fu540_read_serialnum(void)
+{
+	int ret;
+	u32 serial[2] = {0};
+
+	for (int i = 0xfe * 4; i > 0; i -= 8) {
+		ret = fu540_otp_read(i, serial, sizeof(serial));
+		if (ret) {
+			printf("%s: error reading from OTP\n", __func__);
+			break;
+		}
+		if (serial[0] == ~serial[1])
+			return serial[0];
+	}
+
+	return 0;
+}
+
+static void fu540_setup_macaddr(u32 serialnum)
+{
+	/* Default MAC address */
+	unsigned char mac[6] = { 0x70, 0xb3, 0xd5, 0x92, 0xf0, 0x00 };
+
+	/*
+	 * We derive our board MAC address by ORing last three bytes
+	 * of board serial number to above default MAC address.
+	 *
+	 * This logic of deriving board MAC address is taken from
+	 * SiFive FSBL and is kept unchanged.
+	 */
+	mac[5] |= (serialnum >>  0) & 0xff;
+	mac[4] |= (serialnum >>  8) & 0xff;
+	mac[3] |= (serialnum >> 16) & 0xff;
+
+	/* Update environment variable */
+	eth_env_set_enetaddr("ethaddr", mac);
+}
+
+int misc_init_r(void)
+{
+	/* Set ethaddr environment variable if not set */
+	if (!env_get("ethaddr"))
+		fu540_setup_macaddr(fu540_read_serialnum());
+
+	return 0;
+}
+
+#endif
 
 int board_init(void)
 {
diff --git a/configs/sifive_fu540_defconfig b/configs/sifive_fu540_defconfig
index f78412398e..f19203745e 100644
--- a/configs/sifive_fu540_defconfig
+++ b/configs/sifive_fu540_defconfig
@@ -7,4 +7,5 @@  CONFIG_DISTRO_DEFAULTS=y
 CONFIG_FIT=y
 CONFIG_DISPLAY_CPUINFO=y
 CONFIG_DISPLAY_BOARDINFO=y
+CONFIG_MISC_INIT_R=y
 CONFIG_OF_PRIOR_STAGE=y