All of lore.kernel.org
 help / color / mirror / Atom feed
From: Pragnesh Patel <pragnesh.patel@sifive.com>
To: u-boot@lists.denx.de
Subject: [PATCH v3 01/10] misc: add driver for the Sifive otp controller
Date: Fri, 24 Jan 2020 11:20:14 +0530	[thread overview]
Message-ID: <20200124055026.30787-2-pragnesh.patel@sifive.com> (raw)
In-Reply-To: <20200124055026.30787-1-pragnesh.patel@sifive.com>

Added a misc driver to handle OTP memory in FU540.

Signed-off-by: Pragnesh Patel <pragnesh.patel@sifive.com>
Reviewed-by: Anup Patel <anup.patel@wdc.com>
---
 arch/riscv/dts/fu540-c000-u-boot.dtsi         |  13 ++
 .../dts/hifive-unleashed-a00-u-boot.dtsi      |   6 +
 board/sifive/fu540/fu540.c                    | 113 ++++------
 configs/sifive_fu540_defconfig                |   2 +
 drivers/misc/Kconfig                          |   7 +
 drivers/misc/Makefile                         |   1 +
 drivers/misc/ememory-otp.c                    | 207 ++++++++++++++++++
 7 files changed, 276 insertions(+), 73 deletions(-)
 create mode 100644 arch/riscv/dts/fu540-c000-u-boot.dtsi
 create mode 100644 arch/riscv/dts/hifive-unleashed-a00-u-boot.dtsi
 create mode 100644 drivers/misc/ememory-otp.c

diff --git a/arch/riscv/dts/fu540-c000-u-boot.dtsi b/arch/riscv/dts/fu540-c000-u-boot.dtsi
new file mode 100644
index 0000000000..615a68c0e9
--- /dev/null
+++ b/arch/riscv/dts/fu540-c000-u-boot.dtsi
@@ -0,0 +1,13 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * (C) Copyright 2019 SiFive, Inc
+ */
+
+/ {
+	soc {
+		otp: otp at 10070000 {
+			compatible = "sifive,fu540-otp";
+			reg = <0x0 0x10070000 0x0 0x0FFF>;
+		};
+	};
+};
diff --git a/arch/riscv/dts/hifive-unleashed-a00-u-boot.dtsi b/arch/riscv/dts/hifive-unleashed-a00-u-boot.dtsi
new file mode 100644
index 0000000000..bec0d19134
--- /dev/null
+++ b/arch/riscv/dts/hifive-unleashed-a00-u-boot.dtsi
@@ -0,0 +1,6 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2019 SiFive, Inc
+ */
+
+#include "fu540-c000-u-boot.dtsi"
diff --git a/board/sifive/fu540/fu540.c b/board/sifive/fu540/fu540.c
index 47a2090251..3a5e74f1fb 100644
--- a/board/sifive/fu540/fu540.c
+++ b/board/sifive/fu540/fu540.c
@@ -10,94 +10,61 @@
 #include <dm.h>
 #include <linux/delay.h>
 #include <linux/io.h>
+#include <misc.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);
+/*
+ * This define is a value used for error/unknown serial.
+ * If we really care about distinguishing errors and 0 is
+ * valid, we'll need a different one.
+ */
+#define ERROR_READING_SERIAL_NUMBER       0
 
-	return 0;
-}
+#ifdef CONFIG_MISC_INIT_R
 
-static u32 fu540_read_serialnum(void)
+#if CONFIG_IS_ENABLED(EMEMORY_OTP)
+static u32 otp_read_serialnum(struct udevice *dev)
 {
 	int ret;
 	u32 serial[2] = {0};
 
 	for (int i = 0xfe * 4; i > 0; i -= 8) {
-		ret = fu540_otp_read(i, serial, sizeof(serial));
+		ret = misc_read(dev, i, serial, sizeof(serial));
+
 		if (ret) {
-			printf("%s: error reading from OTP\n", __func__);
+			printf("%s: error reading serial from OTP\n", __func__);
 			break;
 		}
+
 		if (serial[0] == ~serial[1])
 			return serial[0];
 	}
 
-	return 0;
+	return ERROR_READING_SERIAL_NUMBER;
+}
+#endif
+
+static u32 fu540_read_serialnum(void)
+{
+	u32 serial = ERROR_READING_SERIAL_NUMBER;
+
+#if CONFIG_IS_ENABLED(EMEMORY_OTP)
+	struct udevice *dev;
+	int ret;
+
+	// init OTP
+	ret = uclass_get_device_by_driver(UCLASS_MISC,
+					  DM_GET_DRIVER(hifive_otp), &dev);
+
+	if (ret) {
+		debug("%s: could not find otp device\n", __func__);
+		return serial;
+	}
+
+	// read serial from OTP and set env var
+	serial = otp_read_serialnum(dev);
+#endif
+
+	return serial;
 }
 
 static void fu540_setup_macaddr(u32 serialnum)
