[PATCH v2 7/9] usb: chipidea: OTG HNP and SRP fsm implementation.

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

 



USB OTG interrupt handling and fsm transition according to USB OTG
spec 2.0, update otg timer timeout handlers.

Signed-off-by: Li Jun <b47624@xxxxxxxxxxxxx>
---
 drivers/usb/chipidea/bits.h    |    2 +
 drivers/usb/chipidea/core.c    |   10 ++-
 drivers/usb/chipidea/otg.c     |    9 ++-
 drivers/usb/chipidea/otg_fsm.c |  193 ++++++++++++++++++++++++++++++++++++++++
 drivers/usb/chipidea/otg_fsm.h |   18 ++++
 5 files changed, 229 insertions(+), 3 deletions(-)

diff --git a/drivers/usb/chipidea/bits.h b/drivers/usb/chipidea/bits.h
index 88136d1..5a74f9d 100644
--- a/drivers/usb/chipidea/bits.h
+++ b/drivers/usb/chipidea/bits.h
@@ -33,6 +33,8 @@
 #define USBCMD_ATDTW          BIT(14)
 
 /* USBSTS & USBINTR */
+#define USBSTS_PCI            BIT(2)
+#define USBSTS_SLI            BIT(8)
 #define USBi_UI               BIT(0)
 #define USBi_UEI              BIT(1)
 #define USBi_PCI              BIT(2)
diff --git a/drivers/usb/chipidea/core.c b/drivers/usb/chipidea/core.c
index 33f22bc..6865451 100644
--- a/drivers/usb/chipidea/core.c
+++ b/drivers/usb/chipidea/core.c
@@ -73,6 +73,7 @@
 #include "host.h"
 #include "debug.h"
 #include "otg.h"
