LinuxPPC-Dev Archive on lore.kernel.org
 help / color / mirror / Atom feed
* Re: [PATCH] powerpc/85xx: clean up FPGA device tree nodes for Freecsale QorIQ boards
From: Tabi Timur-B04825 @ 2011-08-31  2:51 UTC (permalink / raw)
  To: Gala Kumar-B11780
  Cc: linuxppc-dev@ozlabs.org, devicetree-discuss@lists.ozlabs.org
In-Reply-To: <3F34FBCD-3F0B-4D59-A947-C16CC812B42E@freescale.com>

Kumar Gala wrote:
> Do we really want the generic "fsl,fpga-cpld"&  "fsl,fpga-pixis"?  This s=
eems to vague.

Yes, the PIXIS is generally register compatible across all boards.  There=20
may be some minor differences, but the OCM driver can work with all PIXIS=20
boards that have an PIXIS.

>  Also you need "pixis" on MPC8544DS, MPC8572DS, MPC8536DS.

Ok.

> Also fpga-cpld makes no sense if we keep this.  You are either a CPLD or =
FPGA not both.

Then I don't understand what the CPLD is.

--=20
Timur Tabi
Linux kernel developer at Freescale=

^ permalink raw reply

* Re: [PATCH] powerpc/85xx: clean up FPGA device tree nodes for Freecsale QorIQ boards
From: Kumar Gala @ 2011-08-31  2:26 UTC (permalink / raw)
  To: Timur Tabi; +Cc: linuxppc-dev, devicetree-discuss
In-Reply-To: <1314644999-16955-1-git-send-email-timur@freescale.com>


On Aug 29, 2011, at 2:09 PM, Timur Tabi wrote:

> Standarize and document the FPGA nodes used on Freescale QorIQ =
reference
> boards.  There are three kinds of FPGAs used on the boards: pixis, =
qixis, and
> cpld.  Although their are minor differences among the boards that have =
one
> kind of FPGA, most of the functionality is the same, so it makes sense
> to create common compatibility strings.
>=20
> Signed-off-by: Timur Tabi <timur@freescale.com>
> ---
>=20
> Changes for other Freescale boards will be made in future patches.
>=20
> .../devicetree/bindings/powerpc/fsl/board.txt      |   30 =
++++++++++++--------
> arch/powerpc/boot/dts/p1010rdb.dts                 |   10 ++----
> arch/powerpc/boot/dts/p1020rdb.dts                 |    7 ++++-
> arch/powerpc/boot/dts/p1022ds.dts                  |    2 +-
> arch/powerpc/boot/dts/p2020ds.dts                  |    5 +++
> arch/powerpc/boot/dts/p2020rdb.dts                 |    4 ++
> arch/powerpc/boot/dts/p2040rdb.dts                 |    8 ++++-
> arch/powerpc/boot/dts/p3041ds.dts                  |    4 +-
> arch/powerpc/boot/dts/p4080ds.dts                  |    8 ++++-
> arch/powerpc/boot/dts/p5020ds.dts                  |    4 +-
> 10 files changed, 55 insertions(+), 27 deletions(-)

Do we really want the generic "fsl,fpga-cpld" & "fsl,fpga-pixis"?  This =
seems to vague.  Also you need "pixis" on MPC8544DS, MPC8572DS, =
MPC8536DS.

Also fpga-cpld makes no sense if we keep this.  You are either a CPLD or =
FPGA not both.

- k=

^ permalink raw reply

* Re: Virtual addresses in arch/powerpc/boot/dts/tqm8548*.dts ?
From: Paul Walmsley @ 2011-08-31  1:55 UTC (permalink / raw)
  To: Gary Thomas; +Cc: Dmitry Eremin-Solenikov, devicetree-discuss, linuxppc-dev
In-Reply-To: <4E5D668B.1060609@mlbassoc.com>

On Tue, 30 Aug 2011, Gary Thomas wrote:

> On 2011-08-30 15:43, Paul Walmsley wrote:
> 
> > So are these addresses virtual?  My (perhaps incorrect) understanding of
> > the device tree files was that they were intended to describe the physical
> > memory map, rather than the virtual memory map.  Or does this PowerPC
> > variant have the ability to dynamically change its own physical address
> > decoding?  Or is something else going on?
> 
> These addresses correspond to the internal registers which can be moved. 
> The default address is set by hardware at reset time (check out the 
> documentation on the hardware reset word).  Obviously one board is 
> strapped for the IMMR at one address, another board at a different 
> address.  I almost always configure my boards to use 0xF0000000 for the 
> IMMR.

Got it.  Thanks Gary.


- Paul

^ permalink raw reply

* Re: [PATCH 00/14] Modifications for DWC OTG since v13
From: Kyungmin Park @ 2011-08-31  1:18 UTC (permalink / raw)
  To: Pratyush Anand
  Cc: Pratyush Anand, viresh.kumar, vipulkumar.samar, bhupesh.sharma,
	Tirumala Marri, linux-usb, vipin.kumar, shiraz.hashim, Amit.VIRDI,
	rajeev-dlh.kumar, Mark Miesfeld, deepak.sikri, linuxppc-dev,
	Fushen Chen
In-Reply-To: <CAHM4w1nWTxfMTSTbmt4vnWAKhj4JisJf1f0SjirOt7Z9vjORoA@mail.gmail.com>

On Wed, Aug 31, 2011 at 12:46 AM, Pratyush Anand
<pratyush.anand@gmail.com> wrote:
> On Tue, Aug 30, 2011 at 8:57 PM, Tirumala Marri <tmarri@apm.com> wrote:
>> <-----Original Message-----
>> <From: Pratyush Anand [mailto:pratyush.anand@st.com]
>> <Sent: Tuesday, August 30, 2011 4:58 AM
>> <To: linux-usb@vger.kernel.org
>> <Cc: tmarri@apm.com; linuxppc-dev@lists.ozlabs.org; fchen@apm.com;
>> <mmiesfeld@apm.com; shiraz.hashim@st.com; deepak.sikri@st.com;
>> <vipulkumar.samar@st.com; rajeev-dlh.kumar@st.com; vipin.kumar@st.com;
>> <bhupesh.sharma@st.com; viresh.kumar@st.com; Amit.VIRDI@st.com; Pratyush
>> <Anand
>> <Subject: [PATCH 00/14] Modifications for DWC OTG since v13
>> <
>> <These patches are based on:http://patchwork.ozlabs.org/patch/89560/
>> <After not getting any reply from developers, I started to do
>> <modifications for my platform (SPEAr1340).
>> <I have done modifications in such a way that all the code in
>> <driver/usb/dwc/ would be platform independent.
>> <I have tested this code for host/device/dma/slave mode.
>> <My fifo configuration is dedicated and dynamic.
>>
>> [Tirumala Marri] We are working on our next release of patches. They
>> should be coming out soon.
>
> Oh, thats great !!
> Actually, I did not get any reply of my previous mail on your patch relea=
se.
> I thought no one is maintaining., and so I sent them with my modification=
s,
> after testing them in all dev/host/otg mode.
>
> Regards
> Pratyush

Hi,

Can you provide the git repo to test?
and I wonder what's the difference between dwc (from you) and dwc3
(from Felipe Balbi). I think it's dwc targets for usb 2.0 and dwc3 for
usb 3.0.

Thank you,
Kyungmin Park
>
>> --
>> To unsubscribe from this list: send the line "unsubscribe linux-usb" in
>> the body of a message to majordomo@vger.kernel.org
>> More majordomo info at =A0http://vger.kernel.org/majordomo-info.html
>>
> --
> To unsubscribe from this list: send the line "unsubscribe linux-usb" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at =A0http://vger.kernel.org/majordomo-info.html
>

^ permalink raw reply

* Re: [PATCH 2/2] [PowerPC Book3E] Introduce new ptrace debug feature flag
From: Thiago Jung Bauermann @ 2011-08-31  0:27 UTC (permalink / raw)
  To: David Gibson; +Cc: linuxppc-dev, K.Prasad, Edjunior Barbosa Machado
In-Reply-To: <20110826044123.GI2308@yookeroo.fritz.box>

On Fri, 2011-08-26 at 14:41 +1000, David Gibson wrote:
> On Wed, Aug 24, 2011 at 09:41:43PM -0300, Thiago Jung Bauermann wrote:
> > On Wed, 2011-08-24 at 14:00 +1000, David Gibson wrote:
> > > On Tue, Aug 23, 2011 at 02:57:56PM +0530, K.Prasad wrote:
> > > > On Tue, Aug 23, 2011 at 03:09:31PM +1000, David Gibson wrote:
> > > > > On Fri, Aug 19, 2011 at 01:23:38PM +0530, K.Prasad wrote:
> > > > > > 
> > > > > > While PPC_PTRACE_SETHWDEBUG ptrace flag in PowerPC accepts
> > > > > > PPC_BREAKPOINT_MODE_EXACT mode of breakpoint, the same is not intimated to the
> > > > > > user-space debuggers (like GDB) who may want to use it. Hence we introduce a
> > > > > > new PPC_DEBUG_FEATURE_DATA_BP_EXACT flag which will be populated on the
> > > > > > "features" member of "struct ppc_debug_info" to advertise support for the
> > > > > > same on Book3E PowerPC processors.
> > > > > 
> > > > > I thought the idea was that the BP_EXACT mode was the default - if the
> > > > > new interface was supported at all, then BP_EXACT was always
> > > > > supported.  So, why do you need a new flag?
> > > > > 
> > > > 
> > > > Yes, BP_EXACT was always supported but not advertised through
> > > > PPC_PTRACE_GETHWDBGINFO. We're now doing that.
> > > 
> > > I can see that.  But you haven't answered why.
> > 
> > BookS doesn't support BP_EXACT, that's why I suggested this flag.
> 
> Surely you can support it with exactly the same sort of filtering
> you're using for the 8-byte ranges now?

