* Re: radeonfb, dedicate memory to something else
From: Matt Sealey @ 2008-07-22 8:31 UTC (permalink / raw)
To: Jon Smirl; +Cc: ppc-dev
In-Reply-To: <9e4733910807200826m22b676aewa89de8df9fd6da33@mail.gmail.com>
Jon Smirl wrote:
> On 7/20/08, Matt Sealey <matt@genesi-usa.com> wrote:
>> Hi guys,
>>
>> I know this isn't a PPC question, but since some of the RadeonFB developers
>> live here I thought best (and it's about a PPC platform).
>>
>> Is there any way to hack up the RadeonFB driver - or anything related - to
>> reserve portions of the memory for a "fake" MTD or so, and still use the
>> Radeon as a graphics device? What I am talking about basically is turning
>> a 64MB Radeon card into a 32MB Radeon card, or a 128MB one into a 64MB
>> card..
>
> Somebody write this long ago. Maybe around 2000. That's all I
> remember. I think they made the video memory into a ram disk.
Yeah making it into a ramdisk precludes the use of the card as a video card
though.. this is what I am trying to get around. If fbdev and X can cooperate
on believing that a 64MB card is a 32MB card, then the upper 32MB can be used
to attach the MTD "ram" driver at a certain address (maybe we can even make a
hacky stub driver that recognizes this configuration based on OF tree..)
There are obvious limitations in that the Pegasos/Efika firmware only will
map 128MB of video memory, and the PCI BAR is limited to 256MB chunks anyway,
but that shouldn't matter. I just wonder, how it can be done that radeonfb
or other graphics drivers can be told "please only use the first 32MB" and
then either manually or automatically, map the rest as ramdisk.
> I believe there is more to it, the GART window may be smaller than the
> total RAM on the card. You need to use the GART to map in the
> appropriate pieces.
The problem here is the PCI bus on the Efika is a PCI bus, with an AGP
riser. It doesn't add any AGP functionality like real GART on the host
controller side, so there is nothing to map system memory into AGP's
view of the system.. it always confused me how "pcigart" is meant to
work and how an AGP GART does anything different to how PCI works in
the first place (the documentation/spec doesn't make it that clear in
my opinion :)
You would certainly know better than I..
--
Matt Sealey <matt@genesi-usa.com>
Genesi, Manager, Developer Relations
^ permalink raw reply
* Re: [RFC v3 PATCH 1/4] Extract list of relocation offsets
From: Mohan Kumar M @ 2008-07-22 7:58 UTC (permalink / raw)
To: Paul Mackerras; +Cc: naren, Milton Miller, ppcdev
In-Reply-To: <18565.32335.641560.254806@cargo.ozlabs.ibm.com>
Paul Mackerras wrote:
> Mohan Kumar M writes:
>
>> diff --git a/arch/powerpc/boot/relocs.c b/arch/powerpc/boot/relocs.c
>> new file mode 100644
>> index 0000000..31ca903
>
> Where did this file come from? Did you write it all yourself? If
> not, then you need to credit the original author in the patch
> description at least. Also it needs a copyright notice.
>
> There is some evidence that this has been copied from a similar
> program for x86. For instance, this:
Hi Paul,
Yes, its taken from x86 relocs.c. I will include proper credits to the
original author in the next patch.
>
> Also, this:
>
> + if (shdr[i].sh_type == SHT_REL) {
> + reltab[i] = malloc(shdr[i].sh_size);
> + if (!reltab[i]) {
> + die("malloc of %d bytes for relocs failed\n",
> + shdr[i].sh_size);
> + }
> + relp = reltab[i];
>
> is, I think, unnecessary, since as far as I know we never get SHT_REL
> sections on powerpc.
Ok, I will remove SHT_REL code in the relocs.c file.
Regards,
Mohan.
^ permalink raw reply
* Re: bug: mutex_lock() in interrupt conntext via phy_stop() in gianfar
From: Wolfram Sang @ 2008-07-22 7:54 UTC (permalink / raw)
To: Sebastian Siewior; +Cc: Nate Case, netdev, linuxppc-dev, Vitaly Bordug, Li Yang
In-Reply-To: <20080718121008.GA28871@Chamillionaire.breakpoint.cc>
[-- Attachment #1: Type: text/plain, Size: 1994 bytes --]
Hi,
On Fri, Jul 18, 2008 at 02:10:08PM +0200, Sebastian Siewior wrote:
> Commit 35b5f6b1a aka [PHYLIB: Locking fixes for PHY I/O potentially sleeping]
> changed the phydev->lock from spinlock into a mutex. Now, the following
> code path got triggered while NFS was unavailable:
[...]
> I found out that the same code path may be trigger in
> - drivers/net/ucc_geth.c
> - drivers/net/fec_mpc52xx.c
Recently, I described a (I think) similar problem:
(http://ozlabs.org/pipermail/linuxppc-dev/2008-July/059686.html)
===
Hello,
today, I was debugging a kernel crash on a board with a MPC5200B using
2.6.26-rc9. I found the following code in drivers/net/fec_mpc52xx.c:
static irqreturn_t mpc52xx_fec_interrupt(int irq, void *dev_id)
{
[...]
/* on fifo error, soft-reset fec */
if (ievent & (FEC_IEVENT_RFIFO_ERROR | FEC_IEVENT_XFIFO_ERROR)) {
if (net_ratelimit() && (ievent & FEC_IEVENT_RFIFO_ERROR))
dev_warn(&dev->dev, "FEC_IEVENT_RFIFO_ERROR\n");
if (net_ratelimit() && (ievent & FEC_IEVENT_XFIFO_ERROR))
dev_warn(&dev->dev, "FEC_IEVENT_XFIFO_ERROR\n");
mpc52xx_fec_reset(dev);
netif_wake_queue(dev);
return IRQ_HANDLED;
}
[...]
}
Calling mpc52xx_fec_reset() from interrupt context is bad, at least
because
a) it calls phy_write, which contains BUG_ON(in_interrupt())
b) it calls mpc52xx_fec_hw_init, which has a delay-loop to check
if the reset was successful (1..50 us)
I assume the proper thing to do is to set a flag in the ISR and handle
the soft reset later in some other context. Having never dealt with the
network core and its drivers so far, I am not sure which place would be
the right one to perform the soft reset. To not make things worse, I
hope people with more insight to network stuff can deliver a suitable
solution to this problem.
All the best,
Wolfram
===
--
Dipl.-Ing. Wolfram Sang | http://www.pengutronix.de
Pengutronix - Linux Solutions for Science and Industry
[-- Attachment #2: Digital signature --]
[-- Type: application/pgp-signature, Size: 189 bytes --]
^ permalink raw reply
* Re: UIO not working on ppc405 onchip registers
From: Ben Nizette @ 2008-07-22 7:52 UTC (permalink / raw)
To: super.firetwister; +Cc: linuxppc-dev@ozlabs.org, linux-embedded@vger.kernel.org
In-Reply-To: <200807220948.51053.super.firetwister@gmail.com>
On Tue, 2008-07-22 at 09:48 +0200, super.firetwister@googlemail.com
wrote:
> On Tuesday 22 July 2008, Ben Nizette wrote:
>
> > As an aside, you sure you want to do this anyway?
>
> No ;)
>
>
> > I'd suggest that you
> > just do a gpio chip driver for this, tie it in to gpiolib and use the
> > gpiolib user interface (which IIRC has only made it as far as -mm but is
> > on the way up). This gives kernel internals nice access to the pins as
> > well through the standard gpio framework.
>
> This was just an example to make it others easier to reproduce my problem. My
> goal is to have a soft spi driver in userspace, which would probably be
> slower if it uses gpiolib. This driver is integrated in the application I
> want to port to Linux.
Ah right, cool. I donno what the speed would be like, but both David
Brownell and Michael Buesch both have spi-over-gpio patches floating
around (eg [1]). That, plus the spidev interface, might at least be
worth a try..?
But I'll let you get back to solving the UIO problem at hand :-D
--Ben.
[1] http://lwn.net/Articles/290066/
^ permalink raw reply
* Re: UIO not working on ppc405 onchip registers
From: super.firetwister @ 2008-07-22 7:48 UTC (permalink / raw)
To: Ben Nizette; +Cc: linuxppc-dev@ozlabs.org, linux-embedded@vger.kernel.org
In-Reply-To: <1216708967.4004.238.camel@moss.renham>
On Tuesday 22 July 2008, Ben Nizette wrote:
> As an aside, you sure you want to do this anyway?
No ;)
> I'd suggest that you
> just do a gpio chip driver for this, tie it in to gpiolib and use the
> gpiolib user interface (which IIRC has only made it as far as -mm but is
> on the way up). This gives kernel internals nice access to the pins as
> well through the standard gpio framework.
This was just an example to make it others easier to reproduce my problem. My
goal is to have a soft spi driver in userspace, which would probably be
slower if it uses gpiolib. This driver is integrated in the application I
want to port to Linux.
Thanks
Markus
^ permalink raw reply
* Re: UIO not working on ppc405 onchip registers
From: super.firetwister @ 2008-07-22 7:47 UTC (permalink / raw)
To: Uwe Kleine-König
Cc: linuxppc-dev@ozlabs.org, linux-embedded@vger.kernel.org
In-Reply-To: <20080722061730.GB12576@digi.com>
Hello Uwe,
On Tuesday 22 July 2008, Uwe Kleine-K=F6nig wrote:
> I don't know powerpc in general nor ppc405ep in detail but IIRC arm has
> problems if some memory is mapped twice. Might this be the problem
> here?
Maybee, the emac0 (0xef600800) and emac1 (0xef600900) get mapped as well,=20
because AFAIK I have to map a whole pagesize (0x1000).
> > + int uiofd =3D open(UIO_DEV,O_RDWR);
>
> For debugging this is OK, in the final application you should add some
> tests. Check the UIO documentation for the details.
The whole example was meant to be short.
> > +static int uio_gpio_remove(struct device *dev)
> > +{
> > + uio_unregister_device(&info);
> > + info.mem[0].addr =3D 0;
> > + info.mem[0].size =3D 0;
> > + return 0;
> > +}
>
> Are you sure that overwriting info.mem[0].addr is a good idea? Then
> unbinding the platform device and rebinding it fails to do the right
> thing for sure.
This was stolen from uio_dummy. So this might become a common error :(
Thanks a lot for your comments, I will try to get an exclusive memory regio=
n=20
mapped.
Markus
^ permalink raw reply
* [git pull] Please pull from powerpc.git merge branch
From: Benjamin Herrenschmidt @ 2008-07-22 7:32 UTC (permalink / raw)
To: Linus Torvalds; +Cc: linuxppc-dev list, akpm, Linux Kernel list, Kumar Gala
Hi Linus !
Here's the second batch of powerpc updates. I should have a last one
before the end of the merge window for things that are stealing dealing
with conflicts or dependencies but we are getting there.
So please pull from:
git://git.kernel.org/pub/scm/linux/kernel/git/benh/powerpc.git merge
Hopefully this one shouldn't whack the main Makefile :-)
Note a couple of fbdev updates. One is an offb change by me, this is
a powerpc specific driver so that shouldn't affect anybody else, and
one is the ps3 specific fbdev by Geoff, so again, there should be
no collateral damage.
Some drivers/net changes to freescale specific drivers are coming from
Kumar's tree. Kumar, I merged them, however, shouldn't we have Jeff's
ack on these things or is it ok as long as those are arch specific
drivers and the changes boring ?
The cell EDAC change has been ack'ed by the EDAC maintainer.
Cheers,
Ben.
Documentation/DMA-attributes.txt | 9
Documentation/powerpc/booting-without-of.txt | 189 +
.../powerpc/dts-bindings/fsl/cpm_qe/gpio.txt | 38
.../powerpc/dts-bindings/fsl/cpm_qe/qe/usb.txt | 53
.../powerpc/dts-bindings/fsl/mcu-mpc8349emitx.txt | 17
Documentation/powerpc/dts-bindings/fsl/pmc.txt | 63
Documentation/powerpc/dts-bindings/fsl/tsec.txt | 31
.../powerpc/dts-bindings/fsl/upm-nand.txt | 28
Documentation/powerpc/dts-bindings/gpio/led.txt | 15
arch/powerpc/Kconfig | 14
arch/powerpc/boot/Makefile | 4
arch/powerpc/boot/dts/mpc7448hpc2.dts | 24
arch/powerpc/boot/dts/mpc8313erdb.dts | 241 +
arch/powerpc/boot/dts/mpc8610_hpcd.dts | 14
arch/powerpc/configs/85xx/mpc8544_ds_defconfig | 8
arch/powerpc/configs/85xx/mpc8572_ds_defconfig | 8
arch/powerpc/configs/mpc85xx_defconfig | 8
arch/powerpc/configs/mpc8610_hpcd_defconfig | 121 +
arch/powerpc/configs/mpc8641_hpcn_defconfig | 8
arch/powerpc/configs/ppc6xx_defconfig | 3304 ++++++++++++++++++++
arch/powerpc/configs/ps3_defconfig | 196 +
arch/powerpc/kernel/cputable.c | 20
arch/powerpc/kernel/head_fsl_booke.S | 187 -
arch/powerpc/kernel/iommu.c | 13
arch/powerpc/kernel/pci-common.c | 1
arch/powerpc/kernel/prom_parse.c | 44
arch/powerpc/kernel/stacktrace.c | 2
arch/powerpc/kernel/vmlinux.lds.S | 31
arch/powerpc/lib/feature-fixups.c | 2
arch/powerpc/lib/string.S | 18
arch/powerpc/platforms/52xx/Kconfig | 1
arch/powerpc/platforms/82xx/Kconfig | 1
arch/powerpc/platforms/82xx/ep8248e.c | 4
arch/powerpc/platforms/83xx/Kconfig | 3
arch/powerpc/platforms/83xx/Makefile | 2
arch/powerpc/platforms/83xx/mpc831x_rdb.c | 1
arch/powerpc/platforms/83xx/mpc832x_mds.c | 1
arch/powerpc/platforms/83xx/mpc832x_rdb.c | 1
arch/powerpc/platforms/83xx/mpc834x_itx.c | 1
arch/powerpc/platforms/83xx/mpc834x_mds.c | 1
arch/powerpc/platforms/83xx/mpc836x_mds.c | 1
arch/powerpc/platforms/83xx/mpc836x_rdk.c | 1
arch/powerpc/platforms/83xx/mpc837x_mds.c | 1
arch/powerpc/platforms/83xx/mpc837x_rdb.c | 1
arch/powerpc/platforms/83xx/mpc83xx.h | 5
arch/powerpc/platforms/83xx/pci.c | 91 -
arch/powerpc/platforms/83xx/sbc834x.c | 1
arch/powerpc/platforms/83xx/suspend-asm.S | 533 +++
arch/powerpc/platforms/83xx/suspend.c | 388 ++
arch/powerpc/platforms/83xx/usb.c | 24
arch/powerpc/platforms/85xx/Kconfig | 3
arch/powerpc/platforms/85xx/mpc85xx_ds.c | 2
arch/powerpc/platforms/86xx/Kconfig | 3
arch/powerpc/platforms/86xx/mpc8610_hpcd.c | 87 -
arch/powerpc/platforms/86xx/mpc86xx_hpcn.c | 2
arch/powerpc/platforms/Kconfig | 6
arch/powerpc/platforms/Kconfig.cputype | 5
arch/powerpc/platforms/cell/Kconfig | 25
arch/powerpc/platforms/cell/Makefile | 3
arch/powerpc/platforms/cell/cbe_powerbutton.c | 117 +
arch/powerpc/platforms/cell/cpufreq_spudemand.c | 184 +
arch/powerpc/platforms/cell/iommu.c | 118 +
arch/powerpc/platforms/cell/pervasive.c | 27
arch/powerpc/platforms/cell/pervasive.h | 9
arch/powerpc/platforms/cell/ras.c | 46
arch/powerpc/platforms/fsl_uli1575.c | 117 +
arch/powerpc/platforms/iseries/Kconfig | 1
arch/powerpc/platforms/iseries/iommu.c | 3
arch/powerpc/platforms/pasemi/iommu.c | 3
arch/powerpc/platforms/ps3/Kconfig | 1
arch/powerpc/platforms/ps3/device-init.c | 1
arch/powerpc/platforms/ps3/system-bus.c | 21
arch/powerpc/platforms/pseries/Kconfig | 1
arch/powerpc/platforms/pseries/eeh.c | 26
arch/powerpc/platforms/pseries/iommu.c | 14
arch/powerpc/sysdev/axonram.c | 28
arch/powerpc/sysdev/dart_iommu.c | 3
arch/powerpc/sysdev/fsl_pci.c | 61
arch/powerpc/sysdev/fsl_pci.h | 1
arch/powerpc/sysdev/fsl_soc.c | 90 -
arch/powerpc/sysdev/fsl_soc.h | 1
arch/powerpc/sysdev/ipic.c | 71
arch/powerpc/sysdev/qe_lib/qe.c | 6
arch/powerpc/sysdev/qe_lib/ucc.c | 6
arch/powerpc/sysdev/qe_lib/ucc_fast.c | 16
drivers/edac/cell_edac.c | 5
drivers/net/fs_enet/Makefile | 5
drivers/net/fs_enet/fs_enet-main.c | 312 --
drivers/net/fs_enet/fs_enet.h | 4
drivers/net/fs_enet/mac-fcc.c | 67
drivers/net/fs_enet/mac-fec.c | 23
drivers/net/fs_enet/mac-scc.c | 37
drivers/net/fs_enet/mii-bitbang.c | 107 -
drivers/net/fs_enet/mii-fec.c | 144 -
drivers/net/gianfar.c | 122 +
drivers/net/gianfar.h | 12
drivers/net/gianfar_ethtool.c | 41
drivers/video/offb.c | 192 +
drivers/video/ps3fb.c | 1
include/asm-powerpc/asm-compat.h | 2
include/asm-powerpc/machdep.h | 3
include/asm-powerpc/pgtable-ppc32.h | 15
include/asm-powerpc/pmi.h | 1
include/asm-powerpc/ps3.h | 7
include/asm-powerpc/reg.h | 4
include/asm-powerpc/uaccess.h | 21
include/asm-powerpc/ucc_fast.h | 8
include/linux/dma-attrs.h | 1
include/linux/fs_enet_pd.h | 4
include/linux/fsl_devices.h | 7
include/linux/of_gpio.h | 2
111 files changed, 6416 insertions(+), 1612 deletions(-)
create mode 100644 Documentation/powerpc/dts-bindings/fsl/cpm_qe/gpio.txt
create mode 100644 Documentation/powerpc/dts-bindings/fsl/mcu-mpc8349emitx.txt
create mode 100644 Documentation/powerpc/dts-bindings/fsl/pmc.txt
create mode 100644 Documentation/powerpc/dts-bindings/fsl/upm-nand.txt
create mode 100644 Documentation/powerpc/dts-bindings/gpio/led.txt
create mode 100644 arch/powerpc/configs/ppc6xx_defconfig
delete mode 100644 arch/powerpc/platforms/83xx/pci.c
create mode 100644 arch/powerpc/platforms/83xx/suspend-asm.S
create mode 100644 arch/powerpc/platforms/83xx/suspend.c
create mode 100644 arch/powerpc/platforms/cell/cbe_powerbutton.c
create mode 100644 arch/powerpc/platforms/cell/cpufreq_spudemand.c
Adrian Bunk (2):
powerpc: Remove duplicate 6xx option in Kconfig
powerpc/boot: Change spaces to tabs
Andy Fleming (2):
powerpc: Add support for multiple gfar mdio interfaces
powerpc: Fix a bunch of sparse warnings in the qe_lib
Anton Vorontsov (6):
powerpc/83xx: fix ULPI setup for MPC8315 processors
powerpc/fsl_soc: gianfar: don't probe disabled devices
powerpc: add FHCI USB, FSL MCU, FSL UPM and GPIO LEDs bindings
powerpc/fsl_uli1575: fix RTC quirk to work on MPC8572DS and MPC8610HPCD
powerpc/85xx/86xx: some refactoring for fsl_uli1575 code
powerpc/86xx: mpc8610_hpcd - add support for ULI RTC
Arnd Bergmann (1):
powerpc: Fix support for latencytop
Benjamin Herrenschmidt (2):
powerpc: Fix OF parsing of 64 bits PCI addresses
fbdev: Teaches offb about palette on radeon r5xx/r6xx
Christian Krafft (3):
powerpc/cell/cpufreq: Add spu aware cpufreq governor
powerpc/cell: Cleanup sysreset_hack for IBM cell blades
powerpc/cell: Add support for power button of future IBM cell blades
Geoff Levand (1):
powerpc/ps3: Update ps3_defconfig
Jochen Friedrich (1):
powerpc: Add documentation for CPM GPIO banks
John Rigby (2):
powerpc: pci config cleanup
powerpc: Move mpc83xx_add_bridge to fsl_pci.c
Kumar Gala (7):
fs_enet: Remove !CONFIG_PPC_CPM_NEW_BINDING code
powerpc: Remove Kconfig PPC_CPM_NEW_BINDING
powerpc: rework FSL Book-E PTE access and TLB miss
powerpc/fsl: Minor TLBSYNC cleanup for FSL Book-E
powerpc/ep8248e: Unconditionally select PHYLIB for mdio_bitbang
Revert "powerpc/ep8248e: Fix compile problem if !CONFIG_FS_ENET"
powerpc: Add a 6xx defconfig
Mark Nelson (2):
powerpc/dma: Use the struct dma_attrs in iommu code
powerpc/cell: Add DMA_ATTR_WEAK_ORDERING dma attribute and use in Cell IOMMU code
Masakazu Mokuno (1):
powerpc/ps3: Add a sub-match id to ps3_system_bus
Maxim Shchetynin (3):
powerpc/axonram: Use only one block device major number
powerpc/axonram: Enable partitioning of the Axons DDR2 DIMMs
powerpc/cell/edac: Log a syndrome code in case of correctable error
Michael Ellerman (4):
powerpc: Use WARN_ON(1) instead of __WARN()
powerpc: Add a #define for aligning to a long-sized boundary
powerpc: Use PPC_LONG_ALIGN in uaccess.h
powerpc: Use PPC_LONG and PPC_LONG_ALIGN in lib/string.S
Mike Mason (1):
powerpc/eeh: Don't panic when EEH_MAX_FAILS is exceeded
Paul Gortmaker (1):
powerpc/mpc7448: add alias list to DTS, clean out old chosen node
Scott Wood (6):
powerpc: Document Freescale power management nodes, and the sleep property.
powerpc: Update example SOC node in booting-without-of.txt.
powerpc/mpc83xx: Power Management support
powerpc/mpc8313erdb: Add power management to the device tree.
gianfar: Add magic packet and suspend/resume support.
Add fsl,magic-packet to, and clean up, the gianfar binding.
Segher Boessenkool (1):
powerpc: Fix build bug with binutils < 2.18 and GCC < 4.2
Timur Tabi (1):
powerpc: fix ALSA options in Freescale 85xx and 86xx defconfigs
Torez Smith (1):
powerpc: Indicate which oprofile counters to use while in compat mode
Wolfgang Grandegger (1):
of_gpio: Should use new <linux/gpio.h> header
^ permalink raw reply
* Please pull mpc5200-next
From: Grant Likely @ 2008-07-22 7:25 UTC (permalink / raw)
To: Benjamin Herrenschmidt, Paul Mackerras, linuxppc-dev
Hi Paul/Ben
Please pull my mpc5200 next branch for the following commits:
Thanks,
g.
The following changes since commit e89970aa93b06d0605a488e74af3b0a2a80e03b5:
Linus Torvalds (1):
Merge git://git.kernel.org/.../davem/net-2.6
are available in the git repository at:
git://git.secretlab.ca/git/linux-2.6-mpc52xx next
Grant Likely (3):
powerpc/mpc5200: Make mpc5200 GPIO driver select the GENERIC_GPIO config
powerpc/mpc5200: add PSC SICR bit definitions
of-bindings: Add binding documentation for SPI busses and devices
Jon Smirl (2):
powerpc/mpc5200: Add AC97 register definitions for the MPC52xx PSC
powerpc/mpc5200: Remove fsl-soc.c from mpc5200 build, it is not needed.
Wolfram Sang (1):
powerpc/mpc5200: Fix wrong 'no interrupt' handling in of_i2c
Documentation/powerpc/booting-without-of.txt | 57 ++++++++++++++++++++++++++
arch/powerpc/platforms/52xx/Kconfig | 2 +-
drivers/of/of_i2c.c | 2 -
include/asm-powerpc/mpc52xx_psc.h | 40 +++++++++++++++++-
4 files changed, 95 insertions(+), 6 deletions(-)
--
Grant Likely, B.Sc., P.Eng.
Secret Lab Technologies Ltd.
^ permalink raw reply
* [PATCH] powerpc/mpc5200: add PSC SICR bit definitions
From: Grant Likely @ 2008-07-22 7:14 UTC (permalink / raw)
To: linuxppc-dev
From: Grant Likely <grant.likely@secretlab.ca>
Required by the PSC I2S audio driver.
Signed-off-by: Grant Likely <grant.likely@secretlab.ca>
---
include/asm-powerpc/mpc52xx_psc.h | 32 +++++++++++++++++++++++++++++++-
1 files changed, 31 insertions(+), 1 deletions(-)
diff --git a/include/asm-powerpc/mpc52xx_psc.h b/include/asm-powerpc/mpc52xx_psc.h
index 5467c2c..8917ed6 100644
--- a/include/asm-powerpc/mpc52xx_psc.h
+++ b/include/asm-powerpc/mpc52xx_psc.h
@@ -60,10 +60,12 @@
#define MPC52xx_PSC_RXTX_FIFO_ALARM 0x0002
#define MPC52xx_PSC_RXTX_FIFO_EMPTY 0x0001
-/* PSC interrupt mask bits */
+/* PSC interrupt status/mask bits */
#define MPC52xx_PSC_IMR_TXRDY 0x0100
#define MPC52xx_PSC_IMR_RXRDY 0x0200
#define MPC52xx_PSC_IMR_DB 0x0400
+#define MPC52xx_PSC_IMR_TXEMP 0x0800
+#define MPC52xx_PSC_IMR_ORERR 0x1000
#define MPC52xx_PSC_IMR_IPC 0x8000
/* PSC input port change bit */
@@ -92,6 +94,34 @@
#define MPC52xx_PSC_RFNUM_MASK 0x01ff
+#define MPC52xx_PSC_SICR_DTS1 (1 << 29)
+#define MPC52xx_PSC_SICR_SHDR (1 << 28)
+#define MPC52xx_PSC_SICR_SIM_MASK (0xf << 24)
+#define MPC52xx_PSC_SICR_SIM_UART (0x0 << 24)
+#define MPC52xx_PSC_SICR_SIM_UART_DCD (0x8 << 24)
+#define MPC52xx_PSC_SICR_SIM_CODEC_8 (0x1 << 24)
+#define MPC52xx_PSC_SICR_SIM_CODEC_16 (0x2 << 24)
+#define MPC52xx_PSC_SICR_SIM_AC97 (0x3 << 24)
+#define MPC52xx_PSC_SICR_SIM_SIR (0x8 << 24)
+#define MPC52xx_PSC_SICR_SIM_SIR_DCD (0xc << 24)
+#define MPC52xx_PSC_SICR_SIM_MIR (0x5 << 24)
+#define MPC52xx_PSC_SICR_SIM_FIR (0x6 << 24)
+#define MPC52xx_PSC_SICR_SIM_CODEC_24 (0x7 << 24)
+#define MPC52xx_PSC_SICR_SIM_CODEC_32 (0xf << 24)
+#define MPC52xx_PSC_SICR_GENCLK (1 << 23)
+#define MPC52xx_PSC_SICR_I2S (1 << 22)
+#define MPC52xx_PSC_SICR_CLKPOL (1 << 21)
+#define MPC52xx_PSC_SICR_SYNCPOL (1 << 20)
+#define MPC52xx_PSC_SICR_CELLSLAVE (1 << 19)
+#define MPC52xx_PSC_SICR_CELL2XCLK (1 << 18)
+#define MPC52xx_PSC_SICR_ESAI (1 << 17)
+#define MPC52xx_PSC_SICR_ENAC97 (1 << 16)
+#define MPC52xx_PSC_SICR_SPI (1 << 15)
+#define MPC52xx_PSC_SICR_MSTR (1 << 14)
+#define MPC52xx_PSC_SICR_CPOL (1 << 13)
+#define MPC52xx_PSC_SICR_CPHA (1 << 12)
+#define MPC52xx_PSC_SICR_USEEOF (1 << 11)
+#define MPC52xx_PSC_SICR_DISABLEEOF (1 << 10)
/* Structure of the hardware registers */
struct mpc52xx_psc {
^ permalink raw reply related
* [PATCH v3 0/3] ALSA SoC: MPC5200 audio driver
From: Grant Likely @ 2008-07-22 6:58 UTC (permalink / raw)
To: linuxppc-dev, alsa-devel, liam.girdwood; +Cc: broonie, timur
Hi all,
Here is the latest series for adding MPC5200 I2S and TI AIC26 codec
support to ALSA SoC. I believe all the comments are addressed and I
hope that this series is now ready to be merged. I would really like
to see these ones land in 2.6.27.
Thanks,
g.
include/sound/soc-of-simple.h | 21 +
sound/soc/codecs/Kconfig | 4 +
sound/soc/codecs/Makefile | 2 +
sound/soc/codecs/tlv320aic26.c | 519 +++++++++++++++++++++++
sound/soc/codecs/tlv320aic26.h | 93 ++++
sound/soc/fsl/Kconfig | 10 +
sound/soc/fsl/Makefile | 5 +
sound/soc/fsl/mpc5200_psc_i2s.c | 884 +++++++++++++++++++++++++++++++++++++++
sound/soc/fsl/soc-of-simple.c | 171 ++++++++
9 files changed, 1709 insertions(+), 0 deletions(-)
Cheers,
g.
On Tue, Jul 22, 2008 at 12:53 AM, Grant Likely
<grant.likely@secretlab.ca> wrote:
> 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. At the moment, the driver
> is not very capable and it is expected to be extended as more features
> are needed for specifying the configuration in the device tree.
>
> This is most likely temporary glue code to work around limitations in
> the ASoC v1 framework. When v2 is merged, most of this driver will
> need to be reworked.
>
> Signed-off-by: Grant Likely <grant.likely@secretlab.ca>
> ---
>
> include/sound/soc-of-simple.h | 21 +++++
> sound/soc/fsl/Kconfig | 3 +
> sound/soc/fsl/Makefile | 3 +
> sound/soc/fsl/soc-of-simple.c | 171 +++++++++++++++++++++++++++++++++++++++++
> 4 files changed, 198 insertions(+), 0 deletions(-)
>
> diff --git a/include/sound/soc-of-simple.h b/include/sound/soc-of-simple.h
> new file mode 100644
> index 0000000..696fc51
> --- /dev/null
> +++ b/include/sound/soc-of-simple.h
> @@ -0,0 +1,21 @@
> +/*
> + * OF helpers for ALSA SoC
> + *
> + * Copyright (C) 2008, Secret Lab Technologies Ltd.
> + */
> +
> +#ifndef _INCLUDE_SOC_OF_H_
> +#define _INCLUDE_SOC_OF_H_
> +
> +#include <linux/of.h>
> +#include <sound/soc.h>
> +
> +int of_snd_soc_register_codec(struct snd_soc_codec_device *codec_dev,
> + void *codec_data, struct snd_soc_dai *dai,
> + struct device_node *node);
> +
> +int of_snd_soc_register_platform(struct snd_soc_platform *platform,
> + struct device_node *node,
> + struct snd_soc_dai *cpu_dai);
> +
> +#endif /* _INCLUDE_SOC_OF_H_ */
> diff --git a/sound/soc/fsl/Kconfig b/sound/soc/fsl/Kconfig
> index 3368ace..398f002 100644
> --- a/sound/soc/fsl/Kconfig
> +++ b/sound/soc/fsl/Kconfig
> @@ -1,3 +1,6 @@
> +config SND_SOC_OF_SIMPLE
> + tristate
> +
> config SND_SOC_MPC8610
> bool "ALSA SoC support for the MPC8610 SOC"
> depends on MPC8610_HPCD
> diff --git a/sound/soc/fsl/Makefile b/sound/soc/fsl/Makefile
> index 62f680a..aa2100b 100644
> --- a/sound/soc/fsl/Makefile
> +++ b/sound/soc/fsl/Makefile
> @@ -1,3 +1,6 @@
> +# Simple machine driver that extracts configuration from the OF device tree
> +obj-$(CONFIG_SND_SOC_OF_SIMPLE) += soc-of-simple.o
> +
> # MPC8610 HPCD Machine Support
> obj-$(CONFIG_SND_SOC_MPC8610_HPCD) += mpc8610_hpcd.o
>
> diff --git a/sound/soc/fsl/soc-of-simple.c b/sound/soc/fsl/soc-of-simple.c
> new file mode 100644
> index 0000000..0382fda
> --- /dev/null
> +++ b/sound/soc/fsl/soc-of-simple.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-simple.h>
> +#include <sound/initval.h>
> +
> +MODULE_AUTHOR("Grant Likely <grant.likely@secretlab.ca>");
> +MODULE_LICENSE("GPL");
> +MODULE_DESCRIPTION("ALSA SoC OpenFirmware bindings");
> +
> +static DEFINE_MUTEX(of_snd_soc_mutex);
> +static 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_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 = (char *)node->name;
> + of_soc->dai_link.stream_name = (char *)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_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);
>
>
--
Grant Likely, B.Sc., P.Eng.
Secret Lab Technologies Ltd.
^ permalink raw reply
* [PATCH v3 3/3] ALSA SoC: Add Texas Instruments TLV320AIC26 codec driver
From: Grant Likely @ 2008-07-22 6:54 UTC (permalink / raw)
To: linuxppc-dev, alsa-devel, liam.girdwood; +Cc: broonie, timur
In-Reply-To: <20080722065352.7306.60679.stgit@trillian.secretlab.ca>
From: Grant Likely <grant.likely@secretlab.ca>
ASoC Codec driver for the TLV320AIC26 device. As it stands, this driver
doesn't support all the modes and clocking options of the AIC16, but it
is a start.
Signed-off-by: Grant Likely <grant.likely@secretlab.ca>
---
sound/soc/codecs/Kconfig | 4
sound/soc/codecs/Makefile | 2
sound/soc/codecs/tlv320aic26.c | 519 ++++++++++++++++++++++++++++++++++++++++
sound/soc/codecs/tlv320aic26.h | 93 +++++++
4 files changed, 618 insertions(+), 0 deletions(-)
diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig
index 1db04a2..b399a64 100644
--- a/sound/soc/codecs/Kconfig
+++ b/sound/soc/codecs/Kconfig
@@ -47,6 +47,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 I2C
diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile
index d7b97ab..dc0357e 100644
--- a/sound/soc/codecs/Makefile
+++ b/sound/soc/codecs/Makefile
@@ -9,6 +9,7 @@ snd-soc-wm8990-objs := wm8990.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
@@ -22,4 +23,5 @@ obj-$(CONFIG_SND_SOC_WM8990) += snd-soc-wm8990.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..4621fda
--- /dev/null
+++ b/sound/soc/codecs/tlv320aic26.c
@@ -0,0 +1,519 @@
+/*
+ * 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-simple.h>
+#include <sound/initval.h>
+
+#include "tlv320aic26.h"
+
+MODULE_DESCRIPTION("ASoC TLV320AIC26 codec driver");
+MODULE_AUTHOR("Grant Likely <grant.likely@secretlab.ca>");
+MODULE_LICENSE("GPL");
+
+/* AIC26 driver private data */
+struct aic26 {
+ struct spi_device *spi;
+ struct snd_soc_codec codec;
+ u16 reg_cache[AIC26_NUM_REGS]; /* 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 >= 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 */
+ cache[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 >= AIC26_NUM_REGS) {
+ WARN_ON_ONCE(1);
+ return 0;
+ }
+
+ return cache[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 >= 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 */
+ cache[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);
+
+ /* 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_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_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_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
+ */
+#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)
+
+struct snd_soc_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_SINGLE("Keyclick activate", AIC26_REG_AUDIO_CTRL2, 15, 0x1, 0),
+ SOC_SINGLE("Keyclick amplitude", AIC26_REG_AUDIO_CTRL2, 12, 0x7, 0),
+ SOC_SINGLE("Keyclick frequency", AIC26_REG_AUDIO_CTRL2, 8, 0x7, 0),
+ SOC_SINGLE("Keyclick period", AIC26_REG_AUDIO_CTRL2, 4, 0xf, 0),
+ 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_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 >> 4) & 0xf));
+
+ return sprintf(buf, "amp=%x freq=%iHz len=%iclks\n", amp, freq, len);
+}
+
+/* Any write to the keyclick attribute will trigger the keyclick event */
+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(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 = AIC26_NUM_REGS;
+ 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 register cache */
+ for (i = 0; i < ARRAY_SIZE(aic26->reg_cache); i++)
+ aic26_reg_read(&aic26->codec, i);
+
+ /* Register the sysfs files for debugging */
+ /* Create SysFS files */
+ rc = device_create_file(&spi->dev, &dev_attr_keyclick);
+ if (rc)
+ dev_info(&spi->dev, "error creating sysfs files\n");
+
+#if defined(CONFIG_SND_SOC_OF_SIMPLE)
+ /* 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);
+#endif
+
+ 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);
diff --git a/sound/soc/codecs/tlv320aic26.h b/sound/soc/codecs/tlv320aic26.h
new file mode 100644
index 0000000..62b1f22
--- /dev/null
+++ b/sound/soc/codecs/tlv320aic26.h
@@ -0,0 +1,93 @@
+/*
+ * Texas Instruments TLV320AIC26 low power audio CODEC
+ * register definitions
+ *
+ * Copyright (C) 2008 Secret Lab Technologies Ltd.
+ */
+
+#ifndef _TLV320AIC16_H_
+#define _TLV320AIC16_H_
+
+/* 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)
+
+/* 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)
+
+/* 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,
+};
+
+#endif /* _TLV320AIC16_H_ */
^ permalink raw reply related
* [PATCH v3 2/3] ALSA SoC: Add mpc5200-psc I2S driver
From: Grant Likely @ 2008-07-22 6:53 UTC (permalink / raw)
To: linuxppc-dev, alsa-devel, liam.girdwood; +Cc: broonie, timur
In-Reply-To: <20080722065352.7306.60679.stgit@trillian.secretlab.ca>
From: Grant Likely <grant.likely@secretlab.ca>
This is an I2S bus driver for the MPC5200 PSC device. It depends on the
soc-of helper functions to match a PSC device with a codec based on data
in the device tree.
Signed-off-by: Grant Likely <grant.likely@secretlab.ca>
---
sound/soc/fsl/Kconfig | 7
sound/soc/fsl/Makefile | 2
sound/soc/fsl/mpc5200_psc_i2s.c | 884 +++++++++++++++++++++++++++++++++++++++
3 files changed, 893 insertions(+), 0 deletions(-)
diff --git a/sound/soc/fsl/Kconfig b/sound/soc/fsl/Kconfig
index 398f002..bba9546 100644
--- a/sound/soc/fsl/Kconfig
+++ b/sound/soc/fsl/Kconfig
@@ -17,3 +17,10 @@ config SND_SOC_MPC8610_HPCD
default y if MPC8610_HPCD
help
Say Y if you want to enable audio on the Freescale MPC8610 HPCD.
+
+config SND_SOC_MPC5200_I2S
+ tristate "Freescale MPC5200 PSC in I2S mode driver"
+ select SND_SOC_OF_SIMPLE
+ depends on SND_SOC && PPC_MPC52xx
+ help
+ Say Y here to support the MPC5200 PSCs in I2S mode.
diff --git a/sound/soc/fsl/Makefile b/sound/soc/fsl/Makefile
index aa2100b..035da4a 100644
--- a/sound/soc/fsl/Makefile
+++ b/sound/soc/fsl/Makefile
@@ -7,3 +7,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..8692329
--- /dev/null
+++ b/sound/soc/fsl/mpc5200_psc_i2s.c
@@ -0,0 +1,884 @@
+/*
+ * 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-simple.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
+ * @dai: the CPU DAI for this device
+ * @sicr: Base value used in serial interface control register; mode is ORed
+ * with this value.
+ * @playback: Playback stream context data
+ * @capture: 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_dai dai;
+ spinlock_t lock;
+ u32 sicr;
+
+ /* per-stream data */
+ struct psc_i2s_stream playback;
+ struct psc_i2s_stream capture;
+
+ /* 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 isr;
+
+ isr = in_be16(®s->mpc52xx_psc_isr);
+
+ /* Playback underrun error */
+ if (psc_i2s->playback.active && (isr & MPC52xx_PSC_IMR_TXEMP))
+ psc_i2s->stats.underrun_count++;
+
+ /* Capture overrun error */
+ if (psc_i2s->capture.active && (isr & 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;
+
+ /* 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);
+ }
+
+ /* 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)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct psc_i2s *psc_i2s = rtd->dai->cpu_dai->private_data;
+ int rc;
+
+ dev_dbg(psc_i2s->dev, "psc_i2s_startup(substream=%p)\n", substream);
+
+ if (!psc_i2s->playback.active &&
+ !psc_i2s->capture.active) {
+ /* Setup the IRQs */
+ rc = request_irq(psc_i2s->irq, &psc_i2s_status_irq, IRQF_SHARED,
+ "psc-i2s-status", psc_i2s);
+ rc |= request_irq(psc_i2s->capture.irq,
+ &psc_i2s_bcom_irq, IRQF_SHARED,
+ "psc-i2s-capture", &psc_i2s->capture);
+ rc |= request_irq(psc_i2s->playback.irq,
+ &psc_i2s_bcom_irq, IRQF_SHARED,
+ "psc-i2s-playback", &psc_i2s->playback);
+ if (rc) {
+ free_irq(psc_i2s->irq, psc_i2s);
+ free_irq(psc_i2s->capture.irq,
+ &psc_i2s->capture);
+ free_irq(psc_i2s->playback.irq,
+ &psc_i2s->playback);
+ 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 mode;
+
+ dev_dbg(psc_i2s->dev, "%s(substream=%p) p_size=%i p_bytes=%i"
+ " periods=%i buffer_size=%i buffer_bytes=%i\n",
+ __func__, substream, params_period_size(params),
+ params_period_bytes(params), params_periods(params),
+ params_buffer_size(params), params_buffer_bytes(params));
+
+ switch (params_format(params)) {
+ case SNDRV_PCM_FORMAT_S8:
+ mode = MPC52xx_PSC_SICR_SIM_CODEC_8;
+ break;
+ case SNDRV_PCM_FORMAT_S16_BE:
+ mode = MPC52xx_PSC_SICR_SIM_CODEC_16;
+ break;
+ case SNDRV_PCM_FORMAT_S24_BE:
+ mode = MPC52xx_PSC_SICR_SIM_CODEC_24;
+ break;
+ case SNDRV_PCM_FORMAT_S32_BE:
+ mode = 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, psc_i2s->sicr | mode);
+
+ snd_pcm_set_runtime_buffer(substream, &substream->dma_buffer);
+
+ return 0;
+}
+
+static int psc_i2s_hw_free(struct snd_pcm_substream *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;
+ else
+ s = &psc_i2s->playback;
+
+ 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);
+
+ /* 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.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.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.active)
+ imr |= MPC52xx_PSC_IMR_TXEMP;
+ if (psc_i2s->capture.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.active &&
+ !psc_i2s->capture.active) {
+
+ /* Disable all interrupts and reset the PSC */
+ out_be16(&psc_i2s->psc_regs->isr_imr.imr, 0);
+ out_8(&psc_i2s->psc_regs->command, 3 << 4); /* reset tx */
+ out_8(&psc_i2s->psc_regs->command, 2 << 4); /* reset rx */
+ out_8(&psc_i2s->psc_regs->command, 1 << 4); /* reset mode */
+ out_8(&psc_i2s->psc_regs->command, 4 << 4); /* reset error */
+
+ /* Release irqs */
+ free_irq(psc_i2s->irq, psc_i2s);
+ free_irq(psc_i2s->capture.irq, &psc_i2s->capture);
+ free_irq(psc_i2s->playback.irq, &psc_i2s->playback);
+ }
+}
+
+/**
+ * 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_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_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_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,
+ .periods_min = 2,
+ .periods_max = 256,
+ .buffer_bytes_max = 2 * 1024 * 1024,
+ .fifo_size = 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;
+
+ 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;
+ else
+ s = &psc_i2s->playback;
+
+ snd_soc_set_runtime_hwparams(substream, &psc_i2s_pcm_hardware);
+
+ 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;
+ else
+ s = &psc_i2s->playback;
+
+ 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;
+ else
+ s = &psc_i2s->playback;
+
+ 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_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;
+
+ if (pcm->streams[0].substream) {
+ rc = snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, pcm->dev, size,
+ &pcm->streams[0].substream->dma_buffer);
+ if (rc)
+ goto playback_alloc_err;
+ }
+
+ if (pcm->streams[1].substream) {
+ rc = snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, pcm->dev, size,
+ &pcm->streams[1].substream->dma_buffer);
+ if (rc)
+ goto capture_alloc_err;
+ }
+
+ return 0;
+
+ capture_alloc_err:
+ if (pcm->streams[0].substream)
+ snd_dma_free_pages(&pcm->streams[0].substream->dma_buffer);
+ playback_alloc_err:
+ dev_err(card->dev, "Cannot allocate buffer(s)\n");
+ return -ENOMEM;
+}
+
+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.psc_i2s = psc_i2s;
+ psc_i2s->capture.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.bcom_task =
+ bcom_psc_gen_bd_rx_init(psc_id, 10, fifo, 512);
+ psc_i2s->playback.bcom_task =
+ bcom_psc_gen_bd_tx_init(psc_id, 10, fifo);
+ if (!psc_i2s->capture.bcom_task ||
+ !psc_i2s->playback.bcom_task) {
+ dev_err(&op->dev, "Could not allocate bestcomm tasks\n");
+ iounmap(regs);
+ kfree(psc_i2s);
+ return -ENODEV;
+ }
+
+ /* Disable all interrupts and reset the PSC */
+ out_be16(&psc_i2s->psc_regs->isr_imr.imr, 0);
+ out_8(&psc_i2s->psc_regs->command, 3 << 4); /* reset transmitter */
+ out_8(&psc_i2s->psc_regs->command, 2 << 4); /* reset receiver */
+ out_8(&psc_i2s->psc_regs->command, 1 << 4); /* reset mode */
+ out_8(&psc_i2s->psc_regs->command, 4 << 4); /* reset error */
+
+ /* Configure the serial interface mode; defaulting to CODEC8 mode */
+ psc_i2s->sicr = MPC52xx_PSC_SICR_DTS1 | MPC52xx_PSC_SICR_I2S |
+ MPC52xx_PSC_SICR_CLKPOL;
+ if (of_get_property(op->node, "fsl,cellslave", NULL))
+ psc_i2s->sicr |= MPC52xx_PSC_SICR_CELLSLAVE |
+ MPC52xx_PSC_SICR_GENCLK;
+ out_be32(&psc_i2s->psc_regs->sicr,
+ psc_i2s->sicr | MPC52xx_PSC_SICR_SIM_CODEC_8);
+
+ /* Check for the codec handle. If it is not present then we
+ * are done */
+ if (!of_get_property(op->node, "codec-handle", NULL))
+ return 0;
+
+ /* Set up mode register;
+ * First write: RxRdy (FIFO Alarm) generates rx FIFO irq
+ * Second write: register Normal mode for non loopback
+ */
+ out_8(&psc_i2s->psc_regs->mode, 0);
+ out_8(&psc_i2s->psc_regs->mode, 0);
+
+ /* Set the TX and RX fifo alarm thresholds */
+ out_be16(&psc_i2s->fifo_regs->rfalarm, 0x100);
+ out_8(&psc_i2s->fifo_regs->rfcntl, 0x4);
+ out_be16(&psc_i2s->fifo_regs->tfalarm, 0x100);
+ out_8(&psc_i2s->fifo_regs->tfcntl, 0x7);
+
+ /* Lookup the IRQ numbers */
+ psc_i2s->playback.irq =
+ bcom_get_task_irq(psc_i2s->playback.bcom_task);
+ psc_i2s->capture.irq =
+ bcom_get_task_irq(psc_i2s->capture.bcom_task);
+
+ /* 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.bcom_task);
+ bcom_gen_bd_tx_release(psc_i2s->playback.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 v3 1/3] ALSA SoC: Add OpenFirmware helper for matching bus and codec drivers
From: Grant Likely @ 2008-07-22 6:53 UTC (permalink / raw)
To: linuxppc-dev, alsa-devel, liam.girdwood; +Cc: broonie, 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. At the moment, the driver
is not very capable and it is expected to be extended as more features
are needed for specifying the configuration in the device tree.
This is most likely temporary glue code to work around limitations in
the ASoC v1 framework. When v2 is merged, most of this driver will
need to be reworked.
Signed-off-by: Grant Likely <grant.likely@secretlab.ca>
---
include/sound/soc-of-simple.h | 21 +++++
sound/soc/fsl/Kconfig | 3 +
sound/soc/fsl/Makefile | 3 +
sound/soc/fsl/soc-of-simple.c | 171 +++++++++++++++++++++++++++++++++++++++++
4 files changed, 198 insertions(+), 0 deletions(-)
diff --git a/include/sound/soc-of-simple.h b/include/sound/soc-of-simple.h
new file mode 100644
index 0000000..696fc51
--- /dev/null
+++ b/include/sound/soc-of-simple.h
@@ -0,0 +1,21 @@
+/*
+ * OF helpers for ALSA SoC
+ *
+ * Copyright (C) 2008, Secret Lab Technologies Ltd.
+ */
+
+#ifndef _INCLUDE_SOC_OF_H_
+#define _INCLUDE_SOC_OF_H_
+
+#include <linux/of.h>
+#include <sound/soc.h>
+
+int of_snd_soc_register_codec(struct snd_soc_codec_device *codec_dev,
+ void *codec_data, struct snd_soc_dai *dai,
+ struct device_node *node);
+
+int of_snd_soc_register_platform(struct snd_soc_platform *platform,
+ struct device_node *node,
+ struct snd_soc_dai *cpu_dai);
+
+#endif /* _INCLUDE_SOC_OF_H_ */
diff --git a/sound/soc/fsl/Kconfig b/sound/soc/fsl/Kconfig
index 3368ace..398f002 100644
--- a/sound/soc/fsl/Kconfig
+++ b/sound/soc/fsl/Kconfig
@@ -1,3 +1,6 @@
+config SND_SOC_OF_SIMPLE
+ tristate
+
config SND_SOC_MPC8610
bool "ALSA SoC support for the MPC8610 SOC"
depends on MPC8610_HPCD
diff --git a/sound/soc/fsl/Makefile b/sound/soc/fsl/Makefile
index 62f680a..aa2100b 100644
--- a/sound/soc/fsl/Makefile
+++ b/sound/soc/fsl/Makefile
@@ -1,3 +1,6 @@
+# Simple machine driver that extracts configuration from the OF device tree
+obj-$(CONFIG_SND_SOC_OF_SIMPLE) += soc-of-simple.o
+
# MPC8610 HPCD Machine Support
obj-$(CONFIG_SND_SOC_MPC8610_HPCD) += mpc8610_hpcd.o
diff --git a/sound/soc/fsl/soc-of-simple.c b/sound/soc/fsl/soc-of-simple.c
new file mode 100644
index 0000000..0382fda
--- /dev/null
+++ b/sound/soc/fsl/soc-of-simple.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-simple.h>
+#include <sound/initval.h>
+
+MODULE_AUTHOR("Grant Likely <grant.likely@secretlab.ca>");
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("ALSA SoC OpenFirmware bindings");
+
+static DEFINE_MUTEX(of_snd_soc_mutex);
+static 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_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 = (char *)node->name;
+ of_soc->dai_link.stream_name = (char *)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_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
* Re: UIO not working on ppc405 onchip registers
From: Ben Nizette @ 2008-07-22 6:42 UTC (permalink / raw)
To: Markus Brunner; +Cc: linuxppc-dev@ozlabs.org, linux-embedded@vger.kernel.org
In-Reply-To: <20080722061730.GB12576@digi.com>
Hey Markus,
> >
> > +config UIO_GPIO
> > + tristate "Driver for PPC_4xx GPIO"
As an aside, you sure you want to do this anyway? I'd suggest that you
just do a gpio chip driver for this, tie it in to gpiolib and use the
gpiolib user interface (which IIRC has only made it as far as -mm but is
on the way up). This gives kernel internals nice access to the pins as
well through the standard gpio framework.
Thanks :-)
--Ben.
^ permalink raw reply
* Re: how to allocate 9MB of memory in kernel ?
From: Marco Stornelli @ 2008-07-22 6:43 UTC (permalink / raw)
To: Misbah khan; +Cc: linuxppc-embedded
In-Reply-To: <18582612.post@talk.nabble.com>
Misbah khan ha scritto:
> Hi all,
>
> I am getting kernel panic while trying these as suggested by you ,the
> following points will elaborate my concern :-
>
> My circular buffer defination is this :-
> /* Frame */
> typedef struct
> {
> char buffer[SIZE_FRAME];
> unsigned int count;
> }frame_S;
>
> /* Circular Buffer Structured */
> /* Mmaped area Structure */
> typedef struct
> {
>
> frame_S fluke[NO_FRAMES];
> unsigned int count_index;
> unsigned int read_index;
> unsigned int write_index;
>
> }circularbuffer_S;
>
> circularbuffer_S *buf_area=NULL;
>
> i am allocating memory using vmalloc and remaping to the SDRAM area as :-
>
> buf_area = vmalloc(sizeof(circularbuffer_S));
> if(!buf_area)
> {
> printk(KERN_ALERT"vmalloc failed \n");
> return -1;
> }
>
> buf_area = (circularbuffer_S *)ioremap(7700000,900000);
Here you are overwriting buf_area!!!!!
> if(!buf_area)
> {
> printk(KERN_ALERT"ioremap failed \n");
> return -1;
> }
>
>
> mmap Implimentation is this :-
>
> unsigned long start = vma->vm_start;
> unsigned long size = vma->vm_end - vma->vm_start; //0x900000;
> unsigned long phy_add = virt_to_phys(buf_area); //0x7700000;
> int ret = 0;
>
> /* Make the mmaped area noncacheable */
> vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
>
> /* Set the Flags to give permissions to Mmaped area */
> vma->vm_flags |=VM_RESERVED;
> vma->vm_flags |=VM_READ;
> vma->vm_flags |=VM_WRITE;
> vma->vm_flags |=VM_IO;
> //vma->vm_flags |=VM_SHARED;
> //vma->vm_flags |=VM_LOCKED;
>
> printk(KERN_DEBUG"In mmap function\n");
>
> if(remap_vmalloc_range(vma,buf_area,(phy_add >> PAGE_SHIFT)))
> {
> printk(KERN_ALERT"remap_vmalloc_range failed\n");
> goto mmap_exit;
>
> }
>
> I am getting mmap failed .....
>
> some times i am getting this error .......
>
>
> /***************************************************/
>
> insmod fluke_driver.ko
> Ioremap mapped to virtual 0x0c7900e20
> Unable to handle kernel paging request at virtual address c8200e34
> pgd = c0444000
> [c8200e34] *pgd=85c80011, *pte=00000000, *ppte=00000000
> Internal error: Oops: 807 [#1]
> Modules linked in: fluke_driver tstamp sig_router mvci_spi mvci_sf_pcd
> mvci_sci_unidir_s1 mvci_sci_diff mvci_sci_bidir_s
> 1 g_ether mvci_rtmd_s1 mvci_kwiso_s1 mvci_kw1281_s1 mvci_kh_s1 mvci_j1850
> mvci_gm_sbc mvci_diagh_s1 mvci_dcl mvci_can1 f
> pga_conf arcotg_udc adc_dac keypad(F) splc501_lcd(F) cpld
> CPU: 0
> PC is at FlukeDriverInit+0xe4/0x140 [fluke_driver]
> LR is at preempt_schedule+0x48/0x58
> pc : [<bf01c0e4>] lr : [<c0241e98>] Tainted: GF
> sp : c69b9ed0 ip : c69b9e28 fp : c69b9eec
> r10: c7862000 r9 : 00000015 r8 : 00000016
> r7 : bf01aaa0 r6 : c682a5f0 r5 : bf01ac24 r4 : 00000000
> r3 : 00900014 r2 : c7900e20 r1 : c69b8000 r0 : 00000000
> Flags: nZCv IRQs on FIQs on Mode SVC_32 Segment user
> Control: C5387F
> Table: 80444000 DAC: 00000015
> Process insmod (pid: 1089, stack limit = 0xc69b8250)
> Stack: (0xc69b9ed0 to 0xc69ba000)
> 9ec0: 00000000 c682a5c4 c682a5c4
> c682a400
> 9ee0: c69b9fa4 c69b9ef0 c005ec78 bf01c00c 00000000 00000000 00011008
> 00000000
> 9f00: 00000000 00000000 00000000 00000000 00000000 00000000 00000000
> 00000000
> 9f20: 00000000 00000000 00000000 00000000 c78642e8 c6aad200 c7863820
> c7863730
> 9f40: c7863848 00000000 0000006a 0000006a bf01aaac c00485d8 0000000a
> c69b8000
> 9f60: bf01aae8 c7863474 c7863870 00000013 00000000 c0282764 c69b9f9c
> 00008608
> 9f80: 00000003 00011018 00000080 c0020f64 c69b8000 00011008 00000000
> c69b9fa8
> 9fa0: c0020de0 c005d7d4 00008608 00000003 00011018 000025e8 00011008
> 0000002d
> 9fc0: 00008608 00000003 00011018 00000080 bec38f5e 00000000 00011008
> 00000000
> 9fe0: 00004000 bec38d14 000088dc 400db334 60000010 00011018 48010000
> 00000000
> Backtrace:
> [<bf01c000>] (FlukeDriverInit+0x0/0x140 [fluke_driver]) from [<c005ec78>]
> (sys_init_module+0x14b0/0x1698)
> r5 = C682A400 r4 = C682A5C4
> [<c005d7c8>] (sys_init_module+0x0/0x1698) from [<c0020de0>]
> (ret_fast_syscall+0x0/0x2c)
> Code: eb408e00 e5952000 e59f3058 e1a00004 (e7824003)
> Segmentation fault
>
> /****************************************************/
>
> Please suggest me what could be the problem and how to overcome this and do
> my implimentation ....
>
>
> ------Misbah <><
>
> Arnd Bergmann wrote:
>> On Friday 18 July 2008, Misbah khan wrote:
>>
>>> Now my concern is How can i map SDRAM one to one to circular buffer of
>>> such
>>> a huge size ????
>> As I mentioned, use vmalloc to get the memory, and provide an mmap
>> function
>> that uses remap_vmalloc_range to put it into the user address space.
>>
>>> My idea is that i will ioreamp SDRAM and do memcpy_toio() when ever i am
>>> writing to the the circular buffer which is dma allocated and pages are
>>> set
>>> reserved . and read the frame from user space .
>> ioremap and memcpy_toio style accesses only make sense for stuff that is
>> *not* your main memory. Memory is alrady directly accessible in the kernel
>> after allocating with get_free_pages, kmalloc or vmalloc. No need to
>> play with __iomem pointers for this.
>>
>> Arnd <><
>> _______________________________________________
>> Linuxppc-embedded mailing list
>> Linuxppc-embedded@ozlabs.org
>> https://ozlabs.org/mailman/listinfo/linuxppc-embedded
>>
>>
>
--
Marco Stornelli
Embedded Software Engineer
CoRiTeL - Consorzio di Ricerca sulle Telecomunicazioni
http://www.coritel.it
marco.stornelli@coritel.it
+39 06 72582838
^ permalink raw reply
* Re: [RFC v3 PATCH 6/4] Use LOAD_REG_IMMEDIATE macros
From: Paul Mackerras @ 2008-07-22 6:37 UTC (permalink / raw)
To: Mohan Kumar M; +Cc: naren, Milton Miller, ppcdev
In-Reply-To: <48856403.5050207@in.ibm.com>
Mohan Kumar M writes:
> All of the variables references through @got translated into relocation
> type R_PPC64_GOT16_DS entries. All these entries correspond to one of
> the above entries in the .got section. But none of the entries in .got
> section are relocated.
If that last statement is really true, then that would be an absolute
show-stopper, since you're not going to stop the compiler generating
loads from the TOC to get addresses of things.
However, I don't think it's true. I compiled up a kernel using
--emit-relocs on the final link, and with readelf -e I can see a
.rela.got section containing a bunch of R_PPC64_ADDR64 relocs for
entries in the .got section.
So the problem appears to be either just that you are ignoring
R_PPC64_ADDR64 relocs, or else that your relocs.c program has a bug
and isn't seeing the .rela.got section.
> Now I have two options left:
> 1. Check for R_PPC64_GOT16_DS entries and check whether the contents
> addressed by r2+offset is relocated or not and apply relocation if its not.
> 2. Change all LOAD_REG_ADDR macros to LOAD_REG_IMMEDIATE. This I have
> already done.
I was trying to point out that this can't possibly be a viable
solution to the problem, because most of the TOC loads in the binary
are generated by the C compiler, and only a few of them come from use
of the LOAD_REG_ADDR macro in assembly code.
Paul.
^ permalink raw reply
* Re: [RFC v3 PATCH 1/4] Extract list of relocation offsets
From: Paul Mackerras @ 2008-07-22 6:29 UTC (permalink / raw)
To: Mohan Kumar M; +Cc: naren, Milton Miller, ppcdev
In-Reply-To: <4884E0AF.2070809@in.ibm.com>
Mohan Kumar M writes:
> diff --git a/arch/powerpc/boot/relocs.c b/arch/powerpc/boot/relocs.c
> new file mode 100644
> index 0000000..31ca903
Where did this file come from? Did you write it all yourself? If
not, then you need to credit the original author in the patch
description at least. Also it needs a copyright notice.
There is some evidence that this has been copied from a similar
program for x86. For instance, this:
+/*
+ * Following symbols have been audited. There values are constant and do
+ * not change if bzImage is loaded at a different physical address than
+ * the address for which it has been compiled. Don't warn user about
+ * absolute relocations present w.r.t these symbols.
+ */
+static const char* safe_abs_relocs[] = {
+ "__kernel_vsyscall",
+ "__kernel_rt_sigreturn",
+ "__kernel_sigreturn",
+ "SYSENTER_RETURN",
+};
refers to symbols that don't exist on powerpc but (presumably) do on
x86.
Also, this:
+ if (shdr[i].sh_type == SHT_REL) {
+ reltab[i] = malloc(shdr[i].sh_size);
+ if (!reltab[i]) {
+ die("malloc of %d bytes for relocs failed\n",
+ shdr[i].sh_size);
+ }
+ relp = reltab[i];
is, I think, unnecessary, since as far as I know we never get SHT_REL
sections on powerpc.
Paul.
^ permalink raw reply
* Re: UIO not working on ppc405 onchip registers
From: Uwe Kleine-König @ 2008-07-22 6:17 UTC (permalink / raw)
To: Markus Brunner; +Cc: linuxppc-dev@ozlabs.org, linux-embedded@vger.kernel.org
In-Reply-To: <200807212152.16080.super.firetwister@gmail.com>
Hello Markus,
Markus Brunner wrote:
> I'm unable to get UIO working on the ppc405ep onchip registers (e.g. gpio/iic)
> however it's working fine on peripherals.
I don't know powerpc in general nor ppc405ep in detail but IIRC arm has
problems if some memory is mapped twice. Might this be the problem
here?
> It seems to me to be a problem with UIO on powerpc, because if I change the
> address (and nothing more) to point to a external FPGA it's working fine.
> I also tried the generic uio_pdrv which had the same problems.
> Sometimes I get a "bus error" sometimes it only produces wrong results.
> The "bus error" occurred when not a full 32 bit register was read (e.g. only a
> byte of it), but I'm not sure if it doesn't occur for other reasons as well.
Well, if this is a 32bit memory mapped device and you do a non-32 bit
access strage things can happen.
>
> diff -upNr linux-2.6.26/drivers/uio-orig/Kconfig linux-2.6.26/drivers/uio/Kconfig
> --- linux-2.6.26/drivers/uio-orig/Kconfig 2008-07-18 09:15:51.000000000 +0200
> +++ linux-2.6.26/drivers/uio/Kconfig 2008-07-18 09:16:18.000000000 +0200
> @@ -39,4 +39,12 @@ config UIO_SMX
>
> If you compile this as a module, it will be called uio_smx.
>
> +config UIO_GPIO
> + tristate "Driver for PPC_4xx GPIO"
> + depends on UIO
> + default n
> + help
> + Driver for PPC_4xx GPIO Registers
> +
> endif
This endif matches an "if UIO", so there is no need to depend on UIO
explicitly.
> diff -upNr linux-2.6.26/drivers/uio-orig/Makefile linux-2.6.26/drivers/uio/Makefile
> --- linux-2.6.26/drivers/uio-orig/Makefile 2008-07-18 09:27:18.000000000 +0200
> +++ linux-2.6.26/drivers/uio/Makefile 2008-07-18 09:16:50.000000000 +0200
> @@ -1,3 +1,4 @@
> obj-$(CONFIG_UIO) += uio.o
> obj-$(CONFIG_UIO_CIF) += uio_cif.o
> obj-$(CONFIG_UIO_SMX) += uio_smx.o
> +obj-$(CONFIG_UIO_GPIO) += uio_ppc_4xx-gpio.o
> diff -upNr linux-2.6.26/drivers/uio-orig/uio-gpio.c linux-2.6.26/drivers/uio/uio-gpio.c
> --- linux-2.6.26/drivers/uio-orig/uio-gpio.c 1970-01-01 01:00:00.000000000 +0100
> +++ linux-2.6.26/drivers/uio/uio-gpio.c 2008-07-18 09:18:56.000000000 +0200
> @@ -0,0 +1,59 @@
> +#include <sys/types.h>
> +#include <sys/time.h>
> +#include <sys/stat.h>
> +#include <sys/mman.h>
> +#include <fcntl.h>
> +#include <unistd.h>
> +#include <stdio.h>
> +#include <stdlib.h>
> +
> +const unsigned long pin_mask( unsigned int pin) { return (0x80000000 >> (pin));}
> +
> +const char UIO_DEV[] = "/dev/uio0";
> +const unsigned int UIO_SIZE = 0x1000;
> +const unsigned int UIO_ADDR = 0xef600700;
> +
> +const int or = 0;
> +const int tcr = 1;
> +
> +const unsigned int gpio_pin = 0; /* What gpio pin do you want to toggle? */
> +
> +volatile unsigned long *gpio_regs;
> +
> +int main(int argc, char *argv[])
> +{
> + int uiofd = open(UIO_DEV,O_RDWR);
For debugging this is OK, in the final application you should add some
tests. Check the UIO documentation for the details.
> + if (uiofd < 0)
> + return uiofd;
> +
> + unsigned long* map_addr = mmap(NULL,
> + UIO_SIZE,
> + PROT_READ | PROT_WRITE,
> + MAP_SHARED,
> + uiofd,
> + 0);
> + if (map_addr == ((unsigned long*) -1))
> + return -1;
> [...]
> diff -upNr linux-2.6.26/drivers/uio-orig/uio_ppc_4xx-gpio.c linux-2.6.26/drivers/uio/uio_ppc_4xx-gpio.c
> --- linux-2.6.26/drivers/uio-orig/uio_ppc_4xx-gpio.c 1970-01-01 01:00:00.000000000 +0100
> +++ linux-2.6.26/drivers/uio/uio_ppc_4xx-gpio.c 2008-07-18 09:23:32.000000000 +0200
> @@ -0,0 +1,74 @@
> +/*
> + * simple UIO GPIO driver.
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License version 2 as
> + * published by the Free Software Foundation.
> + *
> + */
> +
> +#include <linux/device.h>
> +#include <linux/module.h>
> +#include <linux/platform_device.h>
> +#include <linux/uio_driver.h>
> +
> +#include <asm/io.h>
> +
> +static struct uio_info info = {
> + .name = "uio_gpio",
> + .version = "0.0.0",
> + .irq = UIO_IRQ_NONE,
> + .irq_flags = 0,
> + .mem[0].addr = 0xef600700,
> + .mem[0].size = 0x1000,
> + .mem[0].memtype = UIO_MEM_PHYS,
> +};
IIRC you should initialise the other mem members.
> +static int __devinit uio_gpio_probe(struct device *dev)
> +{
> + if (uio_register_device(dev, &info)){
> + printk(KERN_ERR "uio_gpio: uio_register_device failed\n");
> + return -ENODEV;
> + }
> + return 0;
> +}
> +
> +static int uio_gpio_remove(struct device *dev)
> +{
> + uio_unregister_device(&info);
> + info.mem[0].addr = 0;
> + info.mem[0].size = 0;
> + return 0;
> +}
Are you sure that overwriting info.mem[0].addr is a good idea? Then
unbinding the platform device and rebinding it fails to do the right
thing for sure.
> +static struct platform_device *uio_gpio_device;
> +
> +static struct device_driver uio_gpio_driver = {
> + .name = "uio_gpio",
> + .bus = &platform_bus_type,
> + .probe = uio_gpio_probe,
> + .remove = uio_gpio_remove,
> +};
> +
> +
> +static int __init uio_gpio_init(void)
> +{
> + uio_gpio_device = platform_device_register_simple("uio_gpio", -1,
> + NULL, 0);
> + if (IS_ERR(uio_gpio_device))
> + return PTR_ERR(uio_gpio_device);
> +
> + return driver_register(&uio_gpio_driver);
> +}
> +
> +static void __exit uio_gpio_exit(void)
> +{
> + platform_device_unregister(uio_gpio_device);
> + driver_unregister(&uio_gpio_driver);
> +}
> +
> +module_init(uio_gpio_init);
> +module_exit(uio_gpio_exit);
> +
> +MODULE_LICENSE("GPL");
The header says this is GPL v2. So you should use "GPL v2" here, too.
Best regards
Uwe
--
Uwe Kleine-König, Software Engineer
Digi International GmbH Branch Breisach, Küferstrasse 8, 79206 Breisach, Germany
Tax: 315/5781/0242 / VAT: DE153662976 / Reg. Amtsgericht Dortmund HRB 13962
^ permalink raw reply
* Re: [PATCH 04/16 v3] powerpc: Split retrieval of processor entitlement data into a helper routine
From: Paul Mackerras @ 2008-07-22 5:54 UTC (permalink / raw)
To: Robert Jennings; +Cc: Brian King, linuxppc-dev, David Darrington
In-Reply-To: <20080704125207.GE1310@linux.vnet.ibm.com>
Robert Jennings writes:
> Split the retrieval of processor entitlement data returned in the H_GET_PPP
> hcall into its own helper routine.
This seems to change the value reported for pool_capacity radically:
> /* report pool_capacity in percentage */
> - seq_printf(m, "pool_capacity=%ld\n",
> - ((h_resource >> 2 * 8) & 0xffff) * 100);
> + seq_printf(m, "pool_capacity=%d\n", ppp_data.group_num * 100);
On a Power6 partition here with your patch series applied, I see
pool_capacity=3277200
in /proc/ppc64/lparcfg. Without your patches, I get
pool_capacity=400
pool_idle_time=0
pool_num_procs=0
This looks like an incompatible user-visible change to me, and we
haven't even changed the lparcfg version number at the beginning of
the /proc/ppc64/lparcfg output. Why is the pool_capacity so
different, and why do the pool_idle_time and pool_num_procs lines
disappear?
Regards,
Paul.
^ permalink raw reply
* Re: how to allocate 9MB of memory in kernel ?
From: Misbah khan @ 2008-07-22 5:23 UTC (permalink / raw)
To: linuxppc-embedded
In-Reply-To: <200807181739.10268.arnd@arndb.de>
Hi all,
I am getting kernel panic while trying these as suggested by you ,the
following points will elaborate my concern :-
My circular buffer defination is this :-
/* Frame */
typedef struct
{
char buffer[SIZE_FRAME];
unsigned int count;
}frame_S;
/* Circular Buffer Structured */
/* Mmaped area Structure */
typedef struct
{
frame_S fluke[NO_FRAMES];
unsigned int count_index;
unsigned int read_index;
unsigned int write_index;
}circularbuffer_S;
circularbuffer_S *buf_area=NULL;
i am allocating memory using vmalloc and remaping to the SDRAM area as :-
buf_area = vmalloc(sizeof(circularbuffer_S));
if(!buf_area)
{
printk(KERN_ALERT"vmalloc failed \n");
return -1;
}
buf_area = (circularbuffer_S *)ioremap(7700000,900000);
if(!buf_area)
{
printk(KERN_ALERT"ioremap failed \n");
return -1;
}
mmap Implimentation is this :-
unsigned long start = vma->vm_start;
unsigned long size = vma->vm_end - vma->vm_start; //0x900000;
unsigned long phy_add = virt_to_phys(buf_area); //0x7700000;
int ret = 0;
/* Make the mmaped area noncacheable */
vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
/* Set the Flags to give permissions to Mmaped area */
vma->vm_flags |=VM_RESERVED;
vma->vm_flags |=VM_READ;
vma->vm_flags |=VM_WRITE;
vma->vm_flags |=VM_IO;
//vma->vm_flags |=VM_SHARED;
//vma->vm_flags |=VM_LOCKED;
printk(KERN_DEBUG"In mmap function\n");
if(remap_vmalloc_range(vma,buf_area,(phy_add >> PAGE_SHIFT)))
{
printk(KERN_ALERT"remap_vmalloc_range failed\n");
goto mmap_exit;
}
I am getting mmap failed .....
some times i am getting this error .......
/***************************************************/
insmod fluke_driver.ko
Ioremap mapped to virtual 0x0c7900e20
Unable to handle kernel paging request at virtual address c8200e34
pgd = c0444000
[c8200e34] *pgd=85c80011, *pte=00000000, *ppte=00000000
Internal error: Oops: 807 [#1]
Modules linked in: fluke_driver tstamp sig_router mvci_spi mvci_sf_pcd
mvci_sci_unidir_s1 mvci_sci_diff mvci_sci_bidir_s
1 g_ether mvci_rtmd_s1 mvci_kwiso_s1 mvci_kw1281_s1 mvci_kh_s1 mvci_j1850
mvci_gm_sbc mvci_diagh_s1 mvci_dcl mvci_can1 f
pga_conf arcotg_udc adc_dac keypad(F) splc501_lcd(F) cpld
CPU: 0
PC is at FlukeDriverInit+0xe4/0x140 [fluke_driver]
LR is at preempt_schedule+0x48/0x58
pc : [<bf01c0e4>] lr : [<c0241e98>] Tainted: GF
sp : c69b9ed0 ip : c69b9e28 fp : c69b9eec
r10: c7862000 r9 : 00000015 r8 : 00000016
r7 : bf01aaa0 r6 : c682a5f0 r5 : bf01ac24 r4 : 00000000
r3 : 00900014 r2 : c7900e20 r1 : c69b8000 r0 : 00000000
Flags: nZCv IRQs on FIQs on Mode SVC_32 Segment user
Control: C5387F
Table: 80444000 DAC: 00000015
Process insmod (pid: 1089, stack limit = 0xc69b8250)
Stack: (0xc69b9ed0 to 0xc69ba000)
9ec0: 00000000 c682a5c4 c682a5c4
c682a400
9ee0: c69b9fa4 c69b9ef0 c005ec78 bf01c00c 00000000 00000000 00011008
00000000
9f00: 00000000 00000000 00000000 00000000 00000000 00000000 00000000
00000000
9f20: 00000000 00000000 00000000 00000000 c78642e8 c6aad200 c7863820
c7863730
9f40: c7863848 00000000 0000006a 0000006a bf01aaac c00485d8 0000000a
c69b8000
9f60: bf01aae8 c7863474 c7863870 00000013 00000000 c0282764 c69b9f9c
00008608
9f80: 00000003 00011018 00000080 c0020f64 c69b8000 00011008 00000000
c69b9fa8
9fa0: c0020de0 c005d7d4 00008608 00000003 00011018 000025e8 00011008
0000002d
9fc0: 00008608 00000003 00011018 00000080 bec38f5e 00000000 00011008
00000000
9fe0: 00004000 bec38d14 000088dc 400db334 60000010 00011018 48010000
00000000
Backtrace:
[<bf01c000>] (FlukeDriverInit+0x0/0x140 [fluke_driver]) from [<c005ec78>]
(sys_init_module+0x14b0/0x1698)
r5 = C682A400 r4 = C682A5C4
[<c005d7c8>] (sys_init_module+0x0/0x1698) from [<c0020de0>]
(ret_fast_syscall+0x0/0x2c)
Code: eb408e00 e5952000 e59f3058 e1a00004 (e7824003)
Segmentation fault
/****************************************************/
Please suggest me what could be the problem and how to overcome this and do
my implimentation ....
------Misbah <><
Arnd Bergmann wrote:
>
> On Friday 18 July 2008, Misbah khan wrote:
>
>> Now my concern is How can i map SDRAM one to one to circular buffer of
>> such
>> a huge size ????
>
> As I mentioned, use vmalloc to get the memory, and provide an mmap
> function
> that uses remap_vmalloc_range to put it into the user address space.
>
>> My idea is that i will ioreamp SDRAM and do memcpy_toio() when ever i am
>> writing to the the circular buffer which is dma allocated and pages are
>> set
>> reserved . and read the frame from user space .
>
> ioremap and memcpy_toio style accesses only make sense for stuff that is
> *not* your main memory. Memory is alrady directly accessible in the kernel
> after allocating with get_free_pages, kmalloc or vmalloc. No need to
> play with __iomem pointers for this.
>
> Arnd <><
> _______________________________________________
> Linuxppc-embedded mailing list
> Linuxppc-embedded@ozlabs.org
> https://ozlabs.org/mailman/listinfo/linuxppc-embedded
>
>
--
View this message in context: http://www.nabble.com/how-to-allocate-9MB-of-memory-in-kernel---tp18503022p18582612.html
Sent from the linuxppc-embedded mailing list archive at Nabble.com.
^ permalink raw reply
* Re: [PATCH 10/16 v3] [v2] powerpc: iommu enablement for CMO
From: Paul Mackerras @ 2008-07-22 5:04 UTC (permalink / raw)
To: Robert Jennings; +Cc: Brian King, David Darrington, linuxppc-dev
In-Reply-To: <20080708204813.GC20206@linux.vnet.ibm.com>
Robert Jennings writes:
> From: Robert Jennings <rcj@linux.vnet.ibm.com>
>
> Minor change to add a call to align the return from the device's
> get_desired_dma() function with IOMMU_PAGE_ALIGN(). Also removed a
> comment referring to a non-existent structure member.
>
> This is a large patch but the normal code path is not affected. For
> non-pSeries platforms the code is ifdef'ed out and for non-CMO enabled
> pSeries systems this does not affect the normal code path. Devices that
> do not perform DMA operations do not need modification with this patch.
> The function get_desired_dma was renamed from get_io_entitlement for
> clarity.
This patch is actually a new version of [PATCH 11/16 v3] powerpc: vio
bus support for CMO, not a new version of [PATCH 10/16 v3] powerpc:
iommu enablement for CMO as the subject would indicate, which tripped
me up. Anyway, my first comment is that the first paragraph of the
description ("Minor change to ...") is not appropriate for the git
tree and will have to be edited before the patch is applied. If the
extra changes are worth describing, describe them (in stand-alone
fashion) in the description; otherwise put things like this after the
line of three dashes, which terminates the description.
> +static int vio_dma_iommu_map_sg(struct device *dev, struct scatterlist *sglist,
> + int nelems, enum dma_data_direction direction)
This function, and the related unmap_sg, map_single and unmap_single
functions, now take an extra "struct dma_attrs *attrs" argument since
Mark Nelson's patch "powerpc/dma: implement new dma_*map*_attrs()
interfaces" went in (and it's now in Linus' tree). You need to roll
something like the patch below in with the 11/16 patch.
Paul.
diff --git a/arch/powerpc/kernel/vio.c b/arch/powerpc/kernel/vio.c
index ad818d1..e05baea 100644
--- a/arch/powerpc/kernel/vio.c
+++ b/arch/powerpc/kernel/vio.c
@@ -518,7 +518,8 @@ static void vio_dma_iommu_free_coherent(struct device *dev, size_t size,
static dma_addr_t vio_dma_iommu_map_single(struct device *dev, void *vaddr,
size_t size,
- enum dma_data_direction direction)
+ enum dma_data_direction direction,
+ struct dma_attrs *attrs)
{
struct vio_dev *viodev = to_vio_dev(dev);
dma_addr_t ret = DMA_ERROR_CODE;
@@ -528,7 +529,7 @@ static dma_addr_t vio_dma_iommu_map_single(struct device *dev, void *vaddr,
return ret;
}
- ret = dma_iommu_ops.map_single(dev, vaddr, size, direction);
+ ret = dma_iommu_ops.map_single(dev, vaddr, size, direction, attrs);
if (unlikely(dma_mapping_error(ret))) {
vio_cmo_dealloc(viodev, roundup(size, PAGE_SIZE));
atomic_inc(&viodev->cmo.allocs_failed);
@@ -539,17 +540,18 @@ static dma_addr_t vio_dma_iommu_map_single(struct device *dev, void *vaddr,
static void vio_dma_iommu_unmap_single(struct device *dev,
dma_addr_t dma_handle, size_t size,
- enum dma_data_direction direction)
+ enum dma_data_direction direction, struct dma_attrs *attrs)
{
struct vio_dev *viodev = to_vio_dev(dev);
- dma_iommu_ops.unmap_single(dev, dma_handle, size, direction);
+ dma_iommu_ops.unmap_single(dev, dma_handle, size, direction, attrs);
vio_cmo_dealloc(viodev, roundup(size, PAGE_SIZE));
}
static int vio_dma_iommu_map_sg(struct device *dev, struct scatterlist *sglist,
- int nelems, enum dma_data_direction direction)
+ int nelems, enum dma_data_direction direction,
+ struct dma_attrs *attrs)
{
struct vio_dev *viodev = to_vio_dev(dev);
struct scatterlist *sgl;
@@ -564,7 +566,7 @@ static int vio_dma_iommu_map_sg(struct device *dev, struct scatterlist *sglist,
return 0;
}
- ret = dma_iommu_ops.map_sg(dev, sglist, nelems, direction);
+ ret = dma_iommu_ops.map_sg(dev, sglist, nelems, direction, attrs);
if (unlikely(!ret)) {
vio_cmo_dealloc(viodev, alloc_size);
@@ -581,7 +583,7 @@ static int vio_dma_iommu_map_sg(struct device *dev, struct scatterlist *sglist,
static void vio_dma_iommu_unmap_sg(struct device *dev,
struct scatterlist *sglist, int nelems,
- enum dma_data_direction direction)
+ enum dma_data_direction direction, struct dma_attrs *attrs)
{
struct vio_dev *viodev = to_vio_dev(dev);
struct scatterlist *sgl;
@@ -591,7 +593,7 @@ static void vio_dma_iommu_unmap_sg(struct device *dev,
for (sgl = sglist; count < nelems; count++, sgl++)
alloc_size += roundup(sgl->dma_length, PAGE_SIZE);
- dma_iommu_ops.unmap_sg(dev, sglist, nelems, direction);
+ dma_iommu_ops.unmap_sg(dev, sglist, nelems, direction, attrs);
vio_cmo_dealloc(viodev, alloc_size);
}
^ permalink raw reply related
* Re: [PATCH 10/16 v3] powerpc: iommu enablement for CMO
From: Paul Mackerras @ 2008-07-22 4:57 UTC (permalink / raw)
To: Robert Jennings; +Cc: Brian King, linuxppc-dev, David Darrington
In-Reply-To: <20080704125459.GM1310@linux.vnet.ibm.com>
Robert Jennings writes:
> To support Cooperative Memory Overcommitment (CMO), we need to check
> for failure from some of the tce hcalls.
This patch runs into context mismatches because of changes made by
Michael Ellerman's patch "Fix sparse warnings in
arch/powerpc/platforms/pseries" (now in Linus' tree), which changed
code like
if (condition)
return function_returning_void(args);
into
if (condition) {
function_returning_void(args);
return;
}
which will cause problems for your patch. Please check if any of
these changes need to be undone again.
Paul.
^ permalink raw reply
* Re: [PATCH 07/16 v3] powerpc: Add collaborative memory manager
From: Paul Mackerras @ 2008-07-22 4:53 UTC (permalink / raw)
To: Robert Jennings; +Cc: Brian King, linuxppc-dev, David Darrington
In-Reply-To: <20080704125357.GJ1310@linux.vnet.ibm.com>
Robert Jennings writes:
> From: Brian King <brking@linux.vnet.ibm.com>
>
> Adds a collaborative memory manager, which acts as a simple balloon driver
> for System p machines that support cooperative memory overcommitment
> (CMO).
> +config CMM
> + tristate "Collaborative memory management"
> + depends on PPC_PSERIES
So CMM doesn't depend on LPARCFG, yet h_get_mpp is only defined if
LPARCFG=y, which makes this blow up if CMM=y and LPARCFG=n:
> +static void cmm_get_mpp(void)
> +{
> + int rc;
> + struct hvcall_mpp_data mpp_data;
> + unsigned long active_pages_target;
> + signed long page_loan_request;
> +
> + rc = h_get_mpp(&mpp_data);
Similarly, the call to h_get_mpp in vio_cmo_bus_init fails to link if
PPC_PSERIES=y and LPARCFG=n, which is a possible configuration.
Please think about and fix up the config dependencies.
Paul.
^ permalink raw reply
* Re: [RFC v3 PATCH 6/4] Use LOAD_REG_IMMEDIATE macros
From: Mohan Kumar M @ 2008-07-22 4:37 UTC (permalink / raw)
To: Paul Mackerras; +Cc: naren, Milton Miller, ppcdev
In-Reply-To: <18565.16369.993585.334427@cargo.ozlabs.ibm.com>
Paul Mackerras wrote:
> Mohan Kumar M writes:
>
> @@ -714,7 +714,7 @@ _GLOBAL(enter_rtas)
> std r6,PACASAVEDMSR(r13)
>
> /* Setup our real return addr */
> - LOAD_REG_ADDR(r4,.rtas_return_loc)
> + LOAD_REG_IMMEDIATE(r4,.rtas_return_loc)
>
> If LOAD_REG_ADDR doesn't work, then how are all the TOC loads in
> gcc-generated code going to work?
>
Hi Paul,
We have a problem only when there is a reference to a variable through got.
Following mail sent to Segher yesterday may explain it.
I was going through the output generated by objdump -D vmlinux and
readelf -h vmlinux.
<Snip objdump of vmlinux>
Disassembly of section .got:
c000000000805010 <__toc_start>:
c000000000805010: c0 00 00 00 lfs f0,0(0)
c000000000805014: 00 80 d0 10 .long 0x80d010
c000000000805018: c0 00 00 00 lfs f0,0(0)
c00000000080501c: 00 00 83 58 .long 0x8358
c000000000805020: c0 00 00 00 lfs f0,0(0)
c000000000805024: 00 85 1f e8 .long 0x851fe8
c000000000805028: c0 00 00 00 lfs f0,0(0)
c00000000080502c: 00 00 8d 84 .long 0x8d84
c000000000805030: c0 00 00 00 lfs f0,0(0)
c000000000805034: 00 85 03 38 .long 0x850338
c000000000805038: c0 00 00 00 lfs f0,0(0)
c00000000080503c: 00 85 28 b8 .long 0x8528b8
c000000000805040: c0 00 00 00 lfs f0,0(0)
c000000000805044: 00 85 28 b0 .long 0x8528b0
c000000000805048: c0 00 00 00 lfs f0,0(0)
c00000000080504c: 00 6d ef 60 .long 0x6def60
All of the variables references through @got translated into relocation
type R_PPC64_GOT16_DS entries. All these entries correspond to one of
the above entries in the .got section. But none of the entries in .got
section are relocated.
For example the instruction with relocation type R_PPC64_GOT16_DS,
c00000000000830c: e8 62 80 10 ld r3,-32752(r2)
refers to current_set variable. r2 will be pointing to
0xc00000000280d010 (relocated __toc_start + 0x8000). So the instruction
loads r3 with the content 0xc000000000851fe8 at location
0xc000000002805020, but which is not a relocated entry (0xc000000000851fe8)
But when there is a relocation type of R_PPC64_ADDR16_HI, like
c000000000008110: 64 84 00 00 oris r4,r4,0
we could easily get more info about this relocation from readelf like:
c000000000008112 000100000005 R_PPC64_ADDR16_HI c000000000000000 .text
+ 8124
So from above output we can identify that instruction at address
c000000000008112 needs to be patched with the relocation delta.
Now I have two options left:
1. Check for R_PPC64_GOT16_DS entries and check whether the contents
addressed by r2+offset is relocated or not and apply relocation if its not.
2. Change all LOAD_REG_ADDR macros to LOAD_REG_IMMEDIATE. This I have
already done.
Regards,
Mohan.
^ permalink raw reply
* Re: Mikrotik RouterBoard 333
From: David Gibson @ 2008-07-22 3:48 UTC (permalink / raw)
To: Jerry Van Baren; +Cc: Scott Wood, linuxppc-dev
In-Reply-To: <48854C02.7000307@gmail.com>
On Mon, Jul 21, 2008 at 10:54:58PM -0400, Jerry Van Baren wrote:
> Scott Wood wrote:
>> On Sun, Jul 13, 2008 at 08:44:46PM -0400, Jerry Van Baren wrote:
>>> I'm a half-ack. ;-) I'm partial to u-boot's implementation rather
>>> than using a bootwrapper for obvious reasons. The u-boot
>>> implementation takes the blob as a boot parameter and passes it
>>> along to the kernel after doing appropriate (optional) fixups.
>>
>> And if those fixups expect a malformed device tree?
>
> Oops, very bad choice of terms on my part. :-( The fixups I referred
> to are mostly "fill in the blank" things like setting the various
> clocks, MAC information, PCI information, etc. to the correct values
> based on hardware probing or a priori knowledge. U-boot does not
> (should not / will not!) fix broken device trees. A broken tree w/ the
> u-boot methodology is fixed by loading a corrected one, not requiring a
> full rebuild and reload of the firmware.
>
> Note that the blobs are (should be) made from the *.dts files that are
> part of the linux kernel source, so having correct ones has not been a
> problem. Since replacement ones are easily loaded, broken blobs are
> fixed by replacement, not by contorted fixups a'la the start of this
> thread. Since blobs are well defined, even if some boneheaded company
> didn't release their blob source, it is trivial to dump it (e.g. "fdt
> print /"), fixed, and then replaced.
>
>>> Other than that quibble, I agree that burning the blob into the
>>> firmware so that the firmware must be recompiled and reburned to
>>> change the blob is very undesirable.
>>
>> I thought the device tree was *supposed* to be an interface between the
>> firmware and the kernel? What if the firmware produces the tree
>> dynamically? What if the firmware itself depends on having the device tree
>> in order to operate?
>>
>> -Scott
>
> Well, yes and no on dynamically generated blobs. There isn't much point
> "dynamically" generating the static parts of the blob - if you have
> static code "dynamically" generating the static parts of the blob, is it
> dynamic or static? That is probably exactly where Mikrotik has
> problems. The truly dynamic parts are a small part of the blob.
Well.. they often are, particularly for embedded boards.
If there really is a large part of the tree which is dynamic, then
certainly it makes sense for the firmware to generate it and supply a
blob to the kernel. Of course in this sort of situation it would also
make sense to have a full OF firmware. Likewise, if the firmware is
sufficiently complex to want to make substantial internal use of the
device tree, it makes sense for it to pass it to the kernel. If you
have a firmware of this sort of sophistication, though, please, please
try to get the tree *right*.
A lot of firmwares however, as gvb says just take a skeleton device
tree and do nothing more than poke a few integers into specific places
into it. Maybe either leave in or remove a subtree representing an
optional peripheral.
This is the common case for embedded systems where something in the
installed software stack has to Just Know what the hardware looks
like. In this case, in theory, it's pretty much arbitrary whether the
the devtree is bundled into the piece of the stack labelled
"firmware"/"bootloader", or the piece labelled "kernel", or even (as
with u-boot) yet another "device tree" section.
In practice however, it makes sense for the tree to be bundled with
the most-easily-updated section of the stack, because that way it's
easier to correct errors in it. And usually, that means putting it
with the kernel (using the zImage wrapper). In particular this makes
sense because the kernel is the only real user of the device tree, and
this way platform specific bugs can just be fixed in the device tree,
without needing code to deal with both broken device trees from old
firmwares and fixed device trees from newer firmwares.
> If all else fails, u-boot is GPLed and the user is able to get the
> source and fix it (well, at least for 3 years after purchasing the
> hardware).
>
> There are advantages and disadvantages to u-boot and boot-wrapper
> methods. There are nothing but disadvantages to having the blob
> physically a part of the firmware (with a double whammy if the firmware
> source is not readily available).
Well, "nothing but disadvantages" isn't strictly true. In theory
boards with this arrangement which are sufficiently similar to
existing systems could run a generic kernel without any porting work.
In practice, board firmwares get things right so exceedingly rarely
that the kernel will need workarounds anyway, so it might as well just
include the device tree itself.
--
David Gibson | I'll have my music baroque, and my code
david AT gibson.dropbear.id.au | minimalist, thank you. NOT _the_ _other_
| _way_ _around_!
http://www.ozlabs.org/~dgibson
^ permalink raw reply
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox