lists.openwall.net   lists  /  announce  owl-users  owl-dev  john-users  john-dev  passwdqc-users  yescrypt  popa3d-users  /  oss-security  kernel-hardening  musl  sabotage  tlsify  passwords  /  crypt-dev  xvendor  /  Bugtraq  Full-Disclosure  linux-kernel  linux-netdev  linux-ext4  linux-hardening  linux-cve-announce  PHC 
Open Source and information security mailing list archives
 
Hash Suite: Windows password security audit tool. GUI, reports in PDF.
[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-id: <1426687464-2563-13-git-send-email-r.baldyga@samsung.com>
Date:	Wed, 18 Mar 2015 15:04:17 +0100
From:	Robert Baldyga <r.baldyga@...sung.com>
To:	balbi@...com
Cc:	gregkh@...uxfoundation.org, myungjoo.ham@...sung.com,
	cw00.choi@...sung.com, linux-usb@...r.kernel.org,
	linux-kernel@...r.kernel.org, m.szyprowski@...sung.com,
	Robert Baldyga <r.baldyga@...sung.com>
Subject: [RFC 12/19] dwc3: otg: add ext_otg_ops support

This feature allows to use OTG feature with devices which doesn't support
hardware DWC3 OTG. In such situation we can supply own mechanism of cable
detection.

This code is inspired by DWC3 driver from Hardkernel Linux sources [1].

[1] https://github.com/hardkernel/linux.

Signed-off-by: Robert Baldyga <r.baldyga@...sung.com>
---
 drivers/usb/dwc3/otg.c | 89 ++++++++++++++++++++++++++++++++++++++++----------
 drivers/usb/dwc3/otg.h | 48 +++++++++++++++++++++++++++
 2 files changed, 120 insertions(+), 17 deletions(-)

diff --git a/drivers/usb/dwc3/otg.c b/drivers/usb/dwc3/otg.c
index 708ab22..99dfd2c 100644
--- a/drivers/usb/dwc3/otg.c
+++ b/drivers/usb/dwc3/otg.c
@@ -29,6 +29,11 @@
 #include "otg.h"
 #include "io.h"
 
+static struct dwc3_ext_otg_ops *dwc3_otg_rsw_probe(struct dwc3 *dwc)
+{
+	return NULL;
+}
+
 static void dwc3_otg_set_host_mode(struct dwc3_otg *dotg)
 {
 	struct dwc3	*dwc = dotg->dwc;
@@ -388,25 +393,33 @@ int dwc3_otg_start(struct dwc3 *dwc)
 	if (!dotg)
 		return -ENODEV;
 
-	dotg->regs = dwc->regs;
+	if (dotg->ext_otg_ops) {
+		ret = dwc3_ext_otg_start(dotg);
+		if (ret) {
+			dev_err(dwc->dev, "failed to start external OTG\n");
+			return ret;
+		}
+	} else {
+		dotg->regs = dwc->regs;
 
-	dwc3_otg_reset(dotg);
+		dwc3_otg_reset(dotg);
 
-	dotg->fsm.id = dwc3_otg_get_id_state(dotg);
-	dotg->fsm.b_sess_vld = dwc3_otg_get_b_sess_state(dotg);
+		dotg->fsm.id = dwc3_otg_get_id_state(dotg);
+		dotg->fsm.b_sess_vld = dwc3_otg_get_b_sess_state(dotg);
 
-	dotg->irq = platform_get_irq(to_platform_device(dwc->dev), 0);
-	ret = devm_request_threaded_irq(dwc->dev, dotg->irq,
-			dwc3_otg_interrupt,
-			dwc3_otg_thread_interrupt,
-			IRQF_SHARED, "dwc3-otg", dotg);
-	if (ret) {
-		dev_err(dwc->dev, "failed to request irq #%d --> %d\n",
-				dotg->irq, ret);
-		return ret;
-	}
+		dotg->irq = platform_get_irq(to_platform_device(dwc->dev), 0);
+		ret = devm_request_threaded_irq(dwc->dev, dotg->irq,
+				dwc3_otg_interrupt,
+				dwc3_otg_thread_interrupt,
+				IRQF_SHARED, "dwc3-otg", dotg);
+		if (ret) {
+			dev_err(dwc->dev, "failed to request irq #%d --> %d\n",
+					dotg->irq, ret);
+			return ret;
+		}
 
-	dwc3_otg_enable_irq(dotg);
+		dwc3_otg_enable_irq(dotg);
+	}
 
 	dwc3_otg_run_sm(fsm);
 
@@ -424,8 +437,12 @@ void dwc3_otg_stop(struct dwc3 *dwc)
 	if (!dotg)
 		return;
 
-	dwc3_otg_disable_irq(dotg);
-	free_irq(dotg->irq, dotg);
+	if (dotg->ext_otg_ops) {
+		dwc3_ext_otg_stop(dotg);
+	} else {
+		dwc3_otg_disable_irq(dotg);
+		free_irq(dotg->irq, dotg);
+	}
 }
 
 /**
@@ -437,6 +454,7 @@ void dwc3_otg_stop(struct dwc3 *dwc)
 int dwc3_otg_init(struct dwc3 *dwc)
 {
 	struct dwc3_otg		*dotg;
+	struct dwc3_ext_otg_ops	*ops = NULL;
 	u32			reg;
 	int			ret = 0;
 
@@ -447,9 +465,25 @@ int dwc3_otg_init(struct dwc3 *dwc)
 	reg = dwc3_readl(dwc->regs, DWC3_GHWPARAMS6);
 	if (!(reg & DWC3_GHWPARAMS6_SRP_SUPPORT)) {
 		dev_err(dwc->dev, "dwc3_otg address space is not supported\n");
+
+		/*
+		 * Some SoCs (e.g. Exynos5) don't have HW OTG, however, some
+		 * boards use simplified role switch (rsw) function based on
+		 * ID/BSes gpio interrupts. As a fall-back try to bind to rsw.
+		 */
+		ops = dwc3_otg_rsw_probe(dwc);
+		if (ops)
+			goto has_ext_otg;
+
+		/*
+		 * No HW OTG support in the core.
+		 * We return 0 to indicate no error, since this is acceptable
+		 * situation, just continue probe the dwc3 driver without otg.
+		 */
 		return 0;
 	}
 
+has_ext_otg:
 	/* Allocate and init otg instance */
 	dotg = devm_kzalloc(dwc->dev, sizeof(struct dwc3_otg), GFP_KERNEL);
 	if (!dotg)
@@ -459,6 +493,8 @@ int dwc3_otg_init(struct dwc3 *dwc)
 	dwc->dotg = dotg;
 	dotg->dwc = dwc;
 
+	dotg->ext_otg_ops = ops;
+
 	dotg->otg.set_peripheral = dwc3_otg_set_peripheral;
 	dotg->otg.set_host = NULL;
 
@@ -472,6 +508,14 @@ int dwc3_otg_init(struct dwc3 *dwc)
 	if (IS_ERR(dotg->vbus_reg))
 		dev_info(dwc->dev, "vbus regulator is not available\n");
 
+	if (dotg->ext_otg_ops) {
+		ret = dwc3_ext_otg_setup(dotg);
+		if (ret) {
+			dev_err(dwc->dev, "failed to setup external OTG\n");
+			return ret;
+		}
+	}
+
 	ret = sysfs_create_group(&dwc->dev->kobj, &dwc3_otg_attr_group);
 	if (ret)
 		dev_err(dwc->dev, "failed to create dwc3 otg attributes\n");
@@ -491,6 +535,17 @@ void dwc3_otg_exit(struct dwc3 *dwc)
 	if (!dotg)
 		return;
 
+	reg = dwc3_readl(dwc->regs, DWC3_GHWPARAMS6);
+	if (!(reg & DWC3_GHWPARAMS6_SRP_SUPPORT)) {
+		if (dotg->ext_otg_ops) {
+			dwc3_ext_otg_exit(dotg);
+			goto has_ext_otg;
+		}
+
+		return;
+	}
+
+has_ext_otg:
 	sysfs_remove_group(&dwc->dev->kobj, &dwc3_otg_attr_group);
 	kfree(dotg);
 	dwc->dotg = NULL;
diff --git a/drivers/usb/dwc3/otg.h b/drivers/usb/dwc3/otg.h
index ffb8f0e..4be7165 100644
--- a/drivers/usb/dwc3/otg.h
+++ b/drivers/usb/dwc3/otg.h
@@ -26,6 +26,13 @@
 
 #include "core.h"
 
+struct dwc3_ext_otg_ops {
+	int	(*setup)(struct device *dev, struct otg_fsm *fsm);
+	void	(*exit)(struct device *dev);
+	int	(*start)(struct device *dev);
+	void	(*stop)(struct device *dev);
+};
+
 /**
  * struct dwc3_otg: OTG driver data. Shared by HCD and DCD.
  * @otg: USB OTG Transceiver structure.
@@ -34,6 +41,7 @@
  * @irq: IRQ number assigned for HSUSB controller.
  * @regs: ioremapped register base address.
  * @vbus_reg: Vbus regulator.
+ * @ext_otg_ops: external OTG engine ops.
  */
 struct dwc3_otg {
 	struct usb_otg          otg;
@@ -43,8 +51,48 @@ struct dwc3_otg {
 	void __iomem            *regs;
 
 	struct regulator	*vbus_reg;
+
+	struct dwc3_ext_otg_ops	*ext_otg_ops;
 };
 
+static inline int dwc3_ext_otg_setup(struct dwc3_otg *dotg)
+{
+	struct device *dev = dotg->dwc->dev->parent;
+
+	if (!dotg->ext_otg_ops->setup)
+		return -EOPNOTSUPP;
+	return dotg->ext_otg_ops->setup(dev, &dotg->fsm);
+}
+
+static inline int dwc3_ext_otg_exit(struct dwc3_otg *dotg)
+{
+	struct device *dev = dotg->dwc->dev->parent;
+
+	if (!dotg->ext_otg_ops->exit)
+		return -EOPNOTSUPP;
+	dotg->ext_otg_ops->exit(dev);
+	return 0;
+}
+
+static inline int dwc3_ext_otg_start(struct dwc3_otg *dotg)
+{
+	struct device *dev = dotg->dwc->dev->parent;
+
+	if (!dotg->ext_otg_ops->start)
+		return -EOPNOTSUPP;
+	return dotg->ext_otg_ops->start(dev);
+}
+
+static inline int dwc3_ext_otg_stop(struct dwc3_otg *dotg)
+{
+	struct device *dev = dotg->dwc->dev->parent;
+
+	if (!dotg->ext_otg_ops->stop)
+		return -EOPNOTSUPP;
+	dotg->ext_otg_ops->stop(dev);
+	return 0;
+}
+
 int dwc3_otg_start(struct dwc3 *dwc);
 void dwc3_otg_stop(struct dwc3 *dwc);
 int dwc3_otg_init(struct dwc3 *dwc);
-- 
1.9.1

--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@...r.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Please read the FAQ at  http://www.tux.org/lkml/

Powered by blists - more mailing lists

Powered by Openwall GNU/*/Linux Powered by OpenVZ