* Re: [PATCH 2/2] Add the of_find_i2c_device_by_node function, V4
From: Jon Smirl @ 2008-07-01 18:18 UTC (permalink / raw)
To: Jean Delvare; +Cc: Linuxppc-dev, Paul Mackerras, i2c
In-Reply-To: <9e4733910807011027m5802cf76n3f68b8692ff6832f@mail.gmail.com>
On 7/1/08, Jon Smirl <jonsmirl@gmail.com> wrote:
> On 7/1/08, Jean Delvare <khali@linux-fr.org> wrote:
>
> > On Tue, 1 Jul 2008 13:00:08 -0400, Jon Smirl wrote:
> > > On 7/1/08, Grant Likely <grant.likely@secretlab.ca> wrote:
> >
> > > > My preference is for things like of_spi and of_i2c to go with the
> > > > related busses; I think it makes more sense to keep all the I2C stuff
> > > > together, but I've already lost that battle once.
> > >
> > > This is a similar problem to adding aliases to the i2c driver drivers
> > > for the device tree names of the i2c devices. Instead we have code in
> > > drivers/of/of_i2c.c that tries to guess the translation from device
> > > tree to linux names. Adding aliases to the drivers would eliminate the
> > > need for of_find_i2c_driver().
> > >
> > > I've previously posted patches implementing device tree names in the
> > > drivers that used ifdef to only instantiate on powerpc builds. For
> > > example....
> > >
> > > diff --git a/drivers/i2c/chips/tps65010.c b/drivers/i2c/chips/tps65010.c
> > > index e07274d..9cd1770 100644
> > > --- a/drivers/i2c/chips/tps65010.c
> > > +++ b/drivers/i2c/chips/tps65010.c
> > > @@ -571,6 +571,10 @@ static const struct i2c_device_id tps65010_id[] = {
> > > { "tps65011", TPS65011 },
> > > { "tps65012", TPS65012 },
> > > { "tps65013", TPS65013 },
> > > + OF_ID("ti,tps65010", TPS65010)
> > > + OF_ID("ti,tps65011", TPS65011)
> > > + OF_ID("ti,tps65012", TPS65012)
> > > + OF_ID("ti,tps65013", TPS65013)
> > > { },
> > > };
> > > MODULE_DEVICE_TABLE(i2c, tps65010_id);
> >
> >
> > Yeah, yeah, you've been asking for this for months already, but it's
> > just not going to happen, sorry. You want to abuse the standard Linux
> > alias mechanism for your personal (i.e. openfirmware) use, but that's
> > bad. Linux drivers shouldn't have to know whether they are used in
> > openfirmware trees and what device names are used there. And device
> > names as seen by user-space shouldn't vary depending on whether the
> > device comes from an openfirmware tree or not - otherwise all
> > user-space apps need to learn about both naming conversions.
> >
> > Unsurprisingly, no other subsystem does what you propose.
>
>
> Then what are all of the PCI aliases doing?
>
> The only difference is that you are recognizing the PCI group as a
> naming authority and not recognizing the PowerPC device tree group.
> But on the PowerPC platform that is our naming authority. That's why I
> proposed adding the names on ifdefs so that they disappear on non
> PowerPC platforms.
>
> PS - adding an alias to a driver does not change the name of the
> driver. My PCI e1000 module has about 100 aliases but it is always
> e1000.
Here's my e1000e sysfs entry:
jonsmirl@terra:/sys/bus/pci/devices/0000:00:19.0$ ls
broken_parity_status device local_cpus power resource2 uevent
bus driver modalias resource subsystem vendor
class enable msi_bus resource0 subsystem_device
config irq net:eth0 resource1 subsystem_vendor
jonsmirl@terra:/sys/bus/pci/devices/0000:00:19.0$ cat modalias
pci:v00008086d0000104Bsv00001028sd000001DBbc02sc00i00
>>>> This is the module alias that was used to load the driver.
jonsmirl@terra:/sys/bus/pci/devices/0000:00:19.0$ ls -l driver
lrwxrwxrwx 1 root root 0 2008-07-01 08:52 driver ->
../../../bus/pci/drivers/e1000e
>>>> The driver is always e1000e no matter which alias was used to load it. "e1000e" is controled by the name field of the driver structure. That's the publicly visible name for the driver.
>>>> Adding the OF aliases would change the modalias entry, not the driver name.
The i2c implementation is adding a field to a device entry that
contains the driver name. No other device drivers I could find do
this.
jonsmirl@terra:/sys/bus/i2c/devices/1-0050$ ls
bus driver eeprom modalias name power subsystem uevent
jonsmirl@terra:/sys/bus/i2c/devices/1-0050$ cat name
eeprom
jonsmirl@terra:/sys/bus/i2c/devices/1-0050$ ls -l driver
lrwxrwxrwx 1 root root 0 2008-07-01 14:05 driver ->
../../../../bus/i2c/drivers/eeprom
jonsmirl@terra:/sys/bus/i2c/devices/1-0050$ cat modalias
jonsmirl@terra:/sys/bus/i2c/devices/1-0050$
I believe the correct way to get the driver name from sysfs is to
follow the driver link. The name field is probably legacy. Other
drivers in the system don't have a name entry on the device node.
Is the user space i2c code looking at the modalias entry?
--
Jon Smirl
jonsmirl@gmail.com
^ permalink raw reply
* [PATCH] Add the of_find_i2c_device_by_node function, standalone V1
From: Jon Smirl @ 2008-07-01 18:21 UTC (permalink / raw)
To: linuxppc-dev
Add the of_find_i2c_device_by_node function. This allows you to follow a reference in the device tree to an i2c device node and then locate the linux device instantiated by the device tree. Example use, an i2s codec controlled by i2c. Depends on patch exporting i2c root bus symbol.
Signed-off-by: Jon Smirl <jonsmirl@gmail.com>
---
drivers/of/of_i2c.c | 37 ++++++++++++++++++++++++++-----------
include/linux/of_i2c.h | 2 ++
2 files changed, 28 insertions(+), 11 deletions(-)
diff --git a/drivers/of/of_i2c.c b/drivers/of/of_i2c.c
index 715a444..ca69a16 100644
--- a/drivers/of/of_i2c.c
+++ b/drivers/of/of_i2c.c
@@ -75,7 +75,7 @@ static int of_find_i2c_driver(struct device_node *node,
void of_register_i2c_devices(struct i2c_adapter *adap,
struct device_node *adap_node)
{
- void *result;
+ struct i2c_client *i2c_dev;
struct device_node *node;
for_each_child_of_node(adap_node, node) {
@@ -90,29 +90,44 @@ void of_register_i2c_devices(struct i2c_adapter *adap,
continue;
}
- info.irq = irq_of_parse_and_map(node, 0);
- if (info.irq == NO_IRQ)
- info.irq = -1;
-
- if (of_find_i2c_driver(node, &info) < 0) {
- irq_dispose_mapping(info.irq);
+ if (of_find_i2c_driver(node, &info) < 0)
continue;
- }
+ info.irq = irq_of_parse_and_map(node, 0);
info.addr = *addr;
- request_module(info.type);
+ request_module("%s", info.type);
- result = i2c_new_device(adap, &info);
- if (result == NULL) {
+ i2c_dev = i2c_new_device(adap, &info);
+ if (i2c_dev == NULL) {
printk(KERN_ERR
"of-i2c: Failed to load driver for %s\n",
info.type);
irq_dispose_mapping(info.irq);
continue;
}
+
+ i2c_dev->dev.archdata.of_node = of_node_get(node);
}
}
EXPORT_SYMBOL(of_register_i2c_devices);
+static int of_dev_node_match(struct device *dev, void *data)
+{
+ return dev->archdata.of_node == data;
+}
+
+struct i2c_client *of_find_i2c_device_by_node(struct device_node *node)
+{
+ struct device *dev;
+
+ dev = bus_find_device(&i2c_bus_type, NULL, node,
+ of_dev_node_match);
+ if (!dev)
+ return NULL;
+
+ return to_i2c_client(dev);
+}
+EXPORT_SYMBOL(of_find_i2c_device_by_node);
+
MODULE_LICENSE("GPL");
diff --git a/include/linux/of_i2c.h b/include/linux/of_i2c.h
index bd2a870..17d5897 100644
--- a/include/linux/of_i2c.h
+++ b/include/linux/of_i2c.h
@@ -16,5 +16,7 @@
void of_register_i2c_devices(struct i2c_adapter *adap,
struct device_node *adap_node);
+struct i2c_client *of_find_i2c_device_by_node(struct device_node *node);
+
#endif /* __LINUX_OF_I2C_H */
^ permalink raw reply related
* Re: [PATCH 2/2] Add the of_find_i2c_device_by_node function, V4
From: Jean Delvare @ 2008-07-01 18:24 UTC (permalink / raw)
To: Jon Smirl; +Cc: Linuxppc-dev, Paul Mackerras, i2c
In-Reply-To: <9e4733910807011006t20c7689ctfe26931f96c84092@mail.gmail.com>
On Tue, 1 Jul 2008 13:06:37 -0400, Jon Smirl wrote:
> On 7/1/08, Jean Delvare <khali@linux-fr.org> wrote:
> > On Tue, 1 Jul 2008 10:45:18 -0600, Grant Likely wrote:
> > > But the other side of the coin is that each driver must have
> > > driver-specific OF code to translate data in the device tree to data
> > > usable by the driver. It doesn't make any sense to me for that stuff to
> > > live anywhere other that with the driver that it supports.
> >
> >
> > This code is glue between OF and subsystems. As with any glue code, you
> > can argue forever on which side you want to push the code to. Both
> > answers are valid.
> >
> > All I see on my personal side is that I don't have any system using OF
> > and no knowledge about it either, so I can't maintain of_i2c. So having
> > that file in drivers/of rather than drivers/i2c will make my life
> > easier for sure. While I'd guess that most (all?) OF-based systems have
> > an I2C bus, so whoever is responsible for drivers/of should be able to
> > maintain of_i2c.
>
> We could modify the Makefile for i2c core to get the source out of
> drivers/of and link it into i2c-core. That would remove the need to
> export symbols.
That might be a bit confusing, but could be done. I still don't know
what problem you are trying to solve though. As I repeated, exporting
i2c_bus_type is perfectly fine. All other bus types are always
exported. So if it's all you need from i2c-core, there's no problem and
we can stop the discussion here.
> Or you could move the file into the i2c directory and just put a note
> on it that Grant is the maintainer.
Assuming that developers will read the source code to find out who the
maintainer is, when we have told everyone to search for this
information in MAINTAINERS. I'd rather add an entry in MAINTAINERS, but
even then I have a doubt that people will get it. My experience is that
people map maintainer names to directories and that's about it. That's
one of the reasons why I wanted to move the i2c device drivers from
drivers/i2c/chips to drivers/hwmon, drivers/rtc, drivers/gpio, etc.
--
Jean Delvare
^ permalink raw reply
* Re: [PATCH 2/2] Add the of_find_i2c_device_by_node function, V4
From: Jean Delvare @ 2008-07-01 18:51 UTC (permalink / raw)
To: Jon Smirl; +Cc: Linuxppc-dev, Paul Mackerras, i2c
In-Reply-To: <9e4733910807011027m5802cf76n3f68b8692ff6832f@mail.gmail.com>
On Tue, 1 Jul 2008 13:27:57 -0400, Jon Smirl wrote:
> On 7/1/08, Jean Delvare <khali@linux-fr.org> wrote:
> > On Tue, 1 Jul 2008 13:00:08 -0400, Jon Smirl wrote:
> > > On 7/1/08, Grant Likely <grant.likely@secretlab.ca> wrote:
> >
> > > > My preference is for things like of_spi and of_i2c to go with the
> > > > related busses; I think it makes more sense to keep all the I2C stuff
> > > > together, but I've already lost that battle once.
> > >
> > > This is a similar problem to adding aliases to the i2c driver drivers
> > > for the device tree names of the i2c devices. Instead we have code in
> > > drivers/of/of_i2c.c that tries to guess the translation from device
> > > tree to linux names. Adding aliases to the drivers would eliminate the
> > > need for of_find_i2c_driver().
> > >
> > > I've previously posted patches implementing device tree names in the
> > > drivers that used ifdef to only instantiate on powerpc builds. For
> > > example....
> > >
> > > diff --git a/drivers/i2c/chips/tps65010.c b/drivers/i2c/chips/tps65010.c
> > > index e07274d..9cd1770 100644
> > > --- a/drivers/i2c/chips/tps65010.c
> > > +++ b/drivers/i2c/chips/tps65010.c
> > > @@ -571,6 +571,10 @@ static const struct i2c_device_id tps65010_id[] = {
> > > { "tps65011", TPS65011 },
> > > { "tps65012", TPS65012 },
> > > { "tps65013", TPS65013 },
> > > + OF_ID("ti,tps65010", TPS65010)
> > > + OF_ID("ti,tps65011", TPS65011)
> > > + OF_ID("ti,tps65012", TPS65012)
> > > + OF_ID("ti,tps65013", TPS65013)
> > > { },
> > > };
> > > MODULE_DEVICE_TABLE(i2c, tps65010_id);
> >
> >
> > Yeah, yeah, you've been asking for this for months already, but it's
> > just not going to happen, sorry. You want to abuse the standard Linux
> > alias mechanism for your personal (i.e. openfirmware) use, but that's
> > bad. Linux drivers shouldn't have to know whether they are used in
> > openfirmware trees and what device names are used there. And device
> > names as seen by user-space shouldn't vary depending on whether the
> > device comes from an openfirmware tree or not - otherwise all
> > user-space apps need to learn about both naming conversions.
> >
> > Unsurprisingly, no other subsystem does what you propose.
>
> Then what are all of the PCI aliases doing?
>
> The only difference is that you are recognizing the PCI group as a
> naming authority and not recognizing the PowerPC device tree group.
> But on the PowerPC platform that is our naming authority. That's why I
> proposed adding the names on ifdefs so that they disappear on non
> PowerPC platforms.
You're comparing PCI devices those ID is built-in, with I2C devices
with no ID. This just can't compare.
> PS - adding an alias to a driver does not change the name of the
> driver. My PCI e1000 module has about 100 aliases but it is always
> e1000.
I said device names, not driver names. Linux I2C devices have a name
attribute which contains the (Linux) device name. That's something a
number of user-space applications are relying on.
--
Jean Delvare
^ permalink raw reply
* Re: [PATCH v2] Parameterize EMAC Multicast Match Handling
From: Stefan Roese @ 2008-07-01 19:42 UTC (permalink / raw)
To: Grant Erickson; +Cc: linuxppc-dev
In-Reply-To: <C48FC1C4.10254%gerickson@nuovations.com>
On Tuesday 01 July 2008, Grant Erickson wrote:
> > Yes, this was my feeling too. Not the size of the dtb but more the
> > increased complexity of the EMAC device node. I would prefer Ben's idea
> > with this new compatible entry too.
>
> In terms of the device tree expression, you would both favor something akin
> to the following?
>
> - compatible = "ibm,emac-405exr", "ibm,emac4";
> + compatible = "ibm,emac-405exr", "ibm,emac4",
> "ibm,emac4sync";
If this is how IBM/AMCC call this "new" EMAC version, then yes.
Best regards,
Stefan
^ permalink raw reply
* Re: [PATCH 27/60] microblaze_v4: virtualization
From: Adrian Bunk @ 2008-07-01 20:46 UTC (permalink / raw)
To: monstr
Cc: linux-arch, alan, Michal Simek, vapier.adi, arnd, matthew,
microblaze-uclinux, linux-kernel, drepper, linuxppc-dev,
will.newton, hpa, John.Linn, john.williams
In-Reply-To: <1214483429-32360-28-git-send-email-monstr@seznam.cz>
On Thu, Jun 26, 2008 at 02:29:56PM +0200, monstr@seznam.cz wrote:
> From: Michal Simek <monstr@monstr.eu>
>
>
> Signed-off-by: Michal Simek <monstr@monstr.eu>
> ---
> include/asm-microblaze/kvm.h | 1 +
> 1 files changed, 1 insertions(+), 0 deletions(-)
> create mode 100644 include/asm-microblaze/kvm.h
>
> diff --git a/include/asm-microblaze/kvm.h b/include/asm-microblaze/kvm.h
> new file mode 100644
> index 0000000..01c5e79
> --- /dev/null
> +++ b/include/asm-microblaze/kvm.h
> @@ -0,0 +1 @@
> +/* Microblaze does not support KVM */
There's a patch pending for 2.6.27 that will remove the requirement for
this empty header.
cu
Adrian
--
"Is there not promise of rain?" Ling Tan asked suddenly out
of the darkness. There had been need of rain for many days.
"Only a promise," Lao Er said.
Pearl S. Buck - Dragon Seed
^ permalink raw reply
* [PATCH] powerpc/mpc5200: Fix lite5200b suspend/resume
From: Grant Likely @ 2008-07-01 21:12 UTC (permalink / raw)
To: linuxppc-dev, plasm
From: Tim Yamin <plasm@roo.me.uk>
Suspend/resume ("echo mem > /sys/power/state") does not work with
vanilla kernels -- the system does not suspend correctly and just
hangs. This patch fixes this so suspend/resume works:
1) of_iomap does not map the whole 0xC000 of the MPC5200 immr so
saving registers does not work.
2) PCI registers need to be saved and restored.
Signed-off-by: Tim Yamin <plasm@roo.me.uk>
Signed-off-by: Grant Likely <grant.likely@secretlab.ca>
---
Tim, please test this version. I fixed a couple of bugs and want to make
sure I haven't broken anything. Once you ack it I'll ask Paul to pull it
into 2.6.26.
g.
arch/powerpc/platforms/52xx/lite5200_pm.c | 14 +++++++++++++-
1 files changed, 13 insertions(+), 1 deletions(-)
diff --git a/arch/powerpc/platforms/52xx/lite5200_pm.c b/arch/powerpc/platforms/52xx/lite5200_pm.c
index 41c7fd9..fe92e65 100644
--- a/arch/powerpc/platforms/52xx/lite5200_pm.c
+++ b/arch/powerpc/platforms/52xx/lite5200_pm.c
@@ -14,6 +14,7 @@ static struct mpc52xx_sdma __iomem *bes;
static struct mpc52xx_xlb __iomem *xlb;
static struct mpc52xx_gpio __iomem *gps;
static struct mpc52xx_gpio_wkup __iomem *gpw;
+static void __iomem *pci;
static void __iomem *sram;
static const int sram_size = 0x4000; /* 16 kBytes */
static void __iomem *mbar;
@@ -50,6 +51,8 @@ static int lite5200_pm_prepare(void)
{ .type = "builtin", .compatible = "mpc5200", }, /* efika */
{}
};
+ u64 regaddr64 = 0;
+ const u32 *regaddr_p;
/* deep sleep? let mpc52xx code handle that */
if (lite5200_pm_target_state == PM_SUSPEND_STANDBY)
@@ -60,8 +63,12 @@ static int lite5200_pm_prepare(void)
/* map registers */
np = of_find_matching_node(NULL, immr_ids);
- mbar = of_iomap(np, 0);
+ regaddr_p = of_get_address(np, 0, NULL, NULL);
+ if (regaddr_p)
+ regaddr64 = of_translate_address(np, regaddr_p);
of_node_put(np);
+
+ mbar = ioremap((u32) regaddr64, 0xC000);
if (!mbar) {
printk(KERN_ERR "%s:%i Error mapping registers\n", __func__, __LINE__);
return -ENOSYS;
@@ -71,6 +78,7 @@ static int lite5200_pm_prepare(void)
pic = mbar + 0x500;
gps = mbar + 0xb00;
gpw = mbar + 0xc00;
+ pci = mbar + 0xd00;
bes = mbar + 0x1200;
xlb = mbar + 0x1f00;
sram = mbar + 0x8000;
@@ -85,6 +93,7 @@ static struct mpc52xx_sdma sbes;
static struct mpc52xx_xlb sxlb;
static struct mpc52xx_gpio sgps;
static struct mpc52xx_gpio_wkup sgpw;
+static char spci[0x200];
static void lite5200_save_regs(void)
{
@@ -94,6 +103,7 @@ static void lite5200_save_regs(void)
_memcpy_fromio(&sxlb, xlb, sizeof(*xlb));
_memcpy_fromio(&sgps, gps, sizeof(*gps));
_memcpy_fromio(&sgpw, gpw, sizeof(*gpw));
+ _memcpy_fromio(spci, pci, 0x200);
_memcpy_fromio(saved_sram, sram, sram_size);
}
@@ -103,6 +113,8 @@ static void lite5200_restore_regs(void)
int i;
_memcpy_toio(sram, saved_sram, sram_size);
+ /* PCI Configuration */
+ _memcpy_toio(pci, spci, 0x200);
/*
* GPIOs. Interrupt Master Enable has higher address then other
^ permalink raw reply related
* Please pull into 2.6.26
From: Grant Likely @ 2008-07-01 22:04 UTC (permalink / raw)
To: Josh Boyer, Paul Mackerras, linuxppc-dev, John Linn
Hi Josh & Paul,
Here are a couple of last minute bug fixes for 2.6.26. These are both
Xilinx 4xx related.
Could either of you please pull the tree?
Thanks,
g.
The following changes since commit 1702b52092e9a6d05398d3f9581ddc050ef00d06:
Linus Torvalds (1):
Merge git://git.kernel.org/.../mchehab/v4l-dvb
are available in the git repository at:
git://git.secretlab.ca/git/linux-2.6-virtex.git virtex-for-2.6.26
John Linn (2):
powerpc/bootwrapper: update for initrd with simpleImage
powerpc/legacy_serial: Bail if reg-offset/shift properties are present
arch/powerpc/boot/Makefile | 3 ++-
arch/powerpc/kernel/legacy_serial.c | 5 +++++
2 files changed, 7 insertions(+), 1 deletions(-)
--
Grant Likely, B.Sc., P.Eng.
Secret Lab Technologies Ltd.
^ permalink raw reply
* Re: [PATCH]: [MPC5200] (v2) Add ATA DMA support
From: Grant Likely @ 2008-07-01 23:49 UTC (permalink / raw)
To: Tim Yamin; +Cc: linuxppc-dev
In-Reply-To: <792f5f410806270544p69c773b9o9df4a5618d4babe1@mail.gmail.com>
On Fri, Jun 27, 2008 at 01:44:08PM +0100, Tim Yamin wrote:
> diff -Nurp linux-2.6.26-rc6/arch/powerpc/sysdev/bestcomm/bestcomm.c linux-2.6.26-rc6.new/arch/powerpc/sysdev/bestcomm/bestcomm.c
> --- linux-2.6.26-rc6/arch/powerpc/sysdev/bestcomm/bestcomm.c 2008-03-18 15:49:53.000000000 +0000
> +++ linux-2.6.26-rc6.new/arch/powerpc/sysdev/bestcomm/bestcomm.c 2008-04-15 10:42:38.000000000 +0100
> @@ -330,11 +330,10 @@
> /* Init 'always' initiator */
> out_8(&bcom_eng->regs->ipr[BCOM_INITIATOR_ALWAYS], BCOM_IPR_ALWAYS);
>
> - /* Disable COMM Bus Prefetch on the original 5200; it's broken */
> - if ((mfspr(SPRN_SVR) & MPC5200_SVR_MASK) == MPC5200_SVR) {
> - regval = in_be16(&bcom_eng->regs->PtdCntrl);
> - out_be16(&bcom_eng->regs->PtdCntrl, regval | 1);
> - }
> + /* Disable COMM Bus Prefetch; ATA DMA does not work properly with it
> + enabled. */
> + regval = in_be16(&bcom_eng->regs->PtdCntrl);
> + out_be16(&bcom_eng->regs->PtdCntrl, regval | 1);
This could have a performance impact on existing systems that don't
use/need ATA DMA.
Please run next version through checkpatch. There's lots of minor
cosmetic stuff, but there are also a few important errors.
> diff -Nurp linux-2.6.26-rc6/arch/powerpc/sysdev/bestcomm/bestcomm.h linux-2.6.26-rc6.new/arch/powerpc/sysdev/bestcomm/bestcomm.h
> --- linux-2.6.26-rc6/arch/powerpc/sysdev/bestcomm/bestcomm.h 2008-03-18 15:49:53.000000000 +0000
> +++ linux-2.6.26-rc6.new/arch/powerpc/sysdev/bestcomm/bestcomm.h 2008-04-15 10:42:38.000000000 +0100
> @@ -17,6 +17,7 @@
> #define __BESTCOMM_H__
>
> struct bcom_bd; /* defined later on ... */
> +struct bcom_bd_2;
>
>
> /* ======================================================================== */
> @@ -49,6 +50,22 @@
> void* priv;
> };
>
> +struct bcom_task_2 {
> + unsigned int tasknum;
> + unsigned int flags;
> + int irq;
> +
> + struct bcom_bd_2 *bd;
> + phys_addr_t bd_pa;
> + void **cookie;
> + unsigned short index;
> + unsigned short outdex;
> + unsigned int num_bd;
> + unsigned int bd_size;
> +
> + void* priv;
> +};
> +
Oh, ugly. The only difference seems to be that bcom_bd_2 is 1 word
larger than bcom_bd. There must be a better way to do this rather
than just duplicating all the functions and structures.. It would
probably be better to use bd_size to determine how to dereference an
index into the *bd list. It will require a bit of refactoring, but
it will be much cleaner and more maintainable that way.
> diff -Nurp linux-2.6.26-rc6/arch/powerpc/sysdev/bestcomm/bestcomm_priv.h linux-2.6.26-rc6.new/arch/powerpc/sysdev/bestcomm/bestcomm_priv.h
> --- linux-2.6.26-rc6/arch/powerpc/sysdev/bestcomm/bestcomm_priv.h 2008-03-18 15:49:53.000000000 +0000
> +++ linux-2.6.26-rc6.new/arch/powerpc/sysdev/bestcomm/bestcomm_priv.h 2008-04-15 10:42:38.000000000 +0100
> @@ -198,8 +198,8 @@ struct bcom_task_header {
> #define BCOM_IPR_SCTMR_1 2
> #define BCOM_IPR_FEC_RX 6
> #define BCOM_IPR_FEC_TX 5
> -#define BCOM_IPR_ATA_RX 4
> -#define BCOM_IPR_ATA_TX 3
> +#define BCOM_IPR_ATA_RX 7
> +#define BCOM_IPR_ATA_TX 7
> #define BCOM_IPR_SCPCI_RX 2
> #define BCOM_IPR_SCPCI_TX 2
> #define BCOM_IPR_PSC3_RX 2
Is this a bug fix? If so, please put it into a separate patch.
> diff -Nurp linux-2.6.26-rc6/drivers/ata/Kconfig linux-2.6.26-rc6.new/drivers/ata/Kconfig
> --- linux-2.6.26-rc6/drivers/ata/Kconfig 2008-03-18 15:49:33.000000000 +0000
> +++ linux-2.6.26-rc6.new/drivers/ata/Kconfig 2008-04-15 10:41:51.000000000 +0100
> @@ -462,6 +462,15 @@
>
> If unsure, say N.
>
> +config PATA_MPC52xx_DMA
> + tristate "Freescale MPC52xx SoC internal IDE DMA"
> + depends on PATA_MPC52xx
> + help
> + This option enables support for DMA on the MPC52xx SoC PATA
> + controller.
> +
> + If unsure, say N.
> +
Good, it can be turned off. Do you think there is any risk to existing
ATA users with this patch applied if this is turned off?
> config PATA_MPIIX
> tristate "Intel PATA MPIIX support"
> depends on PCI
> diff -Nurp linux-2.6.26-rc6/drivers/ata/pata_mpc52xx.c linux-2.6.26-rc6.new/drivers/ata/pata_mpc52xx.c
> --- linux-2.6.26-rc6/drivers/ata/pata_mpc52xx.c 2008-03-18 15:49:33.000000000 +0000
> +++ linux-2.6.26-rc6.new/drivers/ata/pata_mpc52xx.c 2008-04-15 10:41:49.000000000 +0100
> @@ -6,6 +6,9 @@
> * Copyright (C) 2006 Sylvain Munaut <tnt@246tNt.com>
> * Copyright (C) 2003 Mipsys - Benjamin Herrenschmidt
> *
> + * UDMA support based on patches by Freescale (Bernard Kuhn, John Rigby),
> + * Domen Puncer and Tim Yamin.
> + *
> * This file is licensed under the terms of the GNU General Public License
> * version 2. This program is licensed "as is" without any warranty of any
> * kind, whether express or implied.
> @@ -17,28 +20,47 @@
> #include <linux/delay.h>
> #include <linux/libata.h>
>
> +#include <asm/cacheflush.h>
> #include <asm/types.h>
> #include <asm/prom.h>
> #include <asm/of_platform.h>
> #include <asm/mpc52xx.h>
>
> +#include <sysdev/bestcomm/bestcomm.h>
> +#include <sysdev/bestcomm/bestcomm_priv.h>
> +#include <sysdev/bestcomm/ata.h>
>
> #define DRV_NAME "mpc52xx_ata"
> #define DRV_VERSION "0.1.2"
>
> -
> /* Private structures used by the driver */
> struct mpc52xx_ata_timings {
> u32 pio1;
> u32 pio2;
> + u32 mdma1;
> + u32 mdma2;
> + u32 udma1;
> + u32 udma2;
> + u32 udma3;
> + u32 udma4;
> + u32 udma5;
> + int using_udma;
> };
>
> struct mpc52xx_ata_priv {
> unsigned int ipb_period;
> struct mpc52xx_ata __iomem * ata_regs;
> + struct mpc52xx_ata __iomem * ata_regs_pa;
Why two copies of the ata_regs? (this should be documented)
> int ata_irq;
> struct mpc52xx_ata_timings timings[2];
> int csel;
> +
> + /* DMA */
> + struct bcom_task * dmatsk;
> + const struct udmaspec * udmaspec;
> + const struct mdmaspec * mdmaspec;
> + int mpc52xx_ata_dma_last_write;
> + int waiting_for_dma;
> };
>
>
> @@ -53,6 +75,102 @@
>
> #define CALC_CLKCYC(c,v) ((((v)+(c)-1)/(c)))
>
> +/* ATAPI-4 MDMA specs (in clocks) */
> +struct mdmaspec {
> + u32 t0M[3];
> + u32 td[3];
> + u32 th[3];
> + u32 tj[3];
> + u32 tkw[3];
> + u32 tm[3];
> + u32 tn[3];
> +};
This gets things to be quite verbose later on and it seems to separate
related data into 7 separate arrays. Would it make more sense to
structure it like this:
struct mdmaspec {
u32 t0M;
u32 td;
u32 th;
u32 tj;
u32 tkw;
u32 tm;
u32 tn;
};
static const struct mdmaspec mdmaspec66[3] = {
{.t0M=32, .td=15, .th=2, .tj=2, .tkw=15, .tm=4, .tn=1,},
{.t0M=10, .td=6, .th=1, .tj=1, .tkw=4, .tm=2, .tn=1,},
{.t0M=8, .td=5, .th=1, .tj=1, .tkw=2, .tm=2, .tn=1,},
};
It would allow some of the calculate code lower down dereference the
array once and should be much less verbose.
> +// -----------------------------------------------------------------------------------------------
Please use /* ----- */ instead
> +static const struct mdmaspec mdmaspec66 = {
> + {32, 10, 8},
> + {15, 6, 5},
> + {2, 1, 1},
> + {2, 1, 1},
> + {15, 4, 2},
> + {4, 2, 2},
> + {1, 1, 1}
> +};
This is a little fragile, please use the following form to make it more
robust against future structure changes. (and ditto through the rest of
the file)
+static const struct mdmaspec mdmaspec66 = {
+ .t0M = {32, 10, 8},
+ .td = {15, 6, 5},
+ .th = {2, 1, 1},
+ .tj = {2, 1, 1},
+ .tkw = {15, 4, 2},
+ .tm = {4, 2, 2},
+ .tn = {1, 1, 1}
+};
> /* ======================================================================== */
> /* Aux fns */
> @@ -141,6 +265,19 @@
>
> /* MPC52xx low level hw control */
>
> +static inline void
Drop 'inline' and trust GCC
> +mpc52xx_ata_wait_tip_bit_clear(struct mpc52xx_ata __iomem *regs)
> +{
> + int timeout = 1000;
> +
> + while (in_be32(®s->host_status) & MPC52xx_ATA_HOSTSTAT_TIP)
> + if (timeout-- == 0) {
> + printk(KERN_ERR "mpc52xx-ide: Timeout waiting for TIP clear\n");
> + break;
> + }
> + udelay(10); /* FIXME: Necessary ??? */
Can you find any way to avoid this? This could be a performance drain.
> +static int
> +mpc52xx_ata_compute_mdma_timings(struct mpc52xx_ata_priv *priv, int dev, int speed)
> +{
> + struct mpc52xx_ata_timings *timing = &priv->timings[dev];
> + u32 t0M, td, tkw, tm, th, tj, tn;
> +
> + if (speed < 0 || speed > 2)
> + return -EINVAL;
> +
> + t0M = priv->mdmaspec->t0M[speed];
> + td = priv->mdmaspec->td[speed];
> + tkw = priv->mdmaspec->tkw[speed];
> + tm = priv->mdmaspec->tm[speed];
> + th = priv->mdmaspec->th[speed];
> + tj = priv->mdmaspec->tj[speed];
> + tn = priv->mdmaspec->tn[speed];
> +
> + DPRINTK ("t0M = %d\n", t0M);
> + DPRINTK ("td = %d\n", td);
> + DPRINTK ("tkw = %d\n", tkw);
> + DPRINTK ("tm = %d\n", tm);
> + DPRINTK ("th = %d\n", th);
> + DPRINTK ("tj = %d\n", tj);
> + DPRINTK ("tn = %d\n", tn);
> +
> + timing->mdma1 = (t0M << 24) | (td << 16) | (tkw << 8) | (tm);
> + timing->mdma2 = (th << 24) | (tj << 16) | (tn << 8);
This is what I'm referring to. You could do:
struct mdmaspec *s = &priv->mdmaspec[speed];
and then access all the data as s->(blah) directly.
> +static int
> +mpc52xx_ata_build_dmatable(struct ata_queued_cmd *qc)
> +{
> + struct ata_port *ap = qc->ap;
> + struct mpc52xx_ata_priv *priv = ap->host->private_data;
> + struct mpc52xx_ata __iomem *regs_pa = priv->ata_regs_pa;
> + unsigned int read = !(qc->tf.flags & ATA_TFLAG_WRITE), si;
> + struct scatterlist *sg;
> + int count = 0;
> +
> + if (read)
> + bcom_ata_rx_prepare(priv->dmatsk);
> + else
> + bcom_ata_tx_prepare(priv->dmatsk);
> +
> + for_each_sg(qc->sg, sg, qc->n_elem, si) {
> + u32 cur_addr = sg_dma_address(sg);
> + u32 cur_len = sg_dma_len(sg);
> +
> + while (cur_len) {
> + unsigned int tc = min(cur_len, MAX_DMA_BUFFER_SIZE);
> + struct bcom_ata_bd *bd = (struct bcom_ata_bd *) bcom_prepare_next_buffer_2((struct bcom_task_2 *) priv->dmatsk);
Ugly casting. Reworking the bestcomm stuff should eliminate it.
> +
> + if (read) {
> + bd->status = tc;
> + bd->src_pa = (__force u32) ®s_pa->fifo_data;
> + bd->dst_pa = cur_addr;
> +
> + invalidate_dcache_range((__force u32) phys_to_virt(cur_addr), (__force u32) phys_to_virt(cur_addr) + (__force u32) cur_len);
Even uglier casting. Is this the right approach?
> +static void
> +mpc52xx_bmdma_setup(struct ata_queued_cmd *qc)
> +{
> + struct ata_port *ap = qc->ap;
> + struct mpc52xx_ata_priv *priv = ap->host->private_data;
> + struct mpc52xx_ata __iomem *regs = priv->ata_regs;
> +
> + unsigned int read = !(qc->tf.flags & ATA_TFLAG_WRITE);
> + u8 dma_mode;
> +
> + if (!mpc52xx_ata_build_dmatable(qc)) {
> + printk(KERN_ALERT "%s: %i, return 1?\n", __func__, __LINE__);
> + }
> +
> + /* Check FIFO is OK... */
> + if(in_8(&priv->ata_regs->fifo_status) & MPC52xx_ATA_FIFOSTAT_ERROR)
> + printk("WARNING: pata_mpc52xx FIFO error detected: 0x%02x!\n", in_8(&priv->ata_regs->fifo_status));
Line too long, please split (goes for the whole file)
> static int __devinit
> @@ -281,9 +720,14 @@
>
> ap = host->ports[0];
> ap->flags |= ATA_FLAG_SLAVE_POSS;
> - ap->pio_mask = 0x1f; /* Up to PIO4 */
> - ap->mwdma_mask = 0x00; /* No MWDMA */
> - ap->udma_mask = 0x00; /* No UDMA */
> + ap->pio_mask = ATA_PIO4; /* Up to PIO4 */
> +#ifdef CONFIG_PATA_MPC52xx_DMA
> + ap->mwdma_mask = 0x07; /* Up to MWDMA2 */
> + ap->udma_mask = ATA_UDMA2; /* Up to UDMA2 */
> +#else
> + ap->mwdma_mask = 0x00; /* No MWDMA */
> + ap->udma_mask = 0x00; /* No UDMA */
> +#endif
Is there any way to turn on/off DMA at runtime instead of CONFIG time?
>
> priv->ipb_period = 1000000000 / (ipb_freq / 1000);
> priv->ata_regs = ata_regs;
> + priv->ata_regs_pa = (struct mpc52xx_ata __iomem *) res_mem.start;
I'm not fond of this. First off, it is *not* __iomem. It is physical
address. It would be better to use the offset_of macro to add an offset
to the physical base address. Doing it this way forces you to cast and
sidestep the compile time checks for incorrect dereferences.
Some big comments, but it looks like a good driver; thanks!
Cheers,
g.
^ permalink raw reply
* Re: [PATCH v2] Parameterize EMAC Multicast Match Handling
From: Benjamin Herrenschmidt @ 2008-07-01 23:52 UTC (permalink / raw)
To: Grant Erickson; +Cc: linuxppc-dev, Stefan Roese
In-Reply-To: <C48FC1C4.10254%gerickson@nuovations.com>
On Tue, 2008-07-01 at 11:13 -0700, Grant Erickson wrote:
> In terms of the device tree expression, you would both favor something akin
> to the following?
>
> - compatible = "ibm,emac-405exr", "ibm,emac4";
> + compatible = "ibm,emac-405exr", "ibm,emac4", "ibm,emac4sync";
leave ibm,emac4 either at the end or totally out
Cheers,
Ben.
^ permalink raw reply
* [PATCH 1/3] ALSA SoC: Add OpenFirmware helper for matching bus and codec drivers
From: Grant Likely @ 2008-07-01 23:53 UTC (permalink / raw)
To: liam.girdwood, broonie, alsa-devel, linuxppc-dev, timur
From: Grant Likely <grant.likely@secretlab.ca>
Simple utility layer for creating ASoC machine instances based on data
in the OpenFirmware device tree. OF aware platform drivers and codec
drivers register themselves with this framework and the framework
automatically instantiates a machine driver.
This is most likely temporary glue code to work around limitations in
the ASoC v1 framework. I expect ASoC v2 won't need this.
---
sound/soc/Kconfig | 6 ++
sound/soc/Makefile | 1
sound/soc/soc-of.c | 171 ++++++++++++++++++++++++++++++++++++++++++++++++++++
3 files changed, 178 insertions(+), 0 deletions(-)
diff --git a/sound/soc/Kconfig b/sound/soc/Kconfig
index 18f28ac..c5736e5 100644
--- a/sound/soc/Kconfig
+++ b/sound/soc/Kconfig
@@ -23,6 +23,12 @@ config SND_SOC
This ASoC audio support can also be built as a module. If so, the module
will be called snd-soc-core.
+config SND_SOC_OF
+ tristate "OF helpers for SoC audio support"
+ depends on SND_SOC
+ ---help---
+ Add support for OpenFirmware device tree descriptions of sound device
+
# All the supported Soc's
source "sound/soc/at91/Kconfig"
source "sound/soc/pxa/Kconfig"
diff --git a/sound/soc/Makefile b/sound/soc/Makefile
index 782db21..191c2e5 100644
--- a/sound/soc/Makefile
+++ b/sound/soc/Makefile
@@ -2,3 +2,4 @@ snd-soc-core-objs := soc-core.o soc-dapm.o
obj-$(CONFIG_SND_SOC) += snd-soc-core.o
obj-$(CONFIG_SND_SOC) += codecs/ at91/ pxa/ s3c24xx/ sh/ fsl/ davinci/ omap/
+obj-$(CONFIG_SND_SOC_OF) += soc-of.o
diff --git a/sound/soc/soc-of.c b/sound/soc/soc-of.c
new file mode 100644
index 0000000..9694979
--- /dev/null
+++ b/sound/soc/soc-of.c
@@ -0,0 +1,171 @@
+/*
+ * OF helpers for ALSA SoC Layer
+ *
+ * Copyright (C) 2008, Secret Lab Technologies Ltd.
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/pm.h>
+#include <linux/bitops.h>
+#include <linux/platform_device.h>
+#include <linux/of.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/soc-of.h>
+#include <sound/initval.h>
+
+MODULE_AUTHOR("Grant Likely <grant.likely@secretlab.ca>");
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("ALSA SoC OpenFirmware bindings");
+
+DEFINE_MUTEX(of_snd_soc_mutex);
+LIST_HEAD(of_snd_soc_device_list);
+static int of_snd_soc_next_index;
+
+struct of_snd_soc_device {
+ int id;
+ struct list_head list;
+ struct snd_soc_device device;
+ struct snd_soc_machine machine;
+ struct snd_soc_dai_link dai_link;
+ struct platform_device *pdev;
+ struct device_node *platform_node;
+ struct device_node *codec_node;
+};
+
+static struct snd_soc_ops of_snd_soc_ops = {
+};
+
+static struct of_snd_soc_device *
+of_snd_soc_get_device(struct device_node *codec_node)
+{
+ struct of_snd_soc_device *of_soc;
+
+ list_for_each_entry(of_soc, &of_snd_soc_device_list, list) {
+ if (of_soc->codec_node == codec_node)
+ return of_soc;
+ }
+
+ of_soc = kzalloc(sizeof(struct of_snd_soc_device), GFP_KERNEL);
+ if (!of_soc)
+ return NULL;
+
+ /* Initialize the structure and add it to the global list */
+ of_soc->codec_node = codec_node;
+ of_soc->id = of_snd_soc_next_index++;
+ of_soc->machine.dai_link = &of_soc->dai_link;
+ of_soc->machine.num_links = 1;
+ of_soc->device.machine = &of_soc->machine;
+ of_soc->dai_link.ops = &of_snd_soc_ops;
+ list_add(&of_soc->list, &of_snd_soc_device_list);
+
+ return of_soc;
+}
+
+static void of_snd_soc_register_device(struct of_snd_soc_device *of_soc)
+{
+ struct platform_device *pdev;
+ int rc;
+
+ /* Only register the device if both the codec and platform have
+ * been registered */
+ if ((!of_soc->device.codec_data) || (!of_soc->platform_node))
+ return;
+
+ pr_info("platform<-->codec match achieved; registering machine\n");
+
+ pdev = platform_device_alloc("soc-audio", of_soc->id);
+ if (!pdev) {
+ pr_err("of_soc: platform_device_alloc() failed\n");
+ return;
+ }
+
+ pdev->dev.platform_data = of_soc;
+ platform_set_drvdata(pdev, &of_soc->device);
+ of_soc->device.dev = &pdev->dev;
+
+ /* The ASoC device is complete; register it */
+ rc = platform_device_add(pdev);
+ if (rc) {
+ pr_err("of_soc: platform_device_add() failed\n");
+ return;
+ }
+
+}
+
+int of_snd_soc_register_codec(struct snd_soc_codec_device *codec_dev,
+ void *codec_data, struct snd_soc_codec_dai *dai,
+ struct device_node *node)
+{
+ struct of_snd_soc_device *of_soc;
+ int rc = 0;
+
+ pr_info("registering ASoC codec driver: %s\n", node->full_name);
+
+ mutex_lock(&of_snd_soc_mutex);
+ of_soc = of_snd_soc_get_device(node);
+ if (!of_soc) {
+ rc = -ENOMEM;
+ goto out;
+ }
+
+ /* Store the codec data */
+ of_soc->device.codec_data = codec_data;
+ of_soc->device.codec_dev = codec_dev;
+ of_soc->dai_link.name = node->name;
+ of_soc->dai_link.stream_name = node->name;
+ of_soc->dai_link.codec_dai = dai;
+
+ /* Now try to register the SoC device */
+ of_snd_soc_register_device(of_soc);
+
+ out:
+ mutex_unlock(&of_snd_soc_mutex);
+ return rc;
+}
+EXPORT_SYMBOL_GPL(of_snd_soc_register_codec);
+
+int of_snd_soc_register_platform(struct snd_soc_platform *platform,
+ struct device_node *node,
+ struct snd_soc_cpu_dai *cpu_dai)
+{
+ struct of_snd_soc_device *of_soc;
+ struct device_node *codec_node;
+ const phandle *handle;
+ int len, rc = 0;
+
+ pr_info("registering ASoC platform driver: %s\n", node->full_name);
+
+ handle = of_get_property(node, "codec-handle", &len);
+ if (!handle || len < sizeof(handle))
+ return -ENODEV;
+ codec_node = of_find_node_by_phandle(*handle);
+ if (!codec_node)
+ return -ENODEV;
+ pr_info("looking for codec: %s\n", codec_node->full_name);
+
+ mutex_lock(&of_snd_soc_mutex);
+ of_soc = of_snd_soc_get_device(codec_node);
+ if (!of_soc) {
+ rc = -ENOMEM;
+ goto out;
+ }
+
+ of_soc->platform_node = node;
+ of_soc->dai_link.cpu_dai = cpu_dai;
+ of_soc->device.platform = platform;
+ of_soc->machine.name = of_soc->dai_link.cpu_dai->name;
+
+ /* Now try to register the SoC device */
+ of_snd_soc_register_device(of_soc);
+
+ out:
+ mutex_unlock(&of_snd_soc_mutex);
+ return rc;
+}
+EXPORT_SYMBOL_GPL(of_snd_soc_register_platform);
^ permalink raw reply related
* [PATCH 2/3] ALSA SoC: Add mpc5200-psc I2S driver
From: Grant Likely @ 2008-07-01 23:53 UTC (permalink / raw)
To: liam.girdwood, broonie, alsa-devel, linuxppc-dev, timur
In-Reply-To: <20080701235330.16923.67218.stgit@trillian.secretlab.ca>
From: Grant Likely <grant.likely@secretlab.ca>
This is an I2S bus driver for the MPC5200 PSC device. It is probably
will not be merged as-is because it uses v1 of the ASoC API, but I want
to get it out there for comments.
---
sound/soc/fsl/Kconfig | 6
sound/soc/fsl/Makefile | 2
sound/soc/fsl/mpc5200_psc_i2s.c | 899 +++++++++++++++++++++++++++++++++++++++
3 files changed, 907 insertions(+), 0 deletions(-)
diff --git a/sound/soc/fsl/Kconfig b/sound/soc/fsl/Kconfig
index 257101f..5daa8d3 100644
--- a/sound/soc/fsl/Kconfig
+++ b/sound/soc/fsl/Kconfig
@@ -17,4 +17,10 @@ config SND_SOC_MPC8610_HPCD
help
Say Y if you want to enable audio on the Freescale MPC8610 HPCD.
+config SND_SOC_MPC5200_I2S
+ bool "Freescale MPC5200 PSC in I2S mode driver"
+ depends on SND_SOC && PPC_MPC52xx
+ help
+ Say Y here to support the MPC5200 PSCs in I2S mode.
+
endmenu
diff --git a/sound/soc/fsl/Makefile b/sound/soc/fsl/Makefile
index 62f680a..98729a1 100644
--- a/sound/soc/fsl/Makefile
+++ b/sound/soc/fsl/Makefile
@@ -4,3 +4,5 @@ obj-$(CONFIG_SND_SOC_MPC8610_HPCD) += mpc8610_hpcd.o
# MPC8610 Platform Support
obj-$(CONFIG_SND_SOC_MPC8610) += fsl_ssi.o fsl_dma.o
+obj-$(CONFIG_SND_SOC_MPC5200_I2S) += mpc5200_psc_i2s.o
+
diff --git a/sound/soc/fsl/mpc5200_psc_i2s.c b/sound/soc/fsl/mpc5200_psc_i2s.c
new file mode 100644
index 0000000..81d0933
--- /dev/null
+++ b/sound/soc/fsl/mpc5200_psc_i2s.c
@@ -0,0 +1,899 @@
+/*
+ * Freescale MPC5200 PSC in I2S mode
+ * ALSA SoC Digital Audio Interface (DAI) driver
+ *
+ * Copyright (C) 2008 Secret Lab Technologies Ltd.
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/interrupt.h>
+#include <linux/device.h>
+#include <linux/delay.h>
+#include <linux/of_device.h>
+#include <linux/of_platform.h>
+#include <linux/dma-mapping.h>
+
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/initval.h>
+#include <sound/soc.h>
+#include <sound/soc-of.h>
+
+#include <sysdev/bestcomm/bestcomm.h>
+#include <sysdev/bestcomm/gen_bd.h>
+#include <asm/mpc52xx_psc.h>
+
+MODULE_AUTHOR("Grant Likely <grant.likely@secretlab.ca>");
+MODULE_DESCRIPTION("Freescale MPC5200 PSC in I2S mode ASoC Driver");
+MODULE_LICENSE("GPL");
+
+/**
+ * PSC_I2S_RATES: sample rates supported by the I2S
+ *
+ * This driver currently only supports the PSC running in I2S slave mode,
+ * which means the codec determines the sample rate. Therefore, we tell
+ * ALSA that we support all rates and let the codec driver decide what rates
+ * are really supported.
+ */
+#define PSC_I2S_RATES (SNDRV_PCM_RATE_5512 | SNDRV_PCM_RATE_8000_192000 | \
+ SNDRV_PCM_RATE_CONTINUOUS)
+
+/**
+ * PSC_I2S_FORMATS: audio formats supported by the PSC I2S mode
+ */
+#define PSC_I2S_FORMATS (SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_S16_BE | \
+ SNDRV_PCM_FMTBIT_S24_BE | SNDRV_PCM_FMTBIT_S24_BE | \
+ SNDRV_PCM_FMTBIT_S32_BE)
+
+/**
+ * psc_i2s_stream - Data specific to a single stream (playback or capture)
+ * @active: flag indicating if the stream is active
+ * @psc_i2s: pointer back to parent psc_i2s data structure
+ * @bcom_task: bestcomm task structure
+ * @irq: irq number for bestcomm task
+ * @period_start: physical address of start of DMA region
+ * @period_end: physical address of end of DMA region
+ * @period_next_pt: physical address of next DMA buffer to enqueue
+ * @period_bytes: size of DMA period in bytes
+ */
+struct psc_i2s_stream {
+ int active;
+ struct psc_i2s *psc_i2s;
+ struct bcom_task *bcom_task;
+ int irq;
+ struct snd_pcm_substream *stream;
+ dma_addr_t period_start;
+ dma_addr_t period_end;
+ dma_addr_t period_next_pt;
+ dma_addr_t period_current_pt;
+ int period_bytes;
+};
+
+/**
+ * psc_i2s - Private driver data
+ * @name: short name for this device ("PSC0", "PSC1", etc)
+ * @psc_regs: pointer to the PSC's registers
+ * @fifo_regs: pointer to the PSC's FIFO registers
+ * @irq: IRQ of this PSC
+ * @dev: struct device pointer
+ * @playback: the number of playback streams opened
+ * @capture: the number of capture streams opened
+ * @dai: the CPU DAI for this device
+ * @playback_stream: Playback stream context data
+ * @capture_stream: Capture stream context data
+ */
+struct psc_i2s {
+ char name[32];
+ struct mpc52xx_psc __iomem *psc_regs;
+ struct mpc52xx_psc_fifo __iomem *fifo_regs;
+ unsigned int irq;
+ struct device *dev;
+ struct snd_soc_cpu_dai dai;
+ spinlock_t lock;
+
+ /* per-stream data */
+ struct psc_i2s_stream playback_stream;
+ struct psc_i2s_stream capture_stream;
+
+ /* Statistics */
+ struct {
+ int overrun_count;
+ int underrun_count;
+ } stats;
+};
+
+/*
+ * Interrupt handlers
+ */
+static irqreturn_t psc_i2s_status_irq(int irq, void *_psc_i2s)
+{
+ struct psc_i2s *psc_i2s = _psc_i2s;
+ struct mpc52xx_psc __iomem *regs = psc_i2s->psc_regs;
+ u16 imr;
+ u16 isr;
+
+ isr = in_be16(®s->mpc52xx_psc_isr);
+ imr = in_be16(®s->mpc52xx_psc_imr);
+
+ /* Playback underrun error */
+ if (isr & imr & MPC52xx_PSC_IMR_TXEMP)
+ psc_i2s->stats.underrun_count++;
+
+ /* Capture overrun error */
+ if (isr & imr & MPC52xx_PSC_IMR_ORERR)
+ psc_i2s->stats.overrun_count++;
+
+ out_8(®s->command, 4 << 4); /* reset the error status */
+
+ return IRQ_HANDLED;
+}
+
+/**
+ * psc_i2s_bcom_enqueue_next_buffer - Enqueue another audio buffer
+ * @s: pointer to stream private data structure
+ *
+ * Enqueues another audio period buffer into the bestcomm queue.
+ *
+ * Note: The routine must only be called when there is space available in
+ * the queue. Otherwise the enqueue will fail and the audio ring buffer
+ * will get out of sync
+ */
+static void psc_i2s_bcom_enqueue_next_buffer(struct psc_i2s_stream *s)
+{
+ struct bcom_bd *bd;
+
+ /* Prepare and enqueue the next buffer descriptor */
+ bd = bcom_prepare_next_buffer(s->bcom_task);
+ bd->status = s->period_bytes;
+ bd->data[0] = s->period_next_pt;
+ bcom_submit_next_buffer(s->bcom_task, NULL);
+
+ /* Update for next period */
+ s->period_next_pt += s->period_bytes;
+ if (s->period_next_pt >= s->period_end)
+ s->period_next_pt = s->period_start;
+}
+
+/* Bestcomm DMA irq handler */
+static irqreturn_t psc_i2s_bcom_irq(int irq, void *_psc_i2s_stream)
+{
+ struct psc_i2s_stream *s = _psc_i2s_stream;
+
+ //spin_lock(&s->psc_i2s->lock);
+
+ /* For each finished period, dequeue the completed period buffer
+ * and enqueue a new one in it's place. */
+ while (bcom_buffer_done(s->bcom_task)) {
+ bcom_retrieve_buffer(s->bcom_task, NULL, NULL);
+ s->period_current_pt += s->period_bytes;
+ if (s->period_current_pt >= s->period_end)
+ s->period_current_pt = s->period_start;
+ psc_i2s_bcom_enqueue_next_buffer(s);
+ bcom_enable(s->bcom_task);
+ }
+
+ //spin_unlock(&s->psc_i2s->lock);
+
+ /* If the stream is active, then also inform the PCM middle layer
+ * of the period finished event. */
+ if (s->active)
+ snd_pcm_period_elapsed(s->stream);
+
+ return IRQ_HANDLED;
+}
+
+/**
+ * psc_i2s_startup: create a new substream
+ *
+ * This is the first function called when a stream is opened.
+ *
+ * If this is the first stream open, then grab the IRQ and program most of
+ * the PSC registers.
+ */
+static int psc_i2s_startup(struct snd_pcm_substream *substream)
+{
+ int playback_irq, capture_irq, rc;
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct psc_i2s *psc_i2s = rtd->dai->cpu_dai->private_data;
+ struct mpc52xx_psc __iomem *regs = psc_i2s->psc_regs;
+ struct mpc52xx_psc_fifo __iomem *fiforegs = psc_i2s->fifo_regs;
+
+ dev_dbg(psc_i2s->dev, "psc_i2s_startup(substream=%p)\n", substream);
+
+ /* Disable all interrupts and reset the PSC */
+ out_be16(®s->mpc52xx_psc_imr, 0);
+ out_8(®s->command, 3 << 4); /* reset transmitter */
+ out_8(®s->command, 2 << 4); /* reset receiver */
+ out_8(®s->command, 1 << 4); /* reset mode */
+ out_8(®s->command, 4 << 4); /* reset error */
+
+ /* Default to CODEC8 mode */
+ out_be32(®s->sicr,
+ MPC52xx_PSC_SICR_DTS1 | MPC52xx_PSC_SICR_I2S |
+ MPC52xx_PSC_SICR_CLKPOL | MPC52xx_PSC_SICR_SIM_CODEC_8);
+
+ /* First write: RxRdy (FIFO Alarm) generates receive FIFO interrupt */
+ /* Second write to mode: register Normal mode for non loopback */
+ out_8(®s->mode, 0);
+ out_8(®s->mode, 0);
+
+ /* Set the TX and RX fifo alarm thresholds */
+ out_be16(&fiforegs->rfalarm, 0x100); /* set RFALARM level */
+ out_8(&fiforegs->rfcntl, 0x4); /* set RFGRAN level (bytes) */
+ out_be16(&fiforegs->tfalarm, 0x100); /* set TFALARM level */
+ out_8(&fiforegs->tfcntl, 0x7); /* set TFGRAN level (bytes*4) */
+
+ /* Setup the IRQs */
+ playback_irq = bcom_get_task_irq(psc_i2s->playback_stream.bcom_task);
+ capture_irq = bcom_get_task_irq(psc_i2s->capture_stream.bcom_task);
+ rc = request_irq(psc_i2s->irq, &psc_i2s_status_irq, IRQF_SHARED,
+ "psc-i2s-status", psc_i2s);
+ rc |= request_irq(capture_irq, &psc_i2s_bcom_irq, IRQF_SHARED,
+ "psc-i2s-capture", &psc_i2s->capture_stream);
+ rc |= request_irq(playback_irq, &psc_i2s_bcom_irq, IRQF_SHARED,
+ "psc-i2s-playback", &psc_i2s->playback_stream);
+ if (rc) {
+ free_irq(psc_i2s->irq, psc_i2s);
+ free_irq(capture_irq, &psc_i2s->capture_stream);
+ free_irq(playback_irq, &psc_i2s->playback_stream);
+ return -ENODEV;
+ }
+
+ return 0;
+}
+
+static int psc_i2s_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct psc_i2s *psc_i2s = rtd->dai->cpu_dai->private_data;
+ u32 sicr;
+
+ dev_dbg(psc_i2s->dev, "%s(substream=%p) p_size=%i p_bytes=%i"
+ " periods=%i buffer_size=%i buffer_bytes=%i\n",
+ __FUNCTION__, substream, params_period_size(params),
+ params_period_bytes(params), params_periods(params),
+ params_buffer_size(params), params_buffer_bytes(params));
+
+ sicr = MPC52xx_PSC_SICR_DTS1 |
+ MPC52xx_PSC_SICR_I2S | MPC52xx_PSC_SICR_CLKPOL;
+ switch (params_format(params)) {
+ case SNDRV_PCM_FORMAT_S8:
+ sicr |= MPC52xx_PSC_SICR_SIM_CODEC_8;
+ break;
+ case SNDRV_PCM_FORMAT_S16_BE:
+ sicr |= MPC52xx_PSC_SICR_SIM_CODEC_16;
+ break;
+ case SNDRV_PCM_FORMAT_S24_BE:
+ sicr |= MPC52xx_PSC_SICR_SIM_CODEC_24;
+ break;
+ case SNDRV_PCM_FORMAT_S32_BE:
+ sicr |= MPC52xx_PSC_SICR_SIM_CODEC_32;
+ break;
+ default:
+ dev_dbg(psc_i2s->dev, "invalid format\n");
+ return -EINVAL;
+ }
+ out_be32(&psc_i2s->psc_regs->sicr, sicr);
+
+ //rc = snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(params));
+ //if (rc) {
+ // dev_err(psc_i2s->dev, "could not allocate dma buffer\n");
+ // return rc;
+ //}
+
+ snd_pcm_set_runtime_buffer(substream, &substream->dma_buffer);
+
+ return 0;
+}
+
+static int psc_i2s_hw_free(struct snd_pcm_substream *substream)
+{
+ //return snd_pcm_lib_free_pages(substream);
+ snd_pcm_set_runtime_buffer(substream, NULL);
+ return 0;
+}
+
+/**
+ * psc_i2s_trigger: start and stop the DMA transfer.
+ *
+ * This function is called by ALSA to start, stop, pause, and resume the DMA
+ * transfer of data.
+ */
+static int psc_i2s_trigger(struct snd_pcm_substream *substream, int cmd)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct psc_i2s *psc_i2s = rtd->dai->cpu_dai->private_data;
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct psc_i2s_stream *s;
+ struct mpc52xx_psc __iomem *regs = psc_i2s->psc_regs;
+ u16 imr;
+ u8 psc_cmd;
+ long flags;
+
+ if (substream->pstr->stream == SNDRV_PCM_STREAM_CAPTURE)
+ s = &psc_i2s->capture_stream;
+ else
+ s = &psc_i2s->playback_stream;
+
+ dev_dbg(psc_i2s->dev, "psc_i2s_trigger(substream=%p, cmd=%i)"
+ " stream_id=%i\n",
+ substream, cmd, substream->pstr->stream);
+
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ s->period_bytes = frames_to_bytes(runtime,
+ runtime->period_size);
+ s->period_start = virt_to_phys(runtime->dma_area);
+ s->period_end = s->period_start +
+ (s->period_bytes * runtime->periods);
+ s->period_next_pt = s->period_start;
+ s->period_current_pt = s->period_start;
+ s->active = 1;
+
+ /* First; reset everything */
+ if (substream->pstr->stream == SNDRV_PCM_STREAM_CAPTURE) {
+ out_8(®s->command, MPC52xx_PSC_RST_RX);
+ out_8(®s->command, MPC52xx_PSC_RST_ERR_STAT);
+ } else {
+ out_8(®s->command, MPC52xx_PSC_RST_TX);
+ out_8(®s->command, MPC52xx_PSC_RST_ERR_STAT);
+ }
+
+ /* Next, fill up the bestcomm bd queue and enable DMA.
+ * This will begin filling the PSC's fifo. */
+ if (substream->pstr->stream == SNDRV_PCM_STREAM_CAPTURE)
+ bcom_gen_bd_rx_reset(s->bcom_task);
+ else
+ bcom_gen_bd_tx_reset(s->bcom_task);
+ while (!bcom_queue_full(s->bcom_task))
+ psc_i2s_bcom_enqueue_next_buffer(s);
+ bcom_enable(s->bcom_task);
+
+ /* Update interrupt enable settings. This must be done
+ * before the PSC is enabled so that TX underrun events
+ * are not missed. */
+ imr = 0;
+ if (psc_i2s->playback_stream.active)
+ imr |= MPC52xx_PSC_IMR_TXEMP;
+ if (psc_i2s->capture_stream.active)
+ imr |= MPC52xx_PSC_IMR_ORERR;
+ out_be16(®s->isr_imr.imr, imr);
+
+ /* Due to errata in the i2s mode; need to line up enabling
+ * the transmitter with a transition on the frame sync
+ * line */
+
+ spin_lock_irqsave(&psc_i2s->lock, flags);
+ /* first make sure it is low */
+ while ((in_8(®s->ipcr_acr.ipcr) & 0x80) != 0);
+ /* then wait for the transition to high */
+ while ((in_8(®s->ipcr_acr.ipcr) & 0x80) == 0);
+ /* Finally, enable the PSC.
+ * Receiver must always be enabled; even when we only want
+ * transmit. (see 15.3.2.3 of MPC5200B User's Guide) */
+ psc_cmd = MPC52xx_PSC_RX_ENABLE;
+ if (substream->pstr->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ psc_cmd |= MPC52xx_PSC_TX_ENABLE;
+ out_8(®s->command, psc_cmd);
+ spin_unlock_irqrestore(&psc_i2s->lock, flags);
+
+ break;
+
+ case SNDRV_PCM_TRIGGER_STOP:
+ /* Turn off the PSC */
+ s->active = 0;
+ if (substream->pstr->stream == SNDRV_PCM_STREAM_CAPTURE) {
+ if (!psc_i2s->playback_stream.active) {
+ out_8(®s->command, 2 << 4); /* reset rx */
+ out_8(®s->command, 3 << 4); /* reset tx */
+ out_8(®s->command, 4 << 4); /* reset err */
+ }
+ } else {
+ out_8(®s->command, 3 << 4); /* reset tx */
+ out_8(®s->command, 4 << 4); /* reset err */
+ if (!psc_i2s->capture_stream.active)
+ out_8(®s->command, 2 << 4); /* reset rx */
+ }
+
+ bcom_disable(s->bcom_task);
+ while (!bcom_queue_empty(s->bcom_task))
+ bcom_retrieve_buffer(s->bcom_task, NULL, NULL);
+
+ break;
+
+ default:
+ dev_dbg(psc_i2s->dev, "invalid command\n");
+ return -EINVAL;
+ }
+
+ /* Update interrupt enable settings */
+ imr = 0;
+ if (psc_i2s->playback_stream.active) imr |= MPC52xx_PSC_IMR_TXEMP;
+ if (psc_i2s->capture_stream.active) imr |= MPC52xx_PSC_IMR_ORERR;
+ out_be16(®s->isr_imr.imr, imr);
+
+ return 0;
+}
+
+/**
+ * psc_i2s_shutdown: shutdown the data transfer on a stream
+ *
+ * Shutdown the PSC if there are no other substreams open.
+ */
+static void psc_i2s_shutdown(struct snd_pcm_substream *substream)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct psc_i2s *psc_i2s = rtd->dai->cpu_dai->private_data;
+
+ dev_dbg(psc_i2s->dev, "psc_i2s_shutdown(substream=%p)\n", substream);
+
+ /*
+ * If this is the last active substream, disable the PSC and release
+ * the IRQ.
+ */
+ if (!psc_i2s->playback_stream.active &&
+ !psc_i2s->capture_stream.active) {
+ /* TODO: shut off channels */
+ free_irq(psc_i2s->irq, psc_i2s);
+ free_irq(bcom_get_task_irq(psc_i2s->capture_stream.bcom_task),
+ &psc_i2s->capture_stream);
+ free_irq(bcom_get_task_irq(psc_i2s->playback_stream.bcom_task),
+ &psc_i2s->playback_stream);
+ }
+}
+
+/**
+ * psc_i2s_set_sysclk: set the clock frequency and direction
+ *
+ * This function is called by the machine driver to tell us what the clock
+ * frequency and direction are.
+ *
+ * Currently, we only support operating as a clock slave (SND_SOC_CLOCK_IN),
+ * and we don't care about the frequency. Return an error if the direction
+ * is not SND_SOC_CLOCK_IN.
+ *
+ * @clk_id: reserved, should be zero
+ * @freq: the frequency of the given clock ID, currently ignored
+ * @dir: SND_SOC_CLOCK_IN (clock slave) or SND_SOC_CLOCK_OUT (clock master)
+ */
+static int psc_i2s_set_sysclk(struct snd_soc_cpu_dai *cpu_dai,
+ int clk_id, unsigned int freq, int dir)
+{
+ struct psc_i2s *psc_i2s = cpu_dai->private_data;
+ dev_dbg(psc_i2s->dev, "psc_i2s_set_sysclk(cpu_dai=%p, dir=%i)\n",
+ cpu_dai, dir);
+ return (dir == SND_SOC_CLOCK_IN) ? 0 : -EINVAL;
+}
+
+/**
+ * psc_i2s_set_fmt: set the serial format.
+ *
+ * This function is called by the machine driver to tell us what serial
+ * format to use.
+ *
+ * This driver only supports I2S mode. Return an error if the format is
+ * not SND_SOC_DAIFMT_I2S.
+ *
+ * @format: one of SND_SOC_DAIFMT_xxx
+ */
+static int psc_i2s_set_fmt(struct snd_soc_cpu_dai *cpu_dai, unsigned int format)
+{
+ struct psc_i2s *psc_i2s = cpu_dai->private_data;
+ dev_dbg(psc_i2s->dev, "psc_i2s_set_fmt(cpu_dai=%p, format=%i)\n",
+ cpu_dai, format);
+ return (format == SND_SOC_DAIFMT_I2S) ? 0 : -EINVAL;
+}
+
+/* ---------------------------------------------------------------------
+ * ALSA SoC Bindings
+ *
+ * - Digital Audio Interface (DAI) template
+ * - create/destroy dai hooks
+ */
+
+/**
+ * psc_i2s_dai_template: template CPU Digital Audio Interface
+ */
+static struct snd_soc_cpu_dai psc_i2s_dai_template = {
+ .type = SND_SOC_DAI_I2S,
+ .playback = {
+ .channels_min = 2,
+ .channels_max = 2,
+ .rates = PSC_I2S_RATES,
+ .formats = PSC_I2S_FORMATS,
+ },
+ .capture = {
+ .channels_min = 2,
+ .channels_max = 2,
+ .rates = PSC_I2S_RATES,
+ .formats = PSC_I2S_FORMATS,
+ },
+ .ops = {
+ .startup = psc_i2s_startup,
+ .hw_params = psc_i2s_hw_params,
+ .hw_free = psc_i2s_hw_free,
+ .shutdown = psc_i2s_shutdown,
+ .trigger = psc_i2s_trigger,
+ },
+ .dai_ops = {
+ .set_sysclk = psc_i2s_set_sysclk,
+ .set_fmt = psc_i2s_set_fmt,
+ },
+};
+
+/* ---------------------------------------------------------------------
+ * The PSC I2S 'ASoC platform' driver
+ *
+ * Can be referenced by an 'ASoC machine' driver
+ * This driver only deals with the audio bus; it doesn't have any
+ * interaction with the attached codec
+ */
+
+static const struct snd_pcm_hardware psc_i2s_pcm_hardware = {
+ .info = SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_MMAP_VALID |
+ SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_BLOCK_TRANSFER,
+ .formats = SNDRV_PCM_FMTBIT_S8 |SNDRV_PCM_FMTBIT_S16_BE |
+ SNDRV_PCM_FMTBIT_S24_BE | SNDRV_PCM_FMTBIT_S32_BE,
+ .rate_min = 8000,
+ .rate_max = 48000,
+ .channels_min = 2,
+ .channels_max = 2,
+ .period_bytes_max = 1024 * 1024,
+ .period_bytes_min = 32,
+ .period_bytes_max = 1024 * 1024,
+ .periods_min = 2,
+ .periods_max = 256,
+ .buffer_bytes_max = 2 * 1024 * 1024,
+ .fifo_size = 0,
+};
+
+static unsigned int psc_i2s_fixed_rates[] = {
+ 8000, 11025, 12000, 16000, 22050, 24000, 32000, 44100, 48000,
+};
+
+static struct snd_pcm_hw_constraint_list psc_i2s_constraints_rates = {
+ .count = ARRAY_SIZE(psc_i2s_fixed_rates),
+ .list = psc_i2s_fixed_rates,
+ .mask = 0,
+};
+
+static int psc_i2s_pcm_open(struct snd_pcm_substream *substream)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct psc_i2s *psc_i2s = rtd->dai->cpu_dai->private_data;
+ struct psc_i2s_stream *s;
+ int rc;
+
+ dev_dbg(psc_i2s->dev, "psc_i2s_pcm_open(substream=%p)\n", substream);
+
+ if (substream->pstr->stream == SNDRV_PCM_STREAM_CAPTURE)
+ s = &psc_i2s->capture_stream;
+ else
+ s = &psc_i2s->playback_stream;
+
+ snd_soc_set_runtime_hwparams(substream, &psc_i2s_pcm_hardware);
+
+ rc = snd_pcm_hw_constraint_integer(substream->runtime,
+ SNDRV_PCM_HW_PARAM_PERIODS);
+ if (rc < 0) {
+ dev_err(psc_i2s->dev, "invalid buffer size\n");
+ return rc;
+ }
+ rc = snd_pcm_hw_constraint_list(substream->runtime, 0,
+ SNDRV_PCM_HW_PARAM_RATE,
+ &psc_i2s_constraints_rates);
+ if (rc < 0) {
+ dev_err(psc_i2s->dev, "invalid rate\n");
+ return rc;
+ }
+
+ s->stream = substream;
+ return 0;
+}
+
+static int psc_i2s_pcm_close(struct snd_pcm_substream * substream)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct psc_i2s *psc_i2s = rtd->dai->cpu_dai->private_data;
+ struct psc_i2s_stream *s;
+
+ dev_dbg(psc_i2s->dev, "psc_i2s_pcm_close(substream=%p)\n", substream);
+
+ if (substream->pstr->stream == SNDRV_PCM_STREAM_CAPTURE)
+ s = &psc_i2s->capture_stream;
+ else
+ s = &psc_i2s->playback_stream;
+
+ s->stream = NULL;
+ return 0;
+}
+
+static snd_pcm_uframes_t
+psc_i2s_pcm_pointer(struct snd_pcm_substream *substream)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct psc_i2s *psc_i2s = rtd->dai->cpu_dai->private_data;
+ struct psc_i2s_stream *s;
+ dma_addr_t count;
+
+ if (substream->pstr->stream == SNDRV_PCM_STREAM_CAPTURE)
+ s = &psc_i2s->capture_stream;
+ else
+ s = &psc_i2s->playback_stream;
+
+ /*FIXME: count = s->sdma->bd[s->sdma->outdex].data - s->period_start;*/
+ count = s->period_current_pt - s->period_start;
+
+ return bytes_to_frames(substream->runtime, count);
+}
+
+static struct snd_pcm_ops psc_i2s_pcm_ops = {
+ .open = psc_i2s_pcm_open,
+ .close = psc_i2s_pcm_close,
+ .ioctl = snd_pcm_lib_ioctl,
+ .pointer = psc_i2s_pcm_pointer,
+};
+
+static u64 psc_i2s_pcm_dmamask = 0xffffffff;
+static int psc_i2s_pcm_new(struct snd_card *card, struct snd_soc_codec_dai *dai,
+ struct snd_pcm *pcm)
+{
+ struct snd_soc_pcm_runtime *rtd = pcm->private_data;
+ size_t size = psc_i2s_pcm_hardware.buffer_bytes_max;
+ int rc = 0;
+
+ dev_dbg(rtd->socdev->dev, "psc_i2s_pcm_new(card=%p, dai=%p, pcm=%p)\n",
+ card, dai, pcm);
+
+ if (!card->dev->dma_mask)
+ card->dev->dma_mask = &psc_i2s_pcm_dmamask;
+ if (!card->dev->coherent_dma_mask)
+ card->dev->coherent_dma_mask = 0xffffffff;
+
+ rc = snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, pcm->dev, size,
+ &pcm->streams[0].substream->dma_buffer);
+ if (rc) {
+ dev_err(card->dev, "Cannot alloc playback DMA buffer\n");
+ return -ENOMEM;
+ }
+
+ rc = snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, pcm->dev, size,
+ &pcm->streams[1].substream->dma_buffer);
+ if (rc) {
+ snd_dma_free_pages(&pcm->streams[0].substream->dma_buffer);
+ dev_err(card->dev, "Can't allocate capture DMA buffer\n");
+ return -ENOMEM;
+ }
+
+ return 0;
+}
+
+static void psc_i2s_pcm_free(struct snd_pcm *pcm)
+{
+ struct snd_soc_pcm_runtime *rtd = pcm->private_data;
+ struct snd_pcm_substream *substream;
+ int stream;
+
+ dev_dbg(rtd->socdev->dev, "psc_i2s_pcm_free(pcm=%p)\n", pcm);
+
+ for (stream = 0; stream < 2; stream++) {
+ substream = pcm->streams[stream].substream;
+ if (substream) {
+ snd_dma_free_pages(&substream->dma_buffer);
+ substream->dma_buffer.area = NULL;
+ substream->dma_buffer.addr = 0;
+ }
+ }
+}
+
+struct snd_soc_platform psc_i2s_pcm_soc_platform = {
+ .name = "mpc5200-psc-audio",
+ .pcm_ops = &psc_i2s_pcm_ops,
+ .pcm_new = &psc_i2s_pcm_new,
+ .pcm_free = &psc_i2s_pcm_free,
+};
+
+/* ---------------------------------------------------------------------
+ * Sysfs attributes for debugging
+ */
+
+static ssize_t psc_i2s_status_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct psc_i2s *psc_i2s = dev_get_drvdata(dev);
+
+ return sprintf(buf, "status=%.4x sicr=%.8x rfnum=%i rfstat=0x%.4x tfnum=%i tfstat=0x%.4x\n",
+ in_be16(&psc_i2s->psc_regs->sr_csr.status),
+ in_be32(&psc_i2s->psc_regs->sicr),
+ in_be16(&psc_i2s->fifo_regs->rfnum) & 0x1ff,
+ in_be16(&psc_i2s->fifo_regs->rfstat),
+ in_be16(&psc_i2s->fifo_regs->tfnum) & 0x1ff,
+ in_be16(&psc_i2s->fifo_regs->tfstat));
+}
+
+static int * psc_i2s_get_stat_attr(struct psc_i2s *psc_i2s,
+ const char *name)
+{
+ if (strcmp(name, "playback_underrun") == 0)
+ return &psc_i2s->stats.underrun_count;
+ if (strcmp(name, "capture_overrun") == 0)
+ return &psc_i2s->stats.overrun_count;
+
+ return NULL;
+}
+
+static ssize_t psc_i2s_stat_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct psc_i2s *psc_i2s = dev_get_drvdata(dev);
+ int *attrib;
+
+ attrib = psc_i2s_get_stat_attr(psc_i2s, attr->attr.name);
+ if (!attrib)
+ return 0;
+
+ return sprintf(buf, "%i\n", *attrib);
+}
+
+static ssize_t psc_i2s_stat_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf,
+ size_t count)
+{
+ struct psc_i2s *psc_i2s = dev_get_drvdata(dev);
+ int *attrib;
+
+ attrib = psc_i2s_get_stat_attr(psc_i2s, attr->attr.name);
+ if (!attrib)
+ return 0;
+
+ *attrib = simple_strtoul(buf, NULL, 0);
+ return count;
+}
+
+DEVICE_ATTR(status, 0644, psc_i2s_status_show, NULL);
+DEVICE_ATTR(playback_underrun, 0644, psc_i2s_stat_show,psc_i2s_stat_store);
+DEVICE_ATTR(capture_overrun, 0644, psc_i2s_stat_show, psc_i2s_stat_store);
+
+/* ---------------------------------------------------------------------
+ * OF platform bus binding code:
+ * - Probe/remove operations
+ * - OF device match table
+ */
+static int __devinit psc_i2s_of_probe(struct of_device *op,
+ const struct of_device_id *match)
+{
+ phys_addr_t fifo;
+ struct psc_i2s *psc_i2s;
+ struct resource res;
+ int size, psc_id, irq, rc;
+ const __be32 *prop;
+ void __iomem *regs;
+
+ dev_dbg(&op->dev, "probing psc i2s device\n");
+
+ /* Get the PSC ID */
+ prop = of_get_property(op->node, "cell-index", &size);
+ if (!prop || size < sizeof *prop)
+ return -ENODEV;
+ psc_id = be32_to_cpu(*prop);
+
+ /* Fetch the registers and IRQ of the PSC */
+ irq = irq_of_parse_and_map(op->node, 0);
+ if (of_address_to_resource(op->node, 0, &res)) {
+ dev_err(&op->dev, "Missing reg property\n");
+ return -ENODEV;
+ }
+ regs = ioremap(res.start, 1 + res.end - res.start);
+ if (!regs) {
+ dev_err(&op->dev, "Could not map registers\n");
+ return -ENODEV;
+ }
+
+ /* Allocate and initialize the driver private data */
+ psc_i2s = kzalloc(sizeof *psc_i2s, GFP_KERNEL);
+ if (!psc_i2s) {
+ iounmap(regs);
+ return -ENOMEM;
+ }
+ spin_lock_init(&psc_i2s->lock);
+ psc_i2s->irq = irq;
+ psc_i2s->psc_regs = regs;
+ psc_i2s->fifo_regs = regs + sizeof *psc_i2s->psc_regs;
+ psc_i2s->dev = &op->dev;
+ psc_i2s->playback_stream.psc_i2s = psc_i2s;
+ psc_i2s->capture_stream.psc_i2s = psc_i2s;
+ snprintf(psc_i2s->name, sizeof psc_i2s->name, "PSC%u", psc_id+1);
+
+ /* Fill out the CPU DAI structure */
+ memcpy(&psc_i2s->dai, &psc_i2s_dai_template, sizeof psc_i2s->dai);
+ psc_i2s->dai.private_data = psc_i2s;
+ psc_i2s->dai.name = psc_i2s->name;
+ psc_i2s->dai.id = psc_id;
+
+ /* Find the address of the fifo data registers and setup the
+ * DMA tasks */
+ fifo = res.start + offsetof(struct mpc52xx_psc, buffer.buffer_32);
+ psc_i2s->capture_stream.bcom_task =
+ bcom_psc_gen_bd_rx_init(psc_id, 10, fifo, 512);
+ psc_i2s->playback_stream.bcom_task =
+ bcom_psc_gen_bd_tx_init(psc_id, 10, fifo);
+ if (!psc_i2s->capture_stream.bcom_task ||
+ !psc_i2s->playback_stream.bcom_task) {
+ dev_err(&op->dev, "Could not allocate bestcomm tasks\n");
+ iounmap(regs);
+ kfree(psc_i2s);
+ return -ENODEV;
+ }
+
+ /* Save what we've done so it can be found again later */
+ dev_set_drvdata(&op->dev, psc_i2s);
+
+ /* Register the SYSFS files */
+ rc = device_create_file(psc_i2s->dev, &dev_attr_status);
+ rc = device_create_file(psc_i2s->dev, &dev_attr_capture_overrun);
+ rc = device_create_file(psc_i2s->dev, &dev_attr_playback_underrun);
+ if (rc)
+ dev_info(psc_i2s->dev, "error creating sysfs files\n");
+
+ /* Tell the ASoC OF helpers about it */
+ of_snd_soc_register_platform(&psc_i2s_pcm_soc_platform, op->node,
+ &psc_i2s->dai);
+
+ return 0;
+}
+
+static int __devexit psc_i2s_of_remove(struct of_device *op)
+{
+ struct psc_i2s *psc_i2s = dev_get_drvdata(&op->dev);
+
+ dev_dbg(&op->dev, "psc_i2s_remove()\n");
+
+ bcom_gen_bd_rx_release(psc_i2s->capture_stream.bcom_task);
+ bcom_gen_bd_tx_release(psc_i2s->playback_stream.bcom_task);
+
+ iounmap(psc_i2s->psc_regs);
+ iounmap(psc_i2s->fifo_regs);
+ kfree(psc_i2s);
+ dev_set_drvdata(&op->dev, NULL);
+
+ return 0;
+}
+
+/* Match table for of_platform binding */
+static struct of_device_id psc_i2s_match[] __devinitdata = {
+ { .compatible = "fsl,mpc5200-psc-i2s", },
+ {}
+};
+MODULE_DEVICE_TABLE(of, psc_i2s_match);
+
+static struct of_platform_driver psc_i2s_driver = {
+ .match_table = psc_i2s_match,
+ .probe = psc_i2s_of_probe,
+ .remove = __devexit_p(psc_i2s_of_remove),
+ .driver = {
+ .name = "mpc5200-psc-i2s",
+ .owner = THIS_MODULE,
+ },
+};
+
+/* ---------------------------------------------------------------------
+ * Module setup and teardown; simply register the of_platform driver
+ * for the PSC in I2S mode.
+ */
+static int __init psc_i2s_init(void)
+{
+ return of_register_platform_driver(&psc_i2s_driver);
+}
+module_init(psc_i2s_init);
+
+static void __exit psc_i2s_exit(void)
+{
+ of_unregister_platform_driver(&psc_i2s_driver);
+}
+module_exit(psc_i2s_exit);
+
+
^ permalink raw reply related
* [PATCH 3/3] ALSA SoC: Add Texas Instruments TLV320AIC26 codec driver
From: Grant Likely @ 2008-07-01 23:53 UTC (permalink / raw)
To: liam.girdwood, broonie, alsa-devel, linuxppc-dev, timur
In-Reply-To: <20080701235330.16923.67218.stgit@trillian.secretlab.ca>
From: Grant Likely <grant.likely@secretlab.ca>
ASoC Codec driver for the TLV320AIC26 device. This driver uses the ASoC
v1 API, so I don't expect it to get merged as-is, but I want to get it
out there for review.
---
sound/soc/codecs/Kconfig | 4
sound/soc/codecs/Makefile | 2
sound/soc/codecs/tlv320aic26.c | 630 ++++++++++++++++++++++++++++++++++++++++
3 files changed, 636 insertions(+), 0 deletions(-)
diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig
index 3903ab7..96c7bfe 100644
--- a/sound/soc/codecs/Kconfig
+++ b/sound/soc/codecs/Kconfig
@@ -41,6 +41,10 @@ config SND_SOC_CS4270_VD33_ERRATA
bool
depends on SND_SOC_CS4270
+config SND_SOC_TLV320AIC26
+ tristate "TI TLB320AIC26 Codec support"
+ depends on SND_SOC && SPI
+
config SND_SOC_TLV320AIC3X
tristate
depends on SND_SOC && I2C
diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile
index 4e1314c..ec0cd93 100644
--- a/sound/soc/codecs/Makefile
+++ b/sound/soc/codecs/Makefile
@@ -5,6 +5,7 @@ snd-soc-wm8753-objs := wm8753.o
snd-soc-wm9712-objs := wm9712.o
snd-soc-wm9713-objs := wm9713.o
snd-soc-cs4270-objs := cs4270.o
+snd-soc-tlv320aic26-objs := tlv320aic26.o
snd-soc-tlv320aic3x-objs := tlv320aic3x.o
obj-$(CONFIG_SND_SOC_AC97_CODEC) += snd-soc-ac97.o
@@ -14,4 +15,5 @@ obj-$(CONFIG_SND_SOC_WM8753) += snd-soc-wm8753.o
obj-$(CONFIG_SND_SOC_WM9712) += snd-soc-wm9712.o
obj-$(CONFIG_SND_SOC_WM9713) += snd-soc-wm9713.o
obj-$(CONFIG_SND_SOC_CS4270) += snd-soc-cs4270.o
+obj-$(CONFIG_SND_SOC_TLV320AIC26) += snd-soc-tlv320aic26.o
obj-$(CONFIG_SND_SOC_TLV320AIC3X) += snd-soc-tlv320aic3x.o
diff --git a/sound/soc/codecs/tlv320aic26.c b/sound/soc/codecs/tlv320aic26.c
new file mode 100644
index 0000000..aee1dbc
--- /dev/null
+++ b/sound/soc/codecs/tlv320aic26.c
@@ -0,0 +1,630 @@
+/*
+ * Texas Instruments TLV320AIC26 low power audio CODEC
+ * ALSA SoC CODEC driver
+ *
+ * Copyright (C) 2008 Secret Lab Technologies Ltd.
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/pm.h>
+#include <linux/device.h>
+#include <linux/sysfs.h>
+#include <linux/spi/spi.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include <sound/soc-of.h>
+#include <sound/initval.h>
+
+MODULE_DESCRIPTION("ASoC TLV320AIC26 codec driver");
+MODULE_AUTHOR("Grant Likely <grant.likely@secretlab.ca>");
+MODULE_LICENSE("GPL");
+
+/* AIC26 Registers */
+#define AIC26_READ_COMMAND_WORD(addr) ((1 << 15) | (addr << 5))
+#define AIC26_WRITE_COMMAND_WORD(addr) ((0 << 15) | (addr << 5))
+#define AIC26_PAGE_ADDR(page, offset) ((page << 6) | offset)
+#define AIC26_NUM_REGS AIC26_PAGE_ADDR(3, 0)
+#define AIC26_REG_CACHE_SIZE (0x20) /* only page 2 cached */
+#define AIC26_REG_IS_CACHED(addr) ((addr & ~0x1f) == (2 << 6))
+#define AIC26_REG_CACHE_ADDR(addr) (addr & 0x1f)
+
+/* Page 0: Auxillary data registers */
+#define AIC26_REG_BAT1 AIC26_PAGE_ADDR(0, 0x05)
+#define AIC26_REG_BAT2 AIC26_PAGE_ADDR(0, 0x06)
+#define AIC26_REG_AUX AIC26_PAGE_ADDR(0, 0x07)
+#define AIC26_REG_TEMP1 AIC26_PAGE_ADDR(0, 0x09)
+#define AIC26_REG_TEMP2 AIC26_PAGE_ADDR(0, 0x0A)
+
+/* Page 1: Auxillary control registers */
+#define AIC26_REG_AUX_ADC AIC26_PAGE_ADDR(1, 0x00)
+#define AIC26_REG_STATUS AIC26_PAGE_ADDR(1, 0x01)
+#define AIC26_REG_REFERENCE AIC26_PAGE_ADDR(1, 0x03)
+#define AIC26_REG_RESET AIC26_PAGE_ADDR(1, 0x04)
+
+/* Page 2: Audio control registers */
+#define AIC26_REG_AUDIO_CTRL1 AIC26_PAGE_ADDR(2, 0x00)
+#define AIC26_REG_ADC_GAIN AIC26_PAGE_ADDR(2, 0x01)
+#define AIC26_REG_DAC_GAIN AIC26_PAGE_ADDR(2, 0x02)
+#define AIC26_REG_SIDETONE AIC26_PAGE_ADDR(2, 0x03)
+#define AIC26_REG_AUDIO_CTRL2 AIC26_PAGE_ADDR(2, 0x04)
+#define AIC26_REG_POWER_CTRL AIC26_PAGE_ADDR(2, 0x05)
+#define AIC26_REG_AUDIO_CTRL3 AIC26_PAGE_ADDR(2, 0x06)
+
+#define AIC26_REG_FILTER_COEFF_L_N0 AIC26_PAGE_ADDR(2, 0x07)
+#define AIC26_REG_FILTER_COEFF_L_N1 AIC26_PAGE_ADDR(2, 0x08)
+#define AIC26_REG_FILTER_COEFF_L_N2 AIC26_PAGE_ADDR(2, 0x09)
+#define AIC26_REG_FILTER_COEFF_L_N3 AIC26_PAGE_ADDR(2, 0x0A)
+#define AIC26_REG_FILTER_COEFF_L_N4 AIC26_PAGE_ADDR(2, 0x0B)
+#define AIC26_REG_FILTER_COEFF_L_N5 AIC26_PAGE_ADDR(2, 0x0C)
+#define AIC26_REG_FILTER_COEFF_L_D1 AIC26_PAGE_ADDR(2, 0x0D)
+#define AIC26_REG_FILTER_COEFF_L_D2 AIC26_PAGE_ADDR(2, 0x0E)
+#define AIC26_REG_FILTER_COEFF_L_D4 AIC26_PAGE_ADDR(2, 0x0F)
+#define AIC26_REG_FILTER_COEFF_L_D5 AIC26_PAGE_ADDR(2, 0x10)
+#define AIC26_REG_FILTER_COEFF_R_N0 AIC26_PAGE_ADDR(2, 0x11)
+#define AIC26_REG_FILTER_COEFF_R_N1 AIC26_PAGE_ADDR(2, 0x12)
+#define AIC26_REG_FILTER_COEFF_R_N2 AIC26_PAGE_ADDR(2, 0x13)
+#define AIC26_REG_FILTER_COEFF_R_N3 AIC26_PAGE_ADDR(2, 0x14)
+#define AIC26_REG_FILTER_COEFF_R_N4 AIC26_PAGE_ADDR(2, 0x15)
+#define AIC26_REG_FILTER_COEFF_R_N5 AIC26_PAGE_ADDR(2, 0x16)
+#define AIC26_REG_FILTER_COEFF_R_D1 AIC26_PAGE_ADDR(2, 0x17)
+#define AIC26_REG_FILTER_COEFF_R_D2 AIC26_PAGE_ADDR(2, 0x18)
+#define AIC26_REG_FILTER_COEFF_R_D4 AIC26_PAGE_ADDR(2, 0x19)
+#define AIC26_REG_FILTER_COEFF_R_D5 AIC26_PAGE_ADDR(2, 0x1A)
+
+#define AIC26_REG_PLL_PROG1 AIC26_PAGE_ADDR(2, 0x1B)
+#define AIC26_REG_PLL_PROG2 AIC26_PAGE_ADDR(2, 0x1C)
+#define AIC26_REG_AUDIO_CTRL4 AIC26_PAGE_ADDR(2, 0x1D)
+#define AIC26_REG_AUDIO_CTRL5 AIC26_PAGE_ADDR(2, 0x1E)
+
+#define AIC26_RATES (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 |\
+ SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_22050 |\
+ SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 |\
+ SNDRV_PCM_RATE_48000)
+#define AIC26_FORMATS (SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_S16_BE |\
+ SNDRV_PCM_FMTBIT_S24_BE | SNDRV_PCM_FMTBIT_S32_BE)
+
+/* fsref dividers; used in register 'Audio Control 1' */
+enum aic26_divisors {
+ AIC26_DIV_1 = 0,
+ AIC26_DIV_1_5 = 1,
+ AIC26_DIV_2 = 2,
+ AIC26_DIV_3 = 3,
+ AIC26_DIV_4 = 4,
+ AIC26_DIV_5 = 5,
+ AIC26_DIV_5_5 = 6,
+ AIC26_DIV_6 = 7,
+};
+
+/* Digital data format */
+enum aic26_datfm {
+ AIC26_DATFM_I2S = 0 << 8,
+ AIC26_DATFM_DSP = 1 << 8,
+ AIC26_DATFM_RIGHTJ = 2 << 8, /* right justified */
+ AIC26_DATFM_LEFTJ = 3 << 8, /* left justified */
+};
+
+/* Sample word length in bits; used in register 'Audio Control 1' */
+enum aic26_wlen {
+ AIC26_WLEN_16 = 0 << 10,
+ AIC26_WLEN_20 = 1 << 10,
+ AIC26_WLEN_24 = 2 << 10,
+ AIC26_WLEN_32 = 3 << 10,
+};
+
+/* AIC26 driver private data */
+struct aic26 {
+ struct spi_device *spi;
+ struct snd_soc_codec codec;
+ u16 reg_cache[AIC26_REG_CACHE_SIZE]; /* shadow registers */
+ int master;
+ int datfm;
+ int mclk;
+
+ /* Keyclick parameters */
+ int keyclick_amplitude;
+ int keyclick_freq;
+ int keyclick_len;
+};
+
+/* ---------------------------------------------------------------------
+ * Register access routines
+ */
+static unsigned int aic26_reg_read(struct snd_soc_codec *codec,
+ unsigned int reg)
+{
+ struct aic26 *aic26 = codec->private_data;
+ u16 *cache = codec->reg_cache;
+ u16 cmd, value;
+ u8 buffer[2];
+ int rc;
+
+ if ((reg < 0) || (reg >= AIC26_NUM_REGS)) {
+ WARN_ON_ONCE(1);
+ return 0;
+ }
+
+ /* Do SPI transfer; first 16bits are command; remaining is
+ * register contents */
+ cmd = AIC26_READ_COMMAND_WORD(reg);
+ buffer[0] = (cmd >> 8) & 0xff;
+ buffer[1] = cmd & 0xff;
+ rc = spi_write_then_read(aic26->spi, buffer, 2, buffer, 2);
+ if (rc) {
+ dev_err(&aic26->spi->dev, "AIC26 reg read error\n");
+ return -EIO;
+ }
+ value = (buffer[0] << 8) | buffer[1];
+
+ /* Update the cache before returning with the value */
+ if (AIC26_REG_IS_CACHED(reg))
+ cache[AIC26_REG_CACHE_ADDR(reg)] = value;
+ return value;
+}
+
+static unsigned int aic26_reg_read_cache(struct snd_soc_codec *codec,
+ unsigned int reg)
+{
+ u16 *cache = codec->reg_cache;
+
+ if ((reg < 0) || (reg >= AIC26_NUM_REGS)) {
+ WARN_ON_ONCE(1);
+ return 0;
+ }
+
+ if (AIC26_REG_IS_CACHED(reg))
+ return cache[AIC26_REG_CACHE_ADDR(reg)];
+
+ return aic26_reg_read(codec, reg);
+}
+
+static int aic26_reg_write(struct snd_soc_codec *codec, unsigned int reg,
+ unsigned int value)
+{
+ struct aic26 *aic26 = codec->private_data;
+ u16 *cache = codec->reg_cache;
+ u16 cmd;
+ u8 buffer[4];
+ int rc;
+
+ if ((reg < 0) || (reg >= AIC26_NUM_REGS)) {
+ WARN_ON_ONCE(1);
+ return -EINVAL;
+ }
+
+ /* Do SPI transfer; first 16bits are command; remaining is data
+ * to write into register */
+ cmd = AIC26_WRITE_COMMAND_WORD(reg);
+ buffer[0] = (cmd >> 8) & 0xff;
+ buffer[1] = cmd & 0xff;
+ buffer[2] = value >> 8;
+ buffer[3] = value;
+ rc = spi_write(aic26->spi, buffer, 4);
+ if (rc) {
+ dev_err(&aic26->spi->dev, "AIC26 reg read error\n");
+ return -EIO;
+ }
+
+ /* update cache before returning */
+ if (AIC26_REG_IS_CACHED(reg))
+ cache[AIC26_REG_CACHE_ADDR(reg)] = value;
+ return 0;
+}
+
+/* ---------------------------------------------------------------------
+ * Digital Audio Interface Operations
+ */
+static int aic26_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_device *socdev = rtd->socdev;
+ struct snd_soc_codec *codec = socdev->codec;
+ struct aic26 *aic26 = codec->private_data;
+ int fsref, divisor, wlen, pval, jval, dval, qval;
+ u16 reg;
+
+ dev_dbg(&aic26->spi->dev, "aic26_hw_params(substream=%p, params=%p)\n",
+ substream, params);
+ dev_dbg(&aic26->spi->dev, "rate=%i format=%i\n", params_rate(params),
+ params_format(params));
+
+ switch (params_rate(params)) {
+ case 8000: fsref = 48000; divisor = AIC26_DIV_6; break;
+ case 11025: fsref = 44100; divisor = AIC26_DIV_4; break;
+ case 12000: fsref = 48000; divisor = AIC26_DIV_4; break;
+ case 16000: fsref = 48000; divisor = AIC26_DIV_3; break;
+ case 22050: fsref = 44100; divisor = AIC26_DIV_2; break;
+ case 24000: fsref = 48000; divisor = AIC26_DIV_2; break;
+ case 32000: fsref = 48000; divisor = AIC26_DIV_1_5; break;
+ case 44100: fsref = 44100; divisor = AIC26_DIV_1; break;
+ case 48000: fsref = 48000; divisor = AIC26_DIV_1; break;
+ default: dev_dbg(&aic26->spi->dev, "bad rate\n"); return -EINVAL;
+ }
+
+ /* select data word length */
+ switch (params_format(params)) {
+ case SNDRV_PCM_FORMAT_S8: wlen = AIC26_WLEN_16; break;
+ case SNDRV_PCM_FORMAT_S16_BE: wlen = AIC26_WLEN_16; break;
+ case SNDRV_PCM_FORMAT_S24_BE: wlen = AIC26_WLEN_24; break;
+ case SNDRV_PCM_FORMAT_S32_BE: wlen = AIC26_WLEN_32; break;
+ default: dev_dbg(&aic26->spi->dev, "bad format\n"); return -EINVAL;
+ }
+
+ /* Configure PLL */
+ pval = 1;
+ jval = (fsref == 44100) ? 7 : 8;
+ dval = (fsref == 44100) ? 5264 : 1920;
+ qval = 0;
+ reg = 0x8000 | qval << 11 | pval << 8 | jval << 2;
+ aic26_reg_write(codec, AIC26_REG_PLL_PROG1, reg);
+ reg = dval << 2;
+ aic26_reg_write(codec, AIC26_REG_PLL_PROG2, reg);
+
+ /* Power up CODEC */
+ aic26_reg_write(codec, AIC26_REG_POWER_CTRL, 0);
+
+ /* Audio Control 3 (master mode, fsref rate) */
+ reg = aic26_reg_read_cache(codec, AIC26_REG_AUDIO_CTRL3);
+ reg &= ~0xf800;
+ if (aic26->master)
+ reg |= 0x0800;
+ if (fsref == 48000)
+ reg |= 0x2000;
+ aic26_reg_write(codec, AIC26_REG_AUDIO_CTRL3, reg);
+
+ /* Audio Control 1 (FSref divisor) */
+ reg = aic26_reg_read_cache(codec, AIC26_REG_AUDIO_CTRL1);
+ reg &= ~0x0fff;
+ reg |= wlen | aic26->datfm | (divisor << 3) | divisor;
+ aic26_reg_write(codec, AIC26_REG_AUDIO_CTRL1, reg);
+
+ return 0;
+}
+
+/**
+ * aic26_mute - Mute control to reduce noise when changing audio format
+ */
+static int aic26_mute(struct snd_soc_codec_dai *dai, int mute)
+{
+ struct snd_soc_codec *codec = dai->codec;
+ struct aic26 *aic26 = codec->private_data;
+ u16 reg = aic26_reg_read_cache(codec, AIC26_REG_DAC_GAIN);
+
+ dev_dbg(&aic26->spi->dev, "aic26_mute(dai=%p, mute=%i)\n",
+ dai, mute);
+
+ if (mute)
+ reg |= 0x8080;
+ else
+ reg &= ~0x8080;
+ aic26_reg_write(codec, AIC26_REG_DAC_GAIN, reg);
+
+ return 0;
+}
+
+static int aic26_set_sysclk(struct snd_soc_codec_dai *codec_dai,
+ int clk_id, unsigned int freq, int dir)
+{
+ struct snd_soc_codec *codec = codec_dai->codec;
+ struct aic26 *aic26 = codec->private_data;
+
+ dev_dbg(&aic26->spi->dev, "aic26_set_sysclk(dai=%p, clk_id==%i,"
+ " freq=%i, dir=%i)\n",
+ codec_dai, clk_id, freq, dir);
+
+ /* MCLK needs to fall between 2MHz and 50 MHz */
+ if ((freq < 2000000) || (freq > 50000000))
+ return -EINVAL;
+
+ aic26->mclk = freq;
+ return 0;
+}
+
+static int aic26_set_fmt(struct snd_soc_codec_dai *codec_dai, unsigned int fmt)
+{
+ struct snd_soc_codec *codec = codec_dai->codec;
+ struct aic26 *aic26 = codec->private_data;
+
+ dev_dbg(&aic26->spi->dev, "aic26_set_fmt(dai=%p, fmt==%i)\n",
+ codec_dai, fmt);
+
+ /* set master/slave audio interface */
+ switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+ case SND_SOC_DAIFMT_CBM_CFM: aic26->master = 1; break;
+ //case SND_SOC_DAIFMT_CBS_CFS: aic26->master = 0; break;
+ default: dev_dbg(&aic26->spi->dev, "bad master\n"); return -EINVAL;
+ }
+
+ /* interface format */
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_I2S: aic26->datfm = AIC26_DATFM_I2S; break;
+ case SND_SOC_DAIFMT_DSP_A: aic26->datfm = AIC26_DATFM_DSP; break;
+ case SND_SOC_DAIFMT_RIGHT_J: aic26->datfm = AIC26_DATFM_RIGHTJ; break;
+ case SND_SOC_DAIFMT_LEFT_J: aic26->datfm = AIC26_DATFM_LEFTJ; break;
+ default: dev_dbg(&aic26->spi->dev, "bad format\n"); return -EINVAL;
+ }
+
+ return 0;
+}
+
+/* ---------------------------------------------------------------------
+ * Digital Audio Interface Definition
+ */
+struct snd_soc_codec_dai aic26_dai = {
+ .name = "tlv320aic26",
+ .playback = {
+ .stream_name = "Playback",
+ .channels_min = 2,
+ .channels_max = 2,
+ .rates = AIC26_RATES,
+ .formats = AIC26_FORMATS,
+ },
+ .capture = {
+ .stream_name = "Capture",
+ .channels_min = 2,
+ .channels_max = 2,
+ .rates = AIC26_RATES,
+ .formats = AIC26_FORMATS,
+ },
+ .ops = {
+ .hw_params = aic26_hw_params,
+ },
+ .dai_ops = {
+ .digital_mute = aic26_mute,
+ .set_sysclk = aic26_set_sysclk,
+ .set_fmt = aic26_set_fmt,
+ },
+};
+EXPORT_SYMBOL_GPL(aic26_dai);
+
+/* ---------------------------------------------------------------------
+ * ALSA controls
+ */
+static const char *aic26_capture_src_text[] = {"Mic", "Aux"};
+static const struct soc_enum aic26_capture_src_enum =
+ SOC_ENUM_SINGLE(AIC26_REG_AUDIO_CTRL1, 12,2, aic26_capture_src_text);
+
+static const struct snd_kcontrol_new aic26_snd_controls[] = {
+ /* Output */
+ SOC_DOUBLE("PCM Playback Volume", AIC26_REG_DAC_GAIN, 8, 0, 0x7f, 1),
+ SOC_DOUBLE("PCM Playback Switch", AIC26_REG_DAC_GAIN, 15, 7, 1, 1),
+ SOC_SINGLE("PCM Capture Volume", AIC26_REG_ADC_GAIN, 8, 0x7f, 0),
+ SOC_SINGLE("PCM Capture Mute", AIC26_REG_ADC_GAIN, 15, 1, 1),
+ SOC_ENUM("Capture Source", aic26_capture_src_enum),
+};
+
+/* ---------------------------------------------------------------------
+ * SoC CODEC portion of driver: probe and release routines
+ */
+static int aic26_probe(struct platform_device *pdev)
+{
+ struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+ struct snd_soc_codec *codec;
+ struct snd_kcontrol *kcontrol;
+ struct aic26 *aic26;
+ int i, ret, err;
+
+ dev_info(&pdev->dev, "Probing AIC26 SoC CODEC driver\n");
+ dev_dbg(&pdev->dev, "socdev=%p\n", socdev);
+ dev_dbg(&pdev->dev, "codec_data=%p\n", socdev->codec_data);
+
+ /* Fetch the relevant aic26 private data here (it's already been
+ * stored in the .codec pointer) */
+ aic26 = socdev->codec_data;
+ if (aic26 == NULL) {
+ dev_err(&pdev->dev, "aic26: missing codec pointer\n");
+ return -ENODEV;
+ }
+ codec = &aic26->codec;
+ socdev->codec = codec;
+
+ dev_dbg(&pdev->dev, "Registering PCMs, dev=%p, socdev->dev=%p\n",
+ &pdev->dev, socdev->dev);
+ /* register pcms */
+ ret = snd_soc_new_pcms(socdev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "aic26: failed to create pcms\n");
+ return -ENODEV;
+ }
+
+ /* register controls */
+ dev_dbg(&pdev->dev, "Registering controls\n");
+ for (i = 0; i < ARRAY_SIZE(aic26_snd_controls); i++) {
+ kcontrol = snd_soc_cnew(&aic26_snd_controls[i], codec, NULL);
+ err = snd_ctl_add(codec->card, kcontrol);
+ WARN_ON(err < 0);
+ }
+
+ /* CODEC is setup, we can register the card now */
+ dev_dbg(&pdev->dev, "Registering card\n");
+ ret = snd_soc_register_card(socdev);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "aic26: failed to register card\n");
+ goto card_err;
+ }
+ return 0;
+
+ card_err:
+ snd_soc_free_pcms(socdev);
+ return ret;
+}
+
+static int aic26_remove(struct platform_device *pdev)
+{
+ struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+ snd_soc_free_pcms(socdev);
+ return 0;
+}
+
+struct snd_soc_codec_device aic26_soc_codec_dev = {
+ .probe = aic26_probe,
+ .remove = aic26_remove,
+};
+
+/* ---------------------------------------------------------------------
+ * SPI device portion of driver: sysfs files for debugging
+ */
+
+static ssize_t aic26_regs_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct aic26 *aic26 = dev_get_drvdata(dev);
+ char *idx = buf;
+ int cache_flag, addr, page, i, reg;
+
+ cache_flag = (strcmp(attr->attr.name, "regs_cache") == 0);
+
+ for (page = 0; page < 3; page++) {
+ for (i = 0; i < 0x20; i++) {
+ addr = AIC26_PAGE_ADDR(page, i);
+ if (i % 8 == 0)
+ idx += sprintf(idx, "%i:%.2i:", page,i);
+ if (cache_flag)
+ reg = aic26_reg_read_cache(&aic26->codec, addr);
+ else
+ reg = aic26_reg_read(&aic26->codec, addr);
+ idx += sprintf(idx, " %.4x", reg);
+ if (i % 8 == 7)
+ idx += sprintf(idx, "\n");
+ }
+ }
+ return idx - buf;
+}
+
+static ssize_t aic26_keyclick_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct aic26 *aic26 = dev_get_drvdata(dev);
+ int val, amp, freq, len;
+
+ val = aic26_reg_read_cache(&aic26->codec, AIC26_REG_AUDIO_CTRL2);
+ amp = (val >> 12) & 0x7;
+ freq = (125 << ((val >> 8) & 0x7)) >> 1;
+ len = 2 * (1 +((val >> 8) & 0xf));
+
+ return sprintf(buf, "amp=%x freq=%iHz len=%iclks\n", amp, freq, len);
+}
+
+/* Any write to the keyclick attribute will trigger the keyclick */
+static ssize_t aic26_keyclick_set(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct aic26 *aic26 = dev_get_drvdata(dev);
+ int val;
+
+ val = aic26_reg_read_cache(&aic26->codec, AIC26_REG_AUDIO_CTRL2);
+ val |= 0x8000;
+ aic26_reg_write(&aic26->codec, AIC26_REG_AUDIO_CTRL2, val);
+
+ return count;
+}
+
+DEVICE_ATTR(regs, 0644, aic26_regs_show, NULL);
+DEVICE_ATTR(regs_cache, 0644, aic26_regs_show, NULL);
+DEVICE_ATTR(keyclick, 0644, aic26_keyclick_show, aic26_keyclick_set);
+
+/* ---------------------------------------------------------------------
+ * SPI device portion of driver: probe and release routines and SPI
+ * driver registration.
+ */
+static int aic26_spi_probe(struct spi_device *spi)
+{
+ struct aic26 *aic26;
+ int rc, i, reg;
+
+ dev_dbg(&spi->dev, "probing tlv320aic26 spi device\n");
+
+ /* Allocate driver data */
+ aic26 = kzalloc(sizeof *aic26, GFP_KERNEL);
+ if (!aic26)
+ return -ENOMEM;
+
+ /* Initialize the driver data */
+ aic26->spi = spi;
+ dev_set_drvdata(&spi->dev, aic26);
+
+ /* Setup what we can in the codec structure so that the register
+ * access functions will work as expected. More will be filled
+ * out when it is probed by the SoC CODEC part of this driver */
+ aic26->codec.private_data = aic26;
+ aic26->codec.name = "aic26";
+ aic26->codec.owner = THIS_MODULE;
+ aic26->codec.dai = &aic26_dai;
+ aic26->codec.num_dai = 1;
+ aic26->codec.read = aic26_reg_read;
+ aic26->codec.write = aic26_reg_write;
+ aic26->master = 1;
+ mutex_init(&aic26->codec.mutex);
+ INIT_LIST_HEAD(&aic26->codec.dapm_widgets);
+ INIT_LIST_HEAD(&aic26->codec.dapm_paths);
+ aic26->codec.reg_cache_size = sizeof(aic26->reg_cache);
+ aic26->codec.reg_cache = aic26->reg_cache;
+
+ /* Reset the codec to power on defaults */
+ aic26_reg_write(&aic26->codec, AIC26_REG_RESET, 0xBB00);
+
+ /* Power up CODEC */
+ aic26_reg_write(&aic26->codec, AIC26_REG_POWER_CTRL, 0);
+
+ /* Audio Control 3 (master mode, fsref rate) */
+ reg = aic26_reg_read(&aic26->codec, AIC26_REG_AUDIO_CTRL3);
+ reg &= ~0xf800;
+ reg |= 0x0800; /* set master mode */
+ aic26_reg_write(&aic26->codec, AIC26_REG_AUDIO_CTRL3, reg);
+
+ /* Fill page 2 register cache */
+ for (i = 0; i < ARRAY_SIZE(aic26->reg_cache); i++)
+ aic26_reg_read(&aic26->codec, AIC26_PAGE_ADDR(2, i));
+
+ /* Register the sysfs files for debugging */
+ /* Create SysFS files */
+ rc = device_create_file(&spi->dev, &dev_attr_regs);
+ rc |= device_create_file(&spi->dev, &dev_attr_regs_cache);
+ rc |= device_create_file(&spi->dev, &dev_attr_keyclick);
+ if (rc)
+ dev_info(&spi->dev, "error creating sysfs files\n");
+
+ /* Tell the of_soc helper about this codec */
+ of_snd_soc_register_codec(&aic26_soc_codec_dev, aic26, &aic26_dai,
+ spi->dev.archdata.of_node);
+
+ dev_dbg(&spi->dev, "SPI device initialized\n");
+ return 0;
+}
+
+static int aic26_spi_remove(struct spi_device *spi)
+{
+ struct aic26 *aic26 = dev_get_drvdata(&spi->dev);
+
+ kfree(aic26);
+
+ return 0;
+}
+
+static struct spi_driver aic26_spi = {
+ .driver = {
+ .name = "tlv320aic26",
+ .owner = THIS_MODULE,
+ },
+ .probe = aic26_spi_probe,
+ .remove = aic26_spi_remove,
+};
+
+static int __init aic26_init(void)
+{
+ return spi_register_driver(&aic26_spi);
+}
+module_init(aic26_init);
+
+static void __exit aic26_exit(void)
+{
+ spi_unregister_driver(&aic26_spi);
+}
+module_exit(aic26_exit);
^ permalink raw reply related
* RE: [PATCH 12/60] microblaze_v4: Generic dts file for platforms
From: Benjamin Herrenschmidt @ 2008-07-02 0:25 UTC (permalink / raw)
To: Stephen Neuendorffer
Cc: linux-arch, alan, Michal Simek, vapier.adi, arnd, matthew,
microblaze-uclinux, linux-kernel, linuxppc-dev, will.newton, hpa,
John Linn, monstr, drepper, John Williams
In-Reply-To: <20080701155845.9DDE299004D@mail180-dub.bigfish.com>
On Tue, 2008-07-01 at 08:58 -0700, Stephen Neuendorffer wrote:
> Doing this at the binary level would be nice, but I see enough problems
> just doing it at the source level and at least for my purposes, doing it
> on a dtb would be overkill, I think.
>
> The main difficulty remains how to deal with cross references between
> nodes in a reasonable way where the references cross from one fragment
> to another.
I would have those done in the toplevel .dts
You can always override the property with the reference....
Ie. you #include your CPU or your SOC for example, and then override
some property to point to something else using the override mechanism I
mentioned.
Ben.
^ permalink raw reply
* Re: [PATCH] powerpc: Xilinx: add dts file for ML507 board
From: Josh Boyer @ 2008-07-02 0:53 UTC (permalink / raw)
To: John Linn; +Cc: linuxppc-dev, John Linn
In-Reply-To: <20080701230301.E23A7198050@mail68-va3.bigfish.com>
On Tue, 1 Jul 2008 16:02:54 -0700
John Linn <john.linn@xilinx.com> wrote:
> This new file adds support for the ML507 board which
> has a Virtex 5 FXT FPGA with a 440.
I haven't looked this over thoroughly yet, but an immediate question
comes to mind. Any way this can get converted to dts-v1 before it's
pulled in? All of the in-tree DTS files should be dts-v1 now, so I'd
like to have new ones go in as that.
(Also CC'ing the proper list)
josh
^ permalink raw reply
* [PATCH] powerpc: cleanup copy_to/from_user for vsx and fpr
From: Michael Neuling @ 2008-07-02 1:56 UTC (permalink / raw)
To: Paul Mackerras; +Cc: linuxppc-dev
In-Reply-To: <18537.30774.608486.268257@cargo.ozlabs.ibm.com>
This merges and cleans up some of the ugly copy/to from user code
which is required for the new fpr and vsx layout in the thread_struct.
Also fixes some hard coded buffer sizes and removes a redundant
fpr_flush_to_thread.
Signed-off-by: Michael Neuling <mikey@neuling.org>
---
arch/powerpc/kernel/signal.h | 10 +++
arch/powerpc/kernel/signal_32.c | 40 ++------------
arch/powerpc/kernel/signal_64.c | 112 ++++++++++++++++++++++++++++------------
3 files changed, 96 insertions(+), 66 deletions(-)
Index: linux-2.6-ozlabs/arch/powerpc/kernel/signal.h
===================================================================
--- linux-2.6-ozlabs.orig/arch/powerpc/kernel/signal.h
+++ linux-2.6-ozlabs/arch/powerpc/kernel/signal.h
@@ -24,6 +24,16 @@ extern int handle_rt_signal32(unsigned l
siginfo_t *info, sigset_t *oldset,
struct pt_regs *regs);
+extern unsigned long copy_fpr_to_user(void __user *to,
+ struct task_struct *task);
+extern unsigned long copy_fpr_from_user(struct task_struct *task,
+ void __user *from);
+#ifdef CONFIG_VSX
+extern unsigned long copy_vsx_to_user(void __user *to,
+ struct task_struct *task);
+extern unsigned long copy_vsx_from_user(struct task_struct *task,
+ void __user *from);
+#endif
#ifdef CONFIG_PPC64
Index: linux-2.6-ozlabs/arch/powerpc/kernel/signal_32.c
===================================================================
--- linux-2.6-ozlabs.orig/arch/powerpc/kernel/signal_32.c
+++ linux-2.6-ozlabs/arch/powerpc/kernel/signal_32.c
@@ -337,10 +337,6 @@ static int save_user_regs(struct pt_regs
int sigret)
{
unsigned long msr = regs->msr;
-#ifdef CONFIG_VSX
- double buf[32];
- int i;
-#endif
/* Make sure floating point registers are stored in regs */
flush_fp_to_thread(current);
@@ -370,14 +366,9 @@ static int save_user_regs(struct pt_regs
if (__put_user(current->thread.vrsave, (u32 __user *)&frame->mc_vregs[32]))
return 1;
#endif /* CONFIG_ALTIVEC */
-#ifdef CONFIG_VSX
- /* save FPR copy to local buffer then write to the thread_struct */
- flush_fp_to_thread(current);
- for (i = 0; i < 32 ; i++)
- buf[i] = current->thread.TS_FPR(i);
- memcpy(&buf[i], ¤t->thread.fpscr, sizeof(double));
- if (__copy_to_user(&frame->mc_fregs, buf, ELF_NFPREG * sizeof(double)))
+ if (copy_fpr_to_user(&frame->mc_fregs, current))
return 1;
+#ifdef CONFIG_VSX
/*
* Copy VSR 0-31 upper half from thread_struct to local
* buffer, then write that to userspace. Also set MSR_VSX in
@@ -386,18 +377,10 @@ static int save_user_regs(struct pt_regs
*/
if (current->thread.used_vsr) {
flush_vsx_to_thread(current);
- for (i = 0; i < 32 ; i++)
- buf[i] = current->thread.fpr[i][TS_VSRLOWOFFSET];
- if (__copy_to_user(&frame->mc_vsregs, buf,
- ELF_NVSRHALFREG * sizeof(double)))
+ if (copy_vsx_to_user(&frame->mc_vsregs, current))
return 1;
msr |= MSR_VSX;
}
-#else
- /* save floating-point registers */
- if (__copy_to_user(&frame->mc_fregs, current->thread.fpr,
- ELF_NFPREG * sizeof(double)))
- return 1;
#endif /* CONFIG_VSX */
#ifdef CONFIG_SPE
/* save spe registers */
@@ -442,7 +425,6 @@ static long restore_user_regs(struct pt_
unsigned int save_r2 = 0;
unsigned long msr;
#ifdef CONFIG_VSX
- double buf[32];
int i;
#endif
@@ -490,13 +472,10 @@ static long restore_user_regs(struct pt_
if (__get_user(current->thread.vrsave, (u32 __user *)&sr->mc_vregs[32]))
return 1;
#endif /* CONFIG_ALTIVEC */
+ if (copy_fpr_from_user(current, &sr->mc_fregs))
+ return 1;
#ifdef CONFIG_VSX
- if (__copy_from_user(buf, &sr->mc_fregs,sizeof(sr->mc_fregs)))
- return 1;
- for (i = 0; i < 32 ; i++)
- current->thread.TS_FPR(i) = buf[i];
- memcpy(¤t->thread.fpscr, &buf[i], sizeof(double));
/*
* Force the process to reload the VSX registers from
* current->thread when it next does VSX instruction.
@@ -507,18 +486,11 @@ static long restore_user_regs(struct pt_
* Restore altivec registers from the stack to a local
* buffer, then write this out to the thread_struct
*/
- if (__copy_from_user(buf, &sr->mc_vsregs,
- sizeof(sr->mc_vsregs)))
+ if (copy_vsx_from_user(current, &sr->mc_vsregs))
return 1;
- for (i = 0; i < 32 ; i++)
- current->thread.fpr[i][TS_VSRLOWOFFSET] = buf[i];
} else if (current->thread.used_vsr)
for (i = 0; i < 32 ; i++)
current->thread.fpr[i][TS_VSRLOWOFFSET] = 0;
-#else
- if (__copy_from_user(current->thread.fpr, &sr->mc_fregs,
- sizeof(sr->mc_fregs)))
- return 1;
#endif /* CONFIG_VSX */
/*
* force the process to reload the FP registers from
Index: linux-2.6-ozlabs/arch/powerpc/kernel/signal_64.c
===================================================================
--- linux-2.6-ozlabs.orig/arch/powerpc/kernel/signal_64.c
+++ linux-2.6-ozlabs/arch/powerpc/kernel/signal_64.c
@@ -69,6 +69,75 @@ static const char fmt32[] = KERN_INFO \
static const char fmt64[] = KERN_INFO \
"%s[%d]: bad frame in %s: %016lx nip %016lx lr %016lx\n";
+#ifdef CONFIG_VSX
+unsigned long copy_fpr_to_user(void __user *to,
+ struct task_struct *task)
+{
+ double buf[ELF_NFPREG];
+ int i;
+
+ /* save FPR copy to local buffer then write to the thread_struct */
+ for (i = 0; i < (ELF_NFPREG - 1) ; i++)
+ buf[i] = task->thread.TS_FPR(i);
+ memcpy(&buf[i], &task->thread.fpscr, sizeof(double));
+ return __copy_to_user(to, buf, ELF_NFPREG * sizeof(double));
+}
+
+unsigned long copy_fpr_from_user(struct task_struct *task,
+ void __user *from)
+{
+ double buf[ELF_NFPREG];
+ int i;
+
+ if (__copy_from_user(buf, from, ELF_NFPREG * sizeof(double)))
+ return 1;
+ for (i = 0; i < (ELF_NFPREG - 1) ; i++)
+ task->thread.TS_FPR(i) = buf[i];
+ memcpy(&task->thread.fpscr, &buf[i], sizeof(double));
+
+ return 0;
+}
+
+unsigned long copy_vsx_to_user(void __user *to,
+ struct task_struct *task)
+{
+ double buf[ELF_NVSRHALFREG];
+ int i;
+
+ /* save FPR copy to local buffer then write to the thread_struct */
+ for (i = 0; i < ELF_NVSRHALFREG; i++)
+ buf[i] = task->thread.fpr[i][TS_VSRLOWOFFSET];
+ return __copy_to_user(to, buf, ELF_NVSRHALFREG * sizeof(double));
+}
+
+unsigned long copy_vsx_from_user(struct task_struct *task,
+ void __user *from)
+{
+ double buf[ELF_NVSRHALFREG];
+ int i;
+
+ if (__copy_from_user(buf, from, ELF_NVSRHALFREG * sizeof(double)))
+ return 1;
+ for (i = 0; i < ELF_NVSRHALFREG ; i++)
+ task->thread.fpr[i][TS_VSRLOWOFFSET] = buf[i];
+ return 0;
+}
+#else
+inline unsigned long copy_fpr_to_user(void __user *to,
+ struct task_struct *task)
+{
+ return __copy_to_user(to, task->thread.fpr,
+ ELF_NFPREG * sizeof(double));
+}
+
+inline unsigned long copy_fpr_from_user(struct task_struct *task,
+ void __user *from)
+{
+ return __copy_from_user(task->thread.fpr, from,
+ ELF_NFPREG * sizeof(double));
+}
+#endif
+
/*
* Set up the sigcontext for the signal frame.
*/
@@ -89,10 +158,6 @@ static long setup_sigcontext(struct sigc
#endif
unsigned long msr = regs->msr;
long err = 0;
-#ifdef CONFIG_VSX
- double buf[FP_REGS_SIZE];
- int i;
-#endif
flush_fp_to_thread(current);
@@ -117,12 +182,9 @@ static long setup_sigcontext(struct sigc
err |= __put_user(0, &sc->v_regs);
#endif /* CONFIG_ALTIVEC */
flush_fp_to_thread(current);
+ /* copy fpr regs and fpscr */
+ err |= copy_fpr_to_user(&sc->fp_regs, current);
#ifdef CONFIG_VSX
- /* Copy FP to local buffer then write that out */
- for (i = 0; i < 32 ; i++)
- buf[i] = current->thread.TS_FPR(i);
- memcpy(&buf[i], ¤t->thread.fpscr, sizeof(double));
- err |= __copy_to_user(&sc->fp_regs, buf, FP_REGS_SIZE);
/*
* Copy VSX low doubleword to local buffer for formatting,
* then out to userspace. Update v_regs to point after the
@@ -131,17 +193,12 @@ static long setup_sigcontext(struct sigc
if (current->thread.used_vsr) {
flush_vsx_to_thread(current);
v_regs += ELF_NVRREG;
- for (i = 0; i < 32 ; i++)
- buf[i] = current->thread.fpr[i][TS_VSRLOWOFFSET];
- err |= __copy_to_user(v_regs, buf, 32 * sizeof(double));
+ err |= copy_vsx_to_user(v_regs, current);
/* set MSR_VSX in the MSR value in the frame to
* indicate that sc->vs_reg) contains valid data.
*/
msr |= MSR_VSX;
}
-#else /* CONFIG_VSX */
- /* copy fpr regs and fpscr */
- err |= __copy_to_user(&sc->fp_regs, ¤t->thread.fpr, FP_REGS_SIZE);
#endif /* CONFIG_VSX */
err |= __put_user(&sc->gp_regs, &sc->regs);
WARN_ON(!FULL_REGS(regs));
@@ -165,13 +222,12 @@ static long restore_sigcontext(struct pt
#ifdef CONFIG_ALTIVEC
elf_vrreg_t __user *v_regs;
#endif
-#ifdef CONFIG_VSX
- double buf[FP_REGS_SIZE];
- int i;
-#endif
unsigned long err = 0;
unsigned long save_r13 = 0;
unsigned long msr;
+#ifdef CONFIG_VSX
+ int i;
+#endif
/* If this is not a signal return, we preserve the TLS in r13 */
if (!sig)
@@ -234,15 +290,9 @@ static long restore_sigcontext(struct pt
else
current->thread.vrsave = 0;
#endif /* CONFIG_ALTIVEC */
-#ifdef CONFIG_VSX
/* restore floating point */
- err |= __copy_from_user(buf, &sc->fp_regs, FP_REGS_SIZE);
- if (err)
- return err;
- for (i = 0; i < 32 ; i++)
- current->thread.TS_FPR(i) = buf[i];
- memcpy(¤t->thread.fpscr, &buf[i], sizeof(double));
-
+ err |= copy_fpr_from_user(current, &sc->fp_regs);
+#ifdef CONFIG_VSX
/*
* Get additional VSX data. Update v_regs to point after the
* VMX data. Copy VSX low doubleword from userspace to local
@@ -250,14 +300,12 @@ static long restore_sigcontext(struct pt
*/
v_regs += ELF_NVRREG;
if ((msr & MSR_VSX) != 0)
- err |= __copy_from_user(buf, v_regs, 32 * sizeof(double));
+ err |= copy_vsx_from_user(current, v_regs);
else
- memset(buf, 0, 32 * sizeof(double));
+ for (i = 0; i < 32 ; i++)
+ current->thread.fpr[i][TS_VSRLOWOFFSET] = 0;
- for (i = 0; i < 32 ; i++)
- current->thread.fpr[i][TS_VSRLOWOFFSET] = buf[i];
#else
- err |= __copy_from_user(¤t->thread.fpr, &sc->fp_regs, FP_REGS_SIZE);
#endif
return err;
}
^ permalink raw reply
* [PATCH] powerpc: cleanup copy_to/from_user for vsx and fpr
From: Michael Neuling @ 2008-07-02 4:06 UTC (permalink / raw)
To: Paul Mackerras; +Cc: linuxppc-dev
In-Reply-To: <1214963802.6328.185630097155.qpush@coopers>
This merges and cleans up some of the ugly copy/to from user code
which is required for the new fpr and vsx layout in the thread_struct.
Also fixes some hard coded buffer sizes and removes a redundant
fpr_flush_to_thread.
Signed-off-by: Michael Neuling <mikey@neuling.org>
---
Sorry, the last version broke 32bit kernel builds
signal.h | 10 +++++
signal_32.c | 109 +++++++++++++++++++++++++++++++++++++++++-------------------
signal_64.c | 43 ++++++-----------------
3 files changed, 96 insertions(+), 66 deletions(-)
Index: linux-2.6-ozlabs/arch/powerpc/kernel/signal.h
===================================================================
--- linux-2.6-ozlabs.orig/arch/powerpc/kernel/signal.h
+++ linux-2.6-ozlabs/arch/powerpc/kernel/signal.h
@@ -24,6 +24,16 @@ extern int handle_rt_signal32(unsigned l
siginfo_t *info, sigset_t *oldset,
struct pt_regs *regs);
+extern unsigned long copy_fpr_to_user(void __user *to,
+ struct task_struct *task);
+extern unsigned long copy_fpr_from_user(struct task_struct *task,
+ void __user *from);
+#ifdef CONFIG_VSX
+extern unsigned long copy_vsx_to_user(void __user *to,
+ struct task_struct *task);
+extern unsigned long copy_vsx_from_user(struct task_struct *task,
+ void __user *from);
+#endif
#ifdef CONFIG_PPC64
Index: linux-2.6-ozlabs/arch/powerpc/kernel/signal_32.c
===================================================================
--- linux-2.6-ozlabs.orig/arch/powerpc/kernel/signal_32.c
+++ linux-2.6-ozlabs/arch/powerpc/kernel/signal_32.c
@@ -328,6 +328,75 @@ struct rt_sigframe {
int abigap[56];
};
+#ifdef CONFIG_VSX
+unsigned long copy_fpr_to_user(void __user *to,
+ struct task_struct *task)
+{
+ double buf[ELF_NFPREG];
+ int i;
+
+ /* save FPR copy to local buffer then write to the thread_struct */
+ for (i = 0; i < (ELF_NFPREG - 1) ; i++)
+ buf[i] = task->thread.TS_FPR(i);
+ memcpy(&buf[i], &task->thread.fpscr, sizeof(double));
+ return __copy_to_user(to, buf, ELF_NFPREG * sizeof(double));
+}
+
+unsigned long copy_fpr_from_user(struct task_struct *task,
+ void __user *from)
+{
+ double buf[ELF_NFPREG];
+ int i;
+
+ if (__copy_from_user(buf, from, ELF_NFPREG * sizeof(double)))
+ return 1;
+ for (i = 0; i < (ELF_NFPREG - 1) ; i++)
+ task->thread.TS_FPR(i) = buf[i];
+ memcpy(&task->thread.fpscr, &buf[i], sizeof(double));
+
+ return 0;
+}
+
+unsigned long copy_vsx_to_user(void __user *to,
+ struct task_struct *task)
+{
+ double buf[ELF_NVSRHALFREG];
+ int i;
+
+ /* save FPR copy to local buffer then write to the thread_struct */
+ for (i = 0; i < ELF_NVSRHALFREG; i++)
+ buf[i] = task->thread.fpr[i][TS_VSRLOWOFFSET];
+ return __copy_to_user(to, buf, ELF_NVSRHALFREG * sizeof(double));
+}
+
+unsigned long copy_vsx_from_user(struct task_struct *task,
+ void __user *from)
+{
+ double buf[ELF_NVSRHALFREG];
+ int i;
+
+ if (__copy_from_user(buf, from, ELF_NVSRHALFREG * sizeof(double)))
+ return 1;
+ for (i = 0; i < ELF_NVSRHALFREG ; i++)
+ task->thread.fpr[i][TS_VSRLOWOFFSET] = buf[i];
+ return 0;
+}
+#else
+inline unsigned long copy_fpr_to_user(void __user *to,
+ struct task_struct *task)
+{
+ return __copy_to_user(to, task->thread.fpr,
+ ELF_NFPREG * sizeof(double));
+}
+
+inline unsigned long copy_fpr_from_user(struct task_struct *task,
+ void __user *from)
+{
+ return __copy_from_user(task->thread.fpr, from,
+ ELF_NFPREG * sizeof(double));
+}
+#endif
+
/*
* Save the current user registers on the user stack.
* We only save the altivec/spe registers if the process has used
@@ -337,10 +406,6 @@ static int save_user_regs(struct pt_regs
int sigret)
{
unsigned long msr = regs->msr;
-#ifdef CONFIG_VSX
- double buf[32];
- int i;
-#endif
/* Make sure floating point registers are stored in regs */
flush_fp_to_thread(current);
@@ -370,14 +435,9 @@ static int save_user_regs(struct pt_regs
if (__put_user(current->thread.vrsave, (u32 __user *)&frame->mc_vregs[32]))
return 1;
#endif /* CONFIG_ALTIVEC */
-#ifdef CONFIG_VSX
- /* save FPR copy to local buffer then write to the thread_struct */
- flush_fp_to_thread(current);
- for (i = 0; i < 32 ; i++)
- buf[i] = current->thread.TS_FPR(i);
- memcpy(&buf[i], ¤t->thread.fpscr, sizeof(double));
- if (__copy_to_user(&frame->mc_fregs, buf, ELF_NFPREG * sizeof(double)))
+ if (copy_fpr_to_user(&frame->mc_fregs, current))
return 1;
+#ifdef CONFIG_VSX
/*
* Copy VSR 0-31 upper half from thread_struct to local
* buffer, then write that to userspace. Also set MSR_VSX in
@@ -386,18 +446,10 @@ static int save_user_regs(struct pt_regs
*/
if (current->thread.used_vsr) {
flush_vsx_to_thread(current);
- for (i = 0; i < 32 ; i++)
- buf[i] = current->thread.fpr[i][TS_VSRLOWOFFSET];
- if (__copy_to_user(&frame->mc_vsregs, buf,
- ELF_NVSRHALFREG * sizeof(double)))
+ if (copy_vsx_to_user(&frame->mc_vsregs, current))
return 1;
msr |= MSR_VSX;
}
-#else
- /* save floating-point registers */
- if (__copy_to_user(&frame->mc_fregs, current->thread.fpr,
- ELF_NFPREG * sizeof(double)))
- return 1;
#endif /* CONFIG_VSX */
#ifdef CONFIG_SPE
/* save spe registers */
@@ -442,7 +494,6 @@ static long restore_user_regs(struct pt_
unsigned int save_r2 = 0;
unsigned long msr;
#ifdef CONFIG_VSX
- double buf[32];
int i;
#endif
@@ -490,13 +541,10 @@ static long restore_user_regs(struct pt_
if (__get_user(current->thread.vrsave, (u32 __user *)&sr->mc_vregs[32]))
return 1;
#endif /* CONFIG_ALTIVEC */
+ if (copy_fpr_from_user(current, &sr->mc_fregs))
+ return 1;
#ifdef CONFIG_VSX
- if (__copy_from_user(buf, &sr->mc_fregs,sizeof(sr->mc_fregs)))
- return 1;
- for (i = 0; i < 32 ; i++)
- current->thread.TS_FPR(i) = buf[i];
- memcpy(¤t->thread.fpscr, &buf[i], sizeof(double));
/*
* Force the process to reload the VSX registers from
* current->thread when it next does VSX instruction.
@@ -507,18 +555,11 @@ static long restore_user_regs(struct pt_
* Restore altivec registers from the stack to a local
* buffer, then write this out to the thread_struct
*/
- if (__copy_from_user(buf, &sr->mc_vsregs,
- sizeof(sr->mc_vsregs)))
+ if (copy_vsx_from_user(current, &sr->mc_vsregs))
return 1;
- for (i = 0; i < 32 ; i++)
- current->thread.fpr[i][TS_VSRLOWOFFSET] = buf[i];
} else if (current->thread.used_vsr)
for (i = 0; i < 32 ; i++)
current->thread.fpr[i][TS_VSRLOWOFFSET] = 0;
-#else
- if (__copy_from_user(current->thread.fpr, &sr->mc_fregs,
- sizeof(sr->mc_fregs)))
- return 1;
#endif /* CONFIG_VSX */
/*
* force the process to reload the FP registers from
Index: linux-2.6-ozlabs/arch/powerpc/kernel/signal_64.c
===================================================================
--- linux-2.6-ozlabs.orig/arch/powerpc/kernel/signal_64.c
+++ linux-2.6-ozlabs/arch/powerpc/kernel/signal_64.c
@@ -89,10 +89,6 @@ static long setup_sigcontext(struct sigc
#endif
unsigned long msr = regs->msr;
long err = 0;
-#ifdef CONFIG_VSX
- double buf[FP_REGS_SIZE];
- int i;
-#endif
flush_fp_to_thread(current);
@@ -117,12 +113,9 @@ static long setup_sigcontext(struct sigc
err |= __put_user(0, &sc->v_regs);
#endif /* CONFIG_ALTIVEC */
flush_fp_to_thread(current);
+ /* copy fpr regs and fpscr */
+ err |= copy_fpr_to_user(&sc->fp_regs, current);
#ifdef CONFIG_VSX
- /* Copy FP to local buffer then write that out */
- for (i = 0; i < 32 ; i++)
- buf[i] = current->thread.TS_FPR(i);
- memcpy(&buf[i], ¤t->thread.fpscr, sizeof(double));
- err |= __copy_to_user(&sc->fp_regs, buf, FP_REGS_SIZE);
/*
* Copy VSX low doubleword to local buffer for formatting,
* then out to userspace. Update v_regs to point after the
@@ -131,17 +124,12 @@ static long setup_sigcontext(struct sigc
if (current->thread.used_vsr) {
flush_vsx_to_thread(current);
v_regs += ELF_NVRREG;
- for (i = 0; i < 32 ; i++)
- buf[i] = current->thread.fpr[i][TS_VSRLOWOFFSET];
- err |= __copy_to_user(v_regs, buf, 32 * sizeof(double));
+ err |= copy_vsx_to_user(v_regs, current);
/* set MSR_VSX in the MSR value in the frame to
* indicate that sc->vs_reg) contains valid data.
*/
msr |= MSR_VSX;
}
-#else /* CONFIG_VSX */
- /* copy fpr regs and fpscr */
- err |= __copy_to_user(&sc->fp_regs, ¤t->thread.fpr, FP_REGS_SIZE);
#endif /* CONFIG_VSX */
err |= __put_user(&sc->gp_regs, &sc->regs);
WARN_ON(!FULL_REGS(regs));
@@ -165,13 +153,12 @@ static long restore_sigcontext(struct pt
#ifdef CONFIG_ALTIVEC
elf_vrreg_t __user *v_regs;
#endif
-#ifdef CONFIG_VSX
- double buf[FP_REGS_SIZE];
- int i;
-#endif
unsigned long err = 0;
unsigned long save_r13 = 0;
unsigned long msr;
+#ifdef CONFIG_VSX
+ int i;
+#endif
/* If this is not a signal return, we preserve the TLS in r13 */
if (!sig)
@@ -234,15 +221,9 @@ static long restore_sigcontext(struct pt
else
current->thread.vrsave = 0;
#endif /* CONFIG_ALTIVEC */
-#ifdef CONFIG_VSX
/* restore floating point */
- err |= __copy_from_user(buf, &sc->fp_regs, FP_REGS_SIZE);
- if (err)
- return err;
- for (i = 0; i < 32 ; i++)
- current->thread.TS_FPR(i) = buf[i];
- memcpy(¤t->thread.fpscr, &buf[i], sizeof(double));
-
+ err |= copy_fpr_from_user(current, &sc->fp_regs);
+#ifdef CONFIG_VSX
/*
* Get additional VSX data. Update v_regs to point after the
* VMX data. Copy VSX low doubleword from userspace to local
@@ -250,14 +231,12 @@ static long restore_sigcontext(struct pt
*/
v_regs += ELF_NVRREG;
if ((msr & MSR_VSX) != 0)
- err |= __copy_from_user(buf, v_regs, 32 * sizeof(double));
+ err |= copy_vsx_from_user(current, v_regs);
else
- memset(buf, 0, 32 * sizeof(double));
+ for (i = 0; i < 32 ; i++)
+ current->thread.fpr[i][TS_VSRLOWOFFSET] = 0;
- for (i = 0; i < 32 ; i++)
- current->thread.fpr[i][TS_VSRLOWOFFSET] = buf[i];
#else
- err |= __copy_from_user(¤t->thread.fpr, &sc->fp_regs, FP_REGS_SIZE);
#endif
return err;
}
^ permalink raw reply
* [PATCH] powerpc/5200: fix compile warnings in bestcomm driver
From: Grant Likely @ 2008-07-02 4:08 UTC (permalink / raw)
To: linuxppc-dev
From: Grant Likely <grant.likely@secretlab.ca>
Fix for the following compiler warnings:
CC arch/powerpc/sysdev/bestcomm/bestcomm.o
arch/powerpc/sysdev/bestcomm/bestcomm.c: In function 'mpc52xx_bcom_probe':
arch/powerpc/sysdev/bestcomm/bestcomm.c:446:
warning: format '%08lx' expects type 'long unsigned int',
but argument 2 has type 'phys_addr_t'
CC arch/powerpc/sysdev/bestcomm/sram.o
arch/powerpc/sysdev/bestcomm/sram.c: In function 'bcom_sram_init':
arch/powerpc/sysdev/bestcomm/sram.c:89:
warning: format '%08lx' expects type 'long unsigned int',
but argument 3 has type 'phys_addr_t'
Signed-off-by: Grant Likely <grant.likely@secretlab.ca>
---
arch/powerpc/sysdev/bestcomm/bestcomm.c | 2 +-
arch/powerpc/sysdev/bestcomm/sram.c | 2 +-
2 files changed, 2 insertions(+), 2 deletions(-)
diff --git a/arch/powerpc/sysdev/bestcomm/bestcomm.c b/arch/powerpc/sysdev/bestcomm/bestcomm.c
index 64ec7d6..446c9ea 100644
--- a/arch/powerpc/sysdev/bestcomm/bestcomm.c
+++ b/arch/powerpc/sysdev/bestcomm/bestcomm.c
@@ -443,7 +443,7 @@ mpc52xx_bcom_probe(struct of_device *op, const struct of_device_id *match)
/* Done ! */
printk(KERN_INFO "DMA: MPC52xx BestComm engine @%08lx ok !\n",
- bcom_eng->regs_base);
+ (long)bcom_eng->regs_base);
return 0;
diff --git a/arch/powerpc/sysdev/bestcomm/sram.c b/arch/powerpc/sysdev/bestcomm/sram.c
index 9978438..5d74ef7 100644
--- a/arch/powerpc/sysdev/bestcomm/sram.c
+++ b/arch/powerpc/sysdev/bestcomm/sram.c
@@ -86,7 +86,7 @@ int bcom_sram_init(struct device_node *sram_node, char *owner)
if (!bcom_sram->base_virt) {
printk(KERN_ERR "%s: bcom_sram_init: "
"Map error SRAM zone 0x%08lx (0x%0x)!\n",
- owner, bcom_sram->base_phys, bcom_sram->size );
+ owner, (long)bcom_sram->base_phys, bcom_sram->size );
rv = -ENOMEM;
goto error_release;
}
^ permalink raw reply related
* Re: [PATCH] ibm_newemac: Fixes memory leak in ibm_newemac ethernet driver
From: SathyaNarayanan @ 2008-07-02 4:45 UTC (permalink / raw)
To: benh; +Cc: linuxppc-dev, Stefan Roese, netdev
In-Reply-To: <1214263310.8011.280.camel@pasglop>
[-- Attachment #1: Type: text/plain, Size: 2363 bytes --]
Hi Benn,
Please find my comments below.
On Tue, Jun 24, 2008 at 4:51 AM, Benjamin Herrenschmidt <
benh@kernel.crashing.org> wrote:
> On Mon, 2008-06-23 at 14:55 +0200, Stefan Roese wrote:
> > From: Sathya Narayanan <sathyan@teamf1.com>
> >
> > This patch addresses the memory leak happenning in drivers transmit queue
> > under heavy load condition. Once the transmit queue becomes full, driver
> > does an automatic wrapup of queue. During which the untransmitted SKB's
> are
> > lost without getting freed up.
>
> This would be a bug. We should stop the queue when full instead.
Actually the meachanism of stopping the queue and starting it is already
there. But even then due to some sync issue between the poll routine and
xmit, we were resulted in using the slots of skb which was not actually got
freed before.
I agree this could a bug , Since its not is not clear why buffers are not
getting transferred timely?. But to handle this we should have a work around
otherwise system may go out of memory. If we go for stopping the queue in
these scenario also ( Where a unfreed skbs slot has been assigned to
another ), Then kernel may call tx timeout, And reset the driver. In that
case handelling this special case here could lead us better performance as
compared to stopping the queue
Let me know your comments.
>
>
> > Signed-off-by: Sathya Narayanan <sathyan@teamf1.com>
> > Signed-off-by: Stefan Roese <sr@denx.de>
> > ---
> > drivers/net/ibm_newemac/core.c | 7 +++++++
> > 1 files changed, 7 insertions(+), 0 deletions(-)
> >
> > diff --git a/drivers/net/ibm_newemac/core.c
> b/drivers/net/ibm_newemac/core.c
> > index aa407b2..ee868b6 100644
> > --- a/drivers/net/ibm_newemac/core.c
> > +++ b/drivers/net/ibm_newemac/core.c
> > @@ -1328,6 +1328,13 @@ static int emac_start_xmit(struct sk_buff *skb,
> struct net_device *ndev)
> >
> > DBG2(dev, "xmit(%u) %d" NL, len, slot);
> >
> > + if (dev->tx_skb[slot] && dev->tx_desc[slot].data_ptr) {
> > + dev_kfree_skb(dev->tx_skb[slot]);
> > + dev->tx_skb[slot] = NULL;
> > + dev->tx_cnt--;
> > + ++dev->estats.tx_dropped;
> > + }
> > +
> > dev->tx_skb[slot] = skb;
> > dev->tx_desc[slot].data_ptr = dma_map_single(&dev->ofdev->dev,
> > skb->data, len,
>
>
[-- Attachment #2: Type: text/html, Size: 3603 bytes --]
^ permalink raw reply
* Re: [PATCH] ibm_newemac: Fixes memory leak in ibm_newemac ethernet driver
From: Benjamin Herrenschmidt @ 2008-07-02 5:46 UTC (permalink / raw)
To: SathyaNarayanan; +Cc: linuxppc-dev, Stefan Roese, netdev
In-Reply-To: <1946a170807012145v6e6d629cvea443f2b8462d708@mail.gmail.com>
>
> Actually the meachanism of stopping the queue and starting it is
> already there. But even then due to some sync issue between the poll
> routine and xmit, we were resulted in using the slots of skb which was
> not actually got freed before.
> I agree this could a bug , Since its not is not clear why buffers are
> not getting transferred timely?. But to handle this we should have a
> work around otherwise system may go out of memory. If we go for
> stopping the queue in these scenario also ( Where a unfreed skbs slot
> has been assigned to another ), Then kernel may call tx timeout, And
> reset the driver. In that case handelling this special case here could
> lead us better performance as compared to stopping the queue
> Let me know your comments.
Well, if we have a bug, we need to fix it. ie, understand how it is that
the existing mechanism to stop the queue doesn't work, and prevent xmit
from overwriting a non-clear transmit slot (possibly displaying an error
to help us track down the bug).
I'll have to dig a bit, I'll see if I can find some time tomorrow.
Cheers,
Ben.
^ permalink raw reply
* Re: [PATCH] ibm_newemac: Fixes memory leak in ibm_newemac ethernet driver
From: SathyaNarayanan @ 2008-07-02 6:11 UTC (permalink / raw)
To: benh; +Cc: linuxppc-dev, Stefan Roese, netdev
In-Reply-To: <1214977560.21182.35.camel@pasglop>
[-- Attachment #1: Type: text/plain, Size: 1448 bytes --]
On Wed, Jul 2, 2008 at 11:16 AM, Benjamin Herrenschmidt <
benh@kernel.crashing.org> wrote:
>
> >
> > Actually the meachanism of stopping the queue and starting it is
> > already there. But even then due to some sync issue between the poll
> > routine and xmit, we were resulted in using the slots of skb which was
> > not actually got freed before.
> > I agree this could a bug , Since its not is not clear why buffers are
> > not getting transferred timely?. But to handle this we should have a
> > work around otherwise system may go out of memory. If we go for
> > stopping the queue in these scenario also ( Where a unfreed skbs slot
> > has been assigned to another ), Then kernel may call tx timeout, And
> > reset the driver. In that case handelling this special case here could
> > lead us better performance as compared to stopping the queue
> > Let me know your comments.
>
> Well, if we have a bug, we need to fix it. ie, understand how it is that
> the existing mechanism to stop the queue doesn't work, and prevent xmit
> from overwriting a non-clear transmit slot (possibly displaying an error
> to help us track down the bug).
>
> I'll have to dig a bit, I'll see if I can find some time tomorrow.
The reason could be sync issue between poll and xmit. I would like to have
one clarification , Why in the present design no locks has been implemented
to protect the queue from simulatenous access ??
>
>
> Cheers,
> Ben.
>
>
>
>
[-- Attachment #2: Type: text/html, Size: 1990 bytes --]
^ permalink raw reply
* [BUG] 2.6.26-rc8-git2 - kernel BUG at mm/page_alloc.c:585
From: Kamalesh Babulal @ 2008-07-02 6:25 UTC (permalink / raw)
To: kernel list
Cc: linuxppc-dev, linux-mm, Andrew Morton, kernel-testers,
Balbir Singh
Hi,
when running kernbench on powerpc box booted with the 2.6.26-rc8-git2
kernel the machine drops to xmon with the kernel BUG
kernel BUG at mm/page_alloc.c:585!
cpu 0x0: Vector: 700 (Program Check) at [c0000000c389ed50]
pc: c0000000000e22ec: .__rmqueue+0x178/0x25c
lr: c0000000000e22ec: .__rmqueue+0x178/0x25c
sp: c0000000c389efd0
msr: 8000000000029032
current = 0xc0000000f6e0e790
paca = 0xc000000000873480
pid = 3421, comm = tar
kernel BUG at mm/page_alloc.c:585!
enter ? for help
[c0000000c389efd0] c0000000000e22d0 .__rmqueue+0x15c/0x25c (unreliable)
[c0000000c389f0a0] c0000000000e2438 .rmqueue_bulk+0x68/0xf0
[c0000000c389f170] c0000000000e43cc .get_page_from_freelist+0x2d0/0x848
[c0000000c389f2b0] c0000000000e4abc .__alloc_pages_internal+0x12c/0x494
[c0000000c389f3c0] c0000000000e4e6c .__alloc_pages+0x1c/0x30
[c0000000c389f440] c0000000001107d8 .kmem_getpages+0x90/0x198
[c0000000c389f4e0] c000000000111200 .fallback_alloc+0x190/0x26c
[c0000000c389f5b0] c000000000111478 .____cache_alloc_node+0x19c/0x1d0
[c0000000c389f660] c000000000111e90 .kmem_cache_alloc+0x150/0x1f8
[c0000000c389f710] d00000000019fb50 .ext3_alloc_inode+0x2c/0x74 [ext3]
[c0000000c389f790] c00000000013725c .alloc_inode+0x58/0x278
[c0000000c389f830] c0000000001374b4 .new_inode+0x38/0xd4
[c0000000c389f8d0] d000000000193930 .ext3_new_inode+0x90/0xc64 [ext3]
[c0000000c389f9f0] d00000000019dc28 .ext3_create+0xc4/0x16c [ext3]
[c0000000c389fab0] c000000000127944 .vfs_create+0x12c/0x1d4
[c0000000c389fb60] c00000000012b54c .do_filp_open+0x210/0x8b4
[c0000000c389fd00] c0000000001191f8 .do_sys_open+0x80/0x144
[c0000000c389fdb0] c00000000015f5d8 .compat_sys_open+0x2c/0x44
[c0000000c389fe30] c0000000000086dc syscall_exit+0x0/0x40
--- Exception: c00 (System Call) at 000000000ff0e6d4
SP (ffd3f5a0) is in userspace
0:mon> r
R00 = 00000000f0008d00 R16 = 0000000000000001
R01 = c0000000c389efd0 R17 = 0000000000000044
R02 = c0000000007e74e0 R18 = 0000000000000001
R03 = 0000000000000001 R19 = c00000010ffff828
R04 = f000000000069000 R20 = c00000010ffff800
R05 = 0000000000000003 R21 = c0000001ffff5700
R06 = 0000000000000008 R22 = 0000000000000000
R07 = 0000000000000000 R23 = 0000000000000001
R08 = 0000000000001180 R24 = 0000000000000007
R09 = 00000000f0008cff R25 = 0000000000000007
R10 = c0000001ffff5700 R26 = 0000000000000080
R11 = c000000000885df8 R27 = c0000001ffff5e28
R12 = c000000010010080 R28 = f000000000066000
R13 = c000000000873480 R29 = f000000000069000
R14 = 0000000000000001 R30 = c000000000791ce0
R15 = 0000000000000001 R31 = c0000000c389efd0
pc = c0000000000e22ec .__rmqueue+0x178/0x25c
lr = c0000000000e22ec .__rmqueue+0x178/0x25c
msr = 8000000000029032 cr = 24000442
ctr = 0000000000000003 xer = 0000000020000000 trap = 700
0:mon> u
SLB contents of cpu 0
00 c000000008000000 40004f7ca3000510 1T ESID= c00000 VSID= 4f7ca3 LLP:110
01 d000000008000000 4000eb71b0000510 1T ESID= d00000 VSID= eb71b0 LLP:110
11 0000000008000000 000020b2b24a4d90 256M ESID= 0 VSID= 20b2b24a4 LLP:110
12 00000000f8000000 00002bea2a039d90 256M ESID= f VSID= 2bea2a039 LLP:110
13 0000000048000000 000023b06bd10d90 256M ESID= 4 VSID= 23b06bd10 LLP:110
14 0000000018000000 0000217220abfd90 256M ESID= 1 VSID= 217220abf LLP:110
38 f000000008000000 4000235bcc000500 1T ESID= f00000 VSID= 235bcc LLP:100
--
Thanks & Regards,
Kamalesh Babulal,
Linux Technology Center,
IBM, ISTL.
^ permalink raw reply
* [PATCH] powerpc/85xx: Add support for MPC8536DS
From: Kumar Gala @ 2008-07-02 7:01 UTC (permalink / raw)
To: linuxppc-dev
Add support for the MPC8536 process and MPC8536DS reference board. The
MPC8536 is an e500v2 based SoC which eTSEC, USB, SATA, PCI, and PCIe.
The USB and SATA IP blocks are similiar to those on the PQ2 Pro SoCs and
thus use the same drivers.
Signed-off-by: Kumar Gala <galak@kernel.crashing.org>
---
I've cut the defconfig out of the patch to make it easier to review. Plus
this patch will go via my powerpc-next tree.
arch/powerpc/boot/dts/mpc8536ds.dts | 420 ++++++++
arch/powerpc/configs/mpc8536_ds_defconfig | 1637 +++++++++++++++++++++++++++++
arch/powerpc/platforms/85xx/Kconfig | 6 +
arch/powerpc/platforms/85xx/Makefile | 1 +
arch/powerpc/platforms/85xx/mpc8536_ds.c | 125 +++
arch/powerpc/sysdev/fsl_pci.c | 2 +
include/linux/pci_ids.h | 2 +
7 files changed, 2193 insertions(+), 0 deletions(-)
create mode 100644 arch/powerpc/boot/dts/mpc8536ds.dts
create mode 100644 arch/powerpc/configs/mpc8536_ds_defconfig
create mode 100644 arch/powerpc/platforms/85xx/mpc8536_ds.c
diff --git a/arch/powerpc/boot/dts/mpc8536ds.dts b/arch/powerpc/boot/dts/mpc8536ds.dts
new file mode 100644
index 0000000..98ad27a
--- /dev/null
+++ b/arch/powerpc/boot/dts/mpc8536ds.dts
@@ -0,0 +1,420 @@
+/*
+ * MPC8536 DS Device Tree Source
+ *
+ * Copyright 2008 Freescale Semiconductor, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ */
+
+/dts-v1/;
+
+/ {
+ model = "fsl,mpc8536ds";
+ compatible = "fsl,mpc8536ds";
+ #address-cells = <1>;
+ #size-cells = <1>;
+
+ aliases {
+ ethernet0 = &enet0;
+ ethernet1 = &enet1;
+ serial0 = &serial0;
+ serial1 = &serial1;
+ pci0 = &pci0;
+ pci1 = &pci1;
+ pci2 = &pci2;
+ pci3 = &pci3;
+ };
+
+ cpus {
+ #cpus = <1>;
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ PowerPC,8536@0 {
+ device_type = "cpu";
+ reg = <0>;
+ next-level-cache = <&L2>;
+ };
+ };
+
+ memory {
+ device_type = "memory";
+ reg = <00000000 00000000>; // Filled by U-Boot
+ };
+
+ soc@ffe00000 {
+ #address-cells = <1>;
+ #size-cells = <1>;
+ device_type = "soc";
+ ranges = <0x0 0xffe00000 0x100000>;
+ reg = <0xffe00000 0x1000>;
+ bus-frequency = <0>; // Filled out by uboot.
+
+ memory-controller@2000 {
+ compatible = "fsl,mpc8536-memory-controller";
+ reg = <0x2000 0x1000>;
+ interrupt-parent = <&mpic>;
+ interrupts = <18 0x2>;
+ };
+
+ L2: l2-cache-controller@20000 {
+ compatible = "fsl,mpc8536-l2-cache-controller";
+ reg = <0x20000 0x1000>;
+ interrupt-parent = <&mpic>;
+ interrupts = <16 0x2>;
+ };
+
+ i2c@3000 {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ cell-index = <0>;
+ compatible = "fsl-i2c";
+ reg = <0x3000 0x100>;
+ interrupts = <43 0x2>;
+ interrupt-parent = <&mpic>;
+ dfsrr;
+ };
+
+ i2c@3100 {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ cell-index = <1>;
+ compatible = "fsl-i2c";
+ reg = <0x3100 0x100>;
+ interrupts = <43 0x2>;
+ interrupt-parent = <&mpic>;
+ dfsrr;
+ rtc@68 {
+ compatible = "dallas,ds3232";
+ reg = <0x68>;
+ };
+ };
+
+ dma@21300 {
+ #address-cells = <1>;
+ #size-cells = <1>;
+ compatible = "fsl,mpc8536-dma", "fsl,eloplus-dma";
+ reg = <0x21300 4>;
+ ranges = <0 0x21100 0x200>;
+ cell-index = <0>;
+ dma-channel@0 {
+ compatible = "fsl,mpc8536-dma-channel",
+ "fsl,eloplus-dma-channel";
+ reg = <0x0 0x80>;
+ cell-index = <0>;
+ interrupt-parent = <&mpic>;
+ interrupts = <14 0x2>;
+ };
+ dma-channel@80 {
+ compatible = "fsl,mpc8536-dma-channel",
+ "fsl,eloplus-dma-channel";
+ reg = <0x80 0x80>;
+ cell-index = <1>;
+ interrupt-parent = <&mpic>;
+ interrupts = <15 0x2>;
+ };
+ dma-channel@100 {
+ compatible = "fsl,mpc8536-dma-channel",
+ "fsl,eloplus-dma-channel";
+ reg = <0x100 0x80>;
+ cell-index = <2>;
+ interrupt-parent = <&mpic>;
+ interrupts = <16 0x2>;
+ };
+ dma-channel@180 {
+ compatible = "fsl,mpc8536-dma-channel",
+ "fsl,eloplus-dma-channel";
+ reg = <0x180 0x80>;
+ cell-index = <3>;
+ interrupt-parent = <&mpic>;
+ interrupts = <17 0x2>;
+ };
+ };
+
+ mdio@24520 {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ compatible = "fsl,gianfar-mdio";
+ reg = <0x24520 0x20>;
+
+ phy0: ethernet-phy@0 {
+ interrupt-parent = <&mpic>;
+ interrupts = <10 0x1>;
+ reg = <0>;
+ device_type = "ethernet-phy";
+ };
+ phy1: ethernet-phy@1 {
+ interrupt-parent = <&mpic>;
+ interrupts = <10 0x1>;
+ reg = <1>;
+ device_type = "ethernet-phy";
+ };
+ };
+
+ usb@22000 {
+ compatible = "fsl,mpc8536-usb2-mph", "fsl-usb2-mph";
+ reg = <0x22000 0x1000>;
+ #address-cells = <1>;
+ #size-cells = <0>;
+ interrupt-parent = <&mpic>;
+ interrupts = <28 0x2>;
+ phy_type = "ulpi";
+ };
+
+ usb@23000 {
+ compatible = "fsl,mpc8536-usb2-mph", "fsl-usb2-mph";
+ reg = <0x23000 0x1000>;
+ #address-cells = <1>;
+ #size-cells = <0>;
+ interrupt-parent = <&mpic>;
+ interrupts = <46 0x2>;
+ phy_type = "ulpi";
+ };
+
+ enet0: ethernet@24000 {
+ cell-index = <0>;
+ device_type = "network";
+ model = "TSEC";
+ compatible = "gianfar";
+ reg = <0x24000 0x1000>;
+ local-mac-address = [ 00 00 00 00 00 00 ];
+ interrupts = <29 2 30 2 34 2>;
+ interrupt-parent = <&mpic>;
+ phy-handle = <&phy1>;
+ phy-connection-type = "rgmii-id";
+ };
+
+ enet1: ethernet@26000 {
+ cell-index = <1>;
+ device_type = "network";
+ model = "TSEC";
+ compatible = "gianfar";
+ reg = <0x26000 0x1000>;
+ local-mac-address = [ 00 00 00 00 00 00 ];
+ interrupts = <31 2 32 2 33 2>;
+ interrupt-parent = <&mpic>;
+ phy-handle = <&phy0>;
+ phy-connection-type = "rgmii-id";
+ };
+
+ usb@2b000 {
+ compatible = "fsl,mpc8536-usb2-dr", "fsl-usb2-dr";
+ reg = <0x2b000 0x1000>;
+ #address-cells = <1>;
+ #size-cells = <0>;
+ interrupt-parent = <&mpic>;
+ interrupts = <60 0x2>;
+ dr_mode = "peripheral";
+ phy_type = "ulpi";
+ };
+
+ serial0: serial@4500 {
+ cell-index = <0>;
+ device_type = "serial";
+ compatible = "ns16550";
+ reg = <0x4500 0x100>;
+ clock-frequency = <0>;
+ interrupts = <42 0x2>;
+ interrupt-parent = <&mpic>;
+ };
+
+ serial1: serial@4600 {
+ cell-index = <1>;
+ device_type = "serial";
+ compatible = "ns16550";
+ reg = <0x4600 0x100>;
+ clock-frequency = <0>;
+ interrupts = <42 0x2>;
+ interrupt-parent = <&mpic>;
+ };
+
+ sata@18000 {
+ compatible = "fsl,mpc8536-sata", "fsl,pq-sata";
+ reg = <0x18000 0x1000>;
+ cell-index = <1>;
+ interrupts = <74 0x2>;
+ interrupt-parent = <&mpic>;
+ };
+
+ sata@19000 {
+ compatible = "fsl,mpc8536-sata", "fsl,pq-sata";
+ reg = <0x19000 0x1000>;
+ cell-index = <2>;
+ interrupts = <41 0x2>;
+ interrupt-parent = <&mpic>;
+ };
+
+ global-utilities@e0000 { //global utilities block
+ compatible = "fsl,mpc8548-guts";
+ reg = <0xe0000 0x1000>;
+ fsl,has-rstcr;
+ };
+
+ mpic: pic@40000 {
+ clock-frequency = <0>;
+ interrupt-controller;
+ #address-cells = <0>;
+ #interrupt-cells = <2>;
+ reg = <0x40000 0x40000>;
+ compatible = "chrp,open-pic";
+ device_type = "open-pic";
+ big-endian;
+ };
+
+ msi@41600 {
+ compatible = "fsl,mpc8536-msi", "fsl,mpic-msi";
+ reg = <0x41600 0x80>;
+ msi-available-ranges = <0 0x100>;
+ interrupts = <
+ 0xe0 0
+ 0xe1 0
+ 0xe2 0
+ 0xe3 0
+ 0xe4 0
+ 0xe5 0
+ 0xe6 0
+ 0xe7 0>;
+ interrupt-parent = <&mpic>;
+ };
+ };
+
+ pci0: pci@ffe08000 {
+ cell-index = <0>;
+ compatible = "fsl,mpc8540-pci";
+ device_type = "pci";
+ interrupt-map-mask = <0xf800 0x0 0x0 0x7>;
+ interrupt-map = <
+
+ /* IDSEL 0x11 J17 Slot 1 */
+ 0x8800 0 0 1 &mpic 1 1
+ 0x8800 0 0 2 &mpic 2 1
+ 0x8800 0 0 3 &mpic 3 1
+ 0x8800 0 0 4 &mpic 4 1>;
+
+ interrupt-parent = <&mpic>;
+ interrupts = <24 0x2>;
+ bus-range = <0 0xff>;
+ ranges = <0x02000000 0 0x80000000 0x80000000 0 0x10000000
+ 0x01000000 0 0x00000000 0xffc00000 0 0x00010000>;
+ clock-frequency = <66666666>;
+ #interrupt-cells = <1>;
+ #size-cells = <2>;
+ #address-cells = <3>;
+ reg = <0xffe08000 0x1000>;
+ };
+
+ pci1: pcie@ffe09000 {
+ cell-index = <1>;
+ compatible = "fsl,mpc8548-pcie";
+ device_type = "pci";
+ #interrupt-cells = <1>;
+ #size-cells = <2>;
+ #address-cells = <3>;
+ reg = <0xffe09000 0x1000>;
+ bus-range = <0 0xff>;
+ ranges = <0x02000000 0 0x98000000 0x98000000 0 0x08000000
+ 0x01000000 0 0x00000000 0xffc20000 0 0x00010000>;
+ clock-frequency = <33333333>;
+ interrupt-parent = <&mpic>;
+ interrupts = <25 0x2>;
+ interrupt-map-mask = <0xf800 0 0 7>;
+ interrupt-map = <
+ /* IDSEL 0x0 */
+ 0000 0 0 1 &mpic 4 1
+ 0000 0 0 2 &mpic 5 1
+ 0000 0 0 3 &mpic 6 1
+ 0000 0 0 4 &mpic 7 1
+ >;
+ pcie@0 {
+ reg = <0 0 0 0 0>;
+ #size-cells = <2>;
+ #address-cells = <3>;
+ device_type = "pci";
+ ranges = <0x02000000 0 0x98000000
+ 0x02000000 0 0x98000000
+ 0 0x08000000
+
+ 0x01000000 0 0x00000000
+ 0x01000000 0 0x00000000
+ 0 0x00010000>;
+ };
+ };
+
+ pci2: pcie@ffe0a000 {
+ cell-index = <2>;
+ compatible = "fsl,mpc8548-pcie";
+ device_type = "pci";
+ #interrupt-cells = <1>;
+ #size-cells = <2>;
+ #address-cells = <3>;
+ reg = <0xffe0a000 0x1000>;
+ bus-range = <0 0xff>;
+ ranges = <0x02000000 0 0x90000000 0x90000000 0 0x08000000
+ 0x01000000 0 0x00000000 0xffc10000 0 0x00010000>;
+ clock-frequency = <33333333>;
+ interrupt-parent = <&mpic>;
+ interrupts = <26 0x2>;
+ interrupt-map-mask = <0xf800 0 0 7>;
+ interrupt-map = <
+ /* IDSEL 0x0 */
+ 0000 0 0 1 &mpic 0 1
+ 0000 0 0 2 &mpic 1 1
+ 0000 0 0 3 &mpic 2 1
+ 0000 0 0 4 &mpic 3 1
+ >;
+ pcie@0 {
+ reg = <0 0 0 0 0>;
+ #size-cells = <2>;
+ #address-cells = <3>;
+ device_type = "pci";
+ ranges = <0x02000000 0 0x90000000
+ 0x02000000 0 0x90000000
+ 0 0x08000000
+
+ 0x01000000 0 0x00000000
+ 0x01000000 0 0x00000000
+ 0 0x00010000>;
+ };
+ };
+
+ pci3: pcie@ffe0b000 {
+ cell-index = <3>;
+ compatible = "fsl,mpc8548-pcie";
+ device_type = "pci";
+ #interrupt-cells = <1>;
+ #size-cells = <2>;
+ #address-cells = <3>;
+ reg = <0xffe0b000 0x1000>;
+ bus-range = <0 0xff>;
+ ranges = <0x02000000 0 0xa0000000 0xa0000000 0 0x20000000
+ 0x01000000 0 0x00000000 0xffc30000 0 0x00010000>;
+ clock-frequency = <33333333>;
+ interrupt-parent = <&mpic>;
+ interrupts = <27 0x2>;
+ interrupt-map-mask = <0xf800 0 0 7>;
+ interrupt-map = <
+ /* IDSEL 0x0 */
+ 0000 0 0 1 &mpic 8 1
+ 0000 0 0 2 &mpic 9 1
+ 0000 0 0 3 &mpic 10 1
+ 0000 0 0 4 &mpic 11 1
+ >;
+
+ pcie@0 {
+ reg = <0 0 0 0 0>;
+ #size-cells = <2>;
+ #address-cells = <3>;
+ device_type = "pci";
+ ranges = <0x02000000 0 0xa0000000
+ 0x02000000 0 0xa0000000
+ 0 0x20000000
+
+ 0x01000000 0 0x00000000
+ 0x01000000 0 0x00000000
+ 0 0x00100000>;
+ };
+ };
+};
diff --git a/arch/powerpc/platforms/85xx/Kconfig b/arch/powerpc/platforms/85xx/Kconfig
index 9cb8e29..cebea5c 100644
--- a/arch/powerpc/platforms/85xx/Kconfig
+++ b/arch/powerpc/platforms/85xx/Kconfig
@@ -38,6 +38,12 @@ config MPC85xx_MDS
help
This option enables support for the MPC85xx MDS board
+config MPC8536_DS
+ bool "Freescale MPC8536 DS"
+ select DEFAULT_UIMAGE
+ help
+ This option enables support for the MPC8536 DS board
+
config MPC85xx_DS
bool "Freescale MPC85xx DS"
select PPC_I8259
diff --git a/arch/powerpc/platforms/85xx/Makefile b/arch/powerpc/platforms/85xx/Makefile
index 6cea185..cb3054e 100644
--- a/arch/powerpc/platforms/85xx/Makefile
+++ b/arch/powerpc/platforms/85xx/Makefile
@@ -4,6 +4,7 @@
obj-$(CONFIG_MPC8540_ADS) += mpc85xx_ads.o
obj-$(CONFIG_MPC8560_ADS) += mpc85xx_ads.o
obj-$(CONFIG_MPC85xx_CDS) += mpc85xx_cds.o
+obj-$(CONFIG_MPC8536_DS) += mpc8536_ds.o
obj-$(CONFIG_MPC85xx_DS) += mpc85xx_ds.o
obj-$(CONFIG_MPC85xx_MDS) += mpc85xx_mds.o
obj-$(CONFIG_STX_GP3) += stx_gp3.o
diff --git a/arch/powerpc/platforms/85xx/mpc8536_ds.c b/arch/powerpc/platforms/85xx/mpc8536_ds.c
new file mode 100644
index 0000000..02f366c
--- /dev/null
+++ b/arch/powerpc/platforms/85xx/mpc8536_ds.c
@@ -0,0 +1,125 @@
+/*
+ * MPC8536 DS Board Setup
+ *
+ * Copyright 2008 Freescale Semiconductor, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ */
+
+#include <linux/stddef.h>
+#include <linux/kernel.h>
+#include <linux/pci.h>
+#include <linux/kdev_t.h>
+#include <linux/delay.h>
+#include <linux/seq_file.h>
+#include <linux/interrupt.h>
+#include <linux/of_platform.h>
+
+#include <asm/system.h>
+#include <asm/time.h>
+#include <asm/machdep.h>
+#include <asm/pci-bridge.h>
+#include <mm/mmu_decl.h>
+#include <asm/prom.h>
+#include <asm/udbg.h>
+#include <asm/mpic.h>
+
+#include <sysdev/fsl_soc.h>
+#include <sysdev/fsl_pci.h>
+
+void __init mpc8536_ds_pic_init(void)
+{
+ struct mpic *mpic;
+ struct resource r;
+ struct device_node *np = NULL;
+
+ np = of_find_node_by_type(np, "open-pic");
+
+ if (np == NULL) {
+ printk(KERN_ERR "Could not find open-pic node\n");
+ return;
+ }
+
+ if (of_address_to_resource(np, 0, &r)) {
+ printk(KERN_ERR "Failed to map mpic register space\n");
+ of_node_put(np);
+ return;
+ }
+
+ mpic = mpic_alloc(np, r.start,
+ MPIC_PRIMARY | MPIC_WANTS_RESET |
+ MPIC_BIG_ENDIAN | MPIC_BROKEN_FRR_NIRQS,
+ 0, 256, " OpenPIC ");
+ BUG_ON(mpic == NULL);
+
+ mpic_init(mpic);
+}
+
+/*
+ * Setup the architecture
+ */
+static void __init mpc8536_ds_setup_arch(void)
+{
+#ifdef CONFIG_PCI
+ struct device_node *np;
+#endif
+
+ if (ppc_md.progress)
+ ppc_md.progress("mpc8536_ds_setup_arch()", 0);
+
+#ifdef CONFIG_PCI
+ for_each_node_by_type(np, "pci") {
+ if (of_device_is_compatible(np, "fsl,mpc8540-pci") ||
+ of_device_is_compatible(np, "fsl,mpc8548-pcie")) {
+ struct resource rsrc;
+ of_address_to_resource(np, 0, &rsrc);
+ if ((rsrc.start & 0xfffff) == 0x8000)
+ fsl_add_bridge(np, 1);
+ else
+ fsl_add_bridge(np, 0);
+ }
+ }
+
+#endif
+
+ printk("MPC8536 DS board from Freescale Semiconductor\n");
+}
+
+static struct of_device_id mpc8536_ds_ids[] = {
+ { .type = "soc", },
+ { .compatible = "soc", },
+ {},
+};
+
+static int __init mpc8536_ds_publish_devices(void)
+{
+ return of_platform_bus_probe(NULL, mpc8536_ds_ids, NULL);
+}
+machine_device_initcall(mpc8536_ds, mpc8536_ds_publish_devices);
+
+/*
+ * Called very early, device-tree isn't unflattened
+ */
+static int __init mpc8536_ds_probe(void)
+{
+ unsigned long root = of_get_flat_dt_root();
+
+ return of_flat_dt_is_compatible(root, "fsl,mpc8536ds");
+}
+
+define_machine(mpc8536_ds) {
+ .name = "MPC8536 DS",
+ .probe = mpc8536_ds_probe,
+ .setup_arch = mpc8536_ds_setup_arch,
+ .init_IRQ = mpc8536_ds_pic_init,
+#ifdef CONFIG_PCI
+ .pcibios_fixup_bus = fsl_pcibios_fixup_bus,
+#endif
+ .get_irq = mpic_get_irq,
+ .restart = fsl_rstcr_restart,
+ .calibrate_decr = generic_calibrate_decr,
+ .progress = udbg_progress,
+};
diff --git a/arch/powerpc/sysdev/fsl_pci.c b/arch/powerpc/sysdev/fsl_pci.c
index 489ca5a..87b0aa1 100644
--- a/arch/powerpc/sysdev/fsl_pci.c
+++ b/arch/powerpc/sysdev/fsl_pci.c
@@ -243,6 +243,8 @@ DECLARE_PCI_FIXUP_HEADER(0x1957, PCI_DEVICE_ID_MPC8544E, quirk_fsl_pcie_header);
DECLARE_PCI_FIXUP_HEADER(0x1957, PCI_DEVICE_ID_MPC8544, quirk_fsl_pcie_header);
DECLARE_PCI_FIXUP_HEADER(0x1957, PCI_DEVICE_ID_MPC8572E, quirk_fsl_pcie_header);
DECLARE_PCI_FIXUP_HEADER(0x1957, PCI_DEVICE_ID_MPC8572, quirk_fsl_pcie_header);
+DECLARE_PCI_FIXUP_HEADER(0x1957, PCI_DEVICE_ID_MPC8536E, quirk_fsl_pcie_header);
+DECLARE_PCI_FIXUP_HEADER(0x1957, PCI_DEVICE_ID_MPC8536, quirk_fsl_pcie_header);
DECLARE_PCI_FIXUP_HEADER(0x1957, PCI_DEVICE_ID_MPC8641, quirk_fsl_pcie_header);
DECLARE_PCI_FIXUP_HEADER(0x1957, PCI_DEVICE_ID_MPC8641D, quirk_fsl_pcie_header);
DECLARE_PCI_FIXUP_HEADER(0x1957, PCI_DEVICE_ID_MPC8610, quirk_fsl_pcie_header);
diff --git a/include/linux/pci_ids.h b/include/linux/pci_ids.h
index eafc9d6..8b605bd 100644
--- a/include/linux/pci_ids.h
+++ b/include/linux/pci_ids.h
@@ -2170,6 +2170,8 @@
#define PCI_DEVICE_ID_MPC8544 0x0033
#define PCI_DEVICE_ID_MPC8572E 0x0040
#define PCI_DEVICE_ID_MPC8572 0x0041
+#define PCI_DEVICE_ID_MPC8536E 0x0050
+#define PCI_DEVICE_ID_MPC8536 0x0051
#define PCI_DEVICE_ID_MPC8641 0x7010
#define PCI_DEVICE_ID_MPC8641D 0x7011
#define PCI_DEVICE_ID_MPC8610 0x7018
--
1.5.5.1
^ permalink raw reply related
* Re: [PATCH] powerpc/85xx: Add support for MPC8536DS
From: Stephen Rothwell @ 2008-07-02 7:08 UTC (permalink / raw)
To: Kumar Gala; +Cc: linuxppc-dev
In-Reply-To: <Pine.LNX.4.64.0807020201030.24791@blarg.am.freescale.net>
[-- Attachment #1: Type: text/plain, Size: 1049 bytes --]
Hi Kumar,
On Wed, 2 Jul 2008 02:01:10 -0500 (CDT) Kumar Gala <galak@kernel.crashing.org> wrote:
>
> +void __init mpc8536_ds_pic_init(void)
> +{
> + struct mpic *mpic;
> + struct resource r;
> + struct device_node *np = NULL;
You don't need to initialise this.
> + np = of_find_node_by_type(np, "open-pic");
> +
Extra blank line.
> + if (np == NULL) {
> + printk(KERN_ERR "Could not find open-pic node\n");
> + return;
> + }
> +
> + if (of_address_to_resource(np, 0, &r)) {
> + printk(KERN_ERR "Failed to map mpic register space\n");
> + of_node_put(np);
> + return;
> + }
> +
> + mpic = mpic_alloc(np, r.start,
> + MPIC_PRIMARY | MPIC_WANTS_RESET |
> + MPIC_BIG_ENDIAN | MPIC_BROKEN_FRR_NIRQS,
> + 0, 256, " OpenPIC ");
> + BUG_ON(mpic == NULL);
> +
> + mpic_init(mpic);
You leak a reference to np here.
> +static struct of_device_id mpc8536_ds_ids[] = {
__initdata, please.
--
Cheers,
Stephen Rothwell sfr@canb.auug.org.au
http://www.canb.auug.org.au/~sfr/
[-- Attachment #2: Type: application/pgp-signature, Size: 197 bytes --]
^ permalink raw reply
* [PATCH] USB: EHCI: Reconciling USB register differences on MPC85xx vs MPC83xx
From: Kumar Gala @ 2008-07-02 7:14 UTC (permalink / raw)
To: dbrownell, Greg KH; +Cc: linuxppc-dev, linux-usb
From: Srikanth Srinivasan <srikanth.srinivasan@freescale.com>
A couple of USB register initializations had to be changed on MPC85xx
platforms. This is due to the internal SoC buses being different on
MPC83xx SoCs vs MPC85xx SoCs.
We currently handle this via an ifdef since 83xx and 85xx are mutually
exclusive kernel builds.
Signed-off-by: Srikanth Srinivasan <srikanth.srinivasan@freescale.com>
Signed-off-by: Kumar Gala <galak@kernel.crashing.org>
---
This is for 2.6.27 and is need for proper USB functionality of the
MPC8536DS platform that will go in via the powerpc tree in 2.6.27.
- k
drivers/usb/host/ehci-fsl.c | 5 +++++
1 files changed, 5 insertions(+), 0 deletions(-)
diff --git a/drivers/usb/host/ehci-fsl.c b/drivers/usb/host/ehci-fsl.c
index 7370d61..237a779 100644
--- a/drivers/usb/host/ehci-fsl.c
+++ b/drivers/usb/host/ehci-fsl.c
@@ -230,8 +230,13 @@ static void mpc83xx_usb_setup(struct usb_hcd *hcd)
/* put controller in host mode. */
ehci_writel(ehci, 0x00000003, non_ehci + FSL_SOC_USB_USBMODE);
+#ifdef CONFIG_PPC_85xx
+ out_be32(non_ehci + FSL_SOC_USB_PRICTRL, 0x00000008);
+ out_be32(non_ehci + FSL_SOC_USB_AGECNTTHRSH, 0x00000080);
+#else
out_be32(non_ehci + FSL_SOC_USB_PRICTRL, 0x0000000c);
out_be32(non_ehci + FSL_SOC_USB_AGECNTTHRSH, 0x00000040);
+#endif
out_be32(non_ehci + FSL_SOC_USB_SICTRL, 0x00000001);
}
--
1.5.5.1
^ permalink raw reply related
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox