From: Hao Wu <hao.wu@xxxxxxxxx> This patch enables kernel timers for OTG State machine. Hardware timer will not work once the PHY enters in Low power mode (clock gated). Main changes: - Enable kernel timers instead of hardware timer in PHY low power mode. - Add timer for SRP failure case. - Correct the timeout value. Signed-off-by: Hao Wu <hao.wu@xxxxxxxxx> Signed-off-by: Alan Cox <alan@xxxxxxxxxxxxxxx> --- drivers/usb/otg/langwell_otg.c | 181 +++++++++++++++++++++++++++----------- include/linux/usb/langwell_otg.h | 20 ++++ 2 files changed, 147 insertions(+), 54 deletions(-) diff --git a/drivers/usb/otg/langwell_otg.c b/drivers/usb/otg/langwell_otg.c index 2cbfec5..1b6124e 100644 --- a/drivers/usb/otg/langwell_otg.c +++ b/drivers/usb/otg/langwell_otg.c @@ -128,9 +128,8 @@ static inline struct langwell_otg_timer *otg_timer_initializer return timer; } -static struct langwell_otg_timer *a_wait_vrise_tmr, *a_wait_bcon_tmr, - *a_aidl_bdis_tmr, *b_ase0_brst_tmr, *b_se0_srp_tmr, *b_srp_res_tmr, - *b_bus_suspend_tmr; +static struct langwell_otg_timer *a_wait_vrise_tmr, *a_aidl_bdis_tmr, + *b_se0_srp_tmr, *b_srp_init_tmr; static struct list_head active_timers; @@ -500,30 +499,90 @@ static void langwell_otg_init_timers(struct otg_hsm *hsm) /* HSM used timers */ a_wait_vrise_tmr = otg_timer_initializer(&set_tmout, TA_WAIT_VRISE, (unsigned long)&hsm->a_wait_vrise_tmout); - a_wait_bcon_tmr = otg_timer_initializer(&set_tmout, TA_WAIT_BCON, - (unsigned long)&hsm->a_wait_bcon_tmout); a_aidl_bdis_tmr = otg_timer_initializer(&set_tmout, TA_AIDL_BDIS, (unsigned long)&hsm->a_aidl_bdis_tmout); - b_ase0_brst_tmr = otg_timer_initializer(&set_tmout, TB_ASE0_BRST, - (unsigned long)&hsm->b_ase0_brst_tmout); b_se0_srp_tmr = otg_timer_initializer(&set_tmout, TB_SE0_SRP, (unsigned long)&hsm->b_se0_srp); - b_srp_res_tmr = otg_timer_initializer(&set_tmout, TB_SRP_RES, - (unsigned long)&hsm->b_srp_res_tmout); - b_bus_suspend_tmr = otg_timer_initializer(&set_tmout, TB_BUS_SUSPEND, - (unsigned long)&hsm->b_bus_suspend_tmout); + b_srp_init_tmr = otg_timer_initializer(&set_tmout, TB_SRP_INIT, + (unsigned long)&hsm->b_srp_init_tmout); } /* Free timers */ static void langwell_otg_free_timers(void) { kfree(a_wait_vrise_tmr); - kfree(a_wait_bcon_tmr); kfree(a_aidl_bdis_tmr); - kfree(b_ase0_brst_tmr); kfree(b_se0_srp_tmr); - kfree(b_srp_res_tmr); - kfree(b_bus_suspend_tmr); + kfree(b_srp_init_tmr); +} + +/* The timeout callback function to set time out bit */ +static void langwell_otg_timer_fn(unsigned long indicator) +{ + struct langwell_otg *lnw = the_transceiver; + + *(int *)indicator = 1; + + dev_dbg(&lnw->pdev->dev, "kernel timer - timeout\n"); + + langwell_update_transceiver(); +} + +/* kernel timer used instead of HW based interrupt */ +static void langwell_otg_add_ktimer(enum langwell_otg_timer_type timers) +{ + struct langwell_otg *lnw = the_transceiver; + unsigned long j = jiffies; + unsigned long data, time; + + switch (timers) { + case TA_WAIT_VRISE_TMR: + lnw->hsm.a_wait_vrise_tmout = 0; + data = (unsigned long)&lnw->hsm.a_wait_vrise_tmout; + time = TA_WAIT_VRISE; + break; + case TA_WAIT_BCON_TMR: + lnw->hsm.a_wait_bcon_tmout = 0; + data = (unsigned long)&lnw->hsm.a_wait_bcon_tmout; + time = TA_WAIT_BCON; + break; + case TA_AIDL_BDIS_TMR: + lnw->hsm.a_aidl_bdis_tmout = 0; + data = (unsigned long)&lnw->hsm.a_aidl_bdis_tmout; + time = TA_AIDL_BDIS; + break; + case TB_ASE0_BRST_TMR: + lnw->hsm.b_ase0_brst_tmout = 0; + data = (unsigned long)&lnw->hsm.b_ase0_brst_tmout; + time = TB_ASE0_BRST; + break; + case TB_SRP_INIT_TMR: + lnw->hsm.b_srp_init_tmout = 0; + data = (unsigned long)&lnw->hsm.b_srp_init_tmout; + time = TB_SRP_INIT; + break; + case TB_SRP_FAIL_TMR: + lnw->hsm.b_srp_fail_tmout = 0; + data = (unsigned long)&lnw->hsm.b_srp_fail_tmout; + time = TB_SRP_FAIL; + break; + case TB_BUS_SUSPEND_TMR: + lnw->hsm.b_bus_suspend_tmout = 0; + data = (unsigned long)&lnw->hsm.b_bus_suspend_tmout; + time = TB_BUS_SUSPEND; + break; + default: + dev_dbg(&lnw->pdev->dev, "unkown timer, cannot enable it\n"); + return; + } + + lnw->hsm_timer.data = data; + lnw->hsm_timer.function = langwell_otg_timer_fn; + lnw->hsm_timer.expires = j + time * HZ / 1000; /* milliseconds */ + + add_timer(&lnw->hsm_timer); + + dev_dbg(&lnw->pdev->dev, "add timer successfully\n"); } /* Add timer to timer list */ @@ -806,7 +865,8 @@ static void langwell_otg_work(struct work_struct *work) case OTG_STATE_UNDEFINED: case OTG_STATE_B_IDLE: if (!lnw->hsm.id) { - langwell_otg_del_timer(b_srp_res_tmr); + langwell_otg_del_timer(b_srp_init_tmr); + del_timer_sync(&lnw->hsm_timer); lnw->otg.default_a = 1; lnw->hsm.a_srp_det = 0; langwell_otg_chrg_vbus(0); @@ -815,12 +875,16 @@ static void langwell_otg_work(struct work_struct *work) langwell_otg_phy_low_power(1); lnw->otg.state = OTG_STATE_A_IDLE; queue_work(lnw->qwork, &lnw->work); - } else if (lnw->hsm.b_srp_res_tmout) { - lnw->hsm.b_srp_res_tmout = 0; + } else if (lnw->hsm.b_srp_init_tmout) { + lnw->hsm.b_srp_init_tmout = 0; + dev_warn(&lnw->pdev->dev, "SRP init timeout\n"); + } else if (lnw->hsm.b_srp_fail_tmout) { + lnw->hsm.b_srp_fail_tmout = 0; lnw->hsm.b_bus_req = 0; langwell_otg_nsf_msg(6); } else if (lnw->hsm.b_sess_vld) { - langwell_otg_del_timer(b_srp_res_tmr); + langwell_otg_del_timer(b_srp_init_tmr); + del_timer_sync(&lnw->hsm_timer); lnw->hsm.b_sess_end = 0; lnw->hsm.a_bus_suspend = 0; langwell_otg_chrg_vbus(0); @@ -833,6 +897,7 @@ static void langwell_otg_work(struct work_struct *work) } else if (lnw->hsm.b_bus_req && (lnw->hsm.b_sess_end)) { + del_timer_sync(&lnw->hsm_timer); /* workaround for b_se0_srp detection */ retval = langwell_otg_check_se0_srp(0); if (retval) { @@ -844,9 +909,10 @@ static void langwell_otg_work(struct work_struct *work) langwell_otg_phy_low_power(0); /* Start SRP */ - langwell_otg_add_timer(b_srp_res_tmr); + langwell_otg_add_timer(b_srp_init_tmr); langwell_otg_start_srp(&lnw->otg); - langwell_otg_del_timer(b_srp_res_tmr); + langwell_otg_del_timer(b_srp_init_tmr); + langwell_otg_add_ktimer(TB_SRP_FAIL_TMR); /* reset PHY low power mode here */ langwell_otg_phy_low_power_wait(1); @@ -925,14 +991,14 @@ static void langwell_otg_work(struct work_struct *work) "host driver not loaded.\n"); lnw->hsm.a_bus_resume = 0; - lnw->hsm.b_ase0_brst_tmout = 0; - langwell_otg_add_timer(b_ase0_brst_tmr); + langwell_otg_add_ktimer(TB_ASE0_BRST_TMR); } break; case OTG_STATE_B_WAIT_ACON: if (!lnw->hsm.id) { - langwell_otg_del_timer(b_ase0_brst_tmr); + /* delete hsm timer for b_ase0_brst_tmr */ + del_timer_sync(&lnw->hsm_timer); lnw->otg.default_a = 1; lnw->hsm.a_srp_det = 0; @@ -950,7 +1016,8 @@ static void langwell_otg_work(struct work_struct *work) lnw->otg.state = OTG_STATE_A_IDLE; queue_work(lnw->qwork, &lnw->work); } else if (!lnw->hsm.b_sess_vld) { - langwell_otg_del_timer(b_ase0_brst_tmr); + /* delete hsm timer for b_ase0_brst_tmr */ + del_timer_sync(&lnw->hsm_timer); lnw->hsm.b_hnp_enable = 0; lnw->hsm.b_bus_req = 0; langwell_otg_chrg_vbus(0); @@ -966,13 +1033,15 @@ static void langwell_otg_work(struct work_struct *work) langwell_otg_phy_low_power(1); lnw->otg.state = OTG_STATE_B_IDLE; } else if (lnw->hsm.a_conn) { - langwell_otg_del_timer(b_ase0_brst_tmr); + /* delete hsm timer for b_ase0_brst_tmr */ + del_timer_sync(&lnw->hsm_timer); langwell_otg_HAAR(0); lnw->otg.state = OTG_STATE_B_HOST; queue_work(lnw->qwork, &lnw->work); } else if (lnw->hsm.a_bus_resume || lnw->hsm.b_ase0_brst_tmout) { - langwell_otg_del_timer(b_ase0_brst_tmr); + /* delete hsm timer for b_ase0_brst_tmr */ + del_timer_sync(&lnw->hsm_timer); langwell_otg_HAAR(0); langwell_otg_nsf_msg(7); @@ -1105,8 +1174,7 @@ static void langwell_otg_work(struct work_struct *work) break; } lnw->hsm.b_conn = 0; - lnw->hsm.a_wait_bcon_tmout = 0; - langwell_otg_add_timer(a_wait_bcon_tmr); + langwell_otg_add_ktimer(TA_WAIT_BCON_TMR); lnw->otg.state = OTG_STATE_A_WAIT_BCON; } else if (lnw->hsm.a_wait_vrise_tmout) { if (lnw->hsm.a_vbus_vld) { @@ -1120,8 +1188,7 @@ static void langwell_otg_work(struct work_struct *work) break; } lnw->hsm.b_conn = 0; - lnw->hsm.a_wait_bcon_tmout = 0; - langwell_otg_add_timer(a_wait_bcon_tmr); + langwell_otg_add_ktimer(TA_WAIT_BCON_TMR); lnw->otg.state = OTG_STATE_A_WAIT_BCON; } else { langwell_otg_drv_vbus(0); @@ -1132,7 +1199,8 @@ static void langwell_otg_work(struct work_struct *work) break; case OTG_STATE_A_WAIT_BCON: if (lnw->hsm.id) { - langwell_otg_del_timer(a_wait_bcon_tmr); + /* delete hsm timer for a_wait_bcon_tmr */ + del_timer_sync(&lnw->hsm_timer); lnw->otg.default_a = 0; lnw->hsm.b_bus_req = 0; @@ -1147,7 +1215,8 @@ static void langwell_otg_work(struct work_struct *work) lnw->otg.state = OTG_STATE_B_IDLE; queue_work(lnw->qwork, &lnw->work); } else if (!lnw->hsm.a_vbus_vld) { - langwell_otg_del_timer(a_wait_bcon_tmr); + /* delete hsm timer for a_wait_bcon_tmr */ + del_timer_sync(&lnw->hsm_timer); if (lnw->host_ops) lnw->host_ops->remove(lnw->pdev); @@ -1160,7 +1229,8 @@ static void langwell_otg_work(struct work_struct *work) } else if (lnw->hsm.a_bus_drop || (lnw->hsm.a_wait_bcon_tmout && !lnw->hsm.a_bus_req)) { - langwell_otg_del_timer(a_wait_bcon_tmr); + /* delete hsm timer for a_wait_bcon_tmr */ + del_timer_sync(&lnw->hsm_timer); if (lnw->host_ops) lnw->host_ops->remove(lnw->pdev); @@ -1170,7 +1240,8 @@ static void langwell_otg_work(struct work_struct *work) langwell_otg_drv_vbus(0); lnw->otg.state = OTG_STATE_A_WAIT_VFALL; } else if (lnw->hsm.b_conn) { - langwell_otg_del_timer(a_wait_bcon_tmr); + /* delete hsm timer for a_wait_bcon_tmr */ + del_timer_sync(&lnw->hsm_timer); lnw->hsm.a_suspend_req = 0; lnw->otg.state = OTG_STATE_A_HOST; @@ -1199,9 +1270,11 @@ static void langwell_otg_work(struct work_struct *work) langwell_otg_HABA(1); lnw->hsm.b_bus_resume = 0; lnw->hsm.a_aidl_bdis_tmout = 0; - langwell_otg_add_timer(a_aidl_bdis_tmr); langwell_otg_loc_sof(0); + /* clear PHCD to enable HW timer */ + langwell_otg_phy_low_power(0); + langwell_otg_add_timer(a_aidl_bdis_tmr); lnw->otg.state = OTG_STATE_A_SUSPEND; } else if (!lnw->hsm.a_bus_req && !lnw->otg.host->b_hnp_enable) { @@ -1268,12 +1341,13 @@ static void langwell_otg_work(struct work_struct *work) langwell_otg_HABA(1); lnw->hsm.b_bus_resume = 0; lnw->hsm.a_aidl_bdis_tmout = 0; - langwell_otg_add_timer(a_aidl_bdis_tmr); langwell_otg_loc_sof(0); + /* clear PHCD to enable HW timer */ + langwell_otg_phy_low_power(0); + langwell_otg_add_timer(a_aidl_bdis_tmr); lnw->otg.state = OTG_STATE_A_SUSPEND; } else if (!lnw->hsm.b_conn || !lnw->hsm.a_bus_req) { - lnw->hsm.a_wait_bcon_tmout = 0; - langwell_otg_add_timer(a_wait_bcon_tmr); + langwell_otg_add_ktimer(TA_WAIT_BCON_TMR); lnw->otg.state = OTG_STATE_A_WAIT_BCON; } break; @@ -1337,7 +1411,7 @@ static void langwell_otg_work(struct work_struct *work) dev_dbg(&lnw->pdev->dev, "client driver not loaded.\n"); - langwell_otg_add_timer(b_bus_suspend_tmr); + langwell_otg_add_ktimer(TB_BUS_SUSPEND_TMR); lnw->otg.state = OTG_STATE_A_PERIPHERAL; break; } else if (!lnw->hsm.a_vbus_vld) { @@ -1356,7 +1430,8 @@ static void langwell_otg_work(struct work_struct *work) break; case OTG_STATE_A_PERIPHERAL: if (lnw->hsm.id) { - langwell_otg_del_timer(b_bus_suspend_tmr); + /* delete hsm timer for b_bus_suspend_tmr */ + del_timer_sync(&lnw->hsm_timer); lnw->otg.default_a = 0; lnw->hsm.b_bus_req = 0; if (lnw->client_ops) @@ -1371,7 +1446,8 @@ static void langwell_otg_work(struct work_struct *work) lnw->otg.state = OTG_STATE_B_IDLE; queue_work(lnw->qwork, &lnw->work); } else if (!lnw->hsm.a_vbus_vld) { - langwell_otg_del_timer(b_bus_suspend_tmr); + /* delete hsm timer for b_bus_suspend_tmr */ + del_timer_sync(&lnw->hsm_timer); if (lnw->client_ops) lnw->client_ops->suspend(lnw->pdev, PMSG_FREEZE); @@ -1382,7 +1458,9 @@ static void langwell_otg_work(struct work_struct *work) langwell_otg_phy_low_power_wait(1); lnw->otg.state = OTG_STATE_A_VBUS_ERR; } else if (lnw->hsm.a_bus_drop) { - langwell_otg_del_timer(b_bus_suspend_tmr); + /* delete hsm timer for b_bus_suspend_tmr */ + del_timer_sync(&lnw->hsm_timer); + if (lnw->client_ops) lnw->client_ops->suspend(lnw->pdev, PMSG_FREEZE); @@ -1392,7 +1470,8 @@ static void langwell_otg_work(struct work_struct *work) langwell_otg_drv_vbus(0); lnw->otg.state = OTG_STATE_A_WAIT_VFALL; } else if (lnw->hsm.b_bus_suspend) { - langwell_otg_del_timer(b_bus_suspend_tmr); + /* delete hsm timer for b_bus_suspend_tmr */ + del_timer_sync(&lnw->hsm_timer); if (lnw->client_ops) lnw->client_ops->suspend(lnw->pdev, PMSG_FREEZE); @@ -1405,8 +1484,7 @@ static void langwell_otg_work(struct work_struct *work) else dev_dbg(&lnw->pdev->dev, "host driver not loaded.\n"); - lnw->hsm.a_wait_bcon_tmout = 0; - langwell_otg_add_timer(a_wait_bcon_tmr); + langwell_otg_add_ktimer(TA_WAIT_BCON_TMR); lnw->otg.state = OTG_STATE_A_WAIT_BCON; } else if (lnw->hsm.b_bus_suspend_tmout) { u32 val; @@ -1425,8 +1503,7 @@ static void langwell_otg_work(struct work_struct *work) else dev_dbg(&lnw->pdev->dev, "host driver not loaded.\n"); - lnw->hsm.a_wait_bcon_tmout = 0; - langwell_otg_add_timer(a_wait_bcon_tmr); + langwell_otg_add_ktimer(TA_WAIT_BCON_TMR); lnw->otg.state = OTG_STATE_A_WAIT_BCON; } break; @@ -2030,7 +2107,7 @@ static int langwell_otg_suspend(struct pci_dev *pdev, pm_message_t message) transceiver_suspend(pdev); break; case OTG_STATE_A_WAIT_BCON: - langwell_otg_del_timer(a_wait_bcon_tmr); + del_timer_sync(&langwell->hsm_timer); if (langwell->host_ops) ret = langwell->host_ops->suspend(pdev, message); else @@ -2057,6 +2134,7 @@ static int langwell_otg_suspend(struct pci_dev *pdev, pm_message_t message) langwell->otg.state = OTG_STATE_A_WAIT_VFALL; break; case OTG_STATE_A_PERIPHERAL: + del_timer_sync(&langwell->hsm_timer); if (langwell->client_ops) ret = langwell->client_ops->suspend(pdev, message); else @@ -2083,7 +2161,8 @@ static int langwell_otg_suspend(struct pci_dev *pdev, pm_message_t message) "client driver has been removed.\n"); break; case OTG_STATE_B_WAIT_ACON: - langwell_otg_del_timer(b_ase0_brst_tmr); + /* delete hsm timer for b_ase0_brst_tmr */ + del_timer_sync(&langwell->hsm_timer); langwell_otg_HAAR(0); if (langwell->host_ops) langwell->host_ops->remove(pdev); @@ -2124,7 +2203,7 @@ static int langwell_otg_resume(struct pci_dev *pdev) transceiver_resume(pdev); break; case OTG_STATE_A_WAIT_BCON: - langwell_otg_add_timer(a_wait_bcon_tmr); + langwell_otg_add_ktimer(TA_WAIT_BCON_TMR); langwell_otg_drv_vbus(1); if (langwell->host_ops) ret = langwell->host_ops->resume(pdev); diff --git a/include/linux/usb/langwell_otg.h b/include/linux/usb/langwell_otg.h index 64ff588..69977c2 100644 --- a/include/linux/usb/langwell_otg.h +++ b/include/linux/usb/langwell_otg.h @@ -120,7 +120,8 @@ struct otg_hsm { int a_aidl_bdis_tmout; int b_ase0_brst_tmout; int b_bus_suspend_tmout; - int b_srp_res_tmout; + int b_srp_init_tmout; + int b_srp_fail_tmout; /* Informative variables */ int a_bus_drop; @@ -139,12 +140,24 @@ struct otg_hsm { int vbus_srp_up; }; -#define TA_WAIT_VRISE 200 +enum langwell_otg_timer_type { + TA_WAIT_VRISE_TMR, + TA_WAIT_BCON_TMR, + TA_AIDL_BDIS_TMR, + TB_ASE0_BRST_TMR, + TB_SE0_SRP_TMR, + TB_SRP_INIT_TMR, + TB_SRP_FAIL_TMR, + TB_BUS_SUSPEND_TMR +}; + +#define TA_WAIT_VRISE 100 #define TA_WAIT_BCON 30000 #define TA_AIDL_BDIS 15000 #define TB_ASE0_BRST 5000 #define TB_SE0_SRP 2 -#define TB_SRP_RES 100 +#define TB_SRP_INIT 100 +#define TB_SRP_FAIL 5500 #define TB_BUS_SUSPEND 500 struct langwell_otg_timer { @@ -167,6 +180,7 @@ struct langwell_otg { struct pci_dev *pdev; struct work_struct work; struct workqueue_struct *qwork; + struct timer_list hsm_timer; spinlock_t lock; spinlock_t wq_lock; }; -- To unsubscribe from this list: send the line "unsubscribe linux-usb" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html