LinuxPPC-Dev Archive on lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH V8 00/10] Add-Synopsys-DesignWare-HS-USB-OTG-driver
From: tmarri @ 2011-01-19 22:57 UTC (permalink / raw)
  To: linux-usb, linuxppc-dev; +Cc: tmarri, greg

From: Tirumala Marri <tmarri@apm.com>

v8:
 1. Add set_wedge to usb_ep_ops.

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

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
  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       | 1220 +++++++++++++++
 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       | 1766 ++++++++++++++++++++++
 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, 14224 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][v2] define binding for fsl mpic interrupt controllers
From: Stuart yoder @ 2011-01-19 22:30 UTC (permalink / raw)
  To: linuxppc-dev, devicetree-discuss; +Cc: Stuart Yoder

From: Stuart Yoder <stuart.yoder@freescale.com>

define the binding for compatible = "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.

Signed-off-by: Stuart Yoder <stuart.yoder@freescale.com>
---
-version 2
   -fix some typos
   -move defintion of interrupt number to the interrupt
    type 0
   -defined no-reset property
   -added some examples

 Documentation/powerpc/dts-bindings/fsl/mpic.txt |  251 +++++++++++++++++++----
 1 files changed, 209 insertions(+), 42 deletions(-)

diff --git a/Documentation/powerpc/dts-bindings/fsl/mpic.txt b/Documentation/powerpc/dts-bindings/fsl/mpic.txt
index 71e39cf..a6160b5 100644
--- a/Documentation/powerpc/dts-bindings/fsl/mpic.txt
+++ b/Documentation/powerpc/dts-bindings/fsl/mpic.txt
@@ -1,42 +1,209 @@
-* OpenPIC and its interrupt numbers on Freescale's e500/e600 cores
-
-The OpenPIC specification does not specify which interrupt source has to
-become which interrupt number. This is up to the software implementation
-of the interrupt controller. The only requirement is that every
-interrupt source has to have an unique interrupt number / vector number.
-To accomplish this the current implementation assigns the number zero to
-the first source, the number one to the second source and so on until
-all interrupt sources have their unique number.
-Usually the assigned vector number equals the interrupt number mentioned
-in the documentation for a given core / CPU. This is however not true
-for the e500 cores (MPC85XX CPUs) where the documentation distinguishes
-between internal and external interrupt sources and starts counting at
-zero for both of them.
-
-So what to write for external interrupt source X or internal interrupt
-source Y into the device tree? Here is an example:
-
-The memory map for the interrupt controller in the MPC8544[0] shows,
-that the first interrupt source starts at 0x5_0000 (PIC Register Address
-Map-Interrupt Source Configuration Registers). This source becomes the
-number zero therefore:
- External interrupt 0 = interrupt number 0
- External interrupt 1 = interrupt number 1
- External interrupt 2 = interrupt number 2
- ...
-Every interrupt number allocates 0x20 bytes register space. So to get
-its number it is sufficient to shift the lower 16bits to right by five.
-So for the external interrupt 10 we have:
-  0x0140 >> 5 = 10
-
-After the external sources, the internal sources follow. The in core I2C
-controller on the MPC8544 for instance has the internal source number
-27. Oo obtain its interrupt number we take the lower 16bits of its memory
-address (0x5_0560) and shift it right:
- 0x0560 >> 5 = 43
-
-Therefore the I2C device node for the MPC8544 CPU has to have the
-interrupt number 43 specified in the device tree.
-
-[0] MPC8544E PowerQUICCTM III, Integrated Host Processor Family Reference Manual
-    MPC8544ERM Rev. 1 10/2007
+=====================================================================
+Freescale MPIC Interrupt Controller Node
+Copyright (C) 2010,2011 Freescale Semiconductor Inc.
+=====================================================================
+
+The Freescale MPIC interrupt controller is found on all PowerQUICC
+and QorIQ processors and is compatible with the Open PIC.  The
+notable difference from Open PIC binding is the addition of 2
+additional cells in the interrupt specifier defining interrupt type
+information.
+
+PROPERTIES
+
+  - compatible
+      Usage: required
+      Value type: <string>
+      Definition: Shall include "fsl,mpic".  Freescale MPIC
+          controllers compatible with this binding have Block
+          Revision Registers BRR1 and BRR2 at offset 0x0 and
+          0x10 in the MPIC.
+
+  - reg
+      Usage: required
+      Value type: <prop-encoded-array>
+      Definition: A standard property.  Specifies the physical
+          offset and length of the device's registers within the
+          CCSR address space.
+
+  - interrupt-controller
+      Usage: required
+      Value type: <empty>
+      Definition: Specifies that this node is an interrupt
+          controller
+
+  - #interrupt-cells
+      Usage: required
+      Value type: <u32>
+      Definition: Shall be 2 or 4.  A value of 2 means that interrupt
+          specifiers do not contain the interrupt-type or type-specific
+          information cells.
+
+  - #address-cells
+      Usage: required
+      Value type: <u32>
+      Definition: Shall be 0.
+
+  - no-reset
+      Usage: optional
+      Value type: <empty>
+      Definition: The presence of this property specifies that the
+          MPIC must not be reset by the client program, and that
+          the boot program has initialized all interrupt source
+          configuration registers to a sane state-- masked or
+          directed at other cores.  This ensures that the client
+          program will not receive interrupts for sources not belonging
+          to the client.
+       
+INTERRUPT SPECIFIER DEFINITION
+
+  Interrupt specifiers consists of 4 cells encoded as
+  follows:
+
+   <1st-cell>   interrupt-number
+
+                Identifies the interrupt source.  The meaning
+                depends on the type of interrupt.
+
+                Note: If the interrupt-type cell is undefined
+                (i.e. #interrupt-cells = 2), this cell
+                should be interpreted the same as for
+                interrupt-type 0-- i.e. an external or
+                normal SoC device interrupt.
+
+   <2nd-cell>   level-sense information, encoded as follows:
+                    0 = low-to-high edge triggered
+                    1 = active low level-sensitive
+                    2 = active high level-sensitive
+                    3 = high-to-low edge triggered
+
+   <3rd-cell>   interrupt-type
+
+                The following types are supported:
+
+                  0 = external or normal SoC device interrupt
+
+                      The interrupt-number cell contains
+                      the SoC device interrupt number.  The
+                      type-specific cell is undefined.  The
+                      interrupt-number is derived from the
+                      MPIC a block of registers referred to as
+                      the "Interrupt Source Configuration Registers".
+                      Each source has 32-bytes of registers
+                      (vector/priority and destination) in this
+                      region.   So interrupt 0 is at offset 0x0,
+                      interrupt 1 is at offset 0x20, and so on.
+
+                  1 = error interrupt
+
+                      The interrupt-number cell contains
+                      the SoC device interrupt number for
+                      the error interrupt.  The type-specific
+                      cell identifies the specific error
+                      interrupt number.
+
+                  2 = MPIC inter-processor interrupt (IPI)
+
+                      The interrupt-number cell identifies
+                      the MPIC IPI number.  The type-specific
+                      cell is undefined.
+
+                  3 = MPIC timer interrupt
+
+                      The interrupt-number cell identifies
+                      the MPIC timer number.  The type-specific
+                      cell is undefined.
+
+   <4th-cell>   type-specific information
+
+                The type-specific cell is encoded as follows:
+
+                 - For interrupt-type 1 (error interrupt),
+                   the type-specific cell contains the
+                   bit number of the error interrupt in the
+                   Error Interrupt Summary Register.
+
+EXAMPLE 1
+	/*
+	 * mpic interrupt controller with 4 cells per specifier
+	 */
+	mpic: pic@40000 {
+		compatible = "fsl,mpic";
+		interrupt-controller;
+		#interrupt-cells = <4>;
+		#address-cells = <0>;
+		reg = <0x40000 0x40000>;
+	};
+
+EXAMPLE 2
+	/*
+	 * The MPC8544 I2C controller node has an internal
+	 * interrupt number of 27.  As per the reference manual
+	 * this corresponds to interrupt source configuration
+	 * registers at 0x5_0560.
+	 *
+	 * The interrupt source configuration registers begin
+	 * at 0x5_0000.
+	 *
+	 * To compute the interrupt specifier interrupt number
+         *
+	 *       0x560 >> 5 = 43
+	 *
+	 * The interrupt source configuration registers begin
+	 * at 0x5_0000, and so the i2c vector/priority registers
+	 * are at 0x5_0560.
+	 */
+	i2c@3000 {
+		#address-cells = <1>;
+		#size-cells = <0>;
+		cell-index = <0>;
+		compatible = "fsl-i2c";
+		reg = <0x3000 0x100>;
+		interrupts = <43 2>;
+		interrupt-parent = <&mpic>;
+		dfsrr;
+	};
+
+
+EXAMPLE 3
+	/*
+	 *  Definition of a node defining the 4
+	 *  MPIC IPI interrupts.  Note the interrupt
+	 *  type of 2.
+	 */
+	ipi@410a0 {
+		compatible = "fsl,mpic-ipi";
+		reg = <0x40040 0x10>;
+		interrupts = <0 0 2 0
+		              1 0 2 0
+		              2 0 2 0
+		              3 0 2 0>;
+	};
+
+EXAMPLE 4
+	/*
+	 *  Definition of a node defining the MPIC
+	 *  global timers.  Note the interrupt
+	 *  type of 3.
+	 */
+	timer0: timer@41100 {
+		compatible = "fsl,mpic-global-timer";
+		reg = <0x41100 0x100>;
+		interrupts = <0 0 3 0
+		              1 0 3 0
+		              2 0 3 0
+		              3 0 3 0>;
+	};
+
+EXAMPLE 5
+	/*
+	 * Definition of an error interrupt (interupt type 1).
+	 * SoC interrupt number is 16 and the specific error
+         * interrupt bit in the error interrupt summary register
+	 * is 23.
+	 */
+	memory-controller@8000 {
+		compatible = "fsl,p4080-memory-controller";
+		reg = <0x8000 0x1000>;
+		interrupts = <16 2 1 23>;
+	};
-- 
1.7.2.2

^ permalink raw reply related

* Re: 2.6.37-git17 virtual IO boot failure
From: Nishanth Aravamudan @ 2011-01-19 22:26 UTC (permalink / raw)
  To: Benjamin Herrenschmidt; +Cc: linuxppc-dev, sonnyrao, Anton Blanchard, miltonm
In-Reply-To: <1295417178.2148.131.camel@pasglop>

On 19.01.2011 [17:06:18 +1100], Benjamin Herrenschmidt wrote:
> On Tue, 2011-01-18 at 16:48 -0800, Nishanth Aravamudan wrote:
> > 
> > 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. 
> 
> I don't believe this specific error is causing the lockup, I think we
> only hit a spurrious message on devices that don't have DMA
> capabilities in the first place. (But I may be wrong, I'll wait for
> you guys to dig more or I'll have a look myself tomorrow if I manage
> to get out of meetings).

Yes, this seems accurate. Like I mentioned elsewhere, this box came up
ok even with these messages and seemed ok (up until the disk locked up).

> So there's another problem with SCSI tho it -could- also be a DMA issue,
> hard to tell at this point.

Right, I'm not sure how to determine that. I did see the lockup, though,
with both my patches reverted (the patches for vio, I mean, after
2.6.37)

> BTW. I'm not too happy with those defaults set to 64-bit. Probably not
> an issue until your other patches go in, but some devices like veth
> cannot do 64-bit DMA. I think we should default to 32-bit in the VIO
> base code and explicitely enable 64-bit DMA from drivers that support it
> (in theory vscsi but I haven't verified the implementation).

Ok, so change the bit-mask to 32-bit? Or would it be appropriate to
attempt 64-bit, if it fails fallback to 32-bit? Seems to be a common
pattern throughout the DMA bit-setting callers.

Thanks,
Nish

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

^ permalink raw reply

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


> +** 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.

Is "protected-sources" really needed for AMP systems to
tell the OSes not to clobber each other?  Won't each
OS be given a device tree with only its interrupt
sources?  ...so you know what you are allowed to touch.

Stuart

^ permalink raw reply

* RE: [PATCH 1/2] powerpc: document the MPIC device tree binding
From: Yoder Stuart-B08248 @ 2011-01-19 20:38 UTC (permalink / raw)
  To: Meador Inge
  Cc: Blanchard, Hollis, devicetree-discuss@lists.ozlabs.org,
	linuxppc-dev@lists.ozlabs.org
In-Reply-To: <4D374899.20402@mentor.com>



> -----Original Message-----
> From: Meador Inge [mailto:meador_inge@mentor.com]
> Sent: Wednesday, January 19, 2011 2:25 PM
> To: Yoder Stuart-B08248
> Cc: linuxppc-dev@lists.ozlabs.org; devicetree-discuss@lists.ozlabs.org;
> Blanchard, Hollis
> Subject: Re: [PATCH 1/2] powerpc: document the MPIC device tree binding
>=20
> On 01/18/2011 02:21 PM, Yoder Stuart-B08248 wrote:
> >>   Documentation/powerpc/dts-bindings/mpic.txt |   78
> >
> > This is really the binding for an open-pic interrupt controller and I
> > think the name should reflect that-- open-pic.txt.
>=20
> Yup, agreed.
>=20
> >> +This binding specifies what properties and child nodes must be
> >> +available on the device tree representation of the "MPIC" interrupt
> >> +controller.  This 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.pd
> > f>
> >
> > The proposed new properties really should go back into the ePAPR.
>=20
> I read portions of ePAPR while writing this binding and considered that.
>   My only worry was that ePAPR is focused on embedded systems and this
> binding will have to cover non-embedded systems that exist in the kernel.
> However, perhaps that is not a legitimate concern?

The ePAPR tried to codify what was previously implemented in
Linux, so I don't think lack of things like "name" and
"device_type" in the binding are an issue.

> >> +
> >> +** Required properties:
> >> +
> >> +   NOTE: Many of these descriptions were paraphrased from [1] to aid
> >> +         readability.
> >> +
> >> +   - name : Specifies the name of the MPIC.
> >
> > Drop this.  No DTS files use it.
>=20
> Done.
>=20
> >> +   - device_type : Specifies the device type of this MPIC.  The
> >> + value of this
> >> +                   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.
>=20
> I will drop this property with the expectation that the kernel will be
> fixed.  From a quick grep of '.../arch/powerpc' it looks like most uses a=
re
> of the form:
>=20
>      np =3D of_find_node_by_type(NULL, "open-pic");
>      if (np =3D=3D NULL)
>         return;
>=20
> In most of these cases I suppose the 'of_find_node_by_type' calls could
> just be replaced with calls to 'of_find_compatible_node(NULL, "open-pic")=
'.

For backwards compatibility, we should continue to accept
the old/deprecated device_type=3D"open-pic", but in addition
we should accept the compatible=3D"open-pic".

> >> +   - reg : Specifies the base physical address(s) and size(s) of this
> >> + MPIC's
> >> +           addressable register space.
> >> +   - compatible : Specifies the compatibility list for the MPIC.  The
> >> + property
> >> +                  value 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.
>=20
> OK, but as a migration path we should allow the kernel to accept both
> (Scott mentioned this in another reply), but "open-pic" is the
> documented correct way.

Right.

> >> +   - interrupt-controller : The presence of this property identifies
> >> + the node
> >> +                            as a MPIC.  No property value should be
> >> defined.
> >> +   - #address-cells : Specifies the number of cells needed to encode =
an
> >> +                      address.  The value of this property shall alwa=
ys
> >> + be 0
> >> +                      so that 'interrupt-map' nodes do not have to
> >> + specify a
> >> +                      parent unit address.
> >> +   - #interrupt-cells : Specifies the number of cells needed to encod=
e
> >> + an
> >> +                        interrupt source.
> >
> > Should be 2, correct?
>=20
> Yup.
>=20
> >> +** 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 a=
re
> >> + not
> >> +                         available for use and whose corresponding
> >> + vectors
> >> +                         should not be initialized.  A typical use ca=
se
> >> + for
> >> +                         this property is in AMP systems where multip=
le
> >> +                         independent operating systems need to share
> >> + the MPIC
> >> +                         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.
>=20
> OK, I will look into that.  Thanks.

I have a version 2 I hope to send out later today.

Stuart

^ permalink raw reply

* Re: [PATCH 1/2] powerpc: document the MPIC device tree binding
From: Meador Inge @ 2011-01-19 20:24 UTC (permalink / raw)
  To: Yoder Stuart-B08248
  Cc: Blanchard, Hollis, devicetree-discuss@lists.ozlabs.org,
	linuxppc-dev@lists.ozlabs.org
In-Reply-To: <9F6FE96B71CF29479FF1CDC8046E150306A7A2@039-SN1MPN1-004.039d.mgd.msft.net>

On 01/18/2011 02:21 PM, Yoder Stuart-B08248 wrote:
>>   Documentation/powerpc/dts-bindings/mpic.txt |   78
>
> This is really the binding for an open-pic interrupt controller
> and I think the name should reflect that-- open-pic.txt.

Yup, agreed.

>> +This binding specifies what properties and child nodes must be
>> +available on the device tree representation of the "MPIC" interrupt
>> +controller.  This 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.

I read portions of ePAPR while writing this binding and considered that. 
  My only worry was that ePAPR is focused on embedded systems and this 
binding will have to cover non-embedded systems that exist in the 
kernel.  However, perhaps that is not a legitimate concern?

>> +
>> +** Required properties:
>> +
>> +   NOTE: Many of these descriptions were paraphrased from [1] to aid
>> +         readability.
>> +
>> +   - name : Specifies the name of the MPIC.
>
> Drop this.  No DTS files use it.

Done.

>> +   - device_type : Specifies the device type of this MPIC.  The value
>> + of this
>> +                   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.

I will drop this property with the expectation that the kernel will be 
fixed.  From a quick grep of '.../arch/powerpc' it looks like most uses 
are of the form:

     np = of_find_node_by_type(NULL, "open-pic");
     if (np == NULL)
        return;

In most of these cases I suppose the 'of_find_node_by_type' calls could 
just be replaced with calls to 'of_find_compatible_node(NULL, "open-pic")'.


>> +   - reg : Specifies the base physical address(s) and size(s) of this
>> + MPIC's
>> +           addressable register space.
>> +   - compatible : Specifies the compatibility list for the MPIC.  The
>> + property
>> +                  value 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.

OK, but as a migration path we should allow the kernel to accept both 
(Scott mentioned this in another reply), but "open-pic" is the 
documented correct way.

>> +   - interrupt-controller : The presence of this property identifies
>> + the node
>> +                            as a MPIC.  No property value should be
>> defined.
>> +   - #address-cells : Specifies the number of cells needed to encode an
>> +                      address.  The value of this property shall always
>> + be 0
>> +                      so that 'interrupt-map' nodes do not have to
>> + specify a
>> +                      parent unit address.
>> +   - #interrupt-cells : Specifies the number of cells needed to encode
>> + an
>> +                        interrupt source.
>
> Should be 2, correct?

Yup.

>> +** 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.
>
> I do think you need to include the definition of interrupt
> specifiers here.   Feel free to cut/paste text from my
> Freescale mpic binding.

OK, I will look into that.  Thanks.


-- 
Meador Inge     | meador_inge AT mentor.com
Mentor Embedded | http://www.mentor.com/embedded-software

^ permalink raw reply

* Re: [PATCH] define binding for fsl mpic interrupt controllers
From: Meador Inge @ 2011-01-19 17:26 UTC (permalink / raw)
  To: Yoder Stuart-B08248
  Cc: Blanchard, Hollis, devicetree-discuss@lists.ozlabs.org,
	linuxppc-dev@lists.ozlabs.org
In-Reply-To: <9F6FE96B71CF29479FF1CDC8046E150306A71F@039-SN1MPN1-004.039d.mgd.msft.net>

On 01/18/2011 01:47 PM, Yoder Stuart-B08248 wrote:
>
> 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.html

I agree that one binding may not make sense, but perhaps we could have 
something like:

    dts-bindings/
    |-- mpic.txt
    |-- fsl
        |-- mpic.txt

where '.../fsl/mpic.txt' only documents the overrides (and maybe refers 
to '.../mpic.txt' in the intro).  For example, the 
'interrupt-controller', '#address-cells', and 'reg' properties are 
really not that different between the two bindings and could be placed 
just in '.../mpic.txt'.  Where as the specific interrupt specifier 
definition and the overrides for 'compatible' and '#interrupt-cells' 
could go in .../fsl/mpic.txt'.

> 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.

Agreed.

> 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.

Great, thanks.

-- 
Meador Inge     | meador_inge AT mentor.com
Mentor Embedded | http://www.mentor.com/embedded-software

^ permalink raw reply

* Re: [PATCH V7 07/10] USB/ppc4xx: Add Synopsys DWC OTG PCD function
From: Alan Stern @ 2011-01-19 16:23 UTC (permalink / raw)
  To: tmarri; +Cc: Mark Miesfeld, greg, linux-usb, linuxppc-dev, Fushen Chen
In-Reply-To: <1295402749-2193-1-git-send-email-tmarri@apm.com>

On Tue, 18 Jan 2011 tmarri@apm.com wrote:

> From: Tirumala Marri <tmarri@apm.com>
> 
> The PCD is responsible for translating requests from the gadget driver
> to appropriate actions on the DWC 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_pcd.c | 1752 +++++++++++++++++++++++++++++++++++++
>  drivers/usb/dwc_otg/dwc_otg_pcd.h |  139 +++
>  2 files changed, 1891 insertions(+), 0 deletions(-)
> 
> diff --git a/drivers/usb/dwc_otg/dwc_otg_pcd.c b/drivers/usb/dwc_otg/dwc_otg_pcd.c
> new file mode 100644
> index 0000000..857dcee
> --- /dev/null
> +++ b/drivers/usb/dwc_otg/dwc_otg_pcd.c

...

> +static struct usb_ep_ops dwc_otg_pcd_ep_ops = {
> +	.enable = dwc_otg_pcd_ep_enable,
> +	.disable = dwc_otg_pcd_ep_disable,
> +	.alloc_request = dwc_otg_pcd_alloc_request,
> +	.free_request = dwc_otg_pcd_free_request,
> +	.queue = dwc_otg_pcd_ep_queue,
> +	.dequeue = dwc_otg_pcd_ep_dequeue,
> +	.set_halt = dwc_otg_pcd_ep_set_halt,
> +	.fifo_status = NULL,
> +	.fifo_flush = NULL,
> +};

This is missing a .set_wedge method.

Alan Stern

^ permalink raw reply

* [PATCH] powerpc/83xx: fix build failures on dt compatible list.
From: Grant Likely @ 2011-01-19 16:20 UTC (permalink / raw)
  To: benh, linuxppc-dev, galak, linux-kernel; +Cc: Stephen Rothwell

Commit a4f740cf, "of/flattree: Add of_flat_dt_match() helper function"
introduced build failures in arch/powerpc/platform/83xx by mistyping
'static' as 'struct' in the compatible string list, and omitting a few
semicolons.  This patch fixes it.

Reported-by: Stephen Rothwell <sfr@canb.auug.org.au>
Signed-off-by: Grant Likely <grant.likely@secretlab.ca>
---

Ben, I can put this one into my merge-devicetree branch and I'll ask Linus
to merge it in a few days.


 arch/powerpc/platforms/83xx/mpc830x_rdb.c |    4 ++--
 arch/powerpc/platforms/83xx/mpc831x_rdb.c |    4 ++--
 2 files changed, 4 insertions(+), 4 deletions(-)

diff --git a/arch/powerpc/platforms/83xx/mpc830x_rdb.c b/arch/powerpc/platforms/83xx/mpc830x_rdb.c
index 661d354..d0c4e15 100644
--- a/arch/powerpc/platforms/83xx/mpc830x_rdb.c
+++ b/arch/powerpc/platforms/83xx/mpc830x_rdb.c
@@ -57,12 +57,12 @@ static void __init mpc830x_rdb_init_IRQ(void)
 	ipic_set_default_priority();
 }
 
-struct const char *board[] __initdata = {
+static const char *board[] __initdata = {
 	"MPC8308RDB",
 	"fsl,mpc8308rdb",
 	"denx,mpc8308_p1m",
 	NULL
-}
+};
 
 /*
  * Called very early, MMU is off, device-tree isn't unflattened
diff --git a/arch/powerpc/platforms/83xx/mpc831x_rdb.c b/arch/powerpc/platforms/83xx/mpc831x_rdb.c
index b54cd73..f859ead 100644
--- a/arch/powerpc/platforms/83xx/mpc831x_rdb.c
+++ b/arch/powerpc/platforms/83xx/mpc831x_rdb.c
@@ -60,11 +60,11 @@ static void __init mpc831x_rdb_init_IRQ(void)
 	ipic_set_default_priority();
 }
 
-struct const char *board[] __initdata = {
+static const char *board[] __initdata = {
 	"MPC8313ERDB",
 	"fsl,mpc8315erdb",
 	NULL
-}
+};
 
 /*
  * Called very early, MMU is off, device-tree isn't unflattened

^ permalink raw reply related

* Re: Problem with Busybox shell
From: MohanReddy koppula @ 2011-01-19 12:06 UTC (permalink / raw)
  To: tiejun.chen; +Cc: Linuxppc-dev, Nicholas Mc Guire
In-Reply-To: <4D3678B6.1040707@windriver.com>

I further debugged and found that flush_to_ldisc() function is not
called which actually wakes up the readers. This is a worker function
and is not being scheduled. I suspected whether timer interrupts are
generated or not. powerpc uses decrementer exceptions as timer
interrupts. I see that timer_interrupt() function in
arch/powerpc/kernel/time.c is not called at all. I printed even
jiffies values and it is not incremented. And I beleivethis makes
scheduler is not scheduling this worker thread.

 I think if flush_to_ldisc is not called nothing can be read from the tty.

Please let me know what could be the reason for timer interrupt being
not called.

Thanks,
Mohan




On Wed, Jan 19, 2011 at 12:37 AM, tiejun.chen <tiejun.chen@windriver.com> wrote:
> MohanReddy koppula wrote:
>> But, if there is any problem with cable I could not have seen any
>> character in the interrupt routine of the driver. I turned off both
>
> I suppose the bootloader, i.e u-boot, works well so looks this should not be
> issued by the cable at least.
>
>> software and hardware flow control as by board doesn't have hardware
>> flow control. tty_read is called and it hangs at ldisc->read. And I
>
> Any panic information? Or any dead lock? Which line in detail?
>
>> see that data is put into the tty buffer by the driver. Will there be
>> any problem with copy_to_user() if there is some problem in the
>> memory?
>
> Can the serial driver support the poll mode? If so maybe you can take a try to
> exclude any interrupt reason.
>
> And even you can remove all codes to initialize the corresponding PIN & CLK
> dedicated to the serial port, then try again since the bootloader already did this.
>
> Tiejun
>
>>
>> Thanks,
>> Mohan
>>
>> On Tue, Jan 18, 2011 at 12:55 PM, Nicholas Mc Guire <der.herr@hofr.at> wrote:
>>> On Tue, 18 Jan 2011, MohanReddy koppula 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.
>>>>
>>> if you are using minicom to connect check if you have hardware/software flow
>>> control turned on - it also could be a cabling problem - had this with the
>>> beagle board where the tx line was on the wrong pin - so I got output but
>>> could not get any response to input.
>>>
>>> hofrat
>>>
>>>
>> _______________________________________________
>> Linuxppc-dev mailing list
>> Linuxppc-dev@lists.ozlabs.org
>> https://lists.ozlabs.org/listinfo/linuxppc-dev
>>
>
>

^ permalink raw reply

* [PATCH 1/2] ATA: Add FSL sata v2 controller support
From: Xulei @ 2011-01-19  9:07 UTC (permalink / raw)
  To: linuxppc-dev; +Cc: linux-ide, kumar.gala, jgarzik, Xulei

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: Lei Xu <B33228@freescale.com>
Signed-off-by: Roy Zang <tie-fei.zang@freescale.com>
Acked-by: Jeff Garzik <jgarzik@redhat.com>
---
 drivers/ata/sata_fsl.c |   25 ++++++++++++++++++-------
 1 files changed, 18 insertions(+), 7 deletions(-)

diff --git a/drivers/ata/sata_fsl.c b/drivers/ata/sata_fsl.c
index b0214d0..01a5400 100644
--- a/drivers/ata/sata_fsl.c
+++ b/drivers/ata/sata_fsl.c
@@ -6,7 +6,7 @@
  * Author: Ashish Kalra <ashish.kalra@freescale.com>
  * Li Yang <leoli@freescale.com>
  *
- * Copyright (c) 2006-2007 Freescale Semiconductor, Inc.
+ * Copyright (c) 2006-2007, 2011 Freescale Semiconductor, Inc.
  *
  * This program is free software; you can redistribute  it and/or modify it
  * under  the terms of  the GNU General  Public License as published by the
@@ -158,7 +158,8 @@ enum {
 	    IE_ON_SINGL_DEVICE_ERR | IE_ON_CMD_COMPLETE,
 
 	EXT_INDIRECT_SEG_PRD_FLAG = (1 << 31),
-	DATA_SNOOP_ENABLE = (1 << 22),
+	DATA_SNOOP_ENABLE_V1 = (1 << 22),
+	DATA_SNOOP_ENABLE_V2 = (1 << 28),
 };
 
 /*
@@ -256,6 +257,7 @@ struct sata_fsl_host_priv {
 	void __iomem *ssr_base;
 	void __iomem *csr_base;
 	int irq;
+	int data_snoop;
 };
 
 static inline unsigned int sata_fsl_tag(unsigned int tag,
@@ -308,7 +310,8 @@ static void sata_fsl_setup_cmd_hdr_entry(struct sata_fsl_port_priv *pp,
 }
 
 static unsigned int sata_fsl_fill_sg(struct ata_queued_cmd *qc, void *cmd_desc,
-				     u32 *ttl, dma_addr_t cmd_desc_paddr)
+				     u32 *ttl, dma_addr_t cmd_desc_paddr,
+				     int data_snoop)
 {
 	struct scatterlist *sg;
 	unsigned int num_prde = 0;
@@ -358,8 +361,7 @@ static unsigned int sata_fsl_fill_sg(struct ata_queued_cmd *qc, void *cmd_desc,
 
 		ttl_dwords += sg_len;
 		prd->dba = cpu_to_le32(sg_addr);
-		prd->ddc_and_ext =
-		    cpu_to_le32(DATA_SNOOP_ENABLE | (sg_len & ~0x03));
+		prd->ddc_and_ext = cpu_to_le32(data_snoop | (sg_len & ~0x03));
 
 		VPRINTK("sg_fill, ttl=%d, dba=0x%x, ddc=0x%x\n",
 			ttl_dwords, prd->dba, prd->ddc_and_ext);
@@ -374,7 +376,7 @@ static unsigned int sata_fsl_fill_sg(struct ata_queued_cmd *qc, void *cmd_desc,
 		/* set indirect extension flag along with indirect ext. size */
 		prd_ptr_to_indirect_ext->ddc_and_ext =
 		    cpu_to_le32((EXT_INDIRECT_SEG_PRD_FLAG |
-				 DATA_SNOOP_ENABLE |
+				 data_snoop |
 				 (indirect_ext_segment_sz & ~0x03)));
 	}
 
@@ -417,7 +419,8 @@ static void sata_fsl_qc_prep(struct ata_queued_cmd *qc)
 
 	if (qc->flags & ATA_QCFLAG_DMAMAP)
 		num_prde = sata_fsl_fill_sg(qc, (void *)cd,
-					    &ttl_dwords, cd_paddr);
+					    &ttl_dwords, cd_paddr,
+					    host_priv->data_snoop);
 
 	if (qc->tf.protocol == ATA_PROT_NCQ)
 		desc_info |= FPDMA_QUEUED_CMD;
@@ -1336,6 +1339,11 @@ static int sata_fsl_probe(struct platform_device *ofdev,
 	}
 	host_priv->irq = irq;
 
+	if (of_device_is_compatible(ofdev->dev.of_node, "fsl,pq-sata-v2"))
+		host_priv->data_snoop = DATA_SNOOP_ENABLE_V2;
+	else
+		host_priv->data_snoop = DATA_SNOOP_ENABLE_V1;
+
 	/* allocate host structure */
 	host = ata_host_alloc_pinfo(&ofdev->dev, ppi, SATA_FSL_MAX_PORTS);
 
@@ -1418,6 +1426,9 @@ static struct of_device_id fsl_sata_match[] = {
 	{
 		.compatible = "fsl,pq-sata",
 	},
+	{
+		.compatible = "fsl,pq-sata-v2",
+	},
 	{},
 };
 
-- 
1.7.0.4

^ permalink raw reply related

* [PATCH 2/2] dts: Update sata controller compatible for p1022ds board
From: Xulei @ 2011-01-19  9:07 UTC (permalink / raw)
  To: linuxppc-dev; +Cc: linux-ide, kumar.gala, jgarzik, Xulei
In-Reply-To: <1295428050-21934-1-git-send-email-B33228@freescale.com>

Update p1022 sata compatible to "fsl,p1022-sata", "fsl,pq-sata-v2".
p1022ds sata controller is v2 version comparing previous FSL sata
controller, for example, mpc8536.

Signed-off-by: Lei Xu <B33228@freescale.com>
Signed-off-by: Roy Zang <tie-fei.zang@freescale.com>
---
 arch/powerpc/boot/dts/p1022ds.dts |    4 ++--
 1 files changed, 2 insertions(+), 2 deletions(-)

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>;
-- 
1.7.0.4

^ permalink raw reply related

* Re: [PATCH] ATA: Add FSL sata v2 controller support
From: Kumar Gala @ 2011-01-19  5:23 UTC (permalink / raw)
  To: Roy Zang, Xulei; +Cc: linuxppc-dev, Jeff Garzik, Sergei Shtylyov, linux-ide
In-Reply-To: <4D3639FB.10608@pobox.com>


On Jan 18, 2011, at 7:10 PM, Jeff Garzik wrote:

> On 01/17/2011 06:47 AM, Sergei Shtylyov wrote:
>> Hello.
>>=20
>> On 17-01-2011 10:10, Xulei 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
>> AFAIK, full name is required.
>>=20
>>> Signed-off-by: Roy Zang <tie-fei.zang@freescale.com>
>> [...]
>>=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 @@
>>> };
>>>=20
>>> 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>;
>>> };
>>>=20
>>> 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 tree.
>=20
> 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).
>=20
> Acked-by: Jeff Garzik <jgarzik@redhat.com>

With Jeff's ack I'll pull this in via the powerpc tree's.

Can we make the minor updates that Sergei has commented on.

- k=

^ permalink raw reply

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

On Tue, 2011-01-18 at 16:48 -0800, Nishanth Aravamudan wrote:
> 
> 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. 

I don't believe this specific error is causing the lockup, I think we
only hit a spurrious message on devices that don't have DMA capabilities
in the first place. (But I may be wrong, I'll wait for you guys to dig
more or I'll have a look myself tomorrow if I manage to get out of
meetings).

So there's another problem with SCSI tho it -could- also be a DMA issue,
hard to tell at this point.

BTW. I'm not too happy with those defaults set to 64-bit. Probably not
an issue until your other patches go in, but some devices like veth
cannot do 64-bit DMA. I think we should default to 32-bit in the VIO
base code and explicitely enable 64-bit DMA from drivers that support it
(in theory vscsi but I haven't verified the implementation).

Cheers,
Ben.

^ permalink raw reply

* Re: Problem with Busybox shell
From: tiejun.chen @ 2011-01-19  5:37 UTC (permalink / raw)
  To: MohanReddy koppula; +Cc: Linuxppc-dev, Nicholas Mc Guire
In-Reply-To: <AANLkTinDsQYS=dHM2EVbCxv0Yk2zVq5LrYJemL6nSn3D@mail.gmail.com>

MohanReddy koppula wrote:
> But, if there is any problem with cable I could not have seen any
> character in the interrupt routine of the driver. I turned off both

I suppose the bootloader, i.e u-boot, works well so looks this should not be
issued by the cable at least.

> software and hardware flow control as by board doesn't have hardware
> flow control. tty_read is called and it hangs at ldisc->read. And I

Any panic information? Or any dead lock? Which line in detail?

> see that data is put into the tty buffer by the driver. Will there be
> any problem with copy_to_user() if there is some problem in the
> memory?

Can the serial driver support the poll mode? If so maybe you can take a try to
exclude any interrupt reason.

And even you can remove all codes to initialize the corresponding PIN & CLK
dedicated to the serial port, then try again since the bootloader already did this.

Tiejun

> 
> Thanks,
> Mohan
> 
> On Tue, Jan 18, 2011 at 12:55 PM, Nicholas Mc Guire <der.herr@hofr.at> wrote:
>> On Tue, 18 Jan 2011, MohanReddy koppula 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.
>>>
>> if you are using minicom to connect check if you have hardware/software flow
>> control turned on - it also could be a cabling problem - had this with the
>> beagle board where the tx line was on the wrong pin - so I got output but
>> could not get any response to input.
>>
>> hofrat
>>
>>
> _______________________________________________
> Linuxppc-dev mailing list
> Linuxppc-dev@lists.ozlabs.org
> https://lists.ozlabs.org/listinfo/linuxppc-dev
> 

^ permalink raw reply

* Re: Problem with Busybox shell
From: MohanReddy koppula @ 2011-01-19  5:06 UTC (permalink / raw)
  To: Nicholas Mc Guire, Linuxppc-dev
In-Reply-To: <20110118175533.GB28268@opentech.at>

But, if there is any problem with cable I could not have seen any
character in the interrupt routine of the driver. I turned off both
software and hardware flow control as by board doesn't have hardware
flow control. tty_read is called and it hangs at ldisc->read. And I
see that data is put into the tty buffer by the driver. Will there be
any problem with copy_to_user() if there is some problem in the
memory?

Thanks,
Mohan

On Tue, Jan 18, 2011 at 12:55 PM, Nicholas Mc Guire <der.herr@hofr.at> wrote:
> On Tue, 18 Jan 2011, MohanReddy koppula 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.
>>
> if you are using minicom to connect check if you have hardware/software flow
> control turned on - it also could be a cabling problem - had this with the
> beagle board where the tx line was on the wrong pin - so I got output but
> could not get any response to input.
>
> hofrat
>
>

^ permalink raw reply

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

On Tue, 2011-01-18 at 20:37 -0800, Nishanth Aravamudan wrote:

> Is this what you were looking for?
> 
> vio 30000000: Warning: IOMMU dma not supported: mask 0xffffffffffffffff, table unavailable
> vio 30000000: Path: /vdevice/vty@30000000
> vio 4000: Warning: IOMMU dma not supported: mask 0xffffffffffffffff, table unavailable
> vio 4000: Path: /vdevice/IBM,sp@4000
> vio 4001: Warning: IOMMU dma not supported: mask 0xffffffffffffffff, table unavailable
> vio 4001: Path: /vdevice/rtc@4001
> vio 4002: Warning: IOMMU dma not supported: mask 0xffffffffffffffff, table unavailable
> vio 4002: Path: /vdevice/nvram@4002
> vio 4004: Warning: IOMMU dma not supported: mask 0xffffffffffffffff, table unavailable
> vio 4004: Path: /vdevice/gscsi@4004

Ok, so they are all harmess (none of those device do DMA, appart maybe
gscsi, I have no idea what it is :-)

> FWIW, I looked at Anton's logs, and I don't think the boot failed, per
> se. I think it may have timed out (but not positive on that). I was able
> to boot 2.6.27-git17 on the exact same box, albeit it locks up at a
> later point (the sd abort I e-mailed about in a follow-up).

I haven't seen your email. I'll dig. Have to run now.

Cheers,
Ben.

> 
> 
> > 
> > Cheers,
> > Ben.
> > 
> > > 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
> > > 
> > 
> > 
> 

^ permalink raw reply

* Re: 2.6.37-git17 virtual IO boot failure
From: Nishanth Aravamudan @ 2011-01-19  4:37 UTC (permalink / raw)
  To: Benjamin Herrenschmidt; +Cc: linuxppc-dev, sonnyrao, Anton Blanchard, miltonm
In-Reply-To: <1295409980.2148.125.camel@pasglop>

On 19.01.2011 [15:06:20 +1100], Benjamin Herrenschmidt wrote:
> On Tue, 2011-01-18 at 14:47 -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.
> 
> Well, this causes messages for vdevices that don't do DMA at all (such
> as vterm etc...) and don't have the necessary properties. However, it
> didn't -break- anything for me in my tests so far, just spurrious
> messages. Not sure what's up with Anton's setup. Anton, can you hack the
> printk to display the OF path to the device so we see what devices are
> complaining ? It could be a different issue that prevents booting.

Is this what you were looking for?

vio 30000000: Warning: IOMMU dma not supported: mask 0xffffffffffffffff, table unavailable
vio 30000000: Path: /vdevice/vty@30000000
vio 4000: Warning: IOMMU dma not supported: mask 0xffffffffffffffff, table unavailable
vio 4000: Path: /vdevice/IBM,sp@4000
vio 4001: Warning: IOMMU dma not supported: mask 0xffffffffffffffff, table unavailable
vio 4001: Path: /vdevice/rtc@4001
vio 4002: Warning: IOMMU dma not supported: mask 0xffffffffffffffff, table unavailable
vio 4002: Path: /vdevice/nvram@4002
vio 4004: Warning: IOMMU dma not supported: mask 0xffffffffffffffff, table unavailable
vio 4004: Path: /vdevice/gscsi@4004

FWIW, I looked at Anton's logs, and I don't think the boot failed, per
se. I think it may have timed out (but not positive on that). I was able
to boot 2.6.27-git17 on the exact same box, albeit it locks up at a
later point (the sd abort I e-mailed about in a follow-up).



> 
> Cheers,
> Ben.
> 
> > 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

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

On Tue, 2011-01-18 at 14:47 -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.

Well, this causes messages for vdevices that don't do DMA at all (such
as vterm etc...) and don't have the necessary properties. However, it
didn't -break- anything for me in my tests so far, just spurrious
messages. Not sure what's up with Anton's setup. Anton, can you hack the
printk to display the OF path to the device so we see what devices are
complaining ? It could be a different issue that prevents booting.

Cheers,
Ben.

> 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
> 

^ permalink raw reply

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

From: Tirumala Marri <tmarri@apm.com>

Add Synopsys DesignWare HS USB OTG driver kernel configuration.
Synopsys OTG driver may operate in  host only, device only, or OTG mode.
The driver also allows user configure the core to use its internal DMA
or Slave (PIO) mode.

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/Makefile             |    2 +
 drivers/usb/Kconfig          |    3 +-
 drivers/usb/dwc_otg/Kconfig  |   96 ++++++++++++++++++++++++++++++++++++++++++
 drivers/usb/dwc_otg/Makefile |   19 ++++++++
 4 files changed, 119 insertions(+), 1 deletions(-)

diff --git a/drivers/Makefile b/drivers/Makefile
index 2cbb4b7..9a9aa05 100644
--- a/drivers/Makefile
+++ b/drivers/Makefile
@@ -67,6 +67,7 @@ obj-$(CONFIG_UWB)		+= uwb/
 obj-$(CONFIG_USB_OTG_UTILS)	+= usb/otg/
 obj-$(CONFIG_USB)		+= usb/
 obj-$(CONFIG_USB_MUSB_HDRC)	+= usb/musb/
+obj-$(CONFIG_USB_DWC_OTG)      += usb/dwc_otg/
 obj-$(CONFIG_PCI)		+= usb/
 obj-$(CONFIG_USB_GADGET)	+= usb/gadget/
 obj-$(CONFIG_SERIO)		+= input/serio/
@@ -105,6 +106,7 @@ obj-$(CONFIG_ARCH_SHMOBILE)	+= sh/
 ifndef CONFIG_ARCH_USES_GETTIMEOFFSET
 obj-y				+= clocksource/
 endif
+obj-$(CONFIG_DMA_ENGINE)       += dma/
 obj-$(CONFIG_DCA)		+= dca/
 obj-$(CONFIG_HID)		+= hid/
 obj-$(CONFIG_PPC_PS3)		+= ps3/
diff --git a/drivers/usb/Kconfig b/drivers/usb/Kconfig
index fceea5e..fa8b879 100644
--- a/drivers/usb/Kconfig
+++ b/drivers/usb/Kconfig
@@ -56,7 +56,6 @@ config USB_ARCH_HAS_OHCI
 config USB_ARCH_HAS_EHCI
 	boolean
 	default y if PPC_83xx
-	default y if PPC_MPC512x
 	default y if SOC_AU1200
 	default y if ARCH_IXP4XX
 	default y if ARCH_W90X900
@@ -116,6 +115,8 @@ source "drivers/usb/host/Kconfig"
 
 source "drivers/usb/musb/Kconfig"
 
+source "drivers/usb/dwc_otg/Kconfig"
+
 source "drivers/usb/class/Kconfig"
 
 source "drivers/usb/storage/Kconfig"
diff --git a/drivers/usb/dwc_otg/Kconfig b/drivers/usb/dwc_otg/Kconfig
new file mode 100644
index 0000000..4d33d72
--- /dev/null
+++ b/drivers/usb/dwc_otg/Kconfig
@@ -0,0 +1,96 @@
+#
+# USB Dual Role (OTG-ready) Controller Drivers
+# for silicon based on Synopsys DesignWare IP
+#
+
+comment "Enable Host or Gadget support for DesignWare OTG controller"
+	depends on !USB && USB_GADGET=n
+
+config USB_DWC_OTG
+	depends on (USB || USB_GADGET)
+	select NOP_USB_XCEIV
+	select USB_OTG_UTILS
+	tristate "Synopsys DWC OTG Controller"
+	default USB_GADGET
+	help
+	  This driver provides USB Device Controller support for the
+	  Synopsys DesignWare USB OTG Core used on the AppliedMicro PowerPC SoC.
+
+config DWC_DEBUG
+	bool "Enable DWC Debugging"
+	depends on USB_DWC_OTG
+	default n
+	help
+	  Enable DWC driver debugging
+
+choice
+	prompt "DWC Mode Selection"
+	depends on USB_DWC_OTG
+	default DWC_HOST_ONLY
+	help
+	  Select the DWC Core in OTG, Host only, or Device only mode.
+
+config DWC_HOST_ONLY
+	bool "DWC Host Only Mode"
+
+config DWC_OTG_MODE
+	bool "DWC OTG Mode"
+	select USB_GADGET_SELECTED
+
+config DWC_DEVICE_ONLY
+	bool "DWC Device Only Mode"
+	select USB_GADGET_SELECTED
+
+endchoice
+
+# enable peripheral support (including with OTG)
+config USB_GADGET_DWC_HDRC
+	bool
+	depends on USB_DWC_OTG && (DWC_DEVICE_ONLY || USB_DWC_OTG)
+
+choice
+	prompt "DWC DMA/SlaveMode Selection"
+	depends on USB_DWC_OTG
+	default DWC_DMA_MODE
+	help
+	  Select the DWC DMA or Slave Mode.
+	  DMA mode uses the DWC core internal DMA engines.
+	  Slave mode uses the processor PIO to tranfer data.
+	  In Slave mode, processor's DMA channels can be used if available.
+
+config DWC_SLAVE
+	bool "DWC Slave Mode"
+
+config DWC_DMA_MODE
+	bool "DWC DMA Mode"
+
+endchoice
+
+config USB_OTG_WHITELIST
+	bool "Rely on OTG Targeted Peripherals List"
+	depends on !USB_SUSPEND && USB_DWC_OTG
+	default n
+	help
+	  This is the same flag as in ../core/Kconfig.
+	  It is here for easy deselect.
+
+config DWC_OTG_REG_LE
+	depends on USB_DWC_OTG
+	bool "DWC Little Endian Register"
+	default y
+	help
+	  OTG core register access is Little-Endian.
+
+config DWC_OTG_FIFO_LE
+	depends on USB_DWC_OTG
+	bool "DWC FIFO Little Endian"
+	default n
+	help
+	  OTG core FIFO access is Little-Endian.
+
+config DWC_LIMITED_XFER_SIZE
+	depends on USB_GADGET_DWC_HDRC
+	bool "DWC Endpoint Limited Xfer Size"
+	default n
+	help
+	  Bit fields in the Device EP Transfer Size Register is 11 bits.
diff --git a/drivers/usb/dwc_otg/Makefile b/drivers/usb/dwc_otg/Makefile
new file mode 100644
index 0000000..31dd5e8
--- /dev/null
+++ b/drivers/usb/dwc_otg/Makefile
@@ -0,0 +1,19 @@
+#
+# OTG infrastructure and transceiver drivers
+#
+obj-$(CONFIG_USB_DWC_OTG)	+= dwc_otg.o
+
+dwc_otg-objs := dwc_otg_cil.o dwc_otg_cil_intr.o dwc_otg_param.o
+
+ifeq ($(CONFIG_4xx_SOC),y)
+dwc_otg-objs += dwc_otg_apmppc.o
+endif
+
+ifneq ($(CONFIG_DWC_DEVICE_ONLY),y)
+dwc_otg-objs += dwc_otg_hcd.o dwc_otg_hcd_intr.o \
+		dwc_otg_hcd_queue.o
+endif
+
+ifneq ($(CONFIG_DWC_HOST_ONLY),y)
+dwc_otg-objs += dwc_otg_pcd.o dwc_otg_pcd_intr.o
+endif
-- 
1.6.1.rc3

^ permalink raw reply related

* [PATCH V7 09/10] USB/ppc4xx:Synopsys DWC OTG driver enable gadget support
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>

Enable gadget support

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/gadget/Kconfig        |   22 ++++++++++++++++++++++
 drivers/usb/gadget/gadget_chips.h |    9 +++++++++
 2 files changed, 31 insertions(+), 0 deletions(-)

diff --git a/drivers/usb/gadget/Kconfig b/drivers/usb/gadget/Kconfig
index 1dc9739..ab7d210 100644
--- a/drivers/usb/gadget/Kconfig
+++ b/drivers/usb/gadget/Kconfig
@@ -365,6 +365,28 @@ config USB_GADGET_MUSB_HDRC
 	  This OTG-capable silicon IP is used in dual designs including
 	  the TI DaVinci, OMAP 243x, OMAP 343x, TUSB 6010, and ADI Blackfin
 
+# dwc_otg builds in ../dwc_otg along with host support
+config USB_GADGET_DWC_HDRC
+	boolean "DesignWare USB Peripheral"
+	depends on DWC_OTG_MODE || DWC_DEVICE_ONLY
+	select USB_GADGET_DUALSPEED
+	select USB_GADGET_SELECTED
+	select USB_GADGET_DWC_OTG
+	help
+	This OTG-capable Designware USB IP
+
+config USB_GADGET_DWC_OTG
+	boolean "OTG Support"
+	depends on USB_GADGET_DWC_HDRC
+	help
+	The most notable feature of USB OTG is support for a
+	"Dual-Role" device, which can act as either a device
+	or a host.  The initial role choice can be changed
+	later, when two dual-role devices talk to each other.
+	Select this only if your board has a Mini-AB connector.
+
+
+
 config USB_GADGET_M66592
 	boolean "Renesas M66592 USB Peripheral Controller"
 	select USB_GADGET_DUALSPEED
diff --git a/drivers/usb/gadget/gadget_chips.h b/drivers/usb/gadget/gadget_chips.h
index 5c2720d..7a7623a 100644
--- a/drivers/usb/gadget/gadget_chips.h
+++ b/drivers/usb/gadget/gadget_chips.h
@@ -142,6 +142,12 @@
 #define gadget_is_s3c_hsotg(g)    0
 #endif
 
+#if defined(CONFIG_DWC_OTG_MODE) || defined(CONFIG_DWC_DEVICE_ONLY)
+#define gadget_is_dwc_otg_pcd(g)	(!strcmp("dwc_otg_pcd", (g)->name))
+#else
+#define gadget_is_dwc_otg_pcd(g)	0
+#endif
+
 #ifdef CONFIG_USB_GADGET_EG20T
 #define	gadget_is_pch(g)	(!strcmp("pch_udc", (g)->name))
 #else
@@ -215,6 +221,9 @@ static inline int usb_gadget_controller_number(struct usb_gadget *gadget)
 		return 0x27;
 	else if (gadget_is_ci13xxx_msm(gadget))
 		return 0x28;
+	else if (gadget_is_dwc_otg_pcd(gadget))
+		return 0x29;
+
 	return -ENOENT;
 }
 
-- 
1.6.1.rc3

^ permalink raw reply related

* [PATCH V7 08/10] USB ppc4xx: Add Synopsys DWC OTG PCD 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 the DWC OTG PCD 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_pcd_intr.c | 2311 ++++++++++++++++++++++++++++++++
 1 files changed, 2311 insertions(+), 0 deletions(-)

diff --git a/drivers/usb/dwc_otg/dwc_otg_pcd_intr.c b/drivers/usb/dwc_otg/dwc_otg_pcd_intr.c
new file mode 100644
index 0000000..682bc27
--- /dev/null
+++ b/drivers/usb/dwc_otg/dwc_otg_pcd_intr.c
@@ -0,0 +1,2311 @@
+/*
+ * 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.
+ *
+ */
+
+#include "dwc_otg_driver.h"
+#include "dwc_otg_pcd.h"
+
+/**
+ * This function returns pointer to in ep struct with number num
+ */
+static struct pcd_ep *get_in_ep(struct dwc_pcd *pcd, u32 num)
+{
+	if (num == 0) {
+		return &pcd->ep0;
+	} else {
+		u32 i;
+		int num_in_eps = GET_CORE_IF(pcd)->dev_if->num_in_eps;
+
+		for (i = 0; i < num_in_eps; ++i) {
+			if (pcd->in_ep[i].dwc_ep.num == num)
+				return &pcd->in_ep[i];
+		}
+	}
+	return NULL;
+}
+
+/**
+ * This function returns pointer to out ep struct with number num
+ */
+static struct pcd_ep *get_out_ep(struct dwc_pcd *pcd, u32 num)
+{
+	if (num == 0) {
+		return &pcd->ep0;
+	} else {
+		u32 i;
+		int num_out_eps = GET_CORE_IF(pcd)->dev_if->num_out_eps;
+
+		for (i = 0; i < num_out_eps; ++i) {
+			if (pcd->out_ep[i].dwc_ep.num == num)
+				return &pcd->out_ep[i];
+		}
+	}
+	return NULL;
+}
+
+/**
+ * This functions gets a pointer to an EP from the wIndex address
+ * value of the control request.
+ */
+static struct pcd_ep *get_ep_by_addr(struct dwc_pcd *pcd, u16 index)
+{
+	struct pcd_ep *ep;
+
+	if (!(index & USB_ENDPOINT_NUMBER_MASK))
+		return &pcd->ep0;
+
+	list_for_each_entry(ep, &pcd->gadget.ep_list, ep.ep_list) {
+		u8 bEndpointAddress;
+
+		if (!ep->desc)
+			continue;
+
+		bEndpointAddress = ep->desc->bEndpointAddress;
+		if ((index ^ bEndpointAddress) & USB_DIR_IN)
+			continue;
+
+		if ((index & 0x0f) == (bEndpointAddress & 0x0f))
+			return ep;
+	}
+	return NULL;
+}
+
+/**
+ * This function checks the EP request queue, if the queue is not
+ * empty the next request is started.
+ */
+void start_next_request(struct pcd_ep *ep)
+{
+	if (!list_empty(&ep->queue)) {
+		struct pcd_request *req;
+
+		req = list_entry(ep->queue.next, struct pcd_request, queue);
+
+		/* Setup and start the Transfer */
+		ep->dwc_ep.start_xfer_buff = req->req.buf;
+		ep->dwc_ep.xfer_buff = req->req.buf;
+		ep->dwc_ep.xfer_len = req->req.length;
+		ep->dwc_ep.xfer_count = 0;
+		ep->dwc_ep.dma_addr = req->req.dma;
+		ep->dwc_ep.sent_zlp = 0;
+		ep->dwc_ep.total_len = ep->dwc_ep.xfer_len;
+
+		/*
+		 * Added-sr: 2007-07-26
+		 *
+		 * When a new transfer will be started, mark this
+		 * endpoint as active. This way it will be blocked
+		 * for further transfers, until the current transfer
+		 * is finished.
+		 */
+		if (dwc_has_feature(GET_CORE_IF(ep->pcd), DWC_LIMITED_XFER))
+			ep->dwc_ep.active = 1;
+
+		dwc_otg_ep_start_transfer(GET_CORE_IF(ep->pcd), &ep->dwc_ep);
+	}
+}
+
+/**
+ * This function handles the SOF Interrupts. At this time the SOF
+ * Interrupt is disabled.
+ */
+static int dwc_otg_pcd_handle_sof_intr(struct dwc_pcd *pcd)
+{
+	struct core_if *core_if = GET_CORE_IF(pcd);
+	u32 gintsts;
+
+	/* Clear interrupt */
+	gintsts = 0;
+	gintsts |= DWC_INTMSK_STRT_OF_FRM;
+	dwc_write32((u32) (core_if->core_global_regs) + DWC_GINTSTS, gintsts);
+	return 1;
+}
+
+/**
+ * This function reads the 8 bytes of the setup packet from the Rx FIFO into the
+ * destination buffer.  It is called from the Rx Status Queue Level (RxStsQLvl)
+ * interrupt routine when a SETUP packet has been received in Slave mode.
+ */
+static void dwc_otg_read_setup_packet(struct core_if *core_if, u32 * dest)
+{
+	dest[0] = dwc_read_datafifo32(core_if->data_fifo[0]);
+	dest[1] = dwc_read_datafifo32(core_if->data_fifo[0]);
+}
+
+/**
+ * This function handles the Rx Status Queue Level Interrupt, which
+ * indicates that there is a least one packet in the Rx FIFO.  The
+ * packets are moved from the FIFO to memory, where they will be
+ * processed when the Endpoint Interrupt Register indicates Transfer
+ * Complete or SETUP Phase Done.
+ *
+ * Repeat the following until the Rx Status Queue is empty:
+ *	 -# Read the Receive Status Pop Register (GRXSTSP) to get Packet
+ *		info
+ *	 -# If Receive FIFO is empty then skip to step Clear the interrupt
+ *		and exit
+ *	 -# If SETUP Packet call dwc_otg_read_setup_packet to copy the
+ *		SETUP data to the buffer
+ *	 -# If OUT Data Packet call dwc_otg_read_packet to copy the data
+ *		to the destination buffer
+ */
+static int dwc_otg_pcd_handle_rx_status_q_level_intr(struct dwc_pcd *pcd)
+{
+	struct core_if *core_if = GET_CORE_IF(pcd);
+	u32 global_regs = (u32) core_if->core_global_regs;
+	u32 gintmask = 0;
+	u32 grxsts;
+	struct pcd_ep *ep;
+	u32 gintsts;
+
+	/* Disable the Rx Status Queue Level interrupt */
+	gintmask |= DWC_INTMSK_RXFIFO_NOT_EMPT;
+	dwc_modify32(global_regs + DWC_GINTMSK, gintmask, 0);
+
+	/* Get the Status from the top of the FIFO */
+	grxsts = dwc_read32(global_regs + DWC_GRXSTSP);
+
+	/* Get pointer to EP structure */
+	ep = get_out_ep(pcd, DWC_DM_RXSTS_CHAN_NUM_RD(grxsts));
+
+	switch (DWC_DM_RXSTS_PKT_STS_RD(grxsts)) {
+	case DWC_DSTS_GOUT_NAK:
+		break;
+	case DWC_STS_DATA_UPDT:
+		if ((grxsts & DWC_DM_RXSTS_BYTE_CNT) && ep->dwc_ep.xfer_buff) {
+			dwc_otg_read_packet(core_if, ep->dwc_ep.xfer_buff,
+					    DWC_DM_RXSTS_BYTE_CNT_RD(grxsts));
+			ep->dwc_ep.xfer_count +=
+			    DWC_DM_RXSTS_BYTE_CNT_RD(grxsts);
+			ep->dwc_ep.xfer_buff +=
+			    DWC_DM_RXSTS_BYTE_CNT_RD(grxsts);
+		}
+		break;
+	case DWC_STS_XFER_COMP:
+		break;
+	case DWC_DSTS_SETUP_COMP:
+		break;
+	case DWC_DSTS_SETUP_UPDT:
+		dwc_otg_read_setup_packet(core_if, pcd->setup_pkt->d32);
+		ep->dwc_ep.xfer_count += DWC_DM_RXSTS_BYTE_CNT_RD(grxsts);
+		break;
+	default:
+		pr_err("RX_STS_Q Interrupt: Unknown status %d\n",
+		       DWC_HM_RXSTS_PKT_STS_RD(grxsts));
+		break;
+	}
+
+	/* Enable the Rx Status Queue Level interrupt */
+	dwc_modify32(global_regs + DWC_GINTMSK, 0, gintmask);
+
+	/* Clear interrupt */
+	gintsts = 0;
+	gintsts |= DWC_INTSTS_RXFIFO_NOT_EMPT;
+	dwc_write32(global_regs + DWC_GINTSTS, gintsts);
+
+	return 1;
+}
+
+/**
+ * This function examines the Device IN Token Learning Queue to
+ * determine the EP number of the last IN token received.  This
+ * implementation is for the Mass Storage device where there are only
+ * 2 IN EPs (Control-IN and BULK-IN).
+ *
+ * The EP numbers for the first six IN Tokens are in DTKNQR1 and there
+ * are 8 EP Numbers in each of the other possible DTKNQ Registers.
+ */
+static int get_ep_of_last_in_token(struct core_if *core_if)
+{
+	u32 regs = (u32) core_if->dev_if->dev_global_regs;
+	const u32 TOKEN_Q_DEPTH =
+	    DWC_HWCFG2_DEV_TKN_Q_DEPTH_RD(core_if->hwcfg2);
+	/* Number of Token Queue Registers */
+	const int DTKNQ_REG_CNT = (TOKEN_Q_DEPTH + 7) / 8;
+	u32 dtknqr1 = 0;
+	u32 in_tkn_epnums[4];
+	int ndx;
+	u32 i;
+	u32 addr = regs + DWC_DTKNQR1;
+	int epnum = 0;
+
+	/* Read the DTKNQ Registers */
+	for (i = 0; i <= DTKNQ_REG_CNT; i++) {
+		in_tkn_epnums[i] = dwc_read32(addr);
+
+		if (addr == (regs + DWC_DVBUSDIS))
+			addr = regs + DWC_DTKNQR3_DTHRCTL;
+		else
+			++addr;
+	}
+
+	/* Copy the DTKNQR1 data to the bit field. */
+	dtknqr1 = in_tkn_epnums[0];
+
+	/* Get the EP numbers */
+	in_tkn_epnums[0] = DWC_DTKNQR1_EP_TKN_NO_RD(dtknqr1);
+	ndx = DWC_DTKNQR1_INT_TKN_Q_WR_PTR_RD(dtknqr1) - 1;
+
+	if (ndx == -1) {
+		/*
+		 * Calculate the max queue position.
+		 */
+		int cnt = TOKEN_Q_DEPTH;
+
+		if (TOKEN_Q_DEPTH <= 6)
+			cnt = TOKEN_Q_DEPTH - 1;
+		else if (TOKEN_Q_DEPTH <= 14)
+			cnt = TOKEN_Q_DEPTH - 7;
+		else if (TOKEN_Q_DEPTH <= 22)
+			cnt = TOKEN_Q_DEPTH - 15;
+		else
+			cnt = TOKEN_Q_DEPTH - 23;
+
+		epnum = (in_tkn_epnums[DTKNQ_REG_CNT - 1] >> (cnt * 4)) & 0xF;
+	} else {
+		if (ndx <= 5) {
+			epnum = (in_tkn_epnums[0] >> (ndx * 4)) & 0xF;
+		} else if (ndx <= 13) {
+			ndx -= 6;
+			epnum = (in_tkn_epnums[1] >> (ndx * 4)) & 0xF;
+		} else if (ndx <= 21) {
+			ndx -= 14;
+			epnum = (in_tkn_epnums[2] >> (ndx * 4)) & 0xF;
+		} else if (ndx <= 29) {
+			ndx -= 22;
+			epnum = (in_tkn_epnums[3] >> (ndx * 4)) & 0xF;
+		}
+	}
+
+	return epnum;
+}
+
+static inline int count_dwords(struct pcd_ep *ep, u32 len)
+{
+	if (len > ep->dwc_ep.maxpacket)
+		len = ep->dwc_ep.maxpacket;
+	return (len + 3) / 4;
+}
+
+/**
+ * This function writes a packet into the Tx FIFO associated with the EP.  For
+ * non-periodic EPs the non-periodic Tx FIFO is written.  For periodic EPs the
+ * periodic Tx FIFO associated with the EP is written with all packets for the
+ * next micro-frame.
+ *
+ * The buffer is padded to DWORD on a per packet basis in
+ * slave/dma mode if the MPS is not DWORD aligned.  The last packet, if
+ * short, is also padded to a multiple of DWORD.
+ *
+ * ep->xfer_buff always starts DWORD aligned in memory and is a
+ * multiple of DWORD in length
+ *
+ * ep->xfer_len can be any number of bytes
+ *
+ * ep->xfer_count is a multiple of ep->maxpacket until the last packet
+ *
+ * FIFO access is DWORD
+ */
+static void dwc_otg_ep_write_packet(struct core_if *core_if, struct dwc_ep *ep,
+				    int dma)
+{
+	u32 i;
+	u32 byte_count;
+	u32 dword_count;
+	u32 *fifo;
+	u32 *data_buff = (u32 *) ep->xfer_buff;
+
+	if (ep->xfer_count >= ep->xfer_len)
+		return;
+
+	/* Find the byte length of the packet either short packet or MPS */
+	if ((ep->xfer_len - ep->xfer_count) < ep->maxpacket)
+		byte_count = ep->xfer_len - ep->xfer_count;
+	else
+		byte_count = ep->maxpacket;
+
+	/*
+	 * Find the DWORD length, padded by extra bytes as neccessary if MPS
+	 * is not a multiple of DWORD
+	 */
+	dword_count = (byte_count + 3) / 4;
+
+	fifo = core_if->data_fifo[ep->num];
+
+	if (!dma)
+		for (i = 0; i < dword_count; i++, data_buff++)
+			dwc_write_datafifo32(fifo, *data_buff);
+
+	ep->xfer_count += byte_count;
+	ep->xfer_buff += byte_count;
+	ep->dma_addr += byte_count;
+}
+
+/**
+ * This interrupt occurs when the non-periodic Tx FIFO is half-empty.
+ * The active request is checked for the next packet to be loaded into
+ * the non-periodic Tx FIFO.
+ */
+static int dwc_otg_pcd_handle_np_tx_fifo_empty_intr(struct dwc_pcd *pcd)
+{
+	struct core_if *core_if = GET_CORE_IF(pcd);
+	u32 global_regs = (u32) core_if->core_global_regs;
+	u32 txstatus = 0;
+	u32 gintsts = 0;
+	int epnum;
+	struct pcd_ep *ep;
+	u32 len;
+	int dwords;
+
+	/* Get the epnum from the IN Token Learning Queue. */
+	epnum = get_ep_of_last_in_token(core_if);
+	ep = get_in_ep(pcd, epnum);
+
+	txstatus = dwc_read32(global_regs + DWC_GNPTXSTS);
+
+	/*
+	 * While there is space in the queue, space in the FIFO, and data to
+	 * tranfer, write packets to the Tx FIFO
+	 */
+	len = ep->dwc_ep.xfer_len - ep->dwc_ep.xfer_count;
+	dwords = count_dwords(ep, len);
+	while ((DWC_GNPTXSTS_NPTXQSPCAVAIL_RD(txstatus) > 0) &&
+	       (DWC_GNPTXSTS_NPTXFSPCAVAIL_RD(txstatus) > dwords) &&
+	       ep->dwc_ep.xfer_count < ep->dwc_ep.xfer_len) {
+		/*
+		 * Added-sr: 2007-07-26
+		 *
+		 * When a new transfer will be started, mark this
+		 * endpoint as active. This way it will be blocked
+		 * for further transfers, until the current transfer
+		 * is finished.
+		 */
+		if (dwc_has_feature(core_if, DWC_LIMITED_XFER))
+			ep->dwc_ep.active = 1;
+
+		dwc_otg_ep_write_packet(core_if, &ep->dwc_ep, 0);
+		len = ep->dwc_ep.xfer_len - ep->dwc_ep.xfer_count;
+		dwords = count_dwords(ep, len);
+		txstatus = dwc_read32(global_regs + DWC_GNPTXSTS);
+	}
+
+	/* Clear nptxfempty interrupt */
+	gintsts |= DWC_INTMSK_RXFIFO_NOT_EMPT;
+	dwc_write32(global_regs + DWC_GINTSTS, gintsts);
+
+	/* Re-enable tx-fifo empty interrupt, if packets are stil pending */
+	if (len)
+		dwc_modify32(global_regs + DWC_GINTSTS, 0, gintsts);
+	return 1;
+}
+
+/**
+ * This function is called when dedicated Tx FIFO Empty interrupt occurs.
+ * The active request is checked for the next packet to be loaded into
+ * apropriate Tx FIFO.
+ */
+static int write_empty_tx_fifo(struct dwc_pcd *pcd, u32 epnum)
+{
+	struct core_if *core_if = GET_CORE_IF(pcd);
+	u32 regs;
+	u32 txstatus = 0;
+	struct pcd_ep *ep;
+	u32 len;
+	int dwords;
+	u32 diepint = 0;
+
+	ep = get_in_ep(pcd, epnum);
+	regs = (u32) core_if->dev_if->in_ep_regs[epnum];
+	txstatus = dwc_read32(regs + DWC_DTXFSTS);
+
+	/*
+	 * While there is space in the queue, space in the FIFO and data to
+	 * tranfer, write packets to the Tx FIFO
+	 */
+	len = ep->dwc_ep.xfer_len - ep->dwc_ep.xfer_count;
+	dwords = count_dwords(ep, len);
+	while (DWC_DTXFSTS_TXFSSPC_AVAI_RD(txstatus) > dwords
+	       && ep->dwc_ep.xfer_count < ep->dwc_ep.xfer_len
+	       && ep->dwc_ep.xfer_len != 0) {
+		dwc_otg_ep_write_packet(core_if, &ep->dwc_ep, 0);
+		len = ep->dwc_ep.xfer_len - ep->dwc_ep.xfer_count;
+		dwords = count_dwords(ep, len);
+		txstatus = dwc_read32(regs + DWC_DTXFSTS);
+	}
+	/* Clear emptyintr */
+	diepint = DWC_DIEPINT_TXFIFO_EMPTY_RW(diepint, 1);
+	dwc_write32(in_ep_int_reg(pcd, epnum), diepint);
+	return 1;
+}
+
+/**
+ * This function is called when the Device is disconnected.  It stops any active
+ * requests and informs the Gadget driver of the disconnect.
+ */
+void dwc_otg_pcd_stop(struct dwc_pcd *pcd)
+{
+	int i, num_in_eps, num_out_eps;
+	struct pcd_ep *ep;
+	u32 intr_mask = 0;
+	u32 global_regs = (u32) GET_CORE_IF(pcd)->core_global_regs;
+
+	num_in_eps = GET_CORE_IF(pcd)->dev_if->num_in_eps;
+	num_out_eps = GET_CORE_IF(pcd)->dev_if->num_out_eps;
+
+	/* Don't disconnect drivers more than once */
+	if (pcd->ep0state == EP0_DISCONNECT)
+		return;
+	pcd->ep0state = EP0_DISCONNECT;
+
+	/* Reset the OTG state. */
+	dwc_otg_pcd_update_otg(pcd, 1);
+
+	/* Disable the NP Tx Fifo Empty Interrupt. */
+	intr_mask |= DWC_INTMSK_NP_TXFIFO_EMPT;
+	dwc_modify32(global_regs + DWC_GINTMSK, intr_mask, 0);
+
+	/* Flush the FIFOs */
+	dwc_otg_flush_tx_fifo(GET_CORE_IF(pcd), 0);
+	dwc_otg_flush_rx_fifo(GET_CORE_IF(pcd));
+
+	/* Prevent new request submissions, kill any outstanding requests  */
+	ep = &pcd->ep0;
+	request_nuke(ep);
+
+	/* Prevent new request submissions, kill any outstanding requests  */
+	for (i = 0; i < num_in_eps; i++)
+		request_nuke((struct pcd_ep *)&pcd->in_ep[i]);
+
+	/* Prevent new request submissions, kill any outstanding requests  */
+	for (i = 0; i < num_out_eps; i++)
+		request_nuke((struct pcd_ep *)&pcd->out_ep[i]);
+
+	/* Report disconnect; the driver is already quiesced */
+	if (pcd->driver && pcd->driver->disconnect) {
+		spin_unlock(&pcd->lock);
+		pcd->driver->disconnect(&pcd->gadget);
+		spin_lock(&pcd->lock);
+	}
+}
+
+/**
+ * This interrupt indicates that ...
+ */
+static int dwc_otg_pcd_handle_i2c_intr(struct dwc_pcd *pcd)
+{
+	u32 intr_mask = 0;
+	u32 gintsts;
+
+	pr_info("Interrupt handler not implemented for i2cintr\n");
+
+	/* Turn off and clean the interrupt */
+	intr_mask |= DWC_INTMSK_I2C_INTR;
+	dwc_modify32((u32) (GET_CORE_IF(pcd)->core_global_regs) + DWC_GINTMSK,
+		     intr_mask, 0);
+
+	gintsts = 0;
+	gintsts |= DWC_INTSTS_I2C_INTR;
+	dwc_write32((u32) (GET_CORE_IF(pcd)->core_global_regs) + DWC_GINTSTS,
+		    gintsts);
+
+	return 1;
+}
+
+/**
+ * This interrupt indicates that ...
+ */
+static int dwc_otg_pcd_handle_early_suspend_intr(struct dwc_pcd *pcd)
+{
+	u32 intr_mask = 0;
+	u32 gintsts;
+
+	pr_info("Early Suspend Detected\n");
+
+	/* Turn off and clean the interrupt */
+	intr_mask |= DWC_INTMSK_EARLY_SUSP;
+	dwc_modify32((u32) (GET_CORE_IF(pcd)->core_global_regs) + DWC_GINTMSK,
+		     intr_mask, 0);
+
+	gintsts = 0;
+	gintsts |= DWC_INTSTS_EARLY_SUSP;
+	dwc_write32((u32) (GET_CORE_IF(pcd)->core_global_regs) + DWC_GINTSTS,
+		    gintsts);
+
+	return 1;
+}
+
+/**
+ * This function configures EPO to receive SETUP packets.
+ *
+ * Program the following fields in the endpoint specific registers for Control
+ * OUT EP 0, in order to receive a setup packet:
+ *
+ * - DOEPTSIZ0.Packet Count = 3 (To receive up to 3 back to back setup packets)
+ *
+ * - DOEPTSIZE0.Transfer Size = 24 Bytes (To receive up to 3 back to back setup
+ *   packets)
+ *
+ * In DMA mode, DOEPDMA0 Register with a memory address to store any setup
+ * packets received
+ */
+static void ep0_out_start(struct core_if *core_if, struct dwc_pcd *pcd)
+{
+	struct device_if *dev_if = core_if->dev_if;
+	u32 doeptsize0 = 0;
+
+	doeptsize0 = DWC_DEPTSIZ0_SUPCNT_RW(doeptsize0, 3);
+	doeptsize0 = DWC_DEPTSIZ0_PKT_CNT_RW(doeptsize0, 1);
+	doeptsize0 = DWC_DEPTSIZ0_XFER_SIZ_RW(doeptsize0, 8 * 3);
+	dwc_write32((u32) dev_if->out_ep_regs[0] + DWC_DOEPTSIZ, doeptsize0);
+
+	if (core_if->dma_enable) {
+		u32 doepctl = 0;
+
+		dwc_write32((u32) dev_if->out_ep_regs[0] + DWC_DOEPDMA,
+			    pcd->setup_pkt_dma_handle);
+		doepctl = DWC_DEPCTL_EPENA_RW(doepctl, 1);
+		doepctl = DWC_DEPCTL_ACT_EP_RW(doepctl, 1);
+		dwc_write32(out_ep_ctl_reg(pcd, 0), doepctl);
+	}
+}
+
+/**
+ * This interrupt occurs when a USB Reset is detected.  When the USB Reset
+ * Interrupt occurs the device state is set to DEFAULT and the EP0 state is set
+ * to IDLE.
+ *
+ * Set the NAK bit for all OUT endpoints (DOEPCTLn.SNAK = 1)
+ *
+ * Unmask the following interrupt bits:
+ *  - DAINTMSK.INEP0 = 1 (Control 0 IN endpoint)
+ *  - DAINTMSK.OUTEP0 = 1 (Control 0 OUT endpoint)
+ *  - DOEPMSK.SETUP = 1
+ *  - DOEPMSK.XferCompl = 1
+ *  - DIEPMSK.XferCompl = 1
+ *  - DIEPMSK.TimeOut = 1
+ *
+ * Program the following fields in the endpoint specific registers for Control
+ * OUT EP 0, in order to receive a setup packet
+ *  - DOEPTSIZ0.Packet Count = 3 (To receive up to 3 back to back setup packets)
+ *  - DOEPTSIZE0.Transfer Size = 24 Bytes (To receive up to 3 back to back setup
+ *    packets)
+ *
+ *  - In DMA mode, DOEPDMA0 Register with a memory address to store any setup
+ *    packets received
+ *
+ * At this point, all the required initialization, except for enabling
+ * the control 0 OUT endpoint is done, for receiving SETUP packets.
+ *
+ * Note that the bits in the Device IN endpoint mask register (diepmsk) are laid
+ * out exactly the same as the Device IN endpoint interrupt register (diepint.)
+ * Likewise for Device OUT endpoint mask / interrupt registers (doepmsk /
+ * doepint.)
+ */
+static int dwc_otg_pcd_handle_usb_reset_intr(struct dwc_pcd *pcd)
+{
+	struct core_if *core_if = GET_CORE_IF(pcd);
+	struct device_if *dev_if = core_if->dev_if;
+	u32 doepctl = 0;
+	u32 daintmsk = 0;
+	u32 doepmsk = 0;
+	u32 diepmsk = 0;
+	u32 dcfg = 0;
+	u32 resetctl = 0;
+	u32 dctl = 0;
+	u32 i;
+	u32 gintsts = 0;
+
+	pr_info("USB RESET\n");
+
+	/* reset the HNP settings */
+	dwc_otg_pcd_update_otg(pcd, 1);
+
+	/* Clear the Remote Wakeup Signalling */
+	dctl = DEC_DCTL_REMOTE_WAKEUP_SIG(dctl, 1);
+	dwc_modify32(dev_ctl_reg(pcd), dctl, 0);
+
+	/* Set NAK for all OUT EPs */
+	doepctl = DWC_DEPCTL_SET_NAK_RW(doepctl, 1);
+	for (i = 0; i <= dev_if->num_out_eps; i++)
+		dwc_write32(out_ep_ctl_reg(pcd, i), doepctl);
+
+	/* Flush the NP Tx FIFO */
+	dwc_otg_flush_tx_fifo(core_if, 0);
+
+	/* Flush the Learning Queue */
+	resetctl |= DWC_RSTCTL_TKN_QUE_FLUSH;
+	dwc_write32((u32) core_if->core_global_regs + DWC_GRSTCTL, resetctl);
+
+	daintmsk |= DWC_DAINT_INEP00;
+	daintmsk |= DWC_DAINT_OUTEP00;
+	dwc_write32((u32) dev_if->dev_global_regs + DWC_DAINTMSK, daintmsk);
+
+	doepmsk = DWC_DOEPMSK_SETUP_DONE_RW(doepmsk, 1);
+	doepmsk = DWC_DOEPMSK_AHB_ERROR_RW(doepmsk, 1);
+	doepmsk = DWC_DOEPMSK_EP_DISA_RW(doepmsk, 1);
+	doepmsk = DWC_DOEPMSK_TX_COMPL_RW(doepmsk, 1);
+	dwc_write32((u32) dev_if->dev_global_regs + DWC_DOEPMSK, doepmsk);
+
+	diepmsk = DWC_DIEPMSK_TX_CMPL_RW(diepmsk, 1);
+	diepmsk = DWC_DIEPMSK_TOUT_COND_RW(diepmsk, 1);
+	diepmsk = DWC_DIEPMSK_EP_DISA_RW(diepmsk, 1);
+	diepmsk = DWC_DIEPMSK_AHB_ERROR_RW(diepmsk, 1);
+	diepmsk = DWC_DIEPMSK_IN_TKN_TX_EMPTY_RW(diepmsk, 1);
+	dwc_write32((u32) dev_if->dev_global_regs + DWC_DIEPMSK, diepmsk);
+
+	/* Reset Device Address */
+	dcfg = dwc_read32((u32) dev_if->dev_global_regs + DWC_DCFG);
+	dcfg = DWC_DCFG_DEV_ADDR_WR(dcfg, 0);
+	dwc_write32((u32) dev_if->dev_global_regs + DWC_DCFG, dcfg);
+
+	/* setup EP0 to receive SETUP packets */
+	ep0_out_start(core_if, pcd);
+
+	/* Clear interrupt */
+	gintsts = 0;
+	gintsts |= DWC_INTSTS_USB_RST;
+	dwc_write32((u32) (core_if->core_global_regs) + DWC_GINTSTS, gintsts);
+
+	return 1;
+}
+
+/**
+ * Get the device speed from the device status register and convert it
+ * to USB speed constant.
+ */
+static int get_device_speed(struct dwc_pcd *pcd)
+{
+	u32 dsts = 0;
+	enum usb_device_speed speed = USB_SPEED_UNKNOWN;
+
+	dsts = dwc_read32(dev_sts_reg(pcd));
+
+	switch (DWC_DSTS_ENUM_SPEED_RD(dsts)) {
+	case DWC_DSTS_ENUMSPD_HS_PHY_30MHZ_OR_60MHZ:
+		speed = USB_SPEED_HIGH;
+		break;
+	case DWC_DSTS_ENUMSPD_FS_PHY_30MHZ_OR_60MHZ:
+	case DWC_DSTS_ENUMSPD_FS_PHY_48MHZ:
+		speed = USB_SPEED_FULL;
+		break;
+	case DWC_DSTS_ENUMSPD_LS_PHY_6MHZ:
+		speed = USB_SPEED_LOW;
+		break;
+	}
+	return speed;
+}
+
+/**
+ * This function enables EP0 OUT to receive SETUP packets and configures EP0
+ * IN for transmitting packets.  It is normally called when the "Enumeration
+ * Done" interrupt occurs.
+ */
+static void dwc_otg_ep0_activate(struct core_if *core_if, struct dwc_ep *ep)
+{
+	struct device_if *dev_if = core_if->dev_if;
+	u32 dsts;
+	u32 diepctl = 0;
+	u32 doepctl = 0;
+	u32 dctl = 0;
+
+	/* Read the Device Status and Endpoint 0 Control registers */
+	dsts = dwc_read32((u32) dev_if->dev_global_regs + DWC_DSTS);
+	diepctl = dwc_read32((u32) dev_if->in_ep_regs[0] + DWC_DIEPCTL);
+	doepctl = dwc_read32((u32) dev_if->out_ep_regs[0] + DWC_DOEPCTL);
+
+	/* Set the MPS of the IN EP based on the enumeration speed */
+	switch (DWC_DSTS_ENUM_SPEED_RD(dsts)) {
+	case DWC_DSTS_ENUMSPD_HS_PHY_30MHZ_OR_60MHZ:
+	case DWC_DSTS_ENUMSPD_FS_PHY_30MHZ_OR_60MHZ:
+	case DWC_DSTS_ENUMSPD_FS_PHY_48MHZ:
+		diepctl = DWC_DEPCTL_MPS_RW(diepctl, DWC_DEP0CTL_MPS_64);
+		break;
+	case DWC_DSTS_ENUMSPD_LS_PHY_6MHZ:
+		diepctl = DWC_DEPCTL_MPS_RW(diepctl, DWC_DEP0CTL_MPS_8);
+		break;
+	}
+	dwc_write32((u32) dev_if->in_ep_regs[0] + DWC_DIEPCTL, diepctl);
+
+	/* Enable OUT EP for receive */
+	doepctl = DWC_DEPCTL_EPENA_RW(doepctl, 1);
+	dwc_write32((u32) dev_if->out_ep_regs[0] + DWC_DOEPCTL, doepctl);
+
+	dctl = DWC_DCTL_CLR_CLBL_NP_IN_NAK(dctl, 1);
+	dwc_modify32((u32) dev_if->dev_global_regs + DWC_DCTL, dctl, dctl);
+}
+
+/**
+ * Read the device status register and set the device speed in the
+ * data structure.
+ * Set up EP0 to receive SETUP packets by calling dwc_ep0_activate.
+ */
+static int dwc_otg_pcd_handle_enum_done_intr(struct dwc_pcd *pcd)
+{
+	struct pcd_ep *ep0 = &pcd->ep0;
+	u32 gintsts;
+	u32 gusbcfg;
+	struct core_if *core_if = GET_CORE_IF(pcd);
+	u32 global_regs = (u32) core_if->core_global_regs;
+	u32 gsnpsid = global_regs + DWC_GSNPSID;
+	u8 utmi16b, utmi8b;
+
+	if (gsnpsid >= (u32) 0x4f54260a) {
+		utmi16b = 5;
+		utmi8b = 9;
+	} else {
+		utmi16b = 4;
+		utmi8b = 8;
+	}
+	dwc_otg_ep0_activate(GET_CORE_IF(pcd), &ep0->dwc_ep);
+
+	pcd->ep0state = EP0_IDLE;
+	ep0->stopped = 0;
+	pcd->gadget.speed = get_device_speed(pcd);
+
+	gusbcfg = dwc_read32(global_regs + DWC_GUSBCFG);
+
+	/* Set USB turnaround time based on device speed and PHY interface. */
+	if (pcd->gadget.speed == USB_SPEED_HIGH) {
+		switch (DWC_HWCFG2_HS_PHY_TYPE_RD(core_if->hwcfg2)) {
+		case DWC_HWCFG2_HS_PHY_TYPE_ULPI:
+			gusbcfg =
+			    (gusbcfg & (~((u32) DWC_USBCFG_TRN_TIME(0xf)))) |
+			    DWC_USBCFG_TRN_TIME(9);
+			break;
+		case DWC_HWCFG2_HS_PHY_TYPE_UTMI:
+			if (DWC_HWCFG4_UTMI_PHY_DATA_WIDTH_RD(core_if->hwcfg4)
+			    == 0)
+				gusbcfg =
+				    (gusbcfg &
+				     (~((u32) DWC_USBCFG_TRN_TIME(0xf)))) |
+				    DWC_USBCFG_TRN_TIME(utmi8b);
+			else if (DWC_HWCFG4_UTMI_PHY_DATA_WIDTH_RD
+				 (core_if->hwcfg4) == 1)
+				gusbcfg =
+				    (gusbcfg &
+				     (~((u32) DWC_USBCFG_TRN_TIME(0xf)))) |
+				    DWC_USBCFG_TRN_TIME(utmi16b);
+			else if (core_if->core_params->phy_utmi_width == 8)
+				gusbcfg =
+				    (gusbcfg &
+				     (~((u32) DWC_USBCFG_TRN_TIME(0xf)))) |
+				    DWC_USBCFG_TRN_TIME(utmi8b);
+			else
+				gusbcfg =
+				    (gusbcfg &
+				     (~((u32) DWC_USBCFG_TRN_TIME(0xf)))) |
+				    DWC_USBCFG_TRN_TIME(utmi16b);
+			break;
+		case DWC_HWCFG2_HS_PHY_TYPE_UTMI_ULPI:
+			if (gusbcfg & DWC_USBCFG_ULPI_UTMI_SEL) {
+				gusbcfg =
+				    (gusbcfg &
+				     (~((u32) DWC_USBCFG_TRN_TIME(0xf)))) |
+				    DWC_USBCFG_TRN_TIME(9);
+			} else {
+				if (core_if->core_params->phy_utmi_width == 16)
+					gusbcfg =
+					    (gusbcfg &
+					     (~
+					      ((u32) DWC_USBCFG_TRN_TIME(0xf))))
+					    | DWC_USBCFG_TRN_TIME(utmi16b);
+				else
+					gusbcfg =
+					    (gusbcfg &
+					     (~
+					      ((u32) DWC_USBCFG_TRN_TIME(0xf))))
+					    | DWC_USBCFG_TRN_TIME(utmi8b);
+			}
+			break;
+		}
+	} else {
+		/* Full or low speed */
+		gusbcfg = (gusbcfg & (~((u32) DWC_USBCFG_TRN_TIME(0xf)))) |
+		    DWC_USBCFG_TRN_TIME(9);
+	}
+	dwc_write32(global_regs + DWC_GUSBCFG, gusbcfg);
+
+	/* Clear interrupt */
+	gintsts = 0;
+	gintsts |= DWC_INTSTS_ENUM_DONE;
+	dwc_write32((u32) (GET_CORE_IF(pcd)->core_global_regs) + DWC_GINTSTS,
+		    gintsts);
+
+	return 1;
+}
+
+/**
+ * This interrupt indicates that the ISO OUT Packet was dropped due to
+ * Rx FIFO full or Rx Status Queue Full.  If this interrupt occurs
+ * read all the data from the Rx FIFO.
+ */
+static int dwc_otg_pcd_handle_isoc_out_packet_dropped_intr(struct dwc_pcd *pcd)
+{
+	u32 intr_mask = 0;
+	u32 gintsts;
+
+	pr_info("Interrupt Handler not implemented for ISOC Out " "Dropped\n");
+
+	/* Turn off and clear the interrupt */
+	intr_mask |= DWC_INTMSK_ISYNC_OUTPKT_DRP;
+	dwc_modify32((u32) (GET_CORE_IF(pcd)->core_global_regs) + DWC_GINTMSK,
+		     intr_mask, 0);
+
+	gintsts = 0;
+	gintsts |= DWC_INTSTS_ISYNC_OUTPKT_DRP;
+	dwc_write32((u32) (GET_CORE_IF(pcd)->core_global_regs) + DWC_GINTSTS,
+		    gintsts);
+
+	return 1;
+}
+
+/**
+ * This interrupt indicates the end of the portion of the micro-frame
+ * for periodic transactions.  If there is a periodic transaction for
+ * the next frame, load the packets into the EP periodic Tx FIFO.
+ */
+static int dwc_otg_pcd_handle_end_periodic_frame_intr(struct dwc_pcd *pcd)
+{
+	u32 intr_mask = 0;
+	u32 gintsts;
+
+	pr_info("Interrupt handler not implemented for End of "
+		"Periodic Portion of Micro-Frame Interrupt");
+
+	/* Turn off and clear the interrupt */
+	intr_mask |= DWC_INTMSK_END_OF_PFRM;
+	dwc_modify32((u32) (GET_CORE_IF(pcd)->core_global_regs) + DWC_GINTMSK,
+		     intr_mask, 0);
+
+	gintsts = 0;
+	gintsts |= DWC_INTSTS_END_OF_PFRM;
+	dwc_write32((u32) (GET_CORE_IF(pcd)->core_global_regs) + DWC_GINTSTS,
+		    gintsts);
+
+	return 1;
+}
+
+/**
+ * This interrupt indicates that EP of the packet on the top of the
+ * non-periodic Tx FIFO does not match EP of the IN Token received.
+ *
+ * The "Device IN Token Queue" Registers are read to determine the
+ * order the IN Tokens have been received.  The non-periodic Tx FIFO is flushed,
+ * so it can be reloaded in the order seen in the IN Token Queue.
+ */
+static int dwc_otg_pcd_handle_ep_mismatch_intr(struct core_if *core_if)
+{
+	u32 intr_mask = 0;
+	u32 gintsts;
+
+	pr_info("Interrupt handler not implemented for End Point "
+		"Mismatch\n");
+
+	/* Turn off and clear the interrupt */
+	intr_mask |= DWC_INTMSK_ENDP_MIS_MTCH;
+	dwc_modify32((u32) (core_if->core_global_regs) + DWC_GINTMSK,
+		     intr_mask, 0);
+
+	gintsts = 0;
+	gintsts |= DWC_INTSTS_ENDP_MIS_MTCH;
+	dwc_write32((u32) (core_if->core_global_regs) + DWC_GINTSTS, gintsts);
+	return 1;
+}
+
+/**
+ * This funcion stalls EP0.
+ */
+static void ep0_do_stall(struct dwc_pcd *pcd, const int val)
+{
+	struct pcd_ep *ep0 = &pcd->ep0;
+	struct usb_ctrlrequest *ctrl = &pcd->setup_pkt->req;
+
+	pr_warning("req %02x.%02x protocol STALL; err %d\n",
+		   ctrl->bRequestType, ctrl->bRequest, val);
+
+	ep0->dwc_ep.is_in = 1;
+	dwc_otg_ep_set_stall(pcd->otg_dev->core_if, &ep0->dwc_ep);
+
+	pcd->ep0.stopped = 1;
+	pcd->ep0state = EP0_IDLE;
+	ep0_out_start(GET_CORE_IF(pcd), pcd);
+}
+
+/**
+ * This functions delegates the setup command to the gadget driver.
+ */
+static void do_gadget_setup(struct dwc_pcd *pcd, struct usb_ctrlrequest *ctrl)
+{
+	if (pcd->driver && pcd->driver->setup) {
+		int ret;
+
+		spin_unlock(&pcd->lock);
+		ret = pcd->driver->setup(&pcd->gadget, ctrl);
+		spin_lock(&pcd->lock);
+
+		if (ret < 0)
+			ep0_do_stall(pcd, ret);
+
+		/** This is a g_file_storage gadget driver specific
+		 * workaround: a DELAYED_STATUS result from the fsg_setup
+		 * routine will result in the gadget queueing a EP0 IN status
+		 * phase for a two-stage control transfer.
+		 *
+		 * Exactly the same as a SET_CONFIGURATION/SET_INTERFACE except
+		 * that this is a class specific request.  Need a generic way to
+		 * know when the gadget driver will queue the status phase.
+		 *
+		 * Can we assume when we call the gadget driver setup() function
+		 * that it will always queue and require the following flag?
+		 * Need to look into this.
+		 */
+		if (ret == 256 + 999)
+			pcd->request_config = 1;
+	}
+}
+
+/**
+ * This function starts the Zero-Length Packet for the IN status phase
+ * of a 2 stage control transfer.
+ */
+static void do_setup_in_status_phase(struct dwc_pcd *pcd)
+{
+	struct pcd_ep *ep0 = &pcd->ep0;
+
+	if (pcd->ep0state == EP0_STALL)
+		return;
+
+	pcd->ep0state = EP0_STATUS;
+
+	ep0->dwc_ep.xfer_len = 0;
+	ep0->dwc_ep.xfer_count = 0;
+	ep0->dwc_ep.is_in = 1;
+	ep0->dwc_ep.dma_addr = pcd->setup_pkt_dma_handle;
+	dwc_otg_ep0_start_transfer(GET_CORE_IF(pcd), &ep0->dwc_ep);
+
+	/* Prepare for more SETUP Packets */
+	ep0_out_start(GET_CORE_IF(pcd), pcd);
+}
+
+/**
+ * This function starts the Zero-Length Packet for the OUT status phase
+ * of a 2 stage control transfer.
+ */
+static void do_setup_out_status_phase(struct dwc_pcd *pcd)
+{
+	struct pcd_ep *ep0 = &pcd->ep0;
+
+	if (pcd->ep0state == EP0_STALL)
+		return;
+	pcd->ep0state = EP0_STATUS;
+
+	ep0->dwc_ep.xfer_len = 0;
+	ep0->dwc_ep.xfer_count = 0;
+	ep0->dwc_ep.is_in = 0;
+	ep0->dwc_ep.dma_addr = pcd->setup_pkt_dma_handle;
+	dwc_otg_ep0_start_transfer(GET_CORE_IF(pcd), &ep0->dwc_ep);
+
+	/* Prepare for more SETUP Packets */
+	ep0_out_start(GET_CORE_IF(pcd), pcd);
+}
+
+/**
+ * Clear the EP halt (STALL) and if pending requests start the
+ * transfer.
+ */
+static void pcd_clear_halt(struct dwc_pcd *pcd, struct pcd_ep *ep)
+{
+	struct core_if *core_if = GET_CORE_IF(pcd);
+
+	if (!ep->dwc_ep.stall_clear_flag)
+		dwc_otg_ep_clear_stall(core_if, &ep->dwc_ep);
+
+	/* Reactive the EP */
+	dwc_otg_ep_activate(core_if, &ep->dwc_ep);
+
+	if (ep->stopped) {
+		ep->stopped = 0;
+		/* If there is a request in the EP queue start it */
+
+		/*
+		 * start_next_request(), outside of interrupt context at some
+		 * time after the current time, after a clear-halt setup packet.
+		 * Still need to implement ep mismatch in the future if a gadget
+		 * ever uses more than one endpoint at once
+		 */
+		if (core_if->dma_enable) {
+			ep->queue_sof = 1;
+			tasklet_schedule(pcd->start_xfer_tasklet);
+		} else {
+			/*
+			 * Added-sr: 2007-07-26
+			 *
+			 * To re-enable this endpoint it's important to
+			 * set this next_ep number. Otherwise the endpoint
+			 * will not get active again after stalling.
+			 */
+			if (dwc_has_feature(core_if, DWC_LIMITED_XFER))
+				start_next_request(ep);
+		}
+	}
+
+	/* Start Control Status Phase */
+	do_setup_in_status_phase(pcd);
+}
+
+/**
+ * This function is called when the SET_FEATURE TEST_MODE Setup packet is sent
+ * from the host.  The Device Control register is written with the Test Mode
+ * bits set to the specified Test Mode.  This is done as a tasklet so that the
+ * "Status" phase of the control transfer completes before transmitting the TEST
+ * packets.
+ *
+ */
+static void do_test_mode(unsigned long data)
+{
+	u32 dctl = 0;
+	struct dwc_pcd *pcd = (struct dwc_pcd *)data;
+	int test_mode = pcd->test_mode;
+
+	dctl = dwc_read32(dev_ctl_reg(pcd));
+	switch (test_mode) {
+	case 1:		/* TEST_J */
+		dctl = DWC_DCTL_TST_CTL(dctl, 1);
+		break;
+	case 2:		/* TEST_K */
+		dctl = DWC_DCTL_TST_CTL(dctl, 2);
+		break;
+	case 3:		/* TEST_SE0_NAK */
+		dctl = DWC_DCTL_TST_CTL(dctl, 3);
+		break;
+	case 4:		/* TEST_PACKET */
+		dctl = DWC_DCTL_TST_CTL(dctl, 4);
+		break;
+	case 5:		/* TEST_FORCE_ENABLE */
+		dctl = DWC_DCTL_TST_CTL(dctl, 5);
+		break;
+	}
+	dwc_write32(dev_ctl_reg(pcd), dctl);
+}
+
+/**
+ * This function process the SET_FEATURE Setup Commands.
+ */
+static void do_set_feature(struct dwc_pcd *pcd)
+{
+	struct core_if *core_if = GET_CORE_IF(pcd);
+	u32 regs = (u32) core_if->core_global_regs;
+	struct usb_ctrlrequest ctrl = pcd->setup_pkt->req;
+	int otg_cap = core_if->core_params->otg_cap;
+	u32 gotgctl = 0;
+
+	switch (ctrl.bRequestType & USB_RECIP_MASK) {
+	case USB_RECIP_DEVICE:
+		switch (__le16_to_cpu(ctrl.wValue)) {
+		case USB_DEVICE_REMOTE_WAKEUP:
+			pcd->remote_wakeup_enable = 1;
+			break;
+		case USB_DEVICE_TEST_MODE:
+			/*
+			 * Setup the Test Mode tasklet to do the Test
+			 * Packet generation after the SETUP Status
+			 * phase has completed.
+			 */
+
+			pcd->test_mode_tasklet.next = NULL;
+			pcd->test_mode_tasklet.state = 0;
+			atomic_set(&pcd->test_mode_tasklet.count, 0);
+
+			pcd->test_mode_tasklet.func = do_test_mode;
+			pcd->test_mode_tasklet.data = (unsigned long)pcd;
+			pcd->test_mode = __le16_to_cpu(ctrl.wIndex) >> 8;
+			tasklet_schedule(&pcd->test_mode_tasklet);
+
+			break;
+		case USB_DEVICE_B_HNP_ENABLE:
+			/* dev may initiate HNP */
+			if (otg_cap == DWC_OTG_CAP_PARAM_HNP_SRP_CAPABLE) {
+				pcd->b_hnp_enable = 1;
+				dwc_otg_pcd_update_otg(pcd, 0);
+				/*
+				 * gotgctl.devhnpen cleared by a
+				 * USB Reset?
+				 */
+				gotgctl |= DWC_GCTL_DEV_HNP_ENA;
+				gotgctl |= DWC_GCTL_HNP_REQ;
+				dwc_write32(regs + DWC_GOTGCTL, gotgctl);
+			} else {
+				ep0_do_stall(pcd, -EOPNOTSUPP);
+			}
+			break;
+		case USB_DEVICE_A_HNP_SUPPORT:
+			/* RH port supports HNP */
+			if (otg_cap == DWC_OTG_CAP_PARAM_HNP_SRP_CAPABLE) {
+				pcd->a_hnp_support = 1;
+				dwc_otg_pcd_update_otg(pcd, 0);
+			} else {
+				ep0_do_stall(pcd, -EOPNOTSUPP);
+			}
+			break;
+		case USB_DEVICE_A_ALT_HNP_SUPPORT:
+			/* other RH port does */
+			if (otg_cap == DWC_OTG_CAP_PARAM_HNP_SRP_CAPABLE) {
+				pcd->a_alt_hnp_support = 1;
+				dwc_otg_pcd_update_otg(pcd, 0);
+			} else {
+				ep0_do_stall(pcd, -EOPNOTSUPP);
+			}
+			break;
+		}
+		do_setup_in_status_phase(pcd);
+		break;
+	case USB_RECIP_INTERFACE:
+		do_gadget_setup(pcd, &ctrl);
+		break;
+	case USB_RECIP_ENDPOINT:
+		if (__le16_to_cpu(ctrl.wValue) == USB_ENDPOINT_HALT) {
+			struct pcd_ep *ep;
+
+			ep = get_ep_by_addr(pcd, __le16_to_cpu(ctrl.wIndex));
+
+			if (ep == NULL) {
+				ep0_do_stall(pcd, -EOPNOTSUPP);
+				return;
+			}
+
+			ep->stopped = 1;
+			dwc_otg_ep_set_stall(core_if, &ep->dwc_ep);
+		}
+		do_setup_in_status_phase(pcd);
+		break;
+	}
+}
+
+/**
+ * This function process the CLEAR_FEATURE Setup Commands.
+ */
+static void do_clear_feature(struct dwc_pcd *pcd)
+{
+	struct usb_ctrlrequest ctrl = pcd->setup_pkt->req;
+	struct pcd_ep *ep;
+
+	switch (ctrl.bRequestType & USB_RECIP_MASK) {
+	case USB_RECIP_DEVICE:
+		switch (__le16_to_cpu(ctrl.wValue)) {
+		case USB_DEVICE_REMOTE_WAKEUP:
+			pcd->remote_wakeup_enable = 0;
+			break;
+		case USB_DEVICE_TEST_MODE:
+			/* Add CLEAR_FEATURE for TEST modes. */
+			break;
+		}
+		do_setup_in_status_phase(pcd);
+		break;
+	case USB_RECIP_ENDPOINT:
+		ep = get_ep_by_addr(pcd, __le16_to_cpu(ctrl.wIndex));
+		if (ep == NULL) {
+			ep0_do_stall(pcd, -EOPNOTSUPP);
+			return;
+		}
+
+		pcd_clear_halt(pcd, ep);
+		break;
+	}
+}
+
+/**
+ * This function processes SETUP commands.  In Linux, the USB Command processing
+ * is done in two places - the first being the PCD and the second in the Gadget
+ * Driver (for example, the File-Backed Storage Gadget Driver).
+ *
+ * GET_STATUS: Command is processed as defined in chapter 9 of the USB 2.0
+ * Specification chapter 9
+ *
+ * CLEAR_FEATURE: The Device and Endpoint requests are the ENDPOINT_HALT feature
+ * is procesed, all others the interface requests are ignored.
+ *
+ * SET_FEATURE: The Device and Endpoint requests are processed by the PCD.
+ * Interface requests are passed to the Gadget Driver.
+ *
+ * SET_ADDRESS: PCD, Program the DCFG reg, with device address received
+ *
+ * GET_DESCRIPTOR: Gadget Driver, Return the requested descriptor
+ *
+ * SET_DESCRIPTOR: Gadget Driver, Optional - not implemented by any of the
+ * existing Gadget Drivers.
+ *
+ * SET_CONFIGURATION: Gadget Driver, Disable all EPs and enable EPs for new
+ * configuration.
+ *
+ * GET_CONFIGURATION: Gadget Driver, Return the current configuration
+ *
+ * SET_INTERFACE: Gadget Driver, Disable all EPs and enable EPs for new
+ * configuration.
+ *
+ * GET_INTERFACE: Gadget Driver, Return the current interface.
+ *
+ * SYNC_FRAME:  Display debug message.
+ *
+ * When the SETUP Phase Done interrupt occurs, the PCD SETUP commands are
+ * processed by pcd_setup. Calling the Function Driver's setup function from
+ * pcd_setup processes the gadget SETUP commands.
+ */
+static void pcd_setup(struct dwc_pcd *pcd)
+{
+	struct core_if *core_if = GET_CORE_IF(pcd);
+	struct device_if *dev_if = core_if->dev_if;
+	struct usb_ctrlrequest ctrl = pcd->setup_pkt->req;
+	struct pcd_ep *ep;
+	struct pcd_ep *ep0 = &pcd->ep0;
+	u16 *status = pcd->status_buf;
+	u32 doeptsize0 = 0;
+
+	doeptsize0 = dwc_read32((u32) dev_if->out_ep_regs[0] + DWC_DOEPTSIZ);
+
+	/* handle > 1 setup packet , assert error for now */
+	if (core_if->dma_enable && (DWC_DEPTSIZ0_SUPCNT_RD(doeptsize0) < 2))
+		pr_err("\n\n	 CANNOT handle > 1 setup packet in "
+		       "DMA mode\n\n");
+
+	/* Clean up the request queue */
+	request_nuke(ep0);
+	ep0->stopped = 0;
+
+	if (ctrl.bRequestType & USB_DIR_IN) {
+		ep0->dwc_ep.is_in = 1;
+		pcd->ep0state = EP0_IN_DATA_PHASE;
+	} else {
+		ep0->dwc_ep.is_in = 0;
+		pcd->ep0state = EP0_OUT_DATA_PHASE;
+	}
+
+	if ((ctrl.bRequestType & USB_TYPE_MASK) != USB_TYPE_STANDARD) {
+		/*
+		 * Handle non-standard (class/vendor) requests in the gadget
+		 * driver
+		 */
+		do_gadget_setup(pcd, &ctrl);
+		return;
+	}
+
+	switch (ctrl.bRequest) {
+	case USB_REQ_GET_STATUS:
+		switch (ctrl.bRequestType & USB_RECIP_MASK) {
+		case USB_RECIP_DEVICE:
+			*status = 0x1;	/* Self powered */
+			*status |= pcd->remote_wakeup_enable << 1;
+			break;
+		case USB_RECIP_INTERFACE:
+			*status = 0;
+			break;
+		case USB_RECIP_ENDPOINT:
+			ep = get_ep_by_addr(pcd, __le16_to_cpu(ctrl.wIndex));
+			if (ep == NULL || __le16_to_cpu(ctrl.wLength) > 2) {
+				ep0_do_stall(pcd, -EOPNOTSUPP);
+				return;
+			}
+			*status = ep->stopped;
+			break;
+		}
+
+		*status = __cpu_to_le16(*status);
+
+		pcd->ep0_pending = 1;
+		ep0->dwc_ep.start_xfer_buff = (u8 *) status;
+		ep0->dwc_ep.xfer_buff = (u8 *) status;
+		ep0->dwc_ep.dma_addr = pcd->status_buf_dma_handle;
+		ep0->dwc_ep.xfer_len = 2;
+		ep0->dwc_ep.xfer_count = 0;
+		ep0->dwc_ep.total_len = ep0->dwc_ep.xfer_len;
+		dwc_otg_ep0_start_transfer(GET_CORE_IF(pcd), &ep0->dwc_ep);
+		break;
+	case USB_REQ_CLEAR_FEATURE:
+		do_clear_feature(pcd);
+		break;
+	case USB_REQ_SET_FEATURE:
+		do_set_feature(pcd);
+		break;
+	case USB_REQ_SET_ADDRESS:
+		if (ctrl.bRequestType == USB_RECIP_DEVICE) {
+			u32 dcfg = 0;
+
+			dcfg = DWC_DCFG_DEV_ADDR_WR(dcfg,
+						    __le16_to_cpu(ctrl.wValue));
+			dwc_modify32((u32) dev_if->dev_global_regs + DWC_DCFG,
+				     0, dcfg);
+			do_setup_in_status_phase(pcd);
+			return;
+		}
+		break;
+	case USB_REQ_SET_INTERFACE:
+	case USB_REQ_SET_CONFIGURATION:
+		pcd->request_config = 1;	/* Configuration changed */
+		do_gadget_setup(pcd, &ctrl);
+		break;
+	case USB_REQ_SYNCH_FRAME:
+		do_gadget_setup(pcd, &ctrl);
+		break;
+	default:
+		/* Call the Gadget Driver's setup functions */
+		do_gadget_setup(pcd, &ctrl);
+		break;
+	}
+}
+
+/**
+ * This function completes the ep0 control transfer.
+ */
+static int ep0_complete_request(struct pcd_ep *ep)
+{
+	struct core_if *core_if = GET_CORE_IF(ep->pcd);
+	struct device_if *dev_if = core_if->dev_if;
+	u32 in_regs = (u32) dev_if->in_ep_regs[ep->dwc_ep.num];
+	u32 deptsiz = 0;
+	struct pcd_request *req;
+	int is_last = 0;
+	struct dwc_pcd *pcd = ep->pcd;
+
+	if (pcd->ep0_pending && list_empty(&ep->queue)) {
+		if (ep->dwc_ep.is_in)
+			do_setup_out_status_phase(pcd);
+		else
+			do_setup_in_status_phase(pcd);
+
+		pcd->ep0_pending = 0;
+		pcd->ep0state = EP0_STATUS;
+		return 1;
+	}
+
+	if (list_empty(&ep->queue))
+		return 0;
+
+	req = list_entry(ep->queue.next, struct pcd_request, queue);
+
+	if (pcd->ep0state == EP0_STATUS) {
+		is_last = 1;
+	} else if (ep->dwc_ep.is_in) {
+		deptsiz = dwc_read32((u32) in_regs + DWC_DIEPTSIZ);
+
+		if (DWC_DEPTSIZ0_XFER_SIZ_RD(deptsiz) == 0) {
+			req->req.actual = ep->dwc_ep.xfer_count;
+			do_setup_out_status_phase(pcd);
+		}
+	} else {
+		/* This is ep0-OUT */
+		req->req.actual = ep->dwc_ep.xfer_count;
+		do_setup_in_status_phase(pcd);
+	}
+
+	/* Complete the request */
+	if (is_last) {
+		request_done(ep, req, 0);
+		ep->dwc_ep.start_xfer_buff = NULL;
+		ep->dwc_ep.xfer_buff = NULL;
+		ep->dwc_ep.xfer_len = 0;
+		return 1;
+	}
+	return 0;
+}
+
+/**
+ * This function completes the request for the EP.  If there are additional
+ * requests for the EP in the queue they will be started.
+ */
+static void complete_ep(struct pcd_ep *ep)
+{
+	struct core_if *core_if = GET_CORE_IF(ep->pcd);
+	struct device_if *dev_if = core_if->dev_if;
+	u32 in_ep_regs = (u32) dev_if->in_ep_regs[ep->dwc_ep.num];
+	u32 deptsiz = 0;
+	struct pcd_request *req = NULL;
+	int is_last = 0;
+
+	/* Get any pending requests */
+	if (!list_empty(&ep->queue))
+		req = list_entry(ep->queue.next, struct pcd_request, queue);
+
+	if (ep->dwc_ep.is_in) {
+		deptsiz = dwc_read32((u32) in_ep_regs + DWC_DIEPTSIZ);
+
+		if (core_if->dma_enable && !DWC_DEPTSIZ_XFER_SIZ_RD(deptsiz))
+			ep->dwc_ep.xfer_count = ep->dwc_ep.xfer_len;
+
+		if (DWC_DEPTSIZ_XFER_SIZ_RD(deptsiz) == 0 &&
+		    DWC_DEPTSIZ_PKT_CNT_RD(deptsiz) == 0 &&
+		    ep->dwc_ep.xfer_count == ep->dwc_ep.xfer_len)
+			is_last = 1;
+		else
+			pr_warning("Incomplete transfer (%s-%s "
+				   "[siz=%d pkt=%d])\n", ep->ep.name,
+				   ep->dwc_ep.is_in ? "IN" : "OUT",
+				   DWC_DEPTSIZ_XFER_SIZ_RD(deptsiz),
+				   DWC_DEPTSIZ_PKT_CNT_RD(deptsiz));
+	} else {
+		u32 out_ep_regs = (u32) dev_if->out_ep_regs[ep->dwc_ep.num];
+
+		deptsiz = dwc_read32((u32) out_ep_regs + DWC_DOEPTSIZ);
+		is_last = 1;
+	}
+
+	/* Complete the request */
+	if (is_last) {
+		/*
+		 * Added-sr: 2007-07-26
+		 *
+		 * Since the 405EZ (Ultra) only support 2047 bytes as
+		 * max transfer size, we have to split up bigger transfers
+		 * into multiple transfers of 1024 bytes sized messages.
+		 * I happens often, that transfers of 4096 bytes are
+		 * required (zero-gadget, file_storage-gadget).
+		 */
+		if ((dwc_has_feature(core_if, DWC_LIMITED_XFER)) &&
+		    ep->dwc_ep.bytes_pending) {
+			u32 in_regs =
+			    (u32) core_if->dev_if->in_ep_regs[ep->dwc_ep.num];
+			u32 intr_mask = 0;
+
+			ep->dwc_ep.xfer_len = ep->dwc_ep.bytes_pending;
+			if (ep->dwc_ep.xfer_len > MAX_XFER_LEN) {
+				ep->dwc_ep.bytes_pending = ep->dwc_ep.xfer_len -
+				    MAX_XFER_LEN;
+				ep->dwc_ep.xfer_len = MAX_XFER_LEN;
+			} else {
+				ep->dwc_ep.bytes_pending = 0;
+			}
+
+			/*
+			 * Restart the current transfer with the next "chunk"
+			 * of data.
+			 */
+			ep->dwc_ep.xfer_count = 0;
+
+			deptsiz = dwc_read32((u32) in_regs + DWC_DIEPTSIZ);
+			deptsiz =
+			    DWC_DEPTSIZ_XFER_SIZ_RW(deptsiz,
+						    ep->dwc_ep.xfer_len);
+			deptsiz =
+			    DWC_DEPTSIZ_PKT_CNT_RW(deptsiz,
+						   ((ep->dwc_ep.xfer_len - 1 +
+						     ep->dwc_ep.maxpacket) /
+						    ep->dwc_ep.maxpacket));
+			dwc_write32((u32) in_regs + DWC_DIEPTSIZ, deptsiz);
+
+			intr_mask |= DWC_INTSTS_NP_TXFIFO_EMPT;
+			dwc_modify32((u32) (core_if->core_global_regs) +
+				     DWC_GINTSTS, intr_mask, 0);
+			dwc_modify32((u32) (core_if->core_global_regs) +
+				     DWC_GINTMSK, intr_mask, intr_mask);
+
+			/*
+			 * Just return here if message was not completely
+			 * transferred.
+			 */
+			return;
+		}
+		if (core_if->dma_enable)
+			req->req.actual = ep->dwc_ep.xfer_len -
+			    DWC_DEPTSIZ_XFER_SIZ_RD(deptsiz);
+		else
+			req->req.actual = ep->dwc_ep.xfer_count;
+
+		request_done(ep, req, 0);
+		ep->dwc_ep.start_xfer_buff = NULL;
+		ep->dwc_ep.xfer_buff = NULL;
+		ep->dwc_ep.xfer_len = 0;
+
+		/* If there is a request in the queue start it. */
+		start_next_request(ep);
+	}
+}
+
+/**
+ * This function continues control IN transfers started by
+ * dwc_otg_ep0_start_transfer, when the transfer does not fit in a
+ * single packet.  NOTE: The DIEPCTL0/DOEPCTL0 registers only have one
+ * bit for the packet count.
+ */
+static void dwc_otg_ep0_continue_transfer(struct core_if *c_if,
+					  struct dwc_ep *ep)
+{
+	if (ep->is_in) {
+		u32 depctl = 0;
+		u32 deptsiz = 0;
+		struct device_if *d_if = c_if->dev_if;
+		u32 in_regs = (u32) d_if->in_ep_regs[0];
+		u32 tx_status = 0;
+		u32 glbl_regs = (u32) c_if->core_global_regs;
+
+		tx_status = dwc_read32(glbl_regs + DWC_GNPTXSTS);
+
+		depctl = dwc_read32((u32) in_regs + DWC_DIEPCTL);
+		deptsiz = dwc_read32((u32) in_regs + DWC_DIEPTSIZ);
+
+		/*
+		 * Program the transfer size and packet count as follows:
+		 *   xfersize = N * maxpacket + short_packet
+		 *   pktcnt = N + (short_packet exist ? 1 : 0)
+		 */
+		if (ep->total_len - ep->xfer_count > ep->maxpacket)
+			deptsiz = DWC_DEPTSIZ0_XFER_SIZ_RW(deptsiz,
+							   ep->maxpacket);
+		else
+			deptsiz = DWC_DEPTSIZ0_XFER_SIZ_RW(deptsiz,
+							   (ep->total_len -
+							    ep->xfer_count));
+
+		deptsiz = DWC_DEPTSIZ0_PKT_CNT_RW(deptsiz, 1);
+		ep->xfer_len += DWC_DEPTSIZ0_XFER_SIZ_RD(deptsiz);
+		dwc_write32((u32) in_regs + DWC_DIEPTSIZ, deptsiz);
+
+		/* Write the DMA register */
+		if (DWC_HWCFG2_ARCH_RD(c_if->hwcfg2) == DWC_INT_DMA_ARCH)
+			dwc_write32((u32) in_regs + DWC_DIEPDMA, ep->dma_addr);
+
+		/* EP enable, IN data in FIFO */
+		depctl = DWC_DEPCTL_CLR_NAK_RW(depctl, 1);
+		depctl = DWC_DEPCTL_EPENA_RW(depctl, 1);
+		dwc_write32((u32) in_regs + DWC_DIEPCTL, depctl);
+
+		/*
+		 * Enable the Non-Periodic Tx FIFO empty interrupt, the
+		 * data will be written into the fifo by the ISR.
+		 */
+		if (!c_if->dma_enable) {
+			u32 intr_mask = 0;
+
+			/* First clear it from GINTSTS */
+			intr_mask |= DWC_INTMSK_NP_TXFIFO_EMPT;
+			dwc_write32(glbl_regs + DWC_GINTSTS, intr_mask);
+
+			/* To avoid spurious NPTxFEmp intr */
+			dwc_modify32(glbl_regs + DWC_GINTMSK, intr_mask,
+				     intr_mask);
+		}
+	}
+}
+
+/**
+ * This function handles EP0 Control transfers.
+ *
+ * The state of the control tranfers are tracked in ep0state
+ */
+static void handle_ep0(struct dwc_pcd *pcd)
+{
+	struct core_if *core_if = GET_CORE_IF(pcd);
+	struct pcd_ep *ep0 = &pcd->ep0;
+
+	switch (pcd->ep0state) {
+	case EP0_DISCONNECT:
+		break;
+	case EP0_IDLE:
+		pcd->request_config = 0;
+		pcd_setup(pcd);
+		break;
+	case EP0_IN_DATA_PHASE:
+		if (core_if->dma_enable)
+			/*
+			 * For EP0 we can only program 1 packet at a time so we
+			 * need to do the calculations after each complete.
+			 * Call write_packet to make the calculations, as in
+			 * slave mode, and use those values to determine if we
+			 * can complete.
+			 */
+			dwc_otg_ep_write_packet(core_if, &ep0->dwc_ep, 1);
+		else
+			dwc_otg_ep_write_packet(core_if, &ep0->dwc_ep, 0);
+
+		if (ep0->dwc_ep.xfer_count < ep0->dwc_ep.total_len)
+			dwc_otg_ep0_continue_transfer(core_if, &ep0->dwc_ep);
+		else
+			ep0_complete_request(ep0);
+		break;
+	case EP0_OUT_DATA_PHASE:
+		ep0_complete_request(ep0);
+		break;
+	case EP0_STATUS:
+		ep0_complete_request(ep0);
+		pcd->ep0state = EP0_IDLE;
+		ep0->stopped = 1;
+		ep0->dwc_ep.is_in = 0;	/* OUT for next SETUP */
+
+		/* Prepare for more SETUP Packets */
+		if (core_if->dma_enable) {
+			ep0_out_start(core_if, pcd);
+		} else {
+			int i;
+			u32 diepctl = 0;
+
+			diepctl = dwc_read32(in_ep_ctl_reg(pcd, 0));
+			if (pcd->ep0.queue_sof) {
+				pcd->ep0.queue_sof = 0;
+				start_next_request(&pcd->ep0);
+			}
+
+			diepctl = dwc_read32(in_ep_ctl_reg(pcd, 0));
+			if (pcd->ep0.queue_sof) {
+				pcd->ep0.queue_sof = 0;
+				start_next_request(&pcd->ep0);
+			}
+
+			for (i = 0; i < core_if->dev_if->num_in_eps; i++) {
+				diepctl = dwc_read32(in_ep_ctl_reg(pcd, i));
+
+				if (pcd->in_ep[i].queue_sof) {
+					pcd->in_ep[i].queue_sof = 0;
+					start_next_request(&pcd->in_ep[i]);
+				}
+			}
+		}
+		break;
+	case EP0_STALL:
+		pr_err("EP0 STALLed, should not get here handle_ep0()\n");
+		break;
+	}
+}
+
+/**
+ * Restart transfer
+ */
+static void restart_transfer(struct dwc_pcd *pcd, const u32 ep_num)
+{
+	struct core_if *core_if = GET_CORE_IF(pcd);
+	struct device_if *dev_if = core_if->dev_if;
+	u32 dieptsiz = 0;
+	struct pcd_ep *ep;
+
+	dieptsiz = dwc_read32((u32) dev_if->in_ep_regs[ep_num] + DWC_DIEPTSIZ);
+	ep = get_in_ep(pcd, ep_num);
+
+	/*
+	 * If pktcnt is not 0, and xfersize is 0, and there is a buffer,
+	 * resend the last packet.
+	 */
+	if (DWC_DEPTSIZ_PKT_CNT_RD(dieptsiz) &&
+	    !DWC_DEPTSIZ_XFER_SIZ_RD(dieptsiz) && ep->dwc_ep.start_xfer_buff) {
+		if (ep->dwc_ep.xfer_len <= ep->dwc_ep.maxpacket) {
+			ep->dwc_ep.xfer_count = 0;
+			ep->dwc_ep.xfer_buff = ep->dwc_ep.start_xfer_buff;
+		} else {
+			ep->dwc_ep.xfer_count -= ep->dwc_ep.maxpacket;
+
+			/* convert packet size to dwords. */
+			ep->dwc_ep.xfer_buff -= ep->dwc_ep.maxpacket;
+		}
+		ep->stopped = 0;
+
+		if (!ep_num)
+			dwc_otg_ep0_start_transfer(core_if, &ep->dwc_ep);
+		else
+			dwc_otg_ep_start_transfer(core_if, &ep->dwc_ep);
+	}
+}
+
+/**
+ * Handle the IN EP Transfer Complete interrupt.
+ *
+ * If dedicated fifos are enabled, then the Tx FIFO empty interrupt for the EP
+ * is disabled.  Otherwise the NP Tx FIFO empty interrupt is  disabled.
+ */
+static void handle_in_ep_xfr_complete_intr(struct dwc_pcd *pcd,
+					   struct pcd_ep *ep, u32 num)
+{
+	struct core_if *c_if = GET_CORE_IF(pcd);
+	struct device_if *d_if = c_if->dev_if;
+	struct dwc_ep *dwc_ep = &ep->dwc_ep;
+	u32 diepint = 0;
+
+	if (c_if->en_multiple_tx_fifo) {
+		u32 fifoemptymsk = 0x1 << dwc_ep->num;
+		dwc_modify32((u32) d_if->dev_global_regs +
+			     DWC_DTKNQR4FIFOEMPTYMSK, fifoemptymsk, 0);
+	} else {
+		u32 intr_mask = 0;
+
+		intr_mask |= DWC_INTMSK_NP_TXFIFO_EMPT;
+		dwc_modify32((u32) (c_if->core_global_regs) + DWC_GINTMSK,
+			     intr_mask, 0);
+	}
+
+	/* Clear the interrupt, then complete the transfer */
+	diepint = DWC_DIEPINT_TX_CMPL_RW(diepint, 1);
+	dwc_write32((u32) d_if->in_ep_regs[num] + DWC_DIEPINT, diepint);
+
+	if (!num)
+		handle_ep0(pcd);
+	else
+		complete_ep(ep);
+}
+
+/**
+ * Handle the IN EP disable interrupt.
+ */
+static void handle_in_ep_disable_intr(struct dwc_pcd *pcd, const u32 ep_num)
+{
+	struct core_if *core_if = GET_CORE_IF(pcd);
+	struct device_if *dev_if = core_if->dev_if;
+	u32 dieptsiz = 0;
+	u32 dctl = 0;
+	struct pcd_ep *ep;
+	struct dwc_ep *dwc_ep;
+	u32 diepint = 0;
+
+	ep = get_in_ep(pcd, ep_num);
+	dwc_ep = &ep->dwc_ep;
+
+	dieptsiz = dwc_read32((u32) dev_if->in_ep_regs[ep_num] + DWC_DIEPTSIZ);
+
+	if (ep->stopped) {
+		/* Flush the Tx FIFO */
+		dwc_otg_flush_tx_fifo(core_if, dwc_ep->tx_fifo_num);
+
+		/* Clear the Global IN NP NAK */
+		dctl = 0;
+		dctl = DWC_DCTL_CLR_CLBL_NP_IN_NAK(dctl, 1);
+		dwc_modify32(dev_ctl_reg(pcd), dctl, 0);
+
+		if (DWC_DEPTSIZ_PKT_CNT_RD(dieptsiz) ||
+		    DWC_DEPTSIZ_XFER_SIZ_RD(dieptsiz))
+			restart_transfer(pcd, ep_num);
+	} else {
+		if (DWC_DEPTSIZ_PKT_CNT_RD(dieptsiz) ||
+		    DWC_DEPTSIZ_XFER_SIZ_RD(dieptsiz))
+			restart_transfer(pcd, ep_num);
+	}
+	/* Clear epdisabled */
+	diepint = DWC_DIEPINT_EP_DISA_RW(diepint, 1);
+	dwc_write32(in_ep_int_reg(pcd, ep_num), diepint);
+
+}
+
+/**
+ * Handler for the IN EP timeout handshake interrupt.
+ */
+static void handle_in_ep_timeout_intr(struct dwc_pcd *pcd, const u32 ep_num)
+{
+	struct core_if *core_if = GET_CORE_IF(pcd);
+	struct pcd_ep *ep;
+	u32 dctl = 0;
+	u32 intr_mask = 0;
+	u32 diepint = 0;
+
+	ep = get_in_ep(pcd, ep_num);
+
+	/* Disable the NP Tx Fifo Empty Interrrupt */
+	if (!core_if->dma_enable) {
+		intr_mask |= DWC_INTMSK_NP_TXFIFO_EMPT;
+		dwc_modify32((u32) (core_if->core_global_regs) + DWC_GINTMSK,
+			     intr_mask, 0);
+	}
+
+	/* Non-periodic EP */
+	/* Enable the Global IN NAK Effective Interrupt */
+	intr_mask |= DWC_INTMSK_GLBL_IN_NAK;
+	dwc_modify32((u32) (core_if->core_global_regs) + DWC_GINTMSK, 0,
+		     intr_mask);
+
+	/* Set Global IN NAK */
+	dctl = DWC_DCTL_CLR_CLBL_NP_IN_NAK(dctl, 1);
+	dwc_modify32(dev_ctl_reg(pcd), dctl, dctl);
+	ep->stopped = 1;
+
+	/* Clear timeout */
+	diepint = DWC_DIEPINT_TOUT_COND_RW(diepint, 1);
+	dwc_write32(in_ep_int_reg(pcd, ep_num), diepint);
+}
+
+/**
+ * Handles the IN Token received with TxF Empty interrupt.
+ *
+ * For the 405EZ, only start the next transfer, when currently no other transfer
+ * is active on this endpoint.
+ *
+ * Note that the bits in the Device IN endpoint mask register are laid out
+ * exactly the same as the Device IN endpoint interrupt register.
+ */
+static void handle_in_ep_tx_fifo_empty_intr(struct dwc_pcd *pcd,
+					    struct pcd_ep *ep, u32 num)
+{
+	u32 diepint = 0;
+
+	if (!ep->stopped && num) {
+		u32 diepmsk = 0;
+
+		diepmsk = DWC_DIEPMSK_IN_TKN_TX_EMPTY_RW(diepmsk, 1);
+		dwc_modify32(dev_diepmsk_reg(pcd), diepmsk, 0);
+
+		if (dwc_has_feature(GET_CORE_IF(pcd), DWC_LIMITED_XFER)) {
+			if (!ep->dwc_ep.active)
+				start_next_request(ep);
+		} else {
+			start_next_request(ep);
+		}
+	}
+	/* Clear intktxfemp */
+	diepint = DWC_DIEPMSK_IN_TKN_TX_EMPTY_RW(diepint, 1);
+	dwc_write32(in_ep_int_reg(pcd, num), diepint);
+}
+
+static void handle_in_ep_nak_effective_intr(struct dwc_pcd *pcd,
+					    struct pcd_ep *ep, u32 num)
+{
+	u32 diepctl = 0;
+	u32 diepint = 0;
+
+	/* Periodic EP */
+	if (ep->disabling) {
+		diepctl = 0;
+		diepctl = DWC_DEPCTL_SET_NAK_RW(diepctl, 1);
+		diepctl = DWC_DEPCTL_DPID_RW(diepctl, 1);
+		dwc_modify32(in_ep_ctl_reg(pcd, num), diepctl, diepctl);
+	}
+	/* Clear inepnakeff */
+	diepint = DWC_DIEPINT_IN_EP_NAK_RW(diepint, 1);
+	dwc_write32(in_ep_int_reg(pcd, num), diepint);
+
+}
+
+/**
+ * This function returns the Device IN EP Interrupt register
+ */
+static inline u32 dwc_otg_read_diep_intr(struct core_if *core_if,
+					 struct dwc_ep *ep)
+{
+	struct device_if *dev_if = core_if->dev_if;
+	u32 v, msk, emp;
+
+	msk = dwc_read32((u32) dev_if->dev_global_regs + DWC_DIEPMSK);
+	emp =
+	    dwc_read32((u32) dev_if->dev_global_regs + DWC_DTKNQR4FIFOEMPTYMSK);
+	msk |= ((emp >> ep->num) & 0x1) << 7;
+	v = dwc_read32((u32) dev_if->in_ep_regs[ep->num] + DWC_DIEPINT) & msk;
+	return v;
+}
+
+/**
+ * This function reads the Device All Endpoints Interrupt register and
+ * returns the IN endpoint interrupt bits.
+ */
+static inline u32 dwc_otg_read_dev_all_in_ep_intr(struct core_if *_if)
+{
+	u32 v;
+
+	v = dwc_read32((u32) _if->dev_if->dev_global_regs + DWC_DAINT) &
+	    dwc_read32((u32) _if->dev_if->dev_global_regs + DWC_DAINTMSK);
+	return v & 0xffff;
+}
+
+/**
+ * This interrupt indicates that an IN EP has a pending Interrupt.
+ * The sequence for handling the IN EP interrupt is shown below:
+ *
+ * - Read the Device All Endpoint Interrupt register
+ * - Repeat the following for each IN EP interrupt bit set (from LSB to MSB).
+ *
+ * - Read the Device Endpoint Interrupt (DIEPINTn) register
+ * - If "Transfer Complete" call the request complete function
+ * - If "Endpoint Disabled" complete the EP disable procedure.
+ * - If "AHB Error Interrupt" log error
+ * - If "Time-out Handshake" log error
+ * - If "IN Token Received when TxFIFO Empty" write packet to Tx FIFO.
+ * - If "IN Token EP Mismatch" (disable, this is handled by EP Mismatch
+ *   Interrupt)
+ */
+static int dwc_otg_pcd_handle_in_ep_intr(struct dwc_pcd *pcd)
+{
+	struct core_if *core_if = GET_CORE_IF(pcd);
+	u32 diepint = 0;
+	u32 ep_intr;
+	u32 epnum = 0;
+	struct pcd_ep *ep;
+	struct dwc_ep *dwc_ep;
+
+	/* Read in the device interrupt bits */
+	ep_intr = dwc_otg_read_dev_all_in_ep_intr(core_if);
+
+	/* Service the Device IN interrupts for each endpoint */
+	while (ep_intr) {
+		if (ep_intr & 0x1) {
+			u32 c_diepint;
+
+			/* Get EP pointer */
+			ep = get_in_ep(pcd, epnum);
+			dwc_ep = &ep->dwc_ep;
+
+			diepint = dwc_otg_read_diep_intr(core_if, dwc_ep);
+
+			/* Transfer complete */
+			if (DWC_DIEPINT_TX_CMPL_RD(diepint))
+				handle_in_ep_xfr_complete_intr(pcd, ep, epnum);
+
+			/* Endpoint disable */
+			if (DWC_DIEPINT_EP_DISA_RD(diepint))
+				handle_in_ep_disable_intr(pcd, epnum);
+
+			/* AHB Error */
+			if (DWC_DIEPINT_AHB_ERROR_RD(diepint)) {
+				/* Clear ahberr */
+				c_diepint = 0;
+				c_diepint =
+				    DWC_DIEPINT_AHB_ERROR_RW(c_diepint, 1);
+				dwc_write32(in_ep_int_reg(pcd, epnum),
+					    c_diepint);
+			}
+
+			/* TimeOUT Handshake (non-ISOC IN EPs) */
+			if (DWC_DIEPINT_TOUT_COND_RD(diepint))
+				handle_in_ep_timeout_intr(pcd, epnum);
+
+			/* IN Token received with TxF Empty */
+			if (DWC_DIEPINT_IN_TKN_TX_EMPTY_RD(diepint))
+				handle_in_ep_tx_fifo_empty_intr(pcd, ep, epnum);
+
+			/* IN Token Received with EP mismatch */
+			if (DWC_DIEPINT_IN_TKN_EP_MISS_RD(diepint)) {
+				/* Clear intknepmis */
+				c_diepint = 0;
+				c_diepint =
+				    DWC_DIEPINT_IN_TKN_EP_MISS_RW(c_diepint, 1);
+				dwc_write32(in_ep_int_reg(pcd, epnum),
+					    c_diepint);
+			}
+
+			/* IN Endpoint NAK Effective */
+			if (DWC_DIEPINT_IN_EP_NAK_RD(diepint))
+				handle_in_ep_nak_effective_intr(pcd, ep, epnum);
+
+			/* IN EP Tx FIFO Empty Intr */
+			if (DWC_DIEPINT_TXFIFO_EMPTY_RD(diepint))
+				write_empty_tx_fifo(pcd, epnum);
+		}
+		epnum++;
+		ep_intr >>= 1;
+	}
+	return 1;
+}
+
+/**
+ * This function reads the Device All Endpoints Interrupt register and
+ * returns the OUT endpoint interrupt bits.
+ */
+static inline u32 dwc_otg_read_dev_all_out_ep_intr(struct core_if *_if)
+{
+	u32 v;
+
+	v = dwc_read32((u32) _if->dev_if->dev_global_regs + DWC_DAINT) &
+	    dwc_read32((u32) _if->dev_if->dev_global_regs + DWC_DAINTMSK);
+	return (v & 0xffff0000) >> 16;
+}
+
+/**
+ * This function returns the Device OUT EP Interrupt register
+ */
+static inline u32 dwc_otg_read_doep_intr(struct core_if *core_if,
+					 struct dwc_ep *ep)
+{
+	struct device_if *dev_if = core_if->dev_if;
+	u32 v;
+
+	v = dwc_read32((u32) dev_if->out_ep_regs[ep->num] + DWC_DOEPINT) &
+	    dwc_read32((u32) dev_if->dev_global_regs + DWC_DOEPMSK);
+	return v;
+}
+
+/**
+ * This interrupt indicates that an OUT EP has a pending Interrupt.
+ * The sequence for handling the OUT EP interrupt is shown below:
+ *
+ * - Read the Device All Endpoint Interrupt register.
+ * - Repeat the following for each OUT EP interrupt bit set (from LSB to MSB).
+ *
+ * - Read the Device Endpoint Interrupt (DOEPINTn) register
+ * - If "Transfer Complete" call the request complete function
+ * - If "Endpoint Disabled" complete the EP disable procedure.
+ * - If "AHB Error Interrupt" log error
+ * - If "Setup Phase Done" process Setup Packet (See Standard USB Command
+ *   Processing)
+ */
+static int dwc_otg_pcd_handle_out_ep_intr(struct dwc_pcd *pcd)
+{
+	struct core_if *core_if = GET_CORE_IF(pcd);
+	u32 ep_intr;
+	u32 doepint = 0;
+	u32 epnum = 0;
+	struct dwc_ep *dwc_ep;
+
+	/* Read in the device interrupt bits */
+	ep_intr = dwc_otg_read_dev_all_out_ep_intr(core_if);
+	while (ep_intr) {
+		if (ep_intr & 0x1) {
+			u32 c_doepint = 0;
+
+			dwc_ep = &((get_out_ep(pcd, epnum))->dwc_ep);
+			doepint = dwc_otg_read_doep_intr(core_if, dwc_ep);
+
+			/* Transfer complete */
+			if (DWC_DOEPINT_TX_COMPL_RD(doepint)) {
+				/* Clear xfercompl */
+				c_doepint = 0;
+				c_doepint =
+				    DWC_DOEPMSK_TX_COMPL_RW(c_doepint, 1);
+				dwc_write32(out_ep_int_reg(pcd, epnum),
+					    c_doepint);
+				if (epnum == 0)
+					handle_ep0(pcd);
+				else
+					complete_ep(get_out_ep(pcd, epnum));
+			}
+
+			/* Endpoint disable */
+			if (DWC_DOEPINT_EP_DISA_RD(doepint)) {
+				/* Clear epdisabled */
+				c_doepint = 0;
+				c_doepint =
+				    DWC_DOEPMSK_EP_DISA_RW(c_doepint, 1);
+				dwc_write32(out_ep_int_reg(pcd, epnum),
+					    c_doepint);
+			}
+
+			/* AHB Error */
+			if (DWC_DOEPINT_AHB_ERROR_RD(doepint)) {
+				c_doepint = 0;
+				c_doepint =
+				    DWC_DOEPMSK_AHB_ERROR_RW(c_doepint, 1);
+				dwc_write32(out_ep_int_reg(pcd, epnum),
+					    c_doepint);
+			}
+
+			/* Setup Phase Done (control EPs) */
+			if (DWC_DOEPINT_SETUP_DONE_RD(doepint)) {
+				c_doepint = 0;
+				c_doepint =
+				    DWC_DOEPMSK_SETUP_DONE_RW(c_doepint, 1);
+				dwc_write32(out_ep_int_reg(pcd, epnum),
+					    c_doepint);
+				handle_ep0(pcd);
+			}
+		}
+		epnum++;
+		ep_intr >>= 1;
+	}
+	return 1;
+}
+
+/**
+ * Incomplete ISO IN Transfer Interrupt.  This interrupt indicates one of the
+ * following conditions occurred while transmitting an ISOC transaction.
+ *
+ * - Corrupted IN Token for ISOC EP.
+ * - Packet not complete in FIFO.
+ *
+ * The follow actions should be taken:
+ * - Determine the EP
+ * - Set incomplete flag in dwc_ep structure
+ *  - Disable EP.  When "Endpoint Disabled" interrupt is received Flush FIFO
+ */
+static int dwc_otg_pcd_handle_incomplete_isoc_in_intr(struct dwc_pcd *pcd)
+{
+	u32 intr_mask = 0;
+	u32 gintsts = 0;
+
+	pr_info("Interrupt handler not implemented for IN ISOC "
+		"Incomplete\n");
+
+	/* Turn off and clear the interrupt */
+	intr_mask |= DWC_INTMSK_INCMP_IN_ATX;
+	dwc_modify32((u32) (GET_CORE_IF(pcd)->core_global_regs) + DWC_GINTMSK,
+		     intr_mask, 0);
+
+	gintsts |= DWC_INTSTS_INCMP_IN_ATX;
+	dwc_write32((u32) (GET_CORE_IF(pcd)->core_global_regs) + DWC_GINTSTS,
+		    gintsts);
+	return 1;
+}
+
+/**
+ * Incomplete ISO OUT Transfer Interrupt.  This interrupt indicates that the
+ * core has dropped an ISO OUT packet.  The following conditions can be the
+ * cause:
+ *
+ * - FIFO Full, the entire packet would not fit in the FIFO.
+ * - CRC Error
+ * - Corrupted Token
+ *
+ * The follow actions should be taken:
+ * - Determine the EP
+ * - Set incomplete flag in dwc_ep structure
+ * - Read any data from the FIFO
+ * - Disable EP.  When "Endpoint Disabled" interrupt is received re-enable EP.
+ */
+static int dwc_otg_pcd_handle_incomplete_isoc_out_intr(struct dwc_pcd *pcd)
+{
+	u32 intr_mask = 0;
+	u32 gintsts = 0;
+
+	pr_info("Interrupt handler not implemented for OUT ISOC "
+		"Incomplete\n");
+
+	/* Turn off and clear the interrupt */
+	intr_mask |= DWC_INTMSK_INCMP_OUT_PTX;
+	dwc_modify32((u32) (GET_CORE_IF(pcd)->core_global_regs) + DWC_GINTMSK,
+		     intr_mask, 0);
+
+	gintsts |= DWC_INTSTS_INCMP_OUT_PTX;
+	dwc_write32((u32) (GET_CORE_IF(pcd)->core_global_regs) + DWC_GINTSTS,
+		    gintsts);
+	return 1;
+}
+
+/**
+ * This function handles the Global IN NAK Effective interrupt.
+ */
+static int dwc_otg_pcd_handle_in_nak_effective(struct dwc_pcd *pcd)
+{
+	struct device_if *dev_if = GET_CORE_IF(pcd)->dev_if;
+	u32 diepctl = 0;
+	u32 diepctl_rd = 0;
+	u32 intr_mask = 0;
+	u32 gintsts = 0;
+	u32 i;
+
+	/* Disable all active IN EPs */
+	diepctl = DWC_DEPCTL_DPID_RW(diepctl, 1);
+	diepctl = DWC_DEPCTL_SET_NAK_RW(diepctl, 1);
+	for (i = 0; i <= dev_if->num_in_eps; i++) {
+		diepctl_rd = dwc_read32(in_ep_ctl_reg(pcd, i));
+		if (DWC_DEPCTL_EPENA_RD(diepctl_rd))
+			dwc_write32(in_ep_ctl_reg(pcd, i), diepctl);
+	}
+
+	/* Disable the Global IN NAK Effective Interrupt */
+	intr_mask |= DWC_INTMSK_GLBL_IN_NAK;
+	dwc_modify32((u32) (GET_CORE_IF(pcd)->core_global_regs) + DWC_GINTMSK,
+		     intr_mask, 0);
+
+	/* Clear interrupt */
+	gintsts |= DWC_INTSTS_GLBL_IN_NAK;
+	dwc_write32((u32) (GET_CORE_IF(pcd)->core_global_regs) + DWC_GINTSTS,
+		    gintsts);
+	return 1;
+}
+
+/**
+ * This function handles the Global OUT NAK Effective interrupt.
+ */
+static int dwc_otg_pcd_handle_out_nak_effective(struct dwc_pcd *pcd)
+{
+	u32 intr_mask = 0;
+	u32 gintsts = 0;
+
+	pr_info("Interrupt handler not implemented for Global IN "
+		"NAK Effective\n");
+
+	/* Turn off and clear the interrupt */
+	intr_mask |= DWC_INTMSK_GLBL_OUT_NAK;
+	dwc_modify32((u32) (GET_CORE_IF(pcd)->core_global_regs) + DWC_GINTMSK,
+		     intr_mask, 0);
+
+	/* Clear goutnakeff */
+	gintsts |= DWC_INTSTS_GLBL_OUT_NAK;
+	dwc_write32((u32) (GET_CORE_IF(pcd)->core_global_regs) + DWC_GINTSTS,
+		    gintsts);
+	return 1;
+}
+
+/**
+ * PCD interrupt handler.
+ *
+ * The PCD handles the device interrupts.  Many conditions can cause a
+ * device interrupt. When an interrupt occurs, the device interrupt
+ * service routine determines the cause of the interrupt and
+ * dispatches handling to the appropriate function. These interrupt
+ * handling functions are described below.
+ *
+ * All interrupt registers are processed from LSB to MSB.
+ *
+ */
+int dwc_otg_pcd_handle_intr(struct dwc_pcd *pcd)
+{
+	struct core_if *core_if = GET_CORE_IF(pcd);
+	u32 gintr_status;
+	int ret = 0;
+
+	if (dwc_otg_is_device_mode(core_if)) {
+		spin_lock(&pcd->lock);
+
+		gintr_status = dwc_otg_read_core_intr(core_if);
+		if (!gintr_status) {
+			spin_unlock(&pcd->lock);
+			return 0;
+		}
+
+		if (gintr_status & DWC_INTSTS_STRT_OF_FRM)
+			ret |= dwc_otg_pcd_handle_sof_intr(pcd);
+		if (gintr_status & DWC_INTSTS_RXFIFO_NOT_EMPT)
+			ret |= dwc_otg_pcd_handle_rx_status_q_level_intr(pcd);
+		if (gintr_status & DWC_INTSTS_NP_TXFIFO_EMPT)
+			ret |= dwc_otg_pcd_handle_np_tx_fifo_empty_intr(pcd);
+		if (gintr_status & DWC_INTSTS_GLBL_IN_NAK)
+			ret |= dwc_otg_pcd_handle_in_nak_effective(pcd);
+		if (gintr_status & DWC_INTSTS_GLBL_OUT_NAK)
+			ret |= dwc_otg_pcd_handle_out_nak_effective(pcd);
+		if (gintr_status & DWC_INTSTS_I2C_INTR)
+			ret |= dwc_otg_pcd_handle_i2c_intr(pcd);
+		if (gintr_status & DWC_INTSTS_EARLY_SUSP)
+			ret |= dwc_otg_pcd_handle_early_suspend_intr(pcd);
+		if (gintr_status & DWC_INTSTS_USB_RST)
+			ret |= dwc_otg_pcd_handle_usb_reset_intr(pcd);
+		if (gintr_status & DWC_INTSTS_ENUM_DONE)
+			ret |= dwc_otg_pcd_handle_enum_done_intr(pcd);
+		if (gintr_status & DWC_INTSTS_ISYNC_OUTPKT_DRP)
+			ret |=
+			    dwc_otg_pcd_handle_isoc_out_packet_dropped_intr
+			    (pcd);
+		if (gintr_status & DWC_INTSTS_END_OF_PFRM)
+			ret |= dwc_otg_pcd_handle_end_periodic_frame_intr(pcd);
+		if (gintr_status & DWC_INTSTS_ENDP_MIS_MTCH)
+			ret |= dwc_otg_pcd_handle_ep_mismatch_intr(core_if);
+		if (gintr_status & DWC_INTSTS_IN_ENDP)
+			ret |= dwc_otg_pcd_handle_in_ep_intr(pcd);
+		if (gintr_status & DWC_INTSTS_OUT_ENDP)
+			ret |= dwc_otg_pcd_handle_out_ep_intr(pcd);
+		if (gintr_status & DWC_INTSTS_INCMP_IN_ATX)
+			ret |= dwc_otg_pcd_handle_incomplete_isoc_in_intr(pcd);
+		if (gintr_status & DWC_INTSTS_INCMP_OUT_PTX)
+			ret |= dwc_otg_pcd_handle_incomplete_isoc_out_intr(pcd);
+
+		spin_unlock(&pcd->lock);
+	}
+	return ret;
+}
-- 
1.6.1.rc3

^ permalink raw reply related

* [PATCH V7 07/10] USB/ppc4xx: Add Synopsys DWC OTG PCD 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>

The PCD is responsible for translating requests from the gadget driver
to appropriate actions on the DWC 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_pcd.c | 1752 +++++++++++++++++++++++++++++++++++++
 drivers/usb/dwc_otg/dwc_otg_pcd.h |  139 +++
 2 files changed, 1891 insertions(+), 0 deletions(-)

diff --git a/drivers/usb/dwc_otg/dwc_otg_pcd.c b/drivers/usb/dwc_otg/dwc_otg_pcd.c
new file mode 100644
index 0000000..857dcee
--- /dev/null
+++ b/drivers/usb/dwc_otg/dwc_otg_pcd.c
@@ -0,0 +1,1752 @@
+/*
+ * 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.
+ *
+ */
+
+/*
+ * This file implements the Peripheral Controller Driver.
+ *
+ * The Peripheral Controller Driver (PCD) is responsible for
+ * translating requests from the Function Driver into the appropriate
+ * actions on the DWC_otg controller. It isolates the Function Driver
+ * from the specifics of the controller by providing an API to the
+ * Function Driver.
+ *
+ * The Peripheral Controller Driver for Linux will implement the
+ * Gadget API, so that the existing Gadget drivers can be used.
+ * (Gadget Driver is the Linux terminology for a Function Driver.)
+ *
+ * The Linux Gadget API is defined in the header file linux/usb/gadget.h. The
+ * USB EP operations API is defined in the structure usb_ep_ops and the USB
+ * Controller API is defined in the structure usb_gadget_ops
+ *
+ * An important function of the PCD is managing interrupts generated
+ * by the DWC_otg controller. The implementation of the DWC_otg device
+ * mode interrupt service routines is in dwc_otg_pcd_intr.c.
+ */
+
+#include <linux/dma-mapping.h>
+#include <linux/delay.h>
+
+#include "dwc_otg_pcd.h"
+
+/*
+ * Static PCD pointer for use in usb_gadget_register_driver and
+ * usb_gadget_unregister_driver.  Initialized in dwc_otg_pcd_init.
+ */
+static struct dwc_pcd *s_pcd;
+
+static inline int need_stop_srp_timer(struct core_if *core_if)
+{
+	if (core_if->core_params->phy_type != DWC_PHY_TYPE_PARAM_FS ||
+	    !core_if->core_params->i2c_enable)
+		return core_if->srp_timer_started ? 1 : 0;
+	return 0;
+}
+
+/**
+ * Tests if the module is set to FS or if the PHY_TYPE is FS. If so, then the
+ * gadget should not report as dual-speed capable.
+ */
+static inline int check_is_dual_speed(struct core_if *core_if)
+{
+	if (core_if->core_params->speed == DWC_SPEED_PARAM_FULL ||
+	    (DWC_HWCFG2_HS_PHY_TYPE_RD(core_if->hwcfg2) == 2 &&
+	     DWC_HWCFG2_P_2_P_RD(core_if->hwcfg2) == 1 &&
+	     core_if->core_params->ulpi_fs_ls))
+		return 0;
+	return 1;
+}
+
+/**
+ * Tests if driver is OTG capable.
+ */
+static inline int check_is_otg(struct core_if *core_if)
+{
+	if (DWC_HWCFG2_OP_MODE_RD(core_if->hwcfg2) ==
+	    DWC_HWCFG2_OP_MODE_NO_SRP_CAPABLE_DEVICE ||
+	    DWC_HWCFG2_OP_MODE_RD(core_if->hwcfg2) ==
+	    DWC_HWCFG2_OP_MODE_NO_SRP_CAPABLE_HOST ||
+	    DWC_HWCFG2_OP_MODE_RD(core_if->hwcfg2) ==
+	    DWC_HWCFG2_OP_MODE_SRP_CAPABLE_DEVICE ||
+	    DWC_HWCFG2_OP_MODE_RD(core_if->hwcfg2) ==
+	    DWC_HWCFG2_OP_MODE_SRP_CAPABLE_HOST)
+		return 0;
+	return 1;
+}
+
+/**
+ * This function completes a request. It calls the request call back.
+ */
+void request_done(struct pcd_ep *ep, struct pcd_request *req, int status)
+{
+	unsigned stopped = ep->stopped;
+
+	list_del_init(&req->queue);
+	if (req->req.status == -EINPROGRESS)
+		req->req.status = status;
+	else
+		status = req->req.status;
+
+	if (GET_CORE_IF(ep->pcd)->dma_enable) {
+		if (req->mapped) {
+			dma_unmap_single(ep->pcd->gadget.dev.parent,
+					 req->req.dma, req->req.length,
+					 ep->dwc_ep.is_in ? DMA_TO_DEVICE :
+					 DMA_FROM_DEVICE);
+			req->req.dma = DMA_ADDR_INVALID;
+			req->mapped = 0;
+		} else {
+			dma_sync_single_for_cpu(ep->pcd->gadget.dev.parent,
+						req->req.dma, req->req.length,
+						ep->dwc_ep.
+						is_in ? DMA_TO_DEVICE :
+						DMA_FROM_DEVICE);
+		}
+	}
+
+	/* don't modify queue heads during completion callback */
+	ep->stopped = 1;
+	spin_unlock(&ep->pcd->lock);
+	req->req.complete(&ep->ep, &req->req);
+	spin_lock(&ep->pcd->lock);
+
+	if (ep->pcd->request_pending > 0)
+		--ep->pcd->request_pending;
+	ep->stopped = stopped;
+
+	/*
+	 * Added-sr: 2007-07-26
+	 *
+	 * Finally, when the current request is done, mark this endpoint
+	 * as not active, so that new requests can be processed.
+	 */
+	if (dwc_has_feature(GET_CORE_IF(ep->pcd), DWC_LIMITED_XFER))
+		ep->dwc_ep.active = 0;
+}
+
+/**
+ * This function terminates all the requsts in the EP request queue.
+ */
+void request_nuke(struct pcd_ep *ep)
+{
+	struct pcd_request *req;
+
+	ep->stopped = 1;
+
+	/* called with irqs blocked?? */
+	while (!list_empty(&ep->queue)) {
+		req = list_entry(ep->queue.next, struct pcd_request, queue);
+		request_done(ep, req, -ESHUTDOWN);
+	}
+}
+
+/*
+ * The following sections briefly describe the behavior of the Gadget
+ * API endpoint operations implemented in the DWC_otg driver
+ * software. Detailed descriptions of the generic behavior of each of
+ * these functions can be found in the Linux header file
+ * include/linux/usb_gadget.h.
+ *
+ * The Gadget API provides wrapper functions for each of the function
+ * pointers defined in usb_ep_ops. The Gadget Driver calls the wrapper
+ * function, which then calls the underlying PCD function. The
+ * following sections are named according to the wrapper
+ * functions. Within each section, the corresponding DWC_otg PCD
+ * function name is specified.
+ *
+ */
+
+/**
+ * This function assigns periodic Tx FIFO to an periodic EP in shared Tx FIFO
+ * mode
+ */
+static u32 assign_perio_tx_fifo(struct core_if *core_if)
+{
+	u32 mask = 1;
+	u32 i;
+
+	for (i = 0; i < DWC_HWCFG4_NUM_DEV_PERIO_IN_EP_RD(core_if->hwcfg4);
+			++i) {
+		if (!(mask & core_if->p_tx_msk)) {
+			core_if->p_tx_msk |= mask;
+			return i + 1;
+		}
+		mask <<= 1;
+	}
+	return 0;
+}
+
+/**
+ * This function releases periodic Tx FIFO in shared Tx FIFO mode
+ */
+static void release_perio_tx_fifo(struct core_if *core_if, u32 fifo_num)
+{
+	core_if->p_tx_msk = (core_if->p_tx_msk & (1 << (fifo_num - 1)))
+	    ^ core_if->p_tx_msk;
+}
+
+/**
+ * This function assigns periodic Tx FIFO to an periodic EP in shared Tx FIFO
+ * mode
+ */
+static u32 assign_tx_fifo(struct core_if *core_if)
+{
+	u32 mask = 1;
+	u32 i;
+
+	for (i = 0; i < DWC_HWCFG4_NUM_IN_EPS_RD(core_if->hwcfg4); ++i) {
+		if (!(mask & core_if->tx_msk)) {
+			core_if->tx_msk |= mask;
+			return i + 1;
+		}
+		mask <<= 1;
+	}
+	return 0;
+}
+
+/**
+ * This function releases periodic Tx FIFO in shared Tx FIFO mode
+ */
+static void release_tx_fifo(struct core_if *core_if, u32 fifo_num)
+{
+	core_if->tx_msk = (core_if->tx_msk & (1 << (fifo_num - 1)))
+	    ^ core_if->tx_msk;
+}
+
+/**
+ * Sets an in endpoint's tx fifo based on the hardware configuration.
+ */
+static void set_in_ep_tx_fifo(struct dwc_pcd *pcd, struct pcd_ep *ep,
+			      const struct usb_endpoint_descriptor *desc)
+{
+	if (pcd->otg_dev->core_if->en_multiple_tx_fifo) {
+		ep->dwc_ep.tx_fifo_num = assign_tx_fifo(pcd->otg_dev->core_if);
+	} else {
+		ep->dwc_ep.tx_fifo_num = 0;
+
+		/* If ISOC EP then assign a Periodic Tx FIFO. */
+		if ((desc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) ==
+		    USB_ENDPOINT_XFER_ISOC)
+			ep->dwc_ep.tx_fifo_num =
+			    assign_perio_tx_fifo(pcd->otg_dev->core_if);
+	}
+}
+
+/**
+ * This function activates an EP.  The Device EP control register for
+ * the EP is configured as defined in the ep structure.  Note: This function is
+ * not used for EP0.
+ */
+void dwc_otg_ep_activate(struct core_if *core_if, struct dwc_ep *ep)
+{
+	struct device_if *dev_if = core_if->dev_if;
+	u32 depctl = 0;
+	u32 addr;
+	u32 daintmsk = 0;
+
+	/* Read DEPCTLn register */
+	if (ep->is_in == 1) {
+		addr = (u32) dev_if->in_ep_regs[ep->num] + DWC_DIEPCTL;
+		daintmsk = DWC_DAINTMSK_IN_EP_RW(daintmsk, ep->num);
+	} else {
+		addr = (u32) dev_if->out_ep_regs[ep->num] + DWC_DOEPCTL;
+		daintmsk = DWC_DAINTMSK_OUT_EP_RW(daintmsk, ep->num);
+	}
+
+	/* If the EP is already active don't change the EP Control register */
+	depctl = dwc_read32(addr);
+	if (!DWC_DEPCTL_ACT_EP_RD(depctl)) {
+		depctl = DWC_DEPCTL_MPS_RW(depctl, ep->maxpacket);
+		depctl = DWC_DEPCTL_EP_TYPE_RW(depctl, ep->type);
+		depctl = DWC_DEPCTL_TX_FIFO_NUM_RW(depctl, ep->tx_fifo_num);
+		depctl = DWC_DEPCTL_SET_DATA0_PID_RW(depctl, 1);
+		depctl = DWC_DEPCTL_ACT_EP_RW(depctl, 1);
+		dwc_write32(addr, depctl);
+	}
+
+	/* Enable the Interrupt for this EP */
+	dwc_modify32((u32) dev_if->dev_global_regs + DWC_DAINTMSK, 0, daintmsk);
+
+	ep->stall_clear_flag = 0;
+}
+
+/**
+ * This function is called by the Gadget Driver for each EP to be
+ * configured for the current configuration (SET_CONFIGURATION).
+ *
+ * This function initializes the dwc_otg_ep_t data structure, and then
+ * calls dwc_otg_ep_activate.
+ */
+static int dwc_otg_pcd_ep_enable(struct usb_ep *_ep,
+				 const struct usb_endpoint_descriptor *desc)
+{
+	struct pcd_ep *ep;
+	struct dwc_pcd *pcd;
+	unsigned long flags;
+
+	ep = container_of(_ep, struct pcd_ep, ep);
+	if (!_ep || !desc || ep->desc || desc->bDescriptorType !=
+	    USB_DT_ENDPOINT) {
+		pr_warning("%s, bad ep or descriptor\n", __func__);
+		return -EINVAL;
+	}
+
+	if (ep == &ep->pcd->ep0) {
+		pr_warning("%s, bad ep(0)\n", __func__);
+		return -EINVAL;
+	}
+
+	/* Check FIFO size */
+	if (!desc->wMaxPacketSize) {
+		pr_warning("%s, bad %s maxpacket\n", __func__, _ep->name);
+		return -ERANGE;
+	}
+
+	pcd = ep->pcd;
+	if (!pcd->driver || pcd->gadget.speed == USB_SPEED_UNKNOWN) {
+		pr_warning("%s, bogus device state\n", __func__);
+		return -ESHUTDOWN;
+	}
+
+	spin_lock_irqsave(&pcd->lock, flags);
+	ep->desc = desc;
+	ep->ep.maxpacket = le16_to_cpu(desc->wMaxPacketSize);
+
+	/* Activate the EP */
+	ep->stopped = 0;
+	ep->dwc_ep.is_in = (USB_DIR_IN & desc->bEndpointAddress) != 0;
+	ep->dwc_ep.maxpacket = ep->ep.maxpacket;
+	ep->dwc_ep.type = desc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK;
+
+	if (ep->dwc_ep.is_in)
+		set_in_ep_tx_fifo(pcd, ep, desc);
+
+	/* Set initial data PID. */
+	if ((desc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) ==
+	    USB_ENDPOINT_XFER_BULK)
+		ep->dwc_ep.data_pid_start = 0;
+
+	dwc_otg_ep_activate(GET_CORE_IF(pcd), &ep->dwc_ep);
+	spin_unlock_irqrestore(&pcd->lock, flags);
+	return 0;
+}
+
+/**
+ * This function deactivates an EP.  This is done by clearing the USB Active EP
+ * bit in the Device EP control register.  Note: This function is not used for
+ * EP0. EP0 cannot be deactivated.
+ */
+static void dwc_otg_ep_deactivate(struct core_if *core_if, struct dwc_ep *ep)
+{
+	u32 depctl = 0;
+	u32 addr;
+	u32 daintmsk = 0;
+
+	/* Read DEPCTLn register */
+	if (ep->is_in == 1) {
+		addr = (u32) core_if->dev_if->in_ep_regs[ep->num] + DWC_DIEPCTL;
+		daintmsk = DWC_DAINTMSK_IN_EP_RW(daintmsk, ep->num);
+	} else {
+		addr =
+		    (u32) core_if->dev_if->out_ep_regs[ep->num] + DWC_DOEPCTL;
+		daintmsk = DWC_DAINTMSK_OUT_EP_RW(daintmsk, ep->num);
+	}
+
+	depctl = DWC_DEPCTL_ACT_EP_RW(depctl, 0);
+	dwc_write32(addr, depctl);
+
+	/* Disable the Interrupt for this EP */
+	dwc_modify32((u32) core_if->dev_if->dev_global_regs + DWC_DAINTMSK,
+		     daintmsk, 0);
+}
+
+/**
+ * This function is called when an EP is disabled due to disconnect or
+ * change in configuration. Any pending requests will terminate with a
+ * status of -ESHUTDOWN.
+ *
+ * This function modifies the dwc_otg_ep_t data structure for this EP,
+ * and then calls dwc_otg_ep_deactivate.
+ */
+static int dwc_otg_pcd_ep_disable(struct usb_ep *_ep)
+{
+	struct pcd_ep *ep;
+	struct core_if *core_if;
+	unsigned long flags;
+
+	ep = container_of(_ep, struct pcd_ep, ep);
+	if (!_ep || !ep->desc)
+		return -EINVAL;
+
+	core_if = ep->pcd->otg_dev->core_if;
+
+	spin_lock_irqsave(&ep->pcd->lock, flags);
+
+	request_nuke(ep);
+	dwc_otg_ep_deactivate(core_if, &ep->dwc_ep);
+
+	ep->desc = NULL;
+	ep->stopped = 1;
+	if (ep->dwc_ep.is_in) {
+		release_perio_tx_fifo(core_if, ep->dwc_ep.tx_fifo_num);
+		release_tx_fifo(core_if, ep->dwc_ep.tx_fifo_num);
+	}
+
+	spin_unlock_irqrestore(&ep->pcd->lock, flags);
+
+	return 0;
+}
+
+/**
+ * This function allocates a request object to use with the specified
+ * endpoint.
+ */
+static struct usb_request *dwc_otg_pcd_alloc_request(struct usb_ep *_ep,
+						     gfp_t gfp_flags)
+{
+	struct pcd_request *req;
+
+	if (!_ep) {
+		pr_warning("%s() Invalid EP\n", __func__);
+		return NULL;
+	}
+
+	req = kzalloc(sizeof(struct pcd_request), gfp_flags);
+	if (!req) {
+		pr_warning("%s() request allocation failed\n", __func__);
+		return NULL;
+	}
+
+	req->req.dma = DMA_ADDR_INVALID;
+	INIT_LIST_HEAD(&req->queue);
+
+	return &req->req;
+}
+
+/**
+ * This function frees a request object.
+ */
+static void dwc_otg_pcd_free_request(struct usb_ep *_ep,
+				     struct usb_request *_req)
+{
+	struct pcd_request *req;
+
+	if (!_ep || !_req) {
+		pr_warning("%s() nvalid ep or req argument\n", __func__);
+		return;
+	}
+
+	req = container_of(_req, struct pcd_request, req);
+	kfree(req);
+}
+
+/*
+ * In dedicated Tx FIFO mode, enable the Non-Periodic Tx FIFO empty interrupt.
+ * Otherwise, enable the Tx FIFO epmty interrupt. The data will be written into
+ * the fifo by the ISR.
+ */
+static void enable_tx_fifo_empty_intr(struct core_if *c_if, struct dwc_ep *ep)
+{
+	u32 intr_mask = 0;
+	struct device_if *d_if = c_if->dev_if;
+	u32 global_regs = (u32) c_if->core_global_regs;
+
+	if (!c_if->en_multiple_tx_fifo) {
+		intr_mask |= DWC_INTMSK_NP_TXFIFO_EMPT;
+		dwc_modify32(global_regs + DWC_GINTSTS, intr_mask, 0);
+		dwc_modify32(global_regs + DWC_GINTMSK, intr_mask, intr_mask);
+	} else if (ep->xfer_len) {
+		/* Enable the Tx FIFO Empty Interrupt for this EP */
+		u32 fifoemptymsk = 1 << ep->num;
+		dwc_modify32((u32) d_if->dev_global_regs +
+			     DWC_DTKNQR4FIFOEMPTYMSK, 0, fifoemptymsk);
+	}
+}
+
+static void set_next_ep(struct device_if *dev_if, u8 num)
+{
+	u32 depctl = 0;
+
+	depctl = dwc_read32((u32) dev_if->in_ep_regs[0]) + DWC_DIEPCTL;
+	depctl = DWC_DEPCTL_NXT_EP_RW(depctl, num);
+
+	dwc_write32(((u32) dev_if->in_ep_regs[0]) + DWC_DIEPCTL, depctl);
+}
+
+/**
+ * This function does the setup for a data transfer for an EP and
+ * starts the transfer.  For an IN transfer, the packets will be loaded into the
+ * appropriate Tx FIFO in the ISR. For OUT transfers, the packets are unloaded
+ * from the Rx FIFO in the ISR.
+ *
+ */
+void dwc_otg_ep_start_transfer(struct core_if *c_if, struct dwc_ep *ep)
+{
+	u32 depctl = 0;
+	u32 deptsiz = 0;
+	struct device_if *d_if = c_if->dev_if;
+	u32 glbl_regs = (u32) c_if->core_global_regs;
+
+	if (ep->is_in) {
+		u32 in_regs = (u32) d_if->in_ep_regs[ep->num];
+		u32 gtxstatus;
+
+		gtxstatus = dwc_read32(glbl_regs + DWC_GNPTXSTS);
+		if (!c_if->en_multiple_tx_fifo
+		    && !DWC_GNPTXSTS_NPTXQSPCAVAIL_RD(gtxstatus))
+			return;
+
+		depctl = dwc_read32((u32) in_regs + DWC_DIEPCTL);
+		deptsiz = dwc_read32((u32) in_regs + DWC_DIEPTSIZ);
+
+		/* Zero Length Packet? */
+		if (!ep->xfer_len) {
+			deptsiz = DWC_DEPTSIZ_XFER_SIZ_RW(deptsiz, 0);
+			deptsiz = DWC_DEPTSIZ_PKT_CNT_RW(deptsiz, 1);
+		} else {
+			/*
+			 * Program the transfer size and packet count as
+			 * follows:
+			 *
+			 * xfersize = N * maxpacket + short_packet
+			 * pktcnt = N + (short_packet exist ? 1 : 0)
+			 */
+
+			/*
+			 * Added-sr: 2007-07-26
+			 *
+			 * Since the 405EZ (Ultra) only support 2047 bytes as
+			 * max transfer size, we have to split up bigger
+			 * transfers into multiple transfers of 1024 bytes sized
+			 * messages. I happens often, that transfers of 4096
+			 * bytes are required (zero-gadget,
+			 * file_storage-gadget).
+			 */
+			if (dwc_has_feature(c_if, DWC_LIMITED_XFER)) {
+				if (ep->xfer_len > MAX_XFER_LEN) {
+					ep->bytes_pending = ep->xfer_len
+					    - MAX_XFER_LEN;
+					ep->xfer_len = MAX_XFER_LEN;
+				}
+			}
+
+			deptsiz =
+			    DWC_DEPTSIZ_XFER_SIZ_RW(deptsiz, ep->xfer_len);
+			deptsiz =
+			    DWC_DEPTSIZ_PKT_CNT_RW(deptsiz,
+						   ((ep->xfer_len - 1 +
+						     ep->maxpacket) /
+						    ep->maxpacket));
+		}
+		dwc_write32((u32) in_regs + DWC_DIEPTSIZ, deptsiz);
+
+		if (c_if->dma_enable)
+			dwc_write32((u32) in_regs + DWC_DIEPDMA, ep->dma_addr);
+		else if (ep->type != DWC_OTG_EP_TYPE_ISOC)
+			enable_tx_fifo_empty_intr(c_if, ep);
+
+		/* EP enable, IN data in FIFO */
+		depctl = DWC_DEPCTL_CLR_NAK_RW(depctl, 1);
+		depctl = DWC_DEPCTL_EPENA_RW(depctl, 1);
+		dwc_write32((u32) in_regs + DWC_DIEPCTL, depctl);
+
+		if (c_if->dma_enable)
+			set_next_ep(d_if, ep->num);
+	} else {
+		u32 out_regs = (u32) d_if->out_ep_regs[ep->num];
+
+		depctl = dwc_read32(out_regs + DWC_DOEPCTL);
+		deptsiz = dwc_read32(out_regs + DWC_DOEPTSIZ);
+
+		/*
+		 * Program the transfer size and packet count as follows:
+		 *
+		 * pktcnt = N
+		 * xfersize = N * maxpacket
+		 */
+		if (!ep->xfer_len) {
+			deptsiz =
+			    DWC_DEPTSIZ_XFER_SIZ_RW(deptsiz, ep->maxpacket);
+			deptsiz = DWC_DEPTSIZ_PKT_CNT_RW(deptsiz, 1);
+		} else {
+			deptsiz = DWC_DEPTSIZ_PKT_CNT_RW(deptsiz,
+							 ((ep->xfer_len +
+							   ep->maxpacket -
+							   1) / ep->maxpacket));
+			deptsiz =
+			    DWC_DEPTSIZ_XFER_SIZ_RW(deptsiz,
+						    DWC_DEPTSIZ_PKT_CNT_RD
+						    (deptsiz) * ep->maxpacket);
+		}
+		dwc_write32(out_regs + DWC_DOEPTSIZ, deptsiz);
+
+		if (c_if->dma_enable)
+			dwc_write32(out_regs + DWC_DOEPDMA, ep->dma_addr);
+
+		if (ep->type == DWC_OTG_EP_TYPE_ISOC) {
+			if (ep->even_odd_frame)
+				depctl = DWC_DEPCTL_SET_DATA1_PID_RW(depctl, 1);
+			else
+				depctl = DWC_DEPCTL_SET_DATA0_PID_RW(depctl, 1);
+		}
+
+		/* EP enable */
+		depctl = DWC_DEPCTL_CLR_NAK_RW(depctl, 1);
+		depctl = DWC_DEPCTL_EPENA_RW(depctl, 1);
+		dwc_write32(out_regs + DWC_DOEPCTL, depctl);
+	}
+}
+
+/**
+ * This function does the setup for a data transfer for EP0 and starts
+ * the transfer.  For an IN transfer, the packets will be loaded into
+ * the appropriate Tx FIFO in the ISR. For OUT transfers, the packets are
+ * unloaded from the Rx FIFO in the ISR.
+ */
+void dwc_otg_ep0_start_transfer(struct core_if *c_if, struct dwc_ep *ep)
+{
+	u32 depctl = 0;
+	u32 deptsiz = 0;
+	struct device_if *d_if = c_if->dev_if;
+	u32 glbl_regs = (u32) c_if->core_global_regs;
+
+	ep->total_len = ep->xfer_len;
+
+	if (ep->is_in) {
+		u32 in_regs = (u32) d_if->in_ep_regs[0];
+		u32 gtxstatus;
+
+		gtxstatus = dwc_read32(glbl_regs + DWC_GNPTXSTS);
+
+		if (!c_if->en_multiple_tx_fifo
+		    && !DWC_GNPTXSTS_NPTXQSPCAVAIL_RD(gtxstatus))
+			return;
+
+		depctl = dwc_read32((u32) in_regs + DWC_DIEPCTL);
+		deptsiz = dwc_read32(in_regs + DWC_DIEPTSIZ);
+
+		/* Zero Length Packet? */
+		if (!ep->xfer_len) {
+			deptsiz = DWC_DEPTSIZ0_XFER_SIZ_RW(deptsiz, 0);
+			deptsiz = DWC_DEPTSIZ0_PKT_CNT_RW(deptsiz, 1);
+		} else {
+			/*
+			 * Program the transfer size and packet count  as
+			 * follows:
+			 *
+			 *  xfersize = N * maxpacket + short_packet
+			 *  pktcnt = N + (short_packet exist ? 1 : 0)
+			 */
+			if (ep->xfer_len > ep->maxpacket) {
+				ep->xfer_len = ep->maxpacket;
+				deptsiz = DWC_DEPTSIZ0_XFER_SIZ_RW(deptsiz,
+								   ep->
+								   maxpacket);
+			} else {
+				deptsiz = DWC_DEPTSIZ0_XFER_SIZ_RW(deptsiz,
+								   ep->
+								   xfer_len);
+			}
+			deptsiz = DWC_DEPTSIZ0_PKT_CNT_RW(deptsiz, 1);
+		}
+		dwc_write32(in_regs + DWC_DIEPTSIZ, deptsiz);
+
+		if (c_if->dma_enable)
+			dwc_write32(in_regs + DWC_DIEPDMA, ep->dma_addr);
+
+		/* EP enable, IN data in FIFO */
+		depctl = DWC_DEPCTL_CLR_NAK_RW(depctl, 1);
+		depctl = DWC_DEPCTL_EPENA_RW(depctl, 1);
+		dwc_write32(in_regs + DWC_DIEPCTL, depctl);
+
+		if (!c_if->dma_enable)
+			enable_tx_fifo_empty_intr(c_if, ep);
+	} else {
+		u32 out_regs = (u32) d_if->out_ep_regs[ep->num];
+
+		depctl = dwc_read32(out_regs + DWC_DOEPCTL);
+		deptsiz = dwc_read32(out_regs + DWC_DOEPTSIZ);
+
+		/*
+		 * Program the transfer size and packet count as follows:
+		 *
+		 * xfersize = N * (maxpacket + 4 - (maxpacket % 4))
+		 * pktcnt = N
+		 */
+		if (!ep->xfer_len) {
+			deptsiz = DWC_DEPTSIZ0_XFER_SIZ_RW(deptsiz,
+							   ep->maxpacket);
+			deptsiz = DWC_DEPTSIZ0_PKT_CNT_RW(deptsiz, 1);
+		} else {
+			deptsiz = DWC_DEPTSIZ0_PKT_CNT_RW(deptsiz,
+							  (ep->xfer_len +
+							   ep->maxpacket -
+							   1) / ep->maxpacket);
+			deptsiz =
+			    DWC_DEPTSIZ0_XFER_SIZ_RW(deptsiz,
+						     DWC_DEPTSIZ_PKT_CNT_RD
+						     (deptsiz) * ep->maxpacket);
+		}
+		dwc_write32(out_regs + DWC_DOEPTSIZ, deptsiz);
+
+		if (c_if->dma_enable)
+			dwc_write32(out_regs + DWC_DOEPDMA, ep->dma_addr);
+
+		/* EP enable */
+		depctl = DWC_DEPCTL_CLR_NAK_RW(depctl, 1);
+		depctl = DWC_DEPCTL_EPENA_RW(depctl, 1);
+		dwc_write32(out_regs + DWC_DOEPCTL, depctl);
+	}
+}
+
+/**
+ * This function is used to submit an I/O Request to an EP.
+ *
+ *	- When the request completes the request's completion callback
+ *	  is called to return the request to the driver.
+ *	- An EP, except control EPs, may have multiple requests
+ *	  pending.
+ *	- Once submitted the request cannot be examined or modified.
+ *	- Each request is turned into one or more packets.
+ *	- A BULK EP can queue any amount of data; the transfer is
+ *	  packetized.
+ *	- Zero length Packets are specified with the request 'zero'
+ *	  flag.
+ */
+static int dwc_otg_pcd_ep_queue(struct usb_ep *_ep, struct usb_request *_req,
+				gfp_t gfp_flags)
+{
+	int prevented = 0;
+	struct pcd_request *req;
+	struct pcd_ep *ep;
+	struct dwc_pcd *pcd;
+	struct core_if *core_if;
+	unsigned long flags = 0;
+
+	req = container_of(_req, struct pcd_request, req);
+	if (!_req || !_req->complete || !_req->buf ||
+			!list_empty(&req->queue)) {
+		pr_warning("%s, bad params\n", __func__);
+		return -EINVAL;
+	}
+
+	ep = container_of(_ep, struct pcd_ep, ep);
+	if (!_ep || (!ep->desc && ep->dwc_ep.num != 0)) {
+		pr_warning("%s, bad ep\n", __func__);
+		return -EINVAL;
+	}
+
+	pcd = ep->pcd;
+	if (!pcd->driver || pcd->gadget.speed == USB_SPEED_UNKNOWN) {
+		pr_warning("%s, bogus device state\n", __func__);
+		return -ESHUTDOWN;
+	}
+	core_if = pcd->otg_dev->core_if;
+
+	if (GET_CORE_IF(pcd)->dma_enable) {
+		if (_req->dma == DMA_ADDR_INVALID) {
+			_req->dma = dma_map_single(pcd->gadget.dev.parent,
+						   _req->buf, _req->length,
+						   ep->dwc_ep.
+						   is_in ? DMA_TO_DEVICE :
+						   DMA_FROM_DEVICE);
+			req->mapped = 1;
+		} else {
+			dma_sync_single_for_device(pcd->gadget.dev.parent,
+						   _req->dma, _req->length,
+						   ep->dwc_ep.
+						   is_in ? DMA_TO_DEVICE :
+						   DMA_FROM_DEVICE);
+			req->mapped = 0;
+		}
+	}
+
+	spin_lock_irqsave(&ep->pcd->lock, flags);
+
+	_req->status = -EINPROGRESS;
+	_req->actual = 0;
+
+	/* Start the transfer */
+	if (list_empty(&ep->queue) && !ep->stopped) {
+		/* EP0 Transfer? */
+		if (ep->dwc_ep.num == 0) {
+			switch (pcd->ep0state) {
+			case EP0_IN_DATA_PHASE:
+				break;
+			case EP0_OUT_DATA_PHASE:
+				if (pcd->request_config) {
+					/* Complete STATUS PHASE */
+					ep->dwc_ep.is_in = 1;
+					pcd->ep0state = EP0_STATUS;
+				}
+				break;
+			default:
+				spin_unlock_irqrestore(&pcd->lock, flags);
+				return -EL2HLT;
+			}
+
+			ep->dwc_ep.dma_addr = _req->dma;
+			ep->dwc_ep.start_xfer_buff = _req->buf;
+			ep->dwc_ep.xfer_buff = _req->buf;
+			ep->dwc_ep.xfer_len = _req->length;
+			ep->dwc_ep.xfer_count = 0;
+			ep->dwc_ep.sent_zlp = 0;
+			ep->dwc_ep.total_len = ep->dwc_ep.xfer_len;
+
+			dwc_otg_ep0_start_transfer(core_if, &ep->dwc_ep);
+		} else {
+			/* Setup and start the Transfer */
+			ep->dwc_ep.dma_addr = _req->dma;
+			ep->dwc_ep.start_xfer_buff = _req->buf;
+			ep->dwc_ep.xfer_buff = _req->buf;
+			ep->dwc_ep.xfer_len = _req->length;
+			ep->dwc_ep.xfer_count = 0;
+			ep->dwc_ep.sent_zlp = 0;
+			ep->dwc_ep.total_len = ep->dwc_ep.xfer_len;
+
+			dwc_otg_ep_start_transfer(core_if, &ep->dwc_ep);
+		}
+	}
+
+	if (req || prevented) {
+		++pcd->request_pending;
+		list_add_tail(&req->queue, &ep->queue);
+
+		if (ep->dwc_ep.is_in && ep->stopped && !core_if->dma_enable) {
+			/*
+			 *  Device IN endpoint interrupt mask register is laid
+			 *  out exactly the same as the device IN endpoint
+			 *  interrupt register.
+			 */
+			u32 diepmsk = 0;
+			diepmsk = DWC_DIEPMSK_IN_TKN_TX_EMPTY_RW(diepmsk, 1);
+
+			dwc_modify32((u32) core_if->dev_if->dev_global_regs +
+				     DWC_DIEPMSK, 0, diepmsk);
+		}
+	}
+
+	spin_unlock_irqrestore(&pcd->lock, flags);
+	return 0;
+}
+
+/**
+ * This function cancels an I/O request from an EP.
+ */
+static int dwc_otg_pcd_ep_dequeue(struct usb_ep *_ep, struct usb_request *_req)
+{
+	struct pcd_request *req;
+	struct pcd_ep *ep;
+	struct dwc_pcd *pcd;
+	unsigned long flags;
+
+	ep = container_of(_ep, struct pcd_ep, ep);
+	if (!_ep || !_req || (!ep->desc && ep->dwc_ep.num != 0)) {
+		pr_warning("%s, bad argument\n", __func__);
+		return -EINVAL;
+	}
+
+	pcd = ep->pcd;
+	if (!pcd->driver || pcd->gadget.speed == USB_SPEED_UNKNOWN) {
+		pr_warning("%s, bogus device state\n", __func__);
+		return -ESHUTDOWN;
+	}
+
+	spin_lock_irqsave(&pcd->lock, flags);
+
+	/* make sure it's actually queued on this endpoint */
+	list_for_each_entry(req, &ep->queue, queue)
+	    if (&req->req == _req)
+		break;
+
+	if (&req->req != _req) {
+		spin_unlock_irqrestore(&pcd->lock, flags);
+		return -EINVAL;
+	}
+
+	if (!list_empty(&req->queue))
+		request_done(ep, req, -ECONNRESET);
+	else
+		req = NULL;
+
+	spin_unlock_irqrestore(&pcd->lock, flags);
+
+	return req ? 0 : -EOPNOTSUPP;
+}
+
+/**
+ * Set the EP STALL.
+ */
+void dwc_otg_ep_set_stall(struct core_if *core_if, struct dwc_ep *ep)
+{
+	u32 depctl = 0;
+	u32 depctl_addr;
+
+	if (ep->is_in) {
+		depctl_addr =
+		    ((u32) core_if->dev_if->in_ep_regs[ep->num]) + DWC_DIEPCTL;
+		depctl = dwc_read32(depctl_addr);
+
+		/* set the disable and stall bits */
+		if (DWC_DEPCTL_EPENA_RD(depctl))
+			depctl = DWC_DEPCTL_EPDIS_RW(depctl, 1);
+		depctl = DWC_DEPCTL_STALL_HNDSHK_RW(depctl, 1);
+		dwc_write32(depctl_addr, depctl);
+	} else {
+		depctl_addr =
+		    ((u32) core_if->dev_if->out_ep_regs[ep->num] + DWC_DOEPCTL);
+		depctl = dwc_read32(depctl_addr);
+
+		/* set the stall bit */
+		depctl = DWC_DEPCTL_STALL_HNDSHK_RW(depctl, 1);
+		dwc_write32(depctl_addr, depctl);
+	}
+}
+
+/**
+ * Clear the EP STALL.
+ */
+void dwc_otg_ep_clear_stall(struct core_if *core_if, struct dwc_ep *ep)
+{
+	u32 depctl = 0;
+	u32 depctl_addr;
+
+	if (ep->is_in == 1)
+		depctl_addr =
+		    ((u32) core_if->dev_if->in_ep_regs[ep->num]) + DWC_DIEPCTL;
+	else
+		depctl_addr =
+		    ((u32) core_if->dev_if->out_ep_regs[ep->num]) + DWC_DOEPCTL;
+
+	depctl = dwc_read32(depctl_addr);
+
+	/* clear the stall bits */
+	depctl = DWC_DEPCTL_STALL_HNDSHK_RW(depctl, 0);
+
+	/*
+	 * USB Spec 9.4.5: For endpoints using data toggle, regardless
+	 * of whether an endpoint has the Halt feature set, a
+	 * ClearFeature(ENDPOINT_HALT) request always results in the
+	 * data toggle being reinitialized to DATA0.
+	 */
+	if (ep->type == DWC_OTG_EP_TYPE_INTR ||
+	    ep->type == DWC_OTG_EP_TYPE_BULK)
+		depctl = DWC_DEPCTL_SET_DATA0_PID_RW(depctl, 1);
+
+	dwc_write32(depctl_addr, depctl);
+}
+
+/**
+ * usb_ep_set_halt stalls an endpoint.
+ *
+ * usb_ep_clear_halt clears an endpoint halt and resets its data
+ * toggle.
+ *
+ * Both of these functions are implemented with the same underlying
+ * function. The behavior depends on the val argument:
+ *	- 0 means clear_halt.
+ *	- 1 means set_halt,
+ *	- 2 means clear stall lock flag.
+ *	- 3 means set  stall lock flag.
+ */
+static int dwc_otg_pcd_ep_set_halt(struct usb_ep *_ep, int val)
+{
+	int retval = 0;
+	unsigned long flags;
+	struct pcd_ep *ep;
+
+	ep = container_of(_ep, struct pcd_ep, ep);
+	if (!_ep || (!ep->desc && ep != &ep->pcd->ep0) ||
+	    ep->desc->bmAttributes == USB_ENDPOINT_XFER_ISOC) {
+		pr_warning("%s, bad ep\n", __func__);
+		return -EINVAL;
+	}
+
+	spin_lock_irqsave(&ep->pcd->lock, flags);
+
+	if (ep->dwc_ep.is_in && !list_empty(&ep->queue)) {
+		pr_warning("%s() %s XFer In process\n", __func__, _ep->name);
+		retval = -EAGAIN;
+	} else if (val == 0) {
+		dwc_otg_ep_clear_stall(ep->pcd->otg_dev->core_if, &ep->dwc_ep);
+	} else if (val == 1) {
+		if (ep->dwc_ep.num == 0)
+			ep->pcd->ep0state = EP0_STALL;
+
+		ep->stopped = 1;
+		dwc_otg_ep_set_stall(ep->pcd->otg_dev->core_if, &ep->dwc_ep);
+	} else if (val == 2) {
+		ep->dwc_ep.stall_clear_flag = 0;
+	} else if (val == 3) {
+		ep->dwc_ep.stall_clear_flag = 1;
+	}
+
+	spin_unlock_irqrestore(&ep->pcd->lock, flags);
+	return retval;
+}
+
+static struct usb_ep_ops dwc_otg_pcd_ep_ops = {
+	.enable = dwc_otg_pcd_ep_enable,
+	.disable = dwc_otg_pcd_ep_disable,
+	.alloc_request = dwc_otg_pcd_alloc_request,
+	.free_request = dwc_otg_pcd_free_request,
+	.queue = dwc_otg_pcd_ep_queue,
+	.dequeue = dwc_otg_pcd_ep_dequeue,
+	.set_halt = dwc_otg_pcd_ep_set_halt,
+	.fifo_status = NULL,
+	.fifo_flush = NULL,
+};
+
+/**
+ * Gets the current USB frame number from the DTS register. This is the frame
+ * number from the last SOF packet.
+ */
+static u32 dwc_otg_get_frame_number(struct core_if *core_if)
+{
+	u32 dsts;
+
+	dsts = dwc_read32((u32) core_if->dev_if->dev_global_regs + DWC_DSTS);
+	return DWC_DSTS_SOFFN_RD(dsts);
+}
+
+/**
+ * The following gadget operations will be implemented in the DWC_otg
+ * PCD. Functions in the API that are not described below are not
+ * implemented.
+ *
+ * The Gadget API provides wrapper functions for each of the function
+ * pointers defined in usb_gadget_ops. The Gadget Driver calls the
+ * wrapper function, which then calls the underlying PCD function. The
+ * following sections are named according to the wrapper functions
+ * (except for ioctl, which doesn't have a wrapper function). Within
+ * each section, the corresponding DWC_otg PCD function name is
+ * specified.
+ *
+ */
+
+/**
+ *Gets the USB Frame number of the last SOF.
+ */
+static int dwc_otg_pcd_get_frame(struct usb_gadget *_gadget)
+{
+	if (!_gadget) {
+		return -ENODEV;
+	} else {
+		struct dwc_pcd *pcd;
+
+		pcd = container_of(_gadget, struct dwc_pcd, gadget);
+		dwc_otg_get_frame_number(GET_CORE_IF(pcd));
+	}
+
+	return 0;
+}
+
+/**
+ * This function is called when the SRP timer expires.  The SRP should complete
+ * within 6 seconds.
+ */
+static void srp_timeout(unsigned long data)
+{
+	u32 gotgctl;
+	struct dwc_pcd *pcd = (struct dwc_pcd *)data;
+	struct core_if *core_if = pcd->otg_dev->core_if;
+	u32 addr = otg_ctl_reg(pcd);
+
+	gotgctl = dwc_read32(addr);
+	core_if->srp_timer_started = 0;
+
+	if (core_if->core_params->phy_type == DWC_PHY_TYPE_PARAM_FS &&
+	    core_if->core_params->i2c_enable) {
+		pr_info("SRP Timeout\n");
+
+		if (core_if->srp_success && (gotgctl &
+					DWC_GCTL_BSESSION_VALID)) {
+			if (core_if->pcd_cb && core_if->pcd_cb->resume_wakeup)
+				core_if->pcd_cb->resume_wakeup(core_if->pcd_cb->
+							       p);
+
+			/* Clear Session Request */
+			gotgctl = 0;
+			gotgctl |= DWC_GCTL_SES_REQ;
+			dwc_modify32(addr, gotgctl, 0);
+
+			core_if->srp_success = 0;
+		} else {
+			pr_err("Device not connected/responding\n");
+			gotgctl &= ~DWC_GCTL_SES_REQ;
+			dwc_write32(addr, gotgctl);
+		}
+	} else if (gotgctl & DWC_GCTL_SES_REQ) {
+		pr_info("SRP Timeout\n");
+		pr_err("Device not connected/responding\n");
+
+		gotgctl &= ~DWC_GCTL_SES_REQ;
+		dwc_write32(addr, gotgctl);
+	} else {
+		pr_info(" SRP GOTGCTL=%0x\n", gotgctl);
+	}
+}
+
+/**
+ * Start the SRP timer to detect when the SRP does not complete within
+ * 6 seconds.
+ */
+static void dwc_otg_pcd_start_srp_timer(struct dwc_pcd *pcd)
+{
+	struct timer_list *srp_timer = &pcd->srp_timer;
+
+	GET_CORE_IF(pcd)->srp_timer_started = 1;
+	init_timer(srp_timer);
+	srp_timer->function = srp_timeout;
+	srp_timer->data = (unsigned long)pcd;
+	srp_timer->expires = jiffies + (HZ * 6);
+
+	add_timer(srp_timer);
+}
+
+static void dwc_otg_pcd_initiate_srp(struct dwc_pcd *pcd)
+{
+	u32 mem;
+	u32 val;
+	u32 addr = otg_ctl_reg(pcd);
+
+	val = dwc_read32(addr);
+	if (val & DWC_GCTL_SES_REQ) {
+		pr_err("Session Request Already active!\n");
+		return;
+	}
+
+	pr_notice("Session Request Initated\n");
+	mem = dwc_read32(addr);
+	mem |= DWC_GCTL_SES_REQ;
+	dwc_write32(addr, mem);
+
+	/* Start the SRP timer */
+	dwc_otg_pcd_start_srp_timer(pcd);
+	return;
+}
+
+static void dwc_otg_pcd_remote_wakeup(struct dwc_pcd *pcd, int set)
+{
+	u32 dctl = 0;
+	u32 addr = dev_ctl_reg(pcd);
+
+	if (dwc_otg_is_device_mode(GET_CORE_IF(pcd))) {
+		if (pcd->remote_wakeup_enable) {
+			if (set) {
+				dctl = DEC_DCTL_REMOTE_WAKEUP_SIG(dctl, 1);
+				dwc_modify32(addr, 0, dctl);
+				msleep(20);
+				dwc_modify32(addr, dctl, 0);
+			}
+		}
+	}
+}
+
+/**
+ * Initiates Session Request Protocol (SRP) to wakeup the host if no
+ * session is in progress. If a session is already in progress, but
+ * the device is suspended, remote wakeup signaling is started.
+ *
+ */
+static int dwc_otg_pcd_wakeup(struct usb_gadget *_gadget)
+{
+	unsigned long flags;
+	struct dwc_pcd *pcd;
+	u32 dsts;
+	u32 gotgctl;
+
+	if (!_gadget)
+		return -ENODEV;
+	else
+		pcd = container_of(_gadget, struct dwc_pcd, gadget);
+
+	spin_lock_irqsave(&pcd->lock, flags);
+
+	/*
+	 * This function starts the Protocol if no session is in progress. If
+	 * a session is already in progress, but the device is suspended,
+	 * remote wakeup signaling is started.
+	 */
+
+	/* Check if valid session */
+	gotgctl = dwc_read32(otg_ctl_reg(pcd));
+	if (gotgctl & DWC_GCTL_BSESSION_VALID) {
+		/* Check if suspend state */
+		dsts = dwc_read32(dev_sts_reg(pcd));
+		if (DWC_DSTS_SUSP_STS_RD(dsts))
+			dwc_otg_pcd_remote_wakeup(pcd, 1);
+	} else {
+		dwc_otg_pcd_initiate_srp(pcd);
+	}
+
+	spin_unlock_irqrestore(&pcd->lock, flags);
+	return 0;
+}
+
+static const struct usb_gadget_ops dwc_otg_pcd_ops = {
+	.get_frame = dwc_otg_pcd_get_frame,
+	.wakeup = dwc_otg_pcd_wakeup,
+	/* not selfpowered */
+};
+
+/**
+ * This function updates the otg values in the gadget structure.
+ */
+void dwc_otg_pcd_update_otg(struct dwc_pcd *pcd, const unsigned reset)
+{
+	if (!pcd->gadget.is_otg)
+		return;
+
+	if (reset) {
+		pcd->b_hnp_enable = 0;
+		pcd->a_hnp_support = 0;
+		pcd->a_alt_hnp_support = 0;
+	}
+
+	pcd->gadget.b_hnp_enable = pcd->b_hnp_enable;
+	pcd->gadget.a_hnp_support = pcd->a_hnp_support;
+	pcd->gadget.a_alt_hnp_support = pcd->a_alt_hnp_support;
+}
+
+/**
+ * This function is the top level PCD interrupt handler.
+ */
+static irqreturn_t dwc_otg_pcd_irq(int _irq, void *dev)
+{
+	struct dwc_pcd *pcd = dev;
+	int retval;
+
+	retval = dwc_otg_pcd_handle_intr(pcd);
+	return IRQ_RETVAL(retval);
+}
+
+/**
+ * PCD Callback function for initializing the PCD when switching to
+ * device mode.
+ */
+static int dwc_otg_pcd_start_cb(void *_p)
+{
+	struct dwc_pcd *pcd = (struct dwc_pcd *)_p;
+
+	/* Initialize the Core for Device mode. */
+	if (dwc_otg_is_device_mode(GET_CORE_IF(pcd)))
+		dwc_otg_core_dev_init(GET_CORE_IF(pcd));
+
+	return 1;
+}
+
+/**
+ * PCD Callback function for stopping the PCD when switching to Host
+ * mode.
+ */
+static int dwc_otg_pcd_stop_cb(void *_p)
+{
+	dwc_otg_pcd_stop((struct dwc_pcd *)_p);
+	return 1;
+}
+
+/**
+ * PCD Callback function for notifying the PCD when resuming from
+ * suspend.
+ *
+ * @param _p void pointer to the <code>struct dwc_pcd</code>
+ */
+static int dwc_otg_pcd_suspend_cb(void *_p)
+{
+	struct dwc_pcd *pcd = (struct dwc_pcd *)_p;
+
+	if (pcd->driver && pcd->driver->suspend) {
+		spin_unlock(&pcd->lock);
+		pcd->driver->suspend(&pcd->gadget);
+		spin_lock(&pcd->lock);
+	}
+	return 1;
+}
+
+/**
+ * PCD Callback function for notifying the PCD when resuming from
+ * suspend.
+ */
+static int dwc_otg_pcd_resume_cb(void *_p)
+{
+	struct dwc_pcd *pcd = (struct dwc_pcd *)_p;
+	struct core_if *core_if = pcd->otg_dev->core_if;
+
+	if (pcd->driver && pcd->driver->resume) {
+		spin_unlock(&pcd->lock);
+		pcd->driver->resume(&pcd->gadget);
+		spin_lock(&pcd->lock);
+	}
+
+	/* Maybe stop the SRP timeout timer. */
+	if (need_stop_srp_timer(core_if)) {
+		core_if->srp_timer_started = 0;
+		del_timer_sync(&pcd->srp_timer);
+	}
+	return 1;
+}
+
+/**
+ * PCD Callback structure for handling mode switching.
+ */
+static struct cil_callbacks pcd_callbacks = {
+	.start = dwc_otg_pcd_start_cb,
+	.stop = dwc_otg_pcd_stop_cb,
+	.suspend = dwc_otg_pcd_suspend_cb,
+	.resume_wakeup = dwc_otg_pcd_resume_cb,
+	.p = NULL,			/* Set at registration */
+};
+
+/**
+ * Tasklet
+ *
+ */
+static void start_xfer_tasklet_func(unsigned long data)
+{
+	struct dwc_pcd *pcd = (struct dwc_pcd *)data;
+	u32 diepctl = 0;
+	int num = pcd->otg_dev->core_if->dev_if->num_in_eps;
+	u32 i;
+	unsigned long flags;
+
+	spin_lock_irqsave(&pcd->lock, flags);
+	diepctl = dwc_read32(in_ep_ctl_reg(pcd, 0));
+
+	if (pcd->ep0.queue_sof) {
+		pcd->ep0.queue_sof = 0;
+		start_next_request(&pcd->ep0);
+	}
+
+	for (i = 0; i < num; i++) {
+		u32 diepctl = 0;
+
+		diepctl = dwc_read32(in_ep_ctl_reg(pcd, i));
+		if (pcd->in_ep[i].queue_sof) {
+			pcd->in_ep[i].queue_sof = 0;
+			start_next_request(&pcd->in_ep[i]);
+		}
+	}
+	spin_unlock_irqrestore(&pcd->lock, flags);
+}
+
+static struct tasklet_struct start_xfer_tasklet = {
+	.next = NULL,
+	.state = 0,
+	.count = ATOMIC_INIT(0),
+	.func = start_xfer_tasklet_func,
+	.data = 0,
+};
+
+/**
+ * This function initialized the pcd Dp structures to there default
+ * state.
+ */
+static void __devinit dwc_otg_pcd_reinit(struct dwc_pcd *pcd)
+{
+	static const char *names[] = {
+		"ep0", "ep1in", "ep2in", "ep3in", "ep4in", "ep5in",
+		"ep6in", "ep7in", "ep8in", "ep9in", "ep10in", "ep11in",
+		"ep12in", "ep13in", "ep14in", "ep15in", "ep1out", "ep2out",
+		"ep3out", "ep4out", "ep5out", "ep6out", "ep7out", "ep8out",
+		"ep9out", "ep10out", "ep11out", "ep12out", "ep13out",
+		"ep14out", "ep15out"
+	};
+	u32 i;
+	int in_ep_cntr, out_ep_cntr;
+	u32 hwcfg1;
+	u32 num_in_eps = (GET_CORE_IF(pcd))->dev_if->num_in_eps;
+	u32 num_out_eps = (GET_CORE_IF(pcd))->dev_if->num_out_eps;
+	struct pcd_ep *ep;
+
+	INIT_LIST_HEAD(&pcd->gadget.ep_list);
+	pcd->gadget.ep0 = &pcd->ep0.ep;
+	pcd->gadget.speed = USB_SPEED_UNKNOWN;
+	INIT_LIST_HEAD(&pcd->gadget.ep0->ep_list);
+
+	/* Initialize the EP0 structure. */
+	ep = &pcd->ep0;
+
+	/* Init EP structure */
+	ep->desc = NULL;
+	ep->pcd = pcd;
+	ep->stopped = 1;
+
+	/* Init DWC ep structure */
+	ep->dwc_ep.num = 0;
+	ep->dwc_ep.active = 0;
+	ep->dwc_ep.tx_fifo_num = 0;
+
+	/* Control until ep is actvated */
+	ep->dwc_ep.type = DWC_OTG_EP_TYPE_CONTROL;
+	ep->dwc_ep.maxpacket = MAX_PACKET_SIZE;
+	ep->dwc_ep.dma_addr = 0;
+	ep->dwc_ep.start_xfer_buff = NULL;
+	ep->dwc_ep.xfer_buff = NULL;
+	ep->dwc_ep.xfer_len = 0;
+	ep->dwc_ep.xfer_count = 0;
+	ep->dwc_ep.sent_zlp = 0;
+	ep->dwc_ep.total_len = 0;
+	ep->queue_sof = 0;
+
+	/* Init the usb_ep structure. */
+	ep->ep.name = names[0];
+	ep->ep.ops = &dwc_otg_pcd_ep_ops;
+
+	ep->ep.maxpacket = MAX_PACKET_SIZE;
+	list_add_tail(&ep->ep.ep_list, &pcd->gadget.ep_list);
+	INIT_LIST_HEAD(&ep->queue);
+
+	/* Initialize the EP structures. */
+	in_ep_cntr = 0;
+	hwcfg1 = (GET_CORE_IF(pcd))->hwcfg1 >> 3;
+
+	for (i = 1; in_ep_cntr < num_in_eps; i++) {
+		if (!(hwcfg1 & 0x1)) {
+			struct pcd_ep *ep = &pcd->in_ep[in_ep_cntr];
+
+			in_ep_cntr++;
+			/* Init EP structure */
+			ep->desc = NULL;
+			ep->pcd = pcd;
+			ep->stopped = 1;
+
+			/* Init DWC ep structure */
+			ep->dwc_ep.is_in = 1;
+			ep->dwc_ep.num = i;
+			ep->dwc_ep.active = 0;
+			ep->dwc_ep.tx_fifo_num = 0;
+
+			/* Control until ep is actvated */
+			ep->dwc_ep.type = DWC_OTG_EP_TYPE_CONTROL;
+			ep->dwc_ep.maxpacket = MAX_PACKET_SIZE;
+			ep->dwc_ep.dma_addr = 0;
+			ep->dwc_ep.start_xfer_buff = NULL;
+			ep->dwc_ep.xfer_buff = NULL;
+			ep->dwc_ep.xfer_len = 0;
+			ep->dwc_ep.xfer_count = 0;
+			ep->dwc_ep.sent_zlp = 0;
+			ep->dwc_ep.total_len = 0;
+			ep->queue_sof = 0;
+
+			ep->ep.name = names[i];
+			ep->ep.ops = &dwc_otg_pcd_ep_ops;
+
+			ep->ep.maxpacket = MAX_PACKET_SIZE;
+			list_add_tail(&ep->ep.ep_list, &pcd->gadget.ep_list);
+			INIT_LIST_HEAD(&ep->queue);
+		}
+		hwcfg1 >>= 2;
+	}
+
+	out_ep_cntr = 0;
+	hwcfg1 = (GET_CORE_IF(pcd))->hwcfg1 >> 2;
+	for (i = 1; out_ep_cntr < num_out_eps; i++) {
+		if (!(hwcfg1 & 0x1)) {
+			struct pcd_ep *ep = &pcd->out_ep[out_ep_cntr];
+
+			out_ep_cntr++;
+			/* Init EP structure */
+			ep->desc = NULL;
+			ep->pcd = pcd;
+			ep->stopped = 1;
+
+			/* Init DWC ep structure */
+			ep->dwc_ep.is_in = 0;
+			ep->dwc_ep.num = i;
+			ep->dwc_ep.active = 0;
+			ep->dwc_ep.tx_fifo_num = 0;
+
+			/* Control until ep is actvated */
+			ep->dwc_ep.type = DWC_OTG_EP_TYPE_CONTROL;
+			ep->dwc_ep.maxpacket = MAX_PACKET_SIZE;
+			ep->dwc_ep.dma_addr = 0;
+			ep->dwc_ep.start_xfer_buff = NULL;
+			ep->dwc_ep.xfer_buff = NULL;
+			ep->dwc_ep.xfer_len = 0;
+			ep->dwc_ep.xfer_count = 0;
+			ep->dwc_ep.sent_zlp = 0;
+			ep->dwc_ep.total_len = 0;
+			ep->queue_sof = 0;
+
+			ep->ep.name = names[15 + i];
+			ep->ep.ops = &dwc_otg_pcd_ep_ops;
+
+			ep->ep.maxpacket = MAX_PACKET_SIZE;
+			list_add_tail(&ep->ep.ep_list, &pcd->gadget.ep_list);
+			INIT_LIST_HEAD(&ep->queue);
+		}
+		hwcfg1 >>= 2;
+	}
+
+	/* remove ep0 from the list.  There is a ep0 pointer. */
+	list_del_init(&pcd->ep0.ep.ep_list);
+
+	pcd->ep0state = EP0_DISCONNECT;
+	pcd->ep0.ep.maxpacket = MAX_EP0_SIZE;
+	pcd->ep0.dwc_ep.maxpacket = MAX_EP0_SIZE;
+	pcd->ep0.dwc_ep.type = DWC_OTG_EP_TYPE_CONTROL;
+}
+
+/**
+ * This function releases the Gadget device.
+ * required by device_unregister().
+ */
+static void dwc_otg_pcd_gadget_release(struct device *dev)
+{
+	pr_info("%s(%p)\n", __func__, dev);
+}
+
+/**
+ * Allocates the buffers for the setup packets when the PCD portion of the
+ * driver is first initialized.
+ */
+static int __devinit init_pkt_buffs(struct device *dev, struct dwc_pcd *pcd)
+{
+	if (pcd->otg_dev->core_if->dma_enable) {
+		pcd->dwc_pool = dma_pool_create("dwc_otg_pcd", dev,
+						sizeof(*pcd->setup_pkt) * 5, 32,
+						0);
+		if (!pcd->dwc_pool)
+			return -ENOMEM;
+		pcd->setup_pkt = dma_pool_alloc(pcd->dwc_pool, GFP_KERNEL,
+						&pcd->setup_pkt_dma_handle);
+		if (!pcd->setup_pkt)
+			goto error;
+		pcd->status_buf = dma_pool_alloc(pcd->dwc_pool, GFP_KERNEL,
+						 &pcd->status_buf_dma_handle);
+		if (!pcd->status_buf)
+			goto error1;
+	} else {
+		pcd->setup_pkt = kmalloc(sizeof(*pcd->setup_pkt) * 5,
+					 GFP_KERNEL);
+		if (!pcd->setup_pkt)
+			return -ENOMEM;
+		pcd->status_buf = kmalloc(sizeof(u16), GFP_KERNEL);
+		if (!pcd->status_buf) {
+			kfree(pcd->setup_pkt);
+			return -ENOMEM;
+		}
+	}
+	return 0;
+
+error1:
+	dma_pool_free(pcd->dwc_pool, pcd->setup_pkt, pcd->setup_pkt_dma_handle);
+error:
+	dma_pool_destroy(pcd->dwc_pool);
+	return -ENOMEM;
+}
+
+/**
+ * This function initializes the PCD portion of the driver.
+ */
+int __devinit dwc_otg_pcd_init(struct device *dev)
+{
+	static const char pcd_name[] = "dwc_otg_pcd";
+	struct dwc_pcd *pcd;
+	struct dwc_otg_device *otg_dev = dev_get_drvdata(dev);
+	struct core_if *core_if = otg_dev->core_if;
+	int retval;
+
+	/* Allocate PCD structure */
+	pcd = kzalloc(sizeof(*pcd), GFP_KERNEL);
+	if (!pcd) {
+		retval = -ENOMEM;
+		goto err;
+	}
+
+	spin_lock_init(&pcd->lock);
+
+	otg_dev->pcd = pcd;
+	s_pcd = pcd;
+	pcd->gadget.name = pcd_name;
+
+	dev_set_name(&pcd->gadget.dev, "gadget");
+	pcd->otg_dev = otg_dev;
+	pcd->gadget.dev.parent = dev;
+	pcd->gadget.dev.release = dwc_otg_pcd_gadget_release;
+	pcd->gadget.ops = &dwc_otg_pcd_ops;
+
+	if (DWC_HWCFG4_DED_FIFO_ENA_RD(core_if->hwcfg4))
+		pr_info("Dedicated Tx FIFOs mode\n");
+	else
+		pr_info("Shared Tx FIFO mode\n");
+
+	pcd->gadget.is_dualspeed = check_is_dual_speed(core_if);
+	pcd->gadget.is_otg = check_is_otg(core_if);
+
+	/* Register the gadget device */
+	retval = device_register(&pcd->gadget.dev);
+
+	/* Initialized the Core for Device mode. */
+	if (dwc_otg_is_device_mode(core_if))
+		dwc_otg_core_dev_init(core_if);
+
+	/*  Initialize EP structures */
+	dwc_otg_pcd_reinit(pcd);
+
+	/* Register the PCD Callbacks. */
+	dwc_otg_cil_register_pcd_callbacks(core_if, &pcd_callbacks, pcd);
+
+	/* Setup interupt handler */
+	retval = request_irq(otg_dev->irq, dwc_otg_pcd_irq, IRQF_SHARED,
+			     pcd->gadget.name, pcd);
+	if (retval) {
+		pr_err("request of irq%d failed\n", otg_dev->irq);
+		retval = -EBUSY;
+		goto err_cleanup;
+	}
+
+	/* Initialize the DMA buffer for SETUP packets */
+	retval = init_pkt_buffs(dev, pcd);
+	if (retval)
+		goto err_cleanup;
+
+	/* Initialize tasklet */
+	start_xfer_tasklet.data = (unsigned long)pcd;
+	pcd->start_xfer_tasklet = &start_xfer_tasklet;
+	return 0;
+
+err_cleanup:
+	kfree(pcd);
+	otg_dev->pcd = NULL;
+	s_pcd = NULL;
+
+err:
+	return retval;
+}
+
+/**
+ * Cleanup the PCD.
+ */
+void __devexit dwc_otg_pcd_remove(struct device *dev)
+{
+	struct dwc_otg_device *otg_dev = dev_get_drvdata(dev);
+	struct dwc_pcd *pcd = otg_dev->pcd;
+
+	/* Free the IRQ */
+	free_irq(otg_dev->irq, pcd);
+
+	/* start with the driver above us */
+	if (pcd->driver) {
+		/* should have been done already by driver model core */
+		pr_warning("driver '%s' is still registered\n",
+			   pcd->driver->driver.name);
+		usb_gadget_unregister_driver(pcd->driver);
+	}
+	if (pcd->start_xfer_tasklet)
+		tasklet_kill(pcd->start_xfer_tasklet);
+	tasklet_kill(&pcd->test_mode_tasklet);
+
+	device_unregister(&pcd->gadget.dev);
+	if (GET_CORE_IF(pcd)->dma_enable) {
+		dma_pool_free(pcd->dwc_pool, pcd->setup_pkt,
+			      pcd->setup_pkt_dma_handle);
+		dma_pool_free(pcd->dwc_pool, pcd->status_buf,
+			      pcd->status_buf_dma_handle);
+		dma_pool_destroy(pcd->dwc_pool);
+	} else {
+		kfree(pcd->setup_pkt);
+		kfree(pcd->status_buf);
+	}
+	kfree(pcd);
+	otg_dev->pcd = NULL;
+}
+
+/**
+ * This function registers a gadget driver with the PCD.
+ *
+ * When a driver is successfully registered, it will receive control
+ * requests including set_configuration(), which enables non-control
+ * requests.  then usb traffic follows until a disconnect is reported.
+ * then a host may connect again, or the driver might get unbound.
+ */
+int usb_gadget_probe_driver(struct usb_gadget_driver *driver,
+			    int (*bind) (struct usb_gadget *))
+{
+	int retval;
+
+	if (!driver || driver->speed == USB_SPEED_UNKNOWN || !bind ||
+	    !driver->unbind || !driver->disconnect || !driver->setup)
+		return -EINVAL;
+
+	if (s_pcd == NULL)
+		return -ENODEV;
+
+	if (s_pcd->driver != NULL)
+		return -EBUSY;
+
+	/* hook up the driver */
+	s_pcd->driver = driver;
+	s_pcd->gadget.dev.driver = &driver->driver;
+
+	retval = bind(&s_pcd->gadget);
+	if (retval) {
+		struct core_if *core_if;
+
+		pr_err("bind to driver %s --> error %d\n",
+		       driver->driver.name, retval);
+		core_if = s_pcd->otg_dev->core_if;
+		otg_set_peripheral(core_if->xceiv, &s_pcd->gadget);
+		s_pcd->driver = NULL;
+		s_pcd->gadget.dev.driver = NULL;
+		return retval;
+	}
+	return 0;
+}
+EXPORT_SYMBOL(usb_gadget_probe_driver);
+
+/**
+ * This function unregisters a gadget driver
+ */
+int usb_gadget_unregister_driver(struct usb_gadget_driver *driver)
+{
+	struct core_if *core_if;
+
+	if (!s_pcd)
+		return -ENODEV;
+	if (!driver || driver != s_pcd->driver)
+		return -EINVAL;
+
+	core_if = s_pcd->otg_dev->core_if;
+	core_if->xceiv->state = OTG_STATE_UNDEFINED;
+	otg_set_peripheral(core_if->xceiv, NULL);
+
+	driver->unbind(&s_pcd->gadget);
+	s_pcd->driver = NULL;
+
+	return 0;
+}
+EXPORT_SYMBOL(usb_gadget_unregister_driver);
diff --git a/drivers/usb/dwc_otg/dwc_otg_pcd.h b/drivers/usb/dwc_otg/dwc_otg_pcd.h
new file mode 100644
index 0000000..8595d71
--- /dev/null
+++ b/drivers/usb/dwc_otg/dwc_otg_pcd.h
@@ -0,0 +1,139 @@
+/*
+ * 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.
+ *
+ */
+
+#if !defined(__DWC_PCD_H__)
+#define __DWC_PCD_H__
+
+#include "dwc_otg_driver.h"
+
+/*
+ * This file contains the structures, constants, and interfaces for
+ * the Perpherial Contoller Driver (PCD).
+ *
+ * The Peripheral Controller Driver (PCD) for Linux will implement the
+ * Gadget API, so that the existing Gadget drivers can be used.	 For
+ * the Mass Storage Function driver the File-backed USB Storage Gadget
+ * (FBS) driver will be used.  The FBS driver supports the
+ * Control-Bulk (CB), Control-Bulk-Interrupt (CBI), and Bulk-Only
+ * transports.
+ *
+ */
+
+/* Invalid DMA Address */
+#define DMA_ADDR_INVALID			(~(dma_addr_t) 0)
+/* Maxpacket size for EP0 */
+#define MAX_EP0_SIZE				64
+/* Maxpacket size for any EP */
+#define MAX_PACKET_SIZE				1024
+
+/*
+ * Get the pointer to the core_if from the pcd pointer.
+ */
+#define GET_CORE_IF(_pcd) (_pcd->otg_dev->core_if)
+
+/*
+ * DWC_otg request structure.
+ * This structure is a list of requests.
+ */
+struct pcd_request {
+	struct usb_request req;	/* USB Request. */
+	struct list_head queue;	/* queue of these requests. */
+	unsigned mapped:1;
+};
+
+static inline u32 in_ep_int_reg(struct dwc_pcd *pd, int i)
+{
+	return (u32) GET_CORE_IF(pd)->dev_if->in_ep_regs[i] + DWC_DIEPINT;
+}
+static inline u32 out_ep_int_reg(struct dwc_pcd *pd, int i)
+{
+	return (u32) GET_CORE_IF(pd)->dev_if->out_ep_regs[i] + DWC_DOEPINT;
+}
+static inline u32 in_ep_ctl_reg(struct dwc_pcd *pd, int i)
+{
+	return (u32) GET_CORE_IF(pd)->dev_if->in_ep_regs[i] + DWC_DIEPCTL;
+}
+
+static inline u32 out_ep_ctl_reg(struct dwc_pcd *pd, int i)
+{
+	return (u32) GET_CORE_IF(pd)->dev_if->out_ep_regs[i] + DWC_DOEPCTL;
+}
+
+static inline u32 dev_ctl_reg(struct dwc_pcd *pd)
+{
+	return (u32) ((u32) GET_CORE_IF(pd)->dev_if->dev_global_regs +
+		      DWC_DCTL);
+}
+
+static inline u32 dev_diepmsk_reg(struct dwc_pcd *pd)
+{
+	return (u32) ((u32) GET_CORE_IF(pd)->dev_if->dev_global_regs +
+		      DWC_DIEPMSK);
+}
+
+static inline u32 dev_sts_reg(struct dwc_pcd *pd)
+{
+	return (u32) ((u32) GET_CORE_IF(pd)->dev_if->dev_global_regs +
+		      DWC_DSTS);
+}
+
+static inline u32 otg_ctl_reg(struct dwc_pcd *pd)
+{
+	return (u32) ((u32) GET_CORE_IF(pd)->core_global_regs + DWC_GOTGCTL);
+}
+
+extern int __init dwc_otg_pcd_init(struct device *dev);
+
+/*
+ * The following functions support managing the DWC_otg controller in device
+ * mode.
+ */
+extern void dwc_otg_ep_activate(struct core_if *core_if, struct dwc_ep *ep);
+extern void dwc_otg_ep_start_transfer(struct core_if *_if, struct dwc_ep *ep);
+extern void dwc_otg_ep_set_stall(struct core_if *core_if, struct dwc_ep *ep);
+extern void dwc_otg_ep_clear_stall(struct core_if *core_if, struct dwc_ep *ep);
+extern void dwc_otg_pcd_remove(struct device *dev);
+extern int dwc_otg_pcd_handle_intr(struct dwc_pcd *pcd);
+extern void dwc_otg_pcd_stop(struct dwc_pcd *pcd);
+extern void request_nuke(struct pcd_ep *ep);
+extern void dwc_otg_pcd_update_otg(struct dwc_pcd *pcd, const unsigned reset);
+extern void dwc_otg_ep0_start_transfer(struct core_if *_if, struct dwc_ep *ep);
+
+extern void request_done(struct pcd_ep *ep, struct pcd_request *req,
+			 int _status);
+
+extern void start_next_request(struct pcd_ep *ep);
+#endif
-- 
1.6.1.rc3

^ permalink raw reply related

* [PATCH V7 03/10] USB/ppc4xx: Add Synopsys DWC OTG Core Interface Layer (CIL)
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>

Core Interface Layer Common provides common functions for both host
controller and peripheral controller.  CIL manages the memory map
for the core. It also handles basic tasks like reading/writing the
registers and data FIFOs in the controller. CIL performs basic
services that are not specific to either the host or device modes
of operation. These services include management of the OTG Host
Negotiation Protocol (HNP) and Session Request Protocol (SRP).

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_cil.c      |  972 +++++++++++++++++++++++++
 drivers/usb/dwc_otg/dwc_otg_cil.h      | 1219 ++++++++++++++++++++++++++++++++
 drivers/usb/dwc_otg/dwc_otg_cil_intr.c |  616 ++++++++++++++++
 3 files changed, 2807 insertions(+), 0 deletions(-)

diff --git a/drivers/usb/dwc_otg/dwc_otg_cil.c b/drivers/usb/dwc_otg/dwc_otg_cil.c
new file mode 100644
index 0000000..cf82768
--- /dev/null
+++ b/drivers/usb/dwc_otg/dwc_otg_cil.c
@@ -0,0 +1,972 @@
+/*
+ * 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 Core Interface Layer provides basic services for accessing and
+ * managing the DWC_otg hardware. These services are used by both the
+ * Host Controller Driver and the Peripheral Controller Driver.
+ *
+ * The CIL manages the memory map for the core so that the HCD and PCD
+ * don't have to do this separately. It also handles basic tasks like
+ * reading/writing the registers and data FIFOs in the controller.
+ * Some of the data access functions provide encapsulation of several
+ * operations required to perform a task, such as writing multiple
+ * registers to start a transfer. Finally, the CIL performs basic
+ * services that are not specific to either the host or device modes
+ * of operation. These services include management of the OTG Host
+ * Negotiation Protocol (HNP) and Session Request Protocol (SRP). A
+ * Diagnostic API is also provided to allow testing of the controller
+ * hardware.
+ *
+ * The Core Interface Layer has the following requirements:
+ * - Provides basic controller operations.
+ * - Minimal use of OS services.
+ * - The OS services used will be abstracted by using inline functions
+ *	 or macros.
+ */
+#include <linux/delay.h>
+
+#include "dwc_otg_cil.h"
+
+const char *op_state_str(enum usb_otg_state state)
+{
+	switch (state) {
+	case OTG_STATE_A_IDLE:
+		return "a_idle";
+	case OTG_STATE_A_WAIT_VRISE:
+		return "a_wait_vrise";
+	case OTG_STATE_A_WAIT_BCON:
+		return "a_wait_bcon";
+	case OTG_STATE_A_HOST:
+		return "a_host";
+	case OTG_STATE_A_SUSPEND:
+		return "a_suspend";
+	case OTG_STATE_A_PERIPHERAL:
+		return "a_peripheral";
+	case OTG_STATE_A_WAIT_VFALL:
+		return "a_wait_vfall";
+	case OTG_STATE_A_VBUS_ERR:
+		return "a_vbus_err";
+	case OTG_STATE_B_IDLE:
+		return "b_idle";
+	case OTG_STATE_B_SRP_INIT:
+		return "b_srp_init";
+	case OTG_STATE_B_PERIPHERAL:
+		return "b_peripheral";
+	case OTG_STATE_B_WAIT_ACON:
+		return "b_wait_acon";
+	case OTG_STATE_B_HOST:
+		return "b_host";
+	default:
+		return "UNDEFINED";
+	}
+}
+
+/**
+ * This function enables the controller's Global Interrupt in the AHB Config
+ * register.
+ */
+void dwc_otg_enable_global_interrupts(struct core_if *core_if)
+{
+	u32 ahbcfg = 0;
+
+	ahbcfg |= DWC_AHBCFG_GLBL_INT_MASK;
+	dwc_modify32((u32) (core_if->core_global_regs) + DWC_GAHBCFG, 0,
+		     ahbcfg);
+}
+
+/**
+ * This function disables the controller's Global Interrupt in the AHB Config
+ * register.
+ */
+void dwc_otg_disable_global_interrupts(struct core_if *core_if)
+{
+	u32 ahbcfg = 0;
+
+	ahbcfg |= DWC_AHBCFG_GLBL_INT_MASK;
+	dwc_modify32((u32) (core_if->core_global_regs) + DWC_GAHBCFG,
+		     ahbcfg, 0);
+}
+
+/**
+ * Tests if the current hardware is using a full speed phy.
+ */
+static inline int full_speed_phy(struct core_if *core_if)
+{
+	if ((DWC_HWCFG2_FS_PHY_TYPE_RD(core_if->hwcfg2) == 2 &&
+	     DWC_HWCFG2_FS_PHY_TYPE_RD(core_if->hwcfg2) == 1 &&
+	     core_if->core_params->ulpi_fs_ls) ||
+	    core_if->core_params->phy_type == DWC_PHY_TYPE_PARAM_FS)
+		return 1;
+	return 0;
+}
+
+/**
+ * Initializes the FSLSPClkSel field of the HCFG register depending on the PHY
+ * type.
+ */
+void init_fslspclksel(struct core_if *core_if)
+{
+	u32 val;
+	u32 hcfg = 0;
+
+	if (full_speed_phy(core_if))
+		val = DWC_HCFG_48_MHZ;
+	else
+		/* High speed PHY running at full speed or high speed */
+		val = DWC_HCFG_30_60_MHZ;
+
+	hcfg = dwc_read32((u32) core_if->host_if->host_global_regs + DWC_HCFG);
+	hcfg = DWC_HCFG_FSLSP_CLK_RW(hcfg, val);
+	dwc_write32((u32) core_if->host_if->host_global_regs + DWC_HCFG, hcfg);
+}
+
+/**
+ * Initializes the DevSpd field of the DCFG register depending on the PHY type
+ * and the enumeration speed of the device.
+ */
+static void init_devspd(struct core_if *core_if)
+{
+	u32 val;
+	u32 dcfg;
+
+	if (full_speed_phy(core_if))
+		val = 0x3;
+	else if (core_if->core_params->speed == DWC_SPEED_PARAM_FULL)
+		/* High speed PHY running at full speed */
+		val = 0x1;
+	else
+		/* High speed PHY running at high speed */
+		val = 0x0;
+
+	dcfg = dwc_read32((u32) core_if->dev_if->dev_global_regs + DWC_DCFG);
+	dcfg = DWC_DCFG_DEV_SPEED_WR(dcfg, val);
+	dwc_write32((u32) core_if->dev_if->dev_global_regs + DWC_DCFG, dcfg);
+}
+
+/**
+ * This function calculates the number of IN EPS using GHWCFG1 and GHWCFG2
+ * registers values
+ */
+static u32 calc_num_in_eps(struct core_if *core_if)
+{
+	u32 num_in_eps = 0;
+	u32 num_eps = DWC_HWCFG2_NO_DEV_EP_RD(core_if->hwcfg2);
+	u32 hwcfg1 = core_if->hwcfg1 >> 2;
+	u32 num_tx_fifos = DWC_HWCFG4_NUM_IN_EPS_RD(core_if->hwcfg4);
+	u32 i;
+
+	for (i = 0; i < num_eps; ++i) {
+		if (!(hwcfg1 & 0x1))
+			num_in_eps++;
+		hwcfg1 >>= 2;
+	}
+
+	if (DWC_HWCFG4_DED_FIFO_ENA_RD(core_if->hwcfg4))
+		num_in_eps = num_in_eps > num_tx_fifos ?
+		    num_tx_fifos : num_in_eps;
+
+	return num_in_eps;
+}
+
+/**
+ * This function calculates the number of OUT EPS using GHWCFG1 and GHWCFG2
+ * registers values
+ */
+static u32 calc_num_out_eps(struct core_if *core_if)
+{
+	u32 num_out_eps = 0;
+	u32 num_eps = DWC_HWCFG2_NO_DEV_EP_RD(core_if->hwcfg2);
+	u32 hwcfg1 = core_if->hwcfg1 >> 2;
+	u32 i;
+
+	for (i = 0; i < num_eps; ++i) {
+		if (!(hwcfg1 & 0x2))
+			num_out_eps++;
+		hwcfg1 >>= 2;
+	}
+	return num_out_eps;
+}
+
+/**
+ * Do core a soft reset of the core.  Be careful with this because it
+ * resets all the internal state machines of the core.
+ */
+static void dwc_otg_core_reset(struct core_if *core_if)
+{
+	u32 global_regs = (u32) core_if->core_global_regs;
+	u32 greset = 0;
+	int count = 0;
+
+	/* Wait for AHB master IDLE state. */
+	do {
+		udelay(10);
+		greset = dwc_read32(global_regs + DWC_GRSTCTL);
+		if (++count > 100000) {
+			pr_warning("%s() HANG! AHB Idle GRSTCTL=%0x\n",
+				   __func__, greset);
+			return;
+		}
+	} while (greset & DWC_RSTCTL_AHB_IDLE);
+
+	/* Core Soft Reset */
+	count = 0;
+	greset |= DWC_RSTCTL_SFT_RST;
+	dwc_write32(global_regs + DWC_GRSTCTL, greset);
+
+	do {
+		greset = dwc_read32(global_regs + DWC_GRSTCTL);
+		if (++count > 10000) {
+			pr_warning("%s() HANG! Soft Reset "
+				   "GRSTCTL=%0x\n", __func__, greset);
+			break;
+		}
+		udelay(1);
+	} while (greset & DWC_RSTCTL_SFT_RST);
+
+	/* Wait for 3 PHY Clocks */
+	msleep(100);
+}
+
+/**
+ * This function initializes the commmon interrupts, used in both
+ * device and host modes.
+ */
+void dwc_otg_enable_common_interrupts(struct core_if *core_if)
+{
+	u32 global_regs = (u32) core_if->core_global_regs;
+	u32 intr_mask = 0;
+
+	/* Clear any pending OTG Interrupts */
+	dwc_write32(global_regs + DWC_GOTGINT, 0xFFFFFFFF);
+
+	/* Clear any pending interrupts */
+	dwc_write32(global_regs + DWC_GINTSTS, 0xFFFFFFFF);
+
+	/* Enable the interrupts in the GINTMSK. */
+	intr_mask |= DWC_INTMSK_MODE_MISMTC;
+	intr_mask |= DWC_INTMSK_OTG;
+	intr_mask |= DWC_INTMSK_CON_ID_STS_CHG;
+	intr_mask |= DWC_INTMSK_WKP;
+	intr_mask |= DWC_INTMSK_SES_DISCON_DET;
+	intr_mask |= DWC_INTMSK_USB_SUSP;
+	intr_mask |= DWC_INTMSK_NEW_SES_DET;
+	if (!core_if->dma_enable)
+		intr_mask |= DWC_INTMSK_RXFIFO_NOT_EMPT;
+	dwc_write32(global_regs + DWC_GINTMSK, intr_mask);
+}
+
+/**
+ * This function initializes the DWC_otg controller registers and prepares the
+ * core for device mode or host mode operation.
+ */
+void dwc_otg_core_init(struct core_if *core_if)
+{
+	u32 i;
+	u32 global_reg = (u32) core_if->core_global_regs;
+	struct device_if *dev_if = core_if->dev_if;
+	u32 ahbcfg = 0;
+	u32 i2cctl = 0;
+	u32 gusbcfg;
+
+	/* Common Initialization */
+	gusbcfg = dwc_read32(global_reg + DWC_GUSBCFG);
+
+	/* Program the ULPI External VBUS bit if needed */
+	gusbcfg |= DWC_USBCFG_ULPI_EXT_VBUS_DRV;
+
+	/* Set external TS Dline pulsing */
+	if (core_if->core_params->ts_dline == 1)
+		gusbcfg |= DWC_USBCFG_TERM_SEL_DL_PULSE;
+	else
+		gusbcfg = gusbcfg & (~((u32) DWC_USBCFG_TERM_SEL_DL_PULSE));
+
+	dwc_write32(global_reg + DWC_GUSBCFG, gusbcfg);
+
+	/* Reset the Controller */
+	dwc_otg_core_reset(core_if);
+
+	/* Initialize parameters from Hardware configuration registers. */
+	dev_if->num_in_eps = calc_num_in_eps(core_if);
+	dev_if->num_out_eps = calc_num_out_eps(core_if);
+
+	for (i = 0; i < DWC_HWCFG4_NUM_DEV_PERIO_IN_EP_RD(core_if->hwcfg4);
+			i++) {
+		dev_if->perio_tx_fifo_size[i] =
+		    dwc_read32(global_reg + DWC_DPTX_FSIZ_DIPTXF(i)) >> 16;
+	}
+	for (i = 0; i < DWC_HWCFG4_NUM_IN_EPS_RD(core_if->hwcfg4); i++) {
+		dev_if->tx_fifo_size[i] =
+		    dwc_read32(global_reg + DWC_DPTX_FSIZ_DIPTXF(i)) >> 16;
+	}
+
+	core_if->total_fifo_size = DWC_HWCFG3_DFIFO_DEPTH_RD(core_if->hwcfg3);
+	core_if->rx_fifo_size = dwc_read32(global_reg + DWC_GRXFSIZ);
+	core_if->nperio_tx_fifo_size =
+	    dwc_read32(global_reg + DWC_GRXFSIZ) >> 16;
+	/*
+	 * This programming sequence needs to happen in FS mode before any
+	 * other programming occurs
+	 */
+	if (core_if->core_params->speed == DWC_SPEED_PARAM_FULL &&
+	    core_if->core_params->phy_type == DWC_PHY_TYPE_PARAM_FS) {
+		/*
+		 * core_init() is now called on every switch so only call the
+		 * following for the first time through.
+		 */
+		if (!core_if->phy_init_done) {
+			core_if->phy_init_done = 1;
+			gusbcfg = dwc_read32(global_reg + DWC_GUSBCFG);
+			gusbcfg |= DWC_USBCFG_ULPI_UTMI_SEL;
+			dwc_write32(global_reg + DWC_GUSBCFG, gusbcfg);
+
+			/* Reset after a PHY select */
+			dwc_otg_core_reset(core_if);
+		}
+
+		/*
+		 * Program DCFG.DevSpd or HCFG.FSLSPclkSel to 48Mhz in FS.
+		 * Also do this on HNP Dev/Host mode switches (done in dev_init
+		 * and host_init).
+		 */
+		if (dwc_otg_is_host_mode(core_if))
+			init_fslspclksel(core_if);
+		else
+			init_devspd(core_if);
+
+		if (core_if->core_params->i2c_enable) {
+			/* Program GUSBCFG.OtgUtmifsSel to I2C */
+			gusbcfg = dwc_read32(global_reg + DWC_GUSBCFG);
+			gusbcfg |= DWC_USBCFG_OTGUTMIFSSEL;
+			dwc_write32(global_reg + DWC_GUSBCFG, gusbcfg);
+
+			/* Program GI2CCTL.I2CEn */
+			i2cctl = dwc_read32(global_reg + DWC_GI2CCTL);
+			i2cctl |= DWC_I2CCTL_I2CDEVADDR(1);
+			i2cctl &= ~DWC_I2CCTL_I2CEN;
+			dwc_write32(global_reg + DWC_GI2CCTL, i2cctl);
+			i2cctl |= DWC_I2CCTL_I2CEN;
+			dwc_write32(global_reg + DWC_GI2CCTL, i2cctl);
+		}
+	} else if (!core_if->phy_init_done) {
+		/*
+		 * High speed PHY. These parameters are preserved during soft
+		 * reset so only program them the first time. Do a soft reset
+		 * immediately after setting phyif.
+		 */
+		gusbcfg = dwc_read32(global_reg + DWC_GUSBCFG);
+		core_if->phy_init_done = 1;
+		if (core_if->core_params->phy_type)
+			gusbcfg |= DWC_USBCFG_ULPI_UTMI_SEL;
+		else
+			gusbcfg &= ~((u32) DWC_USBCFG_ULPI_UTMI_SEL);
+
+		if (gusbcfg & DWC_USBCFG_ULPI_UTMI_SEL) {
+			/* ULPI interface */
+			gusbcfg |= DWC_USBCFG_PHYIF;
+			if (core_if->core_params->phy_ulpi_ddr)
+				gusbcfg |= DWC_USBCFG_DDRSEL;
+			else
+				gusbcfg &= ~((u32) DWC_USBCFG_DDRSEL);
+		} else {
+			/* UTMI+ interface */
+			if (core_if->core_params->phy_utmi_width == 16)
+				gusbcfg |= DWC_USBCFG_PHYIF;
+			else
+				gusbcfg &= ~((u32) DWC_USBCFG_PHYIF);
+		}
+		dwc_write32(global_reg + DWC_GUSBCFG, gusbcfg);
+
+		/* Reset after setting the PHY parameters */
+		dwc_otg_core_reset(core_if);
+	}
+
+	if (DWC_HWCFG2_HS_PHY_TYPE_RD(core_if->hwcfg2) == 2 &&
+	    DWC_HWCFG2_FS_PHY_TYPE_RD(core_if->hwcfg2) == 1 &&
+	    core_if->core_params->ulpi_fs_ls) {
+		gusbcfg = dwc_read32(global_reg + DWC_GUSBCFG);
+		gusbcfg |= DWC_USBCFG_ULPI_FSLS;
+		gusbcfg |= DWC_USBCFG_ULPI_CLK_SUS_M;
+		dwc_write32(global_reg + DWC_GUSBCFG, gusbcfg);
+	} else {
+		gusbcfg = dwc_read32(global_reg + DWC_GUSBCFG);
+		gusbcfg &= ~((u32) DWC_USBCFG_ULPI_FSLS);
+		gusbcfg &= ~((u32) DWC_USBCFG_ULPI_CLK_SUS_M);
+		dwc_write32(global_reg + DWC_GUSBCFG, gusbcfg);
+	}
+
+	/* Program the GAHBCFG Register. */
+	switch (DWC_HWCFG2_ARCH_RD(core_if->hwcfg2)) {
+	case DWC_SLAVE_ONLY_ARCH:
+		ahbcfg &= ~DWC_AHBCFG_NPFIFO_EMPTY;	/* HALF empty */
+		ahbcfg &= ~DWC_AHBCFG_FIFO_EMPTY;	/* HALF empty */
+		core_if->dma_enable = 0;
+		break;
+	case DWC_EXT_DMA_ARCH:
+		ahbcfg = (ahbcfg & ~DWC_AHBCFG_BURST_LEN(0xf)) |
+		    DWC_AHBCFG_BURST_LEN(core_if->core_params->dma_burst_size);
+		core_if->dma_enable = (core_if->core_params->dma_enable != 0);
+		break;
+	case DWC_INT_DMA_ARCH:
+		ahbcfg = (ahbcfg & ~DWC_AHBCFG_BURST_LEN(0xf)) |
+		    DWC_AHBCFG_BURST_LEN(DWC_GAHBCFG_INT_DMA_BURST_INCR);
+		core_if->dma_enable = (core_if->core_params->dma_enable != 0);
+		break;
+	}
+
+	if (core_if->dma_enable)
+		ahbcfg |= DWC_AHBCFG_DMA_ENA;
+	else
+		ahbcfg &= ~DWC_AHBCFG_DMA_ENA;
+	dwc_write32(global_reg + DWC_GAHBCFG, ahbcfg);
+	core_if->en_multiple_tx_fifo =
+	    DWC_HWCFG4_DED_FIFO_ENA_RD(core_if->hwcfg4);
+
+	/* Program the GUSBCFG register. */
+	gusbcfg = dwc_read32(global_reg + DWC_GUSBCFG);
+	switch (DWC_HWCFG2_OP_MODE_RD(core_if->hwcfg2)) {
+	case DWC_MODE_HNP_SRP_CAPABLE:
+		if (core_if->core_params->otg_cap ==
+		    DWC_OTG_CAP_PARAM_HNP_SRP_CAPABLE)
+			gusbcfg |= DWC_USBCFG_HNP_CAP;
+		else
+			gusbcfg &= ~((u32) DWC_USBCFG_HNP_CAP);
+		if (core_if->core_params->otg_cap !=
+		    DWC_OTG_CAP_PARAM_NO_HNP_SRP_CAPABLE)
+			gusbcfg |= DWC_USBCFG_SRP_CAP;
+		else
+			gusbcfg &= ~((u32) DWC_USBCFG_SRP_CAP);
+		break;
+	case DWC_MODE_SRP_ONLY_CAPABLE:
+		gusbcfg &= ~((u32) DWC_USBCFG_HNP_CAP);
+		if (core_if->core_params->otg_cap !=
+		    DWC_OTG_CAP_PARAM_NO_HNP_SRP_CAPABLE)
+			gusbcfg |= DWC_USBCFG_SRP_CAP;
+		else
+			gusbcfg &= ~((u32) DWC_USBCFG_SRP_CAP);
+		break;
+	case DWC_MODE_NO_HNP_SRP_CAPABLE:
+		gusbcfg &= ~((u32) DWC_USBCFG_HNP_CAP);
+		gusbcfg &= ~((u32) DWC_USBCFG_SRP_CAP);
+		break;
+	case DWC_MODE_SRP_CAPABLE_DEVICE:
+		gusbcfg &= ~((u32) DWC_USBCFG_HNP_CAP);
+		if (core_if->core_params->otg_cap !=
+		    DWC_OTG_CAP_PARAM_NO_HNP_SRP_CAPABLE)
+			gusbcfg |= DWC_USBCFG_SRP_CAP;
+		else
+			gusbcfg &= ~((u32) DWC_USBCFG_SRP_CAP);
+		break;
+	case DWC_MODE_NO_SRP_CAPABLE_DEVICE:
+		gusbcfg &= ~((u32) DWC_USBCFG_HNP_CAP);
+		gusbcfg &= ~((u32) DWC_USBCFG_SRP_CAP);
+		break;
+	case DWC_MODE_SRP_CAPABLE_HOST:
+		gusbcfg &= ~((u32) DWC_USBCFG_HNP_CAP);
+		if (core_if->core_params->otg_cap !=
+		    DWC_OTG_CAP_PARAM_NO_HNP_SRP_CAPABLE)
+			gusbcfg |= DWC_USBCFG_SRP_CAP;
+		else
+			gusbcfg &= ~((u32) DWC_USBCFG_SRP_CAP);
+		break;
+	case DWC_MODE_NO_SRP_CAPABLE_HOST:
+		gusbcfg &= ~((u32) DWC_USBCFG_HNP_CAP);
+		gusbcfg &= ~((u32) DWC_USBCFG_SRP_CAP);
+		break;
+	}
+	dwc_write32(global_reg + DWC_GUSBCFG, gusbcfg);
+
+	/* Enable common interrupts */
+	dwc_otg_enable_common_interrupts(core_if);
+
+	/*
+	 * Do device or host intialization based on mode during PCD
+	 * and HCD initialization
+	 */
+	if (dwc_otg_is_host_mode(core_if)) {
+		core_if->xceiv->state = OTG_STATE_A_HOST;
+	} else {
+		core_if->xceiv->state = OTG_STATE_B_PERIPHERAL;
+		if (dwc_has_feature(core_if, DWC_DEVICE_ONLY))
+			dwc_otg_core_dev_init(core_if);
+	}
+}
+
+/**
+ * This function enables the Device mode interrupts.
+ *
+ * Note that the bits in the Device IN endpoint mask register are laid out
+ * exactly the same as the Device IN endpoint interrupt register.
+ */
+static void dwc_otg_enable_device_interrupts(struct core_if *core_if)
+{
+	u32 intr_mask = 0;
+	u32 msk = 0;
+	u32 global_regs = (u32) core_if->core_global_regs;
+
+	/* 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 interrupts */
+	intr_mask |= DWC_INTMSK_USB_RST;
+	intr_mask |= DWC_INTMSK_ENUM_DONE;
+	intr_mask |= DWC_INTMSK_IN_ENDP;
+	intr_mask |= DWC_INTMSK_OUT_ENDP;
+	intr_mask |= DWC_INTMSK_EARLY_SUSP;
+	if (!core_if->en_multiple_tx_fifo)
+		intr_mask |= DWC_INTMSK_ENDP_MIS_MTCH;
+
+	/* Periodic EP */
+	intr_mask |= DWC_INTMSK_ISYNC_OUTPKT_DRP;
+	intr_mask |= DWC_INTMSK_END_OF_PFRM;
+	intr_mask |= DWC_INTMSK_INCMP_IN_ATX;
+	intr_mask |= DWC_INTMSK_INCMP_OUT_PTX;
+
+	dwc_modify32(global_regs + DWC_GINTMSK, intr_mask, intr_mask);
+
+	msk = DWC_DIEPMSK_TXFIFO_UNDERN_RW(msk, 1);
+	dwc_modify32((u32) core_if->dev_if->dev_global_regs + DWC_DIEPMSK,
+		     msk, msk);
+}
+
+/**
+ *  Configures the device data fifo sizes when dynamic sizing is enabled.
+ */
+static void config_dev_dynamic_fifos(struct core_if *core_if)
+{
+	u32 i;
+	u32 regs = (u32) core_if->core_global_regs;
+	struct core_params *params = core_if->core_params;
+	u32 txsize = 0;
+	u32 nptxsize = 0;
+	u32 ptxsize = 0;
+
+	/* Rx FIFO */
+	dwc_write32(regs + DWC_GRXFSIZ, params->dev_rx_fifo_size);
+
+	/* Set Periodic and Non-periodic Tx FIFO Mask bits to all 0 */
+	core_if->p_tx_msk = 0;
+	core_if->tx_msk = 0;
+
+	if (core_if->en_multiple_tx_fifo == 0) {
+		/* Non-periodic Tx FIFO */
+		nptxsize = DWC_RX_FIFO_DEPTH_WR(nptxsize,
+						params->
+						dev_nperio_tx_fifo_size);
+		nptxsize =
+		    DWC_RX_FIFO_START_ADDR_WR(nptxsize,
+					      params->dev_rx_fifo_size);
+		dwc_write32(regs + DWC_GNPTXFSIZ, nptxsize);
+
+		/*
+		 * Periodic Tx FIFOs These FIFOs are numbered from 1 to
+		 * 15. Indexes of the FIFO size module parameters in the
+		 * dev_perio_tx_fifo_size array and the FIFO size
+		 * registers in the dptxfsiz array run from 0 to 14.
+		 */
+		ptxsize = DWC_RX_FIFO_START_ADDR_WR(ptxsize,
+						    (DWC_RX_FIFO_START_ADDR_RD
+						     (nptxsize) +
+						     DWC_RX_FIFO_DEPTH_RD
+						     (nptxsize)));
+		for (i = 0;
+		     i < DWC_HWCFG4_NUM_DEV_PERIO_IN_EP_RD(core_if->hwcfg4);
+		     i++) {
+			ptxsize =
+			    DWC_RX_FIFO_DEPTH_WR(ptxsize,
+						 params->
+						 dev_perio_tx_fifo_size[i]);
+			dwc_write32(regs + DWC_DPTX_FSIZ_DIPTXF(i), ptxsize);
+			ptxsize = DWC_RX_FIFO_START_ADDR_WR(ptxsize,
+						   (DWC_RX_FIFO_START_ADDR_RD
+						    (ptxsize) +
+						    DWC_RX_FIFO_DEPTH_RD
+						    (ptxsize)));
+		}
+	} else {
+		/*
+		 * Non-periodic Tx FIFOs These FIFOs are numbered from
+		 * 1 to 15. Indexes of the FIFO size module parameters
+		 * in the dev_tx_fifo_size array and the FIFO size
+		 * registers in the dptxfsiz_dieptxf array run from 0 to
+		 * 14.
+		 */
+		nptxsize = DWC_RX_FIFO_DEPTH_WR(nptxsize,
+						params->
+						dev_nperio_tx_fifo_size);
+		nptxsize =
+		    DWC_RX_FIFO_START_ADDR_WR(nptxsize,
+					      params->dev_rx_fifo_size);
+		dwc_write32(regs + DWC_GNPTXFSIZ, nptxsize);
+
+		txsize = DWC_RX_FIFO_START_ADDR_WR(txsize,
+						   (DWC_RX_FIFO_START_ADDR_RD
+						    (nptxsize) +
+						    DWC_RX_FIFO_DEPTH_RD
+						    (nptxsize)));
+		for (i = 1;
+		     i < DWC_HWCFG4_NUM_DEV_PERIO_IN_EP_RD(core_if->hwcfg4);
+		     i++) {
+			txsize =
+			    DWC_RX_FIFO_DEPTH_WR(txsize,
+						 params->dev_tx_fifo_size[i]);
+			dwc_write32(regs + DWC_DPTX_FSIZ_DIPTXF(i - 1), txsize);
+			txsize = DWC_RX_FIFO_START_ADDR_WR(txsize,
+						   (DWC_RX_FIFO_START_ADDR_RD
+						    (txsize) +
+						    DWC_RX_FIFO_DEPTH_RD
+						    (txsize)));
+		}
+	}
+}
+
+/**
+ * This function initializes the DWC_otg controller registers for
+ * device mode.
+ */
+void dwc_otg_core_dev_init(struct core_if *c_if)
+{
+	u32 i;
+	struct device_if *d_if = c_if->dev_if;
+	struct core_params *params = c_if->core_params;
+	u32 dcfg = 0;
+	u32 resetctl = 0;
+	u32 dthrctl = 0;
+
+	/* Restart the Phy Clock */
+	dwc_write32(c_if->pcgcctl, 0);
+
+	/* Device configuration register */
+	init_devspd(c_if);
+	dcfg = dwc_read32((u32) d_if->dev_global_regs + DWC_DCFG);
+	dcfg = DWC_DCFG_P_FRM_INTRVL_WR(dcfg, DWC_DCFG_FRAME_INTERVAL_80);
+	dwc_write32((u32) d_if->dev_global_regs + DWC_DCFG, dcfg);
+
+	/* If needed configure data FIFO sizes */
+	if (DWC_HWCFG2_DYN_FIFO_RD(c_if->hwcfg2) && params->enable_dynamic_fifo)
+		config_dev_dynamic_fifos(c_if);
+
+	/* Flush the FIFOs */
+	dwc_otg_flush_tx_fifo(c_if, DWC_GRSTCTL_TXFNUM_ALL);
+	dwc_otg_flush_rx_fifo(c_if);
+
+	/* Flush the Learning Queue. */
+	resetctl |= DWC_RSTCTL_TKN_QUE_FLUSH;
+	dwc_write32((u32) c_if->core_global_regs + DWC_GRSTCTL, resetctl);
+
+	/* Clear all pending Device Interrupts */
+	dwc_write32((u32) d_if->dev_global_regs + DWC_DIEPMSK, 0);
+	dwc_write32((u32) d_if->dev_global_regs + DWC_DOEPMSK, 0);
+	dwc_write32((u32) d_if->dev_global_regs + DWC_DAINT, 0xFFFFFFFF);
+	dwc_write32((u32) d_if->dev_global_regs + DWC_DAINTMSK, 0);
+
+	for (i = 0; i <= d_if->num_in_eps; i++) {
+		u32 depctl = 0;
+
+		depctl = dwc_read32((u32) d_if->in_ep_regs[i] + DWC_DIEPCTL);
+		if (DWC_DEPCTL_EPENA_RD(depctl)) {
+			depctl = 0;
+			depctl = DWC_DEPCTL_EPDIS_RW(depctl, 1);
+			depctl = DWC_DEPCTL_SET_NAK_RW(depctl, 1);
+		} else {
+			depctl = 0;
+		}
+
+		dwc_write32((u32) d_if->in_ep_regs[i] + DWC_DIEPCTL, depctl);
+		dwc_write32((u32) d_if->in_ep_regs[i] + DWC_DIEPTSIZ, 0);
+		dwc_write32((u32) d_if->in_ep_regs[i] + DWC_DIEPDMA, 0);
+		dwc_write32((u32) d_if->in_ep_regs[i] + DWC_DIEPINT, 0xFF);
+	}
+
+	for (i = 0; i <= d_if->num_out_eps; i++) {
+		u32 depctl = 0;
+		depctl = dwc_read32((u32) d_if->out_ep_regs[i] + DWC_DOEPCTL);
+		if (DWC_DEPCTL_EPENA_RD(depctl)) {
+			depctl = 0;
+			depctl = DWC_DEPCTL_EPDIS_RW(depctl, 1);
+			depctl = DWC_DEPCTL_SET_NAK_RW(depctl, 1);
+		} else {
+			depctl = 0;
+		}
+		dwc_write32((u32) d_if->out_ep_regs[i] + DWC_DOEPCTL, depctl);
+		dwc_write32((u32) d_if->out_ep_regs[i] + DWC_DOEPTSIZ, 0);
+		dwc_write32((u32) d_if->out_ep_regs[i] + DWC_DOEPDMA, 0);
+		dwc_write32((u32) d_if->out_ep_regs[i] + DWC_DOEPINT, 0xFF);
+	}
+
+	if (c_if->en_multiple_tx_fifo && c_if->dma_enable) {
+		d_if->non_iso_tx_thr_en = c_if->core_params->thr_ctl & 0x1;
+		d_if->iso_tx_thr_en = (c_if->core_params->thr_ctl >> 1) & 0x1;
+		d_if->rx_thr_en = (c_if->core_params->thr_ctl >> 2) & 0x1;
+		d_if->rx_thr_length = c_if->core_params->rx_thr_length;
+		d_if->tx_thr_length = c_if->core_params->tx_thr_length;
+
+		dthrctl = 0;
+		dthrctl = DWC_DTHCTRL_NON_ISO_THR_ENA_RW
+		    (dthrctl, d_if->non_iso_tx_thr_en);
+		dthrctl = DWC_DTHCTRL_ISO_THR_EN_RW
+		    (dthrctl, d_if->iso_tx_thr_en);
+		dthrctl = DWC_DTHCTRL_TX_THR_LEN_RW
+		    (dthrctl, d_if->tx_thr_length);
+		dthrctl = DWC_DTHCTRL_RX_THR_EN_RW(dthrctl, d_if->rx_thr_en);
+		dthrctl = DWC_DTHCTRL_RX_THR_LEN_RW
+		    (dthrctl, d_if->rx_thr_length);
+		dwc_write32((u32) d_if->dev_global_regs +
+			    DWC_DTKNQR3_DTHRCTL, dthrctl);
+
+	}
+
+	dwc_otg_enable_device_interrupts(c_if);
+}
+
+/**
+ * This function reads a packet from the Rx FIFO into the destination buffer.
+ * To read SETUP data use dwc_otg_read_setup_packet.
+ */
+void dwc_otg_read_packet(struct core_if *core_if, u8 * dest, u16 _bytes)
+{
+	u32 i;
+	int word_count = (_bytes + 3) / 4;
+	u32 *fifo = core_if->data_fifo[0];
+	u32 *data_buff = (u32 *) dest;
+
+	/*
+	 * This requires reading data from the FIFO into a u32 temp buffer,
+	 * then moving it into the data buffer.
+	 */
+	for (i = 0; i < word_count; i++, data_buff++)
+		*data_buff = dwc_read_datafifo32(fifo);
+}
+
+/**
+ * Flush a Tx FIFO.
+ */
+void dwc_otg_flush_tx_fifo(struct core_if *core_if, const int num)
+{
+	u32 global_regs = (u32) core_if->core_global_regs;
+	u32 greset = 0;
+	int count = 0;
+
+	greset |= DWC_RSTCTL_TX_FIFO_FLUSH;
+	greset = DWC_RSTCTL_TX_FIFO_NUM(greset, num);
+	dwc_write32(global_regs + DWC_GRSTCTL, greset);
+
+	do {
+		greset = dwc_read32(global_regs + DWC_GRSTCTL);
+		if (++count > 10000) {
+			pr_warning("%s() HANG! GRSTCTL=%0x "
+				   "GNPTXSTS=0x%08x\n", __func__, greset,
+				   dwc_read32(global_regs + DWC_GNPTXSTS));
+			break;
+		}
+		udelay(1);
+	} while (greset & DWC_RSTCTL_TX_FIFO_FLUSH);
+
+	/* Wait for 3 PHY Clocks */
+	udelay(1);
+}
+
+/**
+ * Flush Rx FIFO.
+ */
+void dwc_otg_flush_rx_fifo(struct core_if *core_if)
+{
+	u32 global_regs = (u32) core_if->core_global_regs;
+	u32 greset = 0;
+	int count = 0;
+
+	greset |= DWC_RSTCTL_RX_FIFO_FLUSH;
+	dwc_write32(global_regs + DWC_GRSTCTL, greset);
+
+	do {
+		greset = dwc_read32(global_regs + DWC_GRSTCTL);
+		if (++count > 10000) {
+			pr_warning("%s() HANG! GRSTCTL=%0x\n",
+				   __func__, greset);
+			break;
+		}
+		udelay(1);
+	} while (greset & DWC_RSTCTL_RX_FIFO_FLUSH);
+
+	/* Wait for 3 PHY Clocks */
+	udelay(1);
+}
+
+/**
+ * Register HCD callbacks.
+ * The callbacks are used to start and stop the HCD for interrupt processing.
+ */
+void __devinit dwc_otg_cil_register_hcd_callbacks(struct core_if *c_if,
+						  struct cil_callbacks *cb,
+						  void *p)
+{
+	c_if->hcd_cb = cb;
+	cb->p = p;
+}
+
+/**
+ * Register PCD callbacks.
+ * The callbacks are used to start and stop the PCD for interrupt processing.
+ */
+void __devinit dwc_otg_cil_register_pcd_callbacks(struct core_if *c_if,
+						  struct cil_callbacks *cb,
+						  void *p)
+{
+	c_if->pcd_cb = cb;
+	cb->p = p;
+}
+
+/**
+ * This function is called to initialize the DWC_otg CSR data structures.
+ *
+ * The register addresses in the device and host structures are initialized from
+ * the base address supplied by the caller. The calling function must make the
+ * OS calls to get the base address of the DWC_otg controller registers.
+ *
+ * The params argument holds the parameters that specify how the core should be
+ * configured.
+ */
+struct core_if __devinit *dwc_otg_cil_init(const __iomem u32 * base,
+					   struct core_params *params)
+{
+	struct core_if *core_if;
+	struct device_if *dev_if;
+	struct dwc_host_if *host_if;
+	u8 *reg_base = (__force u8 *)base;
+	u32 offset;
+	u32 i;
+
+	core_if = kzalloc(sizeof(*core_if), GFP_KERNEL);
+	if (!core_if)
+		return NULL;
+
+	core_if->core_params = params;
+	core_if->core_global_regs = (u32) reg_base;
+
+	/* Allocate the Device Mode structures. */
+	dev_if = kmalloc(sizeof(*dev_if), GFP_KERNEL);
+	if (!dev_if) {
+		kfree(core_if);
+		return NULL;
+	}
+
+	dev_if->dev_global_regs = (u32) (reg_base + DWC_DEV_GLOBAL_REG_OFFSET);
+
+	for (i = 0; i < MAX_EPS_CHANNELS; i++) {
+		offset = i * DWC_EP_REG_OFFSET;
+
+		dev_if->in_ep_regs[i] = (u32) (reg_base +
+					       DWC_DEV_IN_EP_REG_OFFSET +
+					       offset);
+
+		dev_if->out_ep_regs[i] = (u32) (reg_base +
+						DWC_DEV_OUT_EP_REG_OFFSET +
+						offset);
+	}
+
+	dev_if->speed = 0;	/* unknown */
+	core_if->dev_if = dev_if;
+
+	/* Allocate the Host Mode structures. */
+	host_if = kmalloc(sizeof(*host_if), GFP_KERNEL);
+	if (!host_if) {
+		kfree(dev_if);
+		kfree(core_if);
+		return NULL;
+	}
+
+	host_if->host_global_regs = (u32) (reg_base +
+					   DWC_OTG_HOST_GLOBAL_REG_OFFSET);
+
+	host_if->hprt0 = (u32) (reg_base + DWC_OTG_HOST_PORT_REGS_OFFSET);
+
+	for (i = 0; i < MAX_EPS_CHANNELS; i++) {
+		offset = i * DWC_OTG_CHAN_REGS_OFFSET;
+
+		host_if->hc_regs[i] = (u32) (reg_base +
+					     DWC_OTG_HOST_CHAN_REGS_OFFSET +
+					     offset);
+	}
+
+	host_if->num_host_channels = MAX_EPS_CHANNELS;
+	core_if->host_if = host_if;
+	for (i = 0; i < MAX_EPS_CHANNELS; i++) {
+		core_if->data_fifo[i] =
+		    (u32 *) (reg_base + DWC_OTG_DATA_FIFO_OFFSET +
+			     (i * DWC_OTG_DATA_FIFO_SIZE));
+	}
+	core_if->pcgcctl = (u32) (reg_base + DWC_OTG_PCGCCTL_OFFSET);
+
+	/*
+	 * Store the contents of the hardware configuration registers here for
+	 * easy access later.
+	 */
+	core_if->hwcfg1 =
+	    dwc_read32((u32) core_if->core_global_regs + DWC_GHWCFG1);
+	core_if->hwcfg2 =
+	    dwc_read32((u32) core_if->core_global_regs + DWC_GHWCFG2);
+	core_if->hwcfg3 =
+	    dwc_read32((u32) core_if->core_global_regs + DWC_GHWCFG3);
+	core_if->hwcfg4 =
+	    dwc_read32((u32) core_if->core_global_regs + DWC_GHWCFG4);
+
+	/* Set the SRP sucess bit for FS-I2c */
+	core_if->srp_success = 0;
+	core_if->srp_timer_started = 0;
+	return core_if;
+}
+
+/**
+ * This function frees the structures allocated by dwc_otg_cil_init().
+ */
+void dwc_otg_cil_remove(struct core_if *core_if)
+{
+	/* Disable all interrupts */
+	dwc_modify32((u32) core_if->core_global_regs + DWC_GAHBCFG, 1, 0);
+	dwc_write32((u32) core_if->core_global_regs + DWC_GINTMSK, 0);
+
+	if (core_if) {
+		kfree(core_if->dev_if);
+		kfree(core_if->host_if);
+	}
+	kfree(core_if);
+}
diff --git a/drivers/usb/dwc_otg/dwc_otg_cil.h b/drivers/usb/dwc_otg/dwc_otg_cil.h
new file mode 100644
index 0000000..49e6cbf
--- /dev/null
+++ b/drivers/usb/dwc_otg/dwc_otg_cil.h
@@ -0,0 +1,1219 @@
+/*
+ * 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.
+ *
+ */
+
+#if !defined(__DWC_CIL_H__)
+#define __DWC_CIL_H__
+#include <linux/io.h>
+#include <linux/usb/ch9.h>
+#include <linux/usb/gadget.h>
+#include <linux/interrupt.h>
+#include <linux/dmapool.h>
+#include <linux/spinlock.h>
+#include <linux/usb/otg.h>
+
+#include "dwc_otg_regs.h"
+
+#ifdef CONFIG_DWC_DEBUG
+#define DEBUG
+#endif
+
+/**
+ * Reads the content of a register.
+ */
+static inline u32 dwc_read32(u32 reg)
+{
+#ifdef CONFIG_DWC_OTG_REG_LE
+	return in_le32((unsigned __iomem *)reg);
+#else
+	return in_be32((unsigned __iomem *)reg);
+#endif
+};
+static inline u32 dwc_read_reg32(u32 *reg)
+{
+#ifdef CONFIG_DWC_OTG_REG_LE
+	return in_le32(reg);
+#else
+	return in_be32(reg);
+#endif
+};
+
+/**
+ * Writes a register with a 32 bit value.
+ */
+static inline void dwc_write32(u32 reg, const u32 value)
+{
+#ifdef CONFIG_DWC_OTG_REG_LE
+	out_le32((unsigned __iomem *)reg, value);
+#else
+	out_be32((unsigned __iomem *)reg, value);
+#endif
+};
+static inline void dwc_write_reg32(u32 *reg, const u32 value)
+{
+#ifdef CONFIG_DWC_OTG_REG_LE
+	out_le32(reg, value);
+#else
+	out_be32(reg, value);
+#endif
+};
+
+/**
+ * This function modifies bit values in a register.  Using the
+ * algorithm: (reg_contents & ~clear_mask) | set_mask.
+ */
+static inline
+	void dwc_modify_reg32(u32 *_reg, const u32 _clear_mask,
+			  const u32 _set_mask)
+{
+#ifdef CONFIG_DWC_OTG_REG_LE
+	out_le32(_reg, (in_le32(_reg) & ~_clear_mask) | _set_mask);
+#else
+	out_be32(_reg, (in_be32(_reg) & ~_clear_mask) | _set_mask);
+#endif
+};
+static inline
+	void dwc_modify32(u32 reg, const u32 _clear_mask, const u32 _set_mask)
+{
+#ifdef CONFIG_DWC_OTG_REG_LE
+	out_le32((unsigned __iomem *)reg,
+			(in_le32((unsigned __iomem *)reg) & ~_clear_mask) |
+			_set_mask);
+#else
+	out_be32((unsigned __iomem *)reg,
+			(in_be32(((unsigned __iomem *))reg) & ~_clear_mask) |
+			_set_mask);
+#endif
+};
+
+static inline void dwc_write_datafifo32(u32 *reg, const u32 _value)
+{
+#ifdef CONFIG_DWC_OTG_FIFO_LE
+	out_le32((unsigned __iomem *)reg, _value);
+#else
+	out_be32((unsigned __iomem *)reg, _value);
+#endif
+};
+static inline void dwc_write_fifo32(u32 reg, const u32 _value)
+{
+#ifdef CONFIG_DWC_OTG_FIFO_LE
+	out_le32((unsigned __iomem *)reg, _value);
+#else
+	out_be32((unsigned __iomem *)reg, _value);
+#endif
+};
+
+static inline u32 dwc_read_datafifo32(u32 *_reg)
+{
+#ifdef CONFIG_DWC_OTG_FIFO_LE
+	return in_le32((unsigned __iomem *)_reg);
+#else
+	return in_be32((unsigned __iomem *)_reg);
+#endif
+};
+static inline u32 dwc_read_fifo32(u32 _reg)
+{
+#ifdef CONFIG_DWC_OTG_FIFO_LE
+	return in_le32((unsigned __iomem *) _reg);
+#else
+	return in_be32((unsigned __iomem *) _reg);
+#endif
+};
+
+/*
+ * Debugging support vanishes in non-debug builds.
+ */
+/* Display CIL Debug messages */
+#define dwc_dbg_cil		(0x2)
+
+/* Display CIL Verbose debug messages */
+#define dwc_dbg_cilv		(0x20)
+
+/* Display PCD (Device) debug messages */
+#define dwc_dbg_pcd		(0x4)
+
+/* Display PCD (Device) Verbose debug  messages */
+#define dwc_dbg_pcdv		(0x40)
+
+/* Display Host debug messages */
+#define dwc_dbg_hcd		(0x8)
+
+/* Display Verbose Host debug messages */
+#define dwc_dbg_hcdv		(0x80)
+
+/* Display enqueued URBs in host mode. */
+#define dwc_dbg_hcd_urb		(0x800)
+
+/* Display "special purpose" debug messages */
+#define dwc_dbg_sp		(0x400)
+
+/* Display all debug messages */
+#define dwc_dbg_any		(0xFF)
+
+/* All debug messages off */
+#define dwc_dbg_off		0
+
+/* Prefix string for DWC_DEBUG print macros. */
+#define usb_dwc "dwc_otg: "
+
+/*
+ * This file contains the interface to the Core Interface Layer.
+ */
+
+/*
+ * Added-sr: 2007-07-26
+ *
+ * Since the 405EZ (Ultra) only support 2047 bytes as
+ * max transfer size, we have to split up bigger transfers
+ * into multiple transfers of 1024 bytes sized messages.
+ * I happens often, that transfers of 4096 bytes are
+ * required (zero-gadget, file_storage-gadget).
+ *
+ * MAX_XFER_LEN is set to 1024 right now, but could be 2047,
+ * since the xfer-size field in the 405EZ USB device controller
+ * implementation has 11 bits. Using 1024 seems to work for now.
+ */
+#define MAX_XFER_LEN	1024
+
+/*
+ * The dwc_ep structure represents the state of a single endpoint when acting in
+ * device mode. It contains the data items needed for an endpoint to be
+ * activated and transfer packets.
+ */
+struct dwc_ep {
+	/* EP number used for register address lookup */
+	u8 num;
+	/* EP direction 0 = OUT */
+	unsigned is_in:1;
+	/* EP active. */
+	unsigned active:1;
+
+	/*
+	 * Periodic Tx FIFO # for IN EPs For INTR EP set to 0 to use
+	 * non-periodic Tx FIFO If dedicated Tx FIFOs are enabled for all
+	 * IN Eps - Tx FIFO # FOR IN EPs
+	 */
+	unsigned tx_fifo_num:4;
+	/* EP type: 0 - Control, 1 - ISOC,       2 - BULK,      3 - INTR */
+	unsigned type:2;
+#define DWC_OTG_EP_TYPE_CONTROL		0
+#define DWC_OTG_EP_TYPE_ISOC		1
+#define DWC_OTG_EP_TYPE_BULK		2
+#define DWC_OTG_EP_TYPE_INTR		3
+
+	/* DATA start PID for INTR and BULK EP */
+	unsigned data_pid_start:1;
+	/* Frame (even/odd) for ISOC EP */
+	unsigned even_odd_frame:1;
+	/* Max Packet bytes */
+	unsigned maxpacket:11;
+
+	u32 dma_addr;
+
+	/*
+	 * Pointer to the beginning of the transfer buffer -- do not modify
+	 * during transfer.
+	 */
+	u8 *start_xfer_buff;
+	/* pointer to the transfer buffer */
+	u8 *xfer_buff;
+	/* Number of bytes to transfer */
+	unsigned xfer_len:19;
+	/* Number of bytes transferred. */
+	unsigned xfer_count:19;
+	/* Sent ZLP */
+	unsigned sent_zlp:1;
+	/* Total len for control transfer */
+	unsigned total_len:19;
+
+	/* stall clear flag */
+	unsigned stall_clear_flag:1;
+
+	/*
+	 * Added-sr: 2007-07-26
+	 *
+	 * Since the 405EZ (Ultra) only support 2047 bytes as
+	 * max transfer size, we have to split up bigger transfers
+	 * into multiple transfers of 1024 bytes sized messages.
+	 * I happens often, that transfers of 4096 bytes are
+	 * required (zero-gadget, file_storage-gadget).
+	 *
+	 * "bytes_pending" will hold the amount of bytes that are
+	 * still pending to be send in further messages to complete
+	 * the bigger transfer.
+	 */
+	u32 bytes_pending;
+};
+
+/*
+ * States of EP0.
+ */
+enum ep0_state {
+	EP0_DISCONNECT = 0,	/* no host */
+	EP0_IDLE = 1,
+	EP0_IN_DATA_PHASE = 2,
+	EP0_OUT_DATA_PHASE = 3,
+	EP0_STATUS = 4,
+	EP0_STALL = 5,
+};
+
+/* Fordward declaration.*/
+struct dwc_pcd;
+
+/*
+ * This structure describes an EP, there is an array of EPs in the PCD
+ * structure.
+ */
+struct pcd_ep {
+	/* USB EP data */
+	struct usb_ep ep;
+	/* USB EP Descriptor */
+	const struct usb_endpoint_descriptor *desc;
+
+	/* queue of dwc_otg_pcd_requests. */
+	struct list_head queue;
+	unsigned stopped:1;
+	unsigned disabling:1;
+	unsigned dma:1;
+	unsigned queue_sof:1;
+
+	/* DWC_otg ep data. */
+	struct dwc_ep dwc_ep;
+
+	/* Pointer to PCD */
+	struct dwc_pcd *pcd;
+};
+
+/*
+ * DWC_otg PCD Structure.
+ * This structure encapsulates the data for the dwc_otg PCD.
+ */
+struct dwc_pcd {
+	/* USB gadget */
+	struct usb_gadget gadget;
+	/* USB gadget driver pointer */
+	struct usb_gadget_driver *driver;
+	/* The DWC otg device pointer. */
+	struct dwc_otg_device *otg_dev;
+
+	/* State of EP0 */
+	enum ep0_state ep0state;
+	/* EP0 Request is pending */
+	unsigned ep0_pending:1;
+	/* Indicates when SET CONFIGURATION Request is in process */
+	unsigned request_config:1;
+	/* The state of the Remote Wakeup Enable. */
+	unsigned remote_wakeup_enable:1;
+	/* The state of the B-Device HNP Enable. */
+	unsigned b_hnp_enable:1;
+	/* The state of A-Device HNP Support. */
+	unsigned a_hnp_support:1;
+	/* The state of the A-Device Alt HNP support. */
+	unsigned a_alt_hnp_support:1;
+	/* Count of pending Requests */
+	unsigned request_pending;
+
+	/*
+	 * SETUP packet for EP0.  This structure is allocated as a DMA buffer on
+	 * PCD initialization with enough space for up to 3 setup packets.
+	 */
+	union {
+		struct usb_ctrlrequest req;
+		u32 d32[2];
+	} *setup_pkt;
+
+	struct dma_pool *dwc_pool;
+	dma_addr_t setup_pkt_dma_handle;
+
+	/* 2-byte dma buffer used to return status from GET_STATUS */
+	u16 *status_buf;
+	dma_addr_t status_buf_dma_handle;
+
+	/* Array of EPs. */
+	struct pcd_ep ep0;
+	/* Array of IN EPs. */
+	struct pcd_ep in_ep[MAX_EPS_CHANNELS - 1];
+	/* Array of OUT EPs. */
+	struct pcd_ep out_ep[MAX_EPS_CHANNELS - 1];
+	spinlock_t lock;
+	/*
+	 *  Timer for SRP.  If it expires before SRP is successful clear the
+	 *  SRP.
+	 */
+	struct timer_list srp_timer;
+
+	/*
+	 * Tasklet to defer starting of TEST mode transmissions until Status
+	 * Phase has been completed.
+	 */
+	struct tasklet_struct test_mode_tasklet;
+
+	/*  Tasklet to delay starting of xfer in DMA mode */
+	struct tasklet_struct *start_xfer_tasklet;
+
+	/* The test mode to enter when the tasklet is executed. */
+	unsigned test_mode;
+};
+
+/*
+ * This structure holds the state of the HCD, including the non-periodic and
+ * periodic schedules.
+ */
+struct dwc_hcd {
+	spinlock_t lock;
+
+	/* DWC OTG Core Interface Layer */
+	struct core_if *core_if;
+
+	/* Internal DWC HCD Flags */
+	union dwc_otg_hcd_internal_flags {
+		u32 d32;
+		struct {
+			unsigned port_connect_status_change:1;
+			unsigned port_connect_status:1;
+			unsigned port_reset_change:1;
+			unsigned port_enable_change:1;
+			unsigned port_suspend_change:1;
+			unsigned port_over_current_change:1;
+			unsigned reserved:27;
+		} b;
+	} flags;
+
+	/*
+	 * Inactive items in the non-periodic schedule. This is a list of
+	 * Queue Heads. Transfers associated with these Queue Heads are not
+	 * currently assigned to a host channel.
+	 */
+	struct list_head non_periodic_sched_inactive;
+
+	/*
+	 * Deferred items in the non-periodic schedule. This is a list of
+	 * Queue Heads. Transfers associated with these Queue Heads are not
+	 * currently assigned to a host channel.
+	 * When we get an NAK, the QH goes here.
+	 */
+	struct list_head non_periodic_sched_deferred;
+
+	/*
+	 * Active items in the non-periodic schedule. This is a list of
+	 * Queue Heads. Transfers associated with these Queue Heads are
+	 * currently assigned to a host channel.
+	 */
+	struct list_head non_periodic_sched_active;
+
+	/*
+	 * Pointer to the next Queue Head to process in the active
+	 * non-periodic schedule.
+	 */
+	struct list_head *non_periodic_qh_ptr;
+
+	/*
+	 * Inactive items in the periodic schedule. This is a list of QHs for
+	 * periodic transfers that are _not_ scheduled for the next frame.
+	 * Each QH in the list has an interval counter that determines when it
+	 * needs to be scheduled for execution. This scheduling mechanism
+	 * allows only a simple calculation for periodic bandwidth used (i.e.
+	 * must assume that all periodic transfers may need to execute in the
+	 * same frame). However, it greatly simplifies scheduling and should
+	 * be sufficient for the vast majority of OTG hosts, which need to
+	 * connect to a small number of peripherals at one time.
+	 *
+	 * Items move from this list to periodic_sched_ready when the QH
+	 * interval counter is 0 at SOF.
+	 */
+	struct list_head periodic_sched_inactive;
+
+	/*
+	 * List of periodic QHs that are ready for execution in the next
+	 * frame, but have not yet been assigned to host channels.
+	 *
+	 * Items move from this list to periodic_sched_assigned as host
+	 * channels become available during the current frame.
+	 */
+	struct list_head periodic_sched_ready;
+
+	/*
+	 * List of periodic QHs to be executed in the next frame that are
+	 * assigned to host channels.
+	 *
+	 * Items move from this list to periodic_sched_queued as the
+	 * transactions for the QH are queued to the DWC_otg controller.
+	 */
+	struct list_head periodic_sched_assigned;
+
+	/*
+	 * List of periodic QHs that have been queued for execution.
+	 *
+	 * Items move from this list to either periodic_sched_inactive or
+	 * periodic_sched_ready when the channel associated with the transfer
+	 * is released. If the interval for the QH is 1, the item moves to
+	 * periodic_sched_ready because it must be rescheduled for the next
+	 * frame. Otherwise, the item moves to periodic_sched_inactive.
+	 */
+	struct list_head periodic_sched_queued;
+
+	/*
+	 * Total bandwidth claimed so far for periodic transfers. This value
+	 * is in microseconds per (micro)frame. The assumption is that all
+	 * periodic transfers may occur in the same (micro)frame.
+	 */
+	u16 periodic_usecs;
+
+	/*
+	 * Total bandwidth claimed so far for all periodic transfers
+	 * in a frame.
+	 * This will include a mixture of HS and FS transfers.
+	 * Units are microseconds per (micro)frame.
+	 * We have a budget per frame and have to schedule
+	 * transactions accordingly.
+	 * Watch out for the fact that things are actually scheduled for the
+	 * "next frame".
+	 */
+	u16 frame_usecs[8];
+
+	/*
+	 * Frame number read from the core at SOF. The value ranges from 0 to
+	 * DWC_HFNUM_MAX_FRNUM.
+	 */
+	u16 frame_number;
+
+	/*
+	 * Free host channels in the controller. This is a list of
+	 * struct dwc_hc items.
+	 */
+	struct list_head free_hc_list;
+
+	/*
+	 * Number of available host channels.
+	 */
+	u32 available_host_channels;
+
+	/*
+	 * Array of pointers to the host channel descriptors. Allows accessing
+	 * a host channel descriptor given the host channel number. This is
+	 * useful in interrupt handlers.
+	 */
+	struct dwc_hc *hc_ptr_array[MAX_EPS_CHANNELS];
+
+	/*
+	 * Buffer to use for any data received during the status phase of a
+	 * control transfer. Normally no data is transferred during the status
+	 * phase. This buffer is used as a bit bucket.
+	 */
+	u8 *status_buf;
+
+	/*
+	 * DMA address for status_buf.
+	 */
+	dma_addr_t status_buf_dma;
+#define DWC_OTG_HCD_STATUS_BUF_SIZE		64
+
+	/*
+	 * Structure to allow starting the HCD in a non-interrupt context
+	 * during an OTG role change.
+	 */
+	struct work_struct start_work;
+	struct usb_hcd *_p;
+
+	/*
+	 * Connection timer. An OTG host must display a message if the device
+	 * does not connect. Started when the VBus power is turned on via
+	 * sysfs attribute "buspower".
+	 */
+	struct timer_list conn_timer;
+
+	/* workqueue for port wakeup */
+	struct work_struct usb_port_reset;
+
+	/* Addition HCD interrupt */
+	int cp_irq;		/* charge pump interrupt */
+	int cp_irq_installed;
+};
+
+/*
+ * Reasons for halting a host channel.
+ */
+enum dwc_halt_status {
+	DWC_OTG_HC_XFER_NO_HALT_STATUS,
+	DWC_OTG_HC_XFER_COMPLETE,
+	DWC_OTG_HC_XFER_URB_COMPLETE,
+	DWC_OTG_HC_XFER_ACK,
+	DWC_OTG_HC_XFER_NAK,
+	DWC_OTG_HC_XFER_NYET,
+	DWC_OTG_HC_XFER_STALL,
+	DWC_OTG_HC_XFER_XACT_ERR,
+	DWC_OTG_HC_XFER_FRAME_OVERRUN,
+	DWC_OTG_HC_XFER_BABBLE_ERR,
+	DWC_OTG_HC_XFER_DATA_TOGGLE_ERR,
+	DWC_OTG_HC_XFER_AHB_ERR,
+	DWC_OTG_HC_XFER_PERIODIC_INCOMPLETE,
+	DWC_OTG_HC_XFER_URB_DEQUEUE
+};
+
+/*
+ * Host channel descriptor. This structure represents the state of a single
+ * host channel when acting in host mode. It contains the data items needed to
+ * transfer packets to an endpoint via a host channel.
+ */
+struct dwc_hc {
+	/* Host channel number used for register address lookup */
+	u8 hc_num;
+
+	/* Device to access */
+	unsigned dev_addr:7;
+
+	/* EP to access */
+	unsigned ep_num:4;
+
+	/* EP direction. 0: OUT, 1: IN */
+	unsigned ep_is_in:1;
+
+	/*
+	 * EP speed.
+	 * One of the following values:
+	 *      - DWC_OTG_EP_SPEED_LOW
+	 *      - DWC_OTG_EP_SPEED_FULL
+	 *      - DWC_OTG_EP_SPEED_HIGH
+	 */
+	unsigned speed:2;
+#define DWC_OTG_EP_SPEED_LOW		0
+#define DWC_OTG_EP_SPEED_FULL		1
+#define DWC_OTG_EP_SPEED_HIGH		2
+
+	/*
+	 * Endpoint type.
+	 * One of the following values:
+	 *      - DWC_OTG_EP_TYPE_CONTROL: 0
+	 *      - DWC_OTG_EP_TYPE_ISOC: 1
+	 *      - DWC_OTG_EP_TYPE_BULK: 2
+	 *      - DWC_OTG_EP_TYPE_INTR: 3
+	 */
+	unsigned ep_type:2;
+
+	/* Max packet size in bytes */
+	unsigned max_packet:11;
+
+	/*
+	 * PID for initial transaction.
+	 * 0: DATA0,
+	 * 1: DATA2,
+	 * 2: DATA1,
+	 * 3: MDATA (non-Control EP),
+	 *      SETUP (Control EP)
+	 */
+	unsigned data_pid_start:2;
+#define DWC_OTG_HC_PID_DATA0		0
+#define DWC_OTG_HC_PID_DATA2		1
+#define DWC_OTG_HC_PID_DATA1		2
+#define DWC_OTG_HC_PID_MDATA		3
+#define DWC_OTG_HC_PID_SETUP		3
+
+	/* Number of periodic transactions per (micro)frame */
+	unsigned multi_count:2;
+
+	/* Pointer to the current transfer buffer position. */
+	u8 *xfer_buff;
+	/* Total number of bytes to transfer. */
+	u32 xfer_len;
+	/* Number of bytes transferred so far. */
+	u32 xfer_count;
+	/* Packet count at start of transfer. */
+	u16 start_pkt_count;
+
+	/*
+	 * Flag to indicate whether the transfer has been started. Set to 1 if
+	 * it has been started, 0 otherwise.
+	 */
+	u8 xfer_started;
+
+	/*
+	 * Set to 1 to indicate that a PING request should be issued on this
+	 * channel. If 0, process normally.
+	 */
+	u8 do_ping;
+
+	/*
+	 * Set to 1 to indicate that the error count for this transaction is
+	 * non-zero. Set to 0 if the error count is 0.
+	 */
+	u8 error_state;
+
+	/*
+	 * Set to 1 to indicate that this channel should be halted the next
+	 * time a request is queued for the channel. This is necessary in
+	 * slave mode if no request queue space is available when an attempt
+	 * is made to halt the channel.
+	 */
+	u8 halt_on_queue;
+
+	/*
+	 * Set to 1 if the host channel has been halted, but the core is not
+	 * finished flushing queued requests. Otherwise 0.
+	 */
+	u8 halt_pending;
+
+	/* Reason for halting the host channel. */
+	enum dwc_halt_status halt_status;
+
+	/*  Split settings for the host channel */
+	u8 do_split;		/* Enable split for the channel */
+	u8 complete_split;	/* Enable complete split */
+	u8 hub_addr;		/* Address of high speed hub */
+	u8 port_addr;		/* Port of the low/full speed device */
+
+	/*
+	 * Split transaction position. One of the following values:
+	 *      - DWC_HCSPLIT_XACTPOS_MID
+	 *      - DWC_HCSPLIT_XACTPOS_BEGIN
+	 *      - DWC_HCSPLIT_XACTPOS_END
+	 *      - DWC_HCSPLIT_XACTPOS_ALL */
+	u8 xact_pos;
+
+	/* Set when the host channel does a short read. */
+	u8 short_read;
+
+	/*
+	 * Number of requests issued for this channel since it was assigned to
+	 * the current transfer (not counting PINGs).
+	 */
+	u8 requests;
+
+	/* Queue Head for the transfer being processed by this channel. */
+	struct dwc_qh *qh;
+
+	/* Entry in list of host channels. */
+	struct list_head hc_list_entry;
+};
+
+/*
+ * The following parameters may be specified when starting the module. These
+ * parameters define how the DWC_otg controller should be configured.  Parameter
+ * values are passed to the CIL initialization function dwc_otg_cil_init.
+ */
+struct core_params {
+	/*
+	 * Specifies the OTG capabilities. The driver will automatically
+	 * detect the value for this parameter if none is specified.
+	 * 0 - HNP and SRP capable (default)
+	 * 1 - SRP Only capable
+	 * 2 - No HNP/SRP capable
+	 */
+	int otg_cap;
+#define DWC_OTG_CAP_PARAM_HNP_SRP_CAPABLE		0
+#define DWC_OTG_CAP_PARAM_SRP_ONLY_CAPABLE		1
+#define DWC_OTG_CAP_PARAM_NO_HNP_SRP_CAPABLE		2
+
+#define dwc_param_otg_cap_default	DWC_OTG_CAP_PARAM_HNP_SRP_CAPABLE
+
+	/*
+	 * Specifies whether to use slave or DMA mode for accessing the data
+	 * FIFOs. The driver will automatically detect the value for this
+	 * parameter if none is specified.
+	 * 0 - Slave
+	 * 1 - DMA (default, if available)
+	 */
+	int dma_enable;
+#ifdef CONFIG_DWC_SLAVE
+#define dwc_param_dma_enable_default			0
+#else
+#define dwc_param_dma_enable_default			1
+#endif
+
+	/*
+	 * The DMA Burst size (applicable only for External DMA Mode).
+	 * 1, 4, 8 16, 32, 64, 128, 256 (default 32)
+	 */
+	int dma_burst_size;	/* Translate this to GAHBCFG values */
+#define dwc_param_dma_burst_size_default		32
+
+	/*
+	 * Specifies the maximum speed of operation in host and device mode.
+	 * The actual speed depends on the speed of the attached device and
+	 * the value of phy_type. The actual speed depends on the speed of the
+	 * attached device.
+	 *      0 - High Speed (default)
+	 *      1 - Full Speed
+	 */
+	int speed;
+#define dwc_param_speed_default				0
+#define DWC_SPEED_PARAM_HIGH				0
+#define DWC_SPEED_PARAM_FULL				1
+
+	/*
+	 * Specifies whether low power mode is supported when attached to a Full
+	 * Speed or Low Speed device in host mode.
+	 *      0 - Don't support low power mode (default)
+	 *      1 - Support low power mode
+	 */
+	int host_support_fs_ls_low_power;
+#define dwc_param_host_support_fs_ls_low_power_default	0
+
+	/*
+	 * Specifies the PHY clock rate in low power mode when connected to a
+	 * Low Speed device in host mode. This parameter is applicable only if
+	 * HOST_SUPPORT_FS_LS_LOW_POWER is enabled. If PHY_TYPE is set to FS
+	 * then defaults to 6 MHZ otherwise 48 MHZ.
+	 *
+	 *      0 - 48 MHz
+	 *      1 - 6 MHz
+	 */
+	int host_ls_low_power_phy_clk;
+#define dwc_param_host_ls_low_power_phy_clk_default	0
+#define DWC_HOST_LS_LOW_POWER_PHY_CLK_PARAM_48MHZ	0
+#define DWC_HOST_LS_LOW_POWER_PHY_CLK_PARAM_6MHZ	1
+
+	/*
+	 * 0 - Use cC FIFO size parameters
+	 * 1 - Allow dynamic FIFO sizing (default)
+	 */
+	int enable_dynamic_fifo;
+#define dwc_param_enable_dynamic_fifo_default		1
+
+	/*
+	 * Number of 4-byte words in the Rx FIFO in device mode when dynamic
+	 * FIFO sizing is enabled.  16 to 32768 (default 1064)
+	 */
+	int dev_rx_fifo_size;
+#define dwc_param_dev_rx_fifo_size_default		1064
+
+	/*
+	 * Number of 4-byte words in the non-periodic Tx FIFO in device mode
+	 * when dynamic FIFO sizing is enabled.  16 to 32768 (default 1024)
+	 */
+	int dev_nperio_tx_fifo_size;
+#define dwc_param_dev_nperio_tx_fifo_size_default	1024
+
+	/*
+	 * Number of 4-byte words in each of the periodic Tx FIFOs in device
+	 * mode when dynamic FIFO sizing is enabled.  4 to 768 (default 256)
+	 */
+	u32 dev_perio_tx_fifo_size[MAX_PERIO_FIFOS];
+#define dwc_param_dev_perio_tx_fifo_size_default	256
+
+	/*
+	 * Number of 4-byte words in the Rx FIFO in host mode when dynamic
+	 * FIFO sizing is enabled.  16 to 32768 (default 1024)
+	 */
+	int host_rx_fifo_size;
+#define dwc_param_host_rx_fifo_size_default		1024
+
+	/*
+	 * Number of 4-byte words in the non-periodic Tx FIFO in host mode
+	 * when Dynamic FIFO sizing is enabled in the core.  16 to 32768
+	 * (default 1024)
+	 */
+	int host_nperio_tx_fifo_size;
+#define dwc_param_host_nperio_tx_fifo_size_default	1024
+
+	/*
+	   Number of 4-byte words in the host periodic Tx FIFO when dynamic
+	   * FIFO sizing is enabled.  16 to 32768 (default 1024)
+	 */
+	int host_perio_tx_fifo_size;
+#define dwc_param_host_perio_tx_fifo_size_default	1024
+
+	/*
+	 * The maximum transfer size supported in bytes. 2047 to 65,535
+	 * (default 65,535)
+	 */
+	int max_transfer_size;
+#define dwc_param_max_transfer_size_default		65535
+
+	/*
+	 * The maximum number of packets in a transfer. 15 to 511  (default 511)
+	 */
+	int max_packet_count;
+#define dwc_param_max_packet_count_default		511
+
+	/*
+	 * The number of host channel registers to use.
+	 * 1 to 16 (default 12)
+	 * Note: The FPGA configuration supports a maximum of 12 host channels.
+	 */
+	int host_channels;
+#define dwc_param_host_channels_default			12
+
+	/*
+	 * The number of endpoints in addition to EP0 available for device
+	 * mode operations.
+	 * 1 to 15 (default 6 IN and OUT)
+	 * Note: The FPGA configuration supports a maximum of 6 IN and OUT
+	 * endpoints in addition to EP0.
+	 */
+	int dev_endpoints;
+#define dwc_param_dev_endpoints_default			6
+
+	/*
+	 * Specifies the type of PHY interface to use. By default, the driver
+	 * will automatically detect the phy_type.
+	 *
+	 *      0 - Full Speed PHY
+	 *      1 - UTMI+ (default)
+	 *      2 - ULPI
+	 */
+	int phy_type;
+#define DWC_PHY_TYPE_PARAM_FS			0
+#define DWC_PHY_TYPE_PARAM_UTMI			1
+#define DWC_PHY_TYPE_PARAM_ULPI			2
+#define dwc_param_phy_type_default		DWC_PHY_TYPE_PARAM_UTMI
+
+	/*
+	 * Specifies the UTMI+ Data Width.  This parameter is applicable for a
+	 * PHY_TYPE of UTMI+ or ULPI. (For a ULPI PHY_TYPE, this parameter
+	 * indicates the data width between the MAC and the ULPI Wrapper.) Also,
+	 * this parameter is applicable only if the OTG_HSPHY_WIDTH cC parameter
+	 * was set to "8 and 16 bits", meaning that the core has been configured
+	 * to work at either data path width.
+	 *
+	 * 8 or 16 bits (default 16)
+	 */
+	int phy_utmi_width;
+#define dwc_param_phy_utmi_width_default	16
+
+	/*
+	 * Specifies whether the ULPI operates at double or single
+	 * data rate. This parameter is only applicable if PHY_TYPE is
+	 * ULPI.
+	 *
+	 *      0 - single data rate ULPI interface with 8 bit wide data
+	 *              bus (default)
+	 *      1 - double data rate ULPI interface with 4 bit wide data
+	 *              bus
+	 */
+	int phy_ulpi_ddr;
+#define dwc_param_phy_ulpi_ddr_default		0
+
+	/*
+	 * Specifies whether to use the internal or external supply to
+	 * drive the vbus with a ULPI phy.
+	 */
+	int phy_ulpi_ext_vbus;
+#define DWC_PHY_ULPI_INTERNAL_VBUS		0
+#define DWC_PHY_ULPI_EXTERNAL_VBUS		1
+#define dwc_param_phy_ulpi_ext_vbus_default	DWC_PHY_ULPI_INTERNAL_VBUS
+
+	/*
+	 * Specifies whether to use the I2Cinterface for full speed PHY. This
+	 * parameter is only applicable if PHY_TYPE is FS.
+	 *      0 - No (default)
+	 *      1 - Yes
+	 */
+	int i2c_enable;
+#define dwc_param_i2c_enable_default		0
+
+	int ulpi_fs_ls;
+#define dwc_param_ulpi_fs_ls_default		0
+
+	int ts_dline;
+#define dwc_param_ts_dline_default		0
+
+	/*
+	 * Specifies whether dedicated transmit FIFOs are enabled for non
+	 * periodic IN endpoints in device mode
+	 *      0 - No
+	 *      1 - Yes
+	 */
+	int en_multiple_tx_fifo;
+#define dwc_param_en_multiple_tx_fifo_default	1
+
+	/*
+	 * Number of 4-byte words in each of the Tx FIFOs in device
+	 * mode when dynamic FIFO sizing is enabled. 4 to 768 (default 256)
+	 */
+	u32 dev_tx_fifo_size[MAX_TX_FIFOS];
+#define dwc_param_dev_tx_fifo_size_default	256
+
+	/*
+	 * Thresholding enable flag
+	 *      bit 0 - enable non-ISO Tx thresholding
+	 *      bit 1 - enable ISO Tx thresholding
+	 *      bit 2 - enable Rx thresholding
+	 */
+	u32 thr_ctl;
+#define dwc_param_thr_ctl_default		0
+
+	/* Thresholding length for Tx FIFOs in 32 bit DWORDs */
+	u32 tx_thr_length;
+#define dwc_param_tx_thr_length_default		64
+
+	/* Thresholding length for Rx FIFOs in 32 bit DWORDs */
+	u32 rx_thr_length;
+#define dwc_param_rx_thr_length_default		64
+
+};
+
+/*
+ * The core_if structure contains information needed to manage the
+ * DWC_otg controller acting in either host or device mode. It represents the
+ * programming view of the controller as a whole.
+ */
+struct core_if {
+	/* Parameters that define how the core should be configured. */
+	struct core_params *core_params;
+
+	/* Core Global registers starting at offset 000h. */
+	u32 core_global_regs;
+
+	/* Device-specific information */
+	struct device_if *dev_if;
+	/* Host-specific information */
+	struct dwc_host_if *host_if;
+
+	/*
+	 * Set to 1 if the core PHY interface bits in USBCFG have been
+	 * initialized.
+	 */
+	u8 phy_init_done;
+
+	/*
+	 * SRP Success flag, set by srp success interrupt in FS I2C mode
+	 */
+	u8 srp_success;
+	u8 srp_timer_started;
+
+	/* Common configuration information */
+	/* Power and Clock Gating Control Register */
+	u32 pcgcctl;
+#define DWC_OTG_PCGCCTL_OFFSET			0xE00
+
+	/* Push/pop addresses for endpoints or host channels. */
+	u32 *data_fifo[MAX_EPS_CHANNELS];
+#define DWC_OTG_DATA_FIFO_OFFSET		0x1000
+#define DWC_OTG_DATA_FIFO_SIZE			0x1000
+
+	/* Total RAM for FIFOs (Bytes) */
+	u16 total_fifo_size;
+	/* Size of Rx FIFO (Bytes) */
+	u16 rx_fifo_size;
+	/* Size of Non-periodic Tx FIFO (Bytes) */
+	u16 nperio_tx_fifo_size;
+
+	/* 1 if DMA is enabled, 0 otherwise. */
+	u8 dma_enable;
+
+	/* 1 if dedicated Tx FIFOs are enabled, 0 otherwise. */
+	u8 en_multiple_tx_fifo;
+
+	/*
+	 *  Set to 1 if multiple packets of a high-bandwidth transfer is in
+	 * process of being queued
+	 */
+	u8 queuing_high_bandwidth;
+
+	/* Hardware Configuration -- stored here for convenience. */
+	u32 hwcfg1;
+	u32 hwcfg2;
+	u32 hwcfg3;
+	u32 hwcfg4;
+
+	/* HCD callbacks */
+	/* include/linux/usb/otg.h */
+
+	/* HCD callbacks */
+	struct cil_callbacks *hcd_cb;
+	/* PCD callbacks */
+	struct cil_callbacks *pcd_cb;
+
+	/* Device mode Periodic Tx FIFO Mask */
+	u32 p_tx_msk;
+	/* Device mode Periodic Tx FIFO Mask */
+	u32 tx_msk;
+
+	/* Features of various DWC implementation */
+	u32 features;
+
+	/* Added to support PLB DMA : phys-virt mapping */
+	resource_size_t phys_addr;
+
+	struct delayed_work usb_port_wakeup;
+	struct work_struct usb_port_otg;
+	struct otg_transceiver *xceiv;
+};
+
+/*
+ * The following functions support initialization of the CIL driver component
+ * and the DWC_otg controller.
+ */
+extern void dwc_otg_core_init(struct core_if *core_if);
+extern void init_fslspclksel(struct core_if *core_if);
+extern void dwc_otg_core_dev_init(struct core_if *core_if);
+extern const char *op_state_str(enum usb_otg_state state);
+extern void dwc_otg_enable_global_interrupts(struct core_if *core_if);
+extern void dwc_otg_disable_global_interrupts(struct core_if *core_if);
+extern void dwc_otg_enable_common_interrupts(struct core_if *core_if);
+
+/**
+ * This function Reads HPRT0 in preparation to modify.  It keeps the WC bits 0
+ * so that if they are read as 1, they won't clear when you write it back
+ */
+static inline u32 dwc_otg_read_hprt0(struct core_if *core_if)
+{
+	u32 hprt0 = 0;
+	hprt0 = dwc_read32(core_if->host_if->hprt0);
+	hprt0 = DWC_HPRT0_PRT_ENA_RW(hprt0, 0);
+	hprt0 = DWC_HPRT0_PRT_CONN_DET_RW(hprt0, 0);
+	hprt0 = DWC_HPRT0_PRT_ENA_DIS_CHG_RW(hprt0, 0);
+	hprt0 = DWC_HPRT0_PRT_OVRCURR_ACT_RW(hprt0, 0);
+	return hprt0;
+}
+
+/*
+ * The following functions support managing the DWC_otg controller in either
+ * device or host mode.
+ */
+extern void dwc_otg_read_packet(struct core_if *core_if, u8 * dest, u16 bytes);
+extern void dwc_otg_flush_tx_fifo(struct core_if *core_if, const int _num);
+extern void dwc_otg_flush_rx_fifo(struct core_if *core_if);
+
+#define NP_TXFIFO_EMPTY			-1
+#define MAX_NP_TXREQUEST_Q_SLOTS	8
+
+/**
+ * This function returns the Core Interrupt register.
+ */
+static inline u32 dwc_otg_read_core_intr(struct core_if *core_if)
+{
+	u32 global_regs = (u32) core_if->core_global_regs;
+	return dwc_read32(global_regs + DWC_GINTSTS) &
+	    dwc_read32(global_regs + DWC_GINTMSK);
+}
+
+/**
+ * This function returns the mode of the operation, host or device.
+ */
+static inline u32 dwc_otg_mode(struct core_if *core_if)
+{
+	u32 global_regs = (u32) core_if->core_global_regs;
+	return dwc_read32(global_regs + DWC_GINTSTS) & 0x1;
+}
+
+static inline u8 dwc_otg_is_device_mode(struct core_if *core_if)
+{
+	return dwc_otg_mode(core_if) != DWC_HOST_MODE;
+}
+static inline u8 dwc_otg_is_host_mode(struct core_if *core_if)
+{
+	return dwc_otg_mode(core_if) == DWC_HOST_MODE;
+}
+
+extern int dwc_otg_handle_common_intr(struct core_if *core_if);
+
+/*
+ * DWC_otg CIL callback structure.  This structure allows the HCD and PCD to
+ * register functions used for starting and stopping the PCD and HCD for role
+ * change on for a DRD.
+ */
+struct cil_callbacks {
+	/* Start function for role change */
+	int (*start) (void *_p);
+	/* Stop Function for role change */
+	int (*stop) (void *_p);
+	/* Disconnect Function for role change */
+	int (*disconnect) (void *_p);
+	/* Resume/Remote wakeup Function */
+	int (*resume_wakeup) (void *_p);
+	/* Suspend function */
+	int (*suspend) (void *_p);
+	/* Session Start (SRP) */
+	int (*session_start) (void *_p);
+	/* Pointer passed to start() and stop() */
+	void *p;
+};
+
+extern void dwc_otg_cil_register_pcd_callbacks(struct core_if *core_if,
+					       struct cil_callbacks *cb,
+					       void *p);
+extern void dwc_otg_cil_register_hcd_callbacks(struct core_if *core_if,
+					       struct cil_callbacks *cb,
+					       void *p);
+
+#define DWC_LIMITED_XFER		0x00000000
+#define DWC_DEVICE_ONLY			0x00000000
+#define DWC_HOST_ONLY			0x00000000
+
+#ifdef CONFIG_DWC_LIMITED_XFER_SIZE
+#undef DWC_LIMITED_XFER
+#define DWC_LIMITED_XFER		0x00000001
+#endif
+
+#ifdef CONFIG_DWC_DEVICE_ONLY
+#undef DWC_DEVICE_ONLY
+#define DWC_DEVICE_ONLY			0x00000002
+static inline void dwc_otg_hcd_remove(struct device *dev)
+{
+}
+static inline int dwc_otg_hcd_init(struct device *_dev,
+				   struct dwc_otg_device *dwc_dev)
+{
+	return 0;
+}
+#else
+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);
+#endif
+
+#ifdef CONFIG_DWC_HOST_ONLY
+#undef DWC_HOST_ONLY
+#define DWC_HOST_ONLY			0x00000004
+static inline void dwc_otg_pcd_remove(struct device *dev)
+{
+}
+static inline int dwc_otg_pcd_init(struct device *dev)
+{
+	return 0;
+}
+#else
+extern void dwc_otg_pcd_remove(struct device *dev);
+extern int __init dwc_otg_pcd_init(struct device *dev);
+#endif
+
+extern void dwc_otg_cil_remove(struct core_if *core_if);
+extern struct core_if __devinit *dwc_otg_cil_init(const __iomem u32 * base,
+						  struct core_params *params);
+
+static inline void dwc_set_feature(struct core_if *core_if)
+{
+	core_if->features = DWC_LIMITED_XFER | DWC_DEVICE_ONLY | DWC_HOST_ONLY;
+}
+
+static inline int dwc_has_feature(struct core_if *core_if,
+				  unsigned long feature)
+{
+	return core_if->features & feature;
+}
+extern struct core_params dwc_otg_module_params;
+extern int __devinit check_parameters(struct core_if *core_if);
+#endif
diff --git a/drivers/usb/dwc_otg/dwc_otg_cil_intr.c b/drivers/usb/dwc_otg/dwc_otg_cil_intr.c
new file mode 100644
index 0000000..e4dbd62
--- /dev/null
+++ b/drivers/usb/dwc_otg/dwc_otg_cil_intr.c
@@ -0,0 +1,616 @@
+/*
+ * 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 Core Interface Layer provides basic services for accessing and
+ * managing the DWC_otg hardware. These services are used by both the
+ * Host Controller Driver and the Peripheral Controller Driver.
+ *
+ * This file contains the Common Interrupt handlers.
+ */
+#include <linux/delay.h>
+
+#include "dwc_otg_cil.h"
+
+/**
+ *  This function will log a debug message
+ */
+static int dwc_otg_handle_mode_mismatch_intr(struct core_if *core_if)
+{
+	u32 gintsts = 0;
+	u32 global_regs = (u32) core_if->core_global_regs;
+
+	pr_warning("Mode Mismatch Interrupt: currently in %s mode\n",
+		   dwc_otg_mode(core_if) ? "Host" : "Device");
+
+	/* Clear interrupt */
+	gintsts |= DWC_INTSTS_MODE_MISMTC;
+	dwc_write32(global_regs + DWC_GINTSTS, gintsts);
+
+	return 1;
+}
+
+/**
+ *  Start the HCD.  Helper function for using the HCD callbacks.
+ */
+static inline void hcd_start(struct core_if *core_if)
+{
+	if (core_if->hcd_cb && core_if->hcd_cb->start)
+		core_if->hcd_cb->start(core_if->hcd_cb->p);
+}
+
+/**
+ *  Stop the HCD.  Helper function for using the HCD callbacks.
+ */
+static inline void hcd_stop(struct core_if *core_if)
+{
+	if (core_if->hcd_cb && core_if->hcd_cb->stop)
+		core_if->hcd_cb->stop(core_if->hcd_cb->p);
+}
+
+/**
+ *  Disconnect the HCD.  Helper function for using the HCD callbacks.
+ */
+static inline void hcd_disconnect(struct core_if *core_if)
+{
+	if (core_if->hcd_cb && core_if->hcd_cb->disconnect)
+		core_if->hcd_cb->disconnect(core_if->hcd_cb->p);
+}
+
+/**
+ *  Inform the HCD the a New Session has begun.  Helper function for using the
+ *  HCD callbacks.
+ */
+static inline void hcd_session_start(struct core_if *core_if)
+{
+	if (core_if->hcd_cb && core_if->hcd_cb->session_start)
+		core_if->hcd_cb->session_start(core_if->hcd_cb->p);
+}
+
+/**
+ *  Start the PCD.  Helper function for using the PCD callbacks.
+ */
+static inline void pcd_start(struct core_if *core_if)
+{
+	if (core_if->pcd_cb && core_if->pcd_cb->start) {
+		struct dwc_pcd *pcd;
+
+		pcd = (struct dwc_pcd *)core_if->pcd_cb->p;
+		spin_lock(&pcd->lock);
+		core_if->pcd_cb->start(core_if->pcd_cb->p);
+		spin_unlock(&pcd->lock);
+	}
+}
+
+/**
+ *  Stop the PCD.  Helper function for using the PCD callbacks.
+ */
+static inline void pcd_stop(struct core_if *core_if)
+{
+	if (core_if->pcd_cb && core_if->pcd_cb->stop) {
+		struct dwc_pcd *pcd;
+
+		pcd = (struct dwc_pcd *)core_if->pcd_cb->p;
+		spin_lock(&pcd->lock);
+		core_if->pcd_cb->stop(core_if->pcd_cb->p);
+		spin_unlock(&pcd->lock);
+	}
+}
+
+/**
+ *  Suspend the PCD.  Helper function for using the PCD callbacks.
+ */
+static inline void pcd_suspend(struct core_if *core_if)
+{
+	if (core_if->pcd_cb && core_if->pcd_cb->suspend) {
+		struct dwc_pcd *pcd;
+
+		pcd = (struct dwc_pcd *)core_if->pcd_cb->p;
+		spin_lock(&pcd->lock);
+		core_if->pcd_cb->suspend(core_if->pcd_cb->p);
+		spin_unlock(&pcd->lock);
+	}
+}
+
+/**
+ *  Resume the PCD.  Helper function for using the PCD callbacks.
+ */
+static inline void pcd_resume(struct core_if *core_if)
+{
+	if (core_if->pcd_cb && core_if->pcd_cb->resume_wakeup) {
+		struct dwc_pcd *pcd;
+
+		pcd = (struct dwc_pcd *)core_if->pcd_cb->p;
+		spin_lock(&pcd->lock);
+		core_if->pcd_cb->resume_wakeup(core_if->pcd_cb->p);
+		spin_unlock(&pcd->lock);
+	}
+}
+
+/**
+ * This function handles the OTG Interrupts. It reads the OTG
+ * Interrupt Register (GOTGINT) to determine what interrupt has
+ * occurred.
+ */
+static int dwc_otg_handle_otg_intr(struct core_if *core_if)
+{
+	u32 global_regs = (u32) core_if->core_global_regs;
+	u32 gotgint;
+	u32 gotgctl;
+	u32 gintmsk;
+
+	gotgint = dwc_read32(global_regs + DWC_GOTGINT);
+	if (gotgint & DWC_GINT_SES_ENDDET) {
+		gotgctl = dwc_read32(global_regs + DWC_GOTGCTL);
+		if (core_if->xceiv->state == OTG_STATE_B_HOST) {
+			pcd_start(core_if);
+			core_if->xceiv->state = OTG_STATE_B_PERIPHERAL;
+		} else {
+			/*
+			 * If not B_HOST and Device HNP still set. HNP did not
+			 * succeed
+			 */
+			if (gotgctl & DWC_GCTL_DEV_HNP_ENA)
+				pr_err("Device Not Connected / "
+				       "Responding\n");
+			/*
+			 * If Session End Detected the B-Cable has been
+			 * disconnected.  Reset PCD and Gadget driver to a
+			 * clean state.
+			 */
+			pcd_stop(core_if);
+		}
+		gotgctl = 0;
+		gotgctl |= DWC_GCTL_DEV_HNP_ENA;
+		dwc_modify32(global_regs + DWC_GOTGCTL, gotgctl, 0);
+	}
+	if (gotgint & DWC_GINT_SES_REQSUC) {
+		gotgctl = dwc_read32(global_regs + DWC_GOTGCTL);
+		if (gotgctl & DWC_GCTL_SES_REQ_SUCCESS) {
+			if (core_if->core_params->phy_type ==
+			    DWC_PHY_TYPE_PARAM_FS &&
+			    core_if->core_params->i2c_enable) {
+				core_if->srp_success = 1;
+			} else {
+				pcd_resume(core_if);
+
+				/* Clear Session Request */
+				gotgctl = 0;
+				gotgctl |= DWC_GCTL_SES_REQ;
+				dwc_modify32(global_regs + DWC_GOTGCTL,
+					     gotgctl, 0);
+			}
+		}
+	}
+	if (gotgint & DWC_GINT_HST_NEGSUC) {
+		/*
+		 * Print statements during the HNP interrupt handling can cause
+		 * it to fail.
+		 */
+		gotgctl = dwc_read32(global_regs + DWC_GOTGCTL);
+		if (gotgctl & DWC_GCTL_HOST_NEG_SUCCES) {
+			if (dwc_otg_is_host_mode(core_if)) {
+				core_if->xceiv->state = OTG_STATE_B_HOST;
+				/*
+				 * Need to disable SOF interrupt immediately.
+				 * When switching from device to host, the PCD
+				 * interrupt handler won't handle the
+				 * interrupt if host mode is already set. The
+				 * HCD interrupt handler won't get called if
+				 * the HCD state is HALT. This means that the
+				 * interrupt does not get handled and Linux
+				 * complains loudly.
+				 */
+				gintmsk = 0;
+				gintmsk |= DWC_INTMSK_STRT_OF_FRM;
+				dwc_modify32(global_regs + DWC_GINTMSK,
+					     gintmsk, 0);
+				pcd_stop(core_if);
+				/* Initialize the Core for Host mode. */
+				hcd_start(core_if);
+				core_if->xceiv->state = OTG_STATE_B_HOST;
+			}
+		} else {
+			gotgctl = 0;
+			gotgctl |= DWC_GCTL_HNP_REQ;
+			gotgctl |= DWC_GCTL_DEV_HNP_ENA;
+			dwc_modify32(global_regs + DWC_GOTGCTL, gotgctl, 0);
+
+			pr_err("Device Not Connected / Responding\n");
+		}
+	}
+	if (gotgint & DWC_GINT_HST_NEGDET) {
+		/*
+		 * The disconnect interrupt is set at the same time as
+		 * Host Negotiation Detected.  During the mode
+		 * switch all interrupts are cleared so the disconnect
+		 * interrupt handler will not get executed.
+		 */
+		if (dwc_otg_is_device_mode(core_if)) {
+			hcd_disconnect(core_if);
+			pcd_start(core_if);
+			core_if->xceiv->state = OTG_STATE_A_PERIPHERAL;
+		} else {
+			/*
+			 * Need to disable SOF interrupt immediately. When
+			 * switching from device to host, the PCD interrupt
+			 * handler won't handle the interrupt if host mode is
+			 * already set. The HCD interrupt handler won't get
+			 * called if the HCD state is HALT. This means that
+			 * the interrupt does not get handled and Linux
+			 * complains loudly.
+			 */
+			gintmsk = 0;
+			gintmsk |= DWC_INTMSK_STRT_OF_FRM;
+			dwc_modify32(global_regs + DWC_GINTMSK, gintmsk, 0);
+			pcd_stop(core_if);
+			hcd_start(core_if);
+			core_if->xceiv->state = OTG_STATE_A_HOST;
+		}
+	}
+	if (gotgint & DWC_GINT_DEVTOUT)
+		pr_info(" ++OTG Interrupt: A-Device Timeout " "Change++\n");
+	if (gotgint & DWC_GINT_DEBDONE)
+		pr_info(" ++OTG Interrupt: Debounce Done++\n");
+
+	/* Clear GOTGINT */
+	dwc_write32(global_regs + DWC_GOTGINT, gotgint);
+	return 1;
+}
+
+/*
+ * Wakeup Workqueue implementation
+ */
+static void port_otg_wqfunc(struct work_struct *work)
+{
+	struct core_if *core_if = container_of(work, struct core_if,
+					       usb_port_otg);
+	u32 global_regs = (u32) core_if->core_global_regs;
+	u32 count = 0;
+	u32 gotgctl;
+
+	pr_info("%s\n", __func__);
+
+	gotgctl = dwc_read32(global_regs + DWC_GOTGCTL);
+	if (gotgctl & DWC_GCTL_CONN_ID_STATUS) {
+		/*
+		 * B-Device connector (device mode) wait for switch to device
+		 * mode.
+		 */
+		while (!dwc_otg_is_device_mode(core_if) && ++count <= 10000) {
+			pr_info("Waiting for Peripheral Mode, "
+				"Mode=%s\n", dwc_otg_is_host_mode(core_if) ?
+				"Host" : "Peripheral");
+			msleep(100);
+		}
+		BUG_ON(count > 10000);
+		core_if->xceiv->state = OTG_STATE_B_PERIPHERAL;
+		dwc_otg_core_init(core_if);
+		dwc_otg_enable_global_interrupts(core_if);
+		pcd_start(core_if);
+	} else {
+		/*
+		 * A-Device connector (host mode) wait for switch to host
+		 * mode.
+		 */
+		while (!dwc_otg_is_host_mode(core_if) && ++count <= 10000) {
+			pr_info("Waiting for Host Mode, Mode=%s\n",
+				dwc_otg_is_host_mode(core_if) ?
+				"Host" : "Peripheral");
+			msleep(100);
+		}
+		BUG_ON(count > 10000);
+		core_if->xceiv->state = OTG_STATE_A_HOST;
+		dwc_otg_core_init(core_if);
+		dwc_otg_enable_global_interrupts(core_if);
+		hcd_start(core_if);
+	}
+}
+
+/**
+ * This function handles the Connector ID Status Change Interrupt.  It
+ * reads the OTG Interrupt Register (GOTCTL) to determine whether this
+ * is a Device to Host Mode transition or a Host Mode to Device
+ * Transition.
+ *
+ * This only occurs when the cable is connected/removed from the PHY
+ * connector.
+ */
+static int dwc_otg_handle_conn_id_status_change_intr(struct core_if *core_if)
+{
+	u32 gintsts = 0;
+	u32 gintmsk = 0;
+	u32 global_regs = (u32) core_if->core_global_regs;
+
+	/*
+	 * Need to disable SOF interrupt immediately. If switching from device
+	 * to host, the PCD interrupt handler won't handle the interrupt if
+	 * host mode is already set. The HCD interrupt handler won't get
+	 * called if the HCD state is HALT. This means that the interrupt does
+	 * not get handled and Linux complains loudly.
+	 */
+	gintmsk |= DWC_INTSTS_STRT_OF_FRM;
+	dwc_modify32(global_regs + DWC_GINTMSK, gintmsk, 0);
+
+	INIT_WORK(&core_if->usb_port_otg, port_otg_wqfunc);
+	schedule_work(&core_if->usb_port_otg);
+
+	/* Set flag and clear interrupt */
+	gintsts |= DWC_INTSTS_CON_ID_STS_CHG;
+	dwc_write32(global_regs + DWC_GINTSTS, gintsts);
+	return 1;
+}
+
+/**
+ * This interrupt indicates that a device is initiating the Session
+ * Request Protocol to request the host to turn on bus power so a new
+ * session can begin. The handler responds by turning on bus power. If
+ * the DWC_otg controller is in low power mode, the handler brings the
+ * controller out of low power mode before turning on bus power.
+ */
+static int dwc_otg_handle_session_req_intr(struct core_if *core_if)
+{
+	u32 gintsts = 0;
+	u32 global_regs = (u32) core_if->core_global_regs;
+
+	if (!dwc_has_feature(core_if, DWC_HOST_ONLY)) {
+		u32 hprt0;
+
+		if (dwc_otg_is_device_mode(core_if)) {
+			pr_info("SRP: Device mode\n");
+		} else {
+			pr_info("SRP: Host mode\n");
+
+			/* Turn on the port power bit. */
+			hprt0 = dwc_otg_read_hprt0(core_if);
+			hprt0 = DWC_HPRT0_PRT_PWR_RW(hprt0, 1);
+			dwc_write32(core_if->host_if->hprt0, hprt0);
+
+			/*
+			 * Start the Connection timer.
+			 * A message can be displayed,
+			 * if connect does not occur within 10 seconds.
+			 */
+			hcd_session_start(core_if);
+		}
+	}
+	/* Clear interrupt */
+	gintsts |= DWC_INTSTS_NEW_SES_DET;
+	dwc_write32(global_regs + DWC_GINTSTS, gintsts);
+	return 1;
+}
+
+/**
+ * This interrupt indicates that the DWC_otg controller has detected a
+ * resume or remote wakeup sequence. If the DWC_otg controller is in
+ * low power mode, the handler must brings the controller out of low
+ * power mode. The controller automatically begins resume
+ * signaling. The handler schedules a time to stop resume signaling.
+ */
+static int dwc_otg_handle_wakeup_detected_intr(struct core_if *core_if)
+{
+	u32 gintsts = 0;
+	struct device_if *dev_if = core_if->dev_if;
+	u32 global_regs = (u32) core_if->core_global_regs;
+
+	if (dwc_otg_is_device_mode(core_if)) {
+		u32 dctl = 0;
+
+		/* Clear the Remote Wakeup Signalling */
+		dctl = DEC_DCTL_REMOTE_WAKEUP_SIG(dctl, 1);
+		dwc_modify32((u32) dev_if->dev_global_regs + DWC_DCTL, dctl, 0);
+
+		if (core_if->pcd_cb && core_if->pcd_cb->resume_wakeup)
+			core_if->pcd_cb->resume_wakeup(core_if->pcd_cb->p);
+	} else {
+		u32 pcgcctl = 0;
+
+		/* Restart the Phy Clock */
+		pcgcctl = DWC_PCGCCTL_STOP_CLK_SET(pcgcctl);
+		dwc_modify32(core_if->pcgcctl, pcgcctl, 0);
+		schedule_delayed_work(&core_if->usb_port_wakeup, 10);
+	}
+
+	/* Clear interrupt */
+	gintsts |= DWC_INTSTS_WKP;
+	dwc_write32(global_regs + DWC_GINTSTS, gintsts);
+	return 1;
+}
+
+/**
+ * This interrupt indicates that a device has been disconnected from
+ * the root port.
+ */
+static int dwc_otg_handle_disconnect_intr(struct core_if *core_if)
+{
+	u32 gintsts = 0;
+	u32 global_regs = (u32) core_if->core_global_regs;
+
+	if (!dwc_has_feature(core_if, DWC_HOST_ONLY)) {
+		if (core_if->xceiv->state == OTG_STATE_B_HOST) {
+			hcd_disconnect(core_if);
+			pcd_start(core_if);
+			core_if->xceiv->state = OTG_STATE_B_PERIPHERAL;
+		} else if (dwc_otg_is_device_mode(core_if)) {
+			u32 gotgctl;
+
+			gotgctl = dwc_read32(global_regs + DWC_GOTGCTL);
+
+			/*
+			 * If HNP is in process, do nothing.
+			 * The OTG "Host Negotiation Detected"
+			 * interrupt will do the mode switch.
+			 * Otherwise, since we are in device mode,
+			 * disconnect and stop the HCD,
+			 * then start the PCD.
+			 */
+			if ((gotgctl) & DWC_GCTL_DEV_HNP_ENA) {
+				hcd_disconnect(core_if);
+				pcd_start(core_if);
+				core_if->xceiv->state = OTG_STATE_B_PERIPHERAL;
+			}
+		} else if (core_if->xceiv->state == OTG_STATE_A_HOST) {
+			/* A-Cable still connected but device disconnected. */
+			hcd_disconnect(core_if);
+		}
+	}
+	gintsts |= DWC_INTSTS_SES_DISCON_DET;
+	dwc_write32(global_regs + DWC_GINTSTS, gintsts);
+	return 1;
+}
+
+/**
+ * This interrupt indicates that SUSPEND state has been detected on
+ * the USB.
+ *
+ * For HNP the USB Suspend interrupt signals the change from
+ * "a_peripheral" to "a_host".
+ *
+ * When power management is enabled the core will be put in low power
+ * mode.
+ */
+static int dwc_otg_handle_usb_suspend_intr(struct core_if *core_if)
+{
+	u32 dsts = 0;
+	u32 gintsts = 0;
+	u32 global_regs = (u32) core_if->core_global_regs;
+	struct device_if *dev_if = core_if->dev_if;
+
+	if (dwc_otg_is_device_mode(core_if)) {
+		/*
+		 * Check the Device status register to determine if the Suspend
+		 * state is active.
+		 */
+		dsts = dwc_read32((u32) dev_if->dev_global_regs + DWC_DSTS);
+		/* PCD callback for suspend. */
+		pcd_suspend(core_if);
+	} else {
+		if (core_if->xceiv->state == OTG_STATE_A_PERIPHERAL) {
+			/* Clear the a_peripheral flag, back to a_host. */
+			pcd_stop(core_if);
+			hcd_start(core_if);
+			core_if->xceiv->state = OTG_STATE_A_HOST;
+		}
+	}
+
+	/* Clear interrupt */
+	gintsts |= DWC_INTMSK_USB_SUSP;
+	dwc_write32(global_regs + DWC_GINTSTS, gintsts);
+	return 1;
+}
+
+/**
+ * This function returns the Core Interrupt register.
+ *
+ * Although the Host Port interrupt (portintr) is documented as host mode
+ * only, it appears to occur in device mode when Port Enable / Disable Changed
+ * bit in HPRT0 is set. The code in dwc_otg_handle_common_intr checks if in
+ * device mode and just clears the interrupt.
+ */
+static inline u32 dwc_otg_read_common_intr(struct core_if *core_if)
+{
+	u32 gintsts;
+	u32 gintmsk;
+	u32 gintmsk_common = 0;
+	u32 global_regs = (u32) core_if->core_global_regs;
+
+	gintmsk_common |= DWC_INTMSK_WKP;
+	gintmsk_common |= DWC_INTMSK_NEW_SES_DET;
+	gintmsk_common |= DWC_INTMSK_CON_ID_STS_CHG;
+	gintmsk_common |= DWC_INTMSK_OTG;
+	gintmsk_common |= DWC_INTMSK_MODE_MISMTC;
+	gintmsk_common |= DWC_INTMSK_SES_DISCON_DET;
+	gintmsk_common |= DWC_INTMSK_USB_SUSP;
+	gintmsk_common |= DWC_INTMSK_HST_PORT;
+
+	gintsts = dwc_read32(global_regs + DWC_GINTSTS);
+	gintmsk = dwc_read32(global_regs + DWC_GINTMSK);
+
+	return (gintsts & gintmsk) & gintmsk_common;
+}
+
+/**
+ * Common interrupt handler.
+ *
+ * The common interrupts are those that occur in both Host and Device mode.
+ * This handler handles the following interrupts:
+ * - Mode Mismatch Interrupt
+ * - Disconnect Interrupt
+ * - OTG Interrupt
+ * - Connector ID Status Change Interrupt
+ * - Session Request Interrupt.
+ * - Resume / Remote Wakeup Detected Interrupt.
+ *
+ * - Host Port Interrupt.  Although this interrupt is documented as only
+ *   occurring in Host mode, it also occurs in Device mode when Port Enable /
+ *   Disable Changed bit in HPRT0 is set. If it is seen here, while in Device
+ *   mode, the interrupt is just cleared.
+ *
+ */
+int dwc_otg_handle_common_intr(struct core_if *core_if)
+{
+	int retval = 0;
+	u32 gintsts;
+	u32 global_regs = (u32) core_if->core_global_regs;
+
+	gintsts = dwc_otg_read_common_intr(core_if);
+
+	if (gintsts & DWC_INTSTS_MODE_MISMTC)
+		retval |= dwc_otg_handle_mode_mismatch_intr(core_if);
+	if (gintsts & DWC_INTSTS_OTG)
+		retval |= dwc_otg_handle_otg_intr(core_if);
+	if (gintsts & DWC_INTSTS_CON_ID_STS_CHG)
+		retval |= dwc_otg_handle_conn_id_status_change_intr(core_if);
+	if (gintsts & DWC_INTSTS_SES_DISCON_DET)
+		retval |= dwc_otg_handle_disconnect_intr(core_if);
+	if (gintsts & DWC_INTSTS_NEW_SES_DET)
+		retval |= dwc_otg_handle_session_req_intr(core_if);
+	if (gintsts & DWC_INTSTS_WKP)
+		retval |= dwc_otg_handle_wakeup_detected_intr(core_if);
+	if (gintsts & DWC_INTMSK_USB_SUSP)
+		retval |= dwc_otg_handle_usb_suspend_intr(core_if);
+
+	if ((gintsts & DWC_INTSTS_HST_PORT) &&
+			dwc_otg_is_device_mode(core_if)) {
+		gintsts = 0;
+		gintsts |= DWC_INTSTS_HST_PORT;
+		dwc_write32(global_regs + DWC_GINTSTS, gintsts);
+		retval |= 1;
+		pr_info("RECEIVED PORTINT while in Device mode\n");
+	}
+
+	return retval;
+}
-- 
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