diff mbox

[v3] usb: renesas_usbhs: Allow an OTG PHY driver to provide VBUS

Message ID 1435832793-9999-1-git-send-email-phil.edworthy@renesas.com (mailing list archive)
State Superseded
Delegated to: Geert Uytterhoeven
Headers show

Commit Message

Phil Edworthy July 2, 2015, 10:26 a.m. UTC
These changes allow a PHY driver to trigger a VBUS interrupt and
to provide the value of VBUS.

Signed-off-by: Phil Edworthy <phil.edworthy@renesas.com>

---
v3:
  - Changed how indirect vbus is plumbed in.
  - Removed unnecessary (void) on call to otg_set_peripheral.
  - Moved code that connects to bus through transceiver so it
    is before setting gpriv->driver.

v2:
  - vbus variables changed from int to bool.
  - dev_info() changed to dev_err()
---
 drivers/usb/renesas_usbhs/common.c     |  8 +++++--
 drivers/usb/renesas_usbhs/common.h     |  2 ++
 drivers/usb/renesas_usbhs/mod_gadget.c | 38 ++++++++++++++++++++++++++++++++++
 3 files changed, 46 insertions(+), 2 deletions(-)

Comments

Yoshihiro Shimoda July 6, 2015, 7:27 a.m. UTC | #1
Hi Phil-san,

> Sent: Thursday, July 02, 2015 7:27 PM
> 
> These changes allow a PHY driver to trigger a VBUS interrupt and
> to provide the value of VBUS.
> 
> Signed-off-by: Phil Edworthy <phil.edworthy@renesas.com>

Thank you for the patch!

However, if I tested this patch, a kernel panic happened when
I did "rmmod g_mass_storage" on koelsch:

