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-17-git-send-email-r.baldyga@samsung.com>
Date:	Wed, 18 Mar 2015 15:04:21 +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 16/19] dwc3: exynos: add software role switching code

Exynos platform doesn't have hardware OTG support in DWC3 block, so we
need to supply custom mechanism of notification about cable change. For
this purpose we use extcon framework - it would allow to use this code
with each Exynos board equipped with cable detecting hardware which has
its extcon driver.

Signed-off-by: Robert Baldyga <r.baldyga@...sung.com>
---
 .../devicetree/bindings/usb/exynos-usb.txt         |   4 +
 drivers/usb/dwc3/dwc3-exynos.c                     | 162 +++++++++++++++++++++
 drivers/usb/dwc3/otg.c                             |  25 ++++
 drivers/usb/dwc3/otg.h                             |   9 ++
 4 files changed, 200 insertions(+)

diff --git a/Documentation/devicetree/bindings/usb/exynos-usb.txt b/Documentation/devicetree/bindings/usb/exynos-usb.txt
index 9b4dbe3..34b049f 100644
--- a/Documentation/devicetree/bindings/usb/exynos-usb.txt
+++ b/Documentation/devicetree/bindings/usb/exynos-usb.txt
@@ -93,6 +93,10 @@ Required properties:
  - clocks: Clock IDs array as required by the controller.
  - clock-names: names of clocks correseponding to IDs in the clock property
 
+Optional properties:
+ - extcon: Phandle to extcon device which notifies about USB cable changes.
+	   It's used in OTG mode.
+
 Sub-nodes:
 The dwc3 core should be added as subnode to Exynos dwc3 glue.
 - dwc3 :
diff --git a/drivers/usb/dwc3/dwc3-exynos.c b/drivers/usb/dwc3/dwc3-exynos.c
index 7bd0a95..30a0560 100644
--- a/drivers/usb/dwc3/dwc3-exynos.c
+++ b/drivers/usb/dwc3/dwc3-exynos.c
@@ -27,6 +27,10 @@
 #include <linux/of.h>
 #include <linux/of_platform.h>
 #include <linux/regulator/consumer.h>
