diff options
author | Li Jun <jun.li@freescale.com> | 2015-07-22 13:59:50 +0800 |
---|---|---|
committer | Nitin Garg <nitin.garg@nxp.com> | 2016-01-14 10:59:21 -0600 |
commit | 999482b251bcfa6b97183de2806a745b41c3db89 (patch) | |
tree | 5646b9ef0f57ed1826b336b73b307d43afe77a12 /drivers/usb | |
parent | f31b9c209571f7a36dbdde6df22576a2b618b6be (diff) |
MLK-11272 usb: chipidea: otg: data pulse detection work around for imx7d
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)
Diffstat (limited to 'drivers/usb')
-rw-r--r-- | drivers/usb/chipidea/ci_hdrc_imx.c | 8 | ||||
-rw-r--r-- | drivers/usb/chipidea/ci_hdrc_imx.h | 2 | ||||
-rw-r--r-- | drivers/usb/chipidea/otg_fsm.c | 8 | ||||
-rw-r--r-- | drivers/usb/chipidea/usbmisc_imx.c | 52 |
4 files changed, 70 insertions, 0 deletions
diff --git a/drivers/usb/chipidea/ci_hdrc_imx.c b/drivers/usb/chipidea/ci_hdrc_imx.c index 362eb7d4703b..e969156382d4 100644 --- a/drivers/usb/chipidea/ci_hdrc_imx.c +++ b/drivers/usb/chipidea/ci_hdrc_imx.c @@ -347,6 +347,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"); } diff --git a/drivers/usb/chipidea/ci_hdrc_imx.h b/drivers/usb/chipidea/ci_hdrc_imx.h index 814f8326f58a..bdcf1b724abe 100644 --- a/drivers/usb/chipidea/ci_hdrc_imx.h +++ b/drivers/usb/chipidea/ci_hdrc_imx.h @@ -62,5 +62,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 */ diff --git a/drivers/usb/chipidea/otg_fsm.c b/drivers/usb/chipidea/otg_fsm.c index 1be81b442a5a..c8d96bf5feac 100644 --- a/drivers/usb/chipidea/otg_fsm.c +++ b/drivers/usb/chipidea/otg_fsm.c @@ -560,6 +560,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); @@ -718,6 +721,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; @@ -854,6 +860,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); diff --git a/drivers/usb/chipidea/usbmisc_imx.c b/drivers/usb/chipidea/usbmisc_imx.c index 5642cd488b39..bf5b5a1503ce 100644 --- a/drivers/usb/chipidea/usbmisc_imx.c +++ b/drivers/usb/chipidea/usbmisc_imx.c @@ -115,6 +115,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) @@ -145,6 +147,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 { @@ -850,6 +855,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, @@ -892,6 +928,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) @@ -1029,6 +1066,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", |