diff mbox series

[v3,2/8] usb: dwc3: meson-g12a: support the GXL/GXM DWC3 host phy disconnect

Message ID 20200416121910.12723-3-narmstrong@baylibre.com (mailing list archive)
State New, archived
Headers show
Series usb: dwc3: meson: add OTG support for GXL/GXM | expand

Commit Message

Neil Armstrong April 16, 2020, 12:19 p.m. UTC
On the Amlogic GXL/GXM SoCs, the OTG PHY status signals are always
connected to the DWC3 controller, thus crashing the controller when
switching to OTG mode when port is not populated with a device/cable to
Host.

Amlogic added a bit to disconnect the OTG PHY status signals from the DWC3
to be used when switching the OTG PHY as Device to the DWC2 controller.

The drawback is that it makes the DWC3 port state machine stall and needs
a full reset of the DWC3 controller to get connect status to the port
connected to the OTG PHY, but not the other one.

Signed-off-by: Neil Armstrong <narmstrong@baylibre.com>
---
 drivers/usb/dwc3/dwc3-meson-g12a.c | 34 ++++++++++++++++++++++++++++++
 1 file changed, 34 insertions(+)

Comments

Martin Blumenstingl April 17, 2020, 6:58 p.m. UTC | #1
On Thu, Apr 16, 2020 at 2:19 PM Neil Armstrong <narmstrong@baylibre.com> wrote:
>
> On the Amlogic GXL/GXM SoCs, the OTG PHY status signals are always
> connected to the DWC3 controller, thus crashing the controller when
> switching to OTG mode when port is not populated with a device/cable to
> Host.
>
> Amlogic added a bit to disconnect the OTG PHY status signals from the DWC3
> to be used when switching the OTG PHY as Device to the DWC2 controller.
>
> The drawback is that it makes the DWC3 port state machine stall and needs
> a full reset of the DWC3 controller to get connect status to the port
> connected to the OTG PHY, but not the other one.
>
> Signed-off-by: Neil Armstrong <narmstrong@baylibre.com>
one nit-pick below. apart from that:
Reviewed-by: Martin Blumenstingl <martin.blumenstingl@googlemail.com>

[...]
>         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)));
if you have to re-send for whatever reason then I would like an empty
line here to make the code easier to read

>                 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 +318,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);
> +               }
same as above - if you have to re-send for whatever reason then please
add an empty line here
diff mbox series

Patch

diff --git a/drivers/usb/dwc3/dwc3-meson-g12a.c b/drivers/usb/dwc3/dwc3-meson-g12a.c
index e7a6d05f2a72..cc0b8a253932 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,13 @@  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 +318,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 +457,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);
 }