reset: rzg2l-usbphy-ctrl: Add new driver

Add a new driver to control the USB 2.0 PHY reset controller on the
Renesas RZ/G2L and related SoCs.

Signed-off-by: Paul Barker <paul.barker.ct@bp.renesas.com>
Reviewed-by: Marek Vasut <marek.vasut+renesas@mailbox.org>
diff --git a/drivers/reset/reset-rzg2l-usbphy-ctrl.c b/drivers/reset/reset-rzg2l-usbphy-ctrl.c
new file mode 100644
index 0000000..afd647e
--- /dev/null
+++ b/drivers/reset/reset-rzg2l-usbphy-ctrl.c
@@ -0,0 +1,113 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2024 Renesas Electronics Corporation
+ */
+
+#include <asm/io.h>
+#include <dm.h>
+#include <dm/device_compat.h>
+#include <dm/lists.h>
+#include <renesas/rzg2l-usbphy.h>
+#include <reset-uclass.h>
+#include <reset.h>
+
+#define RESET			0x000
+
+#define RESET_SEL_PLLRESET	BIT(12)
+#define RESET_PLLRESET		BIT(8)
+
+#define RESET_SEL_P2RESET	BIT(5)
+#define RESET_SEL_P1RESET	BIT(4)
+#define RESET_PHYRST_2		BIT(1)
+#define RESET_PHYRST_1		BIT(0)
+
+#define PHY_RESET_MASK          (RESET_PHYRST_1 | RESET_PHYRST_2)
+
+#define NUM_PORTS		2
+
+static int rzg2l_usbphy_ctrl_assert(struct reset_ctl *reset_ctl)
+{
+	struct rzg2l_usbphy_ctrl_priv *priv = dev_get_priv(reset_ctl->dev);
+	u32 val;
+
+	val = readl(priv->regs + RESET);
+	val |= reset_ctl->id ? RESET_PHYRST_2 : RESET_PHYRST_1;
+
+	/* If both ports are in reset, we can also place the PLL into reset. */
+	if ((val & PHY_RESET_MASK) == PHY_RESET_MASK)
+		val |= RESET_PLLRESET;
+
+	writel(val, priv->regs + RESET);
+	return 0;
+}
+
+static int rzg2l_usbphy_ctrl_deassert(struct reset_ctl *reset_ctl)
+{
+	struct rzg2l_usbphy_ctrl_priv *priv = dev_get_priv(reset_ctl->dev);
+	u32 val = reset_ctl->id ? RESET_PHYRST_2 : RESET_PHYRST_1;
+
+	/* If either port is out of reset, the PLL must also be out of reset. */
+	val |= RESET_PLLRESET;
+
+	clrbits_le32(priv->regs + RESET, val);
+	return 0;
+}
+
+static int rzg2l_usbphy_ctrl_of_xlate(struct reset_ctl *reset_ctl,
+				      struct ofnode_phandle_args *args)
+{
+	if (args->args[0] >= NUM_PORTS)
+		return -EINVAL;
+
+	reset_ctl->id = args->args[0];
+	return 0;
+}
+
+struct reset_ops rzg2l_usbphy_ctrl_ops = {
+	.rst_assert = rzg2l_usbphy_ctrl_assert,
+	.rst_deassert = rzg2l_usbphy_ctrl_deassert,
+	.of_xlate = rzg2l_usbphy_ctrl_of_xlate,
+};
+
+static int rzg2l_usbphy_ctrl_probe(struct udevice *dev)
+{
+	struct rzg2l_usbphy_ctrl_priv *priv = dev_get_priv(dev);
+	struct reset_ctl rst;
+	int ret;
+
+	priv->regs = dev_read_addr(dev);
+
+	ret = reset_get_by_index(dev, 0, &rst);
+	if (ret < 0) {
+		dev_err(dev, "failed to get reset line: %d\n", ret);
+		return ret;
+	}
+
+	ret = reset_deassert(&rst);
+	if (ret < 0) {
+		dev_err(dev, "failed to de-assert reset line: %d\n", ret);
+		return ret;
+	}
+
+	/* put pll and phy into reset state */
+	setbits_le32(priv->regs + RESET,
+		     RESET_SEL_PLLRESET | RESET_PLLRESET |
+		     RESET_SEL_P1RESET | RESET_PHYRST_1 |
+		     RESET_SEL_P2RESET | RESET_PHYRST_2);
+
+	return 0;
+}
+
+static const struct udevice_id rzg2l_usbphy_ctrl_ids[] = {
+	{ .compatible = "renesas,rzg2l-usbphy-ctrl", },
+	{ /* sentinel */ }
+};
+
+U_BOOT_DRIVER(rzg2l_usbphy_ctrl) = {
+	.name           = "rzg2l_usbphy_ctrl",
+	.id             = UCLASS_RESET,
+	.of_match       = rzg2l_usbphy_ctrl_ids,
+	.probe          = rzg2l_usbphy_ctrl_probe,
+	.ops            = &rzg2l_usbphy_ctrl_ops,
+	.priv_auto      = sizeof(struct rzg2l_usbphy_ctrl_priv),
+};