[PATCH v2 24/25] usb: penwell_otg: add HNP Polling support.

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

 



Add HNP polling support in penwell_otg transceiver driver.

Signed-off-by: Hao Wu <hao.wu@xxxxxxxxx>
---
 drivers/usb/otg/penwell_otg.c     |  175 +++++++++++++++++++++++++++++++++----
 include/linux/usb/intel_mid_otg.h |    7 ++
 2 files changed, 165 insertions(+), 17 deletions(-)

diff --git a/drivers/usb/otg/penwell_otg.c b/drivers/usb/otg/penwell_otg.c
index 2c1d027..9999d77 100644
--- a/drivers/usb/otg/penwell_otg.c
+++ b/drivers/usb/otg/penwell_otg.c
@@ -568,6 +568,96 @@ static void penwell_otg_mon_bus_fn(unsigned long indicator)
 	penwell_otg_mon_bus(BUS_MON_CONTINUE);
 }
 
+/* HNP polling function */
+/* The timeout callback function which polls the host request flag for HNP */
+static void penwell_otg_hnp_poll_fn(unsigned long indicator)
+{
+	struct penwell_otg	*pnw = the_transceiver;
+
+	queue_work(pnw->qwork, &pnw->hnp_poll_work);
+}
+
+/* Start HNP polling */
+/* Call this function with iotg->hnp_poll_lock held */
+static int penwell_otg_add_hnp_poll_timer(struct intel_mid_otg_xceiv *iotg,
+					unsigned long delay)
+{
+	struct penwell_otg	*pnw = the_transceiver;
+	unsigned long		j = jiffies;
+
+	pnw->hnp_poll_timer.data = 1;
+	pnw->hnp_poll_timer.function = penwell_otg_hnp_poll_fn;
+	pnw->hnp_poll_timer.expires = j + msecs_to_jiffies(delay);
+
+	add_timer(&pnw->hnp_poll_timer);
+
+	return 0;
+}
+
+static int penwell_otg_start_hnp_poll(struct intel_mid_otg_xceiv *iotg)
+{
+	struct penwell_otg	*pnw = the_transceiver;
+	unsigned long		flags;
+
+	spin_lock_irqsave(&pnw->iotg.hnp_poll_lock, flags);
+
+	if (pnw->iotg.hsm.hnp_poll_enable) {
+		dev_dbg(pnw->dev, "HNP polling is already enabled\n");
+		spin_unlock_irqrestore(&pnw->iotg.hnp_poll_lock, flags);
+		return 0;
+	}
+
+	/* mark HNP polling enabled and start HNP polling in 50ms */
+	pnw->iotg.hsm.hnp_poll_enable = 1;
+	penwell_otg_add_hnp_poll_timer(&pnw->iotg, 50);
+
+	spin_unlock_irqrestore(&pnw->iotg.hnp_poll_lock, flags);
+
+	return 0;
+}
+
+static int penwell_otg_continue_hnp_poll(struct intel_mid_otg_xceiv *iotg)
+{
+	struct penwell_otg	*pnw = the_transceiver;
+	unsigned long		flags;
+
+	spin_lock_irqsave(&pnw->iotg.hnp_poll_lock, flags);
+
+	if (!pnw->iotg.hsm.hnp_poll_enable) {
+		dev_dbg(pnw->dev, "HNP polling is disabled, stop polling\n");
+		spin_unlock_irqrestore(&pnw->iotg.hnp_poll_lock, flags);
+		return 0;
+	}
+
+	penwell_otg_add_hnp_poll_timer(&pnw->iotg, THOS_REQ_POL);
+
+	spin_unlock_irqrestore(&pnw->iotg.hnp_poll_lock, flags);
+
+	return 0;
+}
+
+/* Stop HNP polling */
+static int penwell_otg_stop_hnp_poll(struct intel_mid_otg_xceiv *iotg)
+{
+	struct penwell_otg	*pnw = the_transceiver;
+	unsigned long		flags;
+
+	spin_lock_irqsave(&pnw->iotg.hnp_poll_lock, flags);
+
+	if (!pnw->iotg.hsm.hnp_poll_enable) {
+		dev_dbg(pnw->dev, "HNP polling is already disabled\n");
+		spin_unlock_irqrestore(&pnw->iotg.hnp_poll_lock, flags);
+		return 0;
+	}
+
+	pnw->iotg.hsm.hnp_poll_enable = 0;
+	del_timer_sync(&pnw->hnp_poll_timer);
+
+	spin_unlock_irqrestore(&pnw->iotg.hnp_poll_lock, flags);
+
+	return 0;
+}
+
 /* Start SRP function */
 static int penwell_otg_start_srp(struct otg_transceiver *otg)
 {
@@ -591,14 +681,6 @@ static int penwell_otg_start_srp(struct otg_transceiver *otg)
 	return 0;
 }
 