Yes, but to detect that the processor doesn't support BP_EXACT in
hardware I'd have to send a ptrace request, and have it rejected. Only
then I'd step back and simulate one with ranges. Considering that it's
easy and backwards compatible to add a new flag to signal that BP_EXACT
is not supported, I don't know why it would be better to go with the
more convoluted process.

-- 
[]'s
Thiago Jung Bauermann
IBM Linux Technology Center

^ permalink raw reply

* Re: Virtual addresses in arch/powerpc/boot/dts/tqm8548*.dts ?
From: Gary Thomas @ 2011-08-30 22:39 UTC (permalink / raw)
  To: Paul Walmsley; +Cc: Dmitry Eremin-Solenikov, devicetree-discuss, linuxppc-dev
In-Reply-To: <alpine.DEB.2.00.1108301508130.26173@utopia.booyaka.com>

On 2011-08-30 15:43, Paul Walmsley wrote:
>
> Hi,
>
> Looking at some of the PPC DTS files in arch/powerpc/boot/dts, there are
> some files that are mostly identical, except for some strange
> differences.
>
> For example, tqm8548.dts and tqm8548-bigflash.dts differ mostly in that
> the former file claims that the SoC registers start at 0xe0000000[1], but
> the latter file claims that the SoC registers start at 0xa0000000[2].
>
> Commit 02b8a3d1eb2ae6353cfbce627ded22e299cf1989 ("powerpc/85xx: support
> for the TQM8548 module using the big Flash") claims that:
>
>      Some TQM85xx boards could be equipped with up to 1 GiB (NOR) flash
>      memory and therefore a modified memory map is required and setup by
>      the board loader. This patch adds an appropriate DTS file.
>
> So are these addresses virtual?  My (perhaps incorrect) understanding of
> the device tree files was that they were intended to describe the physical
> memory map, rather than the virtual memory map.  Or does this PowerPC
> variant have the ability to dynamically change its own physical address
> decoding?  Or is something else going on?

These addresses correspond to the internal registers which can be moved.
The default address is set by hardware at reset time (check out the documentation
on the hardware reset word).  Obviously one board is strapped for the IMMR
at one address, another board at a different address.  I almost always configure
my boards to use 0xF0000000 for the IMMR.

>
>
> thanks for any clarification,
>
> - Paul
>
> 1. arch/powerpc/boot/dts/tqm8548.dts line 53, as of Linux v3.1-rc3:
>     http://git.kernel.org/?p=linux/kernel/git/torvalds/linux-2.6.git;a=blob;f=arch/powerpc/boot/dts/tqm8548.dts;h=619776f72c904c611e9507d44db4bee1200e6688;hb=HEAD#l53
>
> 2. arch/powerpc/boot/dts/tqm8548-bigflash.dts line 53, as of Linux
>     v3.1-rc3:
>     http://git.kernel.org/?p=linux/kernel/git/torvalds/linux-2.6.git;a=blob;f=arch/powerpc/boot/dts/tqm8548-bigflash.dts;h=9452c3c05114e523033eebb278d7f78811890a87;hb=HEAD#l53
> _______________________________________________
> Linuxppc-dev mailing list
> Linuxppc-dev@lists.ozlabs.org
> https://lists.ozlabs.org/listinfo/linuxppc-dev

-- 
------------------------------------------------------------
Gary Thomas                 |  Consulting for the
MLB Associates              |    Embedded world
------------------------------------------------------------

^ permalink raw reply

* Re: [PATCH] powerpc/fsl_msi: clean up and document calculation of MSIIR address
From: Tabi Timur-B04825 @ 2011-08-30 22:24 UTC (permalink / raw)
  To: Gala Kumar-B11780; +Cc: linuxppc-dev@ozlabs.org
In-Reply-To: <1314404912-6947-1-git-send-email-timur@freescale.com>

Timur Tabi wrote:
> Commit 3da34aae (powerpc/fsl: Support unique MSI addresses per PCIe Root
> Complex) redefined the meanings of msi->msi_addr_hi and msi->msi_addr_lo =
to be
> an offset rather than an address.  To help clarify the code, we make the
> following changes:

Kumar, I'm going to merge this patch into a bigger patch that makes a=20
bunch of other fixes.=

^ permalink raw reply

* Virtual addresses in arch/powerpc/boot/dts/tqm8548*.dts ?
From: Paul Walmsley @ 2011-08-30 21:43 UTC (permalink / raw)
  To: Dmitry Eremin-Solenikov, Kumar Gala, Wolfgang Grandegger
  Cc: devicetree-discuss, linuxppc-dev


Hi,

Looking at some of the PPC DTS files in arch/powerpc/boot/dts, there are 
some files that are mostly identical, except for some strange 
differences.

For example, tqm8548.dts and tqm8548-bigflash.dts differ mostly in that 
the former file claims that the SoC registers start at 0xe0000000[1], but 
the latter file claims that the SoC registers start at 0xa0000000[2].

Commit 02b8a3d1eb2ae6353cfbce627ded22e299cf1989 ("powerpc/85xx: support 
for the TQM8548 module using the big Flash") claims that:

    Some TQM85xx boards could be equipped with up to 1 GiB (NOR) flash
    memory and therefore a modified memory map is required and setup by
    the board loader. This patch adds an appropriate DTS file.

So are these addresses virtual?  My (perhaps incorrect) understanding of 
the device tree files was that they were intended to describe the physical 
memory map, rather than the virtual memory map.  Or does this PowerPC 
variant have the ability to dynamically change its own physical address 
decoding?  Or is something else going on?


thanks for any clarification,

- Paul

1. arch/powerpc/boot/dts/tqm8548.dts line 53, as of Linux v3.1-rc3:
   http://git.kernel.org/?p=linux/kernel/git/torvalds/linux-2.6.git;a=blob;f=arch/powerpc/boot/dts/tqm8548.dts;h=619776f72c904c611e9507d44db4bee1200e6688;hb=HEAD#l53

2. arch/powerpc/boot/dts/tqm8548-bigflash.dts line 53, as of Linux 
   v3.1-rc3:
   http://git.kernel.org/?p=linux/kernel/git/torvalds/linux-2.6.git;a=blob;f=arch/powerpc/boot/dts/tqm8548-bigflash.dts;h=9452c3c05114e523033eebb278d7f78811890a87;hb=HEAD#l53

^ permalink raw reply

* [PATCH] Implement CONFIG_STRICT_DEVMEM support for Powerpc
From: Sukadev Bhattiprolu @ 2011-08-30 19:19 UTC (permalink / raw)
  To: benh; +Cc: scottwood, linuxppc-dev, sbest, paulus

>From 9d899c6bcb685afc58245f1fcfe5de1e8b499856 Mon Sep 17 00:00:00 2001
From: Sukadev Bhattiprolu <sukadev@linux.vnet.ibm.com>
Date: Mon, 29 Aug 2011 14:12:08 -0700
Subject: [PATCH 1/1] Implement CONFIG_STRICT_DEVMEM support for Powerpc.

As described in the help text in the patch, this token restricts general
access to /dev/mem as a way of increasing the security. Specifically, access
to exclusive IOMEM and kernel RAM is denied unless CONFIG_STRICT_DEVMEM is
set to 'n'.

Implement the 'devmem_is_allowed()' interface for Powerpc. It will be
called from range_is_allowed() when userpsace attempts to access /dev/mem.

This patch is based on an earlier patch from Steve Best and with input from
Paul Mackerras and Scott Wood.

Signed-off-by: Sukadev Bhattiprolu <sukadev@linux.vnet.ibm.com>
---
 arch/powerpc/Kconfig.debug      |   12 ++++++++++++
 arch/powerpc/include/asm/page.h |    1 +
 arch/powerpc/mm/mem.c           |   16 ++++++++++++++++
 drivers/char/mem.c              |    4 ++--
 4 files changed, 31 insertions(+), 2 deletions(-)

diff --git a/arch/powerpc/Kconfig.debug b/arch/powerpc/Kconfig.debug
index 067cb84..1cf1b00 100644
--- a/arch/powerpc/Kconfig.debug
+++ b/arch/powerpc/Kconfig.debug
@@ -298,4 +298,16 @@ config PPC_EARLY_DEBUG_CPM_ADDR
 	  platform probing is done, all platforms selected must
 	  share the same address.
 
+config STRICT_DEVMEM
+	def_bool y
+	prompt "Filter access to /dev/mem"
+	help
+	  This option restricts access to /dev/mem.  If this option is
+	  disabled, you allow userspace access to all memory, including
+	  kernel and userspace memory. Accidental memory access is likely
+	  to be disastrous.
+	  Memory access is required for experts who want to debug the kernel.
+
+	  If you are unsure, say Y.
+
 endmenu
diff --git a/arch/powerpc/include/asm/page.h b/arch/powerpc/include/asm/page.h
index 2cd664e..9eac49e 100644
--- a/arch/powerpc/include/asm/page.h
+++ b/arch/powerpc/include/asm/page.h
@@ -261,6 +261,7 @@ extern void clear_user_page(void *page, unsigned long vaddr, struct page *pg);
 extern void copy_user_page(void *to, void *from, unsigned long vaddr,
 		struct page *p);
 extern int page_is_ram(unsigned long pfn);
+extern int devmem_is_allowd(unsigned long pfn);
 
 #ifdef CONFIG_PPC_SMLPAR
 void arch_free_page(struct page *page, int order);
diff --git a/arch/powerpc/mm/mem.c b/arch/powerpc/mm/mem.c
index c781bbc..bb7537d 100644
--- a/arch/powerpc/mm/mem.c
+++ b/arch/powerpc/mm/mem.c
@@ -549,3 +549,19 @@ void update_mmu_cache(struct vm_area_struct *vma, unsigned long address,
 	hash_preload(vma->vm_mm, address, access, trap);
 #endif /* CONFIG_PPC_STD_MMU */
 }
+
+/*
+ * devmem_is_allowed(): check to see if /dev/mem access to a certain address
+ * is valid. The argument is a physical page number.
+ *
+ * Access has to be given to non-kernel-ram areas as well, these contain the
+ * PCI mmio resources as well as potential bios/acpi data regions.
+ */
+int devmem_is_allowed(unsigned long pfn)
+{
+	if (iomem_is_exclusive(pfn << PAGE_SHIFT))
+		return 0;
+	if (!page_is_ram(pfn))
+		return 1;
+	return 0;
+}
diff --git a/drivers/char/mem.c b/drivers/char/mem.c
index 8fc04b4..0fb542c 100644
--- a/drivers/char/mem.c
+++ b/drivers/char/mem.c
@@ -66,8 +66,8 @@ static inline int range_is_allowed(unsigned long pfn, unsigned long size)
 	while (cursor < to) {
 		if (!devmem_is_allowed(pfn)) {
 			printk(KERN_INFO
-		"Program %s tried to access /dev/mem between %Lx->%Lx.\n",
-				current->comm, from, to);
+		"Program %s tried to access /dev/mem between %Lx->%Lx and "
+			"pfn %lu.\n", current->comm, from, to, pfn);
 			return 0;
 		}
 		cursor += PAGE_SIZE;
-- 
1.7.0.4

^ permalink raw reply related

* Re: [PATCH] powerpc: Fix xmon for systems without MSR[RI]
From: Scott Wood @ 2011-08-30 18:08 UTC (permalink / raw)
  To: Benjamin Herrenschmidt; +Cc: linuxppc-dev
In-Reply-To: <1314684670.2488.82.camel@pasglop>

On 08/30/2011 01:11 AM, Benjamin Herrenschmidt wrote:
> On Mon, 2011-08-08 at 16:25 -0500, Jimi Xenidis wrote:
>> From: David Gibson <dwg@au1.ibm.com>
>>
>> Based on patch by David Gibson <dwg@au1.ibm.com>
>>
>> xmon has a longstanding bug on systems which are SMP-capable but lack
>> the MSR[RI] bit.  In these cases, xmon invoked by IPI on secondary
>> CPUs will not properly keep quiet, but will print stuff, thereby
>> garbling the primary xmon's output.  This patch fixes it, by ignoring
>> the RI bit if the processor does not support it.
>>
>> There's already a version of this for 4xx upstream, which we'll need
>> to extend to other RI-lacking CPUs at some point.  For now this adds
>> BookE processors to the mix.
> 
> Don't freescale one have RI ?

e500mc does.

e500v2 doesn't -- if a machine check happens while MSR[ME]=0, it causes
a checkstop.

-Scott

^ permalink raw reply

* Re: kvm PCI assignment & VFIO ramblings
From: Joerg Roedel @ 2011-08-30 16:14 UTC (permalink / raw)
  To: Avi Kivity
  Cc: Alex Williamson, Alexey Kardashevskiy, kvm@vger.kernel.org,
	Paul Mackerras, Roedel, Joerg, qemu-devel, Alexander Graf, chrisw,
	iommu, Anthony Liguori, linux-pci@vger.kernel.org, linuxppc-dev,
	benve@cisco.com
In-Reply-To: <4E5A4AF0.20707@redhat.com>

On Sun, Aug 28, 2011 at 05:04:32PM +0300, Avi Kivity wrote:
> On 08/28/2011 04:56 PM, Joerg Roedel wrote:

>> This can't be secured by a lock, because it introduces potential
>> A->B<-->B->A lock problem when two processes try to take each others mm.
>> It could probably be solved by a task->real_mm pointer, havn't thought
>> about this yet...
>>
>
> Or a workqueue -  you get a kernel thread context with a bit of boilerplate.

Right, a workqueue might do the trick. We'll evaluate that. Thanks for
the idea :)

	Joerg

^ permalink raw reply

* Re: kvm PCI assignment & VFIO ramblings
From: Joerg Roedel @ 2011-08-30 16:13 UTC (permalink / raw)
  To: Alex Williamson
  Cc: chrisw, Alexey Kardashevskiy, kvm@vger.kernel.org, Paul Mackerras,
	Roedel, Joerg, linux-pci@vger.kernel.org, qemu-devel,
	Aaron Fabbri, iommu, Avi Kivity, Anthony Liguori, linuxppc-dev,
	benve@cisco.com
In-Reply-To: <1314381863.2859.312.camel@bling.home>

On Fri, Aug 26, 2011 at 12:04:22PM -0600, Alex Williamson wrote:
> On Thu, 2011-08-25 at 20:05 +0200, Joerg Roedel wrote:

> > If we really expect segment numbers that need the full 16 bit then this
> > would be the way to go. Otherwise I would prefer returning the group-id
> > directly and partition the group-id space for the error values (s32 with
> > negative numbers being errors).
> 
> It's unlikely to have segments using the top bit, but it would be broken
> for an iommu driver to define it's group numbers using pci s:b:d.f if we
> don't have that bit available.  Ben/David, do PEs have an identifier of
> a convenient size?  I'd guess any hardware based identifier is going to
> use a full unsigned bit width.

Okay, if we want to go the secure way I am fine with the "int *group"
parameter. Another option is to just return u64 and use the extended
number space for errors. But that is even worse as an interface, I
think.

	Joerg

^ permalink raw reply

* Re: [PATCH 00/14] Modifications for DWC OTG since v13
From: Pratyush Anand @ 2011-08-30 15:46 UTC (permalink / raw)
  To: Tirumala Marri
  Cc: Pratyush Anand, viresh.kumar, vipulkumar.samar, bhupesh.sharma,
	linux-usb, vipin.kumar, shiraz.hashim, Amit.VIRDI,
	rajeev-dlh.kumar, Mark Miesfeld, deepak.sikri, linuxppc-dev,
	Fushen Chen
In-Reply-To: <bff62861217ab3d8dbf8041e2c4d1c8c@mail.gmail.com>

On Tue, Aug 30, 2011 at 8:57 PM, Tirumala Marri <tmarri@apm.com> wrote:
> <-----Original Message-----
> <From: Pratyush Anand [mailto:pratyush.anand@st.com]
> <Sent: Tuesday, August 30, 2011 4:58 AM
> <To: linux-usb@vger.kernel.org
> <Cc: tmarri@apm.com; linuxppc-dev@lists.ozlabs.org; fchen@apm.com;
> <mmiesfeld@apm.com; shiraz.hashim@st.com; deepak.sikri@st.com;
> <vipulkumar.samar@st.com; rajeev-dlh.kumar@st.com; vipin.kumar@st.com;
> <bhupesh.sharma@st.com; viresh.kumar@st.com; Amit.VIRDI@st.com; Pratyush
> <Anand
> <Subject: [PATCH 00/14] Modifications for DWC OTG since v13
> <
> <These patches are based on:http://patchwork.ozlabs.org/patch/89560/
> <After not getting any reply from developers, I started to do
> <modifications for my platform (SPEAr1340).
> <I have done modifications in such a way that all the code in
> <driver/usb/dwc/ would be platform independent.
> <I have tested this code for host/device/dma/slave mode.
> <My fifo configuration is dedicated and dynamic.
>
> [Tirumala Marri] We are working on our next release of patches. They
> should be coming out soon.

Oh, thats great !!
Actually, I did not get any reply of my previous mail on your patch release=
.
I thought no one is maintaining., and so I sent them with my modifications,
after testing them in all dev/host/otg mode.

Regards
Pratyush

> --
> To unsubscribe from this list: send the line "unsubscribe linux-usb" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at =A0http://vger.kernel.org/majordomo-info.html
>

^ permalink raw reply

* Re: [PATCH 02/14] dwc/otg: Structure declaration for shared data
From: Pratyush Anand @ 2011-08-30 15:43 UTC (permalink / raw)
  To: Tirumala Marri
  Cc: Pratyush Anand, viresh.kumar, vipulkumar.samar, bhupesh.sharma,
	linux-usb, vipin.kumar, shiraz.hashim, Amit.VIRDI,
	rajeev-dlh.kumar, Mark Miesfeld, deepak.sikri, linuxppc-dev,
	Fushen Chen
In-Reply-To: <9183dc7afa5b8cde400e9f37f5679d4f@mail.gmail.com>

On Tue, Aug 30, 2011 at 8:59 PM, Tirumala Marri <tmarri@apm.com> wrote:
> <-----Original Message-----
> <From: Pratyush Anand [mailto:pratyush.anand@st.com]
> <Sent: Tuesday, August 30, 2011 4:58 AM
> <To: linux-usb@vger.kernel.org
> <Cc: tmarri@apm.com; linuxppc-dev@lists.ozlabs.org; fchen@apm.com;
> <mmiesfeld@apm.com; shiraz.hashim@st.com; deepak.sikri@st.com;
> <vipulkumar.samar@st.com; rajeev-dlh.kumar@st.com; vipin.kumar@st.com;
> <bhupesh.sharma@st.com; viresh.kumar@st.com; Amit.VIRDI@st.com; Pratyush
> <Anand
> <Subject: [PATCH 02/14] dwc/otg: Structure declaration for shared data
> <
> <There are some DWC OTG parameters which might be passed by a platform.
> <Declaration for structure of those parameters have been provided in this
> <include file.
> <
> <Signed-off-by: Pratyush Anand <pratyush.anand@st.com>
> <---
> < include/linux/usb/dwc_otg.h | =A0274
>
> [Tirumala Marri] I am not sure how to separate your changes. But we need
> More time as our initial patches are still pending.
> --marri

Tirumala,

If you agree , then I can send you modifications which I did over your
patches(v13) separately,
and then you can decide the final inclusion of only these modifications.

Regards
Pratyush

> --
> To unsubscribe from this list: send the line "unsubscribe linux-usb" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at =A0http://vger.kernel.org/majordomo-info.html
>

^ permalink raw reply

* Re: [PATCH 11/14] dwc/otg: Driver enable gadget support
From: Pratyush Anand @ 2011-08-30 15:40 UTC (permalink / raw)
  To: Sergei Shtylyov, tmarri
  Cc: Pratyush Anand, viresh.kumar, vipulkumar.samar, bhupesh.sharma,
	linux-usb, vipin.kumar, shiraz.hashim, Amit.VIRDI,
	rajeev-dlh.kumar, mmiesfeld, deepak.sikri, linuxppc-dev, fchen
In-Reply-To: <4E5CDF2C.8030400@ru.mvista.com>

On Tue, Aug 30, 2011 at 6:31 PM, Sergei Shtylyov <sshtylyov@mvista.com> wro=
te:
>
> Hello.
>
> On 08/30/2011 03:57 PM, Pratyush Anand wrote:
>
>> 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>
>> Signed-off-by: Pratyush Anand<pratyush.anand@st.com>
>> ---
>> =A0drivers/usb/gadget/gadget_chips.h | =A0 18 +++++++++++++++++-
>> =A01 files changed, 17 insertions(+), 1 deletions(-)
>
>> diff --git a/drivers/usb/gadget/gadget_chips.h b/drivers/usb/gadget/gadg=
et_chips.h
>> index 0978103..66b8018 100644
>> --- a/drivers/usb/gadget/gadget_chips.h
>> +++ b/drivers/usb/gadget/gadget_chips.h
>> @@ -148,6 +148,19 @@
>> =A0#define gadget_is_s3c_hsotg(g) =A0 =A00
>> =A0#endif
>>
>> +#if defined(CONFIG_DWC_OTG_MODE) || defined(CONFIG_DWC_DEVICE_ONLY)
>> +#define gadget_is_dwc_otg_pcd(g) =A0 =A0 =A0 (!strcmp("dwc_otg_pcd", (g=
)->name))
>> +#else
>> +#define gadget_is_dwc_otg_pcd(g) =A0 =A0 =A0 0
>> +#endif
>> +
>> +#ifdef CONFIG_USB_GADGET_CI13XXX_MSM
>> +#define gadget_is_ci13xxx_msm(g) =A0 =A0 =A0 (!strcmp("ci13xxx_msm", (g=
)->name))
>> +#else
>> +#define gadget_is_ci13xxx_msm(g) =A0 =A0 =A0 0
>> +#endif
>> +
>> +
>
> =A0 Too many newlines.
>

will correct it.

>>
>> =A0/**
>> =A0 * usb_gadget_controller_number - support bcdDevice id convention
>> @@ -208,10 +221,13 @@ static inline int usb_gadget_controller_number(str=
uct usb_gadget *gadget)
>> =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0return 0x26;
>> =A0 =A0 =A0 =A0else if (gadget_is_designware(gadget))
>> =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0return 0x27;
>> + =A0 =A0 =A0 else if (gadget_is_ci13xxx_msm(gadget))
>> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 return 0x28;
>> + =A0 =A0 =A0 else if (gadget_is_dwc_otg_pcd(gadget))
>> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 return 0x29;
>
> =A0 Hm, why are you adding 2 gadgets?

Yes, I also do not see reason for ci13xxx_msm.
This was added by original auther Tirumala.
@Tirumala , Please reply.

>
>> =A0 =A0 =A0 =A0return -ENOENT;
>> =A0}
>>
>> -
>
> =A0 Unrelated white space change.

will correct.

>
>> =A0/**
>> =A0 * gadget_supports_altsettings - return true if altsettings work
>> =A0 * @gadget: the gadget in question
>
> WBR, Sergei
> --
> To unsubscribe from this list: send the line "unsubscribe linux-usb" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at =A0http://vger.kernel.org/majordomo-info.html

^ permalink raw reply

* RE: [PATCH 02/14] dwc/otg: Structure declaration for shared data
From: Tirumala Marri @ 2011-08-30 15:29 UTC (permalink / raw)
  To: Pratyush Anand, linux-usb
  Cc: viresh.kumar, vipulkumar.samar, bhupesh.sharma, vipin.kumar,
	shiraz.hashim, Amit.VIRDI, rajeev-dlh.kumar, Mark Miesfeld,
	deepak.sikri, linuxppc-dev, Fushen Chen
In-Reply-To: <d10bbf1498af371e41907fa07624456d981e56af.1314704557.git.pratyush.anand@st.com>

<-----Original Message-----
<From: Pratyush Anand [mailto:pratyush.anand@st.com]
<Sent: Tuesday, August 30, 2011 4:58 AM
<To: linux-usb@vger.kernel.org
<Cc: tmarri@apm.com; linuxppc-dev@lists.ozlabs.org; fchen@apm.com;
<mmiesfeld@apm.com; shiraz.hashim@st.com; deepak.sikri@st.com;
<vipulkumar.samar@st.com; rajeev-dlh.kumar@st.com; vipin.kumar@st.com;
<bhupesh.sharma@st.com; viresh.kumar@st.com; Amit.VIRDI@st.com; Pratyush
<Anand
<Subject: [PATCH 02/14] dwc/otg: Structure declaration for shared data
<
<There are some DWC OTG parameters which might be passed by a platform.
<Declaration for structure of those parameters have been provided in this
<include file.
<
<Signed-off-by: Pratyush Anand <pratyush.anand@st.com>
<---
< include/linux/usb/dwc_otg.h |  274

[Tirumala Marri] I am not sure how to separate your changes. But we need
More time as our initial patches are still pending.
--marri

^ permalink raw reply

* RE: [PATCH 00/14] Modifications for DWC OTG since v13
From: Tirumala Marri @ 2011-08-30 15:27 UTC (permalink / raw)
  To: Pratyush Anand, linux-usb
  Cc: viresh.kumar, vipulkumar.samar, bhupesh.sharma, vipin.kumar,
	shiraz.hashim, Amit.VIRDI, rajeev-dlh.kumar, Mark Miesfeld,
	deepak.sikri, linuxppc-dev, Fushen Chen
In-Reply-To: <cover.1314704557.git.pratyush.anand@st.com>

<-----Original Message-----
<From: Pratyush Anand [mailto:pratyush.anand@st.com]
<Sent: Tuesday, August 30, 2011 4:58 AM
<To: linux-usb@vger.kernel.org
<Cc: tmarri@apm.com; linuxppc-dev@lists.ozlabs.org; fchen@apm.com;
<mmiesfeld@apm.com; shiraz.hashim@st.com; deepak.sikri@st.com;
<vipulkumar.samar@st.com; rajeev-dlh.kumar@st.com; vipin.kumar@st.com;
<bhupesh.sharma@st.com; viresh.kumar@st.com; Amit.VIRDI@st.com; Pratyush
<Anand
<Subject: [PATCH 00/14] Modifications for DWC OTG since v13
<
<These patches are based on:http://patchwork.ozlabs.org/patch/89560/
<After not getting any reply from developers, I started to do
<modifications for my platform (SPEAr1340).
<I have done modifications in such a way that all the code in
<driver/usb/dwc/ would be platform independent.
<I have tested this code for host/device/dma/slave mode.
<My fifo configuration is dedicated and dynamic.

[Tirumala Marri] We are working on our next release of patches. They
should be coming out soon.

^ permalink raw reply

* Re: VFIO v2 design plan
From: Alex Williamson @ 2011-08-30 14:51 UTC (permalink / raw)
  To: David Gibson
  Cc: chrisw, Alexey Kardashevskiy, kvm, Paul Mackerras, Roedel, Joerg,
	agraf, qemu-devel, aafabbri, iommu, Avi Kivity, Anthony Liguori,
	linux-pci@vger.kernel.org, linuxppc-dev, benve
In-Reply-To: <20110830074855.GA11906@yookeroo.fritz.box>

On Tue, 2011-08-30 at 17:48 +1000, David Gibson wrote:
> On Mon, Aug 29, 2011 at 10:24:43PM -0600, Alex Williamson wrote:
> > On Tue, 2011-08-30 at 13:04 +1000, David Gibson wrote:
> > > On Fri, Aug 26, 2011 at 11:05:23AM -0600, Alex Williamson wrote:
> > > > 
> > > > I don't think too much has changed since the previous email went out,
> > > > but it seems like a good idea to post a summary in case there were
> > > > suggestions or objections that I missed.
> > > > 
> > > > VFIO v2 will rely on the platform iommu driver reporting grouping
> > > > information.  Again, a group is a set of devices for which the iommu
> > > > cannot differentiate transactions.  An example would be a set of devices
> > > > behind a PCI-to-PCI bridge.  All transactions appear to be from the
> > > > bridge itself rather than devices behind the bridge.  Platforms are free
> > > > to have whatever constraints they need to for what constitutes a group.
> > > > 
> > > > I posted a rough draft of patch to implement that for the base iommu
> > > > driver and VT-d, adding an iommu_device_group callback on iommu ops.
> > > > The iommu base driver also populates an iommu_group sysfs file for each
> > > > device that's part of a group.  Members of the same group return the
> > > > same value via either the sysfs or iommu_device_group.  The value
> > > > returned is arbitrary, should not be assumed to be persistent across
> > > > boots, and is left to the iommu driver to generate.  There are some
> > > > implementation details around how to do this without favoring one bus
> > > > over another, but the interface should be bus/device type agnostic in
> > > > the end.
> > > > 
> > > > When the vfio module is loaded, character devices will be created for
> > > > each group in /dev/vfio/$GROUP.  Setting file permissions on these files
> > > > should be sufficient for providing a user with complete access to the
> > > > group.  Opening this device file provides what we'll call the "group
> > > > fd".  The group fd is restricted to only work with a single mm context.
> > > > Concurrent opens will be denied if the opening process mm does not
> > > > match.  The group fd will provide interfaces for enumerating the devices
> > > > in the group, returning a file descriptor for each device in the group
> > > > (the "device fd"), binding groups together, and returning a file
> > > > descriptor for iommu operations (the "iommu fd").
> > > > 
> > > > A group is "viable" when all member devices of the group are bound to
> > > > the vfio driver.  Until that point, the group fd only allows enumeration
> > > > interfaces (ie. listing of group devices).  I'm currently thinking
> > > > enumeration will be done by a simple read() on the device file returning
> > > > a list of dev_name()s.
> > > 
> > > Ok.  Are you envisaging this interface as a virtual file, or as a
> > > stream?  That is, can you seek around the list of devices like a
> > > regular file - in which case, what are the precise semantics when the
> > > list is changed by a bind - or is there no meaningful notion of file
> > > pointer and read() just gives you the next device - in which case how
> > > to you rewind to enumerate the group again.
> > 
> > I was implementing it as a virtual file that gets generated on read()
> > (see example in note[2] below).  It is a bit clunky as reading it a byte
> > at a time could experience races w/ device add/remove.  If it's read all
> > at once, it's an accurate snapshot.  Suggestions welcome, this just
> > seemed easier than trying to stuff it into a struct for an ioctl.  For a
> > while I thought I could do a VFIO_GROUP_GET_NUM_DEVICES +
> > VFIO_GROUP_GET_DEVICE_INDEX, but that assumes device stability, which I
> > don't think we can guarantee.
> 
> Yeah, that sounds reasonable.
> 
> > > >  Once the group is viable, the user may bind the
> > > > group to another group, retrieve the iommu fd, or retrieve device fds.
> > > > Internally, each of these operations will result in an iommu domain
> > > > being allocated and all of the devices attached to the domain.
> > > > 
> > > > The purpose of binding groups is to share the iommu domain.  Groups
> > > > making use of incompatible iommu domains will fail to bind.  Groups
> > > > making use of different mm's will fail to bind.  The vfio driver may
> > > > reject some binding based on domain capabilities, but final veto power
> > > > is left to the iommu driver[1].  If a user makes use of a group
> > > > independently and later wishes to bind it to another group, all the
> > > > device fds and the iommu fd must first be closed.  This prevents using a
> > > > stale iommu fd or accessing devices while the iommu is being switched.
> > > > Operations on any group fds of a merged group are performed globally on
> > > > the group (ie. enumerating the devices lists all devices in the merged
> > > > group, retrieving the iommu fd from any group fd results in the same fd,
> > > > device fds from any group can be retrieved from any group fd[2]).
> > > > Groups can be merged and unmerged dynamically.  Unmerging a group
> > > > requires the device fds for the outgoing group are closed.  The iommu fd
> > > > will remain persistent for the remaining merged group.
> > > 
> > > As I've said I prefer a persistent group model, rather than this
> > > transient group model, but it's not a dealbreaker by itself.  How are
> > > unmerges specified?
> > 
> > VFIO_GROUP_UNMERGE ioctl taking a group fd parameter.
> > 
> > >  I'm also assuming that in this model closing a
> > > (bound) group fd will unmerge everything down to atomic groups again.
> > 
> > Yes, it will unmerge the closed group down to the atomic group.
> 
> Hrm, not thrilled with the merging semantics, but I can probably live
> with them.  Still some clarifications, though..
> 
> If you open a group, merge in a bunch of other groups, then re-open
> /dev/vfio/NNN for one of the groups mergeed, presumably the new fd
> must also see the merged group?  So presumably you must only unmerge
> everything when all the fds are closed.

The device fds for the group to be unmerged must be closed before an
unmerge.  The iommu fd is tricky.  The iommu fd is really the iommu for
the merged group, not the individual groups, so it's context stays with
the remaining group.  Therefore I don't enforce a refcnt on the iommu
fd.  The usage model I expect is that if a merge works, the user will
probably use a single iommu fd for the whole merged group.  Maybe that
should be enforced?

> If you open groups a and b, then merge a (disjoint) bunch of things
> into each, then merge b into a, what are the semantics?  Wheat about
> if you then unmerge b from a - does it just remove the atomic group b,
> or everything you merged into b earlier?  Or, what happens if you open
> group a, merge in some things, then attempt to unmerge a from the
> merged group?

Simple, don't allow merging and unmerging of merged groups.  Merge and
unmerge only work on singleton groups.  The last case we must support.
In that case you just use:

ioctl(a.fd, VFIO_GROUP_MERGE, b.fd)
ioctl(b.fd, VFIO_GROUP_UNMERGE, a.fd)

The groups are peers when merged, so b can remove a as easily as a can
remove b.  Group b is left with any iommu context setup while merged.

> > > > If a device within a group is unbound from the vfio driver while it's in
> > > > use (iommu fd refcnt > 0 || device fd recnt > 0), vfio will block the
> > > > release and send netlink remove requests for every opened device in the
> > > > group (or merged group).
> > > 
> > > Hrm, I do dislike netlink being yet another aspect of an already
> > > complex interface.  Would it be possible to do kernel->user
> > > notifications with a poll()/read() interface on one of the existing
> > > fds instead?
> > 
> > I think it'd have to be a new eventfd, but yes, it would be possible.
> > Then we'd have to figure out if we filter all requests through that
> > (remove, PCI AER, suspend/resume, etc..) or do we use a new fd for each
> > and how we return info for each of those.
> 
> Well, I wasn't thinking an eventfd(), precisely so that you can return
> a custom packet of information with this stuff on read().
> 
> >  As much as everyone hates
> > netlink, it still feels like the right interface for these.
> 
> Well, maybe.
> 
> > Beyond unbind, we also need to think about hotplug.  If a system had
> > multiple hotplug slots below a P2P bridge and a device was added while
> > the group is in use, what do we do?  Maybe we can somehow disable it or
> > mark it for vfio in our bus notifier routines(?).
> 
> That is a very good point.  It actually brings into focus a niggling
> concern I had about this model where the group becomes vfio usable
> once all the devices in it are bound to vfio.  Because of the
> possibility of hotplug, I think its conceptually more correct to not
> treat vfio as just another kernel driver which can bind devices, but a
> special state that the whole group goes into atomically.  So the
> sequence would be:
> 	- Admin asks that a group go into vfio state
> 	- kernel (attempts to) unbind kernel drivers from every device
> in the group
> 	- group is marked in vfio/limbo state
> 
> At this point no kernel drivers may bind to anything in the group,
> including things that are hotplugged into the group after this initial
> sequence.

It seems like this is a mode that could only be accessible if the group
is opened w/ admin capabilities, I don't think we'd want to let the vfio
group chrdev owner be able to do that automatically.  I don't know of
any other drivers that behave like this, being able to unbind running
drivers and pull devices into itself.

> > > >  If the device fds are not released and
> > > > subsequently the iommu fd released as well, vfio will kill the user
> > > > process after some delay.
> > > 
> > > Ouch, this seems to me a problematic semantic.  Whether the user
> > > process survives depends on whether it processes the remove requests
> > > fast enough - and a user process could be slowed down by system load
> > > or other factors not entirely in its control.
> > 
> > I was assuming "ample" time to process a hot remove, but yes, it's an
> > area of concern.  I'm not sure how much of a problem it is in practice
> > though.  Yes you can shoot your VM accidentally as root... don't do
> > that.
> 
> They can, but with this semantic they can't know in advance whether
> the command is going to kill the VM or not.  I can just see a
> situation where the admin issues a command to remove the device from
> the guest, and usually that goes through the hot guest unplug
> mechanisms, the guest keeps running and everything is happy.  Then one
> time they issue *exactly the same command* and the VM dies, because
> the system is running really slow for some reason (huge load, or maybe
> someone switched the VM into full emulation for debugging).

Not sure how to handle this other than leave a trail of bread crumbs.

> > > I'd be more comfortable with a model where there was a distinction
> > > between a "soft" and "hard" remove.  The soft would either simply
> > > fail, if the device is in use by vfio, or block indefinitely.  The
> > > hard would kill the user process without delay.  This effectively
> > > allows your semantics to be implemented in userspace (soft remove,
> > > wait, hard remove) - where it's easier to tweak the policy of how long
> > > to wait.
> > 
> > Your first example is essentially what current vfio does now, request
> > remove, wait indefinitely and qemu triggers an abort if the guest
> > doesn't respond.  The trouble with moving this policy to userspace is
> > that we're not protecting the host.
> 
> How is the host not protected?  Bear in mind that when I say
> "userspace" I'm not thinking qemu, I'm thinking the admin equipped
> with whatever tools he uses for moving devices between guests.  So
> they go:
> 	- Please remove this group from the guest
> 	- Waits for an amount of time of their choice
> 	- Decide, crap, the guest is broken
> 	- Hard remove the group from the guest, killing the guest
> 
> It's basic in perfect analogy to the old:
> 	- kill -15
> 	- *drum fingers*
> 	- Damn, it's stuck
> 	- kill -9

And what if the remove is initiated by a hardware admin that walks over
to the system, and presses the PCI device hot unplug doorbell?  It just
looks like a driver hang.  Thanks,

Alex

^ permalink raw reply

* Re: [PATCH 11/14] dwc/otg: Driver enable gadget support
From: Sergei Shtylyov @ 2011-08-30 13:01 UTC (permalink / raw)
  To: Pratyush Anand
  Cc: viresh.kumar, vipulkumar.samar, bhupesh.sharma, tmarri, linux-usb,
	vipin.kumar, shiraz.hashim, Amit.VIRDI, rajeev-dlh.kumar,
	mmiesfeld, deepak.sikri, linuxppc-dev, fchen
In-Reply-To: <d12fef0c09645ba1cd56dd011b5090cc8bde4b4b.1314704558.git.pratyush.anand@st.com>

Hello.

On 08/30/2011 03:57 PM, Pratyush Anand wrote:

> 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>
> Signed-off-by: Pratyush Anand<pratyush.anand@st.com>
> ---
>   drivers/usb/gadget/gadget_chips.h |   18 +++++++++++++++++-
>   1 files changed, 17 insertions(+), 1 deletions(-)

> diff --git a/drivers/usb/gadget/gadget_chips.h b/drivers/usb/gadget/gadget_chips.h
> index 0978103..66b8018 100644
> --- a/drivers/usb/gadget/gadget_chips.h
> +++ b/drivers/usb/gadget/gadget_chips.h
> @@ -148,6 +148,19 @@
>   #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_CI13XXX_MSM
> +#define gadget_is_ci13xxx_msm(g)	(!strcmp("ci13xxx_msm", (g)->name))
> +#else
> +#define gadget_is_ci13xxx_msm(g)	0
> +#endif
> +
> +

    Too many newlines.

>
>   /**
>    * usb_gadget_controller_number - support bcdDevice id convention
> @@ -208,10 +221,13 @@ static inline int usb_gadget_controller_number(struct usb_gadget *gadget)
>   		return 0x26;
>   	else if (gadget_is_designware(gadget))
>   		return 0x27;
> +	else if (gadget_is_ci13xxx_msm(gadget))
> +		return 0x28;
> +	else if (gadget_is_dwc_otg_pcd(gadget))
> +		return 0x29;

    Hm, why are you adding 2 gadgets?

>   	return -ENOENT;
>   }
>
> -

    Unrelated white space change.

>   /**
>    * gadget_supports_altsettings - return true if altsettings work
>    * @gadget: the gadget in question

WBR, Sergei

^ permalink raw reply

* Re: [PATCH 12/14] include/linux/usb/gadget.h : include <linux/device.h> for successful compilation
From: Sergei Shtylyov @ 2011-08-30 12:57 UTC (permalink / raw)
  To: Pratyush Anand
  Cc: viresh.kumar, vipulkumar.samar, bhupesh.sharma, tmarri, linux-usb,
	vipin.kumar, shiraz.hashim, Amit.VIRDI, rajeev-dlh.kumar,
	mmiesfeld, deepak.sikri, linuxppc-dev, fchen
In-Reply-To: <171f829cad0bfa86b79ae74e9c6c9ae4eefb2738.1314704558.git.pratyush.anand@st.com>

Hello.

On 08/30/2011 03:57 PM, Pratyush Anand wrote:

> gadget.h uses struct device, which has been declared in linux/device.h. So it
> must be included.

> Signed-off-by: Pratyush Anand<pratyush.anand@st.com>
> ---
>   include/linux/usb/gadget.h |    1 +
>   1 files changed, 1 insertions(+), 0 deletions(-)

> diff --git a/include/linux/usb/gadget.h b/include/linux/usb/gadget.h
> index 006412c..32f7b69 100644
> --- a/include/linux/usb/gadget.h
> +++ b/include/linux/usb/gadget.h
> @@ -16,6 +16,7 @@
>   #define __LINUX_USB_GADGET_H
>
>   #include<linux/slab.h>
> +#include<linux/device.h>

    On what tree are you basing? There have been patches accepted in v3.1-rc1, 
which added a bunch of #include's to that file (including <linux/device.h>).

WBR, Sergei

^ permalink raw reply

* [PATCH 02/14] dwc/otg: Structure declaration for shared data
From: Pratyush Anand @ 2011-08-30 11:57 UTC (permalink / raw)
  To: linux-usb
  Cc: Pratyush Anand, viresh.kumar, vipulkumar.samar, bhupesh.sharma,
	tmarri, vipin.kumar, shiraz.hashim, Amit.VIRDI, rajeev-dlh.kumar,
	mmiesfeld, deepak.sikri, linuxppc-dev, fchen
In-Reply-To: <cover.1314704557.git.pratyush.anand@st.com>

There are some DWC OTG parameters which might be passed by a platform.
Declaration for structure of those parameters have been provided in this
include file.

Signed-off-by: Pratyush Anand <pratyush.anand@st.com>
---
 include/linux/usb/dwc_otg.h |  274 +++++++++++++++++++++++++++++++++++++++++++
 1 files changed, 274 insertions(+), 0 deletions(-)
 create mode 100644 include/linux/usb/dwc_otg.h

diff --git a/include/linux/usb/dwc_otg.h b/include/linux/usb/dwc_otg.h
new file mode 100644
index 0000000..aa86adc
--- /dev/null
+++ b/include/linux/usb/dwc_otg.h
@@ -0,0 +1,274 @@
+/* DWC OTG (On The Go) defines */
+
+#ifndef __LINUX_DWC_OTG_H
+#define __LINUX_DWC_OTG_H
+
+/*
+ * 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)
+	 */
+#define MAX_TX_FIFOS			15	/* Max periodic FIFOs */
+	u32 dev_perio_tx_fifo_size[MAX_TX_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];
+	u32 fifo_number;
+#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
+
+};
+
+
+/**
+ * struct dwc_otg_plat_data - device.platform_data for dwc otg driver.
+ */
+struct dwc_otg_plat_data {
+	int (*phy_init)(void);
+	int (*param_init)(struct core_params *);
+};
+
+#endif /* __LINUX_DWC_OTG_H */
-- 
1.7.2.2

