reset: rzg2l-usbphy-ctrl: Add new driver
authorPaul Barker <paul.barker.ct@bp.renesas.com>
Tue, 11 Mar 2025 20:57:43 +0000 (20:57 +0000)
committerMarek Vasut <marek.vasut+renesas@mailbox.org>
Wed, 19 Mar 2025 02:36:19 +0000 (03:36 +0100)
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>
drivers/reset/Kconfig
drivers/reset/Makefile
drivers/reset/reset-rzg2l-usbphy-ctrl.c [new file with mode: 0644]
include/renesas/rzg2l-usbphy.h [new file with mode: 0644]

index fe5c121..80e83a4 100644 (file)
@@ -235,4 +235,13 @@ config RESET_AT91
          This enables the Reset Controller driver support for Microchip/Atmel
          SoCs. Mainly used to expose assert/deassert methods to other drivers
          that require it.
+
+config RESET_RZG2L_USBPHY_CTRL
+       bool "Enable support for Renesas RZ/G2L USB 2.0 PHY control"
+       depends on DM_RESET
+       help
+         Enable support for controlling USB 2.0 PHY resets on the Renesas
+         RZ/G2L SoC. This is required for USB 2.0 functionality to work on this
+         SoC.
+
 endmenu
index d99a78c..9d438a7 100644 (file)
@@ -33,3 +33,4 @@ obj-$(CONFIG_RESET_ZYNQMP) += reset-zynqmp.o
 obj-$(CONFIG_RESET_DRA7) += reset-dra7.o
 obj-$(CONFIG_RESET_AT91) += reset-at91.o
 obj-$(CONFIG_$(PHASE_)RESET_JH7110) += reset-jh7110.o
+obj-$(CONFIG_RESET_RZG2L_USBPHY_CTRL) += reset-rzg2l-usbphy-ctrl.o
diff --git a/drivers/reset/reset-rzg2l-usbphy-ctrl.c b/drivers/reset/reset-rzg2l-usbphy-ctrl.c
new file mode 100644 (file)
index 0000000..afd647e
--- /dev/null
@@ -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),
+};
diff --git a/include/renesas/rzg2l-usbphy.h b/include/renesas/rzg2l-usbphy.h
new file mode 100644 (file)
index 0000000..1a46b58
--- /dev/null
@@ -0,0 +1,17 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * RZ/G2L USB PHY common definitions
+ *
+ * Copyright (C) 2021-2023 Renesas Electronics Corp.
+ */
+
+#ifndef RENESAS_RZG2L_USBPHY_H
+#define RENESAS_RZG2L_USBPHY_H
+
+#include <fdtdec.h>
+
+struct rzg2l_usbphy_ctrl_priv {
+       fdt_addr_t regs;
+};
+
+#endif /* RENESAS_RZG2L_USBPHY_H */