* RE: IRQ2 and IRQ 3
From: Bhushan Bharat-R65777 @ 2011-10-18 15:03 UTC (permalink / raw)
To: smitha.vanga@wipro.com, Wood Scott-B07421; +Cc: linuxppc-dev@lists.ozlabs.org
In-Reply-To: <40631E9A2581F14BA60888C87A76A1FE016391@HYD-MKD-MBX4.wipro.com>
Hi,=20
For detail please look at ePAPR specification.
Say i2c interrupt number is 21 then you can try
"interrupts =3D <21 8>;" in device tree.
Thanks
-Bharat
> -----Original Message-----
> From: smitha.vanga@wipro.com [mailto:smitha.vanga@wipro.com]
> Sent: Tuesday, October 18, 2011 2:35 PM
> To: Bhushan Bharat-R65777; Wood Scott-B07421
> Cc: linuxppc-dev@lists.ozlabs.org
> Subject: RE: IRQ2 and IRQ 3
>=20
> Yes , there is a vector numbe defined in the specifications .
>=20
> IRQ2 - 20
> IRQ3 - 21
>=20
> But I don't know how to specify them in the .dts file .
>=20
> Regards,
> Smitha
> Please do not print this email unless it is absolutely necessary.
>=20
> The information contained in this electronic message and any attachments
> to this message are intended for the exclusive use of the addressee(s)
> and may contain proprietary, confidential or privileged information. If
> you are not the intended recipient, you should not disseminate,
> distribute or copy this e-mail. Please notify the sender immediately and
> destroy all copies of this message and any attachments.
>=20
> WARNING: Computer viruses can be transmitted via email. The recipient
> should check this email and any attachments for the presence of viruses.
> The company accepts no liability for any damage caused by any virus
> transmitted by this email.
>=20
> www.wipro.com
^ permalink raw reply
* RE: FW: P4080 device tree problems with fsl dpaa ... RESOLVED.
From: Robert Sciuk @ 2011-10-18 21:38 UTC (permalink / raw)
To: Thomas De Schampheleire; +Cc: linuxppc-dev
In-Reply-To: <CAAXf6LWTU_OVBLfn4Tavo6JxE5UfoN8JuCidB4fbr4-RX+XdAw@mail.gmail.com>
[snip]
>=20
> How and what type of packets are you sending? Raw MAC frames, IP? Are
> you using custom code, or can you try things like ping?
> If you are using IP, I trust you checked the Linux configuration (like
> assigning a valid IP address to the interface and making sure the
> routing table is correct).
>=20
> Best regards,
> Thomas
Thomas,
I would like to thank you for your very kind assistance on the DPAA =
problem. We traced the problem to the connection between MAC and PHY, =
and we have resolved it by instrumenting code and comparing the TBI and =
PHY initialization from u-boot to the Linux DPA code, and replacing some =
of the "magic numbers" we found in the driver with "magic numbers" we =
know to work. OUCH!
We spent an inordinate amount of effort in trying to understand the =
undocumented code/tree, but I am convinced that the device-tree approach =
to embedded boards is a very valuable approach, and will ultimately =
solve many more problems than it creates short term. =20
Again, your information was of great assistance, and I look forward to =
being able to pay forward the same kind of assistance in the future.
Cheers,
Rob Sciuk.
^ permalink raw reply
* Re: FW: P4080 device tree problems with fsl dpaa ... RESOLVED.
From: Thomas De Schampheleire @ 2011-10-19 6:34 UTC (permalink / raw)
To: Robert Sciuk; +Cc: linuxppc-dev
In-Reply-To: <2DD52030B5146141BEB762A11AE97C4CF6E5E9@SPQCEXC05.exfo.com>
Hi Robert,
On Tue, Oct 18, 2011 at 11:38 PM, Robert Sciuk <robert.sciuk@exfo.com> wrot=
e:
>
>
> [snip]
>
>>
>> How and what type of packets are you sending? Raw MAC frames, IP? Are
>> you using custom code, or can you try things like ping?
>> If you are using IP, I trust you checked the Linux configuration (like
>> assigning a valid IP address to the interface and making sure the
>> routing table is correct).
>>
>> Best regards,
>> Thomas
>
> Thomas,
>
> I would like to thank you for your very kind assistance on the DPAA probl=
em. =A0We traced the problem to the connection between MAC and PHY, and we =
have resolved it by instrumenting code and comparing the TBI and PHY initia=
lization from u-boot to the Linux DPA code, and replacing some of the "magi=
c numbers" we found in the driver with "magic numbers" we know to work. =A0=
OUCH!
>
> We spent an inordinate amount of effort in trying to understand the undoc=
umented code/tree, but I am convinced that the device-tree approach to embe=
dded boards is a very valuable approach, and will ultimately solve many mor=
e problems than it creates short term.
>
> Again, your information was of great assistance, and I look forward to be=
ing able to pay forward the same kind of assistance in the future.
I'm very glad you found the issue (I was running out of ideas ;-) and
that I was able to help you in some way.
Thanks for posting back.
Good luck with the rest of your project!
Best regards,
Thomas
>
> Cheers,
> Rob Sciuk.
>
>
^ permalink raw reply
* [PATCH] powerpc/cpm: Clear muram before it is in use.
From: Kumar Gala @ 2011-10-19 7:18 UTC (permalink / raw)
To: linuxppc-dev; +Cc: Hongjun Chen
From: Hongjun Chen <Hong-jun.Chen@freescale.com>
We need to ensure that MURAM is in a known and cleared out state before
using it as the bootloader could have utilized it from its own purposes
and left it in an unknown state.
If we don't clear it out we've seen issues with UCC ethernet:
* Multi ethernet interfaces can't work simultanously.
* Multi up/down Ethernet interfaces will halt these ports.
* UCC1 RGMII can't work when kernel boots from some hosts.
Signed-off-by: Kai.Jiang <Kai.Jiang@freescale.com>
Signed-off-by: Hongjun Chen <Hong-jun.Chen@freescale.com>
Signed-off-by: Kumar Gala <galak@kernel.crashing.org>
---
arch/powerpc/sysdev/cpm_common.c | 3 ++-
1 files changed, 2 insertions(+), 1 deletions(-)
diff --git a/arch/powerpc/sysdev/cpm_common.c b/arch/powerpc/sysdev/cpm_common.c
index d55d0ad..8db10bb 100644
--- a/arch/powerpc/sysdev/cpm_common.c
+++ b/arch/powerpc/sysdev/cpm_common.c
@@ -3,7 +3,7 @@
*
* Author: Scott Wood <scottwood@freescale.com>
*
- * Copyright 2007 Freescale Semiconductor, Inc.
+ * Copyright 2007-2008,2010 Freescale Semiconductor, Inc.
*
* Some parts derived from commproc.c/cpm2_common.c, which is:
* Copyright (c) 1997 Dan error_act (dmalek@jlc.net)
@@ -146,6 +146,7 @@ unsigned long cpm_muram_alloc(unsigned long size, unsigned long align)
spin_lock_irqsave(&cpm_muram_lock, flags);
cpm_muram_info.alignment = align;
start = rh_alloc(&cpm_muram_info, size, "commproc");
+ memset(cpm_muram_addr(start), 0, size);
spin_unlock_irqrestore(&cpm_muram_lock, flags);
return start;
--
1.7.3.4
^ permalink raw reply related
* RE: [PATCH 3/5][v2] fsl-rio: Add two ports and rapidio message units support
From: Bounine, Alexandre @ 2011-10-19 19:54 UTC (permalink / raw)
To: Kumar Gala, linuxppc-dev; +Cc: Andrew Morton, Liu Gang, linux-kernel
In-Reply-To: <1318514922-12672-3-git-send-email-galak@kernel.crashing.org>
On Thu, Oct 13, 2011 at 10:09 AM, Kumar Gala wrote:
>=20
> From: Liu Gang <Gang.Liu@freescale.com>
>=20
> Usually, freescale rapidio endpoint can support one 1X or two 4X LP-
> Serial
> link interfaces, and rapidio message transactions can be implemented
by
> two
Is the number of 1x ports described correctly?
Can we have two 1x ports as well?=20
> message units. This patch adds the support of two rapidio ports and
> initializes message unit 0 and message unit 1. And these ports and
> message
... skip ...
> +
> + /* Probe the master port phy type */
> + ccsr =3D in_be32(priv->regs_win + RIO_CCSR + i*0x20);
> + port->phy_type =3D (ccsr & 1) ? RIO_PHY_SERIAL :
> RIO_PHY_PARALLEL;
> + dev_info(&dev->dev, "RapidIO PHY type: %s\n",
> + (port->phy_type =3D=3D RIO_PHY_PARALLEL) ?
> + "parallel" :
> + ((port->phy_type =3D=3D RIO_PHY_SERIAL) ?
"serial"
> :
> + "unknown"));
> + /* Checking the port training status */
> + if (in_be32((priv->regs_win + RIO_ESCSR + i*0x20)) & 1)
{
> + dev_err(&dev->dev, "Port %d is not ready. "
> + "Try to restart connection...\n", i);
> + switch (port->phy_type) {
> + case RIO_PHY_SERIAL:
> + /* Disable ports */
> + out_be32(priv->regs_win
> + + RIO_CCSR + i*0x20, 0);
> + /* Set 1x lane */
> + setbits32(priv->regs_win
> + + RIO_CCSR + i*0x20,
0x02000000);
> + /* Enable ports */
> + setbits32(priv->regs_win
> + + RIO_CCSR + i*0x20,
0x00600000);
> + break;
> + case RIO_PHY_PARALLEL:
> + /* Disable ports */
> + out_be32(priv->regs_win
> + + RIO_CCSR + i*0x20,
0x22000000);
> + /* Enable ports */
> + out_be32(priv->regs_win
> + + RIO_CCSR + i*0x20,
0x44000000);
> + break;
> + }
Probably this may be a good moment to drop the support for parallel
link.
Especially after you renamed controller to "srio" in the device tree.
> + msleep(100);
> + if (in_be32((priv->regs_win
> + + RIO_ESCSR + i*0x20)) & 1) {
> + dev_err(&dev->dev,
> + "Port %d restart failed.\n", i);
> + release_resource(&port->iores);
> + kfree(priv);
> + kfree(port);
> + continue;
> + }
> + dev_info(&dev->dev, "Port %d restart
success!\n", i);
> + }
> + fsl_rio_info(&dev->dev, ccsr);
> +
... skip ...
>=20
> struct rio_msg_regs {
> - u32 omr; /* 0xD_3000 - Outbound message 0 mode register
*/
> - u32 osr; /* 0xD_3004 - Outbound message 0 status register
*/
> + u32 omr;
> + u32 osr;
> u32 pad1;
> - u32 odqdpar; /* 0xD_300C - Outbound message 0 descriptor
> queue
> - dequeue pointer address register */
> + u32 odqdpar;
> u32 pad2;
> - u32 osar; /* 0xD_3014 - Outbound message 0 source address
> - register */
> - u32 odpr; /* 0xD_3018 - Outbound message 0 destination
port
> - register */
> - u32 odatr; /* 0xD_301C - Outbound message 0 destination
> attributes
> - Register*/
> - u32 odcr; /* 0xD_3020 - Outbound message 0 double-word
count
> - register */
> + u32 osar;
> + u32 odpr;
> + u32 odatr;
> + u32 odcr;
> u32 pad3;
> - u32 odqepar; /* 0xD_3028 - Outbound message 0 descriptor
> queue
> - enqueue pointer address register */
> + u32 odqepar;
> u32 pad4[13];
> - u32 imr; /* 0xD_3060 - Inbound message 0 mode register */
> - u32 isr; /* 0xD_3064 - Inbound message 0 status register
*/
> + u32 imr;
> + u32 isr;
> u32 pad5;
> - u32 ifqdpar; /* 0xD_306C - Inbound message 0 frame queue
> dequeue
> - pointer address register*/
> + u32 ifqdpar;
> u32 pad6;
> - u32 ifqepar; /* 0xD_3074 - Inbound message 0 frame queue
> enqueue
> - pointer address register */
> - u32 pad7[226];
> - u32 odmr; /* 0xD_3400 - Outbound doorbell mode register */
> - u32 odsr; /* 0xD_3404 - Outbound doorbell status register
*/
> + u32 ifqepar;
> + u32 pad7;
Do we need pad7 here?
> +};
> +
> +struct rio_dbell_regs {
> + u32 odmr;
> + u32 odsr;
> u32 res0[4];
> - u32 oddpr; /* 0xD_3418 - Outbound doorbell destination port
> - register */
... skip ...
>=20
> @@ -340,35 +327,45 @@ fsl_rio_dbell_handler(int irq, void
> *dev_instance)
> " sid %2.2x tid %2.2x info %4.4x\n",
> DBELL_SID(dmsg), DBELL_TID(dmsg),
DBELL_INF(dmsg));
>=20
> - list_for_each_entry(dbell, &port->dbells, node) {
> - if ((dbell->res->start <=3D DBELL_INF(dmsg)) &&
> - (dbell->res->end >=3D DBELL_INF(dmsg))) {
> - found =3D 1;
> - break;
> + for (i =3D 0; i < MAX_PORT_NUM; i++) {
> + if (fsl_dbell->mport[i]) {
> + list_for_each_entry(dbell,
> + &fsl_dbell->mport[i]->dbells,
node) {
> + if ((dbell->res->start
> + <=3D DBELL_INF(dmsg))
> + && (dbell->res->end
> + >=3D DBELL_INF(dmsg))) {
> + found =3D 1;
> + break;
> + }
> + }
> + if (found && dbell->dinb) {
> + dbell->dinb(fsl_dbell->mport[i],
> + dbell->dev_id,
DBELL_SID(dmsg),
> + DBELL_TID(dmsg),
> + DBELL_INF(dmsg));
> + break;
> + }
> }
> }
Do we need to check for matching DBELL_TID and mport destID here
and scan only doorbell list attached to the right port? Otherwise this
may call service routine associated with doorbell on a wrong port.
> - if (found) {
> - dbell->dinb(port, dbell->dev_id,
> - DBELL_SID(dmsg),
> - DBELL_TID(dmsg),
DBELL_INF(dmsg));
> - } else {
> +
> + if (!found) {
> pr_debug
> ("RIO: spurious doorbell,"
> " sid %2.2x tid %2.2x info %4.4x\n",
> DBELL_SID(dmsg), DBELL_TID(dmsg),
> DBELL_INF(dmsg));
> }
> - setbits32(&rmu->msg_regs->dmr, DOORBELL_DMR_DI);
> - out_be32(&rmu->msg_regs->dsr, DOORBELL_DSR_DIQI);
> + setbits32(&fsl_dbell->dbell_regs->dmr, DOORBELL_DMR_DI);
> + out_be32(&fsl_dbell->dbell_regs->dsr,
DOORBELL_DSR_DIQI);
> }
>=20
> out:
> return IRQ_HANDLED;
> }
>=20
... skip ...
> @@ -1114,50 +1104,48 @@ int fsl_rio_setup_rmu(struct rio_mport *mport,
> struct device_node *node)
> {
> struct rio_priv *priv;
> struct fsl_rmu *rmu;
> - struct rio_ops *ops;
> + u64 msg_start;
> + const u32 *msg_addr;
> + int mlen;
> + int aw;
>=20
> - if (!mport || !mport->priv || !node)
> - return -1;
> + if (!mport || !mport->priv)
> + return -EFAULT;
EINVAL may be better here?
> +
> + priv =3D mport->priv;
> +
> + if (!node) {
> + dev_warn(priv->dev, "Can't get %s property 'fsl,rmu'\n",
> + priv->dev->of_node->full_name);
> + return -EFAULT;
EINVAL as well?
> + }
Regards,
Alex.
^ permalink raw reply
* RE: [PATCH] powerpc/85xx: Setup secondary cores PIR with hard SMP id
From: Bhushan Bharat-R65777 @ 2011-10-20 3:53 UTC (permalink / raw)
To: Kumar Gala, linuxppc-dev@ozlabs.org
In-Reply-To: <1318578771-16865-1-git-send-email-galak@kernel.crashing.org>
> -----Original Message-----
> From: linuxppc-dev-bounces+bharat.bhushan=3Dfreescale.com@lists.ozlabs.or=
g
> [mailto:linuxppc-dev-
> bounces+bharat.bhushan=3Dfreescale.com@lists.ozlabs.org] On Behalf Of Kum=
ar
> Gala
> Sent: Friday, October 14, 2011 1:23 PM
> To: linuxppc-dev@ozlabs.org
> Subject: [PATCH] powerpc/85xx: Setup secondary cores PIR with hard SMP id
>=20
> Normally logical and hard cpu ID are the same, however in same cases like
> on the P3060 they may differ. Where the logical is 0..5, the hard id
> goes 0,1,4..7. This can causes issues for places we utilize PIR to index
> into array like in debug exception handlers for finding the exception
> stack.
Kumar, What should be the CONFIG_NR_CPUS for this? 8 or 6 ?
Thanks
-Bharat
>=20
> Move to setting up PIR with hard_smp_processor_id fixes the issue.
>=20
> Signed-off-by: Kumar Gala <galak@kernel.crashing.org>
> ---
> arch/powerpc/platforms/85xx/smp.c | 9 +++++----
> 1 files changed, 5 insertions(+), 4 deletions(-)
>=20
> diff --git a/arch/powerpc/platforms/85xx/smp.c
> b/arch/powerpc/platforms/85xx/smp.c
> index d6e4746..190d111 100644
> --- a/arch/powerpc/platforms/85xx/smp.c
> +++ b/arch/powerpc/platforms/85xx/smp.c
> @@ -48,10 +48,11 @@ smp_85xx_kick_cpu(int nr)
> const u64 *cpu_rel_addr;
> __iomem u32 *bptr_vaddr;
> struct device_node *np;
> - int n =3D 0;
> + int n =3D 0, hw_cpu =3D get_hard_smp_processor_id(nr);
> int ioremappable;
>=20
> - WARN_ON (nr < 0 || nr >=3D NR_CPUS);
> + WARN_ON(nr < 0 || nr >=3D NR_CPUS);
> + WARN_ON(hw_cpu < 0 || hw_cpu >=3D NR_CPUS);
>=20
> pr_debug("smp_85xx_kick_cpu: kick CPU #%d\n", nr);
>=20
> @@ -79,7 +80,7 @@ smp_85xx_kick_cpu(int nr)
>=20
> local_irq_save(flags);
>=20
> - out_be32(bptr_vaddr + BOOT_ENTRY_PIR, nr);
> + out_be32(bptr_vaddr + BOOT_ENTRY_PIR, hw_cpu);
> #ifdef CONFIG_PPC32
> out_be32(bptr_vaddr + BOOT_ENTRY_ADDR_LOWER, __pa(__early_start));
>=20
> @@ -88,7 +89,7 @@ smp_85xx_kick_cpu(int nr)
> (ulong)(bptr_vaddr + SIZE_BOOT_ENTRY));
>=20
> /* Wait a bit for the CPU to ack. */
> - while ((__secondary_hold_acknowledge !=3D nr) && (++n < 1000))
> + while ((__secondary_hold_acknowledge !=3D hw_cpu) && (++n < 1000))
> mdelay(1);
> #else
> smp_generic_kick_cpu(nr);
> --
> 1.7.3.4
>=20
> _______________________________________________
> Linuxppc-dev mailing list
> Linuxppc-dev@lists.ozlabs.org
> https://lists.ozlabs.org/listinfo/linuxppc-dev
^ permalink raw reply
* Re: [PATCH] powerpc/85xx: Setup secondary cores PIR with hard SMP id
From: Kumar Gala @ 2011-10-20 4:02 UTC (permalink / raw)
To: Bhushan Bharat-R65777; +Cc: linuxppc-dev@ozlabs.org
In-Reply-To: <B8D6CA50DACE9E4AAADE9A4D56FBAAE6244FDF@039-SN1MPN1-005.039d.mgd.msft.net>
On Oct 19, 2011, at 10:53 PM, Bhushan Bharat-R65777 wrote:
>
>
>> -----Original Message-----
>> From: linuxppc-dev-bounces+bharat.bhushan=freescale.com@lists.ozlabs.org
>> [mailto:linuxppc-dev-
>> bounces+bharat.bhushan=freescale.com@lists.ozlabs.org] On Behalf Of Kumar
>> Gala
>> Sent: Friday, October 14, 2011 1:23 PM
>> To: linuxppc-dev@ozlabs.org
>> Subject: [PATCH] powerpc/85xx: Setup secondary cores PIR with hard SMP id
>>
>> Normally logical and hard cpu ID are the same, however in same cases like
>> on the P3060 they may differ. Where the logical is 0..5, the hard id
>> goes 0,1,4..7. This can causes issues for places we utilize PIR to index
>> into array like in debug exception handlers for finding the exception
>> stack.
>
> Kumar, What should be the CONFIG_NR_CPUS for this? 8 or 6 ?
8.
- k
^ permalink raw reply
* RE: [PATCH] powerpc/85xx: Setup secondary cores PIR with hard SMP id
From: Bhushan Bharat-R65777 @ 2011-10-20 5:14 UTC (permalink / raw)
To: Kumar Gala; +Cc: linuxppc-dev@ozlabs.org
In-Reply-To: <C79C8827-F6FE-42E0-8FAF-E62A8FFD6A1F@kernel.crashing.org>
> -----Original Message-----
> From: Kumar Gala [mailto:galak@kernel.crashing.org]
> Sent: Thursday, October 20, 2011 9:32 AM
> To: Bhushan Bharat-R65777
> Cc: linuxppc-dev@ozlabs.org
> Subject: Re: [PATCH] powerpc/85xx: Setup secondary cores PIR with hard
> SMP id
>=20
>=20
> On Oct 19, 2011, at 10:53 PM, Bhushan Bharat-R65777 wrote:
>=20
> >
> >
> >> -----Original Message-----
> >> From:
> >> linuxppc-dev-bounces+bharat.bhushan=3Dfreescale.com@lists.ozlabs.org
> >> [mailto:linuxppc-dev-
> >> bounces+bharat.bhushan=3Dfreescale.com@lists.ozlabs.org] On Behalf Of
> >> bounces+Kumar
> >> Gala
> >> Sent: Friday, October 14, 2011 1:23 PM
> >> To: linuxppc-dev@ozlabs.org
> >> Subject: [PATCH] powerpc/85xx: Setup secondary cores PIR with hard
> >> SMP id
> >>
> >> Normally logical and hard cpu ID are the same, however in same cases
> >> like on the P3060 they may differ. Where the logical is 0..5, the
> >> hard id goes 0,1,4..7. This can causes issues for places we utilize
> >> PIR to index into array like in debug exception handlers for finding
> >> the exception stack.
> >
> > Kumar, What should be the CONFIG_NR_CPUS for this? 8 or 6 ?
>=20
> 8.
Kumar, I am just trying to understand the impact of setting NR_CPUS =3D 8 w=
hile actual cpus are 6 only.
If I understood correctly, cpu_present, cpu_online are logical cpuid bit ma=
p. Also the PACA is allocated for nr_cpu_ids (NR_CPUS) and also indexed by =
logical cpuid. While they have knowledge of physical_cpuid.
So in this case last two entries of PACA will not be used or wasted?
I am not sure I am missing something here?
Thanks
-Bharat
^ permalink raw reply
* [RFC][PATCH 1/2] powerpc/85xx: create dts components to build up an SoC
From: Kumar Gala @ 2011-10-20 7:24 UTC (permalink / raw)
To: linuxppc-dev; +Cc: devicetree-discuss
Introduce some common components that we can utilize to build up the
various PQ3/85xx device trees.
Signed-off-by: Kumar Gala <galak@kernel.crashing.org>
---
arch/powerpc/boot/dts/fsl/pq3-dma-0.dtsi | 32 +++++++++++++++++++++++++++
arch/powerpc/boot/dts/fsl/pq3-duart-0.dtsi | 8 ++++++
arch/powerpc/boot/dts/fsl/pq3-duart-1.dtsi | 8 ++++++
arch/powerpc/boot/dts/fsl/pq3-etsec2-0.dtsi | 32 +++++++++++++++++++++++++++
arch/powerpc/boot/dts/fsl/pq3-etsec2-1.dtsi | 32 +++++++++++++++++++++++++++
arch/powerpc/boot/dts/fsl/pq3-etsec2-2.dtsi | 32 +++++++++++++++++++++++++++
arch/powerpc/boot/dts/fsl/pq3-i2c-0.dtsi | 9 +++++++
arch/powerpc/boot/dts/fsl/pq3-i2c-1.dtsi | 9 +++++++
8 files changed, 162 insertions(+), 0 deletions(-)
create mode 100644 arch/powerpc/boot/dts/fsl/pq3-dma-0.dtsi
create mode 100644 arch/powerpc/boot/dts/fsl/pq3-duart-0.dtsi
create mode 100644 arch/powerpc/boot/dts/fsl/pq3-duart-1.dtsi
create mode 100644 arch/powerpc/boot/dts/fsl/pq3-etsec2-0.dtsi
create mode 100644 arch/powerpc/boot/dts/fsl/pq3-etsec2-1.dtsi
create mode 100644 arch/powerpc/boot/dts/fsl/pq3-etsec2-2.dtsi
create mode 100644 arch/powerpc/boot/dts/fsl/pq3-i2c-0.dtsi
create mode 100644 arch/powerpc/boot/dts/fsl/pq3-i2c-1.dtsi
diff --git a/arch/powerpc/boot/dts/fsl/pq3-dma-0.dtsi b/arch/powerpc/boot/dts/fsl/pq3-dma-0.dtsi
new file mode 100644
index 0000000..60aa877
--- /dev/null
+++ b/arch/powerpc/boot/dts/fsl/pq3-dma-0.dtsi
@@ -0,0 +1,32 @@
+&dma0 {
+ #address-cells = <1>;
+ #size-cells = <1>;
+ compatible = "fsl,eloplus-dma";
+ reg = <0x21300 0x4>;
+ ranges = <0x0 0x21100 0x200>;
+ cell-index = <0>;
+ dma-channel@0 {
+ compatible = "fsl,eloplus-dma-channel";
+ reg = <0x0 0x80>;
+ cell-index = <0>;
+ interrupts = <20 2 0 0>;
+ };
+ dma-channel@80 {
+ compatible = "fsl,eloplus-dma-channel";
+ reg = <0x80 0x80>;
+ cell-index = <1>;
+ interrupts = <21 2 0 0>;
+ };
+ dma-channel@100 {
+ compatible = "fsl,eloplus-dma-channel";
+ reg = <0x100 0x80>;
+ cell-index = <2>;
+ interrupts = <22 2 0 0>;
+ };
+ dma-channel@180 {
+ compatible = "fsl,eloplus-dma-channel";
+ reg = <0x180 0x80>;
+ cell-index = <3>;
+ interrupts = <23 2 0 0>;
+ };
+};
diff --git a/arch/powerpc/boot/dts/fsl/pq3-duart-0.dtsi b/arch/powerpc/boot/dts/fsl/pq3-duart-0.dtsi
new file mode 100644
index 0000000..bac1f0d
--- /dev/null
+++ b/arch/powerpc/boot/dts/fsl/pq3-duart-0.dtsi
@@ -0,0 +1,8 @@
+&serial0 {
+ cell-index = <0>;
+ device_type = "serial";
+ compatible = "ns16550";
+ reg = <0x4500 0x100>;
+ clock-frequency = <0>;
+ interrupts = <42 2 0 0>;
+};
diff --git a/arch/powerpc/boot/dts/fsl/pq3-duart-1.dtsi b/arch/powerpc/boot/dts/fsl/pq3-duart-1.dtsi
new file mode 100644
index 0000000..97bbacb
--- /dev/null
+++ b/arch/powerpc/boot/dts/fsl/pq3-duart-1.dtsi
@@ -0,0 +1,8 @@
+&serial1 {
+ cell-index = <1>;
+ device_type = "serial";
+ compatible = "ns16550";
+ reg = <0x4600 0x100>;
+ clock-frequency = <0>;
+ interrupts = <42 2 0 0>;
+};
diff --git a/arch/powerpc/boot/dts/fsl/pq3-etsec2-0.dtsi b/arch/powerpc/boot/dts/fsl/pq3-etsec2-0.dtsi
new file mode 100644
index 0000000..73fff36
--- /dev/null
+++ b/arch/powerpc/boot/dts/fsl/pq3-etsec2-0.dtsi
@@ -0,0 +1,32 @@
+&mdio0 {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ compatible = "fsl,etsec2-mdio";
+ reg = <0x24000 0x1000 0xb0030 0x4>;
+};
+
+&enet0 {
+ #address-cells = <1>;
+ #size-cells = <1>;
+ device_type = "network";
+ model = "eTSEC";
+ compatible = "fsl,etsec2";
+ fsl,num_rx_queues = <0x8>;
+ fsl,num_tx_queues = <0x8>;
+ fsl,magic-packet;
+ local-mac-address = [ 00 00 00 00 00 00 ];
+
+ queue-group@0 {
+ #address-cells = <1>;
+ #size-cells = <1>;
+ reg = <0xb0000 0x1000>;
+ interrupts = <29 2 0 0 30 2 0 0 34 2 0 0>;
+ };
+
+ queue-group@1 {
+ #address-cells = <1>;
+ #size-cells = <1>;
+ reg = <0xb4000 0x1000>;
+ interrupts = <17 2 0 0 18 2 0 0 24 2 0 0>;
+ };
+};
diff --git a/arch/powerpc/boot/dts/fsl/pq3-etsec2-1.dtsi b/arch/powerpc/boot/dts/fsl/pq3-etsec2-1.dtsi
new file mode 100644
index 0000000..7ec73c4
--- /dev/null
+++ b/arch/powerpc/boot/dts/fsl/pq3-etsec2-1.dtsi
@@ -0,0 +1,32 @@
+&mdio1 {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ compatible = "fsl,etsec2-tbi";
+ reg = <0x25000 0x1000 0xb1030 0x4>;
+};
+
+&enet1 {
+ #address-cells = <1>;
+ #size-cells = <1>;
+ device_type = "network";
+ model = "eTSEC";
+ compatible = "fsl,etsec2";
+ fsl,num_rx_queues = <0x8>;
+ fsl,num_tx_queues = <0x8>;
+ fsl,magic-packet;
+ local-mac-address = [ 00 00 00 00 00 00 ];
+
+ queue-group@0 {
+ #address-cells = <1>;
+ #size-cells = <1>;
+ reg = <0xb1000 0x1000>;
+ interrupts = <35 2 0 0 36 2 0 0 40 2 0 0>;
+ };
+
+ queue-group@1 {
+ #address-cells = <1>;
+ #size-cells = <1>;
+ reg = <0xb5000 0x1000>;
+ interrupts = <51 2 0 0 52 2 0 0 67 2 0 0>;
+ };
+};
diff --git a/arch/powerpc/boot/dts/fsl/pq3-etsec2-2.dtsi b/arch/powerpc/boot/dts/fsl/pq3-etsec2-2.dtsi
new file mode 100644
index 0000000..50a078a
--- /dev/null
+++ b/arch/powerpc/boot/dts/fsl/pq3-etsec2-2.dtsi
@@ -0,0 +1,32 @@
+&mdio2 {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ compatible = "fsl,etsec2-tbi";
+ reg = <0x26000 0x1000 0xb1030 0x4>;
+};
+
+&enet2 {
+ #address-cells = <1>;
+ #size-cells = <1>;
+ device_type = "network";
+ model = "eTSEC";
+ compatible = "fsl,etsec2";
+ fsl,num_rx_queues = <0x8>;
+ fsl,num_tx_queues = <0x8>;
+ fsl,magic-packet;
+ local-mac-address = [ 00 00 00 00 00 00 ];
+
+ queue-group@0 {
+ #address-cells = <1>;
+ #size-cells = <1>;
+ reg = <0xb2000 0x1000>;
+ interrupts = <31 2 0 0 32 2 0 0 33 2 0 0>;
+ };
+
+ queue-group@1 {
+ #address-cells = <1>;
+ #size-cells = <1>;
+ reg = <0xb6000 0x1000>;
+ interrupts = <25 2 0 0 26 2 0 0 27 2 0 0>;
+ };
+};
diff --git a/arch/powerpc/boot/dts/fsl/pq3-i2c-0.dtsi b/arch/powerpc/boot/dts/fsl/pq3-i2c-0.dtsi
new file mode 100644
index 0000000..ef75cca
--- /dev/null
+++ b/arch/powerpc/boot/dts/fsl/pq3-i2c-0.dtsi
@@ -0,0 +1,9 @@
+&i2c0 {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ cell-index = <0>;
+ compatible = "fsl-i2c";
+ reg = <0x3000 0x100>;
+ interrupts = <43 2 0 0>;
+ dfsrr;
+};
diff --git a/arch/powerpc/boot/dts/fsl/pq3-i2c-1.dtsi b/arch/powerpc/boot/dts/fsl/pq3-i2c-1.dtsi
new file mode 100644
index 0000000..e24043a
--- /dev/null
+++ b/arch/powerpc/boot/dts/fsl/pq3-i2c-1.dtsi
@@ -0,0 +1,9 @@
+&i2c1 {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ cell-index = <1>;
+ compatible = "fsl-i2c";
+ reg = <0x3100 0x100>;
+ interrupts = <43 2 0 0>;
+ dfsrr;
+};
--
1.7.3.4
^ permalink raw reply related
* [RFC][PATCH 2/2] powerpc/85xx: Introduce a P1020 SoC device tree
From: Kumar Gala @ 2011-10-20 7:24 UTC (permalink / raw)
To: linuxppc-dev; +Cc: devicetree-discuss
In-Reply-To: <1319095467-6229-1-git-send-email-galak@kernel.crashing.org>
Create a P1020 SoC dts stub that can be included by a board that
utilizes the P1020 SoC. The board can amend any board specific
configuration or paramaters (like locaation of CCSRBAR, PCIe
controllers, I2C components, ethernet PHY information, etc.)
Signed-off-by: Kumar Gala <galak@kernel.crashing.org>
---
arch/powerpc/boot/dts/p1020soc.dtsi | 262 +++++++++++++++++++++++++++++++++++
1 files changed, 262 insertions(+), 0 deletions(-)
create mode 100644 arch/powerpc/boot/dts/p1020soc.dtsi
diff --git a/arch/powerpc/boot/dts/p1020soc.dtsi b/arch/powerpc/boot/dts/p1020soc.dtsi
new file mode 100644
index 0000000..0abc015
--- /dev/null
+++ b/arch/powerpc/boot/dts/p1020soc.dtsi
@@ -0,0 +1,262 @@
+&lbc {
+ #address-cells = <2>;
+ #size-cells = <1>;
+ compatible = "fsl,p1020-elbc", "fsl,elbc", "simple-bus";
+ interrupts = <19 2 0 0>;
+};
+
+&pci0 {
+ compatible = "fsl,mpc8548-pcie";
+ device_type = "pci";
+ #size-cells = <2>;
+ #address-cells = <3>;
+ bus-range = <0 255>;
+ clock-frequency = <33333333>;
+ interrupts = <16 2 0 0>;
+
+ pcie@0 {
+ reg = <0 0 0 0 0>;
+ #interrupt-cells = <1>;
+ #size-cells = <2>;
+ #address-cells = <3>;
+ device_type = "pci";
+ interrupts = <16 2 0 0>;
+ interrupt-map-mask = <0xf800 0 0 7>;
+ interrupt-map = <
+ /* IDSEL 0x0 */
+ 0000 0x0 0x0 0x1 &mpic 0x4 0x1
+ 0000 0x0 0x0 0x2 &mpic 0x5 0x1
+ 0000 0x0 0x0 0x3 &mpic 0x6 0x1
+ 0000 0x0 0x0 0x4 &mpic 0x7 0x1
+ >;
+ };
+
+};
+
+&pci1 {
+ compatible = "fsl,mpc8548-pcie";
+ device_type = "pci";
+ #size-cells = <2>;
+ #address-cells = <3>;
+ bus-range = <0 255>;
+ clock-frequency = <33333333>;
+ interrupts = <16 2 0 0>;
+
+ pcie@0 {
+ reg = <0 0 0 0 0>;
+ #interrupt-cells = <1>;
+ #size-cells = <2>;
+ #address-cells = <3>;
+ device_type = "pci";
+ interrupts = <16 2 0 0>;
+ interrupt-map-mask = <0xf800 0 0 7>;
+
+ interrupt-map = <
+ /* IDSEL 0x0 */
+ 0000 0x0 0x0 0x1 &mpic 0x0 0x1
+ 0000 0x0 0x0 0x2 &mpic 0x1 0x1
+ 0000 0x0 0x0 0x3 &mpic 0x2 0x1
+ 0000 0x0 0x0 0x4 &mpic 0x3 0x1
+ >;
+ };
+};
+
+
+
+&soc {
+ #address-cells = <1>;
+ #size-cells = <1>;
+ device_type = "soc";
+ compatible = "fsl,p1020-immr", "simple-bus";
+ bus-frequency = <0>; // Filled out by uboot.
+
+ ecm-law@0 {
+ compatible = "fsl,ecm-law";
+ reg = <0x0 0x1000>;
+ fsl,num-laws = <12>;
+ };
+
+ ecm@1000 {
+ compatible = "fsl,p1020-ecm", "fsl,ecm";
+ reg = <0x1000 0x1000>;
+ interrupts = <16 2 0 0>;
+ };
+
+ memory-controller@2000 {
+ compatible = "fsl,p1020-memory-controller";
+ reg = <0x2000 0x1000>;
+ interrupts = <16 2 0 0>;
+ };
+
+ i2c0: i2c@3000 {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ cell-index = <0>;
+ compatible = "fsl-i2c";
+ reg = <0x3000 0x100>;
+ interrupts = <43 2 0 0>;
+ dfsrr;
+ };
+
+ i2c1: i2c@3100 {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ cell-index = <1>;
+ compatible = "fsl-i2c";
+ reg = <0x3100 0x100>;
+ interrupts = <43 2 0 0>;
+ dfsrr;
+ };
+
+ serial0: serial@4500 {
+ };
+
+ serial1: serial@4600 {
+ };
+
+ spi@7000 {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ compatible = "fsl,p1020-espi", "fsl,mpc8536-espi";
+ reg = <0x7000 0x1000>;
+ interrupts = <59 0x2 0 0>;
+ fsl,espi-num-chipselects = <4>;
+ };
+
+ gpio: gpio-controller@f000 {
+ #gpio-cells = <2>;
+ compatible = "fsl,mpc8572-gpio";
+ reg = <0xf000 0x100>;
+ interrupts = <47 0x2 0 0>;
+ gpio-controller;
+ };
+
+ L2: l2-cache-controller@20000 {
+ compatible = "fsl,p1020-l2-cache-controller";
+ reg = <0x20000 0x1000>;
+ cache-line-size = <32>; // 32 bytes
+ cache-size = <0x40000>; // L2,256K
+ interrupts = <16 2 0 0>;
+ };
+
+ dma0: dma@21300 {
+ };
+
+ mdio0: mdio@24000 {
+ };
+
+ mdio1: mdio@25000 {
+ };
+
+ mdio2: mdio@26000 {
+ };
+
+ enet0: ethernet@b0000 {
+ };
+
+ enet1: ethernet@b1000 {
+ };
+
+ enet2: ethernet@b2000 {
+ };
+
+ usb@22000 {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ compatible = "fsl-usb2-dr";
+ reg = <0x22000 0x1000>;
+ interrupts = <28 0x2 0 0>;
+ };
+
+ /* USB2 is shared with localbus, so it must be disabled
+ by default. We can't put 'status = "disabled";' here
+ since U-Boot doesn't clear the status property when
+ it enables USB2. OTOH, U-Boot does create a new node
+ when there isn't any. So, just comment it out.
+ usb@23000 {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ compatible = "fsl-usb2-dr";
+ reg = <0x23000 0x1000>;
+ interrupts = <46 0x2 0 0>;
+ phy_type = "ulpi";
+ };
+ */
+
+ sdhci@2e000 {
+ compatible = "fsl,p1020-esdhc", "fsl,esdhc";
+ reg = <0x2e000 0x1000>;
+ interrupts = <72 0x2 0 0>;
+ /* Filled in by U-Boot */
+ clock-frequency = <0>;
+ };
+
+ crypto@30000 {
+ compatible = "fsl,sec3.3", "fsl,sec3.1", "fsl,sec3.0",
+ "fsl,sec2.4", "fsl,sec2.2", "fsl,sec2.1",
+ "fsl,sec2.0";
+ reg = <0x30000 0x10000>;
+ interrupts = <45 2 0 0 58 2 0 0>;
+ fsl,num-channels = <4>;
+ fsl,channel-fifo-len = <24>;
+ fsl,exec-units-mask = <0x97c>;
+ fsl,descriptor-types-mask = <0x3a30abf>;
+ };
+
+ mpic: pic@40000 {
+ interrupt-controller;
+ #address-cells = <0>;
+ #interrupt-cells = <4>;
+ reg = <0x40000 0x40000>;
+ compatible = "chrp,open-pic";
+ device_type = "open-pic";
+ };
+
+ timer@41100 {
+ compatible = "fsl,mpic-global-timer";
+ reg = <0x41100 0x100 0x41300 4>;
+ interrupts = <0 0 3 0
+ 1 0 3 0
+ 2 0 3 0
+ 3 0 3 0>;
+ };
+
+ timer@42100 {
+ compatible = "fsl,mpic-global-timer";
+ reg = <0x42100 0x100 0x42300 4>;
+ interrupts = <4 0 3 0
+ 5 0 3 0
+ 6 0 3 0
+ 7 0 3 0>;
+ };
+
+ msi@41600 {
+ compatible = "fsl,p1020-msi", "fsl,mpic-msi";
+ reg = <0x41600 0x80>;
+ msi-available-ranges = <0 0x100>;
+ interrupts = <
+ 0xe0 0 0 0
+ 0xe1 0 0 0
+ 0xe2 0 0 0
+ 0xe3 0 0 0
+ 0xe4 0 0 0
+ 0xe5 0 0 0
+ 0xe6 0 0 0
+ 0xe7 0 0 0>;
+ };
+
+ global-utilities@e0000 { //global utilities block
+ compatible = "fsl,p1020-guts","fsl,p2020-guts";
+ reg = <0xe0000 0x1000>;
+ fsl,has-rstcr;
+ };
+};
+
+/include/ "fsl/pq3-dma-0.dtsi"
+/include/ "fsl/pq3-duart-0.dtsi"
+/include/ "fsl/pq3-duart-1.dtsi"
+/include/ "fsl/pq3-etsec2-0.dtsi"
+/include/ "fsl/pq3-etsec2-1.dtsi"
+/include/ "fsl/pq3-etsec2-2.dtsi"
+/include/ "fsl/pq3-i2c-0.dtsi"
+/include/ "fsl/pq3-i2c-1.dtsi"
--
1.7.3.4
^ permalink raw reply related
* Re: [PATCH v15 01/10] USB/ppc4xx: Add Synopsys DesignWare HS USB OTG Register definitions
From: Pratyush Anand @ 2011-10-20 8:42 UTC (permalink / raw)
To: tmarri; +Cc: Mark Miesfeld, greg, linux-usb, linuxppc-dev, Fushen Chen
In-Reply-To: <1318630119-14165-1-git-send-email-tmarri@apm.com>
On Sat, Oct 15, 2011 at 3:38 AM, <tmarri@apm.com> wrote:
> From: Tirumala Marri <tmarri@apm.com>
>
[...]
> +/*
> + * These Macros represents the bit fields in the FIFO Size Registers (HPTXFSIZ,
> + * GNPTXFSIZ, DPTXFSIZn). Read the register into the u32 element then
> + * read out the bits using the bit elements.
> + */
> +#define DWC_RX_FIFO_DEPTH_RD(reg) (((reg) & ((u32)0xffff << 16)) >> 16)
> +#define DWC_RX_FIFO_DEPTH_WR(reg, x) \
> + (((reg) & (~((u32)0xffff << 16))) | ((x) << 16))
> +#define DWC_RX_FIFO_START_ADDR_RD(reg) ((reg) & 0xffff)
> +#define DWC_RX_FIFO_START_ADDR_WR(reg, x) \
> + (((reg) & (~((u32)0xffff))) | (x))
> +
These are GNPTX and DPTX registers.
So name should be DWC_TX_FIFO_DEPTH_RD rather than DWC_RX_FIFO_DEPTH_RD.
Same for other defines.
> +/*
> + * These Macros represents the bit fields in the Non-Periodic Tx FIFO/Queue
> +
[...]
> +#define MAX_PERIO_FIFOS 15 /* Max periodic FIFOs */
> +#define MAX_TX_FIFOS 15 /* Max non-periodic FIFOs */
> +
> +/* Maximum number of Endpoints/HostChannels */
> +#define MAX_EPS_CHANNELS 12 /* This come from device tree or defconfig */
There could be some platform even with 16 channels (as specs permits it.)
So, please make it 16.
^ permalink raw reply
* Re: [PATCH v14 03/10] USB/ppc4xx: Add Synopsys DWC OTG Core Interface Layer (CIL)
From: Pratyush Anand @ 2011-10-20 9:12 UTC (permalink / raw)
To: tmarri; +Cc: Mark Miesfeld, greg, linux-usb, linuxppc-dev, Fushen Chen
In-Reply-To: <1317954637-11802-1-git-send-email-tmarri@apm.com>
On Fri, Oct 7, 2011 at 8:00 AM, <tmarri@apm.com> wrote:
> From: Tirumala Marri <tmarri@apm.com>
>
[...]
> + * Do core a soft reset of the core. Be careful with this because it
> + * resets all the internal state machines of the core.
> + */
> +static void dwc_otg_core_reset(struct core_if *core_if)
> +{
> + ulong global_regs = core_if->core_global_regs;
> + u32 greset = 0;
> + int count = 0;
> +
> + /* Wait for AHB master IDLE state. */
> + do {
> + udelay(10);
> + greset = dwc_reg_read(global_regs, DWC_GRSTCTL);
> + if (++count > 100000) {
> + pr_warning("%s() HANG! AHB Idle GRSTCTL=%0x\n",
> + __func__, greset);
> + return;
> + }
> + } while (greset & DWC_RSTCTL_AHB_IDLE);
As per sepcs:
Bit 31: AHB Master Idle (AHBIdle): Indicates that the AHB Master State
Machine is in the IDLE condition.
So when this bit is 1 , AHB would be idle. So, what do you want here?
If AHB is idle, control wil return from above loop rather going ahead to execute
this function.
> +
> + /* Core Soft Reset */
> + count = 0;
> + greset |= DWC_RSTCTL_SFT_RST;
[...]
> + /* Rx FIFO */
> + dwc_reg_write(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);
So, if you will follow the commnet of patch 01 then , here two you will have to
rename as TX_FIFO.
Also, at all other occurances.
> + nptxsize =
> + DWC_RX_FIFO_START_ADDR_WR(nptxsize,
> + params->dev_rx_fifo_size);
> + dwc_reg_write(regs, DWC_GNPTXFSIZ, nptxsize);
> +
> + 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++) {
Reading hwcfg4 will give you maximum nuber of fifos provided by hardware.
But, a particular application (platform) may not need all those fifo. Since, you
have a vriable fifo_num in your param list, so why not use that?
> + ptxsize =
> + DWC_RX_FIFO_DEPTH_WR(ptxsize,
> + params->
> + dev_perio_tx_fifo_size[i]);
> + dwc_reg_write(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 {
> + 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_reg_write(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++) {
Same as above
> + txsize =
> + DWC_RX_FIFO_DEPTH_WR(txsize,
> + params->dev_tx_fifo_size[i]);
> + dwc_reg_write(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)));
> + }
> + }
> +}
> +
Regards
Pratyush
^ permalink raw reply
* Re: [PATCH v15 04/10] USB/ppc4xx: Add Synopsys DWC OTG HCD function
From: Pratyush Anand @ 2011-10-20 9:13 UTC (permalink / raw)
To: tmarri; +Cc: Mark Miesfeld, greg, linux-usb, linuxppc-dev, Fushen Chen
In-Reply-To: <1318630133-14264-1-git-send-email-tmarri@apm.com>
On Sat, Oct 15, 2011 at 3:38 AM, <tmarri@apm.com> wrote:
> From: Tirumala Marri <tmarri@apm.com>
>
[...]
Everything fine in this patch, except the defines of TX
regs(DWC_RX_FIFO_DEPTH_WR) has been
kept with RX.
Regards
Pratyush
^ permalink raw reply
* Re: [PATCH v15 05/10] USB/ppc4xx: Add Synopsys DWC OTG HCD interrupt function
From: Pratyush Anand @ 2011-10-20 9:25 UTC (permalink / raw)
To: tmarri; +Cc: Mark Miesfeld, greg, linux-usb, linuxppc-dev, Fushen Chen
In-Reply-To: <1318630139-14296-1-git-send-email-tmarri@apm.com>
On Sat, Oct 15, 2011 at 3:38 AM, <tmarri@apm.com> wrote:
> From: Tirumala Marri <tmarri@apm.com>
>
> Implements DWC OTG USB HCD interrupt service routine.
>
> Signed-off-by: Tirumala R Marri <tmarri@apm.com>
> Signed-off-by: Fushen Chen <fchen@apm.com>
> Signed-off-by: Mark Miesfeld <mmiesfeld@apm.com>
> ---
> =A0drivers/usb/dwc/hcd_intr.c | 1477 ++++++++++++++++++++++++++++++++++++=
++++++++
> =A01 files changed, 1477 insertions(+), 0 deletions(-)
> =A0create mode 100644 drivers/usb/dwc/hcd_intr.c
>
> diff --git a/drivers/usb/dwc/hcd_intr.c b/drivers/usb/dwc/hcd_intr.c
> new file mode 100644
> index 0000000..b16934d
> --- /dev/null
> +++ b/drivers/usb/dwc/hcd_intr.c
> @@ -0,0 +1,1477 @@
> +/*
> + * DesignWare HS OTG controller driver
> + * Copyright (C) 2006 Synopsys, Inc.
> + * Portions Copyright (C) 2010 Applied Micro Circuits Corporation.
> + *
> + * This program is free software: you can redistribute it and/or
> + * modify it under the terms of the GNU General Public License
> + * version 2 as published by the Free Software Foundation.
> + *
> + * This program is distributed in the hope that it will be useful
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
> + * GNU General Public License version 2 for more details.
> + *
> + * You should have received a copy of the GNU General Public License
> + * along with this program; if not, see http://www.gnu.org/licenses
> + * or write to the Free Software Foundation, Inc., 51 Franklin Street,
> + * Suite 500, Boston, MA 02110-1335 USA.
> + *
> + * Based on Synopsys driver version 2.60a
> + * Modified by Mark Miesfeld <mmiesfeld@apm.com>
> + * Modified by Stefan Roese <sr@denx.de>, DENX Software Engineering
> + * Modified by Chuck Meade <chuck@theptrgroup.com>
> + *
> + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "=
AS IS"
> + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING BUT NOT LIMITED TO T=
HE
> + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PU=
RPOSE
> + * ARE DISCLAIMED. IN NO EVENT SHALL SYNOPSYS, INC. BE LIABLE FOR ANY DI=
RECT,
> + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES
> + * (INCLUDING BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SER=
VICES;
> + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUS=
ED AND
> + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY OR =
TORT
> + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE=
OF
> + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
> + *
> + */
> +
> +#include "hcd.h"
> +
> +/* This file contains the implementation of the HCD Interrupt handlers. =
=A0 =A0 =A0 =A0*/
> +static const int erratum_usb09_patched;
> +static const int deferral_on =3D 1;
> +static const int nak_deferral_delay =3D 8;
> +static const int nyet_deferral_delay =3D 1;
> +
> +/**
> + * Handles the start-of-frame interrupt in host mode. Non-periodic
> + * transactions may be queued to the DWC_otg controller for the current
> + * (micro)frame. Periodic transactions may be queued to the controller f=
or the
> + * next (micro)frame.
> + */
> +static int dwc_otg_hcd_handle_sof_intr(struct dwc_hcd *hcd)
> +{
> + =A0 =A0 =A0 u32 hfnum =3D 0;
> + =A0 =A0 =A0 struct list_head *qh_entry;
> + =A0 =A0 =A0 struct dwc_qh *qh;
> + =A0 =A0 =A0 enum dwc_transaction_type tr_type;
> + =A0 =A0 =A0 u32 gintsts =3D 0;
> +
> + =A0 =A0 =A0 hfnum =3D
> + =A0 =A0 =A0 =A0 =A0 dwc_reg_read(hcd->core_if->host_if->host_global_reg=
s,
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0DWC_HFNUM);
> +
> + =A0 =A0 =A0 hcd->frame_number =3D DWC_HFNUM_FRNUM_RD(hfnum);
> +
> + =A0 =A0 =A0 /* Determine whether any periodic QHs should be executed. *=
/
> + =A0 =A0 =A0 qh_entry =3D hcd->periodic_sched_inactive.next;
> + =A0 =A0 =A0 while (qh_entry !=3D &hcd->periodic_sched_inactive) {
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 qh =3D list_entry(qh_entry, struct dwc_qh, =
qh_list_entry);
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 qh_entry =3D qh_entry->next;
> +
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 /*
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0* If needed, move QH to the ready list t=
o be executed next
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0* (micro)frame.
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0*/
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 if (dwc_frame_num_le(qh->sched_frame, hcd->=
frame_number))
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 list_move(&qh->qh_list_entr=
y,
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 &hcd->p=
eriodic_sched_ready);
> + =A0 =A0 =A0 }
> +
> + =A0 =A0 =A0 tr_type =3D dwc_otg_hcd_select_transactions(hcd);
> + =A0 =A0 =A0 if (tr_type !=3D DWC_OTG_TRANSACTION_NONE)
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 dwc_otg_hcd_queue_transactions(hcd, tr_type=
);
> +
> + =A0 =A0 =A0 /* Clear interrupt */
> + =A0 =A0 =A0 gintsts |=3D DWC_INTMSK_STRT_OF_FRM;
> + =A0 =A0 =A0 dwc_reg_write(gintsts_reg(hcd), 0, gintsts);
> + =A0 =A0 =A0 return 1;
> +}
> +
> +/**
> + * Handles the Rx Status Queue Level Interrupt, which indicates that the=
re is at
> + * least one packet in the Rx FIFO. =A0The packets are moved from the FI=
FO to
> + * memory if the DWC_otg controller is operating in Slave mode.
> + */
> +static int dwc_otg_hcd_handle_rx_status_q_level_intr(struct dwc_hcd *hcd=
)
> +{
> + =A0 =A0 =A0 u32 grxsts;
> + =A0 =A0 =A0 struct dwc_hc *hc;
> +
> + =A0 =A0 =A0 grxsts =3D dwc_reg_read(hcd->core_if->core_global_regs, DWC=
_GRXSTSP);
> + =A0 =A0 =A0 hc =3D hcd->hc_ptr_array[grxsts & DWC_HM_RXSTS_CHAN_NUM_RD(=
grxsts)];
> +
> + =A0 =A0 =A0 /* Packet Status */
> + =A0 =A0 =A0 switch (DWC_HM_RXSTS_PKT_STS_RD(grxsts)) {
> + =A0 =A0 =A0 case DWC_GRXSTS_PKTSTS_IN:
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 /* Read the data into the host buffer. */
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 if (DWC_HM_RXSTS_BYTE_CNT_RD(grxsts) > 0) {
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 dwc_otg_read_packet(hcd->co=
re_if, hc->xfer_buff,
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0=
=A0 =A0 =A0 DWC_HM_RXSTS_BYTE_CNT_RD(grxsts));
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 /* Update the HC fields for=
the next packet received. */
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 hc->xfer_count +=3D DWC_HM_=
RXSTS_BYTE_CNT_RD(grxsts);
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 hc->xfer_buff +=3D DWC_HM_R=
XSTS_BYTE_CNT_RD(grxsts);
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 }
> + =A0 =A0 =A0 case DWC_GRXSTS_PKTSTS_IN_XFER_COMP:
> + =A0 =A0 =A0 case DWC_GRXSTS_PKTSTS_DATA_TOGGLE_ERR:
> + =A0 =A0 =A0 case DWC_GRXSTS_PKTSTS_CH_HALTED:
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 /* Handled in interrupt, just ignore data *=
/
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 break;
> + =A0 =A0 =A0 default:
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 pr_err("RX_STS_Q Interrupt: Unknown status =
%d\n",
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0DWC_HM_RXSTS_PKT_STS_RD(grxs=
ts));
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 break;
> + =A0 =A0 =A0 }
> + =A0 =A0 =A0 return 1;
> +}
> +
> +/**
> + * This interrupt occurs when the non-periodic Tx FIFO is half-empty. Mo=
re
> + * data packets may be written to the FIFO for OUT transfers. More reque=
sts
> + * may be written to the non-periodic request queue for IN transfers. Th=
is
> + * interrupt is enabled only in Slave mode.
> + */
> +static int dwc_otg_hcd_handle_np_tx_fifo_empty_intr(struct dwc_hcd *hcd)
> +{
> + =A0 =A0 =A0 dwc_otg_hcd_queue_transactions(hcd, DWC_OTG_TRANSACTION_NON=
_PERIODIC);
> + =A0 =A0 =A0 return 1;
> +}
> +
> +/**
> + * This interrupt occurs when the periodic Tx FIFO is half-empty. More d=
ata
> + * packets may be written to the FIFO for OUT transfers. More requests m=
ay be
> + * written to the periodic request queue for IN transfers. This interrup=
t is
> + * enabled only in Slave mode.
> + */
> +static int dwc_otg_hcd_handle_perio_tx_fifo_empty_intr(struct dwc_hcd *h=
cd)
> +{
> + =A0 =A0 =A0 dwc_otg_hcd_queue_transactions(hcd, DWC_OTG_TRANSACTION_PER=
IODIC);
> + =A0 =A0 =A0 return 1;
> +}
> +
> +/**
> + * When the port changes to enabled it may be necessary to adjust the ph=
y clock
> + * speed.
> + */
> +static int adjusted_phy_clock_speed(struct dwc_hcd *hcd, u32 hprt0)
> +{
> + =A0 =A0 =A0 int adjusted =3D 0;
> + =A0 =A0 =A0 u32 usbcfg;
> + =A0 =A0 =A0 ulong global_regs =3D hcd->core_if->core_global_regs;
> + =A0 =A0 =A0 struct core_params *params =3D hcd->core_if->core_params;
> + =A0 =A0 =A0 ulong h_regs =3D hcd->core_if->host_if->host_global_regs;
> +
> + =A0 =A0 =A0 usbcfg =3D dwc_reg_read(global_regs, DWC_GUSBCFG);
> +
> + =A0 =A0 =A0 if (DWC_HPRT0_PRT_SPD_RD(hprt0) =3D=3D DWC_HPRT0_PRTSPD_LOW=
_SPEED ||
> + =A0 =A0 =A0 =A0 =A0 DWC_HPRT0_PRT_SPD_RD(hprt0) =3D=3D DWC_HPRT0_PRTSPD=
_FULL_SPEED) {
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 /* Low power */
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 u32 hcfg;
> +
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 if (!(usbcfg & DWC_USBCFG_PHYLPWRCLKSEL)) {
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 /* Set PHY low power clock =
select for FS/LS devices */
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 usbcfg |=3D DWC_USBCFG_PHYL=
PWRCLKSEL;
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 dwc_reg_write(global_regs, =
DWC_GUSBCFG, usbcfg);
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 adjusted =3D 1;
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 }
> +
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 hcfg =3D dwc_reg_read(h_regs, DWC_HCFG);
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 if (DWC_HPRT0_PRT_SPD_RD(hprt0) =3D=3D DWC_=
HPRT0_PRTSPD_LOW_SPEED &&
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 params->host_ls_low_power_phy_clk =
=3D=3D
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 DWC_HOST_LS_LOW_POWER_PHY_CLK_PARAM=
_6MHZ) {
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 /* 6 MHZ, check for 6 MHZ c=
lock select */
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 if (DWC_HCFG_FSLSP_CLK_RD(h=
cfg) !=3D DWC_HCFG_6_MHZ) {
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 hcfg =3D DW=
C_HCFG_FSLSP_CLK_RW(hcfg,
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0=
=A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0DWC_HCFG_6_MHZ);
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 dwc_reg_wri=
te(h_regs, DWC_HCFG, hcfg);
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 adjusted =
=3D 1;
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 }
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 } else if (DWC_HCFG_FSLSP_CLK_RD(hcfg) !=3D=
DWC_HCFG_48_MHZ) {
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 /* 48 MHZ and clock select =
is not 48 MHZ */
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 hcfg =3D DWC_HCFG_FSLSP_CLK=
_RW(hcfg, DWC_HCFG_48_MHZ);
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 dwc_reg_write(h_regs, DWC_H=
CFG, hcfg);
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 adjusted =3D 1;
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 }
> + =A0 =A0 =A0 } else if (usbcfg & DWC_USBCFG_PHYLPWRCLKSEL) {
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 usbcfg &=3D ~((u32) DWC_USBCFG_PHYLPWRCLKSE=
L);
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 dwc_reg_write(global_regs, DWC_GUSBCFG, usb=
cfg);
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 adjusted =3D 1;
> + =A0 =A0 =A0 }
> + =A0 =A0 =A0 if (adjusted)
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 schedule_work(&hcd->usb_port_reset);
> +
> + =A0 =A0 =A0 return adjusted;
> +}
> +
> +/**
> + * Helper function to handle the port enable changed interrupt when the =
port
> + * becomes enabled. =A0Checks if we need to adjust the PHY clock speed f=
or low
> + * power and =A0adjusts it if needed.
> + */
> +static void port_enabled(struct dwc_hcd *hcd, u32 hprt0)
> +{
> + =A0 =A0 =A0 if (hcd->core_if->core_params->host_support_fs_ls_low_power=
)
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 if (!adjusted_phy_clock_speed(hcd, hprt0))
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 hcd->flags.b.port_reset_cha=
nge =3D 1;
> +}
> +
> +/**
> + * There are multiple conditions that can cause a port interrupt. This f=
unction
> + * determines which interrupt conditions have occurred and handles them
> + * appropriately.
> + */
> +static int dwc_otg_hcd_handle_port_intr(struct dwc_hcd *hcd)
> +{
> + =A0 =A0 =A0 int retval =3D 0;
> + =A0 =A0 =A0 u32 hprt0;
> + =A0 =A0 =A0 u32 hprt0_modify;
> +
> + =A0 =A0 =A0 hprt0 =3D dwc_reg_read(hcd->core_if->host_if->hprt0, 0);
> + =A0 =A0 =A0 hprt0_modify =3D dwc_reg_read(hcd->core_if->host_if->hprt0,=
0);
> +
> + =A0 =A0 =A0 /*
> + =A0 =A0 =A0 =A0* Clear appropriate bits in HPRT0 to clear the interrupt=
bit in
> + =A0 =A0 =A0 =A0* GINTSTS
> + =A0 =A0 =A0 =A0*/
> + =A0 =A0 =A0 hprt0_modify =3D DWC_HPRT0_PRT_ENA_RW(hprt0_modify, 0);
> + =A0 =A0 =A0 hprt0_modify =3D DWC_HPRT0_PRT_CONN_DET_RW(hprt0_modify, 0)=
;
> + =A0 =A0 =A0 hprt0_modify =3D DWC_HPRT0_PRT_ENA_DIS_CHG_RW(hprt0_modify,=
0);
> + =A0 =A0 =A0 hprt0_modify =3D DWC_HPRT0_PRT_OVRCURR_CHG_RW(hprt0_modify,=
0);
> +
> + =A0 =A0 =A0 /* Port connect detected interrupt */
> + =A0 =A0 =A0 if (DWC_HPRT0_PRT_CONN_DET_RD(hprt0)) {
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 /* Set the status flags and clear interrupt=
*/
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 hcd->flags.b.port_connect_status_change =3D=
1;
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 hcd->flags.b.port_connect_status =3D 1;
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 hprt0_modify =3D DWC_HPRT0_PRT_CONN_DET_RW(=
hprt0_modify, 1);
> +
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 /* B-Device has connected, Delete the conne=
ction timer. */
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 del_timer_sync(&hcd->conn_timer);
> +
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 /*
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0* The Hub driver asserts a reset when it=
sees port connect
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0* status change flag
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0*/
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 retval |=3D 1;
> + =A0 =A0 =A0 }
> +
> + =A0 =A0 =A0 /* Port enable changed interrupt */
> + =A0 =A0 =A0 if (DWC_HPRT0_PRT_ENA_DIS_CHG_RD(hprt0)) {
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 /* Set the internal flag if the port was di=
sabled */
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 if (DWC_HPRT0_PRT_ENA_RD(hprt0))
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 port_enabled(hcd, hprt0);
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 else
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 hcd->flags.b.port_enable_ch=
ange =3D 1;
> +
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 /* Clear the interrupt */
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 hprt0_modify =3D DWC_HPRT0_PRT_ENA_DIS_CHG_=
RW(hprt0_modify, 1);
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 retval |=3D 1;
> + =A0 =A0 =A0 }
> +
> + =A0 =A0 =A0 /* Overcurrent change interrupt */
> + =A0 =A0 =A0 if (DWC_HPRT0_PRT_OVRCURR_CHG_RD(hprt0)) {
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 hcd->flags.b.port_over_current_change =3D 1=
;
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 hprt0_modify =3D DWC_HPRT0_PRT_OVRCURR_CHG_=
RW(hprt0_modify, 1);
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 retval |=3D 1;
> + =A0 =A0 =A0 }
> +
> + =A0 =A0 =A0 /* Clear the port interrupts */
> + =A0 =A0 =A0 dwc_reg_write(hcd->core_if->host_if->hprt0, 0, hprt0_modify=
);
> + =A0 =A0 =A0 return retval;
> +}
> +
> +/**
> + * Gets the actual length of a transfer after the transfer halts. halt_s=
tatus
> + * holds the reason for the halt.
> + *
> + * For IN transfers where halt_status is DWC_OTG_HC_XFER_COMPLETE, _shor=
t_read
> + * is set to 1 upon return if less than the requested number of bytes we=
re
> + * transferred. Otherwise, _short_read is set to 0 upon return. _short_r=
ead may
> + * also be NULL on entry, in which case it remains unchanged.
> + */
> +static u32 get_actual_xfer_length(struct dwc_hc *hc, ulong regs,
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 struct =
dwc_qtd *qtd,
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 enum dw=
c_halt_status halt_status,
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 int *_s=
hort_read)
> +{
> + =A0 =A0 =A0 u32 hctsiz =3D 0;
> + =A0 =A0 =A0 u32 length;
> +
> + =A0 =A0 =A0 if (_short_read)
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 *_short_read =3D 0;
> +
> + =A0 =A0 =A0 hctsiz =3D dwc_reg_read(regs, DWC_HCTSIZ);
> + =A0 =A0 =A0 if (halt_status =3D=3D DWC_OTG_HC_XFER_COMPLETE) {
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 if (hc->ep_is_in) {
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 length =3D hc->xfer_len - D=
WC_HCTSIZ_XFER_SIZE_RD(hctsiz);
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 if (_short_read)
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 *_short_rea=
d =3D
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 (DW=
C_HCTSIZ_XFER_SIZE_RD(hctsiz) !=3D 0);
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 } else if (hc->qh->do_split) {
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 length =3D qtd->ssplit_out_=
xfer_count;
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 } else {
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 length =3D hc->xfer_len;
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 }
> + =A0 =A0 =A0 } else {
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 /*
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0* Must use the hctsiz.pktcnt field to de=
termine how much data
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0* has been transferred. This field refle=
cts the number of
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0* packets that have been transferred via=
the USB. This is
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0* always an integral number of packets i=
f the transfer was
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0* halted before its normal completion. (=
Can't use the
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0* hctsiz.xfersize field because that ref=
lects the number of
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0* bytes transferred via the AHB, not the=
USB).
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0*/
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 length =3D (hc->start_pkt_count - DWC_HCTSI=
Z_PKT_CNT_RD(hctsiz)) *
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 hc->max_packet;
> + =A0 =A0 =A0 }
> + =A0 =A0 =A0 return length;
> +}
> +
> +/**
> + * Updates the state of the URB after a Transfer Complete interrupt on t=
he
> + * host channel. Updates the actual_length field of the URB based on the
> + * number of bytes transferred via the host channel. Sets the URB status
> + * if the data transfer is finished.
> + */
> +static int update_urb_state_xfer_comp(struct dwc_hc *hc,
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0=
ulong regs, struct urb *urb,
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0=
struct dwc_qtd *qtd, int *status)
> +{
> + =A0 =A0 =A0 int xfer_done =3D 0;
> + =A0 =A0 =A0 int short_read =3D 0;
> +
> + =A0 =A0 =A0 urb->actual_length +=3D get_actual_xfer_length(hc, regs, qt=
d,
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0=
=A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0DWC_OTG_HC_XFER_COMPLETE,
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0=
=A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0&short_read);
> +
> + =A0 =A0 =A0 if (short_read || urb->actual_length =3D=3D urb->transfer_b=
uffer_length) {
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 xfer_done =3D 1;
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 if (short_read && (urb->transfer_flags & UR=
B_SHORT_NOT_OK))
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 *status =3D -EREMOTEIO;
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 else
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 *status =3D 0;
> + =A0 =A0 =A0 }
> + =A0 =A0 =A0 return xfer_done;
> +}
> +
> +/*
> + * Save the starting data toggle for the next transfer. The data toggle =
is
> + * saved in the QH for non-control transfers and it's saved in the QTD f=
or
> + * control transfers.
> + */
> +static void save_data_toggle(struct dwc_hc *hc, ulong regs, struct dwc_q=
td *qtd)
> +{
> + =A0 =A0 =A0 u32 hctsiz =3D 0;
> + =A0 =A0 =A0 hctsiz =3D dwc_reg_read(regs, DWC_HCTSIZ);
> +
> + =A0 =A0 =A0 if (hc->ep_type !=3D DWC_OTG_EP_TYPE_CONTROL) {
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 struct dwc_qh *qh =3D hc->qh;
> +
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 if (DWC_HCTSIZ_PKT_PID_RD(hctsiz) =3D=3D DW=
C_HCTSIZ_DATA0)
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 qh->data_toggle =3D DWC_OTG=
_HC_PID_DATA0;
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 else
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 qh->data_toggle =3D DWC_OTG=
_HC_PID_DATA1;
> + =A0 =A0 =A0 } else {
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 if (DWC_HCTSIZ_PKT_PID_RD(hctsiz) =3D=3D DW=
C_HCTSIZ_DATA0)
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 qtd->data_toggle =3D DWC_OT=
G_HC_PID_DATA0;
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 else
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 qtd->data_toggle =3D DWC_OT=
G_HC_PID_DATA1;
> + =A0 =A0 =A0 }
> +}
> +
> +/**
> + * Frees the first QTD in the QH's list if free_qtd is 1. For non-period=
ic
> + * QHs, removes the QH from the active non-periodic schedule. If any QTD=
s are
> + * still linked to the QH, the QH is added to the end of the inactive
> + * non-periodic schedule. For periodic QHs, removes the QH from the peri=
odic
> + * schedule if no more QTDs are linked to the QH.
> + */
> +static void deactivate_qh(struct dwc_hcd *hcd, struct dwc_qh *qh, int fr=
ee_qtd)
> +{
> + =A0 =A0 =A0 int continue_split =3D 0;
> + =A0 =A0 =A0 struct dwc_qtd *qtd;
> +
> + =A0 =A0 =A0 qtd =3D list_entry(qh->qtd_list.next, struct dwc_qtd, qtd_l=
ist_entry);
> + =A0 =A0 =A0 if (qtd->complete_split)
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 continue_split =3D 1;
> + =A0 =A0 =A0 else if (qtd->isoc_split_pos =3D=3D DWC_HCSPLIT_XACTPOS_MID=
||
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0qtd->isoc_split_pos =3D=3D DWC_HCSPLIT_X=
ACTPOS_END)
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 continue_split =3D 1;
> +
> + =A0 =A0 =A0 if (free_qtd) {
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 dwc_otg_hcd_qtd_remove(qtd);
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 continue_split =3D 0;
> + =A0 =A0 =A0 }
> +
> + =A0 =A0 =A0 qh->channel =3D NULL;
> + =A0 =A0 =A0 qh->qtd_in_process =3D NULL;
> + =A0 =A0 =A0 dwc_otg_hcd_qh_deactivate(hcd, qh, continue_split);
> +}
> +
> +/**
> + * Updates the state of an Isochronous URB when the transfer is stopped =
for
> + * any reason. The fields of the current entry in the frame descriptor a=
rray
> + * are set based on the transfer state and the input status. Completes t=
he
> + * Isochronous URB if all the URB frames have been completed.
> + */
> +static enum dwc_halt_status update_isoc_urb_state(struct dwc_hcd *hcd,
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0=
=A0 =A0 =A0 =A0 =A0 =A0 struct dwc_hc *hc, u32 regs,
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0=
=A0 =A0 =A0 =A0 =A0 =A0 struct dwc_qtd *qtd,
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0=
=A0 =A0 =A0 =A0 =A0 =A0 enum dwc_halt_status status)
> +{
> + =A0 =A0 =A0 struct urb *urb =3D qtd->urb;
> + =A0 =A0 =A0 enum dwc_halt_status ret_val =3D status;
> + =A0 =A0 =A0 struct usb_iso_packet_descriptor *frame_desc;
> + =A0 =A0 =A0 frame_desc =3D &urb->iso_frame_desc[qtd->isoc_frame_index];
> +
> + =A0 =A0 =A0 switch (status) {
> + =A0 =A0 =A0 case DWC_OTG_HC_XFER_COMPLETE:
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 frame_desc->status =3D 0;
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 frame_desc->actual_length =3D
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 get_actual_xfer_length(hc, regs, qt=
d, status, NULL);
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 break;
> + =A0 =A0 =A0 case DWC_OTG_HC_XFER_FRAME_OVERRUN:
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 urb->error_count++;
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 if (hc->ep_is_in)
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 frame_desc->status =3D -ENO=
SR;
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 else
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 frame_desc->status =3D -ECO=
MM;
> +
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 frame_desc->actual_length =3D 0;
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 break;
> + =A0 =A0 =A0 case DWC_OTG_HC_XFER_BABBLE_ERR:
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 /* Don't need to update actual_length in th=
is case. */
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 urb->error_count++;
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 frame_desc->status =3D -EOVERFLOW;
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 break;
> + =A0 =A0 =A0 case DWC_OTG_HC_XFER_XACT_ERR:
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 urb->error_count++;
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 frame_desc->status =3D -EPROTO;
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 frame_desc->actual_length =3D
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 get_actual_xfer_length(hc, regs, qt=
d, status, NULL);
> + =A0 =A0 =A0 default:
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 pr_err("%s: Unhandled halt_status (%d)\n", =
__func__, status);
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 BUG();
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 break;
> + =A0 =A0 =A0 }
> +
> + =A0 =A0 =A0 if (++qtd->isoc_frame_index =3D=3D urb->number_of_packets) =
{
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 /*
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0* urb->status is not used for isoc trans=
fers.
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0* The individual frame_desc statuses are=
used instead.
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0*/
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 dwc_otg_hcd_complete_urb(hcd, urb, 0);
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 ret_val =3D DWC_OTG_HC_XFER_URB_COMPLETE;
> + =A0 =A0 =A0 } else {
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 ret_val =3D DWC_OTG_HC_XFER_COMPLETE;
> + =A0 =A0 =A0 }
> + =A0 =A0 =A0 return ret_val;
> +}
> +
> +/**
> + * Releases a host channel for use by other transfers. Attempts to selec=
t and
> + * queue more transactions since at least one host channel is available.
> + */
> +static void release_channel(struct dwc_hcd *hcd, struct dwc_hc *hc,
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 struct dwc_qtd *qtd=
,
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 enum dwc_halt_statu=
s halt_status, int *must_free)
> +{
> + =A0 =A0 =A0 enum dwc_transaction_type tr_type;
> + =A0 =A0 =A0 int free_qtd;
> + =A0 =A0 =A0 int deact =3D 1;
> + =A0 =A0 =A0 struct dwc_qh *qh;
> + =A0 =A0 =A0 int retry_delay =3D 1;
> +
> + =A0 =A0 =A0 switch (halt_status) {
> + =A0 =A0 =A0 case DWC_OTG_HC_XFER_NYET:
> + =A0 =A0 =A0 case DWC_OTG_HC_XFER_NAK:
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 if (halt_status =3D=3D DWC_OTG_HC_XFER_NYET=
)
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 retry_delay =3D nyet_deferr=
al_delay;
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 else
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 retry_delay =3D nak_deferra=
l_delay;
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 free_qtd =3D 0;
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 if (deferral_on && hc->do_split) {
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 qh =3D hc->qh;
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 if (qh)
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 deact =3D d=
wc_otg_hcd_qh_deferr(hcd, qh,
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0=
=A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 retry_delay);
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 }
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 break;
> + =A0 =A0 =A0 case DWC_OTG_HC_XFER_URB_COMPLETE:
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 free_qtd =3D 1;
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 break;
> + =A0 =A0 =A0 case DWC_OTG_HC_XFER_AHB_ERR:
> + =A0 =A0 =A0 case DWC_OTG_HC_XFER_STALL:
> + =A0 =A0 =A0 case DWC_OTG_HC_XFER_BABBLE_ERR:
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 free_qtd =3D 1;
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 break;
> + =A0 =A0 =A0 case DWC_OTG_HC_XFER_XACT_ERR:
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 if (qtd->error_count >=3D 3) {
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 free_qtd =3D 1;
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 dwc_otg_hcd_complete_urb(hc=
d, qtd->urb, -EPROTO);
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 } else {
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 free_qtd =3D 0;
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 }
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 break;
> + =A0 =A0 =A0 case DWC_OTG_HC_XFER_URB_DEQUEUE:
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 /*
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0* The QTD has already been removed and t=
he QH has been
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0* deactivated. Don't want to do anything=
except release the
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0* host channel and try to queue more tra=
nsfers.
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0*/
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 goto cleanup;
> + =A0 =A0 =A0 case DWC_OTG_HC_XFER_NO_HALT_STATUS:
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 pr_err("%s: No halt_status, channel %d\n", =
__func__,
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0hc->hc_num);
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 free_qtd =3D 0;
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 break;
> + =A0 =A0 =A0 default:
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 free_qtd =3D 0;
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 break;
> + =A0 =A0 =A0 }
> + =A0 =A0 =A0 if (free_qtd)
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 /* must_free pre-initialized to zero */
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 *must_free =3D 1;
> + =A0 =A0 =A0 if (deact)
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 deactivate_qh(hcd, hc->qh, free_qtd);
> +
> +cleanup:
> + =A0 =A0 =A0 /*
> + =A0 =A0 =A0 =A0* Release the host channel for use by other transfers. T=
he cleanup
> + =A0 =A0 =A0 =A0* function clears the channel interrupt enables and cond=
itions, so
> + =A0 =A0 =A0 =A0* there's no need to clear the Channel Halted interrupt =
separately.
> + =A0 =A0 =A0 =A0*/
> + =A0 =A0 =A0 dwc_otg_hc_cleanup(hcd->core_if, hc);
> + =A0 =A0 =A0 list_add_tail(&hc->hc_list_entry, &hcd->free_hc_list);
> + =A0 =A0 =A0 hcd->available_host_channels++;
> + =A0 =A0 =A0 /* Try to queue more transfers now that there's a free chan=
nel. */
> + =A0 =A0 =A0 if (!erratum_usb09_patched) {
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 tr_type =3D dwc_otg_hcd_select_transactions=
(hcd);
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 if (tr_type !=3D DWC_OTG_TRANSACTION_NONE)
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 dwc_otg_hcd_queue_transacti=
ons(hcd, tr_type);
> + =A0 =A0 =A0 }
> +}
> +
> +/**
> + * Halts a host channel. If the channel cannot be halted immediately bec=
ause
> + * the request queue is full, this function ensures that the FIFO empty
> + * interrupt for the appropriate queue is enabled so that the halt reque=
st can
> + * be queued when there is space in the request queue.
> + *
> + * This function may also be called in DMA mode. In that case, the chann=
el is
> + * simply released since the core always halts the channel automatically=
in
> + * DMA mode.
> + */
> +static void halt_channel(struct dwc_hcd *hcd, struct dwc_hc *hc,
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0struct dwc_qtd *qtd, enu=
m dwc_halt_status halt_status,
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0int *must_free)
> +{
> + =A0 =A0 =A0 if (hcd->core_if->dma_enable) {
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 release_channel(hcd, hc, qtd, halt_status, =
must_free);
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 return;
> + =A0 =A0 =A0 }
> +
> + =A0 =A0 =A0 /* Slave mode processing... */
> + =A0 =A0 =A0 dwc_otg_hc_halt(hcd->core_if, hc, halt_status);
> + =A0 =A0 =A0 if (hc->halt_on_queue) {
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 u32 gintmsk =3D 0;
> +
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 if (hc->ep_type =3D=3D DWC_OTG_EP_TYPE_CONT=
ROL ||
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 hc->ep_type =3D=3D DWC_OTG_EP_TYPE_=
BULK) {
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 /*
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0* Make sure the Non-peri=
odic Tx FIFO empty interrupt
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0* is enabled so that the=
non-periodic schedule will
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0* be processed.
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0*/
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 gintmsk |=3D DWC_INTMSK_NP_=
TXFIFO_EMPT;
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 dwc_reg_modify(gintmsk_reg(=
hcd), 0, 0, gintmsk);
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 } else {
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 /*
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0* Move the QH from the p=
eriodic queued schedule to
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0* the periodic assigned =
schedule. This allows the
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0* halt to be queued when=
the periodic schedule is
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0* processed.
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0*/
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 list_move(&hc->qh->qh_list_=
entry,
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 &hcd->p=
eriodic_sched_assigned);
> +
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 /*
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0* Make sure the Periodic=
Tx FIFO Empty interrupt is
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0* enabled so that the pe=
riodic schedule will be
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0* processed.
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0*/
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 gintmsk |=3D DWC_INTMSK_P_T=
XFIFO_EMPTY;
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 dwc_reg_modify(gintmsk_reg(=
hcd), 0, 0, gintmsk);
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 }
> + =A0 =A0 =A0 }
> +}
> +
> +/**
> + * Performs common cleanup for non-periodic transfers after a Transfer
> + * Complete interrupt. This function should be called after any endpoint=
type
> + * specific handling is finished to release the host channel.
> + */
> +static void complete_non_periodic_xfer(struct dwc_hcd *hcd, struct dwc_h=
c *hc,
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0=
=A0ulong regs, struct dwc_qtd *qtd,
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0=
=A0enum dwc_halt_status halt_status,
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0=
=A0int *must_free)
> +{
> + =A0 =A0 =A0 u32 hcint;
> +
> + =A0 =A0 =A0 qtd->error_count =3D 0;
> + =A0 =A0 =A0 hcint =3D dwc_reg_read(regs, DWC_HCINT);
> + =A0 =A0 =A0 if (DWC_HCINT_NYET_RESP_REC_RD(hcint)) {
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 u32 hcint_clear =3D 0;
> +
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 hcint_clear =3D DWC_HCINT_NYET_RESP_REC_RW(=
hcint_clear, 1);
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 /*
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0* Got a NYET on the last transaction of =
the transfer. This
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0* means that the endpoint should be in t=
he PING state at the
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0* beginning of the next transfer.
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0*/
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 hc->qh->ping_state =3D 1;
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 dwc_reg_write(regs, DWC_HCINT, hcint_clear)=
;
> + =A0 =A0 =A0 }
> +
> + =A0 =A0 =A0 /*
> + =A0 =A0 =A0 =A0* Always halt and release the host channel to make it av=
ailable for
> + =A0 =A0 =A0 =A0* more transfers. There may still be more phases for a c=
ontrol
> + =A0 =A0 =A0 =A0* transfer or more data packets for a bulk transfer at t=
his point,
> + =A0 =A0 =A0 =A0* but the host channel is still halted. A channel will b=
e reassigned
> + =A0 =A0 =A0 =A0* to the transfer when the non-periodic schedule is proc=
essed after
> + =A0 =A0 =A0 =A0* the channel is released. This allows transactions to b=
e queued
> + =A0 =A0 =A0 =A0* properly via dwc_otg_hcd_queue_transactions, which als=
o enables the
> + =A0 =A0 =A0 =A0* Tx FIFO Empty interrupt if necessary.
> + =A0 =A0 =A0 =A0*
> + =A0 =A0 =A0 =A0* IN transfers in Slave mode require an explicit disable=
to
> + =A0 =A0 =A0 =A0* halt the channel. (In DMA mode, this call simply relea=
ses
> + =A0 =A0 =A0 =A0* the channel.)
> + =A0 =A0 =A0 =A0*
> + =A0 =A0 =A0 =A0* The channel is automatically disabled by the core for =
OUT
> + =A0 =A0 =A0 =A0* transfers in Slave mode.
> + =A0 =A0 =A0 =A0*/
> + =A0 =A0 =A0 if (hc->ep_is_in)
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 halt_channel(hcd, hc, qtd, halt_status, mus=
t_free);
> + =A0 =A0 =A0 else
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 release_channel(hcd, hc, qtd, halt_status, =
must_free);
> +}
> +
> +/**
> + * Performs common cleanup for periodic transfers after a Transfer Compl=
ete
> + * interrupt. This function should be called after any endpoint type spe=
cific
> + * handling is finished to release the host channel.
> + */
> +static void complete_periodic_xfer(struct dwc_hcd *hcd, struct dwc_hc *h=
c,
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0ulon=
g regs, struct dwc_qtd *qtd,
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0enum=
dwc_halt_status halt_status,
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0int =
*must_free)
> +{
> + =A0 =A0 =A0 u32 hctsiz =3D 0;
> +
> + =A0 =A0 =A0 hctsiz =3D dwc_reg_read(regs, DWC_HCTSIZ);
> + =A0 =A0 =A0 qtd->error_count =3D 0;
> +
> + =A0 =A0 =A0 /*
> + =A0 =A0 =A0 =A0* For OUT transfers and 0 packet count, the Core halts t=
he channel,
> + =A0 =A0 =A0 =A0* otherwise, Flush any outstanding requests from the Tx =
queue.
> + =A0 =A0 =A0 =A0*/
> + =A0 =A0 =A0 if (!hc->ep_is_in || (DWC_HCTSIZ_PKT_CNT_RD(hctsiz) =3D=3D =
0))
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 release_channel(hcd, hc, qtd, halt_status, =
must_free);
> + =A0 =A0 =A0 else
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 halt_channel(hcd, hc, qtd, halt_status, mus=
t_free);
> +}
> +
> +/**
> + * Handles a host channel Transfer Complete interrupt. This handler may =
be
> + * called in either DMA mode or Slave mode.
> + */
> +static int handle_hc_xfercomp_intr(struct dwc_hcd *hcd, struct dwc_hc *h=
c,
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0ulon=
g regs, struct dwc_qtd *qtd,
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0int =
*must_free)
> +{
> + =A0 =A0 =A0 int urb_xfer_done;
> + =A0 =A0 =A0 enum dwc_halt_status halt_status =3D DWC_OTG_HC_XFER_COMPLE=
TE;
> + =A0 =A0 =A0 struct urb *urb =3D qtd->urb;
> + =A0 =A0 =A0 int pipe_type =3D usb_pipetype(urb->pipe);
> + =A0 =A0 =A0 int status =3D -EINPROGRESS;
> + =A0 =A0 =A0 u32 hcintmsk =3D 0;
> +
> + =A0 =A0 =A0 /* Handle xfer complete on CSPLIT. */
> + =A0 =A0 =A0 if (hc->qh->do_split)
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 qtd->complete_split =3D 0;
> +
> + =A0 =A0 =A0 /* Update the QTD and URB states. */
> + =A0 =A0 =A0 switch (pipe_type) {
> + =A0 =A0 =A0 case PIPE_CONTROL:
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 switch (qtd->control_phase) {
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 case DWC_OTG_CONTROL_SETUP:
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 if (urb->transfer_buffer_le=
ngth > 0)
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 qtd->contro=
l_phase =3D DWC_OTG_CONTROL_DATA;
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 else
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 qtd->contro=
l_phase =3D DWC_OTG_CONTROL_STATUS;
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 halt_status =3D DWC_OTG_HC_=
XFER_COMPLETE;
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 break;
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 case DWC_OTG_CONTROL_DATA:
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 urb_xfer_done =3D update_ur=
b_state_xfer_comp(hc, regs,
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0=
=A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0urb, qtd,
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0=
=A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0&status);
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 if (urb_xfer_done)
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 qtd->contro=
l_phase =3D DWC_OTG_CONTROL_STATUS;
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 else
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 save_data_t=
oggle(hc, regs, qtd);
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 halt_status =3D DWC_OTG_HC_=
XFER_COMPLETE;
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 break;
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 case DWC_OTG_CONTROL_STATUS:
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 if (status =3D=3D -EINPROGR=
ESS)
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 status =3D =
0;
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 dwc_otg_hcd_complete_urb(hc=
d, urb, status);
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 halt_status =3D DWC_OTG_HC_=
XFER_URB_COMPLETE;
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 break;
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 }
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 complete_non_periodic_xfer(hcd, hc, regs, q=
td,
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0=
=A0 =A0 =A0halt_status, must_free);
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 break;
> + =A0 =A0 =A0 case PIPE_BULK:
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 urb_xfer_done =3D update_urb_state_xfer_com=
p(hc, regs, urb, qtd,
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0=
=A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0&status);
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 if (urb_xfer_done) {
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 dwc_otg_hcd_complete_urb(hc=
d, urb, status);
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 halt_status =3D DWC_OTG_HC_=
XFER_URB_COMPLETE;
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 } else {
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 halt_status =3D DWC_OTG_HC_=
XFER_COMPLETE;
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 }
> +
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 save_data_toggle(hc, regs, qtd);
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 complete_non_periodic_xfer(hcd, hc, regs, q=
td,
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0=
=A0 =A0 =A0halt_status, must_free);
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 break;
> + =A0 =A0 =A0 case PIPE_INTERRUPT:
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 update_urb_state_xfer_comp(hc, regs, urb, q=
td, &status);
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 /*
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0* Interrupt URB is done on the first tra=
nsfer complete
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0* interrupt.
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0*/
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 dwc_otg_hcd_complete_urb(hcd, urb, status);
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 save_data_toggle(hc, regs, qtd);
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 complete_periodic_xfer(hcd, hc, regs, qtd,
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0=
=A0DWC_OTG_HC_XFER_URB_COMPLETE, must_free);
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 break;
> + =A0 =A0 =A0 case PIPE_ISOCHRONOUS:
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 if (qtd->isoc_split_pos =3D=3D DWC_HCSPLIT_=
XACTPOS_ALL) {
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 halt_status =3D update_isoc=
_urb_state(hcd, hc, regs, qtd,
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0=
=A0 =A0 =A0 =A0 =A0 =A0 =A0 DWC_OTG_HC_XFER_COMPLETE);
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 }
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 complete_periodic_xfer(hcd, hc, regs, qtd,
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0=
=A0halt_status, must_free);
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 break;
> + =A0 =A0 =A0 }
> +
> + =A0 =A0 =A0 /* disable xfercompl */
> + =A0 =A0 =A0 hcintmsk =3D DWC_HCINTMSK_TXFER_CMPL_RW(hcintmsk, 1);
> + =A0 =A0 =A0 dwc_reg_modify(regs, DWC_HCINTMSK, hcintmsk, 0);
> +
> + =A0 =A0 =A0 return 1;
> +}
> +
> +/**
> + * Handles a host channel STALL interrupt. This handler may be called in
> + * either DMA mode or Slave mode.
> + */
> +static int handle_hc_stall_intr(struct dwc_hcd *hcd, struct dwc_hc *hc,
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 u32 regs, s=
truct dwc_qtd *qtd, int *must_free)
> +{
> + =A0 =A0 =A0 struct urb *urb =3D qtd->urb;
> + =A0 =A0 =A0 int pipe_type =3D usb_pipetype(urb->pipe);
> + =A0 =A0 =A0 u32 hcintmsk =3D 0;
> +
> + =A0 =A0 =A0 if (pipe_type =3D=3D PIPE_CONTROL)
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 dwc_otg_hcd_complete_urb(hcd, qtd->urb, -EP=
IPE);
> +
> + =A0 =A0 =A0 if (pipe_type =3D=3D PIPE_BULK || pipe_type =3D=3D PIPE_INT=
ERRUPT) {
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 dwc_otg_hcd_complete_urb(hcd, qtd->urb, -EP=
IPE);
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 /*
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0* USB protocol requires resetting the da=
ta toggle for bulk
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0* and interrupt endpoints when a CLEAR_F=
EATURE(ENDPOINT_HALT)
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0* setup command is issued to the endpoin=
t. Anticipate the
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0* CLEAR_FEATURE command since a STALL ha=
s occurred and reset
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0* the data toggle now.
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0*/
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 hc->qh->data_toggle =3D 0;
> + =A0 =A0 =A0 }
> +
> + =A0 =A0 =A0 halt_channel(hcd, hc, qtd, DWC_OTG_HC_XFER_STALL, must_free=
);
> + =A0 =A0 =A0 /* disable stall */
> + =A0 =A0 =A0 hcintmsk =3D DWC_HCINTMSK_STALL_RESP_REC_RW(hcintmsk, 1);
> + =A0 =A0 =A0 dwc_reg_modify(regs, DWC_HCINTMSK, hcintmsk, 0);
> +
> + =A0 =A0 =A0 return 1;
> +}
> +
> +/**
> + * Updates the state of the URB when a transfer has been stopped due to =
an
> + * abnormal condition before the transfer completes. Modifies the
> + * actual_length field of the URB to reflect the number of bytes that ha=
ve
> + * actually been transferred via the host channel.
> + */
> +static void update_urb_state_xfer_intr(struct dwc_hc *hc,
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0=
=A0u32 regs, struct urb *urb,
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0=
=A0struct dwc_qtd *qtd,
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0=
=A0enum dwc_halt_status sts)
> +{
> + =A0 =A0 =A0 u32 xfr_len =3D get_actual_xfer_length(hc, regs, qtd, sts, =
NULL);
> + =A0 =A0 =A0 urb->actual_length +=3D xfr_len;
> +}
> +
> +/**
> + * Handles a host channel NAK interrupt. This handler may be called in e=
ither
> + * DMA mode or Slave mode.
> + */
> +static int handle_hc_nak_intr(struct dwc_hcd *hcd, struct dwc_hc *hc,
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 u32 regs, struc=
t dwc_qtd *qtd, int *must_free)
> +{
> + =A0 =A0 =A0 u32 hcintmsk =3D 0;
> +
> + =A0 =A0 =A0 /*
> + =A0 =A0 =A0 =A0* Handle NAK for IN/OUT SSPLIT/CSPLIT transfers, bulk, c=
ontrol, and
> + =A0 =A0 =A0 =A0* interrupt. =A0Re-start the SSPLIT transfer.
> + =A0 =A0 =A0 =A0*/
> + =A0 =A0 =A0 if (hc->do_split) {
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 if (hc->complete_split)
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 qtd->error_count =3D 0;
> +
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 qtd->complete_split =3D 0;
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 halt_channel(hcd, hc, qtd, DWC_OTG_HC_XFER_=
NAK, must_free);
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 goto handle_nak_done;
> + =A0 =A0 =A0 }
> + =A0 =A0 =A0 switch (usb_pipetype(qtd->urb->pipe)) {
> + =A0 =A0 =A0 case PIPE_CONTROL:
> + =A0 =A0 =A0 case PIPE_BULK:
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 if (hcd->core_if->dma_enable && hc->ep_is_i=
n) {
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 /*
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0* NAK interrupts are ena=
bled on bulk/control IN
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0* transfers in DMA mode =
for the sole purpose of
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0* resetting the error co=
unt after a transaction error
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0* occurs. The core will =
continue transferring data.
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0*/
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 qtd->error_count =3D 0;
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 goto handle_nak_done;
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 }
> +
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 /*
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0* NAK interrupts normally occur during O=
UT transfers in DMA
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0* or Slave mode. For IN transfers, more =
requests will be
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0* queued as request queue space is avail=
able.
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0*/
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 qtd->error_count =3D 0;
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 if (!hc->qh->ping_state) {
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 update_urb_state_xfer_intr(=
hc, regs, qtd->urb, qtd,
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0=
=A0 =A0 =A0 =A0 =A0 =A0 =A0DWC_OTG_HC_XFER_NAK);
> +
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 save_data_toggle(hc, regs, =
qtd);
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 if (qtd->urb->dev->speed =
=3D=3D USB_SPEED_HIGH)
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 hc->qh->pin=
g_state =3D 1;
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 }
> +
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 /*
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0* Halt the channel so the transfer can b=
e re-started from
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0* the appropriate point or the PING prot=
ocol will
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0* start/continue.
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0*/
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 halt_channel(hcd, hc, qtd, DWC_OTG_HC_XFER_=
NAK, must_free);
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 break;
> + =A0 =A0 =A0 case PIPE_INTERRUPT:
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 qtd->error_count =3D 0;
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 halt_channel(hcd, hc, qtd, DWC_OTG_HC_XFER_=
NAK, must_free);
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 break;
> + =A0 =A0 =A0 case PIPE_ISOCHRONOUS:
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 /* Should never get called for isochronous =
transfers. */
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 BUG();
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 break;
> + =A0 =A0 =A0 }
> +
> +handle_nak_done:
> + =A0 =A0 =A0 /* disable nak */
> + =A0 =A0 =A0 hcintmsk =3D DWC_HCINTMSK_NAK_RESP_REC_RW(hcintmsk, 1);
> + =A0 =A0 =A0 dwc_reg_modify(regs, DWC_HCINTMSK, hcintmsk, 0);
> +
> + =A0 =A0 =A0 return 1;
> +}
> +
> +/**
> + * Helper function for handle_hc_ack_intr(). =A0Sets the split values fo=
r an ACK
> + * on SSPLIT for ISOC OUT.
> + */
> +static void set_isoc_out_vals(struct dwc_hc *hc, struct dwc_qtd *qtd)
> +{
> + =A0 =A0 =A0 struct usb_iso_packet_descriptor *frame_desc;
> +
> + =A0 =A0 =A0 switch (hc->xact_pos) {
> + =A0 =A0 =A0 case DWC_HCSPLIT_XACTPOS_ALL:
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 break;
> + =A0 =A0 =A0 case DWC_HCSPLIT_XACTPOS_END:
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 qtd->isoc_split_pos =3D DWC_HCSPLIT_XACTPOS=
_ALL;
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 qtd->isoc_split_offset =3D 0;
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 break;
> + =A0 =A0 =A0 case DWC_HCSPLIT_XACTPOS_BEGIN:
> + =A0 =A0 =A0 case DWC_HCSPLIT_XACTPOS_MID:
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 /*
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0* For BEGIN or MID, calculate the length=
for the next
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0* microframe to determine the correct SS=
PLIT token, either MID
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0* or END.
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0*/
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 frame_desc =3D &qtd->urb->iso_frame_desc[qt=
d->isoc_frame_index];
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 qtd->isoc_split_offset +=3D 188;
> +
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 if ((frame_desc->length - qtd->isoc_split_o=
ffset) <=3D 188)
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 qtd->isoc_split_pos =3D DWC=
_HCSPLIT_XACTPOS_END;
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 else
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 qtd->isoc_split_pos =3D DWC=
_HCSPLIT_XACTPOS_MID;
> +
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 break;
> + =A0 =A0 =A0 }
> +}
> +
> +/**
> + * Handles a host channel ACK interrupt. This interrupt is enabled when
> + * performing the PING protocol in Slave mode, when errors occur during
> + * either Slave mode or DMA mode, and during Start Split transactions.
> + */
> +static int handle_hc_ack_intr(struct dwc_hcd *hcd, struct dwc_hc *hc,
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 u32 regs, struc=
t dwc_qtd *qtd, int *must_free)
> +{
> + =A0 =A0 =A0 u32 hcintmsk =3D 0;
> +
> + =A0 =A0 =A0 if (hc->do_split) {
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 /* Handle ACK on SSPLIT. ACK should not occ=
ur in CSPLIT. */
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 if (!hc->ep_is_in && hc->data_pid_start !=
=3D DWC_OTG_HC_PID_SETUP)
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 qtd->ssplit_out_xfer_count =
=3D hc->xfer_len;
> +
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 /* Don't need complete for isochronous out =
transfers. */
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 if (!(hc->ep_type =3D=3D DWC_OTG_EP_TYPE_IS=
OC && !hc->ep_is_in))
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 qtd->complete_split =3D 1;
> +
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 if (hc->ep_type =3D=3D DWC_OTG_EP_TYPE_ISOC=
&& !hc->ep_is_in)
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 set_isoc_out_vals(hc, qtd);
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 else
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 halt_channel(hcd, hc, qtd, =
DWC_OTG_HC_XFER_ACK,
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0=
must_free);
> + =A0 =A0 =A0 } else {
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 qtd->error_count =3D 0;
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 if (hc->qh->ping_state) {
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 hc->qh->ping_state =3D 0;
> +
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 /*
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0* Halt the channel so th=
e transfer can be re-started
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0* from the appropriate p=
oint. This only happens in
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0* Slave mode. In DMA mod=
e, the ping_state is cleared
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0* when the transfer is s=
tarted because the core
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0* automatically executes=
the PING, then the transfer.
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0*/
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 halt_channel(hcd, hc, qtd, =
DWC_OTG_HC_XFER_ACK,
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0=
must_free);
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 }
> + =A0 =A0 =A0 }
> +
> + =A0 =A0 =A0 /*
> + =A0 =A0 =A0 =A0* If the ACK occurred when _not_ in the PING state, let =
the channel
> + =A0 =A0 =A0 =A0* continue transferring data after clearing the error co=
unt.
> + =A0 =A0 =A0 =A0*/
> + =A0 =A0 =A0 /* disable ack */
> + =A0 =A0 =A0 hcintmsk =3D DWC_HCINTMSK_ACK_RESP_REC_RW(hcintmsk, 1);
> + =A0 =A0 =A0 dwc_reg_modify(regs, DWC_HCINTMSK, hcintmsk, 0);
> +
> + =A0 =A0 =A0 return 1;
> +}
> +
> +/**
> + * Handles a host channel NYET interrupt. This interrupt should only occ=
ur on
> + * Bulk and Control OUT endpoints and for complete split transactions. I=
f a
> + * NYET occurs at the same time as a Transfer Complete interrupt, it is
> + * handled in the xfercomp interrupt handler, not here. This handler may=
be
> + * called in either DMA mode or Slave mode.
> + */
> +static int handle_hc_nyet_intr(struct dwc_hcd *hcd, struct dwc_hc *hc,
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0u32 regs, st=
ruct dwc_qtd *qtd, int *must_free)
> +{
> + =A0 =A0 =A0 u32 hcintmsk =3D 0;
> + =A0 =A0 =A0 u32 hcint_clear =3D 0;
> +
> + =A0 =A0 =A0 /*
> + =A0 =A0 =A0 =A0* NYET on CSPLIT
> + =A0 =A0 =A0 =A0* re-do the CSPLIT immediately on non-periodic
> + =A0 =A0 =A0 =A0*/
> + =A0 =A0 =A0 if (hc->do_split && hc->complete_split) {
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 if (hc->ep_type =3D=3D DWC_OTG_EP_TYPE_INTR=
||
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 hc->ep_type =3D=3D DWC_OTG_EP_TYPE_=
ISOC) {
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 int frnum =3D
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 dwc_otg_hcd_get_fra=
me_number(dwc_otg_hcd_to_hcd
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0=
=A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0(hcd));
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 if (dwc_full_frame_num(frnu=
m) !=3D
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 dwc_full_frame_num(=
hc->qh->sched_frame)) {
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 qtd->comple=
te_split =3D 0;
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 halt_channe=
l(hcd, hc, qtd,
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0=
=A0 =A0 =A0 =A0DWC_OTG_HC_XFER_XACT_ERR,
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0=
=A0 =A0 =A0 =A0must_free);
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 goto handle=
_nyet_done;
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 }
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 }
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 halt_channel(hcd, hc, qtd, DWC_OTG_HC_XFER_=
NYET, must_free);
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 goto handle_nyet_done;
> + =A0 =A0 =A0 }
> + =A0 =A0 =A0 hc->qh->ping_state =3D 1;
> + =A0 =A0 =A0 qtd->error_count =3D 0;
> + =A0 =A0 =A0 update_urb_state_xfer_intr(hc, regs, qtd->urb, qtd,
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0DWC_=
OTG_HC_XFER_NYET);
> + =A0 =A0 =A0 save_data_toggle(hc, regs, qtd);
> + =A0 =A0 =A0 /*
> + =A0 =A0 =A0 =A0* Halt the channel and re-start the transfer so the PING
> + =A0 =A0 =A0 =A0* protocol will start.
> + =A0 =A0 =A0 =A0*/
> + =A0 =A0 =A0 halt_channel(hcd, hc, qtd, DWC_OTG_HC_XFER_NYET, must_free)=
;
> +
> +handle_nyet_done:
> + =A0 =A0 =A0 /* disable nyet */
> + =A0 =A0 =A0 hcintmsk =3D DWC_HCINTMSK_NYET_RESP_REC_RW(hcintmsk, 1);
> + =A0 =A0 =A0 dwc_reg_modify(regs, DWC_HCINTMSK, hcintmsk, 0);
> + =A0 =A0 =A0 /* clear nyet */
> + =A0 =A0 =A0 hcint_clear =3D DWC_HCINT_NYET_RESP_REC_RW(hcint_clear, 1);
> + =A0 =A0 =A0 dwc_reg_write(regs, DWC_HCINT, hcint_clear);
> + =A0 =A0 =A0 return 1;
> +}
> +
> +/**
> + * Handles a host channel babble interrupt. This handler may be called i=
n
> + * either DMA mode or Slave mode.
> + */
> +static int handle_hc_babble_intr(struct dwc_hcd *hcd, struct dwc_hc *hc,
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0u32 regs=
, struct dwc_qtd *qtd, int *must_free)
> +{
> + =A0 =A0 =A0 u32 hcintmsk =3D 0;
> +
> + =A0 =A0 =A0 if (hc->ep_type !=3D DWC_OTG_EP_TYPE_ISOC) {
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 dwc_otg_hcd_complete_urb(hcd, qtd->urb, -EO=
VERFLOW);
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 halt_channel(hcd, hc, qtd, DWC_OTG_HC_XFER_=
BABBLE_ERR,
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0must_free);
> + =A0 =A0 =A0 } else {
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 enum dwc_halt_status halt_status;
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 halt_status =3D update_isoc_urb_state(hcd, =
hc, regs, qtd,
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0=
=A0 =A0 =A0 =A0 =A0 =A0 =A0 DWC_OTG_HC_XFER_BABBLE_ERR);
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 halt_channel(hcd, hc, qtd, halt_status, mus=
t_free);
> + =A0 =A0 =A0 }
> + =A0 =A0 =A0 /* disable bblerr */
> + =A0 =A0 =A0 hcintmsk =3D DWC_HCINTMSK_BBL_ERR_RW(hcintmsk, 1);
> + =A0 =A0 =A0 dwc_reg_modify(regs, DWC_HCINTMSK, hcintmsk, 0);
> + =A0 =A0 =A0 return 1;
> +}
> +
> +/**
> + * Handles a host channel AHB error interrupt. This handler is only call=
ed in
> + * DMA mode.
> + */
> +static int handle_hc_ahberr_intr(struct dwc_hcd *hcd, struct dwc_hc *hc,
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0u32 regs=
, struct dwc_qtd *qtd)
> +{
> + =A0 =A0 =A0 u32 hcchar;
> + =A0 =A0 =A0 u32 hcsplt;
> + =A0 =A0 =A0 u32 hctsiz =3D 0;
> + =A0 =A0 =A0 u32 hcdma;
> + =A0 =A0 =A0 struct urb *urb =3D qtd->urb;
> + =A0 =A0 =A0 u32 hcintmsk =3D 0;
> +
> + =A0 =A0 =A0 hcchar =3D dwc_reg_read(regs, DWC_HCCHAR);
> + =A0 =A0 =A0 hcsplt =3D dwc_reg_read(regs, DWC_HCSPLT);
> + =A0 =A0 =A0 hctsiz =3D dwc_reg_read(regs, DWC_HCTSIZ);
> + =A0 =A0 =A0 hcdma =3D dwc_reg_read(regs, DWC_HCDMA);
> +
> + =A0 =A0 =A0 pr_err("AHB ERROR, Channel %d\n", hc->hc_num);
> + =A0 =A0 =A0 pr_err(" =A0hcchar 0x%08x, hcsplt 0x%08x\n", hcchar, hcsplt=
);
> + =A0 =A0 =A0 pr_err(" =A0hctsiz 0x%08x, hcdma 0x%08x\n", hctsiz, hcdma);
> +
> + =A0 =A0 =A0 pr_err(" =A0Device address: %d\n", usb_pipedevice(urb->pipe=
));
> + =A0 =A0 =A0 pr_err(" =A0Endpoint: %d, %s\n", usb_pipeendpoint(urb->pipe=
),
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0(usb_pipein(urb->pipe) ? "IN" : "OUT"));
> +
> + =A0 =A0 =A0 pr_err(" =A0Endpoint type: %s\n", pipetype_str(urb->pipe));
> + =A0 =A0 =A0 pr_err(" =A0Speed: %s\n", dev_speed_str(urb->dev->speed));
> + =A0 =A0 =A0 pr_err(" =A0Max packet size: %d\n",
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0usb_maxpacket(urb->dev, urb->pipe, usb_pipeo=
ut(urb->pipe)));
> + =A0 =A0 =A0 pr_err(" =A0Data buffer length: %d\n", urb->transfer_buffer=
_length);
> + =A0 =A0 =A0 pr_err(" =A0Transfer buffer: %p, Transfer DMA: %p\n",
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0urb->transfer_buffer, (void *)(u32) urb->tra=
nsfer_dma);
> + =A0 =A0 =A0 pr_err(" =A0Setup buffer: %p, Setup DMA: %p\n",
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0urb->setup_packet, (void *)(u32) urb->setup_=
dma);
> + =A0 =A0 =A0 pr_err(" =A0Interval: %d\n", urb->interval);
> +
> + =A0 =A0 =A0 dwc_otg_hcd_complete_urb(hcd, urb, -EIO);
> +
> + =A0 =A0 =A0 /*
> + =A0 =A0 =A0 =A0* Force a channel halt. Don't call halt_channel because =
that won't
> + =A0 =A0 =A0 =A0* write to the HCCHARn register in DMA mode to force the=
halt.
> + =A0 =A0 =A0 =A0*/
> + =A0 =A0 =A0 dwc_otg_hc_halt(hcd->core_if, hc, DWC_OTG_HC_XFER_AHB_ERR);
> + =A0 =A0 =A0 /* disable ahberr */
> + =A0 =A0 =A0 hcintmsk =3D DWC_HCINTMSK_AHB_ERR_RW(hcintmsk, 1);
> + =A0 =A0 =A0 dwc_reg_modify(regs, DWC_HCINTMSK, hcintmsk, 0);
> +
> + =A0 =A0 =A0 return 1;
> +}
> +
> +/**
> + * Handles a host channel transaction error interrupt. This handler may =
be
> + * called in either DMA mode or Slave mode.
> + */
> +static int handle_hc_xacterr_intr(struct dwc_hcd *hcd, struct dwc_hc *hc=
,
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 u32 reg=
s, struct dwc_qtd *qtd, int *must_free)
> +{
> + =A0 =A0 =A0 enum dwc_halt_status status =3D DWC_OTG_HC_XFER_XACT_ERR;
> + =A0 =A0 =A0 u32 hcintmsk =3D 0;
> +
> + =A0 =A0 =A0 switch (usb_pipetype(qtd->urb->pipe)) {
> + =A0 =A0 =A0 case PIPE_CONTROL:
> + =A0 =A0 =A0 case PIPE_BULK:
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 qtd->error_count++;
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 if (!hc->qh->ping_state) {
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 update_urb_state_xfer_intr(=
hc, regs, qtd->urb, qtd,
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0=
=A0 =A0 =A0 =A0 =A0 =A0 =A0status);
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 save_data_toggle(hc, regs, =
qtd);
> +
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 if (!hc->ep_is_in && qtd->u=
rb->dev->speed =3D=3D
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 USB_SPEED_HIGH)
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 hc->qh->pin=
g_state =3D 1;
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 }
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 /*
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0* Halt the channel so the transfer can b=
e re-started from
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0* the appropriate point or the PING prot=
ocol will start.
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0*/
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 halt_channel(hcd, hc, qtd, status, must_fre=
e);
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 break;
> + =A0 =A0 =A0 case PIPE_INTERRUPT:
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 qtd->error_count++;
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 if (hc->do_split && hc->complete_split)
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 qtd->complete_split =3D 0;
> +
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 halt_channel(hcd, hc, qtd, status, must_fre=
e);
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 break;
> + =A0 =A0 =A0 case PIPE_ISOCHRONOUS:
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 status =3D update_isoc_urb_state(hcd, hc, r=
egs, qtd, status);
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 halt_channel(hcd, hc, qtd, status, must_fre=
e);
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 break;
> + =A0 =A0 =A0 }
> + =A0 =A0 =A0 /* Disable xacterr */
> + =A0 =A0 =A0 hcintmsk =3D DWC_HCINTMSK_TRANS_ERR_RW(hcintmsk, 1);
> + =A0 =A0 =A0 dwc_reg_modify(regs, DWC_HCINTMSK, hcintmsk, 0);
> +
> + =A0 =A0 =A0 return 1;
> +}
> +
> +/**
> + * Handles a host channel frame overrun interrupt. This handler may be c=
alled
> + * in either DMA mode or Slave mode.
> + */
> +static int handle_hc_frmovrun_intr(struct dwc_hcd *hcd, struct dwc_hc *h=
c,
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0u32 =
regs, struct dwc_qtd *qtd,
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0int =
*must_free)
> +{
> + =A0 =A0 =A0 enum dwc_halt_status status =3D DWC_OTG_HC_XFER_FRAME_OVERR=
UN;
> + =A0 =A0 =A0 u32 hcintmsk =3D 0;
> +
> + =A0 =A0 =A0 switch (usb_pipetype(qtd->urb->pipe)) {
> + =A0 =A0 =A0 case PIPE_CONTROL:
> + =A0 =A0 =A0 case PIPE_BULK:
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 break;
> + =A0 =A0 =A0 case PIPE_INTERRUPT:
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 halt_channel(hcd, hc, qtd, status, must_fre=
e);
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 break;
> + =A0 =A0 =A0 case PIPE_ISOCHRONOUS:
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 status =3D update_isoc_urb_state(hcd, hc, r=
egs, qtd, status);
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 halt_channel(hcd, hc, qtd, status, must_fre=
e);
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 break;
> + =A0 =A0 =A0 }
> + =A0 =A0 =A0 /* Disable frmovrun */
> + =A0 =A0 =A0 hcintmsk =3D DWC_HCINTMSK_FRAME_OVERN_ERR_RW(hcintmsk, 1);
> + =A0 =A0 =A0 dwc_reg_modify(regs, DWC_HCINTMSK, hcintmsk, 0);
> +
> + =A0 =A0 =A0 return 1;
> +}
> +
> +/**
> + * Handles a host channel data toggle error interrupt. This handler may =
be
> + * called in either DMA mode or Slave mode.
> + */
> +static int handle_hc_datatglerr_intr(struct dwc_hcd *hcd, struct dwc_hc =
*hc,
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0=
u32 regs, struct dwc_qtd *qtd)
> +{
> + =A0 =A0 =A0 u32 hcintmsk =3D 0;
> +
> + =A0 =A0 =A0 if (hc->ep_is_in)
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 qtd->error_count =3D 0;
> + =A0 =A0 =A0 else
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 pr_err("Data Toggle Error on OUT transfer, =
channel "
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0"%d\n", hc->hc_num);
> +
> + =A0 =A0 =A0 /* disable datatglerr */
> + =A0 =A0 =A0 hcintmsk =3D DWC_HCINTMSK_DATA_TOG_ERR_RW(hcintmsk, 1);
> + =A0 =A0 =A0 dwc_reg_modify(regs, DWC_HCINTMSK, hcintmsk, 0);
> +
> + =A0 =A0 =A0 return 1;
> +}
> +
> +/**
> + * Handles a host Channel Halted interrupt in DMA mode. This handler
> + * determines the reason the channel halted and proceeds accordingly.
> + */
> +static void handle_hc_chhltd_intr_dma(struct dwc_hcd *hcd, struct dwc_hc=
*hc,
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0=
ulong regs, struct dwc_qtd *qtd,
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0=
int *must_free)
> +{
> + =A0 =A0 =A0 u32 hcint;
> + =A0 =A0 =A0 u32 hcintmsk =3D 0;
> +
> + =A0 =A0 =A0 if (hc->halt_status =3D=3D DWC_OTG_HC_XFER_URB_DEQUEUE ||
> + =A0 =A0 =A0 =A0 =A0 hc->halt_status =3D=3D DWC_OTG_HC_XFER_AHB_ERR) {
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 /*
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0* Just release the channel. A dequeue ca=
n happen on a
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0* transfer timeout. In the case of an AH=
B Error, the channel
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0* was forced to halt because there's no =
way to gracefully
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0* recover.
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0*/
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 release_channel(hcd, hc, qtd, hc->halt_stat=
us, must_free);
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 return;
> + =A0 =A0 =A0 }
> +
> + =A0 =A0 =A0 /* Read the HCINTn register to determine the cause for the =
halt. */
> + =A0 =A0 =A0 hcint =3D dwc_reg_read(regs, DWC_HCINT);
> + =A0 =A0 =A0 hcintmsk =3D dwc_reg_read(regs, DWC_HCINTMSK);
> + =A0 =A0 =A0 if (DWC_HCINT_TXFER_CMPL_RD(hcint)) {
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 /*
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0* This is here because of a possible har=
dware bug. =A0Spec
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0* says that on SPLIT-ISOC OUT transfers =
in DMA mode that a HALT
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0* interrupt w/ACK bit set should occur, =
but I only see the
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0* XFERCOMP bit, even with it masked out.=
=A0This is a workaround
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0* for that behavior. =A0Should fix this =
when hardware is fixed.
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0*/
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 if (hc->ep_type =3D=3D DWC_OTG_EP_TYPE_ISOC=
&& !hc->ep_is_in)
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 handle_hc_ack_intr(hcd, hc,=
regs, qtd, must_free);
> +
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 handle_hc_xfercomp_intr(hcd, hc, regs, qtd,=
must_free);
> + =A0 =A0 =A0 } else if (DWC_HCINT_STALL_RESP_REC_RD(hcint)) {
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 handle_hc_stall_intr(hcd, hc, regs, qtd, mu=
st_free);
> + =A0 =A0 =A0 } else if (DWC_HCINT_TRANS_ERR_RD(hcint)) {
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 /*
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0* Must handle xacterr before nak or ack.=
Could get a xacterr
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0* at the same time as either of these on=
a BULK/CONTROL OUT
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0* that started with a PING. The xacterr =
takes precedence.
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0*/
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 handle_hc_xacterr_intr(hcd, hc, regs, qtd, =
must_free);
> + =A0 =A0 =A0 } else if (DWC_HCINT_NYET_RESP_REC_RD(hcint)) {
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 /*
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0* Must handle nyet before nak or ack. Co=
uld get a nyet at the
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0* same time as either of those on a BULK=
/CONTROL OUT that
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0* started with a PING. The nyet takes pr=
ecedence.
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0*/
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 handle_hc_nyet_intr(hcd, hc, regs, qtd, mus=
t_free);
> + =A0 =A0 =A0 } else if (DWC_HCINT_BBL_ERR_RD(hcint)) {
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 handle_hc_babble_intr(hcd, hc, regs, qtd, m=
ust_free);
> + =A0 =A0 =A0 } else if (DWC_HCINT_FRAME_OVERN_ERR_RD(hcint)) {
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 handle_hc_frmovrun_intr(hcd, hc, regs, qtd,=
must_free);
> + =A0 =A0 =A0 } else if (DWC_HCINT_DATA_TOG_ERR_RD(hcint)) {
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 handle_hc_datatglerr_intr(hcd, hc, regs, qt=
d);
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 hc->qh->data_toggle =3D 0;
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 halt_channel(hcd, hc, qtd, hc->halt_status,=
must_free);
> + =A0 =A0 =A0 } else if (DWC_HCINT_NAK_RESP_REC_RD(hcint) &&
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0!DWC_HCINTMSK_NAK_RESP_REC_RD(hcintm=
sk)) {
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 /*
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0* If nak is not masked, it's because a n=
on-split IN transfer
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0* is in an error state. In that case, th=
e nak is handled by
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0* the nak interrupt handler, not here. H=
andle nak here for
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0* BULK/CONTROL OUT transfers, which halt=
on a NAK to allow
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0* rewinding the buffer pointer.
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0*/
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 handle_hc_nak_intr(hcd, hc, regs, qtd, must=
_free);
> + =A0 =A0 =A0 } else if (DWC_HCINT_ACK_RESP_REC_RD(hcint) &&
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0!DWC_HCINTMSK_ACK_RESP_REC_RD(hcintm=
sk)) {
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 /*
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0* If ack is not masked, it's because a n=
on-split IN transfer
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0* is in an error state. In that case, th=
e ack is handled by
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0* the ack interrupt handler, not here. H=
andle ack here for
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0* split transfers. Start splits halt on =
ACK.
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0*/
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 handle_hc_ack_intr(hcd, hc, regs, qtd, must=
_free);
> + =A0 =A0 =A0 } else {
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 if (hc->ep_type =3D=3D DWC_OTG_EP_TYPE_INTR=
||
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 hc->ep_type =3D=3D DWC_OTG_EP_TYPE_=
ISOC) {
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 /*
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0* A periodic transfer ha=
lted with no other channel
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0* interrupts set. Assume=
it was halted by the core
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0* because it could not b=
e completed in its scheduled
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0* (micro)frame.
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0*/
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 halt_channel(hcd, hc, qtd,
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0=
DWC_OTG_HC_XFER_PERIODIC_INCOMPLETE,
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0=
must_free);
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 } else {
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 pr_err("%s: Channel %d, DMA=
Mode -- ChHltd "
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0"set, but re=
ason for halting is unknown, "
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0"hcint 0x%08=
x, intsts 0x%08x\n",
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0__func__, hc=
->hc_num, hcint,
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0dwc_reg_read=
(gintsts_reg(hcd), 0));
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 }
> + =A0 =A0 =A0 }
> +}
> +
> +/**
> + * Handles a host channel Channel Halted interrupt.
> + *
> + * In slave mode, this handler is called only when the driver specifical=
ly
> + * requests a halt. This occurs during handling other host channel inter=
rupts
> + * (e.g. nak, xacterr, stall, nyet, etc.).
> + *
> + * In DMA mode, this is the interrupt that occurs when the core has fini=
shed
> + * processing a transfer on a channel. Other host channel interrupts (ex=
cept
> + * ahberr) are disabled in DMA mode.
> + */
> +static int handle_hc_chhltd_intr(struct dwc_hcd *hcd, struct dwc_hc *hc,
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 ulong regs, struct dwc_qtd *qtd, int *must_=
free)
> +{
> + =A0 =A0 =A0 if (hcd->core_if->dma_enable)
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 handle_hc_chhltd_intr_dma(hcd, hc, regs, qt=
d, must_free);
> + =A0 =A0 =A0 else
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 release_channel(hcd, hc, qtd, hc->halt_stat=
us, must_free);
> +
> + =A0 =A0 =A0 return 1;
> +}
> +
> +/* Handles interrupt for a specific Host Channel */
> +static int dwc_otg_hcd_handle_hc_n_intr(struct dwc_hcd *hcd, u32 num)
> +{
> + =A0 =A0 =A0 int must_free =3D 0;
> + =A0 =A0 =A0 int retval =3D 0;
> + =A0 =A0 =A0 u32 hcint;
> + =A0 =A0 =A0 u32 hcintmsk =3D 0;
> + =A0 =A0 =A0 struct dwc_hc *hc;
> + =A0 =A0 =A0 ulong hc_regs;
> + =A0 =A0 =A0 struct dwc_qtd *qtd;
> +
> + =A0 =A0 =A0 hc =3D hcd->hc_ptr_array[num];
> + =A0 =A0 =A0 hc_regs =3D hcd->core_if->host_if->hc_regs[num];
> + =A0 =A0 =A0 qtd =3D list_entry(hc->qh->qtd_list.next, struct dwc_qtd, q=
td_list_entry);
> +
> + =A0 =A0 =A0 hcint =3D dwc_reg_read(hc_regs, DWC_HCINT);
> + =A0 =A0 =A0 hcintmsk =3D dwc_reg_read(hc_regs, DWC_HCINTMSK);
> +
> + =A0 =A0 =A0 hcint =3D hcint & hcintmsk;
> + =A0 =A0 =A0 if (!hcd->core_if->dma_enable && DWC_HCINT_CHAN_HALTED_RD(h=
cint)
> + =A0 =A0 =A0 =A0 =A0 && hcint !=3D 0x2)
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 hcint =3D DWC_HCINT_CHAN_HALTED_RW(hcint, 0=
);
> +
> + =A0 =A0 =A0 if (DWC_HCINT_TXFER_CMPL_RD(hcint)) {
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 retval |=3D handle_hc_xfercomp_intr(hcd, hc=
, hc_regs,
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0=
=A0 =A0 =A0 =A0 =A0 =A0 qtd, &must_free);
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 /*
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0* If NYET occurred at same time as Xfer =
Complete, the NYET is
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0* handled by the Xfer Complete interrupt=
handler. Don't want
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0* to call the NYET interrupt handler in =
this case.
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0*/
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 hcint =3D DWC_HCINT_NYET_RESP_REC_RW(hcint,=
0);
> + =A0 =A0 =A0 }
> +
> + =A0 =A0 =A0 if (DWC_HCINT_CHAN_HALTED_RD(hcint))
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 retval |=3D handle_hc_chhltd_intr(hcd, hc, =
hc_regs,
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0=
=A0 =A0 =A0 =A0 =A0 qtd, &must_free);
> + =A0 =A0 =A0 if (DWC_HCINT_AHB_ERR_RD(hcint))
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 retval |=3D handle_hc_ahberr_intr(hcd, hc, =
hc_regs, qtd);
> + =A0 =A0 =A0 if (DWC_HCINT_STALL_RESP_REC_RD(hcint))
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 retval |=3D handle_hc_stall_intr(hcd, hc, h=
c_regs,
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0=
=A0 =A0 =A0 =A0 =A0qtd, &must_free);
> + =A0 =A0 =A0 if (DWC_HCINT_NAK_RESP_REC_RD(hcint))
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 retval |=3D handle_hc_nak_intr(hcd, hc, hc_=
regs, qtd, &must_free);
> + =A0 =A0 =A0 if (DWC_HCINT_ACK_RESP_REC_RD(hcint))
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 retval |=3D handle_hc_ack_intr(hcd, hc, hc_=
regs, qtd, &must_free);
> + =A0 =A0 =A0 if (DWC_HCINT_NYET_RESP_REC_RD(hcint))
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 retval |=3D handle_hc_nyet_intr(hcd, hc, hc=
_regs,
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0=
=A0 =A0 =A0 =A0 qtd, &must_free);
> + =A0 =A0 =A0 if (DWC_HCINT_TRANS_ERR_RD(hcint))
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 retval |=3D handle_hc_xacterr_intr(hcd, hc,=
hc_regs,
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0=
=A0 =A0 =A0 =A0 =A0 =A0qtd, &must_free);
> + =A0 =A0 =A0 if (DWC_HCINT_BBL_ERR_RD(hcint))
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 retval |=3D handle_hc_babble_intr(hcd, hc, =
hc_regs,
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0=
=A0 =A0 =A0 =A0 =A0 qtd, &must_free);
> + =A0 =A0 =A0 if (DWC_HCINT_FRAME_OVERN_ERR_RD(hcint))
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 retval |=3D handle_hc_frmovrun_intr(hcd, hc=
, hc_regs,
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0=
=A0 =A0 =A0 =A0 =A0 =A0 qtd, &must_free);
> + =A0 =A0 =A0 if (DWC_HCINT_DATA_TOG_ERR_RD(hcint))
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 retval |=3D handle_hc_datatglerr_intr(hcd, =
hc, hc_regs, qtd);
> +
> + =A0 =A0 =A0 if (must_free)
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 /* Free the qtd here now that we are done u=
sing it. */
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 dwc_otg_hcd_qtd_free(qtd);
> + =A0 =A0 =A0 return retval;
> +}
> +
> +/**
> + * This function returns the Host All Channel Interrupt register
> + */
> +static inline u32 dwc_otg_read_host_all_channels_intr(struct core_if
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0=
=A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 *core_if)
> +{
> + =A0 =A0 =A0 return dwc_reg_read(core_if->host_if->host_global_regs, DWC=
_HAINT);
> +}
> +
> +/**
> + * This interrupt indicates that one or more host channels has a pending
> + * interrupt. There are multiple conditions that can cause each host cha=
nnel
> + * interrupt. This function determines which conditions have occurred fo=
r each
> + * host channel interrupt and handles them appropriately.
> + */
> +static int dwc_otg_hcd_handle_hc_intr(struct dwc_hcd *hcd)
> +{
> + =A0 =A0 =A0 u32 i;
> + =A0 =A0 =A0 int retval =3D 0;
> + =A0 =A0 =A0 u32 haint;
> +
> + =A0 =A0 =A0 /*
> + =A0 =A0 =A0 =A0* Clear appropriate bits in HCINTn to clear the interrup=
t bit in
> + =A0 =A0 =A0 =A0* =A0GINTSTS
> + =A0 =A0 =A0 =A0*/
> + =A0 =A0 =A0 haint =3D dwc_otg_read_host_all_channels_intr(hcd->core_if)=
;
> + =A0 =A0 =A0 for (i =3D 0; i < hcd->core_if->core_params->host_channels;=
i++)
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 if (DWC_HAINT_RD(haint) & (1 << i))
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 retval |=3D dwc_otg_hcd_han=
dle_hc_n_intr(hcd, i);
> +
> + =A0 =A0 =A0 return retval;
> +}
> +
> +/* This function handles interrupts for the HCD.*/
> +int dwc_otg_hcd_handle_intr(struct dwc_hcd *hcd)
> +{
> + =A0 =A0 =A0 int ret =3D 0;
> + =A0 =A0 =A0 struct core_if *core_if =3D hcd->core_if;
> + =A0 =A0 =A0 u32 gintsts;
> +
> + =A0 =A0 =A0 /* Check if HOST Mode */
> + =A0 =A0 =A0 if (dwc_otg_is_host_mode(core_if)) {
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 spin_lock(&hcd->lock);
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 gintsts =3D dwc_otg_read_core_intr(core_if)=
;
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 if (!gintsts) {
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 spin_unlock(&hcd->lock);
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 return IRQ_NONE;
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 }
> +
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 if (gintsts & DWC_INTMSK_STRT_OF_FRM)
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 ret |=3D dwc_otg_hcd_handle=
_sof_intr(hcd);
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 if (gintsts & DWC_INTMSK_RXFIFO_NOT_EMPT)
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 ret |=3D dwc_otg_hcd_handle=
_rx_status_q_level_intr(hcd);
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 if (gintsts & DWC_INTMSK_NP_TXFIFO_EMPT)
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 ret |=3D dwc_otg_hcd_handle=
_np_tx_fifo_empty_intr(hcd);
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 if (gintsts & DWC_INTMSK_HST_PORT)
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 ret |=3D dwc_otg_hcd_handle=
_port_intr(hcd);
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 if (gintsts & DWC_INTMSK_HST_CHAN)
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 ret |=3D dwc_otg_hcd_handle=
_hc_intr(hcd);
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 if (gintsts & DWC_INTMSK_P_TXFIFO_EMPTY)
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 ret |=3D dwc_otg_hcd_handle=
_perio_tx_fifo_empty_intr(hcd);
> +
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 spin_unlock(&hcd->lock);
> + =A0 =A0 =A0 }
> + =A0 =A0 =A0 return ret;
> +}
> --
> 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 =A0http://vger.kernel.org/majordomo-info.html
>
Reviewed-by: Pratyush Anand <pratyush.anand@st.com>
^ permalink raw reply
* Re: [PATCH v15 06/10] USB/ppc4xx: Add Synopsys DWC OTG HCD queue function
From: Pratyush Anand @ 2011-10-20 9:29 UTC (permalink / raw)
To: tmarri; +Cc: Mark Miesfeld, greg, linux-usb, linuxppc-dev, Fushen Chen
In-Reply-To: <1318630143-14327-1-git-send-email-tmarri@apm.com>
On Sat, Oct 15, 2011 at 3:39 AM, <tmarri@apm.com> wrote:
> From: Tirumala Marri <tmarri@apm.com>
>
> Implements functions to manage Queue Heads and Queue
> Transfer Descriptors of DWC USB OTG Controller.
>
> Signed-off-by: Tirumala R Marri <tmarri@apm.com>
> Signed-off-by: Fushen Chen <fchen@apm.com>
> Signed-off-by: Mark Miesfeld <mmiesfeld@apm.com>
> ---
> =A0drivers/usb/dwc/hcd_queue.c | =A0696 +++++++++++++++++++++++++++++++++=
++++++++++
> =A01 files changed, 696 insertions(+), 0 deletions(-)
> =A0create mode 100644 drivers/usb/dwc/hcd_queue.c
>
> diff --git a/drivers/usb/dwc/hcd_queue.c b/drivers/usb/dwc/hcd_queue.c
> new file mode 100644
> index 0000000..67f0409
> --- /dev/null
> +++ b/drivers/usb/dwc/hcd_queue.c
> @@ -0,0 +1,696 @@
> +/*
> + * DesignWare HS OTG controller driver
> + * Copyright (C) 2006 Synopsys, Inc.
> + * Portions Copyright (C) 2010 Applied Micro Circuits Corporation.
> + *
> + * This program is free software: you can redistribute it and/or
> + * modify it under the terms of the GNU General Public License
> + * version 2 as published by the Free Software Foundation.
> + *
> + * This program is distributed in the hope that it will be useful
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
> + * GNU General Public License version 2 for more details.
> + *
> + * You should have received a copy of the GNU General Public License
> + * along with this program; if not, see http://www.gnu.org/licenses
> + * or write to the Free Software Foundation, Inc., 51 Franklin Street,
> + * Suite 500, Boston, MA 02110-1335 USA.
> + *
> + * Based on Synopsys driver version 2.60a
> + * Modified by Mark Miesfeld <mmiesfeld@apm.com>
> + * Modified by Stefan Roese <sr@denx.de>, DENX Software Engineering
> + * Modified by Chuck Meade <chuck@theptrgroup.com>
> + *
> + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "=
AS IS"
> + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING BUT NOT LIMITED TO T=
HE
> + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PU=
RPOSE
> + * ARE DISCLAIMED. IN NO EVENT SHALL SYNOPSYS, INC. BE LIABLE FOR ANY DI=
RECT,
> + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES
> + * (INCLUDING BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SER=
VICES;
> + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUS=
ED AND
> + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY OR =
TORT
> + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE=
OF
> + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
> + *
> + */
> +
> +/*
> + * This file contains the functions to manage Queue Heads and Queue
> + * Transfer Descriptors.
> + */
> +
> +#include "hcd.h"
> +
> +static inline int is_fs_ls(enum usb_device_speed speed)
> +{
> + =A0 =A0 =A0 return speed =3D=3D USB_SPEED_FULL || speed =3D=3D USB_SPEE=
D_LOW;
> +}
> +
> +/* Allocates memory for a QH structure. */
> +static inline struct dwc_qh *dwc_otg_hcd_qh_alloc(void)
> +{
> + =A0 =A0 =A0 return kmalloc(sizeof(struct dwc_qh), GFP_ATOMIC);
> +}
> +
> +/**
> + * Initializes a QH structure to initialize the QH.
> + */
> +#define SCHEDULE_SLOP 10
> +static void dwc_otg_hcd_qh_init(struct dwc_hcd *hcd, struct dwc_qh *qh,
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 struct urb =
*urb)
> +{
> + =A0 =A0 =A0 memset(qh, 0, sizeof(struct dwc_qh));
> +
> + =A0 =A0 =A0 /* Initialize QH */
> + =A0 =A0 =A0 switch (usb_pipetype(urb->pipe)) {
> + =A0 =A0 =A0 case PIPE_CONTROL:
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 qh->ep_type =3D USB_ENDPOINT_XFER_CONTROL;
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 break;
> + =A0 =A0 =A0 case PIPE_BULK:
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 qh->ep_type =3D USB_ENDPOINT_XFER_BULK;
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 break;
> + =A0 =A0 =A0 case PIPE_ISOCHRONOUS:
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 qh->ep_type =3D USB_ENDPOINT_XFER_ISOC;
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 break;
> + =A0 =A0 =A0 case PIPE_INTERRUPT:
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 qh->ep_type =3D USB_ENDPOINT_XFER_INT;
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 break;
> + =A0 =A0 =A0 }
> +
> + =A0 =A0 =A0 qh->ep_is_in =3D usb_pipein(urb->pipe) ? 1 : 0;
> + =A0 =A0 =A0 qh->data_toggle =3D DWC_OTG_HC_PID_DATA0;
> + =A0 =A0 =A0 qh->maxp =3D usb_maxpacket(urb->dev, urb->pipe, !(usb_pipei=
n(urb->pipe)));
> +
> + =A0 =A0 =A0 INIT_LIST_HEAD(&qh->qtd_list);
> + =A0 =A0 =A0 INIT_LIST_HEAD(&qh->qh_list_entry);
> +
> + =A0 =A0 =A0 qh->channel =3D NULL;
> + =A0 =A0 =A0 qh->speed =3D urb->dev->speed;
> +
> + =A0 =A0 =A0 /*
> + =A0 =A0 =A0 =A0* FS/LS Enpoint on HS Hub NOT virtual root hub
> + =A0 =A0 =A0 =A0*/
> + =A0 =A0 =A0 qh->do_split =3D 0;
> + =A0 =A0 =A0 if (is_fs_ls(urb->dev->speed) && urb->dev->tt && urb->dev->=
tt->hub &&
> + =A0 =A0 =A0 =A0 =A0 urb->dev->tt->hub->devnum !=3D 1)
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 qh->do_split =3D 1;
> +
> + =A0 =A0 =A0 if (qh->ep_type =3D=3D USB_ENDPOINT_XFER_INT ||
> + =A0 =A0 =A0 =A0 =A0 qh->ep_type =3D=3D USB_ENDPOINT_XFER_ISOC) {
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 /* Compute scheduling parameters once and s=
ave them. */
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 u32 hprt;
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 int bytecount =3D dwc_hb_mult(qh->maxp) *
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 dwc_max_packet(qh->maxp);
> +
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 qh->usecs =3D NS_TO_US(usb_calc_bus_time(ur=
b->dev->speed,
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0=
=A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0usb_pipein(urb->pipe),
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0=
=A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0(qh->ep_type =3D=3D
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0=
=A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 USB_ENDPOINT_XFER_ISOC),
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0=
=A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0bytecount));
> +
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 /* Start in a slightly future (micro)frame.=
*/
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 qh->sched_frame =3D dwc_frame_num_inc(hcd->=
frame_number,
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0=
=A0 =A0 =A0 =A0 =A0 =A0 =A0 SCHEDULE_SLOP);
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 qh->interval =3D urb->interval;
> +
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 hprt =3D dwc_reg_read(hcd->core_if->host_if=
->hprt0, 0);
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 if (DWC_HPRT0_PRT_SPD_RD(hprt) =3D=3D DWC_H=
PRT0_PRTSPD_HIGH_SPEED &&
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 is_fs_ls(urb->dev->speed)) {
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 qh->interval *=3D 8;
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 qh->sched_frame |=3D 0x7;
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 qh->start_split_frame =3D q=
h->sched_frame;
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 }
> + =A0 =A0 =A0 }
> +}
> +
> +/**
> + * This function allocates and initializes a QH.
> + */
> +static struct dwc_qh *dwc_otg_hcd_qh_create(struct dwc_hcd *hcd,
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0=
=A0 =A0 =A0 struct urb *urb)
> +{
> + =A0 =A0 =A0 struct dwc_qh *qh;
> +
> + =A0 =A0 =A0 /* Allocate memory */
> + =A0 =A0 =A0 qh =3D dwc_otg_hcd_qh_alloc();
> + =A0 =A0 =A0 if (qh =3D=3D NULL)
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 return NULL;
> +
> + =A0 =A0 =A0 dwc_otg_hcd_qh_init(hcd, qh, urb);
> + =A0 =A0 =A0 return qh;
> +}
> +
> +/**
> + * Free each QTD in the QH's QTD-list then free the QH. =A0QH should alr=
eady be
> + * removed from a list. =A0QTD list should already be empty if called fr=
om URB
> + * Dequeue.
> + */
> +void dwc_otg_hcd_qh_free(struct dwc_qh *qh)
> +{
> + =A0 =A0 =A0 struct dwc_qtd *qtd;
> + =A0 =A0 =A0 struct list_head *pos, *temp;
> +
> + =A0 =A0 =A0 /* Free each QTD in the QTD list */
> + =A0 =A0 =A0 list_for_each_safe(pos, temp, &qh->qtd_list) {
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 list_del(pos);
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 qtd =3D dwc_list_to_qtd(pos);
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 dwc_otg_hcd_qtd_free(qtd);
> + =A0 =A0 =A0 }
> + =A0 =A0 =A0 kfree(qh);
> +}
> +
> +/**
> + * Microframe scheduler
> + * track the total use in hcd->frame_usecs
> + * keep each qh use in qh->frame_usecs
> + * when surrendering the qh then donate the time back
> + */
> +static const u16 max_uframe_usecs[] =3D { 100, 100, 100, 100, 100, 100, =
30, 0 };
> +
> +/*
> + * called from dwc_otg_hcd.c:dwc_otg_hcd_init
> + */
> +int init_hcd_usecs(struct dwc_hcd *hcd)
> +{
> + =A0 =A0 =A0 int i;
> +
> + =A0 =A0 =A0 for (i =3D 0; i < 8; i++)
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 hcd->frame_usecs[i] =3D max_uframe_usecs[i]=
;
> +
> + =A0 =A0 =A0 return 0;
> +}
> +
> +static int find_single_uframe(struct dwc_hcd *hcd, struct dwc_qh *qh)
> +{
> + =A0 =A0 =A0 int i;
> + =A0 =A0 =A0 u16 utime;
> + =A0 =A0 =A0 int t_left;
> + =A0 =A0 =A0 int ret;
> + =A0 =A0 =A0 int done;
> +
> + =A0 =A0 =A0 ret =3D -1;
> + =A0 =A0 =A0 utime =3D qh->usecs;
> + =A0 =A0 =A0 t_left =3D utime;
> + =A0 =A0 =A0 i =3D 0;
> + =A0 =A0 =A0 done =3D 0;
> + =A0 =A0 =A0 while (done =3D=3D 0) {
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 /* At the start hcd->frame_usecs[i] =3D max=
_uframe_usecs[i]; */
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 if (utime <=3D hcd->frame_usecs[i]) {
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 hcd->frame_usecs[i] -=3D ut=
ime;
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 qh->frame_usecs[i] +=3D uti=
me;
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 t_left -=3D utime;
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 ret =3D i;
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 done =3D 1;
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 return ret;
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 } else {
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 i++;
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 if (i =3D=3D 8) {
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 done =3D 1;
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 ret =3D -1;
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 }
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 }
> + =A0 =A0 =A0 }
> + =A0 =A0 =A0 return ret;
> +}
> +
> +/*
> + * use this for FS apps that can span multiple uframes
> + */
> +static int find_multi_uframe(struct dwc_hcd *hcd, struct dwc_qh *qh)
> +{
> + =A0 =A0 =A0 int i;
> + =A0 =A0 =A0 int j;
> + =A0 =A0 =A0 u16 utime;
> + =A0 =A0 =A0 int t_left;
> + =A0 =A0 =A0 int ret;
> + =A0 =A0 =A0 int done;
> + =A0 =A0 =A0 u16 xtime;
> +
> + =A0 =A0 =A0 ret =3D -1;
> + =A0 =A0 =A0 utime =3D qh->usecs;
> + =A0 =A0 =A0 t_left =3D utime;
> + =A0 =A0 =A0 i =3D 0;
> + =A0 =A0 =A0 done =3D 0;
> +loop:
> + =A0 =A0 =A0 while (done =3D=3D 0) {
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 if (hcd->frame_usecs[i] <=3D 0) {
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 i++;
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 if (i =3D=3D 8) {
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 done =3D 1;
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 ret =3D -1;
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 }
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 goto loop;
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 }
> +
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 /*
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0* We need n consequtive slots so use j a=
s a start slot.
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0* j plus j+1 must be enough time (for no=
w)
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0*/
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 xtime =3D hcd->frame_usecs[i];
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 for (j =3D i + 1; j < 8; j++) {
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 /*
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0* if we add this frame r=
emaining time to xtime we may
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0* be OK, if not we need =
to test j for a complete frame.
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0*/
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 if ((xtime + hcd->frame_use=
cs[j]) < utime) {
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 if (hcd->fr=
ame_usecs[j] < max_uframe_usecs[j]) {
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0=
=A0 j =3D 8;
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0=
=A0 ret =3D -1;
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0=
=A0 continue;
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 }
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 }
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 if (xtime >=3D utime) {
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 ret =3D i;
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 j =3D 8; =
=A0/* stop loop with a good value ret */
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 continue;
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 }
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 /* add the frame time to x =
time */
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 xtime +=3D hcd->frame_usecs=
[j];
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 /* we must have a fully ava=
ilable next frame or break */
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 if ((xtime < utime) &&
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 (hcd->frame_usecs[j=
] =3D=3D max_uframe_usecs[j])) {
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 ret =3D -1;
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 j =3D 8; =
=A0/* stop loop with a bad value ret */
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 continue;
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 }
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 }
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 if (ret >=3D 0) {
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 t_left =3D utime;
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 for (j =3D i; (t_left > 0) =
&& (j < 8); j++) {
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 t_left -=3D=
hcd->frame_usecs[j];
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 if (t_left =
<=3D 0) {
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0=
=A0 qh->frame_usecs[j] +=3D
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0=
=A0 =A0 =A0 hcd->frame_usecs[j] + t_left;
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0=
=A0 hcd->frame_usecs[j] =3D -t_left;
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0=
=A0 ret =3D i;
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0=
=A0 done =3D 1;
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 } else {
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0=
=A0 qh->frame_usecs[j] +=3D
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0=
=A0 =A0 =A0 hcd->frame_usecs[j];
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0=
=A0 hcd->frame_usecs[j] =3D 0;
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 }
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 }
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 } else {
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 i++;
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 if (i =3D=3D 8) {
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 done =3D 1;
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 ret =3D -1;
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 }
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 }
> + =A0 =A0 =A0 }
> + =A0 =A0 =A0 return ret;
> +}
> +
> +static int find_uframe(struct dwc_hcd *hcd, struct dwc_qh *qh)
> +{
> + =A0 =A0 =A0 int ret =3D -1;
> +
> + =A0 =A0 =A0 if (qh->speed =3D=3D USB_SPEED_HIGH)
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 /* if this is a hs transaction we need a fu=
ll frame */
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 ret =3D find_single_uframe(hcd, qh);
> + =A0 =A0 =A0 else
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 /* FS transaction may need a sequence of fr=
ames */
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 ret =3D find_multi_uframe(hcd, qh);
> +
> + =A0 =A0 =A0 return ret;
> +}
> +
> +/**
> + * Checks that the max transfer size allowed in a host channel is large =
enough
> + * to handle the maximum data transfer in a single (micro)frame for a pe=
riodic
> + * transfer.
> + */
> +static int check_max_xfer_size(struct dwc_hcd *hcd, struct dwc_qh *qh)
> +{
> + =A0 =A0 =A0 int status =3D 0;
> + =A0 =A0 =A0 u32 max_xfer_size;
> + =A0 =A0 =A0 u32 max_channel_xfer_size;
> +
> + =A0 =A0 =A0 max_xfer_size =3D dwc_max_packet(qh->maxp) * dwc_hb_mult(qh=
->maxp);
> + =A0 =A0 =A0 max_channel_xfer_size =3D hcd->core_if->core_params->max_tr=
ansfer_size;
> +
> + =A0 =A0 =A0 if (max_xfer_size > max_channel_xfer_size) {
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 pr_notice("%s: Periodic xfer length %d > ma=
x xfer "
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 "length for channel %d\=
n", __func__, max_xfer_size,
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 max_channel_xfer_size);
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 status =3D -ENOSPC;
> + =A0 =A0 =A0 }
> +
> + =A0 =A0 =A0 return status;
> +}
> +
> +/**
> + * Schedules an interrupt or isochronous transfer in the periodic schedu=
le.
> + */
> +static int schedule_periodic(struct dwc_hcd *hcd, struct dwc_qh *qh)
> +{
> + =A0 =A0 =A0 int status;
> + =A0 =A0 =A0 struct usb_bus *bus =3D hcd_to_bus(dwc_otg_hcd_to_hcd(hcd))=
;
> + =A0 =A0 =A0 int frame;
> +
> + =A0 =A0 =A0 status =3D find_uframe(hcd, qh);
> + =A0 =A0 =A0 frame =3D -1;
> + =A0 =A0 =A0 if (status =3D=3D 0) {
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 frame =3D 7;
> + =A0 =A0 =A0 } else {
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 if (status > 0)
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 frame =3D status - 1;
> + =A0 =A0 =A0 }
> + =A0 =A0 =A0 /* Set the new frame up */
> + =A0 =A0 =A0 if (frame > -1) {
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 qh->sched_frame &=3D ~0x7;
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 qh->sched_frame |=3D (frame & 7);
> + =A0 =A0 =A0 }
> + =A0 =A0 =A0 if (status !=3D -1)
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 status =3D 0;
> + =A0 =A0 =A0 if (status) {
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 pr_notice("%s: Insufficient periodic bandwi=
dth for "
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 "periodic transfer.\n",=
__func__);
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 return status;
> + =A0 =A0 =A0 }
> + =A0 =A0 =A0 status =3D check_max_xfer_size(hcd, qh);
> + =A0 =A0 =A0 if (status) {
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 pr_notice("%s: Channel max transfer size to=
o small "
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 "for periodic transfer.=
\n", __func__);
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 return status;
> + =A0 =A0 =A0 }
> + =A0 =A0 =A0 /* Always start in the inactive schedule. */
> + =A0 =A0 =A0 list_add_tail(&qh->qh_list_entry, &hcd->periodic_sched_inac=
tive);
> +
> + =A0 =A0 =A0 /* Update claimed usecs per (micro)frame. */
> + =A0 =A0 =A0 hcd->periodic_usecs +=3D qh->usecs;
> +
> + =A0 =A0 =A0 /*
> + =A0 =A0 =A0 =A0* Update average periodic bandwidth claimed and # period=
ic reqs for
> + =A0 =A0 =A0 =A0* usbfs.
> + =A0 =A0 =A0 =A0*/
> + =A0 =A0 =A0 bus->bandwidth_allocated +=3D qh->usecs / qh->interval;
> +
> + =A0 =A0 =A0 if (qh->ep_type =3D=3D USB_ENDPOINT_XFER_INT)
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 bus->bandwidth_int_reqs++;
> + =A0 =A0 =A0 else
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 bus->bandwidth_isoc_reqs++;
> +
> + =A0 =A0 =A0 return status;
> +}
> +
> +/**
> + * This function adds a QH to either the non periodic or periodic schedu=
le if
> + * it is not already in the schedule. If the QH is already in the schedu=
le, no
> + * action is taken.
> + */
> +static int dwc_otg_hcd_qh_add(struct dwc_hcd *hcd, struct dwc_qh *qh)
> +{
> + =A0 =A0 =A0 int status =3D 0;
> +
> + =A0 =A0 =A0 /* QH may already be in a schedule. */
> + =A0 =A0 =A0 if (!list_empty(&qh->qh_list_entry))
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 goto done;
> + =A0 =A0 =A0 /*
> + =A0 =A0 =A0 =A0* Add the new QH to the appropriate schedule. For non-pe=
riodic, always
> + =A0 =A0 =A0 =A0* start in the inactive schedule.
> + =A0 =A0 =A0 =A0*/
> + =A0 =A0 =A0 if (dwc_qh_is_non_per(qh))
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 list_add_tail(&qh->qh_list_entry,
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 &hcd->non_perio=
dic_sched_inactive);
> + =A0 =A0 =A0 else
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 status =3D schedule_periodic(hcd, qh);
> +
> +done:
> + =A0 =A0 =A0 return status;
> +}
> +
> +/**
> + * This function adds a QH to the non periodic deferred schedule.
> + *
> + * @return 0 if successful, negative error code otherwise.
> + */
> +static int dwc_otg_hcd_qh_add_deferred(struct dwc_hcd *hcd, struct dwc_q=
h *qh)
> +{
> + =A0 =A0 =A0 if (!list_empty(&qh->qh_list_entry))
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 /* QH already in a schedule. */
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 goto done;
> +
> + =A0 =A0 =A0 /* Add the new QH to the non periodic deferred schedule */
> + =A0 =A0 =A0 if (dwc_qh_is_non_per(qh))
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 list_add_tail(&qh->qh_list_entry,
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 &hcd->non_perio=
dic_sched_deferred);
> +done:
> + =A0 =A0 =A0 return 0;
> +}
> +
> +/**
> + * Removes an interrupt or isochronous transfer from the periodic schedu=
le.
> + */
> +static void deschedule_periodic(struct dwc_hcd *hcd, struct dwc_qh *qh)
> +{
> + =A0 =A0 =A0 struct usb_bus *bus =3D hcd_to_bus(dwc_otg_hcd_to_hcd(hcd))=
;
> + =A0 =A0 =A0 int i;
> +
> + =A0 =A0 =A0 list_del_init(&qh->qh_list_entry);
> + =A0 =A0 =A0 /* Update claimed usecs per (micro)frame. */
> + =A0 =A0 =A0 hcd->periodic_usecs -=3D qh->usecs;
> + =A0 =A0 =A0 for (i =3D 0; i < 8; i++) {
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 hcd->frame_usecs[i] +=3D qh->frame_usecs[i]=
;
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 qh->frame_usecs[i] =3D 0;
> + =A0 =A0 =A0 }
> + =A0 =A0 =A0 /*
> + =A0 =A0 =A0 =A0* Update average periodic bandwidth claimed and # period=
ic reqs for
> + =A0 =A0 =A0 =A0* usbfs.
> + =A0 =A0 =A0 =A0*/
> + =A0 =A0 =A0 bus->bandwidth_allocated -=3D qh->usecs / qh->interval;
> +
> + =A0 =A0 =A0 if (qh->ep_type =3D=3D USB_ENDPOINT_XFER_INT)
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 bus->bandwidth_int_reqs--;
> + =A0 =A0 =A0 else
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 bus->bandwidth_isoc_reqs--;
> +}
> +
> +/**
> + * Removes a QH from either the non-periodic or periodic schedule. =A0Me=
mory is
> + * not freed.
> + */
> +void dwc_otg_hcd_qh_remove(struct dwc_hcd *hcd, struct dwc_qh *qh)
> +{
> + =A0 =A0 =A0 /* Do nothing if QH is not in a schedule */
> + =A0 =A0 =A0 if (list_empty(&qh->qh_list_entry))
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 return;
> +
> + =A0 =A0 =A0 if (dwc_qh_is_non_per(qh)) {
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 if (hcd->non_periodic_qh_ptr =3D=3D &qh->qh=
_list_entry)
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 hcd->non_periodic_qh_ptr =
=3D
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 hcd->non_periodic_q=
h_ptr->next;
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 list_del_init(&qh->qh_list_entry);
> + =A0 =A0 =A0 } else {
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 deschedule_periodic(hcd, qh);
> + =A0 =A0 =A0 }
> +}
> +
> +/**
> + * Defers a QH. For non-periodic QHs, removes the QH from the active
> + * non-periodic schedule. The QH is added to the deferred non-periodic
> + * schedule if any QTDs are still attached to the QH.
> + */
> +int dwc_otg_hcd_qh_deferr(struct dwc_hcd *hcd, struct dwc_qh *qh, int de=
lay)
> +{
> + =A0 =A0 =A0 int deact =3D 1;
> +
> + =A0 =A0 =A0 if (dwc_qh_is_non_per(qh)) {
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 qh->sched_frame =3D dwc_frame_num_inc(hcd->=
frame_number, delay);
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 qh->channel =3D NULL;
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 qh->qtd_in_process =3D NULL;
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 deact =3D 0;
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 dwc_otg_hcd_qh_remove(hcd, qh);
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 if (!list_empty(&qh->qtd_list))
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 /* Add back to deferred non=
-periodic schedule. */
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 dwc_otg_hcd_qh_add_deferred=
(hcd, qh);
> + =A0 =A0 =A0 }
> + =A0 =A0 =A0 return deact;
> +}
> +
> +/**
> + * =A0Schedule the next continuing periodic split transfer
> + */
> +static void sched_next_per_split_xfr(struct dwc_qh *qh, u16 fr_num,
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0=
int sched_split)
> +{
> + =A0 =A0 =A0 if (sched_split) {
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 qh->sched_frame =3D fr_num;
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 if (dwc_frame_num_le(fr_num,
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0=
dwc_frame_num_inc(qh->start_split_frame,
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0=
=A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A01))) {
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 /*
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0* Allow one frame to ela=
pse after start split
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0* microframe before sche=
duling complete split, but DONT
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0* if we are doing the ne=
xt start split in the
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0* same frame for an ISOC=
out.
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0*/
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 if (qh->ep_type !=3D USB_EN=
DPOINT_XFER_ISOC ||
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 qh->ep_is_in)
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 qh->sched_f=
rame =3D
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 dwc=
_frame_num_inc(qh->sched_frame, 1);
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 }
> + =A0 =A0 =A0 } else {
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 qh->sched_frame =3D dwc_frame_num_inc(qh->s=
tart_split_frame,
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0=
=A0 =A0 =A0 =A0 =A0 =A0 =A0 qh->interval);
> +
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 if (dwc_frame_num_le(qh->sched_frame, fr_nu=
m))
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 qh->sched_frame =3D fr_num;
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 qh->sched_frame |=3D 0x7;
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 qh->start_split_frame =3D qh->sched_frame;
> + =A0 =A0 =A0 }
> +}
> +
> +/**
> + * Deactivates a periodic QH. =A0The QH is removed from the periodic que=
ued
> + * schedule. If there are any QTDs still attached to the QH, the QH is a=
dded to
> + * either the periodic inactive schedule or the periodic ready schedule =
and its
> + * next scheduled frame is calculated. The QH is placed in the ready sch=
edule if
> + * the scheduled frame has been reached already. Otherwise it's placed i=
n the
> + * inactive schedule. If there are no QTDs attached to the QH, the QH is
> + * completely removed from the periodic schedule.
> + */
> +static void deactivate_periodic_qh(struct dwc_hcd *hcd, struct dwc_qh *q=
h,
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0int =
sched_next_split)
> +{
> + =A0 =A0 =A0 /* unsigned long flags; */
> + =A0 =A0 =A0 u16 fr_num =3D dwc_otg_hcd_get_frame_number(dwc_otg_hcd_to_=
hcd(hcd));
> +
> + =A0 =A0 =A0 if (qh->do_split) {
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 sched_next_per_split_xfr(qh, fr_num, sched_=
next_split);
> + =A0 =A0 =A0 } else {
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 qh->sched_frame =3D dwc_frame_num_inc(qh->s=
ched_frame,
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0=
=A0 =A0 =A0 =A0 =A0 =A0 =A0 qh->interval);
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 if (dwc_frame_num_le(qh->sched_frame, fr_nu=
m))
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 qh->sched_frame =3D fr_num;
> + =A0 =A0 =A0 }
> +
> + =A0 =A0 =A0 if (list_empty(&qh->qtd_list)) {
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 dwc_otg_hcd_qh_remove(hcd, qh);
> + =A0 =A0 =A0 } else {
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 /*
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0* Remove from periodic_sched_queued and =
move to appropriate
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0* queue.
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0*/
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 if (qh->sched_frame =3D=3D fr_num)
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 list_move(&qh->qh_list_entr=
y,
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 &hcd->p=
eriodic_sched_ready);
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 else
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 list_move(&qh->qh_list_entr=
y,
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 &hcd->p=
eriodic_sched_inactive);
> + =A0 =A0 =A0 }
> +}
> +
> +/**
> + * Deactivates a non-periodic QH. =A0Removes the QH from the active non-=
periodic
> + * schedule. The QH is added to the inactive non-periodic schedule if an=
y QTDs
> + * are still attached to the QH.
> + */
> +static void deactivate_non_periodic_qh(struct dwc_hcd *hcd, struct dwc_q=
h *qh)
> +{
> + =A0 =A0 =A0 dwc_otg_hcd_qh_remove(hcd, qh);
> + =A0 =A0 =A0 if (!list_empty(&qh->qtd_list))
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 dwc_otg_hcd_qh_add(hcd, qh);
> +}
> +
> +/**
> + * Deactivates a QH. =A0Determines if the QH is periodic or non-periodic=
and takes
> + * the appropriate action.
> + */
> +void dwc_otg_hcd_qh_deactivate(struct dwc_hcd *hcd, struct dwc_qh *qh,
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0int sched_ne=
xt_periodic_split)
> +{
> + =A0 =A0 =A0 if (dwc_qh_is_non_per(qh))
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 deactivate_non_periodic_qh(hcd, qh);
> + =A0 =A0 =A0 else
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 deactivate_periodic_qh(hcd, qh, sched_next_=
periodic_split);
> +}
> +
> +/**
> + * Initializes a QTD structure.
> + */
> +static void dwc_otg_hcd_qtd_init(struct dwc_qtd *qtd, struct urb *urb)
> +{
> + =A0 =A0 =A0 memset(qtd, 0, sizeof(struct dwc_qtd));
> + =A0 =A0 =A0 qtd->urb =3D urb;
> +
> + =A0 =A0 =A0 if (usb_pipecontrol(urb->pipe)) {
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 /*
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0* The only time the QTD data toggle is u=
sed is on the data
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0* phase of control transfers. This phase=
always starts with
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0* DATA1.
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0*/
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 qtd->data_toggle =3D DWC_OTG_HC_PID_DATA1;
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 qtd->control_phase =3D DWC_OTG_CONTROL_SETU=
P;
> + =A0 =A0 =A0 }
> +
> + =A0 =A0 =A0 /* start split */
> + =A0 =A0 =A0 qtd->complete_split =3D 0;
> + =A0 =A0 =A0 qtd->isoc_split_pos =3D DWC_HCSPLIT_XACTPOS_ALL;
> + =A0 =A0 =A0 qtd->isoc_split_offset =3D 0;
> +
> + =A0 =A0 =A0 /* Store the qtd ptr in the urb to reference what QTD. */
> + =A0 =A0 =A0 urb->hcpriv =3D qtd;
> +
> + =A0 =A0 =A0 INIT_LIST_HEAD(&qtd->qtd_list_entry);
> + =A0 =A0 =A0 return;
> +}
> +
> +/* Allocates memory for a QTD structure. */
> +static inline struct dwc_qtd *dwc_otg_hcd_qtd_alloc(gfp_t _mem_flags)
> +{
> + =A0 =A0 =A0 return kmalloc(sizeof(struct dwc_qtd), _mem_flags);
> +}
> +
> +/**
> + * This function allocates and initializes a QTD.
> + */
> +struct dwc_qtd *dwc_otg_hcd_qtd_create(struct urb *urb, gfp_t _mem_flags=
)
> +{
> + =A0 =A0 =A0 struct dwc_qtd *qtd =3D dwc_otg_hcd_qtd_alloc(_mem_flags);
> +
> + =A0 =A0 =A0 if (!qtd)
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 return NULL;
> +
> + =A0 =A0 =A0 dwc_otg_hcd_qtd_init(qtd, urb);
> + =A0 =A0 =A0 return qtd;
> +}
> +
> +/**
> + * This function adds a QTD to the QTD-list of a QH. =A0It will find the=
correct
> + * QH to place the QTD into. =A0If it does not find a QH, then it will c=
reate a
> + * new QH. If the QH to which the QTD is added is not currently schedule=
d, it
> + * is placed into the proper schedule based on its EP type.
> + *
> + */
> +int dwc_otg_hcd_qtd_add(struct dwc_qtd *qtd, struct dwc_hcd *hcd)
> +{
> + =A0 =A0 =A0 struct usb_host_endpoint *ep;
> + =A0 =A0 =A0 struct dwc_qh *qh;
> + =A0 =A0 =A0 int retval =3D 0;
> + =A0 =A0 =A0 struct urb *urb =3D qtd->urb;
> +
> + =A0 =A0 =A0 /*
> + =A0 =A0 =A0 =A0* Get the QH which holds the QTD-list to insert to. Crea=
te QH if it
> + =A0 =A0 =A0 =A0* doesn't exist.
> + =A0 =A0 =A0 =A0*/
> + =A0 =A0 =A0 ep =3D dwc_urb_to_endpoint(urb);
> +
> + =A0 =A0 =A0 qh =3D (struct dwc_qh *)ep->hcpriv;
> + =A0 =A0 =A0 if (!qh) {
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 qh =3D dwc_otg_hcd_qh_create(hcd, urb);
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 if (!qh) {
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 retval =3D -1;
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 goto done;
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 }
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 ep->hcpriv =3D qh;
> + =A0 =A0 =A0 }
> + =A0 =A0 =A0 qtd->qtd_qh_ptr =3D qh;
> + =A0 =A0 =A0 retval =3D dwc_otg_hcd_qh_add(hcd, qh);
> + =A0 =A0 =A0 if (!retval)
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 list_add_tail(&qtd->qtd_list_entry, &qh->qt=
d_list);
> +
> +done:
> + =A0 =A0 =A0 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 =A0http://vger.kernel.org/majordomo-info.html
>
Reviewed-by: Pratyush Anand <pratyush.anand@st.com>
^ permalink raw reply
* Re: [PATCH v15 07/10] USB/ppc4xx: Add Synopsys DWC OTG PCD function
From: Pratyush Anand @ 2011-10-20 9:55 UTC (permalink / raw)
To: tmarri; +Cc: Mark Miesfeld, greg, linux-usb, linuxppc-dev, Fushen Chen
In-Reply-To: <1318630147-14358-1-git-send-email-tmarri@apm.com>
On Sat, Oct 15, 2011 at 3:39 AM, <tmarri@apm.com> wrote:
> From: Tirumala Marri <tmarri@apm.com>
>
> The PCD is responsible for translating requests from the gadget driver
> to appropriate actions on the DWC OTG controller.
>
[...]
> +static int dwc_otg_pcd_ep_disable(struct usb_ep *_ep)
> +{
> + struct pcd_ep *ep;
> + struct core_if *core_if;
> + unsigned long flags;
> +
> + ep = container_of(_ep, struct pcd_ep, ep);
> + if (!_ep || !ep->desc)
> + return -EINVAL;
> +
> + core_if = ep->pcd->otg_dev->core_if;
> +
> + spin_lock_irqsave(&ep->pcd->lock, flags);
> +
> + request_nuke(ep);
> + dwc_otg_ep_deactivate(core_if, &ep->dwc_ep);
> +
> + ep->desc = NULL;
> + ep->stopped = 1;
> + if (ep->dwc_ep.is_in) {
> + release_perio_tx_fifo(core_if, ep->dwc_ep.tx_fifo_num);
> + release_tx_fifo(core_if, ep->dwc_ep.tx_fifo_num);
Is only releasing fifo sufficient?
Will the above procedure insures that EP is disable?
I think not.
To insure that EP is disabled , you need to fllow steps as defines in specs.
Also, Fifo must be flushed completely.
Just a question: Which test have you run to test the driver?
I had tested your v13 with testusb and zero gadget and found that all the IN
test were failing because of this limitation.
> + }
> +
[...]
> +/**
> + * Set the EP STALL.
> + */
> +void dwc_otg_ep_set_stall(struct core_if *core_if, struct dwc_ep *ep)
> +{
> + u32 depctl = 0;
> + ulong depctl_addr;
> +
> + if (ep->is_in) {
> + depctl_addr =
> + (core_if->dev_if->in_ep_regs[ep->num]) + DWC_DIEPCTL;
> + depctl = dwc_reg_read(depctl_addr, 0);
> +
> + /* set the disable and stall bits */
> + if (DWC_DEPCTL_EPENA_RD(depctl))
> + depctl = DWC_DEPCTL_EPDIS_RW(depctl, 1);
> + depctl = DWC_DEPCTL_STALL_HNDSHK_RW(depctl, 1);
> + dwc_reg_write(depctl_addr, 0, depctl);
After stall bit set, you must disable EP.
Also you must clear TX fifo completetly for respective EP.
> + } else {
> + depctl_addr =
> + (core_if->dev_if->out_ep_regs[ep->num] + DWC_DOEPCTL);
> + depctl = dwc_reg_read(depctl_addr, 0);
> +
> + /* set the stall bit */
> + depctl = DWC_DEPCTL_STALL_HNDSHK_RW(depctl, 1);
> + dwc_reg_write(depctl_addr, 0, depctl);
> + }
> +}
Regards
Pratyush
^ permalink raw reply
* Re: [PATCH v15 08/10] USB ppc4xx: Add Synopsys DWC OTG PCD interrupt function
From: Pratyush Anand @ 2011-10-20 10:12 UTC (permalink / raw)
To: tmarri; +Cc: Mark Miesfeld, greg, linux-usb, linuxppc-dev, Fushen Chen
In-Reply-To: <1318630151-14394-1-git-send-email-tmarri@apm.com>
On Sat, Oct 15, 2011 at 3:39 AM, <tmarri@apm.com> wrote:
> From: Tirumala Marri <tmarri@apm.com>
>
[...]
> +static int write_empty_tx_fifo(struct dwc_pcd *pcd, u32 epnum)
> +{
> + struct core_if *core_if = GET_CORE_IF(pcd);
> + ulong regs;
> + u32 txstatus = 0;
> + struct pcd_ep *ep;
> + u32 len;
> + int dwords;
> + u32 diepint = 0;
> +
> + ep = get_in_ep(pcd, epnum);
> + regs = core_if->dev_if->in_ep_regs[epnum];
> + txstatus = dwc_reg_read(regs, DWC_DTXFSTS);
> +
> + /*
> + * While there is space in the queue, space in the FIFO and data to
> + * tranfer, write packets to the Tx FIFO
> + */
> + len = ep->dwc_ep.xfer_len - ep->dwc_ep.xfer_count;
> + dwords = count_dwords(ep, len);
> + while (DWC_DTXFSTS_TXFSSPC_AVAI_RD(txstatus) > dwords
If you use a >= here, it will be more efficient.
Other wise even if you have 512 bytes spece remaining in TXFIFO, you will not be
able to send a 512 byte packet.
> + && ep->dwc_ep.xfer_count < ep->dwc_ep.xfer_len
> + && ep->dwc_ep.xfer_len != 0) {
> + dwc_otg_ep_write_packet(core_if, &ep->dwc_ep, 0);
> + len = ep->dwc_ep.xfer_len - ep->dwc_ep.xfer_count;
> + dwords = count_dwords(ep, len);
> + txstatus = dwc_reg_read(regs, DWC_DTXFSTS);
> + }
> + /* Clear emptyintr */
> + diepint = DWC_DIEPINT_TXFIFO_EMPTY_RW(diepint, 1);
> + dwc_reg_write(in_ep_int_reg(pcd, epnum), 0, diepint);
> + return 1;
> +}
[...]
> +/**
> + * Clear the EP halt (STALL) and if pending requests start the
> + * transfer.
> + */
> +static void pcd_clear_halt(struct dwc_pcd *pcd, struct pcd_ep *ep)
> +{
> + struct core_if *core_if = GET_CORE_IF(pcd);
> +
> + if (!ep->dwc_ep.stall_clear_flag)
> + dwc_otg_ep_clear_stall(core_if, &ep->dwc_ep);
> +
> + /* Reactive the EP */
> + dwc_otg_ep_activate(core_if, &ep->dwc_ep);
> +
> + if (ep->stopped) {
> + ep->stopped = 0;
> + /* If there is a request in the EP queue start it */
> +
> + /*
> + * dwc_start_next_request(), outside of interpt contxt at some
> + * time after the current time, after a clear-halt setup packet.
> + * Still need to implement ep mismatch in the future if a gadget
> + * ever uses more than one endpoint at once
> + */
> + if (core_if->dma_enable) {
> + ep->queue_sof = 1;
> + tasklet_schedule(pcd->start_xfer_tasklet);
> + } else {
> + /*
> + * Added-sr: 2007-07-26
> + *
> + * To re-enable this endpoint it's important to
> + * set this next_ep number. Otherwise the endpoint
> + * will not get active again after stalling.
> + */
> + if (dwc_has_feature(core_if, DWC_LIMITED_XFER))
> + dwc_start_next_request(ep);
Should next request be not started for all the cases?
> + }
> + }
> +
> + /* Start Control Status Phase */
> + do_setup_in_status_phase(pcd);
> +}
> +
Regards
Pratyush
^ permalink raw reply
* Re: [PATCH v15 06/10] USB/ppc4xx: Add Synopsys DWC OTG HCD queue function
From: Philipp Ittershagen @ 2011-10-20 11:29 UTC (permalink / raw)
To: tmarri; +Cc: greg, linux-usb, linuxppc-dev, Fushen Chen, Mark Miesfeld
In-Reply-To: <1318630143-14327-1-git-send-email-tmarri@apm.com>
Hello Tirumala,
I have some coding style comments below.
On Sat, Oct 15, 2011 at 12:09 AM, <tmarri@apm.com> wrote:
> From: Tirumala Marri <tmarri@apm.com>
>
> Implements functions to manage Queue Heads and Queue
> Transfer Descriptors of DWC USB OTG Controller.
>
> Signed-off-by: Tirumala R Marri <tmarri@apm.com>
> Signed-off-by: Fushen Chen <fchen@apm.com>
> Signed-off-by: Mark Miesfeld <mmiesfeld@apm.com>
> ---
> drivers/usb/dwc/hcd_queue.c | 696 +++++++++++++++++++++++++++++++++++++++++++
> 1 files changed, 696 insertions(+), 0 deletions(-)
> create mode 100644 drivers/usb/dwc/hcd_queue.c
>
> diff --git a/drivers/usb/dwc/hcd_queue.c b/drivers/usb/dwc/hcd_queue.c
> new file mode 100644
> index 0000000..67f0409
> --- /dev/null
> +++ b/drivers/usb/dwc/hcd_queue.c
> @@ -0,0 +1,696 @@
> +/*
> + * DesignWare HS OTG controller driver
> + * Copyright (C) 2006 Synopsys, Inc.
> + * Portions Copyright (C) 2010 Applied Micro Circuits Corporation.
> + *
> + * This program is free software: you can redistribute it and/or
> + * modify it under the terms of the GNU General Public License
> + * version 2 as published by the Free Software Foundation.
> + *
> + * This program is distributed in the hope that it will be useful
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
> + * GNU General Public License version 2 for more details.
> + *
> + * You should have received a copy of the GNU General Public License
> + * along with this program; if not, see http://www.gnu.org/licenses
> + * or write to the Free Software Foundation, Inc., 51 Franklin Street,
> + * Suite 500, Boston, MA 02110-1335 USA.
> + *
> + * Based on Synopsys driver version 2.60a
> + * Modified by Mark Miesfeld <mmiesfeld@apm.com>
> + * Modified by Stefan Roese <sr@denx.de>, DENX Software Engineering
> + * Modified by Chuck Meade <chuck@theptrgroup.com>
> + *
> + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
> + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING BUT NOT LIMITED TO THE
> + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
> + * ARE DISCLAIMED. IN NO EVENT SHALL SYNOPSYS, INC. BE LIABLE FOR ANY DIRECT,
> + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES
> + * (INCLUDING BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
> + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
> + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY OR TORT
> + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
> + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
> + *
> + */
> +
> +/*
> + * This file contains the functions to manage Queue Heads and Queue
> + * Transfer Descriptors.
> + */
> +
> +#include "hcd.h"
> +
> +static inline int is_fs_ls(enum usb_device_speed speed)
> +{
> + return speed == USB_SPEED_FULL || speed == USB_SPEED_LOW;
> +}
> +
> +/* Allocates memory for a QH structure. */
> +static inline struct dwc_qh *dwc_otg_hcd_qh_alloc(void)
> +{
> + return kmalloc(sizeof(struct dwc_qh), GFP_ATOMIC);
> +}
Why do you have this extra function? I know its inlined, but it does not
increase code readability.
> +
> +/**
> + * Initializes a QH structure to initialize the QH.
> + */
> +#define SCHEDULE_SLOP 10
> +static void dwc_otg_hcd_qh_init(struct dwc_hcd *hcd, struct dwc_qh *qh,
> + struct urb *urb)
> +{
> + memset(qh, 0, sizeof(struct dwc_qh));
> +
> + /* Initialize QH */
> + switch (usb_pipetype(urb->pipe)) {
> + case PIPE_CONTROL:
> + qh->ep_type = USB_ENDPOINT_XFER_CONTROL;
> + break;
> + case PIPE_BULK:
> + qh->ep_type = USB_ENDPOINT_XFER_BULK;
> + break;
> + case PIPE_ISOCHRONOUS:
> + qh->ep_type = USB_ENDPOINT_XFER_ISOC;
> + break;
> + case PIPE_INTERRUPT:
> + qh->ep_type = USB_ENDPOINT_XFER_INT;
> + break;
> + }
> +
> + qh->ep_is_in = usb_pipein(urb->pipe) ? 1 : 0;
> + qh->data_toggle = DWC_OTG_HC_PID_DATA0;
> + qh->maxp = usb_maxpacket(urb->dev, urb->pipe, !(usb_pipein(urb->pipe)));
> +
> + INIT_LIST_HEAD(&qh->qtd_list);
> + INIT_LIST_HEAD(&qh->qh_list_entry);
> +
> + qh->channel = NULL;
> + qh->speed = urb->dev->speed;
> +
> + /*
> + * FS/LS Enpoint on HS Hub NOT virtual root hub
> + */
> + qh->do_split = 0;
> + if (is_fs_ls(urb->dev->speed) && urb->dev->tt && urb->dev->tt->hub &&
> + urb->dev->tt->hub->devnum != 1)
> + qh->do_split = 1;
> +
> + if (qh->ep_type == USB_ENDPOINT_XFER_INT ||
> + qh->ep_type == USB_ENDPOINT_XFER_ISOC) {
> + /* Compute scheduling parameters once and save them. */
> + u32 hprt;
> + int bytecount = dwc_hb_mult(qh->maxp) *
> + dwc_max_packet(qh->maxp);
> +
> + qh->usecs = NS_TO_US(usb_calc_bus_time(urb->dev->speed,
> + usb_pipein(urb->pipe),
> + (qh->ep_type ==
> + USB_ENDPOINT_XFER_ISOC),
> + bytecount));
> +
> + /* Start in a slightly future (micro)frame. */
> + qh->sched_frame = dwc_frame_num_inc(hcd->frame_number,
> + SCHEDULE_SLOP);
> + qh->interval = urb->interval;
> +
> + hprt = dwc_reg_read(hcd->core_if->host_if->hprt0, 0);
> + if (DWC_HPRT0_PRT_SPD_RD(hprt) == DWC_HPRT0_PRTSPD_HIGH_SPEED &&
> + is_fs_ls(urb->dev->speed)) {
> + qh->interval *= 8;
> + qh->sched_frame |= 0x7;
> + qh->start_split_frame = qh->sched_frame;
> + }
> + }
> +}
> +
> +/**
> + * This function allocates and initializes a QH.
> + */
> +static struct dwc_qh *dwc_otg_hcd_qh_create(struct dwc_hcd *hcd,
> + struct urb *urb)
> +{
> + struct dwc_qh *qh;
> +
> + /* Allocate memory */
> + qh = dwc_otg_hcd_qh_alloc();
> + if (qh == NULL)
> + return NULL;
> +
> + dwc_otg_hcd_qh_init(hcd, qh, urb);
> + return qh;
> +}
> +
> +/**
> + * Free each QTD in the QH's QTD-list then free the QH. QH should already be
> + * removed from a list. QTD list should already be empty if called from URB
> + * Dequeue.
> + */
> +void dwc_otg_hcd_qh_free(struct dwc_qh *qh)
> +{
> + struct dwc_qtd *qtd;
> + struct list_head *pos, *temp;
> +
> + /* Free each QTD in the QTD list */
> + list_for_each_safe(pos, temp, &qh->qtd_list) {
> + list_del(pos);
> + qtd = dwc_list_to_qtd(pos);
> + dwc_otg_hcd_qtd_free(qtd);
> + }
> + kfree(qh);
> +}
> +
> +/**
> + * Microframe scheduler
> + * track the total use in hcd->frame_usecs
> + * keep each qh use in qh->frame_usecs
> + * when surrendering the qh then donate the time back
> + */
> +static const u16 max_uframe_usecs[] = { 100, 100, 100, 100, 100, 100, 30, 0 };
> +
> +/*
> + * called from dwc_otg_hcd.c:dwc_otg_hcd_init
> + */
> +int init_hcd_usecs(struct dwc_hcd *hcd)
> +{
> + int i;
> +
> + for (i = 0; i < 8; i++)
> + hcd->frame_usecs[i] = max_uframe_usecs[i];
> +
> + return 0;
> +}
> +
> +static int find_single_uframe(struct dwc_hcd *hcd, struct dwc_qh *qh)
> +{
> + int i;
> + u16 utime;
> + int t_left;
> + int ret;
> + int done;
> +
> + ret = -1;
> + utime = qh->usecs;
> + t_left = utime;
> + i = 0;
> + done = 0;
> + while (done == 0) {
> + /* At the start hcd->frame_usecs[i] = max_uframe_usecs[i]; */
> + if (utime <= hcd->frame_usecs[i]) {
> + hcd->frame_usecs[i] -= utime;
> + qh->frame_usecs[i] += utime;
> + t_left -= utime;
> + ret = i;
> + done = 1;
> + return ret;
> + } else {
> + i++;
> + if (i == 8) {
> + done = 1;
> + ret = -1;
> + }
> + }
> + }
> + return ret;
> +}
I suggest a for loop for this function, makes it a lot easier to read IMHO.
static int find_single_uframe(struct dwc_hcd *hcd, struct dwc_qh *qh)
{
int i, utime = qh->usecs;
for(i = 0; i < 8; i++) {
if (utime <= hcd->frame_usecs[i]) {
hcd->frame_usecs[i] -= utime;
qh->frame_usecs[i] += utime;
return i;
}
}
return -1;
}
> +
> +/*
> + * use this for FS apps that can span multiple uframes
> + */
> +static int find_multi_uframe(struct dwc_hcd *hcd, struct dwc_qh *qh)
> +{
> + int i;
> + int j;
> + u16 utime;
> + int t_left;
> + int ret;
> + int done;
> + u16 xtime;
> +
> + ret = -1;
> + utime = qh->usecs;
> + t_left = utime;
> + i = 0;
> + done = 0;
> +loop:
> + while (done == 0) {
> + if (hcd->frame_usecs[i] <= 0) {
> + i++;
> + if (i == 8) {
> + done = 1;
> + ret = -1;
> + }
> + goto loop;
This really looks messy. Maybe you can also use a for loop here to prevent
jumping back with a goto in the code.
> + }
> +
> + /*
> + * We need n consequtive slots so use j as a start slot.
> + * j plus j+1 must be enough time (for now)
> + */
> + xtime = hcd->frame_usecs[i];
> + for (j = i + 1; j < 8; j++) {
> + /*
> + * if we add this frame remaining time to xtime we may
> + * be OK, if not we need to test j for a complete frame.
> + */
> + if ((xtime + hcd->frame_usecs[j]) < utime) {
> + if (hcd->frame_usecs[j] < max_uframe_usecs[j]) {
> + j = 8;
> + ret = -1;
> + continue;
> + }
You can use break to break out of the inner for loop here.
> + }
> + if (xtime >= utime) {
> + ret = i;
> + j = 8; /* stop loop with a good value ret */
> + continue;
Same here.
> + }
> + /* add the frame time to x time */
> + xtime += hcd->frame_usecs[j];
> + /* we must have a fully available next frame or break */
> + if ((xtime < utime) &&
> + (hcd->frame_usecs[j] == max_uframe_usecs[j])) {
> + ret = -1;
> + j = 8; /* stop loop with a bad value ret */
> + continue;
And here.
> + }
> + }
Maybe you can export this loop to an inlined function to increase code
readability.
> + if (ret >= 0) {
> + t_left = utime;
> + for (j = i; (t_left > 0) && (j < 8); j++) {
> + t_left -= hcd->frame_usecs[j];
> + if (t_left <= 0) {
> + qh->frame_usecs[j] +=
> + hcd->frame_usecs[j] + t_left;
> + hcd->frame_usecs[j] = -t_left;
> + ret = i;
> + done = 1;
> + } else {
> + qh->frame_usecs[j] +=
> + hcd->frame_usecs[j];
> + hcd->frame_usecs[j] = 0;
> + }
> + }
> + } else {
> + i++;
> + if (i == 8) {
> + done = 1;
> + ret = -1;
> + }
> + }
> + }
> + return ret;
> +}
> +
> +static int find_uframe(struct dwc_hcd *hcd, struct dwc_qh *qh)
> +{
> + int ret = -1;
> +
> + if (qh->speed == USB_SPEED_HIGH)
> + /* if this is a hs transaction we need a full frame */
> + ret = find_single_uframe(hcd, qh);
> + else
> + /* FS transaction may need a sequence of frames */
> + ret = find_multi_uframe(hcd, qh);
> +
> + return ret;
> +}
> +
> +/**
> + * Checks that the max transfer size allowed in a host channel is large enough
> + * to handle the maximum data transfer in a single (micro)frame for a periodic
> + * transfer.
> + */
> +static int check_max_xfer_size(struct dwc_hcd *hcd, struct dwc_qh *qh)
> +{
> + int status = 0;
> + u32 max_xfer_size;
> + u32 max_channel_xfer_size;
> +
> + max_xfer_size = dwc_max_packet(qh->maxp) * dwc_hb_mult(qh->maxp);
> + max_channel_xfer_size = hcd->core_if->core_params->max_transfer_size;
> +
> + if (max_xfer_size > max_channel_xfer_size) {
> + pr_notice("%s: Periodic xfer length %d > max xfer "
> + "length for channel %d\n", __func__, max_xfer_size,
> + max_channel_xfer_size);
> + status = -ENOSPC;
> + }
> +
> + return status;
> +}
> +
> +/**
> + * Schedules an interrupt or isochronous transfer in the periodic schedule.
> + */
> +static int schedule_periodic(struct dwc_hcd *hcd, struct dwc_qh *qh)
> +{
> + int status;
> + struct usb_bus *bus = hcd_to_bus(dwc_otg_hcd_to_hcd(hcd));
> + int frame;
> +
> + status = find_uframe(hcd, qh);
> + frame = -1;
> + if (status == 0) {
> + frame = 7;
> + } else {
> + if (status > 0)
> + frame = status - 1;
> + }
> + /* Set the new frame up */
> + if (frame > -1) {
> + qh->sched_frame &= ~0x7;
> + qh->sched_frame |= (frame & 7);
> + }
> + if (status != -1)
> + status = 0;
> + if (status) {
> + pr_notice("%s: Insufficient periodic bandwidth for "
> + "periodic transfer.\n", __func__);
> + return status;
> + }
> + status = check_max_xfer_size(hcd, qh);
> + if (status) {
> + pr_notice("%s: Channel max transfer size too small "
> + "for periodic transfer.\n", __func__);
> + return status;
> + }
> + /* Always start in the inactive schedule. */
> + list_add_tail(&qh->qh_list_entry, &hcd->periodic_sched_inactive);
> +
> + /* Update claimed usecs per (micro)frame. */
> + hcd->periodic_usecs += qh->usecs;
> +
> + /*
> + * Update average periodic bandwidth claimed and # periodic reqs for
> + * usbfs.
> + */
> + bus->bandwidth_allocated += qh->usecs / qh->interval;
> +
> + if (qh->ep_type == USB_ENDPOINT_XFER_INT)
> + bus->bandwidth_int_reqs++;
> + else
> + bus->bandwidth_isoc_reqs++;
> +
> + return status;
> +}
> +
> +/**
> + * This function adds a QH to either the non periodic or periodic schedule if
> + * it is not already in the schedule. If the QH is already in the schedule, no
> + * action is taken.
> + */
> +static int dwc_otg_hcd_qh_add(struct dwc_hcd *hcd, struct dwc_qh *qh)
> +{
> + int status = 0;
> +
> + /* QH may already be in a schedule. */
> + if (!list_empty(&qh->qh_list_entry))
> + goto done;
You can return here directly and remove the done label.
> + /*
> + * Add the new QH to the appropriate schedule. For non-periodic, always
> + * start in the inactive schedule.
> + */
> + if (dwc_qh_is_non_per(qh))
> + list_add_tail(&qh->qh_list_entry,
> + &hcd->non_periodic_sched_inactive);
> + else
> + status = schedule_periodic(hcd, qh);
> +
> +done:
> + return status;
> +}
> +
> +/**
> + * This function adds a QH to the non periodic deferred schedule.
> + *
> + * @return 0 if successful, negative error code otherwise.
> + */
> +static int dwc_otg_hcd_qh_add_deferred(struct dwc_hcd *hcd, struct dwc_qh *qh)
> +{
> + if (!list_empty(&qh->qh_list_entry))
> + /* QH already in a schedule. */
> + goto done;
You can return here directly and remove the done label.
> +
> + /* Add the new QH to the non periodic deferred schedule */
> + if (dwc_qh_is_non_per(qh))
> + list_add_tail(&qh->qh_list_entry,
> + &hcd->non_periodic_sched_deferred);
> +done:
> + return 0;
> +}
> +
> +/**
> + * Removes an interrupt or isochronous transfer from the periodic schedule.
> + */
> +static void deschedule_periodic(struct dwc_hcd *hcd, struct dwc_qh *qh)
> +{
> + struct usb_bus *bus = hcd_to_bus(dwc_otg_hcd_to_hcd(hcd));
> + int i;
> +
> + list_del_init(&qh->qh_list_entry);
> + /* Update claimed usecs per (micro)frame. */
> + hcd->periodic_usecs -= qh->usecs;
> + for (i = 0; i < 8; i++) {
> + hcd->frame_usecs[i] += qh->frame_usecs[i];
> + qh->frame_usecs[i] = 0;
> + }
> + /*
> + * Update average periodic bandwidth claimed and # periodic reqs for
> + * usbfs.
> + */
> + bus->bandwidth_allocated -= qh->usecs / qh->interval;
> +
> + if (qh->ep_type == USB_ENDPOINT_XFER_INT)
> + bus->bandwidth_int_reqs--;
> + else
> + bus->bandwidth_isoc_reqs--;
> +}
> +
> +/**
> + * Removes a QH from either the non-periodic or periodic schedule. Memory is
> + * not freed.
> + */
> +void dwc_otg_hcd_qh_remove(struct dwc_hcd *hcd, struct dwc_qh *qh)
> +{
> + /* Do nothing if QH is not in a schedule */
> + if (list_empty(&qh->qh_list_entry))
> + return;
> +
> + if (dwc_qh_is_non_per(qh)) {
> + if (hcd->non_periodic_qh_ptr == &qh->qh_list_entry)
> + hcd->non_periodic_qh_ptr =
> + hcd->non_periodic_qh_ptr->next;
> + list_del_init(&qh->qh_list_entry);
> + } else {
> + deschedule_periodic(hcd, qh);
> + }
> +}
> +
> +/**
> + * Defers a QH. For non-periodic QHs, removes the QH from the active
> + * non-periodic schedule. The QH is added to the deferred non-periodic
> + * schedule if any QTDs are still attached to the QH.
> + */
> +int dwc_otg_hcd_qh_deferr(struct dwc_hcd *hcd, struct dwc_qh *qh, int delay)
> +{
> + int deact = 1;
> +
> + if (dwc_qh_is_non_per(qh)) {
> + qh->sched_frame = dwc_frame_num_inc(hcd->frame_number, delay);
> + qh->channel = NULL;
> + qh->qtd_in_process = NULL;
> + deact = 0;
> + dwc_otg_hcd_qh_remove(hcd, qh);
> + if (!list_empty(&qh->qtd_list))
> + /* Add back to deferred non-periodic schedule. */
> + dwc_otg_hcd_qh_add_deferred(hcd, qh);
> + }
> + return deact;
> +}
> +
> +/**
> + * Schedule the next continuing periodic split transfer
> + */
> +static void sched_next_per_split_xfr(struct dwc_qh *qh, u16 fr_num,
> + int sched_split)
> +{
> + if (sched_split) {
> + qh->sched_frame = fr_num;
> + if (dwc_frame_num_le(fr_num,
> + dwc_frame_num_inc(qh->start_split_frame,
> + 1))) {
> + /*
> + * Allow one frame to elapse after start split
> + * microframe before scheduling complete split, but DONT
> + * if we are doing the next start split in the
> + * same frame for an ISOC out.
> + */
> + if (qh->ep_type != USB_ENDPOINT_XFER_ISOC ||
> + qh->ep_is_in)
> + qh->sched_frame =
> + dwc_frame_num_inc(qh->sched_frame, 1);
> + }
> + } else {
> + qh->sched_frame = dwc_frame_num_inc(qh->start_split_frame,
> + qh->interval);
> +
> + if (dwc_frame_num_le(qh->sched_frame, fr_num))
> + qh->sched_frame = fr_num;
> + qh->sched_frame |= 0x7;
> + qh->start_split_frame = qh->sched_frame;
> + }
> +}
> +
> +/**
> + * Deactivates a periodic QH. The QH is removed from the periodic queued
> + * schedule. If there are any QTDs still attached to the QH, the QH is added to
> + * either the periodic inactive schedule or the periodic ready schedule and its
> + * next scheduled frame is calculated. The QH is placed in the ready schedule if
> + * the scheduled frame has been reached already. Otherwise it's placed in the
> + * inactive schedule. If there are no QTDs attached to the QH, the QH is
> + * completely removed from the periodic schedule.
> + */
> +static void deactivate_periodic_qh(struct dwc_hcd *hcd, struct dwc_qh *qh,
> + int sched_next_split)
> +{
> + /* unsigned long flags; */
> + u16 fr_num = dwc_otg_hcd_get_frame_number(dwc_otg_hcd_to_hcd(hcd));
> +
> + if (qh->do_split) {
> + sched_next_per_split_xfr(qh, fr_num, sched_next_split);
> + } else {
> + qh->sched_frame = dwc_frame_num_inc(qh->sched_frame,
> + qh->interval);
> + if (dwc_frame_num_le(qh->sched_frame, fr_num))
> + qh->sched_frame = fr_num;
> + }
> +
> + if (list_empty(&qh->qtd_list)) {
> + dwc_otg_hcd_qh_remove(hcd, qh);
> + } else {
> + /*
> + * Remove from periodic_sched_queued and move to appropriate
> + * queue.
> + */
> + if (qh->sched_frame == fr_num)
> + list_move(&qh->qh_list_entry,
> + &hcd->periodic_sched_ready);
> + else
> + list_move(&qh->qh_list_entry,
> + &hcd->periodic_sched_inactive);
> + }
> +}
> +
> +/**
> + * Deactivates a non-periodic QH. Removes the QH from the active non-periodic
> + * schedule. The QH is added to the inactive non-periodic schedule if any QTDs
> + * are still attached to the QH.
> + */
> +static void deactivate_non_periodic_qh(struct dwc_hcd *hcd, struct dwc_qh *qh)
> +{
> + dwc_otg_hcd_qh_remove(hcd, qh);
> + if (!list_empty(&qh->qtd_list))
> + dwc_otg_hcd_qh_add(hcd, qh);
> +}
> +
> +/**
> + * Deactivates a QH. Determines if the QH is periodic or non-periodic and takes
> + * the appropriate action.
> + */
> +void dwc_otg_hcd_qh_deactivate(struct dwc_hcd *hcd, struct dwc_qh *qh,
> + int sched_next_periodic_split)
> +{
> + if (dwc_qh_is_non_per(qh))
> + deactivate_non_periodic_qh(hcd, qh);
> + else
> + deactivate_periodic_qh(hcd, qh, sched_next_periodic_split);
> +}
> +
> +/**
> + * Initializes a QTD structure.
> + */
> +static void dwc_otg_hcd_qtd_init(struct dwc_qtd *qtd, struct urb *urb)
> +{
> + memset(qtd, 0, sizeof(struct dwc_qtd));
> + qtd->urb = urb;
> +
> + if (usb_pipecontrol(urb->pipe)) {
> + /*
> + * The only time the QTD data toggle is used is on the data
> + * phase of control transfers. This phase always starts with
> + * DATA1.
> + */
> + qtd->data_toggle = DWC_OTG_HC_PID_DATA1;
> + qtd->control_phase = DWC_OTG_CONTROL_SETUP;
> + }
> +
> + /* start split */
> + qtd->complete_split = 0;
> + qtd->isoc_split_pos = DWC_HCSPLIT_XACTPOS_ALL;
> + qtd->isoc_split_offset = 0;
> +
> + /* Store the qtd ptr in the urb to reference what QTD. */
> + urb->hcpriv = qtd;
> +
> + INIT_LIST_HEAD(&qtd->qtd_list_entry);
> + return;
> +}
> +
> +/* Allocates memory for a QTD structure. */
> +static inline struct dwc_qtd *dwc_otg_hcd_qtd_alloc(gfp_t _mem_flags)
> +{
> + return kmalloc(sizeof(struct dwc_qtd), _mem_flags);
> +}
Again, only used once.
> +
> +/**
> + * This function allocates and initializes a QTD.
> + */
> +struct dwc_qtd *dwc_otg_hcd_qtd_create(struct urb *urb, gfp_t _mem_flags)
> +{
> + struct dwc_qtd *qtd = dwc_otg_hcd_qtd_alloc(_mem_flags);
> +
> + if (!qtd)
> + return NULL;
> +
> + dwc_otg_hcd_qtd_init(qtd, urb);
> + return qtd;
> +}
> +
> +/**
> + * This function adds a QTD to the QTD-list of a QH. It will find the correct
> + * QH to place the QTD into. If it does not find a QH, then it will create a
> + * new QH. If the QH to which the QTD is added is not currently scheduled, it
> + * is placed into the proper schedule based on its EP type.
> + *
> + */
> +int dwc_otg_hcd_qtd_add(struct dwc_qtd *qtd, struct dwc_hcd *hcd)
> +{
> + struct usb_host_endpoint *ep;
> + struct dwc_qh *qh;
> + int retval = 0;
> + struct urb *urb = qtd->urb;
> +
> + /*
> + * Get the QH which holds the QTD-list to insert to. Create QH if it
> + * doesn't exist.
> + */
> + ep = dwc_urb_to_endpoint(urb);
> +
> + qh = (struct dwc_qh *)ep->hcpriv;
> + if (!qh) {
> + qh = dwc_otg_hcd_qh_create(hcd, urb);
> + if (!qh) {
> + retval = -1;
> + goto done;
You can return directly and then don't need the goto statement and the
done label.
Greetings,
Philipp
^ permalink raw reply
* Re: [RFC][PATCH 2/2] powerpc/85xx: Introduce a P1020 SoC device tree
From: Tabi Timur-B04825 @ 2011-10-20 16:28 UTC (permalink / raw)
To: Kumar Gala; +Cc: linuxppc-dev@ozlabs.org, devicetree-discuss@lists.ozlabs.org
In-Reply-To: <1319095467-6229-2-git-send-email-galak@kernel.crashing.org>
On Thu, Oct 20, 2011 at 2:24 AM, Kumar Gala <galak@kernel.crashing.org> wro=
te:
>
> =A0arch/powerpc/boot/dts/p1020soc.dtsi | =A0262 +++++++++++++++++++++++++=
++++++++++
All of the other dtsi files are of the format "___si.dtsi". Why does
this one use "___soc.dtsi"?
--=20
Timur Tabi
Linux kernel developer at Freescale=
^ permalink raw reply
* Re: [RFC][PATCH 1/2] powerpc/85xx: create dts components to build up an SoC
From: Tabi Timur-B04825 @ 2011-10-20 16:42 UTC (permalink / raw)
To: Kumar Gala; +Cc: linuxppc-dev@ozlabs.org, devicetree-discuss@lists.ozlabs.org
In-Reply-To: <1319095467-6229-1-git-send-email-galak@kernel.crashing.org>
Ok, your other patch makes more sense now.
On Thu, Oct 20, 2011 at 2:24 AM, Kumar Gala <galak@kernel.crashing.org> wro=
te:
> +++ b/arch/powerpc/boot/dts/fsl/pq3-duart-0.dtsi
> @@ -0,0 +1,8 @@
> +&serial0 {
> + =A0 =A0 =A0 cell-index =3D <0>;
> + =A0 =A0 =A0 device_type =3D "serial";
> + =A0 =A0 =A0 compatible =3D "ns16550";
> + =A0 =A0 =A0 reg =3D <0x4500 0x100>;
> + =A0 =A0 =A0 clock-frequency =3D <0>;
> + =A0 =A0 =A0 interrupts =3D <42 2 0 0>;
> +};
How about we put all serial devices into one pq3-duart.dtsi file, and
let the parent dtsi file reference just the ones that it needs?
--=20
Timur Tabi
Linux kernel developer at Freescale=
^ permalink raw reply
* Re: [RFC][PATCH 1/2] powerpc/85xx: create dts components to build up an SoC
From: Kumar Gala @ 2011-10-20 20:44 UTC (permalink / raw)
To: Tabi Timur-B04825
Cc: linuxppc-dev@ozlabs.org, devicetree-discuss@lists.ozlabs.org
In-Reply-To: <CAOZdJXWtnBB5=DahG8LGQpeP7or0VZv78duhtPiORk3WOir8gQ@mail.gmail.com>
On Oct 20, 2011, at 11:42 AM, Tabi Timur-B04825 wrote:
> Ok, your other patch makes more sense now.
>=20
> On Thu, Oct 20, 2011 at 2:24 AM, Kumar Gala =
<galak@kernel.crashing.org> wrote:
>=20
>> +++ b/arch/powerpc/boot/dts/fsl/pq3-duart-0.dtsi
>> @@ -0,0 +1,8 @@
>> +&serial0 {
>> + cell-index =3D <0>;
>> + device_type =3D "serial";
>> + compatible =3D "ns16550";
>> + reg =3D <0x4500 0x100>;
>> + clock-frequency =3D <0>;
>> + interrupts =3D <42 2 0 0>;
>> +};
>=20
> How about we put all serial devices into one pq3-duart.dtsi file, and
> let the parent dtsi file reference just the ones that it needs?
there isn't an option to do that w/dtc
- k=
^ permalink raw reply
* Re: [RFC][PATCH 2/2] powerpc/85xx: Introduce a P1020 SoC device tree
From: Kumar Gala @ 2011-10-20 20:56 UTC (permalink / raw)
To: Tabi Timur-B04825
Cc: linuxppc-dev@ozlabs.org, devicetree-discuss@lists.ozlabs.org
In-Reply-To: <CAOZdJXU4uHSTRD0vf2jS9gOzjVyO4BLn9=kEmLwwb72LhkTz=w@mail.gmail.com>
On Oct 20, 2011, at 11:28 AM, Tabi Timur-B04825 wrote:
> On Thu, Oct 20, 2011 at 2:24 AM, Kumar Gala =
<galak@kernel.crashing.org> wrote:
>>=20
>> arch/powerpc/boot/dts/p1020soc.dtsi | 262 =
+++++++++++++++++++++++++++++++++++
>=20
> All of the other dtsi files are of the format "___si.dtsi". Why does
> this one use "___soc.dtsi"?
I intend to also have a ___si.dtsi, still playing around with this so we =
have less overall duplication to support both 32 & 36-bit address maps =
as an example.
- k=
^ permalink raw reply
* Re: [RFC][PATCH 1/2] powerpc/85xx: create dts components to build up an SoC
From: Timur Tabi @ 2011-10-20 21:05 UTC (permalink / raw)
To: Kumar Gala; +Cc: linuxppc-dev@ozlabs.org, devicetree-discuss@lists.ozlabs.org
In-Reply-To: <A54F7698-A96F-4EE1-89DB-B6A7C5A071FB@kernel.crashing.org>
Kumar Gala wrote:
>> > How about we put all serial devices into one pq3-duart.dtsi file, and
>> > let the parent dtsi file reference just the ones that it needs?
> there isn't an option to do that w/dtc
What about making all the nodes disabled by default, and then the soc.dtsi
or si.dtsi file can re-enable them?
--
Timur Tabi
Linux kernel developer at Freescale
^ permalink raw reply
* [RFC PATCH 13/17] mpc836x: Move board-specific bcm5481 fixup out of the PHY driver
From: Kyle Moffett @ 2011-10-20 21:00 UTC (permalink / raw)
To: linux-kernel, netdev; +Cc: linuxppc-dev, Paul Mackerras, Kyle Moffett
In-Reply-To: <1319144425-15547-1-git-send-email-Kyle.D.Moffett@boeing.com>
By comparing the BCM5481 registers modified in the ->config_aneg()
method with the datasheet I have for the BCM5482, it appears that the
register writes are adjusting signal timing to work around a known
trace-length issue on the PCB.
Such hardware workarounds don't belong in the generic driver, and should
instead be placed in a board-specific PHY fixup.
NOTE: Needs testing by somebody with the hardware.
Signed-off-by: Kyle Moffett <Kyle.D.Moffett@boeing.com>
---
arch/powerpc/platforms/83xx/mpc836x_rdk.c | 35 ++++++++++++++++++++++++++++
drivers/net/phy/broadcom.c | 36 -----------------------------
2 files changed, 35 insertions(+), 36 deletions(-)
diff --git a/arch/powerpc/platforms/83xx/mpc836x_rdk.c b/arch/powerpc/platforms/83xx/mpc836x_rdk.c
index b0090aa..b7cc607 100644
--- a/arch/powerpc/platforms/83xx/mpc836x_rdk.c
+++ b/arch/powerpc/platforms/83xx/mpc836x_rdk.c
@@ -14,6 +14,7 @@
#include <linux/kernel.h>
#include <linux/pci.h>
+#include <linux/phy.h>
#include <linux/of_platform.h>
#include <linux/io.h>
#include <asm/prom.h>
@@ -38,6 +39,36 @@ static int __init mpc836x_rdk_declare_of_platform_devices(void)
}
machine_device_initcall(mpc836x_rdk, mpc836x_rdk_declare_of_platform_devices);
+static int mpc836x_rdk_bcm5481_fixup(struct phy_device *phydev)
+{
+ int reg;
+
+ if (phydev->interface != PHY_INTERFACE_MODE_RGMII_RXID)
+ return;
+
+ /*
+ * There is no BCM5481 specification available, so down
+ * here is everything we know about "register 0x18". This
+ * at least helps BCM5481 to successfuly receive packets
+ * on MPC8360E-RDK board. Peter Barada <peterb@logicpd.com>
+ * says: "This sets delay between the RXD and RXC signals
+ * instead of using trace lengths to achieve timing".
+ */
+
+ /* Set RDX clk delay. */
+ reg = 0x7 | (0x7 << 12);
+ phy_write(phydev, 0x18, reg);
+ reg = phy_read(phydev, 0x18);
+ if (reg < 0)
+ return reg;
+
+ /* Set RDX-RXC skew. */
+ reg |= (1 << 8);
+ /* Write bits 14:0. */
+ reg |= (1 << 15);
+ return phy_write(phydev, 0x18, reg);
+}
+
static void __init mpc836x_rdk_setup_arch(void)
{
#ifdef CONFIG_PCI
@@ -54,6 +85,10 @@ static void __init mpc836x_rdk_setup_arch(void)
#ifdef CONFIG_QUICC_ENGINE
qe_reset();
#endif
+#ifdef CONFIG_BROADCOM_PHY
+ phy_register_fixup_for_uid(0x0143bca0, 0xfffffff0,
+ &mpc836x_rdk_bcm5481_phy_fixup);
+#endif
}
static void __init mpc836x_rdk_init_IRQ(void)
diff --git a/drivers/net/phy/broadcom.c b/drivers/net/phy/broadcom.c
index 1b83f75..8c03ebc 100644
--- a/drivers/net/phy/broadcom.c
+++ b/drivers/net/phy/broadcom.c
@@ -539,41 +539,6 @@ static int bcm54xx_config_intr(struct phy_device *phydev)
return err;
}
-static int bcm5481_config_aneg(struct phy_device *phydev)
-{
- int ret;
-
- /* Aneg firsly. */
- ret = genphy_config_aneg(phydev);
-
- /* Then we can set up the delay. */
- if (phydev->interface == PHY_INTERFACE_MODE_RGMII_RXID) {
- u16 reg;
-
- /*
- * There is no BCM5481 specification available, so down
- * here is everything we know about "register 0x18". This
- * at least helps BCM5481 to successfuly receive packets
- * on MPC8360E-RDK board. Peter Barada <peterb@logicpd.com>
- * says: "This sets delay between the RXD and RXC signals
- * instead of using trace lengths to achieve timing".
- */
-
- /* Set RDX clk delay. */
- reg = 0x7 | (0x7 << 12);
- phy_write(phydev, 0x18, reg);
-
- reg = phy_read(phydev, 0x18);
- /* Set RDX-RXC skew. */
- reg |= (1 << 8);
- /* Write bits 14:0. */
- reg |= (1 << 15);
- phy_write(phydev, 0x18, reg);
- }
-
- return ret;
-}
-
static int brcm_phy_setbits(struct phy_device *phydev, int reg, int set)
{
int val;
@@ -736,7 +701,6 @@ static struct phy_driver broadcom_drivers[] = { {
SUPPORTED_Pause | SUPPORTED_Asym_Pause,
.flags = PHY_HAS_MAGICANEG | PHY_HAS_INTERRUPT,
.config_init = bcm54xx_config_init,
- .config_aneg = bcm5481_config_aneg,
.ack_interrupt = bcm54xx_ack_interrupt,
.config_intr = bcm54xx_config_intr,
.driver = { .owner = THIS_MODULE },
--
1.7.2.5
^ permalink raw reply related
* [RFC PATCH 17/17] phy_device: Rename phy_start_aneg() to phy_start_link()
From: Kyle Moffett @ 2011-10-20 21:00 UTC (permalink / raw)
To: linux-kernel, netdev
Cc: linux-doc, Randy Dunlap, Stephen Hemminger, David Decotigny,
devel, Matt Carlson, Kyle Moffett, Kuninori Morimoto,
Alexey Dobriyan, Lennert Buytenhek, Mike Frysinger,
Michał Mirosław, Michael Chan, Giuseppe Cavallaro,
Nobuhiro Iwamatsu, John Crispin, Kristoffer Glembo, Ilya Yanok,
Yoshihiro Shimoda, Greg Kroah-Hartman, Ralf Baechle,
Christian Dietrich, Ralph Hempel, Jon Mason, Joe Perches,
Steve Glendinning, Andrew Morton, Richard Cochran, linuxppc-dev,
David S. Miller, Krzysztof Halasa
In-Reply-To: <1319144425-15547-1-git-send-email-Kyle.D.Moffett@boeing.com>
The name of the "phy_start_aneg()" function is very confusing, because
it also handles forced-mode (AUTONEG_DISABLE) links.
Rename the function to phy_start_link() and fix up all users.
Signed-off-by: Kyle Moffett <Kyle.D.Moffett@boeing.com>
---
Documentation/networking/phy.txt | 2 +-
drivers/net/arm/ixp4xx_eth.c | 2 +-
drivers/net/dnet.c | 2 +-
drivers/net/greth.c | 2 +-
drivers/net/lantiq_etop.c | 2 +-
drivers/net/mv643xx_eth.c | 2 +-
drivers/net/octeon/octeon_mgmt.c | 2 +-
drivers/net/phy/phy.c | 12 ++++++------
drivers/net/pxa168_eth.c | 2 +-
drivers/net/sh_eth.c | 2 +-
drivers/net/smsc911x.c | 2 +-
drivers/net/smsc9420.c | 2 +-
drivers/net/stmmac/stmmac_ethtool.c | 2 +-
drivers/net/tg3.c | 8 ++++----
drivers/net/ucc_geth_ethtool.c | 2 +-
drivers/staging/octeon/ethernet-mdio.c | 4 ++--
include/linux/phy.h | 2 +-
net/dsa/slave.c | 2 +-
18 files changed, 27 insertions(+), 27 deletions(-)
diff --git a/Documentation/networking/phy.txt b/Documentation/networking/phy.txt
index 0db8c81..6f862e5 100644
--- a/Documentation/networking/phy.txt
+++ b/Documentation/networking/phy.txt
@@ -190,7 +190,7 @@ Doing it all yourself
driver if none was found during bus initialization. Passes in
any phy-specific flags as needed.
- int phy_start_aneg(struct phy_device *phydev);
+ int phy_start_link(struct phy_device *phydev);
Using variables inside the phydev structure, either configures advertising
and resets autonegotiation, or disables autonegotiation, and configures
diff --git a/drivers/net/arm/ixp4xx_eth.c b/drivers/net/arm/ixp4xx_eth.c
index de51e84..8864be5 100644
--- a/drivers/net/arm/ixp4xx_eth.c
+++ b/drivers/net/arm/ixp4xx_eth.c
@@ -998,7 +998,7 @@ static int ixp4xx_set_settings(struct net_device *dev, struct ethtool_cmd *cmd)
static int ixp4xx_nway_reset(struct net_device *dev)
{
struct port *port = netdev_priv(dev);
- return phy_start_aneg(port->phydev);
+ return phy_start_link(port->phydev);
}
static const struct ethtool_ops ixp4xx_ethtool_ops = {
diff --git a/drivers/net/dnet.c b/drivers/net/dnet.c
index c1063d1..edb4635 100644
--- a/drivers/net/dnet.c
+++ b/drivers/net/dnet.c
@@ -669,7 +669,7 @@ static int dnet_open(struct net_device *dev)
napi_enable(&bp->napi);
dnet_init_hw(bp);
- phy_start_aneg(bp->phy_dev);
+ phy_start_link(bp->phy_dev);
/* schedule a link state check */
phy_start(bp->phy_dev);
diff --git a/drivers/net/greth.c b/drivers/net/greth.c
index e7f268f..0ad3f14 100644
--- a/drivers/net/greth.c
+++ b/drivers/net/greth.c
@@ -1363,7 +1363,7 @@ static int greth_mdio_init(struct greth_private *greth)
/* If Ethernet debug link is used make autoneg happen right away */
if (greth->edcl && greth_edcl == 1) {
- phy_start_aneg(greth->phy);
+ phy_start_link(greth->phy);
timeout = jiffies + 6*HZ;
while (!phy_aneg_done(greth->phy) && time_before(jiffies, timeout)) {
}
diff --git a/drivers/net/lantiq_etop.c b/drivers/net/lantiq_etop.c
index 45f252b..c8795aa 100644
--- a/drivers/net/lantiq_etop.c
+++ b/drivers/net/lantiq_etop.c
@@ -326,7 +326,7 @@ ltq_etop_nway_reset(struct net_device *dev)
{
struct ltq_etop_priv *priv = netdev_priv(dev);
- return phy_start_aneg(priv->phydev);
+ return phy_start_link(priv->phydev);
}
static const struct ethtool_ops ltq_etop_ethtool_ops = {
diff --git a/drivers/net/mv643xx_eth.c b/drivers/net/mv643xx_eth.c
index d3e223c..0ce514c 100644
--- a/drivers/net/mv643xx_eth.c
+++ b/drivers/net/mv643xx_eth.c
@@ -2775,7 +2775,7 @@ static void phy_init(struct mv643xx_eth_private *mp, int speed, int duplex)
phy->speed = speed;
phy->duplex = duplex;
}
- phy_start_aneg(phy);
+ phy_start_link(phy);
}
static void init_pscr(struct mv643xx_eth_private *mp, int speed, int duplex)
diff --git a/drivers/net/octeon/octeon_mgmt.c b/drivers/net/octeon/octeon_mgmt.c
index 429e08c..fcdf28a 100644
--- a/drivers/net/octeon/octeon_mgmt.c
+++ b/drivers/net/octeon/octeon_mgmt.c
@@ -686,7 +686,7 @@ static int octeon_mgmt_init_phy(struct net_device *netdev)
return -1;
}
- phy_start_aneg(p->phydev);
+ phy_start_link(p->phydev);
return 0;
}
diff --git a/drivers/net/phy/phy.c b/drivers/net/phy/phy.c
index 5f72055..fc486fe 100644
--- a/drivers/net/phy/phy.c
+++ b/drivers/net/phy/phy.c
@@ -232,7 +232,7 @@ static void phy_sanitize_settings(struct phy_device *phydev)
* A few notes about parameter checking:
* - We don't set port or transceiver, so we don't care what they
* were set to.
- * - phy_start_aneg() will make sure forced settings are sane, and
+ * - phy_start_link() will make sure forced settings are sane, and
* choose the next best ones from the ones selected, so we don't
* care if ethtool tries to give us bad values.
*/
@@ -276,7 +276,7 @@ int phy_ethtool_sset(struct phy_device *phydev, struct ethtool_cmd *cmd)
phydev->duplex = cmd->duplex;
/* Restart the PHY */
- phy_start_aneg(phydev);
+ phy_start_link(phydev);
return 0;
}
@@ -384,7 +384,7 @@ int phy_mii_ioctl(struct phy_device *phydev,
EXPORT_SYMBOL(phy_mii_ioctl);
/**
- * phy_start_aneg - start auto-negotiation for this PHY device
+ * phy_start_link - start auto-negotiation for this PHY device
* @phydev: the phy_device struct
*
* Description: Sanitizes the settings (if we're not autonegotiating
@@ -392,7 +392,7 @@ EXPORT_SYMBOL(phy_mii_ioctl);
* If the PHYCONTROL Layer is operating, we change the state to
* reflect the beginning of Auto-negotiation or forcing.
*/
-int phy_start_aneg(struct phy_device *phydev)
+int phy_start_link(struct phy_device *phydev)
{
int err;
@@ -423,7 +423,7 @@ out_unlock:
mutex_unlock(&phydev->lock);
return err;
}
-EXPORT_SYMBOL(phy_start_aneg);
+EXPORT_SYMBOL(phy_start_link);
static void phy_change(struct work_struct *work);
@@ -970,7 +970,7 @@ void phy_state_machine(struct work_struct *work)
mutex_unlock(&phydev->lock);
if (needs_aneg)
- err = phy_start_aneg(phydev);
+ err = phy_start_link(phydev);
if (err < 0)
phy_error(phydev);
diff --git a/drivers/net/pxa168_eth.c b/drivers/net/pxa168_eth.c
index ab3bca9..8f3d751 100644
--- a/drivers/net/pxa168_eth.c
+++ b/drivers/net/pxa168_eth.c
@@ -1390,7 +1390,7 @@ static void phy_init(struct pxa168_eth_private *pep, int speed, int duplex)
phy->speed = speed;
phy->duplex = duplex;
}
- phy_start_aneg(phy);
+ phy_start_link(phy);
}
static int ethernet_phy_setup(struct net_device *dev)
diff --git a/drivers/net/sh_eth.c b/drivers/net/sh_eth.c
index 7ef4378..0927b1b 100644
--- a/drivers/net/sh_eth.c
+++ b/drivers/net/sh_eth.c
@@ -1291,7 +1291,7 @@ static int sh_eth_nway_reset(struct net_device *ndev)
int ret;
spin_lock_irqsave(&mdp->lock, flags);
- ret = phy_start_aneg(mdp->phydev);
+ ret = phy_start_link(mdp->phydev);
spin_unlock_irqrestore(&mdp->lock, flags);
return ret;
diff --git a/drivers/net/smsc911x.c b/drivers/net/smsc911x.c
index b9016a3..0b5bb15 100644
--- a/drivers/net/smsc911x.c
+++ b/drivers/net/smsc911x.c
@@ -1712,7 +1712,7 @@ static int smsc911x_ethtool_nwayreset(struct net_device *dev)
{
struct smsc911x_data *pdata = netdev_priv(dev);
- return phy_start_aneg(pdata->phy_dev);
+ return phy_start_link(pdata->phy_dev);
}
static u32 smsc911x_ethtool_getmsglevel(struct net_device *dev)
diff --git a/drivers/net/smsc9420.c b/drivers/net/smsc9420.c
index 459726f..fb9e160 100644
--- a/drivers/net/smsc9420.c
+++ b/drivers/net/smsc9420.c
@@ -302,7 +302,7 @@ static int smsc9420_ethtool_nway_reset(struct net_device *netdev)
if (!pd->phy_dev)
return -ENODEV;
- return phy_start_aneg(pd->phy_dev);
+ return phy_start_link(pd->phy_dev);
}
static int smsc9420_ethtool_getregslen(struct net_device *dev)
diff --git a/drivers/net/stmmac/stmmac_ethtool.c b/drivers/net/stmmac/stmmac_ethtool.c
index 7ed8fb6..68e107b 100644
--- a/drivers/net/stmmac/stmmac_ethtool.c
+++ b/drivers/net/stmmac/stmmac_ethtool.c
@@ -240,7 +240,7 @@ stmmac_set_pauseparam(struct net_device *netdev,
if (phy->autoneg) {
if (netif_running(netdev))
- ret = phy_start_aneg(phy);
+ ret = phy_start_link(phy);
} else
priv->hw->mac->flow_ctrl(priv->ioaddr, phy->duplex,
priv->flow_ctrl, priv->pause);
diff --git a/drivers/net/tg3.c b/drivers/net/tg3.c
index 4a1374d..a317d11 100644
--- a/drivers/net/tg3.c
+++ b/drivers/net/tg3.c
@@ -1661,7 +1661,7 @@ static void tg3_phy_start(struct tg3 *tp)
phy_start(phydev);
- phy_start_aneg(phydev);
+ phy_start_link(phydev);
}
static void tg3_phy_stop(struct tg3 *tp)
@@ -2848,7 +2848,7 @@ static int tg3_power_down_prepare(struct tg3 *tp)
phydev->advertising = advertising;
- phy_start_aneg(phydev);
+ phy_start_link(phydev);
phyid = phydev->drv->phy_id & phydev->drv->phy_id_mask;
if (phyid != PHY_ID_BCMAC131) {
@@ -10340,7 +10340,7 @@ static int tg3_nway_reset(struct net_device *dev)
if (tg3_flag(tp, USE_PHYLIB)) {
if (!(tp->phy_flags & TG3_PHYFLG_IS_CONNECTED))
return -EAGAIN;
- r = phy_start_aneg(tp->mdio_bus->phy_map[TG3_PHY_MII_ADDR]);
+ r = phy_start_link(tp->mdio_bus->phy_map[TG3_PHY_MII_ADDR]);
} else {
u32 bmcr;
@@ -10500,7 +10500,7 @@ static int tg3_set_pauseparam(struct net_device *dev, struct ethtool_pauseparam
* tg3_adjust_link() do the final
* flow control setup.
*/
- return phy_start_aneg(phydev);
+ return phy_start_link(phydev);
}
}
diff --git a/drivers/net/ucc_geth_ethtool.c b/drivers/net/ucc_geth_ethtool.c
index a97257f..68a8743 100644
--- a/drivers/net/ucc_geth_ethtool.c
+++ b/drivers/net/ucc_geth_ethtool.c
@@ -342,7 +342,7 @@ static int uec_nway_reset(struct net_device *netdev)
{
struct ucc_geth_private *ugeth = netdev_priv(netdev);
- return phy_start_aneg(ugeth->phydev);
+ return phy_start_link(ugeth->phydev);
}
/* Report driver information */
diff --git a/drivers/staging/octeon/ethernet-mdio.c b/drivers/staging/octeon/ethernet-mdio.c
index f18e3e1..dc152dd 100644
--- a/drivers/staging/octeon/ethernet-mdio.c
+++ b/drivers/staging/octeon/ethernet-mdio.c
@@ -81,7 +81,7 @@ static int cvm_oct_nway_reset(struct net_device *dev)
return -EPERM;
if (priv->phydev)
- return phy_start_aneg(priv->phydev);
+ return phy_start_link(priv->phydev);
return -EINVAL;
}
@@ -176,7 +176,7 @@ int cvm_oct_phy_setup_device(struct net_device *dev)
return -1;
}
priv->last_link = 0;
- phy_start_aneg(priv->phydev);
+ phy_start_link(priv->phydev);
}
return 0;
}
diff --git a/include/linux/phy.h b/include/linux/phy.h
index 3713c8d..45f6ee8 100644
--- a/include/linux/phy.h
+++ b/include/linux/phy.h
@@ -511,7 +511,7 @@ void phy_disconnect(struct phy_device *phydev);
void phy_detach(struct phy_device *phydev);
void phy_start(struct phy_device *phydev);
void phy_stop(struct phy_device *phydev);
-int phy_start_aneg(struct phy_device *phydev);
+int phy_start_link(struct phy_device *phydev);
int phy_stop_interrupts(struct phy_device *phydev);
diff --git a/net/dsa/slave.c b/net/dsa/slave.c
index 0a47b6c..90ff96d 100644
--- a/net/dsa/slave.c
+++ b/net/dsa/slave.c
@@ -400,7 +400,7 @@ dsa_slave_create(struct dsa_switch *ds, struct device *parent,
p->phy->speed = 0;
p->phy->duplex = 0;
p->phy->advertising = p->phy->supported | ADVERTISED_Autoneg;
- phy_start_aneg(p->phy);
+ phy_start_link(p->phy);
}
return slave_dev;
--
1.7.2.5
^ permalink raw reply related
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