diff mbox series

[2/2] usb: gadget: udc: renesas_usb3: Add support for RZ/V2M

Message ID 20220718134458.19137-3-phil.edworthy@renesas.com
State New
Headers show
Series Add usb gadget support for RZ/V2M | expand

Commit Message

Phil Edworthy July 18, 2022, 1:44 p.m. UTC
RZ/V2M (r9a09g011) has a few differences:
 - The USB3_DRD_CON register has moved, its called USB_PERI_DRD_CON in
   the RZ/V2M hardware manual.
   It has additional bits for host and peripheral reset that need to
   cleared to use usb host and peripheral respectively.
 - The USB3_OTG_STA, USB3_OTG_INT_STA and USB3_OTG_INT_ENA registers
   have been moved and renamed to USB_PERI_DRD_STA, USB_PERI_DRD_INT_STA
   and USB_PERI_DRD_INT_E.
 - The IDMON bit used in the above regs for role detection have moved
   from bit 4 to bit 0.
 - RZ/V2M has an separate interrupt for DRD, i.e. for changes to IDMON.
 - There are reset lines for DRD and USBP
 - There is another clock, managed by runtime PM.

Signed-off-by: Phil Edworthy <phil.edworthy@renesas.com>
Reviewed-by: Biju Das <biju.das.jz@bp.renesas.com>
---
 drivers/usb/gadget/udc/renesas_usb3.c | 109 +++++++++++++++++++++-----
 1 file changed, 91 insertions(+), 18 deletions(-)

Comments

Geert Uytterhoeven July 21, 2022, 12:10 p.m. UTC | #1
Hi Phil,

On Mon, Jul 18, 2022 at 3:45 PM Phil Edworthy <phil.edworthy@renesas.com> wrote:
> RZ/V2M (r9a09g011) has a few differences:
>  - The USB3_DRD_CON register has moved, its called USB_PERI_DRD_CON in
>    the RZ/V2M hardware manual.
>    It has additional bits for host and peripheral reset that need to
>    cleared to use usb host and peripheral respectively.
>  - The USB3_OTG_STA, USB3_OTG_INT_STA and USB3_OTG_INT_ENA registers
>    have been moved and renamed to USB_PERI_DRD_STA, USB_PERI_DRD_INT_STA
>    and USB_PERI_DRD_INT_E.
>  - The IDMON bit used in the above regs for role detection have moved
>    from bit 4 to bit 0.
>  - RZ/V2M has an separate interrupt for DRD, i.e. for changes to IDMON.
>  - There are reset lines for DRD and USBP
>  - There is another clock, managed by runtime PM.
>
> Signed-off-by: Phil Edworthy <phil.edworthy@renesas.com>
> Reviewed-by: Biju Das <biju.das.jz@bp.renesas.com>

Thanks for your patch!

> --- a/drivers/usb/gadget/udc/renesas_usb3.c
> +++ b/drivers/usb/gadget/udc/renesas_usb3.c

> @@ -363,6 +368,7 @@ struct renesas_usb3 {
>         bool forced_b_device;
>         bool start_to_connect;
>         bool role_sw_by_connector;
> +       bool r9a09g011;

Any better name for this feature flag?

>  };

> @@ -2707,6 +2733,12 @@ static const struct renesas_usb3_priv renesas_usb3_priv_r8a77990 = {
>         .workaround_for_vbus = true,
>  };
>
> +static const struct renesas_usb3_priv renesas_usb3_priv_r9a09g011 = {

renesas_usb3_priv_rzv2m?

> +       .ramsize_per_ramif = SZ_32K,
> +       .num_ramif = 1,
> +       .ramsize_per_pipe = SZ_4K,
> +};
> +
>  static const struct of_device_id usb3_of_match[] = {
>         {
>                 .compatible = "renesas,r8a774c0-usb3-peri",
> @@ -2717,6 +2749,9 @@ static const struct of_device_id usb3_of_match[] = {
>         }, {
>                 .compatible = "renesas,r8a77990-usb3-peri",
>                 .data = &renesas_usb3_priv_r8a77990,
> +       }, {
> +               .compatible = "renesas,r9a09g011-usb3-peri",

As the bindings include a family-specific compatible value, you should
use that ("renesas,rzv2m-usb3-peri") here.

> +               .data = &renesas_usb3_priv_r9a09g011,
>         }, {
>                 .compatible = "renesas,rcar-gen3-usb3-peri",
>                 .data = &renesas_usb3_priv_gen3,

> @@ -2758,13 +2793,22 @@ static int renesas_usb3_probe(struct platform_device *pdev)
>         else
>                 priv = of_device_get_match_data(&pdev->dev);
>
> +       usb3 = devm_kzalloc(&pdev->dev, sizeof(*usb3), GFP_KERNEL);
> +       if (!usb3)
> +               return -ENOMEM;
> +
> +       if (priv == &renesas_usb3_priv_r9a09g011)

Please store the feature flag in renesas_usb3_priv instead of doing an
explicit comparison.

> +               usb3->r9a09g011 = true;
> +

Gr{oetje,eeting}s,

                        Geert

--
Geert Uytterhoeven -- There's lots of Linux beyond ia32 -- geert@linux-m68k.org

In personal conversations with technical people, I call myself a hacker. But
when I'm talking to journalists I just say "programmer" or something like that.
                                -- Linus Torvalds
Phil Edworthy July 21, 2022, 12:25 p.m. UTC | #2
Hi Geert,

Thanks for your review!

On 21 July 2022 13:10 Geert Uytterhoeven wrote:
> On Mon, Jul 18, 2022 at 3:45 PM Phil Edworthy wrote:
> > RZ/V2M (r9a09g011) has a few differences:
> >  - The USB3_DRD_CON register has moved, its called USB_PERI_DRD_CON in
> >    the RZ/V2M hardware manual.
> >    It has additional bits for host and peripheral reset that need to
> >    cleared to use usb host and peripheral respectively.
> >  - The USB3_OTG_STA, USB3_OTG_INT_STA and USB3_OTG_INT_ENA registers
> >    have been moved and renamed to USB_PERI_DRD_STA, USB_PERI_DRD_INT_STA
> >    and USB_PERI_DRD_INT_E.
> >  - The IDMON bit used in the above regs for role detection have moved
> >    from bit 4 to bit 0.
> >  - RZ/V2M has an separate interrupt for DRD, i.e. for changes to IDMON.
> >  - There are reset lines for DRD and USBP
> >  - There is another clock, managed by runtime PM.
> >
> > Signed-off-by: Phil Edworthy <phil.edworthy@renesas.com>
> > Reviewed-by: Biju Das <biju.das.jz@bp.renesas.com>
> 
> Thanks for your patch!
> 
> > --- a/drivers/usb/gadget/udc/renesas_usb3.c
> > +++ b/drivers/usb/gadget/udc/renesas_usb3.c
> 
> > @@ -363,6 +368,7 @@ struct renesas_usb3 {
> >         bool forced_b_device;
> >         bool start_to_connect;
> >         bool role_sw_by_connector;
> > +       bool r9a09g011;
> 
> Any better name for this feature flag?
Nothing springs to mind. We could use separate flags for has_resets,
has_drd_irq, max_nr_pipes but I am struggling to come up with names
for the offset registers and moved bits. Any suggestions?


> >  };
> 
> > @@ -2707,6 +2733,12 @@ static const struct renesas_usb3_priv
> renesas_usb3_priv_r8a77990 = {
> >         .workaround_for_vbus = true,
> >  };
> >
> > +static const struct renesas_usb3_priv renesas_usb3_priv_r9a09g011 = {
> 
> renesas_usb3_priv_rzv2m?
Ok

> > +       .ramsize_per_ramif = SZ_32K,
> > +       .num_ramif = 1,
> > +       .ramsize_per_pipe = SZ_4K,
> > +};
> > +
> >  static const struct of_device_id usb3_of_match[] = {
> >         {
> >                 .compatible = "renesas,r8a774c0-usb3-peri",
> > @@ -2717,6 +2749,9 @@ static const struct of_device_id usb3_of_match[] =
> {
> >         }, {
> >                 .compatible = "renesas,r8a77990-usb3-peri",
> >                 .data = &renesas_usb3_priv_r8a77990,
> > +       }, {
> > +               .compatible = "renesas,r9a09g011-usb3-peri",
> 
> As the bindings include a family-specific compatible value, you should
> use that ("renesas,rzv2m-usb3-peri") here.
Ok

> > +               .data = &renesas_usb3_priv_r9a09g011,
> >         }, {
> >                 .compatible = "renesas,rcar-gen3-usb3-peri",
> >                 .data = &renesas_usb3_priv_gen3,
> 
> > @@ -2758,13 +2793,22 @@ static int renesas_usb3_probe(struct
> platform_device *pdev)
> >         else
> >                 priv = of_device_get_match_data(&pdev->dev);
> >
> > +       usb3 = devm_kzalloc(&pdev->dev, sizeof(*usb3), GFP_KERNEL);
> > +       if (!usb3)
> > +               return -ENOMEM;
> > +
> > +       if (priv == &renesas_usb3_priv_r9a09g011)
> 
> Please store the feature flag in renesas_usb3_priv instead of doing an
> explicit comparison.
Ok, will do!

> > +               usb3->r9a09g011 = true;
> > +

Thanks
Phil
Geert Uytterhoeven July 21, 2022, 12:42 p.m. UTC | #3
Hi Phil,

On Thu, Jul 21, 2022 at 2:25 PM Phil Edworthy <phil.edworthy@renesas.com> wrote:
> On 21 July 2022 13:10 Geert Uytterhoeven wrote:
> > On Mon, Jul 18, 2022 at 3:45 PM Phil Edworthy wrote:
> > > RZ/V2M (r9a09g011) has a few differences:
> > >  - The USB3_DRD_CON register has moved, its called USB_PERI_DRD_CON in
> > >    the RZ/V2M hardware manual.
> > >    It has additional bits for host and peripheral reset that need to
> > >    cleared to use usb host and peripheral respectively.
> > >  - The USB3_OTG_STA, USB3_OTG_INT_STA and USB3_OTG_INT_ENA registers
> > >    have been moved and renamed to USB_PERI_DRD_STA, USB_PERI_DRD_INT_STA
> > >    and USB_PERI_DRD_INT_E.
> > >  - The IDMON bit used in the above regs for role detection have moved
> > >    from bit 4 to bit 0.
> > >  - RZ/V2M has an separate interrupt for DRD, i.e. for changes to IDMON.
> > >  - There are reset lines for DRD and USBP
> > >  - There is another clock, managed by runtime PM.
> > >
> > > Signed-off-by: Phil Edworthy <phil.edworthy@renesas.com>
> > > Reviewed-by: Biju Das <biju.das.jz@bp.renesas.com>
> >
> > Thanks for your patch!
> >
> > > --- a/drivers/usb/gadget/udc/renesas_usb3.c
> > > +++ b/drivers/usb/gadget/udc/renesas_usb3.c
> >
> > > @@ -363,6 +368,7 @@ struct renesas_usb3 {
> > >         bool forced_b_device;
> > >         bool start_to_connect;
> > >         bool role_sw_by_connector;
> > > +       bool r9a09g011;
> >
> > Any better name for this feature flag?
> Nothing springs to mind. We could use separate flags for has_resets,
> has_drd_irq, max_nr_pipes but I am struggling to come up with names
> for the offset registers and moved bits. Any suggestions?

OK, so "is_rzv2m"?

Gr{oetje,eeting}s,

                        Geert

--
Geert Uytterhoeven -- There's lots of Linux beyond ia32 -- geert@linux-m68k.org

In personal conversations with technical people, I call myself a hacker. But
when I'm talking to journalists I just say "programmer" or something like that.
                                -- Linus Torvalds
Phil Edworthy July 21, 2022, 12:44 p.m. UTC | #4
Hi Geert,

On 21 July 2022 13:43 Geert Uytterhoeven wrote:
> On Thu, Jul 21, 2022 at 2:25 PM Phil Edworthy wrote:
> > On 21 July 2022 13:10 Geert Uytterhoeven wrote:
> > > On Mon, Jul 18, 2022 at 3:45 PM Phil Edworthy wrote:
> > > > RZ/V2M (r9a09g011) has a few differences:
> > > >  - The USB3_DRD_CON register has moved, its called USB_PERI_DRD_CON
> in
> > > >    the RZ/V2M hardware manual.
> > > >    It has additional bits for host and peripheral reset that need to
> > > >    cleared to use usb host and peripheral respectively.
> > > >  - The USB3_OTG_STA, USB3_OTG_INT_STA and USB3_OTG_INT_ENA registers
> > > >    have been moved and renamed to USB_PERI_DRD_STA,
> USB_PERI_DRD_INT_STA
> > > >    and USB_PERI_DRD_INT_E.
> > > >  - The IDMON bit used in the above regs for role detection have
> moved
> > > >    from bit 4 to bit 0.
> > > >  - RZ/V2M has an separate interrupt for DRD, i.e. for changes to
> IDMON.
> > > >  - There are reset lines for DRD and USBP
> > > >  - There is another clock, managed by runtime PM.
> > > >
> > > > Signed-off-by: Phil Edworthy <phil.edworthy@renesas.com>
> > > > Reviewed-by: Biju Das <biju.das.jz@bp.renesas.com>
> > >
> > > Thanks for your patch!
> > >
> > > > --- a/drivers/usb/gadget/udc/renesas_usb3.c
> > > > +++ b/drivers/usb/gadget/udc/renesas_usb3.c
> > >
> > > > @@ -363,6 +368,7 @@ struct renesas_usb3 {
> > > >         bool forced_b_device;
> > > >         bool start_to_connect;
> > > >         bool role_sw_by_connector;
> > > > +       bool r9a09g011;
> > >
> > > Any better name for this feature flag?
> > Nothing springs to mind. We could use separate flags for has_resets,
> > has_drd_irq, max_nr_pipes but I am struggling to come up with names
> > for the offset registers and moved bits. Any suggestions?
> 
> OK, so "is_rzv2m"?
Ok!

Thanks
Phil
Yoshihiro Shimoda July 21, 2022, 12:51 p.m. UTC | #5
Hi Geert-san, Phil-san,

Thank you for the patch!

> From: Geert Uytterhoeven, Sent: Thursday, July 21, 2022 9:43 PM
> 
> Hi Phil,
> 
> On Thu, Jul 21, 2022 at 2:25 PM Phil Edworthy <phil.edworthy@renesas.com> wrote:
> > On 21 July 2022 13:10 Geert Uytterhoeven wrote:
> > > On Mon, Jul 18, 2022 at 3:45 PM Phil Edworthy wrote:
> > > > RZ/V2M (r9a09g011) has a few differences:
> > > >  - The USB3_DRD_CON register has moved, its called USB_PERI_DRD_CON in
> > > >    the RZ/V2M hardware manual.
> > > >    It has additional bits for host and peripheral reset that need to
> > > >    cleared to use usb host and peripheral respectively.
> > > >  - The USB3_OTG_STA, USB3_OTG_INT_STA and USB3_OTG_INT_ENA registers
> > > >    have been moved and renamed to USB_PERI_DRD_STA, USB_PERI_DRD_INT_STA
> > > >    and USB_PERI_DRD_INT_E.
> > > >  - The IDMON bit used in the above regs for role detection have moved
> > > >    from bit 4 to bit 0.
> > > >  - RZ/V2M has an separate interrupt for DRD, i.e. for changes to IDMON.
> > > >  - There are reset lines for DRD and USBP
> > > >  - There is another clock, managed by runtime PM.
> > > >
> > > > Signed-off-by: Phil Edworthy <phil.edworthy@renesas.com>
> > > > Reviewed-by: Biju Das <biju.das.jz@bp.renesas.com>
> > >
> > > Thanks for your patch!
> > >
> > > > --- a/drivers/usb/gadget/udc/renesas_usb3.c
> > > > +++ b/drivers/usb/gadget/udc/renesas_usb3.c
> > >
> > > > @@ -363,6 +368,7 @@ struct renesas_usb3 {
> > > >         bool forced_b_device;
> > > >         bool start_to_connect;
> > > >         bool role_sw_by_connector;
> > > > +       bool r9a09g011;
> > >
> > > Any better name for this feature flag?
> > Nothing springs to mind. We could use separate flags for has_resets,
> > has_drd_irq, max_nr_pipes but I am struggling to come up with names
> > for the offset registers and moved bits. Any suggestions?
> 
> OK, so "is_rzv2m"?

The flag name looks good to me. However, I don't like the following usage in macros.
---
-#define USB3_DRD_CON		0x218
+#define USB3_DRD_CON		(usb3->r9a09g011 ? 0x400 : 0x218)
...
-#define USB_OTG_IDMON		BIT(4)
+#define USB_OTG_IDMON		(usb3->r9a09g011 ? BIT(0) : BIT(4))
----

About registers' offset/bit, I think having specific members into
a new struct is better like below. But, what do you think?

struct renesas_usb3_reg {
	u16 drd_con_offset;
	...
	u32 otg_idmon_bit;
	...
};

struct renesas_usb3 {
	...
	struct renesas_usb3_reg regs;
	...
};

Best regards,
Yoshihiro Shimoda
Phil Edworthy July 21, 2022, 2:21 p.m. UTC | #6
Hi Yoshihiro,

On 21 July 2022 13:51 Yoshihiro Shimoda wrote:
> > From: Geert Uytterhoeven, Sent: Thursday, July 21, 2022 9:43 PM
> > On Thu, Jul 21, 2022 at 2:25 PM Phil Edworthy wrote:
> > > On 21 July 2022 13:10 Geert Uytterhoeven wrote:
> > > > On Mon, Jul 18, 2022 at 3:45 PM Phil Edworthy wrote:
> > > > > RZ/V2M (r9a09g011) has a few differences:
> > > > >  - The USB3_DRD_CON register has moved, its called
> USB_PERI_DRD_CON in
> > > > >    the RZ/V2M hardware manual.
> > > > >    It has additional bits for host and peripheral reset that need
> to
> > > > >    cleared to use usb host and peripheral respectively.
> > > > >  - The USB3_OTG_STA, USB3_OTG_INT_STA and USB3_OTG_INT_ENA
> registers
> > > > >    have been moved and renamed to USB_PERI_DRD_STA,
> USB_PERI_DRD_INT_STA
> > > > >    and USB_PERI_DRD_INT_E.
> > > > >  - The IDMON bit used in the above regs for role detection have
> moved
> > > > >    from bit 4 to bit 0.
> > > > >  - RZ/V2M has an separate interrupt for DRD, i.e. for changes to
> IDMON.
> > > > >  - There are reset lines for DRD and USBP
> > > > >  - There is another clock, managed by runtime PM.
> > > > >
> > > > > Signed-off-by: Phil Edworthy <phil.edworthy@renesas.com>
> > > > > Reviewed-by: Biju Das <biju.das.jz@bp.renesas.com>
> > > >
> > > > Thanks for your patch!
> > > >
> > > > > --- a/drivers/usb/gadget/udc/renesas_usb3.c
> > > > > +++ b/drivers/usb/gadget/udc/renesas_usb3.c
> > > >
> > > > > @@ -363,6 +368,7 @@ struct renesas_usb3 {
> > > > >         bool forced_b_device;
> > > > >         bool start_to_connect;
> > > > >         bool role_sw_by_connector;
> > > > > +       bool r9a09g011;
> > > >
> > > > Any better name for this feature flag?
> > > Nothing springs to mind. We could use separate flags for has_resets,
> > > has_drd_irq, max_nr_pipes but I am struggling to come up with names
> > > for the offset registers and moved bits. Any suggestions?
> >
> > OK, so "is_rzv2m"?
> 
> The flag name looks good to me. However, I don't like the following usage
> in macros.
> ---
> -#define USB3_DRD_CON		0x218
> +#define USB3_DRD_CON		(usb3->r9a09g011 ? 0x400 : 0x218)
> ...
> -#define USB_OTG_IDMON		BIT(4)
> +#define USB_OTG_IDMON		(usb3->r9a09g011 ? BIT(0) : BIT(4))
> ----
> 
> About registers' offset/bit, I think having specific members into
> a new struct is better like below. But, what do you think?
> 
> struct renesas_usb3_reg {
> 	u16 drd_con_offset;
> 	...
> 	u32 otg_idmon_bit;
> 	...
> };
> 
> struct renesas_usb3 {
> 	...
> 	struct renesas_usb3_reg regs;
> 	...
> };

I think that might be a bit overly complex for the problem.
How about:
#define USB3_DRD_CON(p)	((p)->is_rzv2m ? 0x400 : 0x218)

Thanks
Phil
Yoshihiro Shimoda July 21, 2022, 10:38 p.m. UTC | #7
Hi Phil-san,

> From: Phil Edworthy, Sent: Thursday, July 21, 2022 11:21 PM
> 
> Hi Yoshihiro,
> 
> On 21 July 2022 13:51 Yoshihiro Shimoda wrote:
> > > From: Geert Uytterhoeven, Sent: Thursday, July 21, 2022 9:43 PM
> > > On Thu, Jul 21, 2022 at 2:25 PM Phil Edworthy wrote:
> > > > On 21 July 2022 13:10 Geert Uytterhoeven wrote:
> > > > > On Mon, Jul 18, 2022 at 3:45 PM Phil Edworthy wrote:
> > > > > > RZ/V2M (r9a09g011) has a few differences:
> > > > > >  - The USB3_DRD_CON register has moved, its called
> > USB_PERI_DRD_CON in
> > > > > >    the RZ/V2M hardware manual.
> > > > > >    It has additional bits for host and peripheral reset that need
> > to
> > > > > >    cleared to use usb host and peripheral respectively.
> > > > > >  - The USB3_OTG_STA, USB3_OTG_INT_STA and USB3_OTG_INT_ENA
> > registers
> > > > > >    have been moved and renamed to USB_PERI_DRD_STA,
> > USB_PERI_DRD_INT_STA
> > > > > >    and USB_PERI_DRD_INT_E.
> > > > > >  - The IDMON bit used in the above regs for role detection have
> > moved
> > > > > >    from bit 4 to bit 0.
> > > > > >  - RZ/V2M has an separate interrupt for DRD, i.e. for changes to
> > IDMON.
> > > > > >  - There are reset lines for DRD and USBP
> > > > > >  - There is another clock, managed by runtime PM.
> > > > > >
> > > > > > Signed-off-by: Phil Edworthy <phil.edworthy@renesas.com>
> > > > > > Reviewed-by: Biju Das <biju.das.jz@bp.renesas.com>
> > > > >
> > > > > Thanks for your patch!
> > > > >
> > > > > > --- a/drivers/usb/gadget/udc/renesas_usb3.c
> > > > > > +++ b/drivers/usb/gadget/udc/renesas_usb3.c
> > > > >
> > > > > > @@ -363,6 +368,7 @@ struct renesas_usb3 {
> > > > > >         bool forced_b_device;
> > > > > >         bool start_to_connect;
> > > > > >         bool role_sw_by_connector;
> > > > > > +       bool r9a09g011;
> > > > >
> > > > > Any better name for this feature flag?
> > > > Nothing springs to mind. We could use separate flags for has_resets,
> > > > has_drd_irq, max_nr_pipes but I am struggling to come up with names
> > > > for the offset registers and moved bits. Any suggestions?
> > >
> > > OK, so "is_rzv2m"?
> >
> > The flag name looks good to me. However, I don't like the following usage
> > in macros.
> > ---
> > -#define USB3_DRD_CON		0x218
> > +#define USB3_DRD_CON		(usb3->r9a09g011 ? 0x400 : 0x218)
> > ...
> > -#define USB_OTG_IDMON		BIT(4)
> > +#define USB_OTG_IDMON		(usb3->r9a09g011 ? BIT(0) : BIT(4))
> > ----
> >
> > About registers' offset/bit, I think having specific members into
> > a new struct is better like below. But, what do you think?
> >
> > struct renesas_usb3_reg {
> > 	u16 drd_con_offset;
> > 	...
> > 	u32 otg_idmon_bit;
> > 	...
> > };
> >
> > struct renesas_usb3 {
> > 	...
> > 	struct renesas_usb3_reg regs;
> > 	...
> > };
> 
> I think that might be a bit overly complex for the problem.
> How about:
> #define USB3_DRD_CON(p)	((p)->is_rzv2m ? 0x400 : 0x218)

Thank you for your suggestion. It looks good to me.

Best regards,
Yoshihiro Shimoda
diff mbox series

Patch

diff --git a/drivers/usb/gadget/udc/renesas_usb3.c b/drivers/usb/gadget/udc/renesas_usb3.c
index 648be3fd476a..ede2af06ac30 100644
--- a/drivers/usb/gadget/udc/renesas_usb3.c
+++ b/drivers/usb/gadget/udc/renesas_usb3.c
@@ -17,6 +17,7 @@ 
 #include <linux/phy/phy.h>
 #include <linux/platform_device.h>
 #include <linux/pm_runtime.h>
+#include <linux/reset.h>
 #include <linux/sizes.h>
 #include <linux/slab.h>
 #include <linux/string.h>
@@ -38,16 +39,16 @@ 
 #define USB3_USB20_CON		0x204
 #define USB3_USB30_CON		0x208
 #define USB3_USB_STA		0x210
-#define USB3_DRD_CON		0x218
+#define USB3_DRD_CON		(usb3->r9a09g011 ? 0x400 : 0x218)
 #define USB3_USB_INT_STA_1	0x220
 #define USB3_USB_INT_STA_2	0x224
 #define USB3_USB_INT_ENA_1	0x228
 #define USB3_USB_INT_ENA_2	0x22c
 #define USB3_STUP_DAT_0		0x230
 #define USB3_STUP_DAT_1		0x234
-#define USB3_USB_OTG_STA	0x268
-#define USB3_USB_OTG_INT_STA	0x26c
-#define USB3_USB_OTG_INT_ENA	0x270
+#define USB3_USB_OTG_STA	(usb3->r9a09g011 ? 0x410 : 0x268)
+#define USB3_USB_OTG_INT_STA	(usb3->r9a09g011 ? 0x414 : 0x26c)
+#define USB3_USB_OTG_INT_ENA	(usb3->r9a09g011 ? 0x418 : 0x270)
 #define USB3_P0_MOD		0x280
 #define USB3_P0_CON		0x288
 #define USB3_P0_STA		0x28c
@@ -135,6 +136,8 @@ 
 #define USB_STA_VBUS_STA	BIT(0)
 
 /* DRD_CON */
+#define DRD_CON_PERI_RST	BIT(31)		/* r9a09g011 only */
+#define DRD_CON_HOST_RST	BIT(30)		/* r9a09g011 only */
 #define DRD_CON_PERI_CON	BIT(24)
 #define DRD_CON_VBOUT		BIT(0)
 
@@ -155,7 +158,7 @@ 
 #define USB_INT_2_PIPE(n)	BIT(n)
 
 /* USB_OTG_STA, USB_OTG_INT_STA and USB_OTG_INT_ENA */
-#define USB_OTG_IDMON		BIT(4)
+#define USB_OTG_IDMON		(usb3->r9a09g011 ? BIT(0) : BIT(4))
 
 /* P0_MOD */
 #define P0_MOD_DIR		BIT(6)
@@ -255,7 +258,7 @@ 
 #define USB3_EP0_SS_MAX_PACKET_SIZE	512
 #define USB3_EP0_HSFS_MAX_PACKET_SIZE	64
 #define USB3_EP0_BUF_SIZE		8
-#define USB3_MAX_NUM_PIPES		6	/* This includes PIPE 0 */
+#define USB3_MAX_NUM_PIPES		(usb3->r9a09g011 ? 9 : 6)	/* This includes PIPE 0 */
 #define USB3_WAIT_US			3
 #define USB3_DMA_NUM_SETTING_AREA	4
 /*
@@ -330,6 +333,8 @@  struct renesas_usb3_priv {
 
 struct renesas_usb3 {
 	void __iomem *reg;
+	struct reset_control *drd_rstc;
+	struct reset_control *usbp_rstc;
 
 	struct usb_gadget gadget;
 	struct usb_gadget_driver *driver;
@@ -363,6 +368,7 @@  struct renesas_usb3 {
 	bool forced_b_device;
 	bool start_to_connect;
 	bool role_sw_by_connector;
+	bool r9a09g011;
 };
 
 #define gadget_to_renesas_usb3(_gadget)	\
@@ -674,6 +680,13 @@  static void renesas_usb3_role_work(struct work_struct *work)
 
 static void usb3_set_mode(struct renesas_usb3 *usb3, bool host)
 {
+	if (usb3->r9a09g011) {
+		if (host)
+			usb3_clear_bit(usb3, DRD_CON_HOST_RST, USB3_DRD_CON);
+		else
+			usb3_clear_bit(usb3, DRD_CON_PERI_RST, USB3_DRD_CON);
+	}
+
 	if (host)
 		usb3_clear_bit(usb3, DRD_CON_PERI_CON, USB3_DRD_CON);
 	else
@@ -2005,8 +2018,14 @@  static void usb3_irq_idmon_change(struct renesas_usb3 *usb3)
 	usb3_check_id(usb3);
 }
 
-static void usb3_irq_otg_int(struct renesas_usb3 *usb3, u32 otg_int_sta)
+static void usb3_irq_otg_int(struct renesas_usb3 *usb3)
 {
+	u32 otg_int_sta = usb3_read(usb3, USB3_USB_OTG_INT_STA);
+
+	otg_int_sta &= usb3_read(usb3, USB3_USB_OTG_INT_ENA);
+	if (otg_int_sta)
+		usb3_write(usb3, otg_int_sta, USB3_USB_OTG_INT_STA);
+
 	if (otg_int_sta & USB_OTG_IDMON)
 		usb3_irq_idmon_change(usb3);
 }
@@ -2015,7 +2034,6 @@  static void usb3_irq_epc(struct renesas_usb3 *usb3)
 {
 	u32 int_sta_1 = usb3_read(usb3, USB3_USB_INT_STA_1);
 	u32 int_sta_2 = usb3_read(usb3, USB3_USB_INT_STA_2);
-	u32 otg_int_sta = usb3_read(usb3, USB3_USB_OTG_INT_STA);
 
 	int_sta_1 &= usb3_read(usb3, USB3_USB_INT_ENA_1);
 	if (int_sta_1) {
@@ -2027,11 +2045,8 @@  static void usb3_irq_epc(struct renesas_usb3 *usb3)
 	if (int_sta_2)
 		usb3_irq_epc_int_2(usb3, int_sta_2);
 
-	otg_int_sta &= usb3_read(usb3, USB3_USB_OTG_INT_ENA);
-	if (otg_int_sta) {
-		usb3_write(usb3, otg_int_sta, USB3_USB_OTG_INT_STA);
-		usb3_irq_otg_int(usb3, otg_int_sta);
-	}
+	if (!usb3->r9a09g011)
+		usb3_irq_otg_int(usb3);
 }
 
 static void usb3_irq_dma_int(struct renesas_usb3 *usb3, u32 dma_sta)
@@ -2085,6 +2100,15 @@  static irqreturn_t renesas_usb3_irq(int irq, void *_usb3)
 	return ret;
 }
 
+static irqreturn_t renesas_usb3_otg_irq(int irq, void *_usb3)
+{
+	struct renesas_usb3 *usb3 = _usb3;
+
+	usb3_irq_otg_int(usb3);
+
+	return IRQ_HANDLED;
+}
+
 static void usb3_write_pn_mod(struct renesas_usb3_ep *usb3_ep,
 			      const struct usb_endpoint_descriptor *desc)
 {
@@ -2571,6 +2595,8 @@  static int renesas_usb3_remove(struct platform_device *pdev)
 	usb_role_switch_unregister(usb3->role_sw);
 
 	usb_del_gadget_udc(&usb3->gadget);
+	reset_control_assert(usb3->usbp_rstc);
+	reset_control_assert(usb3->drd_rstc);
 	renesas_usb3_dma_free_prd(usb3, &pdev->dev);
 
 	__renesas_usb3_ep_free_request(usb3->ep0_req);
@@ -2707,6 +2733,12 @@  static const struct renesas_usb3_priv renesas_usb3_priv_r8a77990 = {
 	.workaround_for_vbus = true,
 };
 
+static const struct renesas_usb3_priv renesas_usb3_priv_r9a09g011 = {
+	.ramsize_per_ramif = SZ_32K,
+	.num_ramif = 1,
+	.ramsize_per_pipe = SZ_4K,
+};
+
 static const struct of_device_id usb3_of_match[] = {
 	{
 		.compatible = "renesas,r8a774c0-usb3-peri",
@@ -2717,6 +2749,9 @@  static const struct of_device_id usb3_of_match[] = {
 	}, {
 		.compatible = "renesas,r8a77990-usb3-peri",
 		.data = &renesas_usb3_priv_r8a77990,
+	}, {
+		.compatible = "renesas,r9a09g011-usb3-peri",
+		.data = &renesas_usb3_priv_r9a09g011,
 	}, {
 		.compatible = "renesas,rcar-gen3-usb3-peri",
 		.data = &renesas_usb3_priv_gen3,
@@ -2748,7 +2783,7 @@  static struct usb_role_switch_desc renesas_usb3_role_switch_desc = {
 static int renesas_usb3_probe(struct platform_device *pdev)
 {
 	struct renesas_usb3 *usb3;
-	int irq, ret;
+	int irq, drd_irq, ret;
 	const struct renesas_usb3_priv *priv;
 	const struct soc_device_attribute *attr;
 
@@ -2758,13 +2793,22 @@  static int renesas_usb3_probe(struct platform_device *pdev)
 	else
 		priv = of_device_get_match_data(&pdev->dev);
 
+	usb3 = devm_kzalloc(&pdev->dev, sizeof(*usb3), GFP_KERNEL);
+	if (!usb3)
+		return -ENOMEM;
+
+	if (priv == &renesas_usb3_priv_r9a09g011)
+		usb3->r9a09g011 = true;
+
 	irq = platform_get_irq(pdev, 0);
 	if (irq < 0)
 		return irq;
 
-	usb3 = devm_kzalloc(&pdev->dev, sizeof(*usb3), GFP_KERNEL);
-	if (!usb3)
-		return -ENOMEM;
+	if (usb3->r9a09g011) {
+		drd_irq = platform_get_irq_byname(pdev, "drd");
+		if (drd_irq < 0)
+			return drd_irq;
+	}
 
 	usb3->reg = devm_platform_ioremap_resource(pdev, 0);
 	if (IS_ERR(usb3->reg))
@@ -2787,6 +2831,14 @@  static int renesas_usb3_probe(struct platform_device *pdev)
 	if (ret < 0)
 		return ret;
 
+	if (usb3->r9a09g011) {
+		ret = devm_request_irq(&pdev->dev, drd_irq,
+				       renesas_usb3_otg_irq, 0,
+				       dev_name(&pdev->dev), usb3);
+		if (ret < 0)
+			return ret;
+	}
+
 	INIT_WORK(&usb3->extcon_work, renesas_usb3_extcon_work);
 	usb3->extcon = devm_extcon_dev_allocate(&pdev->dev, renesas_usb3_cable);
 	if (IS_ERR(usb3->extcon))
@@ -2817,10 +2869,27 @@  static int renesas_usb3_probe(struct platform_device *pdev)
 		goto err_add_udc;
 	}
 
+	usb3->drd_rstc = devm_reset_control_get_optional_shared(&pdev->dev,
+								"drd_reset");
+	if (IS_ERR(usb3->drd_rstc)) {
+		ret = PTR_ERR(usb3->drd_rstc);
+		goto err_add_udc;
+	}
+
+	usb3->usbp_rstc = devm_reset_control_get_optional_shared(&pdev->dev,
+								 "aresetn_p");
+	if (IS_ERR(usb3->usbp_rstc)) {
+		ret = PTR_ERR(usb3->usbp_rstc);
+		goto err_add_udc;
+	}
+
+	reset_control_deassert(usb3->drd_rstc);
+	reset_control_deassert(usb3->usbp_rstc);
+
 	pm_runtime_enable(&pdev->dev);
 	ret = usb_add_gadget_udc(&pdev->dev, &usb3->gadget);
 	if (ret < 0)
-		goto err_add_udc;
+		goto err_reset;
 
 	ret = device_create_file(&pdev->dev, &dev_attr_role);
 	if (ret < 0)
@@ -2858,6 +2927,10 @@  static int renesas_usb3_probe(struct platform_device *pdev)
 err_dev_create:
 	usb_del_gadget_udc(&usb3->gadget);
 
+err_reset:
+	reset_control_assert(usb3->usbp_rstc);
+	reset_control_assert(usb3->drd_rstc);
+
 err_add_udc:
 	renesas_usb3_dma_free_prd(usb3, &pdev->dev);