SRP(Session Request Protocol) is one OTG feature that used to request power supply from the connected OTG device. This penwell_otg transceiver driver patch enables SRP function per OTG 2.0 spec requirement. Signed-off-by: Hao Wu <hao.wu@xxxxxxxxx> --- drivers/usb/otg/penwell_otg.c | 156 +++++++++++++++++++++++++++++++------- include/linux/usb/penwell_otg.h | 13 +++- 2 files changed, 138 insertions(+), 31 deletions(-) diff --git a/drivers/usb/otg/penwell_otg.c b/drivers/usb/otg/penwell_otg.c index e26393c..2c1d027 100644 --- a/drivers/usb/otg/penwell_otg.c +++ b/drivers/usb/otg/penwell_otg.c @@ -62,6 +62,9 @@ static int penwell_otg_set_host(struct otg_transceiver *otg, static int penwell_otg_set_peripheral(struct otg_transceiver *otg, struct usb_gadget *gadget); static int penwell_otg_start_srp(struct otg_transceiver *otg); +static void penwell_otg_mon_bus(unsigned long indicator); +static void penwell_otg_mon_bus_fn(unsigned long indicator); + static int penwell_otg_msic_write(u16 addr, u8 data); static const char *charger_string(enum usb_charger_type charger) @@ -485,6 +488,86 @@ static enum msic_vendor penwell_otg_check_msic(void) return MSIC_VD_TI; } +/* Monitor function check if SRP initial conditions. Use polling on current + * status for b_ssend_srp, b_se0_srp */ +static void penwell_otg_mon_bus(unsigned long indicator) +{ + struct penwell_otg *pnw = the_transceiver; + unsigned long j = jiffies; + + switch (indicator) { + case BUS_MON_START: + /* reset all parameters when start monitoring */ + pnw->b_ssend_srp_time = 0; + pnw->b_se0_srp_time = 0; + pnw->iotg.hsm.b_ssend_srp = 0; + pnw->iotg.hsm.b_se0_srp = 0; + case BUS_MON_CONTINUE: + pnw->bus_mon_timer.data = indicator; + pnw->bus_mon_timer.function = penwell_otg_mon_bus_fn; + pnw->bus_mon_timer.expires = j + SRP_MON_INVAL * HZ / 1000; + + add_timer(&pnw->bus_mon_timer); + break; + case BUS_MON_STOP: + del_timer_sync(&pnw->bus_mon_timer); + break; + default: + dev_dbg(pnw->dev, "unknown bus monitor action\n"); + break; + } +} + +/* monitor function to see if srp initial conditions are met or not */ +static void penwell_otg_mon_bus_fn(unsigned long indicator) +{ + struct penwell_otg *pnw = the_transceiver; + u32 val; + + /* For b_ssend_srp, poll OTGSC.BSE bit to + * make sure in session end state */ + val = readl(pnw->iotg.base + CI_OTGSC); + if (val & OTGSC_BSE) + pnw->b_ssend_srp_time += SRP_MON_INVAL; + else { + pnw->b_ssend_srp_time = 0; + pnw->iotg.hsm.b_ssend_srp = 0; + dev_dbg(pnw->dev, "b_ssend_srp = 0\n"); + } + + if (pnw->b_ssend_srp_time >= TB_SSEND_SRP) { + pnw->b_ssend_srp_time = TB_SSEND_SRP; + if (pnw->iotg.hsm.b_ssend_srp != 1) { + dev_dbg(pnw->dev, "b_ssend_srp = 1\n"); + pnw->iotg.hsm.b_ssend_srp = 1; + penwell_update_transceiver(); + } + } + + /* For b_se0_srp, poll PORTSC.LS bits to + * make sure in data line se0 state */ + val = readl(pnw->iotg.base + CI_PORTSC1); + if (val & PORTSC_LS) { + pnw->b_se0_srp_time = 0; + pnw->iotg.hsm.b_se0_srp = 0; + dev_dbg(pnw->dev, "b_se0_srp = 0\n"); + } else { + /* LS = SE0 */ + pnw->b_se0_srp_time += SRP_MON_INVAL; + } + + if (pnw->b_se0_srp_time >= TB_SE0_SRP) { + pnw->b_se0_srp_time = TB_SE0_SRP; + if (pnw->iotg.hsm.b_se0_srp != 1) { + dev_dbg(pnw->dev, "b_se0_srp = 1\n"); + pnw->iotg.hsm.b_se0_srp = 1; + penwell_update_transceiver(); + } + } + + penwell_otg_mon_bus(BUS_MON_CONTINUE); +} + /* Start SRP function */ static int penwell_otg_start_srp(struct otg_transceiver *otg) { @@ -1011,7 +1094,7 @@ static void init_hsm(void) iotg->hsm.a_bus_req = 1; iotg->hsm.a_bus_drop = 0; /* init hsm means power_up case */ - iotg->hsm.power_up = 0; + iotg->hsm.power_up = 1; /* defautly don't request bus as B device */ iotg->hsm.b_bus_req = 0; /* no system error */ @@ -1019,6 +1102,8 @@ static void init_hsm(void) penwell_otg_phy_low_power(1); + if (iotg->otg.state == OTG_STATE_B_IDLE) + penwell_otg_mon_bus(BUS_MON_START); } static void update_hsm(void) @@ -1179,11 +1264,17 @@ penwell_otg_notify(struct otg_transceiver *otg, enum usb_otg_events event) break; case USB_OTG_HOST_SUSP: dev_dbg(pnw->dev, "PNW OTG Notify Host Bus suspend Event\n"); - flag = 0; + if (iotg->otg.default_a == 1) + iotg->hsm.a_bus_req = 0; + else + iotg->hsm.b_bus_req = 0; + flag = 1; break; case USB_OTG_HOST_RESU: dev_dbg(pnw->dev, "PNW OTG Notify Host Bus resume Event\n"); - flag = 0; + if (iotg->otg.default_a == 1) + iotg->hsm.a_bus_req = 1; + flag = 1; break; case USB_OTG_DEV_SUSP: dev_dbg(pnw->dev, "PNW OTG Notify Client Bus suspend Event\n"); @@ -1315,7 +1406,8 @@ static void penwell_otg_work(struct work_struct *work) iotg->otg.default_a = 1; hsm->a_srp_det = 0; set_host_mode(); - penwell_otg_phy_low_power(1); + penwell_otg_phy_low_power(0); + penwell_otg_mon_bus(BUS_MON_STOP); /* Always set a_bus_req to 1, in case no ADP */ hsm->a_bus_req = 1; @@ -1350,8 +1442,7 @@ static void penwell_otg_work(struct work_struct *work) } /* Clear power_up */ - if (hsm->power_up) - hsm->power_up = 0; + hsm->power_up = 0; /* Move to B_PERIPHERAL state, Session Valid */ @@ -1362,6 +1453,7 @@ static void penwell_otg_work(struct work_struct *work) hsm->a_bus_suspend = 0; /* Start USB Battery charger detection flow */ + penwell_otg_mon_bus(BUS_MON_STOP); mutex_lock(&pnw->msic_mutex); /* Enable data contact detection */ @@ -1426,14 +1518,10 @@ static void penwell_otg_work(struct work_struct *work) } else if ((hsm->b_bus_req || hsm->power_up || hsm->adp_change) && !hsm->b_srp_fail_tmr) { - if ((hsm->b_ssend_srp && hsm->b_se0_srp) || - hsm->adp_change || hsm->power_up) { + if (hsm->b_ssend_srp && hsm->b_se0_srp) { - if (hsm->power_up) - hsm->power_up = 0; - - if (hsm->adp_change) - hsm->adp_change = 0; + hsm->power_up = 0; + hsm->adp_change = 0; /* clear the PHCD before start srp */ penwell_otg_phy_low_power(0); @@ -1497,9 +1585,8 @@ static void penwell_otg_work(struct work_struct *work) dev_dbg(pnw->dev, "client driver has been removed.\n"); - hsm->b_ssend_srp = 1; - hsm->b_se0_srp = 1; penwell_otg_phy_low_power(1); + penwell_otg_mon_bus(BUS_MON_START); iotg->otg.state = OTG_STATE_B_IDLE; } else if (hsm->b_bus_req && hsm->b_hnp_enable @@ -1586,9 +1673,7 @@ static void penwell_otg_work(struct work_struct *work) set_client_mode(); penwell_otg_phy_low_power(1); - - hsm->b_ssend_srp = 1; - hsm->b_se0_srp = 1; + penwell_otg_mon_bus(BUS_MON_START); iotg->otg.state = OTG_STATE_B_IDLE; } else if (hsm->a_conn) { @@ -1678,9 +1763,7 @@ static void penwell_otg_work(struct work_struct *work) set_client_mode(); penwell_otg_phy_low_power(1); - - hsm->b_ssend_srp = 1; - hsm->b_se0_srp = 1; + penwell_otg_mon_bus(BUS_MON_START); iotg->otg.state = OTG_STATE_B_IDLE; } else if (!hsm->b_bus_req || !hsm->a_conn @@ -1720,9 +1803,7 @@ static void penwell_otg_work(struct work_struct *work) set_client_mode(); usleep_range(4000, 5000); penwell_otg_phy_low_power(1); - - hsm->b_ssend_srp = 1; - hsm->b_se0_srp = 1; + penwell_otg_mon_bus(BUS_MON_START); iotg->otg.state = OTG_STATE_B_IDLE; penwell_update_transceiver(); @@ -1750,7 +1831,7 @@ static void penwell_otg_work(struct work_struct *work) } iotg->otg.state = OTG_STATE_A_WAIT_BCON; } else if (!hsm->a_bus_drop && (hsm->power_up || hsm->a_bus_req - || hsm->power_up || hsm->adp_change)) { + || hsm->a_srp_det || hsm->adp_change)) { /* power up / adp changes / srp detection should be * cleared at once after handled. */ if (hsm->power_up) @@ -1770,7 +1851,8 @@ static void penwell_otg_work(struct work_struct *work) penwell_update_transceiver(); } else if (hsm->b_sess_end || hsm->a_sess_vld || - !hsm->b_sess_vld) { + hsm->a_srp_det || !hsm->b_sess_vld) { + hsm->a_srp_det = 0; dev_dbg(pnw->dev, "reconfig...PHCD bit for PHY low power mode\n"); penwell_otg_phy_low_power(1); @@ -1791,7 +1873,8 @@ static void penwell_otg_work(struct work_struct *work) penwell_otg_add_timer(TA_WAIT_VFALL_TMR); iotg->otg.state = OTG_STATE_A_WAIT_VFALL; - } else if (hsm->a_wait_vrise_tmout || hsm->id == ID_ACA_A) { + } else if (hsm->a_vbus_vld || hsm->a_wait_vrise_tmout + || hsm->id == ID_ACA_A) { /* Move to A_WAIT_BCON state, a vbus vld */ /* Delete current timer and clear flags */ penwell_otg_del_timer(TA_WAIT_VRISE_TMR); @@ -1813,6 +1896,7 @@ static void penwell_otg_work(struct work_struct *work) CHRG_CURR_ACA); } + hsm->a_bus_req = 1; hsm->b_conn = 0; hsm->hnp_poll_enable = 0; @@ -2121,6 +2205,8 @@ static void penwell_otg_work(struct work_struct *work) break; case OTG_STATE_A_WAIT_VFALL: if (hsm->a_wait_vfall_tmout) { + hsm->a_srp_det = 0; + /* Move to A_IDLE state, vbus falls */ penwell_otg_phy_low_power(1); @@ -2631,6 +2717,7 @@ static int penwell_otg_probe(struct pci_dev *pdev, pnw->iotg.ulpi_ops.write = penwell_otg_ulpi_write; init_timer(&pnw->hsm_timer); + init_timer(&pnw->bus_mon_timer); init_timer(&pnw->hnp_poll_timer); init_completion(&pnw->adp.adp_comp); @@ -2740,9 +2827,9 @@ static void penwell_otg_remove(struct pci_dev *pdev) static void transceiver_suspend(struct pci_dev *pdev) { + penwell_otg_phy_low_power(1); pci_save_state(pdev); pci_set_power_state(pdev, PCI_D3hot); - penwell_otg_phy_low_power(1); } static int penwell_otg_suspend(struct pci_dev *pdev, pm_message_t message) @@ -2767,10 +2854,13 @@ static int penwell_otg_suspend(struct pci_dev *pdev, pm_message_t message) case OTG_STATE_A_WAIT_VFALL: iotg->otg.state = OTG_STATE_A_IDLE; case OTG_STATE_A_IDLE: - case OTG_STATE_B_IDLE: case OTG_STATE_A_VBUS_ERR: transceiver_suspend(pdev); break; + case OTG_STATE_B_IDLE: + penwell_otg_mon_bus(BUS_MON_STOP); + transceiver_suspend(pdev); + break; case OTG_STATE_A_WAIT_VRISE: penwell_otg_del_timer(TA_WAIT_VRISE_TMR); iotg->hsm.a_srp_det = 0; @@ -2901,6 +2991,14 @@ static int penwell_otg_resume(struct pci_dev *pdev) goto error; } + switch (pnw->iotg.otg.state) { + case OTG_STATE_B_IDLE: + penwell_otg_mon_bus(BUS_MON_START); + break; + default: + break; + } + /* enable OTG interrupts */ penwell_otg_intr(1); diff --git a/include/linux/usb/penwell_otg.h b/include/linux/usb/penwell_otg.h index f4e8d5b..3361d01 100644 --- a/include/linux/usb/penwell_otg.h +++ b/include/linux/usb/penwell_otg.h @@ -239,9 +239,9 @@ enum penwell_otg_timer_type { #define TA_BIDL_ADIS 300 #define TA_WAIT_VFALL 950 #define TB_ASE0_BRST 300 -#define TB_SE0_SRP 1800 +#define TB_SE0_SRP 1200 #define TB_SSEND_SRP 1800 -# define SRP_MON_INVAL 200 +# define SRP_MON_INVAL 300 #define TB_SRP_FAIL 5500 #define TB_BUS_SUSPEND 500 #define THOS_REQ_POL 1500 @@ -282,6 +282,11 @@ struct otg_bc_cap { #define CHRG_CURR_ACA 1500 }; +/* Bus monitor action for b_ssend_srp/b_se0_srp */ +#define BUS_MON_STOP 0 +#define BUS_MON_START 1 +#define BUS_MON_CONTINUE 2 + /* define event ids to notify battery driver */ #define USBCHRG_EVENT_CONNECT 1 #define USBCHRG_EVENT_DISCONN 2 @@ -302,6 +307,10 @@ struct penwell_otg { struct timer_list hsm_timer; struct timer_list hnp_poll_timer; + struct timer_list bus_mon_timer; + + unsigned long b_se0_srp_time; + unsigned long b_ssend_srp_time; struct mutex msic_mutex; enum msic_vendor msic; -- 1.6.0.6 -- 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