[PATCH v2 21/25] usb: penwell_otg: add SRP support

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

 



SRP(Session Request Protocol) is one OTG feature that used to request power 
supply from the connected OTG device.
This penwell_otg transceiver driver patch enables SRP function per OTG 2.0
spec requirement.

Signed-off-by: Hao Wu <hao.wu@xxxxxxxxx>
---
 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 e26393c..2c1d027 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 *charger_string(enum usb_charger_type charger)
@@ -485,6 +488,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)
 {
@@ -1011,7 +1094,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 */
@@ -1019,6 +1102,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)
@@ -1179,11 +1264,17 @@ penwell_otg_notify(struct otg_transceiver *otg, enum usb_otg_events event)
 		break;
 	case USB_OTG_HOST_SUSP:
 		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 USB_OTG_HOST_RESU:
 		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 USB_OTG_DEV_SUSP:
 		dev_dbg(pnw->dev, "PNW OTG Notify Client Bus suspend Event\n");
@@ -1315,7 +1406,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;
@@ -1350,8 +1442,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 */
 
@@ -1362,6 +1453,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 */
@@ -1426,14 +1518,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);
@@ -1497,9 +1585,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
@@ -1586,9 +1673,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) {
@@ -1678,9 +1763,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
@@ -1720,9 +1803,7 @@ static void penwell_otg_work(struct work_struct *work)
 			set_client_mode();
 			usleep_range(4000, 5000);
 			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();
@@ -1750,7 +1831,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)
@@ -1770,7 +1851,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);
@@ -1791,7 +1873,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);
@@ -1813,6 +1896,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;
 
@@ -2121,6 +2205,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);
 
@@ -2631,6 +2717,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);
 
@@ -2740,9 +2827,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)
@@ -2767,10 +2854,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;
@@ -2901,6 +2991,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

--
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