usb: msm_otg: fix for TA detection bug
This patch solves the problem of recognize usb link when the TA is slow to
connect.
Change-Id: I9ebd118a53175a272117a75b5a5a1554da9c3d8b
Signed-off-by: Iliyan Malchev <malchev@google.com>
diff --git a/drivers/usb/otg/msm_otg.c b/drivers/usb/otg/msm_otg.c
index a3bd36a..8fe1c1d 100644
--- a/drivers/usb/otg/msm_otg.c
+++ b/drivers/usb/otg/msm_otg.c
@@ -75,6 +75,16 @@
static bool debug_bus_voting_enabled;
static bool mhl_det_in_progress;
+enum check_ta_state_type {
+ CHECK_TA_STATE_UNDEFINED = 0,
+ CHECK_TA_STATE_FIRST_RETRY,
+ CHECK_TA_STATE_SECOND_RETRY,
+ CHECK_TA_STATE_TA_DETECTED,
+ CHECK_TA_STATE_DONE,
+};
+
+enum check_ta_state_type check_ta_state = CHECK_TA_STATE_UNDEFINED;
+
static struct regulator *hsusb_3p3;
static struct regulator *hsusb_1p8;
static struct regulator *hsusb_vddcx;
@@ -1971,6 +1981,64 @@
default: return "INVALID_CHARGER";
}
}
+#define MSM_CHECK_TA_DELAY (5 *HZ)
+#define PORTSC_LS (3 << 10) /* Read - Port's Line status */
+
+static void msm_ta_detect_work(struct work_struct *w)
+{
+ struct msm_otg *motg = the_msm_otg;
+ struct usb_otg *otg = motg->phy.otg;
+ unsigned long delay = MSM_CHECK_TA_DELAY;
+
+ switch (check_ta_state) {
+ case CHECK_TA_STATE_UNDEFINED:
+ pr_info("msm_ta_detect_work: ta detection work\n");
+ if ((readl(USB_PORTSC) & PORTSC_LS) == PORTSC_LS) {
+ check_ta_state = CHECK_TA_STATE_TA_DETECTED;
+ delay = 0;
+ } else {
+ check_ta_state = CHECK_TA_STATE_FIRST_RETRY;
+ delay = MSM_CHECK_TA_DELAY;
+ }
+ break;
+ case CHECK_TA_STATE_FIRST_RETRY:
+ pr_info("msm_ta_detect_work: ta detection first retry\n");
+ if ((readl(USB_PORTSC) & PORTSC_LS) == PORTSC_LS) {
+ check_ta_state = CHECK_TA_STATE_TA_DETECTED;
+ delay = 0;
+ } else {
+ check_ta_state = CHECK_TA_STATE_SECOND_RETRY;
+ delay = MSM_CHECK_TA_DELAY;
+ }
+ break;
+ case CHECK_TA_STATE_SECOND_RETRY:
+ pr_info("msm_ta_detect_work: ta detection second retry\n");
+ if ((readl(USB_PORTSC) & PORTSC_LS) == PORTSC_LS)
+ check_ta_state = CHECK_TA_STATE_TA_DETECTED;
+ else
+ check_ta_state = CHECK_TA_STATE_DONE;
+ delay = 0;
+ break;
+ case CHECK_TA_STATE_TA_DETECTED:
+ pr_info("msm_ta_detect_work: ta dectection detected\n");
+ /* inform to user space that SDP is no longer detected */
+ msm_otg_notify_charger(motg, 0);
+ motg->chg_state = USB_CHG_STATE_DETECTED;
+ motg->chg_type = USB_DCP_CHARGER;
+ motg->cur_power = 0;
+ msm_otg_start_peripheral(otg, 0);
+ otg->phy->state = OTG_STATE_B_IDLE;
+ schedule_work(&motg->sm_work);
+ return;
+ case CHECK_TA_STATE_DONE:
+ /* Fall through */
+ default:
+ pr_info("msm_ta_detect_work: ta detection done\n");
+ return;
+ }
+
+ schedule_delayed_work(&motg->check_ta_work, delay);
+}
#define MSM_CHG_DCD_POLL_TIME (100 * HZ/1000) /* 100 msec */
#define MSM_CHG_DCD_MAX_RETRIES 6 /* Tdcd_tmout = 6 * 100 msec */
@@ -2263,6 +2331,8 @@
msm_otg_start_peripheral(otg, 1);
otg->phy->state =
OTG_STATE_B_PERIPHERAL;
+ schedule_delayed_work(&motg->check_ta_work,
+ MSM_CHECK_TA_DELAY);
break;
default:
break;
@@ -2284,6 +2354,8 @@
} else {
pr_debug("chg_work cancel");
cancel_delayed_work_sync(&motg->chg_work);
+ cancel_delayed_work_sync(&motg->check_ta_work);
+ check_ta_state = CHECK_TA_STATE_UNDEFINED;
motg->chg_state = USB_CHG_STATE_UNDEFINED;
motg->chg_type = USB_INVALID_CHARGER;
msm_otg_notify_charger(motg, 0);
@@ -3615,6 +3687,7 @@
INIT_WORK(&motg->sm_work, msm_otg_sm_work);
INIT_DELAYED_WORK(&motg->chg_work, msm_chg_detect_work);
INIT_DELAYED_WORK(&motg->pmic_id_status_work, msm_pmic_id_status_w);
+ INIT_DELAYED_WORK(&motg->check_ta_work, msm_ta_detect_work);
setup_timer(&motg->id_timer, msm_otg_id_timer_func,
(unsigned long) motg);
ret = request_irq(motg->irq, msm_otg_irq, IRQF_SHARED,
@@ -3777,6 +3850,7 @@
msm_otg_debugfs_cleanup();
cancel_delayed_work_sync(&motg->chg_work);
cancel_delayed_work_sync(&motg->pmic_id_status_work);
+ cancel_delayed_work_sync(&motg->check_ta_work);
cancel_work_sync(&motg->sm_work);
pm_runtime_resume(&pdev->dev);