All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH v4 07/13] usb: otg: add OTG core
  2015-08-24 13:21 [PATCH v4 00/13] USB: OTG/DRD Core functionality Roger Quadros
@ 2015-08-24 13:21   ` Roger Quadros
  0 siblings, 0 replies; 48+ messages in thread
From: Roger Quadros @ 2015-08-24 13:21 UTC (permalink / raw)
  To: stern, balbi, gregkh, peter.chen
  Cc: dan.j.williams, jun.li, mathias.nyman, tony, Joao.Pinto, abrestic,
	linux-usb, linux-kernel, linux-omap, Roger Quadros

The OTG core instantiates the OTG Finite State Machine
per OTG controller and manages starting/stopping the
host and gadget controllers based on the bus state.

It provides APIs for the following tasks

- Registering an OTG capable controller
- Registering Host and Gadget controllers to OTG core
- Providing inputs to and kicking the OTG state machine

Signed-off-by: Roger Quadros <rogerq@ti.com>
---
 MAINTAINERS                  |    4 +-
 drivers/usb/Kconfig          |    2 +-
 drivers/usb/Makefile         |    1 +
 drivers/usb/common/Makefile  |    3 +-
 drivers/usb/common/usb-otg.c | 1061 ++++++++++++++++++++++++++++++++++++++++++
 drivers/usb/common/usb-otg.h |   71 +++
 drivers/usb/core/Kconfig     |   11 +-
 include/linux/usb/otg.h      |  189 +++++++-
 8 files changed, 1321 insertions(+), 21 deletions(-)
 create mode 100644 drivers/usb/common/usb-otg.c
 create mode 100644 drivers/usb/common/usb-otg.h

diff --git a/MAINTAINERS b/MAINTAINERS
index a9ae6c1..4bc1279 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -10667,12 +10667,14 @@ S:	Maintained
 F:	Documentation/usb/ohci.txt
 F:	drivers/usb/host/ohci*
 
-USB OTG FSM (Finite State Machine)
+USB OTG/DRD core and FSM (Finite State Machine)
 M:	Peter Chen <Peter.Chen@freescale.com>
+M:	Roger Quadros <rogerq@ti.com>
 T:	git git://git.kernel.org/pub/scm/linux/kernel/git/peter.chen/usb.git
 L:	linux-usb@vger.kernel.org
 S:	Maintained
 F:	drivers/usb/common/usb-otg-fsm.c
+F:	drivers/usb/common/usb-otg.c
 
 USB OVER IP DRIVER
 M:	Valentina Manea <valentina.manea.m@gmail.com>
diff --git a/drivers/usb/Kconfig b/drivers/usb/Kconfig
index 8ed451d..5b625e2 100644
--- a/drivers/usb/Kconfig
+++ b/drivers/usb/Kconfig
@@ -32,7 +32,7 @@ if USB_SUPPORT
 config USB_COMMON
 	tristate
 	default y
-	depends on USB || USB_GADGET
+	depends on USB || USB_GADGET || USB_OTG
 
 config USB_ARCH_HAS_HCD
 	def_bool y
diff --git a/drivers/usb/Makefile b/drivers/usb/Makefile
index d8926c6..769d13b 100644
--- a/drivers/usb/Makefile
+++ b/drivers/usb/Makefile
@@ -60,5 +60,6 @@ obj-$(CONFIG_USB_RENESAS_USBHS)	+= renesas_usbhs/
 obj-$(CONFIG_USB_GADGET)	+= gadget/
 
 obj-$(CONFIG_USB_COMMON)	+= common/
+obj-$(CONFIG_USB_OTG)		+= common/
 
 obj-$(CONFIG_USBIP_CORE)	+= usbip/
diff --git a/drivers/usb/common/Makefile b/drivers/usb/common/Makefile
index 6bbb3ec..730d928 100644
--- a/drivers/usb/common/Makefile
+++ b/drivers/usb/common/Makefile
@@ -6,5 +6,6 @@ obj-$(CONFIG_USB_COMMON)	  += usb-common.o
 usb-common-y			  += common.o
 usb-common-$(CONFIG_USB_LED_TRIG) += led.o
 
-obj-$(CONFIG_USB_OTG_FSM) += usb-otg-fsm.o
 obj-$(CONFIG_USB_ULPI_BUS)	+= ulpi.o
+usbotg-y		:= usb-otg.o usb-otg-fsm.o
+obj-$(CONFIG_USB_OTG)	+= usbotg.o
diff --git a/drivers/usb/common/usb-otg.c b/drivers/usb/common/usb-otg.c
new file mode 100644
index 0000000..930c2fe
--- /dev/null
+++ b/drivers/usb/common/usb-otg.c
@@ -0,0 +1,1061 @@
+/**
+ * drivers/usb/common/usb-otg.c - USB OTG core
+ *
+ * Copyright (C) 2015 Texas Instruments Incorporated - http://www.ti.com
+ * Author: Roger Quadros <rogerq@ti.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/kernel.h>
+#include <linux/ktime.h>
+#include <linux/hrtimer.h>
+#include <linux/list.h>
+#include <linux/of.h>
+#include <linux/of_platform.h>
+#include <linux/usb/otg.h>
+#include <linux/usb/phy.h>	/* enum usb_otg_state */
+#include <linux/usb/gadget.h>
+#include <linux/workqueue.h>
+
+#include "usb-otg.h"
+
+struct otg_gcd {
+	struct usb_gadget *gadget;
+	struct otg_gadget_ops *ops;
+};
+
+/* OTG device list */
+LIST_HEAD(otg_list);
+static DEFINE_MUTEX(otg_list_mutex);
+
+/* Hosts and Gadgets waiting for OTG controller */
+struct otg_wait_data {
+	struct device *dev;		/* OTG controller device */
+
+	struct otg_hcd primary_hcd;
+	struct otg_hcd shared_hcd;
+	struct otg_gcd gcd;
+	struct list_head list;
+};
+
+LIST_HEAD(wait_list);
+static DEFINE_MUTEX(wait_list_mutex);
+
+static int usb_otg_hcd_is_primary_hcd(struct usb_hcd *hcd)
+{
+	if (!hcd->primary_hcd)
+		return 1;
+	return hcd == hcd->primary_hcd;
+}
+
+/**
+ * Check if the OTG device is in our wait list and return
+ * otg_wait_data, else NULL.
+ *
+ * wait_list_mutex must be held.
+ */
+static struct otg_wait_data *usb_otg_get_wait(struct device *otg_dev)
+{
+	struct otg_wait_data *wait;
+
+	if (!otg_dev)
+		return NULL;
+
+	/* is there an entry for this otg_dev ?*/
+	list_for_each_entry(wait, &wait_list, list) {
+		if (wait->dev == otg_dev)
+			return wait;
+	}
+
+	return NULL;
+}
+
+/**
+ * Add the hcd to our wait list
+ */
+static int usb_otg_hcd_wait_add(struct device *otg_dev, struct usb_hcd *hcd,
+				unsigned int irqnum, unsigned long irqflags,
+				struct otg_hcd_ops *ops)
+{
+	struct otg_wait_data *wait;
+	int ret = -EINVAL;
+
+	mutex_lock(&wait_list_mutex);
+
+	wait = usb_otg_get_wait(otg_dev);
+	if (!wait) {
+		/* Not yet in wait list? allocate and add */
+		wait = kzalloc(sizeof(*wait), GFP_KERNEL);
+		if (!wait) {
+			ret = -ENOMEM;
+			goto fail;
+		}
+
+		wait->dev = otg_dev;
+		list_add_tail(&wait->list, &wait_list);
+	}
+
+	if (usb_otg_hcd_is_primary_hcd(hcd)) {
+		if (wait->primary_hcd.hcd)	/* already assigned? */
+			goto fail;
+
+		wait->primary_hcd.hcd = hcd;
+		wait->primary_hcd.irqnum = irqnum;
+		wait->primary_hcd.irqflags = irqflags;
+		wait->primary_hcd.ops = ops;
+	} else {
+		if (wait->shared_hcd.hcd)	/* already assigned? */
+			goto fail;
+
+		wait->shared_hcd.hcd = hcd;
+		wait->shared_hcd.irqnum = irqnum;
+		wait->shared_hcd.irqflags = irqflags;
+		wait->shared_hcd.ops = ops;
+	}
+
+	mutex_unlock(&wait_list_mutex);
+	return 0;
+
+fail:
+	mutex_unlock(&wait_list_mutex);
+	return ret;
+}
+
+/**
+ * Check and free wait list entry if empty
+ *
+ * wait_list_mutex must be held
+ */
+static void usb_otg_check_free_wait(struct otg_wait_data *wait)
+{
+	if (wait->primary_hcd.hcd || wait->shared_hcd.hcd || wait->gcd.gadget)
+		return;
+
+	list_del(&wait->list);
+	kfree(wait);
+}
+
+/**
+ * Remove the hcd from our wait list
+ */
+static int usb_otg_hcd_wait_remove(struct usb_hcd *hcd)
+{
+	struct otg_wait_data *wait;
+
+	mutex_lock(&wait_list_mutex);
+
+	/* is there an entry for this hcd ?*/
+	list_for_each_entry(wait, &wait_list, list) {
+		if (wait->primary_hcd.hcd == hcd) {
+			wait->primary_hcd.hcd = 0;
+			goto found;
+		} else if (wait->shared_hcd.hcd == hcd) {
+			wait->shared_hcd.hcd = 0;
+			goto found;
+		}
+	}
+
+	mutex_unlock(&wait_list_mutex);
+	return -EINVAL;
+
+found:
+	usb_otg_check_free_wait(wait);
+	mutex_unlock(&wait_list_mutex);
+
+	return 0;
+}
+
+/**
+ * Add the gadget to our wait list
+ */
+static int usb_otg_gadget_wait_add(struct device *otg_dev,
+				   struct usb_gadget *gadget,
+				   struct otg_gadget_ops *ops)
+{
+	struct otg_wait_data *wait;
+	int ret = -EINVAL;
+
+	mutex_lock(&wait_list_mutex);
+
+	wait = usb_otg_get_wait(otg_dev);
+	if (!wait) {
+		/* Not yet in wait list? allocate and add */
+		wait = kzalloc(sizeof(*wait), GFP_KERNEL);
+		if (!wait) {
+			ret = -ENOMEM;
+			goto fail;
+		}
+
+		wait->dev = otg_dev;
+		list_add_tail(&wait->list, &wait_list);
+	}
+
+	if (wait->gcd.gadget) /* already assigned? */
+		goto fail;
+
+	wait->gcd.gadget = gadget;
+	wait->gcd.ops = ops;
+	mutex_unlock(&wait_list_mutex);
+
+	return 0;
+
+fail:
+	mutex_unlock(&wait_list_mutex);
+	return ret;
+}
+
+/**
+ * Remove the gadget from our wait list
+ */
+static int usb_otg_gadget_wait_remove(struct usb_gadget *gadget)
+{
+	struct otg_wait_data *wait;
+
+	mutex_lock(&wait_list_mutex);
+
+	/* is there an entry for this gadget ?*/
+	list_for_each_entry(wait, &wait_list, list) {
+		if (wait->gcd.gadget == gadget) {
+			wait->gcd.gadget = 0;
+			goto found;
+		}
+	}
+
+	mutex_unlock(&wait_list_mutex);
+
+	return -EINVAL;
+
+found:
+	usb_otg_check_free_wait(wait);
+	mutex_unlock(&wait_list_mutex);
+
+	return 0;
+}
+
+/**
+ * Register pending host/gadget and remove entry from wait list
+ */
+static void usb_otg_flush_wait(struct device *otg_dev)
+{
+	struct otg_wait_data *wait;
+	struct otg_hcd *host;
+	struct otg_gcd *gadget;
+
+	mutex_lock(&wait_list_mutex);
+
+	wait = usb_otg_get_wait(otg_dev);
+	if (!wait)
+		goto done;
+
+	dev_dbg(otg_dev, "otg: registering pending host/gadget\n");
+	gadget = &wait->gcd;
+	if (gadget)
+		usb_otg_register_gadget(gadget->gadget, gadget->ops);
+
+	host = &wait->primary_hcd;
+	if (host->hcd)
+		usb_otg_register_hcd(host->hcd, host->irqnum, host->irqflags,
+				     host->ops);
+
+	host = &wait->shared_hcd;
+	if (host->hcd)
+		usb_otg_register_hcd(host->hcd, host->irqnum, host->irqflags,
+				     host->ops);
+
+	list_del(&wait->list);
+	kfree(wait);
+
+done:
+	mutex_unlock(&wait_list_mutex);
+}
+
+/**
+ * Check if the OTG device is in our OTG list and return
+ * usb_otg data, else NULL.
+ *
+ * otg_list_mutex must be held.
+ */
+static struct usb_otg *usb_otg_get_data(struct device *otg_dev)
+{
+	struct usb_otg *otgd;
+
+	if (!otg_dev)
+		return NULL;
+
+	list_for_each_entry(otgd, &otg_list, list) {
+		if (otgd->dev == otg_dev)
+			return otgd;
+	}
+
+	return NULL;
+}
+
+/**
+ * Get OTG device from host or gadget device.
+ *
+ * For non device tree boot, the OTG controller is assumed to be
+ * the parent of the host/gadget device.
+ * For device tree boot, the OTG controller is derived from the
+ * "otg-controller" property.
+ */
+static struct device *usb_otg_get_device(struct device *hcd_gcd_dev)
+{
+	struct device *otg_dev;
+
+	if (!hcd_gcd_dev)
+		return NULL;
+
+	if (hcd_gcd_dev->of_node) {
+		struct device_node *np;
+		struct platform_device *pdev;
+
+		np = of_parse_phandle(hcd_gcd_dev->of_node, "otg-controller",
+				      0);
+		if (!np)
+			goto legacy;	/* continue legacy way */
+
+		pdev = of_find_device_by_node(np);
+		of_node_put(np);
+		if (!pdev) {
+			dev_err(&pdev->dev, "couldn't get otg-controller device\n");
+			return NULL;
+		}
+
+		otg_dev = &pdev->dev;
+		return otg_dev;
+	}
+
+legacy:
+	/* otg device is parent and must be registered */
+	otg_dev = hcd_gcd_dev->parent;
+	if (!usb_otg_get_data(otg_dev))
+		return NULL;
+
+	return otg_dev;
+}
+
+/**
+ * timer callback to set timeout bit and kick FSM
+ */
+static enum hrtimer_restart set_tmout(struct hrtimer *data)
+{
+	struct otg_timer *otgtimer;
+
+	otgtimer = container_of(data, struct otg_timer, timer);
+	if (otgtimer->timeout_bit)
+		*otgtimer->timeout_bit = 1;
+
+	usb_otg_sync_inputs(&otgtimer->otgd->fsm);
+
+	return HRTIMER_NORESTART;
+}
+
+/**
+ * Initialize one OTG timer with callback, timeout and timeout bit
+ */
+static void otg_timer_init(enum otg_fsm_timer id, struct usb_otg *otgd,
+			   enum hrtimer_restart (*callback)(struct hrtimer *),
+			   unsigned long expires_ms,
+			   int *timeout_bit)
+{
+	struct otg_timer *otgtimer = &otgd->timers[id];
+	struct hrtimer *timer = &otgtimer->timer;
+
+	otgtimer->timeout = ms_to_ktime(expires_ms);
+	hrtimer_init(timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
+	timer->function = callback;
+
+	otgtimer->timeout_bit = timeout_bit;
+	otgtimer->otgd = otgd;
+}
+
+/**
+ * Initialize standard OTG timers
+ */
+static void usb_otg_init_timers(struct usb_otg *otgd, unsigned *timeouts)
+{
+	struct otg_fsm *fsm = &otgd->fsm;
+	unsigned long tmouts[NUM_OTG_FSM_TIMERS];
+	int i;
+
+	/* set default timeouts */
+	tmouts[A_WAIT_VRISE] = TA_WAIT_VRISE;
+	tmouts[A_WAIT_VFALL] = TA_WAIT_VFALL;
+	tmouts[A_WAIT_BCON] = TA_WAIT_BCON;
+	tmouts[A_AIDL_BDIS] = TA_AIDL_BDIS;
+	tmouts[A_BIDL_ADIS] = TA_BIDL_ADIS;
+	tmouts[B_ASE0_BRST] = TB_ASE0_BRST;
+	tmouts[B_SE0_SRP] = TB_SE0_SRP;
+	tmouts[B_SRP_FAIL] = TB_SRP_FAIL;
+
+	/* set controller provided timeouts */
+	if (timeouts) {
+		for (i = 0; i < NUM_OTG_FSM_TIMERS; i++) {
+			if (timeouts[i])
+				tmouts[i] = timeouts[i];
+		}
+	}
+
+	otg_timer_init(A_WAIT_VRISE, otgd, set_tmout, TA_WAIT_VRISE,
+		       &fsm->a_wait_vrise_tmout);
+	otg_timer_init(A_WAIT_VFALL, otgd, set_tmout, TA_WAIT_VFALL,
+		       &fsm->a_wait_vfall_tmout);
+	otg_timer_init(A_WAIT_BCON, otgd, set_tmout, TA_WAIT_BCON,
+		       &fsm->a_wait_bcon_tmout);
+	otg_timer_init(A_AIDL_BDIS, otgd, set_tmout, TA_AIDL_BDIS,
+		       &fsm->a_aidl_bdis_tmout);
+	otg_timer_init(A_BIDL_ADIS, otgd, set_tmout, TA_BIDL_ADIS,
+		       &fsm->a_bidl_adis_tmout);
+	otg_timer_init(B_ASE0_BRST, otgd, set_tmout, TB_ASE0_BRST,
+		       &fsm->b_ase0_brst_tmout);
+
+	otg_timer_init(B_SE0_SRP, otgd, set_tmout, TB_SE0_SRP,
+		       &fsm->b_se0_srp);
+	otg_timer_init(B_SRP_FAIL, otgd, set_tmout, TB_SRP_FAIL,
+		       &fsm->b_srp_done);
+
+	/* FIXME: what about A_WAIT_ENUM? */
+}
+
+/**
+ * OTG FSM ops function to add timer
+ */
+static void usb_otg_add_timer(struct otg_fsm *fsm, enum otg_fsm_timer id)
+{
+	struct usb_otg *otgd = container_of(fsm, struct usb_otg, fsm);
+	struct otg_timer *otgtimer = &otgd->timers[id];
+	struct hrtimer *timer = &otgtimer->timer;
+
+	if (!otgd->fsm_running)
+		return;
+
+	/* if timer is already active, exit */
+	if (hrtimer_active(timer)) {
+		dev_err(otgd->dev, "otg: timer %d is already running\n", id);
+		return;
+	}
+
+	hrtimer_start(timer, otgtimer->timeout, HRTIMER_MODE_REL);
+}
+
+/**
+ * OTG FSM ops function to delete timer
+ */
+static void usb_otg_del_timer(struct otg_fsm *fsm, enum otg_fsm_timer id)
+{
+	struct usb_otg *otgd = container_of(fsm, struct usb_otg, fsm);
+	struct hrtimer *timer = &otgd->timers[id].timer;
+
+	hrtimer_cancel(timer);
+}
+
+/**
+ * Helper function to start/stop otg host. For use by otg controller.
+ */
+int usb_otg_start_host(struct otg_fsm *fsm, int on)
+{
+	struct usb_otg *otgd = container_of(fsm, struct usb_otg, fsm);
+	struct otg_hcd_ops *hcd_ops;
+
+	dev_dbg(otgd->dev, "otg: %s %d\n", __func__, on);
+	if (!fsm->otg->host) {
+		WARN_ONCE(1, "otg: fsm running without host\n");
+		return 0;
+	}
+
+	if (on) {
+		/* start host */
+		hcd_ops = otgd->primary_hcd.ops;
+		hcd_ops->add(otgd->primary_hcd.hcd, otgd->primary_hcd.irqnum,
+			     otgd->primary_hcd.irqflags);
+		if (otgd->shared_hcd.hcd) {
+			hcd_ops = otgd->shared_hcd.ops;
+			hcd_ops->add(otgd->shared_hcd.hcd,
+				     otgd->shared_hcd.irqnum,
+				     otgd->shared_hcd.irqflags);
+		}
+	} else {
+		/* stop host */
+		if (otgd->shared_hcd.hcd) {
+			hcd_ops = otgd->shared_hcd.ops;
+			hcd_ops->remove(otgd->shared_hcd.hcd);
+		}
+		hcd_ops = otgd->primary_hcd.ops;
+		hcd_ops->remove(otgd->primary_hcd.hcd);
+	}
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(usb_otg_start_host);
+
+/**
+ * Helper function to start/stop otg gadget. For use by otg controller.
+ */
+int usb_otg_start_gadget(struct otg_fsm *fsm, int on)
+{
+	struct usb_otg *otgd = container_of(fsm, struct usb_otg, fsm);
+	struct usb_gadget *gadget = fsm->otg->gadget;
+
+	dev_dbg(otgd->dev, "otg: %s %d\n", __func__, on);
+	if (!gadget) {
+		WARN_ONCE(1, "otg: fsm running without gadget\n");
+		return 0;
+	}
+
+	if (on)
+		otgd->gadget_ops->start(fsm->otg->gadget);
+	else
+		otgd->gadget_ops->stop(fsm->otg->gadget);
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(usb_otg_start_gadget);
+
+/**
+ * OTG FSM work function
+ */
+static void usb_otg_work(struct work_struct *work)
+{
+	struct usb_otg *otgd = container_of(work, struct usb_otg, work);
+
+	otg_statemachine(&otgd->fsm);
+}
+
+/**
+ * usb_otg_register() - Register the OTG device to OTG core
+ * @dev: OTG controller device.
+ * @config: OTG configuration.
+ *
+ * Register the OTG controller device with the USB OTG core.
+ * The associated Host and Gadget controllers will be prevented from
+ * being started till both are available for use.
+ *
+ * For non device tree boots, the OTG controller device must be the
+ * parent node of the Host and Gadget controllers.
+ *
+ * For device tree case, the otg-controller property must be present
+ * in the Host and Gadget controller node and it must point to the
+ * same OTG controller node.
+ *
+ * Return: struct otg_fsm * if success, NULL if error.
+ */
+struct otg_fsm *usb_otg_register(struct device *dev,
+				 struct usb_otg_config *config)
+{
+	struct usb_otg *otgd;
+	struct otg_wait_data *wait;
+	int ret = 0;
+
+	if (!dev || !config || !config->fsm_ops)
+		return ERR_PTR(-EINVAL);
+
+	/* already in list? */
+	mutex_lock(&otg_list_mutex);
+	if (usb_otg_get_data(dev)) {
+		dev_err(dev, "otg: %s: device already in otg list\n",
+			__func__);
+		ret = -EINVAL;
+		goto unlock;
+	}
+
+	/* allocate and add to list */
+	otgd = kzalloc(sizeof(*otgd), GFP_KERNEL);
+	if (!otgd) {
+		ret = -ENOMEM;
+		goto unlock;
+	}
+
+	otgd->dev = dev;
+	otgd->caps = &config->otg_caps;
+	INIT_WORK(&otgd->work, usb_otg_work);
+	otgd->wq = create_singlethread_workqueue("usb_otg");
+	if (!otgd->wq) {
+		dev_err(dev, "otg: %s: can't create workqueue\n",
+			__func__);
+		ret = -ENOMEM;
+		goto err_wq;
+	}
+
+	usb_otg_init_timers(otgd, config->otg_timeouts);
+
+	/* create copy of original ops */
+	otgd->fsm_ops = *config->fsm_ops;
+	/* FIXME: we ignore caller's timer ops */
+	otgd->fsm_ops.add_timer = usb_otg_add_timer;
+	otgd->fsm_ops.del_timer = usb_otg_del_timer;
+	/* set otg ops */
+	otgd->fsm.ops = &otgd->fsm_ops;
+	otgd->fsm.otg = otgd;
+
+	mutex_init(&otgd->fsm.lock);
+
+	list_add_tail(&otgd->list, &otg_list);
+	mutex_unlock(&otg_list_mutex);
+
+	/* were we in wait list? */
+	mutex_lock(&wait_list_mutex);
+	wait = usb_otg_get_wait(dev);
+	mutex_unlock(&wait_list_mutex);
+	if (wait) {
+		/* register pending host/gadget and flush from list */
+		usb_otg_flush_wait(dev);
+	}
+
+	return &otgd->fsm;
+
+err_wq:
+	kfree(otgd);
+unlock:
+	mutex_unlock(&otg_list_mutex);
+	return ERR_PTR(ret);
+}
+EXPORT_SYMBOL_GPL(usb_otg_register);
+
+/**
+ * usb_otg_unregister() - Unregister the OTG device from USB OTG core
+ * @dev: OTG controller device.
+ *
+ * Unregister OTG controller device from USB OTG core.
+ * Prevents unregistering till both the associated Host and Gadget controllers
+ * have unregistered from the OTG core.
+ *
+ * Return: 0 on success, error value otherwise.
+ */
+int usb_otg_unregister(struct device *dev)
+{
+	struct usb_otg *otgd;
+
+	mutex_lock(&otg_list_mutex);
+	otgd = usb_otg_get_data(dev);
+	if (!otgd) {
+		dev_err(dev, "otg: %s: device not in otg list\n",
+			__func__);
+		mutex_unlock(&otg_list_mutex);
+		return -EINVAL;
+	}
+
+	/* prevent unregister till both host & gadget have unregistered */
+	if (otgd->fsm.otg->host || otgd->fsm.otg->gadget) {
+		dev_err(dev, "otg: %s: host/gadget still registered\n",
+			__func__);
+		return -EBUSY;
+	}
+
+	/* OTG FSM is halted when host/gadget unregistered */
+	destroy_workqueue(otgd->wq);
+
+	/* remove from otg list */
+	list_del(&otgd->list);
+	kfree(otgd);
+	mutex_unlock(&otg_list_mutex);
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(usb_otg_unregister);
+
+/**
+ * start/kick the OTG FSM if we can
+ * fsm->lock must be held
+ */
+static void usb_otg_start_fsm(struct otg_fsm *fsm)
+{
+	struct usb_otg *otgd = container_of(fsm, struct usb_otg, fsm);
+
+	if (otgd->fsm_running)
+		goto kick_fsm;
+
+	if (!fsm->otg->host) {
+		dev_info(otgd->dev, "otg: can't start till host registers\n");
+		return;
+	}
+
+	if (!fsm->otg->gadget) {
+		dev_info(otgd->dev, "otg: can't start till gadget registers\n");
+		return;
+	}
+
+	otgd->fsm_running = true;
+kick_fsm:
+	queue_work(otgd->wq, &otgd->work);
+}
+
+/**
+ * stop the OTG FSM. Stops Host & Gadget controllers as well.
+ * fsm->lock must be held
+ */
+static void usb_otg_stop_fsm(struct otg_fsm *fsm)
+{
+	struct usb_otg *otgd = container_of(fsm, struct usb_otg, fsm);
+	int i;
+
+	if (!otgd->fsm_running)
+		return;
+
+	/* no more new events queued */
+	otgd->fsm_running = false;
+
+	/* Stop state machine / timers */
+	for (i = 0; i < ARRAY_SIZE(otgd->timers); i++)
+		hrtimer_cancel(&otgd->timers[i].timer);
+
+	flush_workqueue(otgd->wq);
+	fsm->otg->state = OTG_STATE_UNDEFINED;
+
+	/* stop host/gadget immediately */
+	if (fsm->protocol == PROTO_HOST)
+		otg_start_host(fsm, 0);
+	else if (fsm->protocol == PROTO_GADGET)
+		otg_start_gadget(fsm, 0);
+	fsm->protocol = PROTO_UNDEF;
+}
+
+/**
+ * usb_otg_sync_inputs - Sync OTG inputs with the OTG state machine
+ * @fsm:	OTG FSM instance
+ *
+ * Used by the OTG driver to update the inputs to the OTG
+ * state machine.
+ *
+ * Can be called in IRQ context.
+ */
+void usb_otg_sync_inputs(struct otg_fsm *fsm)
+{
+	struct usb_otg *otgd = container_of(fsm, struct usb_otg, fsm);
+
+	/* Don't kick FSM till it has started */
+	if (!otgd->fsm_running)
+		return;
+
+	/* Kick FSM */
+	queue_work(otgd->wq, &otgd->work);
+}
+EXPORT_SYMBOL_GPL(usb_otg_sync_inputs);
+
+/**
+ * usb_otg_kick_fsm - Kick the OTG state machine
+ * @hcd_gcd_device:	Host/Gadget controller device
+ *
+ * Used by USB host/device stack to sync OTG related
+ * events to the OTG state machine.
+ * e.g. change in host_bus->b_hnp_enable, gadget->b_hnp_enable
+ *
+ * Returns: 0 on success, error value otherwise.
+ */
+int usb_otg_kick_fsm(struct device *hcd_gcd_device)
+{
+	struct usb_otg *otgd;
+
+	mutex_lock(&otg_list_mutex);
+	otgd = usb_otg_get_data(usb_otg_get_device(hcd_gcd_device));
+	mutex_unlock(&otg_list_mutex);
+	if (!otgd) {
+		dev_dbg(hcd_gcd_device, "otg: %s: invalid host/gadget device\n",
+			__func__);
+		return -ENODEV;
+	}
+
+	usb_otg_sync_inputs(&otgd->fsm);
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(usb_otg_kick_fsm);
+
+/**
+ * usb_otg_register_hcd - Register Host controller to OTG core
+ * @hcd:	Host controller device
+ * @irqnum:	interrupt number
+ * @irqflags:	interrupt flags
+ * @ops:	HCD ops to add/remove the HCD
+ *
+ * This is used by the USB Host stack to register the Host controller
+ * to the OTG core. Host controller must not be started by the
+ * caller as it is left upto the OTG state machine to do so.
+ *
+ * Returns: 0 on success, error value otherwise.
+ */
+int usb_otg_register_hcd(struct usb_hcd *hcd, unsigned int irqnum,
+			 unsigned long irqflags, struct otg_hcd_ops *ops)
+{
+	struct usb_otg *otgd;
+	struct device *hcd_dev = hcd->self.controller;
+	struct device *otg_dev = usb_otg_get_device(hcd_dev);
+
+	if (!otg_dev)
+		return -EINVAL;	/* we're definitely not OTG */
+
+	/* we're otg but otg controller might not yet be registered */
+	mutex_lock(&otg_list_mutex);
+	otgd = usb_otg_get_data(otg_dev);
+	mutex_unlock(&otg_list_mutex);
+	if (!otgd) {
+		dev_dbg(hcd_dev,
+			"otg: controller not yet registered. waiting..\n");
+		/*
+		 * otg controller might register later. Put the hcd in
+		 * wait list and call us back when ready
+		 */
+		if (usb_otg_hcd_wait_add(otg_dev, hcd, irqnum, irqflags, ops)) {
+			dev_dbg(hcd_dev, "otg: failed to add to wait list\n");
+			return -EINVAL;
+		}
+
+		return 0;
+	}
+
+	/* HCD will be started by OTG fsm when needed */
+	mutex_lock(&otgd->fsm.lock);
+	if (otgd->primary_hcd.hcd) {
+		/* probably a shared HCD ? */
+		if (usb_otg_hcd_is_primary_hcd(hcd)) {
+			dev_err(otg_dev, "otg: primary host already registered\n");
+			goto err;
+		}
+
+		if (hcd->shared_hcd == otgd->primary_hcd.hcd) {
+			if (otgd->shared_hcd.hcd) {
+				dev_err(otg_dev, "otg: shared host already registered\n");
+				goto err;
+			}
+
+			otgd->shared_hcd.hcd = hcd;
+			otgd->shared_hcd.irqnum = irqnum;
+			otgd->shared_hcd.irqflags = irqflags;
+			otgd->shared_hcd.ops = ops;
+			dev_info(otg_dev, "otg: shared host %s registered\n",
+				 dev_name(hcd->self.controller));
+		} else {
+			dev_err(otg_dev, "otg: invalid shared host %s\n",
+				dev_name(hcd->self.controller));
+			goto err;
+		}
+	} else {
+		if (!usb_otg_hcd_is_primary_hcd(hcd)) {
+			dev_err(otg_dev, "otg: primary host must be registered first\n");
+			goto err;
+		}
+
+		otgd->primary_hcd.hcd = hcd;
+		otgd->primary_hcd.irqnum = irqnum;
+		otgd->primary_hcd.irqflags = irqflags;
+		otgd->primary_hcd.ops = ops;
+		dev_info(otg_dev, "otg: primary host %s registered\n",
+			 dev_name(hcd->self.controller));
+	}
+
+	/*
+	 * we're ready only if we have shared HCD
+	 * or we don't need shared HCD.
+	 */
+	if (otgd->shared_hcd.hcd || !otgd->primary_hcd.hcd->shared_hcd) {
+		otgd->fsm.otg->host = hcd_to_bus(hcd);
+		/* FIXME: set bus->otg_port if this is true OTG port with HNP */
+
+		/* start FSM */
+		usb_otg_start_fsm(&otgd->fsm);
+	} else {
+		dev_dbg(otg_dev, "otg: can't start till shared host registers\n");
+	}
+
+	mutex_unlock(&otgd->fsm.lock);
+
+	return 0;
+
+err:
+	mutex_unlock(&otgd->fsm.lock);
+	return -EINVAL;
+}
+EXPORT_SYMBOL_GPL(usb_otg_register_hcd);
+
+/**
+ * usb_otg_unregister_hcd - Unregister Host controller from OTG core
+ * @hcd:	Host controller device
+ *
+ * This is used by the USB Host stack to unregister the Host controller
+ * from the OTG core. Ensures that Host controller is not running
+ * on successful return.
+ *
+ * Returns: 0 on success, error value otherwise.
+ */
+int usb_otg_unregister_hcd(struct usb_hcd *hcd)
+{
+	struct usb_otg *otgd;
+	struct device *hcd_dev = hcd_to_bus(hcd)->controller;
+	struct device *otg_dev = usb_otg_get_device(hcd_dev);
+
+	if (!otg_dev)
+		return -EINVAL;	/* we're definitely not OTG */
+
+	mutex_lock(&otg_list_mutex);
+	otgd = usb_otg_get_data(otg_dev);
+	mutex_unlock(&otg_list_mutex);
+	if (!otgd) {
+		/* are we in wait list? */
+		if (!usb_otg_hcd_wait_remove(hcd))
+			return 0;
+
+		dev_dbg(hcd_dev, "otg: host wasn't registered with otg\n");
+		return -EINVAL;
+	}
+
+	mutex_lock(&otgd->fsm.lock);
+	if (hcd == otgd->primary_hcd.hcd) {
+		otgd->primary_hcd.hcd = NULL;
+		dev_info(otg_dev, "otg: primary host %s unregistered\n",
+			 dev_name(hcd_dev));
+	} else if (hcd == otgd->shared_hcd.hcd) {
+		otgd->shared_hcd.hcd = NULL;
+		dev_info(otg_dev, "otg: shared host %s unregistered\n",
+			 dev_name(hcd_dev));
+	} else {
+		dev_err(otg_dev, "otg: host %s wasn't registered with otg\n",
+			dev_name(hcd_dev));
+		mutex_unlock(&otgd->fsm.lock);
+		return -EINVAL;
+	}
+
+	/* stop FSM & Host */
+	usb_otg_stop_fsm(&otgd->fsm);
+	otgd->fsm.otg->host = NULL;
+
+	mutex_unlock(&otgd->fsm.lock);
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(usb_otg_unregister_hcd);
+
+/**
+ * usb_otg_register_gadget - Register Gadget controller to OTG core
+ * @gadget:	Gadget controller
+ *
+ * This is used by the USB Gadget stack to register the Gadget controller
+ * to the OTG core. Gadget controller must not be started by the
+ * caller as it is left upto the OTG state machine to do so.
+ *
+ * Gadget core must call this only when all resources required for
+ * gadget controller to run are available.
+ * i.e. gadget function driver is available.
+ *
+ * Returns: 0 on success, error value otherwise.
+ */
+int usb_otg_register_gadget(struct usb_gadget *gadget,
+			    struct otg_gadget_ops *ops)
+{
+	struct usb_otg *otgd;
+	struct device *gadget_dev = &gadget->dev;
+	struct device *otg_dev = usb_otg_get_device(gadget_dev);
+
+	if (!otg_dev)
+		return -EINVAL;	/* we're definitely not OTG */
+
+	/* we're otg but otg controller might not yet be registered */
+	mutex_lock(&otg_list_mutex);
+	otgd = usb_otg_get_data(otg_dev);
+	mutex_unlock(&otg_list_mutex);
+	if (!otgd) {
+		dev_dbg(gadget_dev,
+			"otg: controller not yet registered. waiting..\n");
+		/*
+		 * otg controller might register later. Put the gadget in
+		 * wait list and call us back when ready
+		 */
+		if (usb_otg_gadget_wait_add(otg_dev, gadget, ops)) {
+			dev_dbg(gadget_dev, "otg: failed to add to wait list\n");
+			return -EINVAL;
+		}
+
+		return 0;
+	}
+
+	mutex_lock(&otgd->fsm.lock);
+	if (otgd->fsm.otg->gadget) {
+		dev_err(otg_dev, "otg: gadget already registered with otg\n");
+		mutex_unlock(&otgd->fsm.lock);
+		return -EINVAL;
+	}
+
+	otgd->fsm.otg->gadget = gadget;
+	otgd->gadget_ops = ops;
+	dev_info(otg_dev, "otg: gadget %s registered\n",
+		 dev_name(&gadget->dev));
+
+	/* start FSM */
+	usb_otg_start_fsm(&otgd->fsm);
+	mutex_unlock(&otgd->fsm.lock);
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(usb_otg_register_gadget);
+
+/**
+ * usb_otg_unregister_gadget - Unregister Gadget controller from OTG core
+ * @gadget:	Gadget controller
+ *
+ * This is used by the USB Gadget stack to unregister the Gadget controller
+ * from the OTG core. Ensures that Gadget controller is not running
+ * on successful return.
+ *
+ * Returns: 0 on success, error value otherwise.
+ */
+int usb_otg_unregister_gadget(struct usb_gadget *gadget)
+{
+	struct usb_otg *otgd;
+	struct device *gadget_dev = &gadget->dev;
+	struct device *otg_dev = usb_otg_get_device(gadget_dev);
+
+	if (!otg_dev)
+		return -EINVAL;
+
+	mutex_lock(&otg_list_mutex);
+	otgd = usb_otg_get_data(otg_dev);
+	mutex_unlock(&otg_list_mutex);
+	if (!otgd) {
+		/* are we in wait list? */
+		if (!usb_otg_gadget_wait_remove(gadget))
+			return 0;
+
+		dev_dbg(gadget_dev, "otg: gadget wasn't registered with otg\n");
+		return -EINVAL;
+	}
+
+	mutex_lock(&otgd->fsm.lock);
+	if (otgd->fsm.otg->gadget != gadget) {
+		dev_err(otg_dev, "otg: gadget %s wasn't registered with otg\n",
+			dev_name(&gadget->dev));
+		mutex_unlock(&otgd->fsm.lock);
+		return -EINVAL;
+	}
+
+	/* Stop FSM & gadget */
+	usb_otg_stop_fsm(&otgd->fsm);
+	otgd->fsm.otg->gadget = NULL;
+	mutex_unlock(&otgd->fsm.lock);
+
+	dev_info(otg_dev, "otg: gadget %s unregistered\n",
+		 dev_name(&gadget->dev));
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(usb_otg_unregister_gadget);
+
+/**
+ * usb_otg_fsm_to_dev - Get OTG controller device from struct otg_fsm
+ * @fsm:	otg_fsm data structure
+ *
+ * This is used by the OTG controller driver to get it's device node
+ * from any of the otg_fsm->ops.
+ */
+struct device *usb_otg_fsm_to_dev(struct otg_fsm *fsm)
+{
+	struct usb_otg *otgd = container_of(fsm, struct usb_otg, fsm);
+
+	return otgd->dev;
+}
+EXPORT_SYMBOL_GPL(usb_otg_fsm_to_dev);
diff --git a/drivers/usb/common/usb-otg.h b/drivers/usb/common/usb-otg.h
new file mode 100644
index 0000000..05331f0
--- /dev/null
+++ b/drivers/usb/common/usb-otg.h
@@ -0,0 +1,71 @@
+/**
+ * drivers/usb/common/usb-otg.h - USB OTG core local header
+ *
+ * Copyright (C) 2015 Texas Instruments Incorporated - http://www.ti.com
+ * Author: Roger Quadros <rogerq@ti.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef __DRIVERS_USB_COMMON_USB_OTG_H
+#define __DRIVERS_USB_COMMON_USB_OTG_H
+
+/*
+ *  A-DEVICE timing constants
+ */
+
+/* Wait for VBUS Rise  */
+#define TA_WAIT_VRISE        (100)	/* a_wait_vrise: section 7.1.2
+					 * a_wait_vrise_tmr: section 7.4.5.1
+					 * TA_VBUS_RISE <= 100ms, section 4.4
+					 * Table 4-1: Electrical Characteristics
+					 * ->DC Electrical Timing
+					 */
+/* Wait for VBUS Fall  */
+#define TA_WAIT_VFALL        (1000)	/* a_wait_vfall: section 7.1.7
+					 * a_wait_vfall_tmr: section: 7.4.5.2
+					 */
+/* Wait for B-Connect */
+#define TA_WAIT_BCON         (10000)	/* a_wait_bcon: section 7.1.3
+					 * TA_WAIT_BCON: should be between 1100
+					 * and 30000 ms, section 5.5, Table 5-1
+					 */
+/* A-Idle to B-Disconnect */
+#define TA_AIDL_BDIS         (5000)	/* a_suspend min 200 ms, section 5.2.1
+					 * TA_AIDL_BDIS: section 5.5, Table 5-1
+					 */
+/* B-Idle to A-Disconnect */
+#define TA_BIDL_ADIS         (500)	/* TA_BIDL_ADIS: section 5.2.1
+					 * 500ms is used for B switch to host
+					 * for safe
+					 */
+
+/*
+ * B-device timing constants
+ */
+
+/* Data-Line Pulse Time*/
+#define TB_DATA_PLS          (10)	/* b_srp_init,continue 5~10ms
+					 * section:5.1.3
+					 */
+/* SRP Fail Time  */
+#define TB_SRP_FAIL          (6000)	/* b_srp_init,fail time 5~6s
+					 * section:5.1.6
+					 */
+/* A-SE0 to B-Reset  */
+#define TB_ASE0_BRST         (155)	/* minimum 155 ms, section:5.3.1 */
+/* SE0 Time Before SRP */
+#define TB_SE0_SRP           (1000)	/* b_idle,minimum 1s, section:5.1.2 */
+/* SSEND time before SRP */
+#define TB_SSEND_SRP         (1500)	/* minimum 1.5 sec, section:5.1.2 */
+
+#define TB_SESS_VLD          (1000)
+
+#endif /* __DRIVERS_USB_COMMON_USB_OTG_H */
diff --git a/drivers/usb/core/Kconfig b/drivers/usb/core/Kconfig
index a99c89e..b468a9f 100644
--- a/drivers/usb/core/Kconfig
+++ b/drivers/usb/core/Kconfig
@@ -42,7 +42,7 @@ config USB_DYNAMIC_MINORS
 	  If you are unsure about this, say N here.
 
 config USB_OTG
-	bool "OTG support"
+	bool "OTG/Dual-role support"
 	depends on PM
 	default n
 	help
@@ -75,15 +75,6 @@ config USB_OTG_BLACKLIST_HUB
 	  and software costs by not supporting external hubs.  So
 	  are "Embedded Hosts" that don't offer OTG support.
 
-config USB_OTG_FSM
-	tristate "USB 2.0 OTG FSM implementation"
-	depends on USB
-	select USB_OTG
-	select USB_PHY
-	help
-	  Implements OTG Finite State Machine as specified in On-The-Go
-	  and Embedded Host Supplement to the USB Revision 2.0 Specification.
-
 config USB_ULPI_BUS
 	tristate "USB ULPI PHY interface support"
 	depends on USB_SUPPORT
diff --git a/include/linux/usb/otg.h b/include/linux/usb/otg.h
index bd1dcf8..38cabe0 100644
--- a/include/linux/usb/otg.h
+++ b/include/linux/usb/otg.h
@@ -10,19 +10,100 @@
 #define __LINUX_USB_OTG_H
 
 #include <linux/phy/phy.h>
+#include <linux/device.h>
+#include <linux/hrtimer.h>
+#include <linux/ktime.h>
+#include <linux/usb.h>
+#include <linux/usb/hcd.h>
+#include <linux/usb/gadget.h>
+#include <linux/usb/otg-fsm.h>
 #include <linux/usb/phy.h>
 
+/**
+ * struct otg_hcd - host controller state and interface
+ *
+ * @hcd: host controller
+ * @irqnum: irq number
+ * @irqflags: irq flags
+ * @ops: otg to host controller interface
+ */
+struct otg_hcd {
+	struct usb_hcd *hcd;
+	unsigned int irqnum;
+	unsigned long irqflags;
+	struct otg_hcd_ops *ops;
+};
+
+struct usb_otg;
+
+/**
+ * struct otg_timer - otg timer data
+ *
+ * @timer: high resolution timer
+ * @timeout: timeout value
+ * @timetout_bit: pointer to variable that is set on timeout
+ * @otgd: usb otg data
+ */
+struct otg_timer {
+	struct hrtimer timer;
+	ktime_t timeout;
+	/* callback data */
+	int *timeout_bit;
+	struct usb_otg *otgd;
+};
+
+/**
+ * struct usb_otg - usb otg controller state
+ *
+ * @default_a: Indicates we are an A device. i.e. Host.
+ * @phy: USB phy interface
+ * @usb_phy: old usb_phy interface
+ * @host: host controller bus
+ * @gadget: gadget device
+ * @state: current otg state
+ * @dev: otg controller device
+ * @caps: otg capabilities revision, hnp, srp, etc
+ * @fsm: otg finite state machine
+ * @fsm_ops: controller hooks for the state machine
+ * ------- internal use only -------
+ * @primary_hcd: primary host state and interface
+ * @shared_hcd: shared host state and interface
+ * @gadget_ops: gadget interface
+ * @timers: otg timers for state machine
+ * @list: list of otg controllers
+ * @work: otg state machine work
+ * @wq: otg state machine work queue
+ * @fsm_running: state machine running/stopped indicator
+ */
 struct usb_otg {
 	u8			default_a;
 
 	struct phy		*phy;
 	/* old usb_phy interface */
 	struct usb_phy		*usb_phy;
+
 	struct usb_bus		*host;
 	struct usb_gadget	*gadget;
 
 	enum usb_otg_state	state;
 
+	struct device *dev;
+	struct usb_otg_caps *caps;
+	struct otg_fsm fsm;
+	struct otg_fsm_ops fsm_ops;
+
+	/* internal use only */
+	struct otg_hcd primary_hcd;
+	struct otg_hcd shared_hcd;
+	struct otg_gadget_ops *gadget_ops;
+	struct otg_timer timers[NUM_OTG_FSM_TIMERS];
+	struct list_head list;
+	struct work_struct work;
+	struct workqueue_struct *wq;
+	bool fsm_running;
+	/* use otg->fsm.lock for serializing access */
+
+/*------------- deprecated interface -----------------------------*/
 	/* bind/unbind the host controller */
 	int	(*set_host)(struct usb_otg *otg, struct usb_bus *host);
 
@@ -38,7 +119,7 @@ struct usb_otg {
 
 	/* start or continue HNP role switch */
 	int	(*start_hnp)(struct usb_otg *otg);
-
+/*---------------------------------------------------------------*/
 };
 
 /**
@@ -56,8 +137,105 @@ struct usb_otg_caps {
 	bool adp_support;
 };
 
+/**
+ * struct usb_otg_config - otg controller configuration
+ * @caps: otg capabilities of the controller
+ * @ops: otg fsm operations
+ * @otg_timeouts: override default otg fsm timeouts
+ */
+struct usb_otg_config {
+	struct usb_otg_caps otg_caps;
+	struct otg_fsm_ops *fsm_ops;
+	unsigned otg_timeouts[NUM_OTG_FSM_TIMERS];
+};
+
 extern const char *usb_otg_state_string(enum usb_otg_state state);
 
+enum usb_dr_mode {
+	USB_DR_MODE_UNKNOWN,
+	USB_DR_MODE_HOST,
+	USB_DR_MODE_PERIPHERAL,
+	USB_DR_MODE_OTG,
+};
+
+#if IS_ENABLED(CONFIG_USB_OTG)
+struct otg_fsm *usb_otg_register(struct device *dev,
+				 struct usb_otg_config *config);
+int usb_otg_unregister(struct device *dev);
+int usb_otg_register_hcd(struct usb_hcd *hcd, unsigned int irqnum,
+			 unsigned long irqflags, struct otg_hcd_ops *ops);
+int usb_otg_unregister_hcd(struct usb_hcd *hcd);
+int usb_otg_register_gadget(struct usb_gadget *gadget,
+			    struct otg_gadget_ops *ops);
+int usb_otg_unregister_gadget(struct usb_gadget *gadget);
+void usb_otg_sync_inputs(struct otg_fsm *fsm);
+int usb_otg_kick_fsm(struct device *hcd_gcd_device);
+struct device *usb_otg_fsm_to_dev(struct otg_fsm *fsm);
+int usb_otg_start_host(struct otg_fsm *fsm, int on);
+int usb_otg_start_gadget(struct otg_fsm *fsm, int on);
+
+#else /* CONFIG_USB_OTG */
+
+static inline struct otg_fsm *usb_otg_register(struct device *dev,
+					       struct usb_otg_config *config)
+{
+	return ERR_PTR(-ENOTSUPP);
+}
+
+static inline int usb_otg_unregister(struct device *dev)
+{
+	return -ENOTSUPP;
+}
+
+static inline int usb_otg_register_hcd(struct usb_hcd *hcd, unsigned int irqnum,
+				       unsigned long irqflags,
+				       struct otg_hcd_ops *ops)
+{
+	return -ENOTSUPP;
+}
+
+static inline int usb_otg_unregister_hcd(struct usb_hcd *hcd)
+{
+	return -ENOTSUPP;
+}
+
+static inline int usb_otg_register_gadget(struct usb_gadget *gadget,
+					  struct otg_gadget_ops *ops)
+{
+	return -ENOTSUPP;
+}
+
+static inline int usb_otg_unregister_gadget(struct usb_gadget *gadget)
+{
+	return -ENOTSUPP;
+}
+
+static inline void usb_otg_sync_inputs(struct otg_fsm *fsm)
+{
+}
+
+static inline int usb_otg_kick_fsm(struct device *hcd_gcd_device)
+{
+	return -ENOTSUPP;
+}
+
+static inline struct device *usb_otg_fsm_to_dev(struct otg_fsm *fsm)
+{
+	return NULL;
+}
+
+static inline int usb_otg_start_host(struct otg_fsm *fsm, int on)
+{
+	return -ENOTSUPP;
+}
+
+static inline int usb_otg_start_gadget(struct otg_fsm *fsm, int on)
+{
+	return -ENOTSUPP;
+}
+#endif /* CONFIG_USB_OTG */
+
+/*------------- deprecated interface -----------------------------*/
 /* Context: can sleep */
 static inline int
 otg_start_hnp(struct usb_otg *otg)
@@ -109,14 +287,9 @@ otg_start_srp(struct usb_otg *otg)
 	return -ENOTSUPP;
 }
 
+/*---------------------------------------------------------------*/
+
 /* for OTG controller drivers (and maybe other stuff) */
 extern int usb_bus_start_enum(struct usb_bus *bus, unsigned port_num);
 
-enum usb_dr_mode {
-	USB_DR_MODE_UNKNOWN,
-	USB_DR_MODE_HOST,
-	USB_DR_MODE_PERIPHERAL,
-	USB_DR_MODE_OTG,
-};
-
 #endif /* __LINUX_USB_OTG_H */
-- 
2.1.4

^ permalink raw reply related	[flat|nested] 48+ messages in thread

* [PATCH v4 07/13] usb: otg: add OTG core
@ 2015-08-24 13:21   ` Roger Quadros
  0 siblings, 0 replies; 48+ messages in thread
From: Roger Quadros @ 2015-08-24 13:21 UTC (permalink / raw)
  To: stern, balbi, gregkh, peter.chen
  Cc: dan.j.williams, jun.li, mathias.nyman, tony, Joao.Pinto, abrestic,
	linux-usb, linux-kernel, linux-omap, Roger Quadros

The OTG core instantiates the OTG Finite State Machine
per OTG controller and manages starting/stopping the
host and gadget controllers based on the bus state.

It provides APIs for the following tasks

- Registering an OTG capable controller
- Registering Host and Gadget controllers to OTG core
- Providing inputs to and kicking the OTG state machine

Signed-off-by: Roger Quadros <rogerq@ti.com>
---
 MAINTAINERS                  |    4 +-
 drivers/usb/Kconfig          |    2 +-
 drivers/usb/Makefile         |    1 +
 drivers/usb/common/Makefile  |    3 +-
 drivers/usb/common/usb-otg.c | 1061 ++++++++++++++++++++++++++++++++++++++++++
 drivers/usb/common/usb-otg.h |   71 +++
 drivers/usb/core/Kconfig     |   11 +-
 include/linux/usb/otg.h      |  189 +++++++-
 8 files changed, 1321 insertions(+), 21 deletions(-)
 create mode 100644 drivers/usb/common/usb-otg.c
 create mode 100644 drivers/usb/common/usb-otg.h

diff --git a/MAINTAINERS b/MAINTAINERS
index a9ae6c1..4bc1279 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -10667,12 +10667,14 @@ S:	Maintained
 F:	Documentation/usb/ohci.txt
 F:	drivers/usb/host/ohci*
 
-USB OTG FSM (Finite State Machine)
+USB OTG/DRD core and FSM (Finite State Machine)
 M:	Peter Chen <Peter.Chen@freescale.com>
+M:	Roger Quadros <rogerq@ti.com>
 T:	git git://git.kernel.org/pub/scm/linux/kernel/git/peter.chen/usb.git
 L:	linux-usb@vger.kernel.org
 S:	Maintained
 F:	drivers/usb/common/usb-otg-fsm.c
+F:	drivers/usb/common/usb-otg.c
 
 USB OVER IP DRIVER
 M:	Valentina Manea <valentina.manea.m@gmail.com>
diff --git a/drivers/usb/Kconfig b/drivers/usb/Kconfig
index 8ed451d..5b625e2 100644
--- a/drivers/usb/Kconfig
+++ b/drivers/usb/Kconfig
@@ -32,7 +32,7 @@ if USB_SUPPORT
 config USB_COMMON
 	tristate
 	default y
-	depends on USB || USB_GADGET
+	depends on USB || USB_GADGET || USB_OTG
 
 config USB_ARCH_HAS_HCD
 	def_bool y
diff --git a/drivers/usb/Makefile b/drivers/usb/Makefile
index d8926c6..769d13b 100644
--- a/drivers/usb/Makefile
+++ b/drivers/usb/Makefile
@@ -60,5 +60,6 @@ obj-$(CONFIG_USB_RENESAS_USBHS)	+= renesas_usbhs/
 obj-$(CONFIG_USB_GADGET)	+= gadget/
 
 obj-$(CONFIG_USB_COMMON)	+= common/
+obj-$(CONFIG_USB_OTG)		+= common/
 
 obj-$(CONFIG_USBIP_CORE)	+= usbip/
diff --git a/drivers/usb/common/Makefile b/drivers/usb/common/Makefile
index 6bbb3ec..730d928 100644
--- a/drivers/usb/common/Makefile
+++ b/drivers/usb/common/Makefile
@@ -6,5 +6,6 @@ obj-$(CONFIG_USB_COMMON)	  += usb-common.o
 usb-common-y			  += common.o
 usb-common-$(CONFIG_USB_LED_TRIG) += led.o
 
-obj-$(CONFIG_USB_OTG_FSM) += usb-otg-fsm.o
 obj-$(CONFIG_USB_ULPI_BUS)	+= ulpi.o
+usbotg-y		:= usb-otg.o usb-otg-fsm.o
+obj-$(CONFIG_USB_OTG)	+= usbotg.o
diff --git a/drivers/usb/common/usb-otg.c b/drivers/usb/common/usb-otg.c
new file mode 100644
index 0000000..930c2fe
--- /dev/null
+++ b/drivers/usb/common/usb-otg.c
@@ -0,0 +1,1061 @@
+/**
+ * drivers/usb/common/usb-otg.c - USB OTG core
+ *
+ * Copyright (C) 2015 Texas Instruments Incorporated - http://www.ti.com
+ * Author: Roger Quadros <rogerq@ti.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/kernel.h>
+#include <linux/ktime.h>
+#include <linux/hrtimer.h>
+#include <linux/list.h>
+#include <linux/of.h>
+#include <linux/of_platform.h>
+#include <linux/usb/otg.h>
+#include <linux/usb/phy.h>	/* enum usb_otg_state */
+#include <linux/usb/gadget.h>
+#include <linux/workqueue.h>
+
+#include "usb-otg.h"
+
+struct otg_gcd {
+	struct usb_gadget *gadget;
+	struct otg_gadget_ops *ops;
+};
+
+/* OTG device list */
+LIST_HEAD(otg_list);
+static DEFINE_MUTEX(otg_list_mutex);
+
+/* Hosts and Gadgets waiting for OTG controller */
+struct otg_wait_data {
+	struct device *dev;		/* OTG controller device */
+
+	struct otg_hcd primary_hcd;
+	struct otg_hcd shared_hcd;
+	struct otg_gcd gcd;
+	struct list_head list;
+};
+
+LIST_HEAD(wait_list);
+static DEFINE_MUTEX(wait_list_mutex);
+
+static int usb_otg_hcd_is_primary_hcd(struct usb_hcd *hcd)
+{
+	if (!hcd->primary_hcd)
+		return 1;
+	return hcd == hcd->primary_hcd;
+}
+
+/**
+ * Check if the OTG device is in our wait list and return
+ * otg_wait_data, else NULL.
+ *
+ * wait_list_mutex must be held.
+ */
+static struct otg_wait_data *usb_otg_get_wait(struct device *otg_dev)
+{
+	struct otg_wait_data *wait;
+
+	if (!otg_dev)
+		return NULL;
+
+	/* is there an entry for this otg_dev ?*/
+	list_for_each_entry(wait, &wait_list, list) {
+		if (wait->dev == otg_dev)
+			return wait;
+	}
+
+	return NULL;
+}
+
+/**
+ * Add the hcd to our wait list
+ */
+static int usb_otg_hcd_wait_add(struct device *otg_dev, struct usb_hcd *hcd,
+				unsigned int irqnum, unsigned long irqflags,
+				struct otg_hcd_ops *ops)
+{
+	struct otg_wait_data *wait;
+	int ret = -EINVAL;
+
+	mutex_lock(&wait_list_mutex);
+
+	wait = usb_otg_get_wait(otg_dev);
+	if (!wait) {
+		/* Not yet in wait list? allocate and add */
+		wait = kzalloc(sizeof(*wait), GFP_KERNEL);
+		if (!wait) {
+			ret = -ENOMEM;
+			goto fail;
+		}
+
+		wait->dev = otg_dev;
+		list_add_tail(&wait->list, &wait_list);
+	}
+
+	if (usb_otg_hcd_is_primary_hcd(hcd)) {
+		if (wait->primary_hcd.hcd)	/* already assigned? */
+			goto fail;
+
+		wait->primary_hcd.hcd = hcd;
+		wait->primary_hcd.irqnum = irqnum;
+		wait->primary_hcd.irqflags = irqflags;
+		wait->primary_hcd.ops = ops;
+	} else {
+		if (wait->shared_hcd.hcd)	/* already assigned? */
+			goto fail;
+
+		wait->shared_hcd.hcd = hcd;
+		wait->shared_hcd.irqnum = irqnum;
+		wait->shared_hcd.irqflags = irqflags;
+		wait->shared_hcd.ops = ops;
+	}
+
+	mutex_unlock(&wait_list_mutex);
+	return 0;
+
+fail:
+	mutex_unlock(&wait_list_mutex);
+	return ret;
+}
+
+/**
+ * Check and free wait list entry if empty
+ *
+ * wait_list_mutex must be held
+ */
+static void usb_otg_check_free_wait(struct otg_wait_data *wait)
+{
+	if (wait->primary_hcd.hcd || wait->shared_hcd.hcd || wait->gcd.gadget)
+		return;
+
+	list_del(&wait->list);
+	kfree(wait);
+}
+
+/**
+ * Remove the hcd from our wait list
+ */
+static int usb_otg_hcd_wait_remove(struct usb_hcd *hcd)
+{
+	struct otg_wait_data *wait;
+
+	mutex_lock(&wait_list_mutex);
+
+	/* is there an entry for this hcd ?*/
+	list_for_each_entry(wait, &wait_list, list) {
+		if (wait->primary_hcd.hcd == hcd) {
+			wait->primary_hcd.hcd = 0;
+			goto found;
+		} else if (wait->shared_hcd.hcd == hcd) {
+			wait->shared_hcd.hcd = 0;
+			goto found;
+		}
+	}
+
+	mutex_unlock(&wait_list_mutex);
+	return -EINVAL;
+
+found:
+	usb_otg_check_free_wait(wait);
+	mutex_unlock(&wait_list_mutex);
+
+	return 0;
+}
+
+/**
+ * Add the gadget to our wait list
+ */
+static int usb_otg_gadget_wait_add(struct device *otg_dev,
+				   struct usb_gadget *gadget,
+				   struct otg_gadget_ops *ops)
+{
+	struct otg_wait_data *wait;
+	int ret = -EINVAL;
+
+	mutex_lock(&wait_list_mutex);
+
+	wait = usb_otg_get_wait(otg_dev);
+	if (!wait) {
+		/* Not yet in wait list? allocate and add */
+		wait = kzalloc(sizeof(*wait), GFP_KERNEL);
+		if (!wait) {
+			ret = -ENOMEM;
+			goto fail;
+		}
+
+		wait->dev = otg_dev;
+		list_add_tail(&wait->list, &wait_list);
+	}
+
+	if (wait->gcd.gadget) /* already assigned? */
+		goto fail;
+
+	wait->gcd.gadget = gadget;
+	wait->gcd.ops = ops;
+	mutex_unlock(&wait_list_mutex);
+
+	return 0;
+
+fail:
+	mutex_unlock(&wait_list_mutex);
+	return ret;
+}
+
+/**
+ * Remove the gadget from our wait list
+ */
+static int usb_otg_gadget_wait_remove(struct usb_gadget *gadget)
+{
+	struct otg_wait_data *wait;
+
+	mutex_lock(&wait_list_mutex);
+
+	/* is there an entry for this gadget ?*/
+	list_for_each_entry(wait, &wait_list, list) {
+		if (wait->gcd.gadget == gadget) {
+			wait->gcd.gadget = 0;
+			goto found;
+		}
+	}
+
+	mutex_unlock(&wait_list_mutex);
+
+	return -EINVAL;
+
+found:
+	usb_otg_check_free_wait(wait);
+	mutex_unlock(&wait_list_mutex);
+
+	return 0;
+}
+
+/**
+ * Register pending host/gadget and remove entry from wait list
+ */
+static void usb_otg_flush_wait(struct device *otg_dev)
+{
+	struct otg_wait_data *wait;
+	struct otg_hcd *host;
+	struct otg_gcd *gadget;
+
+	mutex_lock(&wait_list_mutex);
+
+	wait = usb_otg_get_wait(otg_dev);
+	if (!wait)
+		goto done;
+
+	dev_dbg(otg_dev, "otg: registering pending host/gadget\n");
+	gadget = &wait->gcd;
+	if (gadget)
+		usb_otg_register_gadget(gadget->gadget, gadget->ops);
+
+	host = &wait->primary_hcd;
+	if (host->hcd)
+		usb_otg_register_hcd(host->hcd, host->irqnum, host->irqflags,
+				     host->ops);
+
+	host = &wait->shared_hcd;
+	if (host->hcd)
+		usb_otg_register_hcd(host->hcd, host->irqnum, host->irqflags,
+				     host->ops);
+
+	list_del(&wait->list);
+	kfree(wait);
+
+done:
+	mutex_unlock(&wait_list_mutex);
+}
+
+/**
+ * Check if the OTG device is in our OTG list and return
+ * usb_otg data, else NULL.
+ *
+ * otg_list_mutex must be held.
+ */
+static struct usb_otg *usb_otg_get_data(struct device *otg_dev)
+{
+	struct usb_otg *otgd;
+
+	if (!otg_dev)
+		return NULL;
+
+	list_for_each_entry(otgd, &otg_list, list) {
+		if (otgd->dev == otg_dev)
+			return otgd;
+	}
+
+	return NULL;
+}
+
+/**
+ * Get OTG device from host or gadget device.
+ *
+ * For non device tree boot, the OTG controller is assumed to be
+ * the parent of the host/gadget device.
+ * For device tree boot, the OTG controller is derived from the
+ * "otg-controller" property.
+ */
+static struct device *usb_otg_get_device(struct device *hcd_gcd_dev)
+{
+	struct device *otg_dev;
+
+	if (!hcd_gcd_dev)
+		return NULL;
+
+	if (hcd_gcd_dev->of_node) {
+		struct device_node *np;
+		struct platform_device *pdev;
+
+		np = of_parse_phandle(hcd_gcd_dev->of_node, "otg-controller",
+				      0);
+		if (!np)
+			goto legacy;	/* continue legacy way */
+
+		pdev = of_find_device_by_node(np);
+		of_node_put(np);
+		if (!pdev) {
+			dev_err(&pdev->dev, "couldn't get otg-controller device\n");
+			return NULL;
+		}
+
+		otg_dev = &pdev->dev;
+		return otg_dev;
+	}
+
+legacy:
+	/* otg device is parent and must be registered */
+	otg_dev = hcd_gcd_dev->parent;
+	if (!usb_otg_get_data(otg_dev))
+		return NULL;
+
+	return otg_dev;
+}
+
+/**
+ * timer callback to set timeout bit and kick FSM
+ */
+static enum hrtimer_restart set_tmout(struct hrtimer *data)
+{
+	struct otg_timer *otgtimer;
+
+	otgtimer = container_of(data, struct otg_timer, timer);
+	if (otgtimer->timeout_bit)
+		*otgtimer->timeout_bit = 1;
+
+	usb_otg_sync_inputs(&otgtimer->otgd->fsm);
+
+	return HRTIMER_NORESTART;
+}
+
+/**
+ * Initialize one OTG timer with callback, timeout and timeout bit
+ */
+static void otg_timer_init(enum otg_fsm_timer id, struct usb_otg *otgd,
+			   enum hrtimer_restart (*callback)(struct hrtimer *),
+			   unsigned long expires_ms,
+			   int *timeout_bit)
+{
+	struct otg_timer *otgtimer = &otgd->timers[id];
+	struct hrtimer *timer = &otgtimer->timer;
+
+	otgtimer->timeout = ms_to_ktime(expires_ms);
+	hrtimer_init(timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
+	timer->function = callback;
+
+	otgtimer->timeout_bit = timeout_bit;
+	otgtimer->otgd = otgd;
+}
+
+/**
+ * Initialize standard OTG timers
+ */
+static void usb_otg_init_timers(struct usb_otg *otgd, unsigned *timeouts)
+{
+	struct otg_fsm *fsm = &otgd->fsm;
+	unsigned long tmouts[NUM_OTG_FSM_TIMERS];
+	int i;
+
+	/* set default timeouts */
+	tmouts[A_WAIT_VRISE] = TA_WAIT_VRISE;
+	tmouts[A_WAIT_VFALL] = TA_WAIT_VFALL;
+	tmouts[A_WAIT_BCON] = TA_WAIT_BCON;
+	tmouts[A_AIDL_BDIS] = TA_AIDL_BDIS;
+	tmouts[A_BIDL_ADIS] = TA_BIDL_ADIS;
+	tmouts[B_ASE0_BRST] = TB_ASE0_BRST;
+	tmouts[B_SE0_SRP] = TB_SE0_SRP;
+	tmouts[B_SRP_FAIL] = TB_SRP_FAIL;
+
+	/* set controller provided timeouts */
+	if (timeouts) {
+		for (i = 0; i < NUM_OTG_FSM_TIMERS; i++) {
+			if (timeouts[i])
+				tmouts[i] = timeouts[i];
+		}
+	}
+
+	otg_timer_init(A_WAIT_VRISE, otgd, set_tmout, TA_WAIT_VRISE,
+		       &fsm->a_wait_vrise_tmout);
+	otg_timer_init(A_WAIT_VFALL, otgd, set_tmout, TA_WAIT_VFALL,
+		       &fsm->a_wait_vfall_tmout);
+	otg_timer_init(A_WAIT_BCON, otgd, set_tmout, TA_WAIT_BCON,
+		       &fsm->a_wait_bcon_tmout);
+	otg_timer_init(A_AIDL_BDIS, otgd, set_tmout, TA_AIDL_BDIS,
+		       &fsm->a_aidl_bdis_tmout);
+	otg_timer_init(A_BIDL_ADIS, otgd, set_tmout, TA_BIDL_ADIS,
+		       &fsm->a_bidl_adis_tmout);
+	otg_timer_init(B_ASE0_BRST, otgd, set_tmout, TB_ASE0_BRST,
+		       &fsm->b_ase0_brst_tmout);
+
+	otg_timer_init(B_SE0_SRP, otgd, set_tmout, TB_SE0_SRP,
+		       &fsm->b_se0_srp);
+	otg_timer_init(B_SRP_FAIL, otgd, set_tmout, TB_SRP_FAIL,
+		       &fsm->b_srp_done);
+
+	/* FIXME: what about A_WAIT_ENUM? */
+}
+
+/**
+ * OTG FSM ops function to add timer
+ */
+static void usb_otg_add_timer(struct otg_fsm *fsm, enum otg_fsm_timer id)
+{
+	struct usb_otg *otgd = container_of(fsm, struct usb_otg, fsm);
+	struct otg_timer *otgtimer = &otgd->timers[id];
+	struct hrtimer *timer = &otgtimer->timer;
+
+	if (!otgd->fsm_running)
+		return;
+
+	/* if timer is already active, exit */
+	if (hrtimer_active(timer)) {
+		dev_err(otgd->dev, "otg: timer %d is already running\n", id);
+		return;
+	}
+
+	hrtimer_start(timer, otgtimer->timeout, HRTIMER_MODE_REL);
+}
+
+/**
+ * OTG FSM ops function to delete timer
+ */
+static void usb_otg_del_timer(struct otg_fsm *fsm, enum otg_fsm_timer id)
+{
+	struct usb_otg *otgd = container_of(fsm, struct usb_otg, fsm);
+	struct hrtimer *timer = &otgd->timers[id].timer;
+
+	hrtimer_cancel(timer);
+}
+
+/**
+ * Helper function to start/stop otg host. For use by otg controller.
+ */
+int usb_otg_start_host(struct otg_fsm *fsm, int on)
+{
+	struct usb_otg *otgd = container_of(fsm, struct usb_otg, fsm);
+	struct otg_hcd_ops *hcd_ops;
+
+	dev_dbg(otgd->dev, "otg: %s %d\n", __func__, on);
+	if (!fsm->otg->host) {
+		WARN_ONCE(1, "otg: fsm running without host\n");
+		return 0;
+	}
+
+	if (on) {
+		/* start host */
+		hcd_ops = otgd->primary_hcd.ops;
+		hcd_ops->add(otgd->primary_hcd.hcd, otgd->primary_hcd.irqnum,
+			     otgd->primary_hcd.irqflags);
+		if (otgd->shared_hcd.hcd) {
+			hcd_ops = otgd->shared_hcd.ops;
+			hcd_ops->add(otgd->shared_hcd.hcd,
+				     otgd->shared_hcd.irqnum,
+				     otgd->shared_hcd.irqflags);
+		}
+	} else {
+		/* stop host */
+		if (otgd->shared_hcd.hcd) {
+			hcd_ops = otgd->shared_hcd.ops;
+			hcd_ops->remove(otgd->shared_hcd.hcd);
+		}
+		hcd_ops = otgd->primary_hcd.ops;
+		hcd_ops->remove(otgd->primary_hcd.hcd);
+	}
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(usb_otg_start_host);
+
+/**
+ * Helper function to start/stop otg gadget. For use by otg controller.
+ */
+int usb_otg_start_gadget(struct otg_fsm *fsm, int on)
+{
+	struct usb_otg *otgd = container_of(fsm, struct usb_otg, fsm);
+	struct usb_gadget *gadget = fsm->otg->gadget;
+
+	dev_dbg(otgd->dev, "otg: %s %d\n", __func__, on);
+	if (!gadget) {
+		WARN_ONCE(1, "otg: fsm running without gadget\n");
+		return 0;
+	}
+
+	if (on)
+		otgd->gadget_ops->start(fsm->otg->gadget);
+	else
+		otgd->gadget_ops->stop(fsm->otg->gadget);
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(usb_otg_start_gadget);
+
+/**
+ * OTG FSM work function
+ */
+static void usb_otg_work(struct work_struct *work)
+{
+	struct usb_otg *otgd = container_of(work, struct usb_otg, work);
+
+	otg_statemachine(&otgd->fsm);
+}
+
+/**
+ * usb_otg_register() - Register the OTG device to OTG core
+ * @dev: OTG controller device.
+ * @config: OTG configuration.
+ *
+ * Register the OTG controller device with the USB OTG core.
+ * The associated Host and Gadget controllers will be prevented from
+ * being started till both are available for use.
+ *
+ * For non device tree boots, the OTG controller device must be the
+ * parent node of the Host and Gadget controllers.
+ *
+ * For device tree case, the otg-controller property must be present
+ * in the Host and Gadget controller node and it must point to the
+ * same OTG controller node.
+ *
+ * Return: struct otg_fsm * if success, NULL if error.
+ */
+struct otg_fsm *usb_otg_register(struct device *dev,
+				 struct usb_otg_config *config)
+{
+	struct usb_otg *otgd;
+	struct otg_wait_data *wait;
+	int ret = 0;
+
+	if (!dev || !config || !config->fsm_ops)
+		return ERR_PTR(-EINVAL);
+
+	/* already in list? */
+	mutex_lock(&otg_list_mutex);
+	if (usb_otg_get_data(dev)) {
+		dev_err(dev, "otg: %s: device already in otg list\n",
+			__func__);
+		ret = -EINVAL;
+		goto unlock;
+	}
+
+	/* allocate and add to list */
+	otgd = kzalloc(sizeof(*otgd), GFP_KERNEL);
+	if (!otgd) {
+		ret = -ENOMEM;
+		goto unlock;
+	}
+
+	otgd->dev = dev;
+	otgd->caps = &config->otg_caps;
+	INIT_WORK(&otgd->work, usb_otg_work);
+	otgd->wq = create_singlethread_workqueue("usb_otg");
+	if (!otgd->wq) {
+		dev_err(dev, "otg: %s: can't create workqueue\n",
+			__func__);
+		ret = -ENOMEM;
+		goto err_wq;
+	}
+
+	usb_otg_init_timers(otgd, config->otg_timeouts);
+
+	/* create copy of original ops */
+	otgd->fsm_ops = *config->fsm_ops;
+	/* FIXME: we ignore caller's timer ops */
+	otgd->fsm_ops.add_timer = usb_otg_add_timer;
+	otgd->fsm_ops.del_timer = usb_otg_del_timer;
+	/* set otg ops */
+	otgd->fsm.ops = &otgd->fsm_ops;
+	otgd->fsm.otg = otgd;
+
+	mutex_init(&otgd->fsm.lock);
+
+	list_add_tail(&otgd->list, &otg_list);
+	mutex_unlock(&otg_list_mutex);
+
+	/* were we in wait list? */
+	mutex_lock(&wait_list_mutex);
+	wait = usb_otg_get_wait(dev);
+	mutex_unlock(&wait_list_mutex);
+	if (wait) {
+		/* register pending host/gadget and flush from list */
+		usb_otg_flush_wait(dev);
+	}
+
+	return &otgd->fsm;
+
+err_wq:
+	kfree(otgd);
+unlock:
+	mutex_unlock(&otg_list_mutex);
+	return ERR_PTR(ret);
+}
+EXPORT_SYMBOL_GPL(usb_otg_register);
+
+/**
+ * usb_otg_unregister() - Unregister the OTG device from USB OTG core
+ * @dev: OTG controller device.
+ *
+ * Unregister OTG controller device from USB OTG core.
+ * Prevents unregistering till both the associated Host and Gadget controllers
+ * have unregistered from the OTG core.
+ *
+ * Return: 0 on success, error value otherwise.
+ */
+int usb_otg_unregister(struct device *dev)
+{
+	struct usb_otg *otgd;
+
+	mutex_lock(&otg_list_mutex);
+	otgd = usb_otg_get_data(dev);
+	if (!otgd) {
+		dev_err(dev, "otg: %s: device not in otg list\n",
+			__func__);
+		mutex_unlock(&otg_list_mutex);
+		return -EINVAL;
+	}
+
+	/* prevent unregister till both host & gadget have unregistered */
+	if (otgd->fsm.otg->host || otgd->fsm.otg->gadget) {
+		dev_err(dev, "otg: %s: host/gadget still registered\n",
+			__func__);
+		return -EBUSY;
+	}
+
+	/* OTG FSM is halted when host/gadget unregistered */
+	destroy_workqueue(otgd->wq);
+
+	/* remove from otg list */
+	list_del(&otgd->list);
+	kfree(otgd);
+	mutex_unlock(&otg_list_mutex);
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(usb_otg_unregister);
+
+/**
+ * start/kick the OTG FSM if we can
+ * fsm->lock must be held
+ */
+static void usb_otg_start_fsm(struct otg_fsm *fsm)
+{
+	struct usb_otg *otgd = container_of(fsm, struct usb_otg, fsm);
+
+	if (otgd->fsm_running)
+		goto kick_fsm;
+
+	if (!fsm->otg->host) {
+		dev_info(otgd->dev, "otg: can't start till host registers\n");
+		return;
+	}
+
+	if (!fsm->otg->gadget) {
+		dev_info(otgd->dev, "otg: can't start till gadget registers\n");
+		return;
+	}
+
+	otgd->fsm_running = true;
+kick_fsm:
+	queue_work(otgd->wq, &otgd->work);
+}
+
+/**
+ * stop the OTG FSM. Stops Host & Gadget controllers as well.
+ * fsm->lock must be held
+ */
+static void usb_otg_stop_fsm(struct otg_fsm *fsm)
+{
+	struct usb_otg *otgd = container_of(fsm, struct usb_otg, fsm);
+	int i;
+
+	if (!otgd->fsm_running)
+		return;
+
+	/* no more new events queued */
+	otgd->fsm_running = false;
+
+	/* Stop state machine / timers */
+	for (i = 0; i < ARRAY_SIZE(otgd->timers); i++)
+		hrtimer_cancel(&otgd->timers[i].timer);
+
+	flush_workqueue(otgd->wq);
+	fsm->otg->state = OTG_STATE_UNDEFINED;
+
+	/* stop host/gadget immediately */
+	if (fsm->protocol == PROTO_HOST)
+		otg_start_host(fsm, 0);
+	else if (fsm->protocol == PROTO_GADGET)
+		otg_start_gadget(fsm, 0);
+	fsm->protocol = PROTO_UNDEF;
+}
+
+/**
+ * usb_otg_sync_inputs - Sync OTG inputs with the OTG state machine
+ * @fsm:	OTG FSM instance
+ *
+ * Used by the OTG driver to update the inputs to the OTG
+ * state machine.
+ *
+ * Can be called in IRQ context.
+ */
+void usb_otg_sync_inputs(struct otg_fsm *fsm)
+{
+	struct usb_otg *otgd = container_of(fsm, struct usb_otg, fsm);
+
+	/* Don't kick FSM till it has started */
+	if (!otgd->fsm_running)
+		return;
+
+	/* Kick FSM */
+	queue_work(otgd->wq, &otgd->work);
+}
+EXPORT_SYMBOL_GPL(usb_otg_sync_inputs);
+
+/**
+ * usb_otg_kick_fsm - Kick the OTG state machine
+ * @hcd_gcd_device:	Host/Gadget controller device
+ *
+ * Used by USB host/device stack to sync OTG related
+ * events to the OTG state machine.
+ * e.g. change in host_bus->b_hnp_enable, gadget->b_hnp_enable
+ *
+ * Returns: 0 on success, error value otherwise.
+ */
+int usb_otg_kick_fsm(struct device *hcd_gcd_device)
+{
+	struct usb_otg *otgd;
+
+	mutex_lock(&otg_list_mutex);
+	otgd = usb_otg_get_data(usb_otg_get_device(hcd_gcd_device));
+	mutex_unlock(&otg_list_mutex);
+	if (!otgd) {
+		dev_dbg(hcd_gcd_device, "otg: %s: invalid host/gadget device\n",
+			__func__);
+		return -ENODEV;
+	}
+
+	usb_otg_sync_inputs(&otgd->fsm);
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(usb_otg_kick_fsm);
+
+/**
+ * usb_otg_register_hcd - Register Host controller to OTG core
+ * @hcd:	Host controller device
+ * @irqnum:	interrupt number
+ * @irqflags:	interrupt flags
+ * @ops:	HCD ops to add/remove the HCD
+ *
+ * This is used by the USB Host stack to register the Host controller
+ * to the OTG core. Host controller must not be started by the
+ * caller as it is left upto the OTG state machine to do so.
+ *
+ * Returns: 0 on success, error value otherwise.
+ */
+int usb_otg_register_hcd(struct usb_hcd *hcd, unsigned int irqnum,
+			 unsigned long irqflags, struct otg_hcd_ops *ops)
+{
+	struct usb_otg *otgd;
+	struct device *hcd_dev = hcd->self.controller;
+	struct device *otg_dev = usb_otg_get_device(hcd_dev);
+
+	if (!otg_dev)
+		return -EINVAL;	/* we're definitely not OTG */
+
+	/* we're otg but otg controller might not yet be registered */
+	mutex_lock(&otg_list_mutex);
+	otgd = usb_otg_get_data(otg_dev);
+	mutex_unlock(&otg_list_mutex);
+	if (!otgd) {
+		dev_dbg(hcd_dev,
+			"otg: controller not yet registered. waiting..\n");
+		/*
+		 * otg controller might register later. Put the hcd in
+		 * wait list and call us back when ready
+		 */
+		if (usb_otg_hcd_wait_add(otg_dev, hcd, irqnum, irqflags, ops)) {
+			dev_dbg(hcd_dev, "otg: failed to add to wait list\n");
+			return -EINVAL;
+		}
+
+		return 0;
+	}
+
+	/* HCD will be started by OTG fsm when needed */
+	mutex_lock(&otgd->fsm.lock);
+	if (otgd->primary_hcd.hcd) {
+		/* probably a shared HCD ? */
+		if (usb_otg_hcd_is_primary_hcd(hcd)) {
+			dev_err(otg_dev, "otg: primary host already registered\n");
+			goto err;
+		}
+
+		if (hcd->shared_hcd == otgd->primary_hcd.hcd) {
+			if (otgd->shared_hcd.hcd) {
+				dev_err(otg_dev, "otg: shared host already registered\n");
+				goto err;
+			}
+
+			otgd->shared_hcd.hcd = hcd;
+			otgd->shared_hcd.irqnum = irqnum;
+			otgd->shared_hcd.irqflags = irqflags;
+			otgd->shared_hcd.ops = ops;
+			dev_info(otg_dev, "otg: shared host %s registered\n",
+				 dev_name(hcd->self.controller));
+		} else {
+			dev_err(otg_dev, "otg: invalid shared host %s\n",
+				dev_name(hcd->self.controller));
+			goto err;
+		}
+	} else {
+		if (!usb_otg_hcd_is_primary_hcd(hcd)) {
+			dev_err(otg_dev, "otg: primary host must be registered first\n");
+			goto err;
+		}
+
+		otgd->primary_hcd.hcd = hcd;
+		otgd->primary_hcd.irqnum = irqnum;
+		otgd->primary_hcd.irqflags = irqflags;
+		otgd->primary_hcd.ops = ops;
+		dev_info(otg_dev, "otg: primary host %s registered\n",
+			 dev_name(hcd->self.controller));
+	}
+
+	/*
+	 * we're ready only if we have shared HCD
+	 * or we don't need shared HCD.
+	 */
+	if (otgd->shared_hcd.hcd || !otgd->primary_hcd.hcd->shared_hcd) {
+		otgd->fsm.otg->host = hcd_to_bus(hcd);
+		/* FIXME: set bus->otg_port if this is true OTG port with HNP */
+
+		/* start FSM */
+		usb_otg_start_fsm(&otgd->fsm);
+	} else {
+		dev_dbg(otg_dev, "otg: can't start till shared host registers\n");
+	}
+
+	mutex_unlock(&otgd->fsm.lock);
+
+	return 0;
+
+err:
+	mutex_unlock(&otgd->fsm.lock);
+	return -EINVAL;
+}
+EXPORT_SYMBOL_GPL(usb_otg_register_hcd);
+
+/**
+ * usb_otg_unregister_hcd - Unregister Host controller from OTG core
+ * @hcd:	Host controller device
+ *
+ * This is used by the USB Host stack to unregister the Host controller
+ * from the OTG core. Ensures that Host controller is not running
+ * on successful return.
+ *
+ * Returns: 0 on success, error value otherwise.
+ */
+int usb_otg_unregister_hcd(struct usb_hcd *hcd)
+{
+	struct usb_otg *otgd;
+	struct device *hcd_dev = hcd_to_bus(hcd)->controller;
+	struct device *otg_dev = usb_otg_get_device(hcd_dev);
+
+	if (!otg_dev)
+		return -EINVAL;	/* we're definitely not OTG */
+
+	mutex_lock(&otg_list_mutex);
+	otgd = usb_otg_get_data(otg_dev);
+	mutex_unlock(&otg_list_mutex);
+	if (!otgd) {
+		/* are we in wait list? */
+		if (!usb_otg_hcd_wait_remove(hcd))
+			return 0;
+
+		dev_dbg(hcd_dev, "otg: host wasn't registered with otg\n");
+		return -EINVAL;
+	}
+
+	mutex_lock(&otgd->fsm.lock);
+	if (hcd == otgd->primary_hcd.hcd) {
+		otgd->primary_hcd.hcd = NULL;
+		dev_info(otg_dev, "otg: primary host %s unregistered\n",
+			 dev_name(hcd_dev));
+	} else if (hcd == otgd->shared_hcd.hcd) {
+		otgd->shared_hcd.hcd = NULL;
+		dev_info(otg_dev, "otg: shared host %s unregistered\n",
+			 dev_name(hcd_dev));
+	} else {
+		dev_err(otg_dev, "otg: host %s wasn't registered with otg\n",
+			dev_name(hcd_dev));
+		mutex_unlock(&otgd->fsm.lock);
+		return -EINVAL;
+	}
+
+	/* stop FSM & Host */
+	usb_otg_stop_fsm(&otgd->fsm);
+	otgd->fsm.otg->host = NULL;
+
+	mutex_unlock(&otgd->fsm.lock);
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(usb_otg_unregister_hcd);
+
+/**
+ * usb_otg_register_gadget - Register Gadget controller to OTG core
+ * @gadget:	Gadget controller
+ *
+ * This is used by the USB Gadget stack to register the Gadget controller
+ * to the OTG core. Gadget controller must not be started by the
+ * caller as it is left upto the OTG state machine to do so.
+ *
+ * Gadget core must call this only when all resources required for
+ * gadget controller to run are available.
+ * i.e. gadget function driver is available.
+ *
+ * Returns: 0 on success, error value otherwise.
+ */
+int usb_otg_register_gadget(struct usb_gadget *gadget,
+			    struct otg_gadget_ops *ops)
+{
+	struct usb_otg *otgd;
+	struct device *gadget_dev = &gadget->dev;
+	struct device *otg_dev = usb_otg_get_device(gadget_dev);
+
+	if (!otg_dev)
+		return -EINVAL;	/* we're definitely not OTG */
+
+	/* we're otg but otg controller might not yet be registered */
+	mutex_lock(&otg_list_mutex);
+	otgd = usb_otg_get_data(otg_dev);
+	mutex_unlock(&otg_list_mutex);
+	if (!otgd) {
+		dev_dbg(gadget_dev,
+			"otg: controller not yet registered. waiting..\n");
+		/*
+		 * otg controller might register later. Put the gadget in
+		 * wait list and call us back when ready
+		 */
+		if (usb_otg_gadget_wait_add(otg_dev, gadget, ops)) {
+			dev_dbg(gadget_dev, "otg: failed to add to wait list\n");
+			return -EINVAL;
+		}
+
+		return 0;
+	}
+
+	mutex_lock(&otgd->fsm.lock);
+	if (otgd->fsm.otg->gadget) {
+		dev_err(otg_dev, "otg: gadget already registered with otg\n");
+		mutex_unlock(&otgd->fsm.lock);
+		return -EINVAL;
+	}
+
+	otgd->fsm.otg->gadget = gadget;
+	otgd->gadget_ops = ops;
+	dev_info(otg_dev, "otg: gadget %s registered\n",
+		 dev_name(&gadget->dev));
+
+	/* start FSM */
+	usb_otg_start_fsm(&otgd->fsm);
+	mutex_unlock(&otgd->fsm.lock);
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(usb_otg_register_gadget);
+
+/**
+ * usb_otg_unregister_gadget - Unregister Gadget controller from OTG core
+ * @gadget:	Gadget controller
+ *
+ * This is used by the USB Gadget stack to unregister the Gadget controller
+ * from the OTG core. Ensures that Gadget controller is not running
+ * on successful return.
+ *
+ * Returns: 0 on success, error value otherwise.
+ */
+int usb_otg_unregister_gadget(struct usb_gadget *gadget)
+{
+	struct usb_otg *otgd;
+	struct device *gadget_dev = &gadget->dev;
+	struct device *otg_dev = usb_otg_get_device(gadget_dev);
+
+	if (!otg_dev)
+		return -EINVAL;
+
+	mutex_lock(&otg_list_mutex);
+	otgd = usb_otg_get_data(otg_dev);
+	mutex_unlock(&otg_list_mutex);
+	if (!otgd) {
+		/* are we in wait list? */
+		if (!usb_otg_gadget_wait_remove(gadget))
+			return 0;
+
+		dev_dbg(gadget_dev, "otg: gadget wasn't registered with otg\n");
+		return -EINVAL;
+	}
+
+	mutex_lock(&otgd->fsm.lock);
+	if (otgd->fsm.otg->gadget != gadget) {
+		dev_err(otg_dev, "otg: gadget %s wasn't registered with otg\n",
+			dev_name(&gadget->dev));
+		mutex_unlock(&otgd->fsm.lock);
+		return -EINVAL;
+	}
+
+	/* Stop FSM & gadget */
+	usb_otg_stop_fsm(&otgd->fsm);
+	otgd->fsm.otg->gadget = NULL;
+	mutex_unlock(&otgd->fsm.lock);
+
+	dev_info(otg_dev, "otg: gadget %s unregistered\n",
+		 dev_name(&gadget->dev));
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(usb_otg_unregister_gadget);
+
+/**
+ * usb_otg_fsm_to_dev - Get OTG controller device from struct otg_fsm
+ * @fsm:	otg_fsm data structure
+ *
+ * This is used by the OTG controller driver to get it's device node
+ * from any of the otg_fsm->ops.
+ */
+struct device *usb_otg_fsm_to_dev(struct otg_fsm *fsm)
+{
+	struct usb_otg *otgd = container_of(fsm, struct usb_otg, fsm);
+
+	return otgd->dev;
+}
+EXPORT_SYMBOL_GPL(usb_otg_fsm_to_dev);
diff --git a/drivers/usb/common/usb-otg.h b/drivers/usb/common/usb-otg.h
new file mode 100644
index 0000000..05331f0
--- /dev/null
+++ b/drivers/usb/common/usb-otg.h
@@ -0,0 +1,71 @@
+/**
+ * drivers/usb/common/usb-otg.h - USB OTG core local header
+ *
+ * Copyright (C) 2015 Texas Instruments Incorporated - http://www.ti.com
+ * Author: Roger Quadros <rogerq@ti.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef __DRIVERS_USB_COMMON_USB_OTG_H
+#define __DRIVERS_USB_COMMON_USB_OTG_H
+
+/*
+ *  A-DEVICE timing constants
+ */
+
+/* Wait for VBUS Rise  */
+#define TA_WAIT_VRISE        (100)	/* a_wait_vrise: section 7.1.2
+					 * a_wait_vrise_tmr: section 7.4.5.1
+					 * TA_VBUS_RISE <= 100ms, section 4.4
+					 * Table 4-1: Electrical Characteristics
+					 * ->DC Electrical Timing
+					 */
+/* Wait for VBUS Fall  */
+#define TA_WAIT_VFALL        (1000)	/* a_wait_vfall: section 7.1.7
+					 * a_wait_vfall_tmr: section: 7.4.5.2
+					 */
+/* Wait for B-Connect */
+#define TA_WAIT_BCON         (10000)	/* a_wait_bcon: section 7.1.3
+					 * TA_WAIT_BCON: should be between 1100
+					 * and 30000 ms, section 5.5, Table 5-1
+					 */
+/* A-Idle to B-Disconnect */
+#define TA_AIDL_BDIS         (5000)	/* a_suspend min 200 ms, section 5.2.1
+					 * TA_AIDL_BDIS: section 5.5, Table 5-1
+					 */
+/* B-Idle to A-Disconnect */
+#define TA_BIDL_ADIS         (500)	/* TA_BIDL_ADIS: section 5.2.1
+					 * 500ms is used for B switch to host
+					 * for safe
+					 */
+
+/*
+ * B-device timing constants
+ */
+
+/* Data-Line Pulse Time*/
+#define TB_DATA_PLS          (10)	/* b_srp_init,continue 5~10ms
+					 * section:5.1.3
+					 */
+/* SRP Fail Time  */
+#define TB_SRP_FAIL          (6000)	/* b_srp_init,fail time 5~6s
+					 * section:5.1.6
+					 */
+/* A-SE0 to B-Reset  */
+#define TB_ASE0_BRST         (155)	/* minimum 155 ms, section:5.3.1 */
+/* SE0 Time Before SRP */
+#define TB_SE0_SRP           (1000)	/* b_idle,minimum 1s, section:5.1.2 */
+/* SSEND time before SRP */
+#define TB_SSEND_SRP         (1500)	/* minimum 1.5 sec, section:5.1.2 */
+
+#define TB_SESS_VLD          (1000)
+
+#endif /* __DRIVERS_USB_COMMON_USB_OTG_H */
diff --git a/drivers/usb/core/Kconfig b/drivers/usb/core/Kconfig
index a99c89e..b468a9f 100644
--- a/drivers/usb/core/Kconfig
+++ b/drivers/usb/core/Kconfig
@@ -42,7 +42,7 @@ config USB_DYNAMIC_MINORS
 	  If you are unsure about this, say N here.
 
 config USB_OTG
-	bool "OTG support"
+	bool "OTG/Dual-role support"
 	depends on PM
 	default n
 	help
@@ -75,15 +75,6 @@ config USB_OTG_BLACKLIST_HUB
 	  and software costs by not supporting external hubs.  So
 	  are "Embedded Hosts" that don't offer OTG support.
 
-config USB_OTG_FSM
-	tristate "USB 2.0 OTG FSM implementation"
-	depends on USB
-	select USB_OTG
-	select USB_PHY
-	help
-	  Implements OTG Finite State Machine as specified in On-The-Go
-	  and Embedded Host Supplement to the USB Revision 2.0 Specification.
-
 config USB_ULPI_BUS
 	tristate "USB ULPI PHY interface support"
 	depends on USB_SUPPORT
diff --git a/include/linux/usb/otg.h b/include/linux/usb/otg.h
index bd1dcf8..38cabe0 100644
--- a/include/linux/usb/otg.h
+++ b/include/linux/usb/otg.h
@@ -10,19 +10,100 @@
 #define __LINUX_USB_OTG_H
 
 #include <linux/phy/phy.h>
+#include <linux/device.h>
+#include <linux/hrtimer.h>
+#include <linux/ktime.h>
+#include <linux/usb.h>
+#include <linux/usb/hcd.h>
+#include <linux/usb/gadget.h>
+#include <linux/usb/otg-fsm.h>
 #include <linux/usb/phy.h>
 
+/**
+ * struct otg_hcd - host controller state and interface
+ *
+ * @hcd: host controller
+ * @irqnum: irq number
+ * @irqflags: irq flags
+ * @ops: otg to host controller interface
+ */
+struct otg_hcd {
+	struct usb_hcd *hcd;
+	unsigned int irqnum;
+	unsigned long irqflags;
+	struct otg_hcd_ops *ops;
+};
+
+struct usb_otg;
+
+/**
+ * struct otg_timer - otg timer data
+ *
+ * @timer: high resolution timer
+ * @timeout: timeout value
+ * @timetout_bit: pointer to variable that is set on timeout
+ * @otgd: usb otg data
+ */
+struct otg_timer {
+	struct hrtimer timer;
+	ktime_t timeout;
+	/* callback data */
+	int *timeout_bit;
+	struct usb_otg *otgd;
+};
+
+/**
+ * struct usb_otg - usb otg controller state
+ *
+ * @default_a: Indicates we are an A device. i.e. Host.
+ * @phy: USB phy interface
+ * @usb_phy: old usb_phy interface
+ * @host: host controller bus
+ * @gadget: gadget device
+ * @state: current otg state
+ * @dev: otg controller device
+ * @caps: otg capabilities revision, hnp, srp, etc
+ * @fsm: otg finite state machine
+ * @fsm_ops: controller hooks for the state machine
+ * ------- internal use only -------
+ * @primary_hcd: primary host state and interface
+ * @shared_hcd: shared host state and interface
+ * @gadget_ops: gadget interface
+ * @timers: otg timers for state machine
+ * @list: list of otg controllers
+ * @work: otg state machine work
+ * @wq: otg state machine work queue
+ * @fsm_running: state machine running/stopped indicator
+ */
 struct usb_otg {
 	u8			default_a;
 
 	struct phy		*phy;
 	/* old usb_phy interface */
 	struct usb_phy		*usb_phy;
+
 	struct usb_bus		*host;
 	struct usb_gadget	*gadget;
 
 	enum usb_otg_state	state;
 
+	struct device *dev;
+	struct usb_otg_caps *caps;
+	struct otg_fsm fsm;
+	struct otg_fsm_ops fsm_ops;
+
+	/* internal use only */
+	struct otg_hcd primary_hcd;
+	struct otg_hcd shared_hcd;
+	struct otg_gadget_ops *gadget_ops;
+	struct otg_timer timers[NUM_OTG_FSM_TIMERS];
+	struct list_head list;
+	struct work_struct work;
+	struct workqueue_struct *wq;
+	bool fsm_running;
+	/* use otg->fsm.lock for serializing access */
+
+/*------------- deprecated interface -----------------------------*/
 	/* bind/unbind the host controller */
 	int	(*set_host)(struct usb_otg *otg, struct usb_bus *host);
 
@@ -38,7 +119,7 @@ struct usb_otg {
 
 	/* start or continue HNP role switch */
 	int	(*start_hnp)(struct usb_otg *otg);
-
+/*---------------------------------------------------------------*/
 };
 
 /**
@@ -56,8 +137,105 @@ struct usb_otg_caps {
 	bool adp_support;
 };
 
+/**
+ * struct usb_otg_config - otg controller configuration
+ * @caps: otg capabilities of the controller
+ * @ops: otg fsm operations
+ * @otg_timeouts: override default otg fsm timeouts
+ */
+struct usb_otg_config {
+	struct usb_otg_caps otg_caps;
+	struct otg_fsm_ops *fsm_ops;
+	unsigned otg_timeouts[NUM_OTG_FSM_TIMERS];
+};
+
 extern const char *usb_otg_state_string(enum usb_otg_state state);
 
+enum usb_dr_mode {
+	USB_DR_MODE_UNKNOWN,
+	USB_DR_MODE_HOST,
+	USB_DR_MODE_PERIPHERAL,
+	USB_DR_MODE_OTG,
+};
+
+#if IS_ENABLED(CONFIG_USB_OTG)
+struct otg_fsm *usb_otg_register(struct device *dev,
+				 struct usb_otg_config *config);
+int usb_otg_unregister(struct device *dev);
+int usb_otg_register_hcd(struct usb_hcd *hcd, unsigned int irqnum,
+			 unsigned long irqflags, struct otg_hcd_ops *ops);
+int usb_otg_unregister_hcd(struct usb_hcd *hcd);
+int usb_otg_register_gadget(struct usb_gadget *gadget,
+			    struct otg_gadget_ops *ops);
+int usb_otg_unregister_gadget(struct usb_gadget *gadget);
+void usb_otg_sync_inputs(struct otg_fsm *fsm);
+int usb_otg_kick_fsm(struct device *hcd_gcd_device);
+struct device *usb_otg_fsm_to_dev(struct otg_fsm *fsm);
+int usb_otg_start_host(struct otg_fsm *fsm, int on);
+int usb_otg_start_gadget(struct otg_fsm *fsm, int on);
+
+#else /* CONFIG_USB_OTG */
+
+static inline struct otg_fsm *usb_otg_register(struct device *dev,
+					       struct usb_otg_config *config)
+{
+	return ERR_PTR(-ENOTSUPP);
+}
+
+static inline int usb_otg_unregister(struct device *dev)
+{
+	return -ENOTSUPP;
+}
+
+static inline int usb_otg_register_hcd(struct usb_hcd *hcd, unsigned int irqnum,
+				       unsigned long irqflags,
+				       struct otg_hcd_ops *ops)
+{
+	return -ENOTSUPP;
+}
+
+static inline int usb_otg_unregister_hcd(struct usb_hcd *hcd)
+{
+	return -ENOTSUPP;
+}
+
+static inline int usb_otg_register_gadget(struct usb_gadget *gadget,
+					  struct otg_gadget_ops *ops)
+{
+	return -ENOTSUPP;
+}
+
+static inline int usb_otg_unregister_gadget(struct usb_gadget *gadget)
+{
+	return -ENOTSUPP;
+}
+
+static inline void usb_otg_sync_inputs(struct otg_fsm *fsm)
+{
+}
+
+static inline int usb_otg_kick_fsm(struct device *hcd_gcd_device)
+{
+	return -ENOTSUPP;
+}
+
+static inline struct device *usb_otg_fsm_to_dev(struct otg_fsm *fsm)
+{
+	return NULL;
+}
+
+static inline int usb_otg_start_host(struct otg_fsm *fsm, int on)
+{
+	return -ENOTSUPP;
+}
+
+static inline int usb_otg_start_gadget(struct otg_fsm *fsm, int on)
+{
+	return -ENOTSUPP;
+}
+#endif /* CONFIG_USB_OTG */
+
+/*------------- deprecated interface -----------------------------*/
 /* Context: can sleep */
 static inline int
 otg_start_hnp(struct usb_otg *otg)
@@ -109,14 +287,9 @@ otg_start_srp(struct usb_otg *otg)
 	return -ENOTSUPP;
 }
 
+/*---------------------------------------------------------------*/
+
 /* for OTG controller drivers (and maybe other stuff) */
 extern int usb_bus_start_enum(struct usb_bus *bus, unsigned port_num);
 
-enum usb_dr_mode {
-	USB_DR_MODE_UNKNOWN,
-	USB_DR_MODE_HOST,
-	USB_DR_MODE_PERIPHERAL,
-	USB_DR_MODE_OTG,
-};
-
 #endif /* __LINUX_USB_OTG_H */
-- 
2.1.4


^ permalink raw reply related	[flat|nested] 48+ messages in thread

* Re: [PATCH v4 07/13] usb: otg: add OTG core
  2015-08-24 13:21   ` Roger Quadros
@ 2015-09-07  1:23     ` Peter Chen
  -1 siblings, 0 replies; 48+ messages in thread
From: Peter Chen @ 2015-09-07  1:23 UTC (permalink / raw)
  To: Roger Quadros
  Cc: stern, balbi, gregkh, dan.j.williams, jun.li, mathias.nyman, tony,
	Joao.Pinto, abrestic, linux-usb, linux-kernel, linux-omap

On Mon, Aug 24, 2015 at 04:21:18PM +0300, Roger Quadros wrote:
> + * This is used by the USB Host stack to register the Host controller
> + * to the OTG core. Host controller must not be started by the
> + * caller as it is left upto the OTG state machine to do so.
> + *
> + * Returns: 0 on success, error value otherwise.
> + */
> +int usb_otg_register_hcd(struct usb_hcd *hcd, unsigned int irqnum,
> +			 unsigned long irqflags, struct otg_hcd_ops *ops)
> +{
> +	struct usb_otg *otgd;
> +	struct device *hcd_dev = hcd->self.controller;
> +	struct device *otg_dev = usb_otg_get_device(hcd_dev);
> +

One big problem here is: there are two designs for current (IP) driver
code, one creates dedicated hcd device as roothub's parent, like dwc3.
Another one doesn't do this, roothub's parent is core device (or otg device
in your patch), like chipidea and dwc2.

Then, otg_dev will be glue layer device for chipidea after that.

Peter

> +	if (!otg_dev)
> +		return -EINVAL;	/* we're definitely not OTG */
> +
> +	/* we're otg but otg controller might not yet be registered */
> +	mutex_lock(&otg_list_mutex);
> +	otgd = usb_otg_get_data(otg_dev);
> +	mutex_unlock(&otg_list_mutex);
> +	if (!otgd) {
> +		dev_dbg(hcd_dev,
> +			"otg: controller not yet registered. waiting..\n");
> +		/*
> +		 * otg controller might register later. Put the hcd in
> +		 * wait list and call us back when ready
> +		 */
> +		if (usb_otg_hcd_wait_add(otg_dev, hcd, irqnum, irqflags, ops)) {
> +			dev_dbg(hcd_dev, "otg: failed to add to wait list\n");
> +			return -EINVAL;
> +		}
> +
> +		return 0;
> +	}
> +
> +	/* HCD will be started by OTG fsm when needed */
> +	mutex_lock(&otgd->fsm.lock);
> +	if (otgd->primary_hcd.hcd) {
> +		/* probably a shared HCD ? */
> +		if (usb_otg_hcd_is_primary_hcd(hcd)) {
> +			dev_err(otg_dev, "otg: primary host already registered\n");
> +			goto err;
> +		}
> +
> +		if (hcd->shared_hcd == otgd->primary_hcd.hcd) {
> +			if (otgd->shared_hcd.hcd) {
> +				dev_err(otg_dev, "otg: shared host already registered\n");
> +				goto err;
> +			}
> +
> +			otgd->shared_hcd.hcd = hcd;
> +			otgd->shared_hcd.irqnum = irqnum;
> +			otgd->shared_hcd.irqflags = irqflags;
> +			otgd->shared_hcd.ops = ops;
> +			dev_info(otg_dev, "otg: shared host %s registered\n",
> +				 dev_name(hcd->self.controller));
> +		} else {
> +			dev_err(otg_dev, "otg: invalid shared host %s\n",
> +				dev_name(hcd->self.controller));
> +			goto err;
> +		}
> +	} else {
> +		if (!usb_otg_hcd_is_primary_hcd(hcd)) {
> +			dev_err(otg_dev, "otg: primary host must be registered first\n");
> +			goto err;
> +		}
> +
> +		otgd->primary_hcd.hcd = hcd;
> +		otgd->primary_hcd.irqnum = irqnum;
> +		otgd->primary_hcd.irqflags = irqflags;
> +		otgd->primary_hcd.ops = ops;
> +		dev_info(otg_dev, "otg: primary host %s registered\n",
> +			 dev_name(hcd->self.controller));
> +	}
> +
> +	/*
> +	 * we're ready only if we have shared HCD
> +	 * or we don't need shared HCD.
> +	 */
> +	if (otgd->shared_hcd.hcd || !otgd->primary_hcd.hcd->shared_hcd) {
> +		otgd->fsm.otg->host = hcd_to_bus(hcd);
> +		/* FIXME: set bus->otg_port if this is true OTG port with HNP */
> +
> +		/* start FSM */
> +		usb_otg_start_fsm(&otgd->fsm);
> +	} else {
> +		dev_dbg(otg_dev, "otg: can't start till shared host registers\n");
> +	}
> +
> +	mutex_unlock(&otgd->fsm.lock);
> +
> +	return 0;
> +
> +err:
> +	mutex_unlock(&otgd->fsm.lock);
> +	return -EINVAL;
> +}
> +EXPORT_SYMBOL_GPL(usb_otg_register_hcd);
> +
> +/**
> + * usb_otg_unregister_hcd - Unregister Host controller from OTG core
> + * @hcd:	Host controller device
> + *
> + * This is used by the USB Host stack to unregister the Host controller
> + * from the OTG core. Ensures that Host controller is not running
> + * on successful return.
> + *
> + * Returns: 0 on success, error value otherwise.
> + */
> +int usb_otg_unregister_hcd(struct usb_hcd *hcd)
> +{
> +	struct usb_otg *otgd;
> +	struct device *hcd_dev = hcd_to_bus(hcd)->controller;
> +	struct device *otg_dev = usb_otg_get_device(hcd_dev);
> +
> +	if (!otg_dev)
> +		return -EINVAL;	/* we're definitely not OTG */
> +
> +	mutex_lock(&otg_list_mutex);
> +	otgd = usb_otg_get_data(otg_dev);
> +	mutex_unlock(&otg_list_mutex);
> +	if (!otgd) {
> +		/* are we in wait list? */
> +		if (!usb_otg_hcd_wait_remove(hcd))
> +			return 0;
> +
> +		dev_dbg(hcd_dev, "otg: host wasn't registered with otg\n");
> +		return -EINVAL;
> +	}
> +
> +	mutex_lock(&otgd->fsm.lock);
> +	if (hcd == otgd->primary_hcd.hcd) {
> +		otgd->primary_hcd.hcd = NULL;
> +		dev_info(otg_dev, "otg: primary host %s unregistered\n",
> +			 dev_name(hcd_dev));
> +	} else if (hcd == otgd->shared_hcd.hcd) {
> +		otgd->shared_hcd.hcd = NULL;
> +		dev_info(otg_dev, "otg: shared host %s unregistered\n",
> +			 dev_name(hcd_dev));
> +	} else {
> +		dev_err(otg_dev, "otg: host %s wasn't registered with otg\n",
> +			dev_name(hcd_dev));
> +		mutex_unlock(&otgd->fsm.lock);
> +		return -EINVAL;
> +	}
> +
> +	/* stop FSM & Host */
> +	usb_otg_stop_fsm(&otgd->fsm);
> +	otgd->fsm.otg->host = NULL;
> +
> +	mutex_unlock(&otgd->fsm.lock);
> +
> +	return 0;
> +}
> +EXPORT_SYMBOL_GPL(usb_otg_unregister_hcd);
> +
> +/**
> + * usb_otg_register_gadget - Register Gadget controller to OTG core
> + * @gadget:	Gadget controller
> + *
> + * This is used by the USB Gadget stack to register the Gadget controller
> + * to the OTG core. Gadget controller must not be started by the
> + * caller as it is left upto the OTG state machine to do so.
> + *
> + * Gadget core must call this only when all resources required for
> + * gadget controller to run are available.
> + * i.e. gadget function driver is available.
> + *
> + * Returns: 0 on success, error value otherwise.
> + */
> +int usb_otg_register_gadget(struct usb_gadget *gadget,
> +			    struct otg_gadget_ops *ops)
> +{
> +	struct usb_otg *otgd;
> +	struct device *gadget_dev = &gadget->dev;
> +	struct device *otg_dev = usb_otg_get_device(gadget_dev);
> +
> +	if (!otg_dev)
> +		return -EINVAL;	/* we're definitely not OTG */
> +
> +	/* we're otg but otg controller might not yet be registered */
> +	mutex_lock(&otg_list_mutex);
> +	otgd = usb_otg_get_data(otg_dev);
> +	mutex_unlock(&otg_list_mutex);
> +	if (!otgd) {
> +		dev_dbg(gadget_dev,
> +			"otg: controller not yet registered. waiting..\n");
> +		/*
> +		 * otg controller might register later. Put the gadget in
> +		 * wait list and call us back when ready
> +		 */
> +		if (usb_otg_gadget_wait_add(otg_dev, gadget, ops)) {
> +			dev_dbg(gadget_dev, "otg: failed to add to wait list\n");
> +			return -EINVAL;
> +		}
> +
> +		return 0;
> +	}
> +
> +	mutex_lock(&otgd->fsm.lock);
> +	if (otgd->fsm.otg->gadget) {
> +		dev_err(otg_dev, "otg: gadget already registered with otg\n");
> +		mutex_unlock(&otgd->fsm.lock);
> +		return -EINVAL;
> +	}
> +
> +	otgd->fsm.otg->gadget = gadget;
> +	otgd->gadget_ops = ops;
> +	dev_info(otg_dev, "otg: gadget %s registered\n",
> +		 dev_name(&gadget->dev));
> +
> +	/* start FSM */
> +	usb_otg_start_fsm(&otgd->fsm);
> +	mutex_unlock(&otgd->fsm.lock);
> +
> +	return 0;
> +}
> +EXPORT_SYMBOL_GPL(usb_otg_register_gadget);
> +
> +/**
> + * usb_otg_unregister_gadget - Unregister Gadget controller from OTG core
> + * @gadget:	Gadget controller
> + *
> + * This is used by the USB Gadget stack to unregister the Gadget controller
> + * from the OTG core. Ensures that Gadget controller is not running
> + * on successful return.
> + *
> + * Returns: 0 on success, error value otherwise.
> + */
> +int usb_otg_unregister_gadget(struct usb_gadget *gadget)
> +{
> +	struct usb_otg *otgd;
> +	struct device *gadget_dev = &gadget->dev;
> +	struct device *otg_dev = usb_otg_get_device(gadget_dev);
> +
> +	if (!otg_dev)
> +		return -EINVAL;
> +
> +	mutex_lock(&otg_list_mutex);
> +	otgd = usb_otg_get_data(otg_dev);
> +	mutex_unlock(&otg_list_mutex);
> +	if (!otgd) {
> +		/* are we in wait list? */
> +		if (!usb_otg_gadget_wait_remove(gadget))
> +			return 0;
> +
> +		dev_dbg(gadget_dev, "otg: gadget wasn't registered with otg\n");
> +		return -EINVAL;
> +	}
> +
> +	mutex_lock(&otgd->fsm.lock);
> +	if (otgd->fsm.otg->gadget != gadget) {
> +		dev_err(otg_dev, "otg: gadget %s wasn't registered with otg\n",
> +			dev_name(&gadget->dev));
> +		mutex_unlock(&otgd->fsm.lock);
> +		return -EINVAL;
> +	}
> +
> +	/* Stop FSM & gadget */
> +	usb_otg_stop_fsm(&otgd->fsm);
> +	otgd->fsm.otg->gadget = NULL;
> +	mutex_unlock(&otgd->fsm.lock);
> +
> +	dev_info(otg_dev, "otg: gadget %s unregistered\n",
> +		 dev_name(&gadget->dev));
> +
> +	return 0;
> +}
> +EXPORT_SYMBOL_GPL(usb_otg_unregister_gadget);
> +
> +/**
> + * usb_otg_fsm_to_dev - Get OTG controller device from struct otg_fsm
> + * @fsm:	otg_fsm data structure
> + *
> + * This is used by the OTG controller driver to get it's device node
> + * from any of the otg_fsm->ops.
> + */
> +struct device *usb_otg_fsm_to_dev(struct otg_fsm *fsm)
> +{
> +	struct usb_otg *otgd = container_of(fsm, struct usb_otg, fsm);
> +
> +	return otgd->dev;
> +}
> +EXPORT_SYMBOL_GPL(usb_otg_fsm_to_dev);
> diff --git a/drivers/usb/common/usb-otg.h b/drivers/usb/common/usb-otg.h
> new file mode 100644
> index 0000000..05331f0
> --- /dev/null
> +++ b/drivers/usb/common/usb-otg.h
> @@ -0,0 +1,71 @@
> +/**
> + * drivers/usb/common/usb-otg.h - USB OTG core local header
> + *
> + * Copyright (C) 2015 Texas Instruments Incorporated - http://www.ti.com
> + * Author: Roger Quadros <rogerq@ti.com>
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License version 2 as
> + * published by the Free Software Foundation.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + * GNU General Public License for more details.
> + */
> +
> +#ifndef __DRIVERS_USB_COMMON_USB_OTG_H
> +#define __DRIVERS_USB_COMMON_USB_OTG_H
> +
> +/*
> + *  A-DEVICE timing constants
> + */
> +
> +/* Wait for VBUS Rise  */
> +#define TA_WAIT_VRISE        (100)	/* a_wait_vrise: section 7.1.2
> +					 * a_wait_vrise_tmr: section 7.4.5.1
> +					 * TA_VBUS_RISE <= 100ms, section 4.4
> +					 * Table 4-1: Electrical Characteristics
> +					 * ->DC Electrical Timing
> +					 */
> +/* Wait for VBUS Fall  */
> +#define TA_WAIT_VFALL        (1000)	/* a_wait_vfall: section 7.1.7
> +					 * a_wait_vfall_tmr: section: 7.4.5.2
> +					 */
> +/* Wait for B-Connect */
> +#define TA_WAIT_BCON         (10000)	/* a_wait_bcon: section 7.1.3
> +					 * TA_WAIT_BCON: should be between 1100
> +					 * and 30000 ms, section 5.5, Table 5-1
> +					 */
> +/* A-Idle to B-Disconnect */
> +#define TA_AIDL_BDIS         (5000)	/* a_suspend min 200 ms, section 5.2.1
> +					 * TA_AIDL_BDIS: section 5.5, Table 5-1
> +					 */
> +/* B-Idle to A-Disconnect */
> +#define TA_BIDL_ADIS         (500)	/* TA_BIDL_ADIS: section 5.2.1
> +					 * 500ms is used for B switch to host
> +					 * for safe
> +					 */
> +
> +/*
> + * B-device timing constants
> + */
> +
> +/* Data-Line Pulse Time*/
> +#define TB_DATA_PLS          (10)	/* b_srp_init,continue 5~10ms
> +					 * section:5.1.3
> +					 */
> +/* SRP Fail Time  */
> +#define TB_SRP_FAIL          (6000)	/* b_srp_init,fail time 5~6s
> +					 * section:5.1.6
> +					 */
> +/* A-SE0 to B-Reset  */
> +#define TB_ASE0_BRST         (155)	/* minimum 155 ms, section:5.3.1 */
> +/* SE0 Time Before SRP */
> +#define TB_SE0_SRP           (1000)	/* b_idle,minimum 1s, section:5.1.2 */
> +/* SSEND time before SRP */
> +#define TB_SSEND_SRP         (1500)	/* minimum 1.5 sec, section:5.1.2 */
> +
> +#define TB_SESS_VLD          (1000)
> +
> +#endif /* __DRIVERS_USB_COMMON_USB_OTG_H */
> diff --git a/drivers/usb/core/Kconfig b/drivers/usb/core/Kconfig
> index a99c89e..b468a9f 100644
> --- a/drivers/usb/core/Kconfig
> +++ b/drivers/usb/core/Kconfig
> @@ -42,7 +42,7 @@ config USB_DYNAMIC_MINORS
>  	  If you are unsure about this, say N here.
>  
>  config USB_OTG
> -	bool "OTG support"
> +	bool "OTG/Dual-role support"
>  	depends on PM
>  	default n
>  	help
> @@ -75,15 +75,6 @@ config USB_OTG_BLACKLIST_HUB
>  	  and software costs by not supporting external hubs.  So
>  	  are "Embedded Hosts" that don't offer OTG support.
>  
> -config USB_OTG_FSM
> -	tristate "USB 2.0 OTG FSM implementation"
> -	depends on USB
> -	select USB_OTG
> -	select USB_PHY
> -	help
> -	  Implements OTG Finite State Machine as specified in On-The-Go
> -	  and Embedded Host Supplement to the USB Revision 2.0 Specification.
> -
>  config USB_ULPI_BUS
>  	tristate "USB ULPI PHY interface support"
>  	depends on USB_SUPPORT
> diff --git a/include/linux/usb/otg.h b/include/linux/usb/otg.h
> index bd1dcf8..38cabe0 100644
> --- a/include/linux/usb/otg.h
> +++ b/include/linux/usb/otg.h
> @@ -10,19 +10,100 @@
>  #define __LINUX_USB_OTG_H
>  
>  #include <linux/phy/phy.h>
> +#include <linux/device.h>
> +#include <linux/hrtimer.h>
> +#include <linux/ktime.h>
> +#include <linux/usb.h>
> +#include <linux/usb/hcd.h>
> +#include <linux/usb/gadget.h>
> +#include <linux/usb/otg-fsm.h>
>  #include <linux/usb/phy.h>
>  
> +/**
> + * struct otg_hcd - host controller state and interface
> + *
> + * @hcd: host controller
> + * @irqnum: irq number
> + * @irqflags: irq flags
> + * @ops: otg to host controller interface
> + */
> +struct otg_hcd {
> +	struct usb_hcd *hcd;
> +	unsigned int irqnum;
> +	unsigned long irqflags;
> +	struct otg_hcd_ops *ops;
> +};
> +
> +struct usb_otg;
> +
> +/**
> + * struct otg_timer - otg timer data
> + *
> + * @timer: high resolution timer
> + * @timeout: timeout value
> + * @timetout_bit: pointer to variable that is set on timeout
> + * @otgd: usb otg data
> + */
> +struct otg_timer {
> +	struct hrtimer timer;
> +	ktime_t timeout;
> +	/* callback data */
> +	int *timeout_bit;
> +	struct usb_otg *otgd;
> +};
> +
> +/**
> + * struct usb_otg - usb otg controller state
> + *
> + * @default_a: Indicates we are an A device. i.e. Host.
> + * @phy: USB phy interface
> + * @usb_phy: old usb_phy interface
> + * @host: host controller bus
> + * @gadget: gadget device
> + * @state: current otg state
> + * @dev: otg controller device
> + * @caps: otg capabilities revision, hnp, srp, etc
> + * @fsm: otg finite state machine
> + * @fsm_ops: controller hooks for the state machine
> + * ------- internal use only -------
> + * @primary_hcd: primary host state and interface
> + * @shared_hcd: shared host state and interface
> + * @gadget_ops: gadget interface
> + * @timers: otg timers for state machine
> + * @list: list of otg controllers
> + * @work: otg state machine work
> + * @wq: otg state machine work queue
> + * @fsm_running: state machine running/stopped indicator
> + */
>  struct usb_otg {
>  	u8			default_a;
>  
>  	struct phy		*phy;
>  	/* old usb_phy interface */
>  	struct usb_phy		*usb_phy;
> +
>  	struct usb_bus		*host;
>  	struct usb_gadget	*gadget;
>  
>  	enum usb_otg_state	state;
>  
> +	struct device *dev;
> +	struct usb_otg_caps *caps;
> +	struct otg_fsm fsm;
> +	struct otg_fsm_ops fsm_ops;
> +
> +	/* internal use only */
> +	struct otg_hcd primary_hcd;
> +	struct otg_hcd shared_hcd;
> +	struct otg_gadget_ops *gadget_ops;
> +	struct otg_timer timers[NUM_OTG_FSM_TIMERS];
> +	struct list_head list;
> +	struct work_struct work;
> +	struct workqueue_struct *wq;
> +	bool fsm_running;
> +	/* use otg->fsm.lock for serializing access */
> +
> +/*------------- deprecated interface -----------------------------*/
>  	/* bind/unbind the host controller */
>  	int	(*set_host)(struct usb_otg *otg, struct usb_bus *host);
>  
> @@ -38,7 +119,7 @@ struct usb_otg {
>  
>  	/* start or continue HNP role switch */
>  	int	(*start_hnp)(struct usb_otg *otg);
> -
> +/*---------------------------------------------------------------*/
>  };
>  
>  /**
> @@ -56,8 +137,105 @@ struct usb_otg_caps {
>  	bool adp_support;
>  };
>  
> +/**
> + * struct usb_otg_config - otg controller configuration
> + * @caps: otg capabilities of the controller
> + * @ops: otg fsm operations
> + * @otg_timeouts: override default otg fsm timeouts
> + */
> +struct usb_otg_config {
> +	struct usb_otg_caps otg_caps;
> +	struct otg_fsm_ops *fsm_ops;
> +	unsigned otg_timeouts[NUM_OTG_FSM_TIMERS];
> +};
> +
>  extern const char *usb_otg_state_string(enum usb_otg_state state);
>  
> +enum usb_dr_mode {
> +	USB_DR_MODE_UNKNOWN,
> +	USB_DR_MODE_HOST,
> +	USB_DR_MODE_PERIPHERAL,
> +	USB_DR_MODE_OTG,
> +};
> +
> +#if IS_ENABLED(CONFIG_USB_OTG)
> +struct otg_fsm *usb_otg_register(struct device *dev,
> +				 struct usb_otg_config *config);
> +int usb_otg_unregister(struct device *dev);
> +int usb_otg_register_hcd(struct usb_hcd *hcd, unsigned int irqnum,
> +			 unsigned long irqflags, struct otg_hcd_ops *ops);
> +int usb_otg_unregister_hcd(struct usb_hcd *hcd);
> +int usb_otg_register_gadget(struct usb_gadget *gadget,
> +			    struct otg_gadget_ops *ops);
> +int usb_otg_unregister_gadget(struct usb_gadget *gadget);
> +void usb_otg_sync_inputs(struct otg_fsm *fsm);
> +int usb_otg_kick_fsm(struct device *hcd_gcd_device);
> +struct device *usb_otg_fsm_to_dev(struct otg_fsm *fsm);
> +int usb_otg_start_host(struct otg_fsm *fsm, int on);
> +int usb_otg_start_gadget(struct otg_fsm *fsm, int on);
> +
> +#else /* CONFIG_USB_OTG */
> +
> +static inline struct otg_fsm *usb_otg_register(struct device *dev,
> +					       struct usb_otg_config *config)
> +{
> +	return ERR_PTR(-ENOTSUPP);
> +}
> +
> +static inline int usb_otg_unregister(struct device *dev)
> +{
> +	return -ENOTSUPP;
> +}
> +
> +static inline int usb_otg_register_hcd(struct usb_hcd *hcd, unsigned int irqnum,
> +				       unsigned long irqflags,
> +				       struct otg_hcd_ops *ops)
> +{
> +	return -ENOTSUPP;
> +}
> +
> +static inline int usb_otg_unregister_hcd(struct usb_hcd *hcd)
> +{
> +	return -ENOTSUPP;
> +}
> +
> +static inline int usb_otg_register_gadget(struct usb_gadget *gadget,
> +					  struct otg_gadget_ops *ops)
> +{
> +	return -ENOTSUPP;
> +}
> +
> +static inline int usb_otg_unregister_gadget(struct usb_gadget *gadget)
> +{
> +	return -ENOTSUPP;
> +}
> +
> +static inline void usb_otg_sync_inputs(struct otg_fsm *fsm)
> +{
> +}
> +
> +static inline int usb_otg_kick_fsm(struct device *hcd_gcd_device)
> +{
> +	return -ENOTSUPP;
> +}
> +
> +static inline struct device *usb_otg_fsm_to_dev(struct otg_fsm *fsm)
> +{
> +	return NULL;
> +}
> +
> +static inline int usb_otg_start_host(struct otg_fsm *fsm, int on)
> +{
> +	return -ENOTSUPP;
> +}
> +
> +static inline int usb_otg_start_gadget(struct otg_fsm *fsm, int on)
> +{
> +	return -ENOTSUPP;
> +}
> +#endif /* CONFIG_USB_OTG */
> +
> +/*------------- deprecated interface -----------------------------*/
>  /* Context: can sleep */
>  static inline int
>  otg_start_hnp(struct usb_otg *otg)
> @@ -109,14 +287,9 @@ otg_start_srp(struct usb_otg *otg)
>  	return -ENOTSUPP;
>  }
>  
> +/*---------------------------------------------------------------*/
> +
>  /* for OTG controller drivers (and maybe other stuff) */
>  extern int usb_bus_start_enum(struct usb_bus *bus, unsigned port_num);
>  
> -enum usb_dr_mode {
> -	USB_DR_MODE_UNKNOWN,
> -	USB_DR_MODE_HOST,
> -	USB_DR_MODE_PERIPHERAL,
> -	USB_DR_MODE_OTG,
> -};
> -
>  #endif /* __LINUX_USB_OTG_H */
> -- 
> 2.1.4
> 

-- 

Best Regards,
Peter Chen

^ permalink raw reply	[flat|nested] 48+ messages in thread

* Re: [PATCH v4 07/13] usb: otg: add OTG core
@ 2015-09-07  1:23     ` Peter Chen
  0 siblings, 0 replies; 48+ messages in thread
From: Peter Chen @ 2015-09-07  1:23 UTC (permalink / raw)
  To: Roger Quadros
  Cc: stern, balbi, gregkh, dan.j.williams, jun.li, mathias.nyman, tony,
	Joao.Pinto, abrestic, linux-usb, linux-kernel, linux-omap

On Mon, Aug 24, 2015 at 04:21:18PM +0300, Roger Quadros wrote:
> + * This is used by the USB Host stack to register the Host controller
> + * to the OTG core. Host controller must not be started by the
> + * caller as it is left upto the OTG state machine to do so.
> + *
> + * Returns: 0 on success, error value otherwise.
> + */
> +int usb_otg_register_hcd(struct usb_hcd *hcd, unsigned int irqnum,
> +			 unsigned long irqflags, struct otg_hcd_ops *ops)
> +{
> +	struct usb_otg *otgd;
> +	struct device *hcd_dev = hcd->self.controller;
> +	struct device *otg_dev = usb_otg_get_device(hcd_dev);
> +

One big problem here is: there are two designs for current (IP) driver
code, one creates dedicated hcd device as roothub's parent, like dwc3.
Another one doesn't do this, roothub's parent is core device (or otg device
in your patch), like chipidea and dwc2.

Then, otg_dev will be glue layer device for chipidea after that.

Peter

> +	if (!otg_dev)
> +		return -EINVAL;	/* we're definitely not OTG */
> +
> +	/* we're otg but otg controller might not yet be registered */
> +	mutex_lock(&otg_list_mutex);
> +	otgd = usb_otg_get_data(otg_dev);
> +	mutex_unlock(&otg_list_mutex);
> +	if (!otgd) {
> +		dev_dbg(hcd_dev,
> +			"otg: controller not yet registered. waiting..\n");
> +		/*
> +		 * otg controller might register later. Put the hcd in
> +		 * wait list and call us back when ready
> +		 */
> +		if (usb_otg_hcd_wait_add(otg_dev, hcd, irqnum, irqflags, ops)) {
> +			dev_dbg(hcd_dev, "otg: failed to add to wait list\n");
> +			return -EINVAL;
> +		}
> +
> +		return 0;
> +	}
> +
> +	/* HCD will be started by OTG fsm when needed */
> +	mutex_lock(&otgd->fsm.lock);
> +	if (otgd->primary_hcd.hcd) {
> +		/* probably a shared HCD ? */
> +		if (usb_otg_hcd_is_primary_hcd(hcd)) {
> +			dev_err(otg_dev, "otg: primary host already registered\n");
> +			goto err;
> +		}
> +
> +		if (hcd->shared_hcd == otgd->primary_hcd.hcd) {
> +			if (otgd->shared_hcd.hcd) {
> +				dev_err(otg_dev, "otg: shared host already registered\n");
> +				goto err;
> +			}
> +
> +			otgd->shared_hcd.hcd = hcd;
> +			otgd->shared_hcd.irqnum = irqnum;
> +			otgd->shared_hcd.irqflags = irqflags;
> +			otgd->shared_hcd.ops = ops;
> +			dev_info(otg_dev, "otg: shared host %s registered\n",
> +				 dev_name(hcd->self.controller));
> +		} else {
> +			dev_err(otg_dev, "otg: invalid shared host %s\n",
> +				dev_name(hcd->self.controller));
> +			goto err;
> +		}
> +	} else {
> +		if (!usb_otg_hcd_is_primary_hcd(hcd)) {
> +			dev_err(otg_dev, "otg: primary host must be registered first\n");
> +			goto err;
> +		}
> +
> +		otgd->primary_hcd.hcd = hcd;
> +		otgd->primary_hcd.irqnum = irqnum;
> +		otgd->primary_hcd.irqflags = irqflags;
> +		otgd->primary_hcd.ops = ops;
> +		dev_info(otg_dev, "otg: primary host %s registered\n",
> +			 dev_name(hcd->self.controller));
> +	}
> +
> +	/*
> +	 * we're ready only if we have shared HCD
> +	 * or we don't need shared HCD.
> +	 */
> +	if (otgd->shared_hcd.hcd || !otgd->primary_hcd.hcd->shared_hcd) {
> +		otgd->fsm.otg->host = hcd_to_bus(hcd);
> +		/* FIXME: set bus->otg_port if this is true OTG port with HNP */
> +
> +		/* start FSM */
> +		usb_otg_start_fsm(&otgd->fsm);
> +	} else {
> +		dev_dbg(otg_dev, "otg: can't start till shared host registers\n");
> +	}
> +
> +	mutex_unlock(&otgd->fsm.lock);
> +
> +	return 0;
> +
> +err:
> +	mutex_unlock(&otgd->fsm.lock);
> +	return -EINVAL;
> +}
> +EXPORT_SYMBOL_GPL(usb_otg_register_hcd);
> +
> +/**
> + * usb_otg_unregister_hcd - Unregister Host controller from OTG core
> + * @hcd:	Host controller device
> + *
> + * This is used by the USB Host stack to unregister the Host controller
> + * from the OTG core. Ensures that Host controller is not running
> + * on successful return.
> + *
> + * Returns: 0 on success, error value otherwise.
> + */
> +int usb_otg_unregister_hcd(struct usb_hcd *hcd)
> +{
> +	struct usb_otg *otgd;
> +	struct device *hcd_dev = hcd_to_bus(hcd)->controller;
> +	struct device *otg_dev = usb_otg_get_device(hcd_dev);
> +
> +	if (!otg_dev)
> +		return -EINVAL;	/* we're definitely not OTG */
> +
> +	mutex_lock(&otg_list_mutex);
> +	otgd = usb_otg_get_data(otg_dev);
> +	mutex_unlock(&otg_list_mutex);
> +	if (!otgd) {
> +		/* are we in wait list? */
> +		if (!usb_otg_hcd_wait_remove(hcd))
> +			return 0;
> +
> +		dev_dbg(hcd_dev, "otg: host wasn't registered with otg\n");
> +		return -EINVAL;
> +	}
> +
> +	mutex_lock(&otgd->fsm.lock);
> +	if (hcd == otgd->primary_hcd.hcd) {
> +		otgd->primary_hcd.hcd = NULL;
> +		dev_info(otg_dev, "otg: primary host %s unregistered\n",
> +			 dev_name(hcd_dev));
> +	} else if (hcd == otgd->shared_hcd.hcd) {
> +		otgd->shared_hcd.hcd = NULL;
> +		dev_info(otg_dev, "otg: shared host %s unregistered\n",
> +			 dev_name(hcd_dev));
> +	} else {
> +		dev_err(otg_dev, "otg: host %s wasn't registered with otg\n",
> +			dev_name(hcd_dev));
> +		mutex_unlock(&otgd->fsm.lock);
> +		return -EINVAL;
> +	}
> +
> +	/* stop FSM & Host */
> +	usb_otg_stop_fsm(&otgd->fsm);
> +	otgd->fsm.otg->host = NULL;
> +
> +	mutex_unlock(&otgd->fsm.lock);
> +
> +	return 0;
> +}
> +EXPORT_SYMBOL_GPL(usb_otg_unregister_hcd);
> +
> +/**
> + * usb_otg_register_gadget - Register Gadget controller to OTG core
> + * @gadget:	Gadget controller
> + *
> + * This is used by the USB Gadget stack to register the Gadget controller
> + * to the OTG core. Gadget controller must not be started by the
> + * caller as it is left upto the OTG state machine to do so.
> + *
> + * Gadget core must call this only when all resources required for
> + * gadget controller to run are available.
> + * i.e. gadget function driver is available.
> + *
> + * Returns: 0 on success, error value otherwise.
> + */
> +int usb_otg_register_gadget(struct usb_gadget *gadget,
> +			    struct otg_gadget_ops *ops)
> +{
> +	struct usb_otg *otgd;
> +	struct device *gadget_dev = &gadget->dev;
> +	struct device *otg_dev = usb_otg_get_device(gadget_dev);
> +
> +	if (!otg_dev)
> +		return -EINVAL;	/* we're definitely not OTG */
> +
> +	/* we're otg but otg controller might not yet be registered */
> +	mutex_lock(&otg_list_mutex);
> +	otgd = usb_otg_get_data(otg_dev);
> +	mutex_unlock(&otg_list_mutex);
> +	if (!otgd) {
> +		dev_dbg(gadget_dev,
> +			"otg: controller not yet registered. waiting..\n");
> +		/*
> +		 * otg controller might register later. Put the gadget in
> +		 * wait list and call us back when ready
> +		 */
> +		if (usb_otg_gadget_wait_add(otg_dev, gadget, ops)) {
> +			dev_dbg(gadget_dev, "otg: failed to add to wait list\n");
> +			return -EINVAL;
> +		}
> +
> +		return 0;
> +	}
> +
> +	mutex_lock(&otgd->fsm.lock);
> +	if (otgd->fsm.otg->gadget) {
> +		dev_err(otg_dev, "otg: gadget already registered with otg\n");
> +		mutex_unlock(&otgd->fsm.lock);
> +		return -EINVAL;
> +	}
> +
> +	otgd->fsm.otg->gadget = gadget;
> +	otgd->gadget_ops = ops;
> +	dev_info(otg_dev, "otg: gadget %s registered\n",
> +		 dev_name(&gadget->dev));
> +
> +	/* start FSM */
> +	usb_otg_start_fsm(&otgd->fsm);
> +	mutex_unlock(&otgd->fsm.lock);
> +
> +	return 0;
> +}
> +EXPORT_SYMBOL_GPL(usb_otg_register_gadget);
> +
> +/**
> + * usb_otg_unregister_gadget - Unregister Gadget controller from OTG core
> + * @gadget:	Gadget controller
> + *
> + * This is used by the USB Gadget stack to unregister the Gadget controller
> + * from the OTG core. Ensures that Gadget controller is not running
> + * on successful return.
> + *
> + * Returns: 0 on success, error value otherwise.
> + */
> +int usb_otg_unregister_gadget(struct usb_gadget *gadget)
> +{
> +	struct usb_otg *otgd;
> +	struct device *gadget_dev = &gadget->dev;
> +	struct device *otg_dev = usb_otg_get_device(gadget_dev);
> +
> +	if (!otg_dev)
> +		return -EINVAL;
> +
> +	mutex_lock(&otg_list_mutex);
> +	otgd = usb_otg_get_data(otg_dev);
> +	mutex_unlock(&otg_list_mutex);
> +	if (!otgd) {
> +		/* are we in wait list? */
> +		if (!usb_otg_gadget_wait_remove(gadget))
> +			return 0;
> +
> +		dev_dbg(gadget_dev, "otg: gadget wasn't registered with otg\n");
> +		return -EINVAL;
> +	}
> +
> +	mutex_lock(&otgd->fsm.lock);
> +	if (otgd->fsm.otg->gadget != gadget) {
> +		dev_err(otg_dev, "otg: gadget %s wasn't registered with otg\n",
> +			dev_name(&gadget->dev));
> +		mutex_unlock(&otgd->fsm.lock);
> +		return -EINVAL;
> +	}
> +
> +	/* Stop FSM & gadget */
> +	usb_otg_stop_fsm(&otgd->fsm);
> +	otgd->fsm.otg->gadget = NULL;
> +	mutex_unlock(&otgd->fsm.lock);
> +
> +	dev_info(otg_dev, "otg: gadget %s unregistered\n",
> +		 dev_name(&gadget->dev));
> +
> +	return 0;
> +}
> +EXPORT_SYMBOL_GPL(usb_otg_unregister_gadget);
> +
> +/**
> + * usb_otg_fsm_to_dev - Get OTG controller device from struct otg_fsm
> + * @fsm:	otg_fsm data structure
> + *
> + * This is used by the OTG controller driver to get it's device node
> + * from any of the otg_fsm->ops.
> + */
> +struct device *usb_otg_fsm_to_dev(struct otg_fsm *fsm)
> +{
> +	struct usb_otg *otgd = container_of(fsm, struct usb_otg, fsm);
> +
> +	return otgd->dev;
> +}
> +EXPORT_SYMBOL_GPL(usb_otg_fsm_to_dev);
> diff --git a/drivers/usb/common/usb-otg.h b/drivers/usb/common/usb-otg.h
> new file mode 100644
> index 0000000..05331f0
> --- /dev/null
> +++ b/drivers/usb/common/usb-otg.h
> @@ -0,0 +1,71 @@
> +/**
> + * drivers/usb/common/usb-otg.h - USB OTG core local header
> + *
> + * Copyright (C) 2015 Texas Instruments Incorporated - http://www.ti.com
> + * Author: Roger Quadros <rogerq@ti.com>
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License version 2 as
> + * published by the Free Software Foundation.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + * GNU General Public License for more details.
> + */
> +
> +#ifndef __DRIVERS_USB_COMMON_USB_OTG_H
> +#define __DRIVERS_USB_COMMON_USB_OTG_H
> +
> +/*
> + *  A-DEVICE timing constants
> + */
> +
> +/* Wait for VBUS Rise  */
> +#define TA_WAIT_VRISE        (100)	/* a_wait_vrise: section 7.1.2
> +					 * a_wait_vrise_tmr: section 7.4.5.1
> +					 * TA_VBUS_RISE <= 100ms, section 4.4
> +					 * Table 4-1: Electrical Characteristics
> +					 * ->DC Electrical Timing
> +					 */
> +/* Wait for VBUS Fall  */
> +#define TA_WAIT_VFALL        (1000)	/* a_wait_vfall: section 7.1.7
> +					 * a_wait_vfall_tmr: section: 7.4.5.2
> +					 */
> +/* Wait for B-Connect */
> +#define TA_WAIT_BCON         (10000)	/* a_wait_bcon: section 7.1.3
> +					 * TA_WAIT_BCON: should be between 1100
> +					 * and 30000 ms, section 5.5, Table 5-1
> +					 */
> +/* A-Idle to B-Disconnect */
> +#define TA_AIDL_BDIS         (5000)	/* a_suspend min 200 ms, section 5.2.1
> +					 * TA_AIDL_BDIS: section 5.5, Table 5-1
> +					 */
> +/* B-Idle to A-Disconnect */
> +#define TA_BIDL_ADIS         (500)	/* TA_BIDL_ADIS: section 5.2.1
> +					 * 500ms is used for B switch to host
> +					 * for safe
> +					 */
> +
> +/*
> + * B-device timing constants
> + */
> +
> +/* Data-Line Pulse Time*/
> +#define TB_DATA_PLS          (10)	/* b_srp_init,continue 5~10ms
> +					 * section:5.1.3
> +					 */
> +/* SRP Fail Time  */
> +#define TB_SRP_FAIL          (6000)	/* b_srp_init,fail time 5~6s
> +					 * section:5.1.6
> +					 */
> +/* A-SE0 to B-Reset  */
> +#define TB_ASE0_BRST         (155)	/* minimum 155 ms, section:5.3.1 */
> +/* SE0 Time Before SRP */
> +#define TB_SE0_SRP           (1000)	/* b_idle,minimum 1s, section:5.1.2 */
> +/* SSEND time before SRP */
> +#define TB_SSEND_SRP         (1500)	/* minimum 1.5 sec, section:5.1.2 */
> +
> +#define TB_SESS_VLD          (1000)
> +
> +#endif /* __DRIVERS_USB_COMMON_USB_OTG_H */
> diff --git a/drivers/usb/core/Kconfig b/drivers/usb/core/Kconfig
> index a99c89e..b468a9f 100644
> --- a/drivers/usb/core/Kconfig
> +++ b/drivers/usb/core/Kconfig
> @@ -42,7 +42,7 @@ config USB_DYNAMIC_MINORS
>  	  If you are unsure about this, say N here.
>  
>  config USB_OTG
> -	bool "OTG support"
> +	bool "OTG/Dual-role support"
>  	depends on PM
>  	default n
>  	help
> @@ -75,15 +75,6 @@ config USB_OTG_BLACKLIST_HUB
>  	  and software costs by not supporting external hubs.  So
>  	  are "Embedded Hosts" that don't offer OTG support.
>  
> -config USB_OTG_FSM
> -	tristate "USB 2.0 OTG FSM implementation"
> -	depends on USB
> -	select USB_OTG
> -	select USB_PHY
> -	help
> -	  Implements OTG Finite State Machine as specified in On-The-Go
> -	  and Embedded Host Supplement to the USB Revision 2.0 Specification.
> -
>  config USB_ULPI_BUS
>  	tristate "USB ULPI PHY interface support"
>  	depends on USB_SUPPORT
> diff --git a/include/linux/usb/otg.h b/include/linux/usb/otg.h
> index bd1dcf8..38cabe0 100644
> --- a/include/linux/usb/otg.h
> +++ b/include/linux/usb/otg.h
> @@ -10,19 +10,100 @@
>  #define __LINUX_USB_OTG_H
>  
>  #include <linux/phy/phy.h>
> +#include <linux/device.h>
> +#include <linux/hrtimer.h>
> +#include <linux/ktime.h>
> +#include <linux/usb.h>
> +#include <linux/usb/hcd.h>
> +#include <linux/usb/gadget.h>
> +#include <linux/usb/otg-fsm.h>
>  #include <linux/usb/phy.h>
>  
> +/**
> + * struct otg_hcd - host controller state and interface
> + *
> + * @hcd: host controller
> + * @irqnum: irq number
> + * @irqflags: irq flags
> + * @ops: otg to host controller interface
> + */
> +struct otg_hcd {
> +	struct usb_hcd *hcd;
> +	unsigned int irqnum;
> +	unsigned long irqflags;
> +	struct otg_hcd_ops *ops;
> +};
> +
> +struct usb_otg;
> +
> +/**
> + * struct otg_timer - otg timer data
> + *
> + * @timer: high resolution timer
> + * @timeout: timeout value
> + * @timetout_bit: pointer to variable that is set on timeout
> + * @otgd: usb otg data
> + */
> +struct otg_timer {
> +	struct hrtimer timer;
> +	ktime_t timeout;
> +	/* callback data */
> +	int *timeout_bit;
> +	struct usb_otg *otgd;
> +};
> +
> +/**
> + * struct usb_otg - usb otg controller state
> + *
> + * @default_a: Indicates we are an A device. i.e. Host.
> + * @phy: USB phy interface
> + * @usb_phy: old usb_phy interface
> + * @host: host controller bus
> + * @gadget: gadget device
> + * @state: current otg state
> + * @dev: otg controller device
> + * @caps: otg capabilities revision, hnp, srp, etc
> + * @fsm: otg finite state machine
> + * @fsm_ops: controller hooks for the state machine
> + * ------- internal use only -------
> + * @primary_hcd: primary host state and interface
> + * @shared_hcd: shared host state and interface
> + * @gadget_ops: gadget interface
> + * @timers: otg timers for state machine
> + * @list: list of otg controllers
> + * @work: otg state machine work
> + * @wq: otg state machine work queue
> + * @fsm_running: state machine running/stopped indicator
> + */
>  struct usb_otg {
>  	u8			default_a;
>  
>  	struct phy		*phy;
>  	/* old usb_phy interface */
>  	struct usb_phy		*usb_phy;
> +
>  	struct usb_bus		*host;
>  	struct usb_gadget	*gadget;
>  
>  	enum usb_otg_state	state;
>  
> +	struct device *dev;
> +	struct usb_otg_caps *caps;
> +	struct otg_fsm fsm;
> +	struct otg_fsm_ops fsm_ops;
> +
> +	/* internal use only */
> +	struct otg_hcd primary_hcd;
> +	struct otg_hcd shared_hcd;
> +	struct otg_gadget_ops *gadget_ops;
> +	struct otg_timer timers[NUM_OTG_FSM_TIMERS];
> +	struct list_head list;
> +	struct work_struct work;
> +	struct workqueue_struct *wq;
> +	bool fsm_running;
> +	/* use otg->fsm.lock for serializing access */
> +
> +/*------------- deprecated interface -----------------------------*/
>  	/* bind/unbind the host controller */
>  	int	(*set_host)(struct usb_otg *otg, struct usb_bus *host);
>  
> @@ -38,7 +119,7 @@ struct usb_otg {
>  
>  	/* start or continue HNP role switch */
>  	int	(*start_hnp)(struct usb_otg *otg);
> -
> +/*---------------------------------------------------------------*/
>  };
>  
>  /**
> @@ -56,8 +137,105 @@ struct usb_otg_caps {
>  	bool adp_support;
>  };
>  
> +/**
> + * struct usb_otg_config - otg controller configuration
> + * @caps: otg capabilities of the controller
> + * @ops: otg fsm operations
> + * @otg_timeouts: override default otg fsm timeouts
> + */
> +struct usb_otg_config {
> +	struct usb_otg_caps otg_caps;
> +	struct otg_fsm_ops *fsm_ops;
> +	unsigned otg_timeouts[NUM_OTG_FSM_TIMERS];
> +};
> +
>  extern const char *usb_otg_state_string(enum usb_otg_state state);
>  
> +enum usb_dr_mode {
> +	USB_DR_MODE_UNKNOWN,
> +	USB_DR_MODE_HOST,
> +	USB_DR_MODE_PERIPHERAL,
> +	USB_DR_MODE_OTG,
> +};
> +
> +#if IS_ENABLED(CONFIG_USB_OTG)
> +struct otg_fsm *usb_otg_register(struct device *dev,
> +				 struct usb_otg_config *config);
> +int usb_otg_unregister(struct device *dev);
> +int usb_otg_register_hcd(struct usb_hcd *hcd, unsigned int irqnum,
> +			 unsigned long irqflags, struct otg_hcd_ops *ops);
> +int usb_otg_unregister_hcd(struct usb_hcd *hcd);
> +int usb_otg_register_gadget(struct usb_gadget *gadget,
> +			    struct otg_gadget_ops *ops);
> +int usb_otg_unregister_gadget(struct usb_gadget *gadget);
> +void usb_otg_sync_inputs(struct otg_fsm *fsm);
> +int usb_otg_kick_fsm(struct device *hcd_gcd_device);
> +struct device *usb_otg_fsm_to_dev(struct otg_fsm *fsm);
> +int usb_otg_start_host(struct otg_fsm *fsm, int on);
> +int usb_otg_start_gadget(struct otg_fsm *fsm, int on);
> +
> +#else /* CONFIG_USB_OTG */
> +
> +static inline struct otg_fsm *usb_otg_register(struct device *dev,
> +					       struct usb_otg_config *config)
> +{
> +	return ERR_PTR(-ENOTSUPP);
> +}
> +
> +static inline int usb_otg_unregister(struct device *dev)
> +{
> +	return -ENOTSUPP;
> +}
> +
> +static inline int usb_otg_register_hcd(struct usb_hcd *hcd, unsigned int irqnum,
> +				       unsigned long irqflags,
> +				       struct otg_hcd_ops *ops)
> +{
> +	return -ENOTSUPP;
> +}
> +
> +static inline int usb_otg_unregister_hcd(struct usb_hcd *hcd)
> +{
> +	return -ENOTSUPP;
> +}
> +
> +static inline int usb_otg_register_gadget(struct usb_gadget *gadget,
> +					  struct otg_gadget_ops *ops)
> +{
> +	return -ENOTSUPP;
> +}
> +
> +static inline int usb_otg_unregister_gadget(struct usb_gadget *gadget)
> +{
> +	return -ENOTSUPP;
> +}
> +
> +static inline void usb_otg_sync_inputs(struct otg_fsm *fsm)
> +{
> +}
> +
> +static inline int usb_otg_kick_fsm(struct device *hcd_gcd_device)
> +{
> +	return -ENOTSUPP;
> +}
> +
> +static inline struct device *usb_otg_fsm_to_dev(struct otg_fsm *fsm)
> +{
> +	return NULL;
> +}
> +
> +static inline int usb_otg_start_host(struct otg_fsm *fsm, int on)
> +{
> +	return -ENOTSUPP;
> +}
> +
> +static inline int usb_otg_start_gadget(struct otg_fsm *fsm, int on)
> +{
> +	return -ENOTSUPP;
> +}
> +#endif /* CONFIG_USB_OTG */
> +
> +/*------------- deprecated interface -----------------------------*/
>  /* Context: can sleep */
>  static inline int
>  otg_start_hnp(struct usb_otg *otg)
> @@ -109,14 +287,9 @@ otg_start_srp(struct usb_otg *otg)
>  	return -ENOTSUPP;
>  }
>  
> +/*---------------------------------------------------------------*/
> +
>  /* for OTG controller drivers (and maybe other stuff) */
>  extern int usb_bus_start_enum(struct usb_bus *bus, unsigned port_num);
>  
> -enum usb_dr_mode {
> -	USB_DR_MODE_UNKNOWN,
> -	USB_DR_MODE_HOST,
> -	USB_DR_MODE_PERIPHERAL,
> -	USB_DR_MODE_OTG,
> -};
> -
>  #endif /* __LINUX_USB_OTG_H */
> -- 
> 2.1.4
> 

-- 

Best Regards,
Peter Chen

^ permalink raw reply	[flat|nested] 48+ messages in thread

* Re: [PATCH v4 07/13] usb: otg: add OTG core
  2015-08-24 13:21   ` Roger Quadros
@ 2015-09-07  7:40       ` Li Jun
  -1 siblings, 0 replies; 48+ messages in thread
From: Li Jun @ 2015-09-07  7:40 UTC (permalink / raw)
  To: Roger Quadros
  Cc: stern-nwvwT67g6+6dFdvTe/nMLpVzexx5G7lz, balbi-l0cyMroinI0,
	gregkh-hQyY1W1yCW8ekmWlsbkhG0B+6BGkLq7r,
	peter.chen-KZfg59tc24xl57MIdRCFDg,
	dan.j.williams-ral2JQCrhuEAvxtiuMwx3w,
	jun.li-KZfg59tc24xl57MIdRCFDg,
	mathias.nyman-VuQAYsv1563Yd54FQh9/CA, tony-4v6yS6AI5VpBDgjK7y7TUQ,
	Joao.Pinto-HKixBCOQz3hWk0Htik3J/w,
	abrestic-F7+t8E8rja9g9hUCZPvPmw, linux-usb-u79uwXL29TY76Z2rM5mHXA,
	linux-kernel-u79uwXL29TY76Z2rM5mHXA,
	linux-omap-u79uwXL29TY76Z2rM5mHXA

On Mon, Aug 24, 2015 at 04:21:18PM +0300, Roger Quadros wrote:
> The OTG core instantiates the OTG Finite State Machine
> per OTG controller and manages starting/stopping the
> host and gadget controllers based on the bus state.
> 
> It provides APIs for the following tasks
> 
> - Registering an OTG capable controller
> - Registering Host and Gadget controllers to OTG core
> - Providing inputs to and kicking the OTG state machine
> 
> Signed-off-by: Roger Quadros <rogerq-l0cyMroinI0@public.gmane.org>
> ---
>  MAINTAINERS                  |    4 +-
>  drivers/usb/Kconfig          |    2 +-
>  drivers/usb/Makefile         |    1 +
>  drivers/usb/common/Makefile  |    3 +-
>  drivers/usb/common/usb-otg.c | 1061 ++++++++++++++++++++++++++++++++++++++++++
>  drivers/usb/common/usb-otg.h |   71 +++
>  drivers/usb/core/Kconfig     |   11 +-
>  include/linux/usb/otg.h      |  189 +++++++-
>  8 files changed, 1321 insertions(+), 21 deletions(-)
>  create mode 100644 drivers/usb/common/usb-otg.c
>  create mode 100644 drivers/usb/common/usb-otg.h
> 

... ...

> +
> +/**
> + * Get OTG device from host or gadget device.
> + *
> + * For non device tree boot, the OTG controller is assumed to be
> + * the parent of the host/gadget device.

This assumption/restriction maybe a problem, as I pointed in your previous
version, usb_create_hcd() use the passed dev as its dev, but,
usb_add_gadget_udc() use the passed dev as its parent dev, so often the
host and gadget don't share the same parent device, at least it doesn't
apply to chipidea case.

> + * For device tree boot, the OTG controller is derived from the
> + * "otg-controller" property.
> + */
> +static struct device *usb_otg_get_device(struct device *hcd_gcd_dev)
> +{
> +	struct device *otg_dev;
> +
> +	if (!hcd_gcd_dev)
> +		return NULL;
> +
> +	if (hcd_gcd_dev->of_node) {
> +		struct device_node *np;
> +		struct platform_device *pdev;
> +
> +		np = of_parse_phandle(hcd_gcd_dev->of_node, "otg-controller",
> +				      0);
> +		if (!np)
> +			goto legacy;	/* continue legacy way */
> +
> +		pdev = of_find_device_by_node(np);
> +		of_node_put(np);
> +		if (!pdev) {
> +			dev_err(&pdev->dev, "couldn't get otg-controller device\n");
> +			return NULL;
> +		}
> +
> +		otg_dev = &pdev->dev;
> +		return otg_dev;
> +	}
> +
> +legacy:
> +	/* otg device is parent and must be registered */
> +	otg_dev = hcd_gcd_dev->parent;
> +	if (!usb_otg_get_data(otg_dev))
> +		return NULL;
> +
> +	return otg_dev;
> +}
> +

... ...

> +static void usb_otg_init_timers(struct usb_otg *otgd, unsigned *timeouts)
> +{
> +	struct otg_fsm *fsm = &otgd->fsm;
> +	unsigned long tmouts[NUM_OTG_FSM_TIMERS];
> +	int i;
> +
> +	/* set default timeouts */
> +	tmouts[A_WAIT_VRISE] = TA_WAIT_VRISE;
> +	tmouts[A_WAIT_VFALL] = TA_WAIT_VFALL;
> +	tmouts[A_WAIT_BCON] = TA_WAIT_BCON;
> +	tmouts[A_AIDL_BDIS] = TA_AIDL_BDIS;
> +	tmouts[A_BIDL_ADIS] = TA_BIDL_ADIS;
> +	tmouts[B_ASE0_BRST] = TB_ASE0_BRST;
> +	tmouts[B_SE0_SRP] = TB_SE0_SRP;
> +	tmouts[B_SRP_FAIL] = TB_SRP_FAIL;
> +
> +	/* set controller provided timeouts */
> +	if (timeouts) {
> +		for (i = 0; i < NUM_OTG_FSM_TIMERS; i++) {
> +			if (timeouts[i])
> +				tmouts[i] = timeouts[i];
> +		}
> +	}
> +
> +	otg_timer_init(A_WAIT_VRISE, otgd, set_tmout, TA_WAIT_VRISE,
> +		       &fsm->a_wait_vrise_tmout);
> +	otg_timer_init(A_WAIT_VFALL, otgd, set_tmout, TA_WAIT_VFALL,
> +		       &fsm->a_wait_vfall_tmout);
> +	otg_timer_init(A_WAIT_BCON, otgd, set_tmout, TA_WAIT_BCON,
> +		       &fsm->a_wait_bcon_tmout);
> +	otg_timer_init(A_AIDL_BDIS, otgd, set_tmout, TA_AIDL_BDIS,
> +		       &fsm->a_aidl_bdis_tmout);
> +	otg_timer_init(A_BIDL_ADIS, otgd, set_tmout, TA_BIDL_ADIS,
> +		       &fsm->a_bidl_adis_tmout);
> +	otg_timer_init(B_ASE0_BRST, otgd, set_tmout, TB_ASE0_BRST,
> +		       &fsm->b_ase0_brst_tmout);
> +
> +	otg_timer_init(B_SE0_SRP, otgd, set_tmout, TB_SE0_SRP,
> +		       &fsm->b_se0_srp);
> +	otg_timer_init(B_SRP_FAIL, otgd, set_tmout, TB_SRP_FAIL,
> +		       &fsm->b_srp_done);
> +
> +	/* FIXME: what about A_WAIT_ENUM? */

Either you init it as other timers, or you remove all of it, otherwise
there will be NULL pointer crash.

> +}
> +
> +/**
> + * OTG FSM ops function to add timer
> + */
> +static void usb_otg_add_timer(struct otg_fsm *fsm, enum otg_fsm_timer id)
> +{
> +	struct usb_otg *otgd = container_of(fsm, struct usb_otg, fsm);
> +	struct otg_timer *otgtimer = &otgd->timers[id];
> +	struct hrtimer *timer = &otgtimer->timer;
> +
> +	if (!otgd->fsm_running)
> +		return;
> +
> +	/* if timer is already active, exit */
> +	if (hrtimer_active(timer)) {
> +		dev_err(otgd->dev, "otg: timer %d is already running\n", id);
> +		return;
> +	}
> +
> +	hrtimer_start(timer, otgtimer->timeout, HRTIMER_MODE_REL);
> +}
> +
> +/**
> + * OTG FSM ops function to delete timer
> + */
> +static void usb_otg_del_timer(struct otg_fsm *fsm, enum otg_fsm_timer id)
> +{
> +	struct usb_otg *otgd = container_of(fsm, struct usb_otg, fsm);
> +	struct hrtimer *timer = &otgd->timers[id].timer;
> +
> +	hrtimer_cancel(timer);
> +}
> +
> +/**
> + * Helper function to start/stop otg host. For use by otg controller.
> + */
> +int usb_otg_start_host(struct otg_fsm *fsm, int on)
> +{
> +	struct usb_otg *otgd = container_of(fsm, struct usb_otg, fsm);
> +	struct otg_hcd_ops *hcd_ops;
> +
> +	dev_dbg(otgd->dev, "otg: %s %d\n", __func__, on);
> +	if (!fsm->otg->host) {
> +		WARN_ONCE(1, "otg: fsm running without host\n");
> +		return 0;
> +	}
> +
> +	if (on) {
> +		/* start host */
> +		hcd_ops = otgd->primary_hcd.ops;
> +		hcd_ops->add(otgd->primary_hcd.hcd, otgd->primary_hcd.irqnum,
> +			     otgd->primary_hcd.irqflags);
> +		if (otgd->shared_hcd.hcd) {
> +			hcd_ops = otgd->shared_hcd.ops;
> +			hcd_ops->add(otgd->shared_hcd.hcd,
> +				     otgd->shared_hcd.irqnum,
> +				     otgd->shared_hcd.irqflags);
> +		}
> +	} else {
> +		/* stop host */
> +		if (otgd->shared_hcd.hcd) {
> +			hcd_ops = otgd->shared_hcd.ops;
> +			hcd_ops->remove(otgd->shared_hcd.hcd);
> +		}
> +		hcd_ops = otgd->primary_hcd.ops;
> +		hcd_ops->remove(otgd->primary_hcd.hcd);
> +	}
> +
> +	return 0;
> +}
> +EXPORT_SYMBOL_GPL(usb_otg_start_host);
> +
> +/**
> + * Helper function to start/stop otg gadget. For use by otg controller.
> + */
> +int usb_otg_start_gadget(struct otg_fsm *fsm, int on)
> +{
> +	struct usb_otg *otgd = container_of(fsm, struct usb_otg, fsm);
> +	struct usb_gadget *gadget = fsm->otg->gadget;
> +
> +	dev_dbg(otgd->dev, "otg: %s %d\n", __func__, on);
> +	if (!gadget) {
> +		WARN_ONCE(1, "otg: fsm running without gadget\n");
> +		return 0;
> +	}
> +
> +	if (on)
> +		otgd->gadget_ops->start(fsm->otg->gadget);
> +	else
> +		otgd->gadget_ops->stop(fsm->otg->gadget);
> +
> +	return 0;
> +}
> +EXPORT_SYMBOL_GPL(usb_otg_start_gadget);
> +
> +/**
> + * OTG FSM work function
> + */
> +static void usb_otg_work(struct work_struct *work)
> +{
> +	struct usb_otg *otgd = container_of(work, struct usb_otg, work);
> +

Add runtime pm for it
	pm_runtime_get_sync(otgd->dev);
> +	otg_statemachine(&otgd->fsm);
	pm_runtime_put_sync(otgd->dev);

Also as I raised in your previous version, we need successive state machine
transition, how about:
	if (otg_statemachine(&otgd->fsm))
		usb_otg_sync_inputs(&otgd->fsm);
> +}
> +
> +/**
> + * usb_otg_register() - Register the OTG device to OTG core
> + * @dev: OTG controller device.
> + * @config: OTG configuration.
> + *
> + * Register the OTG controller device with the USB OTG core.
> + * The associated Host and Gadget controllers will be prevented from
> + * being started till both are available for use.
> + *
> + * For non device tree boots, the OTG controller device must be the
> + * parent node of the Host and Gadget controllers.
> + *
> + * For device tree case, the otg-controller property must be present
> + * in the Host and Gadget controller node and it must point to the
> + * same OTG controller node.
> + *
> + * Return: struct otg_fsm * if success, NULL if error.
> + */
> +struct otg_fsm *usb_otg_register(struct device *dev,
> +				 struct usb_otg_config *config)

Why not return usb_otg? Since you create usb_otg which contains all stuff
for otg.

> +{
> +	struct usb_otg *otgd;
> +	struct otg_wait_data *wait;
> +	int ret = 0;
> +
> +	if (!dev || !config || !config->fsm_ops)
> +		return ERR_PTR(-EINVAL);
> +
> +	/* already in list? */
> +	mutex_lock(&otg_list_mutex);
> +	if (usb_otg_get_data(dev)) {
> +		dev_err(dev, "otg: %s: device already in otg list\n",
> +			__func__);
> +		ret = -EINVAL;
> +		goto unlock;
> +	}
> +
> +	/* allocate and add to list */
> +	otgd = kzalloc(sizeof(*otgd), GFP_KERNEL);
> +	if (!otgd) {
> +		ret = -ENOMEM;
> +		goto unlock;
> +	}
> +
> +	otgd->dev = dev;
> +	otgd->caps = &config->otg_caps;

How about define otgd->caps as a pointer, then don't need copy it.

> +	INIT_WORK(&otgd->work, usb_otg_work);
> +	otgd->wq = create_singlethread_workqueue("usb_otg");
> +	if (!otgd->wq) {
> +		dev_err(dev, "otg: %s: can't create workqueue\n",
> +			__func__);
> +		ret = -ENOMEM;
> +		goto err_wq;
> +	}
> +
> +	usb_otg_init_timers(otgd, config->otg_timeouts);
> +
> +	/* create copy of original ops */
> +	otgd->fsm_ops = *config->fsm_ops;

The same, use a pointer is enough?

> +	/* FIXME: we ignore caller's timer ops */
> +	otgd->fsm_ops.add_timer = usb_otg_add_timer;
> +	otgd->fsm_ops.del_timer = usb_otg_del_timer;
> +	/* set otg ops */
> +	otgd->fsm.ops = &otgd->fsm_ops;
> +	otgd->fsm.otg = otgd;
> +
> +	mutex_init(&otgd->fsm.lock);
> +
> +	list_add_tail(&otgd->list, &otg_list);
> +	mutex_unlock(&otg_list_mutex);
> +
> +	/* were we in wait list? */
> +	mutex_lock(&wait_list_mutex);
> +	wait = usb_otg_get_wait(dev);
> +	mutex_unlock(&wait_list_mutex);
> +	if (wait) {
> +		/* register pending host/gadget and flush from list */
> +		usb_otg_flush_wait(dev);
> +	}
> +
> +	return &otgd->fsm;
> +
> +err_wq:
> +	kfree(otgd);
> +unlock:
> +	mutex_unlock(&otg_list_mutex);
> +	return ERR_PTR(ret);
> +}
> +EXPORT_SYMBOL_GPL(usb_otg_register);
> +
> +/**
> + * usb_otg_unregister() - Unregister the OTG device from USB OTG core
> + * @dev: OTG controller device.
> + *
> + * Unregister OTG controller device from USB OTG core.
> + * Prevents unregistering till both the associated Host and Gadget controllers
> + * have unregistered from the OTG core.
> + *
> + * Return: 0 on success, error value otherwise.
> + */
> +int usb_otg_unregister(struct device *dev)
> +{
> +	struct usb_otg *otgd;
> +
> +	mutex_lock(&otg_list_mutex);
> +	otgd = usb_otg_get_data(dev);
> +	if (!otgd) {
> +		dev_err(dev, "otg: %s: device not in otg list\n",
> +			__func__);
> +		mutex_unlock(&otg_list_mutex);
> +		return -EINVAL;
> +	}
> +
> +	/* prevent unregister till both host & gadget have unregistered */
> +	if (otgd->fsm.otg->host || otgd->fsm.otg->gadget) {
> +		dev_err(dev, "otg: %s: host/gadget still registered\n",
> +			__func__);
> +		return -EBUSY;
> +	}
> +
> +	/* OTG FSM is halted when host/gadget unregistered */
> +	destroy_workqueue(otgd->wq);
> +
> +	/* remove from otg list */
> +	list_del(&otgd->list);
> +	kfree(otgd);
> +	mutex_unlock(&otg_list_mutex);
> +
> +	return 0;
> +}
> +EXPORT_SYMBOL_GPL(usb_otg_unregister);
> +
> +/**
> + * start/kick the OTG FSM if we can
> + * fsm->lock must be held
> + */
> +static void usb_otg_start_fsm(struct otg_fsm *fsm)
> +{
> +	struct usb_otg *otgd = container_of(fsm, struct usb_otg, fsm);
> +
> +	if (otgd->fsm_running)
> +		goto kick_fsm;
> +
> +	if (!fsm->otg->host) {
> +		dev_info(otgd->dev, "otg: can't start till host registers\n");
> +		return;

So we need register hcd(usb_otg_register_hcd) before start fsm, right?

> +	}
> +
> +	if (!fsm->otg->gadget) {
> +		dev_info(otgd->dev, "otg: can't start till gadget registers\n");
> +		return;
> +	}
> +
> +	otgd->fsm_running = true;
> +kick_fsm:
> +	queue_work(otgd->wq, &otgd->work);
> +}
> +
> +/**
> + * stop the OTG FSM. Stops Host & Gadget controllers as well.
> + * fsm->lock must be held
> + */
> +static void usb_otg_stop_fsm(struct otg_fsm *fsm)
> +{
> +	struct usb_otg *otgd = container_of(fsm, struct usb_otg, fsm);
> +	int i;
> +
> +	if (!otgd->fsm_running)
> +		return;
> +
> +	/* no more new events queued */
> +	otgd->fsm_running = false;
> +
> +	/* Stop state machine / timers */
> +	for (i = 0; i < ARRAY_SIZE(otgd->timers); i++)
> +		hrtimer_cancel(&otgd->timers[i].timer);
> +
> +	flush_workqueue(otgd->wq);
> +	fsm->otg->state = OTG_STATE_UNDEFINED;
> +
> +	/* stop host/gadget immediately */
> +	if (fsm->protocol == PROTO_HOST)
> +		otg_start_host(fsm, 0);
> +	else if (fsm->protocol == PROTO_GADGET)
> +		otg_start_gadget(fsm, 0);
> +	fsm->protocol = PROTO_UNDEF;
> +}
> +
> +/**
> + * usb_otg_sync_inputs - Sync OTG inputs with the OTG state machine
> + * @fsm:	OTG FSM instance
> + *
> + * Used by the OTG driver to update the inputs to the OTG
> + * state machine.
> + *
> + * Can be called in IRQ context.
> + */
> +void usb_otg_sync_inputs(struct otg_fsm *fsm)
> +{
> +	struct usb_otg *otgd = container_of(fsm, struct usb_otg, fsm);
> +
> +	/* Don't kick FSM till it has started */
> +	if (!otgd->fsm_running)
> +		return;
> +
> +	/* Kick FSM */
> +	queue_work(otgd->wq, &otgd->work);
> +}
> +EXPORT_SYMBOL_GPL(usb_otg_sync_inputs);
> +
> +/**
> + * usb_otg_kick_fsm - Kick the OTG state machine
> + * @hcd_gcd_device:	Host/Gadget controller device
> + *
> + * Used by USB host/device stack to sync OTG related
> + * events to the OTG state machine.
> + * e.g. change in host_bus->b_hnp_enable, gadget->b_hnp_enable
> + *
> + * Returns: 0 on success, error value otherwise.
> + */
> +int usb_otg_kick_fsm(struct device *hcd_gcd_device)
> +{
> +	struct usb_otg *otgd;
> +
> +	mutex_lock(&otg_list_mutex);
> +	otgd = usb_otg_get_data(usb_otg_get_device(hcd_gcd_device));
> +	mutex_unlock(&otg_list_mutex);
> +	if (!otgd) {
> +		dev_dbg(hcd_gcd_device, "otg: %s: invalid host/gadget device\n",
> +			__func__);
> +		return -ENODEV;
> +	}
> +
> +	usb_otg_sync_inputs(&otgd->fsm);
> +
> +	return 0;
> +}
> +EXPORT_SYMBOL_GPL(usb_otg_kick_fsm);
> +
> +/**
> + * usb_otg_register_hcd - Register Host controller to OTG core
> + * @hcd:	Host controller device
> + * @irqnum:	interrupt number
> + * @irqflags:	interrupt flags
> + * @ops:	HCD ops to add/remove the HCD
> + *
> + * This is used by the USB Host stack to register the Host controller
> + * to the OTG core. Host controller must not be started by the
> + * caller as it is left upto the OTG state machine to do so.

I am confused on how to use this function.
- This function should be called before start fsm per usb_otg_start_fsm().
- Called by usb_add_hcd(), so we need call usb_add_hcd() before start fsm.
- If I want to add hcd when switch to host role, and remove hcd when switch
  to peripheral, with this design, I cannot use this function?
- How about split it out of usb_add_hcd()?

> + *
> + * Returns: 0 on success, error value otherwise.
> + */
> +int usb_otg_register_hcd(struct usb_hcd *hcd, unsigned int irqnum,
> +			 unsigned long irqflags, struct otg_hcd_ops *ops)
> +{
> +	struct usb_otg *otgd;
> +	struct device *hcd_dev = hcd->self.controller;
> +	struct device *otg_dev = usb_otg_get_device(hcd_dev);
> +
> +	if (!otg_dev)
> +		return -EINVAL;	/* we're definitely not OTG */
> +
> +	/* we're otg but otg controller might not yet be registered */
> +	mutex_lock(&otg_list_mutex);
> +	otgd = usb_otg_get_data(otg_dev);
> +	mutex_unlock(&otg_list_mutex);
> +	if (!otgd) {
> +		dev_dbg(hcd_dev,
> +			"otg: controller not yet registered. waiting..\n");
> +		/*
> +		 * otg controller might register later. Put the hcd in
> +		 * wait list and call us back when ready
> +		 */
> +		if (usb_otg_hcd_wait_add(otg_dev, hcd, irqnum, irqflags, ops)) {
> +			dev_dbg(hcd_dev, "otg: failed to add to wait list\n");
> +			return -EINVAL;
> +		}
> +
> +		return 0;
> +	}
> +
> +	/* HCD will be started by OTG fsm when needed */
> +	mutex_lock(&otgd->fsm.lock);

If call usb_add_hcd() when start host role, deadlock.

> +	if (otgd->primary_hcd.hcd) {
> +		/* probably a shared HCD ? */
> +		if (usb_otg_hcd_is_primary_hcd(hcd)) {
> +			dev_err(otg_dev, "otg: primary host already registered\n");
> +			goto err;
> +		}
> +
> +		if (hcd->shared_hcd == otgd->primary_hcd.hcd) {
> +			if (otgd->shared_hcd.hcd) {
> +				dev_err(otg_dev, "otg: shared host already registered\n");
> +				goto err;
> +			}
> +
> +			otgd->shared_hcd.hcd = hcd;
> +			otgd->shared_hcd.irqnum = irqnum;
> +			otgd->shared_hcd.irqflags = irqflags;
> +			otgd->shared_hcd.ops = ops;
> +			dev_info(otg_dev, "otg: shared host %s registered\n",
> +				 dev_name(hcd->self.controller));
> +		} else {
> +			dev_err(otg_dev, "otg: invalid shared host %s\n",
> +				dev_name(hcd->self.controller));
> +			goto err;
> +		}
> +	} else {
> +		if (!usb_otg_hcd_is_primary_hcd(hcd)) {
> +			dev_err(otg_dev, "otg: primary host must be registered first\n");
> +			goto err;
> +		}
> +
> +		otgd->primary_hcd.hcd = hcd;
> +		otgd->primary_hcd.irqnum = irqnum;
> +		otgd->primary_hcd.irqflags = irqflags;
> +		otgd->primary_hcd.ops = ops;
> +		dev_info(otg_dev, "otg: primary host %s registered\n",
> +			 dev_name(hcd->self.controller));
> +	}
> +
> +	/*
> +	 * we're ready only if we have shared HCD
> +	 * or we don't need shared HCD.
> +	 */
> +	if (otgd->shared_hcd.hcd || !otgd->primary_hcd.hcd->shared_hcd) {
> +		otgd->fsm.otg->host = hcd_to_bus(hcd);

otgd->host = hcd_to_bus(hcd);

> +		/* FIXME: set bus->otg_port if this is true OTG port with HNP */
> +
> +		/* start FSM */
> +		usb_otg_start_fsm(&otgd->fsm);
> +	} else {
> +		dev_dbg(otg_dev, "otg: can't start till shared host registers\n");
> +	}
> +
> +	mutex_unlock(&otgd->fsm.lock);
> +
> +	return 0;
> +
> +err:
> +	mutex_unlock(&otgd->fsm.lock);
> +	return -EINVAL;

Return non-zero, then if err, do we need call usb_otg_add_hcd() after
usb_otg_register_hcd() fails?

> +}
> +EXPORT_SYMBOL_GPL(usb_otg_register_hcd);
> +
> +/**
> + * usb_otg_unregister_hcd - Unregister Host controller from OTG core
> + * @hcd:	Host controller device
> + *
> + * This is used by the USB Host stack to unregister the Host controller
> + * from the OTG core. Ensures that Host controller is not running
> + * on successful return.
> + *
> + * Returns: 0 on success, error value otherwise.
> + */
> +int usb_otg_unregister_hcd(struct usb_hcd *hcd)
> +{
> +	struct usb_otg *otgd;
> +	struct device *hcd_dev = hcd_to_bus(hcd)->controller;
> +	struct device *otg_dev = usb_otg_get_device(hcd_dev);
> +
> +	if (!otg_dev)
> +		return -EINVAL;	/* we're definitely not OTG */
> +
> +	mutex_lock(&otg_list_mutex);
> +	otgd = usb_otg_get_data(otg_dev);
> +	mutex_unlock(&otg_list_mutex);
> +	if (!otgd) {
> +		/* are we in wait list? */
> +		if (!usb_otg_hcd_wait_remove(hcd))
> +			return 0;
> +
> +		dev_dbg(hcd_dev, "otg: host wasn't registered with otg\n");
> +		return -EINVAL;
> +	}
> +
> +	mutex_lock(&otgd->fsm.lock);
> +	if (hcd == otgd->primary_hcd.hcd) {
> +		otgd->primary_hcd.hcd = NULL;
> +		dev_info(otg_dev, "otg: primary host %s unregistered\n",
> +			 dev_name(hcd_dev));
> +	} else if (hcd == otgd->shared_hcd.hcd) {
> +		otgd->shared_hcd.hcd = NULL;
> +		dev_info(otg_dev, "otg: shared host %s unregistered\n",
> +			 dev_name(hcd_dev));
> +	} else {
> +		dev_err(otg_dev, "otg: host %s wasn't registered with otg\n",
> +			dev_name(hcd_dev));
> +		mutex_unlock(&otgd->fsm.lock);
> +		return -EINVAL;
> +	}
> +
> +	/* stop FSM & Host */
> +	usb_otg_stop_fsm(&otgd->fsm);
> +	otgd->fsm.otg->host = NULL;
> +
> +	mutex_unlock(&otgd->fsm.lock);
> +
> +	return 0;
> +}
> +EXPORT_SYMBOL_GPL(usb_otg_unregister_hcd);
> +
> +/**
> + * usb_otg_register_gadget - Register Gadget controller to OTG core
> + * @gadget:	Gadget controller
> + *
> + * This is used by the USB Gadget stack to register the Gadget controller
> + * to the OTG core. Gadget controller must not be started by the
> + * caller as it is left upto the OTG state machine to do so.
> + *
> + * Gadget core must call this only when all resources required for
> + * gadget controller to run are available.
> + * i.e. gadget function driver is available.
> + *
> + * Returns: 0 on success, error value otherwise.
> + */
> +int usb_otg_register_gadget(struct usb_gadget *gadget,
> +			    struct otg_gadget_ops *ops)
> +{
> +	struct usb_otg *otgd;
> +	struct device *gadget_dev = &gadget->dev;
> +	struct device *otg_dev = usb_otg_get_device(gadget_dev);
> +
> +	if (!otg_dev)
> +		return -EINVAL;	/* we're definitely not OTG */
> +
> +	/* we're otg but otg controller might not yet be registered */
> +	mutex_lock(&otg_list_mutex);
> +	otgd = usb_otg_get_data(otg_dev);
> +	mutex_unlock(&otg_list_mutex);
> +	if (!otgd) {
> +		dev_dbg(gadget_dev,
> +			"otg: controller not yet registered. waiting..\n");
> +		/*
> +		 * otg controller might register later. Put the gadget in
> +		 * wait list and call us back when ready
> +		 */
> +		if (usb_otg_gadget_wait_add(otg_dev, gadget, ops)) {
> +			dev_dbg(gadget_dev, "otg: failed to add to wait list\n");
> +			return -EINVAL;
> +		}
> +
> +		return 0;
> +	}
> +
> +	mutex_lock(&otgd->fsm.lock);
> +	if (otgd->fsm.otg->gadget) {
> +		dev_err(otg_dev, "otg: gadget already registered with otg\n");
> +		mutex_unlock(&otgd->fsm.lock);
> +		return -EINVAL;
> +	}
> +
> +	otgd->fsm.otg->gadget = gadget;
> +	otgd->gadget_ops = ops;
> +	dev_info(otg_dev, "otg: gadget %s registered\n",
> +		 dev_name(&gadget->dev));
> +
> +	/* start FSM */
> +	usb_otg_start_fsm(&otgd->fsm);
> +	mutex_unlock(&otgd->fsm.lock);
> +
> +	return 0;
> +}
> +EXPORT_SYMBOL_GPL(usb_otg_register_gadget);
> +
> +/**
> + * usb_otg_unregister_gadget - Unregister Gadget controller from OTG core
> + * @gadget:	Gadget controller
> + *
> + * This is used by the USB Gadget stack to unregister the Gadget controller
> + * from the OTG core. Ensures that Gadget controller is not running
> + * on successful return.
> + *
> + * Returns: 0 on success, error value otherwise.
> + */
> +int usb_otg_unregister_gadget(struct usb_gadget *gadget)
> +{
> +	struct usb_otg *otgd;
> +	struct device *gadget_dev = &gadget->dev;
> +	struct device *otg_dev = usb_otg_get_device(gadget_dev);
> +
> +	if (!otg_dev)
> +		return -EINVAL;
> +
> +	mutex_lock(&otg_list_mutex);
> +	otgd = usb_otg_get_data(otg_dev);
> +	mutex_unlock(&otg_list_mutex);
> +	if (!otgd) {
> +		/* are we in wait list? */
> +		if (!usb_otg_gadget_wait_remove(gadget))
> +			return 0;
> +
> +		dev_dbg(gadget_dev, "otg: gadget wasn't registered with otg\n");
> +		return -EINVAL;
> +	}
> +
> +	mutex_lock(&otgd->fsm.lock);
> +	if (otgd->fsm.otg->gadget != gadget) {
> +		dev_err(otg_dev, "otg: gadget %s wasn't registered with otg\n",
> +			dev_name(&gadget->dev));
> +		mutex_unlock(&otgd->fsm.lock);
> +		return -EINVAL;
> +	}
> +
> +	/* Stop FSM & gadget */
> +	usb_otg_stop_fsm(&otgd->fsm);
> +	otgd->fsm.otg->gadget = NULL;
> +	mutex_unlock(&otgd->fsm.lock);
> +
> +	dev_info(otg_dev, "otg: gadget %s unregistered\n",
> +		 dev_name(&gadget->dev));
> +
> +	return 0;
> +}
> +EXPORT_SYMBOL_GPL(usb_otg_unregister_gadget);
> +
> +/**
> + * usb_otg_fsm_to_dev - Get OTG controller device from struct otg_fsm
> + * @fsm:	otg_fsm data structure
> + *
> + * This is used by the OTG controller driver to get it's device node
> + * from any of the otg_fsm->ops.
> + */
> +struct device *usb_otg_fsm_to_dev(struct otg_fsm *fsm)
> +{
> +	struct usb_otg *otgd = container_of(fsm, struct usb_otg, fsm);
> +
> +	return otgd->dev;
> +}
> +EXPORT_SYMBOL_GPL(usb_otg_fsm_to_dev);
> diff --git a/drivers/usb/common/usb-otg.h b/drivers/usb/common/usb-otg.h
> new file mode 100644
> index 0000000..05331f0
> --- /dev/null
> +++ b/drivers/usb/common/usb-otg.h
> @@ -0,0 +1,71 @@
> +/**
> + * drivers/usb/common/usb-otg.h - USB OTG core local header
> + *
> + * Copyright (C) 2015 Texas Instruments Incorporated - http://www.ti.com
> + * Author: Roger Quadros <rogerq-l0cyMroinI0@public.gmane.org>
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License version 2 as
> + * published by the Free Software Foundation.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + * GNU General Public License for more details.
> + */
> +
> +#ifndef __DRIVERS_USB_COMMON_USB_OTG_H
> +#define __DRIVERS_USB_COMMON_USB_OTG_H
> +
> +/*
> + *  A-DEVICE timing constants
> + */
> +
> +/* Wait for VBUS Rise  */
> +#define TA_WAIT_VRISE        (100)	/* a_wait_vrise: section 7.1.2
> +					 * a_wait_vrise_tmr: section 7.4.5.1
> +					 * TA_VBUS_RISE <= 100ms, section 4.4
> +					 * Table 4-1: Electrical Characteristics
> +					 * ->DC Electrical Timing
> +					 */
> +/* Wait for VBUS Fall  */
> +#define TA_WAIT_VFALL        (1000)	/* a_wait_vfall: section 7.1.7
> +					 * a_wait_vfall_tmr: section: 7.4.5.2
> +					 */
> +/* Wait for B-Connect */
> +#define TA_WAIT_BCON         (10000)	/* a_wait_bcon: section 7.1.3
> +					 * TA_WAIT_BCON: should be between 1100
> +					 * and 30000 ms, section 5.5, Table 5-1
> +					 */
> +/* A-Idle to B-Disconnect */
> +#define TA_AIDL_BDIS         (5000)	/* a_suspend min 200 ms, section 5.2.1
> +					 * TA_AIDL_BDIS: section 5.5, Table 5-1
> +					 */
> +/* B-Idle to A-Disconnect */
> +#define TA_BIDL_ADIS         (500)	/* TA_BIDL_ADIS: section 5.2.1
> +					 * 500ms is used for B switch to host
> +					 * for safe
> +					 */
> +
> +/*
> + * B-device timing constants
> + */
> +
> +/* Data-Line Pulse Time*/
> +#define TB_DATA_PLS          (10)	/* b_srp_init,continue 5~10ms
> +					 * section:5.1.3
> +					 */
> +/* SRP Fail Time  */
> +#define TB_SRP_FAIL          (6000)	/* b_srp_init,fail time 5~6s
> +					 * section:5.1.6
> +					 */
> +/* A-SE0 to B-Reset  */
> +#define TB_ASE0_BRST         (155)	/* minimum 155 ms, section:5.3.1 */
> +/* SE0 Time Before SRP */
> +#define TB_SE0_SRP           (1000)	/* b_idle,minimum 1s, section:5.1.2 */
> +/* SSEND time before SRP */
> +#define TB_SSEND_SRP         (1500)	/* minimum 1.5 sec, section:5.1.2 */
> +
> +#define TB_SESS_VLD          (1000)
> +
> +#endif /* __DRIVERS_USB_COMMON_USB_OTG_H */
> diff --git a/drivers/usb/core/Kconfig b/drivers/usb/core/Kconfig
> index a99c89e..b468a9f 100644
> --- a/drivers/usb/core/Kconfig
> +++ b/drivers/usb/core/Kconfig
> @@ -42,7 +42,7 @@ config USB_DYNAMIC_MINORS
>  	  If you are unsure about this, say N here.
>  
>  config USB_OTG
> -	bool "OTG support"
> +	bool "OTG/Dual-role support"
>  	depends on PM
>  	default n
>  	help
> @@ -75,15 +75,6 @@ config USB_OTG_BLACKLIST_HUB
>  	  and software costs by not supporting external hubs.  So
>  	  are "Embedded Hosts" that don't offer OTG support.
>  
> -config USB_OTG_FSM
> -	tristate "USB 2.0 OTG FSM implementation"
> -	depends on USB
> -	select USB_OTG
> -	select USB_PHY
> -	help
> -	  Implements OTG Finite State Machine as specified in On-The-Go
> -	  and Embedded Host Supplement to the USB Revision 2.0 Specification.
> -
>  config USB_ULPI_BUS
>  	tristate "USB ULPI PHY interface support"
>  	depends on USB_SUPPORT
> diff --git a/include/linux/usb/otg.h b/include/linux/usb/otg.h
> index bd1dcf8..38cabe0 100644
> --- a/include/linux/usb/otg.h
> +++ b/include/linux/usb/otg.h
> @@ -10,19 +10,100 @@
>  #define __LINUX_USB_OTG_H
>  
>  #include <linux/phy/phy.h>
> +#include <linux/device.h>
> +#include <linux/hrtimer.h>
> +#include <linux/ktime.h>
> +#include <linux/usb.h>
> +#include <linux/usb/hcd.h>
> +#include <linux/usb/gadget.h>
> +#include <linux/usb/otg-fsm.h>
>  #include <linux/usb/phy.h>
>  
> +/**
> + * struct otg_hcd - host controller state and interface
> + *
> + * @hcd: host controller
> + * @irqnum: irq number
> + * @irqflags: irq flags
> + * @ops: otg to host controller interface
> + */
> +struct otg_hcd {
> +	struct usb_hcd *hcd;
> +	unsigned int irqnum;
> +	unsigned long irqflags;
> +	struct otg_hcd_ops *ops;
> +};
> +
> +struct usb_otg;
> +
> +/**
> + * struct otg_timer - otg timer data
> + *
> + * @timer: high resolution timer
> + * @timeout: timeout value
> + * @timetout_bit: pointer to variable that is set on timeout
> + * @otgd: usb otg data
> + */
> +struct otg_timer {
> +	struct hrtimer timer;
> +	ktime_t timeout;
> +	/* callback data */
> +	int *timeout_bit;
> +	struct usb_otg *otgd;
> +};
> +
> +/**
> + * struct usb_otg - usb otg controller state
> + *
> + * @default_a: Indicates we are an A device. i.e. Host.
> + * @phy: USB phy interface
> + * @usb_phy: old usb_phy interface
> + * @host: host controller bus
> + * @gadget: gadget device
> + * @state: current otg state
> + * @dev: otg controller device
> + * @caps: otg capabilities revision, hnp, srp, etc
> + * @fsm: otg finite state machine
> + * @fsm_ops: controller hooks for the state machine
> + * ------- internal use only -------
> + * @primary_hcd: primary host state and interface
> + * @shared_hcd: shared host state and interface
> + * @gadget_ops: gadget interface
> + * @timers: otg timers for state machine
> + * @list: list of otg controllers
> + * @work: otg state machine work
> + * @wq: otg state machine work queue
> + * @fsm_running: state machine running/stopped indicator
> + */
>  struct usb_otg {
>  	u8			default_a;
>  
>  	struct phy		*phy;
>  	/* old usb_phy interface */
>  	struct usb_phy		*usb_phy;
> +

add a blank line?

>  	struct usb_bus		*host;
>  	struct usb_gadget	*gadget;
>  
>  	enum usb_otg_state	state;
>  
> +	struct device *dev;
> +	struct usb_otg_caps *caps;
> +	struct otg_fsm fsm;
> +	struct otg_fsm_ops fsm_ops;
> +
> +	/* internal use only */
> +	struct otg_hcd primary_hcd;
> +	struct otg_hcd shared_hcd;
> +	struct otg_gadget_ops *gadget_ops;
> +	struct otg_timer timers[NUM_OTG_FSM_TIMERS];
> +	struct list_head list;
> +	struct work_struct work;
> +	struct workqueue_struct *wq;
> +	bool fsm_running;

Should fsm_running be added in otg_fsm struct?

> +	/* use otg->fsm.lock for serializing access */
> +
> +/*------------- deprecated interface -----------------------------*/
>  	/* bind/unbind the host controller */
>  	int	(*set_host)(struct usb_otg *otg, struct usb_bus *host);
>  
> @@ -38,7 +119,7 @@ struct usb_otg {
>  
>  	/* start or continue HNP role switch */
>  	int	(*start_hnp)(struct usb_otg *otg);
> -
> +/*---------------------------------------------------------------*/
>  };
>  
>  /**
> @@ -56,8 +137,105 @@ struct usb_otg_caps {
>  	bool adp_support;
>  };
>  
> +/**
> + * struct usb_otg_config - otg controller configuration
> + * @caps: otg capabilities of the controller
> + * @ops: otg fsm operations
> + * @otg_timeouts: override default otg fsm timeouts
> + */
> +struct usb_otg_config {
> +	struct usb_otg_caps otg_caps;
> +	struct otg_fsm_ops *fsm_ops;
> +	unsigned otg_timeouts[NUM_OTG_FSM_TIMERS];
> +};
> +
>  extern const char *usb_otg_state_string(enum usb_otg_state state);
>  
> +enum usb_dr_mode {
> +	USB_DR_MODE_UNKNOWN,
> +	USB_DR_MODE_HOST,
> +	USB_DR_MODE_PERIPHERAL,
> +	USB_DR_MODE_OTG,
> +};
> +
> +#if IS_ENABLED(CONFIG_USB_OTG)
> +struct otg_fsm *usb_otg_register(struct device *dev,
> +				 struct usb_otg_config *config);
> +int usb_otg_unregister(struct device *dev);
> +int usb_otg_register_hcd(struct usb_hcd *hcd, unsigned int irqnum,
> +			 unsigned long irqflags, struct otg_hcd_ops *ops);
> +int usb_otg_unregister_hcd(struct usb_hcd *hcd);
> +int usb_otg_register_gadget(struct usb_gadget *gadget,
> +			    struct otg_gadget_ops *ops);
> +int usb_otg_unregister_gadget(struct usb_gadget *gadget);
> +void usb_otg_sync_inputs(struct otg_fsm *fsm);
> +int usb_otg_kick_fsm(struct device *hcd_gcd_device);
> +struct device *usb_otg_fsm_to_dev(struct otg_fsm *fsm);
> +int usb_otg_start_host(struct otg_fsm *fsm, int on);
> +int usb_otg_start_gadget(struct otg_fsm *fsm, int on);
> +
> +#else /* CONFIG_USB_OTG */
> +
> +static inline struct otg_fsm *usb_otg_register(struct device *dev,
> +					       struct usb_otg_config *config)
> +{
> +	return ERR_PTR(-ENOTSUPP);
> +}
> +
> +static inline int usb_otg_unregister(struct device *dev)
> +{
> +	return -ENOTSUPP;
> +}
> +
> +static inline int usb_otg_register_hcd(struct usb_hcd *hcd, unsigned int irqnum,
> +				       unsigned long irqflags,
> +				       struct otg_hcd_ops *ops)
> +{
> +	return -ENOTSUPP;
> +}
> +
> +static inline int usb_otg_unregister_hcd(struct usb_hcd *hcd)
> +{
> +	return -ENOTSUPP;
> +}
> +
> +static inline int usb_otg_register_gadget(struct usb_gadget *gadget,
> +					  struct otg_gadget_ops *ops)
> +{
> +	return -ENOTSUPP;
> +}
> +
> +static inline int usb_otg_unregister_gadget(struct usb_gadget *gadget)
> +{
> +	return -ENOTSUPP;
> +}
> +
> +static inline void usb_otg_sync_inputs(struct otg_fsm *fsm)
> +{
> +}
> +
> +static inline int usb_otg_kick_fsm(struct device *hcd_gcd_device)
> +{
> +	return -ENOTSUPP;
> +}
> +
> +static inline struct device *usb_otg_fsm_to_dev(struct otg_fsm *fsm)
> +{
> +	return NULL;
> +}
> +
> +static inline int usb_otg_start_host(struct otg_fsm *fsm, int on)
> +{
> +	return -ENOTSUPP;
> +}
> +
> +static inline int usb_otg_start_gadget(struct otg_fsm *fsm, int on)
> +{
> +	return -ENOTSUPP;
> +}
> +#endif /* CONFIG_USB_OTG */
> +
> +/*------------- deprecated interface -----------------------------*/
>  /* Context: can sleep */
>  static inline int
>  otg_start_hnp(struct usb_otg *otg)
> @@ -109,14 +287,9 @@ otg_start_srp(struct usb_otg *otg)
>  	return -ENOTSUPP;
>  }
>  
> +/*---------------------------------------------------------------*/
> +
>  /* for OTG controller drivers (and maybe other stuff) */
>  extern int usb_bus_start_enum(struct usb_bus *bus, unsigned port_num);
>  
> -enum usb_dr_mode {
> -	USB_DR_MODE_UNKNOWN,
> -	USB_DR_MODE_HOST,
> -	USB_DR_MODE_PERIPHERAL,
> -	USB_DR_MODE_OTG,
> -};
> -
>  #endif /* __LINUX_USB_OTG_H */
> -- 
> 2.1.4
> 
--
To unsubscribe from this list: send the line "unsubscribe linux-usb" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply	[flat|nested] 48+ messages in thread

* Re: [PATCH v4 07/13] usb: otg: add OTG core
@ 2015-09-07  7:40       ` Li Jun
  0 siblings, 0 replies; 48+ messages in thread
From: Li Jun @ 2015-09-07  7:40 UTC (permalink / raw)
  To: Roger Quadros
  Cc: stern, balbi, gregkh, peter.chen, dan.j.williams, jun.li,
	mathias.nyman, tony, Joao.Pinto, abrestic, linux-usb,
	linux-kernel, linux-omap

On Mon, Aug 24, 2015 at 04:21:18PM +0300, Roger Quadros wrote:
> The OTG core instantiates the OTG Finite State Machine
> per OTG controller and manages starting/stopping the
> host and gadget controllers based on the bus state.
> 
> It provides APIs for the following tasks
> 
> - Registering an OTG capable controller
> - Registering Host and Gadget controllers to OTG core
> - Providing inputs to and kicking the OTG state machine
> 
> Signed-off-by: Roger Quadros <rogerq@ti.com>
> ---
>  MAINTAINERS                  |    4 +-
>  drivers/usb/Kconfig          |    2 +-
>  drivers/usb/Makefile         |    1 +
>  drivers/usb/common/Makefile  |    3 +-
>  drivers/usb/common/usb-otg.c | 1061 ++++++++++++++++++++++++++++++++++++++++++
>  drivers/usb/common/usb-otg.h |   71 +++
>  drivers/usb/core/Kconfig     |   11 +-
>  include/linux/usb/otg.h      |  189 +++++++-
>  8 files changed, 1321 insertions(+), 21 deletions(-)
>  create mode 100644 drivers/usb/common/usb-otg.c
>  create mode 100644 drivers/usb/common/usb-otg.h
> 

... ...

> +
> +/**
> + * Get OTG device from host or gadget device.
> + *
> + * For non device tree boot, the OTG controller is assumed to be
> + * the parent of the host/gadget device.

This assumption/restriction maybe a problem, as I pointed in your previous
version, usb_create_hcd() use the passed dev as its dev, but,
usb_add_gadget_udc() use the passed dev as its parent dev, so often the
host and gadget don't share the same parent device, at least it doesn't
apply to chipidea case.

> + * For device tree boot, the OTG controller is derived from the
> + * "otg-controller" property.
> + */
> +static struct device *usb_otg_get_device(struct device *hcd_gcd_dev)
> +{
> +	struct device *otg_dev;
> +
> +	if (!hcd_gcd_dev)
> +		return NULL;
> +
> +	if (hcd_gcd_dev->of_node) {
> +		struct device_node *np;
> +		struct platform_device *pdev;
> +
> +		np = of_parse_phandle(hcd_gcd_dev->of_node, "otg-controller",
> +				      0);
> +		if (!np)
> +			goto legacy;	/* continue legacy way */
> +
> +		pdev = of_find_device_by_node(np);
> +		of_node_put(np);
> +		if (!pdev) {
> +			dev_err(&pdev->dev, "couldn't get otg-controller device\n");
> +			return NULL;
> +		}
> +
> +		otg_dev = &pdev->dev;
> +		return otg_dev;
> +	}
> +
> +legacy:
> +	/* otg device is parent and must be registered */
> +	otg_dev = hcd_gcd_dev->parent;
> +	if (!usb_otg_get_data(otg_dev))
> +		return NULL;
> +
> +	return otg_dev;
> +}
> +

... ...

> +static void usb_otg_init_timers(struct usb_otg *otgd, unsigned *timeouts)
> +{
> +	struct otg_fsm *fsm = &otgd->fsm;
> +	unsigned long tmouts[NUM_OTG_FSM_TIMERS];
> +	int i;
> +
> +	/* set default timeouts */
> +	tmouts[A_WAIT_VRISE] = TA_WAIT_VRISE;
> +	tmouts[A_WAIT_VFALL] = TA_WAIT_VFALL;
> +	tmouts[A_WAIT_BCON] = TA_WAIT_BCON;
> +	tmouts[A_AIDL_BDIS] = TA_AIDL_BDIS;
> +	tmouts[A_BIDL_ADIS] = TA_BIDL_ADIS;
> +	tmouts[B_ASE0_BRST] = TB_ASE0_BRST;
> +	tmouts[B_SE0_SRP] = TB_SE0_SRP;
> +	tmouts[B_SRP_FAIL] = TB_SRP_FAIL;
> +
> +	/* set controller provided timeouts */
> +	if (timeouts) {
> +		for (i = 0; i < NUM_OTG_FSM_TIMERS; i++) {
> +			if (timeouts[i])
> +				tmouts[i] = timeouts[i];
> +		}
> +	}
> +
> +	otg_timer_init(A_WAIT_VRISE, otgd, set_tmout, TA_WAIT_VRISE,
> +		       &fsm->a_wait_vrise_tmout);
> +	otg_timer_init(A_WAIT_VFALL, otgd, set_tmout, TA_WAIT_VFALL,
> +		       &fsm->a_wait_vfall_tmout);
> +	otg_timer_init(A_WAIT_BCON, otgd, set_tmout, TA_WAIT_BCON,
> +		       &fsm->a_wait_bcon_tmout);
> +	otg_timer_init(A_AIDL_BDIS, otgd, set_tmout, TA_AIDL_BDIS,
> +		       &fsm->a_aidl_bdis_tmout);
> +	otg_timer_init(A_BIDL_ADIS, otgd, set_tmout, TA_BIDL_ADIS,
> +		       &fsm->a_bidl_adis_tmout);
> +	otg_timer_init(B_ASE0_BRST, otgd, set_tmout, TB_ASE0_BRST,
> +		       &fsm->b_ase0_brst_tmout);
> +
> +	otg_timer_init(B_SE0_SRP, otgd, set_tmout, TB_SE0_SRP,
> +		       &fsm->b_se0_srp);
> +	otg_timer_init(B_SRP_FAIL, otgd, set_tmout, TB_SRP_FAIL,
> +		       &fsm->b_srp_done);
> +
> +	/* FIXME: what about A_WAIT_ENUM? */

Either you init it as other timers, or you remove all of it, otherwise
there will be NULL pointer crash.

> +}
> +
> +/**
> + * OTG FSM ops function to add timer
> + */
> +static void usb_otg_add_timer(struct otg_fsm *fsm, enum otg_fsm_timer id)
> +{
> +	struct usb_otg *otgd = container_of(fsm, struct usb_otg, fsm);
> +	struct otg_timer *otgtimer = &otgd->timers[id];
> +	struct hrtimer *timer = &otgtimer->timer;
> +
> +	if (!otgd->fsm_running)
> +		return;
> +
> +	/* if timer is already active, exit */
> +	if (hrtimer_active(timer)) {
> +		dev_err(otgd->dev, "otg: timer %d is already running\n", id);
> +		return;
> +	}
> +
> +	hrtimer_start(timer, otgtimer->timeout, HRTIMER_MODE_REL);
> +}
> +
> +/**
> + * OTG FSM ops function to delete timer
> + */
> +static void usb_otg_del_timer(struct otg_fsm *fsm, enum otg_fsm_timer id)
> +{
> +	struct usb_otg *otgd = container_of(fsm, struct usb_otg, fsm);
> +	struct hrtimer *timer = &otgd->timers[id].timer;
> +
> +	hrtimer_cancel(timer);
> +}
> +
> +/**
> + * Helper function to start/stop otg host. For use by otg controller.
> + */
> +int usb_otg_start_host(struct otg_fsm *fsm, int on)
> +{
> +	struct usb_otg *otgd = container_of(fsm, struct usb_otg, fsm);
> +	struct otg_hcd_ops *hcd_ops;
> +
> +	dev_dbg(otgd->dev, "otg: %s %d\n", __func__, on);
> +	if (!fsm->otg->host) {
> +		WARN_ONCE(1, "otg: fsm running without host\n");
> +		return 0;
> +	}
> +
> +	if (on) {
> +		/* start host */
> +		hcd_ops = otgd->primary_hcd.ops;
> +		hcd_ops->add(otgd->primary_hcd.hcd, otgd->primary_hcd.irqnum,
> +			     otgd->primary_hcd.irqflags);
> +		if (otgd->shared_hcd.hcd) {
> +			hcd_ops = otgd->shared_hcd.ops;
> +			hcd_ops->add(otgd->shared_hcd.hcd,
> +				     otgd->shared_hcd.irqnum,
> +				     otgd->shared_hcd.irqflags);
> +		}
> +	} else {
> +		/* stop host */
> +		if (otgd->shared_hcd.hcd) {
> +			hcd_ops = otgd->shared_hcd.ops;
> +			hcd_ops->remove(otgd->shared_hcd.hcd);
> +		}
> +		hcd_ops = otgd->primary_hcd.ops;
> +		hcd_ops->remove(otgd->primary_hcd.hcd);
> +	}
> +
> +	return 0;
> +}
> +EXPORT_SYMBOL_GPL(usb_otg_start_host);
> +
> +/**
> + * Helper function to start/stop otg gadget. For use by otg controller.
> + */
> +int usb_otg_start_gadget(struct otg_fsm *fsm, int on)
> +{
> +	struct usb_otg *otgd = container_of(fsm, struct usb_otg, fsm);
> +	struct usb_gadget *gadget = fsm->otg->gadget;
> +
> +	dev_dbg(otgd->dev, "otg: %s %d\n", __func__, on);
> +	if (!gadget) {
> +		WARN_ONCE(1, "otg: fsm running without gadget\n");
> +		return 0;
> +	}
> +
> +	if (on)
> +		otgd->gadget_ops->start(fsm->otg->gadget);
> +	else
> +		otgd->gadget_ops->stop(fsm->otg->gadget);
> +
> +	return 0;
> +}
> +EXPORT_SYMBOL_GPL(usb_otg_start_gadget);
> +
> +/**
> + * OTG FSM work function
> + */
> +static void usb_otg_work(struct work_struct *work)
> +{
> +	struct usb_otg *otgd = container_of(work, struct usb_otg, work);
> +

Add runtime pm for it
	pm_runtime_get_sync(otgd->dev);
> +	otg_statemachine(&otgd->fsm);
	pm_runtime_put_sync(otgd->dev);

Also as I raised in your previous version, we need successive state machine
transition, how about:
	if (otg_statemachine(&otgd->fsm))
		usb_otg_sync_inputs(&otgd->fsm);
> +}
> +
> +/**
> + * usb_otg_register() - Register the OTG device to OTG core
> + * @dev: OTG controller device.
> + * @config: OTG configuration.
> + *
> + * Register the OTG controller device with the USB OTG core.
> + * The associated Host and Gadget controllers will be prevented from
> + * being started till both are available for use.
> + *
> + * For non device tree boots, the OTG controller device must be the
> + * parent node of the Host and Gadget controllers.
> + *
> + * For device tree case, the otg-controller property must be present
> + * in the Host and Gadget controller node and it must point to the
> + * same OTG controller node.
> + *
> + * Return: struct otg_fsm * if success, NULL if error.
> + */
> +struct otg_fsm *usb_otg_register(struct device *dev,
> +				 struct usb_otg_config *config)

Why not return usb_otg? Since you create usb_otg which contains all stuff
for otg.

> +{
> +	struct usb_otg *otgd;
> +	struct otg_wait_data *wait;
> +	int ret = 0;
> +
> +	if (!dev || !config || !config->fsm_ops)
> +		return ERR_PTR(-EINVAL);
> +
> +	/* already in list? */
> +	mutex_lock(&otg_list_mutex);
> +	if (usb_otg_get_data(dev)) {
> +		dev_err(dev, "otg: %s: device already in otg list\n",
> +			__func__);
> +		ret = -EINVAL;
> +		goto unlock;
> +	}
> +
> +	/* allocate and add to list */
> +	otgd = kzalloc(sizeof(*otgd), GFP_KERNEL);
> +	if (!otgd) {
> +		ret = -ENOMEM;
> +		goto unlock;
> +	}
> +
> +	otgd->dev = dev;
> +	otgd->caps = &config->otg_caps;

How about define otgd->caps as a pointer, then don't need copy it.

> +	INIT_WORK(&otgd->work, usb_otg_work);
> +	otgd->wq = create_singlethread_workqueue("usb_otg");
> +	if (!otgd->wq) {
> +		dev_err(dev, "otg: %s: can't create workqueue\n",
> +			__func__);
> +		ret = -ENOMEM;
> +		goto err_wq;
> +	}
> +
> +	usb_otg_init_timers(otgd, config->otg_timeouts);
> +
> +	/* create copy of original ops */
> +	otgd->fsm_ops = *config->fsm_ops;

The same, use a pointer is enough?

> +	/* FIXME: we ignore caller's timer ops */
> +	otgd->fsm_ops.add_timer = usb_otg_add_timer;
> +	otgd->fsm_ops.del_timer = usb_otg_del_timer;
> +	/* set otg ops */
> +	otgd->fsm.ops = &otgd->fsm_ops;
> +	otgd->fsm.otg = otgd;
> +
> +	mutex_init(&otgd->fsm.lock);
> +
> +	list_add_tail(&otgd->list, &otg_list);
> +	mutex_unlock(&otg_list_mutex);
> +
> +	/* were we in wait list? */
> +	mutex_lock(&wait_list_mutex);
> +	wait = usb_otg_get_wait(dev);
> +	mutex_unlock(&wait_list_mutex);
> +	if (wait) {
> +		/* register pending host/gadget and flush from list */
> +		usb_otg_flush_wait(dev);
> +	}
> +
> +	return &otgd->fsm;
> +
> +err_wq:
> +	kfree(otgd);
> +unlock:
> +	mutex_unlock(&otg_list_mutex);
> +	return ERR_PTR(ret);
> +}
> +EXPORT_SYMBOL_GPL(usb_otg_register);
> +
> +/**
> + * usb_otg_unregister() - Unregister the OTG device from USB OTG core
> + * @dev: OTG controller device.
> + *
> + * Unregister OTG controller device from USB OTG core.
> + * Prevents unregistering till both the associated Host and Gadget controllers
> + * have unregistered from the OTG core.
> + *
> + * Return: 0 on success, error value otherwise.
> + */
> +int usb_otg_unregister(struct device *dev)
> +{
> +	struct usb_otg *otgd;
> +
> +	mutex_lock(&otg_list_mutex);
> +	otgd = usb_otg_get_data(dev);
> +	if (!otgd) {
> +		dev_err(dev, "otg: %s: device not in otg list\n",
> +			__func__);
> +		mutex_unlock(&otg_list_mutex);
> +		return -EINVAL;
> +	}
> +
> +	/* prevent unregister till both host & gadget have unregistered */
> +	if (otgd->fsm.otg->host || otgd->fsm.otg->gadget) {
> +		dev_err(dev, "otg: %s: host/gadget still registered\n",
> +			__func__);
> +		return -EBUSY;
> +	}
> +
> +	/* OTG FSM is halted when host/gadget unregistered */
> +	destroy_workqueue(otgd->wq);
> +
> +	/* remove from otg list */
> +	list_del(&otgd->list);
> +	kfree(otgd);
> +	mutex_unlock(&otg_list_mutex);
> +
> +	return 0;
> +}
> +EXPORT_SYMBOL_GPL(usb_otg_unregister);
> +
> +/**
> + * start/kick the OTG FSM if we can
> + * fsm->lock must be held
> + */
> +static void usb_otg_start_fsm(struct otg_fsm *fsm)
> +{
> +	struct usb_otg *otgd = container_of(fsm, struct usb_otg, fsm);
> +
> +	if (otgd->fsm_running)
> +		goto kick_fsm;
> +
> +	if (!fsm->otg->host) {
> +		dev_info(otgd->dev, "otg: can't start till host registers\n");
> +		return;

So we need register hcd(usb_otg_register_hcd) before start fsm, right?

> +	}
> +
> +	if (!fsm->otg->gadget) {
> +		dev_info(otgd->dev, "otg: can't start till gadget registers\n");
> +		return;
> +	}
> +
> +	otgd->fsm_running = true;
> +kick_fsm:
> +	queue_work(otgd->wq, &otgd->work);
> +}
> +
> +/**
> + * stop the OTG FSM. Stops Host & Gadget controllers as well.
> + * fsm->lock must be held
> + */
> +static void usb_otg_stop_fsm(struct otg_fsm *fsm)
> +{
> +	struct usb_otg *otgd = container_of(fsm, struct usb_otg, fsm);
> +	int i;
> +
> +	if (!otgd->fsm_running)
> +		return;
> +
> +	/* no more new events queued */
> +	otgd->fsm_running = false;
> +
> +	/* Stop state machine / timers */
> +	for (i = 0; i < ARRAY_SIZE(otgd->timers); i++)
> +		hrtimer_cancel(&otgd->timers[i].timer);
> +
> +	flush_workqueue(otgd->wq);
> +	fsm->otg->state = OTG_STATE_UNDEFINED;
> +
> +	/* stop host/gadget immediately */
> +	if (fsm->protocol == PROTO_HOST)
> +		otg_start_host(fsm, 0);
> +	else if (fsm->protocol == PROTO_GADGET)
> +		otg_start_gadget(fsm, 0);
> +	fsm->protocol = PROTO_UNDEF;
> +}
> +
> +/**
> + * usb_otg_sync_inputs - Sync OTG inputs with the OTG state machine
> + * @fsm:	OTG FSM instance
> + *
> + * Used by the OTG driver to update the inputs to the OTG
> + * state machine.
> + *
> + * Can be called in IRQ context.
> + */
> +void usb_otg_sync_inputs(struct otg_fsm *fsm)
> +{
> +	struct usb_otg *otgd = container_of(fsm, struct usb_otg, fsm);
> +
> +	/* Don't kick FSM till it has started */
> +	if (!otgd->fsm_running)
> +		return;
> +
> +	/* Kick FSM */
> +	queue_work(otgd->wq, &otgd->work);
> +}
> +EXPORT_SYMBOL_GPL(usb_otg_sync_inputs);
> +
> +/**
> + * usb_otg_kick_fsm - Kick the OTG state machine
> + * @hcd_gcd_device:	Host/Gadget controller device
> + *
> + * Used by USB host/device stack to sync OTG related
> + * events to the OTG state machine.
> + * e.g. change in host_bus->b_hnp_enable, gadget->b_hnp_enable
> + *
> + * Returns: 0 on success, error value otherwise.
> + */
> +int usb_otg_kick_fsm(struct device *hcd_gcd_device)
> +{
> +	struct usb_otg *otgd;
> +
> +	mutex_lock(&otg_list_mutex);
> +	otgd = usb_otg_get_data(usb_otg_get_device(hcd_gcd_device));
> +	mutex_unlock(&otg_list_mutex);
> +	if (!otgd) {
> +		dev_dbg(hcd_gcd_device, "otg: %s: invalid host/gadget device\n",
> +			__func__);
> +		return -ENODEV;
> +	}
> +
> +	usb_otg_sync_inputs(&otgd->fsm);
> +
> +	return 0;
> +}
> +EXPORT_SYMBOL_GPL(usb_otg_kick_fsm);
> +
> +/**
> + * usb_otg_register_hcd - Register Host controller to OTG core
> + * @hcd:	Host controller device
> + * @irqnum:	interrupt number
> + * @irqflags:	interrupt flags
> + * @ops:	HCD ops to add/remove the HCD
> + *
> + * This is used by the USB Host stack to register the Host controller
> + * to the OTG core. Host controller must not be started by the
> + * caller as it is left upto the OTG state machine to do so.

I am confused on how to use this function.
- This function should be called before start fsm per usb_otg_start_fsm().
- Called by usb_add_hcd(), so we need call usb_add_hcd() before start fsm.
- If I want to add hcd when switch to host role, and remove hcd when switch
  to peripheral, with this design, I cannot use this function?
- How about split it out of usb_add_hcd()?

> + *
> + * Returns: 0 on success, error value otherwise.
> + */
> +int usb_otg_register_hcd(struct usb_hcd *hcd, unsigned int irqnum,
> +			 unsigned long irqflags, struct otg_hcd_ops *ops)
> +{
> +	struct usb_otg *otgd;
> +	struct device *hcd_dev = hcd->self.controller;
> +	struct device *otg_dev = usb_otg_get_device(hcd_dev);
> +
> +	if (!otg_dev)
> +		return -EINVAL;	/* we're definitely not OTG */
> +
> +	/* we're otg but otg controller might not yet be registered */
> +	mutex_lock(&otg_list_mutex);
> +	otgd = usb_otg_get_data(otg_dev);
> +	mutex_unlock(&otg_list_mutex);
> +	if (!otgd) {
> +		dev_dbg(hcd_dev,
> +			"otg: controller not yet registered. waiting..\n");
> +		/*
> +		 * otg controller might register later. Put the hcd in
> +		 * wait list and call us back when ready
> +		 */
> +		if (usb_otg_hcd_wait_add(otg_dev, hcd, irqnum, irqflags, ops)) {
> +			dev_dbg(hcd_dev, "otg: failed to add to wait list\n");
> +			return -EINVAL;
> +		}
> +
> +		return 0;
> +	}
> +
> +	/* HCD will be started by OTG fsm when needed */
> +	mutex_lock(&otgd->fsm.lock);

If call usb_add_hcd() when start host role, deadlock.

> +	if (otgd->primary_hcd.hcd) {
> +		/* probably a shared HCD ? */
> +		if (usb_otg_hcd_is_primary_hcd(hcd)) {
> +			dev_err(otg_dev, "otg: primary host already registered\n");
> +			goto err;
> +		}
> +
> +		if (hcd->shared_hcd == otgd->primary_hcd.hcd) {
> +			if (otgd->shared_hcd.hcd) {
> +				dev_err(otg_dev, "otg: shared host already registered\n");
> +				goto err;
> +			}
> +
> +			otgd->shared_hcd.hcd = hcd;
> +			otgd->shared_hcd.irqnum = irqnum;
> +			otgd->shared_hcd.irqflags = irqflags;
> +			otgd->shared_hcd.ops = ops;
> +			dev_info(otg_dev, "otg: shared host %s registered\n",
> +				 dev_name(hcd->self.controller));
> +		} else {
> +			dev_err(otg_dev, "otg: invalid shared host %s\n",
> +				dev_name(hcd->self.controller));
> +			goto err;
> +		}
> +	} else {
> +		if (!usb_otg_hcd_is_primary_hcd(hcd)) {
> +			dev_err(otg_dev, "otg: primary host must be registered first\n");
> +			goto err;
> +		}
> +
> +		otgd->primary_hcd.hcd = hcd;
> +		otgd->primary_hcd.irqnum = irqnum;
> +		otgd->primary_hcd.irqflags = irqflags;
> +		otgd->primary_hcd.ops = ops;
> +		dev_info(otg_dev, "otg: primary host %s registered\n",
> +			 dev_name(hcd->self.controller));
> +	}
> +
> +	/*
> +	 * we're ready only if we have shared HCD
> +	 * or we don't need shared HCD.
> +	 */
> +	if (otgd->shared_hcd.hcd || !otgd->primary_hcd.hcd->shared_hcd) {
> +		otgd->fsm.otg->host = hcd_to_bus(hcd);

otgd->host = hcd_to_bus(hcd);

> +		/* FIXME: set bus->otg_port if this is true OTG port with HNP */
> +
> +		/* start FSM */
> +		usb_otg_start_fsm(&otgd->fsm);
> +	} else {
> +		dev_dbg(otg_dev, "otg: can't start till shared host registers\n");
> +	}
> +
> +	mutex_unlock(&otgd->fsm.lock);
> +
> +	return 0;
> +
> +err:
> +	mutex_unlock(&otgd->fsm.lock);
> +	return -EINVAL;

Return non-zero, then if err, do we need call usb_otg_add_hcd() after
usb_otg_register_hcd() fails?

> +}
> +EXPORT_SYMBOL_GPL(usb_otg_register_hcd);
> +
> +/**
> + * usb_otg_unregister_hcd - Unregister Host controller from OTG core
> + * @hcd:	Host controller device
> + *
> + * This is used by the USB Host stack to unregister the Host controller
> + * from the OTG core. Ensures that Host controller is not running
> + * on successful return.
> + *
> + * Returns: 0 on success, error value otherwise.
> + */
> +int usb_otg_unregister_hcd(struct usb_hcd *hcd)
> +{
> +	struct usb_otg *otgd;
> +	struct device *hcd_dev = hcd_to_bus(hcd)->controller;
> +	struct device *otg_dev = usb_otg_get_device(hcd_dev);
> +
> +	if (!otg_dev)
> +		return -EINVAL;	/* we're definitely not OTG */
> +
> +	mutex_lock(&otg_list_mutex);
> +	otgd = usb_otg_get_data(otg_dev);
> +	mutex_unlock(&otg_list_mutex);
> +	if (!otgd) {
> +		/* are we in wait list? */
> +		if (!usb_otg_hcd_wait_remove(hcd))
> +			return 0;
> +
> +		dev_dbg(hcd_dev, "otg: host wasn't registered with otg\n");
> +		return -EINVAL;
> +	}
> +
> +	mutex_lock(&otgd->fsm.lock);
> +	if (hcd == otgd->primary_hcd.hcd) {
> +		otgd->primary_hcd.hcd = NULL;
> +		dev_info(otg_dev, "otg: primary host %s unregistered\n",
> +			 dev_name(hcd_dev));
> +	} else if (hcd == otgd->shared_hcd.hcd) {
> +		otgd->shared_hcd.hcd = NULL;
> +		dev_info(otg_dev, "otg: shared host %s unregistered\n",
> +			 dev_name(hcd_dev));
> +	} else {
> +		dev_err(otg_dev, "otg: host %s wasn't registered with otg\n",
> +			dev_name(hcd_dev));
> +		mutex_unlock(&otgd->fsm.lock);
> +		return -EINVAL;
> +	}
> +
> +	/* stop FSM & Host */
> +	usb_otg_stop_fsm(&otgd->fsm);
> +	otgd->fsm.otg->host = NULL;
> +
> +	mutex_unlock(&otgd->fsm.lock);
> +
> +	return 0;
> +}
> +EXPORT_SYMBOL_GPL(usb_otg_unregister_hcd);
> +
> +/**
> + * usb_otg_register_gadget - Register Gadget controller to OTG core
> + * @gadget:	Gadget controller
> + *
> + * This is used by the USB Gadget stack to register the Gadget controller
> + * to the OTG core. Gadget controller must not be started by the
> + * caller as it is left upto the OTG state machine to do so.
> + *
> + * Gadget core must call this only when all resources required for
> + * gadget controller to run are available.
> + * i.e. gadget function driver is available.
> + *
> + * Returns: 0 on success, error value otherwise.
> + */
> +int usb_otg_register_gadget(struct usb_gadget *gadget,
> +			    struct otg_gadget_ops *ops)
> +{
> +	struct usb_otg *otgd;
> +	struct device *gadget_dev = &gadget->dev;
> +	struct device *otg_dev = usb_otg_get_device(gadget_dev);
> +
> +	if (!otg_dev)
> +		return -EINVAL;	/* we're definitely not OTG */
> +
> +	/* we're otg but otg controller might not yet be registered */
> +	mutex_lock(&otg_list_mutex);
> +	otgd = usb_otg_get_data(otg_dev);
> +	mutex_unlock(&otg_list_mutex);
> +	if (!otgd) {
> +		dev_dbg(gadget_dev,
> +			"otg: controller not yet registered. waiting..\n");
> +		/*
> +		 * otg controller might register later. Put the gadget in
> +		 * wait list and call us back when ready
> +		 */
> +		if (usb_otg_gadget_wait_add(otg_dev, gadget, ops)) {
> +			dev_dbg(gadget_dev, "otg: failed to add to wait list\n");
> +			return -EINVAL;
> +		}
> +
> +		return 0;
> +	}
> +
> +	mutex_lock(&otgd->fsm.lock);
> +	if (otgd->fsm.otg->gadget) {
> +		dev_err(otg_dev, "otg: gadget already registered with otg\n");
> +		mutex_unlock(&otgd->fsm.lock);
> +		return -EINVAL;
> +	}
> +
> +	otgd->fsm.otg->gadget = gadget;
> +	otgd->gadget_ops = ops;
> +	dev_info(otg_dev, "otg: gadget %s registered\n",
> +		 dev_name(&gadget->dev));
> +
> +	/* start FSM */
> +	usb_otg_start_fsm(&otgd->fsm);
> +	mutex_unlock(&otgd->fsm.lock);
> +
> +	return 0;
> +}
> +EXPORT_SYMBOL_GPL(usb_otg_register_gadget);
> +
> +/**
> + * usb_otg_unregister_gadget - Unregister Gadget controller from OTG core
> + * @gadget:	Gadget controller
> + *
> + * This is used by the USB Gadget stack to unregister the Gadget controller
> + * from the OTG core. Ensures that Gadget controller is not running
> + * on successful return.
> + *
> + * Returns: 0 on success, error value otherwise.
> + */
> +int usb_otg_unregister_gadget(struct usb_gadget *gadget)
> +{
> +	struct usb_otg *otgd;
> +	struct device *gadget_dev = &gadget->dev;
> +	struct device *otg_dev = usb_otg_get_device(gadget_dev);
> +
> +	if (!otg_dev)
> +		return -EINVAL;
> +
> +	mutex_lock(&otg_list_mutex);
> +	otgd = usb_otg_get_data(otg_dev);
> +	mutex_unlock(&otg_list_mutex);
> +	if (!otgd) {
> +		/* are we in wait list? */
> +		if (!usb_otg_gadget_wait_remove(gadget))
> +			return 0;
> +
> +		dev_dbg(gadget_dev, "otg: gadget wasn't registered with otg\n");
> +		return -EINVAL;
> +	}
> +
> +	mutex_lock(&otgd->fsm.lock);
> +	if (otgd->fsm.otg->gadget != gadget) {
> +		dev_err(otg_dev, "otg: gadget %s wasn't registered with otg\n",
> +			dev_name(&gadget->dev));
> +		mutex_unlock(&otgd->fsm.lock);
> +		return -EINVAL;
> +	}
> +
> +	/* Stop FSM & gadget */
> +	usb_otg_stop_fsm(&otgd->fsm);
> +	otgd->fsm.otg->gadget = NULL;
> +	mutex_unlock(&otgd->fsm.lock);
> +
> +	dev_info(otg_dev, "otg: gadget %s unregistered\n",
> +		 dev_name(&gadget->dev));
> +
> +	return 0;
> +}
> +EXPORT_SYMBOL_GPL(usb_otg_unregister_gadget);
> +
> +/**
> + * usb_otg_fsm_to_dev - Get OTG controller device from struct otg_fsm
> + * @fsm:	otg_fsm data structure
> + *
> + * This is used by the OTG controller driver to get it's device node
> + * from any of the otg_fsm->ops.
> + */
> +struct device *usb_otg_fsm_to_dev(struct otg_fsm *fsm)
> +{
> +	struct usb_otg *otgd = container_of(fsm, struct usb_otg, fsm);
> +
> +	return otgd->dev;
> +}
> +EXPORT_SYMBOL_GPL(usb_otg_fsm_to_dev);
> diff --git a/drivers/usb/common/usb-otg.h b/drivers/usb/common/usb-otg.h
> new file mode 100644
> index 0000000..05331f0
> --- /dev/null
> +++ b/drivers/usb/common/usb-otg.h
> @@ -0,0 +1,71 @@
> +/**
> + * drivers/usb/common/usb-otg.h - USB OTG core local header
> + *
> + * Copyright (C) 2015 Texas Instruments Incorporated - http://www.ti.com
> + * Author: Roger Quadros <rogerq@ti.com>
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License version 2 as
> + * published by the Free Software Foundation.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + * GNU General Public License for more details.
> + */
> +
> +#ifndef __DRIVERS_USB_COMMON_USB_OTG_H
> +#define __DRIVERS_USB_COMMON_USB_OTG_H
> +
> +/*
> + *  A-DEVICE timing constants
> + */
> +
> +/* Wait for VBUS Rise  */
> +#define TA_WAIT_VRISE        (100)	/* a_wait_vrise: section 7.1.2
> +					 * a_wait_vrise_tmr: section 7.4.5.1
> +					 * TA_VBUS_RISE <= 100ms, section 4.4
> +					 * Table 4-1: Electrical Characteristics
> +					 * ->DC Electrical Timing
> +					 */
> +/* Wait for VBUS Fall  */
> +#define TA_WAIT_VFALL        (1000)	/* a_wait_vfall: section 7.1.7
> +					 * a_wait_vfall_tmr: section: 7.4.5.2
> +					 */
> +/* Wait for B-Connect */
> +#define TA_WAIT_BCON         (10000)	/* a_wait_bcon: section 7.1.3
> +					 * TA_WAIT_BCON: should be between 1100
> +					 * and 30000 ms, section 5.5, Table 5-1
> +					 */
> +/* A-Idle to B-Disconnect */
> +#define TA_AIDL_BDIS         (5000)	/* a_suspend min 200 ms, section 5.2.1
> +					 * TA_AIDL_BDIS: section 5.5, Table 5-1
> +					 */
> +/* B-Idle to A-Disconnect */
> +#define TA_BIDL_ADIS         (500)	/* TA_BIDL_ADIS: section 5.2.1
> +					 * 500ms is used for B switch to host
> +					 * for safe
> +					 */
> +
> +/*
> + * B-device timing constants
> + */
> +
> +/* Data-Line Pulse Time*/
> +#define TB_DATA_PLS          (10)	/* b_srp_init,continue 5~10ms
> +					 * section:5.1.3
> +					 */
> +/* SRP Fail Time  */
> +#define TB_SRP_FAIL          (6000)	/* b_srp_init,fail time 5~6s
> +					 * section:5.1.6
> +					 */
> +/* A-SE0 to B-Reset  */
> +#define TB_ASE0_BRST         (155)	/* minimum 155 ms, section:5.3.1 */
> +/* SE0 Time Before SRP */
> +#define TB_SE0_SRP           (1000)	/* b_idle,minimum 1s, section:5.1.2 */
> +/* SSEND time before SRP */
> +#define TB_SSEND_SRP         (1500)	/* minimum 1.5 sec, section:5.1.2 */
> +
> +#define TB_SESS_VLD          (1000)
> +
> +#endif /* __DRIVERS_USB_COMMON_USB_OTG_H */
> diff --git a/drivers/usb/core/Kconfig b/drivers/usb/core/Kconfig
> index a99c89e..b468a9f 100644
> --- a/drivers/usb/core/Kconfig
> +++ b/drivers/usb/core/Kconfig
> @@ -42,7 +42,7 @@ config USB_DYNAMIC_MINORS
>  	  If you are unsure about this, say N here.
>  
>  config USB_OTG
> -	bool "OTG support"
> +	bool "OTG/Dual-role support"
>  	depends on PM
>  	default n
>  	help
> @@ -75,15 +75,6 @@ config USB_OTG_BLACKLIST_HUB
>  	  and software costs by not supporting external hubs.  So
>  	  are "Embedded Hosts" that don't offer OTG support.
>  
> -config USB_OTG_FSM
> -	tristate "USB 2.0 OTG FSM implementation"
> -	depends on USB
> -	select USB_OTG
> -	select USB_PHY
> -	help
> -	  Implements OTG Finite State Machine as specified in On-The-Go
> -	  and Embedded Host Supplement to the USB Revision 2.0 Specification.
> -
>  config USB_ULPI_BUS
>  	tristate "USB ULPI PHY interface support"
>  	depends on USB_SUPPORT
> diff --git a/include/linux/usb/otg.h b/include/linux/usb/otg.h
> index bd1dcf8..38cabe0 100644
> --- a/include/linux/usb/otg.h
> +++ b/include/linux/usb/otg.h
> @@ -10,19 +10,100 @@
>  #define __LINUX_USB_OTG_H
>  
>  #include <linux/phy/phy.h>
> +#include <linux/device.h>
> +#include <linux/hrtimer.h>
> +#include <linux/ktime.h>
> +#include <linux/usb.h>
> +#include <linux/usb/hcd.h>
> +#include <linux/usb/gadget.h>
> +#include <linux/usb/otg-fsm.h>
>  #include <linux/usb/phy.h>
>  
> +/**
> + * struct otg_hcd - host controller state and interface
> + *
> + * @hcd: host controller
> + * @irqnum: irq number
> + * @irqflags: irq flags
> + * @ops: otg to host controller interface
> + */
> +struct otg_hcd {
> +	struct usb_hcd *hcd;
> +	unsigned int irqnum;
> +	unsigned long irqflags;
> +	struct otg_hcd_ops *ops;
> +};
> +
> +struct usb_otg;
> +
> +/**
> + * struct otg_timer - otg timer data
> + *
> + * @timer: high resolution timer
> + * @timeout: timeout value
> + * @timetout_bit: pointer to variable that is set on timeout
> + * @otgd: usb otg data
> + */
> +struct otg_timer {
> +	struct hrtimer timer;
> +	ktime_t timeout;
> +	/* callback data */
> +	int *timeout_bit;
> +	struct usb_otg *otgd;
> +};
> +
> +/**
> + * struct usb_otg - usb otg controller state
> + *
> + * @default_a: Indicates we are an A device. i.e. Host.
> + * @phy: USB phy interface
> + * @usb_phy: old usb_phy interface
> + * @host: host controller bus
> + * @gadget: gadget device
> + * @state: current otg state
> + * @dev: otg controller device
> + * @caps: otg capabilities revision, hnp, srp, etc
> + * @fsm: otg finite state machine
> + * @fsm_ops: controller hooks for the state machine
> + * ------- internal use only -------
> + * @primary_hcd: primary host state and interface
> + * @shared_hcd: shared host state and interface
> + * @gadget_ops: gadget interface
> + * @timers: otg timers for state machine
> + * @list: list of otg controllers
> + * @work: otg state machine work
> + * @wq: otg state machine work queue
> + * @fsm_running: state machine running/stopped indicator
> + */
>  struct usb_otg {
>  	u8			default_a;
>  
>  	struct phy		*phy;
>  	/* old usb_phy interface */
>  	struct usb_phy		*usb_phy;
> +

add a blank line?

>  	struct usb_bus		*host;
>  	struct usb_gadget	*gadget;
>  
>  	enum usb_otg_state	state;
>  
> +	struct device *dev;
> +	struct usb_otg_caps *caps;
> +	struct otg_fsm fsm;
> +	struct otg_fsm_ops fsm_ops;
> +
> +	/* internal use only */
> +	struct otg_hcd primary_hcd;
> +	struct otg_hcd shared_hcd;
> +	struct otg_gadget_ops *gadget_ops;
> +	struct otg_timer timers[NUM_OTG_FSM_TIMERS];
> +	struct list_head list;
> +	struct work_struct work;
> +	struct workqueue_struct *wq;
> +	bool fsm_running;

Should fsm_running be added in otg_fsm struct?

> +	/* use otg->fsm.lock for serializing access */
> +
> +/*------------- deprecated interface -----------------------------*/
>  	/* bind/unbind the host controller */
>  	int	(*set_host)(struct usb_otg *otg, struct usb_bus *host);
>  
> @@ -38,7 +119,7 @@ struct usb_otg {
>  
>  	/* start or continue HNP role switch */
>  	int	(*start_hnp)(struct usb_otg *otg);
> -
> +/*---------------------------------------------------------------*/
>  };
>  
>  /**
> @@ -56,8 +137,105 @@ struct usb_otg_caps {
>  	bool adp_support;
>  };
>  
> +/**
> + * struct usb_otg_config - otg controller configuration
> + * @caps: otg capabilities of the controller
> + * @ops: otg fsm operations
> + * @otg_timeouts: override default otg fsm timeouts
> + */
> +struct usb_otg_config {
> +	struct usb_otg_caps otg_caps;
> +	struct otg_fsm_ops *fsm_ops;
> +	unsigned otg_timeouts[NUM_OTG_FSM_TIMERS];
> +};
> +
>  extern const char *usb_otg_state_string(enum usb_otg_state state);
>  
> +enum usb_dr_mode {
> +	USB_DR_MODE_UNKNOWN,
> +	USB_DR_MODE_HOST,
> +	USB_DR_MODE_PERIPHERAL,
> +	USB_DR_MODE_OTG,
> +};
> +
> +#if IS_ENABLED(CONFIG_USB_OTG)
> +struct otg_fsm *usb_otg_register(struct device *dev,
> +				 struct usb_otg_config *config);
> +int usb_otg_unregister(struct device *dev);
> +int usb_otg_register_hcd(struct usb_hcd *hcd, unsigned int irqnum,
> +			 unsigned long irqflags, struct otg_hcd_ops *ops);
> +int usb_otg_unregister_hcd(struct usb_hcd *hcd);
> +int usb_otg_register_gadget(struct usb_gadget *gadget,
> +			    struct otg_gadget_ops *ops);
> +int usb_otg_unregister_gadget(struct usb_gadget *gadget);
> +void usb_otg_sync_inputs(struct otg_fsm *fsm);
> +int usb_otg_kick_fsm(struct device *hcd_gcd_device);
> +struct device *usb_otg_fsm_to_dev(struct otg_fsm *fsm);
> +int usb_otg_start_host(struct otg_fsm *fsm, int on);
> +int usb_otg_start_gadget(struct otg_fsm *fsm, int on);
> +
> +#else /* CONFIG_USB_OTG */
> +
> +static inline struct otg_fsm *usb_otg_register(struct device *dev,
> +					       struct usb_otg_config *config)
> +{
> +	return ERR_PTR(-ENOTSUPP);
> +}
> +
> +static inline int usb_otg_unregister(struct device *dev)
> +{
> +	return -ENOTSUPP;
> +}
> +
> +static inline int usb_otg_register_hcd(struct usb_hcd *hcd, unsigned int irqnum,
> +				       unsigned long irqflags,
> +				       struct otg_hcd_ops *ops)
> +{
> +	return -ENOTSUPP;
> +}
> +
> +static inline int usb_otg_unregister_hcd(struct usb_hcd *hcd)
> +{
> +	return -ENOTSUPP;
> +}
> +
> +static inline int usb_otg_register_gadget(struct usb_gadget *gadget,
> +					  struct otg_gadget_ops *ops)
> +{
> +	return -ENOTSUPP;
> +}
> +
> +static inline int usb_otg_unregister_gadget(struct usb_gadget *gadget)
> +{
> +	return -ENOTSUPP;
> +}
> +
> +static inline void usb_otg_sync_inputs(struct otg_fsm *fsm)
> +{
> +}
> +
> +static inline int usb_otg_kick_fsm(struct device *hcd_gcd_device)
> +{
> +	return -ENOTSUPP;
> +}
> +
> +static inline struct device *usb_otg_fsm_to_dev(struct otg_fsm *fsm)
> +{
> +	return NULL;
> +}
> +
> +static inline int usb_otg_start_host(struct otg_fsm *fsm, int on)
> +{
> +	return -ENOTSUPP;
> +}
> +
> +static inline int usb_otg_start_gadget(struct otg_fsm *fsm, int on)
> +{
> +	return -ENOTSUPP;
> +}
> +#endif /* CONFIG_USB_OTG */
> +
> +/*------------- deprecated interface -----------------------------*/
>  /* Context: can sleep */
>  static inline int
>  otg_start_hnp(struct usb_otg *otg)
> @@ -109,14 +287,9 @@ otg_start_srp(struct usb_otg *otg)
>  	return -ENOTSUPP;
>  }
>  
> +/*---------------------------------------------------------------*/
> +
>  /* for OTG controller drivers (and maybe other stuff) */
>  extern int usb_bus_start_enum(struct usb_bus *bus, unsigned port_num);
>  
> -enum usb_dr_mode {
> -	USB_DR_MODE_UNKNOWN,
> -	USB_DR_MODE_HOST,
> -	USB_DR_MODE_PERIPHERAL,
> -	USB_DR_MODE_OTG,
> -};
> -
>  #endif /* __LINUX_USB_OTG_H */
> -- 
> 2.1.4
> 

^ permalink raw reply	[flat|nested] 48+ messages in thread

* Re: [PATCH v4 07/13] usb: otg: add OTG core
  2015-09-07  1:23     ` Peter Chen
@ 2015-09-07 10:23       ` Roger Quadros
  -1 siblings, 0 replies; 48+ messages in thread
From: Roger Quadros @ 2015-09-07 10:23 UTC (permalink / raw)
  To: Peter Chen
  Cc: stern, balbi, gregkh, dan.j.williams, jun.li, mathias.nyman, tony,
	Joao.Pinto, abrestic, linux-usb, linux-kernel, linux-omap

On 07/09/15 04:23, Peter Chen wrote:
> On Mon, Aug 24, 2015 at 04:21:18PM +0300, Roger Quadros wrote:
>> + * This is used by the USB Host stack to register the Host controller
>> + * to the OTG core. Host controller must not be started by the
>> + * caller as it is left upto the OTG state machine to do so.
>> + *
>> + * Returns: 0 on success, error value otherwise.
>> + */
>> +int usb_otg_register_hcd(struct usb_hcd *hcd, unsigned int irqnum,
>> +			 unsigned long irqflags, struct otg_hcd_ops *ops)
>> +{
>> +	struct usb_otg *otgd;
>> +	struct device *hcd_dev = hcd->self.controller;
>> +	struct device *otg_dev = usb_otg_get_device(hcd_dev);
>> +
> 
> One big problem here is: there are two designs for current (IP) driver
> code, one creates dedicated hcd device as roothub's parent, like dwc3.
> Another one doesn't do this, roothub's parent is core device (or otg device
> in your patch), like chipidea and dwc2.
> 
> Then, otg_dev will be glue layer device for chipidea after that.

OK. Let's add a way for the otg controller driver to provide the host and gadget
information to the otg core for such devices like chipidea and dwc2.

This API must be called before the hcd/gadget-driver is added so that the otg
core knows it's linked to an OTG controller.

Any better idea?

cheers,
-roger

> 
> Peter
> 
>> +	if (!otg_dev)
>> +		return -EINVAL;	/* we're definitely not OTG */
>> +
>> +	/* we're otg but otg controller might not yet be registered */
>> +	mutex_lock(&otg_list_mutex);
>> +	otgd = usb_otg_get_data(otg_dev);
>> +	mutex_unlock(&otg_list_mutex);
>> +	if (!otgd) {
>> +		dev_dbg(hcd_dev,
>> +			"otg: controller not yet registered. waiting..\n");
>> +		/*
>> +		 * otg controller might register later. Put the hcd in
>> +		 * wait list and call us back when ready
>> +		 */
>> +		if (usb_otg_hcd_wait_add(otg_dev, hcd, irqnum, irqflags, ops)) {
>> +			dev_dbg(hcd_dev, "otg: failed to add to wait list\n");
>> +			return -EINVAL;
>> +		}
>> +
>> +		return 0;
>> +	}
>> +
>> +	/* HCD will be started by OTG fsm when needed */
>> +	mutex_lock(&otgd->fsm.lock);
>> +	if (otgd->primary_hcd.hcd) {
>> +		/* probably a shared HCD ? */
>> +		if (usb_otg_hcd_is_primary_hcd(hcd)) {
>> +			dev_err(otg_dev, "otg: primary host already registered\n");
>> +			goto err;
>> +		}
>> +
>> +		if (hcd->shared_hcd == otgd->primary_hcd.hcd) {
>> +			if (otgd->shared_hcd.hcd) {
>> +				dev_err(otg_dev, "otg: shared host already registered\n");
>> +				goto err;
>> +			}
>> +
>> +			otgd->shared_hcd.hcd = hcd;
>> +			otgd->shared_hcd.irqnum = irqnum;
>> +			otgd->shared_hcd.irqflags = irqflags;
>> +			otgd->shared_hcd.ops = ops;
>> +			dev_info(otg_dev, "otg: shared host %s registered\n",
>> +				 dev_name(hcd->self.controller));
>> +		} else {
>> +			dev_err(otg_dev, "otg: invalid shared host %s\n",
>> +				dev_name(hcd->self.controller));
>> +			goto err;
>> +		}
>> +	} else {
>> +		if (!usb_otg_hcd_is_primary_hcd(hcd)) {
>> +			dev_err(otg_dev, "otg: primary host must be registered first\n");
>> +			goto err;
>> +		}
>> +
>> +		otgd->primary_hcd.hcd = hcd;
>> +		otgd->primary_hcd.irqnum = irqnum;
>> +		otgd->primary_hcd.irqflags = irqflags;
>> +		otgd->primary_hcd.ops = ops;
>> +		dev_info(otg_dev, "otg: primary host %s registered\n",
>> +			 dev_name(hcd->self.controller));
>> +	}
>> +
>> +	/*
>> +	 * we're ready only if we have shared HCD
>> +	 * or we don't need shared HCD.
>> +	 */
>> +	if (otgd->shared_hcd.hcd || !otgd->primary_hcd.hcd->shared_hcd) {
>> +		otgd->fsm.otg->host = hcd_to_bus(hcd);
>> +		/* FIXME: set bus->otg_port if this is true OTG port with HNP */
>> +
>> +		/* start FSM */
>> +		usb_otg_start_fsm(&otgd->fsm);
>> +	} else {
>> +		dev_dbg(otg_dev, "otg: can't start till shared host registers\n");
>> +	}
>> +
>> +	mutex_unlock(&otgd->fsm.lock);
>> +
>> +	return 0;
>> +
>> +err:
>> +	mutex_unlock(&otgd->fsm.lock);
>> +	return -EINVAL;
>> +}
>> +EXPORT_SYMBOL_GPL(usb_otg_register_hcd);
>> +
>> +/**
>> + * usb_otg_unregister_hcd - Unregister Host controller from OTG core
>> + * @hcd:	Host controller device
>> + *
>> + * This is used by the USB Host stack to unregister the Host controller
>> + * from the OTG core. Ensures that Host controller is not running
>> + * on successful return.
>> + *
>> + * Returns: 0 on success, error value otherwise.
>> + */
>> +int usb_otg_unregister_hcd(struct usb_hcd *hcd)
>> +{
>> +	struct usb_otg *otgd;
>> +	struct device *hcd_dev = hcd_to_bus(hcd)->controller;
>> +	struct device *otg_dev = usb_otg_get_device(hcd_dev);
>> +
>> +	if (!otg_dev)
>> +		return -EINVAL;	/* we're definitely not OTG */
>> +
>> +	mutex_lock(&otg_list_mutex);
>> +	otgd = usb_otg_get_data(otg_dev);
>> +	mutex_unlock(&otg_list_mutex);
>> +	if (!otgd) {
>> +		/* are we in wait list? */
>> +		if (!usb_otg_hcd_wait_remove(hcd))
>> +			return 0;
>> +
>> +		dev_dbg(hcd_dev, "otg: host wasn't registered with otg\n");
>> +		return -EINVAL;
>> +	}
>> +
>> +	mutex_lock(&otgd->fsm.lock);
>> +	if (hcd == otgd->primary_hcd.hcd) {
>> +		otgd->primary_hcd.hcd = NULL;
>> +		dev_info(otg_dev, "otg: primary host %s unregistered\n",
>> +			 dev_name(hcd_dev));
>> +	} else if (hcd == otgd->shared_hcd.hcd) {
>> +		otgd->shared_hcd.hcd = NULL;
>> +		dev_info(otg_dev, "otg: shared host %s unregistered\n",
>> +			 dev_name(hcd_dev));
>> +	} else {
>> +		dev_err(otg_dev, "otg: host %s wasn't registered with otg\n",
>> +			dev_name(hcd_dev));
>> +		mutex_unlock(&otgd->fsm.lock);
>> +		return -EINVAL;
>> +	}
>> +
>> +	/* stop FSM & Host */
>> +	usb_otg_stop_fsm(&otgd->fsm);
>> +	otgd->fsm.otg->host = NULL;
>> +
>> +	mutex_unlock(&otgd->fsm.lock);
>> +
>> +	return 0;
>> +}
>> +EXPORT_SYMBOL_GPL(usb_otg_unregister_hcd);
>> +
>> +/**
>> + * usb_otg_register_gadget - Register Gadget controller to OTG core
>> + * @gadget:	Gadget controller
>> + *
>> + * This is used by the USB Gadget stack to register the Gadget controller
>> + * to the OTG core. Gadget controller must not be started by the
>> + * caller as it is left upto the OTG state machine to do so.
>> + *
>> + * Gadget core must call this only when all resources required for
>> + * gadget controller to run are available.
>> + * i.e. gadget function driver is available.
>> + *
>> + * Returns: 0 on success, error value otherwise.
>> + */
>> +int usb_otg_register_gadget(struct usb_gadget *gadget,
>> +			    struct otg_gadget_ops *ops)
>> +{
>> +	struct usb_otg *otgd;
>> +	struct device *gadget_dev = &gadget->dev;
>> +	struct device *otg_dev = usb_otg_get_device(gadget_dev);
>> +
>> +	if (!otg_dev)
>> +		return -EINVAL;	/* we're definitely not OTG */
>> +
>> +	/* we're otg but otg controller might not yet be registered */
>> +	mutex_lock(&otg_list_mutex);
>> +	otgd = usb_otg_get_data(otg_dev);
>> +	mutex_unlock(&otg_list_mutex);
>> +	if (!otgd) {
>> +		dev_dbg(gadget_dev,
>> +			"otg: controller not yet registered. waiting..\n");
>> +		/*
>> +		 * otg controller might register later. Put the gadget in
>> +		 * wait list and call us back when ready
>> +		 */
>> +		if (usb_otg_gadget_wait_add(otg_dev, gadget, ops)) {
>> +			dev_dbg(gadget_dev, "otg: failed to add to wait list\n");
>> +			return -EINVAL;
>> +		}
>> +
>> +		return 0;
>> +	}
>> +
>> +	mutex_lock(&otgd->fsm.lock);
>> +	if (otgd->fsm.otg->gadget) {
>> +		dev_err(otg_dev, "otg: gadget already registered with otg\n");
>> +		mutex_unlock(&otgd->fsm.lock);
>> +		return -EINVAL;
>> +	}
>> +
>> +	otgd->fsm.otg->gadget = gadget;
>> +	otgd->gadget_ops = ops;
>> +	dev_info(otg_dev, "otg: gadget %s registered\n",
>> +		 dev_name(&gadget->dev));
>> +
>> +	/* start FSM */
>> +	usb_otg_start_fsm(&otgd->fsm);
>> +	mutex_unlock(&otgd->fsm.lock);
>> +
>> +	return 0;
>> +}
>> +EXPORT_SYMBOL_GPL(usb_otg_register_gadget);
>> +
>> +/**
>> + * usb_otg_unregister_gadget - Unregister Gadget controller from OTG core
>> + * @gadget:	Gadget controller
>> + *
>> + * This is used by the USB Gadget stack to unregister the Gadget controller
>> + * from the OTG core. Ensures that Gadget controller is not running
>> + * on successful return.
>> + *
>> + * Returns: 0 on success, error value otherwise.
>> + */
>> +int usb_otg_unregister_gadget(struct usb_gadget *gadget)
>> +{
>> +	struct usb_otg *otgd;
>> +	struct device *gadget_dev = &gadget->dev;
>> +	struct device *otg_dev = usb_otg_get_device(gadget_dev);
>> +
>> +	if (!otg_dev)
>> +		return -EINVAL;
>> +
>> +	mutex_lock(&otg_list_mutex);
>> +	otgd = usb_otg_get_data(otg_dev);
>> +	mutex_unlock(&otg_list_mutex);
>> +	if (!otgd) {
>> +		/* are we in wait list? */
>> +		if (!usb_otg_gadget_wait_remove(gadget))
>> +			return 0;
>> +
>> +		dev_dbg(gadget_dev, "otg: gadget wasn't registered with otg\n");
>> +		return -EINVAL;
>> +	}
>> +
>> +	mutex_lock(&otgd->fsm.lock);
>> +	if (otgd->fsm.otg->gadget != gadget) {
>> +		dev_err(otg_dev, "otg: gadget %s wasn't registered with otg\n",
>> +			dev_name(&gadget->dev));
>> +		mutex_unlock(&otgd->fsm.lock);
>> +		return -EINVAL;
>> +	}
>> +
>> +	/* Stop FSM & gadget */
>> +	usb_otg_stop_fsm(&otgd->fsm);
>> +	otgd->fsm.otg->gadget = NULL;
>> +	mutex_unlock(&otgd->fsm.lock);
>> +
>> +	dev_info(otg_dev, "otg: gadget %s unregistered\n",
>> +		 dev_name(&gadget->dev));
>> +
>> +	return 0;
>> +}
>> +EXPORT_SYMBOL_GPL(usb_otg_unregister_gadget);
>> +
>> +/**
>> + * usb_otg_fsm_to_dev - Get OTG controller device from struct otg_fsm
>> + * @fsm:	otg_fsm data structure
>> + *
>> + * This is used by the OTG controller driver to get it's device node
>> + * from any of the otg_fsm->ops.
>> + */
>> +struct device *usb_otg_fsm_to_dev(struct otg_fsm *fsm)
>> +{
>> +	struct usb_otg *otgd = container_of(fsm, struct usb_otg, fsm);
>> +
>> +	return otgd->dev;
>> +}
>> +EXPORT_SYMBOL_GPL(usb_otg_fsm_to_dev);
>> diff --git a/drivers/usb/common/usb-otg.h b/drivers/usb/common/usb-otg.h
>> new file mode 100644
>> index 0000000..05331f0
>> --- /dev/null
>> +++ b/drivers/usb/common/usb-otg.h
>> @@ -0,0 +1,71 @@
>> +/**
>> + * drivers/usb/common/usb-otg.h - USB OTG core local header
>> + *
>> + * Copyright (C) 2015 Texas Instruments Incorporated - http://www.ti.com
>> + * Author: Roger Quadros <rogerq@ti.com>
>> + *
>> + * This program is free software; you can redistribute it and/or modify
>> + * it under the terms of the GNU General Public License version 2 as
>> + * published by the Free Software Foundation.
>> + *
>> + * This program is distributed in the hope that it will be useful,
>> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
>> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
>> + * GNU General Public License for more details.
>> + */
>> +
>> +#ifndef __DRIVERS_USB_COMMON_USB_OTG_H
>> +#define __DRIVERS_USB_COMMON_USB_OTG_H
>> +
>> +/*
>> + *  A-DEVICE timing constants
>> + */
>> +
>> +/* Wait for VBUS Rise  */
>> +#define TA_WAIT_VRISE        (100)	/* a_wait_vrise: section 7.1.2
>> +					 * a_wait_vrise_tmr: section 7.4.5.1
>> +					 * TA_VBUS_RISE <= 100ms, section 4.4
>> +					 * Table 4-1: Electrical Characteristics
>> +					 * ->DC Electrical Timing
>> +					 */
>> +/* Wait for VBUS Fall  */
>> +#define TA_WAIT_VFALL        (1000)	/* a_wait_vfall: section 7.1.7
>> +					 * a_wait_vfall_tmr: section: 7.4.5.2
>> +					 */
>> +/* Wait for B-Connect */
>> +#define TA_WAIT_BCON         (10000)	/* a_wait_bcon: section 7.1.3
>> +					 * TA_WAIT_BCON: should be between 1100
>> +					 * and 30000 ms, section 5.5, Table 5-1
>> +					 */
>> +/* A-Idle to B-Disconnect */
>> +#define TA_AIDL_BDIS         (5000)	/* a_suspend min 200 ms, section 5.2.1
>> +					 * TA_AIDL_BDIS: section 5.5, Table 5-1
>> +					 */
>> +/* B-Idle to A-Disconnect */
>> +#define TA_BIDL_ADIS         (500)	/* TA_BIDL_ADIS: section 5.2.1
>> +					 * 500ms is used for B switch to host
>> +					 * for safe
>> +					 */
>> +
>> +/*
>> + * B-device timing constants
>> + */
>> +
>> +/* Data-Line Pulse Time*/
>> +#define TB_DATA_PLS          (10)	/* b_srp_init,continue 5~10ms
>> +					 * section:5.1.3
>> +					 */
>> +/* SRP Fail Time  */
>> +#define TB_SRP_FAIL          (6000)	/* b_srp_init,fail time 5~6s
>> +					 * section:5.1.6
>> +					 */
>> +/* A-SE0 to B-Reset  */
>> +#define TB_ASE0_BRST         (155)	/* minimum 155 ms, section:5.3.1 */
>> +/* SE0 Time Before SRP */
>> +#define TB_SE0_SRP           (1000)	/* b_idle,minimum 1s, section:5.1.2 */
>> +/* SSEND time before SRP */
>> +#define TB_SSEND_SRP         (1500)	/* minimum 1.5 sec, section:5.1.2 */
>> +
>> +#define TB_SESS_VLD          (1000)
>> +
>> +#endif /* __DRIVERS_USB_COMMON_USB_OTG_H */
>> diff --git a/drivers/usb/core/Kconfig b/drivers/usb/core/Kconfig
>> index a99c89e..b468a9f 100644
>> --- a/drivers/usb/core/Kconfig
>> +++ b/drivers/usb/core/Kconfig
>> @@ -42,7 +42,7 @@ config USB_DYNAMIC_MINORS
>>  	  If you are unsure about this, say N here.
>>  
>>  config USB_OTG
>> -	bool "OTG support"
>> +	bool "OTG/Dual-role support"
>>  	depends on PM
>>  	default n
>>  	help
>> @@ -75,15 +75,6 @@ config USB_OTG_BLACKLIST_HUB
>>  	  and software costs by not supporting external hubs.  So
>>  	  are "Embedded Hosts" that don't offer OTG support.
>>  
>> -config USB_OTG_FSM
>> -	tristate "USB 2.0 OTG FSM implementation"
>> -	depends on USB
>> -	select USB_OTG
>> -	select USB_PHY
>> -	help
>> -	  Implements OTG Finite State Machine as specified in On-The-Go
>> -	  and Embedded Host Supplement to the USB Revision 2.0 Specification.
>> -
>>  config USB_ULPI_BUS
>>  	tristate "USB ULPI PHY interface support"
>>  	depends on USB_SUPPORT
>> diff --git a/include/linux/usb/otg.h b/include/linux/usb/otg.h
>> index bd1dcf8..38cabe0 100644
>> --- a/include/linux/usb/otg.h
>> +++ b/include/linux/usb/otg.h
>> @@ -10,19 +10,100 @@
>>  #define __LINUX_USB_OTG_H
>>  
>>  #include <linux/phy/phy.h>
>> +#include <linux/device.h>
>> +#include <linux/hrtimer.h>
>> +#include <linux/ktime.h>
>> +#include <linux/usb.h>
>> +#include <linux/usb/hcd.h>
>> +#include <linux/usb/gadget.h>
>> +#include <linux/usb/otg-fsm.h>
>>  #include <linux/usb/phy.h>
>>  
>> +/**
>> + * struct otg_hcd - host controller state and interface
>> + *
>> + * @hcd: host controller
>> + * @irqnum: irq number
>> + * @irqflags: irq flags
>> + * @ops: otg to host controller interface
>> + */
>> +struct otg_hcd {
>> +	struct usb_hcd *hcd;
>> +	unsigned int irqnum;
>> +	unsigned long irqflags;
>> +	struct otg_hcd_ops *ops;
>> +};
>> +
>> +struct usb_otg;
>> +
>> +/**
>> + * struct otg_timer - otg timer data
>> + *
>> + * @timer: high resolution timer
>> + * @timeout: timeout value
>> + * @timetout_bit: pointer to variable that is set on timeout
>> + * @otgd: usb otg data
>> + */
>> +struct otg_timer {
>> +	struct hrtimer timer;
>> +	ktime_t timeout;
>> +	/* callback data */
>> +	int *timeout_bit;
>> +	struct usb_otg *otgd;
>> +};
>> +
>> +/**
>> + * struct usb_otg - usb otg controller state
>> + *
>> + * @default_a: Indicates we are an A device. i.e. Host.
>> + * @phy: USB phy interface
>> + * @usb_phy: old usb_phy interface
>> + * @host: host controller bus
>> + * @gadget: gadget device
>> + * @state: current otg state
>> + * @dev: otg controller device
>> + * @caps: otg capabilities revision, hnp, srp, etc
>> + * @fsm: otg finite state machine
>> + * @fsm_ops: controller hooks for the state machine
>> + * ------- internal use only -------
>> + * @primary_hcd: primary host state and interface
>> + * @shared_hcd: shared host state and interface
>> + * @gadget_ops: gadget interface
>> + * @timers: otg timers for state machine
>> + * @list: list of otg controllers
>> + * @work: otg state machine work
>> + * @wq: otg state machine work queue
>> + * @fsm_running: state machine running/stopped indicator
>> + */
>>  struct usb_otg {
>>  	u8			default_a;
>>  
>>  	struct phy		*phy;
>>  	/* old usb_phy interface */
>>  	struct usb_phy		*usb_phy;
>> +
>>  	struct usb_bus		*host;
>>  	struct usb_gadget	*gadget;
>>  
>>  	enum usb_otg_state	state;
>>  
>> +	struct device *dev;
>> +	struct usb_otg_caps *caps;
>> +	struct otg_fsm fsm;
>> +	struct otg_fsm_ops fsm_ops;
>> +
>> +	/* internal use only */
>> +	struct otg_hcd primary_hcd;
>> +	struct otg_hcd shared_hcd;
>> +	struct otg_gadget_ops *gadget_ops;
>> +	struct otg_timer timers[NUM_OTG_FSM_TIMERS];
>> +	struct list_head list;
>> +	struct work_struct work;
>> +	struct workqueue_struct *wq;
>> +	bool fsm_running;
>> +	/* use otg->fsm.lock for serializing access */
>> +
>> +/*------------- deprecated interface -----------------------------*/
>>  	/* bind/unbind the host controller */
>>  	int	(*set_host)(struct usb_otg *otg, struct usb_bus *host);
>>  
>> @@ -38,7 +119,7 @@ struct usb_otg {
>>  
>>  	/* start or continue HNP role switch */
>>  	int	(*start_hnp)(struct usb_otg *otg);
>> -
>> +/*---------------------------------------------------------------*/
>>  };
>>  
>>  /**
>> @@ -56,8 +137,105 @@ struct usb_otg_caps {
>>  	bool adp_support;
>>  };
>>  
>> +/**
>> + * struct usb_otg_config - otg controller configuration
>> + * @caps: otg capabilities of the controller
>> + * @ops: otg fsm operations
>> + * @otg_timeouts: override default otg fsm timeouts
>> + */
>> +struct usb_otg_config {
>> +	struct usb_otg_caps otg_caps;
>> +	struct otg_fsm_ops *fsm_ops;
>> +	unsigned otg_timeouts[NUM_OTG_FSM_TIMERS];
>> +};
>> +
>>  extern const char *usb_otg_state_string(enum usb_otg_state state);
>>  
>> +enum usb_dr_mode {
>> +	USB_DR_MODE_UNKNOWN,
>> +	USB_DR_MODE_HOST,
>> +	USB_DR_MODE_PERIPHERAL,
>> +	USB_DR_MODE_OTG,
>> +};
>> +
>> +#if IS_ENABLED(CONFIG_USB_OTG)
>> +struct otg_fsm *usb_otg_register(struct device *dev,
>> +				 struct usb_otg_config *config);
>> +int usb_otg_unregister(struct device *dev);
>> +int usb_otg_register_hcd(struct usb_hcd *hcd, unsigned int irqnum,
>> +			 unsigned long irqflags, struct otg_hcd_ops *ops);
>> +int usb_otg_unregister_hcd(struct usb_hcd *hcd);
>> +int usb_otg_register_gadget(struct usb_gadget *gadget,
>> +			    struct otg_gadget_ops *ops);
>> +int usb_otg_unregister_gadget(struct usb_gadget *gadget);
>> +void usb_otg_sync_inputs(struct otg_fsm *fsm);
>> +int usb_otg_kick_fsm(struct device *hcd_gcd_device);
>> +struct device *usb_otg_fsm_to_dev(struct otg_fsm *fsm);
>> +int usb_otg_start_host(struct otg_fsm *fsm, int on);
>> +int usb_otg_start_gadget(struct otg_fsm *fsm, int on);
>> +
>> +#else /* CONFIG_USB_OTG */
>> +
>> +static inline struct otg_fsm *usb_otg_register(struct device *dev,
>> +					       struct usb_otg_config *config)
>> +{
>> +	return ERR_PTR(-ENOTSUPP);
>> +}
>> +
>> +static inline int usb_otg_unregister(struct device *dev)
>> +{
>> +	return -ENOTSUPP;
>> +}
>> +
>> +static inline int usb_otg_register_hcd(struct usb_hcd *hcd, unsigned int irqnum,
>> +				       unsigned long irqflags,
>> +				       struct otg_hcd_ops *ops)
>> +{
>> +	return -ENOTSUPP;
>> +}
>> +
>> +static inline int usb_otg_unregister_hcd(struct usb_hcd *hcd)
>> +{
>> +	return -ENOTSUPP;
>> +}
>> +
>> +static inline int usb_otg_register_gadget(struct usb_gadget *gadget,
>> +					  struct otg_gadget_ops *ops)
>> +{
>> +	return -ENOTSUPP;
>> +}
>> +
>> +static inline int usb_otg_unregister_gadget(struct usb_gadget *gadget)
>> +{
>> +	return -ENOTSUPP;
>> +}
>> +
>> +static inline void usb_otg_sync_inputs(struct otg_fsm *fsm)
>> +{
>> +}
>> +
>> +static inline int usb_otg_kick_fsm(struct device *hcd_gcd_device)
>> +{
>> +	return -ENOTSUPP;
>> +}
>> +
>> +static inline struct device *usb_otg_fsm_to_dev(struct otg_fsm *fsm)
>> +{
>> +	return NULL;
>> +}
>> +
>> +static inline int usb_otg_start_host(struct otg_fsm *fsm, int on)
>> +{
>> +	return -ENOTSUPP;
>> +}
>> +
>> +static inline int usb_otg_start_gadget(struct otg_fsm *fsm, int on)
>> +{
>> +	return -ENOTSUPP;
>> +}
>> +#endif /* CONFIG_USB_OTG */
>> +
>> +/*------------- deprecated interface -----------------------------*/
>>  /* Context: can sleep */
>>  static inline int
>>  otg_start_hnp(struct usb_otg *otg)
>> @@ -109,14 +287,9 @@ otg_start_srp(struct usb_otg *otg)
>>  	return -ENOTSUPP;
>>  }
>>  
>> +/*---------------------------------------------------------------*/
>> +
>>  /* for OTG controller drivers (and maybe other stuff) */
>>  extern int usb_bus_start_enum(struct usb_bus *bus, unsigned port_num);
>>  
>> -enum usb_dr_mode {
>> -	USB_DR_MODE_UNKNOWN,
>> -	USB_DR_MODE_HOST,
>> -	USB_DR_MODE_PERIPHERAL,
>> -	USB_DR_MODE_OTG,
>> -};
>> -
>>  #endif /* __LINUX_USB_OTG_H */
>> -- 
>> 2.1.4
>>
> 

^ permalink raw reply	[flat|nested] 48+ messages in thread

* Re: [PATCH v4 07/13] usb: otg: add OTG core
@ 2015-09-07 10:23       ` Roger Quadros
  0 siblings, 0 replies; 48+ messages in thread
From: Roger Quadros @ 2015-09-07 10:23 UTC (permalink / raw)
  To: Peter Chen
  Cc: stern, balbi, gregkh, dan.j.williams, jun.li, mathias.nyman, tony,
	Joao.Pinto, abrestic, linux-usb, linux-kernel, linux-omap

On 07/09/15 04:23, Peter Chen wrote:
> On Mon, Aug 24, 2015 at 04:21:18PM +0300, Roger Quadros wrote:
>> + * This is used by the USB Host stack to register the Host controller
>> + * to the OTG core. Host controller must not be started by the
>> + * caller as it is left upto the OTG state machine to do so.
>> + *
>> + * Returns: 0 on success, error value otherwise.
>> + */
>> +int usb_otg_register_hcd(struct usb_hcd *hcd, unsigned int irqnum,
>> +			 unsigned long irqflags, struct otg_hcd_ops *ops)
>> +{
>> +	struct usb_otg *otgd;
>> +	struct device *hcd_dev = hcd->self.controller;
>> +	struct device *otg_dev = usb_otg_get_device(hcd_dev);
>> +
> 
> One big problem here is: there are two designs for current (IP) driver
> code, one creates dedicated hcd device as roothub's parent, like dwc3.
> Another one doesn't do this, roothub's parent is core device (or otg device
> in your patch), like chipidea and dwc2.
> 
> Then, otg_dev will be glue layer device for chipidea after that.

OK. Let's add a way for the otg controller driver to provide the host and gadget
information to the otg core for such devices like chipidea and dwc2.

This API must be called before the hcd/gadget-driver is added so that the otg
core knows it's linked to an OTG controller.

Any better idea?

cheers,
-roger

> 
> Peter
> 
>> +	if (!otg_dev)
>> +		return -EINVAL;	/* we're definitely not OTG */
>> +
>> +	/* we're otg but otg controller might not yet be registered */
>> +	mutex_lock(&otg_list_mutex);
>> +	otgd = usb_otg_get_data(otg_dev);
>> +	mutex_unlock(&otg_list_mutex);
>> +	if (!otgd) {
>> +		dev_dbg(hcd_dev,
>> +			"otg: controller not yet registered. waiting..\n");
>> +		/*
>> +		 * otg controller might register later. Put the hcd in
>> +		 * wait list and call us back when ready
>> +		 */
>> +		if (usb_otg_hcd_wait_add(otg_dev, hcd, irqnum, irqflags, ops)) {
>> +			dev_dbg(hcd_dev, "otg: failed to add to wait list\n");
>> +			return -EINVAL;
>> +		}
>> +
>> +		return 0;
>> +	}
>> +
>> +	/* HCD will be started by OTG fsm when needed */
>> +	mutex_lock(&otgd->fsm.lock);
>> +	if (otgd->primary_hcd.hcd) {
>> +		/* probably a shared HCD ? */
>> +		if (usb_otg_hcd_is_primary_hcd(hcd)) {
>> +			dev_err(otg_dev, "otg: primary host already registered\n");
>> +			goto err;
>> +		}
>> +
>> +		if (hcd->shared_hcd == otgd->primary_hcd.hcd) {
>> +			if (otgd->shared_hcd.hcd) {
>> +				dev_err(otg_dev, "otg: shared host already registered\n");
>> +				goto err;
>> +			}
>> +
>> +			otgd->shared_hcd.hcd = hcd;
>> +			otgd->shared_hcd.irqnum = irqnum;
>> +			otgd->shared_hcd.irqflags = irqflags;
>> +			otgd->shared_hcd.ops = ops;
>> +			dev_info(otg_dev, "otg: shared host %s registered\n",
>> +				 dev_name(hcd->self.controller));
>> +		} else {
>> +			dev_err(otg_dev, "otg: invalid shared host %s\n",
>> +				dev_name(hcd->self.controller));
>> +			goto err;
>> +		}
>> +	} else {
>> +		if (!usb_otg_hcd_is_primary_hcd(hcd)) {
>> +			dev_err(otg_dev, "otg: primary host must be registered first\n");
>> +			goto err;
>> +		}
>> +
>> +		otgd->primary_hcd.hcd = hcd;
>> +		otgd->primary_hcd.irqnum = irqnum;
>> +		otgd->primary_hcd.irqflags = irqflags;
>> +		otgd->primary_hcd.ops = ops;
>> +		dev_info(otg_dev, "otg: primary host %s registered\n",
>> +			 dev_name(hcd->self.controller));
>> +	}
>> +
>> +	/*
>> +	 * we're ready only if we have shared HCD
>> +	 * or we don't need shared HCD.
>> +	 */
>> +	if (otgd->shared_hcd.hcd || !otgd->primary_hcd.hcd->shared_hcd) {
>> +		otgd->fsm.otg->host = hcd_to_bus(hcd);
>> +		/* FIXME: set bus->otg_port if this is true OTG port with HNP */
>> +
>> +		/* start FSM */
>> +		usb_otg_start_fsm(&otgd->fsm);
>> +	} else {
>> +		dev_dbg(otg_dev, "otg: can't start till shared host registers\n");
>> +	}
>> +
>> +	mutex_unlock(&otgd->fsm.lock);
>> +
>> +	return 0;
>> +
>> +err:
>> +	mutex_unlock(&otgd->fsm.lock);
>> +	return -EINVAL;
>> +}
>> +EXPORT_SYMBOL_GPL(usb_otg_register_hcd);
>> +
>> +/**
>> + * usb_otg_unregister_hcd - Unregister Host controller from OTG core
>> + * @hcd:	Host controller device
>> + *
>> + * This is used by the USB Host stack to unregister the Host controller
>> + * from the OTG core. Ensures that Host controller is not running
>> + * on successful return.
>> + *
>> + * Returns: 0 on success, error value otherwise.
>> + */
>> +int usb_otg_unregister_hcd(struct usb_hcd *hcd)
>> +{
>> +	struct usb_otg *otgd;
>> +	struct device *hcd_dev = hcd_to_bus(hcd)->controller;
>> +	struct device *otg_dev = usb_otg_get_device(hcd_dev);
>> +
>> +	if (!otg_dev)
>> +		return -EINVAL;	/* we're definitely not OTG */
>> +
>> +	mutex_lock(&otg_list_mutex);
>> +	otgd = usb_otg_get_data(otg_dev);
>> +	mutex_unlock(&otg_list_mutex);
>> +	if (!otgd) {
>> +		/* are we in wait list? */
>> +		if (!usb_otg_hcd_wait_remove(hcd))
>> +			return 0;
>> +
>> +		dev_dbg(hcd_dev, "otg: host wasn't registered with otg\n");
>> +		return -EINVAL;
>> +	}
>> +
>> +	mutex_lock(&otgd->fsm.lock);
>> +	if (hcd == otgd->primary_hcd.hcd) {
>> +		otgd->primary_hcd.hcd = NULL;
>> +		dev_info(otg_dev, "otg: primary host %s unregistered\n",
>> +			 dev_name(hcd_dev));
>> +	} else if (hcd == otgd->shared_hcd.hcd) {
>> +		otgd->shared_hcd.hcd = NULL;
>> +		dev_info(otg_dev, "otg: shared host %s unregistered\n",
>> +			 dev_name(hcd_dev));
>> +	} else {
>> +		dev_err(otg_dev, "otg: host %s wasn't registered with otg\n",
>> +			dev_name(hcd_dev));
>> +		mutex_unlock(&otgd->fsm.lock);
>> +		return -EINVAL;
>> +	}
>> +
>> +	/* stop FSM & Host */
>> +	usb_otg_stop_fsm(&otgd->fsm);
>> +	otgd->fsm.otg->host = NULL;
>> +
>> +	mutex_unlock(&otgd->fsm.lock);
>> +
>> +	return 0;
>> +}
>> +EXPORT_SYMBOL_GPL(usb_otg_unregister_hcd);
>> +
>> +/**
>> + * usb_otg_register_gadget - Register Gadget controller to OTG core
>> + * @gadget:	Gadget controller
>> + *
>> + * This is used by the USB Gadget stack to register the Gadget controller
>> + * to the OTG core. Gadget controller must not be started by the
>> + * caller as it is left upto the OTG state machine to do so.
>> + *
>> + * Gadget core must call this only when all resources required for
>> + * gadget controller to run are available.
>> + * i.e. gadget function driver is available.
>> + *
>> + * Returns: 0 on success, error value otherwise.
>> + */
>> +int usb_otg_register_gadget(struct usb_gadget *gadget,
>> +			    struct otg_gadget_ops *ops)
>> +{
>> +	struct usb_otg *otgd;
>> +	struct device *gadget_dev = &gadget->dev;
>> +	struct device *otg_dev = usb_otg_get_device(gadget_dev);
>> +
>> +	if (!otg_dev)
>> +		return -EINVAL;	/* we're definitely not OTG */
>> +
>> +	/* we're otg but otg controller might not yet be registered */
>> +	mutex_lock(&otg_list_mutex);
>> +	otgd = usb_otg_get_data(otg_dev);
>> +	mutex_unlock(&otg_list_mutex);
>> +	if (!otgd) {
>> +		dev_dbg(gadget_dev,
>> +			"otg: controller not yet registered. waiting..\n");
>> +		/*
>> +		 * otg controller might register later. Put the gadget in
>> +		 * wait list and call us back when ready
>> +		 */
>> +		if (usb_otg_gadget_wait_add(otg_dev, gadget, ops)) {
>> +			dev_dbg(gadget_dev, "otg: failed to add to wait list\n");
>> +			return -EINVAL;
>> +		}
>> +
>> +		return 0;
>> +	}
>> +
>> +	mutex_lock(&otgd->fsm.lock);
>> +	if (otgd->fsm.otg->gadget) {
>> +		dev_err(otg_dev, "otg: gadget already registered with otg\n");
>> +		mutex_unlock(&otgd->fsm.lock);
>> +		return -EINVAL;
>> +	}
>> +
>> +	otgd->fsm.otg->gadget = gadget;
>> +	otgd->gadget_ops = ops;
>> +	dev_info(otg_dev, "otg: gadget %s registered\n",
>> +		 dev_name(&gadget->dev));
>> +
>> +	/* start FSM */
>> +	usb_otg_start_fsm(&otgd->fsm);
>> +	mutex_unlock(&otgd->fsm.lock);
>> +
>> +	return 0;
>> +}
>> +EXPORT_SYMBOL_GPL(usb_otg_register_gadget);
>> +
>> +/**
>> + * usb_otg_unregister_gadget - Unregister Gadget controller from OTG core
>> + * @gadget:	Gadget controller
>> + *
>> + * This is used by the USB Gadget stack to unregister the Gadget controller
>> + * from the OTG core. Ensures that Gadget controller is not running
>> + * on successful return.
>> + *
>> + * Returns: 0 on success, error value otherwise.
>> + */
>> +int usb_otg_unregister_gadget(struct usb_gadget *gadget)
>> +{
>> +	struct usb_otg *otgd;
>> +	struct device *gadget_dev = &gadget->dev;
>> +	struct device *otg_dev = usb_otg_get_device(gadget_dev);
>> +
>> +	if (!otg_dev)
>> +		return -EINVAL;
>> +
>> +	mutex_lock(&otg_list_mutex);
>> +	otgd = usb_otg_get_data(otg_dev);
>> +	mutex_unlock(&otg_list_mutex);
>> +	if (!otgd) {
>> +		/* are we in wait list? */
>> +		if (!usb_otg_gadget_wait_remove(gadget))
>> +			return 0;
>> +
>> +		dev_dbg(gadget_dev, "otg: gadget wasn't registered with otg\n");
>> +		return -EINVAL;
>> +	}
>> +
>> +	mutex_lock(&otgd->fsm.lock);
>> +	if (otgd->fsm.otg->gadget != gadget) {
>> +		dev_err(otg_dev, "otg: gadget %s wasn't registered with otg\n",
>> +			dev_name(&gadget->dev));
>> +		mutex_unlock(&otgd->fsm.lock);
>> +		return -EINVAL;
>> +	}
>> +
>> +	/* Stop FSM & gadget */
>> +	usb_otg_stop_fsm(&otgd->fsm);
>> +	otgd->fsm.otg->gadget = NULL;
>> +	mutex_unlock(&otgd->fsm.lock);
>> +
>> +	dev_info(otg_dev, "otg: gadget %s unregistered\n",
>> +		 dev_name(&gadget->dev));
>> +
>> +	return 0;
>> +}
>> +EXPORT_SYMBOL_GPL(usb_otg_unregister_gadget);
>> +
>> +/**
>> + * usb_otg_fsm_to_dev - Get OTG controller device from struct otg_fsm
>> + * @fsm:	otg_fsm data structure
>> + *
>> + * This is used by the OTG controller driver to get it's device node
>> + * from any of the otg_fsm->ops.
>> + */
>> +struct device *usb_otg_fsm_to_dev(struct otg_fsm *fsm)
>> +{
>> +	struct usb_otg *otgd = container_of(fsm, struct usb_otg, fsm);
>> +
>> +	return otgd->dev;
>> +}
>> +EXPORT_SYMBOL_GPL(usb_otg_fsm_to_dev);
>> diff --git a/drivers/usb/common/usb-otg.h b/drivers/usb/common/usb-otg.h
>> new file mode 100644
>> index 0000000..05331f0
>> --- /dev/null
>> +++ b/drivers/usb/common/usb-otg.h
>> @@ -0,0 +1,71 @@
>> +/**
>> + * drivers/usb/common/usb-otg.h - USB OTG core local header
>> + *
>> + * Copyright (C) 2015 Texas Instruments Incorporated - http://www.ti.com
>> + * Author: Roger Quadros <rogerq@ti.com>
>> + *
>> + * This program is free software; you can redistribute it and/or modify
>> + * it under the terms of the GNU General Public License version 2 as
>> + * published by the Free Software Foundation.
>> + *
>> + * This program is distributed in the hope that it will be useful,
>> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
>> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
>> + * GNU General Public License for more details.
>> + */
>> +
>> +#ifndef __DRIVERS_USB_COMMON_USB_OTG_H
>> +#define __DRIVERS_USB_COMMON_USB_OTG_H
>> +
>> +/*
>> + *  A-DEVICE timing constants
>> + */
>> +
>> +/* Wait for VBUS Rise  */
>> +#define TA_WAIT_VRISE        (100)	/* a_wait_vrise: section 7.1.2
>> +					 * a_wait_vrise_tmr: section 7.4.5.1
>> +					 * TA_VBUS_RISE <= 100ms, section 4.4
>> +					 * Table 4-1: Electrical Characteristics
>> +					 * ->DC Electrical Timing
>> +					 */
>> +/* Wait for VBUS Fall  */
>> +#define TA_WAIT_VFALL        (1000)	/* a_wait_vfall: section 7.1.7
>> +					 * a_wait_vfall_tmr: section: 7.4.5.2
>> +					 */
>> +/* Wait for B-Connect */
>> +#define TA_WAIT_BCON         (10000)	/* a_wait_bcon: section 7.1.3
>> +					 * TA_WAIT_BCON: should be between 1100
>> +					 * and 30000 ms, section 5.5, Table 5-1
>> +					 */
>> +/* A-Idle to B-Disconnect */
>> +#define TA_AIDL_BDIS         (5000)	/* a_suspend min 200 ms, section 5.2.1
>> +					 * TA_AIDL_BDIS: section 5.5, Table 5-1
>> +					 */
>> +/* B-Idle to A-Disconnect */
>> +#define TA_BIDL_ADIS         (500)	/* TA_BIDL_ADIS: section 5.2.1
>> +					 * 500ms is used for B switch to host
>> +					 * for safe
>> +					 */
>> +
>> +/*
>> + * B-device timing constants
>> + */
>> +
>> +/* Data-Line Pulse Time*/
>> +#define TB_DATA_PLS          (10)	/* b_srp_init,continue 5~10ms
>> +					 * section:5.1.3
>> +					 */
>> +/* SRP Fail Time  */
>> +#define TB_SRP_FAIL          (6000)	/* b_srp_init,fail time 5~6s
>> +					 * section:5.1.6
>> +					 */
>> +/* A-SE0 to B-Reset  */
>> +#define TB_ASE0_BRST         (155)	/* minimum 155 ms, section:5.3.1 */
>> +/* SE0 Time Before SRP */
>> +#define TB_SE0_SRP           (1000)	/* b_idle,minimum 1s, section:5.1.2 */
>> +/* SSEND time before SRP */
>> +#define TB_SSEND_SRP         (1500)	/* minimum 1.5 sec, section:5.1.2 */
>> +
>> +#define TB_SESS_VLD          (1000)
>> +
>> +#endif /* __DRIVERS_USB_COMMON_USB_OTG_H */
>> diff --git a/drivers/usb/core/Kconfig b/drivers/usb/core/Kconfig
>> index a99c89e..b468a9f 100644
>> --- a/drivers/usb/core/Kconfig
>> +++ b/drivers/usb/core/Kconfig
>> @@ -42,7 +42,7 @@ config USB_DYNAMIC_MINORS
>>  	  If you are unsure about this, say N here.
>>  
>>  config USB_OTG
>> -	bool "OTG support"
>> +	bool "OTG/Dual-role support"
>>  	depends on PM
>>  	default n
>>  	help
>> @@ -75,15 +75,6 @@ config USB_OTG_BLACKLIST_HUB
>>  	  and software costs by not supporting external hubs.  So
>>  	  are "Embedded Hosts" that don't offer OTG support.
>>  
>> -config USB_OTG_FSM
>> -	tristate "USB 2.0 OTG FSM implementation"
>> -	depends on USB
>> -	select USB_OTG
>> -	select USB_PHY
>> -	help
>> -	  Implements OTG Finite State Machine as specified in On-The-Go
>> -	  and Embedded Host Supplement to the USB Revision 2.0 Specification.
>> -
>>  config USB_ULPI_BUS
>>  	tristate "USB ULPI PHY interface support"
>>  	depends on USB_SUPPORT
>> diff --git a/include/linux/usb/otg.h b/include/linux/usb/otg.h
>> index bd1dcf8..38cabe0 100644
>> --- a/include/linux/usb/otg.h
>> +++ b/include/linux/usb/otg.h
>> @@ -10,19 +10,100 @@
>>  #define __LINUX_USB_OTG_H
>>  
>>  #include <linux/phy/phy.h>
>> +#include <linux/device.h>
>> +#include <linux/hrtimer.h>
>> +#include <linux/ktime.h>
>> +#include <linux/usb.h>
>> +#include <linux/usb/hcd.h>
>> +#include <linux/usb/gadget.h>
>> +#include <linux/usb/otg-fsm.h>
>>  #include <linux/usb/phy.h>
>>  
>> +/**
>> + * struct otg_hcd - host controller state and interface
>> + *
>> + * @hcd: host controller
>> + * @irqnum: irq number
>> + * @irqflags: irq flags
>> + * @ops: otg to host controller interface
>> + */
>> +struct otg_hcd {
>> +	struct usb_hcd *hcd;
>> +	unsigned int irqnum;
>> +	unsigned long irqflags;
>> +	struct otg_hcd_ops *ops;
>> +};
>> +
>> +struct usb_otg;
>> +
>> +/**
>> + * struct otg_timer - otg timer data
>> + *
>> + * @timer: high resolution timer
>> + * @timeout: timeout value
>> + * @timetout_bit: pointer to variable that is set on timeout
>> + * @otgd: usb otg data
>> + */
>> +struct otg_timer {
>> +	struct hrtimer timer;
>> +	ktime_t timeout;
>> +	/* callback data */
>> +	int *timeout_bit;
>> +	struct usb_otg *otgd;
>> +};
>> +
>> +/**
>> + * struct usb_otg - usb otg controller state
>> + *
>> + * @default_a: Indicates we are an A device. i.e. Host.
>> + * @phy: USB phy interface
>> + * @usb_phy: old usb_phy interface
>> + * @host: host controller bus
>> + * @gadget: gadget device
>> + * @state: current otg state
>> + * @dev: otg controller device
>> + * @caps: otg capabilities revision, hnp, srp, etc
>> + * @fsm: otg finite state machine
>> + * @fsm_ops: controller hooks for the state machine
>> + * ------- internal use only -------
>> + * @primary_hcd: primary host state and interface
>> + * @shared_hcd: shared host state and interface
>> + * @gadget_ops: gadget interface
>> + * @timers: otg timers for state machine
>> + * @list: list of otg controllers
>> + * @work: otg state machine work
>> + * @wq: otg state machine work queue
>> + * @fsm_running: state machine running/stopped indicator
>> + */
>>  struct usb_otg {
>>  	u8			default_a;
>>  
>>  	struct phy		*phy;
>>  	/* old usb_phy interface */
>>  	struct usb_phy		*usb_phy;
>> +
>>  	struct usb_bus		*host;
>>  	struct usb_gadget	*gadget;
>>  
>>  	enum usb_otg_state	state;
>>  
>> +	struct device *dev;
>> +	struct usb_otg_caps *caps;
>> +	struct otg_fsm fsm;
>> +	struct otg_fsm_ops fsm_ops;
>> +
>> +	/* internal use only */
>> +	struct otg_hcd primary_hcd;
>> +	struct otg_hcd shared_hcd;
>> +	struct otg_gadget_ops *gadget_ops;
>> +	struct otg_timer timers[NUM_OTG_FSM_TIMERS];
>> +	struct list_head list;
>> +	struct work_struct work;
>> +	struct workqueue_struct *wq;
>> +	bool fsm_running;
>> +	/* use otg->fsm.lock for serializing access */
>> +
>> +/*------------- deprecated interface -----------------------------*/
>>  	/* bind/unbind the host controller */
>>  	int	(*set_host)(struct usb_otg *otg, struct usb_bus *host);
>>  
>> @@ -38,7 +119,7 @@ struct usb_otg {
>>  
>>  	/* start or continue HNP role switch */
>>  	int	(*start_hnp)(struct usb_otg *otg);
>> -
>> +/*---------------------------------------------------------------*/
>>  };
>>  
>>  /**
>> @@ -56,8 +137,105 @@ struct usb_otg_caps {
>>  	bool adp_support;
>>  };
>>  
>> +/**
>> + * struct usb_otg_config - otg controller configuration
>> + * @caps: otg capabilities of the controller
>> + * @ops: otg fsm operations
>> + * @otg_timeouts: override default otg fsm timeouts
>> + */
>> +struct usb_otg_config {
>> +	struct usb_otg_caps otg_caps;
>> +	struct otg_fsm_ops *fsm_ops;
>> +	unsigned otg_timeouts[NUM_OTG_FSM_TIMERS];
>> +};
>> +
>>  extern const char *usb_otg_state_string(enum usb_otg_state state);
>>  
>> +enum usb_dr_mode {
>> +	USB_DR_MODE_UNKNOWN,
>> +	USB_DR_MODE_HOST,
>> +	USB_DR_MODE_PERIPHERAL,
>> +	USB_DR_MODE_OTG,
>> +};
>> +
>> +#if IS_ENABLED(CONFIG_USB_OTG)
>> +struct otg_fsm *usb_otg_register(struct device *dev,
>> +				 struct usb_otg_config *config);
>> +int usb_otg_unregister(struct device *dev);
>> +int usb_otg_register_hcd(struct usb_hcd *hcd, unsigned int irqnum,
>> +			 unsigned long irqflags, struct otg_hcd_ops *ops);
>> +int usb_otg_unregister_hcd(struct usb_hcd *hcd);
>> +int usb_otg_register_gadget(struct usb_gadget *gadget,
>> +			    struct otg_gadget_ops *ops);
>> +int usb_otg_unregister_gadget(struct usb_gadget *gadget);
>> +void usb_otg_sync_inputs(struct otg_fsm *fsm);
>> +int usb_otg_kick_fsm(struct device *hcd_gcd_device);
>> +struct device *usb_otg_fsm_to_dev(struct otg_fsm *fsm);
>> +int usb_otg_start_host(struct otg_fsm *fsm, int on);
>> +int usb_otg_start_gadget(struct otg_fsm *fsm, int on);
>> +
>> +#else /* CONFIG_USB_OTG */
>> +
>> +static inline struct otg_fsm *usb_otg_register(struct device *dev,
>> +					       struct usb_otg_config *config)
>> +{
>> +	return ERR_PTR(-ENOTSUPP);
>> +}
>> +
>> +static inline int usb_otg_unregister(struct device *dev)
>> +{
>> +	return -ENOTSUPP;
>> +}
>> +
>> +static inline int usb_otg_register_hcd(struct usb_hcd *hcd, unsigned int irqnum,
>> +				       unsigned long irqflags,
>> +				       struct otg_hcd_ops *ops)
>> +{
>> +	return -ENOTSUPP;
>> +}
>> +
>> +static inline int usb_otg_unregister_hcd(struct usb_hcd *hcd)
>> +{
>> +	return -ENOTSUPP;
>> +}
>> +
>> +static inline int usb_otg_register_gadget(struct usb_gadget *gadget,
>> +					  struct otg_gadget_ops *ops)
>> +{
>> +	return -ENOTSUPP;
>> +}
>> +
>> +static inline int usb_otg_unregister_gadget(struct usb_gadget *gadget)
>> +{
>> +	return -ENOTSUPP;
>> +}
>> +
>> +static inline void usb_otg_sync_inputs(struct otg_fsm *fsm)
>> +{
>> +}
>> +
>> +static inline int usb_otg_kick_fsm(struct device *hcd_gcd_device)
>> +{
>> +	return -ENOTSUPP;
>> +}
>> +
>> +static inline struct device *usb_otg_fsm_to_dev(struct otg_fsm *fsm)
>> +{
>> +	return NULL;
>> +}
>> +
>> +static inline int usb_otg_start_host(struct otg_fsm *fsm, int on)
>> +{
>> +	return -ENOTSUPP;
>> +}
>> +
>> +static inline int usb_otg_start_gadget(struct otg_fsm *fsm, int on)
>> +{
>> +	return -ENOTSUPP;
>> +}
>> +#endif /* CONFIG_USB_OTG */
>> +
>> +/*------------- deprecated interface -----------------------------*/
>>  /* Context: can sleep */
>>  static inline int
>>  otg_start_hnp(struct usb_otg *otg)
>> @@ -109,14 +287,9 @@ otg_start_srp(struct usb_otg *otg)
>>  	return -ENOTSUPP;
>>  }
>>  
>> +/*---------------------------------------------------------------*/
>> +
>>  /* for OTG controller drivers (and maybe other stuff) */
>>  extern int usb_bus_start_enum(struct usb_bus *bus, unsigned port_num);
>>  
>> -enum usb_dr_mode {
>> -	USB_DR_MODE_UNKNOWN,
>> -	USB_DR_MODE_HOST,
>> -	USB_DR_MODE_PERIPHERAL,
>> -	USB_DR_MODE_OTG,
>> -};
>> -
>>  #endif /* __LINUX_USB_OTG_H */
>> -- 
>> 2.1.4
>>
> 

^ permalink raw reply	[flat|nested] 48+ messages in thread

* Re: [PATCH v4 07/13] usb: otg: add OTG core
  2015-09-07  7:40       ` Li Jun
@ 2015-09-07 10:53         ` Roger Quadros
  -1 siblings, 0 replies; 48+ messages in thread
From: Roger Quadros @ 2015-09-07 10:53 UTC (permalink / raw)
  To: Li Jun
  Cc: stern, balbi, gregkh, peter.chen, dan.j.williams, jun.li,
	mathias.nyman, tony, Joao.Pinto, abrestic, linux-usb,
	linux-kernel, linux-omap

On 07/09/15 10:40, Li Jun wrote:
> On Mon, Aug 24, 2015 at 04:21:18PM +0300, Roger Quadros wrote:
>> The OTG core instantiates the OTG Finite State Machine
>> per OTG controller and manages starting/stopping the
>> host and gadget controllers based on the bus state.
>>
>> It provides APIs for the following tasks
>>
>> - Registering an OTG capable controller
>> - Registering Host and Gadget controllers to OTG core
>> - Providing inputs to and kicking the OTG state machine
>>
>> Signed-off-by: Roger Quadros <rogerq@ti.com>
>> ---
>>  MAINTAINERS                  |    4 +-
>>  drivers/usb/Kconfig          |    2 +-
>>  drivers/usb/Makefile         |    1 +
>>  drivers/usb/common/Makefile  |    3 +-
>>  drivers/usb/common/usb-otg.c | 1061 ++++++++++++++++++++++++++++++++++++++++++
>>  drivers/usb/common/usb-otg.h |   71 +++
>>  drivers/usb/core/Kconfig     |   11 +-
>>  include/linux/usb/otg.h      |  189 +++++++-
>>  8 files changed, 1321 insertions(+), 21 deletions(-)
>>  create mode 100644 drivers/usb/common/usb-otg.c
>>  create mode 100644 drivers/usb/common/usb-otg.h
>>
> 
> ... ...
> 
>> +
>> +/**
>> + * Get OTG device from host or gadget device.
>> + *
>> + * For non device tree boot, the OTG controller is assumed to be
>> + * the parent of the host/gadget device.
> 
> This assumption/restriction maybe a problem, as I pointed in your previous
> version, usb_create_hcd() use the passed dev as its dev, but,
> usb_add_gadget_udc() use the passed dev as its parent dev, so often the
> host and gadget don't share the same parent device, at least it doesn't
> apply to chipidea case.

Let's provide a way for OTG driver to provide the OTG core exactly which is
the related host/gadget device.

> 
>> + * For device tree boot, the OTG controller is derived from the
>> + * "otg-controller" property.
>> + */
>> +static struct device *usb_otg_get_device(struct device *hcd_gcd_dev)
>> +{
>> +	struct device *otg_dev;
>> +
>> +	if (!hcd_gcd_dev)
>> +		return NULL;
>> +
>> +	if (hcd_gcd_dev->of_node) {
>> +		struct device_node *np;
>> +		struct platform_device *pdev;
>> +
>> +		np = of_parse_phandle(hcd_gcd_dev->of_node, "otg-controller",
>> +				      0);
>> +		if (!np)
>> +			goto legacy;	/* continue legacy way */
>> +
>> +		pdev = of_find_device_by_node(np);
>> +		of_node_put(np);
>> +		if (!pdev) {
>> +			dev_err(&pdev->dev, "couldn't get otg-controller device\n");
>> +			return NULL;
>> +		}
>> +
>> +		otg_dev = &pdev->dev;
>> +		return otg_dev;
>> +	}
>> +
>> +legacy:
>> +	/* otg device is parent and must be registered */
>> +	otg_dev = hcd_gcd_dev->parent;
>> +	if (!usb_otg_get_data(otg_dev))
>> +		return NULL;
>> +
>> +	return otg_dev;
>> +}
>> +
> 
> ... ...
> 
>> +static void usb_otg_init_timers(struct usb_otg *otgd, unsigned *timeouts)
>> +{
>> +	struct otg_fsm *fsm = &otgd->fsm;
>> +	unsigned long tmouts[NUM_OTG_FSM_TIMERS];
>> +	int i;
>> +
>> +	/* set default timeouts */
>> +	tmouts[A_WAIT_VRISE] = TA_WAIT_VRISE;
>> +	tmouts[A_WAIT_VFALL] = TA_WAIT_VFALL;
>> +	tmouts[A_WAIT_BCON] = TA_WAIT_BCON;
>> +	tmouts[A_AIDL_BDIS] = TA_AIDL_BDIS;
>> +	tmouts[A_BIDL_ADIS] = TA_BIDL_ADIS;
>> +	tmouts[B_ASE0_BRST] = TB_ASE0_BRST;
>> +	tmouts[B_SE0_SRP] = TB_SE0_SRP;
>> +	tmouts[B_SRP_FAIL] = TB_SRP_FAIL;
>> +
>> +	/* set controller provided timeouts */
>> +	if (timeouts) {
>> +		for (i = 0; i < NUM_OTG_FSM_TIMERS; i++) {
>> +			if (timeouts[i])
>> +				tmouts[i] = timeouts[i];
>> +		}
>> +	}
>> +
>> +	otg_timer_init(A_WAIT_VRISE, otgd, set_tmout, TA_WAIT_VRISE,
>> +		       &fsm->a_wait_vrise_tmout);
>> +	otg_timer_init(A_WAIT_VFALL, otgd, set_tmout, TA_WAIT_VFALL,
>> +		       &fsm->a_wait_vfall_tmout);
>> +	otg_timer_init(A_WAIT_BCON, otgd, set_tmout, TA_WAIT_BCON,
>> +		       &fsm->a_wait_bcon_tmout);
>> +	otg_timer_init(A_AIDL_BDIS, otgd, set_tmout, TA_AIDL_BDIS,
>> +		       &fsm->a_aidl_bdis_tmout);
>> +	otg_timer_init(A_BIDL_ADIS, otgd, set_tmout, TA_BIDL_ADIS,
>> +		       &fsm->a_bidl_adis_tmout);
>> +	otg_timer_init(B_ASE0_BRST, otgd, set_tmout, TB_ASE0_BRST,
>> +		       &fsm->b_ase0_brst_tmout);
>> +
>> +	otg_timer_init(B_SE0_SRP, otgd, set_tmout, TB_SE0_SRP,
>> +		       &fsm->b_se0_srp);
>> +	otg_timer_init(B_SRP_FAIL, otgd, set_tmout, TB_SRP_FAIL,
>> +		       &fsm->b_srp_done);
>> +
>> +	/* FIXME: what about A_WAIT_ENUM? */
> 
> Either you init it as other timers, or you remove all of it, otherwise
> there will be NULL pointer crash.

I want to initialize it but was not sure about the timeout value.
What timeout value I must use?

> 
>> +}
>> +
>> +/**
>> + * OTG FSM ops function to add timer
>> + */
>> +static void usb_otg_add_timer(struct otg_fsm *fsm, enum otg_fsm_timer id)
>> +{
>> +	struct usb_otg *otgd = container_of(fsm, struct usb_otg, fsm);
>> +	struct otg_timer *otgtimer = &otgd->timers[id];
>> +	struct hrtimer *timer = &otgtimer->timer;
>> +
>> +	if (!otgd->fsm_running)
>> +		return;
>> +
>> +	/* if timer is already active, exit */
>> +	if (hrtimer_active(timer)) {
>> +		dev_err(otgd->dev, "otg: timer %d is already running\n", id);
>> +		return;
>> +	}
>> +
>> +	hrtimer_start(timer, otgtimer->timeout, HRTIMER_MODE_REL);
>> +}
>> +
>> +/**
>> + * OTG FSM ops function to delete timer
>> + */
>> +static void usb_otg_del_timer(struct otg_fsm *fsm, enum otg_fsm_timer id)
>> +{
>> +	struct usb_otg *otgd = container_of(fsm, struct usb_otg, fsm);
>> +	struct hrtimer *timer = &otgd->timers[id].timer;
>> +
>> +	hrtimer_cancel(timer);
>> +}
>> +
>> +/**
>> + * Helper function to start/stop otg host. For use by otg controller.
>> + */
>> +int usb_otg_start_host(struct otg_fsm *fsm, int on)
>> +{
>> +	struct usb_otg *otgd = container_of(fsm, struct usb_otg, fsm);
>> +	struct otg_hcd_ops *hcd_ops;
>> +
>> +	dev_dbg(otgd->dev, "otg: %s %d\n", __func__, on);
>> +	if (!fsm->otg->host) {
>> +		WARN_ONCE(1, "otg: fsm running without host\n");
>> +		return 0;
>> +	}
>> +
>> +	if (on) {
>> +		/* start host */
>> +		hcd_ops = otgd->primary_hcd.ops;
>> +		hcd_ops->add(otgd->primary_hcd.hcd, otgd->primary_hcd.irqnum,
>> +			     otgd->primary_hcd.irqflags);
>> +		if (otgd->shared_hcd.hcd) {
>> +			hcd_ops = otgd->shared_hcd.ops;
>> +			hcd_ops->add(otgd->shared_hcd.hcd,
>> +				     otgd->shared_hcd.irqnum,
>> +				     otgd->shared_hcd.irqflags);
>> +		}
>> +	} else {
>> +		/* stop host */
>> +		if (otgd->shared_hcd.hcd) {
>> +			hcd_ops = otgd->shared_hcd.ops;
>> +			hcd_ops->remove(otgd->shared_hcd.hcd);
>> +		}
>> +		hcd_ops = otgd->primary_hcd.ops;
>> +		hcd_ops->remove(otgd->primary_hcd.hcd);
>> +	}
>> +
>> +	return 0;
>> +}
>> +EXPORT_SYMBOL_GPL(usb_otg_start_host);
>> +
>> +/**
>> + * Helper function to start/stop otg gadget. For use by otg controller.
>> + */
>> +int usb_otg_start_gadget(struct otg_fsm *fsm, int on)
>> +{
>> +	struct usb_otg *otgd = container_of(fsm, struct usb_otg, fsm);
>> +	struct usb_gadget *gadget = fsm->otg->gadget;
>> +
>> +	dev_dbg(otgd->dev, "otg: %s %d\n", __func__, on);
>> +	if (!gadget) {
>> +		WARN_ONCE(1, "otg: fsm running without gadget\n");
>> +		return 0;
>> +	}
>> +
>> +	if (on)
>> +		otgd->gadget_ops->start(fsm->otg->gadget);
>> +	else
>> +		otgd->gadget_ops->stop(fsm->otg->gadget);
>> +
>> +	return 0;
>> +}
>> +EXPORT_SYMBOL_GPL(usb_otg_start_gadget);
>> +
>> +/**
>> + * OTG FSM work function
>> + */
>> +static void usb_otg_work(struct work_struct *work)
>> +{
>> +	struct usb_otg *otgd = container_of(work, struct usb_otg, work);
>> +
> 
> Add runtime pm for it
> 	pm_runtime_get_sync(otgd->dev);
>> +	otg_statemachine(&otgd->fsm);
> 	pm_runtime_put_sync(otgd->dev);
> 

Sorry missed this one. Will add.

> Also as I raised in your previous version, we need successive state machine
> transition, how about:
> 	if (otg_statemachine(&otgd->fsm))
> 		usb_otg_sync_inputs(&otgd->fsm);

OK. will add this too.

>> +}
>> +
>> +/**
>> + * usb_otg_register() - Register the OTG device to OTG core
>> + * @dev: OTG controller device.
>> + * @config: OTG configuration.
>> + *
>> + * Register the OTG controller device with the USB OTG core.
>> + * The associated Host and Gadget controllers will be prevented from
>> + * being started till both are available for use.
>> + *
>> + * For non device tree boots, the OTG controller device must be the
>> + * parent node of the Host and Gadget controllers.
>> + *
>> + * For device tree case, the otg-controller property must be present
>> + * in the Host and Gadget controller node and it must point to the
>> + * same OTG controller node.
>> + *
>> + * Return: struct otg_fsm * if success, NULL if error.
>> + */
>> +struct otg_fsm *usb_otg_register(struct device *dev,
>> +				 struct usb_otg_config *config)
> 
> Why not return usb_otg? Since you create usb_otg which contains all stuff
> for otg.

yes, returning usb_otg makes more sense now.

> 
>> +{
>> +	struct usb_otg *otgd;
>> +	struct otg_wait_data *wait;
>> +	int ret = 0;
>> +
>> +	if (!dev || !config || !config->fsm_ops)
>> +		return ERR_PTR(-EINVAL);
>> +
>> +	/* already in list? */
>> +	mutex_lock(&otg_list_mutex);
>> +	if (usb_otg_get_data(dev)) {
>> +		dev_err(dev, "otg: %s: device already in otg list\n",
>> +			__func__);
>> +		ret = -EINVAL;
>> +		goto unlock;
>> +	}
>> +
>> +	/* allocate and add to list */
>> +	otgd = kzalloc(sizeof(*otgd), GFP_KERNEL);
>> +	if (!otgd) {
>> +		ret = -ENOMEM;
>> +		goto unlock;
>> +	}
>> +
>> +	otgd->dev = dev;
>> +	otgd->caps = &config->otg_caps;
> 
> How about define otgd->caps as a pointer, then don't need copy it.

otgd->caps is a pointer.

> 
>> +	INIT_WORK(&otgd->work, usb_otg_work);
>> +	otgd->wq = create_singlethread_workqueue("usb_otg");
>> +	if (!otgd->wq) {
>> +		dev_err(dev, "otg: %s: can't create workqueue\n",
>> +			__func__);
>> +		ret = -ENOMEM;
>> +		goto err_wq;
>> +	}
>> +
>> +	usb_otg_init_timers(otgd, config->otg_timeouts);
>> +
>> +	/* create copy of original ops */
>> +	otgd->fsm_ops = *config->fsm_ops;
> 
> The same, use a pointer is enough?

We are creating a copy because we are overriding timer ops.

> 
>> +	/* FIXME: we ignore caller's timer ops */
>> +	otgd->fsm_ops.add_timer = usb_otg_add_timer;
>> +	otgd->fsm_ops.del_timer = usb_otg_del_timer;
>> +	/* set otg ops */
>> +	otgd->fsm.ops = &otgd->fsm_ops;
>> +	otgd->fsm.otg = otgd;
>> +
>> +	mutex_init(&otgd->fsm.lock);
>> +
>> +	list_add_tail(&otgd->list, &otg_list);
>> +	mutex_unlock(&otg_list_mutex);
>> +
>> +	/* were we in wait list? */
>> +	mutex_lock(&wait_list_mutex);
>> +	wait = usb_otg_get_wait(dev);
>> +	mutex_unlock(&wait_list_mutex);
>> +	if (wait) {
>> +		/* register pending host/gadget and flush from list */
>> +		usb_otg_flush_wait(dev);
>> +	}
>> +
>> +	return &otgd->fsm;
>> +
>> +err_wq:
>> +	kfree(otgd);
>> +unlock:
>> +	mutex_unlock(&otg_list_mutex);
>> +	return ERR_PTR(ret);
>> +}
>> +EXPORT_SYMBOL_GPL(usb_otg_register);
>> +
>> +/**
>> + * usb_otg_unregister() - Unregister the OTG device from USB OTG core
>> + * @dev: OTG controller device.
>> + *
>> + * Unregister OTG controller device from USB OTG core.
>> + * Prevents unregistering till both the associated Host and Gadget controllers
>> + * have unregistered from the OTG core.
>> + *
>> + * Return: 0 on success, error value otherwise.
>> + */
>> +int usb_otg_unregister(struct device *dev)
>> +{
>> +	struct usb_otg *otgd;
>> +
>> +	mutex_lock(&otg_list_mutex);
>> +	otgd = usb_otg_get_data(dev);
>> +	if (!otgd) {
>> +		dev_err(dev, "otg: %s: device not in otg list\n",
>> +			__func__);
>> +		mutex_unlock(&otg_list_mutex);
>> +		return -EINVAL;
>> +	}
>> +
>> +	/* prevent unregister till both host & gadget have unregistered */
>> +	if (otgd->fsm.otg->host || otgd->fsm.otg->gadget) {
>> +		dev_err(dev, "otg: %s: host/gadget still registered\n",
>> +			__func__);
>> +		return -EBUSY;
>> +	}
>> +
>> +	/* OTG FSM is halted when host/gadget unregistered */
>> +	destroy_workqueue(otgd->wq);
>> +
>> +	/* remove from otg list */
>> +	list_del(&otgd->list);
>> +	kfree(otgd);
>> +	mutex_unlock(&otg_list_mutex);
>> +
>> +	return 0;
>> +}
>> +EXPORT_SYMBOL_GPL(usb_otg_unregister);
>> +
>> +/**
>> + * start/kick the OTG FSM if we can
>> + * fsm->lock must be held
>> + */
>> +static void usb_otg_start_fsm(struct otg_fsm *fsm)
>> +{
>> +	struct usb_otg *otgd = container_of(fsm, struct usb_otg, fsm);
>> +
>> +	if (otgd->fsm_running)
>> +		goto kick_fsm;
>> +
>> +	if (!fsm->otg->host) {
>> +		dev_info(otgd->dev, "otg: can't start till host registers\n");
>> +		return;
> 
> So we need register hcd(usb_otg_register_hcd) before start fsm, right?

Right. But you must use the plain old usb_add_hcd().

> 
>> +	}
>> +
>> +	if (!fsm->otg->gadget) {
>> +		dev_info(otgd->dev, "otg: can't start till gadget registers\n");
>> +		return;
>> +	}
>> +
>> +	otgd->fsm_running = true;
>> +kick_fsm:
>> +	queue_work(otgd->wq, &otgd->work);
>> +}
>> +
>> +/**
>> + * stop the OTG FSM. Stops Host & Gadget controllers as well.
>> + * fsm->lock must be held
>> + */
>> +static void usb_otg_stop_fsm(struct otg_fsm *fsm)
>> +{
>> +	struct usb_otg *otgd = container_of(fsm, struct usb_otg, fsm);
>> +	int i;
>> +
>> +	if (!otgd->fsm_running)
>> +		return;
>> +
>> +	/* no more new events queued */
>> +	otgd->fsm_running = false;
>> +
>> +	/* Stop state machine / timers */
>> +	for (i = 0; i < ARRAY_SIZE(otgd->timers); i++)
>> +		hrtimer_cancel(&otgd->timers[i].timer);
>> +
>> +	flush_workqueue(otgd->wq);
>> +	fsm->otg->state = OTG_STATE_UNDEFINED;
>> +
>> +	/* stop host/gadget immediately */
>> +	if (fsm->protocol == PROTO_HOST)
>> +		otg_start_host(fsm, 0);
>> +	else if (fsm->protocol == PROTO_GADGET)
>> +		otg_start_gadget(fsm, 0);
>> +	fsm->protocol = PROTO_UNDEF;
>> +}
>> +
>> +/**
>> + * usb_otg_sync_inputs - Sync OTG inputs with the OTG state machine
>> + * @fsm:	OTG FSM instance
>> + *
>> + * Used by the OTG driver to update the inputs to the OTG
>> + * state machine.
>> + *
>> + * Can be called in IRQ context.
>> + */
>> +void usb_otg_sync_inputs(struct otg_fsm *fsm)
>> +{
>> +	struct usb_otg *otgd = container_of(fsm, struct usb_otg, fsm);
>> +
>> +	/* Don't kick FSM till it has started */
>> +	if (!otgd->fsm_running)
>> +		return;
>> +
>> +	/* Kick FSM */
>> +	queue_work(otgd->wq, &otgd->work);
>> +}
>> +EXPORT_SYMBOL_GPL(usb_otg_sync_inputs);
>> +
>> +/**
>> + * usb_otg_kick_fsm - Kick the OTG state machine
>> + * @hcd_gcd_device:	Host/Gadget controller device
>> + *
>> + * Used by USB host/device stack to sync OTG related
>> + * events to the OTG state machine.
>> + * e.g. change in host_bus->b_hnp_enable, gadget->b_hnp_enable
>> + *
>> + * Returns: 0 on success, error value otherwise.
>> + */
>> +int usb_otg_kick_fsm(struct device *hcd_gcd_device)
>> +{
>> +	struct usb_otg *otgd;
>> +
>> +	mutex_lock(&otg_list_mutex);
>> +	otgd = usb_otg_get_data(usb_otg_get_device(hcd_gcd_device));
>> +	mutex_unlock(&otg_list_mutex);
>> +	if (!otgd) {
>> +		dev_dbg(hcd_gcd_device, "otg: %s: invalid host/gadget device\n",
>> +			__func__);
>> +		return -ENODEV;
>> +	}
>> +
>> +	usb_otg_sync_inputs(&otgd->fsm);
>> +
>> +	return 0;
>> +}
>> +EXPORT_SYMBOL_GPL(usb_otg_kick_fsm);
>> +
>> +/**
>> + * usb_otg_register_hcd - Register Host controller to OTG core
>> + * @hcd:	Host controller device
>> + * @irqnum:	interrupt number
>> + * @irqflags:	interrupt flags
>> + * @ops:	HCD ops to add/remove the HCD
>> + *
>> + * This is used by the USB Host stack to register the Host controller
>> + * to the OTG core. Host controller must not be started by the
>> + * caller as it is left upto the OTG state machine to do so.
> 
> I am confused on how to use this function.
> - This function should be called before start fsm per usb_otg_start_fsm().

yes.

> - Called by usb_add_hcd(), so we need call usb_add_hcd() before start fsm.

yes.

> - If I want to add hcd when switch to host role, and remove hcd when switch
>   to peripheral, with this design, I cannot use this function?

You add hcd only once during the life of the OTG device. If it is linked to the
OTG controller the OTG fsm manages the start/stop of hcd using the otg_hcd_ops.

"usb/core/hcd.c"
static struct otg_hcd_ops otg_hcd_intf = {
        .add = usb_otg_add_hcd,
        .remove = usb_otg_remove_hcd,
};

Your otg driver must use teh usb_otg_add/remove_hcd to start/stop the controller.
Using usb_remove_hcd() means the hcd resource is no longer available and the
otg fsm will be stopped.

> - How about split it out of usb_add_hcd()?

Adding the HCD and starting/stopping the hcd is split into
usb_add/remove_hcd() and usb_otg_add/remove_hcd() for OTG case.

> 
>> + *
>> + * Returns: 0 on success, error value otherwise.
>> + */
>> +int usb_otg_register_hcd(struct usb_hcd *hcd, unsigned int irqnum,
>> +			 unsigned long irqflags, struct otg_hcd_ops *ops)
>> +{
>> +	struct usb_otg *otgd;
>> +	struct device *hcd_dev = hcd->self.controller;
>> +	struct device *otg_dev = usb_otg_get_device(hcd_dev);
>> +
>> +	if (!otg_dev)
>> +		return -EINVAL;	/* we're definitely not OTG */
>> +
>> +	/* we're otg but otg controller might not yet be registered */
>> +	mutex_lock(&otg_list_mutex);
>> +	otgd = usb_otg_get_data(otg_dev);
>> +	mutex_unlock(&otg_list_mutex);
>> +	if (!otgd) {
>> +		dev_dbg(hcd_dev,
>> +			"otg: controller not yet registered. waiting..\n");
>> +		/*
>> +		 * otg controller might register later. Put the hcd in
>> +		 * wait list and call us back when ready
>> +		 */
>> +		if (usb_otg_hcd_wait_add(otg_dev, hcd, irqnum, irqflags, ops)) {
>> +			dev_dbg(hcd_dev, "otg: failed to add to wait list\n");
>> +			return -EINVAL;
>> +		}
>> +
>> +		return 0;
>> +	}
>> +
>> +	/* HCD will be started by OTG fsm when needed */
>> +	mutex_lock(&otgd->fsm.lock);
> 
> If call usb_add_hcd() when start host role, deadlock.

No. You must call usb_otg_add_hcd() to start host role.

> 
>> +	if (otgd->primary_hcd.hcd) {
>> +		/* probably a shared HCD ? */
>> +		if (usb_otg_hcd_is_primary_hcd(hcd)) {
>> +			dev_err(otg_dev, "otg: primary host already registered\n");
>> +			goto err;
>> +		}
>> +
>> +		if (hcd->shared_hcd == otgd->primary_hcd.hcd) {
>> +			if (otgd->shared_hcd.hcd) {
>> +				dev_err(otg_dev, "otg: shared host already registered\n");
>> +				goto err;
>> +			}
>> +
>> +			otgd->shared_hcd.hcd = hcd;
>> +			otgd->shared_hcd.irqnum = irqnum;
>> +			otgd->shared_hcd.irqflags = irqflags;
>> +			otgd->shared_hcd.ops = ops;
>> +			dev_info(otg_dev, "otg: shared host %s registered\n",
>> +				 dev_name(hcd->self.controller));
>> +		} else {
>> +			dev_err(otg_dev, "otg: invalid shared host %s\n",
>> +				dev_name(hcd->self.controller));
>> +			goto err;
>> +		}
>> +	} else {
>> +		if (!usb_otg_hcd_is_primary_hcd(hcd)) {
>> +			dev_err(otg_dev, "otg: primary host must be registered first\n");
>> +			goto err;
>> +		}
>> +
>> +		otgd->primary_hcd.hcd = hcd;
>> +		otgd->primary_hcd.irqnum = irqnum;
>> +		otgd->primary_hcd.irqflags = irqflags;
>> +		otgd->primary_hcd.ops = ops;
>> +		dev_info(otg_dev, "otg: primary host %s registered\n",
>> +			 dev_name(hcd->self.controller));
>> +	}
>> +
>> +	/*
>> +	 * we're ready only if we have shared HCD
>> +	 * or we don't need shared HCD.
>> +	 */
>> +	if (otgd->shared_hcd.hcd || !otgd->primary_hcd.hcd->shared_hcd) {
>> +		otgd->fsm.otg->host = hcd_to_bus(hcd);
> 
> otgd->host = hcd_to_bus(hcd);

ok. So we set host at both places. struct usb_otg in struct otg_fsm starts to
feel redundant now. I think we should get rid of it and get the usb_otg struct
using container_of() instead.

> 
>> +		/* FIXME: set bus->otg_port if this is true OTG port with HNP */
>> +
>> +		/* start FSM */
>> +		usb_otg_start_fsm(&otgd->fsm);
>> +	} else {
>> +		dev_dbg(otg_dev, "otg: can't start till shared host registers\n");
>> +	}
>> +
>> +	mutex_unlock(&otgd->fsm.lock);
>> +
>> +	return 0;
>> +
>> +err:
>> +	mutex_unlock(&otgd->fsm.lock);
>> +	return -EINVAL;
> 
> Return non-zero, then if err, do we need call usb_otg_add_hcd() after
> usb_otg_register_hcd() fails?

You should not call usb_otg_register_hcd() but usb_add_hcd().
If that fails then you fail as ususal.

> 
>> +}
>> +EXPORT_SYMBOL_GPL(usb_otg_register_hcd);
>> +
>> +/**
>> + * usb_otg_unregister_hcd - Unregister Host controller from OTG core
>> + * @hcd:	Host controller device
>> + *
>> + * This is used by the USB Host stack to unregister the Host controller
>> + * from the OTG core. Ensures that Host controller is not running
>> + * on successful return.
>> + *
>> + * Returns: 0 on success, error value otherwise.
>> + */
>> +int usb_otg_unregister_hcd(struct usb_hcd *hcd)
>> +{
>> +	struct usb_otg *otgd;
>> +	struct device *hcd_dev = hcd_to_bus(hcd)->controller;
>> +	struct device *otg_dev = usb_otg_get_device(hcd_dev);
>> +
>> +	if (!otg_dev)
>> +		return -EINVAL;	/* we're definitely not OTG */
>> +
>> +	mutex_lock(&otg_list_mutex);
>> +	otgd = usb_otg_get_data(otg_dev);
>> +	mutex_unlock(&otg_list_mutex);
>> +	if (!otgd) {
>> +		/* are we in wait list? */
>> +		if (!usb_otg_hcd_wait_remove(hcd))
>> +			return 0;
>> +
>> +		dev_dbg(hcd_dev, "otg: host wasn't registered with otg\n");
>> +		return -EINVAL;
>> +	}
>> +
>> +	mutex_lock(&otgd->fsm.lock);
>> +	if (hcd == otgd->primary_hcd.hcd) {
>> +		otgd->primary_hcd.hcd = NULL;
>> +		dev_info(otg_dev, "otg: primary host %s unregistered\n",
>> +			 dev_name(hcd_dev));
>> +	} else if (hcd == otgd->shared_hcd.hcd) {
>> +		otgd->shared_hcd.hcd = NULL;
>> +		dev_info(otg_dev, "otg: shared host %s unregistered\n",
>> +			 dev_name(hcd_dev));
>> +	} else {
>> +		dev_err(otg_dev, "otg: host %s wasn't registered with otg\n",
>> +			dev_name(hcd_dev));
>> +		mutex_unlock(&otgd->fsm.lock);
>> +		return -EINVAL;
>> +	}
>> +
>> +	/* stop FSM & Host */
>> +	usb_otg_stop_fsm(&otgd->fsm);
>> +	otgd->fsm.otg->host = NULL;
>> +
>> +	mutex_unlock(&otgd->fsm.lock);
>> +
>> +	return 0;
>> +}
>> +EXPORT_SYMBOL_GPL(usb_otg_unregister_hcd);
>> +
>> +/**
>> + * usb_otg_register_gadget - Register Gadget controller to OTG core
>> + * @gadget:	Gadget controller
>> + *
>> + * This is used by the USB Gadget stack to register the Gadget controller
>> + * to the OTG core. Gadget controller must not be started by the
>> + * caller as it is left upto the OTG state machine to do so.
>> + *
>> + * Gadget core must call this only when all resources required for
>> + * gadget controller to run are available.
>> + * i.e. gadget function driver is available.
>> + *
>> + * Returns: 0 on success, error value otherwise.
>> + */
>> +int usb_otg_register_gadget(struct usb_gadget *gadget,
>> +			    struct otg_gadget_ops *ops)
>> +{
>> +	struct usb_otg *otgd;
>> +	struct device *gadget_dev = &gadget->dev;
>> +	struct device *otg_dev = usb_otg_get_device(gadget_dev);
>> +
>> +	if (!otg_dev)
>> +		return -EINVAL;	/* we're definitely not OTG */
>> +
>> +	/* we're otg but otg controller might not yet be registered */
>> +	mutex_lock(&otg_list_mutex);
>> +	otgd = usb_otg_get_data(otg_dev);
>> +	mutex_unlock(&otg_list_mutex);
>> +	if (!otgd) {
>> +		dev_dbg(gadget_dev,
>> +			"otg: controller not yet registered. waiting..\n");
>> +		/*
>> +		 * otg controller might register later. Put the gadget in
>> +		 * wait list and call us back when ready
>> +		 */
>> +		if (usb_otg_gadget_wait_add(otg_dev, gadget, ops)) {
>> +			dev_dbg(gadget_dev, "otg: failed to add to wait list\n");
>> +			return -EINVAL;
>> +		}
>> +
>> +		return 0;
>> +	}
>> +
>> +	mutex_lock(&otgd->fsm.lock);
>> +	if (otgd->fsm.otg->gadget) {
>> +		dev_err(otg_dev, "otg: gadget already registered with otg\n");
>> +		mutex_unlock(&otgd->fsm.lock);
>> +		return -EINVAL;
>> +	}
>> +
>> +	otgd->fsm.otg->gadget = gadget;
>> +	otgd->gadget_ops = ops;
>> +	dev_info(otg_dev, "otg: gadget %s registered\n",
>> +		 dev_name(&gadget->dev));
>> +
>> +	/* start FSM */
>> +	usb_otg_start_fsm(&otgd->fsm);
>> +	mutex_unlock(&otgd->fsm.lock);
>> +
>> +	return 0;
>> +}
>> +EXPORT_SYMBOL_GPL(usb_otg_register_gadget);
>> +
>> +/**
>> + * usb_otg_unregister_gadget - Unregister Gadget controller from OTG core
>> + * @gadget:	Gadget controller
>> + *
>> + * This is used by the USB Gadget stack to unregister the Gadget controller
>> + * from the OTG core. Ensures that Gadget controller is not running
>> + * on successful return.
>> + *
>> + * Returns: 0 on success, error value otherwise.
>> + */
>> +int usb_otg_unregister_gadget(struct usb_gadget *gadget)
>> +{
>> +	struct usb_otg *otgd;
>> +	struct device *gadget_dev = &gadget->dev;
>> +	struct device *otg_dev = usb_otg_get_device(gadget_dev);
>> +
>> +	if (!otg_dev)
>> +		return -EINVAL;
>> +
>> +	mutex_lock(&otg_list_mutex);
>> +	otgd = usb_otg_get_data(otg_dev);
>> +	mutex_unlock(&otg_list_mutex);
>> +	if (!otgd) {
>> +		/* are we in wait list? */
>> +		if (!usb_otg_gadget_wait_remove(gadget))
>> +			return 0;
>> +
>> +		dev_dbg(gadget_dev, "otg: gadget wasn't registered with otg\n");
>> +		return -EINVAL;
>> +	}
>> +
>> +	mutex_lock(&otgd->fsm.lock);
>> +	if (otgd->fsm.otg->gadget != gadget) {
>> +		dev_err(otg_dev, "otg: gadget %s wasn't registered with otg\n",
>> +			dev_name(&gadget->dev));
>> +		mutex_unlock(&otgd->fsm.lock);
>> +		return -EINVAL;
>> +	}
>> +
>> +	/* Stop FSM & gadget */
>> +	usb_otg_stop_fsm(&otgd->fsm);
>> +	otgd->fsm.otg->gadget = NULL;
>> +	mutex_unlock(&otgd->fsm.lock);
>> +
>> +	dev_info(otg_dev, "otg: gadget %s unregistered\n",
>> +		 dev_name(&gadget->dev));
>> +
>> +	return 0;
>> +}
>> +EXPORT_SYMBOL_GPL(usb_otg_unregister_gadget);
>> +
>> +/**
>> + * usb_otg_fsm_to_dev - Get OTG controller device from struct otg_fsm
>> + * @fsm:	otg_fsm data structure
>> + *
>> + * This is used by the OTG controller driver to get it's device node
>> + * from any of the otg_fsm->ops.
>> + */
>> +struct device *usb_otg_fsm_to_dev(struct otg_fsm *fsm)
>> +{
>> +	struct usb_otg *otgd = container_of(fsm, struct usb_otg, fsm);
>> +
>> +	return otgd->dev;
>> +}
>> +EXPORT_SYMBOL_GPL(usb_otg_fsm_to_dev);
>> diff --git a/drivers/usb/common/usb-otg.h b/drivers/usb/common/usb-otg.h
>> new file mode 100644
>> index 0000000..05331f0
>> --- /dev/null
>> +++ b/drivers/usb/common/usb-otg.h
>> @@ -0,0 +1,71 @@
>> +/**
>> + * drivers/usb/common/usb-otg.h - USB OTG core local header
>> + *
>> + * Copyright (C) 2015 Texas Instruments Incorporated - http://www.ti.com
>> + * Author: Roger Quadros <rogerq@ti.com>
>> + *
>> + * This program is free software; you can redistribute it and/or modify
>> + * it under the terms of the GNU General Public License version 2 as
>> + * published by the Free Software Foundation.
>> + *
>> + * This program is distributed in the hope that it will be useful,
>> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
>> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
>> + * GNU General Public License for more details.
>> + */
>> +
>> +#ifndef __DRIVERS_USB_COMMON_USB_OTG_H
>> +#define __DRIVERS_USB_COMMON_USB_OTG_H
>> +
>> +/*
>> + *  A-DEVICE timing constants
>> + */
>> +
>> +/* Wait for VBUS Rise  */
>> +#define TA_WAIT_VRISE        (100)	/* a_wait_vrise: section 7.1.2
>> +					 * a_wait_vrise_tmr: section 7.4.5.1
>> +					 * TA_VBUS_RISE <= 100ms, section 4.4
>> +					 * Table 4-1: Electrical Characteristics
>> +					 * ->DC Electrical Timing
>> +					 */
>> +/* Wait for VBUS Fall  */
>> +#define TA_WAIT_VFALL        (1000)	/* a_wait_vfall: section 7.1.7
>> +					 * a_wait_vfall_tmr: section: 7.4.5.2
>> +					 */
>> +/* Wait for B-Connect */
>> +#define TA_WAIT_BCON         (10000)	/* a_wait_bcon: section 7.1.3
>> +					 * TA_WAIT_BCON: should be between 1100
>> +					 * and 30000 ms, section 5.5, Table 5-1
>> +					 */
>> +/* A-Idle to B-Disconnect */
>> +#define TA_AIDL_BDIS         (5000)	/* a_suspend min 200 ms, section 5.2.1
>> +					 * TA_AIDL_BDIS: section 5.5, Table 5-1
>> +					 */
>> +/* B-Idle to A-Disconnect */
>> +#define TA_BIDL_ADIS         (500)	/* TA_BIDL_ADIS: section 5.2.1
>> +					 * 500ms is used for B switch to host
>> +					 * for safe
>> +					 */
>> +
>> +/*
>> + * B-device timing constants
>> + */
>> +
>> +/* Data-Line Pulse Time*/
>> +#define TB_DATA_PLS          (10)	/* b_srp_init,continue 5~10ms
>> +					 * section:5.1.3
>> +					 */
>> +/* SRP Fail Time  */
>> +#define TB_SRP_FAIL          (6000)	/* b_srp_init,fail time 5~6s
>> +					 * section:5.1.6
>> +					 */
>> +/* A-SE0 to B-Reset  */
>> +#define TB_ASE0_BRST         (155)	/* minimum 155 ms, section:5.3.1 */
>> +/* SE0 Time Before SRP */
>> +#define TB_SE0_SRP           (1000)	/* b_idle,minimum 1s, section:5.1.2 */
>> +/* SSEND time before SRP */
>> +#define TB_SSEND_SRP         (1500)	/* minimum 1.5 sec, section:5.1.2 */
>> +
>> +#define TB_SESS_VLD          (1000)
>> +
>> +#endif /* __DRIVERS_USB_COMMON_USB_OTG_H */
>> diff --git a/drivers/usb/core/Kconfig b/drivers/usb/core/Kconfig
>> index a99c89e..b468a9f 100644
>> --- a/drivers/usb/core/Kconfig
>> +++ b/drivers/usb/core/Kconfig
>> @@ -42,7 +42,7 @@ config USB_DYNAMIC_MINORS
>>  	  If you are unsure about this, say N here.
>>  
>>  config USB_OTG
>> -	bool "OTG support"
>> +	bool "OTG/Dual-role support"
>>  	depends on PM
>>  	default n
>>  	help
>> @@ -75,15 +75,6 @@ config USB_OTG_BLACKLIST_HUB
>>  	  and software costs by not supporting external hubs.  So
>>  	  are "Embedded Hosts" that don't offer OTG support.
>>  
>> -config USB_OTG_FSM
>> -	tristate "USB 2.0 OTG FSM implementation"
>> -	depends on USB
>> -	select USB_OTG
>> -	select USB_PHY
>> -	help
>> -	  Implements OTG Finite State Machine as specified in On-The-Go
>> -	  and Embedded Host Supplement to the USB Revision 2.0 Specification.
>> -
>>  config USB_ULPI_BUS
>>  	tristate "USB ULPI PHY interface support"
>>  	depends on USB_SUPPORT
>> diff --git a/include/linux/usb/otg.h b/include/linux/usb/otg.h
>> index bd1dcf8..38cabe0 100644
>> --- a/include/linux/usb/otg.h
>> +++ b/include/linux/usb/otg.h
>> @@ -10,19 +10,100 @@
>>  #define __LINUX_USB_OTG_H
>>  
>>  #include <linux/phy/phy.h>
>> +#include <linux/device.h>
>> +#include <linux/hrtimer.h>
>> +#include <linux/ktime.h>
>> +#include <linux/usb.h>
>> +#include <linux/usb/hcd.h>
>> +#include <linux/usb/gadget.h>
>> +#include <linux/usb/otg-fsm.h>
>>  #include <linux/usb/phy.h>
>>  
>> +/**
>> + * struct otg_hcd - host controller state and interface
>> + *
>> + * @hcd: host controller
>> + * @irqnum: irq number
>> + * @irqflags: irq flags
>> + * @ops: otg to host controller interface
>> + */
>> +struct otg_hcd {
>> +	struct usb_hcd *hcd;
>> +	unsigned int irqnum;
>> +	unsigned long irqflags;
>> +	struct otg_hcd_ops *ops;
>> +};
>> +
>> +struct usb_otg;
>> +
>> +/**
>> + * struct otg_timer - otg timer data
>> + *
>> + * @timer: high resolution timer
>> + * @timeout: timeout value
>> + * @timetout_bit: pointer to variable that is set on timeout
>> + * @otgd: usb otg data
>> + */
>> +struct otg_timer {
>> +	struct hrtimer timer;
>> +	ktime_t timeout;
>> +	/* callback data */
>> +	int *timeout_bit;
>> +	struct usb_otg *otgd;
>> +};
>> +
>> +/**
>> + * struct usb_otg - usb otg controller state
>> + *
>> + * @default_a: Indicates we are an A device. i.e. Host.
>> + * @phy: USB phy interface
>> + * @usb_phy: old usb_phy interface
>> + * @host: host controller bus
>> + * @gadget: gadget device
>> + * @state: current otg state
>> + * @dev: otg controller device
>> + * @caps: otg capabilities revision, hnp, srp, etc
>> + * @fsm: otg finite state machine
>> + * @fsm_ops: controller hooks for the state machine
>> + * ------- internal use only -------
>> + * @primary_hcd: primary host state and interface
>> + * @shared_hcd: shared host state and interface
>> + * @gadget_ops: gadget interface
>> + * @timers: otg timers for state machine
>> + * @list: list of otg controllers
>> + * @work: otg state machine work
>> + * @wq: otg state machine work queue
>> + * @fsm_running: state machine running/stopped indicator
>> + */
>>  struct usb_otg {
>>  	u8			default_a;
>>  
>>  	struct phy		*phy;
>>  	/* old usb_phy interface */
>>  	struct usb_phy		*usb_phy;
>> +
> 
> add a blank line?
> 
>>  	struct usb_bus		*host;
>>  	struct usb_gadget	*gadget;
>>  
>>  	enum usb_otg_state	state;
>>  
>> +	struct device *dev;
>> +	struct usb_otg_caps *caps;
>> +	struct otg_fsm fsm;
>> +	struct otg_fsm_ops fsm_ops;
>> +
>> +	/* internal use only */
>> +	struct otg_hcd primary_hcd;
>> +	struct otg_hcd shared_hcd;
>> +	struct otg_gadget_ops *gadget_ops;
>> +	struct otg_timer timers[NUM_OTG_FSM_TIMERS];
>> +	struct list_head list;
>> +	struct work_struct work;
>> +	struct workqueue_struct *wq;
>> +	bool fsm_running;
> 
> Should fsm_running be added in otg_fsm struct?

Yes, can be moved there.

> 
>> +	/* use otg->fsm.lock for serializing access */
>> +
>> +/*------------- deprecated interface -----------------------------*/
>>  	/* bind/unbind the host controller */
>>  	int	(*set_host)(struct usb_otg *otg, struct usb_bus *host);
>>  
>> @@ -38,7 +119,7 @@ struct usb_otg {
>>  
>>  	/* start or continue HNP role switch */
>>  	int	(*start_hnp)(struct usb_otg *otg);
>> -
>> +/*---------------------------------------------------------------*/
>>  };
>>  
>>  /**
>> @@ -56,8 +137,105 @@ struct usb_otg_caps {
>>  	bool adp_support;
>>  };
>>  
>> +/**
>> + * struct usb_otg_config - otg controller configuration
>> + * @caps: otg capabilities of the controller
>> + * @ops: otg fsm operations
>> + * @otg_timeouts: override default otg fsm timeouts
>> + */
>> +struct usb_otg_config {
>> +	struct usb_otg_caps otg_caps;
>> +	struct otg_fsm_ops *fsm_ops;
>> +	unsigned otg_timeouts[NUM_OTG_FSM_TIMERS];
>> +};
>> +
>>  extern const char *usb_otg_state_string(enum usb_otg_state state);
>>  
>> +enum usb_dr_mode {
>> +	USB_DR_MODE_UNKNOWN,
>> +	USB_DR_MODE_HOST,
>> +	USB_DR_MODE_PERIPHERAL,
>> +	USB_DR_MODE_OTG,
>> +};
>> +
>> +#if IS_ENABLED(CONFIG_USB_OTG)
>> +struct otg_fsm *usb_otg_register(struct device *dev,
>> +				 struct usb_otg_config *config);
>> +int usb_otg_unregister(struct device *dev);
>> +int usb_otg_register_hcd(struct usb_hcd *hcd, unsigned int irqnum,
>> +			 unsigned long irqflags, struct otg_hcd_ops *ops);
>> +int usb_otg_unregister_hcd(struct usb_hcd *hcd);
>> +int usb_otg_register_gadget(struct usb_gadget *gadget,
>> +			    struct otg_gadget_ops *ops);
>> +int usb_otg_unregister_gadget(struct usb_gadget *gadget);
>> +void usb_otg_sync_inputs(struct otg_fsm *fsm);
>> +int usb_otg_kick_fsm(struct device *hcd_gcd_device);
>> +struct device *usb_otg_fsm_to_dev(struct otg_fsm *fsm);
>> +int usb_otg_start_host(struct otg_fsm *fsm, int on);
>> +int usb_otg_start_gadget(struct otg_fsm *fsm, int on);
>> +
>> +#else /* CONFIG_USB_OTG */
>> +
>> +static inline struct otg_fsm *usb_otg_register(struct device *dev,
>> +					       struct usb_otg_config *config)
>> +{
>> +	return ERR_PTR(-ENOTSUPP);
>> +}
>> +
>> +static inline int usb_otg_unregister(struct device *dev)
>> +{
>> +	return -ENOTSUPP;
>> +}
>> +
>> +static inline int usb_otg_register_hcd(struct usb_hcd *hcd, unsigned int irqnum,
>> +				       unsigned long irqflags,
>> +				       struct otg_hcd_ops *ops)
>> +{
>> +	return -ENOTSUPP;
>> +}
>> +
>> +static inline int usb_otg_unregister_hcd(struct usb_hcd *hcd)
>> +{
>> +	return -ENOTSUPP;
>> +}
>> +
>> +static inline int usb_otg_register_gadget(struct usb_gadget *gadget,
>> +					  struct otg_gadget_ops *ops)
>> +{
>> +	return -ENOTSUPP;
>> +}
>> +
>> +static inline int usb_otg_unregister_gadget(struct usb_gadget *gadget)
>> +{
>> +	return -ENOTSUPP;
>> +}
>> +
>> +static inline void usb_otg_sync_inputs(struct otg_fsm *fsm)
>> +{
>> +}
>> +
>> +static inline int usb_otg_kick_fsm(struct device *hcd_gcd_device)
>> +{
>> +	return -ENOTSUPP;
>> +}
>> +
>> +static inline struct device *usb_otg_fsm_to_dev(struct otg_fsm *fsm)
>> +{
>> +	return NULL;
>> +}
>> +
>> +static inline int usb_otg_start_host(struct otg_fsm *fsm, int on)
>> +{
>> +	return -ENOTSUPP;
>> +}
>> +
>> +static inline int usb_otg_start_gadget(struct otg_fsm *fsm, int on)
>> +{
>> +	return -ENOTSUPP;
>> +}
>> +#endif /* CONFIG_USB_OTG */
>> +
>> +/*------------- deprecated interface -----------------------------*/
>>  /* Context: can sleep */
>>  static inline int
>>  otg_start_hnp(struct usb_otg *otg)
>> @@ -109,14 +287,9 @@ otg_start_srp(struct usb_otg *otg)
>>  	return -ENOTSUPP;
>>  }
>>  
>> +/*---------------------------------------------------------------*/
>> +
>>  /* for OTG controller drivers (and maybe other stuff) */
>>  extern int usb_bus_start_enum(struct usb_bus *bus, unsigned port_num);
>>  
>> -enum usb_dr_mode {
>> -	USB_DR_MODE_UNKNOWN,
>> -	USB_DR_MODE_HOST,
>> -	USB_DR_MODE_PERIPHERAL,
>> -	USB_DR_MODE_OTG,
>> -};
>> -
>>  #endif /* __LINUX_USB_OTG_H */
>> -- 
>> 2.1.4
>>

^ permalink raw reply	[flat|nested] 48+ messages in thread

* Re: [PATCH v4 07/13] usb: otg: add OTG core
@ 2015-09-07 10:53         ` Roger Quadros
  0 siblings, 0 replies; 48+ messages in thread
From: Roger Quadros @ 2015-09-07 10:53 UTC (permalink / raw)
  To: Li Jun
  Cc: stern, balbi, gregkh, peter.chen, dan.j.williams, jun.li,
	mathias.nyman, tony, Joao.Pinto, abrestic, linux-usb,
	linux-kernel, linux-omap

On 07/09/15 10:40, Li Jun wrote:
> On Mon, Aug 24, 2015 at 04:21:18PM +0300, Roger Quadros wrote:
>> The OTG core instantiates the OTG Finite State Machine
>> per OTG controller and manages starting/stopping the
>> host and gadget controllers based on the bus state.
>>
>> It provides APIs for the following tasks
>>
>> - Registering an OTG capable controller
>> - Registering Host and Gadget controllers to OTG core
>> - Providing inputs to and kicking the OTG state machine
>>
>> Signed-off-by: Roger Quadros <rogerq@ti.com>
>> ---
>>  MAINTAINERS                  |    4 +-
>>  drivers/usb/Kconfig          |    2 +-
>>  drivers/usb/Makefile         |    1 +
>>  drivers/usb/common/Makefile  |    3 +-
>>  drivers/usb/common/usb-otg.c | 1061 ++++++++++++++++++++++++++++++++++++++++++
>>  drivers/usb/common/usb-otg.h |   71 +++
>>  drivers/usb/core/Kconfig     |   11 +-
>>  include/linux/usb/otg.h      |  189 +++++++-
>>  8 files changed, 1321 insertions(+), 21 deletions(-)
>>  create mode 100644 drivers/usb/common/usb-otg.c
>>  create mode 100644 drivers/usb/common/usb-otg.h
>>
> 
> ... ...
> 
>> +
>> +/**
>> + * Get OTG device from host or gadget device.
>> + *
>> + * For non device tree boot, the OTG controller is assumed to be
>> + * the parent of the host/gadget device.
> 
> This assumption/restriction maybe a problem, as I pointed in your previous
> version, usb_create_hcd() use the passed dev as its dev, but,
> usb_add_gadget_udc() use the passed dev as its parent dev, so often the
> host and gadget don't share the same parent device, at least it doesn't
> apply to chipidea case.

Let's provide a way for OTG driver to provide the OTG core exactly which is
the related host/gadget device.

> 
>> + * For device tree boot, the OTG controller is derived from the
>> + * "otg-controller" property.
>> + */
>> +static struct device *usb_otg_get_device(struct device *hcd_gcd_dev)
>> +{
>> +	struct device *otg_dev;
>> +
>> +	if (!hcd_gcd_dev)
>> +		return NULL;
>> +
>> +	if (hcd_gcd_dev->of_node) {
>> +		struct device_node *np;
>> +		struct platform_device *pdev;
>> +
>> +		np = of_parse_phandle(hcd_gcd_dev->of_node, "otg-controller",
>> +				      0);
>> +		if (!np)
>> +			goto legacy;	/* continue legacy way */
>> +
>> +		pdev = of_find_device_by_node(np);
>> +		of_node_put(np);
>> +		if (!pdev) {
>> +			dev_err(&pdev->dev, "couldn't get otg-controller device\n");
>> +			return NULL;
>> +		}
>> +
>> +		otg_dev = &pdev->dev;
>> +		return otg_dev;
>> +	}
>> +
>> +legacy:
>> +	/* otg device is parent and must be registered */
>> +	otg_dev = hcd_gcd_dev->parent;
>> +	if (!usb_otg_get_data(otg_dev))
>> +		return NULL;
>> +
>> +	return otg_dev;
>> +}
>> +
> 
> ... ...
> 
>> +static void usb_otg_init_timers(struct usb_otg *otgd, unsigned *timeouts)
>> +{
>> +	struct otg_fsm *fsm = &otgd->fsm;
>> +	unsigned long tmouts[NUM_OTG_FSM_TIMERS];
>> +	int i;
>> +
>> +	/* set default timeouts */
>> +	tmouts[A_WAIT_VRISE] = TA_WAIT_VRISE;
>> +	tmouts[A_WAIT_VFALL] = TA_WAIT_VFALL;
>> +	tmouts[A_WAIT_BCON] = TA_WAIT_BCON;
>> +	tmouts[A_AIDL_BDIS] = TA_AIDL_BDIS;
>> +	tmouts[A_BIDL_ADIS] = TA_BIDL_ADIS;
>> +	tmouts[B_ASE0_BRST] = TB_ASE0_BRST;
>> +	tmouts[B_SE0_SRP] = TB_SE0_SRP;
>> +	tmouts[B_SRP_FAIL] = TB_SRP_FAIL;
>> +
>> +	/* set controller provided timeouts */
>> +	if (timeouts) {
>> +		for (i = 0; i < NUM_OTG_FSM_TIMERS; i++) {
>> +			if (timeouts[i])
>> +				tmouts[i] = timeouts[i];
>> +		}
>> +	}
>> +
>> +	otg_timer_init(A_WAIT_VRISE, otgd, set_tmout, TA_WAIT_VRISE,
>> +		       &fsm->a_wait_vrise_tmout);
>> +	otg_timer_init(A_WAIT_VFALL, otgd, set_tmout, TA_WAIT_VFALL,
>> +		       &fsm->a_wait_vfall_tmout);
>> +	otg_timer_init(A_WAIT_BCON, otgd, set_tmout, TA_WAIT_BCON,
>> +		       &fsm->a_wait_bcon_tmout);
>> +	otg_timer_init(A_AIDL_BDIS, otgd, set_tmout, TA_AIDL_BDIS,
>> +		       &fsm->a_aidl_bdis_tmout);
>> +	otg_timer_init(A_BIDL_ADIS, otgd, set_tmout, TA_BIDL_ADIS,
>> +		       &fsm->a_bidl_adis_tmout);
>> +	otg_timer_init(B_ASE0_BRST, otgd, set_tmout, TB_ASE0_BRST,
>> +		       &fsm->b_ase0_brst_tmout);
>> +
>> +	otg_timer_init(B_SE0_SRP, otgd, set_tmout, TB_SE0_SRP,
>> +		       &fsm->b_se0_srp);
>> +	otg_timer_init(B_SRP_FAIL, otgd, set_tmout, TB_SRP_FAIL,
>> +		       &fsm->b_srp_done);
>> +
>> +	/* FIXME: what about A_WAIT_ENUM? */
> 
> Either you init it as other timers, or you remove all of it, otherwise
> there will be NULL pointer crash.

I want to initialize it but was not sure about the timeout value.
What timeout value I must use?

> 
>> +}
>> +
>> +/**
>> + * OTG FSM ops function to add timer
>> + */
>> +static void usb_otg_add_timer(struct otg_fsm *fsm, enum otg_fsm_timer id)
>> +{
>> +	struct usb_otg *otgd = container_of(fsm, struct usb_otg, fsm);
>> +	struct otg_timer *otgtimer = &otgd->timers[id];
>> +	struct hrtimer *timer = &otgtimer->timer;
>> +
>> +	if (!otgd->fsm_running)
>> +		return;
>> +
>> +	/* if timer is already active, exit */
>> +	if (hrtimer_active(timer)) {
>> +		dev_err(otgd->dev, "otg: timer %d is already running\n", id);
>> +		return;
>> +	}
>> +
>> +	hrtimer_start(timer, otgtimer->timeout, HRTIMER_MODE_REL);
>> +}
>> +
>> +/**
>> + * OTG FSM ops function to delete timer
>> + */
>> +static void usb_otg_del_timer(struct otg_fsm *fsm, enum otg_fsm_timer id)
>> +{
>> +	struct usb_otg *otgd = container_of(fsm, struct usb_otg, fsm);
>> +	struct hrtimer *timer = &otgd->timers[id].timer;
>> +
>> +	hrtimer_cancel(timer);
>> +}
>> +
>> +/**
>> + * Helper function to start/stop otg host. For use by otg controller.
>> + */
>> +int usb_otg_start_host(struct otg_fsm *fsm, int on)
>> +{
>> +	struct usb_otg *otgd = container_of(fsm, struct usb_otg, fsm);
>> +	struct otg_hcd_ops *hcd_ops;
>> +
>> +	dev_dbg(otgd->dev, "otg: %s %d\n", __func__, on);
>> +	if (!fsm->otg->host) {
>> +		WARN_ONCE(1, "otg: fsm running without host\n");
>> +		return 0;
>> +	}
>> +
>> +	if (on) {
>> +		/* start host */
>> +		hcd_ops = otgd->primary_hcd.ops;
>> +		hcd_ops->add(otgd->primary_hcd.hcd, otgd->primary_hcd.irqnum,
>> +			     otgd->primary_hcd.irqflags);
>> +		if (otgd->shared_hcd.hcd) {
>> +			hcd_ops = otgd->shared_hcd.ops;
>> +			hcd_ops->add(otgd->shared_hcd.hcd,
>> +				     otgd->shared_hcd.irqnum,
>> +				     otgd->shared_hcd.irqflags);
>> +		}
>> +	} else {
>> +		/* stop host */
>> +		if (otgd->shared_hcd.hcd) {
>> +			hcd_ops = otgd->shared_hcd.ops;
>> +			hcd_ops->remove(otgd->shared_hcd.hcd);
>> +		}
>> +		hcd_ops = otgd->primary_hcd.ops;
>> +		hcd_ops->remove(otgd->primary_hcd.hcd);
>> +	}
>> +
>> +	return 0;
>> +}
>> +EXPORT_SYMBOL_GPL(usb_otg_start_host);
>> +
>> +/**
>> + * Helper function to start/stop otg gadget. For use by otg controller.
>> + */
>> +int usb_otg_start_gadget(struct otg_fsm *fsm, int on)
>> +{
>> +	struct usb_otg *otgd = container_of(fsm, struct usb_otg, fsm);
>> +	struct usb_gadget *gadget = fsm->otg->gadget;
>> +
>> +	dev_dbg(otgd->dev, "otg: %s %d\n", __func__, on);
>> +	if (!gadget) {
>> +		WARN_ONCE(1, "otg: fsm running without gadget\n");
>> +		return 0;
>> +	}
>> +
>> +	if (on)
>> +		otgd->gadget_ops->start(fsm->otg->gadget);
>> +	else
>> +		otgd->gadget_ops->stop(fsm->otg->gadget);
>> +
>> +	return 0;
>> +}
>> +EXPORT_SYMBOL_GPL(usb_otg_start_gadget);
>> +
>> +/**
>> + * OTG FSM work function
>> + */
>> +static void usb_otg_work(struct work_struct *work)
>> +{
>> +	struct usb_otg *otgd = container_of(work, struct usb_otg, work);
>> +
> 
> Add runtime pm for it
> 	pm_runtime_get_sync(otgd->dev);
>> +	otg_statemachine(&otgd->fsm);
> 	pm_runtime_put_sync(otgd->dev);
> 

Sorry missed this one. Will add.

> Also as I raised in your previous version, we need successive state machine
> transition, how about:
> 	if (otg_statemachine(&otgd->fsm))
> 		usb_otg_sync_inputs(&otgd->fsm);

OK. will add this too.

>> +}
>> +
>> +/**
>> + * usb_otg_register() - Register the OTG device to OTG core
>> + * @dev: OTG controller device.
>> + * @config: OTG configuration.
>> + *
>> + * Register the OTG controller device with the USB OTG core.
>> + * The associated Host and Gadget controllers will be prevented from
>> + * being started till both are available for use.
>> + *
>> + * For non device tree boots, the OTG controller device must be the
>> + * parent node of the Host and Gadget controllers.
>> + *
>> + * For device tree case, the otg-controller property must be present
>> + * in the Host and Gadget controller node and it must point to the
>> + * same OTG controller node.
>> + *
>> + * Return: struct otg_fsm * if success, NULL if error.
>> + */
>> +struct otg_fsm *usb_otg_register(struct device *dev,
>> +				 struct usb_otg_config *config)
> 
> Why not return usb_otg? Since you create usb_otg which contains all stuff
> for otg.

yes, returning usb_otg makes more sense now.

> 
>> +{
>> +	struct usb_otg *otgd;
>> +	struct otg_wait_data *wait;
>> +	int ret = 0;
>> +
>> +	if (!dev || !config || !config->fsm_ops)
>> +		return ERR_PTR(-EINVAL);
>> +
>> +	/* already in list? */
>> +	mutex_lock(&otg_list_mutex);
>> +	if (usb_otg_get_data(dev)) {
>> +		dev_err(dev, "otg: %s: device already in otg list\n",
>> +			__func__);
>> +		ret = -EINVAL;
>> +		goto unlock;
>> +	}
>> +
>> +	/* allocate and add to list */
>> +	otgd = kzalloc(sizeof(*otgd), GFP_KERNEL);
>> +	if (!otgd) {
>> +		ret = -ENOMEM;
>> +		goto unlock;
>> +	}
>> +
>> +	otgd->dev = dev;
>> +	otgd->caps = &config->otg_caps;
> 
> How about define otgd->caps as a pointer, then don't need copy it.

otgd->caps is a pointer.

> 
>> +	INIT_WORK(&otgd->work, usb_otg_work);
>> +	otgd->wq = create_singlethread_workqueue("usb_otg");
>> +	if (!otgd->wq) {
>> +		dev_err(dev, "otg: %s: can't create workqueue\n",
>> +			__func__);
>> +		ret = -ENOMEM;
>> +		goto err_wq;
>> +	}
>> +
>> +	usb_otg_init_timers(otgd, config->otg_timeouts);
>> +
>> +	/* create copy of original ops */
>> +	otgd->fsm_ops = *config->fsm_ops;
> 
> The same, use a pointer is enough?

We are creating a copy because we are overriding timer ops.

> 
>> +	/* FIXME: we ignore caller's timer ops */
>> +	otgd->fsm_ops.add_timer = usb_otg_add_timer;
>> +	otgd->fsm_ops.del_timer = usb_otg_del_timer;
>> +	/* set otg ops */
>> +	otgd->fsm.ops = &otgd->fsm_ops;
>> +	otgd->fsm.otg = otgd;
>> +
>> +	mutex_init(&otgd->fsm.lock);
>> +
>> +	list_add_tail(&otgd->list, &otg_list);
>> +	mutex_unlock(&otg_list_mutex);
>> +
>> +	/* were we in wait list? */
>> +	mutex_lock(&wait_list_mutex);
>> +	wait = usb_otg_get_wait(dev);
>> +	mutex_unlock(&wait_list_mutex);
>> +	if (wait) {
>> +		/* register pending host/gadget and flush from list */
>> +		usb_otg_flush_wait(dev);
>> +	}
>> +
>> +	return &otgd->fsm;
>> +
>> +err_wq:
>> +	kfree(otgd);
>> +unlock:
>> +	mutex_unlock(&otg_list_mutex);
>> +	return ERR_PTR(ret);
>> +}
>> +EXPORT_SYMBOL_GPL(usb_otg_register);
>> +
>> +/**
>> + * usb_otg_unregister() - Unregister the OTG device from USB OTG core
>> + * @dev: OTG controller device.
>> + *
>> + * Unregister OTG controller device from USB OTG core.
>> + * Prevents unregistering till both the associated Host and Gadget controllers
>> + * have unregistered from the OTG core.
>> + *
>> + * Return: 0 on success, error value otherwise.
>> + */
>> +int usb_otg_unregister(struct device *dev)
>> +{
>> +	struct usb_otg *otgd;
>> +
>> +	mutex_lock(&otg_list_mutex);
>> +	otgd = usb_otg_get_data(dev);
>> +	if (!otgd) {
>> +		dev_err(dev, "otg: %s: device not in otg list\n",
>> +			__func__);
>> +		mutex_unlock(&otg_list_mutex);
>> +		return -EINVAL;
>> +	}
>> +
>> +	/* prevent unregister till both host & gadget have unregistered */
>> +	if (otgd->fsm.otg->host || otgd->fsm.otg->gadget) {
>> +		dev_err(dev, "otg: %s: host/gadget still registered\n",
>> +			__func__);
>> +		return -EBUSY;
>> +	}
>> +
>> +	/* OTG FSM is halted when host/gadget unregistered */
>> +	destroy_workqueue(otgd->wq);
>> +
>> +	/* remove from otg list */
>> +	list_del(&otgd->list);
>> +	kfree(otgd);
>> +	mutex_unlock(&otg_list_mutex);
>> +
>> +	return 0;
>> +}
>> +EXPORT_SYMBOL_GPL(usb_otg_unregister);
>> +
>> +/**
>> + * start/kick the OTG FSM if we can
>> + * fsm->lock must be held
>> + */
>> +static void usb_otg_start_fsm(struct otg_fsm *fsm)
>> +{
>> +	struct usb_otg *otgd = container_of(fsm, struct usb_otg, fsm);
>> +
>> +	if (otgd->fsm_running)
>> +		goto kick_fsm;
>> +
>> +	if (!fsm->otg->host) {
>> +		dev_info(otgd->dev, "otg: can't start till host registers\n");
>> +		return;
> 
> So we need register hcd(usb_otg_register_hcd) before start fsm, right?

Right. But you must use the plain old usb_add_hcd().

> 
>> +	}
>> +
>> +	if (!fsm->otg->gadget) {
>> +		dev_info(otgd->dev, "otg: can't start till gadget registers\n");
>> +		return;
>> +	}
>> +
>> +	otgd->fsm_running = true;
>> +kick_fsm:
>> +	queue_work(otgd->wq, &otgd->work);
>> +}
>> +
>> +/**
>> + * stop the OTG FSM. Stops Host & Gadget controllers as well.
>> + * fsm->lock must be held
>> + */
>> +static void usb_otg_stop_fsm(struct otg_fsm *fsm)
>> +{
>> +	struct usb_otg *otgd = container_of(fsm, struct usb_otg, fsm);
>> +	int i;
>> +
>> +	if (!otgd->fsm_running)
>> +		return;
>> +
>> +	/* no more new events queued */
>> +	otgd->fsm_running = false;
>> +
>> +	/* Stop state machine / timers */
>> +	for (i = 0; i < ARRAY_SIZE(otgd->timers); i++)
>> +		hrtimer_cancel(&otgd->timers[i].timer);
>> +
>> +	flush_workqueue(otgd->wq);
>> +	fsm->otg->state = OTG_STATE_UNDEFINED;
>> +
>> +	/* stop host/gadget immediately */
>> +	if (fsm->protocol == PROTO_HOST)
>> +		otg_start_host(fsm, 0);
>> +	else if (fsm->protocol == PROTO_GADGET)
>> +		otg_start_gadget(fsm, 0);
>> +	fsm->protocol = PROTO_UNDEF;
>> +}
>> +
>> +/**
>> + * usb_otg_sync_inputs - Sync OTG inputs with the OTG state machine
>> + * @fsm:	OTG FSM instance
>> + *
>> + * Used by the OTG driver to update the inputs to the OTG
>> + * state machine.
>> + *
>> + * Can be called in IRQ context.
>> + */
>> +void usb_otg_sync_inputs(struct otg_fsm *fsm)
>> +{
>> +	struct usb_otg *otgd = container_of(fsm, struct usb_otg, fsm);
>> +
>> +	/* Don't kick FSM till it has started */
>> +	if (!otgd->fsm_running)
>> +		return;
>> +
>> +	/* Kick FSM */
>> +	queue_work(otgd->wq, &otgd->work);
>> +}
>> +EXPORT_SYMBOL_GPL(usb_otg_sync_inputs);
>> +
>> +/**
>> + * usb_otg_kick_fsm - Kick the OTG state machine
>> + * @hcd_gcd_device:	Host/Gadget controller device
>> + *
>> + * Used by USB host/device stack to sync OTG related
>> + * events to the OTG state machine.
>> + * e.g. change in host_bus->b_hnp_enable, gadget->b_hnp_enable
>> + *
>> + * Returns: 0 on success, error value otherwise.
>> + */
>> +int usb_otg_kick_fsm(struct device *hcd_gcd_device)
>> +{
>> +	struct usb_otg *otgd;
>> +
>> +	mutex_lock(&otg_list_mutex);
>> +	otgd = usb_otg_get_data(usb_otg_get_device(hcd_gcd_device));
>> +	mutex_unlock(&otg_list_mutex);
>> +	if (!otgd) {
>> +		dev_dbg(hcd_gcd_device, "otg: %s: invalid host/gadget device\n",
>> +			__func__);
>> +		return -ENODEV;
>> +	}
>> +
>> +	usb_otg_sync_inputs(&otgd->fsm);
>> +
>> +	return 0;
>> +}
>> +EXPORT_SYMBOL_GPL(usb_otg_kick_fsm);
>> +
>> +/**
>> + * usb_otg_register_hcd - Register Host controller to OTG core
>> + * @hcd:	Host controller device
>> + * @irqnum:	interrupt number
>> + * @irqflags:	interrupt flags
>> + * @ops:	HCD ops to add/remove the HCD
>> + *
>> + * This is used by the USB Host stack to register the Host controller
>> + * to the OTG core. Host controller must not be started by the
>> + * caller as it is left upto the OTG state machine to do so.
> 
> I am confused on how to use this function.
> - This function should be called before start fsm per usb_otg_start_fsm().

yes.

> - Called by usb_add_hcd(), so we need call usb_add_hcd() before start fsm.

yes.

> - If I want to add hcd when switch to host role, and remove hcd when switch
>   to peripheral, with this design, I cannot use this function?

You add hcd only once during the life of the OTG device. If it is linked to the
OTG controller the OTG fsm manages the start/stop of hcd using the otg_hcd_ops.

"usb/core/hcd.c"
static struct otg_hcd_ops otg_hcd_intf = {
        .add = usb_otg_add_hcd,
        .remove = usb_otg_remove_hcd,
};

Your otg driver must use teh usb_otg_add/remove_hcd to start/stop the controller.
Using usb_remove_hcd() means the hcd resource is no longer available and the
otg fsm will be stopped.

> - How about split it out of usb_add_hcd()?

Adding the HCD and starting/stopping the hcd is split into
usb_add/remove_hcd() and usb_otg_add/remove_hcd() for OTG case.

> 
>> + *
>> + * Returns: 0 on success, error value otherwise.
>> + */
>> +int usb_otg_register_hcd(struct usb_hcd *hcd, unsigned int irqnum,
>> +			 unsigned long irqflags, struct otg_hcd_ops *ops)
>> +{
>> +	struct usb_otg *otgd;
>> +	struct device *hcd_dev = hcd->self.controller;
>> +	struct device *otg_dev = usb_otg_get_device(hcd_dev);
>> +
>> +	if (!otg_dev)
>> +		return -EINVAL;	/* we're definitely not OTG */
>> +
>> +	/* we're otg but otg controller might not yet be registered */
>> +	mutex_lock(&otg_list_mutex);
>> +	otgd = usb_otg_get_data(otg_dev);
>> +	mutex_unlock(&otg_list_mutex);
>> +	if (!otgd) {
>> +		dev_dbg(hcd_dev,
>> +			"otg: controller not yet registered. waiting..\n");
>> +		/*
>> +		 * otg controller might register later. Put the hcd in
>> +		 * wait list and call us back when ready
>> +		 */
>> +		if (usb_otg_hcd_wait_add(otg_dev, hcd, irqnum, irqflags, ops)) {
>> +			dev_dbg(hcd_dev, "otg: failed to add to wait list\n");
>> +			return -EINVAL;
>> +		}
>> +
>> +		return 0;
>> +	}
>> +
>> +	/* HCD will be started by OTG fsm when needed */
>> +	mutex_lock(&otgd->fsm.lock);
> 
> If call usb_add_hcd() when start host role, deadlock.

No. You must call usb_otg_add_hcd() to start host role.

> 
>> +	if (otgd->primary_hcd.hcd) {
>> +		/* probably a shared HCD ? */
>> +		if (usb_otg_hcd_is_primary_hcd(hcd)) {
>> +			dev_err(otg_dev, "otg: primary host already registered\n");
>> +			goto err;
>> +		}
>> +
>> +		if (hcd->shared_hcd == otgd->primary_hcd.hcd) {
>> +			if (otgd->shared_hcd.hcd) {
>> +				dev_err(otg_dev, "otg: shared host already registered\n");
>> +				goto err;
>> +			}
>> +
>> +			otgd->shared_hcd.hcd = hcd;
>> +			otgd->shared_hcd.irqnum = irqnum;
>> +			otgd->shared_hcd.irqflags = irqflags;
>> +			otgd->shared_hcd.ops = ops;
>> +			dev_info(otg_dev, "otg: shared host %s registered\n",
>> +				 dev_name(hcd->self.controller));
>> +		} else {
>> +			dev_err(otg_dev, "otg: invalid shared host %s\n",
>> +				dev_name(hcd->self.controller));
>> +			goto err;
>> +		}
>> +	} else {
>> +		if (!usb_otg_hcd_is_primary_hcd(hcd)) {
>> +			dev_err(otg_dev, "otg: primary host must be registered first\n");
>> +			goto err;
>> +		}
>> +
>> +		otgd->primary_hcd.hcd = hcd;
>> +		otgd->primary_hcd.irqnum = irqnum;
>> +		otgd->primary_hcd.irqflags = irqflags;
>> +		otgd->primary_hcd.ops = ops;
>> +		dev_info(otg_dev, "otg: primary host %s registered\n",
>> +			 dev_name(hcd->self.controller));
>> +	}
>> +
>> +	/*
>> +	 * we're ready only if we have shared HCD
>> +	 * or we don't need shared HCD.
>> +	 */
>> +	if (otgd->shared_hcd.hcd || !otgd->primary_hcd.hcd->shared_hcd) {
>> +		otgd->fsm.otg->host = hcd_to_bus(hcd);
> 
> otgd->host = hcd_to_bus(hcd);

ok. So we set host at both places. struct usb_otg in struct otg_fsm starts to
feel redundant now. I think we should get rid of it and get the usb_otg struct
using container_of() instead.

> 
>> +		/* FIXME: set bus->otg_port if this is true OTG port with HNP */
>> +
>> +		/* start FSM */
>> +		usb_otg_start_fsm(&otgd->fsm);
>> +	} else {
>> +		dev_dbg(otg_dev, "otg: can't start till shared host registers\n");
>> +	}
>> +
>> +	mutex_unlock(&otgd->fsm.lock);
>> +
>> +	return 0;
>> +
>> +err:
>> +	mutex_unlock(&otgd->fsm.lock);
>> +	return -EINVAL;
> 
> Return non-zero, then if err, do we need call usb_otg_add_hcd() after
> usb_otg_register_hcd() fails?

You should not call usb_otg_register_hcd() but usb_add_hcd().
If that fails then you fail as ususal.

> 
>> +}
>> +EXPORT_SYMBOL_GPL(usb_otg_register_hcd);
>> +
>> +/**
>> + * usb_otg_unregister_hcd - Unregister Host controller from OTG core
>> + * @hcd:	Host controller device
>> + *
>> + * This is used by the USB Host stack to unregister the Host controller
>> + * from the OTG core. Ensures that Host controller is not running
>> + * on successful return.
>> + *
>> + * Returns: 0 on success, error value otherwise.
>> + */
>> +int usb_otg_unregister_hcd(struct usb_hcd *hcd)
>> +{
>> +	struct usb_otg *otgd;
>> +	struct device *hcd_dev = hcd_to_bus(hcd)->controller;
>> +	struct device *otg_dev = usb_otg_get_device(hcd_dev);
>> +
>> +	if (!otg_dev)
>> +		return -EINVAL;	/* we're definitely not OTG */
>> +
>> +	mutex_lock(&otg_list_mutex);
>> +	otgd = usb_otg_get_data(otg_dev);
>> +	mutex_unlock(&otg_list_mutex);
>> +	if (!otgd) {
>> +		/* are we in wait list? */
>> +		if (!usb_otg_hcd_wait_remove(hcd))
>> +			return 0;
>> +
>> +		dev_dbg(hcd_dev, "otg: host wasn't registered with otg\n");
>> +		return -EINVAL;
>> +	}
>> +
>> +	mutex_lock(&otgd->fsm.lock);
>> +	if (hcd == otgd->primary_hcd.hcd) {
>> +		otgd->primary_hcd.hcd = NULL;
>> +		dev_info(otg_dev, "otg: primary host %s unregistered\n",
>> +			 dev_name(hcd_dev));
>> +	} else if (hcd == otgd->shared_hcd.hcd) {
>> +		otgd->shared_hcd.hcd = NULL;
>> +		dev_info(otg_dev, "otg: shared host %s unregistered\n",
>> +			 dev_name(hcd_dev));
>> +	} else {
>> +		dev_err(otg_dev, "otg: host %s wasn't registered with otg\n",
>> +			dev_name(hcd_dev));
>> +		mutex_unlock(&otgd->fsm.lock);
>> +		return -EINVAL;
>> +	}
>> +
>> +	/* stop FSM & Host */
>> +	usb_otg_stop_fsm(&otgd->fsm);
>> +	otgd->fsm.otg->host = NULL;
>> +
>> +	mutex_unlock(&otgd->fsm.lock);
>> +
>> +	return 0;
>> +}
>> +EXPORT_SYMBOL_GPL(usb_otg_unregister_hcd);
>> +
>> +/**
>> + * usb_otg_register_gadget - Register Gadget controller to OTG core
>> + * @gadget:	Gadget controller
>> + *
>> + * This is used by the USB Gadget stack to register the Gadget controller
>> + * to the OTG core. Gadget controller must not be started by the
>> + * caller as it is left upto the OTG state machine to do so.
>> + *
>> + * Gadget core must call this only when all resources required for
>> + * gadget controller to run are available.
>> + * i.e. gadget function driver is available.
>> + *
>> + * Returns: 0 on success, error value otherwise.
>> + */
>> +int usb_otg_register_gadget(struct usb_gadget *gadget,
>> +			    struct otg_gadget_ops *ops)
>> +{
>> +	struct usb_otg *otgd;
>> +	struct device *gadget_dev = &gadget->dev;
>> +	struct device *otg_dev = usb_otg_get_device(gadget_dev);
>> +
>> +	if (!otg_dev)
>> +		return -EINVAL;	/* we're definitely not OTG */
>> +
>> +	/* we're otg but otg controller might not yet be registered */
>> +	mutex_lock(&otg_list_mutex);
>> +	otgd = usb_otg_get_data(otg_dev);
>> +	mutex_unlock(&otg_list_mutex);
>> +	if (!otgd) {
>> +		dev_dbg(gadget_dev,
>> +			"otg: controller not yet registered. waiting..\n");
>> +		/*
>> +		 * otg controller might register later. Put the gadget in
>> +		 * wait list and call us back when ready
>> +		 */
>> +		if (usb_otg_gadget_wait_add(otg_dev, gadget, ops)) {
>> +			dev_dbg(gadget_dev, "otg: failed to add to wait list\n");
>> +			return -EINVAL;
>> +		}
>> +
>> +		return 0;
>> +	}
>> +
>> +	mutex_lock(&otgd->fsm.lock);
>> +	if (otgd->fsm.otg->gadget) {
>> +		dev_err(otg_dev, "otg: gadget already registered with otg\n");
>> +		mutex_unlock(&otgd->fsm.lock);
>> +		return -EINVAL;
>> +	}
>> +
>> +	otgd->fsm.otg->gadget = gadget;
>> +	otgd->gadget_ops = ops;
>> +	dev_info(otg_dev, "otg: gadget %s registered\n",
>> +		 dev_name(&gadget->dev));
>> +
>> +	/* start FSM */
>> +	usb_otg_start_fsm(&otgd->fsm);
>> +	mutex_unlock(&otgd->fsm.lock);
>> +
>> +	return 0;
>> +}
>> +EXPORT_SYMBOL_GPL(usb_otg_register_gadget);
>> +
>> +/**
>> + * usb_otg_unregister_gadget - Unregister Gadget controller from OTG core
>> + * @gadget:	Gadget controller
>> + *
>> + * This is used by the USB Gadget stack to unregister the Gadget controller
>> + * from the OTG core. Ensures that Gadget controller is not running
>> + * on successful return.
>> + *
>> + * Returns: 0 on success, error value otherwise.
>> + */
>> +int usb_otg_unregister_gadget(struct usb_gadget *gadget)
>> +{
>> +	struct usb_otg *otgd;
>> +	struct device *gadget_dev = &gadget->dev;
>> +	struct device *otg_dev = usb_otg_get_device(gadget_dev);
>> +
>> +	if (!otg_dev)
>> +		return -EINVAL;
>> +
>> +	mutex_lock(&otg_list_mutex);
>> +	otgd = usb_otg_get_data(otg_dev);
>> +	mutex_unlock(&otg_list_mutex);
>> +	if (!otgd) {
>> +		/* are we in wait list? */
>> +		if (!usb_otg_gadget_wait_remove(gadget))
>> +			return 0;
>> +
>> +		dev_dbg(gadget_dev, "otg: gadget wasn't registered with otg\n");
>> +		return -EINVAL;
>> +	}
>> +
>> +	mutex_lock(&otgd->fsm.lock);
>> +	if (otgd->fsm.otg->gadget != gadget) {
>> +		dev_err(otg_dev, "otg: gadget %s wasn't registered with otg\n",
>> +			dev_name(&gadget->dev));
>> +		mutex_unlock(&otgd->fsm.lock);
>> +		return -EINVAL;
>> +	}
>> +
>> +	/* Stop FSM & gadget */
>> +	usb_otg_stop_fsm(&otgd->fsm);
>> +	otgd->fsm.otg->gadget = NULL;
>> +	mutex_unlock(&otgd->fsm.lock);
>> +
>> +	dev_info(otg_dev, "otg: gadget %s unregistered\n",
>> +		 dev_name(&gadget->dev));
>> +
>> +	return 0;
>> +}
>> +EXPORT_SYMBOL_GPL(usb_otg_unregister_gadget);
>> +
>> +/**
>> + * usb_otg_fsm_to_dev - Get OTG controller device from struct otg_fsm
>> + * @fsm:	otg_fsm data structure
>> + *
>> + * This is used by the OTG controller driver to get it's device node
>> + * from any of the otg_fsm->ops.
>> + */
>> +struct device *usb_otg_fsm_to_dev(struct otg_fsm *fsm)
>> +{
>> +	struct usb_otg *otgd = container_of(fsm, struct usb_otg, fsm);
>> +
>> +	return otgd->dev;
>> +}
>> +EXPORT_SYMBOL_GPL(usb_otg_fsm_to_dev);
>> diff --git a/drivers/usb/common/usb-otg.h b/drivers/usb/common/usb-otg.h
>> new file mode 100644
>> index 0000000..05331f0
>> --- /dev/null
>> +++ b/drivers/usb/common/usb-otg.h
>> @@ -0,0 +1,71 @@
>> +/**
>> + * drivers/usb/common/usb-otg.h - USB OTG core local header
>> + *
>> + * Copyright (C) 2015 Texas Instruments Incorporated - http://www.ti.com
>> + * Author: Roger Quadros <rogerq@ti.com>
>> + *
>> + * This program is free software; you can redistribute it and/or modify
>> + * it under the terms of the GNU General Public License version 2 as
>> + * published by the Free Software Foundation.
>> + *
>> + * This program is distributed in the hope that it will be useful,
>> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
>> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
>> + * GNU General Public License for more details.
>> + */
>> +
>> +#ifndef __DRIVERS_USB_COMMON_USB_OTG_H
>> +#define __DRIVERS_USB_COMMON_USB_OTG_H
>> +
>> +/*
>> + *  A-DEVICE timing constants
>> + */
>> +
>> +/* Wait for VBUS Rise  */
>> +#define TA_WAIT_VRISE        (100)	/* a_wait_vrise: section 7.1.2
>> +					 * a_wait_vrise_tmr: section 7.4.5.1
>> +					 * TA_VBUS_RISE <= 100ms, section 4.4
>> +					 * Table 4-1: Electrical Characteristics
>> +					 * ->DC Electrical Timing
>> +					 */
>> +/* Wait for VBUS Fall  */
>> +#define TA_WAIT_VFALL        (1000)	/* a_wait_vfall: section 7.1.7
>> +					 * a_wait_vfall_tmr: section: 7.4.5.2
>> +					 */
>> +/* Wait for B-Connect */
>> +#define TA_WAIT_BCON         (10000)	/* a_wait_bcon: section 7.1.3
>> +					 * TA_WAIT_BCON: should be between 1100
>> +					 * and 30000 ms, section 5.5, Table 5-1
>> +					 */
>> +/* A-Idle to B-Disconnect */
>> +#define TA_AIDL_BDIS         (5000)	/* a_suspend min 200 ms, section 5.2.1
>> +					 * TA_AIDL_BDIS: section 5.5, Table 5-1
>> +					 */
>> +/* B-Idle to A-Disconnect */
>> +#define TA_BIDL_ADIS         (500)	/* TA_BIDL_ADIS: section 5.2.1
>> +					 * 500ms is used for B switch to host
>> +					 * for safe
>> +					 */
>> +
>> +/*
>> + * B-device timing constants
>> + */
>> +
>> +/* Data-Line Pulse Time*/
>> +#define TB_DATA_PLS          (10)	/* b_srp_init,continue 5~10ms
>> +					 * section:5.1.3
>> +					 */
>> +/* SRP Fail Time  */
>> +#define TB_SRP_FAIL          (6000)	/* b_srp_init,fail time 5~6s
>> +					 * section:5.1.6
>> +					 */
>> +/* A-SE0 to B-Reset  */
>> +#define TB_ASE0_BRST         (155)	/* minimum 155 ms, section:5.3.1 */
>> +/* SE0 Time Before SRP */
>> +#define TB_SE0_SRP           (1000)	/* b_idle,minimum 1s, section:5.1.2 */
>> +/* SSEND time before SRP */
>> +#define TB_SSEND_SRP         (1500)	/* minimum 1.5 sec, section:5.1.2 */
>> +
>> +#define TB_SESS_VLD          (1000)
>> +
>> +#endif /* __DRIVERS_USB_COMMON_USB_OTG_H */
>> diff --git a/drivers/usb/core/Kconfig b/drivers/usb/core/Kconfig
>> index a99c89e..b468a9f 100644
>> --- a/drivers/usb/core/Kconfig
>> +++ b/drivers/usb/core/Kconfig
>> @@ -42,7 +42,7 @@ config USB_DYNAMIC_MINORS
>>  	  If you are unsure about this, say N here.
>>  
>>  config USB_OTG
>> -	bool "OTG support"
>> +	bool "OTG/Dual-role support"
>>  	depends on PM
>>  	default n
>>  	help
>> @@ -75,15 +75,6 @@ config USB_OTG_BLACKLIST_HUB
>>  	  and software costs by not supporting external hubs.  So
>>  	  are "Embedded Hosts" that don't offer OTG support.
>>  
>> -config USB_OTG_FSM
>> -	tristate "USB 2.0 OTG FSM implementation"
>> -	depends on USB
>> -	select USB_OTG
>> -	select USB_PHY
>> -	help
>> -	  Implements OTG Finite State Machine as specified in On-The-Go
>> -	  and Embedded Host Supplement to the USB Revision 2.0 Specification.
>> -
>>  config USB_ULPI_BUS
>>  	tristate "USB ULPI PHY interface support"
>>  	depends on USB_SUPPORT
>> diff --git a/include/linux/usb/otg.h b/include/linux/usb/otg.h
>> index bd1dcf8..38cabe0 100644
>> --- a/include/linux/usb/otg.h
>> +++ b/include/linux/usb/otg.h
>> @@ -10,19 +10,100 @@
>>  #define __LINUX_USB_OTG_H
>>  
>>  #include <linux/phy/phy.h>
>> +#include <linux/device.h>
>> +#include <linux/hrtimer.h>
>> +#include <linux/ktime.h>
>> +#include <linux/usb.h>
>> +#include <linux/usb/hcd.h>
>> +#include <linux/usb/gadget.h>
>> +#include <linux/usb/otg-fsm.h>
>>  #include <linux/usb/phy.h>
>>  
>> +/**
>> + * struct otg_hcd - host controller state and interface
>> + *
>> + * @hcd: host controller
>> + * @irqnum: irq number
>> + * @irqflags: irq flags
>> + * @ops: otg to host controller interface
>> + */
>> +struct otg_hcd {
>> +	struct usb_hcd *hcd;
>> +	unsigned int irqnum;
>> +	unsigned long irqflags;
>> +	struct otg_hcd_ops *ops;
>> +};
>> +
>> +struct usb_otg;
>> +
>> +/**
>> + * struct otg_timer - otg timer data
>> + *
>> + * @timer: high resolution timer
>> + * @timeout: timeout value
>> + * @timetout_bit: pointer to variable that is set on timeout
>> + * @otgd: usb otg data
>> + */
>> +struct otg_timer {
>> +	struct hrtimer timer;
>> +	ktime_t timeout;
>> +	/* callback data */
>> +	int *timeout_bit;
>> +	struct usb_otg *otgd;
>> +};
>> +
>> +/**
>> + * struct usb_otg - usb otg controller state
>> + *
>> + * @default_a: Indicates we are an A device. i.e. Host.
>> + * @phy: USB phy interface
>> + * @usb_phy: old usb_phy interface
>> + * @host: host controller bus
>> + * @gadget: gadget device
>> + * @state: current otg state
>> + * @dev: otg controller device
>> + * @caps: otg capabilities revision, hnp, srp, etc
>> + * @fsm: otg finite state machine
>> + * @fsm_ops: controller hooks for the state machine
>> + * ------- internal use only -------
>> + * @primary_hcd: primary host state and interface
>> + * @shared_hcd: shared host state and interface
>> + * @gadget_ops: gadget interface
>> + * @timers: otg timers for state machine
>> + * @list: list of otg controllers
>> + * @work: otg state machine work
>> + * @wq: otg state machine work queue
>> + * @fsm_running: state machine running/stopped indicator
>> + */
>>  struct usb_otg {
>>  	u8			default_a;
>>  
>>  	struct phy		*phy;
>>  	/* old usb_phy interface */
>>  	struct usb_phy		*usb_phy;
>> +
> 
> add a blank line?
> 
>>  	struct usb_bus		*host;
>>  	struct usb_gadget	*gadget;
>>  
>>  	enum usb_otg_state	state;
>>  
>> +	struct device *dev;
>> +	struct usb_otg_caps *caps;
>> +	struct otg_fsm fsm;
>> +	struct otg_fsm_ops fsm_ops;
>> +
>> +	/* internal use only */
>> +	struct otg_hcd primary_hcd;
>> +	struct otg_hcd shared_hcd;
>> +	struct otg_gadget_ops *gadget_ops;
>> +	struct otg_timer timers[NUM_OTG_FSM_TIMERS];
>> +	struct list_head list;
>> +	struct work_struct work;
>> +	struct workqueue_struct *wq;
>> +	bool fsm_running;
> 
> Should fsm_running be added in otg_fsm struct?

Yes, can be moved there.

> 
>> +	/* use otg->fsm.lock for serializing access */
>> +
>> +/*------------- deprecated interface -----------------------------*/
>>  	/* bind/unbind the host controller */
>>  	int	(*set_host)(struct usb_otg *otg, struct usb_bus *host);
>>  
>> @@ -38,7 +119,7 @@ struct usb_otg {
>>  
>>  	/* start or continue HNP role switch */
>>  	int	(*start_hnp)(struct usb_otg *otg);
>> -
>> +/*---------------------------------------------------------------*/
>>  };
>>  
>>  /**
>> @@ -56,8 +137,105 @@ struct usb_otg_caps {
>>  	bool adp_support;
>>  };
>>  
>> +/**
>> + * struct usb_otg_config - otg controller configuration
>> + * @caps: otg capabilities of the controller
>> + * @ops: otg fsm operations
>> + * @otg_timeouts: override default otg fsm timeouts
>> + */
>> +struct usb_otg_config {
>> +	struct usb_otg_caps otg_caps;
>> +	struct otg_fsm_ops *fsm_ops;
>> +	unsigned otg_timeouts[NUM_OTG_FSM_TIMERS];
>> +};
>> +
>>  extern const char *usb_otg_state_string(enum usb_otg_state state);
>>  
>> +enum usb_dr_mode {
>> +	USB_DR_MODE_UNKNOWN,
>> +	USB_DR_MODE_HOST,
>> +	USB_DR_MODE_PERIPHERAL,
>> +	USB_DR_MODE_OTG,
>> +};
>> +
>> +#if IS_ENABLED(CONFIG_USB_OTG)
>> +struct otg_fsm *usb_otg_register(struct device *dev,
>> +				 struct usb_otg_config *config);
>> +int usb_otg_unregister(struct device *dev);
>> +int usb_otg_register_hcd(struct usb_hcd *hcd, unsigned int irqnum,
>> +			 unsigned long irqflags, struct otg_hcd_ops *ops);
>> +int usb_otg_unregister_hcd(struct usb_hcd *hcd);
>> +int usb_otg_register_gadget(struct usb_gadget *gadget,
>> +			    struct otg_gadget_ops *ops);
>> +int usb_otg_unregister_gadget(struct usb_gadget *gadget);
>> +void usb_otg_sync_inputs(struct otg_fsm *fsm);
>> +int usb_otg_kick_fsm(struct device *hcd_gcd_device);
>> +struct device *usb_otg_fsm_to_dev(struct otg_fsm *fsm);
>> +int usb_otg_start_host(struct otg_fsm *fsm, int on);
>> +int usb_otg_start_gadget(struct otg_fsm *fsm, int on);
>> +
>> +#else /* CONFIG_USB_OTG */
>> +
>> +static inline struct otg_fsm *usb_otg_register(struct device *dev,
>> +					       struct usb_otg_config *config)
>> +{
>> +	return ERR_PTR(-ENOTSUPP);
>> +}
>> +
>> +static inline int usb_otg_unregister(struct device *dev)
>> +{
>> +	return -ENOTSUPP;
>> +}
>> +
>> +static inline int usb_otg_register_hcd(struct usb_hcd *hcd, unsigned int irqnum,
>> +				       unsigned long irqflags,
>> +				       struct otg_hcd_ops *ops)
>> +{
>> +	return -ENOTSUPP;
>> +}
>> +
>> +static inline int usb_otg_unregister_hcd(struct usb_hcd *hcd)
>> +{
>> +	return -ENOTSUPP;
>> +}
>> +
>> +static inline int usb_otg_register_gadget(struct usb_gadget *gadget,
>> +					  struct otg_gadget_ops *ops)
>> +{
>> +	return -ENOTSUPP;
>> +}
>> +
>> +static inline int usb_otg_unregister_gadget(struct usb_gadget *gadget)
>> +{
>> +	return -ENOTSUPP;
>> +}
>> +
>> +static inline void usb_otg_sync_inputs(struct otg_fsm *fsm)
>> +{
>> +}
>> +
>> +static inline int usb_otg_kick_fsm(struct device *hcd_gcd_device)
>> +{
>> +	return -ENOTSUPP;
>> +}
>> +
>> +static inline struct device *usb_otg_fsm_to_dev(struct otg_fsm *fsm)
>> +{
>> +	return NULL;
>> +}
>> +
>> +static inline int usb_otg_start_host(struct otg_fsm *fsm, int on)
>> +{
>> +	return -ENOTSUPP;
>> +}
>> +
>> +static inline int usb_otg_start_gadget(struct otg_fsm *fsm, int on)
>> +{
>> +	return -ENOTSUPP;
>> +}
>> +#endif /* CONFIG_USB_OTG */
>> +
>> +/*------------- deprecated interface -----------------------------*/
>>  /* Context: can sleep */
>>  static inline int
>>  otg_start_hnp(struct usb_otg *otg)
>> @@ -109,14 +287,9 @@ otg_start_srp(struct usb_otg *otg)
>>  	return -ENOTSUPP;
>>  }
>>  
>> +/*---------------------------------------------------------------*/
>> +
>>  /* for OTG controller drivers (and maybe other stuff) */
>>  extern int usb_bus_start_enum(struct usb_bus *bus, unsigned port_num);
>>  
>> -enum usb_dr_mode {
>> -	USB_DR_MODE_UNKNOWN,
>> -	USB_DR_MODE_HOST,
>> -	USB_DR_MODE_PERIPHERAL,
>> -	USB_DR_MODE_OTG,
>> -};
>> -
>>  #endif /* __LINUX_USB_OTG_H */
>> -- 
>> 2.1.4
>>

^ permalink raw reply	[flat|nested] 48+ messages in thread

* Re: [PATCH v4 07/13] usb: otg: add OTG core
  2015-09-07 10:23       ` Roger Quadros
@ 2015-09-08  8:31           ` Peter Chen
  -1 siblings, 0 replies; 48+ messages in thread
From: Peter Chen @ 2015-09-08  8:31 UTC (permalink / raw)
  To: Roger Quadros
  Cc: stern-nwvwT67g6+6dFdvTe/nMLpVzexx5G7lz, balbi-l0cyMroinI0,
	gregkh-hQyY1W1yCW8ekmWlsbkhG0B+6BGkLq7r,
	dan.j.williams-ral2JQCrhuEAvxtiuMwx3w,
	jun.li-KZfg59tc24xl57MIdRCFDg,
	mathias.nyman-VuQAYsv1563Yd54FQh9/CA, tony-4v6yS6AI5VpBDgjK7y7TUQ,
	Joao.Pinto-HKixBCOQz3hWk0Htik3J/w,
	abrestic-F7+t8E8rja9g9hUCZPvPmw, linux-usb-u79uwXL29TY76Z2rM5mHXA,
	linux-kernel-u79uwXL29TY76Z2rM5mHXA,
	linux-omap-u79uwXL29TY76Z2rM5mHXA

On Mon, Sep 07, 2015 at 01:23:01PM +0300, Roger Quadros wrote:
> On 07/09/15 04:23, Peter Chen wrote:
> > On Mon, Aug 24, 2015 at 04:21:18PM +0300, Roger Quadros wrote:
> >> + * This is used by the USB Host stack to register the Host controller
> >> + * to the OTG core. Host controller must not be started by the
> >> + * caller as it is left upto the OTG state machine to do so.
> >> + *
> >> + * Returns: 0 on success, error value otherwise.
> >> + */
> >> +int usb_otg_register_hcd(struct usb_hcd *hcd, unsigned int irqnum,
> >> +			 unsigned long irqflags, struct otg_hcd_ops *ops)
> >> +{
> >> +	struct usb_otg *otgd;
> >> +	struct device *hcd_dev = hcd->self.controller;
> >> +	struct device *otg_dev = usb_otg_get_device(hcd_dev);
> >> +
> > 
> > One big problem here is: there are two designs for current (IP) driver
> > code, one creates dedicated hcd device as roothub's parent, like dwc3.
> > Another one doesn't do this, roothub's parent is core device (or otg device
> > in your patch), like chipidea and dwc2.
> > 
> > Then, otg_dev will be glue layer device for chipidea after that.
> 
> OK. Let's add a way for the otg controller driver to provide the host and gadget
> information to the otg core for such devices like chipidea and dwc2.
> 

Roger, not only chipidea and dwc2, I think the musb uses the same
hierarchy. If the host, device, and otg share the same register
region, host part can't be a platform driver since we don't want
to remap the same register region again.

So, in the design, we may need to consider both situations, one
is otg/host/device has its own register region, and host is a
separate platform device (A), the other is three parts share the
same register region, there is only one platform driver (B).

A:

			IP core device 
			    |
			    |
		      |-----|-----|
		      gadget   host platform device	
		      		|
				roothub

B:

			IP core device
			    |
			    |
		      |-----|-----|
		      gadget   	 roothub
		      		

> This API must be called before the hcd/gadget-driver is added so that the otg
> core knows it's linked to an OTG controller.
> 
> Any better idea?
> 

A flag stands for this hcd controller is the same with otg controller
can be used, this flag can be stored at struct usb_otg_config.

Peter

P.S: I still read your code, I find not all APIs in this file are used
in your dwc3 example. 
> > 
> >> +	if (!otg_dev)
> >> +		return -EINVAL;	/* we're definitely not OTG */
> >> +
> >> +	/* we're otg but otg controller might not yet be registered */
> >> +	mutex_lock(&otg_list_mutex);
> >> +	otgd = usb_otg_get_data(otg_dev);
> >> +	mutex_unlock(&otg_list_mutex);
> >> +	if (!otgd) {
> >> +		dev_dbg(hcd_dev,
> >> +			"otg: controller not yet registered. waiting..\n");
> >> +		/*
> >> +		 * otg controller might register later. Put the hcd in
> >> +		 * wait list and call us back when ready
> >> +		 */
> >> +		if (usb_otg_hcd_wait_add(otg_dev, hcd, irqnum, irqflags, ops)) {
> >> +			dev_dbg(hcd_dev, "otg: failed to add to wait list\n");
> >> +			return -EINVAL;
> >> +		}
> >> +
> >> +		return 0;
> >> +	}
> >> +
> >> +	/* HCD will be started by OTG fsm when needed */
> >> +	mutex_lock(&otgd->fsm.lock);
> >> +	if (otgd->primary_hcd.hcd) {
> >> +		/* probably a shared HCD ? */
> >> +		if (usb_otg_hcd_is_primary_hcd(hcd)) {
> >> +			dev_err(otg_dev, "otg: primary host already registered\n");
> >> +			goto err;
> >> +		}
> >> +
> >> +		if (hcd->shared_hcd == otgd->primary_hcd.hcd) {
> >> +			if (otgd->shared_hcd.hcd) {
> >> +				dev_err(otg_dev, "otg: shared host already registered\n");
> >> +				goto err;
> >> +			}
> >> +
> >> +			otgd->shared_hcd.hcd = hcd;
> >> +			otgd->shared_hcd.irqnum = irqnum;
> >> +			otgd->shared_hcd.irqflags = irqflags;
> >> +			otgd->shared_hcd.ops = ops;
> >> +			dev_info(otg_dev, "otg: shared host %s registered\n",
> >> +				 dev_name(hcd->self.controller));
> >> +		} else {
> >> +			dev_err(otg_dev, "otg: invalid shared host %s\n",
> >> +				dev_name(hcd->self.controller));
> >> +			goto err;
> >> +		}
> >> +	} else {
> >> +		if (!usb_otg_hcd_is_primary_hcd(hcd)) {
> >> +			dev_err(otg_dev, "otg: primary host must be registered first\n");
> >> +			goto err;
> >> +		}
> >> +
> >> +		otgd->primary_hcd.hcd = hcd;
> >> +		otgd->primary_hcd.irqnum = irqnum;
> >> +		otgd->primary_hcd.irqflags = irqflags;
> >> +		otgd->primary_hcd.ops = ops;
> >> +		dev_info(otg_dev, "otg: primary host %s registered\n",
> >> +			 dev_name(hcd->self.controller));
> >> +	}
> >> +
> >> +	/*
> >> +	 * we're ready only if we have shared HCD
> >> +	 * or we don't need shared HCD.
> >> +	 */
> >> +	if (otgd->shared_hcd.hcd || !otgd->primary_hcd.hcd->shared_hcd) {
> >> +		otgd->fsm.otg->host = hcd_to_bus(hcd);
> >> +		/* FIXME: set bus->otg_port if this is true OTG port with HNP */
> >> +
> >> +		/* start FSM */
> >> +		usb_otg_start_fsm(&otgd->fsm);
> >> +	} else {
> >> +		dev_dbg(otg_dev, "otg: can't start till shared host registers\n");
> >> +	}
> >> +
> >> +	mutex_unlock(&otgd->fsm.lock);
> >> +
> >> +	return 0;
> >> +
> >> +err:
> >> +	mutex_unlock(&otgd->fsm.lock);
> >> +	return -EINVAL;
> >> +}
> >> +EXPORT_SYMBOL_GPL(usb_otg_register_hcd);
> >> +
> >> +/**
> >> + * usb_otg_unregister_hcd - Unregister Host controller from OTG core
> >> + * @hcd:	Host controller device
> >> + *
> >> + * This is used by the USB Host stack to unregister the Host controller
> >> + * from the OTG core. Ensures that Host controller is not running
> >> + * on successful return.
> >> + *
> >> + * Returns: 0 on success, error value otherwise.
> >> + */
> >> +int usb_otg_unregister_hcd(struct usb_hcd *hcd)
> >> +{
> >> +	struct usb_otg *otgd;
> >> +	struct device *hcd_dev = hcd_to_bus(hcd)->controller;
> >> +	struct device *otg_dev = usb_otg_get_device(hcd_dev);
> >> +
> >> +	if (!otg_dev)
> >> +		return -EINVAL;	/* we're definitely not OTG */
> >> +
> >> +	mutex_lock(&otg_list_mutex);
> >> +	otgd = usb_otg_get_data(otg_dev);
> >> +	mutex_unlock(&otg_list_mutex);
> >> +	if (!otgd) {
> >> +		/* are we in wait list? */
> >> +		if (!usb_otg_hcd_wait_remove(hcd))
> >> +			return 0;
> >> +
> >> +		dev_dbg(hcd_dev, "otg: host wasn't registered with otg\n");
> >> +		return -EINVAL;
> >> +	}
> >> +
> >> +	mutex_lock(&otgd->fsm.lock);
> >> +	if (hcd == otgd->primary_hcd.hcd) {
> >> +		otgd->primary_hcd.hcd = NULL;
> >> +		dev_info(otg_dev, "otg: primary host %s unregistered\n",
> >> +			 dev_name(hcd_dev));
> >> +	} else if (hcd == otgd->shared_hcd.hcd) {
> >> +		otgd->shared_hcd.hcd = NULL;
> >> +		dev_info(otg_dev, "otg: shared host %s unregistered\n",
> >> +			 dev_name(hcd_dev));
> >> +	} else {
> >> +		dev_err(otg_dev, "otg: host %s wasn't registered with otg\n",
> >> +			dev_name(hcd_dev));
> >> +		mutex_unlock(&otgd->fsm.lock);
> >> +		return -EINVAL;
> >> +	}
> >> +
> >> +	/* stop FSM & Host */
> >> +	usb_otg_stop_fsm(&otgd->fsm);
> >> +	otgd->fsm.otg->host = NULL;
> >> +
> >> +	mutex_unlock(&otgd->fsm.lock);
> >> +
> >> +	return 0;
> >> +}
> >> +EXPORT_SYMBOL_GPL(usb_otg_unregister_hcd);
> >> +
> >> +/**
> >> + * usb_otg_register_gadget - Register Gadget controller to OTG core
> >> + * @gadget:	Gadget controller
> >> + *
> >> + * This is used by the USB Gadget stack to register the Gadget controller
> >> + * to the OTG core. Gadget controller must not be started by the
> >> + * caller as it is left upto the OTG state machine to do so.
> >> + *
> >> + * Gadget core must call this only when all resources required for
> >> + * gadget controller to run are available.
> >> + * i.e. gadget function driver is available.
> >> + *
> >> + * Returns: 0 on success, error value otherwise.
> >> + */
> >> +int usb_otg_register_gadget(struct usb_gadget *gadget,
> >> +			    struct otg_gadget_ops *ops)
> >> +{
> >> +	struct usb_otg *otgd;
> >> +	struct device *gadget_dev = &gadget->dev;
> >> +	struct device *otg_dev = usb_otg_get_device(gadget_dev);
> >> +
> >> +	if (!otg_dev)
> >> +		return -EINVAL;	/* we're definitely not OTG */
> >> +
> >> +	/* we're otg but otg controller might not yet be registered */
> >> +	mutex_lock(&otg_list_mutex);
> >> +	otgd = usb_otg_get_data(otg_dev);
> >> +	mutex_unlock(&otg_list_mutex);
> >> +	if (!otgd) {
> >> +		dev_dbg(gadget_dev,
> >> +			"otg: controller not yet registered. waiting..\n");
> >> +		/*
> >> +		 * otg controller might register later. Put the gadget in
> >> +		 * wait list and call us back when ready
> >> +		 */
> >> +		if (usb_otg_gadget_wait_add(otg_dev, gadget, ops)) {
> >> +			dev_dbg(gadget_dev, "otg: failed to add to wait list\n");
> >> +			return -EINVAL;
> >> +		}
> >> +
> >> +		return 0;
> >> +	}
> >> +
> >> +	mutex_lock(&otgd->fsm.lock);
> >> +	if (otgd->fsm.otg->gadget) {
> >> +		dev_err(otg_dev, "otg: gadget already registered with otg\n");
> >> +		mutex_unlock(&otgd->fsm.lock);
> >> +		return -EINVAL;
> >> +	}
> >> +
> >> +	otgd->fsm.otg->gadget = gadget;
> >> +	otgd->gadget_ops = ops;
> >> +	dev_info(otg_dev, "otg: gadget %s registered\n",
> >> +		 dev_name(&gadget->dev));
> >> +
> >> +	/* start FSM */
> >> +	usb_otg_start_fsm(&otgd->fsm);
> >> +	mutex_unlock(&otgd->fsm.lock);
> >> +
> >> +	return 0;
> >> +}
> >> +EXPORT_SYMBOL_GPL(usb_otg_register_gadget);
> >> +
> >> +/**
> >> + * usb_otg_unregister_gadget - Unregister Gadget controller from OTG core
> >> + * @gadget:	Gadget controller
> >> + *
> >> + * This is used by the USB Gadget stack to unregister the Gadget controller
> >> + * from the OTG core. Ensures that Gadget controller is not running
> >> + * on successful return.
> >> + *
> >> + * Returns: 0 on success, error value otherwise.
> >> + */
> >> +int usb_otg_unregister_gadget(struct usb_gadget *gadget)
> >> +{
> >> +	struct usb_otg *otgd;
> >> +	struct device *gadget_dev = &gadget->dev;
> >> +	struct device *otg_dev = usb_otg_get_device(gadget_dev);
> >> +
> >> +	if (!otg_dev)
> >> +		return -EINVAL;
> >> +
> >> +	mutex_lock(&otg_list_mutex);
> >> +	otgd = usb_otg_get_data(otg_dev);
> >> +	mutex_unlock(&otg_list_mutex);
> >> +	if (!otgd) {
> >> +		/* are we in wait list? */
> >> +		if (!usb_otg_gadget_wait_remove(gadget))
> >> +			return 0;
> >> +
> >> +		dev_dbg(gadget_dev, "otg: gadget wasn't registered with otg\n");
> >> +		return -EINVAL;
> >> +	}
> >> +
> >> +	mutex_lock(&otgd->fsm.lock);
> >> +	if (otgd->fsm.otg->gadget != gadget) {
> >> +		dev_err(otg_dev, "otg: gadget %s wasn't registered with otg\n",
> >> +			dev_name(&gadget->dev));
> >> +		mutex_unlock(&otgd->fsm.lock);
> >> +		return -EINVAL;
> >> +	}
> >> +
> >> +	/* Stop FSM & gadget */
> >> +	usb_otg_stop_fsm(&otgd->fsm);
> >> +	otgd->fsm.otg->gadget = NULL;
> >> +	mutex_unlock(&otgd->fsm.lock);
> >> +
> >> +	dev_info(otg_dev, "otg: gadget %s unregistered\n",
> >> +		 dev_name(&gadget->dev));
> >> +
> >> +	return 0;
> >> +}
> >> +EXPORT_SYMBOL_GPL(usb_otg_unregister_gadget);
> >> +
> >> +/**
> >> + * usb_otg_fsm_to_dev - Get OTG controller device from struct otg_fsm
> >> + * @fsm:	otg_fsm data structure
> >> + *
> >> + * This is used by the OTG controller driver to get it's device node
> >> + * from any of the otg_fsm->ops.
> >> + */
> >> +struct device *usb_otg_fsm_to_dev(struct otg_fsm *fsm)
> >> +{
> >> +	struct usb_otg *otgd = container_of(fsm, struct usb_otg, fsm);
> >> +
> >> +	return otgd->dev;
> >> +}
> >> +EXPORT_SYMBOL_GPL(usb_otg_fsm_to_dev);
> >> diff --git a/drivers/usb/common/usb-otg.h b/drivers/usb/common/usb-otg.h
> >> new file mode 100644
> >> index 0000000..05331f0
> >> --- /dev/null
> >> +++ b/drivers/usb/common/usb-otg.h
> >> @@ -0,0 +1,71 @@
> >> +/**
> >> + * drivers/usb/common/usb-otg.h - USB OTG core local header
> >> + *
> >> + * Copyright (C) 2015 Texas Instruments Incorporated - http://www.ti.com
> >> + * Author: Roger Quadros <rogerq-l0cyMroinI0@public.gmane.org>
> >> + *
> >> + * This program is free software; you can redistribute it and/or modify
> >> + * it under the terms of the GNU General Public License version 2 as
> >> + * published by the Free Software Foundation.
> >> + *
> >> + * This program is distributed in the hope that it will be useful,
> >> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> >> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> >> + * GNU General Public License for more details.
> >> + */
> >> +
> >> +#ifndef __DRIVERS_USB_COMMON_USB_OTG_H
> >> +#define __DRIVERS_USB_COMMON_USB_OTG_H
> >> +
> >> +/*
> >> + *  A-DEVICE timing constants
> >> + */
> >> +
> >> +/* Wait for VBUS Rise  */
> >> +#define TA_WAIT_VRISE        (100)	/* a_wait_vrise: section 7.1.2
> >> +					 * a_wait_vrise_tmr: section 7.4.5.1
> >> +					 * TA_VBUS_RISE <= 100ms, section 4.4
> >> +					 * Table 4-1: Electrical Characteristics
> >> +					 * ->DC Electrical Timing
> >> +					 */
> >> +/* Wait for VBUS Fall  */
> >> +#define TA_WAIT_VFALL        (1000)	/* a_wait_vfall: section 7.1.7
> >> +					 * a_wait_vfall_tmr: section: 7.4.5.2
> >> +					 */
> >> +/* Wait for B-Connect */
> >> +#define TA_WAIT_BCON         (10000)	/* a_wait_bcon: section 7.1.3
> >> +					 * TA_WAIT_BCON: should be between 1100
> >> +					 * and 30000 ms, section 5.5, Table 5-1
> >> +					 */
> >> +/* A-Idle to B-Disconnect */
> >> +#define TA_AIDL_BDIS         (5000)	/* a_suspend min 200 ms, section 5.2.1
> >> +					 * TA_AIDL_BDIS: section 5.5, Table 5-1
> >> +					 */
> >> +/* B-Idle to A-Disconnect */
> >> +#define TA_BIDL_ADIS         (500)	/* TA_BIDL_ADIS: section 5.2.1
> >> +					 * 500ms is used for B switch to host
> >> +					 * for safe
> >> +					 */
> >> +
> >> +/*
> >> + * B-device timing constants
> >> + */
> >> +
> >> +/* Data-Line Pulse Time*/
> >> +#define TB_DATA_PLS          (10)	/* b_srp_init,continue 5~10ms
> >> +					 * section:5.1.3
> >> +					 */
> >> +/* SRP Fail Time  */
> >> +#define TB_SRP_FAIL          (6000)	/* b_srp_init,fail time 5~6s
> >> +					 * section:5.1.6
> >> +					 */
> >> +/* A-SE0 to B-Reset  */
> >> +#define TB_ASE0_BRST         (155)	/* minimum 155 ms, section:5.3.1 */
> >> +/* SE0 Time Before SRP */
> >> +#define TB_SE0_SRP           (1000)	/* b_idle,minimum 1s, section:5.1.2 */
> >> +/* SSEND time before SRP */
> >> +#define TB_SSEND_SRP         (1500)	/* minimum 1.5 sec, section:5.1.2 */
> >> +
> >> +#define TB_SESS_VLD          (1000)
> >> +
> >> +#endif /* __DRIVERS_USB_COMMON_USB_OTG_H */
> >> diff --git a/drivers/usb/core/Kconfig b/drivers/usb/core/Kconfig
> >> index a99c89e..b468a9f 100644
> >> --- a/drivers/usb/core/Kconfig
> >> +++ b/drivers/usb/core/Kconfig
> >> @@ -42,7 +42,7 @@ config USB_DYNAMIC_MINORS
> >>  	  If you are unsure about this, say N here.
> >>  
> >>  config USB_OTG
> >> -	bool "OTG support"
> >> +	bool "OTG/Dual-role support"
> >>  	depends on PM
> >>  	default n
> >>  	help
> >> @@ -75,15 +75,6 @@ config USB_OTG_BLACKLIST_HUB
> >>  	  and software costs by not supporting external hubs.  So
> >>  	  are "Embedded Hosts" that don't offer OTG support.
> >>  
> >> -config USB_OTG_FSM
> >> -	tristate "USB 2.0 OTG FSM implementation"
> >> -	depends on USB
> >> -	select USB_OTG
> >> -	select USB_PHY
> >> -	help
> >> -	  Implements OTG Finite State Machine as specified in On-The-Go
> >> -	  and Embedded Host Supplement to the USB Revision 2.0 Specification.
> >> -
> >>  config USB_ULPI_BUS
> >>  	tristate "USB ULPI PHY interface support"
> >>  	depends on USB_SUPPORT
> >> diff --git a/include/linux/usb/otg.h b/include/linux/usb/otg.h
> >> index bd1dcf8..38cabe0 100644
> >> --- a/include/linux/usb/otg.h
> >> +++ b/include/linux/usb/otg.h
> >> @@ -10,19 +10,100 @@
> >>  #define __LINUX_USB_OTG_H
> >>  
> >>  #include <linux/phy/phy.h>
> >> +#include <linux/device.h>
> >> +#include <linux/hrtimer.h>
> >> +#include <linux/ktime.h>
> >> +#include <linux/usb.h>
> >> +#include <linux/usb/hcd.h>
> >> +#include <linux/usb/gadget.h>
> >> +#include <linux/usb/otg-fsm.h>
> >>  #include <linux/usb/phy.h>
> >>  
> >> +/**
> >> + * struct otg_hcd - host controller state and interface
> >> + *
> >> + * @hcd: host controller
> >> + * @irqnum: irq number
> >> + * @irqflags: irq flags
> >> + * @ops: otg to host controller interface
> >> + */
> >> +struct otg_hcd {
> >> +	struct usb_hcd *hcd;
> >> +	unsigned int irqnum;
> >> +	unsigned long irqflags;
> >> +	struct otg_hcd_ops *ops;
> >> +};
> >> +
> >> +struct usb_otg;
> >> +
> >> +/**
> >> + * struct otg_timer - otg timer data
> >> + *
> >> + * @timer: high resolution timer
> >> + * @timeout: timeout value
> >> + * @timetout_bit: pointer to variable that is set on timeout
> >> + * @otgd: usb otg data
> >> + */
> >> +struct otg_timer {
> >> +	struct hrtimer timer;
> >> +	ktime_t timeout;
> >> +	/* callback data */
> >> +	int *timeout_bit;
> >> +	struct usb_otg *otgd;
> >> +};
> >> +
> >> +/**
> >> + * struct usb_otg - usb otg controller state
> >> + *
> >> + * @default_a: Indicates we are an A device. i.e. Host.
> >> + * @phy: USB phy interface
> >> + * @usb_phy: old usb_phy interface
> >> + * @host: host controller bus
> >> + * @gadget: gadget device
> >> + * @state: current otg state
> >> + * @dev: otg controller device
> >> + * @caps: otg capabilities revision, hnp, srp, etc
> >> + * @fsm: otg finite state machine
> >> + * @fsm_ops: controller hooks for the state machine
> >> + * ------- internal use only -------
> >> + * @primary_hcd: primary host state and interface
> >> + * @shared_hcd: shared host state and interface
> >> + * @gadget_ops: gadget interface
> >> + * @timers: otg timers for state machine
> >> + * @list: list of otg controllers
> >> + * @work: otg state machine work
> >> + * @wq: otg state machine work queue
> >> + * @fsm_running: state machine running/stopped indicator
> >> + */
> >>  struct usb_otg {
> >>  	u8			default_a;
> >>  
> >>  	struct phy		*phy;
> >>  	/* old usb_phy interface */
> >>  	struct usb_phy		*usb_phy;
> >> +
> >>  	struct usb_bus		*host;
> >>  	struct usb_gadget	*gadget;
> >>  
> >>  	enum usb_otg_state	state;
> >>  
> >> +	struct device *dev;
> >> +	struct usb_otg_caps *caps;
> >> +	struct otg_fsm fsm;
> >> +	struct otg_fsm_ops fsm_ops;
> >> +
> >> +	/* internal use only */
> >> +	struct otg_hcd primary_hcd;
> >> +	struct otg_hcd shared_hcd;
> >> +	struct otg_gadget_ops *gadget_ops;
> >> +	struct otg_timer timers[NUM_OTG_FSM_TIMERS];
> >> +	struct list_head list;
> >> +	struct work_struct work;
> >> +	struct workqueue_struct *wq;
> >> +	bool fsm_running;
> >> +	/* use otg->fsm.lock for serializing access */
> >> +
> >> +/*------------- deprecated interface -----------------------------*/
> >>  	/* bind/unbind the host controller */
> >>  	int	(*set_host)(struct usb_otg *otg, struct usb_bus *host);
> >>  
> >> @@ -38,7 +119,7 @@ struct usb_otg {
> >>  
> >>  	/* start or continue HNP role switch */
> >>  	int	(*start_hnp)(struct usb_otg *otg);
> >> -
> >> +/*---------------------------------------------------------------*/
> >>  };
> >>  
> >>  /**
> >> @@ -56,8 +137,105 @@ struct usb_otg_caps {
> >>  	bool adp_support;
> >>  };
> >>  
> >> +/**
> >> + * struct usb_otg_config - otg controller configuration
> >> + * @caps: otg capabilities of the controller
> >> + * @ops: otg fsm operations
> >> + * @otg_timeouts: override default otg fsm timeouts
> >> + */
> >> +struct usb_otg_config {
> >> +	struct usb_otg_caps otg_caps;
> >> +	struct otg_fsm_ops *fsm_ops;
> >> +	unsigned otg_timeouts[NUM_OTG_FSM_TIMERS];
> >> +};
> >> +
> >>  extern const char *usb_otg_state_string(enum usb_otg_state state);
> >>  
> >> +enum usb_dr_mode {
> >> +	USB_DR_MODE_UNKNOWN,
> >> +	USB_DR_MODE_HOST,
> >> +	USB_DR_MODE_PERIPHERAL,
> >> +	USB_DR_MODE_OTG,
> >> +};
> >> +
> >> +#if IS_ENABLED(CONFIG_USB_OTG)
> >> +struct otg_fsm *usb_otg_register(struct device *dev,
> >> +				 struct usb_otg_config *config);
> >> +int usb_otg_unregister(struct device *dev);
> >> +int usb_otg_register_hcd(struct usb_hcd *hcd, unsigned int irqnum,
> >> +			 unsigned long irqflags, struct otg_hcd_ops *ops);
> >> +int usb_otg_unregister_hcd(struct usb_hcd *hcd);
> >> +int usb_otg_register_gadget(struct usb_gadget *gadget,
> >> +			    struct otg_gadget_ops *ops);
> >> +int usb_otg_unregister_gadget(struct usb_gadget *gadget);
> >> +void usb_otg_sync_inputs(struct otg_fsm *fsm);
> >> +int usb_otg_kick_fsm(struct device *hcd_gcd_device);
> >> +struct device *usb_otg_fsm_to_dev(struct otg_fsm *fsm);
> >> +int usb_otg_start_host(struct otg_fsm *fsm, int on);
> >> +int usb_otg_start_gadget(struct otg_fsm *fsm, int on);
> >> +
> >> +#else /* CONFIG_USB_OTG */
> >> +
> >> +static inline struct otg_fsm *usb_otg_register(struct device *dev,
> >> +					       struct usb_otg_config *config)
> >> +{
> >> +	return ERR_PTR(-ENOTSUPP);
> >> +}
> >> +
> >> +static inline int usb_otg_unregister(struct device *dev)
> >> +{
> >> +	return -ENOTSUPP;
> >> +}
> >> +
> >> +static inline int usb_otg_register_hcd(struct usb_hcd *hcd, unsigned int irqnum,
> >> +				       unsigned long irqflags,
> >> +				       struct otg_hcd_ops *ops)
> >> +{
> >> +	return -ENOTSUPP;
> >> +}
> >> +
> >> +static inline int usb_otg_unregister_hcd(struct usb_hcd *hcd)
> >> +{
> >> +	return -ENOTSUPP;
> >> +}
> >> +
> >> +static inline int usb_otg_register_gadget(struct usb_gadget *gadget,
> >> +					  struct otg_gadget_ops *ops)
> >> +{
> >> +	return -ENOTSUPP;
> >> +}
> >> +
> >> +static inline int usb_otg_unregister_gadget(struct usb_gadget *gadget)
> >> +{
> >> +	return -ENOTSUPP;
> >> +}
> >> +
> >> +static inline void usb_otg_sync_inputs(struct otg_fsm *fsm)
> >> +{
> >> +}
> >> +
> >> +static inline int usb_otg_kick_fsm(struct device *hcd_gcd_device)
> >> +{
> >> +	return -ENOTSUPP;
> >> +}
> >> +
> >> +static inline struct device *usb_otg_fsm_to_dev(struct otg_fsm *fsm)
> >> +{
> >> +	return NULL;
> >> +}
> >> +
> >> +static inline int usb_otg_start_host(struct otg_fsm *fsm, int on)
> >> +{
> >> +	return -ENOTSUPP;
> >> +}
> >> +
> >> +static inline int usb_otg_start_gadget(struct otg_fsm *fsm, int on)
> >> +{
> >> +	return -ENOTSUPP;
> >> +}
> >> +#endif /* CONFIG_USB_OTG */
> >> +
> >> +/*------------- deprecated interface -----------------------------*/
> >>  /* Context: can sleep */
> >>  static inline int
> >>  otg_start_hnp(struct usb_otg *otg)
> >> @@ -109,14 +287,9 @@ otg_start_srp(struct usb_otg *otg)
> >>  	return -ENOTSUPP;
> >>  }
> >>  
> >> +/*---------------------------------------------------------------*/
> >> +
> >>  /* for OTG controller drivers (and maybe other stuff) */
> >>  extern int usb_bus_start_enum(struct usb_bus *bus, unsigned port_num);
> >>  
> >> -enum usb_dr_mode {
> >> -	USB_DR_MODE_UNKNOWN,
> >> -	USB_DR_MODE_HOST,
> >> -	USB_DR_MODE_PERIPHERAL,
> >> -	USB_DR_MODE_OTG,
> >> -};
> >> -
> >>  #endif /* __LINUX_USB_OTG_H */
> >> -- 
> >> 2.1.4
> >>
> > 

-- 

Best Regards,
Peter Chen
--
To unsubscribe from this list: send the line "unsubscribe linux-usb" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply	[flat|nested] 48+ messages in thread

* Re: [PATCH v4 07/13] usb: otg: add OTG core
@ 2015-09-08  8:31           ` Peter Chen
  0 siblings, 0 replies; 48+ messages in thread
From: Peter Chen @ 2015-09-08  8:31 UTC (permalink / raw)
  To: Roger Quadros
  Cc: stern, balbi, gregkh, dan.j.williams, jun.li, mathias.nyman, tony,
	Joao.Pinto, abrestic, linux-usb, linux-kernel, linux-omap

On Mon, Sep 07, 2015 at 01:23:01PM +0300, Roger Quadros wrote:
> On 07/09/15 04:23, Peter Chen wrote:
> > On Mon, Aug 24, 2015 at 04:21:18PM +0300, Roger Quadros wrote:
> >> + * This is used by the USB Host stack to register the Host controller
> >> + * to the OTG core. Host controller must not be started by the
> >> + * caller as it is left upto the OTG state machine to do so.
> >> + *
> >> + * Returns: 0 on success, error value otherwise.
> >> + */
> >> +int usb_otg_register_hcd(struct usb_hcd *hcd, unsigned int irqnum,
> >> +			 unsigned long irqflags, struct otg_hcd_ops *ops)
> >> +{
> >> +	struct usb_otg *otgd;
> >> +	struct device *hcd_dev = hcd->self.controller;
> >> +	struct device *otg_dev = usb_otg_get_device(hcd_dev);
> >> +
> > 
> > One big problem here is: there are two designs for current (IP) driver
> > code, one creates dedicated hcd device as roothub's parent, like dwc3.
> > Another one doesn't do this, roothub's parent is core device (or otg device
> > in your patch), like chipidea and dwc2.
> > 
> > Then, otg_dev will be glue layer device for chipidea after that.
> 
> OK. Let's add a way for the otg controller driver to provide the host and gadget
> information to the otg core for such devices like chipidea and dwc2.
> 

Roger, not only chipidea and dwc2, I think the musb uses the same
hierarchy. If the host, device, and otg share the same register
region, host part can't be a platform driver since we don't want
to remap the same register region again.

So, in the design, we may need to consider both situations, one
is otg/host/device has its own register region, and host is a
separate platform device (A), the other is three parts share the
same register region, there is only one platform driver (B).

A:

			IP core device 
			    |
			    |
		      |-----|-----|
		      gadget   host platform device	
		      		|
				roothub

B:

			IP core device
			    |
			    |
		      |-----|-----|
		      gadget   	 roothub
		      		

> This API must be called before the hcd/gadget-driver is added so that the otg
> core knows it's linked to an OTG controller.
> 
> Any better idea?
> 

A flag stands for this hcd controller is the same with otg controller
can be used, this flag can be stored at struct usb_otg_config.

Peter

P.S: I still read your code, I find not all APIs in this file are used
in your dwc3 example. 
> > 
> >> +	if (!otg_dev)
> >> +		return -EINVAL;	/* we're definitely not OTG */
> >> +
> >> +	/* we're otg but otg controller might not yet be registered */
> >> +	mutex_lock(&otg_list_mutex);
> >> +	otgd = usb_otg_get_data(otg_dev);
> >> +	mutex_unlock(&otg_list_mutex);
> >> +	if (!otgd) {
> >> +		dev_dbg(hcd_dev,
> >> +			"otg: controller not yet registered. waiting..\n");
> >> +		/*
> >> +		 * otg controller might register later. Put the hcd in
> >> +		 * wait list and call us back when ready
> >> +		 */
> >> +		if (usb_otg_hcd_wait_add(otg_dev, hcd, irqnum, irqflags, ops)) {
> >> +			dev_dbg(hcd_dev, "otg: failed to add to wait list\n");
> >> +			return -EINVAL;
> >> +		}
> >> +
> >> +		return 0;
> >> +	}
> >> +
> >> +	/* HCD will be started by OTG fsm when needed */
> >> +	mutex_lock(&otgd->fsm.lock);
> >> +	if (otgd->primary_hcd.hcd) {
> >> +		/* probably a shared HCD ? */
> >> +		if (usb_otg_hcd_is_primary_hcd(hcd)) {
> >> +			dev_err(otg_dev, "otg: primary host already registered\n");
> >> +			goto err;
> >> +		}
> >> +
> >> +		if (hcd->shared_hcd == otgd->primary_hcd.hcd) {
> >> +			if (otgd->shared_hcd.hcd) {
> >> +				dev_err(otg_dev, "otg: shared host already registered\n");
> >> +				goto err;
> >> +			}
> >> +
> >> +			otgd->shared_hcd.hcd = hcd;
> >> +			otgd->shared_hcd.irqnum = irqnum;
> >> +			otgd->shared_hcd.irqflags = irqflags;
> >> +			otgd->shared_hcd.ops = ops;
> >> +			dev_info(otg_dev, "otg: shared host %s registered\n",
> >> +				 dev_name(hcd->self.controller));
> >> +		} else {
> >> +			dev_err(otg_dev, "otg: invalid shared host %s\n",
> >> +				dev_name(hcd->self.controller));
> >> +			goto err;
> >> +		}
> >> +	} else {
> >> +		if (!usb_otg_hcd_is_primary_hcd(hcd)) {
> >> +			dev_err(otg_dev, "otg: primary host must be registered first\n");
> >> +			goto err;
> >> +		}
> >> +
> >> +		otgd->primary_hcd.hcd = hcd;
> >> +		otgd->primary_hcd.irqnum = irqnum;
> >> +		otgd->primary_hcd.irqflags = irqflags;
> >> +		otgd->primary_hcd.ops = ops;
> >> +		dev_info(otg_dev, "otg: primary host %s registered\n",
> >> +			 dev_name(hcd->self.controller));
> >> +	}
> >> +
> >> +	/*
> >> +	 * we're ready only if we have shared HCD
> >> +	 * or we don't need shared HCD.
> >> +	 */
> >> +	if (otgd->shared_hcd.hcd || !otgd->primary_hcd.hcd->shared_hcd) {
> >> +		otgd->fsm.otg->host = hcd_to_bus(hcd);
> >> +		/* FIXME: set bus->otg_port if this is true OTG port with HNP */
> >> +
> >> +		/* start FSM */
> >> +		usb_otg_start_fsm(&otgd->fsm);
> >> +	} else {
> >> +		dev_dbg(otg_dev, "otg: can't start till shared host registers\n");
> >> +	}
> >> +
> >> +	mutex_unlock(&otgd->fsm.lock);
> >> +
> >> +	return 0;
> >> +
> >> +err:
> >> +	mutex_unlock(&otgd->fsm.lock);
> >> +	return -EINVAL;
> >> +}
> >> +EXPORT_SYMBOL_GPL(usb_otg_register_hcd);
> >> +
> >> +/**
> >> + * usb_otg_unregister_hcd - Unregister Host controller from OTG core
> >> + * @hcd:	Host controller device
> >> + *
> >> + * This is used by the USB Host stack to unregister the Host controller
> >> + * from the OTG core. Ensures that Host controller is not running
> >> + * on successful return.
> >> + *
> >> + * Returns: 0 on success, error value otherwise.
> >> + */
> >> +int usb_otg_unregister_hcd(struct usb_hcd *hcd)
> >> +{
> >> +	struct usb_otg *otgd;
> >> +	struct device *hcd_dev = hcd_to_bus(hcd)->controller;
> >> +	struct device *otg_dev = usb_otg_get_device(hcd_dev);
> >> +
> >> +	if (!otg_dev)
> >> +		return -EINVAL;	/* we're definitely not OTG */
> >> +
> >> +	mutex_lock(&otg_list_mutex);
> >> +	otgd = usb_otg_get_data(otg_dev);
> >> +	mutex_unlock(&otg_list_mutex);
> >> +	if (!otgd) {
> >> +		/* are we in wait list? */
> >> +		if (!usb_otg_hcd_wait_remove(hcd))
> >> +			return 0;
> >> +
> >> +		dev_dbg(hcd_dev, "otg: host wasn't registered with otg\n");
> >> +		return -EINVAL;
> >> +	}
> >> +
> >> +	mutex_lock(&otgd->fsm.lock);
> >> +	if (hcd == otgd->primary_hcd.hcd) {
> >> +		otgd->primary_hcd.hcd = NULL;
> >> +		dev_info(otg_dev, "otg: primary host %s unregistered\n",
> >> +			 dev_name(hcd_dev));
> >> +	} else if (hcd == otgd->shared_hcd.hcd) {
> >> +		otgd->shared_hcd.hcd = NULL;
> >> +		dev_info(otg_dev, "otg: shared host %s unregistered\n",
> >> +			 dev_name(hcd_dev));
> >> +	} else {
> >> +		dev_err(otg_dev, "otg: host %s wasn't registered with otg\n",
> >> +			dev_name(hcd_dev));
> >> +		mutex_unlock(&otgd->fsm.lock);
> >> +		return -EINVAL;
> >> +	}
> >> +
> >> +	/* stop FSM & Host */
> >> +	usb_otg_stop_fsm(&otgd->fsm);
> >> +	otgd->fsm.otg->host = NULL;
> >> +
> >> +	mutex_unlock(&otgd->fsm.lock);
> >> +
> >> +	return 0;
> >> +}
> >> +EXPORT_SYMBOL_GPL(usb_otg_unregister_hcd);
> >> +
> >> +/**
> >> + * usb_otg_register_gadget - Register Gadget controller to OTG core
> >> + * @gadget:	Gadget controller
> >> + *
> >> + * This is used by the USB Gadget stack to register the Gadget controller
> >> + * to the OTG core. Gadget controller must not be started by the
> >> + * caller as it is left upto the OTG state machine to do so.
> >> + *
> >> + * Gadget core must call this only when all resources required for
> >> + * gadget controller to run are available.
> >> + * i.e. gadget function driver is available.
> >> + *
> >> + * Returns: 0 on success, error value otherwise.
> >> + */
> >> +int usb_otg_register_gadget(struct usb_gadget *gadget,
> >> +			    struct otg_gadget_ops *ops)
> >> +{
> >> +	struct usb_otg *otgd;
> >> +	struct device *gadget_dev = &gadget->dev;
> >> +	struct device *otg_dev = usb_otg_get_device(gadget_dev);
> >> +
> >> +	if (!otg_dev)
> >> +		return -EINVAL;	/* we're definitely not OTG */
> >> +
> >> +	/* we're otg but otg controller might not yet be registered */
> >> +	mutex_lock(&otg_list_mutex);
> >> +	otgd = usb_otg_get_data(otg_dev);
> >> +	mutex_unlock(&otg_list_mutex);
> >> +	if (!otgd) {
> >> +		dev_dbg(gadget_dev,
> >> +			"otg: controller not yet registered. waiting..\n");
> >> +		/*
> >> +		 * otg controller might register later. Put the gadget in
> >> +		 * wait list and call us back when ready
> >> +		 */
> >> +		if (usb_otg_gadget_wait_add(otg_dev, gadget, ops)) {
> >> +			dev_dbg(gadget_dev, "otg: failed to add to wait list\n");
> >> +			return -EINVAL;
> >> +		}
> >> +
> >> +		return 0;
> >> +	}
> >> +
> >> +	mutex_lock(&otgd->fsm.lock);
> >> +	if (otgd->fsm.otg->gadget) {
> >> +		dev_err(otg_dev, "otg: gadget already registered with otg\n");
> >> +		mutex_unlock(&otgd->fsm.lock);
> >> +		return -EINVAL;
> >> +	}
> >> +
> >> +	otgd->fsm.otg->gadget = gadget;
> >> +	otgd->gadget_ops = ops;
> >> +	dev_info(otg_dev, "otg: gadget %s registered\n",
> >> +		 dev_name(&gadget->dev));
> >> +
> >> +	/* start FSM */
> >> +	usb_otg_start_fsm(&otgd->fsm);
> >> +	mutex_unlock(&otgd->fsm.lock);
> >> +
> >> +	return 0;
> >> +}
> >> +EXPORT_SYMBOL_GPL(usb_otg_register_gadget);
> >> +
> >> +/**
> >> + * usb_otg_unregister_gadget - Unregister Gadget controller from OTG core
> >> + * @gadget:	Gadget controller
> >> + *
> >> + * This is used by the USB Gadget stack to unregister the Gadget controller
> >> + * from the OTG core. Ensures that Gadget controller is not running
> >> + * on successful return.
> >> + *
> >> + * Returns: 0 on success, error value otherwise.
> >> + */
> >> +int usb_otg_unregister_gadget(struct usb_gadget *gadget)
> >> +{
> >> +	struct usb_otg *otgd;
> >> +	struct device *gadget_dev = &gadget->dev;
> >> +	struct device *otg_dev = usb_otg_get_device(gadget_dev);
> >> +
> >> +	if (!otg_dev)
> >> +		return -EINVAL;
> >> +
> >> +	mutex_lock(&otg_list_mutex);
> >> +	otgd = usb_otg_get_data(otg_dev);
> >> +	mutex_unlock(&otg_list_mutex);
> >> +	if (!otgd) {
> >> +		/* are we in wait list? */
> >> +		if (!usb_otg_gadget_wait_remove(gadget))
> >> +			return 0;
> >> +
> >> +		dev_dbg(gadget_dev, "otg: gadget wasn't registered with otg\n");
> >> +		return -EINVAL;
> >> +	}
> >> +
> >> +	mutex_lock(&otgd->fsm.lock);
> >> +	if (otgd->fsm.otg->gadget != gadget) {
> >> +		dev_err(otg_dev, "otg: gadget %s wasn't registered with otg\n",
> >> +			dev_name(&gadget->dev));
> >> +		mutex_unlock(&otgd->fsm.lock);
> >> +		return -EINVAL;
> >> +	}
> >> +
> >> +	/* Stop FSM & gadget */
> >> +	usb_otg_stop_fsm(&otgd->fsm);
> >> +	otgd->fsm.otg->gadget = NULL;
> >> +	mutex_unlock(&otgd->fsm.lock);
> >> +
> >> +	dev_info(otg_dev, "otg: gadget %s unregistered\n",
> >> +		 dev_name(&gadget->dev));
> >> +
> >> +	return 0;
> >> +}
> >> +EXPORT_SYMBOL_GPL(usb_otg_unregister_gadget);
> >> +
> >> +/**
> >> + * usb_otg_fsm_to_dev - Get OTG controller device from struct otg_fsm
> >> + * @fsm:	otg_fsm data structure
> >> + *
> >> + * This is used by the OTG controller driver to get it's device node
> >> + * from any of the otg_fsm->ops.
> >> + */
> >> +struct device *usb_otg_fsm_to_dev(struct otg_fsm *fsm)
> >> +{
> >> +	struct usb_otg *otgd = container_of(fsm, struct usb_otg, fsm);
> >> +
> >> +	return otgd->dev;
> >> +}
> >> +EXPORT_SYMBOL_GPL(usb_otg_fsm_to_dev);
> >> diff --git a/drivers/usb/common/usb-otg.h b/drivers/usb/common/usb-otg.h
> >> new file mode 100644
> >> index 0000000..05331f0
> >> --- /dev/null
> >> +++ b/drivers/usb/common/usb-otg.h
> >> @@ -0,0 +1,71 @@
> >> +/**
> >> + * drivers/usb/common/usb-otg.h - USB OTG core local header
> >> + *
> >> + * Copyright (C) 2015 Texas Instruments Incorporated - http://www.ti.com
> >> + * Author: Roger Quadros <rogerq@ti.com>
> >> + *
> >> + * This program is free software; you can redistribute it and/or modify
> >> + * it under the terms of the GNU General Public License version 2 as
> >> + * published by the Free Software Foundation.
> >> + *
> >> + * This program is distributed in the hope that it will be useful,
> >> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> >> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> >> + * GNU General Public License for more details.
> >> + */
> >> +
> >> +#ifndef __DRIVERS_USB_COMMON_USB_OTG_H
> >> +#define __DRIVERS_USB_COMMON_USB_OTG_H
> >> +
> >> +/*
> >> + *  A-DEVICE timing constants
> >> + */
> >> +
> >> +/* Wait for VBUS Rise  */
> >> +#define TA_WAIT_VRISE        (100)	/* a_wait_vrise: section 7.1.2
> >> +					 * a_wait_vrise_tmr: section 7.4.5.1
> >> +					 * TA_VBUS_RISE <= 100ms, section 4.4
> >> +					 * Table 4-1: Electrical Characteristics
> >> +					 * ->DC Electrical Timing
> >> +					 */
> >> +/* Wait for VBUS Fall  */
> >> +#define TA_WAIT_VFALL        (1000)	/* a_wait_vfall: section 7.1.7
> >> +					 * a_wait_vfall_tmr: section: 7.4.5.2
> >> +					 */
> >> +/* Wait for B-Connect */
> >> +#define TA_WAIT_BCON         (10000)	/* a_wait_bcon: section 7.1.3
> >> +					 * TA_WAIT_BCON: should be between 1100
> >> +					 * and 30000 ms, section 5.5, Table 5-1
> >> +					 */
> >> +/* A-Idle to B-Disconnect */
> >> +#define TA_AIDL_BDIS         (5000)	/* a_suspend min 200 ms, section 5.2.1
> >> +					 * TA_AIDL_BDIS: section 5.5, Table 5-1
> >> +					 */
> >> +/* B-Idle to A-Disconnect */
> >> +#define TA_BIDL_ADIS         (500)	/* TA_BIDL_ADIS: section 5.2.1
> >> +					 * 500ms is used for B switch to host
> >> +					 * for safe
> >> +					 */
> >> +
> >> +/*
> >> + * B-device timing constants
> >> + */
> >> +
> >> +/* Data-Line Pulse Time*/
> >> +#define TB_DATA_PLS          (10)	/* b_srp_init,continue 5~10ms
> >> +					 * section:5.1.3
> >> +					 */
> >> +/* SRP Fail Time  */
> >> +#define TB_SRP_FAIL          (6000)	/* b_srp_init,fail time 5~6s
> >> +					 * section:5.1.6
> >> +					 */
> >> +/* A-SE0 to B-Reset  */
> >> +#define TB_ASE0_BRST         (155)	/* minimum 155 ms, section:5.3.1 */
> >> +/* SE0 Time Before SRP */
> >> +#define TB_SE0_SRP           (1000)	/* b_idle,minimum 1s, section:5.1.2 */
> >> +/* SSEND time before SRP */
> >> +#define TB_SSEND_SRP         (1500)	/* minimum 1.5 sec, section:5.1.2 */
> >> +
> >> +#define TB_SESS_VLD          (1000)
> >> +
> >> +#endif /* __DRIVERS_USB_COMMON_USB_OTG_H */
> >> diff --git a/drivers/usb/core/Kconfig b/drivers/usb/core/Kconfig
> >> index a99c89e..b468a9f 100644
> >> --- a/drivers/usb/core/Kconfig
> >> +++ b/drivers/usb/core/Kconfig
> >> @@ -42,7 +42,7 @@ config USB_DYNAMIC_MINORS
> >>  	  If you are unsure about this, say N here.
> >>  
> >>  config USB_OTG
> >> -	bool "OTG support"
> >> +	bool "OTG/Dual-role support"
> >>  	depends on PM
> >>  	default n
> >>  	help
> >> @@ -75,15 +75,6 @@ config USB_OTG_BLACKLIST_HUB
> >>  	  and software costs by not supporting external hubs.  So
> >>  	  are "Embedded Hosts" that don't offer OTG support.
> >>  
> >> -config USB_OTG_FSM
> >> -	tristate "USB 2.0 OTG FSM implementation"
> >> -	depends on USB
> >> -	select USB_OTG
> >> -	select USB_PHY
> >> -	help
> >> -	  Implements OTG Finite State Machine as specified in On-The-Go
> >> -	  and Embedded Host Supplement to the USB Revision 2.0 Specification.
> >> -
> >>  config USB_ULPI_BUS
> >>  	tristate "USB ULPI PHY interface support"
> >>  	depends on USB_SUPPORT
> >> diff --git a/include/linux/usb/otg.h b/include/linux/usb/otg.h
> >> index bd1dcf8..38cabe0 100644
> >> --- a/include/linux/usb/otg.h
> >> +++ b/include/linux/usb/otg.h
> >> @@ -10,19 +10,100 @@
> >>  #define __LINUX_USB_OTG_H
> >>  
> >>  #include <linux/phy/phy.h>
> >> +#include <linux/device.h>
> >> +#include <linux/hrtimer.h>
> >> +#include <linux/ktime.h>
> >> +#include <linux/usb.h>
> >> +#include <linux/usb/hcd.h>
> >> +#include <linux/usb/gadget.h>
> >> +#include <linux/usb/otg-fsm.h>
> >>  #include <linux/usb/phy.h>
> >>  
> >> +/**
> >> + * struct otg_hcd - host controller state and interface
> >> + *
> >> + * @hcd: host controller
> >> + * @irqnum: irq number
> >> + * @irqflags: irq flags
> >> + * @ops: otg to host controller interface
> >> + */
> >> +struct otg_hcd {
> >> +	struct usb_hcd *hcd;
> >> +	unsigned int irqnum;
> >> +	unsigned long irqflags;
> >> +	struct otg_hcd_ops *ops;
> >> +};
> >> +
> >> +struct usb_otg;
> >> +
> >> +/**
> >> + * struct otg_timer - otg timer data
> >> + *
> >> + * @timer: high resolution timer
> >> + * @timeout: timeout value
> >> + * @timetout_bit: pointer to variable that is set on timeout
> >> + * @otgd: usb otg data
> >> + */
> >> +struct otg_timer {
> >> +	struct hrtimer timer;
> >> +	ktime_t timeout;
> >> +	/* callback data */
> >> +	int *timeout_bit;
> >> +	struct usb_otg *otgd;
> >> +};
> >> +
> >> +/**
> >> + * struct usb_otg - usb otg controller state
> >> + *
> >> + * @default_a: Indicates we are an A device. i.e. Host.
> >> + * @phy: USB phy interface
> >> + * @usb_phy: old usb_phy interface
> >> + * @host: host controller bus
> >> + * @gadget: gadget device
> >> + * @state: current otg state
> >> + * @dev: otg controller device
> >> + * @caps: otg capabilities revision, hnp, srp, etc
> >> + * @fsm: otg finite state machine
> >> + * @fsm_ops: controller hooks for the state machine
> >> + * ------- internal use only -------
> >> + * @primary_hcd: primary host state and interface
> >> + * @shared_hcd: shared host state and interface
> >> + * @gadget_ops: gadget interface
> >> + * @timers: otg timers for state machine
> >> + * @list: list of otg controllers
> >> + * @work: otg state machine work
> >> + * @wq: otg state machine work queue
> >> + * @fsm_running: state machine running/stopped indicator
> >> + */
> >>  struct usb_otg {
> >>  	u8			default_a;
> >>  
> >>  	struct phy		*phy;
> >>  	/* old usb_phy interface */
> >>  	struct usb_phy		*usb_phy;
> >> +
> >>  	struct usb_bus		*host;
> >>  	struct usb_gadget	*gadget;
> >>  
> >>  	enum usb_otg_state	state;
> >>  
> >> +	struct device *dev;
> >> +	struct usb_otg_caps *caps;
> >> +	struct otg_fsm fsm;
> >> +	struct otg_fsm_ops fsm_ops;
> >> +
> >> +	/* internal use only */
> >> +	struct otg_hcd primary_hcd;
> >> +	struct otg_hcd shared_hcd;
> >> +	struct otg_gadget_ops *gadget_ops;
> >> +	struct otg_timer timers[NUM_OTG_FSM_TIMERS];
> >> +	struct list_head list;
> >> +	struct work_struct work;
> >> +	struct workqueue_struct *wq;
> >> +	bool fsm_running;
> >> +	/* use otg->fsm.lock for serializing access */
> >> +
> >> +/*------------- deprecated interface -----------------------------*/
> >>  	/* bind/unbind the host controller */
> >>  	int	(*set_host)(struct usb_otg *otg, struct usb_bus *host);
> >>  
> >> @@ -38,7 +119,7 @@ struct usb_otg {
> >>  
> >>  	/* start or continue HNP role switch */
> >>  	int	(*start_hnp)(struct usb_otg *otg);
> >> -
> >> +/*---------------------------------------------------------------*/
> >>  };
> >>  
> >>  /**
> >> @@ -56,8 +137,105 @@ struct usb_otg_caps {
> >>  	bool adp_support;
> >>  };
> >>  
> >> +/**
> >> + * struct usb_otg_config - otg controller configuration
> >> + * @caps: otg capabilities of the controller
> >> + * @ops: otg fsm operations
> >> + * @otg_timeouts: override default otg fsm timeouts
> >> + */
> >> +struct usb_otg_config {
> >> +	struct usb_otg_caps otg_caps;
> >> +	struct otg_fsm_ops *fsm_ops;
> >> +	unsigned otg_timeouts[NUM_OTG_FSM_TIMERS];
> >> +};
> >> +
> >>  extern const char *usb_otg_state_string(enum usb_otg_state state);
> >>  
> >> +enum usb_dr_mode {
> >> +	USB_DR_MODE_UNKNOWN,
> >> +	USB_DR_MODE_HOST,
> >> +	USB_DR_MODE_PERIPHERAL,
> >> +	USB_DR_MODE_OTG,
> >> +};
> >> +
> >> +#if IS_ENABLED(CONFIG_USB_OTG)
> >> +struct otg_fsm *usb_otg_register(struct device *dev,
> >> +				 struct usb_otg_config *config);
> >> +int usb_otg_unregister(struct device *dev);
> >> +int usb_otg_register_hcd(struct usb_hcd *hcd, unsigned int irqnum,
> >> +			 unsigned long irqflags, struct otg_hcd_ops *ops);
> >> +int usb_otg_unregister_hcd(struct usb_hcd *hcd);
> >> +int usb_otg_register_gadget(struct usb_gadget *gadget,
> >> +			    struct otg_gadget_ops *ops);
> >> +int usb_otg_unregister_gadget(struct usb_gadget *gadget);
> >> +void usb_otg_sync_inputs(struct otg_fsm *fsm);
> >> +int usb_otg_kick_fsm(struct device *hcd_gcd_device);
> >> +struct device *usb_otg_fsm_to_dev(struct otg_fsm *fsm);
> >> +int usb_otg_start_host(struct otg_fsm *fsm, int on);
> >> +int usb_otg_start_gadget(struct otg_fsm *fsm, int on);
> >> +
> >> +#else /* CONFIG_USB_OTG */
> >> +
> >> +static inline struct otg_fsm *usb_otg_register(struct device *dev,
> >> +					       struct usb_otg_config *config)
> >> +{
> >> +	return ERR_PTR(-ENOTSUPP);
> >> +}
> >> +
> >> +static inline int usb_otg_unregister(struct device *dev)
> >> +{
> >> +	return -ENOTSUPP;
> >> +}
> >> +
> >> +static inline int usb_otg_register_hcd(struct usb_hcd *hcd, unsigned int irqnum,
> >> +				       unsigned long irqflags,
> >> +				       struct otg_hcd_ops *ops)
> >> +{
> >> +	return -ENOTSUPP;
> >> +}
> >> +
> >> +static inline int usb_otg_unregister_hcd(struct usb_hcd *hcd)
> >> +{
> >> +	return -ENOTSUPP;
> >> +}
> >> +
> >> +static inline int usb_otg_register_gadget(struct usb_gadget *gadget,
> >> +					  struct otg_gadget_ops *ops)
> >> +{
> >> +	return -ENOTSUPP;
> >> +}
> >> +
> >> +static inline int usb_otg_unregister_gadget(struct usb_gadget *gadget)
> >> +{
> >> +	return -ENOTSUPP;
> >> +}
> >> +
> >> +static inline void usb_otg_sync_inputs(struct otg_fsm *fsm)
> >> +{
> >> +}
> >> +
> >> +static inline int usb_otg_kick_fsm(struct device *hcd_gcd_device)
> >> +{
> >> +	return -ENOTSUPP;
> >> +}
> >> +
> >> +static inline struct device *usb_otg_fsm_to_dev(struct otg_fsm *fsm)
> >> +{
> >> +	return NULL;
> >> +}
> >> +
> >> +static inline int usb_otg_start_host(struct otg_fsm *fsm, int on)
> >> +{
> >> +	return -ENOTSUPP;
> >> +}
> >> +
> >> +static inline int usb_otg_start_gadget(struct otg_fsm *fsm, int on)
> >> +{
> >> +	return -ENOTSUPP;
> >> +}
> >> +#endif /* CONFIG_USB_OTG */
> >> +
> >> +/*------------- deprecated interface -----------------------------*/
> >>  /* Context: can sleep */
> >>  static inline int
> >>  otg_start_hnp(struct usb_otg *otg)
> >> @@ -109,14 +287,9 @@ otg_start_srp(struct usb_otg *otg)
> >>  	return -ENOTSUPP;
> >>  }
> >>  
> >> +/*---------------------------------------------------------------*/
> >> +
> >>  /* for OTG controller drivers (and maybe other stuff) */
> >>  extern int usb_bus_start_enum(struct usb_bus *bus, unsigned port_num);
> >>  
> >> -enum usb_dr_mode {
> >> -	USB_DR_MODE_UNKNOWN,
> >> -	USB_DR_MODE_HOST,
> >> -	USB_DR_MODE_PERIPHERAL,
> >> -	USB_DR_MODE_OTG,
> >> -};
> >> -
> >>  #endif /* __LINUX_USB_OTG_H */
> >> -- 
> >> 2.1.4
> >>
> > 

-- 

Best Regards,
Peter Chen

^ permalink raw reply	[flat|nested] 48+ messages in thread

* Re: [PATCH v4 07/13] usb: otg: add OTG core
  2015-09-08  8:31           ` Peter Chen
@ 2015-09-08 12:25             ` Roger Quadros
  -1 siblings, 0 replies; 48+ messages in thread
From: Roger Quadros @ 2015-09-08 12:25 UTC (permalink / raw)
  To: Peter Chen
  Cc: stern, balbi, gregkh, dan.j.williams, jun.li, mathias.nyman, tony,
	Joao.Pinto, abrestic, linux-usb, linux-kernel, linux-omap



On 08/09/15 11:31, Peter Chen wrote:
> On Mon, Sep 07, 2015 at 01:23:01PM +0300, Roger Quadros wrote:
>> On 07/09/15 04:23, Peter Chen wrote:
>>> On Mon, Aug 24, 2015 at 04:21:18PM +0300, Roger Quadros wrote:
>>>> + * This is used by the USB Host stack to register the Host controller
>>>> + * to the OTG core. Host controller must not be started by the
>>>> + * caller as it is left upto the OTG state machine to do so.
>>>> + *
>>>> + * Returns: 0 on success, error value otherwise.
>>>> + */
>>>> +int usb_otg_register_hcd(struct usb_hcd *hcd, unsigned int irqnum,
>>>> +			 unsigned long irqflags, struct otg_hcd_ops *ops)
>>>> +{
>>>> +	struct usb_otg *otgd;
>>>> +	struct device *hcd_dev = hcd->self.controller;
>>>> +	struct device *otg_dev = usb_otg_get_device(hcd_dev);
>>>> +
>>>
>>> One big problem here is: there are two designs for current (IP) driver
>>> code, one creates dedicated hcd device as roothub's parent, like dwc3.
>>> Another one doesn't do this, roothub's parent is core device (or otg device
>>> in your patch), like chipidea and dwc2.
>>>
>>> Then, otg_dev will be glue layer device for chipidea after that.
>>
>> OK. Let's add a way for the otg controller driver to provide the host and gadget
>> information to the otg core for such devices like chipidea and dwc2.
>>
> 
> Roger, not only chipidea and dwc2, I think the musb uses the same
> hierarchy. If the host, device, and otg share the same register
> region, host part can't be a platform driver since we don't want
> to remap the same register region again.
> 
> So, in the design, we may need to consider both situations, one
> is otg/host/device has its own register region, and host is a
> separate platform device (A), the other is three parts share the
> same register region, there is only one platform driver (B).
> 
> A:
> 
> 			IP core device 
> 			    |
> 			    |
> 		      |-----|-----|
> 		      gadget   host platform device	
> 		      		|
> 				roothub
> 
> B:
> 
> 			IP core device
> 			    |
> 			    |
> 		      |-----|-----|
> 		      gadget   	 roothub
> 		      		
> 
>> This API must be called before the hcd/gadget-driver is added so that the otg
>> core knows it's linked to an OTG controller.
>>
>> Any better idea?
>>
> 
> A flag stands for this hcd controller is the same with otg controller
> can be used, this flag can be stored at struct usb_otg_config.

What if there is another architecture like so?

C:
			[Parent]
			   |
			   |
		|------------------|--------------|
	[OTG core]		[gadget]	[host]

We need a more flexible mechanism to link the gadget and
host device to the otg core for non DT case.

How about adding struct usb_otg parameter to usb_otg_register_hcd()?

e.g.
int usb_otg_register_hcd(struct usb_otg *otg, struct usb_hcd *hcd, ..)

If otg is NULL it will try DT otg-controller property or parent to
get the otg controller.

> 
> Peter
> 
> P.S: I still read your code, I find not all APIs in this file are used
> in your dwc3 example. 

Which ones? The ones for registering/unregistered host/gadget are used
by hcd/udc core as part of usb_add/remove_hcd() and
udc_bind_to_driver()/usb_gadget_remove_driver()

cheers,
-roger

^ permalink raw reply	[flat|nested] 48+ messages in thread

* Re: [PATCH v4 07/13] usb: otg: add OTG core
@ 2015-09-08 12:25             ` Roger Quadros
  0 siblings, 0 replies; 48+ messages in thread
From: Roger Quadros @ 2015-09-08 12:25 UTC (permalink / raw)
  To: Peter Chen
  Cc: stern, balbi, gregkh, dan.j.williams, jun.li, mathias.nyman, tony,
	Joao.Pinto, abrestic, linux-usb, linux-kernel, linux-omap



On 08/09/15 11:31, Peter Chen wrote:
> On Mon, Sep 07, 2015 at 01:23:01PM +0300, Roger Quadros wrote:
>> On 07/09/15 04:23, Peter Chen wrote:
>>> On Mon, Aug 24, 2015 at 04:21:18PM +0300, Roger Quadros wrote:
>>>> + * This is used by the USB Host stack to register the Host controller
>>>> + * to the OTG core. Host controller must not be started by the
>>>> + * caller as it is left upto the OTG state machine to do so.
>>>> + *
>>>> + * Returns: 0 on success, error value otherwise.
>>>> + */
>>>> +int usb_otg_register_hcd(struct usb_hcd *hcd, unsigned int irqnum,
>>>> +			 unsigned long irqflags, struct otg_hcd_ops *ops)
>>>> +{
>>>> +	struct usb_otg *otgd;
>>>> +	struct device *hcd_dev = hcd->self.controller;
>>>> +	struct device *otg_dev = usb_otg_get_device(hcd_dev);
>>>> +
>>>
>>> One big problem here is: there are two designs for current (IP) driver
>>> code, one creates dedicated hcd device as roothub's parent, like dwc3.
>>> Another one doesn't do this, roothub's parent is core device (or otg device
>>> in your patch), like chipidea and dwc2.
>>>
>>> Then, otg_dev will be glue layer device for chipidea after that.
>>
>> OK. Let's add a way for the otg controller driver to provide the host and gadget
>> information to the otg core for such devices like chipidea and dwc2.
>>
> 
> Roger, not only chipidea and dwc2, I think the musb uses the same
> hierarchy. If the host, device, and otg share the same register
> region, host part can't be a platform driver since we don't want
> to remap the same register region again.
> 
> So, in the design, we may need to consider both situations, one
> is otg/host/device has its own register region, and host is a
> separate platform device (A), the other is three parts share the
> same register region, there is only one platform driver (B).
> 
> A:
> 
> 			IP core device 
> 			    |
> 			    |
> 		      |-----|-----|
> 		      gadget   host platform device	
> 		      		|
> 				roothub
> 
> B:
> 
> 			IP core device
> 			    |
> 			    |
> 		      |-----|-----|
> 		      gadget   	 roothub
> 		      		
> 
>> This API must be called before the hcd/gadget-driver is added so that the otg
>> core knows it's linked to an OTG controller.
>>
>> Any better idea?
>>
> 
> A flag stands for this hcd controller is the same with otg controller
> can be used, this flag can be stored at struct usb_otg_config.

What if there is another architecture like so?

C:
			[Parent]
			   |
			   |
		|------------------|--------------|
	[OTG core]		[gadget]	[host]

We need a more flexible mechanism to link the gadget and
host device to the otg core for non DT case.

How about adding struct usb_otg parameter to usb_otg_register_hcd()?

e.g.
int usb_otg_register_hcd(struct usb_otg *otg, struct usb_hcd *hcd, ..)

If otg is NULL it will try DT otg-controller property or parent to
get the otg controller.

> 
> Peter
> 
> P.S: I still read your code, I find not all APIs in this file are used
> in your dwc3 example. 

Which ones? The ones for registering/unregistered host/gadget are used
by hcd/udc core as part of usb_add/remove_hcd() and
udc_bind_to_driver()/usb_gadget_remove_driver()

cheers,
-roger

^ permalink raw reply	[flat|nested] 48+ messages in thread

* Re: [PATCH v4 07/13] usb: otg: add OTG core
  2015-09-08 12:25             ` Roger Quadros
@ 2015-09-08 14:34                 ` Alan Stern
  -1 siblings, 0 replies; 48+ messages in thread
From: Alan Stern @ 2015-09-08 14:34 UTC (permalink / raw)
  To: Roger Quadros
  Cc: Peter Chen, balbi-l0cyMroinI0,
	gregkh-hQyY1W1yCW8ekmWlsbkhG0B+6BGkLq7r,
	dan.j.williams-ral2JQCrhuEAvxtiuMwx3w,
	jun.li-KZfg59tc24xl57MIdRCFDg,
	mathias.nyman-VuQAYsv1563Yd54FQh9/CA, tony-4v6yS6AI5VpBDgjK7y7TUQ,
	Joao.Pinto-HKixBCOQz3hWk0Htik3J/w,
	abrestic-F7+t8E8rja9g9hUCZPvPmw, linux-usb-u79uwXL29TY76Z2rM5mHXA,
	linux-kernel-u79uwXL29TY76Z2rM5mHXA,
	linux-omap-u79uwXL29TY76Z2rM5mHXA

On Tue, 8 Sep 2015, Roger Quadros wrote:

> On 08/09/15 11:31, Peter Chen wrote:
> > On Mon, Sep 07, 2015 at 01:23:01PM +0300, Roger Quadros wrote:
> >> On 07/09/15 04:23, Peter Chen wrote:
> >>> On Mon, Aug 24, 2015 at 04:21:18PM +0300, Roger Quadros wrote:
> >>>> + * This is used by the USB Host stack to register the Host controller
> >>>> + * to the OTG core. Host controller must not be started by the
> >>>> + * caller as it is left upto the OTG state machine to do so.
> >>>> + *
> >>>> + * Returns: 0 on success, error value otherwise.
> >>>> + */
> >>>> +int usb_otg_register_hcd(struct usb_hcd *hcd, unsigned int irqnum,
> >>>> +			 unsigned long irqflags, struct otg_hcd_ops *ops)
> >>>> +{
> >>>> +	struct usb_otg *otgd;
> >>>> +	struct device *hcd_dev = hcd->self.controller;
> >>>> +	struct device *otg_dev = usb_otg_get_device(hcd_dev);
> >>>> +
> >>>
> >>> One big problem here is: there are two designs for current (IP) driver
> >>> code, one creates dedicated hcd device as roothub's parent, like dwc3.
> >>> Another one doesn't do this, roothub's parent is core device (or otg device
> >>> in your patch), like chipidea and dwc2.
> >>>
> >>> Then, otg_dev will be glue layer device for chipidea after that.
> >>
> >> OK. Let's add a way for the otg controller driver to provide the host and gadget
> >> information to the otg core for such devices like chipidea and dwc2.
> >>
> > 
> > Roger, not only chipidea and dwc2, I think the musb uses the same
> > hierarchy. If the host, device, and otg share the same register
> > region, host part can't be a platform driver since we don't want
> > to remap the same register region again.
> > 
> > So, in the design, we may need to consider both situations, one
> > is otg/host/device has its own register region, and host is a
> > separate platform device (A), the other is three parts share the
> > same register region, there is only one platform driver (B).
> > 
> > A:
> > 
> > 			IP core device 
> > 			    |
> > 			    |
> > 		      |-----|-----|
> > 		      gadget   host platform device	
> > 		      		|
> > 				roothub
> > 
> > B:
> > 
> > 			IP core device
> > 			    |
> > 			    |
> > 		      |-----|-----|
> > 		      gadget   	 roothub
> > 		      		
> > 
> >> This API must be called before the hcd/gadget-driver is added so that the otg
> >> core knows it's linked to an OTG controller.
> >>
> >> Any better idea?
> >>
> > 
> > A flag stands for this hcd controller is the same with otg controller
> > can be used, this flag can be stored at struct usb_otg_config.
> 
> What if there is another architecture like so?
> 
> C:
> 			[Parent]
> 			   |
> 			   |
> 		|------------------|--------------|
> 	[OTG core]		[gadget]	[host]
> 
> We need a more flexible mechanism to link the gadget and
> host device to the otg core for non DT case.
> 
> How about adding struct usb_otg parameter to usb_otg_register_hcd()?
> 
> e.g.
> int usb_otg_register_hcd(struct usb_otg *otg, struct usb_hcd *hcd, ..)
> 
> If otg is NULL it will try DT otg-controller property or parent to
> get the otg controller.

This seems a lot like something Peter and I discussed recently.  See

	http://marc.info/?l=linux-usb&m=143977568021328&w=2

and the following messages in that thread.

Alan Stern


--
To unsubscribe from this list: send the line "unsubscribe linux-usb" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply	[flat|nested] 48+ messages in thread

* Re: [PATCH v4 07/13] usb: otg: add OTG core
@ 2015-09-08 14:34                 ` Alan Stern
  0 siblings, 0 replies; 48+ messages in thread
From: Alan Stern @ 2015-09-08 14:34 UTC (permalink / raw)
  To: Roger Quadros
  Cc: Peter Chen, balbi, gregkh, dan.j.williams, jun.li, mathias.nyman,
	tony, Joao.Pinto, abrestic, linux-usb, linux-kernel, linux-omap

On Tue, 8 Sep 2015, Roger Quadros wrote:

> On 08/09/15 11:31, Peter Chen wrote:
> > On Mon, Sep 07, 2015 at 01:23:01PM +0300, Roger Quadros wrote:
> >> On 07/09/15 04:23, Peter Chen wrote:
> >>> On Mon, Aug 24, 2015 at 04:21:18PM +0300, Roger Quadros wrote:
> >>>> + * This is used by the USB Host stack to register the Host controller
> >>>> + * to the OTG core. Host controller must not be started by the
> >>>> + * caller as it is left upto the OTG state machine to do so.
> >>>> + *
> >>>> + * Returns: 0 on success, error value otherwise.
> >>>> + */
> >>>> +int usb_otg_register_hcd(struct usb_hcd *hcd, unsigned int irqnum,
> >>>> +			 unsigned long irqflags, struct otg_hcd_ops *ops)
> >>>> +{
> >>>> +	struct usb_otg *otgd;
> >>>> +	struct device *hcd_dev = hcd->self.controller;
> >>>> +	struct device *otg_dev = usb_otg_get_device(hcd_dev);
> >>>> +
> >>>
> >>> One big problem here is: there are two designs for current (IP) driver
> >>> code, one creates dedicated hcd device as roothub's parent, like dwc3.
> >>> Another one doesn't do this, roothub's parent is core device (or otg device
> >>> in your patch), like chipidea and dwc2.
> >>>
> >>> Then, otg_dev will be glue layer device for chipidea after that.
> >>
> >> OK. Let's add a way for the otg controller driver to provide the host and gadget
> >> information to the otg core for such devices like chipidea and dwc2.
> >>
> > 
> > Roger, not only chipidea and dwc2, I think the musb uses the same
> > hierarchy. If the host, device, and otg share the same register
> > region, host part can't be a platform driver since we don't want
> > to remap the same register region again.
> > 
> > So, in the design, we may need to consider both situations, one
> > is otg/host/device has its own register region, and host is a
> > separate platform device (A), the other is three parts share the
> > same register region, there is only one platform driver (B).
> > 
> > A:
> > 
> > 			IP core device 
> > 			    |
> > 			    |
> > 		      |-----|-----|
> > 		      gadget   host platform device	
> > 		      		|
> > 				roothub
> > 
> > B:
> > 
> > 			IP core device
> > 			    |
> > 			    |
> > 		      |-----|-----|
> > 		      gadget   	 roothub
> > 		      		
> > 
> >> This API must be called before the hcd/gadget-driver is added so that the otg
> >> core knows it's linked to an OTG controller.
> >>
> >> Any better idea?
> >>
> > 
> > A flag stands for this hcd controller is the same with otg controller
> > can be used, this flag can be stored at struct usb_otg_config.
> 
> What if there is another architecture like so?
> 
> C:
> 			[Parent]
> 			   |
> 			   |
> 		|------------------|--------------|
> 	[OTG core]		[gadget]	[host]
> 
> We need a more flexible mechanism to link the gadget and
> host device to the otg core for non DT case.
> 
> How about adding struct usb_otg parameter to usb_otg_register_hcd()?
> 
> e.g.
> int usb_otg_register_hcd(struct usb_otg *otg, struct usb_hcd *hcd, ..)
> 
> If otg is NULL it will try DT otg-controller property or parent to
> get the otg controller.

This seems a lot like something Peter and I discussed recently.  See

	http://marc.info/?l=linux-usb&m=143977568021328&w=2

and the following messages in that thread.

Alan Stern



^ permalink raw reply	[flat|nested] 48+ messages in thread

* Re: [PATCH v4 07/13] usb: otg: add OTG core
  2015-09-08 14:34                 ` Alan Stern
@ 2015-09-08 17:29                   ` Roger Quadros
  -1 siblings, 0 replies; 48+ messages in thread
From: Roger Quadros @ 2015-09-08 17:29 UTC (permalink / raw)
  To: Alan Stern
  Cc: Peter Chen, balbi, gregkh, dan.j.williams, jun.li, mathias.nyman,
	tony, Joao.Pinto, abrestic, linux-usb, linux-kernel, linux-omap

Alan,

On 08/09/15 17:34, Alan Stern wrote:
> On Tue, 8 Sep 2015, Roger Quadros wrote:
> 
>> On 08/09/15 11:31, Peter Chen wrote:
>>> On Mon, Sep 07, 2015 at 01:23:01PM +0300, Roger Quadros wrote:
>>>> On 07/09/15 04:23, Peter Chen wrote:
>>>>> On Mon, Aug 24, 2015 at 04:21:18PM +0300, Roger Quadros wrote:
>>>>>> + * This is used by the USB Host stack to register the Host controller
>>>>>> + * to the OTG core. Host controller must not be started by the
>>>>>> + * caller as it is left upto the OTG state machine to do so.
>>>>>> + *
>>>>>> + * Returns: 0 on success, error value otherwise.
>>>>>> + */
>>>>>> +int usb_otg_register_hcd(struct usb_hcd *hcd, unsigned int irqnum,
>>>>>> +			 unsigned long irqflags, struct otg_hcd_ops *ops)
>>>>>> +{
>>>>>> +	struct usb_otg *otgd;
>>>>>> +	struct device *hcd_dev = hcd->self.controller;
>>>>>> +	struct device *otg_dev = usb_otg_get_device(hcd_dev);
>>>>>> +
>>>>>
>>>>> One big problem here is: there are two designs for current (IP) driver
>>>>> code, one creates dedicated hcd device as roothub's parent, like dwc3.
>>>>> Another one doesn't do this, roothub's parent is core device (or otg device
>>>>> in your patch), like chipidea and dwc2.
>>>>>
>>>>> Then, otg_dev will be glue layer device for chipidea after that.
>>>>
>>>> OK. Let's add a way for the otg controller driver to provide the host and gadget
>>>> information to the otg core for such devices like chipidea and dwc2.
>>>>
>>>
>>> Roger, not only chipidea and dwc2, I think the musb uses the same
>>> hierarchy. If the host, device, and otg share the same register
>>> region, host part can't be a platform driver since we don't want
>>> to remap the same register region again.
>>>
>>> So, in the design, we may need to consider both situations, one
>>> is otg/host/device has its own register region, and host is a
>>> separate platform device (A), the other is three parts share the
>>> same register region, there is only one platform driver (B).
>>>
>>> A:
>>>
>>> 			IP core device 
>>> 			    |
>>> 			    |
>>> 		      |-----|-----|
>>> 		      gadget   host platform device	
>>> 		      		|
>>> 				roothub
>>>
>>> B:
>>>
>>> 			IP core device
>>> 			    |
>>> 			    |
>>> 		      |-----|-----|
>>> 		      gadget   	 roothub
>>> 		      		
>>>
>>>> This API must be called before the hcd/gadget-driver is added so that the otg
>>>> core knows it's linked to an OTG controller.
>>>>
>>>> Any better idea?
>>>>
>>>
>>> A flag stands for this hcd controller is the same with otg controller
>>> can be used, this flag can be stored at struct usb_otg_config.
>>
>> What if there is another architecture like so?
>>
>> C:
>> 			[Parent]
>> 			   |
>> 			   |
>> 		|------------------|--------------|
>> 	[OTG core]		[gadget]	[host]
>>
>> We need a more flexible mechanism to link the gadget and
>> host device to the otg core for non DT case.
>>
>> How about adding struct usb_otg parameter to usb_otg_register_hcd()?
>>
>> e.g.
>> int usb_otg_register_hcd(struct usb_otg *otg, struct usb_hcd *hcd, ..)
>>
>> If otg is NULL it will try DT otg-controller property or parent to
>> get the otg controller.
> 
> This seems a lot like something Peter and I discussed recently.  See
> 
> 	http://marc.info/?l=linux-usb&m=143977568021328&w=2
> 
> and the following messages in that thread.
> 

If I understood right, your proposal was to add a usb_pointers data
struct to the device's drvdata?

This is fine only if the otg/gadget/host share the same device.
It does not solve the problem where each have different platform devices.

cheers,
-roger

^ permalink raw reply	[flat|nested] 48+ messages in thread

* Re: [PATCH v4 07/13] usb: otg: add OTG core
@ 2015-09-08 17:29                   ` Roger Quadros
  0 siblings, 0 replies; 48+ messages in thread
From: Roger Quadros @ 2015-09-08 17:29 UTC (permalink / raw)
  To: Alan Stern
  Cc: Peter Chen, balbi, gregkh, dan.j.williams, jun.li, mathias.nyman,
	tony, Joao.Pinto, abrestic, linux-usb, linux-kernel, linux-omap

Alan,

On 08/09/15 17:34, Alan Stern wrote:
> On Tue, 8 Sep 2015, Roger Quadros wrote:
> 
>> On 08/09/15 11:31, Peter Chen wrote:
>>> On Mon, Sep 07, 2015 at 01:23:01PM +0300, Roger Quadros wrote:
>>>> On 07/09/15 04:23, Peter Chen wrote:
>>>>> On Mon, Aug 24, 2015 at 04:21:18PM +0300, Roger Quadros wrote:
>>>>>> + * This is used by the USB Host stack to register the Host controller
>>>>>> + * to the OTG core. Host controller must not be started by the
>>>>>> + * caller as it is left upto the OTG state machine to do so.
>>>>>> + *
>>>>>> + * Returns: 0 on success, error value otherwise.
>>>>>> + */
>>>>>> +int usb_otg_register_hcd(struct usb_hcd *hcd, unsigned int irqnum,
>>>>>> +			 unsigned long irqflags, struct otg_hcd_ops *ops)
>>>>>> +{
>>>>>> +	struct usb_otg *otgd;
>>>>>> +	struct device *hcd_dev = hcd->self.controller;
>>>>>> +	struct device *otg_dev = usb_otg_get_device(hcd_dev);
>>>>>> +
>>>>>
>>>>> One big problem here is: there are two designs for current (IP) driver
>>>>> code, one creates dedicated hcd device as roothub's parent, like dwc3.
>>>>> Another one doesn't do this, roothub's parent is core device (or otg device
>>>>> in your patch), like chipidea and dwc2.
>>>>>
>>>>> Then, otg_dev will be glue layer device for chipidea after that.
>>>>
>>>> OK. Let's add a way for the otg controller driver to provide the host and gadget
>>>> information to the otg core for such devices like chipidea and dwc2.
>>>>
>>>
>>> Roger, not only chipidea and dwc2, I think the musb uses the same
>>> hierarchy. If the host, device, and otg share the same register
>>> region, host part can't be a platform driver since we don't want
>>> to remap the same register region again.
>>>
>>> So, in the design, we may need to consider both situations, one
>>> is otg/host/device has its own register region, and host is a
>>> separate platform device (A), the other is three parts share the
>>> same register region, there is only one platform driver (B).
>>>
>>> A:
>>>
>>> 			IP core device 
>>> 			    |
>>> 			    |
>>> 		      |-----|-----|
>>> 		      gadget   host platform device	
>>> 		      		|
>>> 				roothub
>>>
>>> B:
>>>
>>> 			IP core device
>>> 			    |
>>> 			    |
>>> 		      |-----|-----|
>>> 		      gadget   	 roothub
>>> 		      		
>>>
>>>> This API must be called before the hcd/gadget-driver is added so that the otg
>>>> core knows it's linked to an OTG controller.
>>>>
>>>> Any better idea?
>>>>
>>>
>>> A flag stands for this hcd controller is the same with otg controller
>>> can be used, this flag can be stored at struct usb_otg_config.
>>
>> What if there is another architecture like so?
>>
>> C:
>> 			[Parent]
>> 			   |
>> 			   |
>> 		|------------------|--------------|
>> 	[OTG core]		[gadget]	[host]
>>
>> We need a more flexible mechanism to link the gadget and
>> host device to the otg core for non DT case.
>>
>> How about adding struct usb_otg parameter to usb_otg_register_hcd()?
>>
>> e.g.
>> int usb_otg_register_hcd(struct usb_otg *otg, struct usb_hcd *hcd, ..)
>>
>> If otg is NULL it will try DT otg-controller property or parent to
>> get the otg controller.
> 
> This seems a lot like something Peter and I discussed recently.  See
> 
> 	http://marc.info/?l=linux-usb&m=143977568021328&w=2
> 
> and the following messages in that thread.
> 

If I understood right, your proposal was to add a usb_pointers data
struct to the device's drvdata?

This is fine only if the otg/gadget/host share the same device.
It does not solve the problem where each have different platform devices.

cheers,
-roger


^ permalink raw reply	[flat|nested] 48+ messages in thread

* Re: [PATCH v4 07/13] usb: otg: add OTG core
  2015-09-08 12:25             ` Roger Quadros
@ 2015-09-09  2:21                 ` Peter Chen
  -1 siblings, 0 replies; 48+ messages in thread
From: Peter Chen @ 2015-09-09  2:21 UTC (permalink / raw)
  To: Roger Quadros
  Cc: stern-nwvwT67g6+6dFdvTe/nMLpVzexx5G7lz, balbi-l0cyMroinI0,
	gregkh-hQyY1W1yCW8ekmWlsbkhG0B+6BGkLq7r,
	dan.j.williams-ral2JQCrhuEAvxtiuMwx3w,
	jun.li-KZfg59tc24xl57MIdRCFDg,
	mathias.nyman-VuQAYsv1563Yd54FQh9/CA, tony-4v6yS6AI5VpBDgjK7y7TUQ,
	Joao.Pinto-HKixBCOQz3hWk0Htik3J/w,
	abrestic-F7+t8E8rja9g9hUCZPvPmw, linux-usb-u79uwXL29TY76Z2rM5mHXA,
	linux-kernel-u79uwXL29TY76Z2rM5mHXA,
	linux-omap-u79uwXL29TY76Z2rM5mHXA

On Tue, Sep 08, 2015 at 03:25:25PM +0300, Roger Quadros wrote:
> 
> 
> On 08/09/15 11:31, Peter Chen wrote:
> > On Mon, Sep 07, 2015 at 01:23:01PM +0300, Roger Quadros wrote:
> >> On 07/09/15 04:23, Peter Chen wrote:
> >>> On Mon, Aug 24, 2015 at 04:21:18PM +0300, Roger Quadros wrote:
> >>>> + * This is used by the USB Host stack to register the Host controller
> >>>> + * to the OTG core. Host controller must not be started by the
> >>>> + * caller as it is left upto the OTG state machine to do so.
> >>>> + *
> >>>> + * Returns: 0 on success, error value otherwise.
> >>>> + */
> >>>> +int usb_otg_register_hcd(struct usb_hcd *hcd, unsigned int irqnum,
> >>>> +			 unsigned long irqflags, struct otg_hcd_ops *ops)
> >>>> +{
> >>>> +	struct usb_otg *otgd;
> >>>> +	struct device *hcd_dev = hcd->self.controller;
> >>>> +	struct device *otg_dev = usb_otg_get_device(hcd_dev);
> >>>> +
> >>>
> >>> One big problem here is: there are two designs for current (IP) driver
> >>> code, one creates dedicated hcd device as roothub's parent, like dwc3.
> >>> Another one doesn't do this, roothub's parent is core device (or otg device
> >>> in your patch), like chipidea and dwc2.
> >>>
> >>> Then, otg_dev will be glue layer device for chipidea after that.
> >>
> >> OK. Let's add a way for the otg controller driver to provide the host and gadget
> >> information to the otg core for such devices like chipidea and dwc2.
> >>
> > 
> > Roger, not only chipidea and dwc2, I think the musb uses the same
> > hierarchy. If the host, device, and otg share the same register
> > region, host part can't be a platform driver since we don't want
> > to remap the same register region again.
> > 
> > So, in the design, we may need to consider both situations, one
> > is otg/host/device has its own register region, and host is a
> > separate platform device (A), the other is three parts share the
> > same register region, there is only one platform driver (B).
> > 
> > A:
> > 
> > 			IP core device 
> > 			    |
> > 			    |
> > 		      |-----|-----|
> > 		      gadget   host platform device	
> > 		      		|
> > 				roothub
> > 
> > B:
> > 
> > 			IP core device
> > 			    |
> > 			    |
> > 		      |-----|-----|
> > 		      gadget   	 roothub
> > 		      		
> > 
> >> This API must be called before the hcd/gadget-driver is added so that the otg
> >> core knows it's linked to an OTG controller.
> >>
> >> Any better idea?
> >>
> > 
> > A flag stands for this hcd controller is the same with otg controller
> > can be used, this flag can be stored at struct usb_otg_config.
> 
> What if there is another architecture like so?
> 
> C:
> 			[Parent]
> 			   |
> 			   |
> 		|------------------|--------------|
> 	[OTG core]		[gadget]	[host]
> 
> We need a more flexible mechanism to link the gadget and
> host device to the otg core for non DT case.
> 
> How about adding struct usb_otg parameter to usb_otg_register_hcd()?
> 
> e.g.
> int usb_otg_register_hcd(struct usb_otg *otg, struct usb_hcd *hcd, ..)
> 
> If otg is NULL it will try DT otg-controller property or parent to
> get the otg controller.

How usb_otg_register_hcd get struct usb_otg, from where?

> 
> > 
> > Peter
> > 
> > P.S: I still read your code, I find not all APIs in this file are used
> > in your dwc3 example. 
> 
> Which ones? The ones for registering/unregistered host/gadget are used
> by hcd/udc core as part of usb_add/remove_hcd() and
> udc_bind_to_driver()/usb_gadget_remove_driver()
> 

Ok, now I understand your design, usb_create_hcd must be called before
the fsm kicks off. The call flow like below:

usb_otg_register->usb_create_hcd->usb_add_hcd->usb_otg_register_hcd->
usb_otg_start_host->usb_otg_add_hcd

We need to make some changes to let chipidea work since usb_create_hcd
is included at host->start.

-- 

Best Regards,
Peter Chen
--
To unsubscribe from this list: send the line "unsubscribe linux-usb" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply	[flat|nested] 48+ messages in thread

* Re: [PATCH v4 07/13] usb: otg: add OTG core
@ 2015-09-09  2:21                 ` Peter Chen
  0 siblings, 0 replies; 48+ messages in thread
From: Peter Chen @ 2015-09-09  2:21 UTC (permalink / raw)
  To: Roger Quadros
  Cc: stern, balbi, gregkh, dan.j.williams, jun.li, mathias.nyman, tony,
	Joao.Pinto, abrestic, linux-usb, linux-kernel, linux-omap

On Tue, Sep 08, 2015 at 03:25:25PM +0300, Roger Quadros wrote:
> 
> 
> On 08/09/15 11:31, Peter Chen wrote:
> > On Mon, Sep 07, 2015 at 01:23:01PM +0300, Roger Quadros wrote:
> >> On 07/09/15 04:23, Peter Chen wrote:
> >>> On Mon, Aug 24, 2015 at 04:21:18PM +0300, Roger Quadros wrote:
> >>>> + * This is used by the USB Host stack to register the Host controller
> >>>> + * to the OTG core. Host controller must not be started by the
> >>>> + * caller as it is left upto the OTG state machine to do so.
> >>>> + *
> >>>> + * Returns: 0 on success, error value otherwise.
> >>>> + */
> >>>> +int usb_otg_register_hcd(struct usb_hcd *hcd, unsigned int irqnum,
> >>>> +			 unsigned long irqflags, struct otg_hcd_ops *ops)
> >>>> +{
> >>>> +	struct usb_otg *otgd;
> >>>> +	struct device *hcd_dev = hcd->self.controller;
> >>>> +	struct device *otg_dev = usb_otg_get_device(hcd_dev);
> >>>> +
> >>>
> >>> One big problem here is: there are two designs for current (IP) driver
> >>> code, one creates dedicated hcd device as roothub's parent, like dwc3.
> >>> Another one doesn't do this, roothub's parent is core device (or otg device
> >>> in your patch), like chipidea and dwc2.
> >>>
> >>> Then, otg_dev will be glue layer device for chipidea after that.
> >>
> >> OK. Let's add a way for the otg controller driver to provide the host and gadget
> >> information to the otg core for such devices like chipidea and dwc2.
> >>
> > 
> > Roger, not only chipidea and dwc2, I think the musb uses the same
> > hierarchy. If the host, device, and otg share the same register
> > region, host part can't be a platform driver since we don't want
> > to remap the same register region again.
> > 
> > So, in the design, we may need to consider both situations, one
> > is otg/host/device has its own register region, and host is a
> > separate platform device (A), the other is three parts share the
> > same register region, there is only one platform driver (B).
> > 
> > A:
> > 
> > 			IP core device 
> > 			    |
> > 			    |
> > 		      |-----|-----|
> > 		      gadget   host platform device	
> > 		      		|
> > 				roothub
> > 
> > B:
> > 
> > 			IP core device
> > 			    |
> > 			    |
> > 		      |-----|-----|
> > 		      gadget   	 roothub
> > 		      		
> > 
> >> This API must be called before the hcd/gadget-driver is added so that the otg
> >> core knows it's linked to an OTG controller.
> >>
> >> Any better idea?
> >>
> > 
> > A flag stands for this hcd controller is the same with otg controller
> > can be used, this flag can be stored at struct usb_otg_config.
> 
> What if there is another architecture like so?
> 
> C:
> 			[Parent]
> 			   |
> 			   |
> 		|------------------|--------------|
> 	[OTG core]		[gadget]	[host]
> 
> We need a more flexible mechanism to link the gadget and
> host device to the otg core for non DT case.
> 
> How about adding struct usb_otg parameter to usb_otg_register_hcd()?
> 
> e.g.
> int usb_otg_register_hcd(struct usb_otg *otg, struct usb_hcd *hcd, ..)
> 
> If otg is NULL it will try DT otg-controller property or parent to
> get the otg controller.

How usb_otg_register_hcd get struct usb_otg, from where?

> 
> > 
> > Peter
> > 
> > P.S: I still read your code, I find not all APIs in this file are used
> > in your dwc3 example. 
> 
> Which ones? The ones for registering/unregistered host/gadget are used
> by hcd/udc core as part of usb_add/remove_hcd() and
> udc_bind_to_driver()/usb_gadget_remove_driver()
> 

Ok, now I understand your design, usb_create_hcd must be called before
the fsm kicks off. The call flow like below:

usb_otg_register->usb_create_hcd->usb_add_hcd->usb_otg_register_hcd->
usb_otg_start_host->usb_otg_add_hcd

We need to make some changes to let chipidea work since usb_create_hcd
is included at host->start.

-- 

Best Regards,
Peter Chen

^ permalink raw reply	[flat|nested] 48+ messages in thread

* Re: [PATCH v4 07/13] usb: otg: add OTG core
  2015-09-07 10:53         ` Roger Quadros
@ 2015-09-09  6:20             ` Li Jun
  -1 siblings, 0 replies; 48+ messages in thread
From: Li Jun @ 2015-09-09  6:20 UTC (permalink / raw)
  To: Roger Quadros
  Cc: stern-nwvwT67g6+6dFdvTe/nMLpVzexx5G7lz, balbi-l0cyMroinI0,
	gregkh-hQyY1W1yCW8ekmWlsbkhG0B+6BGkLq7r,
	peter.chen-KZfg59tc24xl57MIdRCFDg,
	dan.j.williams-ral2JQCrhuEAvxtiuMwx3w,
	jun.li-KZfg59tc24xl57MIdRCFDg,
	mathias.nyman-VuQAYsv1563Yd54FQh9/CA, tony-4v6yS6AI5VpBDgjK7y7TUQ,
	Joao.Pinto-HKixBCOQz3hWk0Htik3J/w,
	abrestic-F7+t8E8rja9g9hUCZPvPmw, linux-usb-u79uwXL29TY76Z2rM5mHXA,
	linux-kernel-u79uwXL29TY76Z2rM5mHXA,
	linux-omap-u79uwXL29TY76Z2rM5mHXA

On Mon, Sep 07, 2015 at 01:53:19PM +0300, Roger Quadros wrote:
> On 07/09/15 10:40, Li Jun wrote:
> > On Mon, Aug 24, 2015 at 04:21:18PM +0300, Roger Quadros wrote:
> >> The OTG core instantiates the OTG Finite State Machine
> >> per OTG controller and manages starting/stopping the
> >> host and gadget controllers based on the bus state.
> >>
> >> It provides APIs for the following tasks
> >>
> >> - Registering an OTG capable controller
> >> - Registering Host and Gadget controllers to OTG core
> >> - Providing inputs to and kicking the OTG state machine
> >>
> >> Signed-off-by: Roger Quadros <rogerq-l0cyMroinI0@public.gmane.org>
> >> ---
> >>  MAINTAINERS                  |    4 +-
> >>  drivers/usb/Kconfig          |    2 +-
> >>  drivers/usb/Makefile         |    1 +
> >>  drivers/usb/common/Makefile  |    3 +-
> >>  drivers/usb/common/usb-otg.c | 1061 ++++++++++++++++++++++++++++++++++++++++++
> >>  drivers/usb/common/usb-otg.h |   71 +++
> >>  drivers/usb/core/Kconfig     |   11 +-
> >>  include/linux/usb/otg.h      |  189 +++++++-
> >>  8 files changed, 1321 insertions(+), 21 deletions(-)
> >>  create mode 100644 drivers/usb/common/usb-otg.c
> >>  create mode 100644 drivers/usb/common/usb-otg.h
> >>
> > 
> > ... ...
> > 
> >> +
> >> +/**
> >> + * Get OTG device from host or gadget device.
> >> + *
> >> + * For non device tree boot, the OTG controller is assumed to be
> >> + * the parent of the host/gadget device.
> > 
> > This assumption/restriction maybe a problem, as I pointed in your previous
> > version, usb_create_hcd() use the passed dev as its dev, but,
> > usb_add_gadget_udc() use the passed dev as its parent dev, so often the
> > host and gadget don't share the same parent device, at least it doesn't
> > apply to chipidea case.
> 
> Let's provide a way for OTG driver to provide the OTG core exactly which is
> the related host/gadget device.
> 
> > 
> >> + * For device tree boot, the OTG controller is derived from the
> >> + * "otg-controller" property.
> >> + */
> >> +static struct device *usb_otg_get_device(struct device *hcd_gcd_dev)
> >> +{
> >> +	struct device *otg_dev;
> >> +
> >> +	if (!hcd_gcd_dev)
> >> +		return NULL;
> >> +
> >> +	if (hcd_gcd_dev->of_node) {
> >> +		struct device_node *np;
> >> +		struct platform_device *pdev;
> >> +
> >> +		np = of_parse_phandle(hcd_gcd_dev->of_node, "otg-controller",
> >> +				      0);
> >> +		if (!np)
> >> +			goto legacy;	/* continue legacy way */
> >> +
> >> +		pdev = of_find_device_by_node(np);
> >> +		of_node_put(np);
> >> +		if (!pdev) {
> >> +			dev_err(&pdev->dev, "couldn't get otg-controller device\n");
> >> +			return NULL;
> >> +		}
> >> +
> >> +		otg_dev = &pdev->dev;
> >> +		return otg_dev;
> >> +	}
> >> +
> >> +legacy:
> >> +	/* otg device is parent and must be registered */
> >> +	otg_dev = hcd_gcd_dev->parent;
> >> +	if (!usb_otg_get_data(otg_dev))
> >> +		return NULL;
> >> +
> >> +	return otg_dev;
> >> +}
> >> +
> > 
> > ... ...
> > 
> >> +static void usb_otg_init_timers(struct usb_otg *otgd, unsigned *timeouts)
> >> +{
> >> +	struct otg_fsm *fsm = &otgd->fsm;
> >> +	unsigned long tmouts[NUM_OTG_FSM_TIMERS];
> >> +	int i;
> >> +
> >> +	/* set default timeouts */
> >> +	tmouts[A_WAIT_VRISE] = TA_WAIT_VRISE;
> >> +	tmouts[A_WAIT_VFALL] = TA_WAIT_VFALL;
> >> +	tmouts[A_WAIT_BCON] = TA_WAIT_BCON;
> >> +	tmouts[A_AIDL_BDIS] = TA_AIDL_BDIS;
> >> +	tmouts[A_BIDL_ADIS] = TA_BIDL_ADIS;
> >> +	tmouts[B_ASE0_BRST] = TB_ASE0_BRST;
> >> +	tmouts[B_SE0_SRP] = TB_SE0_SRP;
> >> +	tmouts[B_SRP_FAIL] = TB_SRP_FAIL;
> >> +
> >> +	/* set controller provided timeouts */
> >> +	if (timeouts) {
> >> +		for (i = 0; i < NUM_OTG_FSM_TIMERS; i++) {
> >> +			if (timeouts[i])
> >> +				tmouts[i] = timeouts[i];
> >> +		}
> >> +	}
> >> +
> >> +	otg_timer_init(A_WAIT_VRISE, otgd, set_tmout, TA_WAIT_VRISE,
> >> +		       &fsm->a_wait_vrise_tmout);
> >> +	otg_timer_init(A_WAIT_VFALL, otgd, set_tmout, TA_WAIT_VFALL,
> >> +		       &fsm->a_wait_vfall_tmout);
> >> +	otg_timer_init(A_WAIT_BCON, otgd, set_tmout, TA_WAIT_BCON,
> >> +		       &fsm->a_wait_bcon_tmout);
> >> +	otg_timer_init(A_AIDL_BDIS, otgd, set_tmout, TA_AIDL_BDIS,
> >> +		       &fsm->a_aidl_bdis_tmout);
> >> +	otg_timer_init(A_BIDL_ADIS, otgd, set_tmout, TA_BIDL_ADIS,
> >> +		       &fsm->a_bidl_adis_tmout);
> >> +	otg_timer_init(B_ASE0_BRST, otgd, set_tmout, TB_ASE0_BRST,
> >> +		       &fsm->b_ase0_brst_tmout);
> >> +
> >> +	otg_timer_init(B_SE0_SRP, otgd, set_tmout, TB_SE0_SRP,
> >> +		       &fsm->b_se0_srp);
> >> +	otg_timer_init(B_SRP_FAIL, otgd, set_tmout, TB_SRP_FAIL,
> >> +		       &fsm->b_srp_done);
> >> +
> >> +	/* FIXME: what about A_WAIT_ENUM? */
> > 
> > Either you init it as other timers, or you remove all of it, otherwise
> > there will be NULL pointer crash.
> 
> I want to initialize it but was not sure about the timeout value.
> What timeout value I must use?
> 

It's not defined in OTG spec, I don't know either.
or you filter it out when add/del timer in below 2 functions.
	if (id == A_WAIT_ENUM)
		return;

> > 
> >> +}
> >> +
> >> +/**
> >> + * OTG FSM ops function to add timer
> >> + */
> >> +static void usb_otg_add_timer(struct otg_fsm *fsm, enum otg_fsm_timer id)
> >> +{
> >> +	struct usb_otg *otgd = container_of(fsm, struct usb_otg, fsm);
> >> +	struct otg_timer *otgtimer = &otgd->timers[id];
> >> +	struct hrtimer *timer = &otgtimer->timer;
> >> +
> >> +	if (!otgd->fsm_running)
> >> +		return;
> >> +
> >> +	/* if timer is already active, exit */
> >> +	if (hrtimer_active(timer)) {
> >> +		dev_err(otgd->dev, "otg: timer %d is already running\n", id);
> >> +		return;
> >> +	}
> >> +
> >> +	hrtimer_start(timer, otgtimer->timeout, HRTIMER_MODE_REL);
> >> +}
> >> +
> >> +/**
> >> + * OTG FSM ops function to delete timer
> >> + */
> >> +static void usb_otg_del_timer(struct otg_fsm *fsm, enum otg_fsm_timer id)
> >> +{
> >> +	struct usb_otg *otgd = container_of(fsm, struct usb_otg, fsm);
> >> +	struct hrtimer *timer = &otgd->timers[id].timer;
> >> +
> >> +	hrtimer_cancel(timer);
> >> +}
> >> +

... ...

> >> +	}
> >> +
> >> +	otgd->dev = dev;
> >> +	otgd->caps = &config->otg_caps;
> > 
> > How about define otgd->caps as a pointer, then don't need copy it.
> 
> otgd->caps is a pointer.
> 

okay, you are right.

> > 
> >> +	INIT_WORK(&otgd->work, usb_otg_work);
> >> +	otgd->wq = create_singlethread_workqueue("usb_otg");
> >> +	if (!otgd->wq) {
> >> +		dev_err(dev, "otg: %s: can't create workqueue\n",
> >> +			__func__);
> >> +		ret = -ENOMEM;
> >> +		goto err_wq;
> >> +	}
> >> +
> >> +	usb_otg_init_timers(otgd, config->otg_timeouts);
> >> +
> >> +	/* create copy of original ops */
> >> +	otgd->fsm_ops = *config->fsm_ops;
> > 
> > The same, use a pointer is enough?
> 
> We are creating a copy because we are overriding timer ops.
> 

okay.

> > 
> >> +	/* FIXME: we ignore caller's timer ops */
> >> +	otgd->fsm_ops.add_timer = usb_otg_add_timer;
> >> +	otgd->fsm_ops.del_timer = usb_otg_del_timer;
> >> +	/* set otg ops */
> >> +	otgd->fsm.ops = &otgd->fsm_ops;
> >> +	otgd->fsm.otg = otgd;
> >> + *

... ...

> >> + * This is used by the USB Host stack to register the Host controller
> >> + * to the OTG core. Host controller must not be started by the
> >> + * caller as it is left upto the OTG state machine to do so.
> > 
> > I am confused on how to use this function.
> > - This function should be called before start fsm per usb_otg_start_fsm().
> 
> yes.
> 
> > - Called by usb_add_hcd(), so we need call usb_add_hcd() before start fsm.
> 
> yes.
> 
> > - If I want to add hcd when switch to host role, and remove hcd when switch
> >   to peripheral, with this design, I cannot use this function?
> 
> You add hcd only once during the life of the OTG device. If it is linked to the
> OTG controller the OTG fsm manages the start/stop of hcd using the otg_hcd_ops.
> 
> "usb/core/hcd.c"
> static struct otg_hcd_ops otg_hcd_intf = {
>         .add = usb_otg_add_hcd,
>         .remove = usb_otg_remove_hcd,
> };
> 
> Your otg driver must use teh usb_otg_add/remove_hcd to start/stop the controller.
> Using usb_remove_hcd() means the hcd resource is no longer available and the
> otg fsm will be stopped.
> 
> > - How about split it out of usb_add_hcd()?
> 
> Adding the HCD and starting/stopping the hcd is split into
> usb_add/remove_hcd() and usb_otg_add/remove_hcd() for OTG case.
> 

Catch your point now.
Do usb_add_hcd to register hcd before start fsm, and use usb_otg_start_host()
to start/stop host role for otg fsm.

> > 
> >> + *
> >> + * Returns: 0 on success, error value otherwise.
> >> + */
> >> +int usb_otg_register_hcd(struct usb_hcd *hcd, unsigned int irqnum,
> >> +			 unsigned long irqflags, struct otg_hcd_ops *ops)
> >> +{
> >> +	struct usb_otg *otgd;
> >> +	struct device *hcd_dev = hcd->self.controller;
> >> +	struct device *otg_dev = usb_otg_get_device(hcd_dev);
> >> +
> >> +	if (!otg_dev)
> >> +		return -EINVAL;	/* we're definitely not OTG */
> >> +
> >> +	/* we're otg but otg controller might not yet be registered */
> >> +	mutex_lock(&otg_list_mutex);
> >> +	otgd = usb_otg_get_data(otg_dev);
> >> +	mutex_unlock(&otg_list_mutex);
> >> +	if (!otgd) {
> >> +		dev_dbg(hcd_dev,
> >> +			"otg: controller not yet registered. waiting..\n");
> >> +		/*
> >> +		 * otg controller might register later. Put the hcd in
> >> +		 * wait list and call us back when ready
> >> +		 */
> >> +		if (usb_otg_hcd_wait_add(otg_dev, hcd, irqnum, irqflags, ops)) {
> >> +			dev_dbg(hcd_dev, "otg: failed to add to wait list\n");
> >> +			return -EINVAL;
> >> +		}
> >> +
> >> +		return 0;
> >> +	}
> >> +
> >> +	/* HCD will be started by OTG fsm when needed */
> >> +	mutex_lock(&otgd->fsm.lock);
> > 
> > If call usb_add_hcd() when start host role, deadlock.
> 
> No. You must call usb_otg_add_hcd() to start host role.
> 
> > 
> >> +	if (otgd->primary_hcd.hcd) {
> >> +		/* probably a shared HCD ? */
> >> +		if (usb_otg_hcd_is_primary_hcd(hcd)) {
> >> +			dev_err(otg_dev, "otg: primary host already registered\n");
> >> +			goto err;
> >> +		}
> >> +
> >> +		if (hcd->shared_hcd == otgd->primary_hcd.hcd) {
> >> +			if (otgd->shared_hcd.hcd) {
> >> +				dev_err(otg_dev, "otg: shared host already registered\n");
> >> +				goto err;
> >> +			}
> >> +
> >> +			otgd->shared_hcd.hcd = hcd;
> >> +			otgd->shared_hcd.irqnum = irqnum;
> >> +			otgd->shared_hcd.irqflags = irqflags;
> >> +			otgd->shared_hcd.ops = ops;
> >> +			dev_info(otg_dev, "otg: shared host %s registered\n",
> >> +				 dev_name(hcd->self.controller));
> >> +		} else {
> >> +			dev_err(otg_dev, "otg: invalid shared host %s\n",
> >> +				dev_name(hcd->self.controller));
> >> +			goto err;
> >> +		}
> >> +	} else {
> >> +		if (!usb_otg_hcd_is_primary_hcd(hcd)) {
> >> +			dev_err(otg_dev, "otg: primary host must be registered first\n");
> >> +			goto err;
> >> +		}
> >> +
> >> +		otgd->primary_hcd.hcd = hcd;
> >> +		otgd->primary_hcd.irqnum = irqnum;
> >> +		otgd->primary_hcd.irqflags = irqflags;
> >> +		otgd->primary_hcd.ops = ops;
> >> +		dev_info(otg_dev, "otg: primary host %s registered\n",
> >> +			 dev_name(hcd->self.controller));
> >> +	}
> >> +
> >> +	/*
> >> +	 * we're ready only if we have shared HCD
> >> +	 * or we don't need shared HCD.
> >> +	 */
> >> +	if (otgd->shared_hcd.hcd || !otgd->primary_hcd.hcd->shared_hcd) {
> >> +		otgd->fsm.otg->host = hcd_to_bus(hcd);
> > 
> > otgd->host = hcd_to_bus(hcd);
> 
> ok. So we set host at both places. struct usb_otg in struct otg_fsm starts to
> feel redundant now. I think we should get rid of it and get the usb_otg struct
> using container_of() instead.
> 

Then you may come to force existing otg fsm code to use your OTG core.

> > 
> >> +		/* FIXME: set bus->otg_port if this is true OTG port with HNP */
> >> +
> >> +		/* start FSM */
> >> +		usb_otg_start_fsm(&otgd->fsm);
> >> +	} else {
> >> +		dev_dbg(otg_dev, "otg: can't start till shared host registers\n");
> >> +	}
> >> +
> >> +	mutex_unlock(&otgd->fsm.lock);
> >> +
> >> +	return 0;
> >> +
> >> +err:
> >> +	mutex_unlock(&otgd->fsm.lock);
> >> +	return -EINVAL;
> > 
> > Return non-zero, then if err, do we need call usb_otg_add_hcd() after
> > usb_otg_register_hcd() fails?
> 
> You should not call usb_otg_register_hcd() but usb_add_hcd().
> If that fails then you fail as ususal.

My point is if we use usb_add_hcd(), but failed in usb_otg_register_hcd(),
then usb_otg_add_hcd() will be called in *all* error case, is this your
expectation?
	if (usb_otg_register_hcd(hcd, irqnum, irqflags, &otg_hcd_intf))
		return usb_otg_add_hcd(hcd, irqnum, irqflags);

> >> + * @fsm_running: state machine running/stopped indicator
> >> + */
> >>  struct usb_otg {
> >>  	u8			default_a;
> >>  
> >>  	struct phy		*phy;
> >>  	/* old usb_phy interface */
> >>  	struct usb_phy		*usb_phy;
> >> +
> > 
> > add a blank line?
> > 

You missed this.

> >>  	struct usb_bus		*host;
> >>  	struct usb_gadget	*gadget;
> >>  
> >>  	enum usb_otg_state	state;
> >>  
> >> +	struct device *dev;
> >> +	struct usb_otg_caps *caps;
> >> +	struct otg_fsm fsm;
> >> +	struct otg_fsm_ops fsm_ops;
> >> +
> >> +	/* internal use only */
> >> +	struct otg_hcd primary_hcd;
> >> +	struct otg_hcd shared_hcd;
> >> +	struct otg_gadget_ops *gadget_ops;
> >> +	struct otg_timer timers[NUM_OTG_FSM_TIMERS];
> >> +	struct list_head list;
> >> +	struct work_struct work;
> >> -- 
> >> 2.1.4

--
To unsubscribe from this list: send the line "unsubscribe linux-usb" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply	[flat|nested] 48+ messages in thread

* Re: [PATCH v4 07/13] usb: otg: add OTG core
@ 2015-09-09  6:20             ` Li Jun
  0 siblings, 0 replies; 48+ messages in thread
From: Li Jun @ 2015-09-09  6:20 UTC (permalink / raw)
  To: Roger Quadros
  Cc: stern, balbi, gregkh, peter.chen, dan.j.williams, jun.li,
	mathias.nyman, tony, Joao.Pinto, abrestic, linux-usb,
	linux-kernel, linux-omap

On Mon, Sep 07, 2015 at 01:53:19PM +0300, Roger Quadros wrote:
> On 07/09/15 10:40, Li Jun wrote:
> > On Mon, Aug 24, 2015 at 04:21:18PM +0300, Roger Quadros wrote:
> >> The OTG core instantiates the OTG Finite State Machine
> >> per OTG controller and manages starting/stopping the
> >> host and gadget controllers based on the bus state.
> >>
> >> It provides APIs for the following tasks
> >>
> >> - Registering an OTG capable controller
> >> - Registering Host and Gadget controllers to OTG core
> >> - Providing inputs to and kicking the OTG state machine
> >>
> >> Signed-off-by: Roger Quadros <rogerq@ti.com>
> >> ---
> >>  MAINTAINERS                  |    4 +-
> >>  drivers/usb/Kconfig          |    2 +-
> >>  drivers/usb/Makefile         |    1 +
> >>  drivers/usb/common/Makefile  |    3 +-
> >>  drivers/usb/common/usb-otg.c | 1061 ++++++++++++++++++++++++++++++++++++++++++
> >>  drivers/usb/common/usb-otg.h |   71 +++
> >>  drivers/usb/core/Kconfig     |   11 +-
> >>  include/linux/usb/otg.h      |  189 +++++++-
> >>  8 files changed, 1321 insertions(+), 21 deletions(-)
> >>  create mode 100644 drivers/usb/common/usb-otg.c
> >>  create mode 100644 drivers/usb/common/usb-otg.h
> >>
> > 
> > ... ...
> > 
> >> +
> >> +/**
> >> + * Get OTG device from host or gadget device.
> >> + *
> >> + * For non device tree boot, the OTG controller is assumed to be
> >> + * the parent of the host/gadget device.
> > 
> > This assumption/restriction maybe a problem, as I pointed in your previous
> > version, usb_create_hcd() use the passed dev as its dev, but,
> > usb_add_gadget_udc() use the passed dev as its parent dev, so often the
> > host and gadget don't share the same parent device, at least it doesn't
> > apply to chipidea case.
> 
> Let's provide a way for OTG driver to provide the OTG core exactly which is
> the related host/gadget device.
> 
> > 
> >> + * For device tree boot, the OTG controller is derived from the
> >> + * "otg-controller" property.
> >> + */
> >> +static struct device *usb_otg_get_device(struct device *hcd_gcd_dev)
> >> +{
> >> +	struct device *otg_dev;
> >> +
> >> +	if (!hcd_gcd_dev)
> >> +		return NULL;
> >> +
> >> +	if (hcd_gcd_dev->of_node) {
> >> +		struct device_node *np;
> >> +		struct platform_device *pdev;
> >> +
> >> +		np = of_parse_phandle(hcd_gcd_dev->of_node, "otg-controller",
> >> +				      0);
> >> +		if (!np)
> >> +			goto legacy;	/* continue legacy way */
> >> +
> >> +		pdev = of_find_device_by_node(np);
> >> +		of_node_put(np);
> >> +		if (!pdev) {
> >> +			dev_err(&pdev->dev, "couldn't get otg-controller device\n");
> >> +			return NULL;
> >> +		}
> >> +
> >> +		otg_dev = &pdev->dev;
> >> +		return otg_dev;
> >> +	}
> >> +
> >> +legacy:
> >> +	/* otg device is parent and must be registered */
> >> +	otg_dev = hcd_gcd_dev->parent;
> >> +	if (!usb_otg_get_data(otg_dev))
> >> +		return NULL;
> >> +
> >> +	return otg_dev;
> >> +}
> >> +
> > 
> > ... ...
> > 
> >> +static void usb_otg_init_timers(struct usb_otg *otgd, unsigned *timeouts)
> >> +{
> >> +	struct otg_fsm *fsm = &otgd->fsm;
> >> +	unsigned long tmouts[NUM_OTG_FSM_TIMERS];
> >> +	int i;
> >> +
> >> +	/* set default timeouts */
> >> +	tmouts[A_WAIT_VRISE] = TA_WAIT_VRISE;
> >> +	tmouts[A_WAIT_VFALL] = TA_WAIT_VFALL;
> >> +	tmouts[A_WAIT_BCON] = TA_WAIT_BCON;
> >> +	tmouts[A_AIDL_BDIS] = TA_AIDL_BDIS;
> >> +	tmouts[A_BIDL_ADIS] = TA_BIDL_ADIS;
> >> +	tmouts[B_ASE0_BRST] = TB_ASE0_BRST;
> >> +	tmouts[B_SE0_SRP] = TB_SE0_SRP;
> >> +	tmouts[B_SRP_FAIL] = TB_SRP_FAIL;
> >> +
> >> +	/* set controller provided timeouts */
> >> +	if (timeouts) {
> >> +		for (i = 0; i < NUM_OTG_FSM_TIMERS; i++) {
> >> +			if (timeouts[i])
> >> +				tmouts[i] = timeouts[i];
> >> +		}
> >> +	}
> >> +
> >> +	otg_timer_init(A_WAIT_VRISE, otgd, set_tmout, TA_WAIT_VRISE,
> >> +		       &fsm->a_wait_vrise_tmout);
> >> +	otg_timer_init(A_WAIT_VFALL, otgd, set_tmout, TA_WAIT_VFALL,
> >> +		       &fsm->a_wait_vfall_tmout);
> >> +	otg_timer_init(A_WAIT_BCON, otgd, set_tmout, TA_WAIT_BCON,
> >> +		       &fsm->a_wait_bcon_tmout);
> >> +	otg_timer_init(A_AIDL_BDIS, otgd, set_tmout, TA_AIDL_BDIS,
> >> +		       &fsm->a_aidl_bdis_tmout);
> >> +	otg_timer_init(A_BIDL_ADIS, otgd, set_tmout, TA_BIDL_ADIS,
> >> +		       &fsm->a_bidl_adis_tmout);
> >> +	otg_timer_init(B_ASE0_BRST, otgd, set_tmout, TB_ASE0_BRST,
> >> +		       &fsm->b_ase0_brst_tmout);
> >> +
> >> +	otg_timer_init(B_SE0_SRP, otgd, set_tmout, TB_SE0_SRP,
> >> +		       &fsm->b_se0_srp);
> >> +	otg_timer_init(B_SRP_FAIL, otgd, set_tmout, TB_SRP_FAIL,
> >> +		       &fsm->b_srp_done);
> >> +
> >> +	/* FIXME: what about A_WAIT_ENUM? */
> > 
> > Either you init it as other timers, or you remove all of it, otherwise
> > there will be NULL pointer crash.
> 
> I want to initialize it but was not sure about the timeout value.
> What timeout value I must use?
> 

It's not defined in OTG spec, I don't know either.
or you filter it out when add/del timer in below 2 functions.
	if (id == A_WAIT_ENUM)
		return;

> > 
> >> +}
> >> +
> >> +/**
> >> + * OTG FSM ops function to add timer
> >> + */
> >> +static void usb_otg_add_timer(struct otg_fsm *fsm, enum otg_fsm_timer id)
> >> +{
> >> +	struct usb_otg *otgd = container_of(fsm, struct usb_otg, fsm);
> >> +	struct otg_timer *otgtimer = &otgd->timers[id];
> >> +	struct hrtimer *timer = &otgtimer->timer;
> >> +
> >> +	if (!otgd->fsm_running)
> >> +		return;
> >> +
> >> +	/* if timer is already active, exit */
> >> +	if (hrtimer_active(timer)) {
> >> +		dev_err(otgd->dev, "otg: timer %d is already running\n", id);
> >> +		return;
> >> +	}
> >> +
> >> +	hrtimer_start(timer, otgtimer->timeout, HRTIMER_MODE_REL);
> >> +}
> >> +
> >> +/**
> >> + * OTG FSM ops function to delete timer
> >> + */
> >> +static void usb_otg_del_timer(struct otg_fsm *fsm, enum otg_fsm_timer id)
> >> +{
> >> +	struct usb_otg *otgd = container_of(fsm, struct usb_otg, fsm);
> >> +	struct hrtimer *timer = &otgd->timers[id].timer;
> >> +
> >> +	hrtimer_cancel(timer);
> >> +}
> >> +

... ...

> >> +	}
> >> +
> >> +	otgd->dev = dev;
> >> +	otgd->caps = &config->otg_caps;
> > 
> > How about define otgd->caps as a pointer, then don't need copy it.
> 
> otgd->caps is a pointer.
> 

okay, you are right.

> > 
> >> +	INIT_WORK(&otgd->work, usb_otg_work);
> >> +	otgd->wq = create_singlethread_workqueue("usb_otg");
> >> +	if (!otgd->wq) {
> >> +		dev_err(dev, "otg: %s: can't create workqueue\n",
> >> +			__func__);
> >> +		ret = -ENOMEM;
> >> +		goto err_wq;
> >> +	}
> >> +
> >> +	usb_otg_init_timers(otgd, config->otg_timeouts);
> >> +
> >> +	/* create copy of original ops */
> >> +	otgd->fsm_ops = *config->fsm_ops;
> > 
> > The same, use a pointer is enough?
> 
> We are creating a copy because we are overriding timer ops.
> 

okay.

> > 
> >> +	/* FIXME: we ignore caller's timer ops */
> >> +	otgd->fsm_ops.add_timer = usb_otg_add_timer;
> >> +	otgd->fsm_ops.del_timer = usb_otg_del_timer;
> >> +	/* set otg ops */
> >> +	otgd->fsm.ops = &otgd->fsm_ops;
> >> +	otgd->fsm.otg = otgd;
> >> + *

... ...

> >> + * This is used by the USB Host stack to register the Host controller
> >> + * to the OTG core. Host controller must not be started by the
> >> + * caller as it is left upto the OTG state machine to do so.
> > 
> > I am confused on how to use this function.
> > - This function should be called before start fsm per usb_otg_start_fsm().
> 
> yes.
> 
> > - Called by usb_add_hcd(), so we need call usb_add_hcd() before start fsm.
> 
> yes.
> 
> > - If I want to add hcd when switch to host role, and remove hcd when switch
> >   to peripheral, with this design, I cannot use this function?
> 
> You add hcd only once during the life of the OTG device. If it is linked to the
> OTG controller the OTG fsm manages the start/stop of hcd using the otg_hcd_ops.
> 
> "usb/core/hcd.c"
> static struct otg_hcd_ops otg_hcd_intf = {
>         .add = usb_otg_add_hcd,
>         .remove = usb_otg_remove_hcd,
> };
> 
> Your otg driver must use teh usb_otg_add/remove_hcd to start/stop the controller.
> Using usb_remove_hcd() means the hcd resource is no longer available and the
> otg fsm will be stopped.
> 
> > - How about split it out of usb_add_hcd()?
> 
> Adding the HCD and starting/stopping the hcd is split into
> usb_add/remove_hcd() and usb_otg_add/remove_hcd() for OTG case.
> 

Catch your point now.
Do usb_add_hcd to register hcd before start fsm, and use usb_otg_start_host()
to start/stop host role for otg fsm.

> > 
> >> + *
> >> + * Returns: 0 on success, error value otherwise.
> >> + */
> >> +int usb_otg_register_hcd(struct usb_hcd *hcd, unsigned int irqnum,
> >> +			 unsigned long irqflags, struct otg_hcd_ops *ops)
> >> +{
> >> +	struct usb_otg *otgd;
> >> +	struct device *hcd_dev = hcd->self.controller;
> >> +	struct device *otg_dev = usb_otg_get_device(hcd_dev);
> >> +
> >> +	if (!otg_dev)
> >> +		return -EINVAL;	/* we're definitely not OTG */
> >> +
> >> +	/* we're otg but otg controller might not yet be registered */
> >> +	mutex_lock(&otg_list_mutex);
> >> +	otgd = usb_otg_get_data(otg_dev);
> >> +	mutex_unlock(&otg_list_mutex);
> >> +	if (!otgd) {
> >> +		dev_dbg(hcd_dev,
> >> +			"otg: controller not yet registered. waiting..\n");
> >> +		/*
> >> +		 * otg controller might register later. Put the hcd in
> >> +		 * wait list and call us back when ready
> >> +		 */
> >> +		if (usb_otg_hcd_wait_add(otg_dev, hcd, irqnum, irqflags, ops)) {
> >> +			dev_dbg(hcd_dev, "otg: failed to add to wait list\n");
> >> +			return -EINVAL;
> >> +		}
> >> +
> >> +		return 0;
> >> +	}
> >> +
> >> +	/* HCD will be started by OTG fsm when needed */
> >> +	mutex_lock(&otgd->fsm.lock);
> > 
> > If call usb_add_hcd() when start host role, deadlock.
> 
> No. You must call usb_otg_add_hcd() to start host role.
> 
> > 
> >> +	if (otgd->primary_hcd.hcd) {
> >> +		/* probably a shared HCD ? */
> >> +		if (usb_otg_hcd_is_primary_hcd(hcd)) {
> >> +			dev_err(otg_dev, "otg: primary host already registered\n");
> >> +			goto err;
> >> +		}
> >> +
> >> +		if (hcd->shared_hcd == otgd->primary_hcd.hcd) {
> >> +			if (otgd->shared_hcd.hcd) {
> >> +				dev_err(otg_dev, "otg: shared host already registered\n");
> >> +				goto err;
> >> +			}
> >> +
> >> +			otgd->shared_hcd.hcd = hcd;
> >> +			otgd->shared_hcd.irqnum = irqnum;
> >> +			otgd->shared_hcd.irqflags = irqflags;
> >> +			otgd->shared_hcd.ops = ops;
> >> +			dev_info(otg_dev, "otg: shared host %s registered\n",
> >> +				 dev_name(hcd->self.controller));
> >> +		} else {
> >> +			dev_err(otg_dev, "otg: invalid shared host %s\n",
> >> +				dev_name(hcd->self.controller));
> >> +			goto err;
> >> +		}
> >> +	} else {
> >> +		if (!usb_otg_hcd_is_primary_hcd(hcd)) {
> >> +			dev_err(otg_dev, "otg: primary host must be registered first\n");
> >> +			goto err;
> >> +		}
> >> +
> >> +		otgd->primary_hcd.hcd = hcd;
> >> +		otgd->primary_hcd.irqnum = irqnum;
> >> +		otgd->primary_hcd.irqflags = irqflags;
> >> +		otgd->primary_hcd.ops = ops;
> >> +		dev_info(otg_dev, "otg: primary host %s registered\n",
> >> +			 dev_name(hcd->self.controller));
> >> +	}
> >> +
> >> +	/*
> >> +	 * we're ready only if we have shared HCD
> >> +	 * or we don't need shared HCD.
> >> +	 */
> >> +	if (otgd->shared_hcd.hcd || !otgd->primary_hcd.hcd->shared_hcd) {
> >> +		otgd->fsm.otg->host = hcd_to_bus(hcd);
> > 
> > otgd->host = hcd_to_bus(hcd);
> 
> ok. So we set host at both places. struct usb_otg in struct otg_fsm starts to
> feel redundant now. I think we should get rid of it and get the usb_otg struct
> using container_of() instead.
> 

Then you may come to force existing otg fsm code to use your OTG core.

> > 
> >> +		/* FIXME: set bus->otg_port if this is true OTG port with HNP */
> >> +
> >> +		/* start FSM */
> >> +		usb_otg_start_fsm(&otgd->fsm);
> >> +	} else {
> >> +		dev_dbg(otg_dev, "otg: can't start till shared host registers\n");
> >> +	}
> >> +
> >> +	mutex_unlock(&otgd->fsm.lock);
> >> +
> >> +	return 0;
> >> +
> >> +err:
> >> +	mutex_unlock(&otgd->fsm.lock);
> >> +	return -EINVAL;
> > 
> > Return non-zero, then if err, do we need call usb_otg_add_hcd() after
> > usb_otg_register_hcd() fails?
> 
> You should not call usb_otg_register_hcd() but usb_add_hcd().
> If that fails then you fail as ususal.

My point is if we use usb_add_hcd(), but failed in usb_otg_register_hcd(),
then usb_otg_add_hcd() will be called in *all* error case, is this your
expectation?
	if (usb_otg_register_hcd(hcd, irqnum, irqflags, &otg_hcd_intf))
		return usb_otg_add_hcd(hcd, irqnum, irqflags);

> >> + * @fsm_running: state machine running/stopped indicator
> >> + */
> >>  struct usb_otg {
> >>  	u8			default_a;
> >>  
> >>  	struct phy		*phy;
> >>  	/* old usb_phy interface */
> >>  	struct usb_phy		*usb_phy;
> >> +
> > 
> > add a blank line?
> > 

You missed this.

> >>  	struct usb_bus		*host;
> >>  	struct usb_gadget	*gadget;
> >>  
> >>  	enum usb_otg_state	state;
> >>  
> >> +	struct device *dev;
> >> +	struct usb_otg_caps *caps;
> >> +	struct otg_fsm fsm;
> >> +	struct otg_fsm_ops fsm_ops;
> >> +
> >> +	/* internal use only */
> >> +	struct otg_hcd primary_hcd;
> >> +	struct otg_hcd shared_hcd;
> >> +	struct otg_gadget_ops *gadget_ops;
> >> +	struct otg_timer timers[NUM_OTG_FSM_TIMERS];
> >> +	struct list_head list;
> >> +	struct work_struct work;
> >> -- 
> >> 2.1.4


^ permalink raw reply	[flat|nested] 48+ messages in thread

* Re: [PATCH v4 07/13] usb: otg: add OTG core
  2015-09-09  9:08                   ` Roger Quadros
@ 2015-09-09  8:13                       ` Peter Chen
  -1 siblings, 0 replies; 48+ messages in thread
From: Peter Chen @ 2015-09-09  8:13 UTC (permalink / raw)
  To: Roger Quadros
  Cc: stern-nwvwT67g6+6dFdvTe/nMLpVzexx5G7lz, balbi-l0cyMroinI0,
	gregkh-hQyY1W1yCW8ekmWlsbkhG0B+6BGkLq7r,
	dan.j.williams-ral2JQCrhuEAvxtiuMwx3w,
	jun.li-KZfg59tc24xl57MIdRCFDg,
	mathias.nyman-VuQAYsv1563Yd54FQh9/CA, tony-4v6yS6AI5VpBDgjK7y7TUQ,
	Joao.Pinto-HKixBCOQz3hWk0Htik3J/w,
	abrestic-F7+t8E8rja9g9hUCZPvPmw, linux-usb-u79uwXL29TY76Z2rM5mHXA,
	linux-kernel-u79uwXL29TY76Z2rM5mHXA,
	linux-omap-u79uwXL29TY76Z2rM5mHXA

On Wed, Sep 09, 2015 at 12:08:10PM +0300, Roger Quadros wrote:
> On 09/09/15 05:21, Peter Chen wrote:
> > On Tue, Sep 08, 2015 at 03:25:25PM +0300, Roger Quadros wrote:
> >>
> >>
> >> On 08/09/15 11:31, Peter Chen wrote:
> >>> On Mon, Sep 07, 2015 at 01:23:01PM +0300, Roger Quadros wrote:
> >>>> On 07/09/15 04:23, Peter Chen wrote:
> >>>>> On Mon, Aug 24, 2015 at 04:21:18PM +0300, Roger Quadros wrote:
> >>>>>> + * This is used by the USB Host stack to register the Host controller
> >>>>>> + * to the OTG core. Host controller must not be started by the
> >>>>>> + * caller as it is left upto the OTG state machine to do so.
> >>>>>> + *
> >>>>>> + * Returns: 0 on success, error value otherwise.
> >>>>>> + */
> >>>>>> +int usb_otg_register_hcd(struct usb_hcd *hcd, unsigned int irqnum,
> >>>>>> +			 unsigned long irqflags, struct otg_hcd_ops *ops)
> >>>>>> +{
> >>>>>> +	struct usb_otg *otgd;
> >>>>>> +	struct device *hcd_dev = hcd->self.controller;
> >>>>>> +	struct device *otg_dev = usb_otg_get_device(hcd_dev);
> >>>>>> +
> >>>>>
> >>>>> One big problem here is: there are two designs for current (IP) driver
> >>>>> code, one creates dedicated hcd device as roothub's parent, like dwc3.
> >>>>> Another one doesn't do this, roothub's parent is core device (or otg device
> >>>>> in your patch), like chipidea and dwc2.
> >>>>>
> >>>>> Then, otg_dev will be glue layer device for chipidea after that.
> >>>>
> >>>> OK. Let's add a way for the otg controller driver to provide the host and gadget
> >>>> information to the otg core for such devices like chipidea and dwc2.
> >>>>
> >>>
> >>> Roger, not only chipidea and dwc2, I think the musb uses the same
> >>> hierarchy. If the host, device, and otg share the same register
> >>> region, host part can't be a platform driver since we don't want
> >>> to remap the same register region again.
> >>>
> >>> So, in the design, we may need to consider both situations, one
> >>> is otg/host/device has its own register region, and host is a
> >>> separate platform device (A), the other is three parts share the
> >>> same register region, there is only one platform driver (B).
> >>>
> >>> A:
> >>>
> >>> 			IP core device 
> >>> 			    |
> >>> 			    |
> >>> 		      |-----|-----|
> >>> 		      gadget   host platform device	
> >>> 		      		|
> >>> 				roothub
> >>>
> >>> B:
> >>>
> >>> 			IP core device
> >>> 			    |
> >>> 			    |
> >>> 		      |-----|-----|
> >>> 		      gadget   	 roothub
> >>> 		      		
> >>>
> >>>> This API must be called before the hcd/gadget-driver is added so that the otg
> >>>> core knows it's linked to an OTG controller.
> >>>>
> >>>> Any better idea?
> >>>>
> >>>
> >>> A flag stands for this hcd controller is the same with otg controller
> >>> can be used, this flag can be stored at struct usb_otg_config.
> >>
> >> What if there is another architecture like so?
> >>
> >> C:
> >> 			[Parent]
> >> 			   |
> >> 			   |
> >> 		|------------------|--------------|
> >> 	[OTG core]		[gadget]	[host]
> >>
> >> We need a more flexible mechanism to link the gadget and
> >> host device to the otg core for non DT case.
> >>
> >> How about adding struct usb_otg parameter to usb_otg_register_hcd()?
> >>
> >> e.g.
> >> int usb_otg_register_hcd(struct usb_otg *otg, struct usb_hcd *hcd, ..)
> >>
> >> If otg is NULL it will try DT otg-controller property or parent to
> >> get the otg controller.
> > 
> > How usb_otg_register_hcd get struct usb_otg, from where?
> 
> This only works when the parent driver creating the hcd has registered the
> otg controller too.
> 

Sorry? So we need to find another way to solve this issue, right?

> > 
> >>
> >>>
> >>> Peter
> >>>
> >>> P.S: I still read your code, I find not all APIs in this file are used
> >>> in your dwc3 example. 
> >>
> >> Which ones? The ones for registering/unregistered host/gadget are used
> >> by hcd/udc core as part of usb_add/remove_hcd() and
> >> udc_bind_to_driver()/usb_gadget_remove_driver()
> >>
> > 
> > Ok, now I understand your design, usb_create_hcd must be called before
> > the fsm kicks off. The call flow like below:
> > 
> > usb_otg_register->usb_create_hcd->usb_add_hcd->usb_otg_register_hcd->
> > usb_otg_start_host->usb_otg_add_hcd
> 
> Actually these are the discrete steps
> - usb_otg_register
> - usb_create_hcd
> - usb_add_hcd->usb_otg_register_hcd  (HCD is not really added till FSM in host role)
> 
> Above 3 are prerequisite for FSM to start in addition to gadget controller and
> driver being ready.
> 
> When FSM enters in host role
> - usb_otg_start_host(true)->usb_otg_add_hcd (Now HCD is added)
> 
> When FSM exits host role
> - usb_otg_start_host(false)->usb_otg_remove_hcd (Now HCD is removed, but not unregistered)
> 
> If HCD hardware really goes away
> - usb_remove_hcd->usb_otg_unregister_hcd (Now FSM stops as host resource not available)
> 
> > 
> > We need to make some changes to let chipidea work since usb_create_hcd
> > is included at host->start.
> > 
> 
> Yes, you just need to do the usb_add_hcd() in probe
> and usb_otg_start_host() during role switch.
> 

Thanks.

-- 

Best Regards,
Peter Chen
--
To unsubscribe from this list: send the line "unsubscribe linux-usb" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply	[flat|nested] 48+ messages in thread

* Re: [PATCH v4 07/13] usb: otg: add OTG core
@ 2015-09-09  8:13                       ` Peter Chen
  0 siblings, 0 replies; 48+ messages in thread
From: Peter Chen @ 2015-09-09  8:13 UTC (permalink / raw)
  To: Roger Quadros
  Cc: stern, balbi, gregkh, dan.j.williams, jun.li, mathias.nyman, tony,
	Joao.Pinto, abrestic, linux-usb, linux-kernel, linux-omap

On Wed, Sep 09, 2015 at 12:08:10PM +0300, Roger Quadros wrote:
> On 09/09/15 05:21, Peter Chen wrote:
> > On Tue, Sep 08, 2015 at 03:25:25PM +0300, Roger Quadros wrote:
> >>
> >>
> >> On 08/09/15 11:31, Peter Chen wrote:
> >>> On Mon, Sep 07, 2015 at 01:23:01PM +0300, Roger Quadros wrote:
> >>>> On 07/09/15 04:23, Peter Chen wrote:
> >>>>> On Mon, Aug 24, 2015 at 04:21:18PM +0300, Roger Quadros wrote:
> >>>>>> + * This is used by the USB Host stack to register the Host controller
> >>>>>> + * to the OTG core. Host controller must not be started by the
> >>>>>> + * caller as it is left upto the OTG state machine to do so.
> >>>>>> + *
> >>>>>> + * Returns: 0 on success, error value otherwise.
> >>>>>> + */
> >>>>>> +int usb_otg_register_hcd(struct usb_hcd *hcd, unsigned int irqnum,
> >>>>>> +			 unsigned long irqflags, struct otg_hcd_ops *ops)
> >>>>>> +{
> >>>>>> +	struct usb_otg *otgd;
> >>>>>> +	struct device *hcd_dev = hcd->self.controller;
> >>>>>> +	struct device *otg_dev = usb_otg_get_device(hcd_dev);
> >>>>>> +
> >>>>>
> >>>>> One big problem here is: there are two designs for current (IP) driver
> >>>>> code, one creates dedicated hcd device as roothub's parent, like dwc3.
> >>>>> Another one doesn't do this, roothub's parent is core device (or otg device
> >>>>> in your patch), like chipidea and dwc2.
> >>>>>
> >>>>> Then, otg_dev will be glue layer device for chipidea after that.
> >>>>
> >>>> OK. Let's add a way for the otg controller driver to provide the host and gadget
> >>>> information to the otg core for such devices like chipidea and dwc2.
> >>>>
> >>>
> >>> Roger, not only chipidea and dwc2, I think the musb uses the same
> >>> hierarchy. If the host, device, and otg share the same register
> >>> region, host part can't be a platform driver since we don't want
> >>> to remap the same register region again.
> >>>
> >>> So, in the design, we may need to consider both situations, one
> >>> is otg/host/device has its own register region, and host is a
> >>> separate platform device (A), the other is three parts share the
> >>> same register region, there is only one platform driver (B).
> >>>
> >>> A:
> >>>
> >>> 			IP core device 
> >>> 			    |
> >>> 			    |
> >>> 		      |-----|-----|
> >>> 		      gadget   host platform device	
> >>> 		      		|
> >>> 				roothub
> >>>
> >>> B:
> >>>
> >>> 			IP core device
> >>> 			    |
> >>> 			    |
> >>> 		      |-----|-----|
> >>> 		      gadget   	 roothub
> >>> 		      		
> >>>
> >>>> This API must be called before the hcd/gadget-driver is added so that the otg
> >>>> core knows it's linked to an OTG controller.
> >>>>
> >>>> Any better idea?
> >>>>
> >>>
> >>> A flag stands for this hcd controller is the same with otg controller
> >>> can be used, this flag can be stored at struct usb_otg_config.
> >>
> >> What if there is another architecture like so?
> >>
> >> C:
> >> 			[Parent]
> >> 			   |
> >> 			   |
> >> 		|------------------|--------------|
> >> 	[OTG core]		[gadget]	[host]
> >>
> >> We need a more flexible mechanism to link the gadget and
> >> host device to the otg core for non DT case.
> >>
> >> How about adding struct usb_otg parameter to usb_otg_register_hcd()?
> >>
> >> e.g.
> >> int usb_otg_register_hcd(struct usb_otg *otg, struct usb_hcd *hcd, ..)
> >>
> >> If otg is NULL it will try DT otg-controller property or parent to
> >> get the otg controller.
> > 
> > How usb_otg_register_hcd get struct usb_otg, from where?
> 
> This only works when the parent driver creating the hcd has registered the
> otg controller too.
> 

Sorry? So we need to find another way to solve this issue, right?

> > 
> >>
> >>>
> >>> Peter
> >>>
> >>> P.S: I still read your code, I find not all APIs in this file are used
> >>> in your dwc3 example. 
> >>
> >> Which ones? The ones for registering/unregistered host/gadget are used
> >> by hcd/udc core as part of usb_add/remove_hcd() and
> >> udc_bind_to_driver()/usb_gadget_remove_driver()
> >>
> > 
> > Ok, now I understand your design, usb_create_hcd must be called before
> > the fsm kicks off. The call flow like below:
> > 
> > usb_otg_register->usb_create_hcd->usb_add_hcd->usb_otg_register_hcd->
> > usb_otg_start_host->usb_otg_add_hcd
> 
> Actually these are the discrete steps
> - usb_otg_register
> - usb_create_hcd
> - usb_add_hcd->usb_otg_register_hcd  (HCD is not really added till FSM in host role)
> 
> Above 3 are prerequisite for FSM to start in addition to gadget controller and
> driver being ready.
> 
> When FSM enters in host role
> - usb_otg_start_host(true)->usb_otg_add_hcd (Now HCD is added)
> 
> When FSM exits host role
> - usb_otg_start_host(false)->usb_otg_remove_hcd (Now HCD is removed, but not unregistered)
> 
> If HCD hardware really goes away
> - usb_remove_hcd->usb_otg_unregister_hcd (Now FSM stops as host resource not available)
> 
> > 
> > We need to make some changes to let chipidea work since usb_create_hcd
> > is included at host->start.
> > 
> 
> Yes, you just need to do the usb_add_hcd() in probe
> and usb_otg_start_host() during role switch.
> 

Thanks.

-- 

Best Regards,
Peter Chen

^ permalink raw reply	[flat|nested] 48+ messages in thread

* Re: [PATCH v4 07/13] usb: otg: add OTG core
  2015-09-09  9:33                         ` Roger Quadros
@ 2015-09-09  8:45                           ` Peter Chen
  -1 siblings, 0 replies; 48+ messages in thread
From: Peter Chen @ 2015-09-09  8:45 UTC (permalink / raw)
  To: Roger Quadros
  Cc: stern, balbi, gregkh, dan.j.williams, jun.li, mathias.nyman, tony,
	Joao.Pinto, abrestic, linux-usb, linux-kernel, linux-omap

On Wed, Sep 09, 2015 at 12:33:20PM +0300, Roger Quadros wrote:
> On 09/09/15 11:13, Peter Chen wrote:
> > On Wed, Sep 09, 2015 at 12:08:10PM +0300, Roger Quadros wrote:
> >> On 09/09/15 05:21, Peter Chen wrote:
> >>> On Tue, Sep 08, 2015 at 03:25:25PM +0300, Roger Quadros wrote:
> >>>>
> >>>>
> >>>> On 08/09/15 11:31, Peter Chen wrote:
> >>>>> On Mon, Sep 07, 2015 at 01:23:01PM +0300, Roger Quadros wrote:
> >>>>>> On 07/09/15 04:23, Peter Chen wrote:
> >>>>>>> On Mon, Aug 24, 2015 at 04:21:18PM +0300, Roger Quadros wrote:
> >>>>>>>> + * This is used by the USB Host stack to register the Host controller
> >>>>>>>> + * to the OTG core. Host controller must not be started by the
> >>>>>>>> + * caller as it is left upto the OTG state machine to do so.
> >>>>>>>> + *
> >>>>>>>> + * Returns: 0 on success, error value otherwise.
> >>>>>>>> + */
> >>>>>>>> +int usb_otg_register_hcd(struct usb_hcd *hcd, unsigned int irqnum,
> >>>>>>>> +			 unsigned long irqflags, struct otg_hcd_ops *ops)
> >>>>>>>> +{
> >>>>>>>> +	struct usb_otg *otgd;
> >>>>>>>> +	struct device *hcd_dev = hcd->self.controller;
> >>>>>>>> +	struct device *otg_dev = usb_otg_get_device(hcd_dev);
> >>>>>>>> +
> >>>>>>>
> >>>>>>> One big problem here is: there are two designs for current (IP) driver
> >>>>>>> code, one creates dedicated hcd device as roothub's parent, like dwc3.
> >>>>>>> Another one doesn't do this, roothub's parent is core device (or otg device
> >>>>>>> in your patch), like chipidea and dwc2.
> >>>>>>>
> >>>>>>> Then, otg_dev will be glue layer device for chipidea after that.
> >>>>>>
> >>>>>> OK. Let's add a way for the otg controller driver to provide the host and gadget
> >>>>>> information to the otg core for such devices like chipidea and dwc2.
> >>>>>>
> >>>>>
> >>>>> Roger, not only chipidea and dwc2, I think the musb uses the same
> >>>>> hierarchy. If the host, device, and otg share the same register
> >>>>> region, host part can't be a platform driver since we don't want
> >>>>> to remap the same register region again.
> >>>>>
> >>>>> So, in the design, we may need to consider both situations, one
> >>>>> is otg/host/device has its own register region, and host is a
> >>>>> separate platform device (A), the other is three parts share the
> >>>>> same register region, there is only one platform driver (B).
> >>>>>
> >>>>> A:
> >>>>>
> >>>>> 			IP core device 
> >>>>> 			    |
> >>>>> 			    |
> >>>>> 		      |-----|-----|
> >>>>> 		      gadget   host platform device	
> >>>>> 		      		|
> >>>>> 				roothub
> >>>>>
> >>>>> B:
> >>>>>
> >>>>> 			IP core device
> >>>>> 			    |
> >>>>> 			    |
> >>>>> 		      |-----|-----|
> >>>>> 		      gadget   	 roothub
> >>>>> 		      		
> >>>>>
> >>>>>> This API must be called before the hcd/gadget-driver is added so that the otg
> >>>>>> core knows it's linked to an OTG controller.
> >>>>>>
> >>>>>> Any better idea?
> >>>>>>
> >>>>>
> >>>>> A flag stands for this hcd controller is the same with otg controller
> >>>>> can be used, this flag can be stored at struct usb_otg_config.
> >>>>
> >>>> What if there is another architecture like so?
> >>>>
> >>>> C:
> >>>> 			[Parent]
> >>>> 			   |
> >>>> 			   |
> >>>> 		|------------------|--------------|
> >>>> 	[OTG core]		[gadget]	[host]
> >>>>
> >>>> We need a more flexible mechanism to link the gadget and
> >>>> host device to the otg core for non DT case.
> >>>>
> >>>> How about adding struct usb_otg parameter to usb_otg_register_hcd()?
> >>>>
> >>>> e.g.
> >>>> int usb_otg_register_hcd(struct usb_otg *otg, struct usb_hcd *hcd, ..)
> >>>>
> >>>> If otg is NULL it will try DT otg-controller property or parent to
> >>>> get the otg controller.
> >>>
> >>> How usb_otg_register_hcd get struct usb_otg, from where?
> >>
> >> This only works when the parent driver creating the hcd has registered the
> >> otg controller too.
> >>
> > 
> > Sorry? So we need to find another way to solve this issue, right?
> 
> For existing cases this is sufficient.
> The otg device is either the one supplied during usb_otg_register_hcd
> (cases B and C) or it is the parent device (case A).

How we differentiate case A and case B at usb_otg_register_hcd?
Would you show me the sample code?

> 
> It does not work when the 3 devices are totally independent and get registered
> at different times.
> I don't think there is such a case for non-DT yet, but let's not have this
> limitation. So yes, we need to look for better solution :).
> 

Yes, we need to find some places to store gadget/host/otg information,
Alan's suggestion to save them at device drvdata may be a direction, but
I still doesn't have way to cover all cases.

-- 

Best Regards,
Peter Chen

^ permalink raw reply	[flat|nested] 48+ messages in thread

* Re: [PATCH v4 07/13] usb: otg: add OTG core
@ 2015-09-09  8:45                           ` Peter Chen
  0 siblings, 0 replies; 48+ messages in thread
From: Peter Chen @ 2015-09-09  8:45 UTC (permalink / raw)
  To: Roger Quadros
  Cc: stern, balbi, gregkh, dan.j.williams, jun.li, mathias.nyman, tony,
	Joao.Pinto, abrestic, linux-usb, linux-kernel, linux-omap

On Wed, Sep 09, 2015 at 12:33:20PM +0300, Roger Quadros wrote:
> On 09/09/15 11:13, Peter Chen wrote:
> > On Wed, Sep 09, 2015 at 12:08:10PM +0300, Roger Quadros wrote:
> >> On 09/09/15 05:21, Peter Chen wrote:
> >>> On Tue, Sep 08, 2015 at 03:25:25PM +0300, Roger Quadros wrote:
> >>>>
> >>>>
> >>>> On 08/09/15 11:31, Peter Chen wrote:
> >>>>> On Mon, Sep 07, 2015 at 01:23:01PM +0300, Roger Quadros wrote:
> >>>>>> On 07/09/15 04:23, Peter Chen wrote:
> >>>>>>> On Mon, Aug 24, 2015 at 04:21:18PM +0300, Roger Quadros wrote:
> >>>>>>>> + * This is used by the USB Host stack to register the Host controller
> >>>>>>>> + * to the OTG core. Host controller must not be started by the
> >>>>>>>> + * caller as it is left upto the OTG state machine to do so.
> >>>>>>>> + *
> >>>>>>>> + * Returns: 0 on success, error value otherwise.
> >>>>>>>> + */
> >>>>>>>> +int usb_otg_register_hcd(struct usb_hcd *hcd, unsigned int irqnum,
> >>>>>>>> +			 unsigned long irqflags, struct otg_hcd_ops *ops)
> >>>>>>>> +{
> >>>>>>>> +	struct usb_otg *otgd;
> >>>>>>>> +	struct device *hcd_dev = hcd->self.controller;
> >>>>>>>> +	struct device *otg_dev = usb_otg_get_device(hcd_dev);
> >>>>>>>> +
> >>>>>>>
> >>>>>>> One big problem here is: there are two designs for current (IP) driver
> >>>>>>> code, one creates dedicated hcd device as roothub's parent, like dwc3.
> >>>>>>> Another one doesn't do this, roothub's parent is core device (or otg device
> >>>>>>> in your patch), like chipidea and dwc2.
> >>>>>>>
> >>>>>>> Then, otg_dev will be glue layer device for chipidea after that.
> >>>>>>
> >>>>>> OK. Let's add a way for the otg controller driver to provide the host and gadget
> >>>>>> information to the otg core for such devices like chipidea and dwc2.
> >>>>>>
> >>>>>
> >>>>> Roger, not only chipidea and dwc2, I think the musb uses the same
> >>>>> hierarchy. If the host, device, and otg share the same register
> >>>>> region, host part can't be a platform driver since we don't want
> >>>>> to remap the same register region again.
> >>>>>
> >>>>> So, in the design, we may need to consider both situations, one
> >>>>> is otg/host/device has its own register region, and host is a
> >>>>> separate platform device (A), the other is three parts share the
> >>>>> same register region, there is only one platform driver (B).
> >>>>>
> >>>>> A:
> >>>>>
> >>>>> 			IP core device 
> >>>>> 			    |
> >>>>> 			    |
> >>>>> 		      |-----|-----|
> >>>>> 		      gadget   host platform device	
> >>>>> 		      		|
> >>>>> 				roothub
> >>>>>
> >>>>> B:
> >>>>>
> >>>>> 			IP core device
> >>>>> 			    |
> >>>>> 			    |
> >>>>> 		      |-----|-----|
> >>>>> 		      gadget   	 roothub
> >>>>> 		      		
> >>>>>
> >>>>>> This API must be called before the hcd/gadget-driver is added so that the otg
> >>>>>> core knows it's linked to an OTG controller.
> >>>>>>
> >>>>>> Any better idea?
> >>>>>>
> >>>>>
> >>>>> A flag stands for this hcd controller is the same with otg controller
> >>>>> can be used, this flag can be stored at struct usb_otg_config.
> >>>>
> >>>> What if there is another architecture like so?
> >>>>
> >>>> C:
> >>>> 			[Parent]
> >>>> 			   |
> >>>> 			   |
> >>>> 		|------------------|--------------|
> >>>> 	[OTG core]		[gadget]	[host]
> >>>>
> >>>> We need a more flexible mechanism to link the gadget and
> >>>> host device to the otg core for non DT case.
> >>>>
> >>>> How about adding struct usb_otg parameter to usb_otg_register_hcd()?
> >>>>
> >>>> e.g.
> >>>> int usb_otg_register_hcd(struct usb_otg *otg, struct usb_hcd *hcd, ..)
> >>>>
> >>>> If otg is NULL it will try DT otg-controller property or parent to
> >>>> get the otg controller.
> >>>
> >>> How usb_otg_register_hcd get struct usb_otg, from where?
> >>
> >> This only works when the parent driver creating the hcd has registered the
> >> otg controller too.
> >>
> > 
> > Sorry? So we need to find another way to solve this issue, right?
> 
> For existing cases this is sufficient.
> The otg device is either the one supplied during usb_otg_register_hcd
> (cases B and C) or it is the parent device (case A).

How we differentiate case A and case B at usb_otg_register_hcd?
Would you show me the sample code?

> 
> It does not work when the 3 devices are totally independent and get registered
> at different times.
> I don't think there is such a case for non-DT yet, but let's not have this
> limitation. So yes, we need to look for better solution :).
> 

Yes, we need to find some places to store gadget/host/otg information,
Alan's suggestion to save them at device drvdata may be a direction, but
I still doesn't have way to cover all cases.

-- 

Best Regards,
Peter Chen

^ permalink raw reply	[flat|nested] 48+ messages in thread

* Re: [PATCH v4 07/13] usb: otg: add OTG core
  2015-09-09  2:21                 ` Peter Chen
@ 2015-09-09  9:08                   ` Roger Quadros
  -1 siblings, 0 replies; 48+ messages in thread
From: Roger Quadros @ 2015-09-09  9:08 UTC (permalink / raw)
  To: Peter Chen
  Cc: stern-nwvwT67g6+6dFdvTe/nMLpVzexx5G7lz, balbi-l0cyMroinI0,
	gregkh-hQyY1W1yCW8ekmWlsbkhG0B+6BGkLq7r,
	dan.j.williams-ral2JQCrhuEAvxtiuMwx3w,
	jun.li-KZfg59tc24xl57MIdRCFDg,
	mathias.nyman-VuQAYsv1563Yd54FQh9/CA, tony-4v6yS6AI5VpBDgjK7y7TUQ,
	Joao.Pinto-HKixBCOQz3hWk0Htik3J/w,
	abrestic-F7+t8E8rja9g9hUCZPvPmw, linux-usb-u79uwXL29TY76Z2rM5mHXA,
	linux-kernel-u79uwXL29TY76Z2rM5mHXA,
	linux-omap-u79uwXL29TY76Z2rM5mHXA

On 09/09/15 05:21, Peter Chen wrote:
> On Tue, Sep 08, 2015 at 03:25:25PM +0300, Roger Quadros wrote:
>>
>>
>> On 08/09/15 11:31, Peter Chen wrote:
>>> On Mon, Sep 07, 2015 at 01:23:01PM +0300, Roger Quadros wrote:
>>>> On 07/09/15 04:23, Peter Chen wrote:
>>>>> On Mon, Aug 24, 2015 at 04:21:18PM +0300, Roger Quadros wrote:
>>>>>> + * This is used by the USB Host stack to register the Host controller
>>>>>> + * to the OTG core. Host controller must not be started by the
>>>>>> + * caller as it is left upto the OTG state machine to do so.
>>>>>> + *
>>>>>> + * Returns: 0 on success, error value otherwise.
>>>>>> + */
>>>>>> +int usb_otg_register_hcd(struct usb_hcd *hcd, unsigned int irqnum,
>>>>>> +			 unsigned long irqflags, struct otg_hcd_ops *ops)
>>>>>> +{
>>>>>> +	struct usb_otg *otgd;
>>>>>> +	struct device *hcd_dev = hcd->self.controller;
>>>>>> +	struct device *otg_dev = usb_otg_get_device(hcd_dev);
>>>>>> +
>>>>>
>>>>> One big problem here is: there are two designs for current (IP) driver
>>>>> code, one creates dedicated hcd device as roothub's parent, like dwc3.
>>>>> Another one doesn't do this, roothub's parent is core device (or otg device
>>>>> in your patch), like chipidea and dwc2.
>>>>>
>>>>> Then, otg_dev will be glue layer device for chipidea after that.
>>>>
>>>> OK. Let's add a way for the otg controller driver to provide the host and gadget
>>>> information to the otg core for such devices like chipidea and dwc2.
>>>>
>>>
>>> Roger, not only chipidea and dwc2, I think the musb uses the same
>>> hierarchy. If the host, device, and otg share the same register
>>> region, host part can't be a platform driver since we don't want
>>> to remap the same register region again.
>>>
>>> So, in the design, we may need to consider both situations, one
>>> is otg/host/device has its own register region, and host is a
>>> separate platform device (A), the other is three parts share the
>>> same register region, there is only one platform driver (B).
>>>
>>> A:
>>>
>>> 			IP core device 
>>> 			    |
>>> 			    |
>>> 		      |-----|-----|
>>> 		      gadget   host platform device	
>>> 		      		|
>>> 				roothub
>>>
>>> B:
>>>
>>> 			IP core device
>>> 			    |
>>> 			    |
>>> 		      |-----|-----|
>>> 		      gadget   	 roothub
>>> 		      		
>>>
>>>> This API must be called before the hcd/gadget-driver is added so that the otg
>>>> core knows it's linked to an OTG controller.
>>>>
>>>> Any better idea?
>>>>
>>>
>>> A flag stands for this hcd controller is the same with otg controller
>>> can be used, this flag can be stored at struct usb_otg_config.
>>
>> What if there is another architecture like so?
>>
>> C:
>> 			[Parent]
>> 			   |
>> 			   |
>> 		|------------------|--------------|
>> 	[OTG core]		[gadget]	[host]
>>
>> We need a more flexible mechanism to link the gadget and
>> host device to the otg core for non DT case.
>>
>> How about adding struct usb_otg parameter to usb_otg_register_hcd()?
>>
>> e.g.
>> int usb_otg_register_hcd(struct usb_otg *otg, struct usb_hcd *hcd, ..)
>>
>> If otg is NULL it will try DT otg-controller property or parent to
>> get the otg controller.
> 
> How usb_otg_register_hcd get struct usb_otg, from where?

This only works when the parent driver creating the hcd has registered the
otg controller too.

> 
>>
>>>
>>> Peter
>>>
>>> P.S: I still read your code, I find not all APIs in this file are used
>>> in your dwc3 example. 
>>
>> Which ones? The ones for registering/unregistered host/gadget are used
>> by hcd/udc core as part of usb_add/remove_hcd() and
>> udc_bind_to_driver()/usb_gadget_remove_driver()
>>
> 
> Ok, now I understand your design, usb_create_hcd must be called before
> the fsm kicks off. The call flow like below:
> 
> usb_otg_register->usb_create_hcd->usb_add_hcd->usb_otg_register_hcd->
> usb_otg_start_host->usb_otg_add_hcd

Actually these are the discrete steps
- usb_otg_register
- usb_create_hcd
- usb_add_hcd->usb_otg_register_hcd  (HCD is not really added till FSM in host role)

Above 3 are prerequisite for FSM to start in addition to gadget controller and
driver being ready.

When FSM enters in host role
- usb_otg_start_host(true)->usb_otg_add_hcd (Now HCD is added)

When FSM exits host role
- usb_otg_start_host(false)->usb_otg_remove_hcd (Now HCD is removed, but not unregistered)

If HCD hardware really goes away
- usb_remove_hcd->usb_otg_unregister_hcd (Now FSM stops as host resource not available)

> 
> We need to make some changes to let chipidea work since usb_create_hcd
> is included at host->start.
> 

Yes, you just need to do the usb_add_hcd() in probe
and usb_otg_start_host() during role switch.

cheers,
-roger
--
To unsubscribe from this list: send the line "unsubscribe linux-usb" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply	[flat|nested] 48+ messages in thread

* Re: [PATCH v4 07/13] usb: otg: add OTG core
@ 2015-09-09  9:08                   ` Roger Quadros
  0 siblings, 0 replies; 48+ messages in thread
From: Roger Quadros @ 2015-09-09  9:08 UTC (permalink / raw)
  To: Peter Chen
  Cc: stern, balbi, gregkh, dan.j.williams, jun.li, mathias.nyman, tony,
	Joao.Pinto, abrestic, linux-usb, linux-kernel, linux-omap

On 09/09/15 05:21, Peter Chen wrote:
> On Tue, Sep 08, 2015 at 03:25:25PM +0300, Roger Quadros wrote:
>>
>>
>> On 08/09/15 11:31, Peter Chen wrote:
>>> On Mon, Sep 07, 2015 at 01:23:01PM +0300, Roger Quadros wrote:
>>>> On 07/09/15 04:23, Peter Chen wrote:
>>>>> On Mon, Aug 24, 2015 at 04:21:18PM +0300, Roger Quadros wrote:
>>>>>> + * This is used by the USB Host stack to register the Host controller
>>>>>> + * to the OTG core. Host controller must not be started by the
>>>>>> + * caller as it is left upto the OTG state machine to do so.
>>>>>> + *
>>>>>> + * Returns: 0 on success, error value otherwise.
>>>>>> + */
>>>>>> +int usb_otg_register_hcd(struct usb_hcd *hcd, unsigned int irqnum,
>>>>>> +			 unsigned long irqflags, struct otg_hcd_ops *ops)
>>>>>> +{
>>>>>> +	struct usb_otg *otgd;
>>>>>> +	struct device *hcd_dev = hcd->self.controller;
>>>>>> +	struct device *otg_dev = usb_otg_get_device(hcd_dev);
>>>>>> +
>>>>>
>>>>> One big problem here is: there are two designs for current (IP) driver
>>>>> code, one creates dedicated hcd device as roothub's parent, like dwc3.
>>>>> Another one doesn't do this, roothub's parent is core device (or otg device
>>>>> in your patch), like chipidea and dwc2.
>>>>>
>>>>> Then, otg_dev will be glue layer device for chipidea after that.
>>>>
>>>> OK. Let's add a way for the otg controller driver to provide the host and gadget
>>>> information to the otg core for such devices like chipidea and dwc2.
>>>>
>>>
>>> Roger, not only chipidea and dwc2, I think the musb uses the same
>>> hierarchy. If the host, device, and otg share the same register
>>> region, host part can't be a platform driver since we don't want
>>> to remap the same register region again.
>>>
>>> So, in the design, we may need to consider both situations, one
>>> is otg/host/device has its own register region, and host is a
>>> separate platform device (A), the other is three parts share the
>>> same register region, there is only one platform driver (B).
>>>
>>> A:
>>>
>>> 			IP core device 
>>> 			    |
>>> 			    |
>>> 		      |-----|-----|
>>> 		      gadget   host platform device	
>>> 		      		|
>>> 				roothub
>>>
>>> B:
>>>
>>> 			IP core device
>>> 			    |
>>> 			    |
>>> 		      |-----|-----|
>>> 		      gadget   	 roothub
>>> 		      		
>>>
>>>> This API must be called before the hcd/gadget-driver is added so that the otg
>>>> core knows it's linked to an OTG controller.
>>>>
>>>> Any better idea?
>>>>
>>>
>>> A flag stands for this hcd controller is the same with otg controller
>>> can be used, this flag can be stored at struct usb_otg_config.
>>
>> What if there is another architecture like so?
>>
>> C:
>> 			[Parent]
>> 			   |
>> 			   |
>> 		|------------------|--------------|
>> 	[OTG core]		[gadget]	[host]
>>
>> We need a more flexible mechanism to link the gadget and
>> host device to the otg core for non DT case.
>>
>> How about adding struct usb_otg parameter to usb_otg_register_hcd()?
>>
>> e.g.
>> int usb_otg_register_hcd(struct usb_otg *otg, struct usb_hcd *hcd, ..)
>>
>> If otg is NULL it will try DT otg-controller property or parent to
>> get the otg controller.
> 
> How usb_otg_register_hcd get struct usb_otg, from where?

This only works when the parent driver creating the hcd has registered the
otg controller too.

> 
>>
>>>
>>> Peter
>>>
>>> P.S: I still read your code, I find not all APIs in this file are used
>>> in your dwc3 example. 
>>
>> Which ones? The ones for registering/unregistered host/gadget are used
>> by hcd/udc core as part of usb_add/remove_hcd() and
>> udc_bind_to_driver()/usb_gadget_remove_driver()
>>
> 
> Ok, now I understand your design, usb_create_hcd must be called before
> the fsm kicks off. The call flow like below:
> 
> usb_otg_register->usb_create_hcd->usb_add_hcd->usb_otg_register_hcd->
> usb_otg_start_host->usb_otg_add_hcd

Actually these are the discrete steps
- usb_otg_register
- usb_create_hcd
- usb_add_hcd->usb_otg_register_hcd  (HCD is not really added till FSM in host role)

Above 3 are prerequisite for FSM to start in addition to gadget controller and
driver being ready.

When FSM enters in host role
- usb_otg_start_host(true)->usb_otg_add_hcd (Now HCD is added)

When FSM exits host role
- usb_otg_start_host(false)->usb_otg_remove_hcd (Now HCD is removed, but not unregistered)

If HCD hardware really goes away
- usb_remove_hcd->usb_otg_unregister_hcd (Now FSM stops as host resource not available)

> 
> We need to make some changes to let chipidea work since usb_create_hcd
> is included at host->start.
> 

Yes, you just need to do the usb_add_hcd() in probe
and usb_otg_start_host() during role switch.

cheers,
-roger

^ permalink raw reply	[flat|nested] 48+ messages in thread

* Re: [PATCH v4 07/13] usb: otg: add OTG core
  2015-09-09  8:13                       ` Peter Chen
@ 2015-09-09  9:33                         ` Roger Quadros
  -1 siblings, 0 replies; 48+ messages in thread
From: Roger Quadros @ 2015-09-09  9:33 UTC (permalink / raw)
  To: Peter Chen
  Cc: stern-nwvwT67g6+6dFdvTe/nMLpVzexx5G7lz, balbi-l0cyMroinI0,
	gregkh-hQyY1W1yCW8ekmWlsbkhG0B+6BGkLq7r,
	dan.j.williams-ral2JQCrhuEAvxtiuMwx3w,
	jun.li-KZfg59tc24xl57MIdRCFDg,
	mathias.nyman-VuQAYsv1563Yd54FQh9/CA, tony-4v6yS6AI5VpBDgjK7y7TUQ,
	Joao.Pinto-HKixBCOQz3hWk0Htik3J/w,
	abrestic-F7+t8E8rja9g9hUCZPvPmw, linux-usb-u79uwXL29TY76Z2rM5mHXA,
	linux-kernel-u79uwXL29TY76Z2rM5mHXA,
	linux-omap-u79uwXL29TY76Z2rM5mHXA

On 09/09/15 11:13, Peter Chen wrote:
> On Wed, Sep 09, 2015 at 12:08:10PM +0300, Roger Quadros wrote:
>> On 09/09/15 05:21, Peter Chen wrote:
>>> On Tue, Sep 08, 2015 at 03:25:25PM +0300, Roger Quadros wrote:
>>>>
>>>>
>>>> On 08/09/15 11:31, Peter Chen wrote:
>>>>> On Mon, Sep 07, 2015 at 01:23:01PM +0300, Roger Quadros wrote:
>>>>>> On 07/09/15 04:23, Peter Chen wrote:
>>>>>>> On Mon, Aug 24, 2015 at 04:21:18PM +0300, Roger Quadros wrote:
>>>>>>>> + * This is used by the USB Host stack to register the Host controller
>>>>>>>> + * to the OTG core. Host controller must not be started by the
>>>>>>>> + * caller as it is left upto the OTG state machine to do so.
>>>>>>>> + *
>>>>>>>> + * Returns: 0 on success, error value otherwise.
>>>>>>>> + */
>>>>>>>> +int usb_otg_register_hcd(struct usb_hcd *hcd, unsigned int irqnum,
>>>>>>>> +			 unsigned long irqflags, struct otg_hcd_ops *ops)
>>>>>>>> +{
>>>>>>>> +	struct usb_otg *otgd;
>>>>>>>> +	struct device *hcd_dev = hcd->self.controller;
>>>>>>>> +	struct device *otg_dev = usb_otg_get_device(hcd_dev);
>>>>>>>> +
>>>>>>>
>>>>>>> One big problem here is: there are two designs for current (IP) driver
>>>>>>> code, one creates dedicated hcd device as roothub's parent, like dwc3.
>>>>>>> Another one doesn't do this, roothub's parent is core device (or otg device
>>>>>>> in your patch), like chipidea and dwc2.
>>>>>>>
>>>>>>> Then, otg_dev will be glue layer device for chipidea after that.
>>>>>>
>>>>>> OK. Let's add a way for the otg controller driver to provide the host and gadget
>>>>>> information to the otg core for such devices like chipidea and dwc2.
>>>>>>
>>>>>
>>>>> Roger, not only chipidea and dwc2, I think the musb uses the same
>>>>> hierarchy. If the host, device, and otg share the same register
>>>>> region, host part can't be a platform driver since we don't want
>>>>> to remap the same register region again.
>>>>>
>>>>> So, in the design, we may need to consider both situations, one
>>>>> is otg/host/device has its own register region, and host is a
>>>>> separate platform device (A), the other is three parts share the
>>>>> same register region, there is only one platform driver (B).
>>>>>
>>>>> A:
>>>>>
>>>>> 			IP core device 
>>>>> 			    |
>>>>> 			    |
>>>>> 		      |-----|-----|
>>>>> 		      gadget   host platform device	
>>>>> 		      		|
>>>>> 				roothub
>>>>>
>>>>> B:
>>>>>
>>>>> 			IP core device
>>>>> 			    |
>>>>> 			    |
>>>>> 		      |-----|-----|
>>>>> 		      gadget   	 roothub
>>>>> 		      		
>>>>>
>>>>>> This API must be called before the hcd/gadget-driver is added so that the otg
>>>>>> core knows it's linked to an OTG controller.
>>>>>>
>>>>>> Any better idea?
>>>>>>
>>>>>
>>>>> A flag stands for this hcd controller is the same with otg controller
>>>>> can be used, this flag can be stored at struct usb_otg_config.
>>>>
>>>> What if there is another architecture like so?
>>>>
>>>> C:
>>>> 			[Parent]
>>>> 			   |
>>>> 			   |
>>>> 		|------------------|--------------|
>>>> 	[OTG core]		[gadget]	[host]
>>>>
>>>> We need a more flexible mechanism to link the gadget and
>>>> host device to the otg core for non DT case.
>>>>
>>>> How about adding struct usb_otg parameter to usb_otg_register_hcd()?
>>>>
>>>> e.g.
>>>> int usb_otg_register_hcd(struct usb_otg *otg, struct usb_hcd *hcd, ..)
>>>>
>>>> If otg is NULL it will try DT otg-controller property or parent to
>>>> get the otg controller.
>>>
>>> How usb_otg_register_hcd get struct usb_otg, from where?
>>
>> This only works when the parent driver creating the hcd has registered the
>> otg controller too.
>>
> 
> Sorry? So we need to find another way to solve this issue, right?

For existing cases this is sufficient.
The otg device is either the one supplied during usb_otg_register_hcd
(cases B and C) or it is the parent device (case A).

It does not work when the 3 devices are totally independent and get registered
at different times.
I don't think there is such a case for non-DT yet, but let's not have this
limitation. So yes, we need to look for better solution :).

--
cheers,
-roger
--
To unsubscribe from this list: send the line "unsubscribe linux-usb" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply	[flat|nested] 48+ messages in thread

* Re: [PATCH v4 07/13] usb: otg: add OTG core
@ 2015-09-09  9:33                         ` Roger Quadros
  0 siblings, 0 replies; 48+ messages in thread
From: Roger Quadros @ 2015-09-09  9:33 UTC (permalink / raw)
  To: Peter Chen
  Cc: stern, balbi, gregkh, dan.j.williams, jun.li, mathias.nyman, tony,
	Joao.Pinto, abrestic, linux-usb, linux-kernel, linux-omap

On 09/09/15 11:13, Peter Chen wrote:
> On Wed, Sep 09, 2015 at 12:08:10PM +0300, Roger Quadros wrote:
>> On 09/09/15 05:21, Peter Chen wrote:
>>> On Tue, Sep 08, 2015 at 03:25:25PM +0300, Roger Quadros wrote:
>>>>
>>>>
>>>> On 08/09/15 11:31, Peter Chen wrote:
>>>>> On Mon, Sep 07, 2015 at 01:23:01PM +0300, Roger Quadros wrote:
>>>>>> On 07/09/15 04:23, Peter Chen wrote:
>>>>>>> On Mon, Aug 24, 2015 at 04:21:18PM +0300, Roger Quadros wrote:
>>>>>>>> + * This is used by the USB Host stack to register the Host controller
>>>>>>>> + * to the OTG core. Host controller must not be started by the
>>>>>>>> + * caller as it is left upto the OTG state machine to do so.
>>>>>>>> + *
>>>>>>>> + * Returns: 0 on success, error value otherwise.
>>>>>>>> + */
>>>>>>>> +int usb_otg_register_hcd(struct usb_hcd *hcd, unsigned int irqnum,
>>>>>>>> +			 unsigned long irqflags, struct otg_hcd_ops *ops)
>>>>>>>> +{
>>>>>>>> +	struct usb_otg *otgd;
>>>>>>>> +	struct device *hcd_dev = hcd->self.controller;
>>>>>>>> +	struct device *otg_dev = usb_otg_get_device(hcd_dev);
>>>>>>>> +
>>>>>>>
>>>>>>> One big problem here is: there are two designs for current (IP) driver
>>>>>>> code, one creates dedicated hcd device as roothub's parent, like dwc3.
>>>>>>> Another one doesn't do this, roothub's parent is core device (or otg device
>>>>>>> in your patch), like chipidea and dwc2.
>>>>>>>
>>>>>>> Then, otg_dev will be glue layer device for chipidea after that.
>>>>>>
>>>>>> OK. Let's add a way for the otg controller driver to provide the host and gadget
>>>>>> information to the otg core for such devices like chipidea and dwc2.
>>>>>>
>>>>>
>>>>> Roger, not only chipidea and dwc2, I think the musb uses the same
>>>>> hierarchy. If the host, device, and otg share the same register
>>>>> region, host part can't be a platform driver since we don't want
>>>>> to remap the same register region again.
>>>>>
>>>>> So, in the design, we may need to consider both situations, one
>>>>> is otg/host/device has its own register region, and host is a
>>>>> separate platform device (A), the other is three parts share the
>>>>> same register region, there is only one platform driver (B).
>>>>>
>>>>> A:
>>>>>
>>>>> 			IP core device 
>>>>> 			    |
>>>>> 			    |
>>>>> 		      |-----|-----|
>>>>> 		      gadget   host platform device	
>>>>> 		      		|
>>>>> 				roothub
>>>>>
>>>>> B:
>>>>>
>>>>> 			IP core device
>>>>> 			    |
>>>>> 			    |
>>>>> 		      |-----|-----|
>>>>> 		      gadget   	 roothub
>>>>> 		      		
>>>>>
>>>>>> This API must be called before the hcd/gadget-driver is added so that the otg
>>>>>> core knows it's linked to an OTG controller.
>>>>>>
>>>>>> Any better idea?
>>>>>>
>>>>>
>>>>> A flag stands for this hcd controller is the same with otg controller
>>>>> can be used, this flag can be stored at struct usb_otg_config.
>>>>
>>>> What if there is another architecture like so?
>>>>
>>>> C:
>>>> 			[Parent]
>>>> 			   |
>>>> 			   |
>>>> 		|------------------|--------------|
>>>> 	[OTG core]		[gadget]	[host]
>>>>
>>>> We need a more flexible mechanism to link the gadget and
>>>> host device to the otg core for non DT case.
>>>>
>>>> How about adding struct usb_otg parameter to usb_otg_register_hcd()?
>>>>
>>>> e.g.
>>>> int usb_otg_register_hcd(struct usb_otg *otg, struct usb_hcd *hcd, ..)
>>>>
>>>> If otg is NULL it will try DT otg-controller property or parent to
>>>> get the otg controller.
>>>
>>> How usb_otg_register_hcd get struct usb_otg, from where?
>>
>> This only works when the parent driver creating the hcd has registered the
>> otg controller too.
>>
> 
> Sorry? So we need to find another way to solve this issue, right?

For existing cases this is sufficient.
The otg device is either the one supplied during usb_otg_register_hcd
(cases B and C) or it is the parent device (case A).

It does not work when the 3 devices are totally independent and get registered
at different times.
I don't think there is such a case for non-DT yet, but let's not have this
limitation. So yes, we need to look for better solution :).

--
cheers,
-roger

^ permalink raw reply	[flat|nested] 48+ messages in thread

* Re: [PATCH v4 07/13] usb: otg: add OTG core
  2015-09-09  6:20             ` Li Jun
@ 2015-09-09 10:01               ` Roger Quadros
  -1 siblings, 0 replies; 48+ messages in thread
From: Roger Quadros @ 2015-09-09 10:01 UTC (permalink / raw)
  To: Li Jun
  Cc: stern-nwvwT67g6+6dFdvTe/nMLpVzexx5G7lz, balbi-l0cyMroinI0,
	gregkh-hQyY1W1yCW8ekmWlsbkhG0B+6BGkLq7r,
	peter.chen-KZfg59tc24xl57MIdRCFDg,
	dan.j.williams-ral2JQCrhuEAvxtiuMwx3w,
	jun.li-KZfg59tc24xl57MIdRCFDg,
	mathias.nyman-VuQAYsv1563Yd54FQh9/CA, tony-4v6yS6AI5VpBDgjK7y7TUQ,
	Joao.Pinto-HKixBCOQz3hWk0Htik3J/w,
	abrestic-F7+t8E8rja9g9hUCZPvPmw, linux-usb-u79uwXL29TY76Z2rM5mHXA,
	linux-kernel-u79uwXL29TY76Z2rM5mHXA,
	linux-omap-u79uwXL29TY76Z2rM5mHXA

On 09/09/15 09:20, Li Jun wrote:
> On Mon, Sep 07, 2015 at 01:53:19PM +0300, Roger Quadros wrote:
>> On 07/09/15 10:40, Li Jun wrote:
>>> On Mon, Aug 24, 2015 at 04:21:18PM +0300, Roger Quadros wrote:
>>>> The OTG core instantiates the OTG Finite State Machine
>>>> per OTG controller and manages starting/stopping the
>>>> host and gadget controllers based on the bus state.
>>>>
>>>> It provides APIs for the following tasks
>>>>
>>>> - Registering an OTG capable controller
>>>> - Registering Host and Gadget controllers to OTG core
>>>> - Providing inputs to and kicking the OTG state machine
>>>>
>>>> Signed-off-by: Roger Quadros <rogerq-l0cyMroinI0@public.gmane.org>
>>>> ---
>>>>  MAINTAINERS                  |    4 +-
>>>>  drivers/usb/Kconfig          |    2 +-
>>>>  drivers/usb/Makefile         |    1 +
>>>>  drivers/usb/common/Makefile  |    3 +-
>>>>  drivers/usb/common/usb-otg.c | 1061 ++++++++++++++++++++++++++++++++++++++++++
>>>>  drivers/usb/common/usb-otg.h |   71 +++
>>>>  drivers/usb/core/Kconfig     |   11 +-
>>>>  include/linux/usb/otg.h      |  189 +++++++-
>>>>  8 files changed, 1321 insertions(+), 21 deletions(-)
>>>>  create mode 100644 drivers/usb/common/usb-otg.c
>>>>  create mode 100644 drivers/usb/common/usb-otg.h
>>>>
>>>
>>> ... ...
>>>
>>>> +
>>>> +/**
>>>> + * Get OTG device from host or gadget device.
>>>> + *
>>>> + * For non device tree boot, the OTG controller is assumed to be
>>>> + * the parent of the host/gadget device.
>>>
>>> This assumption/restriction maybe a problem, as I pointed in your previous
>>> version, usb_create_hcd() use the passed dev as its dev, but,
>>> usb_add_gadget_udc() use the passed dev as its parent dev, so often the
>>> host and gadget don't share the same parent device, at least it doesn't
>>> apply to chipidea case.
>>
>> Let's provide a way for OTG driver to provide the OTG core exactly which is
>> the related host/gadget device.
>>
>>>
>>>> + * For device tree boot, the OTG controller is derived from the
>>>> + * "otg-controller" property.
>>>> + */
>>>> +static struct device *usb_otg_get_device(struct device *hcd_gcd_dev)
>>>> +{
>>>> +	struct device *otg_dev;
>>>> +
>>>> +	if (!hcd_gcd_dev)
>>>> +		return NULL;
>>>> +
>>>> +	if (hcd_gcd_dev->of_node) {
>>>> +		struct device_node *np;
>>>> +		struct platform_device *pdev;
>>>> +
>>>> +		np = of_parse_phandle(hcd_gcd_dev->of_node, "otg-controller",
>>>> +				      0);
>>>> +		if (!np)
>>>> +			goto legacy;	/* continue legacy way */
>>>> +
>>>> +		pdev = of_find_device_by_node(np);
>>>> +		of_node_put(np);
>>>> +		if (!pdev) {
>>>> +			dev_err(&pdev->dev, "couldn't get otg-controller device\n");
>>>> +			return NULL;
>>>> +		}
>>>> +
>>>> +		otg_dev = &pdev->dev;
>>>> +		return otg_dev;
>>>> +	}
>>>> +
>>>> +legacy:
>>>> +	/* otg device is parent and must be registered */
>>>> +	otg_dev = hcd_gcd_dev->parent;
>>>> +	if (!usb_otg_get_data(otg_dev))
>>>> +		return NULL;
>>>> +
>>>> +	return otg_dev;
>>>> +}
>>>> +
>>>
>>> ... ...
>>>
>>>> +static void usb_otg_init_timers(struct usb_otg *otgd, unsigned *timeouts)
>>>> +{
>>>> +	struct otg_fsm *fsm = &otgd->fsm;
>>>> +	unsigned long tmouts[NUM_OTG_FSM_TIMERS];
>>>> +	int i;
>>>> +
>>>> +	/* set default timeouts */
>>>> +	tmouts[A_WAIT_VRISE] = TA_WAIT_VRISE;
>>>> +	tmouts[A_WAIT_VFALL] = TA_WAIT_VFALL;
>>>> +	tmouts[A_WAIT_BCON] = TA_WAIT_BCON;
>>>> +	tmouts[A_AIDL_BDIS] = TA_AIDL_BDIS;
>>>> +	tmouts[A_BIDL_ADIS] = TA_BIDL_ADIS;
>>>> +	tmouts[B_ASE0_BRST] = TB_ASE0_BRST;
>>>> +	tmouts[B_SE0_SRP] = TB_SE0_SRP;
>>>> +	tmouts[B_SRP_FAIL] = TB_SRP_FAIL;
>>>> +
>>>> +	/* set controller provided timeouts */
>>>> +	if (timeouts) {
>>>> +		for (i = 0; i < NUM_OTG_FSM_TIMERS; i++) {
>>>> +			if (timeouts[i])
>>>> +				tmouts[i] = timeouts[i];
>>>> +		}
>>>> +	}
>>>> +
>>>> +	otg_timer_init(A_WAIT_VRISE, otgd, set_tmout, TA_WAIT_VRISE,
>>>> +		       &fsm->a_wait_vrise_tmout);
>>>> +	otg_timer_init(A_WAIT_VFALL, otgd, set_tmout, TA_WAIT_VFALL,
>>>> +		       &fsm->a_wait_vfall_tmout);
>>>> +	otg_timer_init(A_WAIT_BCON, otgd, set_tmout, TA_WAIT_BCON,
>>>> +		       &fsm->a_wait_bcon_tmout);
>>>> +	otg_timer_init(A_AIDL_BDIS, otgd, set_tmout, TA_AIDL_BDIS,
>>>> +		       &fsm->a_aidl_bdis_tmout);
>>>> +	otg_timer_init(A_BIDL_ADIS, otgd, set_tmout, TA_BIDL_ADIS,
>>>> +		       &fsm->a_bidl_adis_tmout);
>>>> +	otg_timer_init(B_ASE0_BRST, otgd, set_tmout, TB_ASE0_BRST,
>>>> +		       &fsm->b_ase0_brst_tmout);
>>>> +
>>>> +	otg_timer_init(B_SE0_SRP, otgd, set_tmout, TB_SE0_SRP,
>>>> +		       &fsm->b_se0_srp);
>>>> +	otg_timer_init(B_SRP_FAIL, otgd, set_tmout, TB_SRP_FAIL,
>>>> +		       &fsm->b_srp_done);
>>>> +
>>>> +	/* FIXME: what about A_WAIT_ENUM? */
>>>
>>> Either you init it as other timers, or you remove all of it, otherwise
>>> there will be NULL pointer crash.
>>
>> I want to initialize it but was not sure about the timeout value.
>> What timeout value I must use?
>>
> 
> It's not defined in OTG spec, I don't know either.
> or you filter it out when add/del timer in below 2 functions.
> 	if (id == A_WAIT_ENUM)
> 		return;
> 

Looks like it is used for some workaround. See phy-fsl-usb.c line 269

/*
 * Workaround for a_host suspending too fast.  When a_bus_req=0,
 * a_host will start by SRP.  It needs to set b_hnp_enable before
 * actually suspending to start HNP
 */
void a_wait_enum(unsigned long foo)
{
        VDBG("a_wait_enum timeout\n");
        if (!fsl_otg_dev->phy.otg->host->b_hnp_enable)
                fsl_otg_add_timer(&fsl_otg_dev->fsm, a_wait_enum_tmr);
        else
                otg_statemachine(&fsl_otg_dev->fsm);
}

I'll filter it out for now as per your suggestion. We can look back
into it if we face issues as mentioned in the workaround.

>>>
>>>> +}
>>>> +
>>>> +/**
>>>> + * OTG FSM ops function to add timer
>>>> + */
>>>> +static void usb_otg_add_timer(struct otg_fsm *fsm, enum otg_fsm_timer id)
>>>> +{
>>>> +	struct usb_otg *otgd = container_of(fsm, struct usb_otg, fsm);
>>>> +	struct otg_timer *otgtimer = &otgd->timers[id];
>>>> +	struct hrtimer *timer = &otgtimer->timer;
>>>> +
>>>> +	if (!otgd->fsm_running)
>>>> +		return;
>>>> +
>>>> +	/* if timer is already active, exit */
>>>> +	if (hrtimer_active(timer)) {
>>>> +		dev_err(otgd->dev, "otg: timer %d is already running\n", id);
>>>> +		return;
>>>> +	}
>>>> +
>>>> +	hrtimer_start(timer, otgtimer->timeout, HRTIMER_MODE_REL);
>>>> +}
>>>> +
>>>> +/**
>>>> + * OTG FSM ops function to delete timer
>>>> + */
>>>> +static void usb_otg_del_timer(struct otg_fsm *fsm, enum otg_fsm_timer id)
>>>> +{
>>>> +	struct usb_otg *otgd = container_of(fsm, struct usb_otg, fsm);
>>>> +	struct hrtimer *timer = &otgd->timers[id].timer;
>>>> +
>>>> +	hrtimer_cancel(timer);
>>>> +}
>>>> +
> 
> ... ...
> 
>>>> +	}
>>>> +
>>>> +	otgd->dev = dev;
>>>> +	otgd->caps = &config->otg_caps;
>>>
>>> How about define otgd->caps as a pointer, then don't need copy it.
>>
>> otgd->caps is a pointer.
>>
> 
> okay, you are right.
> 
>>>
>>>> +	INIT_WORK(&otgd->work, usb_otg_work);
>>>> +	otgd->wq = create_singlethread_workqueue("usb_otg");
>>>> +	if (!otgd->wq) {
>>>> +		dev_err(dev, "otg: %s: can't create workqueue\n",
>>>> +			__func__);
>>>> +		ret = -ENOMEM;
>>>> +		goto err_wq;
>>>> +	}
>>>> +
>>>> +	usb_otg_init_timers(otgd, config->otg_timeouts);
>>>> +
>>>> +	/* create copy of original ops */
>>>> +	otgd->fsm_ops = *config->fsm_ops;
>>>
>>> The same, use a pointer is enough?
>>
>> We are creating a copy because we are overriding timer ops.
>>
> 
> okay.
> 
>>>
>>>> +	/* FIXME: we ignore caller's timer ops */
>>>> +	otgd->fsm_ops.add_timer = usb_otg_add_timer;
>>>> +	otgd->fsm_ops.del_timer = usb_otg_del_timer;
>>>> +	/* set otg ops */
>>>> +	otgd->fsm.ops = &otgd->fsm_ops;
>>>> +	otgd->fsm.otg = otgd;
>>>> + *
> 
> ... ...
> 
>>>> + * This is used by the USB Host stack to register the Host controller
>>>> + * to the OTG core. Host controller must not be started by the
>>>> + * caller as it is left upto the OTG state machine to do so.
>>>
>>> I am confused on how to use this function.
>>> - This function should be called before start fsm per usb_otg_start_fsm().
>>
>> yes.
>>
>>> - Called by usb_add_hcd(), so we need call usb_add_hcd() before start fsm.
>>
>> yes.
>>
>>> - If I want to add hcd when switch to host role, and remove hcd when switch
>>>   to peripheral, with this design, I cannot use this function?
>>
>> You add hcd only once during the life of the OTG device. If it is linked to the
>> OTG controller the OTG fsm manages the start/stop of hcd using the otg_hcd_ops.
>>
>> "usb/core/hcd.c"
>> static struct otg_hcd_ops otg_hcd_intf = {
>>         .add = usb_otg_add_hcd,
>>         .remove = usb_otg_remove_hcd,
>> };
>>
>> Your otg driver must use teh usb_otg_add/remove_hcd to start/stop the controller.
>> Using usb_remove_hcd() means the hcd resource is no longer available and the
>> otg fsm will be stopped.
>>
>>> - How about split it out of usb_add_hcd()?
>>
>> Adding the HCD and starting/stopping the hcd is split into
>> usb_add/remove_hcd() and usb_otg_add/remove_hcd() for OTG case.
>>
> 
> Catch your point now.
> Do usb_add_hcd to register hcd before start fsm, and use usb_otg_start_host()
> to start/stop host role for otg fsm.
> 
correct :).

>>>
>>>> + *
>>>> + * Returns: 0 on success, error value otherwise.
>>>> + */
>>>> +int usb_otg_register_hcd(struct usb_hcd *hcd, unsigned int irqnum,
>>>> +			 unsigned long irqflags, struct otg_hcd_ops *ops)
>>>> +{
>>>> +	struct usb_otg *otgd;
>>>> +	struct device *hcd_dev = hcd->self.controller;
>>>> +	struct device *otg_dev = usb_otg_get_device(hcd_dev);
>>>> +
>>>> +	if (!otg_dev)
>>>> +		return -EINVAL;	/* we're definitely not OTG */
>>>> +
>>>> +	/* we're otg but otg controller might not yet be registered */
>>>> +	mutex_lock(&otg_list_mutex);
>>>> +	otgd = usb_otg_get_data(otg_dev);
>>>> +	mutex_unlock(&otg_list_mutex);
>>>> +	if (!otgd) {
>>>> +		dev_dbg(hcd_dev,
>>>> +			"otg: controller not yet registered. waiting..\n");
>>>> +		/*
>>>> +		 * otg controller might register later. Put the hcd in
>>>> +		 * wait list and call us back when ready
>>>> +		 */
>>>> +		if (usb_otg_hcd_wait_add(otg_dev, hcd, irqnum, irqflags, ops)) {
>>>> +			dev_dbg(hcd_dev, "otg: failed to add to wait list\n");
>>>> +			return -EINVAL;
>>>> +		}
>>>> +
>>>> +		return 0;
>>>> +	}
>>>> +
>>>> +	/* HCD will be started by OTG fsm when needed */
>>>> +	mutex_lock(&otgd->fsm.lock);
>>>
>>> If call usb_add_hcd() when start host role, deadlock.
>>
>> No. You must call usb_otg_add_hcd() to start host role.
>>
>>>
>>>> +	if (otgd->primary_hcd.hcd) {
>>>> +		/* probably a shared HCD ? */
>>>> +		if (usb_otg_hcd_is_primary_hcd(hcd)) {
>>>> +			dev_err(otg_dev, "otg: primary host already registered\n");
>>>> +			goto err;
>>>> +		}
>>>> +
>>>> +		if (hcd->shared_hcd == otgd->primary_hcd.hcd) {
>>>> +			if (otgd->shared_hcd.hcd) {
>>>> +				dev_err(otg_dev, "otg: shared host already registered\n");
>>>> +				goto err;
>>>> +			}
>>>> +
>>>> +			otgd->shared_hcd.hcd = hcd;
>>>> +			otgd->shared_hcd.irqnum = irqnum;
>>>> +			otgd->shared_hcd.irqflags = irqflags;
>>>> +			otgd->shared_hcd.ops = ops;
>>>> +			dev_info(otg_dev, "otg: shared host %s registered\n",
>>>> +				 dev_name(hcd->self.controller));
>>>> +		} else {
>>>> +			dev_err(otg_dev, "otg: invalid shared host %s\n",
>>>> +				dev_name(hcd->self.controller));
>>>> +			goto err;
>>>> +		}
>>>> +	} else {
>>>> +		if (!usb_otg_hcd_is_primary_hcd(hcd)) {
>>>> +			dev_err(otg_dev, "otg: primary host must be registered first\n");
>>>> +			goto err;
>>>> +		}
>>>> +
>>>> +		otgd->primary_hcd.hcd = hcd;
>>>> +		otgd->primary_hcd.irqnum = irqnum;
>>>> +		otgd->primary_hcd.irqflags = irqflags;
>>>> +		otgd->primary_hcd.ops = ops;
>>>> +		dev_info(otg_dev, "otg: primary host %s registered\n",
>>>> +			 dev_name(hcd->self.controller));
>>>> +	}
>>>> +
>>>> +	/*
>>>> +	 * we're ready only if we have shared HCD
>>>> +	 * or we don't need shared HCD.
>>>> +	 */
>>>> +	if (otgd->shared_hcd.hcd || !otgd->primary_hcd.hcd->shared_hcd) {
>>>> +		otgd->fsm.otg->host = hcd_to_bus(hcd);
>>>
>>> otgd->host = hcd_to_bus(hcd);
>>
>> ok. So we set host at both places. struct usb_otg in struct otg_fsm starts to
>> feel redundant now. I think we should get rid of it and get the usb_otg struct
>> using container_of() instead.
>>
> 
> Then you may come to force existing otg fsm code to use your OTG core.

Let's leave it around then.

> 
>>>
>>>> +		/* FIXME: set bus->otg_port if this is true OTG port with HNP */
>>>> +
>>>> +		/* start FSM */
>>>> +		usb_otg_start_fsm(&otgd->fsm);
>>>> +	} else {
>>>> +		dev_dbg(otg_dev, "otg: can't start till shared host registers\n");
>>>> +	}
>>>> +
>>>> +	mutex_unlock(&otgd->fsm.lock);
>>>> +
>>>> +	return 0;
>>>> +
>>>> +err:
>>>> +	mutex_unlock(&otgd->fsm.lock);
>>>> +	return -EINVAL;
>>>
>>> Return non-zero, then if err, do we need call usb_otg_add_hcd() after
>>> usb_otg_register_hcd() fails?
>>
>> You should not call usb_otg_register_hcd() but usb_add_hcd().
>> If that fails then you fail as ususal.
> 
> My point is if we use usb_add_hcd(), but failed in usb_otg_register_hcd(),
> then usb_otg_add_hcd() will be called in *all* error case, is this your
> expectation?
> 	if (usb_otg_register_hcd(hcd, irqnum, irqflags, &otg_hcd_intf))
> 		return usb_otg_add_hcd(hcd, irqnum, irqflags);
> 

Yes, my intention was that if otg fails then it is a non otg HCD so register normally.
Let me correct my previous statement. If you are absolutely sure
that the HCD is for otg/dual-role usage then you should call usb_otg_register_hcd().

>>>> + * @fsm_running: state machine running/stopped indicator
>>>> + */
>>>>  struct usb_otg {
>>>>  	u8			default_a;
>>>>  
>>>>  	struct phy		*phy;
>>>>  	/* old usb_phy interface */
>>>>  	struct usb_phy		*usb_phy;
>>>> +
>>>
>>> add a blank line?
>>>
> 
> You missed this.

Sorry. Did you suggest to remove that blank line
or add a new one before usb_phy?

> 
>>>>  	struct usb_bus		*host;
>>>>  	struct usb_gadget	*gadget;
>>>>  
>>>>  	enum usb_otg_state	state;
>>>>  
>>>> +	struct device *dev;
>>>> +	struct usb_otg_caps *caps;
>>>> +	struct otg_fsm fsm;
>>>> +	struct otg_fsm_ops fsm_ops;
>>>> +
>>>> +	/* internal use only */
>>>> +	struct otg_hcd primary_hcd;
>>>> +	struct otg_hcd shared_hcd;
>>>> +	struct otg_gadget_ops *gadget_ops;
>>>> +	struct otg_timer timers[NUM_OTG_FSM_TIMERS];
>>>> +	struct list_head list;
>>>> +	struct work_struct work;
>>>> -- 
>>>> 2.1.4
> 

--
cheers,
-roger
--
To unsubscribe from this list: send the line "unsubscribe linux-usb" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply	[flat|nested] 48+ messages in thread

* Re: [PATCH v4 07/13] usb: otg: add OTG core
@ 2015-09-09 10:01               ` Roger Quadros
  0 siblings, 0 replies; 48+ messages in thread
From: Roger Quadros @ 2015-09-09 10:01 UTC (permalink / raw)
  To: Li Jun
  Cc: stern, balbi, gregkh, peter.chen, dan.j.williams, jun.li,
	mathias.nyman, tony, Joao.Pinto, abrestic, linux-usb,
	linux-kernel, linux-omap

On 09/09/15 09:20, Li Jun wrote:
> On Mon, Sep 07, 2015 at 01:53:19PM +0300, Roger Quadros wrote:
>> On 07/09/15 10:40, Li Jun wrote:
>>> On Mon, Aug 24, 2015 at 04:21:18PM +0300, Roger Quadros wrote:
>>>> The OTG core instantiates the OTG Finite State Machine
>>>> per OTG controller and manages starting/stopping the
>>>> host and gadget controllers based on the bus state.
>>>>
>>>> It provides APIs for the following tasks
>>>>
>>>> - Registering an OTG capable controller
>>>> - Registering Host and Gadget controllers to OTG core
>>>> - Providing inputs to and kicking the OTG state machine
>>>>
>>>> Signed-off-by: Roger Quadros <rogerq@ti.com>
>>>> ---
>>>>  MAINTAINERS                  |    4 +-
>>>>  drivers/usb/Kconfig          |    2 +-
>>>>  drivers/usb/Makefile         |    1 +
>>>>  drivers/usb/common/Makefile  |    3 +-
>>>>  drivers/usb/common/usb-otg.c | 1061 ++++++++++++++++++++++++++++++++++++++++++
>>>>  drivers/usb/common/usb-otg.h |   71 +++
>>>>  drivers/usb/core/Kconfig     |   11 +-
>>>>  include/linux/usb/otg.h      |  189 +++++++-
>>>>  8 files changed, 1321 insertions(+), 21 deletions(-)
>>>>  create mode 100644 drivers/usb/common/usb-otg.c
>>>>  create mode 100644 drivers/usb/common/usb-otg.h
>>>>
>>>
>>> ... ...
>>>
>>>> +
>>>> +/**
>>>> + * Get OTG device from host or gadget device.
>>>> + *
>>>> + * For non device tree boot, the OTG controller is assumed to be
>>>> + * the parent of the host/gadget device.
>>>
>>> This assumption/restriction maybe a problem, as I pointed in your previous
>>> version, usb_create_hcd() use the passed dev as its dev, but,
>>> usb_add_gadget_udc() use the passed dev as its parent dev, so often the
>>> host and gadget don't share the same parent device, at least it doesn't
>>> apply to chipidea case.
>>
>> Let's provide a way for OTG driver to provide the OTG core exactly which is
>> the related host/gadget device.
>>
>>>
>>>> + * For device tree boot, the OTG controller is derived from the
>>>> + * "otg-controller" property.
>>>> + */
>>>> +static struct device *usb_otg_get_device(struct device *hcd_gcd_dev)
>>>> +{
>>>> +	struct device *otg_dev;
>>>> +
>>>> +	if (!hcd_gcd_dev)
>>>> +		return NULL;
>>>> +
>>>> +	if (hcd_gcd_dev->of_node) {
>>>> +		struct device_node *np;
>>>> +		struct platform_device *pdev;
>>>> +
>>>> +		np = of_parse_phandle(hcd_gcd_dev->of_node, "otg-controller",
>>>> +				      0);
>>>> +		if (!np)
>>>> +			goto legacy;	/* continue legacy way */
>>>> +
>>>> +		pdev = of_find_device_by_node(np);
>>>> +		of_node_put(np);
>>>> +		if (!pdev) {
>>>> +			dev_err(&pdev->dev, "couldn't get otg-controller device\n");
>>>> +			return NULL;
>>>> +		}
>>>> +
>>>> +		otg_dev = &pdev->dev;
>>>> +		return otg_dev;
>>>> +	}
>>>> +
>>>> +legacy:
>>>> +	/* otg device is parent and must be registered */
>>>> +	otg_dev = hcd_gcd_dev->parent;
>>>> +	if (!usb_otg_get_data(otg_dev))
>>>> +		return NULL;
>>>> +
>>>> +	return otg_dev;
>>>> +}
>>>> +
>>>
>>> ... ...
>>>
>>>> +static void usb_otg_init_timers(struct usb_otg *otgd, unsigned *timeouts)
>>>> +{
>>>> +	struct otg_fsm *fsm = &otgd->fsm;
>>>> +	unsigned long tmouts[NUM_OTG_FSM_TIMERS];
>>>> +	int i;
>>>> +
>>>> +	/* set default timeouts */
>>>> +	tmouts[A_WAIT_VRISE] = TA_WAIT_VRISE;
>>>> +	tmouts[A_WAIT_VFALL] = TA_WAIT_VFALL;
>>>> +	tmouts[A_WAIT_BCON] = TA_WAIT_BCON;
>>>> +	tmouts[A_AIDL_BDIS] = TA_AIDL_BDIS;
>>>> +	tmouts[A_BIDL_ADIS] = TA_BIDL_ADIS;
>>>> +	tmouts[B_ASE0_BRST] = TB_ASE0_BRST;
>>>> +	tmouts[B_SE0_SRP] = TB_SE0_SRP;
>>>> +	tmouts[B_SRP_FAIL] = TB_SRP_FAIL;
>>>> +
>>>> +	/* set controller provided timeouts */
>>>> +	if (timeouts) {
>>>> +		for (i = 0; i < NUM_OTG_FSM_TIMERS; i++) {
>>>> +			if (timeouts[i])
>>>> +				tmouts[i] = timeouts[i];
>>>> +		}
>>>> +	}
>>>> +
>>>> +	otg_timer_init(A_WAIT_VRISE, otgd, set_tmout, TA_WAIT_VRISE,
>>>> +		       &fsm->a_wait_vrise_tmout);
>>>> +	otg_timer_init(A_WAIT_VFALL, otgd, set_tmout, TA_WAIT_VFALL,
>>>> +		       &fsm->a_wait_vfall_tmout);
>>>> +	otg_timer_init(A_WAIT_BCON, otgd, set_tmout, TA_WAIT_BCON,
>>>> +		       &fsm->a_wait_bcon_tmout);
>>>> +	otg_timer_init(A_AIDL_BDIS, otgd, set_tmout, TA_AIDL_BDIS,
>>>> +		       &fsm->a_aidl_bdis_tmout);
>>>> +	otg_timer_init(A_BIDL_ADIS, otgd, set_tmout, TA_BIDL_ADIS,
>>>> +		       &fsm->a_bidl_adis_tmout);
>>>> +	otg_timer_init(B_ASE0_BRST, otgd, set_tmout, TB_ASE0_BRST,
>>>> +		       &fsm->b_ase0_brst_tmout);
>>>> +
>>>> +	otg_timer_init(B_SE0_SRP, otgd, set_tmout, TB_SE0_SRP,
>>>> +		       &fsm->b_se0_srp);
>>>> +	otg_timer_init(B_SRP_FAIL, otgd, set_tmout, TB_SRP_FAIL,
>>>> +		       &fsm->b_srp_done);
>>>> +
>>>> +	/* FIXME: what about A_WAIT_ENUM? */
>>>
>>> Either you init it as other timers, or you remove all of it, otherwise
>>> there will be NULL pointer crash.
>>
>> I want to initialize it but was not sure about the timeout value.
>> What timeout value I must use?
>>
> 
> It's not defined in OTG spec, I don't know either.
> or you filter it out when add/del timer in below 2 functions.
> 	if (id == A_WAIT_ENUM)
> 		return;
> 

Looks like it is used for some workaround. See phy-fsl-usb.c line 269

/*
 * Workaround for a_host suspending too fast.  When a_bus_req=0,
 * a_host will start by SRP.  It needs to set b_hnp_enable before
 * actually suspending to start HNP
 */
void a_wait_enum(unsigned long foo)
{
        VDBG("a_wait_enum timeout\n");
        if (!fsl_otg_dev->phy.otg->host->b_hnp_enable)
                fsl_otg_add_timer(&fsl_otg_dev->fsm, a_wait_enum_tmr);
        else
                otg_statemachine(&fsl_otg_dev->fsm);
}

I'll filter it out for now as per your suggestion. We can look back
into it if we face issues as mentioned in the workaround.

>>>
>>>> +}
>>>> +
>>>> +/**
>>>> + * OTG FSM ops function to add timer
>>>> + */
>>>> +static void usb_otg_add_timer(struct otg_fsm *fsm, enum otg_fsm_timer id)
>>>> +{
>>>> +	struct usb_otg *otgd = container_of(fsm, struct usb_otg, fsm);
>>>> +	struct otg_timer *otgtimer = &otgd->timers[id];
>>>> +	struct hrtimer *timer = &otgtimer->timer;
>>>> +
>>>> +	if (!otgd->fsm_running)
>>>> +		return;
>>>> +
>>>> +	/* if timer is already active, exit */
>>>> +	if (hrtimer_active(timer)) {
>>>> +		dev_err(otgd->dev, "otg: timer %d is already running\n", id);
>>>> +		return;
>>>> +	}
>>>> +
>>>> +	hrtimer_start(timer, otgtimer->timeout, HRTIMER_MODE_REL);
>>>> +}
>>>> +
>>>> +/**
>>>> + * OTG FSM ops function to delete timer
>>>> + */
>>>> +static void usb_otg_del_timer(struct otg_fsm *fsm, enum otg_fsm_timer id)
>>>> +{
>>>> +	struct usb_otg *otgd = container_of(fsm, struct usb_otg, fsm);
>>>> +	struct hrtimer *timer = &otgd->timers[id].timer;
>>>> +
>>>> +	hrtimer_cancel(timer);
>>>> +}
>>>> +
> 
> ... ...
> 
>>>> +	}
>>>> +
>>>> +	otgd->dev = dev;
>>>> +	otgd->caps = &config->otg_caps;
>>>
>>> How about define otgd->caps as a pointer, then don't need copy it.
>>
>> otgd->caps is a pointer.
>>
> 
> okay, you are right.
> 
>>>
>>>> +	INIT_WORK(&otgd->work, usb_otg_work);
>>>> +	otgd->wq = create_singlethread_workqueue("usb_otg");
>>>> +	if (!otgd->wq) {
>>>> +		dev_err(dev, "otg: %s: can't create workqueue\n",
>>>> +			__func__);
>>>> +		ret = -ENOMEM;
>>>> +		goto err_wq;
>>>> +	}
>>>> +
>>>> +	usb_otg_init_timers(otgd, config->otg_timeouts);
>>>> +
>>>> +	/* create copy of original ops */
>>>> +	otgd->fsm_ops = *config->fsm_ops;
>>>
>>> The same, use a pointer is enough?
>>
>> We are creating a copy because we are overriding timer ops.
>>
> 
> okay.
> 
>>>
>>>> +	/* FIXME: we ignore caller's timer ops */
>>>> +	otgd->fsm_ops.add_timer = usb_otg_add_timer;
>>>> +	otgd->fsm_ops.del_timer = usb_otg_del_timer;
>>>> +	/* set otg ops */
>>>> +	otgd->fsm.ops = &otgd->fsm_ops;
>>>> +	otgd->fsm.otg = otgd;
>>>> + *
> 
> ... ...
> 
>>>> + * This is used by the USB Host stack to register the Host controller
>>>> + * to the OTG core. Host controller must not be started by the
>>>> + * caller as it is left upto the OTG state machine to do so.
>>>
>>> I am confused on how to use this function.
>>> - This function should be called before start fsm per usb_otg_start_fsm().
>>
>> yes.
>>
>>> - Called by usb_add_hcd(), so we need call usb_add_hcd() before start fsm.
>>
>> yes.
>>
>>> - If I want to add hcd when switch to host role, and remove hcd when switch
>>>   to peripheral, with this design, I cannot use this function?
>>
>> You add hcd only once during the life of the OTG device. If it is linked to the
>> OTG controller the OTG fsm manages the start/stop of hcd using the otg_hcd_ops.
>>
>> "usb/core/hcd.c"
>> static struct otg_hcd_ops otg_hcd_intf = {
>>         .add = usb_otg_add_hcd,
>>         .remove = usb_otg_remove_hcd,
>> };
>>
>> Your otg driver must use teh usb_otg_add/remove_hcd to start/stop the controller.
>> Using usb_remove_hcd() means the hcd resource is no longer available and the
>> otg fsm will be stopped.
>>
>>> - How about split it out of usb_add_hcd()?
>>
>> Adding the HCD and starting/stopping the hcd is split into
>> usb_add/remove_hcd() and usb_otg_add/remove_hcd() for OTG case.
>>
> 
> Catch your point now.
> Do usb_add_hcd to register hcd before start fsm, and use usb_otg_start_host()
> to start/stop host role for otg fsm.
> 
correct :).

>>>
>>>> + *
>>>> + * Returns: 0 on success, error value otherwise.
>>>> + */
>>>> +int usb_otg_register_hcd(struct usb_hcd *hcd, unsigned int irqnum,
>>>> +			 unsigned long irqflags, struct otg_hcd_ops *ops)
>>>> +{
>>>> +	struct usb_otg *otgd;
>>>> +	struct device *hcd_dev = hcd->self.controller;
>>>> +	struct device *otg_dev = usb_otg_get_device(hcd_dev);
>>>> +
>>>> +	if (!otg_dev)
>>>> +		return -EINVAL;	/* we're definitely not OTG */
>>>> +
>>>> +	/* we're otg but otg controller might not yet be registered */
>>>> +	mutex_lock(&otg_list_mutex);
>>>> +	otgd = usb_otg_get_data(otg_dev);
>>>> +	mutex_unlock(&otg_list_mutex);
>>>> +	if (!otgd) {
>>>> +		dev_dbg(hcd_dev,
>>>> +			"otg: controller not yet registered. waiting..\n");
>>>> +		/*
>>>> +		 * otg controller might register later. Put the hcd in
>>>> +		 * wait list and call us back when ready
>>>> +		 */
>>>> +		if (usb_otg_hcd_wait_add(otg_dev, hcd, irqnum, irqflags, ops)) {
>>>> +			dev_dbg(hcd_dev, "otg: failed to add to wait list\n");
>>>> +			return -EINVAL;
>>>> +		}
>>>> +
>>>> +		return 0;
>>>> +	}
>>>> +
>>>> +	/* HCD will be started by OTG fsm when needed */
>>>> +	mutex_lock(&otgd->fsm.lock);
>>>
>>> If call usb_add_hcd() when start host role, deadlock.
>>
>> No. You must call usb_otg_add_hcd() to start host role.
>>
>>>
>>>> +	if (otgd->primary_hcd.hcd) {
>>>> +		/* probably a shared HCD ? */
>>>> +		if (usb_otg_hcd_is_primary_hcd(hcd)) {
>>>> +			dev_err(otg_dev, "otg: primary host already registered\n");
>>>> +			goto err;
>>>> +		}
>>>> +
>>>> +		if (hcd->shared_hcd == otgd->primary_hcd.hcd) {
>>>> +			if (otgd->shared_hcd.hcd) {
>>>> +				dev_err(otg_dev, "otg: shared host already registered\n");
>>>> +				goto err;
>>>> +			}
>>>> +
>>>> +			otgd->shared_hcd.hcd = hcd;
>>>> +			otgd->shared_hcd.irqnum = irqnum;
>>>> +			otgd->shared_hcd.irqflags = irqflags;
>>>> +			otgd->shared_hcd.ops = ops;
>>>> +			dev_info(otg_dev, "otg: shared host %s registered\n",
>>>> +				 dev_name(hcd->self.controller));
>>>> +		} else {
>>>> +			dev_err(otg_dev, "otg: invalid shared host %s\n",
>>>> +				dev_name(hcd->self.controller));
>>>> +			goto err;
>>>> +		}
>>>> +	} else {
>>>> +		if (!usb_otg_hcd_is_primary_hcd(hcd)) {
>>>> +			dev_err(otg_dev, "otg: primary host must be registered first\n");
>>>> +			goto err;
>>>> +		}
>>>> +
>>>> +		otgd->primary_hcd.hcd = hcd;
>>>> +		otgd->primary_hcd.irqnum = irqnum;
>>>> +		otgd->primary_hcd.irqflags = irqflags;
>>>> +		otgd->primary_hcd.ops = ops;
>>>> +		dev_info(otg_dev, "otg: primary host %s registered\n",
>>>> +			 dev_name(hcd->self.controller));
>>>> +	}
>>>> +
>>>> +	/*
>>>> +	 * we're ready only if we have shared HCD
>>>> +	 * or we don't need shared HCD.
>>>> +	 */
>>>> +	if (otgd->shared_hcd.hcd || !otgd->primary_hcd.hcd->shared_hcd) {
>>>> +		otgd->fsm.otg->host = hcd_to_bus(hcd);
>>>
>>> otgd->host = hcd_to_bus(hcd);
>>
>> ok. So we set host at both places. struct usb_otg in struct otg_fsm starts to
>> feel redundant now. I think we should get rid of it and get the usb_otg struct
>> using container_of() instead.
>>
> 
> Then you may come to force existing otg fsm code to use your OTG core.

Let's leave it around then.

> 
>>>
>>>> +		/* FIXME: set bus->otg_port if this is true OTG port with HNP */
>>>> +
>>>> +		/* start FSM */
>>>> +		usb_otg_start_fsm(&otgd->fsm);
>>>> +	} else {
>>>> +		dev_dbg(otg_dev, "otg: can't start till shared host registers\n");
>>>> +	}
>>>> +
>>>> +	mutex_unlock(&otgd->fsm.lock);
>>>> +
>>>> +	return 0;
>>>> +
>>>> +err:
>>>> +	mutex_unlock(&otgd->fsm.lock);
>>>> +	return -EINVAL;
>>>
>>> Return non-zero, then if err, do we need call usb_otg_add_hcd() after
>>> usb_otg_register_hcd() fails?
>>
>> You should not call usb_otg_register_hcd() but usb_add_hcd().
>> If that fails then you fail as ususal.
> 
> My point is if we use usb_add_hcd(), but failed in usb_otg_register_hcd(),
> then usb_otg_add_hcd() will be called in *all* error case, is this your
> expectation?
> 	if (usb_otg_register_hcd(hcd, irqnum, irqflags, &otg_hcd_intf))
> 		return usb_otg_add_hcd(hcd, irqnum, irqflags);
> 

Yes, my intention was that if otg fails then it is a non otg HCD so register normally.
Let me correct my previous statement. If you are absolutely sure
that the HCD is for otg/dual-role usage then you should call usb_otg_register_hcd().

>>>> + * @fsm_running: state machine running/stopped indicator
>>>> + */
>>>>  struct usb_otg {
>>>>  	u8			default_a;
>>>>  
>>>>  	struct phy		*phy;
>>>>  	/* old usb_phy interface */
>>>>  	struct usb_phy		*usb_phy;
>>>> +
>>>
>>> add a blank line?
>>>
> 
> You missed this.

Sorry. Did you suggest to remove that blank line
or add a new one before usb_phy?

> 
>>>>  	struct usb_bus		*host;
>>>>  	struct usb_gadget	*gadget;
>>>>  
>>>>  	enum usb_otg_state	state;
>>>>  
>>>> +	struct device *dev;
>>>> +	struct usb_otg_caps *caps;
>>>> +	struct otg_fsm fsm;
>>>> +	struct otg_fsm_ops fsm_ops;
>>>> +
>>>> +	/* internal use only */
>>>> +	struct otg_hcd primary_hcd;
>>>> +	struct otg_hcd shared_hcd;
>>>> +	struct otg_gadget_ops *gadget_ops;
>>>> +	struct otg_timer timers[NUM_OTG_FSM_TIMERS];
>>>> +	struct list_head list;
>>>> +	struct work_struct work;
>>>> -- 
>>>> 2.1.4
> 

--
cheers,
-roger

^ permalink raw reply	[flat|nested] 48+ messages in thread

* Re: [PATCH v4 07/13] usb: otg: add OTG core
  2015-09-09  8:45                           ` Peter Chen
@ 2015-09-09 10:21                             ` Roger Quadros
  -1 siblings, 0 replies; 48+ messages in thread
From: Roger Quadros @ 2015-09-09 10:21 UTC (permalink / raw)
  To: Peter Chen
  Cc: stern, balbi, gregkh, dan.j.williams, jun.li, mathias.nyman, tony,
	Joao.Pinto, abrestic, linux-usb, linux-kernel, linux-omap

On 09/09/15 11:45, Peter Chen wrote:
> On Wed, Sep 09, 2015 at 12:33:20PM +0300, Roger Quadros wrote:
>> On 09/09/15 11:13, Peter Chen wrote:
>>> On Wed, Sep 09, 2015 at 12:08:10PM +0300, Roger Quadros wrote:
>>>> On 09/09/15 05:21, Peter Chen wrote:
>>>>> On Tue, Sep 08, 2015 at 03:25:25PM +0300, Roger Quadros wrote:
>>>>>>
>>>>>>
>>>>>> On 08/09/15 11:31, Peter Chen wrote:
>>>>>>> On Mon, Sep 07, 2015 at 01:23:01PM +0300, Roger Quadros wrote:
>>>>>>>> On 07/09/15 04:23, Peter Chen wrote:
>>>>>>>>> On Mon, Aug 24, 2015 at 04:21:18PM +0300, Roger Quadros wrote:
>>>>>>>>>> + * This is used by the USB Host stack to register the Host controller
>>>>>>>>>> + * to the OTG core. Host controller must not be started by the
>>>>>>>>>> + * caller as it is left upto the OTG state machine to do so.
>>>>>>>>>> + *
>>>>>>>>>> + * Returns: 0 on success, error value otherwise.
>>>>>>>>>> + */
>>>>>>>>>> +int usb_otg_register_hcd(struct usb_hcd *hcd, unsigned int irqnum,
>>>>>>>>>> +			 unsigned long irqflags, struct otg_hcd_ops *ops)
>>>>>>>>>> +{
>>>>>>>>>> +	struct usb_otg *otgd;
>>>>>>>>>> +	struct device *hcd_dev = hcd->self.controller;
>>>>>>>>>> +	struct device *otg_dev = usb_otg_get_device(hcd_dev);
>>>>>>>>>> +
>>>>>>>>>
>>>>>>>>> One big problem here is: there are two designs for current (IP) driver
>>>>>>>>> code, one creates dedicated hcd device as roothub's parent, like dwc3.
>>>>>>>>> Another one doesn't do this, roothub's parent is core device (or otg device
>>>>>>>>> in your patch), like chipidea and dwc2.
>>>>>>>>>
>>>>>>>>> Then, otg_dev will be glue layer device for chipidea after that.
>>>>>>>>
>>>>>>>> OK. Let's add a way for the otg controller driver to provide the host and gadget
>>>>>>>> information to the otg core for such devices like chipidea and dwc2.
>>>>>>>>
>>>>>>>
>>>>>>> Roger, not only chipidea and dwc2, I think the musb uses the same
>>>>>>> hierarchy. If the host, device, and otg share the same register
>>>>>>> region, host part can't be a platform driver since we don't want
>>>>>>> to remap the same register region again.
>>>>>>>
>>>>>>> So, in the design, we may need to consider both situations, one
>>>>>>> is otg/host/device has its own register region, and host is a
>>>>>>> separate platform device (A), the other is three parts share the
>>>>>>> same register region, there is only one platform driver (B).
>>>>>>>
>>>>>>> A:
>>>>>>>
>>>>>>> 			IP core device 
>>>>>>> 			    |
>>>>>>> 			    |
>>>>>>> 		      |-----|-----|
>>>>>>> 		      gadget   host platform device	
>>>>>>> 		      		|
>>>>>>> 				roothub
>>>>>>>
>>>>>>> B:
>>>>>>>
>>>>>>> 			IP core device
>>>>>>> 			    |
>>>>>>> 			    |
>>>>>>> 		      |-----|-----|
>>>>>>> 		      gadget   	 roothub
>>>>>>> 		      		
>>>>>>>
>>>>>>>> This API must be called before the hcd/gadget-driver is added so that the otg
>>>>>>>> core knows it's linked to an OTG controller.
>>>>>>>>
>>>>>>>> Any better idea?
>>>>>>>>
>>>>>>>
>>>>>>> A flag stands for this hcd controller is the same with otg controller
>>>>>>> can be used, this flag can be stored at struct usb_otg_config.
>>>>>>
>>>>>> What if there is another architecture like so?
>>>>>>
>>>>>> C:
>>>>>> 			[Parent]
>>>>>> 			   |
>>>>>> 			   |
>>>>>> 		|------------------|--------------|
>>>>>> 	[OTG core]		[gadget]	[host]
>>>>>>
>>>>>> We need a more flexible mechanism to link the gadget and
>>>>>> host device to the otg core for non DT case.
>>>>>>
>>>>>> How about adding struct usb_otg parameter to usb_otg_register_hcd()?
>>>>>>
>>>>>> e.g.
>>>>>> int usb_otg_register_hcd(struct usb_otg *otg, struct usb_hcd *hcd, ..)
>>>>>>
>>>>>> If otg is NULL it will try DT otg-controller property or parent to
>>>>>> get the otg controller.
>>>>>
>>>>> How usb_otg_register_hcd get struct usb_otg, from where?
>>>>
>>>> This only works when the parent driver creating the hcd has registered the
>>>> otg controller too.
>>>>
>>>
>>> Sorry? So we need to find another way to solve this issue, right?
>>
>> For existing cases this is sufficient.
>> The otg device is either the one supplied during usb_otg_register_hcd
>> (cases B and C) or it is the parent device (case A).
> 
> How we differentiate case A and case B at usb_otg_register_hcd?
> Would you show me the sample code?

Case A:

hcd platform driver doesn't know about otg device so it calls

	usb_add_hcd(hcd,..)->usb_otg_register_hcd(NULL, hcd,..);

Case B:

core driver knows about both otg and hcd so it calls
	usb_otg_register_hcd(otg, hcd,...);

code:

usb_otg_register_hcd(struct usb_otg *otg, struct usb_hcd *hcd, ...)
{
	if (!otg) {
		struct device *otg_dev;
		struct device *hcd_dev = hcd->self.controller;

		/* first get otg device */
		if (hcd_dev->of_node) { /* DT */
			struct device_node *np;
			struct platform_device *pdev;

			/* get otg from otg-controller property */
			np = of_parse_phandle(hcd_dev->of_node, "otg-controller",
					      0);
			pdev = of_find_device_by_node(np);
			of_node_put(np);
			if (!pdev) {
				dev_err(&pdev->dev, "couldn't get otg-controller device\n");
				return -EINVAL;
			}

			otg_dev = &pdev->dev;
		} else {
			/* Assume otg dev is parent */
			otg_dev = hcd_dev->parent;
		}

		otg = usb_otg_get_data(otg_dev);
		if (!otg) {
			/* otg controller not registered */
			return -EVINVAL
		}
	} else {
		/* use otg as is */
	}
}

usb_otg_get_data() returns the usb_otg structure if it finds a matching
otg_dev in the otg_dev list.

For this to work otg controller _must_ register before hcd/gadget.

> 
>>
>> It does not work when the 3 devices are totally independent and get registered
>> at different times.
>> I don't think there is such a case for non-DT yet, but let's not have this
>> limitation. So yes, we need to look for better solution :).
>>
> 
> Yes, we need to find some places to store gadget/host/otg information,
> Alan's suggestion to save them at device drvdata may be a direction, but
> I still doesn't have way to cover all cases.
> 

Yes, let's discuss more in that direction.

--
cheers,
-roger

^ permalink raw reply	[flat|nested] 48+ messages in thread

* Re: [PATCH v4 07/13] usb: otg: add OTG core
@ 2015-09-09 10:21                             ` Roger Quadros
  0 siblings, 0 replies; 48+ messages in thread
From: Roger Quadros @ 2015-09-09 10:21 UTC (permalink / raw)
  To: Peter Chen
  Cc: stern, balbi, gregkh, dan.j.williams, jun.li, mathias.nyman, tony,
	Joao.Pinto, abrestic, linux-usb, linux-kernel, linux-omap

On 09/09/15 11:45, Peter Chen wrote:
> On Wed, Sep 09, 2015 at 12:33:20PM +0300, Roger Quadros wrote:
>> On 09/09/15 11:13, Peter Chen wrote:
>>> On Wed, Sep 09, 2015 at 12:08:10PM +0300, Roger Quadros wrote:
>>>> On 09/09/15 05:21, Peter Chen wrote:
>>>>> On Tue, Sep 08, 2015 at 03:25:25PM +0300, Roger Quadros wrote:
>>>>>>
>>>>>>
>>>>>> On 08/09/15 11:31, Peter Chen wrote:
>>>>>>> On Mon, Sep 07, 2015 at 01:23:01PM +0300, Roger Quadros wrote:
>>>>>>>> On 07/09/15 04:23, Peter Chen wrote:
>>>>>>>>> On Mon, Aug 24, 2015 at 04:21:18PM +0300, Roger Quadros wrote:
>>>>>>>>>> + * This is used by the USB Host stack to register the Host controller
>>>>>>>>>> + * to the OTG core. Host controller must not be started by the
>>>>>>>>>> + * caller as it is left upto the OTG state machine to do so.
>>>>>>>>>> + *
>>>>>>>>>> + * Returns: 0 on success, error value otherwise.
>>>>>>>>>> + */
>>>>>>>>>> +int usb_otg_register_hcd(struct usb_hcd *hcd, unsigned int irqnum,
>>>>>>>>>> +			 unsigned long irqflags, struct otg_hcd_ops *ops)
>>>>>>>>>> +{
>>>>>>>>>> +	struct usb_otg *otgd;
>>>>>>>>>> +	struct device *hcd_dev = hcd->self.controller;
>>>>>>>>>> +	struct device *otg_dev = usb_otg_get_device(hcd_dev);
>>>>>>>>>> +
>>>>>>>>>
>>>>>>>>> One big problem here is: there are two designs for current (IP) driver
>>>>>>>>> code, one creates dedicated hcd device as roothub's parent, like dwc3.
>>>>>>>>> Another one doesn't do this, roothub's parent is core device (or otg device
>>>>>>>>> in your patch), like chipidea and dwc2.
>>>>>>>>>
>>>>>>>>> Then, otg_dev will be glue layer device for chipidea after that.
>>>>>>>>
>>>>>>>> OK. Let's add a way for the otg controller driver to provide the host and gadget
>>>>>>>> information to the otg core for such devices like chipidea and dwc2.
>>>>>>>>
>>>>>>>
>>>>>>> Roger, not only chipidea and dwc2, I think the musb uses the same
>>>>>>> hierarchy. If the host, device, and otg share the same register
>>>>>>> region, host part can't be a platform driver since we don't want
>>>>>>> to remap the same register region again.
>>>>>>>
>>>>>>> So, in the design, we may need to consider both situations, one
>>>>>>> is otg/host/device has its own register region, and host is a
>>>>>>> separate platform device (A), the other is three parts share the
>>>>>>> same register region, there is only one platform driver (B).
>>>>>>>
>>>>>>> A:
>>>>>>>
>>>>>>> 			IP core device 
>>>>>>> 			    |
>>>>>>> 			    |
>>>>>>> 		      |-----|-----|
>>>>>>> 		      gadget   host platform device	
>>>>>>> 		      		|
>>>>>>> 				roothub
>>>>>>>
>>>>>>> B:
>>>>>>>
>>>>>>> 			IP core device
>>>>>>> 			    |
>>>>>>> 			    |
>>>>>>> 		      |-----|-----|
>>>>>>> 		      gadget   	 roothub
>>>>>>> 		      		
>>>>>>>
>>>>>>>> This API must be called before the hcd/gadget-driver is added so that the otg
>>>>>>>> core knows it's linked to an OTG controller.
>>>>>>>>
>>>>>>>> Any better idea?
>>>>>>>>
>>>>>>>
>>>>>>> A flag stands for this hcd controller is the same with otg controller
>>>>>>> can be used, this flag can be stored at struct usb_otg_config.
>>>>>>
>>>>>> What if there is another architecture like so?
>>>>>>
>>>>>> C:
>>>>>> 			[Parent]
>>>>>> 			   |
>>>>>> 			   |
>>>>>> 		|------------------|--------------|
>>>>>> 	[OTG core]		[gadget]	[host]
>>>>>>
>>>>>> We need a more flexible mechanism to link the gadget and
>>>>>> host device to the otg core for non DT case.
>>>>>>
>>>>>> How about adding struct usb_otg parameter to usb_otg_register_hcd()?
>>>>>>
>>>>>> e.g.
>>>>>> int usb_otg_register_hcd(struct usb_otg *otg, struct usb_hcd *hcd, ..)
>>>>>>
>>>>>> If otg is NULL it will try DT otg-controller property or parent to
>>>>>> get the otg controller.
>>>>>
>>>>> How usb_otg_register_hcd get struct usb_otg, from where?
>>>>
>>>> This only works when the parent driver creating the hcd has registered the
>>>> otg controller too.
>>>>
>>>
>>> Sorry? So we need to find another way to solve this issue, right?
>>
>> For existing cases this is sufficient.
>> The otg device is either the one supplied during usb_otg_register_hcd
>> (cases B and C) or it is the parent device (case A).
> 
> How we differentiate case A and case B at usb_otg_register_hcd?
> Would you show me the sample code?

Case A:

hcd platform driver doesn't know about otg device so it calls

	usb_add_hcd(hcd,..)->usb_otg_register_hcd(NULL, hcd,..);

Case B:

core driver knows about both otg and hcd so it calls
	usb_otg_register_hcd(otg, hcd,...);

code:

usb_otg_register_hcd(struct usb_otg *otg, struct usb_hcd *hcd, ...)
{
	if (!otg) {
		struct device *otg_dev;
		struct device *hcd_dev = hcd->self.controller;

		/* first get otg device */
		if (hcd_dev->of_node) { /* DT */
			struct device_node *np;
			struct platform_device *pdev;

			/* get otg from otg-controller property */
			np = of_parse_phandle(hcd_dev->of_node, "otg-controller",
					      0);
			pdev = of_find_device_by_node(np);
			of_node_put(np);
			if (!pdev) {
				dev_err(&pdev->dev, "couldn't get otg-controller device\n");
				return -EINVAL;
			}

			otg_dev = &pdev->dev;
		} else {
			/* Assume otg dev is parent */
			otg_dev = hcd_dev->parent;
		}

		otg = usb_otg_get_data(otg_dev);
		if (!otg) {
			/* otg controller not registered */
			return -EVINVAL
		}
	} else {
		/* use otg as is */
	}
}

usb_otg_get_data() returns the usb_otg structure if it finds a matching
otg_dev in the otg_dev list.

For this to work otg controller _must_ register before hcd/gadget.

> 
>>
>> It does not work when the 3 devices are totally independent and get registered
>> at different times.
>> I don't think there is such a case for non-DT yet, but let's not have this
>> limitation. So yes, we need to look for better solution :).
>>
> 
> Yes, we need to find some places to store gadget/host/otg information,
> Alan's suggestion to save them at device drvdata may be a direction, but
> I still doesn't have way to cover all cases.
> 

Yes, let's discuss more in that direction.

--
cheers,
-roger

^ permalink raw reply	[flat|nested] 48+ messages in thread

* Re: [PATCH v4 07/13] usb: otg: add OTG core
       [not found] <Pine.LNX.4.44L0.1509081334190.1298-100000@iolanthe.rowland.org>
@ 2015-09-09 11:10     ` Roger Quadros
  0 siblings, 0 replies; 48+ messages in thread
From: Roger Quadros @ 2015-09-09 11:10 UTC (permalink / raw)
  To: Alan Stern
  Cc: Peter Chen, Balbi, Felipe, Greg Kroah-Hartman, Dan Williams,
	Li Jun, Mathias Nyman, Tony Lindgren,
	Joao.Pinto-HKixBCOQz3hWk0Htik3J/w, Andrew Bresticker,
	linux-usb-u79uwXL29TY76Z2rM5mHXA@public.gmane.org,
	linux-kernel-u79uwXL29TY76Z2rM5mHXA@public.gmane.org,
	linux-omap-u79uwXL29TY76Z2rM5mHXA

(adding back folks in cc)

On 08/09/15 20:35, Alan Stern wrote:
> On Tue, 8 Sep 2015, Roger Quadros wrote:
> 
>>>> What if there is another architecture like so?
>>>>
>>>> C:
>>>> 			[Parent]
>>>> 			   |
>>>> 			   |
>>>> 		|------------------|--------------|
>>>> 	[OTG core]		[gadget]	[host]
>>>>
>>>> We need a more flexible mechanism to link the gadget and
>>>> host device to the otg core for non DT case.
>>>>
>>>> How about adding struct usb_otg parameter to usb_otg_register_hcd()?
>>>>
>>>> e.g.
>>>> int usb_otg_register_hcd(struct usb_otg *otg, struct usb_hcd *hcd, ..)
>>>>
>>>> If otg is NULL it will try DT otg-controller property or parent to
>>>> get the otg controller.
>>>
>>> This seems a lot like something Peter and I discussed recently.  See
>>>
>>> 	http://marc.info/?l=linux-usb&m=143977568021328&w=2
>>>
>>> and the following messages in that thread.
>>>
>>
>> If I understood right, your proposal was to add a usb_pointers data
>> struct to the device's drvdata?
> 
> Right.
> 
>> This is fine only if the otg/gadget/host share the same device.
>> It does not solve the problem where each have different platform devices.
> 
> Why not?  Each of the platform devices can store a pointer to the same 
> usb_pointers structure.  Wouldn't that be suitable?
> 

I didn't understand how all of them get the reference to the
same usb_pointer structure (or contents) when their respective platform devices
get created so that they can store it in their respective drvdata.

Worst case, the 3 devices could be totally independent of each other without a
common parent, and get registered at different times.

The common resource here is the physical USB port for which the 3 drivers
otg/host/gadget are being registered.
There should be a mechanism for each device to tell that it is part of
this particular USB port. (or usb_pointers struct)

cheers,
-roger
--
To unsubscribe from this list: send the line "unsubscribe linux-usb" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply	[flat|nested] 48+ messages in thread

* Re: [PATCH v4 07/13] usb: otg: add OTG core
@ 2015-09-09 11:10     ` Roger Quadros
  0 siblings, 0 replies; 48+ messages in thread
From: Roger Quadros @ 2015-09-09 11:10 UTC (permalink / raw)
  To: Alan Stern
  Cc: Peter Chen, Balbi, Felipe, Greg Kroah-Hartman, Dan Williams,
	Li Jun, Mathias Nyman, Tony Lindgren, Joao.Pinto,
	Andrew Bresticker, linux-usb@vger.kernel.org,
	linux-kernel@vger.kernel.org, linux-omap

(adding back folks in cc)

On 08/09/15 20:35, Alan Stern wrote:
> On Tue, 8 Sep 2015, Roger Quadros wrote:
> 
>>>> What if there is another architecture like so?
>>>>
>>>> C:
>>>> 			[Parent]
>>>> 			   |
>>>> 			   |
>>>> 		|------------------|--------------|
>>>> 	[OTG core]		[gadget]	[host]
>>>>
>>>> We need a more flexible mechanism to link the gadget and
>>>> host device to the otg core for non DT case.
>>>>
>>>> How about adding struct usb_otg parameter to usb_otg_register_hcd()?
>>>>
>>>> e.g.
>>>> int usb_otg_register_hcd(struct usb_otg *otg, struct usb_hcd *hcd, ..)
>>>>
>>>> If otg is NULL it will try DT otg-controller property or parent to
>>>> get the otg controller.
>>>
>>> This seems a lot like something Peter and I discussed recently.  See
>>>
>>> 	http://marc.info/?l=linux-usb&m=143977568021328&w=2
>>>
>>> and the following messages in that thread.
>>>
>>
>> If I understood right, your proposal was to add a usb_pointers data
>> struct to the device's drvdata?
> 
> Right.
> 
>> This is fine only if the otg/gadget/host share the same device.
>> It does not solve the problem where each have different platform devices.
> 
> Why not?  Each of the platform devices can store a pointer to the same 
> usb_pointers structure.  Wouldn't that be suitable?
> 

I didn't understand how all of them get the reference to the
same usb_pointer structure (or contents) when their respective platform devices
get created so that they can store it in their respective drvdata.

Worst case, the 3 devices could be totally independent of each other without a
common parent, and get registered at different times.

The common resource here is the physical USB port for which the 3 drivers
otg/host/gadget are being registered.
There should be a mechanism for each device to tell that it is part of
this particular USB port. (or usb_pointers struct)

cheers,
-roger

^ permalink raw reply	[flat|nested] 48+ messages in thread

* Re: [PATCH v4 07/13] usb: otg: add OTG core
  2015-09-09 11:10     ` Roger Quadros
@ 2015-09-09 15:26         ` Alan Stern
  -1 siblings, 0 replies; 48+ messages in thread
From: Alan Stern @ 2015-09-09 15:26 UTC (permalink / raw)
  To: Roger Quadros
  Cc: Peter Chen, Balbi, Felipe, Greg Kroah-Hartman, Dan Williams,
	Li Jun, Mathias Nyman, Tony Lindgren,
	Joao.Pinto-HKixBCOQz3hWk0Htik3J/w, Andrew Bresticker,
	linux-usb-u79uwXL29TY76Z2rM5mHXA@public.gmane.org,
	linux-kernel-u79uwXL29TY76Z2rM5mHXA@public.gmane.org,
	linux-omap-u79uwXL29TY76Z2rM5mHXA

On Wed, 9 Sep 2015, Roger Quadros wrote:

> (adding back folks in cc)
> 
> On 08/09/15 20:35, Alan Stern wrote:
> > On Tue, 8 Sep 2015, Roger Quadros wrote:
> > 
> >>>> What if there is another architecture like so?
> >>>>
> >>>> C:
> >>>> 			[Parent]
> >>>> 			   |
> >>>> 			   |
> >>>> 		|------------------|--------------|
> >>>> 	[OTG core]		[gadget]	[host]
> >>>>
> >>>> We need a more flexible mechanism to link the gadget and
> >>>> host device to the otg core for non DT case.
> >>>>
> >>>> How about adding struct usb_otg parameter to usb_otg_register_hcd()?
> >>>>
> >>>> e.g.
> >>>> int usb_otg_register_hcd(struct usb_otg *otg, struct usb_hcd *hcd, ..)
> >>>>
> >>>> If otg is NULL it will try DT otg-controller property or parent to
> >>>> get the otg controller.
> >>>
> >>> This seems a lot like something Peter and I discussed recently.  See
> >>>
> >>> 	http://marc.info/?l=linux-usb&m=143977568021328&w=2
> >>>
> >>> and the following messages in that thread.
> >>>
> >>
> >> If I understood right, your proposal was to add a usb_pointers data
> >> struct to the device's drvdata?
> > 
> > Right.
> > 
> >> This is fine only if the otg/gadget/host share the same device.
> >> It does not solve the problem where each have different platform devices.
> > 
> > Why not?  Each of the platform devices can store a pointer to the same 
> > usb_pointers structure.  Wouldn't that be suitable?
> > 
> 
> I didn't understand how all of them get the reference to the
> same usb_pointer structure (or contents) when their respective platform devices
> get created so that they can store it in their respective drvdata.
> 
> Worst case, the 3 devices could be totally independent of each other without a
> common parent, and get registered at different times.
> 
> The common resource here is the physical USB port for which the 3 drivers
> otg/host/gadget are being registered.
> There should be a mechanism for each device to tell that it is part of
> this particular USB port. (or usb_pointers struct)

True, it's not clear how to set up the common relationship among the 
three drivers.  But there must be some way to do it if they all are 
talking to the same port or PHY.

The details are an issue for the platform-specific code to handle.  
I'm not sure what would be involved; maybe you can invent something.

Alan Stern

--
To unsubscribe from this list: send the line "unsubscribe linux-usb" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply	[flat|nested] 48+ messages in thread

* Re: [PATCH v4 07/13] usb: otg: add OTG core
@ 2015-09-09 15:26         ` Alan Stern
  0 siblings, 0 replies; 48+ messages in thread
From: Alan Stern @ 2015-09-09 15:26 UTC (permalink / raw)
  To: Roger Quadros
  Cc: Peter Chen, Balbi, Felipe, Greg Kroah-Hartman, Dan Williams,
	Li Jun, Mathias Nyman, Tony Lindgren, Joao.Pinto,
	Andrew Bresticker, linux-usb@vger.kernel.org,
	linux-kernel@vger.kernel.org, linux-omap

On Wed, 9 Sep 2015, Roger Quadros wrote:

> (adding back folks in cc)
> 
> On 08/09/15 20:35, Alan Stern wrote:
> > On Tue, 8 Sep 2015, Roger Quadros wrote:
> > 
> >>>> What if there is another architecture like so?
> >>>>
> >>>> C:
> >>>> 			[Parent]
> >>>> 			   |
> >>>> 			   |
> >>>> 		|------------------|--------------|
> >>>> 	[OTG core]		[gadget]	[host]
> >>>>
> >>>> We need a more flexible mechanism to link the gadget and
> >>>> host device to the otg core for non DT case.
> >>>>
> >>>> How about adding struct usb_otg parameter to usb_otg_register_hcd()?
> >>>>
> >>>> e.g.
> >>>> int usb_otg_register_hcd(struct usb_otg *otg, struct usb_hcd *hcd, ..)
> >>>>
> >>>> If otg is NULL it will try DT otg-controller property or parent to
> >>>> get the otg controller.
> >>>
> >>> This seems a lot like something Peter and I discussed recently.  See
> >>>
> >>> 	http://marc.info/?l=linux-usb&m=143977568021328&w=2
> >>>
> >>> and the following messages in that thread.
> >>>
> >>
> >> If I understood right, your proposal was to add a usb_pointers data
> >> struct to the device's drvdata?
> > 
> > Right.
> > 
> >> This is fine only if the otg/gadget/host share the same device.
> >> It does not solve the problem where each have different platform devices.
> > 
> > Why not?  Each of the platform devices can store a pointer to the same 
> > usb_pointers structure.  Wouldn't that be suitable?
> > 
> 
> I didn't understand how all of them get the reference to the
> same usb_pointer structure (or contents) when their respective platform devices
> get created so that they can store it in their respective drvdata.
> 
> Worst case, the 3 devices could be totally independent of each other without a
> common parent, and get registered at different times.
> 
> The common resource here is the physical USB port for which the 3 drivers
> otg/host/gadget are being registered.
> There should be a mechanism for each device to tell that it is part of
> this particular USB port. (or usb_pointers struct)

True, it's not clear how to set up the common relationship among the 
three drivers.  But there must be some way to do it if they all are 
talking to the same port or PHY.

The details are an issue for the platform-specific code to handle.  
I'm not sure what would be involved; maybe you can invent something.

Alan Stern


^ permalink raw reply	[flat|nested] 48+ messages in thread

* Re: [PATCH v4 07/13] usb: otg: add OTG core
  2015-09-09 10:21                             ` Roger Quadros
@ 2015-09-10  5:35                               ` Peter Chen
  -1 siblings, 0 replies; 48+ messages in thread
From: Peter Chen @ 2015-09-10  5:35 UTC (permalink / raw)
  To: Roger Quadros
  Cc: stern, balbi, gregkh, dan.j.williams, jun.li, mathias.nyman, tony,
	Joao.Pinto, abrestic, linux-usb, linux-kernel, linux-omap

On Wed, Sep 09, 2015 at 01:21:50PM +0300, Roger Quadros wrote:
> On 09/09/15 11:45, Peter Chen wrote:
> > On Wed, Sep 09, 2015 at 12:33:20PM +0300, Roger Quadros wrote:
> >> On 09/09/15 11:13, Peter Chen wrote:
> >>> On Wed, Sep 09, 2015 at 12:08:10PM +0300, Roger Quadros wrote:
> >>>> On 09/09/15 05:21, Peter Chen wrote:
> >>>>> On Tue, Sep 08, 2015 at 03:25:25PM +0300, Roger Quadros wrote:
> >>>>>>
> >>>>>>
> >>>>>> On 08/09/15 11:31, Peter Chen wrote:
> >>>>>>> On Mon, Sep 07, 2015 at 01:23:01PM +0300, Roger Quadros wrote:
> >>>>>>>> On 07/09/15 04:23, Peter Chen wrote:
> >>>>>>>>> On Mon, Aug 24, 2015 at 04:21:18PM +0300, Roger Quadros wrote:
> >>>>>>>>>> + * This is used by the USB Host stack to register the Host controller
> >>>>>>>>>> + * to the OTG core. Host controller must not be started by the
> >>>>>>>>>> + * caller as it is left upto the OTG state machine to do so.
> >>>>>>>>>> + *
> >>>>>>>>>> + * Returns: 0 on success, error value otherwise.
> >>>>>>>>>> + */
> >>>>>>>>>> +int usb_otg_register_hcd(struct usb_hcd *hcd, unsigned int irqnum,
> >>>>>>>>>> +			 unsigned long irqflags, struct otg_hcd_ops *ops)
> >>>>>>>>>> +{
> >>>>>>>>>> +	struct usb_otg *otgd;
> >>>>>>>>>> +	struct device *hcd_dev = hcd->self.controller;
> >>>>>>>>>> +	struct device *otg_dev = usb_otg_get_device(hcd_dev);
> >>>>>>>>>> +
> >>>>>>>>>
> >>>>>>>>> One big problem here is: there are two designs for current (IP) driver
> >>>>>>>>> code, one creates dedicated hcd device as roothub's parent, like dwc3.
> >>>>>>>>> Another one doesn't do this, roothub's parent is core device (or otg device
> >>>>>>>>> in your patch), like chipidea and dwc2.
> >>>>>>>>>
> >>>>>>>>> Then, otg_dev will be glue layer device for chipidea after that.
> >>>>>>>>
> >>>>>>>> OK. Let's add a way for the otg controller driver to provide the host and gadget
> >>>>>>>> information to the otg core for such devices like chipidea and dwc2.
> >>>>>>>>
> >>>>>>>
> >>>>>>> Roger, not only chipidea and dwc2, I think the musb uses the same
> >>>>>>> hierarchy. If the host, device, and otg share the same register
> >>>>>>> region, host part can't be a platform driver since we don't want
> >>>>>>> to remap the same register region again.
> >>>>>>>
> >>>>>>> So, in the design, we may need to consider both situations, one
> >>>>>>> is otg/host/device has its own register region, and host is a
> >>>>>>> separate platform device (A), the other is three parts share the
> >>>>>>> same register region, there is only one platform driver (B).
> >>>>>>>
> >>>>>>> A:
> >>>>>>>
> >>>>>>> 			IP core device 
> >>>>>>> 			    |
> >>>>>>> 			    |
> >>>>>>> 		      |-----|-----|
> >>>>>>> 		      gadget   host platform device	
> >>>>>>> 		      		|
> >>>>>>> 				roothub
> >>>>>>>
> >>>>>>> B:
> >>>>>>>
> >>>>>>> 			IP core device
> >>>>>>> 			    |
> >>>>>>> 			    |
> >>>>>>> 		      |-----|-----|
> >>>>>>> 		      gadget   	 roothub
> >>>>>>> 		      		
> >>>>>>>
> >>>>>>>> This API must be called before the hcd/gadget-driver is added so that the otg
> >>>>>>>> core knows it's linked to an OTG controller.
> >>>>>>>>
> >>>>>>>> Any better idea?
> >>>>>>>>
> >>>>>>>
> >>>>>>> A flag stands for this hcd controller is the same with otg controller
> >>>>>>> can be used, this flag can be stored at struct usb_otg_config.
> >>>>>>
> >>>>>> What if there is another architecture like so?
> >>>>>>
> >>>>>> C:
> >>>>>> 			[Parent]
> >>>>>> 			   |
> >>>>>> 			   |
> >>>>>> 		|------------------|--------------|
> >>>>>> 	[OTG core]		[gadget]	[host]
> >>>>>>
> >>>>>> We need a more flexible mechanism to link the gadget and
> >>>>>> host device to the otg core for non DT case.
> >>>>>>
> >>>>>> How about adding struct usb_otg parameter to usb_otg_register_hcd()?
> >>>>>>
> >>>>>> e.g.
> >>>>>> int usb_otg_register_hcd(struct usb_otg *otg, struct usb_hcd *hcd, ..)
> >>>>>>
> >>>>>> If otg is NULL it will try DT otg-controller property or parent to
> >>>>>> get the otg controller.
> >>>>>
> >>>>> How usb_otg_register_hcd get struct usb_otg, from where?
> >>>>
> >>>> This only works when the parent driver creating the hcd has registered the
> >>>> otg controller too.
> >>>>
> >>>
> >>> Sorry? So we need to find another way to solve this issue, right?
> >>
> >> For existing cases this is sufficient.
> >> The otg device is either the one supplied during usb_otg_register_hcd
> >> (cases B and C) or it is the parent device (case A).
> > 
> > How we differentiate case A and case B at usb_otg_register_hcd?
> > Would you show me the sample code?
> 
> Case A:
> 
> hcd platform driver doesn't know about otg device so it calls
> 
> 	usb_add_hcd(hcd,..)->usb_otg_register_hcd(NULL, hcd,..);
> 
> Case B:
> 
> core driver knows about both otg and hcd so it calls
> 	usb_otg_register_hcd(otg, hcd,...);
> 

Ok, Get your points, you mean invoke usb_otg_register_hcd at platform
driver directly instead of at hcd.c. It may be not a good solution
due to we use different otg APIs for two cases, it may confuse the
users, unless we can have some APIs (flags) are easy to read and well
documentation.

-- 

Best Regards,
Peter Chen

^ permalink raw reply	[flat|nested] 48+ messages in thread

* Re: [PATCH v4 07/13] usb: otg: add OTG core
@ 2015-09-10  5:35                               ` Peter Chen
  0 siblings, 0 replies; 48+ messages in thread
From: Peter Chen @ 2015-09-10  5:35 UTC (permalink / raw)
  To: Roger Quadros
  Cc: stern, balbi, gregkh, dan.j.williams, jun.li, mathias.nyman, tony,
	Joao.Pinto, abrestic, linux-usb, linux-kernel, linux-omap

On Wed, Sep 09, 2015 at 01:21:50PM +0300, Roger Quadros wrote:
> On 09/09/15 11:45, Peter Chen wrote:
> > On Wed, Sep 09, 2015 at 12:33:20PM +0300, Roger Quadros wrote:
> >> On 09/09/15 11:13, Peter Chen wrote:
> >>> On Wed, Sep 09, 2015 at 12:08:10PM +0300, Roger Quadros wrote:
> >>>> On 09/09/15 05:21, Peter Chen wrote:
> >>>>> On Tue, Sep 08, 2015 at 03:25:25PM +0300, Roger Quadros wrote:
> >>>>>>
> >>>>>>
> >>>>>> On 08/09/15 11:31, Peter Chen wrote:
> >>>>>>> On Mon, Sep 07, 2015 at 01:23:01PM +0300, Roger Quadros wrote:
> >>>>>>>> On 07/09/15 04:23, Peter Chen wrote:
> >>>>>>>>> On Mon, Aug 24, 2015 at 04:21:18PM +0300, Roger Quadros wrote:
> >>>>>>>>>> + * This is used by the USB Host stack to register the Host controller
> >>>>>>>>>> + * to the OTG core. Host controller must not be started by the
> >>>>>>>>>> + * caller as it is left upto the OTG state machine to do so.
> >>>>>>>>>> + *
> >>>>>>>>>> + * Returns: 0 on success, error value otherwise.
> >>>>>>>>>> + */
> >>>>>>>>>> +int usb_otg_register_hcd(struct usb_hcd *hcd, unsigned int irqnum,
> >>>>>>>>>> +			 unsigned long irqflags, struct otg_hcd_ops *ops)
> >>>>>>>>>> +{
> >>>>>>>>>> +	struct usb_otg *otgd;
> >>>>>>>>>> +	struct device *hcd_dev = hcd->self.controller;
> >>>>>>>>>> +	struct device *otg_dev = usb_otg_get_device(hcd_dev);
> >>>>>>>>>> +
> >>>>>>>>>
> >>>>>>>>> One big problem here is: there are two designs for current (IP) driver
> >>>>>>>>> code, one creates dedicated hcd device as roothub's parent, like dwc3.
> >>>>>>>>> Another one doesn't do this, roothub's parent is core device (or otg device
> >>>>>>>>> in your patch), like chipidea and dwc2.
> >>>>>>>>>
> >>>>>>>>> Then, otg_dev will be glue layer device for chipidea after that.
> >>>>>>>>
> >>>>>>>> OK. Let's add a way for the otg controller driver to provide the host and gadget
> >>>>>>>> information to the otg core for such devices like chipidea and dwc2.
> >>>>>>>>
> >>>>>>>
> >>>>>>> Roger, not only chipidea and dwc2, I think the musb uses the same
> >>>>>>> hierarchy. If the host, device, and otg share the same register
> >>>>>>> region, host part can't be a platform driver since we don't want
> >>>>>>> to remap the same register region again.
> >>>>>>>
> >>>>>>> So, in the design, we may need to consider both situations, one
> >>>>>>> is otg/host/device has its own register region, and host is a
> >>>>>>> separate platform device (A), the other is three parts share the
> >>>>>>> same register region, there is only one platform driver (B).
> >>>>>>>
> >>>>>>> A:
> >>>>>>>
> >>>>>>> 			IP core device 
> >>>>>>> 			    |
> >>>>>>> 			    |
> >>>>>>> 		      |-----|-----|
> >>>>>>> 		      gadget   host platform device	
> >>>>>>> 		      		|
> >>>>>>> 				roothub
> >>>>>>>
> >>>>>>> B:
> >>>>>>>
> >>>>>>> 			IP core device
> >>>>>>> 			    |
> >>>>>>> 			    |
> >>>>>>> 		      |-----|-----|
> >>>>>>> 		      gadget   	 roothub
> >>>>>>> 		      		
> >>>>>>>
> >>>>>>>> This API must be called before the hcd/gadget-driver is added so that the otg
> >>>>>>>> core knows it's linked to an OTG controller.
> >>>>>>>>
> >>>>>>>> Any better idea?
> >>>>>>>>
> >>>>>>>
> >>>>>>> A flag stands for this hcd controller is the same with otg controller
> >>>>>>> can be used, this flag can be stored at struct usb_otg_config.
> >>>>>>
> >>>>>> What if there is another architecture like so?
> >>>>>>
> >>>>>> C:
> >>>>>> 			[Parent]
> >>>>>> 			   |
> >>>>>> 			   |
> >>>>>> 		|------------------|--------------|
> >>>>>> 	[OTG core]		[gadget]	[host]
> >>>>>>
> >>>>>> We need a more flexible mechanism to link the gadget and
> >>>>>> host device to the otg core for non DT case.
> >>>>>>
> >>>>>> How about adding struct usb_otg parameter to usb_otg_register_hcd()?
> >>>>>>
> >>>>>> e.g.
> >>>>>> int usb_otg_register_hcd(struct usb_otg *otg, struct usb_hcd *hcd, ..)
> >>>>>>
> >>>>>> If otg is NULL it will try DT otg-controller property or parent to
> >>>>>> get the otg controller.
> >>>>>
> >>>>> How usb_otg_register_hcd get struct usb_otg, from where?
> >>>>
> >>>> This only works when the parent driver creating the hcd has registered the
> >>>> otg controller too.
> >>>>
> >>>
> >>> Sorry? So we need to find another way to solve this issue, right?
> >>
> >> For existing cases this is sufficient.
> >> The otg device is either the one supplied during usb_otg_register_hcd
> >> (cases B and C) or it is the parent device (case A).
> > 
> > How we differentiate case A and case B at usb_otg_register_hcd?
> > Would you show me the sample code?
> 
> Case A:
> 
> hcd platform driver doesn't know about otg device so it calls
> 
> 	usb_add_hcd(hcd,..)->usb_otg_register_hcd(NULL, hcd,..);
> 
> Case B:
> 
> core driver knows about both otg and hcd so it calls
> 	usb_otg_register_hcd(otg, hcd,...);
> 

Ok, Get your points, you mean invoke usb_otg_register_hcd at platform
driver directly instead of at hcd.c. It may be not a good solution
due to we use different otg APIs for two cases, it may confuse the
users, unless we can have some APIs (flags) are easy to read and well
documentation.

-- 

Best Regards,
Peter Chen

^ permalink raw reply	[flat|nested] 48+ messages in thread

* Re: [PATCH v4 07/13] usb: otg: add OTG core
  2015-09-09 10:01               ` Roger Quadros
@ 2015-09-10  9:28                 ` Li Jun
  -1 siblings, 0 replies; 48+ messages in thread
From: Li Jun @ 2015-09-10  9:28 UTC (permalink / raw)
  To: Roger Quadros
  Cc: stern, balbi, gregkh, peter.chen, dan.j.williams, jun.li,
	mathias.nyman, tony, Joao.Pinto, abrestic, linux-usb,
	linux-kernel, linux-omap

On Wed, Sep 09, 2015 at 01:01:14PM +0300, Roger Quadros wrote:
... ...

> >>>> +	return -EINVAL;
> >>>
> >>> Return non-zero, then if err, do we need call usb_otg_add_hcd() after
> >>> usb_otg_register_hcd() fails?
> >>
> >> You should not call usb_otg_register_hcd() but usb_add_hcd().
> >> If that fails then you fail as ususal.
> > 
> > My point is if we use usb_add_hcd(), but failed in usb_otg_register_hcd(),
> > then usb_otg_add_hcd() will be called in *all* error case, is this your
> > expectation?
> > 	if (usb_otg_register_hcd(hcd, irqnum, irqflags, &otg_hcd_intf))
> > 		return usb_otg_add_hcd(hcd, irqnum, irqflags);
> > 
> 
> Yes, my intention was that if otg fails then it is a non otg HCD so register normally.
> Let me correct my previous statement. If you are absolutely sure
> that the HCD is for otg/dual-role usage then you should call usb_otg_register_hcd().
> 

I think this is not just about a statement, in your usb_otg_register_hcd()
implementation, there are several places will return error, I think only
the first two are for a non-otg HCD case, the following error cases seems
mean this is for otg usage, but it fails in middle of registration, if that
is the case, is it reasonable to call usb_otg_add_hcd()?

> >>>> + * @fsm_running: state machine running/stopped indicator
> >>>> + */
> >>>>  struct usb_otg {
> >>>>  	u8			default_a;
> >>>>  
> >>>>  	struct phy		*phy;
> >>>>  	/* old usb_phy interface */
> >>>>  	struct usb_phy		*usb_phy;
> >>>> +
> >>>
> >>> add a blank line?
> >>>
> > 
> > You missed this.
> 
> Sorry. Did you suggest to remove that blank line
> or add a new one before usb_phy?
> 

Remove it.

> > 
> >>>>  	struct usb_bus		*host;
> >>>>  	struct usb_gadget	*gadget;
> >>>>  
> >>>>  	enum usb_otg_state	state;
> >>>>  
> >>>> +	struct device *dev;
> >>>> +	struct usb_otg_caps *caps;
> >>>> +	struct otg_fsm fsm;
> >>>> +	struct otg_fsm_ops fsm_ops;
> >>>> +
> >>>> +	/* internal use only */
> >>>> +	struct otg_hcd primary_hcd;
> >>>> +	struct otg_hcd shared_hcd;
> >>>> +	struct otg_gadget_ops *gadget_ops;
> >>>> +	struct otg_timer timers[NUM_OTG_FSM_TIMERS];
> >>>> +	struct list_head list;
> >>>> +	struct work_struct work;
> >>>> -- 
> >>>> 2.1.4
> > 
> 
> --
> cheers,
> -roger

^ permalink raw reply	[flat|nested] 48+ messages in thread

* Re: [PATCH v4 07/13] usb: otg: add OTG core
@ 2015-09-10  9:28                 ` Li Jun
  0 siblings, 0 replies; 48+ messages in thread
From: Li Jun @ 2015-09-10  9:28 UTC (permalink / raw)
  To: Roger Quadros
  Cc: stern, balbi, gregkh, peter.chen, dan.j.williams, jun.li,
	mathias.nyman, tony, Joao.Pinto, abrestic, linux-usb,
	linux-kernel, linux-omap

On Wed, Sep 09, 2015 at 01:01:14PM +0300, Roger Quadros wrote:
... ...

> >>>> +	return -EINVAL;
> >>>
> >>> Return non-zero, then if err, do we need call usb_otg_add_hcd() after
> >>> usb_otg_register_hcd() fails?
> >>
> >> You should not call usb_otg_register_hcd() but usb_add_hcd().
> >> If that fails then you fail as ususal.
> > 
> > My point is if we use usb_add_hcd(), but failed in usb_otg_register_hcd(),
> > then usb_otg_add_hcd() will be called in *all* error case, is this your
> > expectation?
> > 	if (usb_otg_register_hcd(hcd, irqnum, irqflags, &otg_hcd_intf))
> > 		return usb_otg_add_hcd(hcd, irqnum, irqflags);
> > 
> 
> Yes, my intention was that if otg fails then it is a non otg HCD so register normally.
> Let me correct my previous statement. If you are absolutely sure
> that the HCD is for otg/dual-role usage then you should call usb_otg_register_hcd().
> 

I think this is not just about a statement, in your usb_otg_register_hcd()
implementation, there are several places will return error, I think only
the first two are for a non-otg HCD case, the following error cases seems
mean this is for otg usage, but it fails in middle of registration, if that
is the case, is it reasonable to call usb_otg_add_hcd()?

> >>>> + * @fsm_running: state machine running/stopped indicator
> >>>> + */
> >>>>  struct usb_otg {
> >>>>  	u8			default_a;
> >>>>  
> >>>>  	struct phy		*phy;
> >>>>  	/* old usb_phy interface */
> >>>>  	struct usb_phy		*usb_phy;
> >>>> +
> >>>
> >>> add a blank line?
> >>>
> > 
> > You missed this.
> 
> Sorry. Did you suggest to remove that blank line
> or add a new one before usb_phy?
> 

Remove it.

> > 
> >>>>  	struct usb_bus		*host;
> >>>>  	struct usb_gadget	*gadget;
> >>>>  
> >>>>  	enum usb_otg_state	state;
> >>>>  
> >>>> +	struct device *dev;
> >>>> +	struct usb_otg_caps *caps;
> >>>> +	struct otg_fsm fsm;
> >>>> +	struct otg_fsm_ops fsm_ops;
> >>>> +
> >>>> +	/* internal use only */
> >>>> +	struct otg_hcd primary_hcd;
> >>>> +	struct otg_hcd shared_hcd;
> >>>> +	struct otg_gadget_ops *gadget_ops;
> >>>> +	struct otg_timer timers[NUM_OTG_FSM_TIMERS];
> >>>> +	struct list_head list;
> >>>> +	struct work_struct work;
> >>>> -- 
> >>>> 2.1.4
> > 
> 
> --
> cheers,
> -roger

^ permalink raw reply	[flat|nested] 48+ messages in thread

* Re: [PATCH v4 07/13] usb: otg: add OTG core
  2015-09-10  9:28                 ` Li Jun
@ 2015-09-10 14:14                   ` Roger Quadros
  -1 siblings, 0 replies; 48+ messages in thread
From: Roger Quadros @ 2015-09-10 14:14 UTC (permalink / raw)
  To: Li Jun
  Cc: stern-nwvwT67g6+6dFdvTe/nMLpVzexx5G7lz, balbi-l0cyMroinI0,
	gregkh-hQyY1W1yCW8ekmWlsbkhG0B+6BGkLq7r,
	peter.chen-KZfg59tc24xl57MIdRCFDg,
	dan.j.williams-ral2JQCrhuEAvxtiuMwx3w,
	jun.li-KZfg59tc24xl57MIdRCFDg,
	mathias.nyman-VuQAYsv1563Yd54FQh9/CA, tony-4v6yS6AI5VpBDgjK7y7TUQ,
	Joao.Pinto-HKixBCOQz3hWk0Htik3J/w,
	abrestic-F7+t8E8rja9g9hUCZPvPmw, linux-usb-u79uwXL29TY76Z2rM5mHXA,
	linux-kernel-u79uwXL29TY76Z2rM5mHXA,
	linux-omap-u79uwXL29TY76Z2rM5mHXA

On 10/09/15 12:28, Li Jun wrote:
> On Wed, Sep 09, 2015 at 01:01:14PM +0300, Roger Quadros wrote:
> ... ...
> 
>>>>>> +	return -EINVAL;
>>>>>
>>>>> Return non-zero, then if err, do we need call usb_otg_add_hcd() after
>>>>> usb_otg_register_hcd() fails?
>>>>
>>>> You should not call usb_otg_register_hcd() but usb_add_hcd().
>>>> If that fails then you fail as ususal.
>>>
>>> My point is if we use usb_add_hcd(), but failed in usb_otg_register_hcd(),
>>> then usb_otg_add_hcd() will be called in *all* error case, is this your
>>> expectation?
>>> 	if (usb_otg_register_hcd(hcd, irqnum, irqflags, &otg_hcd_intf))
>>> 		return usb_otg_add_hcd(hcd, irqnum, irqflags);
>>>
>>
>> Yes, my intention was that if otg fails then it is a non otg HCD so register normally.
>> Let me correct my previous statement. If you are absolutely sure
>> that the HCD is for otg/dual-role usage then you should call usb_otg_register_hcd().
>>
> 
> I think this is not just about a statement, in your usb_otg_register_hcd()
> implementation, there are several places will return error, I think only
> the first two are for a non-otg HCD case, the following error cases seems
> mean this is for otg usage, but it fails in middle of registration, if that
> is the case, is it reasonable to call usb_otg_add_hcd()?

OK. We need to check the return value then and differentiate if it is non-otg
or otg with failure.
If it is non-otg then only we must call usb_otg_add_hcd().

I will fix usb_add_hcd().

> 
>>>>>> + * @fsm_running: state machine running/stopped indicator
>>>>>> + */
>>>>>>  struct usb_otg {
>>>>>>  	u8			default_a;
>>>>>>  
>>>>>>  	struct phy		*phy;
>>>>>>  	/* old usb_phy interface */
>>>>>>  	struct usb_phy		*usb_phy;
>>>>>> +
>>>>>
>>>>> add a blank line?
>>>>>
>>>
>>> You missed this.
>>
>> Sorry. Did you suggest to remove that blank line
>> or add a new one before usb_phy?
>>
> 
> Remove it.

OK.

> 
>>>
>>>>>>  	struct usb_bus		*host;
>>>>>>  	struct usb_gadget	*gadget;
>>>>>>  
>>>>>>  	enum usb_otg_state	state;
>>>>>>  
>>>>>> +	struct device *dev;
>>>>>> +	struct usb_otg_caps *caps;
>>>>>> +	struct otg_fsm fsm;
>>>>>> +	struct otg_fsm_ops fsm_ops;
>>>>>> +
>>>>>> +	/* internal use only */
>>>>>> +	struct otg_hcd primary_hcd;
>>>>>> +	struct otg_hcd shared_hcd;
>>>>>> +	struct otg_gadget_ops *gadget_ops;
>>>>>> +	struct otg_timer timers[NUM_OTG_FSM_TIMERS];
>>>>>> +	struct list_head list;
>>>>>> +	struct work_struct work;
>>>>>> -- 
>>>>>> 2.1.4
>>>
--
cheers,
-roger
--
To unsubscribe from this list: send the line "unsubscribe linux-usb" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply	[flat|nested] 48+ messages in thread

* Re: [PATCH v4 07/13] usb: otg: add OTG core
@ 2015-09-10 14:14                   ` Roger Quadros
  0 siblings, 0 replies; 48+ messages in thread
From: Roger Quadros @ 2015-09-10 14:14 UTC (permalink / raw)
  To: Li Jun
  Cc: stern, balbi, gregkh, peter.chen, dan.j.williams, jun.li,
	mathias.nyman, tony, Joao.Pinto, abrestic, linux-usb,
	linux-kernel, linux-omap

On 10/09/15 12:28, Li Jun wrote:
> On Wed, Sep 09, 2015 at 01:01:14PM +0300, Roger Quadros wrote:
> ... ...
> 
>>>>>> +	return -EINVAL;
>>>>>
>>>>> Return non-zero, then if err, do we need call usb_otg_add_hcd() after
>>>>> usb_otg_register_hcd() fails?
>>>>
>>>> You should not call usb_otg_register_hcd() but usb_add_hcd().
>>>> If that fails then you fail as ususal.
>>>
>>> My point is if we use usb_add_hcd(), but failed in usb_otg_register_hcd(),
>>> then usb_otg_add_hcd() will be called in *all* error case, is this your
>>> expectation?
>>> 	if (usb_otg_register_hcd(hcd, irqnum, irqflags, &otg_hcd_intf))
>>> 		return usb_otg_add_hcd(hcd, irqnum, irqflags);
>>>
>>
>> Yes, my intention was that if otg fails then it is a non otg HCD so register normally.
>> Let me correct my previous statement. If you are absolutely sure
>> that the HCD is for otg/dual-role usage then you should call usb_otg_register_hcd().
>>
> 
> I think this is not just about a statement, in your usb_otg_register_hcd()
> implementation, there are several places will return error, I think only
> the first two are for a non-otg HCD case, the following error cases seems
> mean this is for otg usage, but it fails in middle of registration, if that
> is the case, is it reasonable to call usb_otg_add_hcd()?

OK. We need to check the return value then and differentiate if it is non-otg
or otg with failure.
If it is non-otg then only we must call usb_otg_add_hcd().

I will fix usb_add_hcd().

> 
>>>>>> + * @fsm_running: state machine running/stopped indicator
>>>>>> + */
>>>>>>  struct usb_otg {
>>>>>>  	u8			default_a;
>>>>>>  
>>>>>>  	struct phy		*phy;
>>>>>>  	/* old usb_phy interface */
>>>>>>  	struct usb_phy		*usb_phy;
>>>>>> +
>>>>>
>>>>> add a blank line?
>>>>>
>>>
>>> You missed this.
>>
>> Sorry. Did you suggest to remove that blank line
>> or add a new one before usb_phy?
>>
> 
> Remove it.

OK.

> 
>>>
>>>>>>  	struct usb_bus		*host;
>>>>>>  	struct usb_gadget	*gadget;
>>>>>>  
>>>>>>  	enum usb_otg_state	state;
>>>>>>  
>>>>>> +	struct device *dev;
>>>>>> +	struct usb_otg_caps *caps;
>>>>>> +	struct otg_fsm fsm;
>>>>>> +	struct otg_fsm_ops fsm_ops;
>>>>>> +
>>>>>> +	/* internal use only */
>>>>>> +	struct otg_hcd primary_hcd;
>>>>>> +	struct otg_hcd shared_hcd;
>>>>>> +	struct otg_gadget_ops *gadget_ops;
>>>>>> +	struct otg_timer timers[NUM_OTG_FSM_TIMERS];
>>>>>> +	struct list_head list;
>>>>>> +	struct work_struct work;
>>>>>> -- 
>>>>>> 2.1.4
>>>
--
cheers,
-roger

^ permalink raw reply	[flat|nested] 48+ messages in thread

* Re: [PATCH v4 07/13] usb: otg: add OTG core
  2015-09-10  5:35                               ` Peter Chen
@ 2015-09-10 14:17                                 ` Roger Quadros
  -1 siblings, 0 replies; 48+ messages in thread
From: Roger Quadros @ 2015-09-10 14:17 UTC (permalink / raw)
  To: Peter Chen
  Cc: stern, balbi, gregkh, dan.j.williams, jun.li, mathias.nyman, tony,
	Joao.Pinto, abrestic, linux-usb, linux-kernel, linux-omap

On 10/09/15 08:35, Peter Chen wrote:
> On Wed, Sep 09, 2015 at 01:21:50PM +0300, Roger Quadros wrote:
>> On 09/09/15 11:45, Peter Chen wrote:
>>> On Wed, Sep 09, 2015 at 12:33:20PM +0300, Roger Quadros wrote:
>>>> On 09/09/15 11:13, Peter Chen wrote:
>>>>> On Wed, Sep 09, 2015 at 12:08:10PM +0300, Roger Quadros wrote:
>>>>>> On 09/09/15 05:21, Peter Chen wrote:
>>>>>>> On Tue, Sep 08, 2015 at 03:25:25PM +0300, Roger Quadros wrote:
>>>>>>>>
>>>>>>>>
>>>>>>>> On 08/09/15 11:31, Peter Chen wrote:
>>>>>>>>> On Mon, Sep 07, 2015 at 01:23:01PM +0300, Roger Quadros wrote:
>>>>>>>>>> On 07/09/15 04:23, Peter Chen wrote:
>>>>>>>>>>> On Mon, Aug 24, 2015 at 04:21:18PM +0300, Roger Quadros wrote:
>>>>>>>>>>>> + * This is used by the USB Host stack to register the Host controller
>>>>>>>>>>>> + * to the OTG core. Host controller must not be started by the
>>>>>>>>>>>> + * caller as it is left upto the OTG state machine to do so.
>>>>>>>>>>>> + *
>>>>>>>>>>>> + * Returns: 0 on success, error value otherwise.
>>>>>>>>>>>> + */
>>>>>>>>>>>> +int usb_otg_register_hcd(struct usb_hcd *hcd, unsigned int irqnum,
>>>>>>>>>>>> +			 unsigned long irqflags, struct otg_hcd_ops *ops)
>>>>>>>>>>>> +{
>>>>>>>>>>>> +	struct usb_otg *otgd;
>>>>>>>>>>>> +	struct device *hcd_dev = hcd->self.controller;
>>>>>>>>>>>> +	struct device *otg_dev = usb_otg_get_device(hcd_dev);
>>>>>>>>>>>> +
>>>>>>>>>>>
>>>>>>>>>>> One big problem here is: there are two designs for current (IP) driver
>>>>>>>>>>> code, one creates dedicated hcd device as roothub's parent, like dwc3.
>>>>>>>>>>> Another one doesn't do this, roothub's parent is core device (or otg device
>>>>>>>>>>> in your patch), like chipidea and dwc2.
>>>>>>>>>>>
>>>>>>>>>>> Then, otg_dev will be glue layer device for chipidea after that.
>>>>>>>>>>
>>>>>>>>>> OK. Let's add a way for the otg controller driver to provide the host and gadget
>>>>>>>>>> information to the otg core for such devices like chipidea and dwc2.
>>>>>>>>>>
>>>>>>>>>
>>>>>>>>> Roger, not only chipidea and dwc2, I think the musb uses the same
>>>>>>>>> hierarchy. If the host, device, and otg share the same register
>>>>>>>>> region, host part can't be a platform driver since we don't want
>>>>>>>>> to remap the same register region again.
>>>>>>>>>
>>>>>>>>> So, in the design, we may need to consider both situations, one
>>>>>>>>> is otg/host/device has its own register region, and host is a
>>>>>>>>> separate platform device (A), the other is three parts share the
>>>>>>>>> same register region, there is only one platform driver (B).
>>>>>>>>>
>>>>>>>>> A:
>>>>>>>>>
>>>>>>>>> 			IP core device 
>>>>>>>>> 			    |
>>>>>>>>> 			    |
>>>>>>>>> 		      |-----|-----|
>>>>>>>>> 		      gadget   host platform device	
>>>>>>>>> 		      		|
>>>>>>>>> 				roothub
>>>>>>>>>
>>>>>>>>> B:
>>>>>>>>>
>>>>>>>>> 			IP core device
>>>>>>>>> 			    |
>>>>>>>>> 			    |
>>>>>>>>> 		      |-----|-----|
>>>>>>>>> 		      gadget   	 roothub
>>>>>>>>> 		      		
>>>>>>>>>
>>>>>>>>>> This API must be called before the hcd/gadget-driver is added so that the otg
>>>>>>>>>> core knows it's linked to an OTG controller.
>>>>>>>>>>
>>>>>>>>>> Any better idea?
>>>>>>>>>>
>>>>>>>>>
>>>>>>>>> A flag stands for this hcd controller is the same with otg controller
>>>>>>>>> can be used, this flag can be stored at struct usb_otg_config.
>>>>>>>>
>>>>>>>> What if there is another architecture like so?
>>>>>>>>
>>>>>>>> C:
>>>>>>>> 			[Parent]
>>>>>>>> 			   |
>>>>>>>> 			   |
>>>>>>>> 		|------------------|--------------|
>>>>>>>> 	[OTG core]		[gadget]	[host]
>>>>>>>>
>>>>>>>> We need a more flexible mechanism to link the gadget and
>>>>>>>> host device to the otg core for non DT case.
>>>>>>>>
>>>>>>>> How about adding struct usb_otg parameter to usb_otg_register_hcd()?
>>>>>>>>
>>>>>>>> e.g.
>>>>>>>> int usb_otg_register_hcd(struct usb_otg *otg, struct usb_hcd *hcd, ..)
>>>>>>>>
>>>>>>>> If otg is NULL it will try DT otg-controller property or parent to
>>>>>>>> get the otg controller.
>>>>>>>
>>>>>>> How usb_otg_register_hcd get struct usb_otg, from where?
>>>>>>
>>>>>> This only works when the parent driver creating the hcd has registered the
>>>>>> otg controller too.
>>>>>>
>>>>>
>>>>> Sorry? So we need to find another way to solve this issue, right?
>>>>
>>>> For existing cases this is sufficient.
>>>> The otg device is either the one supplied during usb_otg_register_hcd
>>>> (cases B and C) or it is the parent device (case A).
>>>
>>> How we differentiate case A and case B at usb_otg_register_hcd?
>>> Would you show me the sample code?
>>
>> Case A:
>>
>> hcd platform driver doesn't know about otg device so it calls
>>
>> 	usb_add_hcd(hcd,..)->usb_otg_register_hcd(NULL, hcd,..);
>>
>> Case B:
>>
>> core driver knows about both otg and hcd so it calls
>> 	usb_otg_register_hcd(otg, hcd,...);
>>
> 
> Ok, Get your points, you mean invoke usb_otg_register_hcd at platform
> driver directly instead of at hcd.c. It may be not a good solution
> due to we use different otg APIs for two cases, it may confuse the
> users, unless we can have some APIs (flags) are easy to read and well
> documentation.
> 

I need to think how else we can solve this problem so that it is usable
for all scenarios. If you get some bright ideas please do share :)

cheers,
-roger

^ permalink raw reply	[flat|nested] 48+ messages in thread

* Re: [PATCH v4 07/13] usb: otg: add OTG core
@ 2015-09-10 14:17                                 ` Roger Quadros
  0 siblings, 0 replies; 48+ messages in thread
From: Roger Quadros @ 2015-09-10 14:17 UTC (permalink / raw)
  To: Peter Chen
  Cc: stern, balbi, gregkh, dan.j.williams, jun.li, mathias.nyman, tony,
	Joao.Pinto, abrestic, linux-usb, linux-kernel, linux-omap

On 10/09/15 08:35, Peter Chen wrote:
> On Wed, Sep 09, 2015 at 01:21:50PM +0300, Roger Quadros wrote:
>> On 09/09/15 11:45, Peter Chen wrote:
>>> On Wed, Sep 09, 2015 at 12:33:20PM +0300, Roger Quadros wrote:
>>>> On 09/09/15 11:13, Peter Chen wrote:
>>>>> On Wed, Sep 09, 2015 at 12:08:10PM +0300, Roger Quadros wrote:
>>>>>> On 09/09/15 05:21, Peter Chen wrote:
>>>>>>> On Tue, Sep 08, 2015 at 03:25:25PM +0300, Roger Quadros wrote:
>>>>>>>>
>>>>>>>>
>>>>>>>> On 08/09/15 11:31, Peter Chen wrote:
>>>>>>>>> On Mon, Sep 07, 2015 at 01:23:01PM +0300, Roger Quadros wrote:
>>>>>>>>>> On 07/09/15 04:23, Peter Chen wrote:
>>>>>>>>>>> On Mon, Aug 24, 2015 at 04:21:18PM +0300, Roger Quadros wrote:
>>>>>>>>>>>> + * This is used by the USB Host stack to register the Host controller
>>>>>>>>>>>> + * to the OTG core. Host controller must not be started by the
>>>>>>>>>>>> + * caller as it is left upto the OTG state machine to do so.
>>>>>>>>>>>> + *
>>>>>>>>>>>> + * Returns: 0 on success, error value otherwise.
>>>>>>>>>>>> + */
>>>>>>>>>>>> +int usb_otg_register_hcd(struct usb_hcd *hcd, unsigned int irqnum,
>>>>>>>>>>>> +			 unsigned long irqflags, struct otg_hcd_ops *ops)
>>>>>>>>>>>> +{
>>>>>>>>>>>> +	struct usb_otg *otgd;
>>>>>>>>>>>> +	struct device *hcd_dev = hcd->self.controller;
>>>>>>>>>>>> +	struct device *otg_dev = usb_otg_get_device(hcd_dev);
>>>>>>>>>>>> +
>>>>>>>>>>>
>>>>>>>>>>> One big problem here is: there are two designs for current (IP) driver
>>>>>>>>>>> code, one creates dedicated hcd device as roothub's parent, like dwc3.
>>>>>>>>>>> Another one doesn't do this, roothub's parent is core device (or otg device
>>>>>>>>>>> in your patch), like chipidea and dwc2.
>>>>>>>>>>>
>>>>>>>>>>> Then, otg_dev will be glue layer device for chipidea after that.
>>>>>>>>>>
>>>>>>>>>> OK. Let's add a way for the otg controller driver to provide the host and gadget
>>>>>>>>>> information to the otg core for such devices like chipidea and dwc2.
>>>>>>>>>>
>>>>>>>>>
>>>>>>>>> Roger, not only chipidea and dwc2, I think the musb uses the same
>>>>>>>>> hierarchy. If the host, device, and otg share the same register
>>>>>>>>> region, host part can't be a platform driver since we don't want
>>>>>>>>> to remap the same register region again.
>>>>>>>>>
>>>>>>>>> So, in the design, we may need to consider both situations, one
>>>>>>>>> is otg/host/device has its own register region, and host is a
>>>>>>>>> separate platform device (A), the other is three parts share the
>>>>>>>>> same register region, there is only one platform driver (B).
>>>>>>>>>
>>>>>>>>> A:
>>>>>>>>>
>>>>>>>>> 			IP core device 
>>>>>>>>> 			    |
>>>>>>>>> 			    |
>>>>>>>>> 		      |-----|-----|
>>>>>>>>> 		      gadget   host platform device	
>>>>>>>>> 		      		|
>>>>>>>>> 				roothub
>>>>>>>>>
>>>>>>>>> B:
>>>>>>>>>
>>>>>>>>> 			IP core device
>>>>>>>>> 			    |
>>>>>>>>> 			    |
>>>>>>>>> 		      |-----|-----|
>>>>>>>>> 		      gadget   	 roothub
>>>>>>>>> 		      		
>>>>>>>>>
>>>>>>>>>> This API must be called before the hcd/gadget-driver is added so that the otg
>>>>>>>>>> core knows it's linked to an OTG controller.
>>>>>>>>>>
>>>>>>>>>> Any better idea?
>>>>>>>>>>
>>>>>>>>>
>>>>>>>>> A flag stands for this hcd controller is the same with otg controller
>>>>>>>>> can be used, this flag can be stored at struct usb_otg_config.
>>>>>>>>
>>>>>>>> What if there is another architecture like so?
>>>>>>>>
>>>>>>>> C:
>>>>>>>> 			[Parent]
>>>>>>>> 			   |
>>>>>>>> 			   |
>>>>>>>> 		|------------------|--------------|
>>>>>>>> 	[OTG core]		[gadget]	[host]
>>>>>>>>
>>>>>>>> We need a more flexible mechanism to link the gadget and
>>>>>>>> host device to the otg core for non DT case.
>>>>>>>>
>>>>>>>> How about adding struct usb_otg parameter to usb_otg_register_hcd()?
>>>>>>>>
>>>>>>>> e.g.
>>>>>>>> int usb_otg_register_hcd(struct usb_otg *otg, struct usb_hcd *hcd, ..)
>>>>>>>>
>>>>>>>> If otg is NULL it will try DT otg-controller property or parent to
>>>>>>>> get the otg controller.
>>>>>>>
>>>>>>> How usb_otg_register_hcd get struct usb_otg, from where?
>>>>>>
>>>>>> This only works when the parent driver creating the hcd has registered the
>>>>>> otg controller too.
>>>>>>
>>>>>
>>>>> Sorry? So we need to find another way to solve this issue, right?
>>>>
>>>> For existing cases this is sufficient.
>>>> The otg device is either the one supplied during usb_otg_register_hcd
>>>> (cases B and C) or it is the parent device (case A).
>>>
>>> How we differentiate case A and case B at usb_otg_register_hcd?
>>> Would you show me the sample code?
>>
>> Case A:
>>
>> hcd platform driver doesn't know about otg device so it calls
>>
>> 	usb_add_hcd(hcd,..)->usb_otg_register_hcd(NULL, hcd,..);
>>
>> Case B:
>>
>> core driver knows about both otg and hcd so it calls
>> 	usb_otg_register_hcd(otg, hcd,...);
>>
> 
> Ok, Get your points, you mean invoke usb_otg_register_hcd at platform
> driver directly instead of at hcd.c. It may be not a good solution
> due to we use different otg APIs for two cases, it may confuse the
> users, unless we can have some APIs (flags) are easy to read and well
> documentation.
> 

I need to think how else we can solve this problem so that it is usable
for all scenarios. If you get some bright ideas please do share :)

cheers,
-roger

^ permalink raw reply	[flat|nested] 48+ messages in thread

* Re: [PATCH v4 07/13] usb: otg: add OTG core
  2015-09-10 14:17                                 ` Roger Quadros
@ 2015-09-11  1:50                                   ` Peter Chen
  -1 siblings, 0 replies; 48+ messages in thread
From: Peter Chen @ 2015-09-11  1:50 UTC (permalink / raw)
  To: Roger Quadros
  Cc: stern, balbi, gregkh, dan.j.williams, jun.li, mathias.nyman, tony,
	Joao.Pinto, abrestic, linux-usb, linux-kernel, linux-omap

On Thu, Sep 10, 2015 at 05:17:36PM +0300, Roger Quadros wrote:
> On 10/09/15 08:35, Peter Chen wrote:
> > On Wed, Sep 09, 2015 at 01:21:50PM +0300, Roger Quadros wrote:
> >> On 09/09/15 11:45, Peter Chen wrote:
> >>> On Wed, Sep 09, 2015 at 12:33:20PM +0300, Roger Quadros wrote:
> >>>> On 09/09/15 11:13, Peter Chen wrote:
> >>>>> On Wed, Sep 09, 2015 at 12:08:10PM +0300, Roger Quadros wrote:
> >>>>>> On 09/09/15 05:21, Peter Chen wrote:
> >>>>>>> On Tue, Sep 08, 2015 at 03:25:25PM +0300, Roger Quadros wrote:
> >>>>>>>>
> >>>>>>>>
> >>>>>>>> On 08/09/15 11:31, Peter Chen wrote:
> >>>>>>>>> On Mon, Sep 07, 2015 at 01:23:01PM +0300, Roger Quadros wrote:
> >>>>>>>>>> On 07/09/15 04:23, Peter Chen wrote:
> >>>>>>>>>>> On Mon, Aug 24, 2015 at 04:21:18PM +0300, Roger Quadros wrote:
> >>>>>>>>>>>> + * This is used by the USB Host stack to register the Host controller
> >>>>>>>>>>>> + * to the OTG core. Host controller must not be started by the
> >>>>>>>>>>>> + * caller as it is left upto the OTG state machine to do so.
> >>>>>>>>>>>> + *
> >>>>>>>>>>>> + * Returns: 0 on success, error value otherwise.
> >>>>>>>>>>>> + */
> >>>>>>>>>>>> +int usb_otg_register_hcd(struct usb_hcd *hcd, unsigned int irqnum,
> >>>>>>>>>>>> +			 unsigned long irqflags, struct otg_hcd_ops *ops)
> >>>>>>>>>>>> +{
> >>>>>>>>>>>> +	struct usb_otg *otgd;
> >>>>>>>>>>>> +	struct device *hcd_dev = hcd->self.controller;
> >>>>>>>>>>>> +	struct device *otg_dev = usb_otg_get_device(hcd_dev);
> >>>>>>>>>>>> +
> >>>>>>>>>>>
> >>>>>>>>>>> One big problem here is: there are two designs for current (IP) driver
> >>>>>>>>>>> code, one creates dedicated hcd device as roothub's parent, like dwc3.
> >>>>>>>>>>> Another one doesn't do this, roothub's parent is core device (or otg device
> >>>>>>>>>>> in your patch), like chipidea and dwc2.
> >>>>>>>>>>>
> >>>>>>>>>>> Then, otg_dev will be glue layer device for chipidea after that.
> >>>>>>>>>>
> >>>>>>>>>> OK. Let's add a way for the otg controller driver to provide the host and gadget
> >>>>>>>>>> information to the otg core for such devices like chipidea and dwc2.
> >>>>>>>>>>
> >>>>>>>>>
> >>>>>>>>> Roger, not only chipidea and dwc2, I think the musb uses the same
> >>>>>>>>> hierarchy. If the host, device, and otg share the same register
> >>>>>>>>> region, host part can't be a platform driver since we don't want
> >>>>>>>>> to remap the same register region again.
> >>>>>>>>>
> >>>>>>>>> So, in the design, we may need to consider both situations, one
> >>>>>>>>> is otg/host/device has its own register region, and host is a
> >>>>>>>>> separate platform device (A), the other is three parts share the
> >>>>>>>>> same register region, there is only one platform driver (B).
> >>>>>>>>>
> >>>>>>>>> A:
> >>>>>>>>>
> >>>>>>>>> 			IP core device 
> >>>>>>>>> 			    |
> >>>>>>>>> 			    |
> >>>>>>>>> 		      |-----|-----|
> >>>>>>>>> 		      gadget   host platform device	
> >>>>>>>>> 		      		|
> >>>>>>>>> 				roothub
> >>>>>>>>>
> >>>>>>>>> B:
> >>>>>>>>>
> >>>>>>>>> 			IP core device
> >>>>>>>>> 			    |
> >>>>>>>>> 			    |
> >>>>>>>>> 		      |-----|-----|
> >>>>>>>>> 		      gadget   	 roothub
> >>>>>>>>> 		      		
> >>>>>>>>>
> >>>>>>>>>> This API must be called before the hcd/gadget-driver is added so that the otg
> >>>>>>>>>> core knows it's linked to an OTG controller.
> >>>>>>>>>>
> >>>>>>>>>> Any better idea?
> >>>>>>>>>>
> >>>>>>>>>
> >>>>>>>>> A flag stands for this hcd controller is the same with otg controller
> >>>>>>>>> can be used, this flag can be stored at struct usb_otg_config.
> >>>>>>>>
> >>>>>>>> What if there is another architecture like so?
> >>>>>>>>
> >>>>>>>> C:
> >>>>>>>> 			[Parent]
> >>>>>>>> 			   |
> >>>>>>>> 			   |
> >>>>>>>> 		|------------------|--------------|
> >>>>>>>> 	[OTG core]		[gadget]	[host]
> >>>>>>>>
> >>>>>>>> We need a more flexible mechanism to link the gadget and
> >>>>>>>> host device to the otg core for non DT case.
> >>>>>>>>
> >>>>>>>> How about adding struct usb_otg parameter to usb_otg_register_hcd()?
> >>>>>>>>
> >>>>>>>> e.g.
> >>>>>>>> int usb_otg_register_hcd(struct usb_otg *otg, struct usb_hcd *hcd, ..)
> >>>>>>>>
> >>>>>>>> If otg is NULL it will try DT otg-controller property or parent to
> >>>>>>>> get the otg controller.
> >>>>>>>
> >>>>>>> How usb_otg_register_hcd get struct usb_otg, from where?
> >>>>>>
> >>>>>> This only works when the parent driver creating the hcd has registered the
> >>>>>> otg controller too.
> >>>>>>
> >>>>>
> >>>>> Sorry? So we need to find another way to solve this issue, right?
> >>>>
> >>>> For existing cases this is sufficient.
> >>>> The otg device is either the one supplied during usb_otg_register_hcd
> >>>> (cases B and C) or it is the parent device (case A).
> >>>
> >>> How we differentiate case A and case B at usb_otg_register_hcd?
> >>> Would you show me the sample code?
> >>
> >> Case A:
> >>
> >> hcd platform driver doesn't know about otg device so it calls
> >>
> >> 	usb_add_hcd(hcd,..)->usb_otg_register_hcd(NULL, hcd,..);
> >>
> >> Case B:
> >>
> >> core driver knows about both otg and hcd so it calls
> >> 	usb_otg_register_hcd(otg, hcd,...);
> >>
> > 
> > Ok, Get your points, you mean invoke usb_otg_register_hcd at platform
> > driver directly instead of at hcd.c. It may be not a good solution
> > due to we use different otg APIs for two cases, it may confuse the
> > users, unless we can have some APIs (flags) are easy to read and well
> > documentation.
> > 
> 
> I need to think how else we can solve this problem so that it is usable
> for all scenarios. If you get some bright ideas please do share :)
> 

If we want to call OTG stuff at hcd/gadget driver, we'd better store
struct usb_otg pointer at struct usb_hcd and struct usb_gadget, since
the parent/child relationship can't be used now.

The platform driver can call usb_otg_register_hcd/usb_otg_register_gadget
to achieve this, it is a little different with your current design.

-- 

Best Regards,
Peter Chen

^ permalink raw reply	[flat|nested] 48+ messages in thread

* Re: [PATCH v4 07/13] usb: otg: add OTG core
@ 2015-09-11  1:50                                   ` Peter Chen
  0 siblings, 0 replies; 48+ messages in thread
From: Peter Chen @ 2015-09-11  1:50 UTC (permalink / raw)
  To: Roger Quadros
  Cc: stern, balbi, gregkh, dan.j.williams, jun.li, mathias.nyman, tony,
	Joao.Pinto, abrestic, linux-usb, linux-kernel, linux-omap

On Thu, Sep 10, 2015 at 05:17:36PM +0300, Roger Quadros wrote:
> On 10/09/15 08:35, Peter Chen wrote:
> > On Wed, Sep 09, 2015 at 01:21:50PM +0300, Roger Quadros wrote:
> >> On 09/09/15 11:45, Peter Chen wrote:
> >>> On Wed, Sep 09, 2015 at 12:33:20PM +0300, Roger Quadros wrote:
> >>>> On 09/09/15 11:13, Peter Chen wrote:
> >>>>> On Wed, Sep 09, 2015 at 12:08:10PM +0300, Roger Quadros wrote:
> >>>>>> On 09/09/15 05:21, Peter Chen wrote:
> >>>>>>> On Tue, Sep 08, 2015 at 03:25:25PM +0300, Roger Quadros wrote:
> >>>>>>>>
> >>>>>>>>
> >>>>>>>> On 08/09/15 11:31, Peter Chen wrote:
> >>>>>>>>> On Mon, Sep 07, 2015 at 01:23:01PM +0300, Roger Quadros wrote:
> >>>>>>>>>> On 07/09/15 04:23, Peter Chen wrote:
> >>>>>>>>>>> On Mon, Aug 24, 2015 at 04:21:18PM +0300, Roger Quadros wrote:
> >>>>>>>>>>>> + * This is used by the USB Host stack to register the Host controller
> >>>>>>>>>>>> + * to the OTG core. Host controller must not be started by the
> >>>>>>>>>>>> + * caller as it is left upto the OTG state machine to do so.
> >>>>>>>>>>>> + *
> >>>>>>>>>>>> + * Returns: 0 on success, error value otherwise.
> >>>>>>>>>>>> + */
> >>>>>>>>>>>> +int usb_otg_register_hcd(struct usb_hcd *hcd, unsigned int irqnum,
> >>>>>>>>>>>> +			 unsigned long irqflags, struct otg_hcd_ops *ops)
> >>>>>>>>>>>> +{
> >>>>>>>>>>>> +	struct usb_otg *otgd;
> >>>>>>>>>>>> +	struct device *hcd_dev = hcd->self.controller;
> >>>>>>>>>>>> +	struct device *otg_dev = usb_otg_get_device(hcd_dev);
> >>>>>>>>>>>> +
> >>>>>>>>>>>
> >>>>>>>>>>> One big problem here is: there are two designs for current (IP) driver
> >>>>>>>>>>> code, one creates dedicated hcd device as roothub's parent, like dwc3.
> >>>>>>>>>>> Another one doesn't do this, roothub's parent is core device (or otg device
> >>>>>>>>>>> in your patch), like chipidea and dwc2.
> >>>>>>>>>>>
> >>>>>>>>>>> Then, otg_dev will be glue layer device for chipidea after that.
> >>>>>>>>>>
> >>>>>>>>>> OK. Let's add a way for the otg controller driver to provide the host and gadget
> >>>>>>>>>> information to the otg core for such devices like chipidea and dwc2.
> >>>>>>>>>>
> >>>>>>>>>
> >>>>>>>>> Roger, not only chipidea and dwc2, I think the musb uses the same
> >>>>>>>>> hierarchy. If the host, device, and otg share the same register
> >>>>>>>>> region, host part can't be a platform driver since we don't want
> >>>>>>>>> to remap the same register region again.
> >>>>>>>>>
> >>>>>>>>> So, in the design, we may need to consider both situations, one
> >>>>>>>>> is otg/host/device has its own register region, and host is a
> >>>>>>>>> separate platform device (A), the other is three parts share the
> >>>>>>>>> same register region, there is only one platform driver (B).
> >>>>>>>>>
> >>>>>>>>> A:
> >>>>>>>>>
> >>>>>>>>> 			IP core device 
> >>>>>>>>> 			    |
> >>>>>>>>> 			    |
> >>>>>>>>> 		      |-----|-----|
> >>>>>>>>> 		      gadget   host platform device	
> >>>>>>>>> 		      		|
> >>>>>>>>> 				roothub
> >>>>>>>>>
> >>>>>>>>> B:
> >>>>>>>>>
> >>>>>>>>> 			IP core device
> >>>>>>>>> 			    |
> >>>>>>>>> 			    |
> >>>>>>>>> 		      |-----|-----|
> >>>>>>>>> 		      gadget   	 roothub
> >>>>>>>>> 		      		
> >>>>>>>>>
> >>>>>>>>>> This API must be called before the hcd/gadget-driver is added so that the otg
> >>>>>>>>>> core knows it's linked to an OTG controller.
> >>>>>>>>>>
> >>>>>>>>>> Any better idea?
> >>>>>>>>>>
> >>>>>>>>>
> >>>>>>>>> A flag stands for this hcd controller is the same with otg controller
> >>>>>>>>> can be used, this flag can be stored at struct usb_otg_config.
> >>>>>>>>
> >>>>>>>> What if there is another architecture like so?
> >>>>>>>>
> >>>>>>>> C:
> >>>>>>>> 			[Parent]
> >>>>>>>> 			   |
> >>>>>>>> 			   |
> >>>>>>>> 		|------------------|--------------|
> >>>>>>>> 	[OTG core]		[gadget]	[host]
> >>>>>>>>
> >>>>>>>> We need a more flexible mechanism to link the gadget and
> >>>>>>>> host device to the otg core for non DT case.
> >>>>>>>>
> >>>>>>>> How about adding struct usb_otg parameter to usb_otg_register_hcd()?
> >>>>>>>>
> >>>>>>>> e.g.
> >>>>>>>> int usb_otg_register_hcd(struct usb_otg *otg, struct usb_hcd *hcd, ..)
> >>>>>>>>
> >>>>>>>> If otg is NULL it will try DT otg-controller property or parent to
> >>>>>>>> get the otg controller.
> >>>>>>>
> >>>>>>> How usb_otg_register_hcd get struct usb_otg, from where?
> >>>>>>
> >>>>>> This only works when the parent driver creating the hcd has registered the
> >>>>>> otg controller too.
> >>>>>>
> >>>>>
> >>>>> Sorry? So we need to find another way to solve this issue, right?
> >>>>
> >>>> For existing cases this is sufficient.
> >>>> The otg device is either the one supplied during usb_otg_register_hcd
> >>>> (cases B and C) or it is the parent device (case A).
> >>>
> >>> How we differentiate case A and case B at usb_otg_register_hcd?
> >>> Would you show me the sample code?
> >>
> >> Case A:
> >>
> >> hcd platform driver doesn't know about otg device so it calls
> >>
> >> 	usb_add_hcd(hcd,..)->usb_otg_register_hcd(NULL, hcd,..);
> >>
> >> Case B:
> >>
> >> core driver knows about both otg and hcd so it calls
> >> 	usb_otg_register_hcd(otg, hcd,...);
> >>
> > 
> > Ok, Get your points, you mean invoke usb_otg_register_hcd at platform
> > driver directly instead of at hcd.c. It may be not a good solution
> > due to we use different otg APIs for two cases, it may confuse the
> > users, unless we can have some APIs (flags) are easy to read and well
> > documentation.
> > 
> 
> I need to think how else we can solve this problem so that it is usable
> for all scenarios. If you get some bright ideas please do share :)
> 

If we want to call OTG stuff at hcd/gadget driver, we'd better store
struct usb_otg pointer at struct usb_hcd and struct usb_gadget, since
the parent/child relationship can't be used now.

The platform driver can call usb_otg_register_hcd/usb_otg_register_gadget
to achieve this, it is a little different with your current design.

-- 

Best Regards,
Peter Chen

^ permalink raw reply	[flat|nested] 48+ messages in thread

end of thread, other threads:[~2015-09-11  3:03 UTC | newest]

Thread overview: 48+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
     [not found] <Pine.LNX.4.44L0.1509081334190.1298-100000@iolanthe.rowland.org>
     [not found] ` <Pine.LNX.4.44L0.1509081334190.1298-100000-IYeN2dnnYyZXsRXLowluHWD2FQJk+8+b@public.gmane.org>
2015-09-09 11:10   ` [PATCH v4 07/13] usb: otg: add OTG core Roger Quadros
2015-09-09 11:10     ` Roger Quadros
     [not found]     ` <55F013C1.9080400-l0cyMroinI0@public.gmane.org>
2015-09-09 15:26       ` Alan Stern
2015-09-09 15:26         ` Alan Stern
2015-08-24 13:21 [PATCH v4 00/13] USB: OTG/DRD Core functionality Roger Quadros
2015-08-24 13:21 ` [PATCH v4 07/13] usb: otg: add OTG core Roger Quadros
2015-08-24 13:21   ` Roger Quadros
2015-09-07  1:23   ` Peter Chen
2015-09-07  1:23     ` Peter Chen
2015-09-07 10:23     ` Roger Quadros
2015-09-07 10:23       ` Roger Quadros
     [not found]       ` <55ED6585.5050200-l0cyMroinI0@public.gmane.org>
2015-09-08  8:31         ` Peter Chen
2015-09-08  8:31           ` Peter Chen
2015-09-08 12:25           ` Roger Quadros
2015-09-08 12:25             ` Roger Quadros
     [not found]             ` <55EED3B5.5030806-l0cyMroinI0@public.gmane.org>
2015-09-08 14:34               ` Alan Stern
2015-09-08 14:34                 ` Alan Stern
2015-09-08 17:29                 ` Roger Quadros
2015-09-08 17:29                   ` Roger Quadros
2015-09-09  2:21               ` Peter Chen
2015-09-09  2:21                 ` Peter Chen
2015-09-09  9:08                 ` Roger Quadros
2015-09-09  9:08                   ` Roger Quadros
     [not found]                   ` <55EFF6FA.1000705-l0cyMroinI0@public.gmane.org>
2015-09-09  8:13                     ` Peter Chen
2015-09-09  8:13                       ` Peter Chen
2015-09-09  9:33                       ` Roger Quadros
2015-09-09  9:33                         ` Roger Quadros
2015-09-09  8:45                         ` Peter Chen
2015-09-09  8:45                           ` Peter Chen
2015-09-09 10:21                           ` Roger Quadros
2015-09-09 10:21                             ` Roger Quadros
2015-09-10  5:35                             ` Peter Chen
2015-09-10  5:35                               ` Peter Chen
2015-09-10 14:17                               ` Roger Quadros
2015-09-10 14:17                                 ` Roger Quadros
2015-09-11  1:50                                 ` Peter Chen
2015-09-11  1:50                                   ` Peter Chen
     [not found]   ` <1440422484-4737-8-git-send-email-rogerq-l0cyMroinI0@public.gmane.org>
2015-09-07  7:40     ` Li Jun
2015-09-07  7:40       ` Li Jun
2015-09-07 10:53       ` Roger Quadros
2015-09-07 10:53         ` Roger Quadros
     [not found]         ` <55ED6C9F.2000502-l0cyMroinI0@public.gmane.org>
2015-09-09  6:20           ` Li Jun
2015-09-09  6:20             ` Li Jun
2015-09-09 10:01             ` Roger Quadros
2015-09-09 10:01               ` Roger Quadros
2015-09-10  9:28               ` Li Jun
2015-09-10  9:28                 ` Li Jun
2015-09-10 14:14                 ` Roger Quadros
2015-09-10 14:14                   ` Roger Quadros

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.