diff --git a/configs/sifive_fu540_defconfig b/configs/sifive_fu540_defconfig
index 6d61e6c960..40e78f12a2 100644
--- a/configs/sifive_fu540_defconfig
+++ b/configs/sifive_fu540_defconfig
@@ -6,6 +6,8 @@ CONFIG_ARCH_RV64I=y
 CONFIG_RISCV_SMODE=y
 CONFIG_DISTRO_DEFAULTS=y
 CONFIG_FIT=y
+CONFIG_MISC=y
+CONFIG_EMEMORY_OTP=y
 CONFIG_MISC_INIT_R=y
 CONFIG_DISPLAY_CPUINFO=y
 CONFIG_DISPLAY_BOARDINFO=y
diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig
index f18aa8f7ba..cbda0deb14 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 EMEMORY_OTP
+       bool "Sifive Ememory OTP driver"
+       depends on RISCV && MISC
+       help
+         Enable support for reading the Ememory OTP on the HiFive Unleashed
+         OTP storage.
+
 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..dcf9b628c8 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_EMEMORY_OTP) += ememory-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/ememory-otp.c b/drivers/misc/ememory-otp.c
new file mode 100644
index 0000000000..73e7af496a
--- /dev/null
+++ b/drivers/misc/ememory-otp.c
@@ -0,0 +1,207 @@
+// 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)
+ */
+
+#include <common.h>
+#include <dm/device.h>
+#include <dm/read.h>
+#include <linux/io.h>
+#include <misc.h>
+
+struct hifive_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;
+
+struct hifive_otp_platdata {
+	struct hifive_otp_regs __iomem *regs;
+};
+
+typedef u32 fuse_value_t;
+#define BYTES_PER_FUSE     4
+
+#define NUM_FUSES          0x1000
+
+/*
+ * offset and size are assumed aligned to the size of the fuses (32bit).
+ */
+static int hifive_otp_read(struct udevice *dev, int offset,
+			   void *buf, int size)
+{
+	struct hifive_otp_platdata *plat = dev_get_platdata(dev);
+	struct hifive_otp_regs *regs = (struct hifive_otp_regs *)plat->regs;
+
+	int fuseidx = offset / BYTES_PER_FUSE;
+	int fusecount = size / BYTES_PER_FUSE;
+	fuse_value_t 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
+	iowrite32(0x01, &regs->pdstb); // wake up from stand-by
+	iowrite32(0x01, &regs->ptrim); // enable repair function
+	iowrite32(0x01, &regs->pce);   // enable input
+
+	// read all requested fuses
+	for (unsigned int i = 0; i < fusecount; i++, fuseidx++) {
+		iowrite32(fuseidx, &regs->pa);
+
+		// cycle clock to read
+		iowrite32(0x01, &regs->pclk);
+		mdelay(1);
+		iowrite32(0x00, &regs->pclk);
+		mdelay(1);
+
+		// read the value
+		fusebuf[i] = ioread32(&regs->pdout);
+	}
+
+	// shut down
+	iowrite32(0, &regs->pce);
+	iowrite32(0, &regs->ptrim);
+	iowrite32(0, &regs->pdstb);
+
+	// copy out
+	memcpy(buf, fusebuf, size);
+
+	return 0;
+}
+
+/*
+ * Caution:
+ * OTP can be write only once, so use carefully.
+ *
+ * offset and size are assumed aligned to the size of the fuses (32bit).
+ */
+static int hifive_otp_write(struct udevice *dev, int offset,
+			    const void *buf, int size)
+{
+	struct hifive_otp_platdata *plat = dev_get_platdata(dev);
+	struct hifive_otp_regs *regs = (struct hifive_otp_regs *)plat->regs;
+
+	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 >= NUM_FUSES)
+		return -EINVAL;
+	if ((fuseidx + fusecount) > NUM_FUSES)
+		return -EINVAL;
+
+	// init OTP
+	iowrite32(0x01, &regs->pdstb);  // wake up from stand-by
+	iowrite32(0x01, &regs->ptrim);  // enable repair function
+
+	iowrite32(0x00, &regs->pclk);
+	iowrite32(0x00, &regs->pa);
+	iowrite32(0x00, &regs->pas);
+	iowrite32(0x00, &regs->paio);
+	iowrite32(0x00, &regs->pdin);
+	iowrite32(0x00, &regs->pwe);
+	iowrite32(0x02, &regs->ptm);
+	mdelay(1);
+
+	iowrite32(0x01, &regs->pce);    // enable input
+	iowrite32(0x01, &regs->pprog);  // Program mode enable input
+	iowrite32(0x01, &regs->ptrim);
+
+	// write all requested fuses
+	for (i = 0; i < fusecount; i++, fuseidx++) {
+		iowrite32(fuseidx, &regs->pa);
+		write_data = *(write_buf++);
+
+		for (pas = 0; pas < 2; pas++) {
+			iowrite32(pas, &regs->pas);
+
+			for (bit = 0; bit < 32; bit++) {
+				iowrite32(bit, &regs->paio);
+				iowrite32(((write_data >> bit) & 1),
+					  &regs->pdin);
+				mdelay(1);
+
+				iowrite32(0x01, &regs->pwe);
+				mdelay(1);
+				iowrite32(0x00, &regs->pwe);
+				mdelay(1);
+			}
+		}
+
+		iowrite32(0x00, &regs->pas);
+	}
+
+	// shut down
+	iowrite32(0x00, &regs->pwe);
+	iowrite32(0x00, &regs->pprog);
+	iowrite32(0x00, &regs->pce);
+	iowrite32(0x00, &regs->ptm);
+
+	iowrite32(0x00, &regs->ptrim);
+	iowrite32(0x00, &regs->pdstb);
+
+	return 0;
+}
+
+static int hifive_otp_ofdata_to_platdata(struct udevice *dev)
+{
+	struct hifive_otp_platdata *plat = dev_get_platdata(dev);
+
+	plat->regs = dev_read_addr_ptr(dev);
+	return 0;
+}
+
+static const struct misc_ops hifive_otp_ops = {
+	.read = hifive_otp_read,
+	.write = hifive_otp_write,
+};
+
+static const struct udevice_id hifive_otp_ids[] = {
+	{ .compatible = "sifive,fu540-otp" },
+	{}
+};
+
+U_BOOT_DRIVER(hifive_otp) = {
+	.name = "hifive_otp",
+	.id = UCLASS_MISC,
+	.of_match = hifive_otp_ids,
+	.ofdata_to_platdata = hifive_otp_ofdata_to_platdata,
+	.platdata_auto_alloc_size = sizeof(struct hifive_otp_platdata),
+	.ops = &hifive_otp_ops,
+};
-- 
2.17.1

  reply	other threads:[~2020-01-24  5:50 UTC|newest]

