
Hi Jagan,
-----Original Message----- From: Jagan Teki jagan@amarulasolutions.com Sent: 02 April 2020 14:46 To: Pragnesh Patel pragnesh.patel@sifive.com Cc: U-Boot-Denx u-boot@lists.denx.de; Atish Patra atish.patra@wdc.com; palmerdabbelt@google.com; Bin Meng bmeng.cn@gmail.com; Paul Walmsley paul.walmsley@sifive.com; Troy Benjegerdes troy.benjegerdes@sifive.com; Anup Patel anup.patel@wdc.com; Sagar Kadam sagar.kadam@sifive.com; Rick Chen rick@andestech.com; Simon Glass sjg@chromium.org; Eugen Hristev eugen.hristev@microchip.com; Tero Kristo t-kristo@ti.com; Marcel Ziswiler marcel.ziswiler@toradex.com; Finley Xiao <finley.xiao@rock- chips.com>; Peng Fan peng.fan@nxp.com Subject: Re: [PATCH v6 01/17] misc: add driver for the SiFive otp controller
[External Email] Do not click links or attachments unless you recognize the sender and know the content is safe
On Sun, Mar 29, 2020 at 10:36 PM Pragnesh Patel pragnesh.patel@sifive.com wrote:
Added a misc driver to handle OTP memory in SiFive SoCs.
Signed-off-by: Pragnesh Patel pragnesh.patel@sifive.com
drivers/misc/Kconfig | 7 ++ drivers/misc/Makefile | 1 + drivers/misc/sifive-otp.c | 255 ++++++++++++++++++++++++++++++++++++++ 3 files changed, 263 insertions(+) create mode 100644 drivers/misc/sifive-otp.c
diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig index f18aa8f7ba..5caf61d077 100644 --- a/drivers/misc/Kconfig +++ b/drivers/misc/Kconfig @@ -68,6 +68,13 @@ config ROCKCHIP_OTP addressing and a length or through child-nodes that are generated based on the e-fuse map retrieved from the DTS.
+config SIFIVE_OTP
bool "SiFive eMemory OTP driver"
depends on RISCV && MISC
help
Enable support for reading and writing the eMemory OTP on the
SiFive SoCs.
config VEXPRESS_CONFIG bool "Enable support for Arm Versatile Express config bus" depends on MISC diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile index 2b843de93c..ee888631b6 100644 --- a/drivers/misc/Makefile +++ b/drivers/misc/Makefile @@ -58,6 +58,7 @@ obj-$(CONFIG_$(SPL_)PWRSEQ) += pwrseq-uclass.o obj-$(CONFIG_QFW) += qfw.o obj-$(CONFIG_ROCKCHIP_EFUSE) += rockchip-efuse.o obj-$(CONFIG_ROCKCHIP_OTP) += rockchip-otp.o +obj-$(CONFIG_SIFIVE_OTP) += sifive-otp.o obj-$(CONFIG_SANDBOX) += syscon_sandbox.o misc_sandbox.o obj-$(CONFIG_SMSC_LPC47M) += smsc_lpc47m.o obj-$(CONFIG_SMSC_SIO1007) += smsc_sio1007.o diff --git a/drivers/misc/sifive-otp.c b/drivers/misc/sifive-otp.c new file mode 100644 index 0000000000..c1c2386b97 --- /dev/null +++ b/drivers/misc/sifive-otp.c @@ -0,0 +1,255 @@ +// SPDX-License-Identifier: GPL-2.0 +/*
- This is a driver for the eMemory EG004K32TQ028XW01 NeoFuse
- One-Time-Programmable (OTP) memory used within the SiFive FU540.
- It is documented in the FU540 manual here:
+https://www.sifive.com/documentation/chips/freedom-u540-c000-
manual/
- Copyright (C) 2018 Philipp Hug philipp@hug.cx
- Copyright (C) 2018 Joey Hewitt joey@joeyhewitt.com
- Copyright (C) 2020 SiFive, Inc
- */
+/*
- The FU540 stores 4096x32 bit (16KiB) values.
- Index 0x00-0xff are reserved for SiFive internal use. (first 1KiB)
- Right now first 1KiB is used to store only serial number.
- */
+#include <common.h> +#include <dm/device.h> +#include <dm/read.h> +#include <linux/io.h> +#include <misc.h>
+#define BYTES_PER_FUSE 4
+#define PA_RESET_VAL 0x00 +#define PAS_RESET_VAL 0x00 +#define PAIO_RESET_VAL 0x00 +#define PDIN_RESET_VAL 0x00 +#define PTM_RESET_VAL 0x00
+#define PCLK_ENABLE_VAL BIT(0) +#define PCLK_DISABLE_VAL 0x00
+#define PWE_WRITE_ENABLE BIT(0) +#define PWE_WRITE_DISABLE 0x00
+#define PTM_FUSE_PROGRAM_VAL BIT(1)
+#define PCE_ENABLE_INPUT BIT(0) +#define PCE_DISABLE_INPUT 0x00
+#define PPROG_ENABLE_INPUT BIT(0) +#define PPROG_DISABLE_INPUT 0x00
+#define PTRIM_ENABLE_INPUT BIT(0) +#define PTRIM_DISABLE_INPUT 0x00
+#define PDSTB_DEEP_STANDBY_ENABLE BIT(0) +#define PDSTB_DEEP_STANDBY_DISABLE 0x00
+struct sifive_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) */
+};
+struct sifive_otp_platdata {
struct sifive_otp_regs __iomem *regs;
u32 total_fuses;
+};
+/*
- offset and size are assumed aligned to the size of the fuses (32-bit).
- */
+static int sifive_otp_read(struct udevice *dev, int offset,
void *buf, int size) {
struct sifive_otp_platdata *plat = dev_get_platdata(dev);
struct sifive_otp_regs *regs = (struct sifive_otp_regs
+*)plat->regs;
/* Check if offset and size are multiple of BYTES_PER_FUSE */
if ((size % BYTES_PER_FUSE) || (offset % BYTES_PER_FUSE)) {
printf("%s: size and offset must be multiple of 4.\n",
__func__);
return -EINVAL;
}
int fuseidx = offset / BYTES_PER_FUSE;
int fusecount = size / BYTES_PER_FUSE;
/* check bounds */
if (offset < 0 || size < 0)
return -EINVAL;
if (fuseidx >= plat->total_fuses)
return -EINVAL;
if ((fuseidx + fusecount) > plat->total_fuses)
return -EINVAL;
u32 fusebuf[fusecount];
/* init OTP */
writel(PDSTB_DEEP_STANDBY_ENABLE, ®s->pdstb);
writel(PTRIM_ENABLE_INPUT, ®s->ptrim);
writel(PCE_ENABLE_INPUT, ®s->pce);
/* read all requested fuses */
for (unsigned int i = 0; i < fusecount; i++, fuseidx++) {
writel(fuseidx, ®s->pa);
/* cycle clock to read */
writel(PCLK_ENABLE_VAL, ®s->pclk);
mdelay(1);
writel(PCLK_DISABLE_VAL, ®s->pclk);
mdelay(1);
/* read the value */
fusebuf[i] = readl(®s->pdout);
}
/* shut down */
writel(PCE_DISABLE_INPUT, ®s->pce);
writel(PTRIM_DISABLE_INPUT, ®s->ptrim);
writel(PDSTB_DEEP_STANDBY_DISABLE, ®s->pdstb);
/* copy out */
memcpy(buf, fusebuf, size);
return size;
+}
+/*
- Caution:
- OTP can be written only once, so use carefully.
- offset and size are assumed aligned to the size of the fuses (32-bit).
- */
+static int sifive_otp_write(struct udevice *dev, int offset,
const void *buf, int size) {
struct sifive_otp_platdata *plat = dev_get_platdata(dev);
struct sifive_otp_regs *regs = (struct sifive_otp_regs
+*)plat->regs;
/* Check if offset and size are multiple of BYTES_PER_FUSE */
if ((size % BYTES_PER_FUSE) || (offset % BYTES_PER_FUSE)) {
printf("%s: size and offset must be multiple of 4.\n",
__func__);
return -EINVAL;
}
int fuseidx = offset / BYTES_PER_FUSE;
int fusecount = size / BYTES_PER_FUSE;
u32 *write_buf = (u32 *)buf;
u32 write_data;
int i, pas, bit;
/* check bounds */
if (offset < 0 || size < 0)
return -EINVAL;
if (fuseidx >= plat->total_fuses)
return -EINVAL;
if ((fuseidx + fusecount) > plat->total_fuses)
return -EINVAL;
/* init OTP */
writel(PDSTB_DEEP_STANDBY_ENABLE, ®s->pdstb);
writel(PTRIM_ENABLE_INPUT, ®s->ptrim);
/* reset registers */
writel(PCLK_DISABLE_VAL, ®s->pclk);
writel(PA_RESET_VAL, ®s->pa);
writel(PAS_RESET_VAL, ®s->pas);
writel(PAIO_RESET_VAL, ®s->paio);
writel(PDIN_RESET_VAL, ®s->pdin);
writel(PWE_WRITE_DISABLE, ®s->pwe);
writel(PTM_FUSE_PROGRAM_VAL, ®s->ptm);
mdelay(1);
writel(PCE_ENABLE_INPUT, ®s->pce);
writel(PPROG_ENABLE_INPUT, ®s->pprog);
/* write all requested fuses */
for (i = 0; i < fusecount; i++, fuseidx++) {
writel(fuseidx, ®s->pa);
write_data = *(write_buf++);
for (pas = 0; pas < 2; pas++) {
writel(pas, ®s->pas);
for (bit = 0; bit < 32; bit++) {
writel(bit, ®s->paio);
writel(((write_data >> bit) & 1),
®s->pdin);
mdelay(1);
writel(PWE_WRITE_ENABLE, ®s->pwe);
mdelay(1);
writel(PWE_WRITE_DISABLE, ®s->pwe);
mdelay(1);
are these delays with 1msec are based on the otp programming sequence, if yes please add comments on that. rest looks good to me.
Yes, this delays are based on otp programming sequence.
writel(PWE_WRITE_ENABLE, ®s->pwe); mdelay(1); - This is Tpw (Program Pulse width time) and it's range is 10 - 20 micro seconds
writel(PWE_WRITE_DISABLE, ®s->pwe); mdelay(1); - This is Tpwi (Program Pulse interval time) and it's range is 1 - 5 micro seconds
Though I have used mdelay(1) here, it's working fine. I will update delay with description in v7.
Jagan.