+#include "otg_fsm.h"
 
 /* Controller register map */
 static const u8 ci_regs_nolpm[] = {
@@ -351,8 +352,12 @@ static irqreturn_t ci_irq(int irq, void *data)
 	irqreturn_t ret = IRQ_NONE;
 	u32 otgsc = 0;
 
-	if (ci->is_otg)
+	if (ci->is_otg) {
 		otgsc = hw_read(ci, OP_OTGSC, ~0);
+		ret = ci_otg_fsm_irq(ci);
+		if (ret == IRQ_HANDLED)
+			return ret;
+	}
 
 	/*
 	 * Handle id change interrupt, it indicates device/host function
@@ -656,6 +661,9 @@ static int ci_hdrc_probe(struct platform_device *pdev)
 	if (ret)
 		goto stop;
 
+	if (ci->is_otg)
+		ci_hdrc_otg_fsm_start(ci);
+
 	ret = dbg_create_files(ci);
 	if (!ret)
 		return 0;
diff --git a/drivers/usb/chipidea/otg.c b/drivers/usb/chipidea/otg.c
index cbf0167..4fb33a2 100644
--- a/drivers/usb/chipidea/otg.c
+++ b/drivers/usb/chipidea/otg.c
@@ -11,8 +11,8 @@
  */
 
 /*
- * This file mainly handles otgsc register, it may include OTG operation
- * in the future.
+ * This file mainly handles otgsc register, OTG fsm operations for HNP and SRP
+ * are also included.
  */
 
 #include <linux/usb/otg.h>
@@ -77,6 +77,11 @@ static void ci_otg_work(struct work_struct *work)
 {
 	struct ci_hdrc *ci = container_of(work, struct ci_hdrc, work);
 
+	if (!ci_otg_fsm_work(ci)) {
+		enable_irq(ci->irq);
+		return;
+	}
+
 	if (ci->id_event) {
 		ci->id_event = false;
 		ci_handle_id_switch(ci);
diff --git a/drivers/usb/chipidea/otg_fsm.c b/drivers/usb/chipidea/otg_fsm.c
index 5416fe3..e713b76 100644
--- a/drivers/usb/chipidea/otg_fsm.c
+++ b/drivers/usb/chipidea/otg_fsm.c
@@ -433,6 +433,199 @@ static struct otg_fsm_ops ci_otg_ops = {
 	.start_gadget = ci_otg_start_gadget,
 };
 
+int ci_otg_fsm_work(struct ci_hdrc *ci)
+{
+	if (!ci->transceiver->otg || !ci->fsm)
+		return -ENODEV;
+
+	if (otg_statemachine(ci->fsm)) {
+		if (ci->transceiver->state == OTG_STATE_A_IDLE) {
+			if (ci->fsm->id)
+				/* A idle to B idle */
+				otg_statemachine(ci->fsm);
+			else if ((ci->id_event) || (ci->fsm->power_up)) {
+				ci->id_event = false;
+				/* A idle to A wait vrise */
+				otg_statemachine(ci->fsm);
+				ci->fsm->power_up = false;
+			}
+		}
+	}
+	return 0;
+}
+
+static void ci_otg_fsm_event(struct ci_hdrc *ci, struct otg_fsm *fsm)
+{
+	if ((ci == NULL) || (fsm == NULL))
+		return;
+
+	switch (ci->transceiver->state) {
+	case OTG_STATE_A_WAIT_BCON:
+		if (hw_read(ci, OP_PORTSC, PORTSC_CCS)) {
+			fsm->b_conn = 1;
+			fsm->a_bus_req = 1;
+			disable_irq_nosync(ci->irq);
+			queue_work(ci->wq, &ci->work);
+		}
+		break;
+	case OTG_STATE_B_IDLE:
+		if (hw_read(ci, OP_OTGSC, OTGSC_BSV) &&
+			hw_read(ci, OP_USBSTS, USBSTS_PCI) &&
+			hw_read(ci, OP_PORTSC, PORTSC_CCS)) {
+			fsm->b_sess_vld = 1;
+			disable_irq_nosync(ci->irq);
+			queue_work(ci->wq, &ci->work);
+		}
+		break;
+	case OTG_STATE_B_PERIPHERAL:
+		if (hw_read(ci, OP_USBSTS, USBSTS_SLI) &&
+			hw_read(ci, OP_PORTSC, PORTSC_CCS) &&
+			hw_read(ci, OP_OTGSC, OTGSC_BSV)) {
+			fsm->a_bus_suspend = 1;
+			disable_irq_nosync(ci->irq);
+			queue_work(ci->wq, &ci->work);
+		} else if (hw_read(ci, OP_USBSTS, USBSTS_PCI)) {
+			if (fsm->a_bus_suspend == 1)
+				fsm->a_bus_suspend = 0;
+		}
+		break;
+	case OTG_STATE_B_HOST:
+		if (hw_read(ci, OP_USBSTS, USBSTS_PCI) &&
+			!(hw_read(ci, OP_PORTSC, PORTSC_CCS))) {
+			fsm->a_conn = 0;
+			fsm->b_bus_req = 0;
+			disable_irq_nosync(ci->irq);
+			queue_work(ci->wq, &ci->work);
+			ci_otg_add_timer(ci, B_SESS_VLD);
+		}
+		break;
+	case OTG_STATE_A_PERIPHERAL:
+		if (hw_read(ci, OP_USBSTS, USBSTS_SLI)) {
+			 fsm->b_bus_suspend = 1;
+			/* Init a timer to know how long this suspend
+			 * will contine, if time out, indicates B no longer
+			 * wants to be host role */
+			 ci_otg_add_timer(ci, A_BIDL_ADIS);
+		}
+
+		if (hw_read(ci, OP_USBSTS, USBi_URI))
+			ci_otg_del_timer(ci, A_BIDL_ADIS);
+
+		if (hw_read(ci, OP_USBSTS, USBSTS_PCI)) {
+			if (fsm->b_bus_suspend == 1) {
+				ci_otg_del_timer(ci, A_BIDL_ADIS);
+				fsm->b_bus_suspend = 0;
+			}
+		}
+		break;
+	case OTG_STATE_A_SUSPEND:
+		if (hw_read(ci, OP_USBSTS, USBSTS_PCI) &&
+			!(hw_read(ci, OP_PORTSC, PORTSC_CCS))) {
+			fsm->b_conn = 0;
+
+			/* if gadget driver is binded */
+			if (ci->driver) {
+				/* A device to be peripheral mode */
+				ci->gadget.is_a_peripheral = 1;
+			}
+			disable_irq_nosync(ci->irq);
+			queue_work(ci->wq, &ci->work);
+		}
+		break;
+	case OTG_STATE_A_HOST:
+		if (hw_read(ci, OP_USBSTS, USBSTS_PCI) &&
+			!(hw_read(ci, OP_PORTSC, PORTSC_CCS))) {
+			fsm->b_conn = 0;
+			disable_irq_nosync(ci->irq);
+			queue_work(ci->wq, &ci->work);
+		}
+		break;
+	case OTG_STATE_B_WAIT_ACON:
+		if (hw_read(ci, OP_USBSTS, USBSTS_PCI) &&
+			hw_read(ci, OP_PORTSC, PORTSC_CCS)) {
+			fsm->a_conn = 1;
+			disable_irq_nosync(ci->irq);
+			queue_work(ci->wq, &ci->work);
+		}
+		break;
+	default:
+		break;
+	}
+}
+
+/**
+ * ci_otg_irq - perform otg fsm related irq handling
+ * @ci: ci_hdrc
+ */
+irqreturn_t ci_otg_fsm_irq(struct ci_hdrc *ci)
+{
+	irqreturn_t retval =  IRQ_NONE;
+	u32 otgsc, otg_int_src = 0;
+	struct otg_fsm *fsm = ci->fsm;
+
+	if ((ci == NULL) || !(ci->is_otg) ||
+		(ci->platdata->dr_mode != USB_DR_MODE_OTG))
+		return retval;
+
+	otgsc = hw_read(ci, OP_OTGSC, ~0);
+	otg_int_src = otgsc & OTGSC_INT_STATUS_BITS & (otgsc >> 8);
+	fsm->id = (otgsc & OTGSC_ID) ? 1 : 0;
+
+	/* process OTG interrupts: ID,A vbus vld,B sess Vld,1ms */
+	if (otg_int_src) {
+		if (otg_int_src & OTGSC_1MSIS) {
+			ci_clear_otg_interrupt(ci, OTGSC_1MSIS);
+			retval = ci_otg_tick_timer(ci);
+			return IRQ_HANDLED;
+		} else if (otg_int_src & OTGSC_DPIS) {
+			ci_clear_otg_interrupt(ci, OTGSC_DPIS);
+			fsm->a_srp_det = 1;
+			fsm->a_bus_drop = 0;
+		} else if (otg_int_src & OTGSC_IDIS) {
+			ci_clear_otg_interrupt(ci, OTGSC_IDIS);
+			if (fsm->id == 0) {
+				fsm->a_bus_req = 1;
+				ci->id_event = true;
+			}
+		} else if (otg_int_src & OTGSC_BSVIS) {
+			ci_clear_otg_interrupt(ci, OTGSC_BSVIS);
+			if (otgsc & OTGSC_BSV) {
+				fsm->b_sess_vld = 1;
+				ci_otg_del_timer(ci, B_SSEND_SRP);
+				ci_otg_del_timer(ci, B_SRP_FAIL);
+				fsm->b_ssend_srp = 0;
+			} else {
+				fsm->b_sess_vld = 0;
+				if (fsm->id)
+					ci_otg_add_timer(ci, B_SSEND_SRP);
+			}
+		} else if (otg_int_src & OTGSC_AVVIS) {
+			ci_clear_otg_interrupt(ci, OTGSC_AVVIS);
+			if (otgsc & OTGSC_AVV) {
+				fsm->a_vbus_vld = 1;
+			} else {
+				fsm->a_vbus_vld = 0;
+				fsm->b_conn = 0;
+			}
+		}
+		disable_irq_nosync(ci->irq);
+		queue_work(ci->wq, &ci->work);
+		return IRQ_HANDLED;
+	}
+
+	ci_otg_fsm_event(ci, fsm);
+
+	return retval;
+}
+
+void ci_hdrc_otg_fsm_start(struct ci_hdrc *ci)
+{
+	if (ci->platdata->dr_mode == USB_DR_MODE_OTG) {
+		disable_irq_nosync(ci->irq);
+		queue_work(ci->wq, &ci->work);
+	}
+}
+
 int ci_hdrc_otg_fsm_init(struct ci_hdrc *ci)
 {
 	int retval = 0;
diff --git a/drivers/usb/chipidea/otg_fsm.h b/drivers/usb/chipidea/otg_fsm.h
index 77b604b..2177a5e 100644
--- a/drivers/usb/chipidea/otg_fsm.h
+++ b/drivers/usb/chipidea/otg_fsm.h
@@ -104,6 +104,9 @@ struct ci_otg_fsm_timer_list {
 #ifdef CONFIG_USB_OTG_FSM
 
 int ci_hdrc_otg_fsm_init(struct ci_hdrc *ci);
+int ci_otg_fsm_work(struct ci_hdrc *ci);
+irqreturn_t ci_otg_fsm_irq(struct ci_hdrc *ci);
+void ci_hdrc_otg_fsm_start(struct ci_hdrc *ci);
 
 #else
 
@@ -112,6 +115,21 @@ static inline int ci_hdrc_otg_fsm_init(struct ci_hdrc *ci)
 	return 0;
 }
 
+static inline int ci_otg_fsm_work(struct ci_hdrc *ci)
+{
+	return -ENXIO;
+}
+
+static inline irqreturn_t ci_otg_fsm_irq(struct ci_hdrc *ci)
+{
+	return IRQ_NONE;
+}
+
+static inline void ci_hdrc_otg_fsm_start(struct ci_hdrc *ci)
+{
+
+}
+
 #endif
 
 #endif /* __DRIVERS_USB_CHIPIDEA_OTG_FSM_H */
-- 
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