All of lore.kernel.org
 help / color / mirror / Atom feed
From: Roger Quadros <rogerq@ti.com>
To: <balbi@kernel.org>
Cc: <vivek.gautam@codeaurora.org>, <linux-usb@vger.kernel.org>,
	<linux-kernel@vger.kernel.org>, Roger Quadros <rogerq@ti.com>
Subject: [PATCH v2 4/4] usb: dwc3: Workaround for super-speed host on dra7 in dual-role mode
Date: Thu, 16 Feb 2017 15:06:17 +0200	[thread overview]
Message-ID: <1487250377-13653-5-git-send-email-rogerq@ti.com> (raw)
In-Reply-To: <1487250377-13653-1-git-send-email-rogerq@ti.com>

dra7 OTG core limits the host controller to USB2.0 (high-speed) mode
when we're operating in dual-role.

We work around that by bypassing the OTG core and reading the
extcon framework directly for ID/VBUS events.

Signed-off-by: Roger Quadros <rogerq@ti.com>
---
 Documentation/devicetree/bindings/usb/dwc3.txt |   2 +
 drivers/usb/dwc3/core.c                        | 169 ++++++++++++++++++++++++-
 drivers/usb/dwc3/core.h                        |   5 +
 3 files changed, 170 insertions(+), 6 deletions(-)

diff --git a/Documentation/devicetree/bindings/usb/dwc3.txt b/Documentation/devicetree/bindings/usb/dwc3.txt
index e3e6983..9955c0d 100644
--- a/Documentation/devicetree/bindings/usb/dwc3.txt
+++ b/Documentation/devicetree/bindings/usb/dwc3.txt
@@ -53,6 +53,8 @@ Optional properties:
  - snps,quirk-frame-length-adjustment: Value for GFLADJ_30MHZ field of GFLADJ
 	register for post-silicon frame length adjustment when the
 	fladj_30mhz_sdbnd signal is invalid or incorrect.
+ - extcon: phandle to the USB connector extcon device. If present, extcon
+	device will be used to get USB cable events instead of OTG controller.
 
  - <DEPRECATED> tx-fifo-resize: determines if the FIFO *has* to be reallocated.
 
diff --git a/drivers/usb/dwc3/core.c b/drivers/usb/dwc3/core.c
index 619fa7c..b02d911 100644
--- a/drivers/usb/dwc3/core.c
+++ b/drivers/usb/dwc3/core.c
@@ -918,6 +918,19 @@ static void dwc3_otg_fsm_sync(struct dwc3 *dwc)
 	if (dwc->otg_prevent_sync)
 		return;
 
+	if (dwc->edev) {
+		/* get ID */
+		id = extcon_get_state(dwc->edev, EXTCON_USB_HOST);
+		/* Host means ID == 0 */
+		id = !id;
+
+		/* get VBUS */
+		vbus = extcon_get_state(dwc->edev, EXTCON_USB);
+		dwc3_drd_statemachine(dwc, id, vbus);
+
+		return;
+	}
+
 	reg = dwc3_readl(dwc->regs, DWC3_OSTS);
 	id = !!(reg & DWC3_OSTS_CONIDSTS);
 	vbus = !!(reg & DWC3_OSTS_BSESVLD);
@@ -934,6 +947,17 @@ static void dwc3_drd_work(struct work_struct *work)
 	spin_unlock(&dwc->lock);
 }
 