^ permalink raw reply related

* [PATCH 09/14] dwc/otg: Add PCD interrupt function
From: Pratyush Anand @ 2011-08-30 11:57 UTC (permalink / raw)
  To: linux-usb
  Cc: Pratyush Anand, viresh.kumar, vipulkumar.samar, bhupesh.sharma,
	tmarri, vipin.kumar, shiraz.hashim, Amit.VIRDI, rajeev-dlh.kumar,
	mmiesfeld, deepak.sikri, linuxppc-dev, fchen
In-Reply-To: <cover.1314704557.git.pratyush.anand@st.com>

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>
Signed-off-by: Pratyush Anand <pratyush.anand@st.com>
---
 drivers/usb/dwc/pcd_intr.c | 2316 ++++++++++++++++++++++++++++++++++++++++++++
 1 files changed, 2316 insertions(+), 0 deletions(-)
 create mode 100644 drivers/usb/dwc/pcd_intr.c

diff --git a/drivers/usb/dwc/pcd_intr.c b/drivers/usb/dwc/pcd_intr.c
new file mode 100644
index 0000000..bb921e0
--- /dev/null
+++ b/drivers/usb/dwc/pcd_intr.c
@@ -0,0 +1,2316 @@
+/*
+ * 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 "driver.h"
+#include "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((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_fifo32(core_if->data_fifo[0]);
+	dest[1] = dwc_read_fifo32(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);
+	ulong global_regs = 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)
+{
+	ulong regs = 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_fifo32(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);
+	ulong global_regs = 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_NP_TXFIFO_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_GINTMSK, gintsts, gintsts);
+	else
+		dwc_modify32(global_regs + DWC_GINTMSK, gintsts, 0);
+	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);
+	ulong regs;
+	u32 txstatus = 0;
+	struct pcd_ep *ep;
+	u32 len;
+	int dwords;
+	u32 diepint = 0;
+
+	ep = get_in_ep(pcd, epnum);
+	regs = 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;
+	ulong global_regs = 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((GET_CORE_IF(pcd)->core_global_regs) + DWC_GINTMSK,
+		     intr_mask, 0);
+
+	gintsts = 0;
+	gintsts |= DWC_INTSTS_I2C_INTR;
+	dwc_write32((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((GET_CORE_IF(pcd)->core_global_regs) + DWC_GINTMSK,
+		     intr_mask, 0);
+
+	gintsts = 0;
+	gintsts |= DWC_INTSTS_EARLY_SUSP;
+	dwc_write32((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(dev_if->out_ep_regs[0] + DWC_DOEPTSIZ, doeptsize0);
+
+	if (core_if->dma_enable) {
+		u32 doepctl = 0;
+
+		dwc_write32(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(core_if->core_global_regs + DWC_GRSTCTL, resetctl);
+
+	daintmsk |= DWC_DAINT_INEP00;
+	daintmsk |= DWC_DAINT_OUTEP00;
+	dwc_write32(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(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(dev_if->dev_global_regs + DWC_DIEPMSK, diepmsk);
+
+	/* Reset Device Address */
+	dcfg = dwc_read32(dev_if->dev_global_regs + DWC_DCFG);
+	dcfg = DWC_DCFG_DEV_ADDR_WR(dcfg, 0);
+	dwc_write32(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((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(dev_if->dev_global_regs + DWC_DSTS);
+	diepctl = dwc_read32(dev_if->in_ep_regs[0] + DWC_DIEPCTL);
+	doepctl = dwc_read32(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(dev_if->in_ep_regs[0] + DWC_DIEPCTL, diepctl);
+
+	/* Enable OUT EP for receive */
+	doepctl = DWC_DEPCTL_EPENA_RW(doepctl, 1);
+	dwc_write32(dev_if->out_ep_regs[0] + DWC_DOEPCTL, doepctl);
+
+	dctl = DWC_DCTL_CLR_CLBL_NP_IN_NAK(dctl, 1);
+	dwc_modify32(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);
+	ulong global_regs = 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((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((GET_CORE_IF(pcd)->core_global_regs) + DWC_GINTMSK,
+		     intr_mask, 0);
+
+	gintsts = 0;
+	gintsts |= DWC_INTSTS_ISYNC_OUTPKT_DRP;
+	dwc_write32((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((GET_CORE_IF(pcd)->core_global_regs) + DWC_GINTMSK,
+		     intr_mask, 0);
+
+	gintsts = 0;
+	gintsts |= DWC_INTSTS_END_OF_PFRM;
+	dwc_write32((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((core_if->core_global_regs) + DWC_GINTMSK,
+		     intr_mask, 0);
+
+	gintsts = 0;
+	gintsts |= DWC_INTSTS_ENDP_MIS_MTCH;
+	dwc_write32((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.
+			 */
+			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);
+	ulong regs = 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(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(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;
+	ulong in_regs = 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(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;
+	ulong in_ep_regs = 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(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 {
+		ulong out_ep_regs = dev_if->out_ep_regs[ep->dwc_ep.num];
+
+		deptsiz = dwc_read32(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) {
+			ulong in_regs =
+			    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(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(in_regs + DWC_DIEPTSIZ, deptsiz);
+
+			intr_mask |= DWC_INTSTS_NP_TXFIFO_EMPT;
+			dwc_modify32((core_if->core_global_regs) +
+				     DWC_GINTSTS, intr_mask, 0);
+			dwc_modify32((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;
+		ulong in_regs = d_if->in_ep_regs[0];
+		u32 tx_status = 0;
+		ulong glbl_regs = c_if->core_global_regs;
+
+		tx_status = dwc_read32(glbl_regs + DWC_GNPTXSTS);
+
+		depctl = dwc_read32(in_regs + DWC_DIEPCTL);
+		deptsiz = dwc_read32(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(in_regs + DWC_DIEPTSIZ, deptsiz);
+
+		/* Write the DMA register */
+		if (DWC_HWCFG2_ARCH_RD(c_if->hwcfg2) == DWC_INT_DMA_ARCH)
+			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);
+
+		/*
+		 * 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(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(d_if->dev_global_regs +
+			     DWC_DTKNQR4FIFOEMPTYMSK, fifoemptymsk, 0);
+	} else {
+		u32 intr_mask = 0;
+
+		intr_mask |= DWC_INTMSK_NP_TXFIFO_EMPT;
+		dwc_modify32((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(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(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((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((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, deptsiz;
+	struct device_if *dev_if = GET_CORE_IF(pcd)->dev_if;
+	ulong in_regs = dev_if->in_ep_regs[num];
+
+	deptsiz = dwc_read32(in_regs + DWC_DIEPTSIZ);
+
+	if (!ep->stopped && num && (DWC_DEPTSIZ_PKT_CNT_RD(deptsiz) == 0)) {
+		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(dev_if->dev_global_regs + DWC_DIEPMSK);
+	emp =
+	    dwc_read32(dev_if->dev_global_regs + DWC_DTKNQR4FIFOEMPTYMSK);
+	msk |= ((emp >> ep->num) & 0x1) << 7;
+	v = dwc_read32(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(_if->dev_if->dev_global_regs + DWC_DAINT) &
+	    dwc_read32(_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(_if->dev_if->dev_global_regs + DWC_DAINT) &
+	    dwc_read32(_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(dev_if->out_ep_regs[ep->num] + DWC_DOEPINT) &
+	    dwc_read32(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((GET_CORE_IF(pcd)->core_global_regs) + DWC_GINTMSK,
+		     intr_mask, 0);
+
+	gintsts |= DWC_INTSTS_INCMP_IN_ATX;
+	dwc_write32((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((GET_CORE_IF(pcd)->core_global_regs) + DWC_GINTMSK,
+		     intr_mask, 0);
+
+	gintsts |= DWC_INTSTS_INCMP_OUT_PTX;
+	dwc_write32((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((GET_CORE_IF(pcd)->core_global_regs) + DWC_GINTMSK,
+		     intr_mask, 0);
+
+	/* Clear interrupt */
+	gintsts |= DWC_INTSTS_GLBL_IN_NAK;
+	dwc_write32((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((GET_CORE_IF(pcd)->core_global_regs) + DWC_GINTMSK,
+		     intr_mask, 0);
+
+	/* Clear goutnakeff */
+	gintsts |= DWC_INTSTS_GLBL_OUT_NAK;
+	dwc_write32((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.7.2.2

^ permalink raw reply related

* [PATCH 00/14] Modifications for DWC OTG since v13
From: Pratyush Anand @ 2011-08-30 11:57 UTC (permalink / raw)
  To: linux-usb
  Cc: Pratyush Anand, viresh.kumar, vipulkumar.samar, bhupesh.sharma,
	tmarri, vipin.kumar, shiraz.hashim, Amit.VIRDI, rajeev-dlh.kumar,
	mmiesfeld, deepak.sikri, linuxppc-dev, fchen

These patches are based on:http://patchwork.ozlabs.org/patch/89560/
After not getting any reply from developers, I started to do
modifications for my platform (SPEAr1340).
I have done modifications in such a way that all the code in 
driver/usb/dwc/ would be platform independent.
I have tested this code for host/device/dma/slave mode.
My fifo configuration is dedicated and dynamic.

Changes since v13:

1. All the patches has been renamed from usb/ppc4xx to dwc/otg. Changes
   has been done, because these files are valid for other architecture
   also.
2. Parameter passing mechanism has been modified. Now, driver can also
   receive parameters from a platform device.
3. Next request is started after clear halt for all slave transfer
    There might be a situation, when there are data into fifo, EP is
    receiving IN token, but EP is disabled. In this case, EP will just
    stop and do nothing.  So, for each transfer type start_next_request
    is enabled.
4. Dynamic fifo was configured by reading number of endpoints. Number
   of endpoints can be different than the number of dynamic fifo in the
   controller. Number of dynamic fifo is programmed in fifo_number
   parameter. Program fifos only for fifo_number of fifo.
5.  If platform specific functions like phy_init and param_init is
    defined then call it.
6.  Few define which are used for TX, were defined as RX in the code.
    All such definition has been replaced throughout the code.
7. There are some defined steps to disable an endpoint.  It can not be
   disabled just by setting epdisable bit.  Now, it is disabled as
   specification says.
    
    During stall of and endpoint, first stall bit should be set and then
    endpoint should be disabled.
    
    In dwc_otg_pcd_ep_disable, TX fifo must be flushed along with
    disabling of endpoint.
    
    In DMA mode if Fifo is flushed only once then, DMA will write data
    into it again. So, it must be flushed till DMA transfer is complete.
8. dwc_otg_ep_deactivate is called after dwc_otg_ep_disable.
9. platform_get_irq return -ENXIO , if requested IRQ is not found.  So,
   return must be checked accordingly.
    
10.  There is a struct (core_params) for all possible variable
     parameters of OTG.  Earlier, these parameters were not modifiable
     for any platform data. It was reading from the HW and then was
     programming the same value again.
    
    Now, it has been modified. A platform code can modify these
    parameters.
    
    Struct definition has been moved from cil.h to a new file
    include/linux/usb/dwc_otg.h.
    
    Some of the parameters have been initialized with default values,
    while other with -1.
    
    A new parameter fifo_number has been added. This parameter is also
    defined in the specification. Otg code was reading number of
    endpoint from hwcfg regs for the purpose of this parameter, which
    was wrong.
    
    SW checks for only fifo_number fifos and not MAX_TX_FIFOS.
    
    Now , there is only one define for MAX_TX_FIFO and MAX_PERIO_FIFOS.
    As, per specification they can not be different.
11. In dwc_otg_pcd_handle_np_tx_fifo_empty_intr a different interrupt
    was cleared. Corrected it to clear NP TX Fifo empty interrupt.
    
    Re-enabling of interrupt was not correct when packets were still
    pending. This patch also corrects this behaviour.
12. Code checks for space availability in fifo during
    write_epmty_tx_fifo.  It was writing only when space available was
    more than data to be written. So, it was writing 512 bytes only when
    516 bytes space was available.  Modified condition check from > to
	>=.
13.  When TX fifo empty interrupt is received, transfer completion may
     not be true. Next transfer should only be started when transfer is
     complete, i.e. packet count in dieptsiz register has become zero.
14.  Max number of EP increased to 16
15. There can be few devices like SPEAr1340, which issues a connection
    only after removal of soft disconnect. Default reset value is
    disconnected.  It will not have any side effect even for the device
    where reset value is connected.
16.  As per data sheet, when bit 31 of GRSTCTL is 1 then AHB is IDLE.
     Earlier function was waiting for this but to be 0, which was wrong.
17. Although name of file suggest that it is specific for PPC.  But, all
    of its content are common for OTG and can be used for any platform.
    There were some compilation issued on ARM platform with this file.
    It has been removed by calling by proper functions.



Pratyush Anand (4):
  dwc/otg: Structure declaration for shared data
  include/linux/usb/gadget.h : include <linux/device.h> for successful
    compilation
  usb/gadget/kconfig: added dwc otg as an option for peripheral
    controller
  arm/include/asm/io.h : added macros to read/write big/little endian
    register

Tirumala Marri (10):
  dwc/otg: Add Register definitions
  dwc/otg: Add driver framework
  dwc/otg: Add Core Interface Layer (CIL)
  dwc/otg: Add HCD function
  dwc/otg: Add HCD interrupt function
  dwc/otg: Add HCD queue function
  dwc/otg: Add PCD function
  dwc/otg: Add PCD interrupt function
  dwc/otg: Add driver kernel configuration and Makefile
  dwc/otg: Driver enable gadget support

 arch/arm/include/asm/io.h         |    8 +
 drivers/Makefile                  |    1 +
 drivers/usb/Kconfig               |    2 +
 drivers/usb/dwc/Kconfig           |   84 ++
 drivers/usb/dwc/Makefile          |   19 +
 drivers/usb/dwc/apmppc.c          |  436 +++++++
 drivers/usb/dwc/cil.c             |  969 +++++++++++++++
 drivers/usb/dwc/cil.h             |  921 ++++++++++++++
 drivers/usb/dwc/cil_intr.c        |  616 +++++++++
 drivers/usb/dwc/driver.h          |   76 ++
 drivers/usb/dwc/hcd.c             | 2465 +++++++++++++++++++++++++++++++++++++
 drivers/usb/dwc/hcd.h             |  416 +++++++
 drivers/usb/dwc/hcd_intr.c        | 1481 ++++++++++++++++++++++
 drivers/usb/dwc/hcd_queue.c       |  696 +++++++++++
 drivers/usb/dwc/param.c           |  219 ++++
 drivers/usb/dwc/pcd.c             | 1818 +++++++++++++++++++++++++++
 drivers/usb/dwc/pcd.h             |  139 +++
 drivers/usb/dwc/pcd_intr.c        | 2316 ++++++++++++++++++++++++++++++++++
 drivers/usb/dwc/regs.h            | 1324 ++++++++++++++++++++
 drivers/usb/gadget/Kconfig        |   17 +
 drivers/usb/gadget/gadget_chips.h |   18 +-
 include/linux/usb/dwc_otg.h       |  274 ++++
 include/linux/usb/gadget.h        |    1 +
 23 files changed, 14315 insertions(+), 1 deletions(-)
 create mode 100644 drivers/usb/dwc/Kconfig
 create mode 100644 drivers/usb/dwc/Makefile
 create mode 100644 drivers/usb/dwc/apmppc.c
 create mode 100644 drivers/usb/dwc/cil.c
 create mode 100644 drivers/usb/dwc/cil.h
 create mode 100644 drivers/usb/dwc/cil_intr.c
 create mode 100644 drivers/usb/dwc/driver.h
 create mode 100644 drivers/usb/dwc/hcd.c
 create mode 100644 drivers/usb/dwc/hcd.h
 create mode 100644 drivers/usb/dwc/hcd_intr.c
 create mode 100644 drivers/usb/dwc/hcd_queue.c
 create mode 100644 drivers/usb/dwc/param.c
 create mode 100644 drivers/usb/dwc/pcd.c
 create mode 100644 drivers/usb/dwc/pcd.h
 create mode 100644 drivers/usb/dwc/pcd_intr.c
 create mode 100644 drivers/usb/dwc/regs.h
 create mode 100644 include/linux/usb/dwc_otg.h

-- 
1.7.2.2

^ permalink raw reply

* [PATCH 10/14] dwc/otg: Add driver kernel configuration and Makefile
From: Pratyush Anand @ 2011-08-30 11:57 UTC (permalink / raw)
  To: linux-usb
  Cc: viresh.kumar, vipulkumar.samar, bhupesh.sharma, tmarri,
	vipin.kumar, shiraz.hashim, Amit.VIRDI, rajeev-dlh.kumar,
	mmiesfeld, deepak.sikri, linuxppc-dev, fchen
In-Reply-To: <cover.1314704557.git.pratyush.anand@st.com>

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         |    1 +
 drivers/usb/Kconfig      |    2 +
 drivers/usb/dwc/Kconfig  |   84 ++++++++++++++++++++++++++++++++++++++++++++++
 drivers/usb/dwc/Makefile |   19 ++++++++++
 4 files changed, 106 insertions(+), 0 deletions(-)
 create mode 100644 drivers/usb/dwc/Kconfig
 create mode 100644 drivers/usb/dwc/Makefile

diff --git a/drivers/Makefile b/drivers/Makefile
index a0d5aa60..d951bf6 100644
--- a/drivers/Makefile
+++ b/drivers/Makefile
@@ -68,6 +68,7 @@ obj-$(CONFIG_PARIDE) 		+= block/paride/
 obj-$(CONFIG_TC)		+= tc/
 obj-$(CONFIG_UWB)		+= uwb/
 obj-$(CONFIG_USB_OTG_UTILS)	+= usb/otg/
+obj-$(CONFIG_USB_DWC_OTG)	+= usb/dwc/
 obj-$(CONFIG_USB)		+= usb/
 obj-$(CONFIG_USB_MUSB_HDRC)	+= usb/musb/
 obj-$(CONFIG_PCI)		+= usb/
diff --git a/drivers/usb/Kconfig b/drivers/usb/Kconfig
index 8cd1df5..963e4b3 100644
--- a/drivers/usb/Kconfig
+++ b/drivers/usb/Kconfig
@@ -117,6 +117,8 @@ source "drivers/usb/host/Kconfig"
 
 source "drivers/usb/musb/Kconfig"
 
+source "drivers/usb/dwc/Kconfig"
+
 source "drivers/usb/class/Kconfig"
 
 source "drivers/usb/storage/Kconfig"
diff --git a/drivers/usb/dwc/Kconfig b/drivers/usb/dwc/Kconfig
new file mode 100644
index 0000000..eafc5ed
--- /dev/null
+++ b/drivers/usb/dwc/Kconfig
@@ -0,0 +1,84 @@
+#
+# 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
+	tristate "Synopsys DWC OTG Controller"
+	depends on USB || USB_GADGET
+	select NOP_USB_XCEIV
+	select USB_OTG_UTILS
+	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)
+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 DWC_OTG_REG_LE
+	bool "DWC Little Endian Register"
+	depends on USB_DWC_OTG
+	default y
+	help
+	  OTG core register access is Little-Endian.
+
+config DWC_OTG_FIFO_LE
+	bool "DWC FIFO Little Endian"
+	depends on USB_DWC_OTG
+	default n
+	help
+	  OTG core FIFO access is Little-Endian.
+
+config DWC_LIMITED_XFER_SIZE
+	bool "DWC Endpoint Limited Xfer Size"
+	depends on USB_GADGET_DWC_HDRC
+	default n
+	help
+	  Bit fields in the Device EP Transfer Size Register is 11 bits.
diff --git a/drivers/usb/dwc/Makefile b/drivers/usb/dwc/Makefile
new file mode 100644
index 0000000..4102add
--- /dev/null
+++ b/drivers/usb/dwc/Makefile
@@ -0,0 +1,19 @@
+#
+# OTG infrastructure and transceiver drivers
+#
+obj-$(CONFIG_USB_DWC_OTG)	+= dwc.o
+
+dwc-objs := cil.o cil_intr.o param.o
+
+ifeq ($(CONFIG_4xx_SOC),y)
+dwc-objs += apmppc.o
+endif
+
+ifneq ($(CONFIG_DWC_DEVICE_ONLY),y)
+dwc-objs += hcd.o hcd_intr.o \
+		hcd_queue.o
+endif
+
+ifneq ($(CONFIG_DWC_HOST_ONLY),y)
+dwc-objs += pcd.o pcd_intr.o
+endif
-- 
1.7.2.2

^ permalink raw reply related

* [PATCH 03/14] dwc/otg: Add driver framework
From: Pratyush Anand @ 2011-08-30 11:57 UTC (permalink / raw)
  To: linux-usb
  Cc: Pratyush Anand, viresh.kumar, vipulkumar.samar, bhupesh.sharma,
	tmarri, vipin.kumar, shiraz.hashim, Amit.VIRDI, rajeev-dlh.kumar,
	mmiesfeld, deepak.sikri, linuxppc-dev, fchen
In-Reply-To: <cover.1314704557.git.pratyush.anand@st.com>

From: Tirumala Marri <tmarri@apm.com>

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

Signed-off-by: Tirumala R Marri <tmarri@apm.com>
Signed-off-by: Fushen Chen <fchen@apm.com>
Signed-off-by: Mark Miesfeld <mmiesfeld@apm.com>
Signed-off-by: Pratyush Anand <pratyush.anand@st.com>
---
 drivers/usb/dwc/apmppc.c |  436 ++++++++++++++++++++++++++++++++++++++++++++++
 drivers/usb/dwc/driver.h |   76 ++++++++
 drivers/usb/dwc/param.c  |  219 +++++++++++++++++++++++
 3 files changed, 731 insertions(+), 0 deletions(-)
 create mode 100644 drivers/usb/dwc/apmppc.c
 create mode 100644 drivers/usb/dwc/driver.h
 create mode 100644 drivers/usb/dwc/param.c

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

^ 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