+#include <linux/extcon.h>
+#include <linux/workqueue.h>
+
+#include "otg.h"
 
 struct dwc3_exynos {
 	struct platform_device	*usb2_phy;
@@ -39,8 +43,107 @@ struct dwc3_exynos {
 
 	struct regulator	*vdd33;
 	struct regulator	*vdd10;
+
+	struct otg_fsm          *fsm;
+
+	struct extcon_dev       *extcon;
+	struct extcon_specific_cable_nb extcon_usb_dev;
+	struct extcon_specific_cable_nb extcon_usb_host_dev;
+	struct notifier_block   usb_nb;
+	struct notifier_block   usb_host_nb;
+	struct work_struct	work;
 };
 
+static int dwc3_exynos_usb_notifier(struct notifier_block *nb,
+		unsigned long event, void *ptr);
+
+static int dwc3_exynos_usb_host_notifier(struct notifier_block *nb,
+		unsigned long event, void *ptr);
+
+int dwc3_exynos_rsw_start(struct device *dev)
+{
+	struct dwc3_exynos *exynos = dev_get_drvdata(dev);
+	int ret;
+
+	dev_dbg(dev, "%s\n", __func__);
+
+	if (!exynos->extcon)
+		return -ENODEV;
+
+	exynos->usb_nb.notifier_call = dwc3_exynos_usb_notifier;
+	exynos->usb_host_nb.notifier_call = dwc3_exynos_usb_host_notifier;
+
+	ret = extcon_register_interest(&exynos->extcon_usb_dev,
+			exynos->extcon->name, "USB", &exynos->usb_nb);
+	if (ret < 0) {
+		dev_dbg(dev, "failed to register notifier for USB");
+		return -ENODEV;
+	}
+
+	ret = extcon_register_interest(&exynos->extcon_usb_host_dev,
+			exynos->extcon->name, "USB-HOST", &exynos->usb_host_nb);
+	if (ret < 0) {
+		dev_dbg(dev, "failed to register notifier for USB HOST");
+		extcon_unregister_interest(&exynos->extcon_usb_dev);
+		return -ENODEV;
+	}
+
+	if (extcon_get_cable_state(exynos->extcon, "USB")) {
+		exynos->fsm->b_sess_vld = 1;
+		exynos->fsm->id = 1;
+	} else if (extcon_get_cable_state(exynos->extcon, "USB-HOST")) {
+		exynos->fsm->b_sess_vld = 0;
+		exynos->fsm->id = 0;
+	} else {
+		exynos->fsm->b_sess_vld = 0;
+		exynos->fsm->id = 1;
+	}
+
+	dwc3_otg_run_sm(exynos->fsm);
+
+	return 0;
+}
+
+void dwc3_exynos_rsw_stop(struct device *dev)
+{
+	struct dwc3_exynos *exynos = dev_get_drvdata(dev);
+
+	dev_dbg(dev, "%s\n", __func__);
+
+	if (exynos->extcon_usb_dev.edev)
+		extcon_unregister_interest(&exynos->extcon_usb_dev);
+	if (exynos->extcon_usb_host_dev.edev)
+		extcon_unregister_interest(&exynos->extcon_usb_host_dev);
+}
+
+int dwc3_exynos_rsw_setup(struct device *dev, struct otg_fsm *fsm)
+{
+	struct dwc3_exynos *exynos = dev_get_drvdata(dev);
+
+	dev_dbg(dev, "%s\n", __func__);
+
+	exynos->fsm = fsm;
+
+	return 0;
+}
+
+void dwc3_exynos_rsw_exit(struct device *dev)
+{
+	struct dwc3_exynos	*exynos = dev_get_drvdata(dev);
+
+	dev_dbg(dev, "%s\n", __func__);
+
+	exynos->fsm = NULL;
+}
+
+bool dwc3_exynos_rsw_available(struct device *dev)
+{
+	if (of_property_read_bool(dev->of_node, "extcon"))
+		return true;
+
+	return false;
+}
+
 static int dwc3_exynos_register_phys(struct dwc3_exynos *exynos)
 {
 	struct usb_phy_generic_platform_data pdata;
@@ -105,18 +208,77 @@ static int dwc3_exynos_remove_child(struct device *dev, void *unused)
 	return 0;
 }
 
+static void dwc3_exynos_worker(struct work_struct *work)
+{
+	struct dwc3_exynos *exynos =
+		container_of(work, struct dwc3_exynos, work);
+
+	dwc3_otg_run_sm(exynos->fsm);
+}
+
+static int dwc3_exynos_usb_notifier(struct notifier_block *nb,
+		unsigned long event, void *ptr)
+{
+	struct dwc3_exynos *exynos =
+		container_of(nb, struct dwc3_exynos, usb_nb);
+
+	if (event) {
+		exynos->fsm->b_sess_vld = 1;
+		exynos->fsm->id = 1;
+	} else {
+		exynos->fsm->b_sess_vld = 0;
+		exynos->fsm->id = 1;
+	}
+
+	schedule_work(&exynos->work);
+
+	return 0;
+}
+
+static int dwc3_exynos_usb_host_notifier(struct notifier_block *nb,
+		unsigned long event, void *ptr)
+{
+	struct dwc3_exynos *exynos =
+		container_of(nb, struct dwc3_exynos, usb_host_nb);
+
+	if (event) {
+		exynos->fsm->b_sess_vld = 0;
+		exynos->fsm->id = 0;
+	} else {
+		exynos->fsm->b_sess_vld = 0;
+		exynos->fsm->id = 1;
+	}
+
+	schedule_work(&exynos->work);
+
+	return 0;
+}
+
 static int dwc3_exynos_probe(struct platform_device *pdev)
 {
 	struct dwc3_exynos	*exynos;
 	struct device		*dev = &pdev->dev;
 	struct device_node	*node = dev->of_node;
+	struct extcon_dev       *extcon = NULL;
 
 	int			ret;
 
+	if (of_property_read_bool(node, "extcon")) {
+		extcon = extcon_get_edev_by_phandle(dev, 0);
+		if (IS_ERR(extcon)) {
+			dev_vdbg(dev, "couldn't get extcon device\n");
+			return -EPROBE_DEFER;
+		}
+	}
+
 	exynos = devm_kzalloc(dev, sizeof(*exynos), GFP_KERNEL);
 	if (!exynos)
 		return -ENOMEM;
 
+	exynos->extcon = extcon;
+
+	INIT_WORK(&exynos->work, dwc3_exynos_worker);
+
 	/*
 	 * Right now device-tree probed devices don't get dma_mask set.
 	 * Since shared usb code relies on it, set it here for now.
diff --git a/drivers/usb/dwc3/otg.c b/drivers/usb/dwc3/otg.c
index 99dfd2c..507bade 100644
--- a/drivers/usb/dwc3/otg.c
+++ b/drivers/usb/dwc3/otg.c
@@ -29,10 +29,35 @@
 #include "otg.h"
 #include "io.h"
 
+#if IS_ENABLED(CONFIG_USB_DWC3_EXYNOS)
+static struct dwc3_ext_otg_ops *dwc3_otg_rsw_probe(struct dwc3 *dwc)
+{
+	struct dwc3_ext_otg_ops *ops;
+	bool                    ext_otg;
+
+	ext_otg = dwc3_exynos_rsw_available(dwc->dev->parent);
+	if (!ext_otg)
+		return NULL;
+
+	/* Allocate and init otg instance */
+	ops = devm_kzalloc(dwc->dev, sizeof(struct dwc3_ext_otg_ops),
+			GFP_KERNEL);
+	if (!ops)
+		return NULL;
+
+	ops->setup = dwc3_exynos_rsw_setup;
+	ops->exit = dwc3_exynos_rsw_exit;
+	ops->start = dwc3_exynos_rsw_start;
+	ops->stop = dwc3_exynos_rsw_stop;
+
+	return ops;
+}
+#else
 static struct dwc3_ext_otg_ops *dwc3_otg_rsw_probe(struct dwc3 *dwc)
 {
 	return NULL;
 }
+#endif
 
 static void dwc3_otg_set_host_mode(struct dwc3_otg *dotg)
 {
diff --git a/drivers/usb/dwc3/otg.h b/drivers/usb/dwc3/otg.h
index 4be7165..efed700 100644
--- a/drivers/usb/dwc3/otg.h
+++ b/drivers/usb/dwc3/otg.h
@@ -101,4 +101,13 @@ void dwc3_otg_exit(struct dwc3 *dwc);
 
 void dwc3_otg_run_sm(struct otg_fsm *fsm);
 
+/* prototypes */
+#if IS_ENABLED(CONFIG_USB_DWC3_EXYNOS)
+bool dwc3_exynos_rsw_available(struct device *dev);
+int dwc3_exynos_rsw_setup(struct device *dev, struct otg_fsm *fsm);
+void dwc3_exynos_rsw_exit(struct device *dev);
+int dwc3_exynos_rsw_start(struct device *dev);
+void dwc3_exynos_rsw_stop(struct device *dev);
+#endif
+
 #endif /* __LINUX_USB_DWC3_OTG_H */
-- 
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