* Re: [PATCH v2 5/5] xhci: Remove recursive call to xhci_handle_event
From: Sarah Sharp @ 2011-03-29 18:34 UTC (permalink / raw)
To: Benjamin Herrenschmidt
Cc: linuxppc-dev, linux-usb, Sergei Shtylyov, Matt Evans
In-Reply-To: <1301356734.2402.657.camel@pasglop>
On Tue, Mar 29, 2011 at 10:58:54AM +1100, Benjamin Herrenschmidt wrote:
> On Mon, 2011-03-28 at 15:34 -0700, Sarah Sharp wrote:
> >
> > What I'd like to do is take out the read of the status register out of
> > the interrupt handler (which is killing performance), and make it only
> > check the status register when xhci_handle_event() returns a negative
> > error status. If the status register shows the host controller has a
> > critical error, the driver should call usb_hcd_died().
>
> Be careful with removing that read...
>
> Without MSIs, that read is what guarantees that all pending DMA writes
> by the xHCI have been "flushed" before you start poking at memory.
>
> IE. If the chip writes an event and sends an LSI, without that read, you
> might get the interrupt before the writes to memory have completed and
> your driver will "miss" the event.
>
> With MSIs (provided they are not broken on your PCI host bridge of
> course, this is typically the #1 cause of MSI breakage), you don't need
> that as the MSI itself is a DMA store by the device which is ordered
> after the stores done to update the event. So by the time you get the
> MSI interrupt, you -should- have all the updates visible in memory.
>
> But that means that your PCI host bridge is doing the right thing, by
> ensuring whatever queues to the coherency domain it has have been
> properly flushed before it signals the interrupts caused by the MSI to
> the processors. Hopefully most systems get that right nowadays.
>
> Point is: you need to keep that read if MSIs aren't enabled.
Sorry for the sloppy language, yes, I understand I still need the status
register read if only legacy IRQs are enabled.
Sarah Sharp
^ permalink raw reply
* Re: [PATCH 3/4] KVM: PPC: e500: Introduce msr_block for e500v2
From: Scott Wood @ 2011-03-29 17:26 UTC (permalink / raw)
To: Alexander Graf; +Cc: linuxppc-dev
In-Reply-To: <76DF6B63-BCFC-4B8D-80A8-6C1578C6A22C@suse.de>
On Tue, 29 Mar 2011 11:37:44 +0200
Alexander Graf <agraf@suse.de> wrote:
>
> On 28.03.2011, at 21:25, Scott Wood wrote:
>
> > From: yu liu <yu.liu@freescale.com>
> >
> > In order to use lazy SPE register save/restore, we need to
> > know when the guest is using MSR[SPE]. In order to do that, we
> > need to control the actual MSR[SPE] separately from the guest's
> > notion of MSR[SPE].
>
> Please make this generic for BookE. There's no reason to have FSL ifdefs in the code.
Yeah, I was wondering about that too. Will respin.
-Scott
^ permalink raw reply
* Re: [PATCH 4/4] KVM: PPC: e500: SPE switch between guest and host
From: Scott Wood @ 2011-03-29 17:25 UTC (permalink / raw)
To: Alexander Graf; +Cc: linuxppc-dev
In-Reply-To: <6785E5D0-DEE9-4661-88A4-B35A99DB3C66@suse.de>
On Tue, 29 Mar 2011 11:47:51 +0200
Alexander Graf <agraf@suse.de> wrote:
> On 28.03.2011, at 21:25, Scott Wood wrote:
>
> > @@ -344,10 +345,19 @@ int kvmppc_handle_exit(struct kvm_run *run, struct kvm_vcpu *vcpu,
> > r = RESUME_GUEST;
> > break;
> >
> > - case BOOKE_INTERRUPT_SPE_UNAVAIL:
> > - kvmppc_booke_queue_irqprio(vcpu, BOOKE_IRQPRIO_SPE_UNAVAIL);
> > +#ifdef CONFIG_SPE
> > + case BOOKE_INTERRUPT_SPE_UNAVAIL: {
>
> This means that on non-SPE enabled host kernels you'll get a KVM fatal error when the guest triggers SPE_UNAVAIL, as the exit simply isn't handled and triggers:
>
> default:
> printk(KERN_EMERG "exit_nr %d\n", exit_nr);
> BUG();
Right, thanks for spotting -- will have it send an illegal instruction trap
to the guest in that case.
-Scott
^ permalink raw reply
* Re: How to define an I2C-to-SPI bridge device ?
From: Andre Schwarz @ 2011-03-29 16:21 UTC (permalink / raw)
To: Grant Likely; +Cc: LinuxPPC List
In-Reply-To: <4D8C603F.6060501@matrix-vision.de>
On 03/25/2011 10:28 AM, Andre Schwarz wrote:
> Grant, Anton,
got it.
providing modalis = "spidev" within spi_board_info works like a charme ...
Cheers,
André
>
>>>> we're about to get new MPC8377 based hardware with various
>>>> peripherals.
>>>>> There are two I2C-to-SPI bridge devices (NXP SC18IS602) and I'm
>>>>> not sure
>>>>> how to define a proper dts...
>>>>>
>>>>> Of course it's an easy thing creating 2 child nodes on the CPU's I2C
>>>>> device - but how can I represent the created SPI bus ?
>>>> Um.. the same as the other SPI buses? I.e.
>>>>
>>>> i2c-controller { /* SOC I2C controller */
>>>> spi-controller { /* The I2C-to-SPI bridge */
>>>> spi-device@0 {
>>>> };
>>>> spi-device@1 {
>>>> };
>>>> };
>>>> };
>>>>
>>> ok , thanks - looks straight forward.
>>> Is this any more than plain definition, i.e. will this trigger any I2C
>>> or SPI device registration/linking ?
>>>>> Is the (possibly) required driver (of_sc18is60x_spi ?) supposed to
>>>>> be an
>>>>> I2C slave or an SPI host driver ?
>>>> It should be an I2C driver that registers an SPI master (i.e.
>>>> calls spi_alloc_master() and spi_register_master()).
>>> hmm - ok. Will have to do it manually then ...
>> Yes, but this is the case for non-of drivers too. The i2c to spi
>> device driver must always create (and trigger population of) the spi
>> bus instance.
>>
>
> I've kicked that I2C-to-SPI stuff completely because it's been too slow.
> We've connected the SPI busses to the FPGA controlling almost
> everything now.
>
> Unfortunately there are some questions left :
>
> Following Anton's suggestion I've had a look at /drivers/mfd (sm501.c)
> and implemented a pci driver for the FPGA using
> subdevices for additional functionality it exports - besides others we
> now have 2 SPI masters.
>
> For both SPI masters I have created and registered a platform_device.
> pdev->dev is then fed into spi_alloc_master() and the resulting master
> goes into spi_register_master().
>
> master->bus_num is set to 0 and 1, i.e. no dynamic numbering.
> master->chipselect = 8;
>
> Since I can probe the SPI device using FPGA intrinsic information I
> decided to register the client
> devices on runtime using a "struct spi_board_info" which is fed into
> spi_new_device().
>
> The current design has 2 clients on SPI-0 and 5 clients on SPI-1.
>
> static struct spi_board_info mergerbox_spi_boardinfo[] = {
> { .bus_num = 0,
> .chip_select = 0,
> .max_speed_hz = 4<<20, },
> { .bus_num = 0,
> .chip_select = 1,
> .max_speed_hz = 4<<20, },
> { .bus_num = 1,
> .chip_select = 0,
> .max_speed_hz = 4<<20, },
> .....
>
>
> After loading my module I get :
>
> #> ls /sys/devices/platform/AlteraSPI.0/
> /sys/devices/platform/AlteraSPI.0/modalias
> /sys/devices/platform/AlteraSPI.0/spi0.0/
> /sys/devices/platform/AlteraSPI.0/spi0.1/
> /sys/devices/platform/AlteraSPI.0/spi_master/
> /sys/devices/platform/AlteraSPI.0/subsystem/
> /sys/devices/platform/AlteraSPI.0/uevent
>
> #> ls /sys/devices/platform/AlteraSPI.1/
> /sys/devices/platform/AlteraSPI.1/modalias
> /sys/devices/platform/AlteraSPI.1/spi1.0/
> /sys/devices/platform/AlteraSPI.1/spi1.1/
> /sys/devices/platform/AlteraSPI.1/spi1.2/
> /sys/devices/platform/AlteraSPI.1/spi1.3/
> /sys/devices/platform/AlteraSPI.1/spi1.4/
> /sys/devices/platform/AlteraSPI.1/spi_master/
> /sys/devices/platform/AlteraSPI.1/subsystem/
> /sys/devices/platform/AlteraSPI.1/uevent
>
> What I'm missing are the /dev/spi* entries.
>
> There's also a spi_mpc8xxx driver using the CPU's SPI controller.
> It is configured by dts (2 devices) and uses dynamic bus numbering:
>
> #> ls /sys/bus/spi/devices/
> spi0.0 spi1.0 spi1.2 spi1.4 spi32766.1
> spi0.1 spi1.1 spi1.3 spi32766.0
>
> This devices are processed by spidev and proper entries are created
> ready for use :
>
> #> ls /dev/spi*
> /dev/spidev32766.0 /dev/spidev32766.1
>
>
> Am I missing somehting obvious ?
>
> Since there's no driver registration we don't have a probe function -
> is this a problem regarding device binding ?
>
> Do I need to use the .modalias in "struct spi_board_info" ?
>
>
> Any help is welcome.
>
> Regards,
> André
MATRIX VISION GmbH, Talstrasse 16, DE-71570 Oppenweiler
Registergericht: Amtsgericht Stuttgart, HRB 271090
Geschaeftsfuehrer: Gerhard Thullner, Werner Armingeon, Uwe Furtner
^ permalink raw reply
* [PATCH V2] POWER: perf_event: Skip updating kernel counters if register value shrinks
From: Eric B Munson @ 2011-03-29 14:51 UTC (permalink / raw)
To: benh
Cc: a.p.zijlstra, linux-kernel, paulus, anton, acme, mingo,
linuxppc-dev, stable, Eric B Munson
Because of speculative event roll back, it is possible for some event coutners
to decrease between reads on POWER7. This causes a problem with the way that
counters are updated. Delta calues are calculated in a 64 bit value and the
top 32 bits are masked. If the register value has decreased, this leaves us
with a very large positive value added to the kernel counters. This patch
protects against this by skipping the update if the delta would be negative.
This can lead to a lack of precision in the coutner values, but from my testing
the value is typcially fewer than 10 samples at a time.
Signed-off-by: Eric B Munson <emunson@mgebm.net>
Cc: stable@kernel.org
---
Changes from V1:
Updated patch leader
Added stable CC
Use an s32 to hold delta values and discard any values that are less than 0
arch/powerpc/kernel/perf_event.c | 34 +++++++++++++++++++++++++++-------
1 files changed, 27 insertions(+), 7 deletions(-)
diff --git a/arch/powerpc/kernel/perf_event.c b/arch/powerpc/kernel/perf_event.c
index 97e0ae4..0a5178f 100644
--- a/arch/powerpc/kernel/perf_event.c
+++ b/arch/powerpc/kernel/perf_event.c
@@ -416,6 +416,15 @@ static void power_pmu_read(struct perf_event *event)
prev = local64_read(&event->hw.prev_count);
barrier();
val = read_pmc(event->hw.idx);
+ /*
+ * POWER7 can roll back counter values, if the new value is
+ * smaller than the previous value it will cause the delta
+ * and the counter to have bogus values. If this is the
+ * case skip updating anything until the counter grows again.
+ * This can lead to a small lack of precision in the counters.
+ */
+ if (val < prev)
+ return;
} while (local64_cmpxchg(&event->hw.prev_count, prev, val) != prev);
/* The counters are only 32 bits wide */
@@ -439,7 +448,8 @@ static void freeze_limited_counters(struct cpu_hw_events *cpuhw,
unsigned long pmc5, unsigned long pmc6)
{
struct perf_event *event;
- u64 val, prev, delta;
+ u64 val, prev;
+ s32 delta;
int i;
for (i = 0; i < cpuhw->n_limited; ++i) {
@@ -449,8 +459,13 @@ static void freeze_limited_counters(struct cpu_hw_events *cpuhw,
val = (event->hw.idx == 5) ? pmc5 : pmc6;
prev = local64_read(&event->hw.prev_count);
event->hw.idx = 0;
- delta = (val - prev) & 0xfffffffful;
- local64_add(delta, &event->count);
+ /*
+ * The PerfMon registers are only 32 bits wide so the
+ * delta should not overflow.
+ */
+ delta = val - prev;
+ if (delta > 0)
+ local64_add(delta, &event->count);
}
}
@@ -458,14 +473,16 @@ static void thaw_limited_counters(struct cpu_hw_events *cpuhw,
unsigned long pmc5, unsigned long pmc6)
{
struct perf_event *event;
- u64 val;
+ u64 val, prev;
int i;
for (i = 0; i < cpuhw->n_limited; ++i) {
event = cpuhw->limited_counter[i];
event->hw.idx = cpuhw->limited_hwidx[i];
val = (event->hw.idx == 5) ? pmc5 : pmc6;
- local64_set(&event->hw.prev_count, val);
+ prev = local64_read(&event->hw.prev_count);
+ if (val > prev)
+ local64_set(&event->hw.prev_count, val);
perf_event_update_userpage(event);
}
}
@@ -1187,7 +1204,8 @@ static void record_and_restart(struct perf_event *event, unsigned long val,
struct pt_regs *regs, int nmi)
{
u64 period = event->hw.sample_period;
- s64 prev, delta, left;
+ s64 prev, left;
+ s32 delta;
int record = 0;
if (event->hw.state & PERF_HES_STOPPED) {
@@ -1197,7 +1215,9 @@ static void record_and_restart(struct perf_event *event, unsigned long val,
/* we don't have to worry about interrupts here */
prev = local64_read(&event->hw.prev_count);
- delta = (val - prev) & 0xfffffffful;
+ delta = val - prev;
+ if (delta < 0)
+ delta = 0;
local64_add(delta, &event->count);
/*
--
1.7.1
^ permalink raw reply related
* Re: [PATCH] POWER: perf_event: Skip updating kernel counters if register value shrinks
From: Eric B Munson @ 2011-03-29 14:25 UTC (permalink / raw)
To: Benjamin Herrenschmidt
Cc: a.p.zijlstra, linux-kernel, paulus, anton, acme, mingo,
linuxppc-dev
In-Reply-To: <1301378637.2402.671.camel@pasglop>
[-- Attachment #1: Type: text/plain, Size: 2077 bytes --]
On Tue, 29 Mar 2011, Benjamin Herrenschmidt wrote:
> On Fri, 2011-03-25 at 09:28 -0400, Eric B Munson wrote:
> > It is possible on POWER7 for some perf events to have values decrease. This
> > causes a problem with the way the kernel counters are updated. Deltas are
> > computed and then stored in a 64 bit value while the registers are 32 bits
> > wide so if new value is smaller than previous value, the delta is a very
> > large positive value. As a work around this patch skips updating the kernel
> > counter in when the new value is smaller than the previous. This can lead to
> > a lack of precision in the coutner values, but from my testing the value is
> > typcially fewer than 10 samples at a time.
>
> Unfortunately the patch isn't 100% correct I believe:
>
> I think you don't deal with the rollover of the counters. The new value
> could be smaller than the previous one simply because the counter just
> rolled over.
>
> In cases like this:
>
> > @@ -449,8 +458,10 @@ static void freeze_limited_counters(struct cpu_hw_events *cpuhw,
> > val = (event->hw.idx == 5) ? pmc5 : pmc6;
> > prev = local64_read(&event->hw.prev_count);
> > event->hw.idx = 0;
> > - delta = (val - prev) & 0xfffffffful;
> > - local64_add(delta, &event->count);
> > + if (val >= prev) {
> > + delta = (val - prev) & 0xfffffffful;
> > + local64_add(delta, &event->count);
> > + }
> > }
> > }
>
> I wonder if it isn't easier to just define delta to be a s32, get rid
> of the mask and test if delta is positive, something like:
>
> delta = val - prev;
> if (delta > 0)
> local64_add(delta, &event->count);
>
> Wouldn't that be simpler ? Or do I miss a reason why it wouldn't work ?
Here I made the assumption that the hardware would never remove more events in
a speculative roll back than it had added. This is not a situation I
encoutered in my limited testing, so I didn't think underflow was possible. I
will send out a V2 using the signed 32 bit delta and remeber to CC stable
this time.
Eric
[-- Attachment #2: Digital signature --]
[-- Type: application/pgp-signature, Size: 490 bytes --]
^ permalink raw reply
* Re: [PATCH v10 10/10] USB ppc4xx: Add Synopsys DWC OTG driver kernel configuration and Makefile
From: Sergei Shtylyov @ 2011-03-29 12:01 UTC (permalink / raw)
To: tmarri; +Cc: Mark Miesfeld, greg, linux-usb, linuxppc-dev, Fushen Chen
In-Reply-To: <1301336896-2300-1-git-send-email-tmarri@apm.com>
Hello.
On 28-03-2011 22:28, tmarri@apm.com wrote:
> 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>
This patch should precede patch 9 as patch 9 uses config. options defined
here.
> diff --git a/drivers/usb/otg/dwc/Kconfig b/drivers/usb/otg/dwc/Kconfig
> new file mode 100644
> index 0000000..a8f22cb
> --- /dev/null
> +++ b/drivers/usb/otg/dwc/Kconfig
> @@ -0,0 +1,88 @@
> +#
> +# USB Dual Role (OTG-ready) Controller Drivers
> +# for silicon based on Synopsys DesignWare IP
> +#
[...]
> +# enable peripheral support (including with OTG)
> +config USB_GADGET_DWC_HDRC
> + bool
> + depends on USB_DWC_OTG && (DWC_DEVICE_ONLY || USB_DWC_OTG)
Haven't we just defined this in patch 9? Redefinition of options isn't
correct.
> +config DWC_OTG_REG_LE
> + bool "DWC Little Endian Register"
This should preferrably be passed via the platform data, I think.
> + 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"
This too.
> + depends on USB_DWC_OTG
> + default n
"default n" not needed.
> + help
> + OTG core FIFO access is Little-Endian.
Little endian registers and big endian FIFO by default?
> +
> +config DWC_LIMITED_XFER_SIZE
> + bool "DWC Endpoint Limited Xfer Size"
> + depends on USB_GADGET_DWC_HDRC
> + default n
Not needed.
> + help
> + Bit fields in the Device EP Transfer Size Register is 11 bits.
WBR, Sergei
^ permalink raw reply
* Re: [PATCH v10 09/10] USB/ppc4xx:Synopsys DWC OTG driver enable gadget support
From: Sergei Shtylyov @ 2011-03-29 11:53 UTC (permalink / raw)
To: tmarri; +Cc: Mark Miesfeld, greg, linux-usb, linuxppc-dev, Fushen Chen
In-Reply-To: <1301336892-2268-1-git-send-email-tmarri@apm.com>
Hello.
On 28-03-2011 22:28, tmarri@apm.com 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>
[...]
> diff --git a/drivers/usb/gadget/Kconfig b/drivers/usb/gadget/Kconfig
> index bc5123c..61aca75 100644
> --- a/drivers/usb/gadget/Kconfig
> +++ b/drivers/usb/gadget/Kconfig
> @@ -365,6 +365,28 @@ config USB_GADGET_MUSB_HDRC
> This OTG-capable silicon IP is used in dual designs including
> the TI DaVinci, OMAP 243x, OMAP 343x, TUSB 6010, and ADI Blackfin
>
> +# dwc_otg builds in ../dwc_otg along with host support
There's no ../dwc_otg any longer.
> +config USB_GADGET_DWC_HDRC
> + boolean "DesignWare USB Peripheral"
> + depends on DWC_OTG_MODE || DWC_DEVICE_ONLY
> + select USB_GADGET_DUALSPEED
> + select USB_GADGET_SELECTED
> + select USB_GADGET_DWC_OTG
> + help
> + This OTG-capable Designware USB IP
> +
> +config USB_GADGET_DWC_OTG
> + boolean "OTG Support"
> + depends on USB_GADGET_DWC_HDRC
Not on DWC_OTG_MODE? How this option is different from DWC_OTG_MODE?
> + help
> + The most notable feature of USB OTG is support for a
> + "Dual-Role" device, which can act as either a device
> + or a host. The initial role choice can be changed
> + later, when two dual-role devices talk to each other.
> + Select this only if your board has a Mini-AB connector.
> +
> +
> +
Too many empty lines.
> config USB_GADGET_M66592
> boolean "Renesas M66592 USB Peripheral Controller"
> select USB_GADGET_DUALSPEED
WBR, Sergei
^ permalink raw reply
* RE: [PATCH v10 00/10] Add-Synopsys-DesignWare-HS-USB-OTG-driver
From: Keshava Munegowda @ 2011-03-29 10:58 UTC (permalink / raw)
To: tmarri, linux-usb, linuxppc-dev; +Cc: greg
In-Reply-To: <1301336856-1802-1-git-send-email-tmarri@apm.com>
> -----Original Message-----
> From: linux-usb-owner@vger.kernel.org
[mailto:linux-usb-owner@vger.kernel.org] On Behalf Of
> tmarri@apm.com
> Sent: Monday, March 28, 2011 11:58 PM
> To: linux-usb@vger.kernel.org; linuxppc-dev@lists.ozlabs.org
> Cc: greg@kroah.com; tmarri@apm.com
> Subject: [PATCH v10 00/10] Add-Synopsys-DesignWare-HS-USB-OTG-driver
>
> From: Tirumala Marri <tmarri@apm.com>
>
> v10:
> 1. Change address argument type from u32 to ulong to
> make it arch independent.
> 2. Cleaning un-necasary changes from Kconfig.
>
> v9:
> 1. Move the source under usb/otg/dwc.
> 2. Rename the source files with out dwc_otg prefix.
> 3. Remove unused read/write modify functions.
> 4. Including changes related to register base print.
>
> Tirumala Marri (10):
> USB/ppc4xx: Add Synopsys DWC OTG Register definitions
> USB/ppc4xx: Add Synopsys DWC OTG driver framework
> USB/ppc4xx: Add Synopsys DWC OTG Core Interface Layer (CIL)
> USB/ppc4xx: Add Synopsys DWC OTG HCD function
> USB/ppc4xx: Add Synopsys DWC OTG HCD interrupt function
> USB/ppc4xx: Add Synopsys DWC OTG HCD queue function
> USB/ppc4xx: Add Synopsys DWC OTG PCD function
> USB ppc4xx: Add Synopsys DWC OTG PCD interrupt function
> USB/ppc4xx:Synopsys DWC OTG driver enable gadget support
> USB ppc4xx: Add Synopsys DWC OTG driver kernel configuration and
> Makefile
>
> drivers/Makefile | 1 +
> drivers/usb/Kconfig | 2 +
> drivers/usb/gadget/Kconfig | 22 +
> drivers/usb/gadget/gadget_chips.h | 9 +
> drivers/usb/otg/dwc/Kconfig | 88 ++
> drivers/usb/otg/dwc/Makefile | 19 +
> drivers/usb/otg/dwc/apmppc.c | 414 +++++++
> drivers/usb/otg/dwc/cil.c | 972 +++++++++++++++
> drivers/usb/otg/dwc/cil.h | 1177 ++++++++++++++++++
> drivers/usb/otg/dwc/cil_intr.c | 616 +++++++++
> drivers/usb/otg/dwc/driver.h | 76 ++
> drivers/usb/otg/dwc/hcd.c | 2465
+++++++++++++++++++++++++++++++++++++
> drivers/usb/otg/dwc/hcd.h | 416 +++++++
> drivers/usb/otg/dwc/hcd_intr.c | 1477 ++++++++++++++++++++++
> drivers/usb/otg/dwc/hcd_queue.c | 696 +++++++++++
> drivers/usb/otg/dwc/param.c | 180 +++
> drivers/usb/otg/dwc/pcd.c | 1766 ++++++++++++++++++++++++++
> drivers/usb/otg/dwc/pcd.h | 139 +++
> drivers/usb/otg/dwc/pcd_intr.c | 2311
++++++++++++++++++++++++++++++++++
> drivers/usb/otg/dwc/regs.h | 1326 ++++++++++++++++++++
> 20 files changed, 14172 insertions(+), 0 deletions(-)
> create mode 100644 drivers/usb/otg/dwc/Kconfig
> create mode 100644 drivers/usb/otg/dwc/Makefile
> create mode 100644 drivers/usb/otg/dwc/apmppc.c
> create mode 100644 drivers/usb/otg/dwc/cil.c
> create mode 100644 drivers/usb/otg/dwc/cil.h
> create mode 100644 drivers/usb/otg/dwc/cil_intr.c
> create mode 100644 drivers/usb/otg/dwc/driver.h
> create mode 100644 drivers/usb/otg/dwc/hcd.c
> create mode 100644 drivers/usb/otg/dwc/hcd.h
> create mode 100644 drivers/usb/otg/dwc/hcd_intr.c
> create mode 100644 drivers/usb/otg/dwc/hcd_queue.c
> create mode 100644 drivers/usb/otg/dwc/param.c
> create mode 100644 drivers/usb/otg/dwc/pcd.c
> create mode 100644 drivers/usb/otg/dwc/pcd.h
> create mode 100644 drivers/usb/otg/dwc/pcd_intr.c
> create mode 100644 drivers/usb/otg/dwc/regs.h
>
> --
Just a cosmetic review comment: you need to Tabify the declarations of in
the functions, at least when you are declaring variable of different data
types
> 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 http://vger.kernel.org/majordomo-info.html
^ permalink raw reply
* RE: [PATCH v10 04/10] USB/ppc4xx: Add Synopsys DWC OTG HCD function
From: Keshava Munegowda @ 2011-03-29 10:55 UTC (permalink / raw)
To: tmarri, linux-usb, linuxppc-dev; +Cc: greg, Mark Miesfeld, Fushen Chen
In-Reply-To: <1301336869-1944-1-git-send-email-tmarri@apm.com>
> -----Original Message-----
> From: linux-usb-owner@vger.kernel.org
[mailto:linux-usb-owner@vger.kernel.org] On Behalf Of
> tmarri@apm.com
> Sent: Monday, March 28, 2011 11:58 PM
> To: linux-usb@vger.kernel.org; linuxppc-dev@lists.ozlabs.org
> Cc: greg@kroah.com; tmarri@apm.com; Fushen Chen; Mark Miesfeld
> Subject: [PATCH v10 04/10] USB/ppc4xx: Add Synopsys DWC OTG HCD function
>
> From: Tirumala Marri <tmarri@apm.com>
>
> Implements DWC OTG USB Host Controller Driver (HCD) and interface to
> USB Host controller Driver framework.
>
> Signed-off-by: Tirumala R Marri <tmarri@apm.com>
> Signed-off-by: Fushen Chen <fchen@apm.com>
> Signed-off-by: Mark Miesfeld <mmiesfeld@apm.com>
> ---
> drivers/usb/otg/dwc/hcd.c | 2465
+++++++++++++++++++++++++++++++++++++++++++++
> drivers/usb/otg/dwc/hcd.h | 416 ++++++++
> 2 files changed, 2881 insertions(+), 0 deletions(-)
> create mode 100644 drivers/usb/otg/dwc/hcd.c
> create mode 100644 drivers/usb/otg/dwc/hcd.h
>
> diff --git a/drivers/usb/otg/dwc/hcd.c b/drivers/usb/otg/dwc/hcd.c
> new file mode 100644
> index 0000000..884bf31
> --- /dev/null
> +++ b/drivers/usb/otg/dwc/hcd.c
> @@ -0,0 +1,2465 @@
> +/*
> + * DesignWare HS OTG controller driver
> + * Copyright (C) 2006 Synopsys, Inc.
> + * Portions Copyright (C) 2010 Applied Micro Circuits Corporation.
> + *
> + * This program is free software: you can redistribute it and/or
> + * modify it under the terms of the GNU General Public License
> + * version 2 as published by the Free Software Foundation.
> + *
> + * This program is distributed in the hope that it will be useful
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
> + * GNU General Public License version 2 for more details.
> + *
> + * You should have received a copy of the GNU General Public License
> + * along with this program; if not, see http://www.gnu.org/licenses
> + * or write to the Free Software Foundation, Inc., 51 Franklin Street,
> + * Suite 500, Boston, MA 02110-1335 USA.
> + *
> + * Based on Synopsys driver version 2.60a
> + * Modified by Mark Miesfeld <mmiesfeld@apm.com>
> + * Modified by Stefan Roese <sr@denx.de>, DENX Software Engineering
> + * Modified by Chuck Meade <chuck@theptrgroup.com>
> + *
> + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS"
> + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING BUT NOT LIMITED TO
THE
> + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
PURPOSE
> + * ARE DISCLAIMED. IN NO EVENT SHALL SYNOPSYS, INC. BE LIABLE FOR ANY
DIRECT,
> + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES
> + * (INCLUDING BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES;
> + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND
> + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY OR
TORT
> + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
USE OF
> + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
> + *
> + */
> +
> +/*
> + * This file contains the implementation of the HCD. In Linux, the HCD
> + * implements the hc_driver API.
> + */
> +
> +#include <asm/unaligned.h>
> +#include <linux/dma-mapping.h>
> +
> +#include "hcd.h"
> +
> +static const char dwc_otg_hcd_name[] = "dwc_otg_hcd";
> +
> +/**
> + * Clears the transfer state for a host channel. This function is
normally
> + * called after a transfer is done and the host channel is being
released. It
> + * clears the channel interrupt enables and any unhandled channel
interrupt
> + * conditions.
> + */
> +void dwc_otg_hc_cleanup(struct core_if *core_if, struct dwc_hc *hc)
> +{
> + ulong regs;
> +
> + hc->xfer_started = 0;
> + regs = core_if->host_if->hc_regs[hc->hc_num];
> + dwc_write32(regs + DWC_HCINTMSK, 0);
> + dwc_write32(regs + DWC_HCINT, 0xFFFFFFFF);
> +}
> +
> +/**
> + * This function enables the Host mode interrupts.
> + */
> +static void dwc_otg_enable_host_interrupts(struct core_if *core_if)
> +{
> + ulong global_regs = core_if->core_global_regs;
> + u32 intr_mask = 0;
> +
> + /* Disable all interrupts. */
> + dwc_write32(global_regs + DWC_GINTMSK, 0);
> +
> + /* Clear any pending interrupts. */
> + dwc_write32(global_regs + DWC_GINTSTS, 0xFFFFFFFF);
> +
> + /* Enable the common interrupts */
> + dwc_otg_enable_common_interrupts(core_if);
> +
> + /*
> + * Enable host mode interrupts without disturbing common
> + * interrupts.
> + */
> + intr_mask |= DWC_INTMSK_STRT_OF_FRM;
> + intr_mask |= DWC_INTMSK_HST_PORT;
> + intr_mask |= DWC_INTMSK_HST_CHAN;
> + dwc_modify32(global_regs + DWC_GINTMSK, intr_mask, intr_mask);
> +}
> +
> +/**
> + * This function initializes the DWC_otg controller registers for
> + * host mode.
> + *
> + * This function flushes the Tx and Rx FIFOs and it flushes any entries
in the
> + * request queues. Host channels are reset to ensure that they are
ready for
> + * performing transfers.
> + */
> +static void dwc_otg_core_host_init(struct core_if *core_if)
> +{
> + ulong global_regs = core_if->core_global_regs;
> + struct dwc_host_if *host_if = core_if->host_if;
> + struct core_params *params = core_if->core_params;
> + u32 hprt0 = 0;
> + u32 nptxfifosize = 0;
> + u32 ptxfifosize = 0;
> + u32 i;
> + u32 hcchar;
> + ulong hcfg;
> + ulong hc_regs;
> + int num_channels;
> + u32 gotgctl = 0;
Tabify the declarations ;
> +
> + /* Restart the Phy Clock */
> + dwc_write32(core_if->pcgcctl, 0);
> +
> + /* Initialize Host Configuration Register */
> + init_fslspclksel(core_if);
> + if (core_if->core_params->speed == DWC_SPEED_PARAM_FULL) {
> + hcfg = dwc_read32(host_if->host_global_regs + DWC_HCFG);
> + hcfg = DWC_HCFG_FSLSUPP_RW(hcfg, 1);
> + dwc_write32(host_if->host_global_regs + DWC_HCFG, hcfg);
> + }
> +
> + /* Configure data FIFO sizes */
> + if (DWC_HWCFG2_DYN_FIFO_RD(core_if->hwcfg2)
> + && params->enable_dynamic_fifo) {
> + /* Rx FIFO */
> + dwc_write32(global_regs + DWC_GRXFSIZ,
> + params->host_rx_fifo_size);
> +
> + /* Non-periodic Tx FIFO */
> + nptxfifosize = DWC_RX_FIFO_DEPTH_WR(nptxfifosize,
> + params->
> +
host_nperio_tx_fifo_size);
> + nptxfifosize =
> + DWC_RX_FIFO_START_ADDR_WR(nptxfifosize,
> + params->host_rx_fifo_size);
> + dwc_write32(global_regs + DWC_GNPTXFSIZ, nptxfifosize);
> +
> + /* Periodic Tx FIFO */
> + ptxfifosize = DWC_RX_FIFO_DEPTH_WR(ptxfifosize,
> + params->
> +
host_perio_tx_fifo_size);
> + ptxfifosize =
> + DWC_RX_FIFO_START_ADDR_WR(ptxfifosize,
> + (DWC_RX_FIFO_START_ADDR_RD
> + (nptxfifosize) +
> + DWC_RX_FIFO_DEPTH_RD
> + (nptxfifosize)));
> + dwc_write32(global_regs + DWC_HPTXFSIZ, ptxfifosize);
> + }
> +
> + /* Clear Host Set HNP Enable in the OTG Control Register */
> + gotgctl |= DWC_GCTL_HOST_HNP_ENA;
> + dwc_modify32(global_regs + DWC_GOTGCTL, gotgctl, 0);
> +
> + /* Make sure the FIFOs are flushed. */
> + dwc_otg_flush_tx_fifo(core_if, DWC_GRSTCTL_TXFNUM_ALL);
> + dwc_otg_flush_rx_fifo(core_if);
> +
> + /* Flush out any leftover queued requests. */
> + num_channels = core_if->core_params->host_channels;
> + for (i = 0; i < num_channels; i++) {
> + hc_regs = core_if->host_if->hc_regs[i];
> + hcchar = dwc_read32(hc_regs + DWC_HCCHAR);
> + hcchar = DWC_HCCHAR_ENA_RW(hcchar, 0);
> + hcchar = DWC_HCCHAR_DIS_RW(hcchar, 1);
> + hcchar = DWC_HCCHAR_EPDIR_RW(hcchar, 0);
> + dwc_write32(hc_regs + DWC_HCCHAR, hcchar);
> + }
> +
> + /* Halt all channels to put them into a known state. */
> + for (i = 0; i < num_channels; i++) {
> + int count = 0;
> +
> + hc_regs = core_if->host_if->hc_regs[i];
> + hcchar = dwc_read32(hc_regs + DWC_HCCHAR);
> + hcchar = DWC_HCCHAR_ENA_RW(hcchar, 1);
> + hcchar = DWC_HCCHAR_DIS_RW(hcchar, 1);
> + hcchar = DWC_HCCHAR_EPDIR_RW(hcchar, 0);
> + dwc_write32(hc_regs + DWC_HCCHAR, hcchar);
> +
> + do {
> + hcchar = dwc_read32(hc_regs + DWC_HCCHAR);
> + if (++count > 200) {
> + pr_err("%s: Unable to clear halt on "
> + "channel %d\n", __func__, i);
> + break;
> + }
> + udelay(100);
> + } while (DWC_HCCHAR_ENA_RD(hcchar));
> + }
> +
> + /* Turn on the vbus power. */
> + pr_info("Init: Port Power? op_state=%s\n",
> + op_state_str(core_if->xceiv->state));
> +
> + if (core_if->xceiv->state == OTG_STATE_A_HOST) {
> + hprt0 = dwc_otg_read_hprt0(core_if);
> + pr_info("Init: Power Port (%d)\n",
DWC_HPRT0_PRT_PWR_RD(hprt0));
> + if (DWC_HPRT0_PRT_PWR_RD(hprt0) == 0) {
> + hprt0 = DWC_HPRT0_PRT_PWR_RW(hprt0, 1);
> + dwc_write32(host_if->hprt0, hprt0);
> + }
> + }
> + dwc_otg_enable_host_interrupts(core_if);
> +}
> +
> +/**
> + * Initializes dynamic portions of the DWC_otg HCD state.
> + */
> +static void hcd_reinit(struct dwc_hcd *hcd)
> +{
> + struct list_head *item;
> + int num_channels;
> + u32 i;
> + struct dwc_hc *channel;
Tabify the declarations ;
> +
> + hcd->flags.d32 = 0;
> + hcd->non_periodic_qh_ptr = &hcd->non_periodic_sched_active;
> + hcd->available_host_channels =
hcd->core_if->core_params->host_channels;
> +
> + /*
> + * Put all channels in the free channel list and clean up channel
> + * states.
> + */
> + item = hcd->free_hc_list.next;
> + while (item != &hcd->free_hc_list) {
> + list_del(item);
> + item = hcd->free_hc_list.next;
> + }
> +
> + num_channels = hcd->core_if->core_params->host_channels;
> + for (i = 0; i < num_channels; i++) {
> + channel = hcd->hc_ptr_array[i];
> + list_add_tail(&channel->hc_list_entry,
&hcd->free_hc_list);
> + dwc_otg_hc_cleanup(hcd->core_if, channel);
> + }
> +
> + /* Initialize the DWC core for host mode operation. */
> + dwc_otg_core_host_init(hcd->core_if);
> +}
> +
> +/* Gets the dwc_hcd from a struct usb_hcd */
> +static inline struct dwc_hcd *hcd_to_dwc_otg_hcd(struct usb_hcd *hcd)
> +{
> + return (struct dwc_hcd *)hcd->hcd_priv;
> +}
> +
> +/**
> + * Initializes the DWC_otg controller and its root hub and prepares it
for host
> + * mode operation. Activates the root port. Returns 0 on success and a
negative
> + * error code on failure.
> +*/
> +static int dwc_otg_hcd_start(struct usb_hcd *hcd)
> +{
> + struct dwc_hcd *dwc_hcd = hcd_to_dwc_otg_hcd(hcd);
> + struct usb_bus *bus = hcd_to_bus(hcd);
> +
> + hcd->state = HC_STATE_RUNNING;
> +
> + /* Inform the HUB driver to resume. */
> + if (bus->root_hub)
> + usb_hcd_resume_root_hub(hcd);
> +
> + hcd_reinit(dwc_hcd);
> + return 0;
> +}
> +
> +/**
> + * Work queue function for starting the HCD when A-Cable is connected.
> + * The dwc_otg_hcd_start() must be called in a process context.
> + */
> +static void hcd_start_func(struct work_struct *work)
> +{
> + struct dwc_hcd *priv = container_of(work, struct dwc_hcd,
start_work);
> + struct usb_hcd *usb_hcd = (struct usb_hcd *)priv->_p;
> +
> + if (usb_hcd)
> + dwc_otg_hcd_start(usb_hcd);
> +}
> +
> +/**
> + * HCD Callback function for starting the HCD when A-Cable is
> + * connected.
> + */
> +static int dwc_otg_hcd_start_cb(void *_p)
> +{
> + struct dwc_hcd *dwc_hcd = hcd_to_dwc_otg_hcd(_p);
> + struct core_if *core_if = dwc_hcd->core_if;
> + u32 hprt0;
Tabify the declarations ;
> +
> + if (core_if->xceiv->state == OTG_STATE_B_HOST) {
> + /*
> + * Reset the port. During a HNP mode switch the reset
> + * needs to occur within 1ms and have a duration of at
> + * least 50ms.
> + */
> + hprt0 = dwc_otg_read_hprt0(core_if);
> + hprt0 = DWC_HPRT0_PRT_RST_RW(hprt0, 1);
> + dwc_write32(core_if->host_if->hprt0, hprt0);
> + ((struct usb_hcd *)_p)->self.is_b_host = 1;
> + } else {
> + ((struct usb_hcd *)_p)->self.is_b_host = 0;
> + }
> +
> + /* Need to start the HCD in a non-interrupt context. */
> + dwc_hcd->_p = _p;
> + schedule_work(&dwc_hcd->start_work);
> + return 1;
> +}
> +
> +/**
> + * This function disables the Host Mode interrupts.
> + */
> +static void dwc_otg_disable_host_interrupts(struct core_if *core_if)
> +{
> + u32 global_regs = core_if->core_global_regs;
> + u32 intr_mask = 0;
> +
> + /*
> + * Disable host mode interrupts without disturbing common
> + * interrupts.
> + */
> + intr_mask |= DWC_INTMSK_STRT_OF_FRM;
> + intr_mask |= DWC_INTMSK_HST_PORT;
> + intr_mask |= DWC_INTMSK_HST_CHAN;
> + intr_mask |= DWC_INTMSK_P_TXFIFO_EMPTY;
> + intr_mask |= DWC_INTMSK_NP_TXFIFO_EMPT;
> + dwc_modify32(global_regs + DWC_GINTMSK, intr_mask, 0);
> +}
> +
> +/**
> + * Halts the DWC_otg host mode operations in a clean manner. USB
transfers are
> + * stopped.
> + */
> +static void dwc_otg_hcd_stop(struct usb_hcd *hcd)
> +{
> + struct dwc_hcd *dwc_hcd = hcd_to_dwc_otg_hcd(hcd);
> + u32 hprt0 = 0;
> +
> + /* Turn off all host-specific interrupts. */
> + dwc_otg_disable_host_interrupts(dwc_hcd->core_if);
> +
> + /*
> + * The root hub should be disconnected before this function is
called.
> + * The disconnect will clear the QTD lists (via
..._hcd_urb_dequeue)
> + * and the QH lists (via ..._hcd_endpoint_disable).
> + */
> +
> + /* Turn off the vbus power */
> + pr_info("PortPower off\n");
> + hprt0 = DWC_HPRT0_PRT_PWR_RW(hprt0, 0);
> + dwc_write32(dwc_hcd->core_if->host_if->hprt0, hprt0);
> +}
> +
> +/**
> + * HCD Callback function for stopping the HCD.
> + */
> +static int dwc_otg_hcd_stop_cb(void *_p)
> +{
> + struct usb_hcd *usb_hcd = (struct usb_hcd *)_p;
> +
> + dwc_otg_hcd_stop(usb_hcd);
> + return 1;
> +}
> +
> +static void del_timers(struct dwc_hcd *hcd)
> +{
> + del_timer_sync(&hcd->conn_timer);
> +}
> +
> +/**
> + * Processes all the URBs in a single list of QHs. Completes them with
> + * -ETIMEDOUT and frees the QTD.
> + */
> +static void kill_urbs_in_qh_list(struct dwc_hcd *hcd, struct list_head
*qh_list)
> +{
> + struct list_head *qh_item, *q;
> +
> + qh_item = qh_list->next;
> + list_for_each_safe(qh_item, q, qh_list) {
> + struct dwc_qh *qh;
> + struct list_head *qtd_item;
> + struct dwc_qtd *qtd;
> +
> + qh = list_entry(qh_item, struct dwc_qh, qh_list_entry);
> + qtd_item = qh->qtd_list.next;
> + qtd = list_entry(qtd_item, struct dwc_qtd,
qtd_list_entry);
> + if (qtd->urb != NULL) {
> + spin_lock(&hcd->lock);
> + dwc_otg_hcd_complete_urb(hcd, qtd->urb,
-ETIMEDOUT);
> + dwc_otg_hcd_qtd_remove_and_free(qtd);
> + spin_unlock(&hcd->lock);
> + }
> + }
> +}
> +
> +/**
> + * Responds with an error status of ETIMEDOUT to all URBs in the
non-periodic
> + * and periodic schedules. The QTD associated with each URB is removed
from
> + * the schedule and freed. This function may be called when a
disconnect is
> + * detected or when the HCD is being stopped.
> + */
> +static void kill_all_urbs(struct dwc_hcd *hcd)
> +{
> + kill_urbs_in_qh_list(hcd, &hcd->non_periodic_sched_deferred);
> + kill_urbs_in_qh_list(hcd, &hcd->non_periodic_sched_inactive);
> + kill_urbs_in_qh_list(hcd, &hcd->non_periodic_sched_active);
> + kill_urbs_in_qh_list(hcd, &hcd->periodic_sched_inactive);
> + kill_urbs_in_qh_list(hcd, &hcd->periodic_sched_ready);
> + kill_urbs_in_qh_list(hcd, &hcd->periodic_sched_assigned);
> + kill_urbs_in_qh_list(hcd, &hcd->periodic_sched_queued);
> +}
> +
> +/**
> + * HCD Callback function for disconnect of the HCD.
> + */
> +static int dwc_otg_hcd_disconnect_cb(void *_p)
> +{
> + u32 intr;
> + struct dwc_hcd *hcd = hcd_to_dwc_otg_hcd(_p);
> + struct core_if *core_if = hcd->core_if;
> +
> + /* Set status flags for the hub driver. */
> + hcd->flags.b.port_connect_status_change = 1;
> + hcd->flags.b.port_connect_status = 0;
> +
> + /*
> + * Shutdown any transfers in process by clearing the Tx FIFO Empty
> + * interrupt mask and status bits and disabling subsequent host
> + * channel interrupts.
> + */
> + intr = 0;
> + intr |= DWC_INTMSK_NP_TXFIFO_EMPT;
> + intr |= DWC_INTMSK_P_TXFIFO_EMPTY;
> + intr |= DWC_INTMSK_HST_CHAN;
> + dwc_modify32(gintmsk_reg(hcd), intr, 0);
> + dwc_modify32(gintsts_reg(hcd), intr, 0);
> +
> + del_timers(hcd);
> +
> + /*
> + * Turn off the vbus power only if the core has transitioned to
device
> + * mode. If still in host mode, need to keep power on to detect a
> + * reconnection.
> + */
> + if (dwc_otg_is_device_mode(core_if)) {
> + if (core_if->xceiv->state != OTG_STATE_A_SUSPEND) {
> + u32 hprt0 = 0;
> +
> + pr_info("Disconnect: PortPower off\n");
> + hprt0 = DWC_HPRT0_PRT_PWR_RW(hprt0, 0);
> + dwc_write32(core_if->host_if->hprt0, hprt0);
> + }
> + dwc_otg_disable_host_interrupts(core_if);
> + }
> +
> + /* Respond with an error status to all URBs in the schedule. */
> + kill_all_urbs(hcd);
> + if (dwc_otg_is_host_mode(core_if)) {
> + /* Clean up any host channels that were in use. */
> + int num_channels;
> + u32 i;
> + struct dwc_hc *channel;
> + ulong regs;
> + u32 hcchar;
> +
> + num_channels = core_if->core_params->host_channels;
> + if (!core_if->dma_enable) {
> + /* Flush out any channel requests in slave mode.
*/
> + for (i = 0; i < num_channels; i++) {
> + channel = hcd->hc_ptr_array[i];
> + if (list_empty(&channel->hc_list_entry)) {
> + regs =
> + core_if->host_if->hc_regs[i];
> + hcchar = dwc_read32(regs +
DWC_HCCHAR);
> +
> + if (DWC_HCCHAR_ENA_RD(hcchar)) {
> + hcchar =
> +
DWC_HCCHAR_ENA_RW(hcchar,
> + 0);
> + hcchar =
> +
DWC_HCCHAR_DIS_RW(hcchar,
> + 1);
> + hcchar =
> +
DWC_HCCHAR_EPDIR_RW(hcchar,
> +
0);
> + dwc_write32(regs +
DWC_HCCHAR,
> + hcchar);
> + }
> + }
> + }
> + }
> +
> + for (i = 0; i < num_channels; i++) {
> + channel = hcd->hc_ptr_array[i];
> + if (list_empty(&channel->hc_list_entry)) {
> + regs = core_if->host_if->hc_regs[i];
> + hcchar = dwc_read32(regs + DWC_HCCHAR);
> +
> + if (DWC_HCCHAR_ENA_RD(hcchar)) {
> + /* Halt the channel. */
> + hcchar = DWC_HCCHAR_DIS_RW(hcchar,
1);
> + dwc_write32(regs + DWC_HCCHAR,
hcchar);
> + }
> + dwc_otg_hc_cleanup(core_if, channel);
> + list_add_tail(&channel->hc_list_entry,
> + &hcd->free_hc_list);
> + }
> + }
> + }
> +
> + /*
> + * A disconnect will end the session so the B-Device is no
> + * longer a B-host.
> + */
> + ((struct usb_hcd *)_p)->self.is_b_host = 0;
> + return 1;
> +}
> +
> +/**
> + * Connection timeout function. An OTG host is required to display a
> + * message if the device does not connect within 10 seconds.
> + */
> +static void dwc_otg_hcd_connect_timeout(unsigned long _ptr)
> +{
> + pr_info("Connect Timeout\n");
> + pr_err("Device Not Connected/Responding\n");
> +}
> +
> +/**
> + * Start the connection timer. An OTG host is required to display a
> + * message if the device does not connect within 10 seconds. The
> + * timer is deleted if a port connect interrupt occurs before the
> + * timer expires.
> + */
> +static void dwc_otg_hcd_start_connect_timer(struct dwc_hcd *hcd)
> +{
> + init_timer(&hcd->conn_timer);
> + hcd->conn_timer.function = dwc_otg_hcd_connect_timeout;
> + hcd->conn_timer.data = (unsigned long)0;
> + hcd->conn_timer.expires = jiffies + (HZ * 10);
> + add_timer(&hcd->conn_timer);
> +}
> +
> +/**
> + * HCD Callback function for disconnect of the HCD.
> + */
> +static int dwc_otg_hcd_session_start_cb(void *_p)
> +{
> + struct dwc_hcd *hcd = hcd_to_dwc_otg_hcd(_p);
> +
> + dwc_otg_hcd_start_connect_timer(hcd);
> + return 1;
> +}
> +
> +/* HCD Callback structure for handling mode switching. */
> +static struct cil_callbacks hcd_cil_callbacks = {
> + .start = dwc_otg_hcd_start_cb,
> + .stop = dwc_otg_hcd_stop_cb,
> + .disconnect = dwc_otg_hcd_disconnect_cb,
> + .session_start = dwc_otg_hcd_session_start_cb,
> + .p = NULL,
> +};
> +
> +/*
> + * Reset Workqueue implementation
> + */
> +static void port_reset_wqfunc(struct work_struct *work)
> +{
> + struct dwc_hcd *hcd = container_of(work, struct dwc_hcd,
> + usb_port_reset);
> + struct core_if *core_if = hcd->core_if;
> + u32 hprt0 = 0;
> + unsigned long flags;
> +
> + pr_info("%s\n", __func__);
> + spin_lock_irqsave(&hcd->lock, flags);
> + hprt0 = dwc_otg_read_hprt0(core_if);
> + hprt0 = DWC_HPRT0_PRT_RST_RW(hprt0, 1);
> + dwc_write32(core_if->host_if->hprt0, hprt0);
> + spin_unlock_irqrestore(&hcd->lock, flags);
> + msleep(60);
> + spin_lock_irqsave(&hcd->lock, flags);
> + hprt0 = DWC_HPRT0_PRT_RST_RW(hprt0, 0);
> + dwc_write32(core_if->host_if->hprt0, hprt0);
> + hcd->flags.b.port_reset_change = 1;
> + spin_unlock_irqrestore(&hcd->lock, flags);
> +}
> +
> +/*
> + * Wakeup Workqueue implementation
> + */
> +static void port_wakeup_wqfunc(struct work_struct *work)
> +{
> + struct core_if *core_if = container_of(to_delayed_work(work),
> + struct core_if,
usb_port_wakeup);
> + u32 hprt0;
> +
> + pr_info("%s\n", __func__);
> + /* Now wait for 70 ms. */
> + hprt0 = dwc_otg_read_hprt0(core_if);
> + msleep(70);
> + hprt0 = DWC_HPRT0_PRT_RES_RW(hprt0, 0);
> + dwc_write32(core_if->host_if->hprt0, hprt0);
> +}
> +
> +/**
> + * Starts processing a USB transfer request specified by a USB Request
Block
> + * (URB). mem_flags indicates the type of memory allocation to use
while
> + * processing this URB.
> + */
> +static int dwc_otg_hcd_urb_enqueue(struct usb_hcd *hcd, struct urb
*urb,
> + gfp_t _mem_flags)
> +{
> + int retval;
> + unsigned long flags;
> + struct dwc_hcd *dwc_hcd = hcd_to_dwc_otg_hcd(hcd);
> + struct dwc_qtd *qtd;
> +
> + if (!dwc_hcd->flags.b.port_connect_status) {
> + /* No longer connected. */
> + retval = -ENODEV;
> + goto err_enq;
> + }
> +
> + qtd = dwc_otg_hcd_qtd_create(urb, _mem_flags);
> + if (!qtd) {
> + pr_err("DWC OTG HCD URB Enqueue failed creating "
"QTD\n");
> + retval = -ENOMEM;
> + goto err_enq;
> + }
> +
> + spin_lock_irqsave(&dwc_hcd->lock, flags);
> + retval = usb_hcd_link_urb_to_ep(hcd, urb);
> + if (unlikely(retval))
> + goto fail;
> +
> + retval = dwc_otg_hcd_qtd_add(qtd, dwc_hcd);
> + if (retval < 0) {
> + pr_err("DWC OTG HCD URB Enqueue failed adding QTD. "
> + "Error status %d\n", retval);
> + usb_hcd_unlink_urb_from_ep(hcd, urb);
> + goto fail;
> + }
> +
> +fail:
> + if (retval)
> + dwc_otg_hcd_qtd_free(qtd);
> +
> + spin_unlock_irqrestore(&dwc_hcd->lock, flags);
> +err_enq:
> +
> + return retval;
> +}
> +
> +/**
> + * Attempts to halt a host channel. This function should only be called
in
> + * Slave mode or to abort a transfer in either Slave mode or DMA mode.
Under
> + * normal circumstances in DMA mode, the controller halts the channel
when the
> + * transfer is complete or a condition occurs that requires application
> + * intervention.
> + *
> + * In slave mode, checks for a free request queue entry, then sets the
Channel
> + * Enable and Channel Disable bits of the Host Channel Characteristics
> + * register of the specified channel to intiate the halt. If there is
no free
> + * request queue entry, sets only the Channel Disable bit of the
HCCHARn
> + * register to flush requests for this channel. In the latter case,
sets a
> + * flag to indicate that the host channel needs to be halted when a
request
> + * queue slot is open.
> + *
> + * In DMA mode, always sets the Channel Enable and Channel Disable bits
of the
> + * HCCHARn register. The controller ensures there is space in the
request
> + * queue before submitting the halt request.
> + *
> + * Some time may elapse before the core flushes any posted requests for
this
> + * host channel and halts. The Channel Halted interrupt handler
completes the
> + * deactivation of the host channel.
> + */
> +void dwc_otg_hc_halt(struct core_if *core_if, struct dwc_hc *hc,
> + enum dwc_halt_status hlt_sts)
> +{
> + u32 nptxsts;
> + u32 hptxsts = 0;
> + u32 hcchar;
> + ulong hc_regs;
> + ulong global_regs = core_if->core_global_regs;
> + ulong host_global_regs;
> +
> + hc_regs = core_if->host_if->hc_regs[hc->hc_num];
> + host_global_regs = core_if->host_if->host_global_regs;
> +
> + WARN_ON(hlt_sts == DWC_OTG_HC_XFER_NO_HALT_STATUS);
> +
> + if (hlt_sts == DWC_OTG_HC_XFER_URB_DEQUEUE ||
> + hlt_sts == DWC_OTG_HC_XFER_AHB_ERR) {
> + /*
> + * Disable all channel interrupts except Ch Halted. The
QTD
> + * and QH state associated with this transfer has been
cleared
> + * (in the case of URB_DEQUEUE), so the channel needs to
be
> + * shut down carefully to prevent crashes.
> + */
> + u32 hcintmsk;
> + hcintmsk = 0;
> + hcintmsk = DWC_HCINTMSK_CHAN_HALTED_RW(hcintmsk, 1);
> + dwc_write32(hc_regs + DWC_HCINTMSK, hcintmsk);
> +
> + /*
> + * Make sure no other interrupts besides halt are
currently
> + * pending. Handling another interrupt could cause a crash
due
> + * to the QTD and QH state.
> + */
> + dwc_write32(hc_regs + DWC_HCINT, ~hcintmsk);
> +
> + /*
> + * Make sure the halt status is set to URB_DEQUEUE or
AHB_ERR
> + * even if the channel was already halted for some other
reason.
> + */
> + hc->halt_status = hlt_sts;
> +
> + /*
> + * If the channel is not enabled, the channel is either
already
> + * halted or it hasn't started yet. In DMA mode, the
transfer
> + * may halt if it finishes normally or a condition occurs
that
> + * requires driver intervention. Don't want to halt the
channel
> + * again. In either Slave or DMA mode, it's possible that
the
> + * transfer has been assigned to a channel, but not
started yet
> + * when an URB is dequeued. Don't want to halt a channel
that
> + * hasn't started yet.
> + */
> + hcchar = dwc_read32(hc_regs + DWC_HCCHAR);
> + if (!DWC_HCCHAR_ENA_RD(hcchar))
> + return;
> + }
> +
> + if (hc->halt_pending)
> + /*
> + * A halt has already been issued for this channel. This
might
> + * happen when a transfer is aborted by a higher level in
> + * the stack.
> + */
> + return;
> +
> + hcchar = dwc_read32(hc_regs + DWC_HCCHAR);
> + hcchar = DWC_HCCHAR_ENA_RW(hcchar, 1);
> + hcchar = DWC_HCCHAR_DIS_RW(hcchar, 1);
> + if (!core_if->dma_enable) {
> + /* Check for space in the request queue to issue the halt.
*/
> + if (hc->ep_type == DWC_OTG_EP_TYPE_CONTROL ||
> + hc->ep_type == DWC_OTG_EP_TYPE_BULK) {
> + nptxsts = dwc_read32(global_regs + DWC_GNPTXSTS);
> +
> + if (!DWC_GNPTXSTS_NPTXQSPCAVAIL_RD(nptxsts))
> + hcchar = DWC_HCCHAR_ENA_RW(hcchar, 0);
> + } else {
> + hptxsts =
> + dwc_read32(host_global_regs + DWC_HPTXSTS);
> +
> + if (!DWC_HPTXSTS_PTXSPC_AVAIL_RD(hptxsts) ||
> + core_if->queuing_high_bandwidth)
> + hcchar = DWC_HCCHAR_ENA_RW(hcchar, 0);
> + }
> + }
> + dwc_write32(hc_regs + DWC_HCCHAR, hcchar);
> +
> + hc->halt_status = hlt_sts;
> + if (DWC_HCCHAR_ENA_RD(hcchar)) {
> + hc->halt_pending = 1;
> + hc->halt_on_queue = 0;
> + } else {
> + hc->halt_on_queue = 1;
> + }
> +}
> +
> +/**
> + * Aborts/cancels a USB transfer request. Always returns 0 to indicate
> + * success.
> + */
> +static int dwc_otg_hcd_urb_dequeue(struct usb_hcd *hcd, struct urb
*urb,
> + int status)
> +{
> + unsigned long flags;
> + struct dwc_hcd *dwc_hcd;
> + struct dwc_qtd *urb_qtd;
> + struct dwc_qh *qh;
> + int retval;
> +
> + urb_qtd = (struct dwc_qtd *)urb->hcpriv;
> + if (!urb_qtd)
> + return -EINVAL;
> + qh = (struct dwc_qh *)urb_qtd->qtd_qh_ptr;
> + if (!qh)
> + return -EINVAL;
> +
> + dwc_hcd = hcd_to_dwc_otg_hcd(hcd);
> + spin_lock_irqsave(&dwc_hcd->lock, flags);
> +
> + retval = usb_hcd_check_unlink_urb(hcd, urb, status);
> + if (retval) {
> + spin_unlock_irqrestore(&dwc_hcd->lock, flags);
> + return retval;
> + }
> +
> + if (urb_qtd == qh->qtd_in_process) {
> + /* The QTD is in process (it has been assigned to a
channel). */
> + if (dwc_hcd->flags.b.port_connect_status) {
> + /*
> + * If still connected (i.e. in host mode), halt
the
> + * channel so it can be used for other transfers.
If
> + * no longer connected, the host registers can't
be
> + * written to halt the channel since the core is
in
> + * device mode.
> + */
> + dwc_otg_hc_halt(dwc_hcd->core_if, qh->channel,
> + DWC_OTG_HC_XFER_URB_DEQUEUE);
> + }
> + }
> +
> + /*
> + * Free the QTD and clean up the associated QH. Leave the QH in
the
> + * schedule if it has any remaining QTDs.
> + */
> + dwc_otg_hcd_qtd_remove_and_free(urb_qtd);
> + if (qh && urb_qtd == qh->qtd_in_process) {
> + dwc_otg_hcd_qh_deactivate(dwc_hcd, qh, 0);
> + qh->channel = NULL;
> + qh->qtd_in_process = NULL;
> + } else if (qh && list_empty(&qh->qtd_list)) {
> + dwc_otg_hcd_qh_remove(dwc_hcd, qh);
> + }
> +
> + urb->hcpriv = NULL;
> + usb_hcd_unlink_urb_from_ep(hcd, urb);
> + spin_unlock_irqrestore(&dwc_hcd->lock, flags);
> +
> + /* Higher layer software sets URB status. */
> + usb_hcd_giveback_urb(hcd, urb, status);
> +
> + return 0;
> +}
> +
> +/* Remove and free a QH */
> +static inline void dwc_otg_hcd_qh_remove_and_free(struct dwc_hcd *hcd,
> + struct dwc_qh *qh)
> +{
> + dwc_otg_hcd_qh_remove(hcd, qh);
> + dwc_otg_hcd_qh_free(qh);
> +}
> +
> +static void qh_list_free(struct dwc_hcd *hcd, struct list_head
*_qh_list)
> +{
> + struct list_head *item, *tmp;
> + struct dwc_qh *qh;
> +
> + /* If the list hasn't been initialized yet, return. */
> + if (_qh_list->next == NULL)
> + return;
> +
> + /* Ensure there are no QTDs or URBs left. */
> + kill_urbs_in_qh_list(hcd, _qh_list);
> +
> + list_for_each_safe(item, tmp, _qh_list) {
> + qh = list_entry(item, struct dwc_qh, qh_list_entry);
> + dwc_otg_hcd_qh_remove_and_free(hcd, qh);
> + }
> +}
> +
> +/**
> + * Frees resources in the DWC_otg controller related to a given
endpoint. Also
> + * clears state in the HCD related to the endpoint. Any URBs for the
endpoint
> + * must already be dequeued.
> + */
> +static void dwc_otg_hcd_endpoint_disable(struct usb_hcd *hcd,
> + struct usb_host_endpoint *ep)
> +{
> + struct dwc_qh *qh;
> + struct dwc_hcd *dwc_hcd = hcd_to_dwc_otg_hcd(hcd);
> + unsigned long flags;
Tabify the declarations ;
> +
> + spin_lock_irqsave(&dwc_hcd->lock, flags);
> + qh = (struct dwc_qh *)ep->hcpriv;
> + if (qh) {
> + dwc_otg_hcd_qh_remove_and_free(dwc_hcd, qh);
> + ep->hcpriv = NULL;
> + }
> + spin_unlock_irqrestore(&dwc_hcd->lock, flags);
> +}
> +
> +/**
> + * Creates Status Change bitmap for the root hub and root port. The
bitmap is
> + * returned in buf. Bit 0 is the status change indicator for the root
hub. Bit 1
> + * is the status change indicator for the single root port. Returns 1
if either
> + * change indicator is 1, otherwise returns 0.
> + */
> +static int dwc_otg_hcd_hub_status_data(struct usb_hcd *_hcd, char *buf)
> +{
> + struct dwc_hcd *hcd = hcd_to_dwc_otg_hcd(_hcd);
> +
> + buf[0] = 0;
> + buf[0] |= (hcd->flags.b.port_connect_status_change
> + || hcd->flags.b.port_reset_change
> + || hcd->flags.b.port_enable_change
> + || hcd->flags.b.port_suspend_change
> + || hcd->flags.b.port_over_current_change) << 1;
> +
> + return (buf[0] != 0);
> +}
> +
> +/* Handles the hub class-specific ClearPortFeature request.*/
> +static int do_clear_port_feature(struct dwc_hcd *hcd, u16 val)
> +{
> + struct core_if *core_if = hcd->core_if;
> + u32 hprt0 = 0;
> + unsigned long flags;
> +
> + spin_lock_irqsave(&hcd->lock, flags);
> + switch (val) {
> + case USB_PORT_FEAT_ENABLE:
> + hprt0 = dwc_otg_read_hprt0(core_if);
> + hprt0 = DWC_HPRT0_PRT_ENA_RW(hprt0, 1);
> + dwc_write32(core_if->host_if->hprt0, hprt0);
> + break;
> + case USB_PORT_FEAT_SUSPEND:
> + hprt0 = dwc_otg_read_hprt0(core_if);
> + hprt0 = DWC_HPRT0_PRT_RES_RW(hprt0, 1);
> + dwc_write32(core_if->host_if->hprt0, hprt0);
> +
> + /* Clear Resume bit */
> + spin_unlock_irqrestore(&hcd->lock, flags);
> + msleep(100);
> + spin_lock_irqsave(&hcd->lock, flags);
> + hprt0 = DWC_HPRT0_PRT_RES_RW(hprt0, 0);
> + dwc_write32(core_if->host_if->hprt0, hprt0);
> + break;
> + case USB_PORT_FEAT_POWER:
> + hprt0 = dwc_otg_read_hprt0(core_if);
> + hprt0 = DWC_HPRT0_PRT_PWR_RW(hprt0, 0);
> + dwc_write32(core_if->host_if->hprt0, hprt0);
> + break;
> + case USB_PORT_FEAT_INDICATOR:
> + /* Port inidicator not supported */
> + break;
> + case USB_PORT_FEAT_C_CONNECTION:
> + /* Clears drivers internal connect status change flag */
> + hcd->flags.b.port_connect_status_change = 0;
> + break;
> + case USB_PORT_FEAT_C_RESET:
> + /* Clears driver's internal Port Reset Change flag */
> + hcd->flags.b.port_reset_change = 0;
> + break;
> + case USB_PORT_FEAT_C_ENABLE:
> + /* Clears driver's internal Port Enable/Disable Change
flag */
> + hcd->flags.b.port_enable_change = 0;
> + break;
> + case USB_PORT_FEAT_C_SUSPEND:
> + /*
> + * Clears the driver's internal Port Suspend
> + * Change flag, which is set when resume signaling on
> + * the host port is complete
> + */
> + hcd->flags.b.port_suspend_change = 0;
> + break;
> + case USB_PORT_FEAT_C_OVER_CURRENT:
> + hcd->flags.b.port_over_current_change = 0;
> + break;
> + default:
> + pr_err("DWC OTG HCD - ClearPortFeature request %xh "
> + "unknown or unsupported\n", val);
> + spin_unlock_irqrestore(&hcd->lock, flags);
> + return -EINVAL;
> + }
> + spin_unlock_irqrestore(&hcd->lock, flags);
> + return 0;
> +}
> +
> +/* Handles the hub class-specific SetPortFeature request.*/
> +static int do_set_port_feature(struct usb_hcd *hcd, u16 val, u16 index)
> +{
> + struct core_if *core_if = hcd_to_dwc_otg_hcd(hcd)->core_if;
> + u32 hprt0 = 0;
> + struct dwc_hcd *dwc_hcd = hcd_to_dwc_otg_hcd(hcd);
> + unsigned long flags;
> + u32 pcgcctl = 0;
Tabify the declarations ;
> +
> + spin_lock_irqsave(&dwc_hcd->lock, flags);
> +
> + switch (val) {
> + case USB_PORT_FEAT_SUSPEND:
> + if (hcd->self.otg_port == index && hcd->self.b_hnp_enable)
{
> + u32 gotgctl = 0;
> + gotgctl |= DWC_GCTL_HOST_HNP_ENA;
> + dwc_modify32(core_if->core_global_regs +
> + DWC_GOTGCTL, 0, gotgctl);
> + core_if->xceiv->state = OTG_STATE_A_SUSPEND;
> + }
> +
> + hprt0 = dwc_otg_read_hprt0(core_if);
> + hprt0 = DWC_HPRT0_PRT_SUS_RW(hprt0, 1);
> + dwc_write32(core_if->host_if->hprt0, hprt0);
> +
> + /* Suspend the Phy Clock */
> + pcgcctl = DWC_PCGCCTL_STOP_CLK_SET(pcgcctl);
> + dwc_write32(core_if->pcgcctl, pcgcctl);
> +
> + /* For HNP the bus must be suspended for at least 200ms.
*/
> + if (hcd->self.b_hnp_enable) {
> + spin_unlock_irqrestore(&dwc_hcd->lock, flags);
> + msleep(200);
> + spin_lock_irqsave(&dwc_hcd->lock, flags);
> + }
> + break;
> + case USB_PORT_FEAT_POWER:
> + hprt0 = dwc_otg_read_hprt0(core_if);
> + hprt0 = DWC_HPRT0_PRT_PWR_RW(hprt0, 1);
> + dwc_write32(core_if->host_if->hprt0, hprt0);
> + break;
> + case USB_PORT_FEAT_RESET:
> + hprt0 = dwc_otg_read_hprt0(core_if);
> +
> + /*
> + * When B-Host the Port reset bit is set in the Start HCD
> + * Callback function, so that the reset is started within
1ms
> + * of the HNP success interrupt.
> + */
> + if (!hcd->self.is_b_host) {
> + hprt0 = DWC_HPRT0_PRT_RST_RW(hprt0, 1);
> + dwc_write32(core_if->host_if->hprt0, hprt0);
> + }
> +
> + /* Clear reset bit in 10ms (FS/LS) or 50ms (HS) */
> + spin_unlock_irqrestore(&dwc_hcd->lock, flags);
> + msleep(60);
> + spin_lock_irqsave(&dwc_hcd->lock, flags);
> + hprt0 = DWC_HPRT0_PRT_RST_RW(hprt0, 0);
> + dwc_write32(core_if->host_if->hprt0, hprt0);
> + break;
> + case USB_PORT_FEAT_INDICATOR:
> + /* Not supported */
> + break;
> + default:
> + pr_err("DWC OTG HCD - "
> + "SetPortFeature request %xh "
> + "unknown or unsupported\n", val);
> + spin_unlock_irqrestore(&dwc_hcd->lock, flags);
> + return -EINVAL;
> + }
> + spin_unlock_irqrestore(&dwc_hcd->lock, flags);
> + return 0;
> +}
> +
> +/* Handles hub class-specific requests.*/
> +static int dwc_otg_hcd_hub_control(struct usb_hcd *hcd, u16 req_type,
u16 val,
> + u16 index, char *buf, u16 len)
> +{
> + int retval = 0;
> + struct dwc_hcd *dwc_hcd = hcd_to_dwc_otg_hcd(hcd);
> + struct core_if *core_if = hcd_to_dwc_otg_hcd(hcd)->core_if;
> + struct usb_hub_descriptor *desc;
> + u32 hprt0 = 0;
> + u32 port_status;
> + unsigned long flags;
Tabify the declarations ;
> +
> + spin_lock_irqsave(&dwc_hcd->lock, flags);
> + switch (req_type) {
> + case ClearHubFeature:
> + switch (val) {
> + case C_HUB_LOCAL_POWER:
> + case C_HUB_OVER_CURRENT:
> + /* Nothing required here */
> + break;
> + default:
> + retval = -EINVAL;
> + pr_err("DWC OTG HCD - ClearHubFeature request"
> + " %xh unknown\n", val);
> + }
> + break;
> + case ClearPortFeature:
> + if (!index || index > 1)
> + goto error;
> + spin_unlock_irqrestore(&dwc_hcd->lock, flags);
> + retval = do_clear_port_feature(dwc_hcd, val);
> + spin_lock_irqsave(&dwc_hcd->lock, flags);
> + break;
> + case GetHubDescriptor:
> + desc = (struct usb_hub_descriptor *)buf;
> + desc->bDescLength = 9;
> + desc->bDescriptorType = 0x29;
> + desc->bNbrPorts = 1;
> + desc->wHubCharacteristics = 0x08;
> + desc->bPwrOn2PwrGood = 1;
> + desc->bHubContrCurrent = 0;
> + break;
> + case GetHubStatus:
> + memset(buf, 0, 4);
> + break;
> + case GetPortStatus:
> + if (!index || index > 1)
> + goto error;
> +
> + port_status = 0;
> + if (dwc_hcd->flags.b.port_connect_status_change)
> + port_status |= (1 << USB_PORT_FEAT_C_CONNECTION);
> + if (dwc_hcd->flags.b.port_enable_change)
> + port_status |= (1 << USB_PORT_FEAT_C_ENABLE);
> + if (dwc_hcd->flags.b.port_suspend_change)
> + port_status |= (1 << USB_PORT_FEAT_C_SUSPEND);
> + if (dwc_hcd->flags.b.port_reset_change)
> + port_status |= (1 << USB_PORT_FEAT_C_RESET);
> + if (dwc_hcd->flags.b.port_over_current_change) {
> + pr_err("Device Not Supported\n");
> + port_status |= (1 <<
USB_PORT_FEAT_C_OVER_CURRENT);
> + }
> + if (!dwc_hcd->flags.b.port_connect_status) {
> + /*
> + * The port is disconnected, which means the core
is
> + * either in device mode or it soon will be. Just
> + * return 0's for the remainder of the port status
> + * since the port register can't be read if the
core
> + * is in device mode.
> + */
> + *((__le32 *) buf) = cpu_to_le32(port_status);
> + break;
> + }
> +
> + hprt0 = dwc_read32(core_if->host_if->hprt0);
> +
> + if (DWC_HPRT0_PRT_STS_RD(hprt0))
> + port_status |= USB_PORT_STAT_CONNECTION;
> + if (DWC_HPRT0_PRT_ENA_RD(hprt0))
> + port_status |= USB_PORT_STAT_ENABLE;
> + if (DWC_HPRT0_PRT_SUS_RD(hprt0))
> + port_status |= USB_PORT_STAT_SUSPEND;
> + if (DWC_HPRT0_PRT_OVRCURR_ACT_RD(hprt0))
> + port_status |= USB_PORT_STAT_OVERCURRENT;
> + if (DWC_HPRT0_PRT_RST_RD(hprt0))
> + port_status |= USB_PORT_STAT_RESET;
> + if (DWC_HPRT0_PRT_PWR_RD(hprt0))
> + port_status |= USB_PORT_STAT_POWER;
> +
> + if (DWC_HPRT0_PRT_SPD_RD(hprt0) ==
DWC_HPRT0_PRTSPD_HIGH_SPEED)
> + port_status |= USB_PORT_STAT_HIGH_SPEED;
> + else if (DWC_HPRT0_PRT_SPD_RD(hprt0) ==
> + DWC_HPRT0_PRTSPD_LOW_SPEED)
> + port_status |= USB_PORT_STAT_LOW_SPEED;
> +
> + if (DWC_HPRT0_PRT_TST_CTL_RD(hprt0))
> + port_status |= (1 << USB_PORT_FEAT_TEST);
> +
> + /* USB_PORT_FEAT_INDICATOR unsupported always 0 */
> + *((__le32 *) buf) = cpu_to_le32(port_status);
> + break;
> + case SetHubFeature:
> + /* No HUB features supported */
> + break;
> + case SetPortFeature:
> + if (val != USB_PORT_FEAT_TEST && (!index || index > 1))
> + goto error;
> +
> + if (!dwc_hcd->flags.b.port_connect_status) {
> + /*
> + * The port is disconnected, which means the core
is
> + * either in device mode or it soon will be. Just
> + * return without doing anything since the port
> + * register can't be written if the core is in
device
> + * mode.
> + */
> + break;
> + }
> + spin_unlock_irqrestore(&dwc_hcd->lock, flags);
> + retval = do_set_port_feature(hcd, val, index);
> + spin_lock_irqsave(&dwc_hcd->lock, flags);
> + break;
> + default:
> +error:
> + retval = -EINVAL;
> + pr_warning("DWC OTG HCD - Unknown hub control request"
> + " type or invalid req_type: %xh index: %xh "
> + "val: %xh\n", req_type, index, val);
> + break;
> + }
> + spin_unlock_irqrestore(&dwc_hcd->lock, flags);
> + return retval;
> +}
> +
> +/**
> + * Handles host mode interrupts for the DWC_otg controller. Returns
IRQ_NONE if
> + * there was no interrupt to handle. Returns IRQ_HANDLED if there was a
valid
> + * interrupt.
> + *
> + * This function is called by the USB core when an interrupt occurs
> + */
> +static irqreturn_t dwc_otg_hcd_irq(struct usb_hcd *hcd)
> +{
> + struct dwc_hcd *dwc_hcd = hcd_to_dwc_otg_hcd(hcd);
> +
> + return IRQ_RETVAL(dwc_otg_hcd_handle_intr(dwc_hcd));
> +}
> +
> +static const struct hc_driver dwc_otg_hc_driver = {
> + .description = dwc_otg_hcd_name,
> + .product_desc = "DWC OTG Controller",
> + .hcd_priv_size = sizeof(struct dwc_hcd),
> + .irq = dwc_otg_hcd_irq,
> + .flags = HCD_MEMORY | HCD_USB2,
> + .start = dwc_otg_hcd_start,
> + .stop = dwc_otg_hcd_stop,
> + .urb_enqueue = dwc_otg_hcd_urb_enqueue,
> + .urb_dequeue = dwc_otg_hcd_urb_dequeue,
> + .endpoint_disable = dwc_otg_hcd_endpoint_disable,
> + .get_frame_number = dwc_otg_hcd_get_frame_number,
> + .hub_status_data = dwc_otg_hcd_hub_status_data,
> + .hub_control = dwc_otg_hcd_hub_control,
> +};
> +
> +/**
> + * Frees secondary storage associated with the dwc_hcd structure
contained
> + * in the struct usb_hcd field.
> + */
> +static void dwc_otg_hcd_free(struct usb_hcd *hcd)
> +{
> + struct dwc_hcd *dwc_hcd = hcd_to_dwc_otg_hcd(hcd);
> + u32 i;
> +
> + del_timers(dwc_hcd);
> +
> + /* Free memory for QH/QTD lists */
> + qh_list_free(dwc_hcd, &dwc_hcd->non_periodic_sched_inactive);
> + qh_list_free(dwc_hcd, &dwc_hcd->non_periodic_sched_deferred);
> + qh_list_free(dwc_hcd, &dwc_hcd->non_periodic_sched_active);
> + qh_list_free(dwc_hcd, &dwc_hcd->periodic_sched_inactive);
> + qh_list_free(dwc_hcd, &dwc_hcd->periodic_sched_ready);
> + qh_list_free(dwc_hcd, &dwc_hcd->periodic_sched_assigned);
> + qh_list_free(dwc_hcd, &dwc_hcd->periodic_sched_queued);
> +
> + /* Free memory for the host channels. */
> + for (i = 0; i < MAX_EPS_CHANNELS; i++) {
> + struct dwc_hc *hc = dwc_hcd->hc_ptr_array[i];
> +
> + kfree(hc);
> + }
> + if (dwc_hcd->core_if->dma_enable) {
> + if (dwc_hcd->status_buf_dma)
> + dma_free_coherent(hcd->self.controller,
> + DWC_OTG_HCD_STATUS_BUF_SIZE,
> + dwc_hcd->status_buf,
> + dwc_hcd->status_buf_dma);
> + } else {
> + kfree(dwc_hcd->status_buf);
> + }
> +
> +}
> +
> +/**
> + * Initializes the HCD. This function allocates memory for and
initializes the
> + * static parts of the usb_hcd and dwc_hcd structures. It also
registers the
> + * USB bus with the core and calls the hc_driver->start() function. It
returns
> + * a negative error on failure.
> + */
> +int __devinit dwc_otg_hcd_init(struct device *_dev,
> + struct dwc_otg_device *dwc_otg_device)
> +{
> + struct usb_hcd *hcd;
> + struct dwc_hcd *dwc_hcd;
> + struct dwc_otg_device *otg_dev = dev_get_drvdata(_dev);
> + int num_channels;
> + u32 i;
> + struct dwc_hc *channel;
> + int retval = 0;
Tabify the declarations ;
> +
> + /*
> + * Allocate memory for the base HCD plus the DWC OTG HCD.
> + * Initialize the base HCD.
> + */
> + hcd = usb_create_hcd(&dwc_otg_hc_driver, _dev, dwc_otg_hcd_name);
> + if (!hcd) {
> + retval = -ENOMEM;
> + goto error1;
> + }
> + dev_set_drvdata(_dev, dwc_otg_device);
> + hcd->regs = otg_dev->base;
> + hcd->rsrc_start = otg_dev->phys_addr;
> + hcd->rsrc_len = otg_dev->base_len;
> + hcd->self.otg_port = 1;
> +
> + /* Initialize the DWC OTG HCD. */
> + dwc_hcd = hcd_to_dwc_otg_hcd(hcd);
> + dwc_hcd->core_if = otg_dev->core_if;
> + spin_lock_init(&dwc_hcd->lock);
> + otg_dev->hcd = dwc_hcd;
> +
> + /* Register the HCD CIL Callbacks */
> + dwc_otg_cil_register_hcd_callbacks(otg_dev->core_if,
&hcd_cil_callbacks,
> + hcd);
> +
> + /* Initialize the non-periodic schedule. */
> + INIT_LIST_HEAD(&dwc_hcd->non_periodic_sched_inactive);
> + INIT_LIST_HEAD(&dwc_hcd->non_periodic_sched_active);
> + INIT_LIST_HEAD(&dwc_hcd->non_periodic_sched_deferred);
> +
> + /* Initialize the periodic schedule. */
> + INIT_LIST_HEAD(&dwc_hcd->periodic_sched_inactive);
> + INIT_LIST_HEAD(&dwc_hcd->periodic_sched_ready);
> + INIT_LIST_HEAD(&dwc_hcd->periodic_sched_assigned);
> + INIT_LIST_HEAD(&dwc_hcd->periodic_sched_queued);
> +
> + /*
> + * Create a host channel descriptor for each host channel
implemented
> + * in the controller. Initialize the channel descriptor array.
> + */
> + INIT_LIST_HEAD(&dwc_hcd->free_hc_list);
> + num_channels = dwc_hcd->core_if->core_params->host_channels;
> +
> + for (i = 0; i < num_channels; i++) {
> + channel = kzalloc(sizeof(struct dwc_hc), GFP_KERNEL);
> + if (!channel) {
> + retval = -ENOMEM;
> + pr_err("%s: host channel allocation failed\n",
> + __func__);
> + goto error2;
> + }
> +
> + channel->hc_num = i;
> + dwc_hcd->hc_ptr_array[i] = channel;
> + }
> +
> + /* Initialize the Connection timeout timer. */
> + init_timer(&dwc_hcd->conn_timer);
> +
> + /* Initialize workqueue */
> + INIT_WORK(&dwc_hcd->usb_port_reset, port_reset_wqfunc);
> + INIT_WORK(&dwc_hcd->start_work, hcd_start_func);
> + INIT_WORK(&dwc_hcd->core_if->usb_port_otg, NULL);
> + INIT_DELAYED_WORK(&dwc_hcd->core_if->usb_port_wakeup,
> + port_wakeup_wqfunc);
> +
> + /* Set device flags indicating whether the HCD supports DMA. */
> + if (otg_dev->core_if->dma_enable) {
> + static u64 dummy_mask = DMA_BIT_MASK(32);
> +
> + pr_info("Using DMA mode\n");
> + _dev->dma_mask = (void *)&dummy_mask;
> + _dev->coherent_dma_mask = ~0;
> + } else {
> + pr_info("Using Slave mode\n");
> + _dev->dma_mask = (void *)0;
> + _dev->coherent_dma_mask = 0;
> + }
> +
> + init_hcd_usecs(dwc_hcd);
> + /*
> + * Finish generic HCD initialization and start the HCD. This
function
> + * allocates the DMA buffer pool, registers the USB bus, requests
the
> + * IRQ line, and calls dwc_otg_hcd_start method.
> + */
> + retval = usb_add_hcd(hcd, otg_dev->irq, IRQF_SHARED);
> + if (retval < 0)
> + goto error2;
> + hcd->rsrc_start = otg_dev->phys_addr;
> + hcd->rsrc_len = otg_dev->base_len;
> +
> + /*
> + * Allocate space for storing data on status transactions.
Normally no
> + * data is sent, but this space acts as a bit bucket. This must be
> + * done after usb_add_hcd since that function allocates the DMA
buffer
> + * pool.
> + */
> + if (otg_dev->core_if->dma_enable) {
> + dwc_hcd->status_buf =
> + dma_alloc_coherent(_dev, DWC_OTG_HCD_STATUS_BUF_SIZE,
> + &dwc_hcd->status_buf_dma,
> + GFP_KERNEL | GFP_DMA);
> + } else {
> + dwc_hcd->status_buf = kmalloc(DWC_OTG_HCD_STATUS_BUF_SIZE,
> + GFP_KERNEL);
> + }
> + if (!dwc_hcd->status_buf) {
> + retval = -ENOMEM;
> + pr_err("%s: status_buf allocation failed\n", __func__);
> + goto error3;
> + }
> + return 0;
> +
> +error3:
> + usb_remove_hcd(hcd);
> +error2:
> + dwc_otg_hcd_free(hcd);
> + usb_put_hcd(hcd);
> +error1:
> + return retval;
> +}
> +
> +/**
> + * Removes the HCD.
> + * Frees memory and resources associated with the HCD and deregisters
the bus.
> + */
> +void __devexit dwc_otg_hcd_remove(struct device *_dev)
> +{
> + struct dwc_otg_device *otg_dev = dev_get_drvdata(_dev);
> + struct dwc_hcd *dwc_hcd = otg_dev->hcd;
> + struct usb_hcd *hcd = dwc_otg_hcd_to_hcd(dwc_hcd);
> +
> + /* Turn off all interrupts */
> + dwc_write32(gintmsk_reg(dwc_hcd), 0);
> + dwc_modify32(gahbcfg_reg(dwc_hcd), 1, 0);
> +
> + cancel_work_sync(&dwc_hcd->start_work);
> + cancel_work_sync(&dwc_hcd->usb_port_reset);
> + cancel_work_sync(&dwc_hcd->core_if->usb_port_otg);
> +
> + usb_remove_hcd(hcd);
> + dwc_otg_hcd_free(hcd);
> + usb_put_hcd(hcd);
> +}
> +
> +/** Returns the current frame number. */
> +int dwc_otg_hcd_get_frame_number(struct usb_hcd *hcd)
> +{
> + struct dwc_hcd *dwc_hcd = hcd_to_dwc_otg_hcd(hcd);
> + u32 hfnum = 0;
> +
> + hfnum = dwc_read32(dwc_hcd->core_if->host_if->
> + host_global_regs + DWC_HFNUM);
> +
> + return DWC_HFNUM_FRNUM_RD(hfnum);
> +}
> +
> +/**
> + * Prepares a host channel for transferring packets to/from a specific
> + * endpoint. The HCCHARn register is set up with the characteristics
specified
> + * in _hc. Host channel interrupts that may need to be serviced while
this
> + * transfer is in progress are enabled.
> + */
> +static void dwc_otg_hc_init(struct core_if *core_if, struct dwc_hc *hc)
> +{
> + u32 intr_enable;
> + ulong global_regs = core_if->core_global_regs;
> + u32 hc_intr_mask = 0;
> + u32 gintmsk = 0;
> + u32 hcchar;
> + u32 hcsplt;
> + u8 hc_num = hc->hc_num;
> + struct dwc_host_if *host_if = core_if->host_if;
> + ulong hc_regs = host_if->hc_regs[hc_num];
Tabify the declarations ;
> +
> + /* Clear old interrupt conditions for this host channel. */
> + hc_intr_mask = 0x3FF;
> + dwc_write32(hc_regs + DWC_HCINT, hc_intr_mask);
> +
> + /* Enable channel interrupts required for this transfer. */
> + hc_intr_mask = 0;
> + hc_intr_mask = DWC_HCINTMSK_CHAN_HALTED_RW(hc_intr_mask, 1);
> + if (core_if->dma_enable) {
> + hc_intr_mask = DWC_HCINTMSK_AHB_ERR_RW(hc_intr_mask, 1);
> +
> + if (hc->error_state && !hc->do_split &&
> + hc->ep_type != DWC_OTG_EP_TYPE_ISOC) {
> + hc_intr_mask =
> + DWC_HCINTMSK_ACK_RESP_REC_RW(hc_intr_mask, 1);
> + if (hc->ep_is_in) {
> + hc_intr_mask =
> +
DWC_HCINTMSK_DATA_TOG_ERR_RW(hc_intr_mask,
> + 1);
> + if (hc->ep_type != DWC_OTG_EP_TYPE_INTR)
> + hc_intr_mask =
> + DWC_HCINTMSK_NAK_RESP_REC_RW
> + (hc_intr_mask, 1);
> + }
> + }
> + } else {
> + switch (hc->ep_type) {
> + case DWC_OTG_EP_TYPE_CONTROL:
> + case DWC_OTG_EP_TYPE_BULK:
> + hc_intr_mask =
> + DWC_HCINTMSK_TXFER_CMPL_RW(hc_intr_mask, 1);
> + hc_intr_mask =
> + DWC_HCINTMSK_STALL_RESP_REC_RW(hc_intr_mask,
1);
> + hc_intr_mask =
> + DWC_HCINTMSK_TRANS_ERR_RW(hc_intr_mask, 1);
> + hc_intr_mask =
> + DWC_HCINTMSK_DATA_TOG_ERR_RW(hc_intr_mask, 1);
> +
> + if (hc->ep_is_in) {
> + hc_intr_mask =
> + DWC_HCINTMSK_BBL_ERR_RW(hc_intr_mask,
1);
> + } else {
> + hc_intr_mask =
> +
DWC_HCINTMSK_NAK_RESP_REC_RW(hc_intr_mask,
> + 1);
> + hc_intr_mask =
> +
DWC_HCINTMSK_NYET_RESP_REC_RW(hc_intr_mask,
> + 1);
> + if (hc->do_ping)
> + hc_intr_mask =
> + DWC_HCINTMSK_ACK_RESP_REC_RW
> + (hc_intr_mask, 1);
> + }
> +
> + if (hc->do_split) {
> + hc_intr_mask =
> +
DWC_HCINTMSK_NAK_RESP_REC_RW(hc_intr_mask,
> + 1);
> + if (hc->complete_split)
> + hc_intr_mask =
> + DWC_HCINTMSK_NYET_RESP_REC_RW
> + (hc_intr_mask, 1);
> + else
> + hc_intr_mask =
> + DWC_HCINTMSK_ACK_RESP_REC_RW
> + (hc_intr_mask, 1);
> + }
> +
> + if (hc->error_state)
> + hc_intr_mask =
> +
DWC_HCINTMSK_ACK_RESP_REC_RW(hc_intr_mask,
> + 1);
> + break;
> + case DWC_OTG_EP_TYPE_INTR:
> + hc_intr_mask =
> + DWC_HCINTMSK_TXFER_CMPL_RW(hc_intr_mask, 1);
> + hc_intr_mask =
> + DWC_HCINTMSK_NAK_RESP_REC_RW(hc_intr_mask, 1);
> + hc_intr_mask =
> + DWC_HCINTMSK_STALL_RESP_REC_RW(hc_intr_mask,
1);
> + hc_intr_mask =
> + DWC_HCINTMSK_TRANS_ERR_RW(hc_intr_mask, 1);
> + hc_intr_mask =
> + DWC_HCINTMSK_DATA_TOG_ERR_RW(hc_intr_mask, 1);
> + hc_intr_mask =
> + DWC_HCINTMSK_FRAME_OVERN_ERR_RW(hc_intr_mask,
1);
> +
> + if (hc->ep_is_in)
> + hc_intr_mask =
> + DWC_HCINTMSK_BBL_ERR_RW(hc_intr_mask,
1);
> + if (hc->error_state)
> + hc_intr_mask =
> +
DWC_HCINTMSK_ACK_RESP_REC_RW(hc_intr_mask,
> + 1);
> +
> + if (hc->do_split) {
> + if (hc->complete_split)
> + hc_intr_mask =
> + DWC_HCINTMSK_NYET_RESP_REC_RW
> + (hc_intr_mask, 1);
> + else
> + hc_intr_mask =
> + DWC_HCINTMSK_ACK_RESP_REC_RW
> + (hc_intr_mask, 1);
> + }
> + break;
> + case DWC_OTG_EP_TYPE_ISOC:
> + hc_intr_mask =
> + DWC_HCINTMSK_TXFER_CMPL_RW(hc_intr_mask, 1);
> + hc_intr_mask =
> + DWC_HCINTMSK_FRAME_OVERN_ERR_RW(hc_intr_mask,
1);
> + hc_intr_mask =
> + DWC_HCINTMSK_ACK_RESP_REC_RW(hc_intr_mask, 1);
> +
> + if (hc->ep_is_in) {
> + hc_intr_mask =
> +
DWC_HCINTMSK_TRANS_ERR_RW(hc_intr_mask, 1);
> + hc_intr_mask =
> + DWC_HCINTMSK_BBL_ERR_RW(hc_intr_mask,
1);
> + }
> + break;
> + }
> + }
> + dwc_write32(hc_regs + DWC_HCINTMSK, hc_intr_mask);
> +
> + /* Enable the top level host channel interrupt. */
> + intr_enable = (1 << hc_num);
> + dwc_modify32(host_if->host_global_regs + DWC_HAINTMSK, 0,
> + intr_enable);
> +
> + /* Make sure host channel interrupts are enabled. */
> + gintmsk |= DWC_INTMSK_HST_CHAN;
> + dwc_modify32(global_regs + DWC_GINTMSK, 0, gintmsk);
> +
> + /*
> + * Program the HCCHARn register with the endpoint characteristics
for
> + * the current transfer.
> + */
> + hcchar = 0;
> + hcchar = DWC_HCCHAR_DEV_ADDR_RW(hcchar, hc->dev_addr);
> + hcchar = DWC_HCCHAR_EP_NUM_RW(hcchar, hc->ep_num);
> + hcchar = DWC_HCCHAR_EPDIR_RW(hcchar, hc->ep_is_in);
> + hcchar = DWC_HCCHAR_LSP_DEV_RW(hcchar, (hc->speed ==
> + DWC_OTG_EP_SPEED_LOW));
> + hcchar = DWC_HCCHAR_EPTYPE_RW(hcchar, hc->ep_type);
> + hcchar = DWC_HCCHAR_MPS_RW(hcchar, hc->max_packet);
> + dwc_write32(host_if->hc_regs[hc_num] + DWC_HCCHAR, hcchar);
> +
> + /* Program the HCSPLIT register for SPLITs */
> + hcsplt = 0;
> + if (hc->do_split) {
> + hcsplt = DWC_HCSPLT_COMP_SPLT_RW(hcsplt,
hc->complete_split);
> + hcsplt = DWC_HCSPLT_TRANS_POS_RW(hcsplt, hc->xact_pos);
> + hcsplt = DWC_HCSPLT_HUB_ADDR_RW(hcsplt, hc->hub_addr);
> + hcsplt = DWC_HCSPLT_PRT_ADDR_RW(hcsplt, hc->port_addr);
> + }
> + dwc_write32(host_if->hc_regs[hc_num] + DWC_HCSPLT, hcsplt);
> +}
> +
> +/**
> + * Assigns transactions from a QTD to a free host channel and
initializes the
> + * host channel to perform the transactions. The host channel is
removed from
> + * the free list.
> + */
> +static void assign_and_init_hc(struct dwc_hcd *hcd, struct dwc_qh *qh)
> +{
> + struct dwc_hc *hc;
> + struct dwc_qtd *qtd;
> + struct urb *urb;
> + struct usb_iso_packet_descriptor *frame_desc;
> +
> + hc = list_entry(hcd->free_hc_list.next, struct dwc_hc,
hc_list_entry);
> +
> + /* Remove the host channel from the free list. */
> + list_del_init(&hc->hc_list_entry);
> + qtd = list_entry(qh->qtd_list.next, struct dwc_qtd,
qtd_list_entry);
> + urb = qtd->urb;
> + qh->channel = hc;
> + qh->qtd_in_process = qtd;
> +
> + /*
> + * Use usb_pipedevice to determine device address. This address is
> + * 0 before the SET_ADDRESS command and the correct address
afterward.
> + */
> + hc->dev_addr = usb_pipedevice(urb->pipe);
> + hc->ep_num = usb_pipeendpoint(urb->pipe);
> +
> + if (urb->dev->speed == USB_SPEED_LOW)
> + hc->speed = DWC_OTG_EP_SPEED_LOW;
> + else if (urb->dev->speed == USB_SPEED_FULL)
> + hc->speed = DWC_OTG_EP_SPEED_FULL;
> + else
> + hc->speed = DWC_OTG_EP_SPEED_HIGH;
> +
> + hc->max_packet = dwc_max_packet(qh->maxp);
> + hc->xfer_started = 0;
> + hc->halt_status = DWC_OTG_HC_XFER_NO_HALT_STATUS;
> + hc->error_state = (qtd->error_count > 0);
> + hc->halt_on_queue = 0;
> + hc->halt_pending = 0;
> + hc->requests = 0;
> +
> + /*
> + * The following values may be modified in the transfer type
section
> + * below. The xfer_len value may be reduced when the transfer is
> + * started to accommodate the max widths of the XferSize and
PktCnt
> + * fields in the HCTSIZn register.
> + */
> + hc->do_ping = qh->ping_state;
> + hc->ep_is_in = (usb_pipein(urb->pipe) != 0);
> + hc->data_pid_start = qh->data_toggle;
> + hc->multi_count = 1;
> +
> + if (hcd->core_if->dma_enable)
> + hc->xfer_buff = urb->transfer_dma + (u8 *)
urb->actual_length;
> + else
> + hc->xfer_buff = (u8 *) urb->transfer_buffer +
> + urb->actual_length;
> +
> + hc->xfer_len = urb->transfer_buffer_length - urb->actual_length;
> + hc->xfer_count = 0;
> +
> + /*
> + * Set the split attributes
> + */
> + hc->do_split = 0;
> + if (qh->do_split) {
> + hc->do_split = 1;
> + hc->xact_pos = qtd->isoc_split_pos;
> + hc->complete_split = qtd->complete_split;
> + hc->hub_addr = urb->dev->tt->hub->devnum;
> + hc->port_addr = urb->dev->ttport;
> + }
> +
> + switch (usb_pipetype(urb->pipe)) {
> + case PIPE_CONTROL:
> + hc->ep_type = DWC_OTG_EP_TYPE_CONTROL;
> +
> + switch (qtd->control_phase) {
> + case DWC_OTG_CONTROL_SETUP:
> + hc->do_ping = 0;
> + hc->ep_is_in = 0;
> + hc->data_pid_start = DWC_OTG_HC_PID_SETUP;
> +
> + if (hcd->core_if->dma_enable)
> + hc->xfer_buff = (u8 *) (u32)
urb->setup_dma;
> + else
> + hc->xfer_buff = (u8 *) urb->setup_packet;
> +
> + hc->xfer_len = 8;
> + break;
> + case DWC_OTG_CONTROL_DATA:
> + hc->data_pid_start = qtd->data_toggle;
> + break;
> + case DWC_OTG_CONTROL_STATUS:
> + /*
> + * Direction is opposite of data direction or IN
if no
> + * data.
> + */
> + if (urb->transfer_buffer_length == 0)
> + hc->ep_is_in = 1;
> + else
> + hc->ep_is_in = (usb_pipein(urb->pipe) !=
> + USB_DIR_IN);
> +
> + if (hc->ep_is_in)
> + hc->do_ping = 0;
> +
> + hc->data_pid_start = DWC_OTG_HC_PID_DATA1;
> + hc->xfer_len = 0;
> + if (hcd->core_if->dma_enable)
> + hc->xfer_buff =
> + (u8 *) (u32) hcd->status_buf_dma;
> + else
> + hc->xfer_buff = (u8 *) hcd->status_buf;
> + break;
> + }
> + break;
> + case PIPE_BULK:
> + hc->ep_type = DWC_OTG_EP_TYPE_BULK;
> + break;
> + case PIPE_INTERRUPT:
> + hc->ep_type = DWC_OTG_EP_TYPE_INTR;
> + break;
> + case PIPE_ISOCHRONOUS:
> + frame_desc = &urb->iso_frame_desc[qtd->isoc_frame_index];
> + hc->ep_type = DWC_OTG_EP_TYPE_ISOC;
> +
> + if (hcd->core_if->dma_enable)
> + hc->xfer_buff = (u8 *) (u32) urb->transfer_dma;
> + else
> + hc->xfer_buff = (u8 *) urb->transfer_buffer;
> +
> + hc->xfer_buff += frame_desc->offset +
qtd->isoc_split_offset;
> + hc->xfer_len = frame_desc->length -
qtd->isoc_split_offset;
> +
> + if (hc->xact_pos == DWC_HCSPLIT_XACTPOS_ALL) {
> + if (hc->xfer_len <= 188)
> + hc->xact_pos = DWC_HCSPLIT_XACTPOS_ALL;
> + else
> + hc->xact_pos = DWC_HCSPLIT_XACTPOS_BEGIN;
> + }
> + break;
> + }
> +
> + if (hc->ep_type == DWC_OTG_EP_TYPE_INTR ||
> + hc->ep_type == DWC_OTG_EP_TYPE_ISOC)
> + /*
> + * This value may be modified when the transfer is started
to
> + * reflect the actual transfer length.
> + */
> + hc->multi_count = dwc_hb_mult(qh->maxp);
> +
> + dwc_otg_hc_init(hcd->core_if, hc);
> + hc->qh = qh;
> +}
> +
> +/**
> + * This function selects transactions from the HCD transfer schedule
and
> + * assigns them to available host channels. It is called from HCD
interrupt
> + * handler functions.
> + */
> +enum dwc_transaction_type dwc_otg_hcd_select_transactions(struct
dwc_hcd *hcd)
> +{
> + struct list_head *qh_ptr;
> + struct dwc_qh *qh;
> + int num_channels;
> + enum dwc_transaction_type ret_val = DWC_OTG_TRANSACTION_NONE;
> +
> + /* Process entries in the periodic ready list. */
> + num_channels = hcd->core_if->core_params->host_channels;
> + qh_ptr = hcd->periodic_sched_ready.next;
> + while (qh_ptr != &hcd->periodic_sched_ready &&
> + !list_empty(&hcd->free_hc_list)) {
> + /* Leave one channel for non periodic transactions. */
> + if (hcd->available_host_channels <= 1)
> + break;
> + hcd->available_host_channels--;
> + qh = list_entry(qh_ptr, struct dwc_qh, qh_list_entry);
> + assign_and_init_hc(hcd, qh);
> + /*
> + * Move the QH from the periodic ready schedule to the
> + * periodic assigned schedule.
> + */
> + qh_ptr = qh_ptr->next;
> + list_move(&qh->qh_list_entry,
&hcd->periodic_sched_assigned);
> + ret_val = DWC_OTG_TRANSACTION_PERIODIC;
> + }
> +
> + /*
> + * Process entries in the deferred portion of the non-periodic
list.
> + * A NAK put them here and, at the right time, they need to be
> + * placed on the sched_inactive list.
> + */
> + qh_ptr = hcd->non_periodic_sched_deferred.next;
> + while (qh_ptr != &hcd->non_periodic_sched_deferred) {
> + u16 frame_number =
> + dwc_otg_hcd_get_frame_number(dwc_otg_hcd_to_hcd(hcd));
> + qh = list_entry(qh_ptr, struct dwc_qh, qh_list_entry);
> + qh_ptr = qh_ptr->next;
> +
> + if (dwc_frame_num_le(qh->sched_frame, frame_number))
> + /*
> + * Move the QH from the non periodic deferred
schedule
> + * to the non periodic inactive schedule.
> + */
> + list_move(&qh->qh_list_entry,
> + &hcd->non_periodic_sched_inactive);
> + }
> +
> + /*
> + * Process entries in the inactive portion of the non-periodic
> + * schedule. Some free host channels may not be used if they are
> + * reserved for periodic transfers.
> + */
> + qh_ptr = hcd->non_periodic_sched_inactive.next;
> + num_channels = hcd->core_if->core_params->host_channels;
> +
> + while (qh_ptr != &hcd->non_periodic_sched_inactive
> + && !list_empty(&hcd->free_hc_list)) {
> + if (hcd->available_host_channels < 1)
> + break;
> + hcd->available_host_channels--;
> + qh = list_entry(qh_ptr, struct dwc_qh, qh_list_entry);
> + assign_and_init_hc(hcd, qh);
> + /*
> + * Move the QH from the non-periodic inactive schedule to
the
> + * non-periodic active schedule.
> + */
> + qh_ptr = qh_ptr->next;
> + list_move(&qh->qh_list_entry,
&hcd->non_periodic_sched_active);
> + if (ret_val == DWC_OTG_TRANSACTION_NONE)
> + ret_val = DWC_OTG_TRANSACTION_NON_PERIODIC;
> + else
> + ret_val = DWC_OTG_TRANSACTION_ALL;
> +
> + }
> + return ret_val;
> +}
> +
> +/**
> + * Sets the channel property that indicates in which frame a periodic
transfer
> + * should occur. This is always set to the _next_ frame. This function
has no
> + * effect on non-periodic transfers.
> + */
> +static inline void hc_set_even_odd_frame(struct core_if *core_if,
> + struct dwc_hc *hc, u32 * hcchar)
> +{
> + if (hc->ep_type == DWC_OTG_EP_TYPE_INTR ||
> + hc->ep_type == DWC_OTG_EP_TYPE_ISOC) {
> + u32 hfnum = 0;
> +
> + hfnum = dwc_read32(core_if->host_if->host_global_regs +
> + DWC_HFNUM);
> +
> + /* 1 if _next_ frame is odd, 0 if it's even */
> + *hcchar = DWC_HCCHAR_ODD_FRAME_RW(*hcchar,
> +
((DWC_HFNUM_FRNUM_RD(hfnum) &
> + 0x1) ? 0 : 1));
> + }
> +}
> +
> +static void set_initial_xfer_pid(struct dwc_hc *hc)
> +{
> + if (hc->speed == DWC_OTG_EP_SPEED_HIGH) {
> + if (hc->ep_is_in) {
> + if (hc->multi_count == 1)
> + hc->data_pid_start = DWC_OTG_HC_PID_DATA0;
> + else if (hc->multi_count == 2)
> + hc->data_pid_start = DWC_OTG_HC_PID_DATA1;
> + else
> + hc->data_pid_start = DWC_OTG_HC_PID_DATA2;
> + } else {
> + if (hc->multi_count == 1)
> + hc->data_pid_start = DWC_OTG_HC_PID_DATA0;
> + else
> + hc->data_pid_start = DWC_OTG_HC_PID_MDATA;
> + }
> + } else {
> + hc->data_pid_start = DWC_OTG_HC_PID_DATA0;
> + }
> +}
> +
> +/**
> + * Starts a PING transfer. This function should only be called in Slave
mode.
> + * The Do Ping bit is set in the HCTSIZ register, then the channel is
enabled.
> + */
> +static void dwc_otg_hc_do_ping(struct core_if *core_if, struct dwc_hc
*hc)
> +{
> + u32 hcchar;
> + u32 hctsiz = 0;
> +
> + ulong hc_regs = core_if->host_if->hc_regs[hc->hc_num];
> +
> + hctsiz = 0;
> + hctsiz = DWC_HCTSIZ_DO_PING_PROTO_RW(hctsiz, 1);
> + hctsiz = DWC_HCTSIZ_PKT_CNT_RW(hctsiz, 1);
> + dwc_write32(hc_regs + DWC_HCTSIZ, hctsiz);
> +
> + hcchar = dwc_read32(hc_regs + DWC_HCCHAR);
> + hcchar = DWC_HCCHAR_ENA_RW(hcchar, 1);
> + hcchar = DWC_HCCHAR_DIS_RW(hcchar, 0);
> + dwc_write32(hc_regs + DWC_HCCHAR, hcchar);
> +}
> +
> +/**
> + * This function writes a packet into the Tx FIFO associated with the
Host
> + * Channel. For a channel associated with a non-periodic EP, the
non-periodic
> + * Tx FIFO is written. For a channel associated with a periodic EP, the
> + * periodic Tx FIFO is written. This function should only be called in
Slave
> + * mode.
> + *
> + * Upon return the xfer_buff and xfer_count fields in hc are
incremented by
> + * then number of bytes written to the Tx FIFO.
> + */
> +static void dwc_otg_hc_write_packet(struct core_if *core_if, struct
dwc_hc *hc)
> +{
> + u32 i;
> + u32 remaining_count;
> + u32 byte_count;
> + u32 dword_count;
> + u32 *data_buff = (u32 *) (hc->xfer_buff);
> + u32 data_fifo = core_if->data_fifo[hc->hc_num];
> +
> + remaining_count = hc->xfer_len - hc->xfer_count;
> + if (remaining_count > hc->max_packet)
> + byte_count = hc->max_packet;
> + else
> + byte_count = remaining_count;
> +
> + dword_count = (byte_count + 3) / 4;
> +
> + if (((unsigned long)data_buff) & 0x3)
> + /* xfer_buff is not DWORD aligned. */
> + for (i = 0; i < dword_count; i++, data_buff++)
> + dwc_write_fifo32(data_fifo,
> + get_unaligned(data_buff));
> + else
> + /* xfer_buff is DWORD aligned. */
> + for (i = 0; i < dword_count; i++, data_buff++)
> + dwc_write_fifo32(data_fifo, *data_buff);
> +
> + hc->xfer_count += byte_count;
> + hc->xfer_buff += byte_count;
> +}
> +
> +/**
> + * This function does the setup for a data transfer for a host channel
and
> + * starts the transfer. May be called in either Slave mode or DMA mode.
In
> + * Slave mode, the caller must ensure that there is sufficient space in
the
> + * request queue and Tx Data FIFO.
> + *
> + * For an OUT transfer in Slave mode, it loads a data packet into the
> + * appropriate FIFO. If necessary, additional data packets will be
loaded in
> + * the Host ISR.
> + *
> + * For an IN transfer in Slave mode, a data packet is requested. The
data
> + * packets are unloaded from the Rx FIFO in the Host ISR. If necessary,
> + * additional data packets are requested in the Host ISR.
> + *
> + * For a PING transfer in Slave mode, the Do Ping bit is set in the
HCTSIZ
> + * register along with a packet count of 1 and the channel is enabled.
This
> + * causes a single PING transaction to occur. Other fields in HCTSIZ
are
> + * simply set to 0 since no data transfer occurs in this case.
> + *
> + * For a PING transfer in DMA mode, the HCTSIZ register is initialized
with
> + * all the information required to perform the subsequent data
transfer. In
> + * addition, the Do Ping bit is set in the HCTSIZ register. In this
case, the
> + * controller performs the entire PING protocol, then starts the data
> + * transfer.
> + */
> +static void dwc_otg_hc_start_transfer(struct core_if *core_if,
> + struct dwc_hc *hc)
> +{
> + u32 hcchar;
> + u32 hctsiz = 0;
> + u16 num_packets;
> + u32 max_hc_xfer_size = core_if->core_params->max_transfer_size;
> + u16 max_hc_pkt_count = core_if->core_params->max_packet_count;
> + ulong hc_regs = core_if->host_if->hc_regs[hc->hc_num];
> + hctsiz = 0;
> +
> + if (hc->do_ping) {
> + if (!core_if->dma_enable) {
> + dwc_otg_hc_do_ping(core_if, hc);
> + hc->xfer_started = 1;
> + return;
> + } else {
> + hctsiz = DWC_HCTSIZ_DO_PING_PROTO_RW(hctsiz, 1);
> + }
> + }
> +
> + if (hc->do_split) {
> + num_packets = 1;
> +
> + if (hc->complete_split && !hc->ep_is_in)
> + /*
> + * For CSPLIT OUT Transfer, set the size to 0 so
the
> + * core doesn't expect any data written to the
FIFO
> + */
> + hc->xfer_len = 0;
> + else if (hc->ep_is_in || (hc->xfer_len > hc->max_packet))
> + hc->xfer_len = hc->max_packet;
> + else if (!hc->ep_is_in && (hc->xfer_len > 188))
> + hc->xfer_len = 188;
> +
> + hctsiz = DWC_HCTSIZ_XFER_SIZE_RW(hctsiz, hc->xfer_len);
> + } else {
> + /*
> + * Ensure that the transfer length and packet count will
fit
> + * in the widths allocated for them in the HCTSIZn
register.
> + */
> + if (hc->ep_type == DWC_OTG_EP_TYPE_INTR ||
> + hc->ep_type == DWC_OTG_EP_TYPE_ISOC) {
> + u32 max_len = hc->multi_count * hc->max_packet;
> +
> + /*
> + * Make sure the transfer size is no larger than
one
> + * (micro)frame's worth of data. (A check was done
> + * when the periodic transfer was accepted to
ensure
> + * that a (micro)frame's worth of data can be
> + * programmed into a channel.)
> + */
> + if (hc->xfer_len > max_len)
> + hc->xfer_len = max_len;
> + } else if (hc->xfer_len > max_hc_xfer_size) {
> + /*
> + * Make sure that xfer_len is a multiple of max
packet
> + * size.
> + */
> + hc->xfer_len = max_hc_xfer_size - hc->max_packet +
1;
> + }
> + if (hc->xfer_len > 0) {
> + num_packets = (hc->xfer_len + hc->max_packet - 1)
/
> + hc->max_packet;
> + if (num_packets > max_hc_pkt_count) {
> + num_packets = max_hc_pkt_count;
> + hc->xfer_len = num_packets *
hc->max_packet;
> + }
> + } else {
> + /* Need 1 packet for transfer length of 0. */
> + num_packets = 1;
> + }
> +
> + if (hc->ep_is_in)
> + /*
> + * Always program an integral # of max packets for
IN
> + * transfers.
> + */
> + hc->xfer_len = num_packets * hc->max_packet;
> +
> + if (hc->ep_type == DWC_OTG_EP_TYPE_INTR ||
> + hc->ep_type == DWC_OTG_EP_TYPE_ISOC)
> + /*
> + * Make sure that the multi_count field matches
the
> + * actual transfer length.
> + */
> + hc->multi_count = num_packets;
> +
> + /* Set up the initial PID for the transfer. */
> + if (hc->ep_type == DWC_OTG_EP_TYPE_ISOC)
> + set_initial_xfer_pid(hc);
> +
> + hctsiz = DWC_HCTSIZ_XFER_SIZE_RW(hctsiz, hc->xfer_len);
> + }
> +
> + hc->start_pkt_count = num_packets;
> + hctsiz = DWC_HCTSIZ_PKT_CNT_RW(hctsiz, num_packets);
> + hctsiz = DWC_HCTSIZ_PKT_PID_RW(hctsiz, hc->data_pid_start);
> + dwc_write32(hc_regs + DWC_HCTSIZ, hctsiz);
> +
> + if (core_if->dma_enable)
> + dwc_write32(hc_regs + DWC_HCDMA, (u32) hc->xfer_buff);
> +
> + /* Start the split */
> + if (hc->do_split) {
> + u32 hcsplt;
> +
> + hcsplt = dwc_read32(hc_regs + DWC_HCSPLT);
> + hcsplt = DWC_HCSPLT_COMP_SPLT_RW(hcsplt, 1);
> + dwc_write32(hc_regs + DWC_HCSPLT, hcsplt);
> + }
> +
> + hcchar = dwc_read32(hc_regs + DWC_HCCHAR);
> + hcchar = DWC_HCCHAR_MULTI_CNT_RW(hcchar, hc->multi_count);
> + hc_set_even_odd_frame(core_if, hc, &hcchar);
> +
> + /* Set host channel enable after all other setup is complete. */
> + hcchar = DWC_HCCHAR_ENA_RW(hcchar, 1);
> + hcchar = DWC_HCCHAR_DIS_RW(hcchar, 0);
> + dwc_write32(hc_regs + DWC_HCCHAR, hcchar);
> +
> + hc->xfer_started = 1;
> + hc->requests++;
> + if (!core_if->dma_enable && !hc->ep_is_in && hc->xfer_len > 0)
> + /* Load OUT packet into the appropriate Tx FIFO. */
> + dwc_otg_hc_write_packet(core_if, hc);
> +}
> +
> +/**
> + * This function continues a data transfer that was started by previous
call
> + * to dwc_otg_hc_start_transfer</code>. The caller must ensure there is
> + * sufficient space in the request queue and Tx Data FIFO. This
function
> + * should only be called in Slave mode. In DMA mode, the controller
acts
> + * autonomously to complete transfers programmed to a host channel.
> + *
> + * For an OUT transfer, a new data packet is loaded into the
appropriate FIFO
> + * if there is any data remaining to be queued. For an IN transfer,
another
> + * data packet is always requested. For the SETUP phase of a control
transfer,
> + * this function does nothing.
> + */
> +static int dwc_otg_hc_continue_transfer(struct core_if *core_if,
> + struct dwc_hc *hc)
> +{
> + if (hc->do_split) {
> + /* SPLITs always queue just once per channel */
> + return 0;
> + } else if (hc->data_pid_start == DWC_OTG_HC_PID_SETUP) {
> + /* SETUPs are queued only once since they can't be NAKed.
*/
> + return 0;
> + } else if (hc->ep_is_in) {
> + /*
> + * Always queue another request for other IN transfers. If
> + * back-to-back INs are issued and NAKs are received for
both,
> + * the driver may still be processing the first NAK when
the
> + * second NAK is received. When the interrupt handler
clears
> + * the NAK interrupt for the first NAK, the second NAK
will
> + * not be seen. So we can't depend on the NAK interrupt
> + * handler to requeue a NAKed request. Instead, IN
requests
> + * are issued each time this function is called. When the
> + * transfer completes, the extra requests for the channel
will
> + * be flushed.
> + */
> + u32 hcchar;
> + ulong hc_regs = core_if->host_if->hc_regs[hc->hc_num];
> +
> + hcchar = dwc_read32(hc_regs + DWC_HCCHAR);
> + hc_set_even_odd_frame(core_if, hc, &hcchar);
> +
> + hcchar = DWC_HCCHAR_ENA_RW(hcchar, 1);
> + hcchar = DWC_HCCHAR_DIS_RW(hcchar, 0);
> + dwc_write32(hc_regs + DWC_HCCHAR, hcchar);
> +
> + hc->requests++;
> + return 1;
> + } else {
> + /* OUT transfers. */
> + if (hc->xfer_count < hc->xfer_len) {
> + if (hc->ep_type == DWC_OTG_EP_TYPE_INTR ||
> + hc->ep_type == DWC_OTG_EP_TYPE_ISOC) {
> + u32 hcchar;
> + u32 hc_regs;
> +
> + hc_regs =
> + core_if->host_if->hc_regs[hc->hc_num];
> + hcchar = dwc_read32(hc_regs + DWC_HCCHAR);
> + hc_set_even_odd_frame(core_if, hc,
&hcchar);
> + }
> +
> + /* Load OUT packet into the appropriate Tx FIFO.
*/
> + dwc_otg_hc_write_packet(core_if, hc);
> + hc->requests++;
> + return 1;
> + } else {
> + return 0;
> + }
> + }
> +}
> +
> +/**
> + * This function writes a packet into the Tx FIFO associated with the
Host
> + * Channel. For a channel associated with a non-periodic EP, the
non-periodic
> + * Tx FIFO is written. For a channel associated with a periodic EP, the
> + * periodic Tx FIFO is written. This function should only be called in
Slave
> + * mode.
> + *
> + * Upon return the xfer_buff and xfer_count fields in hc are
incremented by
> + * then number of bytes written to the Tx FIFO.
> + */
> +
> +/**
> + * Attempts to queue a single transaction request for a host channel
> + * associated with either a periodic or non-periodic transfer. This
function
> + * assumes that there is space available in the appropriate request
queue. For
> + * an OUT transfer or SETUP transaction in Slave mode, it checks
whether space
> + * is available in the appropriate Tx FIFO.
> + */
> +static int queue_transaction(struct dwc_hcd *hcd, struct dwc_hc *hc,
> + u16 _fifo_dwords_avail)
> +{
> + int retval;
> +
> + if (hcd->core_if->dma_enable) {
> + if (!hc->xfer_started) {
> + dwc_otg_hc_start_transfer(hcd->core_if, hc);
> + hc->qh->ping_state = 0;
> + }
> + retval = 0;
> + } else if (hc->halt_pending) {
> + /* Don't queue a request if the channel has been halted.
*/
> + retval = 0;
> + } else if (hc->halt_on_queue) {
> + dwc_otg_hc_halt(hcd->core_if, hc, hc->halt_status);
> + retval = 0;
> + } else if (hc->do_ping) {
> + if (!hc->xfer_started)
> + dwc_otg_hc_start_transfer(hcd->core_if, hc);
> + retval = 0;
> + } else if (!hc->ep_is_in || hc->data_pid_start ==
> + DWC_OTG_HC_PID_SETUP) {
> + if ((_fifo_dwords_avail * 4) >= hc->max_packet) {
> + if (!hc->xfer_started) {
> + dwc_otg_hc_start_transfer(hcd->core_if,
hc);
> + retval = 1;
> + } else {
> + retval =
> +
dwc_otg_hc_continue_transfer(hcd->core_if,
> + hc);
> + }
> + } else {
> + retval = -1;
> + }
> + } else {
> + if (!hc->xfer_started) {
> + dwc_otg_hc_start_transfer(hcd->core_if, hc);
> + retval = 1;
> + } else {
> + retval =
dwc_otg_hc_continue_transfer(hcd->core_if, hc);
> + }
> + }
> + return retval;
> +}
> +
> +/**
> + * Processes active non-periodic channels and queues transactions for
these
> + * channels to the DWC_otg controller. After queueing transactions, the
NP Tx
> + * FIFO Empty interrupt is enabled if there are more transactions to
queue as
> + * NP Tx FIFO or request queue space becomes available. Otherwise, the
NP Tx
> + * FIFO Empty interrupt is disabled.
> + */
> +static void process_non_periodic_channels(struct dwc_hcd *hcd)
> +{
> + u32 tx_status = 0;
> + struct list_head *orig_qh_ptr;
> + struct dwc_qh *qh;
> + int status;
> + int no_queue_space = 0;
> + int no_fifo_space = 0;
> + int more_to_do = 0;
> + ulong regs = hcd->core_if->core_global_regs;
> +
> + /*
> + * Keep track of the starting point. Skip over the start-of-list
> + * entry.
> + */
> + if (hcd->non_periodic_qh_ptr == &hcd->non_periodic_sched_active)
> + hcd->non_periodic_qh_ptr = hcd->non_periodic_qh_ptr->next;
> + orig_qh_ptr = hcd->non_periodic_qh_ptr;
> +
> + /*
> + * Process once through the active list or until no more space is
> + * available in the request queue or the Tx FIFO.
> + */
> + do {
> + tx_status = dwc_read32(regs + DWC_GNPTXSTS);
> + if (!hcd->core_if->dma_enable &&
> + DWC_GNPTXSTS_NPTXQSPCAVAIL_RD(tx_status) == 0) {
> + no_queue_space = 1;
> + break;
> + }
> +
> + qh = list_entry(hcd->non_periodic_qh_ptr, struct dwc_qh,
> + qh_list_entry);
> + status = queue_transaction(hcd, qh->channel,
> + DWC_GNPTXSTS_NPTXFSPCAVAIL_RD
> + (tx_status));
> +
> + if (status > 0) {
> + more_to_do = 1;
> + } else if (status < 0) {
> + no_fifo_space = 1;
> + break;
> + }
> +
> + /* Advance to next QH, skipping start-of-list entry. */
> + hcd->non_periodic_qh_ptr = hcd->non_periodic_qh_ptr->next;
> + if (hcd->non_periodic_qh_ptr ==
&hcd->non_periodic_sched_active)
> + hcd->non_periodic_qh_ptr =
> + hcd->non_periodic_qh_ptr->next;
> + } while (hcd->non_periodic_qh_ptr != orig_qh_ptr);
> +
> + if (!hcd->core_if->dma_enable) {
> + u32 intr_mask = 0;
> +
> + intr_mask |= DWC_INTMSK_NP_TXFIFO_EMPT;
> + if (more_to_do || no_queue_space || no_fifo_space) {
> + /*
> + * May need to queue more transactions as the
request
> + * queue or Tx FIFO empties. Enable the
non-periodic
> + * Tx FIFO empty interrupt. (Always use the
half-empty
> + * level to ensure that new requests are loaded as
> + * soon as possible.)
> + */
> + dwc_modify32(gintmsk_reg(hcd), 0, intr_mask);
> + } else {
> + /*
> + * Disable the Tx FIFO empty interrupt since there
are
> + * no more transactions that need to be queued
right
> + * now. This function is called from interrupt
> + * handlers to queue more transactions as transfer
> + * states change.
> + */
> + dwc_modify32(gintmsk_reg(hcd), intr_mask, 0);
> + }
> + }
> +}
> +
> +/**
> + * Processes periodic channels for the next frame and queues
transactions for
> + * these channels to the DWC_otg controller. After queueing
transactions, the
> + * Periodic Tx FIFO Empty interrupt is enabled if there are more
transactions
> + * to queue as Periodic Tx FIFO or request queue space becomes
available.
> + * Otherwise, the Periodic Tx FIFO Empty interrupt is disabled.
> + */
> +static void process_periodic_channels(struct dwc_hcd *hcd)
> +{
> + u32 tx_status = 0;
> + struct list_head *qh_ptr;
> + struct dwc_qh *qh;
> + int status;
> + int no_queue_space = 0;
> + int no_fifo_space = 0;
> + ulong host_regs;
Tabify the declarations ;
> +
> + host_regs = hcd->core_if->host_if->host_global_regs;
> +
> + qh_ptr = hcd->periodic_sched_assigned.next;
> + while (qh_ptr != &hcd->periodic_sched_assigned) {
> + tx_status = dwc_read32(host_regs + DWC_HPTXSTS);
> + if (DWC_HPTXSTS_PTXSPC_AVAIL_RD(tx_status) == 0) {
> + no_queue_space = 1;
> + break;
> + }
> +
> + qh = list_entry(qh_ptr, struct dwc_qh, qh_list_entry);
> +
> + /*
> + * Set a flag if we're queuing high-bandwidth in slave
mode.
> + * The flag prevents any halts to get into the request
queue in
> + * the middle of multiple high-bandwidth packets getting
queued.
> + */
> + if (!hcd->core_if->dma_enable && qh->channel->multi_count
> 1)
> + hcd->core_if->queuing_high_bandwidth = 1;
> +
> + status = queue_transaction(hcd, qh->channel,
> + DWC_HPTXSTS_PTXFSPC_AVAIL_RD
> + (tx_status));
> + if (status < 0) {
> + no_fifo_space = 1;
> + break;
> + }
> +
> + /*
> + * In Slave mode, stay on the current transfer until there
is
> + * nothing more to do or the high-bandwidth request count
is
> + * reached. In DMA mode, only need to queue one request.
The
> + * controller automatically handles multiple packets for
> + * high-bandwidth transfers.
> + */
> + if (hcd->core_if->dma_enable || (status == 0 ||
> + qh->channel->requests ==
> +
qh->channel->multi_count)) {
> + qh_ptr = qh_ptr->next;
> +
> + /*
> + * Move the QH from the periodic assigned schedule
to
> + * the periodic queued schedule.
> + */
> + list_move(&qh->qh_list_entry,
> + &hcd->periodic_sched_queued);
> +
> + /* done queuing high bandwidth */
> + hcd->core_if->queuing_high_bandwidth = 0;
> + }
> + }
> +
> + if (!hcd->core_if->dma_enable) {
> + u32 intr_mask = 0;
> +
> + intr_mask |= DWC_INTMSK_NP_TXFIFO_EMPT;
> +
> + if (!list_empty(&hcd->periodic_sched_assigned) ||
> + no_queue_space || no_fifo_space)
> + /*
> + * May need to queue more transactions as the
request
> + * queue or Tx FIFO empties. Enable the periodic
Tx
> + * FIFO empty interrupt. (Always use the
half-empty
> + * level to ensure that new requests are loaded as
> + * soon as possible.)
> + */
> + dwc_modify32(gintmsk_reg(hcd), 0, intr_mask);
> + else
> + /*
> + * Disable the Tx FIFO empty interrupt since there
are
> + * no more transactions that need to be queued
right
> + * now. This function is called from interrupt
> + * handlers to queue more transactions as transfer
> + * states change.
> + */
> + dwc_modify32(gintmsk_reg(hcd), intr_mask, 0);
> + }
> +}
> +
> +/**
> + * This function processes the currently active host channels and
queues
> + * transactions for these channels to the DWC_otg controller. It is
called
> + * from HCD interrupt handler functions.
> + */
> +void dwc_otg_hcd_queue_transactions(struct dwc_hcd *hcd,
> + enum dwc_transaction_type tr_type)
> +{
> + /* Process host channels associated with periodic transfers. */
> + if ((tr_type == DWC_OTG_TRANSACTION_PERIODIC ||
> + tr_type == DWC_OTG_TRANSACTION_ALL) &&
> + !list_empty(&hcd->periodic_sched_assigned))
> + process_periodic_channels(hcd);
> +
> + /* Process host channels associated with non-periodic transfers.
*/
> + if (tr_type == DWC_OTG_TRANSACTION_NON_PERIODIC ||
> + tr_type == DWC_OTG_TRANSACTION_ALL) {
> + if (!list_empty(&hcd->non_periodic_sched_active)) {
> + process_non_periodic_channels(hcd);
> + } else {
> + /*
> + * Ensure NP Tx FIFO empty interrupt is disabled
when
> + * there are no non-periodic transfers to process.
> + */
> + u32 gintmsk = 0;
> + gintmsk |= DWC_INTMSK_NP_TXFIFO_EMPT;
> + dwc_modify32(gintmsk_reg(hcd), gintmsk, 0);
> + }
> + }
> +}
> +
> +/**
> + * Sets the final status of an URB and returns it to the device driver.
Any
> + * required cleanup of the URB is performed.
> + */
> +void dwc_otg_hcd_complete_urb(struct dwc_hcd *hcd, struct urb *urb, int
status)
> +__releases(hcd->lock) __acquires(hcd->lock)
> +{
> + urb->hcpriv = NULL;
> + usb_hcd_unlink_urb_from_ep(dwc_otg_hcd_to_hcd(hcd), urb);
> +
> + spin_unlock(&hcd->lock);
> + usb_hcd_giveback_urb(dwc_otg_hcd_to_hcd(hcd), urb, status);
> + spin_lock(&hcd->lock);
> +}
> diff --git a/drivers/usb/otg/dwc/hcd.h b/drivers/usb/otg/dwc/hcd.h
> new file mode 100644
> index 0000000..c3d86e4
> --- /dev/null
> +++ b/drivers/usb/otg/dwc/hcd.h
> @@ -0,0 +1,416 @@
> +/*
> + * DesignWare HS OTG controller driver
> + * Copyright (C) 2006 Synopsys, Inc.
> + * Portions Copyright (C) 2010 Applied Micro Circuits Corporation.
> + *
> + * This program is free software: you can redistribute it and/or
> + * modify it under the terms of the GNU General Public License
> + * version 2 as published by the Free Software Foundation.
> + *
> + * This program is distributed in the hope that it will be useful
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
> + * GNU General Public License version 2 for more details.
> + *
> + * You should have received a copy of the GNU General Public License
> + * along with this program; if not, see http://www.gnu.org/licenses
> + * or write to the Free Software Foundation, Inc., 51 Franklin Street,
> + * Suite 500, Boston, MA 02110-1335 USA.
> + *
> + * Based on Synopsys driver version 2.60a
> + * Modified by Mark Miesfeld <mmiesfeld@apm.com>
> + * Modified by Stefan Roese <sr@denx.de>, DENX Software Engineering
> + * Modified by Chuck Meade <chuck@theptrgroup.com>
> + *
> + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS"
> + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING BUT NOT LIMITED TO
THE
> + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
PURPOSE
> + * ARE DISCLAIMED. IN NO EVENT SHALL SYNOPSYS, INC. BE LIABLE FOR ANY
DIRECT,
> + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES
> + * (INCLUDING BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES;
> + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND
> + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY OR
TORT
> + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
USE OF
> + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
> + *
> + */
> +
> +#if !defined(__DWC_HCD_H__)
> +#define __DWC_HCD_H__
> +
> +#include <linux/usb.h>
> +#include <linux/usb/hcd.h>
> +
> +#include "driver.h"
> +
> +/*
> + * This file contains the structures, constants, and interfaces for
> + * the Host Contoller Driver (HCD).
> + *
> + * The Host Controller Driver (HCD) is responsible for translating
requests
> + * from the USB Driver into the appropriate actions on the DWC_otg
controller.
> + * It isolates the USBD from the specifics of the controller by
providing an
> + * API to the USBD.
> + */
> +
> +/* Phases for control transfers. */
> +enum dwc_control_phase {
> + DWC_OTG_CONTROL_SETUP,
> + DWC_OTG_CONTROL_DATA,
> + DWC_OTG_CONTROL_STATUS
> +};
> +
> +/* Transaction types. */
> +enum dwc_transaction_type {
> + DWC_OTG_TRANSACTION_NONE,
> + DWC_OTG_TRANSACTION_PERIODIC,
> + DWC_OTG_TRANSACTION_NON_PERIODIC,
> + DWC_OTG_TRANSACTION_ALL
> +};
> +
> +/*
> + * A Queue Transfer Descriptor (QTD) holds the state of a bulk,
control,
> + * interrupt, or isochronous transfer. A single QTD is created for each
URB
> + * (of one of these types) submitted to the HCD. The transfer
associated with
> + * a QTD may require one or multiple transactions.
> + *
> + * A QTD is linked to a Queue Head, which is entered in either the
> + * non-periodic or periodic schedule for execution. When a QTD is
chosen for
> + * execution, some or all of its transactions may be executed. After
> + * execution, the state of the QTD is updated. The QTD may be retired
if all
> + * its transactions are complete or if an error occurred. Otherwise, it
> + * remains in the schedule so more transactions can be executed later.
> + */
> +struct dwc_qtd {
> + /*
> + * Determines the PID of the next data packet for the data phase
of
> + * control transfers. Ignored for other transfer types.
> + * One of the following values:
> + * - DWC_OTG_HC_PID_DATA0
> + * - DWC_OTG_HC_PID_DATA1
> + */
> + u8 data_toggle;
> +
> + /* Current phase for control transfers (Setup, Data, or Status).
*/
> + enum dwc_control_phase control_phase;
> +
> + /*
> + * Keep track of the current split type
> + * for FS/LS endpoints on a HS Hub
> + */
> + u8 complete_split;
> +
> + /* How many bytes transferred during SSPLIT OUT */
> + u32 ssplit_out_xfer_count;
> +
> + /*
> + * Holds the number of bus errors that have occurred for a
transaction
> + * within this transfer.
> + */
> + u8 error_count;
> +
> + /*
> + * Index of the next frame descriptor for an isochronous transfer.
A
> + * frame descriptor describes the buffer position and length of
the
> + * data to be transferred in the next scheduled (micro)frame of an
> + * isochronous transfer. It also holds status for that
transaction.
> + * The frame index starts at 0.
> + */
> + int isoc_frame_index;
> +
> + /* Position of the ISOC split on full/low speed */
> + u8 isoc_split_pos;
> +
> + /* Position of the ISOC split in the buffer for the current frame
*/
> + u16 isoc_split_offset;
> +
> + /* URB for this transfer */
> + struct urb *urb;
> +
> + /* This list of QTDs */
> + struct list_head qtd_list_entry;
> +
> + /* Field to track the qh pointer */
> + struct dwc_qh *qtd_qh_ptr;
> +};
> +
> +/*
> + * A Queue Head (QH) holds the static characteristics of an endpoint
and
> + * maintains a list of transfers (QTDs) for that endpoint. A QH
structure may
> + * be entered in either the non-periodic or periodic schedule.
> + */
> +struct dwc_qh {
> + /*
> + * Endpoint type.
> + * One of the following values:
> + * - USB_ENDPOINT_XFER_CONTROL
> + * - USB_ENDPOINT_XFER_ISOC
> + * - USB_ENDPOINT_XFER_BULK
> + * - USB_ENDPOINT_XFER_INT
> + */
> + u8 ep_type;
> + u8 ep_is_in;
> +
> + /* wMaxPacketSize Field of Endpoint Descriptor. */
> + u16 maxp;
> +
> + /*
> + * Determines the PID of the next data packet for non-control
> + * transfers. Ignored for control transfers.
> + * One of the following values:
> + * - DWC_OTG_HC_PID_DATA0
> + * - DWC_OTG_HC_PID_DATA1
> + */
> + u8 data_toggle;
> +
> + /* Ping state if 1. */
> + u8 ping_state;
> +
> + /* List of QTDs for this QH. */
> + struct list_head qtd_list;
> +
> + /* Host channel currently processing transfers for this QH. */
> + struct dwc_hc *channel;
> +
> + /* QTD currently assigned to a host channel for this QH. */
> + struct dwc_qtd *qtd_in_process;
> +
> + /* Full/low speed endpoint on high-speed hub requires split. */
> + u8 do_split;
> +
> + /* Periodic schedule information */
> +
> + /* Bandwidth in microseconds per (micro)frame. */
> + u8 usecs;
> +
> + /* Interval between transfers in (micro)frames. */
> + u16 interval;
> +
> + /*
> + * (micro)frame to initialize a periodic transfer. The transfer
> + * executes in the following (micro)frame.
> + */
> + u16 sched_frame;
> +
> + /* (micro)frame at which last start split was initialized. */
> + u16 start_split_frame;
> +
> + u16 speed;
> + u16 frame_usecs[8];
> +
> + /* Entry for QH in either the periodic or non-periodic schedule.
*/
> + struct list_head qh_list_entry;
> +};
> +
> +/* Gets the struct usb_hcd that contains a struct dwc_hcd. */
> +static inline struct usb_hcd *dwc_otg_hcd_to_hcd(struct dwc_hcd
*dwc_hcd)
> +{
> + return container_of((void *)dwc_hcd, struct usb_hcd, hcd_priv);
> +}
> +
> +/* HCD Create/Destroy Functions */
> +extern int __init dwc_otg_hcd_init(struct device *_dev,
> + struct dwc_otg_device *dwc_dev);
> +extern void dwc_otg_hcd_remove(struct device *_dev);
> +
> +/*
> + * The following functions support managing the DWC_otg controller in
host
> + * mode.
> + */
> +extern int dwc_otg_hcd_get_frame_number(struct usb_hcd *hcd);
> +extern void dwc_otg_hc_cleanup(struct core_if *core_if, struct dwc_hc
*hc);
> +extern void dwc_otg_hc_halt(struct core_if *core_if, struct dwc_hc *hc,
> + enum dwc_halt_status _halt_status);
> +
> +/* Transaction Execution Functions */
> +extern enum dwc_transaction_type dwc_otg_hcd_select_transactions(struct
dwc_hcd
> + *hcd);
> +extern void dwc_otg_hcd_queue_transactions(struct dwc_hcd *hcd,
> + enum dwc_transaction_type
tr_type);
> +extern void dwc_otg_hcd_complete_urb(struct dwc_hcd *_hcd, struct urb
*urb,
> + int status);
> +
> +/* Interrupt Handler Functions */
> +extern int dwc_otg_hcd_handle_intr(struct dwc_hcd *hcd);
> +
> +/* Schedule Queue Functions */
> +extern int init_hcd_usecs(struct dwc_hcd *hcd);
> +extern void dwc_otg_hcd_qh_free(struct dwc_qh *qh);
> +extern void dwc_otg_hcd_qh_remove(struct dwc_hcd *hcd, struct dwc_qh
*qh);
> +extern void dwc_otg_hcd_qh_deactivate(struct dwc_hcd *hcd, struct
dwc_qh *qh,
> + int sched_csplit);
> +extern int dwc_otg_hcd_qh_deferr(struct dwc_hcd *hcd, struct dwc_qh
*qh,
> + int delay);
> +extern struct dwc_qtd *dwc_otg_hcd_qtd_create(struct urb *urb,
> + gfp_t _mem_flags);
> +extern int dwc_otg_hcd_qtd_add(struct dwc_qtd *qtd, struct dwc_hcd
*dwc_hcd);
> +
> +/*
> + * Frees the memory for a QTD structure. QTD should already be removed
from
> + * list.
> + */
> +static inline void dwc_otg_hcd_qtd_free(struct dwc_qtd *_qtd)
> +{
> + kfree(_qtd);
> +}
> +
> +/* Removes a QTD from list. */
> +static inline void dwc_otg_hcd_qtd_remove(struct dwc_qtd *_qtd)
> +{
> + list_del(&_qtd->qtd_list_entry);
> +}
> +
> +/* Remove and free a QTD */
> +static inline void dwc_otg_hcd_qtd_remove_and_free(struct dwc_qtd
*_qtd)
> +{
> + dwc_otg_hcd_qtd_remove(_qtd);
> + dwc_otg_hcd_qtd_free(_qtd);
> +}
> +
> +struct dwc_qh *dwc_urb_to_qh(struct urb *_urb);
> +
> +/* Gets the usb_host_endpoint associated with an URB. */
> +static inline struct usb_host_endpoint *dwc_urb_to_endpoint(struct urb
*_urb)
> +{
> + struct usb_device *dev = _urb->dev;
> + int ep_num = usb_pipeendpoint(_urb->pipe);
> +
> + if (usb_pipein(_urb->pipe))
> + return dev->ep_in[ep_num];
> + else
> + return dev->ep_out[ep_num];
> +}
> +
> +/*
> + * Gets the endpoint number from a _bEndpointAddress argument. The
endpoint is
> + * qualified with its direction (possible 32 endpoints per device).
> + */
> +#define dwc_ep_addr_to_endpoint(_bEndpointAddress_) \
> + ((_bEndpointAddress_ & USB_ENDPOINT_NUMBER_MASK) | \
> + ((_bEndpointAddress_ & USB_DIR_IN) != 0) << 4)
> +
> +/* Gets the QH that contains the list_head */
> +#define dwc_list_to_qh(_list_head_ptr_) \
> + (container_of(_list_head_ptr_, struct dwc_qh,
qh_list_entry))
> +
> +/* Gets the QTD that contains the list_head */
> +#define dwc_list_to_qtd(_list_head_ptr_) \
> + (container_of(_list_head_ptr_, struct dwc_qtd,
qtd_list_entry))
> +
> +/* Check if QH is non-periodic */
> +#define dwc_qh_is_non_per(_qh_ptr_) \
> + ((_qh_ptr_->ep_type == USB_ENDPOINT_XFER_BULK) || \
> + (_qh_ptr_->ep_type == USB_ENDPOINT_XFER_CONTROL))
> +
> +/* High bandwidth multiplier as encoded in highspeed endpoint
descriptors */
> +#define dwc_hb_mult(wMaxPacketSize) (1 + (((wMaxPacketSize) >> 11) &
0x03))
> +
> +/* Packet size for any kind of endpoint descriptor */
> +#define dwc_max_packet(wMaxPacketSize) ((wMaxPacketSize) &
0x07ff)
> +
> +/*
> + * Returns true if _frame1 is less than or equal to _frame2. The
comparison is
> + * done modulo DWC_HFNUM_MAX_FRNUM. This accounts for the rollover of
the
> + * frame number when the max frame number is reached.
> + */
> +static inline int dwc_frame_num_le(u16 _frame1, u16 _frame2)
> +{
> + return ((_frame2 - _frame1) & DWC_HFNUM_MAX_FRNUM) <=
> + (DWC_HFNUM_MAX_FRNUM >> 1);
> +}
> +
> +/*
> + * Returns true if _frame1 is greater than _frame2. The comparison is
done
> + * modulo DWC_HFNUM_MAX_FRNUM. This accounts for the rollover of the
frame
> + * number when the max frame number is reached.
> + */
> +static inline int dwc_frame_num_gt(u16 _frame1, u16 _frame2)
> +{
> + return (_frame1 != _frame2) &&
> + (((_frame1 - _frame2) &
> + DWC_HFNUM_MAX_FRNUM) < (DWC_HFNUM_MAX_FRNUM >> 1));
> +}
> +
> +/*
> + * Increments _frame by the amount specified by _inc. The addition is
done
> + * modulo DWC_HFNUM_MAX_FRNUM. Returns the incremented value.
> + */
> +static inline u16 dwc_frame_num_inc(u16 _frame, u16 _inc)
> +{
> + return (_frame + _inc) & DWC_HFNUM_MAX_FRNUM;
> +}
> +
> +static inline u16 dwc_full_frame_num(u16 _frame)
> +{
> + return ((_frame) & DWC_HFNUM_MAX_FRNUM) >> 3;
> +}
> +
> +static inline u16 dwc_micro_frame_num(u16 _frame)
> +{
> + return (_frame) & 0x7;
> +}
> +
> +static inline ulong gintsts_reg(struct dwc_hcd *hcd)
> +{
> + ulong global_regs = hcd->core_if->core_global_regs;
> + return global_regs + DWC_GINTSTS;
> +}
> +
> +static inline ulong gintmsk_reg(struct dwc_hcd *hcd)
> +{
> + ulong global_regs = hcd->core_if->core_global_regs;
> + return global_regs + DWC_GINTMSK;
> +}
> +
> +static inline ulong gahbcfg_reg(struct dwc_hcd *hcd)
> +{
> + ulong global_regs = hcd->core_if->core_global_regs;
> + return global_regs + DWC_GAHBCFG;
> +}
> +
> +static inline const char *pipetype_str(unsigned int pipe)
> +{
> + switch (usb_pipetype(pipe)) {
> + case PIPE_CONTROL:
> + return "control";
> + case PIPE_BULK:
> + return "bulk";
> + case PIPE_INTERRUPT:
> + return "interrupt";
> + case PIPE_ISOCHRONOUS:
> + return "isochronous";
> + default:
> + return "unknown";
> + }
> +}
> +
> +static inline const char *dev_speed_str(enum usb_device_speed speed)
> +{
> + switch (speed) {
> + case USB_SPEED_HIGH:
> + return "high";
> + case USB_SPEED_FULL:
> + return "full";
> + case USB_SPEED_LOW:
> + return "low";
> + default:
> + return "unknown";
> + }
> +}
> +
> +static inline const char *ep_type_str(u8 type)
> +{
> + switch (type) {
> + case USB_ENDPOINT_XFER_ISOC:
> + return "isochronous";
> + case USB_ENDPOINT_XFER_INT:
> + return "interrupt";
> + case USB_ENDPOINT_XFER_CONTROL:
> + return "control";
> + case USB_ENDPOINT_XFER_BULK:
> + return "bulk";
> + default:
> + return "?";
> + }
> +}
> +#endif
> --
> 1.6.1.rc3
>
> --
> 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 http://vger.kernel.org/majordomo-info.html
^ permalink raw reply
* RE: [PATCH v10 02/10] USB/ppc4xx: Add Synopsys DWC OTG driver framework
From: Keshava Munegowda @ 2011-03-29 10:46 UTC (permalink / raw)
To: tmarri, linux-usb, linuxppc-dev; +Cc: greg, Mark Miesfeld, Fushen Chen
In-Reply-To: <1301336863-1886-1-git-send-email-tmarri@apm.com>
> +static irqreturn_t dwc_otg_common_irq(int _irq, void *dev)
> +{
> + struct dwc_otg_device *dwc_dev = dev;
> + int retval;
Tabify the declarations;
> +
> + 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);
Tabify the declarations ;
> +
> + /* 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;
> + ulong gusbcfg_addr;
> + u32 usbcfg = 0;
Tabify the declarations; so that it looks clean.
> +
> + dev_dbg(dev, "dwc_otg_driver_probe(%p)\n", dev);
> +
> + dwc_dev = kzalloc(sizeof(*dwc_dev), GFP_KERNEL);
> + if (!dwc_dev) {
> + dev_err(dev, "kmalloc of dwc_otg_device failed\n");
> + retval = -ENOMEM;
> + goto fail_dwc_dev;
> + }
> +
> + /* Retrieve the memory and IRQ resources. */
> + dwc_dev->irq = irq_of_parse_and_map(ofdev->dev.of_node, 0);
> + if (dwc_dev->irq == NO_IRQ) {
> + dev_err(dev, "no device irq\n");
> + retval = -ENODEV;
> + goto fail_of_irq;
> + }
> + dev_dbg(dev, "OTG - device irq: %d\n", dwc_dev->irq);
> +
> + if (of_address_to_resource(ofdev->dev.of_node, 0, &res)) {
> + dev_err(dev, "%s: Can't get USB-OTG register address\n",
> + __func__);
> + retval = -ENOMEM;
> + goto fail_of_irq;
> + }
> + dev_dbg(dev, "OTG - ioresource_mem start0x%llx: end:0x%llx\n",
> + (unsigned long long)res.start, (unsigned long
long)res.end);
> +
> + dwc_dev->phys_addr = res.start;
> + dwc_dev->base_len = res.end - res.start + 1;
> + if (!request_mem_region(dwc_dev->phys_addr,
> + dwc_dev->base_len, dwc_driver_name)) {
> + dev_err(dev, "request_mem_region failed\n");
> + retval = -EBUSY;
> + goto fail_of_irq;
> + }
> +
> + /* Map the DWC_otg Core memory into virtual address space. */
> + dwc_dev->base = ioremap(dwc_dev->phys_addr, dwc_dev->base_len);
> + if (!dwc_dev->base) {
> + dev_err(dev, "ioremap() failed\n");
> + retval = -ENOMEM;
> + goto fail_ioremap;
> + }
> + dev_dbg(dev, "mapped base=0x%08x\n", (__force u32)dwc_dev->base);
> +
> + /*
> + * Initialize driver data to point to the global DWC_otg
> + * Device structure.
> + */
> + dev_set_drvdata(dev, dwc_dev);
> +
> + dwc_dev->core_if =
> + dwc_otg_cil_init(dwc_dev->base, &dwc_otg_module_params);
> + if (!dwc_dev->core_if) {
> + dev_err(dev, "CIL initialization failed!\n");
> + retval = -ENOMEM;
> + goto fail_cil_init;
> + }
> +
> + /*
> + * Validate parameter values after dwc_otg_cil_init.
> + */
> + if (check_parameters(dwc_dev->core_if)) {
> + retval = -EINVAL;
> + goto fail_check_param;
> + }
> +
> + usb_nop_xceiv_register();
> + dwc_dev->core_if->xceiv = otg_get_transceiver();
> + if (!dwc_dev->core_if->xceiv) {
> + retval = -ENODEV;
> + goto fail_xceiv;
> + }
> + dwc_set_feature(dwc_dev->core_if);
> +
> + /* Initialize the DWC_otg core. */
> + dwc_otg_core_init(dwc_dev->core_if);
> +
> + /*
> + * Disable the global interrupt until all the interrupt
> + * handlers are installed.
> + */
> + dwc_otg_disable_global_interrupts(dwc_dev->core_if);
> +
> + /*
> + * Install the interrupt handler for the common interrupts before
> + * enabling common interrupts in core_init below.
> + */
> + retval = request_irq(dwc_dev->irq, dwc_otg_common_irq,
> + IRQF_SHARED, "dwc_otg", dwc_dev);
> + if (retval) {
> + dev_err(dev, "request of irq%d failed retval: %d\n",
> + dwc_dev->irq, retval);
> + retval = -EBUSY;
> + goto fail_req_irq;
> + } else {
> + dwc_dev->common_irq_installed = 1;
> + }
> +
> + if (!dwc_has_feature(dwc_dev->core_if, DWC_HOST_ONLY)) {
> + /* Initialize the PCD */
> + retval = dwc_otg_pcd_init(dev);
> + if (retval) {
> + dev_err(dev, "dwc_otg_pcd_init failed\n");
> + dwc_dev->pcd = NULL;
> + goto fail_req_irq;
> + }
> + }
> +
> + gusbcfg_addr = (ulong) (dwc_dev->core_if->core_global_regs)
> + + DWC_GUSBCFG;
> + if (!dwc_has_feature(dwc_dev->core_if, DWC_DEVICE_ONLY)) {
> + /* Initialize the HCD and force_host_mode */
> + usbcfg = dwc_read32(gusbcfg_addr);
> + usbcfg |= DWC_USBCFG_FRC_HST_MODE;
> + dwc_write32(gusbcfg_addr, usbcfg);
> +
> + retval = dwc_otg_hcd_init(dev, dwc_dev);
> + if (retval) {
> + dev_err(dev, "dwc_otg_hcd_init failed\n");
> + dwc_dev->hcd = NULL;
> + goto fail_hcd;
> + }
> + /* configure chargepump interrupt */
> + dwc_dev->hcd->cp_irq =
irq_of_parse_and_map(ofdev->dev.of_node,
> + 3);
> + if (dwc_dev->hcd->cp_irq) {
> + retval = request_irq(dwc_dev->hcd->cp_irq,
> + dwc_otg_externalchgpump_irq,
> + IRQF_SHARED,
> + "dwc_otg_ext_chg_pump",
dwc_dev);
> + if (retval) {
> + dev_err(dev,
> + "request of irq failed retval:
%d\n",
> + retval);
> + retval = -EBUSY;
> + goto fail_hcd;
> + } else {
> + dev_dbg(dev, "%s: ExtChgPump Detection "
> + "IRQ registered\n",
dwc_driver_name);
> + dwc_dev->hcd->cp_irq_installed = 1;
> + }
> + }
> + }
> + /*
> + * Enable the global interrupt after all the interrupt
> + * handlers are installed.
> + */
> + dwc_otg_enable_global_interrupts(dwc_dev->core_if);
> +
> + usbcfg = dwc_read32(gusbcfg_addr);
> + usbcfg &= ~DWC_USBCFG_FRC_HST_MODE;
> + dwc_write32(gusbcfg_addr, usbcfg);
> +
> + return 0;
> +fail_hcd:
> + free_irq(dwc_dev->irq, dwc_dev);
> + if (!dwc_has_feature(dwc_dev->core_if, DWC_HOST_ONLY)) {
> + if (dwc_dev->pcd)
> + dwc_otg_pcd_remove(dev);
> + }
> +fail_req_irq:
> + otg_put_transceiver(dwc_dev->core_if->xceiv);
> +fail_xceiv:
> + usb_nop_xceiv_unregister();
> +fail_check_param:
> + dwc_otg_cil_remove(dwc_dev->core_if);
> +fail_cil_init:
> + dev_set_drvdata(dev, NULL);
> + iounmap(dwc_dev->base);
> +fail_ioremap:
> + release_mem_region(dwc_dev->phys_addr, dwc_dev->base_len);
> +fail_of_irq:
> + kfree(dwc_dev);
> +fail_dwc_dev:
> + return retval;
> +}
> +
> +/*
> + * This structure defines the methods to be called by a bus driver
> + * during the lifecycle of a device on that bus. Both drivers and
> + * devices are registered with a bus driver. The bus driver matches
> + * devices to drivers based on information in the device and driver
> + * structures.
> + *
> + * The probe function is called when the bus driver matches a device
> + * to this driver. The remove function is called when a device is
> + * unregistered with the bus driver.
> + */
> +static const struct of_device_id dwc_otg_match[] = {
> + {.compatible = "amcc,dwc-otg",},
> + {}
Do you need this line?
> +};
> +
> +MODULE_DEVICE_TABLE(of, dwc_otg_match);
> +
> +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,
> + .of_match_table = dwc_otg_match,
> + },
> +};
> +
> +/**
> + * This function is called when the dwc_otg_driver is installed with
the
> + * insmod command. It registers the dwc_otg_driver structure with the
> + * appropriate bus driver. This will cause the dwc_otg_driver_probe
function
> + * to be called. In addition, the bus driver will automatically expose
> + * attributes defined for the device and driver in the special sysfs
file
> + * system.
> + */
> +static int __init dwc_otg_driver_init(void)
> +{
> +
> + pr_info("%s: version %s\n", dwc_driver_name, DWC_DRIVER_VERSION);
> + return 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/otg/dwc/driver.h b/drivers/usb/otg/dwc/driver.h
> new file mode 100644
> index 0000000..a86532b
> --- /dev/null
> +++ b/drivers/usb/otg/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/otg/dwc/param.c b/drivers/usb/otg/dwc/param.c
> new file mode 100644
> index 0000000..523f0db
> --- /dev/null
> +++ b/drivers/usb/otg/dwc/param.c
> @@ -0,0 +1,180 @@
> +/*
> + * DesignWare HS OTG controller driver
> + * Copyright (C) 2006 Synopsys, Inc.
> + * Portions Copyright (C) 2010 Applied Micro Circuits Corporation.
> + *
> + * This program is free software: you can redistribute it and/or
> + * modify it under the terms of the GNU General Public License
> + * version 2 as published by the Free Software Foundation.
> + *
> + * This program is distributed in the hope that it will be useful
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
> + * GNU General Public License version 2 for more details.
> + *
> + * You should have received a copy of the GNU General Public License
> + * along with this program; if not, see http://www.gnu.org/licenses
> + * or write to the Free Software Foundation, Inc., 51 Franklin Street,
> + * Suite 500, Boston, MA 02110-1335 USA.
> + *
> + * Based on Synopsys driver version 2.60a
> + * Modified by Mark Miesfeld <mmiesfeld@apm.com>
> + *
> + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS"
> + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING BUT NOT LIMITED TO
THE
> + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
PURPOSE
> + * ARE DISCLAIMED. IN NO EVENT SHALL SYNOPSYS, INC. BE LIABLE FOR ANY
DIRECT,
> + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES
> + * (INCLUDING BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES;
> + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND
> + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY OR
TORT
> + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
USE OF
> + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
> + *
> + */
> +
> +/*
> + * This file provides dwc_otg driver parameter and parameter checking.
> + */
> +
> +#include "cil.h"
> +
> +/*
> + * Encapsulate the module parameter settings
> + */
> +struct core_params dwc_otg_module_params = {
> + .otg_cap = -1,
> + .dma_enable = -1,
> + .dma_burst_size = -1,
> + .speed = -1,
> + .host_support_fs_ls_low_power = -1,
> + .host_ls_low_power_phy_clk = -1,
> + .enable_dynamic_fifo = -1,
> + .dev_rx_fifo_size = -1,
> + .dev_nperio_tx_fifo_size = -1,
> + .dev_perio_tx_fifo_size = {-1, -1, -1, -1, -1, -1, -1, -1,
> + -1, -1, -1, -1, -1, -1, -1}, /* 15 */
> + .host_rx_fifo_size = -1,
> + .host_nperio_tx_fifo_size = -1,
> + .host_perio_tx_fifo_size = -1,
> + .max_transfer_size = -1,
> + .max_packet_count = -1,
> + .host_channels = -1,
> + .dev_endpoints = -1,
> + .phy_type = -1,
> + .phy_utmi_width = -1,
> + .phy_ulpi_ddr = -1,
> + .phy_ulpi_ext_vbus = -1,
> + .i2c_enable = -1,
> + .ulpi_fs_ls = -1,
> + .ts_dline = -1,
> + .en_multiple_tx_fifo = -1,
> + .dev_tx_fifo_size = {-1, -1, -1, -1, -1, -1, -1, -1, -1,
> + -1, -1, -1, -1, -1, -1}, /* 15 */
> + .thr_ctl = -1,
> + .tx_thr_length = -1,
> + .rx_thr_length = -1,
> +};
> +
> +/**
> + * Checks that parameter settings for the periodic Tx FIFO sizes are
correct
> + * according to the hardware configuration. Sets the size to the
hardware
> + * configuration if an incorrect size is detected.
> + */
> +static int set_valid_perio_tx_fifo_sizes(struct core_if *core_if)
> +{
> + ulong regs = (u32) core_if->core_global_regs;
> + u32 *param_size =
&dwc_otg_module_params.dev_perio_tx_fifo_size[0];
> + u32 i, size;
Tabify the declarations;
> +
> + for (i = 0; i < MAX_PERIO_FIFOS; i++, param_size++) {
> + size = dwc_read32(regs + DWC_DPTX_FSIZ_DIPTXF(i));
> + *param_size = size;
> + }
> + return 0;
> +}
> +
> +/**
> + * Checks that parameter settings for the Tx FIFO sizes are correct
according to
> + * the hardware configuration. Sets the size to the hardware
configuration if
> + * an incorrect size is detected.
> + */
> +static int set_valid_tx_fifo_sizes(struct core_if *core_if)
> +{
> + ulong regs = (u32) core_if->core_global_regs;
> + u32 *param_size = &dwc_otg_module_params.dev_tx_fifo_size[0];
> + u32 i, size;
Tabify the declarations ;
> +
> + for (i = 0; i < MAX_TX_FIFOS; i++, param_size) {
> + size = dwc_read32(regs + DWC_DPTX_FSIZ_DIPTXF(i));
> + *param_size = size;
> + }
> + return 0;
> +}
> +
> +/**
> + * This function is called during module intialization to verify that
> + * the module parameters are in a valid state.
> + */
> +int __devinit check_parameters(struct core_if *core_if)
> +{
> + /* Default values */
> + dwc_otg_module_params.otg_cap = dwc_param_otg_cap_default;
> + dwc_otg_module_params.dma_enable = dwc_param_dma_enable_default;
> + dwc_otg_module_params.speed = dwc_param_speed_default;
> + dwc_otg_module_params.host_support_fs_ls_low_power =
> + dwc_param_host_support_fs_ls_low_power_default;
> + dwc_otg_module_params.host_ls_low_power_phy_clk =
> + dwc_param_host_ls_low_power_phy_clk_default;
> + dwc_otg_module_params.phy_type = dwc_param_phy_type_default;
> + dwc_otg_module_params.phy_ulpi_ddr =
dwc_param_phy_ulpi_ddr_default;
> + dwc_otg_module_params.phy_ulpi_ext_vbus =
> + dwc_param_phy_ulpi_ext_vbus_default;
> + dwc_otg_module_params.i2c_enable = dwc_param_i2c_enable_default;
> + dwc_otg_module_params.ulpi_fs_ls = dwc_param_ulpi_fs_ls_default;
> + dwc_otg_module_params.ts_dline = dwc_param_ts_dline_default;
> +
> + dwc_otg_module_params.dma_burst_size =
dwc_param_dma_burst_size_default;
> + dwc_otg_module_params.phy_utmi_width =
dwc_param_phy_utmi_width_default;
> + dwc_otg_module_params.thr_ctl = dwc_param_thr_ctl_default;
> + dwc_otg_module_params.tx_thr_length =
dwc_param_tx_thr_length_default;
> + dwc_otg_module_params.rx_thr_length =
dwc_param_rx_thr_length_default;
> +
> + /*
> + * Hardware configurations of the OTG core.
> + */
> + dwc_otg_module_params.enable_dynamic_fifo =
> + DWC_HWCFG2_DYN_FIFO_RD(core_if->hwcfg2);
> + dwc_otg_module_params.dev_rx_fifo_size =
> + dwc_read32(core_if->core_global_regs + DWC_GRXFSIZ);
> + dwc_otg_module_params.dev_nperio_tx_fifo_size =
> + dwc_read32(core_if->core_global_regs + DWC_GNPTXFSIZ) >> 16;
> +
> + dwc_otg_module_params.host_rx_fifo_size =
> + dwc_read32(core_if->core_global_regs + DWC_GRXFSIZ);
> + dwc_otg_module_params.host_nperio_tx_fifo_size =
> + dwc_read32(core_if->core_global_regs + DWC_GNPTXFSIZ) >> 16;
> + dwc_otg_module_params.host_perio_tx_fifo_size =
> + dwc_read32(core_if->core_global_regs + DWC_HPTXFSIZ) >> 16;
> + dwc_otg_module_params.max_transfer_size =
> + (1 << (DWC_HWCFG3_XFERSIZE_CTR_WIDTH_RD(core_if->hwcfg3) +
11))
> + - 1;
> + dwc_otg_module_params.max_packet_count =
> + (1 << (DWC_HWCFG3_PKTSIZE_CTR_WIDTH_RD(core_if->hwcfg3) + 4))
> + - 1;
> +
> + dwc_otg_module_params.host_channels =
> + DWC_HWCFG2_NO_HST_CHAN_RD(core_if->hwcfg2) + 1;
> + dwc_otg_module_params.dev_endpoints =
> + DWC_HWCFG2_NO_DEV_EP_RD(core_if->hwcfg2);
> + dwc_otg_module_params.en_multiple_tx_fifo =
> + (DWC_HWCFG4_DED_FIFO_ENA_RD(core_if->hwcfg4) == 0)
> + ? 0 : 1, 0;
> + set_valid_perio_tx_fifo_sizes(core_if);
> + set_valid_tx_fifo_sizes(core_if);
> +
> + return 0;
> +}
> +
> +module_param_named(dma_enable, dwc_otg_module_params.dma_enable, bool,
0444);
> +MODULE_PARM_DESC(dma_enable, "DMA Mode 0=Slave 1=DMA enabled");
> --
> 1.6.1.rc3
>
> --
> 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 http://vger.kernel.org/majordomo-info.html
^ permalink raw reply
* RE: [PATCH v10 03/10] USB/ppc4xx: Add Synopsys DWC OTG Core Interface Layer (CIL)
From: Keshava Munegowda @ 2011-03-29 10:51 UTC (permalink / raw)
To: tmarri, linux-usb, linuxppc-dev; +Cc: greg, Mark Miesfeld, Fushen Chen
In-Reply-To: <1301336866-1915-1-git-send-email-tmarri@apm.com>
> +/**
> + * This function initializes the DWC_otg controller registers and
prepares the
> + * core for device mode or host mode operation.
> + */
> +void dwc_otg_core_init(struct core_if *core_if)
> +{
> + u32 i;
> + ulong global_reg = core_if->core_global_regs;
> + struct device_if *dev_if = core_if->dev_if;
> + u32 ahbcfg = 0;
> + u32 i2cctl = 0;
> + u32 gusbcfg;
Tabify the declarations ;
> +
> + /* Common Initialization */
> + gusbcfg = dwc_read32(global_reg + DWC_GUSBCFG);
> +
> + /* Program the ULPI External VBUS bit if needed */
> + gusbcfg |= DWC_USBCFG_ULPI_EXT_VBUS_DRV;
> +
> + /* Set external TS Dline pulsing */
> + if (core_if->core_params->ts_dline == 1)
> + gusbcfg |= DWC_USBCFG_TERM_SEL_DL_PULSE;
> + else
> + gusbcfg = gusbcfg & (~((u32)
DWC_USBCFG_TERM_SEL_DL_PULSE));
> +
> + dwc_write32(global_reg + DWC_GUSBCFG, gusbcfg);
> +
> + /* Reset the Controller */
> + dwc_otg_core_reset(core_if);
> +
> + /* Initialize parameters from Hardware configuration registers. */
> + dev_if->num_in_eps = calc_num_in_eps(core_if);
> + dev_if->num_out_eps = calc_num_out_eps(core_if);
> +
> + for (i = 0; i <
DWC_HWCFG4_NUM_DEV_PERIO_IN_EP_RD(core_if->hwcfg4);
> + i++) {
> + dev_if->perio_tx_fifo_size[i] =
> + dwc_read32(global_reg + DWC_DPTX_FSIZ_DIPTXF(i)) >>
16;
> + }
> + for (i = 0; i < DWC_HWCFG4_NUM_IN_EPS_RD(core_if->hwcfg4); i++) {
> + dev_if->tx_fifo_size[i] =
> + dwc_read32(global_reg + DWC_DPTX_FSIZ_DIPTXF(i)) >>
16;
> + }
> +
> + core_if->total_fifo_size =
DWC_HWCFG3_DFIFO_DEPTH_RD(core_if->hwcfg3);
> + core_if->rx_fifo_size = dwc_read32(global_reg + DWC_GRXFSIZ);
> + core_if->nperio_tx_fifo_size =
> + dwc_read32(global_reg + DWC_GRXFSIZ) >> 16;
> + /*
> + * This programming sequence needs to happen in FS mode before any
> + * other programming occurs
> + */
> + if (core_if->core_params->speed == DWC_SPEED_PARAM_FULL &&
> + core_if->core_params->phy_type == DWC_PHY_TYPE_PARAM_FS) {
> + /*
> + * core_init() is now called on every switch so only call
the
> + * following for the first time through.
> + */
> + if (!core_if->phy_init_done) {
> + core_if->phy_init_done = 1;
> + gusbcfg = dwc_read32(global_reg + DWC_GUSBCFG);
> + gusbcfg |= DWC_USBCFG_ULPI_UTMI_SEL;
> + dwc_write32(global_reg + DWC_GUSBCFG, gusbcfg);
> +
> + /* Reset after a PHY select */
> + dwc_otg_core_reset(core_if);
> + }
> +
> + /*
> + * Program DCFG.DevSpd or HCFG.FSLSPclkSel to 48Mhz in FS.
> + * Also do this on HNP Dev/Host mode switches (done in
dev_init
> + * and host_init).
> + */
> + if (dwc_otg_is_host_mode(core_if))
> + init_fslspclksel(core_if);
> + else
> + init_devspd(core_if);
> +
> + if (core_if->core_params->i2c_enable) {
> + /* Program GUSBCFG.OtgUtmifsSel to I2C */
> + gusbcfg = dwc_read32(global_reg + DWC_GUSBCFG);
> + gusbcfg |= DWC_USBCFG_OTGUTMIFSSEL;
> + dwc_write32(global_reg + DWC_GUSBCFG, gusbcfg);
> +
> + /* Program GI2CCTL.I2CEn */
> + i2cctl = dwc_read32(global_reg + DWC_GI2CCTL);
> + i2cctl |= DWC_I2CCTL_I2CDEVADDR(1);
> + i2cctl &= ~DWC_I2CCTL_I2CEN;
> + dwc_write32(global_reg + DWC_GI2CCTL, i2cctl);
> + i2cctl |= DWC_I2CCTL_I2CEN;
> + dwc_write32(global_reg + DWC_GI2CCTL, i2cctl);
> + }
> + } else if (!core_if->phy_init_done) {
> + /*
> + * High speed PHY. These parameters are preserved during
soft
> + * reset so only program them the first time. Do a soft
reset
> + * immediately after setting phyif.
> + */
> + gusbcfg = dwc_read32(global_reg + DWC_GUSBCFG);
> + core_if->phy_init_done = 1;
> + if (core_if->core_params->phy_type)
> + gusbcfg |= DWC_USBCFG_ULPI_UTMI_SEL;
> + else
> + gusbcfg &= ~((u32) DWC_USBCFG_ULPI_UTMI_SEL);
> +
> + if (gusbcfg & DWC_USBCFG_ULPI_UTMI_SEL) {
> + /* ULPI interface */
> + gusbcfg |= DWC_USBCFG_PHYIF;
> + if (core_if->core_params->phy_ulpi_ddr)
> + gusbcfg |= DWC_USBCFG_DDRSEL;
> + else
> + gusbcfg &= ~((u32) DWC_USBCFG_DDRSEL);
> + } else {
> + /* UTMI+ interface */
> + if (core_if->core_params->phy_utmi_width == 16)
> + gusbcfg |= DWC_USBCFG_PHYIF;
> + else
> + gusbcfg &= ~((u32) DWC_USBCFG_PHYIF);
> + }
> + dwc_write32(global_reg + DWC_GUSBCFG, gusbcfg);
> +
> + /* Reset after setting the PHY parameters */
> + dwc_otg_core_reset(core_if);
> + }
> +
> + if (DWC_HWCFG2_HS_PHY_TYPE_RD(core_if->hwcfg2) == 2 &&
> + DWC_HWCFG2_FS_PHY_TYPE_RD(core_if->hwcfg2) == 1 &&
> + core_if->core_params->ulpi_fs_ls) {
> + gusbcfg = dwc_read32(global_reg + DWC_GUSBCFG);
> + gusbcfg |= DWC_USBCFG_ULPI_FSLS;
> + gusbcfg |= DWC_USBCFG_ULPI_CLK_SUS_M;
> + dwc_write32(global_reg + DWC_GUSBCFG, gusbcfg);
> + } else {
> + gusbcfg = dwc_read32(global_reg + DWC_GUSBCFG);
> + gusbcfg &= ~((u32) DWC_USBCFG_ULPI_FSLS);
> + gusbcfg &= ~((u32) DWC_USBCFG_ULPI_CLK_SUS_M);
> + dwc_write32(global_reg + DWC_GUSBCFG, gusbcfg);
> + }
> +
> + /* Program the GAHBCFG Register. */
> + switch (DWC_HWCFG2_ARCH_RD(core_if->hwcfg2)) {
> + case DWC_SLAVE_ONLY_ARCH:
> + ahbcfg &= ~DWC_AHBCFG_NPFIFO_EMPTY; /* HALF empty */
> + ahbcfg &= ~DWC_AHBCFG_FIFO_EMPTY; /* HALF empty */
> + core_if->dma_enable = 0;
> + break;
> + case DWC_EXT_DMA_ARCH:
> + ahbcfg = (ahbcfg & ~DWC_AHBCFG_BURST_LEN(0xf)) |
> +
DWC_AHBCFG_BURST_LEN(core_if->core_params->dma_burst_size);
> + core_if->dma_enable = (core_if->core_params->dma_enable !=
0);
> + break;
> + case DWC_INT_DMA_ARCH:
> + ahbcfg = (ahbcfg & ~DWC_AHBCFG_BURST_LEN(0xf)) |
> + DWC_AHBCFG_BURST_LEN(DWC_GAHBCFG_INT_DMA_BURST_INCR);
> + core_if->dma_enable = (core_if->core_params->dma_enable !=
0);
> + break;
> + }
> +
> + if (core_if->dma_enable)
> + ahbcfg |= DWC_AHBCFG_DMA_ENA;
> + else
> + ahbcfg &= ~DWC_AHBCFG_DMA_ENA;
> + dwc_write32(global_reg + DWC_GAHBCFG, ahbcfg);
> + core_if->en_multiple_tx_fifo =
> + DWC_HWCFG4_DED_FIFO_ENA_RD(core_if->hwcfg4);
> +
> + /* Program the GUSBCFG register. */
> + gusbcfg = dwc_read32(global_reg + DWC_GUSBCFG);
> + switch (DWC_HWCFG2_OP_MODE_RD(core_if->hwcfg2)) {
> + case DWC_MODE_HNP_SRP_CAPABLE:
> + if (core_if->core_params->otg_cap ==
> + DWC_OTG_CAP_PARAM_HNP_SRP_CAPABLE)
> + gusbcfg |= DWC_USBCFG_HNP_CAP;
> + else
> + gusbcfg &= ~((u32) DWC_USBCFG_HNP_CAP);
> + if (core_if->core_params->otg_cap !=
> + DWC_OTG_CAP_PARAM_NO_HNP_SRP_CAPABLE)
> + gusbcfg |= DWC_USBCFG_SRP_CAP;
> + else
> + gusbcfg &= ~((u32) DWC_USBCFG_SRP_CAP);
> + break;
> + case DWC_MODE_SRP_ONLY_CAPABLE:
> + gusbcfg &= ~((u32) DWC_USBCFG_HNP_CAP);
> + if (core_if->core_params->otg_cap !=
> + DWC_OTG_CAP_PARAM_NO_HNP_SRP_CAPABLE)
> + gusbcfg |= DWC_USBCFG_SRP_CAP;
> + else
> + gusbcfg &= ~((u32) DWC_USBCFG_SRP_CAP);
> + break;
> + case DWC_MODE_NO_HNP_SRP_CAPABLE:
> + gusbcfg &= ~((u32) DWC_USBCFG_HNP_CAP);
> + gusbcfg &= ~((u32) DWC_USBCFG_SRP_CAP);
> + break;
> + case DWC_MODE_SRP_CAPABLE_DEVICE:
> + gusbcfg &= ~((u32) DWC_USBCFG_HNP_CAP);
> + if (core_if->core_params->otg_cap !=
> + DWC_OTG_CAP_PARAM_NO_HNP_SRP_CAPABLE)
> + gusbcfg |= DWC_USBCFG_SRP_CAP;
> + else
> + gusbcfg &= ~((u32) DWC_USBCFG_SRP_CAP);
> + break;
> + case DWC_MODE_NO_SRP_CAPABLE_DEVICE:
> + gusbcfg &= ~((u32) DWC_USBCFG_HNP_CAP);
> + gusbcfg &= ~((u32) DWC_USBCFG_SRP_CAP);
> + break;
> + case DWC_MODE_SRP_CAPABLE_HOST:
> + gusbcfg &= ~((u32) DWC_USBCFG_HNP_CAP);
> + if (core_if->core_params->otg_cap !=
> + DWC_OTG_CAP_PARAM_NO_HNP_SRP_CAPABLE)
> + gusbcfg |= DWC_USBCFG_SRP_CAP;
> + else
> + gusbcfg &= ~((u32) DWC_USBCFG_SRP_CAP);
> + break;
> + case DWC_MODE_NO_SRP_CAPABLE_HOST:
> + gusbcfg &= ~((u32) DWC_USBCFG_HNP_CAP);
> + gusbcfg &= ~((u32) DWC_USBCFG_SRP_CAP);
> + break;
> + }
> + dwc_write32(global_reg + DWC_GUSBCFG, gusbcfg);
> +
> + /* Enable common interrupts */
> + dwc_otg_enable_common_interrupts(core_if);
> +
> + /*
> + * Do device or host intialization based on mode during PCD
> + * and HCD initialization
> + */
> + if (dwc_otg_is_host_mode(core_if)) {
> + core_if->xceiv->state = OTG_STATE_A_HOST;
> + } else {
> + core_if->xceiv->state = OTG_STATE_B_PERIPHERAL;
> + if (dwc_has_feature(core_if, DWC_DEVICE_ONLY))
> + dwc_otg_core_dev_init(core_if);
> + }
> +}
> +
> +/**
> + * This function enables the Device mode interrupts.
> + *
> + * Note that the bits in the Device IN endpoint mask register are laid
out
> + * exactly the same as the Device IN endpoint interrupt register.
> + */
> +static void dwc_otg_enable_device_interrupts(struct core_if *core_if)
> +{
> + u32 intr_mask = 0;
> + u32 msk = 0;
> + ulong global_regs = core_if->core_global_regs;
> +
> + /* Disable all interrupts. */
> + dwc_write32(global_regs + DWC_GINTMSK, 0);
> +
> + /* Clear any pending interrupts */
> + dwc_write32(global_regs + DWC_GINTSTS, 0xFFFFFFFF);
> +
> + /* Enable the common interrupts */
> + dwc_otg_enable_common_interrupts(core_if);
> +
> + /* Enable interrupts */
> + intr_mask |= DWC_INTMSK_USB_RST;
> + intr_mask |= DWC_INTMSK_ENUM_DONE;
> + intr_mask |= DWC_INTMSK_IN_ENDP;
> + intr_mask |= DWC_INTMSK_OUT_ENDP;
> + intr_mask |= DWC_INTMSK_EARLY_SUSP;
> + if (!core_if->en_multiple_tx_fifo)
> + intr_mask |= DWC_INTMSK_ENDP_MIS_MTCH;
> +
> + /* Periodic EP */
> + intr_mask |= DWC_INTMSK_ISYNC_OUTPKT_DRP;
> + intr_mask |= DWC_INTMSK_END_OF_PFRM;
> + intr_mask |= DWC_INTMSK_INCMP_IN_ATX;
> + intr_mask |= DWC_INTMSK_INCMP_OUT_PTX;
> +
> + dwc_modify32(global_regs + DWC_GINTMSK, intr_mask, intr_mask);
> +
> + msk = DWC_DIEPMSK_TXFIFO_UNDERN_RW(msk, 1);
> + dwc_modify32(core_if->dev_if->dev_global_regs + DWC_DIEPMSK,
> + msk, msk);
> +}
> +
> +/**
> + * Configures the device data fifo sizes when dynamic sizing is
enabled.
> + */
> +static void config_dev_dynamic_fifos(struct core_if *core_if)
> +{
> + u32 i;
> + ulong regs = core_if->core_global_regs;
> + struct core_params *params = core_if->core_params;
> + u32 txsize = 0;
> + u32 nptxsize = 0;
> + u32 ptxsize = 0;
Tabify the declarations ;
> +
> + /* Rx FIFO */
> + dwc_write32(regs + DWC_GRXFSIZ, params->dev_rx_fifo_size);
> +
> + /* Set Periodic and Non-periodic Tx FIFO Mask bits to all 0 */
> + core_if->p_tx_msk = 0;
> + core_if->tx_msk = 0;
> +
> + if (core_if->en_multiple_tx_fifo == 0) {
> + /* Non-periodic Tx FIFO */
> + nptxsize = DWC_RX_FIFO_DEPTH_WR(nptxsize,
> + params->
> + dev_nperio_tx_fifo_size);
> + nptxsize =
> + DWC_RX_FIFO_START_ADDR_WR(nptxsize,
> + params->dev_rx_fifo_size);
> + dwc_write32(regs + DWC_GNPTXFSIZ, nptxsize);
> +
> + /*
> + * Periodic Tx FIFOs These FIFOs are numbered from 1 to
> + * 15. Indexes of the FIFO size module parameters in the
> + * dev_perio_tx_fifo_size array and the FIFO size
> + * registers in the dptxfsiz array run from 0 to 14.
> + */
> + ptxsize = DWC_RX_FIFO_START_ADDR_WR(ptxsize,
> +
(DWC_RX_FIFO_START_ADDR_RD
> + (nptxsize) +
> + DWC_RX_FIFO_DEPTH_RD
> + (nptxsize)));
> + for (i = 0;
> + i <
DWC_HWCFG4_NUM_DEV_PERIO_IN_EP_RD(core_if->hwcfg4);
> + i++) {
> + ptxsize =
> + DWC_RX_FIFO_DEPTH_WR(ptxsize,
> + params->
> +
dev_perio_tx_fifo_size[i]);
> + dwc_write32(regs + DWC_DPTX_FSIZ_DIPTXF(i),
ptxsize);
> + ptxsize = DWC_RX_FIFO_START_ADDR_WR(ptxsize,
> +
(DWC_RX_FIFO_START_ADDR_RD
> + (ptxsize) +
> + DWC_RX_FIFO_DEPTH_RD
> + (ptxsize)));
> + }
> + } else {
> + /*
> + * Non-periodic Tx FIFOs These FIFOs are numbered from
> + * 1 to 15. Indexes of the FIFO size module parameters
> + * in the dev_tx_fifo_size array and the FIFO size
> + * registers in the dptxfsiz_dieptxf array run from 0 to
> + * 14.
> + */
> + nptxsize = DWC_RX_FIFO_DEPTH_WR(nptxsize,
> + params->
> + dev_nperio_tx_fifo_size);
> + nptxsize =
> + DWC_RX_FIFO_START_ADDR_WR(nptxsize,
> + params->dev_rx_fifo_size);
> + dwc_write32(regs + DWC_GNPTXFSIZ, nptxsize);
> +
> + txsize = DWC_RX_FIFO_START_ADDR_WR(txsize,
> +
(DWC_RX_FIFO_START_ADDR_RD
> + (nptxsize) +
> + DWC_RX_FIFO_DEPTH_RD
> + (nptxsize)));
> + for (i = 1;
> + i <
DWC_HWCFG4_NUM_DEV_PERIO_IN_EP_RD(core_if->hwcfg4);
> + i++) {
> + txsize =
> + DWC_RX_FIFO_DEPTH_WR(txsize,
> +
params->dev_tx_fifo_size[i]);
> + dwc_write32(regs + DWC_DPTX_FSIZ_DIPTXF(i - 1),
txsize);
> + txsize = DWC_RX_FIFO_START_ADDR_WR(txsize,
> +
(DWC_RX_FIFO_START_ADDR_RD
> + (txsize) +
> + DWC_RX_FIFO_DEPTH_RD
> + (txsize)));
> + }
> + }
> +}
> +
> +/**
> + * This function initializes the DWC_otg controller registers for
> + * device mode.
> + */
> +void dwc_otg_core_dev_init(struct core_if *c_if)
> +{
> + u32 i;
> + struct device_if *d_if = c_if->dev_if;
> + struct core_params *params = c_if->core_params;
> + u32 dcfg = 0;
> + u32 resetctl = 0;
> + u32 dthrctl = 0;
Tabify the declarations ;
> +
> + /* Restart the Phy Clock */
> + dwc_write32(c_if->pcgcctl, 0);
> +
> + /* Device configuration register */
> + init_devspd(c_if);
> + dcfg = dwc_read32(d_if->dev_global_regs + DWC_DCFG);
> + dcfg = DWC_DCFG_P_FRM_INTRVL_WR(dcfg, DWC_DCFG_FRAME_INTERVAL_80);
> + dwc_write32(d_if->dev_global_regs + DWC_DCFG, dcfg);
> +
> + /* If needed configure data FIFO sizes */
> + if (DWC_HWCFG2_DYN_FIFO_RD(c_if->hwcfg2) &&
params->enable_dynamic_fifo)
> + config_dev_dynamic_fifos(c_if);
> +
> + /* Flush the FIFOs */
> + dwc_otg_flush_tx_fifo(c_if, DWC_GRSTCTL_TXFNUM_ALL);
> + dwc_otg_flush_rx_fifo(c_if);
> +
> + /* Flush the Learning Queue. */
> + resetctl |= DWC_RSTCTL_TKN_QUE_FLUSH;
> + dwc_write32(c_if->core_global_regs + DWC_GRSTCTL, resetctl);
> +
> + /* Clear all pending Device Interrupts */
> + dwc_write32(d_if->dev_global_regs + DWC_DIEPMSK, 0);
> + dwc_write32(d_if->dev_global_regs + DWC_DOEPMSK, 0);
> + dwc_write32(d_if->dev_global_regs + DWC_DAINT, 0xFFFFFFFF);
> + dwc_write32(d_if->dev_global_regs + DWC_DAINTMSK, 0);
> +
> + for (i = 0; i <= d_if->num_in_eps; i++) {
> + u32 depctl = 0;
> +
> + depctl = dwc_read32(d_if->in_ep_regs[i] + DWC_DIEPCTL);
> + if (DWC_DEPCTL_EPENA_RD(depctl)) {
> + depctl = 0;
> + depctl = DWC_DEPCTL_EPDIS_RW(depctl, 1);
> + depctl = DWC_DEPCTL_SET_NAK_RW(depctl, 1);
> + } else {
> + depctl = 0;
> + }
> +
> + dwc_write32(d_if->in_ep_regs[i] + DWC_DIEPCTL, depctl);
> + dwc_write32(d_if->in_ep_regs[i] + DWC_DIEPTSIZ, 0);
> + dwc_write32(d_if->in_ep_regs[i] + DWC_DIEPDMA, 0);
> + dwc_write32(d_if->in_ep_regs[i] + DWC_DIEPINT, 0xFF);
> + }
> +
> + for (i = 0; i <= d_if->num_out_eps; i++) {
> + u32 depctl = 0;
> + depctl = dwc_read32(d_if->out_ep_regs[i] + DWC_DOEPCTL);
> + if (DWC_DEPCTL_EPENA_RD(depctl)) {
> + depctl = 0;
> + depctl = DWC_DEPCTL_EPDIS_RW(depctl, 1);
> + depctl = DWC_DEPCTL_SET_NAK_RW(depctl, 1);
> + } else {
> + depctl = 0;
> + }
> + dwc_write32(d_if->out_ep_regs[i] + DWC_DOEPCTL, depctl);
> + dwc_write32(d_if->out_ep_regs[i] + DWC_DOEPTSIZ, 0);
> + dwc_write32(d_if->out_ep_regs[i] + DWC_DOEPDMA, 0);
> + dwc_write32(d_if->out_ep_regs[i] + DWC_DOEPINT, 0xFF);
> + }
> +
> + if (c_if->en_multiple_tx_fifo && c_if->dma_enable) {
> + d_if->non_iso_tx_thr_en = c_if->core_params->thr_ctl &
0x1;
> + d_if->iso_tx_thr_en = (c_if->core_params->thr_ctl >> 1) &
0x1;
> + d_if->rx_thr_en = (c_if->core_params->thr_ctl >> 2) & 0x1;
> + d_if->rx_thr_length = c_if->core_params->rx_thr_length;
> + d_if->tx_thr_length = c_if->core_params->tx_thr_length;
> +
> + dthrctl = 0;
> + dthrctl = DWC_DTHCTRL_NON_ISO_THR_ENA_RW
> + (dthrctl, d_if->non_iso_tx_thr_en);
> + dthrctl = DWC_DTHCTRL_ISO_THR_EN_RW
> + (dthrctl, d_if->iso_tx_thr_en);
> + dthrctl = DWC_DTHCTRL_TX_THR_LEN_RW
> + (dthrctl, d_if->tx_thr_length);
> + dthrctl = DWC_DTHCTRL_RX_THR_EN_RW(dthrctl,
d_if->rx_thr_en);
> + dthrctl = DWC_DTHCTRL_RX_THR_LEN_RW
> + (dthrctl, d_if->rx_thr_length);
> + dwc_write32(d_if->dev_global_regs +
> + DWC_DTKNQR3_DTHRCTL, dthrctl);
> +
> + }
> +
> + dwc_otg_enable_device_interrupts(c_if);
> +}
> +
> +/**
> + * This function reads a packet from the Rx FIFO into the destination
buffer.
> + * To read SETUP data use dwc_otg_read_setup_packet.
> + */
> +void dwc_otg_read_packet(struct core_if *core_if, u8 * dest, u16
_bytes)
> +{
> + u32 i;
> + int word_count = (_bytes + 3) / 4;
> + u32 fifo = core_if->data_fifo[0];
> + u32 *data_buff = (u32 *) dest;
> +
> + /*
> + * This requires reading data from the FIFO into a u32 temp
buffer,
> + * then moving it into the data buffer.
> + */
> + for (i = 0; i < word_count; i++, data_buff++)
> + *data_buff = dwc_read_fifo32(fifo);
> +}
> +
> +/**
> + * Flush a Tx FIFO.
> + */
> +void dwc_otg_flush_tx_fifo(struct core_if *core_if, const int num)
> +{
> + ulong global_regs = core_if->core_global_regs;
> + u32 greset = 0;
> + int count = 0;
> +
> + greset |= DWC_RSTCTL_TX_FIFO_FLUSH;
> + greset = DWC_RSTCTL_TX_FIFO_NUM(greset, num);
> + dwc_write32(global_regs + DWC_GRSTCTL, greset);
> +
> + do {
> + greset = dwc_read32(global_regs + DWC_GRSTCTL);
> + if (++count > 10000) {
> + pr_warning("%s() HANG! GRSTCTL=%0x "
> + "GNPTXSTS=0x%08x\n", __func__, greset,
> + dwc_read32(global_regs +
DWC_GNPTXSTS));
> + break;
> + }
> + udelay(1);
> + } while (greset & DWC_RSTCTL_TX_FIFO_FLUSH);
> +
> + /* Wait for 3 PHY Clocks */
> + udelay(1);
> +}
> +
> +/**
> + * Flush Rx FIFO.
> + */
> +void dwc_otg_flush_rx_fifo(struct core_if *core_if)
> +{
> + ulong global_regs = core_if->core_global_regs;
> + u32 greset = 0;
> + int count = 0;
> +
> + greset |= DWC_RSTCTL_RX_FIFO_FLUSH;
> + dwc_write32(global_regs + DWC_GRSTCTL, greset);
> +
> + do {
> + greset = dwc_read32(global_regs + DWC_GRSTCTL);
> + if (++count > 10000) {
> + pr_warning("%s() HANG! GRSTCTL=%0x\n",
> + __func__, greset);
> + break;
> + }
> + udelay(1);
> + } while (greset & DWC_RSTCTL_RX_FIFO_FLUSH);
> +
> + /* Wait for 3 PHY Clocks */
> + udelay(1);
> +}
> +
> +/**
> + * Register HCD callbacks.
> + * The callbacks are used to start and stop the HCD for interrupt
processing.
> + */
> +void __devinit dwc_otg_cil_register_hcd_callbacks(struct core_if *c_if,
> + struct cil_callbacks
*cb,
> + void *p)
> +{
> + c_if->hcd_cb = cb;
> + cb->p = p;
> +}
> +
> +/**
> + * Register PCD callbacks.
> + * The callbacks are used to start and stop the PCD for interrupt
processing.
> + */
> +void __devinit dwc_otg_cil_register_pcd_callbacks(struct core_if *c_if,
> + struct cil_callbacks
*cb,
> + void *p)
> +{
> + c_if->pcd_cb = cb;
> + cb->p = p;
> +}
> +
> +/**
> + * This function is called to initialize the DWC_otg CSR data
structures.
> + *
> + * The register addresses in the device and host structures are
initialized from
> + * the base address supplied by the caller. The calling function must
make the
> + * OS calls to get the base address of the DWC_otg controller
registers.
> + *
> + * The params argument holds the parameters that specify how the core
should be
> + * configured.
> + */
> +struct core_if __devinit *dwc_otg_cil_init(const __iomem u32 * base,
> + struct core_params *params)
> +{
> + struct core_if *core_if;
> + struct device_if *dev_if;
> + struct dwc_host_if *host_if;
> + u8 *reg_base = (__force u8 *)base;
> + u32 offset;
> + u32 i;
Tabify the declarations ;
> +
> + core_if = kzalloc(sizeof(*core_if), GFP_KERNEL);
> + if (!core_if)
> + return NULL;
> +
> + core_if->core_params = params;
> + core_if->core_global_regs = (ulong)reg_base;
> +
> + /* Allocate the Device Mode structures. */
> + dev_if = kmalloc(sizeof(*dev_if), GFP_KERNEL);
> + if (!dev_if) {
> + kfree(core_if);
> + return NULL;
> + }
> +
> + dev_if->dev_global_regs = (ulong)(reg_base +
DWC_DEV_GLOBAL_REG_OFFSET);
> +
> + for (i = 0; i < MAX_EPS_CHANNELS; i++) {
> + offset = i * DWC_EP_REG_OFFSET;
> +
> + dev_if->in_ep_regs[i] = (ulong)(reg_base +
> + DWC_DEV_IN_EP_REG_OFFSET +
> + offset);
> +
> + dev_if->out_ep_regs[i] = (ulong)(reg_base +
> + DWC_DEV_OUT_EP_REG_OFFSET
+
> + offset);
> + }
> +
> + dev_if->speed = 0; /* unknown */
> + core_if->dev_if = dev_if;
> +
> + /* Allocate the Host Mode structures. */
> + host_if = kmalloc(sizeof(*host_if), GFP_KERNEL);
> + if (!host_if) {
> + kfree(dev_if);
> + kfree(core_if);
> + return NULL;
> + }
> +
> + host_if->host_global_regs = (ulong)(reg_base +
> +
DWC_OTG_HOST_GLOBAL_REG_OFFSET);
> +
> + host_if->hprt0 = (ulong)(reg_base +
DWC_OTG_HOST_PORT_REGS_OFFSET);
> +
> + for (i = 0; i < MAX_EPS_CHANNELS; i++) {
> + offset = i * DWC_OTG_CHAN_REGS_OFFSET;
> +
> + host_if->hc_regs[i] = (ulong)(reg_base +
> + DWC_OTG_HOST_CHAN_REGS_OFFSET
+
> + offset);
> + }
> +
> + host_if->num_host_channels = MAX_EPS_CHANNELS;
> + core_if->host_if = host_if;
> + for (i = 0; i < MAX_EPS_CHANNELS; i++) {
> + core_if->data_fifo[i] =
> + (ulong)(reg_base + DWC_OTG_DATA_FIFO_OFFSET +
> + (i * DWC_OTG_DATA_FIFO_SIZE));
> + }
> + core_if->pcgcctl = (ulong)(reg_base + DWC_OTG_PCGCCTL_OFFSET);
> +
> + /*
> + * Store the contents of the hardware configuration registers here
for
> + * easy access later.
> + */
> + core_if->hwcfg1 =
> + dwc_read32(core_if->core_global_regs + DWC_GHWCFG1);
> + core_if->hwcfg2 =
> + dwc_read32(core_if->core_global_regs + DWC_GHWCFG2);
> + core_if->hwcfg3 =
> + dwc_read32(core_if->core_global_regs + DWC_GHWCFG3);
> + core_if->hwcfg4 =
> + dwc_read32(core_if->core_global_regs + DWC_GHWCFG4);
> +
> + /* Set the SRP sucess bit for FS-I2c */
> + core_if->srp_success = 0;
> + core_if->srp_timer_started = 0;
> + return core_if;
> +}
> +
> +/**
> + * This function frees the structures allocated by dwc_otg_cil_init().
> + */
> +void dwc_otg_cil_remove(struct core_if *core_if)
> +{
> + /* Disable all interrupts */
> + dwc_modify32(core_if->core_global_regs + DWC_GAHBCFG, 1, 0);
> + dwc_write32(core_if->core_global_regs + DWC_GINTMSK, 0);
> +
> + if (core_if) {
> + kfree(core_if->dev_if);
> + kfree(core_if->host_if);
> + }
> + kfree(core_if);
> +}
> diff --git a/drivers/usb/otg/dwc/cil.h b/drivers/usb/otg/dwc/cil.h
> new file mode 100644
> index 0000000..80b7da5
> --- /dev/null
> +++ b/drivers/usb/otg/dwc/cil.h
> @@ -0,0 +1,1177 @@
> +/*
> + * DesignWare HS OTG controller driver
> + * Copyright (C) 2006 Synopsys, Inc.
> + * Portions Copyright (C) 2010 Applied Micro Circuits Corporation.
> + *
> + * This program is free software: you can redistribute it and/or
> + * modify it under the terms of the GNU General Public License
> + * version 2 as published by the Free Software Foundation.
> + *
> + * This program is distributed in the hope that it will be useful
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
> + * GNU General Public License version 2 for more details.
> + *
> + * You should have received a copy of the GNU General Public License
> + * along with this program; if not, see http://www.gnu.org/licenses
> + * or write to the Free Software Foundation, Inc., 51 Franklin Street,
> + * Suite 500, Boston, MA 02110-1335 USA.
> + *
> + * Based on Synopsys driver version 2.60a
> + * Modified by Mark Miesfeld <mmiesfeld@apm.com>
> + * Modified by Stefan Roese <sr@denx.de>, DENX Software Engineering
> + *
> + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS"
> + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING BUT NOT LIMITED TO
THE
> + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
PURPOSE
> + * ARE DISCLAIMED. IN NO EVENT SHALL SYNOPSYS, INC. BE LIABLE FOR ANY
DIRECT,
> + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES
> + * (INCLUDING BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES;
> + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND
> + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY OR
TORT
> + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
USE OF
> + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
> + *
> + */
> +
> +#if !defined(__DWC_CIL_H__)
> +#define __DWC_CIL_H__
> +#include <linux/io.h>
> +#include <linux/usb/ch9.h>
> +#include <linux/usb/gadget.h>
> +#include <linux/interrupt.h>
> +#include <linux/dmapool.h>
> +#include <linux/spinlock.h>
> +#include <linux/usb/otg.h>
> +
> +#include "regs.h"
> +
> +#ifdef CONFIG_DWC_DEBUG
> +#define DEBUG
> +#endif
> +
> +/**
> + * Reads the content of a register.
> + */
> +static inline u32 dwc_read32(ulong reg)
> +{
> +#ifdef CONFIG_DWC_OTG_REG_LE
> + return in_le32((unsigned __iomem *)reg);
> +#else
> + return in_be32((unsigned __iomem *)reg);
> +#endif
> +};
> +/**
> + * Writes a register with a 32 bit value.
> + */
> +static inline void dwc_write32(ulong reg, const u32 value)
> +{
> +#ifdef CONFIG_DWC_OTG_REG_LE
> + out_le32((unsigned __iomem *)reg, value);
> +#else
> + out_be32((unsigned __iomem *)reg, value);
> +#endif
> +};
> +
> +/**
> + * This function modifies bit values in a register. Using the
> + * algorithm: (reg_contents & ~clear_mask) | set_mask.
> + */
> +static inline
> + void dwc_modify32(ulong reg, const u32 _clear_mask, const u32
_set_mask)
> +{
> +#ifdef CONFIG_DWC_OTG_REG_LE
> + out_le32((unsigned __iomem *)reg,
> + (in_le32((unsigned __iomem *)reg) & ~_clear_mask)
|
> + _set_mask);
> +#else
> + out_be32((unsigned __iomem *)reg,
> + (in_be32(((unsigned __iomem *))reg) &
~_clear_mask) |
> + _set_mask);
> +#endif
> +};
> +
> +static inline void dwc_write_fifo32(ulong reg, const u32 _value)
> +{
> +#ifdef CONFIG_DWC_OTG_FIFO_LE
> + out_le32((unsigned __iomem *)reg, _value);
> +#else
> + out_be32((unsigned __iomem *)reg, _value);
> +#endif
> +};
> +
> +static inline u32 dwc_read_fifo32(ulong _reg)
> +{
> +#ifdef CONFIG_DWC_OTG_FIFO_LE
> + return in_le32((unsigned __iomem *) _reg);
> +#else
> + return in_be32((unsigned __iomem *) _reg);
> +#endif
> +};
> +
> +/*
> + * Debugging support vanishes in non-debug builds.
> + */
> +/* Display CIL Debug messages */
> +#define dwc_dbg_cil (0x2)
> +
> +/* Display CIL Verbose debug messages */
> +#define dwc_dbg_cilv (0x20)
> +
> +/* Display PCD (Device) debug messages */
> +#define dwc_dbg_pcd (0x4)
> +
> +/* Display PCD (Device) Verbose debug messages */
> +#define dwc_dbg_pcdv (0x40)
> +
> +/* Display Host debug messages */
> +#define dwc_dbg_hcd (0x8)
> +
> +/* Display Verbose Host debug messages */
> +#define dwc_dbg_hcdv (0x80)
> +
> +/* Display enqueued URBs in host mode. */
> +#define dwc_dbg_hcd_urb (0x800)
> +
> +/* Display "special purpose" debug messages */
> +#define dwc_dbg_sp (0x400)
> +
> +/* Display all debug messages */
> +#define dwc_dbg_any (0xFF)
> +
> +/* All debug messages off */
> +#define dwc_dbg_off 0
> +
> +/* Prefix string for DWC_DEBUG print macros. */
> +#define usb_dwc "dwc_otg: "
> +
> +/*
> + * This file contains the interface to the Core Interface Layer.
> + */
> +
> +/*
> + * Added-sr: 2007-07-26
> + *
> + * Since the 405EZ (Ultra) only support 2047 bytes as
> + * max transfer size, we have to split up bigger transfers
> + * into multiple transfers of 1024 bytes sized messages.
> + * I happens often, that transfers of 4096 bytes are
> + * required (zero-gadget, file_storage-gadget).
> + *
> + * MAX_XFER_LEN is set to 1024 right now, but could be 2047,
> + * since the xfer-size field in the 405EZ USB device controller
> + * implementation has 11 bits. Using 1024 seems to work for now.
> + */
> +#define MAX_XFER_LEN 1024
> +
> +/*
> + * The dwc_ep structure represents the state of a single endpoint when
acting in
> + * device mode. It contains the data items needed for an endpoint to be
> + * activated and transfer packets.
> + */
> +struct dwc_ep {
> + /* EP number used for register address lookup */
> + u8 num;
> + /* EP direction 0 = OUT */
> + unsigned is_in:1;
> + /* EP active. */
> + unsigned active:1;
> +
> + /*
> + * Periodic Tx FIFO # for IN EPs For INTR EP set to 0 to use
> + * non-periodic Tx FIFO If dedicated Tx FIFOs are enabled for all
> + * IN Eps - Tx FIFO # FOR IN EPs
> + */
> + unsigned tx_fifo_num:4;
> + /* EP type: 0 - Control, 1 - ISOC, 2 - BULK, 3 - INTR
*/
> + unsigned type:2;
> +#define DWC_OTG_EP_TYPE_CONTROL 0
> +#define DWC_OTG_EP_TYPE_ISOC 1
> +#define DWC_OTG_EP_TYPE_BULK 2
> +#define DWC_OTG_EP_TYPE_INTR 3
> +
> + /* DATA start PID for INTR and BULK EP */
> + unsigned data_pid_start:1;
> + /* Frame (even/odd) for ISOC EP */
> + unsigned even_odd_frame:1;
> + /* Max Packet bytes */
> + unsigned maxpacket:11;
> +
> + ulong dma_addr;
> +
> + /*
> + * Pointer to the beginning of the transfer buffer -- do not
modify
> + * during transfer.
> + */
> + u8 *start_xfer_buff;
> + /* pointer to the transfer buffer */
> + u8 *xfer_buff;
> + /* Number of bytes to transfer */
> + unsigned xfer_len:19;
> + /* Number of bytes transferred. */
> + unsigned xfer_count:19;
> + /* Sent ZLP */
> + unsigned sent_zlp:1;
> + /* Total len for control transfer */
> + unsigned total_len:19;
> +
> + /* stall clear flag */
> + unsigned stall_clear_flag:1;
> +
> + /*
> + * Added-sr: 2007-07-26
> + *
> + * Since the 405EZ (Ultra) only support 2047 bytes as
> + * max transfer size, we have to split up bigger transfers
> + * into multiple transfers of 1024 bytes sized messages.
> + * I happens often, that transfers of 4096 bytes are
> + * required (zero-gadget, file_storage-gadget).
> + *
> + * "bytes_pending" will hold the amount of bytes that are
> + * still pending to be send in further messages to complete
> + * the bigger transfer.
> + */
> + u32 bytes_pending;
> +};
> +
> +/*
> + * States of EP0.
> + */
> +enum ep0_state {
> + EP0_DISCONNECT = 0, /* no host */
> + EP0_IDLE = 1,
> + EP0_IN_DATA_PHASE = 2,
> + EP0_OUT_DATA_PHASE = 3,
> + EP0_STATUS = 4,
> + EP0_STALL = 5,
> +};
> +
> +/* Fordward declaration.*/
> +struct dwc_pcd;
> +
> +/*
> + * This structure describes an EP, there is an array of EPs in the PCD
> + * structure.
> + */
> +struct pcd_ep {
> + /* USB EP data */
> + struct usb_ep ep;
> + /* USB EP Descriptor */
> + const struct usb_endpoint_descriptor *desc;
> +
> + /* queue of dwc_otg_pcd_requests. */
> + struct list_head queue;
> + unsigned stopped:1;
> + unsigned disabling:1;
> + unsigned dma:1;
> + unsigned queue_sof:1;
> + unsigned wedged:1;
> +
> + /* DWC_otg ep data. */
> + struct dwc_ep dwc_ep;
> +
> + /* Pointer to PCD */
> + struct dwc_pcd *pcd;
> +};
> +
> +/*
> + * DWC_otg PCD Structure.
> + * This structure encapsulates the data for the dwc_otg PCD.
> + */
> +struct dwc_pcd {
> + /* USB gadget */
> + struct usb_gadget gadget;
> + /* USB gadget driver pointer */
> + struct usb_gadget_driver *driver;
> + /* The DWC otg device pointer. */
> + struct dwc_otg_device *otg_dev;
> +
> + /* State of EP0 */
> + enum ep0_state ep0state;
> + /* EP0 Request is pending */
> + unsigned ep0_pending:1;
> + /* Indicates when SET CONFIGURATION Request is in process */
> + unsigned request_config:1;
> + /* The state of the Remote Wakeup Enable. */
> + unsigned remote_wakeup_enable:1;
> + /* The state of the B-Device HNP Enable. */
> + unsigned b_hnp_enable:1;
> + /* The state of A-Device HNP Support. */
> + unsigned a_hnp_support:1;
> + /* The state of the A-Device Alt HNP support. */
> + unsigned a_alt_hnp_support:1;
> + /* Count of pending Requests */
> + unsigned request_pending;
> +
> + /*
> + * SETUP packet for EP0. This structure is allocated as a DMA
buffer on
> + * PCD initialization with enough space for up to 3 setup packets.
> + */
> + union {
> + struct usb_ctrlrequest req;
> + u32 d32[2];
> + } *setup_pkt;
> +
> + struct dma_pool *dwc_pool;
> + dma_addr_t setup_pkt_dma_handle;
> +
> + /* 2-byte dma buffer used to return status from GET_STATUS */
> + u16 *status_buf;
> + dma_addr_t status_buf_dma_handle;
> +
> + /* Array of EPs. */
> + struct pcd_ep ep0;
> + /* Array of IN EPs. */
> + struct pcd_ep in_ep[MAX_EPS_CHANNELS - 1];
> + /* Array of OUT EPs. */
> + struct pcd_ep out_ep[MAX_EPS_CHANNELS - 1];
> + spinlock_t lock;
> + /*
> + * Timer for SRP. If it expires before SRP is successful clear
the
> + * SRP.
> + */
> + struct timer_list srp_timer;
> +
> + /*
> + * Tasklet to defer starting of TEST mode transmissions until
Status
> + * Phase has been completed.
> + */
> + struct tasklet_struct test_mode_tasklet;
> +
> + /* Tasklet to delay starting of xfer in DMA mode */
> + struct tasklet_struct *start_xfer_tasklet;
> +
> + /* The test mode to enter when the tasklet is executed. */
> + unsigned test_mode;
> +};
> +
> +/*
> + * This structure holds the state of the HCD, including the
non-periodic and
> + * periodic schedules.
> + */
> +struct dwc_hcd {
> + spinlock_t lock;
> +
> + /* DWC OTG Core Interface Layer */
> + struct core_if *core_if;
> +
> + /* Internal DWC HCD Flags */
> + union dwc_otg_hcd_internal_flags {
> + u32 d32;
> + struct {
> + unsigned port_connect_status_change:1;
> + unsigned port_connect_status:1;
> + unsigned port_reset_change:1;
> + unsigned port_enable_change:1;
> + unsigned port_suspend_change:1;
> + unsigned port_over_current_change:1;
> + unsigned reserved:27;
> + } b;
> + } flags;
> +
> + /*
> + * Inactive items in the non-periodic schedule. This is a list of
> + * Queue Heads. Transfers associated with these Queue Heads are
not
> + * currently assigned to a host channel.
> + */
> + struct list_head non_periodic_sched_inactive;
> +
> + /*
> + * Deferred items in the non-periodic schedule. This is a list of
> + * Queue Heads. Transfers associated with these Queue Heads are
not
> + * currently assigned to a host channel.
> + * When we get an NAK, the QH goes here.
> + */
> + struct list_head non_periodic_sched_deferred;
> +
> + /*
> + * Active items in the non-periodic schedule. This is a list of
> + * Queue Heads. Transfers associated with these Queue Heads are
> + * currently assigned to a host channel.
> + */
> + struct list_head non_periodic_sched_active;
> +
> + /*
> + * Pointer to the next Queue Head to process in the active
> + * non-periodic schedule.
> + */
> + struct list_head *non_periodic_qh_ptr;
> +
> + /*
> + * Inactive items in the periodic schedule. This is a list of QHs
for
> + * periodic transfers that are _not_ scheduled for the next frame.
> + * Each QH in the list has an interval counter that determines
when it
> + * needs to be scheduled for execution. This scheduling mechanism
> + * allows only a simple calculation for periodic bandwidth used
(i.e.
> + * must assume that all periodic transfers may need to execute in
the
> + * same frame). However, it greatly simplifies scheduling and
should
> + * be sufficient for the vast majority of OTG hosts, which need to
> + * connect to a small number of peripherals at one time.
> + *
> + * Items move from this list to periodic_sched_ready when the QH
> + * interval counter is 0 at SOF.
> + */
> + struct list_head periodic_sched_inactive;
> +
> + /*
> + * List of periodic QHs that are ready for execution in the next
> + * frame, but have not yet been assigned to host channels.
> + *
> + * Items move from this list to periodic_sched_assigned as host
> + * channels become available during the current frame.
> + */
> + struct list_head periodic_sched_ready;
> +
> + /*
> + * List of periodic QHs to be executed in the next frame that are
> + * assigned to host channels.
> + *
> + * Items move from this list to periodic_sched_queued as the
> + * transactions for the QH are queued to the DWC_otg controller.
> + */
> + struct list_head periodic_sched_assigned;
> +
> + /*
> + * List of periodic QHs that have been queued for execution.
> + *
> + * Items move from this list to either periodic_sched_inactive or
> + * periodic_sched_ready when the channel associated with the
transfer
> + * is released. If the interval for the QH is 1, the item moves to
> + * periodic_sched_ready because it must be rescheduled for the
next
> + * frame. Otherwise, the item moves to periodic_sched_inactive.
> + */
> + struct list_head periodic_sched_queued;
> +
> + /*
> + * Total bandwidth claimed so far for periodic transfers. This
value
> + * is in microseconds per (micro)frame. The assumption is that all
> + * periodic transfers may occur in the same (micro)frame.
> + */
> + u16 periodic_usecs;
> +
> + /*
> + * Total bandwidth claimed so far for all periodic transfers
> + * in a frame.
> + * This will include a mixture of HS and FS transfers.
> + * Units are microseconds per (micro)frame.
> + * We have a budget per frame and have to schedule
> + * transactions accordingly.
> + * Watch out for the fact that things are actually scheduled for
the
> + * "next frame".
> + */
> + u16 frame_usecs[8];
> +
> + /*
> + * Frame number read from the core at SOF. The value ranges from 0
to
> + * DWC_HFNUM_MAX_FRNUM.
> + */
> + u16 frame_number;
> +
> + /*
> + * Free host channels in the controller. This is a list of
> + * struct dwc_hc items.
> + */
> + struct list_head free_hc_list;
> +
> + /*
> + * Number of available host channels.
> + */
> + u32 available_host_channels;
> +
> + /*
> + * Array of pointers to the host channel descriptors. Allows
accessing
> + * a host channel descriptor given the host channel number. This
is
> + * useful in interrupt handlers.
> + */
> + struct dwc_hc *hc_ptr_array[MAX_EPS_CHANNELS];
> +
> + /*
> + * Buffer to use for any data received during the status phase of
a
> + * control transfer. Normally no data is transferred during the
status
> + * phase. This buffer is used as a bit bucket.
> + */
> + u8 *status_buf;
> +
> + /*
> + * DMA address for status_buf.
> + */
> + dma_addr_t status_buf_dma;
> +#define DWC_OTG_HCD_STATUS_BUF_SIZE 64
> +
> + /*
> + * Structure to allow starting the HCD in a non-interrupt context
> + * during an OTG role change.
> + */
> + struct work_struct start_work;
> + struct usb_hcd *_p;
> +
> + /*
> + * Connection timer. An OTG host must display a message if the
device
> + * does not connect. Started when the VBus power is turned on via
> + * sysfs attribute "buspower".
> + */
> + struct timer_list conn_timer;
> +
> + /* workqueue for port wakeup */
> + struct work_struct usb_port_reset;
> +
> + /* Addition HCD interrupt */
> + int cp_irq; /* charge pump interrupt */
> + int cp_irq_installed;
> +};
> +
> +/*
> + * Reasons for halting a host channel.
> + */
> +enum dwc_halt_status {
> + DWC_OTG_HC_XFER_NO_HALT_STATUS,
> + DWC_OTG_HC_XFER_COMPLETE,
> + DWC_OTG_HC_XFER_URB_COMPLETE,
> + DWC_OTG_HC_XFER_ACK,
> + DWC_OTG_HC_XFER_NAK,
> + DWC_OTG_HC_XFER_NYET,
> + DWC_OTG_HC_XFER_STALL,
> + DWC_OTG_HC_XFER_XACT_ERR,
> + DWC_OTG_HC_XFER_FRAME_OVERRUN,
> + DWC_OTG_HC_XFER_BABBLE_ERR,
> + DWC_OTG_HC_XFER_DATA_TOGGLE_ERR,
> + DWC_OTG_HC_XFER_AHB_ERR,
> + DWC_OTG_HC_XFER_PERIODIC_INCOMPLETE,
> + DWC_OTG_HC_XFER_URB_DEQUEUE
> +};
> +
> +/*
> + * Host channel descriptor. This structure represents the state of a
single
> + * host channel when acting in host mode. It contains the data items
needed to
> + * transfer packets to an endpoint via a host channel.
> + */
> +struct dwc_hc {
> + /* Host channel number used for register address lookup */
> + u8 hc_num;
> +
> + /* Device to access */
> + unsigned dev_addr:7;
> +
> + /* EP to access */
> + unsigned ep_num:4;
> +
> + /* EP direction. 0: OUT, 1: IN */
> + unsigned ep_is_in:1;
> +
> + /*
> + * EP speed.
> + * One of the following values:
> + * - DWC_OTG_EP_SPEED_LOW
> + * - DWC_OTG_EP_SPEED_FULL
> + * - DWC_OTG_EP_SPEED_HIGH
> + */
> + unsigned speed:2;
> +#define DWC_OTG_EP_SPEED_LOW 0
> +#define DWC_OTG_EP_SPEED_FULL 1
> +#define DWC_OTG_EP_SPEED_HIGH 2
> +
> + /*
> + * Endpoint type.
> + * One of the following values:
> + * - DWC_OTG_EP_TYPE_CONTROL: 0
> + * - DWC_OTG_EP_TYPE_ISOC: 1
> + * - DWC_OTG_EP_TYPE_BULK: 2
> + * - DWC_OTG_EP_TYPE_INTR: 3
> + */
> + unsigned ep_type:2;
> +
> + /* Max packet size in bytes */
> + unsigned max_packet:11;
> +
> + /*
> + * PID for initial transaction.
> + * 0: DATA0,
> + * 1: DATA2,
> + * 2: DATA1,
> + * 3: MDATA (non-Control EP),
> + * SETUP (Control EP)
> + */
> + unsigned data_pid_start:2;
> +#define DWC_OTG_HC_PID_DATA0 0
> +#define DWC_OTG_HC_PID_DATA2 1
> +#define DWC_OTG_HC_PID_DATA1 2
> +#define DWC_OTG_HC_PID_MDATA 3
> +#define DWC_OTG_HC_PID_SETUP 3
> +
> + /* Number of periodic transactions per (micro)frame */
> + unsigned multi_count:2;
> +
> + /* Pointer to the current transfer buffer position. */
> + u8 *xfer_buff;
> + /* Total number of bytes to transfer. */
> + u32 xfer_len;
> + /* Number of bytes transferred so far. */
> + u32 xfer_count;
> + /* Packet count at start of transfer. */
> + u16 start_pkt_count;
> +
> + /*
> + * Flag to indicate whether the transfer has been started. Set to
1 if
> + * it has been started, 0 otherwise.
> + */
> + u8 xfer_started;
> +
> + /*
> + * Set to 1 to indicate that a PING request should be issued on
this
> + * channel. If 0, process normally.
> + */
> + u8 do_ping;
> +
> + /*
> + * Set to 1 to indicate that the error count for this transaction
is
> + * non-zero. Set to 0 if the error count is 0.
> + */
> + u8 error_state;
> +
> + /*
> + * Set to 1 to indicate that this channel should be halted the
next
> + * time a request is queued for the channel. This is necessary in
> + * slave mode if no request queue space is available when an
attempt
> + * is made to halt the channel.
> + */
> + u8 halt_on_queue;
> +
> + /*
> + * Set to 1 if the host channel has been halted, but the core is
not
> + * finished flushing queued requests. Otherwise 0.
> + */
> + u8 halt_pending;
> +
> + /* Reason for halting the host channel. */
> + enum dwc_halt_status halt_status;
> +
> + /* Split settings for the host channel */
> + u8 do_split; /* Enable split for the channel */
> + u8 complete_split; /* Enable complete split */
> + u8 hub_addr; /* Address of high speed hub */
> + u8 port_addr; /* Port of the low/full speed device */
> +
> + /*
> + * Split transaction position. One of the following values:
> + * - DWC_HCSPLIT_XACTPOS_MID
> + * - DWC_HCSPLIT_XACTPOS_BEGIN
> + * - DWC_HCSPLIT_XACTPOS_END
> + * - DWC_HCSPLIT_XACTPOS_ALL */
> + u8 xact_pos;
> +
> + /* Set when the host channel does a short read. */
> + u8 short_read;
> +
> + /*
> + * Number of requests issued for this channel since it was
assigned to
> + * the current transfer (not counting PINGs).
> + */
> + u8 requests;
> +
> + /* Queue Head for the transfer being processed by this channel. */
> + struct dwc_qh *qh;
> +
> + /* Entry in list of host channels. */
> + struct list_head hc_list_entry;
> +};
> +
> +/*
> + * The following parameters may be specified when starting the module.
These
> + * parameters define how the DWC_otg controller should be configured.
Parameter
> + * values are passed to the CIL initialization function
dwc_otg_cil_init.
> + */
> +struct core_params {
> + /*
> + * Specifies the OTG capabilities. The driver will automatically
> + * detect the value for this parameter if none is specified.
> + * 0 - HNP and SRP capable (default)
> + * 1 - SRP Only capable
> + * 2 - No HNP/SRP capable
> + */
> + int otg_cap;
> +#define DWC_OTG_CAP_PARAM_HNP_SRP_CAPABLE 0
> +#define DWC_OTG_CAP_PARAM_SRP_ONLY_CAPABLE 1
> +#define DWC_OTG_CAP_PARAM_NO_HNP_SRP_CAPABLE 2
> +
> +#define dwc_param_otg_cap_default DWC_OTG_CAP_PARAM_HNP_SRP_CAPABLE
> +
> + /*
> + * Specifies whether to use slave or DMA mode for accessing the
data
> + * FIFOs. The driver will automatically detect the value for this
> + * parameter if none is specified.
> + * 0 - Slave
> + * 1 - DMA (default, if available)
> + */
> + int dma_enable;
> +#ifdef CONFIG_DWC_SLAVE
> +#define dwc_param_dma_enable_default 0
> +#else
> +#define dwc_param_dma_enable_default 1
> +#endif
> +
> + /*
> + * The DMA Burst size (applicable only for External DMA Mode).
> + * 1, 4, 8 16, 32, 64, 128, 256 (default 32)
> + */
> + int dma_burst_size; /* Translate this to GAHBCFG values */
> +#define dwc_param_dma_burst_size_default 32
> +
> + /*
> + * Specifies the maximum speed of operation in host and device
mode.
> + * The actual speed depends on the speed of the attached device
and
> + * the value of phy_type. The actual speed depends on the speed of
the
> + * attached device.
> + * 0 - High Speed (default)
> + * 1 - Full Speed
> + */
> + int speed;
> +#define dwc_param_speed_default 0
> +#define DWC_SPEED_PARAM_HIGH 0
> +#define DWC_SPEED_PARAM_FULL 1
> +
> + /*
> + * Specifies whether low power mode is supported when attached to
a Full
> + * Speed or Low Speed device in host mode.
> + * 0 - Don't support low power mode (default)
> + * 1 - Support low power mode
> + */
> + int host_support_fs_ls_low_power;
> +#define dwc_param_host_support_fs_ls_low_power_default 0
> +
> + /*
> + * Specifies the PHY clock rate in low power mode when connected
to a
> + * Low Speed device in host mode. This parameter is applicable
only if
> + * HOST_SUPPORT_FS_LS_LOW_POWER is enabled. If PHY_TYPE is set to
FS
> + * then defaults to 6 MHZ otherwise 48 MHZ.
> + *
> + * 0 - 48 MHz
> + * 1 - 6 MHz
> + */
> + int host_ls_low_power_phy_clk;
> +#define dwc_param_host_ls_low_power_phy_clk_default 0
> +#define DWC_HOST_LS_LOW_POWER_PHY_CLK_PARAM_48MHZ 0
> +#define DWC_HOST_LS_LOW_POWER_PHY_CLK_PARAM_6MHZ 1
> +
> + /*
> + * 0 - Use cC FIFO size parameters
> + * 1 - Allow dynamic FIFO sizing (default)
> + */
> + int enable_dynamic_fifo;
> +#define dwc_param_enable_dynamic_fifo_default 1
> +
> + /*
> + * Number of 4-byte words in the Rx FIFO in device mode when
dynamic
> + * FIFO sizing is enabled. 16 to 32768 (default 1064)
> + */
> + int dev_rx_fifo_size;
> +#define dwc_param_dev_rx_fifo_size_default 1064
> +
> + /*
> + * Number of 4-byte words in the non-periodic Tx FIFO in device
mode
> + * when dynamic FIFO sizing is enabled. 16 to 32768 (default
1024)
> + */
> + int dev_nperio_tx_fifo_size;
> +#define dwc_param_dev_nperio_tx_fifo_size_default 1024
> +
> + /*
> + * Number of 4-byte words in each of the periodic Tx FIFOs in
device
> + * mode when dynamic FIFO sizing is enabled. 4 to 768 (default
256)
> + */
> + u32 dev_perio_tx_fifo_size[MAX_PERIO_FIFOS];
> +#define dwc_param_dev_perio_tx_fifo_size_default 256
> +
> + /*
> + * Number of 4-byte words in the Rx FIFO in host mode when dynamic
> + * FIFO sizing is enabled. 16 to 32768 (default 1024)
> + */
> + int host_rx_fifo_size;
> +#define dwc_param_host_rx_fifo_size_default 1024
> +
> + /*
> + * Number of 4-byte words in the non-periodic Tx FIFO in host mode
> + * when Dynamic FIFO sizing is enabled in the core. 16 to 32768
> + * (default 1024)
> + */
> + int host_nperio_tx_fifo_size;
> +#define dwc_param_host_nperio_tx_fifo_size_default 1024
> +
> + /*
> + Number of 4-byte words in the host periodic Tx FIFO when
dynamic
> + * FIFO sizing is enabled. 16 to 32768 (default 1024)
> + */
> + int host_perio_tx_fifo_size;
> +#define dwc_param_host_perio_tx_fifo_size_default 1024
> +
> + /*
> + * The maximum transfer size supported in bytes. 2047 to 65,535
> + * (default 65,535)
> + */
> + int max_transfer_size;
> +#define dwc_param_max_transfer_size_default 65535
> +
> + /*
> + * The maximum number of packets in a transfer. 15 to 511
(default 511)
> + */
> + int max_packet_count;
> +#define dwc_param_max_packet_count_default 511
> +
> + /*
> + * The number of host channel registers to use.
> + * 1 to 16 (default 12)
> + * Note: The FPGA configuration supports a maximum of 12 host
channels.
> + */
> + int host_channels;
> +#define dwc_param_host_channels_default 12
> +
> + /*
> + * The number of endpoints in addition to EP0 available for device
> + * mode operations.
> + * 1 to 15 (default 6 IN and OUT)
> + * Note: The FPGA configuration supports a maximum of 6 IN and OUT
> + * endpoints in addition to EP0.
> + */
> + int dev_endpoints;
> +#define dwc_param_dev_endpoints_default 6
> +
> + /*
> + * Specifies the type of PHY interface to use. By default, the
driver
> + * will automatically detect the phy_type.
> + *
> + * 0 - Full Speed PHY
> + * 1 - UTMI+ (default)
> + * 2 - ULPI
> + */
> + int phy_type;
> +#define DWC_PHY_TYPE_PARAM_FS 0
> +#define DWC_PHY_TYPE_PARAM_UTMI 1
> +#define DWC_PHY_TYPE_PARAM_ULPI 2
> +#define dwc_param_phy_type_default DWC_PHY_TYPE_PARAM_UTMI
> +
> + /*
> + * Specifies the UTMI+ Data Width. This parameter is applicable
for a
> + * PHY_TYPE of UTMI+ or ULPI. (For a ULPI PHY_TYPE, this parameter
> + * indicates the data width between the MAC and the ULPI Wrapper.)
Also,
> + * this parameter is applicable only if the OTG_HSPHY_WIDTH cC
parameter
> + * was set to "8 and 16 bits", meaning that the core has been
configured
> + * to work at either data path width.
> + *
> + * 8 or 16 bits (default 16)
> + */
> + int phy_utmi_width;
> +#define dwc_param_phy_utmi_width_default 16
> +
> + /*
> + * Specifies whether the ULPI operates at double or single
> + * data rate. This parameter is only applicable if PHY_TYPE is
> + * ULPI.
> + *
> + * 0 - single data rate ULPI interface with 8 bit wide data
> + * bus (default)
> + * 1 - double data rate ULPI interface with 4 bit wide data
> + * bus
> + */
> + int phy_ulpi_ddr;
> +#define dwc_param_phy_ulpi_ddr_default 0
> +
> + /*
> + * Specifies whether to use the internal or external supply to
> + * drive the vbus with a ULPI phy.
> + */
> + int phy_ulpi_ext_vbus;
> +#define DWC_PHY_ULPI_INTERNAL_VBUS 0
> +#define DWC_PHY_ULPI_EXTERNAL_VBUS 1
> +#define dwc_param_phy_ulpi_ext_vbus_default DWC_PHY_ULPI_INTERNAL_VBUS
> +
> + /*
> + * Specifies whether to use the I2Cinterface for full speed PHY.
This
> + * parameter is only applicable if PHY_TYPE is FS.
> + * 0 - No (default)
> + * 1 - Yes
> + */
> + int i2c_enable;
> +#define dwc_param_i2c_enable_default 0
> +
> + int ulpi_fs_ls;
> +#define dwc_param_ulpi_fs_ls_default 0
> +
> + int ts_dline;
> +#define dwc_param_ts_dline_default 0
> +
> + /*
> + * Specifies whether dedicated transmit FIFOs are enabled for non
> + * periodic IN endpoints in device mode
> + * 0 - No
> + * 1 - Yes
> + */
> + int en_multiple_tx_fifo;
> +#define dwc_param_en_multiple_tx_fifo_default 1
> +
> + /*
> + * Number of 4-byte words in each of the Tx FIFOs in device
> + * mode when dynamic FIFO sizing is enabled. 4 to 768 (default
256)
> + */
> + u32 dev_tx_fifo_size[MAX_TX_FIFOS];
> +#define dwc_param_dev_tx_fifo_size_default 256
> +
> + /*
> + * Thresholding enable flag
> + * bit 0 - enable non-ISO Tx thresholding
> + * bit 1 - enable ISO Tx thresholding
> + * bit 2 - enable Rx thresholding
> + */
> + u32 thr_ctl;
> +#define dwc_param_thr_ctl_default 0
> +
> + /* Thresholding length for Tx FIFOs in 32 bit DWORDs */
> + u32 tx_thr_length;
> +#define dwc_param_tx_thr_length_default 64
> +
> + /* Thresholding length for Rx FIFOs in 32 bit DWORDs */
> + u32 rx_thr_length;
> +#define dwc_param_rx_thr_length_default 64
> +
> +};
> +
> +/*
> + * The core_if structure contains information needed to manage the
> + * DWC_otg controller acting in either host or device mode. It
represents the
> + * programming view of the controller as a whole.
> + */
> +struct core_if {
> + /* Parameters that define how the core should be configured. */
> + struct core_params *core_params;
> +
> + /* Core Global registers starting at offset 000h. */
> + ulong core_global_regs;
> +
> + /* Device-specific information */
> + struct device_if *dev_if;
> + /* Host-specific information */
> + struct dwc_host_if *host_if;
> +
> + /*
> + * Set to 1 if the core PHY interface bits in USBCFG have been
> + * initialized.
> + */
> + u8 phy_init_done;
> +
> + /*
> + * SRP Success flag, set by srp success interrupt in FS I2C mode
> + */
> + u8 srp_success;
> + u8 srp_timer_started;
> +
> + /* Common configuration information */
> + /* Power and Clock Gating Control Register */
> + ulong pcgcctl;
> +#define DWC_OTG_PCGCCTL_OFFSET 0xE00
> +
> + /* Push/pop addresses for endpoints or host channels. */
> + ulong data_fifo[MAX_EPS_CHANNELS];
> +#define DWC_OTG_DATA_FIFO_OFFSET 0x1000
> +#define DWC_OTG_DATA_FIFO_SIZE 0x1000
> +
> + /* Total RAM for FIFOs (Bytes) */
> + u16 total_fifo_size;
> + /* Size of Rx FIFO (Bytes) */
> + u16 rx_fifo_size;
> + /* Size of Non-periodic Tx FIFO (Bytes) */
> + u16 nperio_tx_fifo_size;
> +
> + /* 1 if DMA is enabled, 0 otherwise. */
> + u8 dma_enable;
> +
> + /* 1 if dedicated Tx FIFOs are enabled, 0 otherwise. */
> + u8 en_multiple_tx_fifo;
> +
> + /*
> + * Set to 1 if multiple packets of a high-bandwidth transfer is
in
> + * process of being queued
> + */
> + u8 queuing_high_bandwidth;
> +
> + /* Hardware Configuration -- stored here for convenience. */
> + ulong hwcfg1;
> + ulong hwcfg2;
> + ulong hwcfg3;
> + ulong hwcfg4;
> +
> + /* HCD callbacks */
> + /* include/linux/usb/otg.h */
> +
> + /* HCD callbacks */
> + struct cil_callbacks *hcd_cb;
> + /* PCD callbacks */
> + struct cil_callbacks *pcd_cb;
> +
> + /* Device mode Periodic Tx FIFO Mask */
> + u32 p_tx_msk;
> + /* Device mode Periodic Tx FIFO Mask */
> + u32 tx_msk;
> +
> + /* Features of various DWC implementation */
> + u32 features;
> +
> + /* Added to support PLB DMA : phys-virt mapping */
> + resource_size_t phys_addr;
> +
> + struct delayed_work usb_port_wakeup;
> + struct work_struct usb_port_otg;
> + struct otg_transceiver *xceiv;
> +};
> +
> +/*
> + * The following functions support initialization of the CIL driver
component
> + * and the DWC_otg controller.
> + */
> +extern void dwc_otg_core_init(struct core_if *core_if);
> +extern void init_fslspclksel(struct core_if *core_if);
> +extern void dwc_otg_core_dev_init(struct core_if *core_if);
> +extern const char *op_state_str(enum usb_otg_state state);
> +extern void dwc_otg_enable_global_interrupts(struct core_if *core_if);
> +extern void dwc_otg_disable_global_interrupts(struct core_if *core_if);
> +extern void dwc_otg_enable_common_interrupts(struct core_if *core_if);
> +
> +/**
> + * This function Reads HPRT0 in preparation to modify. It keeps the WC
bits 0
> + * so that if they are read as 1, they won't clear when you write it
back
> + */
> +static inline u32 dwc_otg_read_hprt0(struct core_if *core_if)
> +{
> + u32 hprt0 = 0;
> + hprt0 = dwc_read32(core_if->host_if->hprt0);
> + hprt0 = DWC_HPRT0_PRT_ENA_RW(hprt0, 0);
> + hprt0 = DWC_HPRT0_PRT_CONN_DET_RW(hprt0, 0);
> + hprt0 = DWC_HPRT0_PRT_ENA_DIS_CHG_RW(hprt0, 0);
> + hprt0 = DWC_HPRT0_PRT_OVRCURR_ACT_RW(hprt0, 0);
> + return hprt0;
> +}
> +
> +/*
> + * The following functions support managing the DWC_otg controller in
either
> + * device or host mode.
> + */
> +extern void dwc_otg_read_packet(struct core_if *core_if, u8 * dest, u16
bytes);
> +extern void dwc_otg_flush_tx_fifo(struct core_if *core_if, const int
_num);
> +extern void dwc_otg_flush_rx_fifo(struct core_if *core_if);
> +
> +#define NP_TXFIFO_EMPTY -1
> +#define MAX_NP_TXREQUEST_Q_SLOTS 8
> +
> +/**
> + * This function returns the Core Interrupt register.
> + */
> +static inline u32 dwc_otg_read_core_intr(struct core_if *core_if)
> +{
> + u32 global_regs = (u32) core_if->core_global_regs;
> + return dwc_read32(global_regs + DWC_GINTSTS) &
> + dwc_read32(global_regs + DWC_GINTMSK);
> +}
> +
> +/**
> + * This function returns the mode of the operation, host or device.
> + */
> +static inline u32 dwc_otg_mode(struct core_if *core_if)
> +{
> + u32 global_regs = (u32) core_if->core_global_regs;
> + return dwc_read32(global_regs + DWC_GINTSTS) & 0x1;
> +}
> +
> +static inline u8 dwc_otg_is_device_mode(struct core_if *core_if)
> +{
> + return dwc_otg_mode(core_if) != DWC_HOST_MODE;
> +}
> +static inline u8 dwc_otg_is_host_mode(struct core_if *core_if)
> +{
> + return dwc_otg_mode(core_if) == DWC_HOST_MODE;
> +}
> +
> +extern int dwc_otg_handle_common_intr(struct core_if *core_if);
> +
> +/*
> + * DWC_otg CIL callback structure. This structure allows the HCD and
PCD to
> + * register functions used for starting and stopping the PCD and HCD
for role
> + * change on for a DRD.
> + */
> +struct cil_callbacks {
> + /* Start function for role change */
> + int (*start) (void *_p);
> + /* Stop Function for role change */
> + int (*stop) (void *_p);
> + /* Disconnect Function for role change */
> + int (*disconnect) (void *_p);
> + /* Resume/Remote wakeup Function */
> + int (*resume_wakeup) (void *_p);
> + /* Suspend function */
> + int (*suspend) (void *_p);
> + /* Session Start (SRP) */
> + int (*session_start) (void *_p);
> + /* Pointer passed to start() and stop() */
> + void *p;
> +};
> +
> +extern void dwc_otg_cil_register_pcd_callbacks(struct core_if *core_if,
> + struct cil_callbacks *cb,
> + void *p);
> +extern void dwc_otg_cil_register_hcd_callbacks(struct core_if *core_if,
> + struct cil_callbacks *cb,
> + void *p);
> +
> +#define DWC_LIMITED_XFER 0x00000000
> +#define DWC_DEVICE_ONLY 0x00000000
> +#define DWC_HOST_ONLY 0x00000000
> +
> +#ifdef DWC_LIMITED_XFER_SIZE
> +#undef DWC_LIMITED_XFER
> +#define DWC_LIMITED_XFER 0x00000001
> +#endif
> +
> +#ifdef CONFIG_DWC_DEVICE_ONLY
> +#undef DWC_DEVICE_ONLY
> +#define DWC_DEVICE_ONLY 0x00000002
> +static inline void dwc_otg_hcd_remove(struct device *dev)
> +{
> +}
> +static inline int dwc_otg_hcd_init(struct device *_dev,
> + struct dwc_otg_device *dwc_dev)
> +{
> + return 0;
> +}
> +#else
> +extern int __init dwc_otg_hcd_init(struct device *_dev,
> + struct dwc_otg_device *dwc_dev);
> +extern void dwc_otg_hcd_remove(struct device *_dev);
> +#endif
> +
> +#ifdef CONFIG_DWC_HOST_ONLY
> +#undef DWC_HOST_ONLY
> +#define DWC_HOST_ONLY 0x00000004
> +static inline void dwc_otg_pcd_remove(struct device *dev)
> +{
> +}
> +static inline int dwc_otg_pcd_init(struct device *dev)
> +{
> + return 0;
> +}
> +#else
> +extern void dwc_otg_pcd_remove(struct device *dev);
> +extern int __init dwc_otg_pcd_init(struct device *dev);
> +#endif
> +
> +extern void dwc_otg_cil_remove(struct core_if *core_if);
> +extern struct core_if __devinit *dwc_otg_cil_init(const __iomem u32 *
base,
> + struct core_params
*params);
> +
> +static inline void dwc_set_feature(struct core_if *core_if)
> +{
> + core_if->features = DWC_LIMITED_XFER | DWC_DEVICE_ONLY |
DWC_HOST_ONLY;
> +}
> +
> +static inline int dwc_has_feature(struct core_if *core_if,
> + unsigned long feature)
> +{
> + return core_if->features & feature;
> +}
> +extern struct core_params dwc_otg_module_params;
> +extern int __devinit check_parameters(struct core_if *core_if);
> +#endif
> diff --git a/drivers/usb/otg/dwc/cil_intr.c
b/drivers/usb/otg/dwc/cil_intr.c
> new file mode 100644
> index 0000000..9111795
> --- /dev/null
> +++ b/drivers/usb/otg/dwc/cil_intr.c
> @@ -0,0 +1,616 @@
> +/*
> + * DesignWare HS OTG controller driver
> + * Copyright (C) 2006 Synopsys, Inc.
> + * Portions Copyright (C) 2010 Applied Micro Circuits Corporation.
> + *
> + * This program is free software: you can redistribute it and/or
> + * modify it under the terms of the GNU General Public License
> + * version 2 as published by the Free Software Foundation.
> + *
> + * This program is distributed in the hope that it will be useful
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
> + * GNU General Public License version 2 for more details.
> + *
> + * You should have received a copy of the GNU General Public License
> + * along with this program; if not, see http://www.gnu.org/licenses
> + * or write to the Free Software Foundation, Inc., 51 Franklin Street,
> + * Suite 500, Boston, MA 02110-1335 USA.
> + *
> + * Based on Synopsys driver version 2.60a
> + * Modified by Mark Miesfeld <mmiesfeld@apm.com>
> + * Modified by Stefan Roese <sr@denx.de>, DENX Software Engineering
> + *
> + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS"
> + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING BUT NOT LIMITED TO
THE
> + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
PURPOSE
> + * ARE DISCLAIMED. IN NO EVENT SHALL SYNOPSYS, INC. BE LIABLE FOR ANY
DIRECT,
> + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES
> + * (INCLUDING BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES;
> + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND
> + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY OR
TORT
> + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
USE OF
> + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
> + *
> + */
> +
> +/*
> + * The Core Interface Layer provides basic services for accessing and
> + * managing the DWC_otg hardware. These services are used by both the
> + * Host Controller Driver and the Peripheral Controller Driver.
> + *
> + * This file contains the Common Interrupt handlers.
> + */
> +#include <linux/delay.h>
> +
> +#include "cil.h"
> +
> +/**
> + * This function will log a debug message
> + */
> +static int dwc_otg_handle_mode_mismatch_intr(struct core_if *core_if)
> +{
> + u32 gintsts = 0;
> + ulong global_regs = core_if->core_global_regs;
> +
> + pr_warning("Mode Mismatch Interrupt: currently in %s mode\n",
> + dwc_otg_mode(core_if) ? "Host" : "Device");
> +
> + /* Clear interrupt */
> + gintsts |= DWC_INTSTS_MODE_MISMTC;
> + dwc_write32(global_regs + DWC_GINTSTS, gintsts);
> +
> + return 1;
> +}
> +
> +/**
> + * Start the HCD. Helper function for using the HCD callbacks.
> + */
> +static inline void hcd_start(struct core_if *core_if)
> +{
> + if (core_if->hcd_cb && core_if->hcd_cb->start)
> + core_if->hcd_cb->start(core_if->hcd_cb->p);
> +}
> +
> +/**
> + * Stop the HCD. Helper function for using the HCD callbacks.
> + */
> +static inline void hcd_stop(struct core_if *core_if)
> +{
> + if (core_if->hcd_cb && core_if->hcd_cb->stop)
> + core_if->hcd_cb->stop(core_if->hcd_cb->p);
> +}
> +
> +/**
> + * Disconnect the HCD. Helper function for using the HCD callbacks.
> + */
> +static inline void hcd_disconnect(struct core_if *core_if)
> +{
> + if (core_if->hcd_cb && core_if->hcd_cb->disconnect)
> + core_if->hcd_cb->disconnect(core_if->hcd_cb->p);
> +}
> +
> +/**
> + * Inform the HCD the a New Session has begun. Helper function for
using the
> + * HCD callbacks.
> + */
> +static inline void hcd_session_start(struct core_if *core_if)
> +{
> + if (core_if->hcd_cb && core_if->hcd_cb->session_start)
> + core_if->hcd_cb->session_start(core_if->hcd_cb->p);
> +}
> +
> +/**
> + * Start the PCD. Helper function for using the PCD callbacks.
> + */
> +static inline void pcd_start(struct core_if *core_if)
> +{
> + if (core_if->pcd_cb && core_if->pcd_cb->start) {
> + struct dwc_pcd *pcd;
> +
> + pcd = (struct dwc_pcd *)core_if->pcd_cb->p;
> + spin_lock(&pcd->lock);
> + core_if->pcd_cb->start(core_if->pcd_cb->p);
> + spin_unlock(&pcd->lock);
> + }
> +}
> +
> +/**
> + * Stop the PCD. Helper function for using the PCD callbacks.
> + */
> +static inline void pcd_stop(struct core_if *core_if)
> +{
> + if (core_if->pcd_cb && core_if->pcd_cb->stop) {
> + struct dwc_pcd *pcd;
> +
> + pcd = (struct dwc_pcd *)core_if->pcd_cb->p;
> + spin_lock(&pcd->lock);
> + core_if->pcd_cb->stop(core_if->pcd_cb->p);
> + spin_unlock(&pcd->lock);
> + }
> +}
> +
> +/**
> + * Suspend the PCD. Helper function for using the PCD callbacks.
> + */
> +static inline void pcd_suspend(struct core_if *core_if)
> +{
> + if (core_if->pcd_cb && core_if->pcd_cb->suspend) {
> + struct dwc_pcd *pcd;
> +
> + pcd = (struct dwc_pcd *)core_if->pcd_cb->p;
> + spin_lock(&pcd->lock);
> + core_if->pcd_cb->suspend(core_if->pcd_cb->p);
> + spin_unlock(&pcd->lock);
> + }
> +}
> +
> +/**
> + * Resume the PCD. Helper function for using the PCD callbacks.
> + */
> +static inline void pcd_resume(struct core_if *core_if)
> +{
> + if (core_if->pcd_cb && core_if->pcd_cb->resume_wakeup) {
> + struct dwc_pcd *pcd;
> +
> + pcd = (struct dwc_pcd *)core_if->pcd_cb->p;
> + spin_lock(&pcd->lock);
> + core_if->pcd_cb->resume_wakeup(core_if->pcd_cb->p);
> + spin_unlock(&pcd->lock);
> + }
> +}
> +
> +/**
> + * This function handles the OTG Interrupts. It reads the OTG
> + * Interrupt Register (GOTGINT) to determine what interrupt has
> + * occurred.
> + */
> +static int dwc_otg_handle_otg_intr(struct core_if *core_if)
> +{
> + ulong global_regs = core_if->core_global_regs;
> + u32 gotgint;
> + u32 gotgctl;
> + u32 gintmsk;
> +
> + gotgint = dwc_read32(global_regs + DWC_GOTGINT);
> + if (gotgint & DWC_GINT_SES_ENDDET) {
> + gotgctl = dwc_read32(global_regs + DWC_GOTGCTL);
> + if (core_if->xceiv->state == OTG_STATE_B_HOST) {
> + pcd_start(core_if);
> + core_if->xceiv->state = OTG_STATE_B_PERIPHERAL;
> + } else {
> + /*
> + * If not B_HOST and Device HNP still set. HNP did
not
> + * succeed
> + */
> + if (gotgctl & DWC_GCTL_DEV_HNP_ENA)
> + pr_err("Device Not Connected / "
> + "Responding\n");
> + /*
> + * If Session End Detected the B-Cable has been
> + * disconnected. Reset PCD and Gadget driver to a
> + * clean state.
> + */
> + pcd_stop(core_if);
> + }
> + gotgctl = 0;
> + gotgctl |= DWC_GCTL_DEV_HNP_ENA;
> + dwc_modify32(global_regs + DWC_GOTGCTL, gotgctl, 0);
> + }
> + if (gotgint & DWC_GINT_SES_REQSUC) {
> + gotgctl = dwc_read32(global_regs + DWC_GOTGCTL);
> + if (gotgctl & DWC_GCTL_SES_REQ_SUCCESS) {
> + if (core_if->core_params->phy_type ==
> + DWC_PHY_TYPE_PARAM_FS &&
> + core_if->core_params->i2c_enable) {
> + core_if->srp_success = 1;
> + } else {
> + pcd_resume(core_if);
> +
> + /* Clear Session Request */
> + gotgctl = 0;
> + gotgctl |= DWC_GCTL_SES_REQ;
> + dwc_modify32(global_regs + DWC_GOTGCTL,
> + gotgctl, 0);
> + }
> + }
> + }
> + if (gotgint & DWC_GINT_HST_NEGSUC) {
> + /*
> + * Print statements during the HNP interrupt handling can
cause
> + * it to fail.
> + */
> + gotgctl = dwc_read32(global_regs + DWC_GOTGCTL);
> + if (gotgctl & DWC_GCTL_HOST_NEG_SUCCES) {
> + if (dwc_otg_is_host_mode(core_if)) {
> + core_if->xceiv->state = OTG_STATE_B_HOST;
> + /*
> + * Need to disable SOF interrupt
immediately.
> + * When switching from device to host, the
PCD
> + * interrupt handler won't handle the
> + * interrupt if host mode is already set.
The
> + * HCD interrupt handler won't get called
if
> + * the HCD state is HALT. This means that
the
> + * interrupt does not get handled and
Linux
> + * complains loudly.
> + */
> + gintmsk = 0;
> + gintmsk |= DWC_INTMSK_STRT_OF_FRM;
> + dwc_modify32(global_regs + DWC_GINTMSK,
> + gintmsk, 0);
> + pcd_stop(core_if);
> + /* Initialize the Core for Host mode. */
> + hcd_start(core_if);
> + core_if->xceiv->state = OTG_STATE_B_HOST;
> + }
> + } else {
> + gotgctl = 0;
> + gotgctl |= DWC_GCTL_HNP_REQ;
> + gotgctl |= DWC_GCTL_DEV_HNP_ENA;
> + dwc_modify32(global_regs + DWC_GOTGCTL, gotgctl,
0);
> +
> + pr_err("Device Not Connected / Responding\n");
> + }
> + }
> + if (gotgint & DWC_GINT_HST_NEGDET) {
> + /*
> + * The disconnect interrupt is set at the same time as
> + * Host Negotiation Detected. During the mode
> + * switch all interrupts are cleared so the disconnect
> + * interrupt handler will not get executed.
> + */
> + if (dwc_otg_is_device_mode(core_if)) {
> + hcd_disconnect(core_if);
> + pcd_start(core_if);
> + core_if->xceiv->state = OTG_STATE_A_PERIPHERAL;
> + } else {
> + /*
> + * Need to disable SOF interrupt immediately. When
> + * switching from device to host, the PCD
interrupt
> + * handler won't handle the interrupt if host mode
is
> + * already set. The HCD interrupt handler won't
get
> + * called if the HCD state is HALT. This means
that
> + * the interrupt does not get handled and Linux
> + * complains loudly.
> + */
> + gintmsk = 0;
> + gintmsk |= DWC_INTMSK_STRT_OF_FRM;
> + dwc_modify32(global_regs + DWC_GINTMSK, gintmsk,
0);
> + pcd_stop(core_if);
> + hcd_start(core_if);
> + core_if->xceiv->state = OTG_STATE_A_HOST;
> + }
> + }
> + if (gotgint & DWC_GINT_DEVTOUT)
> + pr_info(" ++OTG Interrupt: A-Device Timeout "
"Change++\n");
> + if (gotgint & DWC_GINT_DEBDONE)
> + pr_info(" ++OTG Interrupt: Debounce Done++\n");
> +
> + /* Clear GOTGINT */
> + dwc_write32(global_regs + DWC_GOTGINT, gotgint);
> + return 1;
> +}
> +
> +/*
> + * Wakeup Workqueue implementation
> + */
> +static void port_otg_wqfunc(struct work_struct *work)
> +{
> + struct core_if *core_if = container_of(work, struct core_if,
> + usb_port_otg);
> + ulong global_regs = core_if->core_global_regs;
> + u32 count = 0;
> + u32 gotgctl;
> +
> + pr_info("%s\n", __func__);
> +
> + gotgctl = dwc_read32(global_regs + DWC_GOTGCTL);
> + if (gotgctl & DWC_GCTL_CONN_ID_STATUS) {
> + /*
> + * B-Device connector (device mode) wait for switch to
device
> + * mode.
> + */
> + while (!dwc_otg_is_device_mode(core_if) && ++count <=
10000) {
> + pr_info("Waiting for Peripheral Mode, "
> + "Mode=%s\n", dwc_otg_is_host_mode(core_if)
?
> + "Host" : "Peripheral");
> + msleep(100);
> + }
> + BUG_ON(count > 10000);
> + core_if->xceiv->state = OTG_STATE_B_PERIPHERAL;
> + dwc_otg_core_init(core_if);
> + dwc_otg_enable_global_interrupts(core_if);
> + pcd_start(core_if);
> + } else {
> + /*
> + * A-Device connector (host mode) wait for switch to host
> + * mode.
> + */
> + while (!dwc_otg_is_host_mode(core_if) && ++count <= 10000)
{
> + pr_info("Waiting for Host Mode, Mode=%s\n",
> + dwc_otg_is_host_mode(core_if) ?
> + "Host" : "Peripheral");
> + msleep(100);
> + }
> + BUG_ON(count > 10000);
> + core_if->xceiv->state = OTG_STATE_A_HOST;
> + dwc_otg_core_init(core_if);
> + dwc_otg_enable_global_interrupts(core_if);
> + hcd_start(core_if);
> + }
> +}
> +
> +/**
> + * This function handles the Connector ID Status Change Interrupt. It
> + * reads the OTG Interrupt Register (GOTCTL) to determine whether this
> + * is a Device to Host Mode transition or a Host Mode to Device
> + * Transition.
> + *
> + * This only occurs when the cable is connected/removed from the PHY
> + * connector.
> + */
> +static int dwc_otg_handle_conn_id_status_change_intr(struct core_if
*core_if)
> +{
> + u32 gintsts = 0;
> + u32 gintmsk = 0;
> + ulong global_regs = core_if->core_global_regs;
> +
> + /*
> + * Need to disable SOF interrupt immediately. If switching from
device
> + * to host, the PCD interrupt handler won't handle the interrupt
if
> + * host mode is already set. The HCD interrupt handler won't get
> + * called if the HCD state is HALT. This means that the interrupt
does
> + * not get handled and Linux complains loudly.
> + */
> + gintmsk |= DWC_INTSTS_STRT_OF_FRM;
> + dwc_modify32(global_regs + DWC_GINTMSK, gintmsk, 0);
> +
> + INIT_WORK(&core_if->usb_port_otg, port_otg_wqfunc);
> + schedule_work(&core_if->usb_port_otg);
> +
> + /* Set flag and clear interrupt */
> + gintsts |= DWC_INTSTS_CON_ID_STS_CHG;
> + dwc_write32(global_regs + DWC_GINTSTS, gintsts);
> + return 1;
> +}
> +
> +/**
> + * This interrupt indicates that a device is initiating the Session
> + * Request Protocol to request the host to turn on bus power so a new
> + * session can begin. The handler responds by turning on bus power. If
> + * the DWC_otg controller is in low power mode, the handler brings the
> + * controller out of low power mode before turning on bus power.
> + */
> +static int dwc_otg_handle_session_req_intr(struct core_if *core_if)
> +{
> + u32 gintsts = 0;
> + ulong global_regs = core_if->core_global_regs;
> +
> + if (!dwc_has_feature(core_if, DWC_HOST_ONLY)) {
> + u32 hprt0;
> +
> + if (dwc_otg_is_device_mode(core_if)) {
> + pr_info("SRP: Device mode\n");
> + } else {
> + pr_info("SRP: Host mode\n");
> +
> + /* Turn on the port power bit. */
> + hprt0 = dwc_otg_read_hprt0(core_if);
> + hprt0 = DWC_HPRT0_PRT_PWR_RW(hprt0, 1);
> + dwc_write32(core_if->host_if->hprt0, hprt0);
> +
> + /*
> + * Start the Connection timer.
> + * A message can be displayed,
> + * if connect does not occur within 10 seconds.
> + */
> + hcd_session_start(core_if);
> + }
> + }
> + /* Clear interrupt */
> + gintsts |= DWC_INTSTS_NEW_SES_DET;
> + dwc_write32(global_regs + DWC_GINTSTS, gintsts);
> + return 1;
> +}
> +
> +/**
> + * This interrupt indicates that the DWC_otg controller has detected a
> + * resume or remote wakeup sequence. If the DWC_otg controller is in
> + * low power mode, the handler must brings the controller out of low
> + * power mode. The controller automatically begins resume
> + * signaling. The handler schedules a time to stop resume signaling.
> + */
> +static int dwc_otg_handle_wakeup_detected_intr(struct core_if *core_if)
> +{
> + u32 gintsts = 0;
> + struct device_if *dev_if = core_if->dev_if;
> + ulong global_regs = core_if->core_global_regs;
Tabify the declarations ;
> +
> + if (dwc_otg_is_device_mode(core_if)) {
> + u32 dctl = 0;
> +
> + /* Clear the Remote Wakeup Signalling */
> + dctl = DEC_DCTL_REMOTE_WAKEUP_SIG(dctl, 1);
> + dwc_modify32(dev_if->dev_global_regs + DWC_DCTL, dctl, 0);
> +
> + if (core_if->pcd_cb && core_if->pcd_cb->resume_wakeup)
> +
core_if->pcd_cb->resume_wakeup(core_if->pcd_cb->p);
> + } else {
> + u32 pcgcctl = 0;
> +
> + /* Restart the Phy Clock */
> + pcgcctl = DWC_PCGCCTL_STOP_CLK_SET(pcgcctl);
> + dwc_modify32(core_if->pcgcctl, pcgcctl, 0);
> + schedule_delayed_work(&core_if->usb_port_wakeup, 10);
> + }
> +
> + /* Clear interrupt */
> + gintsts |= DWC_INTSTS_WKP;
> + dwc_write32(global_regs + DWC_GINTSTS, gintsts);
> + return 1;
> +}
> +
> +/**
> + * This interrupt indicates that a device has been disconnected from
> + * the root port.
> + */
> +static int dwc_otg_handle_disconnect_intr(struct core_if *core_if)
> +{
> + u32 gintsts = 0;
> + ulong global_regs = core_if->core_global_regs;
> +
> + if (!dwc_has_feature(core_if, DWC_HOST_ONLY)) {
> + if (core_if->xceiv->state == OTG_STATE_B_HOST) {
> + hcd_disconnect(core_if);
> + pcd_start(core_if);
> + core_if->xceiv->state = OTG_STATE_B_PERIPHERAL;
> + } else if (dwc_otg_is_device_mode(core_if)) {
> + u32 gotgctl;
> +
> + gotgctl = dwc_read32(global_regs + DWC_GOTGCTL);
> +
> + /*
> + * If HNP is in process, do nothing.
> + * The OTG "Host Negotiation Detected"
> + * interrupt will do the mode switch.
> + * Otherwise, since we are in device mode,
> + * disconnect and stop the HCD,
> + * then start the PCD.
> + */
> + if ((gotgctl) & DWC_GCTL_DEV_HNP_ENA) {
> + hcd_disconnect(core_if);
> + pcd_start(core_if);
> + core_if->xceiv->state =
OTG_STATE_B_PERIPHERAL;
> + }
> + } else if (core_if->xceiv->state == OTG_STATE_A_HOST) {
> + /* A-Cable still connected but device
disconnected. */
> + hcd_disconnect(core_if);
> + }
> + }
> + gintsts |= DWC_INTSTS_SES_DISCON_DET;
> + dwc_write32(global_regs + DWC_GINTSTS, gintsts);
> + return 1;
> +}
> +
> +/**
> + * This interrupt indicates that SUSPEND state has been detected on
> + * the USB.
> + *
> + * For HNP the USB Suspend interrupt signals the change from
> + * "a_peripheral" to "a_host".
> + *
> + * When power management is enabled the core will be put in low power
> + * mode.
> + */
> +static int dwc_otg_handle_usb_suspend_intr(struct core_if *core_if)
> +{
> + u32 dsts = 0;
> + u32 gintsts = 0;
> + ulong global_regs = core_if->core_global_regs;
> + struct device_if *dev_if = core_if->dev_if;
Tabify the declarations ;
> +
> + if (dwc_otg_is_device_mode(core_if)) {
> + /*
> + * Check the Device status register to determine if the
Suspend
> + * state is active.
> + */
> + dsts = dwc_read32(dev_if->dev_global_regs + DWC_DSTS);
> + /* PCD callback for suspend. */
> + pcd_suspend(core_if);
> + } else {
> + if (core_if->xceiv->state == OTG_STATE_A_PERIPHERAL) {
> + /* Clear the a_peripheral flag, back to a_host. */
> + pcd_stop(core_if);
> + hcd_start(core_if);
> + core_if->xceiv->state = OTG_STATE_A_HOST;
> + }
> + }
> +
> + /* Clear interrupt */
> + gintsts |= DWC_INTMSK_USB_SUSP;
> + dwc_write32(global_regs + DWC_GINTSTS, gintsts);
> + return 1;
> +}
> +
> +/**
> + * This function returns the Core Interrupt register.
> + *
> + * Although the Host Port interrupt (portintr) is documented as host
mode
> + * only, it appears to occur in device mode when Port Enable / Disable
Changed
> + * bit in HPRT0 is set. The code in dwc_otg_handle_common_intr checks
if in
> + * device mode and just clears the interrupt.
> + */
> +static inline u32 dwc_otg_read_common_intr(struct core_if *core_if)
> +{
> + u32 gintsts;
> + u32 gintmsk;
> + u32 gintmsk_common = 0;
> + ulong global_regs = core_if->core_global_regs;
> +
> + gintmsk_common |= DWC_INTMSK_WKP;
> + gintmsk_common |= DWC_INTMSK_NEW_SES_DET;
> + gintmsk_common |= DWC_INTMSK_CON_ID_STS_CHG;
> + gintmsk_common |= DWC_INTMSK_OTG;
> + gintmsk_common |= DWC_INTMSK_MODE_MISMTC;
> + gintmsk_common |= DWC_INTMSK_SES_DISCON_DET;
> + gintmsk_common |= DWC_INTMSK_USB_SUSP;
> + gintmsk_common |= DWC_INTMSK_HST_PORT;
> +
> + gintsts = dwc_read32(global_regs + DWC_GINTSTS);
> + gintmsk = dwc_read32(global_regs + DWC_GINTMSK);
> +
> + return (gintsts & gintmsk) & gintmsk_common;
> +}
> +
> +/**
> + * Common interrupt handler.
> + *
> + * The common interrupts are those that occur in both Host and Device
mode.
> + * This handler handles the following interrupts:
> + * - Mode Mismatch Interrupt
> + * - Disconnect Interrupt
> + * - OTG Interrupt
> + * - Connector ID Status Change Interrupt
> + * - Session Request Interrupt.
> + * - Resume / Remote Wakeup Detected Interrupt.
> + *
> + * - Host Port Interrupt. Although this interrupt is documented as
only
> + * occurring in Host mode, it also occurs in Device mode when Port
Enable /
> + * Disable Changed bit in HPRT0 is set. If it is seen here, while in
Device
> + * mode, the interrupt is just cleared.
> + *
> + */
> +int dwc_otg_handle_common_intr(struct core_if *core_if)
> +{
> + int retval = 0;
> + u32 gintsts;
> + ulong global_regs = core_if->core_global_regs;
> +
> + gintsts = dwc_otg_read_common_intr(core_if);
> +
> + if (gintsts & DWC_INTSTS_MODE_MISMTC)
> + retval |= dwc_otg_handle_mode_mismatch_intr(core_if);
> + if (gintsts & DWC_INTSTS_OTG)
> + retval |= dwc_otg_handle_otg_intr(core_if);
> + if (gintsts & DWC_INTSTS_CON_ID_STS_CHG)
> + retval |=
dwc_otg_handle_conn_id_status_change_intr(core_if);
> + if (gintsts & DWC_INTSTS_SES_DISCON_DET)
> + retval |= dwc_otg_handle_disconnect_intr(core_if);
> + if (gintsts & DWC_INTSTS_NEW_SES_DET)
> + retval |= dwc_otg_handle_session_req_intr(core_if);
> + if (gintsts & DWC_INTSTS_WKP)
> + retval |= dwc_otg_handle_wakeup_detected_intr(core_if);
> + if (gintsts & DWC_INTMSK_USB_SUSP)
> + retval |= dwc_otg_handle_usb_suspend_intr(core_if);
> +
> + if ((gintsts & DWC_INTSTS_HST_PORT) &&
> + dwc_otg_is_device_mode(core_if)) {
> + gintsts = 0;
> + gintsts |= DWC_INTSTS_HST_PORT;
> + dwc_write32(global_regs + DWC_GINTSTS, gintsts);
> + retval |= 1;
> + pr_info("RECEIVED PORTINT while in Device mode\n");
> + }
> +
> + return retval;
> +}
> --
> 1.6.1.rc3
>
> --
> 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 http://vger.kernel.org/majordomo-info.html
^ permalink raw reply
* RE: [PATCH 2/4] KVM: PPC: booke: Wrap __kvmppc_vcpu_run()
From: Liu Yu-B13201 @ 2011-03-29 9:36 UTC (permalink / raw)
To: Alexander Graf, Wood Scott-B07421; +Cc: linuxppc-dev@lists.ozlabs.org
In-Reply-To: <3A85D702-7EA1-48A2-B285-D4507CC48DBC@suse.de>
=20
> -----Original Message-----
> From:=20
> linuxppc-dev-bounces+b13201=3Dfreescale.com@lists.ozlabs.org=20
> [mailto:linuxppc-dev-bounces+b13201=3Dfreescale.com@lists.ozlabs
> .org] On Behalf Of Alexander Graf
> Sent: Tuesday, March 29, 2011 5:32 PM
> To: Wood Scott-B07421
> Cc: linuxppc-dev@lists.ozlabs.org
> Subject: Re: [PATCH 2/4] KVM: PPC: booke: Wrap __kvmppc_vcpu_run()
>=20
>=20
> On 28.03.2011, at 21:25, Scott Wood wrote:
>=20
> > From: yu liu <yu.liu@freescale.com>
> >=20
> > We need to save/restore SPE environment on e500 core.
> > Wrap __kvmppc_vcpu_run() so that we can put the SPE code in
> > e500.c.
> >=20
> > Signed-off-by: Liu Yu <yu.liu@freescale.com>
> > Signed-off-by: Scott Wood <scottwood@freescale.com>
> > ---
> > arch/powerpc/include/asm/kvm_ppc.h | 1 +
> > arch/powerpc/kvm/44x.c | 5 +++++
> > arch/powerpc/kvm/book3s.c | 1 -
> > arch/powerpc/kvm/booke_interrupts.S | 2 +-
> > arch/powerpc/kvm/e500.c | 5 +++++
> > 5 files changed, 12 insertions(+), 2 deletions(-)
> >=20
> > diff --git a/arch/powerpc/include/asm/kvm_ppc.h=20
> b/arch/powerpc/include/asm/kvm_ppc.h
> > index ecb3bc7..4e7a1be 100644
> > --- a/arch/powerpc/include/asm/kvm_ppc.h
> > +++ b/arch/powerpc/include/asm/kvm_ppc.h
> > @@ -42,6 +42,7 @@ enum emulation_result {
> > EMULATE_AGAIN, /* something went wrong. go again */
> > };
> >=20
> > +extern int __kvmppc_vcpu_entry(struct kvm_run *kvm_run,=20
> struct kvm_vcpu *vcpu);
> > extern int __kvmppc_vcpu_run(struct kvm_run *kvm_run,=20
> struct kvm_vcpu *vcpu);
> > extern char kvmppc_handlers_start[];
> > extern unsigned long kvmppc_handler_len;
> > diff --git a/arch/powerpc/kvm/44x.c b/arch/powerpc/kvm/44x.c
> > index 74d0e74..3d2e7d2 100644
> > --- a/arch/powerpc/kvm/44x.c
> > +++ b/arch/powerpc/kvm/44x.c
> > @@ -147,6 +147,11 @@ void kvmppc_core_vcpu_free(struct=20
> kvm_vcpu *vcpu)
> > kmem_cache_free(kvm_vcpu_cache, vcpu_44x);
> > }
> >=20
> > +int __kvmppc_vcpu_run(struct kvm_run *kvm_run, struct=20
> kvm_vcpu *vcpu)
> > +{
> > + return __kvmppc_vcpu_entry(kvm_run, vcpu);
>=20
> 440 calls _entry on _run
>=20
> > +}
> > +
> > static int __init kvmppc_44x_init(void)
> > {
> > int r;
> > diff --git a/arch/powerpc/kvm/book3s.c b/arch/powerpc/kvm/book3s.c
> > index c961de4..fb12853 100644
> > --- a/arch/powerpc/kvm/book3s.c
> > +++ b/arch/powerpc/kvm/book3s.c
> > @@ -1379,7 +1379,6 @@ void kvmppc_core_vcpu_free(struct=20
> kvm_vcpu *vcpu)
> > vfree(vcpu_book3s);
> > }
> >=20
> > -extern int __kvmppc_vcpu_entry(struct kvm_run *kvm_run,=20
> struct kvm_vcpu *vcpu);
>=20
> BookS loses its _entry reference?
>=20
> > int __kvmppc_vcpu_run(struct kvm_run *kvm_run, struct=20
> kvm_vcpu *vcpu)
> > {
> > int ret;
> > diff --git a/arch/powerpc/kvm/booke_interrupts.S=20
> b/arch/powerpc/kvm/booke_interrupts.S
> > index 1cc471f..ab29f5f 100644
> > --- a/arch/powerpc/kvm/booke_interrupts.S
> > +++ b/arch/powerpc/kvm/booke_interrupts.S
> > @@ -293,7 +293,7 @@ heavyweight_exit:
> > * r3: kvm_run pointer
> > * r4: vcpu pointer
> > */
> > -_GLOBAL(__kvmppc_vcpu_run)
> > +_GLOBAL(__kvmppc_vcpu_entry)
>=20
> BookE calls _run _entry now
>=20
> > stwu r1, -HOST_STACK_SIZE(r1)
> > stw r1, VCPU_HOST_STACK(r4) /* Save stack pointer=20
> to vcpu. */
> >=20
> > diff --git a/arch/powerpc/kvm/e500.c b/arch/powerpc/kvm/e500.c
> > index e3768ee..e762634 100644
> > --- a/arch/powerpc/kvm/e500.c
> > +++ b/arch/powerpc/kvm/e500.c
> > @@ -70,6 +70,11 @@ int kvmppc_core_vcpu_setup(struct kvm_vcpu *vcpu)
> > return 0;
> > }
> >=20
> > +int __kvmppc_vcpu_run(struct kvm_run *kvm_run, struct=20
> kvm_vcpu *vcpu)
> > +{
> > + return __kvmppc_vcpu_entry(kvm_run, vcpu);
> > +}
>=20
> e500 calls _entry on _run
>=20
>=20
> So you're basically adding a C wrapper around _entry. That's=20
> fine for me, but what is the BookS change about?
>=20
Hi Alex,
The declaration for Book3S is moved to global header.
Thanks,
Yu
^ permalink raw reply
* Re: [PATCH 2/4] KVM: PPC: booke: Wrap __kvmppc_vcpu_run()
From: Alexander Graf @ 2011-03-29 9:48 UTC (permalink / raw)
To: Liu Yu-B13201; +Cc: Wood Scott-B07421, linuxppc-dev@lists.ozlabs.org
In-Reply-To: <FBDE6FBB4662C043AC9EECB95F62CDDE185985@039-SN1MPN1-004.039d.mgd.msft.net>
On 29.03.2011, at 11:36, Liu Yu-B13201 wrote:
>
>
>> -----Original Message-----
>> From:
>> linuxppc-dev-bounces+b13201=freescale.com@lists.ozlabs.org
>> [mailto:linuxppc-dev-bounces+b13201=freescale.com@lists.ozlabs
>> .org] On Behalf Of Alexander Graf
>> Sent: Tuesday, March 29, 2011 5:32 PM
>> To: Wood Scott-B07421
>> Cc: linuxppc-dev@lists.ozlabs.org
>> Subject: Re: [PATCH 2/4] KVM: PPC: booke: Wrap __kvmppc_vcpu_run()
>>
>>
>> On 28.03.2011, at 21:25, Scott Wood wrote:
>>
>>> From: yu liu <yu.liu@freescale.com>
>>>
>>> We need to save/restore SPE environment on e500 core.
>>> Wrap __kvmppc_vcpu_run() so that we can put the SPE code in
>>> e500.c.
>>>
>>> Signed-off-by: Liu Yu <yu.liu@freescale.com>
>>> Signed-off-by: Scott Wood <scottwood@freescale.com>
>>> ---
>>> arch/powerpc/include/asm/kvm_ppc.h | 1 +
>>> arch/powerpc/kvm/44x.c | 5 +++++
>>> arch/powerpc/kvm/book3s.c | 1 -
>>> arch/powerpc/kvm/booke_interrupts.S | 2 +-
>>> arch/powerpc/kvm/e500.c | 5 +++++
>>> 5 files changed, 12 insertions(+), 2 deletions(-)
>>>
>>> diff --git a/arch/powerpc/include/asm/kvm_ppc.h
>> b/arch/powerpc/include/asm/kvm_ppc.h
>>> index ecb3bc7..4e7a1be 100644
>>> --- a/arch/powerpc/include/asm/kvm_ppc.h
>>> +++ b/arch/powerpc/include/asm/kvm_ppc.h
>>> @@ -42,6 +42,7 @@ enum emulation_result {
>>> EMULATE_AGAIN, /* something went wrong. go again */
>>> };
>>>
>>> +extern int __kvmppc_vcpu_entry(struct kvm_run *kvm_run,
>> struct kvm_vcpu *vcpu);
>>> extern int __kvmppc_vcpu_run(struct kvm_run *kvm_run,
>> struct kvm_vcpu *vcpu);
>>> extern char kvmppc_handlers_start[];
>>> extern unsigned long kvmppc_handler_len;
>>> diff --git a/arch/powerpc/kvm/44x.c b/arch/powerpc/kvm/44x.c
>>> index 74d0e74..3d2e7d2 100644
>>> --- a/arch/powerpc/kvm/44x.c
>>> +++ b/arch/powerpc/kvm/44x.c
>>> @@ -147,6 +147,11 @@ void kvmppc_core_vcpu_free(struct
>> kvm_vcpu *vcpu)
>>> kmem_cache_free(kvm_vcpu_cache, vcpu_44x);
>>> }
>>>
>>> +int __kvmppc_vcpu_run(struct kvm_run *kvm_run, struct
>> kvm_vcpu *vcpu)
>>> +{
>>> + return __kvmppc_vcpu_entry(kvm_run, vcpu);
>>
>> 440 calls _entry on _run
>>
>>> +}
>>> +
>>> static int __init kvmppc_44x_init(void)
>>> {
>>> int r;
>>> diff --git a/arch/powerpc/kvm/book3s.c b/arch/powerpc/kvm/book3s.c
>>> index c961de4..fb12853 100644
>>> --- a/arch/powerpc/kvm/book3s.c
>>> +++ b/arch/powerpc/kvm/book3s.c
>>> @@ -1379,7 +1379,6 @@ void kvmppc_core_vcpu_free(struct
>> kvm_vcpu *vcpu)
>>> vfree(vcpu_book3s);
>>> }
>>>
>>> -extern int __kvmppc_vcpu_entry(struct kvm_run *kvm_run,
>> struct kvm_vcpu *vcpu);
>>
>> BookS loses its _entry reference?
>>
>>> int __kvmppc_vcpu_run(struct kvm_run *kvm_run, struct
>> kvm_vcpu *vcpu)
>>> {
>>> int ret;
>>> diff --git a/arch/powerpc/kvm/booke_interrupts.S
>> b/arch/powerpc/kvm/booke_interrupts.S
>>> index 1cc471f..ab29f5f 100644
>>> --- a/arch/powerpc/kvm/booke_interrupts.S
>>> +++ b/arch/powerpc/kvm/booke_interrupts.S
>>> @@ -293,7 +293,7 @@ heavyweight_exit:
>>> * r3: kvm_run pointer
>>> * r4: vcpu pointer
>>> */
>>> -_GLOBAL(__kvmppc_vcpu_run)
>>> +_GLOBAL(__kvmppc_vcpu_entry)
>>
>> BookE calls _run _entry now
>>
>>> stwu r1, -HOST_STACK_SIZE(r1)
>>> stw r1, VCPU_HOST_STACK(r4) /* Save stack pointer
>> to vcpu. */
>>>
>>> diff --git a/arch/powerpc/kvm/e500.c b/arch/powerpc/kvm/e500.c
>>> index e3768ee..e762634 100644
>>> --- a/arch/powerpc/kvm/e500.c
>>> +++ b/arch/powerpc/kvm/e500.c
>>> @@ -70,6 +70,11 @@ int kvmppc_core_vcpu_setup(struct kvm_vcpu *vcpu)
>>> return 0;
>>> }
>>>
>>> +int __kvmppc_vcpu_run(struct kvm_run *kvm_run, struct
>> kvm_vcpu *vcpu)
>>> +{
>>> + return __kvmppc_vcpu_entry(kvm_run, vcpu);
>>> +}
>>
>> e500 calls _entry on _run
>>
>>
>> So you're basically adding a C wrapper around _entry. That's
>> fine for me, but what is the BookS change about?
>>
>
> Hi Alex,
>
> The declaration for Book3S is moved to global header.
Ah, I missed the first chunk :). Thanks!
Alex
^ permalink raw reply
* Re: [PATCH 4/4] KVM: PPC: e500: SPE switch between guest and host
From: Alexander Graf @ 2011-03-29 9:47 UTC (permalink / raw)
To: Scott Wood; +Cc: linuxppc-dev
In-Reply-To: <20110328192559.GC11104@schlenkerla.am.freescale.net>
On 28.03.2011, at 21:25, Scott Wood wrote:
> From: yu liu <yu.liu@freescale.com>
>=20
> This patch provide a lazy way to do SPE switch.
> The SPE save/restore will be done only if it's needed.
>=20
> Linux already switches SPEFSCR on context switch (non-lazily), so the
> only remaining bit is to save it between qemu and the guest.
>=20
> Signed-off-by: Liu Yu <yu.liu@freescale.com>
> Signed-off-by: Scott Wood <scottwood@freescale.com>
> ---
> arch/powerpc/include/asm/kvm_host.h | 6 +++
> arch/powerpc/kernel/asm-offsets.c | 6 +++
> arch/powerpc/kvm/booke.c | 15 +++++++-
> arch/powerpc/kvm/booke_interrupts.S | 62 =
+++++++++++++++++++++++++++++++++++
> arch/powerpc/kvm/e500.c | 45 ++++++++++++++++++++++++-
> 5 files changed, 130 insertions(+), 4 deletions(-)
>=20
> diff --git a/arch/powerpc/include/asm/kvm_host.h =
b/arch/powerpc/include/asm/kvm_host.h
> index c376f6b..171cd85 100644
> --- a/arch/powerpc/include/asm/kvm_host.h
> +++ b/arch/powerpc/include/asm/kvm_host.h
> @@ -195,6 +195,12 @@ struct kvm_vcpu_arch {
> u64 fpr[32];
> u64 fpscr;
>=20
> +#ifdef CONFIG_SPE
> + ulong evr[32];
> + ulong spefscr;
> + ulong host_spefscr;
> + u64 acc;
> +#endif
> #ifdef CONFIG_ALTIVEC
> vector128 vr[32];
> vector128 vscr;
> diff --git a/arch/powerpc/kernel/asm-offsets.c =
b/arch/powerpc/kernel/asm-offsets.c
> index 75b72c7..554f4d6 100644
> --- a/arch/powerpc/kernel/asm-offsets.c
> +++ b/arch/powerpc/kernel/asm-offsets.c
> @@ -497,6 +497,12 @@ int main(void)
> DEFINE(TLBCAM_MAS3, offsetof(struct tlbcam, MAS3));
> DEFINE(TLBCAM_MAS7, offsetof(struct tlbcam, MAS7));
> #endif
> +#ifdef CONFIG_SPE
> + DEFINE(VCPU_EVR, offsetof(struct kvm_vcpu, arch.evr[0]));
> + DEFINE(VCPU_ACC, offsetof(struct kvm_vcpu, arch.acc));
> + DEFINE(VCPU_SPEFSCR, offsetof(struct kvm_vcpu, arch.spefscr));
> + DEFINE(VCPU_HOST_SPEFSCR, offsetof(struct kvm_vcpu, =
arch.host_spefscr));
> +#endif /* CONFIG_SPE */
>=20
> #ifdef CONFIG_KVM_EXIT_TIMING
> DEFINE(VCPU_TIMING_EXIT_TBU, offsetof(struct kvm_vcpu,
> diff --git a/arch/powerpc/kvm/booke.c b/arch/powerpc/kvm/booke.c
> index ef76acb..4e9c1a9 100644
> --- a/arch/powerpc/kvm/booke.c
> +++ b/arch/powerpc/kvm/booke.c
> @@ -13,6 +13,7 @@
> * Foundation, 51 Franklin Street, Fifth Floor, Boston, MA =
02110-1301, USA.
> *
> * Copyright IBM Corp. 2007
> + * Copyright (C) 2010 Freescale Semiconductor, Inc.
> *
> * Authors: Hollis Blanchard <hollisb@us.ibm.com>
> * Christian Ehrhardt <ehrhardt@linux.vnet.ibm.com>
> @@ -344,10 +345,19 @@ int kvmppc_handle_exit(struct kvm_run *run, =
struct kvm_vcpu *vcpu,
> r =3D RESUME_GUEST;
> break;
>=20
> - case BOOKE_INTERRUPT_SPE_UNAVAIL:
> - kvmppc_booke_queue_irqprio(vcpu, =
BOOKE_IRQPRIO_SPE_UNAVAIL);
> +#ifdef CONFIG_SPE
> + case BOOKE_INTERRUPT_SPE_UNAVAIL: {
This means that on non-SPE enabled host kernels you'll get a KVM fatal =
error when the guest triggers SPE_UNAVAIL, as the exit simply isn't =
handled and triggers:
default:
printk(KERN_EMERG "exit_nr %d\n", exit_nr);
BUG();
Alex
^ permalink raw reply
* Re: [PATCH 3/4] KVM: PPC: e500: Introduce msr_block for e500v2
From: Alexander Graf @ 2011-03-29 9:37 UTC (permalink / raw)
To: Scott Wood; +Cc: linuxppc-dev
In-Reply-To: <20110328192558.GB11104@schlenkerla.am.freescale.net>
On 28.03.2011, at 21:25, Scott Wood wrote:
> From: yu liu <yu.liu@freescale.com>
>=20
> In order to use lazy SPE register save/restore, we need to
> know when the guest is using MSR[SPE]. In order to do that, we
> need to control the actual MSR[SPE] separately from the guest's
> notion of MSR[SPE].
Please make this generic for BookE. There's no reason to have FSL ifdefs =
in the code.
Alex
^ permalink raw reply
* Re: [PATCH 2/4] KVM: PPC: booke: Wrap __kvmppc_vcpu_run()
From: Alexander Graf @ 2011-03-29 9:31 UTC (permalink / raw)
To: Scott Wood; +Cc: linuxppc-dev
In-Reply-To: <20110328192556.GA11104@schlenkerla.am.freescale.net>
On 28.03.2011, at 21:25, Scott Wood wrote:
> From: yu liu <yu.liu@freescale.com>
>=20
> We need to save/restore SPE environment on e500 core.
> Wrap __kvmppc_vcpu_run() so that we can put the SPE code in
> e500.c.
>=20
> Signed-off-by: Liu Yu <yu.liu@freescale.com>
> Signed-off-by: Scott Wood <scottwood@freescale.com>
> ---
> arch/powerpc/include/asm/kvm_ppc.h | 1 +
> arch/powerpc/kvm/44x.c | 5 +++++
> arch/powerpc/kvm/book3s.c | 1 -
> arch/powerpc/kvm/booke_interrupts.S | 2 +-
> arch/powerpc/kvm/e500.c | 5 +++++
> 5 files changed, 12 insertions(+), 2 deletions(-)
>=20
> diff --git a/arch/powerpc/include/asm/kvm_ppc.h =
b/arch/powerpc/include/asm/kvm_ppc.h
> index ecb3bc7..4e7a1be 100644
> --- a/arch/powerpc/include/asm/kvm_ppc.h
> +++ b/arch/powerpc/include/asm/kvm_ppc.h
> @@ -42,6 +42,7 @@ enum emulation_result {
> EMULATE_AGAIN, /* something went wrong. go again */
> };
>=20
> +extern int __kvmppc_vcpu_entry(struct kvm_run *kvm_run, struct =
kvm_vcpu *vcpu);
> extern int __kvmppc_vcpu_run(struct kvm_run *kvm_run, struct kvm_vcpu =
*vcpu);
> extern char kvmppc_handlers_start[];
> extern unsigned long kvmppc_handler_len;
> diff --git a/arch/powerpc/kvm/44x.c b/arch/powerpc/kvm/44x.c
> index 74d0e74..3d2e7d2 100644
> --- a/arch/powerpc/kvm/44x.c
> +++ b/arch/powerpc/kvm/44x.c
> @@ -147,6 +147,11 @@ void kvmppc_core_vcpu_free(struct kvm_vcpu *vcpu)
> kmem_cache_free(kvm_vcpu_cache, vcpu_44x);
> }
>=20
> +int __kvmppc_vcpu_run(struct kvm_run *kvm_run, struct kvm_vcpu *vcpu)
> +{
> + return __kvmppc_vcpu_entry(kvm_run, vcpu);
440 calls _entry on _run
> +}
> +
> static int __init kvmppc_44x_init(void)
> {
> int r;
> diff --git a/arch/powerpc/kvm/book3s.c b/arch/powerpc/kvm/book3s.c
> index c961de4..fb12853 100644
> --- a/arch/powerpc/kvm/book3s.c
> +++ b/arch/powerpc/kvm/book3s.c
> @@ -1379,7 +1379,6 @@ void kvmppc_core_vcpu_free(struct kvm_vcpu =
*vcpu)
> vfree(vcpu_book3s);
> }
>=20
> -extern int __kvmppc_vcpu_entry(struct kvm_run *kvm_run, struct =
kvm_vcpu *vcpu);
BookS loses its _entry reference?
> int __kvmppc_vcpu_run(struct kvm_run *kvm_run, struct kvm_vcpu *vcpu)
> {
> int ret;
> diff --git a/arch/powerpc/kvm/booke_interrupts.S =
b/arch/powerpc/kvm/booke_interrupts.S
> index 1cc471f..ab29f5f 100644
> --- a/arch/powerpc/kvm/booke_interrupts.S
> +++ b/arch/powerpc/kvm/booke_interrupts.S
> @@ -293,7 +293,7 @@ heavyweight_exit:
> * r3: kvm_run pointer
> * r4: vcpu pointer
> */
> -_GLOBAL(__kvmppc_vcpu_run)
> +_GLOBAL(__kvmppc_vcpu_entry)
BookE calls _run _entry now
> stwu r1, -HOST_STACK_SIZE(r1)
> stw r1, VCPU_HOST_STACK(r4) /* Save stack pointer to vcpu. =
*/
>=20
> diff --git a/arch/powerpc/kvm/e500.c b/arch/powerpc/kvm/e500.c
> index e3768ee..e762634 100644
> --- a/arch/powerpc/kvm/e500.c
> +++ b/arch/powerpc/kvm/e500.c
> @@ -70,6 +70,11 @@ int kvmppc_core_vcpu_setup(struct kvm_vcpu *vcpu)
> return 0;
> }
>=20
> +int __kvmppc_vcpu_run(struct kvm_run *kvm_run, struct kvm_vcpu *vcpu)
> +{
> + return __kvmppc_vcpu_entry(kvm_run, vcpu);
> +}
e500 calls _entry on _run
So you're basically adding a C wrapper around _entry. That's fine for =
me, but what is the BookS change about?
Alex
^ permalink raw reply
* Re: [PATCH 15/18] powerpc/smp: Don't expose per-cpu "cpu_state" array
From: Benjamin Herrenschmidt @ 2011-03-29 8:13 UTC (permalink / raw)
To: michael; +Cc: linuxppc-dev
In-Reply-To: <1301377040.32738.14.camel@concordia>
On Tue, 2011-03-29 at 16:37 +1100, Michael Ellerman wrote:
> Takes a cpu parameter ..
>
> > +void generic_set_cpu_dead(unsigned int cpu)
> > +{
> > + __get_cpu_var(cpu_state) = CPU_DEAD;
> > +}
>
> But doesn't use it.
Yeah that's a bug. Fortunately it's always called on the current CPU :-)
I'll fix it tho.
Thanks,
Ben.
^ permalink raw reply
* Re: [PATCH] POWER: perf_event: Skip updating kernel counters if register value shrinks
From: Benjamin Herrenschmidt @ 2011-03-29 6:03 UTC (permalink / raw)
To: Eric B Munson
Cc: a.p.zijlstra, linux-kernel, paulus, anton, acme, mingo,
linuxppc-dev
In-Reply-To: <1301059689-4556-1-git-send-email-emunson@mgebm.net>
On Fri, 2011-03-25 at 09:28 -0400, Eric B Munson wrote:
> It is possible on POWER7 for some perf events to have values decrease. This
> causes a problem with the way the kernel counters are updated. Deltas are
> computed and then stored in a 64 bit value while the registers are 32 bits
> wide so if new value is smaller than previous value, the delta is a very
> large positive value. As a work around this patch skips updating the kernel
> counter in when the new value is smaller than the previous. This can lead to
> a lack of precision in the coutner values, but from my testing the value is
> typcially fewer than 10 samples at a time.
Unfortunately the patch isn't 100% correct I believe:
I think you don't deal with the rollover of the counters. The new value
could be smaller than the previous one simply because the counter just
rolled over.
In cases like this:
> @@ -449,8 +458,10 @@ static void freeze_limited_counters(struct cpu_hw_events *cpuhw,
> val = (event->hw.idx == 5) ? pmc5 : pmc6;
> prev = local64_read(&event->hw.prev_count);
> event->hw.idx = 0;
> - delta = (val - prev) & 0xfffffffful;
> - local64_add(delta, &event->count);
> + if (val >= prev) {
> + delta = (val - prev) & 0xfffffffful;
> + local64_add(delta, &event->count);
> + }
> }
> }
I wonder if it isn't easier to just define delta to be a s32, get rid
of the mask and test if delta is positive, something like:
delta = val - prev;
if (delta > 0)
local64_add(delta, &event->count);
Wouldn't that be simpler ? Or do I miss a reason why it wouldn't work ?
Cheers,
Ben.
^ permalink raw reply
* Re: [PATCH 15/18] powerpc/smp: Don't expose per-cpu "cpu_state" array
From: Michael Ellerman @ 2011-03-29 5:37 UTC (permalink / raw)
To: Benjamin Herrenschmidt; +Cc: linuxppc-dev
In-Reply-To: <1299566250-10516-16-git-send-email-benh@kernel.crashing.org>
[-- Attachment #1: Type: text/plain, Size: 1179 bytes --]
On Tue, 2011-03-08 at 17:37 +1100, Benjamin Herrenschmidt wrote:
> Instead, keep it static, expose an accessor and use that from
> the PowerMac code. Avoids easy namespace collisions and will
> make it easier to consolidate with other implementations.
>
> Signed-off-by: Benjamin Herrenschmidt <benh@kernel.crashing.org>
> ---
> arch/powerpc/include/asm/smp.h | 2 +-
> arch/powerpc/kernel/smp.c | 7 ++++++-
> arch/powerpc/platforms/powermac/smp.c | 12 ++++++++----
> 3 files changed, 15 insertions(+), 6 deletions(-)
>
> diff --git a/arch/powerpc/include/asm/smp.h b/arch/powerpc/include/asm/smp.h
> index 7e99771..a902a0d 100644
> --- a/arch/powerpc/include/asm/smp.h
> +++ b/arch/powerpc/include/asm/smp.h
> @@ -45,7 +45,7 @@ extern void migrate_irqs(void);
> int generic_cpu_disable(void);
> void generic_cpu_die(unsigned int cpu);
> void generic_mach_cpu_die(void);
> -DECLARE_PER_CPU(int, cpu_state);
> +void generic_set_cpu_dead(unsigned int cpu);
Takes a cpu parameter ..
> +void generic_set_cpu_dead(unsigned int cpu)
> +{
> + __get_cpu_var(cpu_state) = CPU_DEAD;
> +}
But doesn't use it.
cheers
[-- Attachment #2: This is a digitally signed message part --]
[-- Type: application/pgp-signature, Size: 198 bytes --]
^ permalink raw reply
* [PATCH v3 4/4] xhci: Remove recursive call to xhci_handle_event
From: Matt Evans @ 2011-03-29 2:41 UTC (permalink / raw)
To: Sarah Sharp; +Cc: linuxppc-dev, linux-usb
Make the caller loop while there are events to handle, instead.
Signed-off-by: Matt Evans <matt@ozlabs.org>
---
Added a comment on the return value, defining <0 to be 'bad'.
drivers/usb/host/xhci-ring.c | 18 +++++++++++-------
1 files changed, 11 insertions(+), 7 deletions(-)
diff --git a/drivers/usb/host/xhci-ring.c b/drivers/usb/host/xhci-ring.c
index 86c8198..5fce46c 100644
--- a/drivers/usb/host/xhci-ring.c
+++ b/drivers/usb/host/xhci-ring.c
@@ -2128,8 +2128,10 @@ cleanup:
/*
* This function handles all OS-owned events on the event ring. It may drop
* xhci->lock between event processing (e.g. to pass up port status changes).
+ * Returns >0 for "possibly more events to process" (caller should call again),
+ * otherwise 0 if done. In future, <0 returns should indicate error code.
*/
-static void xhci_handle_event(struct xhci_hcd *xhci)
+static int xhci_handle_event(struct xhci_hcd *xhci)
{
union xhci_trb *event;
int update_ptrs = 1;
@@ -2138,7 +2140,7 @@ static void xhci_handle_event(struct xhci_hcd *xhci)
xhci_dbg(xhci, "In %s\n", __func__);
if (!xhci->event_ring || !xhci->event_ring->dequeue) {
xhci->error_bitmask |= 1 << 1;
- return;
+ return 0;
}
event = xhci->event_ring->dequeue;
@@ -2146,7 +2148,7 @@ static void xhci_handle_event(struct xhci_hcd *xhci)
if ((le32_to_cpu(event->event_cmd.flags) & TRB_CYCLE) !=
xhci->event_ring->cycle_state) {
xhci->error_bitmask |= 1 << 2;
- return;
+ return 0;
}
xhci_dbg(xhci, "%s - OS owns TRB\n", __func__);
@@ -2190,15 +2192,17 @@ static void xhci_handle_event(struct xhci_hcd *xhci)
if (xhci->xhc_state & XHCI_STATE_DYING) {
xhci_dbg(xhci, "xHCI host dying, returning from "
"event handler.\n");
- return;
+ return 0;
}
if (update_ptrs)
/* Update SW event ring dequeue pointer */
inc_deq(xhci, xhci->event_ring, true);
- /* Are there more items on the event ring? */
- xhci_handle_event(xhci);
+ /* Are there more items on the event ring? Caller will call us again to
+ * check.
+ */
+ return 1;
}
/*
@@ -2280,7 +2284,7 @@ hw_died:
/* FIXME this should be a delayed service routine
* that clears the EHB.
*/
- xhci_handle_event(xhci);
+ while (xhci_handle_event(xhci) > 0) {}
temp_64 = xhci_read_64(xhci, &xhci->ir_set->erst_dequeue);
/* If necessary, update the HW's version of the event ring deq ptr. */
--
1.7.0.4
^ permalink raw reply related
* [PATCH v3 3/4] xhci: Add an assertion to check for virt_dev=0 bug.
From: Matt Evans @ 2011-03-29 2:40 UTC (permalink / raw)
To: Sarah Sharp; +Cc: linuxppc-dev, linux-usb
During a "plug-unplug" stress test on an NEC xHCI card, a null pointer
dereference was observed. xhci_address_device() dereferenced a null
virt_dev (possibly an erroneous udev->slot_id?); this patch adds a WARN_ON &
message to aid debug if it can be recreated.
Signed-off-by: Matt Evans <matt@ozlabs.org>
---
drivers/usb/host/xhci.c | 11 +++++++++++
1 files changed, 11 insertions(+), 0 deletions(-)
diff --git a/drivers/usb/host/xhci.c b/drivers/usb/host/xhci.c
index 3a9f931..d145fa3 100644
--- a/drivers/usb/host/xhci.c
+++ b/drivers/usb/host/xhci.c
@@ -2533,6 +2533,17 @@ int xhci_address_device(struct usb_hcd *hcd, struct usb_device *udev)
virt_dev = xhci->devs[udev->slot_id];
+ if (WARN_ON(!virt_dev)) {
+ /*
+ * In plug/unplug torture test with an NEC controller,
+ * a zero-dereference was observed once due to virt_dev = 0.
+ * Print useful debug rather than crash if it is observed again!
+ */
+ xhci_warn(xhci, "Virt dev invalid for slot_id 0x%x!\n",
+ udev->slot_id);
+ return -EINVAL;
+ }
+
slot_ctx = xhci_get_slot_ctx(xhci, virt_dev->in_ctx);
/*
* If this is the first Set Address since device plug-in or
--
1.7.0.4
^ permalink raw reply related
* [PATCH v3 2/4] xhci: Add rmb() between reading event validity & event data access.
From: Matt Evans @ 2011-03-29 2:40 UTC (permalink / raw)
To: Sarah Sharp; +Cc: linuxppc-dev, linux-usb
On weakly-ordered systems, the reading of an event's content must occur
after reading the event's validity.
Signed-off-by: Matt Evans <matt@ozlabs.org>
---
drivers/usb/host/xhci-ring.c | 5 +++++
1 files changed, 5 insertions(+), 0 deletions(-)
diff --git a/drivers/usb/host/xhci-ring.c b/drivers/usb/host/xhci-ring.c
index 1361032..86c8198 100644
--- a/drivers/usb/host/xhci-ring.c
+++ b/drivers/usb/host/xhci-ring.c
@@ -2150,6 +2150,11 @@ static void xhci_handle_event(struct xhci_hcd *xhci)
}
xhci_dbg(xhci, "%s - OS owns TRB\n", __func__);
+ /*
+ * Barrier between reading the TRB_CYCLE (valid) flag above and any
+ * speculative reads of the event's flags/data below.
+ */
+ rmb();
/* FIXME: Handle more event types. */
switch ((le32_to_cpu(event->event_cmd.flags) & TRB_TYPE_BITMASK)) {
case TRB_TYPE(TRB_COMPLETION):
--
1.7.0.4
^ permalink raw reply related
* [PATCH v3 1/4] xhci: Make xHCI driver endian-safe
From: Matt Evans @ 2011-03-29 2:40 UTC (permalink / raw)
To: Sarah Sharp; +Cc: linuxppc-dev, linux-usb
This patch changes the struct members defining access to xHCI device-visible
memory to use __le32/__le64 where appropriate, and then adds swaps where
required. Checked with sparse that all accesses are correct.
MMIO accesses use readl/writel so already are performed LE, but prototypes
now reflect this with __le*.
There were a couple of (debug) instances of DMA pointers being truncated to
32bits which have been fixed too.
Signed-off-by: Matt Evans <matt@ozlabs.org>
---
drivers/usb/host/xhci-dbg.c | 51 ++++----
drivers/usb/host/xhci-hub.c | 18 ++--
drivers/usb/host/xhci-mem.c | 122 ++++++++++----------
drivers/usb/host/xhci-ring.c | 267 ++++++++++++++++++++++--------------------
drivers/usb/host/xhci.c | 109 +++++++++--------
drivers/usb/host/xhci.h | 134 +++++++++++-----------
6 files changed, 360 insertions(+), 341 deletions(-)
diff --git a/drivers/usb/host/xhci-dbg.c b/drivers/usb/host/xhci-dbg.c
index 0231814..2e04861 100644
--- a/drivers/usb/host/xhci-dbg.c
+++ b/drivers/usb/host/xhci-dbg.c
@@ -147,7 +147,7 @@ static void xhci_print_op_regs(struct xhci_hcd *xhci)
static void xhci_print_ports(struct xhci_hcd *xhci)
{
- u32 __iomem *addr;
+ __le32 __iomem *addr;
int i, j;
int ports;
char *names[NUM_PORT_REGS] = {
@@ -253,27 +253,27 @@ void xhci_print_trb_offsets(struct xhci_hcd *xhci, union xhci_trb *trb)
void xhci_debug_trb(struct xhci_hcd *xhci, union xhci_trb *trb)
{
u64 address;
- u32 type = xhci_readl(xhci, &trb->link.control) & TRB_TYPE_BITMASK;
+ u32 type = le32_to_cpu(trb->link.control) & TRB_TYPE_BITMASK;
switch (type) {
case TRB_TYPE(TRB_LINK):
xhci_dbg(xhci, "Link TRB:\n");
xhci_print_trb_offsets(xhci, trb);
- address = trb->link.segment_ptr;
+ address = le64_to_cpu(trb->link.segment_ptr);
xhci_dbg(xhci, "Next ring segment DMA address = 0x%llx\n", address);
xhci_dbg(xhci, "Interrupter target = 0x%x\n",
- GET_INTR_TARGET(trb->link.intr_target));
+ GET_INTR_TARGET(le32_to_cpu(trb->link.intr_target)));
xhci_dbg(xhci, "Cycle bit = %u\n",
- (unsigned int) (trb->link.control & TRB_CYCLE));
+ (unsigned int) (le32_to_cpu(trb->link.control) & TRB_CYCLE));
xhci_dbg(xhci, "Toggle cycle bit = %u\n",
- (unsigned int) (trb->link.control & LINK_TOGGLE));
+ (unsigned int) (le32_to_cpu(trb->link.control) & LINK_TOGGLE));
xhci_dbg(xhci, "No Snoop bit = %u\n",
- (unsigned int) (trb->link.control & TRB_NO_SNOOP));
+ (unsigned int) (le32_to_cpu(trb->link.control) & TRB_NO_SNOOP));
break;
case TRB_TYPE(TRB_TRANSFER):
- address = trb->trans_event.buffer;
+ address = le64_to_cpu(trb->trans_event.buffer);
/*
* FIXME: look at flags to figure out if it's an address or if
* the data is directly in the buffer field.
@@ -281,11 +281,12 @@ void xhci_debug_trb(struct xhci_hcd *xhci, union xhci_trb *trb)
xhci_dbg(xhci, "DMA address or buffer contents= %llu\n", address);
break;
case TRB_TYPE(TRB_COMPLETION):
- address = trb->event_cmd.cmd_trb;
+ address = le64_to_cpu(trb->event_cmd.cmd_trb);
xhci_dbg(xhci, "Command TRB pointer = %llu\n", address);
xhci_dbg(xhci, "Completion status = %u\n",
- (unsigned int) GET_COMP_CODE(trb->event_cmd.status));
- xhci_dbg(xhci, "Flags = 0x%x\n", (unsigned int) trb->event_cmd.flags);
+ (unsigned int) GET_COMP_CODE(le32_to_cpu(trb->event_cmd.status)));
+ xhci_dbg(xhci, "Flags = 0x%x\n",
+ (unsigned int) le32_to_cpu(trb->event_cmd.flags));
break;
default:
xhci_dbg(xhci, "Unknown TRB with TRB type ID %u\n",
@@ -311,16 +312,16 @@ void xhci_debug_trb(struct xhci_hcd *xhci, union xhci_trb *trb)
void xhci_debug_segment(struct xhci_hcd *xhci, struct xhci_segment *seg)
{
int i;
- u32 addr = (u32) seg->dma;
+ u64 addr = seg->dma;
union xhci_trb *trb = seg->trbs;
for (i = 0; i < TRBS_PER_SEGMENT; ++i) {
trb = &seg->trbs[i];
- xhci_dbg(xhci, "@%08x %08x %08x %08x %08x\n", addr,
- lower_32_bits(trb->link.segment_ptr),
- upper_32_bits(trb->link.segment_ptr),
- (unsigned int) trb->link.intr_target,
- (unsigned int) trb->link.control);
+ xhci_dbg(xhci, "@%016llx %08x %08x %08x %08x\n", addr,
+ (u32)lower_32_bits(le64_to_cpu(trb->link.segment_ptr)),
+ (u32)upper_32_bits(le64_to_cpu(trb->link.segment_ptr)),
+ (unsigned int) le32_to_cpu(trb->link.intr_target),
+ (unsigned int) le32_to_cpu(trb->link.control));
addr += sizeof(*trb);
}
}
@@ -391,18 +392,18 @@ void xhci_dbg_ep_rings(struct xhci_hcd *xhci,
void xhci_dbg_erst(struct xhci_hcd *xhci, struct xhci_erst *erst)
{
- u32 addr = (u32) erst->erst_dma_addr;
+ u64 addr = erst->erst_dma_addr;
int i;
struct xhci_erst_entry *entry;
for (i = 0; i < erst->num_entries; ++i) {
entry = &erst->entries[i];
- xhci_dbg(xhci, "@%08x %08x %08x %08x %08x\n",
- (unsigned int) addr,
- lower_32_bits(entry->seg_addr),
- upper_32_bits(entry->seg_addr),
- (unsigned int) entry->seg_size,
- (unsigned int) entry->rsvd);
+ xhci_dbg(xhci, "@%016llx %08x %08x %08x %08x\n",
+ addr,
+ lower_32_bits(le64_to_cpu(entry->seg_addr)),
+ upper_32_bits(le64_to_cpu(entry->seg_addr)),
+ (unsigned int) le32_to_cpu(entry->seg_size),
+ (unsigned int) le32_to_cpu(entry->rsvd));
addr += sizeof(*entry);
}
}
@@ -436,7 +437,7 @@ char *xhci_get_slot_state(struct xhci_hcd *xhci,
{
struct xhci_slot_ctx *slot_ctx = xhci_get_slot_ctx(xhci, ctx);
- switch (GET_SLOT_STATE(slot_ctx->dev_state)) {
+ switch (GET_SLOT_STATE(le32_to_cpu(slot_ctx->dev_state))) {
case 0:
return "enabled/disabled";
case 1:
diff --git a/drivers/usb/host/xhci-hub.c b/drivers/usb/host/xhci-hub.c
index a78f2eb..ae1d24c 100644
--- a/drivers/usb/host/xhci-hub.c
+++ b/drivers/usb/host/xhci-hub.c
@@ -50,7 +50,7 @@ static void xhci_common_hub_descriptor(struct xhci_hcd *xhci,
temp |= 0x0008;
/* Bits 6:5 - no TTs in root ports */
/* Bit 7 - no port indicators */
- desc->wHubCharacteristics = (__force __u16) cpu_to_le16(temp);
+ desc->wHubCharacteristics = cpu_to_le16(temp);
}
/* Fill in the USB 2.0 roothub descriptor */
@@ -314,7 +314,7 @@ void xhci_ring_device(struct xhci_hcd *xhci, int slot_id)
}
static void xhci_disable_port(struct usb_hcd *hcd, struct xhci_hcd *xhci,
- u16 wIndex, u32 __iomem *addr, u32 port_status)
+ u16 wIndex, __le32 __iomem *addr, u32 port_status)
{
/* Don't allow the USB core to disable SuperSpeed ports. */
if (hcd->speed == HCD_USB3) {
@@ -331,7 +331,7 @@ static void xhci_disable_port(struct usb_hcd *hcd, struct xhci_hcd *xhci,
}
static void xhci_clear_port_change_bit(struct xhci_hcd *xhci, u16 wValue,
- u16 wIndex, u32 __iomem *addr, u32 port_status)
+ u16 wIndex, __le32 __iomem *addr, u32 port_status)
{
char *port_change_bit;
u32 status;
@@ -376,7 +376,7 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,
unsigned long flags;
u32 temp, temp1, status;
int retval = 0;
- u32 __iomem **port_array;
+ __le32 __iomem **port_array;
int slot_id;
struct xhci_bus_state *bus_state;
@@ -664,7 +664,7 @@ int xhci_hub_status_data(struct usb_hcd *hcd, char *buf)
int i, retval;
struct xhci_hcd *xhci = hcd_to_xhci(hcd);
int ports;
- u32 __iomem **port_array;
+ __le32 __iomem **port_array;
struct xhci_bus_state *bus_state;
if (hcd->speed == HCD_USB3) {
@@ -709,7 +709,7 @@ int xhci_bus_suspend(struct usb_hcd *hcd)
{
struct xhci_hcd *xhci = hcd_to_xhci(hcd);
int max_ports, port_index;
- u32 __iomem **port_array;
+ __le32 __iomem **port_array;
struct xhci_bus_state *bus_state;
unsigned long flags;
@@ -779,7 +779,7 @@ int xhci_bus_suspend(struct usb_hcd *hcd)
if (DEV_HIGHSPEED(t1)) {
/* enable remote wake up for USB 2.0 */
- u32 __iomem *addr;
+ __le32 __iomem *addr;
u32 tmp;
/* Add one to the port status register address to get
@@ -801,7 +801,7 @@ int xhci_bus_resume(struct usb_hcd *hcd)
{
struct xhci_hcd *xhci = hcd_to_xhci(hcd);
int max_ports, port_index;
- u32 __iomem **port_array;
+ __le32 __iomem **port_array;
struct xhci_bus_state *bus_state;
u32 temp;
unsigned long flags;
@@ -875,7 +875,7 @@ int xhci_bus_resume(struct usb_hcd *hcd)
if (DEV_HIGHSPEED(temp)) {
/* disable remote wake up for USB 2.0 */
- u32 __iomem *addr;
+ __le32 __iomem *addr;
u32 tmp;
/* Add one to the port status register address to get
diff --git a/drivers/usb/host/xhci-mem.c b/drivers/usb/host/xhci-mem.c
index a003e79..e3a219d 100644
--- a/drivers/usb/host/xhci-mem.c
+++ b/drivers/usb/host/xhci-mem.c
@@ -89,16 +89,17 @@ static void xhci_link_segments(struct xhci_hcd *xhci, struct xhci_segment *prev,
return;
prev->next = next;
if (link_trbs) {
- prev->trbs[TRBS_PER_SEGMENT-1].link.segment_ptr = next->dma;
+ prev->trbs[TRBS_PER_SEGMENT-1].link.
+ segment_ptr = cpu_to_le64(next->dma);
/* Set the last TRB in the segment to have a TRB type ID of Link TRB */
- val = prev->trbs[TRBS_PER_SEGMENT-1].link.control;
+ val = le32_to_cpu(prev->trbs[TRBS_PER_SEGMENT-1].link.control);
val &= ~TRB_TYPE_BITMASK;
val |= TRB_TYPE(TRB_LINK);
/* Always set the chain bit with 0.95 hardware */
if (xhci_link_trb_quirk(xhci))
val |= TRB_CHAIN;
- prev->trbs[TRBS_PER_SEGMENT-1].link.control = val;
+ prev->trbs[TRBS_PER_SEGMENT-1].link.control = cpu_to_le32(val);
}
xhci_dbg(xhci, "Linking segment 0x%llx to segment 0x%llx (DMA)\n",
(unsigned long long)prev->dma,
@@ -186,7 +187,8 @@ static struct xhci_ring *xhci_ring_alloc(struct xhci_hcd *xhci,
if (link_trbs) {
/* See section 4.9.2.1 and 6.4.4.1 */
- prev->trbs[TRBS_PER_SEGMENT-1].link.control |= (LINK_TOGGLE);
+ prev->trbs[TRBS_PER_SEGMENT-1].link.
+ control |= cpu_to_le32(LINK_TOGGLE);
xhci_dbg(xhci, "Wrote link toggle flag to"
" segment %p (virtual), 0x%llx (DMA)\n",
prev, (unsigned long long)prev->dma);
@@ -548,7 +550,8 @@ struct xhci_stream_info *xhci_alloc_stream_info(struct xhci_hcd *xhci,
addr = cur_ring->first_seg->dma |
SCT_FOR_CTX(SCT_PRI_TR) |
cur_ring->cycle_state;
- stream_info->stream_ctx_array[cur_stream].stream_ring = addr;
+ stream_info->stream_ctx_array[cur_stream].
+ stream_ring = cpu_to_le64(addr);
xhci_dbg(xhci, "Setting stream %d ring ptr to 0x%08llx\n",
cur_stream, (unsigned long long) addr);
@@ -614,10 +617,10 @@ void xhci_setup_streams_ep_input_ctx(struct xhci_hcd *xhci,
max_primary_streams = fls(stream_info->num_stream_ctxs) - 2;
xhci_dbg(xhci, "Setting number of stream ctx array entries to %u\n",
1 << (max_primary_streams + 1));
- ep_ctx->ep_info &= ~EP_MAXPSTREAMS_MASK;
- ep_ctx->ep_info |= EP_MAXPSTREAMS(max_primary_streams);
- ep_ctx->ep_info |= EP_HAS_LSA;
- ep_ctx->deq = stream_info->ctx_array_dma;
+ ep_ctx->ep_info &= cpu_to_le32(~EP_MAXPSTREAMS_MASK);
+ ep_ctx->ep_info |= cpu_to_le32(EP_MAXPSTREAMS(max_primary_streams)
+ | EP_HAS_LSA);
+ ep_ctx->deq = cpu_to_le64(stream_info->ctx_array_dma);
}
/*
@@ -630,10 +633,9 @@ void xhci_setup_no_streams_ep_input_ctx(struct xhci_hcd *xhci,
struct xhci_virt_ep *ep)
{
dma_addr_t addr;
- ep_ctx->ep_info &= ~EP_MAXPSTREAMS_MASK;
- ep_ctx->ep_info &= ~EP_HAS_LSA;
+ ep_ctx->ep_info &= cpu_to_le32(~(EP_MAXPSTREAMS_MASK | EP_HAS_LSA));
addr = xhci_trb_virt_to_dma(ep->ring->deq_seg, ep->ring->dequeue);
- ep_ctx->deq = addr | ep->ring->cycle_state;
+ ep_ctx->deq = cpu_to_le64(addr | ep->ring->cycle_state);
}
/* Frees all stream contexts associated with the endpoint,
@@ -781,11 +783,11 @@ int xhci_alloc_virt_device(struct xhci_hcd *xhci, int slot_id,
dev->udev = udev;
/* Point to output device context in dcbaa. */
- xhci->dcbaa->dev_context_ptrs[slot_id] = dev->out_ctx->dma;
+ xhci->dcbaa->dev_context_ptrs[slot_id] = cpu_to_le64(dev->out_ctx->dma);
xhci_dbg(xhci, "Set slot id %d dcbaa entry %p to 0x%llx\n",
- slot_id,
- &xhci->dcbaa->dev_context_ptrs[slot_id],
- (unsigned long long) xhci->dcbaa->dev_context_ptrs[slot_id]);
+ slot_id,
+ &xhci->dcbaa->dev_context_ptrs[slot_id],
+ (unsigned long long) le64_to_cpu(xhci->dcbaa->dev_context_ptrs[slot_id]));
return 1;
fail:
@@ -810,8 +812,9 @@ void xhci_copy_ep0_dequeue_into_input_ctx(struct xhci_hcd *xhci,
* configured device has reset, so all control transfers should have
* been completed or cancelled before the reset.
*/
- ep0_ctx->deq = xhci_trb_virt_to_dma(ep_ring->enq_seg, ep_ring->enqueue);
- ep0_ctx->deq |= ep_ring->cycle_state;
+ ep0_ctx->deq = cpu_to_le64(xhci_trb_virt_to_dma(ep_ring->enq_seg,
+ ep_ring->enqueue)
+ | ep_ring->cycle_state);
}
/*
@@ -885,24 +888,22 @@ int xhci_setup_addressable_virt_dev(struct xhci_hcd *xhci, struct usb_device *ud
slot_ctx = xhci_get_slot_ctx(xhci, dev->in_ctx);
/* 2) New slot context and endpoint 0 context are valid*/
- ctrl_ctx->add_flags = SLOT_FLAG | EP0_FLAG;
+ ctrl_ctx->add_flags = cpu_to_le32(SLOT_FLAG | EP0_FLAG);
/* 3) Only the control endpoint is valid - one endpoint context */
- slot_ctx->dev_info |= LAST_CTX(1);
-
- slot_ctx->dev_info |= (u32) udev->route;
+ slot_ctx->dev_info |= cpu_to_le32(LAST_CTX(1) | (u32) udev->route);
switch (udev->speed) {
case USB_SPEED_SUPER:
- slot_ctx->dev_info |= (u32) SLOT_SPEED_SS;
+ slot_ctx->dev_info |= cpu_to_le32((u32) SLOT_SPEED_SS);
break;
case USB_SPEED_HIGH:
- slot_ctx->dev_info |= (u32) SLOT_SPEED_HS;
+ slot_ctx->dev_info |= cpu_to_le32((u32) SLOT_SPEED_HS);
break;
case USB_SPEED_FULL:
- slot_ctx->dev_info |= (u32) SLOT_SPEED_FS;
+ slot_ctx->dev_info |= cpu_to_le32((u32) SLOT_SPEED_FS);
break;
case USB_SPEED_LOW:
- slot_ctx->dev_info |= (u32) SLOT_SPEED_LS;
+ slot_ctx->dev_info |= cpu_to_le32((u32) SLOT_SPEED_LS);
break;
case USB_SPEED_WIRELESS:
xhci_dbg(xhci, "FIXME xHCI doesn't support wireless speeds\n");
@@ -916,7 +917,7 @@ int xhci_setup_addressable_virt_dev(struct xhci_hcd *xhci, struct usb_device *ud
port_num = xhci_find_real_port_number(xhci, udev);
if (!port_num)
return -EINVAL;
- slot_ctx->dev_info2 |= (u32) ROOT_HUB_PORT(port_num);
+ slot_ctx->dev_info2 |= cpu_to_le32((u32) ROOT_HUB_PORT(port_num));
/* Set the port number in the virtual_device to the faked port number */
for (top_dev = udev; top_dev->parent && top_dev->parent->parent;
top_dev = top_dev->parent)
@@ -927,31 +928,31 @@ int xhci_setup_addressable_virt_dev(struct xhci_hcd *xhci, struct usb_device *ud
/* Is this a LS/FS device under an external HS hub? */
if (udev->tt && udev->tt->hub->parent) {
- slot_ctx->tt_info = udev->tt->hub->slot_id;
- slot_ctx->tt_info |= udev->ttport << 8;
+ slot_ctx->tt_info = cpu_to_le32(udev->tt->hub->slot_id |
+ (udev->ttport << 8));
if (udev->tt->multi)
- slot_ctx->dev_info |= DEV_MTT;
+ slot_ctx->dev_info |= cpu_to_le32(DEV_MTT);
}
xhci_dbg(xhci, "udev->tt = %p\n", udev->tt);
xhci_dbg(xhci, "udev->ttport = 0x%x\n", udev->ttport);
/* Step 4 - ring already allocated */
/* Step 5 */
- ep0_ctx->ep_info2 = EP_TYPE(CTRL_EP);
+ ep0_ctx->ep_info2 = cpu_to_le32(EP_TYPE(CTRL_EP));
/*
* XXX: Not sure about wireless USB devices.
*/
switch (udev->speed) {
case USB_SPEED_SUPER:
- ep0_ctx->ep_info2 |= MAX_PACKET(512);
+ ep0_ctx->ep_info2 |= cpu_to_le32(MAX_PACKET(512));
break;
case USB_SPEED_HIGH:
/* USB core guesses at a 64-byte max packet first for FS devices */
case USB_SPEED_FULL:
- ep0_ctx->ep_info2 |= MAX_PACKET(64);
+ ep0_ctx->ep_info2 |= cpu_to_le32(MAX_PACKET(64));
break;
case USB_SPEED_LOW:
- ep0_ctx->ep_info2 |= MAX_PACKET(8);
+ ep0_ctx->ep_info2 |= cpu_to_le32(MAX_PACKET(8));
break;
case USB_SPEED_WIRELESS:
xhci_dbg(xhci, "FIXME xHCI doesn't support wireless speeds\n");
@@ -962,12 +963,10 @@ int xhci_setup_addressable_virt_dev(struct xhci_hcd *xhci, struct usb_device *ud
BUG();
}
/* EP 0 can handle "burst" sizes of 1, so Max Burst Size field is 0 */
- ep0_ctx->ep_info2 |= MAX_BURST(0);
- ep0_ctx->ep_info2 |= ERROR_COUNT(3);
+ ep0_ctx->ep_info2 |= cpu_to_le32(MAX_BURST(0) | ERROR_COUNT(3));
- ep0_ctx->deq =
- dev->eps[0].ring->first_seg->dma;
- ep0_ctx->deq |= dev->eps[0].ring->cycle_state;
+ ep0_ctx->deq = cpu_to_le64(dev->eps[0].ring->first_seg->dma |
+ dev->eps[0].ring->cycle_state);
/* Steps 7 and 8 were done in xhci_alloc_virt_device() */
@@ -1099,8 +1098,8 @@ static inline u32 xhci_get_max_esit_payload(struct xhci_hcd *xhci,
if (udev->speed == USB_SPEED_SUPER)
return ep->ss_ep_comp.wBytesPerInterval;
- max_packet = GET_MAX_PACKET(ep->desc.wMaxPacketSize);
- max_burst = (ep->desc.wMaxPacketSize & 0x1800) >> 11;
+ max_packet = GET_MAX_PACKET(le16_to_cpu(ep->desc.wMaxPacketSize));
+ max_burst = (le16_to_cpu(ep->desc.wMaxPacketSize) & 0x1800) >> 11;
/* A 0 in max burst means 1 transfer per ESIT */
return max_packet * (max_burst + 1);
}
@@ -1149,10 +1148,10 @@ int xhci_endpoint_init(struct xhci_hcd *xhci,
}
virt_dev->eps[ep_index].skip = false;
ep_ring = virt_dev->eps[ep_index].new_ring;
- ep_ctx->deq = ep_ring->first_seg->dma | ep_ring->cycle_state;
+ ep_ctx->deq = cpu_to_le64(ep_ring->first_seg->dma | ep_ring->cycle_state);
- ep_ctx->ep_info = xhci_get_endpoint_interval(udev, ep);
- ep_ctx->ep_info |= EP_MULT(xhci_get_endpoint_mult(udev, ep));
+ ep_ctx->ep_info = cpu_to_le32(xhci_get_endpoint_interval(udev, ep)
+ | EP_MULT(xhci_get_endpoint_mult(udev, ep)));
/* FIXME dig Mult and streams info out of ep companion desc */
@@ -1160,22 +1159,22 @@ int xhci_endpoint_init(struct xhci_hcd *xhci,
* error count = 0 means infinite retries.
*/
if (!usb_endpoint_xfer_isoc(&ep->desc))
- ep_ctx->ep_info2 = ERROR_COUNT(3);
+ ep_ctx->ep_info2 = cpu_to_le32(ERROR_COUNT(3));
else
- ep_ctx->ep_info2 = ERROR_COUNT(1);
+ ep_ctx->ep_info2 = cpu_to_le32(ERROR_COUNT(1));
- ep_ctx->ep_info2 |= xhci_get_endpoint_type(udev, ep);
+ ep_ctx->ep_info2 |= cpu_to_le32(xhci_get_endpoint_type(udev, ep));
/* Set the max packet size and max burst */
switch (udev->speed) {
case USB_SPEED_SUPER:
- max_packet = ep->desc.wMaxPacketSize;
- ep_ctx->ep_info2 |= MAX_PACKET(max_packet);
+ max_packet = le16_to_cpu(ep->desc.wMaxPacketSize);
+ ep_ctx->ep_info2 |= cpu_to_le32(MAX_PACKET(max_packet));
/* dig out max burst from ep companion desc */
max_packet = ep->ss_ep_comp.bMaxBurst;
if (!max_packet)
xhci_warn(xhci, "WARN no SS endpoint bMaxBurst\n");
- ep_ctx->ep_info2 |= MAX_BURST(max_packet);
+ ep_ctx->ep_info2 |= cpu_to_le32(MAX_BURST(max_packet));
break;
case USB_SPEED_HIGH:
/* bits 11:12 specify the number of additional transaction
@@ -1183,20 +1182,21 @@ int xhci_endpoint_init(struct xhci_hcd *xhci,
*/
if (usb_endpoint_xfer_isoc(&ep->desc) ||
usb_endpoint_xfer_int(&ep->desc)) {
- max_burst = (ep->desc.wMaxPacketSize & 0x1800) >> 11;
- ep_ctx->ep_info2 |= MAX_BURST(max_burst);
+ max_burst = (le16_to_cpu(ep->desc.wMaxPacketSize)
+ & 0x1800) >> 11;
+ ep_ctx->ep_info2 |= cpu_to_le32(MAX_BURST(max_burst));
}
/* Fall through */
case USB_SPEED_FULL:
case USB_SPEED_LOW:
- max_packet = GET_MAX_PACKET(ep->desc.wMaxPacketSize);
- ep_ctx->ep_info2 |= MAX_PACKET(max_packet);
+ max_packet = GET_MAX_PACKET(le16_to_cpu(ep->desc.wMaxPacketSize));
+ ep_ctx->ep_info2 |= cpu_to_le32(MAX_PACKET(max_packet));
break;
default:
BUG();
}
max_esit_payload = xhci_get_max_esit_payload(xhci, udev, ep);
- ep_ctx->tx_info = MAX_ESIT_PAYLOAD_FOR_EP(max_esit_payload);
+ ep_ctx->tx_info = cpu_to_le32(MAX_ESIT_PAYLOAD_FOR_EP(max_esit_payload));
/*
* XXX no idea how to calculate the average TRB buffer length for bulk
@@ -1213,7 +1213,7 @@ int xhci_endpoint_init(struct xhci_hcd *xhci,
* use Event Data TRBs, and we don't chain in a link TRB on short
* transfers, we're basically dividing by 1.
*/
- ep_ctx->tx_info |= AVG_TRB_LENGTH_FOR_EP(max_esit_payload);
+ ep_ctx->tx_info |= cpu_to_le32(AVG_TRB_LENGTH_FOR_EP(max_esit_payload));
/* FIXME Debug endpoint context */
return 0;
@@ -1313,7 +1313,7 @@ static int scratchpad_alloc(struct xhci_hcd *xhci, gfp_t flags)
if (!xhci->scratchpad->sp_dma_buffers)
goto fail_sp4;
- xhci->dcbaa->dev_context_ptrs[0] = xhci->scratchpad->sp_dma;
+ xhci->dcbaa->dev_context_ptrs[0] = cpu_to_le64(xhci->scratchpad->sp_dma);
for (i = 0; i < num_sp; i++) {
dma_addr_t dma;
void *buf = pci_alloc_consistent(to_pci_dev(dev),
@@ -1690,7 +1690,7 @@ static void xhci_set_hc_event_deq(struct xhci_hcd *xhci)
}
static void xhci_add_in_port(struct xhci_hcd *xhci, unsigned int num_ports,
- u32 __iomem *addr, u8 major_revision)
+ __le32 __iomem *addr, u8 major_revision)
{
u32 temp, port_offset, port_count;
int i;
@@ -1755,7 +1755,7 @@ static void xhci_add_in_port(struct xhci_hcd *xhci, unsigned int num_ports,
*/
static int xhci_setup_port_arrays(struct xhci_hcd *xhci, gfp_t flags)
{
- u32 __iomem *addr;
+ __le32 __iomem *addr;
u32 offset;
unsigned int num_ports;
int i, port_index;
@@ -2008,8 +2008,8 @@ int xhci_mem_init(struct xhci_hcd *xhci, gfp_t flags)
/* set ring base address and size for each segment table entry */
for (val = 0, seg = xhci->event_ring->first_seg; val < ERST_NUM_SEGS; val++) {
struct xhci_erst_entry *entry = &xhci->erst.entries[val];
- entry->seg_addr = seg->dma;
- entry->seg_size = TRBS_PER_SEGMENT;
+ entry->seg_addr = cpu_to_le64(seg->dma);
+ entry->seg_size = cpu_to_le32(TRBS_PER_SEGMENT);
entry->rsvd = 0;
seg = seg->next;
}
diff --git a/drivers/usb/host/xhci-ring.c b/drivers/usb/host/xhci-ring.c
index cfc1ad9..1361032 100644
--- a/drivers/usb/host/xhci-ring.c
+++ b/drivers/usb/host/xhci-ring.c
@@ -100,7 +100,7 @@ static inline bool last_trb_on_last_seg(struct xhci_hcd *xhci, struct xhci_ring
return (trb == &seg->trbs[TRBS_PER_SEGMENT]) &&
(seg->next == xhci->event_ring->first_seg);
else
- return trb->link.control & LINK_TOGGLE;
+ return le32_to_cpu(trb->link.control) & LINK_TOGGLE;
}
/* Is this TRB a link TRB or was the last TRB the last TRB in this event ring
@@ -113,13 +113,15 @@ static inline int last_trb(struct xhci_hcd *xhci, struct xhci_ring *ring,
if (ring == xhci->event_ring)
return trb == &seg->trbs[TRBS_PER_SEGMENT];
else
- return (trb->link.control & TRB_TYPE_BITMASK) == TRB_TYPE(TRB_LINK);
+ return (le32_to_cpu(trb->link.control) & TRB_TYPE_BITMASK)
+ == TRB_TYPE(TRB_LINK);
}
static inline int enqueue_is_link_trb(struct xhci_ring *ring)
{
struct xhci_link_trb *link = &ring->enqueue->link;
- return ((link->control & TRB_TYPE_BITMASK) == TRB_TYPE(TRB_LINK));
+ return ((le32_to_cpu(link->control) & TRB_TYPE_BITMASK) ==
+ TRB_TYPE(TRB_LINK));
}
/* Updates trb to point to the next TRB in the ring, and updates seg if the next
@@ -197,7 +199,7 @@ static void inc_enq(struct xhci_hcd *xhci, struct xhci_ring *ring,
union xhci_trb *next;
unsigned long long addr;
- chain = ring->enqueue->generic.field[3] & TRB_CHAIN;
+ chain = le32_to_cpu(ring->enqueue->generic.field[3]) & TRB_CHAIN;
next = ++(ring->enqueue);
ring->enq_updates++;
@@ -223,12 +225,14 @@ static void inc_enq(struct xhci_hcd *xhci, struct xhci_ring *ring,
* (which may mean the chain bit is cleared).
*/
if (!xhci_link_trb_quirk(xhci)) {
- next->link.control &= ~TRB_CHAIN;
- next->link.control |= chain;
+ next->link.control &=
+ cpu_to_le32(~TRB_CHAIN);
+ next->link.control |=
+ cpu_to_le32(chain);
}
/* Give this link TRB to the hardware */
wmb();
- next->link.control ^= TRB_CYCLE;
+ next->link.control ^= cpu_to_le32(TRB_CYCLE);
}
/* Toggle the cycle bit after the last ring segment. */
if (last_trb_on_last_seg(xhci, ring, ring->enq_seg, next)) {
@@ -319,7 +323,7 @@ void xhci_ring_ep_doorbell(struct xhci_hcd *xhci,
unsigned int ep_index,
unsigned int stream_id)
{
- __u32 __iomem *db_addr = &xhci->dba->doorbell[slot_id];
+ __le32 __iomem *db_addr = &xhci->dba->doorbell[slot_id];
struct xhci_virt_ep *ep = &xhci->devs[slot_id]->eps[ep_index];
unsigned int ep_state = ep->ep_state;
@@ -380,7 +384,7 @@ static struct xhci_segment *find_trb_seg(
while (cur_seg->trbs > trb ||
&cur_seg->trbs[TRBS_PER_SEGMENT - 1] < trb) {
generic_trb = &cur_seg->trbs[TRBS_PER_SEGMENT - 1].generic;
- if (generic_trb->field[3] & LINK_TOGGLE)
+ if (le32_to_cpu(generic_trb->field[3]) & LINK_TOGGLE)
*cycle_state ^= 0x1;
cur_seg = cur_seg->next;
if (cur_seg == start_seg)
@@ -447,6 +451,10 @@ static struct xhci_ring *xhci_urb_to_transfer_ring(struct xhci_hcd *xhci,
* any link TRBs with the toggle cycle bit set.
* - Finally we move the dequeue state one TRB further, toggling the cycle bit
* if we've moved it past a link TRB with the toggle cycle bit set.
+ *
+ * Some of the uses of xhci_generic_trb are grotty, but if they're done
+ * with correct __le32 accesses they should work fine. Only users of this are
+ * in here.
*/
void xhci_find_new_dequeue_state(struct xhci_hcd *xhci,
unsigned int slot_id, unsigned int ep_index,
@@ -480,7 +488,7 @@ void xhci_find_new_dequeue_state(struct xhci_hcd *xhci,
/* Dig out the cycle state saved by the xHC during the stop ep cmd */
xhci_dbg(xhci, "Finding endpoint context\n");
ep_ctx = xhci_get_ep_ctx(xhci, dev->out_ctx, ep_index);
- state->new_cycle_state = 0x1 & ep_ctx->deq;
+ state->new_cycle_state = 0x1 & le64_to_cpu(ep_ctx->deq);
state->new_deq_ptr = cur_td->last_trb;
xhci_dbg(xhci, "Finding segment containing last TRB in TD.\n");
@@ -493,8 +501,8 @@ void xhci_find_new_dequeue_state(struct xhci_hcd *xhci,
}
trb = &state->new_deq_ptr->generic;
- if ((trb->field[3] & TRB_TYPE_BITMASK) == TRB_TYPE(TRB_LINK) &&
- (trb->field[3] & LINK_TOGGLE))
+ if ((le32_to_cpu(trb->field[3]) & TRB_TYPE_BITMASK) ==
+ TRB_TYPE(TRB_LINK) && (le32_to_cpu(trb->field[3]) & LINK_TOGGLE))
state->new_cycle_state ^= 0x1;
next_trb(xhci, ep_ring, &state->new_deq_seg, &state->new_deq_ptr);
@@ -529,12 +537,12 @@ static void td_to_noop(struct xhci_hcd *xhci, struct xhci_ring *ep_ring,
for (cur_seg = cur_td->start_seg, cur_trb = cur_td->first_trb;
true;
next_trb(xhci, ep_ring, &cur_seg, &cur_trb)) {
- if ((cur_trb->generic.field[3] & TRB_TYPE_BITMASK) ==
- TRB_TYPE(TRB_LINK)) {
+ if ((le32_to_cpu(cur_trb->generic.field[3]) & TRB_TYPE_BITMASK)
+ == TRB_TYPE(TRB_LINK)) {
/* Unchain any chained Link TRBs, but
* leave the pointers intact.
*/
- cur_trb->generic.field[3] &= ~TRB_CHAIN;
+ cur_trb->generic.field[3] &= cpu_to_le32(~TRB_CHAIN);
xhci_dbg(xhci, "Cancel (unchain) link TRB\n");
xhci_dbg(xhci, "Address = %p (0x%llx dma); "
"in seg %p (0x%llx dma)\n",
@@ -547,8 +555,9 @@ static void td_to_noop(struct xhci_hcd *xhci, struct xhci_ring *ep_ring,
cur_trb->generic.field[1] = 0;
cur_trb->generic.field[2] = 0;
/* Preserve only the cycle bit of this TRB */
- cur_trb->generic.field[3] &= TRB_CYCLE;
- cur_trb->generic.field[3] |= TRB_TYPE(TRB_TR_NOOP);
+ cur_trb->generic.field[3] &= cpu_to_le32(TRB_CYCLE);
+ cur_trb->generic.field[3] |= cpu_to_le32(
+ TRB_TYPE(TRB_TR_NOOP));
xhci_dbg(xhci, "Cancel TRB %p (0x%llx dma) "
"in seg %p (0x%llx dma)\n",
cur_trb,
@@ -655,9 +664,9 @@ static void handle_stopped_endpoint(struct xhci_hcd *xhci,
struct xhci_dequeue_state deq_state;
if (unlikely(TRB_TO_SUSPEND_PORT(
- xhci->cmd_ring->dequeue->generic.field[3]))) {
+ le32_to_cpu(xhci->cmd_ring->dequeue->generic.field[3])))) {
slot_id = TRB_TO_SLOT_ID(
- xhci->cmd_ring->dequeue->generic.field[3]);
+ le32_to_cpu(xhci->cmd_ring->dequeue->generic.field[3]));
virt_dev = xhci->devs[slot_id];
if (virt_dev)
handle_cmd_in_cmd_wait_list(xhci, virt_dev,
@@ -670,8 +679,8 @@ static void handle_stopped_endpoint(struct xhci_hcd *xhci,
}
memset(&deq_state, 0, sizeof(deq_state));
- slot_id = TRB_TO_SLOT_ID(trb->generic.field[3]);
- ep_index = TRB_TO_EP_INDEX(trb->generic.field[3]);
+ slot_id = TRB_TO_SLOT_ID(le32_to_cpu(trb->generic.field[3]));
+ ep_index = TRB_TO_EP_INDEX(le32_to_cpu(trb->generic.field[3]));
ep = &xhci->devs[slot_id]->eps[ep_index];
if (list_empty(&ep->cancelled_td_list)) {
@@ -903,9 +912,9 @@ static void handle_set_deq_completion(struct xhci_hcd *xhci,
struct xhci_ep_ctx *ep_ctx;
struct xhci_slot_ctx *slot_ctx;
- slot_id = TRB_TO_SLOT_ID(trb->generic.field[3]);
- ep_index = TRB_TO_EP_INDEX(trb->generic.field[3]);
- stream_id = TRB_TO_STREAM_ID(trb->generic.field[2]);
+ slot_id = TRB_TO_SLOT_ID(le32_to_cpu(trb->generic.field[3]));
+ ep_index = TRB_TO_EP_INDEX(le32_to_cpu(trb->generic.field[3]));
+ stream_id = TRB_TO_STREAM_ID(le32_to_cpu(trb->generic.field[2]));
dev = xhci->devs[slot_id];
ep_ring = xhci_stream_id_to_ring(dev, ep_index, stream_id);
@@ -921,11 +930,11 @@ static void handle_set_deq_completion(struct xhci_hcd *xhci,
ep_ctx = xhci_get_ep_ctx(xhci, dev->out_ctx, ep_index);
slot_ctx = xhci_get_slot_ctx(xhci, dev->out_ctx);
- if (GET_COMP_CODE(event->status) != COMP_SUCCESS) {
+ if (GET_COMP_CODE(le32_to_cpu(event->status)) != COMP_SUCCESS) {
unsigned int ep_state;
unsigned int slot_state;
- switch (GET_COMP_CODE(event->status)) {
+ switch (GET_COMP_CODE(le32_to_cpu(event->status))) {
case COMP_TRB_ERR:
xhci_warn(xhci, "WARN Set TR Deq Ptr cmd invalid because "
"of stream ID configuration\n");
@@ -933,9 +942,9 @@ static void handle_set_deq_completion(struct xhci_hcd *xhci,
case COMP_CTX_STATE:
xhci_warn(xhci, "WARN Set TR Deq Ptr cmd failed due "
"to incorrect slot or ep state.\n");
- ep_state = ep_ctx->ep_info;
+ ep_state = le32_to_cpu(ep_ctx->ep_info);
ep_state &= EP_STATE_MASK;
- slot_state = slot_ctx->dev_state;
+ slot_state = le32_to_cpu(slot_ctx->dev_state);
slot_state = GET_SLOT_STATE(slot_state);
xhci_dbg(xhci, "Slot state = %u, EP state = %u\n",
slot_state, ep_state);
@@ -947,7 +956,7 @@ static void handle_set_deq_completion(struct xhci_hcd *xhci,
default:
xhci_warn(xhci, "WARN Set TR Deq Ptr cmd with unknown "
"completion code of %u.\n",
- GET_COMP_CODE(event->status));
+ GET_COMP_CODE(le32_to_cpu(event->status)));
break;
}
/* OK what do we do now? The endpoint state is hosed, and we
@@ -958,10 +967,10 @@ static void handle_set_deq_completion(struct xhci_hcd *xhci,
*/
} else {
xhci_dbg(xhci, "Successful Set TR Deq Ptr cmd, deq = @%08llx\n",
- ep_ctx->deq);
+ le64_to_cpu(ep_ctx->deq));
if (xhci_trb_virt_to_dma(dev->eps[ep_index].queued_deq_seg,
- dev->eps[ep_index].queued_deq_ptr) ==
- (ep_ctx->deq & ~(EP_CTX_CYCLE_MASK))) {
+ dev->eps[ep_index].queued_deq_ptr) ==
+ (le64_to_cpu(ep_ctx->deq) & ~(EP_CTX_CYCLE_MASK))) {
/* Update the ring's dequeue segment and dequeue pointer
* to reflect the new position.
*/
@@ -990,13 +999,13 @@ static void handle_reset_ep_completion(struct xhci_hcd *xhci,
int slot_id;
unsigned int ep_index;
- slot_id = TRB_TO_SLOT_ID(trb->generic.field[3]);
- ep_index = TRB_TO_EP_INDEX(trb->generic.field[3]);
+ slot_id = TRB_TO_SLOT_ID(le32_to_cpu(trb->generic.field[3]));
+ ep_index = TRB_TO_EP_INDEX(le32_to_cpu(trb->generic.field[3]));
/* This command will only fail if the endpoint wasn't halted,
* but we don't care.
*/
xhci_dbg(xhci, "Ignoring reset ep completion code of %u\n",
- (unsigned int) GET_COMP_CODE(event->status));
+ (unsigned int) GET_COMP_CODE(le32_to_cpu(event->status)));
/* HW with the reset endpoint quirk needs to have a configure endpoint
* command complete before the endpoint can be used. Queue that here
@@ -1033,8 +1042,7 @@ static int handle_cmd_in_cmd_wait_list(struct xhci_hcd *xhci,
if (xhci->cmd_ring->dequeue != command->command_trb)
return 0;
- command->status =
- GET_COMP_CODE(event->status);
+ command->status = GET_COMP_CODE(le32_to_cpu(event->status));
list_del(&command->cmd_list);
if (command->completion)
complete(command->completion);
@@ -1046,7 +1054,7 @@ static int handle_cmd_in_cmd_wait_list(struct xhci_hcd *xhci,
static void handle_cmd_completion(struct xhci_hcd *xhci,
struct xhci_event_cmd *event)
{
- int slot_id = TRB_TO_SLOT_ID(event->flags);
+ int slot_id = TRB_TO_SLOT_ID(le32_to_cpu(event->flags));
u64 cmd_dma;
dma_addr_t cmd_dequeue_dma;
struct xhci_input_control_ctx *ctrl_ctx;
@@ -1055,7 +1063,7 @@ static void handle_cmd_completion(struct xhci_hcd *xhci,
struct xhci_ring *ep_ring;
unsigned int ep_state;
- cmd_dma = event->cmd_trb;
+ cmd_dma = le64_to_cpu(event->cmd_trb);
cmd_dequeue_dma = xhci_trb_virt_to_dma(xhci->cmd_ring->deq_seg,
xhci->cmd_ring->dequeue);
/* Is the command ring deq ptr out of sync with the deq seg ptr? */
@@ -1068,9 +1076,10 @@ static void handle_cmd_completion(struct xhci_hcd *xhci,
xhci->error_bitmask |= 1 << 5;
return;
}
- switch (xhci->cmd_ring->dequeue->generic.field[3] & TRB_TYPE_BITMASK) {
+ switch (le32_to_cpu(xhci->cmd_ring->dequeue->generic.field[3])
+ & TRB_TYPE_BITMASK) {
case TRB_TYPE(TRB_ENABLE_SLOT):
- if (GET_COMP_CODE(event->status) == COMP_SUCCESS)
+ if (GET_COMP_CODE(le32_to_cpu(event->status)) == COMP_SUCCESS)
xhci->slot_id = slot_id;
else
xhci->slot_id = 0;
@@ -1095,7 +1104,7 @@ static void handle_cmd_completion(struct xhci_hcd *xhci,
ctrl_ctx = xhci_get_input_control_ctx(xhci,
virt_dev->in_ctx);
/* Input ctx add_flags are the endpoint index plus one */
- ep_index = xhci_last_valid_endpoint(ctrl_ctx->add_flags) - 1;
+ ep_index = xhci_last_valid_endpoint(le32_to_cpu(ctrl_ctx->add_flags)) - 1;
/* A usb_set_interface() call directly after clearing a halted
* condition may race on this quirky hardware. Not worth
* worrying about, since this is prototype hardware. Not sure
@@ -1104,8 +1113,8 @@ static void handle_cmd_completion(struct xhci_hcd *xhci,
*/
if (xhci->quirks & XHCI_RESET_EP_QUIRK &&
ep_index != (unsigned int) -1 &&
- ctrl_ctx->add_flags - SLOT_FLAG ==
- ctrl_ctx->drop_flags) {
+ le32_to_cpu(ctrl_ctx->add_flags) - SLOT_FLAG ==
+ le32_to_cpu(ctrl_ctx->drop_flags)) {
ep_ring = xhci->devs[slot_id]->eps[ep_index].ring;
ep_state = xhci->devs[slot_id]->eps[ep_index].ep_state;
if (!(ep_state & EP_HALTED))
@@ -1122,18 +1131,18 @@ static void handle_cmd_completion(struct xhci_hcd *xhci,
bandwidth_change:
xhci_dbg(xhci, "Completed config ep cmd\n");
xhci->devs[slot_id]->cmd_status =
- GET_COMP_CODE(event->status);
+ GET_COMP_CODE(le32_to_cpu(event->status));
complete(&xhci->devs[slot_id]->cmd_completion);
break;
case TRB_TYPE(TRB_EVAL_CONTEXT):
virt_dev = xhci->devs[slot_id];
if (handle_cmd_in_cmd_wait_list(xhci, virt_dev, event))
break;
- xhci->devs[slot_id]->cmd_status = GET_COMP_CODE(event->status);
+ xhci->devs[slot_id]->cmd_status = GET_COMP_CODE(le32_to_cpu(event->status));
complete(&xhci->devs[slot_id]->cmd_completion);
break;
case TRB_TYPE(TRB_ADDR_DEV):
- xhci->devs[slot_id]->cmd_status = GET_COMP_CODE(event->status);
+ xhci->devs[slot_id]->cmd_status = GET_COMP_CODE(le32_to_cpu(event->status));
complete(&xhci->addr_dev);
break;
case TRB_TYPE(TRB_STOP_RING):
@@ -1150,7 +1159,7 @@ bandwidth_change:
case TRB_TYPE(TRB_RESET_DEV):
xhci_dbg(xhci, "Completed reset device command.\n");
slot_id = TRB_TO_SLOT_ID(
- xhci->cmd_ring->dequeue->generic.field[3]);
+ le32_to_cpu(xhci->cmd_ring->dequeue->generic.field[3]));
virt_dev = xhci->devs[slot_id];
if (virt_dev)
handle_cmd_in_cmd_wait_list(xhci, virt_dev, event);
@@ -1164,8 +1173,8 @@ bandwidth_change:
break;
}
xhci_dbg(xhci, "NEC firmware version %2x.%02x\n",
- NEC_FW_MAJOR(event->status),
- NEC_FW_MINOR(event->status));
+ NEC_FW_MAJOR(le32_to_cpu(event->status)),
+ NEC_FW_MINOR(le32_to_cpu(event->status)));
break;
default:
/* Skip over unknown commands on the event ring */
@@ -1180,7 +1189,7 @@ static void handle_vendor_event(struct xhci_hcd *xhci,
{
u32 trb_type;
- trb_type = TRB_FIELD_TO_TYPE(event->generic.field[3]);
+ trb_type = TRB_FIELD_TO_TYPE(le32_to_cpu(event->generic.field[3]));
xhci_dbg(xhci, "Vendor specific event TRB type = %u\n", trb_type);
if (trb_type == TRB_NEC_CMD_COMP && (xhci->quirks & XHCI_NEC_HOST))
handle_cmd_completion(xhci, &event->event_cmd);
@@ -1234,14 +1243,14 @@ static void handle_port_status(struct xhci_hcd *xhci,
unsigned int faked_port_index;
u8 major_revision;
struct xhci_bus_state *bus_state;
- u32 __iomem **port_array;
+ __le32 __iomem **port_array;
/* Port status change events always have a successful completion code */
- if (GET_COMP_CODE(event->generic.field[2]) != COMP_SUCCESS) {
+ if (GET_COMP_CODE(le32_to_cpu(event->generic.field[2])) != COMP_SUCCESS) {
xhci_warn(xhci, "WARN: xHC returned failed port status event\n");
xhci->error_bitmask |= 1 << 8;
}
- port_id = GET_PORT_ID(event->generic.field[0]);
+ port_id = GET_PORT_ID(le32_to_cpu(event->generic.field[0]));
xhci_dbg(xhci, "Port Status Change Event for port %d\n", port_id);
max_ports = HCS_MAX_PORTS(xhci->hcs_params1);
@@ -1438,7 +1447,7 @@ static int xhci_requires_manual_halt_cleanup(struct xhci_hcd *xhci,
* endpoint anyway. Check if a babble halted the
* endpoint.
*/
- if ((ep_ctx->ep_info & EP_STATE_MASK) == EP_STATE_HALTED)
+ if ((le32_to_cpu(ep_ctx->ep_info) & EP_STATE_MASK) == EP_STATE_HALTED)
return 1;
return 0;
@@ -1476,12 +1485,12 @@ static int finish_td(struct xhci_hcd *xhci, struct xhci_td *td,
struct urb_priv *urb_priv;
u32 trb_comp_code;
- slot_id = TRB_TO_SLOT_ID(event->flags);
+ slot_id = TRB_TO_SLOT_ID(le32_to_cpu(event->flags));
xdev = xhci->devs[slot_id];
- ep_index = TRB_TO_EP_ID(event->flags) - 1;
- ep_ring = xhci_dma_to_transfer_ring(ep, event->buffer);
+ ep_index = TRB_TO_EP_ID(le32_to_cpu(event->flags)) - 1;
+ ep_ring = xhci_dma_to_transfer_ring(ep, le64_to_cpu(event->buffer));
ep_ctx = xhci_get_ep_ctx(xhci, xdev->out_ctx, ep_index);
- trb_comp_code = GET_COMP_CODE(event->transfer_len);
+ trb_comp_code = GET_COMP_CODE(le32_to_cpu(event->transfer_len));
if (skip)
goto td_cleanup;
@@ -1575,12 +1584,12 @@ static int process_ctrl_td(struct xhci_hcd *xhci, struct xhci_td *td,
struct xhci_ep_ctx *ep_ctx;
u32 trb_comp_code;
- slot_id = TRB_TO_SLOT_ID(event->flags);
+ slot_id = TRB_TO_SLOT_ID(le32_to_cpu(event->flags));
xdev = xhci->devs[slot_id];
- ep_index = TRB_TO_EP_ID(event->flags) - 1;
- ep_ring = xhci_dma_to_transfer_ring(ep, event->buffer);
+ ep_index = TRB_TO_EP_ID(le32_to_cpu(event->flags)) - 1;
+ ep_ring = xhci_dma_to_transfer_ring(ep, le64_to_cpu(event->buffer));
ep_ctx = xhci_get_ep_ctx(xhci, xdev->out_ctx, ep_index);
- trb_comp_code = GET_COMP_CODE(event->transfer_len);
+ trb_comp_code = GET_COMP_CODE(le32_to_cpu(event->transfer_len));
xhci_debug_trb(xhci, xhci->event_ring->dequeue);
switch (trb_comp_code) {
@@ -1619,7 +1628,7 @@ static int process_ctrl_td(struct xhci_hcd *xhci, struct xhci_td *td,
event_trb != td->last_trb)
td->urb->actual_length =
td->urb->transfer_buffer_length
- - TRB_LEN(event->transfer_len);
+ - TRB_LEN(le32_to_cpu(event->transfer_len));
else
td->urb->actual_length = 0;
@@ -1653,7 +1662,7 @@ static int process_ctrl_td(struct xhci_hcd *xhci, struct xhci_td *td,
/* We didn't stop on a link TRB in the middle */
td->urb->actual_length =
td->urb->transfer_buffer_length -
- TRB_LEN(event->transfer_len);
+ TRB_LEN(le32_to_cpu(event->transfer_len));
xhci_dbg(xhci, "Waiting for status "
"stage event\n");
return 0;
@@ -1680,8 +1689,8 @@ static int process_isoc_td(struct xhci_hcd *xhci, struct xhci_td *td,
struct xhci_segment *cur_seg;
u32 trb_comp_code;
- ep_ring = xhci_dma_to_transfer_ring(ep, event->buffer);
- trb_comp_code = GET_COMP_CODE(event->transfer_len);
+ ep_ring = xhci_dma_to_transfer_ring(ep, le64_to_cpu(event->buffer));
+ trb_comp_code = GET_COMP_CODE(le32_to_cpu(event->transfer_len));
urb_priv = td->urb->hcpriv;
idx = urb_priv->td_cnt;
@@ -1744,15 +1753,14 @@ static int process_isoc_td(struct xhci_hcd *xhci, struct xhci_td *td,
for (cur_trb = ep_ring->dequeue,
cur_seg = ep_ring->deq_seg; cur_trb != event_trb;
next_trb(xhci, ep_ring, &cur_seg, &cur_trb)) {
- if ((cur_trb->generic.field[3] &
+ if ((le32_to_cpu(cur_trb->generic.field[3]) &
TRB_TYPE_BITMASK) != TRB_TYPE(TRB_TR_NOOP) &&
- (cur_trb->generic.field[3] &
+ (le32_to_cpu(cur_trb->generic.field[3]) &
TRB_TYPE_BITMASK) != TRB_TYPE(TRB_LINK))
- len +=
- TRB_LEN(cur_trb->generic.field[2]);
+ len += TRB_LEN(le32_to_cpu(cur_trb->generic.field[2]));
}
- len += TRB_LEN(cur_trb->generic.field[2]) -
- TRB_LEN(event->transfer_len);
+ len += TRB_LEN(le32_to_cpu(cur_trb->generic.field[2])) -
+ TRB_LEN(le32_to_cpu(event->transfer_len));
if (trb_comp_code != COMP_STOP_INVAL) {
td->urb->iso_frame_desc[idx].actual_length = len;
@@ -1778,8 +1786,8 @@ static int process_bulk_intr_td(struct xhci_hcd *xhci, struct xhci_td *td,
struct xhci_segment *cur_seg;
u32 trb_comp_code;
- ep_ring = xhci_dma_to_transfer_ring(ep, event->buffer);
- trb_comp_code = GET_COMP_CODE(event->transfer_len);
+ ep_ring = xhci_dma_to_transfer_ring(ep, le64_to_cpu(event->buffer));
+ trb_comp_code = GET_COMP_CODE(le32_to_cpu(event->transfer_len));
switch (trb_comp_code) {
case COMP_SUCCESS:
@@ -1815,18 +1823,18 @@ static int process_bulk_intr_td(struct xhci_hcd *xhci, struct xhci_td *td,
"%d bytes untransferred\n",
td->urb->ep->desc.bEndpointAddress,
td->urb->transfer_buffer_length,
- TRB_LEN(event->transfer_len));
+ TRB_LEN(le32_to_cpu(event->transfer_len)));
/* Fast path - was this the last TRB in the TD for this URB? */
if (event_trb == td->last_trb) {
- if (TRB_LEN(event->transfer_len) != 0) {
+ if (TRB_LEN(le32_to_cpu(event->transfer_len)) != 0) {
td->urb->actual_length =
td->urb->transfer_buffer_length -
- TRB_LEN(event->transfer_len);
+ TRB_LEN(le32_to_cpu(event->transfer_len));
if (td->urb->transfer_buffer_length <
td->urb->actual_length) {
xhci_warn(xhci, "HC gave bad length "
"of %d bytes left\n",
- TRB_LEN(event->transfer_len));
+ TRB_LEN(le32_to_cpu(event->transfer_len)));
td->urb->actual_length = 0;
if (td->urb->transfer_flags & URB_SHORT_NOT_OK)
*status = -EREMOTEIO;
@@ -1857,20 +1865,20 @@ static int process_bulk_intr_td(struct xhci_hcd *xhci, struct xhci_td *td,
for (cur_trb = ep_ring->dequeue, cur_seg = ep_ring->deq_seg;
cur_trb != event_trb;
next_trb(xhci, ep_ring, &cur_seg, &cur_trb)) {
- if ((cur_trb->generic.field[3] &
+ if ((le32_to_cpu(cur_trb->generic.field[3]) &
TRB_TYPE_BITMASK) != TRB_TYPE(TRB_TR_NOOP) &&
- (cur_trb->generic.field[3] &
+ (le32_to_cpu(cur_trb->generic.field[3]) &
TRB_TYPE_BITMASK) != TRB_TYPE(TRB_LINK))
td->urb->actual_length +=
- TRB_LEN(cur_trb->generic.field[2]);
+ TRB_LEN(le32_to_cpu(cur_trb->generic.field[2]));
}
/* If the ring didn't stop on a Link or No-op TRB, add
* in the actual bytes transferred from the Normal TRB
*/
if (trb_comp_code != COMP_STOP_INVAL)
td->urb->actual_length +=
- TRB_LEN(cur_trb->generic.field[2]) -
- TRB_LEN(event->transfer_len);
+ TRB_LEN(le32_to_cpu(cur_trb->generic.field[2])) -
+ TRB_LEN(le32_to_cpu(event->transfer_len));
}
return finish_td(xhci, td, event_trb, event, ep, status, false);
@@ -1900,7 +1908,7 @@ static int handle_tx_event(struct xhci_hcd *xhci,
u32 trb_comp_code;
int ret = 0;
- slot_id = TRB_TO_SLOT_ID(event->flags);
+ slot_id = TRB_TO_SLOT_ID(le32_to_cpu(event->flags));
xdev = xhci->devs[slot_id];
if (!xdev) {
xhci_err(xhci, "ERROR Transfer event pointed to bad slot\n");
@@ -1908,20 +1916,21 @@ static int handle_tx_event(struct xhci_hcd *xhci,
}
/* Endpoint ID is 1 based, our index is zero based */
- ep_index = TRB_TO_EP_ID(event->flags) - 1;
+ ep_index = TRB_TO_EP_ID(le32_to_cpu(event->flags)) - 1;
xhci_dbg(xhci, "%s - ep index = %d\n", __func__, ep_index);
ep = &xdev->eps[ep_index];
- ep_ring = xhci_dma_to_transfer_ring(ep, event->buffer);
+ ep_ring = xhci_dma_to_transfer_ring(ep, le64_to_cpu(event->buffer));
ep_ctx = xhci_get_ep_ctx(xhci, xdev->out_ctx, ep_index);
if (!ep_ring ||
- (ep_ctx->ep_info & EP_STATE_MASK) == EP_STATE_DISABLED) {
+ (le32_to_cpu(ep_ctx->ep_info) & EP_STATE_MASK) ==
+ EP_STATE_DISABLED) {
xhci_err(xhci, "ERROR Transfer event for disabled endpoint "
"or incorrect stream ring\n");
return -ENODEV;
}
- event_dma = event->buffer;
- trb_comp_code = GET_COMP_CODE(event->transfer_len);
+ event_dma = le64_to_cpu(event->buffer);
+ trb_comp_code = GET_COMP_CODE(le32_to_cpu(event->transfer_len));
/* Look for common error cases */
switch (trb_comp_code) {
/* Skip codes that require special handling depending on
@@ -1974,14 +1983,16 @@ static int handle_tx_event(struct xhci_hcd *xhci,
if (!list_empty(&ep_ring->td_list))
xhci_dbg(xhci, "Underrun Event for slot %d ep %d "
"still with TDs queued?\n",
- TRB_TO_SLOT_ID(event->flags), ep_index);
+ TRB_TO_SLOT_ID(le32_to_cpu(event->flags)),
+ ep_index);
goto cleanup;
case COMP_OVERRUN:
xhci_dbg(xhci, "overrun event on endpoint\n");
if (!list_empty(&ep_ring->td_list))
xhci_dbg(xhci, "Overrun Event for slot %d ep %d "
"still with TDs queued?\n",
- TRB_TO_SLOT_ID(event->flags), ep_index);
+ TRB_TO_SLOT_ID(le32_to_cpu(event->flags)),
+ ep_index);
goto cleanup;
case COMP_MISSED_INT:
/*
@@ -2010,9 +2021,11 @@ static int handle_tx_event(struct xhci_hcd *xhci,
if (list_empty(&ep_ring->td_list)) {
xhci_warn(xhci, "WARN Event TRB for slot %d ep %d "
"with no TDs queued?\n",
- TRB_TO_SLOT_ID(event->flags), ep_index);
+ TRB_TO_SLOT_ID(le32_to_cpu(event->flags)),
+ ep_index);
xhci_dbg(xhci, "Event TRB with TRB type ID %u\n",
- (unsigned int) (event->flags & TRB_TYPE_BITMASK)>>10);
+ (unsigned int) (le32_to_cpu(event->flags)
+ & TRB_TYPE_BITMASK)>>10);
xhci_print_trb_offsets(xhci, (union xhci_trb *) event);
if (ep->skip) {
ep->skip = false;
@@ -2048,7 +2061,8 @@ static int handle_tx_event(struct xhci_hcd *xhci,
* corresponding TD has been cancelled. Just ignore
* the TD.
*/
- if ((event_trb->generic.field[3] & TRB_TYPE_BITMASK)
+ if ((le32_to_cpu(event_trb->generic.field[3])
+ & TRB_TYPE_BITMASK)
== TRB_TYPE(TRB_TR_NOOP)) {
xhci_dbg(xhci, "event_trb is a no-op TRB. "
"Skip it\n");
@@ -2129,15 +2143,15 @@ static void xhci_handle_event(struct xhci_hcd *xhci)
event = xhci->event_ring->dequeue;
/* Does the HC or OS own the TRB? */
- if ((event->event_cmd.flags & TRB_CYCLE) !=
- xhci->event_ring->cycle_state) {
+ if ((le32_to_cpu(event->event_cmd.flags) & TRB_CYCLE) !=
+ xhci->event_ring->cycle_state) {
xhci->error_bitmask |= 1 << 2;
return;
}
xhci_dbg(xhci, "%s - OS owns TRB\n", __func__);
/* FIXME: Handle more event types. */
- switch ((event->event_cmd.flags & TRB_TYPE_BITMASK)) {
+ switch ((le32_to_cpu(event->event_cmd.flags) & TRB_TYPE_BITMASK)) {
case TRB_TYPE(TRB_COMPLETION):
xhci_dbg(xhci, "%s - calling handle_cmd_completion\n", __func__);
handle_cmd_completion(xhci, &event->event_cmd);
@@ -2159,7 +2173,8 @@ static void xhci_handle_event(struct xhci_hcd *xhci)
update_ptrs = 0;
break;
default:
- if ((event->event_cmd.flags & TRB_TYPE_BITMASK) >= TRB_TYPE(48))
+ if ((le32_to_cpu(event->event_cmd.flags) & TRB_TYPE_BITMASK) >=
+ TRB_TYPE(48))
handle_vendor_event(xhci, event);
else
xhci->error_bitmask |= 1 << 3;
@@ -2209,12 +2224,12 @@ irqreturn_t xhci_irq(struct usb_hcd *hcd)
xhci_dbg(xhci, "op reg status = %08x\n", status);
xhci_dbg(xhci, "Event ring dequeue ptr:\n");
xhci_dbg(xhci, "@%llx %08x %08x %08x %08x\n",
- (unsigned long long)
- xhci_trb_virt_to_dma(xhci->event_ring->deq_seg, trb),
- lower_32_bits(trb->link.segment_ptr),
- upper_32_bits(trb->link.segment_ptr),
- (unsigned int) trb->link.intr_target,
- (unsigned int) trb->link.control);
+ (unsigned long long)
+ xhci_trb_virt_to_dma(xhci->event_ring->deq_seg, trb),
+ lower_32_bits(le64_to_cpu(trb->link.segment_ptr)),
+ upper_32_bits(le64_to_cpu(trb->link.segment_ptr)),
+ (unsigned int) le32_to_cpu(trb->link.intr_target),
+ (unsigned int) le32_to_cpu(trb->link.control));
if (status & STS_FATAL) {
xhci_warn(xhci, "WARNING: Host System Error\n");
@@ -2315,10 +2330,10 @@ static void queue_trb(struct xhci_hcd *xhci, struct xhci_ring *ring,
struct xhci_generic_trb *trb;
trb = &ring->enqueue->generic;
- trb->field[0] = field1;
- trb->field[1] = field2;
- trb->field[2] = field3;
- trb->field[3] = field4;
+ trb->field[0] = cpu_to_le32(field1);
+ trb->field[1] = cpu_to_le32(field2);
+ trb->field[2] = cpu_to_le32(field3);
+ trb->field[3] = cpu_to_le32(field4);
inc_enq(xhci, ring, consumer, more_trbs_coming);
}
@@ -2371,17 +2386,16 @@ static int prepare_ring(struct xhci_hcd *xhci, struct xhci_ring *ep_ring,
next = ring->enqueue;
while (last_trb(xhci, ring, ring->enq_seg, next)) {
-
/* If we're not dealing with 0.95 hardware,
* clear the chain bit.
*/
if (!xhci_link_trb_quirk(xhci))
- next->link.control &= ~TRB_CHAIN;
+ next->link.control &= cpu_to_le32(~TRB_CHAIN);
else
- next->link.control |= TRB_CHAIN;
+ next->link.control |= cpu_to_le32(TRB_CHAIN);
wmb();
- next->link.control ^= (u32) TRB_CYCLE;
+ next->link.control ^= cpu_to_le32((u32) TRB_CYCLE);
/* Toggle the cycle bit after the last ring segment. */
if (last_trb_on_last_seg(xhci, ring, ring->enq_seg, next)) {
@@ -2424,8 +2438,8 @@ static int prepare_transfer(struct xhci_hcd *xhci,
}
ret = prepare_ring(xhci, ep_ring,
- ep_ctx->ep_info & EP_STATE_MASK,
- num_trbs, mem_flags);
+ le32_to_cpu(ep_ctx->ep_info) & EP_STATE_MASK,
+ num_trbs, mem_flags);
if (ret)
return ret;
@@ -2527,9 +2541,9 @@ static void giveback_first_trb(struct xhci_hcd *xhci, int slot_id,
*/
wmb();
if (start_cycle)
- start_trb->field[3] |= start_cycle;
+ start_trb->field[3] |= cpu_to_le32(start_cycle);
else
- start_trb->field[3] &= ~0x1;
+ start_trb->field[3] &= cpu_to_le32(~TRB_CYCLE);
xhci_ring_ep_doorbell(xhci, slot_id, ep_index, stream_id);
}
@@ -2547,7 +2561,7 @@ int xhci_queue_intr_tx(struct xhci_hcd *xhci, gfp_t mem_flags,
int xhci_interval;
int ep_interval;
- xhci_interval = EP_INTERVAL_TO_UFRAMES(ep_ctx->ep_info);
+ xhci_interval = EP_INTERVAL_TO_UFRAMES(le32_to_cpu(ep_ctx->ep_info));
ep_interval = urb->interval;
/* Convert to microframes */
if (urb->dev->speed == USB_SPEED_LOW ||
@@ -2936,12 +2950,11 @@ int xhci_queue_ctrl_tx(struct xhci_hcd *xhci, gfp_t mem_flags,
if (start_cycle == 0)
field |= 0x1;
queue_trb(xhci, ep_ring, false, true,
- /* FIXME endianness is probably going to bite my ass here. */
- setup->bRequestType | setup->bRequest << 8 | setup->wValue << 16,
- setup->wIndex | setup->wLength << 16,
- TRB_LEN(8) | TRB_INTR_TARGET(0),
- /* Immediate data in pointer */
- field);
+ setup->bRequestType | setup->bRequest << 8 | le16_to_cpu(setup->wValue) << 16,
+ le16_to_cpu(setup->wIndex) | le16_to_cpu(setup->wLength) << 16,
+ TRB_LEN(8) | TRB_INTR_TARGET(0),
+ /* Immediate data in pointer */
+ field);
/* If there's data, queue data TRBs */
field = 0;
@@ -3162,8 +3175,8 @@ int xhci_queue_isoc_tx_prepare(struct xhci_hcd *xhci, gfp_t mem_flags,
/* Check the ring to guarantee there is enough room for the whole urb.
* Do not insert any td of the urb to the ring if the check failed.
*/
- ret = prepare_ring(xhci, ep_ring, ep_ctx->ep_info & EP_STATE_MASK,
- num_trbs, mem_flags);
+ ret = prepare_ring(xhci, ep_ring, le32_to_cpu(ep_ctx->ep_info) & EP_STATE_MASK,
+ num_trbs, mem_flags);
if (ret)
return ret;
@@ -3175,7 +3188,7 @@ int xhci_queue_isoc_tx_prepare(struct xhci_hcd *xhci, gfp_t mem_flags,
urb->dev->speed == USB_SPEED_FULL)
urb->start_frame >>= 3;
- xhci_interval = EP_INTERVAL_TO_UFRAMES(ep_ctx->ep_info);
+ xhci_interval = EP_INTERVAL_TO_UFRAMES(le32_to_cpu(ep_ctx->ep_info));
ep_interval = urb->interval;
/* Convert to microframes */
if (urb->dev->speed == USB_SPEED_LOW ||
diff --git a/drivers/usb/host/xhci.c b/drivers/usb/host/xhci.c
index 9a3645f..3a9f931 100644
--- a/drivers/usb/host/xhci.c
+++ b/drivers/usb/host/xhci.c
@@ -968,8 +968,8 @@ static int xhci_check_maxpacket(struct xhci_hcd *xhci, unsigned int slot_id,
out_ctx = xhci->devs[slot_id]->out_ctx;
ep_ctx = xhci_get_ep_ctx(xhci, out_ctx, ep_index);
- hw_max_packet_size = MAX_PACKET_DECODED(ep_ctx->ep_info2);
- max_packet_size = urb->dev->ep0.desc.wMaxPacketSize;
+ hw_max_packet_size = MAX_PACKET_DECODED(le32_to_cpu(ep_ctx->ep_info2));
+ max_packet_size = le16_to_cpu(urb->dev->ep0.desc.wMaxPacketSize);
if (hw_max_packet_size != max_packet_size) {
xhci_dbg(xhci, "Max Packet Size for ep 0 changed.\n");
xhci_dbg(xhci, "Max packet size in usb_device = %d\n",
@@ -983,15 +983,15 @@ static int xhci_check_maxpacket(struct xhci_hcd *xhci, unsigned int slot_id,
xhci->devs[slot_id]->out_ctx, ep_index);
in_ctx = xhci->devs[slot_id]->in_ctx;
ep_ctx = xhci_get_ep_ctx(xhci, in_ctx, ep_index);
- ep_ctx->ep_info2 &= ~MAX_PACKET_MASK;
- ep_ctx->ep_info2 |= MAX_PACKET(max_packet_size);
+ ep_ctx->ep_info2 &= cpu_to_le32(~MAX_PACKET_MASK);
+ ep_ctx->ep_info2 |= cpu_to_le32(MAX_PACKET(max_packet_size));
/* Set up the input context flags for the command */
/* FIXME: This won't work if a non-default control endpoint
* changes max packet sizes.
*/
ctrl_ctx = xhci_get_input_control_ctx(xhci, in_ctx);
- ctrl_ctx->add_flags = EP0_FLAG;
+ ctrl_ctx->add_flags = cpu_to_le32(EP0_FLAG);
ctrl_ctx->drop_flags = 0;
xhci_dbg(xhci, "Slot %d input context\n", slot_id);
@@ -1005,7 +1005,7 @@ static int xhci_check_maxpacket(struct xhci_hcd *xhci, unsigned int slot_id,
/* Clean up the input context for later use by bandwidth
* functions.
*/
- ctrl_ctx->add_flags = SLOT_FLAG;
+ ctrl_ctx->add_flags = cpu_to_le32(SLOT_FLAG);
}
return ret;
}
@@ -1326,27 +1326,30 @@ int xhci_drop_endpoint(struct usb_hcd *hcd, struct usb_device *udev,
/* If the HC already knows the endpoint is disabled,
* or the HCD has noted it is disabled, ignore this request
*/
- if ((ep_ctx->ep_info & EP_STATE_MASK) == EP_STATE_DISABLED ||
- ctrl_ctx->drop_flags & xhci_get_endpoint_flag(&ep->desc)) {
+ if ((le32_to_cpu(ep_ctx->ep_info) & EP_STATE_MASK) ==
+ EP_STATE_DISABLED ||
+ le32_to_cpu(ctrl_ctx->drop_flags) &
+ xhci_get_endpoint_flag(&ep->desc)) {
xhci_warn(xhci, "xHCI %s called with disabled ep %p\n",
__func__, ep);
return 0;
}
- ctrl_ctx->drop_flags |= drop_flag;
- new_drop_flags = ctrl_ctx->drop_flags;
+ ctrl_ctx->drop_flags |= cpu_to_le32(drop_flag);
+ new_drop_flags = le32_to_cpu(ctrl_ctx->drop_flags);
- ctrl_ctx->add_flags &= ~drop_flag;
- new_add_flags = ctrl_ctx->add_flags;
+ ctrl_ctx->add_flags &= cpu_to_le32(~drop_flag);
+ new_add_flags = le32_to_cpu(ctrl_ctx->add_flags);
- last_ctx = xhci_last_valid_endpoint(ctrl_ctx->add_flags);
+ last_ctx = xhci_last_valid_endpoint(le32_to_cpu(ctrl_ctx->add_flags));
slot_ctx = xhci_get_slot_ctx(xhci, in_ctx);
/* Update the last valid endpoint context, if we deleted the last one */
- if ((slot_ctx->dev_info & LAST_CTX_MASK) > LAST_CTX(last_ctx)) {
- slot_ctx->dev_info &= ~LAST_CTX_MASK;
- slot_ctx->dev_info |= LAST_CTX(last_ctx);
+ if ((le32_to_cpu(slot_ctx->dev_info) & LAST_CTX_MASK) >
+ LAST_CTX(last_ctx)) {
+ slot_ctx->dev_info &= cpu_to_le32(~LAST_CTX_MASK);
+ slot_ctx->dev_info |= cpu_to_le32(LAST_CTX(last_ctx));
}
- new_slot_info = slot_ctx->dev_info;
+ new_slot_info = le32_to_cpu(slot_ctx->dev_info);
xhci_endpoint_zero(xhci, xhci->devs[udev->slot_id], ep);
@@ -1414,7 +1417,8 @@ int xhci_add_endpoint(struct usb_hcd *hcd, struct usb_device *udev,
/* If the HCD has already noted the endpoint is enabled,
* ignore this request.
*/
- if (ctrl_ctx->add_flags & xhci_get_endpoint_flag(&ep->desc)) {
+ if (le32_to_cpu(ctrl_ctx->add_flags) &
+ xhci_get_endpoint_flag(&ep->desc)) {
xhci_warn(xhci, "xHCI %s called with enabled ep %p\n",
__func__, ep);
return 0;
@@ -1432,8 +1436,8 @@ int xhci_add_endpoint(struct usb_hcd *hcd, struct usb_device *udev,
return -ENOMEM;
}
- ctrl_ctx->add_flags |= added_ctxs;
- new_add_flags = ctrl_ctx->add_flags;
+ ctrl_ctx->add_flags |= cpu_to_le32(added_ctxs);
+ new_add_flags = le32_to_cpu(ctrl_ctx->add_flags);
/* If xhci_endpoint_disable() was called for this endpoint, but the
* xHC hasn't been notified yet through the check_bandwidth() call,
@@ -1441,15 +1445,16 @@ int xhci_add_endpoint(struct usb_hcd *hcd, struct usb_device *udev,
* descriptors. We must drop and re-add this endpoint, so we leave the
* drop flags alone.
*/
- new_drop_flags = ctrl_ctx->drop_flags;
+ new_drop_flags = le32_to_cpu(ctrl_ctx->drop_flags);
slot_ctx = xhci_get_slot_ctx(xhci, in_ctx);
/* Update the last valid endpoint context, if we just added one past */
- if ((slot_ctx->dev_info & LAST_CTX_MASK) < LAST_CTX(last_ctx)) {
- slot_ctx->dev_info &= ~LAST_CTX_MASK;
- slot_ctx->dev_info |= LAST_CTX(last_ctx);
+ if ((le32_to_cpu(slot_ctx->dev_info) & LAST_CTX_MASK) <
+ LAST_CTX(last_ctx)) {
+ slot_ctx->dev_info &= cpu_to_le32(~LAST_CTX_MASK);
+ slot_ctx->dev_info |= cpu_to_le32(LAST_CTX(last_ctx));
}
- new_slot_info = slot_ctx->dev_info;
+ new_slot_info = le32_to_cpu(slot_ctx->dev_info);
/* Store the usb_device pointer for later use */
ep->hcpriv = udev;
@@ -1479,9 +1484,9 @@ static void xhci_zero_in_ctx(struct xhci_hcd *xhci, struct xhci_virt_device *vir
ctrl_ctx->drop_flags = 0;
ctrl_ctx->add_flags = 0;
slot_ctx = xhci_get_slot_ctx(xhci, virt_dev->in_ctx);
- slot_ctx->dev_info &= ~LAST_CTX_MASK;
+ slot_ctx->dev_info &= cpu_to_le32(~LAST_CTX_MASK);
/* Endpoint 0 is always valid */
- slot_ctx->dev_info |= LAST_CTX(1);
+ slot_ctx->dev_info |= cpu_to_le32(LAST_CTX(1));
for (i = 1; i < 31; ++i) {
ep_ctx = xhci_get_ep_ctx(xhci, virt_dev->in_ctx, i);
ep_ctx->ep_info = 0;
@@ -1576,7 +1581,7 @@ static int xhci_configure_endpoint(struct xhci_hcd *xhci,
unsigned long flags;
struct xhci_container_ctx *in_ctx;
struct completion *cmd_completion;
- int *cmd_status;
+ u32 *cmd_status;
struct xhci_virt_device *virt_dev;
spin_lock_irqsave(&xhci->lock, flags);
@@ -1590,8 +1595,8 @@ static int xhci_configure_endpoint(struct xhci_hcd *xhci,
/* Enqueue pointer can be left pointing to the link TRB,
* we must handle that
*/
- if ((command->command_trb->link.control & TRB_TYPE_BITMASK)
- == TRB_TYPE(TRB_LINK))
+ if ((le32_to_cpu(command->command_trb->link.control)
+ & TRB_TYPE_BITMASK) == TRB_TYPE(TRB_LINK))
command->command_trb =
xhci->cmd_ring->enq_seg->next->trbs;
@@ -1667,14 +1672,13 @@ int xhci_check_bandwidth(struct usb_hcd *hcd, struct usb_device *udev)
/* See section 4.6.6 - A0 = 1; A1 = D0 = D1 = 0 */
ctrl_ctx = xhci_get_input_control_ctx(xhci, virt_dev->in_ctx);
- ctrl_ctx->add_flags |= SLOT_FLAG;
- ctrl_ctx->add_flags &= ~EP0_FLAG;
- ctrl_ctx->drop_flags &= ~SLOT_FLAG;
- ctrl_ctx->drop_flags &= ~EP0_FLAG;
+ ctrl_ctx->add_flags |= cpu_to_le32(SLOT_FLAG);
+ ctrl_ctx->add_flags &= cpu_to_le32(~EP0_FLAG);
+ ctrl_ctx->drop_flags &= cpu_to_le32(~(SLOT_FLAG | EP0_FLAG));
xhci_dbg(xhci, "New Input Control Context:\n");
slot_ctx = xhci_get_slot_ctx(xhci, virt_dev->in_ctx);
xhci_dbg_ctx(xhci, virt_dev->in_ctx,
- LAST_CTX_TO_EP_NUM(slot_ctx->dev_info));
+ LAST_CTX_TO_EP_NUM(le32_to_cpu(slot_ctx->dev_info)));
ret = xhci_configure_endpoint(xhci, udev, NULL,
false, false);
@@ -1685,7 +1689,7 @@ int xhci_check_bandwidth(struct usb_hcd *hcd, struct usb_device *udev)
xhci_dbg(xhci, "Output context after successful config ep cmd:\n");
xhci_dbg_ctx(xhci, virt_dev->out_ctx,
- LAST_CTX_TO_EP_NUM(slot_ctx->dev_info));
+ LAST_CTX_TO_EP_NUM(le32_to_cpu(slot_ctx->dev_info)));
xhci_zero_in_ctx(xhci, virt_dev);
/* Install new rings and free or cache any old rings */
@@ -1735,10 +1739,10 @@ static void xhci_setup_input_ctx_for_config_ep(struct xhci_hcd *xhci,
{
struct xhci_input_control_ctx *ctrl_ctx;
ctrl_ctx = xhci_get_input_control_ctx(xhci, in_ctx);
- ctrl_ctx->add_flags = add_flags;
- ctrl_ctx->drop_flags = drop_flags;
+ ctrl_ctx->add_flags = cpu_to_le32(add_flags);
+ ctrl_ctx->drop_flags = cpu_to_le32(drop_flags);
xhci_slot_copy(xhci, in_ctx, out_ctx);
- ctrl_ctx->add_flags |= SLOT_FLAG;
+ ctrl_ctx->add_flags |= cpu_to_le32(SLOT_FLAG);
xhci_dbg(xhci, "Input Context:\n");
xhci_dbg_ctx(xhci, in_ctx, xhci_last_valid_endpoint(add_flags));
@@ -1767,7 +1771,7 @@ static void xhci_setup_input_ctx_for_quirk(struct xhci_hcd *xhci,
deq_state->new_deq_ptr);
return;
}
- ep_ctx->deq = addr | deq_state->new_cycle_state;
+ ep_ctx->deq = cpu_to_le64(addr | deq_state->new_cycle_state);
added_ctxs = xhci_get_endpoint_flag_from_index(ep_index);
xhci_setup_input_ctx_for_config_ep(xhci, xhci->devs[slot_id]->in_ctx,
@@ -2322,8 +2326,8 @@ int xhci_discover_or_reset_device(struct usb_hcd *hcd, struct usb_device *udev)
/* Enqueue pointer can be left pointing to the link TRB,
* we must handle that
*/
- if ((reset_device_cmd->command_trb->link.control & TRB_TYPE_BITMASK)
- == TRB_TYPE(TRB_LINK))
+ if ((le32_to_cpu(reset_device_cmd->command_trb->link.control)
+ & TRB_TYPE_BITMASK) == TRB_TYPE(TRB_LINK))
reset_device_cmd->command_trb =
xhci->cmd_ring->enq_seg->next->trbs;
@@ -2596,10 +2600,10 @@ int xhci_address_device(struct usb_hcd *hcd, struct usb_device *udev)
temp_64 = xhci_read_64(xhci, &xhci->op_regs->dcbaa_ptr);
xhci_dbg(xhci, "Op regs DCBAA ptr = %#016llx\n", temp_64);
xhci_dbg(xhci, "Slot ID %d dcbaa entry @%p = %#016llx\n",
- udev->slot_id,
- &xhci->dcbaa->dev_context_ptrs[udev->slot_id],
- (unsigned long long)
- xhci->dcbaa->dev_context_ptrs[udev->slot_id]);
+ udev->slot_id,
+ &xhci->dcbaa->dev_context_ptrs[udev->slot_id],
+ (unsigned long long)
+ le64_to_cpu(xhci->dcbaa->dev_context_ptrs[udev->slot_id]));
xhci_dbg(xhci, "Output Context DMA address = %#08llx\n",
(unsigned long long)virt_dev->out_ctx->dma);
xhci_dbg(xhci, "Slot ID %d Input Context:\n", udev->slot_id);
@@ -2613,7 +2617,8 @@ int xhci_address_device(struct usb_hcd *hcd, struct usb_device *udev)
slot_ctx = xhci_get_slot_ctx(xhci, virt_dev->out_ctx);
/* Use kernel assigned address for devices; store xHC assigned
* address locally. */
- virt_dev->address = (slot_ctx->dev_state & DEV_ADDR_MASK) + 1;
+ virt_dev->address = (le32_to_cpu(slot_ctx->dev_state) & DEV_ADDR_MASK)
+ + 1;
/* Zero the input context control for later use */
ctrl_ctx = xhci_get_input_control_ctx(xhci, virt_dev->in_ctx);
ctrl_ctx->add_flags = 0;
@@ -2657,16 +2662,16 @@ int xhci_update_hub_device(struct usb_hcd *hcd, struct usb_device *hdev,
spin_lock_irqsave(&xhci->lock, flags);
xhci_slot_copy(xhci, config_cmd->in_ctx, vdev->out_ctx);
ctrl_ctx = xhci_get_input_control_ctx(xhci, config_cmd->in_ctx);
- ctrl_ctx->add_flags |= SLOT_FLAG;
+ ctrl_ctx->add_flags |= cpu_to_le32(SLOT_FLAG);
slot_ctx = xhci_get_slot_ctx(xhci, config_cmd->in_ctx);
- slot_ctx->dev_info |= DEV_HUB;
+ slot_ctx->dev_info |= cpu_to_le32(DEV_HUB);
if (tt->multi)
- slot_ctx->dev_info |= DEV_MTT;
+ slot_ctx->dev_info |= cpu_to_le32(DEV_MTT);
if (xhci->hci_version > 0x95) {
xhci_dbg(xhci, "xHCI version %x needs hub "
"TT think time and number of ports\n",
(unsigned int) xhci->hci_version);
- slot_ctx->dev_info2 |= XHCI_MAX_PORTS(hdev->maxchild);
+ slot_ctx->dev_info2 |= cpu_to_le32(XHCI_MAX_PORTS(hdev->maxchild));
/* Set TT think time - convert from ns to FS bit times.
* 0 = 8 FS bit times, 1 = 16 FS bit times,
* 2 = 24 FS bit times, 3 = 32 FS bit times.
@@ -2674,7 +2679,7 @@ int xhci_update_hub_device(struct usb_hcd *hcd, struct usb_device *hdev,
think_time = tt->think_time;
if (think_time != 0)
think_time = (think_time / 666) - 1;
- slot_ctx->tt_info |= TT_THINK_TIME(think_time);
+ slot_ctx->tt_info |= cpu_to_le32(TT_THINK_TIME(think_time));
} else {
xhci_dbg(xhci, "xHCI version %x doesn't need hub "
"TT think time or number of ports\n",
diff --git a/drivers/usb/host/xhci.h b/drivers/usb/host/xhci.h
index 711de25..7970f6b 100644
--- a/drivers/usb/host/xhci.h
+++ b/drivers/usb/host/xhci.h
@@ -56,13 +56,13 @@
* @run_regs_off: RTSOFF - Runtime register space offset
*/
struct xhci_cap_regs {
- u32 hc_capbase;
- u32 hcs_params1;
- u32 hcs_params2;
- u32 hcs_params3;
- u32 hcc_params;
- u32 db_off;
- u32 run_regs_off;
+ __le32 hc_capbase;
+ __le32 hcs_params1;
+ __le32 hcs_params2;
+ __le32 hcs_params3;
+ __le32 hcc_params;
+ __le32 db_off;
+ __le32 run_regs_off;
/* Reserved up to (CAPLENGTH - 0x1C) */
};
@@ -154,26 +154,26 @@ struct xhci_cap_regs {
* devices.
*/
struct xhci_op_regs {
- u32 command;
- u32 status;
- u32 page_size;
- u32 reserved1;
- u32 reserved2;
- u32 dev_notification;
- u64 cmd_ring;
+ __le32 command;
+ __le32 status;
+ __le32 page_size;
+ __le32 reserved1;
+ __le32 reserved2;
+ __le32 dev_notification;
+ __le64 cmd_ring;
/* rsvd: offset 0x20-2F */
- u32 reserved3[4];
- u64 dcbaa_ptr;
- u32 config_reg;
+ __le32 reserved3[4];
+ __le64 dcbaa_ptr;
+ __le32 config_reg;
/* rsvd: offset 0x3C-3FF */
- u32 reserved4[241];
+ __le32 reserved4[241];
/* port 1 registers, which serve as a base address for other ports */
- u32 port_status_base;
- u32 port_power_base;
- u32 port_link_base;
- u32 reserved5;
+ __le32 port_status_base;
+ __le32 port_power_base;
+ __le32 port_link_base;
+ __le32 reserved5;
/* registers for ports 2-255 */
- u32 reserved6[NUM_PORT_REGS*254];
+ __le32 reserved6[NUM_PORT_REGS*254];
};
/* USBCMD - USB command - command bitmasks */
@@ -378,12 +378,12 @@ struct xhci_op_regs {
* updates the dequeue pointer.
*/
struct xhci_intr_reg {
- u32 irq_pending;
- u32 irq_control;
- u32 erst_size;
- u32 rsvd;
- u64 erst_base;
- u64 erst_dequeue;
+ __le32 irq_pending;
+ __le32 irq_control;
+ __le32 erst_size;
+ __le32 rsvd;
+ __le64 erst_base;
+ __le64 erst_dequeue;
};
/* irq_pending bitmasks */
@@ -428,8 +428,8 @@ struct xhci_intr_reg {
* or larger accesses"
*/
struct xhci_run_regs {
- u32 microframe_index;
- u32 rsvd[7];
+ __le32 microframe_index;
+ __le32 rsvd[7];
struct xhci_intr_reg ir_set[128];
};
@@ -443,7 +443,7 @@ struct xhci_run_regs {
* Section 5.6
*/
struct xhci_doorbell_array {
- u32 doorbell[256];
+ __le32 doorbell[256];
};
#define DB_VALUE(ep, stream) ((((ep) + 1) & 0xff) | ((stream) << 16))
@@ -500,12 +500,12 @@ struct xhci_container_ctx {
* reserved at the end of the slot context for HC internal use.
*/
struct xhci_slot_ctx {
- u32 dev_info;
- u32 dev_info2;
- u32 tt_info;
- u32 dev_state;
+ __le32 dev_info;
+ __le32 dev_info2;
+ __le32 tt_info;
+ __le32 dev_state;
/* offset 0x10 to 0x1f reserved for HC internal use */
- u32 reserved[4];
+ __le32 reserved[4];
};
/* dev_info bitmasks */
@@ -576,12 +576,12 @@ struct xhci_slot_ctx {
* reserved at the end of the endpoint context for HC internal use.
*/
struct xhci_ep_ctx {
- u32 ep_info;
- u32 ep_info2;
- u64 deq;
- u32 tx_info;
+ __le32 ep_info;
+ __le32 ep_info2;
+ __le64 deq;
+ __le32 tx_info;
/* offset 0x14 - 0x1f reserved for HC internal use */
- u32 reserved[3];
+ __le32 reserved[3];
};
/* ep_info bitmasks */
@@ -656,9 +656,9 @@ struct xhci_ep_ctx {
* @add_context: set the bit of the endpoint context you want to enable
*/
struct xhci_input_control_ctx {
- u32 drop_flags;
- u32 add_flags;
- u32 rsvd2[6];
+ __le32 drop_flags;
+ __le32 add_flags;
+ __le32 rsvd2[6];
};
/* Represents everything that is needed to issue a command on the command ring.
@@ -684,9 +684,9 @@ struct xhci_command {
struct xhci_stream_ctx {
/* 64-bit stream ring address, cycle state, and stream type */
- u64 stream_ring;
+ __le64 stream_ring;
/* offset 0x14 - 0x1f reserved for HC internal use */
- u32 reserved[2];
+ __le32 reserved[2];
};
/* Stream Context Types (section 6.4.1) - bits 3:1 of stream ctx deq ptr */
@@ -799,7 +799,7 @@ struct xhci_virt_device {
*/
struct xhci_device_context_array {
/* 64-bit device addresses; we only write 32-bit addresses */
- u64 dev_context_ptrs[MAX_HC_SLOTS];
+ __le64 dev_context_ptrs[MAX_HC_SLOTS];
/* private xHCD pointers */
dma_addr_t dma;
};
@@ -812,10 +812,10 @@ struct xhci_device_context_array {
struct xhci_transfer_event {
/* 64-bit buffer address, or immediate data */
- u64 buffer;
- u32 transfer_len;
+ __le64 buffer;
+ __le32 transfer_len;
/* This field is interpreted differently based on the type of TRB */
- u32 flags;
+ __le32 flags;
};
/** Transfer Event bit fields **/
@@ -894,9 +894,9 @@ struct xhci_transfer_event {
struct xhci_link_trb {
/* 64-bit segment pointer*/
- u64 segment_ptr;
- u32 intr_target;
- u32 control;
+ __le64 segment_ptr;
+ __le32 intr_target;
+ __le32 control;
};
/* control bitfields */
@@ -905,9 +905,9 @@ struct xhci_link_trb {
/* Command completion event TRB */
struct xhci_event_cmd {
/* Pointer to command TRB, or the value passed by the event data trb */
- u64 cmd_trb;
- u32 status;
- u32 flags;
+ __le64 cmd_trb;
+ __le32 status;
+ __le32 flags;
};
/* flags bitmasks */
@@ -966,7 +966,7 @@ struct xhci_event_cmd {
#define TRB_SIA (1<<31)
struct xhci_generic_trb {
- u32 field[4];
+ __le32 field[4];
};
union xhci_trb {
@@ -1114,10 +1114,10 @@ struct xhci_ring {
struct xhci_erst_entry {
/* 64-bit event ring segment address */
- u64 seg_addr;
- u32 seg_size;
+ __le64 seg_addr;
+ __le32 seg_size;
/* Set to zero */
- u32 rsvd;
+ __le32 rsvd;
};
struct xhci_erst {
@@ -1281,10 +1281,10 @@ struct xhci_hcd {
/* Is each xHCI roothub port a USB 3.0, USB 2.0, or USB 1.1 port? */
u8 *port_array;
/* Array of pointers to USB 3.0 PORTSC registers */
- u32 __iomem **usb3_ports;
+ __le32 __iomem **usb3_ports;
unsigned int num_usb3_ports;
/* Array of pointers to USB 2.0 PORTSC registers */
- u32 __iomem **usb2_ports;
+ __le32 __iomem **usb2_ports;
unsigned int num_usb2_ports;
};
@@ -1317,12 +1317,12 @@ static inline struct usb_hcd *xhci_to_hcd(struct xhci_hcd *xhci)
/* TODO: copied from ehci.h - can be refactored? */
/* xHCI spec says all registers are little endian */
static inline unsigned int xhci_readl(const struct xhci_hcd *xhci,
- __u32 __iomem *regs)
+ __le32 __iomem *regs)
{
return readl(regs);
}
static inline void xhci_writel(struct xhci_hcd *xhci,
- const unsigned int val, __u32 __iomem *regs)
+ const unsigned int val, __le32 __iomem *regs)
{
xhci_dbg(xhci,
"`MEM_WRITE_DWORD(3'b000, 32'h%p, 32'h%0x, 4'hf);\n",
@@ -1340,7 +1340,7 @@ static inline void xhci_writel(struct xhci_hcd *xhci,
* the high dword, and write order is irrelevant.
*/
static inline u64 xhci_read_64(const struct xhci_hcd *xhci,
- __u64 __iomem *regs)
+ __le64 __iomem *regs)
{
__u32 __iomem *ptr = (__u32 __iomem *) regs;
u64 val_lo = readl(ptr);
@@ -1348,7 +1348,7 @@ static inline u64 xhci_read_64(const struct xhci_hcd *xhci,
return val_lo + (val_hi << 32);
}
static inline void xhci_write_64(struct xhci_hcd *xhci,
- const u64 val, __u64 __iomem *regs)
+ const u64 val, __le64 __iomem *regs)
{
__u32 __iomem *ptr = (__u32 __iomem *) regs;
u32 val_lo = lower_32_bits(val);
--
1.7.0.4
^ permalink raw reply related
* [PATCH v3 0/4] xhci: Make xHCI driver endian-safe
From: Matt Evans @ 2011-03-29 2:40 UTC (permalink / raw)
To: Sarah Sharp; +Cc: linuxppc-dev, linux-usb
Hi Sarah,
Reposting the whole set, since there're 4 instead of 5 having dropped the 'add
debug' patch in the middle and I thought differing 'vN a/b' subjects may get
confusing otherwise. (Two of the patches haven't changed. :/ )
These remove the scratty temp variables and comment the return value
of xhci_handle_event().
Cheers,
Matt
^ permalink raw reply
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox