From: Hao Wu <hao.wu@xxxxxxxxx> Add MHL support in penwell_otg transceiver driver including pm related support. Use a new MHL state for MHL device connected state, in this new state, usb will stop activity on the bus. Signed-off-by: Hao Wu <hao.wu@xxxxxxxxx> --- drivers/usb/otg/penwell_otg.c | 164 ++++++++++++++++++++++++++++++++++----- include/linux/usb/penwell_otg.h | 2 +- 2 files changed, 145 insertions(+), 21 deletions(-) diff --git a/drivers/usb/otg/penwell_otg.c b/drivers/usb/otg/penwell_otg.c index e2b12d3..3b8fa77 100644 --- a/drivers/usb/otg/penwell_otg.c +++ b/drivers/usb/otg/penwell_otg.c @@ -63,6 +63,9 @@ 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 int penwell_otg_msic_write(u16 addr, u8 data); +static void penwell_otg_intr(int on); +static void penwell_otg_add_timer(enum penwell_otg_timer_type timers); +static void update_hsm(void); static const char *state_string(enum usb_otg_state state) { @@ -91,6 +94,8 @@ static const char *state_string(enum usb_otg_state state) return "b_wait_acon"; case OTG_STATE_B_HOST: return "b_host"; + case OTG_STATE_MHL: + return "mhl"; default: return "UNDEFINED"; } @@ -540,6 +545,111 @@ static int penwell_otg_start_srp(struct otg_transceiver *otg) return 0; } +/* Enter MHL mode */ +static int penwell_otg_enter_mhl_mode(struct otg_transceiver *otg) +{ + struct penwell_otg *pnw = the_transceiver; + + dev_dbg(pnw->dev, "%s --->\n", __func__); + + pm_runtime_get_sync(pnw->dev); + + mutex_lock(&pnw->iotg.otg.state_mutex); + + /* stop otg interrupt */ + penwell_otg_intr(0); + + /* move to OTG_MHL state */ + pnw->iotg.otg.state = OTG_STATE_MHL; + + mutex_unlock(&pnw->iotg.otg.state_mutex); + + pm_runtime_put_sync(pnw->dev); + + dev_dbg(pnw->dev, "%s <---\n", __func__); + return 0; +} + +/* called with otg.state_mutex held */ +static int _penwell_otg_enter_mhl_mode(struct otg_transceiver *otg) +{ + struct penwell_otg *pnw = the_transceiver; + + dev_dbg(pnw->dev, "%s --->\n", __func__); + + /* stop otg interrupt */ + penwell_otg_intr(0); + + /* move to OTG_MHL state */ + pnw->iotg.otg.state = OTG_STATE_MHL; + + dev_dbg(pnw->dev, "%s <---\n", __func__); + return 0; +} + +/* Exit MHL mode */ +static int penwell_otg_exit_mhl_mode(struct otg_transceiver *otg) +{ + struct penwell_otg *pnw = the_transceiver; + + dev_dbg(pnw->dev, "%s --->\n", __func__); + + mutex_lock(&pnw->iotg.otg.state_mutex); + + if (pnw->iotg.otg.state != OTG_STATE_MHL) + return -EINVAL; + + /* enable otg interrupt */ + penwell_otg_intr(1); + + pnw->iotg.hsm.power_up = 0; + pnw->iotg.hsm.adp_change = 0; + pnw->iotg.hsm.a_srp_det = 0; + + if (pnw->iotg.otg.set_vbus) + pnw->iotg.otg.set_vbus(&pnw->iotg.otg, true); + + penwell_otg_add_timer(TA_WAIT_VRISE_TMR); + pnw->iotg.otg.state = OTG_STATE_A_WAIT_VRISE; + + update_hsm(); + penwell_update_transceiver(); + + mutex_unlock(&pnw->iotg.otg.state_mutex); + dev_dbg(pnw->dev, "%s <---\n", __func__); + return 0; +} + +/* called with otg.state_mutex held */ +static int _penwell_otg_exit_mhl_mode(struct otg_transceiver *otg) +{ + struct penwell_otg *pnw = the_transceiver; + + dev_dbg(pnw->dev, "%s --->\n", __func__); + + if (pnw->iotg.otg.state != OTG_STATE_MHL) + return -EINVAL; + + /* enable otg interrupt */ + penwell_otg_intr(1); + + pnw->iotg.hsm.power_up = 0; + pnw->iotg.hsm.adp_change = 0; + pnw->iotg.hsm.a_srp_det = 0; + + if (pnw->iotg.otg.set_vbus) + pnw->iotg.otg.set_vbus(&pnw->iotg.otg, true); + + penwell_otg_add_timer(TA_WAIT_VRISE_TMR); + pnw->iotg.otg.state = OTG_STATE_A_WAIT_VRISE; + + update_hsm(); + penwell_update_transceiver(); + + dev_dbg(pnw->dev, "%s <---\n", __func__); + return 0; +} + /* The timeout callback function to poll the host request flag */ static void penwell_otg_hnp_poll_fn(unsigned long indicator) { @@ -1011,6 +1121,8 @@ static void init_hsm(void) struct intel_mid_otg_xceiv *iotg = &pnw->iotg; u32 val32; + mutex_lock(&iotg->otg.state_mutex); + /* read OTGSC after reset */ val32 = readl(iotg->base + CI_OTGSC); dev_dbg(pnw->dev, @@ -1051,6 +1163,7 @@ static void init_hsm(void) penwell_otg_phy_low_power(1); + mutex_unlock(&iotg->otg.state_mutex); } static void update_hsm(void) @@ -1293,6 +1406,7 @@ static void penwell_otg_work(struct work_struct *work) "old state = %s\n", state_string(iotg->otg.state)); pm_runtime_get_sync(pnw->dev); + mutex_lock(&iotg->otg.state_mutex); switch (iotg->otg.state) { case OTG_STATE_UNDEFINED: @@ -1744,22 +1858,14 @@ static void penwell_otg_work(struct work_struct *work) || hsm->power_up || hsm->adp_change)) { /* power up / adp changes / srp detection should be * cleared at once after handled. */ - if (hsm->power_up) - hsm->power_up = 0; - - if (hsm->adp_change) - hsm->adp_change = 0; - - if (hsm->a_srp_det) - hsm->a_srp_det = 0; - - if (iotg->otg.set_vbus) - iotg->otg.set_vbus(&iotg->otg, true); - - penwell_otg_add_timer(TA_WAIT_VRISE_TMR); - iotg->otg.state = OTG_STATE_A_WAIT_VRISE; - - penwell_update_transceiver(); + _penwell_otg_enter_mhl_mode(&iotg->otg); + if (otg_mhl_notify(&iotg->otg, 1)) { + _penwell_otg_exit_mhl_mode(&iotg->otg); + penwell_update_transceiver(); + } else { + dev_dbg(pnw->dev, "Now in MHL mode\n"); + break; + } } else if (hsm->b_sess_end || hsm->a_sess_vld || !hsm->b_sess_vld) { dev_dbg(pnw->dev, @@ -1819,7 +1925,8 @@ static void penwell_otg_work(struct work_struct *work) } break; case OTG_STATE_A_WAIT_BCON: - if (hsm->id == ID_B || hsm->id == ID_ACA_B || hsm->a_bus_drop) { + if (hsm->id == ID_B || hsm->id == ID_ACA_B || hsm->a_bus_drop || + hsm->a_wait_bcon_tmout) { /* Move to A_WAIT_VFALL state, user request */ /* Delete current timer and clear flags for B-Device */ @@ -1984,7 +2091,6 @@ static void penwell_otg_work(struct work_struct *work) penwell_otg_del_timer(TA_AIDL_BDIS_TMR); /* add kernel timer */ - penwell_otg_add_timer(TA_WAIT_BCON_TMR); iotg->otg.state = OTG_STATE_A_WAIT_BCON; } else if (!hsm->b_conn && pnw->iotg.otg.host->b_hnp_enable) { /* Move to A_PERIPHERAL state, HNP */ @@ -2082,7 +2188,6 @@ static void penwell_otg_work(struct work_struct *work) dev_dbg(pnw->dev, "host driver not loaded.\n"); - penwell_otg_add_timer(TA_WAIT_BCON_TMR); iotg->otg.state = OTG_STATE_A_WAIT_BCON; } else if (!hsm->b_bus_suspend && hsm->a_bidl_adis_tmr) { /* Client report suspend state end, delete timer */ @@ -2126,6 +2231,7 @@ static void penwell_otg_work(struct work_struct *work) ; } + mutex_unlock(&iotg->otg.state_mutex); pm_runtime_put_sync(pnw->dev); dev_dbg(pnw->dev, @@ -2559,6 +2665,8 @@ static int penwell_otg_probe(struct pci_dev *pdev, pnw->iotg.otg.set_power = penwell_otg_set_power; pnw->iotg.otg.set_vbus = penwell_otg_set_vbus; pnw->iotg.otg.start_srp = penwell_otg_start_srp; + pnw->iotg.otg.enter_mhl_mode = penwell_otg_enter_mhl_mode; + pnw->iotg.otg.exit_mhl_mode = penwell_otg_exit_mhl_mode; pnw->iotg.set_adp_probe = NULL; pnw->iotg.set_adp_sense = NULL; pnw->iotg.otg.state = OTG_STATE_UNDEFINED; @@ -2571,6 +2679,9 @@ static int penwell_otg_probe(struct pci_dev *pdev, pnw->iotg.ulpi_ops.read = penwell_otg_ulpi_read; pnw->iotg.ulpi_ops.write = penwell_otg_ulpi_write; + mutex_init(&pnw->iotg.otg.state_mutex); + mutex_init(&pnw->iotg.otg.mhl_mutex); + init_timer(&pnw->hsm_timer); init_timer(&pnw->hnp_poll_timer); init_completion(&pnw->adp.adp_comp); @@ -2695,6 +2806,13 @@ static int penwell_otg_suspend(struct pci_dev *pdev, pm_message_t message) struct intel_mid_otg_xceiv *iotg = &pnw->iotg; int ret = 0; + mutex_lock(&iotg->otg.state_mutex); + if (iotg->otg.state == OTG_STATE_MHL) { + mutex_unlock(&iotg->otg.state_mutex); + return -EBUSY; + } + mutex_unlock(&iotg->otg.state_mutex); + /* Disbale OTG interrupts */ penwell_otg_intr(0); @@ -2706,6 +2824,7 @@ static int penwell_otg_suspend(struct pci_dev *pdev, pm_message_t message) destroy_workqueue(pnw->qwork); pnw->qwork = NULL; + mutex_lock(&iotg->otg.state_mutex); /* start actions */ switch (iotg->otg.state) { case OTG_STATE_A_WAIT_VFALL: @@ -2814,6 +2933,7 @@ static int penwell_otg_suspend(struct pci_dev *pdev, pm_message_t message) dev_dbg(pnw->dev, "error state before suspend\n"); break; } + mutex_unlock(&iotg->otg.state_mutex); return ret; } @@ -2874,6 +2994,7 @@ static int penwell_otg_runtime_suspend(struct device *dev) switch (pnw->iotg.otg.state) { case OTG_STATE_A_IDLE: case OTG_STATE_B_IDLE: + case OTG_STATE_MHL: /* Transceiver handle it itself */ penwell_otg_phy_low_power(1); pci_save_state(pdev); @@ -2909,13 +3030,14 @@ static int penwell_otg_runtime_resume(struct device *dev) pdev = to_pci_dev(dev); - penwell_otg_intr(1); penwell_otg_phy_low_power(0); switch (pnw->iotg.otg.state) { case OTG_STATE_A_IDLE: case OTG_STATE_B_IDLE: /* Transceiver handle it itself */ + penwell_otg_intr(1); + case OTG_STATE_MHL: pci_set_power_state(pdev, PCI_D0); pci_restore_state(pdev); ret = pci_enable_device(pdev); @@ -2926,11 +3048,13 @@ static int penwell_otg_runtime_resume(struct device *dev) case OTG_STATE_A_WAIT_BCON: case OTG_STATE_A_HOST: case OTG_STATE_A_SUSPEND: + penwell_otg_intr(1); if (pnw->iotg.runtime_resume_host) ret = pnw->iotg.runtime_resume_host(&pnw->iotg); break; case OTG_STATE_A_PERIPHERAL: case OTG_STATE_B_PERIPHERAL: + penwell_otg_intr(1); if (pnw->iotg.runtime_resume_peripheral) ret = pnw->iotg.runtime_resume_peripheral(&pnw->iotg); break; diff --git a/include/linux/usb/penwell_otg.h b/include/linux/usb/penwell_otg.h index 77776ca..f4284d0 100644 --- a/include/linux/usb/penwell_otg.h +++ b/include/linux/usb/penwell_otg.h @@ -229,7 +229,7 @@ enum penwell_otg_timer_type { }; #define TA_WAIT_VRISE 100 -#define TA_WAIT_BCON 30000 +#define TA_WAIT_BCON 15000 #define TA_AIDL_BDIS 1500 #define TA_BIDL_ADIS 300 #define TA_WAIT_VFALL 950 -- 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