root@192:~/usb# rmmod g_mass_storage
Unable to handle kernel NULL pointer dereference at virtual address 00000004
pgd = eebd4bc0
[00000004] *pgd=00000000
Internal error: Oops: 205 [#1] SMP ARM
Modules linked in: g_mass_storage(-) usb_f_mass_storage libcomposite
CPU: 0 PID: 2496 Comm: rmmod Not tainted 4.1.0-dirty #32
Hardware name: Generic R8A7791 (Flattened Device Tree)
task: eeb64ac0 ti: ee022000 task.ti: ee022000
PC is at usbhs_pkt_pop+0x1c/0x100
LR is at usbhsg_ep_dequeue+0x28/0x40
pc : [<c036d934>]    lr : [<c036e364>]    psr: 20000013
sp : ee023e70  ip : ee023e98  fp : ee023e94
r10: 00000000  r9 : ee022000  r8 : c00101e4
r7 : 00200200  r6 : 00100100  r5 : 00000000  r4 : eeb917f8
r3 : c036e33c  r2 : 00000041  r1 : eeb917f8  r0 : 00000000
Flags: nzCv  IRQs on  FIQs on  Mode SVC_32  ISA ARM  Segment user
Control: 30c5307d  Table: 6ebd4bc0  DAC: fffffffd
Process rmmod (pid: 2496, stack limit = 0xee022210)
Stack: (0xee023e70 to 0xee024000)
3e60:                                     ee1b1400 eeb917c0 00100100 00200200
3e80: c00101e4 ee022000 ee023eac ee023e98 c036e364 c036d924 eeb34180 eeb341c0
3ea0: ee023ecc ee023eb0 bf002f20 c036e348 eeb34180 00000001 ee1b1800 eeb341b8
3ec0: ee023eec ee023ed0 bf002fec bf002e5c ee181600 bf01d624 bed3ff0d 00000081
3ee0: ee023efc ee023ef0 bf003028 bf002f78 ee023f14 ee023f00 c036fb88 bf003018
3f00: ee181600 bf01d624 ee023f2c ee023f18 c036fc10 c036fb34 bf01d694 7f64cd30
3f20: ee023f3c ee023f30 bf0013d0 c036fbbc ee023f4c ee023f40 bf01d26c bf0013c0
3f40: ee023fa4 ee023f50 c0090bd8 bf01d248 ee023f6c 616d5f67 735f7373 61726f74
3f60: 00006567 ee023f70 c00460f4 c052a8c0 c00101e4 ee022010 ee023fb0 ee022000
3f80: ee023fac ee023f90 c00136c4 00046038 00000001 7f64cd00 00000000 ee023fa8
3fa0: c0010040 c0090adc 00000001 7f64cd00 7f64cd30 00000800 ccd5c100 ccd5c100
3fc0: 00000001 7f64cd00 bed3ff0d 00000081 7f64c008 00000000 00000002 00000800
3fe0: bed3fe0c bed3fb98 b6eeb068 b6e4f05c 60000010 7f64cd30 00000000 00000000
Backtrace:
[<c036d918>] (usbhs_pkt_pop) from [<c036e364>] (usbhsg_ep_dequeue+0x28/0x40)
 r9:ee022000 r8:c00101e4 r7:00200200 r6:00100100 r5:eeb917c0 r4:ee1b1400
[<c036e33c>] (usbhsg_ep_dequeue) from [<bf002f20>] (composite_dev_cleanup+0xd0/0x11c [libcomposite])
 r5:eeb341c0 r4:eeb34180
[<bf002e50>] (composite_dev_cleanup [libcomposite]) from [<bf002fec>] (__composite_unbind+0x80/0xa0 [libcomposite])
 r7:eeb341b8 r6:ee1b1800 r5:00000001 r4:eeb34180
[<bf002f6c>] (__composite_unbind [libcomposite]) from [<bf003028>] (composite_unbind+0x1c/0x20 [libcomposite])
 r7:00000081 r6:bed3ff0d r5:bf01d624 r4:ee181600
[<bf00300c>] (composite_unbind [libcomposite]) from [<c036fb88>] (usb_gadget_remove_driver+0x60/0x88)
[<c036fb28>] (usb_gadget_remove_driver) from [<c036fc10>] (usb_gadget_unregister_driver+0x60/0xa0)
 r5:bf01d624 r4:ee181600
[<c036fbb0>] (usb_gadget_unregister_driver) from [<bf0013d0>] (usb_composite_unregister+0x1c/0x20 [libcomposite])
 r5:7f64cd30 r4:bf01d694
[<bf0013b4>] (usb_composite_unregister [libcomposite]) from [<bf01d26c>] (msg_cleanup+0x30/0x3c [g_mass_storage])
[<bf01d23c>] (msg_cleanup [g_mass_storage]) from [<c0090bd8>] (SyS_delete_module+0x108/0x1c4)
[<c0090ad0>] (SyS_delete_module) from [<c0010040>] (ret_fast_syscall+0x0/0x3c)
 r5:7f64cd00 r4:00000001
Code: e52de004 e8bd4000 e1a05000 e1a04001 (e5907004)
---[ end trace 8152e492b3f02f66 ]---


I don't know why this kernel panic happened after I applied this patch.
But, if I added the following code in the mod_gadget.c, this issue disappeared:

@@ -682,7 +684,8 @@ static int usbhsg_ep_dequeue(struct usb_ep *ep, struct usb_r
        struct usbhsg_request *ureq = usbhsg_req_to_ureq(req);
        struct usbhs_pipe *pipe = usbhsg_uep_to_pipe(uep);

-       usbhs_pkt_pop(pipe, usbhsg_ureq_to_pkt(ureq));
+       if (pipe)
+               usbhs_pkt_pop(pipe, usbhsg_ureq_to_pkt(ureq));
        usbhsg_queue_pop(uep, ureq, -ECONNRESET);

        return 0;

Best regards,
Yoshihiro Shimoda


--
To unsubscribe from this list: send the line "unsubscribe linux-sh" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Laurent Pinchart July 6, 2015, 8:20 a.m. UTC | #2
Hi Phil,

Thank you for the patch.

On Thursday 02 July 2015 11:26:33 Phil Edworthy wrote:
> These changes allow a PHY driver to trigger a VBUS interrupt and
> to provide the value of VBUS.
> 
> Signed-off-by: Phil Edworthy <phil.edworthy@renesas.com>
> 
> ---
> v3:
>   - Changed how indirect vbus is plumbed in.
>   - Removed unnecessary (void) on call to otg_set_peripheral.
>   - Moved code that connects to bus through transceiver so it
>     is before setting gpriv->driver.
> 
> v2:
>   - vbus variables changed from int to bool.
>   - dev_info() changed to dev_err()
> ---
>  drivers/usb/renesas_usbhs/common.c     |  8 +++++--
>  drivers/usb/renesas_usbhs/common.h     |  2 ++
>  drivers/usb/renesas_usbhs/mod_gadget.c | 38 +++++++++++++++++++++++++++++++
>  3 files changed, 46 insertions(+), 2 deletions(-)
> 
> diff --git a/drivers/usb/renesas_usbhs/common.c
> b/drivers/usb/renesas_usbhs/common.c index 0f7e850..15e67d8 100644
> --- a/drivers/usb/renesas_usbhs/common.c
> +++ b/drivers/usb/renesas_usbhs/common.c
> @@ -379,7 +379,10 @@ static void usbhsc_hotplug(struct usbhs_priv *priv)
>  	/*
>  	 * get vbus status from platform
>  	 */
> -	enable = usbhs_platform_call(priv, get_vbus, pdev);
> +	if (priv->vbus_is_indirect)
> +		enable = priv->vbus_indirect_value;
> +	else
> +		enable = usbhs_platform_call(priv, get_vbus, pdev);
> 
>  	/*
>  	 * get id from platform
> @@ -662,7 +665,8 @@ static int usbhs_probe(struct platform_device *pdev)
>  	pm_runtime_enable(&pdev->dev);
>  	if (!usbhsc_flags_has(priv, USBHSF_RUNTIME_PWCTRL)) {
>  		usbhsc_power_ctrl(priv, 1);
> -		usbhs_mod_autonomy_mode(priv);
> +		if (!priv->vbus_is_indirect)

Isn't this racy ? The vbus_session operation is called by transceivers from 
interrupt handlers or work queues. Do we have a guarantee that it will be 
called at least once before this code is reached ? Please see below for a 
possible way to fix this.

> +			usbhs_mod_autonomy_mode(priv);
>  	}
> 
>  	/*
> diff --git a/drivers/usb/renesas_usbhs/common.h
> b/drivers/usb/renesas_usbhs/common.h index 8c5fc12..478700c 100644
> --- a/drivers/usb/renesas_usbhs/common.h
> +++ b/drivers/usb/renesas_usbhs/common.h
> @@ -255,6 +255,8 @@ struct usbhs_priv {
>  	struct renesas_usbhs_driver_param	dparam;
> 
>  	struct delayed_work notify_hotplug_work;
> +	bool vbus_is_indirect;
> +	bool vbus_indirect_value;
>  	struct platform_device *pdev;
> 
>  	struct extcon_dev *edev;
> diff --git a/drivers/usb/renesas_usbhs/mod_gadget.c
> b/drivers/usb/renesas_usbhs/mod_gadget.c index dc2aa32..93ad4ea 100644
> --- a/drivers/usb/renesas_usbhs/mod_gadget.c
> +++ b/drivers/usb/renesas_usbhs/mod_gadget.c
> @@ -21,6 +21,7 @@
>  #include <linux/platform_device.h>
>  #include <linux/usb/ch9.h>
>  #include <linux/usb/gadget.h>
> +#include <linux/usb/otg.h>
>  #include "common.h"
> 
>  /*
> @@ -50,6 +51,7 @@ struct usbhsg_gpriv {
>  	int			 uep_size;
> 
>  	struct usb_gadget_driver	*driver;
> +	struct usb_phy		*transceiver;
> 
>  	u32	status;
>  #define USBHSG_STATUS_STARTED		(1 << 0)
> @@ -882,12 +884,25 @@ static int usbhsg_gadget_start(struct usb_gadget
> *gadget, {
>  	struct usbhsg_gpriv *gpriv = usbhsg_gadget_to_gpriv(gadget);
>  	struct usbhs_priv *priv = usbhsg_gpriv_to_priv(gpriv);
> +	struct device *dev = usbhs_priv_to_dev(priv);
> +	int ret;
> 
>  	if (!driver		||
>  	    !driver->setup	||
>  	    driver->max_speed < USB_SPEED_FULL)
>  		return -EINVAL;
> 
> +	/* connect to bus through transceiver */
> +	if (!IS_ERR_OR_NULL(gpriv->transceiver)) {
> +		ret = otg_set_peripheral(gpriv->transceiver->otg,
> +					&gpriv->gadget);
> +		if (ret) {
> +			dev_err(dev, "%s: can't bind to transceiver\n",
> +				gpriv->gadget.name);
> +			return ret;
> +		}
> +	}
> +

How is this change related to the topic ? I'm not too familiar with this 
driver so I might just have missed something, but then others could as well, 
and the commit message could do with a bit more information.

>  	/* first hook up the driver ... */
>  	gpriv->driver = driver;
> 
> @@ -900,6 +915,10 @@ static int usbhsg_gadget_stop(struct usb_gadget
> *gadget) struct usbhs_priv *priv = usbhsg_gpriv_to_priv(gpriv);
> 
>  	usbhsg_try_stop(priv, USBHSG_STATUS_REGISTERD);
> +
> +	if (!IS_ERR_OR_NULL(gpriv->transceiver))
> +		otg_set_peripheral(gpriv->transceiver->otg, NULL);
> +
>  	gpriv->driver = NULL;
> 
>  	return 0;
> @@ -947,12 +966,27 @@ static int usbhsg_set_selfpowered(struct usb_gadget
> *gadget, int is_self) return 0;
>  }
> 
> +static int usbhsg_vbus_session(struct usb_gadget *gadget, int is_active)
> +{
> +	struct usbhsg_gpriv *gpriv = usbhsg_gadget_to_gpriv(gadget);
> +	struct usbhs_priv *priv = usbhsg_gpriv_to_priv(gpriv);
> +	struct platform_device *pdev = usbhs_priv_to_pdev(priv);
> +
> +	priv->vbus_is_indirect = 1;

vbus_is_indirect is never reset to 0. Shouldn't you set it to 1 (or rather to 
true) in usbhs_mod_gadget_probe() if an external transceiver is found ?

What would you think of implementing and wiring up the get_vbus operation in 
usbhs_mod_gadget_probe() in the same way that usbhs_mod_autonomy_mode() does, 
and return the value of vbus_indirect_value there ? You could then get rid of 
the vbus_is_indirect field and move the vbus_indirect_value field to struct 
usbhsg_gpriv, removing the need to modify drivers/usb/renesas_usbhs/common.c. 
It should also solve the race condition mentioned above.

> +	priv->vbus_indirect_value = !!is_active;
> +
> +	renesas_usbhs_call_notify_hotplug(pdev);
> +
> +	return 0;
> +}
> +
>  static const struct usb_gadget_ops usbhsg_gadget_ops = {
>  	.get_frame		= usbhsg_get_frame,
>  	.set_selfpowered	= usbhsg_set_selfpowered,
>  	.udc_start		= usbhsg_gadget_start,
>  	.udc_stop		= usbhsg_gadget_stop,
>  	.pullup			= usbhsg_pullup,
> +	.vbus_session		= usbhsg_vbus_session,
>  };
> 
>  static int usbhsg_start(struct usbhs_priv *priv)
> @@ -994,6 +1028,10 @@ int usbhs_mod_gadget_probe(struct usbhs_priv *priv)
>  		goto usbhs_mod_gadget_probe_err_gpriv;
>  	}
> 
> +	gpriv->transceiver = usb_get_phy(USB_PHY_TYPE_UNDEFINED);
> +	dev_info(dev, "%s transceiver found\n",
> +		 gpriv->transceiver ? "" : "No");
> +
>  	/*
>  	 * CAUTION
>  	 *
Phil Edworthy July 7, 2015, 8:59 a.m. UTC | #3
Hi Shimoda-san,

On 06 July 2015 08:28, Shimoda-san wrote:
> Hi Phil-san,
> 
> > Sent: Thursday, July 02, 2015 7:27 PM
> >
> > These changes allow a PHY driver to trigger a VBUS interrupt and
> > to provide the value of VBUS.
> >
> > Signed-off-by: Phil Edworthy <phil.edworthy@renesas.com>
> 
> Thank you for the patch!
> 
> However, if I tested this patch, a kernel panic happened when
> I did "rmmod g_mass_storage" on koelsch:
> 
> root@192:~/usb# rmmod g_mass_storage
> Unable to handle kernel NULL pointer dereference at virtual address 00000004
> pgd = eebd4bc0
> [00000004] *pgd=00000000
> Internal error: Oops: 205 [#1] SMP ARM
> Modules linked in: g_mass_storage(-) usb_f_mass_storage libcomposite
> CPU: 0 PID: 2496 Comm: rmmod Not tainted 4.1.0-dirty #32
> Hardware name: Generic R8A7791 (Flattened Device Tree)
> task: eeb64ac0 ti: ee022000 task.ti: ee022000
> PC is at usbhs_pkt_pop+0x1c/0x100
> LR is at usbhsg_ep_dequeue+0x28/0x40
> pc : [<c036d934>]    lr : [<c036e364>]    psr: 20000013
> sp : ee023e70  ip : ee023e98  fp : ee023e94
> r10: 00000000  r9 : ee022000  r8 : c00101e4
> r7 : 00200200  r6 : 00100100  r5 : 00000000  r4 : eeb917f8
> r3 : c036e33c  r2 : 00000041  r1 : eeb917f8  r0 : 00000000
> Flags: nzCv  IRQs on  FIQs on  Mode SVC_32  ISA ARM  Segment user
> Control: 30c5307d  Table: 6ebd4bc0  DAC: fffffffd
> Process rmmod (pid: 2496, stack limit = 0xee022210)
> Stack: (0xee023e70 to 0xee024000)
> 3e60:                                     ee1b1400 eeb917c0 00100100 00200200
> 3e80: c00101e4 ee022000 ee023eac ee023e98 c036e364 c036d924 eeb34180
> eeb341c0
> 3ea0: ee023ecc ee023eb0 bf002f20 c036e348 eeb34180 00000001 ee1b1800
> eeb341b8
> 3ec0: ee023eec ee023ed0 bf002fec bf002e5c ee181600 bf01d624 bed3ff0d
> 00000081
> 3ee0: ee023efc ee023ef0 bf003028 bf002f78 ee023f14 ee023f00 c036fb88
> bf003018
> 3f00: ee181600 bf01d624 ee023f2c ee023f18 c036fc10 c036fb34 bf01d694 7f64cd30
> 3f20: ee023f3c ee023f30 bf0013d0 c036fbbc ee023f4c ee023f40 bf01d26c bf0013c0
> 3f40: ee023fa4 ee023f50 c0090bd8 bf01d248 ee023f6c 616d5f67 735f7373
> 61726f74
> 3f60: 00006567 ee023f70 c00460f4 c052a8c0 c00101e4 ee022010 ee023fb0
> ee022000
> 3f80: ee023fac ee023f90 c00136c4 00046038 00000001 7f64cd00 00000000
> ee023fa8
> 3fa0: c0010040 c0090adc 00000001 7f64cd00 7f64cd30 00000800 ccd5c100 ccd5c100
> 3fc0: 00000001 7f64cd00 bed3ff0d 00000081 7f64c008 00000000 00000002 00000800
> 3fe0: bed3fe0c bed3fb98 b6eeb068 b6e4f05c 60000010 7f64cd30 00000000
> 00000000
> Backtrace:
> [<c036d918>] (usbhs_pkt_pop) from [<c036e364>]
> (usbhsg_ep_dequeue+0x28/0x40)
>  r9:ee022000 r8:c00101e4 r7:00200200 r6:00100100 r5:eeb917c0 r4:ee1b1400
> [<c036e33c>] (usbhsg_ep_dequeue) from [<bf002f20>]
> (composite_dev_cleanup+0xd0/0x11c [libcomposite])
>  r5:eeb341c0 r4:eeb34180
> [<bf002e50>] (composite_dev_cleanup [libcomposite]) from [<bf002fec>]
> (__composite_unbind+0x80/0xa0 [libcomposite])
>  r7:eeb341b8 r6:ee1b1800 r5:00000001 r4:eeb34180
> [<bf002f6c>] (__composite_unbind [libcomposite]) from [<bf003028>]
> (composite_unbind+0x1c/0x20 [libcomposite])
>  r7:00000081 r6:bed3ff0d r5:bf01d624 r4:ee181600
> [<bf00300c>] (composite_unbind [libcomposite]) from [<c036fb88>]
> (usb_gadget_remove_driver+0x60/0x88)
> [<c036fb28>] (usb_gadget_remove_driver) from [<c036fc10>]
> (usb_gadget_unregister_driver+0x60/0xa0)
>  r5:bf01d624 r4:ee181600
> [<c036fbb0>] (usb_gadget_unregister_driver) from [<bf0013d0>]
> (usb_composite_unregister+0x1c/0x20 [libcomposite])
>  r5:7f64cd30 r4:bf01d694
> [<bf0013b4>] (usb_composite_unregister [libcomposite]) from [<bf01d26c>]
> (msg_cleanup+0x30/0x3c [g_mass_storage])
> [<bf01d23c>] (msg_cleanup [g_mass_storage]) from [<c0090bd8>]
> (SyS_delete_module+0x108/0x1c4)
> [<c0090ad0>] (SyS_delete_module) from [<c0010040>]
> (ret_fast_syscall+0x0/0x3c)
>  r5:7f64cd00 r4:00000001
> Code: e52de004 e8bd4000 e1a05000 e1a04001 (e5907004)
> ---[ end trace 8152e492b3f02f66 ]---
> 
> 
> I don't know why this kernel panic happened after I applied this patch.
> But, if I added the following code in the mod_gadget.c, this issue disappeared:
> 
> @@ -682,7 +684,8 @@ static int usbhsg_ep_dequeue(struct usb_ep *ep, struct
> usb_r
>         struct usbhsg_request *ureq = usbhsg_req_to_ureq(req);
>         struct usbhs_pipe *pipe = usbhsg_uep_to_pipe(uep);
> 
> -       usbhs_pkt_pop(pipe, usbhsg_ureq_to_pkt(ureq));
> +       if (pipe)
> +               usbhs_pkt_pop(pipe, usbhsg_ureq_to_pkt(ureq));
>         usbhsg_queue_pop(uep, ureq, -ECONNRESET);
> 
>         return 0;

That's odd, I can't see how my patch causes this problem. Do you think
that there has always been a race problem here and my changes make this
happen?

> Best regards,
> Yoshihiro Shimoda
> 

Best regards
Phil
--
To unsubscribe from this list: send the line "unsubscribe linux-sh" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Phil Edworthy July 7, 2015, 9:12 a.m. UTC | #4
Hi Laurent,

On 06 July 2015 09:20, Laurent wrote:
> Hi Phil,
> 
> Thank you for the patch.
Thanks for your review!
 
> On Thursday 02 July 2015 11:26:33 Phil Edworthy wrote:
> > These changes allow a PHY driver to trigger a VBUS interrupt and
> > to provide the value of VBUS.
> >
> > Signed-off-by: Phil Edworthy <phil.edworthy@renesas.com>
> >
> > ---
> > v3:
> >   - Changed how indirect vbus is plumbed in.
> >   - Removed unnecessary (void) on call to otg_set_peripheral.
> >   - Moved code that connects to bus through transceiver so it
> >     is before setting gpriv->driver.
> >
> > v2:
> >   - vbus variables changed from int to bool.
> >   - dev_info() changed to dev_err()
> > ---
> >  drivers/usb/renesas_usbhs/common.c     |  8 +++++--
> >  drivers/usb/renesas_usbhs/common.h     |  2 ++
> >  drivers/usb/renesas_usbhs/mod_gadget.c | 38
> +++++++++++++++++++++++++++++++
> >  3 files changed, 46 insertions(+), 2 deletions(-)
> >
> > diff --git a/drivers/usb/renesas_usbhs/common.c
> > b/drivers/usb/renesas_usbhs/common.c index 0f7e850..15e67d8 100644
> > --- a/drivers/usb/renesas_usbhs/common.c
> > +++ b/drivers/usb/renesas_usbhs/common.c
> > @@ -379,7 +379,10 @@ static void usbhsc_hotplug(struct usbhs_priv *priv)
> >  	/*
> >  	 * get vbus status from platform
> >  	 */
> > -	enable = usbhs_platform_call(priv, get_vbus, pdev);
> > +	if (priv->vbus_is_indirect)
> > +		enable = priv->vbus_indirect_value;
> > +	else
> > +		enable = usbhs_platform_call(priv, get_vbus, pdev);
> >
> >  	/*
> >  	 * get id from platform
> > @@ -662,7 +665,8 @@ static int usbhs_probe(struct platform_device *pdev)
> >  	pm_runtime_enable(&pdev->dev);
> >  	if (!usbhsc_flags_has(priv, USBHSF_RUNTIME_PWCTRL)) {
> >  		usbhsc_power_ctrl(priv, 1);
> > -		usbhs_mod_autonomy_mode(priv);
> > +		if (!priv->vbus_is_indirect)
> 
> Isn't this racy ? The vbus_session operation is called by transceivers from
> interrupt handlers or work queues. Do we have a guarantee that it will be
> called at least once before this code is reached ? Please see below for a
> possible way to fix this.
You are right, it is racy... :(

> > +			usbhs_mod_autonomy_mode(priv);
> >  	}
> >
> >  	/*
> > diff --git a/drivers/usb/renesas_usbhs/common.h
> > b/drivers/usb/renesas_usbhs/common.h index 8c5fc12..478700c 100644
> > --- a/drivers/usb/renesas_usbhs/common.h
> > +++ b/drivers/usb/renesas_usbhs/common.h
> > @@ -255,6 +255,8 @@ struct usbhs_priv {
> >  	struct renesas_usbhs_driver_param	dparam;
> >
> >  	struct delayed_work notify_hotplug_work;
> > +	bool vbus_is_indirect;
> > +	bool vbus_indirect_value;
> >  	struct platform_device *pdev;
> >
> >  	struct extcon_dev *edev;
> > diff --git a/drivers/usb/renesas_usbhs/mod_gadget.c
> > b/drivers/usb/renesas_usbhs/mod_gadget.c index dc2aa32..93ad4ea 100644
> > --- a/drivers/usb/renesas_usbhs/mod_gadget.c
> > +++ b/drivers/usb/renesas_usbhs/mod_gadget.c
> > @@ -21,6 +21,7 @@
> >  #include <linux/platform_device.h>
> >  #include <linux/usb/ch9.h>
> >  #include <linux/usb/gadget.h>
> > +#include <linux/usb/otg.h>
> >  #include "common.h"
> >
> >  /*
> > @@ -50,6 +51,7 @@ struct usbhsg_gpriv {
> >  	int			 uep_size;
> >
> >  	struct usb_gadget_driver	*driver;
> > +	struct usb_phy		*transceiver;
> >
> >  	u32	status;
> >  #define USBHSG_STATUS_STARTED		(1 << 0)
> > @@ -882,12 +884,25 @@ static int usbhsg_gadget_start(struct usb_gadget
> > *gadget, {
> >  	struct usbhsg_gpriv *gpriv = usbhsg_gadget_to_gpriv(gadget);
> >  	struct usbhs_priv *priv = usbhsg_gpriv_to_priv(gpriv);
> > +	struct device *dev = usbhs_priv_to_dev(priv);
> > +	int ret;
> >
> >  	if (!driver		||
> >  	    !driver->setup	||
> >  	    driver->max_speed < USB_SPEED_FULL)
> >  		return -EINVAL;
> >
> > +	/* connect to bus through transceiver */
> > +	if (!IS_ERR_OR_NULL(gpriv->transceiver)) {
> > +		ret = otg_set_peripheral(gpriv->transceiver->otg,
> > +					&gpriv->gadget);
> > +		if (ret) {
> > +			dev_err(dev, "%s: can't bind to transceiver\n",
> > +				gpriv->gadget.name);
> > +			return ret;
> > +		}
> > +	}
> > +
> 
> How is this change related to the topic ? I'm not too familiar with this
> driver so I might just have missed something, but then others could as well,
> and the commit message could do with a bit more information.
I'm not sure what you mean, this code is needed to tell the phy driver
if it is in host or gadget mode. I would have thought this is clear as it calls
otg_set_peripheral.

> >  	/* first hook up the driver ... */
> >  	gpriv->driver = driver;
> >
> > @@ -900,6 +915,10 @@ static int usbhsg_gadget_stop(struct usb_gadget
> > *gadget) struct usbhs_priv *priv = usbhsg_gpriv_to_priv(gpriv);
> >
> >  	usbhsg_try_stop(priv, USBHSG_STATUS_REGISTERD);
> > +
> > +	if (!IS_ERR_OR_NULL(gpriv->transceiver))
> > +		otg_set_peripheral(gpriv->transceiver->otg, NULL);
> > +
> >  	gpriv->driver = NULL;
> >
> >  	return 0;
> > @@ -947,12 +966,27 @@ static int usbhsg_set_selfpowered(struct usb_gadget
> > *gadget, int is_self) return 0;
> >  }
> >
> > +static int usbhsg_vbus_session(struct usb_gadget *gadget, int is_active)
> > +{
> > +	struct usbhsg_gpriv *gpriv = usbhsg_gadget_to_gpriv(gadget);
> > +	struct usbhs_priv *priv = usbhsg_gpriv_to_priv(gpriv);
> > +	struct platform_device *pdev = usbhs_priv_to_pdev(priv);
> > +
> > +	priv->vbus_is_indirect = 1;
> 
> vbus_is_indirect is never reset to 0. Shouldn't you set it to 1 (or rather to
> true) in usbhs_mod_gadget_probe() if an external transceiver is found ?
Yes, I see what you say.

> What would you think of implementing and wiring up the get_vbus operation in
> usbhs_mod_gadget_probe() in the same way that
> usbhs_mod_autonomy_mode() does,
> and return the value of vbus_indirect_value there ? You could then get rid of
> the vbus_is_indirect field and move the vbus_indirect_value field to struct
> usbhsg_gpriv, removing the need to modify
> drivers/usb/renesas_usbhs/common.c.
> It should also solve the race condition mentioned above.
Ok, I'll have a look at that.
 
> > +	priv->vbus_indirect_value = !!is_active;
> > +
> > +	renesas_usbhs_call_notify_hotplug(pdev);
> > +
> > +	return 0;
> > +}
> > +
> >  static const struct usb_gadget_ops usbhsg_gadget_ops = {
> >  	.get_frame		= usbhsg_get_frame,
> >  	.set_selfpowered	= usbhsg_set_selfpowered,
> >  	.udc_start		= usbhsg_gadget_start,
> >  	.udc_stop		= usbhsg_gadget_stop,
> >  	.pullup			= usbhsg_pullup,
> > +	.vbus_session		= usbhsg_vbus_session,
> >  };
> >
> >  static int usbhsg_start(struct usbhs_priv *priv)
> > @@ -994,6 +1028,10 @@ int usbhs_mod_gadget_probe(struct usbhs_priv
> *priv)
> >  		goto usbhs_mod_gadget_probe_err_gpriv;
> >  	}
> >
> > +	gpriv->transceiver = usb_get_phy(USB_PHY_TYPE_UNDEFINED);
> > +	dev_info(dev, "%s transceiver found\n",
> > +		 gpriv->transceiver ? "" : "No");
> > +
> >  	/*
> >  	 * CAUTION
> >  	 *
> 
> --
> Regards,
> 
> Laurent Pinchart

Thanks
Phil
--
To unsubscribe from this list: send the line "unsubscribe linux-sh" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
diff mbox

Patch

diff --git a/drivers/usb/renesas_usbhs/common.c b/drivers/usb/renesas_usbhs/common.c
index 0f7e850..15e67d8 100644
--- a/drivers/usb/renesas_usbhs/common.c
+++ b/drivers/usb/renesas_usbhs/common.c
@@ -379,7 +379,10 @@  static void usbhsc_hotplug(struct usbhs_priv *priv)
 	/*
 	 * get vbus status from platform
 	 */
-	enable = usbhs_platform_call(priv, get_vbus, pdev);
+	if (priv->vbus_is_indirect)
+		enable = priv->vbus_indirect_value;
+	else
+		enable = usbhs_platform_call(priv, get_vbus, pdev);
 
 	/*
 	 * get id from platform
@@ -662,7 +665,8 @@  static int usbhs_probe(struct platform_device *pdev)
 	pm_runtime_enable(&pdev->dev);
 	if (!usbhsc_flags_has(priv, USBHSF_RUNTIME_PWCTRL)) {
 		usbhsc_power_ctrl(priv, 1);
-		usbhs_mod_autonomy_mode(priv);
+		if (!priv->vbus_is_indirect)
+			usbhs_mod_autonomy_mode(priv);
 	}
 
 	/*
diff --git a/drivers/usb/renesas_usbhs/common.h b/drivers/usb/renesas_usbhs/common.h
index 8c5fc12..478700c 100644
--- a/drivers/usb/renesas_usbhs/common.h
+++ b/drivers/usb/renesas_usbhs/common.h
@@ -255,6 +255,8 @@  struct usbhs_priv {
 	struct renesas_usbhs_driver_param	dparam;
 
 	struct delayed_work notify_hotplug_work;
+	bool vbus_is_indirect;
+	bool vbus_indirect_value;
 	struct platform_device *pdev;
 
 	struct extcon_dev *edev;
diff --git a/drivers/usb/renesas_usbhs/mod_gadget.c b/drivers/usb/renesas_usbhs/mod_gadget.c
index dc2aa32..93ad4ea 100644
--- a/drivers/usb/renesas_usbhs/mod_gadget.c
+++ b/drivers/usb/renesas_usbhs/mod_gadget.c
@@ -21,6 +21,7 @@ 
 #include <linux/platform_device.h>
 #include <linux/usb/ch9.h>
 #include <linux/usb/gadget.h>
+#include <linux/usb/otg.h>
 #include "common.h"
 
 /*
@@ -50,6 +51,7 @@  struct usbhsg_gpriv {
 	int			 uep_size;
 
 	struct usb_gadget_driver	*driver;
+	struct usb_phy		*transceiver;
 
 	u32	status;
 #define USBHSG_STATUS_STARTED		(1 << 0)
@@ -882,12 +884,25 @@  static int usbhsg_gadget_start(struct usb_gadget *gadget,
 {
 	struct usbhsg_gpriv *gpriv = usbhsg_gadget_to_gpriv(gadget);
 	struct usbhs_priv *priv = usbhsg_gpriv_to_priv(gpriv);
+	struct device *dev = usbhs_priv_to_dev(priv);
+	int ret;
 
 	if (!driver		||
 	    !driver->setup	||
 	    driver->max_speed < USB_SPEED_FULL)
 		return -EINVAL;
 
+	/* connect to bus through transceiver */
+	if (!IS_ERR_OR_NULL(gpriv->transceiver)) {
+		ret = otg_set_peripheral(gpriv->transceiver->otg,
+					&gpriv->gadget);
+		if (ret) {
+			dev_err(dev, "%s: can't bind to transceiver\n",
+				gpriv->gadget.name);
+			return ret;
+		}
+	}
+
 	/* first hook up the driver ... */
 	gpriv->driver = driver;
 
@@ -900,6 +915,10 @@  static int usbhsg_gadget_stop(struct usb_gadget *gadget)
 	struct usbhs_priv *priv = usbhsg_gpriv_to_priv(gpriv);
 
 	usbhsg_try_stop(priv, USBHSG_STATUS_REGISTERD);
+
+	if (!IS_ERR_OR_NULL(gpriv->transceiver))
+		otg_set_peripheral(gpriv->transceiver->otg, NULL);
+
 	gpriv->driver = NULL;
 
 	return 0;
@@ -947,12 +966,27 @@  static int usbhsg_set_selfpowered(struct usb_gadget *gadget, int is_self)
 	return 0;
 }
 
+static int usbhsg_vbus_session(struct usb_gadget *gadget, int is_active)
+{
+	struct usbhsg_gpriv *gpriv = usbhsg_gadget_to_gpriv(gadget);
+	struct usbhs_priv *priv = usbhsg_gpriv_to_priv(gpriv);
+	struct platform_device *pdev = usbhs_priv_to_pdev(priv);
+
+	priv->vbus_is_indirect = 1;
+	priv->vbus_indirect_value = !!is_active;
+
+	renesas_usbhs_call_notify_hotplug(pdev);
+
+	return 0;
+}
+
 static const struct usb_gadget_ops usbhsg_gadget_ops = {
 	.get_frame		= usbhsg_get_frame,
 	.set_selfpowered	= usbhsg_set_selfpowered,
 	.udc_start		= usbhsg_gadget_start,
 	.udc_stop		= usbhsg_gadget_stop,
 	.pullup			= usbhsg_pullup,
+	.vbus_session		= usbhsg_vbus_session,
 };
 
 static int usbhsg_start(struct usbhs_priv *priv)
@@ -994,6 +1028,10 @@  int usbhs_mod_gadget_probe(struct usbhs_priv *priv)
 		goto usbhs_mod_gadget_probe_err_gpriv;
 	}
 
+	gpriv->transceiver = usb_get_phy(USB_PHY_TYPE_UNDEFINED);
+	dev_info(dev, "%s transceiver found\n",
+		 gpriv->transceiver ? "" : "No");
+
 	/*
 	 * CAUTION
 	 *