From 0c33bf781a0da4bdab207ccc323c9afa940852af Mon Sep 17 00:00:00 2001
From: Li Jun <b47624@freescale.com>
Date: Wed, 23 Apr 2014 15:56:38 +0800
Subject: usb: chipidea: operate on otgsc register in a general way

Use a more general way to read and write otgsc register.

Signed-off-by: Peter Chen <peter.chen@freescale.com>
Signed-off-by: Li Jun <b47624@freescale.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
---
 drivers/usb/chipidea/udc.c | 19 ++++++++++---------
 1 file changed, 10 insertions(+), 9 deletions(-)

(limited to 'drivers/usb/chipidea/udc.c')

diff --git a/drivers/usb/chipidea/udc.c b/drivers/usb/chipidea/udc.c
index 7739c64ef25..798943b6ef7 100644
--- a/drivers/usb/chipidea/udc.c
+++ b/drivers/usb/chipidea/udc.c
@@ -1843,21 +1843,22 @@ void ci_hdrc_gadget_destroy(struct ci_hdrc *ci)
 
 static int udc_id_switch_for_device(struct ci_hdrc *ci)
 {
-	if (ci->is_otg) {
-		ci_clear_otg_interrupt(ci, OTGSC_BSVIS);
-		ci_enable_otg_interrupt(ci, OTGSC_BSVIE);
-	}
+	if (ci->is_otg)
+		/* Clear and enable BSV irq */
+		hw_write_otgsc(ci, OTGSC_BSVIS | OTGSC_BSVIE,
+					OTGSC_BSVIS | OTGSC_BSVIE);
 
 	return 0;
 }
 
 static void udc_id_switch_for_host(struct ci_hdrc *ci)
 {
-	if (ci->is_otg) {
-		/* host doesn't care B_SESSION_VALID event */
-		ci_clear_otg_interrupt(ci, OTGSC_BSVIS);
-		ci_disable_otg_interrupt(ci, OTGSC_BSVIE);
-	}
+	/*
+	 * host doesn't care B_SESSION_VALID event
+	 * so clear and disbale BSV irq
+	 */
+	if (ci->is_otg)
+		hw_write_otgsc(ci, OTGSC_BSVIE | OTGSC_BSVIS, OTGSC_BSVIS);
 }
 
 /**
-- 
cgit v1.2.3-70-g09d2


From 36304b0616280809a58ebdd69d74f7c61286f9b5 Mon Sep 17 00:00:00 2001
From: Li Jun <B47624@freescale.com>
Date: Wed, 23 Apr 2014 15:56:39 +0800
Subject: usb: chipidea: export interrupt enable and status register read
 functions

This patch moves usb interrupt enable and status register read functions
from udc driver to core driver to use them in all ci drivers.

Signed-off-by: Peter Chen <peter.chen@freescale.com>
Acked-by: Peter Chen <peter.chen@freescale.com>
Signed-off-by: Li Jun <b47624@freescale.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
---
 drivers/usb/chipidea/ci.h   |  4 ++++
 drivers/usb/chipidea/core.c | 20 ++++++++++++++++++++
 drivers/usb/chipidea/udc.c  | 20 --------------------
 3 files changed, 24 insertions(+), 20 deletions(-)

(limited to 'drivers/usb/chipidea/udc.c')

diff --git a/drivers/usb/chipidea/ci.h b/drivers/usb/chipidea/ci.h
index e206406ae1d..7ae8cb68032 100644
--- a/drivers/usb/chipidea/ci.h
+++ b/drivers/usb/chipidea/ci.h
@@ -319,6 +319,10 @@ static inline u32 hw_test_and_write(struct ci_hdrc *ci, enum ci_hw_regs reg,
 	return (val & mask) >> __ffs(mask);
 }
 
+u32 hw_read_intr_enable(struct ci_hdrc *ci);
+
+u32 hw_read_intr_status(struct ci_hdrc *ci);
+
 int hw_device_reset(struct ci_hdrc *ci, u32 mode);
 
 int hw_port_test_set(struct ci_hdrc *ci, u8 mode);
diff --git a/drivers/usb/chipidea/core.c b/drivers/usb/chipidea/core.c
index f0cfa5b64bf..ff38cf36746 100644
--- a/drivers/usb/chipidea/core.c
+++ b/drivers/usb/chipidea/core.c
@@ -139,6 +139,26 @@ static int hw_alloc_regmap(struct ci_hdrc *ci, bool is_lpm)
 	return 0;
 }
 
+/**
+ * hw_read_intr_enable: returns interrupt enable register
+ *
+ * This function returns register data
+ */
+u32 hw_read_intr_enable(struct ci_hdrc *ci)
+{
+	return hw_read(ci, OP_USBINTR, ~0);
+}
+
+/**
+ * hw_read_intr_status: returns interrupt status register
+ *
+ * This function returns register data
+ */
+u32 hw_read_intr_status(struct ci_hdrc *ci)
+{
+	return hw_read(ci, OP_USBSTS, ~0);
+}
+
 /**
  * hw_port_test_set: writes port test mode (execute without interruption)
  * @mode: new value
diff --git a/drivers/usb/chipidea/udc.c b/drivers/usb/chipidea/udc.c
index 798943b6ef7..f58857d4505 100644
--- a/drivers/usb/chipidea/udc.c
+++ b/drivers/usb/chipidea/udc.c
@@ -241,26 +241,6 @@ static int hw_port_is_high_speed(struct ci_hdrc *ci)
 		hw_read(ci, OP_PORTSC, PORTSC_HSP);
 }
 
-/**
- * hw_read_intr_enable: returns interrupt enable register
- *
- * This function returns register data
- */
-static u32 hw_read_intr_enable(struct ci_hdrc *ci)
-{
-	return hw_read(ci, OP_USBINTR, ~0);
-}
-
-/**
- * hw_read_intr_status: returns interrupt status register
- *
- * This function returns register data
- */
-static u32 hw_read_intr_status(struct ci_hdrc *ci)
-{
-	return hw_read(ci, OP_USBSTS, ~0);
-}
-
 /**
  * hw_test_and_clear_complete: test & clear complete status (execute without
  *                             interruption)
-- 
cgit v1.2.3-70-g09d2


From 95f5555fa0f0176da338e8f42bca08f236032832 Mon Sep 17 00:00:00 2001
From: Li Jun <B47624@freescale.com>
Date: Wed, 23 Apr 2014 15:56:47 +0800
Subject: usb: chipidea: udc: driver update for OTG HNP

Add b_hnp_enable request handling and enable gadget->is_otg

Signed-off-by: Peter Chen <peter.chen@freescale.com>
Acked-by: Peter Chen <peter.chen@freescale.com>
Signed-off-by: Li Jun <b47624@freescale.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
---
 drivers/usb/chipidea/udc.c | 11 ++++++++++-
 1 file changed, 10 insertions(+), 1 deletion(-)

(limited to 'drivers/usb/chipidea/udc.c')

diff --git a/drivers/usb/chipidea/udc.c b/drivers/usb/chipidea/udc.c
index f58857d4505..cba7fd63d6e 100644
--- a/drivers/usb/chipidea/udc.c
+++ b/drivers/usb/chipidea/udc.c
@@ -20,6 +20,7 @@
 #include <linux/pm_runtime.h>
 #include <linux/usb/ch9.h>
 #include <linux/usb/gadget.h>
+#include <linux/usb/otg-fsm.h>
 #include <linux/usb/chipidea.h>
 
 #include "ci.h"
@@ -1052,6 +1053,14 @@ __acquires(ci->lock)
 				default:
 					break;
 				}
+				break;
+			case USB_DEVICE_B_HNP_ENABLE:
+				if (ci_otg_is_fsm_mode(ci)) {
+					ci->gadget.b_hnp_enable = 1;
+					err = isr_setup_status_phase(
+							ci);
+				}
+				break;
 			default:
 				goto delegate;
 			}
@@ -1759,7 +1768,7 @@ static int udc_start(struct ci_hdrc *ci)
 	ci->gadget.ops          = &usb_gadget_ops;
 	ci->gadget.speed        = USB_SPEED_UNKNOWN;
 	ci->gadget.max_speed    = USB_SPEED_HIGH;
-	ci->gadget.is_otg       = 0;
+	ci->gadget.is_otg       = ci->is_otg ? 1 : 0;
 	ci->gadget.name         = ci->platdata->name;
 
 	INIT_LIST_HEAD(&ci->gadget.ep_list);
-- 
cgit v1.2.3-70-g09d2


From 4dcf720c5d40b27c916e7115ad75b335c9c1e264 Mon Sep 17 00:00:00 2001
From: Li Jun <b47624@freescale.com>
Date: Wed, 23 Apr 2014 15:56:50 +0800
Subject: usb: chipidea: OTG HNP and SRP fsm implementation

USB OTG interrupt handling and fsm transitions according to USB OTG
and EH 2.0.

Signed-off-by: Peter Chen <peter.chen@freescale.com>
Signed-off-by: Li Jun <b47624@freescale.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
---
 drivers/usb/chipidea/core.c    |  24 +++-
 drivers/usb/chipidea/otg.c     |   9 +-
 drivers/usb/chipidea/otg_fsm.c | 243 +++++++++++++++++++++++++++++++++++++++++
 drivers/usb/chipidea/otg_fsm.h |  18 +++
 drivers/usb/chipidea/udc.c     |   8 ++
 5 files changed, 294 insertions(+), 8 deletions(-)

(limited to 'drivers/usb/chipidea/udc.c')

diff --git a/drivers/usb/chipidea/core.c b/drivers/usb/chipidea/core.c
index 6a6379a8f1f..128b92ba58a 100644
--- a/drivers/usb/chipidea/core.c
+++ b/drivers/usb/chipidea/core.c
@@ -42,7 +42,6 @@
  * - Not Supported: 15 & 16 (ISO)
  *
  * TODO List
- * - OTG
  * - Interrupt Traffic
  * - GET_STATUS(device) - always reports 0
  * - Gadget API (majority of optional features)
@@ -74,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[] = {
@@ -411,8 +411,14 @@ 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_otgsc(ci, ~0);
+		if (ci_otg_is_fsm_mode(ci)) {
+			ret = ci_otg_fsm_irq(ci);
+			if (ret == IRQ_HANDLED)
+				return ret;
+		}
+	}
 
 	/*
 	 * Handle id change interrupt, it indicates device/host function
@@ -691,10 +697,13 @@ static int ci_hdrc_probe(struct platform_device *pdev)
 	if (ci->role == CI_ROLE_GADGET)
 		ci_handle_vbus_change(ci);
 
-	ret = ci_role_start(ci, ci->role);
-	if (ret) {
-		dev_err(dev, "can't start %s role\n", ci_role(ci)->name);
-		goto stop;
+	if (!ci_otg_is_fsm_mode(ci)) {
+		ret = ci_role_start(ci, ci->role);
+		if (ret) {
+			dev_err(dev, "can't start %s role\n",
+						ci_role(ci)->name);
+			goto stop;
+		}
 	}
 
 	platform_set_drvdata(pdev, ci);
@@ -703,6 +712,9 @@ static int ci_hdrc_probe(struct platform_device *pdev)
 	if (ret)
 		goto stop;
 
+	if (ci_otg_is_fsm_mode(ci))
+		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 d76db51ecda..38e340cba1f 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>
@@ -91,6 +91,11 @@ static void ci_otg_work(struct work_struct *work)
 {
 	struct ci_hdrc *ci = container_of(work, struct ci_hdrc, work);
 
+	if (ci_otg_is_fsm_mode(ci) && !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 bb64fb486ee..67902a16cb7 100644
--- a/drivers/usb/chipidea/otg_fsm.c
+++ b/drivers/usb/chipidea/otg_fsm.c
@@ -13,6 +13,10 @@
 /*
  * This file mainly handles OTG fsm, it includes OTG fsm operations
  * for HNP and SRP.
+ *
+ * TODO List
+ * - ADP
+ * - OTG test device
  */
 
 #include <linux/usb/otg.h>
