Linux-mediatek Archive on lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH net-next v2 RESEND 0/3] net: ethernet: mediatek: check the hw lro capability by the chip id instead of the dtsi
From: Nelson Chang @ 2016-10-05 12:32 UTC (permalink / raw)
  To: john, davem; +Cc: nbd, netdev, linux-mediatek, nelsonch.tw, Nelson Chang

The series modify to check if hw lro is supported by the chip id.

changes since v2:
- Refine mtk_get_chip_id() function

changes since v1:
- Because hw lro started to be supported from MT7623, the proper way to check if the feature is capable is to judge by the chip id instead of by the dtsi.

Nelson Chang (3):
  net: ethernet: mediatek: get the chip id by ETHDMASYS registers
  net: ethernet: mediatek: get hw lro capability by the chip id instead
    of by the dtsi
  net: ethernet: mediatek: remove hwlro property in the device tree

 .../devicetree/bindings/net/mediatek-net.txt       |  2 --
 drivers/net/ethernet/mediatek/mtk_eth_soc.c        | 41 ++++++++++++++++++++--
 drivers/net/ethernet/mediatek/mtk_eth_soc.h        |  6 ++++
 3 files changed, 45 insertions(+), 4 deletions(-)

-- 
1.9.1

^ permalink raw reply

* [PATCH net-next v2 RESEND 1/3] net: ethernet: mediatek: get the chip id by ETHDMASYS registers
From: Nelson Chang @ 2016-10-05 12:32 UTC (permalink / raw)
  To: john, davem; +Cc: nbd, netdev, linux-mediatek, nelsonch.tw, Nelson Chang
In-Reply-To: <1475670742-11498-1-git-send-email-nelson.chang@mediatek.com>

The driver gets the chip id by ETHSYS_CHIPID0_3/ETHSYS_CHIPID4_7 registers
in mtk_probe().

Signed-off-by: Nelson Chang <nelson.chang@mediatek.com>
---
 drivers/net/ethernet/mediatek/mtk_eth_soc.c | 29 +++++++++++++++++++++++++++++
 drivers/net/ethernet/mediatek/mtk_eth_soc.h |  5 +++++
 2 files changed, 34 insertions(+)

diff --git a/drivers/net/ethernet/mediatek/mtk_eth_soc.c b/drivers/net/ethernet/mediatek/mtk_eth_soc.c
index ad4ab97..0c67ab1 100644
--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c
+++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c
@@ -2323,6 +2323,31 @@ free_netdev:
 	return err;
 }
 
+static int mtk_get_chip_id(struct mtk_eth *eth, u32 *chip_id)
+{
+	u32 val[2], id[4];
+
+	regmap_read(eth->ethsys, ETHSYS_CHIPID0_3, &val[0]);
+	regmap_read(eth->ethsys, ETHSYS_CHIPID4_7, &val[1]);
+
+	id[3] = ((val[0] >> 16) & 0xff) - '0';
+	id[2] = ((val[0] >> 24) & 0xff) - '0';
+	id[1] = (val[1] & 0xff) - '0';
+	id[0] = ((val[1] >> 8) & 0xff) - '0';
+
+	*chip_id = (id[3] * 1000) + (id[2] * 100) +
+		   (id[1] * 10) + id[0];
+
+	if (!(*chip_id)) {
+		dev_err(eth->dev, "failed to get chip id\n");
+		return -ENODEV;
+	}
+
+	dev_info(eth->dev, "chip id = %d\n", *chip_id);
+
+	return 0;
+}
+
 static int mtk_probe(struct platform_device *pdev)
 {
 	struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
@@ -2388,6 +2413,10 @@ static int mtk_probe(struct platform_device *pdev)
 	if (err)
 		return err;
 
+	err = mtk_get_chip_id(eth, &eth->chip_id);
+	if (err)
+		return err;
+
 	for_each_child_of_node(pdev->dev.of_node, mac_np) {
 		if (!of_device_is_compatible(mac_np,
 					     "mediatek,eth-mac"))
diff --git a/drivers/net/ethernet/mediatek/mtk_eth_soc.h b/drivers/net/ethernet/mediatek/mtk_eth_soc.h
index 3003195..a5b422b 100644
--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.h
+++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.h
@@ -342,6 +342,10 @@
 #define GPIO_BIAS_CTRL		0xed0
 #define GPIO_DRV_SEL10		0xf00
 
+/* ethernet subsystem chip id register */
+#define ETHSYS_CHIPID0_3	0x0
+#define ETHSYS_CHIPID4_7	0x4
+
 /* ethernet subsystem config register */
 #define ETHSYS_SYSCFG0		0x14
 #define SYSCFG0_GE_MASK		0x3
@@ -534,6 +538,7 @@ struct mtk_eth {
 	unsigned long			sysclk;
 	struct regmap			*ethsys;
 	struct regmap			*pctl;
+	u32				chip_id;
 	bool				hwlro;
 	atomic_t			dma_refcnt;
 	struct mtk_tx_ring		tx_ring;
-- 
1.9.1

^ permalink raw reply related

* [PATCH net-next v2 RESEND 2/3] net: ethernet: mediatek: get hw lro capability by the chip id instead of by the dtsi
From: Nelson Chang @ 2016-10-05 12:32 UTC (permalink / raw)
  To: john, davem; +Cc: nbd, netdev, linux-mediatek, nelsonch.tw, Nelson Chang
In-Reply-To: <1475670742-11498-1-git-send-email-nelson.chang@mediatek.com>

Because hw lro started to be supported from MT7623, the proper way to check if
the feature is capable is to judge by the chip id instead of by the dtsi.

Signed-off-by: Nelson Chang <nelson.chang@mediatek.com>
---
 drivers/net/ethernet/mediatek/mtk_eth_soc.c | 12 ++++++++++--
 drivers/net/ethernet/mediatek/mtk_eth_soc.h |  1 +
 2 files changed, 11 insertions(+), 2 deletions(-)

diff --git a/drivers/net/ethernet/mediatek/mtk_eth_soc.c b/drivers/net/ethernet/mediatek/mtk_eth_soc.c
index 0c67ab1..07f3ffa 100644
--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c
+++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c
@@ -2348,6 +2348,14 @@ static int mtk_get_chip_id(struct mtk_eth *eth, u32 *chip_id)
 	return 0;
 }
 
+static bool mtk_is_hwlro_supported(struct mtk_eth *eth)
+{
+	if (eth->chip_id == MT7623_ETH)
+		return true;
+	else
+		return false;
+}
+
 static int mtk_probe(struct platform_device *pdev)
 {
 	struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
@@ -2387,8 +2395,6 @@ static int mtk_probe(struct platform_device *pdev)
 		return PTR_ERR(eth->pctl);
 	}
 
-	eth->hwlro = of_property_read_bool(pdev->dev.of_node, "mediatek,hwlro");
-
 	for (i = 0; i < 3; i++) {
 		eth->irq[i] = platform_get_irq(pdev, i);
 		if (eth->irq[i] < 0) {
@@ -2417,6 +2423,8 @@ static int mtk_probe(struct platform_device *pdev)
 	if (err)
 		return err;
 
+	eth->hwlro = mtk_is_hwlro_supported(eth);
+
 	for_each_child_of_node(pdev->dev.of_node, mac_np) {
 		if (!of_device_is_compatible(mac_np,
 					     "mediatek,eth-mac"))
diff --git a/drivers/net/ethernet/mediatek/mtk_eth_soc.h b/drivers/net/ethernet/mediatek/mtk_eth_soc.h
index a5b422b..99b1c8e 100644
--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.h
+++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.h
@@ -345,6 +345,7 @@
 /* ethernet subsystem chip id register */
 #define ETHSYS_CHIPID0_3	0x0
 #define ETHSYS_CHIPID4_7	0x4
+#define MT7623_ETH		7623
 
 /* ethernet subsystem config register */
 #define ETHSYS_SYSCFG0		0x14
-- 
1.9.1

^ permalink raw reply related

* [PATCH net-next v2 RESEND 3/3] net: ethernet: mediatek: remove hwlro property in the device tree
From: Nelson Chang @ 2016-10-05 12:32 UTC (permalink / raw)
  To: john, davem; +Cc: nbd, netdev, linux-mediatek, nelsonch.tw, Nelson Chang
In-Reply-To: <1475670742-11498-1-git-send-email-nelson.chang@mediatek.com>

Since the proper way to check the hw lro capability is by the chip id,
hwlro property in the device tree should be removed.

Signed-off-by: Nelson Chang <nelson.chang@mediatek.com>
---
 Documentation/devicetree/bindings/net/mediatek-net.txt | 2 --
 1 file changed, 2 deletions(-)

diff --git a/Documentation/devicetree/bindings/net/mediatek-net.txt b/Documentation/devicetree/bindings/net/mediatek-net.txt
index f095257..c010faf 100644
--- a/Documentation/devicetree/bindings/net/mediatek-net.txt
+++ b/Documentation/devicetree/bindings/net/mediatek-net.txt
@@ -24,7 +24,6 @@ Required properties:
 Optional properties:
 - interrupt-parent: Should be the phandle for the interrupt controller
   that services interrupts for this device
-- mediatek,hwlro: the capability if the hardware supports LRO functions
 
 * Ethernet MAC node
 
@@ -54,7 +53,6 @@ eth: ethernet@1b100000 {
 	reset-names = "eth";
 	mediatek,ethsys = <&ethsys>;
 	mediatek,pctl = <&syscfg_pctl_a>;
-	mediatek,hwlro;
 	#address-cells = <1>;
 	#size-cells = <0>;
 
-- 
1.9.1

^ permalink raw reply related

* RE: [PATCH net-next v2 2/3] net: ethernet: mediatek: get hw lro capability by the chip id instead of by the dtsi
From: Nelson Chang @ 2016-10-05 12:38 UTC (permalink / raw)
  To: sergei.shtylyov, john, davem; +Cc: nbd, netdev, linux-mediatek, nelsonch.tw

Hi Sergei,

Thanks for your comments.
Modified.


Nelson

-----Original Message-----
From: Sergei Shtylyov [mailto:sergei.shtylyov@cogentembedded.com] 
Sent: Wednesday, October 05, 2016 8:18 PM
To: Nelson Chang (張家祥); john@phrozen.org; davem@davemloft.net
Cc: nbd@openwrt.org; netdev@vger.kernel.org;
linux-mediatek@lists.infradead.org; nelsonch.tw@gmail.com
Subject: Re: [PATCH net-next v2 2/3] net: ethernet: mediatek: get hw lro
capability by the chip id instead of by the dtsi

Hello.

On 10/05/2016 03:12 PM, Nelson Chang wrote:

> Because hw lro started to be supported from MT7623, the proper way to 
> check if the feature is capable is to judge by the chip id instead of
by the dtsi.
>
> Signed-off-by: Nelson Chang <nelson.chang@mediatek.com>
> ---
>  drivers/net/ethernet/mediatek/mtk_eth_soc.c | 12 ++++++++++--  
> drivers/net/ethernet/mediatek/mtk_eth_soc.h |  1 +
>  2 files changed, 11 insertions(+), 2 deletions(-)
>
> diff --git a/drivers/net/ethernet/mediatek/mtk_eth_soc.c 
> b/drivers/net/ethernet/mediatek/mtk_eth_soc.c
> index 0c67ab1..07f3ffa 100644
> --- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c
> +++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c
> @@ -2348,6 +2348,14 @@ static int mtk_get_chip_id(struct mtk_eth *eth,
u32 *chip_id)
>  	return 0;
>  }
>
> +static bool mtk_is_hwlro_supported(struct mtk_eth *eth) {
> +	if (eth->chip_id == MT7623_ETH)
> +		return true;
> +	else
> +		return false;

	return eth->chip_id == MT7623_ETH;

[...]
> diff --git a/drivers/net/ethernet/mediatek/mtk_eth_soc.h 
> b/drivers/net/ethernet/mediatek/mtk_eth_soc.h
> index a5b422b..58738fd 100644
> --- a/drivers/net/ethernet/mediatek/mtk_eth_soc.h
> +++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.h
> @@ -345,6 +345,7 @@
>  /* ethernet subsystem chip id register */
>  #define ETHSYS_CHIPID0_3	0x0
>  #define ETHSYS_CHIPID4_7	0x4
> +#define MT7623_ETH		(7623)

    () not needed at all.

MBR, Sergei

^ permalink raw reply

* RE: [PATCH net-next v2 2/3] net: ethernet: mediatek: get hw lro capability by the chip id instead of by the dtsi
From: Nelson Chang @ 2016-10-05 12:46 UTC (permalink / raw)
  To: sergei.shtylyov, john, davem; +Cc: nbd, netdev, linux-mediatek, nelsonch.tw

Hi Sergei,

Sorry, miss that.

> +static bool mtk_is_hwlro_supported(struct mtk_eth *eth) {
> +     if (eth->chip_id == MT7623_ETH)
> +             return true;
> +     else
> +             return false;

        return eth->chip_id == MT7623_ETH;

=> Since there will be more chips support hw lro in the future, keep the
original codes to have the scalability like this:
if (eth->chip_id == MTxxxx_ETH ||
    eth->chip_id == MTyyyy_ETH ||
    ....)
	return true;



Nelson

-----Original Message-----
From: Sergei Shtylyov [mailto:sergei.shtylyov@cogentembedded.com]
Sent: Wednesday, October 05, 2016 8:18 PM
To: Nelson Chang (張家祥); john@phrozen.org; davem@davemloft.net
Cc: nbd@openwrt.org; netdev@vger.kernel.org;
linux-mediatek@lists.infradead.org; nelsonch.tw@gmail.com
Subject: Re: [PATCH net-next v2 2/3] net: ethernet: mediatek: get hw lro
capability by the chip id instead of by the dtsi

Hello.

On 10/05/2016 03:12 PM, Nelson Chang wrote:

> Because hw lro started to be supported from MT7623, the proper way to
> check if the feature is capable is to judge by the chip id instead of
by the dtsi.
>
> Signed-off-by: Nelson Chang <nelson.chang@mediatek.com>
> ---
>  drivers/net/ethernet/mediatek/mtk_eth_soc.c | 12 ++++++++++--
> drivers/net/ethernet/mediatek/mtk_eth_soc.h |  1 +
>  2 files changed, 11 insertions(+), 2 deletions(-)
>
> diff --git a/drivers/net/ethernet/mediatek/mtk_eth_soc.c
> b/drivers/net/ethernet/mediatek/mtk_eth_soc.c
> index 0c67ab1..07f3ffa 100644
> --- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c
> +++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c
> @@ -2348,6 +2348,14 @@ static int mtk_get_chip_id(struct mtk_eth *eth,
u32 *chip_id)
>       return 0;
>  }
>
> +static bool mtk_is_hwlro_supported(struct mtk_eth *eth) {
> +     if (eth->chip_id == MT7623_ETH)
> +             return true;
> +     else
> +             return false;

        return eth->chip_id == MT7623_ETH;

[...]
> diff --git a/drivers/net/ethernet/mediatek/mtk_eth_soc.h
> b/drivers/net/ethernet/mediatek/mtk_eth_soc.h
> index a5b422b..58738fd 100644
> --- a/drivers/net/ethernet/mediatek/mtk_eth_soc.h
> +++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.h
> @@ -345,6 +345,7 @@
>  /* ethernet subsystem chip id register */
>  #define ETHSYS_CHIPID0_3     0x0
>  #define ETHSYS_CHIPID4_7     0x4
> +#define MT7623_ETH           (7623)

    () not needed at all.

MBR, Sergei

^ permalink raw reply

* Re: [PATCH net-next v2 2/3] net: ethernet: mediatek: get hw lro capability by the chip id instead of by the dtsi
From: Sergei Shtylyov @ 2016-10-05 12:57 UTC (permalink / raw)
  To: Nelson Chang, john, davem; +Cc: nbd, netdev, linux-mediatek, nelsonch.tw
In-Reply-To: <1475671572.6101.7.camel@mtksdaap41>

On 10/05/2016 03:46 PM, Nelson Chang wrote:

>> +static bool mtk_is_hwlro_supported(struct mtk_eth *eth) {
>> +     if (eth->chip_id == MT7623_ETH)
>> +             return true;
>> +     else
>> +             return false;
>
>         return eth->chip_id == MT7623_ETH;
>
> => Since there will be more chips support hw lro in the future, keep the
> original codes to have the scalability like this:
> if (eth->chip_id == MTxxxx_ETH ||
>     eth->chip_id == MTyyyy_ETH ||
>     ....)
> 	return true;

    Then use *switch*, not *if*.

> Nelson

MBR, Sergei

^ permalink raw reply

* RE: [PATCH net-next v2 2/3] net: ethernet: mediatek: get hw lro capability by the chip id instead of by the dtsi
From: David Laight @ 2016-10-05 13:07 UTC (permalink / raw)
  To: 'Nelson Chang', sergei.shtylyov@cogentembedded.com,
	john@phrozen.org, davem@davemloft.net
  Cc: nbd@openwrt.org, netdev@vger.kernel.org,
	linux-mediatek@lists.infradead.org, nelsonch.tw@gmail.com
In-Reply-To: <1475671572.6101.7.camel@mtksdaap41>

From: Nelson Chang
> Sent: 05 October 2016 13:46
> > +static bool mtk_is_hwlro_supported(struct mtk_eth *eth) {
> > +     if (eth->chip_id == MT7623_ETH)
> > +             return true;
> > +     else
> > +             return false;
> 
>         return eth->chip_id == MT7623_ETH;
> 
> => Since there will be more chips support hw lro in the future, keep the
> original codes to have the scalability like this:
> if (eth->chip_id == MTxxxx_ETH ||
>     eth->chip_id == MTyyyy_ETH ||
>     ....)
> 	return true;

Nothing wrong with:

	return eth->chip_id == MTxxxx_ETH ||
		eth->chip_id == MTyyyy_ETH;

	David


^ permalink raw reply

* RE: [PATCH net-next v2 2/3] net: ethernet: mediatek: get hw lro capability by the chip id instead of by the dtsi
From: Nelson Chang @ 2016-10-05 13:21 UTC (permalink / raw)
  To: David.Laight, sergei.shtylyov, john, davem
  Cc: nbd, netdev, linux-mediatek, nelsonch.tw

Hi Sergei, David,

I think modifying that as below is clear and scalable. Agree?

static bool mtk_is_hwlro_supported(struct mtk_eth *eth)
{
	switch (eth->chip_id) {
	case MT7623_ETH:
		return true;
	}

	return false;
}

Thanks.


Nelson
-----Original Message-----
From: David Laight [mailto:David.Laight@ACULAB.COM] 
Sent: Wednesday, October 05, 2016 9:07 PM
To: Nelson Chang (張家祥); sergei.shtylyov@cogentembedded.com;
john@phrozen.org; davem@davemloft.net
Cc: nbd@openwrt.org; netdev@vger.kernel.org;
linux-mediatek@lists.infradead.org; nelsonch.tw@gmail.com
Subject: RE: [PATCH net-next v2 2/3] net: ethernet: mediatek: get hw lro
capability by the chip id instead of by the dtsi

From: Nelson Chang
> Sent: 05 October 2016 13:46
> > +static bool mtk_is_hwlro_supported(struct mtk_eth *eth) {
> > +     if (eth->chip_id == MT7623_ETH)
> > +             return true;
> > +     else
> > +             return false;
> 
>         return eth->chip_id == MT7623_ETH;
> 
> => Since there will be more chips support hw lro in the future, keep 
> the original codes to have the scalability like this:
> if (eth->chip_id == MTxxxx_ETH ||
>     eth->chip_id == MTyyyy_ETH ||
>     ....)
> 	return true;

Nothing wrong with:

	return eth->chip_id == MTxxxx_ETH ||
		eth->chip_id == MTyyyy_ETH;

	David

^ permalink raw reply

* Re: [PATCH v14 2/4] CMDQ: Mediatek CMDQ driver
From: Jassi Brar @ 2016-10-05 14:43 UTC (permalink / raw)
  To: Horng-Shyang Liao
  Cc: CK Hu, Daniel Kurtz, Monica Wang, Jiaguang Zhang, Nicolas Boichat,
	Jassi Brar, cawa cheng, Bibby Hsieh, YT Shen, Damon Chu,
	Devicetree List, Sascha Hauer, Daoyuan Huang, Sascha Hauer,
	Glory Hung, Rob Herring, linux-mediatek, Matthias Brugger,
	"linux-arm-kernel@lists.infradead.org" <linux-arm-kernel>
In-Reply-To: <1475670705.335.27.camel@mtksdaap41>

On 5 October 2016 at 18:01, Horng-Shyang Liao <hs.liao@mediatek.com> wrote:
> On Wed, 2016-10-05 at 09:07 +0530, Jassi Brar wrote:
>> On 5 October 2016 at 08:24, Horng-Shyang Liao <hs.liao@mediatek.com> wrote:
>> > On Fri, 2016-09-30 at 17:47 +0800, Horng-Shyang Liao wrote:
>> >> On Fri, 2016-09-30 at 17:11 +0800, CK Hu wrote:
>>
>> >
>> > After I trace mailbox driver, I realize that CMDQ driver cannot use
>> > tx_done.
>> >
>> > CMDQ clients will flush many tasks into CMDQ driver, and then CMDQ
>> > driver will apply these tasks into GCE HW "immediately". These tasks,
>> > which are queued in GCE HW, may not execute immediately since they
>> > may need to wait event(s), e.g. vsync.
>> >
>> > However, in mailbox driver, mailbox uses a software buffer to queue
>> > sent messages. It only sends next message until previous message is
>> > done. This cannot fulfill CMDQ's requirement.
>> >
>> I understand
>>  a) GCE HW can internally queue many tasks in some 'FIFO'
>>  b) Execution of some task may have to wait until some external event
>> occurs (like vsync)
>>  c) GCE does not generate irq/flag for each task executed (?)
>>
>> If so, may be your tx_done should return 'true' so long as the GCE HW
>> can accept tasks in its 'FIFO'. For mailbox api, any task that is
>> queued on GCE, is assumed to be transmitted.
>>
>> > Quote some code from mailbox driver. Please notice "active_req" part.
>> >
>> > static void msg_submit(struct mbox_chan *chan)
>> > {
>> >         ...
>> >         if (!chan->msg_count || chan->active_req)
>> >                 goto exit;
>> >         ...
>> >         err = chan->mbox->ops->send_data(chan, data);
>> >         if (!err) {
>> >                 chan->active_req = data;
>> >                 chan->msg_count--;
>> >         }
>> >         ...
>> > }
>> >
>> > static void tx_tick(struct mbox_chan *chan, int r)
>> > {
>> >         ...
>> >         spin_lock_irqsave(&chan->lock, flags);
>> >         mssg = chan->active_req;
>> >         chan->active_req = NULL;
>> >         spin_unlock_irqrestore(&chan->lock, flags);
>> >         ...
>> > }
>> >
>> > Current workable CMDQ driver uses mbox_client_txdone() to prevent
>> > this issue, and then uses self callback functions to handle done tasks.
>> >
>> > int cmdq_task_flush_async(struct cmdq_client *client, struct cmdq_task
>> > *task, cmdq_async_flush_cb cb, void *data)
>> > {
>> >         ...
>> >         mbox_send_message(client->chan, task);
>> >         /* We can send next task immediately, so just call txdone. */
>> >         mbox_client_txdone(client->chan, 0);
>> >         ...
>> > }
>> >
>> > Another solution is to use rx_callback; i.e. CMDQ mailbox controller
>> > call mbox_chan_received_data() when CMDQ task is done. But, this may
>> > violate the design of mailbox. What do you think?
>> >
>> If my point (c) above does not hold, maybe look at implementing
>> tx_done() callback and submit next task from the callback of last
>> done.
>
>
> Hi Jassi,
>
> For point (c), GCE irq means 1~n tasks done or
> 0~n tasks done + 1 task error.
> In irq, we can know which tasks are done by register and GCE pc.
>
> As I mentioned before, we cannot submit next task after previous task
> call tx_done. We need to submit multiple tasks to GCE HW immediately
> and queue them in GCE HW.

> Let me explain this requirement by mouse
> cursor example. User may move mouse quickly between two vsync, so DRM
> may update display registers frequently. For CMDQ, that means many tasks
> are flushed into CMDQ driver, and CMDQ driver needs to process all of
> them in next vblank. Therefore, we cannot block any CMDQ task in SW
> buffer.
>
We are interested only in the current position of cursor and not its
trail. Also the current position should be updated at next vsync (and
not the one after it).
Going by this example, if the GCE HW can take in 'N' tasks at a time,
then the N+1th submission should shift out (drop) the 1st task queued.
So that at any time GCE HW has only the latest N tasks. Right?

 If yes, maybe you don't need to care about tx-done and simply keep
shoving tasks as you generate them.

 If no, maybe your client driver need to emulate such a circular
buffer where oldest task is overwritten by newest submission. And you
submit the circular buffer (most relevant tasks) at one go to the GCE
HW.

^ permalink raw reply

* [PATCH net-next v3 0/3] net: ethernet: mediatek: check the hw lro capability by the chip id instead of the dtsi
From: Nelson Chang @ 2016-10-06 11:44 UTC (permalink / raw)
  To: john-Pj+rj9U5foFAfugRpC6u6w, davem-fT/PcQaiUtIeIZ0/mPfg9Q
  Cc: netdev-u79uwXL29TY76Z2rM5mHXA, nbd-p3rKhJxN3npAfugRpC6u6w,
	linux-mediatek-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	nelsonch.tw-Re5JQEeQqe8AvxtiuMwx3w, Nelson Chang

The series modify to check if hw lro is supported by the chip id.

changes since v3:
- Refine mtk_is_hwlro_supported() function

changes since v2:
- Refine mtk_get_chip_id() function

changes since v1:
- Because hw lro started to be supported from MT7623, the proper way to check if the feature is capable is to judge by the chip id instead of by the dtsi.

Nelson Chang (3):
  net: ethernet: mediatek: get the chip id by ETHDMASYS registers
  net: ethernet: mediatek: get hw lro capability by the chip id instead
    of by the dtsi
  net: ethernet: mediatek: remove hwlro property in the device tree

 .../devicetree/bindings/net/mediatek-net.txt       |  2 -
 drivers/net/ethernet/mediatek/mtk_eth_soc.c        | 43 +++++++++++++++++++++-
 drivers/net/ethernet/mediatek/mtk_eth_soc.h        |  6 +++
 3 files changed, 47 insertions(+), 4 deletions(-)

-- 
1.9.1

^ permalink raw reply

* [PATCH net-next v3 1/3] net: ethernet: mediatek: get the chip id by ETHDMASYS registers
From: Nelson Chang @ 2016-10-06 11:44 UTC (permalink / raw)
  To: john-Pj+rj9U5foFAfugRpC6u6w, davem-fT/PcQaiUtIeIZ0/mPfg9Q
  Cc: netdev-u79uwXL29TY76Z2rM5mHXA, nbd-p3rKhJxN3npAfugRpC6u6w,
	linux-mediatek-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	nelsonch.tw-Re5JQEeQqe8AvxtiuMwx3w, Nelson Chang
In-Reply-To: <1475754243-12557-1-git-send-email-nelson.chang-NuS5LvNUpcJWk0Htik3J/w@public.gmane.org>

The driver gets the chip id by ETHSYS_CHIPID0_3/ETHSYS_CHIPID4_7 registers
in mtk_probe().

Signed-off-by: Nelson Chang <nelson.chang-NuS5LvNUpcJWk0Htik3J/w@public.gmane.org>
---
 drivers/net/ethernet/mediatek/mtk_eth_soc.c | 29 +++++++++++++++++++++++++++++
 drivers/net/ethernet/mediatek/mtk_eth_soc.h |  5 +++++
 2 files changed, 34 insertions(+)

diff --git a/drivers/net/ethernet/mediatek/mtk_eth_soc.c b/drivers/net/ethernet/mediatek/mtk_eth_soc.c
index ad4ab97..0c67ab1 100644
--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c
+++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c
@@ -2323,6 +2323,31 @@ free_netdev:
 	return err;
 }
 
+static int mtk_get_chip_id(struct mtk_eth *eth, u32 *chip_id)
+{
+	u32 val[2], id[4];
+
+	regmap_read(eth->ethsys, ETHSYS_CHIPID0_3, &val[0]);
+	regmap_read(eth->ethsys, ETHSYS_CHIPID4_7, &val[1]);
+
+	id[3] = ((val[0] >> 16) & 0xff) - '0';
+	id[2] = ((val[0] >> 24) & 0xff) - '0';
+	id[1] = (val[1] & 0xff) - '0';
+	id[0] = ((val[1] >> 8) & 0xff) - '0';
+
+	*chip_id = (id[3] * 1000) + (id[2] * 100) +
+		   (id[1] * 10) + id[0];
+
+	if (!(*chip_id)) {
+		dev_err(eth->dev, "failed to get chip id\n");
+		return -ENODEV;
+	}
+
+	dev_info(eth->dev, "chip id = %d\n", *chip_id);
+
+	return 0;
+}
+
 static int mtk_probe(struct platform_device *pdev)
 {
 	struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
@@ -2388,6 +2413,10 @@ static int mtk_probe(struct platform_device *pdev)
 	if (err)
 		return err;
 
+	err = mtk_get_chip_id(eth, &eth->chip_id);
+	if (err)
+		return err;
+
 	for_each_child_of_node(pdev->dev.of_node, mac_np) {
 		if (!of_device_is_compatible(mac_np,
 					     "mediatek,eth-mac"))
diff --git a/drivers/net/ethernet/mediatek/mtk_eth_soc.h b/drivers/net/ethernet/mediatek/mtk_eth_soc.h
index 3003195..a5b422b 100644
--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.h
+++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.h
@@ -342,6 +342,10 @@
 #define GPIO_BIAS_CTRL		0xed0
 #define GPIO_DRV_SEL10		0xf00
 
+/* ethernet subsystem chip id register */
+#define ETHSYS_CHIPID0_3	0x0
+#define ETHSYS_CHIPID4_7	0x4
+
 /* ethernet subsystem config register */
 #define ETHSYS_SYSCFG0		0x14
 #define SYSCFG0_GE_MASK		0x3
@@ -534,6 +538,7 @@ struct mtk_eth {
 	unsigned long			sysclk;
 	struct regmap			*ethsys;
 	struct regmap			*pctl;
+	u32				chip_id;
 	bool				hwlro;
 	atomic_t			dma_refcnt;
 	struct mtk_tx_ring		tx_ring;
-- 
1.9.1

^ permalink raw reply related

* [PATCH net-next v3 2/3] net: ethernet: mediatek: get hw lro capability by the chip id instead of by the dtsi
From: Nelson Chang @ 2016-10-06 11:44 UTC (permalink / raw)
  To: john-Pj+rj9U5foFAfugRpC6u6w, davem-fT/PcQaiUtIeIZ0/mPfg9Q
  Cc: netdev-u79uwXL29TY76Z2rM5mHXA, nbd-p3rKhJxN3npAfugRpC6u6w,
	linux-mediatek-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	nelsonch.tw-Re5JQEeQqe8AvxtiuMwx3w, Nelson Chang
In-Reply-To: <1475754243-12557-1-git-send-email-nelson.chang-NuS5LvNUpcJWk0Htik3J/w@public.gmane.org>

Because hw lro started to be supported from MT7623, the proper way to check if
the feature is capable is to judge by the chip id instead of by the dtsi.

Signed-off-by: Nelson Chang <nelson.chang-NuS5LvNUpcJWk0Htik3J/w@public.gmane.org>
---
 drivers/net/ethernet/mediatek/mtk_eth_soc.c | 14 ++++++++++++--
 drivers/net/ethernet/mediatek/mtk_eth_soc.h |  1 +
 2 files changed, 13 insertions(+), 2 deletions(-)

diff --git a/drivers/net/ethernet/mediatek/mtk_eth_soc.c b/drivers/net/ethernet/mediatek/mtk_eth_soc.c
index 0c67ab1..4a62ffd 100644
--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c
+++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c
@@ -2348,6 +2348,16 @@ static int mtk_get_chip_id(struct mtk_eth *eth, u32 *chip_id)
 	return 0;
 }
 
+static bool mtk_is_hwlro_supported(struct mtk_eth *eth)
+{
+	switch (eth->chip_id) {
+	case MT7623_ETH:
+		return true;
+	}
+
+	return false;
+}
+
 static int mtk_probe(struct platform_device *pdev)
 {
 	struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
@@ -2387,8 +2397,6 @@ static int mtk_probe(struct platform_device *pdev)
 		return PTR_ERR(eth->pctl);
 	}
 
-	eth->hwlro = of_property_read_bool(pdev->dev.of_node, "mediatek,hwlro");
-
 	for (i = 0; i < 3; i++) {
 		eth->irq[i] = platform_get_irq(pdev, i);
 		if (eth->irq[i] < 0) {
@@ -2417,6 +2425,8 @@ static int mtk_probe(struct platform_device *pdev)
 	if (err)
 		return err;
 
+	eth->hwlro = mtk_is_hwlro_supported(eth);
+
 	for_each_child_of_node(pdev->dev.of_node, mac_np) {
 		if (!of_device_is_compatible(mac_np,
 					     "mediatek,eth-mac"))
diff --git a/drivers/net/ethernet/mediatek/mtk_eth_soc.h b/drivers/net/ethernet/mediatek/mtk_eth_soc.h
index a5b422b..99b1c8e 100644
--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.h
+++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.h
@@ -345,6 +345,7 @@
 /* ethernet subsystem chip id register */
 #define ETHSYS_CHIPID0_3	0x0
 #define ETHSYS_CHIPID4_7	0x4
+#define MT7623_ETH		7623
 
 /* ethernet subsystem config register */
 #define ETHSYS_SYSCFG0		0x14
-- 
1.9.1

^ permalink raw reply related

* [PATCH net-next v3 3/3] net: ethernet: mediatek: remove hwlro property in the device tree
From: Nelson Chang @ 2016-10-06 11:44 UTC (permalink / raw)
  To: john-Pj+rj9U5foFAfugRpC6u6w, davem-fT/PcQaiUtIeIZ0/mPfg9Q
  Cc: netdev-u79uwXL29TY76Z2rM5mHXA, nbd-p3rKhJxN3npAfugRpC6u6w,
	linux-mediatek-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	nelsonch.tw-Re5JQEeQqe8AvxtiuMwx3w, Nelson Chang
In-Reply-To: <1475754243-12557-1-git-send-email-nelson.chang-NuS5LvNUpcJWk0Htik3J/w@public.gmane.org>

Since the proper way to check the hw lro capability is by the chip id,
hwlro property in the device tree should be removed.

Signed-off-by: Nelson Chang <nelson.chang-NuS5LvNUpcJWk0Htik3J/w@public.gmane.org>
---
 Documentation/devicetree/bindings/net/mediatek-net.txt | 2 --
 1 file changed, 2 deletions(-)

diff --git a/Documentation/devicetree/bindings/net/mediatek-net.txt b/Documentation/devicetree/bindings/net/mediatek-net.txt
index f095257..c010faf 100644
--- a/Documentation/devicetree/bindings/net/mediatek-net.txt
+++ b/Documentation/devicetree/bindings/net/mediatek-net.txt
@@ -24,7 +24,6 @@ Required properties:
 Optional properties:
 - interrupt-parent: Should be the phandle for the interrupt controller
   that services interrupts for this device
-- mediatek,hwlro: the capability if the hardware supports LRO functions
 
 * Ethernet MAC node
 
@@ -54,7 +53,6 @@ eth: ethernet@1b100000 {
 	reset-names = "eth";
 	mediatek,ethsys = <&ethsys>;
 	mediatek,pctl = <&syscfg_pctl_a>;
-	mediatek,hwlro;
 	#address-cells = <1>;
 	#size-cells = <0>;
 
-- 
1.9.1

^ permalink raw reply related

* Re: [PATCH v14 2/4] CMDQ: Mediatek CMDQ driver
From: Horng-Shyang Liao @ 2016-10-06 13:01 UTC (permalink / raw)
  To: Jassi Brar
  Cc: CK Hu, Daniel Kurtz, Monica Wang, Jiaguang Zhang, Nicolas Boichat,
	Jassi Brar, cawa cheng, Bibby Hsieh, YT Shen, Damon Chu,
	Devicetree List, Sascha Hauer, Daoyuan Huang, Sascha Hauer,
	Glory Hung, Rob Herring, linux-mediatek, Matthias Brugger,
	"linux-arm-kernel@lists.infradead.org" <linux-arm->
In-Reply-To: <CAJe_ZheeEe0xEj733pxr4q=-YzxKHgRM4MMPDJNHa9uRC9Q2zA@mail.gmail.com>

On Wed, 2016-10-05 at 20:13 +0530, Jassi Brar wrote:
> On 5 October 2016 at 18:01, Horng-Shyang Liao <hs.liao@mediatek.com> wrote:
> > On Wed, 2016-10-05 at 09:07 +0530, Jassi Brar wrote:
> >> On 5 October 2016 at 08:24, Horng-Shyang Liao <hs.liao@mediatek.com> wrote:
> >> > On Fri, 2016-09-30 at 17:47 +0800, Horng-Shyang Liao wrote:
> >> >> On Fri, 2016-09-30 at 17:11 +0800, CK Hu wrote:
> >>
> >> >
> >> > After I trace mailbox driver, I realize that CMDQ driver cannot use
> >> > tx_done.
> >> >
> >> > CMDQ clients will flush many tasks into CMDQ driver, and then CMDQ
> >> > driver will apply these tasks into GCE HW "immediately". These tasks,
> >> > which are queued in GCE HW, may not execute immediately since they
> >> > may need to wait event(s), e.g. vsync.
> >> >
> >> > However, in mailbox driver, mailbox uses a software buffer to queue
> >> > sent messages. It only sends next message until previous message is
> >> > done. This cannot fulfill CMDQ's requirement.
> >> >
> >> I understand
> >>  a) GCE HW can internally queue many tasks in some 'FIFO'
> >>  b) Execution of some task may have to wait until some external event
> >> occurs (like vsync)
> >>  c) GCE does not generate irq/flag for each task executed (?)
> >>
> >> If so, may be your tx_done should return 'true' so long as the GCE HW
> >> can accept tasks in its 'FIFO'. For mailbox api, any task that is
> >> queued on GCE, is assumed to be transmitted.
> >>
> >> > Quote some code from mailbox driver. Please notice "active_req" part.
> >> >
> >> > static void msg_submit(struct mbox_chan *chan)
> >> > {
> >> >         ...
> >> >         if (!chan->msg_count || chan->active_req)
> >> >                 goto exit;
> >> >         ...
> >> >         err = chan->mbox->ops->send_data(chan, data);
> >> >         if (!err) {
> >> >                 chan->active_req = data;
> >> >                 chan->msg_count--;
> >> >         }
> >> >         ...
> >> > }
> >> >
> >> > static void tx_tick(struct mbox_chan *chan, int r)
> >> > {
> >> >         ...
> >> >         spin_lock_irqsave(&chan->lock, flags);
> >> >         mssg = chan->active_req;
> >> >         chan->active_req = NULL;
> >> >         spin_unlock_irqrestore(&chan->lock, flags);
> >> >         ...
> >> > }
> >> >
> >> > Current workable CMDQ driver uses mbox_client_txdone() to prevent
> >> > this issue, and then uses self callback functions to handle done tasks.
> >> >
> >> > int cmdq_task_flush_async(struct cmdq_client *client, struct cmdq_task
> >> > *task, cmdq_async_flush_cb cb, void *data)
> >> > {
> >> >         ...
> >> >         mbox_send_message(client->chan, task);
> >> >         /* We can send next task immediately, so just call txdone. */
> >> >         mbox_client_txdone(client->chan, 0);
> >> >         ...
> >> > }
> >> >
> >> > Another solution is to use rx_callback; i.e. CMDQ mailbox controller
> >> > call mbox_chan_received_data() when CMDQ task is done. But, this may
> >> > violate the design of mailbox. What do you think?
> >> >
> >> If my point (c) above does not hold, maybe look at implementing
> >> tx_done() callback and submit next task from the callback of last
> >> done.
> >
> >
> > Hi Jassi,
> >
> > For point (c), GCE irq means 1~n tasks done or
> > 0~n tasks done + 1 task error.
> > In irq, we can know which tasks are done by register and GCE pc.
> >
> > As I mentioned before, we cannot submit next task after previous task
> > call tx_done. We need to submit multiple tasks to GCE HW immediately
> > and queue them in GCE HW.
> 
> > Let me explain this requirement by mouse
> > cursor example. User may move mouse quickly between two vsync, so DRM
> > may update display registers frequently. For CMDQ, that means many tasks
> > are flushed into CMDQ driver, and CMDQ driver needs to process all of
> > them in next vblank. Therefore, we cannot block any CMDQ task in SW
> > buffer.
> >
> We are interested only in the current position of cursor and not its
> trail. Also the current position should be updated at next vsync (and
> not the one after it).
> Going by this example, if the GCE HW can take in 'N' tasks at a time,
> then the N+1th submission should shift out (drop) the 1st task queued.
> So that at any time GCE HW has only the latest N tasks. Right?
> 
>  If yes, maybe you don't need to care about tx-done and simply keep
> shoving tasks as you generate them.
> 
>  If no, maybe your client driver need to emulate such a circular
> buffer where oldest task is overwritten by newest submission. And you
> submit the circular buffer (most relevant tasks) at one go to the GCE
> HW.


Hi Jassi,

CMDQ driver doesn't know the task type, so CMDQ cannot decide which
tasks can be dropped. So, I think the answer is "no".

Client driver is also hard to emulate a circular buffer where oldest
task is overwritten by newest submission. Let me illustrate with DRM
client (display driver). For DRM, the only way to know the latest
mouse cursor task before vsync is vsync interrupt. However, if we
keep latest mouse cursor task and start to send it to CMDQ after
vsync, the task may be executed outside the vblank and generate garbage
on screen in this case. (since we allow nested interrupt) We can wait
one more vsync to prevent garbage, but we will lose mouse cursor
performance. Therefore, our current solution is to flush all DRM tasks
into CMDQ, and queue all tasks in GCE HW. When GCE get vsync event, it
can finish all tasks within vblank to fulfill DRM's requirement.

Back to our original statement, we need to flush all tasks to queue
in GCE HW; i.e. we need to use mbox_client_txdone after
mbox_send_message, or send tx_done once mailbox controller receive
message (task). However, we still need a way to notice done tasks to
clients. Currently, we don't have a good way to call callback in mailbox
framework. Therefore, CMDQ driver has its owner callback functions.

Thanks,
HS

^ permalink raw reply

* Re: [PATCH v14 2/4] CMDQ: Mediatek CMDQ driver
From: Jassi Brar @ 2016-10-06 13:10 UTC (permalink / raw)
  To: Horng-Shyang Liao
  Cc: CK Hu, Daniel Kurtz, Monica Wang, Jiaguang Zhang, Nicolas Boichat,
	Jassi Brar, cawa cheng, Bibby Hsieh, YT Shen, Damon Chu,
	Devicetree List, Sascha Hauer, Daoyuan Huang, Sascha Hauer,
	Glory Hung, Rob Herring,
	linux-mediatek-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r, Matthias Brugger,
	"linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r@public.gmane.org" <linux-arm-kernel>
In-Reply-To: <1475758897.4102.31.camel@mtksdaap41>

On 6 October 2016 at 18:31, Horng-Shyang Liao <hs.liao-NuS5LvNUpcJWk0Htik3J/w@public.gmane.org> wrote:

> Back to our original statement, we need to flush all tasks to queue
> in GCE HW; i.e. we need to use mbox_client_txdone after
> mbox_send_message, or send tx_done once mailbox controller receive
> message (task). However, we still need a way to notice done tasks to
> clients. Currently, we don't have a good way to call callback in mailbox
> framework. Therefore, CMDQ driver has its owner callback functions.
>
mbox_client_txdone() is called by the client driver when only it knows
the messages has been transmitted (i.e your submitted tasks are done).
Obviously the client driver should do any callbacks to its users
upstream.
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply

* Re: [PATCH net-next v3 0/3] net: ethernet: mediatek: check the hw lro capability by the chip id instead of the dtsi
From: David Miller @ 2016-10-07  1:08 UTC (permalink / raw)
  To: nelson.chang; +Cc: john, nbd, netdev, linux-mediatek, nelsonch.tw
In-Reply-To: <1475754243-12557-1-git-send-email-nelson.chang@mediatek.com>

From: Nelson Chang <nelson.chang@mediatek.com>
Date: Thu, 6 Oct 2016 19:44:00 +0800

> The series modify to check if hw lro is supported by the chip id.
> 
> changes since v3:
> - Refine mtk_is_hwlro_supported() function
> 
> changes since v2:
> - Refine mtk_get_chip_id() function
> 
> changes since v1:
> - Because hw lro started to be supported from MT7623, the proper way to check if the feature is capable is to judge by the chip id instead of by the dtsi.

Series applied, thanks.

^ permalink raw reply

* Re: [PATCH 0/2] fix issue: vblank interrupts are never disabled
From: Daniel Kurtz @ 2016-10-07 10:37 UTC (permalink / raw)
  To: Bibby Hsieh
  Cc: David Airlie, Matthias Brugger, Daniel Vetter, dri-devel,
	moderated list:ARM/Mediatek SoC support, Yingjoe Chen, Cawa Cheng,
	Philipp Zabel, YT Shen, Thierry Reding, CK Hu, Mao Huang,
	linux-arm-kernel@lists.infradead.org,
	linux-kernel@vger.kernel.org, Sascha Hauer
In-Reply-To: <1475119789-64619-1-git-send-email-bibby.hsieh@mediatek.com>

On Thu, Sep 29, 2016 at 11:29 AM, Bibby Hsieh <bibby.hsieh@mediatek.com> wrote:
>
> Clean the interrupt status before enable interrupt
> and set the vblank_disable_allowed to fix the issue.

For the series:

Reviewed-by: Daniel Kurtz <djkurtz@chromium.org>

>
> Bibby Hsieh (2):
>   drm/mediatek: set vblank_disable_allowed to true
>   drm/mediatek: clear IRQ status before enable OVL interrupt
>
>  drivers/gpu/drm/mediatek/mtk_disp_ovl.c |    1 +
>  drivers/gpu/drm/mediatek/mtk_drm_drv.c  |    1 +
>  2 files changed, 2 insertions(+)
>
> --
> 1.7.9.5
>

^ permalink raw reply

* Shipment delivery problem #00000713688
From: FedEx International Ground @ 2016-10-08 21:39 UTC (permalink / raw)
  To: linux-mediatek-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r

[-- Attachment #1: Type: text/plain, Size: 140 bytes --]

Dear Customer,

We could not deliver your item.
Shipment Label is attached to this email.

Yours trully,
Don Fields,
FedEx Delivery Agent.


[-- Attachment #2: FedEx_00000713688.zip --]
[-- Type: application/zip, Size: 11890 bytes --]

[-- Attachment #3: Type: text/plain, Size: 200 bytes --]

_______________________________________________
Linux-mediatek mailing list
Linux-mediatek-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r@public.gmane.org
http://lists.infradead.org/mailman/listinfo/linux-mediatek

^ permalink raw reply

* Problems with item delivery, n.000927677
From: FedEx International MailService @ 2016-10-09 19:28 UTC (permalink / raw)
  To: linux-mediatek-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r

[-- Attachment #1: Type: text/plain, Size: 165 bytes --]

Dear Customer,

We could not deliver your parcel.
Please, open email attachment to print shipment label.

Yours trully,
Matthew Rutherford,
FedEx Delivery Manager.


[-- Attachment #2: 000927677.zip --]
[-- Type: application/zip, Size: 6717 bytes --]

[-- Attachment #3: Type: text/plain, Size: 200 bytes --]

_______________________________________________
Linux-mediatek mailing list
Linux-mediatek-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r@public.gmane.org
http://lists.infradead.org/mailman/listinfo/linux-mediatek

^ permalink raw reply

* Re: [RESEND PATCH v6, 3/5] usb: xhci-mtk: make IPPC register optional
From: Matthias Brugger @ 2016-10-10 10:55 UTC (permalink / raw)
  To: Chunfeng Yun, Greg Kroah-Hartman, Felipe Balbi, Mathias Nyman
  Cc: Oliver Neukum, Alan Stern, Rob Herring, Mark Rutland,
	Ian Campbell, Sergei Shtylyov, Pawel Moll, Kumar Gala,
	Sascha Hauer, Alan Cooper, linux-usb, devicetree, linux-kernel,
	linux-arm-kernel, linux-mediatek
In-Reply-To: <1474437277-27201-4-git-send-email-chunfeng.yun@mediatek.com>



On 09/21/2016 07:54 AM, Chunfeng Yun wrote:
> Make IPPC register optional to support host side of dual-role mode,
> due to it is moved into common glue layer for simplification.
>
> Signed-off-by: Chunfeng Yun <chunfeng.yun@mediatek.com>
> ---
>  drivers/usb/host/xhci-mtk.c |   36 +++++++++++++++++++++++++++++-------
>  1 file changed, 29 insertions(+), 7 deletions(-)
>
> diff --git a/drivers/usb/host/xhci-mtk.c b/drivers/usb/host/xhci-mtk.c
> index 79959f1..4bf99b9 100644
> --- a/drivers/usb/host/xhci-mtk.c
> +++ b/drivers/usb/host/xhci-mtk.c
> @@ -94,6 +94,9 @@ static int xhci_mtk_host_enable(struct xhci_hcd_mtk *mtk)
>  	int ret;
>  	int i;
>
> +	if (ippc == NULL)
> +		return 0;
> +
>  	/* power on host ip */
>  	value = readl(&ippc->ip_pw_ctr1);
>  	value &= ~CTRL1_IP_HOST_PDN;
> @@ -139,6 +142,9 @@ static int xhci_mtk_host_disable(struct xhci_hcd_mtk *mtk)
>  	int ret;
>  	int i;
>
> +	if (ippc == NULL)
> +		return 0;
> +
>  	/* power down all u3 ports */
>  	for (i = 0; i < mtk->num_u3_ports; i++) {
>  		value = readl(&ippc->u3_ctrl_p[i]);
> @@ -173,6 +179,9 @@ static int xhci_mtk_ssusb_config(struct xhci_hcd_mtk *mtk)
>  	struct mu3c_ippc_regs __iomem *ippc = mtk->ippc_regs;
>  	u32 value;
>
> +	if (ippc == NULL)
> +		return 0;
> +

I would prefer to add a flag/bool in xhci_hcd_mtk to signal the absence 
of the ippc. Or at least use a macro which checks the presence before 
calling any of this three functions.

Regards,
Matthias
>  	/* reset whole ip */
>  	value = readl(&ippc->ip_pw_ctr0);
>  	value |= CTRL0_IP_SW_RST;
> @@ -475,6 +484,7 @@ static void xhci_mtk_quirks(struct device *dev, struct xhci_hcd *xhci)
>  /* called during probe() after chip reset completes */
>  static int xhci_mtk_setup(struct usb_hcd *hcd)
>  {
> +	struct xhci_hcd *xhci = hcd_to_xhci(hcd);
>  	struct xhci_hcd_mtk *mtk = hcd_to_mtk(hcd);
>  	int ret;
>
> @@ -482,12 +492,21 @@ static int xhci_mtk_setup(struct usb_hcd *hcd)
>  		ret = xhci_mtk_ssusb_config(mtk);
>  		if (ret)
>  			return ret;
> +	}
> +
> +	ret = xhci_gen_setup(hcd, xhci_mtk_quirks);
> +	if (ret)
> +		return ret;
> +
> +	if (usb_hcd_is_primary_hcd(hcd)) {
> +		mtk->num_u3_ports = xhci->num_usb3_ports;
> +		mtk->num_u2_ports = xhci->num_usb2_ports;
>  		ret = xhci_mtk_sch_init(mtk);
>  		if (ret)
>  			return ret;
>  	}
>
> -	return xhci_gen_setup(hcd, xhci_mtk_quirks);
> +	return ret;
>  }
>
>  static int xhci_mtk_probe(struct platform_device *pdev)
> @@ -586,7 +605,7 @@ static int xhci_mtk_probe(struct platform_device *pdev)
>  	mtk->hcd = platform_get_drvdata(pdev);
>  	platform_set_drvdata(pdev, mtk);
>
> -	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> +	res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "mac");
>  	hcd->regs = devm_ioremap_resource(dev, res);
>  	if (IS_ERR(hcd->regs)) {
>  		ret = PTR_ERR(hcd->regs);
> @@ -595,11 +614,14 @@ static int xhci_mtk_probe(struct platform_device *pdev)
>  	hcd->rsrc_start = res->start;
>  	hcd->rsrc_len = resource_size(res);
>
> -	res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
> -	mtk->ippc_regs = devm_ioremap_resource(dev, res);
> -	if (IS_ERR(mtk->ippc_regs)) {
> -		ret = PTR_ERR(mtk->ippc_regs);
> -		goto put_usb2_hcd;
> +	mtk->ippc_regs = NULL;
> +	res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "ippc");
> +	if (res) {	/* ippc register is optional */
> +		mtk->ippc_regs = devm_ioremap_resource(dev, res);
> +		if (IS_ERR(mtk->ippc_regs)) {
> +			ret = PTR_ERR(mtk->ippc_regs);
> +			goto put_usb2_hcd;
> +		}
>  	}
>
>  	for (phy_num = 0; phy_num < mtk->num_phys; phy_num++) {
>

^ permalink raw reply

* Re: [RESEND PATCH v6, 4/5] usb: Add MediaTek USB3 DRD Driver
From: Matthias Brugger @ 2016-10-10 11:00 UTC (permalink / raw)
  To: Chunfeng Yun, Greg Kroah-Hartman, Felipe Balbi, Mathias Nyman
  Cc: Oliver Neukum, Alan Stern, Rob Herring, Mark Rutland,
	Ian Campbell, Sergei Shtylyov, Pawel Moll, Kumar Gala,
	Sascha Hauer, Alan Cooper, linux-usb, devicetree, linux-kernel,
	linux-arm-kernel, linux-mediatek
In-Reply-To: <1474437277-27201-5-git-send-email-chunfeng.yun@mediatek.com>



On 09/21/2016 07:54 AM, Chunfeng Yun wrote:
> This patch adds support for the MediaTek USB3 controller
> integrated into MT8173. It can be configured as Dual-Role
> Device (DRD), Peripheral Only and Host Only (xHCI) modes.
>
> Signed-off-by: Chunfeng Yun <chunfeng.yun@mediatek.com>
> ---
>  drivers/usb/Kconfig                |    2 +
>  drivers/usb/Makefile               |    1 +
>  drivers/usb/mtu3/Kconfig           |   54 +++
>  drivers/usb/mtu3/Makefile          |   19 +
>  drivers/usb/mtu3/mtu3.h            |  422 +++++++++++++++++
>  drivers/usb/mtu3/mtu3_core.c       |  871 +++++++++++++++++++++++++++++++++++
>  drivers/usb/mtu3/mtu3_dr.c         |  379 ++++++++++++++++
>  drivers/usb/mtu3/mtu3_dr.h         |  108 +++++
>  drivers/usb/mtu3/mtu3_gadget.c     |  731 +++++++++++++++++++++++++++++
>  drivers/usb/mtu3/mtu3_gadget_ep0.c |  883 ++++++++++++++++++++++++++++++++++++
>  drivers/usb/mtu3/mtu3_host.c       |  294 ++++++++++++
>  drivers/usb/mtu3/mtu3_hw_regs.h    |  473 +++++++++++++++++++
>  drivers/usb/mtu3/mtu3_plat.c       |  490 ++++++++++++++++++++
>  drivers/usb/mtu3/mtu3_qmu.c        |  599 ++++++++++++++++++++++++
>  drivers/usb/mtu3/mtu3_qmu.h        |   43 ++
>  15 files changed, 5369 insertions(+)
>  create mode 100644 drivers/usb/mtu3/Kconfig
>  create mode 100644 drivers/usb/mtu3/Makefile
>  create mode 100644 drivers/usb/mtu3/mtu3.h
>  create mode 100644 drivers/usb/mtu3/mtu3_core.c
>  create mode 100644 drivers/usb/mtu3/mtu3_dr.c
>  create mode 100644 drivers/usb/mtu3/mtu3_dr.h
>  create mode 100644 drivers/usb/mtu3/mtu3_gadget.c
>  create mode 100644 drivers/usb/mtu3/mtu3_gadget_ep0.c
>  create mode 100644 drivers/usb/mtu3/mtu3_host.c
>  create mode 100644 drivers/usb/mtu3/mtu3_hw_regs.h
>  create mode 100644 drivers/usb/mtu3/mtu3_plat.c
>  create mode 100644 drivers/usb/mtu3/mtu3_qmu.c
>  create mode 100644 drivers/usb/mtu3/mtu3_qmu.h
>

As Oliver already said, this patch is quiet big which makes it difficult 
to review.
I propose to provide a first implementation with minimal functionality 
and incremental patches on top of this when the first got merged.

You could split the patch in three series/parts:
1. Host only
2. Peripheral only
3. Dual mode

What do you think?

Regards,
Matthias

> diff --git a/drivers/usb/Kconfig b/drivers/usb/Kconfig
> index 8689dcb..9ca0bf0 100644
> --- a/drivers/usb/Kconfig
> +++ b/drivers/usb/Kconfig
> @@ -95,6 +95,8 @@ source "drivers/usb/usbip/Kconfig"
>
>  endif
>
> +source "drivers/usb/mtu3/Kconfig"
> +
>  source "drivers/usb/musb/Kconfig"
>
>  source "drivers/usb/dwc3/Kconfig"
> diff --git a/drivers/usb/Makefile b/drivers/usb/Makefile
> index dca7856..7791af6 100644
> --- a/drivers/usb/Makefile
> +++ b/drivers/usb/Makefile
> @@ -12,6 +12,7 @@ obj-$(CONFIG_USB_DWC2)		+= dwc2/
>  obj-$(CONFIG_USB_ISP1760)	+= isp1760/
>
>  obj-$(CONFIG_USB_MON)		+= mon/
> +obj-$(CONFIG_USB_MTU3)		+= mtu3/
>
>  obj-$(CONFIG_PCI)		+= host/
>  obj-$(CONFIG_USB_EHCI_HCD)	+= host/
> diff --git a/drivers/usb/mtu3/Kconfig b/drivers/usb/mtu3/Kconfig
> new file mode 100644
> index 0000000..25cd619
> --- /dev/null
> +++ b/drivers/usb/mtu3/Kconfig
> @@ -0,0 +1,54 @@
> +# For MTK USB3.0 IP
> +
> +config USB_MTU3
> +	tristate "MediaTek USB3 Dual Role controller"
> +	depends on EXTCON && (USB || USB_GADGET) && HAS_DMA
> +	depends on ARCH_MEDIATEK || COMPILE_TEST
> +	select USB_XHCI_MTK if USB_SUPPORT && USB_XHCI_HCD
> +	help
> +	  Say Y or M here if your system runs on MediaTek SoCs with
> +	  Dual Role SuperSpeed USB controller. You can select usb
> +	  mode as peripheral role or host role, or both.
> +
> +	  If you don't know what this is, please say N.
> +
> +	  Choose M here to compile this driver as a module, and it
> +	  will be called mtu3.ko.
> +
> +
> +if USB_MTU3
> +choice
> +	bool "MTU3 Mode Selection"
> +	default USB_MTU3_DUAL_ROLE if (USB && USB_GADGET)
> +	default USB_MTU3_HOST if (USB && !USB_GADGET)
> +	default USB_MTU3_GADGET if (!USB && USB_GADGET)
> +
> +config USB_MTU3_HOST
> +	bool "Host only mode"
> +	depends on USB=y || USB=USB_MTU3
> +	help
> +	  Select this when you want to use MTU3 in host mode only,
> +	  thereby the gadget feature will be regressed.
> +
> +config USB_MTU3_GADGET
> +	bool "Gadget only mode"
> +	depends on USB_GADGET=y || USB_GADGET=USB_MTU3
> +	help
> +	  Select this when you want to use MTU3 in gadget mode only,
> +	  thereby the host feature will be regressed.
> +
> +config USB_MTU3_DUAL_ROLE
> +	bool "Dual Role mode"
> +	depends on ((USB=y || USB=USB_MTU3) && (USB_GADGET=y || USB_GADGET=USB_MTU3))
> +	help
> +	  This is the default mode of working of MTU3 controller where
> +	  both host and gadget features are enabled.
> +
> +endchoice
> +
> +config USB_MTU3_DEBUG
> +	bool "Enable Debugging Messages"
> +	help
> +	  Say Y here to enable debugging messages in the MTU3 Driver.
> +
> +endif
> diff --git a/drivers/usb/mtu3/Makefile b/drivers/usb/mtu3/Makefile
> new file mode 100644
> index 0000000..3e17ff7
> --- /dev/null
> +++ b/drivers/usb/mtu3/Makefile
> @@ -0,0 +1,19 @@
> +
> +ccflags-$(CONFIG_USB_MTU3_DEBUG)	+= -DDEBUG
> +
> +obj-$(CONFIG_USB_MTU3)	+= mtu3.o
> +
> +mtu3-y	:= mtu3_plat.o
> +
> +ifneq ($(filter y,$(CONFIG_USB_MTU3_HOST) $(CONFIG_USB_MTU3_DUAL_ROLE)),)
> +	mtu3-y	+= mtu3_host.o
> +endif
> +
> +ifneq ($(filter y,$(CONFIG_USB_MTU3_GADGET) $(CONFIG_USB_MTU3_DUAL_ROLE)),)
> +	mtu3-y	+= mtu3_core.o mtu3_gadget_ep0.o mtu3_gadget.o mtu3_qmu.o
> +endif
> +
> +ifneq ($(CONFIG_USB_MTU3_DUAL_ROLE),)
> +	mtu3-y	+= mtu3_dr.o
> +endif
> +
> diff --git a/drivers/usb/mtu3/mtu3.h b/drivers/usb/mtu3/mtu3.h
> new file mode 100644
> index 0000000..6d5ed26
> --- /dev/null
> +++ b/drivers/usb/mtu3/mtu3.h
> @@ -0,0 +1,422 @@
> +/*
> + * mtu3.h - MediaTek USB3 DRD header
> + *
> + * Copyright (C) 2016 MediaTek Inc.
> + *
> + * Author: Chunfeng Yun <chunfeng.yun@mediatek.com>
> + *
> + * This software is licensed under the terms of the GNU General Public
> + * License version 2, as published by the Free Software Foundation, and
> + * may be copied, distributed, and modified under those terms.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + * GNU General Public License for more details.
> + *
> + */
> +
> +#ifndef __MTU3_H__
> +#define __MTU3_H__
> +
> +#include <linux/device.h>
> +#include <linux/dmapool.h>
> +#include <linux/extcon.h>
> +#include <linux/interrupt.h>
> +#include <linux/list.h>
> +#include <linux/phy/phy.h>
> +#include <linux/regulator/consumer.h>
> +#include <linux/usb.h>
> +#include <linux/usb/ch9.h>
> +#include <linux/usb/gadget.h>
> +#include <linux/usb/otg.h>
> +#include <linux/workqueue.h>
> +
> +struct mtu3;
> +struct mtu3_ep;
> +struct mtu3_request;
> +
> +#include "mtu3_hw_regs.h"
> +#include "mtu3_qmu.h"
> +
> +#define	MU3D_EP_TXCR0(epnum)	(U3D_TX1CSR0 + (((epnum) - 1) * 0x10))
> +#define	MU3D_EP_TXCR1(epnum)	(U3D_TX1CSR1 + (((epnum) - 1) * 0x10))
> +#define	MU3D_EP_TXCR2(epnum)	(U3D_TX1CSR2 + (((epnum) - 1) * 0x10))
> +
> +#define	MU3D_EP_RXCR0(epnum)	(U3D_RX1CSR0 + (((epnum) - 1) * 0x10))
> +#define	MU3D_EP_RXCR1(epnum)	(U3D_RX1CSR1 + (((epnum) - 1) * 0x10))
> +#define	MU3D_EP_RXCR2(epnum)	(U3D_RX1CSR2 + (((epnum) - 1) * 0x10))
> +
> +#define USB_QMU_RQCSR(epnum)	(U3D_RXQCSR1 + (((epnum) - 1) * 0x10))
> +#define USB_QMU_RQSAR(epnum)	(U3D_RXQSAR1 + (((epnum) - 1) * 0x10))
> +#define USB_QMU_RQCPR(epnum)	(U3D_RXQCPR1 + (((epnum) - 1) * 0x10))
> +#define USB_QMU_RQLDPR(epnum)	(U3D_RXQLDPR1 + (((epnum) - 1) * 0x10))
> +
> +#define USB_QMU_TQCSR(epnum)	(U3D_TXQCSR1 + (((epnum) - 1) * 0x10))
> +#define USB_QMU_TQSAR(epnum)	(U3D_TXQSAR1 + (((epnum) - 1) * 0x10))
> +#define USB_QMU_TQCPR(epnum)	(U3D_TXQCPR1 + (((epnum) - 1) * 0x10))
> +
> +#define SSUSB_U3_CTRL(p)	(U3D_SSUSB_U3_CTRL_0P + ((p) * 0x08))
> +#define SSUSB_U2_CTRL(p)	(U3D_SSUSB_U2_CTRL_0P + ((p) * 0x08))
> +
> +#define MTU3_DRIVER_NAME	"mtu3"
> +#define	DMA_ADDR_INVALID	(~(dma_addr_t)0)
> +
> +#define MTU3_EP_ENABLED		BIT(0)
> +#define MTU3_EP_STALL		BIT(1)
> +#define MTU3_EP_WEDGE		BIT(2)
> +#define MTU3_EP_BUSY		BIT(3)
> +
> +#define MTU3_U3_IP_SLOT_DEFAULT 2
> +#define MTU3_U2_IP_SLOT_DEFAULT 1
> +
> +/**
> + * Normally the device works on HS or SS, to simplify fifo management,
> + * devide fifo into some 512B parts, use bitmap to manage it; And
> + * 128 bits size of bitmap is large enough, that means it can manage
> + * up to 64KB fifo size.
> + * NOTE: MTU3_EP_FIFO_UNIT should be power of two
> + */
> +#define MTU3_EP_FIFO_UNIT		(1 << 9)
> +#define MTU3_FIFO_BIT_SIZE		128
> +#define MTU3_U2_IP_EP0_FIFO_SIZE	64
> +
> +/**
> + * Maximum size of ep0 response buffer for ch9 requests,
> + * the set_sel request uses 6 so far, get_status is 2
> + */
> +#define EP0_RESPONSE_BUF  6
> +
> +/* device operated link and speed got from DEVICE_CONF register */
> +enum mtu3_speed {
> +	MTU3_SPEED_INACTIVE = 0,
> +	MTU3_SPEED_FULL = 1,
> +	MTU3_SPEED_HIGH = 3,
> +	MTU3_SPEED_SUPER = 4,
> +};
> +
> +/**
> + * @MU3D_EP0_STATE_SETUP: waits for SETUP or received a SETUP
> + *		without data stage.
> + * @MU3D_EP0_STATE_TX: IN data stage
> + * @MU3D_EP0_STATE_RX: OUT data stage
> + * @MU3D_EP0_STATE_TX_END: the last IN data is transferred, and
> + *		waits for its completion interrupt
> + * @MU3D_EP0_STATE_STALL: ep0 is in stall status, will be auto-cleared
> + *		after receives a SETUP.
> + */
> +enum mtu3_g_ep0_state {
> +	MU3D_EP0_STATE_SETUP = 1,
> +	MU3D_EP0_STATE_TX,
> +	MU3D_EP0_STATE_RX,
> +	MU3D_EP0_STATE_TX_END,
> +	MU3D_EP0_STATE_STALL,
> +};
> +
> +/**
> + * @base: the base address of fifo
> + * @limit: the bitmap size in bits
> + * @bitmap: fifo bitmap in unit of @MTU3_EP_FIFO_UNIT
> + */
> +struct mtu3_fifo_info {
> +	u32 base;
> +	u32 limit;
> +	DECLARE_BITMAP(bitmap, MTU3_FIFO_BIT_SIZE);
> +};
> +
> +/**
> + * General Purpose Descriptor (GPD):
> + *	The format of TX GPD is a little different from RX one.
> + *	And the size of GPD is 16 bytes.
> + *
> + * @flag:
> + *	bit0: Hardware Own (HWO)
> + *	bit1: Buffer Descriptor Present (BDP), always 0, BD is not supported
> + *	bit2: Bypass (BPS), 1: HW skips this GPD if HWO = 1
> + *	bit7: Interrupt On Completion (IOC)
> + * @chksum: This is used to validate the contents of this GPD;
> + *	If TXQ_CS_EN / RXQ_CS_EN bit is set, an interrupt is issued
> + *	when checksum validation fails;
> + *	Checksum value is calculated over the 16 bytes of the GPD by default;
> + * @data_buf_len (RX ONLY): This value indicates the length of
> + *	the assigned data buffer
> + * @next_gpd: Physical address of the next GPD
> + * @buffer: Physical address of the data buffer
> + * @buf_len:
> + *	(TX): This value indicates the length of the assigned data buffer
> + *	(RX): The total length of data received
> + * @ext_len: reserved
> + * @ext_flag:
> + *	bit5 (TX ONLY): Zero Length Packet (ZLP),
> + */
> +struct qmu_gpd {
> +	__u8 flag;
> +	__u8 chksum;
> +	__le16 data_buf_len;
> +	__le32 next_gpd;
> +	__le32 buffer;
> +	__le16 buf_len;
> +	__u8 ext_len;
> +	__u8 ext_flag;
> +} __packed;
> +
> +/**
> +* dma: physical base address of GPD segment
> +* start: virtual base address of GPD segment
> +* end: the last GPD element
> +* enqueue: the first empty GPD to use
> +* dequeue: the first completed GPD serviced by ISR
> +* NOTE: the size of GPD ring should be >= 2
> +*/
> +struct mtu3_gpd_ring {
> +	dma_addr_t dma;
> +	struct qmu_gpd *start;
> +	struct qmu_gpd *end;
> +	struct qmu_gpd *enqueue;
> +	struct qmu_gpd *dequeue;
> +};
> +
> +/**
> +* @vbus: vbus 5V used by host mode
> +* @edev: external connector used to detect vbus and iddig changes
> +* @vbus_nb: notifier for vbus detection
> +* @vbus_nb: notifier for iddig(idpin) detection
> +* @extcon_reg_dwork: delay work for extcon notifier register, waiting for
> +*		xHCI driver initialization, it's necessary for system bootup
> +*		as device.
> +* @is_u3_drd: whether port0 supports usb3.0 dual-role device or not
> +* @id_*: used to maually switch between host and device modes by idpin
> +* @manual_drd_enabled: it's true when supports dual-role device by debugfs
> +*		to switch host/device modes depending on user input.
> +*/
> +struct otg_switch_mtk {
> +	struct regulator *vbus;
> +	struct extcon_dev *edev;
> +	struct notifier_block vbus_nb;
> +	struct notifier_block id_nb;
> +	struct delayed_work extcon_reg_dwork;
> +	bool is_u3_drd;
> +	/* dual-role switch by debugfs */
> +	struct pinctrl *id_pinctrl;
> +	struct pinctrl_state *id_float;
> +	struct pinctrl_state *id_ground;
> +	bool manual_drd_enabled;
> +};
> +
> +/**
> + * @mac_base: register base address of device MAC, exclude xHCI's
> + * @ippc_base: register base address of ip port controller interface (IPPC)
> + * @vusb33: usb3.3V shared by device/host IP
> + * @sys_clk: system clock of mtu3, shared by device/host IP
> + * @dr_mode: works in which mode:
> + *		host only, device only or dual-role mode
> + * @u2_ports: number of usb2.0 host ports
> + * @u3_ports: number of usb3.0 host ports
> + * @dbgfs_root: only used when supports manual dual-role switch via debugfs
> + * @wakeup_en: it's true when supports remote wakeup in host mode
> + * @wk_deb_p0: port0's wakeup debounce clock
> + * @wk_deb_p1: it's optional, and depends on port1 is supported or not
> + */
> +struct ssusb_mtk {
> +	struct device *dev;
> +	struct mtu3 *u3d;
> +	void __iomem *mac_base;
> +	void __iomem *ippc_base;
> +	struct phy **phys;
> +	int num_phys;
> +	/* common power & clock */
> +	struct regulator *vusb33;
> +	struct clk *sys_clk;
> +	/* otg */
> +	struct otg_switch_mtk otg_switch;
> +	enum usb_dr_mode dr_mode;
> +	bool is_host;
> +	int u2_ports;
> +	int u3_ports;
> +	struct dentry *dbgfs_root;
> +	/* usb wakeup for host mode */
> +	bool wakeup_en;
> +	struct clk *wk_deb_p0;
> +	struct clk *wk_deb_p1;
> +	struct regmap *pericfg;
> +};
> +
> +/**
> + * @fifo_size: it is (@slot + 1) * @fifo_seg_size
> + * @fifo_seg_size: it is roundup_pow_of_two(@maxp)
> + */
> +struct mtu3_ep {
> +	struct usb_ep ep;
> +	char name[12];
> +	struct mtu3 *mtu;
> +	u8 epnum;
> +	u8 type;
> +	u8 is_in;
> +	u16 maxp;
> +	int slot;
> +	u32 fifo_size;
> +	u32 fifo_addr;
> +	u32 fifo_seg_size;
> +	struct mtu3_fifo_info *fifo;
> +
> +	struct list_head req_list;
> +	struct mtu3_gpd_ring gpd_ring;
> +	const struct usb_ss_ep_comp_descriptor *comp_desc;
> +	const struct usb_endpoint_descriptor *desc;
> +
> +	int flags;
> +	u8 wedged;
> +	u8 busy;
> +};
> +
> +struct mtu3_request {
> +	struct usb_request request;
> +	struct list_head list;
> +	struct mtu3_ep *mep;
> +	struct mtu3 *mtu;
> +	struct qmu_gpd *gpd;
> +	int epnum;
> +};
> +
> +static inline struct ssusb_mtk *dev_to_ssusb(struct device *dev)
> +{
> +	return dev_get_drvdata(dev);
> +}
> +
> +/**
> + * struct mtu3 - device driver instance data.
> + * @slot: MTU3_U2_IP_SLOT_DEFAULT for U2 IP only,
> + *		MTU3_U3_IP_SLOT_DEFAULT for U3 IP
> + * @may_wakeup: means device's remote wakeup is enabled
> + * @is_self_powered: is reported in device status and the config descriptor
> + * @ep0_req: dummy request used while handling standard USB requests
> + *		for GET_STATUS and SET_SEL
> + * @setup_buf: ep0 response buffer for GET_STATUS and SET_SEL requests
> + */
> +struct mtu3 {
> +	spinlock_t lock;
> +	struct ssusb_mtk *ssusb;
> +	struct device *dev;
> +	void __iomem *mac_base;
> +	void __iomem *ippc_base;
> +	int irq;
> +
> +	struct mtu3_fifo_info tx_fifo;
> +	struct mtu3_fifo_info rx_fifo;
> +
> +	struct mtu3_ep *ep_array;
> +	struct mtu3_ep *in_eps;
> +	struct mtu3_ep *out_eps;
> +	struct mtu3_ep *ep0;
> +	int num_eps;
> +	int slot;
> +	int active_ep;
> +
> +	struct dma_pool	*qmu_gpd_pool;
> +	struct tasklet_struct qmu_done;
> +	u32 qmu_done_intr;
> +
> +	enum mtu3_g_ep0_state ep0_state;
> +	struct usb_gadget g;	/* the gadget */
> +	struct usb_gadget_driver *gadget_driver;
> +	struct mtu3_request ep0_req;
> +	u8 setup_buf[EP0_RESPONSE_BUF];
> +	u32 max_speed;
> +
> +	unsigned is_active:1;
> +	unsigned may_wakeup:1;
> +	unsigned is_self_powered:1;
> +	unsigned test_mode:1;
> +	unsigned softconnect:1;
> +	unsigned u1_enable:1;
> +	unsigned u2_enable:1;
> +	unsigned is_u3_ip:1;
> +
> +	u8 address;
> +	u8 test_mode_nr;
> +	u32 hw_version;
> +};
> +
> +static inline struct mtu3 *gadget_to_mtu3(struct usb_gadget *g)
> +{
> +	return container_of(g, struct mtu3, g);
> +}
> +
> +static inline int is_first_entry(const struct list_head *list,
> +	const struct list_head *head)
> +{
> +	return list_is_last(head, list);
> +}
> +
> +static inline struct mtu3_request *to_mtu3_request(struct usb_request *req)
> +{
> +	return req ? container_of(req, struct mtu3_request, request) : NULL;
> +}
> +
> +static inline struct mtu3_ep *to_mtu3_ep(struct usb_ep *ep)
> +{
> +	return ep ? container_of(ep, struct mtu3_ep, ep) : NULL;
> +}
> +
> +static inline struct mtu3_request *next_request(struct mtu3_ep *mep)
> +{
> +	struct list_head *queue = &mep->req_list;
> +
> +	if (list_empty(queue))
> +		return NULL;
> +
> +	return list_first_entry(queue, struct mtu3_request, list);
> +}
> +
> +static inline void mtu3_writel(void __iomem *base, u32 offset, u32 data)
> +{
> +	writel(data, base + offset);
> +}
> +
> +static inline u32 mtu3_readl(void __iomem *base, u32 offset)
> +{
> +	return readl(base + offset);
> +}
> +
> +static inline void mtu3_setbits(void __iomem *base, u32 offset, u32 bits)
> +{
> +	void __iomem *addr = base + offset;
> +	u32 tmp = readl(addr);
> +
> +	writel((tmp | (bits)), addr);
> +}
> +
> +static inline void mtu3_clrbits(void __iomem *base, u32 offset, u32 bits)
> +{
> +	void __iomem *addr = base + offset;
> +	u32 tmp = readl(addr);
> +
> +	writel((tmp & ~(bits)), addr);
> +}
> +
> +int ssusb_check_clocks(struct ssusb_mtk *ssusb, u32 ex_clks);
> +struct usb_request *mtu3_alloc_request(struct usb_ep *ep, gfp_t gfp_flags);
> +void mtu3_free_request(struct usb_ep *ep, struct usb_request *req);
> +void mtu3_req_complete(struct mtu3_ep *mep,
> +		struct usb_request *req, int status);
> +
> +int mtu3_config_ep(struct mtu3 *mtu, struct mtu3_ep *mep,
> +		int interval, int burst, int mult);
> +void mtu3_deconfig_ep(struct mtu3 *mtu, struct mtu3_ep *mep);
> +void mtu3_ep_stall_set(struct mtu3_ep *mep, bool set);
> +void mtu3_ep0_setup(struct mtu3 *mtu);
> +void mtu3_start(struct mtu3 *mtu);
> +void mtu3_stop(struct mtu3 *mtu);
> +void mtu3_dev_on_off(struct mtu3 *mtu, int is_on);
> +
> +int mtu3_gadget_setup(struct mtu3 *mtu);
> +void mtu3_gadget_cleanup(struct mtu3 *mtu);
> +void mtu3_gadget_reset(struct mtu3 *mtu);
> +void mtu3_gadget_suspend(struct mtu3 *mtu);
> +void mtu3_gadget_resume(struct mtu3 *mtu);
> +void mtu3_gadget_disconnect(struct mtu3 *mtu);
> +
> +irqreturn_t mtu3_ep0_isr(struct mtu3 *mtu);
> +extern const struct usb_ep_ops mtu3_ep0_ops;
> +
> +#endif
> diff --git a/drivers/usb/mtu3/mtu3_core.c b/drivers/usb/mtu3/mtu3_core.c
> new file mode 100644
> index 0000000..408e939
> --- /dev/null
> +++ b/drivers/usb/mtu3/mtu3_core.c
> @@ -0,0 +1,871 @@
> +/*
> + * mtu3_core.c - hardware access layer and gadget init/exit of
> + *                     MediaTek usb3 Dual-Role Controller Driver
> + *
> + * Copyright (C) 2016 MediaTek Inc.
> + *
> + * Author: Chunfeng Yun <chunfeng.yun@mediatek.com>
> + *
> + * This software is licensed under the terms of the GNU General Public
> + * License version 2, as published by the Free Software Foundation, and
> + * may be copied, distributed, and modified under those terms.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + * GNU General Public License for more details.
> + *
> + */
> +
> +#include <linux/kernel.h>
> +#include <linux/module.h>
> +#include <linux/of_address.h>
> +#include <linux/of_irq.h>
> +#include <linux/platform_device.h>
> +
> +#include "mtu3.h"
> +
> +static int ep_fifo_alloc(struct mtu3_ep *mep, u32 seg_size)
> +{
> +	struct mtu3_fifo_info *fifo = mep->fifo;
> +	u32 num_bits = DIV_ROUND_UP(seg_size, MTU3_EP_FIFO_UNIT);
> +	u32 start_bit;
> +
> +	/* ensure that @mep->fifo_seg_size is power of two */
> +	num_bits = roundup_pow_of_two(num_bits);
> +	if (num_bits > fifo->limit)
> +		return -EINVAL;
> +
> +	mep->fifo_seg_size = num_bits * MTU3_EP_FIFO_UNIT;
> +	num_bits = num_bits * (mep->slot + 1);
> +	start_bit = bitmap_find_next_zero_area(fifo->bitmap,
> +			fifo->limit, 0, num_bits, 0);
> +	if (start_bit >= fifo->limit)
> +		return -EOVERFLOW;
> +
> +	bitmap_set(fifo->bitmap, start_bit, num_bits);
> +	mep->fifo_size = num_bits * MTU3_EP_FIFO_UNIT;
> +	mep->fifo_addr = fifo->base + MTU3_EP_FIFO_UNIT * start_bit;
> +
> +	dev_dbg(mep->mtu->dev, "%s fifo:%#x/%#x, start_bit: %d\n",
> +		__func__, mep->fifo_seg_size, mep->fifo_size, start_bit);
> +
> +	return mep->fifo_addr;
> +}
> +
> +static void ep_fifo_free(struct mtu3_ep *mep)
> +{
> +	struct mtu3_fifo_info *fifo = mep->fifo;
> +	u32 addr = mep->fifo_addr;
> +	u32 bits = mep->fifo_size / MTU3_EP_FIFO_UNIT;
> +	u32 start_bit;
> +
> +	if (unlikely(addr < fifo->base || bits > fifo->limit))
> +		return;
> +
> +	start_bit = (addr - fifo->base) / MTU3_EP_FIFO_UNIT;
> +	bitmap_clear(fifo->bitmap, start_bit, bits);
> +	mep->fifo_size = 0;
> +	mep->fifo_seg_size = 0;
> +
> +	dev_dbg(mep->mtu->dev, "%s size:%#x/%#x, start_bit: %d\n",
> +		__func__, mep->fifo_seg_size, mep->fifo_size, start_bit);
> +}
> +
> +/* enable/disable U3D SS function */
> +static inline void mtu3_ss_func_set(struct mtu3 *mtu, bool enable)
> +{
> +	/* If usb3_en==0, LTSSM will go to SS.Disable state */
> +	if (enable)
> +		mtu3_setbits(mtu->mac_base, U3D_USB3_CONFIG, USB3_EN);
> +	else
> +		mtu3_clrbits(mtu->mac_base, U3D_USB3_CONFIG, USB3_EN);
> +
> +	dev_dbg(mtu->dev, "USB3_EN = %d\n", !!enable);
> +}
> +
> +/* set/clear U3D HS device soft connect */
> +static inline void mtu3_hs_softconn_set(struct mtu3 *mtu, bool enable)
> +{
> +	if (enable) {
> +		mtu3_setbits(mtu->mac_base, U3D_POWER_MANAGEMENT,
> +			SOFT_CONN | SUSPENDM_ENABLE);
> +	} else {
> +		mtu3_clrbits(mtu->mac_base, U3D_POWER_MANAGEMENT,
> +			SOFT_CONN | SUSPENDM_ENABLE);
> +	}
> +	dev_dbg(mtu->dev, "SOFTCONN = %d\n", !!enable);
> +}
> +
> +/* only port0 of U2/U3 supports device mode */
> +static int mtu3_device_enable(struct mtu3 *mtu)
> +{
> +	void __iomem *ibase = mtu->ippc_base;
> +	u32 check_clk = 0;
> +
> +	mtu3_clrbits(ibase, U3D_SSUSB_IP_PW_CTRL2, SSUSB_IP_DEV_PDN);
> +
> +	if (mtu->is_u3_ip) {
> +		check_clk = SSUSB_U3_MAC_RST_B_STS;
> +		mtu3_clrbits(ibase, SSUSB_U3_CTRL(0),
> +			(SSUSB_U3_PORT_DIS | SSUSB_U3_PORT_PDN |
> +			SSUSB_U3_PORT_HOST_SEL));
> +	}
> +	mtu3_clrbits(ibase, SSUSB_U2_CTRL(0),
> +		(SSUSB_U2_PORT_DIS | SSUSB_U2_PORT_PDN |
> +		SSUSB_U2_PORT_HOST_SEL));
> +
> +	mtu3_setbits(ibase, SSUSB_U2_CTRL(0), SSUSB_U2_PORT_OTG_SEL);
> +
> +	return ssusb_check_clocks(mtu->ssusb, check_clk);
> +}
> +
> +static void mtu3_device_disable(struct mtu3 *mtu)
> +{
> +	void __iomem *ibase = mtu->ippc_base;
> +
> +	if (mtu->is_u3_ip)
> +		mtu3_setbits(ibase, SSUSB_U3_CTRL(0),
> +			(SSUSB_U3_PORT_DIS | SSUSB_U3_PORT_PDN));
> +
> +	mtu3_setbits(ibase, SSUSB_U2_CTRL(0),
> +		SSUSB_U2_PORT_DIS | SSUSB_U2_PORT_PDN);
> +
> +	mtu3_clrbits(ibase, SSUSB_U2_CTRL(0), SSUSB_U2_PORT_OTG_SEL);
> +
> +	mtu3_setbits(ibase, U3D_SSUSB_IP_PW_CTRL2, SSUSB_IP_DEV_PDN);
> +}
> +
> +/* reset U3D's device module. */
> +static void mtu3_device_reset(struct mtu3 *mtu)
> +{
> +	void __iomem *ibase = mtu->ippc_base;
> +
> +	mtu3_setbits(ibase, U3D_SSUSB_DEV_RST_CTRL, SSUSB_DEV_SW_RST);
> +	udelay(1);
> +	mtu3_clrbits(ibase, U3D_SSUSB_DEV_RST_CTRL, SSUSB_DEV_SW_RST);
> +}
> +
> +/* disable all interrupts */
> +static void mtu3_intr_disable(struct mtu3 *mtu)
> +{
> +	void __iomem *mbase = mtu->mac_base;
> +
> +	/* Disable level 1 interrupts */
> +	mtu3_writel(mbase, U3D_LV1IECR, ~0x0);
> +
> +	/* Disable endpoint interrupts */
> +	mtu3_writel(mbase, U3D_EPIECR, ~0x0);
> +}
> +
> +static void mtu3_intr_status_clear(struct mtu3 *mtu)
> +{
> +	void __iomem *mbase = mtu->mac_base;
> +
> +	/* Clear EP0 and Tx/Rx EPn interrupts status */
> +	mtu3_writel(mbase, U3D_EPISR, ~0x0);
> +
> +	/* Clear U2 USB common interrupts status */
> +	mtu3_writel(mbase, U3D_COMMON_USB_INTR, ~0x0);
> +
> +	/* Clear U3 LTSSM interrupts status */
> +	mtu3_writel(mbase, U3D_LTSSM_INTR, ~0x0);
> +
> +	/* Clear speed change interrupt status */
> +	mtu3_writel(mbase, U3D_DEV_LINK_INTR, ~0x0);
> +}
> +
> +/* enable system global interrupt */
> +static void mtu3_intr_enable(struct mtu3 *mtu)
> +{
> +	void __iomem *mbase = mtu->mac_base;
> +	u32 value;
> +
> +	/*Enable level 1 interrupts (BMU, QMU, MAC3, DMA, MAC2, EPCTL) */
> +	value = BMU_INTR | QMU_INTR | MAC3_INTR | MAC2_INTR | EP_CTRL_INTR;
> +	mtu3_writel(mbase, U3D_LV1IESR, value);
> +
> +	/* Enable U2 common USB interrupts */
> +	value = SUSPEND_INTR | RESUME_INTR | RESET_INTR;
> +	mtu3_writel(mbase, U3D_COMMON_USB_INTR_ENABLE, value);
> +
> +	if (mtu->is_u3_ip) {
> +		/* Enable U3 LTSSM interrupts */
> +		value = HOT_RST_INTR | WARM_RST_INTR | VBUS_RISE_INTR |
> +		    VBUS_FALL_INTR | ENTER_U3_INTR | EXIT_U3_INTR;
> +		mtu3_writel(mbase, U3D_LTSSM_INTR_ENABLE, value);
> +	}
> +
> +	/* Enable QMU interrupts. */
> +	value = TXQ_CSERR_INT | TXQ_LENERR_INT | RXQ_CSERR_INT |
> +			RXQ_LENERR_INT | RXQ_ZLPERR_INT;
> +	mtu3_writel(mbase, U3D_QIESR1, value);
> +
> +	/* Enable speed change interrupt */
> +	mtu3_writel(mbase, U3D_DEV_LINK_INTR_ENABLE, SSUSB_DEV_SPEED_CHG_INTR);
> +}
> +
> +/* set/clear the stall and toggle bits for non-ep0 */
> +void mtu3_ep_stall_set(struct mtu3_ep *mep, bool set)
> +{
> +	struct mtu3 *mtu = mep->mtu;
> +	void __iomem *mbase = mtu->mac_base;
> +	u8 epnum = mep->epnum;
> +	u32 csr;
> +
> +	if (mep->is_in) {	/* TX */
> +		csr = mtu3_readl(mbase, MU3D_EP_TXCR0(epnum)) & TX_W1C_BITS;
> +		if (set)
> +			csr |= TX_SENDSTALL;
> +		else
> +			csr = (csr & (~TX_SENDSTALL)) | TX_SENTSTALL;
> +		mtu3_writel(mbase, MU3D_EP_TXCR0(epnum), csr);
> +	} else {	/* RX */
> +		csr = mtu3_readl(mbase, MU3D_EP_RXCR0(epnum)) & RX_W1C_BITS;
> +		if (set)
> +			csr |= RX_SENDSTALL;
> +		else
> +			csr = (csr & (~RX_SENDSTALL)) | RX_SENTSTALL;
> +		mtu3_writel(mbase, MU3D_EP_RXCR0(epnum), csr);
> +	}
> +
> +	if (!set) {
> +		mtu3_setbits(mbase, U3D_EP_RST, EP_RST(mep->is_in, epnum));
> +		mtu3_clrbits(mbase, U3D_EP_RST, EP_RST(mep->is_in, epnum));
> +		mep->flags &= ~MTU3_EP_STALL;
> +	} else {
> +		mep->flags |= MTU3_EP_STALL;
> +	}
> +
> +	dev_dbg(mtu->dev, "%s: %s\n", mep->name,
> +		set ? "SEND STALL" : "CLEAR STALL, with EP RESET");
> +}
> +
> +void mtu3_dev_on_off(struct mtu3 *mtu, int is_on)
> +{
> +	if (mtu->is_u3_ip && (mtu->max_speed == USB_SPEED_SUPER))
> +		mtu3_ss_func_set(mtu, is_on);
> +	else
> +		mtu3_hs_softconn_set(mtu, is_on);
> +
> +	dev_info(mtu->dev, "gadget (%s) pullup D%s\n",
> +		usb_speed_string(mtu->max_speed), is_on ? "+" : "-");
> +}
> +
> +void mtu3_start(struct mtu3 *mtu)
> +{
> +	void __iomem *mbase = mtu->mac_base;
> +
> +	dev_dbg(mtu->dev, "%s devctl 0x%x\n", __func__,
> +		mtu3_readl(mbase, U3D_DEVICE_CONTROL));
> +
> +	mtu3_clrbits(mtu->ippc_base, U3D_SSUSB_IP_PW_CTRL2, SSUSB_IP_DEV_PDN);
> +
> +	/*
> +	 * When disable U2 port, USB2_CSR's register will be reset to
> +	 * default value after re-enable it again(HS is enabled by default).
> +	 * So if force mac to work as FS, disable HS function.
> +	 */
> +	if (mtu->max_speed == USB_SPEED_FULL)
> +		mtu3_clrbits(mbase, U3D_POWER_MANAGEMENT, HS_ENABLE);
> +
> +	/* Initialize the default interrupts */
> +	mtu3_intr_enable(mtu);
> +
> +	mtu->is_active = 1;
> +
> +	if (mtu->softconnect)
> +		mtu3_dev_on_off(mtu, 1);
> +
> +}
> +
> +void mtu3_stop(struct mtu3 *mtu)
> +{
> +	dev_dbg(mtu->dev, "%s\n", __func__);
> +
> +	mtu3_intr_disable(mtu);
> +	mtu3_intr_status_clear(mtu);
> +
> +	if (mtu->softconnect)
> +		mtu3_dev_on_off(mtu, 0);
> +
> +	mtu->is_active = 0;
> +	mtu3_setbits(mtu->ippc_base, U3D_SSUSB_IP_PW_CTRL2, SSUSB_IP_DEV_PDN);
> +}
> +
> +/* for non-ep0 */
> +int mtu3_config_ep(struct mtu3 *mtu, struct mtu3_ep *mep,
> +			int interval, int burst, int mult)
> +{
> +	void __iomem *mbase = mtu->mac_base;
> +	int epnum = mep->epnum;
> +	u32 csr0, csr1, csr2;
> +	int fifo_sgsz, fifo_addr;
> +	int num_pkts;
> +
> +	fifo_addr = ep_fifo_alloc(mep, mep->maxp);
> +	if (fifo_addr < 0) {
> +		dev_err(mtu->dev, "alloc ep fifo failed(%d)\n", mep->maxp);
> +		return -ENOMEM;
> +	}
> +	fifo_sgsz = ilog2(mep->fifo_seg_size);
> +	dev_dbg(mtu->dev, "%s fifosz: %x(%x/%x)\n", __func__, fifo_sgsz,
> +		mep->fifo_seg_size, mep->fifo_size);
> +
> +	if (mep->is_in) {
> +		csr0 = TX_TXMAXPKTSZ(mep->maxp);
> +		csr0 |= TX_DMAREQEN;
> +
> +		num_pkts = (burst + 1) * (mult + 1) - 1;
> +		csr1 = TX_SS_BURST(burst) | TX_SLOT(mep->slot);
> +		csr1 |= TX_MAX_PKT(num_pkts) | TX_MULT(mult);
> +
> +		csr2 = TX_FIFOADDR(fifo_addr >> 4);
> +		csr2 |= TX_FIFOSEGSIZE(fifo_sgsz);
> +
> +		switch (mep->type) {
> +		case USB_ENDPOINT_XFER_BULK:
> +			csr1 |= TX_TYPE(TYPE_BULK);
> +			break;
> +		case USB_ENDPOINT_XFER_ISOC:
> +			csr1 |= TX_TYPE(TYPE_ISO);
> +			csr2 |= TX_BINTERVAL(interval);
> +			break;
> +		case USB_ENDPOINT_XFER_INT:
> +			csr1 |= TX_TYPE(TYPE_INT);
> +			csr2 |= TX_BINTERVAL(interval);
> +			break;
> +		}
> +
> +		/* Enable QMU Done interrupt */
> +		mtu3_setbits(mbase, U3D_QIESR0, QMU_TX_DONE_INT(epnum));
> +
> +		mtu3_writel(mbase, MU3D_EP_TXCR0(epnum), csr0);
> +		mtu3_writel(mbase, MU3D_EP_TXCR1(epnum), csr1);
> +		mtu3_writel(mbase, MU3D_EP_TXCR2(epnum), csr2);
> +
> +		dev_dbg(mtu->dev, "U3D_TX%d CSR0:%#x, CSR1:%#x, CSR2:%#x\n",
> +			epnum, mtu3_readl(mbase, MU3D_EP_TXCR0(epnum)),
> +			mtu3_readl(mbase, MU3D_EP_TXCR1(epnum)),
> +			mtu3_readl(mbase, MU3D_EP_TXCR2(epnum)));
> +	} else {
> +		csr0 = RX_RXMAXPKTSZ(mep->maxp);
> +		csr0 |= RX_DMAREQEN;
> +
> +		num_pkts = (burst + 1) * (mult + 1) - 1;
> +		csr1 = RX_SS_BURST(burst) | RX_SLOT(mep->slot);
> +		csr1 |= RX_MAX_PKT(num_pkts) | RX_MULT(mult);
> +
> +		csr2 = RX_FIFOADDR(fifo_addr >> 4);
> +		csr2 |= RX_FIFOSEGSIZE(fifo_sgsz);
> +
> +		switch (mep->type) {
> +		case USB_ENDPOINT_XFER_BULK:
> +			csr1 |= RX_TYPE(TYPE_BULK);
> +			break;
> +		case USB_ENDPOINT_XFER_ISOC:
> +			csr1 |= RX_TYPE(TYPE_ISO);
> +			csr2 |= RX_BINTERVAL(interval);
> +			break;
> +		case USB_ENDPOINT_XFER_INT:
> +			csr1 |= RX_TYPE(TYPE_INT);
> +			csr2 |= RX_BINTERVAL(interval);
> +			break;
> +		}
> +
> +		/*Enable QMU Done interrupt */
> +		mtu3_setbits(mbase, U3D_QIESR0, QMU_RX_DONE_INT(epnum));
> +
> +		mtu3_writel(mbase, MU3D_EP_RXCR0(epnum), csr0);
> +		mtu3_writel(mbase, MU3D_EP_RXCR1(epnum), csr1);
> +		mtu3_writel(mbase, MU3D_EP_RXCR2(epnum), csr2);
> +
> +		dev_dbg(mtu->dev, "U3D_RX%d CSR0:%#x, CSR1:%#x, CSR2:%#x\n",
> +			epnum, mtu3_readl(mbase, MU3D_EP_RXCR0(epnum)),
> +			mtu3_readl(mbase, MU3D_EP_RXCR1(epnum)),
> +			mtu3_readl(mbase, MU3D_EP_RXCR2(epnum)));
> +	}
> +
> +	dev_dbg(mtu->dev, "csr0:%#x, csr1:%#x, csr2:%#x\n", csr0, csr1, csr2);
> +	dev_dbg(mtu->dev, "%s: %s, fifo-addr:%#x, fifo-size:%#x(%#x/%#x)\n",
> +		__func__, mep->name, mep->fifo_addr, mep->fifo_size,
> +		fifo_sgsz, mep->fifo_seg_size);
> +
> +	return 0;
> +}
> +
> +/* for non-ep0 */
> +void mtu3_deconfig_ep(struct mtu3 *mtu, struct mtu3_ep *mep)
> +{
> +	void __iomem *mbase = mtu->mac_base;
> +	int epnum = mep->epnum;
> +
> +	if (mep->is_in) {
> +		mtu3_writel(mbase, MU3D_EP_TXCR0(epnum), 0);
> +		mtu3_writel(mbase, MU3D_EP_TXCR1(epnum), 0);
> +		mtu3_writel(mbase, MU3D_EP_TXCR2(epnum), 0);
> +		mtu3_setbits(mbase, U3D_QIECR0, QMU_TX_DONE_INT(epnum));
> +	} else {
> +		mtu3_writel(mbase, MU3D_EP_RXCR0(epnum), 0);
> +		mtu3_writel(mbase, MU3D_EP_RXCR1(epnum), 0);
> +		mtu3_writel(mbase, MU3D_EP_RXCR2(epnum), 0);
> +		mtu3_setbits(mbase, U3D_QIECR0, QMU_RX_DONE_INT(epnum));
> +	}
> +
> +	ep_fifo_free(mep);
> +
> +	dev_dbg(mtu->dev, "%s: %s\n", __func__, mep->name);
> +}
> +
> +/*
> + * Two scenarios:
> + * 1. when device IP supports SS, the fifo of EP0, TX EPs, RX EPs
> + *	are separated;
> + * 2. when supports only HS, the fifo is shared for all EPs, and
> + *	the capability registers of @EPNTXFFSZ or @EPNRXFFSZ indicate
> + *	the total fifo size of non-ep0, and ep0's is fixed to 64B,
> + *	so the total fifo size is 64B + @EPNTXFFSZ;
> + *	Due to the first 64B should be reserved for EP0, non-ep0's fifo
> + *	starts from offset 64 and are divided into two equal parts for
> + *	TX or RX EPs for simplification.
> + */
> +static void get_ep_fifo_config(struct mtu3 *mtu)
> +{
> +	struct mtu3_fifo_info *tx_fifo;
> +	struct mtu3_fifo_info *rx_fifo;
> +	u32 fifosize;
> +
> +	if (mtu->is_u3_ip) {
> +		fifosize = mtu3_readl(mtu->mac_base, U3D_CAP_EPNTXFFSZ);
> +		tx_fifo = &mtu->tx_fifo;
> +		tx_fifo->base = 0;
> +		tx_fifo->limit = fifosize / MTU3_EP_FIFO_UNIT;
> +		bitmap_zero(tx_fifo->bitmap, MTU3_FIFO_BIT_SIZE);
> +
> +		fifosize = mtu3_readl(mtu->mac_base, U3D_CAP_EPNRXFFSZ);
> +		rx_fifo = &mtu->rx_fifo;
> +		rx_fifo->base = 0;
> +		rx_fifo->limit = fifosize / MTU3_EP_FIFO_UNIT;
> +		bitmap_zero(rx_fifo->bitmap, MTU3_FIFO_BIT_SIZE);
> +		mtu->slot = MTU3_U3_IP_SLOT_DEFAULT;
> +	} else {
> +		fifosize = mtu3_readl(mtu->mac_base, U3D_CAP_EPNTXFFSZ);
> +		tx_fifo = &mtu->tx_fifo;
> +		tx_fifo->base = MTU3_U2_IP_EP0_FIFO_SIZE;
> +		tx_fifo->limit = (fifosize / MTU3_EP_FIFO_UNIT) >> 1;
> +		bitmap_zero(tx_fifo->bitmap, MTU3_FIFO_BIT_SIZE);
> +
> +		rx_fifo = &mtu->rx_fifo;
> +		rx_fifo->base =
> +			tx_fifo->base + tx_fifo->limit * MTU3_EP_FIFO_UNIT;
> +		rx_fifo->limit = tx_fifo->limit;
> +		bitmap_zero(rx_fifo->bitmap, MTU3_FIFO_BIT_SIZE);
> +		mtu->slot = MTU3_U2_IP_SLOT_DEFAULT;
> +	}
> +
> +	dev_dbg(mtu->dev, "%s, TX: base-%d, limit-%d; RX: base-%d, limit-%d\n",
> +		__func__, tx_fifo->base, tx_fifo->limit,
> +		rx_fifo->base, rx_fifo->limit);
> +}
> +
> +void mtu3_ep0_setup(struct mtu3 *mtu)
> +{
> +	u32 maxpacket = mtu->g.ep0->maxpacket;
> +	u32 csr;
> +
> +	dev_dbg(mtu->dev, "%s maxpacket: %d\n", __func__, maxpacket);
> +
> +	csr = mtu3_readl(mtu->mac_base, U3D_EP0CSR);
> +	csr &= ~EP0_MAXPKTSZ_MSK;
> +	csr |= EP0_MAXPKTSZ(maxpacket);
> +	csr &= EP0_W1C_BITS;
> +	mtu3_writel(mtu->mac_base, U3D_EP0CSR, csr);
> +
> +	/* Enable EP0 interrupt */
> +	mtu3_writel(mtu->mac_base, U3D_EPIESR, EP0ISR);
> +}
> +
> +static int mtu3_mem_alloc(struct mtu3 *mtu)
> +{
> +	void __iomem *mbase = mtu->mac_base;
> +	struct mtu3_ep *ep_array;
> +	int in_ep_num, out_ep_num;
> +	u32 cap_epinfo, cap_dev;
> +	int ret;
> +	int i;
> +
> +	mtu->hw_version = mtu3_readl(mtu->ippc_base, U3D_SSUSB_HW_ID);
> +
> +	cap_dev = mtu3_readl(mtu->ippc_base, U3D_SSUSB_IP_DEV_CAP);
> +	mtu->is_u3_ip = !!SSUSB_IP_DEV_U3_PORT_NUM(cap_dev);
> +
> +	cap_epinfo = mtu3_readl(mbase, U3D_CAP_EPINFO);
> +	in_ep_num = CAP_TX_EP_NUM(cap_epinfo);
> +	out_ep_num = CAP_RX_EP_NUM(cap_epinfo);
> +
> +	dev_info(mtu->dev, "IP version 0x%x(%s IP)\n", mtu->hw_version,
> +		mtu->is_u3_ip ? "U3" : "U2");
> +	dev_info(mtu->dev, "fifosz/epnum: Tx=%#x/%d, Rx=%#x/%d\n",
> +		 mtu3_readl(mbase, U3D_CAP_EPNTXFFSZ), in_ep_num,
> +		 mtu3_readl(mbase, U3D_CAP_EPNRXFFSZ), out_ep_num);
> +
> +	/* one for ep0, another is reserved */
> +	mtu->num_eps = min(in_ep_num, out_ep_num) + 1;
> +	ep_array = kcalloc(mtu->num_eps * 2, sizeof(*ep_array), GFP_KERNEL);
> +	if (ep_array == NULL)
> +		return -ENOMEM;
> +
> +	mtu->ep_array = ep_array;
> +	mtu->in_eps = ep_array;
> +	mtu->out_eps = &ep_array[mtu->num_eps];
> +	/* ep0 uses in_eps[0], out_eps[0] is reserved */
> +	mtu->ep0 = mtu->in_eps;
> +
> +	mtu->ep0->mtu = mtu;
> +	mtu->ep0->epnum = 0;
> +
> +	for (i = 1; i < mtu->num_eps; i++) {
> +		struct mtu3_ep *mep = mtu->in_eps + i;
> +
> +		mep->fifo = &mtu->tx_fifo;
> +		mep = mtu->out_eps + i;
> +		mep->fifo = &mtu->rx_fifo;
> +	}
> +
> +	get_ep_fifo_config(mtu);
> +
> +	ret = mtu3_qmu_init(mtu);
> +	if (ret)
> +		kfree(mtu->ep_array);
> +
> +	return ret;
> +}
> +
> +static void mtu3_mem_free(struct mtu3 *mtu)
> +{
> +	mtu3_qmu_exit(mtu);
> +	kfree(mtu->ep_array);
> +}
> +
> +static void mtu3_set_speed(struct mtu3 *mtu)
> +{
> +	void __iomem *mbase = mtu->mac_base;
> +
> +	if (!mtu->is_u3_ip && (mtu->max_speed > USB_SPEED_HIGH))
> +		mtu->max_speed = USB_SPEED_HIGH;
> +
> +	if (mtu->max_speed == USB_SPEED_FULL) {
> +		/* disable U3 SS function */
> +		mtu3_clrbits(mbase, U3D_USB3_CONFIG, USB3_EN);
> +		/* disable HS function */
> +		mtu3_clrbits(mbase, U3D_POWER_MANAGEMENT, HS_ENABLE);
> +	} else if (mtu->max_speed == USB_SPEED_HIGH) {
> +		mtu3_clrbits(mbase, U3D_USB3_CONFIG, USB3_EN);
> +		/* HS/FS detected by HW */
> +		mtu3_setbits(mbase, U3D_POWER_MANAGEMENT, HS_ENABLE);
> +	}
> +	dev_info(mtu->dev, "max_speed: %s\n",
> +		usb_speed_string(mtu->max_speed));
> +}
> +
> +static void mtu3_regs_init(struct mtu3 *mtu)
> +{
> +
> +	void __iomem *mbase = mtu->mac_base;
> +
> +	/* be sure interrupts are disabled before registration of ISR */
> +	mtu3_intr_disable(mtu);
> +	mtu3_intr_status_clear(mtu);
> +
> +	if (mtu->is_u3_ip) {
> +		/* disable LGO_U1/U2 by default */
> +		mtu3_clrbits(mbase, U3D_LINK_POWER_CONTROL,
> +				SW_U1_ACCEPT_ENABLE | SW_U2_ACCEPT_ENABLE |
> +				SW_U1_REQUEST_ENABLE | SW_U2_REQUEST_ENABLE);
> +		/* device responses to u3_exit from host automatically */
> +		mtu3_clrbits(mbase, U3D_LTSSM_CTRL, SOFT_U3_EXIT_EN);
> +	}
> +
> +	mtu3_set_speed(mtu);
> +
> +	/* delay about 0.1us from detecting reset to send chirp-K */
> +	mtu3_clrbits(mbase, U3D_LINK_RESET_INFO, WTCHRP_MSK);
> +
> +	/* U2/U3 detected by HW */
> +	mtu3_writel(mbase, U3D_DEVICE_CONF, 0);
> +
> +	/* enable QMU 16B checksum */
> +	mtu3_setbits(mbase, U3D_QCR0, QMU_CS16B_EN);
> +
> +	/* vbus detected by HW */
> +	mtu3_clrbits(mbase, U3D_MISC_CTRL, VBUS_FRC_EN | VBUS_ON);
> +}
> +
> +static irqreturn_t mtu3_link_isr(struct mtu3 *mtu)
> +{
> +	void __iomem *mbase = mtu->mac_base;
> +	enum usb_device_speed udev_speed;
> +	u32 maxpkt = 64;
> +	u32 link;
> +	u32 speed;
> +
> +	link = mtu3_readl(mbase, U3D_DEV_LINK_INTR);
> +	link &= mtu3_readl(mbase, U3D_DEV_LINK_INTR_ENABLE);
> +	mtu3_writel(mbase, U3D_DEV_LINK_INTR, link); /* W1C */
> +	dev_dbg(mtu->dev, "=== LINK[%x] ===\n", link);
> +
> +	if (!(link & SSUSB_DEV_SPEED_CHG_INTR))
> +		return IRQ_NONE;
> +
> +	speed = SSUSB_DEV_SPEED(mtu3_readl(mbase, U3D_DEVICE_CONF));
> +
> +	switch (speed) {
> +	case MTU3_SPEED_FULL:
> +		udev_speed = USB_SPEED_FULL;
> +		/*BESLCK = 4 < BESLCK_U3 = 10 < BESLDCK = 15 */
> +		mtu3_writel(mbase, U3D_USB20_LPM_PARAMETER, LPM_BESLDCK(0xf)
> +				| LPM_BESLCK(4) | LPM_BESLCK_U3(0xa));
> +		mtu3_setbits(mbase, U3D_POWER_MANAGEMENT,
> +				LPM_BESL_STALL | LPM_BESLD_STALL);
> +		break;
> +	case MTU3_SPEED_HIGH:
> +		udev_speed = USB_SPEED_HIGH;
> +		/*BESLCK = 4 < BESLCK_U3 = 10 < BESLDCK = 15 */
> +		mtu3_writel(mbase, U3D_USB20_LPM_PARAMETER, LPM_BESLDCK(0xf)
> +				| LPM_BESLCK(4) | LPM_BESLCK_U3(0xa));
> +		mtu3_setbits(mbase, U3D_POWER_MANAGEMENT,
> +				LPM_BESL_STALL | LPM_BESLD_STALL);
> +		break;
> +	case MTU3_SPEED_SUPER:
> +		udev_speed = USB_SPEED_SUPER;
> +		maxpkt = 512;
> +		break;
> +	default:
> +		udev_speed = USB_SPEED_UNKNOWN;
> +		break;
> +	}
> +	dev_dbg(mtu->dev, "%s: %s\n", __func__, usb_speed_string(udev_speed));
> +
> +	mtu->g.speed = udev_speed;
> +	mtu->g.ep0->maxpacket = maxpkt;
> +	mtu->ep0_state = MU3D_EP0_STATE_SETUP;
> +
> +	if (udev_speed == USB_SPEED_UNKNOWN)
> +		mtu3_gadget_disconnect(mtu);
> +	else
> +		mtu3_ep0_setup(mtu);
> +
> +	return IRQ_HANDLED;
> +}
> +
> +static irqreturn_t mtu3_u3_ltssm_isr(struct mtu3 *mtu)
> +{
> +	void __iomem *mbase = mtu->mac_base;
> +	u32 ltssm;
> +
> +	ltssm = mtu3_readl(mbase, U3D_LTSSM_INTR);
> +	ltssm &= mtu3_readl(mbase, U3D_LTSSM_INTR_ENABLE);
> +	mtu3_writel(mbase, U3D_LTSSM_INTR, ltssm); /* W1C */
> +	dev_dbg(mtu->dev, "=== LTSSM[%x] ===\n", ltssm);
> +
> +	if (ltssm & (HOT_RST_INTR | WARM_RST_INTR))
> +		mtu3_gadget_reset(mtu);
> +
> +	if (ltssm & VBUS_FALL_INTR)
> +		mtu3_ss_func_set(mtu, false);
> +
> +	if (ltssm & VBUS_RISE_INTR)
> +		mtu3_ss_func_set(mtu, true);
> +
> +	if (ltssm & EXIT_U3_INTR)
> +		mtu3_gadget_resume(mtu);
> +
> +	if (ltssm & ENTER_U3_INTR)
> +		mtu3_gadget_suspend(mtu);
> +
> +	return IRQ_HANDLED;
> +}
> +
> +static irqreturn_t mtu3_u2_common_isr(struct mtu3 *mtu)
> +{
> +	void __iomem *mbase = mtu->mac_base;
> +	u32 u2comm;
> +
> +	u2comm = mtu3_readl(mbase, U3D_COMMON_USB_INTR);
> +	u2comm &= mtu3_readl(mbase, U3D_COMMON_USB_INTR_ENABLE);
> +	mtu3_writel(mbase, U3D_COMMON_USB_INTR, u2comm); /* W1C */
> +	dev_dbg(mtu->dev, "=== U2COMM[%x] ===\n", u2comm);
> +
> +	if (u2comm & SUSPEND_INTR)
> +		mtu3_gadget_suspend(mtu);
> +
> +	if (u2comm & RESUME_INTR)
> +		mtu3_gadget_resume(mtu);
> +
> +	if (u2comm & RESET_INTR)
> +		mtu3_gadget_reset(mtu);
> +
> +	return IRQ_HANDLED;
> +}
> +
> +irqreturn_t mtu3_irq(int irq, void *data)
> +{
> +	struct mtu3 *mtu = (struct mtu3 *)data;
> +	unsigned long flags;
> +	u32 level1;
> +
> +	spin_lock_irqsave(&mtu->lock, flags);
> +
> +	/* U3D_LV1ISR is RU */
> +	level1 = mtu3_readl(mtu->mac_base, U3D_LV1ISR);
> +	level1 &= mtu3_readl(mtu->mac_base, U3D_LV1IER);
> +
> +	if (level1 & EP_CTRL_INTR)
> +		mtu3_link_isr(mtu);
> +
> +	if (level1 & MAC2_INTR)
> +		mtu3_u2_common_isr(mtu);
> +
> +	if (level1 & MAC3_INTR)
> +		mtu3_u3_ltssm_isr(mtu);
> +
> +	if (level1 & BMU_INTR)
> +		mtu3_ep0_isr(mtu);
> +
> +	if (level1 & QMU_INTR)
> +		mtu3_qmu_isr(mtu);
> +
> +	spin_unlock_irqrestore(&mtu->lock, flags);
> +
> +	return IRQ_HANDLED;
> +}
> +
> +static int mtu3_hw_init(struct mtu3 *mtu)
> +{
> +	int ret;
> +
> +	mtu3_device_reset(mtu);
> +
> +	ret = mtu3_device_enable(mtu);
> +	if (ret) {
> +		dev_err(mtu->dev, "device enable failed %d\n", ret);
> +		return ret;
> +	}
> +
> +	ret = mtu3_mem_alloc(mtu);
> +	if (ret)
> +		return -ENOMEM;
> +
> +	mtu3_regs_init(mtu);
> +
> +	return 0;
> +}
> +
> +static void mtu3_hw_exit(struct mtu3 *mtu)
> +{
> +	mtu3_device_disable(mtu);
> +	mtu3_mem_free(mtu);
> +}
> +
> +/*-------------------------------------------------------------------------*/
> +
> +int ssusb_gadget_init(struct ssusb_mtk *ssusb)
> +{
> +	struct device *dev = ssusb->dev;
> +	struct platform_device *pdev = to_platform_device(dev);
> +	struct mtu3 *mtu = NULL;
> +	struct resource *res;
> +	int ret = -ENOMEM;
> +
> +	mtu = devm_kzalloc(dev, sizeof(struct mtu3), GFP_KERNEL);
> +	if (mtu == NULL)
> +		return -ENOMEM;
> +
> +	mtu->irq = platform_get_irq(pdev, 0);
> +	if (mtu->irq <= 0) {
> +		dev_err(dev, "fail to get irq number\n");
> +		return -ENODEV;
> +	}
> +	dev_info(dev, "irq %d\n", mtu->irq);
> +
> +	res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "mac");
> +	mtu->mac_base = devm_ioremap_resource(dev, res);
> +	if (IS_ERR(mtu->mac_base)) {
> +		dev_err(dev, "error mapping memory for dev mac\n");
> +		return PTR_ERR(mtu->mac_base);
> +	}
> +
> +	spin_lock_init(&mtu->lock);
> +	mtu->dev = dev;
> +	mtu->ippc_base = ssusb->ippc_base;
> +	ssusb->mac_base	= mtu->mac_base;
> +	ssusb->u3d = mtu;
> +	mtu->ssusb = ssusb;
> +	mtu->max_speed = usb_get_maximum_speed(dev);
> +
> +	/* check the max_speed parameter */
> +	switch (mtu->max_speed) {
> +	case USB_SPEED_FULL:
> +	case USB_SPEED_HIGH:
> +	case USB_SPEED_SUPER:
> +		break;
> +	default:
> +		dev_err(dev, "invalid max_speed: %s\n",
> +			usb_speed_string(mtu->max_speed));
> +		/* fall through */
> +	case USB_SPEED_UNKNOWN:
> +		/* default as SS */
> +		mtu->max_speed = USB_SPEED_SUPER;
> +		break;
> +	}
> +
> +	dev_dbg(dev, "mac_base=0x%p, ippc_base=0x%p\n",
> +		mtu->mac_base, mtu->ippc_base);
> +
> +	ret = mtu3_hw_init(mtu);
> +	if (ret) {
> +		dev_err(dev, "mtu3 hw init failed:%d\n", ret);
> +		return ret;
> +	}
> +
> +	ret = devm_request_irq(dev, mtu->irq, mtu3_irq, 0, dev_name(dev), mtu);
> +	if (ret) {
> +		dev_err(dev, "request irq %d failed!\n", mtu->irq);
> +		goto irq_err;
> +	}
> +
> +	device_init_wakeup(dev, true);
> +
> +	ret = mtu3_gadget_setup(mtu);
> +	if (ret) {
> +		dev_err(dev, "mtu3 gadget init failed:%d\n", ret);
> +		goto gadget_err;
> +	}
> +
> +	/* init as host mode, power down device IP for power saving */
> +	if (mtu->ssusb->dr_mode == USB_DR_MODE_OTG)
> +		mtu3_stop(mtu);
> +
> +	dev_dbg(dev, " %s() done...\n", __func__);
> +
> +	return 0;
> +
> +gadget_err:
> +	device_init_wakeup(dev, false);
> +
> +irq_err:
> +	mtu3_hw_exit(mtu);
> +	ssusb->u3d = NULL;
> +	dev_err(dev, " %s() fail...\n", __func__);
> +
> +	return ret;
> +}
> +
> +void ssusb_gadget_exit(struct ssusb_mtk *ssusb)
> +{
> +	struct mtu3 *mtu = ssusb->u3d;
> +
> +	mtu3_gadget_cleanup(mtu);
> +	device_init_wakeup(ssusb->dev, false);
> +	mtu3_hw_exit(mtu);
> +}
> diff --git a/drivers/usb/mtu3/mtu3_dr.c b/drivers/usb/mtu3/mtu3_dr.c
> new file mode 100644
> index 0000000..1a8987e
> --- /dev/null
> +++ b/drivers/usb/mtu3/mtu3_dr.c
> @@ -0,0 +1,379 @@
> +/*
> + * mtu3_dr.c - dual role switch and host glue layer
> + *
> + * Copyright (C) 2016 MediaTek Inc.
> + *
> + * Author: Chunfeng Yun <chunfeng.yun@mediatek.com>
> + *
> + * This software is licensed under the terms of the GNU General Public
> + * License version 2, as published by the Free Software Foundation, and
> + * may be copied, distributed, and modified under those terms.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + * GNU General Public License for more details.
> + *
> + */
> +
> +#include <linux/debugfs.h>
> +#include <linux/irq.h>
> +#include <linux/kernel.h>
> +#include <linux/of_device.h>
> +#include <linux/pinctrl/consumer.h>
> +#include <linux/seq_file.h>
> +#include <linux/uaccess.h>
> +
> +#include "mtu3.h"
> +#include "mtu3_dr.h"
> +
> +#define USB2_PORT 2
> +#define USB3_PORT 3
> +
> +enum mtu3_vbus_id_state {
> +	MTU3_ID_FLOAT = 1,
> +	MTU3_ID_GROUND,
> +	MTU3_VBUS_OFF,
> +	MTU3_VBUS_VALID,
> +};
> +
> +static void toggle_opstate(struct ssusb_mtk *ssusb)
> +{
> +	if (!ssusb->otg_switch.is_u3_drd) {
> +		mtu3_setbits(ssusb->mac_base, U3D_DEVICE_CONTROL, DC_SESSION);
> +		mtu3_setbits(ssusb->mac_base, U3D_POWER_MANAGEMENT, SOFT_CONN);
> +	}
> +}
> +
> +/* only port0 supports dual-role mode */
> +static int ssusb_port0_switch(struct ssusb_mtk *ssusb,
> +	int version, bool tohost)
> +{
> +	void __iomem *ibase = ssusb->ippc_base;
> +	u32 value;
> +
> +	dev_dbg(ssusb->dev, "%s (switch u%d port0 to %s)\n", __func__,
> +		version, tohost ? "host" : "device");
> +
> +	if (version == USB2_PORT) {
> +		/* 1. power off and disable u2 port0 */
> +		value = mtu3_readl(ibase, SSUSB_U2_CTRL(0));
> +		value |= SSUSB_U2_PORT_PDN | SSUSB_U2_PORT_DIS;
> +		mtu3_writel(ibase, SSUSB_U2_CTRL(0), value);
> +
> +		/* 2. power on, enable u2 port0 and select its mode */
> +		value = mtu3_readl(ibase, SSUSB_U2_CTRL(0));
> +		value &= ~(SSUSB_U2_PORT_PDN | SSUSB_U2_PORT_DIS);
> +		value = tohost ? (value | SSUSB_U2_PORT_HOST_SEL) :
> +			(value & (~SSUSB_U2_PORT_HOST_SEL));
> +		mtu3_writel(ibase, SSUSB_U2_CTRL(0), value);
> +	} else {
> +		/* 1. power off and disable u3 port0 */
> +		value = mtu3_readl(ibase, SSUSB_U3_CTRL(0));
> +		value |= SSUSB_U3_PORT_PDN | SSUSB_U3_PORT_DIS;
> +		mtu3_writel(ibase, SSUSB_U3_CTRL(0), value);
> +
> +		/* 2. power on, enable u3 port0 and select its mode */
> +		value = mtu3_readl(ibase, SSUSB_U3_CTRL(0));
> +		value &= ~(SSUSB_U3_PORT_PDN | SSUSB_U3_PORT_DIS);
> +		value = tohost ? (value | SSUSB_U3_PORT_HOST_SEL) :
> +			(value & (~SSUSB_U3_PORT_HOST_SEL));
> +		mtu3_writel(ibase, SSUSB_U3_CTRL(0), value);
> +	}
> +
> +	return 0;
> +}
> +
> +static void switch_port_to_host(struct ssusb_mtk *ssusb)
> +{
> +	u32 check_clk = 0;
> +
> +	dev_dbg(ssusb->dev, "%s\n", __func__);
> +
> +	ssusb_port0_switch(ssusb, USB2_PORT, true);
> +
> +	if (ssusb->otg_switch.is_u3_drd) {
> +		ssusb_port0_switch(ssusb, USB3_PORT, true);
> +		check_clk = SSUSB_U3_MAC_RST_B_STS;
> +	}
> +
> +	ssusb_check_clocks(ssusb, check_clk);
> +
> +	/* after all clocks are stable */
> +	toggle_opstate(ssusb);
> +}
> +
> +static void switch_port_to_device(struct ssusb_mtk *ssusb)
> +{
> +	u32 check_clk = 0;
> +
> +	dev_dbg(ssusb->dev, "%s\n", __func__);
> +
> +	ssusb_port0_switch(ssusb, USB2_PORT, false);
> +
> +	if (ssusb->otg_switch.is_u3_drd) {
> +		ssusb_port0_switch(ssusb, USB3_PORT, false);
> +		check_clk = SSUSB_U3_MAC_RST_B_STS;
> +	}
> +
> +	ssusb_check_clocks(ssusb, check_clk);
> +}
> +
> +int ssusb_set_vbus(struct otg_switch_mtk *otg_sx, int is_on)
> +{
> +	struct ssusb_mtk *ssusb =
> +		container_of(otg_sx, struct ssusb_mtk, otg_switch);
> +	struct regulator *vbus = otg_sx->vbus;
> +	int ret;
> +
> +	/* vbus is optional */
> +	if (!vbus)
> +		return 0;
> +
> +	dev_dbg(ssusb->dev, "%s: turn %s\n", __func__, is_on ? "on" : "off");
> +
> +	if (is_on) {
> +		ret = regulator_enable(vbus);
> +		if (ret) {
> +			dev_err(ssusb->dev, "vbus regulator enable failed\n");
> +			return ret;
> +		}
> +	} else {
> +		regulator_disable(vbus);
> +	}
> +
> +	return 0;
> +}
> +
> +/*
> + * switch to host: -> MTU3_VBUS_OFF --> MTU3_ID_GROUND
> + * switch to device: -> MTU3_ID_FLOAT --> MTU3_VBUS_VALID
> + */
> +static void ssusb_set_mailbox(struct otg_switch_mtk *otg_sx,
> +	enum mtu3_vbus_id_state status)
> +{
> +	struct ssusb_mtk *ssusb =
> +		container_of(otg_sx, struct ssusb_mtk, otg_switch);
> +	struct mtu3 *mtu = ssusb->u3d;
> +
> +	dev_dbg(ssusb->dev, "mailbox state(%d)\n", status);
> +
> +	switch (status) {
> +	case MTU3_ID_GROUND:
> +		switch_port_to_host(ssusb);
> +		ssusb_set_vbus(otg_sx, 1);
> +		ssusb->is_host = true;
> +		break;
> +	case MTU3_ID_FLOAT:
> +		ssusb->is_host = false;
> +		ssusb_set_vbus(otg_sx, 0);
> +		switch_port_to_device(ssusb);
> +		break;
> +	case MTU3_VBUS_OFF:
> +		mtu3_stop(mtu);
> +		pm_relax(ssusb->dev);
> +		break;
> +	case MTU3_VBUS_VALID:
> +		/* avoid suspend when works as device */
> +		pm_stay_awake(ssusb->dev);
> +		mtu3_start(mtu);
> +		break;
> +	default:
> +		dev_err(ssusb->dev, "invalid state\n");
> +	}
> +}
> +
> +static int ssusb_id_notifier(struct notifier_block *nb,
> +	unsigned long event, void *ptr)
> +{
> +	struct otg_switch_mtk *otg_sx =
> +		container_of(nb, struct otg_switch_mtk, id_nb);
> +
> +	if (event)
> +		ssusb_set_mailbox(otg_sx, MTU3_ID_GROUND);
> +	else
> +		ssusb_set_mailbox(otg_sx, MTU3_ID_FLOAT);
> +
> +	return NOTIFY_DONE;
> +}
> +
> +static int ssusb_vbus_notifier(struct notifier_block *nb,
> +	unsigned long event, void *ptr)
> +{
> +	struct otg_switch_mtk *otg_sx =
> +		container_of(nb, struct otg_switch_mtk, vbus_nb);
> +
> +	if (event)
> +		ssusb_set_mailbox(otg_sx, MTU3_VBUS_VALID);
> +	else
> +		ssusb_set_mailbox(otg_sx, MTU3_VBUS_OFF);
> +
> +	return NOTIFY_DONE;
> +}
> +
> +static int ssusb_extcon_register(struct otg_switch_mtk *otg_sx)
> +{
> +	struct ssusb_mtk *ssusb =
> +		container_of(otg_sx, struct ssusb_mtk, otg_switch);
> +	struct extcon_dev *edev = otg_sx->edev;
> +	int ret;
> +
> +	/* extcon is optional */
> +	if (!edev)
> +		return 0;
> +
> +	otg_sx->vbus_nb.notifier_call = ssusb_vbus_notifier;
> +	ret = extcon_register_notifier(edev, EXTCON_USB,
> +					&otg_sx->vbus_nb);
> +	if (ret < 0)
> +		dev_err(ssusb->dev, "failed to register notifier for USB\n");
> +
> +	otg_sx->id_nb.notifier_call = ssusb_id_notifier;
> +	ret = extcon_register_notifier(edev, EXTCON_USB_HOST,
> +					&otg_sx->id_nb);
> +	if (ret < 0)
> +		dev_err(ssusb->dev, "failed to register notifier for USB-HOST\n");
> +
> +	dev_dbg(ssusb->dev, "EXTCON_USB: %d, EXTCON_USB_HOST: %d\n",
> +		extcon_get_cable_state_(edev, EXTCON_USB),
> +		extcon_get_cable_state_(edev, EXTCON_USB_HOST));
> +
> +	/* default as host, switch to device mode if needed */
> +	if (extcon_get_cable_state_(edev, EXTCON_USB_HOST) == false)
> +		ssusb_set_mailbox(otg_sx, MTU3_ID_FLOAT);
> +	if (extcon_get_cable_state_(edev, EXTCON_USB) == true)
> +		ssusb_set_mailbox(otg_sx, MTU3_VBUS_VALID);
> +
> +	return 0;
> +}
> +
> +static void extcon_register_dwork(struct work_struct *work)
> +{
> +	struct delayed_work *dwork = to_delayed_work(work);
> +	struct otg_switch_mtk *otg_sx =
> +	    container_of(dwork, struct otg_switch_mtk, extcon_reg_dwork);
> +
> +	ssusb_extcon_register(otg_sx);
> +}
> +
> +/*
> + * We provide an interface via debugfs to switch between host and device modes
> + * depending on user input.
> + * This is useful in special cases, such as uses TYPE-A receptacle but also
> + * wants to support dual-role mode.
> + * It generates cable state changes by pulling up/down IDPIN and
> + * notifies driver to switch mode by "extcon-usb-gpio".
> + * NOTE: when use MICRO receptacle, should not enable this interface.
> + */
> +static void ssusb_mode_manual_switch(struct ssusb_mtk *ssusb, int to_host)
> +{
> +	struct otg_switch_mtk *otg_sx = &ssusb->otg_switch;
> +
> +	if (to_host)
> +		pinctrl_select_state(otg_sx->id_pinctrl, otg_sx->id_ground);
> +	else
> +		pinctrl_select_state(otg_sx->id_pinctrl, otg_sx->id_float);
> +}
> +
> +
> +static int ssusb_mode_show(struct seq_file *sf, void *unused)
> +{
> +	struct ssusb_mtk *ssusb = sf->private;
> +
> +	seq_printf(sf, "current mode: %s(%s drd)\n(echo device/host)\n",
> +		ssusb->is_host ? "host" : "device",
> +		ssusb->otg_switch.manual_drd_enabled ? "manual" : "auto");
> +
> +	return 0;
> +}
> +
> +static int ssusb_mode_open(struct inode *inode, struct file *file)
> +{
> +	return single_open(file, ssusb_mode_show, inode->i_private);
> +}
> +
> +static ssize_t ssusb_mode_write(struct file *file,
> +	const char __user *ubuf, size_t count, loff_t *ppos)
> +{
> +	struct seq_file *sf = file->private_data;
> +	struct ssusb_mtk *ssusb = sf->private;
> +	char buf[16];
> +
> +	if (copy_from_user(&buf, ubuf, min_t(size_t, sizeof(buf) - 1, count)))
> +		return -EFAULT;
> +
> +	if (!strncmp(buf, "host", 4) && !ssusb->is_host) {
> +		ssusb_mode_manual_switch(ssusb, 1);
> +	} else if (!strncmp(buf, "device", 6) && ssusb->is_host) {
> +		ssusb_mode_manual_switch(ssusb, 0);
> +	} else {
> +		dev_err(ssusb->dev, "wrong or duplicated setting\n");
> +		return -EINVAL;
> +	}
> +
> +	return count;
> +}
> +
> +static const struct file_operations ssusb_mode_fops = {
> +	.open = ssusb_mode_open,
> +	.write = ssusb_mode_write,
> +	.read = seq_read,
> +	.llseek = seq_lseek,
> +	.release = single_release,
> +};
> +
> +static void ssusb_debugfs_init(struct ssusb_mtk *ssusb)
> +{
> +	struct dentry *root;
> +	struct dentry *file;
> +
> +	root = debugfs_create_dir(dev_name(ssusb->dev), usb_debug_root);
> +	if (IS_ERR_OR_NULL(root)) {
> +		if (!root)
> +			dev_err(ssusb->dev, "create debugfs root failed\n");
> +		return;
> +	}
> +	ssusb->dbgfs_root = root;
> +
> +	file = debugfs_create_file("mode", S_IRUGO | S_IWUSR, root,
> +			ssusb, &ssusb_mode_fops);
> +	if (!file)
> +		dev_dbg(ssusb->dev, "create debugfs mode failed\n");
> +}
> +
> +static void ssusb_debugfs_exit(struct ssusb_mtk *ssusb)
> +{
> +	debugfs_remove_recursive(ssusb->dbgfs_root);
> +}
> +
> +int ssusb_otg_switch_init(struct ssusb_mtk *ssusb)
> +{
> +	struct otg_switch_mtk *otg_sx = &ssusb->otg_switch;
> +
> +	INIT_DELAYED_WORK(&otg_sx->extcon_reg_dwork, extcon_register_dwork);
> +
> +	if (otg_sx->manual_drd_enabled)
> +		ssusb_debugfs_init(ssusb);
> +
> +	/* It is enough to delay 1s for waiting for host initialization */
> +	schedule_delayed_work(&otg_sx->extcon_reg_dwork, HZ);
> +
> +	return 0;
> +}
> +
> +void ssusb_otg_switch_exit(struct ssusb_mtk *ssusb)
> +{
> +	struct otg_switch_mtk *otg_sx = &ssusb->otg_switch;
> +
> +	cancel_delayed_work(&otg_sx->extcon_reg_dwork);
> +
> +	if (otg_sx->edev) {
> +		extcon_unregister_notifier(otg_sx->edev,
> +			EXTCON_USB, &otg_sx->vbus_nb);
> +		extcon_unregister_notifier(otg_sx->edev,
> +			EXTCON_USB_HOST, &otg_sx->id_nb);
> +	}
> +
> +	if (otg_sx->manual_drd_enabled)
> +		ssusb_debugfs_exit(ssusb);
> +}
> diff --git a/drivers/usb/mtu3/mtu3_dr.h b/drivers/usb/mtu3/mtu3_dr.h
> new file mode 100644
> index 0000000..9b228b5
> --- /dev/null
> +++ b/drivers/usb/mtu3/mtu3_dr.h
> @@ -0,0 +1,108 @@
> +/*
> + * mtu3_dr.h - dual role switch and host glue layer header
> + *
> + * Copyright (C) 2016 MediaTek Inc.
> + *
> + * Author: Chunfeng Yun <chunfeng.yun@mediatek.com>
> + *
> + * This software is licensed under the terms of the GNU General Public
> + * License version 2, as published by the Free Software Foundation, and
> + * may be copied, distributed, and modified under those terms.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + * GNU General Public License for more details.
> + *
> + */
> +
> +#ifndef _MTU3_DR_H_
> +#define _MTU3_DR_H_
> +
> +#if IS_ENABLED(CONFIG_USB_MTU3_HOST) || IS_ENABLED(CONFIG_USB_MTU3_DUAL_ROLE)
> +
> +int ssusb_host_init(struct ssusb_mtk *ssusb, struct device_node *parent_dn);
> +void ssusb_host_exit(struct ssusb_mtk *ssusb);
> +int ssusb_wakeup_of_property_parse(struct ssusb_mtk *ssusb,
> +				struct device_node *dn);
> +int ssusb_host_enable(struct ssusb_mtk *ssusb);
> +int ssusb_host_disable(struct ssusb_mtk *ssusb, bool suspend);
> +int ssusb_wakeup_enable(struct ssusb_mtk *ssusb);
> +void ssusb_wakeup_disable(struct ssusb_mtk *ssusb);
> +
> +#else
> +
> +static inline int ssusb_host_init(struct ssusb_mtk *ssusb,
> +
> +	struct device_node *parent_dn)
> +{
> +	return 0;
> +}
> +
> +static inline void ssusb_host_exit(struct ssusb_mtk *ssusb)
> +{}
> +
> +static inline int ssusb_wakeup_of_property_parse(
> +	struct ssusb_mtk *ssusb, struct device_node *dn)
> +{
> +	return 0;
> +}
> +
> +static inline int ssusb_host_enable(struct ssusb_mtk *ssusb)
> +{
> +	return 0;
> +}
> +
> +static inline int ssusb_host_disable(struct ssusb_mtk *ssusb, bool suspend)
> +{
> +	return 0;
> +}
> +
> +static inline int ssusb_wakeup_enable(struct ssusb_mtk *ssusb)
> +{
> +	return 0;
> +}
> +
> +static inline void ssusb_wakeup_disable(struct ssusb_mtk *ssusb)
> +{}
> +
> +#endif
> +
> +
> +#if IS_ENABLED(CONFIG_USB_MTU3_GADGET) || IS_ENABLED(CONFIG_USB_MTU3_DUAL_ROLE)
> +int ssusb_gadget_init(struct ssusb_mtk *ssusb);
> +void ssusb_gadget_exit(struct ssusb_mtk *ssusb);
> +#else
> +static inline int ssusb_gadget_init(struct ssusb_mtk *ssusb)
> +{
> +	return 0;
> +}
> +
> +static inline void ssusb_gadget_exit(struct ssusb_mtk *ssusb)
> +{}
> +#endif
> +
> +
> +#if IS_ENABLED(CONFIG_USB_MTU3_DUAL_ROLE)
> +int ssusb_otg_switch_init(struct ssusb_mtk *ssusb);
> +void ssusb_otg_switch_exit(struct ssusb_mtk *ssusb);
> +int ssusb_set_vbus(struct otg_switch_mtk *otg_sx, int is_on);
> +
> +#else
> +
> +static inline int ssusb_otg_switch_init(struct ssusb_mtk *ssusb)
> +{
> +	return 0;
> +}
> +
> +static inline void ssusb_otg_switch_exit(struct ssusb_mtk *ssusb)
> +{}
> +
> +static inline int ssusb_set_vbus(struct otg_switch_mtk *otg_sx, int is_on)
> +{
> +	return 0;
> +}
> +
> +#endif
> +
> +#endif		/* _MTU3_DR_H_ */
> diff --git a/drivers/usb/mtu3/mtu3_gadget.c b/drivers/usb/mtu3/mtu3_gadget.c
> new file mode 100644
> index 0000000..b88064f
> --- /dev/null
> +++ b/drivers/usb/mtu3/mtu3_gadget.c
> @@ -0,0 +1,731 @@
> +/*
> + * mtu3_gadget.c - MediaTek usb3 DRD peripheral support
> + *
> + * Copyright (C) 2016 MediaTek Inc.
> + *
> + * Author: Chunfeng Yun <chunfeng.yun@mediatek.com>
> + *
> + * This software is licensed under the terms of the GNU General Public
> + * License version 2, as published by the Free Software Foundation, and
> + * may be copied, distributed, and modified under those terms.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + * GNU General Public License for more details.
> + *
> + */
> +
> +#include "mtu3.h"
> +
> +void mtu3_req_complete(struct mtu3_ep *mep,
> +		     struct usb_request *req, int status)
> +__releases(mep->mtu->lock)
> +__acquires(mep->mtu->lock)
> +{
> +	struct mtu3_request *mreq;
> +	struct mtu3 *mtu;
> +	int busy = mep->busy;
> +
> +	mreq = to_mtu3_request(req);
> +	list_del(&mreq->list);
> +	if (mreq->request.status == -EINPROGRESS)
> +		mreq->request.status = status;
> +
> +	mtu = mreq->mtu;
> +	mep->busy = 1;
> +	spin_unlock(&mtu->lock);
> +
> +	/* ep0 makes use of PIO, needn't unmap it */
> +	if (mep->epnum)
> +		usb_gadget_unmap_request(&mtu->g, req, mep->is_in);
> +
> +	dev_dbg(mtu->dev, "%s complete req: %p, sts %d, %d/%d\n", mep->name,
> +		req, req->status, mreq->request.actual, mreq->request.length);
> +
> +	usb_gadget_giveback_request(&mep->ep, &mreq->request);
> +
> +	spin_lock(&mtu->lock);
> +	mep->busy = busy;
> +}
> +
> +static void nuke(struct mtu3_ep *mep, const int status)
> +{
> +	struct mtu3_request *mreq = NULL;
> +
> +	mep->busy = 1;
> +	if (list_empty(&mep->req_list))
> +		return;
> +
> +	dev_dbg(mep->mtu->dev, "abort %s's req: sts %d\n", mep->name, status);
> +
> +	/* exclude EP0 */
> +	if (mep->epnum)
> +		mtu3_qmu_flush(mep);
> +
> +	while (!list_empty(&mep->req_list)) {
> +		mreq = list_first_entry(&mep->req_list,
> +					struct mtu3_request, list);
> +		mtu3_req_complete(mep, &mreq->request, status);
> +	}
> +}
> +
> +static int mtu3_ep_enable(struct mtu3_ep *mep)
> +{
> +	const struct usb_endpoint_descriptor *desc;
> +	const struct usb_ss_ep_comp_descriptor *comp_desc;
> +	struct mtu3 *mtu = mep->mtu;
> +	u32 interval = 0;
> +	u32 mult = 0;
> +	u32 burst = 0;
> +	int max_packet;
> +	int ret;
> +
> +	desc = mep->desc;
> +	comp_desc = mep->comp_desc;
> +	mep->type = usb_endpoint_type(desc);
> +	max_packet = usb_endpoint_maxp(desc);
> +	mep->maxp = max_packet & GENMASK(10, 0);
> +
> +	switch (mtu->g.speed) {
> +	case USB_SPEED_SUPER:
> +		if (usb_endpoint_xfer_int(desc) ||
> +				usb_endpoint_xfer_isoc(desc)) {
> +			interval = desc->bInterval;
> +			interval = clamp_val(interval, 1, 16) - 1;
> +			if (usb_endpoint_xfer_isoc(desc) && comp_desc)
> +				mult = comp_desc->bmAttributes;
> +		}
> +		if (comp_desc)
> +			burst = comp_desc->bMaxBurst;
> +
> +		break;
> +	case USB_SPEED_HIGH:
> +		if (usb_endpoint_xfer_isoc(desc) ||
> +				usb_endpoint_xfer_int(desc)) {
> +			interval = desc->bInterval;
> +			interval = clamp_val(interval, 1, 16) - 1;
> +			burst = (max_packet & GENMASK(12, 11)) >> 11;
> +		}
> +		break;
> +	default:
> +		break; /*others are ignored */
> +	}
> +
> +	dev_dbg(mtu->dev, "%s maxp:%d, interval:%d, burst:%d, mult:%d\n",
> +		__func__, mep->maxp, interval, burst, mult);
> +
> +	mep->ep.maxpacket = mep->maxp;
> +	mep->ep.desc = desc;
> +	mep->ep.comp_desc = comp_desc;
> +
> +	/* slot mainly affects bulk/isoc transfer, so ignore int */
> +	mep->slot = usb_endpoint_xfer_int(desc) ? 0 : mtu->slot;
> +
> +	ret = mtu3_config_ep(mtu, mep, interval, burst, mult);
> +	if (ret < 0)
> +		return ret;
> +
> +	ret = mtu3_gpd_ring_alloc(mep);
> +	if (ret < 0) {
> +		mtu3_deconfig_ep(mtu, mep);
> +		return ret;
> +	}
> +
> +	mtu3_qmu_start(mep);
> +
> +	return 0;
> +}
> +
> +static int mtu3_ep_disable(struct mtu3_ep *mep)
> +{
> +	struct mtu3 *mtu = mep->mtu;
> +
> +	mtu3_qmu_stop(mep);
> +
> +	/* abort all pending requests */
> +	nuke(mep, -ESHUTDOWN);
> +	mtu3_deconfig_ep(mtu, mep);
> +	mtu3_gpd_ring_free(mep);
> +
> +	mep->desc = NULL;
> +	mep->ep.desc = NULL;
> +	mep->comp_desc = NULL;
> +	mep->type = 0;
> +	mep->flags = 0;
> +
> +	return 0;
> +}
> +
> +static int mtu3_gadget_ep_enable(struct usb_ep *ep,
> +		const struct usb_endpoint_descriptor *desc)
> +{
> +	struct mtu3_ep *mep;
> +	struct mtu3 *mtu;
> +	unsigned long flags;
> +	int ret = -EINVAL;
> +
> +	if (!ep || !desc || desc->bDescriptorType != USB_DT_ENDPOINT) {
> +		pr_debug("%s invalid parameters\n", __func__);
> +		return -EINVAL;
> +	}
> +
> +	if (!desc->wMaxPacketSize) {
> +		pr_debug("%s missing wMaxPacketSize\n", __func__);
> +		return -EINVAL;
> +	}
> +	mep = to_mtu3_ep(ep);
> +	mtu = mep->mtu;
> +
> +	/* check ep number and direction against endpoint */
> +	if (usb_endpoint_num(desc) != mep->epnum)
> +		return -EINVAL;
> +
> +	if (!!usb_endpoint_dir_in(desc) ^ !!mep->is_in)
> +		return -EINVAL;
> +
> +	dev_dbg(mtu->dev, "%s %s\n", __func__, ep->name);
> +
> +	if (mep->flags & MTU3_EP_ENABLED) {
> +		dev_WARN_ONCE(mtu->dev, true, "%s is already enabled\n",
> +				mep->name);
> +		return 0;
> +	}
> +
> +	spin_lock_irqsave(&mtu->lock, flags);
> +	mep->desc = desc;
> +	mep->comp_desc = ep->comp_desc;
> +
> +	ret = mtu3_ep_enable(mep);
> +	if (ret)
> +		goto error;
> +
> +	mep->busy = 0;
> +	mep->wedged = 0;
> +	mep->flags |= MTU3_EP_ENABLED;
> +	mtu->active_ep++;
> +
> +error:
> +	spin_unlock_irqrestore(&mtu->lock, flags);
> +
> +	dev_dbg(mtu->dev, "%s active_ep=%d\n", __func__, mtu->active_ep);
> +
> +	return ret;
> +}
> +
> +static int mtu3_gadget_ep_disable(struct usb_ep *ep)
> +{
> +	struct mtu3_ep *mep = to_mtu3_ep(ep);
> +	struct mtu3 *mtu = mep->mtu;
> +	unsigned long flags;
> +
> +	dev_dbg(mtu->dev, "%s %s\n", __func__, mep->name);
> +
> +	if (!(mep->flags & MTU3_EP_ENABLED)) {
> +		dev_warn(mtu->dev, "%s is already disabled\n", mep->name);
> +		return 0;
> +	}
> +
> +	spin_lock_irqsave(&mtu->lock, flags);
> +	mtu3_ep_disable(mep);
> +	mep->flags &= ~MTU3_EP_ENABLED;
> +	mtu->active_ep--;
> +	spin_unlock_irqrestore(&(mtu->lock), flags);
> +
> +	dev_dbg(mtu->dev, "%s active_ep=%d, mtu3 is_active=%d\n",
> +		__func__, mtu->active_ep, mtu->is_active);
> +
> +	return 0;
> +}
> +
> +struct usb_request *mtu3_alloc_request(struct usb_ep *ep, gfp_t gfp_flags)
> +{
> +	struct mtu3_ep *mep = to_mtu3_ep(ep);
> +	struct mtu3_request *mreq;
> +
> +	mreq = kzalloc(sizeof(*mreq), gfp_flags);
> +	if (!mreq)
> +		return NULL;
> +
> +	mreq->request.dma = DMA_ADDR_INVALID;
> +	mreq->epnum = mep->epnum;
> +	mreq->mep = mep;
> +
> +	return &mreq->request;
> +}
> +
> +void mtu3_free_request(struct usb_ep *ep, struct usb_request *req)
> +{
> +	kfree(to_mtu3_request(req));
> +}
> +
> +static int mtu3_gadget_queue(struct usb_ep *ep,
> +		struct usb_request *req, gfp_t gfp_flags)
> +{
> +	struct mtu3_ep *mep;
> +	struct mtu3_request *mreq;
> +	struct mtu3 *mtu;
> +	unsigned long flags;
> +	int ret = 0;
> +
> +	if (!ep || !req)
> +		return -EINVAL;
> +
> +	if (!req->buf)
> +		return -ENODATA;
> +
> +	mep = to_mtu3_ep(ep);
> +	mtu = mep->mtu;
> +	mreq = to_mtu3_request(req);
> +	mreq->mtu = mtu;
> +
> +	if (mreq->mep != mep)
> +		return -EINVAL;
> +
> +	dev_dbg(mtu->dev, "%s %s EP%d(%s), req=%p, maxp=%d, len#%d\n",
> +		__func__, mep->is_in ? "TX" : "RX", mreq->epnum, ep->name,
> +		mreq, ep->maxpacket, mreq->request.length);
> +
> +	if (req->length > GPD_BUF_SIZE) {
> +		dev_warn(mtu->dev,
> +			"req length > supported MAX:%d requested:%d\n",
> +			GPD_BUF_SIZE, req->length);
> +		return -EOPNOTSUPP;
> +	}
> +
> +	/* don't queue if the ep is down */
> +	if (!mep->desc) {
> +		dev_dbg(mtu->dev, "req=%p queued to %s while it's disabled\n",
> +			req, ep->name);
> +		return -ESHUTDOWN;
> +	}
> +
> +	mreq->request.actual = 0;
> +	mreq->request.status = -EINPROGRESS;
> +
> +	ret = usb_gadget_map_request(&mtu->g, req, mep->is_in);
> +	if (ret) {
> +		dev_err(mtu->dev, "dma mapping failed\n");
> +		return ret;
> +	}
> +
> +	spin_lock_irqsave(&mtu->lock, flags);
> +
> +	if (mtu3_prepare_transfer(mep)) {
> +		ret = -EAGAIN;
> +		goto error;
> +	}
> +
> +	list_add_tail(&mreq->list, &mep->req_list);
> +	mtu3_insert_gpd(mep, mreq);
> +	mtu3_qmu_resume(mep);
> +
> +error:
> +	spin_unlock_irqrestore(&mtu->lock, flags);
> +
> +	return ret;
> +}
> +
> +static int mtu3_gadget_dequeue(struct usb_ep *ep, struct usb_request *req)
> +{
> +	struct mtu3_ep *mep = to_mtu3_ep(ep);
> +	struct mtu3_request *mreq = to_mtu3_request(req);
> +	struct mtu3_request *r;
> +	unsigned long flags;
> +	int ret = 0;
> +	struct mtu3 *mtu = mep->mtu;
> +
> +	if (!ep || !req || mreq->mep != mep)
> +		return -EINVAL;
> +
> +	dev_dbg(mtu->dev, "%s : req=%p\n", __func__, req);
> +
> +	spin_lock_irqsave(&mtu->lock, flags);
> +
> +	list_for_each_entry(r, &mep->req_list, list) {
> +		if (r == mreq)
> +			break;
> +	}
> +	if (r != mreq) {
> +		dev_dbg(mtu->dev, "req=%p not queued to %s\n", req, ep->name);
> +		ret = -EINVAL;
> +		goto done;
> +	}
> +
> +	mtu3_qmu_flush(mep);  /* REVISIT: set BPS ?? */
> +	mtu3_req_complete(mep, req, -ECONNRESET);
> +	mtu3_qmu_start(mep);
> +
> +done:
> +	spin_unlock_irqrestore(&mtu->lock, flags);
> +
> +	return ret;
> +}
> +
> +/*
> + * Set or clear the halt bit of an EP.
> + * A halted EP won't TX/RX any data but will queue requests.
> + */
> +static int mtu3_gadget_ep_set_halt(struct usb_ep *ep, int value)
> +{
> +	struct mtu3_ep *mep = to_mtu3_ep(ep);
> +	struct mtu3 *mtu = mep->mtu;
> +	struct mtu3_request *mreq;
> +	unsigned long flags;
> +	int ret = 0;
> +
> +	if (!ep)
> +		return -EINVAL;
> +
> +	dev_dbg(mtu->dev, "%s : %s...", __func__, ep->name);
> +
> +	spin_lock_irqsave(&mtu->lock, flags);
> +
> +	if (mep->type == USB_ENDPOINT_XFER_ISOC) {
> +		ret = -EINVAL;
> +		goto done;
> +	}
> +
> +	mreq = next_request(mep);
> +	if (value) {
> +		/*
> +		 * If there is not request for TX-EP, QMU will not transfer
> +		 * data to TX-FIFO, so no need check whether TX-FIFO
> +		 * holds bytes or not here
> +		 */
> +		if (mreq) {
> +			dev_dbg(mtu->dev, "req in progress, cannot halt %s\n",
> +				ep->name);
> +			ret = -EAGAIN;
> +			goto done;
> +		}
> +	} else {
> +		mep->wedged = 0;
> +	}
> +
> +	dev_dbg(mtu->dev, "%s %s stall\n", ep->name, value ? "set" : "clear");
> +
> +	mtu3_ep_stall_set(mep, value);
> +
> +done:
> +	spin_unlock_irqrestore(&mtu->lock, flags);
> +
> +	return ret;
> +}
> +
> +/* Sets the halt feature with the clear requests ignored */
> +static int mtu3_gadget_ep_set_wedge(struct usb_ep *ep)
> +{
> +	struct mtu3_ep *mep = to_mtu3_ep(ep);
> +
> +	if (!ep)
> +		return -EINVAL;
> +
> +	mep->wedged = 1;
> +
> +	return usb_ep_set_halt(ep);
> +}
> +
> +static const struct usb_ep_ops mtu3_ep_ops = {
> +	.enable = mtu3_gadget_ep_enable,
> +	.disable = mtu3_gadget_ep_disable,
> +	.alloc_request = mtu3_alloc_request,
> +	.free_request = mtu3_free_request,
> +	.queue = mtu3_gadget_queue,
> +	.dequeue = mtu3_gadget_dequeue,
> +	.set_halt = mtu3_gadget_ep_set_halt,
> +	.set_wedge = mtu3_gadget_ep_set_wedge,
> +};
> +
> +static int mtu3_gadget_get_frame(struct usb_gadget *gadget)
> +{
> +	struct mtu3 *mtu = gadget_to_mtu3(gadget);
> +
> +	return (int)mtu3_readl(mtu->mac_base, U3D_USB20_FRAME_NUM);
> +}
> +
> +static int mtu3_gadget_wakeup(struct usb_gadget *gadget)
> +{
> +	struct mtu3 *mtu = gadget_to_mtu3(gadget);
> +	unsigned long flags;
> +
> +	dev_dbg(mtu->dev, "%s\n", __func__);
> +
> +	/* remote wakeup feature is not enabled by host */
> +	if (!mtu->may_wakeup)
> +		return  -EOPNOTSUPP;
> +
> +	spin_lock_irqsave(&mtu->lock, flags);
> +	if (mtu->g.speed == USB_SPEED_SUPER) {
> +		mtu3_setbits(mtu->mac_base, U3D_LINK_POWER_CONTROL, UX_EXIT);
> +	} else {
> +		mtu3_setbits(mtu->mac_base, U3D_POWER_MANAGEMENT, RESUME);
> +		spin_unlock_irqrestore(&mtu->lock, flags);
> +		usleep_range(10000, 11000);
> +		spin_lock_irqsave(&mtu->lock, flags);
> +		mtu3_clrbits(mtu->mac_base, U3D_POWER_MANAGEMENT, RESUME);
> +	}
> +	spin_unlock_irqrestore(&mtu->lock, flags);
> +	return 0;
> +}
> +
> +static int mtu3_gadget_set_self_powered(struct usb_gadget *gadget,
> +		int is_selfpowered)
> +{
> +	struct mtu3 *mtu = gadget_to_mtu3(gadget);
> +
> +	mtu->is_self_powered = !!is_selfpowered;
> +	return 0;
> +}
> +
> +static int mtu3_gadget_pullup(struct usb_gadget *gadget, int is_on)
> +{
> +	struct mtu3 *mtu = gadget_to_mtu3(gadget);
> +	unsigned long flags;
> +
> +	dev_dbg(mtu->dev, "%s (%s) for %sactive device\n", __func__,
> +		is_on ? "on" : "off", mtu->is_active ? "" : "in");
> +
> +	/* we'd rather not pullup unless the device is active. */
> +	spin_lock_irqsave(&mtu->lock, flags);
> +
> +	is_on = !!is_on;
> +	if (!mtu->is_active) {
> +		/* save it for mtu3_start() to process the request */
> +		mtu->softconnect = is_on;
> +	} else if (is_on != mtu->softconnect) {
> +		mtu->softconnect = is_on;
> +		mtu3_dev_on_off(mtu, is_on);
> +	}
> +
> +	spin_unlock_irqrestore(&mtu->lock, flags);
> +
> +	return 0;
> +}
> +
> +static int mtu3_gadget_start(struct usb_gadget *gadget,
> +		struct usb_gadget_driver *driver)
> +{
> +	struct mtu3 *mtu = gadget_to_mtu3(gadget);
> +	unsigned long flags;
> +
> +	if (mtu->gadget_driver) {
> +		dev_err(mtu->dev, "%s is already bound to %s\n",
> +			mtu->g.name, mtu->gadget_driver->driver.name);
> +		return -EBUSY;
> +	}
> +
> +	dev_dbg(mtu->dev, "bind driver %s\n", driver->function);
> +
> +	spin_lock_irqsave(&mtu->lock, flags);
> +
> +	mtu->softconnect = 0;
> +	mtu->gadget_driver = driver;
> +
> +	if (mtu->ssusb->dr_mode == USB_DR_MODE_PERIPHERAL)
> +		mtu3_start(mtu);
> +
> +	spin_unlock_irqrestore(&mtu->lock, flags);
> +
> +	return 0;
> +}
> +
> +static void stop_activity(struct mtu3 *mtu)
> +{
> +	struct usb_gadget_driver *driver = mtu->gadget_driver;
> +	int i;
> +
> +	/* don't disconnect if it's not connected */
> +	if (mtu->g.speed == USB_SPEED_UNKNOWN)
> +		driver = NULL;
> +	else
> +		mtu->g.speed = USB_SPEED_UNKNOWN;
> +
> +	/* deactivate the hardware */
> +	if (mtu->softconnect) {
> +		mtu->softconnect = 0;
> +		mtu3_dev_on_off(mtu, 0);
> +	}
> +
> +	/*
> +	 * killing any outstanding requests will quiesce the driver;
> +	 * then report disconnect
> +	 */
> +	nuke(mtu->ep0, -ESHUTDOWN);
> +	for (i = 1; i < mtu->num_eps; i++) {
> +		nuke(mtu->in_eps + i, -ESHUTDOWN);
> +		nuke(mtu->out_eps + i, -ESHUTDOWN);
> +	}
> +
> +	if (driver) {
> +		spin_unlock(&mtu->lock);
> +		driver->disconnect(&mtu->g);
> +		spin_lock(&mtu->lock);
> +	}
> +}
> +
> +static int mtu3_gadget_stop(struct usb_gadget *g)
> +{
> +	struct mtu3 *mtu = gadget_to_mtu3(g);
> +	unsigned long flags;
> +
> +	dev_dbg(mtu->dev, "%s\n", __func__);
> +
> +	spin_lock_irqsave(&mtu->lock, flags);
> +
> +	stop_activity(mtu);
> +	mtu->gadget_driver = NULL;
> +
> +	if (mtu->ssusb->dr_mode == USB_DR_MODE_PERIPHERAL)
> +		mtu3_stop(mtu);
> +
> +	spin_unlock_irqrestore(&mtu->lock, flags);
> +
> +	return 0;
> +}
> +
> +static const struct usb_gadget_ops mtu3_gadget_ops = {
> +	.get_frame = mtu3_gadget_get_frame,
> +	.wakeup = mtu3_gadget_wakeup,
> +	.set_selfpowered = mtu3_gadget_set_self_powered,
> +	.pullup = mtu3_gadget_pullup,
> +	.udc_start = mtu3_gadget_start,
> +	.udc_stop = mtu3_gadget_stop,
> +};
> +
> +static void init_hw_ep(struct mtu3 *mtu, struct mtu3_ep *mep,
> +		u32 epnum, u32 is_in)
> +{
> +	mep->epnum = epnum;
> +	mep->mtu = mtu;
> +	mep->is_in = is_in;
> +
> +	INIT_LIST_HEAD(&mep->req_list);
> +
> +	sprintf(mep->name, "ep%d%s", epnum,
> +		!epnum ? "" : (is_in ? "in" : "out"));
> +
> +	mep->ep.name = mep->name;
> +	INIT_LIST_HEAD(&mep->ep.ep_list);
> +
> +	/* initialize maxpacket as SS */
> +	if (!epnum) {
> +		usb_ep_set_maxpacket_limit(&mep->ep, 512);
> +		mep->ep.caps.type_control = true;
> +		mep->ep.ops = &mtu3_ep0_ops;
> +		mtu->g.ep0 = &mep->ep;
> +	} else {
> +		usb_ep_set_maxpacket_limit(&mep->ep, 1024);
> +		mep->ep.caps.type_iso = true;
> +		mep->ep.caps.type_bulk = true;
> +		mep->ep.caps.type_int = true;
> +		mep->ep.ops = &mtu3_ep_ops;
> +		list_add_tail(&mep->ep.ep_list, &mtu->g.ep_list);
> +	}
> +
> +	dev_dbg(mtu->dev, "%s, name=%s, maxp=%d\n", __func__, mep->ep.name,
> +		 mep->ep.maxpacket);
> +
> +	if (!epnum) {
> +		mep->ep.caps.dir_in = true;
> +		mep->ep.caps.dir_out = true;
> +	} else if (is_in) {
> +		mep->ep.caps.dir_in = true;
> +	} else {
> +		mep->ep.caps.dir_out = true;
> +	}
> +}
> +
> +static void mtu3_gadget_init_eps(struct mtu3 *mtu)
> +{
> +	u8 epnum;
> +
> +	/* initialize endpoint list just once */
> +	INIT_LIST_HEAD(&(mtu->g.ep_list));
> +
> +	dev_dbg(mtu->dev, "%s num_eps(1 for a pair of tx&rx ep)=%d\n",
> +		__func__, mtu->num_eps);
> +
> +	init_hw_ep(mtu, mtu->ep0, 0, 0);
> +	for (epnum = 1; epnum < mtu->num_eps; epnum++) {
> +		init_hw_ep(mtu, mtu->in_eps + epnum, epnum, 1);
> +		init_hw_ep(mtu, mtu->out_eps + epnum, epnum, 0);
> +	}
> +}
> +
> +int mtu3_gadget_setup(struct mtu3 *mtu)
> +{
> +	int ret;
> +
> +	mtu->g.ops = &mtu3_gadget_ops;
> +	mtu->g.max_speed = mtu->max_speed;
> +	mtu->g.speed = USB_SPEED_UNKNOWN;
> +	mtu->g.sg_supported = 0;
> +	mtu->g.name = MTU3_DRIVER_NAME;
> +	mtu->is_active = 0;
> +
> +	mtu3_gadget_init_eps(mtu);
> +
> +	ret = usb_add_gadget_udc(mtu->dev, &mtu->g);
> +	if (ret) {
> +		dev_err(mtu->dev, "failed to register udc\n");
> +		return ret;
> +	}
> +
> +	usb_gadget_set_state(&mtu->g, USB_STATE_NOTATTACHED);
> +
> +	return 0;
> +}
> +
> +void mtu3_gadget_cleanup(struct mtu3 *mtu)
> +{
> +	usb_del_gadget_udc(&mtu->g);
> +}
> +
> +void mtu3_gadget_resume(struct mtu3 *mtu)
> +{
> +	dev_dbg(mtu->dev, "gadget RESUME\n");
> +	if (mtu->gadget_driver && mtu->gadget_driver->resume) {
> +		spin_unlock(&mtu->lock);
> +		mtu->gadget_driver->resume(&mtu->g);
> +		spin_lock(&mtu->lock);
> +	}
> +}
> +
> +/* called when SOF packets stop for 3+ msec or enters U3 */
> +void mtu3_gadget_suspend(struct mtu3 *mtu)
> +{
> +	dev_dbg(mtu->dev, "gadget SUSPEND\n");
> +	if (mtu->gadget_driver && mtu->gadget_driver->suspend) {
> +		spin_unlock(&mtu->lock);
> +		mtu->gadget_driver->suspend(&mtu->g);
> +		spin_lock(&mtu->lock);
> +	}
> +}
> +
> +/* called when VBUS drops below session threshold, and in other cases */
> +void mtu3_gadget_disconnect(struct mtu3 *mtu)
> +{
> +	dev_dbg(mtu->dev, "gadget DISCONNECT\n");
> +	if (mtu->gadget_driver && mtu->gadget_driver->disconnect) {
> +		spin_unlock(&mtu->lock);
> +		mtu->gadget_driver->disconnect(&mtu->g);
> +		spin_lock(&mtu->lock);
> +	}
> +
> +	usb_gadget_set_state(&mtu->g, USB_STATE_NOTATTACHED);
> +}
> +
> +void mtu3_gadget_reset(struct mtu3 *mtu)
> +{
> +	dev_dbg(mtu->dev, "gadget RESET\n");
> +
> +	/* report disconnect, if we didn't flush EP state */
> +	if (mtu->g.speed != USB_SPEED_UNKNOWN)
> +		mtu3_gadget_disconnect(mtu);
> +
> +	mtu->address = 0;
> +	mtu->ep0_state = MU3D_EP0_STATE_SETUP;
> +	mtu->may_wakeup = 0;
> +}
> +
> diff --git a/drivers/usb/mtu3/mtu3_gadget_ep0.c b/drivers/usb/mtu3/mtu3_gadget_ep0.c
> new file mode 100644
> index 0000000..9803afd
> --- /dev/null
> +++ b/drivers/usb/mtu3/mtu3_gadget_ep0.c
> @@ -0,0 +1,883 @@
> +/*
> + * mtu3_gadget_ep0.c - MediaTek USB3 DRD peripheral driver ep0 handling
> + *
> + * Copyright (c) 2016 MediaTek Inc.
> + *
> + * Author:  Chunfeng.Yun <chunfeng.yun@mediatek.com>
> + *
> + * This software is licensed under the terms of the GNU General Public
> + * License version 2, as published by the Free Software Foundation, and
> + * may be copied, distributed, and modified under those terms.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + * GNU General Public License for more details.
> + *
> + */
> +
> +#include "mtu3.h"
> +
> +/* ep0 is always mtu3->in_eps[0] */
> +#define	next_ep0_request(mtu)	next_request((mtu)->ep0)
> +
> +/* for high speed test mode; see USB 2.0 spec 7.1.20 */
> +static const u8 mtu3_test_packet[53] = {
> +	/* implicit SYNC then DATA0 to start */
> +
> +	/* JKJKJKJK x9 */
> +	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
> +	/* JJKKJJKK x8 */
> +	0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
> +	/* JJJJKKKK x8 */
> +	0xee, 0xee, 0xee, 0xee, 0xee, 0xee, 0xee, 0xee,
> +	/* JJJJJJJKKKKKKK x8 */
> +	0xfe, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
> +	/* JJJJJJJK x8 */
> +	0x7f, 0xbf, 0xdf, 0xef, 0xf7, 0xfb, 0xfd,
> +	/* JKKKKKKK x10, JK */
> +	0xfc, 0x7e, 0xbf, 0xdf, 0xef, 0xf7, 0xfb, 0xfd, 0x7e,
> +	/* implicit CRC16 then EOP to end */
> +};
> +
> +static char *decode_ep0_state(struct mtu3 *mtu)
> +{
> +	switch (mtu->ep0_state) {
> +	case MU3D_EP0_STATE_SETUP:
> +		return "SETUP";
> +	case MU3D_EP0_STATE_TX:
> +		return "IN";
> +	case MU3D_EP0_STATE_RX:
> +		return "OUT";
> +	case MU3D_EP0_STATE_TX_END:
> +		return "TX-END";
> +	case MU3D_EP0_STATE_STALL:
> +		return "STALL";
> +	default:
> +		return "??";
> +	}
> +}
> +
> +static void ep0_req_giveback(struct mtu3 *mtu, struct usb_request *req)
> +{
> +	mtu3_req_complete(mtu->ep0, req, 0);
> +}
> +
> +static int
> +forward_to_driver(struct mtu3 *mtu, const struct usb_ctrlrequest *setup)
> +__releases(mtu->lock)
> +__acquires(mtu->lock)
> +{
> +	int ret;
> +
> +	if (!mtu->gadget_driver)
> +		return -EOPNOTSUPP;
> +
> +	spin_unlock(&mtu->lock);
> +	ret = mtu->gadget_driver->setup(&mtu->g, setup);
> +	spin_lock(&mtu->lock);
> +
> +	dev_dbg(mtu->dev, "%s ret %d\n", __func__, ret);
> +	return ret;
> +}
> +
> +static void ep0_write_fifo(struct mtu3_ep *mep, const u8 *src, u16 len)
> +{
> +	void __iomem *fifo = mep->mtu->mac_base + U3D_FIFO0;
> +	u16 index = 0;
> +
> +	dev_dbg(mep->mtu->dev, "%s: ep%din, len=%d, buf=%p\n",
> +		__func__, mep->epnum, len, src);
> +
> +	if (len >= 4) {
> +		iowrite32_rep(fifo, src, len >> 2);
> +		index = len & ~0x03;
> +	}
> +	if (len & 0x02) {
> +		writew(*(u16 *)&src[index], fifo);
> +		index += 2;
> +	}
> +	if (len & 0x01)
> +		writeb(src[index], fifo);
> +}
> +
> +static void ep0_read_fifo(struct mtu3_ep *mep, u8 *dst, u16 len)
> +{
> +	void __iomem *fifo = mep->mtu->mac_base + U3D_FIFO0;
> +	u32 value;
> +	u16 index = 0;
> +
> +	dev_dbg(mep->mtu->dev, "%s: ep%dout len=%d buf=%p\n",
> +		 __func__, mep->epnum, len, dst);
> +
> +	if (len >= 4) {
> +		ioread32_rep(fifo, dst, len >> 2);
> +		index = len & ~0x03;
> +	}
> +	if (len & 0x3) {
> +		value = readl(fifo);
> +		memcpy(&dst[index], &value, len & 0x3);
> +	}
> +
> +}
> +
> +static void ep0_load_test_packet(struct mtu3 *mtu)
> +{
> +	/*
> +	 * because the length of test packet is less than max packet of HS ep0,
> +	 * write it into fifo directly.
> +	 */
> +	ep0_write_fifo(mtu->ep0, mtu3_test_packet, sizeof(mtu3_test_packet));
> +}
> +
> +/*
> + * A. send STALL for setup transfer without data stage:
> + *		set SENDSTALL and SETUPPKTRDY at the same time;
> + * B. send STALL for other cases:
> + *		set SENDSTALL only.
> + */
> +static void ep0_stall_set(struct mtu3_ep *mep0, bool set, u32 pktrdy)
> +{
> +	struct mtu3 *mtu = mep0->mtu;
> +	void __iomem *mbase = mtu->mac_base;
> +	u32 csr;
> +
> +	/* EP0_SENTSTALL is W1C */
> +	csr = mtu3_readl(mbase, U3D_EP0CSR) & EP0_W1C_BITS;
> +	if (set)
> +		csr |= EP0_SENDSTALL | pktrdy;
> +	else
> +		csr = (csr & ~EP0_SENDSTALL) | EP0_SENTSTALL;
> +	mtu3_writel(mtu->mac_base, U3D_EP0CSR, csr);
> +
> +	mtu->ep0_state = MU3D_EP0_STATE_SETUP;
> +
> +	dev_dbg(mtu->dev, "ep0: %s STALL, ep0_state: %s\n",
> +		set ? "SEND" : "CLEAR", decode_ep0_state(mtu));
> +}
> +
> +static int ep0_queue(struct mtu3_ep *mep0, struct mtu3_request *mreq);
> +
> +static void ep0_dummy_complete(struct usb_ep *ep, struct usb_request *req)
> +{}
> +
> +static void ep0_set_sel_complete(struct usb_ep *ep, struct usb_request *req)
> +{
> +	struct mtu3_request *mreq;
> +	struct mtu3 *mtu;
> +	struct usb_set_sel_req sel;
> +
> +	memcpy(&sel, req->buf, sizeof(sel));
> +
> +	mreq = to_mtu3_request(req);
> +	mtu = mreq->mtu;
> +	dev_dbg(mtu->dev, "u1sel:%d, u1pel:%d, u2sel:%d, u2pel:%d\n",
> +		sel.u1_sel, sel.u1_pel, sel.u2_sel, sel.u2_pel);
> +}
> +
> +/* queue data stage to handle 6 byte SET_SEL request */
> +static int ep0_set_sel(struct mtu3 *mtu, struct usb_ctrlrequest *setup)
> +{
> +	int ret;
> +	u16 length = le16_to_cpu(setup->wLength);
> +
> +	if (unlikely(length != 6)) {
> +		dev_err(mtu->dev, "%s wrong wLength:%d\n",
> +			__func__, length);
> +		return -EINVAL;
> +	}
> +
> +	mtu->ep0_req.mep = mtu->ep0;
> +	mtu->ep0_req.request.length = 6;
> +	mtu->ep0_req.request.buf = mtu->setup_buf;
> +	mtu->ep0_req.request.complete = ep0_set_sel_complete;
> +	ret = ep0_queue(mtu->ep0, &mtu->ep0_req);
> +
> +	return ret < 0 ? ret : 1;
> +}
> +
> +static int
> +ep0_get_status(struct mtu3 *mtu, const struct usb_ctrlrequest *setup)
> +{
> +	struct mtu3_ep *mep = NULL;
> +	int handled = 1;
> +	u8 result[2] = {0, 0};
> +	u8 epnum = 0;
> +	int is_in;
> +
> +	switch (setup->bRequestType & USB_RECIP_MASK) {
> +	case USB_RECIP_DEVICE:
> +		result[0] = mtu->is_self_powered << USB_DEVICE_SELF_POWERED;
> +		result[0] |= mtu->may_wakeup << USB_DEVICE_REMOTE_WAKEUP;
> +		/* superspeed only */
> +		if (mtu->g.speed == USB_SPEED_SUPER) {
> +			result[0] |= mtu->u1_enable << USB_DEV_STAT_U1_ENABLED;
> +			result[0] |= mtu->u2_enable << USB_DEV_STAT_U2_ENABLED;
> +		}
> +
> +		dev_dbg(mtu->dev, "%s result=%x, U1=%x, U2=%x\n", __func__,
> +			result[0], mtu->u1_enable, mtu->u2_enable);
> +
> +		break;
> +	case USB_RECIP_INTERFACE:
> +		break;
> +	case USB_RECIP_ENDPOINT:
> +		epnum = (u8) le16_to_cpu(setup->wIndex);
> +		is_in = epnum & USB_DIR_IN;
> +		epnum &= USB_ENDPOINT_NUMBER_MASK;
> +
> +		if (epnum >= mtu->num_eps) {
> +			handled = -EINVAL;
> +			break;
> +		}
> +		if (!epnum)
> +			break;
> +
> +		mep = (is_in ? mtu->in_eps : mtu->out_eps) + epnum;
> +		if (!mep->desc) {
> +			handled = -EINVAL;
> +			break;
> +		}
> +
> +		if (mep->flags & MTU3_EP_STALL)
> +			result[0] |= 1 << USB_ENDPOINT_HALT;
> +
> +		break;
> +	default:
> +		/* class, vendor, etc ... delegate */
> +		handled = 0;
> +		break;
> +	}
> +
> +	if (handled > 0) {
> +		int ret;
> +
> +		/* prepare a data stage for GET_STATUS */
> +		dev_dbg(mtu->dev, "get_status=%x\n", *(u16 *)result);
> +		memcpy(mtu->setup_buf, result, sizeof(result));
> +		mtu->ep0_req.mep = mtu->ep0;
> +		mtu->ep0_req.request.length = 2;
> +		mtu->ep0_req.request.buf = &mtu->setup_buf;
> +		mtu->ep0_req.request.complete = ep0_dummy_complete;
> +		ret = ep0_queue(mtu->ep0, &mtu->ep0_req);
> +		if (ret < 0)
> +			handled = ret;
> +	}
> +	return handled;
> +}
> +
> +static int handle_test_mode(struct mtu3 *mtu, struct usb_ctrlrequest *setup)
> +{
> +	void __iomem *mbase = mtu->mac_base;
> +	int handled = 1;
> +
> +	switch (le16_to_cpu(setup->wIndex) >> 8) {
> +	case TEST_J:
> +		dev_dbg(mtu->dev, "TEST_J\n");
> +		mtu->test_mode_nr = TEST_J_MODE;
> +		break;
> +	case TEST_K:
> +		dev_dbg(mtu->dev, "TEST_K\n");
> +		mtu->test_mode_nr = TEST_K_MODE;
> +		break;
> +	case TEST_SE0_NAK:
> +		dev_dbg(mtu->dev, "TEST_SE0_NAK\n");
> +		mtu->test_mode_nr = TEST_SE0_NAK_MODE;
> +		break;
> +	case TEST_PACKET:
> +		dev_dbg(mtu->dev, "TEST_PACKET\n");
> +		mtu->test_mode_nr = TEST_PACKET_MODE;
> +		break;
> +	default:
> +		handled = -EINVAL;
> +		goto out;
> +	}
> +
> +	mtu->test_mode = true;
> +
> +	/* no TX completion interrupt, and need restart platform after test */
> +	if (mtu->test_mode_nr == TEST_PACKET_MODE)
> +		ep0_load_test_packet(mtu);
> +
> +	mtu3_writel(mbase, U3D_USB2_TEST_MODE, mtu->test_mode_nr);
> +
> +	mtu->ep0_state = MU3D_EP0_STATE_SETUP;
> +
> +out:
> +	return handled;
> +}
> +
> +static int ep0_handle_feature_dev(struct mtu3 *mtu,
> +		struct usb_ctrlrequest *setup, bool set)
> +{
> +	void __iomem *mbase = mtu->mac_base;
> +	int handled = -EINVAL;
> +	u32 lpc;
> +
> +	switch (le16_to_cpu(setup->wValue)) {
> +	case USB_DEVICE_REMOTE_WAKEUP:
> +		mtu->may_wakeup = !!set;
> +		handled = 1;
> +		break;
> +	case USB_DEVICE_TEST_MODE:
> +		if (!set || (mtu->g.speed != USB_SPEED_HIGH) ||
> +			(le16_to_cpu(setup->wIndex) & 0xff))
> +			break;
> +
> +		handled = handle_test_mode(mtu, setup);
> +		break;
> +	case USB_DEVICE_U1_ENABLE:
> +		if (mtu->g.speed != USB_SPEED_SUPER ||
> +			mtu->g.state != USB_STATE_CONFIGURED)
> +			break;
> +
> +		lpc = mtu3_readl(mbase, U3D_LINK_POWER_CONTROL);
> +		if (set)
> +			lpc |= SW_U1_ACCEPT_ENABLE;
> +		else
> +			lpc &= ~SW_U1_ACCEPT_ENABLE;
> +		mtu3_writel(mbase, U3D_LINK_POWER_CONTROL, lpc);
> +
> +		mtu->u1_enable = !!set;
> +		handled = 1;
> +		break;
> +	case USB_DEVICE_U2_ENABLE:
> +		if (mtu->g.speed != USB_SPEED_SUPER ||
> +			mtu->g.state != USB_STATE_CONFIGURED)
> +			break;
> +
> +		lpc = mtu3_readl(mbase, U3D_LINK_POWER_CONTROL);
> +		if (set)
> +			lpc |= SW_U2_ACCEPT_ENABLE;
> +		else
> +			lpc &= ~SW_U2_ACCEPT_ENABLE;
> +		mtu3_writel(mbase, U3D_LINK_POWER_CONTROL, lpc);
> +
> +		mtu->u2_enable = !!set;
> +		handled = 1;
> +		break;
> +	default:
> +		handled = -EINVAL;
> +		break;
> +	}
> +	return handled;
> +}
> +
> +static int ep0_handle_feature(struct mtu3 *mtu,
> +		struct usb_ctrlrequest *setup, bool set)
> +{
> +	struct mtu3_ep *mep;
> +	int handled = -EINVAL;
> +	int is_in;
> +	u16 value;
> +	u16 index;
> +	u8 epnum;
> +
> +	value = le16_to_cpu(setup->wValue);
> +	index = le16_to_cpu(setup->wIndex);
> +
> +	switch (setup->bRequestType & USB_RECIP_MASK) {
> +	case USB_RECIP_DEVICE:
> +		handled = ep0_handle_feature_dev(mtu, setup, set);
> +		break;
> +	case USB_RECIP_INTERFACE:
> +		/* superspeed only */
> +		if ((value == USB_INTRF_FUNC_SUSPEND)
> +			&& (mtu->g.speed == USB_SPEED_SUPER)) {
> +			/*
> +			 * forward the request because function drivers
> +			 * should handle it
> +			 */
> +			handled = 0;
> +		}
> +		break;
> +	case USB_RECIP_ENDPOINT:
> +		epnum = index & USB_ENDPOINT_NUMBER_MASK;
> +		if (epnum == 0 || epnum >= mtu->num_eps ||
> +			value != USB_ENDPOINT_HALT)
> +			break;
> +
> +		is_in = index & USB_DIR_IN;
> +		mep = (is_in ? mtu->in_eps : mtu->out_eps) + epnum;
> +		if (!mep->desc)
> +			break;
> +
> +		handled = 1;
> +		/* ignore request if endpoint is wedged */
> +		if (mep->wedged)
> +			break;
> +
> +		mtu3_ep_stall_set(mep, set);
> +		break;
> +	default:
> +		/* class, vendor, etc ... delegate */
> +		handled = 0;
> +		break;
> +	}
> +	return handled;
> +}
> +
> +/*
> + * handle all control requests can be handled
> + * returns:
> + *	negative errno - error happened
> + *	zero - need delegate SETUP to gadget driver
> + *	positive - already handled
> + */
> +static int handle_standard_request(struct mtu3 *mtu,
> +			  struct usb_ctrlrequest *setup)
> +{
> +	void __iomem *mbase = mtu->mac_base;
> +	enum usb_device_state state = mtu->g.state;
> +	int handled = -EINVAL;
> +	u32 dev_conf;
> +	u16 value;
> +
> +	value = le16_to_cpu(setup->wValue);
> +
> +	/* the gadget driver handles everything except what we must handle */
> +	switch (setup->bRequest) {
> +	case USB_REQ_SET_ADDRESS:
> +		/* change it after the status stage */
> +		mtu->address = (u8) (value & 0x7f);
> +		dev_dbg(mtu->dev, "set address to 0x%x\n", mtu->address);
> +
> +		dev_conf = mtu3_readl(mbase, U3D_DEVICE_CONF);
> +		dev_conf &= ~DEV_ADDR_MSK;
> +		dev_conf |= DEV_ADDR(mtu->address);
> +		mtu3_writel(mbase, U3D_DEVICE_CONF, dev_conf);
> +
> +		if (mtu->address)
> +			usb_gadget_set_state(&mtu->g, USB_STATE_ADDRESS);
> +		else
> +			usb_gadget_set_state(&mtu->g, USB_STATE_DEFAULT);
> +
> +		handled = 1;
> +		break;
> +	case USB_REQ_SET_CONFIGURATION:
> +		if (state == USB_STATE_ADDRESS) {
> +			usb_gadget_set_state(&mtu->g,
> +					USB_STATE_CONFIGURED);
> +		} else if (state == USB_STATE_CONFIGURED) {
> +			/*
> +			 * USB2 spec sec 9.4.7, if wValue is 0 then dev
> +			 * is moved to addressed state
> +			 */
> +			if (!value)
> +				usb_gadget_set_state(&mtu->g,
> +						USB_STATE_ADDRESS);
> +		}
> +		handled = 0;
> +		break;
> +	case USB_REQ_CLEAR_FEATURE:
> +		handled = ep0_handle_feature(mtu, setup, 0);
> +		break;
> +	case USB_REQ_SET_FEATURE:
> +		handled = ep0_handle_feature(mtu, setup, 1);
> +		break;
> +	case USB_REQ_GET_STATUS:
> +		handled = ep0_get_status(mtu, setup);
> +		break;
> +	case USB_REQ_SET_SEL:
> +		handled = ep0_set_sel(mtu, setup);
> +		break;
> +	case USB_REQ_SET_ISOCH_DELAY:
> +		handled = 1;
> +		break;
> +	default:
> +		/* delegate SET_CONFIGURATION, etc */
> +		handled = 0;
> +	}
> +
> +	return handled;
> +}
> +
> +/* receive an data packet (OUT) */
> +static void ep0_rx_state(struct mtu3 *mtu)
> +{
> +	struct mtu3_request *mreq;
> +	struct usb_request *req;
> +	void __iomem *mbase = mtu->mac_base;
> +	u32 maxp;
> +	u32 csr;
> +	u16 count = 0;
> +
> +	dev_dbg(mtu->dev, "%s\n", __func__);
> +
> +	csr = mtu3_readl(mbase, U3D_EP0CSR) & EP0_W1C_BITS;
> +	mreq = next_ep0_request(mtu);
> +	req = &mreq->request;
> +
> +	/* read packet and ack; or stall because of gadget driver bug */
> +	if (req) {
> +		void *buf = req->buf + req->actual;
> +		unsigned int len = req->length - req->actual;
> +
> +		/* read the buffer */
> +		count = mtu3_readl(mbase, U3D_RXCOUNT0);
> +		if (count > len) {
> +			req->status = -EOVERFLOW;
> +			count = len;
> +		}
> +		ep0_read_fifo(mtu->ep0, buf, count);
> +		req->actual += count;
> +		csr |= EP0_RXPKTRDY;
> +
> +		maxp = mtu->g.ep0->maxpacket;
> +		if (count < maxp || req->actual == req->length) {
> +			mtu->ep0_state = MU3D_EP0_STATE_SETUP;
> +			dev_dbg(mtu->dev, "ep0 state: %s\n",
> +				decode_ep0_state(mtu));
> +
> +			csr |= EP0_DATAEND;
> +		} else {
> +			req = NULL;
> +		}
> +	} else {
> +		csr |= EP0_RXPKTRDY | EP0_SENDSTALL;
> +		dev_dbg(mtu->dev, "%s: SENDSTALL\n", __func__);
> +	}
> +
> +	mtu3_writel(mbase, U3D_EP0CSR, csr);
> +
> +	/* give back the request if have received all data */
> +	if (req)
> +		ep0_req_giveback(mtu, req);
> +
> +}
> +
> +/* transmitting to the host (IN) */
> +static void ep0_tx_state(struct mtu3 *mtu)
> +{
> +	struct mtu3_request *mreq = next_ep0_request(mtu);
> +	struct usb_request *req;
> +	u32 csr;
> +	u8 *src;
> +	u8 count;
> +	u32 maxp;
> +
> +	dev_dbg(mtu->dev, "%s\n", __func__);
> +
> +	if (!mreq)
> +		return;
> +
> +	maxp = mtu->g.ep0->maxpacket;
> +	req = &mreq->request;
> +
> +	/* load the data */
> +	src = (u8 *)req->buf + req->actual;
> +	count = min(maxp, req->length - req->actual);
> +	if (count)
> +		ep0_write_fifo(mtu->ep0, src, count);
> +
> +	dev_dbg(mtu->dev, "%s act=%d, len=%d, cnt=%d, maxp=%d zero=%d\n",
> +		 __func__, req->actual, req->length, count, maxp, req->zero);
> +
> +	req->actual += count;
> +
> +	if ((count < maxp)
> +		|| ((req->actual == req->length) && !req->zero))
> +		mtu->ep0_state = MU3D_EP0_STATE_TX_END;
> +
> +	/* send it out, triggering a "txpktrdy cleared" irq */
> +	csr = mtu3_readl(mtu->mac_base, U3D_EP0CSR) & EP0_W1C_BITS;
> +	mtu3_writel(mtu->mac_base, U3D_EP0CSR, csr | EP0_TXPKTRDY);
> +
> +	dev_dbg(mtu->dev, "%s ep0csr=0x%x\n", __func__,
> +		mtu3_readl(mtu->mac_base, U3D_EP0CSR));
> +}
> +
> +static void ep0_read_setup(struct mtu3 *mtu, struct usb_ctrlrequest *setup)
> +{
> +	struct mtu3_request *mreq;
> +	u32 count;
> +	u32 csr;
> +
> +	csr = mtu3_readl(mtu->mac_base, U3D_EP0CSR) & EP0_W1C_BITS;
> +	count = mtu3_readl(mtu->mac_base, U3D_RXCOUNT0);
> +
> +	ep0_read_fifo(mtu->ep0, (u8 *)setup, count);
> +
> +	dev_dbg(mtu->dev, "SETUP req%02x.%02x v%04x i%04x l%04x\n",
> +		 setup->bRequestType, setup->bRequest,
> +		 le16_to_cpu(setup->wValue), le16_to_cpu(setup->wIndex),
> +		 le16_to_cpu(setup->wLength));
> +
> +	/* clean up any leftover transfers */
> +	mreq = next_ep0_request(mtu);
> +	if (mreq)
> +		ep0_req_giveback(mtu, &mreq->request);
> +
> +	if (le16_to_cpu(setup->wLength) == 0) {
> +		;	/* no data stage, nothing to do */
> +	} else if (setup->bRequestType & USB_DIR_IN) {
> +		mtu3_writel(mtu->mac_base, U3D_EP0CSR,
> +			csr | EP0_SETUPPKTRDY | EP0_DPHTX);
> +		mtu->ep0_state = MU3D_EP0_STATE_TX;
> +	} else {
> +		mtu3_writel(mtu->mac_base, U3D_EP0CSR,
> +			(csr | EP0_SETUPPKTRDY) & (~EP0_DPHTX));
> +		mtu->ep0_state = MU3D_EP0_STATE_RX;
> +	}
> +}
> +
> +static int ep0_handle_setup(struct mtu3 *mtu)
> +__releases(mtu->lock)
> +__acquires(mtu->lock)
> +{
> +	struct usb_ctrlrequest setup;
> +	struct mtu3_request *mreq;
> +	void __iomem *mbase = mtu->mac_base;
> +	int handled = 0;
> +
> +	ep0_read_setup(mtu, &setup);
> +
> +	if ((setup.bRequestType & USB_TYPE_MASK) == USB_TYPE_STANDARD)
> +		handled = handle_standard_request(mtu, &setup);
> +
> +	dev_dbg(mtu->dev, "handled %d, ep0_state: %s\n",
> +		 handled, decode_ep0_state(mtu));
> +
> +	if (handled < 0)
> +		goto stall;
> +	else if (handled > 0)
> +		goto finish;
> +
> +	handled = forward_to_driver(mtu, &setup);
> +	if (handled < 0) {
> +stall:
> +		dev_err(mtu->dev, "%s stall (%d)\n", __func__, handled);
> +
> +		ep0_stall_set(mtu->ep0, true,
> +			le16_to_cpu(setup.wLength) ? 0 : EP0_SETUPPKTRDY);
> +
> +		return 0;
> +	}
> +
> +finish:
> +	if (mtu->test_mode) {
> +		;	/* nothing to do */
> +	} else if (le16_to_cpu(setup.wLength) == 0) { /* no data stage */
> +
> +		mtu3_writel(mbase, U3D_EP0CSR,
> +			(mtu3_readl(mbase, U3D_EP0CSR) & EP0_W1C_BITS)
> +			| EP0_SETUPPKTRDY | EP0_DATAEND);
> +
> +		/* complete zlp request directly */
> +		mreq = next_ep0_request(mtu);
> +		if (mreq && !mreq->request.length)
> +			ep0_req_giveback(mtu, &mreq->request);
> +	}
> +
> +	return 0;
> +}
> +
> +irqreturn_t mtu3_ep0_isr(struct mtu3 *mtu)
> +{
> +	void __iomem *mbase = mtu->mac_base;
> +	struct mtu3_request *mreq;
> +	u32 int_status;
> +	irqreturn_t ret = IRQ_NONE;
> +	u32 csr;
> +	u32 len;
> +
> +	int_status = mtu3_readl(mbase, U3D_EPISR);
> +	int_status &= mtu3_readl(mbase, U3D_EPIER);
> +	mtu3_writel(mbase, U3D_EPISR, int_status); /* W1C */
> +
> +	/* only handle ep0's */
> +	if (!(int_status & EP0ISR))
> +		return IRQ_NONE;
> +
> +	csr = mtu3_readl(mbase, U3D_EP0CSR);
> +
> +	dev_dbg(mtu->dev, "%s csr=0x%x\n", __func__, csr);
> +
> +	/* we sent a stall.. need to clear it now.. */
> +	if (csr & EP0_SENTSTALL) {
> +		ep0_stall_set(mtu->ep0, false, 0);
> +		csr = mtu3_readl(mbase, U3D_EP0CSR);
> +		ret = IRQ_HANDLED;
> +	}
> +	dev_dbg(mtu->dev, "ep0_state: %s\n", decode_ep0_state(mtu));
> +
> +	switch (mtu->ep0_state) {
> +	case MU3D_EP0_STATE_TX:
> +		/* irq on clearing txpktrdy */
> +		if ((csr & EP0_FIFOFULL) == 0) {
> +			ep0_tx_state(mtu);
> +			ret = IRQ_HANDLED;
> +		}
> +		break;
> +	case MU3D_EP0_STATE_RX:
> +		/* irq on set rxpktrdy */
> +		if (csr & EP0_RXPKTRDY) {
> +			ep0_rx_state(mtu);
> +			ret = IRQ_HANDLED;
> +		}
> +		break;
> +	case MU3D_EP0_STATE_TX_END:
> +		mtu3_writel(mbase, U3D_EP0CSR,
> +			(csr & EP0_W1C_BITS) | EP0_DATAEND);
> +
> +		mreq = next_ep0_request(mtu);
> +		if (mreq)
> +			ep0_req_giveback(mtu, &mreq->request);
> +
> +		mtu->ep0_state = MU3D_EP0_STATE_SETUP;
> +		ret = IRQ_HANDLED;
> +		dev_dbg(mtu->dev, "ep0_state: %s\n", decode_ep0_state(mtu));
> +		break;
> +	case MU3D_EP0_STATE_SETUP:
> +		if (!(csr & EP0_SETUPPKTRDY))
> +			break;
> +
> +		len = mtu3_readl(mbase, U3D_RXCOUNT0);
> +		if (len != 8) {
> +			dev_err(mtu->dev, "SETUP packet len %d != 8 ?\n", len);
> +			break;
> +		}
> +
> +		ep0_handle_setup(mtu);
> +		ret = IRQ_HANDLED;
> +		break;
> +	default:
> +		/* can't happen */
> +		ep0_stall_set(mtu->ep0, true, 0);
> +		WARN_ON(1);
> +		break;
> +	}
> +
> +	return ret;
> +}
> +
> +
> +static int mtu3_ep0_enable(struct usb_ep *ep,
> +	const struct usb_endpoint_descriptor *desc)
> +{
> +	/* always enabled */
> +	return -EINVAL;
> +}
> +
> +static int mtu3_ep0_disable(struct usb_ep *ep)
> +{
> +	/* always enabled */
> +	return -EINVAL;
> +}
> +
> +static int ep0_queue(struct mtu3_ep *mep, struct mtu3_request *mreq)
> +{
> +	struct mtu3 *mtu = mep->mtu;
> +
> +	mreq->mtu = mtu;
> +	mreq->request.actual = 0;
> +	mreq->request.status = -EINPROGRESS;
> +
> +	dev_dbg(mtu->dev, "%s %s (ep0_state: %s), len#%d\n", __func__,
> +		mep->name, decode_ep0_state(mtu), mreq->request.length);
> +
> +	if (!list_empty(&mep->req_list))
> +		return -EBUSY;
> +
> +	switch (mtu->ep0_state) {
> +	case MU3D_EP0_STATE_SETUP:
> +	case MU3D_EP0_STATE_RX:	/* control-OUT data */
> +	case MU3D_EP0_STATE_TX:	/* control-IN data */
> +		break;
> +	default:
> +		dev_err(mtu->dev, "%s, error in ep0 state %s\n", __func__,
> +			decode_ep0_state(mtu));
> +		return -EINVAL;
> +	}
> +
> +	list_add_tail(&mreq->list, &mep->req_list);
> +
> +	/* sequence #1, IN ... start writing the data */
> +	if (mtu->ep0_state == MU3D_EP0_STATE_TX)
> +		ep0_tx_state(mtu);
> +
> +	return 0;
> +}
> +
> +static int mtu3_ep0_queue(struct usb_ep *ep,
> +	struct usb_request *req, gfp_t gfp)
> +{
> +	struct mtu3_ep *mep;
> +	struct mtu3_request *mreq;
> +	struct mtu3 *mtu;
> +	unsigned long flags;
> +	int ret = 0;
> +
> +	if (!ep || !req)
> +		return -EINVAL;
> +
> +	mep = to_mtu3_ep(ep);
> +	mtu = mep->mtu;
> +	mreq = to_mtu3_request(req);
> +
> +	spin_lock_irqsave(&mtu->lock, flags);
> +	ret = ep0_queue(mep, mreq);
> +	spin_unlock_irqrestore(&mtu->lock, flags);
> +	return ret;
> +}
> +
> +static int mtu3_ep0_dequeue(struct usb_ep *ep, struct usb_request *req)
> +{
> +	/* we just won't support this */
> +	return -EINVAL;
> +}
> +
> +static int mtu3_ep0_halt(struct usb_ep *ep, int value)
> +{
> +	struct mtu3_ep *mep;
> +	struct mtu3 *mtu;
> +	unsigned long flags;
> +	int ret = 0;
> +
> +	if (!ep || !value)
> +		return -EINVAL;
> +
> +	mep = to_mtu3_ep(ep);
> +	mtu = mep->mtu;
> +
> +	dev_dbg(mtu->dev, "%s\n", __func__);
> +
> +	spin_lock_irqsave(&mtu->lock, flags);
> +
> +	if (!list_empty(&mep->req_list)) {
> +		ret = -EBUSY;
> +		goto cleanup;
> +	}
> +
> +	switch (mtu->ep0_state) {
> +	/*
> +	 * stalls are usually issued after parsing SETUP packet, either
> +	 * directly in irq context from setup() or else later.
> +	 */
> +	case MU3D_EP0_STATE_TX:
> +	case MU3D_EP0_STATE_TX_END:
> +	case MU3D_EP0_STATE_RX:
> +	case MU3D_EP0_STATE_SETUP:
> +		ep0_stall_set(mtu->ep0, true, 0);
> +		break;
> +	default:
> +		dev_dbg(mtu->dev, "ep0 can't halt in state %s\n",
> +			decode_ep0_state(mtu));
> +		ret = -EINVAL;
> +	}
> +
> +cleanup:
> +	spin_unlock_irqrestore(&mtu->lock, flags);
> +	return ret;
> +}
> +
> +const struct usb_ep_ops mtu3_ep0_ops = {
> +	.enable = mtu3_ep0_enable,
> +	.disable = mtu3_ep0_disable,
> +	.alloc_request = mtu3_alloc_request,
> +	.free_request = mtu3_free_request,
> +	.queue = mtu3_ep0_queue,
> +	.dequeue = mtu3_ep0_dequeue,
> +	.set_halt = mtu3_ep0_halt,
> +};
> +
> diff --git a/drivers/usb/mtu3/mtu3_host.c b/drivers/usb/mtu3/mtu3_host.c
> new file mode 100644
> index 0000000..cd4d010
> --- /dev/null
> +++ b/drivers/usb/mtu3/mtu3_host.c
> @@ -0,0 +1,294 @@
> +/*
> + * mtu3_dr.c - dual role switch and host glue layer
> + *
> + * Copyright (C) 2016 MediaTek Inc.
> + *
> + * Author: Chunfeng Yun <chunfeng.yun@mediatek.com>
> + *
> + * This software is licensed under the terms of the GNU General Public
> + * License version 2, as published by the Free Software Foundation, and
> + * may be copied, distributed, and modified under those terms.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + * GNU General Public License for more details.
> + *
> + */
> +
> +#include <linux/clk.h>
> +#include <linux/iopoll.h>
> +#include <linux/irq.h>
> +#include <linux/kernel.h>
> +#include <linux/mfd/syscon.h>
> +#include <linux/of_device.h>
> +#include <linux/regmap.h>
> +
> +#include "mtu3.h"
> +#include "mtu3_dr.h"
> +
> +#define PERI_WK_CTRL1		0x404
> +#define UWK_CTL1_IS_C(x)	(((x) & 0xf) << 26)
> +#define UWK_CTL1_IS_E		BIT(25)
> +#define UWK_CTL1_IDDIG_C(x)	(((x) & 0xf) << 11)  /* cycle debounce */
> +#define UWK_CTL1_IDDIG_E	BIT(10) /* enable debounce */
> +#define UWK_CTL1_IDDIG_P	BIT(9)  /* polarity */
> +#define UWK_CTL1_IS_P		BIT(6)  /* polarity for ip sleep */
> +
> +/*
> + * ip-sleep wakeup mode:
> + * all clocks can be turn off, but power domain should be kept on
> + */
> +static void ssusb_wakeup_ip_sleep_en(struct ssusb_mtk *ssusb)
> +{
> +	u32 tmp;
> +	struct regmap *pericfg = ssusb->pericfg;
> +
> +	regmap_read(pericfg, PERI_WK_CTRL1, &tmp);
> +	tmp &= ~UWK_CTL1_IS_P;
> +	tmp &= ~(UWK_CTL1_IS_C(0xf));
> +	tmp |= UWK_CTL1_IS_C(0x8);
> +	regmap_write(pericfg, PERI_WK_CTRL1, tmp);
> +	regmap_write(pericfg, PERI_WK_CTRL1, tmp | UWK_CTL1_IS_E);
> +
> +	regmap_read(pericfg, PERI_WK_CTRL1, &tmp);
> +	dev_dbg(ssusb->dev, "%s(): WK_CTRL1[P6,E25,C26:29]=%#x\n",
> +		__func__, tmp);
> +}
> +
> +static void ssusb_wakeup_ip_sleep_dis(struct ssusb_mtk *ssusb)
> +{
> +	u32 tmp;
> +
> +	regmap_read(ssusb->pericfg, PERI_WK_CTRL1, &tmp);
> +	tmp &= ~UWK_CTL1_IS_E;
> +	regmap_write(ssusb->pericfg, PERI_WK_CTRL1, tmp);
> +}
> +
> +int ssusb_wakeup_of_property_parse(struct ssusb_mtk *ssusb,
> +				struct device_node *dn)
> +{
> +	struct device *dev = ssusb->dev;
> +
> +	/*
> +	 * Wakeup function is optional, so it is not an error if this property
> +	 * does not exist, and in such case, no need to get relative
> +	 * properties anymore.
> +	 */
> +	ssusb->wakeup_en = of_property_read_bool(dn, "mediatek,enable-wakeup");
> +	if (!ssusb->wakeup_en)
> +		return 0;
> +
> +	ssusb->wk_deb_p0 = devm_clk_get(dev, "wakeup_deb_p0");
> +	if (IS_ERR(ssusb->wk_deb_p0)) {
> +		dev_err(dev, "fail to get wakeup_deb_p0\n");
> +		return PTR_ERR(ssusb->wk_deb_p0);
> +	}
> +
> +	if (of_property_read_bool(dn, "wakeup_deb_p1")) {
> +		ssusb->wk_deb_p1 = devm_clk_get(dev, "wakeup_deb_p1");
> +		if (IS_ERR(ssusb->wk_deb_p1)) {
> +			dev_err(dev, "fail to get wakeup_deb_p1\n");
> +			return PTR_ERR(ssusb->wk_deb_p1);
> +		}
> +	}
> +
> +	ssusb->pericfg = syscon_regmap_lookup_by_phandle(dn,
> +						"mediatek,syscon-wakeup");
> +	if (IS_ERR(ssusb->pericfg)) {
> +		dev_err(dev, "fail to get pericfg regs\n");
> +		return PTR_ERR(ssusb->pericfg);
> +	}
> +
> +	return 0;
> +}
> +
> +static int ssusb_wakeup_clks_enable(struct ssusb_mtk *ssusb)
> +{
> +	int ret;
> +
> +	ret = clk_prepare_enable(ssusb->wk_deb_p0);
> +	if (ret) {
> +		dev_err(ssusb->dev, "failed to enable wk_deb_p0\n");
> +		goto usb_p0_err;
> +	}
> +
> +	ret = clk_prepare_enable(ssusb->wk_deb_p1);
> +	if (ret) {
> +		dev_err(ssusb->dev, "failed to enable wk_deb_p1\n");
> +		goto usb_p1_err;
> +	}
> +
> +	return 0;
> +
> +usb_p1_err:
> +	clk_disable_unprepare(ssusb->wk_deb_p0);
> +usb_p0_err:
> +	return -EINVAL;
> +}
> +
> +static void ssusb_wakeup_clks_disable(struct ssusb_mtk *ssusb)
> +{
> +	clk_disable_unprepare(ssusb->wk_deb_p1);
> +	clk_disable_unprepare(ssusb->wk_deb_p0);
> +}
> +
> +static void host_ports_num_get(struct ssusb_mtk *ssusb)
> +{
> +	u32 xhci_cap;
> +
> +	xhci_cap = mtu3_readl(ssusb->ippc_base, U3D_SSUSB_IP_XHCI_CAP);
> +	ssusb->u2_ports = SSUSB_IP_XHCI_U2_PORT_NUM(xhci_cap);
> +	ssusb->u3_ports = SSUSB_IP_XHCI_U3_PORT_NUM(xhci_cap);
> +
> +	dev_dbg(ssusb->dev, "host - u2_ports:%d, u3_ports:%d\n",
> +		 ssusb->u2_ports, ssusb->u3_ports);
> +}
> +
> +/* only configure ports will be used later */
> +int ssusb_host_enable(struct ssusb_mtk *ssusb)
> +{
> +	void __iomem *ibase = ssusb->ippc_base;
> +	int num_u3p = ssusb->u3_ports;
> +	int num_u2p = ssusb->u2_ports;
> +	u32 check_clk;
> +	u32 value;
> +	int i;
> +
> +	/* power on host ip */
> +	mtu3_clrbits(ibase, U3D_SSUSB_IP_PW_CTRL1, SSUSB_IP_HOST_PDN);
> +
> +	/* power on and enable all u3 ports */
> +	for (i = 0; i < num_u3p; i++) {
> +		value = mtu3_readl(ibase, SSUSB_U3_CTRL(i));
> +		value &= ~(SSUSB_U3_PORT_PDN | SSUSB_U3_PORT_DIS);
> +		value |= SSUSB_U3_PORT_HOST_SEL;
> +		mtu3_writel(ibase, SSUSB_U3_CTRL(i), value);
> +	}
> +
> +	/* power on and enable all u2 ports */
> +	for (i = 0; i < num_u2p; i++) {
> +		value = mtu3_readl(ibase, SSUSB_U2_CTRL(i));
> +		value &= ~(SSUSB_U2_PORT_PDN | SSUSB_U2_PORT_DIS);
> +		value |= SSUSB_U2_PORT_HOST_SEL;
> +		mtu3_writel(ibase, SSUSB_U2_CTRL(i), value);
> +	}
> +
> +	check_clk = SSUSB_XHCI_RST_B_STS;
> +	if (num_u3p)
> +		check_clk = SSUSB_U3_MAC_RST_B_STS;
> +
> +	return ssusb_check_clocks(ssusb, check_clk);
> +}
> +
> +int ssusb_host_disable(struct ssusb_mtk *ssusb, bool suspend)
> +{
> +	void __iomem *ibase = ssusb->ippc_base;
> +	int num_u3p = ssusb->u3_ports;
> +	int num_u2p = ssusb->u2_ports;
> +	u32 value;
> +	int ret;
> +	int i;
> +
> +	/* power down and disable all u3 ports */
> +	for (i = 0; i < num_u3p; i++) {
> +		value = mtu3_readl(ibase, SSUSB_U3_CTRL(i));
> +		value |= SSUSB_U3_PORT_PDN;
> +		value |= suspend ? 0 : SSUSB_U3_PORT_DIS;
> +		mtu3_writel(ibase, SSUSB_U3_CTRL(i), value);
> +	}
> +
> +	/* power down and disable all u2 ports */
> +	for (i = 0; i < num_u2p; i++) {
> +		value = mtu3_readl(ibase, SSUSB_U2_CTRL(i));
> +		value |= SSUSB_U2_PORT_PDN;
> +		value |= suspend ? 0 : SSUSB_U2_PORT_DIS;
> +		mtu3_writel(ibase, SSUSB_U2_CTRL(i), value);
> +	}
> +
> +	/* power down host ip */
> +	mtu3_setbits(ibase, U3D_SSUSB_IP_PW_CTRL1, SSUSB_IP_HOST_PDN);
> +
> +	if (!suspend)
> +		return 0;
> +
> +	/* wait for host ip to sleep */
> +	ret = readl_poll_timeout(ibase + U3D_SSUSB_IP_PW_STS1, value,
> +			  (value & SSUSB_IP_SLEEP_STS), 100, 100000);
> +	if (ret)
> +		dev_err(ssusb->dev, "ip sleep failed!!!\n");
> +
> +	return ret;
> +}
> +
> +static void ssusb_host_setup(struct ssusb_mtk *ssusb)
> +{
> +	host_ports_num_get(ssusb);
> +
> +	/*
> +	 * power on host and power on/enable all ports
> +	 * if support OTG, gadget driver will switch port0 to device mode
> +	 */
> +	ssusb_host_enable(ssusb);
> +
> +	/* if port0 supports dual-role, works as host mode by default */
> +	ssusb_set_vbus(&ssusb->otg_switch, 1);
> +}
> +
> +static void ssusb_host_cleanup(struct ssusb_mtk *ssusb)
> +{
> +	if (ssusb->is_host)
> +		ssusb_set_vbus(&ssusb->otg_switch, 0);
> +
> +	ssusb_host_disable(ssusb, false);
> +}
> +
> +/*
> + * If host supports multiple ports, the VBUSes(5V) of ports except port0
> + * which supports OTG are better to be enabled by default in DTS.
> + * Because the host driver will keep link with devices attached when system
> + * enters suspend mode, so no need to control VBUSes after initialization.
> + */
> +int ssusb_host_init(struct ssusb_mtk *ssusb, struct device_node *parent_dn)
> +{
> +	struct device *parent_dev = ssusb->dev;
> +	int ret;
> +
> +	ssusb_host_setup(ssusb);
> +
> +	ret = of_platform_populate(parent_dn, NULL, NULL, parent_dev);
> +	if (ret) {
> +		dev_dbg(parent_dev, "failed to create child devices at %s\n",
> +				parent_dn->full_name);
> +		return ret;
> +	}
> +
> +	dev_info(parent_dev, "xHCI platform device register success...\n");
> +
> +	return 0;
> +}
> +
> +void ssusb_host_exit(struct ssusb_mtk *ssusb)
> +{
> +	of_platform_depopulate(ssusb->dev);
> +	ssusb_host_cleanup(ssusb);
> +}
> +
> +int ssusb_wakeup_enable(struct ssusb_mtk *ssusb)
> +{
> +	int ret = 0;
> +
> +	if (ssusb->wakeup_en) {
> +		ret = ssusb_wakeup_clks_enable(ssusb);
> +		ssusb_wakeup_ip_sleep_en(ssusb);
> +	}
> +	return ret;
> +}
> +
> +void ssusb_wakeup_disable(struct ssusb_mtk *ssusb)
> +{
> +	if (ssusb->wakeup_en) {
> +		ssusb_wakeup_ip_sleep_dis(ssusb);
> +		ssusb_wakeup_clks_disable(ssusb);
> +	}
> +}
> diff --git a/drivers/usb/mtu3/mtu3_hw_regs.h b/drivers/usb/mtu3/mtu3_hw_regs.h
> new file mode 100644
> index 0000000..2123672
> --- /dev/null
> +++ b/drivers/usb/mtu3/mtu3_hw_regs.h
> @@ -0,0 +1,473 @@
> +/*
> + * mtu3_hw_regs.h - MediaTek USB3 DRD register and field definitions
> + *
> + * Copyright (C) 2016 MediaTek Inc.
> + *
> + * Author: Chunfeng Yun <chunfeng.yun@mediatek.com>
> + *
> + * This software is licensed under the terms of the GNU General Public
> + * License version 2, as published by the Free Software Foundation, and
> + * may be copied, distributed, and modified under those terms.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + * GNU General Public License for more details.
> + *
> + */
> +
> +#ifndef _SSUSB_HW_REGS_H_
> +#define _SSUSB_HW_REGS_H_
> +
> +/* segment offset of MAC register */
> +#define SSUSB_DEV_BASE		0x0000
> +#define SSUSB_EPCTL_CSR_BASE	0x0800
> +#define SSUSB_USB3_MAC_CSR_BASE	0x1400
> +#define SSUSB_USB3_SYS_CSR_BASE	0x1400
> +#define SSUSB_USB2_CSR_BASE	0x2400
> +
> +/* IPPC register in Infra */
> +#define SSUSB_SIFSLV_IPPC_BASE	0x0000
> +
> +/* --------------- SSUSB_DEV REGISTER DEFINITION --------------- */
> +
> +#define U3D_LV1ISR		(SSUSB_DEV_BASE + 0x0000)
> +#define U3D_LV1IER		(SSUSB_DEV_BASE + 0x0004)
> +#define U3D_LV1IESR		(SSUSB_DEV_BASE + 0x0008)
> +#define U3D_LV1IECR		(SSUSB_DEV_BASE + 0x000C)
> +
> +#define U3D_EPISR		(SSUSB_DEV_BASE + 0x0080)
> +#define U3D_EPIER		(SSUSB_DEV_BASE + 0x0084)
> +#define U3D_EPIESR		(SSUSB_DEV_BASE + 0x0088)
> +#define U3D_EPIECR		(SSUSB_DEV_BASE + 0x008C)
> +
> +#define U3D_EP0CSR		(SSUSB_DEV_BASE + 0x0100)
> +#define U3D_RXCOUNT0		(SSUSB_DEV_BASE + 0x0108)
> +#define U3D_RESERVED		(SSUSB_DEV_BASE + 0x010C)
> +#define U3D_TX1CSR0		(SSUSB_DEV_BASE + 0x0110)
> +#define U3D_TX1CSR1		(SSUSB_DEV_BASE + 0x0114)
> +#define U3D_TX1CSR2		(SSUSB_DEV_BASE + 0x0118)
> +
> +#define U3D_RX1CSR0		(SSUSB_DEV_BASE + 0x0210)
> +#define U3D_RX1CSR1		(SSUSB_DEV_BASE + 0x0214)
> +#define U3D_RX1CSR2		(SSUSB_DEV_BASE + 0x0218)
> +
> +#define U3D_FIFO0		(SSUSB_DEV_BASE + 0x0300)
> +
> +#define U3D_QCR0		(SSUSB_DEV_BASE + 0x0400)
> +#define U3D_QCR1		(SSUSB_DEV_BASE + 0x0404)
> +#define U3D_QCR2		(SSUSB_DEV_BASE + 0x0408)
> +#define U3D_QCR3		(SSUSB_DEV_BASE + 0x040C)
> +
> +#define U3D_TXQCSR1		(SSUSB_DEV_BASE + 0x0510)
> +#define U3D_TXQSAR1		(SSUSB_DEV_BASE + 0x0514)
> +#define U3D_TXQCPR1		(SSUSB_DEV_BASE + 0x0518)
> +
> +#define U3D_RXQCSR1		(SSUSB_DEV_BASE + 0x0610)
> +#define U3D_RXQSAR1		(SSUSB_DEV_BASE + 0x0614)
> +#define U3D_RXQCPR1		(SSUSB_DEV_BASE + 0x0618)
> +#define U3D_RXQLDPR1		(SSUSB_DEV_BASE + 0x061C)
> +
> +#define U3D_QISAR0		(SSUSB_DEV_BASE + 0x0700)
> +#define U3D_QIER0		(SSUSB_DEV_BASE + 0x0704)
> +#define U3D_QIESR0		(SSUSB_DEV_BASE + 0x0708)
> +#define U3D_QIECR0		(SSUSB_DEV_BASE + 0x070C)
> +#define U3D_QISAR1		(SSUSB_DEV_BASE + 0x0710)
> +#define U3D_QIER1		(SSUSB_DEV_BASE + 0x0714)
> +#define U3D_QIESR1		(SSUSB_DEV_BASE + 0x0718)
> +#define U3D_QIECR1		(SSUSB_DEV_BASE + 0x071C)
> +
> +#define U3D_TQERRIR0		(SSUSB_DEV_BASE + 0x0780)
> +#define U3D_TQERRIER0		(SSUSB_DEV_BASE + 0x0784)
> +#define U3D_TQERRIESR0		(SSUSB_DEV_BASE + 0x0788)
> +#define U3D_TQERRIECR0		(SSUSB_DEV_BASE + 0x078C)
> +#define U3D_RQERRIR0		(SSUSB_DEV_BASE + 0x07C0)
> +#define U3D_RQERRIER0		(SSUSB_DEV_BASE + 0x07C4)
> +#define U3D_RQERRIESR0		(SSUSB_DEV_BASE + 0x07C8)
> +#define U3D_RQERRIECR0		(SSUSB_DEV_BASE + 0x07CC)
> +#define U3D_RQERRIR1		(SSUSB_DEV_BASE + 0x07D0)
> +#define U3D_RQERRIER1		(SSUSB_DEV_BASE + 0x07D4)
> +#define U3D_RQERRIESR1		(SSUSB_DEV_BASE + 0x07D8)
> +#define U3D_RQERRIECR1		(SSUSB_DEV_BASE + 0x07DC)
> +
> +#define U3D_CAP_EP0FFSZ		(SSUSB_DEV_BASE + 0x0C04)
> +#define U3D_CAP_EPNTXFFSZ	(SSUSB_DEV_BASE + 0x0C08)
> +#define U3D_CAP_EPNRXFFSZ	(SSUSB_DEV_BASE + 0x0C0C)
> +#define U3D_CAP_EPINFO		(SSUSB_DEV_BASE + 0x0C10)
> +#define U3D_MISC_CTRL		(SSUSB_DEV_BASE + 0x0C84)
> +
> +/*---------------- SSUSB_DEV FIELD DEFINITION ---------------*/
> +
> +/* U3D_LV1ISR */
> +#define EP_CTRL_INTR		BIT(5)
> +#define MAC2_INTR		BIT(4)
> +#define DMA_INTR		BIT(3)
> +#define MAC3_INTR		BIT(2)
> +#define QMU_INTR		BIT(1)
> +#define BMU_INTR		BIT(0)
> +
> +/* U3D_LV1IECR */
> +#define LV1IECR_MSK		GENMASK(31, 0)
> +
> +/* U3D_EPISR */
> +#define EPRISR(x)		(BIT(16) << (x))
> +#define EPTISR(x)		(BIT(0) << (x))
> +#define EP0ISR			BIT(0)
> +
> +/* U3D_EP0CSR */
> +#define EP0_SENDSTALL		BIT(25)
> +#define EP0_FIFOFULL		BIT(23)
> +#define EP0_SENTSTALL		BIT(22)
> +#define EP0_DPHTX		BIT(20)
> +#define EP0_DATAEND		BIT(19)
> +#define EP0_TXPKTRDY		BIT(18)
> +#define EP0_SETUPPKTRDY		BIT(17)
> +#define EP0_RXPKTRDY		BIT(16)
> +#define EP0_MAXPKTSZ_MSK	GENMASK(9, 0)
> +#define EP0_MAXPKTSZ(x)		((x) & EP0_MAXPKTSZ_MSK)
> +#define EP0_W1C_BITS	(~(EP0_RXPKTRDY | EP0_SETUPPKTRDY | EP0_SENTSTALL))
> +
> +/* U3D_TX1CSR0 */
> +#define TX_DMAREQEN		BIT(29)
> +#define TX_FIFOFULL		BIT(25)
> +#define TX_FIFOEMPTY		BIT(24)
> +#define TX_SENTSTALL		BIT(22)
> +#define TX_SENDSTALL		BIT(21)
> +#define TX_TXPKTRDY		BIT(16)
> +#define TX_TXMAXPKTSZ_MSK	GENMASK(10, 0)
> +#define TX_TXMAXPKTSZ(x)	((x) & TX_TXMAXPKTSZ_MSK)
> +#define TX_W1C_BITS		(~(TX_SENTSTALL))
> +
> +/* U3D_TX1CSR1 */
> +#define TX_MULT(x)		(((x) & 0x3) << 22)
> +#define TX_MAX_PKT(x)		(((x) & 0x3f) << 16)
> +#define TX_SLOT(x)		(((x) & 0x3f) << 8)
> +#define TX_TYPE(x)		(((x) & 0x3) << 4)
> +#define TX_SS_BURST(x)		(((x) & 0xf) << 0)
> +
> +/* for TX_TYPE & RX_TYPE */
> +#define TYPE_BULK		(0x0)
> +#define TYPE_INT		(0x1)
> +#define TYPE_ISO		(0x2)
> +#define TYPE_MASK		(0x3)
> +
> +/* U3D_TX1CSR2 */
> +#define TX_BINTERVAL(x)		(((x) & 0xff) << 24)
> +#define TX_FIFOSEGSIZE(x)	(((x) & 0xf) << 16)
> +#define TX_FIFOADDR(x)		(((x) & 0x1fff) << 0)
> +
> +/* U3D_RX1CSR0 */
> +#define RX_DMAREQEN		BIT(29)
> +#define RX_SENTSTALL		BIT(22)
> +#define RX_SENDSTALL		BIT(21)
> +#define RX_RXPKTRDY		BIT(16)
> +#define RX_RXMAXPKTSZ_MSK	GENMASK(10, 0)
> +#define RX_RXMAXPKTSZ(x)	((x) & RX_RXMAXPKTSZ_MSK)
> +#define RX_W1C_BITS		(~(RX_SENTSTALL | RX_RXPKTRDY))
> +
> +/* U3D_RX1CSR1 */
> +#define RX_MULT(x)		(((x) & 0x3) << 22)
> +#define RX_MAX_PKT(x)		(((x) & 0x3f) << 16)
> +#define RX_SLOT(x)		(((x) & 0x3f) << 8)
> +#define RX_TYPE(x)		(((x) & 0x3) << 4)
> +#define RX_SS_BURST(x)		(((x) & 0xf) << 0)
> +
> +/* U3D_RX1CSR2 */
> +#define RX_BINTERVAL(x)		(((x) & 0xff) << 24)
> +#define RX_FIFOSEGSIZE(x)	(((x) & 0xf) << 16)
> +#define RX_FIFOADDR(x)		(((x) & 0x1fff) << 0)
> +
> +/* U3D_QCR0 */
> +#define QMU_RX_CS_EN(x)		(BIT(16) << (x))
> +#define QMU_TX_CS_EN(x)		(BIT(0) << (x))
> +#define QMU_CS16B_EN		BIT(0)
> +
> +/* U3D_QCR1 */
> +#define QMU_TX_ZLP(x)		(BIT(0) << (x))
> +
> +/* U3D_QCR3 */
> +#define QMU_RX_COZ(x)		(BIT(16) << (x))
> +#define QMU_RX_ZLP(x)		(BIT(0) << (x))
> +
> +/* U3D_TXQCSR1 */
> +/* U3D_RXQCSR1 */
> +#define QMU_Q_ACTIVE		BIT(15)
> +#define QMU_Q_STOP		BIT(2)
> +#define QMU_Q_RESUME		BIT(1)
> +#define QMU_Q_START		BIT(0)
> +
> +/* U3D_QISAR0, U3D_QIER0, U3D_QIESR0, U3D_QIECR0 */
> +#define QMU_RX_DONE_INT(x)	(BIT(16) << (x))
> +#define QMU_TX_DONE_INT(x)	(BIT(0) << (x))
> +
> +/* U3D_QISAR1, U3D_QIER1, U3D_QIESR1, U3D_QIECR1 */
> +#define RXQ_ZLPERR_INT		BIT(20)
> +#define RXQ_LENERR_INT		BIT(18)
> +#define RXQ_CSERR_INT		BIT(17)
> +#define RXQ_EMPTY_INT		BIT(16)
> +#define TXQ_LENERR_INT		BIT(2)
> +#define TXQ_CSERR_INT		BIT(1)
> +#define TXQ_EMPTY_INT		BIT(0)
> +
> +/* U3D_TQERRIR0, U3D_TQERRIER0, U3D_TQERRIESR0, U3D_TQERRIECR0 */
> +#define QMU_TX_LEN_ERR(x)	(BIT(16) << (x))
> +#define QMU_TX_CS_ERR(x)	(BIT(0) << (x))
> +
> +/* U3D_RQERRIR0, U3D_RQERRIER0, U3D_RQERRIESR0, U3D_RQERRIECR0 */
> +#define QMU_RX_LEN_ERR(x)	(BIT(16) << (x))
> +#define QMU_RX_CS_ERR(x)	(BIT(0) << (x))
> +
> +/* U3D_RQERRIR1, U3D_RQERRIER1, U3D_RQERRIESR1, U3D_RQERRIECR1 */
> +#define QMU_RX_ZLP_ERR(n)	(BIT(16) << (n))
> +
> +/* U3D_CAP_EPINFO */
> +#define CAP_RX_EP_NUM(x)	(((x) >> 8) & 0x1f)
> +#define CAP_TX_EP_NUM(x)	((x) & 0x1f)
> +
> +/* U3D_MISC_CTRL */
> +#define VBUS_ON			BIT(1)
> +#define VBUS_FRC_EN		BIT(0)
> +
> +
> +/*---------------- SSUSB_EPCTL_CSR REGISTER DEFINITION ----------------*/
> +
> +#define U3D_DEVICE_CONF			(SSUSB_EPCTL_CSR_BASE + 0x0000)
> +#define U3D_EP_RST			(SSUSB_EPCTL_CSR_BASE + 0x0004)
> +
> +#define U3D_DEV_LINK_INTR_ENABLE	(SSUSB_EPCTL_CSR_BASE + 0x0050)
> +#define U3D_DEV_LINK_INTR		(SSUSB_EPCTL_CSR_BASE + 0x0054)
> +
> +/*---------------- SSUSB_EPCTL_CSR FIELD DEFINITION ----------------*/
> +
> +/* U3D_DEVICE_CONF */
> +#define DEV_ADDR_MSK		GENMASK(30, 24)
> +#define DEV_ADDR(x)		((0x7f & (x)) << 24)
> +#define HW_USB2_3_SEL		BIT(18)
> +#define SW_USB2_3_SEL_EN	BIT(17)
> +#define SW_USB2_3_SEL		BIT(16)
> +#define SSUSB_DEV_SPEED(x)	((x) & 0x7)
> +
> +/* U3D_EP_RST */
> +#define EP1_IN_RST		BIT(17)
> +#define EP1_OUT_RST		BIT(1)
> +#define EP_RST(is_in, epnum)	(((is_in) ? BIT(16) : BIT(0)) << (epnum))
> +#define EP0_RST			BIT(0)
> +
> +/* U3D_DEV_LINK_INTR_ENABLE */
> +/* U3D_DEV_LINK_INTR */
> +#define SSUSB_DEV_SPEED_CHG_INTR	BIT(0)
> +
> +
> +/*---------------- SSUSB_USB3_MAC_CSR REGISTER DEFINITION ----------------*/
> +
> +#define U3D_LTSSM_CTRL		(SSUSB_USB3_MAC_CSR_BASE + 0x0010)
> +#define U3D_USB3_CONFIG		(SSUSB_USB3_MAC_CSR_BASE + 0x001C)
> +
> +#define U3D_LTSSM_INTR_ENABLE	(SSUSB_USB3_MAC_CSR_BASE + 0x013C)
> +#define U3D_LTSSM_INTR		(SSUSB_USB3_MAC_CSR_BASE + 0x0140)
> +
> +/*---------------- SSUSB_USB3_MAC_CSR FIELD DEFINITION ----------------*/
> +
> +/* U3D_LTSSM_CTRL */
> +#define FORCE_POLLING_FAIL	BIT(4)
> +#define FORCE_RXDETECT_FAIL	BIT(3)
> +#define SOFT_U3_EXIT_EN		BIT(2)
> +#define COMPLIANCE_EN		BIT(1)
> +#define U1_GO_U2_EN		BIT(0)
> +
> +/* U3D_USB3_CONFIG */
> +#define USB3_EN			BIT(0)
> +
> +/* U3D_LTSSM_INTR_ENABLE */
> +/* U3D_LTSSM_INTR */
> +#define U3_RESUME_INTR		BIT(18)
> +#define U3_LFPS_TMOUT_INTR	BIT(17)
> +#define VBUS_FALL_INTR		BIT(16)
> +#define VBUS_RISE_INTR		BIT(15)
> +#define RXDET_SUCCESS_INTR	BIT(14)
> +#define EXIT_U3_INTR		BIT(13)
> +#define EXIT_U2_INTR		BIT(12)
> +#define EXIT_U1_INTR		BIT(11)
> +#define ENTER_U3_INTR		BIT(10)
> +#define ENTER_U2_INTR		BIT(9)
> +#define ENTER_U1_INTR		BIT(8)
> +#define ENTER_U0_INTR		BIT(7)
> +#define RECOVERY_INTR		BIT(6)
> +#define WARM_RST_INTR		BIT(5)
> +#define HOT_RST_INTR		BIT(4)
> +#define LOOPBACK_INTR		BIT(3)
> +#define COMPLIANCE_INTR		BIT(2)
> +#define SS_DISABLE_INTR		BIT(1)
> +#define SS_INACTIVE_INTR	BIT(0)
> +
> +/*---------------- SSUSB_USB3_SYS_CSR REGISTER DEFINITION ----------------*/
> +
> +#define U3D_LINK_UX_INACT_TIMER	(SSUSB_USB3_SYS_CSR_BASE + 0x020C)
> +#define U3D_LINK_POWER_CONTROL	(SSUSB_USB3_SYS_CSR_BASE + 0x0210)
> +#define U3D_LINK_ERR_COUNT	(SSUSB_USB3_SYS_CSR_BASE + 0x0214)
> +
> +/*---------------- SSUSB_USB3_SYS_CSR FIELD DEFINITION ----------------*/
> +
> +/* U3D_LINK_UX_INACT_TIMER */
> +#define DEV_U2_INACT_TIMEOUT_MSK	GENMASK(23, 16)
> +#define DEV_U2_INACT_TIMEOUT_VALUE(x)	(((x) & 0xff) << 16)
> +#define U2_INACT_TIMEOUT_MSK		GENMASK(15, 8)
> +#define U1_INACT_TIMEOUT_MSK		GENMASK(7, 0)
> +#define U1_INACT_TIMEOUT_VALUE(x)	((x) & 0xff)
> +
> +/* U3D_LINK_POWER_CONTROL */
> +#define SW_U2_ACCEPT_ENABLE	BIT(9)
> +#define SW_U1_ACCEPT_ENABLE	BIT(8)
> +#define UX_EXIT			BIT(5)
> +#define LGO_U3			BIT(4)
> +#define LGO_U2			BIT(3)
> +#define LGO_U1			BIT(2)
> +#define SW_U2_REQUEST_ENABLE	BIT(1)
> +#define SW_U1_REQUEST_ENABLE	BIT(0)
> +
> +/* U3D_LINK_ERR_COUNT */
> +#define CLR_LINK_ERR_CNT	BIT(16)
> +#define LINK_ERROR_COUNT	GENMASK(15, 0)
> +
> +/*---------------- SSUSB_USB2_CSR REGISTER DEFINITION ----------------*/
> +
> +#define U3D_POWER_MANAGEMENT		(SSUSB_USB2_CSR_BASE + 0x0004)
> +#define U3D_DEVICE_CONTROL		(SSUSB_USB2_CSR_BASE + 0x000C)
> +#define U3D_USB2_TEST_MODE		(SSUSB_USB2_CSR_BASE + 0x0014)
> +#define U3D_COMMON_USB_INTR_ENABLE	(SSUSB_USB2_CSR_BASE + 0x0018)
> +#define U3D_COMMON_USB_INTR		(SSUSB_USB2_CSR_BASE + 0x001C)
> +#define U3D_LINK_RESET_INFO		(SSUSB_USB2_CSR_BASE + 0x0024)
> +#define U3D_USB20_FRAME_NUM		(SSUSB_USB2_CSR_BASE + 0x003C)
> +#define U3D_USB20_LPM_PARAMETER		(SSUSB_USB2_CSR_BASE + 0x0044)
> +#define U3D_USB20_MISC_CONTROL		(SSUSB_USB2_CSR_BASE + 0x004C)
> +
> +/*---------------- SSUSB_USB2_CSR FIELD DEFINITION ----------------*/
> +
> +/* U3D_POWER_MANAGEMENT */
> +#define LPM_BESL_STALL		BIT(14)
> +#define LPM_BESLD_STALL		BIT(13)
> +#define LPM_RWP			BIT(11)
> +#define LPM_HRWE		BIT(10)
> +#define LPM_MODE(x)		(((x) & 0x3) << 8)
> +#define ISO_UPDATE		BIT(7)
> +#define SOFT_CONN		BIT(6)
> +#define HS_ENABLE		BIT(5)
> +#define RESUME			BIT(2)
> +#define SUSPENDM_ENABLE		BIT(0)
> +
> +/* U3D_DEVICE_CONTROL */
> +#define DC_HOSTREQ		BIT(1)
> +#define DC_SESSION		BIT(0)
> +
> +/* U3D_USB2_TEST_MODE */
> +#define U2U3_AUTO_SWITCH	BIT(10)
> +#define LPM_FORCE_STALL		BIT(8)
> +#define FIFO_ACCESS		BIT(6)
> +#define FORCE_FS		BIT(5)
> +#define FORCE_HS		BIT(4)
> +#define TEST_PACKET_MODE	BIT(3)
> +#define TEST_K_MODE		BIT(2)
> +#define TEST_J_MODE		BIT(1)
> +#define TEST_SE0_NAK_MODE	BIT(0)
> +
> +/* U3D_COMMON_USB_INTR_ENABLE */
> +/* U3D_COMMON_USB_INTR */
> +#define LPM_RESUME_INTR		BIT(9)
> +#define LPM_INTR		BIT(8)
> +#define DISCONN_INTR		BIT(5)
> +#define CONN_INTR		BIT(4)
> +#define SOF_INTR		BIT(3)
> +#define RESET_INTR		BIT(2)
> +#define RESUME_INTR		BIT(1)
> +#define SUSPEND_INTR		BIT(0)
> +
> +/* U3D_LINK_RESET_INFO */
> +#define WTCHRP_MSK		GENMASK(19, 16)
> +
> +/* U3D_USB20_LPM_PARAMETER */
> +#define LPM_BESLCK_U3(x)	(((x) & 0xf) << 12)
> +#define LPM_BESLCK(x)		(((x) & 0xf) << 8)
> +#define LPM_BESLDCK(x)		(((x) & 0xf) << 4)
> +#define LPM_BESL		GENMASK(3, 0)
> +
> +/* U3D_USB20_MISC_CONTROL */
> +#define LPM_U3_ACK_EN		BIT(0)
> +
> +/*---------------- SSUSB_SIFSLV_IPPC REGISTER DEFINITION ----------------*/
> +
> +#define U3D_SSUSB_IP_PW_CTRL0	(SSUSB_SIFSLV_IPPC_BASE + 0x0000)
> +#define U3D_SSUSB_IP_PW_CTRL1	(SSUSB_SIFSLV_IPPC_BASE + 0x0004)
> +#define U3D_SSUSB_IP_PW_CTRL2	(SSUSB_SIFSLV_IPPC_BASE + 0x0008)
> +#define U3D_SSUSB_IP_PW_CTRL3	(SSUSB_SIFSLV_IPPC_BASE + 0x000C)
> +#define U3D_SSUSB_IP_PW_STS1	(SSUSB_SIFSLV_IPPC_BASE + 0x0010)
> +#define U3D_SSUSB_IP_PW_STS2	(SSUSB_SIFSLV_IPPC_BASE + 0x0014)
> +#define U3D_SSUSB_OTG_STS	(SSUSB_SIFSLV_IPPC_BASE + 0x0018)
> +#define U3D_SSUSB_OTG_STS_CLR	(SSUSB_SIFSLV_IPPC_BASE + 0x001C)
> +#define U3D_SSUSB_IP_XHCI_CAP	(SSUSB_SIFSLV_IPPC_BASE + 0x0024)
> +#define U3D_SSUSB_IP_DEV_CAP	(SSUSB_SIFSLV_IPPC_BASE + 0x0028)
> +#define U3D_SSUSB_OTG_INT_EN	(SSUSB_SIFSLV_IPPC_BASE + 0x002C)
> +#define U3D_SSUSB_U3_CTRL_0P	(SSUSB_SIFSLV_IPPC_BASE + 0x0030)
> +#define U3D_SSUSB_U2_CTRL_0P	(SSUSB_SIFSLV_IPPC_BASE + 0x0050)
> +#define U3D_SSUSB_REF_CK_CTRL	(SSUSB_SIFSLV_IPPC_BASE + 0x008C)
> +#define U3D_SSUSB_DEV_RST_CTRL	(SSUSB_SIFSLV_IPPC_BASE + 0x0098)
> +#define U3D_SSUSB_HW_ID		(SSUSB_SIFSLV_IPPC_BASE + 0x00A0)
> +#define U3D_SSUSB_HW_SUB_ID	(SSUSB_SIFSLV_IPPC_BASE + 0x00A4)
> +#define U3D_SSUSB_IP_SPARE0	(SSUSB_SIFSLV_IPPC_BASE + 0x00C8)
> +
> +/*---------------- SSUSB_SIFSLV_IPPC FIELD DEFINITION ----------------*/
> +
> +/* U3D_SSUSB_IP_PW_CTRL0 */
> +#define SSUSB_IP_SW_RST			BIT(0)
> +
> +/* U3D_SSUSB_IP_PW_CTRL1 */
> +#define SSUSB_IP_HOST_PDN		BIT(0)
> +
> +/* U3D_SSUSB_IP_PW_CTRL2 */
> +#define SSUSB_IP_DEV_PDN		BIT(0)
> +
> +/* U3D_SSUSB_IP_PW_CTRL3 */
> +#define SSUSB_IP_PCIE_PDN		BIT(0)
> +
> +/* U3D_SSUSB_IP_PW_STS1 */
> +#define SSUSB_IP_SLEEP_STS		BIT(30)
> +#define SSUSB_U3_MAC_RST_B_STS		BIT(16)
> +#define SSUSB_XHCI_RST_B_STS		BIT(11)
> +#define SSUSB_SYS125_RST_B_STS		BIT(10)
> +#define SSUSB_REF_RST_B_STS		BIT(8)
> +#define SSUSB_SYSPLL_STABLE		BIT(0)
> +
> +/* U3D_SSUSB_IP_PW_STS2 */
> +#define SSUSB_U2_MAC_SYS_RST_B_STS	BIT(0)
> +
> +/* U3D_SSUSB_OTG_STS */
> +#define SSUSB_VBUS_VALID		BIT(9)
> +
> +/* U3D_SSUSB_OTG_STS_CLR */
> +#define SSUSB_VBUS_INTR_CLR		BIT(6)
> +
> +/* U3D_SSUSB_IP_XHCI_CAP */
> +#define SSUSB_IP_XHCI_U2_PORT_NUM(x)	(((x) >> 8) & 0xff)
> +#define SSUSB_IP_XHCI_U3_PORT_NUM(x)	((x) & 0xff)
> +
> +/* U3D_SSUSB_IP_DEV_CAP */
> +#define SSUSB_IP_DEV_U3_PORT_NUM(x)	((x) & 0xff)
> +
> +/* U3D_SSUSB_OTG_INT_EN */
> +#define SSUSB_VBUS_CHG_INT_A_EN		BIT(7)
> +#define SSUSB_VBUS_CHG_INT_B_EN		BIT(6)
> +
> +/* U3D_SSUSB_U3_CTRL_0P */
> +#define SSUSB_U3_PORT_HOST_SEL		BIT(2)
> +#define SSUSB_U3_PORT_PDN		BIT(1)
> +#define SSUSB_U3_PORT_DIS		BIT(0)
> +
> +/* U3D_SSUSB_U2_CTRL_0P */
> +#define SSUSB_U2_PORT_OTG_SEL		BIT(7)
> +#define SSUSB_U2_PORT_HOST_SEL		BIT(2)
> +#define SSUSB_U2_PORT_PDN		BIT(1)
> +#define SSUSB_U2_PORT_DIS		BIT(0)
> +
> +/* U3D_SSUSB_DEV_RST_CTRL */
> +#define SSUSB_DEV_SW_RST		BIT(0)
> +
> +#endif	/* _SSUSB_HW_REGS_H_ */
> diff --git a/drivers/usb/mtu3/mtu3_plat.c b/drivers/usb/mtu3/mtu3_plat.c
> new file mode 100644
> index 0000000..d74bd9e
> --- /dev/null
> +++ b/drivers/usb/mtu3/mtu3_plat.c
> @@ -0,0 +1,490 @@
> +/*
> + * Copyright (C) 2016 MediaTek Inc.
> + *
> + * Author: Chunfeng Yun <chunfeng.yun@mediatek.com>
> + *
> + * This software is licensed under the terms of the GNU General Public
> + * License version 2, as published by the Free Software Foundation, and
> + * may be copied, distributed, and modified under those terms.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + * GNU General Public License for more details.
> + *
> + */
> +
> +#include <linux/clk.h>
> +#include <linux/dma-mapping.h>
> +#include <linux/iopoll.h>
> +#include <linux/kernel.h>
> +#include <linux/module.h>
> +#include <linux/of_address.h>
> +#include <linux/of_irq.h>
> +#include <linux/pinctrl/consumer.h>
> +#include <linux/platform_device.h>
> +
> +#include "mtu3.h"
> +#include "mtu3_dr.h"
> +
> +/* u2-port0 should be powered on and enabled; */
> +int ssusb_check_clocks(struct ssusb_mtk *ssusb, u32 ex_clks)
> +{
> +	void __iomem *ibase = ssusb->ippc_base;
> +	u32 value, check_val;
> +	int ret;
> +
> +	check_val = ex_clks | SSUSB_SYS125_RST_B_STS | SSUSB_SYSPLL_STABLE |
> +			SSUSB_REF_RST_B_STS;
> +
> +	ret = readl_poll_timeout(ibase + U3D_SSUSB_IP_PW_STS1, value,
> +			(check_val == (value & check_val)), 100, 20000);
> +	if (ret) {
> +		dev_err(ssusb->dev, "clks of sts1 are not stable!\n");
> +		return ret;
> +	}
> +
> +	ret = readl_poll_timeout(ibase + U3D_SSUSB_IP_PW_STS2, value,
> +			(value & SSUSB_U2_MAC_SYS_RST_B_STS), 100, 10000);
> +	if (ret) {
> +		dev_err(ssusb->dev, "mac2 clock is not stable\n");
> +		return ret;
> +	}
> +
> +	return 0;
> +}
> +
> +static int ssusb_phy_init(struct ssusb_mtk *ssusb)
> +{
> +	int i;
> +	int ret;
> +
> +	for (i = 0; i < ssusb->num_phys; i++) {
> +		ret = phy_init(ssusb->phys[i]);
> +		if (ret)
> +			goto exit_phy;
> +	}
> +	return 0;
> +
> +exit_phy:
> +	for (; i > 0; i--)
> +		phy_exit(ssusb->phys[i - 1]);
> +
> +	return ret;
> +}
> +
> +static int ssusb_phy_exit(struct ssusb_mtk *ssusb)
> +{
> +	int i;
> +
> +	for (i = 0; i < ssusb->num_phys; i++)
> +		phy_exit(ssusb->phys[i]);
> +
> +	return 0;
> +}
> +
> +static int ssusb_phy_power_on(struct ssusb_mtk *ssusb)
> +{
> +	int i;
> +	int ret;
> +
> +	for (i = 0; i < ssusb->num_phys; i++) {
> +		ret = phy_power_on(ssusb->phys[i]);
> +		if (ret)
> +			goto power_off_phy;
> +	}
> +	return 0;
> +
> +power_off_phy:
> +	for (; i > 0; i--)
> +		phy_power_off(ssusb->phys[i - 1]);
> +
> +	return ret;
> +}
> +
> +static void ssusb_phy_power_off(struct ssusb_mtk *ssusb)
> +{
> +	unsigned int i;
> +
> +	for (i = 0; i < ssusb->num_phys; i++)
> +		phy_power_off(ssusb->phys[i]);
> +}
> +
> +static int ssusb_common_init(struct ssusb_mtk *ssusb)
> +{
> +	int ret = 0;
> +
> +	ret = regulator_enable(ssusb->vusb33);
> +	if (ret) {
> +		dev_err(ssusb->dev, "failed to enable vusb33\n");
> +		goto vusb33_err;
> +	}
> +
> +	ret = clk_prepare_enable(ssusb->sys_clk);
> +	if (ret) {
> +		dev_err(ssusb->dev, "failed to enable sys_clk\n");
> +		goto clk_err;
> +	}
> +
> +	ret = ssusb_phy_init(ssusb);
> +	if (ret) {
> +		dev_err(ssusb->dev, "failed to init phy\n");
> +		goto phy_init_err;
> +	}
> +
> +	ret = ssusb_phy_power_on(ssusb);
> +	if (ret) {
> +		dev_err(ssusb->dev, "failed to power on phy\n");
> +		goto phy_err;
> +	}
> +
> +	return 0;
> +
> +phy_err:
> +	ssusb_phy_exit(ssusb);
> +
> +phy_init_err:
> +	clk_disable_unprepare(ssusb->sys_clk);
> +
> +clk_err:
> +	regulator_disable(ssusb->vusb33);
> +
> +vusb33_err:
> +
> +	return ret;
> +}
> +
> +static void ssusb_common_exit(struct ssusb_mtk *ssusb)
> +{
> +	clk_disable_unprepare(ssusb->sys_clk);
> +	regulator_disable(ssusb->vusb33);
> +	ssusb_phy_power_off(ssusb);
> +	ssusb_phy_exit(ssusb);
> +}
> +
> +static void ssusb_ip_sw_reset(struct ssusb_mtk *ssusb)
> +{
> +	/* reset whole ip (xhci & u3d) */
> +	mtu3_setbits(ssusb->ippc_base, U3D_SSUSB_IP_PW_CTRL0, SSUSB_IP_SW_RST);
> +	udelay(1);
> +	mtu3_clrbits(ssusb->ippc_base, U3D_SSUSB_IP_PW_CTRL0, SSUSB_IP_SW_RST);
> +}
> +
> +static int get_iddig_pinctrl(struct ssusb_mtk *ssusb)
> +{
> +	struct otg_switch_mtk *otg_sx = &ssusb->otg_switch;
> +
> +	otg_sx->id_pinctrl = devm_pinctrl_get(ssusb->dev);
> +	if (IS_ERR(otg_sx->id_pinctrl)) {
> +		dev_err(ssusb->dev, "Cannot find id pinctrl!\n");
> +		return PTR_ERR(otg_sx->id_pinctrl);
> +	}
> +
> +	otg_sx->id_float =
> +		pinctrl_lookup_state(otg_sx->id_pinctrl, "id_float");
> +	if (IS_ERR(otg_sx->id_float)) {
> +		dev_err(ssusb->dev, "Cannot find pinctrl id_float!\n");
> +		return PTR_ERR(otg_sx->id_float);
> +	}
> +
> +	otg_sx->id_ground =
> +		pinctrl_lookup_state(otg_sx->id_pinctrl, "id_ground");
> +	if (IS_ERR(otg_sx->id_ground)) {
> +		dev_err(ssusb->dev, "Cannot find pinctrl id_ground!\n");
> +		return PTR_ERR(otg_sx->id_ground);
> +	}
> +
> +	return 0;
> +}
> +
> +static int get_ssusb_rscs(struct platform_device *pdev, struct ssusb_mtk *ssusb)
> +{
> +	struct device_node *node = pdev->dev.of_node;
> +	struct otg_switch_mtk *otg_sx = &ssusb->otg_switch;
> +	struct device *dev = &pdev->dev;
> +	struct regulator *vbus;
> +	struct resource *res;
> +	int i;
> +	int ret;
> +
> +	ssusb->num_phys = of_count_phandle_with_args(node,
> +			"phys", "#phy-cells");
> +	if (ssusb->num_phys > 0) {
> +		ssusb->phys = devm_kcalloc(dev, ssusb->num_phys,
> +					sizeof(*ssusb->phys), GFP_KERNEL);
> +		if (!ssusb->phys)
> +			return -ENOMEM;
> +	} else {
> +		ssusb->num_phys = 0;
> +	}
> +
> +	for (i = 0; i < ssusb->num_phys; i++) {
> +		ssusb->phys[i] = devm_of_phy_get_by_index(dev, node, i);
> +		if (IS_ERR(ssusb->phys[i])) {
> +			dev_err(dev, "failed to get phy-%d\n", i);
> +			return PTR_ERR(ssusb->phys[i]);
> +		}
> +	}
> +
> +	res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "ippc");
> +	ssusb->ippc_base = devm_ioremap_resource(dev, res);
> +	if (IS_ERR(ssusb->ippc_base)) {
> +		dev_err(dev, "failed to map memory for ippc\n");
> +		return PTR_ERR(ssusb->ippc_base);
> +	}
> +
> +	ssusb->vusb33 = devm_regulator_get(&pdev->dev, "vusb33");
> +	if (IS_ERR(ssusb->vusb33)) {
> +		dev_err(dev, "failed to get vusb33\n");
> +		return PTR_ERR(ssusb->vusb33);
> +	}
> +
> +	ssusb->sys_clk = devm_clk_get(dev, "sys_ck");
> +	if (IS_ERR(ssusb->sys_clk)) {
> +		dev_err(dev, "failed to get sys clock\n");
> +		return PTR_ERR(ssusb->sys_clk);
> +	}
> +
> +	ssusb->dr_mode = usb_get_dr_mode(dev);
> +	if (ssusb->dr_mode == USB_DR_MODE_UNKNOWN) {
> +		dev_err(dev, "dr_mode is error\n");
> +		return -EINVAL;
> +	}
> +
> +	if (ssusb->dr_mode == USB_DR_MODE_PERIPHERAL)
> +		return 0;
> +
> +	/* if host role is supported */
> +	ret = ssusb_wakeup_of_property_parse(ssusb, node);
> +	if (ret)
> +		return ret;
> +
> +	if (ssusb->dr_mode != USB_DR_MODE_OTG)
> +		return 0;
> +
> +	/* if dual-role mode is supported */
> +	vbus = devm_regulator_get(&pdev->dev, "vbus");
> +	if (IS_ERR(vbus)) {
> +		dev_err(dev, "failed to get vbus\n");
> +		return PTR_ERR(vbus);
> +	}
> +	otg_sx->vbus = vbus;
> +
> +	otg_sx->is_u3_drd = of_property_read_bool(node, "mediatek,usb3-drd");
> +	otg_sx->manual_drd_enabled =
> +		of_property_read_bool(node, "enable-manual-drd");
> +
> +	if (of_property_read_bool(node, "extcon")) {
> +		otg_sx->edev = extcon_get_edev_by_phandle(ssusb->dev, 0);
> +		if (IS_ERR(otg_sx->edev)) {
> +			dev_err(ssusb->dev, "couldn't get extcon device\n");
> +			return -EPROBE_DEFER;
> +		}
> +		if (otg_sx->manual_drd_enabled) {
> +			ret = get_iddig_pinctrl(ssusb);
> +			if (ret)
> +				return ret;
> +		}
> +	}
> +
> +	dev_info(dev, "dr_mode: %d, is_u3_dr: %d\n",
> +		ssusb->dr_mode, otg_sx->is_u3_drd);
> +
> +	return 0;
> +}
> +
> +static int mtu3_probe(struct platform_device *pdev)
> +{
> +	struct device_node *node = pdev->dev.of_node;
> +	struct device *dev = &pdev->dev;
> +	struct ssusb_mtk *ssusb;
> +	int ret = -ENOMEM;
> +
> +	/* all elements are set to ZERO as default value */
> +	ssusb = devm_kzalloc(dev, sizeof(*ssusb), GFP_KERNEL);
> +	if (!ssusb)
> +		return -ENOMEM;
> +
> +	ret = dma_set_mask_and_coherent(dev, DMA_BIT_MASK(32));
> +	if (ret) {
> +		dev_err(dev, "No suitable DMA config available\n");
> +		return -ENOTSUPP;
> +	}
> +
> +	platform_set_drvdata(pdev, ssusb);
> +	ssusb->dev = dev;
> +
> +	ret = get_ssusb_rscs(pdev, ssusb);
> +	if (ret)
> +		return ret;
> +
> +	/* enable power domain */
> +	pm_runtime_enable(dev);
> +	pm_runtime_get_sync(dev);
> +	device_enable_async_suspend(dev);
> +
> +	ret = ssusb_common_init(ssusb);
> +	if (ret)
> +		goto comm_init_err;
> +
> +	ssusb_ip_sw_reset(ssusb);
> +
> +	if (IS_ENABLED(CONFIG_USB_MTU3_HOST))
> +		ssusb->dr_mode = USB_DR_MODE_HOST;
> +	else if (IS_ENABLED(CONFIG_USB_MTU3_GADGET))
> +		ssusb->dr_mode = USB_DR_MODE_PERIPHERAL;
> +
> +	/* default as host */
> +	ssusb->is_host = !(ssusb->dr_mode == USB_DR_MODE_PERIPHERAL);
> +
> +	switch (ssusb->dr_mode) {
> +	case USB_DR_MODE_PERIPHERAL:
> +		ret = ssusb_gadget_init(ssusb);
> +		if (ret) {
> +			dev_err(dev, "failed to initialize gadget\n");
> +			goto comm_exit;
> +		}
> +		break;
> +	case USB_DR_MODE_HOST:
> +		ret = ssusb_host_init(ssusb, node);
> +		if (ret) {
> +			dev_err(dev, "failed to initialize host\n");
> +			goto comm_exit;
> +		}
> +		break;
> +	case USB_DR_MODE_OTG:
> +		ret = ssusb_gadget_init(ssusb);
> +		if (ret) {
> +			dev_err(dev, "failed to initialize gadget\n");
> +			goto comm_exit;
> +		}
> +
> +		ret = ssusb_host_init(ssusb, node);
> +		if (ret) {
> +			dev_err(dev, "failed to initialize host\n");
> +			goto gadget_exit;
> +		}
> +
> +		ssusb_otg_switch_init(ssusb);
> +		break;
> +	default:
> +		dev_err(dev, "unsupported mode: %d\n", ssusb->dr_mode);
> +		ret = -EINVAL;
> +		goto comm_exit;
> +	}
> +
> +	return 0;
> +
> +gadget_exit:
> +	ssusb_gadget_exit(ssusb);
> +
> +comm_exit:
> +	ssusb_common_exit(ssusb);
> +
> +comm_init_err:
> +	pm_runtime_put_sync(dev);
> +	pm_runtime_disable(dev);
> +
> +	return ret;
> +}
> +
> +static int mtu3_remove(struct platform_device *pdev)
> +{
> +	struct ssusb_mtk *ssusb = platform_get_drvdata(pdev);
> +
> +	switch (ssusb->dr_mode) {
> +	case USB_DR_MODE_PERIPHERAL:
> +		ssusb_gadget_exit(ssusb);
> +		break;
> +	case USB_DR_MODE_HOST:
> +		ssusb_host_exit(ssusb);
> +		break;
> +	case USB_DR_MODE_OTG:
> +		ssusb_otg_switch_exit(ssusb);
> +		ssusb_gadget_exit(ssusb);
> +		ssusb_host_exit(ssusb);
> +		break;
> +	default:
> +		return -EINVAL;
> +	}
> +
> +	ssusb_common_exit(ssusb);
> +	pm_runtime_put_sync(&pdev->dev);
> +	pm_runtime_disable(&pdev->dev);
> +
> +	return 0;
> +}
> +
> +/*
> + * when support dual-role mode, we reject suspend when
> + * it works as device mode;
> + */
> +static int __maybe_unused mtu3_suspend(struct device *dev)
> +{
> +	struct platform_device *pdev = to_platform_device(dev);
> +	struct ssusb_mtk *ssusb = platform_get_drvdata(pdev);
> +
> +	dev_dbg(dev, "%s\n", __func__);
> +
> +	/* REVISIT: disconnect it for only device mode? */
> +	if (!ssusb->is_host)
> +		return 0;
> +
> +	ssusb_host_disable(ssusb, true);
> +	ssusb_phy_power_off(ssusb);
> +	clk_disable_unprepare(ssusb->sys_clk);
> +	ssusb_wakeup_enable(ssusb);
> +
> +	return 0;
> +}
> +
> +static int __maybe_unused mtu3_resume(struct device *dev)
> +{
> +	struct platform_device *pdev = to_platform_device(dev);
> +	struct ssusb_mtk *ssusb = platform_get_drvdata(pdev);
> +
> +	dev_dbg(dev, "%s\n", __func__);
> +
> +	if (!ssusb->is_host)
> +		return 0;
> +
> +	ssusb_wakeup_disable(ssusb);
> +	clk_prepare_enable(ssusb->sys_clk);
> +	ssusb_phy_power_on(ssusb);
> +	ssusb_host_enable(ssusb);
> +
> +	return 0;
> +}
> +
> +static const struct dev_pm_ops mtu3_pm_ops = {
> +	SET_SYSTEM_SLEEP_PM_OPS(mtu3_suspend, mtu3_resume)
> +};
> +
> +#define DEV_PM_OPS (IS_ENABLED(CONFIG_PM) ? &mtu3_pm_ops : NULL)
> +
> +#ifdef CONFIG_OF
> +
> +static const struct of_device_id mtu3_of_match[] = {
> +	{.compatible = "mediatek,mt8173-mtu3",},
> +	{},
> +};
> +
> +MODULE_DEVICE_TABLE(of, mtu3_of_match);
> +
> +#endif
> +
> +static struct platform_driver mtu3_driver = {
> +	.probe = mtu3_probe,
> +	.remove = mtu3_remove,
> +	.driver = {
> +		.name = MTU3_DRIVER_NAME,
> +		.pm = DEV_PM_OPS,
> +		.of_match_table = of_match_ptr(mtu3_of_match),
> +	},
> +};
> +module_platform_driver(mtu3_driver);
> +
> +MODULE_AUTHOR("Chunfeng Yun <chunfeng.yun@mediatek.com>");
> +MODULE_LICENSE("GPL v2");
> +MODULE_DESCRIPTION("MediaTek USB3 DRD Controller Driver");
> +
> diff --git a/drivers/usb/mtu3/mtu3_qmu.c b/drivers/usb/mtu3/mtu3_qmu.c
> new file mode 100644
> index 0000000..8df78dd
> --- /dev/null
> +++ b/drivers/usb/mtu3/mtu3_qmu.c
> @@ -0,0 +1,599 @@
> +/*
> + * mtu3_qmu.c - Queue Management Unit driver for device controller
> + *
> + * Copyright (C) 2016 MediaTek Inc.
> + *
> + * Author: Chunfeng Yun <chunfeng.yun@mediatek.com>
> + *
> + * This software is licensed under the terms of the GNU General Public
> + * License version 2, as published by the Free Software Foundation, and
> + * may be copied, distributed, and modified under those terms.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + * GNU General Public License for more details.
> + *
> + */
> +
> +/*
> + * Queue Management Unit (QMU) is designed to unload SW effort
> + * to serve DMA interrupts.
> + * By preparing General Purpose Descriptor (GPD) and Buffer Descriptor (BD),
> + * SW links data buffers and triggers QMU to send / receive data to
> + * host / from device at a time.
> + * And now only GPD is supported.
> + *
> + * For more detailed information, please refer to QMU Programming Guide
> + */
> +
> +#include <linux/dmapool.h>
> +#include <linux/iopoll.h>
> +
> +#include "mtu3.h"
> +
> +#define QMU_CHECKSUM_LEN	16
> +
> +#define GPD_FLAGS_HWO	BIT(0)
> +#define GPD_FLAGS_BDP	BIT(1)
> +#define GPD_FLAGS_BPS	BIT(2)
> +#define GPD_FLAGS_IOC	BIT(7)
> +
> +#define GPD_EXT_FLAG_ZLP	BIT(5)
> +
> +
> +static struct qmu_gpd *gpd_dma_to_virt(struct mtu3_gpd_ring *ring,
> +		dma_addr_t dma_addr)
> +{
> +	dma_addr_t dma_base = ring->dma;
> +	struct qmu_gpd *gpd_head = ring->start;
> +	u32 offset = (dma_addr - dma_base) / sizeof(*gpd_head);
> +
> +	if (offset >= MAX_GPD_NUM)
> +		return NULL;
> +
> +	return gpd_head + offset;
> +}
> +
> +static dma_addr_t gpd_virt_to_dma(struct mtu3_gpd_ring *ring,
> +		struct qmu_gpd *gpd)
> +{
> +	dma_addr_t dma_base = ring->dma;
> +	struct qmu_gpd *gpd_head = ring->start;
> +	u32 offset;
> +
> +	offset = gpd - gpd_head;
> +	if (offset >= MAX_GPD_NUM)
> +		return 0;
> +
> +	return dma_base + (offset * sizeof(*gpd));
> +}
> +
> +static void gpd_ring_init(struct mtu3_gpd_ring *ring, struct qmu_gpd *gpd)
> +{
> +	ring->start = gpd;
> +	ring->enqueue = gpd;
> +	ring->dequeue = gpd;
> +	ring->end = gpd + MAX_GPD_NUM - 1;
> +}
> +
> +static void reset_gpd_list(struct mtu3_ep *mep)
> +{
> +	struct mtu3_gpd_ring *ring = &mep->gpd_ring;
> +	struct qmu_gpd *gpd = ring->start;
> +
> +	if (gpd) {
> +		gpd->flag &= ~GPD_FLAGS_HWO;
> +		gpd_ring_init(ring, gpd);
> +	}
> +}
> +
> +int mtu3_gpd_ring_alloc(struct mtu3_ep *mep)
> +{
> +	struct qmu_gpd *gpd;
> +	struct mtu3_gpd_ring *ring = &mep->gpd_ring;
> +
> +	/* software own all gpds as default */
> +	gpd = dma_pool_zalloc(mep->mtu->qmu_gpd_pool, GFP_ATOMIC, &ring->dma);
> +	if (gpd == NULL)
> +		return -ENOMEM;
> +
> +	gpd_ring_init(ring, gpd);
> +
> +	return 0;
> +}
> +
> +void mtu3_gpd_ring_free(struct mtu3_ep *mep)
> +{
> +	struct mtu3_gpd_ring *ring = &mep->gpd_ring;
> +
> +	dma_pool_free(mep->mtu->qmu_gpd_pool,
> +			ring->start, ring->dma);
> +	memset(ring, 0, sizeof(*ring));
> +}
> +
> +/*
> + * calculate check sum of a gpd or bd
> + * add "noinline" and "mb" to prevent wrong calculation
> + */
> +static noinline u8 qmu_calc_checksum(u8 *data)
> +{
> +	u8 chksum = 0;
> +	int i;
> +
> +	data[1] = 0x0;  /* set checksum to 0 */
> +
> +	mb();	/* ensure the gpd/bd is really up-to-date */
> +	for (i = 0; i < QMU_CHECKSUM_LEN; i++)
> +		chksum += data[i];
> +
> +	/* Default: HWO=1, @flag[bit0] */
> +	chksum += 1;
> +
> +	return 0xFF - chksum;
> +}
> +
> +void mtu3_qmu_resume(struct mtu3_ep *mep)
> +{
> +	struct mtu3 *mtu = mep->mtu;
> +	void __iomem *mbase = mtu->mac_base;
> +	int epnum = mep->epnum;
> +	u32 offset;
> +
> +	offset = mep->is_in ? USB_QMU_TQCSR(epnum) : USB_QMU_RQCSR(epnum);
> +
> +	mtu3_writel(mbase, offset, QMU_Q_RESUME);
> +	if (!(mtu3_readl(mbase, offset) & QMU_Q_ACTIVE))
> +		mtu3_writel(mbase, offset, QMU_Q_RESUME);
> +}
> +
> +static struct qmu_gpd *advance_enq_gpd(struct mtu3_gpd_ring *ring)
> +{
> +	if (ring->enqueue < ring->end)
> +		ring->enqueue++;
> +	else
> +		ring->enqueue = ring->start;
> +
> +	return ring->enqueue;
> +}
> +
> +static struct qmu_gpd *advance_deq_gpd(struct mtu3_gpd_ring *ring)
> +{
> +	if (ring->dequeue < ring->end)
> +		ring->dequeue++;
> +	else
> +		ring->dequeue = ring->start;
> +
> +	return ring->dequeue;
> +}
> +
> +/* check if a ring is emtpy */
> +int gpd_ring_empty(struct mtu3_gpd_ring *ring)
> +{
> +	struct qmu_gpd *enq = ring->enqueue;
> +	struct qmu_gpd *next;
> +
> +	if (ring->enqueue < ring->end)
> +		next = enq + 1;
> +	else
> +		next = ring->start;
> +
> +	/* one gpd is reserved to simplify gpd preparation */
> +	return next == ring->dequeue;
> +}
> +
> +int mtu3_prepare_transfer(struct mtu3_ep *mep)
> +{
> +	return gpd_ring_empty(&mep->gpd_ring);
> +}
> +
> +static int mtu3_prepare_tx_gpd(struct mtu3_ep *mep, struct mtu3_request *mreq)
> +{
> +	struct qmu_gpd *enq;
> +	struct mtu3_gpd_ring *ring = &mep->gpd_ring;
> +	struct qmu_gpd *gpd = ring->enqueue;
> +	struct usb_request *req = &mreq->request;
> +
> +	/* set all fields to zero as default value */
> +	memset(gpd, 0, sizeof(*gpd));
> +
> +	gpd->buffer = cpu_to_le32((u32)req->dma);
> +	gpd->buf_len = cpu_to_le16(req->length);
> +	gpd->flag |= GPD_FLAGS_IOC;
> +
> +	/* get the next GPD */
> +	enq = advance_enq_gpd(ring);
> +	dev_dbg(mep->mtu->dev, "TX-EP%d queue gpd=%p, enq=%p\n",
> +		mep->epnum, gpd, enq);
> +
> +	enq->flag &= ~GPD_FLAGS_HWO;
> +	gpd->next_gpd = cpu_to_le32((u32)gpd_virt_to_dma(ring, enq));
> +
> +	if (req->zero)
> +		gpd->ext_flag |= GPD_EXT_FLAG_ZLP;
> +
> +	gpd->chksum = qmu_calc_checksum((u8 *)gpd);
> +	gpd->flag |= GPD_FLAGS_HWO;
> +
> +	mreq->gpd = gpd;
> +
> +	return 0;
> +}
> +
> +static int mtu3_prepare_rx_gpd(struct mtu3_ep *mep, struct mtu3_request *mreq)
> +{
> +	struct qmu_gpd *enq;
> +	struct mtu3_gpd_ring *ring = &mep->gpd_ring;
> +	struct qmu_gpd *gpd = ring->enqueue;
> +	struct usb_request *req = &mreq->request;
> +
> +	/* set all fields to zero as default value */
> +	memset(gpd, 0, sizeof(*gpd));
> +
> +	gpd->buffer = cpu_to_le32((u32)req->dma);
> +	gpd->data_buf_len = cpu_to_le16(req->length);
> +	gpd->flag |= GPD_FLAGS_IOC;
> +
> +	/* get the next GPD */
> +	enq = advance_enq_gpd(ring);
> +	dev_dbg(mep->mtu->dev, "RX-EP%d queue gpd=%p, enq=%p\n",
> +		mep->epnum, gpd, enq);
> +
> +	enq->flag &= ~GPD_FLAGS_HWO;
> +	gpd->next_gpd = cpu_to_le32((u32)gpd_virt_to_dma(ring, enq));
> +	gpd->chksum = qmu_calc_checksum((u8 *)gpd);
> +	gpd->flag |= GPD_FLAGS_HWO;
> +
> +	mreq->gpd = gpd;
> +
> +	return 0;
> +}
> +
> +void mtu3_insert_gpd(struct mtu3_ep *mep, struct mtu3_request *mreq)
> +{
> +
> +	if (mep->is_in)
> +		mtu3_prepare_tx_gpd(mep, mreq);
> +	else
> +		mtu3_prepare_rx_gpd(mep, mreq);
> +}
> +
> +int mtu3_qmu_start(struct mtu3_ep *mep)
> +{
> +	struct mtu3 *mtu = mep->mtu;
> +	void __iomem *mbase = mtu->mac_base;
> +	struct mtu3_gpd_ring *ring = &mep->gpd_ring;
> +	u8 epnum = mep->epnum;
> +
> +	if (mep->is_in) {
> +		/* set QMU start address */
> +		mtu3_writel(mbase, USB_QMU_TQSAR(mep->epnum), ring->dma);
> +		mtu3_setbits(mbase, MU3D_EP_TXCR0(mep->epnum), TX_DMAREQEN);
> +		mtu3_setbits(mbase, U3D_QCR0, QMU_TX_CS_EN(epnum));
> +
> +		/* send zero length packet according to ZLP flag in GPD */
> +		mtu3_setbits(mbase, U3D_QCR1, QMU_TX_ZLP(epnum));
> +
> +		mtu3_writel(mbase, U3D_TQERRIESR0,
> +				QMU_TX_LEN_ERR(epnum) | QMU_TX_CS_ERR(epnum));
> +
> +		if (mtu3_readl(mbase, USB_QMU_TQCSR(epnum)) & QMU_Q_ACTIVE) {
> +			dev_warn(mtu->dev, "Tx %d Active Now!\n", epnum);
> +			return 0;
> +		}
> +
> +		mtu3_writel(mbase, USB_QMU_TQCSR(epnum), QMU_Q_START);
> +
> +	} else {
> +
> +		mtu3_writel(mbase, USB_QMU_RQSAR(mep->epnum), ring->dma);
> +		mtu3_setbits(mbase, MU3D_EP_RXCR0(mep->epnum), RX_DMAREQEN);
> +		mtu3_setbits(mbase, U3D_QCR0, QMU_RX_CS_EN(epnum));
> +
> +		/* don't expect ZLP */
> +		mtu3_clrbits(mbase, U3D_QCR3, QMU_RX_ZLP(epnum));
> +		/* move to next GPD when receive ZLP */
> +		mtu3_setbits(mbase, U3D_QCR3, QMU_RX_COZ(epnum));
> +
> +		mtu3_writel(mbase, U3D_RQERRIESR0,
> +				QMU_RX_LEN_ERR(epnum) | QMU_RX_CS_ERR(epnum));
> +		mtu3_writel(mbase, U3D_RQERRIESR1, QMU_RX_ZLP_ERR(epnum));
> +
> +		if (mtu3_readl(mbase, USB_QMU_RQCSR(epnum)) & QMU_Q_ACTIVE) {
> +			dev_warn(mtu->dev, "Rx %d Active Now!\n", epnum);
> +			return 0;
> +		}
> +
> +		mtu3_writel(mbase, USB_QMU_RQCSR(epnum), QMU_Q_START);
> +	}
> +
> +	return 0;
> +}
> +
> +/* may called in atomic context */
> +void mtu3_qmu_stop(struct mtu3_ep *mep)
> +{
> +	struct mtu3 *mtu = mep->mtu;
> +	void __iomem *mbase = mtu->mac_base;
> +	int epnum = mep->epnum;
> +	u32 value = 0;
> +	u32 qcsr;
> +	int ret;
> +
> +	qcsr = mep->is_in ? USB_QMU_TQCSR(epnum) : USB_QMU_RQCSR(epnum);
> +
> +	if (!(mtu3_readl(mbase, qcsr) & QMU_Q_ACTIVE)) {
> +		dev_dbg(mtu->dev, "%s's qmu is inactive now!\n", mep->name);
> +		return;
> +	}
> +	mtu3_writel(mbase, qcsr, QMU_Q_STOP);
> +
> +	ret = readl_poll_timeout_atomic(mbase + qcsr, value,
> +			!(value & QMU_Q_ACTIVE), 1, 1000);
> +	if (ret) {
> +		dev_err(mtu->dev, "stop %s's qmu failed\n", mep->name);
> +		return;
> +	}
> +
> +	dev_dbg(mtu->dev, "%s's qmu stop now!\n", mep->name);
> +}
> +
> +void mtu3_qmu_flush(struct mtu3_ep *mep)
> +{
> +
> +	dev_dbg(mep->mtu->dev, "%s flush QMU %s\n", __func__,
> +		((mep->is_in) ? "TX" : "RX"));
> +
> +	/*Stop QMU */
> +	mtu3_qmu_stop(mep);
> +	reset_gpd_list(mep);
> +}
> +
> +/*
> + * QMU can't transfer zero length packet directly (a hardware limit
> + * on old SoCs), so when needs to send ZLP, we intentionally trigger
> + * a length error interrupt, and in the ISR sends a ZLP by BMU.
> + */
> +static void qmu_tx_zlp_error_handler(struct mtu3 *mtu, u8 epnum)
> +{
> +	struct mtu3_ep *mep = mtu->in_eps + epnum;
> +	struct mtu3_gpd_ring *ring = &mep->gpd_ring;
> +	void __iomem *mbase = mtu->mac_base;
> +	struct qmu_gpd *gpd_current = NULL;
> +	dma_addr_t gpd_dma = mtu3_readl(mbase, USB_QMU_TQCPR(epnum));
> +	struct usb_request *req = NULL;
> +	struct mtu3_request *mreq;
> +	u32 txcsr = 0;
> +	int ret;
> +
> +	mreq = next_request(mep);
> +	if (mreq && mreq->request.length == 0)
> +		req = &mreq->request;
> +	else
> +		return;
> +
> +	gpd_current = gpd_dma_to_virt(ring, gpd_dma);
> +
> +	if (le16_to_cpu(gpd_current->buf_len) != 0) {
> +		dev_err(mtu->dev, "TX EP%d buffer length error(!=0)\n", epnum);
> +		return;
> +	}
> +
> +	dev_dbg(mtu->dev, "%s send ZLP for req=%p\n", __func__, mreq);
> +
> +	mtu3_clrbits(mbase, MU3D_EP_TXCR0(mep->epnum), TX_DMAREQEN);
> +
> +	ret = readl_poll_timeout_atomic(mbase + MU3D_EP_TXCR0(mep->epnum),
> +			txcsr, !(txcsr & TX_FIFOFULL), 1, 1000);
> +	if (ret) {
> +		dev_err(mtu->dev, "%s wait for fifo empty fail\n", __func__);
> +		return;
> +	}
> +	mtu3_setbits(mbase, MU3D_EP_TXCR0(mep->epnum), TX_TXPKTRDY);
> +
> +	/* by pass the current GDP */
> +	gpd_current->flag |= GPD_FLAGS_BPS;
> +	gpd_current->chksum = qmu_calc_checksum((u8 *)gpd_current);
> +	gpd_current->flag |= GPD_FLAGS_HWO;
> +
> +	/*enable DMAREQEN, switch back to QMU mode */
> +	mtu3_setbits(mbase, MU3D_EP_TXCR0(mep->epnum), TX_DMAREQEN);
> +	mtu3_qmu_resume(mep);
> +}
> +
> +/*
> + * NOTE: request list maybe is already empty as following case:
> + * queue_tx --> qmu_interrupt(clear interrupt pending, schedule tasklet)-->
> + * queue_tx --> process_tasklet(meanwhile, the second one is transferred,
> + * tasklet process both of them)-->qmu_interrupt for second one.
> + * To avoid upper case, put qmu_done_tx in ISR directly to process it.
> + */
> +static void qmu_done_tx(struct mtu3 *mtu, u8 epnum)
> +{
> +	struct mtu3_ep *mep = mtu->in_eps + epnum;
> +	struct mtu3_gpd_ring *ring = &mep->gpd_ring;
> +	void __iomem *mbase = mtu->mac_base;
> +	struct qmu_gpd *gpd = ring->dequeue;
> +	struct qmu_gpd *gpd_current = NULL;
> +	dma_addr_t gpd_dma = mtu3_readl(mbase, USB_QMU_TQCPR(epnum));
> +	struct usb_request *request = NULL;
> +	struct mtu3_request *mreq;
> +
> +	/*transfer phy address got from QMU register to virtual address */
> +	gpd_current = gpd_dma_to_virt(ring, gpd_dma);
> +
> +	dev_dbg(mtu->dev, "%s EP%d, last=%p, current=%p, enq=%p\n",
> +		__func__, epnum, gpd, gpd_current, ring->enqueue);
> +
> +	while (gpd != gpd_current && !(gpd->flag & GPD_FLAGS_HWO)) {
> +
> +		mreq = next_request(mep);
> +
> +		if (mreq == NULL || mreq->gpd != gpd) {
> +			dev_err(mtu->dev, "no correct TX req is found\n");
> +			break;
> +		}
> +
> +		request = &mreq->request;
> +		request->actual = le16_to_cpu(gpd->buf_len);
> +		mtu3_req_complete(mep, request, 0);
> +
> +		gpd = advance_deq_gpd(ring);
> +	}
> +
> +	dev_dbg(mtu->dev, "%s EP%d, deq=%p, enq=%p, complete\n",
> +		__func__, epnum, ring->dequeue, ring->enqueue);
> +
> +}
> +
> +static void qmu_done_rx(struct mtu3 *mtu, u8 epnum)
> +{
> +	struct mtu3_ep *mep = mtu->out_eps + epnum;
> +	struct mtu3_gpd_ring *ring = &mep->gpd_ring;
> +	void __iomem *mbase = mtu->mac_base;
> +	struct qmu_gpd *gpd = ring->dequeue;
> +	struct qmu_gpd *gpd_current = NULL;
> +	dma_addr_t gpd_dma = mtu3_readl(mbase, USB_QMU_RQCPR(epnum));
> +	struct usb_request *req = NULL;
> +	struct mtu3_request *mreq;
> +
> +	gpd_current = gpd_dma_to_virt(ring, gpd_dma);
> +
> +	dev_dbg(mtu->dev, "%s EP%d, last=%p, current=%p, enq=%p\n",
> +		__func__, epnum, gpd, gpd_current, ring->enqueue);
> +
> +	while (gpd != gpd_current && !(gpd->flag & GPD_FLAGS_HWO)) {
> +
> +		mreq = next_request(mep);
> +
> +		if (mreq == NULL || mreq->gpd != gpd) {
> +			dev_err(mtu->dev, "no correct RX req is found\n");
> +			break;
> +		}
> +		req = &mreq->request;
> +
> +		req->actual = le16_to_cpu(gpd->buf_len);
> +		mtu3_req_complete(mep, req, 0);
> +
> +		gpd = advance_deq_gpd(ring);
> +	}
> +
> +	dev_dbg(mtu->dev, "%s EP%d, deq=%p, enq=%p, complete\n",
> +		__func__, epnum, ring->dequeue, ring->enqueue);
> +}
> +
> +static void qmu_done_tasklet(unsigned long data)
> +{
> +	struct mtu3 *mtu = (struct mtu3 *)data;
> +	unsigned long flags;
> +	u32 qmu_val;
> +	int i;
> +
> +	spin_lock_irqsave(&mtu->lock, flags);
> +
> +	qmu_val = mtu->qmu_done_intr;
> +
> +	mtu->qmu_done_intr = 0;
> +
> +	for (i = 1; i < mtu->num_eps; i++) {
> +		if (qmu_val & QMU_RX_DONE_INT(i))
> +			qmu_done_rx(mtu, i);
> +		if (qmu_val & QMU_TX_DONE_INT(i))
> +			qmu_done_tx(mtu, i);
> +	}
> +	spin_unlock_irqrestore(&mtu->lock, flags);
> +}
> +
> +static void qmu_exception_isr(struct mtu3 *mtu, u32 qmu_status)
> +{
> +	void __iomem *mbase = mtu->mac_base;
> +	u32 errval;
> +	int i;
> +
> +	if ((qmu_status & RXQ_CSERR_INT) || (qmu_status & RXQ_LENERR_INT)) {
> +		errval = mtu3_readl(mbase, U3D_RQERRIR0);
> +		for (i = 1; i < mtu->num_eps; i++) {
> +			if (errval & QMU_RX_CS_ERR(i))
> +				dev_err(mtu->dev, "Rx %d CS error!\n", i);
> +
> +			if (errval & QMU_RX_LEN_ERR(i))
> +				dev_err(mtu->dev, "RX %d Length error\n", i);
> +		}
> +		mtu3_writel(mbase, U3D_RQERRIR0, errval);
> +	}
> +
> +	if (qmu_status & RXQ_ZLPERR_INT) {
> +		errval = mtu3_readl(mbase, U3D_RQERRIR1);
> +		for (i = 1; i < mtu->num_eps; i++) {
> +			if (errval & QMU_RX_ZLP_ERR(i))
> +				dev_dbg(mtu->dev, "RX EP%d Recv ZLP\n", i);
> +		}
> +		mtu3_writel(mbase, U3D_RQERRIR1, errval);
> +	}
> +
> +	if ((qmu_status & TXQ_CSERR_INT) || (qmu_status & TXQ_LENERR_INT)) {
> +		errval = mtu3_readl(mbase, U3D_TQERRIR0);
> +		for (i = 1; i < mtu->num_eps; i++) {
> +			if (errval & QMU_TX_CS_ERR(i))
> +				dev_err(mtu->dev, "Tx %d checksum error!\n", i);
> +
> +			if (errval & QMU_TX_LEN_ERR(i))
> +				qmu_tx_zlp_error_handler(mtu, i);
> +		}
> +		mtu3_writel(mbase, U3D_TQERRIR0, errval);
> +	}
> +}
> +
> +irqreturn_t mtu3_qmu_isr(struct mtu3 *mtu)
> +{
> +	void __iomem *mbase = mtu->mac_base;
> +	u32 qmu_status;
> +	u32 qmu_done_status;
> +
> +	/* U3D_QISAR1 is read update */
> +	qmu_status = mtu3_readl(mbase, U3D_QISAR1);
> +	qmu_status &= mtu3_readl(mbase, U3D_QIER1);
> +
> +	qmu_done_status = mtu3_readl(mbase, U3D_QISAR0);
> +	qmu_done_status &= mtu3_readl(mbase, U3D_QIER0);
> +	mtu3_writel(mbase, U3D_QISAR0, qmu_done_status); /* W1C */
> +	dev_dbg(mtu->dev, "=== QMUdone[tx=%x, rx=%x] QMUexp[%x] ===\n",
> +		(qmu_done_status & 0xFFFF), qmu_done_status >> 16,
> +		qmu_status);
> +
> +	if (qmu_done_status) {
> +		/* qmu maybe not handled yet */
> +		mtu->qmu_done_intr |= qmu_done_status;
> +		tasklet_schedule(&mtu->qmu_done);
> +	}
> +
> +	if (qmu_status)
> +		qmu_exception_isr(mtu, qmu_status);
> +
> +	return IRQ_HANDLED;
> +}
> +
> +int mtu3_qmu_init(struct mtu3 *mtu)
> +{
> +
> +	compiletime_assert(QMU_GPD_SIZE == 16, "QMU_GPD size SHOULD be 16B");
> +
> +	mtu->qmu_gpd_pool = dma_pool_create("QMU_GPD", mtu->dev,
> +			QMU_GPD_RING_SIZE, QMU_GPD_SIZE, 0);
> +
> +	if (!mtu->qmu_gpd_pool)
> +		return -ENOMEM;
> +
> +	tasklet_init(&mtu->qmu_done, qmu_done_tasklet, (unsigned long)mtu);
> +
> +	return 0;
> +}
> +
> +int mtu3_qmu_exit(struct mtu3 *mtu)
> +{
> +	tasklet_kill(&mtu->qmu_done);
> +	dma_pool_destroy(mtu->qmu_gpd_pool);
> +
> +	return 0;
> +}
> +
> diff --git a/drivers/usb/mtu3/mtu3_qmu.h b/drivers/usb/mtu3/mtu3_qmu.h
> new file mode 100644
> index 0000000..2e07f5b
> --- /dev/null
> +++ b/drivers/usb/mtu3/mtu3_qmu.h
> @@ -0,0 +1,43 @@
> +/*
> + * mtu3_qmu.h - Queue Management Unit driver header
> + *
> + * Copyright (C) 2016 MediaTek Inc.
> + *
> + * Author: Chunfeng Yun <chunfeng.yun@mediatek.com>
> + *
> + * This software is licensed under the terms of the GNU General Public
> + * License version 2, as published by the Free Software Foundation, and
> + * may be copied, distributed, and modified under those terms.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + * GNU General Public License for more details.
> + *
> + */
> +
> +#ifndef __MTK_QMU_H__
> +#define __MTK_QMU_H__
> +
> +#define MAX_GPD_NUM		64
> +#define QMU_GPD_SIZE		(sizeof(struct qmu_gpd))
> +#define QMU_GPD_RING_SIZE	(MAX_GPD_NUM * QMU_GPD_SIZE)
> +
> +#define GPD_BUF_SIZE		65532
> +
> +void mtu3_qmu_stop(struct mtu3_ep *mep);
> +int mtu3_qmu_start(struct mtu3_ep *mep);
> +void mtu3_qmu_resume(struct mtu3_ep *mep);
> +void mtu3_qmu_flush(struct mtu3_ep *mep);
> +
> +void mtu3_insert_gpd(struct mtu3_ep *mep, struct mtu3_request *mreq);
> +int mtu3_prepare_transfer(struct mtu3_ep *mep);
> +
> +int mtu3_gpd_ring_alloc(struct mtu3_ep *mep);
> +void mtu3_gpd_ring_free(struct mtu3_ep *mep);
> +
> +irqreturn_t mtu3_qmu_isr(struct mtu3 *mtu);
> +int mtu3_qmu_init(struct mtu3 *mtu);
> +int mtu3_qmu_exit(struct mtu3 *mtu);
> +
> +#endif
>

^ permalink raw reply

* Re: [PATCH 4/4] clk: mediatek: Add MT6797 clock support
From: Matthias Brugger @ 2016-10-10 11:06 UTC (permalink / raw)
  To: Mars Cheng, Rob Herring, Marc Zyngier, Mark Rutland,
	Michael Turquette, Stephen Boyd, Erin Lo, James Liao
  Cc: linux-clk, CC Hwang, Loda Choui, Miles Chen, Scott Shu,
	Jades Shih, Yingjoe Chen, My Chuang, linux-kernel, linux-mediatek,
	devicetree, wsd_upstream
In-Reply-To: <1473331794-27542-5-git-send-email-mars.cheng@mediatek.com>



On 09/08/2016 12:49 PM, Mars Cheng wrote:
> Add MT6797 clock support, include topckgen, apmixedsys,
> infracfg and subsystem clocks.
>
> Signed-off-by: Mars Cheng <mars.cheng@mediatek.com>
> ---
>  arch/arm64/boot/dts/mediatek/mt6797.dtsi |   66 ++-
>  drivers/clk/mediatek/Kconfig             |   36 ++
>  drivers/clk/mediatek/Makefile            |    5 +
>  drivers/clk/mediatek/clk-mt6797-img.c    |   87 ++++
>  drivers/clk/mediatek/clk-mt6797-mm.c     |  146 ++++++
>  drivers/clk/mediatek/clk-mt6797-vdec.c   |  102 +++++
>  drivers/clk/mediatek/clk-mt6797-venc.c   |   86 ++++
>  drivers/clk/mediatek/clk-mt6797.c        |  716 ++++++++++++++++++++++++++++++
>  include/dt-bindings/clock/mt6797-clk.h   |  281 ++++++++++++
>  9 files changed, 1514 insertions(+), 11 deletions(-)
>  create mode 100644 drivers/clk/mediatek/clk-mt6797-img.c
>  create mode 100644 drivers/clk/mediatek/clk-mt6797-mm.c
>  create mode 100644 drivers/clk/mediatek/clk-mt6797-vdec.c
>  create mode 100644 drivers/clk/mediatek/clk-mt6797-venc.c
>  create mode 100644 drivers/clk/mediatek/clk-mt6797.c
>  create mode 100644 include/dt-bindings/clock/mt6797-clk.h
>
> diff --git a/arch/arm64/boot/dts/mediatek/mt6797.dtsi b/arch/arm64/boot/dts/mediatek/mt6797.dtsi
> index 66f6442..75d3877 100644
> --- a/arch/arm64/boot/dts/mediatek/mt6797.dtsi
> +++ b/arch/arm64/boot/dts/mediatek/mt6797.dtsi
> @@ -10,6 +10,7 @@
>   * GNU General Public License for more details.
>   */
>
> +#include <dt-bindings/clock/mt6797-clk.h>
>  #include <dt-bindings/interrupt-controller/irq.h>
>  #include <dt-bindings/interrupt-controller/arm-gic.h>
>
> @@ -113,12 +114,6 @@
>  		clock-output-names = "clk32k";
>  	};
>
> -	uart_clk: dummy26m {
> -		compatible = "fixed-clock";
> -		clock-frequency = <26000000>;
> -		#clock-cells = <0>;
> -	};
> -
>  	timer {
>  		compatible = "arm,armv8-timer";
>  		interrupt-parent = <&gic>;
> @@ -132,6 +127,24 @@
>  			     (GIC_CPU_MASK_SIMPLE(10) | IRQ_TYPE_LEVEL_LOW)>;
>  	};
>
> +	topckgen: topckgen@10000000 {
> +		compatible = "mediatek,mt6797-topckgen";
> +		reg = <0 0x10000000 0 0x1000>;
> +		#clock-cells = <1>;
> +	};
> +
> +	infrasys: infracfg_ao@10001000 {
> +		compatible = "mediatek,mt6797-infracfg", "syscon";
> +		reg = <0 0x10001000 0 0x1000>;
> +		#clock-cells = <1>;
> +	};
> +
> +	apmixedsys: apmixed@1000c000 {
> +		compatible = "mediatek,mt6797-apmixedsys";
> +		reg = <0 0x1000c000 0 0x1000>;
> +		#clock-cells = <1>;
> +	};
> +
>  	sysirq: intpol-controller@10200620 {
>  		compatible = "mediatek,mt6797-sysirq",
>  			     "mediatek,mt6577-sysirq";
> @@ -148,7 +161,9 @@
>  			     "mediatek,mt6577-uart";
>  		reg = <0 0x11002000 0 0x400>;
>  		interrupts = <GIC_SPI 91 IRQ_TYPE_LEVEL_LOW>;
> -		clocks = <&uart_clk>;
> +		clocks = <&infrasys CLK_INFRA_UART0>,
> +			 <&infrasys CLK_INFRA_AP_DMA>;
> +		clock-names = "baud", "bus";
>  		status = "disabled";
>  	};
>
> @@ -157,7 +172,9 @@
>  			     "mediatek,mt6577-uart";
>  		reg = <0 0x11003000 0 0x400>;
>  		interrupts = <GIC_SPI 92 IRQ_TYPE_LEVEL_LOW>;
> -		clocks = <&uart_clk>;
> +		clocks = <&infrasys CLK_INFRA_UART1>,
> +			 <&infrasys CLK_INFRA_AP_DMA>;
> +		clock-names = "baud", "bus";
>  		status = "disabled";
>  	};
>
> @@ -166,7 +183,9 @@
>  			     "mediatek,mt6577-uart";
>  		reg = <0 0x11004000 0 0x400>;
>  		interrupts = <GIC_SPI 93 IRQ_TYPE_LEVEL_LOW>;
> -		clocks = <&uart_clk>;
> +		clocks = <&infrasys CLK_INFRA_UART2>,
> +			 <&infrasys CLK_INFRA_AP_DMA>;
> +		clock-names = "baud", "bus";
>  		status = "disabled";
>  	};
>
> @@ -175,10 +194,36 @@
>  			     "mediatek,mt6577-uart";
>  		reg = <0 0x11005000 0 0x400>;
>  		interrupts = <GIC_SPI 94 IRQ_TYPE_LEVEL_LOW>;
> -		clocks = <&uart_clk>;
> +		clocks = <&infrasys CLK_INFRA_UART3>,
> +			 <&infrasys CLK_INFRA_AP_DMA>;
> +		clock-names = "baud", "bus";
>  		status = "disabled";
>  	};
>
> +	mmsys: mmsys_config@14000000 {
> +		compatible = "mediatek,mt6797-mmsys", "syscon";
> +		reg = <0 0x14000000 0 0x1000>;
> +		#clock-cells = <1>;
> +	};
> +
> +	imgsys: imgsys_config@15000000  {
> +		compatible = "mediatek,mt6797-imgsys", "syscon";
> +		reg = <0 0x15000000 0 0x1000>;
> +		#clock-cells = <1>;
> +	};
> +
> +	vdecsys: vdec_gcon@16000000 {
> +		compatible = "mediatek,mt6797-vdecsys", "syscon";
> +		reg = <0 0x16000000 0 0x10000>;
> +		#clock-cells = <1>;
> +	};
> +
> +	vencsys: venc_gcon@17000000 {
> +		compatible = "mediatek,mt6797-vencsys", "syscon";
> +		reg = <0 0x17000000 0 0x1000>;
> +		#clock-cells = <1>;
> +	};
> +
>  	gic: interrupt-controller@19000000 {
>  		compatible = "arm,gic-v3";
>  		#interrupt-cells = <3>;
> @@ -189,5 +234,4 @@
>  		      <0 0x19200000 0 0x200000>,   /* GICR */
>  		      <0 0x10240000 0 0x2000>;     /* GICC */
>  	};
> -
>  };
> diff --git a/drivers/clk/mediatek/Kconfig b/drivers/clk/mediatek/Kconfig
> index 5aa6204..ce91ecb 100644
> --- a/drivers/clk/mediatek/Kconfig
> +++ b/drivers/clk/mediatek/Kconfig
> @@ -56,6 +56,42 @@ config COMMON_CLK_MT2701_BDPSYS
>  	---help---
>  	  This driver supports Mediatek MT2701 bdpsys clocks.
>
> +config COMMON_CLK_MT6797
> +	bool "Clock driver for Mediatek MT6797"
> +	depends on COMMON_CLK
> +	select COMMON_CLK_MEDIATEK
> +	default ARCH_MEDIATEK
> +	---help---
> +	  This driver supports Mediatek MT6797 basic clocks.
> +
> +config COMMON_CLK_MT6797_MMSYS
> +	bool "Clock driver for Mediatek MT6797 mmsys"
> +	depends on COMMON_CLK
> +	select COMMON_CLK_MT6797
> +	---help---
> +	  This driver supports Mediatek MT6797 mmsys clocks.
> +
> +config COMMON_CLK_MT6797_IMGSYS
> +	bool "Clock driver for Mediatek MT6797 imgsys"
> +	depends on COMMON_CLK
> +	select COMMON_CLK_MT6797
> +	---help---
> +	  This driver supports Mediatek MT6797 imgsys clocks.
> +
> +config COMMON_CLK_MT6797_VDECSYS
> +	bool "Clock driver for Mediatek MT6797 vdecsys"
> +	depends on COMMON_CLK
> +	select COMMON_CLK_MT6797
> +	---help---
> +	  This driver supports Mediatek MT6797 vdecsys clocks.
> +
> +config COMMON_CLK_MT6797_VENCSYS
> +	bool "Clock driver for Mediatek MT6797 vencsys"
> +	depends on COMMON_CLK
> +	select COMMON_CLK_MT6797
> +	---help---
> +	  This driver supports Mediatek MT6797 vencsys clocks.
> +
>  config COMMON_CLK_MT8135
>  	bool "Clock driver for Mediatek MT8135"
>  	depends on COMMON_CLK
> diff --git a/drivers/clk/mediatek/Makefile b/drivers/clk/mediatek/Makefile
> index 19ae7ef..a5bae28d 100644
> --- a/drivers/clk/mediatek/Makefile
> +++ b/drivers/clk/mediatek/Makefile
> @@ -7,5 +7,10 @@ obj-$(CONFIG_COMMON_CLK_MT2701_HIFSYS) += clk-mt2701-hif.o
>  obj-$(CONFIG_COMMON_CLK_MT2701_IMGSYS) += clk-mt2701-img.o
>  obj-$(CONFIG_COMMON_CLK_MT2701_MMSYS) += clk-mt2701-mm.o
>  obj-$(CONFIG_COMMON_CLK_MT2701_VDECSYS) += clk-mt2701-vdec.o
> +obj-$(CONFIG_COMMON_CLK_MT6797) += clk-mt6797.o
> +obj-$(CONFIG_COMMON_CLK_MT6797_IMGSYS) += clk-mt6797-img.o
> +obj-$(CONFIG_COMMON_CLK_MT6797_MMSYS) += clk-mt6797-mm.o
> +obj-$(CONFIG_COMMON_CLK_MT6797_VDECSYS) += clk-mt6797-vdec.o
> +obj-$(CONFIG_COMMON_CLK_MT6797_VENCSYS) += clk-mt6797-venc.o
>  obj-$(CONFIG_COMMON_CLK_MT8135) += clk-mt8135.o
>  obj-$(CONFIG_COMMON_CLK_MT8173) += clk-mt8173.o
> diff --git a/drivers/clk/mediatek/clk-mt6797-img.c b/drivers/clk/mediatek/clk-mt6797-img.c
> new file mode 100644
> index 0000000..4ecd201
> --- /dev/null
> +++ b/drivers/clk/mediatek/clk-mt6797-img.c
> @@ -0,0 +1,87 @@
> +/* Copyright (c) 2016 MediaTek Inc.
> + * Author: Kevin Chen <kevin-cw.chen@mediatek.com>
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License version 2 as
> + * published by the Free Software Foundation.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + * GNU General Public License for more details.
> + */
> +
> +#include <linux/clk.h>
> +#include <linux/platform_device.h>
> +#include <dt-bindings/clock/mt6797-clk.h>
> +
> +#include "clk-mtk.h"
> +#include "clk-gate.h"
> +
> +static const struct mtk_gate_regs img_cg_regs = {
> +	.set_ofs = 0x0004,
> +	.clr_ofs = 0x0008,
> +	.sta_ofs = 0x0000,
> +};
> +
> +#define GATE_IMG(_id, _name, _parent, _shift) {		\
> +		.id = _id,				\
> +		.name = _name,				\
> +		.parent_name = _parent,			\
> +		.regs = &img_cg_regs,			\
> +		.shift = _shift,			\
> +		.ops = &mtk_clk_gate_ops_setclr,	\
> +	}
> +
> +static const struct mtk_gate img_clks[] = {
> +	GATE_IMG(CLK_IMG_FDVT, "img_fdvt", "mm_sel", 11),
> +	GATE_IMG(CLK_IMG_DPE, "img_dpe", "mm_sel", 10),
> +	GATE_IMG(CLK_IMG_DIP, "img_dip", "mm_sel", 6),
> +	GATE_IMG(CLK_IMG_LARB6, "img_larb6", "mm_sel", 0),
> +};
> +
> +static int mtk_imgsys_init(struct device *dev)
> +{
> +	struct clk_onecell_data *clk_data;
> +	int r;
> +
> +	clk_data = mtk_alloc_clk_data(CLK_IMG_NR);
> +	if (!clk_data) {
> +		pr_err("%s: alloc failed\n", __func__);
> +		goto alloc_err;
> +	}
> +
> +	mtk_clk_register_gates(dev->of_node, img_clks, ARRAY_SIZE(img_clks),
> +			       clk_data);
> +
> +	r = of_clk_add_provider(dev->of_node, of_clk_src_onecell_get,
> +				clk_data);
> +	if (r)
> +		pr_err("%s: could not register clock provider: %d\n",
> +		       __func__, r);
> +
> +	return r;
> +
> +alloc_err:
> +	return -ENOMEM;
> +}
> +
> +static const struct of_device_id of_match_clk_mt6797_img[] = {
> +	{ .compatible = "mediatek,mt6797-imgsys", },
> +	{}
> +};
> +
> +static int clk_mt6797_img_probe(struct platform_device *pdev)
> +{
> +	return mtk_imgsys_init(&pdev->dev);
> +}
> +
> +static struct platform_driver clk_mt6797_img_drv = {
> +	.probe = clk_mt6797_img_probe,
> +	.driver = {
> +		.name = "clk-mt6797-img",
> +		.of_match_table = of_match_clk_mt6797_img,
> +	},
> +};
> +
> +builtin_platform_driver(clk_mt6797_img_drv);
> diff --git a/drivers/clk/mediatek/clk-mt6797-mm.c b/drivers/clk/mediatek/clk-mt6797-mm.c
> new file mode 100644
> index 0000000..77f0342
> --- /dev/null
> +++ b/drivers/clk/mediatek/clk-mt6797-mm.c
> @@ -0,0 +1,146 @@
> +/*
> + * Copyright (c) 2016 MediaTek Inc.
> + * Author: Kevin Chen <kevin-cw.chen@mediatek.com>
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License version 2 as
> + * published by the Free Software Foundation.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + * GNU General Public License for more details.
> + */
> +
> +#include <linux/clk.h>
> +#include <linux/platform_device.h>
> +#include <dt-bindings/clock/mt6797-clk.h>
> +
> +#include "clk-mtk.h"
> +#include "clk-gate.h"
> +
> +static const struct mtk_gate_regs mm0_cg_regs = {
> +	.set_ofs = 0x0104,
> +	.clr_ofs = 0x0108,
> +	.sta_ofs = 0x0100,
> +};
> +
> +static const struct mtk_gate_regs mm1_cg_regs = {
> +	.set_ofs = 0x0114,
> +	.clr_ofs = 0x0118,
> +	.sta_ofs = 0x0110,
> +};
> +
> +#define GATE_MM0(_id, _name, _parent, _shift) {			\
> +	.id = _id,					\
> +	.name = _name,					\
> +	.parent_name = _parent,				\
> +	.regs = &mm0_cg_regs,				\
> +	.shift = _shift,				\
> +	.ops = &mtk_clk_gate_ops_setclr,		\
> +}
> +
> +#define GATE_MM1(_id, _name, _parent, _shift) {			\
> +	.id = _id,					\
> +	.name = _name,					\
> +	.parent_name = _parent,				\
> +	.regs = &mm1_cg_regs,				\
> +	.shift = _shift,				\
> +	.ops = &mtk_clk_gate_ops_setclr,		\
> +}
> +
> +static const struct mtk_gate mm_clks[] = {
> +	GATE_MM0(CLK_MM_SMI_COMMON, "mm_smi_common", "mm_sel", 0),
> +	GATE_MM0(CLK_MM_SMI_LARB0, "mm_smi_larb0", "mm_sel", 1),
> +	GATE_MM0(CLK_MM_SMI_LARB5, "mm_smi_larb5", "mm_sel", 2),
> +	GATE_MM0(CLK_MM_CAM_MDP, "mm_cam_mdp", "mm_sel", 3),
> +	GATE_MM0(CLK_MM_MDP_RDMA0, "mm_mdp_rdma0", "mm_sel", 4),
> +	GATE_MM0(CLK_MM_MDP_RDMA1, "mm_mdp_rdma1", "mm_sel", 5),
> +	GATE_MM0(CLK_MM_MDP_RSZ0, "mm_mdp_rsz0", "mm_sel", 6),
> +	GATE_MM0(CLK_MM_MDP_RSZ1, "mm_mdp_rsz1", "mm_sel", 7),
> +	GATE_MM0(CLK_MM_MDP_RSZ2, "mm_mdp_rsz2", "mm_sel", 8),
> +	GATE_MM0(CLK_MM_MDP_TDSHP, "mm_mdp_tdshp", "mm_sel", 9),
> +	GATE_MM0(CLK_MM_MDP_COLOR, "mm_mdp_color", "mm_sel", 10),
> +	GATE_MM0(CLK_MM_MDP_WDMA, "mm_mdp_wdma", "mm_sel", 11),
> +	GATE_MM0(CLK_MM_MDP_WROT0, "mm_mdp_wrot0", "mm_sel", 12),
> +	GATE_MM0(CLK_MM_MDP_WROT1, "mm_mdp_wrot1", "mm_sel", 13),
> +	GATE_MM0(CLK_MM_FAKE_ENG, "mm_fake_eng", "mm_sel", 14),
> +	GATE_MM0(CLK_MM_DISP_OVL0, "mm_disp_ovl0", "mm_sel", 15),
> +	GATE_MM0(CLK_MM_DISP_OVL1, "mm_disp_ovl1", "mm_sel", 16),
> +	GATE_MM0(CLK_MM_DISP_OVL0_2L, "mm_disp_ovl0_2l", "mm_sel", 17),
> +	GATE_MM0(CLK_MM_DISP_OVL1_2L, "mm_disp_ovl1_2l", "mm_sel", 18),
> +	GATE_MM0(CLK_MM_DISP_RDMA0, "mm_disp_rdma0", "mm_sel", 19),
> +	GATE_MM0(CLK_MM_DISP_RDMA1, "mm_disp_rdma1", "mm_sel", 20),
> +	GATE_MM0(CLK_MM_DISP_WDMA0, "mm_disp_wdma0", "mm_sel", 21),
> +	GATE_MM0(CLK_MM_DISP_WDMA1, "mm_disp_wdma1", "mm_sel", 22),
> +	GATE_MM0(CLK_MM_DISP_COLOR, "mm_disp_color", "mm_sel", 23),
> +	GATE_MM0(CLK_MM_DISP_CCORR, "mm_disp_ccorr", "mm_sel", 24),
> +	GATE_MM0(CLK_MM_DISP_AAL, "mm_disp_aal", "mm_sel", 25),
> +	GATE_MM0(CLK_MM_DISP_GAMMA, "mm_disp_gamma", "mm_sel", 26),
> +	GATE_MM0(CLK_MM_DISP_OD, "mm_disp_od", "mm_sel", 27),
> +	GATE_MM0(CLK_MM_DISP_DITHER, "mm_disp_dither", "mm_sel", 28),
> +	GATE_MM0(CLK_MM_DISP_UFOE, "mm_disp_ufoe", "mm_sel", 29),
> +	GATE_MM0(CLK_MM_DISP_DSC, "mm_disp_dsc", "mm_sel", 30),
> +	GATE_MM0(CLK_MM_DISP_SPLIT, "mm_disp_split", "mm_sel", 31),
> +	GATE_MM1(CLK_MM_DSI0_MM_CLOCK, "mm_dsi0_mm_clock", "mm_sel", 0),
> +	GATE_MM1(CLK_MM_DSI1_MM_CLOCK, "mm_dsi1_mm_clock", "mm_sel", 2),
> +	GATE_MM1(CLK_MM_DPI_MM_CLOCK, "mm_dpi_mm_clock", "mm_sel", 4),
> +	GATE_MM1(CLK_MM_DPI_INTERFACE_CLOCK, "mm_dpi_interface_clock",
> +		 "dpi0_sel", 5),
> +	GATE_MM1(CLK_MM_LARB4_AXI_ASIF_MM_CLOCK, "mm_larb4_axi_asif_mm_clock",
> +		 "mm_sel", 6),
> +	GATE_MM1(CLK_MM_LARB4_AXI_ASIF_MJC_CLOCK, "mm_larb4_axi_asif_mjc_clock",
> +		 "mjc_sel", 7),
> +	GATE_MM1(CLK_MM_DISP_OVL0_MOUT_CLOCK, "mm_disp_ovl0_mout_clock",
> +		 "mm_sel", 8),
> +	GATE_MM1(CLK_MM_FAKE_ENG2, "mm_fake_eng2", "mm_sel", 9),
> +	GATE_MM1(CLK_MM_DSI0_INTERFACE_CLOCK, "mm_dsi0_interface_clock",
> +		 "clk26m", 1),
> +	GATE_MM1(CLK_MM_DSI1_INTERFACE_CLOCK, "mm_dsi1_interface_clock",
> +		 "clk26m", 3),
> +};
> +
> +static void mtk_mmsys_init(struct device *dev)
> +{
> +	struct clk_onecell_data *clk_data;
> +	int r;
> +
> +	clk_data = mtk_alloc_clk_data(CLK_MM_NR);
> +	if (!clk_data) {
> +		pr_err("%s: alloc failed\n", __func__);
> +		goto alloc_err;
> +	}
> +
> +	mtk_clk_register_gates(dev->of_node, mm_clks, ARRAY_SIZE(mm_clks),
> +			       clk_data);
> +
> +	r = of_clk_add_provider(node, of_clk_src_onecell_get, clk_data);

node is undeclared here. The same happens for imgsys, vdecsys and vencsys.

Regards,
Matthias

> +	if (r)
> +		pr_err("%s: could not register clock provider: %d\n",
> +		       __func__, r);
> +
> +	return r;
> +
> +alloc_err:
> +	return -ENOMEM;
> +}
> +
> +static const struct of_device_id of_match_clk_mt6797_mm[] = {
> +	{ .compatible = "mediatek,mt6797-mmsys", },
> +	{}
> +};
> +
> +static int clk_mt6797_mm_probe(struct platform_device *pdev)
> +{
> +	return mtk_mmsys_init(&pdev->dev);
> +}
> +
> +static struct platform_driver clk_mt6797_mm_drv = {
> +	.probe = clk_mt6797_mm_probe,
> +	.driver = {
> +		.name = "clk-mt6797-mm",
> +		.of_match_table = of_match_clk_mt6797_mm,
> +	},
> +};
> +
> +builtin_platform_driver(clk_mt6797_mm_drv);
> diff --git a/drivers/clk/mediatek/clk-mt6797-vdec.c b/drivers/clk/mediatek/clk-mt6797-vdec.c
> new file mode 100644
> index 0000000..48cba6b
> --- /dev/null
> +++ b/drivers/clk/mediatek/clk-mt6797-vdec.c
> @@ -0,0 +1,102 @@
> +/*
> + * Copyright (c) 2016 MediaTek Inc.
> + * Author: Kevin-CW Chen <kevin-cw.chen@mediatek.com>
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License version 2 as
> + * published by the Free Software Foundation.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + * GNU General Public License for more details.
> + */
> +
> +#include <linux/clk.h>
> +#include <linux/platform_device.h>
> +
> +#include "clk-mtk.h"
> +#include "clk-gate.h"
> +
> +#include <dt-bindings/clock/mt6797-clk.h>
> +
> +static const struct mtk_gate_regs vdec0_cg_regs = {
> +	.set_ofs = 0x0000,
> +	.clr_ofs = 0x0004,
> +	.sta_ofs = 0x0000,
> +};
> +
> +static const struct mtk_gate_regs vdec1_cg_regs = {
> +	.set_ofs = 0x0008,
> +	.clr_ofs = 0x000c,
> +	.sta_ofs = 0x0008,
> +};
> +
> +#define GATE_VDEC0(_id, _name, _parent, _shift) {		\
> +	.id = _id,					\
> +	.name = _name,					\
> +	.parent_name = _parent,				\
> +	.regs = &vdec0_cg_regs,				\
> +	.shift = _shift,				\
> +	.ops = &mtk_clk_gate_ops_setclr_inv,		\
> +}
> +
> +#define GATE_VDEC1(_id, _name, _parent, _shift) {		\
> +	.id = _id,					\
> +	.name = _name,					\
> +	.parent_name = _parent,				\
> +	.regs = &vdec1_cg_regs,				\
> +	.shift = _shift,				\
> +	.ops = &mtk_clk_gate_ops_setclr_inv,		\
> +}
> +
> +static const struct mtk_gate vdec_clks[] = {
> +	GATE_VDEC0(CLK_VDEC_CKEN_ENG, "vdec_cken_eng", "vdec_sel", 8),
> +	GATE_VDEC0(CLK_VDEC_ACTIVE, "vdec_active", "vdec_sel", 4),
> +	GATE_VDEC0(CLK_VDEC_CKEN, "vdec_cken", "vdec_sel", 0),
> +	GATE_VDEC1(CLK_VDEC_LARB1_CKEN, "vdec_larb1_cken", "mm_sel", 0),
> +};
> +
> +static void mtk_vdecsys_init(struct device *dev)
> +{
> +	struct clk_onecell_data *clk_data;
> +	int r;
> +
> +	clk_data = mtk_alloc_clk_data(CLK_VDEC_NR);
> +	if (!clk_data) {
> +		pr_err("%s: alloc failed\n", __func__);
> +		goto alloc_err;
> +	}
> +
> +	mtk_clk_register_gates(dev->of_node, vdec_clks, ARRAY_SIZE(vdec_clks),
> +			       clk_data);
> +
> +	r = of_clk_add_provider(dev->of_node, of_clk_src_onecell_get, clk_data);
> +	if (r)
> +		pr_err("%s: could not register clock provider: %d\n",
> +		       __func__, r);
> +	return r;
> +
> +alloc_err:
> +	return -ENOMEM;
> +}
> +
> +static const struct of_device_id of_match_clk_mt6797_vdec[] = {
> +	{ .compatible = "mediatek,mt6797-vdecsys", },
> +	{}
> +};
> +
> +static int clk_mt6797_vdec_probe(struct platform_device *pdev)
> +{
> +	return mtk_vdecsys_init(&pdev->dev);
> +}
> +
> +static struct platform_driver clk_mt6797_vdec_drv = {
> +	.probe = clk_mt6797_vdec_probe,
> +	.driver = {
> +		.name = "clk-mt6797-vdec",
> +		.of_match_table = of_match_clk_mt6797_vdec,
> +	},
> +};
> +
> +builtin_platform_driver(clk_mt6797_vdec_drv);
> diff --git a/drivers/clk/mediatek/clk-mt6797-venc.c b/drivers/clk/mediatek/clk-mt6797-venc.c
> new file mode 100644
> index 0000000..787e010
> --- /dev/null
> +++ b/drivers/clk/mediatek/clk-mt6797-venc.c
> @@ -0,0 +1,86 @@
> +/*
> + * Copyright (c) 2016 MediaTek Inc.
> + * Author: Kevin Chen <kevin-cw.chen@mediatek.com>
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License version 2 as
> + * published by the Free Software Foundation.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + * GNU General Public License for more details.
> + */
> +
> +#include <linux/clk.h>
> +#include <linux/platform_device.h>
> +
> +#include "clk-mtk.h"
> +#include "clk-gate.h"
> +
> +#include <dt-bindings/clock/mt6797-clk.h>
> +
> +static const struct mtk_gate_regs venc_cg_regs = {
> +	.set_ofs = 0x0004,
> +	.clr_ofs = 0x0008,
> +	.sta_ofs = 0x0000,
> +};
> +
> +#define GATE_VENC(_id, _name, _parent, _shift) {	\
> +		.id = _id,				\
> +		.name = _name,				\
> +		.parent_name = _parent,			\
> +		.regs = &venc_cg_regs,			\
> +		.shift = _shift,			\
> +		.ops = &mtk_clk_gate_ops_setclr_inv,	\
> +	}
> +
> +static const struct mtk_gate venc_clks[] = {
> +	GATE_VENC(CLK_VENC_0, "venc_0", "mm_sel", 0),
> +	GATE_VENC(CLK_VENC_1, "venc_1", "venc_sel", 4),
> +	GATE_VENC(CLK_VENC_2, "venc_2", "venc_sel", 8),
> +	GATE_VENC(CLK_VENC_3, "venc_3", "venc_sel", 12),
> +};
> +
> +static void mtk_vencsys_init(struct device_node *node)
> +{
> +	struct clk_onecell_data *clk_data;
> +	int r;
> +
> +	clk_data = mtk_alloc_clk_data(CLK_VENC_NR);
> +	if (!clk_data) {
> +		pr_err("%s: alloc failed\n", __func__);
> +		goto alloc_err;
> +	}
> +
> +	mtk_clk_register_gates(node, venc_clks, ARRAY_SIZE(venc_clks),
> +			       clk_data);
> +
> +	r = of_clk_add_provider(node, of_clk_src_onecell_get, clk_data);
> +	if (r)
> +		pr_err("%s: could not register clock provider: %d\n",
> +		       __func__, r);
> +	return r;
> +alloc_err:
> +	return -ENOMEM;
> +}
> +
> +static const struct of_device_id of_match_clk_mt6797_venc[] = {
> +	{ .compatible = "mediatek,mt6797-vencsys", },
> +	{}
> +};
> +
> +static int clk_mt6797_venc_probe(struct platform_device *pdev)
> +{
> +	return mtk_vencsys_init(pdev->dev.of_node);
> +}
> +
> +static struct platform_driver clk_mt6797_venc_drv = {
> +	.probe = clk_mt6797_venc_probe,
> +	.driver = {
> +		.name = "clk-mt6797-venc",
> +		.of_match_table = of_match_clk_mt6797_venc,
> +	},
> +};
> +
> +builtin_platform_driver(clk_mt6797_venc_drv);
> diff --git a/drivers/clk/mediatek/clk-mt6797.c b/drivers/clk/mediatek/clk-mt6797.c
> new file mode 100644
> index 0000000..a851d0f
> --- /dev/null
> +++ b/drivers/clk/mediatek/clk-mt6797.c
> @@ -0,0 +1,716 @@
> +/*
> + * Copyright (c) 2016 MediaTek Inc.
> + * Author: Kevin Chen <kevin-cw.chen@mediatek.com>
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License version 2 as
> + * published by the Free Software Foundation.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + * GNU General Public License for more details.
> + */
> +
> +#include <linux/clk.h>
> +#include <linux/of.h>
> +#include <linux/of_address.h>
> +#include <linux/of_device.h>
> +#include <linux/platform_device.h>
> +
> +#include "clk-mtk.h"
> +#include "clk-gate.h"
> +
> +#include <dt-bindings/clock/mt6797-clk.h>
> +
> +/*
> + * For some clocks, we don't care what their actual rates are. And these
> + * clocks may change their rate on different products or different scenarios.
> + * So we model these clocks' rate as 0, to denote it's not an actual rate.
> + */
> +
> +static DEFINE_SPINLOCK(mt6797_clk_lock);
> +
> +static const struct mtk_fixed_factor top_divs[] = {
> +	FACTOR(CLK_TOP_SYSPLL_CK, "syspll_ck", "mainpll", 1, 1),
> +	FACTOR(CLK_TOP_SYSPLL_D2, "syspll_d2", "mainpll", 1, 2),
> +	FACTOR(CLK_TOP_SYSPLL1_D2, "syspll1_d2", "syspll_d2", 1, 2),
> +	FACTOR(CLK_TOP_SYSPLL1_D4, "syspll1_d4", "syspll_d2", 1, 4),
> +	FACTOR(CLK_TOP_SYSPLL1_D8, "syspll1_d8", "syspll_d2", 1, 8),
> +	FACTOR(CLK_TOP_SYSPLL1_D16, "syspll1_d16", "syspll_d2", 1, 16),
> +	FACTOR(CLK_TOP_SYSPLL_D3, "syspll_d3", "mainpll", 1, 3),
> +	FACTOR(CLK_TOP_SYSPLL_D3_D3, "syspll_d3_d3", "syspll_d3", 1, 3),
> +	FACTOR(CLK_TOP_SYSPLL2_D2, "syspll2_d2", "syspll_d3", 1, 2),
> +	FACTOR(CLK_TOP_SYSPLL2_D4, "syspll2_d4", "syspll_d3", 1, 4),
> +	FACTOR(CLK_TOP_SYSPLL2_D8, "syspll2_d8", "syspll_d3", 1, 8),
> +	FACTOR(CLK_TOP_SYSPLL_D5, "syspll_d5", "mainpll", 1, 5),
> +	FACTOR(CLK_TOP_SYSPLL3_D2, "syspll3_d2", "syspll_d5", 1, 2),
> +	FACTOR(CLK_TOP_SYSPLL3_D4, "syspll3_d4", "syspll_d5", 1, 4),
> +	FACTOR(CLK_TOP_SYSPLL_D7, "syspll_d7", "mainpll", 1, 7),
> +	FACTOR(CLK_TOP_SYSPLL4_D2, "syspll4_d2", "syspll_d7", 1, 2),
> +	FACTOR(CLK_TOP_SYSPLL4_D4, "syspll4_d4", "syspll_d7", 1, 4),
> +	FACTOR(CLK_TOP_UNIVPLL_CK, "univpll_ck", "univpll", 1, 1),
> +	FACTOR(CLK_TOP_UNIVPLL_D7, "univpll_d7", "univpll", 1, 7),
> +	FACTOR(CLK_TOP_UNIVPLL_D26, "univpll_d26", "univpll", 1, 26),
> +	FACTOR(CLK_TOP_SSUSB_PHY_48M_CK, "ssusb_phy_48m_ck", "univpll", 1, 1),
> +	FACTOR(CLK_TOP_USB_PHY48M_CK, "usb_phy48m_ck", "univpll", 1, 1),
> +	FACTOR(CLK_TOP_UNIVPLL_D2, "univpll_d2", "univpll", 1, 2),
> +	FACTOR(CLK_TOP_UNIVPLL1_D2, "univpll1_d2", "univpll_d2", 1, 2),
> +	FACTOR(CLK_TOP_UNIVPLL1_D4, "univpll1_d4", "univpll_d2", 1, 4),
> +	FACTOR(CLK_TOP_UNIVPLL1_D8, "univpll1_d8", "univpll_d2", 1, 8),
> +	FACTOR(CLK_TOP_UNIVPLL_D3, "univpll_d3", "univpll", 1, 3),
> +	FACTOR(CLK_TOP_UNIVPLL2_D2, "univpll2_d2", "univpll", 1, 2),
> +	FACTOR(CLK_TOP_UNIVPLL2_D4, "univpll2_d4", "univpll", 1, 4),
> +	FACTOR(CLK_TOP_UNIVPLL2_D8, "univpll2_d8", "univpll", 1, 8),
> +	FACTOR(CLK_TOP_UNIVPLL_D5, "univpll_d5", "univpll", 1, 5),
> +	FACTOR(CLK_TOP_UNIVPLL3_D2, "univpll3_d2", "univpll_d5", 1, 2),
> +	FACTOR(CLK_TOP_UNIVPLL3_D4, "univpll3_d4", "univpll_d5", 1, 4),
> +	FACTOR(CLK_TOP_UNIVPLL3_D8, "univpll3_d8", "univpll_d5", 1, 8),
> +	FACTOR(CLK_TOP_ULPOSC_CK_ORG, "ulposc_ck_org", "ulposc", 1, 1),
> +	FACTOR(CLK_TOP_ULPOSC_CK, "ulposc_ck", "ulposc_ck_org", 1, 3),
> +	FACTOR(CLK_TOP_ULPOSC_D2, "ulposc_d2", "ulposc_ck", 1, 2),
> +	FACTOR(CLK_TOP_ULPOSC_D3, "ulposc_d3", "ulposc_ck", 1, 4),
> +	FACTOR(CLK_TOP_ULPOSC_D4, "ulposc_d4", "ulposc_ck", 1, 8),
> +	FACTOR(CLK_TOP_ULPOSC_D8, "ulposc_d8", "ulposc_ck", 1, 10),
> +	FACTOR(CLK_TOP_ULPOSC_D10, "ulposc_d10", "ulposc_ck_org", 1, 1),
> +	FACTOR(CLK_TOP_APLL1_CK, "apll1_ck", "apll1", 1, 1),
> +	FACTOR(CLK_TOP_APLL2_CK, "apll2_ck", "apll2", 1, 1),
> +	FACTOR(CLK_TOP_MFGPLL_CK, "mfgpll_ck", "mfgpll", 1, 1),
> +	FACTOR(CLK_TOP_MFGPLL_D2, "mfgpll_d2", "mfgpll_ck", 1, 2),
> +	FACTOR(CLK_TOP_IMGPLL_CK, "imgpll_ck", "imgpll", 1, 1),
> +	FACTOR(CLK_TOP_IMGPLL_D2, "imgpll_d2", "imgpll_ck", 1, 2),
> +	FACTOR(CLK_TOP_IMGPLL_D4, "imgpll_d4", "imgpll_ck", 1, 4),
> +	FACTOR(CLK_TOP_CODECPLL_CK, "codecpll_ck", "codecpll", 1, 1),
> +	FACTOR(CLK_TOP_CODECPLL_D2, "codecpll_d2", "codecpll_ck", 1, 2),
> +	FACTOR(CLK_TOP_VDECPLL_CK, "vdecpll_ck", "vdecpll", 1, 1),
> +	FACTOR(CLK_TOP_TVDPLL_CK, "tvdpll_ck", "tvdpll", 1, 1),
> +	FACTOR(CLK_TOP_TVDPLL_D2, "tvdpll_d2", "tvdpll_ck", 1, 2),
> +	FACTOR(CLK_TOP_TVDPLL_D4, "tvdpll_d4", "tvdpll_ck", 1, 4),
> +	FACTOR(CLK_TOP_TVDPLL_D8, "tvdpll_d8", "tvdpll_ck", 1, 8),
> +	FACTOR(CLK_TOP_TVDPLL_D16, "tvdpll_d16", "tvdpll_ck", 1, 16),
> +	FACTOR(CLK_TOP_MSDCPLL_CK, "msdcpll_ck", "msdcpll", 1, 1),
> +	FACTOR(CLK_TOP_MSDCPLL_D2, "msdcpll_d2", "msdcpll_ck", 1, 2),
> +	FACTOR(CLK_TOP_MSDCPLL_D4, "msdcpll_d4", "msdcpll_ck", 1, 4),
> +	FACTOR(CLK_TOP_MSDCPLL_D8, "msdcpll_d8", "msdcpll_ck", 1, 8),
> +};
> +
> +static const char * const axi_parents[] = {
> +	"clk26m",
> +	"syspll_d7",
> +	"ulposc_axi_ck_mux",
> +};
> +
> +static const char * const ulposc_axi_ck_mux_parents[] = {
> +	"syspll1_d4",
> +	"ulposc_axi_ck_mux_pre",
> +};
> +
> +static const char * const ulposc_axi_ck_mux_pre_parents[] = {
> +	"ulposc_d2",
> +	"ulposc_d3",
> +};
> +
> +static const char * const ddrphycfg_parents[] = {
> +	"clk26m",
> +	"syspll3_d2",
> +	"syspll2_d4",
> +	"syspll1_d8",
> +};
> +
> +static const char * const mm_parents[] = {
> +	"clk26m",
> +	"imgpll_ck",
> +	"univpll1_d2",
> +	"syspll1_d2",
> +};
> +
> +static const char * const pwm_parents[] = {
> +	"clk26m",
> +	"univpll2_d4",
> +	"ulposc_d2",
> +	"ulposc_d3",
> +	"ulposc_d8",
> +	"ulposc_d10",
> +	"ulposc_d4",
> +};
> +
> +static const char * const vdec_parents[] = {
> +	"clk26m",
> +	"vdecpll_ck",
> +	"imgpll_ck",
> +	"syspll_d3",
> +	"univpll_d5",
> +	"clk26m",
> +	"clk26m",
> +};
> +
> +static const char * const venc_parents[] = {
> +	"clk26m",
> +	"codecpll_ck",
> +	"syspll_d3",
> +};
> +
> +static const char * const mfg_parents[] = {
> +	"clk26m",
> +	"mfgpll_ck",
> +	"syspll_d3",
> +	"univpll_d3",
> +};
> +
> +static const char * const camtg[] = {
> +	"clk26m",
> +	"univpll_d26",
> +	"univpll2_d2",
> +};
> +
> +static const char * const uart_parents[] = {
> +	"clk26m",
> +	"univpll2_d8",
> +};
> +
> +static const char * const spi_parents[] = {
> +	"clk26m",
> +	"syspll3_d2",
> +	"syspll2_d4",
> +	"ulposc_spi_ck_mux",
> +};
> +
> +static const char * const ulposc_spi_ck_mux_parents[] = {
> +	"ulposc_d2",
> +	"ulposc_d3",
> +};
> +
> +static const char * const usb20_parents[] = {
> +	"clk26m",
> +	"univpll1_d8",
> +	"syspll4_d2",
> +};
> +
> +static const char * const msdc50_0_hclk_parents[] = {
> +	"clk26m",
> +	"syspll1_d2",
> +	"syspll2_d2",
> +	"syspll4_d2",
> +};
> +
> +static const char * const msdc50_0_parents[] = {
> +	"clk26m",
> +	"msdcpll",
> +	"syspll_d3",
> +	"univpll1_d4",
> +	"syspll2_d2",
> +	"syspll_d7",
> +	"msdcpll_d2",
> +	"univpll1_d2",
> +	"univpll_d3",
> +};
> +
> +static const char * const msdc30_1_parents[] = {
> +	"clk26m",
> +	"univpll2_d2",
> +	"msdcpll_d2",
> +	"univpll1_d4",
> +	"syspll2_d2",
> +	"syspll_d7",
> +	"univpll_d7",
> +};
> +
> +static const char * const msdc30_2_parents[] = {
> +	"clk26m",
> +	"univpll2_d8",
> +	"syspll2_d8",
> +	"syspll1_d8",
> +	"msdcpll_d8",
> +	"syspll3_d4",
> +	"univpll_d26",
> +};
> +
> +static const char * const audio_parents[] = {
> +	"clk26m",
> +	"syspll3_d4",
> +	"syspll4_d4",
> +	"syspll1_d16",
> +};
> +
> +static const char * const aud_intbus_parents[] = {
> +	"clk26m",
> +	"syspll1_d4",
> +	"syspll4_d2",
> +};
> +
> +static const char * const pmicspi_parents[] = {
> +	"clk26m",
> +	"univpll_d26",
> +	"syspll3_d4",
> +	"syspll1_d8",
> +	"ulposc_d4",
> +	"ulposc_d8",
> +	"syspll2_d8",
> +};
> +
> +static const char * const scp_parents[] = {
> +	"clk26m",
> +	"syspll_d3",
> +	"ulposc_ck",
> +	"univpll_d5",
> +};
> +
> +static const char * const atb_parents[] = {
> +	"clk26m",
> +	"syspll1_d2",
> +	"syspll_d5",
> +};
> +
> +static const char * const mjc_parents[] = {
> +	"clk26m",
> +	"imgpll_ck",
> +	"univpll_d5",
> +	"syspll1_d2",
> +};
> +
> +static const char * const dpi0_parents[] = {
> +	"clk26m",
> +	"tvdpll_d2",
> +	"tvdpll_d4",
> +	"tvdpll_d8",
> +	"tvdpll_d16",
> +	"clk26m",
> +	"clk26m",
> +};
> +
> +static const char * const aud_1_parents[] = {
> +	"clk26m",
> +	"apll1_ck",
> +};
> +
> +static const char * const aud_2_parents[] = {
> +	"clk26m",
> +	"apll2_ck",
> +};
> +
> +static const char * const ssusb_top_sys_parents[] = {
> +	"clk26m",
> +	"univpll3_d2",
> +};
> +
> +static const char * const spm_parents[] = {
> +	"clk26m",
> +	"syspll1_d8",
> +};
> +
> +static const char * const bsi_spi_parents[] = {
> +	"clk26m",
> +	"syspll_d3_d3",
> +	"syspll1_d4",
> +	"syspll_d7",
> +};
> +
> +static const char * const audio_h_parents[] = {
> +	"clk26m",
> +	"apll2_ck",
> +	"apll1_ck",
> +	"univpll_d7",
> +};
> +
> +static const char * const mfg_52m_parents[] = {
> +	"clk26m",
> +	"univpll2_d8",
> +	"univpll2_d4",
> +	"univpll2_d4",
> +};
> +
> +static const char * const anc_md32_parents[] = {
> +	"clk26m",
> +	"syspll1_d2",
> +	"univpll_d5",
> +};
> +
> +static const struct mtk_composite top_muxes[] = {
> +	MUX_GATE(CLK_TOP_MUX_ULPOSC_AXI_CK_MUX_PRE, "ulposc_axi_ck_mux_pre",
> +		 ulposc_axi_ck_mux_pre_parents, 0x0040, 3, 1,
> +		 INVALID_MUX_GATE_BIT),
> +	MUX_GATE(CLK_TOP_MUX_ULPOSC_AXI_CK_MUX, "ulposc_axi_ck_mux",
> +		 ulposc_axi_ck_mux_parents, 0x0040, 2, 1, INVALID_MUX_GATE_BIT),
> +	MUX_GATE(CLK_TOP_MUX_AXI, "axi_sel", axi_parents,
> +		 0x0040, 0, 2, INVALID_MUX_GATE_BIT),
> +	MUX_GATE(CLK_TOP_MUX_DDRPHYCFG, "ddrphycfg_sel", ddrphycfg_parents,
> +		 0x0040, 16, 2, INVALID_MUX_GATE_BIT),
> +	MUX_GATE(CLK_TOP_MUX_MM, "mm_sel", mm_parents,
> +		 0x0040, 24, 2, INVALID_MUX_GATE_BIT),
> +	MUX_GATE(CLK_TOP_MUX_PWM, "pwm_sel", pwm_parents, 0x0050, 0, 3, 7),
> +	MUX_GATE(CLK_TOP_MUX_VDEC, "vdec_sel", vdec_parents, 0x0050, 8, 3, 15),
> +	MUX_GATE(CLK_TOP_MUX_VENC, "venc_sel", venc_parents, 0x0050, 16, 2, 23),
> +	MUX_GATE(CLK_TOP_MUX_MFG, "mfg_sel", mfg_parents, 0x0050, 24, 2, 31),
> +	MUX_GATE(CLK_TOP_MUX_CAMTG, "camtg_sel", camtg, 0x0060, 0, 2, 7),
> +	MUX_GATE(CLK_TOP_MUX_UART, "uart_sel", uart_parents, 0x0060, 8, 1, 15),
> +	MUX_GATE(CLK_TOP_MUX_SPI, "spi_sel", spi_parents, 0x0060, 16, 2, 23),
> +	MUX_GATE(CLK_TOP_MUX_ULPOSC_SPI_CK_MUX, "ulposc_spi_ck_mux",
> +		 ulposc_spi_ck_mux_parents, 0x0060, 18, 1,
> +		 INVALID_MUX_GATE_BIT),
> +	MUX_GATE(CLK_TOP_MUX_USB20, "usb20_sel", usb20_parents,
> +		 0x0060, 24, 2, 31),
> +	MUX_GATE(CLK_TOP_MUX_MSDC50_0_HCLK, "msdc50_0_hclk_sel",
> +		 msdc50_0_hclk_parents, 0x0070, 8, 2, INVALID_MUX_GATE_BIT),
> +	MUX_GATE(CLK_TOP_MUX_MSDC50_0, "msdc50_0_sel", msdc50_0_parents,
> +		 0x0070, 16, 4, 23),
> +	MUX_GATE(CLK_TOP_MUX_MSDC30_1, "msdc30_1_sel", msdc30_1_parents,
> +		 0x0070, 24, 3, 31),
> +	MUX_GATE(CLK_TOP_MUX_MSDC30_2, "msdc30_2_sel", msdc30_2_parents,
> +		 0x0080, 0, 3, 7),
> +	MUX_GATE(CLK_TOP_MUX_AUDIO, "audio_sel", audio_parents,
> +		 0x0080, 16, 2, 23),
> +	MUX_GATE(CLK_TOP_MUX_AUD_INTBUS, "aud_intbus_sel", aud_intbus_parents,
> +		 0x0080, 24, 2, INVALID_MUX_GATE_BIT),
> +	MUX_GATE(CLK_TOP_MUX_PMICSPI, "pmicspi_sel", pmicspi_parents,
> +		 0x0090, 0, 3, INVALID_MUX_GATE_BIT),
> +	MUX_GATE(CLK_TOP_MUX_SCP, "scp_sel", scp_parents,
> +		 0x0090, 8, 2, INVALID_MUX_GATE_BIT),
> +	MUX_GATE(CLK_TOP_MUX_ATB, "atb_sel", atb_parents,
> +		 0x0090, 16, 2, INVALID_MUX_GATE_BIT),
> +	MUX_GATE(CLK_TOP_MUX_MJC, "mjc_sel", mjc_parents, 0x0090, 24, 2, 31),
> +	MUX_GATE(CLK_TOP_MUX_DPI0, "dpi0_sel", dpi0_parents, 0x00A0, 0, 3, 7),
> +	MUX_GATE(CLK_TOP_MUX_AUD_1, "aud_1_sel", aud_1_parents,
> +		 0x00A0, 16, 1, 23),
> +	MUX_GATE(CLK_TOP_MUX_AUD_2, "aud_2_sel", aud_2_parents,
> +		 0x00A0, 24, 1, 31),
> +	MUX_GATE(CLK_TOP_MUX_SSUSB_TOP_SYS, "ssusb_top_sys_sel",
> +		 ssusb_top_sys_parents, 0x00B0, 8, 1, INVALID_MUX_GATE_BIT),
> +	MUX_GATE(CLK_TOP_MUX_SPM, "spm_sel", spm_parents,
> +		 0x00C0, 0, 1, INVALID_MUX_GATE_BIT),
> +	MUX_GATE(CLK_TOP_MUX_BSI_SPI, "bsi_spi_sel", bsi_spi_parents,
> +		 0x00C0, 8, 2, INVALID_MUX_GATE_BIT),
> +	MUX_GATE(CLK_TOP_MUX_AUDIO_H, "audio_h_sel", audio_h_parents,
> +		 0x00C0, 16, 2, 23),
> +	MUX_GATE(CLK_TOP_MUX_ANC_MD32, "anc_md32_sel", anc_md32_parents,
> +		 0x00C0, 24, 2, 31),
> +	MUX_GATE(CLK_TOP_MUX_MFG_52M, "mfg_52m_sel", mfg_52m_parents,
> +		 0x0104, 1, 2, INVALID_MUX_GATE_BIT),
> +};
> +
> +static const struct mtk_gate_regs infra0_cg_regs = {
> +	.set_ofs = 0x0080,
> +	.clr_ofs = 0x0084,
> +	.sta_ofs = 0x0090,
> +};
> +
> +static const struct mtk_gate_regs infra1_cg_regs = {
> +	.set_ofs = 0x0088,
> +	.clr_ofs = 0x008c,
> +	.sta_ofs = 0x0094,
> +};
> +
> +static const struct mtk_gate_regs infra2_cg_regs = {
> +	.set_ofs = 0x00a8,
> +	.clr_ofs = 0x00ac,
> +	.sta_ofs = 0x00b0,
> +};
> +
> +#define GATE_ICG0(_id, _name, _parent, _shift) {	\
> +	.id = _id,					\
> +	.name = _name,					\
> +	.parent_name = _parent,				\
> +	.regs = &infra0_cg_regs,			\
> +	.shift = _shift,				\
> +	.ops = &mtk_clk_gate_ops_setclr,		\
> +}
> +
> +#define GATE_ICG1(_id, _name, _parent, _shift) {	\
> +	.id = _id,					\
> +	.name = _name,					\
> +	.parent_name = _parent,				\
> +	.regs = &infra1_cg_regs,			\
> +	.shift = _shift,				\
> +	.ops = &mtk_clk_gate_ops_setclr,		\
> +}
> +
> +#define GATE_ICG2(_id, _name, _parent, _shift) {	\
> +	.id = _id,					\
> +	.name = _name,					\
> +	.parent_name = _parent,				\
> +	.regs = &infra2_cg_regs,			\
> +	.shift = _shift,				\
> +	.ops = &mtk_clk_gate_ops_setclr,		\
> +}
> +
> +static const struct mtk_gate infra_gates[] = {
> +	GATE_ICG0(CLK_INFRA_PMIC_TMR, "infra_pmic_tmr", "ulposc", 0),
> +	GATE_ICG0(CLK_INFRA_PMIC_AP, "infra_pmic_ap", "pmicspi_sel", 1),
> +	GATE_ICG0(CLK_INFRA_PMIC_MD, "infra_pmic_md", "pmicspi_sel", 2),
> +	GATE_ICG0(CLK_INFRA_PMIC_CONN, "infra_pmic_conn", "pmicspi_sel", 3),
> +	GATE_ICG0(CLK_INFRA_SCP, "infra_scp", "scp_sel", 4),
> +	GATE_ICG0(CLK_INFRA_SEJ, "infra_sej", "axi_sel", 5),
> +	GATE_ICG0(CLK_INFRA_APXGPT, "infra_apxgpt", "axi_sel", 6),
> +	GATE_ICG0(CLK_INFRA_SEJ_13M, "infra_sej_13m", "clk26m", 7),
> +	GATE_ICG0(CLK_INFRA_ICUSB, "infra_icusb", "usb20_sel", 8),
> +	GATE_ICG0(CLK_INFRA_GCE, "infra_gce", "axi_sel", 9),
> +	GATE_ICG0(CLK_INFRA_THERM, "infra_therm", "axi_sel", 10),
> +	GATE_ICG0(CLK_INFRA_I2C0, "infra_i2c0", "axi_sel", 11),
> +	GATE_ICG0(CLK_INFRA_I2C1, "infra_i2c1", "axi_sel", 12),
> +	GATE_ICG0(CLK_INFRA_I2C2, "infra_i2c2", "axi_sel", 13),
> +	GATE_ICG0(CLK_INFRA_I2C3, "infra_i2c3", "axi_sel", 14),
> +	GATE_ICG0(CLK_INFRA_PWM_HCLK, "infra_pwm_hclk", "axi_sel", 15),
> +	GATE_ICG0(CLK_INFRA_PWM1, "infra_pwm1", "axi_sel", 16),
> +	GATE_ICG0(CLK_INFRA_PWM2, "infra_pwm2", "axi_sel", 17),
> +	GATE_ICG0(CLK_INFRA_PWM3, "infra_pwm3", "axi_sel", 18),
> +	GATE_ICG0(CLK_INFRA_PWM4, "infra_pwm4", "axi_sel", 19),
> +	GATE_ICG0(CLK_INFRA_PWM, "infra_pwm", "axi_sel", 21),
> +	GATE_ICG0(CLK_INFRA_UART0, "infra_uart0", "uart_sel", 22),
> +	GATE_ICG0(CLK_INFRA_UART1, "infra_uart1", "uart_sel", 23),
> +	GATE_ICG0(CLK_INFRA_UART2, "infra_uart2", "uart_sel", 24),
> +	GATE_ICG0(CLK_INFRA_UART3, "infra_uart3", "uart_sel", 25),
> +	GATE_ICG0(CLK_INFRA_MD2MD_CCIF_0, "infra_md2md_ccif_0", "axi_sel", 27),
> +	GATE_ICG0(CLK_INFRA_MD2MD_CCIF_1, "infra_md2md_ccif_1", "axi_sel", 28),
> +	GATE_ICG0(CLK_INFRA_MD2MD_CCIF_2, "infra_md2md_ccif_2", "axi_sel", 29),
> +	GATE_ICG0(CLK_INFRA_FHCTL, "infra_fhctl", "clk26m", 30),
> +	GATE_ICG0(CLK_INFRA_BTIF, "infra_btif", "axi_sel", 31),
> +	GATE_ICG1(CLK_INFRA_MD2MD_CCIF_3, "infra_md2md_ccif_3", "axi_sel", 0),
> +	GATE_ICG1(CLK_INFRA_SPI, "infra_spi", "spi_sel", 1),
> +	GATE_ICG1(CLK_INFRA_MSDC0, "infra_msdc0", "msdc50_0_sel", 2),
> +	GATE_ICG1(CLK_INFRA_MD2MD_CCIF_4, "infra_md2md_ccif_4", "axi_sel", 3),
> +	GATE_ICG1(CLK_INFRA_MSDC1, "infra_msdc1", "msdc30_1_sel", 4),
> +	GATE_ICG1(CLK_INFRA_MSDC2, "infra_msdc2", "msdc30_2_sel", 5),
> +	GATE_ICG1(CLK_INFRA_MD2MD_CCIF_5, "infra_md2md_ccif_5", "axi_sel", 7),
> +	GATE_ICG1(CLK_INFRA_GCPU, "infra_gcpu", "axi_sel", 8),
> +	GATE_ICG1(CLK_INFRA_TRNG, "infra_trng", "axi_sel", 9),
> +	GATE_ICG1(CLK_INFRA_AUXADC, "infra_auxadc", "clk26m", 10),
> +	GATE_ICG1(CLK_INFRA_CPUM, "infra_cpum", "axi_sel", 11),
> +	GATE_ICG1(CLK_INFRA_AP_C2K_CCIF_0, "infra_ap_c2k_ccif_0",
> +		  "axi_sel", 12),
> +	GATE_ICG1(CLK_INFRA_AP_C2K_CCIF_1, "infra_ap_c2k_ccif_1",
> +		  "axi_sel", 13),
> +	GATE_ICG1(CLK_INFRA_CLDMA, "infra_cldma", "axi_sel", 16),
> +	GATE_ICG1(CLK_INFRA_DISP_PWM, "infra_disp_pwm", "pwm_sel", 17),
> +	GATE_ICG1(CLK_INFRA_AP_DMA, "infra_ap_dma", "axi_sel", 18),
> +	GATE_ICG1(CLK_INFRA_DEVICE_APC, "infra_device_apc", "axi_sel", 20),
> +	GATE_ICG1(CLK_INFRA_L2C_SRAM, "infra_l2c_sram", "mm_sel", 22),
> +	GATE_ICG1(CLK_INFRA_CCIF_AP, "infra_ccif_ap", "axi_sel", 23),
> +	GATE_ICG1(CLK_INFRA_AUDIO, "infra_audio", "axi_sel", 25),
> +	GATE_ICG1(CLK_INFRA_CCIF_MD, "infra_ccif_md", "axi_sel", 26),
> +	GATE_ICG1(CLK_INFRA_DRAMC_F26M, "infra_dramc_f26m", "clk26m", 31),
> +	GATE_ICG2(CLK_INFRA_I2C4, "infra_i2c4", "axi_sel", 0),
> +	GATE_ICG2(CLK_INFRA_I2C_APPM, "infra_i2c_appm", "axi_sel", 1),
> +	GATE_ICG2(CLK_INFRA_I2C_GPUPM, "infra_i2c_gpupm", "axi_sel", 2),
> +	GATE_ICG2(CLK_INFRA_I2C2_IMM, "infra_i2c2_imm", "axi_sel", 3),
> +	GATE_ICG2(CLK_INFRA_I2C2_ARB, "infra_i2c2_arb", "axi_sel", 4),
> +	GATE_ICG2(CLK_INFRA_I2C3_IMM, "infra_i2c3_imm", "axi_sel", 5),
> +	GATE_ICG2(CLK_INFRA_I2C3_ARB, "infra_i2c3_arb", "axi_sel", 6),
> +	GATE_ICG2(CLK_INFRA_I2C5, "infra_i2c5", "axi_sel", 7),
> +	GATE_ICG2(CLK_INFRA_SYS_CIRQ, "infra_sys_cirq", "axi_sel", 8),
> +	GATE_ICG2(CLK_INFRA_SPI1, "infra_spi1", "spi_sel", 10),
> +	GATE_ICG2(CLK_INFRA_DRAMC_B_F26M, "infra_dramc_b_f26m", "clk26m", 11),
> +	GATE_ICG2(CLK_INFRA_ANC_MD32, "infra_anc_md32", "anc_md32_sel", 12),
> +	GATE_ICG2(CLK_INFRA_ANC_MD32_32K, "infra_anc_md32_32k", "clk26m", 13),
> +	GATE_ICG2(CLK_INFRA_DVFS_SPM1, "infra_dvfs_spm1", "axi_sel", 15),
> +	GATE_ICG2(CLK_INFRA_AES_TOP0, "infra_aes_top0", "axi_sel", 16),
> +	GATE_ICG2(CLK_INFRA_AES_TOP1, "infra_aes_top1", "axi_sel", 17),
> +	GATE_ICG2(CLK_INFRA_SSUSB_BUS, "infra_ssusb_bus", "axi_sel", 18),
> +	GATE_ICG2(CLK_INFRA_SPI2, "infra_spi2", "spi_sel", 19),
> +	GATE_ICG2(CLK_INFRA_SPI3, "infra_spi3", "spi_sel", 20),
> +	GATE_ICG2(CLK_INFRA_SPI4, "infra_spi4", "spi_sel", 21),
> +	GATE_ICG2(CLK_INFRA_SPI5, "infra_spi5", "spi_sel", 22),
> +	GATE_ICG2(CLK_INFRA_IRTX, "infra_irtx", "spi_sel", 23),
> +	GATE_ICG2(CLK_INFRA_SSUSB_SYS, "infra_ssusb_sys",
> +		  "ssusb_top_sys_sel", 24),
> +	GATE_ICG2(CLK_INFRA_SSUSB_REF, "infra_ssusb_ref", "clk26m", 9),
> +	GATE_ICG2(CLK_INFRA_AUDIO_26M, "infra_audio_26m", "clk26m", 26),
> +	GATE_ICG2(CLK_INFRA_AUDIO_26M_PAD_TOP, "infra_audio_26m_pad_top",
> +		  "clk26m", 27),
> +	GATE_ICG2(CLK_INFRA_MODEM_TEMP_SHARE, "infra_modem_temp_share",
> +		  "axi_sel", 28),
> +	GATE_ICG2(CLK_INFRA_VAD_WRAP_SOC, "infra_vad_wrap_soc", "axi_sel", 29),
> +	GATE_ICG2(CLK_INFRA_DRAMC_CONF, "infra_dramc_conf", "axi_sel", 30),
> +	GATE_ICG2(CLK_INFRA_DRAMC_B_CONF, "infra_dramc_b_conf", "axi_sel", 31),
> +	GATE_ICG1(CLK_INFRA_MFG_VCG, "infra_mfg_vcg", "mfg_52m_sel", 14),
> +};
> +
> +static const struct mtk_fixed_factor infra_divs[] = {
> +	FACTOR(CLK_INFRA_13M, "clk13m", "clk26m", 1, 2),
> +};
> +
> +#define MT6797_PLL_FMAX		(3000UL * MHZ)
> +
> +#define CON0_MT6797_RST_BAR	BIT(24)
> +
> +#define PLL_B(_id, _name, _reg, _pwr_reg, _en_mask, _flags, _pcwbits,	\
> +			_pd_reg, _pd_shift, _tuner_reg, _pcw_reg,	\
> +			_pcw_shift, _div_table) {			\
> +	.id = _id,						\
> +	.name = _name,						\
> +	.reg = _reg,						\
> +	.pwr_reg = _pwr_reg,					\
> +	.en_mask = _en_mask,					\
> +	.flags = _flags,					\
> +	.rst_bar_mask = CON0_MT6797_RST_BAR,			\
> +	.fmax = MT6797_PLL_FMAX,				\
> +	.pcwbits = _pcwbits,					\
> +	.pd_reg = _pd_reg,					\
> +	.pd_shift = _pd_shift,					\
> +	.tuner_reg = _tuner_reg,				\
> +	.pcw_reg = _pcw_reg,					\
> +	.pcw_shift = _pcw_shift,				\
> +	.div_table = _div_table,				\
> +}
> +
> +#define PLL(_id, _name, _reg, _pwr_reg, _en_mask, _flags, _pcwbits,	\
> +			_pd_reg, _pd_shift, _tuner_reg, _pcw_reg,	\
> +			_pcw_shift)					\
> +		PLL_B(_id, _name, _reg, _pwr_reg, _en_mask, _flags, _pcwbits, \
> +			_pd_reg, _pd_shift, _tuner_reg, _pcw_reg, _pcw_shift, \
> +			NULL)
> +
> +static const struct mtk_pll_data plls[] = {
> +	PLL(CLK_APMIXED_MAINPLL, "mainpll", 0x0220, 0x022C, 0xF0000101, 0, 21,
> +	    0x220, 4, 0x0, 0x224, 0),
> +	PLL(CLK_APMIXED_UNIVPLL, "univpll", 0x0230, 0x023C, 0xFE000011, 0, 7,
> +	    0x230, 4, 0x0, 0x234, 14),
> +	PLL(CLK_APMIXED_MFGPLL, "mfgpll", 0x0240, 0x024C, 0x00000101, 0, 21,
> +	    0x244, 24, 0x0, 0x244, 0),
> +	PLL(CLK_APMIXED_MSDCPLL, "msdcpll", 0x0250, 0x025C, 0x00000121, 0, 21,
> +	    0x250, 4, 0x0, 0x254, 0),
> +	PLL(CLK_APMIXED_IMGPLL, "imgpll", 0x0260, 0x026C, 0x00000121, 0, 21,
> +	    0x260, 4, 0x0, 0x264, 0),
> +	PLL(CLK_APMIXED_TVDPLL, "tvdpll", 0x0270, 0x027C, 0xC0000121, 0, 21,
> +	    0x270, 4, 0x0, 0x274, 0),
> +	PLL(CLK_APMIXED_CODECPLL, "codecpll", 0x0290, 0x029C, 0x00000121, 0, 21,
> +	    0x290, 4, 0x0, 0x294, 0),
> +	PLL(CLK_APMIXED_VDECPLL, "vdecpll", 0x02E4, 0x02F0, 0x00000121, 0, 21,
> +	    0x2E4, 4, 0x0, 0x2E8, 0),
> +	PLL(CLK_APMIXED_APLL1, "apll1", 0x02A0, 0x02B0, 0x00000131, 0, 31,
> +	    0x2A0, 4, 0x2A8, 0x2A4, 0),
> +	PLL(CLK_APMIXED_APLL2, "apll2", 0x02B4, 0x02C4, 0x00000131, 0, 31,
> +	    0x2B4, 4, 0x2BC, 0x2B8, 0),
> +};
> +
> +static struct clk_onecell_data * __init mtk_topckgen_init(struct device *dev)
> +{
> +	struct clk_onecell_data *clk_data;
> +	struct resource mem;
> +	void __iomem *base;
> +
> +	if (of_address_to_resource(dev->of_node, 0, &mem)) {
> +		pr_err("%s: get resource failed\n", __func__);
> +		goto res_err;
> +	}
> +
> +	base = devm_ioremap(dev, mem.start, resource_size(&mem));
> +	if (!base) {
> +		pr_err("%s: ioremap failed\n", __func__);
> +		goto ioremap_err;
> +	}
> +
> +	clk_data = mtk_alloc_clk_data(CLK_TOP_NR);
> +	if (!clk_data) {
> +		pr_err("%s: alloc failed\n", __func__);
> +		goto alloc_err;
> +	}
> +
> +	mtk_clk_register_factors(top_divs, ARRAY_SIZE(top_divs), clk_data);
> +	mtk_clk_register_composites(top_muxes, ARRAY_SIZE(top_muxes), base,
> +				    &mt6797_clk_lock, clk_data);
> +
> +	return clk_data;
> +
> +alloc_err:
> +	devm_iounmap(dev, base);
> +res_err:
> +ioremap_err:
> +	return NULL;
> +}
> +
> +static struct clk_onecell_data * __init mtk_infrasys_init(struct device *dev)
> +{
> +	struct clk_onecell_data *clk_data;
> +
> +	clk_data = mtk_alloc_clk_data(CLK_INFRA_NR);
> +	if (!clk_data) {
> +		pr_err("%s: alloc failed\n", __func__);
> +		goto alloc_err;
> +	}
> +
> +	mtk_clk_register_gates(dev->of_node, infra_gates,
> +			       ARRAY_SIZE(infra_gates), clk_data);
> +	mtk_clk_register_factors(infra_divs, ARRAY_SIZE(infra_divs), clk_data);
> +
> +	return clk_data;
> +
> +alloc_err:
> +	return NULL;
> +}
> +
> +static struct clk_onecell_data * __init mtk_apmixedsys_init(struct device *dev)
> +{
> +	struct clk_onecell_data *clk_data;
> +
> +	clk_data = mtk_alloc_clk_data(CLK_APMIXED_NR);
> +	if (!clk_data) {
> +		pr_err("%s: alloc failed\n", __func__);
> +		goto alloc_err;
> +	}
> +
> +	mtk_clk_register_plls(dev->of_node, plls, ARRAY_SIZE(plls), clk_data);
> +
> +	return clk_data;
> +
> +alloc_err:
> +	return NULL;
> +}
> +
> +static const struct of_device_id of_match_clk_mt6797[] = {
> +	{
> +		.compatible = "mediatek,mt6797-topckgen",
> +		.data = mtk_topckgen_init,
> +	}, {
> +		.compatible = "mediatek,mt6797-infracfg",
> +		.data = mtk_infrasys_init,
> +	}, {
> +		.compatible = "mediatek,mt6797-apmixedsys",
> +		.data = mtk_apmixedsys_init,
> +	}, {
> +		/* sentinel */
> +	}
> +};
> +
> +static int clk_mt6797_probe(struct platform_device *pdev)
> +{
> +	struct clk_onecell_data * (*clk_init)(struct device *);
> +	struct clk_onecell_data *clk_data;
> +	int r;
> +
> +	clk_init = of_device_get_match_data(&pdev->dev);
> +	if (!clk_init) {
> +		pr_err("%s: matched clk not found\n", __func__);
> +		return -EINVAL;
> +	}
> +
> +	clk_data = clk_init(&pdev->dev);
> +	if (!clk_data) {
> +		pr_err("%s: clk init failed\n", __func__);
> +		return -EINVAL;
> +	}
> +
> +	r = of_clk_add_provider(pdev->dev.of_node, of_clk_src_onecell_get,
> +				clk_data);
> +	if (r) {
> +		pr_err("%s: could not register clock provider: %d\n",
> +		       __func__, r);
> +		return r;
> +	}
> +
> +	return 0;
> +}
> +
> +static struct platform_driver clk_mt6797_drv = {
> +	.probe = clk_mt6797_probe,
> +	.driver = {
> +		.name = "clk-mt6797",
> +		.owner = THIS_MODULE,
> +		.of_match_table = of_match_clk_mt6797,
> +	},
> +};
> +
> +static int __init clk_mt6797_init(void)
> +{
> +	return platform_driver_register(&clk_mt6797_drv);
> +}
> +
> +arch_initcall(clk_mt6797_init);
> diff --git a/include/dt-bindings/clock/mt6797-clk.h b/include/dt-bindings/clock/mt6797-clk.h
> new file mode 100644
> index 0000000..6f32e6b
> --- /dev/null
> +++ b/include/dt-bindings/clock/mt6797-clk.h
> @@ -0,0 +1,281 @@
> +/*
> +* Copyright (c) 2016 MediaTek Inc.
> +* Author: Kevin Chen <kevin-cw.chen@mediatek.com>
> +*
> +* This program is free software; you can redistribute it and/or modify
> +* it under the terms of the GNU General Public License version 2 as
> +* published by the Free Software Foundation.
> +*
> +* This program is distributed in the hope that it will be useful,
> +* but WITHOUT ANY WARRANTY; without even the implied warranty of
> +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> +* GNU General Public License for more details.
> +*/
> +
> +#ifndef _DT_BINDINGS_CLK_MT6797_H
> +#define _DT_BINDINGS_CLK_MT6797_H
> +
> +/* TOPCKGEN */
> +#define	CLK_TOP_MUX_ULPOSC_AXI_CK_MUX_PRE	1
> +#define	CLK_TOP_MUX_ULPOSC_AXI_CK_MUX	2
> +#define	CLK_TOP_MUX_AXI	3
> +#define	CLK_TOP_MUX_MEM	4
> +#define	CLK_TOP_MUX_DDRPHYCFG	5
> +#define	CLK_TOP_MUX_MM	6
> +#define	CLK_TOP_MUX_PWM	7
> +#define	CLK_TOP_MUX_VDEC	8
> +#define	CLK_TOP_MUX_VENC	9
> +#define	CLK_TOP_MUX_MFG	10
> +#define	CLK_TOP_MUX_CAMTG	11
> +#define	CLK_TOP_MUX_UART	12
> +#define	CLK_TOP_MUX_SPI	13
> +#define	CLK_TOP_MUX_ULPOSC_SPI_CK_MUX	14
> +#define	CLK_TOP_MUX_USB20	15
> +#define	CLK_TOP_MUX_MSDC50_0_HCLK	16
> +#define	CLK_TOP_MUX_MSDC50_0	17
> +#define	CLK_TOP_MUX_MSDC30_1	18
> +#define	CLK_TOP_MUX_MSDC30_2	19
> +#define	CLK_TOP_MUX_AUDIO	20
> +#define	CLK_TOP_MUX_AUD_INTBUS	21
> +#define	CLK_TOP_MUX_PMICSPI	22
> +#define	CLK_TOP_MUX_SCP	23
> +#define	CLK_TOP_MUX_ATB	24
> +#define	CLK_TOP_MUX_MJC	25
> +#define	CLK_TOP_MUX_DPI0	26
> +#define	CLK_TOP_MUX_AUD_1	27
> +#define	CLK_TOP_MUX_AUD_2	28
> +#define	CLK_TOP_MUX_SSUSB_TOP_SYS	29
> +#define	CLK_TOP_MUX_SPM	30
> +#define	CLK_TOP_MUX_BSI_SPI	31
> +#define	CLK_TOP_MUX_AUDIO_H	32
> +#define	CLK_TOP_MUX_ANC_MD32	33
> +#define	CLK_TOP_MUX_MFG_52M	34
> +#define	CLK_TOP_SYSPLL_CK	35
> +#define	CLK_TOP_SYSPLL_D2	36
> +#define	CLK_TOP_SYSPLL1_D2	37
> +#define	CLK_TOP_SYSPLL1_D4	38
> +#define	CLK_TOP_SYSPLL1_D8	39
> +#define	CLK_TOP_SYSPLL1_D16	40
> +#define	CLK_TOP_SYSPLL_D3	41
> +#define	CLK_TOP_SYSPLL_D3_D3	42
> +#define	CLK_TOP_SYSPLL2_D2	43
> +#define	CLK_TOP_SYSPLL2_D4	44
> +#define	CLK_TOP_SYSPLL2_D8	45
> +#define	CLK_TOP_SYSPLL_D5	46
> +#define	CLK_TOP_SYSPLL3_D2	47
> +#define	CLK_TOP_SYSPLL3_D4	48
> +#define	CLK_TOP_SYSPLL_D7	49
> +#define	CLK_TOP_SYSPLL4_D2	50
> +#define	CLK_TOP_SYSPLL4_D4	51
> +#define	CLK_TOP_UNIVPLL_CK	52
> +#define	CLK_TOP_UNIVPLL_D7	53
> +#define	CLK_TOP_UNIVPLL_D26	54
> +#define	CLK_TOP_SSUSB_PHY_48M_CK	55
> +#define	CLK_TOP_USB_PHY48M_CK	56
> +#define	CLK_TOP_UNIVPLL_D2	57
> +#define	CLK_TOP_UNIVPLL1_D2	58
> +#define	CLK_TOP_UNIVPLL1_D4	59
> +#define	CLK_TOP_UNIVPLL1_D8	60
> +#define	CLK_TOP_UNIVPLL_D3	61
> +#define	CLK_TOP_UNIVPLL2_D2	62
> +#define	CLK_TOP_UNIVPLL2_D4	63
> +#define	CLK_TOP_UNIVPLL2_D8	64
> +#define	CLK_TOP_UNIVPLL_D5	65
> +#define	CLK_TOP_UNIVPLL3_D2	66
> +#define	CLK_TOP_UNIVPLL3_D4	67
> +#define	CLK_TOP_UNIVPLL3_D8	68
> +#define	CLK_TOP_ULPOSC_CK_ORG	69
> +#define	CLK_TOP_ULPOSC_CK	70
> +#define	CLK_TOP_ULPOSC_D2	71
> +#define	CLK_TOP_ULPOSC_D3	72
> +#define	CLK_TOP_ULPOSC_D4	73
> +#define	CLK_TOP_ULPOSC_D8	74
> +#define	CLK_TOP_ULPOSC_D10	75
> +#define	CLK_TOP_APLL1_CK	76
> +#define	CLK_TOP_APLL2_CK	77
> +#define	CLK_TOP_MFGPLL_CK	78
> +#define	CLK_TOP_MFGPLL_D2	79
> +#define	CLK_TOP_IMGPLL_CK	80
> +#define	CLK_TOP_IMGPLL_D2	81
> +#define	CLK_TOP_IMGPLL_D4	82
> +#define	CLK_TOP_CODECPLL_CK	83
> +#define	CLK_TOP_CODECPLL_D2	84
> +#define	CLK_TOP_VDECPLL_CK	85
> +#define	CLK_TOP_TVDPLL_CK	86
> +#define	CLK_TOP_TVDPLL_D2	87
> +#define	CLK_TOP_TVDPLL_D4	88
> +#define	CLK_TOP_TVDPLL_D8	89
> +#define	CLK_TOP_TVDPLL_D16	90
> +#define	CLK_TOP_MSDCPLL_CK	91
> +#define	CLK_TOP_MSDCPLL_D2	92
> +#define	CLK_TOP_MSDCPLL_D4	93
> +#define	CLK_TOP_MSDCPLL_D8	94
> +#define CLK_TOP_NR		95
> +
> +/* APMIXED_SYS */
> +#define CLK_APMIXED_MAINPLL	1
> +#define CLK_APMIXED_UNIVPLL 2
> +#define CLK_APMIXED_MFGPLL	3
> +#define CLK_APMIXED_MSDCPLL	4
> +#define CLK_APMIXED_IMGPLL	5
> +#define CLK_APMIXED_TVDPLL	6
> +#define CLK_APMIXED_CODECPLL	7
> +#define CLK_APMIXED_VDECPLL	8
> +#define CLK_APMIXED_APLL1	9
> +#define CLK_APMIXED_APLL2	10
> +#define CLK_APMIXED_NR	11
> +
> +/* INFRA_SYS */
> +#define	CLK_INFRA_PMIC_TMR	1
> +#define	CLK_INFRA_PMIC_AP	2
> +#define	CLK_INFRA_PMIC_MD	3
> +#define	CLK_INFRA_PMIC_CONN	4
> +#define	CLK_INFRA_SCP	5
> +#define	CLK_INFRA_SEJ	6
> +#define	CLK_INFRA_APXGPT	7
> +#define	CLK_INFRA_SEJ_13M	8
> +#define	CLK_INFRA_ICUSB	9
> +#define	CLK_INFRA_GCE	10
> +#define	CLK_INFRA_THERM	11
> +#define	CLK_INFRA_I2C0	12
> +#define	CLK_INFRA_I2C1	13
> +#define	CLK_INFRA_I2C2	14
> +#define	CLK_INFRA_I2C3	15
> +#define	CLK_INFRA_PWM_HCLK	16
> +#define	CLK_INFRA_PWM1	17
> +#define	CLK_INFRA_PWM2	18
> +#define	CLK_INFRA_PWM3	19
> +#define	CLK_INFRA_PWM4	20
> +#define	CLK_INFRA_PWM	21
> +#define	CLK_INFRA_UART0	22
> +#define	CLK_INFRA_UART1	23
> +#define	CLK_INFRA_UART2	24
> +#define	CLK_INFRA_UART3	25
> +#define	CLK_INFRA_MD2MD_CCIF_0	26
> +#define	CLK_INFRA_MD2MD_CCIF_1	27
> +#define	CLK_INFRA_MD2MD_CCIF_2	28
> +#define	CLK_INFRA_FHCTL	29
> +#define	CLK_INFRA_BTIF	30
> +#define	CLK_INFRA_MD2MD_CCIF_3	31
> +#define	CLK_INFRA_SPI	32
> +#define	CLK_INFRA_MSDC0	33
> +#define	CLK_INFRA_MD2MD_CCIF_4	34
> +#define	CLK_INFRA_MSDC1	35
> +#define	CLK_INFRA_MSDC2	36
> +#define	CLK_INFRA_MD2MD_CCIF_5	37
> +#define	CLK_INFRA_GCPU	38
> +#define	CLK_INFRA_TRNG	39
> +#define	CLK_INFRA_AUXADC	40
> +#define	CLK_INFRA_CPUM	41
> +#define	CLK_INFRA_AP_C2K_CCIF_0	42
> +#define	CLK_INFRA_AP_C2K_CCIF_1	43
> +#define	CLK_INFRA_CLDMA	44
> +#define	CLK_INFRA_DISP_PWM	45
> +#define	CLK_INFRA_AP_DMA	46
> +#define	CLK_INFRA_DEVICE_APC	47
> +#define	CLK_INFRA_L2C_SRAM	48
> +#define	CLK_INFRA_CCIF_AP	49
> +#define	CLK_INFRA_AUDIO	50
> +#define	CLK_INFRA_CCIF_MD	51
> +#define	CLK_INFRA_DRAMC_F26M	52
> +#define	CLK_INFRA_I2C4	53
> +#define	CLK_INFRA_I2C_APPM	54
> +#define	CLK_INFRA_I2C_GPUPM	55
> +#define	CLK_INFRA_I2C2_IMM	56
> +#define	CLK_INFRA_I2C2_ARB	57
> +#define	CLK_INFRA_I2C3_IMM	58
> +#define	CLK_INFRA_I2C3_ARB	59
> +#define	CLK_INFRA_I2C5	60
> +#define	CLK_INFRA_SYS_CIRQ	61
> +#define	CLK_INFRA_SPI1	62
> +#define	CLK_INFRA_DRAMC_B_F26M	63
> +#define	CLK_INFRA_ANC_MD32	64
> +#define	CLK_INFRA_ANC_MD32_32K	65
> +#define	CLK_INFRA_DVFS_SPM1	66
> +#define	CLK_INFRA_AES_TOP0	67
> +#define	CLK_INFRA_AES_TOP1	68
> +#define	CLK_INFRA_SSUSB_BUS	69
> +#define	CLK_INFRA_SPI2	70
> +#define	CLK_INFRA_SPI3	71
> +#define	CLK_INFRA_SPI4	72
> +#define	CLK_INFRA_SPI5	73
> +#define	CLK_INFRA_IRTX	74
> +#define	CLK_INFRA_SSUSB_SYS	75
> +#define	CLK_INFRA_SSUSB_REF	76
> +#define	CLK_INFRA_AUDIO_26M	77
> +#define	CLK_INFRA_AUDIO_26M_PAD_TOP	78
> +#define	CLK_INFRA_MODEM_TEMP_SHARE	79
> +#define	CLK_INFRA_VAD_WRAP_SOC	80
> +#define	CLK_INFRA_DRAMC_CONF	81
> +#define	CLK_INFRA_DRAMC_B_CONF	82
> +#define CLK_INFRA_MFG_VCG 83
> +#define CLK_INFRA_13M 84
> +#define CLK_INFRA_NR 85
> +
> +/* IMG_SYS */
> +#define	CLK_IMG_FDVT	1
> +#define	CLK_IMG_DPE	2
> +#define	CLK_IMG_DIP	3
> +#define	CLK_IMG_LARB6	4
> +#define CLK_IMG_NR	5
> +
> +/* MM_SYS */
> +#define	CLK_MM_SMI_COMMON	1
> +#define	CLK_MM_SMI_LARB0	2
> +#define	CLK_MM_SMI_LARB5	3
> +#define	CLK_MM_CAM_MDP	4
> +#define	CLK_MM_MDP_RDMA0	5
> +#define	CLK_MM_MDP_RDMA1	6
> +#define	CLK_MM_MDP_RSZ0	7
> +#define	CLK_MM_MDP_RSZ1	8
> +#define	CLK_MM_MDP_RSZ2	9
> +#define	CLK_MM_MDP_TDSHP	10
> +#define	CLK_MM_MDP_COLOR	11
> +#define	CLK_MM_MDP_WDMA	12
> +#define	CLK_MM_MDP_WROT0	13
> +#define	CLK_MM_MDP_WROT1	14
> +#define	CLK_MM_FAKE_ENG	15
> +#define	CLK_MM_DISP_OVL0	16
> +#define	CLK_MM_DISP_OVL1	17
> +#define	CLK_MM_DISP_OVL0_2L	18
> +#define	CLK_MM_DISP_OVL1_2L	19
> +#define	CLK_MM_DISP_RDMA0	20
> +#define	CLK_MM_DISP_RDMA1	21
> +#define	CLK_MM_DISP_WDMA0	22
> +#define	CLK_MM_DISP_WDMA1	23
> +#define	CLK_MM_DISP_COLOR	24
> +#define	CLK_MM_DISP_CCORR	25
> +#define	CLK_MM_DISP_AAL	26
> +#define	CLK_MM_DISP_GAMMA	27
> +#define	CLK_MM_DISP_OD	28
> +#define	CLK_MM_DISP_DITHER	29
> +#define	CLK_MM_DISP_UFOE	30
> +#define	CLK_MM_DISP_DSC	31
> +#define	CLK_MM_DISP_SPLIT	32
> +#define	CLK_MM_DSI0_MM_CLOCK	33
> +#define	CLK_MM_DSI1_MM_CLOCK	34
> +#define	CLK_MM_DPI_MM_CLOCK	35
> +#define	CLK_MM_DPI_INTERFACE_CLOCK	36
> +#define	CLK_MM_LARB4_AXI_ASIF_MM_CLOCK	37
> +#define	CLK_MM_LARB4_AXI_ASIF_MJC_CLOCK	38
> +#define	CLK_MM_DISP_OVL0_MOUT_CLOCK	39
> +#define	CLK_MM_FAKE_ENG2	40
> +#define	CLK_MM_DSI0_INTERFACE_CLOCK	41
> +#define	CLK_MM_DSI1_INTERFACE_CLOCK	42
> +#define CLK_MM_NR		43
> +
> +/* VDEC_SYS */
> +#define	CLK_VDEC_CKEN_ENG	1
> +#define	CLK_VDEC_ACTIVE	2
> +#define	CLK_VDEC_CKEN	3
> +#define	CLK_VDEC_LARB1_CKEN	4
> +#define CLK_VDEC_NR		5
> +
> +/* VENC_SYS */
> +#define	CLK_VENC_0	1
> +#define	CLK_VENC_1	2
> +#define	CLK_VENC_2	3
> +#define	CLK_VENC_3	4
> +#define CLK_VENC_NR	5
> +
> +#endif	/* _DT_BINDINGS_CLK_MT6797_H */
>

^ permalink raw reply

* Delivery Notification, ID 000799467
From: FedEx 2Day A.M. @ 2016-10-10 16:33 UTC (permalink / raw)
  To: linux-mediatek-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r

[-- Attachment #1: Type: text/plain, Size: 162 bytes --]

Dear Customer,

Courier was unable to deliver the parcel to you.
Shipment Label is attached to this email.

Yours faithfully,
Stephen Dunn,
Sr. Support Manager.


[-- Attachment #2: 000799467.zip --]
[-- Type: application/zip, Size: 6068 bytes --]

[-- Attachment #3: Type: text/plain, Size: 200 bytes --]

_______________________________________________
Linux-mediatek mailing list
Linux-mediatek-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r@public.gmane.org
http://lists.infradead.org/mailman/listinfo/linux-mediatek

^ permalink raw reply

* Re: [PATCH v14 2/4] CMDQ: Mediatek CMDQ driver
From: Horng-Shyang Liao @ 2016-10-11  2:40 UTC (permalink / raw)
  To: Jassi Brar
  Cc: CK Hu, Daniel Kurtz, Monica Wang, Jiaguang Zhang, Nicolas Boichat,
	Jassi Brar, cawa cheng, Bibby Hsieh, YT Shen, Damon Chu,
	Devicetree List, Sascha Hauer, Daoyuan Huang, Sascha Hauer,
	Glory Hung, Rob Herring,
	linux-mediatek-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r, Matthias Brugger,
	"linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r@public.gmane.org" <linux-arm->
In-Reply-To: <CAJe_ZheptRQSmQp2gagqUyXqkOpi1qaTo8QPDTFpQ-+62B6kUw-JsoAwUIsXosN+BqQ9rBEUg@public.gmane.org>

On Thu, 2016-10-06 at 18:40 +0530, Jassi Brar wrote:
> On 6 October 2016 at 18:31, Horng-Shyang Liao <hs.liao-NuS5LvNUpcJWk0Htik3J/w@public.gmane.org> wrote:
> 
> > Back to our original statement, we need to flush all tasks to queue
> > in GCE HW; i.e. we need to use mbox_client_txdone after
> > mbox_send_message, or send tx_done once mailbox controller receive
> > message (task). However, we still need a way to notice done tasks to
> > clients. Currently, we don't have a good way to call callback in mailbox
> > framework. Therefore, CMDQ driver has its owner callback functions.
> >
> mbox_client_txdone() is called by the client driver when only it knows
> the messages has been transmitted (i.e your submitted tasks are done).
> Obviously the client driver should do any callbacks to its users
> upstream.

Hi Jassi,

In current CMDQ driver, mbox_client_txdone() is called to prevent the
blocking of chan->active_req. It is not the real point of CMDQ task
done, so the client driver cannot do any callbacks to its user upstream.

(1) If we don't use mbox_client_txdone(), could you tell us an
    alternative way to prevent the blocking of chan->active_req?
    And then we can use tx_done when CMDQ task is relly done.
(2) If we use mbox_client_txdone() to prevent the blocking of
    chan->active_req, could CMDQ driver just uses self-defined callback
    function to notice client driver CMDQ task done?

Thanks,
HS


--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply


This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox