Browse Source

starfive/vic7100: added OTP driver for ethernet MAC address

TekkamanV 3 years ago
parent
commit
3f3ac01a29

+ 5 - 0
arch/riscv/dts/starfive_vic7100_beagle_v.dts

@@ -669,6 +669,11 @@
 			interrupts = <122>;
 			status = "okay";
 		};
+		otp:otp@11810000{
+			compatible = "starfive,fu740-otp";
+			reg = <0x0 0x11810000 0x0 0x10000>;
+			fuse-count = <0x200>;
+		};
 	};
 };
 #include "tda998x_1080p.dtsi"

+ 61 - 28
board/starfive/vic7100/vic7100.c

@@ -855,6 +855,7 @@ INIT_FUNC_DEF(trng)
 INIT_FUNC_DEF(otp)
 {
     _ENABLE_CLOCK_clk_otp_apb_;
+    _ASSERT_RESET_rstgen_rstn_otp_apb_
     _CLEAR_RESET_rstgen_rstn_otp_apb_;
 }
 
@@ -1182,14 +1183,14 @@ void reset_phy(void)
 #define ERROR_READING_SERIAL_NUMBER        0
 
 #ifdef CONFIG_MISC_INIT_R
-static u32 setup_serialnum(void);
-static void setup_macaddr(u32 serialnum);
+static u32 vic_read_serialnum(void);
+static void vic_setup_macaddr(u32 serialnum);
 
 int misc_init_r(void)
 {
 	if (!env_get("serial#")) {
-		u32 serialnum = setup_serialnum();
-		setup_macaddr(serialnum);
+		u32 serialnum = vic_read_serialnum();
+		vic_setup_macaddr(serialnum);
 	}
 	return 0;
 }
@@ -1223,55 +1224,87 @@ ulong board_flash_get_legacy(ulong base, int banknum, flash_info_t *info)
 	return (void *)CONFIG_SYS_FDT_BASE;
 }*/
 
