]> git.ipfire.org Git - thirdparty/kernel/linux.git/commitdiff
phy: renesas: rcar-gen3-usb2: Fix role detection on unbind/bind
authorClaudiu Beznea <claudiu.beznea.uj@bp.renesas.com>
Wed, 7 May 2025 12:50:28 +0000 (15:50 +0300)
committerVinod Koul <vkoul@kernel.org>
Wed, 14 May 2025 11:25:09 +0000 (12:25 +0100)
It has been observed on the Renesas RZ/G3S SoC that unbinding and binding
the PHY driver leads to role autodetection failures. This issue occurs when
PHY 3 is the first initialized PHY. PHY 3 does not have an interrupt
associated with the USB2_INT_ENABLE register (as
rcar_gen3_int_enable[3] = 0). As a result, rcar_gen3_init_otg() is called
to initialize OTG without enabling PHY interrupts.

To resolve this, add rcar_gen3_is_any_otg_rphy_initialized() and call it in
role_store(), role_show(), and rcar_gen3_init_otg(). At the same time,
rcar_gen3_init_otg() is only called when initialization for a PHY with
interrupt bits is in progress. As a result, the
struct rcar_gen3_phy::otg_initialized is no longer needed.

Fixes: 549b6b55b005 ("phy: renesas: rcar-gen3-usb2: enable/disable independent irqs")
Cc: stable@vger.kernel.org
Reviewed-by: Yoshihiro Shimoda <yoshihiro.shimoda.uh@renesas.com>
Tested-by: Yoshihiro Shimoda <yoshihiro.shimoda.uh@renesas.com>
Reviewed-by: Lad Prabhakar <prabhakar.mahadev-lad.rj@bp.renesas.com>
Signed-off-by: Claudiu Beznea <claudiu.beznea.uj@bp.renesas.com>
Link: https://lore.kernel.org/r/20250507125032.565017-2-claudiu.beznea.uj@bp.renesas.com
Signed-off-by: Vinod Koul <vkoul@kernel.org>
drivers/phy/renesas/phy-rcar-gen3-usb2.c

index 775f4f973a6cc25886e43cd3c6135bc0082608be..46afba2fe0dc69430185a924b121aa5afaff0941 100644 (file)
@@ -107,7 +107,6 @@ struct rcar_gen3_phy {
        struct rcar_gen3_chan *ch;
        u32 int_enable_bits;
        bool initialized;
-       bool otg_initialized;
        bool powered;
 };
 
@@ -320,16 +319,15 @@ static bool rcar_gen3_is_any_rphy_initialized(struct rcar_gen3_chan *ch)
        return false;
 }
 
-static bool rcar_gen3_needs_init_otg(struct rcar_gen3_chan *ch)
+static bool rcar_gen3_is_any_otg_rphy_initialized(struct rcar_gen3_chan *ch)
 {
-       int i;
-
-       for (i = 0; i < NUM_OF_PHYS; i++) {
-               if (ch->rphys[i].otg_initialized)
-                       return false;
+       for (enum rcar_gen3_phy_index i = PHY_INDEX_BOTH_HC; i <= PHY_INDEX_EHCI;
+            i++) {
+               if (ch->rphys[i].initialized)
+                       return true;
        }
 
-       return true;
+       return false;
 }
 
 static bool rcar_gen3_are_all_rphys_power_off(struct rcar_gen3_chan *ch)
@@ -351,7 +349,7 @@ static ssize_t role_store(struct device *dev, struct device_attribute *attr,
        bool is_b_device;
        enum phy_mode cur_mode, new_mode;
 
-       if (!ch->is_otg_channel || !rcar_gen3_is_any_rphy_initialized(ch))
+       if (!ch->is_otg_channel || !rcar_gen3_is_any_otg_rphy_initialized(ch))
                return -EIO;
 
        if (sysfs_streq(buf, "host"))
@@ -389,7 +387,7 @@ static ssize_t role_show(struct device *dev, struct device_attribute *attr,
 {
        struct rcar_gen3_chan *ch = dev_get_drvdata(dev);
 
-       if (!ch->is_otg_channel || !rcar_gen3_is_any_rphy_initialized(ch))
+       if (!ch->is_otg_channel || !rcar_gen3_is_any_otg_rphy_initialized(ch))
                return -EIO;
 
        return sprintf(buf, "%s\n", rcar_gen3_is_host(ch) ? "host" :
@@ -402,6 +400,9 @@ static void rcar_gen3_init_otg(struct rcar_gen3_chan *ch)
        void __iomem *usb2_base = ch->base;
        u32 val;
 
+       if (!ch->is_otg_channel || rcar_gen3_is_any_otg_rphy_initialized(ch))
+               return;
+
        /* Should not use functions of read-modify-write a register */
        val = readl(usb2_base + USB2_LINECTRL1);
        val = (val & ~USB2_LINECTRL1_DP_RPD) | USB2_LINECTRL1_DPRPD_EN |
@@ -465,12 +466,9 @@ static int rcar_gen3_phy_usb2_init(struct phy *p)
        writel(USB2_SPD_RSM_TIMSET_INIT, usb2_base + USB2_SPD_RSM_TIMSET);
        writel(USB2_OC_TIMSET_INIT, usb2_base + USB2_OC_TIMSET);
 
-       /* Initialize otg part */
-       if (channel->is_otg_channel) {
-               if (rcar_gen3_needs_init_otg(channel))
-                       rcar_gen3_init_otg(channel);
-               rphy->otg_initialized = true;
-       }
+       /* Initialize otg part (only if we initialize a PHY with IRQs). */
+       if (rphy->int_enable_bits)
+               rcar_gen3_init_otg(channel);
 
        rphy->initialized = true;
 
@@ -486,9 +484,6 @@ static int rcar_gen3_phy_usb2_exit(struct phy *p)
 
        rphy->initialized = false;
 
-       if (channel->is_otg_channel)
-               rphy->otg_initialized = false;
-
        val = readl(usb2_base + USB2_INT_ENABLE);
        val &= ~rphy->int_enable_bits;
        if (!rcar_gen3_is_any_rphy_initialized(channel))