All of lore.kernel.org
 help / color / mirror / Atom feed
From: Chunfeng Yun <chunfeng.yun@mediatek.com>
To: Greg Kroah-Hartman <gregkh@linuxfoundation.org>,
	Felipe Balbi <felipe.balbi@linux.intel.com>,
	Rob Herring <robh+dt@kernel.org>
Cc: Mathias Nyman <mathias.nyman@intel.com>,
	Matthias Brugger <matthias.bgg@gmail.com>,
	Mark Rutland <mark.rutland@arm.com>,
	Ian Campbell <ijc+devicetree@hellion.org.uk>,
	Chunfeng Yun <chunfeng.yun@mediatek.com>,
	<linux-kernel@vger.kernel.org>,
	<linux-arm-kernel@lists.infradead.org>,
	<linux-usb@vger.kernel.org>, <linux-mediatek@lists.infradead.org>,
	<devicetree@vger.kernel.org>
Subject: [PATCH 06/12] usb: mtu3: use FORCE/RG_IDDIG to implement manual DRD switch
Date: Wed, 27 Sep 2017 18:58:35 +0800	[thread overview]
Message-ID: <bfe4b110c3128498d9c36cd49c8cfb4370f43891.1506509658.git.chunfeng.yun@mediatek.com> (raw)
In-Reply-To: <fe8df8503f602586bcea4b1598eb09678b1525e3.1506509658.git.chunfeng.yun@mediatek.com>

In order to keep manual DRD switch independent on IDDIG interrupt,
make use of FORCE/RG_IDDIG instead of IDDIG EINT interrupt to
implement manual DRD switch function.

Signed-off-by: Chunfeng Yun <chunfeng.yun@mediatek.com>
---
 drivers/usb/mtu3/mtu3.h         |   18 ++++++++----
 drivers/usb/mtu3/mtu3_dr.c      |   61 +++++++++++++++++++++++++++++----------
 drivers/usb/mtu3/mtu3_dr.h      |    6 ++++
 drivers/usb/mtu3/mtu3_host.c    |    5 ++++
 drivers/usb/mtu3/mtu3_hw_regs.h |    2 ++
 drivers/usb/mtu3/mtu3_plat.c    |   38 ++----------------------
 6 files changed, 74 insertions(+), 56 deletions(-)

diff --git a/drivers/usb/mtu3/mtu3.h b/drivers/usb/mtu3/mtu3.h
index ef2dc92..b0c2b5d 100644
--- a/drivers/usb/mtu3/mtu3.h
+++ b/drivers/usb/mtu3/mtu3.h
@@ -115,6 +115,19 @@ enum mtu3_g_ep0_state {
 };
 
 /**
+ * MTU3_DR_FORCE_NONE: automatically switch host and periperal mode
+ *		by IDPIN signal.
+ * MTU3_DR_FORCE_HOST: force to enter host mode and override OTG
+ *		IDPIN signal.
+ * MTU3_DR_FORCE_DEVICE: force to enter peripheral mode.
+ */
+enum mtu3_dr_force_mode {
+	MTU3_DR_FORCE_NONE = 0,
+	MTU3_DR_FORCE_HOST,
+	MTU3_DR_FORCE_DEVICE,
+};
+
+/**
  * @base: the base address of fifo
  * @limit: the bitmap size in bits
  * @bitmap: fifo bitmap in unit of @MTU3_EP_FIFO_UNIT
@@ -196,7 +209,6 @@ struct mtu3_gpd_ring {
 *		xHCI driver initialization, it's necessary for system bootup
 *		as device.
 * @is_u3_drd: whether port0 supports usb3.0 dual-role device or not
-* @id_*: used to maually switch between host and device modes by idpin
 * @manual_drd_enabled: it's true when supports dual-role device by debugfs
 *		to switch host/device modes depending on user input.
 */
@@ -207,10 +219,6 @@ struct otg_switch_mtk {
 	struct notifier_block id_nb;
 	struct delayed_work extcon_reg_dwork;
 	bool is_u3_drd;
-	/* dual-role switch by debugfs */
-	struct pinctrl *id_pinctrl;
-	struct pinctrl_state *id_float;
-	struct pinctrl_state *id_ground;
 	bool manual_drd_enabled;
 };
 
diff --git a/drivers/usb/mtu3/mtu3_dr.c b/drivers/usb/mtu3/mtu3_dr.c
index 5602561..ec442cd 100644
--- a/drivers/usb/mtu3/mtu3_dr.c
+++ b/drivers/usb/mtu3/mtu3_dr.c
@@ -261,21 +261,22 @@ static void extcon_register_dwork(struct work_struct *work)
  * depending on user input.
  * This is useful in special cases, such as uses TYPE-A receptacle but also
  * wants to support dual-role mode.
- * It generates cable state changes by pulling up/down IDPIN and
- * notifies driver to switch mode by "extcon-usb-gpio".
- * NOTE: when use MICRO receptacle, should not enable this interface.
  */
 static void ssusb_mode_manual_switch(struct ssusb_mtk *ssusb, int to_host)
 {
 	struct otg_switch_mtk *otg_sx = &ssusb->otg_switch;
 
-	if (to_host)
-		pinctrl_select_state(otg_sx->id_pinctrl, otg_sx->id_ground);
-	else
-		pinctrl_select_state(otg_sx->id_pinctrl, otg_sx->id_float);
+	if (to_host) {
+		ssusb_set_force_mode(ssusb, MTU3_DR_FORCE_HOST);
+		ssusb_set_mailbox(otg_sx, MTU3_VBUS_OFF);
+		ssusb_set_mailbox(otg_sx, MTU3_ID_GROUND);
+	} else {
+		ssusb_set_force_mode(ssusb, MTU3_DR_FORCE_DEVICE);
+		ssusb_set_mailbox(otg_sx, MTU3_ID_FLOAT);
+		ssusb_set_mailbox(otg_sx, MTU3_VBUS_VALID);
+	}
 }
 
-
 static int ssusb_mode_show(struct seq_file *sf, void *unused)
 {
 	struct ssusb_mtk *ssusb = sf->private;
@@ -388,17 +389,45 @@ static void ssusb_debugfs_exit(struct ssusb_mtk *ssusb)
 	debugfs_remove_recursive(ssusb->dbgfs_root);
 }
 