@@ -93,6 +97,33 @@ static void ci_otg_del_timer(struct ci_hdrc *ci, enum ci_otg_fsm_timer_index t)
 		hw_write_otgsc(ci, OTGSC_1MSIE, 0);
 }
 
+/*
+ * Reduce timer count by 1, and find timeout conditions.
+ * Called by otg 1ms timer interrupt
+ */
+static inline int ci_otg_tick_timer(struct ci_hdrc *ci)
+{
+	struct ci_otg_fsm_timer *tmp_timer, *del_tmp;
+	struct list_head *active_timers = &ci->fsm_timer->active_timers;
+	int expired = 0;
+
+	list_for_each_entry_safe(tmp_timer, del_tmp, active_timers, list) {
+		tmp_timer->count--;
+		/* check if timer expires */
+		if (!tmp_timer->count) {
+			list_del(&tmp_timer->list);
+			tmp_timer->function(ci, tmp_timer->data);
+			expired = 1;
+		}
+	}
+
+	/* disable 1ms irq if there is no any timer active */
+	if ((expired == 1) && list_empty(active_timers))
+		hw_write_otgsc(ci, OTGSC_1MSIE, 0);
+
+	return expired;
+}
+
 /* The timeout callback function to set time out bit */
 static void set_tmout(void *ptr, unsigned long indicator)
 {
@@ -394,6 +425,218 @@ static struct otg_fsm_ops ci_otg_ops = {
 	.start_gadget = ci_otg_start_gadget,
 };
 
+int ci_otg_fsm_work(struct ci_hdrc *ci)
+{
+	/*
+	 * Don't do fsm transition for B device
+	 * when there is no gadget class driver
+	 */
+	if (ci->fsm.id && !(ci->driver) &&
+		ci->transceiver->state < OTG_STATE_A_IDLE)
+		return 0;
+
+	if (otg_statemachine(&ci->fsm)) {
+		if (ci->transceiver->state == OTG_STATE_A_IDLE) {
+			/*
+			 * Further state change for cases:
+			 * a_idle to b_idle; or
+			 * a_idle to a_wait_vrise due to ID change(1->0), so
+			 * B-dev becomes A-dev can try to start new session
+			 * consequently; or
+			 * a_idle to a_wait_vrise when power up
+			 */
+			if ((ci->fsm.id) || (ci->id_event) ||
+						(ci->fsm.power_up)) {
+				disable_irq_nosync(ci->irq);
+				queue_work(ci->wq, &ci->work);
+			}
+			if (ci->id_event)
+				ci->id_event = false;
+		} else if (ci->transceiver->state == OTG_STATE_B_IDLE) {
+			if (ci->fsm.b_sess_vld) {
+				ci->fsm.power_up = 0;
+				/*
+				 * Further transite to b_periphearl state
+				 * when register gadget driver with vbus on
+				 */
+				disable_irq_nosync(ci->irq);
+				queue_work(ci->wq, &ci->work);
+			}
+		}
+	}
+	return 0;
+}
+
+/*
+ * Update fsm variables in each state if catching expected interrupts,
+ * called by otg fsm isr.
+ */
+static void ci_otg_fsm_event(struct ci_hdrc *ci)
+{
+	u32 intr_sts, otg_bsess_vld, port_conn;
+	struct otg_fsm *fsm = &ci->fsm;
+
+	intr_sts = hw_read_intr_status(ci);
+	otg_bsess_vld = hw_read_otgsc(ci, OTGSC_BSV);
+	port_conn = hw_read(ci, OP_PORTSC, PORTSC_CCS);
+
+	switch (ci->transceiver->state) {
+	case OTG_STATE_A_WAIT_BCON:
+		if (port_conn) {
+			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 (otg_bsess_vld && (intr_sts & USBi_PCI) && port_conn) {
+			fsm->b_sess_vld = 1;
+			disable_irq_nosync(ci->irq);
+			queue_work(ci->wq, &ci->work);
+		}
+		break;
+	case OTG_STATE_B_PERIPHERAL:
+		if ((intr_sts & USBi_SLI) && port_conn && otg_bsess_vld) {
+			fsm->a_bus_suspend = 1;
+			disable_irq_nosync(ci->irq);
+			queue_work(ci->wq, &ci->work);
+		} else if (intr_sts & USBi_PCI) {
+			if (fsm->a_bus_suspend == 1)
+				fsm->a_bus_suspend = 0;
+		}
+		break;
+	case OTG_STATE_B_HOST:
+		if ((intr_sts & USBi_PCI) && !port_conn) {
+			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 (intr_sts & USBi_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 (intr_sts & USBi_URI)
+			ci_otg_del_timer(ci, A_BIDL_ADIS);
+
+		if (intr_sts & USBi_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 ((intr_sts & USBi_PCI) && !port_conn) {
+			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 ((intr_sts & USBi_PCI) && !port_conn) {
+			fsm->b_conn = 0;
+			disable_irq_nosync(ci->irq);
+			queue_work(ci->wq, &ci->work);
+		}
+		break;
+	case OTG_STATE_B_WAIT_ACON:
+		if ((intr_sts & USBi_PCI) && port_conn) {
+			fsm->a_conn = 1;
+			disable_irq_nosync(ci->irq);
+			queue_work(ci->wq, &ci->work);
+		}
+		break;
+	default:
+		break;
+	}
+}
+
+/*
+ * ci_otg_irq - otg fsm related irq handling
+ * and also update otg fsm variable by monitoring usb host and udc
+ * state change interrupts.
+ * @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;
+
+	otgsc = hw_read_otgsc(ci, ~0);
+	otg_int_src = otgsc & OTGSC_INT_STATUS_BITS & (otgsc >> 8);
+	fsm->id = (otgsc & OTGSC_ID) ? 1 : 0;
+
+	if (otg_int_src) {
+		if (otg_int_src & OTGSC_1MSIS) {
+			hw_write_otgsc(ci, OTGSC_1MSIS, OTGSC_1MSIS);
+			retval = ci_otg_tick_timer(ci);
+			return IRQ_HANDLED;
+		} else if (otg_int_src & OTGSC_DPIS) {
+			hw_write_otgsc(ci, OTGSC_DPIS, OTGSC_DPIS);
+			fsm->a_srp_det = 1;
+			fsm->a_bus_drop = 0;
+		} else if (otg_int_src & OTGSC_IDIS) {
+			hw_write_otgsc(ci, OTGSC_IDIS, OTGSC_IDIS);
+			if (fsm->id == 0) {
+				fsm->a_bus_drop = 0;
+				fsm->a_bus_req = 1;
+				ci->id_event = true;
+			}
+		} else if (otg_int_src & OTGSC_BSVIS) {
+			hw_write_otgsc(ci, OTGSC_BSVIS, 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) {
+			hw_write_otgsc(ci, OTGSC_AVVIS, 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);
+
+	return retval;
+}
+
+void ci_hdrc_otg_fsm_start(struct ci_hdrc *ci)
+{
+	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 420f081e7e6..6ec8247d817 100644
--- a/drivers/usb/chipidea/otg_fsm.h
+++ b/drivers/usb/chipidea/otg_fsm.h
@@ -92,6 +92,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
 
@@ -100,6 +103,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 */
diff --git a/drivers/usb/chipidea/udc.c b/drivers/usb/chipidea/udc.c
index cba7fd63d6e..150592f10b3 100644
--- a/drivers/usb/chipidea/udc.c
+++ b/drivers/usb/chipidea/udc.c
@@ -28,6 +28,7 @@
 #include "bits.h"
 #include "debug.h"
 #include "otg.h"
+#include "otg_fsm.h"
 
 /* control endpoint description */
 static const struct usb_endpoint_descriptor
@@ -1644,6 +1645,13 @@ static int ci_udc_start(struct usb_gadget *gadget,
 		return retval;
 
 	ci->driver = driver;
+
+	/* Start otg fsm for B-device */
+	if (ci_otg_is_fsm_mode(ci) && ci->fsm.id) {
+		ci_hdrc_otg_fsm_start(ci);
+		return retval;
+	}
+
 	pm_runtime_get_sync(&ci->gadget.dev);
 	if (ci->vbus_active) {
 		spin_lock_irqsave(&ci->lock, flags);
-- 
cgit v1.2.3-70-g09d2


From 2dbd633f3a5ec60cec1bb33b86513d768730681b Mon Sep 17 00:00:00 2001
From: Peter Chen <peter.chen@freescale.com>
Date: Fri, 23 May 2014 08:12:48 +0800
Subject: usb: chipidea: udc: delete useless code

Delete useless code

Signed-off-by: Peter Chen <peter.chen@freescale.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
---
 drivers/usb/chipidea/udc.c | 1 -
 1 file changed, 1 deletion(-)

(limited to 'drivers/usb/chipidea/udc.c')

diff --git a/drivers/usb/chipidea/udc.c b/drivers/usb/chipidea/udc.c
index 150592f10b3..d8ab4c190aa 100644
--- a/drivers/usb/chipidea/udc.c
+++ b/drivers/usb/chipidea/udc.c
@@ -823,7 +823,6 @@ __acquires(hwep->lock)
 	if ((setup->bRequestType & USB_RECIP_MASK) == USB_RECIP_DEVICE) {
 		/* Assume that device is bus powered for now. */
 		*(u16 *)req->buf = ci->remote_wakeup << 1;
-		retval = 0;
 	} else if ((setup->bRequestType & USB_RECIP_MASK) \
 		   == USB_RECIP_ENDPOINT) {
 		dir = (le16_to_cpu(setup->wIndex) & USB_ENDPOINT_DIR_MASK) ?
-- 
cgit v1.2.3-70-g09d2


From 10775eb17bee1ccc02ac22bb85e50699e0576a84 Mon Sep 17 00:00:00 2001
From: Peter Chen <peter.chen@freescale.com>
Date: Sun, 4 May 2014 09:24:44 +0800
Subject: usb: chipidea: udc: update gadget states according to ch9

Update device states according to ch9 in USB 2.0 specification

Signed-off-by: Peter Chen <peter.chen@freescale.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
---
 drivers/usb/chipidea/udc.c | 10 ++++++++--
 1 file changed, 8 insertions(+), 2 deletions(-)

(limited to 'drivers/usb/chipidea/udc.c')

diff --git a/drivers/usb/chipidea/udc.c b/drivers/usb/chipidea/udc.c
index d8ab4c190aa..69425b3cb6b 100644
--- a/drivers/usb/chipidea/udc.c
+++ b/drivers/usb/chipidea/udc.c
@@ -709,6 +709,8 @@ __acquires(ci->lock)
 	if (ci->status == NULL)
 		retval = -ENOMEM;
 
+	usb_gadget_set_state(&ci->gadget, USB_STATE_DEFAULT);
+
 done:
 	spin_lock(&ci->lock);
 
@@ -864,6 +866,8 @@ isr_setup_status_complete(struct usb_ep *ep, struct usb_request *req)
 	if (ci->setaddr) {
 		hw_usb_set_address(ci, ci->address);
 		ci->setaddr = false;
+		if (ci->address)
+			usb_gadget_set_state(&ci->gadget, USB_STATE_ADDRESS);
 	}
 
 	spin_lock_irqsave(&ci->lock, flags);
@@ -1466,7 +1470,7 @@ static int ci_udc_vbus_session(struct usb_gadget *_gadget, int is_active)
 			pm_runtime_get_sync(&_gadget->dev);
 			hw_device_reset(ci, USBMODE_CM_DC);
 			hw_device_state(ci, ci->ep0out->qh.dma);
-			dev_dbg(ci->dev, "Connected to host\n");
+			usb_gadget_set_state(_gadget, USB_STATE_POWERED);
 		} else {
 			if (ci->driver)
 				ci->driver->disconnect(&ci->gadget);
@@ -1476,7 +1480,7 @@ static int ci_udc_vbus_session(struct usb_gadget *_gadget, int is_active)
 				CI_HDRC_CONTROLLER_STOPPED_EVENT);
 			_gadget_stop_activity(&ci->gadget);
 			pm_runtime_put_sync(&_gadget->dev);
-			dev_dbg(ci->dev, "Disconnected from host\n");
+			usb_gadget_set_state(_gadget, USB_STATE_NOTATTACHED);
 		}
 	}
 
@@ -1749,6 +1753,8 @@ static irqreturn_t udc_irq(struct ci_hdrc *ci)
 				ci->suspended = 1;
 				spin_unlock(&ci->lock);
 				ci->driver->suspend(&ci->gadget);
+				usb_gadget_set_state(&ci->gadget,
+						USB_STATE_SUSPENDED);
 				spin_lock(&ci->lock);
 			}
 		}
-- 
cgit v1.2.3-70-g09d2


From e4adcff09ca39ecbcc4851d40d0f0a5458e7b77a Mon Sep 17 00:00:00 2001
From: Peter Chen <peter.chen@freescale.com>
Date: Wed, 2 Jul 2014 12:16:31 +0800
Subject: usb: chipidea: udc: delete td from req's td list at ep_dequeue

We need to delete un-finished td from current request's td list
at ep_dequeue API, otherwise, this non-user td will be remained
at td list before this request is freed. So if we do ep_queue->
ep_dequeue->ep_queue sequence, when the complete interrupt for
the second ep_queue comes, we search td list for this request,
the first td (added by the first ep_queue) will be handled, and
its status is still active, so we will consider the this transfer
still not be completed, but in fact, it has completed. It causes
the peripheral side considers it never receives current data for
this transfer.

We met this problem when do "Error Recovery Test - Device Configured"
test item for USBCV2 MSC test, the host has never received ACK for
the IN token for CSW due to peripheral considers it does not get this
CBW, the USBCV test log like belows:

--------------------------------------------------------------------------
INFO
Issuing BOT MSC Reset, reset should always succeed
INFO
Retrieving status on CBW endpoint
INFO
CBW endpoint status = 0x0
INFO
Retrieving status on CSW endpoint
INFO
CSW endpoint status = 0x0
INFO
Issuing required command (Test Unit Ready) to verify device has recovered
INFO
Issuing CBW (attempt #1):
INFO
|----- CBW LUN                  = 0x0
INFO
|----- CBW Flags                = 0x0
INFO
|----- CBW Data Transfer Length = 0x0
INFO
|----- CBW CDB Length           = 0x6
INFO
|----- CBW CDB-00 = 0x0
INFO
|----- CBW CDB-01 = 0x0
INFO
|----- CBW CDB-02 = 0x0
INFO
|----- CBW CDB-03 = 0x0
INFO
|----- CBW CDB-04 = 0x0
INFO
|----- CBW CDB-05 = 0x0
INFO
Issuing CSW : try 1
INFO
CSW Bulk Request timed out!
ERROR
Failed CSW phase : should have been success or stall
FAIL
(5.3.4) The CSW status value must be 0x00, 0x01, or 0x02.
ERROR
BOTCommonMSCRequest failed:  error=80004000

Cc: Andrzej Pietrasiewicz <andrzej.p@samsung.com>
Cc: stable@vger.kernel.org
Signed-off-by: Peter Chen <peter.chen@freescale.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
---
 drivers/usb/chipidea/udc.c | 7 +++++++
 1 file changed, 7 insertions(+)

(limited to 'drivers/usb/chipidea/udc.c')

diff --git a/drivers/usb/chipidea/udc.c b/drivers/usb/chipidea/udc.c
index 69425b3cb6b..9d2b673f90e 100644
--- a/drivers/usb/chipidea/udc.c
+++ b/drivers/usb/chipidea/udc.c
@@ -1321,6 +1321,7 @@ static int ep_dequeue(struct usb_ep *ep, struct usb_request *req)
 	struct ci_hw_ep  *hwep  = container_of(ep,  struct ci_hw_ep, ep);
 	struct ci_hw_req *hwreq = container_of(req, struct ci_hw_req, req);
 	unsigned long flags;
+	struct td_node *node, *tmpnode;
 
 	if (ep == NULL || req == NULL || hwreq->req.status != -EALREADY ||
 		hwep->ep.desc == NULL || list_empty(&hwreq->queue) ||
@@ -1331,6 +1332,12 @@ static int ep_dequeue(struct usb_ep *ep, struct usb_request *req)
 
 	hw_ep_flush(hwep->ci, hwep->num, hwep->dir);
 
+	list_for_each_entry_safe(node, tmpnode, &hwreq->tds, td) {
+		dma_pool_free(hwep->td_pool, node->ptr, node->dma);
+		list_del(&node->td);
+		kfree(node);
+	}
+
 	/* pop request */
 	list_del_init(&hwreq->queue);
 
-- 
cgit v1.2.3-70-g09d2