diff options
author | Li Jun <jun.li@freescale.com> | 2015-07-22 13:59:50 +0800 |
---|---|---|
committer | Nitin Garg <nitin.garg@freescale.com> | 2015-09-17 09:24:07 -0500 |
commit | f8ede93e68cdb3845cbd8970f2606fb7659628b1 (patch) | |
tree | ad9b870aa08459edb5f32a29e3e75d5d9d9dd3c4 | |
parent | 0e9a87bbd4f7d1c48e42c65aa94939a7283599dd (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>
-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 | ||||
-rw-r--r-- | include/linux/usb/chipidea.h | 2 |
5 files changed, 72 insertions, 0 deletions
diff --git a/drivers/usb/chipidea/ci_hdrc_imx.c b/drivers/usb/chipidea/ci_hdrc_imx.c index f78b3c16c098..0976c8503d05 100644 --- a/drivers/usb/chipidea/ci_hdrc_imx.c +++ b/drivers/usb/chipidea/ci_hdrc_imx.c @@ -292,6 +292,14 @@ static int ci_hdrc_imx_notify_event(struct ci_hdrc *ci, unsigned event) case CI_HDRC_IMX_ADP_ATTACH_EVENT: if (data->usbmisc_data) return imx_usbmisc_adp_attach_event(data->usbmisc_data); + 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 ec28272085c3..99f466ff3f35 100644 --- a/drivers/usb/chipidea/ci_hdrc_imx.h +++ b/drivers/usb/chipidea/ci_hdrc_imx.h @@ -45,5 +45,7 @@ bool imx_usbmisc_adp_is_probe_int(struct imx_usbmisc_data *data); bool imx_usbmisc_adp_is_sense_int(struct imx_usbmisc_data *data); bool imx_usbmisc_adp_sense_connection(struct imx_usbmisc_data *data); bool imx_usbmisc_adp_attach_event(struct imx_usbmisc_data *data); +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 0c6e01961814..69917100972c 100644 --- a/drivers/usb/chipidea/otg_fsm.c +++ b/drivers/usb/chipidea/otg_fsm.c @@ -580,6 +580,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); @@ -822,6 +825,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; @@ -1016,6 +1022,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 f0f8edc078d6..a8dabfff463e 100644 --- a/drivers/usb/chipidea/usbmisc_imx.c +++ b/drivers/usb/chipidea/usbmisc_imx.c @@ -97,6 +97,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 OTG_DRVVBUS0 BIT(16) #define OTG_ADP_PRBENB0 BIT(13) @@ -144,6 +146,9 @@ struct usbmisc_ops { bool (*adp_sense_connection)(struct imx_usbmisc_data *data); /* Check if it's a device attach or dettach event */ bool (*adp_attach_event)(struct imx_usbmisc_data *data); + /* override UTMI termination select */ + int (*term_select_override)(struct imx_usbmisc_data *data, + bool enable, int val); }; struct imx_usbmisc { @@ -736,6 +741,37 @@ static int usbmisc_imx7d_power_lost_check(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, @@ -779,6 +815,7 @@ static const struct usbmisc_ops imx7d_usbmisc_ops = { .is_sense_int = usbmisc_otg_adp_is_sense_int, .adp_sense_connection = usbmisc_otg_adp_sense_connection, .adp_attach_event = usbmisc_otg_adp_is_attach_event, + .term_select_override = usbmisc_term_select_override, }; int imx_usbmisc_init(struct imx_usbmisc_data *data) @@ -963,6 +1000,21 @@ bool imx_usbmisc_adp_attach_event(struct imx_usbmisc_data *data) } EXPORT_SYMBOL_GPL(imx_usbmisc_adp_attach_event); +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", diff --git a/include/linux/usb/chipidea.h b/include/linux/usb/chipidea.h index e3ea5b5a2cad..3ac366dcf500 100644 --- a/include/linux/usb/chipidea.h +++ b/include/linux/usb/chipidea.h @@ -51,6 +51,8 @@ struct ci_hdrc_platform_data { #define CI_HDRC_IMX_ADP_IS_SENSE_INT 11 #define CI_HDRC_IMX_ADP_SENSE_CONNECTION 12 #define CI_HDRC_IMX_ADP_ATTACH_EVENT 13 +#define CI_HDRC_IMX_TERM_SELECT_OVERRIDE_FS 14 +#define CI_HDRC_IMX_TERM_SELECT_OVERRIDE_OFF 15 int (*notify_event)(struct ci_hdrc *ci, unsigned event); struct regulator *reg_vbus; struct usb_otg_caps ci_otg_caps; |