Thread overview: 32+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2020-01-24  5:50 [PATCH v3 00/10] RISC-V SiFive FU540 support SPL Pragnesh Patel
2020-01-24  5:50 ` Pragnesh Patel [this message]
2020-01-24  6:41   ` [PATCH v3 01/10] misc: add driver for the Sifive otp controller Jagan Teki
2020-01-27 10:18     ` Pragnesh Patel
2020-02-10 12:44       ` Pragnesh Patel
2020-01-24  5:50 ` [PATCH v3 02/10] riscv: Add _image_binary_end for SPL Pragnesh Patel
2020-01-24  6:44   ` Jagan Teki
2020-01-24  5:50 ` [PATCH v3 03/10] lib: Makefile: build crc7.c when CONFIG_MMC_SPI Pragnesh Patel
2020-01-24  6:27   ` Jagan Teki
2020-01-24  8:06     ` Pragnesh Patel
2020-01-27  7:58       ` Jagan Teki
2020-01-24  5:50 ` [PATCH v3 04/10] riscv: sifive: dts: fu540: Add board -u-boot.dtsi files Pragnesh Patel
2020-01-24  5:50 ` [PATCH v3 05/10] riscv: sifive: fu540: add DDR4 info Pragnesh Patel
2020-01-25  8:31   ` Anup Patel
2020-01-27  7:51   ` Jagan Teki
2020-01-28  6:41     ` Pragnesh Patel
2020-02-04 15:19       ` Bin Meng
2020-01-24  5:50 ` [PATCH v3 06/10] riscv: sifive: fu540: add SPL configuration Pragnesh Patel
2020-01-25  8:35   ` Anup Patel
2020-01-24  5:50 ` [PATCH v3 07/10] configs: fu540: Add config file for U-boot SPL Pragnesh Patel
2020-01-24  6:50   ` Jagan Teki
2020-01-25  8:41     ` Anup Patel
2020-01-27  7:49       ` Pragnesh Patel
2020-01-27 12:45         ` Anup Patel
2020-01-27  6:50     ` Pragnesh Patel
2020-01-27  7:38       ` Anup Patel
2020-01-27  7:41         ` Pragnesh Patel
2020-01-24  5:50 ` [PATCH v3 08/10] riscv: sifive: fu540: enable all cache ways from u-boot proper Pragnesh Patel
2020-01-25  8:36   ` Anup Patel
2020-01-24  5:50 ` [PATCH v3 09/10] sifive: fix palmer's email address and add sifive_fu540_spl_defconfig Pragnesh Patel
2020-01-25  8:37   ` Anup Patel
2020-01-24  5:50 ` [PATCH v3 10/10] doc: update FU540 RISC-V documentation Pragnesh Patel

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20200124055026.30787-2-pragnesh.patel@sifive.com \
    --to=pragnesh.patel@sifive.com \
    --cc=u-boot@lists.denx.de \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.