diff mbox series

[5/8] xhci: mediatek: Add support for MTK xHCI host controller

Message ID 1583909445-27444-6-git-send-email-chunfeng.yun@mediatek.com
State New
Headers show
Series Add support for MediaTek xHCI host controller | expand

Commit Message

Chunfeng Yun (云春峰) March 11, 2020, 6:50 a.m. UTC
This patch is used to support the on-chip xHCI controller on
MediaTek SoCs, currently only control/bulk transfers are
supported.

Signed-off-by: Chunfeng Yun <chunfeng.yun at mediatek.com>
---
 drivers/usb/host/Kconfig    |   6 +
 drivers/usb/host/Makefile   |   1 +
 drivers/usb/host/xhci-mtk.c | 508 ++++++++++++++++++++++++++++++++++++
 3 files changed, 515 insertions(+)
 create mode 100644 drivers/usb/host/xhci-mtk.c

Comments

Marek Vasut March 11, 2020, 7:11 a.m. UTC | #1
On 3/11/20 7:50 AM, Chunfeng Yun wrote:
[...]
> + * @u3_ctrl_p[x]: ip usb3 port x control register, only low 4bytes are used
> + * @u2_ctrl_p[x]: ip usb2 port x control register, only low 4bytes are used
> + * @u2_phy_pll: usb2 phy pll control register
> + */
> +struct mtk_ippc_regs {
> +	__le32 ip_pw_ctr0;
> +	__le32 ip_pw_ctr1;
> +	__le32 ip_pw_ctr2;

Please define the registers with #define macros , this struct-based
approach doesn't scale.

[..]

> +static int xhci_mtk_host_enable(struct mtk_xhci *mtk)
> +{
> +	struct mtk_ippc_regs *ippc = mtk->ippc;
> +	u32 value, check_val;
> +	int ret;
> +	int i;
> +
> +	/* power on host ip */
> +	value = readl(&ippc->ip_pw_ctr1);
> +	value &= ~CTRL1_IP_HOST_PDN;
> +	writel(value, &ippc->ip_pw_ctr1);
> +
> +	/* power on and enable all u3 ports */
> +	for (i = 0; i < mtk->num_u3_ports; i++) {
> +		value = readl(&ippc->u3_ctrl_p[i]);
> +		value &= ~(CTRL_U3_PORT_PDN | CTRL_U3_PORT_DIS);
> +		value |= CTRL_U3_PORT_HOST_SEL;
> +		writel(value, &ippc->u3_ctrl_p[i]);
> +	}

Use clrsetbits_le32() above and below and where applicable.

> +	/* power on and enable all u2 ports */
> +	for (i = 0; i < mtk->num_u2_ports; i++) {
> +		value = readl(&ippc->u2_ctrl_p[i]);
> +		value &= ~(CTRL_U2_PORT_PDN | CTRL_U2_PORT_DIS);
> +		value |= CTRL_U2_PORT_HOST_SEL;
> +		writel(value, &ippc->u2_ctrl_p[i]);
> +	}

[...]

> +static int xhci_mtk_clks_enable(struct mtk_xhci *mtk)
> +{
> +	int ret;
> +
> +	ret = clk_enable(mtk->sys_clk);
> +	if (ret) {
> +		dev_err(mtk->dev, "failed to enable sys_clk\n");
> +		goto sys_clk_err;
> +	}

Can you use clk_enable_bulk() and other clk.*bulk functions ?
[...]
Chunfeng Yun (云春峰) March 11, 2020, 8:17 a.m. UTC | #2
On Wed, 2020-03-11 at 08:11 +0100, Marek Vasut wrote:
> On 3/11/20 7:50 AM, Chunfeng Yun wrote:
> [...]
> > + * @u3_ctrl_p[x]: ip usb3 port x control register, only low 4bytes are used
> > + * @u2_ctrl_p[x]: ip usb2 port x control register, only low 4bytes are used
> > + * @u2_phy_pll: usb2 phy pll control register
> > + */
> > +struct mtk_ippc_regs {
> > +	__le32 ip_pw_ctr0;
> > +	__le32 ip_pw_ctr1;
> > +	__le32 ip_pw_ctr2;
> 
> Please define the registers with #define macros , this struct-based
> approach doesn't scale.
Ok
> 
> [..]
> 
> > +static int xhci_mtk_host_enable(struct mtk_xhci *mtk)
> > +{
> > +	struct mtk_ippc_regs *ippc = mtk->ippc;
> > +	u32 value, check_val;
> > +	int ret;
> > +	int i;
> > +
> > +	/* power on host ip */
> > +	value = readl(&ippc->ip_pw_ctr1);
> > +	value &= ~CTRL1_IP_HOST_PDN;
> > +	writel(value, &ippc->ip_pw_ctr1);
> > +
> > +	/* power on and enable all u3 ports */
> > +	for (i = 0; i < mtk->num_u3_ports; i++) {
> > +		value = readl(&ippc->u3_ctrl_p[i]);
> > +		value &= ~(CTRL_U3_PORT_PDN | CTRL_U3_PORT_DIS);
> > +		value |= CTRL_U3_PORT_HOST_SEL;
> > +		writel(value, &ippc->u3_ctrl_p[i]);
> > +	}
> 
> Use clrsetbits_le32() above and below and where applicable.
Ok
> 
> > +	/* power on and enable all u2 ports */
> > +	for (i = 0; i < mtk->num_u2_ports; i++) {
> > +		value = readl(&ippc->u2_ctrl_p[i]);
> > +		value &= ~(CTRL_U2_PORT_PDN | CTRL_U2_PORT_DIS);
> > +		value |= CTRL_U2_PORT_HOST_SEL;
> > +		writel(value, &ippc->u2_ctrl_p[i]);
> > +	}
> 
> [...]
> 
> > +static int xhci_mtk_clks_enable(struct mtk_xhci *mtk)
> > +{
> > +	int ret;
> > +
> > +	ret = clk_enable(mtk->sys_clk);
> > +	if (ret) {
> > +		dev_err(mtk->dev, "failed to enable sys_clk\n");
> > +		goto sys_clk_err;
> > +	}
> 
> Can you use clk_enable_bulk() and other clk.*bulk functions ?
Will try it, thank you
> [...]
>
Simon Glass March 11, 2020, 12:18 p.m. UTC | #3
Hi Chunfeng,

On Wed, 11 Mar 2020 at 01:00, Chunfeng Yun <chunfeng.yun at mediatek.com> wrote:
>
> This patch is used to support the on-chip xHCI controller on
> MediaTek SoCs, currently only control/bulk transfers are
> supported.
>

Are interrupt transfers planned for later?

> Signed-off-by: Chunfeng Yun <chunfeng.yun at mediatek.com>
> ---
>  drivers/usb/host/Kconfig    |   6 +
>  drivers/usb/host/Makefile   |   1 +
>  drivers/usb/host/xhci-mtk.c | 508 ++++++++++++++++++++++++++++++++++++
>  3 files changed, 515 insertions(+)
>  create mode 100644 drivers/usb/host/xhci-mtk.c

Regards,
Simon
Chunfeng Yun (云春峰) March 12, 2020, 6:23 a.m. UTC | #4
On Wed, 2020-03-11 at 06:18 -0600, Simon Glass wrote:
> Hi Chunfeng,
> 
> On Wed, 11 Mar 2020 at 01:00, Chunfeng Yun <chunfeng.yun at mediatek.com> wrote:
> >
> > This patch is used to support the on-chip xHCI controller on
> > MediaTek SoCs, currently only control/bulk transfers are
> > supported.
> >
> 
> Are interrupt transfers planned for later?
Yes, will support it in the later version

> 
> > Signed-off-by: Chunfeng Yun <chunfeng.yun at mediatek.com>
> > ---
> >  drivers/usb/host/Kconfig    |   6 +
> >  drivers/usb/host/Makefile   |   1 +
> >  drivers/usb/host/xhci-mtk.c | 508 ++++++++++++++++++++++++++++++++++++
> >  3 files changed, 515 insertions(+)
> >  create mode 100644 drivers/usb/host/xhci-mtk.c
> 
> Regards,
> Simon
Chunfeng Yun (云春峰) March 21, 2020, 8:53 a.m. UTC | #5
On Wed, 2020-03-11 at 08:11 +0100, Marek Vasut wrote:
> On 3/11/20 7:50 AM, Chunfeng Yun wrote:
> [...]
> > + * @u3_ctrl_p[x]: ip usb3 port x control register, only low 4bytes are used
> > + * @u2_ctrl_p[x]: ip usb2 port x control register, only low 4bytes are used
> > + * @u2_phy_pll: usb2 phy pll control register
> > + */
> > +struct mtk_ippc_regs {
> > +	__le32 ip_pw_ctr0;
> > +	__le32 ip_pw_ctr1;
> > +	__le32 ip_pw_ctr2;
> 
> Please define the registers with #define macros , this struct-based
> approach doesn't scale.

When I prepare for v2, and find that if define registers as macros, it do not keep
the same style with the xhci core, so I leave it unchanged in v2, if you
still suggest to avoid struct-based approach, I will change it in v3
version, thanks

> [..]
>
Simon Glass March 21, 2020, 2:12 p.m. UTC | #6
Hi Marek,

On Wed, 11 Mar 2020 at 01:11, Marek Vasut <marex at denx.de> wrote:
>
> On 3/11/20 7:50 AM, Chunfeng Yun wrote:
> [...]
> > + * @u3_ctrl_p[x]: ip usb3 port x control register, only low 4bytes are used
> > + * @u2_ctrl_p[x]: ip usb2 port x control register, only low 4bytes are used
> > + * @u2_phy_pll: usb2 phy pll control register
> > + */
> > +struct mtk_ippc_regs {
> > +     __le32 ip_pw_ctr0;
> > +     __le32 ip_pw_ctr1;
> > +     __le32 ip_pw_ctr2;
>
> Please define the registers with #define macros , this struct-based
> approach doesn't scale.
>

What does this mean? I much prefer the struct approach, unless the
registers move around in future generations.

Regards,
Simon
Marek Vasut March 21, 2020, 3:08 p.m. UTC | #7
On 3/21/20 3:12 PM, Simon Glass wrote:
> Hi Marek,
> 
> On Wed, 11 Mar 2020 at 01:11, Marek Vasut <marex at denx.de> wrote:
>>
>> On 3/11/20 7:50 AM, Chunfeng Yun wrote:
>> [...]
>>> + * @u3_ctrl_p[x]: ip usb3 port x control register, only low 4bytes are used
>>> + * @u2_ctrl_p[x]: ip usb2 port x control register, only low 4bytes are used
>>> + * @u2_phy_pll: usb2 phy pll control register
>>> + */
>>> +struct mtk_ippc_regs {
>>> +     __le32 ip_pw_ctr0;
>>> +     __le32 ip_pw_ctr1;
>>> +     __le32 ip_pw_ctr2;
>>
>> Please define the registers with #define macros , this struct-based
>> approach doesn't scale.
>>
> 
> What does this mean? I much prefer the struct approach, unless the
> registers move around in future generations.

This means I want to see #define MTK_XHCI_<register name> 0xf00

The struct based approach doesn't scale and on e.g. altera is causing us
a massive amount of problems now. It looked like a good idea back then,
but now it's a massive burden, so I don't want that to proliferate. And
here I would expect the registers to move around, just like everywhere else.
Simon Glass March 21, 2020, 4:42 p.m. UTC | #8
Hi Marek,

On Sat, 21 Mar 2020 at 09:09, Marek Vasut <marex at denx.de> wrote:
>
> On 3/21/20 3:12 PM, Simon Glass wrote:
> > Hi Marek,
> >
> > On Wed, 11 Mar 2020 at 01:11, Marek Vasut <marex at denx.de> wrote:
> >>
> >> On 3/11/20 7:50 AM, Chunfeng Yun wrote:
> >> [...]
> >>> + * @u3_ctrl_p[x]: ip usb3 port x control register, only low 4bytes are used
> >>> + * @u2_ctrl_p[x]: ip usb2 port x control register, only low 4bytes are used
> >>> + * @u2_phy_pll: usb2 phy pll control register
> >>> + */
> >>> +struct mtk_ippc_regs {
> >>> +     __le32 ip_pw_ctr0;
> >>> +     __le32 ip_pw_ctr1;
> >>> +     __le32 ip_pw_ctr2;
> >>
> >> Please define the registers with #define macros , this struct-based
> >> approach doesn't scale.
> >>
> >
> > What does this mean? I much prefer the struct approach, unless the
> > registers move around in future generations.
>
> This means I want to see #define MTK_XHCI_<register name> 0xf00
>
> The struct based approach doesn't scale and on e.g. altera is causing us
> a massive amount of problems now. It looked like a good idea back then,
> but now it's a massive burden, so I don't want that to proliferate. And
> here I would expect the registers to move around, just like everywhere else.

That sounds like something that is easily avoided. For example,
Designware doesn't seem to move things around.

Perhaps MediaTek has a policy of not doing this either?

Regards,
Simon
Marek Vasut March 21, 2020, 4:59 p.m. UTC | #9
On 3/21/20 5:42 PM, Simon Glass wrote:
> Hi Marek,

Hi,

> On Sat, 21 Mar 2020 at 09:09, Marek Vasut <marex at denx.de> wrote:
>>
>> On 3/21/20 3:12 PM, Simon Glass wrote:
>>> Hi Marek,
>>>
>>> On Wed, 11 Mar 2020 at 01:11, Marek Vasut <marex at denx.de> wrote:
>>>>
>>>> On 3/11/20 7:50 AM, Chunfeng Yun wrote:
>>>> [...]
>>>>> + * @u3_ctrl_p[x]: ip usb3 port x control register, only low 4bytes are used
>>>>> + * @u2_ctrl_p[x]: ip usb2 port x control register, only low 4bytes are used
>>>>> + * @u2_phy_pll: usb2 phy pll control register
>>>>> + */
>>>>> +struct mtk_ippc_regs {
>>>>> +     __le32 ip_pw_ctr0;
>>>>> +     __le32 ip_pw_ctr1;
>>>>> +     __le32 ip_pw_ctr2;
>>>>
>>>> Please define the registers with #define macros , this struct-based
>>>> approach doesn't scale.
>>>>
>>>
>>> What does this mean? I much prefer the struct approach, unless the
>>> registers move around in future generations.
>>
>> This means I want to see #define MTK_XHCI_<register name> 0xf00
>>
>> The struct based approach doesn't scale and on e.g. altera is causing us
>> a massive amount of problems now. It looked like a good idea back then,
>> but now it's a massive burden, so I don't want that to proliferate. And
>> here I would expect the registers to move around, just like everywhere else.
> 
> That sounds like something that is easily avoided. For example,
> Designware doesn't seem to move things around.
> 
> Perhaps MediaTek has a policy of not doing this either?

Such policies change as the hardware evolves. I got burned by this
struct-based approach more often then it was beneficial, if it ever
really was beneficial, hence I don't want to see it used.
Simon Glass March 21, 2020, 6:41 p.m. UTC | #10
Hi Marek,

On Sat, 21 Mar 2020 at 11:00, Marek Vasut <marex at denx.de> wrote:
>
> On 3/21/20 5:42 PM, Simon Glass wrote:
> > Hi Marek,
>
> Hi,
>
> > On Sat, 21 Mar 2020 at 09:09, Marek Vasut <marex at denx.de> wrote:
> >>
> >> On 3/21/20 3:12 PM, Simon Glass wrote:
> >>> Hi Marek,
> >>>
> >>> On Wed, 11 Mar 2020 at 01:11, Marek Vasut <marex at denx.de> wrote:
> >>>>
> >>>> On 3/11/20 7:50 AM, Chunfeng Yun wrote:
> >>>> [...]
> >>>>> + * @u3_ctrl_p[x]: ip usb3 port x control register, only low 4bytes are used
> >>>>> + * @u2_ctrl_p[x]: ip usb2 port x control register, only low 4bytes are used
> >>>>> + * @u2_phy_pll: usb2 phy pll control register
> >>>>> + */
> >>>>> +struct mtk_ippc_regs {
> >>>>> +     __le32 ip_pw_ctr0;
> >>>>> +     __le32 ip_pw_ctr1;
> >>>>> +     __le32 ip_pw_ctr2;
> >>>>
> >>>> Please define the registers with #define macros , this struct-based
> >>>> approach doesn't scale.
> >>>>
> >>>
> >>> What does this mean? I much prefer the struct approach, unless the
> >>> registers move around in future generations.
> >>
> >> This means I want to see #define MTK_XHCI_<register name> 0xf00
> >>
> >> The struct based approach doesn't scale and on e.g. altera is causing us
> >> a massive amount of problems now. It looked like a good idea back then,
> >> but now it's a massive burden, so I don't want that to proliferate. And
> >> here I would expect the registers to move around, just like everywhere else.
> >
> > That sounds like something that is easily avoided. For example,
> > Designware doesn't seem to move things around.
> >
> > Perhaps MediaTek has a policy of not doing this either?
>
> Such policies change as the hardware evolves. I got burned by this
> struct-based approach more often then it was beneficial, if it ever
> really was beneficial, hence I don't want to see it used.

Some benefits:
- using C struct instead of 'assembler-like' addition is less error-prone
- you can pass the regs struct around between functions in the driver,
particularly helpful in large drivers
- lower-case letters are reasier to read
- you can specify the data size and endianness in the struct using C types

If the hardware changes enough, then you need a new driver anyway, or
perhaps separate C implementations of the low-level register access if
the high-level flow is similar. In general you can't always determine
this sort of thing at compile time since the version of the hardware
may not be apparent until run-time. You end up with variables holding
the offset of each register.

Sometimes is it better to have an umbrella driver with a common core.

So I would push back against a move in this direction in general. It
depends on the circumstances, and anyway, converting from struct to
offsets if desirable later is not that hard.

Regards,
Simon
Marek Vasut March 21, 2020, 7:01 p.m. UTC | #11
On 3/21/20 7:41 PM, Simon Glass wrote:
> Hi Marek,

Hi,

> On Sat, 21 Mar 2020 at 11:00, Marek Vasut <marex at denx.de> wrote:
>>
>> On 3/21/20 5:42 PM, Simon Glass wrote:
>>> Hi Marek,
>>
>> Hi,
>>
>>> On Sat, 21 Mar 2020 at 09:09, Marek Vasut <marex at denx.de> wrote:
>>>>
>>>> On 3/21/20 3:12 PM, Simon Glass wrote:
>>>>> Hi Marek,
>>>>>
>>>>> On Wed, 11 Mar 2020 at 01:11, Marek Vasut <marex at denx.de> wrote:
>>>>>>
>>>>>> On 3/11/20 7:50 AM, Chunfeng Yun wrote:
>>>>>> [...]
>>>>>>> + * @u3_ctrl_p[x]: ip usb3 port x control register, only low 4bytes are used
>>>>>>> + * @u2_ctrl_p[x]: ip usb2 port x control register, only low 4bytes are used
>>>>>>> + * @u2_phy_pll: usb2 phy pll control register
>>>>>>> + */
>>>>>>> +struct mtk_ippc_regs {
>>>>>>> +     __le32 ip_pw_ctr0;
>>>>>>> +     __le32 ip_pw_ctr1;
>>>>>>> +     __le32 ip_pw_ctr2;
>>>>>>
>>>>>> Please define the registers with #define macros , this struct-based
>>>>>> approach doesn't scale.
>>>>>>
>>>>>
>>>>> What does this mean? I much prefer the struct approach, unless the
>>>>> registers move around in future generations.
>>>>
>>>> This means I want to see #define MTK_XHCI_<register name> 0xf00
>>>>
>>>> The struct based approach doesn't scale and on e.g. altera is causing us
>>>> a massive amount of problems now. It looked like a good idea back then,
>>>> but now it's a massive burden, so I don't want that to proliferate. And
>>>> here I would expect the registers to move around, just like everywhere else.
>>>
>>> That sounds like something that is easily avoided. For example,
>>> Designware doesn't seem to move things around.
>>>
>>> Perhaps MediaTek has a policy of not doing this either?
>>
>> Such policies change as the hardware evolves. I got burned by this
>> struct-based approach more often then it was beneficial, if it ever
>> really was beneficial, hence I don't want to see it used.
> 
> Some benefits:
> - using C struct instead of 'assembler-like' addition is less error-prone

In my experience, this is exactly the opposite. If I have a long
structure describing some register set and I have to count lines to
figure out which register it is at offset e.g. 0x124 , then I am very
likely to make a mistake. However, if I have a macro which looks like
#define REG_FOO 0x124 , I see it right away.

And I am not even talking about modifying such structures, where one has
to over and over recount and verify that nothing got padded the wrong
way. Worse yet, that the structure might just be embedded in some other
super-structure, in which case the superstructure gets padded
differently if the sub-structure changes in size.

Let alone the problematic fact that you might have multiple versions of
the same IP in the system, with just slightly different register
layouts, at which point the structures become ridden with unions {} and
it becomes a total hell.

So no, this argument I am not buying, sorry.

Also, how is C preprocessor macro assembler like ?

> - you can pass the regs struct around between functions in the driver,
> particularly helpful in large drivers

You can pass base addresses around too.

> - lower-case letters are reasier to read

That's a matter of taste.

I find it much easier to identify register offsets (and other macros)
among functions if the offsets are written in uppercase while functions
in lowercase.

> - you can specify the data size and endianness in the struct using C types

Yes, you can, which is not a benefit but another problem, esp. if that
structure gets used on systems where CPU and bus endianness can differ.
(take a look e.g. at the macro monstrosity that is NS16550)

> If the hardware changes enough, then you need a new driver anyway, or
> perhaps separate C implementations of the low-level register access if
> the high-level flow is similar.

Not really, there is a lot of hardware where registers just move around,
but existing driver can very well handle all such instances.

> In general you can't always determine
> this sort of thing at compile time since the version of the hardware
> may not be apparent until run-time. You end up with variables holding
> the offset of each register.

You can use some/any sort of register abstraction to do the remapping.
This is completely independent of how you represent the register offsets.

> Sometimes is it better to have an umbrella driver with a common core.
> 
> So I would push back against a move in this direction in general. It
> depends on the circumstances, and anyway, converting from struct to
> offsets if desirable later is not that hard.

NAK, I am opposed to the struct based access, sorry.

We had massive problems moving toward it in multiple areas (socfpga was
hit very bad and I regret it to this day), only to move back from it
these days because it is borderline impossible to accommodate newly
emerging hardware and we suffered problems outlined above often.

If you want one more argument, then Linux is not using this anywhere and
it has much larger driver base. One might expect Linux to be somewhat
ahead of U-Boot in adopting such techniques.
Simon Glass March 21, 2020, 7:34 p.m. UTC | #12
Hi Marek,

On Sat, 21 Mar 2020 at 13:01, Marek Vasut <marex at denx.de> wrote:
>
> On 3/21/20 7:41 PM, Simon Glass wrote:
> > Hi Marek,
>
> Hi,
>
> > On Sat, 21 Mar 2020 at 11:00, Marek Vasut <marex at denx.de> wrote:
> >>
> >> On 3/21/20 5:42 PM, Simon Glass wrote:
> >>> Hi Marek,
> >>
> >> Hi,
> >>
> >>> On Sat, 21 Mar 2020 at 09:09, Marek Vasut <marex at denx.de> wrote:
> >>>>
> >>>> On 3/21/20 3:12 PM, Simon Glass wrote:
> >>>>> Hi Marek,
> >>>>>
> >>>>> On Wed, 11 Mar 2020 at 01:11, Marek Vasut <marex at denx.de> wrote:
> >>>>>>
> >>>>>> On 3/11/20 7:50 AM, Chunfeng Yun wrote:
> >>>>>> [...]
> >>>>>>> + * @u3_ctrl_p[x]: ip usb3 port x control register, only low 4bytes are used
> >>>>>>> + * @u2_ctrl_p[x]: ip usb2 port x control register, only low 4bytes are used
> >>>>>>> + * @u2_phy_pll: usb2 phy pll control register
> >>>>>>> + */
> >>>>>>> +struct mtk_ippc_regs {
> >>>>>>> +     __le32 ip_pw_ctr0;
> >>>>>>> +     __le32 ip_pw_ctr1;
> >>>>>>> +     __le32 ip_pw_ctr2;
> >>>>>>
> >>>>>> Please define the registers with #define macros , this struct-based
> >>>>>> approach doesn't scale.
> >>>>>>
> >>>>>
> >>>>> What does this mean? I much prefer the struct approach, unless the
> >>>>> registers move around in future generations.
> >>>>
> >>>> This means I want to see #define MTK_XHCI_<register name> 0xf00
> >>>>
> >>>> The struct based approach doesn't scale and on e.g. altera is causing us
> >>>> a massive amount of problems now. It looked like a good idea back then,
> >>>> but now it's a massive burden, so I don't want that to proliferate. And
> >>>> here I would expect the registers to move around, just like everywhere else.
> >>>
> >>> That sounds like something that is easily avoided. For example,
> >>> Designware doesn't seem to move things around.
> >>>
> >>> Perhaps MediaTek has a policy of not doing this either?
> >>
> >> Such policies change as the hardware evolves. I got burned by this
> >> struct-based approach more often then it was beneficial, if it ever
> >> really was beneficial, hence I don't want to see it used.
> >
> > Some benefits:
> > - using C struct instead of 'assembler-like' addition is less error-prone
>
> In my experience, this is exactly the opposite. If I have a long
> structure describing some register set and I have to count lines to
> figure out which register it is at offset e.g. 0x124 , then I am very
> likely to make a mistake. However, if I have a macro which looks like
> #define REG_FOO 0x124 , I see it right away.

See check_member() though. How do you know it is as offset 0x124?

>
> And I am not even talking about modifying such structures, where one has
> to over and over recount and verify that nothing got padded the wrong
> way. Worse yet, that the structure might just be embedded in some other
> super-structure, in which case the superstructure gets padded
> differently if the sub-structure changes in size.

You always have the choice. You are over-dramatising the difference by
coming up with cases where 'struct' doesn't work. I'm not suggesting
struct should be used always, but to me it has clear benefits in many
drivers in terms of readability and maintainability. We have both
written and maintained a lot of drivers. Also see
ich_init_controller() for a hybrid approach.

>
> Let alone the problematic fact that you might have multiple versions of
> the same IP in the system, with just slightly different register
> layouts, at which point the structures become ridden with unions {} and
> it becomes a total hell.

I don't think structs should be used in that situation though, unless
it is just for a few fields.

>
> So no, this argument I am not buying, sorry.
>
> Also, how is C preprocessor macro assembler like ?

Well in assembler we used to have [BASE + REG_TX], [BASE + REG_RX].
That is what structs are for, so not using them is ignoring a useful
feature of the C language.

>
> > - you can pass the regs struct around between functions in the driver,
> > particularly helpful in large drivers
>
> You can pass base addresses around too.

Untyped though. Then you need casts and defeat the type system, etc.
For example in one driver there are two different register sets and it
is nice to pass just the pointer you need without any ambiguity.

>
> > - lower-case letters are reasier to read
>
> That's a matter of taste.
>
> I find it much easier to identify register offsets (and other macros)
> among functions if the offsets are written in uppercase while functions
> in lowercase.

OK

>
> > - you can specify the data size and endianness in the struct using C types
>
> Yes, you can, which is not a benefit but another problem, esp. if that
> structure gets used on systems where CPU and bus endianness can differ.
> (take a look e.g. at the macro monstrosity that is NS16550)

This is a good example of a driver which should be split out, rather
than cramming all the the little tweaks into ifdefs, etc. Also see
serial_out_dynamic() which heads in that direction. Anyway ns16550 has
to deal with registers being of different sizes. I agree it is a bad
candidate for struct, unless the driver is split.

>
> > If the hardware changes enough, then you need a new driver anyway, or
> > perhaps separate C implementations of the low-level register access if
> > the high-level flow is similar.
>
> Not really, there is a lot of hardware where registers just move around,
> but existing driver can very well handle all such instances.

In my experience things seldom move around and never within the same
SoC generation. In other words there are loads of drivers where it
makes sense to use struct.

>
> > In general you can't always determine
> > this sort of thing at compile time since the version of the hardware
> > may not be apparent until run-time. You end up with variables holding
> > the offset of each register.
>
> You can use some/any sort of register abstraction to do the remapping.
> This is completely independent of how you represent the register offsets.

Yes my point is that you go too far with trying to use the same drive
for different hardware.

>
> > Sometimes is it better to have an umbrella driver with a common core.
> >
> > So I would push back against a move in this direction in general. It
> > depends on the circumstances, and anyway, converting from struct to
> > offsets if desirable later is not that hard.
>
> NAK, I am opposed to the struct based access, sorry.

NAK to what exactly? If you are NAKing using struct when it has the
problems you mention above, I agree with you. If you are NAKing struct
when it has none of these problems, I disagree.

>
> We had massive problems moving toward it in multiple areas (socfpga was
> hit very bad and I regret it to this day), only to move back from it
> these days because it is borderline impossible to accommodate newly
> emerging hardware and we suffered problems outlined above often.

I'm sorry about your socfpga problems. But then, just use offsets, right?

>
> If you want one more argument, then Linux is not using this anywhere and
> it has much larger driver base. One might expect Linux to be somewhat
> ahead of U-Boot in adopting such techniques.

Yes I remember when I first moved from Linux to U-Boot I saw the
struct approach and was very impressed. Has there been any discussion
of moving Linux in that direction? When there is little to no
variability in the register offsets, it is far superior in the right
circumstances I think.

Regards,
Simon
Marek Vasut March 21, 2020, 8:04 p.m. UTC | #13
On 3/21/20 8:34 PM, Simon Glass wrote:
> Hi Marek,

Hi,

> On Sat, 21 Mar 2020 at 13:01, Marek Vasut <marex at denx.de> wrote:
>>
>> On 3/21/20 7:41 PM, Simon Glass wrote:
>>> Hi Marek,
>>
>> Hi,
>>
>>> On Sat, 21 Mar 2020 at 11:00, Marek Vasut <marex at denx.de> wrote:
>>>>
>>>> On 3/21/20 5:42 PM, Simon Glass wrote:
>>>>> Hi Marek,
>>>>
>>>> Hi,
>>>>
>>>>> On Sat, 21 Mar 2020 at 09:09, Marek Vasut <marex at denx.de> wrote:
>>>>>>
>>>>>> On 3/21/20 3:12 PM, Simon Glass wrote:
>>>>>>> Hi Marek,
>>>>>>>
>>>>>>> On Wed, 11 Mar 2020 at 01:11, Marek Vasut <marex at denx.de> wrote:
>>>>>>>>
>>>>>>>> On 3/11/20 7:50 AM, Chunfeng Yun wrote:
>>>>>>>> [...]
>>>>>>>>> + * @u3_ctrl_p[x]: ip usb3 port x control register, only low 4bytes are used
>>>>>>>>> + * @u2_ctrl_p[x]: ip usb2 port x control register, only low 4bytes are used
>>>>>>>>> + * @u2_phy_pll: usb2 phy pll control register
>>>>>>>>> + */
>>>>>>>>> +struct mtk_ippc_regs {
>>>>>>>>> +     __le32 ip_pw_ctr0;
>>>>>>>>> +     __le32 ip_pw_ctr1;
>>>>>>>>> +     __le32 ip_pw_ctr2;
>>>>>>>>
>>>>>>>> Please define the registers with #define macros , this struct-based
>>>>>>>> approach doesn't scale.
>>>>>>>>
>>>>>>>
>>>>>>> What does this mean? I much prefer the struct approach, unless the
>>>>>>> registers move around in future generations.
>>>>>>
>>>>>> This means I want to see #define MTK_XHCI_<register name> 0xf00
>>>>>>
>>>>>> The struct based approach doesn't scale and on e.g. altera is causing us
>>>>>> a massive amount of problems now. It looked like a good idea back then,
>>>>>> but now it's a massive burden, so I don't want that to proliferate. And
>>>>>> here I would expect the registers to move around, just like everywhere else.
>>>>>
>>>>> That sounds like something that is easily avoided. For example,
>>>>> Designware doesn't seem to move things around.
>>>>>
>>>>> Perhaps MediaTek has a policy of not doing this either?
>>>>
>>>> Such policies change as the hardware evolves. I got burned by this
>>>> struct-based approach more often then it was beneficial, if it ever
>>>> really was beneficial, hence I don't want to see it used.
>>>
>>> Some benefits:
>>> - using C struct instead of 'assembler-like' addition is less error-prone
>>
>> In my experience, this is exactly the opposite. If I have a long
>> structure describing some register set and I have to count lines to
>> figure out which register it is at offset e.g. 0x124 , then I am very
>> likely to make a mistake. However, if I have a macro which looks like
>> #define REG_FOO 0x124 , I see it right away.
> 
> See check_member() though.

This only adds duplication and more complexity. With this, not only do
you have a structure which is difficult and error-prone to work with,
but you also have a macro which defines the offset, but only for the
purpose of preventing errors originally induced by the complexity of
working with the structure.

So this is complexity on top of complexity.

> How do you know it is as offset 0x124?

I see this offset in the datasheet of the IP/SoC/whatever and/or I can
simply git grep for this value in the sources. And since this value is
there verbatim, I see #define REG_FOO 0x124, simple.

With the complexity presented above, this no longer works. I have to
comb through structures (possibly embedded in other structures)
manually, I cannot directly grep for the register offsets, and it's
complicated, cumbersome and massively error prone.

>> And I am not even talking about modifying such structures, where one has
>> to over and over recount and verify that nothing got padded the wrong
>> way. Worse yet, that the structure might just be embedded in some other
>> super-structure, in which case the superstructure gets padded
>> differently if the sub-structure changes in size.
> 
> You always have the choice. You are over-dramatising the difference by
> coming up with cases where 'struct' doesn't work.

I am coming up with real world experience I have, which tells me that
this does not work in the long run in vast majority of cases and/or is a
massive pain to work with.

Obviously, I come up with cases where this struct based approach fails
and/or displays it's shortcomings, where else would I demonstrate all
the problems with this ?

> I'm not suggesting
> struct should be used always, but to me it has clear benefits in many
> drivers in terms of readability and maintainability. We have both
> written and maintained a lot of drivers. Also see
> ich_init_controller() for a hybrid approach.

Sorry, I just don't see the clear benefit.

Originally I thought this struct-based approach was a good idea too,
which is why I was pushing for it a lot back then, but now I think it
was a big mistake.

>> Let alone the problematic fact that you might have multiple versions of
>> the same IP in the system, with just slightly different register
>> layouts, at which point the structures become ridden with unions {} and
>> it becomes a total hell.
> 
> I don't think structs should be used in that situation though, unless
> it is just for a few fields.

So this would imply you would have drivers which have struct based
access, but then suddenly when the hardware complexity increases, you
would convert them to plain macros ? In that case, we can very well just
stick with plain macros right from the beginning.

>> So no, this argument I am not buying, sorry.
>>
>> Also, how is C preprocessor macro assembler like ?
> 
> Well in assembler we used to have [BASE + REG_TX], [BASE + REG_RX].
> That is what structs are for, so not using them is ignoring a useful
> feature of the C language.

In my experience, this is not helpful at all, it only is in the way and
is cumbersome to deal with.

The base + offset is easy to calculate, which makes it easy and straight
forward to work with and to debug. But trying to find out what the type
of some structure is, and then calculate at what offset in &foo->bar the
BAR register really is, awful and error prone.

>>> - you can pass the regs struct around between functions in the driver,
>>> particularly helpful in large drivers
>>
>> You can pass base addresses around too.
> 
> Untyped though. Then you need casts and defeat the type system, etc.
> For example in one driver there are two different register sets and it
> is nice to pass just the pointer you need without any ambiguity.

Or, you have a driver which handles two different version of the same
IP, with different register layouts. Suddenly you have two structures
and massive amount of casts.

>>> - lower-case letters are reasier to read
>>
>> That's a matter of taste.
>>
>> I find it much easier to identify register offsets (and other macros)
>> among functions if the offsets are written in uppercase while functions
>> in lowercase.
> 
> OK
> 
>>
>>> - you can specify the data size and endianness in the struct using C types
>>
>> Yes, you can, which is not a benefit but another problem, esp. if that
>> structure gets used on systems where CPU and bus endianness can differ.
>> (take a look e.g. at the macro monstrosity that is NS16550)
> 
> This is a good example of a driver which should be split out, rather
> than cramming all the the little tweaks into ifdefs, etc. Also see
> serial_out_dynamic() which heads in that direction. Anyway ns16550 has
> to deal with registers being of different sizes. I agree it is a bad
> candidate for struct, unless the driver is split.

It's a great driver for using macros though.

>>> If the hardware changes enough, then you need a new driver anyway, or
>>> perhaps separate C implementations of the low-level register access if
>>> the high-level flow is similar.
>>
>> Not really, there is a lot of hardware where registers just move around,
>> but existing driver can very well handle all such instances.
> 
> In my experience things seldom move around and never within the same
> SoC generation. In other words there are loads of drivers where it
> makes sense to use struct.

My experience is the exact opposite.

>>> In general you can't always determine
>>> this sort of thing at compile time since the version of the hardware
>>> may not be apparent until run-time. You end up with variables holding
>>> the offset of each register.
>>
>> You can use some/any sort of register abstraction to do the remapping.
>> This is completely independent of how you represent the register offsets.
> 
> Yes my point is that you go too far with trying to use the same drive
> for different hardware.

No. If the hardware is basically the same , except registers move
slightly , then it only makes sense to reuse the same driver.

>>> Sometimes is it better to have an umbrella driver with a common core.
>>>
>>> So I would push back against a move in this direction in general. It
>>> depends on the circumstances, and anyway, converting from struct to
>>> offsets if desirable later is not that hard.
>>
>> NAK, I am opposed to the struct based access, sorry.
> 
> NAK to what exactly? If you are NAKing using struct when it has the
> problems you mention above, I agree with you. If you are NAKing struct
> when it has none of these problems, I disagree.

I am NAKing the use of structs here, because in my experience this will
come back to bite us later on and because I don't see any benefits from
using them.

We can revisit this discussion a few years down the road and see who was
right if you want.

>> We had massive problems moving toward it in multiple areas (socfpga was
>> hit very bad and I regret it to this day), only to move back from it
>> these days because it is borderline impossible to accommodate newly
>> emerging hardware and we suffered problems outlined above often.
> 
> I'm sorry about your socfpga problems. But then, just use offsets, right?

offsets ?

>> If you want one more argument, then Linux is not using this anywhere and
>> it has much larger driver base. One might expect Linux to be somewhat
>> ahead of U-Boot in adopting such techniques.
> 
> Yes I remember when I first moved from Linux to U-Boot I saw the
> struct approach and was very impressed. Has there been any discussion
> of moving Linux in that direction? When there is little to no
> variability in the register offsets, it is far superior in the right
> circumstances I think.

Most embedded hardware has this variability it seems.
Feel free to start debating this on the Linux side.
Simon Glass March 22, 2020, 2:08 a.m. UTC | #14
Hi Marek,

I think at this point we've covered all the ground and mentioned the
pros and cons of each method, so I'll leave the discussion where it
is.

[..]

Regards,
Simon
Marek Vasut March 22, 2020, 2:15 a.m. UTC | #15
On 3/22/20 3:08 AM, Simon Glass wrote:
> Hi Marek,

Hi,

> I think at this point we've covered all the ground and mentioned the
> pros and cons of each method, so I'll leave the discussion where it
> is.

Great, so let's remove the struct-based access from the driver and use
regular #define REGISTER 0xoffset.

Thank you.
Simon Glass March 22, 2020, 3:17 p.m. UTC | #16
Hi Marek,

On Sat, 21 Mar 2020 at 20:15, Marek Vasut <marex at denx.de> wrote:
>
> On 3/22/20 3:08 AM, Simon Glass wrote:
> > Hi Marek,
>
> Hi,
>
> > I think at this point we've covered all the ground and mentioned the
> > pros and cons of each method, so I'll leave the discussion where it
> > is.
>
> Great, so let's remove the struct-based access from the driver and use
> regular #define REGISTER 0xoffset.

I think any individual decision depends on the pros and cons we
outlined in our discussion. I don't have any information to suggest
that the Mediatek XHCI driver has any of the variations you talked
about in your worst-case scenarios, so I can't comment on that. I am
more concerned about this as a general rule as I feel that the
struct-based approach is generally best for U-Boot, except for the
cases you highlighted:

- where the registers appear at different offsets in different
hardware revisions served by the same driver
- where the driver only uses a small subset of the registers and it is
not worth defining a struct to cover them all, with associated empty
regions, etc.

Anything else?

This is a USB driver and you are the USB maintainer, so your decision
is OK with me. For driver model in general I feel that struct access
should be the default, but individual maintainers with strong views on
their subsystem need to have preference.

Regards,
Simon
Marek Vasut March 22, 2020, 3:34 p.m. UTC | #17
On 3/22/20 4:17 PM, Simon Glass wrote:
> Hi Marek,

Hi,

> On Sat, 21 Mar 2020 at 20:15, Marek Vasut <marex at denx.de> wrote:
>>
>> On 3/22/20 3:08 AM, Simon Glass wrote:
>>> Hi Marek,
>>
>> Hi,
>>
>>> I think at this point we've covered all the ground and mentioned the
>>> pros and cons of each method, so I'll leave the discussion where it
>>> is.
>>
>> Great, so let's remove the struct-based access from the driver and use
>> regular #define REGISTER 0xoffset.
> 
> I think any individual decision depends on the pros and cons we
> outlined in our discussion. I don't have any information to suggest
> that the Mediatek XHCI driver has any of the variations you talked
> about in your worst-case scenarios, so I can't comment on that. I am
> more concerned about this as a general rule as I feel that the
> struct-based approach is generally best for U-Boot, except for the
> cases you highlighted:
> 
> - where the registers appear at different offsets in different
> hardware revisions served by the same driver
> - where the driver only uses a small subset of the registers and it is
> not worth defining a struct to cover them all, with associated empty
> regions, etc.
> 
> Anything else?

It's also very difficult to easily figure out the address of a register
that's buried somewhere down in a long structure, possibly with embedded
sub-structures.

> This is a USB driver and you are the USB maintainer, so your decision
> is OK with me. For driver model in general I feel that struct access
> should be the default, but individual maintainers with strong views on
> their subsystem need to have preference.

Well, like I said, my experience tells me the struct approach was a big
mistake in multiple places, so I would prefer macros here.
Chunfeng Yun (云春峰) March 24, 2020, 3:56 a.m. UTC | #18
Hi Marek & Simon,


Firstly, thanks for your suggestion and discussion.

As Simon guess, MediaTek indeed has some policies to avoid to move
registers around for USB IP, I think we will not encounter the
worst-case scenarios as Marek mentioned. Due to there is only a little
registers, both struct and macros approach are OK for me.

And in a word, I tend to agree with Simon's opinion, choose struct or
macro approach, case by case, after weigh the pros and cons. But Marek
is the maintainer of USB subsystem, so I'd better to make him happy, and
should follow his suggetions:)

Again, thank you guys


On Sun, 2020-03-22 at 16:34 +0100, Marek Vasut wrote:
> On 3/22/20 4:17 PM, Simon Glass wrote:
> > Hi Marek,
> 
> Hi,
> 
> > On Sat, 21 Mar 2020 at 20:15, Marek Vasut <marex at denx.de> wrote:
> >>
> >> On 3/22/20 3:08 AM, Simon Glass wrote:
> >>> Hi Marek,
> >>
> >> Hi,
> >>
> >>> I think at this point we've covered all the ground and mentioned the
> >>> pros and cons of each method, so I'll leave the discussion where it
> >>> is.
> >>
> >> Great, so let's remove the struct-based access from the driver and use
> >> regular #define REGISTER 0xoffset.
> > 
> > I think any individual decision depends on the pros and cons we
> > outlined in our discussion. I don't have any information to suggest
> > that the Mediatek XHCI driver has any of the variations you talked
> > about in your worst-case scenarios, so I can't comment on that. I am
> > more concerned about this as a general rule as I feel that the
> > struct-based approach is generally best for U-Boot, except for the
> > cases you highlighted:
> > 
> > - where the registers appear at different offsets in different
> > hardware revisions served by the same driver
> > - where the driver only uses a small subset of the registers and it is
> > not worth defining a struct to cover them all, with associated empty
> > regions, etc.
> > 
> > Anything else?
> 
> It's also very difficult to easily figure out the address of a register
> that's buried somewhere down in a long structure, possibly with embedded
> sub-structures.
> 
> > This is a USB driver and you are the USB maintainer, so your decision
> > is OK with me. For driver model in general I feel that struct access
> > should be the default, but individual maintainers with strong views on
> > their subsystem need to have preference.
> 
> Well, like I said, my experience tells me the struct approach was a big
> mistake in multiple places, so I would prefer macros here.
>
Simon Glass March 30, 2020, 11:31 p.m. UTC | #19
Hi Marek,

On Sun, 22 Mar 2020 at 09:34, Marek Vasut <marex at denx.de> wrote:
>
> On 3/22/20 4:17 PM, Simon Glass wrote:
> > Hi Marek,
>
> Hi,
>
> > On Sat, 21 Mar 2020 at 20:15, Marek Vasut <marex at denx.de> wrote:
> >>
> >> On 3/22/20 3:08 AM, Simon Glass wrote:
> >>> Hi Marek,
> >>
> >> Hi,
> >>
> >>> I think at this point we've covered all the ground and mentioned the
> >>> pros and cons of each method, so I'll leave the discussion where it
> >>> is.
> >>
> >> Great, so let's remove the struct-based access from the driver and use
> >> regular #define REGISTER 0xoffset.
> >
> > I think any individual decision depends on the pros and cons we
> > outlined in our discussion. I don't have any information to suggest
> > that the Mediatek XHCI driver has any of the variations you talked
> > about in your worst-case scenarios, so I can't comment on that. I am
> > more concerned about this as a general rule as I feel that the
> > struct-based approach is generally best for U-Boot, except for the
> > cases you highlighted:
> >
> > - where the registers appear at different offsets in different
> > hardware revisions served by the same driver
> > - where the driver only uses a small subset of the registers and it is
> > not worth defining a struct to cover them all, with associated empty
> > regions, etc.
> >
> > Anything else?
>
> It's also very difficult to easily figure out the address of a register
> that's buried somewhere down in a long structure, possibly with embedded
> sub-structures.

OK I have updated the coding style page with all of this.

Regards,
Simon
Marek Vasut March 31, 2020, 12:38 a.m. UTC | #20
On 3/31/20 1:31 AM, Simon Glass wrote:
> Hi Marek,

Hi,

> On Sun, 22 Mar 2020 at 09:34, Marek Vasut <marex at denx.de> wrote:
>>
>> On 3/22/20 4:17 PM, Simon Glass wrote:
>>> Hi Marek,
>>
>> Hi,
>>
>>> On Sat, 21 Mar 2020 at 20:15, Marek Vasut <marex at denx.de> wrote:
>>>>
>>>> On 3/22/20 3:08 AM, Simon Glass wrote:
>>>>> Hi Marek,
>>>>
>>>> Hi,
>>>>
>>>>> I think at this point we've covered all the ground and mentioned the
>>>>> pros and cons of each method, so I'll leave the discussion where it
>>>>> is.
>>>>
>>>> Great, so let's remove the struct-based access from the driver and use
>>>> regular #define REGISTER 0xoffset.
>>>
>>> I think any individual decision depends on the pros and cons we
>>> outlined in our discussion. I don't have any information to suggest
>>> that the Mediatek XHCI driver has any of the variations you talked
>>> about in your worst-case scenarios, so I can't comment on that. I am
>>> more concerned about this as a general rule as I feel that the
>>> struct-based approach is generally best for U-Boot, except for the
>>> cases you highlighted:
>>>
>>> - where the registers appear at different offsets in different
>>> hardware revisions served by the same driver
>>> - where the driver only uses a small subset of the registers and it is
>>> not worth defining a struct to cover them all, with associated empty
>>> regions, etc.
>>>
>>> Anything else?
>>
>> It's also very difficult to easily figure out the address of a register
>> that's buried somewhere down in a long structure, possibly with embedded
>> sub-structures.
> 
> OK I have updated the coding style page with all of this.

Which page ?
Simon Glass March 31, 2020, 1:24 p.m. UTC | #21
Hi Marek,

On Mon, 30 Mar 2020 at 18:39, Marek Vasut <marex at denx.de> wrote:
>
> On 3/31/20 1:31 AM, Simon Glass wrote:
> > Hi Marek,
>
> Hi,
>
> > On Sun, 22 Mar 2020 at 09:34, Marek Vasut <marex at denx.de> wrote:
> >>
> >> On 3/22/20 4:17 PM, Simon Glass wrote:
> >>> Hi Marek,
> >>
> >> Hi,
> >>
> >>> On Sat, 21 Mar 2020 at 20:15, Marek Vasut <marex at denx.de> wrote:
> >>>>
> >>>> On 3/22/20 3:08 AM, Simon Glass wrote:
> >>>>> Hi Marek,
> >>>>
> >>>> Hi,
> >>>>
> >>>>> I think at this point we've covered all the ground and mentioned the
> >>>>> pros and cons of each method, so I'll leave the discussion where it
> >>>>> is.
> >>>>
> >>>> Great, so let's remove the struct-based access from the driver and use
> >>>> regular #define REGISTER 0xoffset.
> >>>
> >>> I think any individual decision depends on the pros and cons we
> >>> outlined in our discussion. I don't have any information to suggest
> >>> that the Mediatek XHCI driver has any of the variations you talked
> >>> about in your worst-case scenarios, so I can't comment on that. I am
> >>> more concerned about this as a general rule as I feel that the
> >>> struct-based approach is generally best for U-Boot, except for the
> >>> cases you highlighted:
> >>>
> >>> - where the registers appear at different offsets in different
> >>> hardware revisions served by the same driver
> >>> - where the driver only uses a small subset of the registers and it is
> >>> not worth defining a struct to cover them all, with associated empty
> >>> regions, etc.
> >>>
> >>> Anything else?
> >>
> >> It's also very difficult to easily figure out the address of a register
> >> that's buried somewhere down in a long structure, possibly with embedded
> >> sub-structures.
> >
> > OK I have updated the coding style page with all of this.
>
> Which page ?

https://www.denx.de/wiki/U-Boot/CodingStyle

Separately, I sent a patch a while back which updated checkpatch for
U-Boot. I got some pushback, but I think that was wrong and we should
do it. For example I am saying many of the same things in code reviews
and many of them could be caught by the script. Examples include using
if() instead of #if where possible.

Regards,
Simon
Marek Vasut March 31, 2020, 1:29 p.m. UTC | #22
On 3/31/20 3:24 PM, Simon Glass wrote:
> Hi Marek,
> 
> On Mon, 30 Mar 2020 at 18:39, Marek Vasut <marex at denx.de> wrote:
>>
>> On 3/31/20 1:31 AM, Simon Glass wrote:
>>> Hi Marek,
>>
>> Hi,
>>
>>> On Sun, 22 Mar 2020 at 09:34, Marek Vasut <marex at denx.de> wrote:
>>>>
>>>> On 3/22/20 4:17 PM, Simon Glass wrote:
>>>>> Hi Marek,
>>>>
>>>> Hi,
>>>>
>>>>> On Sat, 21 Mar 2020 at 20:15, Marek Vasut <marex at denx.de> wrote:
>>>>>>
>>>>>> On 3/22/20 3:08 AM, Simon Glass wrote:
>>>>>>> Hi Marek,
>>>>>>
>>>>>> Hi,
>>>>>>
>>>>>>> I think at this point we've covered all the ground and mentioned the
>>>>>>> pros and cons of each method, so I'll leave the discussion where it
>>>>>>> is.
>>>>>>
>>>>>> Great, so let's remove the struct-based access from the driver and use
>>>>>> regular #define REGISTER 0xoffset.
>>>>>
>>>>> I think any individual decision depends on the pros and cons we
>>>>> outlined in our discussion. I don't have any information to suggest
>>>>> that the Mediatek XHCI driver has any of the variations you talked
>>>>> about in your worst-case scenarios, so I can't comment on that. I am
>>>>> more concerned about this as a general rule as I feel that the
>>>>> struct-based approach is generally best for U-Boot, except for the
>>>>> cases you highlighted:
>>>>>
>>>>> - where the registers appear at different offsets in different
>>>>> hardware revisions served by the same driver
>>>>> - where the driver only uses a small subset of the registers and it is
>>>>> not worth defining a struct to cover them all, with associated empty
>>>>> regions, etc.
>>>>>
>>>>> Anything else?
>>>>
>>>> It's also very difficult to easily figure out the address of a register
>>>> that's buried somewhere down in a long structure, possibly with embedded
>>>> sub-structures.
>>>
>>> OK I have updated the coding style page with all of this.
>>
>> Which page ?
> 
> https://www.denx.de/wiki/U-Boot/CodingStyle

" U-Boot typically uses a C structure to map out the registers in an I/O
region, rather than offsets. The reasons for this are: " is misleading
and suggests that using structures is the best practice. This should be
reworded to make it clear both options are equally valid.

> Separately, I sent a patch a while back which updated checkpatch for
> U-Boot. I got some pushback

Link ?

I am very much opposed to this struct-based approach, so there is my
pushback too. I think we should NOT do it.

>, but I think that was wrong and we should
> do it. For example I am saying many of the same things in code reviews
> and many of them could be caught by the script. Examples include using
> if() instead of #if where possible.
> 
> Regards,
> Simon
>
Simon Glass March 31, 2020, 2:16 p.m. UTC | #23
HI Marek,

On Tue, 31 Mar 2020 at 07:29, Marek Vasut <marex at denx.de> wrote:
>
> On 3/31/20 3:24 PM, Simon Glass wrote:
> > Hi Marek,
> >
> > On Mon, 30 Mar 2020 at 18:39, Marek Vasut <marex at denx.de> wrote:
> >>
> >> On 3/31/20 1:31 AM, Simon Glass wrote:
> >>> Hi Marek,
> >>
> >> Hi,
> >>
> >>> On Sun, 22 Mar 2020 at 09:34, Marek Vasut <marex at denx.de> wrote:
> >>>>
> >>>> On 3/22/20 4:17 PM, Simon Glass wrote:
> >>>>> Hi Marek,
> >>>>
> >>>> Hi,
> >>>>
> >>>>> On Sat, 21 Mar 2020 at 20:15, Marek Vasut <marex at denx.de> wrote:
> >>>>>>
> >>>>>> On 3/22/20 3:08 AM, Simon Glass wrote:
> >>>>>>> Hi Marek,
> >>>>>>
> >>>>>> Hi,
> >>>>>>
> >>>>>>> I think at this point we've covered all the ground and mentioned the
> >>>>>>> pros and cons of each method, so I'll leave the discussion where it
> >>>>>>> is.
> >>>>>>
> >>>>>> Great, so let's remove the struct-based access from the driver and use
> >>>>>> regular #define REGISTER 0xoffset.
> >>>>>
> >>>>> I think any individual decision depends on the pros and cons we
> >>>>> outlined in our discussion. I don't have any information to suggest
> >>>>> that the Mediatek XHCI driver has any of the variations you talked
> >>>>> about in your worst-case scenarios, so I can't comment on that. I am
> >>>>> more concerned about this as a general rule as I feel that the
> >>>>> struct-based approach is generally best for U-Boot, except for the
> >>>>> cases you highlighted:
> >>>>>
> >>>>> - where the registers appear at different offsets in different
> >>>>> hardware revisions served by the same driver
> >>>>> - where the driver only uses a small subset of the registers and it is
> >>>>> not worth defining a struct to cover them all, with associated empty
> >>>>> regions, etc.
> >>>>>
> >>>>> Anything else?
> >>>>
> >>>> It's also very difficult to easily figure out the address of a register
> >>>> that's buried somewhere down in a long structure, possibly with embedded
> >>>> sub-structures.
> >>>
> >>> OK I have updated the coding style page with all of this.
> >>
> >> Which page ?
> >
> > https://www.denx.de/wiki/U-Boot/CodingStyle
>
> " U-Boot typically uses a C structure to map out the registers in an I/O
> region, rather than offsets. The reasons for this are: " is misleading
> and suggests that using structures is the best practice. This should be
> reworded to make it clear both options are equally valid.

I'd like to see a preference to use struct where it makes sense and
not use it when it doesn't, with the different tradeoffs clearly
written. Are asking that we say nothing about which is better in each
situation?

>
> > Separately, I sent a patch a while back which updated checkpatch for
> > U-Boot. I got some pushback
>
> Link ?
>

http://patchwork.ozlabs.org/patch/999510/

> I am very much opposed to this struct-based approach, so there is my
> pushback too. I think we should NOT do it.

Right, but there are arguments on both sides. I strongly prefer it
where it makes sense, for reasons that we've already discussed.

>
> >, but I think that was wrong and we should
> > do it. For example I am saying many of the same things in code reviews
> > and many of them could be caught by the script. Examples include using
> > if() instead of #if where possible.
> >
> > Regards,
> > Simon
> >

Regards,
Simon
Marek Vasut March 31, 2020, 4:05 p.m. UTC | #24
On 3/31/20 4:16 PM, Simon Glass wrote:
> HI Marek,

Hi,

[...]

>>>>> OK I have updated the coding style page with all of this.
>>>>
>>>> Which page ?
>>>
>>> https://www.denx.de/wiki/U-Boot/CodingStyle
>>
>> " U-Boot typically uses a C structure to map out the registers in an I/O
>> region, rather than offsets. The reasons for this are: " is misleading
>> and suggests that using structures is the best practice. This should be
>> reworded to make it clear both options are equally valid.
> 
> I'd like to see a preference to use struct where it makes sense and
> not use it when it doesn't, with the different tradeoffs clearly
> written. Are asking that we say nothing about which is better in each
> situation?

Correct, because I don't see a clear agreement on which one is better
and should be preferred.

>>> Separately, I sent a patch a while back which updated checkpatch for
>>> U-Boot. I got some pushback
>>
>> Link ?
>>
> 
> http://patchwork.ozlabs.org/patch/999510/

OK, this has nothing to do with structs, right ?

>> I am very much opposed to this struct-based approach, so there is my
>> pushback too. I think we should NOT do it.
> 
> Right, but there are arguments on both sides. I strongly prefer it
> where it makes sense, for reasons that we've already discussed.

Your preference does not mean it is also the project preference though.
If you want to decide on that, make sure there is some agreement first.
diff mbox series

Patch

diff --git a/drivers/usb/host/Kconfig b/drivers/usb/host/Kconfig
index 0987ff25b1..931af268f4 100644
--- a/drivers/usb/host/Kconfig
+++ b/drivers/usb/host/Kconfig
@@ -30,6 +30,12 @@  config USB_XHCI_DWC3_OF_SIMPLE
 	  Support USB2/3 functionality in simple SoC integrations with
 	  USB controller based on the DesignWare USB3 IP Core.
 
+config USB_XHCI_MTK
+	bool "Support for MediaTek on-chip xHCI USB controller"
+	depends on ARCH_MEDIATEK
+	help
+	  Enables support for the on-chip xHCI controller on MediaTek SoCs.
+
 config USB_XHCI_MVEBU
 	bool "MVEBU USB 3.0 support"
 	default y
diff --git a/drivers/usb/host/Makefile b/drivers/usb/host/Makefile
index 7feeff679c..104821f188 100644
--- a/drivers/usb/host/Makefile
+++ b/drivers/usb/host/Makefile
@@ -50,6 +50,7 @@  obj-$(CONFIG_USB_XHCI_DWC3_OF_SIMPLE) += dwc3-of-simple.o
 obj-$(CONFIG_USB_XHCI_ROCKCHIP) += xhci-rockchip.o
 obj-$(CONFIG_USB_XHCI_EXYNOS) += xhci-exynos5.o
 obj-$(CONFIG_USB_XHCI_FSL) += xhci-fsl.o
+obj-$(CONFIG_USB_XHCI_MTK) += xhci-mtk.o
 obj-$(CONFIG_USB_XHCI_MVEBU) += xhci-mvebu.o
 obj-$(CONFIG_USB_XHCI_OMAP) += xhci-omap.o
 obj-$(CONFIG_USB_XHCI_PCI) += xhci-pci.o
diff --git a/drivers/usb/host/xhci-mtk.c b/drivers/usb/host/xhci-mtk.c
new file mode 100644
index 0000000000..a2a042b07c
--- /dev/null
+++ b/drivers/usb/host/xhci-mtk.c
@@ -0,0 +1,508 @@ 
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (c) 2019 MediaTek, Inc.
+ * Authors: Chunfeng Yun <chunfeng.yun at mediatek.com>
+ */
+
+#include <clk.h>
+#include <common.h>
+#include <dm.h>
+#include <dm/devres.h>
+#include <generic-phy.h>
+#include <malloc.h>
+#include <usb.h>
+#include <linux/errno.h>
+#include <linux/compat.h>
+#include <power/regulator.h>
+#include <linux/iopoll.h>
+
+#include <usb/xhci.h>
+
+#define MU3C_U3_PORT_MAX 4
+#define MU3C_U2_PORT_MAX 5
+
+/**
+ * struct mtk_ippc_regs: MTK ssusb ip port control registers
+ * @ip_pw_ctr0~3: ip power and clock control registers
+ * @ip_pw_sts1~2: ip power and clock status registers
+ * @ip_xhci_cap: ip xHCI capability register
+ * @u3_ctrl_p[x]: ip usb3 port x control register, only low 4bytes are used
+ * @u2_ctrl_p[x]: ip usb2 port x control register, only low 4bytes are used
+ * @u2_phy_pll: usb2 phy pll control register
+ */
+struct mtk_ippc_regs {
+	__le32 ip_pw_ctr0;
+	__le32 ip_pw_ctr1;
+	__le32 ip_pw_ctr2;
+	__le32 ip_pw_ctr3;
+	__le32 ip_pw_sts1;
+	__le32 ip_pw_sts2;
+	__le32 reserved0[3];
+	__le32 ip_xhci_cap;
+	__le32 reserved1[2];
+	__le64 u3_ctrl_p[MU3C_U3_PORT_MAX];
+	__le64 u2_ctrl_p[MU3C_U2_PORT_MAX];
+	__le32 reserved2;
+	__le32 u2_phy_pll;
+	__le32 reserved3[33]; /* 0x80 ~ 0xff */
+};
+
+/* ip_pw_ctrl0 register */
+#define CTRL0_IP_SW_RST	BIT(0)
+
+/* ip_pw_ctrl1 register */
+#define CTRL1_IP_HOST_PDN	BIT(0)
+
+/* ip_pw_ctrl2 register */
+#define CTRL2_IP_DEV_PDN	BIT(0)
+
+/* ip_pw_sts1 register */
+#define STS1_IP_SLEEP_STS	BIT(30)
+#define STS1_U3_MAC_RST	BIT(16)
+#define STS1_XHCI_RST		BIT(11)
+#define STS1_SYS125_RST	BIT(10)
+#define STS1_REF_RST		BIT(8)
+#define STS1_SYSPLL_STABLE	BIT(0)
+
+/* ip_xhci_cap register */
+#define CAP_U3_PORT_NUM(p)	((p) & 0xff)
+#define CAP_U2_PORT_NUM(p)	(((p) >> 8) & 0xff)
+
+/* u3_ctrl_p register */
+#define CTRL_U3_PORT_HOST_SEL	BIT(2)
+#define CTRL_U3_PORT_PDN	BIT(1)
+#define CTRL_U3_PORT_DIS	BIT(0)
+
+/* u2_ctrl_p register */
+#define CTRL_U2_PORT_HOST_SEL	BIT(2)
+#define CTRL_U2_PORT_PDN	BIT(1)
+#define CTRL_U2_PORT_DIS	BIT(0)
+
+struct mtk_xhci {
+	struct xhci_ctrl ctrl;	/* Needs to come first in this struct! */
+	struct xhci_hccr *hcd;
+	struct mtk_ippc_regs *ippc;
+	struct udevice *dev;
+	struct udevice *vusb33_supply;
+	struct udevice *vbus_supply;
+	struct clk *sys_clk;
+	struct clk *xhci_clk;
+	struct clk *ref_clk;
+	struct clk *mcu_clk;
+	struct clk *dma_clk;
+	int num_u2_ports;
+	int num_u3_ports;
+	struct phy *phys;
+	int num_phys;
+};
+
+static int xhci_mtk_host_enable(struct mtk_xhci *mtk)
+{
+	struct mtk_ippc_regs *ippc = mtk->ippc;
+	u32 value, check_val;
+	int ret;
+	int i;
+
+	/* power on host ip */
+	value = readl(&ippc->ip_pw_ctr1);
+	value &= ~CTRL1_IP_HOST_PDN;
+	writel(value, &ippc->ip_pw_ctr1);
+
+	/* power on and enable all u3 ports */
+	for (i = 0; i < mtk->num_u3_ports; i++) {
+		value = readl(&ippc->u3_ctrl_p[i]);
+		value &= ~(CTRL_U3_PORT_PDN | CTRL_U3_PORT_DIS);
+		value |= CTRL_U3_PORT_HOST_SEL;
+		writel(value, &ippc->u3_ctrl_p[i]);
+	}
+
+	/* power on and enable all u2 ports */
+	for (i = 0; i < mtk->num_u2_ports; i++) {
+		value = readl(&ippc->u2_ctrl_p[i]);
+		value &= ~(CTRL_U2_PORT_PDN | CTRL_U2_PORT_DIS);
+		value |= CTRL_U2_PORT_HOST_SEL;
+		writel(value, &ippc->u2_ctrl_p[i]);
+	}
+
+	/*
+	 * wait for clocks to be stable, and clock domains reset to
+	 * be inactive after power on and enable ports
+	 */
+	check_val = STS1_SYSPLL_STABLE | STS1_REF_RST |
+			STS1_SYS125_RST | STS1_XHCI_RST;
+
+	if (mtk->num_u3_ports)
+		check_val |= STS1_U3_MAC_RST;
+
+	ret = readl_poll_timeout(&ippc->ip_pw_sts1, value,
+				 (check_val == (value & check_val)), 20000);
+	if (ret) {
+		dev_err(mtk->dev, "clocks are not stable (0x%x)\n", value);
+		return ret;
+	}
+
+	return 0;
+}
+
+static int xhci_mtk_host_disable(struct mtk_xhci *mtk)
+{
+	struct mtk_ippc_regs *ippc = mtk->ippc;
+	u32 value;
+	int i;
+
+	/* power down all u3 ports */
+	for (i = 0; i < mtk->num_u3_ports; i++) {
+		value = readl(&ippc->u3_ctrl_p[i]);
+		value |= CTRL_U3_PORT_PDN;
+		writel(value, &ippc->u3_ctrl_p[i]);
+	}
+
+	/* power down all u2 ports */
+	for (i = 0; i < mtk->num_u2_ports; i++) {
+		value = readl(&ippc->u2_ctrl_p[i]);
+		value |= CTRL_U2_PORT_PDN;
+		writel(value, &ippc->u2_ctrl_p[i]);
+	}
+
+	/* power down host ip */
+	value = readl(&ippc->ip_pw_ctr1);
+	value |= CTRL1_IP_HOST_PDN;
+	writel(value, &ippc->ip_pw_ctr1);
+
+	return 0;
+}
+
+static int xhci_mtk_ssusb_init(struct mtk_xhci *mtk)
+{
+	struct mtk_ippc_regs *ippc = mtk->ippc;
+	u32 value;
+
+	/* reset whole ip */
+	value = readl(&ippc->ip_pw_ctr0);
+	value |= CTRL0_IP_SW_RST;
+	writel(value, &ippc->ip_pw_ctr0);
+	udelay(1);
+	value = readl(&ippc->ip_pw_ctr0);
+	value &= ~CTRL0_IP_SW_RST;
+	writel(value, &ippc->ip_pw_ctr0);
+
+	value = readl(&ippc->ip_xhci_cap);
+	mtk->num_u3_ports = CAP_U3_PORT_NUM(value);
+	mtk->num_u2_ports = CAP_U2_PORT_NUM(value);
+	dev_info(mtk->dev, "%s u2p:%d, u3p:%d\n", __func__,
+		 mtk->num_u2_ports, mtk->num_u3_ports);
+
+	return xhci_mtk_host_enable(mtk);
+}
+
+static int xhci_mtk_clks_enable(struct mtk_xhci *mtk)
+{
+	int ret;
+
+	ret = clk_enable(mtk->sys_clk);
+	if (ret) {
+		dev_err(mtk->dev, "failed to enable sys_clk\n");
+		goto sys_clk_err;
+	}
+
+	ret = clk_enable(mtk->ref_clk);
+	if (ret) {
+		dev_err(mtk->dev, "failed to enable ref_clk\n");
+		goto ref_clk_err;
+	}
+
+	ret = clk_enable(mtk->xhci_clk);
+	if (ret) {
+		dev_err(mtk->dev, "failed to enable xhci_clk\n");
+		goto xhci_clk_err;
+	}
+
+	ret = clk_enable(mtk->mcu_clk);
+	if (ret) {
+		dev_err(mtk->dev, "failed to enable mcu_clk\n");
+		goto mcu_clk_err;
+	}
+
+	ret = clk_enable(mtk->dma_clk);
+	if (ret) {
+		dev_err(mtk->dev, "failed to enable dma_clk\n");
+		goto dma_clk_err;
+	}
+
+	return 0;
+
+dma_clk_err:
+	clk_disable(mtk->mcu_clk);
+mcu_clk_err:
+	clk_disable(mtk->xhci_clk);
+xhci_clk_err:
+	clk_disable(mtk->sys_clk);
+sys_clk_err:
+	clk_disable(mtk->ref_clk);
+ref_clk_err:
+	return ret;
+}
+
+static void xhci_mtk_clks_disable(struct mtk_xhci *mtk)
+{
+	clk_disable(mtk->dma_clk);
+	clk_disable(mtk->mcu_clk);
+	clk_disable(mtk->xhci_clk);
+	clk_disable(mtk->ref_clk);
+	clk_disable(mtk->sys_clk);
+}
+
+static int xhci_mtk_ofdata_get(struct mtk_xhci *mtk)
+{
+	struct udevice *dev = mtk->dev;
+	fdt_addr_t reg_base;
+	int ret = 0;
+
+	reg_base = devfdt_get_addr_name(dev, "mac");
+	if (reg_base == FDT_ADDR_T_NONE) {
+		pr_err("Failed to get xHCI base address\n");
+		return -ENXIO;
+	}
+
+	mtk->hcd = (struct xhci_hccr *)reg_base;
+
+	reg_base = devfdt_get_addr_name(dev, "ippc");
+	if (reg_base == FDT_ADDR_T_NONE) {
+		pr_err("Failed to get IPPC base address\n");
+		return -ENXIO;
+	}
+
+	mtk->ippc = (struct mtk_ippc_regs *)reg_base;
+
+	dev_info(dev, "hcd: 0x%x, ippc: 0x%x\n",
+		 (int)mtk->hcd, (int)mtk->ippc);
+
+	mtk->sys_clk = devm_clk_get(dev, "sys_ck");
+	if (IS_ERR(mtk->sys_clk)) {
+		dev_err(dev, "Failed to get sys clock\n");
+		goto clk_err;
+	}
+
+	mtk->ref_clk = devm_clk_get_optional(dev, "ref_ck");
+	if (IS_ERR(mtk->ref_clk)) {
+		dev_err(dev, "Failed to get ref clock\n");
+		goto clk_err;
+	}
+
+	mtk->xhci_clk = devm_clk_get_optional(dev, "xhci_ck");
+	if (IS_ERR(mtk->xhci_clk)) {
+		dev_err(dev, "Failed to get xhci clock\n");
+		goto clk_err;
+	}
+
+	mtk->mcu_clk = devm_clk_get_optional(dev, "mcu_ck");
+	if (IS_ERR(mtk->mcu_clk)) {
+		dev_err(dev, "Failed to get mcu clock\n");
+		goto clk_err;
+	}
+
+	mtk->dma_clk = devm_clk_get_optional(dev, "dma_ck");
+	if (IS_ERR(mtk->dma_clk)) {
+		dev_err(dev, "Failed to get dma clock\n");
+		goto clk_err;
+	}
+
+	ret = device_get_supply_regulator(dev, "vusb33-supply",
+					  &mtk->vusb33_supply);
+	if (ret)
+		debug("Can't get vusb33 regulator! %d\n", ret);
+
+	ret = device_get_supply_regulator(dev, "vbus-supply",
+					  &mtk->vbus_supply);
+	if (ret)
+		debug("Can't get vbus regulator! %d\n", ret);
+
+	return 0;
+
+clk_err:
+	return ret;
+}
+
+static int xhci_mtk_ldos_enable(struct mtk_xhci *mtk)
+{
+	int ret;
+
+	ret = regulator_set_enable(mtk->vusb33_supply, true);
+	if (ret < 0 && ret != -ENOSYS) {
+		dev_err(mtk->dev, "failed to enable vusb33\n");
+		return ret;
+	}
+
+	ret = regulator_set_enable(mtk->vbus_supply, true);
+	if (ret < 0 && ret != -ENOSYS) {
+		dev_err(mtk->dev, "failed to enable vbus\n");
+		regulator_set_enable(mtk->vusb33_supply, false);
+		return ret;
+	}
+
+	return 0;
+}
+
+static void xhci_mtk_ldos_disable(struct mtk_xhci *mtk)
+{
+	regulator_set_enable(mtk->vbus_supply, false);
+	regulator_set_enable(mtk->vusb33_supply, false);
+}
+
+int xhci_mtk_phy_setup(struct mtk_xhci *mtk)
+{
+	struct udevice *dev = mtk->dev;
+	struct phy *usb_phys;
+	int i, ret, count;
+
+	/* Return if no phy declared */
+	if (!dev_read_prop(dev, "phys", NULL))
+		return 0;
+
+	count = dev_count_phandle_with_args(dev, "phys", "#phy-cells");
+	if (count <= 0)
+		return count;
+
+	usb_phys = devm_kcalloc(dev, count, sizeof(*usb_phys),
+				GFP_KERNEL);
+	if (!usb_phys)
+		return -ENOMEM;
+
+	for (i = 0; i < count; i++) {
+		ret = generic_phy_get_by_index(dev, i, &usb_phys[i]);
+		if (ret && ret != -ENOENT) {
+			dev_err(dev, "Failed to get USB PHY%d for %s\n",
+				i, dev->name);
+			return ret;
+		}
+	}
+
+	for (i = 0; i < count; i++) {
+		ret = generic_phy_init(&usb_phys[i]);
+		if (ret) {
+			dev_err(dev, "Can't init USB PHY%d for %s\n",
+				i, dev->name);
+			goto phys_init_err;
+		}
+	}
+
+	for (i = 0; i < count; i++) {
+		ret = generic_phy_power_on(&usb_phys[i]);
+		if (ret) {
+			dev_err(dev, "Can't power USB PHY%d for %s\n",
+				i, dev->name);
+			goto phys_poweron_err;
+		}
+	}
+
+	mtk->phys = usb_phys;
+	mtk->num_phys =  count;
+
+	return 0;
+
+phys_poweron_err:
+	for (i = count - 1; i >= 0; i--)
+		generic_phy_power_off(&usb_phys[i]);
+
+	for (i = 0; i < count; i++)
+		generic_phy_exit(&usb_phys[i]);
+
+	return ret;
+
+phys_init_err:
+	for (; i >= 0; i--)
+		generic_phy_exit(&usb_phys[i]);
+
+	return ret;
+}
+
+void xhci_mtk_phy_shutdown(struct mtk_xhci *mtk)
+{
+	struct udevice *dev = mtk->dev;
+	struct phy *usb_phys;
+	int i, ret;
+
+	usb_phys = mtk->phys;
+	for (i = 0; i < mtk->num_phys; i++) {
+		if (!generic_phy_valid(&usb_phys[i]))
+			continue;
+
+		ret = generic_phy_power_off(&usb_phys[i]);
+		ret |= generic_phy_exit(&usb_phys[i]);
+		if (ret) {
+			dev_err(dev, "Can't shutdown USB PHY%d for %s\n",
+				i, dev->name);
+		}
+	}
+}
+
+static int xhci_mtk_probe(struct udevice *dev)
+{
+	struct mtk_xhci *mtk = dev_get_priv(dev);
+	struct xhci_hcor *hcor;
+	int ret;
+
+	mtk->dev = dev;
+	ret = xhci_mtk_ofdata_get(mtk);
+	if (ret)
+		return ret;
+
+	ret = xhci_mtk_ldos_enable(mtk);
+	if (ret)
+		goto ldos_err;
+
+	ret = xhci_mtk_clks_enable(mtk);
+	if (ret)
+		goto clks_err;
+
+	ret = xhci_mtk_phy_setup(mtk);
+	if (ret)
+		goto phys_err;
+
+	ret = xhci_mtk_ssusb_init(mtk);
+	if (ret)
+		goto ssusb_init_err;
+
+	hcor = (struct xhci_hcor *)((uintptr_t)mtk->hcd +
+			HC_LENGTH(xhci_readl(&mtk->hcd->cr_capbase)));
+
+	return xhci_register(dev, mtk->hcd, hcor);
+
+ssusb_init_err:
+	xhci_mtk_phy_shutdown(mtk);
+phys_err:
+	xhci_mtk_clks_disable(mtk);
+clks_err:
+	xhci_mtk_ldos_disable(mtk);
+ldos_err:
+	return ret;
+}
+
+static int xhci_mtk_remove(struct udevice *dev)
+{
+	struct mtk_xhci *mtk = dev_get_priv(dev);
+
+	xhci_deregister(dev);
+	xhci_mtk_host_disable(mtk);
+	xhci_mtk_ldos_disable(mtk);
+	xhci_mtk_clks_disable(mtk);
+
+	return 0;
+}
+
+static const struct udevice_id xhci_mtk_ids[] = {
+	{ .compatible = "mediatek,mtk-xhci" },
+	{ }
+};
+
+U_BOOT_DRIVER(usb_xhci) = {
+	.name	= "xhci-mtk",
+	.id	= UCLASS_USB,
+	.of_match = xhci_mtk_ids,
+	.probe = xhci_mtk_probe,
+	.remove = xhci_mtk_remove,
+	.ops	= &xhci_usb_ops,
+	.bind	= dm_scan_fdt_dev,
+	.priv_auto_alloc_size = sizeof(struct mtk_xhci),
+	.flags	= DM_FLAG_ALLOC_PRIV_DMA,
+};