[PATCH 17/25] USB OTG Langwell: Update PCI suspend/resume function

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



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


[Index of Archives]     [Linux Media]     [Linux Input]     [Linux Audio Users]     [Yosemite News]     [Linux Kernel]     [Linux SCSI]     [Old Linux USB Devel Archive]

  Powered by Linux