diff mbox series

[4/9] usb: gadget: udc: renesas_usb3: use extcon framework to receive connect/disconnect

Message ID 1551863246-11656-5-git-send-email-biju.das@bp.renesas.com (mailing list archive)
State Superseded
Headers show
Series Add USB3.0 and TI HD3SS3220 driver support | expand

Commit Message

Biju Das March 6, 2019, 9:07 a.m. UTC
RZ/G2E cat874 board is capable of detecting cable connect and disconnect
events. Add support for renesas_usb3 to receive connect and disconnect
notification using extcon framework.

Signed-off-by: Biju Das <biju.das@bp.renesas.com>
---
 drivers/usb/gadget/udc/renesas_usb3.c | 115 +++++++++++++++++++++++++++++-----
 1 file changed, 99 insertions(+), 16 deletions(-)

Comments

Heikki Krogerus March 6, 2019, 12:43 p.m. UTC | #1
On Wed, Mar 06, 2019 at 09:07:21AM +0000, Biju Das wrote:
> RZ/G2E cat874 board is capable of detecting cable connect and disconnect
> events. Add support for renesas_usb3 to receive connect and disconnect
> notification using extcon framework.

I think you will need the series from Yu Chen [1], but once we have
that, you can do this with USB role class instead of relying on
extcon, and that is what you need to do.

There is a reason why we need to use a dedicated class with the USB
role switching instead of relying on extcon. We actually originally
planned on using extcon with the USB role switches, but the extcon
maintainers refused some the USB mux drivers. The problem from extcon
perspective is that the consumer/supplier roles seem to be inverted in
some cases. USB role switch can simply be too many things - discrete
mux, integrated mux (like in your case) or something like a dual role
capable USB controller could simply represent one.

On the other hand, using a dedicated API and class now feel like a
much better idea in general compared to a multipurpose thing like
extcon.

[1] https://lkml.org/lkml/2019/3/2/42

thanks,
Biju Das March 7, 2019, 4:02 p.m. UTC | #2
Hi Heikki,

Thanks for the feedback.

> Subject: Re: [PATCH 4/9] usb: gadget: udc: renesas_usb3: use extcon
> framework to receive connect/disconnect
> 
> On Wed, Mar 06, 2019 at 09:07:21AM +0000, Biju Das wrote:
> > RZ/G2E cat874 board is capable of detecting cable connect and
> > disconnect events. Add support for renesas_usb3 to receive connect and
> > disconnect notification using extcon framework.
> 
> I think you will need the series from Yu Chen [1], but once we have that, you
> can do this with USB role class instead of relying on extcon, and that is what
> you need to do.
> 
> There is a reason why we need to use a dedicated class with the USB role
> switching instead of relying on extcon. We actually originally planned on using
> extcon with the USB role switches, but the extcon maintainers refused some
> the USB mux drivers. The problem from extcon perspective is that the
> consumer/supplier roles seem to be inverted in some cases. USB role switch
> can simply be too many things - discrete mux, integrated mux (like in your
> case) or something like a dual role capable USB controller could simply
> represent one.
> 
> On the other hand, using a dedicated API and class now feel like a much
> better idea in general compared to a multipurpose thing like extcon.
> 
> [1] https://lkml.org/lkml/2019/3/2/42

Ok. Will use usb role class instead of extcon . 

Regards,
Biju
diff mbox series

Patch

diff --git a/drivers/usb/gadget/udc/renesas_usb3.c b/drivers/usb/gadget/udc/renesas_usb3.c
index 7dc2485..2c69d5d 100644
--- a/drivers/usb/gadget/udc/renesas_usb3.c
+++ b/drivers/usb/gadget/udc/renesas_usb3.c
@@ -351,6 +351,11 @@  struct renesas_usb3 {
 	int disabled_count;
 
 	struct usb_request *ep0_req;
+
+	struct extcon_dev *edev;
+	struct notifier_block ufp_nb;
+	struct notifier_block dfp_nb;
+
 	u16 test_mode;
 	u8 ep0_buf[USB3_EP0_BUF_SIZE];
 	bool softconnect;
@@ -644,22 +649,6 @@  static void usb3_disconnect(struct renesas_usb3 *usb3)
 		usb3->driver->disconnect(&usb3->gadget);
 }
 
-static void usb3_check_vbus(struct renesas_usb3 *usb3)
-{
-	if (usb3->workaround_for_vbus) {
-		usb3_connect(usb3);
-	} else {
-		usb3->extcon_usb = !!(usb3_read(usb3, USB3_USB_STA) &
-							USB_STA_VBUS_STA);
-		if (usb3->extcon_usb)
-			usb3_connect(usb3);
-		else
-			usb3_disconnect(usb3);
-
-		schedule_work(&usb3->extcon_work);
-	}
-}
-
 static void renesas_usb3_role_work(struct work_struct *work)
 {
 	struct renesas_usb3 *usb3 =
@@ -724,6 +713,32 @@  static void usb3_check_id(struct renesas_usb3 *usb3)
 	schedule_work(&usb3->extcon_work);
 }
 
+static void usb3_check_vbus(struct renesas_usb3 *usb3)
+{
+	if (usb3->workaround_for_vbus) {
+		if (usb3->edev) {
+			if (extcon_get_state(usb3->edev, EXTCON_USB) == true) {
+				usb3->forced_b_device = true;
+				usb3->start_to_connect = true;
+				usb3_disconnect(usb3);
+				usb3_check_id(usb3);
+			} else if (extcon_get_state(usb3->edev,
+						EXTCON_USB_HOST) == false)
+				usb3_vbus_out(usb3, false);
+		} else
+			usb3_connect(usb3);
+	} else {
+		usb3->extcon_usb = !!(usb3_read(usb3, USB3_USB_STA) &
+							USB_STA_VBUS_STA);
+		if (usb3->extcon_usb)
+			usb3_connect(usb3);
+		else
+			usb3_disconnect(usb3);
+
+		schedule_work(&usb3->extcon_work);
+	}
+}
+
 static void renesas_usb3_init_controller(struct renesas_usb3 *usb3)
 {
 	usb3_init_axi_bridge(usb3);
@@ -2656,6 +2671,47 @@  static const struct usb_role_switch_desc renesas_usb3_role_switch_desc = {
 	.allow_userspace_control = true,
 };
 
+static int renesas_usb3_ufp_notifier(struct notifier_block *nb,
+					unsigned long event, void *ptr)
+{
+	struct renesas_usb3 *usb3 = container_of(nb,
+					struct renesas_usb3, ufp_nb);
+
+	usb3->start_to_connect = false;
+	if (event && usb3->driver) {
+		usb3->forced_b_device = true;
+		usb3->start_to_connect = true;
+	}
+
+	if (usb3->driver) {
+		usb3_disconnect(usb3);
+		usb3_check_id(usb3);
+	}
+
+	usb3_vbus_out(usb3, false);
+	dev_dbg(usb3_to_dev(usb3), "ufp_notifier event=%ld", event);
+
+	return NOTIFY_DONE;
+}
+
+static int renesas_usb3_dfp_notifier(struct notifier_block *nb,
+					unsigned long event, void *ptr)
+{
+	struct renesas_usb3 *usb3 = container_of(nb,
+					struct renesas_usb3, dfp_nb);
+
+	if (event) {
+		usb3->forced_b_device = false;
+		usb3_disconnect(usb3);
+		usb3_check_id(usb3);
+	} else
+		usb3_vbus_out(usb3, false);
+
+	dev_dbg(usb3_to_dev(usb3), "dfp_notifier event=%ld", event);
+
+	return NOTIFY_DONE;
+}
+
 static int renesas_usb3_probe(struct platform_device *pdev)
 {
 	struct renesas_usb3 *usb3;
@@ -2703,6 +2759,33 @@  static int renesas_usb3_probe(struct platform_device *pdev)
 		return ret;
 
 	INIT_WORK(&usb3->extcon_work, renesas_usb3_extcon_work);
+
+	if (priv->workaround_for_vbus &&
+			of_property_read_bool(pdev->dev.of_node, "extcon")) {
+		usb3->edev = extcon_get_edev_by_phandle(&pdev->dev, 0);
+		if (IS_ERR(usb3->edev))
+			return PTR_ERR(usb3->edev);
+
+		usb3->ufp_nb.notifier_call = renesas_usb3_ufp_notifier;
+		ret = devm_extcon_register_notifier(&pdev->dev, usb3->edev,
+						EXTCON_USB, &usb3->ufp_nb);
+		if (ret < 0)
+			dev_dbg(&pdev->dev, "USB register notifier failed\n");
+
+		usb3->dfp_nb.notifier_call = renesas_usb3_dfp_notifier;
+		ret = devm_extcon_register_notifier(&pdev->dev, usb3->edev,
+						EXTCON_USB_HOST, &usb3->dfp_nb);
+		if (ret < 0)
+			dev_dbg(&pdev->dev,
+					"USB-HOST register notifier failed\n");
+		if (extcon_get_state(usb3->edev, EXTCON_USB) == true) {
+			usb3->forced_b_device = true;
+			usb3->start_to_connect = true;
+		} else	if (extcon_get_state(usb3->edev,
+						 EXTCON_USB_HOST) == false)
+			usb3_vbus_out(usb3, false);
+	}
+
 	usb3->extcon = devm_extcon_dev_allocate(&pdev->dev, renesas_usb3_cable);
 	if (IS_ERR(usb3->extcon))
 		return PTR_ERR(usb3->extcon);