-/* The timeout callback function to poll the host request flag */
-static void penwell_otg_hnp_poll_fn(unsigned long indicator)
-{
-	struct penwell_otg *pnw = the_transceiver;
-
-	queue_work(pnw->qwork, &pnw->hnp_poll_work);
-}
-
 /* stop SOF via bus_suspend */
 static void penwell_otg_loc_sof(int on)
 {
@@ -1327,7 +1409,6 @@ static void penwell_otg_hnp_poll_work(struct work_struct *work)
 	struct penwell_otg		*pnw = the_transceiver;
 	struct intel_mid_otg_xceiv	*iotg = &pnw->iotg;
 	struct usb_device		*udev;
-	unsigned long			j = jiffies;
 	int				err = 0;
 	u8				data;
 
@@ -1359,7 +1440,9 @@ static void penwell_otg_hnp_poll_work(struct work_struct *work)
 	}
 
 	if (data & HOST_REQUEST_FLAG) {
-		/* set a_bus_req = 0 */
+		/* start HNP sequence to switch role */
+		dev_dbg(pnw->dev, "host_request_flag = 1\n");
+
 		if (iotg->hsm.id == ID_B) {
 			dev_dbg(pnw->dev,
 				"Device B host - start HNP - b_bus_req = 0\n");
@@ -1371,12 +1454,8 @@ static void penwell_otg_hnp_poll_work(struct work_struct *work)
 		}
 		penwell_update_transceiver();
 	} else {
-		pnw->hnp_poll_timer.data = 1;
-		pnw->hnp_poll_timer.function = penwell_otg_hnp_poll_fn;
-		pnw->hnp_poll_timer.expires = j + THOS_REQ_POL * HZ / 1000;
-		add_timer(&pnw->hnp_poll_timer);
-
-		dev_dbg(pnw->dev, "HNP Polling - continue\n");
+		dev_dbg(pnw->dev, "host_request_flag = 0\n");
+		penwell_otg_continue_hnp_poll(&pnw->iotg);
 	}
 }
 
@@ -1484,6 +1563,10 @@ static void penwell_otg_work(struct work_struct *work)
 				penwell_otg_update_chrg_cap(CHRG_CDP,
 							CHRG_CURR_CDP);
 
