MLK-11272 usb: chipidea: otg: data pulse detection work around for imx7d
authorLi Jun <jun.li@freescale.com>
Wed, 22 Jul 2015 05:59:50 +0000 (13:59 +0800)
committerNitin Garg <nitin.garg@nxp.com>
Mon, 19 Mar 2018 19:47:19 +0000 (14:47 -0500)
i.MX7D has a silicon issue on full speed termination after A device ends
a session, which causes it can not detect data pulse from B device if
A device isn't in low power mode, this patch work around it by override
Termsel bit to be 1 for FS mode termination. After A device detects data
pulse or turns on vbus, this override will be cleared and disabled.

Signed-off-by: Li Jun <jun.li@freescale.com>
(cherry picked from commit 1ec37968c892efbb1925784446a75d35e8bda228)

drivers/usb/chipidea/ci_hdrc_imx.c
drivers/usb/chipidea/ci_hdrc_imx.h
drivers/usb/chipidea/otg_fsm.c
drivers/usb/chipidea/usbmisc_imx.c
include/linux/usb/chipidea.h

index 9ea5a38..9205c8a 100644 (file)
@@ -362,6 +362,14 @@ static int ci_hdrc_imx_notify_event(struct ci_hdrc *ci, unsigned event)
                        return ret;
                }
                break;
+       case CI_HDRC_IMX_TERM_SELECT_OVERRIDE_FS:
+               if (data->usbmisc_data)
+                       return imx_usbmisc_term_select_override(
+                                       data->usbmisc_data, true, 1);
+       case CI_HDRC_IMX_TERM_SELECT_OVERRIDE_OFF:
+               if (data->usbmisc_data)
+                       return imx_usbmisc_term_select_override(
+                                       data->usbmisc_data, false, 0);
        default:
                dev_dbg(dev, "unknown event\n");
        }
index 3be2cbe..5ab88b5 100644 (file)
@@ -63,5 +63,7 @@ int imx_usbmisc_charger_secondary_detection(struct imx_usbmisc_data *data);
 int imx_usbmisc_power_lost_check(struct imx_usbmisc_data *);
 int imx_usbmisc_hsic_set_connect(struct imx_usbmisc_data *);
 int imx_usbmisc_hsic_set_clk(struct imx_usbmisc_data *, bool);
+int imx_usbmisc_term_select_override(struct imx_usbmisc_data *data,
+                                               bool enable, int val);
 
 #endif /* __DRIVER_USB_CHIPIDEA_CI_HDRC_IMX_H */
index b6a8f7f..ef7629f 100644 (file)
@@ -530,6 +530,9 @@ static void ci_otg_drv_vbus(struct otg_fsm *fsm, int on)
        struct ci_hdrc  *ci = container_of(fsm, struct ci_hdrc, fsm);
 
        if (on) {
+               ci->platdata->notify_event(ci,
+                       CI_HDRC_IMX_TERM_SELECT_OVERRIDE_OFF);
+
                /* Enable power power */
                hw_write(ci, OP_PORTSC, PORTSC_W1C_BITS | PORTSC_PP,
                                                        PORTSC_PP);
@@ -698,6 +701,9 @@ int ci_otg_fsm_work(struct ci_hdrc *ci)
                                                                PORTSC_PP, 0);
                                hw_write_otgsc(ci, OTGSC_DPIS, OTGSC_DPIS);
                                hw_write_otgsc(ci, OTGSC_DPIE, OTGSC_DPIE);
+                               /* FS termination override if needed */
+                               ci->platdata->notify_event(ci,
+                                       CI_HDRC_IMX_TERM_SELECT_OVERRIDE_FS);
                        }
                        if (ci->id_event)
                                ci->id_event = false;
