]> git.ipfire.org Git - thirdparty/kernel/stable.git/commitdiff
phy: renesas: rcar-gen3-usb2: Add support to initialize the bus
authorClaudiu Beznea <claudiu.beznea.uj@bp.renesas.com>
Thu, 22 Aug 2024 15:27:55 +0000 (18:27 +0300)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Wed, 4 Jun 2025 12:41:51 +0000 (14:41 +0200)
[ Upstream commit 4eae16375357a2a7e8501be5469532f7636064b3 ]

The Renesas RZ/G3S need to initialize the USB BUS before transferring data
due to hardware limitation. As the register that need to be touched for
this is in the address space of the USB PHY, and the UBS PHY need to be
initialized before any other USB drivers handling data transfer, add
support to initialize the USB BUS.

As the USB PHY is probed before any other USB drivers that enables
clocks and de-assert the reset signals and the BUS initialization is done
in the probe phase, we need to add code to de-assert reset signal and
runtime resume the device (which enables its clocks) before accessing
the registers.

As the reset signals are not required by the USB PHY driver for the other
USB PHY hardware variants, the reset signals and runtime PM was handled
only in the function that initialize the USB BUS.

The PHY initialization was done right after runtime PM enable to have
all in place when the PHYs are registered.

Signed-off-by: Claudiu Beznea <claudiu.beznea.uj@bp.renesas.com>
Link: https://lore.kernel.org/r/20240822152801.602318-11-claudiu.beznea.uj@bp.renesas.com
Signed-off-by: Vinod Koul <vkoul@kernel.org>
Stable-dep-of: 9ce71e85b29e ("phy: renesas: rcar-gen3-usb2: Assert PLL reset on PHY power off")
Signed-off-by: Sasha Levin <sashal@kernel.org>
drivers/phy/renesas/phy-rcar-gen3-usb2.c

index aa578be2bcb6df3f446c582eba4c09b9405cd45a..d2bbf6c6ed5e09e4252a8e8f1274d0afa4c2eef8 100644 (file)
 #include <linux/platform_device.h>
 #include <linux/pm_runtime.h>
 #include <linux/regulator/consumer.h>
+#include <linux/reset.h>
 #include <linux/string.h>
 #include <linux/usb/of.h>
 #include <linux/workqueue.h>
 
 /******* USB2.0 Host registers (original offset is +0x200) *******/
 #define USB2_INT_ENABLE                0x000
+#define USB2_AHB_BUS_CTR       0x008
 #define USB2_USBCTR            0x00c
 #define USB2_SPD_RSM_TIMSET    0x10c
 #define USB2_OC_TIMSET         0x110
 #define USB2_INT_ENABLE_USBH_INTB_EN   BIT(2)  /* For EHCI */
 #define USB2_INT_ENABLE_USBH_INTA_EN   BIT(1)  /* For OHCI */
 
+/* AHB_BUS_CTR */
+#define USB2_AHB_BUS_CTR_MBL_MASK      GENMASK(1, 0)
+#define USB2_AHB_BUS_CTR_MBL_INCR4     2
+
 /* USBCTR */
 #define USB2_USBCTR_DIRPD      BIT(2)
 #define USB2_USBCTR_PLL_RST    BIT(1)
@@ -110,6 +116,7 @@ struct rcar_gen3_chan {
        struct extcon_dev *extcon;
        struct rcar_gen3_phy rphys[NUM_OF_PHYS];
        struct regulator *vbus;
+       struct reset_control *rstc;
        struct work_struct work;
        struct mutex lock;      /* protects rphys[...].powered */
        enum usb_dr_mode dr_mode;
@@ -124,6 +131,7 @@ struct rcar_gen3_chan {
 struct rcar_gen3_phy_drv_data {
        const struct phy_ops *phy_usb2_ops;
        bool no_adp_ctrl;
+       bool init_bus;
 };
 
 /*
@@ -645,6 +653,35 @@ static enum usb_dr_mode rcar_gen3_get_dr_mode(struct device_node *np)
        return candidate;
 }
 
+static int rcar_gen3_phy_usb2_init_bus(struct rcar_gen3_chan *channel)
+{
+       struct device *dev = channel->dev;
+       int ret;
+       u32 val;
+
+       channel->rstc = devm_reset_control_array_get_shared(dev);
+       if (IS_ERR(channel->rstc))
+               return PTR_ERR(channel->rstc);
+
+       ret = pm_runtime_resume_and_get(dev);
+       if (ret)
+               return ret;
+
+       ret = reset_control_deassert(channel->rstc);
+       if (ret)
+               goto rpm_put;
+
+       val = readl(channel->base + USB2_AHB_BUS_CTR);
+       val &= ~USB2_AHB_BUS_CTR_MBL_MASK;
+       val |= USB2_AHB_BUS_CTR_MBL_INCR4;
+       writel(val, channel->base + USB2_AHB_BUS_CTR);
+
+rpm_put:
+       pm_runtime_put(dev);
+
+       return ret;
+}
+
 static int rcar_gen3_phy_usb2_probe(struct platform_device *pdev)
 {
        const struct rcar_gen3_phy_drv_data *phy_data;
@@ -698,6 +735,15 @@ static int rcar_gen3_phy_usb2_probe(struct platform_device *pdev)
                goto error;
        }
 
+       platform_set_drvdata(pdev, channel);
+       channel->dev = dev;
+
+       if (phy_data->init_bus) {
+               ret = rcar_gen3_phy_usb2_init_bus(channel);
+               if (ret)
+                       goto error;
+       }
+
        channel->soc_no_adp_ctrl = phy_data->no_adp_ctrl;
        if (phy_data->no_adp_ctrl)
                channel->obint_enable_bits = USB2_OBINT_IDCHG_EN;
@@ -725,9 +771,6 @@ static int rcar_gen3_phy_usb2_probe(struct platform_device *pdev)
                channel->vbus = NULL;
        }
 
-       platform_set_drvdata(pdev, channel);
-       channel->dev = dev;
-
        provider = devm_of_phy_provider_register(dev, rcar_gen3_phy_usb2_xlate);
        if (IS_ERR(provider)) {
                dev_err(dev, "Failed to register PHY provider\n");
@@ -754,6 +797,7 @@ static void rcar_gen3_phy_usb2_remove(struct platform_device *pdev)
        if (channel->is_otg_channel)
                device_remove_file(&pdev->dev, &dev_attr_role);
 
+       reset_control_assert(channel->rstc);
        pm_runtime_disable(&pdev->dev);
 };