+				/* Clear HNP polling flag */
+				if (iotg->otg.gadget)
+					iotg->otg.gadget->host_request_flag = 0;
+
 				if (iotg->start_peripheral) {
 					iotg->start_peripheral(iotg);
 				} else {
@@ -1498,6 +1581,10 @@ static void penwell_otg_work(struct work_struct *work)
 				penwell_otg_update_chrg_cap(CHRG_SDP,
 							pnw->charging_cap.mA);
 
+				/* Clear HNP polling flag */
+				if (iotg->otg.gadget)
+					iotg->otg.gadget->host_request_flag = 0;
+
 				if (iotg->start_peripheral) {
 					iotg->start_peripheral(iotg);
 				} else {
@@ -1726,6 +1813,9 @@ static void penwell_otg_work(struct work_struct *work)
 			iotg->otg.default_a = 1;
 			hsm->a_srp_det = 0;
 
+			/* Stop HNP polling */
+			iotg->stop_hnp_poll(iotg);
+
 			if (iotg->stop_host)
 				iotg->stop_host(iotg);
 			else
@@ -1755,6 +1845,9 @@ static void penwell_otg_work(struct work_struct *work)
 			hsm->b_hnp_enable = 0;
 			hsm->b_bus_req = 0;
 
+			/* Stop HNP polling */
+			iotg->stop_hnp_poll(iotg);
+
 			if (iotg->stop_host)
 				iotg->stop_host(iotg);
 			else
@@ -1770,6 +1863,9 @@ static void penwell_otg_work(struct work_struct *work)
 					|| hsm->test_device) {
 			hsm->b_bus_req = 0;
 
+			/* Stop HNP polling */
+			iotg->stop_hnp_poll(iotg);
+
 			if (iotg->stop_host)
 				iotg->stop_host(iotg);
 			else
@@ -1778,6 +1874,10 @@ static void penwell_otg_work(struct work_struct *work)
 
 			hsm->a_bus_suspend = 0;
 
+			/* Clear HNP polling flag */
+			if (iotg->otg.gadget)
+				iotg->otg.gadget->host_request_flag = 0;
+
 			if (iotg->start_peripheral)
 				iotg->start_peripheral(iotg);
 			else
@@ -1956,6 +2056,9 @@ static void penwell_otg_work(struct work_struct *work)
 			/* Delete current timer and disable host function */
 			penwell_otg_del_timer(TA_WAIT_BCON_TMR);
 
+			/* Start HNP polling */
+			iotg->start_hnp_poll(iotg);
+
 			iotg->otg.state = OTG_STATE_A_HOST;
 
 			if (!hsm->a_bus_req)
@@ -1991,6 +2094,9 @@ static void penwell_otg_work(struct work_struct *work)
 		} else if (!hsm->a_vbus_vld) {
 			/* Move to A_VBUS_ERR state */
 
+			/* Stop HNP polling */
+			iotg->stop_hnp_poll(iotg);
+
 			if (iotg->stop_host)
 				iotg->stop_host(iotg);
 			else
@@ -2006,6 +2112,9 @@ static void penwell_otg_work(struct work_struct *work)
 		} else if (!hsm->a_bus_req) {
 			/* Move to A_SUSPEND state */
 
+			/* Stop HNP polling */
+			iotg->stop_hnp_poll(iotg);
+
 			penwell_otg_loc_sof(0);
 
 			if (iotg->otg.host->b_hnp_enable) {
@@ -2015,7 +2124,9 @@ static void penwell_otg_work(struct work_struct *work)
 
 			iotg->otg.state = OTG_STATE_A_SUSPEND;
 		} else if (!hsm->b_conn) {
-			hsm->hnp_poll_enable = 0;
+			/* Stop HNP polling */
+			iotg->stop_hnp_poll(iotg);
+
 			/* add kernel timer */
 			iotg->otg.state = OTG_STATE_A_WAIT_BCON;
 		} else if (hsm->id == ID_ACA_A) {
@@ -2041,6 +2152,9 @@ static void penwell_otg_work(struct work_struct *work)
 				penwell_otg_update_chrg_cap(CHRG_ACA,
 							CHRG_CURR_ACA);
 
+			/* Stop HNP polling */
+			iotg->stop_hnp_poll(iotg);
+
 			if (iotg->stop_host)
 				iotg->stop_host(iotg);
 			else
@@ -2085,6 +2199,9 @@ static void penwell_otg_work(struct work_struct *work)
 			/* Delete current timer and clear flags */
 			penwell_otg_del_timer(TA_AIDL_BDIS_TMR);
 
+			/* Stop HNP polling */
+			iotg->stop_hnp_poll(iotg);
+
 			if (iotg->stop_host)
 				iotg->stop_host(iotg);
 			else
@@ -2093,6 +2210,10 @@ static void penwell_otg_work(struct work_struct *work)
 
 			hsm->b_bus_suspend = 0;
 
+			/* Clear HNP polling flag */
+			if (iotg->otg.gadget)
+				iotg->otg.gadget->host_request_flag = 0;
+
 			if (iotg->start_peripheral)
 				iotg->start_peripheral(iotg);
 			else
@@ -2107,6 +2228,9 @@ static void penwell_otg_work(struct work_struct *work)
 			/* Delete current timer and clear flags */
 			penwell_otg_del_timer(TA_AIDL_BDIS_TMR);
 
+			/* Start HNP polling */
+			iotg->start_hnp_poll(iotg);
+
 			penwell_otg_loc_sof(1);
 			iotg->otg.state = OTG_STATE_A_HOST;
 		} else if (hsm->id == ID_ACA_A) {
@@ -2526,10 +2650,17 @@ set_b_bus_req(struct device *dev, struct device_attribute *attr,
 	if (buf[0] == '0') {
 		iotg->hsm.b_bus_req = 0;
 		dev_dbg(pnw->dev, "b_bus_req = 0\n");
+
+		if (iotg->otg.gadget)
+			iotg->otg.gadget->host_request_flag = 0;
 	} else if (buf[0] == '1') {
 		iotg->hsm.b_bus_req = 1;
 		dev_dbg(pnw->dev, "b_bus_req = 1\n");
+
 		if (iotg->otg.state == OTG_STATE_B_PERIPHERAL) {
+			if (iotg->otg.gadget)
+				iotg->otg.gadget->host_request_flag = 1;
+
 			dev_warn(pnw->dev, "Role switch will be "
 				"performed soon, if connected OTG device "
 				"supports role switch request.\n");
@@ -2706,6 +2837,8 @@ static int penwell_otg_probe(struct pci_dev *pdev,
 	pnw->iotg.otg.otg_notify = penwell_otg_notify;
 	pnw->iotg.set_adp_probe = NULL;
 	pnw->iotg.set_adp_sense = NULL;
+	pnw->iotg.start_hnp_poll = penwell_otg_start_hnp_poll;
+	pnw->iotg.stop_hnp_poll = penwell_otg_stop_hnp_poll;
 	pnw->iotg.otg.state = OTG_STATE_UNDEFINED;
 	if (otg_set_transceiver(&pnw->iotg.otg)) {
 		dev_dbg(pnw->dev, "can't set transceiver\n");
@@ -2716,6 +2849,8 @@ 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;
 
+	spin_lock_init(&pnw->iotg.hnp_poll_lock);
+
 	init_timer(&pnw->hsm_timer);
 	init_timer(&pnw->bus_mon_timer);
 	init_timer(&pnw->hnp_poll_timer);
@@ -2885,6 +3020,9 @@ static int penwell_otg_suspend(struct pci_dev *pdev, pm_message_t message)
 		transceiver_suspend(pdev);
 		break;
 	case OTG_STATE_A_HOST:
+		/* Stop HNP polling */
+		iotg->stop_hnp_poll(iotg);
+
 		if (pnw->iotg.stop_host)
 			pnw->iotg.stop_host(&pnw->iotg);
 		else
@@ -2927,6 +3065,9 @@ static int penwell_otg_suspend(struct pci_dev *pdev, pm_message_t message)
 		transceiver_suspend(pdev);
 		break;
 	case OTG_STATE_B_HOST:
+		/* Stop HNP polling */
+		iotg->stop_hnp_poll(iotg);
+
 		if (pnw->iotg.stop_host)
 			pnw->iotg.stop_host(&pnw->iotg);
 		else
diff --git a/include/linux/usb/intel_mid_otg.h b/include/linux/usb/intel_mid_otg.h
index 62db841..ca2c4a6 100644
--- a/include/linux/usb/intel_mid_otg.h
+++ b/include/linux/usb/intel_mid_otg.h
@@ -120,6 +120,9 @@ struct intel_mid_otg_xceiv {
 	/* atomic notifier for interrupt context */
 	struct atomic_notifier_head	iotg_notifier;
 
+	/* hnp poll lock */
+	spinlock_t			hnp_poll_lock;
+
 	/* start/stop USB Host function */
 	int	(*start_host)(struct intel_mid_otg_xceiv *iotg);
 	int	(*stop_host)(struct intel_mid_otg_xceiv *iotg);
@@ -134,6 +137,10 @@ struct intel_mid_otg_xceiv {
 	int	(*set_adp_sense)(struct intel_mid_otg_xceiv *iotg,
 					bool enabled);
 
+	/* start/stop HNP Polling function */
+	int	(*start_hnp_poll)(struct intel_mid_otg_xceiv *iotg);
+	int	(*stop_hnp_poll)(struct intel_mid_otg_xceiv *iotg);
+
 #ifdef CONFIG_PM
 	/* suspend/resume USB host function */
 	int	(*suspend_host)(struct intel_mid_otg_xceiv *iotg,
-- 
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


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

  Powered by Linux