ram: renesas: Add Renesas R-Car Gen4 DBSC5 driver

Add Renesas R-Car Gen4 DBSC5 DRAM controller driver. This driver is currently
capable of bringing LPDDR5 DRAM on Renesas R-Car V4H Whitehawk board. Further
boards can be supported by supplying board specific DRAM configuration data
via dbsc5_get_board_data(). Support for R-Car V4M is not implemented, however
the driver is already mostly prepared to support this SoC.

Signed-off-by: Marek Vasut <marek.vasut+renesas@mailbox.org>
diff --git a/drivers/ram/renesas/dbsc5/rtvram.c b/drivers/ram/renesas/dbsc5/rtvram.c
new file mode 100644
index 0000000..6c14928
--- /dev/null
+++ b/drivers/ram/renesas/dbsc5/rtvram.c
@@ -0,0 +1,93 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2024 Renesas Electronics Corp.
+ */
+
+#include <asm/io.h>
+#include <dm.h>
+#include <errno.h>
+#include <ram.h>
+#include <linux/sizes.h>
+
+/* RT-VRAM register base address */
+#define RTVRAM_VBUF_CFG			0x6504
+#define RTVRAM_VBUF_CFG_CACHE_MODE_8WAY	(1 << 8)
+#define RTVRAM_VBUF_CFG_VBUF_SIZE_28M	(6 << 0)
+#define RTVRAM_EXT_MODE			0x8500
+#define RTVRAM_EXT_MODE_EXT		BIT(0)
+#define RTVRAM_VBUF_BADDR		0xC580
+
+#define RTVRAM_VBUF_NUM			7
+
+#define SDRAM_40BIT_ADDR_TOP		0x0400000000ULL
+#define RTVRAM_VBUF_AREA_SIZE		SZ_4M
+
+struct renesas_dbsc5_rtvram_priv {
+	void __iomem		*regs;
+};
+
+static int renesas_dbsc5_rtvram_probe(struct udevice *dev)
+{
+	struct renesas_dbsc5_rtvram_priv *priv = dev_get_priv(dev);
+	u64 addr;
+	int i;
+
+	/* Set each 4MB from the top of SDRAM as the buffer area of RT-VRAM. */
+	for (i = 0; i < RTVRAM_VBUF_NUM; i++) {
+		addr = (SDRAM_40BIT_ADDR_TOP + (RTVRAM_VBUF_AREA_SIZE * i)) >> 16;
+		writel(lower_32_bits(addr), priv->regs + (RTVRAM_VBUF_BADDR + (4 * i)));
+	}
+
+	/* Cache Mode: 8-way, VBF size: 28M */
+	setbits_le32(priv->regs + RTVRAM_VBUF_CFG,
+		     RTVRAM_VBUF_CFG_CACHE_MODE_8WAY | RTVRAM_VBUF_CFG_VBUF_SIZE_28M);
+
+	/* Change from Compatible Mode to Extended Mode */
+	writel(RTVRAM_EXT_MODE_EXT, priv->regs + RTVRAM_EXT_MODE);
+
+	dsb();
+
+	return 0;
+}
+
+static int renesas_dbsc5_rtvram_of_to_plat(struct udevice *dev)
+{
+	struct renesas_dbsc5_rtvram_priv *priv = dev_get_priv(dev);
+
+	priv->regs = dev_read_addr_ptr(dev);
+	if (!priv->regs)
+		return -EINVAL;
+
+	return 0;
+}
+
+static int renesas_dbsc5_rtvram_get_info(struct udevice *dev,
+					 struct ram_info *info)
+{
+	struct renesas_dbsc5_rtvram_priv *priv = dev_get_priv(dev);
+
+	info->base = (phys_addr_t)priv->regs;
+	info->size = 28 * SZ_1M;
+
+	return 0;
+}
+
+static const struct ram_ops renesas_dbsc5_rtvram_ops = {
+	.get_info = renesas_dbsc5_rtvram_get_info,
+};
+
+static const struct udevice_id renesas_dbsc5_rtvram_ids[] = {
+	{ .compatible = "renesas,r8a779g0-rtvram" },
+	{ .compatible = "renesas,r8a779h0-rtvram" },
+	{ /* sentinel */ }
+};
+
+U_BOOT_DRIVER(renesas_dbsc5_rtvram) = {
+	.name		= "rtvram",
+	.id		= UCLASS_RAM,
+	.of_match	= renesas_dbsc5_rtvram_ids,
+	.of_to_plat	= renesas_dbsc5_rtvram_of_to_plat,
+	.ops		= &renesas_dbsc5_rtvram_ops,
+	.probe		= renesas_dbsc5_rtvram_probe,
+	.priv_auto	= sizeof(struct renesas_dbsc5_rtvram_priv),
+};