rockchip: efuse: Refactor to use driver data and ops

Refactor the driver to use driver data and ops to simplify handling
of SoCs that require a unique read op.

Move handling of the aligned bounce buffer to main read op in order to
keep the SoC unique read op simple.

Signed-off-by: Jonas Karlman <jonas@kwiboo.se>
Reviewed-by: Kever Yang <kever.yang@rock-chips.com>
diff --git a/drivers/misc/rockchip-efuse.c b/drivers/misc/rockchip-efuse.c
index 083ee65..7d4a984 100644
--- a/drivers/misc/rockchip-efuse.c
+++ b/drivers/misc/rockchip-efuse.c
@@ -13,50 +13,40 @@
 #include <dm.h>
 #include <linux/bitops.h>
 #include <linux/delay.h>
+#include <malloc.h>
 #include <misc.h>
 
-#define RK3399_A_SHIFT          16
-#define RK3399_A_MASK           0x3ff
-#define RK3399_NFUSES           32
-#define RK3399_BYTES_PER_FUSE   4
-#define RK3399_STROBSFTSEL      BIT(9)
-#define RK3399_RSB              BIT(7)
-#define RK3399_PD               BIT(5)
-#define RK3399_PGENB            BIT(3)
-#define RK3399_LOAD             BIT(2)
-#define RK3399_STROBE           BIT(1)
-#define RK3399_CSB              BIT(0)
-
-struct rockchip_efuse_regs {
-	u32 ctrl;      /* 0x00  efuse control register */
-	u32 dout;      /* 0x04  efuse data out register */
-	u32 rf;        /* 0x08  efuse redundancy bit used register */
-	u32 _rsvd0;
-	u32 jtag_pass; /* 0x10  JTAG password */
-	u32 strobe_finish_ctrl;
-		       /* 0x14	efuse strobe finish control register */
-};
+#define EFUSE_CTRL		0x0000
+#define RK3399_A_SHIFT		16
+#define RK3399_A_MASK		GENMASK(25, 16)
+#define RK3399_ADDR(n)		((n) << RK3399_A_SHIFT)
+#define RK3399_STROBSFTSEL	BIT(9)
+#define RK3399_RSB		BIT(7)
+#define RK3399_PD		BIT(5)
+#define RK3399_PGENB		BIT(3)
+#define RK3399_LOAD		BIT(2)
+#define RK3399_STROBE		BIT(1)
+#define RK3399_CSB		BIT(0)
+#define EFUSE_DOUT		0x0004
 
 struct rockchip_efuse_plat {
 	void __iomem *base;
-	struct clk *clk;
 };
 
+struct rockchip_efuse_data {
+	int (*read)(struct udevice *dev, int offset, void *buf, int size);
+	int size;
+	int block_size;
+};
+
 #if defined(DEBUG)
-static int dump_efuses(struct cmd_tbl *cmdtp, int flag,
-		       int argc, char *const argv[])
+static int dump_efuse(struct cmd_tbl *cmdtp, int flag,
+		      int argc, char *const argv[])
 {
-	/*
-	 * N.B.: This function is tailored towards the RK3399 and assumes that
-	 *       there's always 32 fuses x 32 bits (i.e. 128 bytes of data) to
-	 *       be read.
-	 */
-
 	struct udevice *dev;
-	u8 fuses[128];
-	int ret;
+	u8 data[4];
+	int ret, i;
 
-	/* retrieve the device */
 	ret = uclass_get_device_by_driver(UCLASS_MISC,
 					  DM_DRIVER_GET(rockchip_efuse), &dev);
 	if (ret) {
@@ -64,21 +54,20 @@
 		return 0;
 	}
 
-	ret = misc_read(dev, 0, &fuses, sizeof(fuses));
-	if (ret < 0) {
-		printf("%s: misc_read failed\n", __func__);
-		return 0;
-	}
+	for (i = 0; true; i += sizeof(data)) {
+		ret = misc_read(dev, i, &data, sizeof(data));
+		if (ret < 0)
+			return 0;
 
-	printf("efuse-contents:\n");
-	print_buffer(0, fuses, 1, 128, 16);
+		print_buffer(i, data, 1, sizeof(data), sizeof(data));
+	}
 
 	return 0;
 }
 
 U_BOOT_CMD(
-	rk3399_dump_efuses, 1, 1, dump_efuses,
-	"Dump the content of the efuses",
+	dump_efuse, 1, 1, dump_efuse,
+	"Dump the content of the efuse",
 	""
 );
 #endif
