rockchip-inno-usb
authorVagrant Cascadian <vagrant@debian.org>
Wed, 26 Jan 2022 19:58:27 +0000 (19:58 +0000)
committerVagrant Cascadian <vagrant@debian.org>
Wed, 26 Jan 2022 19:58:27 +0000 (19:58 +0000)
Downloaded from:
https://patchwork.ozlabs.org/project/uboot/patch/20210406151059.1187379-1-icenowy@aosc.io

Downloaded from:
https://patchwork.ozlabs.org/project/uboot/patch/20210406151059.1187379-1-icenowy@aosc.io

From: Icenowy Zheng <icenowy@aosc.io>
To: Simon Glass <sjg@chromium.org>, Kever Yang <kever.yang@rock-chips.com>,
 Frank Wang <frank.wang@rock-chips.com>,
 Jagan Teki <jagan@amarulasolutions.com>
Cc: u-boot@lists.denx.de,
Icenowy Zheng <icenowy@aosc.io>
Subject: [PATCH] phy: rockchip: inno-usb2: fix hang when multiple controllers
 exit
Date: Tue,  6 Apr 2021 23:10:59 +0800
Message-Id: <20210406151059.1187379-1-icenowy@aosc.io>

The OHCI and EHCI controllers are both bound to the same PHY. They will
both do init and power_on operations when the controller is brought up
and both do power_off and exit when the controller is stopped. However,
the PHY uclass of U-Boot is not as sane as we thought -- they won't
maintain a status mark for PHYs, and thus the functions of the PHYs
could be called for multiple times. Calling init/power_on for multiple
times have no severe problems, however calling power_off/exit for
multiple times have a problem -- the first exit call will stop the PHY
clock, and power_off/exit calls after it still trying to write to PHY
registers. The write operation to PHY registers will fail because clock
is already stopped.

Adapt the count mechanism from phy-sun4i-usb to both init/exit and
power_on/power_off functions to phy-rockchip-inno-usb2 to fix this
problem. With this stopping USB controllers (manually or before booting
a kernel) will work.

Signed-off-by: Icenowy Zheng <icenowy@aosc.io>
Fixes: ac97a9ece14e ("phy: rockchip: Add Rockchip USB2PHY driver")
Tested-by: Peter Robinson <pbrobinson@gmail.com>
Gbp-Pq: Topic rockchip
Gbp-Pq: Name rockchip-inno-usb.patch

drivers/phy/rockchip/phy-rockchip-inno-usb2.c

index 62b8ba3a4a8924de32c1c069fd78566f0f588107..be9cc99d9046e6e0882bd3f48f5952a9ba100fe2 100644 (file)
@@ -62,6 +62,8 @@ struct rockchip_usb2phy {
        void *reg_base;
        struct clk phyclk;
        const struct rockchip_usb2phy_cfg *phy_cfg;
+       int init_count;
+       int power_on_count;
 };
 
 static inline int property_enable(void *reg_base,
@@ -92,6 +94,10 @@ static int rockchip_usb2phy_power_on(struct phy *phy)
        struct rockchip_usb2phy *priv = dev_get_priv(parent);
        const struct rockchip_usb2phy_port_cfg *port_cfg = us2phy_get_port(phy);
 
+       priv->power_on_count++;
+       if (priv->power_on_count != 1)
+               return 0;
+
        property_enable(priv->reg_base, &port_cfg->phy_sus, false);
 
        /* waiting for the utmi_clk to become stable */
@@ -106,6 +112,10 @@ static int rockchip_usb2phy_power_off(struct phy *phy)
        struct rockchip_usb2phy *priv = dev_get_priv(parent);
        const struct rockchip_usb2phy_port_cfg *port_cfg = us2phy_get_port(phy);
 
+       priv->power_on_count--;
+       if (priv->power_on_count != 0)
+               return 0;
+
        property_enable(priv->reg_base, &port_cfg->phy_sus, true);
 
        return 0;
@@ -118,6 +128,10 @@ static int rockchip_usb2phy_init(struct phy *phy)
        const struct rockchip_usb2phy_port_cfg *port_cfg = us2phy_get_port(phy);
        int ret;
 
+       priv->init_count++;
+       if (priv->init_count != 1)
+               return 0;
+
        ret = clk_enable(&priv->phyclk);
        if (ret) {
                dev_err(phy->dev, "failed to enable phyclk (ret=%d)\n", ret);
@@ -140,6 +154,10 @@ static int rockchip_usb2phy_exit(struct phy *phy)
        struct udevice *parent = dev_get_parent(phy->dev);
        struct rockchip_usb2phy *priv = dev_get_priv(parent);
 
+       priv->init_count--;
+       if (priv->init_count != 0)
+               return 0;
+
        clk_disable(&priv->phyclk);
 
        return 0;
@@ -212,6 +230,9 @@ static int rockchip_usb2phy_probe(struct udevice *dev)
                return ret;
        }
 
+       priv->power_on_count = 0;
+       priv->init_count = 0;
+
        return 0;
 }