]> git.dujemihanovic.xyz Git - u-boot.git/commitdiff
phy: rcar: Split init and set_mode operations
authorMarek Vasut <marek.vasut+renesas@mailbox.org>
Sun, 8 Sep 2024 21:09:04 +0000 (23:09 +0200)
committerMarek Vasut <marex@denx.de>
Mon, 9 Sep 2024 15:18:04 +0000 (17:18 +0200)
The current init operation also sets the PHY into USB host mode.
Split the mode configuration into set_mode callback instead and
implement support for device and OTG modes as well.

The OTG mode performs auto-detection and selects either host or
device mode. In case the OTG mode is configured, submode field
can be used to select full PHY (re)initialization or only mode
auto-detection. The full (re)initialization is only necessary
once, on start up.

Since the OTG mode may enable IRQ generation in the PHY, disable
that IRQ generation in the exit callback again.

Reviewed-by: Mattijs Korpershoek <mkorpershoek@baylibre.com>
Signed-off-by: Marek Vasut <marek.vasut+renesas@mailbox.org>
drivers/phy/phy-rcar-gen3.c

index 7c292cae0e2fa00516ac5c1c23e45ba9cbce9baf..b278f995f373e3edb4e22f3b6a454b0ca858c319 100644 (file)
@@ -8,6 +8,7 @@
 #include <clk.h>
 #include <div64.h>
 #include <dm.h>
+#include <dm/device_compat.h>
 #include <fdtdec.h>
 #include <generic-phy.h>
 #include <malloc.h>
 #define USB2_LINECTRL1         0x610
 #define USB2_ADPCTRL           0x630
 
+/* INT_ENABLE */
+#define USB2_INT_ENABLE_UCOM_INTEN     BIT(3)
+#define USB2_INT_ENABLE_USBH_INTB_EN   BIT(2)
+#define USB2_INT_ENABLE_USBH_INTA_EN   BIT(1)
+
 /* USBCTR */
-#define USB2_USBCTR_PLL_RST    BIT(1)
+#define USB2_USBCTR_PLL_RST            BIT(1)
 
 /* SPD_RSM_TIMSET */
 #define USB2_SPD_RSM_TIMSET_INIT       0x014e029b
 /* COMMCTRL */
 #define USB2_COMMCTRL_OTG_PERI         BIT(31) /* 1 = Peripheral mode */
 
+/* OBINTSTA and OBINTEN */
+#define USB2_OBINT_SESSVLDCHG          BIT(12)
+#define USB2_OBINT_IDDIGCHG            BIT(11)
+
+/* VBCTRL */
+#define USB2_VBCTRL_DRVVBUSSEL         BIT(8)
+
 /* LINECTRL1 */
+#define USB2_LINECTRL1_DPRPD_EN                BIT(19)
 #define USB2_LINECTRL1_DP_RPD          BIT(18)
+#define USB2_LINECTRL1_DMRPD_EN                BIT(17)
 #define USB2_LINECTRL1_DM_RPD          BIT(16)
 
 /* ADPCTRL */
+#define USB2_ADPCTRL_OTGSESSVLD                BIT(20)
+#define USB2_ADPCTRL_IDDIG             BIT(19)
+#define USB2_ADPCTRL_IDPULLUP          BIT(5)  /* 1 = ID sampling is enabled */
 #define USB2_ADPCTRL_DRVVBUS           BIT(4)
 
 struct rcar_gen3_phy {
@@ -65,12 +83,14 @@ static int rcar_gen3_phy_phy_init(struct phy *phy)
        writel(USB2_SPD_RSM_TIMSET_INIT, priv->regs + USB2_SPD_RSM_TIMSET);
        writel(USB2_OC_TIMSET_INIT, priv->regs + USB2_OC_TIMSET);
 
-       setbits_le32(priv->regs + USB2_LINECTRL1,
-                    USB2_LINECTRL1_DP_RPD | USB2_LINECTRL1_DM_RPD);
+       return 0;
+}
 
-       clrbits_le32(priv->regs + USB2_COMMCTRL, USB2_COMMCTRL_OTG_PERI);
+static int rcar_gen3_phy_phy_exit(struct phy *phy)
+{
+       struct rcar_gen3_phy *priv = dev_get_priv(phy->dev);
 
-       setbits_le32(priv->regs + USB2_ADPCTRL, USB2_ADPCTRL_DRVVBUS);
+       writel(0, priv->regs + USB2_INT_ENABLE);
 
        return 0;
 }
@@ -102,10 +122,70 @@ static int rcar_gen3_phy_phy_power_off(struct phy *phy)
        return regulator_set_enable(priv->vbus_supply, false);
 }
 
+static int rcar_gen3_phy_phy_set_mode(struct phy *phy, enum phy_mode mode,
+                                     int submode)
+{
+       const u32 adpdevmask = USB2_ADPCTRL_IDDIG | USB2_ADPCTRL_OTGSESSVLD;
+       struct rcar_gen3_phy *priv = dev_get_priv(phy->dev);
+       u32 adpctrl;
+
+       if (mode == PHY_MODE_USB_OTG) {
+               if (submode) {
+                       /* OTG submode is used as initialization indicator */
+                       writel(USB2_INT_ENABLE_UCOM_INTEN |
+                              USB2_INT_ENABLE_USBH_INTB_EN |
+                              USB2_INT_ENABLE_USBH_INTA_EN,
+                              priv->regs + USB2_INT_ENABLE);
+                       setbits_le32(priv->regs + USB2_VBCTRL,
+                                    USB2_VBCTRL_DRVVBUSSEL);
+                       writel(USB2_OBINT_SESSVLDCHG | USB2_OBINT_IDDIGCHG,
+                              priv->regs + USB2_OBINTSTA);
+                       setbits_le32(priv->regs + USB2_OBINTEN,
+                                    USB2_OBINT_SESSVLDCHG |
+                                    USB2_OBINT_IDDIGCHG);
+                       setbits_le32(priv->regs + USB2_ADPCTRL,
+                                    USB2_ADPCTRL_IDPULLUP);
+                       clrsetbits_le32(priv->regs + USB2_LINECTRL1,
+                                       USB2_LINECTRL1_DP_RPD |
+                                       USB2_LINECTRL1_DM_RPD |
+                                       USB2_LINECTRL1_DPRPD_EN |
+                                       USB2_LINECTRL1_DMRPD_EN,
+                                       USB2_LINECTRL1_DPRPD_EN |
+                                       USB2_LINECTRL1_DMRPD_EN);
+               }
+
+               adpctrl = readl(priv->regs + USB2_ADPCTRL);
+               if ((adpctrl & adpdevmask) == adpdevmask)
+                       mode = PHY_MODE_USB_DEVICE;
+               else
+                       mode = PHY_MODE_USB_HOST;
+       }
+
+       if (mode == PHY_MODE_USB_HOST) {
+               clrbits_le32(priv->regs + USB2_COMMCTRL, USB2_COMMCTRL_OTG_PERI);
+               setbits_le32(priv->regs + USB2_LINECTRL1,
+                            USB2_LINECTRL1_DP_RPD | USB2_LINECTRL1_DM_RPD);
+               setbits_le32(priv->regs + USB2_ADPCTRL, USB2_ADPCTRL_DRVVBUS);
+       } else if (mode == PHY_MODE_USB_DEVICE) {
+               setbits_le32(priv->regs + USB2_COMMCTRL, USB2_COMMCTRL_OTG_PERI);
+               clrsetbits_le32(priv->regs + USB2_LINECTRL1,
+                               USB2_LINECTRL1_DP_RPD | USB2_LINECTRL1_DM_RPD,
+                               USB2_LINECTRL1_DM_RPD);
+               clrbits_le32(priv->regs + USB2_ADPCTRL, USB2_ADPCTRL_DRVVBUS);
+       } else {
+               dev_err(phy->dev, "Unknown mode %d\n", mode);
+               return -EINVAL;
+       }
+
+       return 0;
+}
+
 static const struct phy_ops rcar_gen3_phy_phy_ops = {
        .init           = rcar_gen3_phy_phy_init,
+       .exit           = rcar_gen3_phy_phy_exit,
        .power_on       = rcar_gen3_phy_phy_power_on,
        .power_off      = rcar_gen3_phy_phy_power_off,
+       .set_mode       = rcar_gen3_phy_phy_set_mode,
 };
 
 static int rcar_gen3_phy_probe(struct udevice *dev)