@@ -834,6 +840,8 @@ irqreturn_t ci_otg_fsm_irq(struct ci_hdrc *ci)
        if (otg_int_src) {
                if (otg_int_src & OTGSC_DPIS) {
                        hw_write_otgsc(ci, OTGSC_DPIS, OTGSC_DPIS);
+                       ci->platdata->notify_event(ci,
+                               CI_HDRC_IMX_TERM_SELECT_OVERRIDE_OFF);
                        ci_otg_add_timer(ci, A_DP_END);
                } else if (otg_int_src & OTGSC_IDIS) {
                        hw_write_otgsc(ci, OTGSC_IDIS, OTGSC_IDIS);
index 052fa1f..57731f4 100644 (file)
@@ -98,6 +98,8 @@
 #define MX7D_USB_VBUS_WAKEUP_SOURCE_AVALID     MX7D_USB_VBUS_WAKEUP_SOURCE(1)
 #define MX7D_USB_VBUS_WAKEUP_SOURCE_BVALID     MX7D_USB_VBUS_WAKEUP_SOURCE(2)
 #define MX7D_USB_VBUS_WAKEUP_SOURCE_SESS_END   MX7D_USB_VBUS_WAKEUP_SOURCE(3)
+#define MX7D_USB_TERMSEL_OVERRIDE      BIT(4)
+#define MX7D_USB_TERMSEL_OVERRIDE_EN   BIT(5)
 
 #define MX7D_USB_OTG_PHY_CFG2_CHRG_DCDENB      BIT(3)
 #define MX7D_USB_OTG_PHY_CFG2_CHRG_VDATSRCENB0 BIT(2)
@@ -146,6 +148,9 @@ struct usbmisc_ops {
        int (*hsic_set_connect)(struct imx_usbmisc_data *data);
        /* It's called during suspend/resume */
        int (*hsic_set_clk)(struct imx_usbmisc_data *data, bool enabled);
+       /* override UTMI termination select */
+       int (*term_select_override)(struct imx_usbmisc_data *data,
+                                               bool enable, int val);
 };
 
 struct imx_usbmisc {
@@ -857,6 +862,37 @@ int imx7d_charger_secondary_detection(struct imx_usbmisc_data *data)
        return 0;
 }
 
+static int usbmisc_term_select_override(struct imx_usbmisc_data *data,
+                                               bool enable, int val)
+{
+       struct imx_usbmisc *usbmisc = dev_get_drvdata(data->dev);
+       unsigned long flags;
+       u32 reg;
+
+       spin_lock_irqsave(&usbmisc->lock, flags);
+
+       reg = readl(usbmisc->base + MX7D_USBNC_USB_CTRL2);
+       if (enable) {
+               if (val)
+                       writel(reg | MX7D_USB_TERMSEL_OVERRIDE,
+                               usbmisc->base + MX7D_USBNC_USB_CTRL2);
+               else
+                       writel(reg & ~MX7D_USB_TERMSEL_OVERRIDE,
+                               usbmisc->base + MX7D_USBNC_USB_CTRL2);
+
+               reg = readl(usbmisc->base + MX7D_USBNC_USB_CTRL2);
+               writel(reg | MX7D_USB_TERMSEL_OVERRIDE_EN,
+                       usbmisc->base + MX7D_USBNC_USB_CTRL2);
+       } else {
+               writel(reg & ~MX7D_USB_TERMSEL_OVERRIDE_EN,
+                       usbmisc->base + MX7D_USBNC_USB_CTRL2);
+       }
+
+       spin_unlock_irqrestore(&usbmisc->lock, flags);
+
+       return 0;
+}
+
 static const struct usbmisc_ops imx25_usbmisc_ops = {
        .init = usbmisc_imx25_init,
        .post = usbmisc_imx25_post,
@@ -899,6 +935,7 @@ static const struct usbmisc_ops imx7d_usbmisc_ops = {
        .power_lost_check = usbmisc_imx7d_power_lost_check,
        .charger_primary_detection = imx7d_charger_primary_detection,
        .charger_secondary_detection = imx7d_charger_secondary_detection,
+       .term_select_override = usbmisc_term_select_override,
 };
 
 int imx_usbmisc_init(struct imx_usbmisc_data *data)
@@ -1036,6 +1073,21 @@ int imx_usbmisc_hsic_set_clk(struct imx_usbmisc_data *data, bool on)
 }
 EXPORT_SYMBOL_GPL(imx_usbmisc_hsic_set_clk);
 
+int imx_usbmisc_term_select_override(struct imx_usbmisc_data *data,
+                                               bool enable, int val)
+{
+       struct imx_usbmisc *usbmisc;
+
+       if (!data)
+               return 0;
+
+       usbmisc = dev_get_drvdata(data->dev);
+       if (!usbmisc->ops->term_select_override)
+               return 0;
+       return usbmisc->ops->term_select_override(data, enable, val);
+}
+EXPORT_SYMBOL_GPL(imx_usbmisc_term_select_override);
+
 static const struct of_device_id usbmisc_imx_dt_ids[] = {
        {
                .compatible = "fsl,imx25-usbmisc",
index 38f03e7..2f0ed8c 100644 (file)
@@ -67,6 +67,8 @@ struct ci_hdrc_platform_data {
 #define CI_HDRC_CONTROLLER_CHARGER_POST_EVENT  4
 #define CI_HDRC_IMX_HSIC_ACTIVE_EVENT          5
 #define CI_HDRC_IMX_HSIC_SUSPEND_EVENT         6
+#define CI_HDRC_IMX_TERM_SELECT_OVERRIDE_FS    7
+#define CI_HDRC_IMX_TERM_SELECT_OVERRIDE_OFF   8
        int     (*notify_event)(struct ci_hdrc *ci, unsigned event);
        struct regulator        *reg_vbus;
        struct usb_otg_caps     ci_otg_caps;