@@ -86,43 +75,25 @@
 static int rockchip_rk3399_efuse_read(struct udevice *dev, int offset,
 				      void *buf, int size)
 {
-	struct rockchip_efuse_plat *plat = dev_get_plat(dev);
-	struct rockchip_efuse_regs *efuse =
-		(struct rockchip_efuse_regs *)plat->base;
-
-	unsigned int addr_start, addr_end, addr_offset;
-	u32 out_value;
-	u8  bytes[RK3399_NFUSES * RK3399_BYTES_PER_FUSE];
-	int i = 0;
-	u32 addr;
+	struct rockchip_efuse_plat *efuse = dev_get_plat(dev);
+	u32 *buffer = buf;
 
-	addr_start = offset / RK3399_BYTES_PER_FUSE;
-	addr_offset = offset % RK3399_BYTES_PER_FUSE;
-	addr_end = DIV_ROUND_UP(offset + size, RK3399_BYTES_PER_FUSE);
-
-	/* cap to the size of the efuse block */
-	if (addr_end > RK3399_NFUSES)
-		addr_end = RK3399_NFUSES;
-
+	/* Switch to array read mode */
 	writel(RK3399_LOAD | RK3399_PGENB | RK3399_STROBSFTSEL | RK3399_RSB,
-	       &efuse->ctrl);
+	       efuse->base + EFUSE_CTRL);
 	udelay(1);
-	for (addr = addr_start; addr < addr_end; addr++) {
-		setbits_le32(&efuse->ctrl,
-			     RK3399_STROBE | (addr << RK3399_A_SHIFT));
+
+	while (size--) {
+		setbits_le32(efuse->base + EFUSE_CTRL,
+			     RK3399_STROBE | RK3399_ADDR(offset++));
 		udelay(1);
-		out_value = readl(&efuse->dout);
-		clrbits_le32(&efuse->ctrl, RK3399_STROBE);
+		*buffer++ = readl(efuse->base + EFUSE_DOUT);
+		clrbits_le32(efuse->base + EFUSE_CTRL, RK3399_STROBE);
 		udelay(1);
-
-		memcpy(&bytes[i], &out_value, RK3399_BYTES_PER_FUSE);
-		i += RK3399_BYTES_PER_FUSE;
 	}
 
-	/* Switch to standby mode */
-	writel(RK3399_PD | RK3399_CSB, &efuse->ctrl);
-
-	memcpy(buf, bytes + addr_offset, size);
+	/* Switch to power-down mode */
+	writel(RK3399_PD | RK3399_CSB, efuse->base + EFUSE_CTRL);
 
 	return 0;
 }
@@ -130,7 +101,36 @@
 static int rockchip_efuse_read(struct udevice *dev, int offset,
 			       void *buf, int size)
 {
-	return rockchip_rk3399_efuse_read(dev, offset, buf, size);
+	const struct rockchip_efuse_data *data =
+		(void *)dev_get_driver_data(dev);
+	u32 block_start, block_end, block_offset, blocks;
+	u8 *buffer;
+	int ret;
+
+	if (offset < 0 || !buf || size <= 0 || offset + size > data->size)
+		return -EINVAL;
+
+	if (!data->read)
+		return -ENOSYS;
+
+	if (data->block_size <= 1)
+		return data->read(dev, offset, buf, size);
+
+	block_start = offset / data->block_size;
+	block_offset = offset % data->block_size;
+	block_end = DIV_ROUND_UP(offset + size, data->block_size);
+	blocks = block_end - block_start;
+
+	buffer = calloc(blocks, data->block_size);
+	if (!buffer)
+		return -ENOMEM;
+
+	ret = data->read(dev, block_start, buffer, blocks);
+	if (!ret)
+		memcpy(buf, buffer + block_offset, size);
+
+	free(buffer);
+	return ret;
 }
 
 static const struct misc_ops rockchip_efuse_ops = {
@@ -142,11 +142,21 @@
 	struct rockchip_efuse_plat *plat = dev_get_plat(dev);
 
 	plat->base = dev_read_addr_ptr(dev);
+
 	return 0;
 }
 
+static const struct rockchip_efuse_data rk3399_data = {
+	.read = rockchip_rk3399_efuse_read,
+	.size = 0x80,
+	.block_size = 4,
+};
+
 static const struct udevice_id rockchip_efuse_ids[] = {
-	{ .compatible = "rockchip,rk3399-efuse" },
+	{
+		.compatible = "rockchip,rk3399-efuse",
+		.data = (ulong)&rk3399_data,
+	},
 	{}
 };
 
@@ -155,6 +165,6 @@
 	.id = UCLASS_MISC,
 	.of_match = rockchip_efuse_ids,
 	.of_to_plat = rockchip_efuse_of_to_plat,
-	.plat_auto	= sizeof(struct rockchip_efuse_plat),
+	.plat_auto = sizeof(struct rockchip_efuse_plat),
 	.ops = &rockchip_efuse_ops,
 };