This penwell_otg transceiver driver patch enables MFLD USB OTG SRP function per OTG 2.0 spec requirement.
Signed-off-by: Hao Wu <[email protected]> --- 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 229a1ee..0b29710 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 *state_string(enum usb_otg_state state) @@ -517,6 +520,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) { @@ -1043,7 +1126,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 */ @@ -1051,6 +1134,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) @@ -1217,11 +1302,17 @@ static int penwell_otg_iotg_notify(struct notifier_block *nb, break; case MID_OTG_NOTIFY_HSUSPEND: 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 MID_OTG_NOTIFY_HRESUME: 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 MID_OTG_NOTIFY_CSUSPEND: dev_dbg(pnw->dev, "PNW OTG Notify Client Bus suspend Event\n"); @@ -1353,7 +1444,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; @@ -1388,8 +1480,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 */ @@ -1400,6 +1491,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 */ @@ -1464,14 +1556,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); @@ -1535,9 +1623,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 @@ -1624,9 +1711,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) { @@ -1716,9 +1801,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 @@ -1758,9 +1841,7 @@ static void penwell_otg_work(struct work_struct *work) set_client_mode(); msleep(5); 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(); @@ -1788,7 +1869,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) @@ -1808,7 +1889,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); @@ -1829,7 +1911,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); @@ -1851,6 +1934,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; @@ -2159,6 +2243,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); @@ -2668,6 +2754,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); @@ -2794,9 +2881,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) @@ -2821,10 +2908,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; @@ -2955,6 +3045,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 _______________________________________________ MeeGo-kernel mailing list [email protected] http://lists.meego.com/listinfo/meego-kernel
