phy: sun4i-usb: Add H616 USB PHY quirk support
authorAndre Przywara <andre.przywara@arm.com>
Sun, 11 Jun 2023 23:32:39 +0000 (00:32 +0100)
committerAndre Przywara <andre.przywara@arm.com>
Thu, 20 Jul 2023 23:28:13 +0000 (00:28 +0100)
The H616 USB PHY is some kind of special snowflake: Only port2 works out
of the box, but all other ports need some help from this port2 to work
correctly: The CLK_BUS_PHY2 and RST_USB_PHY2 clock and reset need to be
enabled, and the SIDDQ bit in the PMU PHY control register needs to be
cleared. For this register to be accessible, CLK_BUS_ECHI2 needs to be
ungated. Don't ask ....

Follow the respective Linux patch (b45c6d80325b) and add a quirk bit,
triggering the special sequence as outlined above, for PHYs other than
PHY2: ungate this one special clock, and clear the SIDDQ bit. We also
pick the clock and reset from PHY2 and enable them as well.

Signed-off-by: Andre Przywara <andre.przywara@arm.com>
Reviewed-by: Jernej Skrabec <jernej.skrabec@gmail.com>
drivers/phy/allwinner/phy-sun4i-usb.c

index 88c1a3d..c81811a 100644 (file)
@@ -82,6 +82,7 @@ struct sun4i_usb_phy_cfg {
        bool dedicated_clocks;
        bool phy0_dual_route;
        bool siddq_in_base;
+       bool needs_phy2_siddq;
        int missing_phys;
 };
 
@@ -118,6 +119,7 @@ struct sun4i_usb_phy_plat {
        struct gpio_desc gpio_vbus_det;
        struct gpio_desc gpio_id_det;
        struct clk clocks;
+       struct clk clk2;
        struct reset_ctl resets;
        int id;
 };
@@ -272,6 +274,41 @@ static int sun4i_usb_phy_init(struct phy *phy)
                return ret;
        }
 
+       /* Some PHYs on some SoCs (the H616) need the help of PHY2 to work. */
+       if (data->cfg->needs_phy2_siddq && phy->id != 2) {
+               struct sun4i_usb_phy_plat *phy2 = &data->usb_phy[2];
+
+               ret = clk_enable(&phy2->clocks);
+               if (ret) {
+                       dev_err(phy->dev, "failed to enable aux clock\n");
+                       return ret;
+               }
+
+               ret = reset_deassert(&phy2->resets);
+               if (ret) {
+                       dev_err(phy->dev, "failed to deassert aux reset\n");
+                       return ret;
+               }
+
+               /*
+                * This extra clock is just needed to access the
+                * REG_HCI_PHY_CTL PMU register for PHY2.
+                */
+               ret = clk_enable(&phy2->clk2);
+               if (ret) {
+                       dev_err(phy->dev, "failed to enable PHY2 clock\n");
+                       return ret;
+               }
+
+               if (phy2->pmu && data->cfg->hci_phy_ctl_clear) {
+                       val = readl(phy2->pmu + REG_HCI_PHY_CTL);
+                       val &= ~data->cfg->hci_phy_ctl_clear;
+                       writel(val, phy2->pmu + REG_HCI_PHY_CTL);
+               }
+
+               clk_disable(&phy2->clk2);
+       }
+
        if (usb_phy->pmu && data->cfg->hci_phy_ctl_clear) {
                val = readl(usb_phy->pmu + REG_HCI_PHY_CTL);
                val &= ~data->cfg->hci_phy_ctl_clear;
@@ -500,6 +537,15 @@ static int sun4i_usb_phy_probe(struct udevice *dev)
                        return ret;
                }
 
+               /* Helper clock from PHY2 for the H616 PHY quirk */
+               snprintf(name, sizeof(name), "pmu%d_clk", i);
+               ret = clk_get_by_name_optional(dev, name, &phy->clk2);
+               if (ret) {
+                       dev_err(dev, "failed to get pmu%d_clk clock phandle\n",
+                               i);
+                       return ret;
+               }
+
                snprintf(name, sizeof(name), "usb%d_reset", i);
                ret = reset_get_by_name(dev, name, &phy->resets);
                if (ret) {