* Re: [PATCH] ASoC drivers for the Freescale MPC8610 SoC
From: Grant Likely @ 2008-01-02 16:28 UTC (permalink / raw)
To: Timur Tabi; +Cc: linuxppc-dev, alsa-devel
In-Reply-To: <477BADF5.9060003@freescale.com>
On 1/2/08, Timur Tabi <timur@freescale.com> wrote:
> That's the best plan I came up with. This is apparently fixed in ASoC
> V2. From ASoC V1's perspective, the fabric driver must be the master.
> However, it doesn't make sense to have a node in the device tree for the
> fabric driver, because there is no such "device". The fabric driver is
> an abstraction. So I need to chose some other node to probe the fabric
> driver with. I chose the SSI, since each SSI can have only one codec.
Does that mean with ASoC V2 you can instantiate it with the board
specific platform code instead? I think that is the consensus we were
leaning towards in the last discussion about this issue.
> > But that doesn't work in my environment. My generic channel is
> > "fsl,i2s". I have four different systems booting off from a shared
> > network drive. Each of these systems needs the common "fsl,i2s" driver
> > but they all four need different fabric drivers.
>
> That, I don't understand. fsl,ssi is pretty much the same thing as
> fsl,i2s, since the SSI *is* an I2S device. It's also an AC97 device,
> which is why I added the fsl,mode property.
This is one of the examples of where the compatible properties are
trying to be far to generic about what they are. How do you define
what "fsl,ssi" is? What happens when Freescale produces another
peripheral that can do ssi but isn't register level compatible?
In my opinion, it is far better to be specific in the device tree and
teach the driver about what versions it is able to bind against. In
this case, I would use "fsl,mpc8610-ssi" or maybe better yet:
"fsl,mpc8610-ssi,i2s" (MPC8610 SSI device in I2S mode).
I don't like the idea of a separate fsl,mode property to describe the
behaviour of multifunction peripherals. It makes probing more
difficult when there is a different driver for each mode.
>
> The fabric driver is specific to the board. So you should be using
> Kconfig to select the fabric driver. There is no node in the device
> tree for fabric drivers. I thought that was the consensus.
No, the desire is to go multiplatform in ppc. That means you cannot
use Kconfig to select the correct fabric driver.
> Are you saying that you want to use the same kernel on four different
> systems? If so, then you need to find a way to compile all fabric
> drivers together, and at boot time each fabric driver will decide
> whether it will do anything.
Yes! That is exactly what we want to support in arch/powerpc. Use
platform code to select the correct fabric driver.
Cheers,
g.
--
Grant Likely, B.Sc., P.Eng.
Secret Lab Technologies Ltd.
grant.likely@secretlab.ca
(403) 399-0195
^ permalink raw reply
* Re: [PATCH] ASoC drivers for the Freescale MPC8610 SoC
From: Grant Likely @ 2008-01-02 16:12 UTC (permalink / raw)
To: Timur Tabi; +Cc: linuxppc-dev, alsa-devel
In-Reply-To: <477BAB67.4080003@freescale.com>
On 1/2/08, Timur Tabi <timur@freescale.com> wrote:
> Jon Smirl wrote:
> > If that's the case the cs4270 should be in the i2c bus node (missing
> > currently) and then a link from the SSI bus would point to it.
>
> The CS4270 is a child of both the I2C bus *and* the SSI bus. It needs
> to have two nodes, one under each. Your're right in that there needs to
> be a link, but until the code is updated to ASoC V2, I think it's
> premature to add that support.
Why not be a child of the i2c bus with a phandle to the ssi bus? That
is the direction we've gone with other multi attachment devices. (ie.
Ethernet PHYs. Child of the MDIO node, phandle to link the Ethernet
controller with the PHY)
Cheers,
g.
--
Grant Likely, B.Sc., P.Eng.
Secret Lab Technologies Ltd.
grant.likely@secretlab.ca
(403) 399-0195
^ permalink raw reply
* Re: [PATCH] ASoC drivers for the Freescale MPC8610 SoC
From: Jon Smirl @ 2008-01-02 15:56 UTC (permalink / raw)
To: Timur Tabi; +Cc: linuxppc-dev, alsa-devel
In-Reply-To: <477BADF5.9060003@freescale.com>
On 1/2/08, Timur Tabi <timur@freescale.com> wrote:
> Are you saying that you want to use the same kernel on four different
> systems? If so, then you need to find a way to compile all fabric
> drivers together, and at boot time each fabric driver will decide
> whether it will do anything.
Yes, I have four different but similar systems. They only differer in
the codec chips used. I want to make a single kernel image and then
use the device tree to dynamically load the correct codec driver from
initrd. That will let me ship a single kernel image that services all
four machines. The codecs implement different sound systems from low
end to high end.
The correct solution for this is to use kernel modules and trigger
their loading based on the device tree. This is the same mechanism
used by USB and PCI.
For this model to work you need to split your driver. fsl-ssi and
mpc8610_hpcd need to be in two separate drivers. fsl-ssi is easy
enough to load since it has a device tree entry.
mpc8610_hpcd is the harder one to load since it doesn't have a device
tree entry. What you want to do it match on the compatible field of
the root node.
static struct of_device_id fabric_of_match[] = {
{
.compatible = "fsl,MPC8610HPCD",
},
{},
};
But this doesn't work since the root is the device tree isn't passed
down into the device probe code. (Could this be fixed?)
Instead we could make the separated mpc8610_hpcd fabric driver attach
to fsl,ssi.
static struct of_device_id fabric_of_match[] = {
{
.compatible = "fsl,ssi",
},
{},
};
Then in it's probe code check for the right platform.
unsigned long node = of_get_flat_dt_root();
if (!of_flat_dt_is_compatible(node, "fsl,MPC8610HPCD"))
return 0;
.. activate the code ...
You also need a static flag to make sure you don't active the driver
more than once.
This isn't the best solution since my four fabric drivers will still
load and check what platform they are on before exiting but at least
it works.
--
Jon Smirl
jonsmirl@gmail.com
^ permalink raw reply
* Re: [PATCH] powerpc: Add MPC837x PCIE controller RC mode support
From: Olof Johansson @ 2008-01-02 15:51 UTC (permalink / raw)
To: Li Li; +Cc: Wood Scott, linuxppc-dev, kim phillips
In-Reply-To: <1199272605.22416.8.camel@Guyver>
On Wed, Jan 02, 2008 at 07:16:45PM +0800, Li Li wrote:
> diff --git a/include/linux/pci_ids.h b/include/linux/pci_ids.h
> index 1ee009e..f84caa7 100644
> --- a/include/linux/pci_ids.h
> +++ b/include/linux/pci_ids.h
> @@ -2111,6 +2111,10 @@
> #define PCI_DEVICE_ID_TDI_EHCI 0x0101
>
> #define PCI_VENDOR_ID_FREESCALE 0x1957
> +#define PCI_DEVICE_ID_MPC8378E 0x00c4
> +#define PCI_DEVICE_ID_MPC8378 0x00c5
> +#define PCI_DEVICE_ID_MPC8377E 0x00c6
> +#define PCI_DEVICE_ID_MPC8377 0x00c7
> #define PCI_DEVICE_ID_MPC8548E 0x0012
> #define PCI_DEVICE_ID_MPC8548 0x0013
> #define PCI_DEVICE_ID_MPC8543E 0x0014
In general it's not needed to add device ID's to the global table,
especially when they're only used once or twice. Just use the hex constant
in the driver instead.
-Olof
^ permalink raw reply
* Re: [PATCH] ASoC drivers for the Freescale MPC8610 SoC
From: Jon Smirl @ 2008-01-02 15:34 UTC (permalink / raw)
To: Timur Tabi, Liam Girdwood; +Cc: linuxppc-dev, alsa-devel
In-Reply-To: <477BAB67.4080003@freescale.com>
On 1/2/08, Timur Tabi <timur@freescale.com> wrote:
> Jon Smirl wrote:
> > On 1/1/08, Jon Smirl <jonsmirl@gmail.com> wrote:
> >> On 12/19/07, Timur Tabi <timur@freescale.com> wrote:
> >>> + ssi@16000 {
> >>> + compatible = "fsl,ssi";
> >>> + cell-index = <0>;
> >>> + reg = <16000 100>;
> >>> + interrupt-parent = <&mpic>;
> >>> + interrupts = <3e 2>;
> >>> + fsl,mode = "i2s-slave";
> >>> + codec {
> >>> + compatible = "cirrus,cs4270";
> >>> + /* MCLK source is a stand-alone oscillator */
> >>> + bus-frequency = <bb8000>;
> >>> + };
> >>> + };
> >> Does this need to be bus-frequency? It's always called MCLK in all of
> >> the literature.
> >>
> >> In my case the MCLK comes from a chip on the i2c bus that is
> >> programmable How would that be encoded?.
> >
> > Looking at the cs4270 codec driver it is controlled by i2c (supports
> > SPI too). What happened to the conversation about putting codecs on
> > the controlling bus and then linking them to the data bus?
>
> The current CS4270 driver doesn't support device trees. When I wrote
> it, the idea of putting I2C info in the device tree was not finalized,
> and since the driver is supposed to be cross-platform, I decided to do
> it the old-fashioned way. Before I update the code, however, I'm
> waiting for:
>
> 1) The current code to be accepted into the tree
> 2) ASoC is updated to V2
> 3) The current drivers are updated to support ASoC V2.
I've been trying to get the i2c code in for two months. Hopefully it
will go in soon, no one had made any comments on it recently. Have you
tried your code with it?
There is nothing stopping your from putting a node for the CS4270 on
the i2c bus today. It just won't trigger the loading of the driver.
Don't we want to follow the device tree policy of putting the device
on the controlling bus and then link it to the data bus?
> I think ASoC V2 will make it easier to support device trees, but I'm not
> ready yet for that.
It makes it a little easier but it doesn't fix everything. We need to
start looking at it since none of the example driver for it are device
tree based. It still has problems with wanting 'struct
platform_device' when we have 'struct of_platform_device' pointers. It
also doesn't know how to dynamically load codecs based on device
trees.
Liam messed up all of my code when he refactored it in late December.
I've switched over to the current SOC code for the moment. The big
thing that v2 fixes is that SOC is changed to being a subsystem
instead of platform driver. Being a subsystem is the correct model.
It would be good if more pieces of v2 get push forward. Then we can
sort out the device tree issues in it.
>
> > If that's the case the cs4270 should be in the i2c bus node (missing
> > currently) and then a link from the SSI bus would point to it.
>
> The CS4270 is a child of both the I2C bus *and* the SSI bus. It needs
> to have two nodes, one under each. Your're right in that there needs to
> be a link, but until the code is updated to ASoC V2, I think it's
> premature to add that support.
Adding the second device tree node doesn't have anything to do with
ASOC v2. It's specific to powerpc and device trees.
--
Jon Smirl
jonsmirl@gmail.com
^ permalink raw reply
* Re: [PATCH] ASoC drivers for the Freescale MPC8610 SoC
From: Timur Tabi @ 2008-01-02 15:29 UTC (permalink / raw)
To: Jon Smirl; +Cc: linuxppc-dev, alsa-devel
In-Reply-To: <9e4733910801012027p4be16b92r43af773f4e5ae531@mail.gmail.com>
Jon Smirl wrote:
> On 12/19/07, Timur Tabi <timur@freescale.com> wrote:
>> sound/soc/fsl/fsl_ssi.c | 614 +++++++++++++++++++
>> sound/soc/fsl/fsl_ssi.h | 224 +++++++
>
> I'm confused about this part. You built a driver for the mpc8610 ssi
> port. This port has a device tree entry.
>
> + ssi@16000 {
> + compatible = "fsl,ssi";
> + cell-index = <0>;
> + reg = <16000 100>;
> + interrupt-parent = <&mpic>;
> + interrupts = <3e 2>;
> + fsl,mode = "i2s-slave";
> + codec {
> + compatible = "cirrus,cs4270";
> + /* MCLK source is a stand-alone oscillator */
> + bus-frequency = <bb8000>;
> + };
> + };
>
> But then you don't create an of_platform_driver for this device.
> Instead you create one for the fabric driver, struct
> of_platform_driver mpc8610_hpcd_of_driver, and directly link the SSI
> driver into it.
That's the best plan I came up with. This is apparently fixed in ASoC
V2. From ASoC V1's perspective, the fabric driver must be the master.
However, it doesn't make sense to have a node in the device tree for the
fabric driver, because there is no such "device". The fabric driver is
an abstraction. So I need to chose some other node to probe the fabric
driver with. I chose the SSI, since each SSI can have only one codec.
>
> +static struct of_device_id mpc8610_hpcd_match[] = {
> + {
> + .compatible = "fsl,ssi",
> + },
> + {}
> +};
> +MODULE_DEVICE_TABLE(of, mpc8610_hpcd_match);
> +
> +static struct of_platform_driver mpc8610_hpcd_of_driver = {
> + .owner = THIS_MODULE,
> + .name = "mpc8610_hpcd",
> + .match_table = mpc8610_hpcd_match,
> + .probe = mpc8610_hpcd_probe,
> + .remove = mpc8610_hpcd_remove,
> +};
>
> static int mpc8610_hpcd_probe(struct of_device *ofdev,
> const struct of_device_id *match)
> {
> .....
> machine_data->dai.cpu_dai = fsl_ssi_create_dai(&ssi_info);
>
> Isn't this two separate drivers that have been combined into one
> driver? Or does the fsl_ssi channel only work on the mpc8610_hpcd?
Sorry, I don't understand your question.
> This is the problem of knowing how to load the fabric driver that I
> was talking about in the other threads.
Yes, and the decision I made on this topic is to have the fabric driver
probed on the SSI node.
For ASoC V1, I believe the problem is undefined and each driver should
be implemented in whatever way works best.
> A device that can occur on
> more than one chip ".compatible = "fsl,ssi"," is being used to pull in
> a platform specific fabric driver, "mpc8610_hpcd". You can use the
> kernel config system to select the right driver for ".compatible =
> "fsl,ssi"," that matches you hardware and compile it in.
Ok, I think I understand that.
> But that doesn't work in my environment. My generic channel is
> "fsl,i2s". I have four different systems booting off from a shared
> network drive. Each of these systems needs the common "fsl,i2s" driver
> but they all four need different fabric drivers.
That, I don't understand. fsl,ssi is pretty much the same thing as
fsl,i2s, since the SSI *is* an I2S device. It's also an AC97 device,
which is why I added the fsl,mode property.
The fabric driver is specific to the board. So you should be using
Kconfig to select the fabric driver. There is no node in the device
tree for fabric drivers. I thought that was the consensus.
Are you saying that you want to use the same kernel on four different
systems? If so, then you need to find a way to compile all fabric
drivers together, and at boot time each fabric driver will decide
whether it will do anything.
^ permalink raw reply
* Re: [PATCH] powerpc: Add MPC837x PCIE controller RC mode support
From: Kumar Gala @ 2008-01-02 15:23 UTC (permalink / raw)
To: Li Li; +Cc: Wood Scott, linuxppc-dev, kim phillips
In-Reply-To: <1199272605.22416.8.camel@Guyver>
On Jan 2, 2008, at 5:16 AM, Li Li wrote:
> * The MPC837x PCIE controller hardware resources and SerDes are
> initiated in u-boot.
> * Merge the MPC837x PCIE code into arch/powerpc/sysdev/fsl_pci.c
> * The MPC837x PCIE controller`s configure address bit field is uniqe:
> bus number: bits 31-24
> device number: bits 23-19
> function number: bits 18-16
> ext reg number: bits 11-8
> reg number: bits 7-2
> * Add mpc837x_exclude_device to fixup a controller bug.
what is the bug that is being worked around?
- k
^ permalink raw reply
* Re: [PATCH] ASoC drivers for the Freescale MPC8610 SoC
From: Timur Tabi @ 2008-01-02 15:19 UTC (permalink / raw)
To: Jon Smirl; +Cc: linuxppc-dev, alsa-devel
In-Reply-To: <9e4733910801010942y47e4cdbfge5e0d3e44ab96760@mail.gmail.com>
Jon Smirl wrote:
> On 1/1/08, Jon Smirl <jonsmirl@gmail.com> wrote:
>> On 12/19/07, Timur Tabi <timur@freescale.com> wrote:
>>> + ssi@16000 {
>>> + compatible = "fsl,ssi";
>>> + cell-index = <0>;
>>> + reg = <16000 100>;
>>> + interrupt-parent = <&mpic>;
>>> + interrupts = <3e 2>;
>>> + fsl,mode = "i2s-slave";
>>> + codec {
>>> + compatible = "cirrus,cs4270";
>>> + /* MCLK source is a stand-alone oscillator */
>>> + bus-frequency = <bb8000>;
>>> + };
>>> + };
>> Does this need to be bus-frequency? It's always called MCLK in all of
>> the literature.
>>
>> In my case the MCLK comes from a chip on the i2c bus that is
>> programmable How would that be encoded?.
>
> Looking at the cs4270 codec driver it is controlled by i2c (supports
> SPI too). What happened to the conversation about putting codecs on
> the controlling bus and then linking them to the data bus?
The current CS4270 driver doesn't support device trees. When I wrote
it, the idea of putting I2C info in the device tree was not finalized,
and since the driver is supposed to be cross-platform, I decided to do
it the old-fashioned way. Before I update the code, however, I'm
waiting for:
1) The current code to be accepted into the tree
2) ASoC is updated to V2
3) The current drivers are updated to support ASoC V2.
I think ASoC V2 will make it easier to support device trees, but I'm not
ready yet for that.
> If that's the case the cs4270 should be in the i2c bus node (missing
> currently) and then a link from the SSI bus would point to it.
The CS4270 is a child of both the I2C bus *and* the SSI bus. It needs
to have two nodes, one under each. Your're right in that there needs to
be a link, but until the code is updated to ASoC V2, I think it's
premature to add that support.
^ permalink raw reply
* Re: [PATCH] ASoC drivers for the Freescale MPC8610 SoC
From: Timur Tabi @ 2008-01-02 15:10 UTC (permalink / raw)
To: Jon Smirl; +Cc: linuxppc-dev, alsa-devel
In-Reply-To: <9e4733910801010925j67192427o4e0e824b9d7e0ad0@mail.gmail.com>
Jon Smirl wrote:
> On 12/19/07, Timur Tabi <timur@freescale.com> wrote:
>> + ssi@16000 {
>> + compatible = "fsl,ssi";
>> + cell-index = <0>;
>> + reg = <16000 100>;
>> + interrupt-parent = <&mpic>;
>> + interrupts = <3e 2>;
>> + fsl,mode = "i2s-slave";
>> + codec {
>> + compatible = "cirrus,cs4270";
>> + /* MCLK source is a stand-alone oscillator */
>> + bus-frequency = <bb8000>;
>> + };
>> + };
>
> Does this need to be bus-frequency? It's always called MCLK in all of
> the literature.
I'm trying to make this node as generic as possible. The fabric driver
is the one that will parse this node and pass the data to the codec
driver, so I can't use any codec-specific terms.
The API from the fabric driver for passing clock information includes a
clock ID, a direction, and a frequency. I can do something like this:
clock1 = <0, bb8000>
Would that be better?
>
> In my case the MCLK comes from a chip on the i2c bus that is
> programmable How would that be encoded?.
I'm going under the assumption that MCLK does not change once the board
is up and running. In your case, you'd need to do something quite
different, because you're not reading the clock info from the device
tree and passing it to the codec at initialization once. If you want to
define an extension to the 'codec' child node that handles that, I'll
add it to the documentation.
^ permalink raw reply
* Re: [PATCH] powerpc: Add MPC837x PCIE controller RC mode support
From: Arnd Bergmann @ 2008-01-02 11:53 UTC (permalink / raw)
To: linuxppc-dev; +Cc: Wood Scott, kim phillips, Li Li
In-Reply-To: <1199272605.22416.8.camel@Guyver>
On Wednesday 02 January 2008, Li Li wrote:
> =A0#ifdef CONFIG_PCI
> -=A0=A0=A0=A0=A0=A0=A0for_each_compatible_node(np, "pci", "fsl,mpc8349-pc=
i")
> -=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0mpc83xx_add_bridge(np);
> +=A0=A0=A0=A0=A0=A0=A0for_each_compatible_node(np, "pci", "fsl,mpc8349-pc=
i") {
> +=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0if (primary_pci_bus) {
> +=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0mpc=
83xx_add_bridge(np, PPC_83XX_PCI | PPC_83XX_PCI_PRIMARY);
> +=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0pri=
mary_pci_bus =3D 0;
> +=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=A0=A0=A0=A0mpc=
83xx_add_bridge(np, PPC_83XX_PCI);
> +=A0=A0=A0=A0=A0=A0=A0}
> +
> +=A0=A0=A0=A0=A0=A0=A0for_each_compatible_node(np, "pci", "fsl,mpc8377-pc=
ie") {
> +=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0if (primary_pci_bus) {
> +=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0mpc=
83xx_add_bridge(np, PPC_83XX_PCIE | PPC_83XX_PCI_PRIMARY);
> +=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0pri=
mary_pci_bus =3D 0;
> +=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=A0=A0=A0=A0mpc=
83xx_add_bridge(np, PPC_83XX_PCIE);
> +=A0=A0=A0=A0=A0=A0=A0}
> +
> +=A0=A0=A0=A0=A0=A0=A0ppc_md.pci_exclude_device =3D mpc837x_exclude_devic=
e;
> =A0#endif
A few comments on how I think this can be improved:
* Instead of duplicating this code across all platforms, make a single
function that does the probing in one place (including the #ifdef
CONFIG_PCI).
* Better yet, get rid of mpc83xx_add_bridge and make it possible for the
mpc83xx code to use the of_pci_phb_driver from
arch/powerpc/kernel/of_platform.c. This used to be impossible because
of the differences between 32 and 64 bit code for PCI probing, but
I think with the work than benh put into unifying the two, we should
be pretty close now.
* The detection method for the primary bus is somewhat fragile, because
we depend on the order of the nodes in the device tree, which is not
specified to have a meaning. /Maybe/ there could be a property in
(at most) one of the PCI host bridge nodes saying that this specific bus
is the primary one.
* Since you are using exactly the same probing code for pci and pcie,
you may want to check for a more generic "compatible" property than
the specific model, so you can use the same code for both.
Arnd <><
^ permalink raw reply
* [PATCH] powerpc: Add MPC837x PCIE controller RC mode support
From: Li Li @ 2008-01-02 11:16 UTC (permalink / raw)
To: Kumar Gala, kim phillips, Wood Scott, linuxppc-dev
* The MPC837x PCIE controller hardware resources and SerDes are initiated in u-boot.
* Merge the MPC837x PCIE code into arch/powerpc/sysdev/fsl_pci.c
* The MPC837x PCIE controller`s configure address bit field is uniqe:
bus number: bits 31-24
device number: bits 23-19
function number: bits 18-16
ext reg number: bits 11-8
reg number: bits 7-2
* Add mpc837x_exclude_device to fixup a controller bug.
* Add flag variant to mpc83xx_add_bridge function.
Signed-off-by: Tony Li <tony.li@freescale.com>
---
arch/powerpc/boot/dts/mpc8377_mds.dts | 54 ++++++++--
arch/powerpc/boot/dts/mpc8378_mds.dts | 54 ++++++++--
arch/powerpc/platforms/83xx/Kconfig | 2 +
arch/powerpc/platforms/83xx/mpc8313_rdb.c | 10 ++-
arch/powerpc/platforms/83xx/mpc832x_mds.c | 12 ++-
arch/powerpc/platforms/83xx/mpc832x_rdb.c | 10 ++-
arch/powerpc/platforms/83xx/mpc834x_itx.c | 10 ++-
arch/powerpc/platforms/83xx/mpc834x_mds.c | 10 ++-
arch/powerpc/platforms/83xx/mpc836x_mds.c | 12 ++-
arch/powerpc/platforms/83xx/mpc837x_mds.c | 39 +++++++-
arch/powerpc/platforms/83xx/mpc83xx.h | 6 +-
arch/powerpc/platforms/83xx/pci.c | 29 ++++--
arch/powerpc/sysdev/fsl_pci.c | 159 +++++++++++++++++++++++++++++
arch/powerpc/sysdev/fsl_pci.h | 3 +
include/asm-powerpc/pci-bridge.h | 1 +
include/linux/pci_ids.h | 4 +
16 files changed, 375 insertions(+), 40 deletions(-)
diff --git a/arch/powerpc/boot/dts/mpc8377_mds.dts b/arch/powerpc/boot/dts/mpc8377_mds.dts
index 4402e39..4af3802 100644
--- a/arch/powerpc/boot/dts/mpc8377_mds.dts
+++ b/arch/powerpc/boot/dts/mpc8377_mds.dts
@@ -197,14 +197,6 @@
clock = <d#100>;
};
- serdes2:serdes@e3100 {
- compatible = "fsl,serdes";
- reg = <e3100 100>;
- vdd-1v;
- protocol = "pcie";
- clock = <d#100>;
- };
-
/* IPIC
* interrupts cell = <intr #, sense>
* sense values match linux IORESOURCE_IRQ_* defines:
@@ -279,4 +271,50 @@
compatible = "fsl,mpc8349-pci";
device_type = "pci";
};
+
+ pcie@e0009000 {
+ interrupt-map-mask = <f800 0 0 7>;
+ interrupt-map = <
+ 0000 0 0 1 &ipic 1 8
+ 0000 0 0 2 &ipic 1 8
+ 0000 0 0 3 &ipic 1 8
+ 0000 0 0 4 &ipic 1 8
+ >;
+ interrupt-parent = < &ipic >;
+ interrupts = <1 8>;
+ bus-range = <0 0>;
+ ranges = <02000000 0 A0000000 A0000000 0 10000000
+ 01000000 0 00000000 B1000000 0 00800000>;
+ clock-frequency = <0>;
+ #interrupt-cells = <1>;
+ #size-cells = <2>;
+ #address-cells = <3>;
+ reg = <e0009000 00001000
+ b0000000 01000000>;
+ compatible = "fsl,mpc8377-pcie";
+ device_type = "pci";
+ };
+
+ pcie@e000a000 {
+ interrupt-map-mask = <f800 0 0 7>;
+ interrupt-map = <
+ 0000 0 0 1 &ipic 2 8
+ 0000 0 0 2 &ipic 2 8
+ 0000 0 0 3 &ipic 2 8
+ 0000 0 0 4 &ipic 2 8
+ >;
+ interrupt-parent = < &ipic >;
+ interrupts = <2 8>;
+ bus-range = <0 0>;
+ ranges = <02000000 0 C0000000 C0000000 0 10000000
+ 01000000 0 00000000 D1000000 0 00800000>;
+ clock-frequency = <0>;
+ #interrupt-cells = <1>;
+ #size-cells = <2>;
+ #address-cells = <3>;
+ reg = <e000a000 00001000
+ d0000000 01000000>;
+ compatible = "fsl,mpc8377-pcie";
+ device_type = "pci";
+ };
};
diff --git a/arch/powerpc/boot/dts/mpc8378_mds.dts b/arch/powerpc/boot/dts/mpc8378_mds.dts
index 54171f4..de9d40c 100644
--- a/arch/powerpc/boot/dts/mpc8378_mds.dts
+++ b/arch/powerpc/boot/dts/mpc8378_mds.dts
@@ -179,14 +179,6 @@
clock = <d#100>;
};
- serdes2:serdes@e3100 {
- compatible = "fsl,serdes";
- reg = <e3100 100>;
- vdd-1v;
- protocol = "pcie";
- clock = <d#100>;
- };
-
/* IPIC
* interrupts cell = <intr #, sense>
* sense values match linux IORESOURCE_IRQ_* defines:
@@ -261,4 +253,50 @@
compatible = "fsl,mpc8349-pci";
device_type = "pci";
};
+
+ pcie@e0009000 {
+ interrupt-map-mask = <f800 0 0 7>;
+ interrupt-map = <
+ 0000 0 0 1 &ipic 1 8
+ 0000 0 0 2 &ipic 1 8
+ 0000 0 0 3 &ipic 1 8
+ 0000 0 0 4 &ipic 1 8
+ >;
+ interrupt-parent = < &ipic >;
+ interrupts = <1 8>;
+ bus-range = <0 0>;
+ ranges = <02000000 0 A0000000 A0000000 0 10000000
+ 01000000 0 00000000 B1000000 0 00800000>;
+ clock-frequency = <0>;
+ #interrupt-cells = <1>;
+ #size-cells = <2>;
+ #address-cells = <3>;
+ reg = <e0009000 00001000
+ b0000000 01000000>;
+ compatible = "fsl,mpc8377-pcie";
+ device_type = "pci";
+ };
+
+ pcie@e000a000 {
+ interrupt-map-mask = <f800 0 0 7>;
+ interrupt-map = <
+ 0000 0 0 1 &ipic 2 8
+ 0000 0 0 2 &ipic 2 8
+ 0000 0 0 3 &ipic 2 8
+ 0000 0 0 4 &ipic 2 8
+ >;
+ interrupt-parent = < &ipic >;
+ interrupts = <2 8>;
+ bus-range = <0 0>;
+ ranges = <02000000 0 C0000000 C0000000 0 10000000
+ 01000000 0 00000000 D1000000 0 00800000>;
+ clock-frequency = <0>;
+ #interrupt-cells = <1>;
+ #size-cells = <2>;
+ #address-cells = <3>;
+ reg = <e000a000 00001000
+ d0000000 01000000>;
+ compatible = "fsl,mpc8377-pcie";
+ device_type = "pci";
+ };
};
diff --git a/arch/powerpc/platforms/83xx/Kconfig b/arch/powerpc/platforms/83xx/Kconfig
index 0c61e7a..0b4bfb5 100644
--- a/arch/powerpc/platforms/83xx/Kconfig
+++ b/arch/powerpc/platforms/83xx/Kconfig
@@ -86,4 +86,6 @@ config PPC_MPC837x
select PPC_UDBG_16550
select PPC_INDIRECT_PCI
select FSL_SERDES
+ select FSL_PCI if PCI
default y if MPC837x_MDS
+
diff --git a/arch/powerpc/platforms/83xx/mpc8313_rdb.c b/arch/powerpc/platforms/83xx/mpc8313_rdb.c
index 33766b8..25d8df4 100644
--- a/arch/powerpc/platforms/83xx/mpc8313_rdb.c
+++ b/arch/powerpc/platforms/83xx/mpc8313_rdb.c
@@ -37,14 +37,20 @@ static void __init mpc8313_rdb_setup_arch(void)
{
#ifdef CONFIG_PCI
struct device_node *np;
+ int primary_pci_bus = 1;
#endif
if (ppc_md.progress)
ppc_md.progress("mpc8313_rdb_setup_arch()", 0);
#ifdef CONFIG_PCI
- for_each_compatible_node(np, "pci", "fsl,mpc8349-pci")
- mpc83xx_add_bridge(np);
+ for_each_compatible_node(np, "pci", "fsl,mpc8349-pci") {
+ if (primary_pci_bus) {
+ mpc83xx_add_bridge(np, PPC_83XX_PCI | PPC_83XX_PCI_PRIMARY);
+ primary_pci_bus = 0;
+ } else
+ mpc83xx_add_bridge(np, PPC_83XX_PCI);
+ }
#endif
mpc831x_usb_cfg();
}
diff --git a/arch/powerpc/platforms/83xx/mpc832x_mds.c b/arch/powerpc/platforms/83xx/mpc832x_mds.c
index 972fa85..ddb0b2e 100644
--- a/arch/powerpc/platforms/83xx/mpc832x_mds.c
+++ b/arch/powerpc/platforms/83xx/mpc832x_mds.c
@@ -58,6 +58,9 @@ static u8 *bcsr_regs = NULL;
static void __init mpc832x_sys_setup_arch(void)
{
struct device_node *np;
+#ifdef CONFIG_PCI
+ int primary_pci_bus = 1;
+#endif
if (ppc_md.progress)
ppc_md.progress("mpc832x_sys_setup_arch()", 0);
@@ -73,8 +76,13 @@ static void __init mpc832x_sys_setup_arch(void)
}
#ifdef CONFIG_PCI
- for_each_compatible_node(np, "pci", "fsl,mpc8349-pci")
- mpc83xx_add_bridge(np);
+ for_each_compatible_node(np, "pci", "fsl,mpc8349-pci") {
+ if (primary_pci_bus) {
+ mpc83xx_add_bridge(np, PPC_83XX_PCI | PPC_83XX_PCI_PRIMARY);
+ primary_pci_bus = 0;
+ } else
+ mpc83xx_add_bridge(np, PPC_83XX_PCI);
+ }
#endif
#ifdef CONFIG_QUICC_ENGINE
diff --git a/arch/powerpc/platforms/83xx/mpc832x_rdb.c b/arch/powerpc/platforms/83xx/mpc832x_rdb.c
index fbca336..aef35f5 100644
--- a/arch/powerpc/platforms/83xx/mpc832x_rdb.c
+++ b/arch/powerpc/platforms/83xx/mpc832x_rdb.c
@@ -87,14 +87,20 @@ static void __init mpc832x_rdb_setup_arch(void)
{
#if defined(CONFIG_PCI) || defined(CONFIG_QUICC_ENGINE)
struct device_node *np;
+ int primary_pci_bus = 1;
#endif
if (ppc_md.progress)
ppc_md.progress("mpc832x_rdb_setup_arch()", 0);
#ifdef CONFIG_PCI
- for_each_compatible_node(np, "pci", "fsl,mpc8349-pci")
- mpc83xx_add_bridge(np);
+ for_each_compatible_node(np, "pci", "fsl,mpc8349-pci") {
+ if (primary_pci_bus) {
+ mpc83xx_add_bridge(np, PPC_83XX_PCI | PPC_83XX_PCI_PRIMARY);
+ primary_pci_bus = 0;
+ } else
+ mpc83xx_add_bridge(np, PPC_83XX_PCI);
+ }
#endif
#ifdef CONFIG_QUICC_ENGINE
diff --git a/arch/powerpc/platforms/83xx/mpc834x_itx.c b/arch/powerpc/platforms/83xx/mpc834x_itx.c
index aa76819..c428f62 100644
--- a/arch/powerpc/platforms/83xx/mpc834x_itx.c
+++ b/arch/powerpc/platforms/83xx/mpc834x_itx.c
@@ -46,14 +46,20 @@ static void __init mpc834x_itx_setup_arch(void)
{
#ifdef CONFIG_PCI
struct device_node *np;
+ int primary_pci_bus = 1;
#endif
if (ppc_md.progress)
ppc_md.progress("mpc834x_itx_setup_arch()", 0);
#ifdef CONFIG_PCI
- for_each_compatible_node(np, "pci", "fsl,mpc8349-pci")
- mpc83xx_add_bridge(np);
+ for_each_compatible_node(np, "pci", "fsl,mpc8349-pci") {
+ if (primary_pci_bus) {
+ mpc83xx_add_bridge(np, PPC_83XX_PCI | PPC_83XX_PCI_PRIMARY);
+ primary_pci_bus = 0;
+ } else
+ mpc83xx_add_bridge(np, PPC_83XX_PCI);
+ }
#endif
mpc834x_usb_cfg();
diff --git a/arch/powerpc/platforms/83xx/mpc834x_mds.c b/arch/powerpc/platforms/83xx/mpc834x_mds.c
index 00aed7c..e86dce8 100644
--- a/arch/powerpc/platforms/83xx/mpc834x_mds.c
+++ b/arch/powerpc/platforms/83xx/mpc834x_mds.c
@@ -77,14 +77,20 @@ static void __init mpc834x_mds_setup_arch(void)
{
#ifdef CONFIG_PCI
struct device_node *np;
+ int primary_pci_bus = 1;
#endif
if (ppc_md.progress)
ppc_md.progress("mpc834x_mds_setup_arch()", 0);
#ifdef CONFIG_PCI
- for_each_compatible_node(np, "pci", "fsl,mpc8349-pci")
- mpc83xx_add_bridge(np);
+ for_each_compatible_node(np, "pci", "fsl,mpc8349-pci") {
+ if (primary_pci_bus) {
+ mpc83xx_add_bridge(np, PPC_83XX_PCI | PPC_83XX_PCI_PRIMARY);
+ primary_pci_bus = 0;
+ } else
+ mpc83xx_add_bridge(np, PPC_83XX_PCI);
+ }
#endif
mpc834xemds_usb_cfg();
diff --git a/arch/powerpc/platforms/83xx/mpc836x_mds.c b/arch/powerpc/platforms/83xx/mpc836x_mds.c
index 0f3855c..6e6670c 100644
--- a/arch/powerpc/platforms/83xx/mpc836x_mds.c
+++ b/arch/powerpc/platforms/83xx/mpc836x_mds.c
@@ -64,6 +64,9 @@ static u8 *bcsr_regs = NULL;
static void __init mpc836x_mds_setup_arch(void)
{
struct device_node *np;
+#ifdef CONFIG_PCI
+ int primary_pci_bus = 1;
+#endif
if (ppc_md.progress)
ppc_md.progress("mpc836x_mds_setup_arch()", 0);
@@ -79,8 +82,13 @@ static void __init mpc836x_mds_setup_arch(void)
}
#ifdef CONFIG_PCI
- for_each_compatible_node(np, "pci", "fsl,mpc8349-pci")
- mpc83xx_add_bridge(np);
+ for_each_compatible_node(np, "pci", "fsl,mpc8349-pci") {
+ if (primary_pci_bus) {
+ mpc83xx_add_bridge(np, PPC_83XX_PCI | PPC_83XX_PCI_PRIMARY);
+ primary_pci_bus = 0;
+ } else
+ mpc83xx_add_bridge(np, PPC_83XX_PCI);
+ }
#endif
#ifdef CONFIG_QUICC_ENGINE
diff --git a/arch/powerpc/platforms/83xx/mpc837x_mds.c b/arch/powerpc/platforms/83xx/mpc837x_mds.c
index 166c111..ec2fa9f 100644
--- a/arch/powerpc/platforms/83xx/mpc837x_mds.c
+++ b/arch/powerpc/platforms/83xx/mpc837x_mds.c
@@ -25,8 +25,27 @@
#ifndef CONFIG_PCI
unsigned long isa_io_base = 0;
unsigned long isa_mem_base = 0;
+
#endif
+#ifdef CONFIG_PCI
+static int mpc837x_exclude_device(struct pci_controller *hose,
+ u_char bus, u_char devfn)
+{
+ struct pci_bus *pci_bus;
+
+ if (hose->indirect_type & PPC_INDIRECT_TYPE_MPC83XX_PCIE) {
+ pci_bus = pci_find_bus(hose->global_number, bus);
+ if ((bus == hose->first_busno) ||
+ (pci_bus->primary == hose->first_busno)) {
+ if (devfn & 0xf8)
+ return PCIBIOS_DEVICE_NOT_FOUND;
+ }
+ }
+
+ return PCIBIOS_SUCCESSFUL;
+}
+#endif
/* ************************************************************************
*
* Setup the architecture
@@ -36,14 +55,30 @@ static void __init mpc837x_mds_setup_arch(void)
{
#ifdef CONFIG_PCI
struct device_node *np;
+ int primary_pci_bus = 1;
#endif
if (ppc_md.progress)
ppc_md.progress("mpc837x_mds_setup_arch()", 0);
#ifdef CONFIG_PCI
- for_each_compatible_node(np, "pci", "fsl,mpc8349-pci")
- mpc83xx_add_bridge(np);
+ for_each_compatible_node(np, "pci", "fsl,mpc8349-pci") {
+ if (primary_pci_bus) {
+ mpc83xx_add_bridge(np, PPC_83XX_PCI | PPC_83XX_PCI_PRIMARY);
+ primary_pci_bus = 0;
+ } else
+ mpc83xx_add_bridge(np, PPC_83XX_PCI);
+ }
+
+ for_each_compatible_node(np, "pci", "fsl,mpc8377-pcie") {
+ if (primary_pci_bus) {
+ mpc83xx_add_bridge(np, PPC_83XX_PCIE | PPC_83XX_PCI_PRIMARY);
+ primary_pci_bus = 0;
+ } else
+ mpc83xx_add_bridge(np, PPC_83XX_PCIE);
+ }
+
+ ppc_md.pci_exclude_device = mpc837x_exclude_device;
#endif
}
diff --git a/arch/powerpc/platforms/83xx/mpc83xx.h b/arch/powerpc/platforms/83xx/mpc83xx.h
index b778cb4..552e9bf 100644
--- a/arch/powerpc/platforms/83xx/mpc83xx.h
+++ b/arch/powerpc/platforms/83xx/mpc83xx.h
@@ -47,8 +47,10 @@
* Declaration for the various functions exported by the
* mpc83xx_* files. Mostly for use by mpc83xx_setup
*/
-
-extern int mpc83xx_add_bridge(struct device_node *dev);
+#define PPC_83XX_PCI_PRIMARY 0x1
+#define PPC_83XX_PCI 0x2
+#define PPC_83XX_PCIE 0x4
+extern int mpc83xx_add_bridge(struct device_node *dev, int flags);
extern void mpc83xx_restart(char *cmd);
extern long mpc83xx_time_init(void);
extern int mpc834x_usb_cfg(void);
diff --git a/arch/powerpc/platforms/83xx/pci.c b/arch/powerpc/platforms/83xx/pci.c
index 80425d7..7a3382a 100644
--- a/arch/powerpc/platforms/83xx/pci.c
+++ b/arch/powerpc/platforms/83xx/pci.c
@@ -24,6 +24,9 @@
#include <asm/pci-bridge.h>
#include <asm/prom.h>
#include <sysdev/fsl_soc.h>
+#include <sysdev/fsl_pci.h>
+
+#include "mpc83xx.h"
#undef DEBUG
@@ -33,13 +36,13 @@
#define DBG(x...)
#endif
-int __init mpc83xx_add_bridge(struct device_node *dev)
+int __init mpc83xx_add_bridge(struct device_node *dev, int flags)
{
int len;
struct pci_controller *hose;
struct resource rsrc;
const int *bus_range;
- int primary = 1, has_address = 0;
+ int has_address = 0;
phys_addr_t immr = get_immrbase();
DBG("Adding PCI host bridge %s\n", dev->full_name);
@@ -63,16 +66,23 @@ int __init mpc83xx_add_bridge(struct device_node *dev)
hose->last_busno = bus_range ? bus_range[1] : 0xff;
/* MPC83xx supports up to two host controllers one at 0x8500 from immrbar
- * the other at 0x8600, we consider the 0x8500 the primary controller
+ * the other at 0x8600.
*/
/* PCI 1 */
- if ((rsrc.start & 0xfffff) == 0x8500) {
+ if ((rsrc.start & 0xfffff) == 0x8500)
setup_indirect_pci(hose, immr + 0x8300, immr + 0x8304, 0);
- }
/* PCI 2 */
- if ((rsrc.start & 0xfffff) == 0x8600) {
+ if ((rsrc.start & 0xfffff) == 0x8600)
setup_indirect_pci(hose, immr + 0x8380, immr + 0x8384, 0);
- primary = 0;
+
+ if (flags & PPC_83XX_PCIE) {
+ struct resource cfg_space;
+
+ if (of_address_to_resource(dev, 1, &cfg_space)) {
+ printk("PCIE RC losts configure space. Skip it\n");
+ return 1;
+ }
+ mpc83xx_setup_pcie(hose, &rsrc, &cfg_space);
}
printk(KERN_INFO "Found MPC83xx PCI host bridge at 0x%016llx. "
@@ -85,7 +95,10 @@ int __init mpc83xx_add_bridge(struct device_node *dev)
/* Interpret the "ranges" property */
/* This also maps the I/O region and sets isa_io/mem_base */
- pci_process_bridge_OF_ranges(hose, dev, primary);
+ if (flags & PPC_83XX_PCI_PRIMARY)
+ pci_process_bridge_OF_ranges(hose, dev, 1);
+ else
+ pci_process_bridge_OF_ranges(hose, dev, 0);
return 0;
}
diff --git a/arch/powerpc/sysdev/fsl_pci.c b/arch/powerpc/sysdev/fsl_pci.c
index 33df4c3..dfd9a40 100644
--- a/arch/powerpc/sysdev/fsl_pci.c
+++ b/arch/powerpc/sysdev/fsl_pci.c
@@ -240,6 +240,165 @@ int __init fsl_add_bridge(struct device_node *dev, int is_primary)
return 0;
}
+/* MPC83xx PCIE routines*/
+/* PCIE Registers */
+#define PEX_LTSSM_STAT 0x404
+#define PEX_LTSSM_STAT_L0 0x16
+#define PEX_GCLK_RATIO 0x440
+
+/* With the convention of u-boot, the PCIE outbound window 0 serves
+ * as configuration transactions outbound */
+#define PEX_OUTWIN0_TAL 0xCA8
+#define PEX_OUTWIN0_TAH 0xCAC
+
+void remap_cfg_outbound(void * __iomem reg_base, u32 tal, u32 tah)
+{
+ out_le32(reg_base + PEX_OUTWIN0_TAL, tal);
+ out_le32(reg_base + PEX_OUTWIN0_TAH, tah);
+}
+
+static int mpc83xx_read_config_pcie(struct pci_bus *bus,
+ uint devfn, int offset, int len, u32 *val)
+{
+ struct pci_controller *hose = bus->sysdata;
+ void __iomem *cfg_addr;
+ static u32 orig_busno = 0;
+ u32 bus_no;
+
+ if (hose->indirect_type & PPC_INDIRECT_TYPE_NO_PCIE_LINK)
+ return PCIBIOS_DEVICE_NOT_FOUND;
+
+ if (ppc_md.pci_exclude_device)
+ if (ppc_md.pci_exclude_device(hose, bus->number, devfn))
+ return PCIBIOS_DEVICE_NOT_FOUND;
+
+ switch (len) {
+ case 2:
+ if (offset & 1)
+ return -EINVAL;
+ break;
+ case 4:
+ if (offset & 3)
+ return -EINVAL;
+ break;
+ }
+
+ if ((bus->number == hose->first_busno) &&
+ (hose->indirect_type & PPC_INDIRECT_TYPE_MPC83XX_PCIE))
+ cfg_addr = (void __iomem *)((ulong) hose->cfg_data + (offset & 0xfff));
+ else {
+ bus_no = bus->number - hose->first_busno;
+ if (bus_no != orig_busno) {
+ remap_cfg_outbound((void __iomem *)hose->cfg_data, bus_no, 0);
+ orig_busno = bus_no;
+ }
+ cfg_addr = (void __iomem *)((ulong) hose->cfg_addr +
+ ((devfn << 16) | (offset & 0xfff)));
+ }
+
+ switch (len) {
+ case 1:
+ *val = in_8(cfg_addr);
+ break;
+ case 2:
+ *val = in_le16(cfg_addr);
+ break;
+ default:
+ *val = in_le32(cfg_addr);
+ break;
+ }
+
+ return PCIBIOS_SUCCESSFUL;
+}
+
+static int mpc83xx_write_config_pcie(struct pci_bus *bus,
+ uint devfn, int offset, int len, u32 val)
+{
+ struct pci_controller *hose = bus->sysdata;
+ void __iomem *cfg_addr;
+ static u32 orig_busno = 0;
+ u32 bus_no;
+
+ if (hose->indirect_type & PPC_INDIRECT_TYPE_NO_PCIE_LINK)
+ return PCIBIOS_DEVICE_NOT_FOUND;
+
+ if (ppc_md.pci_exclude_device)
+ if (ppc_md.pci_exclude_device(hose, bus->number, devfn))
+ return PCIBIOS_DEVICE_NOT_FOUND;
+
+ switch (len) {
+ case 2:
+ if (offset & 1)
+ return -EINVAL;
+ break;
+ case 4:
+ if (offset & 3)
+ return -EINVAL;
+ break;
+ }
+
+
+ if ((bus->number == hose->first_busno) &&
+ (hose->indirect_type & PPC_INDIRECT_TYPE_MPC83XX_PCIE))
+ cfg_addr = (void __iomem *)((ulong) hose->cfg_data + (offset & 0xfff));
+ else {
+ bus_no = bus->number - hose->first_busno;
+ if (bus_no != orig_busno) {
+ remap_cfg_outbound((void __iomem *)hose->cfg_data, bus_no, 0);
+ orig_busno = bus_no;
+ }
+ cfg_addr = (void __iomem *)((ulong) hose->cfg_addr +
+ ((devfn << 16) | (offset & 0xfff)));
+ }
+
+ switch (len) {
+ case 1:
+ out_8(cfg_addr, val);
+ break;
+ case 2:
+ out_le16(cfg_addr, val);
+ break;
+ default:
+ out_le32(cfg_addr, val);
+ break;
+ }
+
+ return PCIBIOS_SUCCESSFUL;
+}
+
+static struct pci_ops mpc83xx_pcie_ops = {
+ mpc83xx_read_config_pcie,
+ mpc83xx_write_config_pcie
+};
+
+void __init mpc83xx_setup_pcie(struct pci_controller *hose,
+ struct resource *reg, struct resource *cfg_space)
+{
+ void __iomem *hose_cfg_header, *mbase;
+ u32 val;
+
+ hose_cfg_header = ioremap(reg->start, reg->end - reg->start + 1);
+
+ val = in_le32(hose_cfg_header + PEX_LTSSM_STAT);
+ if (val < PEX_LTSSM_STAT_L0)
+ hose->indirect_type |= PPC_INDIRECT_TYPE_NO_PCIE_LINK;
+ hose->indirect_type |= PPC_INDIRECT_TYPE_MPC83XX_PCIE;
+
+ mbase = ioremap(cfg_space->start & PAGE_MASK, cfg_space->end - cfg_space->start + 1);
+ hose->ops = &mpc83xx_pcie_ops;
+ hose->cfg_addr = mbase + (cfg_space->start & ~PAGE_MASK);
+
+ /* The MPC83xx PCIE implements direct access configure space
+ * routines instead of indirect ones. So, the cfg_data field is free.
+ * The MPC83xx PCIE RC configure header is memory-mapped,
+ * we use cfg_data as this header pointer */
+ hose->cfg_data = hose_cfg_header;
+}
+
+DECLARE_PCI_FIXUP_EARLY(0x1957, PCI_DEVICE_ID_MPC8377E, quirk_fsl_pcie_transparent);
+DECLARE_PCI_FIXUP_EARLY(0x1957, PCI_DEVICE_ID_MPC8377, quirk_fsl_pcie_transparent);
+DECLARE_PCI_FIXUP_EARLY(0x1957, PCI_DEVICE_ID_MPC8378E, quirk_fsl_pcie_transparent);
+DECLARE_PCI_FIXUP_EARLY(0x1957, PCI_DEVICE_ID_MPC8378, quirk_fsl_pcie_transparent);
DECLARE_PCI_FIXUP_EARLY(0x1957, PCI_DEVICE_ID_MPC8548E, quirk_fsl_pcie_transparent);
DECLARE_PCI_FIXUP_EARLY(0x1957, PCI_DEVICE_ID_MPC8548, quirk_fsl_pcie_transparent);
DECLARE_PCI_FIXUP_EARLY(0x1957, PCI_DEVICE_ID_MPC8543E, quirk_fsl_pcie_transparent);
diff --git a/arch/powerpc/sysdev/fsl_pci.h b/arch/powerpc/sysdev/fsl_pci.h
index 37b04ad..a70ee0f 100644
--- a/arch/powerpc/sysdev/fsl_pci.h
+++ b/arch/powerpc/sysdev/fsl_pci.h
@@ -84,5 +84,8 @@ struct ccsr_pci {
extern int fsl_add_bridge(struct device_node *dev, int is_primary);
extern void fsl_pcibios_fixup_bus(struct pci_bus *bus);
+extern void mpc83xx_setup_pcie(struct pci_controller *hose,
+ struct resource *reg, struct resource *cfg_space);
+
#endif /* __POWERPC_FSL_PCI_H */
#endif /* __KERNEL__ */
diff --git a/include/asm-powerpc/pci-bridge.h b/include/asm-powerpc/pci-bridge.h
index dc31845..372598b 100644
--- a/include/asm-powerpc/pci-bridge.h
+++ b/include/asm-powerpc/pci-bridge.h
@@ -56,6 +56,7 @@ struct pci_controller {
#define PPC_INDIRECT_TYPE_SURPRESS_PRIMARY_BUS (0x00000004)
#define PPC_INDIRECT_TYPE_NO_PCIE_LINK (0x00000008)
#define PPC_INDIRECT_TYPE_BIG_ENDIAN (0x00000010)
+#define PPC_INDIRECT_TYPE_MPC83XX_PCIE (0x00000020)
u32 indirect_type;
/* Currently, we limit ourselves to 1 IO range and 3 mem
diff --git a/include/linux/pci_ids.h b/include/linux/pci_ids.h
index 1ee009e..f84caa7 100644
--- a/include/linux/pci_ids.h
+++ b/include/linux/pci_ids.h
@@ -2111,6 +2111,10 @@
#define PCI_DEVICE_ID_TDI_EHCI 0x0101
#define PCI_VENDOR_ID_FREESCALE 0x1957
+#define PCI_DEVICE_ID_MPC8378E 0x00c4
+#define PCI_DEVICE_ID_MPC8378 0x00c5
+#define PCI_DEVICE_ID_MPC8377E 0x00c6
+#define PCI_DEVICE_ID_MPC8377 0x00c7
#define PCI_DEVICE_ID_MPC8548E 0x0012
#define PCI_DEVICE_ID_MPC8548 0x0013
#define PCI_DEVICE_ID_MPC8543E 0x0014
--
1.5.3
^ permalink raw reply related
* Re: [PATCHv3] powerpc: DBox2 Board Support
From: David Gibson @ 2008-01-02 11:20 UTC (permalink / raw)
To: Jochen Friedrich
Cc: Scott Wood, linuxppc-dev, linux-kernel,
linuxppc-embedded@ozlabs.org
In-Reply-To: <477522DB.4000100@scram.de>
On Fri, Dec 28, 2007 at 05:22:51PM +0100, Jochen Friedrich wrote:
> This patch adds device tree source, default config and setup code for
> DBox2 devices.
[snip]
> +++ b/arch/powerpc/boot/dts/dbox2.dts
> @@ -0,0 +1,251 @@
> +/*
> + * DBOX2 Device Tree Source
> + *
> + * Copyright 2007 Jochen Friedrich <jochen@scram.de>
> + *
> + * This program is free software; you can redistribute it and/or modify it
> + * under the terms of the GNU General Public License as published by the
> + * Free Software Foundation; either version 2 of the License, or (at your
> + * option) any later version.
> + */
> +
> +/ {
> + model = "unknown,dbox2"; // boot wrapper fills in correct manufacturer
Probably better just to leave model out of the dts and let the
bootwrapper add it.
[snip]
> + cam@3,0 {
> + compatible = "betaresearch,dbox2-cam";
> + reg = <3 0 20000 6 0 20000>;
> + interrupts = <6 2>;
> + interrupt-parent = <&PIC>;
> + gpios = <1 1c 1 1d 1 1e 1 1f>;
> + gpio-parent = <&CPM1_PIO>;
> + };
> +
> + mpegdec@4,0 {
The established generic names don't generally use abbreviations. So I
would suggest "mpeg-decoder" here instead.
> + compatible = "c-cube,avia";
> + reg = <4 0 200>;
> + interrupts = <8 2>;
> + interrupt-parent = <&PIC>;
> + };
> +
> + mpegdemux@5,0 {
Likewise "mpeg-demultiplexer"
> + compatible = "c-cube,enx";
> + reg = <5 0 3400 7 0 200000>;
> + interrupts = <2 2>;
> + interrupt-parent = <&PIC>;
> + };
> +
> + flash@0,0 {
> + compatible = "cfi-flash";
> + reg = <0 0 800000>;
> + bank-width = <4>;
> + device-width = <1>;
> + #address-cells = <1>;
> + #size-cells = <1>;
> + partition@0 {
> + label = "BR bootloader";
> + reg = <0 20000>;
> + read-only;
> + };
> + partition@20000 {
> + label = "FLFS (U-Boot)";
> + reg = <20000 20000>;
> + };
> + partition@40000 {
> + label = "rootfs";
> + reg = <40000 7c0000>;
> + };
> + ovpartition@20000 {
Uh.. I really don't know what the MTD driver will handle these
overlapping partitions. I suspect it will not be pretty...
> + label = "Flash without bootloader";
> + reg = <20000 7e0000>;
> + };
> + ovpartition@0 {
> + label = "Complete Flash";
> + reg = <0 800000>;
> + read-only;
> + };
> + };
> + };
[snip]
> + chosen {
> + linux,stdout-path = "/soc/cpm/serial@a80";
Probably worth using a path reference here, now they're implemented.
--
David Gibson | I'll have my music baroque, and my code
david AT gibson.dropbear.id.au | minimalist, thank you. NOT _the_ _other_
| _way_ _around_!
http://www.ozlabs.org/~dgibson
^ permalink raw reply
* How to creat a .config file for a driver and How to use it in a device driver?????
From: Misbah khan @ 2008-01-02 10:54 UTC (permalink / raw)
To: linuxppc-dev
Hi all ...
i am writing a device driver in which the default configuration for LCD
display i need to keep it in a .config file so that as the system boots it
take the value from this file and does the initial configuration in the init
of the driver. If user changes the .config file content in the next boot the
LCD driver should take the new default configuration
There will also be a run time config which could be changed at the rum time
but will not change the default configuration.
i am writing a LCD driver for PPC arch i need to know that how should i
creat a .config file and How should i use it my device driver. ??????
If you had an exprience in this regard please do share with me ...
----Misbah <><
--
View this message in context: http://www.nabble.com/How-to-creat-a-.config-file-for-a-driver-and-How-to-use-it-in-a-device-driver------tp14576412p14576412.html
Sent from the linuxppc-dev mailing list archive at Nabble.com.
^ permalink raw reply
* Re: How to do IO mapped Implimentation ???
From: Misbah khan @ 2008-01-02 9:18 UTC (permalink / raw)
To: linuxppc-embedded
In-Reply-To: <14522349.post@talk.nabble.com>
Till now i assume that Io mapped and memory mapped Implimentation are same
...the biggest doubt is How the two would remain the same ....If any of you
have worked on it Please share your experience with me ....
Misbah khan wrote:
>
> Hi all...
>
> I am writing a driver in which device port is mapped to CPLD and 8 bit
> data bus is directly connected from processor to CPLD. Read write on CPLD
> memory mapped (buffer/register) is required to control the device. This is
> now IO mapped to processor.
>
> I need to know whether i am right if i impliment like this :-
> addr=ioremap(base_addr); // Remap to Mem mapped address
> outb(addr) and inb(addr);
>
> Please suggest me if i am wrong or there could be better solution to this
> .
>
> -----Misbah <><
>
>
--
View this message in context: http://www.nabble.com/How-to-do-IO-mapped-Implimentation-----tp14522349p14575369.html
Sent from the linuxppc-embedded mailing list archive at Nabble.com.
^ permalink raw reply
* MTD - partioning
From: Sekhar Pedamallu @ 2008-01-02 7:33 UTC (permalink / raw)
To: linuxppc-embedded
[-- Attachment #1: Type: text/plain, Size: 1444 bytes --]
Hi all
i have 2 flash devices total of 16MB each of size 8MB and i need to
make partitions on first chip requirements as such:
256 KB - u-boot
2 MB - kernel
And remaining 13.5 MB for root file system and user space application
running on it.
The partition information is defined for each chip separately . I have seen
the some of the drivers in drivres/mtd/maps directory. Now how do I make
use of 6MB of first chips and 8MB from second chip to been seen as a single
partition??
If someone who have faced this problem or worked on this plea let me know
Thanks & Regards ,
Sekhar Pedamallu
http://www.patni.com
World-Wide Partnerships. World-Class Solutions.
_____________________________________________________________________
This e-mail message may contain proprietary, confidential or legally
privileged information for the sole use of the person or entity to
whom this message was originally addressed. Any review, e-transmission
dissemination or other use of or taking of any action in reliance upon
this information by persons or entities other than the intended
recipient is prohibited. If you have received this e-mail in error
kindly delete this e-mail from your records. If it appears that this
mail has been forwarded to you without proper authority, please notify
us immediately at netadmin@patni.com and delete this mail.
_____________________________________________________________________
[-- Attachment #2: Type: text/html, Size: 4411 bytes --]
^ permalink raw reply
* [PATCH v3 4/4] USB: add Cypress c67x00 OTG controller driver to Kconfig and Makefiles
From: Grant Likely @ 2008-01-02 6:21 UTC (permalink / raw)
To: linux-usb-devel, dbrownell, gregkh, linuxppc-dev, jacmet, stern
In-Reply-To: <20080102062119.15073.22456.stgit@trillian.secretlab.ca>
From: Grant Likely <grant.likely@secretlab.ca>
add c67x00 driver to build
Signed-off-by: Grant Likely <grant.likely@secretlab.ca>
---
drivers/usb/Makefile | 2 ++
drivers/usb/c67x00/Makefile | 11 +++++++++++
drivers/usb/host/Kconfig | 12 ++++++++++++
3 files changed, 25 insertions(+), 0 deletions(-)
diff --git a/drivers/usb/Makefile b/drivers/usb/Makefile
index 516a640..a419c42 100644
--- a/drivers/usb/Makefile
+++ b/drivers/usb/Makefile
@@ -17,6 +17,8 @@ obj-$(CONFIG_USB_SL811_HCD) += host/
obj-$(CONFIG_USB_U132_HCD) += host/
obj-$(CONFIG_USB_R8A66597_HCD) += host/
+obj-$(CONFIG_USB_C67X00_HCD) += c67x00/
+
obj-$(CONFIG_USB_ACM) += class/
obj-$(CONFIG_USB_PRINTER) += class/
diff --git a/drivers/usb/c67x00/Makefile b/drivers/usb/c67x00/Makefile
new file mode 100644
index 0000000..7e6eb0b
--- /dev/null
+++ b/drivers/usb/c67x00/Makefile
@@ -0,0 +1,11 @@
+#
+# Makefile for Cypress C67X00 USB Controller
+#
+
+ifeq ($(CONFIG_USB_DEBUG),y)
+ EXTRA_CFLAGS += -DDEBUG
+endif
+
+obj-$(CONFIG_USB_C67X00_HCD) += c67x00.o
+
+c67x00-objs := c67x00-drv.o c67x00-ll-hpi.o c67x00-hcd.o c67x00-sched.o
diff --git a/drivers/usb/host/Kconfig b/drivers/usb/host/Kconfig
index 49a91c5..49521d1 100644
--- a/drivers/usb/host/Kconfig
+++ b/drivers/usb/host/Kconfig
@@ -261,3 +261,15 @@ config USB_R8A66597_HCD
To compile this driver as a module, choose M here: the
module will be called r8a66597-hcd.
+config USB_C67X00_HCD
+ tristate "Cypress C67x00 HCD support"
+ depends on USB
+ help
+ The Cypress C67x00 (EZ-Host/EZ-OTG) chips are dual-role
+ host/peripheral/OTG USB controllers.
+
+ Enable this option to support this chip in host controller mode.
+ If unsure, say N.
+
+ To compile this driver as a module, choose M here: the
+ module will be called c67x00.
^ permalink raw reply related
* [PATCH v3 3/4] USB: add Cypress c67x00 OTG controller HCD driver
From: Grant Likely @ 2008-01-02 6:21 UTC (permalink / raw)
To: linux-usb-devel, dbrownell, gregkh, linuxppc-dev, jacmet, stern
In-Reply-To: <20080102062119.15073.22456.stgit@trillian.secretlab.ca>
From: Grant Likely <grant.likely@secretlab.ca>
This patch adds HDC support for the Cypress c67x00 family of devices.
Signed-off-by: Grant Likely <grant.likely@secretlab.ca>
---
drivers/usb/c67x00/c67x00-hcd.c | 391 ++++++++++++
drivers/usb/c67x00/c67x00-hcd.h | 137 ++++
drivers/usb/c67x00/c67x00-sched.c | 1203 +++++++++++++++++++++++++++++++++++++
3 files changed, 1731 insertions(+), 0 deletions(-)
diff --git a/drivers/usb/c67x00/c67x00-hcd.c b/drivers/usb/c67x00/c67x00-hcd.c
new file mode 100644
index 0000000..3d0b77e
--- /dev/null
+++ b/drivers/usb/c67x00/c67x00-hcd.c
@@ -0,0 +1,391 @@
+/*
+ * c67x00-hcd.c: Cypress C67X00 USB Host Controller Driver
+ *
+ * Copyright (C) 2006-2007 Barco N.V.
+ * Derived from the Cypress cy7c67200/300 ezusb linux driver and
+ * based on multiple host controller drivers inside the linux kernel.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * 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 for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+ * MA 02110-1301 USA.
+ */
+
+#include <linux/device.h>
+#include <linux/platform_device.h>
+#include <linux/usb.h>
+
+#include "c67x00.h"
+#include "c67x00-hcd.h"
+
+/* --------------------------------------------------------------------------
+ * Root Hub Support
+ */
+
+static __u8 c67x00_hub_des[] = {
+ 0x09, /* __u8 bLength; */
+ 0x29, /* __u8 bDescriptorType; Hub-descriptor */
+ 0x02, /* __u8 bNbrPorts; */
+ 0x00, /* __u16 wHubCharacteristics; */
+ 0x00, /* (per-port OC, no power switching) */
+ 0x32, /* __u8 bPwrOn2pwrGood; 2ms */
+ 0x00, /* __u8 bHubContrCurrent; 0 mA */
+ 0x00, /* __u8 DeviceRemovable; ** 7 Ports max ** */
+ 0xff, /* __u8 PortPwrCtrlMask; ** 7 ports max ** */
+};
+
+static void c67x00_hub_reset_host_port(struct c67x00_sie *sie, int port)
+{
+ struct c67x00_hcd *c67x00 = sie->private_data;
+ unsigned long flags;
+
+ c67x00_ll_husb_reset(sie, port);
+
+ spin_lock_irqsave(&c67x00->lock, flags);
+ c67x00_ll_husb_reset_port(sie, port);
+ spin_unlock_irqrestore(&c67x00->lock, flags);
+
+ c67x00_ll_set_husb_eot(sie->dev, DEFAULT_EOT);
+}
+
+static int c67x00_hub_status_data(struct usb_hcd *hcd, char *buf)
+{
+ struct c67x00_hcd *c67x00 = hcd_to_c67x00_hcd(hcd);
+ struct c67x00_sie *sie = c67x00->sie;
+ u16 status;
+ int i;
+
+ *buf = 0;
+ status = c67x00_ll_husb_get_status(sie);
+ for (i = 0; i < C67X00_PORTS; i++)
+ if (status & PORT_CONNECT_CHANGE(i))
+ *buf |= (1 << i);
+
+ /* bit 0 denotes hub change, b1..n port change */
+ *buf <<= 1;
+
+ return !!*buf;
+}
+
+static int c67x00_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,
+ u16 wIndex, char *buf, u16 wLength)
+{
+ struct c67x00_hcd *c67x00 = hcd_to_c67x00_hcd(hcd);
+ struct c67x00_sie *sie = c67x00->sie;
+ u16 status, usb_status;
+ int len = 0;
+ unsigned int port = wIndex-1;
+ u16 wPortChange, wPortStatus;
+
+ switch (typeReq) {
+
+ case GetHubStatus:
+ *(__le32 *) buf = cpu_to_le32(0);
+ len = 4; /* hub power */
+ break;
+ case GetPortStatus:
+ if (wIndex > C67X00_PORTS)
+ return -EPIPE;
+
+ status = c67x00_ll_husb_get_status(sie);
+ usb_status = c67x00_ll_get_usb_ctl(sie);
+
+ wPortChange = 0;
+ if (status & PORT_CONNECT_CHANGE(port))
+ wPortChange |= USB_PORT_STAT_C_CONNECTION;
+
+ wPortStatus = USB_PORT_STAT_POWER;
+ if (!(status & PORT_SE0_STATUS(port)))
+ wPortStatus |= USB_PORT_STAT_CONNECTION;
+ if (usb_status & LOW_SPEED_PORT(port)) {
+ wPortStatus |= USB_PORT_STAT_LOW_SPEED;
+ c67x00->low_speed_ports |= (1 << port);
+ } else
+ c67x00->low_speed_ports &= ~(1 << port);
+
+ if (usb_status & SOF_EOP_EN(port))
+ wPortStatus |= USB_PORT_STAT_ENABLE;
+
+ *(__le16 *) buf = cpu_to_le16(wPortStatus);
+ *(__le16 *) (buf + 2) = cpu_to_le16(wPortChange);
+ len = 4;
+ break;
+ case SetHubFeature: /* We don't implement these */
+ case ClearHubFeature:
+ switch (wValue) {
+ case C_HUB_OVER_CURRENT:
+ case C_HUB_LOCAL_POWER:
+ len = 0;
+ break;
+ default:
+ return -EPIPE;
+ }
+ break;
+ case SetPortFeature:
+ if (wIndex > C67X00_PORTS)
+ return -EPIPE;
+
+ switch (wValue) {
+ case USB_PORT_FEAT_SUSPEND:
+ dev_dbg(c67x00_hcd_dev(c67x00),
+ "SetPortFeature %d (SUSPEND)\n", port);
+ len = 0;
+ break;
+ case USB_PORT_FEAT_RESET:
+ c67x00_hub_reset_host_port(sie, port);
+ len = 0;
+ break;
+ case USB_PORT_FEAT_POWER:
+ /* Power always enabled */
+ len = 0;
+ break;
+ default:
+ dev_dbg(c67x00_hcd_dev(c67x00),
+ "%s: SetPortFeature %d (0x%04x) Error!\n",
+ __FUNCTION__, port, wValue);
+ return -EPIPE;
+ }
+ break;
+ case ClearPortFeature:
+ if (wIndex > C67X00_PORTS)
+ return -EPIPE;
+
+ switch (wValue) {
+ case USB_PORT_FEAT_ENABLE:
+ /* Reset the port so that the c67x00 also notices the
+ * disconnect */
+ c67x00_hub_reset_host_port(sie, port);
+ len = 0;
+ break;
+ case USB_PORT_FEAT_C_ENABLE:
+ dev_dbg(c67x00_hcd_dev(c67x00),
+ "ClearPortFeature (%d): C_ENABLE\n", port);
+ len = 0;
+ break;
+ case USB_PORT_FEAT_SUSPEND:
+ dev_dbg(c67x00_hcd_dev(c67x00),
+ "ClearPortFeature (%d): SUSPEND\n", port);
+ len = 0;
+ break;
+ case USB_PORT_FEAT_C_SUSPEND:
+ dev_dbg(c67x00_hcd_dev(c67x00),
+ "ClearPortFeature (%d): C_SUSPEND\n", port);
+ len = 0;
+ break;
+ case USB_PORT_FEAT_POWER:
+ dev_dbg(c67x00_hcd_dev(c67x00),
+ "ClearPortFeature (%d): POWER\n", port);
+ return -EPIPE;
+ case USB_PORT_FEAT_C_CONNECTION:
+ c67x00_ll_husb_clear_status(sie,
+ PORT_CONNECT_CHANGE(port));
+ len = 0;
+ break;
+ case USB_PORT_FEAT_C_OVER_CURRENT:
+ dev_dbg(c67x00_hcd_dev(c67x00),
+ "ClearPortFeature (%d): OVER_CURRENT\n", port);
+ len = 0;
+ break;
+ case USB_PORT_FEAT_C_RESET:
+ dev_dbg(c67x00_hcd_dev(c67x00),
+ "ClearPortFeature (%d): C_RESET\n", port);
+ len = 0;
+ break;
+ default:
+ dev_dbg(c67x00_hcd_dev(c67x00),
+ "%s: ClearPortFeature %d (0x%04x) Error!\n",
+ __FUNCTION__, port, wValue);
+ return -EPIPE;
+ }
+ break;
+ case GetHubDescriptor:
+ len = min_t(unsigned int, sizeof(c67x00_hub_des), wLength);
+ memcpy(buf, c67x00_hub_des, len);
+ break;
+ default:
+ dev_dbg(c67x00_hcd_dev(c67x00), "%s: unknown\n", __FUNCTION__);
+ }
+
+ return 0;
+}
+
+/* ---------------------------------------------------------------------
+ * Main part of host controller driver
+ */
+
+/**
+ * c67x00_hcd_irq
+ *
+ * This function is called from the interrupt handler in c67x00-drv.c
+ */
+static void c67x00_hcd_irq(struct c67x00_sie *sie, u16 msg)
+{
+ struct c67x00_device *c67x00 = sie->dev;
+ struct c67x00_hcd *c67x00_hcd = sie->private_data;
+ struct usb_hcd *hcd = c67x00_hcd_to_hcd(c67x00_hcd);
+
+ /* Handle sie message flags */
+ if (msg) {
+ if (msg & HUSB_TDListDone)
+ c67x00_sched_kick(c67x00_hcd);
+ else
+ dev_warn(c67x00_hcd_dev(c67x00_hcd),
+ "Unknown SIE msg flag(s): 0x%04x\n", msg);
+ }
+
+ if (unlikely(hcd->state == HC_STATE_HALT))
+ return;
+
+ if (!test_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags))
+ return;
+
+ /* Handle Start of frame events */
+ if (sie->dev->int_status & SOFEOP_FLG(sie->sie_num)) {
+ c67x00_ll_husb_clear_status(sie, SOF_EOP_IRQ_FLG);
+ c67x00_sched_kick(c67x00_hcd);
+ set_bit(HCD_FLAG_SAW_IRQ, &hcd->flags);
+ }
+}
+
+/**
+ * c67x00_hcd_start: Host Controller start hook
+ */
+static int c67x00_hcd_start(struct usb_hcd *hcd)
+{
+ hcd->uses_new_polling = 1;
+ hcd->state = HC_STATE_RUNNING;
+ hcd->poll_rh = 1;
+ return 0;
+}
+
+/**
+ * c67x00_hcd_start: Host Controller stop hook
+ */
+static void c67x00_hcd_stop(struct usb_hcd *hcd)
+{
+ /* Nothing todo */
+}
+
+static int c67x00_hcd_get_frame(struct usb_hcd *hcd)
+{
+ struct c67x00_hcd *c67x00 = hcd_to_c67x00_hcd(hcd);
+ u16 temp_val;
+
+ dev_dbg(c67x00_hcd_dev(c67x00), "%s\n", __FUNCTION__);
+ temp_val = c67x00_ll_husb_get_frame(c67x00->sie);
+ temp_val &= HOST_FRAME_MASK;
+ return temp_val ? (temp_val - 1) : HOST_FRAME_MASK;
+}
+
+static struct hc_driver c67x00_hc_driver = {
+ .description = "c67x00-hcd",
+ .product_desc = "Cypress C67X00 Host Controller",
+ .hcd_priv_size = sizeof(struct c67x00_hcd),
+ .flags = HCD_USB11 | HCD_MEMORY,
+
+ /*
+ * basic lifecycle operations
+ */
+ .start = c67x00_hcd_start,
+ .stop = c67x00_hcd_stop,
+
+ /*
+ * managing i/o requests and associated device resources
+ */
+ .urb_enqueue = c67x00_urb_enqueue,
+ .urb_dequeue = c67x00_urb_dequeue,
+ .endpoint_disable = c67x00_endpoint_disable,
+
+ /*
+ * scheduling support
+ */
+ .get_frame_number = c67x00_hcd_get_frame,
+
+ /*
+ * root hub support
+ */
+ .hub_status_data = c67x00_hub_status_data,
+ .hub_control = c67x00_hub_control,
+};
+
+/* ---------------------------------------------------------------------
+ * Setup/Teardown routines
+ */
+
+int c67x00_hcd_probe(struct c67x00_sie *sie)
+{
+ int retval;
+ struct usb_hcd *hcd = NULL;
+ struct c67x00_hcd *c67x00;
+
+ hcd = usb_create_hcd(&c67x00_hc_driver, sie_dev(sie), "c67x00_sie");
+ if (!hcd) {
+ retval = -ENOMEM;
+ goto err0;
+ }
+ c67x00 = hcd_to_c67x00_hcd(hcd);
+
+ spin_lock_init(&c67x00->lock);
+ c67x00->sie = sie;
+
+ INIT_LIST_HEAD(&c67x00->list[PIPE_ISOCHRONOUS]);
+ INIT_LIST_HEAD(&c67x00->list[PIPE_INTERRUPT]);
+ INIT_LIST_HEAD(&c67x00->list[PIPE_CONTROL]);
+ INIT_LIST_HEAD(&c67x00->list[PIPE_BULK]);
+ c67x00->urb_count = 0;
+ INIT_LIST_HEAD(&c67x00->td_list);
+ INIT_LIST_HEAD(&c67x00->done_list);
+ c67x00->td_base_addr = CY_HCD_BUF_ADDR + SIE_TD_OFFSET(sie->sie_num);
+ c67x00->buf_base_addr = CY_HCD_BUF_ADDR + SIE_BUF_OFFSET(sie->sie_num);
+ c67x00->max_frame_bw = MAX_FRAME_BW_STD;
+
+ spin_lock(&sie->lock);
+ sie->private_data = c67x00;
+ sie->irq = c67x00_hcd_irq;
+ spin_unlock(&sie->lock);
+
+ c67x00_ll_husb_init_host_port(sie);
+
+ init_completion(&c67x00->endpoint_disable);
+ retval = c67x00_sched_start_scheduler(c67x00);
+ if (retval)
+ goto err1;
+
+ retval = usb_add_hcd(hcd, 0, 0);
+ if (retval) {
+ dev_dbg(sie_dev(sie), "%s: usb_add_hcd returned %d\n",
+ __FUNCTION__, retval);
+ goto err2;
+ }
+
+ return retval;
+ err2:
+ c67x00_sched_stop_scheduler(c67x00);
+ err1:
+ usb_put_hcd(hcd);
+ err0:
+ return retval;
+}
+
+/* may be called with controller, bus, and devices active */
+void c67x00_hcd_remove(struct c67x00_sie *sie)
+{
+ struct usb_hcd *hcd;
+ struct c67x00_hcd *c67x00;
+
+ c67x00 = sie->private_data;
+ hcd = c67x00_hcd_to_hcd(c67x00);
+ c67x00_sched_stop_scheduler(c67x00);
+ usb_remove_hcd(hcd);
+ usb_put_hcd(hcd);
+}
diff --git a/drivers/usb/c67x00/c67x00-hcd.h b/drivers/usb/c67x00/c67x00-hcd.h
new file mode 100644
index 0000000..5b35f01
--- /dev/null
+++ b/drivers/usb/c67x00/c67x00-hcd.h
@@ -0,0 +1,137 @@
+/*
+ * c67x00-hcd.h: Cypress C67X00 USB HCD
+ *
+ * Copyright (C) 2006-2007 Barco N.V.
+ * Derived from the Cypress cy7c67200/300 ezusb linux driver and
+ * based on multiple host controller drivers inside the linux kernel.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * 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 for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+ * MA 02110-1301 USA.
+ */
+
+#ifndef _USB_C67X00_HCD_H
+#define _USB_C67X00_HCD_H
+
+#include <linux/kernel.h>
+#include <linux/spinlock.h>
+#include <linux/list.h>
+#include <linux/usb.h>
+#include "../core/hcd.h"
+#include "c67x00.h"
+
+/*
+ * The following parameters depend on the CPU speed, bus speed, ...
+ * These can be tuned for specific use cases, e.g. if isochronous transfers
+ * are very important, bandwith can be sacrificed to guarantee that the
+ * 1ms deadline will be met.
+ * If bulk transfers are important, the MAX_FRAME_BW can be increased,
+ * but some (or many) isochronous deadlines might not be met.
+ *
+ * The values are specified in bittime.
+ */
+
+/*
+ * The current implementation switches between _STD (default) and _ISO (when
+ * isochronous transfers are scheduled), in order to optimize the throughput
+ * in normal cicrumstances, but also provide good isochronous behaviour.
+ *
+ * Bandwidth is described in bit time so with a 12MHz USB clock and 1ms
+ * frames; there are 12000 bit times per frame.
+ */
+
+#define TOTAL_FRAME_BW 12000
+#define DEFAULT_EOT 2250
+
+#define MAX_FRAME_BW_STD (TOTAL_FRAME_BW - DEFAULT_EOT)
+#define MAX_FRAME_BW_ISO 2400
+
+/*
+ * Periodic transfers may only use 90% of the full frame, but as
+ * we currently don't even use 90% of the full frame, we may
+ * use the full usable time for periodic transfers.
+ */
+#define MAX_PERIODIC_BW(full_bw) full_bw
+
+/* -------------------------------------------------------------------------- */
+
+struct c67x00_hcd {
+ spinlock_t lock;
+ struct c67x00_sie *sie;
+ /* Requirement:
+ * All enabled bits in ports must have a position <= MAX_NB_HCD_PORTS
+ * (position starts counting from 1!)
+ * ( ports & ~((1<<MAX_NB_HCD_PORTS)-1) ) == 0
+ *
+ * This might be relaxed if needed by using an other indexing scheme
+ * for port[] (e.g. use wIndex instead of real port number)
+ * */
+#define MAX_NB_HCD_PORTS 2
+ unsigned int low_speed_ports; /* bitmask of low speed ports */
+ unsigned int urb_count;
+ unsigned int urb_iso_count;
+
+ struct list_head list[4]; /* iso, int, ctrl, bulk */
+#if PIPE_BULK != 3
+#error "Sanity check failed, this code presumes PIPE_... to range from 0 to 3"
+#endif
+
+ /* USB bandwidth allocated to td_list */
+ int bandwidth_allocated;
+ /* USB bandwidth allocated for isoc/int transfer */
+ int periodic_bw_allocated;
+ struct list_head td_list;
+ struct list_head done_list;
+ int max_frame_bw;
+
+ u16 td_base_addr;
+ u16 buf_base_addr;
+ u16 next_td_addr;
+ u16 next_buf_addr;
+
+ struct tasklet_struct tasklet;
+ struct tasklet_struct done_tasklet;
+
+ struct completion endpoint_disable;
+
+ u16 current_frame;
+ u16 last_frame;
+};
+
+static inline struct c67x00_hcd *hcd_to_c67x00_hcd(struct usb_hcd *hcd)
+{
+ return (struct c67x00_hcd *)(hcd->hcd_priv);
+}
+
+static inline struct usb_hcd *c67x00_hcd_to_hcd(struct c67x00_hcd *c67x00)
+{
+ return container_of((void *)c67x00, struct usb_hcd, hcd_priv);
+}
+
+/* ---------------------------------------------------------------------
+ * Transfer Descriptor scheduling functions
+ */
+int c67x00_urb_enqueue(struct usb_hcd *hcd, struct urb *urb, gfp_t mem_flags);
+int c67x00_urb_dequeue(struct usb_hcd *hcd, struct urb *urb, int status);
+void c67x00_endpoint_disable(struct usb_hcd *hcd,
+ struct usb_host_endpoint *ep);
+
+void c67x00_hcd_msg_received(struct c67x00_sie *sie, u16 msg);
+void c67x00_sched_kick(struct c67x00_hcd *c67x00);
+int c67x00_sched_start_scheduler(struct c67x00_hcd *c67x00);
+void c67x00_sched_stop_scheduler(struct c67x00_hcd *c67x00);
+
+#define c67x00_hcd_dev(x) (c67x00_hcd_to_hcd(x)->self.controller)
+
+#endif /* _USB_C67X00_HCD_H */
diff --git a/drivers/usb/c67x00/c67x00-sched.c b/drivers/usb/c67x00/c67x00-sched.c
new file mode 100644
index 0000000..35d7318
--- /dev/null
+++ b/drivers/usb/c67x00/c67x00-sched.c
@@ -0,0 +1,1203 @@
+/*
+ * c67x00-sched.c: Cypress C67X00 USB Host Controller Driver - TD scheduling
+ *
+ * Copyright (C) 2006-2007 Barco N.V.
+ * Derived from the Cypress cy7c67200/300 ezusb linux driver and
+ * based on multiple host controller drivers inside the linux kernel.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * 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 for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+ * MA 02110-1301 USA.
+ */
+
+#include <linux/kthread.h>
+
+#include "c67x00.h"
+#include "c67x00-hcd.h"
+
+/*
+ * These are the stages for a control urb, they are kept
+ * in both urb->interval and td->privdata.
+ */
+#define SETUP_STAGE 0
+#define DATA_STAGE 1
+#define STATUS_STAGE 2
+
+/* -------------------------------------------------------------------------- */
+
+/**
+ * struct c67x00_ep_data: Host endpoint data structure
+ */
+struct c67x00_ep_data {
+ struct list_head queue;
+ struct list_head node;
+ struct usb_host_endpoint *hep;
+ struct usb_device *dev;
+ u16 next_frame; /* For int/isoc transactions */
+};
+
+/**
+ * struct c67x00_td
+ *
+ * Hardware parts are little endiannes, SW in CPU endianess.
+ */
+struct c67x00_td {
+ /* HW specific part */
+ __le16 ly_base_addr; /* Bytes 0-1 */
+ __le16 port_length; /* Bytes 2-3 */
+ u8 pid_ep; /* Byte 4 */
+ u8 dev_addr; /* Byte 5 */
+ u8 ctrl_reg; /* Byte 6 */
+ u8 status; /* Byte 7 */
+ u8 retry_cnt; /* Byte 8 */
+#define TT_OFFSET 2
+#define TT_CONTROL 0
+#define TT_ISOCHRONOUS 1
+#define TT_BULK 2
+#define TT_INTERRUPT 3
+ u8 residue; /* Byte 9 */
+ __le16 next_td_addr; /* Bytes 10-11 */
+ /* SW part */
+ struct list_head td_list;
+ u16 td_addr;
+ char *data;
+ struct urb *urb;
+ unsigned long privdata;
+
+ /* These are needed for handling the toggle bits:
+ * an urb can be dequeued while a td is in progress
+ * after checking the td, the toggle bit might need to
+ * be fixed */
+ struct c67x00_ep_data *ep_data;
+ unsigned int pipe;
+};
+
+struct c67x00_urb_priv {
+ struct list_head hep_node;
+ struct urb *urb;
+ int port;
+ int cnt; /* packet number for isoc */
+ int status;
+ struct c67x00_ep_data *ep_data;
+};
+
+#define td_udev(td) ((td)->ep_data->dev)
+
+#define CY_TD_SIZE 12
+
+#define TD_PIDEP_OFFSET 0x04
+#define TD_PIDEPMASK_PID 0xF0
+#define TD_PIDEPMASK_EP 0x0F
+#define TD_PORTLENMASK_DL 0x02FF
+#define TD_PORTLENMASK_PN 0xC000
+
+#define TD_STATUS_OFFSET 0x07
+#define TD_STATUSMASK_ACK 0x01
+#define TD_STATUSMASK_ERR 0x02
+#define TD_STATUSMASK_TMOUT 0x04
+#define TD_STATUSMASK_SEQ 0x08
+#define TD_STATUSMASK_SETUP 0x10
+#define TD_STATUSMASK_OVF 0x20
+#define TD_STATUSMASK_NAK 0x40
+#define TD_STATUSMASK_STALL 0x80
+
+#define TD_ERROR_MASK (TD_STATUSMASK_ERR | TD_STATUSMASK_TMOUT | \
+ TD_STATUSMASK_STALL )
+
+#define TD_RETRYCNT_OFFSET 0x08
+#define TD_RETRYCNTMASK_ACT_FLG 0x10
+#define TD_RETRYCNTMASK_TX_TYPE 0x0C
+#define TD_RETRYCNTMASK_RTY_CNT 0x03
+
+#define TD_RESIDUE_OVERFLOW 0x80
+
+#define TD_PID_IN 0x90
+
+/* Residue: signed 8bits, neg -> OVERFLOW, pos -> UNDERFLOW */
+#define td_residue(td) ((__s8)(td->residue))
+#define td_ly_base_addr(td) (__le16_to_cpu((td)->ly_base_addr))
+#define td_port_length(td) (__le16_to_cpu((td)->port_length))
+#define td_next_td_addr(td) (__le16_to_cpu((td)->next_td_addr))
+
+#define td_active(td) ((td)->retry_cnt & TD_RETRYCNTMASK_ACT_FLG)
+#define td_length(td) (td_port_length(td) & TD_PORTLENMASK_DL)
+
+#define td_sequence_ok(td) (!td->status || \
+ ( !(td->status & TD_STATUSMASK_SEQ) == \
+ !(td->ctrl_reg & SEQ_SEL) ))
+
+#define td_acked(td) (!td->status || \
+ (td->status & TD_STATUSMASK_ACK))
+#define td_actual_bytes(td) (td_length(td) - td_residue(td))
+
+/* -------------------------------------------------------------------------- */
+
+#ifdef DEBUG
+/*
+ * These patterns are written into the c67x00 internal memory and the
+ * urb->transfer_buffer respectively in order to simplify debugging.
+ */
+#define NON_RECEIVED_PATTERN 0xac
+#define UNREAD_PATTERN 0x0c
+/* #define DEBUG_PATTERN */
+
+/**
+ * dbg_td - Dump the contents of the TD
+ */
+static void dbg_td(struct c67x00_hcd *c67x00, struct c67x00_td *td, char *msg)
+{
+ struct device *dev = c67x00_hcd_dev(c67x00);
+ int i, len = td_length(td);
+ dev_dbg(dev, "### %s at 0x%04x\n", msg, td->td_addr);
+ dev_dbg(dev, "urb: 0x%p\n", td->urb);
+ dev_dbg(dev, "endpoint: %4d\n", usb_pipeendpoint(td->pipe));
+ dev_dbg(dev, "pipeout: %4d\n", usb_pipeout(td->pipe));
+ dev_dbg(dev, "ly_base_addr: 0x%04x\n", td_ly_base_addr(td));
+ dev_dbg(dev, "port_length: 0x%04x\n", td_port_length(td));
+ dev_dbg(dev, "pid_ep: 0x%02x\n", td->pid_ep);
+ dev_dbg(dev, "dev_addr: 0x%02x\n", td->dev_addr);
+ dev_dbg(dev, "ctrl_reg: 0x%02x\n", td->ctrl_reg);
+ dev_dbg(dev, "status: 0x%02x\n", td->status);
+ dev_dbg(dev, "retry_cnt: 0x%02x\n", td->retry_cnt);
+ dev_dbg(dev, "residue: 0x%02x\n", td->residue);
+ dev_dbg(dev, "next_td_addr: 0x%04x\n", td_next_td_addr(td));
+ dev_dbg(dev, "data:");
+ for (i = 0; i < len; i++) {
+ if (!(i % 8))
+ printk("\n ");
+ printk(" 0x%02x", td->data[i]);
+ }
+ printk("\n");
+}
+#else /* DEBUG */
+
+static inline void
+dbg_td(struct c67x00_hcd *c67x00, struct c67x00_td *td, char *msg) { }
+
+#endif /* DEBUG */
+
+/* -------------------------------------------------------------------------- */
+/* Helper functions */
+
+/* -------------------------------------------------------------------------- */
+
+static inline u16 c67x00_get_current_frame_number(struct c67x00_hcd *c67x00)
+{
+ u16 temp_val;
+ temp_val = c67x00_ll_husb_get_frame(c67x00->sie);
+ temp_val &= HOST_FRAME_MASK;
+ return temp_val;
+}
+
+/**
+ * frame_add
+ * Software wraparound for framenumbers.
+ */
+static inline u16 frame_add(u16 a, u16 b)
+{
+ return (a + b) & HOST_FRAME_MASK;
+}
+
+/**
+ * frame_after - is frame a after frame b
+ */
+static inline int frame_after(u16 a, u16 b)
+{
+ return ((HOST_FRAME_MASK + a - b) & HOST_FRAME_MASK) <
+ (HOST_FRAME_MASK / 2);
+}
+
+/**
+ * frame_after_eq - is frame a after or equal to frame b
+ */
+static inline int frame_after_eq(u16 a, u16 b)
+{
+ return ((HOST_FRAME_MASK + 1 + a - b) & HOST_FRAME_MASK) <
+ (HOST_FRAME_MASK / 2);
+}
+
+/* -------------------------------------------------------------------------- */
+
+/**
+ * c67x00_release_urb - remove link from all tds to this urb
+ * Disconnects the urb from it's tds, so that it can be given back.
+ * pre: urb->hcpriv != NULL
+ */
+static void c67x00_release_urb(struct c67x00_hcd *c67x00, struct urb *urb)
+{
+ struct c67x00_td *td;
+ struct c67x00_urb_priv *urbp;
+
+ BUG_ON(!urb);
+
+ c67x00->urb_count--;
+
+ if (usb_pipetype(urb->pipe) == PIPE_ISOCHRONOUS) {
+ c67x00->urb_iso_count--;
+ if (c67x00->urb_iso_count == 0)
+ c67x00->max_frame_bw = MAX_FRAME_BW_STD;
+ }
+
+ /* TODO this might be not so efficient when we've got many urbs!
+ * Alternatives:
+ * * only clear when needed
+ * * keep a list of tds with earch urbp
+ */
+ list_for_each_entry(td, &c67x00->td_list, td_list) {
+ if (urb == td->urb)
+ td->urb = NULL;
+ }
+
+ /* Discard the urb private data */
+ urbp = urb->hcpriv;
+ urb->hcpriv = NULL;
+ list_del(&urbp->hep_node);
+ kfree(urbp);
+}
+
+/* -------------------------------------------------------------------------- */
+
+static struct c67x00_ep_data *
+c67x00_ep_data_alloc(struct c67x00_hcd *c67x00, struct urb *urb)
+{
+ //struct usb_device *udev = urb->dev;
+ struct usb_host_endpoint *hep = urb->ep;
+ struct c67x00_ep_data *ep_data;
+ struct c67x00_ep_data *prev;
+ int type;
+
+ c67x00->current_frame = c67x00_get_current_frame_number(c67x00);
+
+ /* Check if endpoint already has a c67x00_ep_data struct allocated */
+ if (hep->hcpriv) {
+ ep_data = hep->hcpriv;
+ if (frame_after(c67x00->current_frame, ep_data->next_frame))
+ ep_data->next_frame =
+ frame_add(c67x00->current_frame, 1);
+ return hep->hcpriv;
+ }
+
+ /* Allocate and initialize a new c67x00 endpoint data structure */
+ ep_data = kzalloc(sizeof(*ep_data), GFP_ATOMIC);
+ if (!ep_data)
+ return NULL;
+
+ INIT_LIST_HEAD(&ep_data->queue);
+ INIT_LIST_HEAD(&ep_data->node);
+ ep_data->hep = hep;
+
+ /* hold a reference to udev as long as this endpoint lives,
+ * this is needed to possibly fix the data toggle */
+ ep_data->dev = usb_get_dev(urb->dev);
+ hep->hcpriv = ep_data;
+
+ /* For ISOC and INT endpoints, start ASAP: */
+ ep_data->next_frame = frame_add(c67x00->current_frame, 1);
+
+ /* Add the endpoint data to one of the pipe lists; must be added
+ * in order of endpoint address */
+ type = usb_pipetype(urb->pipe);
+ if (list_empty(&ep_data->node)) {
+ list_add(&ep_data->node, &c67x00->list[type]);
+ } else {
+ list_for_each_entry(prev, &c67x00->list[type], node) {
+ if (prev->hep->desc.bEndpointAddress >
+ hep->desc.bEndpointAddress) {
+ list_add(&ep_data->node, prev->node.prev);
+ break;
+ }
+ }
+ }
+
+ return ep_data;
+}
+
+static int c67x00_ep_data_free(struct usb_host_endpoint *hep)
+{
+ struct c67x00_ep_data *ep_data = hep->hcpriv;
+ if (!ep_data)
+ return 0;
+
+ if (!list_empty(&ep_data->queue))
+ return -EBUSY;
+
+ usb_put_dev(ep_data->dev);
+ list_del(&ep_data->queue);
+ list_del(&ep_data->node);
+
+ kfree(ep_data);
+ hep->hcpriv = NULL;
+
+ return 0;
+}
+
+void c67x00_endpoint_disable(struct usb_hcd *hcd, struct usb_host_endpoint *ep)
+{
+ struct c67x00_hcd *c67x00 = hcd_to_c67x00_hcd(hcd);
+ unsigned long flags;
+
+ if (!list_empty(&ep->urb_list))
+ dev_warn(c67x00_hcd_dev(c67x00), "error: urb list not empty\n");
+
+ spin_lock_irqsave(&c67x00->lock, flags);
+
+ /* Loop waiting for all transfers in the endpoint queue to complete */
+ while (c67x00_ep_data_free(ep)) {
+ /* Drop the lock so we can sleep waiting for the hardware */
+ spin_unlock_irqrestore(&c67x00->lock, flags);
+
+ /* it could happen that we reinitialize this completion, while
+ * somebody was waiting for that completion. The timeout and
+ * while loop handle such cases, but this might be improved */
+ INIT_COMPLETION(c67x00->endpoint_disable);
+ c67x00_sched_kick(c67x00);
+ wait_for_completion_timeout(&c67x00->endpoint_disable, 1 * HZ);
+
+ spin_lock_irqsave(&c67x00->lock, flags);
+ }
+
+ spin_unlock_irqrestore(&c67x00->lock, flags);
+}
+
+/* -------------------------------------------------------------------------- */
+
+static inline int get_root_port(struct usb_device *dev)
+{
+ while (dev->parent->parent)
+ dev = dev->parent;
+ return dev->portnum;
+}
+
+int c67x00_urb_enqueue(struct usb_hcd *hcd,
+ struct urb *urb, gfp_t mem_flags)
+{
+ int ret;
+ unsigned long flags;
+ struct c67x00_urb_priv *urbp = NULL;
+ struct c67x00_hcd *c67x00 = hcd_to_c67x00_hcd(hcd);
+ int port = get_root_port(urb->dev)-1;
+
+ spin_lock_irqsave(&c67x00->lock, flags);
+
+ /* Make sure host controller is running */
+ if (!HC_IS_RUNNING(hcd->state)) {
+ ret = -ENODEV;
+ goto err_not_linked;
+ }
+
+ ret = usb_hcd_link_urb_to_ep(hcd, urb);
+ if (ret)
+ goto err_not_linked;
+
+ /* Allocate and initialize urb private data */
+ urbp = kzalloc(sizeof(*urbp), mem_flags);
+ if (!urbp) {
+ ret = -ENOMEM;
+ goto err_urbp;
+ }
+ INIT_LIST_HEAD(&urbp->hep_node);
+ urbp->urb = urb;
+ urbp->port = port;
+
+ urbp->ep_data = c67x00_ep_data_alloc(c67x00, urb);
+ if (!urbp->ep_data) {
+ ret = -ENOMEM;
+ goto err_epdata;
+ }
+
+ /* TODO claim bandwidth with usb_claim_bandwidth?
+ * also release it somewhere! */
+
+ urb->hcpriv = urbp;
+
+ urb->actual_length = 0; /* Nothing received/transmitted yet */
+
+ switch (usb_pipetype(urb->pipe)) {
+ case PIPE_CONTROL:
+ urb->interval = SETUP_STAGE;
+ break;
+ case PIPE_INTERRUPT:
+ break;
+ case PIPE_BULK:
+ break;
+ case PIPE_ISOCHRONOUS:
+ if (c67x00->urb_iso_count == 0)
+ c67x00->max_frame_bw = MAX_FRAME_BW_ISO;
+ c67x00->urb_iso_count++;
+ /* Assume always URB_ISO_ASAP, FIXME */
+ if (list_empty(&urbp->ep_data->queue))
+ urb->start_frame = urbp->ep_data->next_frame;
+ else {
+ /* Go right after the last one */
+ struct urb *last_urb;
+
+ last_urb = list_entry(urbp->ep_data->queue.prev,
+ struct c67x00_urb_priv, hep_node)->urb;
+ urb->start_frame =
+ frame_add(last_urb->start_frame,
+ last_urb->number_of_packets *
+ last_urb->interval);
+ }
+ urbp->cnt = 0;
+ break;
+ }
+
+ /* Add the URB to the endpoint queue */
+ list_add_tail(&urbp->hep_node, &urbp->ep_data->queue);
+
+ /* If this is the only urb, kick start the controller */
+ if (!c67x00->urb_count++)
+ c67x00_ll_hpi_enable_sofeop(c67x00->sie);
+
+ c67x00_sched_kick(c67x00);
+
+ spin_unlock_irqrestore(&c67x00->lock, flags);
+ return 0;
+
+ /* Something went wrong; unwind the allocations */
+ err_epdata:
+ kfree(urbp);
+ err_urbp:
+ usb_hcd_unlink_urb_from_ep(hcd, urb);
+ err_not_linked:
+ spin_unlock_irqrestore(&c67x00->lock, flags);
+ return ret;
+}
+
+int c67x00_urb_dequeue(struct usb_hcd *hcd, struct urb *urb, int status)
+{
+ struct c67x00_hcd *c67x00 = hcd_to_c67x00_hcd(hcd);
+ unsigned long flags;
+ int rc;
+
+ spin_lock_irqsave(&c67x00->lock, flags);
+ rc = usb_hcd_check_unlink_urb(hcd, urb, status);
+ if (rc)
+ goto done;
+
+ c67x00_release_urb(c67x00, urb);
+
+done:
+ spin_unlock_irqrestore(&c67x00->lock, flags);
+ return rc;
+}
+
+/* -------------------------------------------------------------------------- */
+
+/*
+ * pre: urb != NULL and c67x00 locked, urb unlocked
+ */
+static inline void
+c67x00_giveback_urb(struct c67x00_hcd *c67x00, struct urb *urb, int status)
+{
+ struct c67x00_urb_priv *urbp;
+ if (!urb)
+ return;
+
+ urbp = urb->hcpriv;
+ urbp->status = status;
+
+ list_del_init(&urbp->hep_node);
+ list_add_tail(&urbp->hep_node, &c67x00->done_list);
+ tasklet_schedule(&c67x00->done_tasklet);
+}
+
+/* -------------------------------------------------------------------------- */
+
+static int claim_frame_bw(struct c67x00_hcd *c67x00, struct urb *urb,
+ int len, int periodic)
+{
+ struct c67x00_urb_priv *urbp = urb->hcpriv;
+ int bit_time;
+
+ /* According to the C67x00 BIOS user manual, page 3-18,19, the
+ * following calculations provide the full speed bit times for
+ * a transaction.
+ *
+ * FS(in) = 112.5 + 9.36*BC + HOST_DELAY
+ * FS(in,iso) = 90.5 + 9.36*BC + HOST_DELAY
+ * FS(out) = 112.5 + 9.36*BC + HOST_DELAY
+ * FS(out,iso) = 78.4 + 9.36*BC + HOST_DELAY
+ * LS(in) = 802.4 + 75.78*BC + HOST_DELAY
+ * LS(out) = 802.6 + 74.67*BC + HOST_DELAY
+ *
+ * HOST_DELAY == 106 for the c67200 and c67300.
+ */
+
+ /* make calculations in 1/100 bit times to maintain resolution */
+ if (urbp->ep_data->dev->speed == USB_SPEED_LOW) {
+ /* Low speed pipe */
+ if (usb_pipein(urb->pipe))
+ bit_time = 80240 + 7578*len;
+ else
+ bit_time = 80260 + 7467*len;
+ } else {
+ /* FS pipes */
+ if (usb_pipeisoc(urb->pipe))
+ bit_time = usb_pipein(urb->pipe) ? 9050 : 7840;
+ else
+ bit_time = 11250;
+ bit_time += 936*len;
+ }
+
+ /* Scale back down to integer bit times. Use a host delay of 106.
+ * (this is the only place it is used) */
+ bit_time = ((bit_time+50) / 100) + 106;
+
+ if (unlikely(bit_time + c67x00->bandwidth_allocated >=
+ c67x00->max_frame_bw))
+ return -EMSGSIZE;
+
+ if (unlikely(c67x00->next_td_addr + CY_TD_SIZE >=
+ c67x00->td_base_addr + SIE_TD_SIZE))
+ return -EMSGSIZE;
+
+ if (unlikely(c67x00->next_buf_addr + len >=
+ c67x00->buf_base_addr + SIE_TD_BUF_SIZE))
+ return -EMSGSIZE;
+
+ if (periodic) {
+ if (unlikely(bit_time + c67x00->periodic_bw_allocated >=
+ MAX_PERIODIC_BW(c67x00->max_frame_bw)))
+ return -EMSGSIZE;
+ c67x00->periodic_bw_allocated += bit_time;
+ }
+
+ c67x00->bandwidth_allocated += bit_time;
+ return 0;
+}
+
+/* -------------------------------------------------------------------------- */
+
+/**
+ * td_addr and buf_addr must be word aligned
+ */
+static int create_td(struct c67x00_hcd *c67x00,
+ struct urb *urb,
+ char *data,
+ int len, int pid, int toggle, unsigned long privdata)
+{
+ struct c67x00_td *td;
+ struct c67x00_urb_priv *urbp = urb->hcpriv;
+ const __u8 active_flag = 1, retry_cnt = 1;
+ __u8 cmd = 0;
+ int tt = 0;
+
+ if (claim_frame_bw(c67x00, urb, len,
+ usb_pipeisoc(urb->pipe) || usb_pipeint(urb->pipe)))
+ return -EMSGSIZE; /* Not really an error, but expected */
+
+ td = kzalloc(sizeof(*td), GFP_ATOMIC);
+ if (!td)
+ return -ENOMEM;
+
+ td->pipe = urb->pipe;
+ td->ep_data = urbp->ep_data;
+
+ if ((td_udev(td)->speed == USB_SPEED_LOW) &&
+ !(c67x00->low_speed_ports & (1 << urbp->port)))
+ cmd |= PREAMBLE_EN;
+
+ switch (usb_pipetype(td->pipe)) {
+ case PIPE_ISOCHRONOUS:
+ tt = TT_ISOCHRONOUS;
+ cmd |= ISO_EN;
+ break;
+ case PIPE_CONTROL:
+ tt = TT_CONTROL;
+ break;
+ case PIPE_BULK:
+ tt = TT_BULK;
+ break;
+ case PIPE_INTERRUPT:
+ tt = TT_INTERRUPT;
+ break;
+ }
+
+ if (toggle)
+ cmd |= SEQ_SEL;
+
+ cmd |= ARM_EN;
+
+ /* SW part */
+ td->td_addr = c67x00->next_td_addr;
+ c67x00->next_td_addr = c67x00->next_td_addr + CY_TD_SIZE;
+
+ /* HW part */
+ td->ly_base_addr = __cpu_to_le16(c67x00->next_buf_addr);
+ td->port_length = __cpu_to_le16((c67x00->sie->sie_num << 15) |
+ (urbp->port << 14) | (len & 0x3FF));
+ td->pid_ep = ((pid & 0xF) << TD_PIDEP_OFFSET) |
+ (usb_pipeendpoint(td->pipe) & 0xF);
+ td->dev_addr = usb_pipedevice(td->pipe) & 0x7F;
+ td->ctrl_reg = cmd;
+ td->status = 0;
+ td->retry_cnt = (tt << TT_OFFSET) | (active_flag << 4) | retry_cnt;
+ td->residue = 0;
+ td->next_td_addr = __cpu_to_le16(c67x00->next_td_addr);
+
+ /* SW part */
+ td->data = data;
+ td->urb = urb;
+ td->privdata = privdata;
+
+ c67x00->next_buf_addr += (len + 1) & ~0x01; /* properly align */
+
+ list_add_tail(&td->td_list, &c67x00->td_list);
+ return 0;
+}
+
+static inline void release_td(struct c67x00_td *td)
+{
+ list_del_init(&td->td_list);
+ kfree(td);
+}
+
+/* -------------------------------------------------------------------------- */
+
+static int add_data_urb(struct c67x00_hcd *c67x00, struct urb *urb)
+{
+ int remaining;
+ int toggle;
+ int pid;
+ int ret = 0;
+ int maxps;
+ int need_empty;
+
+ toggle = usb_gettoggle(urb->dev, usb_pipeendpoint(urb->pipe),
+ usb_pipeout(urb->pipe));
+ remaining = urb->transfer_buffer_length - urb->actual_length;
+
+ maxps = usb_maxpacket(urb->dev, urb->pipe, usb_pipeout(urb->pipe));
+
+ need_empty = (urb->transfer_flags & URB_ZERO_PACKET) &&
+ usb_pipeout(urb->pipe) && !(remaining % maxps);
+
+ while (remaining || need_empty) {
+ int len;
+ char *td_buf;
+
+ len = (remaining > maxps) ? maxps : remaining;
+ if (!len)
+ need_empty = 0;
+
+ pid = usb_pipeout(urb->pipe) ? USB_PID_OUT : USB_PID_IN;
+ td_buf = urb->transfer_buffer + urb->transfer_buffer_length -
+ remaining;
+ ret = create_td(c67x00, urb, td_buf, len, pid, toggle,
+ DATA_STAGE);
+ if (ret)
+ goto out; /* td wasn't created */
+ toggle ^= 1;
+ remaining -= len;
+ if (usb_pipecontrol(urb->pipe))
+ break;
+ }
+ out:
+ return ret;
+}
+
+/**
+ *
+ * return 0 in case more bandwidth is available, else errorcode
+ */
+static int add_ctrl_urb(struct c67x00_hcd *c67x00, struct urb *urb)
+{
+ int ret;
+ int pid;
+
+ switch (urb->interval) {
+ default:
+ case SETUP_STAGE:
+ ret = create_td(c67x00, urb,
+ urb->setup_packet,
+ 8, USB_PID_SETUP, 0, SETUP_STAGE);
+ if (ret)
+ return ret;
+ urb->interval = SETUP_STAGE;
+ usb_settoggle(urb->dev, usb_pipeendpoint(urb->pipe),
+ usb_pipeout(urb->pipe), 1);
+ break;
+ case DATA_STAGE:
+ if (urb->transfer_buffer_length) {
+ ret = add_data_urb(c67x00, urb);
+ if (ret)
+ return ret;
+ break;
+ } /* else fallthrough */
+ case STATUS_STAGE:
+ pid = !usb_pipeout(urb->pipe) ? USB_PID_OUT : USB_PID_IN;
+ ret = create_td(c67x00, urb, NULL, 0, pid, 1, STATUS_STAGE);
+ if (ret)
+ return ret;
+ break;
+ }
+
+ return 0;
+}
+
+/*
+ * return 0 in case more bandwidth is available, else errorcode
+ */
+static int add_int_urb(struct c67x00_hcd *c67x00, struct urb *urb)
+{
+ struct c67x00_urb_priv *urbp = urb->hcpriv;
+
+ if (frame_after_eq(c67x00->current_frame, urbp->ep_data->next_frame)) {
+ urbp->ep_data->next_frame =
+ frame_add(urbp->ep_data->next_frame, urb->interval);
+ return add_data_urb(c67x00, urb);
+ }
+ return 0;
+}
+
+static int add_iso_urb(struct c67x00_hcd *c67x00, struct urb *urb)
+{
+ struct c67x00_urb_priv *urbp = urb->hcpriv;
+
+ if (frame_after_eq(c67x00->current_frame, urbp->ep_data->next_frame)) {
+ char *td_buf;
+ int len, pid, ret;
+
+ BUG_ON(urbp->cnt >= urb->number_of_packets);
+
+ td_buf = urb->transfer_buffer +
+ urb->iso_frame_desc[urbp->cnt].offset;
+ len = urb->iso_frame_desc[urbp->cnt].length;
+ pid = usb_pipeout(urb->pipe) ? USB_PID_OUT : USB_PID_IN;
+
+ ret = create_td(c67x00, urb, td_buf, len, pid, 0, urbp->cnt);
+ if (ret) {
+ printk(KERN_DEBUG "create failed: %d\n", ret);
+ urb->iso_frame_desc[urbp->cnt].actual_length = 0;
+ urb->iso_frame_desc[urbp->cnt].status = ret;
+ if (urbp->cnt + 1 == urb->number_of_packets)
+ c67x00_giveback_urb(c67x00, urb, 0);
+ }
+
+ urbp->ep_data->next_frame =
+ frame_add(urbp->ep_data->next_frame, urb->interval);
+ urbp->cnt++;
+ }
+ return 0;
+}
+
+/* -------------------------------------------------------------------------- */
+
+static void c67x00_fill_from_list(struct c67x00_hcd *c67x00, int type,
+ int (*add)(struct c67x00_hcd *, struct urb *))
+{
+ struct c67x00_ep_data *ep_data;
+ struct urb *urb;
+
+ /* traverse every endpoint on the list */
+ list_for_each_entry(ep_data, &c67x00->list[type], node) {
+ if (!list_empty(&ep_data->queue)) {
+ /* and add the first urb */
+ /* isochronous transfer rely on this */
+ urb = list_entry(ep_data->queue.next, struct c67x00_urb_priv,
+ hep_node)->urb;
+ add(c67x00, urb);
+ }
+ }
+}
+
+static void c67x00_fill_frame(struct c67x00_hcd *c67x00)
+{
+ struct c67x00_td *td, *ttd;
+
+ /* Check if we can proceed */
+ if (!list_empty(&c67x00->td_list)) {
+ dev_warn(c67x00_hcd_dev(c67x00),
+ "TD list not empty! This should not happen!\n");
+ list_for_each_entry_safe(td, ttd, &c67x00->td_list, td_list) {
+ dbg_td(c67x00, td, "Unprocessed td");
+ release_td(td);
+ }
+ }
+
+ /* Reinitialize variables */
+ c67x00->bandwidth_allocated = 0;
+ c67x00->periodic_bw_allocated = 0;
+
+ c67x00->next_td_addr = c67x00->td_base_addr;
+ c67x00->next_buf_addr = c67x00->buf_base_addr;
+
+ /* Fill the list */
+ c67x00_fill_from_list(c67x00, PIPE_ISOCHRONOUS, add_iso_urb);
+ c67x00_fill_from_list(c67x00, PIPE_INTERRUPT, add_int_urb);
+ c67x00_fill_from_list(c67x00, PIPE_CONTROL, add_ctrl_urb);
+ c67x00_fill_from_list(c67x00, PIPE_BULK, add_data_urb);
+}
+
+/* -------------------------------------------------------------------------- */
+
+/**
+ * Get TD from C67X00
+ */
+static inline void
+c67x00_parse_td(struct c67x00_hcd *c67x00, struct c67x00_td *td)
+{
+ c67x00_ll_hpi_read_mem_le16(c67x00->sie->dev, td->td_addr, CY_TD_SIZE,
+ (char *)td);
+
+ if (usb_pipein(td->pipe) && td_actual_bytes(td))
+ c67x00_ll_hpi_read_mem_le16(c67x00->sie->dev,
+ td_ly_base_addr(td),
+ td_actual_bytes(td), td->data);
+}
+
+/* -------------------------------------------------------------------------- */
+
+static int
+c67x00_td_to_error(struct c67x00_hcd *c67x00, struct c67x00_td *td)
+{
+ if (td->status & TD_STATUSMASK_ERR) {
+ dbg_td(c67x00, td, "ERROR_FLAG");
+ return -EILSEQ;
+ }
+ if (td->status & TD_STATUSMASK_STALL) {
+ /* dbg_td(c67x00, td, "STALL"); */
+ return -EPIPE;
+ }
+ if (td->status & TD_STATUSMASK_TMOUT) {
+ dbg_td(c67x00, td, "TIMEOUT");
+ return -ETIMEDOUT;
+ }
+
+ return 0;
+}
+
+static inline int c67x00_end_of_data(struct c67x00_td *td)
+{
+ int maxps, need_empty, remaining;
+ struct urb *urb = td->urb;
+ int act_bytes;
+
+ act_bytes = td_actual_bytes(td);
+
+ if (unlikely(!act_bytes))
+ return 1; /* This was an empty packet */
+
+ maxps = usb_maxpacket(td_udev(td), td->pipe, usb_pipeout(td->pipe));
+
+ if (unlikely(act_bytes < maxps))
+ return 1; /* Smaller then full packet */
+
+ remaining = urb->transfer_buffer_length - urb->actual_length;
+ need_empty = (urb->transfer_flags & URB_ZERO_PACKET) &&
+ usb_pipeout(urb->pipe) && !(remaining % maxps);
+
+ if (unlikely(!remaining && !need_empty))
+ return 1;
+
+ return 0;
+}
+
+/* -------------------------------------------------------------------------- */
+
+/* Remove all td's from the list which come
+ * after last_td and are meant for the same pipe.
+ * This is used when a short packet has occured */
+static inline void
+c67x00_clear_pipe(struct c67x00_hcd *c67x00, struct c67x00_td *last_td)
+{
+ struct c67x00_td *td, *tmp;
+ td = last_td;
+ tmp = last_td;
+ while (td->td_list.next != &c67x00->td_list) {
+ td = list_entry(td->td_list.next, struct c67x00_td, td_list);
+ if (td->pipe == last_td->pipe) {
+ release_td(td);
+ td = tmp;
+ }
+ tmp = td;
+ }
+}
+
+/* -------------------------------------------------------------------------- */
+
+static void c67x00_handle_successful_td(struct c67x00_hcd *c67x00,
+ struct c67x00_td *td)
+{
+ struct urb *urb = td->urb;
+
+ if (!urb)
+ return;
+
+ urb->actual_length += td_actual_bytes(td);
+
+ switch (usb_pipetype(td->pipe)) {
+ /* isochronous tds are handled separately */
+ case PIPE_CONTROL:
+ switch (td->privdata) {
+ case SETUP_STAGE:
+ urb->interval =
+ urb->transfer_buffer_length ?
+ DATA_STAGE : STATUS_STAGE;
+ /* Don't count setup_packet with normal data: */
+ urb->actual_length = 0;
+ break;
+ case DATA_STAGE:
+ if (c67x00_end_of_data(td)) {
+ urb->interval = STATUS_STAGE;
+ c67x00_clear_pipe(c67x00, td);
+ }
+ break;
+ case STATUS_STAGE:
+ urb->interval = 0;
+ c67x00_giveback_urb(c67x00, urb, 0);
+ break;
+ }
+ break;
+ case PIPE_INTERRUPT:
+ case PIPE_BULK:
+ if (unlikely(c67x00_end_of_data(td))) {
+ c67x00_clear_pipe(c67x00, td);
+ c67x00_giveback_urb(c67x00, urb, 0);
+ }
+ break;
+ }
+}
+
+static void c67x00_handle_isoc(struct c67x00_hcd *c67x00, struct c67x00_td *td)
+{
+ struct urb *urb = td->urb;
+ struct c67x00_urb_priv *urbp;
+ int cnt;
+
+ if (!urb)
+ return;
+
+ urbp = urb->hcpriv;
+ cnt = td->privdata;
+
+ if (td->status & TD_ERROR_MASK)
+ urb->error_count++;
+
+ urb->iso_frame_desc[cnt].actual_length = td_actual_bytes(td);
+ urb->iso_frame_desc[cnt].status = c67x00_td_to_error(c67x00, td);
+ if (cnt + 1 == urb->number_of_packets) /* Last packet */
+ c67x00_giveback_urb(c67x00, urb, 0);
+}
+
+/* -------------------------------------------------------------------------- */
+
+/**
+ * c67x00_check_td_list - handle tds which have been processed by the c67x00
+ * pre: current_td == 0
+ */
+static inline void c67x00_check_td_list(struct c67x00_hcd *c67x00)
+{
+ struct c67x00_td *td, *tmp;
+ struct urb *urb;
+ int ack_ok;
+ int clear_endpoint;
+
+ list_for_each_entry_safe(td, tmp, &c67x00->td_list, td_list) {
+ /* get the TD */
+ c67x00_parse_td(c67x00, td);
+ urb = td->urb; /* urb can be NULL! */
+ ack_ok = 0;
+ clear_endpoint = 1;
+
+ /* Handle isochronous transfers separately */
+ if (usb_pipeisoc(td->pipe)) {
+ clear_endpoint = 0;
+ c67x00_handle_isoc(c67x00, td);
+ goto cont;
+ }
+
+ /* When an error occurs, all td's for that pipe go into an
+ * inactive state. This state matches successful transfers so
+ * we must make sure not to service them. */
+ if (td->status & TD_ERROR_MASK) {
+ c67x00_giveback_urb(c67x00, urb, c67x00_td_to_error(c67x00, td));
+ goto cont;
+ }
+
+ if ((td->status & TD_STATUSMASK_NAK) || !td_sequence_ok(td) ||
+ !td_acked(td))
+ goto cont;
+
+ /* Sequence ok and acked, don't need to fix toggle */
+ ack_ok = 1;
+
+ if (unlikely(td->status & TD_STATUSMASK_OVF)) {
+ if (td_residue(td) & TD_RESIDUE_OVERFLOW) {
+ /* Overflow */
+ c67x00_giveback_urb(c67x00, urb, -EOVERFLOW);
+ goto cont;
+ }
+ }
+
+ clear_endpoint = 0;
+ c67x00_handle_successful_td(c67x00, td);
+
+ cont:
+ if (clear_endpoint)
+ c67x00_clear_pipe(c67x00, td);
+ if (ack_ok)
+ usb_settoggle(td_udev(td), usb_pipeendpoint(td->pipe),
+ usb_pipeout(td->pipe),
+ !(td->ctrl_reg & SEQ_SEL));
+ /* next in list could have been removed, due to clear_pipe! */
+ tmp = list_entry(td->td_list.next, typeof(*td), td_list);
+ release_td(td);
+ }
+}
+
+/* -------------------------------------------------------------------------- */
+
+static inline int c67x00_all_tds_processed(struct c67x00_hcd *c67x00)
+{
+ /* If all tds are processed, we can check the previous frame (if
+ * there was any) and start our next frame.
+ */
+ return !c67x00_ll_husb_get_current_td(c67x00->sie);
+}
+
+/**
+ * Send td to C67X00
+ */
+static void c67x00_send_td(struct c67x00_hcd *c67x00, struct c67x00_td *td)
+{
+ int len = td_length(td);
+
+ if (len && ((td->pid_ep & TD_PIDEPMASK_PID) != TD_PID_IN))
+ c67x00_ll_hpi_write_mem_le16(c67x00->sie->dev,
+ td_ly_base_addr(td),
+ len, td->data);
+
+ c67x00_ll_hpi_write_mem_le16(c67x00->sie->dev, td->td_addr,
+ CY_TD_SIZE, (char *)td);
+}
+
+static void c67x00_send_frame(struct c67x00_hcd *c67x00)
+{
+ struct c67x00_td *td;
+
+ if (list_empty(&c67x00->td_list))
+ dev_warn(c67x00_hcd_dev(c67x00),
+ "%s: td list should not be empty here!\n",
+ __FUNCTION__);
+
+ list_for_each_entry(td, &c67x00->td_list, td_list) {
+ if (td->td_list.next == &c67x00->td_list)
+ td->next_td_addr = 0; /* Last td in list */
+
+ c67x00_send_td(c67x00, td);
+ }
+
+ c67x00_ll_husb_set_current_td(c67x00->sie, c67x00->td_base_addr);
+}
+
+/* -------------------------------------------------------------------------- */
+
+/**
+ * c67x00_do_work - Schedulers state machine
+ */
+static void c67x00_do_work(struct c67x00_hcd *c67x00)
+{
+ spin_lock(&c67x00->lock);
+ /* Make sure all tds are processed */
+ if (!c67x00_all_tds_processed(c67x00))
+ goto out;
+
+ c67x00_check_td_list(c67x00);
+
+ /* no td's are being processed (current == 0)
+ * and all have been "checked" */
+ complete(&c67x00->endpoint_disable);
+
+ if (!list_empty(&c67x00->td_list))
+ goto out;
+
+ c67x00->current_frame = c67x00_get_current_frame_number(c67x00);
+ if (c67x00->current_frame == c67x00->last_frame)
+ goto out; /* Don't send tds in same frame */
+ c67x00->last_frame = c67x00->current_frame;
+
+ /* If no urbs are scheduled, our work is done */
+ if (!c67x00->urb_count) {
+ c67x00_ll_hpi_disable_sofeop(c67x00->sie);
+ goto out;
+ }
+
+ c67x00_fill_frame(c67x00);
+ if (list_empty(&c67x00->td_list))
+ /* URBs aren't for this frame */
+ goto out;
+ else {
+ /* TD's have been added to the frame */
+ c67x00_send_frame(c67x00);
+ goto out;
+ }
+ out:
+ spin_unlock(&c67x00->lock);
+}
+
+/* -------------------------------------------------------------------------- */
+
+static void c67x00_sched_tasklet(unsigned long __c67x00)
+{
+ struct c67x00_hcd *c67x00 = (struct c67x00_hcd *)__c67x00;
+ c67x00_do_work(c67x00);
+}
+
+static void c67x00_sched_done(unsigned long __c67x00)
+{
+ struct c67x00_hcd *c67x00 = (struct c67x00_hcd *)__c67x00;
+ struct c67x00_urb_priv *urbp, *tmp;
+ struct usb_hcd *hcd = c67x00_hcd_to_hcd(c67x00);
+ struct urb *urb;
+ int status;
+
+ spin_lock(&c67x00->lock);
+
+ /* Loop over the done list and give back all the urbs */
+ list_for_each_entry_safe(urbp, tmp, &c67x00->done_list, hep_node) {
+ urb = urbp->urb;
+ status = urbp->status;
+
+ c67x00_release_urb(c67x00, urb);
+
+ usb_hcd_unlink_urb_from_ep(hcd, urb);
+
+ spin_unlock(&c67x00->lock);
+ usb_hcd_giveback_urb(hcd, urb, status);
+ spin_lock(&c67x00->lock);
+ }
+ spin_unlock(&c67x00->lock);
+}
+
+void c67x00_sched_kick(struct c67x00_hcd *c67x00)
+{
+ tasklet_hi_schedule(&c67x00->tasklet);
+}
+
+int c67x00_sched_start_scheduler(struct c67x00_hcd *c67x00)
+{
+ tasklet_init(&c67x00->tasklet, c67x00_sched_tasklet,
+ (unsigned long)c67x00);
+ tasklet_init(&c67x00->done_tasklet, c67x00_sched_done,
+ (unsigned long)c67x00);
+ return 0;
+}
+
+void c67x00_sched_stop_scheduler(struct c67x00_hcd *c67x00)
+{
+ tasklet_kill(&c67x00->tasklet);
+ tasklet_kill(&c67x00->done_tasklet);
+}
^ permalink raw reply related
* [PATCH v3 1/4] USB: add Cypress c67x00 low level interface code
From: Grant Likely @ 2008-01-02 6:21 UTC (permalink / raw)
To: linux-usb-devel, dbrownell, gregkh, linuxppc-dev, jacmet, stern
From: Grant Likely <grant.likely@secretlab.ca>
This patch adds the low level support code for the Cypress c67x00 family of
OTG controllers. The low level code is responsible for register access and
implements the software protocol for communicating with the c67x00 device.
Signed-off-by: Grant Likely <grant.likely@secretlab.ca>
---
drivers/usb/c67x00/c67x00-ll-hpi.c | 516 ++++++++++++++++++++++++++++++++++++
drivers/usb/c67x00/c67x00.h | 242 +++++++++++++++++
2 files changed, 758 insertions(+), 0 deletions(-)
diff --git a/drivers/usb/c67x00/c67x00-ll-hpi.c b/drivers/usb/c67x00/c67x00-ll-hpi.c
new file mode 100644
index 0000000..d6a95d6
--- /dev/null
+++ b/drivers/usb/c67x00/c67x00-ll-hpi.c
@@ -0,0 +1,516 @@
+/*
+ * c67x00-ll-hpi.c: Cypress C67X00 USB Low level interface using HPI
+ *
+ * Copyright (C) 2006-2007 Barco N.V.
+ * Derived from the Cypress cy7c67200/300 ezusb linux driver and
+ * based on multiple host controller drivers inside the linux kernel.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * 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 for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+ * MA 02110-1301 USA.
+ */
+
+#include <asm/io.h>
+#include <asm/byteorder.h>
+#include "c67x00.h"
+
+/* -------------------------------------------------------------------------- */
+/* Interface definitions */
+
+#define COMM_ACK 0x0FED
+#define COMM_NAK 0xDEAD
+
+#define COMM_CTRL_REG_ADDR 0x01BC
+#define COMM_CTRL_REG_DATA 0x01BE
+#define COMM_CTRL_REG_LOGIC 0x01C0
+#define COMM_WRITE_CTRL_REG 0xCE03
+#define COMM_READ_CTRL_REG 0xCE02
+
+#define COMM_RESET 0xFA50
+#define COMM_EXEC_INT 0xCE01
+#define COMM_INT_NUM 0x01C2
+/* Registers 0 to COMM_REGS-1 */
+#define COMM_R(x) (0x01C4 + 2 * (x))
+
+#define HUSB_SIE_pCurrentTDPtr(x) ( (x) ? 0x01B2 : 0x01B0 )
+#define HUSB_SIE_pTDListDone_Sem(x) ( (x) ? 0x01B8 : 0x01B6 )
+#define HUSB_pEOT 0x01B4
+
+/* Software interrupts */
+/* 114, 115: */
+#define HUSB_SIE_INIT_INT(x) ( (x) ? 0x0073 : 0x0072 )
+#define HUSB_RESET_INT 0x0074 /* 116 */
+
+#define SUSB_INIT_INT 0x0071
+/* ---------------------------------------------------------------------
+ * HPI implementation
+ *
+ * The c67x00 chip also support control via SPI or HSS serial
+ * interfaces. However, this driver assumes that register access can
+ * be performed from IRQ context. While this is a safe assuption with
+ * the HPI interface, it is not true for the serial interfaces.
+ */
+
+/* HPI registers */
+#define HPI_DATA 0
+#define HPI_MAILBOX 1
+#define HPI_ADDR 2
+#define HPI_STATUS 3
+
+static inline u16 hpi_read_reg(struct c67x00_device *dev, int reg)
+{
+ return __raw_readw(dev->hpi.base + reg * dev->hpi.regstep);
+}
+
+static inline void hpi_write_reg(struct c67x00_device *dev, int reg, u16 value)
+{
+ __raw_writew(value, dev->hpi.base + reg * dev->hpi.regstep);
+}
+
+static inline u16 hpi_read_word_nolock(struct c67x00_device *dev, u16 reg)
+{
+ hpi_write_reg(dev, HPI_ADDR, reg);
+ return hpi_read_reg(dev, HPI_DATA);
+}
+
+static inline u16 hpi_read_word(struct c67x00_device *dev, u16 reg)
+{
+ u16 value;
+ unsigned long flags;
+
+ spin_lock_irqsave(&dev->hw_lock, flags);
+ value = hpi_read_word_nolock(dev, reg);
+ spin_unlock_irqrestore(&dev->hw_lock, flags);
+
+ return value;
+}
+
+static inline void hpi_write_word_nolock(struct c67x00_device *dev, u16 reg,
+ u16 value)
+{
+ hpi_write_reg(dev, HPI_ADDR, reg);
+ hpi_write_reg(dev, HPI_DATA, value);
+}
+
+static inline void hpi_write_word(struct c67x00_device *dev, u16 reg, u16 value)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&dev->hw_lock, flags);
+ hpi_write_word_nolock(dev, reg, value);
+ spin_unlock_irqrestore(&dev->hw_lock, flags);
+}
+
+/*
+ * Only data is little endian, addr has cpu endianess
+ */
+static inline void hpi_write_words_le16(struct c67x00_device *dev, u16 addr,
+ u16 * data, u16 count)
+{
+ unsigned long flags;
+ int i;
+
+ spin_lock_irqsave(&dev->hw_lock, flags);
+
+ hpi_write_reg(dev, HPI_ADDR, addr);
+ for (i = 0; i < count; i++)
+ hpi_write_reg(dev, HPI_DATA, cpu_to_le16(*data++));
+
+ spin_unlock_irqrestore(&dev->hw_lock, flags);
+}
+
+/*
+ * Only data is little endian, addr has cpu endianess
+ */
+static inline void hpi_read_words_le16(struct c67x00_device *dev, u16 addr,
+ u16 * data, u16 count)
+{
+ unsigned long flags;
+ int i;
+ spin_lock_irqsave(&dev->hw_lock, flags);
+ hpi_write_reg(dev, HPI_ADDR, addr);
+ for (i = 0; i < count; i++)
+ *data++ = le16_to_cpu(hpi_read_reg(dev, HPI_DATA));
+
+ spin_unlock_irqrestore(&dev->hw_lock, flags);
+}
+
+static inline void hpi_set_bits(struct c67x00_device *dev, u16 reg, u16 mask)
+{
+ u16 value;
+ unsigned long flags;
+
+ spin_lock_irqsave(&dev->hw_lock, flags);
+ value = hpi_read_word_nolock(dev, reg);
+ hpi_write_word_nolock(dev, reg, value | mask);
+ spin_unlock_irqrestore(&dev->hw_lock, flags);
+}
+
+static inline void hpi_clear_bits(struct c67x00_device *dev, u16 reg, u16 mask)
+{
+ u16 value;
+ unsigned long flags;
+
+ spin_lock_irqsave(&dev->hw_lock, flags);
+ value = hpi_read_word_nolock(dev, reg);
+ hpi_write_word_nolock(dev, reg, value & ~mask);
+ spin_unlock_irqrestore(&dev->hw_lock, flags);
+}
+
+static inline u16 hpi_recv_mbox(struct c67x00_device *dev)
+{
+ u16 value;
+ unsigned long flags;
+
+ spin_lock_irqsave(&dev->hw_lock, flags);
+ value = hpi_read_reg(dev, HPI_MAILBOX);
+ spin_unlock_irqrestore(&dev->hw_lock, flags);
+
+ return value;
+}
+
+static inline u16 hpi_send_mbox(struct c67x00_device *dev, u16 value)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&dev->hw_lock, flags);
+ hpi_write_reg(dev, HPI_MAILBOX, value);
+ spin_unlock_irqrestore(&dev->hw_lock, flags);
+
+ return value;
+}
+
+u16 c67x00_ll_hpi_status(struct c67x00_device * dev)
+{
+ u16 value;
+ unsigned long flags;
+
+ spin_lock_irqsave(&dev->hw_lock, flags);
+ value = hpi_read_reg(dev, HPI_STATUS);
+ spin_unlock_irqrestore(&dev->hw_lock, flags);
+
+ return value;
+}
+
+void c67x00_ll_hpi_reg_init(struct c67x00_device *dev)
+{
+ int i;
+
+ hpi_recv_mbox(dev);
+ c67x00_ll_hpi_status(dev);
+ hpi_write_word(dev, HPI_IRQ_ROUTING_REG, 0);
+
+ for (i=0; i<C67X00_SIES; i++) {
+ hpi_write_word(dev, SIEMSG_REG(i), 0);
+ hpi_read_word(dev, SIEMSG_REG(i));
+ }
+}
+
+void c67x00_ll_hpi_enable_sofeop(struct c67x00_sie *sie)
+{
+ hpi_set_bits(sie->dev, HPI_IRQ_ROUTING_REG,
+ SOFEOP_TO_HPI_EN(sie->sie_num));
+}
+
+void c67x00_ll_hpi_disable_sofeop(struct c67x00_sie *sie)
+{
+ hpi_clear_bits(sie->dev, HPI_IRQ_ROUTING_REG,
+ SOFEOP_TO_HPI_EN(sie->sie_num));
+}
+
+/* -------------------------------------------------------------------------- */
+/* Transactions */
+
+static inline u16 ll_recv_msg(struct c67x00_device *dev)
+{
+ u16 res;
+
+ res = wait_for_completion_timeout(&dev->lcp.msg_received, 5 * HZ);
+ WARN_ON(!res);
+
+ return (res == 0) ? -EIO : 0;
+}
+
+/* -------------------------------------------------------------------------- */
+
+u16 c67x00_comm_read_ctrl_reg(struct c67x00_device * dev, u16 addr)
+{
+ unsigned long msg, res;
+ int rc;
+
+ mutex_lock(&dev->lcp.mutex);
+ hpi_write_word(dev, COMM_CTRL_REG_ADDR, addr);
+ hpi_send_mbox(dev, COMM_READ_CTRL_REG);
+ rc = ll_recv_msg(dev);
+
+ BUG_ON(rc); /* No return path for error code; crash spectacularly */
+
+ msg = dev->lcp.last_msg;
+ if (msg != COMM_ACK) {
+ dev_warn(&dev->pdev->dev, "COMM_READ_CTRL_REG didn't ACK!\n");
+ res = 0;
+ } else {
+ res = hpi_read_word(dev, COMM_CTRL_REG_DATA);
+ }
+ mutex_unlock(&dev->lcp.mutex);
+ return res;
+}
+
+int c67x00_comm_exec_int(struct c67x00_device *dev, u16 nr,
+ struct c67x00_lcp_int_data *data)
+{
+ int i, rc;
+
+ mutex_lock(&dev->lcp.mutex);
+ hpi_write_word(dev, COMM_INT_NUM, nr);
+ for (i = 0; i < COMM_REGS; i++)
+ hpi_write_word(dev, COMM_R(i), data->regs[i]);
+ hpi_send_mbox(dev, COMM_EXEC_INT);
+ rc = ll_recv_msg(dev);
+ mutex_unlock(&dev->lcp.mutex);
+
+ return rc;
+}
+
+/* -------------------------------------------------------------------------- */
+/* General functions */
+
+u16 c67x00_ll_get_siemsg(struct c67x00_device *dev, int sie)
+{
+ return hpi_read_word(dev, SIEMSG_REG(sie));
+}
+
+void c67x00_ll_set_siemsg(struct c67x00_device *dev, int sie, u16 val)
+{
+ hpi_write_word(dev, SIEMSG_REG(sie), val);
+}
+
+u16 c67x00_ll_get_usb_ctl(struct c67x00_sie *sie)
+{
+ return hpi_read_word(sie->dev, USB_CTL_REG(sie->sie_num));
+}
+
+/* -------------------------------------------------------------------------- */
+/* Host specific functions */
+
+void c67x00_ll_set_husb_eot(struct c67x00_device *dev, u16 value)
+{
+ mutex_lock(&dev->lcp.mutex);
+ hpi_write_word(dev, HUSB_pEOT, value);
+ mutex_unlock(&dev->lcp.mutex);
+}
+
+static inline void c67x00_ll_husb_sie_init(struct c67x00_sie *sie)
+{
+ struct c67x00_device *dev = sie->dev;
+ struct c67x00_lcp_int_data data;
+ int rc;
+
+ rc = c67x00_comm_exec_int(dev, HUSB_SIE_INIT_INT(sie->sie_num), &data);
+ BUG_ON(rc); /* No return path for error code; crash spectacularly */
+}
+
+void c67x00_ll_husb_reset(struct c67x00_sie *sie, int port)
+{
+ struct c67x00_device *dev = sie->dev;
+ struct c67x00_lcp_int_data data;
+ int rc;
+
+ data.regs[0] = 50; /* Reset USB port for 50ms */
+ data.regs[1] = port | (sie->sie_num << 1);
+ rc = c67x00_comm_exec_int(dev, HUSB_RESET_INT, &data);
+ BUG_ON(rc); /* No return path for error code; crash spectacularly */
+}
+
+void c67x00_ll_husb_set_current_td(struct c67x00_sie *sie, u16 addr)
+{
+ hpi_write_word(sie->dev, HUSB_SIE_pCurrentTDPtr(sie->sie_num), addr);
+}
+
+u16 c67x00_ll_husb_get_current_td(struct c67x00_sie *sie)
+{
+ return hpi_read_word(sie->dev, HUSB_SIE_pCurrentTDPtr(sie->sie_num));
+}
+
+/**
+ * c67x00_ll_husb_clear_status - clear the host status bits
+ */
+void c67x00_ll_husb_clear_status(struct c67x00_sie *sie, u16 bits)
+{
+ hpi_write_word(sie->dev, HOST_STAT_REG(sie->sie_num), bits);
+}
+
+u16 c67x00_ll_husb_get_status(struct c67x00_sie *sie)
+{
+ return hpi_read_word(sie->dev, HOST_STAT_REG(sie->sie_num));
+}
+
+u16 c67x00_ll_husb_get_frame(struct c67x00_sie * sie)
+{
+ return hpi_read_word(sie->dev, HOST_FRAME_REG(sie->sie_num));
+}
+
+void c67x00_ll_husb_init_host_port(struct c67x00_sie *sie)
+{
+ /* Set port into host mode */
+ hpi_set_bits(sie->dev, USB_CTL_REG(sie->sie_num), HOST_MODE);
+ c67x00_ll_husb_sie_init(sie);
+ /* Clear interrupts */
+ c67x00_ll_husb_clear_status(sie, HOST_STAT_MASK);
+ /* Check */
+ if (!(hpi_read_word(sie->dev, USB_CTL_REG(sie->sie_num)) & HOST_MODE))
+ dev_warn(sie_dev(sie),
+ "SIE %d not set to host mode\n", sie->sie_num);
+}
+
+void c67x00_ll_husb_reset_port(struct c67x00_sie *sie, int port)
+{
+ /* Clear connect change */
+ c67x00_ll_husb_clear_status(sie, PORT_CONNECT_CHANGE(port));
+
+ /* Enable interrupts */
+ hpi_set_bits(sie->dev, HPI_IRQ_ROUTING_REG,
+ SOFEOP_TO_CPU_EN(sie->sie_num));
+ hpi_set_bits(sie->dev, HOST_IRQ_EN_REG(sie->sie_num),
+ SOF_EOP_IRQ_EN | DONE_IRQ_EN);
+
+ /* Enable pull down transistors */
+ hpi_set_bits(sie->dev, USB_CTL_REG(sie->sie_num), PORT_RES_EN(port));
+}
+
+/* -------------------------------------------------------------------------- */
+void c67x00_ll_susb_init(struct c67x00_sie *sie)
+{
+ struct c67x00_device *dev = sie->dev;
+ struct c67x00_lcp_int_data data;
+ int rc;
+
+ data.regs[1] = 1; /* full speed */
+ data.regs[2] = sie->sie_num + 1;
+ rc = c67x00_comm_exec_int(dev, SUSB_INIT_INT, &data);
+ BUG_ON(rc); /* No return path for error code; crash spectacularly */
+
+ hpi_clear_bits(dev, HPI_IRQ_ROUTING_REG,
+ SOFEOP_TO_HPI_EN(sie->sie_num));
+ hpi_set_bits(dev, HPI_IRQ_ROUTING_REG, SOFEOP_TO_CPU_EN(sie->sie_num));
+}
+
+/* -------------------------------------------------------------------------- */
+
+void c67x00_ll_irq(struct c67x00_device *dev)
+{
+ if ((dev->int_status & MBX_OUT_FLG) == 0)
+ return;
+
+ dev->lcp.last_msg = hpi_recv_mbox(dev);
+ complete(&dev->lcp.msg_received);
+}
+
+/* -------------------------------------------------------------------------- */
+
+int c67x00_ll_reset(struct c67x00_device *dev)
+{
+ int rc;
+
+ mutex_lock(&dev->lcp.mutex);
+ hpi_send_mbox(dev, COMM_RESET);
+ rc = ll_recv_msg(dev);
+ mutex_unlock(&dev->lcp.mutex);
+
+ return rc;
+}
+
+/* -------------------------------------------------------------------------- */
+
+/**
+ * c67x00_write_mem_le16 - write into c67x00 memory
+ * Only data is little endian, addr has cpu endianess.
+ */
+void c67x00_ll_hpi_write_mem_le16(struct c67x00_device *dev, u16 addr, int len,
+ char *data)
+{
+ /* Sanity check */
+ if (addr + len > 0xffff) {
+ dev_err(&dev->pdev->dev,
+ "Trying to write beyond writable region!\n");
+ return;
+ }
+
+ if (addr & 0x01) {
+ /* unaligned access */
+ u16 tmp;
+ tmp = hpi_read_word(dev, addr - 1);
+ tmp = (tmp & 0x00ff) | (*data++ << 8);
+ hpi_write_word(dev, addr - 1, tmp);
+ addr++;
+ len--;
+ }
+
+ hpi_write_words_le16(dev, addr, (u16 *) data, len / 2);
+ data += len & ~0x01;
+ addr += len & ~0x01;
+ len &= 0x01;
+
+ if (len) {
+ u16 tmp;
+ tmp = hpi_read_word(dev, addr);
+ tmp = (tmp & 0xff00) | (*data++);
+ hpi_write_word(dev, addr, tmp);
+ addr++;
+ len--;
+ }
+
+}
+
+/**
+ * c67x00_ll_hpi_read_mem_le16 - read from c67x00 memory
+ * Only data is little endian, addr has cpu endianess.
+ */
+void c67x00_ll_hpi_read_mem_le16(struct c67x00_device *dev, u16 addr, int len,
+ char *data)
+{
+ if (addr & 0x01) {
+ /* unaligned access */
+ u16 tmp;
+ tmp = hpi_read_word(dev, addr - 1);
+ *data++ = (tmp >> 8) & 0x00ff;
+ addr++;
+ len--;
+ }
+
+ hpi_read_words_le16(dev, addr, (u16 *) data, len / 2);
+ data += len & ~0x01;
+ addr += len & ~0x01;
+ len &= 0x01;
+
+ if (len) {
+ u16 tmp;
+ tmp = hpi_read_word(dev, addr);
+ *data++ = tmp & 0x00ff;
+ addr++;
+ len--;
+ }
+}
+
+/* -------------------------------------------------------------------------- */
+
+void c67x00_ll_init(struct c67x00_device *dev)
+{
+ mutex_init(&dev->lcp.mutex);
+ init_completion(&dev->lcp.msg_received);
+}
+
+void c67x00_ll_release(struct c67x00_device *dev)
+{
+}
diff --git a/drivers/usb/c67x00/c67x00.h b/drivers/usb/c67x00/c67x00.h
new file mode 100644
index 0000000..00a38df
--- /dev/null
+++ b/drivers/usb/c67x00/c67x00.h
@@ -0,0 +1,242 @@
+/*
+ * c67x00.h: Cypress C67X00 USB register and field definitions
+ *
+ * Copyright (C) 2006-2007 Barco N.V.
+ * Derived from the Cypress cy7c67200/300 ezusb linux driver and
+ * based on multiple host controller drivers inside the linux kernel.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * 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 for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+ * MA 02110-1301 USA.
+ */
+
+#ifndef _USB_C67X00_H
+#define _USB_C67X00_H
+
+#include <linux/interrupt.h>
+#include <linux/list.h>
+#include <linux/platform_device.h>
+#include <linux/completion.h>
+#include <linux/mutex.h>
+
+/* ---------------------------------------------------------------------
+ * Cypress C67x00 register definitions
+ */
+
+/* Processor control registers */
+/* =========================== */
+
+/* Hardware Revision Register */
+#define HW_REV_REG 0xC004
+
+/* General USB registers */
+/* ===================== */
+
+/* USB Control Register */
+#define USB_CTL_REG(x) ( (x) ? 0xC0AA : 0xC08A )
+
+#define LOW_SPEED_PORT(x) ( (x) ? 0x0800 : 0x0400 )
+#define HOST_MODE 0x0200
+#define PORT_RES_EN(x) ( (x) ? 0x0100 : 0x0080 )
+#define SOF_EOP_EN(x) ( (x) ? 0x0002 : 0x0001 )
+
+/* USB Host only registers */
+/* ======================= */
+
+/* Host n Control Register */
+#define HOST_CTL_REG(x) ((x) ? 0xC0A0 : 0xC080)
+
+#define PREAMBLE_EN 0x0080 /* Preamble enable */
+#define SEQ_SEL 0x0040 /* Data Toggle Sequence Bit Select */
+#define ISO_EN 0x0010 /* Isochronous enable */
+#define ARM_EN 0x0001 /* Arm operation */
+
+/* Host n Interrupt Enable Register */
+#define HOST_IRQ_EN_REG(x) ( (x) ? 0xC0AC : 0xC08C)
+
+#define SOF_EOP_IRQ_EN 0x0200 /* SOF/EOP Interrupt Enable */
+#define DONE_IRQ_EN 0x0001 /* Done Interrupt Enable */
+
+/* Host n status register */
+#define HOST_STAT_REG(x) ( (x) ? 0xC0B0 : 0xC090 )
+
+#define HOST_STAT_MASK 0x02FD
+#define SOF_EOP_IRQ_FLG 0x0200
+#define PORT_CONNECT_CHANGE(x) ( (x) ? 0x0020 : 0x0010 )
+#define PORT_SE0_STATUS(x) ( (x) ? 0x0008 : 0x0004 )
+
+/* Host Frame Register */
+#define HOST_FRAME_REG(x) ( (x) ? 0xC0B6 : 0xC096 )
+
+#define HOST_FRAME_MASK 0x07FF
+
+/* HPI registers */
+/* ============= */
+
+/* HPI Status register */
+#define SOFEOP_FLG(x) (1 << ( (x) ? 12 : 10 ))
+#define SIEMSG_FLAG(x) (1 << (4 + (x)))
+#define MBX_OUT_FLG 0x0001 /* Message out available */
+
+/* Interrupt routing register */
+#define HPI_IRQ_ROUTING_REG 0x0142
+
+#define SOFEOP_TO_HPI_EN(x) ( (x) ? 0x2000 : 0x0800 )
+#define SOFEOP_TO_CPU_EN(x) ( (x) ? 0x1000 : 0x0400 )
+
+/* SIE msg registers */
+#define SIEMSG_REG(x) ( (x) ? 0x0148 : 0x0144 )
+
+#define HUSB_TDListDone 0x1000
+
+#define CY_HCD_BUF_ADDR 0x500 /* Base address for host */
+#define SIE_TD_SIZE 0x200 /* size of the td list */
+#define SIE_TD_BUF_SIZE 0x400 /* size of the data buffer */
+
+#define SIE_TD_OFFSET(host) ( (host) ? (SIE_TD_SIZE+SIE_TD_BUF_SIZE) : 0 )
+#define SIE_BUF_OFFSET(host) (SIE_TD_OFFSET(host) + SIE_TD_SIZE)
+
+/* ---------------------------------------------------------------------
+ * Driver data structures
+ */
+
+struct c67x00_device;
+
+/**
+ * struct c67x00_lcp
+ */
+struct c67x00_lcp {
+ /* Internal use only */
+ struct mutex mutex;
+ struct completion msg_received;
+ u16 last_msg;
+};
+
+/**
+ * struct c67x00_lcp_data
+ */
+#define COMM_REGS 14
+struct c67x00_lcp_int_data {
+ u16 regs[COMM_REGS];
+};
+
+/**
+ * struct c67x00_sie - Common data associated with an SIE
+ * @lock: lock to protect this struct
+ * @private_data: subdriver dependent data
+ * @pdev: platform device associated with this SIE, created in c67x00-drv.c
+ * @irq: subdriver depenent irq handler, set NULL when not used
+ * @msg_received: called when an SIEmsg has been received
+ * @dev: link to common driver structure
+ * @sie_num: SIE number on chip, starting from 0
+ * @mode: SIE mode (host/peripheral/otg/not used)
+ *
+ * Each SIE has a separate platform_device associated with it, because the
+ * hcd needs such a device for itself (TODO: use a struct device instead
+ * of a new platform device).
+ */
+struct c67x00_sie {
+ /* Entries to be used by the subdrivers */
+ spinlock_t lock; /* protect this structure */
+ void *private_data;
+ struct platform_device *pdev;
+ void (*irq) (struct c67x00_sie * sie, u16 msg);
+
+ /* Read only: */
+ struct c67x00_device *dev;
+ int sie_num;
+ int mode;
+};
+
+#define sie_dev(s) (&(s)->pdev->dev)
+
+struct c67x00_hpi {
+ void __iomem *base;
+ int regstep;
+};
+
+#define C67X00_SIES 2
+#define C67X00_PORTS 2
+
+/**
+ * struct c67x00_device - Common data structure for a c67x00 instance
+ * @hpi: hpi addresses
+ * @sie: array of sie's on this chip
+ * @pdata: configuration provided by the platform
+ * @hw_lock: hardware lock
+ * @int_status: interrupt status register, only valid in_interrupt()
+ * @lcp: link control protocol dependent data
+ */
+struct c67x00_device {
+ struct c67x00_hpi hpi;
+ struct c67x00_sie sie[C67X00_SIES];
+ struct platform_device *pdev;
+ struct c67x00_platform_data *pdata;
+ spinlock_t hw_lock;
+
+ u16 int_status;
+ struct c67x00_lcp lcp;
+};
+
+/* ---------------------------------------------------------------------
+ * HCD setup and teardown hooks; defined here because these are the
+ * only routines which are called directly by the core driver.
+ */
+extern int c67x00_hcd_probe(struct c67x00_sie *sie);
+extern void c67x00_hcd_remove(struct c67x00_sie *sie);
+
+/* ---------------------------------------------------------------------
+ * Low level interface functions
+ */
+
+/* Host Port Interface (hpi) functions */
+u16 c67x00_ll_hpi_status(struct c67x00_device *dev);
+void c67x00_ll_hpi_reg_init(struct c67x00_device *dev);
+void c67x00_ll_hpi_enable_sofeop(struct c67x00_sie *sie);
+void c67x00_ll_hpi_disable_sofeop(struct c67x00_sie *sie);
+
+/* General functions */
+u16 c67x00_ll_get_siemsg(struct c67x00_device *dev, int sie);
+void c67x00_ll_set_siemsg(struct c67x00_device *dev, int sie, u16 val);
+u16 c67x00_ll_get_usb_ctl(struct c67x00_sie *sie);
+
+/* Host specific functions */
+void c67x00_ll_set_husb_eot(struct c67x00_device *dev, u16 value);
+void c67x00_ll_husb_reset(struct c67x00_sie *sie, int port);
+void c67x00_ll_husb_set_current_td(struct c67x00_sie *sie, u16 addr);
+u16 c67x00_ll_husb_get_current_td(struct c67x00_sie *sie);
+void c67x00_ll_husb_clear_status(struct c67x00_sie *sie, u16 bits);
+u16 c67x00_ll_husb_get_status(struct c67x00_sie *sie);
+u16 c67x00_ll_husb_get_frame(struct c67x00_sie *sie);
+void c67x00_ll_husb_init_host_port(struct c67x00_sie *sie);
+void c67x00_ll_husb_reset_port(struct c67x00_sie *sie, int port);
+
+/* Slave specific functions */
+void c67x00_ll_susb_init(struct c67x00_sie *sie);
+
+/* Read and write to memory */
+void c67x00_ll_hpi_write_mem_le16(struct c67x00_device *dev, u16 addr,
+ int len, char *data);
+void c67x00_ll_hpi_read_mem_le16(struct c67x00_device *dev, u16 addr,
+ int len, char *data);
+
+/* Called by c67x00_irq to handle lcp interrupts */
+void c67x00_ll_irq(struct c67x00_device *dev);
+
+/* Setup and teardown */
+void c67x00_ll_init(struct c67x00_device *dev);
+void c67x00_ll_release(struct c67x00_device *dev);
+int c67x00_ll_reset(struct c67x00_device *dev);
+
+#endif /* _USB_C67X00_H */
^ permalink raw reply related
* [PATCH v3 2/4] USB: add Cypress c67x00 OTG controller core driver
From: Grant Likely @ 2008-01-02 6:21 UTC (permalink / raw)
To: linux-usb-devel, dbrownell, gregkh, linuxppc-dev, jacmet, stern
In-Reply-To: <20080102062119.15073.22456.stgit@trillian.secretlab.ca>
From: Grant Likely <grant.likely@secretlab.ca>
This patch add the core driver for the c67x00 USB OTG controller. The core
driver is responsible for the platform bus binding and creating either
USB HCD or USB Gadget instances for each of the serial interface engines
on the chip.
This driver does not directly implement the HCD or gadget behaviours; it
just controls access to the chip.
Signed-off-by: Grant Likely <grant.likely@secretlab.ca>
---
drivers/usb/c67x00/c67x00-drv.c | 278 +++++++++++++++++++++++++++++++++++++++
include/linux/usb/c67x00.h | 45 ++++++
2 files changed, 323 insertions(+), 0 deletions(-)
diff --git a/drivers/usb/c67x00/c67x00-drv.c b/drivers/usb/c67x00/c67x00-drv.c
new file mode 100644
index 0000000..0f0720a
--- /dev/null
+++ b/drivers/usb/c67x00/c67x00-drv.c
@@ -0,0 +1,278 @@
+/*
+ * c67x00-drv.c: Cypress C67X00 USB Common infrastructure
+ *
+ * Copyright (C) 2006-2007 Barco N.V.
+ * Derived from the Cypress cy7c67200/300 ezusb linux driver and
+ * based on multiple host controller drivers inside the linux kernel.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * 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 for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+ * MA 02110-1301 USA.
+ */
+
+/*
+ * This file implements the common infrastructure for using the c67x00.
+ * It is both the link between the platform configuration and subdrivers and
+ * the link between the common hardware parts and the subdrivers (e.g.
+ * interrupt handling).
+ *
+ * The c67x00 has 2 SIE's (serial interface engine) wich can be configured
+ * to be host, device or OTG (with some limitations, E.G. only SIE1 can be OTG).
+ *
+ * Depending on the platform configuration, the SIE's are created (setup_sie)
+ * and the corresponding subdriver is initialized (c67x00_probe_sie).
+ */
+
+#include <linux/device.h>
+#include <linux/list.h>
+#include <linux/usb.h>
+#include <linux/usb/c67x00.h>
+#include <asm/io.h>
+
+#include "c67x00.h"
+
+static struct platform_driver c67x00_driver;
+
+static void
+c67x00_setup_sie(struct c67x00_sie *sie, struct c67x00_device *dev, int sie_num)
+{
+ static unsigned int id = 0;
+
+ /* Fill in needed attributes */
+ sie->pdev = platform_device_register_simple("c67x00_sie",
+ id++, NULL, 0);
+ /* driver used in hub.c: hub_port_init */
+ sie->pdev->dev.driver = &c67x00_driver.driver;
+ spin_lock_init(&sie->lock);
+ sie->dev = dev;
+ sie->sie_num = sie_num;
+ sie->mode = c67x00_sie_config(dev->pdata->sie_config, sie_num);
+}
+
+static void c67x00_teardown_sie(struct c67x00_sie *sie)
+{
+ sie->pdev->dev.driver = NULL;
+ platform_device_unregister(sie->pdev);
+}
+
+/* ------------------------------------------------------------------ */
+
+static void c67x00_probe_sie(struct c67x00_sie *sie)
+{
+ switch (c67x00_sie_config(sie->dev->pdata->sie_config, sie->sie_num)) {
+ case C67X00_SIE_HOST:
+ c67x00_hcd_probe(sie);
+ break;
+
+ case C67X00_SIE_UNUSED:
+ dev_info(sie_dev(sie),
+ "Not using SIE %d as requested\n", sie->sie_num);
+ break;
+
+ default:
+ dev_err(sie_dev(sie),
+ "Unsupported configuration: 0x%x for SIE %d\n",
+ c67x00_sie_config(sie->dev->pdata->sie_config,
+ sie->sie_num), sie->sie_num);
+ break;
+ }
+}
+
+static void c67x00_remove_sie(struct c67x00_sie *sie)
+{
+ switch (c67x00_sie_config(sie->dev->pdata->sie_config, sie->sie_num)) {
+ case C67X00_SIE_HOST:
+ c67x00_hcd_remove(sie);
+ break;
+
+ default:
+ break;
+ }
+}
+
+/* ------------------------------------------------------------------ */
+
+static irqreturn_t c67x00_irq(int irq, void *__dev)
+{
+ struct c67x00_device *c67x00 = __dev;
+ struct c67x00_sie *sie;
+ u16 msg;
+ int i, count = 8;
+
+ c67x00->int_status = c67x00_ll_hpi_status(c67x00);
+ if (!c67x00->int_status)
+ return IRQ_NONE;
+
+ while (c67x00->int_status != 0 && (count-- >= 0)) {
+ c67x00_ll_irq(c67x00);
+ for (i = 0; i < C67X00_SIES; i++) {
+ sie = &c67x00->sie[i];
+ msg = 0;
+ spin_lock(&sie->lock);
+ if (c67x00->int_status & SIEMSG_FLAG(sie->sie_num))
+ msg = c67x00_ll_get_siemsg(c67x00,sie->sie_num);
+ if (sie->irq)
+ sie->irq(sie, msg);
+ spin_unlock(&sie->lock);
+ }
+ c67x00->int_status = c67x00_ll_hpi_status(c67x00);
+ }
+
+ if (c67x00->int_status)
+ dev_warn(&c67x00->pdev->dev, "Not all interrupts handled! "
+ "status = 0x%04x\n", c67x00->int_status);
+
+ return IRQ_HANDLED;
+}
+
+/* ---------------------------------------------------------------------
+ * Platform bus binding
+ */
+
+static int __devinit c67x00_drv_probe(struct platform_device *pdev)
+{
+ struct c67x00_device *c67x00;
+ struct c67x00_platform_data *pdata;
+ struct resource *res, *res2;
+ int ret, i;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!res)
+ return -ENODEV;
+
+ res2 = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
+ if (!res2)
+ return -ENODEV;
+
+ pdata = pdev->dev.platform_data;
+ if (!pdata)
+ return -ENODEV;
+
+ c67x00 = kzalloc(sizeof(*c67x00), GFP_KERNEL);
+ if (!c67x00)
+ return -ENOMEM;
+
+ if (!request_mem_region(res->start, res->end - res->start + 1,
+ pdev->name)) {
+ dev_err(&pdev->dev, "Memory region busy\n");
+ ret = -EBUSY;
+ goto request_mem_failed;
+ }
+ c67x00->hpi.base = ioremap(res->start, res->end - res->start + 1);
+ if (!c67x00->hpi.base) {
+ dev_err(&pdev->dev, "Unable to map HPI registers\n");
+ ret = -EIO;
+ goto map_failed;
+ }
+
+ spin_lock_init(&c67x00->hw_lock);
+ c67x00->hpi.regstep = pdata->hpi_regstep;
+ c67x00->pdata = pdev->dev.platform_data;
+ c67x00->pdev = pdev;
+
+ for (i = 0; i < C67X00_SIES; i++)
+ c67x00_setup_sie(&c67x00->sie[i], c67x00, i);
+
+ c67x00_ll_init(c67x00);
+ c67x00_ll_hpi_reg_init(c67x00);
+
+ dev_info(&pdev->dev, "USB OTG controller, p:0x%x, v:0x%p, irq:%i\n",
+ res->start, c67x00->hpi.base, res2->start);
+
+ ret = request_irq(res2->start, c67x00_irq, 0, pdev->name, c67x00);
+ if (ret) {
+ dev_err(&pdev->dev, "Cannot claim IRQ\n");
+ goto request_irq_failed;
+ }
+
+ ret = c67x00_ll_reset(c67x00);
+ if (ret) {
+ dev_err(&pdev->dev, "Device reset failed\n");
+ goto reset_failed;
+ }
+
+ for (i = 0; i < C67X00_SIES; i++)
+ c67x00_probe_sie(&c67x00->sie[i]);
+
+ platform_set_drvdata(pdev, c67x00);
+
+ return 0;
+
+ reset_failed:
+ free_irq(res2->start, c67x00);
+ request_irq_failed:
+ iounmap(c67x00->hpi.base);
+ map_failed:
+ release_mem_region(res->start, res->end - res->start + 1);
+ request_mem_failed:
+ kfree(c67x00);
+
+ return ret;
+}
+
+static int __devexit c67x00_drv_remove(struct platform_device *pdev)
+{
+ struct c67x00_device *c67x00 = platform_get_drvdata(pdev);
+ struct resource *res;
+ int i;
+
+ for (i = 0; i < C67X00_SIES; i++) {
+ c67x00_remove_sie(&c67x00->sie[i]);
+ c67x00_teardown_sie(&c67x00->sie[i]);
+ }
+
+ c67x00_ll_release(c67x00);
+
+ res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
+ if (res)
+ free_irq(res->start, c67x00);
+
+ iounmap(c67x00->hpi.base);
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (res)
+ release_mem_region(res->start, res->end - res->start + 1);
+
+ kfree(c67x00);
+
+ return 0;
+}
+
+static struct platform_driver c67x00_driver = {
+ .probe = c67x00_drv_probe,
+ .remove = __devexit_p(c67x00_drv_remove),
+ .driver = {
+ .owner = THIS_MODULE,
+ .name = "c67x00",
+ },
+};
+
+static int __init c67x00_init(void)
+{
+ if (usb_disabled())
+ return -ENODEV;
+
+ return platform_driver_register(&c67x00_driver);
+}
+module_init(c67x00_init);
+
+static void __exit c67x00_exit(void)
+{
+ platform_driver_unregister(&c67x00_driver);
+}
+module_exit(c67x00_exit);
+
+MODULE_AUTHOR("Peter Korsgaard, Jan Veldeman, Grant Likely");
+MODULE_DESCRIPTION("Cypress C67X00 USB Controller Driver");
+MODULE_LICENSE("GPL");
diff --git a/include/linux/usb/c67x00.h b/include/linux/usb/c67x00.h
new file mode 100644
index 0000000..80b1a87
--- /dev/null
+++ b/include/linux/usb/c67x00.h
@@ -0,0 +1,45 @@
+/*
+ * usb_c67x00.h: platform definitions for the Cypress C67X00 USB chip
+ *
+ * Copyright (C) 2006-2007 Barco N.V.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * 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 for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+ * MA 02110-1301 USA.
+ */
+
+#ifndef _LINUX_USB_C67X00_H
+#define _LINUX_USB_C67X00_H
+
+/* SIE configuration */
+#define C67X00_SIE_UNUSED 0
+#define C67X00_SIE_HOST 1
+#define C67X00_SIE_PERIPHERAL 2
+
+#define c67x00_sie_config(config, n) (((config)>>(4*(n)))&0x3)
+
+#define C67X00_SIE1_UNUSED (C67X00_SIE_UNUSED << 0)
+#define C67X00_SIE1_HOST (C67X00_SIE_HOST << 0)
+#define C67X00_SIE1_PERIPHERAL (C67X00_SIE_PERIPHERAL << 0)
+
+#define C67X00_SIE2_UNUSED (C67X00_SIE_UNUSED << 4)
+#define C67X00_SIE2_HOST (C67X00_SIE_HOST << 4)
+#define C67X00_SIE2_PERIPHERAL (C67X00_SIE_PERIPHERAL << 4)
+
+struct c67x00_platform_data {
+ int sie_config; /* SIEs config (C67X00_SIEx_*) */
+ unsigned long hpi_regstep; /* Step between HPI registers */
+};
+
+#endif /* _LINUX_USB_C67X00_H */
^ permalink raw reply related
* [PATCH] [POWERPC] therm_pm72: suppress some compile warnings
From: Stephen Rothwell @ 2008-01-02 5:25 UTC (permalink / raw)
To: paulus; +Cc: ppc-dev
We don't really care if any of these calls to device_create_file fails,
so just issue warnings in that case.
drivers/macintosh/therm_pm72.c: In function 'init_cpu_state':
drivers/macintosh/therm_pm72.c:1185: warning: ignoring return value of 'device_create_file', declared with attribute warn_unused_result
drivers/macintosh/therm_pm72.c:1186: warning: ignoring return value of 'device_create_file', declared with attribute warn_unused_result
drivers/macintosh/therm_pm72.c:1187: warning: ignoring return value of 'device_create_file', declared with attribute warn_unused_result
drivers/macintosh/therm_pm72.c:1188: warning: ignoring return value of 'device_create_file', declared with attribute warn_unused_result
drivers/macintosh/therm_pm72.c:1189: warning: ignoring return value of 'device_create_file', declared with attribute warn_unused_result
drivers/macintosh/therm_pm72.c:1191: warning: ignoring return value of 'device_create_file', declared with attribute warn_unused_result
drivers/macintosh/therm_pm72.c:1192: warning: ignoring return value of 'device_create_file', declared with attribute warn_unused_result
drivers/macintosh/therm_pm72.c:1193: warning: ignoring return value of 'device_create_file', declared with attribute warn_unused_result
drivers/macintosh/therm_pm72.c:1194: warning: ignoring return value of 'device_create_file', declared with attribute warn_unused_result
drivers/macintosh/therm_pm72.c:1195: warning: ignoring return value of 'device_create_file', declared with attribute warn_unused_result
drivers/macintosh/therm_pm72.c: In function 'init_backside_state':
drivers/macintosh/therm_pm72.c:1383: warning: ignoring return value of 'device_create_file', declared with attribute warn_unused_result
drivers/macintosh/therm_pm72.c:1384: warning: ignoring return value of 'device_create_file', declared with attribute warn_unused_result
drivers/macintosh/therm_pm72.c: In function 'init_drives_state':
drivers/macintosh/therm_pm72.c:1503: warning: ignoring return value of 'device_create_file', declared with attribute warn_unused_result
drivers/macintosh/therm_pm72.c:1504: warning: ignoring return value of 'device_create_file', declared with attribute warn_unused_result
drivers/macintosh/therm_pm72.c: In function 'init_dimms_state':
drivers/macintosh/therm_pm72.c:1625: warning: ignoring return value of 'device_create_file', declared with attribute warn_unused_result
drivers/macintosh/therm_pm72.c: In function 'init_slots_state':
drivers/macintosh/therm_pm72.c:1743: warning: ignoring return value of 'device_create_file', declared with attribute warn_unused_result
drivers/macintosh/therm_pm72.c:1744: warning: ignoring return value of 'device_create_file', declared with attribute warn_unused_result
Signed-off-by: Stephen Rothwell <sfr@canb.auug.org.au>
---
drivers/macintosh/therm_pm72.c | 55 +++++++++++++++++++++++++++------------
1 files changed, 38 insertions(+), 17 deletions(-)
diff --git a/drivers/macintosh/therm_pm72.c b/drivers/macintosh/therm_pm72.c
index 6fadc9a..1e0a69a 100644
--- a/drivers/macintosh/therm_pm72.c
+++ b/drivers/macintosh/therm_pm72.c
@@ -1157,6 +1157,8 @@ static void do_monitor_cpu_rack(struct cpu_pid_state *state)
*/
static int init_cpu_state(struct cpu_pid_state *state, int index)
{
+ int err;
+
state->index = index;
state->first = 1;
state->rpm = (cpu_pid_type == CPU_PID_TYPE_RACKMAC) ? 4000 : 1000;
@@ -1182,18 +1184,21 @@ static int init_cpu_state(struct cpu_pid_state *state, int index)
DBG("CPU %d Using %d power history entries\n", index, state->count_power);
if (index == 0) {
- device_create_file(&of_dev->dev, &dev_attr_cpu0_temperature);
- device_create_file(&of_dev->dev, &dev_attr_cpu0_voltage);
- device_create_file(&of_dev->dev, &dev_attr_cpu0_current);
- device_create_file(&of_dev->dev, &dev_attr_cpu0_exhaust_fan_rpm);
- device_create_file(&of_dev->dev, &dev_attr_cpu0_intake_fan_rpm);
+ err = device_create_file(&of_dev->dev, &dev_attr_cpu0_temperature);
+ err |= device_create_file(&of_dev->dev, &dev_attr_cpu0_voltage);
+ err |= device_create_file(&of_dev->dev, &dev_attr_cpu0_current);
+ err |= device_create_file(&of_dev->dev, &dev_attr_cpu0_exhaust_fan_rpm);
+ err |= device_create_file(&of_dev->dev, &dev_attr_cpu0_intake_fan_rpm);
} else {
- device_create_file(&of_dev->dev, &dev_attr_cpu1_temperature);
- device_create_file(&of_dev->dev, &dev_attr_cpu1_voltage);
- device_create_file(&of_dev->dev, &dev_attr_cpu1_current);
- device_create_file(&of_dev->dev, &dev_attr_cpu1_exhaust_fan_rpm);
- device_create_file(&of_dev->dev, &dev_attr_cpu1_intake_fan_rpm);
+ err = device_create_file(&of_dev->dev, &dev_attr_cpu1_temperature);
+ err |= device_create_file(&of_dev->dev, &dev_attr_cpu1_voltage);
+ err |= device_create_file(&of_dev->dev, &dev_attr_cpu1_current);
+ err |= device_create_file(&of_dev->dev, &dev_attr_cpu1_exhaust_fan_rpm);
+ err |= device_create_file(&of_dev->dev, &dev_attr_cpu1_intake_fan_rpm);
}
+ if (err)
+ printk(KERN_WARNING "Failed to create some of the atribute"
+ "files for CPU %d\n", index);
return 0;
fail:
@@ -1329,6 +1334,7 @@ static int init_backside_state(struct backside_pid_state *state)
{
struct device_node *u3;
int u3h = 1; /* conservative by default */
+ int err;
/*
* There are different PID params for machines with U3 and machines
@@ -1380,8 +1386,11 @@ static int init_backside_state(struct backside_pid_state *state)
if (state->monitor == NULL)
return -ENODEV;
- device_create_file(&of_dev->dev, &dev_attr_backside_temperature);
- device_create_file(&of_dev->dev, &dev_attr_backside_fan_pwm);
+ err = device_create_file(&of_dev->dev, &dev_attr_backside_temperature);
+ err |= device_create_file(&of_dev->dev, &dev_attr_backside_fan_pwm);
+ if (err)
+ printk(KERN_WARNING "Failed to create attribute file(s)"
+ " for backside fan\n");
return 0;
}
@@ -1492,6 +1501,8 @@ static void do_monitor_drives(struct drives_pid_state *state)
*/
static int init_drives_state(struct drives_pid_state *state)
{
+ int err;
+
state->ticks = 1;
state->first = 1;
state->rpm = 1000;
@@ -1500,8 +1511,11 @@ static int init_drives_state(struct drives_pid_state *state)
if (state->monitor == NULL)
return -ENODEV;
- device_create_file(&of_dev->dev, &dev_attr_drives_temperature);
- device_create_file(&of_dev->dev, &dev_attr_drives_fan_rpm);
+ err = device_create_file(&of_dev->dev, &dev_attr_drives_temperature);
+ err |= device_create_file(&of_dev->dev, &dev_attr_drives_fan_rpm);
+ if (err)
+ printk(KERN_WARNING "Failed to create attribute file(s)"
+ " for drives bay fan\n");
return 0;
}
@@ -1622,7 +1636,9 @@ static int init_dimms_state(struct dimm_pid_state *state)
if (state->monitor == NULL)
return -ENODEV;
- device_create_file(&of_dev->dev, &dev_attr_dimms_temperature);
+ if (device_create_file(&of_dev->dev, &dev_attr_dimms_temperature))
+ printk(KERN_WARNING "Failed to create attribute file"
+ " for DIMM temperature\n");
return 0;
}
@@ -1732,6 +1748,8 @@ static void do_monitor_slots(struct slots_pid_state *state)
*/
static int init_slots_state(struct slots_pid_state *state)
{
+ int err;
+
state->ticks = 1;
state->first = 1;
state->pwm = 50;
@@ -1740,8 +1758,11 @@ static int init_slots_state(struct slots_pid_state *state)
if (state->monitor == NULL)
return -ENODEV;
- device_create_file(&of_dev->dev, &dev_attr_slots_temperature);
- device_create_file(&of_dev->dev, &dev_attr_slots_fan_pwm);
+ err = device_create_file(&of_dev->dev, &dev_attr_slots_temperature);
+ err |= device_create_file(&of_dev->dev, &dev_attr_slots_fan_pwm);
+ if (err)
+ printk(KERN_WARNING "Failed to create attribute file(s)"
+ " for slots bay fan\n");
return 0;
}
--
1.5.3.7
--
Cheers,
Stephen Rothwell sfr@canb.auug.org.au
http://www.canb.auug.org.au/~sfr/
^ permalink raw reply related
* Please pull powerpc.git merge branch
From: Paul Mackerras @ 2008-01-02 5:21 UTC (permalink / raw)
To: torvalds; +Cc: linuxppc-dev
Linus,
Please do
git pull \
git://git.kernel.org/pub/scm/linux/kernel/git/paulus/powerpc.git merge
to get a fix for a bug introduced in one of the two commits I asked
you to pull last time.
Thanks,
Paul.
arch/powerpc/platforms/cell/spufs/sched.c | 9 ---------
1 files changed, 0 insertions(+), 9 deletions(-)
commit a5a971129cc6155e26315fd28a450505ccc35fd8
Author: Paul Mackerras <paulus@samba.org>
Date: Wed Jan 2 15:56:30 2008 +1100
[POWERPC] Fix build failure on Cell when CONFIG_SPU_FS=y
Commit aed3a8c9bb1a8623a618232087c5ff62718e3b9a introduced a
definition of notify_spus_active in .../cell/spu_syscalls.c, and
another definition under #ifndef MODULE in .../cell/spufs/sched.c.
The latter is not necessary and causes the build to fail when
CONFIG_SPU_FS=y, so this removes it. It also removes the export
of do_notify_spus_active, which is unnecessary.
Signed-off-by: Paul Mackerras <paulus@samba.org>
Acked-by: Arnd Bergmann <arnd@arndb.de>
Acked-by: Jeremy Kerr <jk@ozlabs.org>
^ permalink raw reply
* Re: Please pull powerpc.git merge branch
From: Jeremy Kerr @ 2008-01-02 4:39 UTC (permalink / raw)
To: linuxppc-dev; +Cc: rrnelson, Paul Mackerras
In-Reply-To: <200712311712.46075.arnd@arndb.de>
Hi all,
> I'm still out of office and can't do a proper test on this in the
> way I should have done on the original patch, but your assessment and
> patch looks entirely correct.
Just built this for =y and =m, all seems fine.
Cheers,
Jeremy
^ permalink raw reply
* Re: Using C67x00 on PXA255 with ov511
From: Grant Likely @ 2008-01-02 4:34 UTC (permalink / raw)
To: LIU KAN, linuxppc-dev
In-Reply-To: <e99250190801011938w54ad1921oc85bb54fc0481b9d@mail.gmail.com>
On 1/1/08, LIU KAN <liukanh@gmail.com> wrote:
> Hello, Grant
>
> Thank you for the work on the patch of c67x00, I have use it
> successfully on PXA255, but I still have some problem.
> Now I can use c67x00 together with usb-disk in 2.6 kerenl at a
> writing speed around 5MB/s. But when I use c67x00 to connect webcam, which
> is drived by ov511, I get the mosaic like picture in attachment. And when I
> use cy7c67300 provided by cypress in 2.4 kernel with ov511, it work fine. Is
> this because the speed the c67x00 driver for iso_ transfers is too slow? or
> any other reason?
> How can I solve the problem?
Most likely the scheduling algorithm is not quite right for the
c67x00. I've had problems with that in the past myself. You can try
playing around with the parameters in claim_frame_bw() in
c67x00-sched.c. The web cam is probably using iso transfers so you
should try to find out if all the requested ISO transfers are actually
getting scheduled by the driver.
Please let me know what you find out. This driver still needs a lot
of tuning and your observations will help a lot.
Cheers,
g.
--
Grant Likely, B.Sc., P.Eng.
Secret Lab Technologies Ltd.
grant.likely@secretlab.ca
(403) 399-0195
^ permalink raw reply
* Re: [PATCH] ASoC drivers for the Freescale MPC8610 SoC
From: Jon Smirl @ 2008-01-02 4:27 UTC (permalink / raw)
To: Timur Tabi; +Cc: linuxppc-dev, alsa-devel
In-Reply-To: <11981089894052-git-send-email-timur@freescale.com>
On 12/19/07, Timur Tabi <timur@freescale.com> wrote:
> sound/soc/fsl/fsl_ssi.c | 614 +++++++++++++++++++
> sound/soc/fsl/fsl_ssi.h | 224 +++++++
I'm confused about this part. You built a driver for the mpc8610 ssi
port. This port has a device tree entry.
+ ssi@16000 {
+ compatible = "fsl,ssi";
+ cell-index = <0>;
+ reg = <16000 100>;
+ interrupt-parent = <&mpic>;
+ interrupts = <3e 2>;
+ fsl,mode = "i2s-slave";
+ codec {
+ compatible = "cirrus,cs4270";
+ /* MCLK source is a stand-alone oscillator */
+ bus-frequency = <bb8000>;
+ };
+ };
But then you don't create an of_platform_driver for this device.
Instead you create one for the fabric driver, struct
of_platform_driver mpc8610_hpcd_of_driver, and directly link the SSI
driver into it.
+static struct of_device_id mpc8610_hpcd_match[] = {
+ {
+ .compatible = "fsl,ssi",
+ },
+ {}
+};
+MODULE_DEVICE_TABLE(of, mpc8610_hpcd_match);
+
+static struct of_platform_driver mpc8610_hpcd_of_driver = {
+ .owner = THIS_MODULE,
+ .name = "mpc8610_hpcd",
+ .match_table = mpc8610_hpcd_match,
+ .probe = mpc8610_hpcd_probe,
+ .remove = mpc8610_hpcd_remove,
+};
static int mpc8610_hpcd_probe(struct of_device *ofdev,
const struct of_device_id *match)
{
.....
machine_data->dai.cpu_dai = fsl_ssi_create_dai(&ssi_info);
Isn't this two separate drivers that have been combined into one
driver? Or does the fsl_ssi channel only work on the mpc8610_hpcd?
This is the problem of knowing how to load the fabric driver that I
was talking about in the other threads. A device that can occur on
more than one chip ".compatible = "fsl,ssi"," is being used to pull in
a platform specific fabric driver, "mpc8610_hpcd". You can use the
kernel config system to select the right driver for ".compatible =
"fsl,ssi"," that matches you hardware and compile it in.
But that doesn't work in my environment. My generic channel is
"fsl,i2s". I have four different systems booting off from a shared
network drive. Each of these systems needs the common "fsl,i2s" driver
but they all four need different fabric drivers.
--
Jon Smirl
jonsmirl@gmail.com
^ permalink raw reply
* Re: [PATCH] [POWERPC] Update .gitignore files.
From: David Gibson @ 2008-01-02 0:38 UTC (permalink / raw)
To: Jochen Friedrich; +Cc: linuxppc-dev, paulus
In-Reply-To: <4779157D.2040305@scram.de>
On Mon, Dec 31, 2007 at 05:14:53PM +0100, Jochen Friedrich wrote:
> Update .gitignore as needed by dtc addition.
>
> Signed-off-by: Jochen Friedrich <jochen@scram.de>
Acked-by: David Gibson <david@gibson.dropbear.id.au>
--
David Gibson | I'll have my music baroque, and my code
david AT gibson.dropbear.id.au | minimalist, thank you. NOT _the_ _other_
| _way_ _around_!
http://www.ozlabs.org/~dgibson
^ permalink raw reply
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox