From: Hao Wu <hao.wu@xxxxxxxxx> Update PCI suspend/resume function to fix suspend/resume issue. This modification will make sure in D3, Vbus is powered off, both client/host function will be stopped and controller moves to OTG IDLE state. Once it resumes back to work, state machine will restart from the IDLE state, and move to host/client based on inputs event. Main changes - Remove hw timer irq from irq enable/disable function used in suspend/resume. - Stop client/host function and move to OTG IDLE state in suspend. - Remove useless spinlock in suspend/resume function. Signed-off-by: Hao Wu <hao.wu@xxxxxxxxx> Signed-off-by: Alan Cox <alan@xxxxxxxxxxxxxxx> --- drivers/usb/otg/langwell_otg.c | 141 ++++++++++++++++++-------------------- include/linux/usb/langwell_otg.h | 1 2 files changed, 68 insertions(+), 74 deletions(-) diff --git a/drivers/usb/otg/langwell_otg.c b/drivers/usb/otg/langwell_otg.c index 62d9828..852b5df 100644 --- a/drivers/usb/otg/langwell_otg.c +++ b/drivers/usb/otg/langwell_otg.c @@ -406,10 +406,10 @@ static void langwell_otg_intr(int on) val = readl(lnw->regs + CI_OTGSC); if (on) { - val = val | (OTGSC_INTEN_MASK); + val = val | (OTGSC_INT_MASK); writel(val, lnw->regs + CI_OTGSC); } else { - val = val & ~(OTGSC_INTEN_MASK); + val = val & ~(OTGSC_INT_MASK); writel(val, lnw->regs + CI_OTGSC); } dev_dbg(&lnw->pdev->dev, "%s <---\n", __func__); @@ -2105,105 +2105,123 @@ static void transceiver_suspend(struct pci_dev *pdev) static int langwell_otg_suspend(struct pci_dev *pdev, pm_message_t message) { - struct langwell_otg *langwell = the_transceiver; + struct langwell_otg *lnw = the_transceiver; int ret = 0; /* Disbale OTG interrupts */ langwell_otg_intr(0); if (pdev->irq) - free_irq(pdev->irq, langwell); + free_irq(pdev->irq, lnw); /* Prevent more otg_work */ - flush_workqueue(langwell->qwork); - spin_lock(&langwell->wq_lock); + flush_workqueue(lnw->qwork); + destroy_workqueue(lnw->qwork); + lnw->qwork = NULL; /* start actions */ - switch (langwell->otg.state) { + switch (lnw->otg.state) { + case OTG_STATE_A_WAIT_VFALL: + lnw->otg.state = OTG_STATE_A_IDLE; case OTG_STATE_A_IDLE: case OTG_STATE_B_IDLE: - case OTG_STATE_A_WAIT_VFALL: case OTG_STATE_A_VBUS_ERR: transceiver_suspend(pdev); break; case OTG_STATE_A_WAIT_VRISE: langwell_otg_del_timer(a_wait_vrise_tmr); - langwell->hsm.a_srp_det = 0; + lnw->hsm.a_srp_det = 0; + langwell_otg_drv_vbus(0); - langwell->otg.state = OTG_STATE_A_IDLE; + lnw->otg.state = OTG_STATE_A_IDLE; transceiver_suspend(pdev); break; case OTG_STATE_A_WAIT_BCON: - del_timer_sync(&langwell->hsm_timer); - if (langwell->host_ops) - ret = langwell->host_ops->suspend(pdev, message); + del_timer_sync(&lnw->hsm_timer); + if (lnw->host_ops) + lnw->host_ops->remove(pdev); else dev_dbg(&pdev->dev, "host driver has been removed.\n"); + + lnw->hsm.a_srp_det = 0; + langwell_otg_drv_vbus(0); + + lnw->otg.state = OTG_STATE_A_IDLE; + transceiver_suspend(pdev); break; case OTG_STATE_A_HOST: - if (langwell->host_ops) - ret = langwell->host_ops->suspend(pdev, message); + if (lnw->host_ops) + lnw->host_ops->remove(pdev); else dev_dbg(&pdev->dev, "host driver has been removed.\n"); + + lnw->hsm.a_srp_det = 0; + langwell_otg_drv_vbus(0); - langwell_otg_phy_low_power(1); + lnw->otg.state = OTG_STATE_A_IDLE; + transceiver_suspend(pdev); break; case OTG_STATE_A_SUSPEND: langwell_otg_del_timer(a_aidl_bdis_tmr); langwell_otg_HABA(0); - if (langwell->host_ops) - langwell->host_ops->remove(pdev); + if (lnw->host_ops) + lnw->host_ops->remove(pdev); else dev_dbg(&pdev->dev, "host driver has been removed.\n"); + lnw->hsm.a_srp_det = 0; + langwell_otg_drv_vbus(0); + lnw->otg.state = OTG_STATE_A_IDLE; transceiver_suspend(pdev); - 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); + del_timer_sync(&lnw->hsm_timer); + if (lnw->client_ops) + lnw->client_ops->suspend(pdev, message); else dev_dbg(&pdev->dev, "client driver has been removed.\n"); + lnw->hsm.a_srp_det = 0; + langwell_otg_drv_vbus(0); + lnw->otg.state = OTG_STATE_A_IDLE; transceiver_suspend(pdev); - langwell->otg.state = OTG_STATE_A_WAIT_VFALL; break; case OTG_STATE_B_HOST: - if (langwell->host_ops) - langwell->host_ops->remove(pdev); + if (lnw->host_ops) + lnw->host_ops->remove(pdev); else dev_dbg(&pdev->dev, "host driver has been removed.\n"); - langwell->hsm.b_bus_req = 0; + lnw->hsm.b_bus_req = 0; + lnw->otg.state = OTG_STATE_B_IDLE; transceiver_suspend(pdev); - langwell->otg.state = OTG_STATE_B_IDLE; break; case OTG_STATE_B_PERIPHERAL: - if (langwell->client_ops) - ret = langwell->client_ops->suspend(pdev, message); + if (lnw->client_ops) + lnw->client_ops->suspend(pdev, message); else dev_dbg(&pdev->dev, "client driver has been removed.\n"); + lnw->otg.state = OTG_STATE_B_IDLE; + transceiver_suspend(pdev); break; case OTG_STATE_B_WAIT_ACON: /* delete hsm timer for b_ase0_brst_tmr */ - del_timer_sync(&langwell->hsm_timer); + del_timer_sync(&lnw->hsm_timer); langwell_otg_HAAR(0); - if (langwell->host_ops) - langwell->host_ops->remove(pdev); + if (lnw->host_ops) + lnw->host_ops->remove(pdev); else dev_dbg(&pdev->dev, "host driver has been removed.\n"); - langwell->hsm.b_bus_req = 0; - langwell->otg.state = OTG_STATE_B_IDLE; + lnw->hsm.b_bus_req = 0; + lnw->otg.state = OTG_STATE_B_IDLE; transceiver_suspend(pdev); break; default: dev_dbg(&pdev->dev, "error state before suspend\n "); break; } - spin_unlock(&langwell->wq_lock); return ret; } @@ -2212,66 +2230,41 @@ static void transceiver_resume(struct pci_dev *pdev) { pci_restore_state(pdev); pci_set_power_state(pdev, PCI_D0); - langwell_otg_phy_low_power(0); } static int langwell_otg_resume(struct pci_dev *pdev) { - struct langwell_otg *langwell = the_transceiver; + struct langwell_otg *lnw = the_transceiver; int ret = 0; - spin_lock(&langwell->wq_lock); + transceiver_resume(pdev); - switch (langwell->otg.state) { - case OTG_STATE_A_IDLE: - case OTG_STATE_B_IDLE: - case OTG_STATE_A_WAIT_VFALL: - case OTG_STATE_A_VBUS_ERR: - transceiver_resume(pdev); - break; - case OTG_STATE_A_WAIT_BCON: - langwell_otg_add_ktimer(TA_WAIT_BCON_TMR); - langwell_otg_drv_vbus(1); - if (langwell->host_ops) - ret = langwell->host_ops->resume(pdev); - else - dev_dbg(&pdev->dev, "host driver not loaded.\n"); - break; - case OTG_STATE_A_HOST: - langwell_otg_drv_vbus(1); - langwell_otg_phy_low_power(0); - if (langwell->host_ops) - ret = langwell->host_ops->resume(pdev); - else - dev_dbg(&pdev->dev, "host driver not loaded.\n"); - break; - case OTG_STATE_B_PERIPHERAL: - if (langwell->client_ops) - ret = langwell->client_ops->resume(pdev); - else - dev_dbg(&pdev->dev, "client driver not loaded.\n"); - break; - default: - dev_dbg(&pdev->dev, "error state before suspend\n "); - break; + lnw->qwork = create_singlethread_workqueue("langwell_otg_queue"); + if (!lnw->qwork) { + dev_dbg(&pdev->dev, "cannot create langwell otg workqueuen"); + ret = -ENOMEM; + goto error; } if (request_irq(pdev->irq, otg_irq, IRQF_SHARED, - driver_name, langwell) != 0) { + driver_name, lnw) != 0) { dev_dbg(&pdev->dev, "request interrupt %d failed\n", pdev->irq); ret = -EBUSY; + goto error; } - spin_unlock(&langwell->wq_lock); - /* enable OTG interrupts */ langwell_otg_intr(1); update_hsm(); - queue_work(langwell->qwork, &langwell->work); + queue_work(lnw->qwork, &lnw->work); return ret; +error: + langwell_otg_intr(0); + transceiver_suspend(pdev); + return ret; } static int __init langwell_otg_init(void) diff --git a/include/linux/usb/langwell_otg.h b/include/linux/usb/langwell_otg.h index 69977c2..a5a3744 100644 --- a/include/linux/usb/langwell_otg.h +++ b/include/linux/usb/langwell_otg.h @@ -78,6 +78,7 @@ extern void langwell_otg_nsf_msg(unsigned long message); # define OTGSC_VC BIT(1) # define OTGSC_VD BIT(0) # define OTGSC_INTEN_MASK (0x7f << 24) +# define OTGSC_INT_MASK (0x5f << 24) # define OTGSC_INTSTS_MASK (0x7f << 16) #define CI_USBMODE 0xf8 # define USBMODE_CM (BIT(1) | BIT(0)) -- 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