LinuxPPC-Dev Archive on lore.kernel.org
 help / color / mirror / Atom feed
* Re: Gianfar TCP checksumming broken in 2.6.35+
From: Alex Dubov @ 2011-01-18  7:56 UTC (permalink / raw)
  To: mlcreech; +Cc: linuxppc-dev, davem

It appears that I'm hitting an exactly the same problem with my MPC8548=0Ar=
ev2.0 (errata number eTSEC 49).=0A=0AConsidering that it's close to 3 month=
 now since this most unfortunate=0Abug was reported, was there any resoluti=
on/patch that can fix it for good?=0A=0A=0A=0A      

^ permalink raw reply

* [PATCH] Fix masking of interrupts for 52xx GPT IRQ.
From: Henk Stegeman @ 2011-01-15  1:28 UTC (permalink / raw)
  To: linuxppc-dev; +Cc: Henk Stegeman
In-Reply-To: <fa686aa41003181102i2a702c61h8ecfe05568c2a42e@mail.gmail.com>

When using the GPT as interrupt controller interupts were missing.
It turned out the IrqEn bit of the GPT is not a mask bit, but it also
disables interrupt generation. This modification masks interrupt one
level down the cascade. Note that masking one level down the cascade
is only valid here because the GPT as interrupt ontroller only serves
one IRQ.

Signed-off-by: Henk Stegeman <henk.stegeman@gmail.com>
---
 arch/powerpc/platforms/52xx/mpc52xx_gpt.c |   25 ++++++++++++++++++++++---
 1 files changed, 22 insertions(+), 3 deletions(-)

diff --git a/arch/powerpc/platforms/52xx/mpc52xx_gpt.c b/arch/powerpc/platforms/52xx/mpc52xx_gpt.c
index 6f8ebe1..9ae2045 100644
--- a/arch/powerpc/platforms/52xx/mpc52xx_gpt.c
+++ b/arch/powerpc/platforms/52xx/mpc52xx_gpt.c
@@ -91,7 +91,7 @@ struct mpc52xx_gpt_priv {
 	struct irq_host *irqhost;
 	u32 ipb_freq;
 	u8 wdt_mode;
-
+	int cascade_virq;
 #if defined(CONFIG_GPIOLIB)
 	struct of_gpio_chip of_gc;
 #endif
@@ -136,18 +136,35 @@ DEFINE_MUTEX(mpc52xx_gpt_list_mutex);
 static void mpc52xx_gpt_irq_unmask(unsigned int virq)
 {
 	struct mpc52xx_gpt_priv *gpt = get_irq_chip_data(virq);
+
+	enable_irq(gpt->cascade_virq);
+
+}
+
+static void mpc52xx_gpt_irq_mask(unsigned int virq)
+{
+	struct mpc52xx_gpt_priv *gpt = get_irq_chip_data(virq);
+
+	disable_irq(gpt->cascade_virq);
+}
+
+static void mpc52xx_gpt_irq_enable(unsigned int virq)
+{
+	struct mpc52xx_gpt_priv *gpt = get_irq_chip_data(virq);
 	unsigned long flags;
 
+	dev_dbg(gpt->dev, "%s %d\n", __func__, virq);
 	spin_lock_irqsave(&gpt->lock, flags);
 	setbits32(&gpt->regs->mode, MPC52xx_GPT_MODE_IRQ_EN);
 	spin_unlock_irqrestore(&gpt->lock, flags);
 }
 
-static void mpc52xx_gpt_irq_mask(unsigned int virq)
+static void mpc52xx_gpt_irq_disable(unsigned int virq)
 {
 	struct mpc52xx_gpt_priv *gpt = get_irq_chip_data(virq);
 	unsigned long flags;
 
+	dev_dbg(gpt->dev, "%s %d\n", __func__, virq);
 	spin_lock_irqsave(&gpt->lock, flags);
 	clrbits32(&gpt->regs->mode, MPC52xx_GPT_MODE_IRQ_EN);
 	spin_unlock_irqrestore(&gpt->lock, flags);
@@ -184,6 +201,8 @@ static struct irq_chip mpc52xx_gpt_irq_chip = {
 	.name = "MPC52xx GPT",
 	.unmask = mpc52xx_gpt_irq_unmask,
 	.mask = mpc52xx_gpt_irq_mask,
+	.enable = mpc52xx_gpt_irq_enable,
+	.disable = mpc52xx_gpt_irq_disable,
 	.ack = mpc52xx_gpt_irq_ack,
 	.set_type = mpc52xx_gpt_irq_set_type,
 };
@@ -268,7 +287,7 @@ mpc52xx_gpt_irq_setup(struct mpc52xx_gpt_priv *gpt, struct device_node *node)
 	if ((mode & MPC52xx_GPT_MODE_MS_MASK) == 0)
 		out_be32(&gpt->regs->mode, mode | MPC52xx_GPT_MODE_MS_IC);
 	spin_unlock_irqrestore(&gpt->lock, flags);
-
+	gpt->cascade_virq = cascade_virq;
 	dev_dbg(gpt->dev, "%s() complete. virq=%i\n", __func__, cascade_virq);
 }
 
-- 
1.5.4.3

^ permalink raw reply related

* [PATCH] powerpc: perf: Fix frequency calculation for overflowing counters (FSL version)
From: Anton Blanchard @ 2011-01-18 10:44 UTC (permalink / raw)
  To: Peter Zijlstra
  Cc: linux-kernel, Paul Mackerras, Arnaldo Carvalho de Melo,
	Scott Wood, Ingo Molnar, linuxppc-dev
In-Reply-To: <1295285881.30950.218.camel@laptop>


> > Does perf_event_fsl_emb.c need this as well (it has almost the same
> > record_and_restart code)? 
> 
> I would think so.

Good point:


When fixing the frequency calculations for perf on powerpc I forgot
to fix the FSL version.

If we dont set event->hw.last_period the frequency to period calculations
in perf go haywire and we continually throttle/unthrottle the PMU.

Signed-off-by: Anton Blanchard <anton@samba.org>
---

This is only compile tested.

Index: powerpc.git/arch/powerpc/kernel/perf_event_fsl_emb.c
===================================================================
--- powerpc.git.orig/arch/powerpc/kernel/perf_event_fsl_emb.c	2011-01-18 21:23:57.761644115 +1100
+++ powerpc.git/arch/powerpc/kernel/perf_event_fsl_emb.c	2011-01-18 21:25:28.994477247 +1100
@@ -596,6 +596,7 @@ static void record_and_restart(struct pe
 			if (left <= 0)
 				left = period;
 			record = 1;
+			event->hw.last_period = event->hw.sample_period;
 		}
 		if (left < 0x80000000LL)
 			val = 0x80000000LL - left;

^ permalink raw reply

* Re: [PATCH] ATA: Add FSL sata v2 controller support
From: Sergei Shtylyov @ 2011-01-18 11:27 UTC (permalink / raw)
  To: Zang Roy-R61911
  Cc: Xu Lei-B33228, Sergei Shtylyov, jgarzik@pobox.com,
	linux-ide@vger.kernel.org, Gala Kumar-B11780,
	linuxppc-dev@lists.ozlabs.org
In-Reply-To: <2239AC579C7D3646A720227A37E0268109001B@039-SN1MPN1-004.039d.mgd.msft.net>

Hello.

On 18-01-2011 6:01, Zang Roy-R61911 wrote:

>>> In FSL sata v2 block, the snoop bit of PRDT Word3 description
>>> information is at bit28 instead of bit22.

>>> This patch adds FSL sata v2 probe and resolve this difference.

>>> Signed-off-by: Xulei<B33228@freescale.com>

> [snip]

>>> diff --git a/arch/powerpc/boot/dts/p1022ds.dts
>> b/arch/powerpc/boot/dts/p1022ds.dts
>>> index 2bbecbb..9ad41dd 100644
>>> --- a/arch/powerpc/boot/dts/p1022ds.dts
>>> +++ b/arch/powerpc/boot/dts/p1022ds.dts
>>> @@ -475,14 +475,14 @@
>>>    		};
>>>
>>>    		sata@18000 {
>>> -			compatible = "fsl,mpc8536-sata", "fsl,pq-sata";
>>> +			compatible = "fsl,p1022-sata", "fsl,pq-sata-v2";
>>>    			reg =<0x18000 0x1000>;
>>>    			cell-index =<1>;
>>>    			interrupts =<74 0x2>;
>>>    		};
>>>
>>>    		sata@19000 {
>>> -			compatible = "fsl,mpc8536-sata", "fsl,pq-sata";
>>> +			compatible = "fsl,p1022-sata", "fsl,pq-sata-v2";
>>>    			reg =<0x19000 0x1000>;
>>>    			cell-index =<2>;
>>>    			interrupts =<41 0x2>;

>>      Please put this into the separate patch and push thru the PPC tree.

> I agree to put to them to separate patches, but should we
> pull in the separate patches to one candidate tree to make
> the function work?

    I don't understand. What is "candidate tree"?

> Thanks.
> Roy

WBR, Sergei

^ permalink raw reply

* RE: [PATCH] ATA: Add FSL sata v2 controller support
From: Zang Roy-R61911 @ 2011-01-18 11:43 UTC (permalink / raw)
  To: Sergei Shtylyov
  Cc: linux-ide@vger.kernel.org, Xu Lei-B33228, Gala Kumar-B11780,
	linuxppc-dev@lists.ozlabs.org, jgarzik@pobox.com
In-Reply-To: <4D357918.4040001@ru.mvista.com>



> -----Original Message-----
> From: Sergei Shtylyov [mailto:sshtylyov@mvista.com]
> Sent: Tuesday, January 18, 2011 19:27 PM
> To: Zang Roy-R61911
> Cc: Sergei Shtylyov; Xu Lei-B33228; linuxppc-dev@lists.ozlabs.org; Gala K=
umar-
> B11780; jgarzik@pobox.com; linux-ide@vger.kernel.org
> Subject: Re: [PATCH] ATA: Add FSL sata v2 controller support
>=20
> Hello.
>=20
> On 18-01-2011 6:01, Zang Roy-R61911 wrote:
>=20
> >>> In FSL sata v2 block, the snoop bit of PRDT Word3 description
> >>> information is at bit28 instead of bit22.
>=20
> >>> This patch adds FSL sata v2 probe and resolve this difference.
>=20
> >>> Signed-off-by: Xulei<B33228@freescale.com>
>=20
> > [snip]
>=20
> >>> diff --git a/arch/powerpc/boot/dts/p1022ds.dts
> >> b/arch/powerpc/boot/dts/p1022ds.dts
> >>> index 2bbecbb..9ad41dd 100644
> >>> --- a/arch/powerpc/boot/dts/p1022ds.dts
> >>> +++ b/arch/powerpc/boot/dts/p1022ds.dts
> >>> @@ -475,14 +475,14 @@
> >>>    		};
> >>>
> >>>    		sata@18000 {
> >>> -			compatible =3D "fsl,mpc8536-sata", "fsl,pq-sata";
> >>> +			compatible =3D "fsl,p1022-sata", "fsl,pq-sata-v2";
> >>>    			reg =3D<0x18000 0x1000>;
> >>>    			cell-index =3D<1>;
> >>>    			interrupts =3D<74 0x2>;
> >>>    		};
> >>>
> >>>    		sata@19000 {
> >>> -			compatible =3D "fsl,mpc8536-sata", "fsl,pq-sata";
> >>> +			compatible =3D "fsl,p1022-sata", "fsl,pq-sata-v2";
> >>>    			reg =3D<0x19000 0x1000>;
> >>>    			cell-index =3D<2>;
> >>>    			interrupts =3D<41 0x2>;
>=20
> >>      Please put this into the separate patch and push thru the PPC tre=
e.
>=20
> > I agree to put to them to separate patches, but should we
> > pull in the separate patches to one candidate tree to make
> > the function work?
>=20
>     I don't understand. What is "candidate tree"?

Powerpc or ide git repository for upstream to mainline.
It is odd that patches for one function in different trees.
Thanks.
Roy

^ permalink raw reply

* Re: [PATCH] ATA: Add FSL sata v2 controller support
From: Sergei Shtylyov @ 2011-01-18 12:33 UTC (permalink / raw)
  To: Zang Roy-R61911
  Cc: Xu Lei-B33228, Sergei Shtylyov, jgarzik@pobox.com,
	linux-ide@vger.kernel.org, Gala Kumar-B11780,
	linuxppc-dev@lists.ozlabs.org
In-Reply-To: <2239AC579C7D3646A720227A37E02681090699@039-SN1MPN1-004.039d.mgd.msft.net>

Hello.

On 18-01-2011 14:43, Zang Roy-R61911 wrote:

>> On 18-01-2011 6:01, Zang Roy-R61911 wrote:
>>
>>>>> In FSL sata v2 block, the snoop bit of PRDT Word3 description
>>>>> information is at bit28 instead of bit22.

>>>>> This patch adds FSL sata v2 probe and resolve this difference.

>>>>> Signed-off-by: Xulei<B33228@freescale.com>

>>> [snip]

>>>>> diff --git a/arch/powerpc/boot/dts/p1022ds.dts
>>>> b/arch/powerpc/boot/dts/p1022ds.dts
>>>>> index 2bbecbb..9ad41dd 100644
>>>>> --- a/arch/powerpc/boot/dts/p1022ds.dts
>>>>> +++ b/arch/powerpc/boot/dts/p1022ds.dts
>>>>> @@ -475,14 +475,14 @@
>>>>>     		};
>>>>>
>>>>>     		sata@18000 {
>>>>> -			compatible = "fsl,mpc8536-sata", "fsl,pq-sata";
>>>>> +			compatible = "fsl,p1022-sata", "fsl,pq-sata-v2";
>>>>>     			reg =<0x18000 0x1000>;
>>>>>     			cell-index =<1>;
>>>>>     			interrupts =<74 0x2>;
>>>>>     		};
>>>>>
>>>>>     		sata@19000 {
>>>>> -			compatible = "fsl,mpc8536-sata", "fsl,pq-sata";
>>>>> +			compatible = "fsl,p1022-sata", "fsl,pq-sata-v2";
>>>>>     			reg =<0x19000 0x1000>;
>>>>>     			cell-index =<2>;
>>>>>     			interrupts =<41 0x2>;

>>>>       Please put this into the separate patch and push thru the PPC tree.

>>> I agree to put to them to separate patches, but should we
>>> pull in the separate patches to one candidate tree to make
>>> the function work?

>>      I don't understand. What is "candidate tree"?

> Powerpc or ide git repository for upstream to mainline.

    s/ide/libata/

> It is odd that patches for one function in different trees.

    The libata tree doesn't need to be concerned with PPC board specific 
device trees. Although it's up to the maintainer... just cross post the patch, 
and let the corresponding maintainers fogure it out.

> Thanks.
> Roy

WBR, Sergei

^ permalink raw reply

* Problem with Busybox shell
From: MohanReddy koppula @ 2011-01-18 12:54 UTC (permalink / raw)
  To: Linuxppc-dev

Hi All,

I am working on an MPC885 based custom board. I am able to boot up the
linux (linux-2.6.33.7). I could see busybox shell (ash) prompt. But it
is not accepting any inputs, I am not able to enter any command, it
just hangs there. I am using ttyCPM0 terminal.

I suspected if there was any problem in CPM driver interrupts
generation and put some printk's in the interrupt handler and could
see interrupts are raised and data is read, but shell is not taking
the input.

I wrote an init.c and opened the ttyCPM0 and tried to read from it,
but couldn't. I am able to write to ttyCPM0 and see it on the host
minicom.

Please some one help me.

thanks in advance for your help.

-Mohan

^ permalink raw reply

* RE: [PATCH] define binding for fsl mpic interrupt controllers
From: Yoder Stuart-B08248 @ 2011-01-18 19:47 UTC (permalink / raw)
  To: Meador Inge
  Cc: Blanchard, Hollis, devicetree-discuss@lists.ozlabs.org,
	linuxppc-dev@lists.ozlabs.org
In-Reply-To: <4D34EB06.9040908@mentor.com>



> -----Original Message-----
> From: Meador Inge [mailto:meador_inge@mentor.com]
> Sent: Monday, January 17, 2011 7:21 PM
> To: Yoder Stuart-B08248
> Cc: linuxppc-dev@lists.ozlabs.org; devicetree-discuss@lists.ozlabs.org;
> Blanchard, Hollis; Meador Inge
> Subject: Re: [PATCH] define binding for fsl mpic interrupt controllers
>=20
> Hi Stuart,
>=20
> > From: Stuart Yoder<stuart.yoder@freescale.com>
> >
> > define the binding for compatible =3D "fsl,mpic", including the
> > definition of 4-cell interrupt specifiers.  The 3rd and 4th cells are
> > needed to define additional types of interrupt source outside the
> > "normal"
> > external and internal interrupts in FSL SoCs.  Define error interrupt,
> > IPIs, and PIC timer sources.
>=20
> Recently I posted an RFC concerning a binding for the MPIC [1].  I also
> submitted a patch based on that feedback today (patch subject '[PATCH 1/2=
]
> powerpc: document the MPIC device tree binding').  You provide much more
> information concerning the interrupt sources and how interrupt specifiers
> are encoded.  I had a few more properties listed: 'name', 'device_type',
> 'protected-sources', and 'no-reset'.  The 'no-reset'
> property is a proposed addition to the binding, the others are already in
> use.
>=20

I'm not sure a complete merge into one binding makes sense.  The thing that
motivated creating this new binding with 4 cells was a thread from
last year.  See:

http://lists.ozlabs.org/pipermail/devicetree-discuss/2010-January/001489.ht=
ml

Since Freescale had the need to represent additional information
in interrupt specifiers (error ints, timers, ipis), the suggestion
at the end was to add additional cells in a FSL-specific binding.
So this really is a Freescale specific thing and does belong in the
Freescale binding directory.

That being said, if there is consensus on the AMP related properties,
I should probably incorporate those as well-- protected-sources
and no-reset.

I'll separately comment on your mpic binding patch.

Stuart=20

^ permalink raw reply

* RE: [PATCH 1/2] powerpc: document the MPIC device tree binding
From: Yoder Stuart-B08248 @ 2011-01-18 20:21 UTC (permalink / raw)
  To: Meador Inge
  Cc: Blanchard, Hollis, devicetree-discuss@lists.ozlabs.org,
	linuxppc-dev@lists.ozlabs.org
In-Reply-To: <AANLkTi=QX4BfLvPfQDMOgmh90TtX4MQqio6AOZR8JKas@mail.gmail.com>

> From: Meador Inge <meador_inge@mentor.com>
> Date: Mon, Jan 17, 2011 at 6:52 PM
> Subject: [PATCH 1/2] powerpc: document the MPIC device tree binding
> To: linuxppc-dev@lists.ozlabs.org
> Cc: minge <meador_inge@mentor.com>,
> devicetree-discuss@lists.ozlabs.org, "Blanchard, Hollis"
> <Hollis_Blanchard@mentor.com>
>=20
>=20
> This binding documents several properties that have been in use for quite
> some time, and adds one new property 'no-reset', which controls whether t=
he
> MPIC should be reset during runtime initialization.
>=20
> Signed-off-by: Meador Inge <meador_inge@mentor.com>
> CC: Hollis Blanchard <hollis_blanchard@mentor.com>
> ---
> =A0Documentation/powerpc/dts-bindings/mpic.txt | =A0 78

This is really the binding for an open-pic interrupt controller
and I think the name should reflect that-- open-pic.txt.

> +++++++++++++++++++++++++++
> =A01 files changed, 78 insertions(+), 0 deletions(-)
> =A0create mode 100644 Documentation/powerpc/dts-bindings/mpic.txt
>=20
> diff --git a/Documentation/powerpc/dts-bindings/mpic.txt
> b/Documentation/powerpc/dts-bindings/mpic.txt
> new file mode 100644
> index 0000000..3a67919
> --- /dev/null
> +++ b/Documentation/powerpc/dts-bindings/mpic.txt
> @@ -0,0 +1,78 @@
> +* MPIC Binding
> +
> +This binding specifies what properties and child nodes must be
> +available on the device tree representation of the "MPIC" interrupt
> +controller. =A0This binding is based on the binding defined for Open PIC
> +in [1] and is a superset of that binding.

I think it would be better to base this on the ePAPR binding which
was based on the original chrp binding.  Properties like "name"
and "device_type" are deprecated not being used in flat device trees.

<http://www.power.org/resources/downloads/Power_ePAPR_APPROVED_v1.0.pdf>

The proposed new properties really should go back into the ePAPR.=20

> +
> +** Required properties:
> +
> + =A0 NOTE: Many of these descriptions were paraphrased from [1] to aid
> + =A0 =A0 =A0 =A0 readability.
> +
> + =A0 - name : Specifies the name of the MPIC.

Drop this.  No DTS files use it.

> + =A0 - device_type : Specifies the device type of this MPIC. =A0The valu=
e
> + of this
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 property shall be "open-pic".

device_type is deprecated, since this is not real open-firmware.  In
practice the kernel is matching on device_type, but we want to move
away from that to match on "compatible", just hasn't been implemented
yet.

> + =A0 - reg : Specifies the base physical address(s) and size(s) of this
> + MPIC's
> + =A0 =A0 =A0 =A0 =A0 addressable register space.
> + =A0 - compatible : Specifies the compatibility list for the MPIC. =A0Th=
e
> + property
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0value shall include "chrp,open-pic".

In the ePAPR we modified this to just "open-pic", because this has
nothing to do with chrp anymore.   I think just "open-pic" is
what we want.

> + =A0 - interrupt-controller : The presence of this property identifies
> + the node
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0as a MPIC. =A0No=
 property value should be
> defined.
> + =A0 - #address-cells : Specifies the number of cells needed to encode a=
n
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0address. =A0The value of thi=
s property shall always
> + be 0
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0so that 'interrupt-map' node=
s do not have to
> + specify a
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0parent unit address.
> + =A0 - #interrupt-cells : Specifies the number of cells needed to encode
> + an
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0interrupt source.

Should be 2, correct?

> +** Optional properties:
> +
> + =A0 - no-reset : The presence of this property indicates that the MPIC
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0should not be reset during runtime initi=
alization.
> + =A0 - protected-sources : Specifies a list of interrupt sources that ar=
e
> + not
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 available for use and w=
hose corresponding
> + vectors
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 should not be initializ=
ed. =A0A typical use case
> + for
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 this property is in AMP=
 systems where multiple
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 independent operating s=
ystems need to share
> + the MPIC
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 without clobbering each=
 other.

I do think you need to include the definition of interrupt
specifiers here.   Feel free to cut/paste text from my
Freescale mpic binding.

Stuart

^ permalink raw reply

* Re: [PATCH 1/2] powerpc: document the MPIC device tree binding
From: Scott Wood @ 2011-01-18 20:31 UTC (permalink / raw)
  To: Meador Inge; +Cc: Blanchard, Hollis, devicetree-discuss, linuxppc-dev
In-Reply-To: <4D34E448.8000902@mentor.com>

On Mon, 17 Jan 2011 18:52:24 -0600
Meador Inge <meador_inge@mentor.com> wrote:

> +** Required properties:
> +
> +   NOTE: Many of these descriptions were paraphrased from [1] to aid
> +         readability.
> +
> +   - name : Specifies the name of the MPIC.

"name" isn't really a property with flat trees.  The appropriate
node name, according to the Generic Names recommendation and ePAPR, is
interrupt-controller.

> +   - device_type : Specifies the device type of this MPIC.  The value 
> of this
> +                   property shall be "open-pic".

Can we drop device_type, and fix the kernel to look for compatible
instead?

> +   - compatible : Specifies the compatibility list for the MPIC.  The 
> property
> +                  value shall include "chrp,open-pic".

ePAPR wants just "open-pic".  And while chrp,open-pic is common in
existing trees, only one platform currently checks for it.

I'd make in "open-pic" in the binding, and have the kernel accept
either one.

> +** Optional properties:
> +
> +   - no-reset : The presence of this property indicates that the MPIC
> +                should not be reset during runtime initialization.
> +   - protected-sources : Specifies a list of interrupt sources that are 
> not
> +                         available for use and whose corresponding vectors
> +                         should not be initialized.  A typical use case for
> +                         this property is in AMP systems where multiple
> +                         independent operating systems need to share 
> the MPIC
> +                         without clobbering each other.
> +

Can we define no-reset as meaning that all vectors are in a sane state
(either directed at other cores, or masked)?

If we do that, maybe we can get rid of protected-sources.

-Scott

^ permalink raw reply

* Re: Problem with Busybox shell
From: Scott Wood @ 2011-01-18 20:59 UTC (permalink / raw)
  To: MohanReddy koppula; +Cc: Linuxppc-dev
In-Reply-To: <AANLkTinSfn+TQvzNWc++qP=4B2tbcHo-2iGv6E8tsq+b@mail.gmail.com>

On Tue, 18 Jan 2011 07:54:42 -0500
MohanReddy koppula <mohanreddykv@gmail.com> wrote:

> Hi All,
> 
> I am working on an MPC885 based custom board. I am able to boot up the
> linux (linux-2.6.33.7). I could see busybox shell (ash) prompt. But it
> is not accepting any inputs, I am not able to enter any command, it
> just hangs there. I am using ttyCPM0 terminal.
> 
> I suspected if there was any problem in CPM driver interrupts
> generation and put some printk's in the interrupt handler and could
> see interrupts are raised and data is read, but shell is not taking
> the input.
> 
> I wrote an init.c and opened the ttyCPM0 and tried to read from it,
> but couldn't. I am able to write to ttyCPM0 and see it on the host
> minicom.
> 
> Please some one help me.

Check your pin and clock setup -- perhaps tx is set up correctly but rx
isn't.

-Scott

^ permalink raw reply

* [patch 2/2] spufs: use simple_write_to_buffer()
From: akpm @ 2011-01-18 21:09 UTC (permalink / raw)
  To: benh; +Cc: arnd, akinobu.mita, linuxppc-dev, paulus, jk, akpm

From: Akinobu Mita <akinobu.mita@gmail.com>

Simplify several write fileoperations for spufs by using
simple_write_to_buffer().

Signed-off-by: Akinobu Mita <akinobu.mita@gmail.com>
Cc: Benjamin Herrenschmidt <benh@kernel.crashing.org>
Cc: Paul Mackerras <paulus@samba.org>
Cc: Jeremy Kerr <jk@ozlabs.org>
Acked-by: Arnd Bergmann <arnd@arndb.de>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
---

 arch/powerpc/platforms/cell/spufs/file.c |   27 +++++----------------
 1 file changed, 7 insertions(+), 20 deletions(-)

diff -puN arch/powerpc/platforms/cell/spufs/file.c~spufs-use-simple_write_to_buffer arch/powerpc/platforms/cell/spufs/file.c
--- a/arch/powerpc/platforms/cell/spufs/file.c~spufs-use-simple_write_to_buffer
+++ a/arch/powerpc/platforms/cell/spufs/file.c
@@ -219,24 +219,17 @@ spufs_mem_write(struct file *file, const
 	loff_t pos = *ppos;
 	int ret;
 
-	if (pos < 0)
-		return -EINVAL;
 	if (pos > LS_SIZE)
 		return -EFBIG;
-	if (size > LS_SIZE - pos)
-		size = LS_SIZE - pos;
 
 	ret = spu_acquire(ctx);
 	if (ret)
 		return ret;
 
 	local_store = ctx->ops->get_ls(ctx);
-	ret = copy_from_user(local_store + pos, buffer, size);
+	size = simple_write_to_buffer(local_store, LS_SIZE, ppos, buffer, size);
 	spu_release(ctx);
 
-	if (ret)
-		return -EFAULT;
-	*ppos = pos + size;
 	return size;
 }
 
@@ -574,18 +567,15 @@ spufs_regs_write(struct file *file, cons
 	if (*pos >= sizeof(lscsa->gprs))
 		return -EFBIG;
 
-	size = min_t(ssize_t, sizeof(lscsa->gprs) - *pos, size);
-	*pos += size;
-
 	ret = spu_acquire_saved(ctx);
 	if (ret)
 		return ret;
 
-	ret = copy_from_user((char *)lscsa->gprs + *pos - size,
-			     buffer, size) ? -EFAULT : size;
+	size = simple_write_to_buffer(lscsa->gprs, sizeof(lscsa->gprs), pos,
+					buffer, size);
 
 	spu_release_saved(ctx);
-	return ret;
+	return size;
 }
 
 static const struct file_operations spufs_regs_fops = {
@@ -630,18 +620,15 @@ spufs_fpcr_write(struct file *file, cons
 	if (*pos >= sizeof(lscsa->fpcr))
 		return -EFBIG;
 
-	size = min_t(ssize_t, sizeof(lscsa->fpcr) - *pos, size);
-
 	ret = spu_acquire_saved(ctx);
 	if (ret)
 		return ret;
 
-	*pos += size;
-	ret = copy_from_user((char *)&lscsa->fpcr + *pos - size,
-			     buffer, size) ? -EFAULT : size;
+	size = simple_write_to_buffer(&lscsa->fpcr, sizeof(lscsa->fpcr), pos,
+					buffer, size);
 
 	spu_release_saved(ctx);
-	return ret;
+	return size;
 }
 
 static const struct file_operations spufs_fpcr_fops = {
_

^ permalink raw reply

* [patch 1/2] macintosh: wrong test in fan_{read,write}_reg()
From: akpm @ 2011-01-18 21:09 UTC (permalink / raw)
  To: benh; +Cc: linuxppc-dev, akpm, roel.kluin

From: roel kluin <roel.kluin@gmail.com>

Fix error test in fan_{read,write}_reg()

Signed-off-by: Roel Kluin <roel.kluin@gmail.com>
Cc: Benjamin Herrenschmidt <benh@kernel.crashing.org>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
---

 drivers/macintosh/therm_pm72.c |    4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff -puN drivers/macintosh/therm_pm72.c~macintosh-wrong-test-in-fan_readwrite_reg drivers/macintosh/therm_pm72.c
--- a/drivers/macintosh/therm_pm72.c~macintosh-wrong-test-in-fan_readwrite_reg
+++ a/drivers/macintosh/therm_pm72.c
@@ -443,7 +443,7 @@ static int fan_read_reg(int reg, unsigne
 	tries = 0;
 	for (;;) {
 		nr = i2c_master_recv(fcu, buf, nb);
-		if (nr > 0 || (nr < 0 && nr != ENODEV) || tries >= 100)
+		if (nr > 0 || (nr < 0 && nr != -ENODEV) || tries >= 100)
 			break;
 		msleep(10);
 		++tries;
@@ -464,7 +464,7 @@ static int fan_write_reg(int reg, const 
 	tries = 0;
 	for (;;) {
 		nw = i2c_master_send(fcu, buf, nb);
-		if (nw > 0 || (nw < 0 && nw != EIO) || tries >= 100)
+		if (nw > 0 || (nw < 0 && nw != -EIO) || tries >= 100)
 			break;
 		msleep(10);
 		++tries;
_

^ permalink raw reply

* Re: [patch 1/2] macintosh: wrong test in fan_{read,write}_reg()
From: Benjamin Herrenschmidt @ 2011-01-18 21:22 UTC (permalink / raw)
  To: akpm; +Cc: linuxppc-dev, roel.kluin
In-Reply-To: <201101182109.p0IL992W005451@imap1.linux-foundation.org>

On Tue, 2011-01-18 at 13:09 -0800, akpm@linux-foundation.org wrote:
> From: roel kluin <roel.kluin@gmail.com>
> 
> Fix error test in fan_{read,write}_reg()

Thanks, I'll pick that up.

Cheers,
Ben.

> Signed-off-by: Roel Kluin <roel.kluin@gmail.com>
> Cc: Benjamin Herrenschmidt <benh@kernel.crashing.org>
> Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
> ---
> 
>  drivers/macintosh/therm_pm72.c |    4 ++--
>  1 file changed, 2 insertions(+), 2 deletions(-)
> 
> diff -puN drivers/macintosh/therm_pm72.c~macintosh-wrong-test-in-fan_readwrite_reg drivers/macintosh/therm_pm72.c
> --- a/drivers/macintosh/therm_pm72.c~macintosh-wrong-test-in-fan_readwrite_reg
> +++ a/drivers/macintosh/therm_pm72.c
> @@ -443,7 +443,7 @@ static int fan_read_reg(int reg, unsigne
>  	tries = 0;
>  	for (;;) {
>  		nr = i2c_master_recv(fcu, buf, nb);
> -		if (nr > 0 || (nr < 0 && nr != ENODEV) || tries >= 100)
> +		if (nr > 0 || (nr < 0 && nr != -ENODEV) || tries >= 100)
>  			break;
>  		msleep(10);
>  		++tries;
> @@ -464,7 +464,7 @@ static int fan_write_reg(int reg, const 
>  	tries = 0;
>  	for (;;) {
>  		nw = i2c_master_send(fcu, buf, nb);
> -		if (nw > 0 || (nw < 0 && nw != EIO) || tries >= 100)
> +		if (nw > 0 || (nw < 0 && nw != -EIO) || tries >= 100)
>  			break;
>  		msleep(10);
>  		++tries;
> _

^ permalink raw reply

* Re: [patch 2/2] spufs: use simple_write_to_buffer()
From: Benjamin Herrenschmidt @ 2011-01-18 21:22 UTC (permalink / raw)
  To: akpm; +Cc: linuxppc-dev, paulus, akinobu.mita, arnd, jk
In-Reply-To: <201101182109.p0IL9A3h005454@imap1.linux-foundation.org>

On Tue, 2011-01-18 at 13:09 -0800, akpm@linux-foundation.org wrote:
> From: Akinobu Mita <akinobu.mita@gmail.com>
> 
> Simplify several write fileoperations for spufs by using
> simple_write_to_buffer().

I'll pick that one up too.

Cheers,
Ben.

> Signed-off-by: Akinobu Mita <akinobu.mita@gmail.com>
> Cc: Benjamin Herrenschmidt <benh@kernel.crashing.org>
> Cc: Paul Mackerras <paulus@samba.org>
> Cc: Jeremy Kerr <jk@ozlabs.org>
> Acked-by: Arnd Bergmann <arnd@arndb.de>
> Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
> ---
> 
>  arch/powerpc/platforms/cell/spufs/file.c |   27 +++++----------------
>  1 file changed, 7 insertions(+), 20 deletions(-)
> 
> diff -puN arch/powerpc/platforms/cell/spufs/file.c~spufs-use-simple_write_to_buffer arch/powerpc/platforms/cell/spufs/file.c
> --- a/arch/powerpc/platforms/cell/spufs/file.c~spufs-use-simple_write_to_buffer
> +++ a/arch/powerpc/platforms/cell/spufs/file.c
> @@ -219,24 +219,17 @@ spufs_mem_write(struct file *file, const
>  	loff_t pos = *ppos;
>  	int ret;
>  
> -	if (pos < 0)
> -		return -EINVAL;
>  	if (pos > LS_SIZE)
>  		return -EFBIG;
> -	if (size > LS_SIZE - pos)
> -		size = LS_SIZE - pos;
>  
>  	ret = spu_acquire(ctx);
>  	if (ret)
>  		return ret;
>  
>  	local_store = ctx->ops->get_ls(ctx);
> -	ret = copy_from_user(local_store + pos, buffer, size);
> +	size = simple_write_to_buffer(local_store, LS_SIZE, ppos, buffer, size);
>  	spu_release(ctx);
>  
> -	if (ret)
> -		return -EFAULT;
> -	*ppos = pos + size;
>  	return size;
>  }
>  
> @@ -574,18 +567,15 @@ spufs_regs_write(struct file *file, cons
>  	if (*pos >= sizeof(lscsa->gprs))
>  		return -EFBIG;
>  
> -	size = min_t(ssize_t, sizeof(lscsa->gprs) - *pos, size);
> -	*pos += size;
> -
>  	ret = spu_acquire_saved(ctx);
>  	if (ret)
>  		return ret;
>  
> -	ret = copy_from_user((char *)lscsa->gprs + *pos - size,
> -			     buffer, size) ? -EFAULT : size;
> +	size = simple_write_to_buffer(lscsa->gprs, sizeof(lscsa->gprs), pos,
> +					buffer, size);
>  
>  	spu_release_saved(ctx);
> -	return ret;
> +	return size;
>  }
>  
>  static const struct file_operations spufs_regs_fops = {
> @@ -630,18 +620,15 @@ spufs_fpcr_write(struct file *file, cons
>  	if (*pos >= sizeof(lscsa->fpcr))
>  		return -EFBIG;
>  
> -	size = min_t(ssize_t, sizeof(lscsa->fpcr) - *pos, size);
> -
>  	ret = spu_acquire_saved(ctx);
>  	if (ret)
>  		return ret;
>  
> -	*pos += size;
> -	ret = copy_from_user((char *)&lscsa->fpcr + *pos - size,
> -			     buffer, size) ? -EFAULT : size;
> +	size = simple_write_to_buffer(&lscsa->fpcr, sizeof(lscsa->fpcr), pos,
> +					buffer, size);
>  
>  	spu_release_saved(ctx);
> -	return ret;
> +	return size;
>  }
>  
>  static const struct file_operations spufs_fpcr_fops = {
> _

^ permalink raw reply

* Re: 2.6.37-git17 virtual IO boot failure
From: Nishanth Aravamudan @ 2011-01-18 22:47 UTC (permalink / raw)
  To: Anton Blanchard; +Cc: sonnyrao, miltonm, linuxppc-dev
In-Reply-To: <20110118123152.50f75a72@kryten>

On 18.01.2011 [12:31:52 +1100], Anton Blanchard wrote:
> Hi,
> 
> I was testing 2.6.37-git17 on a POWER7 with virtual IO and hit this:
> 
> Trying to unpack rootfs image as initramfs...
> Freeing initrd memory: 7446k freed
> vio 30000000: Warning: IOMMU dma not supported: mask
> 0xffffffffffffffff, table unavailable
> vio 4000: Warning: IOMMU dma not supported: mask 0xffffffffffffffff,
> table unavailable
> vio 4001: Warning: IOMMU dma not supported: mask 0xffffffffffffffff,
> table unavailable
> vio 4002: Warning: IOMMU dma not supported: mask 0xffffffffffffffff,
> table unavailable
> vio 4004: Warning: IOMMU dma not supported: mask 0xffffffffffffffff,
> table unavailable
> audit: initializing netlink socket (disabled)
> 
> Haven't had a chance to look closer yet.

After debugging a bit, this would appear to be due to the second hunk of
b3c73856ae47d43d0d181f9de1c1c6c0820c4515.

diff --git a/arch/powerpc/kernel/vio.c b/arch/powerpc/kernel/vio.c
index b265405..1b695fd 100644
--- a/arch/powerpc/kernel/vio.c
+++ b/arch/powerpc/kernel/vio.c
@@ -1257,6 +1257,10 @@ struct vio_dev *vio_register_device_node(struct device_node *of_node)
        viodev->dev.parent = &vio_bus_device.dev;
        viodev->dev.bus = &vio_bus_type;
        viodev->dev.release = vio_dev_release;
+        /* needed to ensure proper operation of coherent allocations
+         * later, in case driver doesn't set it explicitly */
+        dma_set_mask(&viodev->dev, DMA_BIT_MASK(64));
+        dma_set_coherent_mask(&viodev->dev, DMA_BIT_MASK(64));

        /* register with generic device framework */
        if (device_register(&viodev->dev)) {

Milton, Sonny, any thoughts?

Thanks,
Nish

-- 
Nishanth Aravamudan <nacc@us.ibm.com>
IBM Linux Technology Center

^ permalink raw reply related

* Re: 2.6.37-git17 virtual IO boot failure
From: Nishanth Aravamudan @ 2011-01-19  0:48 UTC (permalink / raw)
  To: Anton Blanchard; +Cc: sonnyrao, miltonm, linuxppc-dev
In-Reply-To: <20110118224718.GA19039@us.ibm.com>

On 18.01.2011 [14:47:18 -0800], Nishanth Aravamudan wrote:
> On 18.01.2011 [12:31:52 +1100], Anton Blanchard wrote:
> > Hi,
> > 
> > I was testing 2.6.37-git17 on a POWER7 with virtual IO and hit this:
> > 
> > Trying to unpack rootfs image as initramfs...
> > Freeing initrd memory: 7446k freed
> > vio 30000000: Warning: IOMMU dma not supported: mask
> > 0xffffffffffffffff, table unavailable
> > vio 4000: Warning: IOMMU dma not supported: mask 0xffffffffffffffff,
> > table unavailable
> > vio 4001: Warning: IOMMU dma not supported: mask 0xffffffffffffffff,
> > table unavailable
> > vio 4002: Warning: IOMMU dma not supported: mask 0xffffffffffffffff,
> > table unavailable
> > vio 4004: Warning: IOMMU dma not supported: mask 0xffffffffffffffff,
> > table unavailable
> > audit: initializing netlink socket (disabled)
> > 
> > Haven't had a chance to look closer yet.
> 
> After debugging a bit, this would appear to be due to the second hunk of
> b3c73856ae47d43d0d181f9de1c1c6c0820c4515.
> 
> diff --git a/arch/powerpc/kernel/vio.c b/arch/powerpc/kernel/vio.c
> index b265405..1b695fd 100644
> --- a/arch/powerpc/kernel/vio.c
> +++ b/arch/powerpc/kernel/vio.c
> @@ -1257,6 +1257,10 @@ struct vio_dev *vio_register_device_node(struct device_node *of_node)
>         viodev->dev.parent = &vio_bus_device.dev;
>         viodev->dev.bus = &vio_bus_type;
>         viodev->dev.release = vio_dev_release;
> +        /* needed to ensure proper operation of coherent allocations
> +         * later, in case driver doesn't set it explicitly */
> +        dma_set_mask(&viodev->dev, DMA_BIT_MASK(64));
> +        dma_set_coherent_mask(&viodev->dev, DMA_BIT_MASK(64));
> 
>         /* register with generic device framework */
>         if (device_register(&viodev->dev)) {
> 
> Milton, Sonny, any thoughts?

A bit more detail after trying a few more kernels on the box that
originally showed the error:

1) This doesn't actually prevent booting, afaict. I think it "just"
disables DMA, which is bad, but not a boot fail, technically.

2) Reverting the above commit definitely prevents those messages.

3) I'm seeing a separate issue with 2.6.37-git17 (that's not present in
2.6.37):

sd 0:4:2:0: [sda] Aborting command: 2A
sd 0:4:2:0: Abort timed out. Resetting bus.

At which point the box locks up :)

So testing fixes is a bit of a challenge right now.

Ben, if you're ok with waiting to see if Milton or Sonny have any ideas,
I'd like to hold off on asking for a revert. In the case they do, I'll
be able to test and send out any proposed fix rapidly.

Thanks,
Nish

-- 
Nishanth Aravamudan <nacc@us.ibm.com>
IBM Linux Technology Center

^ permalink raw reply

* Re: [PATCH #upstream-fixes] pata_mpc52xx: inherit from ata_bmdma_port_ops
From: Jeff Garzik @ 2011-01-19  1:00 UTC (permalink / raw)
  To: Tejun Heo; +Cc: linux-ide, linuxppc-dev, Sergei Shtylyov, Roman Fietze
In-Reply-To: <20110109224820.GB26607@mtj.dyndns.org>

On 01/09/2011 05:48 PM, Tejun Heo wrote:
> pata_mpc52xx supports BMDMA but inherits ata_sff_port_ops which
> triggers BUG_ON() when a DMA command is issued.  Fix it.
>
> Signed-off-by: Tejun Heo<tj@kernel.org>
> Reported-by: Roman Fietze<roman.fietze@telemotive.de>
> Cc: Sergei Shtylyov<sshtylyov@mvista.com>
> Cc: stable@kernel.org
> ---
>   drivers/ata/pata_mpc52xx.c |    2 +-
>   1 file changed, 1 insertion(+), 1 deletion(-)
>
> diff --git a/drivers/ata/pata_mpc52xx.c b/drivers/ata/pata_mpc52xx.c
> index 8cc536e..d7d8026 100644
> --- a/drivers/ata/pata_mpc52xx.c
> +++ b/drivers/ata/pata_mpc52xx.c
> @@ -610,7 +610,7 @@ static struct scsi_host_template mpc52xx_ata_sht = {
>   };
>
>   static struct ata_port_operations mpc52xx_ata_port_ops = {
> -	.inherits		=&ata_sff_port_ops,
> +	.inherits		=&ata_bmdma_port_ops,
>   	.sff_dev_select		= mpc52xx_ata_dev_select,
>   	.set_piomode		= mpc52xx_ata_set_piomode,

applied

^ permalink raw reply

* Re: [PATCH] ATA: Add FSL sata v2 controller support
From: Jeff Garzik @ 2011-01-19  1:10 UTC (permalink / raw)
  To: Sergei Shtylyov, kumar.gala, Roy Zang; +Cc: Xulei, linuxppc-dev, linux-ide
In-Reply-To: <4D342C6E.6000608@ru.mvista.com>

On 01/17/2011 06:47 AM, Sergei Shtylyov wrote:
> Hello.
>
> On 17-01-2011 10:10, Xulei wrote:
>
>> In FSL sata v2 block, the snoop bit of PRDT Word3 description
>> information is at bit28 instead of bit22.
>
>> This patch adds FSL sata v2 probe and resolve this difference.
>
>> Signed-off-by: Xulei <B33228@freescale.com>
>
> AFAIK, full name is required.
>
>> Signed-off-by: Roy Zang <tie-fei.zang@freescale.com>
> [...]
>
>> diff --git a/arch/powerpc/boot/dts/p1022ds.dts
>> b/arch/powerpc/boot/dts/p1022ds.dts
>> index 2bbecbb..9ad41dd 100644
>> --- a/arch/powerpc/boot/dts/p1022ds.dts
>> +++ b/arch/powerpc/boot/dts/p1022ds.dts
>> @@ -475,14 +475,14 @@
>> };
>>
>> sata@18000 {
>> - compatible = "fsl,mpc8536-sata", "fsl,pq-sata";
>> + compatible = "fsl,p1022-sata", "fsl,pq-sata-v2";
>> reg =<0x18000 0x1000>;
>> cell-index =<1>;
>> interrupts =<74 0x2>;
>> };
>>
>> sata@19000 {
>> - compatible = "fsl,mpc8536-sata", "fsl,pq-sata";
>> + compatible = "fsl,p1022-sata", "fsl,pq-sata-v2";
>> reg =<0x19000 0x1000>;
>> cell-index =<2>;
>> interrupts =<41 0x2>;
>
> Please put this into the separate patch and push thru the PPC tree.

I think it's OK to send 100% of this via the PPC tree.  The sata_fsl.c 
patch for data_snoop variability directly keys off a call to 
platform-specific detail (of_device_is_compatible call).

Acked-by: Jeff Garzik <jgarzik@redhat.com>

^ permalink raw reply

* [PATCH V7 00/10] Add-Synopsys-DesignWare-HS-USB-OTG-driver
From: tmarri @ 2011-01-19  2:05 UTC (permalink / raw)
  To: linux-usb, linuxppc-dev; +Cc: tmarri, greg

From: Tirumala Marri <tmarri@apm.com>

v7:
 1. Fix sparse tool warnings.
 2. Fix checkpatch errors and warnings.
 3. Rename USB_OTG config variable to USB_DWC_CONFIG

v6:
 1. Replaced register definitions and bit fields with macros.
 2. Replace printks with dev_dbg or dev_err functions.
 3. Cleanup some assignments.
 4. Remove chip specific selections in Kconfig file.

Tirumala Marri (10):
  USB/ppc4xx: Add Synopsys DWC OTG Register definitions
  USB/ppc4xx: Add Synopsys DWC OTG driver framework
  USB/ppc4xx: Add Synopsys DWC OTG Core Interface Layer (CIL)
  USB/ppc4xx: Add Synopsys DWC OTG HCD function
  USB/ppc4xx: Add Synopsys DWC OTG HCD interrupt function
  USB/ppc4xx: Add Synopsys DWC OTG HCD queue function
  USB/ppc4xx: Add Synopsys DWC OTG PCD function
  USB ppc4xx: Add Synopsys DWC OTG PCD interrupt function
  USB/ppc4xx:Synopsys DWC OTG driver enable gadget support
  USB ppc4xx: Add Synopsys DWC OTG driver kernel configuration and
    Makefile

 drivers/Makefile                        |    2 +
 drivers/usb/Kconfig                     |    3 +-
 drivers/usb/dwc_otg/Kconfig             |   96 ++
 drivers/usb/dwc_otg/Makefile            |   19 +
 drivers/usb/dwc_otg/dwc_otg_apmppc.c    |  414 ++++++
 drivers/usb/dwc_otg/dwc_otg_cil.c       |  972 ++++++++++++
 drivers/usb/dwc_otg/dwc_otg_cil.h       | 1219 +++++++++++++++
 drivers/usb/dwc_otg/dwc_otg_cil_intr.c  |  616 ++++++++
 drivers/usb/dwc_otg/dwc_otg_driver.h    |   76 +
 drivers/usb/dwc_otg/dwc_otg_hcd.c       | 2466 +++++++++++++++++++++++++++++++
 drivers/usb/dwc_otg/dwc_otg_hcd.h       |  416 ++++++
 drivers/usb/dwc_otg/dwc_otg_hcd_intr.c  | 1477 ++++++++++++++++++
 drivers/usb/dwc_otg/dwc_otg_hcd_queue.c |  696 +++++++++
 drivers/usb/dwc_otg/dwc_otg_param.c     |  180 +++
 drivers/usb/dwc_otg/dwc_otg_pcd.c       | 1752 ++++++++++++++++++++++
 drivers/usb/dwc_otg/dwc_otg_pcd.h       |  139 ++
 drivers/usb/dwc_otg/dwc_otg_pcd_intr.c  | 2311 +++++++++++++++++++++++++++++
 drivers/usb/dwc_otg/dwc_otg_regs.h      | 1325 +++++++++++++++++
 drivers/usb/gadget/Kconfig              |   22 +
 drivers/usb/gadget/gadget_chips.h       |    9 +
 20 files changed, 14209 insertions(+), 1 deletions(-)
 create mode 100644 drivers/usb/dwc_otg/Kconfig
 create mode 100644 drivers/usb/dwc_otg/Makefile
 create mode 100644 drivers/usb/dwc_otg/dwc_otg_apmppc.c
 create mode 100644 drivers/usb/dwc_otg/dwc_otg_cil.c
 create mode 100644 drivers/usb/dwc_otg/dwc_otg_cil.h
 create mode 100644 drivers/usb/dwc_otg/dwc_otg_cil_intr.c
 create mode 100644 drivers/usb/dwc_otg/dwc_otg_driver.h
 create mode 100644 drivers/usb/dwc_otg/dwc_otg_hcd.c
 create mode 100644 drivers/usb/dwc_otg/dwc_otg_hcd.h
 create mode 100644 drivers/usb/dwc_otg/dwc_otg_hcd_intr.c
 create mode 100644 drivers/usb/dwc_otg/dwc_otg_hcd_queue.c
 create mode 100644 drivers/usb/dwc_otg/dwc_otg_param.c
 create mode 100644 drivers/usb/dwc_otg/dwc_otg_pcd.c
 create mode 100644 drivers/usb/dwc_otg/dwc_otg_pcd.h
 create mode 100644 drivers/usb/dwc_otg/dwc_otg_pcd_intr.c
 create mode 100644 drivers/usb/dwc_otg/dwc_otg_regs.h

^ permalink raw reply

* [PATCH V7 01/10] USB/ppc4xx: Add Synopsys DWC OTG Register definitions
From: tmarri @ 2011-01-19  2:05 UTC (permalink / raw)
  To: linux-usb, linuxppc-dev; +Cc: tmarri, greg, Mark Miesfeld, Fushen Chen

From: Tirumala Marri <tmarri@apm.com>

Add Synopsys Design Ware core register definitions.

Signed-off-by: Tirumala R Marri <tmarri@apm.com>
Signed-off-by: Fushen Chen <fchen@apm.com>
Signed-off-by: Mark Miesfeld <mmiesfeld@apm.com>
---
 drivers/usb/dwc_otg/dwc_otg_regs.h | 1325 ++++++++++++++++++++++++++++++++++++
 1 files changed, 1325 insertions(+), 0 deletions(-)

diff --git a/drivers/usb/dwc_otg/dwc_otg_regs.h b/drivers/usb/dwc_otg/dwc_otg_regs.h
new file mode 100644
index 0000000..bd53bf6
--- /dev/null
+++ b/drivers/usb/dwc_otg/dwc_otg_regs.h
@@ -0,0 +1,1325 @@
+/*
+ * DesignWare HS OTG controller driver
+ * Copyright (C) 2006 Synopsys, Inc.
+ * Portions Copyright (C) 2010 Applied Micro Circuits Corporation.
+ *
+ * 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 version 2 for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see http://www.gnu.org/licenses
+ * or write to the Free Software Foundation, Inc., 51 Franklin Street,
+ * Suite 500, Boston, MA 02110-1335 USA.
+ *
+ * Based on Synopsys driver version 2.60a
+ * Modified by Mark Miesfeld <mmiesfeld@apm.com>
+ *
+ * Revamped register difinitions by Tirumala R Marri(tmarri@apm.com)
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING BUT NOT LIMITED TO THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL SYNOPSYS, INC. BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+#ifndef __DWC_OTG_REGS_H__
+#define __DWC_OTG_REGS_H__
+
+#include <linux/types.h>
+
+/*
+ * This file contains the Macro defintions for accessing the DWC_otg core
+ * registers.
+ *
+ * The application interfaces with the HS OTG core by reading from and
+ * writing to the Control and Status Register (CSR) space through the
+ * AHB Slave interface. These registers are 32 bits wide, and the
+ * addresses are 32-bit-block aligned.
+ * CSRs are classified as follows:
+ * - Core Global Registers
+ * - Device Mode Registers
+ * - Device Global Registers
+ * - Device Endpoint Specific Registers
+ * - Host Mode Registers
+ * - Host Global Registers
+ * - Host Port CSRs
+ * - Host Channel Specific Registers
+ *
+ * Only the Core Global registers can be accessed in both Device and
+ * Host modes. When the HS OTG core is operating in one mode, either
+ * Device or Host, the application must not access registers from the
+ * other mode. When the core switches from one mode to another, the
+ * registers in the new mode of operation must be reprogrammed as they
+ * would be after a power-on reset.
+ */
+
+/*
+ * DWC_otg Core registers.  The core_global_regs structure defines the
+ * size and relative field offsets for the Core Global registers.
+ */
+#define	DWC_GOTGCTL		0x000
+#define	DWC_GOTGINT		0x004
+#define	DWC_GAHBCFG		0x008
+#define	DWC_GUSBCFG		0x00C
+#define	DWC_GRSTCTL		0x010
+#define	DWC_GINTSTS		0x014
+#define	DWC_GINTMSK		0x018
+#define	DWC_GRXSTSR		0x01C
+#define	DWC_GRXSTSP		0x020
+#define	DWC_GRXFSIZ		0x024
+#define	DWC_GNPTXFSIZ		0x028
+#define	DWC_GNPTXSTS		0x02C
+#define	DWC_GI2CCTL		0x030
+#define	DWC_VDCTL		0x034
+#define	DWC_GGPIO		0x038
+#define	DWC_GUID		0x03C
+#define	DWC_GSNPSID		0x040
+#define	DWC_GHWCFG1		0x044
+#define	DWC_GHWCFG2		0x048
+#define	DWC_GHWCFG3		0x04c
+#define	DWC_GHWCFG4		0x050
+#define	DWC_HPTXFSIZ		0x100
+#define	DWC_DPTX_FSIZ_DIPTXF(x)	(0x104 + x * 4)	/* 15 <= x > 1 */
+
+#define DWC_GLBINTRMASK				0x0001
+#define DWC_DMAENABLE				0x0020
+#define DWC_NPTXEMPTYLVL_EMPTY			0x0080
+#define DWC_NPTXEMPTYLVL_HALFEMPTY		0x0000
+#define DWC_PTXEMPTYLVL_EMPTY			0x0100
+#define DWC_PTXEMPTYLVL_HALFEMPTY		0x0000
+
+#define DWC_SLAVE_ONLY_ARCH			0
+#define DWC_EXT_DMA_ARCH			1
+#define DWC_INT_DMA_ARCH			2
+
+#define DWC_MODE_HNP_SRP_CAPABLE		0
+#define DWC_MODE_SRP_ONLY_CAPABLE		1
+#define DWC_MODE_NO_HNP_SRP_CAPABLE		2
+#define DWC_MODE_SRP_CAPABLE_DEVICE		3
+#define DWC_MODE_NO_SRP_CAPABLE_DEVICE		4
+#define DWC_MODE_SRP_CAPABLE_HOST		5
+#define DWC_MODE_NO_SRP_CAPABLE_HOST		6
+
+/*
+ * These Macros represents the bit fields of the Core OTG Controland Status
+ * Register (GOTGCTL).  Set the bits using the bit fields then write the u32
+ * value to the register.
+ */
+#define DWC_GCTL_BSESSION_VALID     (1 << 19)
+#define DWC_GCTL_CSESSION_VALID     (1 << 18)
+#define DWC_GCTL_DEBOUNCE           (1 << 17)
+#define DWC_GCTL_CONN_ID_STATUS     (1 << 16)
+#define DWC_GCTL_DEV_HNP_ENA        (1 << 11)
+#define DWC_GCTL_HOST_HNP_ENA       (1 << 10)
+#define DWC_GCTL_HNP_REQ            (1 << 9)
+#define DWC_GCTL_HOST_NEG_SUCCES    (1 << 8)
+#define DWC_GCTL_SES_REQ            (1 << 1)
+#define DWC_GCTL_SES_REQ_SUCCESS    (1 << 0)
+
+#define DWC_GCTL_BSESSION_VALID_RD(reg)		(((reg) & (0x001 << 19)) >> 19)
+#define DWC_GCTL_CSESSION_VALID_RD(reg)		(((reg) & (0x001 << 18)) >> 18)
+#define DWC_GCTL_DEBOUNCE_RD(reg)		(((reg) & (0x001 << 17)) >> 17)
+#define DWC_GCTL_CONN_ID_STATUS_RD(reg)		(((reg) & (0x001 << 16)) >> 16)
+#define DWC_GCTL_DEV_HNP_ENA_RD(reg)		(((reg) & (0x001 << 11)) >> 11)
+#define DWC_GCTL_HOST_HNP_ENA_RD(reg)		(((reg) & (0x001 << 10)) >> 10)
+#define DWC_GCTL_HNP_REQ_RD(reg)		(((reg) & (0x001 << 9)) >> 9)
+#define DWC_GCTL_HOST_NEG_SUCCES_RD(reg)	(((reg) & (0x001 << 8)) >> 8)
+#define DWC_GCTL_SES_REQ_RD(reg)		(((reg) & (0x001 << 1)) >> 1)
+#define DWC_GCTL_SES_REQ_SUCCESS_RD(reg)	(((reg) & (0x001 << 0)) >> 0)
+
+#define DWC_GCTL_BSESSION_VALID_RW(reg, x)	\
+	(((reg) & (~((u32)0x01 << 19))) | ((x) << 19))
+#define DWC_GCTL_CSESSION_VALID_RW(reg, x)	\
+	(((reg) & (~((u32)0x01 << 18))) | ((x) << 18))
+#define DWC_GCTL_DEBOUNCE_RW(reg, x)		\
+	(((reg) & (~((u32)0x01 << 17))) | ((x) << 17))
+#define DWC_GCTL_CONN_ID_STATUS_RW(reg, x)	\
+	(((reg) & (~((u32)0x01 << 16))) | ((x) << 16))
+#define DWC_GCTL_DEV_HNP_ENA_RW	(reg, x)	\
+	(((reg) & (~((u32)0x01 << 11))) | ((x) << 11))
+#define DWC_GCTL_HOST_HNP_ENA_RW(reg, x)	\
+	(((reg) & (~((u32)0x01 << 10))) | ((x) << 10))
+#define DWC_GCTL_HNP_REQ_RW(reg, x)		\
+	(((reg) & (~((u32)0x01 << 9))) | ((x) << 9))
+#define DWC_GCTL_HOST_NEG_SUCCES_RW(reg, x)	\
+	(((reg) & (~((u32)0x01 << 8))) | ((x) << 8))
+#define DWC_GCTL_SES_REQ_RW(reg, x)		\
+	(((reg) & (~((u32)0x01 << 1))) | ((x) << 1))
+#define DWC_GCTL_SES_REQ_SUCCESS_RW(reg, x)	\
+	(((reg) & (~((u32)0x01 << 0))) | ((x) << 0))
+/*
+ * These Macros represents the bit fields of the Core OTG Interrupt Register
+ * (GOTGINT).  Set/clear the bits using the bit fields then write the u32
+ * value to the register.
+ */
+#define DWC_GINT_DEBDONE	(1 << 19)
+#define DWC_GINT_DEVTOUT	(1 << 18)
+#define DWC_GINT_HST_NEGDET	(1 << 17)
+#define DWC_GINT_HST_NEGSUC	(1 << 9)
+#define DWC_GINT_SES_REQSUC	(1 << 8)
+#define DWC_GINT_SES_ENDDET	(1 << 2)
+
+/*
+ * These Macros represents the bit fields of the Core AHB Configuration Register
+ * (GAHBCFG).  Set/clear the bits using the bit fields then write the u32 value
+ * to the register.
+ */
+#define DWC_AHBCFG_FIFO_EMPTY		(1 << 8)
+#define DWC_AHBCFG_NPFIFO_EMPTY		(1 << 7)
+#define DWC_AHBCFG_DMA_ENA		(1 << 5)
+#define DWC_AHBCFG_BURST_LEN(x)		(x << 1)
+#define DWC_AHBCFG_GLBL_INT_MASK	(1 << 0)
+
+#define DWC_GAHBCFG_TXFEMPTYLVL_EMPTY		1
+#define DWC_GAHBCFG_TXFEMPTYLVL_HALFEMPTY	0
+#define DWC_GAHBCFG_DMAENABLE			1
+#define DWC_GAHBCFG_INT_DMA_BURST_SINGLE	0
+#define DWC_GAHBCFG_INT_DMA_BURST_INCR		1
+#define DWC_GAHBCFG_INT_DMA_BURST_INCR4		3
+#define DWC_GAHBCFG_INT_DMA_BURST_INCR8		5
+#define DWC_GAHBCFG_INT_DMA_BURST_INCR16	7
+
+/*
+
+ * (GUSBCFG).  Set the bits using the bit fields then write the u32 value to the
+ * register.
+ */
+#define DWC_USBCFG_CORR_PKT		(1 << 31)
+#define DWC_USBCFG_FRC_DEV_MODE		(1 << 30)
+#define DWC_USBCFG_FRC_HST_MODE		(1 << 29)
+#define DWC_USBCFG_TERM_SEL_DL_PULSE	(1 << 22)
+#define DWC_USBCFG_ULPI_INTVBUS_INDICATOR (1 << 21)
+#define DWC_USBCFG_ULPI_EXT_VBUS_DRV	(1 << 20)
+#define DWC_USBCFG_ULPI_CLK_SUS_M	(1 << 19)
+#define DWC_USBCFG_ULPI_AUTO_RES	(1 << 18)
+#define DWC_USBCFG_ULPI_FSLS		(1 << 17)
+#define DWC_USBCFG_OTGUTMIFSSEL		(1 << 16)
+#define DWC_USBCFG_PHYLPWRCLKSEL	(1 << 15)
+#define DWC_USBCFG_NPTXFRWNDEN		(1 << 14)
+#define DWC_USBCFG_TRN_TIME(x)		(x << 10)
+#define DWC_USBCFG_HNP_CAP		(1 << 9)
+#define DWC_USBCFG_SRP_CAP		(1 << 8)
+#define DWC_USBCFG_DDRSEL		(1 << 7)
+#define DWC_USBCFG_USB_2_11		(1 << 6)
+#define DWC_USBCFG_FSINTF		(1 << 5)
+#define DWC_USBCFG_ULPI_UTMI_SEL	(1 << 4)
+#define DWC_USBCFG_PHYIF		(1 << 3)
+#define DWC_USBCFG_TOUT_CAL(x)		(x << 0)
+
+/*
+ * These Macros represents the bit fields of the Core Reset Register (GRSTCTL).
+ * Set/clear the bits using the bit fields then write the u32 value to the
+ * register.
+ */
+#define DWC_RSTCTL_AHB_IDLE	(1 << 31)
+#define DWC_RSTCTL_DMA_REQ	(1 << 30)
+#define DWC_RSTCTL_TX_FIFO_NUM(reg, x)	\
+	(((reg) & (~((u32)0x1f << 6))) | ((x) << 6))
+#define DWC_RSTCTL_TX_FIFO_FLUSH	(1 << 5)
+#define DWC_RSTCTL_RX_FIFO_FLUSH	(1 << 4)
+#define DWC_RSTCTL_TKN_QUE_FLUSH	(1 << 3)
+#define DWC_RSTCTL_HSTFRM_CNTR_RST	(1 << 2)
+#define DWC_RSTCTL_HCLK_SFT_RST	(1 << 1)
+#define DWC_RSTCTL_SFT_RST	(1 << 1)
+#define DWC_GRSTCTL_TXFNUM_ALL	0x10
+
+/*
+ * These Macros represents the bit fields of the Core Interrupt Mask Register
+ * (GINTMSK).  Set/clear the bits using the bit fields then write the u32 value
+ * to the register.
+ */
+#define DWC_INTMSK_WKP			(1 << 31)
+#define DWC_INTMSK_NEW_SES_DET		(1 << 30)
+#define DWC_INTMSK_SES_DISCON_DET	(1 << 29)
+#define DWC_INTMSK_CON_ID_STS_CHG	(1 << 28)
+#define DWC_INTMSK_P_TXFIFO_EMPTY	(1 << 26)
+#define DWC_INTMSK_HST_CHAN		(1 << 25)
+#define DWC_INTMSK_HST_PORT		(1 << 24)
+#define DWC_INTMSK_DATA_FETCH_SUS	(1 << 23)
+#define DWC_INTMSK_INCMP_PTX		(1 << 22)
+#define DWC_INTMSK_INCMP_OUT_PTX	(1 << 21)
+#define DWC_INTMSK_INCMP_IN_ATX		(1 << 20)
+#define DWC_INTMSK_OUT_ENDP		(1 << 19)
+#define DWC_INTMSK_IN_ENDP		(1 << 18)
+#define DWC_INTMSK_ENDP_MIS_MTCH	(1 << 17)
+#define DWC_INTMSK_END_OF_PFRM		(1 << 15)
+#define DWC_INTMSK_ISYNC_OUTPKT_DRP	(1 << 14)
+#define DWC_INTMSK_ENUM_DONE		(1 << 13)
+#define DWC_INTMSK_USB_RST		(1 << 12)
+#define DWC_INTMSK_USB_SUSP		(1 << 11)
+#define DWC_INTMSK_EARLY_SUSP		(1 << 10)
+#define DWC_INTMSK_I2C_INTR		(1 << 9)
+#define DWC_INTMSK_GLBL_OUT_NAK		(1 << 7)
+#define DWC_INTMSK_GLBL_IN_NAK		(1 << 6)
+#define DWC_INTMSK_NP_TXFIFO_EMPT	(1 << 5)
+#define DWC_INTMSK_RXFIFO_NOT_EMPT	(1 << 4)
+#define DWC_INTMSK_STRT_OF_FRM		(1 << 3)
+#define DWC_INTMSK_OTG			(1 << 2)
+#define DWC_INTMSK_MODE_MISMTC		(1 << 1)
+/*
+ * These Macros represents the bit fields of the Core Interrupt Register
+ * (GINTSTS).  Set/clear the bits using the bit fields then write the u32 value
+ * to the register.
+ */
+#define DWC_INTSTS_WKP			(1 << 31)
+#define DWC_INTSTS_NEW_SES_DET		(1 << 30)
+#define DWC_INTSTS_SES_DISCON_DET	(1 << 29)
+#define DWC_INTSTS_CON_ID_STS_CHG	(1 << 28)
+#define DWC_INTSTS_P_TXFIFO_EMPTY	(1 << 26)
+#define DWC_INTSTS_HST_CHAN		(1 << 25)
+#define DWC_INTSTS_HST_PORT		(1 << 24)
+#define DWC_INTSTS_DATA_FETCH_SUS	(1 << 23)
+#define DWC_INTSTS_INCMP_PTX		(1 << 22)
+#define DWC_INTSTS_INCMP_OUT_PTX	(1 << 21)
+#define DWC_INTSTS_INCMP_IN_ATX		(1 << 20)
+#define DWC_INTSTS_OUT_ENDP		(1 << 19)
+#define DWC_INTSTS_IN_ENDP		(1 << 18)
+#define DWC_INTSTS_ENDP_MIS_MTCH	(1 << 17)
+#define DWC_INTSTS_END_OF_PFRM		(1 << 15)
+#define DWC_INTSTS_ISYNC_OUTPKT_DRP	(1 << 14)
+#define DWC_INTSTS_ENUM_DONE		(1 << 13)
+#define DWC_INTSTS_USB_RST		(1 << 12)
+#define DWC_INTSTS_USB_SUSP		(1 << 11)
+#define DWC_INTSTS_EARLY_SUSP		(1 << 10)
+#define DWC_INTSTS_I2C_INTR		(1 << 9)
+#define DWC_INTSTS_GLBL_OUT_NAK		(1 << 7)
+#define DWC_INTSTS_GLBL_IN_NAK		(1 << 6)
+#define DWC_INTSTS_NP_TXFIFO_EMPT	(1 << 5)
+#define DWC_INTSTS_RXFIFO_NOT_EMPT	(1 << 4)
+#define DWC_INTSTS_STRT_OF_FRM		(1 << 3)
+#define DWC_INTSTS_OTG			(1 << 2)
+#define DWC_INTSTS_MODE_MISMTC		(1 << 1)
+#define DWC_INTSTS_CURR_MODE		(1 << 0)
+#define DWC_SOF_INTR_MASK		0x0008
+#define DWC_HOST_MODE			1
+
+/*
+ * These Macros represents the bit fields in the Device Receive Status Read and
+ * Pop Registers (GRXSTSR, GRXSTSP) Read the register into the u32
+ * element then read out the bits using the bit elements.
+ */
+#define DWC_DM_RXSTS_PKT_STS	(0x01f << 17)
+#define DWC_DM_RXSTS_PKT_DPID	(0x003 << 15)
+#define DWC_DM_RXSTS_BYTE_CNT	(0x7ff << 4)
+#define DWC_DM_RXSTS_CHAN_NUM	(0x00f << 0)
+
+#define DWC_DM_RXSTS_PKT_STS_RD(reg)	(((reg) & (0x00f << 17)) >> 17)
+#define DWC_DM_RXSTS_PKT_DPID_RD(reg)	(((reg) & (0x003 << 15)) >> 15)
+#define DWC_DM_RXSTS_BYTE_CNT_RD(reg)	(((reg) & (0x7ff << 04)) >> 04)
+#define DWC_DM_RXSTS_CHAN_NUM_RD(reg)	((reg) & 0x00f)
+
+#define DWC_STS_DATA_UPDT		0x2	/* OUT Data Packet */
+#define DWC_STS_XFER_COMP		0x3	/* OUT Data Transfer Complete */
+#define DWC_DSTS_GOUT_NAK		0x1	/* Global OUT NAK */
+#define DWC_DSTS_SETUP_COMP		0x4	/* Setup Phase Complete */
+#define DWC_DSTS_SETUP_UPDT		0x6	/* SETUP Packet */
+
+/*
+ * These Macros represents the bit fields in the Host Receive Status Read and
+ * Pop Registers (GRXSTSR, GRXSTSP) Read the register into the u32
+ * element then read out the bits using the bit elements.
+ */
+#define DWC_HM_RXSTS_FRM_NUM	(0x00f << 21)
+#define DWC_HM_RXSTS_PKT_STS	(0x01f << 17)
+#define DWC_HM_RXSTS_PKT_DPID	(0x003 << 15)
+#define DWC_HM_RXSTS_BYTE_CNT	(0x7ff << 4)
+#define DWC_HM_RXSTS_CHAN_NUM	(0x00f << 0)
+
+#define DWC_HM_RXSTS_PKT_STS_RD(reg)	(((reg) & (0x00f << 17)) >> 17)
+#define DWC_HM_RXSTS_PKT_DPID_RD(reg)	(((reg) & (0x003 << 15)) >> 15)
+#define DWC_HM_RXSTS_BYTE_CNT_RD(reg)	(((reg) & (0x7ff << 04)) >> 04)
+#define DWC_HM_RXSTS_CHAN_NUM_RD(reg)	((reg) & 0x00f)
+
+#define DWC_GRXSTS_PKTSTS_IN			0x2
+#define DWC_GRXSTS_PKTSTS_IN_XFER_COMP		0x3
+#define DWC_GRXSTS_PKTSTS_DATA_TOGGLE_ERR	0x5
+#define DWC_GRXSTS_PKTSTS_CH_HALTED		0x7
+
+/*
+ * These Macros represents the bit fields in the FIFO Size Registers (HPTXFSIZ,
+ * GNPTXFSIZ, DPTXFSIZn). Read the register into the u32 element then
+ * read out the bits using the bit elements.
+ */
+#define DWC_RX_FIFO_DEPTH_RD(reg)	(((reg) & ((u32)0xffff << 16)) >> 16)
+#define DWC_RX_FIFO_DEPTH_WR(reg, x)	\
+	(((reg) & (~((u32)0xffff << 16))) | ((x) << 16))
+#define DWC_RX_FIFO_START_ADDR_RD(reg)		((reg) & 0xffff)
+#define DWC_RX_FIFO_START_ADDR_WR(reg, x)	\
+	(((reg) & (~((u32)0xffff))) | (x))
+
+/*
+ * These Macros represents the bit fields in the Non-Periodic Tx FIFO/Queue
+ * Status Register (GNPTXSTS). Read the register into the u32 element then read
+ * out the bits using the bit elements.
+ */
+#define DWC_GNPTXSTS_NPTXQTOP_CHNEP_RD(x)	(((x) & (0x3f << 26)) >> 26)
+#define DWC_GNPTXSTS_NPTXQTOP_TKN_RD(x)		(((x) & (0x03 << 24)) >> 24)
+#define DWC_GNPTXSTS_NPTXQSPCAVAIL_RD(x)	(((x) & (0xff << 16)) >> 16)
+#define DWC_GNPTXSTS_NPTXFSPCAVAIL_RD(x)	(0xffff & (x))
+
+/*
+ * These Macros represents the bit fields in the Transmit FIFO Status Register
+ * (DTXFSTS). Read the register into the u32 element then read out the bits
+ * using the bit elements.
+ */
+#define DWC_DTXFSTS_TXFSSPC_AVAI_RD(x)	((x) & 0xffff)
+
+/*
+ * These Macros represents the bit fields in the I2C Control Register (I2CCTL).
+ * Read the register into the u32 element then read out the bits using the bit
+ * elements.
+ */
+#define DWC_I2CCTL_BSYDNE	(1 << 31)
+#define DWC_I2CCTL_RW		(1 << 30)
+#define DWC_I2CCTL_I2CDEVADDR(x)	((x) << 27)
+#define DWC_I2CCTL_I2CSUSCTL	(1 << 25)
+#define DWC_I2CCTL_ACK		(1 << 24)
+#define DWC_I2CCTL_I2CEN	(1 << 23)
+#define DWC_I2CCTL_ADDR		(1 << 22)
+#define DWC_I2CCTL_REGADDR(x)	((x) << 14)
+#define DWC_I2CCTL_RWDATA(x)	((x) << 6)
+
+/*
+ * These Macros represents the bit fields in the User HW Config1 Register.  Read
+ * the register into the u32 element then read out the bits using the bit
+ * elements.
+ */
+#define DWC_HWCFG1_EPDIR15(x)	((x) << 30)
+#define DWC_HWCFG1_EPDIR14(x)	((x) << 28)
+#define DWC_HWCFG1_EPDIR13(x)	((x) << 26)
+#define DWC_HWCFG1_EPDIR12(x)	((x) << 24)
+#define DWC_HWCFG1_EPDIR11(x)	((x) << 22)
+#define DWC_HWCFG1_EPDIR10(x)	((x) << 20)
+#define DWC_HWCFG1_EPDIR9(x)	((x) << 18)
+#define DWC_HWCFG1_EPDIR8(x)	((x) << 16)
+#define DWC_HWCFG1_EPDIR7(x)	((x) << 14)
+#define DWC_HWCFG1_EPDIR6(x)	((x) << 13)
+#define DWC_HWCFG1_EPDIR5(x)	((x) << 10)
+#define DWC_HWCFG1_EPDIR4(x)	((x) << 08)
+#define DWC_HWCFG1_EPDIR3(x)	((x) << 06)
+#define DWC_HWCFG1_EPDIR2(x)	((x) << 04)
+#define DWC_HWCFG1_EPDIR1(x)	((x) << 02)
+#define DWC_HWCFG1_EPDIR0(x)	((x) << 00)
+
+/*
+ * These Macros represents the bit fields in the User HW Config2 Register.  Read
+ * the register into the u32 element then read out the bits using the bit
+ * elements.
+ */
+#define DWC_HWCFG2_DEV_TKN_Q_DEPTH_RD(x)	(((x) & (0x1F << 26)) >> 26)
+#define DWC_HWCFG2_HOST_PERIO_Q_DEPTH_RD(x)	(((x) & (0x3 << 24)) >> 24)
+#define DWC_HWCFG2_NP_TX_Q_DEPTH_RD(x)		(((x) & (0x3 << 22)) >> 22)
+#define DWC_HWCFG2_RX_STS_Q_DEPTH_RD(x)		(((x) & (0x3 << 20)) >> 20)
+#define DWC_HWCFG2_DYN_FIFO_RD(x)		(((x) & (0x1 << 19)) >> 19)
+#define DWC_HWCFG2_PERIO_EP_SUPP_RD(x)		(((x) & (0x1 << 18)) >> 18)
+#define DWC_HWCFG2_NO_HST_CHAN_RD(x)		(((x) & (0xf << 14)) >> 14)
+#define DWC_HWCFG2_NO_DEV_EP_RD(x)		(((x) & (0xf << 10)) >> 10)
+#define DWC_HWCFG2_FS_PHY_TYPE_RD(x)		(((x) & (0x3 <<  8)) >> 8)
+#define DWC_HWCFG2_HS_PHY_TYPE_RD(x)		(((x) & (0x3 << 06)) >> 06)
+#define DWC_HWCFG2_P_2_P_RD(x)			(((x) & (0x1 << 05)) >> 05)
+#define DWC_HWCFG2_ARCH_RD(x)			(((x) & (0x3 << 03)) >> 03)
+#define DWC_HWCFG2_OP_MODE_RD(x)		((x) & 0x7)
+
+#define DWC_HWCFG2_HS_PHY_TYPE_NOT_SUPPORTED		0
+#define DWC_HWCFG2_HS_PHY_TYPE_UTMI			1
+#define DWC_HWCFG2_HS_PHY_TYPE_ULPI			2
+#define DWC_HWCFG2_HS_PHY_TYPE_UTMI_ULPI		3
+#define DWC_HWCFG2_OP_MODE_HNP_SRP_CAPABLE_OTG		0
+#define DWC_HWCFG2_OP_MODE_SRP_ONLY_CAPABLE_OTG		1
+#define DWC_HWCFG2_OP_MODE_NO_HNP_SRP_CAPABLE_OTG	2
+#define DWC_HWCFG2_OP_MODE_SRP_CAPABLE_DEVICE		3
+#define DWC_HWCFG2_OP_MODE_NO_SRP_CAPABLE_DEVICE	4
+#define DWC_HWCFG2_OP_MODE_SRP_CAPABLE_HOST		5
+#define DWC_HWCFG2_OP_MODE_NO_SRP_CAPABLE_HOST		6
+
+/*
+ * These Macros represents the bit fields in the User HW Config3 Register.  ead
+ * the register into the u32 element then read out the bits using the bit
+ * elements.
+ */
+#define DWC_HWCFG3_DFIFO_DEPTH_RD(x)		(((x) & (0xffff << 16)) >> 16)
+#define	DWC_HWCFG3_AHB_PHY_CLK_SYNC_RD(x)	(((x) & (0x1 << 12)) >> 12)
+#define DWC_HWCFG3_SYNC_RST_TYPE_RD(x)		(((x) & (0x1 << 11)) >> 11)
+#define DWC_HWCFG3_OPT_FEATURES_RD(x)		(((x) & (0x1 << 10)) >> 10)
+#define DWC_HWCFG3_VEND_CTRL_IF_RD(x)		(((x) & (0x1 << 9)) >> 9)
+#define DWC_HWCFG3_I2C_RD(x)			(((x) & (0x1 << 8)) >> 8)
+#define DWC_HWCFG3_OTG_FUNC_RD(x)		(((x) & (0x1 << 07)) >> 07)
+#define DWC_HWCFG3_PKTSIZE_CTR_WIDTH_RD(x)	(((x) & (0x7 << 04)) >> 04)
+#define DWC_HWCFG3_XFERSIZE_CTR_WIDTH_RD(x)	((x) & 0xf)
+
+/*
+ * These Macros represents the bit fields in the User HW Config4 Register.  Read
+ * the register into the u32 element then read out the bits using the bit
+ * elements.
+ */
+#define DWC_HWCFG4_NUM_IN_EPS_RD(x)		(((x) & (0xF << 26)) >> 26)
+#define DWC_HWCFG4_DED_FIFO_ENA_RD(x)		(((x) & (0x1 << 25)) >> 25)
+#define DWC_HWCFG4_SES_END_FILT_EN_RD(x)	(((x) & (0x1 << 24)) >> 24)
+#define DWC_HWCFG4_BVALID_FILT_EN_RD(x)		(((x) & (0x1 << 23)) >> 23)
+#define DWC_HWCFG4_AVALID_FILT_EN_RD(x)		(((x) & (0x1 << 22)) >> 22)
+#define DWC_HWCFG4_VBUS_VALID_FILT_EN_RD(x)	(((x) & (0x1 << 21)) >> 21)
+#define DWC_HWCFG4_IDDIG_FILT_EN_RD(x)		(((x) & (0x1 << 20)) >> 20)
+#define DWC_HWCFG4_NUM_DEV_MODE_CTRL_EP_RD(x)	(((x) & (0xF << 16)) >> 16)
+#define DWC_HWCFG4_UTMI_PHY_DATA_WIDTH_RD(x)	(((x) & (0x3 << 14)) >> 14)
+#define DWC_HWCFG4_MIN_AHB_FREQ_RD(x)		(((x) & (0x1 << 05)) >> 05)
+#define DWC_HWCFG4_POWER_OPT_RD(x)		(((x) & (0x1 << 04)) >> 04)
+#define	DWC_HWCFG4_NUM_DEV_PERIO_IN_EP_RD(x)	((x) & 0xf)
+
+/*
+ * Device Global Registers. Offsets 800h-BFFh
+ *
+ * The following structures define the size and relative field offsets for the
+ * Device Mode Registers.
+ *
+ * These registers are visible only in Device mode and must not be accessed in
+ * Host mode, as the results are unknown.
+ */
+#define DWC_DCFG	0x000
+#define DWC_DCTL	0x004
+#define DWC_DSTS	0x008
+#define DWC_DIEPMSK	0x010
+#define DWC_DOEPMSK	0x014
+#define DWC_DAINT	0x018
+#define DWC_DAINTMSK	0x01C
+#define DWC_DTKNQR1	0x020
+#define DWC_DTKNQR2	0x024
+#define DWC_DVBUSDIS	0x028
+#define DWC_DVBUSPULSE	0x02C
+#define DWC_DTKNQR3_DTHRCTL	0x030
+#define DWC_DTKNQR4FIFOEMPTYMSK	0x034
+
+/*
+ * These Macros represents the bit fields in the Device Configuration
+ * Register.  Read the register into the u32 member then
+ * set/clear the bits using the bit elements.  Write the
+ * u32 member to the dcfg register.
+*/
+#define DWC_DCFG_IN_EP_MISMATCH_CNT_RD(x)	(((x) & (0x1f << 18)) >> 18)
+#define DWC_DCFG_P_FRM_INTRVL_RD(x)		(((x) & (0x03 << 11)) >> 11)
+#define DWC_DCFG_DEV_ADDR_RD(x)			(((x) & (0x3f << 04)) >> 04)
+#define DWC_DCFG_NGL_STS_OUT_RD(x)		(((x) & (0x1 << 2)) >> 2)
+#define DWC_DCFG_DEV_SPEED_RD(x)		((x) & 0x3)
+
+#define DWC_DCFG_IN_EP_MISMATCH_CNT_WR(reg, x)	\
+	(((reg) & (~((u32)0x1f << 18))) | ((x) << 18))
+#define DWC_DCFG_P_FRM_INTRVL_WR(reg, x)	\
+	(((reg) & (~((u32)0x03 << 11))) | ((x) << 11))
+#define DWC_DCFG_DEV_ADDR_WR(reg, x)	\
+	(((reg) & (~((u32)0x3f << 04))) | ((x) << 04))
+#define DWC_DCFG_NGL_STS_OUT_WR(reg, x)	\
+	(((reg) & (~((u32)0x1 << 2)))   | ((x) << 2))
+#define DWC_DCFG_DEV_SPEED_WR(reg, x)	\
+	(((reg) & (~(u32)0x3)) | (x))
+
+#define DWC_DCFG_FRAME_INTERVAL_80		0
+#define DWC_DCFG_FRAME_INTERVAL_85		1
+#define DWC_DCFG_FRAME_INTERVAL_90		2
+#define DWC_DCFG_FRAME_INTERVAL_95		3
+
+/*
+ * These Macros represents the bit fields in the Device Control Register.  Read
+ * the register into the u32 member then set/clear the bits using the bit
+ * elements.
+ */
+#define DWC_DCTL_PWR_ON_PROG_DONE_RD(x)	(((x) & (1 << 11)) >> 11)
+
+#define DWC_DCTL_PWR_ON_PROG_DONE_WR(reg, x)	\
+	(((reg) & (~((u32)0x01 << 11))) | ((x) << 11))
+#define DWC_DCTL_CLR_GLBL_OUT_NAK_WR(reg, x)	\
+	(((reg) & (~((u32)0x01 << 10))) | ((x) << 10))
+#define DWC_DCTL_SET_GLBL_OUT_NAL(reg, x)	\
+	(((reg) & (~((u32)0x01 << 9))) | ((x) << 9))
+#define DWC_DCTL_CLR_CLBL_NP_IN_NAK(reg, x)	\
+	(((reg) & (~((u32)0x01 << 8))) | ((x) << 8))
+#define DWC_DCTL_SET_GLBL_NP_IN_NAK(reg, x)	\
+	(((reg) & (~((u32)0x01 << 07))) | ((x) << 07))
+#define DWC_DCTL_TST_CTL(reg, x)	\
+	(((reg) & (~((u32)0x07 << 04))) | ((x) << 04))
+#define DWC_DCTL_GLBL_OUT_NAK_STS(reg, x)	\
+	(((reg) & (~((u32)0x01 << 03))) | ((x) << 03))
+#define DWC_DCTL_GLBL_NP_IN_NAK(reg, x)		\
+	(((reg) & (~((u32)0x01 << 02))) | ((x) << 02))
+#define DWC_DCTL_SFT_DISCONNECT(reg, x)		\
+	(((reg) & (~((u32)0x01 << 01))) | ((x) << 01))
+#define DEC_DCTL_REMOTE_WAKEUP_SIG(reg, x)	\
+	(((reg) & (~((u32)0x01 << 00))) | ((x) << 00))
+
+/*
+ * These Macros represents the bit fields in the Dev Status Register. Read the
+ * register into the u32 member then set/clear the bits using the bit elements.
+ */
+#define DWC_DSTS_SOFFN_RD(x)		(((x) & (0x3fff << 8)) >> 8)
+#define DWC_DSTS_ERRTICERR_RD(x)	(((x) & (0x0001 << 3)) >> 3)
+#define DWC_DSTS_ENUM_SPEED_RD(x)	(((x) & (0x0003 << 1)) >> 1)
+#define DWC_DSTS_SUSP_STS_RD(x)		((x) & 1)
+
+#define DWC_DSTS_ENUMSPD_HS_PHY_30MHZ_OR_60MHZ		0
+#define DWC_DSTS_ENUMSPD_FS_PHY_30MHZ_OR_60MHZ		1
+#define DWC_DSTS_ENUMSPD_LS_PHY_6MHZ			2
+#define DWC_DSTS_ENUMSPD_FS_PHY_48MHZ			3
+
+/*
+ * These Macros represents the bit fields in the Device IN EP Interrupt Register
+ * and the Device IN EP Common Mask Register.
+ *
+ * Read the register into the u32 member then set/clear the bits using the bit
+ * elements.
+ */
+#define DWC_DIEPINT_TXFIFO_UNDERN_RD(x)		(((x) & (0x1 << 8)) >> 8)
+#define DWC_DIEPINT_TXFIFO_EMPTY_RD(x)		(((x) & (0x1 << 7)) >> 7)
+#define DWC_DIEPINT_IN_EP_NAK_RD(x)		(((x) & (0x1 << 6)) >> 6)
+#define DWC_DIEPINT_IN_TKN_EP_MISS_RD(x)	(((x) & (0x1 << 5)) >> 5)
+#define DWC_DIEPINT_IN_TKN_TX_EMPTY_RD(x)	(((x) & (0x1 << 4)) >> 4)
+#define DWC_DIEPINT_TOUT_COND_RD(x)		(((x) & (0x1 << 3)) >> 3)
+#define DWC_DIEPINT_AHB_ERROR_RD(x)		(((x) & (0x1 << 2)) >> 2)
+#define DWC_DIEPINT_EP_DISA_RD(x)		(((x) & (0x1 << 1)) >> 1)
+#define DWC_DIEPINT_TX_CMPL_RD(x)		((x) & 0x1)
+
+#define DWC_DIEPINT_TXFIFO_UNDERN_RW(reg, x)	\
+	(((reg) & (~((u32)0x01 << 8))) | ((x) << 8))
+#define DWC_DIEPINT_TXFIFO_EMPTY_RW(reg, x)	\
+	(((reg) & (~((u32)0x01 << 7))) | ((x) << 7))
+#define DWC_DIEPINT_IN_EP_NAK_RW(reg, x)	\
+	(((reg) & (~((u32)0x01 << 6))) | ((x) << 6))
+#define DWC_DIEPINT_IN_TKN_EP_MISS_RW(reg, x)	\
+	(((reg) & (~((u32)0x01 << 5))) | ((x) << 5))
+#define DWC_DIEPINT_IN_TKN_TX_EMPTY_RW(reg, x)	\
+	(((reg) & (~((u32)0x01 << 4))) | ((x) << 4))
+#define DWC_DIEPINT_TOUT_COND_RW(reg, x)	\
+	(((reg) & (~((u32)0x01 << 3))) | ((x) << 3))
+#define DWC_DIEPINT_AHB_ERROR_RW(reg, x)	\
+	(((reg) & (~((u32)0x01 << 2))) | ((x) << 2))
+#define DWC_DIEPINT_EP_DISA_RW(reg, x)		\
+	(((reg) & (~((u32)0x01 << 1))) | ((x) << 1))
+#define DWC_DIEPINT_TX_CMPL_RW(reg, x)		\
+	(((reg) & (~((u32)0x01 << 0))) | ((x) << 0))
+
+#define DWC_DIEPMSK_TXFIFO_UNDERN_RW(reg, x)	\
+	(((reg) & (~((u32)0x01 << 8))) | ((x) << 8))
+#define DWC_DIEPMSK_TXFIFO_EMPTY_RW(reg, x)	\
+	(((reg) & (~((u32)0x01 << 7))) | ((x) << 7))
+#define DWC_DIEPMSK_IN_EP_NAK_RW(reg, x)	\
+	(((reg) & (~((u32)0x01 << 6))) | ((x) << 6))
+#define DWC_DIEPMSK_IN_TKN_EP_MISS_RW(reg, x)	\
+	(((reg) & (~((u32)0x01 << 5))) | ((x) << 5))
+#define DWC_DIEPMSK_IN_TKN_TX_EMPTY_RW(reg, x)	\
+	(((reg) & (~((u32)0x01 << 4))) | ((x) << 4))
+#define DWC_DIEPMSK_TOUT_COND_RW(reg, x)	\
+	(((reg) & (~((u32)0x01 << 3))) | ((x) << 3))
+#define DWC_DIEPMSK_AHB_ERROR_RW(reg, x)	\
+	(((reg) & (~((u32)0x01 << 2))) | ((x) << 2))
+#define DWC_DIEPMSK_EP_DISA_RW(reg, x)		\
+	(((reg) & (~((u32)0x01 << 1))) | ((x) << 1))
+#define DWC_DIEPMSK_TX_CMPL_RW(reg, x)		\
+	(((reg) & (~((u32)0x01 << 0))) | ((x) << 0))
+
+/*
+ * These Macros represents the bit fields in the Device OUT EP Itr Register
+ * and Device OUT EP Common Interrupt Mask Register.
+ *
+ * Read the register into the u32 member then set/clear the bits using the bit
+ * elements.
+ */
+#define DWC_DOEPINT_OUTPKT_ERR_RD(x)	(((x) & (0x1 << 8)) >> 8)
+#define DWC_DOEPINT_B2B_PKTS_RD(x)	(((x) & (0x1 << 6)) >> 6)
+#define DWC_DOEPINT_OUT_TKN_RD(x)	(((x) & (0x1 << 4)) >> 4)
+#define DWC_DOEPINT_SETUP_DONE_RD(x)	(((x) & (0x1 << 3)) >> 3)
+#define DWC_DOEPINT_AHB_ERROR_RD(x)	(((x) & (0x1 << 2)) >> 2)
+#define DWC_DOEPINT_EP_DISA_RD(x)	(((x) & (0x1 << 1)) >> 1)
+#define DWC_DOEPINT_TX_COMPL_RD(x)	(((x) & (0x1 << 0)) >> 0)
+
+#define DWC_DOEPMSK_OUTPKT_ERR_RW(reg, x)	\
+	(((reg) & (~((u32)0x01 << 8))) | ((x) << 8))
+#define DWC_DOEPMSK_B2B_PKTS_RW(reg, x)		\
+	(((reg) & (~((u32)0x01 << 6))) | ((x) << 6))
+#define DWC_DOEPMSK_OUT_TKN_RW(reg, x)		\
+	(((reg) & (~((u32)0x01 << 4))) | ((x) << 4))
+#define DWC_DOEPMSK_SETUP_DONE_RW(reg, x)	\
+	(((reg) & (~((u32)0x01 << 3))) | ((x) << 3))
+#define DWC_DOEPMSK_AHB_ERROR_RW(reg, x)	\
+	(((reg) & (~((u32)0x01 << 2))) | ((x) << 2))
+#define DWC_DOEPMSK_EP_DISA_RW(reg, x)		\
+	(((reg) & (~((u32)0x01 << 1))) | ((x) << 1))
+#define DWC_DOEPMSK_TX_COMPL_RW(reg, x)		\
+	(((reg) & (~((u32)0x01 << 0))) | ((x) << 0))
+
+/*
+ * These Macros represents the bit fields in the Device All EP Intr and Mask
+ * Registers.  Read the register into the u32 member then set/clear the bits
+ * using the bit elements.
+ */
+#define DWC_DAINT_OUT_EP_RD(reg, ep)	\
+	(((reg) & (1 << (ep + 16))) >> (ep + 16))
+#define DWC_DAINTMSK_OUT_EP_RW(reg, ep)	\
+	(((reg) & (~(u32)(1 << (ep + 16)))) | (1 << (ep + 16)))
+#define DWC_DAINT_IN_EP_RD(reg, ep)		(((reg) & (1 << ep)) >> ep)
+#define DWC_DAINTMSK_IN_EP_RW(reg, ep)	\
+	(((reg) & (~(u32)(1 << ep))) | (1 << ep))
+#define DWC_DAINT_OUTEP15	(1 << 31)
+#define DWC_DAINT_OUTEP14	(1 << 30)
+#define DWC_DAINT_OUTEP13	(1 << 29)
+#define DWC_DAINT_OUTEP12	(1 << 28)
+#define DWC_DAINT_OUTEP11	(1 << 27)
+#define DWC_DAINT_OUTEP10	(1 << 26)
+#define DWC_DAINT_OUTEP09	(1 << 25)
+#define DWC_DAINT_OUTEP08	(1 << 24)
+#define DWC_DAINT_OUTEP07	(1 << 23)
+#define DWC_DAINT_OUTEP06	(1 << 22)
+#define DWC_DAINT_OUTEP05	(1 << 21)
+#define DWC_DAINT_OUTEP04	(1 << 20)
+#define DWC_DAINT_OUTEP03	(1 << 19)
+#define DWC_DAINT_OUTEP02	(1 << 18)
+#define DWC_DAINT_OUTEP01	(1 << 17)
+#define DWC_DAINT_OUTEP00	(1 << 16)
+#define DWC_DAINT_INEP15	(1 << 15)
+#define DWC_DAINT_INEP14	(1 << 14)
+#define DWC_DAINT_INEP13	(1 << 13)
+#define DWC_DAINT_INEP12	(1 << 12)
+#define DWC_DAINT_INEP11	(1 << 11)
+#define DWC_DAINT_INEP10	(1 << 10)
+#define DWC_DAINT_INEP09	(1 << 09)
+#define DWC_DAINT_INEP08	(1 << 08)
+#define DWC_DAINT_INEP07	(1 << 07)
+#define DWC_DAINT_INEP06	(1 << 06)
+#define DWC_DAINT_INEP05	(1 << 05)
+#define DWC_DAINT_INEP04	(1 << 04)
+#define DWC_DAINT_INEP03	(1 << 03)
+#define DWC_DAINT_INEP02	(1 << 02)
+#define DWC_DAINT_INEP01	(1 << 01)
+#define DWC_DAINT_INEP00	(1 << 00)
+
+/*
+ * These Macros represents the bit fields in the Device IN Token Queue Read
+ * Registers.  Read the register into the u32 member. READ-ONLY Register
+ */
+#define DWC_DTKNQR1_EP_TKN_NO_RD(x)		(((x) & (0xffffff << 8)) >> 8)
+#define DWC_DTKNQR1_WRAP_BIT_RD(x)		(((x) & (1 << 7)) >> 7)
+#define DWC_DTKNQR1_INT_TKN_Q_WR_PTR_RD(x)	((x)  & 0x1f)
+
+/*
+ * These Macros represents Threshold control Register. Read and wr the register
+ * into the u32 member.  READ-WRITABLE Register
+ */
+#define DWC_DTHCTRL_RX_ARB_PARK_EN_RD(x)	(((x) & (0x001 << 27)) >> 27)
+#define DWC_DTHCTRL_RX_THR_LEN_RD(x)		(((x) & (0x1ff << 17)) >> 17)
+#define DWC_DTHCTRL_RX_THR_EN_RD(x)		(((x) & (0x001 << 16)) >> 16)
+#define DWC_DTHCTRL_TX_THR_LEN_RD(x)		(((x) & (0x1ff << 02)) >> 02)
+#define DWC_DTHCTRL_ISO_THR_EN(x)		(((x) & (0x001 << 01)) >> 01)
+#define DWC_DTHCTRL_NON_ISO_THR_ENA_RD(x)	(((x) & (0x001 << 00)) >> 00)
+
+#define DWC_DTHCTRL_RX_ARB_PARK_EN_RW(reg, x)	\
+	(((reg) & (~((u32)0x001 << 27))) | ((x) << 27))
+#define DWC_DTHCTRL_RX_THR_LEN_RW(reg, x)	\
+	(((reg) & (~((u32)0x1ff << 17))) | ((x) << 17))
+#define DWC_DTHCTRL_RX_THR_EN_RW(reg, x)	\
+	(((reg) & (~((u32)0x001 << 16))) | ((x) << 16))
+#define DWC_DTHCTRL_TX_THR_LEN_RW(reg, x)	\
+	(((reg) & (~((u32)0x1ff << 02))) | ((x) << 02))
+#define DWC_DTHCTRL_ISO_THR_EN_RW(reg, x)	\
+	(((reg) & (~((u32)0x001 << 01))) | ((x) << 01))
+#define DWC_DTHCTRL_NON_ISO_THR_ENA_RW(reg, x)	\
+	(((reg) & (~((u32)0x001 << 00))) | ((x) << 00))
+
+/*
+ * Device Logical IN Endpoint-Specific Registers. Offsets 900h-AFCh
+ *
+ * There will be one set of endpoint registers per logical endpoint implemented.
+ *
+ * These registers are visible only in Device mode and must not be accessed in
+ * Host mode, as the results are unknown.
+ */
+#define DWC_DIEPCTL         0x00
+#define DWC_DIEPINT         0x08
+#define DWC_DIEPTSIZ        0x10
+#define DWC_DIEPDMA         0x14
+#define DWC_DTXFSTS         0x18
+
+/*
+ * Device Logical OUT Endpoint-Specific Registers. Offsets: B00h-CFCh
+ *
+ * There will be one set of endpoint registers per logical endpoint implemented.
+ *
+ * These registers are visible only in Device mode and must not be accessed in
+ * Host mode, as the results are unknown.
+ */
+#define DWC_DOEPCTL		0x00
+#define DWC_DOEPFN		0x04
+#define DWC_DOEPINT		0x08
+#define DWC_DOEPTSIZ		0x10
+#define DWC_DOEPDMA		0x14
+
+/*
+ * These Macros represents the bit fields in the Device EP Ctrl Register. Read
+ * the register into the u32 member then set/clear the bits using the bit
+ * elements.
+ */
+#define DWC_DEP0CTL_MPS_64			0
+#define DWC_DEP0CTL_MPS_32			1
+#define DWC_DEP0CTL_MPS_16			2
+#define DWC_DEP0CTL_MPS_8			3
+
+#define DWC_DEPCTL_EPENA_RD(x)		(((x) & (0x1 << 31)) >> 31)
+#define DWC_DEPCTL_EPDIS_RD(x)		(((x) & (0x1 << 30)) >> 30)
+#define DWC_DEPCTL_SET_DATA1_PID_RD(x)	(((x) & (0x1 << 29)) >> 29)
+#define DWC_DEPCTL_SET_DATA0_PID_RD(x)	(((x) & (0x1 << 28)) >> 28)
+#define DWC_DEPCTL_SET_NAK_RD(x)	(((x) & (0x1 << 27)) >> 27)
+#define DWC_DEPCTL_CLR_NAK_RD(x)	(((x) & (0x1 << 26)) >> 26)
+#define DWC_DEPCTL_TX_FIFO_NUM_RD(x)	(((x) & (0xf << 22)) >> 22)
+#define DWC_DEPCTL_STALL_HNDSHK	_RD(x)	(((x) & (0x1 << 21)) >> 21)
+#define DWC_DEPCTL_SNP_MODE_RD(x)	(((x) & (0x1 << 20)) >> 20)
+#define DWC_DEPCTL_EP_TYPE_RD(x)	(((x) & (0x3 << 18)) >> 18)
+#define DWC_DEPCTL_NKASTS_RD(x)		(((x) & (0x1 << 17)) >> 17)
+#define DWC_DEPCTL_DPID	_RD(x)		(((x) & (0x1 << 16)) >> 16)
+#define DWC_DEPCTL_ACT_EP_RD(x)		(((x) & (0x1 << 15)) >> 15)
+#define DWC_DEPCTL_NXT_EP_RD(x)		(((x) & (0xf << 11)) >> 11)
+#define DWC_DEPCTL_MPS_RD(x)		(((x) & (0x7ff << 00)) >> 00)
+
+#define DWC_DEPCTL_EPENA_RW(reg, x)		\
+	(((reg) & (~((u32)0x001 << 31))) | ((x) << 31))
+#define DWC_DEPCTL_EPDIS_RW(reg, x)		\
+	(((reg) & (~((u32)0x001 << 30))) | ((x) << 30))
+#define DWC_DEPCTL_SET_DATA1_PID_RW(reg, x)	\
+	(((reg) & (~((u32)0x001 << 29))) | ((x) << 29))
+#define DWC_DEPCTL_SET_DATA0_PID_RW(reg, x)	\
+	(((reg) & (~((u32)0x001 << 28))) | ((x) << 28))
+#define DWC_DEPCTL_SET_NAK_RW(reg, x)		\
+	(((reg) & (~((u32)0x001 << 27))) | ((x) << 27))
+#define DWC_DEPCTL_CLR_NAK_RW(reg, x)		\
+	(((reg) & (~((u32)0x001 << 26))) | ((x) << 26))
+#define DWC_DEPCTL_TX_FIFO_NUM_RW(reg, x)	\
+	(((reg) & (~((u32)0x00f << 22))) | ((x) << 22))
+#define DWC_DEPCTL_STALL_HNDSHK_RW(reg, x)	\
+	(((reg) & (~((u32)0x001 << 21))) | ((x) << 21))
+#define DWC_DEPCTL_SNP_MODE_RW(reg, x)		\
+	(((reg) & (~((u32)0x001 << 20))) | ((x) << 20))
+#define DWC_DEPCTL_EP_TYPE_RW(reg, x)		\
+	(((reg) & (~((u32)0x003 << 18))) | ((x) << 18))
+#define DWC_DEPCTL_NKASTS_RW(reg, x)		\
+	(((reg) & (~((u32)0x001 << 17))) | ((x) << 17))
+#define DWC_DEPCTL_DPID_RW(reg, x)		\
+	(((reg) & (~((u32)0x001 << 16))) | ((x) << 16))
+#define DWC_DEPCTL_ACT_EP_RW(reg, x)		\
+	(((reg) & (~((u32)0x001 << 15))) | ((x) << 15))
+#define DWC_DEPCTL_NXT_EP_RW(reg, x)		\
+	(((reg) & (~((u32)0x00f << 11))) | ((x) << 11))
+#define DWC_DEPCTL_MPS_RW(reg, x)		\
+	(((reg) & (~((u32)0x7ff << 00))) | ((x) << 00))
+
+/*
+ * These Macros represents the bit fields in the Device EP Txfer Size Register.
+ * Read the register into the u32 member then set/clear the bits using the bit
+ * elements.
+ */
+#if defined(CONFIG_DWC_LIMITED_XFER_SIZE)
+#define DWC_DEPTSIZ_MCOUNT_RD(x)	(((x) & (0x003 << 29)) >> 29)
+#define	DWC_DEPTSIZ_PKT_CNT_RD(x)	(((x) & (0x01f << 19)) >> 19)
+#define DWC_DEPTSIZ_XFER_SIZ_RD(x)	(((x) & (0x7ff << 00)) >> 00)
+#define DWC_DEPTSIZ_MCOUNT_RW(reg, x)	\
+	(((reg) & (~((u32)0x003 << 29))) | ((x) << 29))
+#define	DWC_DEPTSIZ_PKT_CNT_RW(reg, x)	\
+	(((reg) & (~((u32)0x01f << 19))) | ((x) << 19))
+#define DWC_DEPTSIZ_XFER_SIZ_RW(reg, x)	\
+	(((reg) & (~((u32)0x7ff << 00))) | ((x) << 00))
+#else
+#define DWC_DEPTSIZ_MCOUNT_RD(x)	\
+	(((x) & (0x003 << 29)) >> 29)
+#define	DWC_DEPTSIZ_PKT_CNT_RD(x)	\
+	(((x) & (0x3ff << 19)) >> 19)
+#define DWC_DEPTSIZ_XFER_SIZ_RD(x)	\
+	(((x) & (0x7ffff << 00)) >> 00)
+#define DWC_DEPTSIZ_MCOUNT_RW(reg, x)	\
+	(((reg) & (~((u32)0x003 << 29))) | ((x) << 29))
+#define	DWC_DEPTSIZ_PKT_CNT_RW(reg, x)	\
+	(((reg) & (~((u32)0x7ff << 19))) | ((x) << 19))
+#define DWC_DEPTSIZ_XFER_SIZ_RW(reg, x)	\
+	(((reg) & (~((u32)0x7ffff << 00))) | ((x) << 00))
+#endif
+
+/*
+ * These Macros represents the bit fields in the Device EP 0 Transfer Size
+ * Register.  Read the register into the u32 member then set/clear the bits
+ * using the bit elements.
+ */
+#define DWC_DEPTSIZ0_SUPCNT_RD(x)	(((x) & (0x003 << 29)) >> 29)
+#define	DWC_DEPTSIZ0_PKT_CNT_RD(x)	(((x) & (0x001 << 19)) >> 19)
+#define DWC_DEPTSIZ0_XFER_SIZ_RD(x)	(((x) & (0x07f << 00)) >> 00)
+#define DWC_DEPTSIZ0_SUPCNT_RW(reg, x)	\
+	(((reg) & (~((u32)0x003 << 29))) | ((x) << 29))
+#define	DWC_DEPTSIZ0_PKT_CNT_RW(reg, x)	\
+	(((reg) & (~((u32)0x001 << 19))) | ((x) << 19))
+#define DWC_DEPTSIZ0_XFER_SIZ_RW(reg, x)	\
+	(((reg) & (~((u32)0x07f << 00))) | ((x) << 00))
+
+#define MAX_PERIO_FIFOS			15	/* Max periodic FIFOs */
+#define MAX_TX_FIFOS			15	/* Max non-periodic FIFOs */
+
+/* Maximum number of Endpoints/HostChannels */
+#define MAX_EPS_CHANNELS 12	/* This come from device tree or defconfig */
+
+/*
+ * The device_if structure contains information needed to manage the DWC_otg
+ * controller acting in device mode. It represents the programming view of the
+ * device-specific aspects of the controller.
+ */
+struct device_if {
+	/* Device Global Registers starting at offset 800h */
+	u32 dev_global_regs;
+#define DWC_DEV_GLOBAL_REG_OFFSET		0x800
+
+	/* Device Logical IN Endpoint-Specific Registers 900h-AFCh */
+	u32 in_ep_regs[MAX_EPS_CHANNELS];
+#define DWC_DEV_IN_EP_REG_OFFSET		0x900
+#define DWC_EP_REG_OFFSET			0x20
+
+	/* Device Logical OUT Endpoint-Specific Registers B00h-CFCh */
+	u32 out_ep_regs[MAX_EPS_CHANNELS];
+#define DWC_DEV_OUT_EP_REG_OFFSET		0xB00
+
+	/* Device configuration information */
+	/* Device Speed  0: Unknown, 1: LS, 2:FS, 3: HS */
+	u8 speed;
+	/*  Number # of Tx EP range: 0-15 exept ep0 */
+	u8 num_in_eps;
+	/*  Number # of Rx EP range: 0-15 exept ep 0 */
+	u8 num_out_eps;
+
+	/* Size of periodic FIFOs (Bytes) */
+	u16 perio_tx_fifo_size[MAX_PERIO_FIFOS];
+
+	/* Size of Tx FIFOs (Bytes) */
+	u16 tx_fifo_size[MAX_TX_FIFOS];
+
+	/* Thresholding enable flags and length varaiables */
+	u16 rx_thr_en;
+	u16 iso_tx_thr_en;
+	u16 non_iso_tx_thr_en;
+	u16 rx_thr_length;
+	u16 tx_thr_length;
+};
+
+/*
+ * These Macros represents the bit fields in the Power and Clock Gating Control
+ * Register. Read the register into the u32 member then set/clear the
+ * bits using the bit elements.
+ */
+#define DWC_PCGCCTL_PHY_SUS_RD(x)		(((x) & (0x001 << 4)) >> 4)
+#define DWC_PCGCCTL_RSTP_DWN_RD(x)		(((x) & (0x001 << 3)) >> 3)
+#define DWC_PCGCCTL_PWR_CLAMP_RD(x)		(((x) & (0x001 << 2)) >> 2)
+#define DWC_PCGCCTL_GATE_HCLK_RD(x)		(((x) & (0x001 << 1)) >> 1)
+#define DWC_PCGCCTL_STOP_CLK_RD(x)		(((x) & (0x001 << 0)) >> 0)
+
+#define DWC_PCGCCTL_RSTP_DWN_RW(reg, x)		\
+	(((reg) & (~((u32)0x001 << 3))) | ((x) << 3))
+#define DWC_PCGCCTL_PWR_CLAMP_RW(reg, x)	\
+	(((reg) & (~((u32)0x001 << 2))) | ((x) << 2))
+#define DWC_PCGCCTL_GATE_HCLK_RW(reg, x)	\
+	(((reg) & (~((u32)0x001 << 1))) | ((x) << 1))
+#define DWC_PCGCCTL_STOP_CLK_SET(reg)		\
+	(((reg) | 1))
+#define DWC_PCGCCTL_STOP_CLK_CLR(reg)		\
+	(((reg) & (~((u32)0x001 << 0))))
+
+/*
+ * Host Mode Register Structures
+ */
+
+/*
+ * The Host Global Registers structure defines the size and relative field
+ * offsets for the Host Mode Global Registers.  Host Global Registers offsets
+ * 400h-7FFh.
+*/
+#define DWC_HCFG		0x00
+#define DWC_HFIR		0x04
+#define DWC_HFNUM		0x08
+#define DWC_HPTXSTS		0x10
+#define DWC_HAINT		0x14
+#define DWC_HAINTMSK		0x18
+
+/*
+ * These Macros represents the bit fields in the Host Configuration Register.
+ * Read the register into the u32 member then set/clear the bits using the bit
+ * elements. Write the u32 member to the hcfg register.
+ */
+#define DWC_HCFG_FSLSUPP_RD(x)		(((x) & (0x001 << 2)) >> 2)
+#define DWC_HCFG_FSLSP_CLK_RD(x)	(((x) & (0x003 << 0)) >> 0)
+#define DWC_HCFG_FSLSUPP_RW(reg, x)	\
+	(((reg) & (~((u32)0x001 << 2))) | ((x) << 2))
+#define DWC_HCFG_FSLSP_CLK_RW(reg, x)	\
+	(((reg) & (~((u32)0x003 << 0))) | ((x) << 0))
+
+#define DWC_HCFG_30_60_MHZ			0
+#define DWC_HCFG_48_MHZ				1
+#define DWC_HCFG_6_MHZ				2
+
+/*
+ * These Macros represents the bit fields in the Host Frame Remaing/Number
+ * Register.
+ */
+#define DWC_HFIR_FRINT_RD(x)	(((x) & (0xffff << 0)) >> 0)
+#define DWC_HFIR_FRINT_RW(reg, x)	\
+	(((reg) & (~((u32)0xffff << 0))) | ((x) << 0))
+
+/*
+ * These Macros represents the bit fields in the Host Frame Remaing/Number
+ * Register.
+ */
+#define DWC_HFNUM_FRREM_RD(x)		(((x) & (0xffff << 16)) >> 16)
+#define DWC_HFNUM_FRNUM_RD(x)		(((x) & (0xffff << 0)) >> 0)
+#define DWC_HFNUM_FRREM_RW(reg, x)	\
+	(((reg) & (~((u32)0xffff << 16))) | ((x) << 16))
+#define DWC_HFNUM_FRNUM_RW(reg, x)	\
+	(((reg) & (~((u32)0xffff << 0))) | ((x) << 0))
+#define DWC_HFNUM_MAX_FRNUM			0x3FFF
+#define DWC_HFNUM_MAX_FRNUM			0x3FFF
+
+#define DWC_HPTXSTS_PTXQTOP_ODD_RD(x)	(((x) & (0x01 << 31)) >> 31)
+#define DWC_HPTXSTS_PTXQTOP_CHNUM_RD(x)	(((x) & (0x0f << 27)) >> 27)
+#define DWC_HPTXSTS_PTXQTOP_TKN_RD(x)	(((x) & (0x03 << 25)) >> 25)
+#define DWC_HPTXSTS_PTXQTOP_TERM_RD(x)	(((x) & (0x01 << 24)) >> 24)
+#define DWC_HPTXSTS_PTXSPC_AVAIL_RD(x)	(((x) & (0xff << 16)) >> 16)
+#define DWC_HPTXSTS_PTXFSPC_AVAIL_RD(x)	(((x) & (0xffff << 00)) >> 00)
+
+/*
+ * These Macros represents the bit fields in the Host Port Control and Status
+ * Register. Read the register into the u32 member then set/clear the bits using
+ * the bit elements. Write the u32 member to the hprt0 register.
+ */
+#define DWC_HPRT0_PRT_SPD_RD(x)		(((x) & (0x3 << 17)) >> 17)
+#define DWC_HPRT0_PRT_TST_CTL_RD(x)	(((x) & (0xf << 13)) >> 13)
+#define DWC_HPRT0_PRT_PWR_RD(x)		(((x) & (0x1 << 12)) >> 12)
+#define DWC_HPRT0_PRT_LSTS_RD(x)	(((x) & (0x3 << 10)) >> 10)
+#define DWC_HPRT0_PRT_RST_RD(x)		(((x) & (0x1 << 8)) >> 8)
+#define DWC_HPRT0_PRT_SUS_RD(x)		(((x) & (0x1 << 7)) >> 7)
+#define DWC_HPRT0_PRT_RES_RD(x)		(((x) & (0x1 << 6)) >> 6)
+#define DWC_HPRT0_PRT_OVRCURR_CHG_RD(x)	(((x) & (0x1 << 5)) >> 5)
+#define DWC_HPRT0_PRT_OVRCURR_ACT_RD(x)	(((x) & (0x1 << 4)) >> 4)
+#define DWC_HPRT0_PRT_ENA_DIS_CHG_RD(x)	(((x) & (0x1 << 3)) >> 3)
+#define DWC_HPRT0_PRT_ENA_RD(x)		(((x) & (0x1 << 2)) >> 2)
+#define DWC_HPRT0_PRT_CONN_DET_RD(x)	(((x) & (0x1 << 1)) >> 1)
+#define DWC_HPRT0_PRT_STS_RD(x)		(((x) & (0x1 << 0)) >> 0)
+
+#define DWC_HPRT0_PRT_SPD_RW(reg, x)		\
+	(((reg) & (~((u32)0x3 << 17))) | ((x) << 17))
+#define DWC_HPRT0_PRT_TST_CTL_RW(reg, x)	\
+	(((reg) & (~((u32)0xf << 13))) | ((x) << 13))
+#define DWC_HPRT0_PRT_PWR_RW(reg, x)		\
+	(((reg) & (~((u32)0x1 << 12))) | ((x) << 12))
+#define DWC_HPRT0_PRT_LSTS_RW(reg, x)		\
+	(((reg) & (~((u32)0x3 << 10))) | ((x) << 10))
+#define DWC_HPRT0_PRT_RST_RW(reg, x)		\
+	(((reg) & (~((u32)0x1 << 8))) | ((x) << 8))
+#define DWC_HPRT0_PRT_SUS_RW(reg, x)		\
+	(((reg) & (~((u32)0x1 << 7))) | ((x) << 7))
+#define DWC_HPRT0_PRT_RES_RW(reg, x)		\
+	(((reg) & (~((u32)0x1 << 6))) | ((x) << 6))
+#define DWC_HPRT0_PRT_OVRCURR_CHG_RW(reg, x)	\
+	(((reg) & (~((u32)0x1 << 5))) | ((x) << 5))
+#define DWC_HPRT0_PRT_OVRCURR_ACT_RW(reg, x)	\
+	(((reg) & (~((u32)0x1 << 4))) | ((x) << 4))
+#define DWC_HPRT0_PRT_ENA_DIS_CHG_RW(reg, x)	\
+	(((reg) & (~((u32)0x1 << 3))) | ((x) << 3))
+#define DWC_HPRT0_PRT_ENA_RW(reg, x)		\
+	(((reg) & (~((u32)0x1 << 2))) | ((x) << 2))
+#define DWC_HPRT0_PRT_CONN_DET_RW(reg, x)	\
+	(((reg) & (~((u32)0x1 << 1))) | ((x) << 1))
+#define DWC_HPRT0_PRT_STS_RW(reg, x)		\
+	(((reg) & (~((u32)0x1 << 0))) | ((x) << 0))
+
+#define DWC_HPRT0_PRTSPD_HIGH_SPEED		0
+#define DWC_HPRT0_PRTSPD_FULL_SPEED		1
+#define DWC_HPRT0_PRTSPD_LOW_SPEED		2
+
+/*
+ * These Macros represents the bit fields in the Host All Interrupt Register.
+ */
+#define DWC_HAINT_CH15_RD(x)	(((x) & (0x1 << 15)) >> 15)
+#define DWC_HAINT_CH14_RD(x)	(((x) & (0x1 << 14)) >> 14)
+#define DWC_HAINT_CH13_RD(x)	(((x) & (0x1 << 13)) >> 13)
+#define DWC_HAINT_CH12_RD(x)	(((x) & (0x1 << 12)) >> 12)
+#define DWC_HAINT_CH11_RD(x)	(((x) & (0x1 << 11)) >> 11)
+#define DWC_HAINT_CH10_RD(x)	(((x) & (0x1 << 10)) >> 10)
+#define DWC_HAINT_CH09_RD(x)	(((x) & (0x1 << 9)) >> 9)
+#define DWC_HAINT_CH08_RD(x)	(((x) & (0x1 << 8)) >> 8)
+#define DWC_HAINT_CH07_RD(x)	(((x) & (0x1 << 7)) >> 7)
+#define DWC_HAINT_CH06_RD(x)	(((x) & (0x1 << 6)) >> 6)
+#define DWC_HAINT_CH05_RD(x)	(((x) & (0x1 << 5)) >> 5)
+#define DWC_HAINT_CH04_RD(x)	(((x) & (0x1 << 4)) >> 4)
+#define DWC_HAINT_CH03_RD(x)	(((x) & (0x1 << 3)) >> 3)
+#define DWC_HAINT_CH02_RD(x)	(((x) & (0x1 << 2)) >> 2)
+#define DWC_HAINT_CH01_RD(x)	(((x) & (0x1 << 1)) >> 1)
+#define DWC_HAINT_CH00_RD(x)	(((x) & (0x1 << 0)) >> 0)
+
+#define DWC_HAINT_RD(x)	(((x) & (0xffff << 0)) >> 0)
+
+/*
+ * These Macros represents the bit fields in the Host All Interrupt Register.
+ */
+#define DWC_HAINTMSK_CH15_RD(x)	(((x) & (0x1 << 15)) >> 15)
+#define DWC_HAINTMSK_CH14_RD(x)	(((x) & (0x1 << 14)) >> 14)
+#define DWC_HAINTMSK_CH13_RD(x)	(((x) & (0x1 << 13)) >> 13)
+#define DWC_HAINTMSK_CH12_RD(x)	(((x) & (0x1 << 12)) >> 12)
+#define DWC_HAINTMSK_CH11_RD(x)	(((x) & (0x1 << 11)) >> 11)
+#define DWC_HAINTMSK_CH10_RD(x)	(((x) & (0x1 << 10)) >> 10)
+#define DWC_HAINTMSK_CH09_RD(x)	(((x) & (0x1 << 9)) >> 9)
+#define DWC_HAINTMSK_CH08_RD(x)	(((x) & (0x1 << 8)) >> 8)
+#define DWC_HAINTMSK_CH07_RD(x)	(((x) & (0x1 << 7)) >> 7)
+#define DWC_HAINTMSK_CH06_RD(x)	(((x) & (0x1 << 6)) >> 6)
+#define DWC_HAINTMSK_CH05_RD(x)	(((x) & (0x1 << 5)) >> 5)
+#define DWC_HAINTMSK_CH04_RD(x)	(((x) & (0x1 << 4)) >> 4)
+#define DWC_HAINTMSK_CH03_RD(x)	(((x) & (0x1 << 3)) >> 3)
+#define DWC_HAINTMSK_CH02_RD(x)	(((x) & (0x1 << 2)) >> 2)
+#define DWC_HAINTMSK_CH01_RD(x)	(((x) & (0x1 << 1)) >> 1)
+#define DWC_HAINTMSK_CH00_RD(x)	(((x) & (0x1 << 0)) >> 0)
+#define DWC_HAINTMSK_RD(x)	((x) & 0xffff)
+
+#define DWC_HAINTMSK_CH15_RW(reg, x)	\
+	(((reg) & (~((u32)0x1 << 15))) | ((x) << 15))
+#define DWC_HAINTMSK_CH14_RW(reg, x)	\
+	(((reg) & (~((u32)0x1 << 14))) | ((x) << 14))
+#define DWC_HAINTMSK_CH13_RW(reg, x)	\
+	(((reg) & (~((u32)0x1 << 13))) | ((x) << 13))
+#define DWC_HAINTMSK_CH12_RW(reg, x)	\
+	(((reg) & (~((u32)0x1 << 12))) | ((x) << 12))
+#define DWC_HAINTMSK_CH11_RW(reg, x)	\
+	(((reg) & (~((u32)0x1 << 11))) | ((x) << 11))
+#define DWC_HAINTMSK_CH10_RW(reg, x)	\
+	(((reg) & (~((u32)0x1 << 10))) | ((x) << 10))
+#define DWC_HAINTMSK_CH09_RW(reg, x)	\
+	(((reg) & (~((u32)0x1 << 9))) | ((x) << 9))
+#define DWC_HAINTMSK_CH08_RW(reg, x)	\
+	(((reg) & (~((u32)0x1 << 8))) | ((x) << 8))
+#define DWC_HAINTMSK_CH07_RW(reg, x)	\
+	(((reg) & (~((u32)0x1 << 7))) | ((x) << 7))
+#define DWC_HAINTMSK_CH06_RW(reg, x)	\
+	(((reg) & (~((u32)0x1 << 6))) | ((x) << 6))
+#define DWC_HAINTMSK_CH05_RW(reg, x)	\
+	(((reg) & (~((u32)0x1 << 5))) | ((x) << 5))
+#define DWC_HAINTMSK_CH04_RW(reg, x)	\
+	(((reg) & (~((u32)0x1 << 4))) | ((x) << 4))
+#define DWC_HAINTMSK_CH03_RW(reg, x)	\
+	(((reg) & (~((u32)0x1 << 3))) | ((x) << 3))
+#define DWC_HAINTMSK_CH02_RW(reg, x)	\
+	(((reg) & (~((u32)0x1 << 2))) | ((x) << 2))
+#define DWC_HAINTMSK_CH01_RW(reg, x)	\
+	(((reg) & (~((u32)0x1 << 1))) | ((x) << 1))
+#define DWC_HAINTMSK_CH00_RW(reg, x)	\
+	(((reg) & (~((u32)0x1 << 0))) | ((x) << 0))
+#define DWC_HAINTMSK_RW(reg, x)		\
+	(((reg) & (~((u32)0xffff))) | x)
+
+/*
+ * Host Channel Specific Registers. 500h-5FCh
+ */
+#define DWC_HCCHAR	0x00
+#define DWC_HCSPLT	0x04
+#define DWC_HCINT	0x08
+#define DWC_HCINTMSK	0x0C
+#define DWC_HCTSIZ	0x10
+#define DWC_HCDMA	0x14
+
+/*
+ * These Macros represents the bit fields in the Host Channel Characteristics
+ * Register. Read the register into the u32 member then set/clear the bits using
+ * the bit elements. Write the u32 member to the hcchar register.
+ */
+#define DWC_HCCHAR_ENA_RD(x)		(((x) & (0x001 << 31)) >> 31)
+#define DWC_HCCHAR_DIS_RD(x)		(((x) & (0x001 << 30)) >> 30)
+#define DWC_HCCHAR_ODD_FRAME_RD(x)	(((x) & (0x001 << 29)) >> 29)
+#define DWC_HCCHAR_DEV_ADDR_RD(x)	(((x) & (0x07f << 22)) >> 22)
+#define DWC_HCCHAR_MULTI_CNT_RD(x)	(((x) & (0x003 << 20)) >> 20)
+#define DWC_HCCHAR_EPTYPE_RD(x)		(((x) & (0x003 << 18)) >> 18)
+#define DWC_HCCHAR_LSP_DEV_RD(x)	(((x) & (0x001 << 17)) >> 17)
+#define DWC_HCCHAR_EPDIR_RD(x)		(((x) & (0x001 << 15)) >> 15)
+#define DWC_HCCHAR_EP_NUM_RD(x)	(((x) & (0x00f << 11)) >> 11)
+#define DWC_HCCHAR_MPS_RD(x)		(((x) & (0x7ff << 0)) >> 0)
+
+#define DWC_HCCHAR_ENA_RW(reg, x)		\
+	(((reg) & (~((u32)0x001 << 31))) | ((x) << 31))
+#define DWC_HCCHAR_DIS_RW(reg, x)		\
+	(((reg) & (~((u32)0x001 << 30))) | ((x) << 30))
+#define DWC_HCCHAR_ODD_FRAME_RW(reg, x)		\
+	(((reg) & (~((u32)0x001 << 29))) | ((x) << 29))
+#define DWC_HCCHAR_DEV_ADDR_RW(reg, x)		\
+	(((reg) & (~((u32)0x07f << 22))) | ((x) << 22))
+#define DWC_HCCHAR_MULTI_CNT_RW(reg, x)		\
+	(((reg) & (~((u32)0x003 << 20))) | ((x) << 20))
+#define DWC_HCCHAR_EPTYPE_RW(reg, x)		\
+	(((reg) & (~((u32)0x003 << 18))) | ((x) << 18))
+#define DWC_HCCHAR_LSP_DEV_RW(reg, x)		\
+	(((reg) & (~((u32)0x001 << 17))) | ((x) << 17))
+#define DWC_HCCHAR_EPDIR_RW(reg, x)		\
+	(((reg) & (~((u32)0x001 << 15))) | ((x) << 15))
+#define DWC_HCCHAR_EP_NUM_RW(reg, x)		\
+	(((reg) & (~((u32)0x00f << 11))) | ((x) << 11))
+#define DWC_HCCHAR_MPS_RW(reg, x)		\
+	(((reg) & (~((u32)0x7ff << 0))) | ((x) << 0))
+
+#define DWC_HCSPLT_ENA_RD(x)		(((x) & (0x01 << 31)) >> 31)
+#define DWC_HCSPLT_COMP_SPLT_RD(x)	(((x) & (0x01 << 16)) >> 16)
+#define DWC_HCSPLT_TRANS_POS_RD(x)	(((x) & (0x03 << 14)) >> 14)
+#define DWC_HCSPLT_HUB_ADDR_RD(x)	(((x) & (0x7f << 7)) >> 7)
+#define DWC_HCSPLT_PRT_ADDR_RD(x)	(((x) & (0x7f << 0)) >> 0)
+
+#define DWC_HCSPLT_ENA_RW(reg, x)	\
+	(((reg) & (~((u32)0x01 << 31))) | ((x) << 31))
+#define DWC_HCSPLT_COMP_SPLT_RW(reg, x)	\
+	(((reg) & (~((u32)0x01 << 16))) | ((x) << 16))
+#define DWC_HCSPLT_TRANS_POS_RW(reg, x)	\
+	(((reg) & (~((u32)0x03 << 14))) | ((x) << 14))
+#define DWC_HCSPLT_HUB_ADDR_RW(reg, x)	\
+	(((reg) & (~((u32)0x7f << 7))) | ((x) << 7))
+#define DWC_HCSPLT_PRT_ADDR_RW(reg, x)	\
+	(((reg) & (~((u32)0x7f << 0))) | ((x) << 0))
+
+#define DWC_HCSPLIT_XACTPOS_MID			0
+#define DWC_HCSPLIT_XACTPOS_END			1
+#define DWC_HCSPLIT_XACTPOS_BEGIN		2
+#define DWC_HCSPLIT_XACTPOS_ALL			3
+
+/*
+ * These Macros represents the bit fields in the Host All Interrupt
+ * Register.
+ */
+#define DWC_HCINT_DATA_TOG_ERR_RD(x)		(((x) & (0x1 << 10)) >> 10)
+#define DWC_HCINT_FRAME_OVERN_ERR_RD(x)		(((x) & (0x1 << 9)) >> 9)
+#define DWC_HCINT_BBL_ERR_RD(x)			(((x) & (0x1 << 8)) >> 8)
+#define DWC_HCINT_TRANS_ERR_RD(x)		(((x) & (0x1 << 7)) >> 7)
+#define DWC_HCINT_NYET_RESP_REC_RD(x)		(((x) & (0x1 << 6)) >> 6)
+#define DWC_HCINT_ACK_RESP_REC_RD(x)		(((x) & (0x1 << 5)) >> 5)
+#define DWC_HCINT_NAK_RESP_REC_RD(x)		(((x) & (0x1 << 4)) >> 4)
+#define DWC_HCINT_STALL_RESP_REC_RD(x)		(((x) & (0x1 << 3)) >> 3)
+#define DWC_HCINT_AHB_ERR_RD(x)			(((x) & (0x1 << 2)) >> 2)
+#define DWC_HCINT_CHAN_HALTED_RD(x)		(((x) & (0x1 << 1)) >> 1)
+#define DWC_HCINT_TXFER_CMPL_RD(x)		(((x) & (0x1 << 0)) >> 0)
+
+#define DWC_HCINT_DATA_TOG_ERR_RW(reg, x)	\
+	(((reg) & (~((u32)0x1 << 10))) | ((x) << 10))
+#define DWC_HCINT_FRAME_OVERN_ERR_RW(reg, x)	\
+	(((reg) & (~((u32)0x1 << 9))) | ((x) << 9))
+#define DWC_HCINT_BBL_ERR_RW(reg, x)		\
+	(((reg) & (~((u32)0x1 << 8))) | ((x) << 8))
+#define DWC_HCINT_TRANS_ERR_RW(reg, x)		\
+	(((reg) & (~((u32)0x1 << 7))) | ((x) << 7))
+#define DWC_HCINT_NYET_RESP_REC_RW(reg, x)	\
+	(((reg) & (~((u32)0x1 << 6))) | ((x) << 6))
+#define DWC_HCINT_ACK_RESP_REC_RW(reg, x)	\
+	(((reg) & (~((u32)0x1 << 5))) | ((x) << 5))
+#define DWC_HCINT_NAK_RESP_REC_RW(reg, x)	\
+	(((reg) & (~((u32)0x1 << 4))) | ((x) << 4))
+#define DWC_HCINT_STALL_RESP_REC_RW(reg, x)	\
+	(((reg) & (~((u32)0x1 << 3))) | ((x) << 3))
+#define DWC_HCINT_AHB_ERR_RW(reg, x)		\
+	(((reg) & (~((u32)0x1 << 2))) | ((x) << 2))
+#define DWC_HCINT_CHAN_HALTED_RW(reg, x)	\
+	(((reg) & (~((u32)0x1 << 1))) | ((x) << 1))
+#define DWC_HCINT_TXFER_CMPL_RW(reg, x)		\
+	(((reg) & (~((u32)0x1 << 0))) | ((x) << 0))
+
+/*
+ * These Macros represents the bit fields in the Host Channel Transfer Size
+ * Register. Read the register into the u32 member then set/clear the  bits
+ * using the bit elements. Write the u32 member to the hcchar register.
+ */
+#define DWC_HCTSIZ_DO_PING_PROTO_RD(x)	(((x) & (0x00001 << 31)) >> 31)
+#define DWC_HCTSIZ_PKT_PID_RD(x)	(((x) & (0x00003 << 29)) >> 29)
+#define DWC_HCTSIZ_PKT_CNT_RD(x)	(((x) & (0x003ff << 19)) >> 19)
+#define DWC_HCTSIZ_XFER_SIZE_RD(x)	(((x) & (0x7ffff << 00)) >> 00)
+
+#define DWC_HCTSIZ_DO_PING_PROTO_RW(reg, x)	\
+	(((reg) & (~((u32)0x00001 << 31))) | ((x) << 31))
+#define DWC_HCTSIZ_PKT_PID_RW(reg, x)	\
+	(((reg) & (~((u32)0x00003 << 29))) | ((x) << 29))
+#define DWC_HCTSIZ_PKT_CNT_RW(reg, x)	\
+	(((reg) & (~((u32)0x003ff << 19))) | ((x) << 19))
+#define DWC_HCTSIZ_XFER_SIZE_RW(reg, x)	\
+	(((reg) & (~((u32)0x7ffff << 00))) | ((x) << 00))
+
+#define DWC_HCTSIZ_DATA0			0
+#define DWC_HCTSIZ_DATA1			2
+#define DWC_HCTSIZ_DATA2			1
+#define DWC_HCTSIZ_MDATA			3
+#define DWC_HCTSIZ_SETUP			3
+
+/*
+ * These Macros represents the bit fields in the Host Channel Interrupt Mask
+ * Register. Read the register into the u32 member then set/clear the bits using
+ * the bit elements. Write the u32 member to the hcintmsk register.
+ */
+#define DWC_HCINTMSK_DATA_TOG_ERR_RD(x)		(((x) & (0x1 << 10)) >> 10)
+#define DWC_HCINTMSK_FRAME_OVERN_ERR_RD(x)	(((x) & (0x1 << 9)) >> 9)
+#define DWC_HCINTMSK_BBL_ERR_RD(x)		(((x) & (0x1 << 8)) >> 8)
+#define DWC_HCINTMSK_TRANS_ERR_RD(x)		(((x) & (0x1 << 7)) >> 7)
+#define DWC_HCINTMSK_NYET_RESP_REC_RD(x)	(((x) & (0x1 << 6)) >> 6)
+#define DWC_HCINTMSK_ACK_RESP_REC_RD(x)		(((x) & (0x1 << 5)) >> 5)
+#define DWC_HCINTMSK_NAK_RESP_REC_RD(x)		(((x) & (0x1 << 4)) >> 4)
+#define DWC_HCINTMSK_STALL_RESP_REC_RD(x)	(((x) & (0x1 << 3)) >> 3)
+#define DWC_HCINTMSK_AHB_ERR_RD(x)		(((x) & (0x1 << 2)) >> 2)
+#define DWC_HCINTMSK_CHAN_HALTED_RD(x)		(((x) & (0x1 << 1)) >> 1)
+#define DWC_HCINTMSK_TXFER_CMPL_RD(x)		(((x) & (0x1 << 0)) >> 0)
+
+#define DWC_HCINTMSK_DATA_TOG_ERR_RW(reg, x)	\
+	(((reg) & (~((u32)0x1 << 10))) | ((x) << 10))
+#define DWC_HCINTMSK_FRAME_OVERN_ERR_RW(reg, x)	\
+	(((reg) & (~((u32)0x1 << 9))) | ((x) << 9))
+#define DWC_HCINTMSK_BBL_ERR_RW(reg, x)		\
+	(((reg) & (~((u32)0x1 << 8))) | ((x) << 8))
+#define DWC_HCINTMSK_TRANS_ERR_RW(reg, x)	\
+	(((reg) & (~((u32)0x1 << 7))) | ((x) << 7))
+#define DWC_HCINTMSK_NYET_RESP_REC_RW(reg, x)	\
+	(((reg) & (~((u32)0x1 << 6))) | ((x) << 6))
+#define DWC_HCINTMSK_ACK_RESP_REC_RW(reg, x)	\
+	(((reg) & (~((u32)0x1 << 5))) | ((x) << 5))
+#define DWC_HCINTMSK_NAK_RESP_REC_RW(reg, x)	\
+	(((reg) & (~((u32)0x1 << 4))) | ((x) << 4))
+#define DWC_HCINTMSK_STALL_RESP_REC_RW(reg, x)	\
+	(((reg) & (~((u32)0x1 << 3))) | ((x) << 3))
+#define DWC_HCINTMSK_AHB_ERR_RW(reg, x)		\
+	(((reg) & (~((u32)0x1 << 2))) | ((x) << 2))
+#define DWC_HCINTMSK_CHAN_HALTED_RW(reg, x)	\
+	(((reg) & (~((u32)0x1 << 1))) | ((x) << 1))
+#define DWC_HCINTMSK_TXFER_CMPL_RW(reg, x)	\
+	(((reg) & (~((u32)0x1 << 0))) | ((x) << 0))
+
+/*
+ * OTG Host Interface Structure.
+ *
+ * The OTG Host Interface Structure structure contains information needed to
+ * manage the DWC_otg controller acting in host mode. It represents the
+ * programming view of the host-specific aspects of the controller.
+ */
+struct dwc_host_if {		/* CONFIG_DWC_OTG_REG_LE */
+	/* Host Global Registers starting at offset 400h. */
+	u32 host_global_regs;
+#define DWC_OTG_HOST_GLOBAL_REG_OFFSET		0x400
+
+	/* Host Port 0 Control and Status Register */
+	u32 hprt0;
+#define DWC_OTG_HOST_PORT_REGS_OFFSET		0x440
+
+	/* Host Channel Specific Registers at offsets 500h-5FCh. */
+	u32 hc_regs[MAX_EPS_CHANNELS];
+#define DWC_OTG_HOST_CHAN_REGS_OFFSET		0x500
+#define DWC_OTG_CHAN_REGS_OFFSET		0x20
+
+	/* Host configuration information */
+	/* Number of Host Channels (range: 1-16) */
+	u8 num_host_channels;
+	/* Periodic EPs supported (0: no, 1: yes) */
+	u8 perio_eps_supported;
+	/* Periodic Tx FIFO Size (Only 1 host periodic Tx FIFO) */
+	u16 perio_tx_fifo_size;
+};
+#endif
-- 
1.6.1.rc3

^ permalink raw reply related

* [PATCH V7 05/10] USB/ppc4xx: Add Synopsys DWC OTG HCD interrupt function
From: tmarri @ 2011-01-19  2:05 UTC (permalink / raw)
  To: linux-usb, linuxppc-dev; +Cc: tmarri, greg, Mark Miesfeld, Fushen Chen

From: Tirumala Marri <tmarri@apm.com>

Implements DWC OTG USB HCD interrupt service routine.

Signed-off-by: Tirumala R Marri <tmarri@apm.com>
Signed-off-by: Fushen Chen <fchen@apm.com>
Signed-off-by: Mark Miesfeld <mmiesfeld@apm.com>
---
 drivers/usb/dwc_otg/dwc_otg_hcd_intr.c | 1477 ++++++++++++++++++++++++++++++++
 1 files changed, 1477 insertions(+), 0 deletions(-)

diff --git a/drivers/usb/dwc_otg/dwc_otg_hcd_intr.c b/drivers/usb/dwc_otg/dwc_otg_hcd_intr.c
new file mode 100644
index 0000000..027635d
--- /dev/null
+++ b/drivers/usb/dwc_otg/dwc_otg_hcd_intr.c
@@ -0,0 +1,1477 @@
+/*
+ * DesignWare HS OTG controller driver
+ * Copyright (C) 2006 Synopsys, Inc.
+ * Portions Copyright (C) 2010 Applied Micro Circuits Corporation.
+ *
+ * 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 version 2 for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see http://www.gnu.org/licenses
+ * or write to the Free Software Foundation, Inc., 51 Franklin Street,
+ * Suite 500, Boston, MA 02110-1335 USA.
+ *
+ * Based on Synopsys driver version 2.60a
+ * Modified by Mark Miesfeld <mmiesfeld@apm.com>
+ * Modified by Stefan Roese <sr@denx.de>, DENX Software Engineering
+ * Modified by Chuck Meade <chuck@theptrgroup.com>
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING BUT NOT LIMITED TO THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL SYNOPSYS, INC. BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+#include "dwc_otg_hcd.h"
+
+/* This file contains the implementation of the HCD Interrupt handlers.	*/
+static const int erratum_usb09_patched;
+static const int deferral_on = 1;
+static const int nak_deferral_delay = 8;
+static const int nyet_deferral_delay = 1;
+
+/**
+ * Handles the start-of-frame interrupt in host mode. Non-periodic
+ * transactions may be queued to the DWC_otg controller for the current
+ * (micro)frame. Periodic transactions may be queued to the controller for the
+ * next (micro)frame.
+ */
+static int dwc_otg_hcd_handle_sof_intr(struct dwc_hcd *hcd)
+{
+	u32 hfnum = 0;
+	struct list_head *qh_entry;
+	struct dwc_qh *qh;
+	enum dwc_transaction_type tr_type;
+	u32 gintsts = 0;
+
+	hfnum =
+	    dwc_read32((u32) hcd->core_if->host_if->host_global_regs +
+		       DWC_HFNUM);
+
+	hcd->frame_number = DWC_HFNUM_FRNUM_RD(hfnum);
+
+	/* Determine whether any periodic QHs should be executed. */
+	qh_entry = hcd->periodic_sched_inactive.next;
+	while (qh_entry != &hcd->periodic_sched_inactive) {
+		qh = list_entry(qh_entry, struct dwc_qh, qh_list_entry);
+		qh_entry = qh_entry->next;
+
+		/*
+		 * If needed, move QH to the ready list to be executed next
+		 * (micro)frame.
+		 */
+		if (dwc_frame_num_le(qh->sched_frame, hcd->frame_number))
+			list_move(&qh->qh_list_entry,
+				  &hcd->periodic_sched_ready);
+	}
+
+	tr_type = dwc_otg_hcd_select_transactions(hcd);
+	if (tr_type != DWC_OTG_TRANSACTION_NONE)
+		dwc_otg_hcd_queue_transactions(hcd, tr_type);
+
+	/* Clear interrupt */
+	gintsts |= DWC_INTMSK_STRT_OF_FRM;
+	dwc_write32(gintsts_reg(hcd), gintsts);
+	return 1;
+}
+
+/**
+ * Handles the Rx Status Queue Level Interrupt, which indicates that there is at
+ * least one packet in the Rx FIFO.  The packets are moved from the FIFO to
+ * memory if the DWC_otg controller is operating in Slave mode.
+ */
+static int dwc_otg_hcd_handle_rx_status_q_level_intr(struct dwc_hcd *hcd)
+{
+	u32 grxsts;
+	struct dwc_hc *hc;
+
+	grxsts = dwc_read32((u32) hcd->core_if->core_global_regs + DWC_GRXSTSP);
+	hc = hcd->hc_ptr_array[grxsts & DWC_HM_RXSTS_CHAN_NUM_RD(grxsts)];
+
+	/* Packet Status */
+	switch (DWC_HM_RXSTS_PKT_STS_RD(grxsts)) {
+	case DWC_GRXSTS_PKTSTS_IN:
+		/* Read the data into the host buffer. */
+		if (DWC_HM_RXSTS_BYTE_CNT_RD(grxsts) > 0) {
+			dwc_otg_read_packet(hcd->core_if, hc->xfer_buff,
+					    DWC_HM_RXSTS_BYTE_CNT_RD(grxsts));
+			/* Update the HC fields for the next packet received. */
+			hc->xfer_count += DWC_HM_RXSTS_BYTE_CNT_RD(grxsts);
+			hc->xfer_buff += DWC_HM_RXSTS_BYTE_CNT_RD(grxsts);
+		}
+	case DWC_GRXSTS_PKTSTS_IN_XFER_COMP:
+	case DWC_GRXSTS_PKTSTS_DATA_TOGGLE_ERR:
+	case DWC_GRXSTS_PKTSTS_CH_HALTED:
+		/* Handled in interrupt, just ignore data */
+		break;
+	default:
+		pr_err("RX_STS_Q Interrupt: Unknown status %d\n",
+		       DWC_HM_RXSTS_PKT_STS_RD(grxsts));
+		break;
+	}
+	return 1;
+}
+
+/**
+ * This interrupt occurs when the non-periodic Tx FIFO is half-empty. More
+ * data packets may be written to the FIFO for OUT transfers. More requests
+ * may be written to the non-periodic request queue for IN transfers. This
+ * interrupt is enabled only in Slave mode.
+ */
+static int dwc_otg_hcd_handle_np_tx_fifo_empty_intr(struct dwc_hcd *hcd)
+{
+	dwc_otg_hcd_queue_transactions(hcd, DWC_OTG_TRANSACTION_NON_PERIODIC);
+	return 1;
+}
+
+/**
+ * This interrupt occurs when the periodic Tx FIFO is half-empty. More data
+ * packets may be written to the FIFO for OUT transfers. More requests may be
+ * written to the periodic request queue for IN transfers. This interrupt is
+ * enabled only in Slave mode.
+ */
+static int dwc_otg_hcd_handle_perio_tx_fifo_empty_intr(struct dwc_hcd *hcd)
+{
+	dwc_otg_hcd_queue_transactions(hcd, DWC_OTG_TRANSACTION_PERIODIC);
+	return 1;
+}
+
+/**
+ * When the port changes to enabled it may be necessary to adjust the phy clock
+ * speed.
+ */
+static int adjusted_phy_clock_speed(struct dwc_hcd *hcd, u32 hprt0)
+{
+	int adjusted = 0;
+	u32 usbcfg;
+	u32 global_regs = (u32) hcd->core_if->core_global_regs;
+	struct core_params *params = hcd->core_if->core_params;
+	u32 h_regs = (u32) hcd->core_if->host_if->host_global_regs;
+
+	usbcfg = dwc_read32(global_regs + DWC_GUSBCFG);
+
+	if (DWC_HPRT0_PRT_SPD_RD(hprt0) == DWC_HPRT0_PRTSPD_LOW_SPEED ||
+	    DWC_HPRT0_PRT_SPD_RD(hprt0) == DWC_HPRT0_PRTSPD_FULL_SPEED) {
+		/* Low power */
+		u32 hcfg;
+
+		if (!(usbcfg & DWC_USBCFG_PHYLPWRCLKSEL)) {
+			/* Set PHY low power clock select for FS/LS devices */
+			usbcfg |= DWC_USBCFG_PHYLPWRCLKSEL;
+			dwc_write32(global_regs + DWC_GUSBCFG, usbcfg);
+			adjusted = 1;
+		}
+
+		hcfg = dwc_read32(h_regs + DWC_HCFG);
+		if (DWC_HPRT0_PRT_SPD_RD(hprt0) == DWC_HPRT0_PRTSPD_LOW_SPEED &&
+		    params->host_ls_low_power_phy_clk ==
+		    DWC_HOST_LS_LOW_POWER_PHY_CLK_PARAM_6MHZ) {
+			/* 6 MHZ, check for 6 MHZ clock select */
+			if (DWC_HCFG_FSLSP_CLK_RD(hcfg) != DWC_HCFG_6_MHZ) {
+				hcfg = DWC_HCFG_FSLSP_CLK_RW(hcfg,
+							     DWC_HCFG_6_MHZ);
+				dwc_write32(h_regs + DWC_HCFG, hcfg);
+				adjusted = 1;
+			}
+		} else if (DWC_HCFG_FSLSP_CLK_RD(hcfg) != DWC_HCFG_48_MHZ) {
+			/* 48 MHZ and clock select is not 48 MHZ */
+			hcfg = DWC_HCFG_FSLSP_CLK_RW(hcfg, DWC_HCFG_48_MHZ);
+			dwc_write32(h_regs + DWC_HCFG, hcfg);
+			adjusted = 1;
+		}
+	} else if (usbcfg & DWC_USBCFG_PHYLPWRCLKSEL) {
+		usbcfg &= ~((u32) DWC_USBCFG_PHYLPWRCLKSEL);
+		dwc_write32(global_regs + DWC_GUSBCFG, usbcfg);
+		adjusted = 1;
+	}
+	if (adjusted)
+		schedule_work(&hcd->usb_port_reset);
+
+	return adjusted;
+}
+
+/**
+ * Helper function to handle the port enable changed interrupt when the port
+ * becomes enabled.  Checks if we need to adjust the PHY clock speed for low
+ * power and  adjusts it if needed.
+ */
+static void port_enabled(struct dwc_hcd *hcd, u32 hprt0)
+{
+	if (hcd->core_if->core_params->host_support_fs_ls_low_power)
+		if (!adjusted_phy_clock_speed(hcd, hprt0))
+			hcd->flags.b.port_reset_change = 1;
+}
+
+/**
+ * There are multiple conditions that can cause a port interrupt. This function
+ * determines which interrupt conditions have occurred and handles them
+ * appropriately.
+ */
+static int dwc_otg_hcd_handle_port_intr(struct dwc_hcd *hcd)
+{
+	int retval = 0;
+	u32 hprt0;
+	u32 hprt0_modify;
+
+	hprt0 = dwc_read32(hcd->core_if->host_if->hprt0);
+	hprt0_modify = dwc_read32(hcd->core_if->host_if->hprt0);
+
+	/*
+	 * Clear appropriate bits in HPRT0 to clear the interrupt bit in
+	 * GINTSTS
+	 */
+	hprt0_modify = DWC_HPRT0_PRT_ENA_RW(hprt0_modify, 0);
+	hprt0_modify = DWC_HPRT0_PRT_CONN_DET_RW(hprt0_modify, 0);
+	hprt0_modify = DWC_HPRT0_PRT_ENA_DIS_CHG_RW(hprt0_modify, 0);
+	hprt0_modify = DWC_HPRT0_PRT_OVRCURR_CHG_RW(hprt0_modify, 0);
+
+	/* Port connect detected interrupt */
+	if (DWC_HPRT0_PRT_CONN_DET_RD(hprt0)) {
+		/* Set the status flags and clear interrupt */
+		hcd->flags.b.port_connect_status_change = 1;
+		hcd->flags.b.port_connect_status = 1;
+		hprt0_modify = DWC_HPRT0_PRT_CONN_DET_RW(hprt0_modify, 1);
+
+		/* B-Device has connected, Delete the connection timer. */
+		del_timer_sync(&hcd->conn_timer);
+
+		/*
+		 * The Hub driver asserts a reset when it sees port connect
+		 * status change flag
+		 */
+		retval |= 1;
+	}
+
+	/* Port enable changed interrupt */
+	if (DWC_HPRT0_PRT_ENA_DIS_CHG_RD(hprt0)) {
+		/* Set the internal flag if the port was disabled */
+		if (DWC_HPRT0_PRT_ENA_RD(hprt0))
+			port_enabled(hcd, hprt0);
+		else
+			hcd->flags.b.port_enable_change = 1;
+
+		/* Clear the interrupt */
+		hprt0_modify = DWC_HPRT0_PRT_ENA_DIS_CHG_RW(hprt0_modify, 1);
+		retval |= 1;
+	}
+
+	/* Overcurrent change interrupt */
+	if (DWC_HPRT0_PRT_OVRCURR_CHG_RD(hprt0)) {
+		hcd->flags.b.port_over_current_change = 1;
+		hprt0_modify = DWC_HPRT0_PRT_OVRCURR_CHG_RW(hprt0_modify, 1);
+		retval |= 1;
+	}
+
+	/* Clear the port interrupts */
+	dwc_write32(hcd->core_if->host_if->hprt0, hprt0_modify);
+	return retval;
+}
+
+/**
+ * Gets the actual length of a transfer after the transfer halts. halt_status
+ * holds the reason for the halt.
+ *
+ * For IN transfers where halt_status is DWC_OTG_HC_XFER_COMPLETE, _short_read
+ * is set to 1 upon return if less than the requested number of bytes were
+ * transferred. Otherwise, _short_read is set to 0 upon return. _short_read may
+ * also be NULL on entry, in which case it remains unchanged.
+ */
+static u32 get_actual_xfer_length(struct dwc_hc *hc, u32 regs,
+				  struct dwc_qtd *qtd,
+				  enum dwc_halt_status halt_status,
+				  int *_short_read)
+{
+	u32 hctsiz = 0;
+	u32 length;
+
+	if (_short_read)
+		*_short_read = 0;
+
+	hctsiz = dwc_read32(regs + DWC_HCTSIZ);
+	if (halt_status == DWC_OTG_HC_XFER_COMPLETE) {
+		if (hc->ep_is_in) {
+			length = hc->xfer_len - DWC_HCTSIZ_XFER_SIZE_RD(hctsiz);
+			if (_short_read)
+				*_short_read =
+				    (DWC_HCTSIZ_XFER_SIZE_RD(hctsiz) != 0);
+		} else if (hc->qh->do_split) {
+			length = qtd->ssplit_out_xfer_count;
+		} else {
+			length = hc->xfer_len;
+		}
+	} else {
+		/*
+		 * Must use the hctsiz.pktcnt field to determine how much data
+		 * has been transferred. This field reflects the number of
+		 * packets that have been transferred via the USB. This is
+		 * always an integral number of packets if the transfer was
+		 * halted before its normal completion. (Can't use the
+		 * hctsiz.xfersize field because that reflects the number of
+		 * bytes transferred via the AHB, not the USB).
+		 */
+		length = (hc->start_pkt_count - DWC_HCTSIZ_PKT_CNT_RD(hctsiz)) *
+		    hc->max_packet;
+	}
+	return length;
+}
+
+/**
+ * Updates the state of the URB after a Transfer Complete interrupt on the
+ * host channel. Updates the actual_length field of the URB based on the
+ * number of bytes transferred via the host channel. Sets the URB status
+ * if the data transfer is finished.
+ */
+static int update_urb_state_xfer_comp(struct dwc_hc *hc,
+				      u32 regs, struct urb *urb,
+				      struct dwc_qtd *qtd, int *status)
+{
+	int xfer_done = 0;
+	int short_read = 0;
+
+	urb->actual_length += get_actual_xfer_length(hc, regs, qtd,
+						     DWC_OTG_HC_XFER_COMPLETE,
+						     &short_read);
+
+	if (short_read || urb->actual_length == urb->transfer_buffer_length) {
+		xfer_done = 1;
+		if (short_read && (urb->transfer_flags & URB_SHORT_NOT_OK))
+			*status = -EREMOTEIO;
+		else
+			*status = 0;
+	}
+	return xfer_done;
+}
+
+/*
+ * Save the starting data toggle for the next transfer. The data toggle is
+ * saved in the QH for non-control transfers and it's saved in the QTD for
+ * control transfers.
+ */
+static void save_data_toggle(struct dwc_hc *hc, u32 regs, struct dwc_qtd *qtd)
+{
+	u32 hctsiz = 0;
+	hctsiz = dwc_read32(regs + DWC_HCTSIZ);
+
+	if (hc->ep_type != DWC_OTG_EP_TYPE_CONTROL) {
+		struct dwc_qh *qh = hc->qh;
+
+		if (DWC_HCTSIZ_PKT_PID_RD(hctsiz) == DWC_HCTSIZ_DATA0)
+			qh->data_toggle = DWC_OTG_HC_PID_DATA0;
+		else
+			qh->data_toggle = DWC_OTG_HC_PID_DATA1;
+	} else {
+		if (DWC_HCTSIZ_PKT_PID_RD(hctsiz) == DWC_HCTSIZ_DATA0)
+			qtd->data_toggle = DWC_OTG_HC_PID_DATA0;
+		else
+			qtd->data_toggle = DWC_OTG_HC_PID_DATA1;
+	}
+}
+
+/**
+ * Frees the first QTD in the QH's list if free_qtd is 1. For non-periodic
+ * QHs, removes the QH from the active non-periodic schedule. If any QTDs are
+ * still linked to the QH, the QH is added to the end of the inactive
+ * non-periodic schedule. For periodic QHs, removes the QH from the periodic
+ * schedule if no more QTDs are linked to the QH.
+ */
+static void deactivate_qh(struct dwc_hcd *hcd, struct dwc_qh *qh, int free_qtd)
+{
+	int continue_split = 0;
+	struct dwc_qtd *qtd;
+
+	qtd = list_entry(qh->qtd_list.next, struct dwc_qtd, qtd_list_entry);
+	if (qtd->complete_split)
+		continue_split = 1;
+	else if (qtd->isoc_split_pos == DWC_HCSPLIT_XACTPOS_MID ||
+		 qtd->isoc_split_pos == DWC_HCSPLIT_XACTPOS_END)
+		continue_split = 1;
+
+	if (free_qtd) {
+		dwc_otg_hcd_qtd_remove(qtd);
+		continue_split = 0;
+	}
+
+	qh->channel = NULL;
+	qh->qtd_in_process = NULL;
+	dwc_otg_hcd_qh_deactivate(hcd, qh, continue_split);
+}
+
+/**
+ * Updates the state of an Isochronous URB when the transfer is stopped for
+ * any reason. The fields of the current entry in the frame descriptor array
+ * are set based on the transfer state and the input status. Completes the
+ * Isochronous URB if all the URB frames have been completed.
+ */
+static enum dwc_halt_status update_isoc_urb_state(struct dwc_hcd *hcd,
+						  struct dwc_hc *hc, u32 regs,
+						  struct dwc_qtd *qtd,
+						  enum dwc_halt_status status)
+{
+	struct urb *urb = qtd->urb;
+	enum dwc_halt_status ret_val = status;
+	struct usb_iso_packet_descriptor *frame_desc;
+	frame_desc = &urb->iso_frame_desc[qtd->isoc_frame_index];
+
+	switch (status) {
+	case DWC_OTG_HC_XFER_COMPLETE:
+		frame_desc->status = 0;
+		frame_desc->actual_length =
+		    get_actual_xfer_length(hc, regs, qtd, status, NULL);
+		break;
+	case DWC_OTG_HC_XFER_FRAME_OVERRUN:
+		urb->error_count++;
+		if (hc->ep_is_in)
+			frame_desc->status = -ENOSR;
+		else
+			frame_desc->status = -ECOMM;
+
+		frame_desc->actual_length = 0;
+		break;
+	case DWC_OTG_HC_XFER_BABBLE_ERR:
+		/* Don't need to update actual_length in this case. */
+		urb->error_count++;
+		frame_desc->status = -EOVERFLOW;
+		break;
+	case DWC_OTG_HC_XFER_XACT_ERR:
+		urb->error_count++;
+		frame_desc->status = -EPROTO;
+		frame_desc->actual_length =
+		    get_actual_xfer_length(hc, regs, qtd, status, NULL);
+	default:
+		pr_err("%s: Unhandled halt_status (%d)\n", __func__, status);
+		BUG();
+		break;
+	}
+
+	if (++qtd->isoc_frame_index == urb->number_of_packets) {
+		/*
+		 * urb->status is not used for isoc transfers.
+		 * The individual frame_desc statuses are used instead.
+		 */
+		dwc_otg_hcd_complete_urb(hcd, urb, 0);
+		ret_val = DWC_OTG_HC_XFER_URB_COMPLETE;
+	} else {
+		ret_val = DWC_OTG_HC_XFER_COMPLETE;
+	}
+	return ret_val;
+}
+
+/**
+ * Releases a host channel for use by other transfers. Attempts to select and
+ * queue more transactions since at least one host channel is available.
+ */
+static void release_channel(struct dwc_hcd *hcd, struct dwc_hc *hc,
+			    struct dwc_qtd *qtd,
+			    enum dwc_halt_status halt_status, int *must_free)
+{
+	enum dwc_transaction_type tr_type;
+	int free_qtd;
+	int deact = 1;
+	struct dwc_qh *qh;
+	int retry_delay = 1;
+
+	switch (halt_status) {
+	case DWC_OTG_HC_XFER_NYET:
+	case DWC_OTG_HC_XFER_NAK:
+		if (halt_status == DWC_OTG_HC_XFER_NYET)
+			retry_delay = nyet_deferral_delay;
+		else
+			retry_delay = nak_deferral_delay;
+		free_qtd = 0;
+		if (deferral_on && hc->do_split) {
+			qh = hc->qh;
+			if (qh)
+				deact = dwc_otg_hcd_qh_deferr(hcd, qh,
+							      retry_delay);
+		}
+		break;
+	case DWC_OTG_HC_XFER_URB_COMPLETE:
+		free_qtd = 1;
+		break;
+	case DWC_OTG_HC_XFER_AHB_ERR:
+	case DWC_OTG_HC_XFER_STALL:
+	case DWC_OTG_HC_XFER_BABBLE_ERR:
+		free_qtd = 1;
+		break;
+	case DWC_OTG_HC_XFER_XACT_ERR:
+		if (qtd->error_count >= 3) {
+			free_qtd = 1;
+			dwc_otg_hcd_complete_urb(hcd, qtd->urb, -EPROTO);
+		} else {
+			free_qtd = 0;
+		}
+		break;
+	case DWC_OTG_HC_XFER_URB_DEQUEUE:
+		/*
+		 * The QTD has already been removed and the QH has been
+		 * deactivated. Don't want to do anything except release the
+		 * host channel and try to queue more transfers.
+		 */
+		goto cleanup;
+	case DWC_OTG_HC_XFER_NO_HALT_STATUS:
+		pr_err("%s: No halt_status, channel %d\n", __func__,
+		       hc->hc_num);
+		free_qtd = 0;
+		break;
+	default:
+		free_qtd = 0;
+		break;
+	}
+	if (free_qtd)
+		/* must_free pre-initialized to zero */
+		*must_free = 1;
+	if (deact)
+		deactivate_qh(hcd, hc->qh, free_qtd);
+
+cleanup:
+	/*
+	 * Release the host channel for use by other transfers. The cleanup
+	 * function clears the channel interrupt enables and conditions, so
+	 * there's no need to clear the Channel Halted interrupt separately.
+	 */
+	dwc_otg_hc_cleanup(hcd->core_if, hc);
+	list_add_tail(&hc->hc_list_entry, &hcd->free_hc_list);
+	hcd->available_host_channels++;
+	/* Try to queue more transfers now that there's a free channel. */
+	if (!erratum_usb09_patched) {
+		tr_type = dwc_otg_hcd_select_transactions(hcd);
+		if (tr_type != DWC_OTG_TRANSACTION_NONE)
+			dwc_otg_hcd_queue_transactions(hcd, tr_type);
+	}
+}
+
+/**
+ * Halts a host channel. If the channel cannot be halted immediately because
+ * the request queue is full, this function ensures that the FIFO empty
+ * interrupt for the appropriate queue is enabled so that the halt request can
+ * be queued when there is space in the request queue.
+ *
+ * This function may also be called in DMA mode. In that case, the channel is
+ * simply released since the core always halts the channel automatically in
+ * DMA mode.
+ */
+static void halt_channel(struct dwc_hcd *hcd, struct dwc_hc *hc,
+			 struct dwc_qtd *qtd, enum dwc_halt_status halt_status,
+			 int *must_free)
+{
+	if (hcd->core_if->dma_enable) {
+		release_channel(hcd, hc, qtd, halt_status, must_free);
+		return;
+	}
+
+	/* Slave mode processing... */
+	dwc_otg_hc_halt(hcd->core_if, hc, halt_status);
+	if (hc->halt_on_queue) {
+		u32 gintmsk = 0;
+
+		if (hc->ep_type == DWC_OTG_EP_TYPE_CONTROL ||
+		    hc->ep_type == DWC_OTG_EP_TYPE_BULK) {
+			/*
+			 * Make sure the Non-periodic Tx FIFO empty interrupt
+			 * is enabled so that the non-periodic schedule will
+			 * be processed.
+			 */
+			gintmsk |= DWC_INTMSK_NP_TXFIFO_EMPT;
+			dwc_modify32(gintmsk_reg(hcd), 0, gintmsk);
+		} else {
+			/*
+			 * Move the QH from the periodic queued schedule to
+			 * the periodic assigned schedule. This allows the
+			 * halt to be queued when the periodic schedule is
+			 * processed.
+			 */
+			list_move(&hc->qh->qh_list_entry,
+				  &hcd->periodic_sched_assigned);
+
+			/*
+			 * Make sure the Periodic Tx FIFO Empty interrupt is
+			 * enabled so that the periodic schedule will be
+			 * processed.
+			 */
+			gintmsk |= DWC_INTMSK_P_TXFIFO_EMPTY;
+			dwc_modify32(gintmsk_reg(hcd), 0, gintmsk);
+		}
+	}
+}
+
+/**
+ * Performs common cleanup for non-periodic transfers after a Transfer
+ * Complete interrupt. This function should be called after any endpoint type
+ * specific handling is finished to release the host channel.
+ */
+static void complete_non_periodic_xfer(struct dwc_hcd *hcd, struct dwc_hc *hc,
+				       u32 regs, struct dwc_qtd *qtd,
+				       enum dwc_halt_status halt_status,
+				       int *must_free)
+{
+	u32 hcint;
+
+	qtd->error_count = 0;
+	hcint = dwc_read32(regs + DWC_HCINT);
+	if (DWC_HCINT_NYET_RESP_REC_RD(hcint)) {
+		u32 hcint_clear = 0;
+
+		hcint_clear = DWC_HCINT_NYET_RESP_REC_RW(hcint_clear, 1);
+		/*
+		 * Got a NYET on the last transaction of the transfer. This
+		 * means that the endpoint should be in the PING state at the
+		 * beginning of the next transfer.
+		 */
+		hc->qh->ping_state = 1;
+		dwc_write32((regs + DWC_HCINT), hcint_clear);
+	}
+
+	/*
+	 * Always halt and release the host channel to make it available for
+	 * more transfers. There may still be more phases for a control
+	 * transfer or more data packets for a bulk transfer at this point,
+	 * but the host channel is still halted. A channel will be reassigned
+	 * to the transfer when the non-periodic schedule is processed after
+	 * the channel is released. This allows transactions to be queued
+	 * properly via dwc_otg_hcd_queue_transactions, which also enables the
+	 * Tx FIFO Empty interrupt if necessary.
+	 *
+	 * IN transfers in Slave mode require an explicit disable to
+	 * halt the channel. (In DMA mode, this call simply releases
+	 * the channel.)
+	 *
+	 * The channel is automatically disabled by the core for OUT
+	 * transfers in Slave mode.
+	 */
+	if (hc->ep_is_in)
+		halt_channel(hcd, hc, qtd, halt_status, must_free);
+	else
+		release_channel(hcd, hc, qtd, halt_status, must_free);
+}
+
+/**
+ * Performs common cleanup for periodic transfers after a Transfer Complete
+ * interrupt. This function should be called after any endpoint type specific
+ * handling is finished to release the host channel.
+ */
+static void complete_periodic_xfer(struct dwc_hcd *hcd, struct dwc_hc *hc,
+				   u32 regs, struct dwc_qtd *qtd,
+				   enum dwc_halt_status halt_status,
+				   int *must_free)
+{
+	u32 hctsiz = 0;
+
+	hctsiz = dwc_read32(regs + DWC_HCTSIZ);
+	qtd->error_count = 0;
+
+	/*
+	 * For OUT transfers and 0 packet count, the Core halts the channel,
+	 * otherwise, Flush any outstanding requests from the Tx queue.
+	 */
+	if (!hc->ep_is_in || (DWC_HCTSIZ_PKT_CNT_RD(hctsiz) == 0))
+		release_channel(hcd, hc, qtd, halt_status, must_free);
+	else
+		halt_channel(hcd, hc, qtd, halt_status, must_free);
+}
+
+/**
+ * Handles a host channel Transfer Complete interrupt. This handler may be
+ * called in either DMA mode or Slave mode.
+ */
+static int handle_hc_xfercomp_intr(struct dwc_hcd *hcd, struct dwc_hc *hc,
+				   u32 regs, struct dwc_qtd *qtd,
+				   int *must_free)
+{
+	int urb_xfer_done;
+	enum dwc_halt_status halt_status = DWC_OTG_HC_XFER_COMPLETE;
+	struct urb *urb = qtd->urb;
+	int pipe_type = usb_pipetype(urb->pipe);
+	int status = -EINPROGRESS;
+	u32 hcintmsk = 0;
+
+	/* Handle xfer complete on CSPLIT. */
+	if (hc->qh->do_split)
+		qtd->complete_split = 0;
+
+	/* Update the QTD and URB states. */
+	switch (pipe_type) {
+	case PIPE_CONTROL:
+		switch (qtd->control_phase) {
+		case DWC_OTG_CONTROL_SETUP:
+			if (urb->transfer_buffer_length > 0)
+				qtd->control_phase = DWC_OTG_CONTROL_DATA;
+			else
+				qtd->control_phase = DWC_OTG_CONTROL_STATUS;
+			halt_status = DWC_OTG_HC_XFER_COMPLETE;
+			break;
+		case DWC_OTG_CONTROL_DATA:
+			urb_xfer_done = update_urb_state_xfer_comp(hc, regs,
+								   urb, qtd,
+								   &status);
+			if (urb_xfer_done)
+				qtd->control_phase = DWC_OTG_CONTROL_STATUS;
+			else
+				save_data_toggle(hc, regs, qtd);
+			halt_status = DWC_OTG_HC_XFER_COMPLETE;
+			break;
+		case DWC_OTG_CONTROL_STATUS:
+			if (status == -EINPROGRESS)
+				status = 0;
+			dwc_otg_hcd_complete_urb(hcd, urb, status);
+			halt_status = DWC_OTG_HC_XFER_URB_COMPLETE;
+			break;
+		}
+		complete_non_periodic_xfer(hcd, hc, regs, qtd,
+					   halt_status, must_free);
+		break;
+	case PIPE_BULK:
+		urb_xfer_done = update_urb_state_xfer_comp(hc, regs, urb, qtd,
+							   &status);
+		if (urb_xfer_done) {
+			dwc_otg_hcd_complete_urb(hcd, urb, status);
+			halt_status = DWC_OTG_HC_XFER_URB_COMPLETE;
+		} else {
+			halt_status = DWC_OTG_HC_XFER_COMPLETE;
+		}
+
+		save_data_toggle(hc, regs, qtd);
+		complete_non_periodic_xfer(hcd, hc, regs, qtd,
+					   halt_status, must_free);
+		break;
+	case PIPE_INTERRUPT:
+		update_urb_state_xfer_comp(hc, regs, urb, qtd, &status);
+		/*
+		 * Interrupt URB is done on the first transfer complete
+		 * interrupt.
+		 */
+		dwc_otg_hcd_complete_urb(hcd, urb, status);
+		save_data_toggle(hc, regs, qtd);
+		complete_periodic_xfer(hcd, hc, regs, qtd,
+				       DWC_OTG_HC_XFER_URB_COMPLETE, must_free);
+		break;
+	case PIPE_ISOCHRONOUS:
+		if (qtd->isoc_split_pos == DWC_HCSPLIT_XACTPOS_ALL) {
+			halt_status = update_isoc_urb_state(hcd, hc, regs, qtd,
+						    DWC_OTG_HC_XFER_COMPLETE);
+		}
+		complete_periodic_xfer(hcd, hc, regs, qtd,
+				       halt_status, must_free);
+		break;
+	}
+
+	/* disable xfercompl */
+	hcintmsk = DWC_HCINTMSK_TXFER_CMPL_RW(hcintmsk, 1);
+	dwc_modify32(regs + DWC_HCINTMSK, hcintmsk, 0);
+
+	return 1;
+}
+
+/**
+ * Handles a host channel STALL interrupt. This handler may be called in
+ * either DMA mode or Slave mode.
+ */
+static int handle_hc_stall_intr(struct dwc_hcd *hcd, struct dwc_hc *hc,
+				u32 regs, struct dwc_qtd *qtd, int *must_free)
+{
+	struct urb *urb = qtd->urb;
+	int pipe_type = usb_pipetype(urb->pipe);
+	u32 hcintmsk = 0;
+
+	if (pipe_type == PIPE_CONTROL)
+		dwc_otg_hcd_complete_urb(hcd, qtd->urb, -EPIPE);
+
+	if (pipe_type == PIPE_BULK || pipe_type == PIPE_INTERRUPT) {
+		dwc_otg_hcd_complete_urb(hcd, qtd->urb, -EPIPE);
+		/*
+		 * USB protocol requires resetting the data toggle for bulk
+		 * and interrupt endpoints when a CLEAR_FEATURE(ENDPOINT_HALT)
+		 * setup command is issued to the endpoint. Anticipate the
+		 * CLEAR_FEATURE command since a STALL has occurred and reset
+		 * the data toggle now.
+		 */
+		hc->qh->data_toggle = 0;
+	}
+
+	halt_channel(hcd, hc, qtd, DWC_OTG_HC_XFER_STALL, must_free);
+	/* disable stall */
+	hcintmsk = DWC_HCINTMSK_STALL_RESP_REC_RW(hcintmsk, 1);
+	dwc_modify32(regs + DWC_HCINTMSK, hcintmsk, 0);
+
+	return 1;
+}
+
+/**
+ * Updates the state of the URB when a transfer has been stopped due to an
+ * abnormal condition before the transfer completes. Modifies the
+ * actual_length field of the URB to reflect the number of bytes that have
+ * actually been transferred via the host channel.
+ */
+static void update_urb_state_xfer_intr(struct dwc_hc *hc,
+				       u32 regs, struct urb *urb,
+				       struct dwc_qtd *qtd,
+				       enum dwc_halt_status sts)
+{
+	u32 xfr_len = get_actual_xfer_length(hc, regs, qtd, sts, NULL);
+	urb->actual_length += xfr_len;
+}
+
+/**
+ * Handles a host channel NAK interrupt. This handler may be called in either
+ * DMA mode or Slave mode.
+ */
+static int handle_hc_nak_intr(struct dwc_hcd *hcd, struct dwc_hc *hc,
+			      u32 regs, struct dwc_qtd *qtd, int *must_free)
+{
+	u32 hcintmsk = 0;
+
+	/*
+	 * Handle NAK for IN/OUT SSPLIT/CSPLIT transfers, bulk, control, and
+	 * interrupt.  Re-start the SSPLIT transfer.
+	 */
+	if (hc->do_split) {
+		if (hc->complete_split)
+			qtd->error_count = 0;
+
+		qtd->complete_split = 0;
+		halt_channel(hcd, hc, qtd, DWC_OTG_HC_XFER_NAK, must_free);
+		goto handle_nak_done;
+	}
+	switch (usb_pipetype(qtd->urb->pipe)) {
+	case PIPE_CONTROL:
+	case PIPE_BULK:
+		if (hcd->core_if->dma_enable && hc->ep_is_in) {
+			/*
+			 * NAK interrupts are enabled on bulk/control IN
+			 * transfers in DMA mode for the sole purpose of
+			 * resetting the error count after a transaction error
+			 * occurs. The core will continue transferring data.
+			 */
+			qtd->error_count = 0;
+			goto handle_nak_done;
+		}
+
+		/*
+		 * NAK interrupts normally occur during OUT transfers in DMA
+		 * or Slave mode. For IN transfers, more requests will be
+		 * queued as request queue space is available.
+		 */
+		qtd->error_count = 0;
+		if (!hc->qh->ping_state) {
+			update_urb_state_xfer_intr(hc, regs, qtd->urb, qtd,
+						   DWC_OTG_HC_XFER_NAK);
+
+			save_data_toggle(hc, regs, qtd);
+			if (qtd->urb->dev->speed == USB_SPEED_HIGH)
+				hc->qh->ping_state = 1;
+		}
+
+		/*
+		 * Halt the channel so the transfer can be re-started from
+		 * the appropriate point or the PING protocol will
+		 * start/continue.
+		 */
+		halt_channel(hcd, hc, qtd, DWC_OTG_HC_XFER_NAK, must_free);
+		break;
+	case PIPE_INTERRUPT:
+		qtd->error_count = 0;
+		halt_channel(hcd, hc, qtd, DWC_OTG_HC_XFER_NAK, must_free);
+		break;
+	case PIPE_ISOCHRONOUS:
+		/* Should never get called for isochronous transfers. */
+		BUG();
+		break;
+	}
+
+handle_nak_done:
+	/* disable nak */
+	hcintmsk = DWC_HCINTMSK_NAK_RESP_REC_RW(hcintmsk, 1);
+	dwc_modify32(regs + DWC_HCINTMSK, hcintmsk, 0);
+
+	return 1;
+}
+
+/**
+ * Helper function for handle_hc_ack_intr().  Sets the split values for an ACK
+ * on SSPLIT for ISOC OUT.
+ */
+static void set_isoc_out_vals(struct dwc_hc *hc, struct dwc_qtd *qtd)
+{
+	struct usb_iso_packet_descriptor *frame_desc;
+
+	switch (hc->xact_pos) {
+	case DWC_HCSPLIT_XACTPOS_ALL:
+		break;
+	case DWC_HCSPLIT_XACTPOS_END:
+		qtd->isoc_split_pos = DWC_HCSPLIT_XACTPOS_ALL;
+		qtd->isoc_split_offset = 0;
+		break;
+	case DWC_HCSPLIT_XACTPOS_BEGIN:
+	case DWC_HCSPLIT_XACTPOS_MID:
+		/*
+		 * For BEGIN or MID, calculate the length for the next
+		 * microframe to determine the correct SSPLIT token, either MID
+		 * or END.
+		 */
+		frame_desc = &qtd->urb->iso_frame_desc[qtd->isoc_frame_index];
+		qtd->isoc_split_offset += 188;
+
+		if ((frame_desc->length - qtd->isoc_split_offset) <= 188)
+			qtd->isoc_split_pos = DWC_HCSPLIT_XACTPOS_END;
+		else
+			qtd->isoc_split_pos = DWC_HCSPLIT_XACTPOS_MID;
+
+		break;
+	}
+}
+
+/**
+ * Handles a host channel ACK interrupt. This interrupt is enabled when
+ * performing the PING protocol in Slave mode, when errors occur during
+ * either Slave mode or DMA mode, and during Start Split transactions.
+ */
+static int handle_hc_ack_intr(struct dwc_hcd *hcd, struct dwc_hc *hc,
+			      u32 regs, struct dwc_qtd *qtd, int *must_free)
+{
+	u32 hcintmsk = 0;
+
+	if (hc->do_split) {
+		/* Handle ACK on SSPLIT. ACK should not occur in CSPLIT. */
+		if (!hc->ep_is_in && hc->data_pid_start != DWC_OTG_HC_PID_SETUP)
+			qtd->ssplit_out_xfer_count = hc->xfer_len;
+
+		/* Don't need complete for isochronous out transfers. */
+		if (!(hc->ep_type == DWC_OTG_EP_TYPE_ISOC && !hc->ep_is_in))
+			qtd->complete_split = 1;
+
+		if (hc->ep_type == DWC_OTG_EP_TYPE_ISOC && !hc->ep_is_in)
+			set_isoc_out_vals(hc, qtd);
+		else
+			halt_channel(hcd, hc, qtd, DWC_OTG_HC_XFER_ACK,
+				     must_free);
+	} else {
+		qtd->error_count = 0;
+		if (hc->qh->ping_state) {
+			hc->qh->ping_state = 0;
+
+			/*
+			 * Halt the channel so the transfer can be re-started
+			 * from the appropriate point. This only happens in
+			 * Slave mode. In DMA mode, the ping_state is cleared
+			 * when the transfer is started because the core
+			 * automatically executes the PING, then the transfer.
+			 */
+			halt_channel(hcd, hc, qtd, DWC_OTG_HC_XFER_ACK,
+				     must_free);
+		}
+	}
+
+	/*
+	 * If the ACK occurred when _not_ in the PING state, let the channel
+	 * continue transferring data after clearing the error count.
+	 */
+	/* disable ack */
+	hcintmsk = DWC_HCINTMSK_ACK_RESP_REC_RW(hcintmsk, 1);
+	dwc_modify32(regs + DWC_HCINTMSK, hcintmsk, 0);
+
+	return 1;
+}
+
+/**
+ * Handles a host channel NYET interrupt. This interrupt should only occur on
+ * Bulk and Control OUT endpoints and for complete split transactions. If a
+ * NYET occurs at the same time as a Transfer Complete interrupt, it is
+ * handled in the xfercomp interrupt handler, not here. This handler may be
+ * called in either DMA mode or Slave mode.
+ */
+static int handle_hc_nyet_intr(struct dwc_hcd *hcd, struct dwc_hc *hc,
+			       u32 regs, struct dwc_qtd *qtd, int *must_free)
+{
+	u32 hcintmsk = 0;
+	u32 hcint_clear = 0;
+
+	/*
+	 * NYET on CSPLIT
+	 * re-do the CSPLIT immediately on non-periodic
+	 */
+	if (hc->do_split && hc->complete_split) {
+		if (hc->ep_type == DWC_OTG_EP_TYPE_INTR ||
+		    hc->ep_type == DWC_OTG_EP_TYPE_ISOC) {
+			int frnum =
+			    dwc_otg_hcd_get_frame_number(dwc_otg_hcd_to_hcd
+							 (hcd));
+			if (dwc_full_frame_num(frnum) !=
+			    dwc_full_frame_num(hc->qh->sched_frame)) {
+				qtd->complete_split = 0;
+				halt_channel(hcd, hc, qtd,
+					     DWC_OTG_HC_XFER_XACT_ERR,
+					     must_free);
+				goto handle_nyet_done;
+			}
+		}
+		halt_channel(hcd, hc, qtd, DWC_OTG_HC_XFER_NYET, must_free);
+		goto handle_nyet_done;
+	}
+	hc->qh->ping_state = 1;
+	qtd->error_count = 0;
+	update_urb_state_xfer_intr(hc, regs, qtd->urb, qtd,
+				   DWC_OTG_HC_XFER_NYET);
+	save_data_toggle(hc, regs, qtd);
+	/*
+	 * Halt the channel and re-start the transfer so the PING
+	 * protocol will start.
+	 */
+	halt_channel(hcd, hc, qtd, DWC_OTG_HC_XFER_NYET, must_free);
+
+handle_nyet_done:
+	/* disable nyet */
+	hcintmsk = DWC_HCINTMSK_NYET_RESP_REC_RW(hcintmsk, 1);
+	dwc_modify32(regs + DWC_HCINTMSK, hcintmsk, 0);
+	/* clear nyet */
+	hcint_clear = DWC_HCINT_NYET_RESP_REC_RW(hcint_clear, 1);
+	dwc_write32((regs + DWC_HCINT), hcint_clear);
+	return 1;
+}
+
+/**
+ * Handles a host channel babble interrupt. This handler may be called in
+ * either DMA mode or Slave mode.
+ */
+static int handle_hc_babble_intr(struct dwc_hcd *hcd, struct dwc_hc *hc,
+				 u32 regs, struct dwc_qtd *qtd, int *must_free)
+{
+	u32 hcintmsk = 0;
+
+	if (hc->ep_type != DWC_OTG_EP_TYPE_ISOC) {
+		dwc_otg_hcd_complete_urb(hcd, qtd->urb, -EOVERFLOW);
+		halt_channel(hcd, hc, qtd, DWC_OTG_HC_XFER_BABBLE_ERR,
+			     must_free);
+	} else {
+		enum dwc_halt_status halt_status;
+		halt_status = update_isoc_urb_state(hcd, hc, regs, qtd,
+						    DWC_OTG_HC_XFER_BABBLE_ERR);
+		halt_channel(hcd, hc, qtd, halt_status, must_free);
+	}
+	/* disable bblerr */
+	hcintmsk = DWC_HCINTMSK_BBL_ERR_RW(hcintmsk, 1);
+	dwc_modify32(regs + DWC_HCINTMSK, hcintmsk, 0);
+	return 1;
+}
+
+/**
+ * Handles a host channel AHB error interrupt. This handler is only called in
+ * DMA mode.
+ */
+static int handle_hc_ahberr_intr(struct dwc_hcd *hcd, struct dwc_hc *hc,
+				 u32 regs, struct dwc_qtd *qtd)
+{
+	u32 hcchar;
+	u32 hcsplt;
+	u32 hctsiz = 0;
+	u32 hcdma;
+	struct urb *urb = qtd->urb;
+	u32 hcintmsk = 0;
+
+	hcchar = dwc_read32(regs + DWC_HCCHAR);
+	hcsplt = dwc_read32(regs + DWC_HCSPLT);
+	hctsiz = dwc_read32(regs + DWC_HCTSIZ);
+	hcdma = dwc_read32(regs + DWC_HCDMA);
+
+	pr_err("AHB ERROR, Channel %d\n", hc->hc_num);
+	pr_err("  hcchar 0x%08x, hcsplt 0x%08x\n", hcchar, hcsplt);
+	pr_err("  hctsiz 0x%08x, hcdma 0x%08x\n", hctsiz, hcdma);
+
+	pr_err("  Device address: %d\n", usb_pipedevice(urb->pipe));
+	pr_err("  Endpoint: %d, %s\n", usb_pipeendpoint(urb->pipe),
+	       (usb_pipein(urb->pipe) ? "IN" : "OUT"));
+
+	pr_err("  Endpoint type: %s\n", pipetype_str(urb->pipe));
+	pr_err("  Speed: %s\n", dev_speed_str(urb->dev->speed));
+	pr_err("  Max packet size: %d\n",
+	       usb_maxpacket(urb->dev, urb->pipe, usb_pipeout(urb->pipe)));
+	pr_err("  Data buffer length: %d\n", urb->transfer_buffer_length);
+	pr_err("  Transfer buffer: %p, Transfer DMA: %p\n",
+	       urb->transfer_buffer, (void *)(u32) urb->transfer_dma);
+	pr_err("  Setup buffer: %p, Setup DMA: %p\n",
+	       urb->setup_packet, (void *)(u32) urb->setup_dma);
+	pr_err("  Interval: %d\n", urb->interval);
+
+	dwc_otg_hcd_complete_urb(hcd, urb, -EIO);
+
+	/*
+	 * Force a channel halt. Don't call halt_channel because that won't
+	 * write to the HCCHARn register in DMA mode to force the halt.
+	 */
+	dwc_otg_hc_halt(hcd->core_if, hc, DWC_OTG_HC_XFER_AHB_ERR);
+	/* disable ahberr */
+	hcintmsk = DWC_HCINTMSK_AHB_ERR_RW(hcintmsk, 1);
+	dwc_modify32(regs + DWC_HCINTMSK, hcintmsk, 0);
+
+	return 1;
+}
+
+/**
+ * Handles a host channel transaction error interrupt. This handler may be
+ * called in either DMA mode or Slave mode.
+ */
+static int handle_hc_xacterr_intr(struct dwc_hcd *hcd, struct dwc_hc *hc,
+				  u32 regs, struct dwc_qtd *qtd, int *must_free)
+{
+	enum dwc_halt_status status = DWC_OTG_HC_XFER_XACT_ERR;
+	u32 hcintmsk = 0;
+
+	switch (usb_pipetype(qtd->urb->pipe)) {
+	case PIPE_CONTROL:
+	case PIPE_BULK:
+		qtd->error_count++;
+		if (!hc->qh->ping_state) {
+			update_urb_state_xfer_intr(hc, regs, qtd->urb, qtd,
+						   status);
+			save_data_toggle(hc, regs, qtd);
+
+			if (!hc->ep_is_in && qtd->urb->dev->speed ==
+			    USB_SPEED_HIGH)
+				hc->qh->ping_state = 1;
+		}
+		/*
+		 * Halt the channel so the transfer can be re-started from
+		 * the appropriate point or the PING protocol will start.
+		 */
+		halt_channel(hcd, hc, qtd, status, must_free);
+		break;
+	case PIPE_INTERRUPT:
+		qtd->error_count++;
+		if (hc->do_split && hc->complete_split)
+			qtd->complete_split = 0;
+
+		halt_channel(hcd, hc, qtd, status, must_free);
+		break;
+	case PIPE_ISOCHRONOUS:
+		status = update_isoc_urb_state(hcd, hc, regs, qtd, status);
+		halt_channel(hcd, hc, qtd, status, must_free);
+		break;
+	}
+	/* Disable xacterr */
+	hcintmsk = DWC_HCINTMSK_TRANS_ERR_RW(hcintmsk, 1);
+	dwc_modify32(regs + DWC_HCINTMSK, hcintmsk, 0);
+
+	return 1;
+}
+
+/**
+ * Handles a host channel frame overrun interrupt. This handler may be called
+ * in either DMA mode or Slave mode.
+ */
+static int handle_hc_frmovrun_intr(struct dwc_hcd *hcd, struct dwc_hc *hc,
+				   u32 regs, struct dwc_qtd *qtd,
+				   int *must_free)
+{
+	enum dwc_halt_status status = DWC_OTG_HC_XFER_FRAME_OVERRUN;
+	u32 hcintmsk = 0;
+
+	switch (usb_pipetype(qtd->urb->pipe)) {
+	case PIPE_CONTROL:
+	case PIPE_BULK:
+		break;
+	case PIPE_INTERRUPT:
+		halt_channel(hcd, hc, qtd, status, must_free);
+		break;
+	case PIPE_ISOCHRONOUS:
+		status = update_isoc_urb_state(hcd, hc, regs, qtd, status);
+		halt_channel(hcd, hc, qtd, status, must_free);
+		break;
+	}
+	/* Disable frmovrun */
+	hcintmsk = DWC_HCINTMSK_FRAME_OVERN_ERR_RW(hcintmsk, 1);
+	dwc_modify32(regs + DWC_HCINTMSK, hcintmsk, 0);
+
+	return 1;
+}
+
+/**
+ * Handles a host channel data toggle error interrupt. This handler may be
+ * called in either DMA mode or Slave mode.
+ */
+static int handle_hc_datatglerr_intr(struct dwc_hcd *hcd, struct dwc_hc *hc,
+				     u32 regs, struct dwc_qtd *qtd)
+{
+	u32 hcintmsk = 0;
+
+	if (hc->ep_is_in)
+		qtd->error_count = 0;
+	else
+		pr_err("Data Toggle Error on OUT transfer, channel "
+		       "%d\n", hc->hc_num);
+
+	/* disable datatglerr */
+	hcintmsk = DWC_HCINTMSK_DATA_TOG_ERR_RW(hcintmsk, 1);
+	dwc_modify32(regs + DWC_HCINTMSK, hcintmsk, 0);
+
+	return 1;
+}
+
+/**
+ * Handles a host Channel Halted interrupt in DMA mode. This handler
+ * determines the reason the channel halted and proceeds accordingly.
+ */
+static void handle_hc_chhltd_intr_dma(struct dwc_hcd *hcd, struct dwc_hc *hc,
+				      u32 regs, struct dwc_qtd *qtd,
+				      int *must_free)
+{
+	u32 hcint;
+	u32 hcintmsk = 0;
+
+	if (hc->halt_status == DWC_OTG_HC_XFER_URB_DEQUEUE ||
+	    hc->halt_status == DWC_OTG_HC_XFER_AHB_ERR) {
+		/*
+		 * Just release the channel. A dequeue can happen on a
+		 * transfer timeout. In the case of an AHB Error, the channel
+		 * was forced to halt because there's no way to gracefully
+		 * recover.
+		 */
+		release_channel(hcd, hc, qtd, hc->halt_status, must_free);
+		return;
+	}
+
+	/* Read the HCINTn register to determine the cause for the halt. */
+	hcint = dwc_read32(regs + DWC_HCINT);
+	hcintmsk = dwc_read32(regs + DWC_HCINTMSK);
+	if (DWC_HCINT_TXFER_CMPL_RD(hcint)) {
+		/*
+		 * This is here because of a possible hardware bug.  Spec
+		 * says that on SPLIT-ISOC OUT transfers in DMA mode that a HALT
+		 * interrupt w/ACK bit set should occur, but I only see the
+		 * XFERCOMP bit, even with it masked out.  This is a workaround
+		 * for that behavior.  Should fix this when hardware is fixed.
+		 */
+		if (hc->ep_type == DWC_OTG_EP_TYPE_ISOC && !hc->ep_is_in)
+			handle_hc_ack_intr(hcd, hc, regs, qtd, must_free);
+
+		handle_hc_xfercomp_intr(hcd, hc, regs, qtd, must_free);
+	} else if (DWC_HCINT_STALL_RESP_REC_RD(hcint)) {
+		handle_hc_stall_intr(hcd, hc, regs, qtd, must_free);
+	} else if (DWC_HCINT_TRANS_ERR_RD(hcint)) {
+		/*
+		 * Must handle xacterr before nak or ack. Could get a xacterr
+		 * at the same time as either of these on a BULK/CONTROL OUT
+		 * that started with a PING. The xacterr takes precedence.
+		 */
+		handle_hc_xacterr_intr(hcd, hc, regs, qtd, must_free);
+	} else if (DWC_HCINT_NYET_RESP_REC_RD(hcint)) {
+		/*
+		 * Must handle nyet before nak or ack. Could get a nyet at the
+		 * same time as either of those on a BULK/CONTROL OUT that
+		 * started with a PING. The nyet takes precedence.
+		 */
+		handle_hc_nyet_intr(hcd, hc, regs, qtd, must_free);
+	} else if (DWC_HCINT_BBL_ERR_RD(hcint)) {
+		handle_hc_babble_intr(hcd, hc, regs, qtd, must_free);
+	} else if (DWC_HCINT_FRAME_OVERN_ERR_RD(hcint)) {
+		handle_hc_frmovrun_intr(hcd, hc, regs, qtd, must_free);
+	} else if (DWC_HCINT_DATA_TOG_ERR_RD(hcint)) {
+		handle_hc_datatglerr_intr(hcd, hc, regs, qtd);
+		hc->qh->data_toggle = 0;
+		halt_channel(hcd, hc, qtd, hc->halt_status, must_free);
+	} else if (DWC_HCINT_NAK_RESP_REC_RD(hcint) &&
+		   !DWC_HCINTMSK_NAK_RESP_REC_RD(hcintmsk)) {
+		/*
+		 * If nak is not masked, it's because a non-split IN transfer
+		 * is in an error state. In that case, the nak is handled by
+		 * the nak interrupt handler, not here. Handle nak here for
+		 * BULK/CONTROL OUT transfers, which halt on a NAK to allow
+		 * rewinding the buffer pointer.
+		 */
+		handle_hc_nak_intr(hcd, hc, regs, qtd, must_free);
+	} else if (DWC_HCINT_ACK_RESP_REC_RD(hcint) &&
+		   !DWC_HCINTMSK_ACK_RESP_REC_RD(hcintmsk)) {
+		/*
+		 * If ack is not masked, it's because a non-split IN transfer
+		 * is in an error state. In that case, the ack is handled by
+		 * the ack interrupt handler, not here. Handle ack here for
+		 * split transfers. Start splits halt on ACK.
+		 */
+		handle_hc_ack_intr(hcd, hc, regs, qtd, must_free);
+	} else {
+		if (hc->ep_type == DWC_OTG_EP_TYPE_INTR ||
+		    hc->ep_type == DWC_OTG_EP_TYPE_ISOC) {
+			/*
+			 * A periodic transfer halted with no other channel
+			 * interrupts set. Assume it was halted by the core
+			 * because it could not be completed in its scheduled
+			 * (micro)frame.
+			 */
+			halt_channel(hcd, hc, qtd,
+				     DWC_OTG_HC_XFER_PERIODIC_INCOMPLETE,
+				     must_free);
+		} else {
+			pr_err("%s: Channel %d, DMA Mode -- ChHltd "
+			       "set, but reason for halting is unknown, "
+			       "hcint 0x%08x, intsts 0x%08x\n",
+			       __func__, hc->hc_num, hcint,
+			       dwc_read32(gintsts_reg(hcd)));
+		}
+	}
+}
+
+/**
+ * Handles a host channel Channel Halted interrupt.
+ *
+ * In slave mode, this handler is called only when the driver specifically
+ * requests a halt. This occurs during handling other host channel interrupts
+ * (e.g. nak, xacterr, stall, nyet, etc.).
+ *
+ * In DMA mode, this is the interrupt that occurs when the core has finished
+ * processing a transfer on a channel. Other host channel interrupts (except
+ * ahberr) are disabled in DMA mode.
+ */
+static int handle_hc_chhltd_intr(struct dwc_hcd *hcd, struct dwc_hc *hc,
+				 u32 regs, struct dwc_qtd *qtd, int *must_free)
+{
+	if (hcd->core_if->dma_enable)
+		handle_hc_chhltd_intr_dma(hcd, hc, regs, qtd, must_free);
+	else
+		release_channel(hcd, hc, qtd, hc->halt_status, must_free);
+
+	return 1;
+}
+
+/* Handles interrupt for a specific Host Channel */
+static int dwc_otg_hcd_handle_hc_n_intr(struct dwc_hcd *hcd, u32 num)
+{
+	int must_free = 0;
+	int retval = 0;
+	u32 hcint;
+	u32 hcintmsk = 0;
+	struct dwc_hc *hc;
+	u32 hc_regs;
+	struct dwc_qtd *qtd;
+
+	hc = hcd->hc_ptr_array[num];
+	hc_regs = (u32) hcd->core_if->host_if->hc_regs[num];
+	qtd = list_entry(hc->qh->qtd_list.next, struct dwc_qtd, qtd_list_entry);
+
+	hcint = dwc_read32(hc_regs + DWC_HCINT);
+	hcintmsk = dwc_read32(hc_regs + DWC_HCINTMSK);
+
+	hcint = hcint & hcintmsk;
+	if (!hcd->core_if->dma_enable && DWC_HCINT_CHAN_HALTED_RD(hcint)
+	    && hcint != 0x2)
+		hcint = DWC_HCINT_CHAN_HALTED_RW(hcint, 0);
+
+	if (DWC_HCINT_TXFER_CMPL_RD(hcint)) {
+		retval |= handle_hc_xfercomp_intr(hcd, hc, hc_regs,
+						  qtd, &must_free);
+		/*
+		 * If NYET occurred at same time as Xfer Complete, the NYET is
+		 * handled by the Xfer Complete interrupt handler. Don't want
+		 * to call the NYET interrupt handler in this case.
+		 */
+		hcint = DWC_HCINT_NYET_RESP_REC_RW(hcint, 0);
+	}
+
+	if (DWC_HCINT_CHAN_HALTED_RD(hcint))
+		retval |= handle_hc_chhltd_intr(hcd, hc, hc_regs,
+						qtd, &must_free);
+	if (DWC_HCINT_AHB_ERR_RD(hcint))
+		retval |= handle_hc_ahberr_intr(hcd, hc, hc_regs, qtd);
+	if (DWC_HCINT_STALL_RESP_REC_RD(hcint))
+		retval |= handle_hc_stall_intr(hcd, hc, hc_regs,
+					       qtd, &must_free);
+	if (DWC_HCINT_NAK_RESP_REC_RD(hcint))
+		retval |= handle_hc_nak_intr(hcd, hc, hc_regs, qtd, &must_free);
+	if (DWC_HCINT_ACK_RESP_REC_RD(hcint))
+		retval |= handle_hc_ack_intr(hcd, hc, hc_regs, qtd, &must_free);
+	if (DWC_HCINT_NYET_RESP_REC_RD(hcint))
+		retval |= handle_hc_nyet_intr(hcd, hc, hc_regs,
+					      qtd, &must_free);
+	if (DWC_HCINT_TRANS_ERR_RD(hcint))
+		retval |= handle_hc_xacterr_intr(hcd, hc, hc_regs,
+						 qtd, &must_free);
+	if (DWC_HCINT_BBL_ERR_RD(hcint))
+		retval |= handle_hc_babble_intr(hcd, hc, hc_regs,
+						qtd, &must_free);
+	if (DWC_HCINT_FRAME_OVERN_ERR_RD(hcint))
+		retval |= handle_hc_frmovrun_intr(hcd, hc, hc_regs,
+						  qtd, &must_free);
+	if (DWC_HCINT_DATA_TOG_ERR_RD(hcint))
+		retval |= handle_hc_datatglerr_intr(hcd, hc, hc_regs, qtd);
+
+	if (must_free)
+		/* Free the qtd here now that we are done using it. */
+		dwc_otg_hcd_qtd_free(qtd);
+	return retval;
+}
+
+/**
+ * This function returns the Host All Channel Interrupt register
+ */
+static inline u32 dwc_otg_read_host_all_channels_intr(struct core_if
+						      *core_if)
+{
+	return dwc_read32((u32) core_if->host_if->host_global_regs + DWC_HAINT);
+}
+
+/**
+ * This interrupt indicates that one or more host channels has a pending
+ * interrupt. There are multiple conditions that can cause each host channel
+ * interrupt. This function determines which conditions have occurred for each
+ * host channel interrupt and handles them appropriately.
+ */
+static int dwc_otg_hcd_handle_hc_intr(struct dwc_hcd *hcd)
+{
+	u32 i;
+	int retval = 0;
+	u32 haint;
+
+	/*
+	 * Clear appropriate bits in HCINTn to clear the interrupt bit in
+	 *  GINTSTS
+	 */
+	haint = dwc_otg_read_host_all_channels_intr(hcd->core_if);
+	for (i = 0; i < hcd->core_if->core_params->host_channels; i++)
+		if (DWC_HAINT_RD(haint) & (1 << i))
+			retval |= dwc_otg_hcd_handle_hc_n_intr(hcd, i);
+
+	return retval;
+}
+
+/* This function handles interrupts for the HCD.*/
+int dwc_otg_hcd_handle_intr(struct dwc_hcd *hcd)
+{
+	int ret = 0;
+	struct core_if *core_if = hcd->core_if;
+	u32 gintsts;
+
+	/* Check if HOST Mode */
+	if (dwc_otg_is_host_mode(core_if)) {
+		spin_lock(&hcd->lock);
+		gintsts = dwc_otg_read_core_intr(core_if);
+		if (!gintsts) {
+			spin_unlock(&hcd->lock);
+			return IRQ_NONE;
+		}
+
+		if (gintsts & DWC_INTMSK_STRT_OF_FRM)
+			ret |= dwc_otg_hcd_handle_sof_intr(hcd);
+		if (gintsts & DWC_INTMSK_RXFIFO_NOT_EMPT)
+			ret |= dwc_otg_hcd_handle_rx_status_q_level_intr(hcd);
+		if (gintsts & DWC_INTMSK_NP_TXFIFO_EMPT)
+			ret |= dwc_otg_hcd_handle_np_tx_fifo_empty_intr(hcd);
+		if (gintsts & DWC_INTMSK_HST_PORT)
+			ret |= dwc_otg_hcd_handle_port_intr(hcd);
+		if (gintsts & DWC_INTMSK_HST_CHAN)
+			ret |= dwc_otg_hcd_handle_hc_intr(hcd);
+		if (gintsts & DWC_INTMSK_P_TXFIFO_EMPTY)
+			ret |= dwc_otg_hcd_handle_perio_tx_fifo_empty_intr(hcd);
+
+		spin_unlock(&hcd->lock);
+	}
+	return ret;
+}
-- 
1.6.1.rc3

^ permalink raw reply related

* [PATCH V7 04/10] USB/ppc4xx: Add Synopsys DWC OTG HCD function
From: tmarri @ 2011-01-19  2:05 UTC (permalink / raw)
  To: linux-usb, linuxppc-dev; +Cc: tmarri, greg, Mark Miesfeld, Fushen Chen

From: Tirumala Marri <tmarri@apm.com>

Implements DWC OTG USB Host Controller Driver (HCD) and interface to
USB Host controller Driver framework.

Signed-off-by: Tirumala R Marri <tmarri@apm.com>
Signed-off-by: Fushen Chen <fchen@apm.com>
Signed-off-by: Mark Miesfeld <mmiesfeld@apm.com>
---
 drivers/usb/dwc_otg/dwc_otg_hcd.c | 2466 +++++++++++++++++++++++++++++++++++++
 drivers/usb/dwc_otg/dwc_otg_hcd.h |  416 +++++++
 2 files changed, 2882 insertions(+), 0 deletions(-)

diff --git a/drivers/usb/dwc_otg/dwc_otg_hcd.c b/drivers/usb/dwc_otg/dwc_otg_hcd.c
new file mode 100644
index 0000000..3a5c4aa
--- /dev/null
+++ b/drivers/usb/dwc_otg/dwc_otg_hcd.c
@@ -0,0 +1,2466 @@
+/*
+ * DesignWare HS OTG controller driver
+ * Copyright (C) 2006 Synopsys, Inc.
+ * Portions Copyright (C) 2010 Applied Micro Circuits Corporation.
+ *
+ * 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 version 2 for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see http://www.gnu.org/licenses
+ * or write to the Free Software Foundation, Inc., 51 Franklin Street,
+ * Suite 500, Boston, MA 02110-1335 USA.
+ *
+ * Based on Synopsys driver version 2.60a
+ * Modified by Mark Miesfeld <mmiesfeld@apm.com>
+ * Modified by Stefan Roese <sr@denx.de>, DENX Software Engineering
+ * Modified by Chuck Meade <chuck@theptrgroup.com>
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING BUT NOT LIMITED TO THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL SYNOPSYS, INC. BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+/*
+ * This file contains the implementation of the HCD. In Linux, the HCD
+ * implements the hc_driver API.
+ */
+
+#include <asm/unaligned.h>
+#include <linux/dma-mapping.h>
+
+#include "dwc_otg_hcd.h"
+
+static const char dwc_otg_hcd_name[] = "dwc_otg_hcd";
+
+/**
+ * Clears the transfer state for a host channel. This function is normally
+ * called after a transfer is done and the host channel is being released. It
+ * clears the channel interrupt enables and any unhandled channel interrupt
+ * conditions.
+ */
+void dwc_otg_hc_cleanup(struct core_if *core_if, struct dwc_hc *hc)
+{
+	u32 regs;
+
+	hc->xfer_started = 0;
+	regs = (u32) core_if->host_if->hc_regs[hc->hc_num];
+	dwc_write32(regs + DWC_HCINTMSK, 0);
+	dwc_write32(regs + DWC_HCINT, 0xFFFFFFFF);
+}
+
+/**
+ * This function enables the Host mode interrupts.
+ */
+static void dwc_otg_enable_host_interrupts(struct core_if *core_if)
+{
+	u32 global_regs = (u32) core_if->core_global_regs;
+	u32 intr_mask = 0;
+
+	/* Disable all interrupts. */
+	dwc_write32(global_regs + DWC_GINTMSK, 0);
+
+	/* Clear any pending interrupts. */
+	dwc_write32(global_regs + DWC_GINTSTS, 0xFFFFFFFF);
+
+	/* Enable the common interrupts */
+	dwc_otg_enable_common_interrupts(core_if);
+
+	/*
+	 * Enable host mode interrupts without disturbing common
+	 * interrupts.
+	 */
+	intr_mask |= DWC_INTMSK_STRT_OF_FRM;
+	intr_mask |= DWC_INTMSK_HST_PORT;
+	intr_mask |= DWC_INTMSK_HST_CHAN;
+	dwc_modify32(global_regs + DWC_GINTMSK, intr_mask, intr_mask);
+}
+
+/**
+ * This function initializes the DWC_otg controller registers for
+ * host mode.
+ *
+ * This function flushes the Tx and Rx FIFOs and it flushes any entries in the
+ * request queues. Host channels are reset to ensure that they are ready for
+ * performing transfers.
+ */
+static void dwc_otg_core_host_init(struct core_if *core_if)
+{
+	u32 global_regs = (u32) core_if->core_global_regs;
+	struct dwc_host_if *host_if = core_if->host_if;
+	struct core_params *params = core_if->core_params;
+	u32 hprt0 = 0;
+	u32 nptxfifosize = 0;
+	u32 ptxfifosize = 0;
+	u32 i;
+	u32 hcchar;
+	u32 hcfg;
+	u32 hc_regs;
+	int num_channels;
+	u32 gotgctl = 0;
+
+	/* Restart the Phy Clock */
+	dwc_write32(core_if->pcgcctl, 0);
+
+	/* Initialize Host Configuration Register */
+	init_fslspclksel(core_if);
+	if (core_if->core_params->speed == DWC_SPEED_PARAM_FULL) {
+		hcfg = dwc_read32((u32) host_if->host_global_regs + DWC_HCFG);
+		hcfg = DWC_HCFG_FSLSUPP_RW(hcfg, 1);
+		dwc_write32((u32) host_if->host_global_regs + DWC_HCFG, hcfg);
+	}
+
+	/* Configure data FIFO sizes */
+	if (DWC_HWCFG2_DYN_FIFO_RD(core_if->hwcfg2)
+	    && params->enable_dynamic_fifo) {
+		/* Rx FIFO */
+		dwc_write32(global_regs + DWC_GRXFSIZ,
+			    params->host_rx_fifo_size);
+
+		/* Non-periodic Tx FIFO */
+		nptxfifosize = DWC_RX_FIFO_DEPTH_WR(nptxfifosize,
+						    params->
+						    host_nperio_tx_fifo_size);
+		nptxfifosize =
+		    DWC_RX_FIFO_START_ADDR_WR(nptxfifosize,
+					      params->host_rx_fifo_size);
+		dwc_write32(global_regs + DWC_GNPTXFSIZ, nptxfifosize);
+
+		/* Periodic Tx FIFO */
+		ptxfifosize = DWC_RX_FIFO_DEPTH_WR(ptxfifosize,
+						   params->
+						   host_perio_tx_fifo_size);
+		ptxfifosize =
+		    DWC_RX_FIFO_START_ADDR_WR(ptxfifosize,
+					      (DWC_RX_FIFO_START_ADDR_RD
+					       (nptxfifosize) +
+					       DWC_RX_FIFO_DEPTH_RD
+					       (nptxfifosize)));
+		dwc_write32(global_regs + DWC_HPTXFSIZ, ptxfifosize);
+	}
+
+	/* Clear Host Set HNP Enable in the OTG Control Register */
+	gotgctl |= DWC_GCTL_HOST_HNP_ENA;
+	dwc_modify32(global_regs + DWC_GOTGCTL, gotgctl, 0);
+
+	/* Make sure the FIFOs are flushed. */
+	dwc_otg_flush_tx_fifo(core_if, DWC_GRSTCTL_TXFNUM_ALL);
+	dwc_otg_flush_rx_fifo(core_if);
+
+	/* Flush out any leftover queued requests. */
+	num_channels = core_if->core_params->host_channels;
+	for (i = 0; i < num_channels; i++) {
+		hc_regs = (u32) core_if->host_if->hc_regs[i];
+		hcchar = dwc_read32(hc_regs + DWC_HCCHAR);
+		hcchar = DWC_HCCHAR_ENA_RW(hcchar, 0);
+		hcchar = DWC_HCCHAR_DIS_RW(hcchar, 1);
+		hcchar = DWC_HCCHAR_EPDIR_RW(hcchar, 0);
+		dwc_write32(hc_regs + DWC_HCCHAR, hcchar);
+	}
+
+	/* Halt all channels to put them into a known state. */
+	for (i = 0; i < num_channels; i++) {
+		int count = 0;
+
+		hc_regs = (u32) core_if->host_if->hc_regs[i];
+		hcchar = dwc_read32(hc_regs + DWC_HCCHAR);
+		hcchar = DWC_HCCHAR_ENA_RW(hcchar, 1);
+		hcchar = DWC_HCCHAR_DIS_RW(hcchar, 1);
+		hcchar = DWC_HCCHAR_EPDIR_RW(hcchar, 0);
+		dwc_write32(hc_regs + DWC_HCCHAR, hcchar);
+
+		do {
+			hcchar = dwc_read32(hc_regs + DWC_HCCHAR);
+			if (++count > 200) {
+				pr_err("%s: Unable to clear halt on "
+				       "channel %d\n", __func__, i);
+				break;
+			}
+			udelay(100);
+		} while (DWC_HCCHAR_ENA_RD(hcchar));
+	}
+
+	/* Turn on the vbus power. */
+	pr_info("Init: Port Power? op_state=%s\n",
+		op_state_str(core_if->xceiv->state));
+
+	if (core_if->xceiv->state == OTG_STATE_A_HOST) {
+		hprt0 = dwc_otg_read_hprt0(core_if);
+		pr_info("Init: Power Port (%d)\n", DWC_HPRT0_PRT_PWR_RD(hprt0));
+		if (DWC_HPRT0_PRT_PWR_RD(hprt0) == 0) {
+			hprt0 = DWC_HPRT0_PRT_PWR_RW(hprt0, 1);
+			dwc_write32(host_if->hprt0, hprt0);
+		}
+	}
+	dwc_otg_enable_host_interrupts(core_if);
+}
+
+/**
+ * Initializes dynamic portions of the DWC_otg HCD state.
+ */
+static void hcd_reinit(struct dwc_hcd *hcd)
+{
+	struct list_head *item;
+	int num_channels;
+	u32 i;
+	struct dwc_hc *channel;
+
+	hcd->flags.d32 = 0;
+	hcd->non_periodic_qh_ptr = &hcd->non_periodic_sched_active;
+	hcd->available_host_channels = hcd->core_if->core_params->host_channels;
+
+	/*
+	 * Put all channels in the free channel list and clean up channel
+	 * states.
+	 */
+	item = hcd->free_hc_list.next;
+	while (item != &hcd->free_hc_list) {
+		list_del(item);
+		item = hcd->free_hc_list.next;
+	}
+
+	num_channels = hcd->core_if->core_params->host_channels;
+	for (i = 0; i < num_channels; i++) {
+		channel = hcd->hc_ptr_array[i];
+		list_add_tail(&channel->hc_list_entry, &hcd->free_hc_list);
+		dwc_otg_hc_cleanup(hcd->core_if, channel);
+	}
+
+	/* Initialize the DWC core for host mode operation. */
+	dwc_otg_core_host_init(hcd->core_if);
+}
+
+/* Gets the dwc_hcd from a struct usb_hcd */
+static inline struct dwc_hcd *hcd_to_dwc_otg_hcd(struct usb_hcd *hcd)
+{
+	return (struct dwc_hcd *)hcd->hcd_priv;
+}
+
+/**
+ * Initializes the DWC_otg controller and its root hub and prepares it for host
+ * mode operation. Activates the root port. Returns 0 on success and a negative
+ * error code on failure.
+*/
+static int dwc_otg_hcd_start(struct usb_hcd *hcd)
+{
+	struct dwc_hcd *dwc_hcd = hcd_to_dwc_otg_hcd(hcd);
+	struct usb_bus *bus = hcd_to_bus(hcd);
+
+	hcd->state = HC_STATE_RUNNING;
+
+	/* Inform the HUB driver to resume. */
+	if (bus->root_hub)
+		usb_hcd_resume_root_hub(hcd);
+
+	hcd_reinit(dwc_hcd);
+	return 0;
+}
+
+/**
+ * Work queue function for starting the HCD when A-Cable is connected.
+ * The dwc_otg_hcd_start() must be called in a process context.
+ */
+static void hcd_start_func(struct work_struct *work)
+{
+	struct dwc_hcd *priv = container_of(work, struct dwc_hcd, start_work);
+	struct usb_hcd *usb_hcd = (struct usb_hcd *)priv->_p;
+
+	if (usb_hcd)
+		dwc_otg_hcd_start(usb_hcd);
+}
+
+/**
+ * HCD Callback function for starting the HCD when A-Cable is
+ * connected.
+ */
+static int dwc_otg_hcd_start_cb(void *_p)
+{
+	struct dwc_hcd *dwc_hcd = hcd_to_dwc_otg_hcd(_p);
+	struct core_if *core_if = dwc_hcd->core_if;
+	u32 hprt0;
+
+	if (core_if->xceiv->state == OTG_STATE_B_HOST) {
+		/*
+		 * Reset the port.  During a HNP mode switch the reset
+		 * needs to occur within 1ms and have a duration of at
+		 * least 50ms.
+		 */
+		hprt0 = dwc_otg_read_hprt0(core_if);
+		hprt0 = DWC_HPRT0_PRT_RST_RW(hprt0, 1);
+		dwc_write32(core_if->host_if->hprt0, hprt0);
+		((struct usb_hcd *)_p)->self.is_b_host = 1;
+	} else {
+		((struct usb_hcd *)_p)->self.is_b_host = 0;
+	}
+
+	/* Need to start the HCD in a non-interrupt context. */
+	dwc_hcd->_p = _p;
+	schedule_work(&dwc_hcd->start_work);
+	return 1;
+}
+
+/**
+ * This function disables the Host Mode interrupts.
+ */
+static void dwc_otg_disable_host_interrupts(struct core_if *core_if)
+{
+	u32 global_regs = (u32) core_if->core_global_regs;
+	u32 intr_mask = 0;
+
+	/*
+	 * Disable host mode interrupts without disturbing common
+	 * interrupts.
+	 */
+	intr_mask |= DWC_INTMSK_STRT_OF_FRM;
+	intr_mask |= DWC_INTMSK_HST_PORT;
+	intr_mask |= DWC_INTMSK_HST_CHAN;
+	intr_mask |= DWC_INTMSK_P_TXFIFO_EMPTY;
+	intr_mask |= DWC_INTMSK_NP_TXFIFO_EMPT;
+	dwc_modify32(global_regs + DWC_GINTMSK, intr_mask, 0);
+}
+
+/**
+ * Halts the DWC_otg host mode operations in a clean manner. USB transfers are
+ * stopped.
+ */
+static void dwc_otg_hcd_stop(struct usb_hcd *hcd)
+{
+	struct dwc_hcd *dwc_hcd = hcd_to_dwc_otg_hcd(hcd);
+	u32 hprt0 = 0;
+
+	/* Turn off all host-specific interrupts. */
+	dwc_otg_disable_host_interrupts(dwc_hcd->core_if);
+
+	/*
+	 * The root hub should be disconnected before this function is called.
+	 * The disconnect will clear the QTD lists (via ..._hcd_urb_dequeue)
+	 * and the QH lists (via ..._hcd_endpoint_disable).
+	 */
+
+	/* Turn off the vbus power */
+	pr_info("PortPower off\n");
+	hprt0 = DWC_HPRT0_PRT_PWR_RW(hprt0, 0);
+	dwc_write32(dwc_hcd->core_if->host_if->hprt0, hprt0);
+}
+
+/**
+ * HCD Callback function for stopping the HCD.
+ */
+static int dwc_otg_hcd_stop_cb(void *_p)
+{
+	struct usb_hcd *usb_hcd = (struct usb_hcd *)_p;
+
+	dwc_otg_hcd_stop(usb_hcd);
+	return 1;
+}
+
+static void del_timers(struct dwc_hcd *hcd)
+{
+	del_timer_sync(&hcd->conn_timer);
+}
+
+/**
+ * Processes all the URBs in a single list of QHs. Completes them with
+ * -ETIMEDOUT and frees the QTD.
+ */
+static void kill_urbs_in_qh_list(struct dwc_hcd *hcd, struct list_head *qh_list)
+{
+	struct list_head *qh_item, *q;
+
+	qh_item = qh_list->next;
+	list_for_each_safe(qh_item, q, qh_list) {
+		struct dwc_qh *qh;
+		struct list_head *qtd_item;
+		struct dwc_qtd *qtd;
+
+		qh = list_entry(qh_item, struct dwc_qh, qh_list_entry);
+		qtd_item = qh->qtd_list.next;
+		qtd = list_entry(qtd_item, struct dwc_qtd, qtd_list_entry);
+		if (qtd->urb != NULL) {
+			spin_lock(&hcd->lock);
+			dwc_otg_hcd_complete_urb(hcd, qtd->urb, -ETIMEDOUT);
+			dwc_otg_hcd_qtd_remove_and_free(qtd);
+			spin_unlock(&hcd->lock);
+		}
+	}
+}
+
+/**
+ * Responds with an error status of ETIMEDOUT to all URBs in the non-periodic
+ * and periodic schedules. The QTD associated with each URB is removed from
+ * the schedule and freed. This function may be called when a disconnect is
+ * detected or when the HCD is being stopped.
+ */
+static void kill_all_urbs(struct dwc_hcd *hcd)
+{
+	kill_urbs_in_qh_list(hcd, &hcd->non_periodic_sched_deferred);
+	kill_urbs_in_qh_list(hcd, &hcd->non_periodic_sched_inactive);
+	kill_urbs_in_qh_list(hcd, &hcd->non_periodic_sched_active);
+	kill_urbs_in_qh_list(hcd, &hcd->periodic_sched_inactive);
+	kill_urbs_in_qh_list(hcd, &hcd->periodic_sched_ready);
+	kill_urbs_in_qh_list(hcd, &hcd->periodic_sched_assigned);
+	kill_urbs_in_qh_list(hcd, &hcd->periodic_sched_queued);
+}
+
+/**
+ * HCD Callback function for disconnect of the HCD.
+ */
+static int dwc_otg_hcd_disconnect_cb(void *_p)
+{
+	u32 intr;
+	struct dwc_hcd *hcd = hcd_to_dwc_otg_hcd(_p);
+	struct core_if *core_if = hcd->core_if;
+
+	/* Set status flags for the hub driver. */
+	hcd->flags.b.port_connect_status_change = 1;
+	hcd->flags.b.port_connect_status = 0;
+
+	/*
+	 * Shutdown any transfers in process by clearing the Tx FIFO Empty
+	 * interrupt mask and status bits and disabling subsequent host
+	 * channel interrupts.
+	 */
+	intr = 0;
+	intr |= DWC_INTMSK_NP_TXFIFO_EMPT;
+	intr |= DWC_INTMSK_P_TXFIFO_EMPTY;
+	intr |= DWC_INTMSK_HST_CHAN;
+	dwc_modify32(gintmsk_reg(hcd), intr, 0);
+	dwc_modify32(gintsts_reg(hcd), intr, 0);
+
+	del_timers(hcd);
+
+	/*
+	 * Turn off the vbus power only if the core has transitioned to device
+	 * mode. If still in host mode, need to keep power on to detect a
+	 * reconnection.
+	 */
+	if (dwc_otg_is_device_mode(core_if)) {
+		if (core_if->xceiv->state != OTG_STATE_A_SUSPEND) {
+			u32 hprt0 = 0;
+
+			pr_info("Disconnect: PortPower off\n");
+			hprt0 = DWC_HPRT0_PRT_PWR_RW(hprt0, 0);
+			dwc_write32(core_if->host_if->hprt0, hprt0);
+		}
+		dwc_otg_disable_host_interrupts(core_if);
+	}
+
+	/* Respond with an error status to all URBs in the schedule. */
+	kill_all_urbs(hcd);
+	if (dwc_otg_is_host_mode(core_if)) {
+		/* Clean up any host channels that were in use. */
+		int num_channels;
+		u32 i;
+		struct dwc_hc *channel;
+		u32 regs;
+		u32 hcchar;
+
+		num_channels = core_if->core_params->host_channels;
+		if (!core_if->dma_enable) {
+			/* Flush out any channel requests in slave mode. */
+			for (i = 0; i < num_channels; i++) {
+				channel = hcd->hc_ptr_array[i];
+				if (list_empty(&channel->hc_list_entry)) {
+					regs =
+					    (u32) core_if->host_if->hc_regs[i];
+					hcchar = dwc_read32(regs + DWC_HCCHAR);
+
+					if (DWC_HCCHAR_ENA_RD(hcchar)) {
+						hcchar =
+						    DWC_HCCHAR_ENA_RW(hcchar,
+								      0);
+						hcchar =
+						    DWC_HCCHAR_DIS_RW(hcchar,
+								      1);
+						hcchar =
+						    DWC_HCCHAR_EPDIR_RW(hcchar,
+									0);
+						dwc_write32(regs + DWC_HCCHAR,
+							    hcchar);
+					}
+				}
+			}
+		}
+
+		for (i = 0; i < num_channels; i++) {
+			channel = hcd->hc_ptr_array[i];
+			if (list_empty(&channel->hc_list_entry)) {
+				regs = (u32) core_if->host_if->hc_regs[i];
+				hcchar = dwc_read32(regs + DWC_HCCHAR);
+
+				if (DWC_HCCHAR_ENA_RD(hcchar)) {
+					/* Halt the channel. */
+					hcchar = DWC_HCCHAR_DIS_RW(hcchar, 1);
+					dwc_write32(regs + DWC_HCCHAR, hcchar);
+				}
+				dwc_otg_hc_cleanup(core_if, channel);
+				list_add_tail(&channel->hc_list_entry,
+					      &hcd->free_hc_list);
+			}
+		}
+	}
+
+	/*
+	 * A disconnect will end the session so the B-Device is no
+	 * longer a B-host.
+	 */
+	((struct usb_hcd *)_p)->self.is_b_host = 0;
+	return 1;
+}
+
+/**
+ * Connection timeout function.  An OTG host is required to display a
+ * message if the device does not connect within 10 seconds.
+ */
+static void dwc_otg_hcd_connect_timeout(unsigned long _ptr)
+{
+	pr_info("Connect Timeout\n");
+	pr_err("Device Not Connected/Responding\n");
+}
+
+/**
+ * Start the connection timer.  An OTG host is required to display a
+ * message if the device does not connect within 10 seconds.  The
+ * timer is deleted if a port connect interrupt occurs before the
+ * timer expires.
+ */
+static void dwc_otg_hcd_start_connect_timer(struct dwc_hcd *hcd)
+{
+	init_timer(&hcd->conn_timer);
+	hcd->conn_timer.function = dwc_otg_hcd_connect_timeout;
+	hcd->conn_timer.data = (unsigned long)0;
+	hcd->conn_timer.expires = jiffies + (HZ * 10);
+	add_timer(&hcd->conn_timer);
+}
+
+/**
+ * HCD Callback function for disconnect of the HCD.
+ */
+static int dwc_otg_hcd_session_start_cb(void *_p)
+{
+	struct dwc_hcd *hcd = hcd_to_dwc_otg_hcd(_p);
+
+	dwc_otg_hcd_start_connect_timer(hcd);
+	return 1;
+}
+
+/* HCD Callback structure for handling mode switching. */
+static struct cil_callbacks hcd_cil_callbacks = {
+	.start = dwc_otg_hcd_start_cb,
+	.stop = dwc_otg_hcd_stop_cb,
+	.disconnect = dwc_otg_hcd_disconnect_cb,
+	.session_start = dwc_otg_hcd_session_start_cb,
+	.p = 0,
+};
+
+/*
+ * Reset Workqueue implementation
+ */
+static void port_reset_wqfunc(struct work_struct *work)
+{
+	struct dwc_hcd *hcd = container_of(work, struct dwc_hcd,
+					   usb_port_reset);
+	struct core_if *core_if = hcd->core_if;
+	u32 hprt0 = 0;
+	unsigned long flags;
+
+	pr_info("%s\n", __func__);
+	spin_lock_irqsave(&hcd->lock, flags);
+	hprt0 = dwc_otg_read_hprt0(core_if);
+	hprt0 = DWC_HPRT0_PRT_RST_RW(hprt0, 1);
+	dwc_write32(core_if->host_if->hprt0, hprt0);
+	spin_unlock_irqrestore(&hcd->lock, flags);
+	msleep(60);
+	spin_lock_irqsave(&hcd->lock, flags);
+	hprt0 = DWC_HPRT0_PRT_RST_RW(hprt0, 0);
+	dwc_write32(core_if->host_if->hprt0, hprt0);
+	hcd->flags.b.port_reset_change = 1;
+	spin_unlock_irqrestore(&hcd->lock, flags);
+}
+
+/*
+ * Wakeup Workqueue implementation
+ */
+static void port_wakeup_wqfunc(struct work_struct *work)
+{
+	struct core_if *core_if = container_of(to_delayed_work(work),
+					       struct core_if, usb_port_wakeup);
+	u32 hprt0;
+
+	pr_info("%s\n", __func__);
+	/* Now wait for 70 ms. */
+	hprt0 = dwc_otg_read_hprt0(core_if);
+	msleep(70);
+	hprt0 = DWC_HPRT0_PRT_RES_RW(hprt0, 0);
+	dwc_write32(core_if->host_if->hprt0, hprt0);
+}
+
+/**
+ * Starts processing a USB transfer request specified by a USB Request Block
+ * (URB). mem_flags indicates the type of memory allocation to use while
+ * processing this URB.
+ */
+static int dwc_otg_hcd_urb_enqueue(struct usb_hcd *hcd, struct urb *urb,
+				   gfp_t _mem_flags)
+{
+	int retval;
+	unsigned long flags;
+	struct dwc_hcd *dwc_hcd = hcd_to_dwc_otg_hcd(hcd);
+	struct dwc_qtd *qtd;
+
+	if (!dwc_hcd->flags.b.port_connect_status) {
+		/* No longer connected. */
+		retval = -ENODEV;
+		goto err_enq;
+	}
+
+	qtd = dwc_otg_hcd_qtd_create(urb, _mem_flags);
+	if (!qtd) {
+		pr_err("DWC OTG HCD URB Enqueue failed creating " "QTD\n");
+		retval = -ENOMEM;
+		goto err_enq;
+	}
+
+	spin_lock_irqsave(&dwc_hcd->lock, flags);
+	retval = usb_hcd_link_urb_to_ep(hcd, urb);
+	if (unlikely(retval))
+		goto fail;
+
+	retval = dwc_otg_hcd_qtd_add(qtd, dwc_hcd);
+	if (retval < 0) {
+		pr_err("DWC OTG HCD URB Enqueue failed adding QTD. "
+		       "Error status %d\n", retval);
+		usb_hcd_unlink_urb_from_ep(hcd, urb);
+		goto fail;
+	}
+
+fail:
+	if (retval)
+		dwc_otg_hcd_qtd_free(qtd);
+
+	spin_unlock_irqrestore(&dwc_hcd->lock, flags);
+err_enq:
+
+	return retval;
+}
+
+/**
+ * Attempts to halt a host channel. This function should only be called in
+ * Slave mode or to abort a transfer in either Slave mode or DMA mode. Under
+ * normal circumstances in DMA mode, the controller halts the channel when the
+ * transfer is complete or a condition occurs that requires application
+ * intervention.
+ *
+ * In slave mode, checks for a free request queue entry, then sets the Channel
+ * Enable and Channel Disable bits of the Host Channel Characteristics
+ * register of the specified channel to intiate the halt. If there is no free
+ * request queue entry, sets only the Channel Disable bit of the HCCHARn
+ * register to flush requests for this channel. In the latter case, sets a
+ * flag to indicate that the host channel needs to be halted when a request
+ * queue slot is open.
+ *
+ * In DMA mode, always sets the Channel Enable and Channel Disable bits of the
+ * HCCHARn register. The controller ensures there is space in the request
+ * queue before submitting the halt request.
+ *
+ * Some time may elapse before the core flushes any posted requests for this
+ * host channel and halts. The Channel Halted interrupt handler completes the
+ * deactivation of the host channel.
+ */
+void dwc_otg_hc_halt(struct core_if *core_if, struct dwc_hc *hc,
+		     enum dwc_halt_status hlt_sts)
+{
+	u32 nptxsts;
+	u32 hptxsts = 0;
+	u32 hcchar;
+	u32 hc_regs;
+	u32 global_regs = (u32) core_if->core_global_regs;
+	u32 host_global_regs;
+
+	hc_regs = (u32) core_if->host_if->hc_regs[hc->hc_num];
+	host_global_regs = (u32) core_if->host_if->host_global_regs;
+
+	WARN_ON(hlt_sts == DWC_OTG_HC_XFER_NO_HALT_STATUS);
+
+	if (hlt_sts == DWC_OTG_HC_XFER_URB_DEQUEUE ||
+	    hlt_sts == DWC_OTG_HC_XFER_AHB_ERR) {
+		/*
+		 * Disable all channel interrupts except Ch Halted. The QTD
+		 * and QH state associated with this transfer has been cleared
+		 * (in the case of URB_DEQUEUE), so the channel needs to be
+		 * shut down carefully to prevent crashes.
+		 */
+		u32 hcintmsk;
+		hcintmsk = 0;
+		hcintmsk = DWC_HCINTMSK_CHAN_HALTED_RW(hcintmsk, 1);
+		dwc_write32(hc_regs + DWC_HCINTMSK, hcintmsk);
+
+		/*
+		 * Make sure no other interrupts besides halt are currently
+		 * pending. Handling another interrupt could cause a crash due
+		 * to the QTD and QH state.
+		 */
+		dwc_write32(hc_regs + DWC_HCINT, ~hcintmsk);
+
+		/*
+		 * Make sure the halt status is set to URB_DEQUEUE or AHB_ERR
+		 * even if the channel was already halted for some other reason.
+		 */
+		hc->halt_status = hlt_sts;
+
+		/*
+		 * If the channel is not enabled, the channel is either already
+		 * halted or it hasn't started yet. In DMA mode, the transfer
+		 * may halt if it finishes normally or a condition occurs that
+		 * requires driver intervention. Don't want to halt the channel
+		 * again. In either Slave or DMA mode, it's possible that the
+		 * transfer has been assigned to a channel, but not started yet
+		 * when an URB is dequeued. Don't want to halt a channel that
+		 * hasn't started yet.
+		 */
+		hcchar = dwc_read32(hc_regs + DWC_HCCHAR);
+		if (!DWC_HCCHAR_ENA_RD(hcchar))
+			return;
+	}
+
+	if (hc->halt_pending)
+		/*
+		 * A halt has already been issued for this channel. This might
+		 * happen when a transfer is aborted by a higher level in
+		 * the stack.
+		 */
+		return;
+
+	hcchar = dwc_read32(hc_regs + DWC_HCCHAR);
+	hcchar = DWC_HCCHAR_ENA_RW(hcchar, 1);
+	hcchar = DWC_HCCHAR_DIS_RW(hcchar, 1);
+	if (!core_if->dma_enable) {
+		/* Check for space in the request queue to issue the halt. */
+		if (hc->ep_type == DWC_OTG_EP_TYPE_CONTROL ||
+		    hc->ep_type == DWC_OTG_EP_TYPE_BULK) {
+			nptxsts = dwc_read32(global_regs + DWC_GNPTXSTS);
+
+			if (!DWC_GNPTXSTS_NPTXQSPCAVAIL_RD(nptxsts))
+				hcchar = DWC_HCCHAR_ENA_RW(hcchar, 0);
+		} else {
+			hptxsts =
+			    dwc_read32((u32) host_global_regs + DWC_HPTXSTS);
+
+			if (!DWC_HPTXSTS_PTXSPC_AVAIL_RD(hptxsts) ||
+			    core_if->queuing_high_bandwidth)
+				hcchar = DWC_HCCHAR_ENA_RW(hcchar, 0);
+		}
+	}
+	dwc_write32(hc_regs + DWC_HCCHAR, hcchar);
+
+	hc->halt_status = hlt_sts;
+	if (DWC_HCCHAR_ENA_RD(hcchar)) {
+		hc->halt_pending = 1;
+		hc->halt_on_queue = 0;
+	} else {
+		hc->halt_on_queue = 1;
+	}
+}
+
+/**
+ * Aborts/cancels a USB transfer request. Always returns 0 to indicate
+ * success.
+ */
+static int dwc_otg_hcd_urb_dequeue(struct usb_hcd *hcd, struct urb *urb,
+				   int status)
+{
+	unsigned long flags;
+	struct dwc_hcd *dwc_hcd;
+	struct dwc_qtd *urb_qtd;
+	struct dwc_qh *qh;
+	int retval;
+
+	urb_qtd = (struct dwc_qtd *)urb->hcpriv;
+	if (!urb_qtd)
+		return -EINVAL;
+	qh = (struct dwc_qh *)urb_qtd->qtd_qh_ptr;
+	if (!qh)
+		return -EINVAL;
+
+	dwc_hcd = hcd_to_dwc_otg_hcd(hcd);
+	spin_lock_irqsave(&dwc_hcd->lock, flags);
+
+	retval = usb_hcd_check_unlink_urb(hcd, urb, status);
+	if (retval) {
+		spin_unlock_irqrestore(&dwc_hcd->lock, flags);
+		return retval;
+	}
+
+	if (urb_qtd == qh->qtd_in_process) {
+		/* The QTD is in process (it has been assigned to a channel). */
+		if (dwc_hcd->flags.b.port_connect_status) {
+			/*
+			 * If still connected (i.e. in host mode), halt the
+			 * channel so it can be used for other transfers. If
+			 * no longer connected, the host registers can't be
+			 * written to halt the channel since the core is in
+			 * device mode.
+			 */
+			dwc_otg_hc_halt(dwc_hcd->core_if, qh->channel,
+					DWC_OTG_HC_XFER_URB_DEQUEUE);
+		}
+	}
+
+	/*
+	 * Free the QTD and clean up the associated QH. Leave the QH in the
+	 * schedule if it has any remaining QTDs.
+	 */
+	dwc_otg_hcd_qtd_remove_and_free(urb_qtd);
+	if (qh && urb_qtd == qh->qtd_in_process) {
+		dwc_otg_hcd_qh_deactivate(dwc_hcd, qh, 0);
+		qh->channel = NULL;
+		qh->qtd_in_process = NULL;
+	} else if (qh && list_empty(&qh->qtd_list)) {
+		dwc_otg_hcd_qh_remove(dwc_hcd, qh);
+	}
+
+	urb->hcpriv = NULL;
+	usb_hcd_unlink_urb_from_ep(hcd, urb);
+	spin_unlock_irqrestore(&dwc_hcd->lock, flags);
+
+	/* Higher layer software sets URB status. */
+	usb_hcd_giveback_urb(hcd, urb, status);
+
+	return 0;
+}
+
+/* Remove and free a QH */
+static inline void dwc_otg_hcd_qh_remove_and_free(struct dwc_hcd *hcd,
+						  struct dwc_qh *qh)
+{
+	dwc_otg_hcd_qh_remove(hcd, qh);
+	dwc_otg_hcd_qh_free(qh);
+}
+
+static void qh_list_free(struct dwc_hcd *hcd, struct list_head *_qh_list)
+{
+	struct list_head *item, *tmp;
+	struct dwc_qh *qh;
+
+	/* If the list hasn't been initialized yet, return. */
+	if (_qh_list->next == NULL)
+		return;
+
+	/* Ensure there are no QTDs or URBs left. */
+	kill_urbs_in_qh_list(hcd, _qh_list);
+
+	list_for_each_safe(item, tmp, _qh_list) {
+		qh = list_entry(item, struct dwc_qh, qh_list_entry);
+		dwc_otg_hcd_qh_remove_and_free(hcd, qh);
+	}
+}
+
+/**
+ * Frees resources in the DWC_otg controller related to a given endpoint. Also
+ * clears state in the HCD related to the endpoint. Any URBs for the endpoint
+ * must already be dequeued.
+ */
+static void dwc_otg_hcd_endpoint_disable(struct usb_hcd *hcd,
+					 struct usb_host_endpoint *ep)
+{
+	struct dwc_qh *qh;
+	struct dwc_hcd *dwc_hcd = hcd_to_dwc_otg_hcd(hcd);
+	unsigned long flags;
+
+	spin_lock_irqsave(&dwc_hcd->lock, flags);
+	qh = (struct dwc_qh *)ep->hcpriv;
+	if (qh) {
+		dwc_otg_hcd_qh_remove_and_free(dwc_hcd, qh);
+		ep->hcpriv = NULL;
+	}
+	spin_unlock_irqrestore(&dwc_hcd->lock, flags);
+}
+
+/**
+ * Creates Status Change bitmap for the root hub and root port. The bitmap is
+ * returned in buf. Bit 0 is the status change indicator for the root hub. Bit 1
+ * is the status change indicator for the single root port. Returns 1 if either
+ * change indicator is 1, otherwise returns 0.
+ */
+static int dwc_otg_hcd_hub_status_data(struct usb_hcd *_hcd, char *buf)
+{
+	struct dwc_hcd *hcd = hcd_to_dwc_otg_hcd(_hcd);
+
+	buf[0] = 0;
+	buf[0] |= (hcd->flags.b.port_connect_status_change
+		   || hcd->flags.b.port_reset_change
+		   || hcd->flags.b.port_enable_change
+		   || hcd->flags.b.port_suspend_change
+		   || hcd->flags.b.port_over_current_change) << 1;
+
+	return (buf[0] != 0);
+}
+
+/* Handles the hub class-specific ClearPortFeature request.*/
+static int do_clear_port_feature(struct dwc_hcd *hcd, u16 val)
+{
+	struct core_if *core_if = hcd->core_if;
+	u32 hprt0 = 0;
+	unsigned long flags;
+
+	spin_lock_irqsave(&hcd->lock, flags);
+	switch (val) {
+	case USB_PORT_FEAT_ENABLE:
+		hprt0 = dwc_otg_read_hprt0(core_if);
+		hprt0 = DWC_HPRT0_PRT_ENA_RW(hprt0, 1);
+		dwc_write32(core_if->host_if->hprt0, hprt0);
+		break;
+	case USB_PORT_FEAT_SUSPEND:
+		hprt0 = dwc_otg_read_hprt0(core_if);
+		hprt0 = DWC_HPRT0_PRT_RES_RW(hprt0, 1);
+		dwc_write32(core_if->host_if->hprt0, hprt0);
+
+		/* Clear Resume bit */
+		spin_unlock_irqrestore(&hcd->lock, flags);
+		msleep(100);
+		spin_lock_irqsave(&hcd->lock, flags);
+		hprt0 = DWC_HPRT0_PRT_RES_RW(hprt0, 0);
+		dwc_write32(core_if->host_if->hprt0, hprt0);
+		break;
+	case USB_PORT_FEAT_POWER:
+		hprt0 = dwc_otg_read_hprt0(core_if);
+		hprt0 = DWC_HPRT0_PRT_PWR_RW(hprt0, 0);
+		dwc_write32(core_if->host_if->hprt0, hprt0);
+		break;
+	case USB_PORT_FEAT_INDICATOR:
+		/* Port inidicator not supported */
+		break;
+	case USB_PORT_FEAT_C_CONNECTION:
+		/* Clears drivers internal connect status change flag */
+		hcd->flags.b.port_connect_status_change = 0;
+		break;
+	case USB_PORT_FEAT_C_RESET:
+		/* Clears driver's internal Port Reset Change flag */
+		hcd->flags.b.port_reset_change = 0;
+		break;
+	case USB_PORT_FEAT_C_ENABLE:
+		/* Clears driver's internal Port Enable/Disable Change flag  */
+		hcd->flags.b.port_enable_change = 0;
+		break;
+	case USB_PORT_FEAT_C_SUSPEND:
+		/*
+		 * Clears the driver's internal Port Suspend
+		 * Change flag, which is set when resume signaling on
+		 * the host port is complete
+		 */
+		hcd->flags.b.port_suspend_change = 0;
+		break;
+	case USB_PORT_FEAT_C_OVER_CURRENT:
+		hcd->flags.b.port_over_current_change = 0;
+		break;
+	default:
+		pr_err("DWC OTG HCD - ClearPortFeature request %xh "
+		       "unknown or unsupported\n", val);
+		spin_unlock_irqrestore(&hcd->lock, flags);
+		return -EINVAL;
+	}
+	spin_unlock_irqrestore(&hcd->lock, flags);
+	return 0;
+}
+
+/* Handles the hub class-specific SetPortFeature request.*/
+static int do_set_port_feature(struct usb_hcd *hcd, u16 val, u16 index)
+{
+	struct core_if *core_if = hcd_to_dwc_otg_hcd(hcd)->core_if;
+	u32 hprt0 = 0;
+	struct dwc_hcd *dwc_hcd = hcd_to_dwc_otg_hcd(hcd);
+	unsigned long flags;
+	u32 pcgcctl = 0;
+
+	spin_lock_irqsave(&dwc_hcd->lock, flags);
+
+	switch (val) {
+	case USB_PORT_FEAT_SUSPEND:
+		if (hcd->self.otg_port == index && hcd->self.b_hnp_enable) {
+			u32 gotgctl = 0;
+			gotgctl |= DWC_GCTL_HOST_HNP_ENA;
+			dwc_modify32((u32) core_if->core_global_regs +
+				     DWC_GOTGCTL, 0, gotgctl);
+			core_if->xceiv->state = OTG_STATE_A_SUSPEND;
+		}
+
+		hprt0 = dwc_otg_read_hprt0(core_if);
+		hprt0 = DWC_HPRT0_PRT_SUS_RW(hprt0, 1);
+		dwc_write32(core_if->host_if->hprt0, hprt0);
+
+		/* Suspend the Phy Clock */
+		pcgcctl = DWC_PCGCCTL_STOP_CLK_SET(pcgcctl);
+		dwc_write32(core_if->pcgcctl, pcgcctl);
+
+		/* For HNP the bus must be suspended for at least 200ms. */
+		if (hcd->self.b_hnp_enable) {
+			spin_unlock_irqrestore(&dwc_hcd->lock, flags);
+			msleep(200);
+			spin_lock_irqsave(&dwc_hcd->lock, flags);
+		}
+		break;
+	case USB_PORT_FEAT_POWER:
+		hprt0 = dwc_otg_read_hprt0(core_if);
+		hprt0 = DWC_HPRT0_PRT_PWR_RW(hprt0, 1);
+		dwc_write32(core_if->host_if->hprt0, hprt0);
+		break;
+	case USB_PORT_FEAT_RESET:
+		hprt0 = dwc_otg_read_hprt0(core_if);
+
+		/*
+		 * When B-Host the Port reset bit is set in the Start HCD
+		 * Callback function, so that the reset is started within 1ms
+		 * of the HNP success interrupt.
+		 */
+		if (!hcd->self.is_b_host) {
+			hprt0 = DWC_HPRT0_PRT_RST_RW(hprt0, 1);
+			dwc_write32(core_if->host_if->hprt0, hprt0);
+		}
+
+		/* Clear reset bit in 10ms (FS/LS) or 50ms (HS) */
+		spin_unlock_irqrestore(&dwc_hcd->lock, flags);
+		msleep(60);
+		spin_lock_irqsave(&dwc_hcd->lock, flags);
+		hprt0 = DWC_HPRT0_PRT_RST_RW(hprt0, 0);
+		dwc_write32(core_if->host_if->hprt0, hprt0);
+		break;
+	case USB_PORT_FEAT_INDICATOR:
+		/* Not supported */
+		break;
+	default:
+		pr_err("DWC OTG HCD - "
+		       "SetPortFeature request %xh "
+		       "unknown or unsupported\n", val);
+		spin_unlock_irqrestore(&dwc_hcd->lock, flags);
+		return -EINVAL;
+	}
+	spin_unlock_irqrestore(&dwc_hcd->lock, flags);
+	return 0;
+}
+
+/* Handles hub class-specific requests.*/
+static int dwc_otg_hcd_hub_control(struct usb_hcd *hcd, u16 req_type, u16 val,
+				   u16 index, char *buf, u16 len)
+{
+	int retval = 0;
+	struct dwc_hcd *dwc_hcd = hcd_to_dwc_otg_hcd(hcd);
+	struct core_if *core_if = hcd_to_dwc_otg_hcd(hcd)->core_if;
+	struct usb_hub_descriptor *desc;
+	u32 hprt0 = 0;
+	u32 port_status;
+	unsigned long flags;
+
+	spin_lock_irqsave(&dwc_hcd->lock, flags);
+	switch (req_type) {
+	case ClearHubFeature:
+		switch (val) {
+		case C_HUB_LOCAL_POWER:
+		case C_HUB_OVER_CURRENT:
+			/* Nothing required here */
+			break;
+		default:
+			retval = -EINVAL;
+			pr_err("DWC OTG HCD - ClearHubFeature request"
+			       " %xh unknown\n", val);
+		}
+		break;
+	case ClearPortFeature:
+		if (!index || index > 1)
+			goto error;
+		spin_unlock_irqrestore(&dwc_hcd->lock, flags);
+		retval = do_clear_port_feature(dwc_hcd, val);
+		spin_lock_irqsave(&dwc_hcd->lock, flags);
+		break;
+	case GetHubDescriptor:
+		desc = (struct usb_hub_descriptor *)buf;
+		desc->bDescLength = 9;
+		desc->bDescriptorType = 0x29;
+		desc->bNbrPorts = 1;
+		desc->wHubCharacteristics = 0x08;
+		desc->bPwrOn2PwrGood = 1;
+		desc->bHubContrCurrent = 0;
+		desc->bitmap[0] = 0;
+		desc->bitmap[1] = 0xff;
+		break;
+	case GetHubStatus:
+		memset(buf, 0, 4);
+		break;
+	case GetPortStatus:
+		if (!index || index > 1)
+			goto error;
+
+		port_status = 0;
+		if (dwc_hcd->flags.b.port_connect_status_change)
+			port_status |= (1 << USB_PORT_FEAT_C_CONNECTION);
+		if (dwc_hcd->flags.b.port_enable_change)
+			port_status |= (1 << USB_PORT_FEAT_C_ENABLE);
+		if (dwc_hcd->flags.b.port_suspend_change)
+			port_status |= (1 << USB_PORT_FEAT_C_SUSPEND);
+		if (dwc_hcd->flags.b.port_reset_change)
+			port_status |= (1 << USB_PORT_FEAT_C_RESET);
+		if (dwc_hcd->flags.b.port_over_current_change) {
+			pr_err("Device Not Supported\n");
+			port_status |= (1 << USB_PORT_FEAT_C_OVER_CURRENT);
+		}
+		if (!dwc_hcd->flags.b.port_connect_status) {
+			/*
+			 * The port is disconnected, which means the core is
+			 * either in device mode or it soon will be. Just
+			 * return 0's for the remainder of the port status
+			 * since the port register can't be read if the core
+			 * is in device mode.
+			 */
+			*((__le32 *) buf) = cpu_to_le32(port_status);
+			break;
+		}
+
+		hprt0 = dwc_read32(core_if->host_if->hprt0);
+
+		if (DWC_HPRT0_PRT_STS_RD(hprt0))
+			port_status |= USB_PORT_STAT_CONNECTION;
+		if (DWC_HPRT0_PRT_ENA_RD(hprt0))
+			port_status |= USB_PORT_STAT_ENABLE;
+		if (DWC_HPRT0_PRT_SUS_RD(hprt0))
+			port_status |= USB_PORT_STAT_SUSPEND;
+		if (DWC_HPRT0_PRT_OVRCURR_ACT_RD(hprt0))
+			port_status |= USB_PORT_STAT_OVERCURRENT;
+		if (DWC_HPRT0_PRT_RST_RD(hprt0))
+			port_status |= USB_PORT_STAT_RESET;
+		if (DWC_HPRT0_PRT_PWR_RD(hprt0))
+			port_status |= USB_PORT_STAT_POWER;
+
+		if (DWC_HPRT0_PRT_SPD_RD(hprt0) == DWC_HPRT0_PRTSPD_HIGH_SPEED)
+			port_status |= USB_PORT_STAT_HIGH_SPEED;
+		else if (DWC_HPRT0_PRT_SPD_RD(hprt0) ==
+			 DWC_HPRT0_PRTSPD_LOW_SPEED)
+			port_status |= USB_PORT_STAT_LOW_SPEED;
+
+		if (DWC_HPRT0_PRT_TST_CTL_RD(hprt0))
+			port_status |= (1 << USB_PORT_FEAT_TEST);
+
+		/* USB_PORT_FEAT_INDICATOR unsupported always 0 */
+		*((__le32 *) buf) = cpu_to_le32(port_status);
+		break;
+	case SetHubFeature:
+		/* No HUB features supported */
+		break;
+	case SetPortFeature:
+		if (val != USB_PORT_FEAT_TEST && (!index || index > 1))
+			goto error;
+
+		if (!dwc_hcd->flags.b.port_connect_status) {
+			/*
+			 * The port is disconnected, which means the core is
+			 * either in device mode or it soon will be. Just
+			 * return without doing anything since the port
+			 * register can't be written if the core is in device
+			 * mode.
+			 */
+			break;
+		}
+		spin_unlock_irqrestore(&dwc_hcd->lock, flags);
+		retval = do_set_port_feature(hcd, val, index);
+		spin_lock_irqsave(&dwc_hcd->lock, flags);
+		break;
+	default:
+error:
+		retval = -EINVAL;
+		pr_warning("DWC OTG HCD - Unknown hub control request"
+			   " type or invalid req_type: %xh index: %xh "
+			   "val: %xh\n", req_type, index, val);
+		break;
+	}
+	spin_unlock_irqrestore(&dwc_hcd->lock, flags);
+	return retval;
+}
+
+/**
+ * Handles host mode interrupts for the DWC_otg controller. Returns IRQ_NONE if
+ * there was no interrupt to handle. Returns IRQ_HANDLED if there was a valid
+ * interrupt.
+ *
+ * This function is called by the USB core when an interrupt occurs
+ */
+static irqreturn_t dwc_otg_hcd_irq(struct usb_hcd *hcd)
+{
+	struct dwc_hcd *dwc_hcd = hcd_to_dwc_otg_hcd(hcd);
+
+	return IRQ_RETVAL(dwc_otg_hcd_handle_intr(dwc_hcd));
+}
+
+static const struct hc_driver dwc_otg_hc_driver = {
+	.description = dwc_otg_hcd_name,
+	.product_desc = "DWC OTG Controller",
+	.hcd_priv_size = sizeof(struct dwc_hcd),
+	.irq = dwc_otg_hcd_irq,
+	.flags = HCD_MEMORY | HCD_USB2,
+	.start = dwc_otg_hcd_start,
+	.stop = dwc_otg_hcd_stop,
+	.urb_enqueue = dwc_otg_hcd_urb_enqueue,
+	.urb_dequeue = dwc_otg_hcd_urb_dequeue,
+	.endpoint_disable = dwc_otg_hcd_endpoint_disable,
+	.get_frame_number = dwc_otg_hcd_get_frame_number,
+	.hub_status_data = dwc_otg_hcd_hub_status_data,
+	.hub_control = dwc_otg_hcd_hub_control,
+};
+
+/**
+ * Frees secondary storage associated with the dwc_hcd structure contained
+ * in the struct usb_hcd field.
+ */
+static void dwc_otg_hcd_free(struct usb_hcd *hcd)
+{
+	struct dwc_hcd *dwc_hcd = hcd_to_dwc_otg_hcd(hcd);
+	u32 i;
+
+	del_timers(dwc_hcd);
+
+	/* Free memory for QH/QTD lists */
+	qh_list_free(dwc_hcd, &dwc_hcd->non_periodic_sched_inactive);
+	qh_list_free(dwc_hcd, &dwc_hcd->non_periodic_sched_deferred);
+	qh_list_free(dwc_hcd, &dwc_hcd->non_periodic_sched_active);
+	qh_list_free(dwc_hcd, &dwc_hcd->periodic_sched_inactive);
+	qh_list_free(dwc_hcd, &dwc_hcd->periodic_sched_ready);
+	qh_list_free(dwc_hcd, &dwc_hcd->periodic_sched_assigned);
+	qh_list_free(dwc_hcd, &dwc_hcd->periodic_sched_queued);
+
+	/* Free memory for the host channels. */
+	for (i = 0; i < MAX_EPS_CHANNELS; i++) {
+		struct dwc_hc *hc = dwc_hcd->hc_ptr_array[i];
+
+		kfree(hc);
+	}
+	if (dwc_hcd->core_if->dma_enable) {
+		if (dwc_hcd->status_buf_dma)
+			dma_free_coherent(hcd->self.controller,
+					  DWC_OTG_HCD_STATUS_BUF_SIZE,
+					  dwc_hcd->status_buf,
+					  dwc_hcd->status_buf_dma);
+	} else {
+		kfree(dwc_hcd->status_buf);
+	}
+
+}
+
+/**
+ * Initializes the HCD. This function allocates memory for and initializes the
+ * static parts of the usb_hcd and dwc_hcd structures. It also registers the
+ * USB bus with the core and calls the hc_driver->start() function. It returns
+ * a negative error on failure.
+ */
+int __devinit dwc_otg_hcd_init(struct device *_dev,
+			       struct dwc_otg_device *dwc_otg_device)
+{
+	struct usb_hcd *hcd;
+	struct dwc_hcd *dwc_hcd;
+	struct dwc_otg_device *otg_dev = dev_get_drvdata(_dev);
+	int num_channels;
+	u32 i;
+	struct dwc_hc *channel;
+	int retval = 0;
+
+	/*
+	 * Allocate memory for the base HCD plus the DWC OTG HCD.
+	 * Initialize the base HCD.
+	 */
+	hcd = usb_create_hcd(&dwc_otg_hc_driver, _dev, dwc_otg_hcd_name);
+	if (!hcd) {
+		retval = -ENOMEM;
+		goto error1;
+	}
+	dev_set_drvdata(_dev, dwc_otg_device);
+	hcd->regs = otg_dev->base;
+	hcd->self.otg_port = 1;
+
+	/* Initialize the DWC OTG HCD. */
+	dwc_hcd = hcd_to_dwc_otg_hcd(hcd);
+	dwc_hcd->core_if = otg_dev->core_if;
+	spin_lock_init(&dwc_hcd->lock);
+	otg_dev->hcd = dwc_hcd;
+
+	/* Register the HCD CIL Callbacks */
+	dwc_otg_cil_register_hcd_callbacks(otg_dev->core_if, &hcd_cil_callbacks,
+					   hcd);
+
+	/* Initialize the non-periodic schedule. */
+	INIT_LIST_HEAD(&dwc_hcd->non_periodic_sched_inactive);
+	INIT_LIST_HEAD(&dwc_hcd->non_periodic_sched_active);
+	INIT_LIST_HEAD(&dwc_hcd->non_periodic_sched_deferred);
+
+	/* Initialize the periodic schedule. */
+	INIT_LIST_HEAD(&dwc_hcd->periodic_sched_inactive);
+	INIT_LIST_HEAD(&dwc_hcd->periodic_sched_ready);
+	INIT_LIST_HEAD(&dwc_hcd->periodic_sched_assigned);
+	INIT_LIST_HEAD(&dwc_hcd->periodic_sched_queued);
+
+	/*
+	 * Create a host channel descriptor for each host channel implemented
+	 * in the controller. Initialize the channel descriptor array.
+	 */
+	INIT_LIST_HEAD(&dwc_hcd->free_hc_list);
+	num_channels = dwc_hcd->core_if->core_params->host_channels;
+
+	for (i = 0; i < num_channels; i++) {
+		channel = kzalloc(sizeof(struct dwc_hc), GFP_KERNEL);
+		if (!channel) {
+			retval = -ENOMEM;
+			pr_err("%s: host channel allocation failed\n",
+			       __func__);
+			goto error2;
+		}
+
+		channel->hc_num = i;
+		dwc_hcd->hc_ptr_array[i] = channel;
+	}
+
+	/* Initialize the Connection timeout timer. */
+	init_timer(&dwc_hcd->conn_timer);
+
+	/* Initialize workqueue */
+	INIT_WORK(&dwc_hcd->usb_port_reset, port_reset_wqfunc);
+	INIT_WORK(&dwc_hcd->start_work, hcd_start_func);
+	INIT_WORK(&dwc_hcd->core_if->usb_port_otg, NULL);
+	INIT_DELAYED_WORK(&dwc_hcd->core_if->usb_port_wakeup,
+			  port_wakeup_wqfunc);
+
+	/* Set device flags indicating whether the HCD supports DMA. */
+	if (otg_dev->core_if->dma_enable) {
+		static u64 dummy_mask = DMA_BIT_MASK(32);
+
+		pr_info("Using DMA mode\n");
+		_dev->dma_mask = (void *)&dummy_mask;
+		_dev->coherent_dma_mask = ~0;
+	} else {
+		pr_info("Using Slave mode\n");
+		_dev->dma_mask = (void *)0;
+		_dev->coherent_dma_mask = 0;
+	}
+
+	init_hcd_usecs(dwc_hcd);
+	/*
+	 * Finish generic HCD initialization and start the HCD. This function
+	 * allocates the DMA buffer pool, registers the USB bus, requests the
+	 * IRQ line, and calls dwc_otg_hcd_start method.
+	 */
+	retval = usb_add_hcd(hcd, otg_dev->irq, IRQF_SHARED);
+	if (retval < 0)
+		goto error2;
+	hcd->rsrc_start = otg_dev->phys_addr;
+	hcd->rsrc_len = otg_dev->base_len;
+
+	/*
+	 * Allocate space for storing data on status transactions. Normally no
+	 * data is sent, but this space acts as a bit bucket. This must be
+	 * done after usb_add_hcd since that function allocates the DMA buffer
+	 * pool.
+	 */
+	if (otg_dev->core_if->dma_enable) {
+		dwc_hcd->status_buf =
+		    dma_alloc_coherent(_dev, DWC_OTG_HCD_STATUS_BUF_SIZE,
+				       &dwc_hcd->status_buf_dma,
+				       GFP_KERNEL | GFP_DMA);
+	} else {
+		dwc_hcd->status_buf = kmalloc(DWC_OTG_HCD_STATUS_BUF_SIZE,
+					      GFP_KERNEL);
+	}
+	if (!dwc_hcd->status_buf) {
+		retval = -ENOMEM;
+		pr_err("%s: status_buf allocation failed\n", __func__);
+		goto error3;
+	}
+	return 0;
+
+error3:
+	usb_remove_hcd(hcd);
+error2:
+	dwc_otg_hcd_free(hcd);
+	usb_put_hcd(hcd);
+error1:
+	return retval;
+}
+
+/**
+ * Removes the HCD.
+ * Frees memory and resources associated with the HCD and deregisters the bus.
+ */
+void __devexit dwc_otg_hcd_remove(struct device *_dev)
+{
+	struct dwc_otg_device *otg_dev = dev_get_drvdata(_dev);
+	struct dwc_hcd *dwc_hcd = otg_dev->hcd;
+	struct usb_hcd *hcd = dwc_otg_hcd_to_hcd(dwc_hcd);
+
+	/* Turn off all interrupts */
+	dwc_write32(gintmsk_reg(dwc_hcd), 0);
+	dwc_modify32(gahbcfg_reg(dwc_hcd), 1, 0);
+
+	cancel_work_sync(&dwc_hcd->start_work);
+	cancel_work_sync(&dwc_hcd->usb_port_reset);
+	cancel_work_sync(&dwc_hcd->core_if->usb_port_otg);
+	cancel_rearming_delayed_work(&dwc_hcd->core_if->usb_port_wakeup);
+
+	usb_remove_hcd(hcd);
+	dwc_otg_hcd_free(hcd);
+	usb_put_hcd(hcd);
+}
+
+/** Returns the current frame number. */
+int dwc_otg_hcd_get_frame_number(struct usb_hcd *hcd)
+{
+	struct dwc_hcd *dwc_hcd = hcd_to_dwc_otg_hcd(hcd);
+	u32 hfnum = 0;
+
+	hfnum = dwc_read32((u32) dwc_hcd->core_if->host_if->
+			   host_global_regs + DWC_HFNUM);
+
+	return DWC_HFNUM_FRNUM_RD(hfnum);
+}
+
+/**
+ * Prepares a host channel for transferring packets to/from a specific
+ * endpoint. The HCCHARn register is set up with the characteristics specified
+ * in _hc. Host channel interrupts that may need to be serviced while this
+ * transfer is in progress are enabled.
+ */
+static void dwc_otg_hc_init(struct core_if *core_if, struct dwc_hc *hc)
+{
+	u32 intr_enable;
+	u32 global_regs = (u32) core_if->core_global_regs;
+	u32 hc_intr_mask = 0;
+	u32 gintmsk = 0;
+	u32 hcchar;
+	u32 hcsplt;
+	u8 hc_num = hc->hc_num;
+	struct dwc_host_if *host_if = core_if->host_if;
+	u32 hc_regs = (u32) host_if->hc_regs[hc_num];
+
+	/* Clear old interrupt conditions for this host channel. */
+	hc_intr_mask = 0x3FF;
+	dwc_write32(hc_regs + DWC_HCINT, hc_intr_mask);
+
+	/* Enable channel interrupts required for this transfer. */
+	hc_intr_mask = 0;
+	hc_intr_mask = DWC_HCINTMSK_CHAN_HALTED_RW(hc_intr_mask, 1);
+	if (core_if->dma_enable) {
+		hc_intr_mask = DWC_HCINTMSK_AHB_ERR_RW(hc_intr_mask, 1);
+
+		if (hc->error_state && !hc->do_split &&
+		    hc->ep_type != DWC_OTG_EP_TYPE_ISOC) {
+			hc_intr_mask =
+			    DWC_HCINTMSK_ACK_RESP_REC_RW(hc_intr_mask, 1);
+			if (hc->ep_is_in) {
+				hc_intr_mask =
+				    DWC_HCINTMSK_DATA_TOG_ERR_RW(hc_intr_mask,
+								 1);
+				if (hc->ep_type != DWC_OTG_EP_TYPE_INTR)
+					hc_intr_mask =
+					    DWC_HCINTMSK_NAK_RESP_REC_RW
+					    (hc_intr_mask, 1);
+			}
+		}
+	} else {
+		switch (hc->ep_type) {
+		case DWC_OTG_EP_TYPE_CONTROL:
+		case DWC_OTG_EP_TYPE_BULK:
+			hc_intr_mask =
+			    DWC_HCINTMSK_TXFER_CMPL_RW(hc_intr_mask, 1);
+			hc_intr_mask =
+			    DWC_HCINTMSK_STALL_RESP_REC_RW(hc_intr_mask, 1);
+			hc_intr_mask =
+			    DWC_HCINTMSK_TRANS_ERR_RW(hc_intr_mask, 1);
+			hc_intr_mask =
+			    DWC_HCINTMSK_DATA_TOG_ERR_RW(hc_intr_mask, 1);
+
+			if (hc->ep_is_in) {
+				hc_intr_mask =
+				    DWC_HCINTMSK_BBL_ERR_RW(hc_intr_mask, 1);
+			} else {
+				hc_intr_mask =
+				    DWC_HCINTMSK_NAK_RESP_REC_RW(hc_intr_mask,
+								 1);
+				hc_intr_mask =
+				    DWC_HCINTMSK_NYET_RESP_REC_RW(hc_intr_mask,
+								  1);
+				if (hc->do_ping)
+					hc_intr_mask =
+					    DWC_HCINTMSK_ACK_RESP_REC_RW
+					    (hc_intr_mask, 1);
+			}
+
+			if (hc->do_split) {
+				hc_intr_mask =
+				    DWC_HCINTMSK_NAK_RESP_REC_RW(hc_intr_mask,
+								 1);
+				if (hc->complete_split)
+					hc_intr_mask =
+					    DWC_HCINTMSK_NYET_RESP_REC_RW
+					    (hc_intr_mask, 1);
+				else
+					hc_intr_mask =
+					    DWC_HCINTMSK_ACK_RESP_REC_RW
+					    (hc_intr_mask, 1);
+			}
+
+			if (hc->error_state)
+				hc_intr_mask =
+				    DWC_HCINTMSK_ACK_RESP_REC_RW(hc_intr_mask,
+								 1);
+			break;
+		case DWC_OTG_EP_TYPE_INTR:
+			hc_intr_mask =
+			    DWC_HCINTMSK_TXFER_CMPL_RW(hc_intr_mask, 1);
+			hc_intr_mask =
+			    DWC_HCINTMSK_NAK_RESP_REC_RW(hc_intr_mask, 1);
+			hc_intr_mask =
+			    DWC_HCINTMSK_STALL_RESP_REC_RW(hc_intr_mask, 1);
+			hc_intr_mask =
+			    DWC_HCINTMSK_TRANS_ERR_RW(hc_intr_mask, 1);
+			hc_intr_mask =
+			    DWC_HCINTMSK_DATA_TOG_ERR_RW(hc_intr_mask, 1);
+			hc_intr_mask =
+			    DWC_HCINTMSK_FRAME_OVERN_ERR_RW(hc_intr_mask, 1);
+
+			if (hc->ep_is_in)
+				hc_intr_mask =
+				    DWC_HCINTMSK_BBL_ERR_RW(hc_intr_mask, 1);
+			if (hc->error_state)
+				hc_intr_mask =
+				    DWC_HCINTMSK_ACK_RESP_REC_RW(hc_intr_mask,
+								 1);
+
+			if (hc->do_split) {
+				if (hc->complete_split)
+					hc_intr_mask =
+					    DWC_HCINTMSK_NYET_RESP_REC_RW
+					    (hc_intr_mask, 1);
+				else
+					hc_intr_mask =
+					    DWC_HCINTMSK_ACK_RESP_REC_RW
+					    (hc_intr_mask, 1);
+			}
+			break;
+		case DWC_OTG_EP_TYPE_ISOC:
+			hc_intr_mask =
+			    DWC_HCINTMSK_TXFER_CMPL_RW(hc_intr_mask, 1);
+			hc_intr_mask =
+			    DWC_HCINTMSK_FRAME_OVERN_ERR_RW(hc_intr_mask, 1);
+			hc_intr_mask =
+			    DWC_HCINTMSK_ACK_RESP_REC_RW(hc_intr_mask, 1);
+
+			if (hc->ep_is_in) {
+				hc_intr_mask =
+				    DWC_HCINTMSK_TRANS_ERR_RW(hc_intr_mask, 1);
+				hc_intr_mask =
+				    DWC_HCINTMSK_BBL_ERR_RW(hc_intr_mask, 1);
+			}
+			break;
+		}
+	}
+	dwc_write32(hc_regs + DWC_HCINTMSK, hc_intr_mask);
+
+	/* Enable the top level host channel interrupt. */
+	intr_enable = (1 << hc_num);
+	dwc_modify32((u32) host_if->host_global_regs + DWC_HAINTMSK, 0,
+		     intr_enable);
+
+	/* Make sure host channel interrupts are enabled. */
+	gintmsk |= DWC_INTMSK_HST_CHAN;
+	dwc_modify32(global_regs + DWC_GINTMSK, 0, gintmsk);
+
+	/*
+	 * Program the HCCHARn register with the endpoint characteristics for
+	 * the current transfer.
+	 */
+	hcchar = 0;
+	hcchar = DWC_HCCHAR_DEV_ADDR_RW(hcchar, hc->dev_addr);
+	hcchar = DWC_HCCHAR_EP_NUM_RW(hcchar, hc->ep_num);
+	hcchar = DWC_HCCHAR_EPDIR_RW(hcchar, hc->ep_is_in);
+	hcchar = DWC_HCCHAR_LSP_DEV_RW(hcchar, (hc->speed ==
+						DWC_OTG_EP_SPEED_LOW));
+	hcchar = DWC_HCCHAR_EPTYPE_RW(hcchar, hc->ep_type);
+	hcchar = DWC_HCCHAR_MPS_RW(hcchar, hc->max_packet);
+	dwc_write32((u32) host_if->hc_regs[hc_num] + DWC_HCCHAR, hcchar);
+
+	/* Program the HCSPLIT register for SPLITs */
+	hcsplt = 0;
+	if (hc->do_split) {
+		hcsplt = DWC_HCSPLT_COMP_SPLT_RW(hcsplt, hc->complete_split);
+		hcsplt = DWC_HCSPLT_TRANS_POS_RW(hcsplt, hc->xact_pos);
+		hcsplt = DWC_HCSPLT_HUB_ADDR_RW(hcsplt, hc->hub_addr);
+		hcsplt = DWC_HCSPLT_PRT_ADDR_RW(hcsplt, hc->port_addr);
+	}
+	dwc_write32((u32) host_if->hc_regs[hc_num] + DWC_HCSPLT, hcsplt);
+}
+
+/**
+ * Assigns transactions from a QTD to a free host channel and initializes the
+ * host channel to perform the transactions. The host channel is removed from
+ * the free list.
+ */
+static void assign_and_init_hc(struct dwc_hcd *hcd, struct dwc_qh *qh)
+{
+	struct dwc_hc *hc;
+	struct dwc_qtd *qtd;
+	struct urb *urb;
+	struct usb_iso_packet_descriptor *frame_desc;
+
+	hc = list_entry(hcd->free_hc_list.next, struct dwc_hc, hc_list_entry);
+
+	/* Remove the host channel from the free list. */
+	list_del_init(&hc->hc_list_entry);
+	qtd = list_entry(qh->qtd_list.next, struct dwc_qtd, qtd_list_entry);
+	urb = qtd->urb;
+	qh->channel = hc;
+	qh->qtd_in_process = qtd;
+
+	/*
+	 * Use usb_pipedevice to determine device address. This address is
+	 * 0 before the SET_ADDRESS command and the correct address afterward.
+	 */
+	hc->dev_addr = usb_pipedevice(urb->pipe);
+	hc->ep_num = usb_pipeendpoint(urb->pipe);
+
+	if (urb->dev->speed == USB_SPEED_LOW)
+		hc->speed = DWC_OTG_EP_SPEED_LOW;
+	else if (urb->dev->speed == USB_SPEED_FULL)
+		hc->speed = DWC_OTG_EP_SPEED_FULL;
+	else
+		hc->speed = DWC_OTG_EP_SPEED_HIGH;
+
+	hc->max_packet = dwc_max_packet(qh->maxp);
+	hc->xfer_started = 0;
+	hc->halt_status = DWC_OTG_HC_XFER_NO_HALT_STATUS;
+	hc->error_state = (qtd->error_count > 0);
+	hc->halt_on_queue = 0;
+	hc->halt_pending = 0;
+	hc->requests = 0;
+
+	/*
+	 * The following values may be modified in the transfer type section
+	 * below. The xfer_len value may be reduced when the transfer is
+	 * started to accommodate the max widths of the XferSize and PktCnt
+	 * fields in the HCTSIZn register.
+	 */
+	hc->do_ping = qh->ping_state;
+	hc->ep_is_in = (usb_pipein(urb->pipe) != 0);
+	hc->data_pid_start = qh->data_toggle;
+	hc->multi_count = 1;
+
+	if (hcd->core_if->dma_enable)
+		hc->xfer_buff = urb->transfer_dma + (u8 *) urb->actual_length;
+	else
+		hc->xfer_buff = (u8 *) urb->transfer_buffer +
+		    urb->actual_length;
+
+	hc->xfer_len = urb->transfer_buffer_length - urb->actual_length;
+	hc->xfer_count = 0;
+
+	/*
+	 * Set the split attributes
+	 */
+	hc->do_split = 0;
+	if (qh->do_split) {
+		hc->do_split = 1;
+		hc->xact_pos = qtd->isoc_split_pos;
+		hc->complete_split = qtd->complete_split;
+		hc->hub_addr = urb->dev->tt->hub->devnum;
+		hc->port_addr = urb->dev->ttport;
+	}
+
+	switch (usb_pipetype(urb->pipe)) {
+	case PIPE_CONTROL:
+		hc->ep_type = DWC_OTG_EP_TYPE_CONTROL;
+
+		switch (qtd->control_phase) {
+		case DWC_OTG_CONTROL_SETUP:
+			hc->do_ping = 0;
+			hc->ep_is_in = 0;
+			hc->data_pid_start = DWC_OTG_HC_PID_SETUP;
+
+			if (hcd->core_if->dma_enable)
+				hc->xfer_buff = (u8 *) (u32) urb->setup_dma;
+			else
+				hc->xfer_buff = (u8 *) urb->setup_packet;
+
+			hc->xfer_len = 8;
+			break;
+		case DWC_OTG_CONTROL_DATA:
+			hc->data_pid_start = qtd->data_toggle;
+			break;
+		case DWC_OTG_CONTROL_STATUS:
+			/*
+			 * Direction is opposite of data direction or IN if no
+			 * data.
+			 */
+			if (urb->transfer_buffer_length == 0)
+				hc->ep_is_in = 1;
+			else
+				hc->ep_is_in = (usb_pipein(urb->pipe) !=
+						USB_DIR_IN);
+
+			if (hc->ep_is_in)
+				hc->do_ping = 0;
+
+			hc->data_pid_start = DWC_OTG_HC_PID_DATA1;
+			hc->xfer_len = 0;
+			if (hcd->core_if->dma_enable)
+				hc->xfer_buff =
+				    (u8 *) (u32) hcd->status_buf_dma;
+			else
+				hc->xfer_buff = (u8 *) hcd->status_buf;
+			break;
+		}
+		break;
+	case PIPE_BULK:
+		hc->ep_type = DWC_OTG_EP_TYPE_BULK;
+		break;
+	case PIPE_INTERRUPT:
+		hc->ep_type = DWC_OTG_EP_TYPE_INTR;
+		break;
+	case PIPE_ISOCHRONOUS:
+		frame_desc = &urb->iso_frame_desc[qtd->isoc_frame_index];
+		hc->ep_type = DWC_OTG_EP_TYPE_ISOC;
+
+		if (hcd->core_if->dma_enable)
+			hc->xfer_buff = (u8 *) (u32) urb->transfer_dma;
+		else
+			hc->xfer_buff = (u8 *) urb->transfer_buffer;
+
+		hc->xfer_buff += frame_desc->offset + qtd->isoc_split_offset;
+		hc->xfer_len = frame_desc->length - qtd->isoc_split_offset;
+
+		if (hc->xact_pos == DWC_HCSPLIT_XACTPOS_ALL) {
+			if (hc->xfer_len <= 188)
+				hc->xact_pos = DWC_HCSPLIT_XACTPOS_ALL;
+			else
+				hc->xact_pos = DWC_HCSPLIT_XACTPOS_BEGIN;
+		}
+		break;
+	}
+
+	if (hc->ep_type == DWC_OTG_EP_TYPE_INTR ||
+	    hc->ep_type == DWC_OTG_EP_TYPE_ISOC)
+		/*
+		 * This value may be modified when the transfer is started to
+		 * reflect the actual transfer length.
+		 */
+		hc->multi_count = dwc_hb_mult(qh->maxp);
+
+	dwc_otg_hc_init(hcd->core_if, hc);
+	hc->qh = qh;
+}
+
+/**
+ * This function selects transactions from the HCD transfer schedule and
+ * assigns them to available host channels. It is called from HCD interrupt
+ * handler functions.
+ */
+enum dwc_transaction_type dwc_otg_hcd_select_transactions(struct dwc_hcd *hcd)
+{
+	struct list_head *qh_ptr;
+	struct dwc_qh *qh;
+	int num_channels;
+	enum dwc_transaction_type ret_val = DWC_OTG_TRANSACTION_NONE;
+
+	/* Process entries in the periodic ready list. */
+	num_channels = hcd->core_if->core_params->host_channels;
+	qh_ptr = hcd->periodic_sched_ready.next;
+	while (qh_ptr != &hcd->periodic_sched_ready &&
+	       !list_empty(&hcd->free_hc_list)) {
+		/* Leave one channel for non periodic transactions. */
+		if (hcd->available_host_channels <= 1)
+			break;
+		hcd->available_host_channels--;
+		qh = list_entry(qh_ptr, struct dwc_qh, qh_list_entry);
+		assign_and_init_hc(hcd, qh);
+		/*
+		 * Move the QH from the periodic ready schedule to the
+		 * periodic assigned schedule.
+		 */
+		qh_ptr = qh_ptr->next;
+		list_move(&qh->qh_list_entry, &hcd->periodic_sched_assigned);
+		ret_val = DWC_OTG_TRANSACTION_PERIODIC;
+	}
+
+	/*
+	 * Process entries in the deferred portion of the non-periodic list.
+	 * A NAK put them here and, at the right time, they need to be
+	 * placed on the sched_inactive list.
+	 */
+	qh_ptr = hcd->non_periodic_sched_deferred.next;
+	while (qh_ptr != &hcd->non_periodic_sched_deferred) {
+		u16 frame_number =
+		    dwc_otg_hcd_get_frame_number(dwc_otg_hcd_to_hcd(hcd));
+		qh = list_entry(qh_ptr, struct dwc_qh, qh_list_entry);
+		qh_ptr = qh_ptr->next;
+
+		if (dwc_frame_num_le(qh->sched_frame, frame_number))
+			/*
+			 * Move the QH from the non periodic deferred schedule
+			 * to the non periodic inactive schedule.
+			 */
+			list_move(&qh->qh_list_entry,
+				  &hcd->non_periodic_sched_inactive);
+	}
+
+	/*
+	 * Process entries in the inactive portion of the non-periodic
+	 * schedule. Some free host channels may not be used if they are
+	 * reserved for periodic transfers.
+	 */
+	qh_ptr = hcd->non_periodic_sched_inactive.next;
+	num_channels = hcd->core_if->core_params->host_channels;
+
+	while (qh_ptr != &hcd->non_periodic_sched_inactive
+	       && !list_empty(&hcd->free_hc_list)) {
+		if (hcd->available_host_channels < 1)
+			break;
+		hcd->available_host_channels--;
+		qh = list_entry(qh_ptr, struct dwc_qh, qh_list_entry);
+		assign_and_init_hc(hcd, qh);
+		/*
+		 * Move the QH from the non-periodic inactive schedule to the
+		 * non-periodic active schedule.
+		 */
+		qh_ptr = qh_ptr->next;
+		list_move(&qh->qh_list_entry, &hcd->non_periodic_sched_active);
+		if (ret_val == DWC_OTG_TRANSACTION_NONE)
+			ret_val = DWC_OTG_TRANSACTION_NON_PERIODIC;
+		else
+			ret_val = DWC_OTG_TRANSACTION_ALL;
+
+	}
+	return ret_val;
+}
+
+/**
+ * Sets the channel property that indicates in which frame a periodic transfer
+ * should occur. This is always set to the _next_ frame. This function has no
+ * effect on non-periodic transfers.
+ */
+static inline void hc_set_even_odd_frame(struct core_if *core_if,
+					 struct dwc_hc *hc, u32 * hcchar)
+{
+	if (hc->ep_type == DWC_OTG_EP_TYPE_INTR ||
+	    hc->ep_type == DWC_OTG_EP_TYPE_ISOC) {
+		u32 hfnum = 0;
+
+		hfnum = dwc_read32((u32) core_if->host_if->host_global_regs +
+				   DWC_HFNUM);
+
+		/* 1 if _next_ frame is odd, 0 if it's even */
+		*hcchar = DWC_HCCHAR_ODD_FRAME_RW(*hcchar,
+						  ((DWC_HFNUM_FRNUM_RD(hfnum) &
+						    0x1) ? 0 : 1));
+	}
+}
+
+static void set_initial_xfer_pid(struct dwc_hc *hc)
+{
+	if (hc->speed == DWC_OTG_EP_SPEED_HIGH) {
+		if (hc->ep_is_in) {
+			if (hc->multi_count == 1)
+				hc->data_pid_start = DWC_OTG_HC_PID_DATA0;
+			else if (hc->multi_count == 2)
+				hc->data_pid_start = DWC_OTG_HC_PID_DATA1;
+			else
+				hc->data_pid_start = DWC_OTG_HC_PID_DATA2;
+		} else {
+			if (hc->multi_count == 1)
+				hc->data_pid_start = DWC_OTG_HC_PID_DATA0;
+			else
+				hc->data_pid_start = DWC_OTG_HC_PID_MDATA;
+		}
+	} else {
+		hc->data_pid_start = DWC_OTG_HC_PID_DATA0;
+	}
+}
+
+/**
+ * Starts a PING transfer. This function should only be called in Slave mode.
+ * The Do Ping bit is set in the HCTSIZ register, then the channel is enabled.
+ */
+void dwc_otg_hc_do_ping(struct core_if *core_if, struct dwc_hc *hc)
+{
+	u32 hcchar;
+	u32 hctsiz = 0;
+
+	u32 hc_regs = (u32) core_if->host_if->hc_regs[hc->hc_num];
+
+	hctsiz = 0;
+	hctsiz = DWC_HCTSIZ_DO_PING_PROTO_RW(hctsiz, 1);
+	hctsiz = DWC_HCTSIZ_PKT_CNT_RW(hctsiz, 1);
+	dwc_write32(hc_regs + DWC_HCTSIZ, hctsiz);
+
+	hcchar = dwc_read32(hc_regs + DWC_HCCHAR);
+	hcchar = DWC_HCCHAR_ENA_RW(hcchar, 1);
+	hcchar = DWC_HCCHAR_DIS_RW(hcchar, 0);
+	dwc_write32(hc_regs + DWC_HCCHAR, hcchar);
+}
+
+/**
+ * This function writes a packet into the Tx FIFO associated with the Host
+ * Channel. For a channel associated with a non-periodic EP, the non-periodic
+ * Tx FIFO is written. For a channel associated with a periodic EP, the
+ * periodic Tx FIFO is written. This function should only be called in Slave
+ * mode.
+ *
+ * Upon return the xfer_buff and xfer_count fields in hc are incremented by
+ * then number of bytes written to the Tx FIFO.
+ */
+static void dwc_otg_hc_write_packet(struct core_if *core_if, struct dwc_hc *hc)
+{
+	u32 i;
+	u32 remaining_count;
+	u32 byte_count;
+	u32 dword_count;
+	u32 *data_buff = (u32 *) (hc->xfer_buff);
+	u32 *data_fifo = core_if->data_fifo[hc->hc_num];
+
+	remaining_count = hc->xfer_len - hc->xfer_count;
+	if (remaining_count > hc->max_packet)
+		byte_count = hc->max_packet;
+	else
+		byte_count = remaining_count;
+
+	dword_count = (byte_count + 3) / 4;
+
+	if (((unsigned long)data_buff) & 0x3)
+		/* xfer_buff is not DWORD aligned. */
+		for (i = 0; i < dword_count; i++, data_buff++)
+			dwc_write_datafifo32(data_fifo,
+					     get_unaligned(data_buff));
+	else
+		/* xfer_buff is DWORD aligned. */
+		for (i = 0; i < dword_count; i++, data_buff++)
+			dwc_write_datafifo32(data_fifo, *data_buff);
+
+	hc->xfer_count += byte_count;
+	hc->xfer_buff += byte_count;
+}
+
+/**
+ * This function does the setup for a data transfer for a host channel and
+ * starts the transfer. May be called in either Slave mode or DMA mode. In
+ * Slave mode, the caller must ensure that there is sufficient space in the
+ * request queue and Tx Data FIFO.
+ *
+ * For an OUT transfer in Slave mode, it loads a data packet into the
+ * appropriate FIFO. If necessary, additional data packets will be loaded in
+ * the Host ISR.
+ *
+ * For an IN transfer in Slave mode, a data packet is requested. The data
+ * packets are unloaded from the Rx FIFO in the Host ISR. If necessary,
+ * additional data packets are requested in the Host ISR.
+ *
+ * For a PING transfer in Slave mode, the Do Ping bit is set in the HCTSIZ
+ * register along with a packet count of 1 and the channel is enabled. This
+ * causes a single PING transaction to occur. Other fields in HCTSIZ are
+ * simply set to 0 since no data transfer occurs in this case.
+ *
+ * For a PING transfer in DMA mode, the HCTSIZ register is initialized with
+ * all the information required to perform the subsequent data transfer. In
+ * addition, the Do Ping bit is set in the HCTSIZ register. In this case, the
+ * controller performs the entire PING protocol, then starts the data
+ * transfer.
+ */
+static void dwc_otg_hc_start_transfer(struct core_if *core_if,
+				      struct dwc_hc *hc)
+{
+	u32 hcchar;
+	u32 hctsiz = 0;
+	u16 num_packets;
+	u32 max_hc_xfer_size = core_if->core_params->max_transfer_size;
+	u16 max_hc_pkt_count = core_if->core_params->max_packet_count;
+	u32 hc_regs = (u32) core_if->host_if->hc_regs[hc->hc_num];
+	hctsiz = 0;
+
+	if (hc->do_ping) {
+		if (!core_if->dma_enable) {
+			dwc_otg_hc_do_ping(core_if, hc);
+			hc->xfer_started = 1;
+			return;
+		} else {
+			hctsiz = DWC_HCTSIZ_DO_PING_PROTO_RW(hctsiz, 1);
+		}
+	}
+
+	if (hc->do_split) {
+		num_packets = 1;
+
+		if (hc->complete_split && !hc->ep_is_in)
+			/*
+			 * For CSPLIT OUT Transfer, set the size to 0 so the
+			 * core doesn't expect any data written to the FIFO
+			 */
+			hc->xfer_len = 0;
+		else if (hc->ep_is_in || (hc->xfer_len > hc->max_packet))
+			hc->xfer_len = hc->max_packet;
+		else if (!hc->ep_is_in && (hc->xfer_len > 188))
+			hc->xfer_len = 188;
+
+		hctsiz = DWC_HCTSIZ_XFER_SIZE_RW(hctsiz, hc->xfer_len);
+	} else {
+		/*
+		 * Ensure that the transfer length and packet count will fit
+		 * in the widths allocated for them in the HCTSIZn register.
+		 */
+		if (hc->ep_type == DWC_OTG_EP_TYPE_INTR ||
+		    hc->ep_type == DWC_OTG_EP_TYPE_ISOC) {
+			u32 max_len = hc->multi_count * hc->max_packet;
+
+			/*
+			 * Make sure the transfer size is no larger than one
+			 * (micro)frame's worth of data. (A check was done
+			 * when the periodic transfer was accepted to ensure
+			 * that a (micro)frame's worth of data can be
+			 * programmed into a channel.)
+			 */
+			if (hc->xfer_len > max_len)
+				hc->xfer_len = max_len;
+		} else if (hc->xfer_len > max_hc_xfer_size) {
+			/*
+			 * Make sure that xfer_len is a multiple of max packet
+			 * size.
+			 */
+			hc->xfer_len = max_hc_xfer_size - hc->max_packet + 1;
+		}
+		if (hc->xfer_len > 0) {
+			num_packets = (hc->xfer_len + hc->max_packet - 1) /
+			    hc->max_packet;
+			if (num_packets > max_hc_pkt_count) {
+				num_packets = max_hc_pkt_count;
+				hc->xfer_len = num_packets * hc->max_packet;
+			}
+		} else {
+			/* Need 1 packet for transfer length of 0. */
+			num_packets = 1;
+		}
+
+		if (hc->ep_is_in)
+			/*
+			 * Always program an integral # of max packets for IN
+			 * transfers.
+			 */
+			hc->xfer_len = num_packets * hc->max_packet;
+
+		if (hc->ep_type == DWC_OTG_EP_TYPE_INTR ||
+		    hc->ep_type == DWC_OTG_EP_TYPE_ISOC)
+			/*
+			 * Make sure that the multi_count field matches the
+			 * actual transfer length.
+			 */
+			hc->multi_count = num_packets;
+
+		/* Set up the initial PID for the transfer. */
+		if (hc->ep_type == DWC_OTG_EP_TYPE_ISOC)
+			set_initial_xfer_pid(hc);
+
+		hctsiz = DWC_HCTSIZ_XFER_SIZE_RW(hctsiz, hc->xfer_len);
+	}
+
+	hc->start_pkt_count = num_packets;
+	hctsiz = DWC_HCTSIZ_PKT_CNT_RW(hctsiz, num_packets);
+	hctsiz = DWC_HCTSIZ_PKT_PID_RW(hctsiz, hc->data_pid_start);
+	dwc_write32(hc_regs + DWC_HCTSIZ, hctsiz);
+
+	if (core_if->dma_enable)
+		dwc_write32(hc_regs + DWC_HCDMA, (u32) hc->xfer_buff);
+
+	/* Start the split */
+	if (hc->do_split) {
+		u32 hcsplt;
+
+		hcsplt = dwc_read32(hc_regs + DWC_HCSPLT);
+		hcsplt = DWC_HCSPLT_COMP_SPLT_RW(hcsplt, 1);
+		dwc_write32(hc_regs + DWC_HCSPLT, hcsplt);
+	}
+
+	hcchar = dwc_read32(hc_regs + DWC_HCCHAR);
+	hcchar = DWC_HCCHAR_MULTI_CNT_RW(hcchar, hc->multi_count);
+	hc_set_even_odd_frame(core_if, hc, &hcchar);
+
+	/* Set host channel enable after all other setup is complete. */
+	hcchar = DWC_HCCHAR_ENA_RW(hcchar, 1);
+	hcchar = DWC_HCCHAR_DIS_RW(hcchar, 0);
+	dwc_write32(hc_regs + DWC_HCCHAR, hcchar);
+
+	hc->xfer_started = 1;
+	hc->requests++;
+	if (!core_if->dma_enable && !hc->ep_is_in && hc->xfer_len > 0)
+		/* Load OUT packet into the appropriate Tx FIFO. */
+		dwc_otg_hc_write_packet(core_if, hc);
+}
+
+/**
+ * This function continues a data transfer that was started by previous call
+ * to dwc_otg_hc_start_transfer</code>. The caller must ensure there is
+ * sufficient space in the request queue and Tx Data FIFO. This function
+ * should only be called in Slave mode. In DMA mode, the controller acts
+ * autonomously to complete transfers programmed to a host channel.
+ *
+ * For an OUT transfer, a new data packet is loaded into the appropriate FIFO
+ * if there is any data remaining to be queued. For an IN transfer, another
+ * data packet is always requested. For the SETUP phase of a control transfer,
+ * this function does nothing.
+ */
+static int dwc_otg_hc_continue_transfer(struct core_if *core_if,
+					struct dwc_hc *hc)
+{
+	if (hc->do_split) {
+		/* SPLITs always queue just once per channel */
+		return 0;
+	} else if (hc->data_pid_start == DWC_OTG_HC_PID_SETUP) {
+		/* SETUPs are queued only once since they can't be NAKed. */
+		return 0;
+	} else if (hc->ep_is_in) {
+		/*
+		 * Always queue another request for other IN transfers. If
+		 * back-to-back INs are issued and NAKs are received for both,
+		 * the driver may still be processing the first NAK when the
+		 * second NAK is received. When the interrupt handler clears
+		 * the NAK interrupt for the first NAK, the second NAK will
+		 * not be seen. So we can't depend on the NAK interrupt
+		 * handler to requeue a NAKed request. Instead, IN requests
+		 * are issued each time this function is called. When the
+		 * transfer completes, the extra requests for the channel will
+		 * be flushed.
+		 */
+		u32 hcchar;
+		u32 hc_regs = (u32) core_if->host_if->hc_regs[hc->hc_num];
+
+		hcchar = dwc_read32(hc_regs + DWC_HCCHAR);
+		hc_set_even_odd_frame(core_if, hc, &hcchar);
+
+		hcchar = DWC_HCCHAR_ENA_RW(hcchar, 1);
+		hcchar = DWC_HCCHAR_DIS_RW(hcchar, 0);
+		dwc_write32(hc_regs + DWC_HCCHAR, hcchar);
+
+		hc->requests++;
+		return 1;
+	} else {
+		/* OUT transfers. */
+		if (hc->xfer_count < hc->xfer_len) {
+			if (hc->ep_type == DWC_OTG_EP_TYPE_INTR ||
+			    hc->ep_type == DWC_OTG_EP_TYPE_ISOC) {
+				u32 hcchar;
+				u32 hc_regs;
+
+				hc_regs =
+				    (u32) core_if->host_if->hc_regs[hc->hc_num];
+				hcchar = dwc_read32(hc_regs + DWC_HCCHAR);
+				hc_set_even_odd_frame(core_if, hc, &hcchar);
+			}
+
+			/* Load OUT packet into the appropriate Tx FIFO. */
+			dwc_otg_hc_write_packet(core_if, hc);
+			hc->requests++;
+			return 1;
+		} else {
+			return 0;
+		}
+	}
+}
+
+/**
+ * This function writes a packet into the Tx FIFO associated with the Host
+ * Channel. For a channel associated with a non-periodic EP, the non-periodic
+ * Tx FIFO is written. For a channel associated with a periodic EP, the
+ * periodic Tx FIFO is written. This function should only be called in Slave
+ * mode.
+ *
+ * Upon return the xfer_buff and xfer_count fields in hc are incremented by
+ * then number of bytes written to the Tx FIFO.
+ */
+
+/**
+ * Attempts to queue a single transaction request for a host channel
+ * associated with either a periodic or non-periodic transfer. This function
+ * assumes that there is space available in the appropriate request queue. For
+ * an OUT transfer or SETUP transaction in Slave mode, it checks whether space
+ * is available in the appropriate Tx FIFO.
+ */
+static int queue_transaction(struct dwc_hcd *hcd, struct dwc_hc *hc,
+			     u16 _fifo_dwords_avail)
+{
+	int retval;
+
+	if (hcd->core_if->dma_enable) {
+		if (!hc->xfer_started) {
+			dwc_otg_hc_start_transfer(hcd->core_if, hc);
+			hc->qh->ping_state = 0;
+		}
+		retval = 0;
+	} else if (hc->halt_pending) {
+		/* Don't queue a request if the channel has been halted. */
+		retval = 0;
+	} else if (hc->halt_on_queue) {
+		dwc_otg_hc_halt(hcd->core_if, hc, hc->halt_status);
+		retval = 0;
+	} else if (hc->do_ping) {
+		if (!hc->xfer_started)
+			dwc_otg_hc_start_transfer(hcd->core_if, hc);
+		retval = 0;
+	} else if (!hc->ep_is_in || hc->data_pid_start ==
+			DWC_OTG_HC_PID_SETUP) {
+		if ((_fifo_dwords_avail * 4) >= hc->max_packet) {
+			if (!hc->xfer_started) {
+				dwc_otg_hc_start_transfer(hcd->core_if, hc);
+				retval = 1;
+			} else {
+				retval =
+				    dwc_otg_hc_continue_transfer(hcd->core_if,
+								 hc);
+			}
+		} else {
+			retval = -1;
+		}
+	} else {
+		if (!hc->xfer_started) {
+			dwc_otg_hc_start_transfer(hcd->core_if, hc);
+			retval = 1;
+		} else {
+			retval = dwc_otg_hc_continue_transfer(hcd->core_if, hc);
+		}
+	}
+	return retval;
+}
+
+/**
+ * Processes active non-periodic channels and queues transactions for these
+ * channels to the DWC_otg controller. After queueing transactions, the NP Tx
+ * FIFO Empty interrupt is enabled if there are more transactions to queue as
+ * NP Tx FIFO or request queue space becomes available. Otherwise, the NP Tx
+ * FIFO Empty interrupt is disabled.
+ */
+static void process_non_periodic_channels(struct dwc_hcd *hcd)
+{
+	u32 tx_status = 0;
+	struct list_head *orig_qh_ptr;
+	struct dwc_qh *qh;
+	int status;
+	int no_queue_space = 0;
+	int no_fifo_space = 0;
+	int more_to_do = 0;
+	u32 regs = (u32) hcd->core_if->core_global_regs;
+
+	/*
+	 * Keep track of the starting point. Skip over the start-of-list
+	 * entry.
+	 */
+	if (hcd->non_periodic_qh_ptr == &hcd->non_periodic_sched_active)
+		hcd->non_periodic_qh_ptr = hcd->non_periodic_qh_ptr->next;
+	orig_qh_ptr = hcd->non_periodic_qh_ptr;
+
+	/*
+	 * Process once through the active list or until no more space is
+	 * available in the request queue or the Tx FIFO.
+	 */
+	do {
+		tx_status = dwc_read32(regs + DWC_GNPTXSTS);
+		if (!hcd->core_if->dma_enable &&
+		    DWC_GNPTXSTS_NPTXQSPCAVAIL_RD(tx_status) == 0) {
+			no_queue_space = 1;
+			break;
+		}
+
+		qh = list_entry(hcd->non_periodic_qh_ptr, struct dwc_qh,
+				qh_list_entry);
+		status = queue_transaction(hcd, qh->channel,
+					   DWC_GNPTXSTS_NPTXFSPCAVAIL_RD
+					   (tx_status));
+
+		if (status > 0) {
+			more_to_do = 1;
+		} else if (status < 0) {
+			no_fifo_space = 1;
+			break;
+		}
+
+		/* Advance to next QH, skipping start-of-list entry. */
+		hcd->non_periodic_qh_ptr = hcd->non_periodic_qh_ptr->next;
+		if (hcd->non_periodic_qh_ptr == &hcd->non_periodic_sched_active)
+			hcd->non_periodic_qh_ptr =
+			    hcd->non_periodic_qh_ptr->next;
+	} while (hcd->non_periodic_qh_ptr != orig_qh_ptr);
+
+	if (!hcd->core_if->dma_enable) {
+		u32 intr_mask = 0;
+
+		intr_mask |= DWC_INTMSK_NP_TXFIFO_EMPT;
+		if (more_to_do || no_queue_space || no_fifo_space) {
+			/*
+			 * May need to queue more transactions as the request
+			 * queue or Tx FIFO empties. Enable the non-periodic
+			 * Tx FIFO empty interrupt. (Always use the half-empty
+			 * level to ensure that new requests are loaded as
+			 * soon as possible.)
+			 */
+			dwc_modify32(gintmsk_reg(hcd), 0, intr_mask);
+		} else {
+			/*
+			 * Disable the Tx FIFO empty interrupt since there are
+			 * no more transactions that need to be queued right
+			 * now. This function is called from interrupt
+			 * handlers to queue more transactions as transfer
+			 * states change.
+			 */
+			dwc_modify32(gintmsk_reg(hcd), intr_mask, 0);
+		}
+	}
+}
+
+/**
+ * Processes periodic channels for the next frame and queues transactions for
+ * these channels to the DWC_otg controller. After queueing transactions, the
+ * Periodic Tx FIFO Empty interrupt is enabled if there are more transactions
+ * to queue as Periodic Tx FIFO or request queue space becomes available.
+ * Otherwise, the Periodic Tx FIFO Empty interrupt is disabled.
+ */
+static void process_periodic_channels(struct dwc_hcd *hcd)
+{
+	u32 tx_status = 0;
+	struct list_head *qh_ptr;
+	struct dwc_qh *qh;
+	int status;
+	int no_queue_space = 0;
+	int no_fifo_space = 0;
+	u32 host_regs;
+
+	host_regs = (u32) hcd->core_if->host_if->host_global_regs;
+
+	qh_ptr = hcd->periodic_sched_assigned.next;
+	while (qh_ptr != &hcd->periodic_sched_assigned) {
+		tx_status = dwc_read32(host_regs + DWC_HPTXSTS);
+		if (DWC_HPTXSTS_PTXSPC_AVAIL_RD(tx_status) == 0) {
+			no_queue_space = 1;
+			break;
+		}
+
+		qh = list_entry(qh_ptr, struct dwc_qh, qh_list_entry);
+
+		/*
+		 * Set a flag if we're queuing high-bandwidth in slave mode.
+		 * The flag prevents any halts to get into the request queue in
+		 * the middle of multiple high-bandwidth packets getting queued.
+		 */
+		if (!hcd->core_if->dma_enable && qh->channel->multi_count > 1)
+			hcd->core_if->queuing_high_bandwidth = 1;
+
+		status = queue_transaction(hcd, qh->channel,
+					   DWC_HPTXSTS_PTXFSPC_AVAIL_RD
+					   (tx_status));
+		if (status < 0) {
+			no_fifo_space = 1;
+			break;
+		}
+
+		/*
+		 * In Slave mode, stay on the current transfer until there is
+		 * nothing more to do or the high-bandwidth request count is
+		 * reached. In DMA mode, only need to queue one request. The
+		 * controller automatically handles multiple packets for
+		 * high-bandwidth transfers.
+		 */
+		if (hcd->core_if->dma_enable || (status == 0 ||
+						 qh->channel->requests ==
+						 qh->channel->multi_count)) {
+			qh_ptr = qh_ptr->next;
+
+			/*
+			 * Move the QH from the periodic assigned schedule to
+			 * the periodic queued schedule.
+			 */
+			list_move(&qh->qh_list_entry,
+				  &hcd->periodic_sched_queued);
+
+			/* done queuing high bandwidth */
+			hcd->core_if->queuing_high_bandwidth = 0;
+		}
+	}
+
+	if (!hcd->core_if->dma_enable) {
+		u32 intr_mask = 0;
+
+		intr_mask |= DWC_INTMSK_NP_TXFIFO_EMPT;
+
+		if (!list_empty(&hcd->periodic_sched_assigned) ||
+		    no_queue_space || no_fifo_space)
+			/*
+			 * May need to queue more transactions as the request
+			 * queue or Tx FIFO empties. Enable the periodic Tx
+			 * FIFO empty interrupt. (Always use the half-empty
+			 * level to ensure that new requests are loaded as
+			 * soon as possible.)
+			 */
+			dwc_modify32(gintmsk_reg(hcd), 0, intr_mask);
+		else
+			/*
+			 * Disable the Tx FIFO empty interrupt since there are
+			 * no more transactions that need to be queued right
+			 * now. This function is called from interrupt
+			 * handlers to queue more transactions as transfer
+			 * states change.
+			 */
+			dwc_modify32(gintmsk_reg(hcd), intr_mask, 0);
+	}
+}
+
+/**
+ * This function processes the currently active host channels and queues
+ * transactions for these channels to the DWC_otg controller. It is called
+ * from HCD interrupt handler functions.
+ */
+void dwc_otg_hcd_queue_transactions(struct dwc_hcd *hcd,
+				    enum dwc_transaction_type tr_type)
+{
+	/* Process host channels associated with periodic transfers. */
+	if ((tr_type == DWC_OTG_TRANSACTION_PERIODIC ||
+	     tr_type == DWC_OTG_TRANSACTION_ALL) &&
+	    !list_empty(&hcd->periodic_sched_assigned))
+		process_periodic_channels(hcd);
+
+	/* Process host channels associated with non-periodic transfers. */
+	if (tr_type == DWC_OTG_TRANSACTION_NON_PERIODIC ||
+	    tr_type == DWC_OTG_TRANSACTION_ALL) {
+		if (!list_empty(&hcd->non_periodic_sched_active)) {
+			process_non_periodic_channels(hcd);
+		} else {
+			/*
+			 * Ensure NP Tx FIFO empty interrupt is disabled when
+			 * there are no non-periodic transfers to process.
+			 */
+			u32 gintmsk = 0;
+			gintmsk |= DWC_INTMSK_NP_TXFIFO_EMPT;
+			dwc_modify32(gintmsk_reg(hcd), gintmsk, 0);
+		}
+	}
+}
+
+/**
+ * Sets the final status of an URB and returns it to the device driver. Any
+ * required cleanup of the URB is performed.
+ */
+void dwc_otg_hcd_complete_urb(struct dwc_hcd *hcd, struct urb *urb, int status)
+__releases(hcd->lock) __acquires(hcd->lock)
+{
+	urb->hcpriv = NULL;
+	usb_hcd_unlink_urb_from_ep(dwc_otg_hcd_to_hcd(hcd), urb);
+
+	spin_unlock(&hcd->lock);
+	usb_hcd_giveback_urb(dwc_otg_hcd_to_hcd(hcd), urb, status);
+	spin_lock(&hcd->lock);
+}
diff --git a/drivers/usb/dwc_otg/dwc_otg_hcd.h b/drivers/usb/dwc_otg/dwc_otg_hcd.h
new file mode 100644
index 0000000..13108eb
--- /dev/null
+++ b/drivers/usb/dwc_otg/dwc_otg_hcd.h
@@ -0,0 +1,416 @@
+/*
+ * DesignWare HS OTG controller driver
+ * Copyright (C) 2006 Synopsys, Inc.
+ * Portions Copyright (C) 2010 Applied Micro Circuits Corporation.
+ *
+ * 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 version 2 for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see http://www.gnu.org/licenses
+ * or write to the Free Software Foundation, Inc., 51 Franklin Street,
+ * Suite 500, Boston, MA 02110-1335 USA.
+ *
+ * Based on Synopsys driver version 2.60a
+ * Modified by Mark Miesfeld <mmiesfeld@apm.com>
+ * Modified by Stefan Roese <sr@denx.de>, DENX Software Engineering
+ * Modified by Chuck Meade <chuck@theptrgroup.com>
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING BUT NOT LIMITED TO THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL SYNOPSYS, INC. BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+#if !defined(__DWC_HCD_H__)
+#define __DWC_HCD_H__
+
+#include <linux/usb.h>
+#include <linux/usb/hcd.h>
+
+#include "dwc_otg_driver.h"
+
+/*
+ * This file contains the structures, constants, and interfaces for
+ * the Host Contoller Driver (HCD).
+ *
+ * The Host Controller Driver (HCD) is responsible for translating requests
+ * from the USB Driver into the appropriate actions on the DWC_otg controller.
+ * It isolates the USBD from the specifics of the controller by providing an
+ * API to the USBD.
+ */
+
+/* Phases for control transfers. */
+enum dwc_control_phase {
+	DWC_OTG_CONTROL_SETUP,
+	DWC_OTG_CONTROL_DATA,
+	DWC_OTG_CONTROL_STATUS
+};
+
+/* Transaction types. */
+enum dwc_transaction_type {
+	DWC_OTG_TRANSACTION_NONE,
+	DWC_OTG_TRANSACTION_PERIODIC,
+	DWC_OTG_TRANSACTION_NON_PERIODIC,
+	DWC_OTG_TRANSACTION_ALL
+};
+
+/*
+ * A Queue Transfer Descriptor (QTD) holds the state of a bulk, control,
+ * interrupt, or isochronous transfer. A single QTD is created for each URB
+ * (of one of these types) submitted to the HCD. The transfer associated with
+ * a QTD may require one or multiple transactions.
+ *
+ * A QTD is linked to a Queue Head, which is entered in either the
+ * non-periodic or periodic schedule for execution. When a QTD is chosen for
+ * execution, some or all of its transactions may be executed. After
+ * execution, the state of the QTD is updated. The QTD may be retired if all
+ * its transactions are complete or if an error occurred. Otherwise, it
+ * remains in the schedule so more transactions can be executed later.
+ */
+struct dwc_qtd {
+	/*
+	 * Determines the PID of the next data packet for the data phase of
+	 * control transfers. Ignored for other transfer types.
+	 * One of the following values:
+	 *      - DWC_OTG_HC_PID_DATA0
+	 *      - DWC_OTG_HC_PID_DATA1
+	 */
+	u8 data_toggle;
+
+	/* Current phase for control transfers (Setup, Data, or Status). */
+	enum dwc_control_phase control_phase;
+
+	/*
+	 * Keep track of the current split type
+	 * for FS/LS endpoints on a HS Hub
+	 */
+	u8 complete_split;
+
+	/* How many bytes transferred during SSPLIT OUT */
+	u32 ssplit_out_xfer_count;
+
+	/*
+	 * Holds the number of bus errors that have occurred for a transaction
+	 * within this transfer.
+	 */
+	u8 error_count;
+
+	/*
+	 * Index of the next frame descriptor for an isochronous transfer. A
+	 * frame descriptor describes the buffer position and length of the
+	 * data to be transferred in the next scheduled (micro)frame of an
+	 * isochronous transfer. It also holds status for that transaction.
+	 * The frame index starts at 0.
+	 */
+	int isoc_frame_index;
+
+	/* Position of the ISOC split on full/low speed */
+	u8 isoc_split_pos;
+
+	/* Position of the ISOC split in the buffer for the current frame */
+	u16 isoc_split_offset;
+
+	/* URB for this transfer */
+	struct urb *urb;
+
+	/* This list of QTDs */
+	struct list_head qtd_list_entry;
+
+	/* Field to track the qh pointer */
+	struct dwc_qh *qtd_qh_ptr;
+};
+
+/*
+ * A Queue Head (QH) holds the static characteristics of an endpoint and
+ * maintains a list of transfers (QTDs) for that endpoint. A QH structure may
+ * be entered in either the non-periodic or periodic schedule.
+ */
+struct dwc_qh {
+	/*
+	 * Endpoint type.
+	 * One of the following values:
+	 *      - USB_ENDPOINT_XFER_CONTROL
+	 *      - USB_ENDPOINT_XFER_ISOC
+	 *      - USB_ENDPOINT_XFER_BULK
+	 *      - USB_ENDPOINT_XFER_INT
+	 */
+	u8 ep_type;
+	u8 ep_is_in;
+
+	/* wMaxPacketSize Field of Endpoint Descriptor. */
+	u16 maxp;
+
+	/*
+	 * Determines the PID of the next data packet for non-control
+	 * transfers. Ignored for control transfers.
+	 * One of the following values:
+	 *      - DWC_OTG_HC_PID_DATA0
+	 *      - DWC_OTG_HC_PID_DATA1
+	 */
+	u8 data_toggle;
+
+	/* Ping state if 1. */
+	u8 ping_state;
+
+	/* List of QTDs for this QH. */
+	struct list_head qtd_list;
+
+	/* Host channel currently processing transfers for this QH. */
+	struct dwc_hc *channel;
+
+	/* QTD currently assigned to a host channel for this QH. */
+	struct dwc_qtd *qtd_in_process;
+
+	/* Full/low speed endpoint on high-speed hub requires split. */
+	u8 do_split;
+
+	/* Periodic schedule information */
+
+	/* Bandwidth in microseconds per (micro)frame. */
+	u8 usecs;
+
+	/* Interval between transfers in (micro)frames. */
+	u16 interval;
+
+	/*
+	 * (micro)frame to initialize a periodic transfer. The transfer
+	 * executes in the following (micro)frame.
+	 */
+	u16 sched_frame;
+
+	/* (micro)frame at which last start split was initialized. */
+	u16 start_split_frame;
+
+	u16 speed;
+	u16 frame_usecs[8];
+
+	/* Entry for QH in either the periodic or non-periodic schedule. */
+	struct list_head qh_list_entry;
+};
+
+/* Gets the struct usb_hcd that contains a struct dwc_hcd. */
+static inline struct usb_hcd *dwc_otg_hcd_to_hcd(struct dwc_hcd *dwc_hcd)
+{
+	return container_of((void *)dwc_hcd, struct usb_hcd, hcd_priv);
+}
+
+/* HCD Create/Destroy Functions */
+extern int __init dwc_otg_hcd_init(struct device *_dev,
+				   struct dwc_otg_device *dwc_dev);
+extern void dwc_otg_hcd_remove(struct device *_dev);
+
+/*
+ * The following functions support managing the DWC_otg controller in host
+ * mode.
+ */
+extern int dwc_otg_hcd_get_frame_number(struct usb_hcd *hcd);
+extern void dwc_otg_hc_cleanup(struct core_if *core_if, struct dwc_hc *hc);
+extern void dwc_otg_hc_halt(struct core_if *core_if, struct dwc_hc *hc,
+			    enum dwc_halt_status _halt_status);
+
+/* Transaction Execution Functions */
+extern enum dwc_transaction_type dwc_otg_hcd_select_transactions(struct dwc_hcd
+								 *hcd);
+extern void dwc_otg_hcd_queue_transactions(struct dwc_hcd *hcd,
+					   enum dwc_transaction_type tr_type);
+extern void dwc_otg_hcd_complete_urb(struct dwc_hcd *_hcd, struct urb *urb,
+				     int status);
+
+/* Interrupt Handler Functions */
+extern int dwc_otg_hcd_handle_intr(struct dwc_hcd *hcd);
+
+/* Schedule Queue Functions */
+extern int init_hcd_usecs(struct dwc_hcd *hcd);
+extern void dwc_otg_hcd_qh_free(struct dwc_qh *qh);
+extern void dwc_otg_hcd_qh_remove(struct dwc_hcd *hcd, struct dwc_qh *qh);
+extern void dwc_otg_hcd_qh_deactivate(struct dwc_hcd *hcd, struct dwc_qh *qh,
+				      int sched_csplit);
+extern int dwc_otg_hcd_qh_deferr(struct dwc_hcd *hcd, struct dwc_qh *qh,
+				 int delay);
+extern struct dwc_qtd *dwc_otg_hcd_qtd_create(struct urb *urb,
+					      gfp_t _mem_flags);
+extern int dwc_otg_hcd_qtd_add(struct dwc_qtd *qtd, struct dwc_hcd *dwc_hcd);
+
+/*
+ * Frees the memory for a QTD structure.  QTD should already be removed from
+ * list.
+ */
+static inline void dwc_otg_hcd_qtd_free(struct dwc_qtd *_qtd)
+{
+	kfree(_qtd);
+}
+
+/* Removes a QTD from list. */
+static inline void dwc_otg_hcd_qtd_remove(struct dwc_qtd *_qtd)
+{
+	list_del(&_qtd->qtd_list_entry);
+}
+
+/* Remove and free a QTD */
+static inline void dwc_otg_hcd_qtd_remove_and_free(struct dwc_qtd *_qtd)
+{
+	dwc_otg_hcd_qtd_remove(_qtd);
+	dwc_otg_hcd_qtd_free(_qtd);
+}
+
+struct dwc_qh *dwc_urb_to_qh(struct urb *_urb);
+
+/* Gets the usb_host_endpoint associated with an URB. */
+static inline struct usb_host_endpoint *dwc_urb_to_endpoint(struct urb *_urb)
+{
+	struct usb_device *dev = _urb->dev;
+	int ep_num = usb_pipeendpoint(_urb->pipe);
+
+	if (usb_pipein(_urb->pipe))
+		return dev->ep_in[ep_num];
+	else
+		return dev->ep_out[ep_num];
+}
+
+/*
+ * Gets the endpoint number from a _bEndpointAddress argument. The endpoint is
+ * qualified with its direction (possible 32 endpoints per device).
+ */
+#define dwc_ep_addr_to_endpoint(_bEndpointAddress_) \
+		((_bEndpointAddress_ & USB_ENDPOINT_NUMBER_MASK) | \
+		((_bEndpointAddress_ & USB_DIR_IN) != 0) << 4)
+
+/* Gets the QH that contains the list_head */
+#define dwc_list_to_qh(_list_head_ptr_) \
+		(container_of(_list_head_ptr_, struct dwc_qh, qh_list_entry))
+
+/* Gets the QTD that contains the list_head */
+#define dwc_list_to_qtd(_list_head_ptr_) \
+		(container_of(_list_head_ptr_, struct dwc_qtd, qtd_list_entry))
+
+/* Check if QH is non-periodic  */
+#define dwc_qh_is_non_per(_qh_ptr_) \
+		((_qh_ptr_->ep_type == USB_ENDPOINT_XFER_BULK) || \
+		(_qh_ptr_->ep_type == USB_ENDPOINT_XFER_CONTROL))
+
+/* High bandwidth multiplier as encoded in highspeed endpoint descriptors */
+#define dwc_hb_mult(wMaxPacketSize)	(1 + (((wMaxPacketSize) >> 11) & 0x03))
+
+/* Packet size for any kind of endpoint descriptor */
+#define dwc_max_packet(wMaxPacketSize)	((wMaxPacketSize) & 0x07ff)
+
+/*
+ * Returns true if _frame1 is less than or equal to _frame2. The comparison is
+ * done modulo DWC_HFNUM_MAX_FRNUM. This accounts for the rollover of the
+ * frame number when the max frame number is reached.
+ */
+static inline int dwc_frame_num_le(u16 _frame1, u16 _frame2)
+{
+	return ((_frame2 - _frame1) & DWC_HFNUM_MAX_FRNUM) <=
+	    (DWC_HFNUM_MAX_FRNUM >> 1);
+}
+
+/*
+ * Returns true if _frame1 is greater than _frame2. The comparison is done
+ * modulo DWC_HFNUM_MAX_FRNUM. This accounts for the rollover of the frame
+ * number when the max frame number is reached.
+ */
+static inline int dwc_frame_num_gt(u16 _frame1, u16 _frame2)
+{
+	return (_frame1 != _frame2) &&
+	    (((_frame1 - _frame2) &
+	      DWC_HFNUM_MAX_FRNUM) < (DWC_HFNUM_MAX_FRNUM >> 1));
+}
+
+/*
+ * Increments _frame by the amount specified by _inc. The addition is done
+ * modulo DWC_HFNUM_MAX_FRNUM. Returns the incremented value.
+ */
+static inline u16 dwc_frame_num_inc(u16 _frame, u16 _inc)
+{
+	return (_frame + _inc) & DWC_HFNUM_MAX_FRNUM;
+}
+
+static inline u16 dwc_full_frame_num(u16 _frame)
+{
+	return ((_frame) & DWC_HFNUM_MAX_FRNUM) >> 3;
+}
+
+static inline u16 dwc_micro_frame_num(u16 _frame)
+{
+	return (_frame) & 0x7;
+}
+
+static inline u32 gintsts_reg(struct dwc_hcd *hcd)
+{
+	u32 global_regs = (u32) hcd->core_if->core_global_regs;
+	return global_regs + DWC_GINTSTS;
+}
+
+static inline u32 gintmsk_reg(struct dwc_hcd *hcd)
+{
+	u32 global_regs = (u32) hcd->core_if->core_global_regs;
+	return global_regs + DWC_GINTMSK;
+}
+
+static inline u32 gahbcfg_reg(struct dwc_hcd *hcd)
+{
+	u32 global_regs = (u32) hcd->core_if->core_global_regs;
+	return global_regs + DWC_GAHBCFG;
+}
+
+static inline const char *pipetype_str(unsigned int pipe)
+{
+	switch (usb_pipetype(pipe)) {
+	case PIPE_CONTROL:
+		return "control";
+	case PIPE_BULK:
+		return "bulk";
+	case PIPE_INTERRUPT:
+		return "interrupt";
+	case PIPE_ISOCHRONOUS:
+		return "isochronous";
+	default:
+		return "unknown";
+	}
+}
+
+static inline const char *dev_speed_str(enum usb_device_speed speed)
+{
+	switch (speed) {
+	case USB_SPEED_HIGH:
+		return "high";
+	case USB_SPEED_FULL:
+		return "full";
+	case USB_SPEED_LOW:
+		return "low";
+	default:
+		return "unknown";
+	}
+}
+
+static inline const char *ep_type_str(u8 type)
+{
+	switch (type) {
+	case USB_ENDPOINT_XFER_ISOC:
+		return "isochronous";
+	case USB_ENDPOINT_XFER_INT:
+		return "interrupt";
+	case USB_ENDPOINT_XFER_CONTROL:
+		return "control";
+	case USB_ENDPOINT_XFER_BULK:
+		return "bulk";
+	default:
+		return "?";
+	}
+}
+#endif
-- 
1.6.1.rc3

^ permalink raw reply related

* [PATCH V7 02/10] USB/ppc4xx: Add Synopsys DWC OTG driver framework
From: tmarri @ 2011-01-19  2:05 UTC (permalink / raw)
  To: linux-usb, linuxppc-dev; +Cc: tmarri, greg, Mark Miesfeld, Fushen Chen

From: Tirumala Marri <tmarri@apm.com>

Platform probing is in dwc_otg_apmppc.c.
Driver parameter and parameter checking are in dwc_otg_param.c.

Signed-off-by: Tirumala R Marri <tmarri@apm.com>
Signed-off-by: Fushen Chen <fchen@apm.com>
Signed-off-by: Mark Miesfeld <mmiesfeld@apm.com>
---
 drivers/usb/dwc_otg/dwc_otg_apmppc.c |  414 ++++++++++++++++++++++++++++++++++
 drivers/usb/dwc_otg/dwc_otg_driver.h |   76 ++++++
 drivers/usb/dwc_otg/dwc_otg_param.c  |  180 +++++++++++++++
 3 files changed, 670 insertions(+), 0 deletions(-)

diff --git a/drivers/usb/dwc_otg/dwc_otg_apmppc.c b/drivers/usb/dwc_otg/dwc_otg_apmppc.c
new file mode 100644
index 0000000..d925548
--- /dev/null
+++ b/drivers/usb/dwc_otg/dwc_otg_apmppc.c
@@ -0,0 +1,414 @@
+/*
+ * DesignWare HS OTG controller driver
+ * Copyright (C) 2006 Synopsys, Inc.
+ * Portions Copyright (C) 2010 Applied Micro Circuits Corporation.
+ *
+ * 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 version 2 for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see http://www.gnu.org/licenses
+ * or write to the Free Software Foundation, Inc., 51 Franklin Street,
+ * Suite 500, Boston, MA 02110-1335 USA.
+ *
+ * Based on Synopsys driver version 2.60a
+ * Modified by Mark Miesfeld <mmiesfeld@apm.com>
+ * Modified by Stefan Roese <sr@denx.de>, DENX Software Engineering
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING BUT NOT LIMITED TO THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL SYNOPSYS, INC. BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+/*
+ * The dwc_otg module provides the initialization and cleanup entry
+ * points for the dwcotg driver. This module will be dynamically installed
+ * after Linux is booted using the insmod command. When the module is
+ * installed, the dwc_otg_driver_init function is called. When the module is
+ * removed (using rmmod), the dwc_otg_driver_cleanup function is called.
+ *
+ * This module also defines a data structure for the dwc_otg driver, which is
+ * used in conjunction with the standard device structure. These
+ * structures allow the OTG driver to comply with the standard Linux driver
+ * model in which devices and drivers are registered with a bus driver. This
+ * has the benefit that Linux can expose attributes of the driver and device
+ * in its special sysfs file system. Users can then read or write files in
+ * this file system to perform diagnostics on the driver components or the
+ * device.
+ */
+
+#include <linux/of_platform.h>
+
+#include "dwc_otg_driver.h"
+
+#define DWC_DRIVER_VERSION		"1.05"
+#define DWC_DRIVER_DESC			"HS OTG USB Controller driver"
+static const char dwc_driver_name[] = "dwc_otg";
+
+/**
+ * This function is the top level interrupt handler for the Common
+ * (Device and host modes) interrupts.
+ */
+static irqreturn_t dwc_otg_common_irq(int _irq, void *dev)
+{
+	struct dwc_otg_device *dwc_dev = dev;
+	int retval;
+
+	retval = dwc_otg_handle_common_intr(dwc_dev->core_if);
+	return IRQ_RETVAL(retval);
+}
+
+/**
+ * This function is the interrupt handler for the OverCurrent condition
+ * from the external charge pump (if enabled)
+ */
+static irqreturn_t dwc_otg_externalchgpump_irq(int _irq, void *dev)
+{
+	struct dwc_otg_device *dwc_dev = dev;
+
+	if (dwc_otg_is_host_mode(dwc_dev->core_if)) {
+		struct dwc_hcd *dwc_hcd;
+		u32 hprt0 = 0;
+
+		dwc_hcd = dwc_dev->hcd;
+		spin_lock(&dwc_hcd->lock);
+		dwc_hcd->flags.b.port_over_current_change = 1;
+
+		hprt0 = DWC_HPRT0_PRT_PWR_RW(hprt0, 0);
+		dwc_write32(dwc_dev->core_if->host_if->hprt0, hprt0);
+		spin_unlock(&dwc_hcd->lock);
+	} else {
+		/* Device mode - This int is n/a for device mode */
+		dev_dbg(dev, "DeviceMode: OTG OverCurrent Detected\n");
+	}
+
+	return IRQ_HANDLED;
+}
+
+/**
+ * This function is called when a device is unregistered with the
+ * dwc_otg_driver. This happens, for example, when the rmmod command is
+ * executed. The device may or may not be electrically present. If it is
+ * present, the driver stops device processing. Any resources used on behalf
+ * of this device are freed.
+ */
+static int __devexit dwc_otg_driver_remove(struct platform_device *ofdev)
+{
+	struct device *dev = &ofdev->dev;
+	struct dwc_otg_device *dwc_dev = dev_get_drvdata(dev);
+
+	/* Memory allocation for dwc_otg_device may have failed. */
+	if (!dwc_dev)
+		return 0;
+
+	/* Free the IRQ */
+	if (dwc_dev->common_irq_installed)
+		free_irq(dwc_dev->irq, dwc_dev);
+
+	if (!dwc_has_feature(dwc_dev->core_if, DWC_DEVICE_ONLY)) {
+		if (dwc_dev->hcd) {
+			if (dwc_dev->hcd->cp_irq_installed)
+				free_irq(dwc_dev->hcd->cp_irq, dwc_dev);
+			dwc_otg_hcd_remove(dev);
+		}
+	}
+
+	if (!dwc_has_feature(dwc_dev->core_if, DWC_HOST_ONLY)) {
+		if (dwc_dev->pcd)
+			dwc_otg_pcd_remove(dev);
+	}
+
+	if (dwc_dev->core_if)
+		dwc_otg_cil_remove(dwc_dev->core_if);
+
+	/* Return the memory. */
+	if (dwc_dev->base)
+		iounmap(dwc_dev->base);
+
+	if (dwc_dev->phys_addr)
+		release_mem_region(dwc_dev->phys_addr, dwc_dev->base_len);
+
+	if (dwc_dev->core_if->xceiv) {
+		otg_put_transceiver(dwc_dev->core_if->xceiv);
+		dwc_dev->core_if->xceiv = NULL;
+		usb_nop_xceiv_unregister();
+	}
+
+	kfree(dwc_dev);
+
+	/* Clear the drvdata pointer. */
+	dev_set_drvdata(dev, NULL);
+	return 0;
+}
+
+/**
+ * This function is called when an device is bound to a
+ * dwc_otg_driver. It creates the driver components required to
+ * control the device (CIL, HCD, and PCD) and it initializes the
+ * device. The driver components are stored in a dwc_otg_device
+ * structure. A reference to the dwc_otg_device is saved in the
+ * device. This allows the driver to access the dwc_otg_device
+ * structure on subsequent calls to driver methods for this device.
+ */
+static int __devinit dwc_otg_driver_probe(struct platform_device *ofdev,
+					  const struct of_device_id *match)
+{
+	int retval;
+	struct dwc_otg_device *dwc_dev;
+	struct device *dev = &ofdev->dev;
+	struct resource res;
+	u32 gusbcfg_addr;
+	u32 usbcfg = 0;
+
+	dev_dbg(dev, "dwc_otg_driver_probe(%p)\n", dev);
+
+	dwc_dev = kzalloc(sizeof(*dwc_dev), GFP_KERNEL);
+	if (!dwc_dev) {
+		dev_err(dev, "kmalloc of dwc_otg_device failed\n");
+		retval = -ENOMEM;
+		goto fail_dwc_dev;
+	}
+
+	/* Retrieve the memory and IRQ resources. */
+	dwc_dev->irq = irq_of_parse_and_map(ofdev->dev.of_node, 0);
+	if (dwc_dev->irq == NO_IRQ) {
+		dev_err(dev, "no device irq\n");
+		retval = -ENODEV;
+		goto fail_of_irq;
+	}
+	dev_dbg(dev, "OTG - device irq: %d\n", dwc_dev->irq);
+
+	if (of_address_to_resource(ofdev->dev.of_node, 0, &res)) {
+		dev_err(dev, "%s: Can't get USB-OTG register address\n",
+			__func__);
+		retval = -ENOMEM;
+		goto fail_of_irq;
+	}
+	dev_dbg(dev, "OTG - ioresource_mem start0x%llx: end:0x%llx\n",
+		(unsigned long long)res.start, (unsigned long long)res.end);
+
+	dwc_dev->phys_addr = res.start;
+	dwc_dev->base_len = res.end - res.start + 1;
+	if (!request_mem_region(dwc_dev->phys_addr,
+				dwc_dev->base_len, dwc_driver_name)) {
+		dev_err(dev, "request_mem_region failed\n");
+		retval = -EBUSY;
+		goto fail_of_irq;
+	}
+
+	/* Map the DWC_otg Core memory into virtual address space. */
+	dwc_dev->base = ioremap(dwc_dev->phys_addr, dwc_dev->base_len);
+	if (!dwc_dev->base) {
+		dev_err(dev, "ioremap() failed\n");
+		retval = -ENOMEM;
+		goto fail_ioremap;
+	}
+	dev_dbg(dev, "mapped base=0x%08x\n", (__force u32)dwc_dev->base);
+
+	/*
+	 * Initialize driver data to point to the global DWC_otg
+	 * Device structure.
+	 */
+	dev_set_drvdata(dev, dwc_dev);
+
+	dwc_dev->core_if =
+	    dwc_otg_cil_init(dwc_dev->base, &dwc_otg_module_params);
+	if (!dwc_dev->core_if) {
+		dev_err(dev, "CIL initialization failed!\n");
+		retval = -ENOMEM;
+		goto fail_cil_init;
+	}
+
+	/*
+	 * Validate parameter values after dwc_otg_cil_init.
+	 */
+	if (check_parameters(dwc_dev->core_if)) {
+		retval = -EINVAL;
+		goto fail_check_param;
+	}
+
+	usb_nop_xceiv_register();
+	dwc_dev->core_if->xceiv = otg_get_transceiver();
+	if (!dwc_dev->core_if->xceiv) {
+		retval = -ENODEV;
+		goto fail_xceiv;
+	}
+	dwc_set_feature(dwc_dev->core_if);
+
+	/* Initialize the DWC_otg core. */
+	dwc_otg_core_init(dwc_dev->core_if);
+
+	/*
+	 * Disable the global interrupt until all the interrupt
+	 * handlers are installed.
+	 */
+	dwc_otg_disable_global_interrupts(dwc_dev->core_if);
+
+	/*
+	 * Install the interrupt handler for the common interrupts before
+	 * enabling common interrupts in core_init below.
+	 */
+	retval = request_irq(dwc_dev->irq, dwc_otg_common_irq,
+			     IRQF_SHARED, "dwc_otg", dwc_dev);
+	if (retval) {
+		dev_err(dev, "request of irq%d failed retval: %d\n",
+			dwc_dev->irq, retval);
+		retval = -EBUSY;
+		goto fail_req_irq;
+	} else {
+		dwc_dev->common_irq_installed = 1;
+	}
+
+	if (!dwc_has_feature(dwc_dev->core_if, DWC_HOST_ONLY)) {
+		/* Initialize the PCD */
+		retval = dwc_otg_pcd_init(dev);
+		if (retval) {
+			dev_err(dev, "dwc_otg_pcd_init failed\n");
+			dwc_dev->pcd = NULL;
+			goto fail_req_irq;
+		}
+	}
+
+	gusbcfg_addr = (u32) (dwc_dev->core_if->core_global_regs) + DWC_GUSBCFG;
+	if (!dwc_has_feature(dwc_dev->core_if, DWC_DEVICE_ONLY)) {
+		/* Initialize the HCD and force_host_mode */
+		usbcfg = dwc_read32(gusbcfg_addr);
+		usbcfg |= DWC_USBCFG_FRC_HST_MODE;
+		dwc_write32(gusbcfg_addr, usbcfg);
+
+		retval = dwc_otg_hcd_init(dev, dwc_dev);
+		if (retval) {
+			dev_err(dev, "dwc_otg_hcd_init failed\n");
+			dwc_dev->hcd = NULL;
+			goto fail_hcd;
+		}
+		/* configure chargepump interrupt */
+		dwc_dev->hcd->cp_irq = irq_of_parse_and_map(ofdev->dev.of_node,
+							    3);
+		if (dwc_dev->hcd->cp_irq) {
+			retval = request_irq(dwc_dev->hcd->cp_irq,
+					     dwc_otg_externalchgpump_irq,
+					     IRQF_SHARED,
+					     "dwc_otg_ext_chg_pump", dwc_dev);
+			if (retval) {
+				dev_err(dev,
+					"request of irq failed retval: %d\n",
+					retval);
+				retval = -EBUSY;
+				goto fail_hcd;
+			} else {
+				dev_dbg(dev, "%s: ExtChgPump Detection "
+					"IRQ registered\n", dwc_driver_name);
+				dwc_dev->hcd->cp_irq_installed = 1;
+			}
+		}
+	}
+	/*
+	 * Enable the global interrupt after all the interrupt
+	 * handlers are installed.
+	 */
+	dwc_otg_enable_global_interrupts(dwc_dev->core_if);
+
+	usbcfg = dwc_read32(gusbcfg_addr);
+	usbcfg &= ~DWC_USBCFG_FRC_HST_MODE;
+	dwc_write32(gusbcfg_addr, usbcfg);
+
+	return 0;
+fail_hcd:
+	free_irq(dwc_dev->irq, dwc_dev);
+	if (!dwc_has_feature(dwc_dev->core_if, DWC_HOST_ONLY)) {
+		if (dwc_dev->pcd)
+			dwc_otg_pcd_remove(dev);
+	}
+fail_req_irq:
+	otg_put_transceiver(dwc_dev->core_if->xceiv);
+fail_xceiv:
+	usb_nop_xceiv_unregister();
+fail_check_param:
+	dwc_otg_cil_remove(dwc_dev->core_if);
+fail_cil_init:
+	dev_set_drvdata(dev, NULL);
+	iounmap(dwc_dev->base);
+fail_ioremap:
+	release_mem_region(dwc_dev->phys_addr, dwc_dev->base_len);
+fail_of_irq:
+	kfree(dwc_dev);
+fail_dwc_dev:
+	return retval;
+}
+
+/*
+ * This structure defines the methods to be called by a bus driver
+ * during the lifecycle of a device on that bus. Both drivers and
+ * devices are registered with a bus driver. The bus driver matches
+ * devices to drivers based on information in the device and driver
+ * structures.
+ *
+ * The probe function is called when the bus driver matches a device
+ * to this driver. The remove function is called when a device is
+ * unregistered with the bus driver.
+ */
+static const struct of_device_id dwc_otg_match[] = {
+	{.compatible = "amcc,dwc-otg",},
+	{}
+};
+
+MODULE_DEVICE_TABLE(of, dwc_otg_match);
+
+static struct of_platform_driver dwc_otg_driver = {
+	.probe = dwc_otg_driver_probe,
+	.remove = __devexit_p(dwc_otg_driver_remove),
+	.driver = {
+		   .name = "dwc_otg",
+		   .owner = THIS_MODULE,
+		   .of_match_table = dwc_otg_match,
+		   },
+};
+
+/**
+ * This function is called when the dwc_otg_driver is installed with the
+ * insmod command. It registers the dwc_otg_driver structure with the
+ * appropriate bus driver. This will cause the dwc_otg_driver_probe function
+ * to be called. In addition, the bus driver will automatically expose
+ * attributes defined for the device and driver in the special sysfs file
+ * system.
+ */
+static int __init dwc_otg_driver_init(void)
+{
+
+	pr_info("%s: version %s\n", dwc_driver_name, DWC_DRIVER_VERSION);
+	return of_register_platform_driver(&dwc_otg_driver);
+}
+
+module_init(dwc_otg_driver_init);
+
+/**
+ * This function is called when the driver is removed from the kernel
+ * with the rmmod command. The driver unregisters itself with its bus
+ * driver.
+ *
+ */
+static void __exit dwc_otg_driver_cleanup(void)
+{
+	of_unregister_platform_driver(&dwc_otg_driver);
+}
+
+module_exit(dwc_otg_driver_cleanup);
+
+MODULE_DESCRIPTION(DWC_DRIVER_DESC);
+MODULE_AUTHOR("Mark Miesfeld <mmiesfeld@apm.com");
+MODULE_LICENSE("GPL");
diff --git a/drivers/usb/dwc_otg/dwc_otg_driver.h b/drivers/usb/dwc_otg/dwc_otg_driver.h
new file mode 100644
index 0000000..e82daa3
--- /dev/null
+++ b/drivers/usb/dwc_otg/dwc_otg_driver.h
@@ -0,0 +1,76 @@
+/*
+ * DesignWare HS OTG controller driver
+ * Copyright (C) 2006 Synopsys, Inc.
+ * Portions Copyright (C) 2010 Applied Micro Circuits Corporation.
+ *
+ * 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 version 2 for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see http://www.gnu.org/licenses
+ * or write to the Free Software Foundation, Inc., 51 Franklin Street,
+ * Suite 500, Boston, MA 02110-1335 USA.
+ *
+ * Based on Synopsys driver version 2.60a
+ * Modified by Mark Miesfeld <mmiesfeld@apm.com>
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING BUT NOT LIMITED TO THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL SYNOPSYS, INC. BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+#if !defined(__DWC_OTG_DRIVER_H__)
+#define __DWC_OTG_DRIVER_H__
+
+/*
+ * This file contains the interface to the Linux driver.
+ */
+#include "dwc_otg_cil.h"
+
+/*
+ * This structure is a wrapper that encapsulates the driver components used to
+ * manage a single DWC_otg controller.
+ */
+struct dwc_otg_device {
+	/* Base address returned from ioremap() */
+	__iomem void *base;
+
+	/* Pointer to the core interface structure. */
+	struct core_if *core_if;
+
+	/* Pointer to the PCD structure. */
+	struct dwc_pcd *pcd;
+
+	/* Pointer to the HCD structure. */
+	struct dwc_hcd *hcd;
+
+	/* Flag to indicate whether the common IRQ handler is installed. */
+	u8 common_irq_installed;
+
+	/* Interrupt request number. */
+	unsigned int irq;
+
+	/*
+	 * Physical address of Control and Status registers, used by
+	 * release_mem_region().
+	 */
+	resource_size_t phys_addr;
+
+	/* Length of memory region, used by release_mem_region(). */
+	unsigned long base_len;
+};
+#endif
diff --git a/drivers/usb/dwc_otg/dwc_otg_param.c b/drivers/usb/dwc_otg/dwc_otg_param.c
new file mode 100644
index 0000000..3f6988f
--- /dev/null
+++ b/drivers/usb/dwc_otg/dwc_otg_param.c
@@ -0,0 +1,180 @@
+/*
+ * DesignWare HS OTG controller driver
+ * Copyright (C) 2006 Synopsys, Inc.
+ * Portions Copyright (C) 2010 Applied Micro Circuits Corporation.
+ *
+ * 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 version 2 for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see http://www.gnu.org/licenses
+ * or write to the Free Software Foundation, Inc., 51 Franklin Street,
+ * Suite 500, Boston, MA 02110-1335 USA.
+ *
+ * Based on Synopsys driver version 2.60a
+ * Modified by Mark Miesfeld <mmiesfeld@apm.com>
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING BUT NOT LIMITED TO THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL SYNOPSYS, INC. BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+/*
+ * This file provides dwc_otg driver parameter and parameter checking.
+ */
+
+#include "dwc_otg_cil.h"
+
+/*
+ * Encapsulate the module parameter settings
+ */
+struct core_params dwc_otg_module_params = {
+	.otg_cap = -1,
+	.dma_enable = -1,
+	.dma_burst_size = -1,
+	.speed = -1,
+	.host_support_fs_ls_low_power = -1,
+	.host_ls_low_power_phy_clk = -1,
+	.enable_dynamic_fifo = -1,
+	.dev_rx_fifo_size = -1,
+	.dev_nperio_tx_fifo_size = -1,
+	.dev_perio_tx_fifo_size = {-1, -1, -1, -1, -1, -1, -1, -1,
+		-1, -1, -1, -1, -1, -1, -1},	/* 15 */
+	.host_rx_fifo_size = -1,
+	.host_nperio_tx_fifo_size = -1,
+	.host_perio_tx_fifo_size = -1,
+	.max_transfer_size = -1,
+	.max_packet_count = -1,
+	.host_channels = -1,
+	.dev_endpoints = -1,
+	.phy_type = -1,
+	.phy_utmi_width = -1,
+	.phy_ulpi_ddr = -1,
+	.phy_ulpi_ext_vbus = -1,
+	.i2c_enable = -1,
+	.ulpi_fs_ls = -1,
+	.ts_dline = -1,
+	.en_multiple_tx_fifo = -1,
+	.dev_tx_fifo_size = {-1, -1, -1, -1, -1, -1, -1, -1, -1,
+		-1, -1, -1, -1, -1, -1},	/* 15 */
+	.thr_ctl = -1,
+	.tx_thr_length = -1,
+	.rx_thr_length = -1,
+};
+
+/**
+ * Checks that parameter settings for the periodic Tx FIFO sizes are correct
+ * according to the hardware configuration. Sets the size to the hardware
+ * configuration if an incorrect size is detected.
+ */
+static int set_valid_perio_tx_fifo_sizes(struct core_if *core_if)
+{
+	u32 regs = (u32) core_if->core_global_regs;
+	u32 *param_size = &dwc_otg_module_params.dev_perio_tx_fifo_size[0];
+	u32 i, size;
+
+	for (i = 0; i < MAX_PERIO_FIFOS; i++, param_size++) {
+		size = dwc_read32(regs + DWC_DPTX_FSIZ_DIPTXF(i));
+		*param_size = size;
+	}
+	return 0;
+}
+
+/**
+ * Checks that parameter settings for the Tx FIFO sizes are correct according to
+ * the hardware configuration.  Sets the size to the hardware configuration if
+ * an incorrect size is detected.
+ */
+static int set_valid_tx_fifo_sizes(struct core_if *core_if)
+{
+	u32 regs = (u32) core_if->core_global_regs;
+	u32 *param_size = &dwc_otg_module_params.dev_tx_fifo_size[0];
+	u32 i, size;
+
+	for (i = 0; i < MAX_TX_FIFOS; i++, param_size) {
+		size = dwc_read32(regs + DWC_DPTX_FSIZ_DIPTXF(i));
+		*param_size = size;
+	}
+	return 0;
+}
+
+/**
+ * This function is called during module intialization to verify that
+ * the module parameters are in a valid state.
+ */
+int __devinit check_parameters(struct core_if *core_if)
+{
+	/* Default values */
+	dwc_otg_module_params.otg_cap = dwc_param_otg_cap_default;
+	dwc_otg_module_params.dma_enable = dwc_param_dma_enable_default;
+	dwc_otg_module_params.speed = dwc_param_speed_default;
+	dwc_otg_module_params.host_support_fs_ls_low_power =
+	    dwc_param_host_support_fs_ls_low_power_default;
+	dwc_otg_module_params.host_ls_low_power_phy_clk =
+	    dwc_param_host_ls_low_power_phy_clk_default;
+	dwc_otg_module_params.phy_type = dwc_param_phy_type_default;
+	dwc_otg_module_params.phy_ulpi_ddr = dwc_param_phy_ulpi_ddr_default;
+	dwc_otg_module_params.phy_ulpi_ext_vbus =
+	    dwc_param_phy_ulpi_ext_vbus_default;
+	dwc_otg_module_params.i2c_enable = dwc_param_i2c_enable_default;
+	dwc_otg_module_params.ulpi_fs_ls = dwc_param_ulpi_fs_ls_default;
+	dwc_otg_module_params.ts_dline = dwc_param_ts_dline_default;
+
+	dwc_otg_module_params.dma_burst_size = dwc_param_dma_burst_size_default;
+	dwc_otg_module_params.phy_utmi_width = dwc_param_phy_utmi_width_default;
+	dwc_otg_module_params.thr_ctl = dwc_param_thr_ctl_default;
+	dwc_otg_module_params.tx_thr_length = dwc_param_tx_thr_length_default;
+	dwc_otg_module_params.rx_thr_length = dwc_param_rx_thr_length_default;
+
+	/*
+	 * Hardware configurations of the OTG core.
+	 */
+	dwc_otg_module_params.enable_dynamic_fifo =
+	    DWC_HWCFG2_DYN_FIFO_RD(core_if->hwcfg2);
+	dwc_otg_module_params.dev_rx_fifo_size =
+	    dwc_read32((u32) core_if->core_global_regs + DWC_GRXFSIZ);
+	dwc_otg_module_params.dev_nperio_tx_fifo_size =
+	    dwc_read32((u32) core_if->core_global_regs + DWC_GNPTXFSIZ) >> 16;
+
+	dwc_otg_module_params.host_rx_fifo_size =
+	    dwc_read32((u32) core_if->core_global_regs + DWC_GRXFSIZ);
+	dwc_otg_module_params.host_nperio_tx_fifo_size =
+	    dwc_read32((u32) core_if->core_global_regs + DWC_GNPTXFSIZ) >> 16;
+	dwc_otg_module_params.host_perio_tx_fifo_size =
+	    dwc_read32((u32) core_if->core_global_regs + DWC_HPTXFSIZ) >> 16;
+	dwc_otg_module_params.max_transfer_size =
+	    (1 << (DWC_HWCFG3_XFERSIZE_CTR_WIDTH_RD(core_if->hwcfg3) + 11))
+	    - 1;
+	dwc_otg_module_params.max_packet_count =
+	    (1 << (DWC_HWCFG3_PKTSIZE_CTR_WIDTH_RD(core_if->hwcfg3) + 4))
+	    - 1;
+
+	dwc_otg_module_params.host_channels =
+	    DWC_HWCFG2_NO_HST_CHAN_RD(core_if->hwcfg2) + 1;
+	dwc_otg_module_params.dev_endpoints =
+	    DWC_HWCFG2_NO_DEV_EP_RD(core_if->hwcfg2);
+	dwc_otg_module_params.en_multiple_tx_fifo =
+	    (DWC_HWCFG4_DED_FIFO_ENA_RD(core_if->hwcfg4) == 0)
+	    ? 0 : 1, 0;
+	set_valid_perio_tx_fifo_sizes(core_if);
+	set_valid_tx_fifo_sizes(core_if);
+
+	return 0;
+}
+
+module_param_named(dma_enable, dwc_otg_module_params.dma_enable, bool, 0444);
+MODULE_PARM_DESC(dma_enable, "DMA Mode 0=Slave 1=DMA enabled");
-- 
1.6.1.rc3

^ permalink raw reply related

* [PATCH V7 06/10] USB/ppc4xx: Add Synopsys DWC OTG HCD queue function
From: tmarri @ 2011-01-19  2:05 UTC (permalink / raw)
  To: linux-usb, linuxppc-dev; +Cc: tmarri, greg, Mark Miesfeld, Fushen Chen

From: Tirumala Marri <tmarri@apm.com>

Implements functions to manage Queue Heads and Queue
Transfer Descriptors of DWC USB OTG Controller.

Signed-off-by: Tirumala R Marri <tmarri@apm.com>
Signed-off-by: Fushen Chen <fchen@apm.com>
Signed-off-by: Mark Miesfeld <mmiesfeld@apm.com>
---
 drivers/usb/dwc_otg/dwc_otg_hcd_queue.c |  696 +++++++++++++++++++++++++++++++
 1 files changed, 696 insertions(+), 0 deletions(-)

diff --git a/drivers/usb/dwc_otg/dwc_otg_hcd_queue.c b/drivers/usb/dwc_otg/dwc_otg_hcd_queue.c
new file mode 100644
index 0000000..a083a54
--- /dev/null
+++ b/drivers/usb/dwc_otg/dwc_otg_hcd_queue.c
@@ -0,0 +1,696 @@
+/*
+ * DesignWare HS OTG controller driver
+ * Copyright (C) 2006 Synopsys, Inc.
+ * Portions Copyright (C) 2010 Applied Micro Circuits Corporation.
+ *
+ * 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 version 2 for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see http://www.gnu.org/licenses
+ * or write to the Free Software Foundation, Inc., 51 Franklin Street,
+ * Suite 500, Boston, MA 02110-1335 USA.
+ *
+ * Based on Synopsys driver version 2.60a
+ * Modified by Mark Miesfeld <mmiesfeld@apm.com>
+ * Modified by Stefan Roese <sr@denx.de>, DENX Software Engineering
+ * Modified by Chuck Meade <chuck@theptrgroup.com>
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING BUT NOT LIMITED TO THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL SYNOPSYS, INC. BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+/*
+ * This file contains the functions to manage Queue Heads and Queue
+ * Transfer Descriptors.
+ */
+
+#include "dwc_otg_hcd.h"
+
+static inline int is_fs_ls(enum usb_device_speed speed)
+{
+	return speed == USB_SPEED_FULL || speed == USB_SPEED_LOW;
+}
+
+/* Allocates memory for a QH structure. */
+static inline struct dwc_qh *dwc_otg_hcd_qh_alloc(void)
+{
+	return kmalloc(sizeof(struct dwc_qh), GFP_ATOMIC);
+}
+
+/**
+ * Initializes a QH structure to initialize the QH.
+ */
+#define SCHEDULE_SLOP 10
+static void dwc_otg_hcd_qh_init(struct dwc_hcd *hcd, struct dwc_qh *qh,
+				struct urb *urb)
+{
+	memset(qh, 0, sizeof(struct dwc_qh));
+
+	/* Initialize QH */
+	switch (usb_pipetype(urb->pipe)) {
+	case PIPE_CONTROL:
+		qh->ep_type = USB_ENDPOINT_XFER_CONTROL;
+		break;
+	case PIPE_BULK:
+		qh->ep_type = USB_ENDPOINT_XFER_BULK;
+		break;
+	case PIPE_ISOCHRONOUS:
+		qh->ep_type = USB_ENDPOINT_XFER_ISOC;
+		break;
+	case PIPE_INTERRUPT:
+		qh->ep_type = USB_ENDPOINT_XFER_INT;
+		break;
+	}
+
+	qh->ep_is_in = usb_pipein(urb->pipe) ? 1 : 0;
+	qh->data_toggle = DWC_OTG_HC_PID_DATA0;
+	qh->maxp = usb_maxpacket(urb->dev, urb->pipe, !(usb_pipein(urb->pipe)));
+
+	INIT_LIST_HEAD(&qh->qtd_list);
+	INIT_LIST_HEAD(&qh->qh_list_entry);
+
+	qh->channel = NULL;
+	qh->speed = urb->dev->speed;
+
+	/*
+	 * FS/LS Enpoint on HS Hub NOT virtual root hub
+	 */
+	qh->do_split = 0;
+	if (is_fs_ls(urb->dev->speed) && urb->dev->tt && urb->dev->tt->hub &&
+	    urb->dev->tt->hub->devnum != 1)
+		qh->do_split = 1;
+
+	if (qh->ep_type == USB_ENDPOINT_XFER_INT ||
+	    qh->ep_type == USB_ENDPOINT_XFER_ISOC) {
+		/* Compute scheduling parameters once and save them. */
+		u32 hprt;
+		int bytecount = dwc_hb_mult(qh->maxp) *
+		    dwc_max_packet(qh->maxp);
+
+		qh->usecs = NS_TO_US(usb_calc_bus_time(urb->dev->speed,
+						       usb_pipein(urb->pipe),
+						       (qh->ep_type ==
+							USB_ENDPOINT_XFER_ISOC),
+						       bytecount));
+
+		/* Start in a slightly future (micro)frame. */
+		qh->sched_frame = dwc_frame_num_inc(hcd->frame_number,
+						    SCHEDULE_SLOP);
+		qh->interval = urb->interval;
+
+		hprt = dwc_read32(hcd->core_if->host_if->hprt0);
+		if (DWC_HPRT0_PRT_SPD_RD(hprt) == DWC_HPRT0_PRTSPD_HIGH_SPEED &&
+		    is_fs_ls(urb->dev->speed)) {
+			qh->interval *= 8;
+			qh->sched_frame |= 0x7;
+			qh->start_split_frame = qh->sched_frame;
+		}
+	}
+}
+
+/**
+ * This function allocates and initializes a QH.
+ */
+static struct dwc_qh *dwc_otg_hcd_qh_create(struct dwc_hcd *hcd,
+					    struct urb *urb)
+{
+	struct dwc_qh *qh;
+
+	/* Allocate memory */
+	qh = dwc_otg_hcd_qh_alloc();
+	if (qh == NULL)
+		return NULL;
+
+	dwc_otg_hcd_qh_init(hcd, qh, urb);
+	return qh;
+}
+
+/**
+ * Free each QTD in the QH's QTD-list then free the QH.  QH should already be
+ * removed from a list.  QTD list should already be empty if called from URB
+ * Dequeue.
+ */
+void dwc_otg_hcd_qh_free(struct dwc_qh *qh)
+{
+	struct dwc_qtd *qtd;
+	struct list_head *pos, *temp;
+
+	/* Free each QTD in the QTD list */
+	list_for_each_safe(pos, temp, &qh->qtd_list) {
+		list_del(pos);
+		qtd = dwc_list_to_qtd(pos);
+		dwc_otg_hcd_qtd_free(qtd);
+	}
+	kfree(qh);
+}
+
+/**
+ * Microframe scheduler
+ * track the total use in hcd->frame_usecs
+ * keep each qh use in qh->frame_usecs
+ * when surrendering the qh then donate the time back
+ */
+static const u16 max_uframe_usecs[] = { 100, 100, 100, 100, 100, 100, 30, 0 };
+
+/*
+ * called from dwc_otg_hcd.c:dwc_otg_hcd_init
+ */
+int init_hcd_usecs(struct dwc_hcd *hcd)
+{
+	int i;
+
+	for (i = 0; i < 8; i++)
+		hcd->frame_usecs[i] = max_uframe_usecs[i];
+
+	return 0;
+}
+
+static int find_single_uframe(struct dwc_hcd *hcd, struct dwc_qh *qh)
+{
+	int i;
+	u16 utime;
+	int t_left;
+	int ret;
+	int done;
+
+	ret = -1;
+	utime = qh->usecs;
+	t_left = utime;
+	i = 0;
+	done = 0;
+	while (done == 0) {
+		/* At the start hcd->frame_usecs[i] = max_uframe_usecs[i]; */
+		if (utime <= hcd->frame_usecs[i]) {
+			hcd->frame_usecs[i] -= utime;
+			qh->frame_usecs[i] += utime;
+			t_left -= utime;
+			ret = i;
+			done = 1;
+			return ret;
+		} else {
+			i++;
+			if (i == 8) {
+				done = 1;
+				ret = -1;
+			}
+		}
+	}
+	return ret;
+}
+
+/*
+ * use this for FS apps that can span multiple uframes
+ */
+static int find_multi_uframe(struct dwc_hcd *hcd, struct dwc_qh *qh)
+{
+	int i;
+	int j;
+	u16 utime;
+	int t_left;
+	int ret;
+	int done;
+	u16 xtime;
+
+	ret = -1;
+	utime = qh->usecs;
+	t_left = utime;
+	i = 0;
+	done = 0;
+loop:
+	while (done == 0) {
+		if (hcd->frame_usecs[i] <= 0) {
+			i++;
+			if (i == 8) {
+				done = 1;
+				ret = -1;
+			}
+			goto loop;
+		}
+
+		/*
+		 * We need n consequtive slots so use j as a start slot.
+		 * j plus j+1 must be enough time (for now)
+		 */
+		xtime = hcd->frame_usecs[i];
+		for (j = i + 1; j < 8; j++) {
+			/*
+			 * if we add this frame remaining time to xtime we may
+			 * be OK, if not we need to test j for a complete frame.
+			 */
+			if ((xtime + hcd->frame_usecs[j]) < utime) {
+				if (hcd->frame_usecs[j] < max_uframe_usecs[j]) {
+					j = 8;
+					ret = -1;
+					continue;
+				}
+			}
+			if (xtime >= utime) {
+				ret = i;
+				j = 8;	/* stop loop with a good value ret */
+				continue;
+			}
+			/* add the frame time to x time */
+			xtime += hcd->frame_usecs[j];
+			/* we must have a fully available next frame or break */
+			if ((xtime < utime) &&
+			    (hcd->frame_usecs[j] == max_uframe_usecs[j])) {
+				ret = -1;
+				j = 8;	/* stop loop with a bad value ret */
+				continue;
+			}
+		}
+		if (ret >= 0) {
+			t_left = utime;
+			for (j = i; (t_left > 0) && (j < 8); j++) {
+				t_left -= hcd->frame_usecs[j];
+				if (t_left <= 0) {
+					qh->frame_usecs[j] +=
+					    hcd->frame_usecs[j] + t_left;
+					hcd->frame_usecs[j] = -t_left;
+					ret = i;
+					done = 1;
+				} else {
+					qh->frame_usecs[j] +=
+					    hcd->frame_usecs[j];
+					hcd->frame_usecs[j] = 0;
+				}
+			}
+		} else {
+			i++;
+			if (i == 8) {
+				done = 1;
+				ret = -1;
+			}
+		}
+	}
+	return ret;
+}
+
+static int find_uframe(struct dwc_hcd *hcd, struct dwc_qh *qh)
+{
+	int ret = -1;
+
+	if (qh->speed == USB_SPEED_HIGH)
+		/* if this is a hs transaction we need a full frame */
+		ret = find_single_uframe(hcd, qh);
+	else
+		/* FS transaction may need a sequence of frames */
+		ret = find_multi_uframe(hcd, qh);
+
+	return ret;
+}
+
+/**
+ * Checks that the max transfer size allowed in a host channel is large enough
+ * to handle the maximum data transfer in a single (micro)frame for a periodic
+ * transfer.
+ */
+static int check_max_xfer_size(struct dwc_hcd *hcd, struct dwc_qh *qh)
+{
+	int status = 0;
+	u32 max_xfer_size;
+	u32 max_channel_xfer_size;
+
+	max_xfer_size = dwc_max_packet(qh->maxp) * dwc_hb_mult(qh->maxp);
+	max_channel_xfer_size = hcd->core_if->core_params->max_transfer_size;
+
+	if (max_xfer_size > max_channel_xfer_size) {
+		pr_notice("%s: Periodic xfer length %d > max xfer "
+			  "length for channel %d\n", __func__, max_xfer_size,
+			  max_channel_xfer_size);
+		status = -ENOSPC;
+	}
+
+	return status;
+}
+
+/**
+ * Schedules an interrupt or isochronous transfer in the periodic schedule.
+ */
+static int schedule_periodic(struct dwc_hcd *hcd, struct dwc_qh *qh)
+{
+	int status;
+	struct usb_bus *bus = hcd_to_bus(dwc_otg_hcd_to_hcd(hcd));
+	int frame;
+
+	status = find_uframe(hcd, qh);
+	frame = -1;
+	if (status == 0) {
+		frame = 7;
+	} else {
+		if (status > 0)
+			frame = status - 1;
+	}
+	/* Set the new frame up */
+	if (frame > -1) {
+		qh->sched_frame &= ~0x7;
+		qh->sched_frame |= (frame & 7);
+	}
+	if (status != -1)
+		status = 0;
+	if (status) {
+		pr_notice("%s: Insufficient periodic bandwidth for "
+			  "periodic transfer.\n", __func__);
+		return status;
+	}
+	status = check_max_xfer_size(hcd, qh);
+	if (status) {
+		pr_notice("%s: Channel max transfer size too small "
+			  "for periodic transfer.\n", __func__);
+		return status;
+	}
+	/* Always start in the inactive schedule. */
+	list_add_tail(&qh->qh_list_entry, &hcd->periodic_sched_inactive);
+
+	/* Update claimed usecs per (micro)frame. */
+	hcd->periodic_usecs += qh->usecs;
+
+	/*
+	 * Update average periodic bandwidth claimed and # periodic reqs for
+	 * usbfs.
+	 */
+	bus->bandwidth_allocated += qh->usecs / qh->interval;
+
+	if (qh->ep_type == USB_ENDPOINT_XFER_INT)
+		bus->bandwidth_int_reqs++;
+	else
+		bus->bandwidth_isoc_reqs++;
+
+	return status;
+}
+
+/**
+ * This function adds a QH to either the non periodic or periodic schedule if
+ * it is not already in the schedule. If the QH is already in the schedule, no
+ * action is taken.
+ */
+static int dwc_otg_hcd_qh_add(struct dwc_hcd *hcd, struct dwc_qh *qh)
+{
+	int status = 0;
+
+	/* QH may already be in a schedule. */
+	if (!list_empty(&qh->qh_list_entry))
+		goto done;
+	/*
+	 * Add the new QH to the appropriate schedule. For non-periodic, always
+	 * start in the inactive schedule.
+	 */
+	if (dwc_qh_is_non_per(qh))
+		list_add_tail(&qh->qh_list_entry,
+			      &hcd->non_periodic_sched_inactive);
+	else
+		status = schedule_periodic(hcd, qh);
+
+done:
+	return status;
+}
+
+/**
+ * This function adds a QH to the non periodic deferred schedule.
+ *
+ * @return 0 if successful, negative error code otherwise.
+ */
+int dwc_otg_hcd_qh_add_deferred(struct dwc_hcd *hcd, struct dwc_qh *qh)
+{
+	if (!list_empty(&qh->qh_list_entry))
+		/* QH already in a schedule. */
+		goto done;
+
+	/* Add the new QH to the non periodic deferred schedule */
+	if (dwc_qh_is_non_per(qh))
+		list_add_tail(&qh->qh_list_entry,
+			      &hcd->non_periodic_sched_deferred);
+done:
+	return 0;
+}
+
+/**
+ * Removes an interrupt or isochronous transfer from the periodic schedule.
+ */
+static void deschedule_periodic(struct dwc_hcd *hcd, struct dwc_qh *qh)
+{
+	struct usb_bus *bus = hcd_to_bus(dwc_otg_hcd_to_hcd(hcd));
+	int i;
+
+	list_del_init(&qh->qh_list_entry);
+	/* Update claimed usecs per (micro)frame. */
+	hcd->periodic_usecs -= qh->usecs;
+	for (i = 0; i < 8; i++) {
+		hcd->frame_usecs[i] += qh->frame_usecs[i];
+		qh->frame_usecs[i] = 0;
+	}
+	/*
+	 * Update average periodic bandwidth claimed and # periodic reqs for
+	 * usbfs.
+	 */
+	bus->bandwidth_allocated -= qh->usecs / qh->interval;
+
+	if (qh->ep_type == USB_ENDPOINT_XFER_INT)
+		bus->bandwidth_int_reqs--;
+	else
+		bus->bandwidth_isoc_reqs--;
+}
+
+/**
+ * Removes a QH from either the non-periodic or periodic schedule.  Memory is
+ * not freed.
+ */
+void dwc_otg_hcd_qh_remove(struct dwc_hcd *hcd, struct dwc_qh *qh)
+{
+	/* Do nothing if QH is not in a schedule */
+	if (list_empty(&qh->qh_list_entry))
+		return;
+
+	if (dwc_qh_is_non_per(qh)) {
+		if (hcd->non_periodic_qh_ptr == &qh->qh_list_entry)
+			hcd->non_periodic_qh_ptr =
+			    hcd->non_periodic_qh_ptr->next;
+		list_del_init(&qh->qh_list_entry);
+	} else {
+		deschedule_periodic(hcd, qh);
+	}
+}
+
+/**
+ * Defers a QH. For non-periodic QHs, removes the QH from the active
+ * non-periodic schedule. The QH is added to the deferred non-periodic
+ * schedule if any QTDs are still attached to the QH.
+ */
+int dwc_otg_hcd_qh_deferr(struct dwc_hcd *hcd, struct dwc_qh *qh, int delay)
+{
+	int deact = 1;
+
+	if (dwc_qh_is_non_per(qh)) {
+		qh->sched_frame = dwc_frame_num_inc(hcd->frame_number, delay);
+		qh->channel = NULL;
+		qh->qtd_in_process = NULL;
+		deact = 0;
+		dwc_otg_hcd_qh_remove(hcd, qh);
+		if (!list_empty(&qh->qtd_list))
+			/* Add back to deferred non-periodic schedule. */
+			dwc_otg_hcd_qh_add_deferred(hcd, qh);
+	}
+	return deact;
+}
+
+/**
+ *  Schedule the next continuing periodic split transfer
+ */
+static void sched_next_per_split_xfr(struct dwc_qh *qh, u16 fr_num,
+				     int sched_split)
+{
+	if (sched_split) {
+		qh->sched_frame = fr_num;
+		if (dwc_frame_num_le(fr_num,
+				     dwc_frame_num_inc(qh->start_split_frame,
+						       1))) {
+			/*
+			 * Allow one frame to elapse after start split
+			 * microframe before scheduling complete split, but DONT
+			 * if we are doing the next start split in the
+			 * same frame for an ISOC out.
+			 */
+			if (qh->ep_type != USB_ENDPOINT_XFER_ISOC ||
+			    qh->ep_is_in)
+				qh->sched_frame =
+				    dwc_frame_num_inc(qh->sched_frame, 1);
+		}
+	} else {
+		qh->sched_frame = dwc_frame_num_inc(qh->start_split_frame,
+						    qh->interval);
+
+		if (dwc_frame_num_le(qh->sched_frame, fr_num))
+			qh->sched_frame = fr_num;
+		qh->sched_frame |= 0x7;
+		qh->start_split_frame = qh->sched_frame;
+	}
+}
+
+/**
+ * Deactivates a periodic QH.  The QH is removed from the periodic queued
+ * schedule. If there are any QTDs still attached to the QH, the QH is added to
+ * either the periodic inactive schedule or the periodic ready schedule and its
+ * next scheduled frame is calculated. The QH is placed in the ready schedule if
+ * the scheduled frame has been reached already. Otherwise it's placed in the
+ * inactive schedule. If there are no QTDs attached to the QH, the QH is
+ * completely removed from the periodic schedule.
+ */
+static void deactivate_periodic_qh(struct dwc_hcd *hcd, struct dwc_qh *qh,
+				   int sched_next_split)
+{
+	/* unsigned long flags; */
+	u16 fr_num = dwc_otg_hcd_get_frame_number(dwc_otg_hcd_to_hcd(hcd));
+
+	if (qh->do_split) {
+		sched_next_per_split_xfr(qh, fr_num, sched_next_split);
+	} else {
+		qh->sched_frame = dwc_frame_num_inc(qh->sched_frame,
+						    qh->interval);
+		if (dwc_frame_num_le(qh->sched_frame, fr_num))
+			qh->sched_frame = fr_num;
+	}
+
+	if (list_empty(&qh->qtd_list)) {
+		dwc_otg_hcd_qh_remove(hcd, qh);
+	} else {
+		/*
+		 * Remove from periodic_sched_queued and move to appropriate
+		 * queue.
+		 */
+		if (qh->sched_frame == fr_num)
+			list_move(&qh->qh_list_entry,
+				  &hcd->periodic_sched_ready);
+		else
+			list_move(&qh->qh_list_entry,
+				  &hcd->periodic_sched_inactive);
+	}
+}
+
+/**
+ * Deactivates a non-periodic QH.  Removes the QH from the active non-periodic
+ * schedule. The QH is added to the inactive non-periodic schedule if any QTDs
+ * are still attached to the QH.
+ */
+static void deactivate_non_periodic_qh(struct dwc_hcd *hcd, struct dwc_qh *qh)
+{
+	dwc_otg_hcd_qh_remove(hcd, qh);
+	if (!list_empty(&qh->qtd_list))
+		dwc_otg_hcd_qh_add(hcd, qh);
+}
+
+/**
+ * Deactivates a QH.  Determines if the QH is periodic or non-periodic and takes
+ * the appropriate action.
+ */
+void dwc_otg_hcd_qh_deactivate(struct dwc_hcd *hcd, struct dwc_qh *qh,
+			       int sched_next_periodic_split)
+{
+	if (dwc_qh_is_non_per(qh))
+		deactivate_non_periodic_qh(hcd, qh);
+	else
+		deactivate_periodic_qh(hcd, qh, sched_next_periodic_split);
+}
+
+/**
+ * Initializes a QTD structure.
+ */
+static void dwc_otg_hcd_qtd_init(struct dwc_qtd *qtd, struct urb *urb)
+{
+	memset(qtd, 0, sizeof(struct dwc_qtd));
+	qtd->urb = urb;
+
+	if (usb_pipecontrol(urb->pipe)) {
+		/*
+		 * The only time the QTD data toggle is used is on the data
+		 * phase of control transfers. This phase always starts with
+		 * DATA1.
+		 */
+		qtd->data_toggle = DWC_OTG_HC_PID_DATA1;
+		qtd->control_phase = DWC_OTG_CONTROL_SETUP;
+	}
+
+	/* start split */
+	qtd->complete_split = 0;
+	qtd->isoc_split_pos = DWC_HCSPLIT_XACTPOS_ALL;
+	qtd->isoc_split_offset = 0;
+
+	/* Store the qtd ptr in the urb to reference what QTD. */
+	urb->hcpriv = qtd;
+
+	INIT_LIST_HEAD(&qtd->qtd_list_entry);
+	return;
+}
+
+/* Allocates memory for a QTD structure. */
+static inline struct dwc_qtd *dwc_otg_hcd_qtd_alloc(gfp_t _mem_flags)
+{
+	return kmalloc(sizeof(struct dwc_qtd), _mem_flags);
+}
+
+/**
+ * This function allocates and initializes a QTD.
+ */
+struct dwc_qtd *dwc_otg_hcd_qtd_create(struct urb *urb, gfp_t _mem_flags)
+{
+	struct dwc_qtd *qtd = dwc_otg_hcd_qtd_alloc(_mem_flags);
+
+	if (!qtd)
+		return NULL;
+
+	dwc_otg_hcd_qtd_init(qtd, urb);
+	return qtd;
+}
+
+/**
+ * This function adds a QTD to the QTD-list of a QH.  It will find the correct
+ * QH to place the QTD into.  If it does not find a QH, then it will create a
+ * new QH. If the QH to which the QTD is added is not currently scheduled, it
+ * is placed into the proper schedule based on its EP type.
+ *
+ */
+int dwc_otg_hcd_qtd_add(struct dwc_qtd *qtd, struct dwc_hcd *hcd)
+{
+	struct usb_host_endpoint *ep;
+	struct dwc_qh *qh;
+	int retval = 0;
+	struct urb *urb = qtd->urb;
+
+	/*
+	 * Get the QH which holds the QTD-list to insert to. Create QH if it
+	 * doesn't exist.
+	 */
+	ep = dwc_urb_to_endpoint(urb);
+
+	qh = (struct dwc_qh *)ep->hcpriv;
+	if (!qh) {
+		qh = dwc_otg_hcd_qh_create(hcd, urb);
+		if (!qh) {
+			retval = -1;
+			goto done;
+		}
+		ep->hcpriv = qh;
+	}
+	qtd->qtd_qh_ptr = qh;
+	retval = dwc_otg_hcd_qh_add(hcd, qh);
+	if (!retval)
+		list_add_tail(&qtd->qtd_list_entry, &qh->qtd_list);
+
+done:
+	return retval;
+}
-- 
1.6.1.rc3

^ permalink raw reply related


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