+void ssusb_set_force_mode(struct ssusb_mtk *ssusb,
+			  enum mtu3_dr_force_mode mode)
+{
+	u32 value;
+
+	value = mtu3_readl(ssusb->ippc_base, SSUSB_U2_CTRL(0));
+	switch (mode) {
+	case MTU3_DR_FORCE_DEVICE:
+		value |= SSUSB_U2_PORT_FORCE_IDDIG | SSUSB_U2_PORT_RG_IDDIG;
+		break;
+	case MTU3_DR_FORCE_HOST:
+		value |= SSUSB_U2_PORT_FORCE_IDDIG;
+		value &= ~SSUSB_U2_PORT_RG_IDDIG;
+		break;
+	case MTU3_DR_FORCE_NONE:
+		value &= ~(SSUSB_U2_PORT_FORCE_IDDIG | SSUSB_U2_PORT_RG_IDDIG);
+		break;
+	default:
+		return;
+	}
+	mtu3_writel(ssusb->ippc_base, SSUSB_U2_CTRL(0), value);
+}
+
 int ssusb_otg_switch_init(struct ssusb_mtk *ssusb)
 {
 	struct otg_switch_mtk *otg_sx = &ssusb->otg_switch;
 
-	INIT_DELAYED_WORK(&otg_sx->extcon_reg_dwork, extcon_register_dwork);
-
-	if (otg_sx->manual_drd_enabled)
+	if (otg_sx->manual_drd_enabled) {
 		ssusb_debugfs_init(ssusb);
-
-	/* It is enough to delay 1s for waiting for host initialization */
-	schedule_delayed_work(&otg_sx->extcon_reg_dwork, HZ);
+	} else {
+		INIT_DELAYED_WORK(&otg_sx->extcon_reg_dwork,
+				  extcon_register_dwork);
+
+		/*
+		 * It is enough to delay 1s for waiting for
+		 * host initialization
+		 */
+		schedule_delayed_work(&otg_sx->extcon_reg_dwork, HZ);
+	}
 
 	return 0;
 }
@@ -407,8 +436,8 @@ void ssusb_otg_switch_exit(struct ssusb_mtk *ssusb)
 {
 	struct otg_switch_mtk *otg_sx = &ssusb->otg_switch;
 
-	cancel_delayed_work(&otg_sx->extcon_reg_dwork);
-
 	if (otg_sx->manual_drd_enabled)
 		ssusb_debugfs_exit(ssusb);
+	else
+		cancel_delayed_work(&otg_sx->extcon_reg_dwork);
 }
diff --git a/drivers/usb/mtu3/mtu3_dr.h b/drivers/usb/mtu3/mtu3_dr.h
index 9b228b5..0f0cbac 100644
--- a/drivers/usb/mtu3/mtu3_dr.h
+++ b/drivers/usb/mtu3/mtu3_dr.h
@@ -87,6 +87,8 @@ static inline void ssusb_gadget_exit(struct ssusb_mtk *ssusb)
 int ssusb_otg_switch_init(struct ssusb_mtk *ssusb);
 void ssusb_otg_switch_exit(struct ssusb_mtk *ssusb);
 int ssusb_set_vbus(struct otg_switch_mtk *otg_sx, int is_on);
+void ssusb_set_force_mode(struct ssusb_mtk *ssusb,
+			  enum mtu3_dr_force_mode mode);
 
 #else
 
@@ -103,6 +105,10 @@ static inline int ssusb_set_vbus(struct otg_switch_mtk *otg_sx, int is_on)
 	return 0;
 }
 
+static inline void
+ssusb_set_force_mode(struct ssusb_mtk *ssusb, enum mtu3_dr_force_mode mode)
+{}
+
 #endif
 
 #endif		/* _MTU3_DR_H_ */
diff --git a/drivers/usb/mtu3/mtu3_host.c b/drivers/usb/mtu3/mtu3_host.c
index edcc591..ec76b86 100644
--- a/drivers/usb/mtu3/mtu3_host.c
+++ b/drivers/usb/mtu3/mtu3_host.c
@@ -189,6 +189,8 @@ int ssusb_host_disable(struct ssusb_mtk *ssusb, bool suspend)
 
 static void ssusb_host_setup(struct ssusb_mtk *ssusb)
 {
+	struct otg_switch_mtk *otg_sx = &ssusb->otg_switch;
+
 	host_ports_num_get(ssusb);
 
 	/*
@@ -197,6 +199,9 @@ static void ssusb_host_setup(struct ssusb_mtk *ssusb)
 	 */
 	ssusb_host_enable(ssusb);
 
+	if (otg_sx->manual_drd_enabled)
+		ssusb_set_force_mode(ssusb, MTU3_DR_FORCE_HOST);
+
 	/* if port0 supports dual-role, works as host mode by default */
 	ssusb_set_vbus(&ssusb->otg_switch, 1);
 }
diff --git a/drivers/usb/mtu3/mtu3_hw_regs.h b/drivers/usb/mtu3/mtu3_hw_regs.h
index b605975..a7e35f6 100644
--- a/drivers/usb/mtu3/mtu3_hw_regs.h
+++ b/drivers/usb/mtu3/mtu3_hw_regs.h
@@ -472,6 +472,8 @@
 #define SSUSB_U3_PORT_DIS		BIT(0)
 
 /* U3D_SSUSB_U2_CTRL_0P */
+#define SSUSB_U2_PORT_RG_IDDIG		BIT(12)
+#define SSUSB_U2_PORT_FORCE_IDDIG	BIT(11)
 #define SSUSB_U2_PORT_VBUSVALID	BIT(9)
 #define SSUSB_U2_PORT_OTG_SEL		BIT(7)
 #define SSUSB_U2_PORT_HOST		BIT(2)
diff --git a/drivers/usb/mtu3/mtu3_plat.c b/drivers/usb/mtu3/mtu3_plat.c
index fb89920..1e473b0 100644
--- a/drivers/usb/mtu3/mtu3_plat.c
+++ b/drivers/usb/mtu3/mtu3_plat.c
@@ -21,7 +21,6 @@
 #include <linux/module.h>
 #include <linux/of_address.h>
 #include <linux/of_irq.h>
-#include <linux/pinctrl/consumer.h>
 #include <linux/platform_device.h>
 
 #include "mtu3.h"
@@ -212,33 +211,6 @@ static void ssusb_ip_sw_reset(struct ssusb_mtk *ssusb)
 	mtu3_clrbits(ssusb->ippc_base, U3D_SSUSB_IP_PW_CTRL0, SSUSB_IP_SW_RST);
 }
 
-static int get_iddig_pinctrl(struct ssusb_mtk *ssusb)
-{
-	struct otg_switch_mtk *otg_sx = &ssusb->otg_switch;
-
-	otg_sx->id_pinctrl = devm_pinctrl_get(ssusb->dev);
-	if (IS_ERR(otg_sx->id_pinctrl)) {
-		dev_err(ssusb->dev, "Cannot find id pinctrl!\n");
-		return PTR_ERR(otg_sx->id_pinctrl);
-	}
-
-	otg_sx->id_float =
-		pinctrl_lookup_state(otg_sx->id_pinctrl, "id_float");
-	if (IS_ERR(otg_sx->id_float)) {
-		dev_err(ssusb->dev, "Cannot find pinctrl id_float!\n");
-		return PTR_ERR(otg_sx->id_float);
-	}
-
-	otg_sx->id_ground =
-		pinctrl_lookup_state(otg_sx->id_pinctrl, "id_ground");
-	if (IS_ERR(otg_sx->id_ground)) {
-		dev_err(ssusb->dev, "Cannot find pinctrl id_ground!\n");
-		return PTR_ERR(otg_sx->id_ground);
-	}
-
-	return 0;
-}
-
 /* ignore the error if the clock does not exist */
 static struct clk *get_optional_clk(struct device *dev, const char *id)
 {
@@ -349,15 +321,11 @@ static int get_ssusb_rscs(struct platform_device *pdev, struct ssusb_mtk *ssusb)
 			dev_err(ssusb->dev, "couldn't get extcon device\n");
 			return -EPROBE_DEFER;
 		}
-		if (otg_sx->manual_drd_enabled) {
-			ret = get_iddig_pinctrl(ssusb);
-			if (ret)
-				return ret;
-		}
 	}
 
-	dev_info(dev, "dr_mode: %d, is_u3_dr: %d, u3p_dis_msk:%x\n",
-		ssusb->dr_mode, otg_sx->is_u3_drd, ssusb->u3p_dis_msk);
+	dev_info(dev, "dr_mode: %d, is_u3_dr: %d, u3p_dis_msk: %x, drd: %s\n",
+		ssusb->dr_mode, otg_sx->is_u3_drd, ssusb->u3p_dis_msk,
+		otg_sx->manual_drd_enabled ? "manual" : "auto");
 
 	return 0;
 }
-- 
1.7.9.5

WARNING: multiple messages have this Message-ID (diff)
From: Chunfeng Yun <chunfeng.yun@mediatek.com>
To: Greg Kroah-Hartman <gregkh@linuxfoundation.org>,
	Felipe Balbi <felipe.balbi@linux.intel.com>,
	Rob Herring <robh+dt@kernel.org>
Cc: Mathias Nyman <mathias.nyman@intel.com>,
	Matthias Brugger <matthias.bgg@gmail.com>,
	Mark Rutland <mark.rutland@arm.com>,
	Ian Campbell <ijc+devicetree@hellion.org.uk>,
	Chunfeng Yun <chunfeng.yun@mediatek.com>,
	linux-kernel@vger.kernel.org,
	linux-arm-kernel@lists.infradead.org, linux-usb@vger.kernel.org,
	linux-mediatek@lists.infradead.org, devicetree@vger.kernel.org
Subject: [PATCH 06/12] usb: mtu3: use FORCE/RG_IDDIG to implement manual DRD switch
Date: Wed, 27 Sep 2017 18:58:35 +0800	[thread overview]
Message-ID: <bfe4b110c3128498d9c36cd49c8cfb4370f43891.1506509658.git.chunfeng.yun@mediatek.com> (raw)
In-Reply-To: <fe8df8503f602586bcea4b1598eb09678b1525e3.1506509658.git.chunfeng.yun@mediatek.com>

In order to keep manual DRD switch independent on IDDIG interrupt,
make use of FORCE/RG_IDDIG instead of IDDIG EINT interrupt to
implement manual DRD switch function.

Signed-off-by: Chunfeng Yun <chunfeng.yun@mediatek.com>
---
 drivers/usb/mtu3/mtu3.h         |   18 ++++++++----
 drivers/usb/mtu3/mtu3_dr.c      |   61 +++++++++++++++++++++++++++++----------
 drivers/usb/mtu3/mtu3_dr.h      |    6 ++++
 drivers/usb/mtu3/mtu3_host.c    |    5 ++++
 drivers/usb/mtu3/mtu3_hw_regs.h |    2 ++
 drivers/usb/mtu3/mtu3_plat.c    |   38 ++----------------------
 6 files changed, 74 insertions(+), 56 deletions(-)

diff --git a/drivers/usb/mtu3/mtu3.h b/drivers/usb/mtu3/mtu3.h
index ef2dc92..b0c2b5d 100644
--- a/drivers/usb/mtu3/mtu3.h
+++ b/drivers/usb/mtu3/mtu3.h
@@ -115,6 +115,19 @@ enum mtu3_g_ep0_state {
 };
 
 /**
+ * MTU3_DR_FORCE_NONE: automatically switch host and periperal mode
+ *		by IDPIN signal.
+ * MTU3_DR_FORCE_HOST: force to enter host mode and override OTG
+ *		IDPIN signal.
+ * MTU3_DR_FORCE_DEVICE: force to enter peripheral mode.
+ */
+enum mtu3_dr_force_mode {
+	MTU3_DR_FORCE_NONE = 0,
+	MTU3_DR_FORCE_HOST,
+	MTU3_DR_FORCE_DEVICE,
+};
+
+/**
  * @base: the base address of fifo
  * @limit: the bitmap size in bits
  * @bitmap: fifo bitmap in unit of @MTU3_EP_FIFO_UNIT
@@ -196,7 +209,6 @@ struct mtu3_gpd_ring {
 *		xHCI driver initialization, it's necessary for system bootup
 *		as device.
 * @is_u3_drd: whether port0 supports usb3.0 dual-role device or not
-* @id_*: used to maually switch between host and device modes by idpin
 * @manual_drd_enabled: it's true when supports dual-role device by debugfs
 *		to switch host/device modes depending on user input.
 */
@@ -207,10 +219,6 @@ struct otg_switch_mtk {
 	struct notifier_block id_nb;
 	struct delayed_work extcon_reg_dwork;
 	bool is_u3_drd;
-	/* dual-role switch by debugfs */
-	struct pinctrl *id_pinctrl;
-	struct pinctrl_state *id_float;
-	struct pinctrl_state *id_ground;
 	bool manual_drd_enabled;
 };
 
diff --git a/drivers/usb/mtu3/mtu3_dr.c b/drivers/usb/mtu3/mtu3_dr.c
index 5602561..ec442cd 100644
--- a/drivers/usb/mtu3/mtu3_dr.c
+++ b/drivers/usb/mtu3/mtu3_dr.c
@@ -261,21 +261,22 @@ static void extcon_register_dwork(struct work_struct *work)
  * depending on user input.
  * This is useful in special cases, such as uses TYPE-A receptacle but also
  * wants to support dual-role mode.
- * It generates cable state changes by pulling up/down IDPIN and
- * notifies driver to switch mode by "extcon-usb-gpio".
- * NOTE: when use MICRO receptacle, should not enable this interface.
  */
 static void ssusb_mode_manual_switch(struct ssusb_mtk *ssusb, int to_host)
 {
 	struct otg_switch_mtk *otg_sx = &ssusb->otg_switch;
 
-	if (to_host)
-		pinctrl_select_state(otg_sx->id_pinctrl, otg_sx->id_ground);
-	else
-		pinctrl_select_state(otg_sx->id_pinctrl, otg_sx->id_float);
+	if (to_host) {
+		ssusb_set_force_mode(ssusb, MTU3_DR_FORCE_HOST);
+		ssusb_set_mailbox(otg_sx, MTU3_VBUS_OFF);
+		ssusb_set_mailbox(otg_sx, MTU3_ID_GROUND);
+	} else {
+		ssusb_set_force_mode(ssusb, MTU3_DR_FORCE_DEVICE);
+		ssusb_set_mailbox(otg_sx, MTU3_ID_FLOAT);
+		ssusb_set_mailbox(otg_sx, MTU3_VBUS_VALID);
+	}
 }
 
-
 static int ssusb_mode_show(struct seq_file *sf, void *unused)
 {
 	struct ssusb_mtk *ssusb = sf->private;
@@ -388,17 +389,45 @@ static void ssusb_debugfs_exit(struct ssusb_mtk *ssusb)
 	debugfs_remove_recursive(ssusb->dbgfs_root);
 }
 
+void ssusb_set_force_mode(struct ssusb_mtk *ssusb,
+			  enum mtu3_dr_force_mode mode)
+{
+	u32 value;
+
+	value = mtu3_readl(ssusb->ippc_base, SSUSB_U2_CTRL(0));
+	switch (mode) {
+	case MTU3_DR_FORCE_DEVICE:
+		value |= SSUSB_U2_PORT_FORCE_IDDIG | SSUSB_U2_PORT_RG_IDDIG;
+		break;
+	case MTU3_DR_FORCE_HOST:
+		value |= SSUSB_U2_PORT_FORCE_IDDIG;
+		value &= ~SSUSB_U2_PORT_RG_IDDIG;
+		break;
+	case MTU3_DR_FORCE_NONE:
+		value &= ~(SSUSB_U2_PORT_FORCE_IDDIG | SSUSB_U2_PORT_RG_IDDIG);
+		break;
+	default:
+		return;
+	}
+	mtu3_writel(ssusb->ippc_base, SSUSB_U2_CTRL(0), value);
+}
+
 int ssusb_otg_switch_init(struct ssusb_mtk *ssusb)
 {
 	struct otg_switch_mtk *otg_sx = &ssusb->otg_switch;
 
-	INIT_DELAYED_WORK(&otg_sx->extcon_reg_dwork, extcon_register_dwork);
-
-	if (otg_sx->manual_drd_enabled)
+	if (otg_sx->manual_drd_enabled) {
 		ssusb_debugfs_init(ssusb);
-
-	/* It is enough to delay 1s for waiting for host initialization */
-	schedule_delayed_work(&otg_sx->extcon_reg_dwork, HZ);
+	} else {
+		INIT_DELAYED_WORK(&otg_sx->extcon_reg_dwork,
+				  extcon_register_dwork);
+
+		/*
+		 * It is enough to delay 1s for waiting for
+		 * host initialization
+		 */
+		schedule_delayed_work(&otg_sx->extcon_reg_dwork, HZ);
+	}
 
 	return 0;
 }
@@ -407,8 +436,8 @@ void ssusb_otg_switch_exit(struct ssusb_mtk *ssusb)
 {
 	struct otg_switch_mtk *otg_sx = &ssusb->otg_switch;
 
-	cancel_delayed_work(&otg_sx->extcon_reg_dwork);
-
 	if (otg_sx->manual_drd_enabled)
 		ssusb_debugfs_exit(ssusb);
+	else
+		cancel_delayed_work(&otg_sx->extcon_reg_dwork);
 }
diff --git a/drivers/usb/mtu3/mtu3_dr.h b/drivers/usb/mtu3/mtu3_dr.h
index 9b228b5..0f0cbac 100644
--- a/drivers/usb/mtu3/mtu3_dr.h
+++ b/drivers/usb/mtu3/mtu3_dr.h
@@ -87,6 +87,8 @@ static inline void ssusb_gadget_exit(struct ssusb_mtk *ssusb)
 int ssusb_otg_switch_init(struct ssusb_mtk *ssusb);
 void ssusb_otg_switch_exit(struct ssusb_mtk *ssusb);
 int ssusb_set_vbus(struct otg_switch_mtk *otg_sx, int is_on);
+void ssusb_set_force_mode(struct ssusb_mtk *ssusb,
+			  enum mtu3_dr_force_mode mode);
 
 #else
 
@@ -103,6 +105,10 @@ static inline int ssusb_set_vbus(struct otg_switch_mtk *otg_sx, int is_on)
 	return 0;
 }
 
+static inline void
+ssusb_set_force_mode(struct ssusb_mtk *ssusb, enum mtu3_dr_force_mode mode)
+{}
+
 #endif
 
 #endif		/* _MTU3_DR_H_ */
diff --git a/drivers/usb/mtu3/mtu3_host.c b/drivers/usb/mtu3/mtu3_host.c
index edcc591..ec76b86 100644
--- a/drivers/usb/mtu3/mtu3_host.c
+++ b/drivers/usb/mtu3/mtu3_host.c
@@ -189,6 +189,8 @@ int ssusb_host_disable(struct ssusb_mtk *ssusb, bool suspend)
 
 static void ssusb_host_setup(struct ssusb_mtk *ssusb)
 {
+	struct otg_switch_mtk *otg_sx = &ssusb->otg_switch;
+
 	host_ports_num_get(ssusb);
 
 	/*
@@ -197,6 +199,9 @@ static void ssusb_host_setup(struct ssusb_mtk *ssusb)
 	 */
 	ssusb_host_enable(ssusb);
 
+	if (otg_sx->manual_drd_enabled)
+		ssusb_set_force_mode(ssusb, MTU3_DR_FORCE_HOST);
+
 	/* if port0 supports dual-role, works as host mode by default */
 	ssusb_set_vbus(&ssusb->otg_switch, 1);
 }
diff --git a/drivers/usb/mtu3/mtu3_hw_regs.h b/drivers/usb/mtu3/mtu3_hw_regs.h
index b605975..a7e35f6 100644
--- a/drivers/usb/mtu3/mtu3_hw_regs.h
+++ b/drivers/usb/mtu3/mtu3_hw_regs.h
@@ -472,6 +472,8 @@
 #define SSUSB_U3_PORT_DIS		BIT(0)
 
 /* U3D_SSUSB_U2_CTRL_0P */
+#define SSUSB_U2_PORT_RG_IDDIG		BIT(12)
+#define SSUSB_U2_PORT_FORCE_IDDIG	BIT(11)
 #define SSUSB_U2_PORT_VBUSVALID	BIT(9)
 #define SSUSB_U2_PORT_OTG_SEL		BIT(7)
 #define SSUSB_U2_PORT_HOST		BIT(2)
diff --git a/drivers/usb/mtu3/mtu3_plat.c b/drivers/usb/mtu3/mtu3_plat.c
index fb89920..1e473b0 100644
--- a/drivers/usb/mtu3/mtu3_plat.c
+++ b/drivers/usb/mtu3/mtu3_plat.c
@@ -21,7 +21,6 @@
 #include <linux/module.h>
 #include <linux/of_address.h>
 #include <linux/of_irq.h>
-#include <linux/pinctrl/consumer.h>
 #include <linux/platform_device.h>
 
 #include "mtu3.h"
@@ -212,33 +211,6 @@ static void ssusb_ip_sw_reset(struct ssusb_mtk *ssusb)
 	mtu3_clrbits(ssusb->ippc_base, U3D_SSUSB_IP_PW_CTRL0, SSUSB_IP_SW_RST);
 }
 
-static int get_iddig_pinctrl(struct ssusb_mtk *ssusb)
-{
-	struct otg_switch_mtk *otg_sx = &ssusb->otg_switch;
-
-	otg_sx->id_pinctrl = devm_pinctrl_get(ssusb->dev);
-	if (IS_ERR(otg_sx->id_pinctrl)) {
-		dev_err(ssusb->dev, "Cannot find id pinctrl!\n");
-		return PTR_ERR(otg_sx->id_pinctrl);
-	}
-
-	otg_sx->id_float =
-		pinctrl_lookup_state(otg_sx->id_pinctrl, "id_float");
-	if (IS_ERR(otg_sx->id_float)) {
-		dev_err(ssusb->dev, "Cannot find pinctrl id_float!\n");
-		return PTR_ERR(otg_sx->id_float);
-	}
-
-	otg_sx->id_ground =
-		pinctrl_lookup_state(otg_sx->id_pinctrl, "id_ground");
-	if (IS_ERR(otg_sx->id_ground)) {
-		dev_err(ssusb->dev, "Cannot find pinctrl id_ground!\n");
-		return PTR_ERR(otg_sx->id_ground);
-	}
-
-	return 0;
-}
-
 /* ignore the error if the clock does not exist */
 static struct clk *get_optional_clk(struct device *dev, const char *id)
 {
@@ -349,15 +321,11 @@ static int get_ssusb_rscs(struct platform_device *pdev, struct ssusb_mtk *ssusb)
 			dev_err(ssusb->dev, "couldn't get extcon device\n");
 			return -EPROBE_DEFER;
 		}
-		if (otg_sx->manual_drd_enabled) {
-			ret = get_iddig_pinctrl(ssusb);
-			if (ret)
-				return ret;
-		}
 	}
 
-	dev_info(dev, "dr_mode: %d, is_u3_dr: %d, u3p_dis_msk:%x\n",
-		ssusb->dr_mode, otg_sx->is_u3_drd, ssusb->u3p_dis_msk);
+	dev_info(dev, "dr_mode: %d, is_u3_dr: %d, u3p_dis_msk: %x, drd: %s\n",
+		ssusb->dr_mode, otg_sx->is_u3_drd, ssusb->u3p_dis_msk,
+		otg_sx->manual_drd_enabled ? "manual" : "auto");
 
 	return 0;
 }
-- 
1.7.9.5

WARNING: multiple messages have this Message-ID (diff)
From: chunfeng.yun@mediatek.com (Chunfeng Yun)
To: linux-arm-kernel@lists.infradead.org
Subject: [PATCH 06/12] usb: mtu3: use FORCE/RG_IDDIG to implement manual DRD switch
Date: Wed, 27 Sep 2017 18:58:35 +0800	[thread overview]
Message-ID: <bfe4b110c3128498d9c36cd49c8cfb4370f43891.1506509658.git.chunfeng.yun@mediatek.com> (raw)
In-Reply-To: <fe8df8503f602586bcea4b1598eb09678b1525e3.1506509658.git.chunfeng.yun@mediatek.com>

In order to keep manual DRD switch independent on IDDIG interrupt,
make use of FORCE/RG_IDDIG instead of IDDIG EINT interrupt to
implement manual DRD switch function.

Signed-off-by: Chunfeng Yun <chunfeng.yun@mediatek.com>
---
 drivers/usb/mtu3/mtu3.h         |   18 ++++++++----
 drivers/usb/mtu3/mtu3_dr.c      |   61 +++++++++++++++++++++++++++++----------
 drivers/usb/mtu3/mtu3_dr.h      |    6 ++++
 drivers/usb/mtu3/mtu3_host.c    |    5 ++++
 drivers/usb/mtu3/mtu3_hw_regs.h |    2 ++
 drivers/usb/mtu3/mtu3_plat.c    |   38 ++----------------------
 6 files changed, 74 insertions(+), 56 deletions(-)

diff --git a/drivers/usb/mtu3/mtu3.h b/drivers/usb/mtu3/mtu3.h
index ef2dc92..b0c2b5d 100644
--- a/drivers/usb/mtu3/mtu3.h
+++ b/drivers/usb/mtu3/mtu3.h
@@ -115,6 +115,19 @@ enum mtu3_g_ep0_state {
 };
 
 /**
+ * MTU3_DR_FORCE_NONE: automatically switch host and periperal mode
+ *		by IDPIN signal.
+ * MTU3_DR_FORCE_HOST: force to enter host mode and override OTG
+ *		IDPIN signal.
+ * MTU3_DR_FORCE_DEVICE: force to enter peripheral mode.
+ */
+enum mtu3_dr_force_mode {
+	MTU3_DR_FORCE_NONE = 0,
+	MTU3_DR_FORCE_HOST,
+	MTU3_DR_FORCE_DEVICE,
+};
+
+/**
  * @base: the base address of fifo
  * @limit: the bitmap size in bits
  * @bitmap: fifo bitmap in unit of @MTU3_EP_FIFO_UNIT
@@ -196,7 +209,6 @@ struct mtu3_gpd_ring {
 *		xHCI driver initialization, it's necessary for system bootup
 *		as device.
 * @is_u3_drd: whether port0 supports usb3.0 dual-role device or not
-* @id_*: used to maually switch between host and device modes by idpin
 * @manual_drd_enabled: it's true when supports dual-role device by debugfs
 *		to switch host/device modes depending on user input.
 */
@@ -207,10 +219,6 @@ struct otg_switch_mtk {
 	struct notifier_block id_nb;
 	struct delayed_work extcon_reg_dwork;
 	bool is_u3_drd;
-	/* dual-role switch by debugfs */
-	struct pinctrl *id_pinctrl;
-	struct pinctrl_state *id_float;
-	struct pinctrl_state *id_ground;
 	bool manual_drd_enabled;
 };
 
diff --git a/drivers/usb/mtu3/mtu3_dr.c b/drivers/usb/mtu3/mtu3_dr.c
index 5602561..ec442cd 100644
--- a/drivers/usb/mtu3/mtu3_dr.c
+++ b/drivers/usb/mtu3/mtu3_dr.c
@@ -261,21 +261,22 @@ static void extcon_register_dwork(struct work_struct *work)
  * depending on user input.
  * This is useful in special cases, such as uses TYPE-A receptacle but also
  * wants to support dual-role mode.
- * It generates cable state changes by pulling up/down IDPIN and
- * notifies driver to switch mode by "extcon-usb-gpio".
- * NOTE: when use MICRO receptacle, should not enable this interface.
  */
 static void ssusb_mode_manual_switch(struct ssusb_mtk *ssusb, int to_host)
 {
 	struct otg_switch_mtk *otg_sx = &ssusb->otg_switch;
 
-	if (to_host)
-		pinctrl_select_state(otg_sx->id_pinctrl, otg_sx->id_ground);
-	else
-		pinctrl_select_state(otg_sx->id_pinctrl, otg_sx->id_float);
+	if (to_host) {
+		ssusb_set_force_mode(ssusb, MTU3_DR_FORCE_HOST);
+		ssusb_set_mailbox(otg_sx, MTU3_VBUS_OFF);
+		ssusb_set_mailbox(otg_sx, MTU3_ID_GROUND);
+	} else {
+		ssusb_set_force_mode(ssusb, MTU3_DR_FORCE_DEVICE);
+		ssusb_set_mailbox(otg_sx, MTU3_ID_FLOAT);
+		ssusb_set_mailbox(otg_sx, MTU3_VBUS_VALID);
+	}
 }
 
-
 static int ssusb_mode_show(struct seq_file *sf, void *unused)
 {
 	struct ssusb_mtk *ssusb = sf->private;
@@ -388,17 +389,45 @@ static void ssusb_debugfs_exit(struct ssusb_mtk *ssusb)
 	debugfs_remove_recursive(ssusb->dbgfs_root);
 }
 
+void ssusb_set_force_mode(struct ssusb_mtk *ssusb,
+			  enum mtu3_dr_force_mode mode)
+{
+	u32 value;
+
+	value = mtu3_readl(ssusb->ippc_base, SSUSB_U2_CTRL(0));
+	switch (mode) {
+	case MTU3_DR_FORCE_DEVICE:
+		value |= SSUSB_U2_PORT_FORCE_IDDIG | SSUSB_U2_PORT_RG_IDDIG;
+		break;
+	case MTU3_DR_FORCE_HOST:
+		value |= SSUSB_U2_PORT_FORCE_IDDIG;
+		value &= ~SSUSB_U2_PORT_RG_IDDIG;
+		break;
+	case MTU3_DR_FORCE_NONE:
+		value &= ~(SSUSB_U2_PORT_FORCE_IDDIG | SSUSB_U2_PORT_RG_IDDIG);
+		break;
+	default:
+		return;
+	}
+	mtu3_writel(ssusb->ippc_base, SSUSB_U2_CTRL(0), value);
+}
+
 int ssusb_otg_switch_init(struct ssusb_mtk *ssusb)
 {
 	struct otg_switch_mtk *otg_sx = &ssusb->otg_switch;
 
-	INIT_DELAYED_WORK(&otg_sx->extcon_reg_dwork, extcon_register_dwork);
-
-	if (otg_sx->manual_drd_enabled)
+	if (otg_sx->manual_drd_enabled) {
 		ssusb_debugfs_init(ssusb);
-
-	/* It is enough to delay 1s for waiting for host initialization */
-	schedule_delayed_work(&otg_sx->extcon_reg_dwork, HZ);
+	} else {
+		INIT_DELAYED_WORK(&otg_sx->extcon_reg_dwork,
+				  extcon_register_dwork);
+
+		/*
+		 * It is enough to delay 1s for waiting for
+		 * host initialization
+		 */
+		schedule_delayed_work(&otg_sx->extcon_reg_dwork, HZ);
+	}
 
 	return 0;
 }
@@ -407,8 +436,8 @@ void ssusb_otg_switch_exit(struct ssusb_mtk *ssusb)
 {
 	struct otg_switch_mtk *otg_sx = &ssusb->otg_switch;
 
-	cancel_delayed_work(&otg_sx->extcon_reg_dwork);
-
 	if (otg_sx->manual_drd_enabled)
 		ssusb_debugfs_exit(ssusb);
+	else
+		cancel_delayed_work(&otg_sx->extcon_reg_dwork);
 }
diff --git a/drivers/usb/mtu3/mtu3_dr.h b/drivers/usb/mtu3/mtu3_dr.h
index 9b228b5..0f0cbac 100644
--- a/drivers/usb/mtu3/mtu3_dr.h
+++ b/drivers/usb/mtu3/mtu3_dr.h
@@ -87,6 +87,8 @@ static inline void ssusb_gadget_exit(struct ssusb_mtk *ssusb)
 int ssusb_otg_switch_init(struct ssusb_mtk *ssusb);
 void ssusb_otg_switch_exit(struct ssusb_mtk *ssusb);
 int ssusb_set_vbus(struct otg_switch_mtk *otg_sx, int is_on);
+void ssusb_set_force_mode(struct ssusb_mtk *ssusb,
+			  enum mtu3_dr_force_mode mode);
 
 #else
 
@@ -103,6 +105,10 @@ static inline int ssusb_set_vbus(struct otg_switch_mtk *otg_sx, int is_on)
 	return 0;
 }
 
+static inline void
+ssusb_set_force_mode(struct ssusb_mtk *ssusb, enum mtu3_dr_force_mode mode)
+{}
+
 #endif
 
 #endif		/* _MTU3_DR_H_ */
diff --git a/drivers/usb/mtu3/mtu3_host.c b/drivers/usb/mtu3/mtu3_host.c
index edcc591..ec76b86 100644
--- a/drivers/usb/mtu3/mtu3_host.c
+++ b/drivers/usb/mtu3/mtu3_host.c
@@ -189,6 +189,8 @@ int ssusb_host_disable(struct ssusb_mtk *ssusb, bool suspend)
 
 static void ssusb_host_setup(struct ssusb_mtk *ssusb)
 {
+	struct otg_switch_mtk *otg_sx = &ssusb->otg_switch;
+
 	host_ports_num_get(ssusb);
 
 	/*
@@ -197,6 +199,9 @@ static void ssusb_host_setup(struct ssusb_mtk *ssusb)
 	 */
 	ssusb_host_enable(ssusb);
 
+	if (otg_sx->manual_drd_enabled)
+		ssusb_set_force_mode(ssusb, MTU3_DR_FORCE_HOST);
+
 	/* if port0 supports dual-role, works as host mode by default */
 	ssusb_set_vbus(&ssusb->otg_switch, 1);
 }
diff --git a/drivers/usb/mtu3/mtu3_hw_regs.h b/drivers/usb/mtu3/mtu3_hw_regs.h
index b605975..a7e35f6 100644
--- a/drivers/usb/mtu3/mtu3_hw_regs.h
+++ b/drivers/usb/mtu3/mtu3_hw_regs.h
@@ -472,6 +472,8 @@
 #define SSUSB_U3_PORT_DIS		BIT(0)
 
 /* U3D_SSUSB_U2_CTRL_0P */
+#define SSUSB_U2_PORT_RG_IDDIG		BIT(12)
+#define SSUSB_U2_PORT_FORCE_IDDIG	BIT(11)
 #define SSUSB_U2_PORT_VBUSVALID	BIT(9)
 #define SSUSB_U2_PORT_OTG_SEL		BIT(7)
 #define SSUSB_U2_PORT_HOST		BIT(2)
diff --git a/drivers/usb/mtu3/mtu3_plat.c b/drivers/usb/mtu3/mtu3_plat.c
index fb89920..1e473b0 100644
--- a/drivers/usb/mtu3/mtu3_plat.c
+++ b/drivers/usb/mtu3/mtu3_plat.c
@@ -21,7 +21,6 @@
 #include <linux/module.h>
 #include <linux/of_address.h>
 #include <linux/of_irq.h>
-#include <linux/pinctrl/consumer.h>
 #include <linux/platform_device.h>
 
 #include "mtu3.h"
@@ -212,33 +211,6 @@ static void ssusb_ip_sw_reset(struct ssusb_mtk *ssusb)
 	mtu3_clrbits(ssusb->ippc_base, U3D_SSUSB_IP_PW_CTRL0, SSUSB_IP_SW_RST);
 }
 
-static int get_iddig_pinctrl(struct ssusb_mtk *ssusb)
-{
-	struct otg_switch_mtk *otg_sx = &ssusb->otg_switch;
-
-	otg_sx->id_pinctrl = devm_pinctrl_get(ssusb->dev);
-	if (IS_ERR(otg_sx->id_pinctrl)) {
-		dev_err(ssusb->dev, "Cannot find id pinctrl!\n");
-		return PTR_ERR(otg_sx->id_pinctrl);
-	}
-
-	otg_sx->id_float =
-		pinctrl_lookup_state(otg_sx->id_pinctrl, "id_float");
-	if (IS_ERR(otg_sx->id_float)) {
-		dev_err(ssusb->dev, "Cannot find pinctrl id_float!\n");
-		return PTR_ERR(otg_sx->id_float);
-	}
-
-	otg_sx->id_ground =
-		pinctrl_lookup_state(otg_sx->id_pinctrl, "id_ground");
-	if (IS_ERR(otg_sx->id_ground)) {
-		dev_err(ssusb->dev, "Cannot find pinctrl id_ground!\n");
-		return PTR_ERR(otg_sx->id_ground);
-	}
-
-	return 0;
-}
-
 /* ignore the error if the clock does not exist */
 static struct clk *get_optional_clk(struct device *dev, const char *id)
 {
@@ -349,15 +321,11 @@ static int get_ssusb_rscs(struct platform_device *pdev, struct ssusb_mtk *ssusb)
 			dev_err(ssusb->dev, "couldn't get extcon device\n");
 			return -EPROBE_DEFER;
 		}
-		if (otg_sx->manual_drd_enabled) {
-			ret = get_iddig_pinctrl(ssusb);
-			if (ret)
-				return ret;
-		}
 	}
 
-	dev_info(dev, "dr_mode: %d, is_u3_dr: %d, u3p_dis_msk:%x\n",
-		ssusb->dr_mode, otg_sx->is_u3_drd, ssusb->u3p_dis_msk);
+	dev_info(dev, "dr_mode: %d, is_u3_dr: %d, u3p_dis_msk: %x, drd: %s\n",
+		ssusb->dr_mode, otg_sx->is_u3_drd, ssusb->u3p_dis_msk,
+		otg_sx->manual_drd_enabled ? "manual" : "auto");
 
 	return 0;
 }
-- 
1.7.9.5

  parent reply	other threads:[~2017-09-27 10:59 UTC|newest]

Thread overview: 56+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2017-09-27 10:58 [PATCH 01/12] usb: mtu3: fix error return code in ssusb_gadget_init() Chunfeng Yun
2017-09-27 10:58 ` Chunfeng Yun
2017-09-27 10:58 ` Chunfeng Yun
2017-09-27 10:58 ` [PATCH 02/12] usb: mtu3: support option to disable usb3 ports Chunfeng Yun
2017-09-27 10:58   ` Chunfeng Yun
2017-09-27 10:58   ` Chunfeng Yun
2017-09-27 10:58 ` [PATCH 03/12] usb: mtu3: remove dummy wakeup debounce clocks Chunfeng Yun
2017-09-27 10:58   ` Chunfeng Yun
2017-09-27 10:58   ` Chunfeng Yun
2017-09-27 10:58 ` [PATCH 04/12] usb: mtu3: add optional mcu and dma bus clocks Chunfeng Yun
2017-09-27 10:58   ` Chunfeng Yun
2017-09-27 10:58   ` Chunfeng Yun
2017-09-27 10:58 ` [PATCH 05/12] usb: mtu3: support 36-bit DMA address Chunfeng Yun
2017-09-27 10:58   ` Chunfeng Yun
2017-09-27 10:58   ` Chunfeng Yun
2017-09-27 10:58 ` Chunfeng Yun [this message]
2017-09-27 10:58   ` [PATCH 06/12] usb: mtu3: use FORCE/RG_IDDIG to implement manual DRD switch Chunfeng Yun
2017-09-27 10:58   ` Chunfeng Yun
2017-09-27 10:58 ` [PATCH 07/12] usb: mtu3: add support for usb3.1 IP Chunfeng Yun
2017-09-27 10:58   ` Chunfeng Yun
2017-09-27 10:58   ` Chunfeng Yun
2017-09-29 14:11   ` Chunfeng Yun
2017-09-29 14:11     ` Chunfeng Yun
2017-09-29 14:11     ` Chunfeng Yun
2017-09-27 10:58 ` [PATCH 08/12] usb: mtu3: get optional vbus for host only mode Chunfeng Yun
2017-09-27 10:58   ` Chunfeng Yun
2017-09-27 10:58   ` Chunfeng Yun
2017-09-27 10:58 ` [PATCH 09/12] usb: mtu3: set invalid dr_mode as dual-role mode Chunfeng Yun
2017-09-27 10:58   ` Chunfeng Yun
2017-09-27 10:58   ` Chunfeng Yun
2017-09-28  0:17 ` [PATCH 10/12] dt-bindings: usb: mtu3: add a optional property to disable u3ports Chunfeng Yun
2017-09-28  0:17   ` Chunfeng Yun
2017-09-28  0:17   ` Chunfeng Yun
2017-10-05 22:31   ` Rob Herring
2017-10-05 22:31     ` Rob Herring
2017-10-09  2:43     ` Chunfeng Yun
2017-10-09  2:43       ` Chunfeng Yun
2017-10-09  2:43       ` Chunfeng Yun
2017-09-28  0:17 ` [PATCH 11/12] dt-bindings: usb: mtu3: remove dummy clocks and add optional ones Chunfeng Yun
2017-09-28  0:17   ` Chunfeng Yun
2017-09-28  0:17   ` Chunfeng Yun
2017-09-28  0:17 ` [PATCH 12/12] dt-bindings: usb: mtu3: remove optional pinctrls Chunfeng Yun
2017-09-28  0:17   ` Chunfeng Yun
2017-09-28  0:17   ` Chunfeng Yun
2017-10-05 22:33   ` Rob Herring
2017-10-05 22:33     ` Rob Herring
2017-10-05 22:33     ` Rob Herring
2017-10-09  3:25     ` Chunfeng Yun
2017-10-09  3:25       ` Chunfeng Yun
2017-10-09  3:25       ` Chunfeng Yun
2017-09-30 13:00 ` [PATCH 01/12] usb: mtu3: fix error return code in ssusb_gadget_init() Sergei Shtylyov
2017-09-30 13:00   ` Sergei Shtylyov
2017-09-30 13:00   ` Sergei Shtylyov
2017-10-09  1:18   ` Chunfeng Yun
2017-10-09  1:18     ` Chunfeng Yun
2017-10-09  1:18     ` Chunfeng Yun

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=bfe4b110c3128498d9c36cd49c8cfb4370f43891.1506509658.git.chunfeng.yun@mediatek.com \
    --to=chunfeng.yun@mediatek.com \
    --cc=devicetree@vger.kernel.org \
    --cc=felipe.balbi@linux.intel.com \
    --cc=gregkh@linuxfoundation.org \
    --cc=ijc+devicetree@hellion.org.uk \
    --cc=linux-arm-kernel@lists.infradead.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=linux-mediatek@lists.infradead.org \
    --cc=linux-usb@vger.kernel.org \
    --cc=mark.rutland@arm.com \
    --cc=mathias.nyman@intel.com \
    --cc=matthias.bgg@gmail.com \
    --cc=robh+dt@kernel.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.