Просмотр исходного кода

misc:OTP:Starfive-jh7110: Add driver for the Starfive otp controller

Added a misc driver to handle OTP memory in Starfive SoCs.

Signed-off-by: yanhong.wang <yanhong.wang@starfivetech.com>
yanhong.wang 2 лет назад
Родитель
Сommit
4187fa6a2d
3 измененных файлов с 196 добавлено и 0 удалено
  1. 7 0
      drivers/misc/Kconfig
  2. 1 0
      drivers/misc/Makefile
  3. 188 0
      drivers/misc/starfive-otp.c

+ 7 - 0
drivers/misc/Kconfig

@@ -75,6 +75,13 @@ config SIFIVE_OTP
 	  Enable support for reading and writing the eMemory OTP on the
 	  SiFive SoCs.
 
+config STARFIVE_OTP
+        bool "StarFive eFuse OTP driver"
+        depends on MISC
+        help
+          Enable support for reading and writing the eFuse 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

@@ -66,6 +66,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

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

@@ -0,0 +1,188 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2022 Starfive, Inc.
+ * Author:	yanhong <yanhong.wang@starfivetech.com>
+ *
+ */
+
+#include <common.h>
+#include <clk.h>
+#include <dm/device.h>
+#include <dm/read.h>
+#include <linux/delay.h>
+#include <asm/io.h>
+#include <misc.h>
+
+/*otp param*/
+#define OTP_MEM_START   0x800
+#define OTP_MEM_SIZE    0x800
+#define OTP_DIV_1US	1000000
+
+/* program pulse time select. default value is 11*/
+#define OTP_TPW_TIME	11
+
+/* Read Data Access Time. max 50ns*/
+#define OTP_TCD_TIME	20
+
+#define OTP_PAOGRAM_DELAY	100
+#define OTP_TURN_ON_DELAY	200
+
+/*timing Register*/
+#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
+
+/*status Register*/
+#define OTPC_SRR_BUSY           (1<<31)
+
+/*otp operation mode select*/
+#define OTP_OPRR_OPR_MASK           0x7
+#define OTP_OPR_STANDBY             0x0 /* set otp to standby*/
+#define OTP_OPR_READ                0x1 /* set otp to read*/
+
+#define OTPC_TIMEOUT_CNT	10000
+#define BYTES_PER_INT		4
+
+struct starfive_otp_regs {
+	u32 otp_cfg;        /*timing Register*/
+	u32 otpc_ie;        /*interrupt Enable Register*/
+	u32 otpc_sr;        /*status Register*/
+	u32 otp_opr;        /*operation mode select Register*/
+	u32 otpc_ctl;       /*otp control port*/
+	u32 otpc_addr;      /*otp pa port*/
+	u32 otpc_din;       /*otp pdin port*/
+	u32 otpc_dout;      /*otp pdout*/
+};
+
+struct starfive_otp_platdata {
+	struct starfive_otp_regs __iomem *regs;
+	struct clk clk;
+	u32 pclk_hz;
+};
+
+static int starfive_otp_regstatus(struct starfive_otp_regs *regs,
+							u32 mask)
+{
+	int delay = OTPC_TIMEOUT_CNT;
+
+	while (readl(&regs->otpc_sr) & mask) {
+		udelay(OTP_PAOGRAM_DELAY);
+		delay--;
+		if (delay <= 0) {
+			printf("%s: check otp status timeout\n", __func__);
+			return -ETIMEDOUT;
+		}
+	}
+	return 0;
+}
+
+static int starfive_otp_setmode(struct starfive_otp_regs *regs,
+							u32 mode)
+{
+	writel(mode & OTP_OPRR_OPR_MASK, &regs->otp_opr);
+	starfive_otp_regstatus(regs, OTPC_SRR_BUSY);
+
+	return 0;
+}
+
+static int starfive_otp_config(struct udevice *dev)
+{
+	struct starfive_otp_platdata *plat = dev_get_plat(dev);
+	struct starfive_otp_regs *regs = (struct starfive_otp_regs *)plat->regs;
+	u32 div_1us;
+	u32 rd_cyc;
+	u32 val;
+
+	div_1us = plat->pclk_hz / OTP_DIV_1US;
+	rd_cyc = div_1us / OTP_TCD_TIME + 2;
+
+	val = OTP_TPW_TIME;
+	val |= (rd_cyc & OTP_CFGR_RD_CYC_MASK) << OTP_CFGR_RD_CYC_SHIFT;
+	val |= (div_1us & OTP_CFGR_DIV_1US_MASK) << OTP_CFGR_DIV_1US_SHIFT;
+
+	writel(val, &regs->otp_cfg);
+
+	return 0;
+}
+
+static int starfive_otp_read(struct udevice *dev, int offset,
+			   void *buf, int size)
+{
+	struct starfive_otp_platdata *plat = dev_get_plat(dev);
+	void *base = (void *)plat->regs;
+	u32 *databuf = (u32 *)buf;
+	u32 data;
+	int bytescnt;
+	int i;
+
+	if (!buf || (offset >= OTP_MEM_SIZE) || (offset & 0x3)) {
+		printf("%s:invalid parameter.\n", __func__);
+		return -EINVAL;
+	}
+
+	bytescnt = size / BYTES_PER_INT;
+
+	for (i = 0; i < bytescnt; i++) {
+		starfive_otp_setmode(plat->regs, OTP_OPR_READ);
+
+		/* read the value */
+		data = readl(base + OTP_MEM_START + offset);
+		starfive_otp_regstatus(plat->regs, OTPC_SRR_BUSY);
+		starfive_otp_setmode(plat->regs, OTP_OPR_STANDBY);
+		databuf[i] = data;
+		offset += 4;
+	}
+	return 0;
+}
+
+static int starfive_otp_probe(struct udevice *dev)
+{
+	starfive_otp_config(dev);
+	udelay(OTP_TURN_ON_DELAY);
+
+	return 0;
+}
+
+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);
+	if (!plat->regs)
+		goto err;
+
+	ret = dev_read_u32(dev, "clock-frequency", &plat->pclk_hz);
+	if (ret < 0)
+		goto err;
+
+	ret = clk_get_by_name(dev, "apb", &plat->clk);
+	if (ret)
+		goto err;
+
+	ret = clk_enable(&plat->clk);
+	if (ret < 0)
+		goto err;
+
+	return 0;
+err:
+	printf("%s init fail.\n", __func__);
+	return ret;
+}
+static const struct misc_ops starfive_otp_ops = {
+	.read = starfive_otp_read,
+};
+static const struct udevice_id starfive_otp_ids[] = {
+	{ .compatible = "starfive,jh7110-otp" },
+	{}
+};
+U_BOOT_DRIVER(starfive_otp) = {
+	.name = "starfive_otp",
+	.id = UCLASS_MISC,
+	.of_match = starfive_otp_ids,
+	.probe = starfive_otp_probe,
+	.of_to_plat = starfive_otp_ofdata_to_platdata,
+	.plat_auto = sizeof(struct starfive_otp_platdata),
+	.ops = &starfive_otp_ops,
+};