phy: sun4i-usb: Add a sunxi specific function for setting squelch-detect
authorJagan Teki <jagan@amarulasolutions.com>
Mon, 7 May 2018 07:33:37 +0000 (13:03 +0530)
committerJagan Teki <jagan@amarulasolutions.com>
Mon, 28 May 2018 11:10:43 +0000 (16:40 +0530)
The sunxi otg phy has a bug where it wrongly detects a high speed squelch
when reset on the root port gets de-asserted with a lo-speed device.

The workaround for this is to disable squelch detect before de-asserting
reset, and re-enabling it after the reset de-assert is done. Add a sunxi
specific phy function to allow the sunxi-musb glue to do this.

Signed-off-by: Jagan Teki <jagan@amarulasolutions.com>
Acked-by: Jun Nie <jun.nie@linaro.org>
drivers/phy/allwinner/phy-sun4i-usb.c
drivers/usb/musb-new/musb_core.h
drivers/usb/musb-new/musb_uboot.c
drivers/usb/musb-new/sunxi.c
include/phy-sun4i-usb.h

index 4194a1500779556ff86e298d7301c1165ad949b2..2b3cf48025cd1377d84d62bb56f9f113be8663e1 100644 (file)
@@ -36,6 +36,7 @@
 #define PHY_TX_AMPLITUDE_TUNE          0x20
 #define PHY_TX_SLEWRATE_TUNE           0x22
 #define PHY_DISCON_TH_SEL              0x2a
+#define PHY_SQUELCH_DETECT             0x3c
 
 #define PHYCTL_DATA                    BIT(7)
 #define OTGCTL_ROUTE_MUSB              BIT(0)
@@ -383,6 +384,11 @@ int sun4i_usb_phy_id_detect(struct phy *phy)
        return gpio_get_value(usb_phy->gpio_id_det);
 }
 
+void sun4i_usb_phy_set_squelch_detect(struct phy *phy, bool enabled)
+{
+       sun4i_usb_phy_write(phy, PHY_SQUELCH_DETECT, enabled ? 0 : 2, 2);
+}
+
 static struct phy_ops sun4i_usb_phy_ops = {
        .of_xlate = sun4i_usb_phy_xlate,
        .init = sun4i_usb_phy_init,
index fa4cf86c79bb14627e5f9ba49986bf953e5ce880..821d0e06f619f2d27191e5c9ccaaef85a8b47978 100644 (file)
@@ -200,6 +200,8 @@ enum musb_g_ep0_state {
  * @vbus_status: returns vbus status if possible
  * @set_vbus:  forces vbus status
  * @adjust_channel_params: pre check for standard dma channel_program func
+ * @pre_root_reset_end: called before the root usb port reset flag gets cleared
+ * @post_root_reset_end: called after the root usb port reset flag gets cleared
  */
 struct musb_platform_ops {
        int     (*init)(struct musb *musb);
@@ -221,6 +223,8 @@ struct musb_platform_ops {
        int     (*adjust_channel_params)(struct dma_channel *channel,
                                u16 packet_sz, u8 *mode,
                                dma_addr_t *dma_addr, u32 *len);
+       void    (*pre_root_reset_end)(struct musb *musb);
+       void    (*post_root_reset_end)(struct musb *musb);
 };
 
 /*
index 8662c0ff70782bb872db836516e3e12a4c6df3a7..48242c04756f082ca844a6820b9f52c806fe6836 100644 (file)
@@ -193,19 +193,16 @@ static int _musb_reset_root_port(struct musb_host_data *host,
        power &= 0xf0;
        musb_writeb(mbase, MUSB_POWER, MUSB_POWER_RESET | power);
        mdelay(50);
-#ifdef CONFIG_ARCH_SUNXI
-       /*
-        * sunxi phy has a bug and it will wrongly detect high speed squelch
-        * when clearing reset on low-speed devices, temporary disable
-        * squelch detection to work around this.
-        */
-       sunxi_usb_phy_enable_squelch_detect(0, 0);
-#endif
+
+       if (host->host->ops->pre_root_reset_end)
+               host->host->ops->pre_root_reset_end(host->host);
+
        power = musb_readb(mbase, MUSB_POWER);
        musb_writeb(mbase, MUSB_POWER, ~MUSB_POWER_RESET & power);
-#ifdef CONFIG_ARCH_SUNXI
-       sunxi_usb_phy_enable_squelch_detect(0, 1);
-#endif
+
+       if (host->host->ops->post_root_reset_end)
+               host->host->ops->post_root_reset_end(host->host);
+
        host->host->isr(0, host->host);
        host->host_speed = (musb_readb(mbase, MUSB_POWER) & MUSB_POWER_HSMODE) ?
                        USB_SPEED_HIGH :
index a33ebe5cafbbe61a4fa6febb84c4665221b5284b..5728a3f384f5512c837ec373b25511066cf7765a 100644 (file)
@@ -304,10 +304,26 @@ static int sunxi_musb_init(struct musb *musb)
        return 0;
 }
 
+static void sunxi_musb_pre_root_reset_end(struct musb *musb)
+{
+       struct sunxi_glue *glue = to_sunxi_glue(musb->controller);
+
+       sun4i_usb_phy_set_squelch_detect(glue->phy, false);
+}
+
+static void sunxi_musb_post_root_reset_end(struct musb *musb)
+{
+       struct sunxi_glue *glue = to_sunxi_glue(musb->controller);
+
+       sun4i_usb_phy_set_squelch_detect(glue->phy, true);
+}
+
 static const struct musb_platform_ops sunxi_musb_ops = {
        .init           = sunxi_musb_init,
        .enable         = sunxi_musb_enable,
        .disable        = sunxi_musb_disable,
+       .pre_root_reset_end = sunxi_musb_pre_root_reset_end,
+       .post_root_reset_end = sunxi_musb_post_root_reset_end,
 };
 
 /* Allwinner OTG supports up to 5 endpoints */
index 040e9d375032bed60df79ecc890dba410efd96be..b0a45b205803f61d225e3f8736d15cee09b10ad1 100644 (file)
@@ -23,4 +23,12 @@ int sun4i_usb_phy_id_detect(struct phy *phy);
  */
 int sun4i_usb_phy_vbus_detect(struct phy *phy);
 
+/**
+ * sun4i_usb_phy_set_squelch_detect() - Enable/disable squelch detect
+ *
+ * @phy: reference to a sun4i usb phy
+ * @enabled: wether to enable or disable squelch detect
+ */
+void sun4i_usb_phy_set_squelch_detect(struct phy *phy, bool enabled);
+
 #endif /*__GENERIC_PHY_SUN4I_USB_H */