diff options
Diffstat (limited to 'drivers/usb/dwc3/dwc3-meson-g12a.c')
-rw-r--r-- | drivers/usb/dwc3/dwc3-meson-g12a.c | 35 |
1 files changed, 35 insertions, 0 deletions
diff --git a/drivers/usb/dwc3/dwc3-meson-g12a.c b/drivers/usb/dwc3/dwc3-meson-g12a.c index e7a6d05f2a72..b4d728c4a051 100644 --- a/drivers/usb/dwc3/dwc3-meson-g12a.c +++ b/drivers/usb/dwc3/dwc3-meson-g12a.c @@ -131,6 +131,7 @@ struct dwc3_meson_g12a; struct dwc3_meson_g12a_drvdata { bool otg_switch_supported; + bool otg_phy_host_port_disable; struct clk_bulk_data *clks; int num_clks; const char **phy_names; @@ -155,6 +156,19 @@ static int dwc3_meson_g12a_set_phy_mode(struct dwc3_meson_g12a *priv, static int dwc3_meson_g12a_usb_init(struct dwc3_meson_g12a *priv); +/* + * For GXL and GXM SoCs: + * USB Phy muxing between the DWC2 Device controller and the DWC3 Host + * controller is buggy when switching from Device to Host when USB port + * is unpopulated, it causes the DWC3 to hard crash. + * When populated (including OTG switching with ID pin), the switch works + * like a charm like on the G12A platforms. + * In order to still switch from Host to Device on an USB Type-A port, + * an U2_PORT_DISABLE bit has been added to disconnect the DWC3 Host + * controller from the port, but when used the DWC3 controller must be + * reset to recover usage of the port. + */ + static struct dwc3_meson_g12a_drvdata g12a_drvdata = { .otg_switch_supported = true, .clks = meson_g12a_clocks, @@ -290,6 +304,14 @@ static void dwc3_meson_g12a_usb_otg_apply_mode(struct dwc3_meson_g12a *priv, enum phy_mode mode) { if (mode == PHY_MODE_USB_DEVICE) { + if (priv->otg_mode != USB_DR_MODE_OTG && + priv->drvdata->otg_phy_host_port_disable) + /* Isolate the OTG PHY port from the Host Controller */ + regmap_update_bits(priv->usb_glue_regmap, USB_R1, + USB_R1_U3H_HOST_U2_PORT_DISABLE_MASK, + FIELD_PREP(USB_R1_U3H_HOST_U2_PORT_DISABLE_MASK, + BIT(USB2_OTG_PHY))); + regmap_update_bits(priv->usb_glue_regmap, USB_R0, USB_R0_U2D_ACT, USB_R0_U2D_ACT); regmap_update_bits(priv->usb_glue_regmap, USB_R0, @@ -297,6 +319,12 @@ static void dwc3_meson_g12a_usb_otg_apply_mode(struct dwc3_meson_g12a *priv, regmap_update_bits(priv->usb_glue_regmap, USB_R4, USB_R4_P21_SLEEP_M0, USB_R4_P21_SLEEP_M0); } else { + if (priv->otg_mode != USB_DR_MODE_OTG && + priv->drvdata->otg_phy_host_port_disable) { + regmap_update_bits(priv->usb_glue_regmap, USB_R1, + USB_R1_U3H_HOST_U2_PORT_DISABLE_MASK, 0); + msleep(500); + } regmap_update_bits(priv->usb_glue_regmap, USB_R0, USB_R0_U2D_ACT, 0); regmap_update_bits(priv->usb_glue_regmap, USB_R4, @@ -430,6 +458,13 @@ static int dwc3_meson_g12a_role_set(struct usb_role_switch *sw, if (mode == priv->otg_phy_mode) return 0; + if (priv->drvdata->otg_phy_host_port_disable) + dev_warn_once(priv->dev, "Manual OTG switch is broken on this "\ + "SoC, when manual switching from "\ + "Host to device, DWC3 controller "\ + "will need to be resetted in order "\ + "to recover usage of the Host port"); + return dwc3_meson_g12a_otg_mode_set(priv, mode); } |