-#if CONFIG_IS_ENABLED(SIFIVE_OTP)
+#if CONFIG_IS_ENABLED(STARFIVE_OTP)
+static u32 otp_read_mac(struct udevice *dev, unsigned char *buf)
+ {
+	u32 serial[2] = {0};
+	int ret = misc_read(dev, STARFIVE_OTP_MAC_OFFSET,
+			    serial, sizeof(serial));
+	if (ret != sizeof(serial)) {
+		printf("%s: error reading mac from OTP\n", __func__);
+		return ERROR_READING_SERIAL_NUMBER;
+	}
+
+	buf[3] = (serial[0] >> 24) & 0xff;
+	buf[2] = (serial[0] >> 16) & 0xff;
+	buf[1] = (serial[0] >> 8) & 0xff;
+	buf[0] = serial[0] & 0xff;
+
+	buf[5] = (serial[1] >> 8) & 0xff;
+	buf[4] = serial[1] & 0xff;
+
+	return ret;
+}
+
 static u32 otp_read_serialnum(struct udevice *dev)
 {
 	u32 serial[2] = {0};
-	int ret;
-	for (int i = 0xfe * 4; i > 0; i -= 8) {
-		ret = misc_read(dev, i, serial, sizeof(serial));
-		if (ret) {
-			printf("%s: error reading serial from OTP\n", __func__);
-			break;
-		}
-		if (serial[0] == ~serial[1])
-			return serial[0];
+	int ret = misc_read(dev, STARFIVE_OTP_MAC_OFFSET-8,
+			    serial, sizeof(serial));
+	if (ret != sizeof(serial)) {
+		printf("%s: error reading serial from OTP\n", __func__);
+		return ERROR_READING_SERIAL_NUMBER;
 	}
+
+	if (serial[0] == ~serial[1])
+		return serial[0];
+
 	return ERROR_READING_SERIAL_NUMBER;
 }
 #endif
 
-static u32 setup_serialnum(void)
+static u32 vic_read_serialnum(void)
 {
 	u32 serial = ERROR_READING_SERIAL_NUMBER;
-
-#if CONFIG_IS_ENABLED(SIFIVE_OTP)
-	char serial_str[6];
+#if CONFIG_IS_ENABLED(STARFIVE_OTP)
 	struct udevice *dev;
-	int ret;
+	char buf[9] = {0};
 
-	// init OTP
-	ret = uclass_get_device_by_driver(UCLASS_MISC, DM_GET_DRIVER(hifive_otp), &dev);
-	if (ret) {
+	if (uclass_get_device_by_driver(UCLASS_MISC,
+					DM_DRIVER_GET(starfive_otp), &dev)) {
 		debug("%s: could not find otp device\n", __func__);
-		return serial;
+		return ERROR_READING_SERIAL_NUMBER;
 	}
 
 	// read serial from OTP and set env var
 	serial = otp_read_serialnum(dev);
-	snprintf(serial_str, sizeof(serial_str), "%05"PRIu32, serial);
-	env_set("serial#", serial_str);
+	snprintf(buf, sizeof(buf), "%08x", serial);
+	env_set("serial#", buf);
 #endif
 
 	return serial;
 }
 
-static void setup_macaddr(u32 serialnum) {
-	// OR the serial into the MAC -- see SiFive FSBL
-	unsigned char mac[6] = {0x66,0x34,0xb0,0x6c,0xde,0xad };
+static void vic_setup_macaddr(u32 serialnum)
+{
+#if CONFIG_IS_ENABLED(STARFIVE_OTP)
+	struct udevice *dev;
+	unsigned char mac[6]={0};
+
+	// init OTP
+	if (uclass_get_device_by_driver(UCLASS_MISC,
+					DM_DRIVER_GET(starfive_otp), &dev)) {
+		debug("%s: could not find otp device\n", __func__);
+		return;
+	}
+
+	otp_read_mac(dev, mac);
+#else
+	unsigned char mac[6] = {0x66, 0x34, 0xb0, 0x6c, 0xde, 0xad};
 	mac[5] |= (serialnum >>  0) & 0xff;
 	mac[4] |= (serialnum >>  8) & 0xff;
 	mac[3] |= (serialnum >> 16) & 0xff;
+#endif
 	eth_env_set_enetaddr("ethaddr", mac);
 }
 

+ 8 - 0
drivers/misc/Kconfig

@@ -75,6 +75,14 @@ config SIFIVE_OTP
 	  Enable support for reading and writing the eMemory OTP on the
 	  SiFive SoCs.
 
+config STARFIVE_OTP
+	bool "StarFive eMemory OTP driver"
+	depends on MISC && VIC_BEAGLE_V
+	default y if VIC_BEAGLE_V
+	help
+	  Enable support for reading and writing the eMemory OTP on the
+	  StarFive SoCs.
+
 config VEXPRESS_CONFIG
 	bool "Enable support for Arm Versatile Express config bus"
 	depends on MISC

+ 1 - 0
drivers/misc/Makefile

@@ -60,6 +60,7 @@ obj-$(CONFIG_ROCKCHIP_EFUSE) += rockchip-efuse.o
 obj-$(CONFIG_ROCKCHIP_OTP) += rockchip-otp.o
 obj-$(CONFIG_SANDBOX) += syscon_sandbox.o misc_sandbox.o
 obj-$(CONFIG_SIFIVE_OTP) += sifive-otp.o
+obj-$(CONFIG_STARFIVE_OTP) += starfive-otp.o
 obj-$(CONFIG_SMSC_LPC47M) += smsc_lpc47m.o
 obj-$(CONFIG_SMSC_SIO1007) += smsc_sio1007.o
 obj-$(CONFIG_STM32MP_FUSE) += stm32mp_fuse.o

+ 219 - 0
drivers/misc/starfive-otp.c

@@ -0,0 +1,219 @@
+/*
+ *   Copyright 2021 StarFive, Inc
+ *
+ *   This program is free software; you can redistribute it and/or
+ *   modify it under the terms of the GNU General Public License
+ *   as published by the Free Software Foundation, version 2.
+ *
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU General Public License for more details.
+ */
+
+#include <common.h>
+#include <dm/device.h>
+#include <dm/read.h>
+#include <linux/bitops.h>
+#include <linux/delay.h>
+#include <linux/io.h>
+#include <misc.h>
+
+#include <asm/arch/global_reg.h>
+#include <asm/arch/io.h>
+
+#define BYTES_PER_FUSE		4
+
+// otp reg offset
+#define OTP_CFGR        0x00
+#define OTPC_IER        0x04
+#define OTPC_SRR        0x08
+#define OTP_OPRR        0x0c
+#define OTPC_CTLR       0x10
+#define OTPC_ADDRR      0x14
+#define OTPC_DINR       0x18
+#define OTPC_DOUTR      0x1c
+
+#define OTP_MEM_START   0x800
+#define OTP_MEM_SIZE    0x800
+#define OTP_EMPTY_CELL_VALUE  0xffffffffUL
+
+// cfgr (offset 0x00)
+#define OTP_CFGR_PRG_CNT_MASK   0xff
+#define OTP_CFGR_PRG_CNT_SHIFT  0
+#define OTP_CFGR_DIV_1US_MASK   0xff
+#define OTP_CFGR_DIV_1US_SHIFT  8
+#define OTP_CFGR_RD_CYC_MASK    0x0f
+#define OTP_CFGR_RD_CYC_SHIFT   16
+
+// ier (offset 0x04)
+#define OTPC_IER_DONE_IE        BIT(0)
+#define OTPC_IER_BUSY_OPR_IE    BIT(1)
+
+// srr (offset 0x08)
+#define OTPC_SRR_DONE           BIT(0)
+#define OTPC_SRR_BUSY_OPR       BIT(1)
+#define OTPC_SRR_INFO_RD_LOCK   BIT(29)
+#define OTPC_SRR_INFO_WR_LOCK   BIT(30)
+#define OTPC_SRR_BUSY           BIT(31)
+
+// oprr (offset 0x0c)
+#define OTP_OPRR_OPR_MASK           0x00000007
+#define OTP_OPRR_OPR_SHIFT          0
+
+#define OTP_OPR_STANDBY             0x0 // user mode
+#define OTP_OPR_READ                0x1 // user mode
+#define OTP_OPR_MARGIN_READ_PROG    0x2 // testing mode
+#define OTP_OPR_MARGIN_READ_INIT    0x3 // testing mode
+#define OTP_OPR_PROGRAM             0x4 // user mode
+#define OTP_OPR_DEEP_STANDBY        0x5 // user mode
+#define OTP_OPR_DEBUG               0x6 // user mode
+
+// ctlr (offset 0x10, see EG512X32TH028CW01_v1.0.pdf "Pin Description")
+#define OTPC_CTLR_PCE               BIT(0)
+#define OTPC_CTLR_PTM_MASK          0x0000000e
+#define OTPC_CTLR_PTM_SHIFT         1
+#define OTPC_CTLR_PDSTB             BIT(4)
+#define OTPC_CTLR_PTR               BIT(5)
+#define OTPC_CTLR_PPROG             BIT(6)
+#define OTPC_CTLR_PWE               BIT(7)
+#define OTPC_CTLR_PCLK              BIT(8)
+
+// addrr (offset 0x14)
+#define OTPC_ADDRR_PA_MASK          0x000001ff
+#define OTPC_ADDRR_PA_SHIFT         0
+
+/*
+data formate
+struct starfive_otp_data{
+	char vendor[32];
+	uint64_t sn;
+	uint8_t mac_addr[6];
+	uint8_t padding_0[2];
+}
+*/
+
+#define STARFIVE_OTP_GPIO	56		/*control power*/
+
+struct starfive_otp_regs {
+	/* TODO: add otp ememory_eg512x32 registers define */
+	uint32_t otp_cfg;		/*!< timing Register.,							Address offset = 0x00  */
+	uint32_t otpc_ie;		/*!< interrupt Enable Register.,				Address offset = 0x04  */
+	uint32_t otpc_sr;		/*!< status Register.,							Address offset = 0x08  */
+	uint32_t otp_opr;		/*!< operation mode select Register.,			Address offset = 0x0C  */
+	uint32_t otpc_ctl; 		/*!< otp control port,							Address offset = 0x10  */
+	uint32_t otpc_addr;		/*!< otp pa port ,								Address offset = 0x14  */
+	uint32_t otpc_din; 		/*!< otp pdin port, 							Address offset = 0x18  */
+	uint32_t otpc_dout; 	/*!< otp pdout, 								Address offset = 0x1C  */
+};
+
+struct starfive_otp_platdata {
+	struct starfive_otp_regs __iomem *regs;
+	u32 total_fuses;
+};
+
+static void gpio_direction_output(u32 gpio, u32 val)
+{
+	volatile uint32_t __iomem *addr = EZGPIO_FULLMUX_BASE_ADDR + 0x50 +
+					  gpio * 8;
+
+	MA_OUTW(addr, val);
+	MA_OUTW(addr + 1, val);
+}
+
+static void gpio_set_value(u32 gpio, u32 val)
+{
+	volatile uint32_t __iomem *addr = EZGPIO_FULLMUX_BASE_ADDR + 0x50 +
+					  gpio * 8;
+
+	MA_OUTW(addr, val);
+}
+
+/*
+ * offset and size are assumed aligned to the size of the fuses (32-bit).
+ */
+static int starfive_otp_read(struct udevice *dev, int offset,
+			     void *buf, int size)
+{
+	struct starfive_otp_platdata *plat = dev_get_plat(dev);
+	struct starfive_otp_regs *regs = (struct starfive_otp_regs *)plat->regs;
+	int fuseidx = offset / BYTES_PER_FUSE;
+	int fusecount = size / BYTES_PER_FUSE;
+	u32 fusebuf[fusecount];
+	u32 addr = (u32)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;
+	}
+
+	/* check bounds */
+	if (offset < 0 || size < 0)
+		return -EINVAL;
+	if (fuseidx >= plat->total_fuses)
+		return -EINVAL;
+	if ((fuseidx + fusecount) > plat->total_fuses)
+		return -EINVAL;
+
+	//otp power off and on
+	gpio_direction_output(STARFIVE_OTP_GPIO, 0);
+	mdelay(5);
+	gpio_set_value(STARFIVE_OTP_GPIO, 1);
+	mdelay(10);
+
+	//otp set to read mode
+	writel(OTP_OPR_READ, &regs->otp_opr);
+	mdelay(5);
+
+	/* read all requested fuses */
+	for (unsigned int i = 0; i < fusecount; i++, fuseidx++) {
+		/* read the value */
+		fusebuf[i] = readl((volatile void __iomem *)
+				   (addr + OTP_MEM_START +
+				   fuseidx * BYTES_PER_FUSE));
+	}
+
+	//otp power off
+	gpio_set_value(STARFIVE_OTP_GPIO, 0);
+	mdelay(5);
+
+	/* copy out */
+	memcpy(buf, fusebuf, size);
+
+	return size;
+}
+
+static int starfive_otp_ofdata_to_platdata(struct udevice *dev)
+{
+	struct starfive_otp_platdata *plat = dev_get_plat(dev);
+	int ret;
+
+	plat->regs = dev_read_addr_ptr(dev);
+
+	ret = dev_read_u32(dev, "fuse-count", &plat->total_fuses);
+	if (ret < 0) {
+		pr_err("\"fuse-count\" not found\n");
+		return ret;
+	}
+	return 0;
+}
+
+static const struct misc_ops starfive_otp_ops = {
+	.read = starfive_otp_read,
+};
+
+static const struct udevice_id sifive_otp_ids[] = {
+	{ .compatible = "starfive,fu740-otp" },
+	{ }
+};
+
+U_BOOT_DRIVER(starfive_otp) = {
+	.name = "starfive_otp",
+	.id = UCLASS_MISC,
+	.of_match = sifive_otp_ids,
+	.of_to_plat = starfive_otp_ofdata_to_platdata,
+	.plat_auto = sizeof(struct starfive_otp_platdata),
+	.ops = &starfive_otp_ops,
+};

+ 2 - 0
include/configs/starfive-vic7100.h

@@ -77,6 +77,8 @@
 
 #define CONFIG_BOOTFILE			"vmlinuz"
 
+/*mac addr offset in otp*/
+#define STARFIVE_OTP_MAC_OFFSET		0x28
 
 /*
  * Ethernet