+static int dwc3_drd_notifier(struct notifier_block *nb,
+			     unsigned long event, void *ptr)
+{
+	struct dwc3 *dwc = container_of(nb, struct dwc3, edev_nb);
+
+	if (!dwc->otg_prevent_sync)
+		queue_work(system_power_efficient_wq, &dwc->otg_work);
+
+	return NOTIFY_DONE;
+}
+
 static void dwc3_otg_disable_events(struct dwc3 *dwc, u32 disable_mask)
 {
 	dwc->oevten &= ~(disable_mask);
@@ -1040,6 +1064,27 @@ static int dwc3_drd_start_host(struct dwc3 *dwc, int on, bool skip)
 {
 	u32 reg;
 
+	if (!dwc->edev)
+		goto otg;
+
+	if (on)
+		dwc3_set_mode(dwc, DWC3_GCTL_PRTCAP_HOST);
+
+	if (!skip) {
+		spin_unlock(&dwc->lock);
+
+		/* start or stop the HCD */
+		if (on)
+			dwc3_host_init(dwc);
+		else
+			dwc3_host_exit(dwc);
+
+		spin_lock(&dwc->lock);
+	}
+
+	return 0;
+
+otg:
 	/* switch OTG core */
 	if (on) {
 		/* As per Figure 11-10 A-Device Flow Diagram */
@@ -1133,6 +1178,33 @@ static int dwc3_drd_start_gadget(struct dwc3 *dwc, int on)
 	if (on)
 		dwc3_event_buffers_setup(dwc);
 
+	if (!dwc->edev)
+		goto otg;
+
+	if (on) {
+		dwc3_set_mode(dwc, DWC3_GCTL_PRTCAP_DEVICE);
+		/* start the Peripheral driver  */
+		if (dwc->gadget_driver) {
+			__dwc3_gadget_start(dwc);
+			if (dwc->gadget_pullup)
+				dwc3_gadget_run_stop(dwc, true, false);
+		}
+	} else {
+		/* stop the Peripheral driver */
+		if (dwc->gadget_driver) {
+			if (dwc->gadget_pullup)
+				dwc3_gadget_run_stop(dwc, false, false);
+			spin_unlock(&dwc->lock);
+			if (dwc->gadget_driver->disconnect)
+				dwc->gadget_driver->disconnect(&dwc->gadget);
+			spin_lock(&dwc->lock);
+			__dwc3_gadget_stop(dwc);
+		}
+	}
+
+	return 0;
+
+otg:
 	if (on) {
 		/* As per Figure 11-20 B-Device Flow Diagram */
 
@@ -1251,6 +1323,13 @@ static void dwc3_otg_core_init(struct dwc3 *dwc)
 {
 	u32 reg;
 
+	/* force drd state machine update the first time */
+	dwc->otg_fsm.b_sess_vld = -1;
+	dwc->otg_fsm.id = -1;
+
+	if (dwc->edev)
+		return;
+
 	/*
 	 * As per Figure 11-4 OTG Driver Overall Programming Flow,
 	 * block "Initialize GCTL for OTG operation".
@@ -1264,15 +1343,14 @@ static void dwc3_otg_core_init(struct dwc3 *dwc)
 
 	/* Initialize OTG registers */
 	dwc3_otgregs_init(dwc);
-
-	/* force drd state machine update the first time */
-	dwc->otg_fsm.b_sess_vld = -1;
-	dwc->otg_fsm.id = -1;
 }
 
 /* dwc->lock must be held */
 static void dwc3_otg_core_exit(struct dwc3 *dwc)
 {
+	if (dwc->edev)
+		return;
+
 	/* disable all otg irqs */
 	dwc3_otg_disable_events(dwc, DWC3_OTG_ALL_EVENTS);
 	/* clear all events */
@@ -1286,6 +1364,57 @@ static int dwc3_drd_init(struct dwc3 *dwc)
 
 	INIT_WORK(&dwc->otg_work, dwc3_drd_work);
 
+	/* If extcon device is present we don't rely on OTG core for ID event */
+	if (dwc->edev) {
+		int id, vbus;
+
+		dwc->edev_nb.notifier_call = dwc3_drd_notifier;
+		ret = extcon_register_notifier(dwc->edev, EXTCON_USB,
+					       &dwc->edev_nb);
+		if (ret < 0) {
+			dev_err(dwc->dev, "Couldn't register USB cable notifier\n");
+			return -ENODEV;
+		}
+
+		ret = extcon_register_notifier(dwc->edev, EXTCON_USB_HOST,
+					       &dwc->edev_nb);
+		if (ret < 0) {
+			dev_err(dwc->dev, "Couldn't register USB-HOST cable notifier\n");
+			ret = -ENODEV;
+			goto extcon_fail;
+		}
+
+		/* sanity check id & vbus states */
+		id = extcon_get_state(dwc->edev, EXTCON_USB_HOST);
+		vbus = extcon_get_state(dwc->edev, EXTCON_USB);
+		if (id < 0 || vbus < 0) {
+			dev_err(dwc->dev, "Invalid USB cable state. id %d, vbus %d\n",
+				id, vbus);
+			ret = -ENODEV;
+			goto fail;
+		}
+
+		ret = dwc3_gadget_init(dwc);
+		if (ret)
+			goto fail;
+
+		spin_lock_irqsave(&dwc->lock, flags);
+		dwc3_otg_core_init(dwc);
+		spin_unlock_irqrestore(&dwc->lock, flags);
+		queue_work(system_power_efficient_wq, &dwc->otg_work);
+
+		return 0;
+
+fail:
+		extcon_unregister_notifier(dwc->edev, EXTCON_USB_HOST,
+					   &dwc->edev_nb);
+extcon_fail:
+		extcon_unregister_notifier(dwc->edev, EXTCON_USB,
+					   &dwc->edev_nb);
+
+		return ret;
+	}
+
 	irq = dwc3_otg_get_irq(dwc);
 	if (irq < 0)
 		return irq;
@@ -1326,13 +1455,24 @@ static void dwc3_drd_exit(struct dwc3 *dwc)
 {
 	unsigned long flags;
 
+	spin_lock(&dwc->lock);
+	dwc->otg_prevent_sync = true;
+	spin_unlock(&dwc->lock);
 	cancel_work_sync(&dwc->otg_work);
+
 	spin_lock_irqsave(&dwc->lock, flags);
 	dwc3_otg_core_exit(dwc);
 	if (dwc->otg_fsm.protocol == PROTO_HOST)
 		dwc3_drd_start_host(dwc, 0, 0);
 	dwc->otg_fsm.protocol = PROTO_UNDEF;
-	free_irq(dwc->otg_irq, dwc);
+	if (dwc->edev) {
+		extcon_unregister_notifier(dwc->edev, EXTCON_USB_HOST,
+					   &dwc->edev_nb);
+		extcon_unregister_notifier(dwc->edev, EXTCON_USB,
+					   &dwc->edev_nb);
+	} else {
+		free_irq(dwc->otg_irq, dwc);
+	}
 	spin_unlock_irqrestore(&dwc->lock, flags);
 
 	dwc3_gadget_exit(dwc);
@@ -1587,6 +1727,14 @@ static int dwc3_probe(struct platform_device *pdev)
 
 	dwc3_get_properties(dwc);
 
+	if (dev->of_node) {
+		if (of_property_read_bool(dev->of_node, "extcon"))
+			dwc->edev = extcon_get_edev_by_phandle(dev, 0);
+
+		if (IS_ERR(dwc->edev))
+			return PTR_ERR(dwc->edev);
+	}
+
 	platform_set_drvdata(pdev, dwc);
 	dwc3_cache_hwparams(dwc);
 
@@ -1743,6 +1891,13 @@ static int dwc3_resume_common(struct dwc3 *dwc)
 	if (ret)
 		return ret;
 
+	if (dwc->dr_mode == USB_DR_MODE_OTG &&
+	    dwc->edev) {
+		if (dwc->otg_fsm.protocol == PROTO_HOST)
+			dwc3_set_mode(dwc, DWC3_GCTL_PRTCAP_HOST);
+		else if (dwc->otg_fsm.protocol == PROTO_GADGET)
+			dwc3_set_mode(dwc, DWC3_GCTL_PRTCAP_DEVICE);
+	}
 	spin_lock_irqsave(&dwc->lock, flags);
 
 	switch (dwc->dr_mode) {
@@ -1771,8 +1926,10 @@ static int dwc3_resume_common(struct dwc3 *dwc)
 
 	if (dwc->dr_mode == USB_DR_MODE_OTG) {
 		dwc3_otg_core_init(dwc);
-		if (dwc->otg_fsm.protocol == PROTO_HOST)
+		if ((dwc->otg_fsm.protocol == PROTO_HOST) &&
+		    !dwc->edev) {
 			dwc3_drd_start_host(dwc, true, 1);
+		}
 	}
 
 	spin_unlock_irqrestore(&dwc->lock, flags);
diff --git a/drivers/usb/dwc3/core.h b/drivers/usb/dwc3/core.h
index bf8951d..fc060ae 100644
--- a/drivers/usb/dwc3/core.h
+++ b/drivers/usb/dwc3/core.h
@@ -38,6 +38,7 @@
 #include <linux/usb/otg.h>
 #include <linux/usb/otg-fsm.h>
 
+#include <linux/extcon.h>
 #include <linux/workqueue.h>
 
 #define DWC3_MSG_MAX	500
@@ -869,6 +870,8 @@ struct dwc3_scratchpad_array {
  * @current_mode: current mode of operation written to PRTCAPDIR
  * @otg_work: work struct for otg/dual-role
  * @otg_needs_host_start: flag that OTG controller needs to start host
+ * @edev: extcon handle
+ * @edev_nb: extcon notifier
  * @fladj: frame length adjustment
  * @irq_gadget: peripheral controller's IRQ number
  * @otg_irq: IRQ number for OTG IRQs
@@ -1007,6 +1010,8 @@ struct dwc3 {
 	u32			current_mode;
 	struct work_struct	otg_work;
 	bool			otg_needs_host_start;
+	struct extcon_dev	*edev;
+	struct notifier_block	edev_nb;
 
 	enum usb_phy_interface	hsphy_mode;
 
-- 
2.7.4

  parent reply	other threads:[~2017-02-16 13:07 UTC|newest]

Thread overview: 37+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2017-02-16 13:06 [PATCH v2 0/4] usb: dwc3: dual-role support Roger Quadros
2017-02-16 13:06 ` [PATCH v2 1/4] usb: dwc3: core.h: add some register definitions Roger Quadros
2017-02-16 13:06 ` [PATCH v2 2/4] usb: dwc3: omap: don't miss events during suspend/resume Roger Quadros
2017-02-16 13:06 ` [PATCH v2 3/4] usb: dwc3: add dual-role support Roger Quadros
2017-03-28 11:07   ` Felipe Balbi
2017-03-29 11:33     ` Roger Quadros
2017-03-29 13:15       ` Felipe Balbi
2017-03-30  6:40         ` Roger Quadros
2017-03-30  9:27           ` Felipe Balbi
2017-04-03  5:31             ` John Youn
2017-02-16 13:06 ` Roger Quadros [this message]
2017-02-23  8:34   ` [PATCH v2 4/4] usb: dwc3: Workaround for super-speed host on dra7 in dual-role mode Vivek Gautam
2017-02-24  0:57     ` Peter Chen
2017-02-24  3:08       ` Vivek Gautam
2017-02-24 12:02     ` Roger Quadros
2017-02-25  3:46       ` Chanwoo Choi
2017-02-25  3:50         ` Chanwoo Choi
2017-02-28 13:54           ` Vivek Gautam
2017-02-25  3:35   ` Chanwoo Choi
2017-02-28 15:17     ` Roger Quadros
2017-03-28 11:10   ` Felipe Balbi
2017-03-29  9:57     ` Roger Quadros
2017-03-29 10:32       ` Felipe Balbi
2017-03-29 12:00         ` Roger Quadros
2017-03-29 13:21           ` Felipe Balbi
2017-03-29 13:58             ` Roger Quadros
2017-03-30  9:32               ` Felipe Balbi
2017-03-30 10:11                 ` Roger Quadros
2017-03-31  7:43         ` Roger Quadros
2017-03-31  7:46           ` Felipe Balbi
2017-03-31 11:50             ` Roger Quadros
2017-03-31 12:00               ` Felipe Balbi
2017-03-31 12:21                 ` Roger Quadros
2017-03-31 12:58                   ` Felipe Balbi
2017-03-13  8:33 ` [PATCH v2 0/4] usb: dwc3: dual-role support Roger Quadros
2017-03-28 10:27 ` Felipe Balbi
2017-03-29  9:50   ` Roger Quadros

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=1487250377-13653-5-git-send-email-rogerq@ti.com \
    --to=rogerq@ti.com \
    --cc=balbi@kernel.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=linux-usb@vger.kernel.org \
    --cc=vivek.gautam@codeaurora.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
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.