[PATCH v2 6/6] usb: chipidea: OTG fsm driver HNP polling implementation.

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

 



This patch implements OTG host request polling and does role switch
when peripheral sets the host request flag.

Signed-off-by: Li Jun <b47624@xxxxxxxxxxxxx>
---
 drivers/usb/chipidea/ci.h      |    1 +
 drivers/usb/chipidea/otg_fsm.c |   81 ++++++++++++++++++++++++++++++++++++++++
 drivers/usb/chipidea/otg_fsm.h |    4 ++
 3 files changed, 86 insertions(+), 0 deletions(-)

diff --git a/drivers/usb/chipidea/ci.h b/drivers/usb/chipidea/ci.h
index 8152020..8d6032e 100644
--- a/drivers/usb/chipidea/ci.h
+++ b/drivers/usb/chipidea/ci.h
@@ -205,6 +205,7 @@ struct ci_hdrc {
 	struct dentry			*debugfs;
 	bool				id_event;
 	bool				b_sess_valid_event;
+	bool				hnp_polling_event;
 	bool				imx28_write_fix;
 };
 
diff --git a/drivers/usb/chipidea/otg_fsm.c b/drivers/usb/chipidea/otg_fsm.c
index 613eb86..2586cdb 100644
--- a/drivers/usb/chipidea/otg_fsm.c
+++ b/drivers/usb/chipidea/otg_fsm.c
@@ -463,6 +463,14 @@ static void ci_otg_fsm_del_timer(struct otg_fsm *fsm, enum otg_fsm_timer t)
 	return;
 }
 
+static void ci_otg_start_hnp_polling(struct otg_fsm *fsm)
+{
+	mod_timer(&fsm->hnp_polling_timer,
+		jiffies + msecs_to_jiffies(T_HOST_REQ_POLL));
+
+	return;
+}
+
 static void ci_otg_chrg_vbus(struct otg_fsm *fsm, int on)
 {
 	struct ci_hdrc	*ci = container_of(fsm->otg->gadget,
@@ -613,6 +621,7 @@ static struct otg_fsm_ops ci_otg_ops = {
 	.loc_sof = ci_otg_loc_sof,
 	.start_pulse = ci_otg_start_pulse,
 	.start_adp_prb = ci_otg_start_adp_prb,
+	.start_hnp_polling = ci_otg_start_hnp_polling,
 
 	.add_timer = ci_otg_fsm_add_timer,
 	.del_timer = ci_otg_fsm_del_timer,
@@ -621,11 +630,80 @@ static struct otg_fsm_ops ci_otg_ops = {
 	.start_gadget = ci_otg_start_gadget,
 };
 
+static void hnp_polling_timer_work(unsigned long arg)
+{
+	struct ci_hdrc *ci = (struct ci_hdrc *)arg;
+
+	ci->hnp_polling_event = true;
+	disable_irq_nosync(ci->irq);
+	queue_work(ci->wq, &ci->work);
+}
+
+static int ci_hnp_polling(struct ci_hdrc *ci)
+{
+	struct usb_device	*udev;
+	int			err = 0;
+	u16			data;
+
+	if (ci->transceiver->state != OTG_STATE_A_HOST
+			&& ci->transceiver->state != OTG_STATE_B_HOST)
+		return -EINVAL;
+
+	if (ci->transceiver->otg->host &&
+			ci->transceiver->otg->host->root_hub) {
+		udev = usb_hub_find_child(
+			ci->transceiver->otg->host->root_hub, 1);
+		if (!udev) {
+			dev_dbg(ci->dev,
+			"no usb dev connected, stop HNP polling\n");
+			return -ENODEV;
+		} else if (udev->state < USB_STATE_DEFAULT) {
+			ci_otg_start_hnp_polling(ci->fsm);
+			return -EAGAIN;
+		}
+	} else {
+		dev_dbg(ci->dev, "no host or root_hub registered\n");
+		return -ENODEV;
+	}
+
+	/* get host request flag from connected USB device */
+	err = usb_get_status(udev, USB_RECIP_DEVICE, OTG_STS_SELECTOR, &data);
+	if (err) {
+		dev_warn(ci->dev,
+			"ERR in HNP polling = %d, stop HNP polling\n", err);
+		return -EINVAL;
+	}
+
+	if ((data & 0xff) == HOST_REQUEST_FLAG) {
+		/* Start role switch */
+		dev_dbg(ci->dev, "host request flag = 1\n");
+		if (ci->transceiver->state == OTG_STATE_A_HOST)
+			ci->fsm->a_bus_req = 0;
+		else if (ci->transceiver->state == OTG_STATE_B_HOST)
+			ci->fsm->b_bus_req = 0;
+		return 0;
+	} else if ((data & 0xff) == 0) {
+		/* Continue polling */
+		ci_otg_start_hnp_polling(ci->fsm);
+		return -EAGAIN;
+	} else
+		dev_err(ci->dev, "host request flag is invalid value\n");
+
+	return err;
+}
+
 int ci_otg_fsm_work(struct ci_hdrc *ci)
 {
 	if (!ci->transceiver->otg || !ci->fsm)
 		return -ENODEV;
 
+	if (ci->hnp_polling_event) {
+		ci->hnp_polling_event = false;
+		if (ci_hnp_polling(ci)) {
+			return 0;
+		}
+	}
+
 	if (otg_statemachine(ci->fsm)) {
 		if (ci->transceiver->state == OTG_STATE_A_IDLE) {
 			if (ci->fsm->id)
@@ -869,6 +947,9 @@ int ci_hdrc_otg_fsm_init(struct ci_hdrc *ci)
 		return retval;
 	}
 
+	setup_timer(&ci->fsm->hnp_polling_timer, hnp_polling_timer_work,
+							(unsigned long)ci);
+
 	/* enable ID and A vbus valid irq */
 	hw_write(ci, OP_OTGSC, OTGSC_INT_EN_BITS | OTGSC_INT_STATUS_BITS,
 						OTGSC_IDIE | OTGSC_AVVIE);
diff --git a/drivers/usb/chipidea/otg_fsm.h b/drivers/usb/chipidea/otg_fsm.h
index 6cdd55c2..53f75b2 100644
--- a/drivers/usb/chipidea/otg_fsm.h
+++ b/drivers/usb/chipidea/otg_fsm.h
@@ -78,6 +78,10 @@
 #define TB_SSEND_SRP    (1500)  /* Table 5-1, Session end to SRP init */
 #define TB_SESS_VLD     (1000)
 
+#define T_HOST_REQ_POLL (1500)	/* Section 6.3.1 HNP polling process
+				 * OTG and EH Revison 2.0 version 1.1a
+				 * July 27, 2012 */
+
 enum ci_otg_fsm_timer_index {
 	/* CI specific timers, start from the end
 	 * of standard and auxiliary OTG timers */
-- 
1.7.8


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