LinuxPPC-Dev Archive on lore.kernel.org
 help / color / mirror / Atom feed
* Re: [PATCH 4/4] ste_dma40: implement support for scatterlist to scatterlist copy
From: Dan Williams @ 2010-09-29 21:19 UTC (permalink / raw)
  To: Ira W. Snyder; +Cc: Per Fridén, Linus Walleij, linuxppc-dev, linux-kernel
In-Reply-To: <1285628277-26894-5-git-send-email-iws@ovro.caltech.edu>

On Mon, Sep 27, 2010 at 3:57 PM, Ira W. Snyder <iws@ovro.caltech.edu> wrote=
:
> Now that the DMAEngine API has support for scatterlist to scatterlist
> copy, implement support for the STE DMA40 DMA controller.
>
> Cc: Linus Walleij <linus.ml.walleij@gmail.com>
> Cc: Per Frid=E9n <per.friden@stericsson.com>
> Signed-off-by: Ira W. Snyder <iws@ovro.caltech.edu>
> ---
> =A0drivers/dma/ste_dma40.c | =A0 17 +++++++++++++++++
> =A01 files changed, 17 insertions(+), 0 deletions(-)
>
> diff --git a/drivers/dma/ste_dma40.c b/drivers/dma/ste_dma40.c
> index 17e2600..cd48859 100644
> --- a/drivers/dma/ste_dma40.c
> +++ b/drivers/dma/ste_dma40.c
> @@ -1857,6 +1857,18 @@ err:
> =A0 =A0 =A0 =A0return NULL;
> =A0}
>
> +static struct dma_async_tx_descriptor *
> +d40_prep_sg(struct dma_chan *chan,
> + =A0 =A0 =A0 =A0 =A0 struct scatterlist *dst_sg, unsigned int dst_nents,
> + =A0 =A0 =A0 =A0 =A0 struct scatterlist *src_sg, unsigned int src_nents,
> + =A0 =A0 =A0 =A0 =A0 unsigned long dma_flags)
> +{
> + =A0 =A0 =A0 if (dst_nents !=3D src_nents)
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 return -EINVAL;

I suspect you wanted "return NULL;" here.  I can fix that up.

Linus, Per ack?

--
Dan

^ permalink raw reply

* Re: Parsing a bus fault message?
From: david.hagood @ 2010-09-29 21:24 UTC (permalink / raw)
  To: tiejun.chen; +Cc: Scott Wood, david.hagood, linuxppc-dev, Ira W. Snyder
In-Reply-To: <4CA2FDA3.20704@windriver.com>

> Scott Wood wrote:
> I also meet machine check exception if configure LAW improperly for PCI.
> (i.e.
> unmatched PCIe controller id.)
>
> From you log looks 0xexxxxxxx should be your PCI space. So you can check
> if that
>  fall into appropriate LAW configuration. Maybe you can post your boot log
> and
> error log here.

Using EP8641A machine description
Total memory = 1024MB; using 2048kB for hash table (at cfa00000)
Linux version 2.6.26.2-ep1.10 (SRWhite@WIC-102333) (gcc version 4.0.0
(DENX ELDK 4.1 4.0.0)) #269 SMP PREEMPT Tue Sep 28 15:48:43 CDT 2010
Found initrd at 0xcfdfa000:0xcffa9663
Found legacy serial port 0 for /soc8641@e0000000/serial@4500
  mem=e0004500, taddr=e0004500, irq=0, clk=500000000, speed=0
Found legacy serial port 1 for /soc8641@e0000000/serial@4600
  mem=e0004600, taddr=e0004600, irq=0, clk=500000000, speed=0
CPU maps initialized for 1 thread per core
 (thread shift is 0)
console [udbg0] enabled
Entering add_active_range(0, 0, 262144) 0 entries of 256 used
EP8641A board from Embedded Planet
Top of RAM: 0x40000000, Total RAM: 0x40000000
Memory hole size: 0MB
Zone PFN ranges:
  DMA             0 ->   196608
  Normal     196608 ->   196608
  HighMem    196608 ->   262144
Movable zone start PFN for each node
early_node_map[1] active PFN ranges
    0:        0 ->   262144
On node 0 totalpages: 262144
  DMA zone: 1536 pages used for memmap
  DMA zone: 0 pages reserved
  DMA zone: 195072 pages, LIFO batch:31
  Normal zone: 0 pages used for memmap
  HighMem zone: 512 pages used for memmap
  HighMem zone: 65024 pages, LIFO batch:15
  Movable zone: 0 pages used for memmap
Built 1 zonelists in Zone order, mobility grouping on.  Total pages: 260096
Kernel command line: root=/dev/ram rw console=ttyS0,115200
ip=10.200.120.158:::255.255.0.0::eth0
mtdparts=physmap-flash.0:0x1300000(linux)ro,0x6bc0000(jffs),-(rsvd)ro
mpic: Setting up MPIC " MPIC     " version 1.2 at e0040000, max 2 CPUs
mpic: ISU size: 16, shift: 4, mask: f
mpic: Initializing for 80 sources
PID hash table entries: 4096 (order: 12, 16384 bytes)
time_init: decrementer frequency = 125.000000 MHz
time_init: processor frequency   = 1500.000000 MHz
clocksource: timebase mult[2000000] shift[22] registered
clockevent: decrementer mult[2000] shift[16] cpu[0]
Dentry cache hash table entries: 131072 (order: 7, 524288 bytes)
Inode-cache hash table entries: 65536 (order: 6, 262144 bytes)
High memory: 262144k
Memory: 1031248k/1048576k available (3064k kernel code, 16476k reserved,
84k data, 149k bss, 164k init)
Calibrating delay loop... 249.85 BogoMIPS (lpj=499712)
Mount-cache hash table entries: 512
mpic: requesting IPIs ...
Processor 1 found.
Synchronizing timebase
Got ack
score 299, offset 1000
score 299, offset 500
score 299, offset 250
score 299, offset 125
score 299, offset 62
score 297, offset 31
score -299, offset 15
score 297, offset 23
score 253, offset 19
score -299, offset 17
score -269, offset 18
Min 18 (score -269), Max 19 (score 253)
Final offset: 19 (269/300)
clockevent: decrementer mult[2000] shift[16] cpu[1]
Brought up 2 CPUs
net_namespace: 208 bytes
NET: Registered protocol family 16
NET: Registered protocol family 2
IP route cache hash table entries: 32768 (order: 5, 131072 bytes)
TCP established hash table entries: 131072 (order: 8, 1048576 bytes)
TCP bind hash table entries: 65536 (order: 7, 786432 bytes)
TCP: Hash tables configured (established 131072 bind 65536)
TCP reno registered
NET: Registered protocol family 1
checking if image is initramfs...it isn't (no cpio magic); looks like an
initrd
Freeing initrd memory: 1725k freed
setup_kcore: restrict size=3fffffff
of-fsl-dma e0021300.dma: Probe the Freescale DMA driver for
fsl,eloplus-dma controller at e0021300...
of-fsl-dma-channel e0021100.dma-channe: #0 (fsl,eloplus-dma-channel), irq 20
of-fsl-dma-channel e0021180.dma-channe: #1 (fsl,eloplus-dma-channel), irq 21
of-fsl-dma-channel e0021200.dma-channe: #2 (fsl,eloplus-dma-channel), irq 22
of-fsl-dma-channel e0021280.dma-channe: #3 (fsl,eloplus-dma-channel), irq 23
Setting up RapidIO peer-to-peer network /rapidio@e00c0000
fsl-of-rio e00c0000.rapidio: Of-device full name /rapidio@e00c0000
fsl-of-rio e00c0000.rapidio: Regs start 0xe00c0000 size 0x20000
fsl-of-rio e00c0000.rapidio: LAW start 0x00000000c0000000, size
0x0000000020000000.
fsl-of-rio e00c0000.rapidio: bellirq: 50, txirq: 53, rxirq 54
fsl-of-rio e00c0000.rapidio: RapidIO PHY type: serial
fsl-of-rio e00c0000.rapidio: Hardware port width: 4
fsl-of-rio e00c0000.rapidio: Training connection status: Four-lane
fsl-of-rio e00c0000.rapidio: RapidIO Common Transport System size: 256
highmem bounce pool size: 64 pages
Installing knfsd (copyright (C) 1996 okir@monad.swb.de).
JFFS2 version 2.2. (NAND) © 2001-2006 Red Hat, Inc.
msgmni has been set to 1507
io scheduler noop registered (default)
Generic RTC Driver v1.07
Serial: 8250/16550 driver $Revision: 1.90 $ 2 ports, IRQ sharing disabled
serial8250.0: ttyS0 at MMIO 0xe0004500 (irq = 42) is a 16550A
console handover: boot [udbg0] -> real [ttyS0]
serial8250.0: ttyS1 at MMIO 0xe0004600 (irq = 28) is a 16550A
brd: module loaded
loop: module loaded
Gianfar MII Bus: probed
eth0: Gianfar Ethernet Controller Version 1.2, 00:10:ec:01:1a:d3
eth0: Running with NAPI enabled
eth0: 256/256 RX/TX BD ring size
eth1: Gianfar Ethernet Controller Version 1.2, 00:10:ec:81:1a:d3
eth1: Running with NAPI enabled
eth1: 256/256 RX/TX BD ring size
eth2: Gianfar Ethernet Controller Version 1.2, 00:10:ec:41:1a:d3
eth2: Running with NAPI enabled
eth2: 256/256 RX/TX BD ring size
eth3: Gianfar Ethernet Controller Version 1.2, 00:10:ec:c1:1a:d3
eth3: Running with NAPI enabled
eth3: 256/256 RX/TX BD ring size
physmap platform flash device: 08000000 at f8000000
physmap-flash.0: Found 2 x16 devices at 0x0 in 32-bit bank
 Amd/Fujitsu Extended Query Table at 0x0040
physmap-flash.0: CFI does not contain boot bank location. Assuming top.
number of CFI chips: 1
cfi_cmdset_0002: Disabling erase-suspend-program due to code brokenness.
3 cmdlinepart partitions found on MTD device physmap-flash.0
Creating 3 MTD partitions on "physmap-flash.0":
0x00000000-0x01300000 : "linux"
0x01300000-0x07ec0000 : "jffs"
0x07ec0000-0x08000000 : "rsvd"
TCP cubic registered
NET: Registered protocol family 17
RPC: Registered udp transport module.
RPC: Registered tcp transport module.
IP-Config: Complete:
     device=eth0, addr=10.200.120.158, mask=255.255.0.0, gw=255.255.255.255,
     host=10.200.120.158, domain=, nis-domain=(none),
     bootserver=255.255.255.255, rootserver=255.255.255.255, rootpath=
RAMDISK: Compressed image found at block 0
VFS: Mounted root (ext2 filesystem).
Freeing unused kernel memory: 164k init
PHY: e0024520:10 - Link is Up - 100/Full
RIO: discover master port 0, RIO0 mport
rionetlink_init: receive handler registration suceeded!!!!
rionetlink_init: rio_register_driver suceeded!!!!

Besides, in my setup, there are 2 LAWS programmed to point at the PEX: one
mapping A0000000 to BFFFFFFF to the PEX, and one mapping  E2000000 to
E2FFFFFF. My code directly scans the LAWS and picks the first one it sees
mapped to the PEX, so it is picking A00000000, and using that. Is there an
issue with having 2 LAWs mapping the same device to different locations?


Being unfortunately a man of many hats, I haven't had a lot of time today
to work on this particular fire. Hopefully in the next couple of days I
can get some more time to look into it more.

I do thank all of you for the pointers, and I'll look into them.

(BTW: Anybody near Wichita, and want to earn some extra $$$ helping me out?)

^ permalink raw reply

* Re: [PATCH 3/4] fsldma: remove DMA_SLAVE support
From: Dan Williams @ 2010-09-29 21:52 UTC (permalink / raw)
  To: Ira W. Snyder; +Cc: linuxppc-dev, linux-kernel
In-Reply-To: <1285628277-26894-4-git-send-email-iws@ovro.caltech.edu>

On Mon, Sep 27, 2010 at 3:57 PM, Ira W. Snyder <iws@ovro.caltech.edu> wrote=
:
> Now that the generic DMAEngine API has support for scatterlist to
> scatterlist copying, this implementation of the DMA_SLAVE API is no
> longer necessary.
>
> In order to let device_control() continue to function, a stub
> device_prep_slave_sg() function is provided. This allows custom device
> configuration, such as enabling external control.
>

> + =A0 =A0 =A0 case DMA_SLAVE_CONFIG:
> +
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 cfg =3D (struct fsldma_slave_config *)arg;

Now that I actually see someone trying to use the recommended
extension model it comes across as unsafe, what guarantees that arg is
pointing to a fsldma_slave_config.  At at minimum you could ensure
that this channel has been claimed for private usage which loosely
implies that the client knows that it is talking to an fsldma channel.
 Even safer is to just assign you a one-off dma_ctrl_cmd
(FSLDMA_EXTERNAL_START) for this purpose.  Otherwise this and the
other patches look good.

--
Dan

^ permalink raw reply

* Re: [PATCH 3/4] fsldma: remove DMA_SLAVE support
From: Ira W. Snyder @ 2010-09-29 22:19 UTC (permalink / raw)
  To: Dan Williams; +Cc: linuxppc-dev, linux-kernel
In-Reply-To: <AANLkTik4_se70GkKb2FLwsiUtQGyaHgxsLn3MQhmv5jh@mail.gmail.com>

On Wed, Sep 29, 2010 at 02:52:16PM -0700, Dan Williams wrote:
> On Mon, Sep 27, 2010 at 3:57 PM, Ira W. Snyder <iws@ovro.caltech.edu> wrote:
> > Now that the generic DMAEngine API has support for scatterlist to
> > scatterlist copying, this implementation of the DMA_SLAVE API is no
> > longer necessary.
> >
> > In order to let device_control() continue to function, a stub
> > device_prep_slave_sg() function is provided. This allows custom device
> > configuration, such as enabling external control.
> >
> 
> > +       case DMA_SLAVE_CONFIG:
> > +
> > +               cfg = (struct fsldma_slave_config *)arg;
> 
> Now that I actually see someone trying to use the recommended
> extension model it comes across as unsafe, what guarantees that arg is
> pointing to a fsldma_slave_config.  At at minimum you could ensure
> that this channel has been claimed for private usage which loosely
> implies that the client knows that it is talking to an fsldma channel.
>  Even safer is to just assign you a one-off dma_ctrl_cmd
> (FSLDMA_EXTERNAL_START) for this purpose.  Otherwise this and the
> other patches look good.
> 

I agree, it is a very unsafe model.

I'll take your suggestion, and do that instead. A new patch will be
forthcoming shortly.

Thanks,
Ira

^ permalink raw reply

* Re: [PATCH v6 0/8] ptp: IEEE 1588 hardware clock support
From: Christian Riesch @ 2010-09-30  3:50 UTC (permalink / raw)
  To: Christoph Lameter
  Cc: Rodolfo Giometti, Arnd Bergmann, Peter Zijlstra, john stultz,
	devicetree-discuss, linux-kernel, David Miller, netdev,
	linux-arm-kernel, linux-api, Thomas Gleixner, linuxppc-dev,
	Richard Cochran, Alan Cox, Krzysztof Halasa
In-Reply-To: <alpine.DEB.2.00.1009271035110.9258@router.home>

Quoting Christoph Lameter <cl@linux.com>:
> On Thu, 23 Sep 2010, Christian Riesch wrote:
>
>> > > It implies clock tuning in userspace for a potential sub microsecond
>> > > accurate clock. The clock accuracy will be limited by user space
>> > > latencies and noise. You wont be able to discipline the system clock
>> > > accurately.
>> >
>> > Noise matters, latency doesn't.
>>
>> Well put! That's why we need hardware support for PTP timestamping to reduce
>> the noise, but get along well with the clock servo that is steering  
>> the PHC in
>> user space.
>
> Even if I buy into the catch phrase above: User space is subject to noise
> that the in kernel code is not. If you do the tuning over long intervals
> then it hopefully averages out but it still causes jitter effects that
> affects the degree of accuracy (or sync) that you can reach. And the noise
> varies with the load on the system.

Yes and no. If you regard it as a control system: The latencies of the  
operating system are a dead time in the control system. The sampling  
time is quite large, one second, maybe around 100ms or 10ms in  
telecommunication applications, but that is still large compared to  
the latencies you expect to have in the operating system. Hence, this  
latencies (=dead time) can be neglected and the important thing that  
remains is the noise that you introduce in the measurements of the  
time stamps, which is therefore done in hardware.
I admit that my short statement above is not completely correct, I  
should have mentioned the rather large sampling time we are dealing  
with here.

Christian

^ permalink raw reply

* Re: [PATCH 4/4] ste_dma40: implement support for scatterlist to scatterlist copy
From: Per Friden @ 2010-09-30  7:13 UTC (permalink / raw)
  To: Dan Williams
  Cc: Per FORLIN, Linus Walleij, linuxppc-dev@lists.ozlabs.org,
	linux-kernel@vger.kernel.org, Ira W. Snyder
In-Reply-To: <AANLkTinYG4LqtEXxM5pdA0vavahtqbt6=yV341pnFmZw@mail.gmail.com>

On 09/29/2010 11:19 PM, Dan Williams wrote:
> On Mon, Sep 27, 2010 at 3:57 PM, Ira W. Snyder <iws@ovro.caltech.edu> wrote:
>> Now that the DMAEngine API has support for scatterlist to scatterlist
>> copy, implement support for the STE DMA40 DMA controller.
>>
>> Cc: Linus Walleij <linus.ml.walleij@gmail.com>
>> Cc: Per Fridén <per.friden@stericsson.com>
>> Signed-off-by: Ira W. Snyder <iws@ovro.caltech.edu>
>> ---
>>  drivers/dma/ste_dma40.c |   17 +++++++++++++++++
>>  1 files changed, 17 insertions(+), 0 deletions(-)
>>
>> diff --git a/drivers/dma/ste_dma40.c b/drivers/dma/ste_dma40.c
>> index 17e2600..cd48859 100644
>> --- a/drivers/dma/ste_dma40.c
>> +++ b/drivers/dma/ste_dma40.c
>> @@ -1857,6 +1857,18 @@ err:
>>        return NULL;
>>  }
>>
>> +static struct dma_async_tx_descriptor *
>> +d40_prep_sg(struct dma_chan *chan,
>> +           struct scatterlist *dst_sg, unsigned int dst_nents,
>> +           struct scatterlist *src_sg, unsigned int src_nents,
>> +           unsigned long dma_flags)
>> +{
>> +       if (dst_nents != src_nents)
>> +               return -EINVAL;
> 
> I suspect you wanted "return NULL;" here.  I can fix that up.
> 
> Linus, Per ack?
> 
> --
> Dan
Thanks Dan. Acked by Per.

/Per

^ permalink raw reply

* [PATCH v3 0/7] refactor spi_mpc8xxx.c and add eSPI controller support
From: Mingkai Hu @ 2010-09-30  8:00 UTC (permalink / raw)
  To: linuxppc-dev, spi-devel-general, linux-mtd; +Cc: kumar.gala

This patchset refactor the file spi_mpc8xxx.c to abstract some common 
code as a lib used by the SPI/eSPI controller driver, move the SPI 
controller driver code to spi_fsl_spi.c, and add the eSPI controller 
support with spi_fsl_espi.c.

Tested on P4080DS and MPC8536DS board based on latest Linux tree.

v3 main changes:
 - Update to the latest kernel base.
 - Change the file spi_mpc8xxx.c to spi_fsl_fsl.c verbatim.
 - Add the SPI flash partition code to the m25p80 probe function.
 - Add a quirks for SPI master to handle the contrains of the controller.

[PATCH v3 1/7] spi/mpc8xxx: rename spi_mpc8xxx.c to spi_fsl_spi.c
[PATCH v3 2/7] spi/mpc8xxx: refactor the common code for SPI/eSPI controller
[PATCH v3 3/7] eSPI: add eSPI controller support
[PATCH v3 4/7] powerpc/of: add eSPI controller dts bindings and DTS modification
[PATCH v3 5/7] mtd: m25p80: add support to parse the SPI flash's partitions
[PATCH v3 6/7] mtd: m25p80: add a read function to read page by page
[PATCH v3 7/7] DTS: add fsl,spi-quirk-trans-len-limit property

Thanks,
Mingkai

^ permalink raw reply

* [PATCH v3 1/7] spi/mpc8xxx: rename spi_mpc8xxx.c to spi_fsl_spi.c
From: Mingkai Hu @ 2010-09-30  8:00 UTC (permalink / raw)
  To: linuxppc-dev, spi-devel-general, linux-mtd; +Cc: kumar.gala, Mingkai Hu
In-Reply-To: <1285833646-12006-1-git-send-email-Mingkai.hu@freescale.com>

This will pave the way to refactor out the common code which can be used
by the eSPI controller driver, and rename the SPI controller dirver to the
file spi_fsl_spi.c.

Signed-off-by: Mingkai Hu <Mingkai.hu@freescale.com>
---
v3:
 - Update to the latest Kernel base.
 - Rename from spi_mpc8xxx.c to spi_fsl_fsl.c verbatim.

 drivers/spi/Kconfig       |    9 +-
 drivers/spi/Makefile      |    2 +-
 drivers/spi/spi_fsl_spi.c | 1419 +++++++++++++++++++++++++++++++++++++++++++++
 drivers/spi/spi_mpc8xxx.c | 1419 ---------------------------------------------
 4 files changed, 1425 insertions(+), 1424 deletions(-)
 create mode 100644 drivers/spi/spi_fsl_spi.c
 delete mode 100644 drivers/spi/spi_mpc8xxx.c

diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig
index 91c2f4f..6af34c6 100644
--- a/drivers/spi/Kconfig
+++ b/drivers/spi/Kconfig
@@ -182,12 +182,13 @@ config SPI_MPC512x_PSC
 	  This enables using the Freescale MPC5121 Programmable Serial
 	  Controller in SPI master mode.
 
-config SPI_MPC8xxx
-	tristate "Freescale MPC8xxx SPI controller"
+config SPI_FSL_SPI
+	tristate "Freescale SPI controller"
 	depends on FSL_SOC
 	help
-	  This enables using the Freescale MPC8xxx SPI controllers in master
-	  mode.
+	  This enables using the Freescale SPI controllers in master mode.
+	  MPC83xx platform uses the controller in cpu mode or CPM/QE mode.
+	  MPC8569 uses the controller in QE mode, MPC8610 in cpu mode.
 
 config SPI_OMAP_UWIRE
 	tristate "OMAP1 MicroWire"
diff --git a/drivers/spi/Makefile b/drivers/spi/Makefile
index e9cbd18..770817c 100644
--- a/drivers/spi/Makefile
+++ b/drivers/spi/Makefile
@@ -34,7 +34,7 @@ obj-$(CONFIG_SPI_PL022)			+= amba-pl022.o
 obj-$(CONFIG_SPI_MPC512x_PSC)		+= mpc512x_psc_spi.o
 obj-$(CONFIG_SPI_MPC52xx_PSC)		+= mpc52xx_psc_spi.o
 obj-$(CONFIG_SPI_MPC52xx)		+= mpc52xx_spi.o
-obj-$(CONFIG_SPI_MPC8xxx)		+= spi_mpc8xxx.o
+obj-$(CONFIG_SPI_FSL_SPI)		+= spi_fsl_spi.o
 obj-$(CONFIG_SPI_PPC4xx)		+= spi_ppc4xx.o
 obj-$(CONFIG_SPI_S3C24XX_GPIO)		+= spi_s3c24xx_gpio.o
 obj-$(CONFIG_SPI_S3C24XX)		+= spi_s3c24xx_hw.o
diff --git a/drivers/spi/spi_fsl_spi.c b/drivers/spi/spi_fsl_spi.c
new file mode 100644
index 0000000..d31b57f
--- /dev/null
+++ b/drivers/spi/spi_fsl_spi.c
@@ -0,0 +1,1419 @@
+/*
+ * MPC8xxx SPI controller driver.
+ *
+ * Maintainer: Kumar Gala
+ *
+ * Copyright (C) 2006 Polycom, Inc.
+ *
+ * CPM SPI and QE buffer descriptors mode support:
+ * Copyright (c) 2009  MontaVista Software, Inc.
+ * Author: Anton Vorontsov <avorontsov@ru.mvista.com>
+ *
+ * This program is free software; you can redistribute  it and/or modify it
+ * under  the terms of  the GNU General  Public License as published by the
+ * Free Software Foundation;  either version 2 of the  License, or (at your
+ * option) any later version.
+ */
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/bug.h>
+#include <linux/errno.h>
+#include <linux/err.h>
+#include <linux/io.h>
+#include <linux/completion.h>
+#include <linux/interrupt.h>
+#include <linux/delay.h>
+#include <linux/irq.h>
+#include <linux/device.h>
+#include <linux/spi/spi.h>
+#include <linux/spi/spi_bitbang.h>
+#include <linux/platform_device.h>
+#include <linux/fsl_devices.h>
+#include <linux/dma-mapping.h>
+#include <linux/mm.h>
+#include <linux/mutex.h>
+#include <linux/of.h>
+#include <linux/of_platform.h>
+#include <linux/gpio.h>
+#include <linux/of_gpio.h>
+#include <linux/slab.h>
+
+#include <sysdev/fsl_soc.h>
+#include <asm/cpm.h>
+#include <asm/qe.h>
+#include <asm/irq.h>
+
+/* CPM1 and CPM2 are mutually exclusive. */
+#ifdef CONFIG_CPM1
+#include <asm/cpm1.h>
+#define CPM_SPI_CMD mk_cr_cmd(CPM_CR_CH_SPI, 0)
+#else
+#include <asm/cpm2.h>
+#define CPM_SPI_CMD mk_cr_cmd(CPM_CR_SPI_PAGE, CPM_CR_SPI_SBLOCK, 0, 0)
+#endif
+
+/* SPI Controller registers */
+struct mpc8xxx_spi_reg {
+	u8 res1[0x20];
+	__be32 mode;
+	__be32 event;
+	__be32 mask;
+	__be32 command;
+	__be32 transmit;
+	__be32 receive;
+};
+
+/* SPI Controller mode register definitions */
+#define	SPMODE_LOOP		(1 << 30)
+#define	SPMODE_CI_INACTIVEHIGH	(1 << 29)
+#define	SPMODE_CP_BEGIN_EDGECLK	(1 << 28)
+#define	SPMODE_DIV16		(1 << 27)
+#define	SPMODE_REV		(1 << 26)
+#define	SPMODE_MS		(1 << 25)
+#define	SPMODE_ENABLE		(1 << 24)
+#define	SPMODE_LEN(x)		((x) << 20)
+#define	SPMODE_PM(x)		((x) << 16)
+#define	SPMODE_OP		(1 << 14)
+#define	SPMODE_CG(x)		((x) << 7)
+
+/*
+ * Default for SPI Mode:
+ * 	SPI MODE 0 (inactive low, phase middle, MSB, 8-bit length, slow clk
+ */
+#define	SPMODE_INIT_VAL (SPMODE_CI_INACTIVEHIGH | SPMODE_DIV16 | SPMODE_REV | \
+			 SPMODE_MS | SPMODE_LEN(7) | SPMODE_PM(0xf))
+
+/* SPIE register values */
+#define	SPIE_NE		0x00000200	/* Not empty */
+#define	SPIE_NF		0x00000100	/* Not full */
+
+/* SPIM register values */
+#define	SPIM_NE		0x00000200	/* Not empty */
+#define	SPIM_NF		0x00000100	/* Not full */
+
+#define	SPIE_TXB	0x00000200	/* Last char is written to tx fifo */
+#define	SPIE_RXB	0x00000100	/* Last char is written to rx buf */
+
+/* SPCOM register values */
+#define	SPCOM_STR	(1 << 23)	/* Start transmit */
+
+#define	SPI_PRAM_SIZE	0x100
+#define	SPI_MRBLR	((unsigned int)PAGE_SIZE)
+
+/* SPI Controller driver's private data. */
+struct mpc8xxx_spi {
+	struct device *dev;
+	struct mpc8xxx_spi_reg __iomem *base;
+
+	/* rx & tx bufs from the spi_transfer */
+	const void *tx;
+	void *rx;
+
+	int subblock;
+	struct spi_pram __iomem *pram;
+	struct cpm_buf_desc __iomem *tx_bd;
+	struct cpm_buf_desc __iomem *rx_bd;
+
+	struct spi_transfer *xfer_in_progress;
+
+	/* dma addresses for CPM transfers */
+	dma_addr_t tx_dma;
+	dma_addr_t rx_dma;
+	bool map_tx_dma;
+	bool map_rx_dma;
+
+	dma_addr_t dma_dummy_tx;
+	dma_addr_t dma_dummy_rx;
+
+	/* functions to deal with different sized buffers */
+	void (*get_rx) (u32 rx_data, struct mpc8xxx_spi *);
+	u32(*get_tx) (struct mpc8xxx_spi *);
+
+	unsigned int count;
+	unsigned int irq;
+
+	unsigned nsecs;		/* (clock cycle time)/2 */
+
+	u32 spibrg;		/* SPIBRG input clock */
+	u32 rx_shift;		/* RX data reg shift when in qe mode */
+	u32 tx_shift;		/* TX data reg shift when in qe mode */
+
+	unsigned int flags;
+
+	struct workqueue_struct *workqueue;
+	struct work_struct work;
+
+	struct list_head queue;
+	spinlock_t lock;
+
+	struct completion done;
+};
+
+static void *mpc8xxx_dummy_rx;
+static DEFINE_MUTEX(mpc8xxx_dummy_rx_lock);
+static int mpc8xxx_dummy_rx_refcnt;
+
+struct spi_mpc8xxx_cs {
+	/* functions to deal with different sized buffers */
+	void (*get_rx) (u32 rx_data, struct mpc8xxx_spi *);
+	u32 (*get_tx) (struct mpc8xxx_spi *);
+	u32 rx_shift;		/* RX data reg shift when in qe mode */
+	u32 tx_shift;		/* TX data reg shift when in qe mode */
+	u32 hw_mode;		/* Holds HW mode register settings */
+};
+
+static inline void mpc8xxx_spi_write_reg(__be32 __iomem *reg, u32 val)
+{
+	out_be32(reg, val);
+}
+
+static inline u32 mpc8xxx_spi_read_reg(__be32 __iomem *reg)
+{
+	return in_be32(reg);
+}
+
+#define MPC83XX_SPI_RX_BUF(type) 					  \
+static									  \
+void mpc8xxx_spi_rx_buf_##type(u32 data, struct mpc8xxx_spi *mpc8xxx_spi) \
+{									  \
+	type *rx = mpc8xxx_spi->rx;					  \
+	*rx++ = (type)(data >> mpc8xxx_spi->rx_shift);			  \
+	mpc8xxx_spi->rx = rx;						  \
+}
+
+#define MPC83XX_SPI_TX_BUF(type)				\
+static								\
+u32 mpc8xxx_spi_tx_buf_##type(struct mpc8xxx_spi *mpc8xxx_spi)	\
+{								\
+	u32 data;						\
+	const type *tx = mpc8xxx_spi->tx;			\
+	if (!tx)						\
+		return 0;					\
+	data = *tx++ << mpc8xxx_spi->tx_shift;			\
+	mpc8xxx_spi->tx = tx;					\
+	return data;						\
+}
+
+MPC83XX_SPI_RX_BUF(u8)
+MPC83XX_SPI_RX_BUF(u16)
+MPC83XX_SPI_RX_BUF(u32)
+MPC83XX_SPI_TX_BUF(u8)
+MPC83XX_SPI_TX_BUF(u16)
+MPC83XX_SPI_TX_BUF(u32)
+
+static void mpc8xxx_spi_change_mode(struct spi_device *spi)
+{
+	struct mpc8xxx_spi *mspi = spi_master_get_devdata(spi->master);
+	struct spi_mpc8xxx_cs *cs = spi->controller_state;
+	__be32 __iomem *mode = &mspi->base->mode;
+	unsigned long flags;
+
+	if (cs->hw_mode == mpc8xxx_spi_read_reg(mode))
+		return;
+
+	/* Turn off IRQs locally to minimize time that SPI is disabled. */
+	local_irq_save(flags);
+
+	/* Turn off SPI unit prior changing mode */
+	mpc8xxx_spi_write_reg(mode, cs->hw_mode & ~SPMODE_ENABLE);
+
+	/* When in CPM mode, we need to reinit tx and rx. */
+	if (mspi->flags & SPI_CPM_MODE) {
+		if (mspi->flags & SPI_QE) {
+			qe_issue_cmd(QE_INIT_TX_RX, mspi->subblock,
+				     QE_CR_PROTOCOL_UNSPECIFIED, 0);
+		} else {
+			cpm_command(CPM_SPI_CMD, CPM_CR_INIT_TRX);
+			if (mspi->flags & SPI_CPM1) {
+				out_be16(&mspi->pram->rbptr,
+					 in_be16(&mspi->pram->rbase));
+				out_be16(&mspi->pram->tbptr,
+					 in_be16(&mspi->pram->tbase));
+			}
+		}
+	}
+	mpc8xxx_spi_write_reg(mode, cs->hw_mode);
+	local_irq_restore(flags);
+}
+
+static void mpc8xxx_spi_chipselect(struct spi_device *spi, int value)
+{
+	struct mpc8xxx_spi *mpc8xxx_spi = spi_master_get_devdata(spi->master);
+	struct fsl_spi_platform_data *pdata = spi->dev.parent->platform_data;
+	bool pol = spi->mode & SPI_CS_HIGH;
+	struct spi_mpc8xxx_cs	*cs = spi->controller_state;
+
+	if (value == BITBANG_CS_INACTIVE) {
+		if (pdata->cs_control)
+			pdata->cs_control(spi, !pol);
+	}
+
+	if (value == BITBANG_CS_ACTIVE) {
+		mpc8xxx_spi->rx_shift = cs->rx_shift;
+		mpc8xxx_spi->tx_shift = cs->tx_shift;
+		mpc8xxx_spi->get_rx = cs->get_rx;
+		mpc8xxx_spi->get_tx = cs->get_tx;
+
+		mpc8xxx_spi_change_mode(spi);
+
+		if (pdata->cs_control)
+			pdata->cs_control(spi, pol);
+	}
+}
+
+static int
+mspi_apply_cpu_mode_quirks(struct spi_mpc8xxx_cs *cs,
+			   struct spi_device *spi,
+			   struct mpc8xxx_spi *mpc8xxx_spi,
+			   int bits_per_word)
+{
+	cs->rx_shift = 0;
+	cs->tx_shift = 0;
+	if (bits_per_word <= 8) {
+		cs->get_rx = mpc8xxx_spi_rx_buf_u8;
+		cs->get_tx = mpc8xxx_spi_tx_buf_u8;
+		if (mpc8xxx_spi->flags & SPI_QE_CPU_MODE) {
+			cs->rx_shift = 16;
+			cs->tx_shift = 24;
+		}
+	} else if (bits_per_word <= 16) {
+		cs->get_rx = mpc8xxx_spi_rx_buf_u16;
+		cs->get_tx = mpc8xxx_spi_tx_buf_u16;
+		if (mpc8xxx_spi->flags & SPI_QE_CPU_MODE) {
+			cs->rx_shift = 16;
+			cs->tx_shift = 16;
+		}
+	} else if (bits_per_word <= 32) {
+		cs->get_rx = mpc8xxx_spi_rx_buf_u32;
+		cs->get_tx = mpc8xxx_spi_tx_buf_u32;
+	} else
+		return -EINVAL;
+
+	if (mpc8xxx_spi->flags & SPI_QE_CPU_MODE &&
+	    spi->mode & SPI_LSB_FIRST) {
+		cs->tx_shift = 0;
+		if (bits_per_word <= 8)
+			cs->rx_shift = 8;
+		else
+			cs->rx_shift = 0;
+	}
+	mpc8xxx_spi->rx_shift = cs->rx_shift;
+	mpc8xxx_spi->tx_shift = cs->tx_shift;
+	mpc8xxx_spi->get_rx = cs->get_rx;
+	mpc8xxx_spi->get_tx = cs->get_tx;
+
+	return bits_per_word;
+}
+
+static int
+mspi_apply_qe_mode_quirks(struct spi_mpc8xxx_cs *cs,
+			  struct spi_device *spi,
+			  int bits_per_word)
+{
+	/* QE uses Little Endian for words > 8
+	 * so transform all words > 8 into 8 bits
+	 * Unfortnatly that doesn't work for LSB so
+	 * reject these for now */
+	/* Note: 32 bits word, LSB works iff
+	 * tfcr/rfcr is set to CPMFCR_GBL */
+	if (spi->mode & SPI_LSB_FIRST &&
+	    bits_per_word > 8)
+		return -EINVAL;
+	if (bits_per_word > 8)
+		return 8; /* pretend its 8 bits */
+	return bits_per_word;
+}
+
+static
+int mpc8xxx_spi_setup_transfer(struct spi_device *spi, struct spi_transfer *t)
+{
+	struct mpc8xxx_spi *mpc8xxx_spi;
+	int bits_per_word;
+	u8 pm;
+	u32 hz;
+	struct spi_mpc8xxx_cs	*cs = spi->controller_state;
+
+	mpc8xxx_spi = spi_master_get_devdata(spi->master);
+
+	if (t) {
+		bits_per_word = t->bits_per_word;
+		hz = t->speed_hz;
+	} else {
+		bits_per_word = 0;
+		hz = 0;
+	}
+
+	/* spi_transfer level calls that work per-word */
+	if (!bits_per_word)
+		bits_per_word = spi->bits_per_word;
+
+	/* Make sure its a bit width we support [4..16, 32] */
+	if ((bits_per_word < 4)
+	    || ((bits_per_word > 16) && (bits_per_word != 32)))
+		return -EINVAL;
+
+	if (!hz)
+		hz = spi->max_speed_hz;
+
+	if (!(mpc8xxx_spi->flags & SPI_CPM_MODE))
+		bits_per_word = mspi_apply_cpu_mode_quirks(cs, spi,
+							   mpc8xxx_spi,
+							   bits_per_word);
+	else if (mpc8xxx_spi->flags & SPI_QE)
+		bits_per_word = mspi_apply_qe_mode_quirks(cs, spi,
+							  bits_per_word);
+
+	if (bits_per_word < 0)
+		return bits_per_word;
+
+	if (bits_per_word == 32)
+		bits_per_word = 0;
+	else
+		bits_per_word = bits_per_word - 1;
+
+	/* mask out bits we are going to set */
+	cs->hw_mode &= ~(SPMODE_LEN(0xF) | SPMODE_DIV16
+				  | SPMODE_PM(0xF));
+
+	cs->hw_mode |= SPMODE_LEN(bits_per_word);
+
+	if ((mpc8xxx_spi->spibrg / hz) > 64) {
+		cs->hw_mode |= SPMODE_DIV16;
+		pm = (mpc8xxx_spi->spibrg - 1) / (hz * 64) + 1;
+
+		WARN_ONCE(pm > 16, "%s: Requested speed is too low: %d Hz. "
+			  "Will use %d Hz instead.\n", dev_name(&spi->dev),
+			  hz, mpc8xxx_spi->spibrg / 1024);
+		if (pm > 16)
+			pm = 16;
+	} else
+		pm = (mpc8xxx_spi->spibrg - 1) / (hz * 4) + 1;
+	if (pm)
+		pm--;
+
+	cs->hw_mode |= SPMODE_PM(pm);
+
+	mpc8xxx_spi_change_mode(spi);
+	return 0;
+}
+
+static void mpc8xxx_spi_cpm_bufs_start(struct mpc8xxx_spi *mspi)
+{
+	struct cpm_buf_desc __iomem *tx_bd = mspi->tx_bd;
+	struct cpm_buf_desc __iomem *rx_bd = mspi->rx_bd;
+	unsigned int xfer_len = min(mspi->count, SPI_MRBLR);
+	unsigned int xfer_ofs;
+
+	xfer_ofs = mspi->xfer_in_progress->len - mspi->count;
+
+	out_be32(&rx_bd->cbd_bufaddr, mspi->rx_dma + xfer_ofs);
+	out_be16(&rx_bd->cbd_datlen, 0);
+	out_be16(&rx_bd->cbd_sc, BD_SC_EMPTY | BD_SC_INTRPT | BD_SC_WRAP);
+
+	out_be32(&tx_bd->cbd_bufaddr, mspi->tx_dma + xfer_ofs);
+	out_be16(&tx_bd->cbd_datlen, xfer_len);
+	out_be16(&tx_bd->cbd_sc, BD_SC_READY | BD_SC_INTRPT | BD_SC_WRAP |
+				 BD_SC_LAST);
+
+	/* start transfer */
+	mpc8xxx_spi_write_reg(&mspi->base->command, SPCOM_STR);
+}
+
+static int mpc8xxx_spi_cpm_bufs(struct mpc8xxx_spi *mspi,
+				struct spi_transfer *t, bool is_dma_mapped)
+{
+	struct device *dev = mspi->dev;
+
+	if (is_dma_mapped) {
+		mspi->map_tx_dma = 0;
+		mspi->map_rx_dma = 0;
+	} else {
+		mspi->map_tx_dma = 1;
+		mspi->map_rx_dma = 1;
+	}
+
+	if (!t->tx_buf) {
+		mspi->tx_dma = mspi->dma_dummy_tx;
+		mspi->map_tx_dma = 0;
+	}
+
+	if (!t->rx_buf) {
+		mspi->rx_dma = mspi->dma_dummy_rx;
+		mspi->map_rx_dma = 0;
+	}
+
+	if (mspi->map_tx_dma) {
+		void *nonconst_tx = (void *)mspi->tx; /* shut up gcc */
+
+		mspi->tx_dma = dma_map_single(dev, nonconst_tx, t->len,
+					      DMA_TO_DEVICE);
+		if (dma_mapping_error(dev, mspi->tx_dma)) {
+			dev_err(dev, "unable to map tx dma\n");
+			return -ENOMEM;
+		}
+	} else if (t->tx_buf) {
+		mspi->tx_dma = t->tx_dma;
+	}
+
+	if (mspi->map_rx_dma) {
+		mspi->rx_dma = dma_map_single(dev, mspi->rx, t->len,
+					      DMA_FROM_DEVICE);
+		if (dma_mapping_error(dev, mspi->rx_dma)) {
+			dev_err(dev, "unable to map rx dma\n");
+			goto err_rx_dma;
+		}
+	} else if (t->rx_buf) {
+		mspi->rx_dma = t->rx_dma;
+	}
+
+	/* enable rx ints */
+	mpc8xxx_spi_write_reg(&mspi->base->mask, SPIE_RXB);
+
+	mspi->xfer_in_progress = t;
+	mspi->count = t->len;
+
+	/* start CPM transfers */
+	mpc8xxx_spi_cpm_bufs_start(mspi);
+
+	return 0;
+
+err_rx_dma:
+	if (mspi->map_tx_dma)
+		dma_unmap_single(dev, mspi->tx_dma, t->len, DMA_TO_DEVICE);
+	return -ENOMEM;
+}
+
+static void mpc8xxx_spi_cpm_bufs_complete(struct mpc8xxx_spi *mspi)
+{
+	struct device *dev = mspi->dev;
+	struct spi_transfer *t = mspi->xfer_in_progress;
+
+	if (mspi->map_tx_dma)
+		dma_unmap_single(dev, mspi->tx_dma, t->len, DMA_TO_DEVICE);
+	if (mspi->map_rx_dma)
+		dma_unmap_single(dev, mspi->rx_dma, t->len, DMA_FROM_DEVICE);
+	mspi->xfer_in_progress = NULL;
+}
+
+static int mpc8xxx_spi_cpu_bufs(struct mpc8xxx_spi *mspi,
+				struct spi_transfer *t, unsigned int len)
+{
+	u32 word;
+
+	mspi->count = len;
+
+	/* enable rx ints */
+	mpc8xxx_spi_write_reg(&mspi->base->mask, SPIM_NE);
+
+	/* transmit word */
+	word = mspi->get_tx(mspi);
+	mpc8xxx_spi_write_reg(&mspi->base->transmit, word);
+
+	return 0;
+}
+
+static int mpc8xxx_spi_bufs(struct spi_device *spi, struct spi_transfer *t,
+			    bool is_dma_mapped)
+{
+	struct mpc8xxx_spi *mpc8xxx_spi = spi_master_get_devdata(spi->master);
+	unsigned int len = t->len;
+	u8 bits_per_word;
+	int ret;
+
+	bits_per_word = spi->bits_per_word;
+	if (t->bits_per_word)
+		bits_per_word = t->bits_per_word;
+
+	if (bits_per_word > 8) {
+		/* invalid length? */
+		if (len & 1)
+			return -EINVAL;
+		len /= 2;
+	}
+	if (bits_per_word > 16) {
+		/* invalid length? */
+		if (len & 1)
+			return -EINVAL;
+		len /= 2;
+	}
+
+	mpc8xxx_spi->tx = t->tx_buf;
+	mpc8xxx_spi->rx = t->rx_buf;
+
+	INIT_COMPLETION(mpc8xxx_spi->done);
+
+	if (mpc8xxx_spi->flags & SPI_CPM_MODE)
+		ret = mpc8xxx_spi_cpm_bufs(mpc8xxx_spi, t, is_dma_mapped);
+	else
+		ret = mpc8xxx_spi_cpu_bufs(mpc8xxx_spi, t, len);
+	if (ret)
+		return ret;
+
+	wait_for_completion(&mpc8xxx_spi->done);
+
+	/* disable rx ints */
+	mpc8xxx_spi_write_reg(&mpc8xxx_spi->base->mask, 0);
+
+	if (mpc8xxx_spi->flags & SPI_CPM_MODE)
+		mpc8xxx_spi_cpm_bufs_complete(mpc8xxx_spi);
+
+	return mpc8xxx_spi->count;
+}
+
+static void mpc8xxx_spi_do_one_msg(struct spi_message *m)
+{
+	struct spi_device *spi = m->spi;
+	struct spi_transfer *t;
+	unsigned int cs_change;
+	const int nsecs = 50;
+	int status;
+
+	cs_change = 1;
+	status = 0;
+	list_for_each_entry(t, &m->transfers, transfer_list) {
+		if (t->bits_per_word || t->speed_hz) {
+			/* Don't allow changes if CS is active */
+			status = -EINVAL;
+
+			if (cs_change)
+				status = mpc8xxx_spi_setup_transfer(spi, t);
+			if (status < 0)
+				break;
+		}
+
+		if (cs_change) {
+			mpc8xxx_spi_chipselect(spi, BITBANG_CS_ACTIVE);
+			ndelay(nsecs);
+		}
+		cs_change = t->cs_change;
+		if (t->len)
+			status = mpc8xxx_spi_bufs(spi, t, m->is_dma_mapped);
+		if (status) {
+			status = -EMSGSIZE;
+			break;
+		}
+		m->actual_length += t->len;
+
+		if (t->delay_usecs)
+			udelay(t->delay_usecs);
+
+		if (cs_change) {
+			ndelay(nsecs);
+			mpc8xxx_spi_chipselect(spi, BITBANG_CS_INACTIVE);
+			ndelay(nsecs);
+		}
+	}
+
+	m->status = status;
+	m->complete(m->context);
+
+	if (status || !cs_change) {
+		ndelay(nsecs);
+		mpc8xxx_spi_chipselect(spi, BITBANG_CS_INACTIVE);
+	}
+
+	mpc8xxx_spi_setup_transfer(spi, NULL);
+}
+
+static void mpc8xxx_spi_work(struct work_struct *work)
+{
+	struct mpc8xxx_spi *mpc8xxx_spi = container_of(work, struct mpc8xxx_spi,
+						       work);
+
+	spin_lock_irq(&mpc8xxx_spi->lock);
+	while (!list_empty(&mpc8xxx_spi->queue)) {
+		struct spi_message *m = container_of(mpc8xxx_spi->queue.next,
+						   struct spi_message, queue);
+
+		list_del_init(&m->queue);
+		spin_unlock_irq(&mpc8xxx_spi->lock);
+
+		mpc8xxx_spi_do_one_msg(m);
+
+		spin_lock_irq(&mpc8xxx_spi->lock);
+	}
+	spin_unlock_irq(&mpc8xxx_spi->lock);
+}
+
+static int mpc8xxx_spi_setup(struct spi_device *spi)
+{
+	struct mpc8xxx_spi *mpc8xxx_spi;
+	int retval;
+	u32 hw_mode;
+	struct spi_mpc8xxx_cs	*cs = spi->controller_state;
+
+	if (!spi->max_speed_hz)
+		return -EINVAL;
+
+	if (!cs) {
+		cs = kzalloc(sizeof *cs, GFP_KERNEL);
+		if (!cs)
+			return -ENOMEM;
+		spi->controller_state = cs;
+	}
+	mpc8xxx_spi = spi_master_get_devdata(spi->master);
+
+	hw_mode = cs->hw_mode; /* Save original settings */
+	cs->hw_mode = mpc8xxx_spi_read_reg(&mpc8xxx_spi->base->mode);
+	/* mask out bits we are going to set */
+	cs->hw_mode &= ~(SPMODE_CP_BEGIN_EDGECLK | SPMODE_CI_INACTIVEHIGH
+			 | SPMODE_REV | SPMODE_LOOP);
+
+	if (spi->mode & SPI_CPHA)
+		cs->hw_mode |= SPMODE_CP_BEGIN_EDGECLK;
+	if (spi->mode & SPI_CPOL)
+		cs->hw_mode |= SPMODE_CI_INACTIVEHIGH;
+	if (!(spi->mode & SPI_LSB_FIRST))
+		cs->hw_mode |= SPMODE_REV;
+	if (spi->mode & SPI_LOOP)
+		cs->hw_mode |= SPMODE_LOOP;
+
+	retval = mpc8xxx_spi_setup_transfer(spi, NULL);
+	if (retval < 0) {
+		cs->hw_mode = hw_mode; /* Restore settings */
+		return retval;
+	}
+	return 0;
+}
+
+static void mpc8xxx_spi_cpm_irq(struct mpc8xxx_spi *mspi, u32 events)
+{
+	u16 len;
+
+	dev_dbg(mspi->dev, "%s: bd datlen %d, count %d\n", __func__,
+		in_be16(&mspi->rx_bd->cbd_datlen), mspi->count);
+
+	len = in_be16(&mspi->rx_bd->cbd_datlen);
+	if (len > mspi->count) {
+		WARN_ON(1);
+		len = mspi->count;
+	}
+
+	/* Clear the events */
+	mpc8xxx_spi_write_reg(&mspi->base->event, events);
+
+	mspi->count -= len;
+	if (mspi->count)
+		mpc8xxx_spi_cpm_bufs_start(mspi);
+	else
+		complete(&mspi->done);
+}
+
+static void mpc8xxx_spi_cpu_irq(struct mpc8xxx_spi *mspi, u32 events)
+{
+	/* We need handle RX first */
+	if (events & SPIE_NE) {
+		u32 rx_data = mpc8xxx_spi_read_reg(&mspi->base->receive);
+
+		if (mspi->rx)
+			mspi->get_rx(rx_data, mspi);
+	}
+
+	if ((events & SPIE_NF) == 0)
+		/* spin until TX is done */
+		while (((events =
+			mpc8xxx_spi_read_reg(&mspi->base->event)) &
+						SPIE_NF) == 0)
+			cpu_relax();
+
+	/* Clear the events */
+	mpc8xxx_spi_write_reg(&mspi->base->event, events);
+
+	mspi->count -= 1;
+	if (mspi->count) {
+		u32 word = mspi->get_tx(mspi);
+
+		mpc8xxx_spi_write_reg(&mspi->base->transmit, word);
+	} else {
+		complete(&mspi->done);
+	}
+}
+
+static irqreturn_t mpc8xxx_spi_irq(s32 irq, void *context_data)
+{
+	struct mpc8xxx_spi *mspi = context_data;
+	irqreturn_t ret = IRQ_NONE;
+	u32 events;
+
+	/* Get interrupt events(tx/rx) */
+	events = mpc8xxx_spi_read_reg(&mspi->base->event);
+	if (events)
+		ret = IRQ_HANDLED;
+
+	dev_dbg(mspi->dev, "%s: events %x\n", __func__, events);
+
+	if (mspi->flags & SPI_CPM_MODE)
+		mpc8xxx_spi_cpm_irq(mspi, events);
+	else
+		mpc8xxx_spi_cpu_irq(mspi, events);
+
+	return ret;
+}
+
+static int mpc8xxx_spi_transfer(struct spi_device *spi,
+				struct spi_message *m)
+{
+	struct mpc8xxx_spi *mpc8xxx_spi = spi_master_get_devdata(spi->master);
+	unsigned long flags;
+
+	m->actual_length = 0;
+	m->status = -EINPROGRESS;
+
+	spin_lock_irqsave(&mpc8xxx_spi->lock, flags);
+	list_add_tail(&m->queue, &mpc8xxx_spi->queue);
+	queue_work(mpc8xxx_spi->workqueue, &mpc8xxx_spi->work);
+	spin_unlock_irqrestore(&mpc8xxx_spi->lock, flags);
+
+	return 0;
+}
+
+
+static void mpc8xxx_spi_cleanup(struct spi_device *spi)
+{
+	kfree(spi->controller_state);
+}
+
+static void *mpc8xxx_spi_alloc_dummy_rx(void)
+{
+	mutex_lock(&mpc8xxx_dummy_rx_lock);
+
+	if (!mpc8xxx_dummy_rx)
+		mpc8xxx_dummy_rx = kmalloc(SPI_MRBLR, GFP_KERNEL);
+	if (mpc8xxx_dummy_rx)
+		mpc8xxx_dummy_rx_refcnt++;
+
+	mutex_unlock(&mpc8xxx_dummy_rx_lock);
+
+	return mpc8xxx_dummy_rx;
+}
+
+static void mpc8xxx_spi_free_dummy_rx(void)
+{
+	mutex_lock(&mpc8xxx_dummy_rx_lock);
+
+	switch (mpc8xxx_dummy_rx_refcnt) {
+	case 0:
+		WARN_ON(1);
+		break;
+	case 1:
+		kfree(mpc8xxx_dummy_rx);
+		mpc8xxx_dummy_rx = NULL;
+		/* fall through */
+	default:
+		mpc8xxx_dummy_rx_refcnt--;
+		break;
+	}
+
+	mutex_unlock(&mpc8xxx_dummy_rx_lock);
+}
+
+static unsigned long mpc8xxx_spi_cpm_get_pram(struct mpc8xxx_spi *mspi)
+{
+	struct device *dev = mspi->dev;
+	struct device_node *np = dev->of_node;
+	const u32 *iprop;
+	int size;
+	unsigned long spi_base_ofs;
+	unsigned long pram_ofs = -ENOMEM;
+
+	/* Can't use of_address_to_resource(), QE muram isn't at 0. */
+	iprop = of_get_property(np, "reg", &size);
+
+	/* QE with a fixed pram location? */
+	if (mspi->flags & SPI_QE && iprop && size == sizeof(*iprop) * 4)
+		return cpm_muram_alloc_fixed(iprop[2], SPI_PRAM_SIZE);
+
+	/* QE but with a dynamic pram location? */
+	if (mspi->flags & SPI_QE) {
+		pram_ofs = cpm_muram_alloc(SPI_PRAM_SIZE, 64);
+		qe_issue_cmd(QE_ASSIGN_PAGE_TO_DEVICE, mspi->subblock,
+				QE_CR_PROTOCOL_UNSPECIFIED, pram_ofs);
+		return pram_ofs;
+	}
+
+	/* CPM1 and CPM2 pram must be at a fixed addr. */
+	if (!iprop || size != sizeof(*iprop) * 4)
+		return -ENOMEM;
+
+	spi_base_ofs = cpm_muram_alloc_fixed(iprop[2], 2);
+	if (IS_ERR_VALUE(spi_base_ofs))
+		return -ENOMEM;
+
+	if (mspi->flags & SPI_CPM2) {
+		pram_ofs = cpm_muram_alloc(SPI_PRAM_SIZE, 64);
+		if (!IS_ERR_VALUE(pram_ofs)) {
+			u16 __iomem *spi_base = cpm_muram_addr(spi_base_ofs);
+
+			out_be16(spi_base, pram_ofs);
+		}
+	} else {
+		struct spi_pram __iomem *pram = cpm_muram_addr(spi_base_ofs);
+		u16 rpbase = in_be16(&pram->rpbase);
+
+		/* Microcode relocation patch applied? */
+		if (rpbase)
+			pram_ofs = rpbase;
+		else
+			return spi_base_ofs;
+	}
+
+	cpm_muram_free(spi_base_ofs);
+	return pram_ofs;
+}
+
+static int mpc8xxx_spi_cpm_init(struct mpc8xxx_spi *mspi)
+{
+	struct device *dev = mspi->dev;
+	struct device_node *np = dev->of_node;
+	const u32 *iprop;
+	int size;
+	unsigned long pram_ofs;
+	unsigned long bds_ofs;
+
+	if (!(mspi->flags & SPI_CPM_MODE))
+		return 0;
+
+	if (!mpc8xxx_spi_alloc_dummy_rx())
+		return -ENOMEM;
+
+	if (mspi->flags & SPI_QE) {
+		iprop = of_get_property(np, "cell-index", &size);
+		if (iprop && size == sizeof(*iprop))
+			mspi->subblock = *iprop;
+
+		switch (mspi->subblock) {
+		default:
+			dev_warn(dev, "cell-index unspecified, assuming SPI1");
+			/* fall through */
+		case 0:
+			mspi->subblock = QE_CR_SUBBLOCK_SPI1;
+			break;
+		case 1:
+			mspi->subblock = QE_CR_SUBBLOCK_SPI2;
+			break;
+		}
+	}
+
+	pram_ofs = mpc8xxx_spi_cpm_get_pram(mspi);
+	if (IS_ERR_VALUE(pram_ofs)) {
+		dev_err(dev, "can't allocate spi parameter ram\n");
+		goto err_pram;
+	}
+
+	bds_ofs = cpm_muram_alloc(sizeof(*mspi->tx_bd) +
+				  sizeof(*mspi->rx_bd), 8);
+	if (IS_ERR_VALUE(bds_ofs)) {
+		dev_err(dev, "can't allocate bds\n");
+		goto err_bds;
+	}
+
+	mspi->dma_dummy_tx = dma_map_single(dev, empty_zero_page, PAGE_SIZE,
+					    DMA_TO_DEVICE);
+	if (dma_mapping_error(dev, mspi->dma_dummy_tx)) {
+		dev_err(dev, "unable to map dummy tx buffer\n");
+		goto err_dummy_tx;
+	}
+
+	mspi->dma_dummy_rx = dma_map_single(dev, mpc8xxx_dummy_rx, SPI_MRBLR,
+					    DMA_FROM_DEVICE);
+	if (dma_mapping_error(dev, mspi->dma_dummy_rx)) {
+		dev_err(dev, "unable to map dummy rx buffer\n");
+		goto err_dummy_rx;
+	}
+
+	mspi->pram = cpm_muram_addr(pram_ofs);
+
+	mspi->tx_bd = cpm_muram_addr(bds_ofs);
+	mspi->rx_bd = cpm_muram_addr(bds_ofs + sizeof(*mspi->tx_bd));
+
+	/* Initialize parameter ram. */
+	out_be16(&mspi->pram->tbase, cpm_muram_offset(mspi->tx_bd));
+	out_be16(&mspi->pram->rbase, cpm_muram_offset(mspi->rx_bd));
+	out_8(&mspi->pram->tfcr, CPMFCR_EB | CPMFCR_GBL);
+	out_8(&mspi->pram->rfcr, CPMFCR_EB | CPMFCR_GBL);
+	out_be16(&mspi->pram->mrblr, SPI_MRBLR);
+	out_be32(&mspi->pram->rstate, 0);
+	out_be32(&mspi->pram->rdp, 0);
+	out_be16(&mspi->pram->rbptr, 0);
+	out_be16(&mspi->pram->rbc, 0);
+	out_be32(&mspi->pram->rxtmp, 0);
+	out_be32(&mspi->pram->tstate, 0);
+	out_be32(&mspi->pram->tdp, 0);
+	out_be16(&mspi->pram->tbptr, 0);
+	out_be16(&mspi->pram->tbc, 0);
+	out_be32(&mspi->pram->txtmp, 0);
+
+	return 0;
+
+err_dummy_rx:
+	dma_unmap_single(dev, mspi->dma_dummy_tx, PAGE_SIZE, DMA_TO_DEVICE);
+err_dummy_tx:
+	cpm_muram_free(bds_ofs);
+err_bds:
+	cpm_muram_free(pram_ofs);
+err_pram:
+	mpc8xxx_spi_free_dummy_rx();
+	return -ENOMEM;
+}
+
+static void mpc8xxx_spi_cpm_free(struct mpc8xxx_spi *mspi)
+{
+	struct device *dev = mspi->dev;
+
+	dma_unmap_single(dev, mspi->dma_dummy_rx, SPI_MRBLR, DMA_FROM_DEVICE);
+	dma_unmap_single(dev, mspi->dma_dummy_tx, PAGE_SIZE, DMA_TO_DEVICE);
+	cpm_muram_free(cpm_muram_offset(mspi->tx_bd));
+	cpm_muram_free(cpm_muram_offset(mspi->pram));
+	mpc8xxx_spi_free_dummy_rx();
+}
+
+static const char *mpc8xxx_spi_strmode(unsigned int flags)
+{
+	if (flags & SPI_QE_CPU_MODE) {
+		return "QE CPU";
+	} else if (flags & SPI_CPM_MODE) {
+		if (flags & SPI_QE)
+			return "QE";
+		else if (flags & SPI_CPM2)
+			return "CPM2";
+		else
+			return "CPM1";
+	}
+	return "CPU";
+}
+
+static struct spi_master * __devinit
+mpc8xxx_spi_probe(struct device *dev, struct resource *mem, unsigned int irq)
+{
+	struct fsl_spi_platform_data *pdata = dev->platform_data;
+	struct spi_master *master;
+	struct mpc8xxx_spi *mpc8xxx_spi;
+	u32 regval;
+	int ret = 0;
+
+	master = spi_alloc_master(dev, sizeof(struct mpc8xxx_spi));
+	if (master == NULL) {
+		ret = -ENOMEM;
+		goto err;
+	}
+
+	dev_set_drvdata(dev, master);
+
+	/* the spi->mode bits understood by this driver: */
+	master->mode_bits = SPI_CPOL | SPI_CPHA | SPI_CS_HIGH
+			| SPI_LSB_FIRST | SPI_LOOP;
+
+	master->setup = mpc8xxx_spi_setup;
+	master->transfer = mpc8xxx_spi_transfer;
+	master->cleanup = mpc8xxx_spi_cleanup;
+	master->dev.of_node = dev->of_node;
+
+	mpc8xxx_spi = spi_master_get_devdata(master);
+	mpc8xxx_spi->dev = dev;
+	mpc8xxx_spi->get_rx = mpc8xxx_spi_rx_buf_u8;
+	mpc8xxx_spi->get_tx = mpc8xxx_spi_tx_buf_u8;
+	mpc8xxx_spi->flags = pdata->flags;
+	mpc8xxx_spi->spibrg = pdata->sysclk;
+
+	ret = mpc8xxx_spi_cpm_init(mpc8xxx_spi);
+	if (ret)
+		goto err_cpm_init;
+
+	mpc8xxx_spi->rx_shift = 0;
+	mpc8xxx_spi->tx_shift = 0;
+	if (mpc8xxx_spi->flags & SPI_QE_CPU_MODE) {
+		mpc8xxx_spi->rx_shift = 16;
+		mpc8xxx_spi->tx_shift = 24;
+	}
+
+	init_completion(&mpc8xxx_spi->done);
+
+	mpc8xxx_spi->base = ioremap(mem->start, resource_size(mem));
+	if (mpc8xxx_spi->base == NULL) {
+		ret = -ENOMEM;
+		goto err_ioremap;
+	}
+
+	mpc8xxx_spi->irq = irq;
+
+	/* Register for SPI Interrupt */
+	ret = request_irq(mpc8xxx_spi->irq, mpc8xxx_spi_irq,
+			  0, "mpc8xxx_spi", mpc8xxx_spi);
+
+	if (ret != 0)
+		goto unmap_io;
+
+	master->bus_num = pdata->bus_num;
+	master->num_chipselect = pdata->max_chipselect;
+
+	/* SPI controller initializations */
+	mpc8xxx_spi_write_reg(&mpc8xxx_spi->base->mode, 0);
+	mpc8xxx_spi_write_reg(&mpc8xxx_spi->base->mask, 0);
+	mpc8xxx_spi_write_reg(&mpc8xxx_spi->base->command, 0);
+	mpc8xxx_spi_write_reg(&mpc8xxx_spi->base->event, 0xffffffff);
+
+	/* Enable SPI interface */
+	regval = pdata->initial_spmode | SPMODE_INIT_VAL | SPMODE_ENABLE;
+	if (mpc8xxx_spi->flags & SPI_QE_CPU_MODE)
+		regval |= SPMODE_OP;
+
+	mpc8xxx_spi_write_reg(&mpc8xxx_spi->base->mode, regval);
+	spin_lock_init(&mpc8xxx_spi->lock);
+	init_completion(&mpc8xxx_spi->done);
+	INIT_WORK(&mpc8xxx_spi->work, mpc8xxx_spi_work);
+	INIT_LIST_HEAD(&mpc8xxx_spi->queue);
+
+	mpc8xxx_spi->workqueue = create_singlethread_workqueue(
+		dev_name(master->dev.parent));
+	if (mpc8xxx_spi->workqueue == NULL) {
+		ret = -EBUSY;
+		goto free_irq;
+	}
+
+	ret = spi_register_master(master);
+	if (ret < 0)
+		goto unreg_master;
+
+	dev_info(dev, "at 0x%p (irq = %d), %s mode\n", mpc8xxx_spi->base,
+		 mpc8xxx_spi->irq, mpc8xxx_spi_strmode(mpc8xxx_spi->flags));
+
+	return master;
+
+unreg_master:
+	destroy_workqueue(mpc8xxx_spi->workqueue);
+free_irq:
+	free_irq(mpc8xxx_spi->irq, mpc8xxx_spi);
+unmap_io:
+	iounmap(mpc8xxx_spi->base);
+err_ioremap:
+	mpc8xxx_spi_cpm_free(mpc8xxx_spi);
+err_cpm_init:
+	spi_master_put(master);
+err:
+	return ERR_PTR(ret);
+}
+
+static int __devexit mpc8xxx_spi_remove(struct device *dev)
+{
+	struct mpc8xxx_spi *mpc8xxx_spi;
+	struct spi_master *master;
+
+	master = dev_get_drvdata(dev);
+	mpc8xxx_spi = spi_master_get_devdata(master);
+
+	flush_workqueue(mpc8xxx_spi->workqueue);
+	destroy_workqueue(mpc8xxx_spi->workqueue);
+	spi_unregister_master(master);
+
+	free_irq(mpc8xxx_spi->irq, mpc8xxx_spi);
+	iounmap(mpc8xxx_spi->base);
+	mpc8xxx_spi_cpm_free(mpc8xxx_spi);
+
+	return 0;
+}
+
+struct mpc8xxx_spi_probe_info {
+	struct fsl_spi_platform_data pdata;
+	int *gpios;
+	bool *alow_flags;
+};
+
+static struct mpc8xxx_spi_probe_info *
+to_of_pinfo(struct fsl_spi_platform_data *pdata)
+{
+	return container_of(pdata, struct mpc8xxx_spi_probe_info, pdata);
+}
+
+static void mpc8xxx_spi_cs_control(struct spi_device *spi, bool on)
+{
+	struct device *dev = spi->dev.parent;
+	struct mpc8xxx_spi_probe_info *pinfo = to_of_pinfo(dev->platform_data);
+	u16 cs = spi->chip_select;
+	int gpio = pinfo->gpios[cs];
+	bool alow = pinfo->alow_flags[cs];
+
+	gpio_set_value(gpio, on ^ alow);
+}
+
+static int of_mpc8xxx_spi_get_chipselects(struct device *dev)
+{
+	struct device_node *np = dev->of_node;
+	struct fsl_spi_platform_data *pdata = dev->platform_data;
+	struct mpc8xxx_spi_probe_info *pinfo = to_of_pinfo(pdata);
+	unsigned int ngpios;
+	int i = 0;
+	int ret;
+
+	ngpios = of_gpio_count(np);
+	if (!ngpios) {
+		/*
+		 * SPI w/o chip-select line. One SPI device is still permitted
+		 * though.
+		 */
+		pdata->max_chipselect = 1;
+		return 0;
+	}
+
+	pinfo->gpios = kmalloc(ngpios * sizeof(*pinfo->gpios), GFP_KERNEL);
+	if (!pinfo->gpios)
+		return -ENOMEM;
+	memset(pinfo->gpios, -1, ngpios * sizeof(*pinfo->gpios));
+
+	pinfo->alow_flags = kzalloc(ngpios * sizeof(*pinfo->alow_flags),
+				    GFP_KERNEL);
+	if (!pinfo->alow_flags) {
+		ret = -ENOMEM;
+		goto err_alloc_flags;
+	}
+
+	for (; i < ngpios; i++) {
+		int gpio;
+		enum of_gpio_flags flags;
+
+		gpio = of_get_gpio_flags(np, i, &flags);
+		if (!gpio_is_valid(gpio)) {
+			dev_err(dev, "invalid gpio #%d: %d\n", i, gpio);
+			ret = gpio;
+			goto err_loop;
+		}
+
+		ret = gpio_request(gpio, dev_name(dev));
+		if (ret) {
+			dev_err(dev, "can't request gpio #%d: %d\n", i, ret);
+			goto err_loop;
+		}
+
+		pinfo->gpios[i] = gpio;
+		pinfo->alow_flags[i] = flags & OF_GPIO_ACTIVE_LOW;
+
+		ret = gpio_direction_output(pinfo->gpios[i],
+					    pinfo->alow_flags[i]);
+		if (ret) {
+			dev_err(dev, "can't set output direction for gpio "
+				"#%d: %d\n", i, ret);
+			goto err_loop;
+		}
+	}
+
+	pdata->max_chipselect = ngpios;
+	pdata->cs_control = mpc8xxx_spi_cs_control;
+
+	return 0;
+
+err_loop:
+	while (i >= 0) {
+		if (gpio_is_valid(pinfo->gpios[i]))
+			gpio_free(pinfo->gpios[i]);
+		i--;
+	}
+
+	kfree(pinfo->alow_flags);
+	pinfo->alow_flags = NULL;
+err_alloc_flags:
+	kfree(pinfo->gpios);
+	pinfo->gpios = NULL;
+	return ret;
+}
+
+static int of_mpc8xxx_spi_free_chipselects(struct device *dev)
+{
+	struct fsl_spi_platform_data *pdata = dev->platform_data;
+	struct mpc8xxx_spi_probe_info *pinfo = to_of_pinfo(pdata);
+	int i;
+
+	if (!pinfo->gpios)
+		return 0;
+
+	for (i = 0; i < pdata->max_chipselect; i++) {
+		if (gpio_is_valid(pinfo->gpios[i]))
+			gpio_free(pinfo->gpios[i]);
+	}
+
+	kfree(pinfo->gpios);
+	kfree(pinfo->alow_flags);
+	return 0;
+}
+
+static int __devinit of_mpc8xxx_spi_probe(struct platform_device *ofdev,
+					  const struct of_device_id *ofid)
+{
+	struct device *dev = &ofdev->dev;
+	struct device_node *np = ofdev->dev.of_node;
+	struct mpc8xxx_spi_probe_info *pinfo;
+	struct fsl_spi_platform_data *pdata;
+	struct spi_master *master;
+	struct resource mem;
+	struct resource irq;
+	const void *prop;
+	int ret = -ENOMEM;
+
+	pinfo = kzalloc(sizeof(*pinfo), GFP_KERNEL);
+	if (!pinfo)
+		return -ENOMEM;
+
+	pdata = &pinfo->pdata;
+	dev->platform_data = pdata;
+
+	/* Allocate bus num dynamically. */
+	pdata->bus_num = -1;
+
+	/* SPI controller is either clocked from QE or SoC clock. */
+	pdata->sysclk = get_brgfreq();
+	if (pdata->sysclk == -1) {
+		pdata->sysclk = fsl_get_sys_freq();
+		if (pdata->sysclk == -1) {
+			ret = -ENODEV;
+			goto err_clk;
+		}
+	}
+
+	prop = of_get_property(np, "mode", NULL);
+	if (prop && !strcmp(prop, "cpu-qe"))
+		pdata->flags = SPI_QE_CPU_MODE;
+	else if (prop && !strcmp(prop, "qe"))
+		pdata->flags = SPI_CPM_MODE | SPI_QE;
+	else if (of_device_is_compatible(np, "fsl,cpm2-spi"))
+		pdata->flags = SPI_CPM_MODE | SPI_CPM2;
+	else if (of_device_is_compatible(np, "fsl,cpm1-spi"))
+		pdata->flags = SPI_CPM_MODE | SPI_CPM1;
+
+	ret = of_mpc8xxx_spi_get_chipselects(dev);
+	if (ret)
+		goto err;
+
+	ret = of_address_to_resource(np, 0, &mem);
+	if (ret)
+		goto err;
+
+	ret = of_irq_to_resource(np, 0, &irq);
+	if (!ret) {
+		ret = -EINVAL;
+		goto err;
+	}
+
+	master = mpc8xxx_spi_probe(dev, &mem, irq.start);
+	if (IS_ERR(master)) {
+		ret = PTR_ERR(master);
+		goto err;
+	}
+
+	return 0;
+
+err:
+	of_mpc8xxx_spi_free_chipselects(dev);
+err_clk:
+	kfree(pinfo);
+	return ret;
+}
+
+static int __devexit of_mpc8xxx_spi_remove(struct platform_device *ofdev)
+{
+	int ret;
+
+	ret = mpc8xxx_spi_remove(&ofdev->dev);
+	if (ret)
+		return ret;
+	of_mpc8xxx_spi_free_chipselects(&ofdev->dev);
+	return 0;
+}
+
+static const struct of_device_id of_mpc8xxx_spi_match[] = {
+	{ .compatible = "fsl,spi" },
+	{},
+};
+MODULE_DEVICE_TABLE(of, of_mpc8xxx_spi_match);
+
+static struct of_platform_driver of_mpc8xxx_spi_driver = {
+	.driver = {
+		.name = "mpc8xxx_spi",
+		.owner = THIS_MODULE,
+		.of_match_table = of_mpc8xxx_spi_match,
+	},
+	.probe		= of_mpc8xxx_spi_probe,
+	.remove		= __devexit_p(of_mpc8xxx_spi_remove),
+};
+
+#ifdef CONFIG_MPC832x_RDB
+/*
+ * 				XXX XXX XXX
+ * This is "legacy" platform driver, was used by the MPC8323E-RDB boards
+ * only. The driver should go away soon, since newer MPC8323E-RDB's device
+ * tree can work with OpenFirmware driver. But for now we support old trees
+ * as well.
+ */
+static int __devinit plat_mpc8xxx_spi_probe(struct platform_device *pdev)
+{
+	struct resource *mem;
+	int irq;
+	struct spi_master *master;
+
+	if (!pdev->dev.platform_data)
+		return -EINVAL;
+
+	mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	if (!mem)
+		return -EINVAL;
+
+	irq = platform_get_irq(pdev, 0);
+	if (irq <= 0)
+		return -EINVAL;
+
+	master = mpc8xxx_spi_probe(&pdev->dev, mem, irq);
+	if (IS_ERR(master))
+		return PTR_ERR(master);
+	return 0;
+}
+
+static int __devexit plat_mpc8xxx_spi_remove(struct platform_device *pdev)
+{
+	return mpc8xxx_spi_remove(&pdev->dev);
+}
+
+MODULE_ALIAS("platform:mpc8xxx_spi");
+static struct platform_driver mpc8xxx_spi_driver = {
+	.probe = plat_mpc8xxx_spi_probe,
+	.remove = __devexit_p(plat_mpc8xxx_spi_remove),
+	.driver = {
+		.name = "mpc8xxx_spi",
+		.owner = THIS_MODULE,
+	},
+};
+
+static bool legacy_driver_failed;
+
+static void __init legacy_driver_register(void)
+{
+	legacy_driver_failed = platform_driver_register(&mpc8xxx_spi_driver);
+}
+
+static void __exit legacy_driver_unregister(void)
+{
+	if (legacy_driver_failed)
+		return;
+	platform_driver_unregister(&mpc8xxx_spi_driver);
+}
+#else
+static void __init legacy_driver_register(void) {}
+static void __exit legacy_driver_unregister(void) {}
+#endif /* CONFIG_MPC832x_RDB */
+
+static int __init mpc8xxx_spi_init(void)
+{
+	legacy_driver_register();
+	return of_register_platform_driver(&of_mpc8xxx_spi_driver);
+}
+
+static void __exit mpc8xxx_spi_exit(void)
+{
+	of_unregister_platform_driver(&of_mpc8xxx_spi_driver);
+	legacy_driver_unregister();
+}
+
+module_init(mpc8xxx_spi_init);
+module_exit(mpc8xxx_spi_exit);
+
+MODULE_AUTHOR("Kumar Gala");
+MODULE_DESCRIPTION("Simple MPC8xxx SPI Driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/spi/spi_mpc8xxx.c b/drivers/spi/spi_mpc8xxx.c
deleted file mode 100644
index d31b57f..0000000
--- a/drivers/spi/spi_mpc8xxx.c
+++ /dev/null
@@ -1,1419 +0,0 @@
-/*
- * MPC8xxx SPI controller driver.
- *
- * Maintainer: Kumar Gala
- *
- * Copyright (C) 2006 Polycom, Inc.
- *
- * CPM SPI and QE buffer descriptors mode support:
- * Copyright (c) 2009  MontaVista Software, Inc.
- * Author: Anton Vorontsov <avorontsov@ru.mvista.com>
- *
- * This program is free software; you can redistribute  it and/or modify it
- * under  the terms of  the GNU General  Public License as published by the
- * Free Software Foundation;  either version 2 of the  License, or (at your
- * option) any later version.
- */
-#include <linux/module.h>
-#include <linux/init.h>
-#include <linux/types.h>
-#include <linux/kernel.h>
-#include <linux/bug.h>
-#include <linux/errno.h>
-#include <linux/err.h>
-#include <linux/io.h>
-#include <linux/completion.h>
-#include <linux/interrupt.h>
-#include <linux/delay.h>
-#include <linux/irq.h>
-#include <linux/device.h>
-#include <linux/spi/spi.h>
-#include <linux/spi/spi_bitbang.h>
-#include <linux/platform_device.h>
-#include <linux/fsl_devices.h>
-#include <linux/dma-mapping.h>
-#include <linux/mm.h>
-#include <linux/mutex.h>
-#include <linux/of.h>
-#include <linux/of_platform.h>
-#include <linux/gpio.h>
-#include <linux/of_gpio.h>
-#include <linux/slab.h>
-
-#include <sysdev/fsl_soc.h>
-#include <asm/cpm.h>
-#include <asm/qe.h>
-#include <asm/irq.h>
-
-/* CPM1 and CPM2 are mutually exclusive. */
-#ifdef CONFIG_CPM1
-#include <asm/cpm1.h>
-#define CPM_SPI_CMD mk_cr_cmd(CPM_CR_CH_SPI, 0)
-#else
-#include <asm/cpm2.h>
-#define CPM_SPI_CMD mk_cr_cmd(CPM_CR_SPI_PAGE, CPM_CR_SPI_SBLOCK, 0, 0)
-#endif
-
-/* SPI Controller registers */
-struct mpc8xxx_spi_reg {
-	u8 res1[0x20];
-	__be32 mode;
-	__be32 event;
-	__be32 mask;
-	__be32 command;
-	__be32 transmit;
-	__be32 receive;
-};
-
-/* SPI Controller mode register definitions */
-#define	SPMODE_LOOP		(1 << 30)
-#define	SPMODE_CI_INACTIVEHIGH	(1 << 29)
-#define	SPMODE_CP_BEGIN_EDGECLK	(1 << 28)
-#define	SPMODE_DIV16		(1 << 27)
-#define	SPMODE_REV		(1 << 26)
-#define	SPMODE_MS		(1 << 25)
-#define	SPMODE_ENABLE		(1 << 24)
-#define	SPMODE_LEN(x)		((x) << 20)
-#define	SPMODE_PM(x)		((x) << 16)
-#define	SPMODE_OP		(1 << 14)
-#define	SPMODE_CG(x)		((x) << 7)
-
-/*
- * Default for SPI Mode:
- * 	SPI MODE 0 (inactive low, phase middle, MSB, 8-bit length, slow clk
- */
-#define	SPMODE_INIT_VAL (SPMODE_CI_INACTIVEHIGH | SPMODE_DIV16 | SPMODE_REV | \
-			 SPMODE_MS | SPMODE_LEN(7) | SPMODE_PM(0xf))
-
-/* SPIE register values */
-#define	SPIE_NE		0x00000200	/* Not empty */
-#define	SPIE_NF		0x00000100	/* Not full */
-
-/* SPIM register values */
-#define	SPIM_NE		0x00000200	/* Not empty */
-#define	SPIM_NF		0x00000100	/* Not full */
-
-#define	SPIE_TXB	0x00000200	/* Last char is written to tx fifo */
-#define	SPIE_RXB	0x00000100	/* Last char is written to rx buf */
-
-/* SPCOM register values */
-#define	SPCOM_STR	(1 << 23)	/* Start transmit */
-
-#define	SPI_PRAM_SIZE	0x100
-#define	SPI_MRBLR	((unsigned int)PAGE_SIZE)
-
-/* SPI Controller driver's private data. */
-struct mpc8xxx_spi {
-	struct device *dev;
-	struct mpc8xxx_spi_reg __iomem *base;
-
-	/* rx & tx bufs from the spi_transfer */
-	const void *tx;
-	void *rx;
-
-	int subblock;
-	struct spi_pram __iomem *pram;
-	struct cpm_buf_desc __iomem *tx_bd;
-	struct cpm_buf_desc __iomem *rx_bd;
-
-	struct spi_transfer *xfer_in_progress;
-
-	/* dma addresses for CPM transfers */
-	dma_addr_t tx_dma;
-	dma_addr_t rx_dma;
-	bool map_tx_dma;
-	bool map_rx_dma;
-
-	dma_addr_t dma_dummy_tx;
-	dma_addr_t dma_dummy_rx;
-
-	/* functions to deal with different sized buffers */
-	void (*get_rx) (u32 rx_data, struct mpc8xxx_spi *);
-	u32(*get_tx) (struct mpc8xxx_spi *);
-
-	unsigned int count;
-	unsigned int irq;
-
-	unsigned nsecs;		/* (clock cycle time)/2 */
-
-	u32 spibrg;		/* SPIBRG input clock */
-	u32 rx_shift;		/* RX data reg shift when in qe mode */
-	u32 tx_shift;		/* TX data reg shift when in qe mode */
-
-	unsigned int flags;
-
-	struct workqueue_struct *workqueue;
-	struct work_struct work;
-
-	struct list_head queue;
-	spinlock_t lock;
-
-	struct completion done;
-};
-
-static void *mpc8xxx_dummy_rx;
-static DEFINE_MUTEX(mpc8xxx_dummy_rx_lock);
-static int mpc8xxx_dummy_rx_refcnt;
-
-struct spi_mpc8xxx_cs {
-	/* functions to deal with different sized buffers */
-	void (*get_rx) (u32 rx_data, struct mpc8xxx_spi *);
-	u32 (*get_tx) (struct mpc8xxx_spi *);
-	u32 rx_shift;		/* RX data reg shift when in qe mode */
-	u32 tx_shift;		/* TX data reg shift when in qe mode */
-	u32 hw_mode;		/* Holds HW mode register settings */
-};
-
-static inline void mpc8xxx_spi_write_reg(__be32 __iomem *reg, u32 val)
-{
-	out_be32(reg, val);
-}
-
-static inline u32 mpc8xxx_spi_read_reg(__be32 __iomem *reg)
-{
-	return in_be32(reg);
-}
-
-#define MPC83XX_SPI_RX_BUF(type) 					  \
-static									  \
-void mpc8xxx_spi_rx_buf_##type(u32 data, struct mpc8xxx_spi *mpc8xxx_spi) \
-{									  \
-	type *rx = mpc8xxx_spi->rx;					  \
-	*rx++ = (type)(data >> mpc8xxx_spi->rx_shift);			  \
-	mpc8xxx_spi->rx = rx;						  \
-}
-
-#define MPC83XX_SPI_TX_BUF(type)				\
-static								\
-u32 mpc8xxx_spi_tx_buf_##type(struct mpc8xxx_spi *mpc8xxx_spi)	\
-{								\
-	u32 data;						\
-	const type *tx = mpc8xxx_spi->tx;			\
-	if (!tx)						\
-		return 0;					\
-	data = *tx++ << mpc8xxx_spi->tx_shift;			\
-	mpc8xxx_spi->tx = tx;					\
-	return data;						\
-}
-
-MPC83XX_SPI_RX_BUF(u8)
-MPC83XX_SPI_RX_BUF(u16)
-MPC83XX_SPI_RX_BUF(u32)
-MPC83XX_SPI_TX_BUF(u8)
-MPC83XX_SPI_TX_BUF(u16)
-MPC83XX_SPI_TX_BUF(u32)
-
-static void mpc8xxx_spi_change_mode(struct spi_device *spi)
-{
-	struct mpc8xxx_spi *mspi = spi_master_get_devdata(spi->master);
-	struct spi_mpc8xxx_cs *cs = spi->controller_state;
-	__be32 __iomem *mode = &mspi->base->mode;
-	unsigned long flags;
-
-	if (cs->hw_mode == mpc8xxx_spi_read_reg(mode))
-		return;
-
-	/* Turn off IRQs locally to minimize time that SPI is disabled. */
-	local_irq_save(flags);
-
-	/* Turn off SPI unit prior changing mode */
-	mpc8xxx_spi_write_reg(mode, cs->hw_mode & ~SPMODE_ENABLE);
-
-	/* When in CPM mode, we need to reinit tx and rx. */
-	if (mspi->flags & SPI_CPM_MODE) {
-		if (mspi->flags & SPI_QE) {
-			qe_issue_cmd(QE_INIT_TX_RX, mspi->subblock,
-				     QE_CR_PROTOCOL_UNSPECIFIED, 0);
-		} else {
-			cpm_command(CPM_SPI_CMD, CPM_CR_INIT_TRX);
-			if (mspi->flags & SPI_CPM1) {
-				out_be16(&mspi->pram->rbptr,
-					 in_be16(&mspi->pram->rbase));
-				out_be16(&mspi->pram->tbptr,
-					 in_be16(&mspi->pram->tbase));
-			}
-		}
-	}
-	mpc8xxx_spi_write_reg(mode, cs->hw_mode);
-	local_irq_restore(flags);
-}
-
-static void mpc8xxx_spi_chipselect(struct spi_device *spi, int value)
-{
-	struct mpc8xxx_spi *mpc8xxx_spi = spi_master_get_devdata(spi->master);
-	struct fsl_spi_platform_data *pdata = spi->dev.parent->platform_data;
-	bool pol = spi->mode & SPI_CS_HIGH;
-	struct spi_mpc8xxx_cs	*cs = spi->controller_state;
-
-	if (value == BITBANG_CS_INACTIVE) {
-		if (pdata->cs_control)
-			pdata->cs_control(spi, !pol);
-	}
-
-	if (value == BITBANG_CS_ACTIVE) {
-		mpc8xxx_spi->rx_shift = cs->rx_shift;
-		mpc8xxx_spi->tx_shift = cs->tx_shift;
-		mpc8xxx_spi->get_rx = cs->get_rx;
-		mpc8xxx_spi->get_tx = cs->get_tx;
-
-		mpc8xxx_spi_change_mode(spi);
-
-		if (pdata->cs_control)
-			pdata->cs_control(spi, pol);
-	}
-}
-
-static int
-mspi_apply_cpu_mode_quirks(struct spi_mpc8xxx_cs *cs,
-			   struct spi_device *spi,
-			   struct mpc8xxx_spi *mpc8xxx_spi,
-			   int bits_per_word)
-{
-	cs->rx_shift = 0;
-	cs->tx_shift = 0;
-	if (bits_per_word <= 8) {
-		cs->get_rx = mpc8xxx_spi_rx_buf_u8;
-		cs->get_tx = mpc8xxx_spi_tx_buf_u8;
-		if (mpc8xxx_spi->flags & SPI_QE_CPU_MODE) {
-			cs->rx_shift = 16;
-			cs->tx_shift = 24;
-		}
-	} else if (bits_per_word <= 16) {
-		cs->get_rx = mpc8xxx_spi_rx_buf_u16;
-		cs->get_tx = mpc8xxx_spi_tx_buf_u16;
-		if (mpc8xxx_spi->flags & SPI_QE_CPU_MODE) {
-			cs->rx_shift = 16;
-			cs->tx_shift = 16;
-		}
-	} else if (bits_per_word <= 32) {
-		cs->get_rx = mpc8xxx_spi_rx_buf_u32;
-		cs->get_tx = mpc8xxx_spi_tx_buf_u32;
-	} else
-		return -EINVAL;
-
-	if (mpc8xxx_spi->flags & SPI_QE_CPU_MODE &&
-	    spi->mode & SPI_LSB_FIRST) {
-		cs->tx_shift = 0;
-		if (bits_per_word <= 8)
-			cs->rx_shift = 8;
-		else
-			cs->rx_shift = 0;
-	}
-	mpc8xxx_spi->rx_shift = cs->rx_shift;
-	mpc8xxx_spi->tx_shift = cs->tx_shift;
-	mpc8xxx_spi->get_rx = cs->get_rx;
-	mpc8xxx_spi->get_tx = cs->get_tx;
-
-	return bits_per_word;
-}
-
-static int
-mspi_apply_qe_mode_quirks(struct spi_mpc8xxx_cs *cs,
-			  struct spi_device *spi,
-			  int bits_per_word)
-{
-	/* QE uses Little Endian for words > 8
-	 * so transform all words > 8 into 8 bits
-	 * Unfortnatly that doesn't work for LSB so
-	 * reject these for now */
-	/* Note: 32 bits word, LSB works iff
-	 * tfcr/rfcr is set to CPMFCR_GBL */
-	if (spi->mode & SPI_LSB_FIRST &&
-	    bits_per_word > 8)
-		return -EINVAL;
-	if (bits_per_word > 8)
-		return 8; /* pretend its 8 bits */
-	return bits_per_word;
-}
-
-static
-int mpc8xxx_spi_setup_transfer(struct spi_device *spi, struct spi_transfer *t)
-{
-	struct mpc8xxx_spi *mpc8xxx_spi;
-	int bits_per_word;
-	u8 pm;
-	u32 hz;
-	struct spi_mpc8xxx_cs	*cs = spi->controller_state;
-
-	mpc8xxx_spi = spi_master_get_devdata(spi->master);
-
-	if (t) {
-		bits_per_word = t->bits_per_word;
-		hz = t->speed_hz;
-	} else {
-		bits_per_word = 0;
-		hz = 0;
-	}
-
-	/* spi_transfer level calls that work per-word */
-	if (!bits_per_word)
-		bits_per_word = spi->bits_per_word;
-
-	/* Make sure its a bit width we support [4..16, 32] */
-	if ((bits_per_word < 4)
-	    || ((bits_per_word > 16) && (bits_per_word != 32)))
-		return -EINVAL;
-
-	if (!hz)
-		hz = spi->max_speed_hz;
-
-	if (!(mpc8xxx_spi->flags & SPI_CPM_MODE))
-		bits_per_word = mspi_apply_cpu_mode_quirks(cs, spi,
-							   mpc8xxx_spi,
-							   bits_per_word);
-	else if (mpc8xxx_spi->flags & SPI_QE)
-		bits_per_word = mspi_apply_qe_mode_quirks(cs, spi,
-							  bits_per_word);
-
-	if (bits_per_word < 0)
-		return bits_per_word;
-
-	if (bits_per_word == 32)
-		bits_per_word = 0;
-	else
-		bits_per_word = bits_per_word - 1;
-
-	/* mask out bits we are going to set */
-	cs->hw_mode &= ~(SPMODE_LEN(0xF) | SPMODE_DIV16
-				  | SPMODE_PM(0xF));
-
-	cs->hw_mode |= SPMODE_LEN(bits_per_word);
-
-	if ((mpc8xxx_spi->spibrg / hz) > 64) {
-		cs->hw_mode |= SPMODE_DIV16;
-		pm = (mpc8xxx_spi->spibrg - 1) / (hz * 64) + 1;
-
-		WARN_ONCE(pm > 16, "%s: Requested speed is too low: %d Hz. "
-			  "Will use %d Hz instead.\n", dev_name(&spi->dev),
-			  hz, mpc8xxx_spi->spibrg / 1024);
-		if (pm > 16)
-			pm = 16;
-	} else
-		pm = (mpc8xxx_spi->spibrg - 1) / (hz * 4) + 1;
-	if (pm)
-		pm--;
-
-	cs->hw_mode |= SPMODE_PM(pm);
-
-	mpc8xxx_spi_change_mode(spi);
-	return 0;
-}
-
-static void mpc8xxx_spi_cpm_bufs_start(struct mpc8xxx_spi *mspi)
-{
-	struct cpm_buf_desc __iomem *tx_bd = mspi->tx_bd;
-	struct cpm_buf_desc __iomem *rx_bd = mspi->rx_bd;
-	unsigned int xfer_len = min(mspi->count, SPI_MRBLR);
-	unsigned int xfer_ofs;
-
-	xfer_ofs = mspi->xfer_in_progress->len - mspi->count;
-
-	out_be32(&rx_bd->cbd_bufaddr, mspi->rx_dma + xfer_ofs);
-	out_be16(&rx_bd->cbd_datlen, 0);
-	out_be16(&rx_bd->cbd_sc, BD_SC_EMPTY | BD_SC_INTRPT | BD_SC_WRAP);
-
-	out_be32(&tx_bd->cbd_bufaddr, mspi->tx_dma + xfer_ofs);
-	out_be16(&tx_bd->cbd_datlen, xfer_len);
-	out_be16(&tx_bd->cbd_sc, BD_SC_READY | BD_SC_INTRPT | BD_SC_WRAP |
-				 BD_SC_LAST);
-
-	/* start transfer */
-	mpc8xxx_spi_write_reg(&mspi->base->command, SPCOM_STR);
-}
-
-static int mpc8xxx_spi_cpm_bufs(struct mpc8xxx_spi *mspi,
-				struct spi_transfer *t, bool is_dma_mapped)
-{
-	struct device *dev = mspi->dev;
-
-	if (is_dma_mapped) {
-		mspi->map_tx_dma = 0;
-		mspi->map_rx_dma = 0;
-	} else {
-		mspi->map_tx_dma = 1;
-		mspi->map_rx_dma = 1;
-	}
-
-	if (!t->tx_buf) {
-		mspi->tx_dma = mspi->dma_dummy_tx;
-		mspi->map_tx_dma = 0;
-	}
-
-	if (!t->rx_buf) {
-		mspi->rx_dma = mspi->dma_dummy_rx;
-		mspi->map_rx_dma = 0;
-	}
-
-	if (mspi->map_tx_dma) {
-		void *nonconst_tx = (void *)mspi->tx; /* shut up gcc */
-
-		mspi->tx_dma = dma_map_single(dev, nonconst_tx, t->len,
-					      DMA_TO_DEVICE);
-		if (dma_mapping_error(dev, mspi->tx_dma)) {
-			dev_err(dev, "unable to map tx dma\n");
-			return -ENOMEM;
-		}
-	} else if (t->tx_buf) {
-		mspi->tx_dma = t->tx_dma;
-	}
-
-	if (mspi->map_rx_dma) {
-		mspi->rx_dma = dma_map_single(dev, mspi->rx, t->len,
-					      DMA_FROM_DEVICE);
-		if (dma_mapping_error(dev, mspi->rx_dma)) {
-			dev_err(dev, "unable to map rx dma\n");
-			goto err_rx_dma;
-		}
-	} else if (t->rx_buf) {
-		mspi->rx_dma = t->rx_dma;
-	}
-
-	/* enable rx ints */
-	mpc8xxx_spi_write_reg(&mspi->base->mask, SPIE_RXB);
-
-	mspi->xfer_in_progress = t;
-	mspi->count = t->len;
-
-	/* start CPM transfers */
-	mpc8xxx_spi_cpm_bufs_start(mspi);
-
-	return 0;
-
-err_rx_dma:
-	if (mspi->map_tx_dma)
-		dma_unmap_single(dev, mspi->tx_dma, t->len, DMA_TO_DEVICE);
-	return -ENOMEM;
-}
-
-static void mpc8xxx_spi_cpm_bufs_complete(struct mpc8xxx_spi *mspi)
-{
-	struct device *dev = mspi->dev;
-	struct spi_transfer *t = mspi->xfer_in_progress;
-
-	if (mspi->map_tx_dma)
-		dma_unmap_single(dev, mspi->tx_dma, t->len, DMA_TO_DEVICE);
-	if (mspi->map_rx_dma)
-		dma_unmap_single(dev, mspi->rx_dma, t->len, DMA_FROM_DEVICE);
-	mspi->xfer_in_progress = NULL;
-}
-
-static int mpc8xxx_spi_cpu_bufs(struct mpc8xxx_spi *mspi,
-				struct spi_transfer *t, unsigned int len)
-{
-	u32 word;
-
-	mspi->count = len;
-
-	/* enable rx ints */
-	mpc8xxx_spi_write_reg(&mspi->base->mask, SPIM_NE);
-
-	/* transmit word */
-	word = mspi->get_tx(mspi);
-	mpc8xxx_spi_write_reg(&mspi->base->transmit, word);
-
-	return 0;
-}
-
-static int mpc8xxx_spi_bufs(struct spi_device *spi, struct spi_transfer *t,
-			    bool is_dma_mapped)
-{
-	struct mpc8xxx_spi *mpc8xxx_spi = spi_master_get_devdata(spi->master);
-	unsigned int len = t->len;
-	u8 bits_per_word;
-	int ret;
-
-	bits_per_word = spi->bits_per_word;
-	if (t->bits_per_word)
-		bits_per_word = t->bits_per_word;
-
-	if (bits_per_word > 8) {
-		/* invalid length? */
-		if (len & 1)
-			return -EINVAL;
-		len /= 2;
-	}
-	if (bits_per_word > 16) {
-		/* invalid length? */
-		if (len & 1)
-			return -EINVAL;
-		len /= 2;
-	}
-
-	mpc8xxx_spi->tx = t->tx_buf;
-	mpc8xxx_spi->rx = t->rx_buf;
-
-	INIT_COMPLETION(mpc8xxx_spi->done);
-
-	if (mpc8xxx_spi->flags & SPI_CPM_MODE)
-		ret = mpc8xxx_spi_cpm_bufs(mpc8xxx_spi, t, is_dma_mapped);
-	else
-		ret = mpc8xxx_spi_cpu_bufs(mpc8xxx_spi, t, len);
-	if (ret)
-		return ret;
-
-	wait_for_completion(&mpc8xxx_spi->done);
-
-	/* disable rx ints */
-	mpc8xxx_spi_write_reg(&mpc8xxx_spi->base->mask, 0);
-
-	if (mpc8xxx_spi->flags & SPI_CPM_MODE)
-		mpc8xxx_spi_cpm_bufs_complete(mpc8xxx_spi);
-
-	return mpc8xxx_spi->count;
-}
-
-static void mpc8xxx_spi_do_one_msg(struct spi_message *m)
-{
-	struct spi_device *spi = m->spi;
-	struct spi_transfer *t;
-	unsigned int cs_change;
-	const int nsecs = 50;
-	int status;
-
-	cs_change = 1;
-	status = 0;
-	list_for_each_entry(t, &m->transfers, transfer_list) {
-		if (t->bits_per_word || t->speed_hz) {
-			/* Don't allow changes if CS is active */
-			status = -EINVAL;
-
-			if (cs_change)
-				status = mpc8xxx_spi_setup_transfer(spi, t);
-			if (status < 0)
-				break;
-		}
-
-		if (cs_change) {
-			mpc8xxx_spi_chipselect(spi, BITBANG_CS_ACTIVE);
-			ndelay(nsecs);
-		}
-		cs_change = t->cs_change;
-		if (t->len)
-			status = mpc8xxx_spi_bufs(spi, t, m->is_dma_mapped);
-		if (status) {
-			status = -EMSGSIZE;
-			break;
-		}
-		m->actual_length += t->len;
-
-		if (t->delay_usecs)
-			udelay(t->delay_usecs);
-
-		if (cs_change) {
-			ndelay(nsecs);
-			mpc8xxx_spi_chipselect(spi, BITBANG_CS_INACTIVE);
-			ndelay(nsecs);
-		}
-	}
-
-	m->status = status;
-	m->complete(m->context);
-
-	if (status || !cs_change) {
-		ndelay(nsecs);
-		mpc8xxx_spi_chipselect(spi, BITBANG_CS_INACTIVE);
-	}
-
-	mpc8xxx_spi_setup_transfer(spi, NULL);
-}
-
-static void mpc8xxx_spi_work(struct work_struct *work)
-{
-	struct mpc8xxx_spi *mpc8xxx_spi = container_of(work, struct mpc8xxx_spi,
-						       work);
-
-	spin_lock_irq(&mpc8xxx_spi->lock);
-	while (!list_empty(&mpc8xxx_spi->queue)) {
-		struct spi_message *m = container_of(mpc8xxx_spi->queue.next,
-						   struct spi_message, queue);
-
-		list_del_init(&m->queue);
-		spin_unlock_irq(&mpc8xxx_spi->lock);
-
-		mpc8xxx_spi_do_one_msg(m);
-
-		spin_lock_irq(&mpc8xxx_spi->lock);
-	}
-	spin_unlock_irq(&mpc8xxx_spi->lock);
-}
-
-static int mpc8xxx_spi_setup(struct spi_device *spi)
-{
-	struct mpc8xxx_spi *mpc8xxx_spi;
-	int retval;
-	u32 hw_mode;
-	struct spi_mpc8xxx_cs	*cs = spi->controller_state;
-
-	if (!spi->max_speed_hz)
-		return -EINVAL;
-
-	if (!cs) {
-		cs = kzalloc(sizeof *cs, GFP_KERNEL);
-		if (!cs)
-			return -ENOMEM;
-		spi->controller_state = cs;
-	}
-	mpc8xxx_spi = spi_master_get_devdata(spi->master);
-
-	hw_mode = cs->hw_mode; /* Save original settings */
-	cs->hw_mode = mpc8xxx_spi_read_reg(&mpc8xxx_spi->base->mode);
-	/* mask out bits we are going to set */
-	cs->hw_mode &= ~(SPMODE_CP_BEGIN_EDGECLK | SPMODE_CI_INACTIVEHIGH
-			 | SPMODE_REV | SPMODE_LOOP);
-
-	if (spi->mode & SPI_CPHA)
-		cs->hw_mode |= SPMODE_CP_BEGIN_EDGECLK;
-	if (spi->mode & SPI_CPOL)
-		cs->hw_mode |= SPMODE_CI_INACTIVEHIGH;
-	if (!(spi->mode & SPI_LSB_FIRST))
-		cs->hw_mode |= SPMODE_REV;
-	if (spi->mode & SPI_LOOP)
-		cs->hw_mode |= SPMODE_LOOP;
-
-	retval = mpc8xxx_spi_setup_transfer(spi, NULL);
-	if (retval < 0) {
-		cs->hw_mode = hw_mode; /* Restore settings */
-		return retval;
-	}
-	return 0;
-}
-
-static void mpc8xxx_spi_cpm_irq(struct mpc8xxx_spi *mspi, u32 events)
-{
-	u16 len;
-
-	dev_dbg(mspi->dev, "%s: bd datlen %d, count %d\n", __func__,
-		in_be16(&mspi->rx_bd->cbd_datlen), mspi->count);
-
-	len = in_be16(&mspi->rx_bd->cbd_datlen);
-	if (len > mspi->count) {
-		WARN_ON(1);
-		len = mspi->count;
-	}
-
-	/* Clear the events */
-	mpc8xxx_spi_write_reg(&mspi->base->event, events);
-
-	mspi->count -= len;
-	if (mspi->count)
-		mpc8xxx_spi_cpm_bufs_start(mspi);
-	else
-		complete(&mspi->done);
-}
-
-static void mpc8xxx_spi_cpu_irq(struct mpc8xxx_spi *mspi, u32 events)
-{
-	/* We need handle RX first */
-	if (events & SPIE_NE) {
-		u32 rx_data = mpc8xxx_spi_read_reg(&mspi->base->receive);
-
-		if (mspi->rx)
-			mspi->get_rx(rx_data, mspi);
-	}
-
-	if ((events & SPIE_NF) == 0)
-		/* spin until TX is done */
-		while (((events =
-			mpc8xxx_spi_read_reg(&mspi->base->event)) &
-						SPIE_NF) == 0)
-			cpu_relax();
-
-	/* Clear the events */
-	mpc8xxx_spi_write_reg(&mspi->base->event, events);
-
-	mspi->count -= 1;
-	if (mspi->count) {
-		u32 word = mspi->get_tx(mspi);
-
-		mpc8xxx_spi_write_reg(&mspi->base->transmit, word);
-	} else {
-		complete(&mspi->done);
-	}
-}
-
-static irqreturn_t mpc8xxx_spi_irq(s32 irq, void *context_data)
-{
-	struct mpc8xxx_spi *mspi = context_data;
-	irqreturn_t ret = IRQ_NONE;
-	u32 events;
-
-	/* Get interrupt events(tx/rx) */
-	events = mpc8xxx_spi_read_reg(&mspi->base->event);
-	if (events)
-		ret = IRQ_HANDLED;
-
-	dev_dbg(mspi->dev, "%s: events %x\n", __func__, events);
-
-	if (mspi->flags & SPI_CPM_MODE)
-		mpc8xxx_spi_cpm_irq(mspi, events);
-	else
-		mpc8xxx_spi_cpu_irq(mspi, events);
-
-	return ret;
-}
-
-static int mpc8xxx_spi_transfer(struct spi_device *spi,
-				struct spi_message *m)
-{
-	struct mpc8xxx_spi *mpc8xxx_spi = spi_master_get_devdata(spi->master);
-	unsigned long flags;
-
-	m->actual_length = 0;
-	m->status = -EINPROGRESS;
-
-	spin_lock_irqsave(&mpc8xxx_spi->lock, flags);
-	list_add_tail(&m->queue, &mpc8xxx_spi->queue);
-	queue_work(mpc8xxx_spi->workqueue, &mpc8xxx_spi->work);
-	spin_unlock_irqrestore(&mpc8xxx_spi->lock, flags);
-
-	return 0;
-}
-
-
-static void mpc8xxx_spi_cleanup(struct spi_device *spi)
-{
-	kfree(spi->controller_state);
-}
-
-static void *mpc8xxx_spi_alloc_dummy_rx(void)
-{
-	mutex_lock(&mpc8xxx_dummy_rx_lock);
-
-	if (!mpc8xxx_dummy_rx)
-		mpc8xxx_dummy_rx = kmalloc(SPI_MRBLR, GFP_KERNEL);
-	if (mpc8xxx_dummy_rx)
-		mpc8xxx_dummy_rx_refcnt++;
-
-	mutex_unlock(&mpc8xxx_dummy_rx_lock);
-
-	return mpc8xxx_dummy_rx;
-}
-
-static void mpc8xxx_spi_free_dummy_rx(void)
-{
-	mutex_lock(&mpc8xxx_dummy_rx_lock);
-
-	switch (mpc8xxx_dummy_rx_refcnt) {
-	case 0:
-		WARN_ON(1);
-		break;
-	case 1:
-		kfree(mpc8xxx_dummy_rx);
-		mpc8xxx_dummy_rx = NULL;
-		/* fall through */
-	default:
-		mpc8xxx_dummy_rx_refcnt--;
-		break;
-	}
-
-	mutex_unlock(&mpc8xxx_dummy_rx_lock);
-}
-
-static unsigned long mpc8xxx_spi_cpm_get_pram(struct mpc8xxx_spi *mspi)
-{
-	struct device *dev = mspi->dev;
-	struct device_node *np = dev->of_node;
-	const u32 *iprop;
-	int size;
-	unsigned long spi_base_ofs;
-	unsigned long pram_ofs = -ENOMEM;
-
-	/* Can't use of_address_to_resource(), QE muram isn't at 0. */
-	iprop = of_get_property(np, "reg", &size);
-
-	/* QE with a fixed pram location? */
-	if (mspi->flags & SPI_QE && iprop && size == sizeof(*iprop) * 4)
-		return cpm_muram_alloc_fixed(iprop[2], SPI_PRAM_SIZE);
-
-	/* QE but with a dynamic pram location? */
-	if (mspi->flags & SPI_QE) {
-		pram_ofs = cpm_muram_alloc(SPI_PRAM_SIZE, 64);
-		qe_issue_cmd(QE_ASSIGN_PAGE_TO_DEVICE, mspi->subblock,
-				QE_CR_PROTOCOL_UNSPECIFIED, pram_ofs);
-		return pram_ofs;
-	}
-
-	/* CPM1 and CPM2 pram must be at a fixed addr. */
-	if (!iprop || size != sizeof(*iprop) * 4)
-		return -ENOMEM;
-
-	spi_base_ofs = cpm_muram_alloc_fixed(iprop[2], 2);
-	if (IS_ERR_VALUE(spi_base_ofs))
-		return -ENOMEM;
-
-	if (mspi->flags & SPI_CPM2) {
-		pram_ofs = cpm_muram_alloc(SPI_PRAM_SIZE, 64);
-		if (!IS_ERR_VALUE(pram_ofs)) {
-			u16 __iomem *spi_base = cpm_muram_addr(spi_base_ofs);
-
-			out_be16(spi_base, pram_ofs);
-		}
-	} else {
-		struct spi_pram __iomem *pram = cpm_muram_addr(spi_base_ofs);
-		u16 rpbase = in_be16(&pram->rpbase);
-
-		/* Microcode relocation patch applied? */
-		if (rpbase)
-			pram_ofs = rpbase;
-		else
-			return spi_base_ofs;
-	}
-
-	cpm_muram_free(spi_base_ofs);
-	return pram_ofs;
-}
-
-static int mpc8xxx_spi_cpm_init(struct mpc8xxx_spi *mspi)
-{
-	struct device *dev = mspi->dev;
-	struct device_node *np = dev->of_node;
-	const u32 *iprop;
-	int size;
-	unsigned long pram_ofs;
-	unsigned long bds_ofs;
-
-	if (!(mspi->flags & SPI_CPM_MODE))
-		return 0;
-
-	if (!mpc8xxx_spi_alloc_dummy_rx())
-		return -ENOMEM;
-
-	if (mspi->flags & SPI_QE) {
-		iprop = of_get_property(np, "cell-index", &size);
-		if (iprop && size == sizeof(*iprop))
-			mspi->subblock = *iprop;
-
-		switch (mspi->subblock) {
-		default:
-			dev_warn(dev, "cell-index unspecified, assuming SPI1");
-			/* fall through */
-		case 0:
-			mspi->subblock = QE_CR_SUBBLOCK_SPI1;
-			break;
-		case 1:
-			mspi->subblock = QE_CR_SUBBLOCK_SPI2;
-			break;
-		}
-	}
-
-	pram_ofs = mpc8xxx_spi_cpm_get_pram(mspi);
-	if (IS_ERR_VALUE(pram_ofs)) {
-		dev_err(dev, "can't allocate spi parameter ram\n");
-		goto err_pram;
-	}
-
-	bds_ofs = cpm_muram_alloc(sizeof(*mspi->tx_bd) +
-				  sizeof(*mspi->rx_bd), 8);
-	if (IS_ERR_VALUE(bds_ofs)) {
-		dev_err(dev, "can't allocate bds\n");
-		goto err_bds;
-	}
-
-	mspi->dma_dummy_tx = dma_map_single(dev, empty_zero_page, PAGE_SIZE,
-					    DMA_TO_DEVICE);
-	if (dma_mapping_error(dev, mspi->dma_dummy_tx)) {
-		dev_err(dev, "unable to map dummy tx buffer\n");
-		goto err_dummy_tx;
-	}
-
-	mspi->dma_dummy_rx = dma_map_single(dev, mpc8xxx_dummy_rx, SPI_MRBLR,
-					    DMA_FROM_DEVICE);
-	if (dma_mapping_error(dev, mspi->dma_dummy_rx)) {
-		dev_err(dev, "unable to map dummy rx buffer\n");
-		goto err_dummy_rx;
-	}
-
-	mspi->pram = cpm_muram_addr(pram_ofs);
-
-	mspi->tx_bd = cpm_muram_addr(bds_ofs);
-	mspi->rx_bd = cpm_muram_addr(bds_ofs + sizeof(*mspi->tx_bd));
-
-	/* Initialize parameter ram. */
-	out_be16(&mspi->pram->tbase, cpm_muram_offset(mspi->tx_bd));
-	out_be16(&mspi->pram->rbase, cpm_muram_offset(mspi->rx_bd));
-	out_8(&mspi->pram->tfcr, CPMFCR_EB | CPMFCR_GBL);
-	out_8(&mspi->pram->rfcr, CPMFCR_EB | CPMFCR_GBL);
-	out_be16(&mspi->pram->mrblr, SPI_MRBLR);
-	out_be32(&mspi->pram->rstate, 0);
-	out_be32(&mspi->pram->rdp, 0);
-	out_be16(&mspi->pram->rbptr, 0);
-	out_be16(&mspi->pram->rbc, 0);
-	out_be32(&mspi->pram->rxtmp, 0);
-	out_be32(&mspi->pram->tstate, 0);
-	out_be32(&mspi->pram->tdp, 0);
-	out_be16(&mspi->pram->tbptr, 0);
-	out_be16(&mspi->pram->tbc, 0);
-	out_be32(&mspi->pram->txtmp, 0);
-
-	return 0;
-
-err_dummy_rx:
-	dma_unmap_single(dev, mspi->dma_dummy_tx, PAGE_SIZE, DMA_TO_DEVICE);
-err_dummy_tx:
-	cpm_muram_free(bds_ofs);
-err_bds:
-	cpm_muram_free(pram_ofs);
-err_pram:
-	mpc8xxx_spi_free_dummy_rx();
-	return -ENOMEM;
-}
-
-static void mpc8xxx_spi_cpm_free(struct mpc8xxx_spi *mspi)
-{
-	struct device *dev = mspi->dev;
-
-	dma_unmap_single(dev, mspi->dma_dummy_rx, SPI_MRBLR, DMA_FROM_DEVICE);
-	dma_unmap_single(dev, mspi->dma_dummy_tx, PAGE_SIZE, DMA_TO_DEVICE);
-	cpm_muram_free(cpm_muram_offset(mspi->tx_bd));
-	cpm_muram_free(cpm_muram_offset(mspi->pram));
-	mpc8xxx_spi_free_dummy_rx();
-}
-
-static const char *mpc8xxx_spi_strmode(unsigned int flags)
-{
-	if (flags & SPI_QE_CPU_MODE) {
-		return "QE CPU";
-	} else if (flags & SPI_CPM_MODE) {
-		if (flags & SPI_QE)
-			return "QE";
-		else if (flags & SPI_CPM2)
-			return "CPM2";
-		else
-			return "CPM1";
-	}
-	return "CPU";
-}
-
-static struct spi_master * __devinit
-mpc8xxx_spi_probe(struct device *dev, struct resource *mem, unsigned int irq)
-{
-	struct fsl_spi_platform_data *pdata = dev->platform_data;
-	struct spi_master *master;
-	struct mpc8xxx_spi *mpc8xxx_spi;
-	u32 regval;
-	int ret = 0;
-
-	master = spi_alloc_master(dev, sizeof(struct mpc8xxx_spi));
-	if (master == NULL) {
-		ret = -ENOMEM;
-		goto err;
-	}
-
-	dev_set_drvdata(dev, master);
-
-	/* the spi->mode bits understood by this driver: */
-	master->mode_bits = SPI_CPOL | SPI_CPHA | SPI_CS_HIGH
-			| SPI_LSB_FIRST | SPI_LOOP;
-
-	master->setup = mpc8xxx_spi_setup;
-	master->transfer = mpc8xxx_spi_transfer;
-	master->cleanup = mpc8xxx_spi_cleanup;
-	master->dev.of_node = dev->of_node;
-
-	mpc8xxx_spi = spi_master_get_devdata(master);
-	mpc8xxx_spi->dev = dev;
-	mpc8xxx_spi->get_rx = mpc8xxx_spi_rx_buf_u8;
-	mpc8xxx_spi->get_tx = mpc8xxx_spi_tx_buf_u8;
-	mpc8xxx_spi->flags = pdata->flags;
-	mpc8xxx_spi->spibrg = pdata->sysclk;
-
-	ret = mpc8xxx_spi_cpm_init(mpc8xxx_spi);
-	if (ret)
-		goto err_cpm_init;
-
-	mpc8xxx_spi->rx_shift = 0;
-	mpc8xxx_spi->tx_shift = 0;
-	if (mpc8xxx_spi->flags & SPI_QE_CPU_MODE) {
-		mpc8xxx_spi->rx_shift = 16;
-		mpc8xxx_spi->tx_shift = 24;
-	}
-
-	init_completion(&mpc8xxx_spi->done);
-
-	mpc8xxx_spi->base = ioremap(mem->start, resource_size(mem));
-	if (mpc8xxx_spi->base == NULL) {
-		ret = -ENOMEM;
-		goto err_ioremap;
-	}
-
-	mpc8xxx_spi->irq = irq;
-
-	/* Register for SPI Interrupt */
-	ret = request_irq(mpc8xxx_spi->irq, mpc8xxx_spi_irq,
-			  0, "mpc8xxx_spi", mpc8xxx_spi);
-
-	if (ret != 0)
-		goto unmap_io;
-
-	master->bus_num = pdata->bus_num;
-	master->num_chipselect = pdata->max_chipselect;
-
-	/* SPI controller initializations */
-	mpc8xxx_spi_write_reg(&mpc8xxx_spi->base->mode, 0);
-	mpc8xxx_spi_write_reg(&mpc8xxx_spi->base->mask, 0);
-	mpc8xxx_spi_write_reg(&mpc8xxx_spi->base->command, 0);
-	mpc8xxx_spi_write_reg(&mpc8xxx_spi->base->event, 0xffffffff);
-
-	/* Enable SPI interface */
-	regval = pdata->initial_spmode | SPMODE_INIT_VAL | SPMODE_ENABLE;
-	if (mpc8xxx_spi->flags & SPI_QE_CPU_MODE)
-		regval |= SPMODE_OP;
-
-	mpc8xxx_spi_write_reg(&mpc8xxx_spi->base->mode, regval);
-	spin_lock_init(&mpc8xxx_spi->lock);
-	init_completion(&mpc8xxx_spi->done);
-	INIT_WORK(&mpc8xxx_spi->work, mpc8xxx_spi_work);
-	INIT_LIST_HEAD(&mpc8xxx_spi->queue);
-
-	mpc8xxx_spi->workqueue = create_singlethread_workqueue(
-		dev_name(master->dev.parent));
-	if (mpc8xxx_spi->workqueue == NULL) {
-		ret = -EBUSY;
-		goto free_irq;
-	}
-
-	ret = spi_register_master(master);
-	if (ret < 0)
-		goto unreg_master;
-
-	dev_info(dev, "at 0x%p (irq = %d), %s mode\n", mpc8xxx_spi->base,
-		 mpc8xxx_spi->irq, mpc8xxx_spi_strmode(mpc8xxx_spi->flags));
-
-	return master;
-
-unreg_master:
-	destroy_workqueue(mpc8xxx_spi->workqueue);
-free_irq:
-	free_irq(mpc8xxx_spi->irq, mpc8xxx_spi);
-unmap_io:
-	iounmap(mpc8xxx_spi->base);
-err_ioremap:
-	mpc8xxx_spi_cpm_free(mpc8xxx_spi);
-err_cpm_init:
-	spi_master_put(master);
-err:
-	return ERR_PTR(ret);
-}
-
-static int __devexit mpc8xxx_spi_remove(struct device *dev)
-{
-	struct mpc8xxx_spi *mpc8xxx_spi;
-	struct spi_master *master;
-
-	master = dev_get_drvdata(dev);
-	mpc8xxx_spi = spi_master_get_devdata(master);
-
-	flush_workqueue(mpc8xxx_spi->workqueue);
-	destroy_workqueue(mpc8xxx_spi->workqueue);
-	spi_unregister_master(master);
-
-	free_irq(mpc8xxx_spi->irq, mpc8xxx_spi);
-	iounmap(mpc8xxx_spi->base);
-	mpc8xxx_spi_cpm_free(mpc8xxx_spi);
-
-	return 0;
-}
-
-struct mpc8xxx_spi_probe_info {
-	struct fsl_spi_platform_data pdata;
-	int *gpios;
-	bool *alow_flags;
-};
-
-static struct mpc8xxx_spi_probe_info *
-to_of_pinfo(struct fsl_spi_platform_data *pdata)
-{
-	return container_of(pdata, struct mpc8xxx_spi_probe_info, pdata);
-}
-
-static void mpc8xxx_spi_cs_control(struct spi_device *spi, bool on)
-{
-	struct device *dev = spi->dev.parent;
-	struct mpc8xxx_spi_probe_info *pinfo = to_of_pinfo(dev->platform_data);
-	u16 cs = spi->chip_select;
-	int gpio = pinfo->gpios[cs];
-	bool alow = pinfo->alow_flags[cs];
-
-	gpio_set_value(gpio, on ^ alow);
-}
-
-static int of_mpc8xxx_spi_get_chipselects(struct device *dev)
-{
-	struct device_node *np = dev->of_node;
-	struct fsl_spi_platform_data *pdata = dev->platform_data;
-	struct mpc8xxx_spi_probe_info *pinfo = to_of_pinfo(pdata);
-	unsigned int ngpios;
-	int i = 0;
-	int ret;
-
-	ngpios = of_gpio_count(np);
-	if (!ngpios) {
-		/*
-		 * SPI w/o chip-select line. One SPI device is still permitted
-		 * though.
-		 */
-		pdata->max_chipselect = 1;
-		return 0;
-	}
-
-	pinfo->gpios = kmalloc(ngpios * sizeof(*pinfo->gpios), GFP_KERNEL);
-	if (!pinfo->gpios)
-		return -ENOMEM;
-	memset(pinfo->gpios, -1, ngpios * sizeof(*pinfo->gpios));
-
-	pinfo->alow_flags = kzalloc(ngpios * sizeof(*pinfo->alow_flags),
-				    GFP_KERNEL);
-	if (!pinfo->alow_flags) {
-		ret = -ENOMEM;
-		goto err_alloc_flags;
-	}
-
-	for (; i < ngpios; i++) {
-		int gpio;
-		enum of_gpio_flags flags;
-
-		gpio = of_get_gpio_flags(np, i, &flags);
-		if (!gpio_is_valid(gpio)) {
-			dev_err(dev, "invalid gpio #%d: %d\n", i, gpio);
-			ret = gpio;
-			goto err_loop;
-		}
-
-		ret = gpio_request(gpio, dev_name(dev));
-		if (ret) {
-			dev_err(dev, "can't request gpio #%d: %d\n", i, ret);
-			goto err_loop;
-		}
-
-		pinfo->gpios[i] = gpio;
-		pinfo->alow_flags[i] = flags & OF_GPIO_ACTIVE_LOW;
-
-		ret = gpio_direction_output(pinfo->gpios[i],
-					    pinfo->alow_flags[i]);
-		if (ret) {
-			dev_err(dev, "can't set output direction for gpio "
-				"#%d: %d\n", i, ret);
-			goto err_loop;
-		}
-	}
-
-	pdata->max_chipselect = ngpios;
-	pdata->cs_control = mpc8xxx_spi_cs_control;
-
-	return 0;
-
-err_loop:
-	while (i >= 0) {
-		if (gpio_is_valid(pinfo->gpios[i]))
-			gpio_free(pinfo->gpios[i]);
-		i--;
-	}
-
-	kfree(pinfo->alow_flags);
-	pinfo->alow_flags = NULL;
-err_alloc_flags:
-	kfree(pinfo->gpios);
-	pinfo->gpios = NULL;
-	return ret;
-}
-
-static int of_mpc8xxx_spi_free_chipselects(struct device *dev)
-{
-	struct fsl_spi_platform_data *pdata = dev->platform_data;
-	struct mpc8xxx_spi_probe_info *pinfo = to_of_pinfo(pdata);
-	int i;
-
-	if (!pinfo->gpios)
-		return 0;
-
-	for (i = 0; i < pdata->max_chipselect; i++) {
-		if (gpio_is_valid(pinfo->gpios[i]))
-			gpio_free(pinfo->gpios[i]);
-	}
-
-	kfree(pinfo->gpios);
-	kfree(pinfo->alow_flags);
-	return 0;
-}
-
-static int __devinit of_mpc8xxx_spi_probe(struct platform_device *ofdev,
-					  const struct of_device_id *ofid)
-{
-	struct device *dev = &ofdev->dev;
-	struct device_node *np = ofdev->dev.of_node;
-	struct mpc8xxx_spi_probe_info *pinfo;
-	struct fsl_spi_platform_data *pdata;
-	struct spi_master *master;
-	struct resource mem;
-	struct resource irq;
-	const void *prop;
-	int ret = -ENOMEM;
-
-	pinfo = kzalloc(sizeof(*pinfo), GFP_KERNEL);
-	if (!pinfo)
-		return -ENOMEM;
-
-	pdata = &pinfo->pdata;
-	dev->platform_data = pdata;
-
-	/* Allocate bus num dynamically. */
-	pdata->bus_num = -1;
-
-	/* SPI controller is either clocked from QE or SoC clock. */
-	pdata->sysclk = get_brgfreq();
-	if (pdata->sysclk == -1) {
-		pdata->sysclk = fsl_get_sys_freq();
-		if (pdata->sysclk == -1) {
-			ret = -ENODEV;
-			goto err_clk;
-		}
-	}
-
-	prop = of_get_property(np, "mode", NULL);
-	if (prop && !strcmp(prop, "cpu-qe"))
-		pdata->flags = SPI_QE_CPU_MODE;
-	else if (prop && !strcmp(prop, "qe"))
-		pdata->flags = SPI_CPM_MODE | SPI_QE;
-	else if (of_device_is_compatible(np, "fsl,cpm2-spi"))
-		pdata->flags = SPI_CPM_MODE | SPI_CPM2;
-	else if (of_device_is_compatible(np, "fsl,cpm1-spi"))
-		pdata->flags = SPI_CPM_MODE | SPI_CPM1;
-
-	ret = of_mpc8xxx_spi_get_chipselects(dev);
-	if (ret)
-		goto err;
-
-	ret = of_address_to_resource(np, 0, &mem);
-	if (ret)
-		goto err;
-
-	ret = of_irq_to_resource(np, 0, &irq);
-	if (!ret) {
-		ret = -EINVAL;
-		goto err;
-	}
-
-	master = mpc8xxx_spi_probe(dev, &mem, irq.start);
-	if (IS_ERR(master)) {
-		ret = PTR_ERR(master);
-		goto err;
-	}
-
-	return 0;
-
-err:
-	of_mpc8xxx_spi_free_chipselects(dev);
-err_clk:
-	kfree(pinfo);
-	return ret;
-}
-
-static int __devexit of_mpc8xxx_spi_remove(struct platform_device *ofdev)
-{
-	int ret;
-
-	ret = mpc8xxx_spi_remove(&ofdev->dev);
-	if (ret)
-		return ret;
-	of_mpc8xxx_spi_free_chipselects(&ofdev->dev);
-	return 0;
-}
-
-static const struct of_device_id of_mpc8xxx_spi_match[] = {
-	{ .compatible = "fsl,spi" },
-	{},
-};
-MODULE_DEVICE_TABLE(of, of_mpc8xxx_spi_match);
-
-static struct of_platform_driver of_mpc8xxx_spi_driver = {
-	.driver = {
-		.name = "mpc8xxx_spi",
-		.owner = THIS_MODULE,
-		.of_match_table = of_mpc8xxx_spi_match,
-	},
-	.probe		= of_mpc8xxx_spi_probe,
-	.remove		= __devexit_p(of_mpc8xxx_spi_remove),
-};
-
-#ifdef CONFIG_MPC832x_RDB
-/*
- * 				XXX XXX XXX
- * This is "legacy" platform driver, was used by the MPC8323E-RDB boards
- * only. The driver should go away soon, since newer MPC8323E-RDB's device
- * tree can work with OpenFirmware driver. But for now we support old trees
- * as well.
- */
-static int __devinit plat_mpc8xxx_spi_probe(struct platform_device *pdev)
-{
-	struct resource *mem;
-	int irq;
-	struct spi_master *master;
-
-	if (!pdev->dev.platform_data)
-		return -EINVAL;
-
-	mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
-	if (!mem)
-		return -EINVAL;
-
-	irq = platform_get_irq(pdev, 0);
-	if (irq <= 0)
-		return -EINVAL;
-
-	master = mpc8xxx_spi_probe(&pdev->dev, mem, irq);
-	if (IS_ERR(master))
-		return PTR_ERR(master);
-	return 0;
-}
-
-static int __devexit plat_mpc8xxx_spi_remove(struct platform_device *pdev)
-{
-	return mpc8xxx_spi_remove(&pdev->dev);
-}
-
-MODULE_ALIAS("platform:mpc8xxx_spi");
-static struct platform_driver mpc8xxx_spi_driver = {
-	.probe = plat_mpc8xxx_spi_probe,
-	.remove = __devexit_p(plat_mpc8xxx_spi_remove),
-	.driver = {
-		.name = "mpc8xxx_spi",
-		.owner = THIS_MODULE,
-	},
-};
-
-static bool legacy_driver_failed;
-
-static void __init legacy_driver_register(void)
-{
-	legacy_driver_failed = platform_driver_register(&mpc8xxx_spi_driver);
-}
-
-static void __exit legacy_driver_unregister(void)
-{
-	if (legacy_driver_failed)
-		return;
-	platform_driver_unregister(&mpc8xxx_spi_driver);
-}
-#else
-static void __init legacy_driver_register(void) {}
-static void __exit legacy_driver_unregister(void) {}
-#endif /* CONFIG_MPC832x_RDB */
-
-static int __init mpc8xxx_spi_init(void)
-{
-	legacy_driver_register();
-	return of_register_platform_driver(&of_mpc8xxx_spi_driver);
-}
-
-static void __exit mpc8xxx_spi_exit(void)
-{
-	of_unregister_platform_driver(&of_mpc8xxx_spi_driver);
-	legacy_driver_unregister();
-}
-
-module_init(mpc8xxx_spi_init);
-module_exit(mpc8xxx_spi_exit);
-
-MODULE_AUTHOR("Kumar Gala");
-MODULE_DESCRIPTION("Simple MPC8xxx SPI Driver");
-MODULE_LICENSE("GPL");
-- 
1.6.4

^ permalink raw reply related

* [PATCH v3 4/7] powerpc/of: add eSPI controller dts bindings and DTS modification
From: Mingkai Hu @ 2010-09-30  8:00 UTC (permalink / raw)
  To: linuxppc-dev, spi-devel-general, linux-mtd; +Cc: kumar.gala, Mingkai Hu
In-Reply-To: <1285833646-12006-4-git-send-email-Mingkai.hu@freescale.com>

Also modifiy the document of cell-index in SPI controller. Add the
SPI flash(s25fl128p01) support on p4080ds and mpc8536ds board.

Signed-off-by: Mingkai Hu <Mingkai.hu@freescale.com>
---
v3:
 - Add "fsl,p4080-espi" compatible property.

 Documentation/powerpc/dts-bindings/fsl/spi.txt |   24 ++++++++++-
 arch/powerpc/boot/dts/mpc8536ds.dts            |   52 ++++++++++++++++++++++++
 arch/powerpc/boot/dts/p4080ds.dts              |   11 ++---
 3 files changed, 79 insertions(+), 8 deletions(-)

diff --git a/Documentation/powerpc/dts-bindings/fsl/spi.txt b/Documentation/powerpc/dts-bindings/fsl/spi.txt
index 80510c0..777abd7 100644
--- a/Documentation/powerpc/dts-bindings/fsl/spi.txt
+++ b/Documentation/powerpc/dts-bindings/fsl/spi.txt
@@ -1,7 +1,9 @@
 * SPI (Serial Peripheral Interface)
 
 Required properties:
-- cell-index : SPI controller index.
+- cell-index : QE SPI subblock index.
+		0: QE subblock SPI1
+		1: QE subblock SPI2
 - compatible : should be "fsl,spi".
 - mode : the SPI operation mode, it can be "cpu" or "cpu-qe".
 - reg : Offset and length of the register set for the device
@@ -29,3 +31,23 @@ Example:
 		gpios = <&gpio 18 1	// device reg=<0>
 			 &gpio 19 1>;	// device reg=<1>
 	};
+
+
+* eSPI (Enhanced Serial Peripheral Interface)
+
+Required properties:
+- compatible : should be "fsl,mpc8536-espi".
+- reg : Offset and length of the register set for the device.
+- interrupts : should contain eSPI interrupt, the device has one interrupt.
+- fsl,espi-num-chipselects : the number of the chipselect signals.
+
+Example:
+	spi@110000 {
+		#address-cells = <1>;
+		#size-cells = <0>;
+		compatible = "fsl,mpc8536-espi";
+		reg = <0x110000 0x1000>;
+		interrupts = <53 0x2>;
+		interrupt-parent = <&mpic>;
+		fsl,espi-num-chipselects = <4>;
+	};
diff --git a/arch/powerpc/boot/dts/mpc8536ds.dts b/arch/powerpc/boot/dts/mpc8536ds.dts
index 815cebb..a75c10e 100644
--- a/arch/powerpc/boot/dts/mpc8536ds.dts
+++ b/arch/powerpc/boot/dts/mpc8536ds.dts
@@ -108,6 +108,58 @@
 			};
 		};
 
+		spi@7000 {
+			#address-cells = <1>;
+			#size-cells = <0>;
+			compatible = "fsl,mpc8536-espi";
+			reg = <0x7000 0x1000>;
+			interrupts = <59 0x2>;
+			interrupt-parent = <&mpic>;
+			fsl,espi-num-chipselects = <4>;
+
+			flash@0 {
+				#address-cells = <1>;
+				#size-cells = <1>;
+				compatible = "spansion,s25sl12801";
+				reg = <0>;
+				spi-max-frequency = <40000000>;
+				partition@u-boot {
+					label = "u-boot";
+					reg = <0x00000000 0x00100000>;
+					read-only;
+				};
+				partition@kernel {
+					label = "kernel";
+					reg = <0x00100000 0x00500000>;
+					read-only;
+				};
+				partition@dtb {
+					label = "dtb";
+					reg = <0x00600000 0x00100000>;
+					read-only;
+				};
+				partition@fs {
+					label = "file system";
+					reg = <0x00700000 0x00900000>;
+				};
+			};
+			flash@1 {
+				compatible = "spansion,s25sl12801";
+				reg = <1>;
+				spi-max-frequency = <40000000>;
+			};
+			flash@2 {
+				compatible = "spansion,s25sl12801";
+				reg = <2>;
+				spi-max-frequency = <40000000>;
+			};
+			flash@3 {
+				compatible = "spansion,s25sl12801";
+				reg = <3>;
+				spi-max-frequency = <40000000>;
+			};
+		};
+
 		dma@21300 {
 			#address-cells = <1>;
 			#size-cells = <1>;
diff --git a/arch/powerpc/boot/dts/p4080ds.dts b/arch/powerpc/boot/dts/p4080ds.dts
index 2f0de24..5b7fc29 100644
--- a/arch/powerpc/boot/dts/p4080ds.dts
+++ b/arch/powerpc/boot/dts/p4080ds.dts
@@ -236,22 +236,19 @@
 		};
 
 		spi@110000 {
-			cell-index = <0>;
 			#address-cells = <1>;
 			#size-cells = <0>;
-			compatible = "fsl,espi";
+			compatible = "fsl,p4080-espi", "fsl,mpc8536-espi";
 			reg = <0x110000 0x1000>;
 			interrupts = <53 0x2>;
 			interrupt-parent = <&mpic>;
-			espi,num-ss-bits = <4>;
-			mode = "cpu";
+			fsl,espi-num-chipselects = <4>;
 
-			fsl_m25p80@0 {
+			flash@0 {
 				#address-cells = <1>;
 				#size-cells = <1>;
-				compatible = "fsl,espi-flash";
+				compatible = "spansion,s25sl12801";
 				reg = <0>;
-				linux,modalias = "fsl_m25p80";
 				spi-max-frequency = <40000000>; /* input clock */
 				partition@u-boot {
 					label = "u-boot";
-- 
1.6.4

^ permalink raw reply related

* [PATCH v3 5/7] mtd: m25p80: add support to parse the SPI flash's partitions
From: Mingkai Hu @ 2010-09-30  8:00 UTC (permalink / raw)
  To: linuxppc-dev, spi-devel-general, linux-mtd; +Cc: kumar.gala, Mingkai Hu
In-Reply-To: <1285833646-12006-5-git-send-email-Mingkai.hu@freescale.com>

Signed-off-by: Mingkai Hu <Mingkai.hu@freescale.com>
---
v3:
 - Move the SPI flash partition code to the probe function.

 drivers/mtd/devices/m25p80.c |   39 +++++++++++++++++++++++++++------------
 1 files changed, 27 insertions(+), 12 deletions(-)

diff --git a/drivers/mtd/devices/m25p80.c b/drivers/mtd/devices/m25p80.c
index 6f512b5..47d53c7 100644
--- a/drivers/mtd/devices/m25p80.c
+++ b/drivers/mtd/devices/m25p80.c
@@ -772,7 +772,7 @@ static const struct spi_device_id *__devinit jedec_probe(struct spi_device *spi)
 static int __devinit m25p_probe(struct spi_device *spi)
 {
 	const struct spi_device_id	*id = spi_get_device_id(spi);
-	struct flash_platform_data	*data;
+	struct flash_platform_data	data, *pdata;
 	struct m25p			*flash;
 	struct flash_info		*info;
 	unsigned			i;
@@ -782,13 +782,27 @@ static int __devinit m25p_probe(struct spi_device *spi)
 	 * a chip ID, try the JEDEC id commands; they'll work for most
 	 * newer chips, even if we don't recognize the particular chip.
 	 */
-	data = spi->dev.platform_data;
-	if (data && data->type) {
+	pdata = spi->dev.platform_data;
+	if (!pdata && spi->dev.of_node) {
+		int nr_parts;
+		struct mtd_partition *parts;
+		struct device_node *np = spi->dev.of_node;
+
+		nr_parts = of_mtd_parse_partitions(&spi->dev, np, &parts);
+		if (nr_parts) {
+			pdata = &data;
+			memset(pdata, 0, sizeof(*pdata));
+			pdata->parts = parts;
+			pdata->nr_parts = nr_parts;
+		}
+	}
+
+	if (pdata && pdata->type) {
 		const struct spi_device_id *plat_id;
 
 		for (i = 0; i < ARRAY_SIZE(m25p_ids) - 1; i++) {
 			plat_id = &m25p_ids[i];
-			if (strcmp(data->type, plat_id->name))
+			if (strcmp(pdata->type, plat_id->name))
 				continue;
 			break;
 		}
@@ -796,7 +810,8 @@ static int __devinit m25p_probe(struct spi_device *spi)
 		if (i < ARRAY_SIZE(m25p_ids) - 1)
 			id = plat_id;
 		else
-			dev_warn(&spi->dev, "unrecognized id %s\n", data->type);
+			dev_warn(&spi->dev, "unrecognized id %s\n",
+					pdata->type);
 	}
 
 	info = (void *)id->driver_data;
@@ -847,8 +862,8 @@ static int __devinit m25p_probe(struct spi_device *spi)
 		write_sr(flash, 0);
 	}
 
-	if (data && data->name)
-		flash->mtd.name = data->name;
+	if (pdata && pdata->name)
+		flash->mtd.name = pdata->name;
 	else
 		flash->mtd.name = dev_name(&spi->dev);
 
@@ -919,9 +934,9 @@ static int __devinit m25p_probe(struct spi_device *spi)
 					part_probes, &parts, 0);
 		}
 
-		if (nr_parts <= 0 && data && data->parts) {
-			parts = data->parts;
-			nr_parts = data->nr_parts;
+		if (nr_parts <= 0 && pdata && pdata->parts) {
+			parts = pdata->parts;
+			nr_parts = pdata->nr_parts;
 		}
 
 		if (nr_parts > 0) {
@@ -937,9 +952,9 @@ static int __devinit m25p_probe(struct spi_device *spi)
 			flash->partitioned = 1;
 			return add_mtd_partitions(&flash->mtd, parts, nr_parts);
 		}
-	} else if (data && data->nr_parts)
+	} else if (pdata && pdata->nr_parts)
 		dev_warn(&spi->dev, "ignoring %d default partitions on %s\n",
-				data->nr_parts, data->name);
+				pdata->nr_parts, pdata->name);
 
 	return add_mtd_device(&flash->mtd) == 1 ? -ENODEV : 0;
 }
-- 
1.6.4

^ permalink raw reply related

* [PATCH v3 6/7] mtd: m25p80: add a read function to read page by page
From: Mingkai Hu @ 2010-09-30  8:00 UTC (permalink / raw)
  To: linuxppc-dev, spi-devel-general, linux-mtd; +Cc: kumar.gala, Mingkai Hu
In-Reply-To: <1285833646-12006-6-git-send-email-Mingkai.hu@freescale.com>

For Freescale's eSPI controller, the max transaction length one time
is limitted by the SPCOM[TRANSLEN] field which is 0xFFFF. When used
mkfs.ext2 command to create ext2 filesystem on the flash, the read
length will exceed the max value of the SPCOM[TRANSLEN] field, so
change the read function to read page by page.

For other SPI flash driver, also needed to supply the read function
if used the eSPI controller.

Signed-off-by: Mingkai Hu <Mingkai.hu@freescale.com>
---
v3:
 - Add a quirks member for the SPI master to handle the contrains of the
   SPI controller. I can't think of other method. :-(

 drivers/mtd/devices/m25p80.c |   78 ++++++++++++++++++++++++++++++++++++++++++
 drivers/spi/spi_fsl_lib.c    |    4 ++
 include/linux/spi/spi.h      |    5 +++
 3 files changed, 87 insertions(+), 0 deletions(-)

diff --git a/drivers/mtd/devices/m25p80.c b/drivers/mtd/devices/m25p80.c
index 47d53c7..f65cca8 100644
--- a/drivers/mtd/devices/m25p80.c
+++ b/drivers/mtd/devices/m25p80.c
@@ -377,6 +377,81 @@ static int m25p80_read(struct mtd_info *mtd, loff_t from, size_t len,
 }
 
 /*
+ * Read an address range from the flash chip page by page.
+ * Some controller has transaction length limitation such as the
+ * Freescale's eSPI controller can only trasmit 0xFFFF bytes one
+ * time, so we have to read page by page if the len is more than
+ * the limitation.
+ */
+static int m25p80_page_read(struct mtd_info *mtd, loff_t from, size_t len,
+	size_t *retlen, u_char *buf)
+{
+	struct m25p *flash = mtd_to_m25p(mtd);
+	struct spi_transfer t[2];
+	struct spi_message m;
+	u32 i, page_size = 0;
+
+	DEBUG(MTD_DEBUG_LEVEL2, "%s: %s %s 0x%08x, len %zd\n",
+			dev_name(&flash->spi->dev), __func__, "from",
+			(u32)from, len);
+
+	/* sanity checks */
+	if (!len)
+		return 0;
+
+	if (from + len > flash->mtd.size)
+		return -EINVAL;
+
+	spi_message_init(&m);
+	memset(t, 0, (sizeof t));
+
+	/* NOTE:
+	 * OPCODE_FAST_READ (if available) is faster.
+	 * Should add 1 byte DUMMY_BYTE.
+	 */
+	t[0].tx_buf = flash->command;
+	t[0].len = m25p_cmdsz(flash) + FAST_READ_DUMMY_BYTE;
+	spi_message_add_tail(&t[0], &m);
+
+	t[1].rx_buf = buf;
+	spi_message_add_tail(&t[1], &m);
+
+	/* Byte count starts at zero. */
+	if (retlen)
+		*retlen = 0;
+
+	mutex_lock(&flash->lock);
+
+	/* Wait till previous write/erase is done. */
+	if (wait_till_ready(flash)) {
+		/* REVISIT status return?? */
+		mutex_unlock(&flash->lock);
+		return 1;
+	}
+
+	/* Set up the write data buffer. */
+	flash->command[0] = OPCODE_READ;
+
+	for (i = page_size; i < len; i += page_size) {
+		page_size = len - i;
+		if (page_size > flash->page_size)
+			page_size = flash->page_size;
+		m25p_addr2cmd(flash, from + i, flash->command);
+		t[1].len = page_size;
+		t[1].rx_buf = buf + i;
+
+		spi_sync(flash->spi, &m);
+
+		*retlen += m.actual_length - m25p_cmdsz(flash)
+			- FAST_READ_DUMMY_BYTE;
+	}
+
+	mutex_unlock(&flash->lock);
+
+	return 0;
+}
+
+/*
  * Write an address range to the flash chip.  Data must be written in
  * FLASH_PAGESIZE chunks.  The address range may be any size provided
  * it is within the physical boundaries.
@@ -874,6 +949,9 @@ static int __devinit m25p_probe(struct spi_device *spi)
 	flash->mtd.erase = m25p80_erase;
 	flash->mtd.read = m25p80_read;
 
+	if (spi->master->quirks & SPI_QUIRK_TRANS_LEN_LIMIT)
+		flash->mtd.read = m25p80_page_read;
+
 	/* sst flash chips use AAI word program */
 	if (info->jedec_id >> 16 == 0xbf)
 		flash->mtd.write = sst_write;
diff --git a/drivers/spi/spi_fsl_lib.c b/drivers/spi/spi_fsl_lib.c
index 5cd741f..c8d8c2d 100644
--- a/drivers/spi/spi_fsl_lib.c
+++ b/drivers/spi/spi_fsl_lib.c
@@ -135,6 +135,10 @@ int mpc8xxx_spi_probe(struct device *dev, struct resource *mem,
 	master->cleanup = mpc8xxx_spi_cleanup;
 	master->dev.of_node = dev->of_node;
 
+	if (of_get_property(dev->of_node,
+				"fsl,spi-quirk-trans-len-limit", NULL))
+		master->quirks |= SPI_QUIRK_TRANS_LEN_LIMIT;
+
 	mpc8xxx_spi = spi_master_get_devdata(master);
 	mpc8xxx_spi->dev = dev;
 	mpc8xxx_spi->get_rx = mpc8xxx_spi_rx_buf_u8;
diff --git a/include/linux/spi/spi.h b/include/linux/spi/spi.h
index 92e52a1..4234dfd 100644
--- a/include/linux/spi/spi.h
+++ b/include/linux/spi/spi.h
@@ -304,6 +304,11 @@ struct spi_master {
 
 	/* called on release() to free memory provided by spi_master */
 	void			(*cleanup)(struct spi_device *spi);
+
+	/* some constraints of the controller */
+	u16			quirks;
+#define SPI_QUIRK_TRANS_LEN_LIMIT	BIT(0)	/* have trans length limit */
+
 };
 
 static inline void *spi_master_get_devdata(struct spi_master *master)
-- 
1.6.4

^ permalink raw reply related

* [PATCH v3 2/7] spi/mpc8xxx: refactor the common code for SPI/eSPI controller
From: Mingkai Hu @ 2010-09-30  8:00 UTC (permalink / raw)
  To: linuxppc-dev, spi-devel-general, linux-mtd; +Cc: kumar.gala, Mingkai Hu
In-Reply-To: <1285833646-12006-2-git-send-email-Mingkai.hu@freescale.com>

Refactor the common code in file spi_fsl_spi.c to spi_fsl_lib.c used
by SPI/eSPI controller driver as a library, and leave the QE/CPM SPI
controller code in the SPI controller driver spi_fsl_spi.c.

Because the register map of the SPI controller and eSPI controller
is so different, also leave the code operated the register to the
driver code, not the common code.

Signed-off-by: Mingkai Hu <Mingkai.hu@freescale.com>
---

v3:
 - Update to the latest kernel base.
 - Add a void *reg_base to compatible for SPI and eSPI register base.

 drivers/spi/Kconfig       |    5 +
 drivers/spi/Makefile      |    1 +
 drivers/spi/spi_fsl_lib.c |  237 +++++++++++++++++++
 drivers/spi/spi_fsl_lib.h |  119 ++++++++++
 drivers/spi/spi_fsl_spi.c |  552 +++++++++++++--------------------------------
 5 files changed, 522 insertions(+), 392 deletions(-)
 create mode 100644 drivers/spi/spi_fsl_lib.c
 create mode 100644 drivers/spi/spi_fsl_lib.h

diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig
index 6af34c6..79ad06f 100644
--- a/drivers/spi/Kconfig
+++ b/drivers/spi/Kconfig
@@ -182,9 +182,14 @@ config SPI_MPC512x_PSC
 	  This enables using the Freescale MPC5121 Programmable Serial
 	  Controller in SPI master mode.
 
+config SPI_FSL_LIB
+	tristate
+	depends on FSL_SOC
+
 config SPI_FSL_SPI
 	tristate "Freescale SPI controller"
 	depends on FSL_SOC
+	select SPI_FSL_LIB
 	help
 	  This enables using the Freescale SPI controllers in master mode.
 	  MPC83xx platform uses the controller in cpu mode or CPM/QE mode.
diff --git a/drivers/spi/Makefile b/drivers/spi/Makefile
index 770817c..7974c21 100644
--- a/drivers/spi/Makefile
+++ b/drivers/spi/Makefile
@@ -34,6 +34,7 @@ obj-$(CONFIG_SPI_PL022)			+= amba-pl022.o
 obj-$(CONFIG_SPI_MPC512x_PSC)		+= mpc512x_psc_spi.o
 obj-$(CONFIG_SPI_MPC52xx_PSC)		+= mpc52xx_psc_spi.o
 obj-$(CONFIG_SPI_MPC52xx)		+= mpc52xx_spi.o
+obj-$(CONFIG_SPI_FSL_LIB)		+= spi_fsl_lib.o
 obj-$(CONFIG_SPI_FSL_SPI)		+= spi_fsl_spi.o
 obj-$(CONFIG_SPI_PPC4xx)		+= spi_ppc4xx.o
 obj-$(CONFIG_SPI_S3C24XX_GPIO)		+= spi_s3c24xx_gpio.o
diff --git a/drivers/spi/spi_fsl_lib.c b/drivers/spi/spi_fsl_lib.c
new file mode 100644
index 0000000..5cd741f
--- /dev/null
+++ b/drivers/spi/spi_fsl_lib.c
@@ -0,0 +1,237 @@
+/*
+ * Freescale SPI/eSPI controller driver library.
+ *
+ * Maintainer: Kumar Gala
+ *
+ * Copyright (C) 2006 Polycom, Inc.
+ *
+ * CPM SPI and QE buffer descriptors mode support:
+ * Copyright (c) 2009  MontaVista Software, Inc.
+ * Author: Anton Vorontsov <avorontsov@ru.mvista.com>
+ *
+ * Copyright 2010 Freescale Semiconductor, Inc.
+ *
+ * This program is free software; you can redistribute  it and/or modify it
+ * under  the terms of  the GNU General  Public License as published by the
+ * Free Software Foundation;  either version 2 of the  License, or (at your
+ * option) any later version.
+ */
+#include <linux/kernel.h>
+#include <linux/interrupt.h>
+#include <linux/fsl_devices.h>
+#include <linux/dma-mapping.h>
+#include <linux/mm.h>
+#include <linux/of_platform.h>
+#include <linux/of_spi.h>
+#include <sysdev/fsl_soc.h>
+
+#include "spi_fsl_lib.h"
+
+#define MPC8XXX_SPI_RX_BUF(type) 					  \
+void mpc8xxx_spi_rx_buf_##type(u32 data, struct mpc8xxx_spi *mpc8xxx_spi) \
+{									  \
+	type *rx = mpc8xxx_spi->rx;					  \
+	*rx++ = (type)(data >> mpc8xxx_spi->rx_shift);			  \
+	mpc8xxx_spi->rx = rx;						  \
+}
+
+#define MPC8XXX_SPI_TX_BUF(type)				\
+u32 mpc8xxx_spi_tx_buf_##type(struct mpc8xxx_spi *mpc8xxx_spi)	\
+{								\
+	u32 data;						\
+	const type *tx = mpc8xxx_spi->tx;			\
+	if (!tx)						\
+		return 0;					\
+	data = *tx++ << mpc8xxx_spi->tx_shift;			\
+	mpc8xxx_spi->tx = tx;					\
+	return data;						\
+}
+
+MPC8XXX_SPI_RX_BUF(u8)
+MPC8XXX_SPI_RX_BUF(u16)
+MPC8XXX_SPI_RX_BUF(u32)
+MPC8XXX_SPI_TX_BUF(u8)
+MPC8XXX_SPI_TX_BUF(u16)
+MPC8XXX_SPI_TX_BUF(u32)
+
+struct mpc8xxx_spi_probe_info *to_of_pinfo(struct fsl_spi_platform_data *pdata)
+{
+	return container_of(pdata, struct mpc8xxx_spi_probe_info, pdata);
+}
+
+void mpc8xxx_spi_work(struct work_struct *work)
+{
+	struct mpc8xxx_spi *mpc8xxx_spi = container_of(work, struct mpc8xxx_spi,
+						       work);
+
+	spin_lock_irq(&mpc8xxx_spi->lock);
+	while (!list_empty(&mpc8xxx_spi->queue)) {
+		struct spi_message *m = container_of(mpc8xxx_spi->queue.next,
+						   struct spi_message, queue);
+
+		list_del_init(&m->queue);
+		spin_unlock_irq(&mpc8xxx_spi->lock);
+
+		if (mpc8xxx_spi->spi_do_one_msg)
+			mpc8xxx_spi->spi_do_one_msg(m);
+
+		spin_lock_irq(&mpc8xxx_spi->lock);
+	}
+	spin_unlock_irq(&mpc8xxx_spi->lock);
+}
+
+int mpc8xxx_spi_transfer(struct spi_device *spi,
+				struct spi_message *m)
+{
+	struct mpc8xxx_spi *mpc8xxx_spi = spi_master_get_devdata(spi->master);
+	unsigned long flags;
+
+	m->actual_length = 0;
+	m->status = -EINPROGRESS;
+
+	spin_lock_irqsave(&mpc8xxx_spi->lock, flags);
+	list_add_tail(&m->queue, &mpc8xxx_spi->queue);
+	queue_work(mpc8xxx_spi->workqueue, &mpc8xxx_spi->work);
+	spin_unlock_irqrestore(&mpc8xxx_spi->lock, flags);
+
+	return 0;
+}
+
+void mpc8xxx_spi_cleanup(struct spi_device *spi)
+{
+	kfree(spi->controller_state);
+}
+
+const char *mpc8xxx_spi_strmode(unsigned int flags)
+{
+	if (flags & SPI_QE_CPU_MODE) {
+		return "QE CPU";
+	} else if (flags & SPI_CPM_MODE) {
+		if (flags & SPI_QE)
+			return "QE";
+		else if (flags & SPI_CPM2)
+			return "CPM2";
+		else
+			return "CPM1";
+	}
+	return "CPU";
+}
+
+int mpc8xxx_spi_probe(struct device *dev, struct resource *mem,
+			unsigned int irq)
+{
+	struct fsl_spi_platform_data *pdata = dev->platform_data;
+	struct spi_master *master;
+	struct mpc8xxx_spi *mpc8xxx_spi;
+	int ret = 0;
+
+	master = dev_get_drvdata(dev);
+
+	/* the spi->mode bits understood by this driver: */
+	master->mode_bits = SPI_CPOL | SPI_CPHA | SPI_CS_HIGH
+			| SPI_LSB_FIRST | SPI_LOOP;
+
+	master->transfer = mpc8xxx_spi_transfer;
+	master->cleanup = mpc8xxx_spi_cleanup;
+	master->dev.of_node = dev->of_node;
+
+	mpc8xxx_spi = spi_master_get_devdata(master);
+	mpc8xxx_spi->dev = dev;
+	mpc8xxx_spi->get_rx = mpc8xxx_spi_rx_buf_u8;
+	mpc8xxx_spi->get_tx = mpc8xxx_spi_tx_buf_u8;
+	mpc8xxx_spi->flags = pdata->flags;
+	mpc8xxx_spi->spibrg = pdata->sysclk;
+	mpc8xxx_spi->irq = irq;
+
+	mpc8xxx_spi->rx_shift = 0;
+	mpc8xxx_spi->tx_shift = 0;
+
+	init_completion(&mpc8xxx_spi->done);
+
+	master->bus_num = pdata->bus_num;
+	master->num_chipselect = pdata->max_chipselect;
+
+	spin_lock_init(&mpc8xxx_spi->lock);
+	init_completion(&mpc8xxx_spi->done);
+	INIT_WORK(&mpc8xxx_spi->work, mpc8xxx_spi_work);
+	INIT_LIST_HEAD(&mpc8xxx_spi->queue);
+
+	mpc8xxx_spi->workqueue = create_singlethread_workqueue(
+		dev_name(master->dev.parent));
+	if (mpc8xxx_spi->workqueue == NULL) {
+		ret = -EBUSY;
+		goto err;
+	}
+
+	return 0;
+
+err:
+	return ret;
+}
+
+int __devexit mpc8xxx_spi_remove(struct device *dev)
+{
+	struct mpc8xxx_spi *mpc8xxx_spi;
+	struct spi_master *master;
+
+	master = dev_get_drvdata(dev);
+	mpc8xxx_spi = spi_master_get_devdata(master);
+
+	flush_workqueue(mpc8xxx_spi->workqueue);
+	destroy_workqueue(mpc8xxx_spi->workqueue);
+	spi_unregister_master(master);
+
+	free_irq(mpc8xxx_spi->irq, mpc8xxx_spi);
+
+	if (mpc8xxx_spi->spi_remove)
+		mpc8xxx_spi->spi_remove(mpc8xxx_spi);
+
+	return 0;
+}
+
+int __devinit of_mpc8xxx_spi_probe(struct platform_device *ofdev,
+					const struct of_device_id *ofid)
+{
+	struct device *dev = &ofdev->dev;
+	struct device_node *np = ofdev->dev.of_node;
+	struct mpc8xxx_spi_probe_info *pinfo;
+	struct fsl_spi_platform_data *pdata;
+	const void *prop;
+	int ret = -ENOMEM;
+
+	pinfo = kzalloc(sizeof(*pinfo), GFP_KERNEL);
+	if (!pinfo)
+		return -ENOMEM;
+
+	pdata = &pinfo->pdata;
+	dev->platform_data = pdata;
+
+	/* Allocate bus num dynamically. */
+	pdata->bus_num = -1;
+
+	/* SPI controller is either clocked from QE or SoC clock. */
+	pdata->sysclk = get_brgfreq();
+	if (pdata->sysclk == -1) {
+		pdata->sysclk = fsl_get_sys_freq();
+		if (pdata->sysclk == -1) {
+			ret = -ENODEV;
+			goto err;
+		}
+	}
+
+	prop = of_get_property(np, "mode", NULL);
+	if (prop && !strcmp(prop, "cpu-qe"))
+		pdata->flags = SPI_QE_CPU_MODE;
+	else if (prop && !strcmp(prop, "qe"))
+		pdata->flags = SPI_CPM_MODE | SPI_QE;
+	else if (of_device_is_compatible(np, "fsl,cpm2-spi"))
+		pdata->flags = SPI_CPM_MODE | SPI_CPM2;
+	else if (of_device_is_compatible(np, "fsl,cpm1-spi"))
+		pdata->flags = SPI_CPM_MODE | SPI_CPM1;
+
+	return 0;
+
+err:
+	kfree(pinfo);
+	return ret;
+}
diff --git a/drivers/spi/spi_fsl_lib.h b/drivers/spi/spi_fsl_lib.h
new file mode 100644
index 0000000..6ae8949
--- /dev/null
+++ b/drivers/spi/spi_fsl_lib.h
@@ -0,0 +1,119 @@
+/*
+ * Freescale SPI/eSPI controller driver library.
+ *
+ * Maintainer: Kumar Gala
+ *
+ * Copyright 2010 Freescale Semiconductor, Inc.
+ * Copyright (C) 2006 Polycom, Inc.
+ *
+ * CPM SPI and QE buffer descriptors mode support:
+ * Copyright (c) 2009  MontaVista Software, Inc.
+ * Author: Anton Vorontsov <avorontsov@ru.mvista.com>
+ *
+ * This program is free software; you can redistribute  it and/or modify it
+ * under  the terms of  the GNU General  Public License as published by the
+ * Free Software Foundation;  either version 2 of the  License, or (at your
+ * option) any later version.
+ */
+#ifndef __SPI_FSL_LIB_H__
+#define __SPI_FSL_LIB_H__
+
+/* SPI/eSPI Controller driver's private data. */
+struct mpc8xxx_spi {
+	struct device *dev;
+	void *reg_base;
+
+	/* rx & tx bufs from the spi_transfer */
+	const void *tx;
+	void *rx;
+
+	int subblock;
+	struct spi_pram __iomem *pram;
+	struct cpm_buf_desc __iomem *tx_bd;
+	struct cpm_buf_desc __iomem *rx_bd;
+
+	struct spi_transfer *xfer_in_progress;
+
+	/* dma addresses for CPM transfers */
+	dma_addr_t tx_dma;
+	dma_addr_t rx_dma;
+	bool map_tx_dma;
+	bool map_rx_dma;
+
+	dma_addr_t dma_dummy_tx;
+	dma_addr_t dma_dummy_rx;
+
+	/* functions to deal with different sized buffers */
+	void (*get_rx) (u32 rx_data, struct mpc8xxx_spi *);
+	u32(*get_tx) (struct mpc8xxx_spi *);
+
+	/* hooks for different controller driver */
+	void (*spi_do_one_msg) (struct spi_message *m);
+	void (*spi_remove) (struct mpc8xxx_spi *mspi);
+
+	unsigned int count;
+	unsigned int irq;
+
+	unsigned nsecs;		/* (clock cycle time)/2 */
+
+	u32 spibrg;		/* SPIBRG input clock */
+	u32 rx_shift;		/* RX data reg shift when in qe mode */
+	u32 tx_shift;		/* TX data reg shift when in qe mode */
+
+	unsigned int flags;
+
+	struct workqueue_struct *workqueue;
+	struct work_struct work;
+
+	struct list_head queue;
+	spinlock_t lock;
+
+	struct completion done;
+};
+
+struct spi_mpc8xxx_cs {
+	/* functions to deal with different sized buffers */
+	void (*get_rx) (u32 rx_data, struct mpc8xxx_spi *);
+	u32 (*get_tx) (struct mpc8xxx_spi *);
+	u32 rx_shift;		/* RX data reg shift when in qe mode */
+	u32 tx_shift;		/* TX data reg shift when in qe mode */
+	u32 hw_mode;		/* Holds HW mode register settings */
+};
+
+static inline void mpc8xxx_spi_write_reg(__be32 __iomem *reg, u32 val)
+{
+	out_be32(reg, val);
+}
+
+static inline u32 mpc8xxx_spi_read_reg(__be32 __iomem *reg)
+{
+	return in_be32(reg);
+}
+
+struct mpc8xxx_spi_probe_info {
+	struct fsl_spi_platform_data pdata;
+	int *gpios;
+	bool *alow_flags;
+};
+
+extern u32 mpc8xxx_spi_tx_buf_u8(struct mpc8xxx_spi *mpc8xxx_spi);
+extern u32 mpc8xxx_spi_tx_buf_u16(struct mpc8xxx_spi *mpc8xxx_spi);
+extern u32 mpc8xxx_spi_tx_buf_u32(struct mpc8xxx_spi *mpc8xxx_spi);
+extern void mpc8xxx_spi_rx_buf_u8(u32 data, struct mpc8xxx_spi *mpc8xxx_spi);
+extern void mpc8xxx_spi_rx_buf_u16(u32 data, struct mpc8xxx_spi *mpc8xxx_spi);
+extern void mpc8xxx_spi_rx_buf_u32(u32 data, struct mpc8xxx_spi *mpc8xxx_spi);
+
+extern struct mpc8xxx_spi_probe_info *to_of_pinfo(
+		struct fsl_spi_platform_data *pdata);
+extern int mpc8xxx_spi_bufs(struct mpc8xxx_spi *mspi,
+		struct spi_transfer *t, unsigned int len);
+extern int mpc8xxx_spi_transfer(struct spi_device *spi, struct spi_message *m);
+extern void mpc8xxx_spi_cleanup(struct spi_device *spi);
+extern const char *mpc8xxx_spi_strmode(unsigned int flags);
+extern int mpc8xxx_spi_probe(struct device *dev, struct resource *mem,
+		unsigned int irq);
+extern int mpc8xxx_spi_remove(struct device *dev);
+extern int of_mpc8xxx_spi_probe(struct platform_device *ofdev,
+				const struct of_device_id *ofid);
+
+#endif /* __SPI_FSL_LIB_H__ */
diff --git a/drivers/spi/spi_fsl_spi.c b/drivers/spi/spi_fsl_spi.c
index d31b57f..c662e7e 100644
--- a/drivers/spi/spi_fsl_spi.c
+++ b/drivers/spi/spi_fsl_spi.c
@@ -1,9 +1,10 @@
 /*
- * MPC8xxx SPI controller driver.
+ * Freescale SPI controller driver.
  *
  * Maintainer: Kumar Gala
  *
  * Copyright (C) 2006 Polycom, Inc.
+ * Copyright 2010 Freescale Semiconductor, Inc.
  *
  * CPM SPI and QE buffer descriptors mode support:
  * Copyright (c) 2009  MontaVista Software, Inc.
@@ -15,18 +16,11 @@
  * option) any later version.
  */
 #include <linux/module.h>
-#include <linux/init.h>
 #include <linux/types.h>
 #include <linux/kernel.h>
-#include <linux/bug.h>
-#include <linux/errno.h>
-#include <linux/err.h>
-#include <linux/io.h>
-#include <linux/completion.h>
 #include <linux/interrupt.h>
 #include <linux/delay.h>
 #include <linux/irq.h>
-#include <linux/device.h>
 #include <linux/spi/spi.h>
 #include <linux/spi/spi_bitbang.h>
 #include <linux/platform_device.h>
@@ -38,12 +32,12 @@
 #include <linux/of_platform.h>
 #include <linux/gpio.h>
 #include <linux/of_gpio.h>
-#include <linux/slab.h>
 
 #include <sysdev/fsl_soc.h>
 #include <asm/cpm.h>
 #include <asm/qe.h>
-#include <asm/irq.h>
+
+#include "spi_fsl_lib.h"
 
 /* CPM1 and CPM2 are mutually exclusive. */
 #ifdef CONFIG_CPM1
@@ -55,7 +49,7 @@
 #endif
 
 /* SPI Controller registers */
-struct mpc8xxx_spi_reg {
+struct fsl_spi_reg {
 	u8 res1[0x20];
 	__be32 mode;
 	__be32 event;
@@ -80,7 +74,7 @@ struct mpc8xxx_spi_reg {
 
 /*
  * Default for SPI Mode:
- * 	SPI MODE 0 (inactive low, phase middle, MSB, 8-bit length, slow clk
+ *	SPI MODE 0 (inactive low, phase middle, MSB, 8-bit length, slow clk
  */
 #define	SPMODE_INIT_VAL (SPMODE_CI_INACTIVEHIGH | SPMODE_DIV16 | SPMODE_REV | \
 			 SPMODE_MS | SPMODE_LEN(7) | SPMODE_PM(0xf))
@@ -102,112 +96,16 @@ struct mpc8xxx_spi_reg {
 #define	SPI_PRAM_SIZE	0x100
 #define	SPI_MRBLR	((unsigned int)PAGE_SIZE)
 
-/* SPI Controller driver's private data. */
-struct mpc8xxx_spi {
-	struct device *dev;
-	struct mpc8xxx_spi_reg __iomem *base;
-
-	/* rx & tx bufs from the spi_transfer */
-	const void *tx;
-	void *rx;
-
-	int subblock;
-	struct spi_pram __iomem *pram;
-	struct cpm_buf_desc __iomem *tx_bd;
-	struct cpm_buf_desc __iomem *rx_bd;
-
-	struct spi_transfer *xfer_in_progress;
-
-	/* dma addresses for CPM transfers */
-	dma_addr_t tx_dma;
-	dma_addr_t rx_dma;
-	bool map_tx_dma;
-	bool map_rx_dma;
-
-	dma_addr_t dma_dummy_tx;
-	dma_addr_t dma_dummy_rx;
-
-	/* functions to deal with different sized buffers */
-	void (*get_rx) (u32 rx_data, struct mpc8xxx_spi *);
-	u32(*get_tx) (struct mpc8xxx_spi *);
-
-	unsigned int count;
-	unsigned int irq;
-
-	unsigned nsecs;		/* (clock cycle time)/2 */
-
-	u32 spibrg;		/* SPIBRG input clock */
-	u32 rx_shift;		/* RX data reg shift when in qe mode */
-	u32 tx_shift;		/* TX data reg shift when in qe mode */
-
-	unsigned int flags;
-
-	struct workqueue_struct *workqueue;
-	struct work_struct work;
-
-	struct list_head queue;
-	spinlock_t lock;
-
-	struct completion done;
-};
-
-static void *mpc8xxx_dummy_rx;
-static DEFINE_MUTEX(mpc8xxx_dummy_rx_lock);
-static int mpc8xxx_dummy_rx_refcnt;
-
-struct spi_mpc8xxx_cs {
-	/* functions to deal with different sized buffers */
-	void (*get_rx) (u32 rx_data, struct mpc8xxx_spi *);
-	u32 (*get_tx) (struct mpc8xxx_spi *);
-	u32 rx_shift;		/* RX data reg shift when in qe mode */
-	u32 tx_shift;		/* TX data reg shift when in qe mode */
-	u32 hw_mode;		/* Holds HW mode register settings */
-};
-
-static inline void mpc8xxx_spi_write_reg(__be32 __iomem *reg, u32 val)
-{
-	out_be32(reg, val);
-}
-
-static inline u32 mpc8xxx_spi_read_reg(__be32 __iomem *reg)
-{
-	return in_be32(reg);
-}
-
-#define MPC83XX_SPI_RX_BUF(type) 					  \
-static									  \
-void mpc8xxx_spi_rx_buf_##type(u32 data, struct mpc8xxx_spi *mpc8xxx_spi) \
-{									  \
-	type *rx = mpc8xxx_spi->rx;					  \
-	*rx++ = (type)(data >> mpc8xxx_spi->rx_shift);			  \
-	mpc8xxx_spi->rx = rx;						  \
-}
-
-#define MPC83XX_SPI_TX_BUF(type)				\
-static								\
-u32 mpc8xxx_spi_tx_buf_##type(struct mpc8xxx_spi *mpc8xxx_spi)	\
-{								\
-	u32 data;						\
-	const type *tx = mpc8xxx_spi->tx;			\
-	if (!tx)						\
-		return 0;					\
-	data = *tx++ << mpc8xxx_spi->tx_shift;			\
-	mpc8xxx_spi->tx = tx;					\
-	return data;						\
-}
+static void *fsl_dummy_rx;
+static DEFINE_MUTEX(fsl_dummy_rx_lock);
+static int fsl_dummy_rx_refcnt;
 
-MPC83XX_SPI_RX_BUF(u8)
-MPC83XX_SPI_RX_BUF(u16)
-MPC83XX_SPI_RX_BUF(u32)
-MPC83XX_SPI_TX_BUF(u8)
-MPC83XX_SPI_TX_BUF(u16)
-MPC83XX_SPI_TX_BUF(u32)
-
-static void mpc8xxx_spi_change_mode(struct spi_device *spi)
+static void fsl_spi_change_mode(struct spi_device *spi)
 {
 	struct mpc8xxx_spi *mspi = spi_master_get_devdata(spi->master);
 	struct spi_mpc8xxx_cs *cs = spi->controller_state;
-	__be32 __iomem *mode = &mspi->base->mode;
+	struct fsl_spi_reg *reg_base = (struct fsl_spi_reg *)mspi->reg_base;
+	__be32 __iomem *mode = &reg_base->mode;
 	unsigned long flags;
 
 	if (cs->hw_mode == mpc8xxx_spi_read_reg(mode))
@@ -238,7 +136,7 @@ static void mpc8xxx_spi_change_mode(struct spi_device *spi)
 	local_irq_restore(flags);
 }
 
-static void mpc8xxx_spi_chipselect(struct spi_device *spi, int value)
+static void fsl_spi_chipselect(struct spi_device *spi, int value)
 {
 	struct mpc8xxx_spi *mpc8xxx_spi = spi_master_get_devdata(spi->master);
 	struct fsl_spi_platform_data *pdata = spi->dev.parent->platform_data;
@@ -256,18 +154,17 @@ static void mpc8xxx_spi_chipselect(struct spi_device *spi, int value)
 		mpc8xxx_spi->get_rx = cs->get_rx;
 		mpc8xxx_spi->get_tx = cs->get_tx;
 
-		mpc8xxx_spi_change_mode(spi);
+		fsl_spi_change_mode(spi);
 
 		if (pdata->cs_control)
 			pdata->cs_control(spi, pol);
 	}
 }
 
-static int
-mspi_apply_cpu_mode_quirks(struct spi_mpc8xxx_cs *cs,
-			   struct spi_device *spi,
-			   struct mpc8xxx_spi *mpc8xxx_spi,
-			   int bits_per_word)
+static int mspi_apply_cpu_mode_quirks(struct spi_mpc8xxx_cs *cs,
+				struct spi_device *spi,
+				struct mpc8xxx_spi *mpc8xxx_spi,
+				int bits_per_word)
 {
 	cs->rx_shift = 0;
 	cs->tx_shift = 0;
@@ -307,10 +204,9 @@ mspi_apply_cpu_mode_quirks(struct spi_mpc8xxx_cs *cs,
 	return bits_per_word;
 }
 
-static int
-mspi_apply_qe_mode_quirks(struct spi_mpc8xxx_cs *cs,
-			  struct spi_device *spi,
-			  int bits_per_word)
+static int mspi_apply_qe_mode_quirks(struct spi_mpc8xxx_cs *cs,
+				struct spi_device *spi,
+				int bits_per_word)
 {
 	/* QE uses Little Endian for words > 8
 	 * so transform all words > 8 into 8 bits
@@ -326,13 +222,13 @@ mspi_apply_qe_mode_quirks(struct spi_mpc8xxx_cs *cs,
 	return bits_per_word;
 }
 
-static
-int mpc8xxx_spi_setup_transfer(struct spi_device *spi, struct spi_transfer *t)
+static int fsl_spi_setup_transfer(struct spi_device *spi,
+					struct spi_transfer *t)
 {
 	struct mpc8xxx_spi *mpc8xxx_spi;
-	int bits_per_word;
+	int bits_per_word = 0;
 	u8 pm;
-	u32 hz;
+	u32 hz = 0;
 	struct spi_mpc8xxx_cs	*cs = spi->controller_state;
 
 	mpc8xxx_spi = spi_master_get_devdata(spi->master);
@@ -340,9 +236,6 @@ int mpc8xxx_spi_setup_transfer(struct spi_device *spi, struct spi_transfer *t)
 	if (t) {
 		bits_per_word = t->bits_per_word;
 		hz = t->speed_hz;
-	} else {
-		bits_per_word = 0;
-		hz = 0;
 	}
 
 	/* spi_transfer level calls that work per-word */
@@ -388,23 +281,25 @@ int mpc8xxx_spi_setup_transfer(struct spi_device *spi, struct spi_transfer *t)
 			  hz, mpc8xxx_spi->spibrg / 1024);
 		if (pm > 16)
 			pm = 16;
-	} else
+	} else {
 		pm = (mpc8xxx_spi->spibrg - 1) / (hz * 4) + 1;
+	}
 	if (pm)
 		pm--;
 
 	cs->hw_mode |= SPMODE_PM(pm);
 
-	mpc8xxx_spi_change_mode(spi);
+	fsl_spi_change_mode(spi);
 	return 0;
 }
 
-static void mpc8xxx_spi_cpm_bufs_start(struct mpc8xxx_spi *mspi)
+static void fsl_spi_cpm_bufs_start(struct mpc8xxx_spi *mspi)
 {
 	struct cpm_buf_desc __iomem *tx_bd = mspi->tx_bd;
 	struct cpm_buf_desc __iomem *rx_bd = mspi->rx_bd;
 	unsigned int xfer_len = min(mspi->count, SPI_MRBLR);
 	unsigned int xfer_ofs;
+	struct fsl_spi_reg *reg_base = (struct fsl_spi_reg *)mspi->reg_base;
 
 	xfer_ofs = mspi->xfer_in_progress->len - mspi->count;
 
@@ -418,13 +313,14 @@ static void mpc8xxx_spi_cpm_bufs_start(struct mpc8xxx_spi *mspi)
 				 BD_SC_LAST);
 
 	/* start transfer */
-	mpc8xxx_spi_write_reg(&mspi->base->command, SPCOM_STR);
+	mpc8xxx_spi_write_reg(&reg_base->command, SPCOM_STR);
 }
 
-static int mpc8xxx_spi_cpm_bufs(struct mpc8xxx_spi *mspi,
+static int fsl_spi_cpm_bufs(struct mpc8xxx_spi *mspi,
 				struct spi_transfer *t, bool is_dma_mapped)
 {
 	struct device *dev = mspi->dev;
+	struct fsl_spi_reg *reg_base = (struct fsl_spi_reg *)mspi->reg_base;
 
 	if (is_dma_mapped) {
 		mspi->map_tx_dma = 0;
@@ -469,13 +365,13 @@ static int mpc8xxx_spi_cpm_bufs(struct mpc8xxx_spi *mspi,
 	}
 
 	/* enable rx ints */
-	mpc8xxx_spi_write_reg(&mspi->base->mask, SPIE_RXB);
+	mpc8xxx_spi_write_reg(&reg_base->mask, SPIE_RXB);
 
 	mspi->xfer_in_progress = t;
 	mspi->count = t->len;
 
 	/* start CPM transfers */
-	mpc8xxx_spi_cpm_bufs_start(mspi);
+	fsl_spi_cpm_bufs_start(mspi);
 
 	return 0;
 
@@ -485,7 +381,7 @@ err_rx_dma:
 	return -ENOMEM;
 }
 
-static void mpc8xxx_spi_cpm_bufs_complete(struct mpc8xxx_spi *mspi)
+static void fsl_spi_cpm_bufs_complete(struct mpc8xxx_spi *mspi)
 {
 	struct device *dev = mspi->dev;
 	struct spi_transfer *t = mspi->xfer_in_progress;
@@ -497,31 +393,34 @@ static void mpc8xxx_spi_cpm_bufs_complete(struct mpc8xxx_spi *mspi)
 	mspi->xfer_in_progress = NULL;
 }
 
-static int mpc8xxx_spi_cpu_bufs(struct mpc8xxx_spi *mspi,
+static int fsl_spi_cpu_bufs(struct mpc8xxx_spi *mspi,
 				struct spi_transfer *t, unsigned int len)
 {
 	u32 word;
+	struct fsl_spi_reg *reg_base = (struct fsl_spi_reg *)mspi->reg_base;
 
 	mspi->count = len;
 
 	/* enable rx ints */
-	mpc8xxx_spi_write_reg(&mspi->base->mask, SPIM_NE);
+	mpc8xxx_spi_write_reg(&reg_base->mask, SPIM_NE);
 
 	/* transmit word */
 	word = mspi->get_tx(mspi);
-	mpc8xxx_spi_write_reg(&mspi->base->transmit, word);
+	mpc8xxx_spi_write_reg(&reg_base->transmit, word);
 
 	return 0;
 }
 
-static int mpc8xxx_spi_bufs(struct spi_device *spi, struct spi_transfer *t,
+static int fsl_spi_bufs(struct spi_device *spi, struct spi_transfer *t,
 			    bool is_dma_mapped)
 {
 	struct mpc8xxx_spi *mpc8xxx_spi = spi_master_get_devdata(spi->master);
+	struct fsl_spi_reg *reg_base;
 	unsigned int len = t->len;
 	u8 bits_per_word;
 	int ret;
 
+	reg_base = (struct fsl_spi_reg *)&mpc8xxx_spi->reg_base;
 	bits_per_word = spi->bits_per_word;
 	if (t->bits_per_word)
 		bits_per_word = t->bits_per_word;
@@ -545,24 +444,24 @@ static int mpc8xxx_spi_bufs(struct spi_device *spi, struct spi_transfer *t,
 	INIT_COMPLETION(mpc8xxx_spi->done);
 
 	if (mpc8xxx_spi->flags & SPI_CPM_MODE)
-		ret = mpc8xxx_spi_cpm_bufs(mpc8xxx_spi, t, is_dma_mapped);
+		ret = fsl_spi_cpm_bufs(mpc8xxx_spi, t, is_dma_mapped);
 	else
-		ret = mpc8xxx_spi_cpu_bufs(mpc8xxx_spi, t, len);
+		ret = fsl_spi_cpu_bufs(mpc8xxx_spi, t, len);
 	if (ret)
 		return ret;
 
 	wait_for_completion(&mpc8xxx_spi->done);
 
 	/* disable rx ints */
-	mpc8xxx_spi_write_reg(&mpc8xxx_spi->base->mask, 0);
+	mpc8xxx_spi_write_reg(&reg_base->mask, 0);
 
 	if (mpc8xxx_spi->flags & SPI_CPM_MODE)
-		mpc8xxx_spi_cpm_bufs_complete(mpc8xxx_spi);
+		fsl_spi_cpm_bufs_complete(mpc8xxx_spi);
 
 	return mpc8xxx_spi->count;
 }
 
-static void mpc8xxx_spi_do_one_msg(struct spi_message *m)
+static void fsl_spi_do_one_msg(struct spi_message *m)
 {
 	struct spi_device *spi = m->spi;
 	struct spi_transfer *t;
@@ -578,18 +477,18 @@ static void mpc8xxx_spi_do_one_msg(struct spi_message *m)
 			status = -EINVAL;
 
 			if (cs_change)
-				status = mpc8xxx_spi_setup_transfer(spi, t);
+				status = fsl_spi_setup_transfer(spi, t);
 			if (status < 0)
 				break;
 		}
 
 		if (cs_change) {
-			mpc8xxx_spi_chipselect(spi, BITBANG_CS_ACTIVE);
+			fsl_spi_chipselect(spi, BITBANG_CS_ACTIVE);
 			ndelay(nsecs);
 		}
 		cs_change = t->cs_change;
 		if (t->len)
-			status = mpc8xxx_spi_bufs(spi, t, m->is_dma_mapped);
+			status = fsl_spi_bufs(spi, t, m->is_dma_mapped);
 		if (status) {
 			status = -EMSGSIZE;
 			break;
@@ -601,7 +500,7 @@ static void mpc8xxx_spi_do_one_msg(struct spi_message *m)
 
 		if (cs_change) {
 			ndelay(nsecs);
-			mpc8xxx_spi_chipselect(spi, BITBANG_CS_INACTIVE);
+			fsl_spi_chipselect(spi, BITBANG_CS_INACTIVE);
 			ndelay(nsecs);
 		}
 	}
@@ -611,35 +510,16 @@ static void mpc8xxx_spi_do_one_msg(struct spi_message *m)
 
 	if (status || !cs_change) {
 		ndelay(nsecs);
-		mpc8xxx_spi_chipselect(spi, BITBANG_CS_INACTIVE);
+		fsl_spi_chipselect(spi, BITBANG_CS_INACTIVE);
 	}
 
-	mpc8xxx_spi_setup_transfer(spi, NULL);
-}
-
-static void mpc8xxx_spi_work(struct work_struct *work)
-{
-	struct mpc8xxx_spi *mpc8xxx_spi = container_of(work, struct mpc8xxx_spi,
-						       work);
-
-	spin_lock_irq(&mpc8xxx_spi->lock);
-	while (!list_empty(&mpc8xxx_spi->queue)) {
-		struct spi_message *m = container_of(mpc8xxx_spi->queue.next,
-						   struct spi_message, queue);
-
-		list_del_init(&m->queue);
-		spin_unlock_irq(&mpc8xxx_spi->lock);
-
-		mpc8xxx_spi_do_one_msg(m);
-
-		spin_lock_irq(&mpc8xxx_spi->lock);
-	}
-	spin_unlock_irq(&mpc8xxx_spi->lock);
+	fsl_spi_setup_transfer(spi, NULL);
 }
 
-static int mpc8xxx_spi_setup(struct spi_device *spi)
+static int fsl_spi_setup(struct spi_device *spi)
 {
 	struct mpc8xxx_spi *mpc8xxx_spi;
+	struct fsl_spi_reg *reg_base;
 	int retval;
 	u32 hw_mode;
 	struct spi_mpc8xxx_cs	*cs = spi->controller_state;
@@ -655,8 +535,10 @@ static int mpc8xxx_spi_setup(struct spi_device *spi)
 	}
 	mpc8xxx_spi = spi_master_get_devdata(spi->master);
 
+	reg_base = (struct fsl_spi_reg *)&mpc8xxx_spi->reg_base;
+
 	hw_mode = cs->hw_mode; /* Save original settings */
-	cs->hw_mode = mpc8xxx_spi_read_reg(&mpc8xxx_spi->base->mode);
+	cs->hw_mode = mpc8xxx_spi_read_reg(&reg_base->mode);
 	/* mask out bits we are going to set */
 	cs->hw_mode &= ~(SPMODE_CP_BEGIN_EDGECLK | SPMODE_CI_INACTIVEHIGH
 			 | SPMODE_REV | SPMODE_LOOP);
@@ -670,7 +552,7 @@ static int mpc8xxx_spi_setup(struct spi_device *spi)
 	if (spi->mode & SPI_LOOP)
 		cs->hw_mode |= SPMODE_LOOP;
 
-	retval = mpc8xxx_spi_setup_transfer(spi, NULL);
+	retval = fsl_spi_setup_transfer(spi, NULL);
 	if (retval < 0) {
 		cs->hw_mode = hw_mode; /* Restore settings */
 		return retval;
@@ -678,9 +560,10 @@ static int mpc8xxx_spi_setup(struct spi_device *spi)
 	return 0;
 }
 
-static void mpc8xxx_spi_cpm_irq(struct mpc8xxx_spi *mspi, u32 events)
+static void fsl_spi_cpm_irq(struct mpc8xxx_spi *mspi, u32 events)
 {
 	u16 len;
+	struct fsl_spi_reg *reg_base = (struct fsl_spi_reg *)mspi->reg_base;
 
 	dev_dbg(mspi->dev, "%s: bd datlen %d, count %d\n", __func__,
 		in_be16(&mspi->rx_bd->cbd_datlen), mspi->count);
@@ -692,20 +575,22 @@ static void mpc8xxx_spi_cpm_irq(struct mpc8xxx_spi *mspi, u32 events)
 	}
 
 	/* Clear the events */
-	mpc8xxx_spi_write_reg(&mspi->base->event, events);
+	mpc8xxx_spi_write_reg(&reg_base->event, events);
 
 	mspi->count -= len;
 	if (mspi->count)
-		mpc8xxx_spi_cpm_bufs_start(mspi);
+		fsl_spi_cpm_bufs_start(mspi);
 	else
 		complete(&mspi->done);
 }
 
-static void mpc8xxx_spi_cpu_irq(struct mpc8xxx_spi *mspi, u32 events)
+static void fsl_spi_cpu_irq(struct mpc8xxx_spi *mspi, u32 events)
 {
+	struct fsl_spi_reg *reg_base = (struct fsl_spi_reg *)mspi->reg_base;
+
 	/* We need handle RX first */
 	if (events & SPIE_NE) {
-		u32 rx_data = mpc8xxx_spi_read_reg(&mspi->base->receive);
+		u32 rx_data = mpc8xxx_spi_read_reg(&reg_base->receive);
 
 		if (mspi->rx)
 			mspi->get_rx(rx_data, mspi);
@@ -714,102 +599,80 @@ static void mpc8xxx_spi_cpu_irq(struct mpc8xxx_spi *mspi, u32 events)
 	if ((events & SPIE_NF) == 0)
 		/* spin until TX is done */
 		while (((events =
-			mpc8xxx_spi_read_reg(&mspi->base->event)) &
+			mpc8xxx_spi_read_reg(&reg_base->event)) &
 						SPIE_NF) == 0)
 			cpu_relax();
 
 	/* Clear the events */
-	mpc8xxx_spi_write_reg(&mspi->base->event, events);
+	mpc8xxx_spi_write_reg(&reg_base->event, events);
 
 	mspi->count -= 1;
 	if (mspi->count) {
 		u32 word = mspi->get_tx(mspi);
 
-		mpc8xxx_spi_write_reg(&mspi->base->transmit, word);
+		mpc8xxx_spi_write_reg(&reg_base->transmit, word);
 	} else {
 		complete(&mspi->done);
 	}
 }
 
-static irqreturn_t mpc8xxx_spi_irq(s32 irq, void *context_data)
+static irqreturn_t fsl_spi_irq(s32 irq, void *context_data)
 {
 	struct mpc8xxx_spi *mspi = context_data;
 	irqreturn_t ret = IRQ_NONE;
 	u32 events;
+	struct fsl_spi_reg *reg_base = (struct fsl_spi_reg *)mspi->reg_base;
 
 	/* Get interrupt events(tx/rx) */
-	events = mpc8xxx_spi_read_reg(&mspi->base->event);
+	events = mpc8xxx_spi_read_reg(&reg_base->event);
 	if (events)
 		ret = IRQ_HANDLED;
 
 	dev_dbg(mspi->dev, "%s: events %x\n", __func__, events);
 
 	if (mspi->flags & SPI_CPM_MODE)
-		mpc8xxx_spi_cpm_irq(mspi, events);
+		fsl_spi_cpm_irq(mspi, events);
 	else
-		mpc8xxx_spi_cpu_irq(mspi, events);
+		fsl_spi_cpu_irq(mspi, events);
 
 	return ret;
 }
 
-static int mpc8xxx_spi_transfer(struct spi_device *spi,
-				struct spi_message *m)
+static void *fsl_spi_alloc_dummy_rx(void)
 {
-	struct mpc8xxx_spi *mpc8xxx_spi = spi_master_get_devdata(spi->master);
-	unsigned long flags;
+	mutex_lock(&fsl_dummy_rx_lock);
 
-	m->actual_length = 0;
-	m->status = -EINPROGRESS;
+	if (!fsl_dummy_rx)
+		fsl_dummy_rx = kmalloc(SPI_MRBLR, GFP_KERNEL);
+	if (fsl_dummy_rx)
+		fsl_dummy_rx_refcnt++;
 
-	spin_lock_irqsave(&mpc8xxx_spi->lock, flags);
-	list_add_tail(&m->queue, &mpc8xxx_spi->queue);
-	queue_work(mpc8xxx_spi->workqueue, &mpc8xxx_spi->work);
-	spin_unlock_irqrestore(&mpc8xxx_spi->lock, flags);
+	mutex_unlock(&fsl_dummy_rx_lock);
 
-	return 0;
+	return fsl_dummy_rx;
 }
 
-
-static void mpc8xxx_spi_cleanup(struct spi_device *spi)
+static void fsl_spi_free_dummy_rx(void)
 {
-	kfree(spi->controller_state);
-}
+	mutex_lock(&fsl_dummy_rx_lock);
 
-static void *mpc8xxx_spi_alloc_dummy_rx(void)
-{
-	mutex_lock(&mpc8xxx_dummy_rx_lock);
-
-	if (!mpc8xxx_dummy_rx)
-		mpc8xxx_dummy_rx = kmalloc(SPI_MRBLR, GFP_KERNEL);
-	if (mpc8xxx_dummy_rx)
-		mpc8xxx_dummy_rx_refcnt++;
-
-	mutex_unlock(&mpc8xxx_dummy_rx_lock);
-
-	return mpc8xxx_dummy_rx;
-}
-
-static void mpc8xxx_spi_free_dummy_rx(void)
-{
-	mutex_lock(&mpc8xxx_dummy_rx_lock);
-
-	switch (mpc8xxx_dummy_rx_refcnt) {
+	switch (fsl_dummy_rx_refcnt) {
 	case 0:
 		WARN_ON(1);
 		break;
 	case 1:
-		kfree(mpc8xxx_dummy_rx);
-		mpc8xxx_dummy_rx = NULL;
+		kfree(fsl_dummy_rx);
+		fsl_dummy_rx = NULL;
 		/* fall through */
 	default:
-		mpc8xxx_dummy_rx_refcnt--;
+		fsl_dummy_rx_refcnt--;
 		break;
 	}
 
-	mutex_unlock(&mpc8xxx_dummy_rx_lock);
+	mutex_unlock(&fsl_dummy_rx_lock);
 }
 
-static unsigned long mpc8xxx_spi_cpm_get_pram(struct mpc8xxx_spi *mspi)
+static unsigned long fsl_spi_cpm_get_pram(struct mpc8xxx_spi *mspi)
 {
 	struct device *dev = mspi->dev;
 	struct device_node *np = dev->of_node;
@@ -863,7 +726,7 @@ static unsigned long mpc8xxx_spi_cpm_get_pram(struct mpc8xxx_spi *mspi)
 	return pram_ofs;
 }
 
-static int mpc8xxx_spi_cpm_init(struct mpc8xxx_spi *mspi)
+static int fsl_spi_cpm_init(struct mpc8xxx_spi *mspi)
 {
 	struct device *dev = mspi->dev;
 	struct device_node *np = dev->of_node;
@@ -875,7 +738,7 @@ static int mpc8xxx_spi_cpm_init(struct mpc8xxx_spi *mspi)
 	if (!(mspi->flags & SPI_CPM_MODE))
 		return 0;
 
-	if (!mpc8xxx_spi_alloc_dummy_rx())
+	if (!fsl_spi_alloc_dummy_rx())
 		return -ENOMEM;
 
 	if (mspi->flags & SPI_QE) {
@@ -896,7 +759,7 @@ static int mpc8xxx_spi_cpm_init(struct mpc8xxx_spi *mspi)
 		}
 	}
 
-	pram_ofs = mpc8xxx_spi_cpm_get_pram(mspi);
+	pram_ofs = fsl_spi_cpm_get_pram(mspi);
 	if (IS_ERR_VALUE(pram_ofs)) {
 		dev_err(dev, "can't allocate spi parameter ram\n");
 		goto err_pram;
@@ -916,7 +779,7 @@ static int mpc8xxx_spi_cpm_init(struct mpc8xxx_spi *mspi)
 		goto err_dummy_tx;
 	}
 
-	mspi->dma_dummy_rx = dma_map_single(dev, mpc8xxx_dummy_rx, SPI_MRBLR,
+	mspi->dma_dummy_rx = dma_map_single(dev, fsl_dummy_rx, SPI_MRBLR,
 					    DMA_FROM_DEVICE);
 	if (dma_mapping_error(dev, mspi->dma_dummy_rx)) {
 		dev_err(dev, "unable to map dummy rx buffer\n");
@@ -954,11 +817,11 @@ err_dummy_tx:
 err_bds:
 	cpm_muram_free(pram_ofs);
 err_pram:
-	mpc8xxx_spi_free_dummy_rx();
+	fsl_spi_free_dummy_rx();
 	return -ENOMEM;
 }
 
-static void mpc8xxx_spi_cpm_free(struct mpc8xxx_spi *mspi)
+static void fsl_spi_cpm_free(struct mpc8xxx_spi *mspi)
 {
 	struct device *dev = mspi->dev;
 
@@ -966,30 +829,22 @@ static void mpc8xxx_spi_cpm_free(struct mpc8xxx_spi *mspi)
 	dma_unmap_single(dev, mspi->dma_dummy_tx, PAGE_SIZE, DMA_TO_DEVICE);
 	cpm_muram_free(cpm_muram_offset(mspi->tx_bd));
 	cpm_muram_free(cpm_muram_offset(mspi->pram));
-	mpc8xxx_spi_free_dummy_rx();
+	fsl_spi_free_dummy_rx();
 }
 
-static const char *mpc8xxx_spi_strmode(unsigned int flags)
+static void fsl_spi_remove(struct mpc8xxx_spi *mspi)
 {
-	if (flags & SPI_QE_CPU_MODE) {
-		return "QE CPU";
-	} else if (flags & SPI_CPM_MODE) {
-		if (flags & SPI_QE)
-			return "QE";
-		else if (flags & SPI_CPM2)
-			return "CPM2";
-		else
-			return "CPM1";
-	}
-	return "CPU";
+	iounmap(mspi->reg_base);
+	fsl_spi_cpm_free(mspi);
 }
 
-static struct spi_master * __devinit
-mpc8xxx_spi_probe(struct device *dev, struct resource *mem, unsigned int irq)
+static struct spi_master * __devinit fsl_spi_probe(struct device *dev,
+		struct resource *mem, unsigned int irq)
 {
 	struct fsl_spi_platform_data *pdata = dev->platform_data;
 	struct spi_master *master;
 	struct mpc8xxx_spi *mpc8xxx_spi;
+	struct fsl_spi_reg *reg_base;
 	u32 regval;
 	int ret = 0;
 
@@ -1001,132 +856,77 @@ mpc8xxx_spi_probe(struct device *dev, struct resource *mem, unsigned int irq)
 
 	dev_set_drvdata(dev, master);
 
-	/* the spi->mode bits understood by this driver: */
-	master->mode_bits = SPI_CPOL | SPI_CPHA | SPI_CS_HIGH
-			| SPI_LSB_FIRST | SPI_LOOP;
+	ret = mpc8xxx_spi_probe(dev, mem, irq);
+	if (ret)
+		goto err_probe;
 
-	master->setup = mpc8xxx_spi_setup;
-	master->transfer = mpc8xxx_spi_transfer;
-	master->cleanup = mpc8xxx_spi_cleanup;
-	master->dev.of_node = dev->of_node;
+	master->setup = fsl_spi_setup;
 
 	mpc8xxx_spi = spi_master_get_devdata(master);
-	mpc8xxx_spi->dev = dev;
-	mpc8xxx_spi->get_rx = mpc8xxx_spi_rx_buf_u8;
-	mpc8xxx_spi->get_tx = mpc8xxx_spi_tx_buf_u8;
-	mpc8xxx_spi->flags = pdata->flags;
-	mpc8xxx_spi->spibrg = pdata->sysclk;
+	mpc8xxx_spi->spi_do_one_msg = fsl_spi_do_one_msg;
+	mpc8xxx_spi->spi_remove = fsl_spi_remove;
+
 
-	ret = mpc8xxx_spi_cpm_init(mpc8xxx_spi);
+	ret = fsl_spi_cpm_init(mpc8xxx_spi);
 	if (ret)
 		goto err_cpm_init;
 
-	mpc8xxx_spi->rx_shift = 0;
-	mpc8xxx_spi->tx_shift = 0;
 	if (mpc8xxx_spi->flags & SPI_QE_CPU_MODE) {
 		mpc8xxx_spi->rx_shift = 16;
 		mpc8xxx_spi->tx_shift = 24;
 	}
 
-	init_completion(&mpc8xxx_spi->done);
-
-	mpc8xxx_spi->base = ioremap(mem->start, resource_size(mem));
-	if (mpc8xxx_spi->base == NULL) {
+	mpc8xxx_spi->reg_base = ioremap(mem->start, resource_size(mem));
+	if (mpc8xxx_spi->reg_base == NULL) {
 		ret = -ENOMEM;
 		goto err_ioremap;
 	}
 
-	mpc8xxx_spi->irq = irq;
-
 	/* Register for SPI Interrupt */
-	ret = request_irq(mpc8xxx_spi->irq, mpc8xxx_spi_irq,
-			  0, "mpc8xxx_spi", mpc8xxx_spi);
+	ret = request_irq(mpc8xxx_spi->irq, fsl_spi_irq,
+			  0, "fsl_spi", mpc8xxx_spi);
 
 	if (ret != 0)
-		goto unmap_io;
+		goto free_irq;
 
-	master->bus_num = pdata->bus_num;
-	master->num_chipselect = pdata->max_chipselect;
+	reg_base = (struct fsl_spi_reg *)mpc8xxx_spi->reg_base;
 
 	/* SPI controller initializations */
-	mpc8xxx_spi_write_reg(&mpc8xxx_spi->base->mode, 0);
-	mpc8xxx_spi_write_reg(&mpc8xxx_spi->base->mask, 0);
-	mpc8xxx_spi_write_reg(&mpc8xxx_spi->base->command, 0);
-	mpc8xxx_spi_write_reg(&mpc8xxx_spi->base->event, 0xffffffff);
+	mpc8xxx_spi_write_reg(&reg_base->mode, 0);
+	mpc8xxx_spi_write_reg(&reg_base->mask, 0);
+	mpc8xxx_spi_write_reg(&reg_base->command, 0);
+	mpc8xxx_spi_write_reg(&reg_base->event, 0xffffffff);
 
 	/* Enable SPI interface */
 	regval = pdata->initial_spmode | SPMODE_INIT_VAL | SPMODE_ENABLE;
 	if (mpc8xxx_spi->flags & SPI_QE_CPU_MODE)
 		regval |= SPMODE_OP;
 
-	mpc8xxx_spi_write_reg(&mpc8xxx_spi->base->mode, regval);
-	spin_lock_init(&mpc8xxx_spi->lock);
-	init_completion(&mpc8xxx_spi->done);
-	INIT_WORK(&mpc8xxx_spi->work, mpc8xxx_spi_work);
-	INIT_LIST_HEAD(&mpc8xxx_spi->queue);
-
-	mpc8xxx_spi->workqueue = create_singlethread_workqueue(
-		dev_name(master->dev.parent));
-	if (mpc8xxx_spi->workqueue == NULL) {
-		ret = -EBUSY;
-		goto free_irq;
-	}
+	mpc8xxx_spi_write_reg(&reg_base->mode, regval);
 
 	ret = spi_register_master(master);
 	if (ret < 0)
 		goto unreg_master;
 
-	dev_info(dev, "at 0x%p (irq = %d), %s mode\n", mpc8xxx_spi->base,
+	dev_info(dev, "at 0x%p (irq = %d), %s mode\n", reg_base,
 		 mpc8xxx_spi->irq, mpc8xxx_spi_strmode(mpc8xxx_spi->flags));
 
 	return master;
 
 unreg_master:
-	destroy_workqueue(mpc8xxx_spi->workqueue);
-free_irq:
 	free_irq(mpc8xxx_spi->irq, mpc8xxx_spi);
-unmap_io:
-	iounmap(mpc8xxx_spi->base);
+free_irq:
+	iounmap(mpc8xxx_spi->reg_base);
 err_ioremap:
-	mpc8xxx_spi_cpm_free(mpc8xxx_spi);
+	fsl_spi_cpm_free(mpc8xxx_spi);
 err_cpm_init:
+err_probe:
 	spi_master_put(master);
 err:
 	return ERR_PTR(ret);
 }
 
-static int __devexit mpc8xxx_spi_remove(struct device *dev)
-{
-	struct mpc8xxx_spi *mpc8xxx_spi;
-	struct spi_master *master;
-
-	master = dev_get_drvdata(dev);
-	mpc8xxx_spi = spi_master_get_devdata(master);
-
-	flush_workqueue(mpc8xxx_spi->workqueue);
-	destroy_workqueue(mpc8xxx_spi->workqueue);
-	spi_unregister_master(master);
-
-	free_irq(mpc8xxx_spi->irq, mpc8xxx_spi);
-	iounmap(mpc8xxx_spi->base);
-	mpc8xxx_spi_cpm_free(mpc8xxx_spi);
-
-	return 0;
-}
-
-struct mpc8xxx_spi_probe_info {
-	struct fsl_spi_platform_data pdata;
-	int *gpios;
-	bool *alow_flags;
-};
-
-static struct mpc8xxx_spi_probe_info *
-to_of_pinfo(struct fsl_spi_platform_data *pdata)
-{
-	return container_of(pdata, struct mpc8xxx_spi_probe_info, pdata);
-}
-
-static void mpc8xxx_spi_cs_control(struct spi_device *spi, bool on)
+static void fsl_spi_cs_control(struct spi_device *spi, bool on)
 {
 	struct device *dev = spi->dev.parent;
 	struct mpc8xxx_spi_probe_info *pinfo = to_of_pinfo(dev->platform_data);
@@ -1137,7 +937,7 @@ static void mpc8xxx_spi_cs_control(struct spi_device *spi, bool on)
 	gpio_set_value(gpio, on ^ alow);
 }
 
-static int of_mpc8xxx_spi_get_chipselects(struct device *dev)
+static int of_fsl_spi_get_chipselects(struct device *dev)
 {
 	struct device_node *np = dev->of_node;
 	struct fsl_spi_platform_data *pdata = dev->platform_data;
@@ -1198,7 +998,7 @@ static int of_mpc8xxx_spi_get_chipselects(struct device *dev)
 	}
 
 	pdata->max_chipselect = ngpios;
-	pdata->cs_control = mpc8xxx_spi_cs_control;
+	pdata->cs_control = fsl_spi_cs_control;
 
 	return 0;
 
@@ -1217,7 +1017,7 @@ err_alloc_flags:
 	return ret;
 }
 
-static int of_mpc8xxx_spi_free_chipselects(struct device *dev)
+static int of_fsl_spi_free_chipselects(struct device *dev)
 {
 	struct fsl_spi_platform_data *pdata = dev->platform_data;
 	struct mpc8xxx_spi_probe_info *pinfo = to_of_pinfo(pdata);
@@ -1236,50 +1036,21 @@ static int of_mpc8xxx_spi_free_chipselects(struct device *dev)
 	return 0;
 }
 
-static int __devinit of_mpc8xxx_spi_probe(struct platform_device *ofdev,
-					  const struct of_device_id *ofid)
+static int __devinit of_fsl_spi_probe(struct platform_device *ofdev,
+					const struct of_device_id *ofid)
 {
 	struct device *dev = &ofdev->dev;
 	struct device_node *np = ofdev->dev.of_node;
-	struct mpc8xxx_spi_probe_info *pinfo;
-	struct fsl_spi_platform_data *pdata;
 	struct spi_master *master;
 	struct resource mem;
 	struct resource irq;
-	const void *prop;
 	int ret = -ENOMEM;
 
-	pinfo = kzalloc(sizeof(*pinfo), GFP_KERNEL);
-	if (!pinfo)
-		return -ENOMEM;
-
-	pdata = &pinfo->pdata;
-	dev->platform_data = pdata;
-
-	/* Allocate bus num dynamically. */
-	pdata->bus_num = -1;
-
-	/* SPI controller is either clocked from QE or SoC clock. */
-	pdata->sysclk = get_brgfreq();
-	if (pdata->sysclk == -1) {
-		pdata->sysclk = fsl_get_sys_freq();
-		if (pdata->sysclk == -1) {
-			ret = -ENODEV;
-			goto err_clk;
-		}
-	}
+	ret = of_mpc8xxx_spi_probe(ofdev, ofid);
+	if (ret)
+		return ret;
 
-	prop = of_get_property(np, "mode", NULL);
-	if (prop && !strcmp(prop, "cpu-qe"))
-		pdata->flags = SPI_QE_CPU_MODE;
-	else if (prop && !strcmp(prop, "qe"))
-		pdata->flags = SPI_CPM_MODE | SPI_QE;
-	else if (of_device_is_compatible(np, "fsl,cpm2-spi"))
-		pdata->flags = SPI_CPM_MODE | SPI_CPM2;
-	else if (of_device_is_compatible(np, "fsl,cpm1-spi"))
-		pdata->flags = SPI_CPM_MODE | SPI_CPM1;
-
-	ret = of_mpc8xxx_spi_get_chipselects(dev);
+	ret = of_fsl_spi_get_chipselects(dev);
 	if (ret)
 		goto err;
 
@@ -1293,7 +1064,7 @@ static int __devinit of_mpc8xxx_spi_probe(struct platform_device *ofdev,
 		goto err;
 	}
 
-	master = mpc8xxx_spi_probe(dev, &mem, irq.start);
+	master = fsl_spi_probe(dev, &mem, irq.start);
 	if (IS_ERR(master)) {
 		ret = PTR_ERR(master);
 		goto err;
@@ -1302,42 +1073,40 @@ static int __devinit of_mpc8xxx_spi_probe(struct platform_device *ofdev,
 	return 0;
 
 err:
-	of_mpc8xxx_spi_free_chipselects(dev);
-err_clk:
-	kfree(pinfo);
+	of_fsl_spi_free_chipselects(dev);
 	return ret;
 }
 
-static int __devexit of_mpc8xxx_spi_remove(struct platform_device *ofdev)
+static int __devexit of_fsl_spi_remove(struct platform_device *ofdev)
 {
 	int ret;
 
 	ret = mpc8xxx_spi_remove(&ofdev->dev);
 	if (ret)
 		return ret;
-	of_mpc8xxx_spi_free_chipselects(&ofdev->dev);
+	of_fsl_spi_free_chipselects(&ofdev->dev);
 	return 0;
 }
 
-static const struct of_device_id of_mpc8xxx_spi_match[] = {
+static const struct of_device_id of_fsl_spi_match[] = {
 	{ .compatible = "fsl,spi" },
-	{},
+	{}
 };
-MODULE_DEVICE_TABLE(of, of_mpc8xxx_spi_match);
+MODULE_DEVICE_TABLE(of, of_fsl_spi_match);
 
-static struct of_platform_driver of_mpc8xxx_spi_driver = {
+static struct of_platform_driver of_fsl_spi_driver = {
 	.driver = {
-		.name = "mpc8xxx_spi",
+		.name = "fsl_spi",
 		.owner = THIS_MODULE,
-		.of_match_table = of_mpc8xxx_spi_match,
+		.of_match_table = of_fsl_spi_match,
 	},
-	.probe		= of_mpc8xxx_spi_probe,
-	.remove		= __devexit_p(of_mpc8xxx_spi_remove),
+	.probe		= of_fsl_spi_probe,
+	.remove		= __devexit_p(of_fsl_spi_remove),
 };
 
 #ifdef CONFIG_MPC832x_RDB
 /*
- * 				XXX XXX XXX
+ * XXX XXX XXX
  * This is "legacy" platform driver, was used by the MPC8323E-RDB boards
  * only. The driver should go away soon, since newer MPC8323E-RDB's device
  * tree can work with OpenFirmware driver. But for now we support old trees
@@ -1360,7 +1129,7 @@ static int __devinit plat_mpc8xxx_spi_probe(struct platform_device *pdev)
 	if (irq <= 0)
 		return -EINVAL;
 
-	master = mpc8xxx_spi_probe(&pdev->dev, mem, irq);
+	master = fsl_spi_probe(&pdev->dev, mem, irq);
 	if (IS_ERR(master))
 		return PTR_ERR(master);
 	return 0;
@@ -1399,21 +1168,20 @@ static void __init legacy_driver_register(void) {}
 static void __exit legacy_driver_unregister(void) {}
 #endif /* CONFIG_MPC832x_RDB */
 
-static int __init mpc8xxx_spi_init(void)
+static int __init fsl_spi_init(void)
 {
 	legacy_driver_register();
-	return of_register_platform_driver(&of_mpc8xxx_spi_driver);
+	return of_register_platform_driver(&of_fsl_spi_driver);
 }
+module_init(fsl_spi_init);
 
-static void __exit mpc8xxx_spi_exit(void)
+static void __exit fsl_spi_exit(void)
 {
-	of_unregister_platform_driver(&of_mpc8xxx_spi_driver);
+	of_unregister_platform_driver(&of_fsl_spi_driver);
 	legacy_driver_unregister();
 }
-
-module_init(mpc8xxx_spi_init);
-module_exit(mpc8xxx_spi_exit);
+module_exit(fsl_spi_exit);
 
 MODULE_AUTHOR("Kumar Gala");
-MODULE_DESCRIPTION("Simple MPC8xxx SPI Driver");
+MODULE_DESCRIPTION("Simple Freescale SPI Driver");
 MODULE_LICENSE("GPL");
-- 
1.6.4

^ permalink raw reply related

* [PATCH v3 3/7] eSPI: add eSPI controller support
From: Mingkai Hu @ 2010-09-30  8:00 UTC (permalink / raw)
  To: linuxppc-dev, spi-devel-general, linux-mtd; +Cc: kumar.gala, Mingkai Hu
In-Reply-To: <1285833646-12006-3-git-send-email-Mingkai.hu@freescale.com>

Add eSPI controller support based on the library code spi_fsl_lib.c.

The eSPI controller is newer controller 85xx/Pxxx devices supported.
There're some differences comparing to the SPI controller:

1. Has different register map and different bit definition
   So leave the code operated the register to the driver code, not
   the common code.

2. Support 4 dedicated chip selects
   The software can't controll the chip selects directly, The SPCOM[CS]
   field is used to select which chip selects is used, and the
   SPCOM[TRANLEN] field is set to tell the controller how long the CS
   signal need to be asserted. So the driver doesn't need the chipselect
   related function when transfering data, just set corresponding register
   fields to controll the chipseclect.

3. Different Transmit/Receive FIFO access register behavior
   For SPI controller, the Tx/Rx FIFO access register can hold only
   one character regardless of the character length, but for eSPI
   controller, the register can hold 4 or 2 characters according to
   the character lengths. Access the Tx/Rx FIFO access register of the
   eSPI controller will shift out/in 4/2 characters one time. For SPI
   subsystem, the command and data are put into different transfers, so
   we need to combine all the transfers to one transfer in order to pass
   the transfer to eSPI controller.

Signed-off-by: Mingkai Hu <Mingkai.hu@freescale.com>
---
v3:
 - Update to the latest kernel base.

 drivers/spi/Kconfig        |    9 +
 drivers/spi/Makefile       |    1 +
 drivers/spi/spi_fsl_espi.c |  642 ++++++++++++++++++++++++++++++++++++++++++++
 drivers/spi/spi_fsl_lib.h  |    1 +
 4 files changed, 653 insertions(+), 0 deletions(-)
 create mode 100644 drivers/spi/spi_fsl_espi.c

diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig
index 79ad06f..f6888af 100644
--- a/drivers/spi/Kconfig
+++ b/drivers/spi/Kconfig
@@ -195,6 +195,15 @@ config SPI_FSL_SPI
 	  MPC83xx platform uses the controller in cpu mode or CPM/QE mode.
 	  MPC8569 uses the controller in QE mode, MPC8610 in cpu mode.
 
+config SPI_FSL_ESPI
+	tristate "Freescale eSPI controller"
+	depends on FSL_SOC
+	select SPI_FSL_LIB
+	help
+	  This enables using the Freescale eSPI controllers in master mode.
+	  From MPC8536, 85xx platform uses the controller, and all P10xx,
+	  P20xx, P30xx,P40xx, P50xx uses this controller.
+
 config SPI_OMAP_UWIRE
 	tristate "OMAP1 MicroWire"
 	depends on ARCH_OMAP1
diff --git a/drivers/spi/Makefile b/drivers/spi/Makefile
index 7974c21..833d17e 100644
--- a/drivers/spi/Makefile
+++ b/drivers/spi/Makefile
@@ -35,6 +35,7 @@ obj-$(CONFIG_SPI_MPC512x_PSC)		+= mpc512x_psc_spi.o
 obj-$(CONFIG_SPI_MPC52xx_PSC)		+= mpc52xx_psc_spi.o
 obj-$(CONFIG_SPI_MPC52xx)		+= mpc52xx_spi.o
 obj-$(CONFIG_SPI_FSL_LIB)		+= spi_fsl_lib.o
+obj-$(CONFIG_SPI_FSL_ESPI)		+= spi_fsl_espi.o
 obj-$(CONFIG_SPI_FSL_SPI)		+= spi_fsl_spi.o
 obj-$(CONFIG_SPI_PPC4xx)		+= spi_ppc4xx.o
 obj-$(CONFIG_SPI_S3C24XX_GPIO)		+= spi_s3c24xx_gpio.o
diff --git a/drivers/spi/spi_fsl_espi.c b/drivers/spi/spi_fsl_espi.c
new file mode 100644
index 0000000..be98148
--- /dev/null
+++ b/drivers/spi/spi_fsl_espi.c
@@ -0,0 +1,642 @@
+/*
+ * Freescale eSPI controller driver.
+ *
+ * Copyright 2010 Freescale Semiconductor, Inc.
+ *
+ * This program is free software; you can redistribute  it and/or modify it
+ * under  the terms of  the GNU General  Public License as published by the
+ * Free Software Foundation;  either version 2 of the  License, or (at your
+ * option) any later version.
+ */
+#include <linux/module.h>
+#include <linux/delay.h>
+#include <linux/irq.h>
+#include <linux/spi/spi.h>
+#include <linux/platform_device.h>
+#include <linux/fsl_devices.h>
+#include <linux/mm.h>
+#include <linux/of.h>
+#include <linux/of_platform.h>
+#include <linux/of_spi.h>
+#include <sysdev/fsl_soc.h>
+#include <linux/interrupt.h>
+#include <linux/err.h>
+
+#include "spi_fsl_lib.h"
+
+/* eSPI Controller registers */
+struct fsl_espi_reg {
+	__be32 mode;		/* 0x000 - eSPI mode register */
+	__be32 event;		/* 0x004 - eSPI event register */
+	__be32 mask;		/* 0x008 - eSPI mask register */
+	__be32 command;		/* 0x00c - eSPI command register */
+	__be32 transmit;	/* 0x010 - eSPI transmit FIFO access register*/
+	__be32 receive;		/* 0x014 - eSPI receive FIFO access register*/
+	u8 res[8];		/* 0x018 - 0x01c reserved */
+	__be32 csmode[4];	/* 0x020 - 0x02c eSPI cs mode register */
+};
+
+/* eSPI Controller mode register definitions */
+#define SPMODE_ENABLE		(1 << 31)
+#define SPMODE_LOOP		(1 << 30)
+#define SPMODE_TXTHR(x)		((x) << 8)
+#define SPMODE_RXTHR(x)		((x) << 0)
+
+/* eSPI Controller CS mode register definitions */
+#define CSMODE_CI_INACTIVEHIGH	(1 << 31)
+#define CSMODE_CP_BEGIN_EDGECLK	(1 << 30)
+#define CSMODE_REV		(1 << 29)
+#define CSMODE_DIV16		(1 << 28)
+#define CSMODE_PM(x)		((x) << 24)
+#define CSMODE_POL_1		(1 << 20)
+#define CSMODE_LEN(x)		((x) << 16)
+#define CSMODE_BEF(x)		((x) << 12)
+#define CSMODE_AFT(x)		((x) << 8)
+#define CSMODE_CG(x)		((x) << 3)
+
+/* Default mode/csmode for eSPI controller */
+#define SPMODE_INIT_VAL (SPMODE_TXTHR(4) | SPMODE_RXTHR(3))
+#define CSMODE_INIT_VAL (CSMODE_POL_1 | CSMODE_BEF(0) \
+		| CSMODE_AFT(0) | CSMODE_CG(1))
+
+/* SPIE register values */
+#define	SPIE_NE		0x00000200	/* Not empty */
+#define	SPIE_NF		0x00000100	/* Not full */
+
+/* SPIM register values */
+#define	SPIM_NE		0x00000200	/* Not empty */
+#define	SPIM_NF		0x00000100	/* Not full */
+#define SPIE_RXCNT(reg)     ((reg >> 24) & 0x3F)
+#define SPIE_TXCNT(reg)     ((reg >> 16) & 0x3F)
+
+/* SPCOM register values */
+#define SPCOM_CS(x)		((x) << 30)
+#define SPCOM_TRANLEN(x)	((x) << 0)
+#define	SPCOM_TRANLEN_MAX	0xFFFF	/* Max transaction length */
+
+static void fsl_espi_change_mode(struct spi_device *spi)
+{
+	struct mpc8xxx_spi *mspi = spi_master_get_devdata(spi->master);
+	struct spi_mpc8xxx_cs *cs = spi->controller_state;
+	struct fsl_espi_reg *reg_base = (struct fsl_espi_reg *)mspi->reg_base;
+	__be32 __iomem *mode;
+	__be32 __iomem *espi_mode = NULL;
+	u32 tmp;
+	unsigned long flags;
+
+	espi_mode = &reg_base->mode;
+	mode = &reg_base->csmode[spi->chip_select];
+
+	/* Turn off IRQs locally to minimize time that SPI is disabled. */
+	local_irq_save(flags);
+
+	/* Turn off SPI unit prior changing mode */
+	tmp = mpc8xxx_spi_read_reg(espi_mode);
+	mpc8xxx_spi_write_reg(espi_mode, tmp & ~SPMODE_ENABLE);
+	mpc8xxx_spi_write_reg(mode, cs->hw_mode);
+	mpc8xxx_spi_write_reg(espi_mode, tmp);
+
+	local_irq_restore(flags);
+}
+
+static u32 fsl_espi_tx_buf_lsb(struct mpc8xxx_spi *mpc8xxx_spi)
+{
+	u32 data;
+	u16 data_h, data_l;
+
+	const u32 *tx = mpc8xxx_spi->tx;
+	if (!tx)
+		return 0;
+
+	data = *tx++ << mpc8xxx_spi->tx_shift;
+	data_l = data & 0xffff;
+	data_h = (data >> 16) & 0xffff;
+	swab16s(&data_l);
+	swab16s(&data_h);
+	data = data_h | data_l;
+
+	mpc8xxx_spi->tx = tx;
+	return data;
+}
+
+static int fsl_espi_setup_transfer(struct spi_device *spi,
+					struct spi_transfer *t)
+{
+	struct mpc8xxx_spi *mpc8xxx_spi;
+	int bits_per_word = 0;
+	u8 pm;
+	u32 hz = 0;
+	struct spi_mpc8xxx_cs	*cs = spi->controller_state;
+
+	mpc8xxx_spi = spi_master_get_devdata(spi->master);
+
+	if (t) {
+		bits_per_word = t->bits_per_word;
+		hz = t->speed_hz;
+	}
+
+	/* spi_transfer level calls that work per-word */
+	if (!bits_per_word)
+		bits_per_word = spi->bits_per_word;
+
+	/* Make sure its a bit width we support [4..16] */
+	if ((bits_per_word < 4) || (bits_per_word > 16))
+		return -EINVAL;
+
+	if (!hz)
+		hz = spi->max_speed_hz;
+
+	cs->rx_shift = 0;
+	cs->tx_shift = 0;
+	cs->get_rx = mpc8xxx_spi_rx_buf_u32;
+	cs->get_tx = mpc8xxx_spi_tx_buf_u32;
+	if (bits_per_word <= 8) {
+		cs->rx_shift = 8 - bits_per_word;
+	} else if (bits_per_word <= 16) {
+		cs->rx_shift = 16 - bits_per_word;
+		if (spi->mode & SPI_LSB_FIRST)
+			cs->get_tx = fsl_espi_tx_buf_lsb;
+	} else
+		return -EINVAL;
+
+	mpc8xxx_spi->rx_shift = cs->rx_shift;
+	mpc8xxx_spi->tx_shift = cs->tx_shift;
+	mpc8xxx_spi->get_rx = cs->get_rx;
+	mpc8xxx_spi->get_tx = cs->get_tx;
+
+	bits_per_word = bits_per_word - 1;
+
+	/* mask out bits we are going to set */
+	cs->hw_mode &= ~(CSMODE_LEN(0xF) | CSMODE_DIV16
+				  | CSMODE_PM(0xF));
+
+	cs->hw_mode |= CSMODE_LEN(bits_per_word);
+
+	if ((mpc8xxx_spi->spibrg / hz) > 64) {
+		cs->hw_mode |= CSMODE_DIV16;
+		pm = (mpc8xxx_spi->spibrg - 1) / (hz * 64) + 1;
+
+		WARN_ONCE(pm > 16, "%s: Requested speed is too low: %d Hz. "
+			  "Will use %d Hz instead.\n", dev_name(&spi->dev),
+			  hz, mpc8xxx_spi->spibrg / 1024);
+		if (pm > 16)
+			pm = 16;
+	} else {
+		pm = (mpc8xxx_spi->spibrg - 1) / (hz * 4) + 1;
+	}
+	if (pm)
+		pm--;
+
+	cs->hw_mode |= CSMODE_PM(pm);
+
+	fsl_espi_change_mode(spi);
+	return 0;
+}
+
+int fsl_espi_cpu_bufs(struct mpc8xxx_spi *mspi, struct spi_transfer *t,
+		unsigned int len)
+{
+	u32 word;
+	struct fsl_espi_reg *reg_base = (struct fsl_espi_reg *)mspi->reg_base;
+
+	mspi->count = len;
+
+	/* enable rx ints */
+	mpc8xxx_spi_write_reg(&reg_base->mask, SPIM_NE);
+
+	/* transmit word */
+	word = mspi->get_tx(mspi);
+	mpc8xxx_spi_write_reg(&reg_base->transmit, word);
+
+	return 0;
+}
+
+static int fsl_espi_bufs(struct spi_device *spi, struct spi_transfer *t,
+			    bool is_dma_mapped)
+{
+	struct mpc8xxx_spi *mpc8xxx_spi = spi_master_get_devdata(spi->master);
+	struct fsl_espi_reg *reg_base;
+	unsigned int len = t->len;
+	u8 bits_per_word;
+	int ret;
+
+	reg_base = (struct fsl_espi_reg *)mpc8xxx_spi->reg_base;
+
+	bits_per_word = spi->bits_per_word;
+	if (t->bits_per_word)
+		bits_per_word = t->bits_per_word;
+
+	mpc8xxx_spi->len = t->len;
+	len = roundup(len, 4) / 4;
+
+	mpc8xxx_spi->tx = t->tx_buf;
+	mpc8xxx_spi->rx = t->rx_buf;
+
+	INIT_COMPLETION(mpc8xxx_spi->done);
+
+	/* Set SPCOM[CS] and SPCOM[TRANLEN] field */
+	if ((t->len - 1) > SPCOM_TRANLEN_MAX) {
+		dev_err(mpc8xxx_spi->dev, "Transaction length (%d)"
+				" beyond the SPCOM[TRANLEN] field\n", t->len);
+		return -EINVAL;
+	}
+	mpc8xxx_spi_write_reg(&reg_base->command,
+		(SPCOM_CS(spi->chip_select) | SPCOM_TRANLEN(t->len - 1)));
+
+	ret = fsl_espi_cpu_bufs(mpc8xxx_spi, t, len);
+	if (ret)
+		return ret;
+
+	wait_for_completion(&mpc8xxx_spi->done);
+
+	/* disable rx ints */
+	mpc8xxx_spi_write_reg(&reg_base->mask, 0);
+
+	return mpc8xxx_spi->count;
+}
+
+static void fsl_espi_do_one_msg(struct spi_message *m)
+{
+	struct spi_device *spi = m->spi;
+	struct mpc8xxx_spi *mspi = spi_master_get_devdata(spi->master);
+	struct spi_message message;
+	struct spi_transfer *t, *first, trans;
+	u8 *local_buf, *rx_buf = NULL;
+	unsigned int n_tx = 0;
+	unsigned int n_rx = 0;
+	int status = 0;
+	int i = 0;
+
+	spi_message_init(&message);
+	memset(&trans, 0, sizeof(trans));
+
+	first = list_first_entry(&m->transfers, struct spi_transfer,
+			transfer_list);
+	list_for_each_entry(t, &m->transfers, transfer_list) {
+		if ((first->bits_per_word != t->bits_per_word) ||
+			(first->speed_hz != t->speed_hz)) {
+			status = -EINVAL;
+			dev_err(mspi->dev, "bits_per_word/speed_hz should be"
+					" same for the same SPI transfer\n");
+			return;
+		}
+
+		trans.speed_hz = t->speed_hz;
+		trans.bits_per_word = t->bits_per_word;
+		trans.delay_usecs = max(first->delay_usecs, t->delay_usecs);
+
+		if (t->tx_buf)
+			n_tx += t->len;
+
+		if (t->rx_buf) {
+			n_rx += t->len;
+			rx_buf = t->rx_buf;
+		}
+	}
+
+	local_buf = kzalloc(n_tx * 2 + roundup(n_rx + n_tx, 4), GFP_KERNEL);
+	if (!local_buf) {
+		status = -ENOMEM;
+		return;
+	}
+
+	list_for_each_entry(t, &m->transfers, transfer_list) {
+		if (t->tx_buf) {
+			memcpy(local_buf + i, t->tx_buf, t->len);
+			i += t->len;
+		}
+	}
+
+	trans.len = n_tx + n_rx;
+	trans.tx_buf = local_buf;
+	trans.rx_buf = local_buf + n_tx;
+	spi_message_add_tail(&trans, &message);
+
+	list_for_each_entry(t, &message.transfers, transfer_list) {
+		if (t->bits_per_word || t->speed_hz) {
+			status = -EINVAL;
+
+			status = fsl_espi_setup_transfer(spi, t);
+			if (status < 0)
+				break;
+		}
+
+		if (t->len)
+			status = fsl_espi_bufs(spi, t, 0);
+		if (status) {
+			status = -EMSGSIZE;
+			break;
+		}
+		m->actual_length += t->len;
+
+		if (rx_buf)
+			memcpy(rx_buf, t->rx_buf + n_tx, n_rx);
+
+		if (t->delay_usecs)
+			udelay(t->delay_usecs);
+	}
+
+	m->status = status;
+	m->complete(m->context);
+
+	fsl_espi_setup_transfer(spi, NULL);
+	kfree(local_buf);
+}
+
+static int fsl_espi_setup(struct spi_device *spi)
+{
+	struct mpc8xxx_spi *mpc8xxx_spi;
+	struct fsl_espi_reg *reg_base;
+	int retval;
+	u32 hw_mode;
+	u32 loop_mode;
+	struct spi_mpc8xxx_cs	*cs = spi->controller_state;
+
+	if (!spi->max_speed_hz)
+		return -EINVAL;
+
+	if (!cs) {
+		cs = kzalloc(sizeof *cs, GFP_KERNEL);
+		if (!cs)
+			return -ENOMEM;
+		spi->controller_state = cs;
+	}
+
+	mpc8xxx_spi = spi_master_get_devdata(spi->master);
+	reg_base = (struct fsl_espi_reg *)mpc8xxx_spi->reg_base;
+
+	hw_mode = cs->hw_mode; /* Save orginal settings */
+	cs->hw_mode = mpc8xxx_spi_read_reg(
+			&reg_base->csmode[spi->chip_select]);
+	/* mask out bits we are going to set */
+	cs->hw_mode &= ~(CSMODE_CP_BEGIN_EDGECLK | CSMODE_CI_INACTIVEHIGH
+			 | CSMODE_REV);
+
+	if (spi->mode & SPI_CPHA)
+		cs->hw_mode |= CSMODE_CP_BEGIN_EDGECLK;
+	if (spi->mode & SPI_CPOL)
+		cs->hw_mode |= CSMODE_CI_INACTIVEHIGH;
+	if (!(spi->mode & SPI_LSB_FIRST))
+		cs->hw_mode |= CSMODE_REV;
+
+	/* Handle the loop mode */
+	loop_mode = mpc8xxx_spi_read_reg(&reg_base->mode);
+	loop_mode &= ~SPMODE_LOOP;
+	if (spi->mode & SPI_LOOP)
+		loop_mode |= SPMODE_LOOP;
+	mpc8xxx_spi_write_reg(&reg_base->mode, loop_mode);
+
+	retval = fsl_espi_setup_transfer(spi, NULL);
+	if (retval < 0) {
+		cs->hw_mode = hw_mode; /* Restore settings */
+		return retval;
+	}
+	return 0;
+}
+
+static void fsl_espi_cpu_irq(struct mpc8xxx_spi *mspi, u32 events)
+{
+	struct fsl_espi_reg *reg_base = (struct fsl_espi_reg *)mspi->reg_base;
+
+	/* We need handle RX first */
+	if (events & SPIE_NE) {
+		u32 rx_data;
+
+		/* Spin until RX is done */
+		while (SPIE_RXCNT(events) < min(4, mspi->len)) {
+			cpu_relax();
+			events = mpc8xxx_spi_read_reg(&reg_base->event);
+		}
+		mspi->len -= 4;
+
+		rx_data = mpc8xxx_spi_read_reg(&reg_base->receive);
+
+		if (mspi->rx)
+			mspi->get_rx(rx_data, mspi);
+	}
+
+	if ((events & SPIE_NF) == 0)
+		/* spin until TX is done */
+		while (((events = mpc8xxx_spi_read_reg(&reg_base->event))
+					& SPIE_NF) == 0)
+			cpu_relax();
+
+	/* Clear the events */
+	mpc8xxx_spi_write_reg(&reg_base->event, events);
+
+	mspi->count -= 1;
+	if (mspi->count) {
+		u32 word = mspi->get_tx(mspi);
+
+		mpc8xxx_spi_write_reg(&reg_base->transmit, word);
+	} else {
+		complete(&mspi->done);
+	}
+}
+
+static irqreturn_t fsl_espi_irq(s32 irq, void *context_data)
+{
+	struct mpc8xxx_spi *mspi = context_data;
+	struct fsl_espi_reg *reg_base = (struct fsl_espi_reg *)mspi->reg_base;
+	irqreturn_t ret = IRQ_NONE;
+	u32 events;
+
+	/* Get interrupt events(tx/rx) */
+	events = mpc8xxx_spi_read_reg(&reg_base->event);
+	if (events)
+		ret = IRQ_HANDLED;
+
+	dev_dbg(mspi->dev, "%s: events %x\n", __func__, events);
+
+	fsl_espi_cpu_irq(mspi, events);
+
+	return ret;
+}
+
+static void fsl_espi_remove(struct mpc8xxx_spi *mspi)
+{
+	iounmap(mspi->reg_base);
+}
+
+static struct spi_master * __devinit fsl_espi_probe(struct device *dev,
+		struct resource *mem, unsigned int irq)
+{
+	struct fsl_spi_platform_data *pdata = dev->platform_data;
+	struct spi_master *master;
+	struct mpc8xxx_spi *mpc8xxx_spi;
+	struct fsl_espi_reg *reg_base;
+	u32 regval;
+	int i, ret = 0;
+
+	master = spi_alloc_master(dev, sizeof(struct mpc8xxx_spi));
+	if (master == NULL) {
+		ret = -ENOMEM;
+		goto err;
+	}
+
+	dev_set_drvdata(dev, master);
+
+	ret = mpc8xxx_spi_probe(dev, mem, irq);
+	if (ret)
+		goto err_probe;
+
+	master->setup = fsl_espi_setup;
+
+	mpc8xxx_spi = spi_master_get_devdata(master);
+	mpc8xxx_spi->spi_do_one_msg = fsl_espi_do_one_msg;
+	mpc8xxx_spi->spi_remove = fsl_espi_remove;
+
+	mpc8xxx_spi->reg_base = ioremap(mem->start, resource_size(mem));
+	if (mpc8xxx_spi->reg_base == NULL) {
+		ret = -ENOMEM;
+		goto err_probe;
+	}
+
+	reg_base = (struct fsl_espi_reg *)mpc8xxx_spi->reg_base;
+
+	/* Register for SPI Interrupt */
+	ret = request_irq(mpc8xxx_spi->irq, fsl_espi_irq,
+			  0, "fsl_espi", mpc8xxx_spi);
+
+	if (ret != 0)
+		goto free_irq;
+
+	if (mpc8xxx_spi->flags & SPI_QE_CPU_MODE) {
+		mpc8xxx_spi->rx_shift = 16;
+		mpc8xxx_spi->tx_shift = 24;
+	}
+
+	/* SPI controller initializations */
+	mpc8xxx_spi_write_reg(&reg_base->mode, 0);
+	mpc8xxx_spi_write_reg(&reg_base->mask, 0);
+	mpc8xxx_spi_write_reg(&reg_base->command, 0);
+	mpc8xxx_spi_write_reg(&reg_base->event, 0xffffffff);
+
+	/* Init eSPI CS mode register */
+	for (i = 0; i < pdata->max_chipselect; i++)
+		mpc8xxx_spi_write_reg(&reg_base->csmode[i], CSMODE_INIT_VAL);
+
+	/* Enable SPI interface */
+	regval = pdata->initial_spmode | SPMODE_INIT_VAL | SPMODE_ENABLE;
+
+	mpc8xxx_spi_write_reg(&reg_base->mode, regval);
+
+	ret = spi_register_master(master);
+	if (ret < 0)
+		goto unreg_master;
+
+	dev_info(dev, "at 0x%p (irq = %d)\n", reg_base, mpc8xxx_spi->irq);
+
+	return master;
+
+unreg_master:
+	free_irq(mpc8xxx_spi->irq, mpc8xxx_spi);
+free_irq:
+	iounmap(mpc8xxx_spi->reg_base);
+err_probe:
+	spi_master_put(master);
+err:
+	return ERR_PTR(ret);
+}
+
+static int of_fsl_espi_get_chipselects(struct device *dev)
+{
+	struct device_node *np = dev->of_node;
+	struct fsl_spi_platform_data *pdata = dev->platform_data;
+	const u32 *prop;
+	int len;
+
+	prop = of_get_property(np, "fsl,espi-num-chipselects", &len);
+	if (!prop || len < sizeof(*prop)) {
+		dev_err(dev, "No 'fsl,espi-num-chipselects' property\n");
+		return -EINVAL;
+	}
+
+	pdata->max_chipselect = *prop;
+	pdata->cs_control = NULL;
+
+	return 0;
+}
+
+static int __devinit of_fsl_espi_probe(struct platform_device *ofdev,
+					const struct of_device_id *ofid)
+{
+	struct device *dev = &ofdev->dev;
+	struct device_node *np = ofdev->dev.of_node;
+	struct spi_master *master;
+	struct resource mem;
+	struct resource irq;
+	int ret = -ENOMEM;
+
+	ret = of_mpc8xxx_spi_probe(ofdev, ofid);
+	if (ret)
+		return ret;
+
+	ret = of_fsl_espi_get_chipselects(dev);
+	if (ret)
+		goto err;
+
+	ret = of_address_to_resource(np, 0, &mem);
+	if (ret)
+		goto err;
+
+	ret = of_irq_to_resource(np, 0, &irq);
+	if (!ret) {
+		ret = -EINVAL;
+		goto err;
+	}
+
+	master = fsl_espi_probe(dev, &mem, irq.start);
+	if (IS_ERR(master)) {
+		ret = PTR_ERR(master);
+		goto err;
+	}
+
+	return 0;
+
+err:
+	return ret;
+}
+
+static int __devexit of_fsl_espi_remove(struct platform_device *dev)
+{
+	int ret;
+
+	ret = mpc8xxx_spi_remove(&dev->dev);
+	if (ret)
+		return ret;
+
+	return 0;
+}
+
+static const struct of_device_id of_fsl_espi_match[] = {
+	{ .compatible = "fsl,mpc8536-espi" },
+	{}
+};
+MODULE_DEVICE_TABLE(of, of_fsl_espi_match);
+
+static struct of_platform_driver fsl_espi_driver = {
+	.driver = {
+		.name = "fsl_espi",
+		.owner = THIS_MODULE,
+		.of_match_table = of_fsl_espi_match,
+	},
+	.probe		= of_fsl_espi_probe,
+	.remove		= __devexit_p(of_fsl_espi_remove),
+};
+
+static int __init fsl_espi_init(void)
+{
+	return of_register_platform_driver(&fsl_espi_driver);
+}
+module_init(fsl_espi_init);
+
+static void __exit fsl_espi_exit(void)
+{
+	of_unregister_platform_driver(&fsl_espi_driver);
+}
+module_exit(fsl_espi_exit);
+
+MODULE_AUTHOR("Mingkai Hu");
+MODULE_DESCRIPTION("Enhanced Freescale SPI Driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/spi/spi_fsl_lib.h b/drivers/spi/spi_fsl_lib.h
index 6ae8949..9c81498 100644
--- a/drivers/spi/spi_fsl_lib.h
+++ b/drivers/spi/spi_fsl_lib.h
@@ -26,6 +26,7 @@ struct mpc8xxx_spi {
 	/* rx & tx bufs from the spi_transfer */
 	const void *tx;
 	void *rx;
+	int len;
 
 	int subblock;
 	struct spi_pram __iomem *pram;
-- 
1.6.4

^ permalink raw reply related

* [PATCH v3 7/7] DTS: add fsl,spi-quirk-trans-len-limit property
From: Mingkai Hu @ 2010-09-30  8:00 UTC (permalink / raw)
  To: linuxppc-dev, spi-devel-general, linux-mtd; +Cc: kumar.gala, Mingkai Hu
In-Reply-To: <1285833646-12006-7-git-send-email-Mingkai.hu@freescale.com>

Signed-off-by: Mingkai Hu <Mingkai.hu@freescale.com>
---
v3:
 - Add "spi-quirk-trans-len-limit" property to the board's dts.

 Documentation/powerpc/dts-bindings/fsl/spi.txt |    3 +++
 arch/powerpc/boot/dts/mpc8536ds.dts            |    1 +
 arch/powerpc/boot/dts/p4080ds.dts              |    1 +
 3 files changed, 5 insertions(+), 0 deletions(-)

diff --git a/Documentation/powerpc/dts-bindings/fsl/spi.txt b/Documentation/powerpc/dts-bindings/fsl/spi.txt
index 777abd7..e1cb84e 100644
--- a/Documentation/powerpc/dts-bindings/fsl/spi.txt
+++ b/Documentation/powerpc/dts-bindings/fsl/spi.txt
@@ -41,6 +41,9 @@ Required properties:
 - interrupts : should contain eSPI interrupt, the device has one interrupt.
 - fsl,espi-num-chipselects : the number of the chipselect signals.
 
+Optional properties:
+- fsl,spi-quirk-trans-len-limit : the max trans length is limited to 0xFFFF.
+
 Example:
 	spi@110000 {
 		#address-cells = <1>;
diff --git a/arch/powerpc/boot/dts/mpc8536ds.dts b/arch/powerpc/boot/dts/mpc8536ds.dts
index a75c10e..6911c76 100644
--- a/arch/powerpc/boot/dts/mpc8536ds.dts
+++ b/arch/powerpc/boot/dts/mpc8536ds.dts
@@ -116,6 +116,7 @@
 			interrupts = <59 0x2>;
 			interrupt-parent = <&mpic>;
 			fsl,espi-num-chipselects = <4>;
+			fsl,spi-quirk-trans-len-limit;
 
 			flash@0 {
 				#address-cells = <1>;
diff --git a/arch/powerpc/boot/dts/p4080ds.dts b/arch/powerpc/boot/dts/p4080ds.dts
index 5b7fc29..060fc45 100644
--- a/arch/powerpc/boot/dts/p4080ds.dts
+++ b/arch/powerpc/boot/dts/p4080ds.dts
@@ -243,6 +243,7 @@
 			interrupts = <53 0x2>;
 			interrupt-parent = <&mpic>;
 			fsl,espi-num-chipselects = <4>;
+			fsl,spi-quirk-trans-len-limit;
 
 			flash@0 {
 				#address-cells = <1>;
-- 
1.6.4

^ permalink raw reply related

* Re: [PATCH v3 6/7] mtd: m25p80: add a read function to read page by page
From: David Brownell @ 2010-09-30 10:46 UTC (permalink / raw)
  To: linuxppc-dev, spi-devel-general, linux-mtd, Mingkai Hu
  Cc: kumar.gala, Mingkai Hu

=0A--- On Thu, 9/30/10, Mingkai Hu <Mingkai.hu@freescale.com> wrote:=0A=0A>=
 From: Mingkai Hu <Mingkai.hu@freescale.com>=0A> Subject: [PATCH v3 6/7] mt=
d: m25p80: add a read function to read page by page=0A=0ANAK.=0A=0AWe went =
over this before.=0A=0A=A0 The bug is in your SPI master controller driver,=
=0Aand the fix there involves mapping large reads=0A into multiple smaller =
reads.=A0 (Example, 128K=0Aread as two 64K reads instead of one of 128K.=0A=
=0AIt's *NEVER* appropriate to commit to patching all=0Aupper level drivers=
 in order to work around bugs=0Ain lower level ones.=A0 The set of such upp=
er level=0Adrivers that may need bugfixing is quite large,=0Amost will neve=
r be used with your buggy controller=0Adriver, and all such patches will ne=
ed testing (but=0Athe test resources are probably not available).=0A=0AWhat=
ever SPI controller driver you're working with=0Ais clearly buggy ... but n=
ot unfixably so.=0A=0ADO NOT head down the path of requiring every SPI=0Ade=
vice driver to include workarounds for this odd=0Alittle SPI master driver =
bug.=0A=0A- Dave=0A=0A=0A=0A=0A=0A

^ permalink raw reply

* Re: [PATCH v3 6/7] mtd: m25p80: add a read function to read page by page
From: Grant Likely @ 2010-09-30 14:16 UTC (permalink / raw)
  To: David Brownell
  Cc: linuxppc-dev, Mingkai Hu, linux-mtd, kumar.gala,
	spi-devel-general
In-Reply-To: <841976.76219.qm@web180306.mail.gq1.yahoo.com>

On Thu, Sep 30, 2010 at 7:46 PM, David Brownell <david-b@pacbell.net> wrote:
>
> --- On Thu, 9/30/10, Mingkai Hu <Mingkai.hu@freescale.com> wrote:
>
>> From: Mingkai Hu <Mingkai.hu@freescale.com>
>> Subject: [PATCH v3 6/7] mtd: m25p80: add a read function to read page by page
>
> NAK.
>
> We went over this before.

Yes, I agree with David on this.  If large transfers don't work, then
it is the SPI master driver that is buggy.

g.

^ permalink raw reply

* Re: [PATCH v3 6/7] mtd: m25p80: add a read function to read page by page
From: Grant Likely @ 2010-09-30 14:41 UTC (permalink / raw)
  To: David Brownell
  Cc: linuxppc-dev, Mingkai Hu, linux-mtd, kumar.gala,
	spi-devel-general
In-Reply-To: <AANLkTinBDdEyMDt3KoauNdmTCz1ZAJ5mZ23iMWiwsUm4@mail.gmail.com>

On Thu, Sep 30, 2010 at 11:16 PM, Grant Likely
<grant.likely@secretlab.ca> wrote:
> On Thu, Sep 30, 2010 at 7:46 PM, David Brownell <david-b@pacbell.net> wro=
te:
>>
>> --- On Thu, 9/30/10, Mingkai Hu <Mingkai.hu@freescale.com> wrote:
>>
>>> From: Mingkai Hu <Mingkai.hu@freescale.com>
>>> Subject: [PATCH v3 6/7] mtd: m25p80: add a read function to read page b=
y page
>>
>> NAK.
>>
>> We went over this before.
>
> Yes, I agree with David on this. =A0If large transfers don't work, then
> it is the SPI master driver that is buggy.

By the way, does this fix your problem?

https://patchwork.kernel.org/patch/184752/

g.

^ permalink raw reply

* Re: [PATCH v3 6/7] mtd: m25p80: add a read function to read page by page
From: Anton Vorontsov @ 2010-09-30 15:06 UTC (permalink / raw)
  To: Grant Likely
  Cc: kumar.gala, David Brownell, linuxppc-dev, linux-mtd,
	spi-devel-general, Mingkai Hu
In-Reply-To: <AANLkTi=GydTyhf3TOzCZOtrme1Jh0UmNDHqTVP59q=LH@mail.gmail.com>

On Thu, Sep 30, 2010 at 11:41:40PM +0900, Grant Likely wrote:
> On Thu, Sep 30, 2010 at 11:16 PM, Grant Likely
> <grant.likely@secretlab.ca> wrote:
> > On Thu, Sep 30, 2010 at 7:46 PM, David Brownell <david-b@pacbell.net> wrote:
> >>
> >> --- On Thu, 9/30/10, Mingkai Hu <Mingkai.hu@freescale.com> wrote:
> >>
> >>> From: Mingkai Hu <Mingkai.hu@freescale.com>
> >>> Subject: [PATCH v3 6/7] mtd: m25p80: add a read function to read page by page
> >>
> >> NAK.
> >>
> >> We went over this before.
> >
> > Yes, I agree with David on this.  If large transfers don't work, then
> > it is the SPI master driver that is buggy.
> 
> By the way, does this fix your problem?
> 
> https://patchwork.kernel.org/patch/184752/

It shouldn't. AFAIK, eSPI is PIO-only controller, and the overrun
fix is for the DMA mode.

Thanks,

p.s. Btw, in patch 3/7, is_dma_mapped argument of fsl_espi_bufs()
is unneeded.

-- 
Anton Vorontsov
email: cbouatmailru@gmail.com
irc://irc.freenode.net/bd2

^ permalink raw reply

* Re: [PATCH 0/8] v2 De-Couple sysfs memory directories from memory sections
From: Nathan Fontenot @ 2010-09-30 15:17 UTC (permalink / raw)
  To: Robin Holt
  Cc: linuxppc-dev, Greg KH, linux-kernel, Dave Hansen, linux-mm,
	KAMEZAWA Hiroyuki
In-Reply-To: <20100929192830.GK14068@sgi.com>

On 09/29/2010 02:28 PM, Robin Holt wrote:
> On Tue, Sep 28, 2010 at 01:17:33PM -0500, Nathan Fontenot wrote:
>> On 09/28/2010 07:38 AM, Robin Holt wrote:
>>> I was tasked with looking at a slowdown in similar sized SGI machines
>>> booting x86_64.  Jack Steiner had already looked into the memory_dev_init.
>>> I was looking at link_mem_sections().
>>>
>>> I made a dramatic improvement on a 16TB machine in that function by
>>> merely caching the most recent memory section and checking to see if
>>> the next memory section happens to be the subsequent in the linked list
>>> of kobjects.
>>>
>>> That simple cache reduced the time for link_mem_sections from 1 hour 27
>>> minutes down to 46 seconds.
>>
>> Nice!
>>
>>>
>>> I would like to propose we implement something along those lines also,
>>> but I am currently swamped.  I can probably get you a patch tomorrow
>>> afternoon that applies at the end of this set.
>>
>> Should this be done as a separate patch?  This patch set concentrates on
>> updates to the memory code with the node updates only being done due to the
>> memory changes.
>>
>> I think its a good idea to do the caching and have no problem adding on to
>> this patchset if no one else has any objections.
> 
> I am sorry.  I had meant to include you on the Cc: list.  I just posted a
> set of patches (3 small patches) which implement the cache most recent bit
> I aluded to above.  Search for a subject of "Speed up link_mem_sections
> during boot" and you will find them.  I did add you to the Cc: list for
> the next time I end up sending the set.
> 
> My next task is to implement a x86_64 SGI UV specific chunk of code
> to memory_block_size_bytes().  Would you consider adding that to your
> patch set?  I expect to have that either later today or early tomorrow.
> 

No problem. I'm putting together a new patch set with updates from all of
the comments now so go ahead and send it to me when you have it ready.

-Nathan

^ permalink raw reply

* Re: [PATCH 0/8] v2 De-Couple sysfs memory directories from memory sections
From: Robin Holt @ 2010-09-30 16:39 UTC (permalink / raw)
  To: Nathan Fontenot
  Cc: linux-mm, Greg KH, linux-kernel, Dave Hansen, linuxppc-dev,
	Robin Holt, H. Peter Anvin, Ingo Molnar, Thomas Gleixner,
	KAMEZAWA Hiroyuki
In-Reply-To: <20100929192830.GK14068@sgi.com>

On Wed, Sep 29, 2010 at 02:28:30PM -0500, Robin Holt wrote:
> On Tue, Sep 28, 2010 at 01:17:33PM -0500, Nathan Fontenot wrote:
...
> My next task is to implement a x86_64 SGI UV specific chunk of code
> to memory_block_size_bytes().  Would you consider adding that to your
> patch set?  I expect to have that either later today or early tomorrow.

The patch is below.

I left things at a u32, but I would really like it if you changed to an
unsigned long and adjusted my patch for me.

Thanks,
Robin

------------------------------------------------------------------------
Subject: [Patch] Implement memory_block_size_bytes for x86_64 when CONFIG_X86_UV


Nathan Fontenot has implemented a patch set for large memory configuration
systems which will combine drivers/base/memory.c memory sections
together into memory blocks with the default behavior being
unchanged from the current behavior.

In his patch set, he implements a memory_block_size_bytes() function
for PPC.  This is the equivalent patch for x86_64 when it has
CONFIG_X86_UV set.

Signed-off-by: Robin Holt <holt@sgi.com>
Signed-off-by: Jack Steiner <steiner@sgi.com>
To: Nathan Fontenot <nfont@austin.ibm.com>
Cc: Ingo Molnar <mingo@elte.hu>
Cc: Thomas Gleixner <tglx@linutronix.de>
Cc: "H. Peter Anvin" <hpa@zytor.com>
Cc: lkml <linux-kernel@vger.kernel.org>

---

 arch/x86/mm/init_64.c |   15 +++++++++++++++
 1 file changed, 15 insertions(+)

Index: memory_block/arch/x86/mm/init_64.c
===================================================================
--- memory_block.orig/arch/x86/mm/init_64.c	2010-09-29 14:46:50.711824616 -0500
+++ memory_block/arch/x86/mm/init_64.c	2010-09-29 14:46:55.683997672 -0500
@@ -50,6 +50,7 @@
 #include <asm/numa.h>
 #include <asm/cacheflush.h>
 #include <asm/init.h>
+#include <asm/uv/uv.h>
 #include <linux/bootmem.h>
 
 static unsigned long dma_reserve __initdata;
@@ -928,6 +929,20 @@ const char *arch_vma_name(struct vm_area
 	return NULL;
 }
 
+#ifdef CONFIG_X86_UV
+#define MIN_MEMORY_BLOCK_SIZE   (1 << SECTION_SIZE_BITS)
+
+u32 memory_block_size_bytes(void)
+{
+	if (is_uv_system()) {
+		printk("UV: memory block size 2GB\n");
+		return 2UL * 1024 * 1024 * 1024;
+	}
+	return MIN_MEMORY_BLOCK_SIZE;
+}
+#endif
+
+
 #ifdef CONFIG_SPARSEMEM_VMEMMAP
 /*
  * Initialise the sparsemem vmemmap using huge-pages at the PMD level.

^ permalink raw reply

* [PATCH] PPC4xx: ADMA separating SoC specific functions
From: tmarri @ 2010-09-30 16:55 UTC (permalink / raw)
  To: linuxppc-dev; +Cc: tmarri, yur, linux-raid, linux-crypto, dan.j.williams

From: Tirumala Marri <tmarri@apm.com>

This patch separates the SoC specific functions and moved
to different files.

The reason for ppc440spe-adma.h is to define in-line functions which
are called by both adma.c and ppc440spe-adma.c . 

Where as ppc440spe-adma.c is to define functions are completely
completely dependent on 440spe, also which are too big to define
as in-line functions.

Signed-off-by: Tirumala R Marri <tmarri@apm.com>
Acked-by: Yuri Tikhonov <yur@emcraft.com>
CC:  Dan Williams <dan.j.williams@intel.com>
CC:  Josh Boyer <jwboyer@linux.vnet.ibm.com>
---
 drivers/dma/ppc4xx/Makefile         |    2 +-
 drivers/dma/ppc4xx/adma.c           | 3913 +----------------------------------
 drivers/dma/ppc4xx/ppc440spe-adma.c | 1658 +++++++++++++++
 drivers/dma/ppc4xx/ppc440spe-adma.h | 2391 +++++++++++++++++++++
 4 files changed, 4123 insertions(+), 3841 deletions(-)
 create mode 100644 drivers/dma/ppc4xx/ppc440spe-adma.c
 create mode 100644 drivers/dma/ppc4xx/ppc440spe-adma.h

diff --git a/drivers/dma/ppc4xx/Makefile b/drivers/dma/ppc4xx/Makefile
index b3d259b..ad9265a 100644
--- a/drivers/dma/ppc4xx/Makefile
+++ b/drivers/dma/ppc4xx/Makefile
@@ -1 +1 @@
-obj-$(CONFIG_AMCC_PPC440SPE_ADMA) += adma.o
+obj-$(CONFIG_AMCC_PPC440SPE_ADMA) += ppc440spe-adma.o adma.o
diff --git a/drivers/dma/ppc4xx/adma.c b/drivers/dma/ppc4xx/adma.c
index 0d58a4a..a1053cb 100644
--- a/drivers/dma/ppc4xx/adma.c
+++ b/drivers/dma/ppc4xx/adma.c
@@ -46,149 +46,40 @@
 #include <asm/dcr.h>
 #include <asm/dcr-regs.h>
 #include "adma.h"
-
-enum ppc_adma_init_code {
-	PPC_ADMA_INIT_OK = 0,
-	PPC_ADMA_INIT_MEMRES,
-	PPC_ADMA_INIT_MEMREG,
-	PPC_ADMA_INIT_ALLOC,
-	PPC_ADMA_INIT_COHERENT,
-	PPC_ADMA_INIT_CHANNEL,
-	PPC_ADMA_INIT_IRQ1,
-	PPC_ADMA_INIT_IRQ2,
-	PPC_ADMA_INIT_REGISTER
-};
-
-static char *ppc_adma_errors[] = {
-	[PPC_ADMA_INIT_OK] = "ok",
-	[PPC_ADMA_INIT_MEMRES] = "failed to get memory resource",
-	[PPC_ADMA_INIT_MEMREG] = "failed to request memory region",
-	[PPC_ADMA_INIT_ALLOC] = "failed to allocate memory for adev "
-				"structure",
-	[PPC_ADMA_INIT_COHERENT] = "failed to allocate coherent memory for "
-				   "hardware descriptors",
-	[PPC_ADMA_INIT_CHANNEL] = "failed to allocate memory for channel",
-	[PPC_ADMA_INIT_IRQ1] = "failed to request first irq",
-	[PPC_ADMA_INIT_IRQ2] = "failed to request second irq",
-	[PPC_ADMA_INIT_REGISTER] = "failed to register dma async device",
-};
-
-static enum ppc_adma_init_code
-ppc440spe_adma_devices[PPC440SPE_ADMA_ENGINES_NUM];
-
-struct ppc_dma_chan_ref {
-	struct dma_chan *chan;
-	struct list_head node;
-};
-
-/* The list of channels exported by ppc440spe ADMA */
-struct list_head
-ppc440spe_adma_chan_list = LIST_HEAD_INIT(ppc440spe_adma_chan_list);
-
-/* This flag is set when want to refetch the xor chain in the interrupt
- * handler
- */
-static u32 do_xor_refetch;
-
-/* Pointer to DMA0, DMA1 CP/CS FIFO */
-static void *ppc440spe_dma_fifo_buf;
-
-/* Pointers to last submitted to DMA0, DMA1 CDBs */
-static struct ppc440spe_adma_desc_slot *chan_last_sub[3];
-static struct ppc440spe_adma_desc_slot *chan_first_cdb[3];
-
-/* Pointer to last linked and submitted xor CB */
-static struct ppc440spe_adma_desc_slot *xor_last_linked;
-static struct ppc440spe_adma_desc_slot *xor_last_submit;
-
-/* This array is used in data-check operations for storing a pattern */
-static char ppc440spe_qword[16];
-
-static atomic_t ppc440spe_adma_err_irq_ref;
-static dcr_host_t ppc440spe_mq_dcr_host;
-static unsigned int ppc440spe_mq_dcr_len;
-
-/* Since RXOR operations use the common register (MQ0_CF2H) for setting-up
- * the block size in transactions, then we do not allow to activate more than
- * only one RXOR transactions simultaneously. So use this var to store
- * the information about is RXOR currently active (PPC440SPE_RXOR_RUN bit is
- * set) or not (PPC440SPE_RXOR_RUN is clear).
- */
-static unsigned long ppc440spe_rxor_state;
-
-/* These are used in enable & check routines
- */
-static u32 ppc440spe_r6_enabled;
-static struct ppc440spe_adma_chan *ppc440spe_r6_tchan;
-static struct completion ppc440spe_r6_test_comp;
-
-static int ppc440spe_adma_dma2rxor_prep_src(
-		struct ppc440spe_adma_desc_slot *desc,
-		struct ppc440spe_rxor *cursor, int index,
-		int src_cnt, u32 addr);
-static void ppc440spe_adma_dma2rxor_set_src(
-		struct ppc440spe_adma_desc_slot *desc,
-		int index, dma_addr_t addr);
-static void ppc440spe_adma_dma2rxor_set_mult(
-		struct ppc440spe_adma_desc_slot *desc,
-		int index, u8 mult);
-
-#ifdef ADMA_LL_DEBUG
-#define ADMA_LL_DBG(x) ({ if (1) x; 0; })
-#else
-#define ADMA_LL_DBG(x) ({ if (0) x; 0; })
-#endif
-
-static void print_cb(struct ppc440spe_adma_chan *chan, void *block)
-{
-	struct dma_cdb *cdb;
-	struct xor_cb *cb;
-	int i;
-
-	switch (chan->device->id) {
-	case 0:
-	case 1:
-		cdb = block;
-
-		pr_debug("CDB at %p [%d]:\n"
-			"\t attr 0x%02x opc 0x%02x cnt 0x%08x\n"
-			"\t sg1u 0x%08x sg1l 0x%08x\n"
-			"\t sg2u 0x%08x sg2l 0x%08x\n"
-			"\t sg3u 0x%08x sg3l 0x%08x\n",
-			cdb, chan->device->id,
-			cdb->attr, cdb->opc, le32_to_cpu(cdb->cnt),
-			le32_to_cpu(cdb->sg1u), le32_to_cpu(cdb->sg1l),
-			le32_to_cpu(cdb->sg2u), le32_to_cpu(cdb->sg2l),
-			le32_to_cpu(cdb->sg3u), le32_to_cpu(cdb->sg3l)
-		);
-		break;
-	case 2:
-		cb = block;
-
-		pr_debug("CB at %p [%d]:\n"
-			"\t cbc 0x%08x cbbc 0x%08x cbs 0x%08x\n"
-			"\t cbtah 0x%08x cbtal 0x%08x\n"
-			"\t cblah 0x%08x cblal 0x%08x\n",
-			cb, chan->device->id,
-			cb->cbc, cb->cbbc, cb->cbs,
-			cb->cbtah, cb->cbtal,
-			cb->cblah, cb->cblal);
-		for (i = 0; i < 16; i++) {
-			if (i && !cb->ops[i].h && !cb->ops[i].l)
-				continue;
-			pr_debug("\t ops[%2d]: h 0x%08x l 0x%08x\n",
-				i, cb->ops[i].h, cb->ops[i].l);
-		}
-		break;
-	}
-}
-
-static void print_cb_list(struct ppc440spe_adma_chan *chan,
-			  struct ppc440spe_adma_desc_slot *iter)
-{
-	for (; iter; iter = iter->hw_next)
-		print_cb(chan, iter->hw_desc);
-}
+#include "ppc440spe-adma.h"
+
+struct dma_async_tx_descriptor
+*ppc440spe_adma_prep_dma_pq(struct dma_chan *chan,
+			       dma_addr_t * dst,
+			       dma_addr_t * src,
+			       unsigned int src_cnt,
+			       const unsigned char *scf,
+			       size_t len,
+			       unsigned long flags);
+struct dma_async_tx_descriptor
+*ppc440spe_adma_prep_dma_pqzero_sum(struct dma_chan *chan,
+				       dma_addr_t * pq,
+				       dma_addr_t * src,
+				       unsigned int src_cnt,
+				       const unsigned char *scf,
+				       size_t len,
+				       enum sum_check_flags *pqres,
+				       unsigned long flags);
+struct dma_async_tx_descriptor
+*ppc440spe_adma_prep_dma_xor_zero_sum(struct dma_chan *chan,
+					dma_addr_t * src,
+					unsigned int src_cnt,
+					size_t len,
+					enum sum_check_flags *result,
+					unsigned long flags);
+void ppc440spe_adma_set_capabilities(struct ppc440spe_adma_device *adev);
+int ppc440spe_adma_setup_irqs(struct ppc440spe_adma_device *adev,
+				  struct ppc440spe_adma_chan *chan, int *initcode);
+void ppc440spe_adma_release_irqs(struct ppc440spe_adma_device *adev,
+				     struct ppc440spe_adma_chan *chan);
+int __devexit ppc440spe_adma_remove(struct platform_device *ofdev);
+void __exit ppc440spe_adma_exit(void);
+__init int ppc440spe_adma_hw_init(void);
 
 static void prep_dma_xor_dbg(int id, dma_addr_t dst, dma_addr_t *src,
 			     unsigned int src_cnt)
@@ -200,7 +91,7 @@ static void prep_dma_xor_dbg(int id, dma_addr_t dst, dma_addr_t *src,
 		pr_debug("\t0x%016llx ", src[i]);
 	pr_debug("dst:\n\t0x%016llx\n", dst);
 }
-
+#if 0
 static void prep_dma_pq_dbg(int id, dma_addr_t *dst, dma_addr_t *src,
 			    unsigned int src_cnt)
 {
@@ -213,8 +104,9 @@ static void prep_dma_pq_dbg(int id, dma_addr_t *dst, dma_addr_t *src,
 	for (i = 0; i < 2; i++)
 		pr_debug("\t0x%016llx ", dst[i]);
 }
+#endif
 
-static void prep_dma_pqzero_sum_dbg(int id, dma_addr_t *src,
+void prep_dma_pqzero_sum_dbg(int id, dma_addr_t *src,
 				    unsigned int src_cnt,
 				    const unsigned char *scf)
 {
@@ -237,782 +129,11 @@ static void prep_dma_pqzero_sum_dbg(int id, dma_addr_t *src,
 /******************************************************************************
  * Command (Descriptor) Blocks low-level routines
  ******************************************************************************/
-/**
- * ppc440spe_desc_init_interrupt - initialize the descriptor for INTERRUPT
- * pseudo operation
- */
-static void ppc440spe_desc_init_interrupt(struct ppc440spe_adma_desc_slot *desc,
-					  struct ppc440spe_adma_chan *chan)
-{
-	struct xor_cb *p;
-
-	switch (chan->device->id) {
-	case PPC440SPE_XOR_ID:
-		p = desc->hw_desc;
-		memset(desc->hw_desc, 0, sizeof(struct xor_cb));
-		/* NOP with Command Block Complete Enable */
-		p->cbc = XOR_CBCR_CBCE_BIT;
-		break;
-	case PPC440SPE_DMA0_ID:
-	case PPC440SPE_DMA1_ID:
-		memset(desc->hw_desc, 0, sizeof(struct dma_cdb));
-		/* NOP with interrupt */
-		set_bit(PPC440SPE_DESC_INT, &desc->flags);
-		break;
-	default:
-		printk(KERN_ERR "Unsupported id %d in %s\n", chan->device->id,
-				__func__);
-		break;
-	}
-}
-
-/**
- * ppc440spe_desc_init_null_xor - initialize the descriptor for NULL XOR
- * pseudo operation
- */
-static void ppc440spe_desc_init_null_xor(struct ppc440spe_adma_desc_slot *desc)
-{
-	memset(desc->hw_desc, 0, sizeof(struct xor_cb));
-	desc->hw_next = NULL;
-	desc->src_cnt = 0;
-	desc->dst_cnt = 1;
-}
-
-/**
- * ppc440spe_desc_init_xor - initialize the descriptor for XOR operation
- */
-static void ppc440spe_desc_init_xor(struct ppc440spe_adma_desc_slot *desc,
-					 int src_cnt, unsigned long flags)
-{
-	struct xor_cb *hw_desc = desc->hw_desc;
-
-	memset(desc->hw_desc, 0, sizeof(struct xor_cb));
-	desc->hw_next = NULL;
-	desc->src_cnt = src_cnt;
-	desc->dst_cnt = 1;
-
-	hw_desc->cbc = XOR_CBCR_TGT_BIT | src_cnt;
-	if (flags & DMA_PREP_INTERRUPT)
-		/* Enable interrupt on completion */
-		hw_desc->cbc |= XOR_CBCR_CBCE_BIT;
-}
-
-/**
- * ppc440spe_desc_init_dma2pq - initialize the descriptor for PQ
- * operation in DMA2 controller
- */
-static void ppc440spe_desc_init_dma2pq(struct ppc440spe_adma_desc_slot *desc,
-		int dst_cnt, int src_cnt, unsigned long flags)
-{
-	struct xor_cb *hw_desc = desc->hw_desc;
-
-	memset(desc->hw_desc, 0, sizeof(struct xor_cb));
-	desc->hw_next = NULL;
-	desc->src_cnt = src_cnt;
-	desc->dst_cnt = dst_cnt;
-	memset(desc->reverse_flags, 0, sizeof(desc->reverse_flags));
-	desc->descs_per_op = 0;
-
-	hw_desc->cbc = XOR_CBCR_TGT_BIT;
-	if (flags & DMA_PREP_INTERRUPT)
-		/* Enable interrupt on completion */
-		hw_desc->cbc |= XOR_CBCR_CBCE_BIT;
-}
-
 #define DMA_CTRL_FLAGS_LAST	DMA_PREP_FENCE
 #define DMA_PREP_ZERO_P		(DMA_CTRL_FLAGS_LAST << 1)
 #define DMA_PREP_ZERO_Q		(DMA_PREP_ZERO_P << 1)
 
 /**
- * ppc440spe_desc_init_dma01pq - initialize the descriptors for PQ operation
- * with DMA0/1
- */
-static void ppc440spe_desc_init_dma01pq(struct ppc440spe_adma_desc_slot *desc,
-				int dst_cnt, int src_cnt, unsigned long flags,
-				unsigned long op)
-{
-	struct dma_cdb *hw_desc;
-	struct ppc440spe_adma_desc_slot *iter;
-	u8 dopc;
-
-	/* Common initialization of a PQ descriptors chain */
-	set_bits(op, &desc->flags);
-	desc->src_cnt = src_cnt;
-	desc->dst_cnt = dst_cnt;
-
-	/* WXOR MULTICAST if both P and Q are being computed
-	 * MV_SG1_SG2 if Q only
-	 */
-	dopc = (desc->dst_cnt == DMA_DEST_MAX_NUM) ?
-		DMA_CDB_OPC_MULTICAST : DMA_CDB_OPC_MV_SG1_SG2;
-
-	list_for_each_entry(iter, &desc->group_list, chain_node) {
-		hw_desc = iter->hw_desc;
-		memset(iter->hw_desc, 0, sizeof(struct dma_cdb));
-
-		if (likely(!list_is_last(&iter->chain_node,
-				&desc->group_list))) {
-			/* set 'next' pointer */
-			iter->hw_next = list_entry(iter->chain_node.next,
-				struct ppc440spe_adma_desc_slot, chain_node);
-			clear_bit(PPC440SPE_DESC_INT, &iter->flags);
-		} else {
-			/* this is the last descriptor.
-			 * this slot will be pasted from ADMA level
-			 * each time it wants to configure parameters
-			 * of the transaction (src, dst, ...)
-			 */
-			iter->hw_next = NULL;
-			if (flags & DMA_PREP_INTERRUPT)
-				set_bit(PPC440SPE_DESC_INT, &iter->flags);
-			else
-				clear_bit(PPC440SPE_DESC_INT, &iter->flags);
-		}
-	}
-
-	/* Set OPS depending on WXOR/RXOR type of operation */
-	if (!test_bit(PPC440SPE_DESC_RXOR, &desc->flags)) {
-		/* This is a WXOR only chain:
-		 * - first descriptors are for zeroing destinations
-		 *   if PPC440SPE_ZERO_P/Q set;
-		 * - descriptors remained are for GF-XOR operations.
-		 */
-		iter = list_first_entry(&desc->group_list,
-					struct ppc440spe_adma_desc_slot,
-					chain_node);
-
-		if (test_bit(PPC440SPE_ZERO_P, &desc->flags)) {
-			hw_desc = iter->hw_desc;
-			hw_desc->opc = DMA_CDB_OPC_MV_SG1_SG2;
-			iter = list_first_entry(&iter->chain_node,
-					struct ppc440spe_adma_desc_slot,
-					chain_node);
-		}
-
-		if (test_bit(PPC440SPE_ZERO_Q, &desc->flags)) {
-			hw_desc = iter->hw_desc;
-			hw_desc->opc = DMA_CDB_OPC_MV_SG1_SG2;
-			iter = list_first_entry(&iter->chain_node,
-					struct ppc440spe_adma_desc_slot,
-					chain_node);
-		}
-
-		list_for_each_entry_from(iter, &desc->group_list, chain_node) {
-			hw_desc = iter->hw_desc;
-			hw_desc->opc = dopc;
-		}
-	} else {
-		/* This is either RXOR-only or mixed RXOR/WXOR */
-
-		/* The first 1 or 2 slots in chain are always RXOR,
-		 * if need to calculate P & Q, then there are two
-		 * RXOR slots; if only P or only Q, then there is one
-		 */
-		iter = list_first_entry(&desc->group_list,
-					struct ppc440spe_adma_desc_slot,
-					chain_node);
-		hw_desc = iter->hw_desc;
-		hw_desc->opc = DMA_CDB_OPC_MV_SG1_SG2;
-
-		if (desc->dst_cnt == DMA_DEST_MAX_NUM) {
-			iter = list_first_entry(&iter->chain_node,
-						struct ppc440spe_adma_desc_slot,
-						chain_node);
-			hw_desc = iter->hw_desc;
-			hw_desc->opc = DMA_CDB_OPC_MV_SG1_SG2;
-		}
-
-		/* The remaining descs (if any) are WXORs */
-		if (test_bit(PPC440SPE_DESC_WXOR, &desc->flags)) {
-			iter = list_first_entry(&iter->chain_node,
-						struct ppc440spe_adma_desc_slot,
-						chain_node);
-			list_for_each_entry_from(iter, &desc->group_list,
-						chain_node) {
-				hw_desc = iter->hw_desc;
-				hw_desc->opc = dopc;
-			}
-		}
-	}
-}
-
-/**
- * ppc440spe_desc_init_dma01pqzero_sum - initialize the descriptor
- * for PQ_ZERO_SUM operation
- */
-static void ppc440spe_desc_init_dma01pqzero_sum(
-				struct ppc440spe_adma_desc_slot *desc,
-				int dst_cnt, int src_cnt)
-{
-	struct dma_cdb *hw_desc;
-	struct ppc440spe_adma_desc_slot *iter;
-	int i = 0;
-	u8 dopc = (dst_cnt == 2) ? DMA_CDB_OPC_MULTICAST :
-				   DMA_CDB_OPC_MV_SG1_SG2;
-	/*
-	 * Initialize starting from 2nd or 3rd descriptor dependent
-	 * on dst_cnt. First one or two slots are for cloning P
-	 * and/or Q to chan->pdest and/or chan->qdest as we have
-	 * to preserve original P/Q.
-	 */
-	iter = list_first_entry(&desc->group_list,
-				struct ppc440spe_adma_desc_slot, chain_node);
-	iter = list_entry(iter->chain_node.next,
-			  struct ppc440spe_adma_desc_slot, chain_node);
-
-	if (dst_cnt > 1) {
-		iter = list_entry(iter->chain_node.next,
-				  struct ppc440spe_adma_desc_slot, chain_node);
-	}
-	/* initialize each source descriptor in chain */
-	list_for_each_entry_from(iter, &desc->group_list, chain_node) {
-		hw_desc = iter->hw_desc;
-		memset(iter->hw_desc, 0, sizeof(struct dma_cdb));
-		iter->src_cnt = 0;
-		iter->dst_cnt = 0;
-
-		/* This is a ZERO_SUM operation:
-		 * - <src_cnt> descriptors starting from 2nd or 3rd
-		 *   descriptor are for GF-XOR operations;
-		 * - remaining <dst_cnt> descriptors are for checking the result
-		 */
-		if (i++ < src_cnt)
-			/* MV_SG1_SG2 if only Q is being verified
-			 * MULTICAST if both P and Q are being verified
-			 */
-			hw_desc->opc = dopc;
-		else
-			/* DMA_CDB_OPC_DCHECK128 operation */
-			hw_desc->opc = DMA_CDB_OPC_DCHECK128;
-
-		if (likely(!list_is_last(&iter->chain_node,
-					 &desc->group_list))) {
-			/* set 'next' pointer */
-			iter->hw_next = list_entry(iter->chain_node.next,
-						struct ppc440spe_adma_desc_slot,
-						chain_node);
-		} else {
-			/* this is the last descriptor.
-			 * this slot will be pasted from ADMA level
-			 * each time it wants to configure parameters
-			 * of the transaction (src, dst, ...)
-			 */
-			iter->hw_next = NULL;
-			/* always enable interrupt generation since we get
-			 * the status of pqzero from the handler
-			 */
-			set_bit(PPC440SPE_DESC_INT, &iter->flags);
-		}
-	}
-	desc->src_cnt = src_cnt;
-	desc->dst_cnt = dst_cnt;
-}
-
-/**
- * ppc440spe_desc_init_memcpy - initialize the descriptor for MEMCPY operation
- */
-static void ppc440spe_desc_init_memcpy(struct ppc440spe_adma_desc_slot *desc,
-					unsigned long flags)
-{
-	struct dma_cdb *hw_desc = desc->hw_desc;
-
-	memset(desc->hw_desc, 0, sizeof(struct dma_cdb));
-	desc->hw_next = NULL;
-	desc->src_cnt = 1;
-	desc->dst_cnt = 1;
-
-	if (flags & DMA_PREP_INTERRUPT)
-		set_bit(PPC440SPE_DESC_INT, &desc->flags);
-	else
-		clear_bit(PPC440SPE_DESC_INT, &desc->flags);
-
-	hw_desc->opc = DMA_CDB_OPC_MV_SG1_SG2;
-}
-
-/**
- * ppc440spe_desc_init_memset - initialize the descriptor for MEMSET operation
- */
-static void ppc440spe_desc_init_memset(struct ppc440spe_adma_desc_slot *desc,
-					int value, unsigned long flags)
-{
-	struct dma_cdb *hw_desc = desc->hw_desc;
-
-	memset(desc->hw_desc, 0, sizeof(struct dma_cdb));
-	desc->hw_next = NULL;
-	desc->src_cnt = 1;
-	desc->dst_cnt = 1;
-
-	if (flags & DMA_PREP_INTERRUPT)
-		set_bit(PPC440SPE_DESC_INT, &desc->flags);
-	else
-		clear_bit(PPC440SPE_DESC_INT, &desc->flags);
-
-	hw_desc->sg1u = hw_desc->sg1l = cpu_to_le32((u32)value);
-	hw_desc->sg3u = hw_desc->sg3l = cpu_to_le32((u32)value);
-	hw_desc->opc = DMA_CDB_OPC_DFILL128;
-}
-
-/**
- * ppc440spe_desc_set_src_addr - set source address into the descriptor
- */
-static void ppc440spe_desc_set_src_addr(struct ppc440spe_adma_desc_slot *desc,
-					struct ppc440spe_adma_chan *chan,
-					int src_idx, dma_addr_t addrh,
-					dma_addr_t addrl)
-{
-	struct dma_cdb *dma_hw_desc;
-	struct xor_cb *xor_hw_desc;
-	phys_addr_t addr64, tmplow, tmphi;
-
-	switch (chan->device->id) {
-	case PPC440SPE_DMA0_ID:
-	case PPC440SPE_DMA1_ID:
-		if (!addrh) {
-			addr64 = addrl;
-			tmphi = (addr64 >> 32);
-			tmplow = (addr64 & 0xFFFFFFFF);
-		} else {
-			tmphi = addrh;
-			tmplow = addrl;
-		}
-		dma_hw_desc = desc->hw_desc;
-		dma_hw_desc->sg1l = cpu_to_le32((u32)tmplow);
-		dma_hw_desc->sg1u |= cpu_to_le32((u32)tmphi);
-		break;
-	case PPC440SPE_XOR_ID:
-		xor_hw_desc = desc->hw_desc;
-		xor_hw_desc->ops[src_idx].l = addrl;
-		xor_hw_desc->ops[src_idx].h |= addrh;
-		break;
-	}
-}
-
-/**
- * ppc440spe_desc_set_src_mult - set source address mult into the descriptor
- */
-static void ppc440spe_desc_set_src_mult(struct ppc440spe_adma_desc_slot *desc,
-			struct ppc440spe_adma_chan *chan, u32 mult_index,
-			int sg_index, unsigned char mult_value)
-{
-	struct dma_cdb *dma_hw_desc;
-	struct xor_cb *xor_hw_desc;
-	u32 *psgu;
-
-	switch (chan->device->id) {
-	case PPC440SPE_DMA0_ID:
-	case PPC440SPE_DMA1_ID:
-		dma_hw_desc = desc->hw_desc;
-
-		switch (sg_index) {
-		/* for RXOR operations set multiplier
-		 * into source cued address
-		 */
-		case DMA_CDB_SG_SRC:
-			psgu = &dma_hw_desc->sg1u;
-			break;
-		/* for WXOR operations set multiplier
-		 * into destination cued address(es)
-		 */
-		case DMA_CDB_SG_DST1:
-			psgu = &dma_hw_desc->sg2u;
-			break;
-		case DMA_CDB_SG_DST2:
-			psgu = &dma_hw_desc->sg3u;
-			break;
-		default:
-			BUG();
-		}
-
-		*psgu |= cpu_to_le32(mult_value << mult_index);
-		break;
-	case PPC440SPE_XOR_ID:
-		xor_hw_desc = desc->hw_desc;
-		break;
-	default:
-		BUG();
-	}
-}
-
-/**
- * ppc440spe_desc_set_dest_addr - set destination address into the descriptor
- */
-static void ppc440spe_desc_set_dest_addr(struct ppc440spe_adma_desc_slot *desc,
-				struct ppc440spe_adma_chan *chan,
-				dma_addr_t addrh, dma_addr_t addrl,
-				u32 dst_idx)
-{
-	struct dma_cdb *dma_hw_desc;
-	struct xor_cb *xor_hw_desc;
-	phys_addr_t addr64, tmphi, tmplow;
-	u32 *psgu, *psgl;
-
-	switch (chan->device->id) {
-	case PPC440SPE_DMA0_ID:
-	case PPC440SPE_DMA1_ID:
-		if (!addrh) {
-			addr64 = addrl;
-			tmphi = (addr64 >> 32);
-			tmplow = (addr64 & 0xFFFFFFFF);
-		} else {
-			tmphi = addrh;
-			tmplow = addrl;
-		}
-		dma_hw_desc = desc->hw_desc;
-
-		psgu = dst_idx ? &dma_hw_desc->sg3u : &dma_hw_desc->sg2u;
-		psgl = dst_idx ? &dma_hw_desc->sg3l : &dma_hw_desc->sg2l;
-
-		*psgl = cpu_to_le32((u32)tmplow);
-		*psgu |= cpu_to_le32((u32)tmphi);
-		break;
-	case PPC440SPE_XOR_ID:
-		xor_hw_desc = desc->hw_desc;
-		xor_hw_desc->cbtal = addrl;
-		xor_hw_desc->cbtah |= addrh;
-		break;
-	}
-}
-
-/**
- * ppc440spe_desc_set_byte_count - set number of data bytes involved
- * into the operation
- */
-static void ppc440spe_desc_set_byte_count(struct ppc440spe_adma_desc_slot *desc,
-				struct ppc440spe_adma_chan *chan,
-				u32 byte_count)
-{
-	struct dma_cdb *dma_hw_desc;
-	struct xor_cb *xor_hw_desc;
-
-	switch (chan->device->id) {
-	case PPC440SPE_DMA0_ID:
-	case PPC440SPE_DMA1_ID:
-		dma_hw_desc = desc->hw_desc;
-		dma_hw_desc->cnt = cpu_to_le32(byte_count);
-		break;
-	case PPC440SPE_XOR_ID:
-		xor_hw_desc = desc->hw_desc;
-		xor_hw_desc->cbbc = byte_count;
-		break;
-	}
-}
-
-/**
- * ppc440spe_desc_set_rxor_block_size - set RXOR block size
- */
-static inline void ppc440spe_desc_set_rxor_block_size(u32 byte_count)
-{
-	/* assume that byte_count is aligned on the 512-boundary;
-	 * thus write it directly to the register (bits 23:31 are
-	 * reserved there).
-	 */
-	dcr_write(ppc440spe_mq_dcr_host, DCRN_MQ0_CF2H, byte_count);
-}
-
-/**
- * ppc440spe_desc_set_dcheck - set CHECK pattern
- */
-static void ppc440spe_desc_set_dcheck(struct ppc440spe_adma_desc_slot *desc,
-				struct ppc440spe_adma_chan *chan, u8 *qword)
-{
-	struct dma_cdb *dma_hw_desc;
-
-	switch (chan->device->id) {
-	case PPC440SPE_DMA0_ID:
-	case PPC440SPE_DMA1_ID:
-		dma_hw_desc = desc->hw_desc;
-		iowrite32(qword[0], &dma_hw_desc->sg3l);
-		iowrite32(qword[4], &dma_hw_desc->sg3u);
-		iowrite32(qword[8], &dma_hw_desc->sg2l);
-		iowrite32(qword[12], &dma_hw_desc->sg2u);
-		break;
-	default:
-		BUG();
-	}
-}
-
-/**
- * ppc440spe_xor_set_link - set link address in xor CB
- */
-static void ppc440spe_xor_set_link(struct ppc440spe_adma_desc_slot *prev_desc,
-				struct ppc440spe_adma_desc_slot *next_desc)
-{
-	struct xor_cb *xor_hw_desc = prev_desc->hw_desc;
-
-	if (unlikely(!next_desc || !(next_desc->phys))) {
-		printk(KERN_ERR "%s: next_desc=0x%p; next_desc->phys=0x%llx\n",
-			__func__, next_desc,
-			next_desc ? next_desc->phys : 0);
-		BUG();
-	}
-
-	xor_hw_desc->cbs = 0;
-	xor_hw_desc->cblal = next_desc->phys;
-	xor_hw_desc->cblah = 0;
-	xor_hw_desc->cbc |= XOR_CBCR_LNK_BIT;
-}
-
-/**
- * ppc440spe_desc_set_link - set the address of descriptor following this
- * descriptor in chain
- */
-static void ppc440spe_desc_set_link(struct ppc440spe_adma_chan *chan,
-				struct ppc440spe_adma_desc_slot *prev_desc,
-				struct ppc440spe_adma_desc_slot *next_desc)
-{
-	unsigned long flags;
-	struct ppc440spe_adma_desc_slot *tail = next_desc;
-
-	if (unlikely(!prev_desc || !next_desc ||
-		(prev_desc->hw_next && prev_desc->hw_next != next_desc))) {
-		/* If previous next is overwritten something is wrong.
-		 * though we may refetch from append to initiate list
-		 * processing; in this case - it's ok.
-		 */
-		printk(KERN_ERR "%s: prev_desc=0x%p; next_desc=0x%p; "
-			"prev->hw_next=0x%p\n", __func__, prev_desc,
-			next_desc, prev_desc ? prev_desc->hw_next : 0);
-		BUG();
-	}
-
-	local_irq_save(flags);
-
-	/* do s/w chaining both for DMA and XOR descriptors */
-	prev_desc->hw_next = next_desc;
-
-	switch (chan->device->id) {
-	case PPC440SPE_DMA0_ID:
-	case PPC440SPE_DMA1_ID:
-		break;
-	case PPC440SPE_XOR_ID:
-		/* bind descriptor to the chain */
-		while (tail->hw_next)
-			tail = tail->hw_next;
-		xor_last_linked = tail;
-
-		if (prev_desc == xor_last_submit)
-			/* do not link to the last submitted CB */
-			break;
-		ppc440spe_xor_set_link(prev_desc, next_desc);
-		break;
-	}
-
-	local_irq_restore(flags);
-}
-
-/**
- * ppc440spe_desc_get_src_addr - extract the source address from the descriptor
- */
-static u32 ppc440spe_desc_get_src_addr(struct ppc440spe_adma_desc_slot *desc,
-				struct ppc440spe_adma_chan *chan, int src_idx)
-{
-	struct dma_cdb *dma_hw_desc;
-	struct xor_cb *xor_hw_desc;
-
-	switch (chan->device->id) {
-	case PPC440SPE_DMA0_ID:
-	case PPC440SPE_DMA1_ID:
-		dma_hw_desc = desc->hw_desc;
-		/* May have 0, 1, 2, or 3 sources */
-		switch (dma_hw_desc->opc) {
-		case DMA_CDB_OPC_NO_OP:
-		case DMA_CDB_OPC_DFILL128:
-			return 0;
-		case DMA_CDB_OPC_DCHECK128:
-			if (unlikely(src_idx)) {
-				printk(KERN_ERR "%s: try to get %d source for"
-				    " DCHECK128\n", __func__, src_idx);
-				BUG();
-			}
-			return le32_to_cpu(dma_hw_desc->sg1l);
-		case DMA_CDB_OPC_MULTICAST:
-		case DMA_CDB_OPC_MV_SG1_SG2:
-			if (unlikely(src_idx > 2)) {
-				printk(KERN_ERR "%s: try to get %d source from"
-				    " DMA descr\n", __func__, src_idx);
-				BUG();
-			}
-			if (src_idx) {
-				if (le32_to_cpu(dma_hw_desc->sg1u) &
-				    DMA_CUED_XOR_WIN_MSK) {
-					u8 region;
-
-					if (src_idx == 1)
-						return le32_to_cpu(
-						    dma_hw_desc->sg1l) +
-							desc->unmap_len;
-
-					region = (le32_to_cpu(
-					    dma_hw_desc->sg1u)) >>
-						DMA_CUED_REGION_OFF;
-
-					region &= DMA_CUED_REGION_MSK;
-					switch (region) {
-					case DMA_RXOR123:
-						return le32_to_cpu(
-						    dma_hw_desc->sg1l) +
-							(desc->unmap_len << 1);
-					case DMA_RXOR124:
-						return le32_to_cpu(
-						    dma_hw_desc->sg1l) +
-							(desc->unmap_len * 3);
-					case DMA_RXOR125:
-						return le32_to_cpu(
-						    dma_hw_desc->sg1l) +
-							(desc->unmap_len << 2);
-					default:
-						printk(KERN_ERR
-						    "%s: try to"
-						    " get src3 for region %02x"
-						    "PPC440SPE_DESC_RXOR12?\n",
-						    __func__, region);
-						BUG();
-					}
-				} else {
-					printk(KERN_ERR
-						"%s: try to get %d"
-						" source for non-cued descr\n",
-						__func__, src_idx);
-					BUG();
-				}
-			}
-			return le32_to_cpu(dma_hw_desc->sg1l);
-		default:
-			printk(KERN_ERR "%s: unknown OPC 0x%02x\n",
-				__func__, dma_hw_desc->opc);
-			BUG();
-		}
-		return le32_to_cpu(dma_hw_desc->sg1l);
-	case PPC440SPE_XOR_ID:
-		/* May have up to 16 sources */
-		xor_hw_desc = desc->hw_desc;
-		return xor_hw_desc->ops[src_idx].l;
-	}
-	return 0;
-}
-
-/**
- * ppc440spe_desc_get_dest_addr - extract the destination address from the
- * descriptor
- */
-static u32 ppc440spe_desc_get_dest_addr(struct ppc440spe_adma_desc_slot *desc,
-				struct ppc440spe_adma_chan *chan, int idx)
-{
-	struct dma_cdb *dma_hw_desc;
-	struct xor_cb *xor_hw_desc;
-
-	switch (chan->device->id) {
-	case PPC440SPE_DMA0_ID:
-	case PPC440SPE_DMA1_ID:
-		dma_hw_desc = desc->hw_desc;
-
-		if (likely(!idx))
-			return le32_to_cpu(dma_hw_desc->sg2l);
-		return le32_to_cpu(dma_hw_desc->sg3l);
-	case PPC440SPE_XOR_ID:
-		xor_hw_desc = desc->hw_desc;
-		return xor_hw_desc->cbtal;
-	}
-	return 0;
-}
-
-/**
- * ppc440spe_desc_get_src_num - extract the number of source addresses from
- * the descriptor
- */
-static u32 ppc440spe_desc_get_src_num(struct ppc440spe_adma_desc_slot *desc,
-				struct ppc440spe_adma_chan *chan)
-{
-	struct dma_cdb *dma_hw_desc;
-	struct xor_cb *xor_hw_desc;
-
-	switch (chan->device->id) {
-	case PPC440SPE_DMA0_ID:
-	case PPC440SPE_DMA1_ID:
-		dma_hw_desc = desc->hw_desc;
-
-		switch (dma_hw_desc->opc) {
-		case DMA_CDB_OPC_NO_OP:
-		case DMA_CDB_OPC_DFILL128:
-			return 0;
-		case DMA_CDB_OPC_DCHECK128:
-			return 1;
-		case DMA_CDB_OPC_MV_SG1_SG2:
-		case DMA_CDB_OPC_MULTICAST:
-			/*
-			 * Only for RXOR operations we have more than
-			 * one source
-			 */
-			if (le32_to_cpu(dma_hw_desc->sg1u) &
-			    DMA_CUED_XOR_WIN_MSK) {
-				/* RXOR op, there are 2 or 3 sources */
-				if (((le32_to_cpu(dma_hw_desc->sg1u) >>
-				    DMA_CUED_REGION_OFF) &
-				      DMA_CUED_REGION_MSK) == DMA_RXOR12) {
-					/* RXOR 1-2 */
-					return 2;
-				} else {
-					/* RXOR 1-2-3/1-2-4/1-2-5 */
-					return 3;
-				}
-			}
-			return 1;
-		default:
-			printk(KERN_ERR "%s: unknown OPC 0x%02x\n",
-				__func__, dma_hw_desc->opc);
-			BUG();
-		}
-	case PPC440SPE_XOR_ID:
-		/* up to 16 sources */
-		xor_hw_desc = desc->hw_desc;
-		return xor_hw_desc->cbc & XOR_CDCR_OAC_MSK;
-	default:
-		BUG();
-	}
-	return 0;
-}
-
-/**
- * ppc440spe_desc_get_dst_num - get the number of destination addresses in
- * this descriptor
- */
-static u32 ppc440spe_desc_get_dst_num(struct ppc440spe_adma_desc_slot *desc,
-				struct ppc440spe_adma_chan *chan)
-{
-	struct dma_cdb *dma_hw_desc;
-
-	switch (chan->device->id) {
-	case PPC440SPE_DMA0_ID:
-	case PPC440SPE_DMA1_ID:
-		/* May be 1 or 2 destinations */
-		dma_hw_desc = desc->hw_desc;
-		switch (dma_hw_desc->opc) {
-		case DMA_CDB_OPC_NO_OP:
-		case DMA_CDB_OPC_DCHECK128:
-			return 0;
-		case DMA_CDB_OPC_MV_SG1_SG2:
-		case DMA_CDB_OPC_DFILL128:
-			return 1;
-		case DMA_CDB_OPC_MULTICAST:
-			if (desc->dst_cnt == 2)
-				return 2;
-			else
-				return 1;
-		default:
-			printk(KERN_ERR "%s: unknown OPC 0x%02x\n",
-				__func__, dma_hw_desc->opc);
-			BUG();
-		}
-	case PPC440SPE_XOR_ID:
-		/* Always only 1 destination */
-		return 1;
-	default:
-		BUG();
-	}
-	return 0;
-}
-
-/**
  * ppc440spe_desc_get_link - get the address of the descriptor that
  * follows this one
  */
@@ -1055,643 +176,23 @@ static int ppc440spe_chan_xor_slot_count(size_t len, int src_cnt,
 	return slot_cnt;
 }
 
-/**
- * ppc440spe_dma2_pq_slot_count - get the number of slots necessary for
- * DMA2 PQ operation
- */
-static int ppc440spe_dma2_pq_slot_count(dma_addr_t *srcs,
-		int src_cnt, size_t len)
-{
-	signed long long order = 0;
-	int state = 0;
-	int addr_count = 0;
-	int i;
-	for (i = 1; i < src_cnt; i++) {
-		dma_addr_t cur_addr = srcs[i];
-		dma_addr_t old_addr = srcs[i-1];
-		switch (state) {
-		case 0:
-			if (cur_addr == old_addr + len) {
-				/* direct RXOR */
-				order = 1;
-				state = 1;
-				if (i == src_cnt-1)
-					addr_count++;
-			} else if (old_addr == cur_addr + len) {
-				/* reverse RXOR */
-				order = -1;
-				state = 1;
-				if (i == src_cnt-1)
-					addr_count++;
-			} else {
-				state = 3;
-			}
-			break;
-		case 1:
-			if (i == src_cnt-2 || (order == -1
-				&& cur_addr != old_addr - len)) {
-				order = 0;
-				state = 0;
-				addr_count++;
-			} else if (cur_addr == old_addr + len*order) {
-				state = 2;
-				if (i == src_cnt-1)
-					addr_count++;
-			} else if (cur_addr == old_addr + 2*len) {
-				state = 2;
-				if (i == src_cnt-1)
-					addr_count++;
-			} else if (cur_addr == old_addr + 3*len) {
-				state = 2;
-				if (i == src_cnt-1)
-					addr_count++;
-			} else {
-				order = 0;
-				state = 0;
-				addr_count++;
-			}
-			break;
-		case 2:
-			order = 0;
-			state = 0;
-			addr_count++;
-				break;
-		}
-		if (state == 3)
-			break;
-	}
-	if (src_cnt <= 1 || (state != 1 && state != 2)) {
-		pr_err("%s: src_cnt=%d, state=%d, addr_count=%d, order=%lld\n",
-			__func__, src_cnt, state, addr_count, order);
-		for (i = 0; i < src_cnt; i++)
-			pr_err("\t[%d] 0x%llx \n", i, srcs[i]);
-		BUG();
-	}
-
-	return (addr_count + XOR_MAX_OPS - 1) / XOR_MAX_OPS;
-}
-
-
 /******************************************************************************
  * ADMA channel low-level routines
  ******************************************************************************/
 
-static u32
-ppc440spe_chan_get_current_descriptor(struct ppc440spe_adma_chan *chan);
-static void ppc440spe_chan_append(struct ppc440spe_adma_chan *chan);
-
-/**
- * ppc440spe_adma_device_clear_eot_status - interrupt ack to XOR or DMA engine
- */
-static void ppc440spe_adma_device_clear_eot_status(
-					struct ppc440spe_adma_chan *chan)
-{
-	struct dma_regs *dma_reg;
-	struct xor_regs *xor_reg;
-	u8 *p = chan->device->dma_desc_pool_virt;
-	struct dma_cdb *cdb;
-	u32 rv, i;
-
-	switch (chan->device->id) {
-	case PPC440SPE_DMA0_ID:
-	case PPC440SPE_DMA1_ID:
-		/* read FIFO to ack */
-		dma_reg = chan->device->dma_reg;
-		while ((rv = ioread32(&dma_reg->csfpl))) {
-			i = rv & DMA_CDB_ADDR_MSK;
-			cdb = (struct dma_cdb *)&p[i -
-			    (u32)chan->device->dma_desc_pool];
-
-			/* Clear opcode to ack. This is necessary for
-			 * ZeroSum operations only
-			 */
-			cdb->opc = 0;
-
-			if (test_bit(PPC440SPE_RXOR_RUN,
-			    &ppc440spe_rxor_state)) {
-				/* probably this is a completed RXOR op,
-				 * get pointer to CDB using the fact that
-				 * physical and virtual addresses of CDB
-				 * in pools have the same offsets
-				 */
-				if (le32_to_cpu(cdb->sg1u) &
-				    DMA_CUED_XOR_BASE) {
-					/* this is a RXOR */
-					clear_bit(PPC440SPE_RXOR_RUN,
-						  &ppc440spe_rxor_state);
-				}
-			}
-
-			if (rv & DMA_CDB_STATUS_MSK) {
-				/* ZeroSum check failed
-				 */
-				struct ppc440spe_adma_desc_slot *iter;
-				dma_addr_t phys = rv & ~DMA_CDB_MSK;
-
-				/*
-				 * Update the status of corresponding
-				 * descriptor.
-				 */
-				list_for_each_entry(iter, &chan->chain,
-				    chain_node) {
-					if (iter->phys == phys)
-						break;
-				}
-				/*
-				 * if cannot find the corresponding
-				 * slot it's a bug
-				 */
-				BUG_ON(&iter->chain_node == &chan->chain);
-
-				if (iter->xor_check_result) {
-					if (test_bit(PPC440SPE_DESC_PCHECK,
-						     &iter->flags)) {
-						*iter->xor_check_result |=
-							SUM_CHECK_P_RESULT;
-					} else
-					if (test_bit(PPC440SPE_DESC_QCHECK,
-						     &iter->flags)) {
-						*iter->xor_check_result |=
-							SUM_CHECK_Q_RESULT;
-					} else
-						BUG();
-				}
-			}
-		}
-
-		rv = ioread32(&dma_reg->dsts);
-		if (rv) {
-			pr_err("DMA%d err status: 0x%x\n",
-			       chan->device->id, rv);
-			/* write back to clear */
-			iowrite32(rv, &dma_reg->dsts);
-		}
-		break;
-	case PPC440SPE_XOR_ID:
-		/* reset status bits to ack */
-		xor_reg = chan->device->xor_reg;
-		rv = ioread32be(&xor_reg->sr);
-		iowrite32be(rv, &xor_reg->sr);
-
-		if (rv & (XOR_IE_ICBIE_BIT|XOR_IE_ICIE_BIT|XOR_IE_RPTIE_BIT)) {
-			if (rv & XOR_IE_RPTIE_BIT) {
-				/* Read PLB Timeout Error.
-				 * Try to resubmit the CB
-				 */
-				u32 val = ioread32be(&xor_reg->ccbalr);
-
-				iowrite32be(val, &xor_reg->cblalr);
-
-				val = ioread32be(&xor_reg->crsr);
-				iowrite32be(val | XOR_CRSR_XAE_BIT,
-					    &xor_reg->crsr);
-			} else
-				pr_err("XOR ERR 0x%x status\n", rv);
-			break;
-		}
-
-		/*  if the XORcore is idle, but there are unprocessed CBs
-		 * then refetch the s/w chain here
-		 */
-		if (!(ioread32be(&xor_reg->sr) & XOR_SR_XCP_BIT) &&
-		    do_xor_refetch)
-			ppc440spe_chan_append(chan);
-		break;
-	}
-}
-
-/**
- * ppc440spe_chan_is_busy - get the channel status
- */
-static int ppc440spe_chan_is_busy(struct ppc440spe_adma_chan *chan)
-{
-	struct dma_regs *dma_reg;
-	struct xor_regs *xor_reg;
-	int busy = 0;
-
-	switch (chan->device->id) {
-	case PPC440SPE_DMA0_ID:
-	case PPC440SPE_DMA1_ID:
-		dma_reg = chan->device->dma_reg;
-		/*  if command FIFO's head and tail pointers are equal and
-		 * status tail is the same as command, then channel is free
-		 */
-		if (ioread16(&dma_reg->cpfhp) != ioread16(&dma_reg->cpftp) ||
-		    ioread16(&dma_reg->cpftp) != ioread16(&dma_reg->csftp))
-			busy = 1;
-		break;
-	case PPC440SPE_XOR_ID:
-		/* use the special status bit for the XORcore
-		 */
-		xor_reg = chan->device->xor_reg;
-		busy = (ioread32be(&xor_reg->sr) & XOR_SR_XCP_BIT) ? 1 : 0;
-		break;
-	}
-
-	return busy;
-}
-
-/**
- * ppc440spe_chan_set_first_xor_descriptor -  init XORcore chain
- */
-static void ppc440spe_chan_set_first_xor_descriptor(
-				struct ppc440spe_adma_chan *chan,
-				struct ppc440spe_adma_desc_slot *next_desc)
-{
-	struct xor_regs *xor_reg = chan->device->xor_reg;
-
-	if (ioread32be(&xor_reg->sr) & XOR_SR_XCP_BIT)
-		printk(KERN_INFO "%s: Warn: XORcore is running "
-			"when try to set the first CDB!\n",
-			__func__);
-
-	xor_last_submit = xor_last_linked = next_desc;
-
-	iowrite32be(XOR_CRSR_64BA_BIT, &xor_reg->crsr);
-
-	iowrite32be(next_desc->phys, &xor_reg->cblalr);
-	iowrite32be(0, &xor_reg->cblahr);
-	iowrite32be(ioread32be(&xor_reg->cbcr) | XOR_CBCR_LNK_BIT,
-		    &xor_reg->cbcr);
-
-	chan->hw_chain_inited = 1;
-}
-
-/**
- * ppc440spe_dma_put_desc - put DMA0,1 descriptor to FIFO.
- * called with irqs disabled
- */
-static void ppc440spe_dma_put_desc(struct ppc440spe_adma_chan *chan,
-		struct ppc440spe_adma_desc_slot *desc)
-{
-	u32 pcdb;
-	struct dma_regs *dma_reg = chan->device->dma_reg;
-
-	pcdb = desc->phys;
-	if (!test_bit(PPC440SPE_DESC_INT, &desc->flags))
-		pcdb |= DMA_CDB_NO_INT;
-
-	chan_last_sub[chan->device->id] = desc;
-
-	ADMA_LL_DBG(print_cb(chan, desc->hw_desc));
-
-	iowrite32(pcdb, &dma_reg->cpfpl);
-}
-
-/**
- * ppc440spe_chan_append - update the h/w chain in the channel
- */
-static void ppc440spe_chan_append(struct ppc440spe_adma_chan *chan)
-{
-	struct xor_regs *xor_reg;
-	struct ppc440spe_adma_desc_slot *iter;
-	struct xor_cb *xcb;
-	u32 cur_desc;
-	unsigned long flags;
-
-	local_irq_save(flags);
-
-	switch (chan->device->id) {
-	case PPC440SPE_DMA0_ID:
-	case PPC440SPE_DMA1_ID:
-		cur_desc = ppc440spe_chan_get_current_descriptor(chan);
-
-		if (likely(cur_desc)) {
-			iter = chan_last_sub[chan->device->id];
-			BUG_ON(!iter);
-		} else {
-			/* first peer */
-			iter = chan_first_cdb[chan->device->id];
-			BUG_ON(!iter);
-			ppc440spe_dma_put_desc(chan, iter);
-			chan->hw_chain_inited = 1;
-		}
-
-		/* is there something new to append */
-		if (!iter->hw_next)
-			break;
-
-		/* flush descriptors from the s/w queue to fifo */
-		list_for_each_entry_continue(iter, &chan->chain, chain_node) {
-			ppc440spe_dma_put_desc(chan, iter);
-			if (!iter->hw_next)
-				break;
-		}
-		break;
-	case PPC440SPE_XOR_ID:
-		/* update h/w links and refetch */
-		if (!xor_last_submit->hw_next)
-			break;
-
-		xor_reg = chan->device->xor_reg;
-		/* the last linked CDB has to generate an interrupt
-		 * that we'd be able to append the next lists to h/w
-		 * regardless of the XOR engine state at the moment of
-		 * appending of these next lists
-		 */
-		xcb = xor_last_linked->hw_desc;
-		xcb->cbc |= XOR_CBCR_CBCE_BIT;
-
-		if (!(ioread32be(&xor_reg->sr) & XOR_SR_XCP_BIT)) {
-			/* XORcore is idle. Refetch now */
-			do_xor_refetch = 0;
-			ppc440spe_xor_set_link(xor_last_submit,
-				xor_last_submit->hw_next);
-
-			ADMA_LL_DBG(print_cb_list(chan,
-				xor_last_submit->hw_next));
-
-			xor_last_submit = xor_last_linked;
-			iowrite32be(ioread32be(&xor_reg->crsr) |
-				    XOR_CRSR_RCBE_BIT | XOR_CRSR_64BA_BIT,
-				    &xor_reg->crsr);
-		} else {
-			/* XORcore is running. Refetch later in the handler */
-			do_xor_refetch = 1;
-		}
-
-		break;
-	}
-
-	local_irq_restore(flags);
-}
-
-/**
- * ppc440spe_chan_get_current_descriptor - get the currently executed descriptor
- */
-static u32
-ppc440spe_chan_get_current_descriptor(struct ppc440spe_adma_chan *chan)
-{
-	struct dma_regs *dma_reg;
-	struct xor_regs *xor_reg;
-
-	if (unlikely(!chan->hw_chain_inited))
-		/* h/w descriptor chain is not initialized yet */
-		return 0;
-
-	switch (chan->device->id) {
-	case PPC440SPE_DMA0_ID:
-	case PPC440SPE_DMA1_ID:
-		dma_reg = chan->device->dma_reg;
-		return ioread32(&dma_reg->acpl) & (~DMA_CDB_MSK);
-	case PPC440SPE_XOR_ID:
-		xor_reg = chan->device->xor_reg;
-		return ioread32be(&xor_reg->ccbalr);
-	}
-	return 0;
-}
-
-/**
- * ppc440spe_chan_run - enable the channel
- */
-static void ppc440spe_chan_run(struct ppc440spe_adma_chan *chan)
-{
-	struct xor_regs *xor_reg;
-
-	switch (chan->device->id) {
-	case PPC440SPE_DMA0_ID:
-	case PPC440SPE_DMA1_ID:
-		/* DMAs are always enabled, do nothing */
-		break;
-	case PPC440SPE_XOR_ID:
-		/* drain write buffer */
-		xor_reg = chan->device->xor_reg;
-
-		/* fetch descriptor pointed to in <link> */
-		iowrite32be(XOR_CRSR_64BA_BIT | XOR_CRSR_XAE_BIT,
-			    &xor_reg->crsr);
-		break;
-	}
-}
 
 /******************************************************************************
  * ADMA device level
  ******************************************************************************/
 
-static void ppc440spe_chan_start_null_xor(struct ppc440spe_adma_chan *chan);
 static int ppc440spe_adma_alloc_chan_resources(struct dma_chan *chan);
 
-static dma_cookie_t
-ppc440spe_adma_tx_submit(struct dma_async_tx_descriptor *tx);
-
-static void ppc440spe_adma_set_dest(struct ppc440spe_adma_desc_slot *tx,
-				    dma_addr_t addr, int index);
-static void
-ppc440spe_adma_memcpy_xor_set_src(struct ppc440spe_adma_desc_slot *tx,
-				  dma_addr_t addr, int index);
-
-static void
-ppc440spe_adma_pq_set_dest(struct ppc440spe_adma_desc_slot *tx,
-			   dma_addr_t *paddr, unsigned long flags);
-static void
-ppc440spe_adma_pq_set_src(struct ppc440spe_adma_desc_slot *tx,
-			  dma_addr_t addr, int index);
-static void
-ppc440spe_adma_pq_set_src_mult(struct ppc440spe_adma_desc_slot *tx,
-			       unsigned char mult, int index, int dst_pos);
-static void
-ppc440spe_adma_pqzero_sum_set_dest(struct ppc440spe_adma_desc_slot *tx,
-				   dma_addr_t paddr, dma_addr_t qaddr);
-
-static struct page *ppc440spe_rxor_srcs[32];
-
-/**
- * ppc440spe_can_rxor - check if the operands may be processed with RXOR
- */
-static int ppc440spe_can_rxor(struct page **srcs, int src_cnt, size_t len)
-{
-	int i, order = 0, state = 0;
-	int idx = 0;
-
-	if (unlikely(!(src_cnt > 1)))
-		return 0;
-
-	BUG_ON(src_cnt > ARRAY_SIZE(ppc440spe_rxor_srcs));
-
-	/* Skip holes in the source list before checking */
-	for (i = 0; i < src_cnt; i++) {
-		if (!srcs[i])
-			continue;
-		ppc440spe_rxor_srcs[idx++] = srcs[i];
-	}
-	src_cnt = idx;
-
-	for (i = 1; i < src_cnt; i++) {
-		char *cur_addr = page_address(ppc440spe_rxor_srcs[i]);
-		char *old_addr = page_address(ppc440spe_rxor_srcs[i - 1]);
-
-		switch (state) {
-		case 0:
-			if (cur_addr == old_addr + len) {
-				/* direct RXOR */
-				order = 1;
-				state = 1;
-			} else if (old_addr == cur_addr + len) {
-				/* reverse RXOR */
-				order = -1;
-				state = 1;
-			} else
-				goto out;
-			break;
-		case 1:
-			if ((i == src_cnt - 2) ||
-			    (order == -1 && cur_addr != old_addr - len)) {
-				order = 0;
-				state = 0;
-			} else if ((cur_addr == old_addr + len * order) ||
-				   (cur_addr == old_addr + 2 * len) ||
-				   (cur_addr == old_addr + 3 * len)) {
-				state = 2;
-			} else {
-				order = 0;
-				state = 0;
-			}
-			break;
-		case 2:
-			order = 0;
-			state = 0;
-			break;
-		}
-	}
-
-out:
-	if (state == 1 || state == 2)
-		return 1;
-
-	return 0;
-}
-
-/**
- * ppc440spe_adma_device_estimate - estimate the efficiency of processing
- *	the operation given on this channel. It's assumed that 'chan' is
- *	capable to process 'cap' type of operation.
- * @chan: channel to use
- * @cap: type of transaction
- * @dst_lst: array of destination pointers
- * @dst_cnt: number of destination operands
- * @src_lst: array of source pointers
- * @src_cnt: number of source operands
- * @src_sz: size of each source operand
- */
-static int ppc440spe_adma_estimate(struct dma_chan *chan,
-	enum dma_transaction_type cap, struct page **dst_lst, int dst_cnt,
-	struct page **src_lst, int src_cnt, size_t src_sz)
-{
-	int ef = 1;
-
-	if (cap == DMA_PQ || cap == DMA_PQ_VAL) {
-		/* If RAID-6 capabilities were not activated don't try
-		 * to use them
-		 */
-		if (unlikely(!ppc440spe_r6_enabled))
-			return -1;
-	}
-	/*  In the current implementation of ppc440spe ADMA driver it
-	 * makes sense to pick out only pq case, because it may be
-	 * processed:
-	 * (1) either using Biskup method on DMA2;
-	 * (2) or on DMA0/1.
-	 *  Thus we give a favour to (1) if the sources are suitable;
-	 * else let it be processed on one of the DMA0/1 engines.
-	 *  In the sum_product case where destination is also the
-	 * source process it on DMA0/1 only.
-	 */
-	if (cap == DMA_PQ && chan->chan_id == PPC440SPE_XOR_ID) {
-
-		if (dst_cnt == 1 && src_cnt == 2 && dst_lst[0] == src_lst[1])
-			ef = 0; /* sum_product case, process on DMA0/1 */
-		else if (ppc440spe_can_rxor(src_lst, src_cnt, src_sz))
-			ef = 3; /* override (DMA0/1 + idle) */
-		else
-			ef = 0; /* can't process on DMA2 if !rxor */
-	}
-
-	/* channel idleness increases the priority */
-	if (likely(ef) &&
-	    !ppc440spe_chan_is_busy(to_ppc440spe_adma_chan(chan)))
-		ef++;
-
-	return ef;
-}
-
-struct dma_chan *
-ppc440spe_async_tx_find_best_channel(enum dma_transaction_type cap,
-	struct page **dst_lst, int dst_cnt, struct page **src_lst,
-	int src_cnt, size_t src_sz)
-{
-	struct dma_chan *best_chan = NULL;
-	struct ppc_dma_chan_ref *ref;
-	int best_rank = -1;
-
-	if (unlikely(!src_sz))
-		return NULL;
-	if (src_sz > PAGE_SIZE) {
-		/*
-		 * should a user of the api ever pass > PAGE_SIZE requests
-		 * we sort out cases where temporary page-sized buffers
-		 * are used.
-		 */
-		switch (cap) {
-		case DMA_PQ:
-			if (src_cnt == 1 && dst_lst[1] == src_lst[0])
-				return NULL;
-			if (src_cnt == 2 && dst_lst[1] == src_lst[1])
-				return NULL;
-			break;
-		case DMA_PQ_VAL:
-		case DMA_XOR_VAL:
-			return NULL;
-		default:
-			break;
-		}
-	}
-
-	list_for_each_entry(ref, &ppc440spe_adma_chan_list, node) {
-		if (dma_has_cap(cap, ref->chan->device->cap_mask)) {
-			int rank;
-
-			rank = ppc440spe_adma_estimate(ref->chan, cap, dst_lst,
-					dst_cnt, src_lst, src_cnt, src_sz);
-			if (rank > best_rank) {
-				best_rank = rank;
-				best_chan = ref->chan;
-			}
-		}
-	}
-
-	return best_chan;
-}
-EXPORT_SYMBOL_GPL(ppc440spe_async_tx_find_best_channel);
-
-/**
- * ppc440spe_get_group_entry - get group entry with index idx
- * @tdesc: is the last allocated slot in the group.
- */
-static struct ppc440spe_adma_desc_slot *
-ppc440spe_get_group_entry(struct ppc440spe_adma_desc_slot *tdesc, u32 entry_idx)
-{
-	struct ppc440spe_adma_desc_slot *iter = tdesc->group_head;
-	int i = 0;
-
-	if (entry_idx < 0 || entry_idx >= (tdesc->src_cnt + tdesc->dst_cnt)) {
-		printk("%s: entry_idx %d, src_cnt %d, dst_cnt %d\n",
-			__func__, entry_idx, tdesc->src_cnt, tdesc->dst_cnt);
-		BUG();
-	}
-
-	list_for_each_entry(iter, &tdesc->group_list, chain_node) {
-		if (i++ == entry_idx)
-			break;
-	}
-	return iter;
-}
-
 /**
  * ppc440spe_adma_free_slots - flags descriptor slots for reuse
  * @slot: Slot to free
  * Caller must hold &ppc440spe_chan->lock while calling this function
  */
-static void ppc440spe_adma_free_slots(struct ppc440spe_adma_desc_slot *slot,
+void ppc440spe_adma_free_slots(struct ppc440spe_adma_desc_slot *slot,
 				      struct ppc440spe_adma_chan *chan)
 {
 	int stride = slot->slots_per_op;
@@ -1793,48 +294,6 @@ static dma_cookie_t ppc440spe_adma_run_tx_complete_actions(
 }
 
 /**
- * ppc440spe_adma_clean_slot - clean up CDB slot (if ack is set)
- */
-static int ppc440spe_adma_clean_slot(struct ppc440spe_adma_desc_slot *desc,
-		struct ppc440spe_adma_chan *chan)
-{
-	/* the client is allowed to attach dependent operations
-	 * until 'ack' is set
-	 */
-	if (!async_tx_test_ack(&desc->async_tx))
-		return 0;
-
-	/* leave the last descriptor in the chain
-	 * so we can append to it
-	 */
-	if (list_is_last(&desc->chain_node, &chan->chain) ||
-	    desc->phys == ppc440spe_chan_get_current_descriptor(chan))
-		return 1;
-
-	if (chan->device->id != PPC440SPE_XOR_ID) {
-		/* our DMA interrupt handler clears opc field of
-		 * each processed descriptor. For all types of
-		 * operations except for ZeroSum we do not actually
-		 * need ack from the interrupt handler. ZeroSum is a
-		 * special case since the result of this operation
-		 * is available from the handler only, so if we see
-		 * such type of descriptor (which is unprocessed yet)
-		 * then leave it in chain.
-		 */
-		struct dma_cdb *cdb = desc->hw_desc;
-		if (cdb->opc == DMA_CDB_OPC_DCHECK128)
-			return 1;
-	}
-
-	dev_dbg(chan->device->common.dev, "\tfree slot %llx: %d stride: %d\n",
-		desc->phys, desc->idx, desc->slots_per_op);
-
-	list_del(&desc->chain_node);
-	ppc440spe_adma_free_slots(desc, chan);
-	return 0;
-}
-
-/**
  * __ppc440spe_adma_slot_cleanup - this is the common clean-up routine
  *	which runs through the channel CDBs list until reach the descriptor
  *	currently processed. When routine determines that all CDBs of group
@@ -1991,7 +450,7 @@ static void ppc440spe_adma_slot_cleanup(struct ppc440spe_adma_chan *chan)
 /**
  * ppc440spe_adma_alloc_slots - allocate free slots (if any)
  */
-static struct ppc440spe_adma_desc_slot *ppc440spe_adma_alloc_slots(
+struct ppc440spe_adma_desc_slot *ppc440spe_adma_alloc_slots(
 		struct ppc440spe_adma_chan *chan, int num_slots,
 		int slots_per_op)
 {
@@ -2166,40 +625,6 @@ static dma_cookie_t ppc440spe_desc_assign_cookie(
 }
 
 /**
- * ppc440spe_rxor_set_region_data -
- */
-static void ppc440spe_rxor_set_region(struct ppc440spe_adma_desc_slot *desc,
-	u8 xor_arg_no, u32 mask)
-{
-	struct xor_cb *xcb = desc->hw_desc;
-
-	xcb->ops[xor_arg_no].h |= mask;
-}
-
-/**
- * ppc440spe_rxor_set_src -
- */
-static void ppc440spe_rxor_set_src(struct ppc440spe_adma_desc_slot *desc,
-	u8 xor_arg_no, dma_addr_t addr)
-{
-	struct xor_cb *xcb = desc->hw_desc;
-
-	xcb->ops[xor_arg_no].h |= DMA_CUED_XOR_BASE;
-	xcb->ops[xor_arg_no].l = addr;
-}
-
-/**
- * ppc440spe_rxor_set_mult -
- */
-static void ppc440spe_rxor_set_mult(struct ppc440spe_adma_desc_slot *desc,
-	u8 xor_arg_no, u8 idx, u8 mult)
-{
-	struct xor_cb *xcb = desc->hw_desc;
-
-	xcb->ops[xor_arg_no].h |= mult << (DMA_CUED_MULT1_OFF + idx * 8);
-}
-
-/**
  * ppc440spe_adma_check_threshold - append CDBs to h/w chain if threshold
  *	has been achieved
  */
@@ -2219,7 +644,7 @@ static void ppc440spe_adma_check_threshold(struct ppc440spe_adma_chan *chan)
  *	(it's not necessary that descriptors will be submitted to the h/w
  *	chains too right now)
  */
-static dma_cookie_t ppc440spe_adma_tx_submit(struct dma_async_tx_descriptor *tx)
+dma_cookie_t ppc440spe_adma_tx_submit(struct dma_async_tx_descriptor *tx)
 {
 	struct ppc440spe_adma_desc_slot *sw_desc;
 	struct ppc440spe_adma_chan *chan = to_ppc440spe_adma_chan(tx->chan);
@@ -2424,1478 +849,6 @@ static struct dma_async_tx_descriptor *ppc440spe_adma_prep_dma_xor(
 	return sw_desc ? &sw_desc->async_tx : NULL;
 }
 
-static inline void
-ppc440spe_desc_set_xor_src_cnt(struct ppc440spe_adma_desc_slot *desc,
-				int src_cnt);
-static void ppc440spe_init_rxor_cursor(struct ppc440spe_rxor *cursor);
-
-/**
- * ppc440spe_adma_init_dma2rxor_slot -
- */
-static void ppc440spe_adma_init_dma2rxor_slot(
-		struct ppc440spe_adma_desc_slot *desc,
-		dma_addr_t *src, int src_cnt)
-{
-	int i;
-
-	/* initialize CDB */
-	for (i = 0; i < src_cnt; i++) {
-		ppc440spe_adma_dma2rxor_prep_src(desc, &desc->rxor_cursor, i,
-						 desc->src_cnt, (u32)src[i]);
-	}
-}
-
-/**
- * ppc440spe_dma01_prep_mult -
- * for Q operation where destination is also the source
- */
-static struct ppc440spe_adma_desc_slot *ppc440spe_dma01_prep_mult(
-		struct ppc440spe_adma_chan *ppc440spe_chan,
-		dma_addr_t *dst, int dst_cnt, dma_addr_t *src, int src_cnt,
-		const unsigned char *scf, size_t len, unsigned long flags)
-{
-	struct ppc440spe_adma_desc_slot *sw_desc = NULL;
-	unsigned long op = 0;
-	int slot_cnt;
-
-	set_bit(PPC440SPE_DESC_WXOR, &op);
-	slot_cnt = 2;
-
-	spin_lock_bh(&ppc440spe_chan->lock);
-
-	/* use WXOR, each descriptor occupies one slot */
-	sw_desc = ppc440spe_adma_alloc_slots(ppc440spe_chan, slot_cnt, 1);
-	if (sw_desc) {
-		struct ppc440spe_adma_chan *chan;
-		struct ppc440spe_adma_desc_slot *iter;
-		struct dma_cdb *hw_desc;
-
-		chan = to_ppc440spe_adma_chan(sw_desc->async_tx.chan);
-		set_bits(op, &sw_desc->flags);
-		sw_desc->src_cnt = src_cnt;
-		sw_desc->dst_cnt = dst_cnt;
-		/* First descriptor, zero data in the destination and copy it
-		 * to q page using MULTICAST transfer.
-		 */
-		iter = list_first_entry(&sw_desc->group_list,
-					struct ppc440spe_adma_desc_slot,
-					chain_node);
-		memset(iter->hw_desc, 0, sizeof(struct dma_cdb));
-		/* set 'next' pointer */
-		iter->hw_next = list_entry(iter->chain_node.next,
-					   struct ppc440spe_adma_desc_slot,
-					   chain_node);
-		clear_bit(PPC440SPE_DESC_INT, &iter->flags);
-		hw_desc = iter->hw_desc;
-		hw_desc->opc = DMA_CDB_OPC_MULTICAST;
-
-		ppc440spe_desc_set_dest_addr(iter, chan,
-					     DMA_CUED_XOR_BASE, dst[0], 0);
-		ppc440spe_desc_set_dest_addr(iter, chan, 0, dst[1], 1);
-		ppc440spe_desc_set_src_addr(iter, chan, 0, DMA_CUED_XOR_HB,
-					    src[0]);
-		ppc440spe_desc_set_byte_count(iter, ppc440spe_chan, len);
-		iter->unmap_len = len;
-
-		/*
-		 * Second descriptor, multiply data from the q page
-		 * and store the result in real destination.
-		 */
-		iter = list_first_entry(&iter->chain_node,
-					struct ppc440spe_adma_desc_slot,
-					chain_node);
-		memset(iter->hw_desc, 0, sizeof(struct dma_cdb));
-		iter->hw_next = NULL;
-		if (flags & DMA_PREP_INTERRUPT)
-			set_bit(PPC440SPE_DESC_INT, &iter->flags);
-		else
-			clear_bit(PPC440SPE_DESC_INT, &iter->flags);
-
-		hw_desc = iter->hw_desc;
-		hw_desc->opc = DMA_CDB_OPC_MV_SG1_SG2;
-		ppc440spe_desc_set_src_addr(iter, chan, 0,
-					    DMA_CUED_XOR_HB, dst[1]);
-		ppc440spe_desc_set_dest_addr(iter, chan,
-					     DMA_CUED_XOR_BASE, dst[0], 0);
-
-		ppc440spe_desc_set_src_mult(iter, chan, DMA_CUED_MULT1_OFF,
-					    DMA_CDB_SG_DST1, scf[0]);
-		ppc440spe_desc_set_byte_count(iter, ppc440spe_chan, len);
-		iter->unmap_len = len;
-		sw_desc->async_tx.flags = flags;
-	}
-
-	spin_unlock_bh(&ppc440spe_chan->lock);
-
-	return sw_desc;
-}
-
-/**
- * ppc440spe_dma01_prep_sum_product -
- * Dx = A*(P+Pxy) + B*(Q+Qxy) operation where destination is also
- * the source.
- */
-static struct ppc440spe_adma_desc_slot *ppc440spe_dma01_prep_sum_product(
-		struct ppc440spe_adma_chan *ppc440spe_chan,
-		dma_addr_t *dst, dma_addr_t *src, int src_cnt,
-		const unsigned char *scf, size_t len, unsigned long flags)
-{
-	struct ppc440spe_adma_desc_slot *sw_desc = NULL;
-	unsigned long op = 0;
-	int slot_cnt;
-
-	set_bit(PPC440SPE_DESC_WXOR, &op);
-	slot_cnt = 3;
-
-	spin_lock_bh(&ppc440spe_chan->lock);
-
-	/* WXOR, each descriptor occupies one slot */
-	sw_desc = ppc440spe_adma_alloc_slots(ppc440spe_chan, slot_cnt, 1);
-	if (sw_desc) {
-		struct ppc440spe_adma_chan *chan;
-		struct ppc440spe_adma_desc_slot *iter;
-		struct dma_cdb *hw_desc;
-
-		chan = to_ppc440spe_adma_chan(sw_desc->async_tx.chan);
-		set_bits(op, &sw_desc->flags);
-		sw_desc->src_cnt = src_cnt;
-		sw_desc->dst_cnt = 1;
-		/* 1st descriptor, src[1] data to q page and zero destination */
-		iter = list_first_entry(&sw_desc->group_list,
-					struct ppc440spe_adma_desc_slot,
-					chain_node);
-		memset(iter->hw_desc, 0, sizeof(struct dma_cdb));
-		iter->hw_next = list_entry(iter->chain_node.next,
-					   struct ppc440spe_adma_desc_slot,
-					   chain_node);
-		clear_bit(PPC440SPE_DESC_INT, &iter->flags);
-		hw_desc = iter->hw_desc;
-		hw_desc->opc = DMA_CDB_OPC_MULTICAST;
-
-		ppc440spe_desc_set_dest_addr(iter, chan, DMA_CUED_XOR_BASE,
-					     *dst, 0);
-		ppc440spe_desc_set_dest_addr(iter, chan, 0,
-					     ppc440spe_chan->qdest, 1);
-		ppc440spe_desc_set_src_addr(iter, chan, 0, DMA_CUED_XOR_HB,
-					    src[1]);
-		ppc440spe_desc_set_byte_count(iter, ppc440spe_chan, len);
-		iter->unmap_len = len;
-
-		/* 2nd descriptor, multiply src[1] data and store the
-		 * result in destination */
-		iter = list_first_entry(&iter->chain_node,
-					struct ppc440spe_adma_desc_slot,
-					chain_node);
-		memset(iter->hw_desc, 0, sizeof(struct dma_cdb));
-		/* set 'next' pointer */
-		iter->hw_next = list_entry(iter->chain_node.next,
-					   struct ppc440spe_adma_desc_slot,
-					   chain_node);
-		if (flags & DMA_PREP_INTERRUPT)
-			set_bit(PPC440SPE_DESC_INT, &iter->flags);
-		else
-			clear_bit(PPC440SPE_DESC_INT, &iter->flags);
-
-		hw_desc = iter->hw_desc;
-		hw_desc->opc = DMA_CDB_OPC_MV_SG1_SG2;
-		ppc440spe_desc_set_src_addr(iter, chan, 0, DMA_CUED_XOR_HB,
-					    ppc440spe_chan->qdest);
-		ppc440spe_desc_set_dest_addr(iter, chan, DMA_CUED_XOR_BASE,
-					     *dst, 0);
-		ppc440spe_desc_set_src_mult(iter, chan,	DMA_CUED_MULT1_OFF,
-					    DMA_CDB_SG_DST1, scf[1]);
-		ppc440spe_desc_set_byte_count(iter, ppc440spe_chan, len);
-		iter->unmap_len = len;
-
-		/*
-		 * 3rd descriptor, multiply src[0] data and xor it
-		 * with destination
-		 */
-		iter = list_first_entry(&iter->chain_node,
-					struct ppc440spe_adma_desc_slot,
-					chain_node);
-		memset(iter->hw_desc, 0, sizeof(struct dma_cdb));
-		iter->hw_next = NULL;
-		if (flags & DMA_PREP_INTERRUPT)
-			set_bit(PPC440SPE_DESC_INT, &iter->flags);
-		else
-			clear_bit(PPC440SPE_DESC_INT, &iter->flags);
-
-		hw_desc = iter->hw_desc;
-		hw_desc->opc = DMA_CDB_OPC_MV_SG1_SG2;
-		ppc440spe_desc_set_src_addr(iter, chan, 0, DMA_CUED_XOR_HB,
-					    src[0]);
-		ppc440spe_desc_set_dest_addr(iter, chan, DMA_CUED_XOR_BASE,
-					     *dst, 0);
-		ppc440spe_desc_set_src_mult(iter, chan, DMA_CUED_MULT1_OFF,
-					    DMA_CDB_SG_DST1, scf[0]);
-		ppc440spe_desc_set_byte_count(iter, ppc440spe_chan, len);
-		iter->unmap_len = len;
-		sw_desc->async_tx.flags = flags;
-	}
-
-	spin_unlock_bh(&ppc440spe_chan->lock);
-
-	return sw_desc;
-}
-
-static struct ppc440spe_adma_desc_slot *ppc440spe_dma01_prep_pq(
-		struct ppc440spe_adma_chan *ppc440spe_chan,
-		dma_addr_t *dst, int dst_cnt, dma_addr_t *src, int src_cnt,
-		const unsigned char *scf, size_t len, unsigned long flags)
-{
-	int slot_cnt;
-	struct ppc440spe_adma_desc_slot *sw_desc = NULL, *iter;
-	unsigned long op = 0;
-	unsigned char mult = 1;
-
-	pr_debug("%s: dst_cnt %d, src_cnt %d, len %d\n",
-		 __func__, dst_cnt, src_cnt, len);
-	/*  select operations WXOR/RXOR depending on the
-	 * source addresses of operators and the number
-	 * of destinations (RXOR support only Q-parity calculations)
-	 */
-	set_bit(PPC440SPE_DESC_WXOR, &op);
-	if (!test_and_set_bit(PPC440SPE_RXOR_RUN, &ppc440spe_rxor_state)) {
-		/* no active RXOR;
-		 * do RXOR if:
-		 * - there are more than 1 source,
-		 * - len is aligned on 512-byte boundary,
-		 * - source addresses fit to one of 4 possible regions.
-		 */
-		if (src_cnt > 1 &&
-		    !(len & MQ0_CF2H_RXOR_BS_MASK) &&
-		    (src[0] + len) == src[1]) {
-			/* may do RXOR R1 R2 */
-			set_bit(PPC440SPE_DESC_RXOR, &op);
-			if (src_cnt != 2) {
-				/* may try to enhance region of RXOR */
-				if ((src[1] + len) == src[2]) {
-					/* do RXOR R1 R2 R3 */
-					set_bit(PPC440SPE_DESC_RXOR123,
-						&op);
-				} else if ((src[1] + len * 2) == src[2]) {
-					/* do RXOR R1 R2 R4 */
-					set_bit(PPC440SPE_DESC_RXOR124, &op);
-				} else if ((src[1] + len * 3) == src[2]) {
-					/* do RXOR R1 R2 R5 */
-					set_bit(PPC440SPE_DESC_RXOR125,
-						&op);
-				} else {
-					/* do RXOR R1 R2 */
-					set_bit(PPC440SPE_DESC_RXOR12,
-						&op);
-				}
-			} else {
-				/* do RXOR R1 R2 */
-				set_bit(PPC440SPE_DESC_RXOR12, &op);
-			}
-		}
-
-		if (!test_bit(PPC440SPE_DESC_RXOR, &op)) {
-			/* can not do this operation with RXOR */
-			clear_bit(PPC440SPE_RXOR_RUN,
-				&ppc440spe_rxor_state);
-		} else {
-			/* can do; set block size right now */
-			ppc440spe_desc_set_rxor_block_size(len);
-		}
-	}
-
-	/* Number of necessary slots depends on operation type selected */
-	if (!test_bit(PPC440SPE_DESC_RXOR, &op)) {
-		/*  This is a WXOR only chain. Need descriptors for each
-		 * source to GF-XOR them with WXOR, and need descriptors
-		 * for each destination to zero them with WXOR
-		 */
-		slot_cnt = src_cnt;
-
-		if (flags & DMA_PREP_ZERO_P) {
-			slot_cnt++;
-			set_bit(PPC440SPE_ZERO_P, &op);
-		}
-		if (flags & DMA_PREP_ZERO_Q) {
-			slot_cnt++;
-			set_bit(PPC440SPE_ZERO_Q, &op);
-		}
-	} else {
-		/*  Need 1/2 descriptor for RXOR operation, and
-		 * need (src_cnt - (2 or 3)) for WXOR of sources
-		 * remained (if any)
-		 */
-		slot_cnt = dst_cnt;
-
-		if (flags & DMA_PREP_ZERO_P)
-			set_bit(PPC440SPE_ZERO_P, &op);
-		if (flags & DMA_PREP_ZERO_Q)
-			set_bit(PPC440SPE_ZERO_Q, &op);
-
-		if (test_bit(PPC440SPE_DESC_RXOR12, &op))
-			slot_cnt += src_cnt - 2;
-		else
-			slot_cnt += src_cnt - 3;
-
-		/*  Thus we have either RXOR only chain or
-		 * mixed RXOR/WXOR
-		 */
-		if (slot_cnt == dst_cnt)
-			/* RXOR only chain */
-			clear_bit(PPC440SPE_DESC_WXOR, &op);
-	}
-
-	spin_lock_bh(&ppc440spe_chan->lock);
-	/* for both RXOR/WXOR each descriptor occupies one slot */
-	sw_desc = ppc440spe_adma_alloc_slots(ppc440spe_chan, slot_cnt, 1);
-	if (sw_desc) {
-		ppc440spe_desc_init_dma01pq(sw_desc, dst_cnt, src_cnt,
-				flags, op);
-
-		/* setup dst/src/mult */
-		pr_debug("%s: set dst descriptor 0, 1: 0x%016llx, 0x%016llx\n",
-			 __func__, dst[0], dst[1]);
-		ppc440spe_adma_pq_set_dest(sw_desc, dst, flags);
-		while (src_cnt--) {
-			ppc440spe_adma_pq_set_src(sw_desc, src[src_cnt],
-						  src_cnt);
-
-			/* NOTE: "Multi = 0 is equivalent to = 1" as it
-			 * stated in 440SPSPe_RAID6_Addendum_UM_1_17.pdf
-			 * doesn't work for RXOR with DMA0/1! Instead, multi=0
-			 * leads to zeroing source data after RXOR.
-			 * So, for P case set-up mult=1 explicitly.
-			 */
-			if (!(flags & DMA_PREP_PQ_DISABLE_Q))
-				mult = scf[src_cnt];
-			ppc440spe_adma_pq_set_src_mult(sw_desc,
-				mult, src_cnt,  dst_cnt - 1);
-		}
-
-		/* Setup byte count foreach slot just allocated */
-		sw_desc->async_tx.flags = flags;
-		list_for_each_entry(iter, &sw_desc->group_list,
-				chain_node) {
-			ppc440spe_desc_set_byte_count(iter,
-				ppc440spe_chan, len);
-			iter->unmap_len = len;
-		}
-	}
-	spin_unlock_bh(&ppc440spe_chan->lock);
-
-	return sw_desc;
-}
-
-static struct ppc440spe_adma_desc_slot *ppc440spe_dma2_prep_pq(
-		struct ppc440spe_adma_chan *ppc440spe_chan,
-		dma_addr_t *dst, int dst_cnt, dma_addr_t *src, int src_cnt,
-		const unsigned char *scf, size_t len, unsigned long flags)
-{
-	int slot_cnt, descs_per_op;
-	struct ppc440spe_adma_desc_slot *sw_desc = NULL, *iter;
-	unsigned long op = 0;
-	unsigned char mult = 1;
-
-	BUG_ON(!dst_cnt);
-	/*pr_debug("%s: dst_cnt %d, src_cnt %d, len %d\n",
-		 __func__, dst_cnt, src_cnt, len);*/
-
-	spin_lock_bh(&ppc440spe_chan->lock);
-	descs_per_op = ppc440spe_dma2_pq_slot_count(src, src_cnt, len);
-	if (descs_per_op < 0) {
-		spin_unlock_bh(&ppc440spe_chan->lock);
-		return NULL;
-	}
-
-	/* depending on number of sources we have 1 or 2 RXOR chains */
-	slot_cnt = descs_per_op * dst_cnt;
-
-	sw_desc = ppc440spe_adma_alloc_slots(ppc440spe_chan, slot_cnt, 1);
-	if (sw_desc) {
-		op = slot_cnt;
-		sw_desc->async_tx.flags = flags;
-		list_for_each_entry(iter, &sw_desc->group_list, chain_node) {
-			ppc440spe_desc_init_dma2pq(iter, dst_cnt, src_cnt,
-				--op ? 0 : flags);
-			ppc440spe_desc_set_byte_count(iter, ppc440spe_chan,
-				len);
-			iter->unmap_len = len;
-
-			ppc440spe_init_rxor_cursor(&(iter->rxor_cursor));
-			iter->rxor_cursor.len = len;
-			iter->descs_per_op = descs_per_op;
-		}
-		op = 0;
-		list_for_each_entry(iter, &sw_desc->group_list, chain_node) {
-			op++;
-			if (op % descs_per_op == 0)
-				ppc440spe_adma_init_dma2rxor_slot(iter, src,
-								  src_cnt);
-			if (likely(!list_is_last(&iter->chain_node,
-						 &sw_desc->group_list))) {
-				/* set 'next' pointer */
-				iter->hw_next =
-					list_entry(iter->chain_node.next,
-						struct ppc440spe_adma_desc_slot,
-						chain_node);
-				ppc440spe_xor_set_link(iter, iter->hw_next);
-			} else {
-				/* this is the last descriptor. */
-				iter->hw_next = NULL;
-			}
-		}
-
-		/* fixup head descriptor */
-		sw_desc->dst_cnt = dst_cnt;
-		if (flags & DMA_PREP_ZERO_P)
-			set_bit(PPC440SPE_ZERO_P, &sw_desc->flags);
-		if (flags & DMA_PREP_ZERO_Q)
-			set_bit(PPC440SPE_ZERO_Q, &sw_desc->flags);
-
-		/* setup dst/src/mult */
-		ppc440spe_adma_pq_set_dest(sw_desc, dst, flags);
-
-		while (src_cnt--) {
-			/* handle descriptors (if dst_cnt == 2) inside
-			 * the ppc440spe_adma_pq_set_srcxxx() functions
-			 */
-			ppc440spe_adma_pq_set_src(sw_desc, src[src_cnt],
-						  src_cnt);
-			if (!(flags & DMA_PREP_PQ_DISABLE_Q))
-				mult = scf[src_cnt];
-			ppc440spe_adma_pq_set_src_mult(sw_desc,
-					mult, src_cnt, dst_cnt - 1);
-		}
-	}
-	spin_unlock_bh(&ppc440spe_chan->lock);
-	ppc440spe_desc_set_rxor_block_size(len);
-	return sw_desc;
-}
-
-/**
- * ppc440spe_adma_prep_dma_pq - prepare CDB (group) for a GF-XOR operation
- */
-static struct dma_async_tx_descriptor *ppc440spe_adma_prep_dma_pq(
-		struct dma_chan *chan, dma_addr_t *dst, dma_addr_t *src,
-		unsigned int src_cnt, const unsigned char *scf,
-		size_t len, unsigned long flags)
-{
-	struct ppc440spe_adma_chan *ppc440spe_chan;
-	struct ppc440spe_adma_desc_slot *sw_desc = NULL;
-	int dst_cnt = 0;
-
-	ppc440spe_chan = to_ppc440spe_adma_chan(chan);
-
-	ADMA_LL_DBG(prep_dma_pq_dbg(ppc440spe_chan->device->id,
-				    dst, src, src_cnt));
-	BUG_ON(!len);
-	BUG_ON(unlikely(len > PPC440SPE_ADMA_XOR_MAX_BYTE_COUNT));
-	BUG_ON(!src_cnt);
-
-	if (src_cnt == 1 && dst[1] == src[0]) {
-		dma_addr_t dest[2];
-
-		/* dst[1] is real destination (Q) */
-		dest[0] = dst[1];
-		/* this is the page to multicast source data to */
-		dest[1] = ppc440spe_chan->qdest;
-		sw_desc = ppc440spe_dma01_prep_mult(ppc440spe_chan,
-				dest, 2, src, src_cnt, scf, len, flags);
-		return sw_desc ? &sw_desc->async_tx : NULL;
-	}
-
-	if (src_cnt == 2 && dst[1] == src[1]) {
-		sw_desc = ppc440spe_dma01_prep_sum_product(ppc440spe_chan,
-					&dst[1], src, 2, scf, len, flags);
-		return sw_desc ? &sw_desc->async_tx : NULL;
-	}
-
-	if (!(flags & DMA_PREP_PQ_DISABLE_P)) {
-		BUG_ON(!dst[0]);
-		dst_cnt++;
-		flags |= DMA_PREP_ZERO_P;
-	}
-
-	if (!(flags & DMA_PREP_PQ_DISABLE_Q)) {
-		BUG_ON(!dst[1]);
-		dst_cnt++;
-		flags |= DMA_PREP_ZERO_Q;
-	}
-
-	BUG_ON(!dst_cnt);
-
-	dev_dbg(ppc440spe_chan->device->common.dev,
-		"ppc440spe adma%d: %s src_cnt: %d len: %u int_en: %d\n",
-		ppc440spe_chan->device->id, __func__, src_cnt, len,
-		flags & DMA_PREP_INTERRUPT ? 1 : 0);
-
-	switch (ppc440spe_chan->device->id) {
-	case PPC440SPE_DMA0_ID:
-	case PPC440SPE_DMA1_ID:
-		sw_desc = ppc440spe_dma01_prep_pq(ppc440spe_chan,
-				dst, dst_cnt, src, src_cnt, scf,
-				len, flags);
-		break;
-
-	case PPC440SPE_XOR_ID:
-		sw_desc = ppc440spe_dma2_prep_pq(ppc440spe_chan,
-				dst, dst_cnt, src, src_cnt, scf,
-				len, flags);
-		break;
-	}
-
-	return sw_desc ? &sw_desc->async_tx : NULL;
-}
-
-/**
- * ppc440spe_adma_prep_dma_pqzero_sum - prepare CDB group for
- * a PQ_ZERO_SUM operation
- */
-static struct dma_async_tx_descriptor *ppc440spe_adma_prep_dma_pqzero_sum(
-		struct dma_chan *chan, dma_addr_t *pq, dma_addr_t *src,
-		unsigned int src_cnt, const unsigned char *scf, size_t len,
-		enum sum_check_flags *pqres, unsigned long flags)
-{
-	struct ppc440spe_adma_chan *ppc440spe_chan;
-	struct ppc440spe_adma_desc_slot *sw_desc, *iter;
-	dma_addr_t pdest, qdest;
-	int slot_cnt, slots_per_op, idst, dst_cnt;
-
-	ppc440spe_chan = to_ppc440spe_adma_chan(chan);
-
-	if (flags & DMA_PREP_PQ_DISABLE_P)
-		pdest = 0;
-	else
-		pdest = pq[0];
-
-	if (flags & DMA_PREP_PQ_DISABLE_Q)
-		qdest = 0;
-	else
-		qdest = pq[1];
-
-	ADMA_LL_DBG(prep_dma_pqzero_sum_dbg(ppc440spe_chan->device->id,
-					    src, src_cnt, scf));
-
-	/* Always use WXOR for P/Q calculations (two destinations).
-	 * Need 1 or 2 extra slots to verify results are zero.
-	 */
-	idst = dst_cnt = (pdest && qdest) ? 2 : 1;
-
-	/* One additional slot per destination to clone P/Q
-	 * before calculation (we have to preserve destinations).
-	 */
-	slot_cnt = src_cnt + dst_cnt * 2;
-	slots_per_op = 1;
-
-	spin_lock_bh(&ppc440spe_chan->lock);
-	sw_desc = ppc440spe_adma_alloc_slots(ppc440spe_chan, slot_cnt,
-					     slots_per_op);
-	if (sw_desc) {
-		ppc440spe_desc_init_dma01pqzero_sum(sw_desc, dst_cnt, src_cnt);
-
-		/* Setup byte count for each slot just allocated */
-		sw_desc->async_tx.flags = flags;
-		list_for_each_entry(iter, &sw_desc->group_list, chain_node) {
-			ppc440spe_desc_set_byte_count(iter, ppc440spe_chan,
-						      len);
-			iter->unmap_len = len;
-		}
-
-		if (pdest) {
-			struct dma_cdb *hw_desc;
-			struct ppc440spe_adma_chan *chan;
-
-			iter = sw_desc->group_head;
-			chan = to_ppc440spe_adma_chan(iter->async_tx.chan);
-			memset(iter->hw_desc, 0, sizeof(struct dma_cdb));
-			iter->hw_next = list_entry(iter->chain_node.next,
-						struct ppc440spe_adma_desc_slot,
-						chain_node);
-			hw_desc = iter->hw_desc;
-			hw_desc->opc = DMA_CDB_OPC_MV_SG1_SG2;
-			iter->src_cnt = 0;
-			iter->dst_cnt = 0;
-			ppc440spe_desc_set_dest_addr(iter, chan, 0,
-						     ppc440spe_chan->pdest, 0);
-			ppc440spe_desc_set_src_addr(iter, chan, 0, 0, pdest);
-			ppc440spe_desc_set_byte_count(iter, ppc440spe_chan,
-						      len);
-			iter->unmap_len = 0;
-			/* override pdest to preserve original P */
-			pdest = ppc440spe_chan->pdest;
-		}
-		if (qdest) {
-			struct dma_cdb *hw_desc;
-			struct ppc440spe_adma_chan *chan;
-
-			iter = list_first_entry(&sw_desc->group_list,
-						struct ppc440spe_adma_desc_slot,
-						chain_node);
-			chan = to_ppc440spe_adma_chan(iter->async_tx.chan);
-
-			if (pdest) {
-				iter = list_entry(iter->chain_node.next,
-						struct ppc440spe_adma_desc_slot,
-						chain_node);
-			}
-
-			memset(iter->hw_desc, 0, sizeof(struct dma_cdb));
-			iter->hw_next = list_entry(iter->chain_node.next,
-						struct ppc440spe_adma_desc_slot,
-						chain_node);
-			hw_desc = iter->hw_desc;
-			hw_desc->opc = DMA_CDB_OPC_MV_SG1_SG2;
-			iter->src_cnt = 0;
-			iter->dst_cnt = 0;
-			ppc440spe_desc_set_dest_addr(iter, chan, 0,
-						     ppc440spe_chan->qdest, 0);
-			ppc440spe_desc_set_src_addr(iter, chan, 0, 0, qdest);
-			ppc440spe_desc_set_byte_count(iter, ppc440spe_chan,
-						      len);
-			iter->unmap_len = 0;
-			/* override qdest to preserve original Q */
-			qdest = ppc440spe_chan->qdest;
-		}
-
-		/* Setup destinations for P/Q ops */
-		ppc440spe_adma_pqzero_sum_set_dest(sw_desc, pdest, qdest);
-
-		/* Setup zero QWORDs into DCHECK CDBs */
-		idst = dst_cnt;
-		list_for_each_entry_reverse(iter, &sw_desc->group_list,
-					    chain_node) {
-			/*
-			 * The last CDB corresponds to Q-parity check,
-			 * the one before last CDB corresponds
-			 * P-parity check
-			 */
-			if (idst == DMA_DEST_MAX_NUM) {
-				if (idst == dst_cnt) {
-					set_bit(PPC440SPE_DESC_QCHECK,
-						&iter->flags);
-				} else {
-					set_bit(PPC440SPE_DESC_PCHECK,
-						&iter->flags);
-				}
-			} else {
-				if (qdest) {
-					set_bit(PPC440SPE_DESC_QCHECK,
-						&iter->flags);
-				} else {
-					set_bit(PPC440SPE_DESC_PCHECK,
-						&iter->flags);
-				}
-			}
-			iter->xor_check_result = pqres;
-
-			/*
-			 * set it to zero, if check fail then result will
-			 * be updated
-			 */
-			*iter->xor_check_result = 0;
-			ppc440spe_desc_set_dcheck(iter, ppc440spe_chan,
-				ppc440spe_qword);
-
-			if (!(--dst_cnt))
-				break;
-		}
-
-		/* Setup sources and mults for P/Q ops */
-		list_for_each_entry_continue_reverse(iter, &sw_desc->group_list,
-						     chain_node) {
-			struct ppc440spe_adma_chan *chan;
-			u32 mult_dst;
-
-			chan = to_ppc440spe_adma_chan(iter->async_tx.chan);
-			ppc440spe_desc_set_src_addr(iter, chan, 0,
-						    DMA_CUED_XOR_HB,
-						    src[src_cnt - 1]);
-			if (qdest) {
-				mult_dst = (dst_cnt - 1) ? DMA_CDB_SG_DST2 :
-							   DMA_CDB_SG_DST1;
-				ppc440spe_desc_set_src_mult(iter, chan,
-							    DMA_CUED_MULT1_OFF,
-							    mult_dst,
-							    scf[src_cnt - 1]);
-			}
-			if (!(--src_cnt))
-				break;
-		}
-	}
-	spin_unlock_bh(&ppc440spe_chan->lock);
-	return sw_desc ? &sw_desc->async_tx : NULL;
-}
-
-/**
- * ppc440spe_adma_prep_dma_xor_zero_sum - prepare CDB group for
- * XOR ZERO_SUM operation
- */
-static struct dma_async_tx_descriptor *ppc440spe_adma_prep_dma_xor_zero_sum(
-		struct dma_chan *chan, dma_addr_t *src, unsigned int src_cnt,
-		size_t len, enum sum_check_flags *result, unsigned long flags)
-{
-	struct dma_async_tx_descriptor *tx;
-	dma_addr_t pq[2];
-
-	/* validate P, disable Q */
-	pq[0] = src[0];
-	pq[1] = 0;
-	flags |= DMA_PREP_PQ_DISABLE_Q;
-
-	tx = ppc440spe_adma_prep_dma_pqzero_sum(chan, pq, &src[1],
-						src_cnt - 1, 0, len,
-						result, flags);
-	return tx;
-}
-
-/**
- * ppc440spe_adma_set_dest - set destination address into descriptor
- */
-static void ppc440spe_adma_set_dest(struct ppc440spe_adma_desc_slot *sw_desc,
-		dma_addr_t addr, int index)
-{
-	struct ppc440spe_adma_chan *chan;
-
-	BUG_ON(index >= sw_desc->dst_cnt);
-
-	chan = to_ppc440spe_adma_chan(sw_desc->async_tx.chan);
-
-	switch (chan->device->id) {
-	case PPC440SPE_DMA0_ID:
-	case PPC440SPE_DMA1_ID:
-		/* to do: support transfers lengths >
-		 * PPC440SPE_ADMA_DMA/XOR_MAX_BYTE_COUNT
-		 */
-		ppc440spe_desc_set_dest_addr(sw_desc->group_head,
-			chan, 0, addr, index);
-		break;
-	case PPC440SPE_XOR_ID:
-		sw_desc = ppc440spe_get_group_entry(sw_desc, index);
-		ppc440spe_desc_set_dest_addr(sw_desc,
-			chan, 0, addr, index);
-		break;
-	}
-}
-
-static void ppc440spe_adma_pq_zero_op(struct ppc440spe_adma_desc_slot *iter,
-		struct ppc440spe_adma_chan *chan, dma_addr_t addr)
-{
-	/*  To clear destinations update the descriptor
-	 * (P or Q depending on index) as follows:
-	 * addr is destination (0 corresponds to SG2):
-	 */
-	ppc440spe_desc_set_dest_addr(iter, chan, DMA_CUED_XOR_BASE, addr, 0);
-
-	/* ... and the addr is source: */
-	ppc440spe_desc_set_src_addr(iter, chan, 0, DMA_CUED_XOR_HB, addr);
-
-	/* addr is always SG2 then the mult is always DST1 */
-	ppc440spe_desc_set_src_mult(iter, chan, DMA_CUED_MULT1_OFF,
-				    DMA_CDB_SG_DST1, 1);
-}
-
-/**
- * ppc440spe_adma_pq_set_dest - set destination address into descriptor
- * for the PQXOR operation
- */
-static void ppc440spe_adma_pq_set_dest(struct ppc440spe_adma_desc_slot *sw_desc,
-		dma_addr_t *addrs, unsigned long flags)
-{
-	struct ppc440spe_adma_desc_slot *iter;
-	struct ppc440spe_adma_chan *chan;
-	dma_addr_t paddr, qaddr;
-	dma_addr_t addr = 0, ppath, qpath;
-	int index = 0, i;
-
-	chan = to_ppc440spe_adma_chan(sw_desc->async_tx.chan);
-
-	if (flags & DMA_PREP_PQ_DISABLE_P)
-		paddr = 0;
-	else
-		paddr = addrs[0];
-
-	if (flags & DMA_PREP_PQ_DISABLE_Q)
-		qaddr = 0;
-	else
-		qaddr = addrs[1];
-
-	if (!paddr || !qaddr)
-		addr = paddr ? paddr : qaddr;
-
-	switch (chan->device->id) {
-	case PPC440SPE_DMA0_ID:
-	case PPC440SPE_DMA1_ID:
-		/* walk through the WXOR source list and set P/Q-destinations
-		 * for each slot:
-		 */
-		if (!test_bit(PPC440SPE_DESC_RXOR, &sw_desc->flags)) {
-			/* This is WXOR-only chain; may have 1/2 zero descs */
-			if (test_bit(PPC440SPE_ZERO_P, &sw_desc->flags))
-				index++;
-			if (test_bit(PPC440SPE_ZERO_Q, &sw_desc->flags))
-				index++;
-
-			iter = ppc440spe_get_group_entry(sw_desc, index);
-			if (addr) {
-				/* one destination */
-				list_for_each_entry_from(iter,
-					&sw_desc->group_list, chain_node)
-					ppc440spe_desc_set_dest_addr(iter, chan,
-						DMA_CUED_XOR_BASE, addr, 0);
-			} else {
-				/* two destinations */
-				list_for_each_entry_from(iter,
-					&sw_desc->group_list, chain_node) {
-					ppc440spe_desc_set_dest_addr(iter, chan,
-						DMA_CUED_XOR_BASE, paddr, 0);
-					ppc440spe_desc_set_dest_addr(iter, chan,
-						DMA_CUED_XOR_BASE, qaddr, 1);
-				}
-			}
-
-			if (index) {
-				/*  To clear destinations update the descriptor
-				 * (1st,2nd, or both depending on flags)
-				 */
-				index = 0;
-				if (test_bit(PPC440SPE_ZERO_P,
-						&sw_desc->flags)) {
-					iter = ppc440spe_get_group_entry(
-							sw_desc, index++);
-					ppc440spe_adma_pq_zero_op(iter, chan,
-							paddr);
-				}
-
-				if (test_bit(PPC440SPE_ZERO_Q,
-						&sw_desc->flags)) {
-					iter = ppc440spe_get_group_entry(
-							sw_desc, index++);
-					ppc440spe_adma_pq_zero_op(iter, chan,
-							qaddr);
-				}
-
-				return;
-			}
-		} else {
-			/* This is RXOR-only or RXOR/WXOR mixed chain */
-
-			/* If we want to include destination into calculations,
-			 * then make dest addresses cued with mult=1 (XOR).
-			 */
-			ppath = test_bit(PPC440SPE_ZERO_P, &sw_desc->flags) ?
-					DMA_CUED_XOR_HB :
-					DMA_CUED_XOR_BASE |
-						(1 << DMA_CUED_MULT1_OFF);
-			qpath = test_bit(PPC440SPE_ZERO_Q, &sw_desc->flags) ?
-					DMA_CUED_XOR_HB :
-					DMA_CUED_XOR_BASE |
-						(1 << DMA_CUED_MULT1_OFF);
-
-			/* Setup destination(s) in RXOR slot(s) */
-			iter = ppc440spe_get_group_entry(sw_desc, index++);
-			ppc440spe_desc_set_dest_addr(iter, chan,
-						paddr ? ppath : qpath,
-						paddr ? paddr : qaddr, 0);
-			if (!addr) {
-				/* two destinations */
-				iter = ppc440spe_get_group_entry(sw_desc,
-								 index++);
-				ppc440spe_desc_set_dest_addr(iter, chan,
-						qpath, qaddr, 0);
-			}
-
-			if (test_bit(PPC440SPE_DESC_WXOR, &sw_desc->flags)) {
-				/* Setup destination(s) in remaining WXOR
-				 * slots
-				 */
-				iter = ppc440spe_get_group_entry(sw_desc,
-								 index);
-				if (addr) {
-					/* one destination */
-					list_for_each_entry_from(iter,
-					    &sw_desc->group_list,
-					    chain_node)
-						ppc440spe_desc_set_dest_addr(
-							iter, chan,
-							DMA_CUED_XOR_BASE,
-							addr, 0);
-
-				} else {
-					/* two destinations */
-					list_for_each_entry_from(iter,
-					    &sw_desc->group_list,
-					    chain_node) {
-						ppc440spe_desc_set_dest_addr(
-							iter, chan,
-							DMA_CUED_XOR_BASE,
-							paddr, 0);
-						ppc440spe_desc_set_dest_addr(
-							iter, chan,
-							DMA_CUED_XOR_BASE,
-							qaddr, 1);
-					}
-				}
-			}
-
-		}
-		break;
-
-	case PPC440SPE_XOR_ID:
-		/* DMA2 descriptors have only 1 destination, so there are
-		 * two chains - one for each dest.
-		 * If we want to include destination into calculations,
-		 * then make dest addresses cued with mult=1 (XOR).
-		 */
-		ppath = test_bit(PPC440SPE_ZERO_P, &sw_desc->flags) ?
-				DMA_CUED_XOR_HB :
-				DMA_CUED_XOR_BASE |
-					(1 << DMA_CUED_MULT1_OFF);
-
-		qpath = test_bit(PPC440SPE_ZERO_Q, &sw_desc->flags) ?
-				DMA_CUED_XOR_HB :
-				DMA_CUED_XOR_BASE |
-					(1 << DMA_CUED_MULT1_OFF);
-
-		iter = ppc440spe_get_group_entry(sw_desc, 0);
-		for (i = 0; i < sw_desc->descs_per_op; i++) {
-			ppc440spe_desc_set_dest_addr(iter, chan,
-				paddr ? ppath : qpath,
-				paddr ? paddr : qaddr, 0);
-			iter = list_entry(iter->chain_node.next,
-					  struct ppc440spe_adma_desc_slot,
-					  chain_node);
-		}
-
-		if (!addr) {
-			/* Two destinations; setup Q here */
-			iter = ppc440spe_get_group_entry(sw_desc,
-				sw_desc->descs_per_op);
-			for (i = 0; i < sw_desc->descs_per_op; i++) {
-				ppc440spe_desc_set_dest_addr(iter,
-					chan, qpath, qaddr, 0);
-				iter = list_entry(iter->chain_node.next,
-						struct ppc440spe_adma_desc_slot,
-						chain_node);
-			}
-		}
-
-		break;
-	}
-}
-
-/**
- * ppc440spe_adma_pq_zero_sum_set_dest - set destination address into descriptor
- * for the PQ_ZERO_SUM operation
- */
-static void ppc440spe_adma_pqzero_sum_set_dest(
-		struct ppc440spe_adma_desc_slot *sw_desc,
-		dma_addr_t paddr, dma_addr_t qaddr)
-{
-	struct ppc440spe_adma_desc_slot *iter, *end;
-	struct ppc440spe_adma_chan *chan;
-	dma_addr_t addr = 0;
-	int idx;
-
-	chan = to_ppc440spe_adma_chan(sw_desc->async_tx.chan);
-
-	/* walk through the WXOR source list and set P/Q-destinations
-	 * for each slot
-	 */
-	idx = (paddr && qaddr) ? 2 : 1;
-	/* set end */
-	list_for_each_entry_reverse(end, &sw_desc->group_list,
-				    chain_node) {
-		if (!(--idx))
-			break;
-	}
-	/* set start */
-	idx = (paddr && qaddr) ? 2 : 1;
-	iter = ppc440spe_get_group_entry(sw_desc, idx);
-
-	if (paddr && qaddr) {
-		/* two destinations */
-		list_for_each_entry_from(iter, &sw_desc->group_list,
-					 chain_node) {
-			if (unlikely(iter == end))
-				break;
-			ppc440spe_desc_set_dest_addr(iter, chan,
-						DMA_CUED_XOR_BASE, paddr, 0);
-			ppc440spe_desc_set_dest_addr(iter, chan,
-						DMA_CUED_XOR_BASE, qaddr, 1);
-		}
-	} else {
-		/* one destination */
-		addr = paddr ? paddr : qaddr;
-		list_for_each_entry_from(iter, &sw_desc->group_list,
-					 chain_node) {
-			if (unlikely(iter == end))
-				break;
-			ppc440spe_desc_set_dest_addr(iter, chan,
-						DMA_CUED_XOR_BASE, addr, 0);
-		}
-	}
-
-	/*  The remaining descriptors are DATACHECK. These have no need in
-	 * destination. Actually, these destinations are used there
-	 * as sources for check operation. So, set addr as source.
-	 */
-	ppc440spe_desc_set_src_addr(end, chan, 0, 0, addr ? addr : paddr);
-
-	if (!addr) {
-		end = list_entry(end->chain_node.next,
-				 struct ppc440spe_adma_desc_slot, chain_node);
-		ppc440spe_desc_set_src_addr(end, chan, 0, 0, qaddr);
-	}
-}
-
-/**
- * ppc440spe_desc_set_xor_src_cnt - set source count into descriptor
- */
-static inline void ppc440spe_desc_set_xor_src_cnt(
-			struct ppc440spe_adma_desc_slot *desc,
-			int src_cnt)
-{
-	struct xor_cb *hw_desc = desc->hw_desc;
-
-	hw_desc->cbc &= ~XOR_CDCR_OAC_MSK;
-	hw_desc->cbc |= src_cnt;
-}
-
-/**
- * ppc440spe_adma_pq_set_src - set source address into descriptor
- */
-static void ppc440spe_adma_pq_set_src(struct ppc440spe_adma_desc_slot *sw_desc,
-		dma_addr_t addr, int index)
-{
-	struct ppc440spe_adma_chan *chan;
-	dma_addr_t haddr = 0;
-	struct ppc440spe_adma_desc_slot *iter = NULL;
-
-	chan = to_ppc440spe_adma_chan(sw_desc->async_tx.chan);
-
-	switch (chan->device->id) {
-	case PPC440SPE_DMA0_ID:
-	case PPC440SPE_DMA1_ID:
-		/* DMA0,1 may do: WXOR, RXOR, RXOR+WXORs chain
-		 */
-		if (test_bit(PPC440SPE_DESC_RXOR, &sw_desc->flags)) {
-			/* RXOR-only or RXOR/WXOR operation */
-			int iskip = test_bit(PPC440SPE_DESC_RXOR12,
-				&sw_desc->flags) ?  2 : 3;
-
-			if (index == 0) {
-				/* 1st slot (RXOR) */
-				/* setup sources region (R1-2-3, R1-2-4,
-				 * or R1-2-5)
-				 */
-				if (test_bit(PPC440SPE_DESC_RXOR12,
-						&sw_desc->flags))
-					haddr = DMA_RXOR12 <<
-						DMA_CUED_REGION_OFF;
-				else if (test_bit(PPC440SPE_DESC_RXOR123,
-				    &sw_desc->flags))
-					haddr = DMA_RXOR123 <<
-						DMA_CUED_REGION_OFF;
-				else if (test_bit(PPC440SPE_DESC_RXOR124,
-				    &sw_desc->flags))
-					haddr = DMA_RXOR124 <<
-						DMA_CUED_REGION_OFF;
-				else if (test_bit(PPC440SPE_DESC_RXOR125,
-				    &sw_desc->flags))
-					haddr = DMA_RXOR125 <<
-						DMA_CUED_REGION_OFF;
-				else
-					BUG();
-				haddr |= DMA_CUED_XOR_BASE;
-				iter = ppc440spe_get_group_entry(sw_desc, 0);
-			} else if (index < iskip) {
-				/* 1st slot (RXOR)
-				 * shall actually set source address only once
-				 * instead of first <iskip>
-				 */
-				iter = NULL;
-			} else {
-				/* 2nd/3d and next slots (WXOR);
-				 * skip first slot with RXOR
-				 */
-				haddr = DMA_CUED_XOR_HB;
-				iter = ppc440spe_get_group_entry(sw_desc,
-				    index - iskip + sw_desc->dst_cnt);
-			}
-		} else {
-			int znum = 0;
-
-			/* WXOR-only operation; skip first slots with
-			 * zeroing destinations
-			 */
-			if (test_bit(PPC440SPE_ZERO_P, &sw_desc->flags))
-				znum++;
-			if (test_bit(PPC440SPE_ZERO_Q, &sw_desc->flags))
-				znum++;
-
-			haddr = DMA_CUED_XOR_HB;
-			iter = ppc440spe_get_group_entry(sw_desc,
-					index + znum);
-		}
-
-		if (likely(iter)) {
-			ppc440spe_desc_set_src_addr(iter, chan, 0, haddr, addr);
-
-			if (!index &&
-			    test_bit(PPC440SPE_DESC_RXOR, &sw_desc->flags) &&
-			    sw_desc->dst_cnt == 2) {
-				/* if we have two destinations for RXOR, then
-				 * setup source in the second descr too
-				 */
-				iter = ppc440spe_get_group_entry(sw_desc, 1);
-				ppc440spe_desc_set_src_addr(iter, chan, 0,
-					haddr, addr);
-			}
-		}
-		break;
-
-	case PPC440SPE_XOR_ID:
-		/* DMA2 may do Biskup */
-		iter = sw_desc->group_head;
-		if (iter->dst_cnt == 2) {
-			/* both P & Q calculations required; set P src here */
-			ppc440spe_adma_dma2rxor_set_src(iter, index, addr);
-
-			/* this is for Q */
-			iter = ppc440spe_get_group_entry(sw_desc,
-				sw_desc->descs_per_op);
-		}
-		ppc440spe_adma_dma2rxor_set_src(iter, index, addr);
-		break;
-	}
-}
-
-/**
- * ppc440spe_adma_memcpy_xor_set_src - set source address into descriptor
- */
-static void ppc440spe_adma_memcpy_xor_set_src(
-		struct ppc440spe_adma_desc_slot *sw_desc,
-		dma_addr_t addr, int index)
-{
-	struct ppc440spe_adma_chan *chan;
-
-	chan = to_ppc440spe_adma_chan(sw_desc->async_tx.chan);
-	sw_desc = sw_desc->group_head;
-
-	if (likely(sw_desc))
-		ppc440spe_desc_set_src_addr(sw_desc, chan, index, 0, addr);
-}
-
-/**
- * ppc440spe_adma_dma2rxor_inc_addr  -
- */
-static void ppc440spe_adma_dma2rxor_inc_addr(
-		struct ppc440spe_adma_desc_slot *desc,
-		struct ppc440spe_rxor *cursor, int index, int src_cnt)
-{
-	cursor->addr_count++;
-	if (index == src_cnt - 1) {
-		ppc440spe_desc_set_xor_src_cnt(desc, cursor->addr_count);
-	} else if (cursor->addr_count == XOR_MAX_OPS) {
-		ppc440spe_desc_set_xor_src_cnt(desc, cursor->addr_count);
-		cursor->addr_count = 0;
-		cursor->desc_count++;
-	}
-}
-
-/**
- * ppc440spe_adma_dma2rxor_prep_src - setup RXOR types in DMA2 CDB
- */
-static int ppc440spe_adma_dma2rxor_prep_src(
-		struct ppc440spe_adma_desc_slot *hdesc,
-		struct ppc440spe_rxor *cursor, int index,
-		int src_cnt, u32 addr)
-{
-	int rval = 0;
-	u32 sign;
-	struct ppc440spe_adma_desc_slot *desc = hdesc;
-	int i;
-
-	for (i = 0; i < cursor->desc_count; i++) {
-		desc = list_entry(hdesc->chain_node.next,
-				  struct ppc440spe_adma_desc_slot,
-				  chain_node);
-	}
-
-	switch (cursor->state) {
-	case 0:
-		if (addr == cursor->addrl + cursor->len) {
-			/* direct RXOR */
-			cursor->state = 1;
-			cursor->xor_count++;
-			if (index == src_cnt-1) {
-				ppc440spe_rxor_set_region(desc,
-					cursor->addr_count,
-					DMA_RXOR12 << DMA_CUED_REGION_OFF);
-				ppc440spe_adma_dma2rxor_inc_addr(
-					desc, cursor, index, src_cnt);
-			}
-		} else if (cursor->addrl == addr + cursor->len) {
-			/* reverse RXOR */
-			cursor->state = 1;
-			cursor->xor_count++;
-			set_bit(cursor->addr_count, &desc->reverse_flags[0]);
-			if (index == src_cnt-1) {
-				ppc440spe_rxor_set_region(desc,
-					cursor->addr_count,
-					DMA_RXOR12 << DMA_CUED_REGION_OFF);
-				ppc440spe_adma_dma2rxor_inc_addr(
-					desc, cursor, index, src_cnt);
-			}
-		} else {
-			printk(KERN_ERR "Cannot build "
-				"DMA2 RXOR command block.\n");
-			BUG();
-		}
-		break;
-	case 1:
-		sign = test_bit(cursor->addr_count,
-				desc->reverse_flags)
-			? -1 : 1;
-		if (index == src_cnt-2 || (sign == -1
-			&& addr != cursor->addrl - 2*cursor->len)) {
-			cursor->state = 0;
-			cursor->xor_count = 1;
-			cursor->addrl = addr;
-			ppc440spe_rxor_set_region(desc,
-				cursor->addr_count,
-				DMA_RXOR12 << DMA_CUED_REGION_OFF);
-			ppc440spe_adma_dma2rxor_inc_addr(
-				desc, cursor, index, src_cnt);
-		} else if (addr == cursor->addrl + 2*sign*cursor->len) {
-			cursor->state = 2;
-			cursor->xor_count = 0;
-			ppc440spe_rxor_set_region(desc,
-				cursor->addr_count,
-				DMA_RXOR123 << DMA_CUED_REGION_OFF);
-			if (index == src_cnt-1) {
-				ppc440spe_adma_dma2rxor_inc_addr(
-					desc, cursor, index, src_cnt);
-			}
-		} else if (addr == cursor->addrl + 3*cursor->len) {
-			cursor->state = 2;
-			cursor->xor_count = 0;
-			ppc440spe_rxor_set_region(desc,
-				cursor->addr_count,
-				DMA_RXOR124 << DMA_CUED_REGION_OFF);
-			if (index == src_cnt-1) {
-				ppc440spe_adma_dma2rxor_inc_addr(
-					desc, cursor, index, src_cnt);
-			}
-		} else if (addr == cursor->addrl + 4*cursor->len) {
-			cursor->state = 2;
-			cursor->xor_count = 0;
-			ppc440spe_rxor_set_region(desc,
-				cursor->addr_count,
-				DMA_RXOR125 << DMA_CUED_REGION_OFF);
-			if (index == src_cnt-1) {
-				ppc440spe_adma_dma2rxor_inc_addr(
-					desc, cursor, index, src_cnt);
-			}
-		} else {
-			cursor->state = 0;
-			cursor->xor_count = 1;
-			cursor->addrl = addr;
-			ppc440spe_rxor_set_region(desc,
-				cursor->addr_count,
-				DMA_RXOR12 << DMA_CUED_REGION_OFF);
-			ppc440spe_adma_dma2rxor_inc_addr(
-				desc, cursor, index, src_cnt);
-		}
-		break;
-	case 2:
-		cursor->state = 0;
-		cursor->addrl = addr;
-		cursor->xor_count++;
-		if (index) {
-			ppc440spe_adma_dma2rxor_inc_addr(
-				desc, cursor, index, src_cnt);
-		}
-		break;
-	}
-
-	return rval;
-}
-
-/**
- * ppc440spe_adma_dma2rxor_set_src - set RXOR source address; it's assumed that
- *	ppc440spe_adma_dma2rxor_prep_src() has already done prior this call
- */
-static void ppc440spe_adma_dma2rxor_set_src(
-		struct ppc440spe_adma_desc_slot *desc,
-		int index, dma_addr_t addr)
-{
-	struct xor_cb *xcb = desc->hw_desc;
-	int k = 0, op = 0, lop = 0;
-
-	/* get the RXOR operand which corresponds to index addr */
-	while (op <= index) {
-		lop = op;
-		if (k == XOR_MAX_OPS) {
-			k = 0;
-			desc = list_entry(desc->chain_node.next,
-				struct ppc440spe_adma_desc_slot, chain_node);
-			xcb = desc->hw_desc;
-
-		}
-		if ((xcb->ops[k++].h & (DMA_RXOR12 << DMA_CUED_REGION_OFF)) ==
-		    (DMA_RXOR12 << DMA_CUED_REGION_OFF))
-			op += 2;
-		else
-			op += 3;
-	}
-
-	BUG_ON(k < 1);
-
-	if (test_bit(k-1, desc->reverse_flags)) {
-		/* reverse operand order; put last op in RXOR group */
-		if (index == op - 1)
-			ppc440spe_rxor_set_src(desc, k - 1, addr);
-	} else {
-		/* direct operand order; put first op in RXOR group */
-		if (index == lop)
-			ppc440spe_rxor_set_src(desc, k - 1, addr);
-	}
-}
-
-/**
- * ppc440spe_adma_dma2rxor_set_mult - set RXOR multipliers; it's assumed that
- *	ppc440spe_adma_dma2rxor_prep_src() has already done prior this call
- */
-static void ppc440spe_adma_dma2rxor_set_mult(
-		struct ppc440spe_adma_desc_slot *desc,
-		int index, u8 mult)
-{
-	struct xor_cb *xcb = desc->hw_desc;
-	int k = 0, op = 0, lop = 0;
-
-	/* get the RXOR operand which corresponds to index mult */
-	while (op <= index) {
-		lop = op;
-		if (k == XOR_MAX_OPS) {
-			k = 0;
-			desc = list_entry(desc->chain_node.next,
-					  struct ppc440spe_adma_desc_slot,
-					  chain_node);
-			xcb = desc->hw_desc;
-
-		}
-		if ((xcb->ops[k++].h & (DMA_RXOR12 << DMA_CUED_REGION_OFF)) ==
-		    (DMA_RXOR12 << DMA_CUED_REGION_OFF))
-			op += 2;
-		else
-			op += 3;
-	}
-
-	BUG_ON(k < 1);
-	if (test_bit(k-1, desc->reverse_flags)) {
-		/* reverse order */
-		ppc440spe_rxor_set_mult(desc, k - 1, op - index - 1, mult);
-	} else {
-		/* direct order */
-		ppc440spe_rxor_set_mult(desc, k - 1, index - lop, mult);
-	}
-}
-
-/**
- * ppc440spe_init_rxor_cursor -
- */
-static void ppc440spe_init_rxor_cursor(struct ppc440spe_rxor *cursor)
-{
-	memset(cursor, 0, sizeof(struct ppc440spe_rxor));
-	cursor->state = 2;
-}
-
-/**
- * ppc440spe_adma_pq_set_src_mult - set multiplication coefficient into
- * descriptor for the PQXOR operation
- */
-static void ppc440spe_adma_pq_set_src_mult(
-		struct ppc440spe_adma_desc_slot *sw_desc,
-		unsigned char mult, int index, int dst_pos)
-{
-	struct ppc440spe_adma_chan *chan;
-	u32 mult_idx, mult_dst;
-	struct ppc440spe_adma_desc_slot *iter = NULL, *iter1 = NULL;
-
-	chan = to_ppc440spe_adma_chan(sw_desc->async_tx.chan);
-
-	switch (chan->device->id) {
-	case PPC440SPE_DMA0_ID:
-	case PPC440SPE_DMA1_ID:
-		if (test_bit(PPC440SPE_DESC_RXOR, &sw_desc->flags)) {
-			int region = test_bit(PPC440SPE_DESC_RXOR12,
-					&sw_desc->flags) ? 2 : 3;
-
-			if (index < region) {
-				/* RXOR multipliers */
-				iter = ppc440spe_get_group_entry(sw_desc,
-					sw_desc->dst_cnt - 1);
-				if (sw_desc->dst_cnt == 2)
-					iter1 = ppc440spe_get_group_entry(
-							sw_desc, 0);
-
-				mult_idx = DMA_CUED_MULT1_OFF + (index << 3);
-				mult_dst = DMA_CDB_SG_SRC;
-			} else {
-				/* WXOR multiplier */
-				iter = ppc440spe_get_group_entry(sw_desc,
-							index - region +
-							sw_desc->dst_cnt);
-				mult_idx = DMA_CUED_MULT1_OFF;
-				mult_dst = dst_pos ? DMA_CDB_SG_DST2 :
-						     DMA_CDB_SG_DST1;
-			}
-		} else {
-			int znum = 0;
-
-			/* WXOR-only;
-			 * skip first slots with destinations (if ZERO_DST has
-			 * place)
-			 */
-			if (test_bit(PPC440SPE_ZERO_P, &sw_desc->flags))
-				znum++;
-			if (test_bit(PPC440SPE_ZERO_Q, &sw_desc->flags))
-				znum++;
-
-			iter = ppc440spe_get_group_entry(sw_desc, index + znum);
-			mult_idx = DMA_CUED_MULT1_OFF;
-			mult_dst = dst_pos ? DMA_CDB_SG_DST2 : DMA_CDB_SG_DST1;
-		}
-
-		if (likely(iter)) {
-			ppc440spe_desc_set_src_mult(iter, chan,
-				mult_idx, mult_dst, mult);
-
-			if (unlikely(iter1)) {
-				/* if we have two destinations for RXOR, then
-				 * we've just set Q mult. Set-up P now.
-				 */
-				ppc440spe_desc_set_src_mult(iter1, chan,
-					mult_idx, mult_dst, 1);
-			}
-
-		}
-		break;
-
-	case PPC440SPE_XOR_ID:
-		iter = sw_desc->group_head;
-		if (sw_desc->dst_cnt == 2) {
-			/* both P & Q calculations required; set P mult here */
-			ppc440spe_adma_dma2rxor_set_mult(iter, index, 1);
-
-			/* and then set Q mult */
-			iter = ppc440spe_get_group_entry(sw_desc,
-			       sw_desc->descs_per_op);
-		}
-		ppc440spe_adma_dma2rxor_set_mult(iter, index, mult);
-		break;
-	}
-}
-
 /**
  * ppc440spe_adma_free_chan_resources - free the resources allocated
  */
@@ -3971,7 +924,7 @@ static enum dma_status ppc440spe_adma_tx_status(struct dma_chan *chan,
 /**
  * ppc440spe_adma_eot_handler - end of transfer interrupt handler
  */
-static irqreturn_t ppc440spe_adma_eot_handler(int irq, void *data)
+irqreturn_t ppc440spe_adma_eot_handler(int irq, void *data)
 {
 	struct ppc440spe_adma_chan *chan = data;
 
@@ -3988,7 +941,7 @@ static irqreturn_t ppc440spe_adma_eot_handler(int irq, void *data)
  * ppc440spe_adma_err_handler - DMA error interrupt handler;
  *	do the same things as a eot handler
  */
-static irqreturn_t ppc440spe_adma_err_handler(int irq, void *data)
+irqreturn_t ppc440spe_adma_err_handler(int irq, void *data)
 {
 	struct ppc440spe_adma_chan *chan = data;
 
@@ -4002,17 +955,9 @@ static irqreturn_t ppc440spe_adma_err_handler(int irq, void *data)
 }
 
 /**
- * ppc440spe_test_callback - called when test operation has been done
- */
-static void ppc440spe_test_callback(void *unused)
-{
-	complete(&ppc440spe_r6_test_comp);
-}
-
-/**
  * ppc440spe_adma_issue_pending - flush all pending descriptors to h/w
  */
-static void ppc440spe_adma_issue_pending(struct dma_chan *chan)
+void ppc440spe_adma_issue_pending(struct dma_chan *chan)
 {
 	struct ppc440spe_adma_chan *ppc440spe_chan;
 
@@ -4032,7 +977,7 @@ static void ppc440spe_adma_issue_pending(struct dma_chan *chan)
  *	use FIFOs (as opposite to chains used in XOR) so this is a XOR
  *	specific operation)
  */
-static void ppc440spe_chan_start_null_xor(struct ppc440spe_adma_chan *chan)
+void ppc440spe_chan_start_null_xor(struct ppc440spe_adma_chan *chan)
 {
 	struct ppc440spe_adma_desc_slot *sw_desc, *group_start;
 	dma_cookie_t cookie;
@@ -4076,100 +1021,8 @@ static void ppc440spe_chan_start_null_xor(struct ppc440spe_adma_chan *chan)
 	spin_unlock_bh(&chan->lock);
 }
 
-/**
- * ppc440spe_test_raid6 - test are RAID-6 capabilities enabled successfully.
- *	For this we just perform one WXOR operation with the same source
- *	and destination addresses, the GF-multiplier is 1; so if RAID-6
- *	capabilities are enabled then we'll get src/dst filled with zero.
- */
-static int ppc440spe_test_raid6(struct ppc440spe_adma_chan *chan)
-{
-	struct ppc440spe_adma_desc_slot *sw_desc, *iter;
-	struct page *pg;
-	char *a;
-	dma_addr_t dma_addr, addrs[2];
-	unsigned long op = 0;
-	int rval = 0;
-
-	set_bit(PPC440SPE_DESC_WXOR, &op);
-
-	pg = alloc_page(GFP_KERNEL);
-	if (!pg)
-		return -ENOMEM;
-
-	spin_lock_bh(&chan->lock);
-	sw_desc = ppc440spe_adma_alloc_slots(chan, 1, 1);
-	if (sw_desc) {
-		/* 1 src, 1 dsr, int_ena, WXOR */
-		ppc440spe_desc_init_dma01pq(sw_desc, 1, 1, 1, op);
-		list_for_each_entry(iter, &sw_desc->group_list, chain_node) {
-			ppc440spe_desc_set_byte_count(iter, chan, PAGE_SIZE);
-			iter->unmap_len = PAGE_SIZE;
-		}
-	} else {
-		rval = -EFAULT;
-		spin_unlock_bh(&chan->lock);
-		goto exit;
-	}
-	spin_unlock_bh(&chan->lock);
-
-	/* Fill the test page with ones */
-	memset(page_address(pg), 0xFF, PAGE_SIZE);
-	dma_addr = dma_map_page(chan->device->dev, pg, 0,
-				PAGE_SIZE, DMA_BIDIRECTIONAL);
-
-	/* Setup addresses */
-	ppc440spe_adma_pq_set_src(sw_desc, dma_addr, 0);
-	ppc440spe_adma_pq_set_src_mult(sw_desc, 1, 0, 0);
-	addrs[0] = dma_addr;
-	addrs[1] = 0;
-	ppc440spe_adma_pq_set_dest(sw_desc, addrs, DMA_PREP_PQ_DISABLE_Q);
-
-	async_tx_ack(&sw_desc->async_tx);
-	sw_desc->async_tx.callback = ppc440spe_test_callback;
-	sw_desc->async_tx.callback_param = NULL;
-
-	init_completion(&ppc440spe_r6_test_comp);
-
-	ppc440spe_adma_tx_submit(&sw_desc->async_tx);
-	ppc440spe_adma_issue_pending(&chan->common);
-
-	wait_for_completion(&ppc440spe_r6_test_comp);
-
-	/* Now check if the test page is zeroed */
-	a = page_address(pg);
-	if ((*(u32 *)a) == 0 && memcmp(a, a+4, PAGE_SIZE-4) == 0) {
-		/* page is zero - RAID-6 enabled */
-		rval = 0;
-	} else {
-		/* RAID-6 was not enabled */
-		rval = -EINVAL;
-	}
-exit:
-	__free_page(pg);
-	return rval;
-}
-
 static void ppc440spe_adma_init_capabilities(struct ppc440spe_adma_device *adev)
 {
-	switch (adev->id) {
-	case PPC440SPE_DMA0_ID:
-	case PPC440SPE_DMA1_ID:
-		dma_cap_set(DMA_MEMCPY, adev->common.cap_mask);
-		dma_cap_set(DMA_INTERRUPT, adev->common.cap_mask);
-		dma_cap_set(DMA_MEMSET, adev->common.cap_mask);
-		dma_cap_set(DMA_PQ, adev->common.cap_mask);
-		dma_cap_set(DMA_PQ_VAL, adev->common.cap_mask);
-		dma_cap_set(DMA_XOR_VAL, adev->common.cap_mask);
-		break;
-	case PPC440SPE_XOR_ID:
-		dma_cap_set(DMA_XOR, adev->common.cap_mask);
-		dma_cap_set(DMA_PQ, adev->common.cap_mask);
-		dma_cap_set(DMA_INTERRUPT, adev->common.cap_mask);
-		adev->common.cap_mask = adev->common.cap_mask;
-		break;
-	}
-
 	/* Set base routines */
 	adev->common.device_alloc_chan_resources =
 				ppc440spe_adma_alloc_chan_resources;
@@ -4193,47 +1046,14 @@ static void ppc440spe_adma_init_capabilities(struct ppc440spe_adma_device *adev)
 			ppc440spe_adma_prep_dma_xor;
 	}
 	if (dma_has_cap(DMA_PQ, adev->common.cap_mask)) {
-		switch (adev->id) {
-		case PPC440SPE_DMA0_ID:
-			dma_set_maxpq(&adev->common,
-				DMA0_FIFO_SIZE / sizeof(struct dma_cdb), 0);
-			break;
-		case PPC440SPE_DMA1_ID:
-			dma_set_maxpq(&adev->common,
-				DMA1_FIFO_SIZE / sizeof(struct dma_cdb), 0);
-			break;
-		case PPC440SPE_XOR_ID:
-			adev->common.max_pq = XOR_MAX_OPS * 3;
-			break;
-		}
 		adev->common.device_prep_dma_pq =
 			ppc440spe_adma_prep_dma_pq;
 	}
 	if (dma_has_cap(DMA_PQ_VAL, adev->common.cap_mask)) {
-		switch (adev->id) {
-		case PPC440SPE_DMA0_ID:
-			adev->common.max_pq = DMA0_FIFO_SIZE /
-						sizeof(struct dma_cdb);
-			break;
-		case PPC440SPE_DMA1_ID:
-			adev->common.max_pq = DMA1_FIFO_SIZE /
-						sizeof(struct dma_cdb);
-			break;
-		}
 		adev->common.device_prep_dma_pq_val =
 			ppc440spe_adma_prep_dma_pqzero_sum;
 	}
 	if (dma_has_cap(DMA_XOR_VAL, adev->common.cap_mask)) {
-		switch (adev->id) {
-		case PPC440SPE_DMA0_ID:
-			adev->common.max_xor = DMA0_FIFO_SIZE /
-						sizeof(struct dma_cdb);
-			break;
-		case PPC440SPE_DMA1_ID:
-			adev->common.max_xor = DMA1_FIFO_SIZE /
-						sizeof(struct dma_cdb);
-			break;
-		}
 		adev->common.device_prep_dma_xor_val =
 			ppc440spe_adma_prep_dma_xor_zero_sum;
 	}
@@ -4241,205 +1061,34 @@ static void ppc440spe_adma_init_capabilities(struct ppc440spe_adma_device *adev)
 		adev->common.device_prep_dma_interrupt =
 			ppc440spe_adma_prep_dma_interrupt;
 	}
-	pr_info("%s: AMCC(R) PPC440SP(E) ADMA Engine: "
-	  "( %s%s%s%s%s%s%s)\n",
-	  dev_name(adev->dev),
-	  dma_has_cap(DMA_PQ, adev->common.cap_mask) ? "pq " : "",
-	  dma_has_cap(DMA_PQ_VAL, adev->common.cap_mask) ? "pq_val " : "",
-	  dma_has_cap(DMA_XOR, adev->common.cap_mask) ? "xor " : "",
-	  dma_has_cap(DMA_XOR_VAL, adev->common.cap_mask) ? "xor_val " : "",
-	  dma_has_cap(DMA_MEMCPY, adev->common.cap_mask) ? "memcpy " : "",
-	  dma_has_cap(DMA_MEMSET, adev->common.cap_mask)  ? "memset " : "",
-	  dma_has_cap(DMA_INTERRUPT, adev->common.cap_mask) ? "intr " : "");
-}
-
-static int ppc440spe_adma_setup_irqs(struct ppc440spe_adma_device *adev,
-				     struct ppc440spe_adma_chan *chan,
-				     int *initcode)
-{
-	struct platform_device *ofdev;
-	struct device_node *np;
-	int ret;
-
-	ofdev = container_of(adev->dev, struct platform_device, dev);
-	np = ofdev->dev.of_node;
-	if (adev->id != PPC440SPE_XOR_ID) {
-		adev->err_irq = irq_of_parse_and_map(np, 1);
-		if (adev->err_irq == NO_IRQ) {
-			dev_warn(adev->dev, "no err irq resource?\n");
-			*initcode = PPC_ADMA_INIT_IRQ2;
-			adev->err_irq = -ENXIO;
-		} else
-			atomic_inc(&ppc440spe_adma_err_irq_ref);
-	} else {
-		adev->err_irq = -ENXIO;
-	}
-
-	adev->irq = irq_of_parse_and_map(np, 0);
-	if (adev->irq == NO_IRQ) {
-		dev_err(adev->dev, "no irq resource\n");
-		*initcode = PPC_ADMA_INIT_IRQ1;
-		ret = -ENXIO;
-		goto err_irq_map;
-	}
-	dev_dbg(adev->dev, "irq %d, err irq %d\n",
-		adev->irq, adev->err_irq);
-
-	ret = request_irq(adev->irq, ppc440spe_adma_eot_handler,
-			  0, dev_driver_string(adev->dev), chan);
-	if (ret) {
-		dev_err(adev->dev, "can't request irq %d\n",
-			adev->irq);
-		*initcode = PPC_ADMA_INIT_IRQ1;
-		ret = -EIO;
-		goto err_req1;
-	}
-
-	/* only DMA engines have a separate error IRQ
-	 * so it's Ok if err_irq < 0 in XOR engine case.
-	 */
-	if (adev->err_irq > 0) {
-		/* both DMA engines share common error IRQ */
-		ret = request_irq(adev->err_irq,
-				  ppc440spe_adma_err_handler,
-				  IRQF_SHARED,
-				  dev_driver_string(adev->dev),
-				  chan);
-		if (ret) {
-			dev_err(adev->dev, "can't request irq %d\n",
-				adev->err_irq);
-			*initcode = PPC_ADMA_INIT_IRQ2;
-			ret = -EIO;
-			goto err_req2;
-		}
-	}
-
-	if (adev->id == PPC440SPE_XOR_ID) {
-		/* enable XOR engine interrupts */
-		iowrite32be(XOR_IE_CBCIE_BIT | XOR_IE_ICBIE_BIT |
-			    XOR_IE_ICIE_BIT | XOR_IE_RPTIE_BIT,
-			    &adev->xor_reg->ier);
-	} else {
-		u32 mask, enable;
-
-		np = of_find_compatible_node(NULL, NULL, "ibm,i2o-440spe");
-		if (!np) {
-			pr_err("%s: can't find I2O device tree node\n",
-				__func__);
-			ret = -ENODEV;
-			goto err_req2;
-		}
-		adev->i2o_reg = of_iomap(np, 0);
-		if (!adev->i2o_reg) {
-			pr_err("%s: failed to map I2O registers\n", __func__);
-			of_node_put(np);
-			ret = -EINVAL;
-			goto err_req2;
-		}
-		of_node_put(np);
-		/* Unmask 'CS FIFO Attention' interrupts and
-		 * enable generating interrupts on errors
-		 */
-		enable = (adev->id == PPC440SPE_DMA0_ID) ?
-			 ~(I2O_IOPIM_P0SNE | I2O_IOPIM_P0EM) :
-			 ~(I2O_IOPIM_P1SNE | I2O_IOPIM_P1EM);
-		mask = ioread32(&adev->i2o_reg->iopim) & enable;
-		iowrite32(mask, &adev->i2o_reg->iopim);
-	}
-	return 0;
-
-err_req2:
-	free_irq(adev->irq, chan);
-err_req1:
-	irq_dispose_mapping(adev->irq);
-err_irq_map:
-	if (adev->err_irq > 0) {
-		if (atomic_dec_and_test(&ppc440spe_adma_err_irq_ref))
-			irq_dispose_mapping(adev->err_irq);
-	}
-	return ret;
-}
-
-static void ppc440spe_adma_release_irqs(struct ppc440spe_adma_device *adev,
-					struct ppc440spe_adma_chan *chan)
-{
-	u32 mask, disable;
-
-	if (adev->id == PPC440SPE_XOR_ID) {
-		/* disable XOR engine interrupts */
-		mask = ioread32be(&adev->xor_reg->ier);
-		mask &= ~(XOR_IE_CBCIE_BIT | XOR_IE_ICBIE_BIT |
-			  XOR_IE_ICIE_BIT | XOR_IE_RPTIE_BIT);
-		iowrite32be(mask, &adev->xor_reg->ier);
-	} else {
-		/* disable DMAx engine interrupts */
-		disable = (adev->id == PPC440SPE_DMA0_ID) ?
-			  (I2O_IOPIM_P0SNE | I2O_IOPIM_P0EM) :
-			  (I2O_IOPIM_P1SNE | I2O_IOPIM_P1EM);
-		mask = ioread32(&adev->i2o_reg->iopim) | disable;
-		iowrite32(mask, &adev->i2o_reg->iopim);
-	}
-	free_irq(adev->irq, chan);
-	irq_dispose_mapping(adev->irq);
-	if (adev->err_irq > 0) {
-		free_irq(adev->err_irq, chan);
-		if (atomic_dec_and_test(&ppc440spe_adma_err_irq_ref)) {
-			irq_dispose_mapping(adev->err_irq);
-			iounmap(adev->i2o_reg);
-		}
-	}
 }
 
 /**
  * ppc440spe_adma_probe - probe the asynch device
  */
-static int __devinit ppc440spe_adma_probe(struct platform_device *ofdev,
+int __devinit ppc440spe_adma_probe(struct platform_device *ofdev,
 					  const struct of_device_id *match)
 {
 	struct device_node *np = ofdev->dev.of_node;
 	struct resource res;
-	struct ppc440spe_adma_device *adev;
+	struct ppc440spe_adma_device *adev = NULL;
 	struct ppc440spe_adma_chan *chan;
 	struct ppc_dma_chan_ref *ref, *_ref;
 	int ret = 0, initcode = PPC_ADMA_INIT_OK;
-	const u32 *idx;
-	int len;
 	void *regs;
-	u32 id, pool_size;
-
-	if (of_device_is_compatible(np, "amcc,xor-accelerator")) {
-		id = PPC440SPE_XOR_ID;
-		/* As far as the XOR engine is concerned, it does not
-		 * use FIFOs but uses linked list. So there is no dependency
-		 * between pool size to allocate and the engine configuration.
-		 */
-		pool_size = PAGE_SIZE << 1;
-	} else {
-		/* it is DMA0 or DMA1 */
-		idx = of_get_property(np, "cell-index", &len);
-		if (!idx || (len != sizeof(u32))) {
-			dev_err(&ofdev->dev, "Device node %s has missing "
-				"or invalid cell-index property\n",
-				np->full_name);
-			return -EINVAL;
-		}
-		id = *idx;
-		/* DMA0,1 engines use FIFO to maintain CDBs, so we
-		 * should allocate the pool accordingly to size of this
-		 * FIFO. Thus, the pool size depends on the FIFO depth:
-		 * how much CDBs pointers the FIFO may contain then so
-		 * much CDBs we should provide in the pool.
-		 * That is
-		 *   CDB size = 32B;
-		 *   CDBs number = (DMA0_FIFO_SIZE >> 3);
-		 *   Pool size = CDBs number * CDB size =
-		 *      = (DMA0_FIFO_SIZE >> 3) << 5 = DMA0_FIFO_SIZE << 2.
-		 */
-		pool_size = (id == PPC440SPE_DMA0_ID) ?
-			    DMA0_FIFO_SIZE : DMA1_FIFO_SIZE;
-		pool_size <<= 2;
-	}
+	u32 id = 0, pool_size;
 
+	/*
+	 *  get device ID
+	 */
+	adev->id = ppc440spe_adma_get_devid(ofdev, np);
+	/*
+	 * Get DMA pool size
+	 */
+	pool_size = ppc440spe_adma_get_pool_size(np, adev->id);
+	/*
+	 * Get resource info
+	 */
 	if (of_address_to_resource(np, 0, &res)) {
 		dev_err(&ofdev->dev, "failed to get memory resource\n");
 		initcode = PPC_ADMA_INIT_MEMRES;
@@ -4489,28 +1138,10 @@ static int __devinit ppc440spe_adma_probe(struct platform_device *ofdev,
 		goto err_regs_alloc;
 	}
 
-	if (adev->id == PPC440SPE_XOR_ID) {
-		adev->xor_reg = regs;
-		/* Reset XOR */
-		iowrite32be(XOR_CRSR_XASR_BIT, &adev->xor_reg->crsr);
-		iowrite32be(XOR_CRSR_64BA_BIT, &adev->xor_reg->crrr);
-	} else {
-		size_t fifo_size = (adev->id == PPC440SPE_DMA0_ID) ?
-				   DMA0_FIFO_SIZE : DMA1_FIFO_SIZE;
-		adev->dma_reg = regs;
-		/* DMAx_FIFO_SIZE is defined in bytes,
-		 * <fsiz> - is defined in number of CDB pointers (8byte).
-		 * DMA FIFO Length = CSlength + CPlength, where
-		 * CSlength = CPlength = (fsiz + 1) * 8.
-		 */
-		iowrite32(DMA_FIFO_ENABLE | ((fifo_size >> 3) - 2),
-			  &adev->dma_reg->fsiz);
-		/* Configure DMA engine */
-		iowrite32(DMA_CFG_DXEPR_HP | DMA_CFG_DFMPP_HP | DMA_CFG_FALGN,
-			  &adev->dma_reg->cfg);
-		/* Clear Status */
-		iowrite32(~0, &adev->dma_reg->dsts);
-	}
+	/*
+	 * reset DMA and config FIFO
+	 */
+	ppc440spe_adma_init_hw(adev, regs);
 
 	adev->dev = &ofdev->dev;
 	adev->common.dev = &ofdev->dev;
@@ -4535,26 +1166,11 @@ static int __devinit ppc440spe_adma_probe(struct platform_device *ofdev,
 	tasklet_init(&chan->irq_tasklet, ppc440spe_adma_tasklet,
 		     (unsigned long)chan);
 
-	/* allocate and map helper pages for async validation or
-	 * async_mult/async_sum_product operations on DMA0/1.
+	/*
+	 * Create helper pages
 	 */
-	if (adev->id != PPC440SPE_XOR_ID) {
-		chan->pdest_page = alloc_page(GFP_KERNEL);
-		chan->qdest_page = alloc_page(GFP_KERNEL);
-		if (!chan->pdest_page ||
-		    !chan->qdest_page) {
-			if (chan->pdest_page)
-				__free_page(chan->pdest_page);
-			if (chan->qdest_page)
-				__free_page(chan->qdest_page);
-			ret = -ENOMEM;
+	if (ppc440spe_create_helper_pages(adev, ofdev, chan))
 			goto err_page_alloc;
-		}
-		chan->pdest = dma_map_page(&ofdev->dev, chan->pdest_page, 0,
-					   PAGE_SIZE, DMA_BIDIRECTIONAL);
-		chan->qdest = dma_map_page(&ofdev->dev, chan->qdest_page, 0,
-					   PAGE_SIZE, DMA_BIDIRECTIONAL);
-	}
 
 	ref = kmalloc(sizeof(*ref), GFP_KERNEL);
 	if (ref) {
@@ -4571,6 +1187,7 @@ static int __devinit ppc440spe_adma_probe(struct platform_device *ofdev,
 	if (ret)
 		goto err_irq;
 
+	ppc440spe_adma_set_capabilities(adev);
 	ppc440spe_adma_init_capabilities(adev);
 
 	ret = dma_async_device_register(&adev->common);
@@ -4592,21 +1209,11 @@ err_irq:
 		}
 	}
 err_ref_alloc:
-	if (adev->id != PPC440SPE_XOR_ID) {
-		dma_unmap_page(&ofdev->dev, chan->pdest,
-			       PAGE_SIZE, DMA_BIDIRECTIONAL);
-		dma_unmap_page(&ofdev->dev, chan->qdest,
-			       PAGE_SIZE, DMA_BIDIRECTIONAL);
-		__free_page(chan->pdest_page);
-		__free_page(chan->qdest_page);
-	}
+	ppc440spe_free_ref(adev, ofdev, chan);
 err_page_alloc:
 	kfree(chan);
 err_chan_alloc:
-	if (adev->id == PPC440SPE_XOR_ID)
-		iounmap(adev->xor_reg);
-	else
-		iounmap(adev->dma_reg);
+	ppc440spe_free_reg(adev);
 err_regs_alloc:
 	dma_free_coherent(adev->dev, adev->pool_size,
 			  adev->dma_desc_pool_virt,
@@ -4622,327 +1229,6 @@ out:
 	return ret;
 }
 
-/**
- * ppc440spe_adma_remove - remove the asynch device
- */
-static int __devexit ppc440spe_adma_remove(struct platform_device *ofdev)
-{
-	struct ppc440spe_adma_device *adev = dev_get_drvdata(&ofdev->dev);
-	struct device_node *np = ofdev->dev.of_node;
-	struct resource res;
-	struct dma_chan *chan, *_chan;
-	struct ppc_dma_chan_ref *ref, *_ref;
-	struct ppc440spe_adma_chan *ppc440spe_chan;
-
-	dev_set_drvdata(&ofdev->dev, NULL);
-	if (adev->id < PPC440SPE_ADMA_ENGINES_NUM)
-		ppc440spe_adma_devices[adev->id] = -1;
-
-	dma_async_device_unregister(&adev->common);
-
-	list_for_each_entry_safe(chan, _chan, &adev->common.channels,
-				 device_node) {
-		ppc440spe_chan = to_ppc440spe_adma_chan(chan);
-		ppc440spe_adma_release_irqs(adev, ppc440spe_chan);
-		tasklet_kill(&ppc440spe_chan->irq_tasklet);
-		if (adev->id != PPC440SPE_XOR_ID) {
-			dma_unmap_page(&ofdev->dev, ppc440spe_chan->pdest,
-					PAGE_SIZE, DMA_BIDIRECTIONAL);
-			dma_unmap_page(&ofdev->dev, ppc440spe_chan->qdest,
-					PAGE_SIZE, DMA_BIDIRECTIONAL);
-			__free_page(ppc440spe_chan->pdest_page);
-			__free_page(ppc440spe_chan->qdest_page);
-		}
-		list_for_each_entry_safe(ref, _ref, &ppc440spe_adma_chan_list,
-					 node) {
-			if (ppc440spe_chan ==
-			    to_ppc440spe_adma_chan(ref->chan)) {
-				list_del(&ref->node);
-				kfree(ref);
-			}
-		}
-		list_del(&chan->device_node);
-		kfree(ppc440spe_chan);
-	}
-
-	dma_free_coherent(adev->dev, adev->pool_size,
-			  adev->dma_desc_pool_virt, adev->dma_desc_pool);
-	if (adev->id == PPC440SPE_XOR_ID)
-		iounmap(adev->xor_reg);
-	else
-		iounmap(adev->dma_reg);
-	of_address_to_resource(np, 0, &res);
-	release_mem_region(res.start, resource_size(&res));
-	kfree(adev);
-	return 0;
-}
-
-/*
- * /sys driver interface to enable h/w RAID-6 capabilities
- * Files created in e.g. /sys/devices/plb.0/400100100.dma0/driver/
- * directory are "devices", "enable" and "poly".
- * "devices" shows available engines.
- * "enable" is used to enable RAID-6 capabilities or to check
- * whether these has been activated.
- * "poly" allows setting/checking used polynomial (for PPC440SPe only).
- */
-
-static ssize_t show_ppc440spe_devices(struct device_driver *dev, char *buf)
-{
-	ssize_t size = 0;
-	int i;
-
-	for (i = 0; i < PPC440SPE_ADMA_ENGINES_NUM; i++) {
-		if (ppc440spe_adma_devices[i] == -1)
-			continue;
-		size += snprintf(buf + size, PAGE_SIZE - size,
-				 "PPC440SP(E)-ADMA.%d: %s\n", i,
-				 ppc_adma_errors[ppc440spe_adma_devices[i]]);
-	}
-	return size;
-}
-
-static ssize_t show_ppc440spe_r6enable(struct device_driver *dev, char *buf)
-{
-	return snprintf(buf, PAGE_SIZE,
-			"PPC440SP(e) RAID-6 capabilities are %sABLED.\n",
-			ppc440spe_r6_enabled ? "EN" : "DIS");
-}
-
-static ssize_t store_ppc440spe_r6enable(struct device_driver *dev,
-					const char *buf, size_t count)
-{
-	unsigned long val;
-
-	if (!count || count > 11)
-		return -EINVAL;
-
-	if (!ppc440spe_r6_tchan)
-		return -EFAULT;
-
-	/* Write a key */
-	sscanf(buf, "%lx", &val);
-	dcr_write(ppc440spe_mq_dcr_host, DCRN_MQ0_XORBA, val);
-	isync();
-
-	/* Verify whether it really works now */
-	if (ppc440spe_test_raid6(ppc440spe_r6_tchan) == 0) {
-		pr_info("PPC440SP(e) RAID-6 has been activated "
-			"successfully\n");
-		ppc440spe_r6_enabled = 1;
-	} else {
-		pr_info("PPC440SP(e) RAID-6 hasn't been activated!"
-			" Error key ?\n");
-		ppc440spe_r6_enabled = 0;
-	}
-	return count;
-}
-
-static ssize_t show_ppc440spe_r6poly(struct device_driver *dev, char *buf)
-{
-	ssize_t size = 0;
-	u32 reg;
-
-#ifdef CONFIG_440SP
-	/* 440SP has fixed polynomial */
-	reg = 0x4d;
-#else
-	reg = dcr_read(ppc440spe_mq_dcr_host, DCRN_MQ0_CFBHL);
-	reg >>= MQ0_CFBHL_POLY;
-	reg &= 0xFF;
-#endif
-
-	size = snprintf(buf, PAGE_SIZE, "PPC440SP(e) RAID-6 driver "
-			"uses 0x1%02x polynomial.\n", reg);
-	return size;
-}
-
-static ssize_t store_ppc440spe_r6poly(struct device_driver *dev,
-				      const char *buf, size_t count)
-{
-	unsigned long reg, val;
-
-#ifdef CONFIG_440SP
-	/* 440SP uses default 0x14D polynomial only */
-	return -EINVAL;
-#endif
-
-	if (!count || count > 6)
-		return -EINVAL;
-
-	/* e.g., 0x14D or 0x11D */
-	sscanf(buf, "%lx", &val);
-
-	if (val & ~0x1FF)
-		return -EINVAL;
-
-	val &= 0xFF;
-	reg = dcr_read(ppc440spe_mq_dcr_host, DCRN_MQ0_CFBHL);
-	reg &= ~(0xFF << MQ0_CFBHL_POLY);
-	reg |= val << MQ0_CFBHL_POLY;
-	dcr_write(ppc440spe_mq_dcr_host, DCRN_MQ0_CFBHL, reg);
-
-	return count;
-}
-
-static DRIVER_ATTR(devices, S_IRUGO, show_ppc440spe_devices, NULL);
-static DRIVER_ATTR(enable, S_IRUGO | S_IWUSR, show_ppc440spe_r6enable,
-		   store_ppc440spe_r6enable);
-static DRIVER_ATTR(poly, S_IRUGO | S_IWUSR, show_ppc440spe_r6poly,
-		   store_ppc440spe_r6poly);
-
-/*
- * Common initialisation for RAID engines; allocate memory for
- * DMAx FIFOs, perform configuration common for all DMA engines.
- * Further DMA engine specific configuration is done at probe time.
- */
-static int ppc440spe_configure_raid_devices(void)
-{
-	struct device_node *np;
-	struct resource i2o_res;
-	struct i2o_regs __iomem *i2o_reg;
-	dcr_host_t i2o_dcr_host;
-	unsigned int dcr_base, dcr_len;
-	int i, ret;
-
-	np = of_find_compatible_node(NULL, NULL, "ibm,i2o-440spe");
-	if (!np) {
-		pr_err("%s: can't find I2O device tree node\n",
-			__func__);
-		return -ENODEV;
-	}
-
-	if (of_address_to_resource(np, 0, &i2o_res)) {
-		of_node_put(np);
-		return -EINVAL;
-	}
-
-	i2o_reg = of_iomap(np, 0);
-	if (!i2o_reg) {
-		pr_err("%s: failed to map I2O registers\n", __func__);
-		of_node_put(np);
-		return -EINVAL;
-	}
-
-	/* Get I2O DCRs base */
-	dcr_base = dcr_resource_start(np, 0);
-	dcr_len = dcr_resource_len(np, 0);
-	if (!dcr_base && !dcr_len) {
-		pr_err("%s: can't get DCR registers base/len!\n",
-			np->full_name);
-		of_node_put(np);
-		iounmap(i2o_reg);
-		return -ENODEV;
-	}
-
-	i2o_dcr_host = dcr_map(np, dcr_base, dcr_len);
-	if (!DCR_MAP_OK(i2o_dcr_host)) {
-		pr_err("%s: failed to map DCRs!\n", np->full_name);
-		of_node_put(np);
-		iounmap(i2o_reg);
-		return -ENODEV;
-	}
-	of_node_put(np);
-
-	/* Provide memory regions for DMA's FIFOs: I2O, DMA0 and DMA1 share
-	 * the base address of FIFO memory space.
-	 * Actually we need twice more physical memory than programmed in the
-	 * <fsiz> register (because there are two FIFOs for each DMA: CP and CS)
-	 */
-	ppc440spe_dma_fifo_buf = kmalloc((DMA0_FIFO_SIZE + DMA1_FIFO_SIZE) << 1,
-					 GFP_KERNEL);
-	if (!ppc440spe_dma_fifo_buf) {
-		pr_err("%s: DMA FIFO buffer allocation failed.\n", __func__);
-		iounmap(i2o_reg);
-		dcr_unmap(i2o_dcr_host, dcr_len);
-		return -ENOMEM;
-	}
-
-	/*
-	 * Configure h/w
-	 */
-	/* Reset I2O/DMA */
-	mtdcri(SDR0, DCRN_SDR0_SRST, DCRN_SDR0_SRST_I2ODMA);
-	mtdcri(SDR0, DCRN_SDR0_SRST, 0);
-
-	/* Setup the base address of mmaped registers */
-	dcr_write(i2o_dcr_host, DCRN_I2O0_IBAH, (u32)(i2o_res.start >> 32));
-	dcr_write(i2o_dcr_host, DCRN_I2O0_IBAL, (u32)(i2o_res.start) |
-						I2O_REG_ENABLE);
-	dcr_unmap(i2o_dcr_host, dcr_len);
-
-	/* Setup FIFO memory space base address */
-	iowrite32(0, &i2o_reg->ifbah);
-	iowrite32(((u32)__pa(ppc440spe_dma_fifo_buf)), &i2o_reg->ifbal);
-
-	/* set zero FIFO size for I2O, so the whole
-	 * ppc440spe_dma_fifo_buf is used by DMAs.
-	 * DMAx_FIFOs will be configured while probe.
-	 */
-	iowrite32(0, &i2o_reg->ifsiz);
-	iounmap(i2o_reg);
-
-	/* To prepare WXOR/RXOR functionality we need access to
-	 * Memory Queue Module DCRs (finally it will be enabled
-	 * via /sys interface of the ppc440spe ADMA driver).
-	 */
-	np = of_find_compatible_node(NULL, NULL, "ibm,mq-440spe");
-	if (!np) {
-		pr_err("%s: can't find MQ device tree node\n",
-			__func__);
-		ret = -ENODEV;
-		goto out_free;
-	}
-
-	/* Get MQ DCRs base */
-	dcr_base = dcr_resource_start(np, 0);
-	dcr_len = dcr_resource_len(np, 0);
-	if (!dcr_base && !dcr_len) {
-		pr_err("%s: can't get DCR registers base/len!\n",
-			np->full_name);
-		ret = -ENODEV;
-		goto out_mq;
-	}
-
-	ppc440spe_mq_dcr_host = dcr_map(np, dcr_base, dcr_len);
-	if (!DCR_MAP_OK(ppc440spe_mq_dcr_host)) {
-		pr_err("%s: failed to map DCRs!\n", np->full_name);
-		ret = -ENODEV;
-		goto out_mq;
-	}
-	of_node_put(np);
-	ppc440spe_mq_dcr_len = dcr_len;
-
-	/* Set HB alias */
-	dcr_write(ppc440spe_mq_dcr_host, DCRN_MQ0_BAUH, DMA_CUED_XOR_HB);
-
-	/* Set:
-	 * - LL transaction passing limit to 1;
-	 * - Memory controller cycle limit to 1;
-	 * - Galois Polynomial to 0x14d (default)
-	 */
-	dcr_write(ppc440spe_mq_dcr_host, DCRN_MQ0_CFBHL,
-		  (1 << MQ0_CFBHL_TPLM) | (1 << MQ0_CFBHL_HBCL) |
-		  (PPC440SPE_DEFAULT_POLY << MQ0_CFBHL_POLY));
-
-	atomic_set(&ppc440spe_adma_err_irq_ref, 0);
-	for (i = 0; i < PPC440SPE_ADMA_ENGINES_NUM; i++)
-		ppc440spe_adma_devices[i] = -1;
-
-	return 0;
-
-out_mq:
-	of_node_put(np);
-out_free:
-	kfree(ppc440spe_dma_fifo_buf);
-	return ret;
-}
-
-static const struct of_device_id ppc440spe_adma_of_match[] __devinitconst = {
-	{ .compatible	= "ibm,dma-440spe", },
-	{ .compatible	= "amcc,xor-accelerator", },
-	{},
-};
 MODULE_DEVICE_TABLE(of, ppc440spe_adma_of_match);
 
 static struct of_platform_driver ppc440spe_adma_driver = {
@@ -4955,68 +1241,15 @@ static struct of_platform_driver ppc440spe_adma_driver = {
 	},
 };
 
-static __init int ppc440spe_adma_init(void)
+__init int ppc440spe_adma_init(void)
 {
-	int ret;
-
-	ret = ppc440spe_configure_raid_devices();
-	if (ret)
-		return ret;
-
-	ret = of_register_platform_driver(&ppc440spe_adma_driver);
-	if (ret) {
-		pr_err("%s: failed to register platform driver\n",
-			__func__);
-		goto out_reg;
-	}
-
-	/* Initialization status */
-	ret = driver_create_file(&ppc440spe_adma_driver.driver,
-				 &driver_attr_devices);
-	if (ret)
-		goto out_dev;
-
-	/* RAID-6 h/w enable entry */
-	ret = driver_create_file(&ppc440spe_adma_driver.driver,
-				 &driver_attr_enable);
-	if (ret)
-		goto out_en;
-
-	/* GF polynomial to use */
-	ret = driver_create_file(&ppc440spe_adma_driver.driver,
-				 &driver_attr_poly);
-	if (!ret)
-		return ret;
+	int ret = 0;
 
-	driver_remove_file(&ppc440spe_adma_driver.driver,
-			   &driver_attr_enable);
-out_en:
-	driver_remove_file(&ppc440spe_adma_driver.driver,
-			   &driver_attr_devices);
-out_dev:
-	/* User will not be able to enable h/w RAID-6 */
-	pr_err("%s: failed to create RAID-6 driver interface\n",
-		__func__);
-	of_unregister_platform_driver(&ppc440spe_adma_driver);
-out_reg:
-	dcr_unmap(ppc440spe_mq_dcr_host, ppc440spe_mq_dcr_len);
-	kfree(ppc440spe_dma_fifo_buf);
+	ret = ppc440spe_adma_hw_init();
+	if(ret)
+		of_unregister_platform_driver(&ppc440spe_adma_driver);
 	return ret;
 }
-
-static void __exit ppc440spe_adma_exit(void)
-{
-	driver_remove_file(&ppc440spe_adma_driver.driver,
-			   &driver_attr_poly);
-	driver_remove_file(&ppc440spe_adma_driver.driver,
-			   &driver_attr_enable);
-	driver_remove_file(&ppc440spe_adma_driver.driver,
-			   &driver_attr_devices);
-	of_unregister_platform_driver(&ppc440spe_adma_driver);
-	dcr_unmap(ppc440spe_mq_dcr_host, ppc440spe_mq_dcr_len);
-	kfree(ppc440spe_dma_fifo_buf);
-}
-
 arch_initcall(ppc440spe_adma_init);
 module_exit(ppc440spe_adma_exit);
 
diff --git a/drivers/dma/ppc4xx/ppc440spe-adma.c b/drivers/dma/ppc4xx/ppc440spe-adma.c
new file mode 100644
index 0000000..da467b4
--- /dev/null
+++ b/drivers/dma/ppc4xx/ppc440spe-adma.c
@@ -0,0 +1,1658 @@
+/*
+ * Copyright (C) 2006-2009 DENX Software Engineering.
+ *
+ * Author: Yuri Tikhonov <yur@emcraft.com>
+ *
+ * Further porting to arch/powerpc by
+ * 	Anatolij Gustschin <agust@denx.de>
+ * 	Tirumala R Marri <tmarri@apm.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc., 59
+ * Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+ *
+ * The full GNU General Public License is included in this distribution in the
+ * file called COPYING.
+ */
+
+/*
+ * This driver supports the asynchrounous DMA copy and RAID engines available
+ * on the AMCC PPC440SPe Processors.
+ * Based on the Intel Xscale(R) family of I/O Processors (IOP 32x, 33x, 134x)
+ * ADMA driver written by D.Williams.
+ */
+
+#include <linux/of.h>
+#include <linux/of_platform.h>
+#include <asm/dcr.h>
+#include <asm/dcr-regs.h>
+#include <linux/async_tx.h>
+#include <linux/dma-mapping.h>
+#include <linux/slab.h>
+#include "adma.h"
+#include "ppc440spe-adma.h"
+
+/* This array is used in data-check operations for storing a pattern */
+static char ppc440spe_qword[16];
+static atomic_t ppc440spe_adma_err_irq_ref;
+static unsigned int ppc440spe_mq_dcr_len;
+
+/* These are used in enable & check routines
+ */
+static u32 ppc440spe_r6_enabled;
+static struct completion ppc440spe_r6_test_comp;
+
+static struct page *ppc440spe_rxor_srcs[32];
+
+static dcr_host_t ppc440spe_mq_dcr_host;
+/* Pointer to DMA0, DMA1 CP/CS FIFO */
+static void *ppc440spe_dma_fifo_buf;
+
+static char *ppc_adma_errors[] = {
+	[PPC_ADMA_INIT_OK] = "ok",
+	[PPC_ADMA_INIT_MEMRES] = "failed to get memory resource",
+	[PPC_ADMA_INIT_MEMREG] = "failed to request memory region",
+	[PPC_ADMA_INIT_ALLOC] = "failed to allocate memory for adev "
+	    "structure",
+	[PPC_ADMA_INIT_COHERENT] = "failed to allocate coherent memory for "
+	    "hardware descriptors",
+	[PPC_ADMA_INIT_CHANNEL] = "failed to allocate memory for channel",
+	[PPC_ADMA_INIT_IRQ1] = "failed to request first irq",
+	[PPC_ADMA_INIT_IRQ2] = "failed to request second irq",
+	[PPC_ADMA_INIT_REGISTER] = "failed to register dma async device",
+};
+
+static void ppc440spe_adma_dma2rxor_set_mult(struct ppc440spe_adma_desc_slot *desc,
+					  int index, u8 mult);
+static void print_cb_list(struct ppc440spe_adma_chan *chan,
+			  struct ppc440spe_adma_desc_slot *iter);
+/**
+ * ppc440spe_can_rxor - check if the operands may be processed with RXOR
+ */
+static int ppc440spe_can_rxor(struct page **srcs, int src_cnt, size_t len)
+{
+	int i, order = 0, state = 0;
+	int idx = 0;
+
+	if (unlikely(!(src_cnt > 1)))
+		return 0;
+
+	BUG_ON(src_cnt > ARRAY_SIZE(ppc440spe_rxor_srcs));
+
+	/* Skip holes in the source list before checking */
+	for (i = 0; i < src_cnt; i++) {
+		if (!srcs[i])
+			continue;
+		ppc440spe_rxor_srcs[idx++] = srcs[i];
+	}
+	src_cnt = idx;
+
+	for (i = 1; i < src_cnt; i++) {
+		char *cur_addr = page_address(ppc440spe_rxor_srcs[i]);
+		char *old_addr = page_address(ppc440spe_rxor_srcs[i - 1]);
+
+		switch (state) {
+		case 0:
+			if (cur_addr == old_addr + len) {
+				/* direct RXOR */
+				order = 1;
+				state = 1;
+			} else if (old_addr == cur_addr + len) {
+				/* reverse RXOR */
+				order = -1;
+				state = 1;
+			} else
+				goto out;
+			break;
+		case 1:
+			if ((i == src_cnt - 2) ||
+			    (order == -1 && cur_addr != old_addr - len)) {
+				order = 0;
+				state = 0;
+			} else if ((cur_addr == old_addr + len * order) ||
+				   (cur_addr == old_addr + 2 * len) ||
+				   (cur_addr == old_addr + 3 * len)) {
+				state = 2;
+			} else {
+				order = 0;
+				state = 0;
+			}
+			break;
+		case 2:
+			order = 0;
+			state = 0;
+			break;
+		}
+	}
+
+      out:
+	if (state == 1 || state == 2)
+		return 1;
+
+	return 0;
+}
+
+/**
+ * ppc440spe_init_rxor_cursor -
+ */
+static void ppc440spe_init_rxor_cursor(struct ppc440spe_rxor *cursor)
+{
+	memset(cursor, 0, sizeof(struct ppc440spe_rxor));
+	cursor->state = 2;
+}
+
+/**
+ * ppc440spe_adma_init_dma2rxor_slot -
+ */
+static  void ppc440spe_adma_init_dma2rxor_slot(struct ppc440spe_adma_desc_slot
+						  *desc, dma_addr_t * src,
+						  int src_cnt)
+{
+	int i;
+
+	/* initialize CDB */
+	for (i = 0; i < src_cnt; i++) {
+		ppc440spe_adma_dma2rxor_prep_src(desc, &desc->rxor_cursor, i,
+					      desc->src_cnt, (u32) src[i]);
+	}
+}
+
+/******************************************************************************
+ * Command (Descriptor) Blocks low-level routines
+ ******************************************************************************/
+/**
+ * ppc440spe_desc_set_rxor_block_size - set RXOR block size
+ */
+static   void ppc440spe_desc_set_rxor_block_size(u32 byte_count)
+{
+	/* assume that byte_count is aligned on the 512-boundary;
+	 * thus write it directly to the register (bits 23:31 are
+	 * reserved there).
+	 */
+	dcr_write(ppc440spe_mq_dcr_host, DCRN_MQ0_CF2H, byte_count);
+}
+
+
+static int ppc440spe_adma_estimate(struct dma_chan *chan,
+				enum dma_transaction_type cap,
+				struct page **dst_lst, int dst_cnt,
+				struct page **src_lst, int src_cnt,
+				size_t src_sz)
+{
+	int ef = 1;
+
+	if (cap == DMA_PQ || cap == DMA_PQ_VAL) {
+		/* If RAID-6 capabilities were not activated don't try
+		 * to use them
+		 */
+		if (unlikely(!ppc440spe_r6_enabled))
+			return -1;
+	}
+	/*  In the current implementation of ppc440spe ADMA driver it
+
+
+
+	 * makes sense to pick out only pq case, because it may be
+	 * processed:
+	 * (1) either using Biskup method on DMA2;
+	 * (2) or on DMA0/1.
+	 *  Thus we give a favour to (1) if the sources are suitable;
+	 * else let it be processed on one of the DMA0/1 engines.
+	 *  In the sum_product case where destination is also the
+	 * source process it on DMA0/1 only.
+	 */
+	if (cap == DMA_PQ && chan->chan_id == PPC440SPE_XOR_ID) {
+
+		if (dst_cnt == 1 && src_cnt == 2 && dst_lst[0] == src_lst[1])
+			ef = 0;	/* sum_product case, process on DMA0/1 */
+		else if (ppc440spe_can_rxor(src_lst, src_cnt, src_sz))
+			ef = 3;	/* override (DMA0/1 + idle) */
+		else
+			ef = 0;	/* can't process on DMA2 if !rxor */
+	}
+
+	/* channel idleness increases the priority */
+	if (likely(ef) && !ppc440spe_chan_is_busy(to_ppc440spe_adma_chan(chan)))
+		ef++;
+
+	return ef;
+}
+
+struct dma_chan *ppc440spe_async_tx_find_best_channel(enum dma_transaction_type cap,
+						      struct page **dst_lst,
+						      int dst_cnt,
+						      struct page **src_lst,
+						      int src_cnt,
+						      size_t src_sz)
+{
+	struct dma_chan *best_chan = NULL;
+	struct ppc_dma_chan_ref *ref;
+	int best_rank = -1;
+
+	if (unlikely(!src_sz))
+		return NULL;
+	if (src_sz > PAGE_SIZE) {
+		/*
+		 * should a user of the api ever pass > PAGE_SIZE requests
+		 * we sort out cases where temporary page-sized buffers
+		 * are used.
+		 */
+		switch (cap) {
+		case DMA_PQ:
+			if (src_cnt == 1 && dst_lst[1] == src_lst[0])
+				return NULL;
+			if (src_cnt == 2 && dst_lst[1] == src_lst[1])
+				return NULL;
+			break;
+		case DMA_PQ_VAL:
+		case DMA_XOR_VAL:
+			return NULL;
+		default:
+			break;
+		}
+	}
+
+	list_for_each_entry(ref, &ppc440spe_adma_chan_list, node) {
+		if (dma_has_cap(cap, ref->chan->device->cap_mask)) {
+			int rank;
+
+			rank = ppc440spe_adma_estimate(ref->chan, cap, dst_lst,
+						    dst_cnt, src_lst,
+						    src_cnt, src_sz);
+			if (rank > best_rank) {
+				best_rank = rank;
+				best_chan = ref->chan;
+			}
+		}
+	}
+
+	return best_chan;
+}
+
+EXPORT_SYMBOL_GPL(ppc440spe_async_tx_find_best_channel);
+
+/**
+ * ppc440spe_dma01_prep_sum_product -
+ * Dx = A*(P+Pxy) + B*(Q+Qxy) operation where destination is also
+ * the source.
+ */
+static  struct ppc440spe_adma_desc_slot
+*ppc440spe_dma01_prep_sum_product(struct ppc440spe_adma_chan*ppc440spe_chan,
+				dma_addr_t * dst,
+				dma_addr_t * src,
+				int src_cnt,
+				const unsigned char *scf,
+				size_t len,
+				unsigned long flags)
+{
+	struct ppc440spe_adma_desc_slot *sw_desc = NULL;
+	unsigned long op = 0;
+	int slot_cnt;
+
+	set_bit(PPC440SPE_DESC_WXOR, &op);
+	slot_cnt = 3;
+
+	spin_lock_bh(&ppc440spe_chan->lock);
+
+	/* WXOR, each descriptor occupies one slot */
+	sw_desc = ppc440spe_adma_alloc_slots(ppc440spe_chan, slot_cnt, 1);
+	if (sw_desc) {
+		struct ppc440spe_adma_chan *chan;
+		struct ppc440spe_adma_desc_slot *iter;
+		struct dma_cdb *hw_desc;
+
+		chan = to_ppc440spe_adma_chan(sw_desc->async_tx.chan);
+		set_bits(op, &sw_desc->flags);
+		sw_desc->src_cnt = src_cnt;
+		sw_desc->dst_cnt = 1;
+		/* 1st descriptor, src[1] data to q page and zero destination */
+		iter = list_first_entry(&sw_desc->group_list,
+					struct ppc440spe_adma_desc_slot,
+					chain_node);
+		memset(iter->hw_desc, 0, sizeof(struct dma_cdb));
+		iter->hw_next = list_entry(iter->chain_node.next,
+					   struct ppc440spe_adma_desc_slot,
+					   chain_node);
+		clear_bit(PPC440SPE_DESC_INT, &iter->flags);
+		hw_desc = iter->hw_desc;
+		hw_desc->opc = DMA_CDB_OPC_MULTICAST;
+
+		ppc440spe_desc_set_dest_addr(iter, chan, DMA_CUED_XOR_BASE,
+					  *dst, 0);
+		ppc440spe_desc_set_dest_addr(iter, chan, 0, ppc440spe_chan->qdest, 1);
+		ppc440spe_desc_set_src_addr(iter, chan, 0, DMA_CUED_XOR_HB,
+					 src[1]);
+		ppc440spe_desc_set_byte_count(iter, ppc440spe_chan, len);
+		iter->unmap_len = len;
+
+		/* 2nd descriptor, multiply src[1] data and store the
+		 * result in destination */
+		iter = list_first_entry(&iter->chain_node,
+					struct ppc440spe_adma_desc_slot,
+					chain_node);
+		memset(iter->hw_desc, 0, sizeof(struct dma_cdb));
+		/* set 'next' pointer */
+		iter->hw_next = list_entry(iter->chain_node.next,
+					   struct ppc440spe_adma_desc_slot,
+					   chain_node);
+		if (flags & DMA_PREP_INTERRUPT)
+			set_bit(PPC440SPE_DESC_INT, &iter->flags);
+		else
+			clear_bit(PPC440SPE_DESC_INT, &iter->flags);
+
+		hw_desc = iter->hw_desc;
+		hw_desc->opc = DMA_CDB_OPC_MV_SG1_SG2;
+		ppc440spe_desc_set_src_addr(iter, chan, 0, DMA_CUED_XOR_HB,
+					 ppc440spe_chan->qdest);
+		ppc440spe_desc_set_dest_addr(iter, chan, DMA_CUED_XOR_BASE,
+					  *dst, 0);
+		ppc440spe_desc_set_src_mult(iter, chan, DMA_CUED_MULT1_OFF,
+					 DMA_CDB_SG_DST1, scf[1]);
+		ppc440spe_desc_set_byte_count(iter, ppc440spe_chan, len);
+		iter->unmap_len = len;
+
+		/*
+		 * 3rd descriptor, multiply src[0] data and xor it
+		 * with destination
+		 */
+		iter = list_first_entry(&iter->chain_node,
+					struct ppc440spe_adma_desc_slot,
+					chain_node);
+		memset(iter->hw_desc, 0, sizeof(struct dma_cdb));
+		iter->hw_next = NULL;
+		if (flags & DMA_PREP_INTERRUPT)
+			set_bit(PPC440SPE_DESC_INT, &iter->flags);
+		else
+			clear_bit(PPC440SPE_DESC_INT, &iter->flags);
+
+		hw_desc = iter->hw_desc;
+		hw_desc->opc = DMA_CDB_OPC_MV_SG1_SG2;
+		ppc440spe_desc_set_src_addr(iter, chan, 0, DMA_CUED_XOR_HB,
+					 src[0]);
+		ppc440spe_desc_set_dest_addr(iter, chan, DMA_CUED_XOR_BASE,
+					  *dst, 0);
+		ppc440spe_desc_set_src_mult(iter, chan, DMA_CUED_MULT1_OFF,
+					 DMA_CDB_SG_DST1, scf[0]);
+		ppc440spe_desc_set_byte_count(iter, ppc440spe_chan, len);
+		iter->unmap_len = len;
+		sw_desc->async_tx.flags = flags;
+	}
+
+	spin_unlock_bh(&ppc440spe_chan->lock);
+
+	return sw_desc;
+}
+
+static
+struct ppc440spe_adma_desc_slot *ppc440spe_dma01_prep_pq(struct ppc440spe_adma_chan *ppc440spe_chan,
+							dma_addr_t *dst,
+							int dst_cnt,
+							dma_addr_t *src,
+							int src_cnt,
+							const unsigned char *scf,
+							size_t len,
+							unsigned long flags)
+{
+	int slot_cnt;
+	struct ppc440spe_adma_desc_slot *sw_desc = NULL, *iter;
+	unsigned long op = 0;
+	unsigned char mult = 1;
+
+	pr_debug("%s: dst_cnt %d, src_cnt %d, len %d\n",
+		 __func__, dst_cnt, src_cnt, len);
+	/*  select operations WXOR/RXOR depending on the
+	 * source addresses of operators and the number
+	 * of destinations (RXOR support only Q-parity calculations)
+	 */
+	set_bit(PPC440SPE_DESC_WXOR, &op);
+	if (!test_and_set_bit(PPC440SPE_RXOR_RUN, &ppc440spe_rxor_state)) {
+		/* no active RXOR;
+		 * do RXOR if:
+		 * - there are more than 1 source,
+		 * - len is aligned on 512-byte boundary,
+		 * - source addresses fit to one of 4 possible regions.
+		 */
+		if (src_cnt > 1 &&
+		    !(len & MQ0_CF2H_RXOR_BS_MASK) &&
+		    (src[0] + len) == src[1]) {
+			/* may do RXOR R1 R2 */
+			set_bit(PPC440SPE_DESC_RXOR, &op);
+			if (src_cnt != 2) {
+				/* may try to enhance region of RXOR */
+				if ((src[1] + len) == src[2]) {
+					/* do RXOR R1 R2 R3 */
+					set_bit(PPC440SPE_DESC_RXOR123, &op);
+				} else if ((src[1] + len * 2) == src[2]) {
+					/* do RXOR R1 R2 R4 */
+					set_bit(PPC440SPE_DESC_RXOR124, &op);
+				} else if ((src[1] + len * 3) == src[2]) {
+					/* do RXOR R1 R2 R5 */
+					set_bit(PPC440SPE_DESC_RXOR125, &op);
+				} else {
+					/* do RXOR R1 R2 */
+					set_bit(PPC440SPE_DESC_RXOR12, &op);
+				}
+			} else {
+				/* do RXOR R1 R2 */
+				set_bit(PPC440SPE_DESC_RXOR12, &op);
+			}
+		}
+
+		if (!test_bit(PPC440SPE_DESC_RXOR, &op)) {
+			/* can not do this operation with RXOR */
+			clear_bit(PPC440SPE_RXOR_RUN, &ppc440spe_rxor_state);
+		} else {
+			/* can do; set block size right now */
+			ppc440spe_desc_set_rxor_block_size(len);
+		}
+	}
+
+	/* Number of necessary slots depends on operation type selected */
+	if (!test_bit(PPC440SPE_DESC_RXOR, &op)) {
+		/*  This is a WXOR only chain. Need descriptors for each
+		 * source to GF-XOR them with WXOR, and need descriptors
+		 * for each destination to zero them with WXOR
+		 */
+		slot_cnt = src_cnt;
+
+		if (flags & DMA_PREP_ZERO_P) {
+			slot_cnt++;
+			set_bit(PPC440SPE_ZERO_P, &op);
+		}
+		if (flags & DMA_PREP_ZERO_Q) {
+			slot_cnt++;
+			set_bit(PPC440SPE_ZERO_Q, &op);
+		}
+	} else {
+		/*  Need 1/2 descriptor for RXOR operation, and
+		 * need (src_cnt - (2 or 3)) for WXOR of sources
+		 * remained (if any)
+		 */
+		slot_cnt = dst_cnt;
+
+		if (flags & DMA_PREP_ZERO_P)
+			set_bit(PPC440SPE_ZERO_P, &op);
+		if (flags & DMA_PREP_ZERO_Q)
+			set_bit(PPC440SPE_ZERO_Q, &op);
+
+		if (test_bit(PPC440SPE_DESC_RXOR12, &op))
+			slot_cnt += src_cnt - 2;
+		else
+			slot_cnt += src_cnt - 3;
+
+		/*  Thus we have either RXOR only chain or
+		 * mixed RXOR/WXOR
+		 */
+		if (slot_cnt == dst_cnt)
+			/* RXOR only chain */
+			clear_bit(PPC440SPE_DESC_WXOR, &op);
+	}
+
+	spin_lock_bh(&ppc440spe_chan->lock);
+	/* for both RXOR/WXOR each descriptor occupies one slot */
+	sw_desc = ppc440spe_adma_alloc_slots(ppc440spe_chan, slot_cnt, 1);
+	if (sw_desc) {
+		ppc440spe_desc_init_dma01pq(sw_desc, dst_cnt, src_cnt, flags, op);
+
+		/* setup dst/src/mult */
+		pr_debug("%s: set dst descriptor 0, 1: 0x%016llx, 0x%016llx\n",
+			 __func__, dst[0], dst[1]);
+		ppc440spe_adma_pq_set_dest(sw_desc, dst, flags);
+		while (src_cnt--) {
+			ppc440spe_adma_pq_set_src(sw_desc, src[src_cnt], src_cnt);
+
+			/* NOTE: "Multi = 0 is equivalent to = 1" as it
+			 * stated in 440SPSPe_RAID6_Addendum_UM_1_17.pdf
+			 * doesn't work for RXOR with DMA0/1! Instead, multi=0
+			 * leads to zeroing source data after RXOR.
+			 * So, for P case set-up mult=1 explicitly.
+			 */
+			if (!(flags & DMA_PREP_PQ_DISABLE_Q))
+				mult = scf[src_cnt];
+			ppc440spe_adma_pq_set_src_mult(sw_desc,
+						    mult, src_cnt, dst_cnt - 1);
+		}
+
+		/* Setup byte count foreach slot just allocated */
+		sw_desc->async_tx.flags = flags;
+		list_for_each_entry(iter, &sw_desc->group_list, chain_node) {
+			ppc440spe_desc_set_byte_count(iter, ppc440spe_chan, len);
+			iter->unmap_len = len;
+		}
+	}
+	spin_unlock_bh(&ppc440spe_chan->lock);
+
+	return sw_desc;
+}
+
+/**
+ * ppc440spe_adma_prep_dma_pqzero_sum - prepare CDB group for
+ * a PQ_ZERO_SUM operation
+ */
+struct dma_async_tx_descriptor
+*ppc440spe_adma_prep_dma_pqzero_sum(struct dma_chan *chan,
+					dma_addr_t * pq,
+					dma_addr_t * src,
+					unsigned int src_cnt,
+					const unsigned 	char *scf,
+					size_t len,
+					enum sum_check_flags *pqres,
+					unsigned long flags)
+{
+	struct ppc440spe_adma_chan *ppc440spe_chan;
+	struct ppc440spe_adma_desc_slot *sw_desc, *iter;
+	dma_addr_t pdest, qdest;
+	int slot_cnt, slots_per_op, idst, dst_cnt;
+
+	ppc440spe_chan = to_ppc440spe_adma_chan(chan);
+
+	if (flags & DMA_PREP_PQ_DISABLE_P)
+		pdest = 0;
+	else
+		pdest = pq[0];
+
+	if (flags & DMA_PREP_PQ_DISABLE_Q)
+		qdest = 0;
+	else
+		qdest = pq[1];
+
+	ADMA_LL_DBG(prep_dma_pqzero_sum_dbg(ppc440spe_chan->device->id,
+					    src, src_cnt, scf));
+
+	/* Always use WXOR for P/Q calculations (two destinations).
+	 * Need 1 or 2 extra slots to verify results are zero.
+	 */
+	idst = dst_cnt = (pdest && qdest) ? 2 : 1;
+
+	/* One additional slot per destination to clone P/Q
+	 * before calculation (we have to preserve destinations).
+	 */
+	slot_cnt = src_cnt + dst_cnt * 2;
+	slots_per_op = 1;
+
+	spin_lock_bh(&ppc440spe_chan->lock);
+	sw_desc = ppc440spe_adma_alloc_slots(ppc440spe_chan, slot_cnt, slots_per_op);
+	if (sw_desc) {
+		ppc440spe_desc_init_dma01pqzero_sum(sw_desc, dst_cnt, src_cnt);
+
+		/* Setup byte count for each slot just allocated */
+		sw_desc->async_tx.flags = flags;
+		list_for_each_entry(iter, &sw_desc->group_list, chain_node) {
+			ppc440spe_desc_set_byte_count(iter, ppc440spe_chan, len);
+			iter->unmap_len = len;
+		}
+
+		if (pdest) {
+			struct dma_cdb *hw_desc;
+			struct ppc440spe_adma_chan *chan;
+
+			iter = sw_desc->group_head;
+			chan = to_ppc440spe_adma_chan(iter->async_tx.chan);
+			memset(iter->hw_desc, 0, sizeof(struct dma_cdb));
+			iter->hw_next = list_entry(iter->chain_node.next,
+						   struct ppc440spe_adma_desc_slot,
+						   chain_node);
+			hw_desc = iter->hw_desc;
+			hw_desc->opc = DMA_CDB_OPC_MV_SG1_SG2;
+			iter->src_cnt = 0;
+			iter->dst_cnt = 0;
+			ppc440spe_desc_set_dest_addr(iter, chan, 0,
+						  ppc440spe_chan->pdest, 0);
+			ppc440spe_desc_set_src_addr(iter, chan, 0, 0, pdest);
+			ppc440spe_desc_set_byte_count(iter, ppc440spe_chan, len);
+			iter->unmap_len = 0;
+			/* override pdest to preserve original P */
+			pdest = ppc440spe_chan->pdest;
+		}
+		if (qdest) {
+			struct dma_cdb *hw_desc;
+			struct ppc440spe_adma_chan *chan;
+
+			iter = list_first_entry(&sw_desc->group_list,
+						struct ppc440spe_adma_desc_slot,
+						chain_node);
+			chan = to_ppc440spe_adma_chan(iter->async_tx.chan);
+
+			if (pdest) {
+				iter = list_entry(iter->chain_node.next,
+						  struct ppc440spe_adma_desc_slot,
+						  chain_node);
+			}
+
+			memset(iter->hw_desc, 0, sizeof(struct dma_cdb));
+			iter->hw_next = list_entry(iter->chain_node.next,
+						   struct ppc440spe_adma_desc_slot,
+						   chain_node);
+			hw_desc = iter->hw_desc;
+			hw_desc->opc = DMA_CDB_OPC_MV_SG1_SG2;
+			iter->src_cnt = 0;
+			iter->dst_cnt = 0;
+			ppc440spe_desc_set_dest_addr(iter, chan, 0,
+						  ppc440spe_chan->qdest, 0);
+			ppc440spe_desc_set_src_addr(iter, chan, 0, 0, qdest);
+			ppc440spe_desc_set_byte_count(iter, ppc440spe_chan, len);
+			iter->unmap_len = 0;
+			/* override qdest to preserve original Q */
+			qdest = ppc440spe_chan->qdest;
+		}
+
+		/* Setup destinations for P/Q ops */
+		ppc440spe_adma_pqzero_sum_set_dest(sw_desc, pdest, qdest);
+
+		/* Setup zero QWORDs into DCHECK CDBs */
+		idst = dst_cnt;
+		list_for_each_entry_reverse(iter, &sw_desc->group_list,
+					    chain_node) {
+			/*
+			 * The last CDB corresponds to Q-parity check,
+			 * the one before last CDB corresponds
+			 * P-parity check
+			 */
+			if (idst == DMA_DEST_MAX_NUM) {
+				if (idst == dst_cnt) {
+					set_bit(PPC440SPE_DESC_QCHECK,
+						&iter->flags);
+				} else {
+					set_bit(PPC440SPE_DESC_PCHECK,
+						&iter->flags);
+				}
+			} else {
+				if (qdest) {
+					set_bit(PPC440SPE_DESC_QCHECK,
+						&iter->flags);
+				} else {
+					set_bit(PPC440SPE_DESC_PCHECK,
+						&iter->flags);
+				}
+			}
+			iter->xor_check_result = pqres;
+
+			/*
+			 * set it to zero, if check fail then result will
+			 * be updated
+			 */
+			*iter->xor_check_result = 0;
+			ppc440spe_desc_set_dcheck(iter, ppc440spe_chan, ppc440spe_qword);
+
+			if (!(--dst_cnt))
+				break;
+		}
+
+		/* Setup sources and mults for P/Q ops */
+		list_for_each_entry_continue_reverse(iter, &sw_desc->group_list,
+						     chain_node) {
+			struct ppc440spe_adma_chan *chan;
+			u32 mult_dst;
+
+			chan = to_ppc440spe_adma_chan(iter->async_tx.chan);
+			ppc440spe_desc_set_src_addr(iter, chan, 0,
+						 DMA_CUED_XOR_HB,
+						 src[src_cnt - 1]);
+			if (qdest) {
+				mult_dst = (dst_cnt - 1) ? DMA_CDB_SG_DST2 :
+				    DMA_CDB_SG_DST1;
+				ppc440spe_desc_set_src_mult(iter, chan,
+							 DMA_CUED_MULT1_OFF,
+							 mult_dst,
+							 scf[src_cnt - 1]);
+			}
+			if (!(--src_cnt))
+				break;
+		}
+	}
+	spin_unlock_bh(&ppc440spe_chan->lock);
+	return sw_desc ? &sw_desc->async_tx : NULL;
+}
+
+/**
+ * ppc440spe_adma_prep_dma_xor_zero_sum - prepare CDB group for
+ * XOR ZERO_SUM operation
+ */
+struct dma_async_tx_descriptor
+*ppc440spe_adma_prep_dma_xor_zero_sum(struct dma_chan *chan,
+					dma_addr_t * src,
+					unsigned int src_cnt,
+					size_t len,
+					enum sum_check_flags *result,
+					unsigned long flags)
+{
+	struct dma_async_tx_descriptor *tx;
+	dma_addr_t pq[2];
+
+	/* validate P, disable Q */
+	pq[0] = src[0];
+	pq[1] = 0;
+	flags |= DMA_PREP_PQ_DISABLE_Q;
+
+	tx = ppc440spe_adma_prep_dma_pqzero_sum(chan, pq, &src[1],
+					     src_cnt - 1, 0, len,
+					     result, flags);
+	return tx;
+}
+
+void ppc440spe_adma_set_capabilities(struct ppc440spe_adma_device *adev)
+{
+	switch (adev->id) {
+	case PPC440SPE_DMA0_ID:
+	case PPC440SPE_DMA1_ID:
+		dma_cap_set(DMA_MEMCPY, adev->common.cap_mask);
+		dma_cap_set(DMA_INTERRUPT, adev->common.cap_mask);
+		dma_cap_set(DMA_MEMSET, adev->common.cap_mask);
+		dma_cap_set(DMA_PQ, adev->common.cap_mask);
+		dma_cap_set(DMA_PQ_VAL, adev->common.cap_mask);
+		dma_cap_set(DMA_XOR_VAL, adev->common.cap_mask);
+		break;
+	case PPC440SPE_XOR_ID:
+		dma_cap_set(DMA_XOR, adev->common.cap_mask);
+		dma_cap_set(DMA_PQ, adev->common.cap_mask);
+		dma_cap_set(DMA_INTERRUPT, adev->common.cap_mask);
+		adev->common.cap_mask = adev->common.cap_mask;
+		break;
+	}
+
+	if (dma_has_cap(DMA_PQ, adev->common.cap_mask)) {
+		switch (adev->id) {
+		case PPC440SPE_DMA0_ID:
+			dma_set_maxpq(&adev->common,
+				      DMA0_FIFO_SIZE / sizeof(struct dma_cdb),
+				      0);
+			break;
+		case PPC440SPE_DMA1_ID:
+			dma_set_maxpq(&adev->common,
+				      DMA1_FIFO_SIZE / sizeof(struct dma_cdb),
+				      0);
+			break;
+		case PPC440SPE_XOR_ID:
+			adev->common.max_pq = XOR_MAX_OPS * 3;
+			break;
+		}
+	}
+	if (dma_has_cap(DMA_PQ_VAL, adev->common.cap_mask)) {
+		switch (adev->id) {
+		case PPC440SPE_DMA0_ID:
+			adev->common.max_pq = DMA0_FIFO_SIZE /
+			    sizeof(struct dma_cdb);
+			break;
+		case PPC440SPE_DMA1_ID:
+			adev->common.max_pq = DMA1_FIFO_SIZE /
+			    sizeof(struct dma_cdb);
+			break;
+		}
+	}
+	if (dma_has_cap(DMA_XOR_VAL, adev->common.cap_mask)) {
+		switch (adev->id) {
+		case PPC440SPE_DMA0_ID:
+			adev->common.max_xor = DMA0_FIFO_SIZE /
+			    sizeof(struct dma_cdb);
+			break;
+		case PPC440SPE_DMA1_ID:
+			adev->common.max_xor = DMA1_FIFO_SIZE /
+			    sizeof(struct dma_cdb);
+			break;
+		}
+	}
+	pr_info("%s: AMCC(R) PPC440SP(E) ADMA Engine: "
+		"( %s%s%s%s%s%s%s)\n",
+		dev_name(adev->dev),
+		dma_has_cap(DMA_PQ, adev->common.cap_mask) ? "pq " : "",
+		dma_has_cap(DMA_PQ_VAL, adev->common.cap_mask) ? "pq_val " : "",
+		dma_has_cap(DMA_XOR, adev->common.cap_mask) ? "xor " : "",
+		dma_has_cap(DMA_XOR_VAL,
+			    adev->common.cap_mask) ? "xor_val " : "",
+		dma_has_cap(DMA_MEMCPY, adev->common.cap_mask) ? "memcpy " : "",
+		dma_has_cap(DMA_MEMSET, adev->common.cap_mask) ? "memset " : "",
+		dma_has_cap(DMA_INTERRUPT,
+			    adev->common.cap_mask) ? "intr " : "");
+}
+static  struct ppc440spe_adma_desc_slot
+*ppc440spe_dma2_prep_pq(struct ppc440spe_adma_chan *ppc440spe_chan,
+			dma_addr_t * dst,
+			int dst_cnt,
+			dma_addr_t * src,
+			int src_cnt,
+			const unsigned char *scf,
+			size_t len,
+			unsigned long flags)
+{
+	int slot_cnt, descs_per_op;
+	struct ppc440spe_adma_desc_slot *sw_desc = NULL, *iter;
+	unsigned long op = 0;
+	unsigned char mult = 1;
+
+	BUG_ON(!dst_cnt);
+	/*pr_debug("%s: dst_cnt %d, src_cnt %d, len %d\n",
+	   __func__, dst_cnt, src_cnt, len); */
+
+	spin_lock_bh(&ppc440spe_chan->lock);
+	descs_per_op = ppc440spe_dma2_pq_slot_count(src, src_cnt, len);
+	if (descs_per_op < 0) {
+		spin_unlock_bh(&ppc440spe_chan->lock);
+		return NULL;
+	}
+
+	/* depending on number of sources we have 1 or 2 RXOR chains */
+	slot_cnt = descs_per_op * dst_cnt;
+
+	sw_desc = ppc440spe_adma_alloc_slots(ppc440spe_chan, slot_cnt, 1);
+	if (sw_desc) {
+		op = slot_cnt;
+		sw_desc->async_tx.flags = flags;
+		list_for_each_entry(iter, &sw_desc->group_list, chain_node) {
+			ppc440spe_desc_init_dma2pq(iter, dst_cnt, src_cnt,
+						--op ? 0 : flags);
+			ppc440spe_desc_set_byte_count(iter, ppc440spe_chan, len);
+			iter->unmap_len = len;
+
+			ppc440spe_init_rxor_cursor(&(iter->rxor_cursor));
+			iter->rxor_cursor.len = len;
+			iter->descs_per_op = descs_per_op;
+		}
+		op = 0;
+		list_for_each_entry(iter, &sw_desc->group_list, chain_node) {
+			op++;
+			if (op % descs_per_op == 0)
+				ppc440spe_adma_init_dma2rxor_slot(iter, src,
+							       src_cnt);
+			if (likely(!list_is_last(&iter->chain_node,
+						 &sw_desc->group_list))) {
+				/* set 'next' pointer */
+				iter->hw_next =
+				    list_entry(iter->chain_node.next,
+					       struct ppc440spe_adma_desc_slot,
+					       chain_node);
+				ppc440spe_xor_set_link(iter, iter->hw_next);
+			} else {
+				/* this is the last descriptor. */
+				iter->hw_next = NULL;
+			}
+		}
+
+		/* fixup head descriptor */
+		sw_desc->dst_cnt = dst_cnt;
+		if (flags & DMA_PREP_ZERO_P)
+			set_bit(PPC440SPE_ZERO_P, &sw_desc->flags);
+		if (flags & DMA_PREP_ZERO_Q)
+			set_bit(PPC440SPE_ZERO_Q, &sw_desc->flags);
+
+		/* setup dst/src/mult */
+		ppc440spe_adma_pq_set_dest(sw_desc, dst, flags);
+
+		while (src_cnt--) {
+			/* handle descriptors (if dst_cnt == 2) inside
+			 * the ppc440spe_adma_pq_set_srcxxx() functions
+			 */
+			ppc440spe_adma_pq_set_src(sw_desc, src[src_cnt], src_cnt);
+			if (!(flags & DMA_PREP_PQ_DISABLE_Q))
+				mult = scf[src_cnt];
+			ppc440spe_adma_pq_set_src_mult(sw_desc,
+						    mult, src_cnt, dst_cnt - 1);
+		}
+	}
+	spin_unlock_bh(&ppc440spe_chan->lock);
+	ppc440spe_desc_set_rxor_block_size(len);
+	return sw_desc;
+}
+
+/**
+ * ppc440spe_dma01_prep_mult -
+ * for Q operation where destination is also the source
+ */
+static  struct ppc440spe_adma_desc_slot
+*ppc440spe_dma01_prep_mult(struct ppc440spe_adma_chan *ppc440spe_chan,
+			dma_addr_t * dst,
+			int dst_cnt,
+			dma_addr_t * src,
+			int src_cnt,
+			const unsigned char *scf,
+			size_t len,
+			unsigned long flags)
+{
+	struct ppc440spe_adma_desc_slot *sw_desc = NULL;
+	unsigned long op = 0;
+	int slot_cnt;
+
+	set_bit(PPC440SPE_DESC_WXOR, &op);
+	slot_cnt = 2;
+
+	spin_lock_bh(&ppc440spe_chan->lock);
+
+	/* use WXOR, each descriptor occupies one slot */
+	sw_desc = ppc440spe_adma_alloc_slots(ppc440spe_chan, slot_cnt, 1);
+	if (sw_desc) {
+		struct ppc440spe_adma_chan *chan;
+		struct ppc440spe_adma_desc_slot *iter;
+		struct dma_cdb *hw_desc;
+
+		chan = to_ppc440spe_adma_chan(sw_desc->async_tx.chan);
+		set_bits(op, &sw_desc->flags);
+		sw_desc->src_cnt = src_cnt;
+		sw_desc->dst_cnt = dst_cnt;
+		/* First descriptor, zero data in the destination and copy it
+		 * to q page using MULTICAST transfer.
+		 */
+		iter = list_first_entry(&sw_desc->group_list,
+					struct ppc440spe_adma_desc_slot,
+					chain_node);
+		memset(iter->hw_desc, 0, sizeof(struct dma_cdb));
+		/* set 'next' pointer */
+		iter->hw_next = list_entry(iter->chain_node.next,
+					   struct ppc440spe_adma_desc_slot,
+					   chain_node);
+		clear_bit(PPC440SPE_DESC_INT, &iter->flags);
+		hw_desc = iter->hw_desc;
+		hw_desc->opc = DMA_CDB_OPC_MULTICAST;
+
+		ppc440spe_desc_set_dest_addr(iter, chan,
+					  DMA_CUED_XOR_BASE, dst[0], 0);
+		ppc440spe_desc_set_dest_addr(iter, chan, 0, dst[1], 1);
+		ppc440spe_desc_set_src_addr(iter, chan, 0, DMA_CUED_XOR_HB,
+					 src[0]);
+		ppc440spe_desc_set_byte_count(iter, ppc440spe_chan, len);
+		iter->unmap_len = len;
+
+		/*
+		 * Second descriptor, multiply data from the q page
+		 * and store the result in real destination.
+		 */
+		iter = list_first_entry(&iter->chain_node,
+					struct ppc440spe_adma_desc_slot,
+					chain_node);
+		memset(iter->hw_desc, 0, sizeof(struct dma_cdb));
+		iter->hw_next = NULL;
+		if (flags & DMA_PREP_INTERRUPT)
+			set_bit(PPC440SPE_DESC_INT, &iter->flags);
+		else
+			clear_bit(PPC440SPE_DESC_INT, &iter->flags);
+
+		hw_desc = iter->hw_desc;
+		hw_desc->opc = DMA_CDB_OPC_MV_SG1_SG2;
+		ppc440spe_desc_set_src_addr(iter, chan, 0,
+					 DMA_CUED_XOR_HB, dst[1]);
+		ppc440spe_desc_set_dest_addr(iter, chan,
+					  DMA_CUED_XOR_BASE, dst[0], 0);
+
+		ppc440spe_desc_set_src_mult(iter, chan, DMA_CUED_MULT1_OFF,
+					 DMA_CDB_SG_DST1, scf[0]);
+		ppc440spe_desc_set_byte_count(iter, ppc440spe_chan, len);
+		iter->unmap_len = len;
+		sw_desc->async_tx.flags = flags;
+	}
+
+	spin_unlock_bh(&ppc440spe_chan->lock);
+
+	return sw_desc;
+}
+
+/**
+ * ppc440spe_adma_prep_dma_pq - prepare CDB (group) for a GF-XOR operation
+ */
+struct dma_async_tx_descriptor *ppc440spe_adma_prep_dma_pq(struct dma_chan
+							*chan,
+							dma_addr_t * dst,
+							dma_addr_t * src,
+							unsigned int
+							src_cnt, const unsigned
+							char *scf,
+							size_t len,
+							unsigned long flags)
+{
+	struct ppc440spe_adma_chan *ppc440spe_chan;
+	struct ppc440spe_adma_desc_slot *sw_desc = NULL;
+	int dst_cnt = 0;
+
+	ppc440spe_chan = to_ppc440spe_adma_chan(chan);
+
+	BUG_ON(!len);
+	BUG_ON(unlikely(len > PPC440SPE_ADMA_XOR_MAX_BYTE_COUNT));
+	BUG_ON(!src_cnt);
+
+	if (src_cnt == 1 && dst[1] == src[0]) {
+		dma_addr_t dest[2];
+
+		/* dst[1] is real destination (Q) */
+		dest[0] = dst[1];
+		/* this is the page to multicast source data to */
+		dest[1] = ppc440spe_chan->qdest;
+		sw_desc = ppc440spe_dma01_prep_mult(ppc440spe_chan,
+						 dest, 2, src, src_cnt, scf,
+						 len, flags);
+		return sw_desc ? &sw_desc->async_tx : NULL;
+	}
+
+	if (src_cnt == 2 && dst[1] == src[1]) {
+		sw_desc = ppc440spe_dma01_prep_sum_product(ppc440spe_chan,
+							&dst[1], src, 2, scf,
+							len, flags);
+		return sw_desc ? &sw_desc->async_tx : NULL;
+	}
+
+	if (!(flags & DMA_PREP_PQ_DISABLE_P)) {
+		BUG_ON(!dst[0]);
+		dst_cnt++;
+		flags |= DMA_PREP_ZERO_P;
+	}
+
+	if (!(flags & DMA_PREP_PQ_DISABLE_Q)) {
+		BUG_ON(!dst[1]);
+		dst_cnt++;
+		flags |= DMA_PREP_ZERO_Q;
+	}
+
+	BUG_ON(!dst_cnt);
+
+	dev_dbg(ppc440spe_chan->device->common.dev,
+		"ppc440spe adma%d: %s src_cnt: %d len: %u int_en: %d\n",
+		ppc440spe_chan->device->id, __func__, src_cnt, len,
+		flags & DMA_PREP_INTERRUPT ? 1 : 0);
+
+	switch (ppc440spe_chan->device->id) {
+	case PPC440SPE_DMA0_ID:
+	case PPC440SPE_DMA1_ID:
+		sw_desc = ppc440spe_dma01_prep_pq(ppc440spe_chan,
+						  dst, dst_cnt, src, src_cnt,
+						  scf, len, flags);
+		break;
+
+	case PPC440SPE_XOR_ID:
+		sw_desc = ppc440spe_dma2_prep_pq(ppc440spe_chan,
+						 dst, dst_cnt, src, src_cnt,
+						 scf, len, flags);
+		break;
+	}
+
+	return sw_desc ? &sw_desc->async_tx : NULL;
+}
+int ppc440spe_adma_setup_irqs(struct ppc440spe_adma_device *adev,
+			   struct ppc440spe_adma_chan *chan, int *initcode)
+{
+	struct platform_device *ofdev;
+	struct device_node *np;
+	int ret;
+
+	ofdev = container_of(adev->dev, struct platform_device, dev);
+	np = ofdev->dev.of_node;
+	if (adev->id != PPC440SPE_XOR_ID) {
+		adev->err_irq = irq_of_parse_and_map(np, 1);
+		if (adev->err_irq == NO_IRQ) {
+			dev_warn(adev->dev, "no err irq resource?\n");
+			*initcode = PPC_ADMA_INIT_IRQ2;
+			adev->err_irq = -ENXIO;
+		} else
+			atomic_inc(&ppc440spe_adma_err_irq_ref);
+	} else {
+		adev->err_irq = -ENXIO;
+	}
+
+	adev->irq = irq_of_parse_and_map(np, 0);
+	if (adev->irq == NO_IRQ) {
+		dev_err(adev->dev, "no irq resource\n");
+		*initcode = PPC_ADMA_INIT_IRQ1;
+		ret = -ENXIO;
+		goto err_irq_map;
+	}
+	dev_dbg(adev->dev, "irq %d, err irq %d\n", adev->irq, adev->err_irq);
+
+	ret = request_irq(adev->irq, ppc440spe_adma_eot_handler,
+			  0, dev_driver_string(adev->dev), chan);
+	if (ret) {
+		dev_err(adev->dev, "can't request irq %d\n", adev->irq);
+		*initcode = PPC_ADMA_INIT_IRQ1;
+		ret = -EIO;
+		goto err_req1;
+	}
+
+	/* only DMA engines have a separate error IRQ
+	 * so it's Ok if err_irq < 0 in XOR engine case.
+	 */
+	if (adev->err_irq > 0) {
+		/* both DMA engines share common error IRQ */
+		ret = request_irq(adev->err_irq,
+				  ppc440spe_adma_err_handler,
+				  IRQF_SHARED,
+				  dev_driver_string(adev->dev), chan);
+		if (ret) {
+			dev_err(adev->dev, "can't request irq %d\n",
+				adev->err_irq);
+			*initcode = PPC_ADMA_INIT_IRQ2;
+			ret = -EIO;
+			goto err_req2;
+		}
+	}
+
+	if (adev->id == PPC440SPE_XOR_ID) {
+		/* enable XOR engine interrupts */
+		iowrite32be(XOR_IE_CBCIE_BIT | XOR_IE_ICBIE_BIT |
+			    XOR_IE_ICIE_BIT | XOR_IE_RPTIE_BIT,
+			    &adev->xor_reg->ier);
+	} else {
+		u32 mask, enable;
+
+#if defined(CONFIG_440SPe) || defined(CONFIG_440SP)
+		np = of_find_compatible_node(NULL, NULL, "ibm,i2o-440spe");
+#endif
+		if (!np) {
+			pr_err("%s: can't find I2O device tree node\n",
+			       __func__);
+			ret = -ENODEV;
+			goto err_req2;
+		}
+		adev->i2o_reg = of_iomap(np, 0);
+		if (!adev->i2o_reg) {
+			pr_err("%s: failed to map I2O registers\n", __func__);
+			of_node_put(np);
+			ret = -EINVAL;
+			goto err_req2;
+		}
+		of_node_put(np);
+		/* Unmask 'CS FIFO Attention' interrupts and
+		 * enable generating interrupts on errors
+		 */
+		enable = (adev->id == PPC440SPE_DMA0_ID) ?
+		    ~(I2O_IOPIM_P0SNE | I2O_IOPIM_P0EM) :
+		    ~(I2O_IOPIM_P1SNE | I2O_IOPIM_P1EM);
+		mask = ioread32(&adev->i2o_reg->iopim) & enable;
+		iowrite32(mask, &adev->i2o_reg->iopim);
+	}
+	return 0;
+
+      err_req2:
+	free_irq(adev->irq, chan);
+      err_req1:
+	irq_dispose_mapping(adev->irq);
+      err_irq_map:
+	if (adev->err_irq > 0) {
+		if (atomic_dec_and_test(&ppc440spe_adma_err_irq_ref))
+			irq_dispose_mapping(adev->err_irq);
+	}
+	return ret;
+}
+
+void ppc440spe_adma_release_irqs(struct ppc440spe_adma_device *adev,
+			      struct ppc440spe_adma_chan *chan)
+{
+	u32 mask, disable;
+
+	if (adev->id == PPC440SPE_XOR_ID) {
+		/* disable XOR engine interrupts */
+		mask = ioread32be(&adev->xor_reg->ier);
+		mask &= ~(XOR_IE_CBCIE_BIT | XOR_IE_ICBIE_BIT |
+			  XOR_IE_ICIE_BIT | XOR_IE_RPTIE_BIT);
+		iowrite32be(mask, &adev->xor_reg->ier);
+	} else {
+		/* disable DMAx engine interrupts */
+		disable = (adev->id == PPC440SPE_DMA0_ID) ?
+		    (I2O_IOPIM_P0SNE | I2O_IOPIM_P0EM) :
+		    (I2O_IOPIM_P1SNE | I2O_IOPIM_P1EM);
+		mask = ioread32(&adev->i2o_reg->iopim) | disable;
+		iowrite32(mask, &adev->i2o_reg->iopim);
+	}
+	free_irq(adev->irq, chan);
+	irq_dispose_mapping(adev->irq);
+	if (adev->err_irq > 0) {
+		free_irq(adev->err_irq, chan);
+		if (atomic_dec_and_test(&ppc440spe_adma_err_irq_ref)) {
+			irq_dispose_mapping(adev->err_irq);
+			iounmap(adev->i2o_reg);
+		}
+	}
+}
+
+/*
+ * Common initialisation for RAID engines; allocate memory for
+ * DMAx FIFOs, perform configuration common for all DMA engines.
+ * Further DMA engine specific configuration is done at probe time.
+ */
+static int ppc440spe_configure_raid_devices(void)
+{
+	struct device_node *np;
+	struct resource i2o_res;
+	struct i2o_regs __iomem *i2o_reg;
+	dcr_host_t i2o_dcr_host;
+	unsigned int dcr_base, dcr_len;
+	int i, ret;
+
+#if defined(CONFIG_440SPe) || defined(CONFIG_440SP)
+	np = of_find_compatible_node(NULL, NULL, "ibm,i2o-440spe");
+#endif
+	if (!np) {
+		pr_err("%s: can't find I2O device tree node\n", __func__);
+		return -ENODEV;
+	}
+
+	if (of_address_to_resource(np, 0, &i2o_res)) {
+		of_node_put(np);
+		return -EINVAL;
+	}
+
+	i2o_reg = of_iomap(np, 0);
+	if (!i2o_reg) {
+		pr_err("%s: failed to map I2O registers\n", __func__);
+		of_node_put(np);
+		return -EINVAL;
+	}
+
+	/* Get I2O DCRs base */
+	dcr_base = dcr_resource_start(np, 0);
+	dcr_len = dcr_resource_len(np, 0);
+	if (!dcr_base && !dcr_len) {
+		pr_err("%s: can't get DCR registers base/len!\n",
+		       np->full_name);
+		of_node_put(np);
+		iounmap(i2o_reg);
+		return -ENODEV;
+	}
+
+	i2o_dcr_host = dcr_map(np, dcr_base, dcr_len);
+	if (!DCR_MAP_OK(i2o_dcr_host)) {
+		pr_err("%s: failed to map DCRs!\n", np->full_name);
+		of_node_put(np);
+		iounmap(i2o_reg);
+		return -ENODEV;
+	}
+	of_node_put(np);
+
+	/* Provide memory regions for DMA's FIFOs: I2O, DMA0 and DMA1 share
+	 * the base address of FIFO memory space.
+	 * Actually we need twice more physical memory than programmed in the
+	 * <fsiz> register (because there are two FIFOs for each DMA: CP and CS)
+	 */
+	ppc440spe_dma_fifo_buf = kmalloc((DMA0_FIFO_SIZE + DMA1_FIFO_SIZE) << 1,
+				      GFP_KERNEL);
+	if (!ppc440spe_dma_fifo_buf) {
+		pr_err("%s: DMA FIFO buffer allocation failed.\n", __func__);
+		iounmap(i2o_reg);
+		dcr_unmap(i2o_dcr_host, dcr_len);
+		return -ENOMEM;
+	}
+
+	/*
+	 * Configure h/w
+	 */
+	/* Reset I2O/DMA */
+	mtdcri(SDR0, DCRN_SDR0_SRST, DCRN_SDR0_SRST_I2ODMA);
+	mtdcri(SDR0, DCRN_SDR0_SRST, 0);
+
+	/* Setup the base address of mmaped registers */
+	dcr_write(i2o_dcr_host, DCRN_I2O0_IBAH, (u32) (i2o_res.start >> 32));
+	dcr_write(i2o_dcr_host, DCRN_I2O0_IBAL, (u32) (i2o_res.start) |
+		  I2O_REG_ENABLE);
+	dcr_unmap(i2o_dcr_host, dcr_len);
+
+	/* Setup FIFO memory space base address */
+	iowrite32(0, &i2o_reg->ifbah);
+	iowrite32(((u32) __pa(ppc440spe_dma_fifo_buf)), &i2o_reg->ifbal);
+
+	/* set zero FIFO size for I2O, so the whole
+	 * ppc440spe_dma_fifo_buf is used by DMAs.
+	 * DMAx_FIFOs will be configured while probe.
+	 */
+	iowrite32(0, &i2o_reg->ifsiz);
+	iounmap(i2o_reg);
+
+	/* To prepare WXOR/RXOR functionality we need access to
+	 * Memory Queue Module DCRs (finally it will be enabled
+	 * via /sys interface of the ppc440spe ADMA driver).
+	 */
+#if defined(CONFIG_440SPe) || defined(CONFIG_440SP)
+	np = of_find_compatible_node(NULL, NULL, "ibm,mq-440spe");
+#endif
+	if (!np) {
+		pr_err("%s: can't find MQ device tree node\n", __func__);
+		ret = -ENODEV;
+		goto out_free;
+	}
+
+	/* Get MQ DCRs base */
+	dcr_base = dcr_resource_start(np, 0);
+	dcr_len = dcr_resource_len(np, 0);
+	if (!dcr_base && !dcr_len) {
+		pr_err("%s: can't get DCR registers base/len!\n",
+		       np->full_name);
+		ret = -ENODEV;
+		goto out_mq;
+	}
+
+	ppc440spe_mq_dcr_host = dcr_map(np, dcr_base, dcr_len);
+	if (!DCR_MAP_OK(ppc440spe_mq_dcr_host)) {
+		pr_err("%s: failed to map DCRs!\n", np->full_name);
+		ret = -ENODEV;
+		goto out_mq;
+	}
+	of_node_put(np);
+	ppc440spe_mq_dcr_len = dcr_len;
+
+	/* Set HB alias */
+	dcr_write(ppc440spe_mq_dcr_host, DCRN_MQ0_BAUH, DMA_CUED_XOR_HB);
+
+	/* Set:
+	 * - LL transaction passing limit to 1;
+	 * - Memory controller cycle limit to 1;
+	 * - Galois Polynomial to 0x14d (default)
+	 */
+	dcr_write(ppc440spe_mq_dcr_host, DCRN_MQ0_CFBHL,
+		  (1 << MQ0_CFBHL_TPLM) | (1 << MQ0_CFBHL_HBCL) |
+		  (PPC440SPE_DEFAULT_POLY << MQ0_CFBHL_POLY));
+
+	atomic_set(&ppc440spe_adma_err_irq_ref, 0);
+	for (i = 0; i < PPC440SPE_ADMA_ENGINES_NUM; i++)
+		ppc440spe_adma_devices[i] = -1;
+
+	return 0;
+
+      out_mq:
+	of_node_put(np);
+      out_free:
+	kfree(ppc440spe_dma_fifo_buf);
+	return ret;
+}
+
+/**
+ * ppc440spe_test_callback - called when test operation has been done
+ */
+static void ppc440spe_test_callback(void *unused)
+{
+	complete(&ppc440spe_r6_test_comp);
+}
+
+/**
+ * ppc440spe_test_raid6 - test are RAID-6 capabilities enabled successfully.
+ *	For this we just perform one WXOR operation with the same source
+ *	and destination addresses, the GF-multiplier is 1; so if RAID-6
+ *	capabilities are enabled then we'll get src/dst filled with zero.
+ */
+static int ppc440spe_test_raid6(struct ppc440spe_adma_chan *chan)
+{
+	struct ppc440spe_adma_desc_slot *sw_desc, *iter;
+	struct page *pg;
+	char *a;
+	dma_addr_t dma_addr, addrs[2];
+	unsigned long op = 0;
+	int rval = 0;
+
+	set_bit(PPC440SPE_DESC_WXOR, &op);
+
+	pg = alloc_page(GFP_KERNEL);
+	if (!pg)
+		return -ENOMEM;
+
+	spin_lock_bh(&chan->lock);
+	sw_desc = ppc440spe_adma_alloc_slots(chan, 1, 1);
+	if (sw_desc) {
+		/* 1 src, 1 dsr, int_ena, WXOR */
+		ppc440spe_desc_init_dma01pq(sw_desc, 1, 1, 1, op);
+		list_for_each_entry(iter, &sw_desc->group_list, chain_node) {
+			ppc440spe_desc_set_byte_count(iter, chan, PAGE_SIZE);
+			iter->unmap_len = PAGE_SIZE;
+		}
+	} else {
+		rval = -EFAULT;
+		spin_unlock_bh(&chan->lock);
+		goto exit;
+	}
+	spin_unlock_bh(&chan->lock);
+
+	/* Fill the test page with ones */
+	memset(page_address(pg), 0xFF, PAGE_SIZE);
+	dma_addr = dma_map_page(chan->device->dev, pg, 0,
+				PAGE_SIZE, DMA_BIDIRECTIONAL);
+
+	/* Setup addresses */
+	ppc440spe_adma_pq_set_src(sw_desc, dma_addr, 0);
+	ppc440spe_adma_pq_set_src_mult(sw_desc, 1, 0, 0);
+	addrs[0] = dma_addr;
+	addrs[1] = 0;
+	ppc440spe_adma_pq_set_dest(sw_desc, addrs, DMA_PREP_PQ_DISABLE_Q);
+
+	async_tx_ack(&sw_desc->async_tx);
+	sw_desc->async_tx.callback = ppc440spe_test_callback;
+	sw_desc->async_tx.callback_param = NULL;
+
+	init_completion(&ppc440spe_r6_test_comp);
+
+	ppc440spe_adma_tx_submit(&sw_desc->async_tx);
+	ppc440spe_adma_issue_pending(&chan->common);
+
+	wait_for_completion(&ppc440spe_r6_test_comp);
+
+	/* Now check if the test page is zeroed */
+	a = page_address(pg);
+	if ((*(u32 *) a) == 0 && memcmp(a, a + 4, PAGE_SIZE - 4) == 0) {
+		/* page is zero - RAID-6 enabled */
+		rval = 0;
+	} else {
+		/* RAID-6 was not enabled */
+		rval = -EINVAL;
+	}
+      exit:
+	__free_page(pg);
+	return rval;
+}
+
+/**
+ * ppc440spe_adma_remove - remove the asynch device
+ */
+int __devexit ppc440spe_adma_remove(struct platform_device *ofdev)
+{
+	struct ppc440spe_adma_device *adev = dev_get_drvdata(&ofdev->dev);
+	struct device_node *np = ofdev->dev.of_node;
+	struct resource res;
+	struct dma_chan *chan, *_chan;
+	struct ppc_dma_chan_ref *ref, *_ref;
+	struct ppc440spe_adma_chan *ppc440spe_chan;
+
+	dev_set_drvdata(&ofdev->dev, NULL);
+	if (adev->id < PPC440SPE_ADMA_ENGINES_NUM)
+		ppc440spe_adma_devices[adev->id] = -1;
+
+	dma_async_device_unregister(&adev->common);
+
+	list_for_each_entry_safe(chan, _chan, &adev->common.channels,
+				 device_node) {
+		ppc440spe_chan = to_ppc440spe_adma_chan(chan);
+		ppc440spe_adma_release_irqs(adev, ppc440spe_chan);
+		tasklet_kill(&ppc440spe_chan->irq_tasklet);
+		if (adev->id != PPC440SPE_XOR_ID) {
+			dma_unmap_page(&ofdev->dev, ppc440spe_chan->pdest,
+				       PAGE_SIZE, DMA_BIDIRECTIONAL);
+			dma_unmap_page(&ofdev->dev, ppc440spe_chan->qdest,
+				       PAGE_SIZE, DMA_BIDIRECTIONAL);
+			__free_page(ppc440spe_chan->pdest_page);
+			__free_page(ppc440spe_chan->qdest_page);
+		}
+		list_for_each_entry_safe(ref, _ref, &ppc440spe_adma_chan_list,
+					 node) {
+			if (ppc440spe_chan == to_ppc440spe_adma_chan(ref->chan)) {
+				list_del(&ref->node);
+				kfree(ref);
+			}
+		}
+		list_del(&chan->device_node);
+		kfree(ppc440spe_chan);
+	}
+
+	dma_free_coherent(adev->dev, adev->pool_size,
+			  adev->dma_desc_pool_virt, adev->dma_desc_pool);
+	if (adev->id == PPC440SPE_XOR_ID)
+		iounmap(adev->xor_reg);
+	else
+		iounmap(adev->dma_reg);
+	of_address_to_resource(np, 0, &res);
+	release_mem_region(res.start, resource_size(&res));
+	kfree(adev);
+	return 0;
+}
+
+/*
+ * /sys driver interface to enable h/w RAID-6 capabilities
+ * Files created in e.g. /sys/devices/plb.0/400100100.dma0/driver/
+ * directory are "devices", "enable" and "poly".
+ * "devices" shows available engines.
+ * "enable" is used to enable RAID-6 capabilities or to check
+ * whether these has been activated.
+ * "poly" allows setting/checking used polynomial (for PPC440spe only).
+ */
+
+static ssize_t show_ppc440spe_devices(struct device_driver *dev, char *buf)
+{
+	ssize_t size = 0;
+	int i;
+
+	for (i = 0; i < PPC440SPE_ADMA_ENGINES_NUM; i++) {
+		if (ppc440spe_adma_devices[i] == -1)
+			continue;
+		size += snprintf(buf + size, PAGE_SIZE - size,
+				 "PPC440SPe-ADMA.%d: %s\n", i,
+				 ppc_adma_errors[ppc440spe_adma_devices[i]]);
+	}
+	return size;
+}
+static ssize_t show_ppc440spe_r6enable(struct device_driver *dev, char *buf)
+{
+	return snprintf(buf, PAGE_SIZE,
+			"PPC440SP(e) RAID-6 capabilities are %sABLED.\n",
+			ppc440spe_r6_enabled ? "EN" : "DIS");
+}
+
+static ssize_t store_ppc440spe_r6enable(struct device_driver *dev,
+					const char *buf, size_t count)
+{
+	unsigned long val;
+
+	if (!count || count > 11)
+		return -EINVAL;
+
+	if (!ppc440spe_r6_tchan)
+		return -EFAULT;
+
+	/* Write a key */
+	sscanf(buf, "%lx", &val);
+	dcr_write(ppc440spe_mq_dcr_host, DCRN_MQ0_XORBA, val);
+	isync();
+
+	/* Verify whether it really works now */
+	if (ppc440spe_test_raid6(ppc440spe_r6_tchan) == 0) {
+		pr_info("PPC440SP(e) RAID-6 has been activated "
+			"successfully\n");
+		ppc440spe_r6_enabled = 1;
+	} else {
+		pr_info("PPC440SP(e) RAID-6 hasn't been activated!"
+			" Error key ?\n");
+		ppc440spe_r6_enabled = 0;
+	}
+	return count;
+}
+
+static ssize_t show_ppc4440spe_r6poly(struct device_driver *dev, char *buf)
+{
+	ssize_t size = 0;
+	u32 reg;
+
+#ifdef CONFIG_440SP
+	/* 440SP has fixed polynomial */
+	reg = 0x4d;
+#else
+	reg = dcr_read(ppc440spe_mq_dcr_host, DCRN_MQ0_CFBHL);
+	reg >>= MQ0_CFBHL_POLY;
+	reg &= 0xFF;
+#endif
+
+	size = snprintf(buf, PAGE_SIZE, "PPC440SP(e) RAID-6 driver "
+			"uses 0x1%02x polynomial.\n", reg);
+	return size;
+}
+
+static ssize_t store_ppc440spe_r6poly(struct device_driver *dev,
+				      const char *buf, size_t count)
+{
+	unsigned long reg, val;
+
+#ifdef CONFIG_440SP
+	/* 440SP uses default 0x14D polynomial only */
+	return -EINVAL;
+#endif
+
+	if (!count || count > 6)
+		return -EINVAL;
+
+	/* e.g., 0x14D or 0x11D */
+	sscanf(buf, "%lx", &val);
+
+	if (val & ~0x1FF)
+		return -EINVAL;
+
+	val &= 0xFF;
+	reg = dcr_read(ppc440spe_mq_dcr_host, DCRN_MQ0_CFBHL);
+	reg &= ~(0xFF << MQ0_CFBHL_POLY);
+	reg |= val << MQ0_CFBHL_POLY;
+	dcr_write(ppc440spe_mq_dcr_host, DCRN_MQ0_CFBHL, reg);
+
+	return count;
+}
+
+static DRIVER_ATTR(devices, S_IRUGO, show_ppc440spe_devices, NULL);
+static DRIVER_ATTR(enable, S_IRUGO | S_IWUSR, show_ppc440spe_r6enable,
+		   store_ppc440spe_r6enable);
+static DRIVER_ATTR(poly, S_IRUGO | S_IWUSR, show_ppc4440spe_r6poly,
+		   store_ppc440spe_r6poly);
+int ppc440spe_adma_hw_init(void)
+{
+	int ret;
+
+	ret = ppc440spe_configure_raid_devices();
+	if (ret)
+		return ret;
+
+	ret = of_register_platform_driver(&ppc440spe_adma_driver);
+	if (ret) {
+		pr_err("%s: failed to register platform driver\n", __func__);
+		goto out_reg;
+	}
+
+	/* Initialization status */
+	ret = driver_create_file(&ppc440spe_adma_driver.driver,
+				 &driver_attr_devices);
+	if (ret)
+		goto out_dev;
+
+	/* RAID-6 h/w enable entry */
+	ret = driver_create_file(&ppc440spe_adma_driver.driver,
+				 &driver_attr_enable);
+	if (ret)
+		goto out_en;
+
+	/* GF polynomial to use */
+	ret = driver_create_file(&ppc440spe_adma_driver.driver, &driver_attr_poly);
+	if (!ret)
+		return ret;
+
+	driver_remove_file(&ppc440spe_adma_driver.driver, &driver_attr_enable);
+      out_en:
+	driver_remove_file(&ppc440spe_adma_driver.driver, &driver_attr_devices);
+      out_dev:
+	/* User will not be able to enable h/w RAID-6 */
+	pr_err("%s: failed to create RAID-6 driver interface\n", __func__);
+      out_reg:
+	dcr_unmap(ppc440spe_mq_dcr_host, ppc440spe_mq_dcr_len);
+	kfree(ppc440spe_dma_fifo_buf);
+	return ret;
+}
+
+static void __exit ppc440spe_adma_exit(void)
+{
+	driver_remove_file(&ppc440spe_adma_driver.driver, &driver_attr_poly);
+	driver_remove_file(&ppc440spe_adma_driver.driver, &driver_attr_enable);
+	driver_remove_file(&ppc440spe_adma_driver.driver, &driver_attr_devices);
+	of_unregister_platform_driver(&ppc440spe_adma_driver);
+	dcr_unmap(ppc440spe_mq_dcr_host, ppc440spe_mq_dcr_len);
+	kfree(ppc440spe_dma_fifo_buf);
+}
diff --git a/drivers/dma/ppc4xx/ppc440spe-adma.h b/drivers/dma/ppc4xx/ppc440spe-adma.h
new file mode 100644
index 0000000..81a1f46
--- /dev/null
+++ b/drivers/dma/ppc4xx/ppc440spe-adma.h
@@ -0,0 +1,2391 @@
+/*
+ * Copyright (C) 2006-2009 DENX Software Engineering.
+ *
+ * Author: Yuri Tikhonov <yur@emcraft.com>
+ *
+ * Further porting to arch/powerpc by
+ * 	Anatolij Gustschin <agust@denx.de>
+ * 	Tirumala R Marri <tmarri@apm.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc., 59
+ * Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+ *
+ * The full GNU General Public License is included in this distribution in the
+ * file called COPYING.
+ */
+#ifndef __PPC440SPE_ADMA_H
+#define __PPC440SPE_ADMA_H
+#include <linux/of.h>
+#include <linux/of_platform.h>
+#include <asm/dcr.h>
+#include <asm/dcr-regs.h>
+#include "adma.h"
+#include "dma.h"
+#include "xor.h"
+
+#ifdef ADMA_LL_DEBUG
+#define ADMA_LL_DBG(x) ({ if (1) x; 0; })
+#else
+#define ADMA_LL_DBG(x) ({ if (0) x; 0; })
+#endif
+
+enum ppc_adma_init_code {
+	PPC_ADMA_INIT_OK = 0,
+	PPC_ADMA_INIT_MEMRES,
+	PPC_ADMA_INIT_MEMREG,
+	PPC_ADMA_INIT_ALLOC,
+	PPC_ADMA_INIT_COHERENT,
+	PPC_ADMA_INIT_CHANNEL,
+	PPC_ADMA_INIT_IRQ1,
+	PPC_ADMA_INIT_IRQ2,
+	PPC_ADMA_INIT_REGISTER
+};
+
+struct ppc_dma_chan_ref {
+	struct dma_chan *chan;
+	struct list_head node;
+};
+
+/* This flag is set when want to refetch the xor chain in the interrupt
+ * handler
+ */
+static u32 do_xor_refetch;
+static struct ppc440spe_adma_chan *ppc440spe_r6_tchan;
+/* Since RXOR operations use the common register (MQ0_CF2H) for setting-up
+ * the block size in transactions, then we do not allow to activate more than
+ * only one RXOR transactions simultaneously. So use this var to store
+ * the information about is RXOR currently active (PPC440SPE_RXOR_RUN bit is
+ * set) or not (PPC440SPE_RXOR_RUN is clear).
+ */
+static unsigned long ppc440spe_rxor_state;
+
+/* Pointer to last linked and submitted xor CB */
+static struct ppc440spe_adma_desc_slot *xor_last_linked;
+static struct ppc440spe_adma_desc_slot *xor_last_submit;
+
+
+/* Pointers to last submitted to DMA0, DMA1 CDBs */
+static struct ppc440spe_adma_desc_slot *chan_last_sub[3];
+static struct ppc440spe_adma_desc_slot *chan_first_cdb[3];
+
+/* The list of channels exported by ppc4xx ADMA */
+static struct list_head ppc440spe_adma_chan_list =
+LIST_HEAD_INIT(ppc440spe_adma_chan_list);
+
+static int ppc440spe_adma_devices[PPC440SPE_ADMA_ENGINES_NUM];
+static struct of_platform_driver ppc440spe_adma_driver;
+
+static const struct of_device_id ppc440spe_adma_of_match[] __devinitconst = {
+	{.compatible = "ibm,dma-440spe",},
+	{.compatible = "amcc,xor-accelerator",},
+	{},
+};
+
+MODULE_DEVICE_TABLE(of, ppc440spe_adma_of_match);
+
+
+
+irqreturn_t ppc440spe_adma_eot_handler(int irq, void *data);
+irqreturn_t ppc440spe_adma_err_handler(int irq, void *data);
+
+void ppc440spe_adma_issue_pending(struct dma_chan *chan);
+struct ppc440spe_adma_desc_slot *ppc440spe_adma_alloc_slots(struct
+						      ppc440spe_adma_chan
+						      *chan,
+						      int num_slots,
+						      int slots_per_op);
+void ppc440spe_adma_free_slots(struct ppc440spe_adma_desc_slot *slot,
+			    struct ppc440spe_adma_chan *chan);
+dma_cookie_t ppc440spe_adma_tx_submit(struct dma_async_tx_descriptor *tx);
+void ppc440spe_chan_start_null_xor(struct ppc440spe_adma_chan *chan);
+void prep_dma_pqzero_sum_dbg(int id, dma_addr_t * src,
+			     unsigned int src_cnt, const unsigned char *scf);
+/*
+ * ppc440spe_get_group_entry - get group entry with index idx
+ * @tdesc: is the last allocated slot in the group.
+ */
+static struct ppc440spe_adma_desc_slot *ppc440spe_get_group_entry(struct
+							    ppc440spe_adma_desc_slot
+							    *tdesc,
+							    u32 entry_idx)
+{
+	struct ppc440spe_adma_desc_slot *iter = tdesc->group_head;
+	int i = 0;
+
+	if (entry_idx < 0 || entry_idx >= (tdesc->src_cnt + tdesc->dst_cnt)) {
+		printk("%s: entry_idx %d, src_cnt %d, dst_cnt %d\n",
+		       __func__, entry_idx, tdesc->src_cnt, tdesc->dst_cnt);
+		BUG();
+	}
+
+	list_for_each_entry(iter, &tdesc->group_list, chain_node) {
+		if (i++ == entry_idx)
+			break;
+	}
+	return iter;
+}
+
+static inline void print_cb(struct ppc440spe_adma_chan *chan, void *block)
+{
+	struct dma_cdb *cdb;
+	struct xor_cb *cb;
+	int i;
+
+	switch (chan->device->id) {
+	case 0:
+	case 1:
+		cdb = block;
+
+		pr_debug("CDB at %p [%d]:\n"
+			 "\t attr 0x%02x opc 0x%02x cnt 0x%08x\n"
+			 "\t sg1u 0x%08x sg1l 0x%08x\n"
+			 "\t sg2u 0x%08x sg2l 0x%08x\n"
+			 "\t sg3u 0x%08x sg3l 0x%08x\n",
+			 cdb, chan->device->id,
+			 cdb->attr, cdb->opc, le32_to_cpu(cdb->cnt),
+			 le32_to_cpu(cdb->sg1u), le32_to_cpu(cdb->sg1l),
+			 le32_to_cpu(cdb->sg2u), le32_to_cpu(cdb->sg2l),
+			 le32_to_cpu(cdb->sg3u), le32_to_cpu(cdb->sg3l)
+		    );
+		break;
+	case 2:
+		cb = block;
+
+		pr_debug("CB at %p [%d]:\n"
+			 "\t cbc 0x%08x cbbc 0x%08x cbs 0x%08x\n"
+			 "\t cbtah 0x%08x cbtal 0x%08x\n"
+			 "\t cblah 0x%08x cblal 0x%08x\n",
+			 cb, chan->device->id,
+			 cb->cbc, cb->cbbc, cb->cbs,
+			 cb->cbtah, cb->cbtal, cb->cblah, cb->cblal);
+		for (i = 0; i < 16; i++) {
+			if (i && !cb->ops[i].h && !cb->ops[i].l)
+				continue;
+			pr_debug("\t ops[%2d]: h 0x%08x l 0x%08x\n",
+				 i, cb->ops[i].h, cb->ops[i].l);
+		}
+		break;
+	}
+}
+
+/******************************************************************************
+ * Command (Descriptor) Blocks low-level routines
+ ******************************************************************************/
+/**
+ * ppc440spe_desc_init_interrupt - initialize the descriptor for INTERRUPT
+ * pseudo operation
+ */
+static inline void ppc440spe_desc_init_interrupt(struct ppc440spe_adma_desc_slot
+					      *desc,
+					      struct ppc440spe_adma_chan *chan)
+{
+	struct xor_cb *p;
+
+	switch (chan->device->id) {
+	case PPC440SPE_XOR_ID:
+		p = desc->hw_desc;
+		memset(desc->hw_desc, 0, sizeof(struct xor_cb));
+		/* NOP with Command Block Complete Enable */
+		p->cbc = XOR_CBCR_CBCE_BIT;
+		break;
+	case PPC440SPE_DMA0_ID:
+	case PPC440SPE_DMA1_ID:
+		memset(desc->hw_desc, 0, sizeof(struct dma_cdb));
+		/* NOP with interrupt */
+		set_bit(PPC440SPE_DESC_INT, &desc->flags);
+		break;
+	default:
+		printk(KERN_ERR "Unsupported id %d in %s\n", chan->device->id,
+		       __func__);
+		break;
+	}
+}
+
+/**
+ * ppc440spe_desc_init_null_xor - initialize the descriptor for NULL XOR
+ * pseudo operation
+ */
+static inline void ppc440spe_desc_init_null_xor(struct ppc440spe_adma_desc_slot *desc)
+{
+	memset(desc->hw_desc, 0, sizeof(struct xor_cb));
+	desc->hw_next = NULL;
+	desc->src_cnt = 0;
+	desc->dst_cnt = 1;
+}
+
+/**
+ * ppc440spe_desc_init_xor - initialize the descriptor for XOR operation
+ */
+static inline void ppc440spe_desc_init_xor(struct ppc440spe_adma_desc_slot *desc,
+					int src_cnt, unsigned long flags)
+{
+	struct xor_cb *hw_desc = desc->hw_desc;
+
+	memset(desc->hw_desc, 0, sizeof(struct xor_cb));
+	desc->hw_next = NULL;
+	desc->src_cnt = src_cnt;
+	desc->dst_cnt = 1;
+
+	hw_desc->cbc = XOR_CBCR_TGT_BIT | src_cnt;
+	if (flags & DMA_PREP_INTERRUPT)
+		/* Enable interrupt on completion */
+		hw_desc->cbc |= XOR_CBCR_CBCE_BIT;
+}
+
+/**
+ * ppc440spe_desc_init_dma2pq - initialize the descriptor for PQ
+ * operation in DMA2 controller
+ */
+static inline void ppc440spe_desc_init_dma2pq(struct ppc440spe_adma_desc_slot *desc,
+					   int dst_cnt, int src_cnt,
+					   unsigned long flags)
+{
+	struct xor_cb *hw_desc = desc->hw_desc;
+
+	memset(desc->hw_desc, 0, sizeof(struct xor_cb));
+	desc->hw_next = NULL;
+	desc->src_cnt = src_cnt;
+	desc->dst_cnt = dst_cnt;
+	memset(desc->reverse_flags, 0, sizeof(desc->reverse_flags));
+	desc->descs_per_op = 0;
+
+	hw_desc->cbc = XOR_CBCR_TGT_BIT;
+	if (flags & DMA_PREP_INTERRUPT)
+		/* Enable interrupt on completion */
+		hw_desc->cbc |= XOR_CBCR_CBCE_BIT;
+}
+
+#define DMA_CTRL_FLAGS_LAST	DMA_PREP_FENCE
+#define DMA_PREP_ZERO_P		(DMA_CTRL_FLAGS_LAST << 1)
+#define DMA_PREP_ZERO_Q		(DMA_PREP_ZERO_P << 1)
+
+/**
+ * ppc440spe_desc_init_dma01pq - initialize the descriptors for PQ operation
+ * with DMA0/1
+ */
+static inline void ppc440spe_desc_init_dma01pq(struct ppc440spe_adma_desc_slot *desc,
+					    int dst_cnt, int src_cnt,
+					    unsigned long flags,
+					    unsigned long op)
+{
+	struct dma_cdb *hw_desc;
+	struct ppc440spe_adma_desc_slot *iter;
+	u8 dopc;
+
+	/* Common initialization of a PQ descriptors chain */
+	set_bits(op, &desc->flags);
+	desc->src_cnt = src_cnt;
+	desc->dst_cnt = dst_cnt;
+
+	/* WXOR MULTICAST if both P and Q are being computed
+	 * MV_SG1_SG2 if Q only
+	 */
+	dopc = (desc->dst_cnt == DMA_DEST_MAX_NUM) ?
+	    DMA_CDB_OPC_MULTICAST : DMA_CDB_OPC_MV_SG1_SG2;
+
+	list_for_each_entry(iter, &desc->group_list, chain_node) {
+		hw_desc = iter->hw_desc;
+		memset(iter->hw_desc, 0, sizeof(struct dma_cdb));
+
+		if (likely(!list_is_last(&iter->chain_node, &desc->group_list))) {
+			/* set 'next' pointer */
+			iter->hw_next = list_entry(iter->chain_node.next,
+						   struct ppc440spe_adma_desc_slot,
+						   chain_node);
+			clear_bit(PPC440SPE_DESC_INT, &iter->flags);
+		} else {
+			/* this is the last descriptor.
+			 * this slot will be pasted from ADMA level
+			 * each time it wants to configure parameters
+			 * of the transaction (src, dst, ...)
+			 */
+			iter->hw_next = NULL;
+			if (flags & DMA_PREP_INTERRUPT)
+				set_bit(PPC440SPE_DESC_INT, &iter->flags);
+			else
+				clear_bit(PPC440SPE_DESC_INT, &iter->flags);
+		}
+	}
+
+	/* Set OPS depending on WXOR/RXOR type of operation */
+	if (!test_bit(PPC440SPE_DESC_RXOR, &desc->flags)) {
+		/* This is a WXOR only chain:
+		 * - first descriptors are for zeroing destinations
+		 *   if PPC440SPE_ZERO_P/Q set;
+		 * - descriptors remained are for GF-XOR operations.
+		 */
+		iter = list_first_entry(&desc->group_list,
+					struct ppc440spe_adma_desc_slot,
+					chain_node);
+
+		if (test_bit(PPC440SPE_ZERO_P, &desc->flags)) {
+			hw_desc = iter->hw_desc;
+			hw_desc->opc = DMA_CDB_OPC_MV_SG1_SG2;
+			iter = list_first_entry(&iter->chain_node,
+						struct ppc440spe_adma_desc_slot,
+						chain_node);
+		}
+
+		if (test_bit(PPC440SPE_ZERO_Q, &desc->flags)) {
+			hw_desc = iter->hw_desc;
+			hw_desc->opc = DMA_CDB_OPC_MV_SG1_SG2;
+			iter = list_first_entry(&iter->chain_node,
+						struct ppc440spe_adma_desc_slot,
+						chain_node);
+		}
+
+		list_for_each_entry_from(iter, &desc->group_list, chain_node) {
+			hw_desc = iter->hw_desc;
+			hw_desc->opc = dopc;
+		}
+	} else {
+		/* This is either RXOR-only or mixed RXOR/WXOR */
+
+		/* The first 1 or 2 slots in chain are always RXOR,
+		 * if need to calculate P & Q, then there are two
+		 * RXOR slots; if only P or only Q, then there is one
+		 */
+		iter = list_first_entry(&desc->group_list,
+					struct ppc440spe_adma_desc_slot,
+					chain_node);
+		hw_desc = iter->hw_desc;
+		hw_desc->opc = DMA_CDB_OPC_MV_SG1_SG2;
+
+		if (desc->dst_cnt == DMA_DEST_MAX_NUM) {
+			iter = list_first_entry(&iter->chain_node,
+						struct ppc440spe_adma_desc_slot,
+						chain_node);
+			hw_desc = iter->hw_desc;
+			hw_desc->opc = DMA_CDB_OPC_MV_SG1_SG2;
+		}
+
+		/* The remaining descs (if any) are WXORs */
+		if (test_bit(PPC440SPE_DESC_WXOR, &desc->flags)) {
+			iter = list_first_entry(&iter->chain_node,
+						struct ppc440spe_adma_desc_slot,
+						chain_node);
+			list_for_each_entry_from(iter, &desc->group_list,
+						 chain_node) {
+				hw_desc = iter->hw_desc;
+				hw_desc->opc = dopc;
+			}
+		}
+	}
+}
+
+/**
+ * ppc440spe_desc_init_dma01pqzero_sum - initialize the descriptor
+ * for PQ_ZERO_SUM operation
+ */
+static inline void ppc440spe_desc_init_dma01pqzero_sum(struct ppc440spe_adma_desc_slot
+						    *desc, int dst_cnt,
+						    int src_cnt)
+{
+	struct dma_cdb *hw_desc;
+	struct ppc440spe_adma_desc_slot *iter;
+	int i = 0;
+	u8 dopc = (dst_cnt == 2) ? DMA_CDB_OPC_MULTICAST :
+	    DMA_CDB_OPC_MV_SG1_SG2;
+	/*
+	 * Initialize starting from 2nd or 3rd descriptor dependent
+	 * on dst_cnt. First one or two slots are for cloning P
+	 * and/or Q to chan->pdest and/or chan->qdest as we have
+	 * to preserve original P/Q.
+	 */
+	iter = list_first_entry(&desc->group_list,
+				struct ppc440spe_adma_desc_slot, chain_node);
+	iter = list_entry(iter->chain_node.next,
+			  struct ppc440spe_adma_desc_slot, chain_node);
+
+	if (dst_cnt > 1) {
+		iter = list_entry(iter->chain_node.next,
+				  struct ppc440spe_adma_desc_slot, chain_node);
+	}
+	/* initialize each source descriptor in chain */
+	list_for_each_entry_from(iter, &desc->group_list, chain_node) {
+		hw_desc = iter->hw_desc;
+		memset(iter->hw_desc, 0, sizeof(struct dma_cdb));
+		iter->src_cnt = 0;
+		iter->dst_cnt = 0;
+
+		/* This is a ZERO_SUM operation:
+		 * - <src_cnt> descriptors starting from 2nd or 3rd
+		 *   descriptor are for GF-XOR operations;
+		 * - remaining <dst_cnt> descriptors are for checking the result
+		 */
+		if (i++ < src_cnt)
+			/* MV_SG1_SG2 if only Q is being verified
+			 * MULTICAST if both P and Q are being verified
+			 */
+			hw_desc->opc = dopc;
+		else
+			/* DMA_CDB_OPC_DCHECK128 operation */
+			hw_desc->opc = DMA_CDB_OPC_DCHECK128;
+
+		if (likely(!list_is_last(&iter->chain_node, &desc->group_list))) {
+			/* set 'next' pointer */
+			iter->hw_next = list_entry(iter->chain_node.next,
+						   struct ppc440spe_adma_desc_slot,
+						   chain_node);
+		} else {
+			/* this is the last descriptor.
+			 * this slot will be pasted from ADMA level
+			 * each time it wants to configure parameters
+			 * of the transaction (src, dst, ...)
+			 */
+			iter->hw_next = NULL;
+			/* always enable interrupt generation since we get
+			 * the status of pqzero from the handler
+			 */
+			set_bit(PPC440SPE_DESC_INT, &iter->flags);
+		}
+	}
+	desc->src_cnt = src_cnt;
+	desc->dst_cnt = dst_cnt;
+}
+
+/**
+ * ppc440spe_desc_init_memcpy - initialize the descriptor for MEMCPY operation
+ */
+static inline void ppc440spe_desc_init_memcpy(struct ppc440spe_adma_desc_slot *desc,
+					   unsigned long flags)
+{
+	struct dma_cdb *hw_desc = desc->hw_desc;
+
+	memset(desc->hw_desc, 0, sizeof(struct dma_cdb));
+	desc->hw_next = NULL;
+	desc->src_cnt = 1;
+	desc->dst_cnt = 1;
+
+	if (flags & DMA_PREP_INTERRUPT)
+		set_bit(PPC440SPE_DESC_INT, &desc->flags);
+	else
+		clear_bit(PPC440SPE_DESC_INT, &desc->flags);
+
+	hw_desc->opc = DMA_CDB_OPC_MV_SG1_SG2;
+}
+
+/**
+ * ppc440spe_desc_init_memset - initialize the descriptor for MEMSET operation
+ */
+static inline void ppc440spe_desc_init_memset(struct ppc440spe_adma_desc_slot *desc,
+					   int value, unsigned long flags)
+{
+	struct dma_cdb *hw_desc = desc->hw_desc;
+
+	memset(desc->hw_desc, 0, sizeof(struct dma_cdb));
+	desc->hw_next = NULL;
+	desc->src_cnt = 1;
+	desc->dst_cnt = 1;
+
+	if (flags & DMA_PREP_INTERRUPT)
+		set_bit(PPC440SPE_DESC_INT, &desc->flags);
+	else
+		clear_bit(PPC440SPE_DESC_INT, &desc->flags);
+
+	hw_desc->sg1u = hw_desc->sg1l = cpu_to_le32((u32) value);
+	hw_desc->sg3u = hw_desc->sg3l = cpu_to_le32((u32) value);
+	hw_desc->opc = DMA_CDB_OPC_DFILL128;
+}
+
+/**
+ * ppc440spe_desc_set_byte_count - set number of data bytes involved
+ * into the operation
+ */
+static inline void ppc440spe_desc_set_byte_count(struct ppc440spe_adma_desc_slot
+					      *desc,
+					      struct ppc440spe_adma_chan *chan,
+					      u32 byte_count)
+{
+	struct dma_cdb *dma_hw_desc;
+	struct xor_cb *xor_hw_desc;
+
+	switch (chan->device->id) {
+	case PPC440SPE_DMA0_ID:
+	case PPC440SPE_DMA1_ID:
+		dma_hw_desc = desc->hw_desc;
+		dma_hw_desc->cnt = cpu_to_le32(byte_count);
+		break;
+	case PPC440SPE_XOR_ID:
+		xor_hw_desc = desc->hw_desc;
+		xor_hw_desc->cbbc = byte_count;
+		break;
+	}
+}
+
+/**
+ * ppc440spe_desc_set_dcheck - set CHECK pattern
+ */
+static inline void ppc440spe_desc_set_dcheck(struct ppc440spe_adma_desc_slot *desc,
+					  struct ppc440spe_adma_chan *chan,
+					  u8 * qword)
+{
+	struct dma_cdb *dma_hw_desc;
+
+	switch (chan->device->id) {
+	case PPC440SPE_DMA0_ID:
+	case PPC440SPE_DMA1_ID:
+		dma_hw_desc = desc->hw_desc;
+		iowrite32(qword[0], &dma_hw_desc->sg3l);
+		iowrite32(qword[4], &dma_hw_desc->sg3u);
+		iowrite32(qword[8], &dma_hw_desc->sg2l);
+		iowrite32(qword[12], &dma_hw_desc->sg2u);
+		break;
+	default:
+		BUG();
+	}
+}
+
+/**
+ * ppc440spe_desc_get_src_addr - extract the source address from the descriptor
+ */
+static inline u32 ppc440spe_desc_get_src_addr(struct ppc440spe_adma_desc_slot *desc,
+					   struct ppc440spe_adma_chan *chan,
+					   int src_idx)
+{
+	struct dma_cdb *dma_hw_desc;
+	struct xor_cb *xor_hw_desc;
+
+	switch (chan->device->id) {
+	case PPC440SPE_DMA0_ID:
+	case PPC440SPE_DMA1_ID:
+		dma_hw_desc = desc->hw_desc;
+		/* May have 0, 1, 2, or 3 sources */
+		switch (dma_hw_desc->opc) {
+		case DMA_CDB_OPC_NO_OP:
+		case DMA_CDB_OPC_DFILL128:
+			return 0;
+		case DMA_CDB_OPC_DCHECK128:
+			if (unlikely(src_idx)) {
+				printk(KERN_ERR "%s: try to get %d source for"
+				       " DCHECK128\n", __func__, src_idx);
+				BUG();
+			}
+			return le32_to_cpu(dma_hw_desc->sg1l);
+		case DMA_CDB_OPC_MULTICAST:
+		case DMA_CDB_OPC_MV_SG1_SG2:
+			if (unlikely(src_idx > 2)) {
+				printk(KERN_ERR "%s: try to get %d source from"
+				       " DMA descr\n", __func__, src_idx);
+				BUG();
+			}
+			if (src_idx) {
+				if (le32_to_cpu(dma_hw_desc->sg1u) &
+				    DMA_CUED_XOR_WIN_MSK) {
+					u8 region;
+
+					if (src_idx == 1)
+						return le32_to_cpu(dma_hw_desc->
+								   sg1l) +
+						    desc->unmap_len;
+
+					region =
+					    (le32_to_cpu(dma_hw_desc->sg1u)) >>
+					    DMA_CUED_REGION_OFF;
+
+					region &= DMA_CUED_REGION_MSK;
+					switch (region) {
+					case DMA_RXOR123:
+						return le32_to_cpu(dma_hw_desc->
+								   sg1l) +
+						    (desc->unmap_len << 1);
+					case DMA_RXOR124:
+						return le32_to_cpu(dma_hw_desc->
+								   sg1l) +
+						    (desc->unmap_len * 3);
+					case DMA_RXOR125:
+						return le32_to_cpu(dma_hw_desc->
+								   sg1l) +
+						    (desc->unmap_len << 2);
+					default:
+						printk(KERN_ERR
+						       "%s: try to"
+						       " get src3 for region %02x"
+						       "PPC440SPE_DESC_RXOR12?\n",
+						       __func__, region);
+						BUG();
+					}
+				} else {
+					printk(KERN_ERR
+					       "%s: try to get %d"
+					       " source for non-cued descr\n",
+					       __func__, src_idx);
+					BUG();
+				}
+			}
+			return le32_to_cpu(dma_hw_desc->sg1l);
+		default:
+			printk(KERN_ERR "%s: unknown OPC 0x%02x\n",
+			       __func__, dma_hw_desc->opc);
+			BUG();
+		}
+		return le32_to_cpu(dma_hw_desc->sg1l);
+	case PPC440SPE_XOR_ID:
+		/* May have up to 16 sources */
+		xor_hw_desc = desc->hw_desc;
+		return xor_hw_desc->ops[src_idx].l;
+	}
+	return 0;
+}
+
+/**
+ * ppc440spe_desc_get_dest_addr - extract the destination address from the
+ * descriptor
+ */
+static inline u32 ppc440spe_desc_get_dest_addr(struct ppc440spe_adma_desc_slot *desc,
+					    struct ppc440spe_adma_chan *chan,
+					    int idx)
+{
+	struct dma_cdb *dma_hw_desc;
+	struct xor_cb *xor_hw_desc;
+
+	switch (chan->device->id) {
+	case PPC440SPE_DMA0_ID:
+	case PPC440SPE_DMA1_ID:
+		dma_hw_desc = desc->hw_desc;
+
+		if (likely(!idx))
+			return le32_to_cpu(dma_hw_desc->sg2l);
+		return le32_to_cpu(dma_hw_desc->sg3l);
+	case PPC440SPE_XOR_ID:
+		xor_hw_desc = desc->hw_desc;
+		return xor_hw_desc->cbtal;
+	}
+	return 0;
+}
+
+/**
+ * ppc440spe_desc_get_src_num - extract the number of source addresses from
+ * the descriptor
+ */
+static inline u32 ppc440spe_desc_get_src_num(struct ppc440spe_adma_desc_slot *desc,
+					  struct ppc440spe_adma_chan *chan)
+{
+	struct dma_cdb *dma_hw_desc;
+	struct xor_cb *xor_hw_desc;
+
+	switch (chan->device->id) {
+	case PPC440SPE_DMA0_ID:
+	case PPC440SPE_DMA1_ID:
+		dma_hw_desc = desc->hw_desc;
+
+		switch (dma_hw_desc->opc) {
+		case DMA_CDB_OPC_NO_OP:
+		case DMA_CDB_OPC_DFILL128:
+			return 0;
+		case DMA_CDB_OPC_DCHECK128:
+			return 1;
+		case DMA_CDB_OPC_MV_SG1_SG2:
+		case DMA_CDB_OPC_MULTICAST:
+			/*
+			 * Only for RXOR operations we have more than
+			 * one source
+			 */
+			if (le32_to_cpu(dma_hw_desc->sg1u) &
+			    DMA_CUED_XOR_WIN_MSK) {
+				/* RXOR op, there are 2 or 3 sources */
+				if (((le32_to_cpu(dma_hw_desc->sg1u) >>
+				      DMA_CUED_REGION_OFF) &
+				     DMA_CUED_REGION_MSK) == DMA_RXOR12) {
+					/* RXOR 1-2 */
+					return 2;
+				} else {
+					/* RXOR 1-2-3/1-2-4/1-2-5 */
+					return 3;
+				}
+			}
+			return 1;
+		default:
+			printk(KERN_ERR "%s: unknown OPC 0x%02x\n",
+			       __func__, dma_hw_desc->opc);
+			BUG();
+		}
+	case PPC440SPE_XOR_ID:
+		/* up to 16 sources */
+		xor_hw_desc = desc->hw_desc;
+		return xor_hw_desc->cbc & XOR_CDCR_OAC_MSK;
+	default:
+		BUG();
+	}
+	return 0;
+}
+
+/**
+ * ppc440spe_desc_get_dst_num - get the number of destination addresses in
+ * this descriptor
+ */
+static inline u32 ppc440spe_desc_get_dst_num(struct ppc440spe_adma_desc_slot *desc,
+					  struct ppc440spe_adma_chan *chan)
+{
+	struct dma_cdb *dma_hw_desc;
+
+	switch (chan->device->id) {
+	case PPC440SPE_DMA0_ID:
+	case PPC440SPE_DMA1_ID:
+		/* May be 1 or 2 destinations */
+		dma_hw_desc = desc->hw_desc;
+		switch (dma_hw_desc->opc) {
+		case DMA_CDB_OPC_NO_OP:
+		case DMA_CDB_OPC_DCHECK128:
+			return 0;
+		case DMA_CDB_OPC_MV_SG1_SG2:
+		case DMA_CDB_OPC_DFILL128:
+			return 1;
+		case DMA_CDB_OPC_MULTICAST:
+			if (desc->dst_cnt == 2)
+				return 2;
+			else
+				return 1;
+		default:
+			printk(KERN_ERR "%s: unknown OPC 0x%02x\n",
+			       __func__, dma_hw_desc->opc);
+			BUG();
+		}
+	case PPC440SPE_XOR_ID:
+		/* Always only 1 destination */
+		return 1;
+	default:
+		BUG();
+	}
+	return 0;
+}
+
+/******************************************************************************
+ * ADMA channel low-level routines
+ ******************************************************************************/
+
+static inline u32 ppc440spe_chan_get_current_descriptor(struct ppc440spe_adma_chan
+						     *chan);
+static inline void ppc440spe_dma_put_desc(struct ppc440spe_adma_chan *chan,
+				       struct ppc440spe_adma_desc_slot *desc);
+static inline void ppc440spe_xor_set_link(struct ppc440spe_adma_desc_slot *prev_desc,
+				       struct ppc440spe_adma_desc_slot *next_desc);
+static inline void print_cb_list(struct ppc440spe_adma_chan *chan,
+				 struct ppc440spe_adma_desc_slot *iter);
+/**
+ * ppc440spe_chan_append - update the h/w chain in the channel
+ */
+static inline void ppc440spe_chan_append(struct ppc440spe_adma_chan *chan)
+{
+	struct xor_regs *xor_reg;
+	struct ppc440spe_adma_desc_slot *iter;
+	struct xor_cb *xcb;
+	u32 cur_desc;
+	unsigned long flags;
+
+	local_irq_save(flags);
+
+	switch (chan->device->id) {
+	case PPC440SPE_DMA0_ID:
+	case PPC440SPE_DMA1_ID:
+		cur_desc = ppc440spe_chan_get_current_descriptor(chan);
+
+		if (likely(cur_desc)) {
+			iter = chan_last_sub[chan->device->id];
+			BUG_ON(!iter);
+		} else {
+			/* first peer */
+			iter = chan_first_cdb[chan->device->id];
+			BUG_ON(!iter);
+			ppc440spe_dma_put_desc(chan, iter);
+			chan->hw_chain_inited = 1;
+		}
+
+		/* is there something new to append */
+		if (!iter->hw_next)
+			break;
+
+		/* flush descriptors from the s/w queue to fifo */
+		list_for_each_entry_continue(iter, &chan->chain, chain_node) {
+			ppc440spe_dma_put_desc(chan, iter);
+			if (!iter->hw_next)
+				break;
+		}
+		break;
+	case PPC440SPE_XOR_ID:
+		/* update h/w links and refetch */
+		if (!xor_last_submit->hw_next)
+			break;
+
+		xor_reg = chan->device->xor_reg;
+		/* the last linked CDB has to generate an interrupt
+		 * that we'd be able to append the next lists to h/w
+		 * regardless of the XOR engine state at the moment of
+		 * appending of these next lists
+		 */
+		xcb = xor_last_linked->hw_desc;
+		xcb->cbc |= XOR_CBCR_CBCE_BIT;
+
+		if (!(ioread32be(&xor_reg->sr) & XOR_SR_XCP_BIT)) {
+			/* XORcore is idle. Refetch now */
+			do_xor_refetch = 0;
+			ppc440spe_xor_set_link(xor_last_submit,
+					    xor_last_submit->hw_next);
+
+			ADMA_LL_DBG(print_cb_list(chan,
+						  xor_last_submit->hw_next));
+
+			xor_last_submit = xor_last_linked;
+			iowrite32be(ioread32be(&xor_reg->crsr) |
+				    XOR_CRSR_RCBE_BIT | XOR_CRSR_64BA_BIT,
+				    &xor_reg->crsr);
+		} else {
+			/* XORcore is running. Refetch later in the handler */
+			do_xor_refetch = 1;
+		}
+
+		break;
+	}
+
+	local_irq_restore(flags);
+}
+
+/**
+ * ppc440spe_adma_device_clear_eot_status - interrupt ack to XOR or DMA engine
+ */
+static inline void ppc440spe_adma_device_clear_eot_status(struct ppc440spe_adma_chan
+						       *chan)
+{
+	struct dma_regs *dma_reg;
+	struct xor_regs *xor_reg;
+	u8 *p = chan->device->dma_desc_pool_virt;
+	struct dma_cdb *cdb;
+	u32 rv, i;
+
+	switch (chan->device->id) {
+	case PPC440SPE_DMA0_ID:
+	case PPC440SPE_DMA1_ID:
+		/* read FIFO to ack */
+		dma_reg = chan->device->dma_reg;
+		while ((rv = ioread32(&dma_reg->csfpl))) {
+			i = rv & DMA_CDB_ADDR_MSK;
+			cdb = (struct dma_cdb *)&p[i -
+						   (u32) chan->device->
+						   dma_desc_pool];
+
+			/* Clear opcode to ack. This is necessary for
+			 * ZeroSum operations only
+			 */
+			cdb->opc = 0;
+
+			if (test_bit(PPC440SPE_RXOR_RUN, &ppc440spe_rxor_state)) {
+				/* probably this is a completed RXOR op,
+				 * get pointer to CDB using the fact that
+				 * physical and virtual addresses of CDB
+				 * in pools have the same offsets
+				 */
+				if (le32_to_cpu(cdb->sg1u) & DMA_CUED_XOR_BASE) {
+					/* this is a RXOR */
+					clear_bit(PPC440SPE_RXOR_RUN,
+						  &ppc440spe_rxor_state);
+				}
+			}
+
+			if (rv & DMA_CDB_STATUS_MSK) {
+				/* ZeroSum check failed
+				 */
+				struct ppc440spe_adma_desc_slot *iter;
+				dma_addr_t phys = rv & ~DMA_CDB_MSK;
+
+				/*
+				 * Update the status of corresponding
+				 * descriptor.
+				 */
+				list_for_each_entry(iter, &chan->chain,
+						    chain_node) {
+					if (iter->phys == phys)
+						break;
+				}
+				/*
+				 * if cannot find the corresponding
+				 * slot it's a bug
+				 */
+				BUG_ON(&iter->chain_node == &chan->chain);
+
+				if (iter->xor_check_result) {
+					if (test_bit(PPC440SPE_DESC_PCHECK,
+						     &iter->flags)) {
+						*iter->xor_check_result |=
+						    SUM_CHECK_P_RESULT;
+					} else
+					    if (test_bit(PPC440SPE_DESC_QCHECK,
+							 &iter->flags)) {
+						*iter->xor_check_result |=
+						    SUM_CHECK_Q_RESULT;
+					} else
+						BUG();
+				}
+			}
+		}
+
+		rv = ioread32(&dma_reg->dsts);
+		if (rv) {
+			pr_err("DMA%d err status: 0x%x\n",
+			       chan->device->id, rv);
+			/* write back to clear */
+			iowrite32(rv, &dma_reg->dsts);
+		}
+		break;
+	case PPC440SPE_XOR_ID:
+		/* reset status bits to ack */
+		xor_reg = chan->device->xor_reg;
+		rv = ioread32be(&xor_reg->sr);
+		iowrite32be(rv, &xor_reg->sr);
+
+		if (rv &
+		    (XOR_IE_ICBIE_BIT | XOR_IE_ICIE_BIT | XOR_IE_RPTIE_BIT)) {
+			if (rv & XOR_IE_RPTIE_BIT) {
+				/* Read PLB Timeout Error.
+				 * Try to resubmit the CB
+				 */
+				u32 val = ioread32be(&xor_reg->ccbalr);
+
+				iowrite32be(val, &xor_reg->cblalr);
+
+				val = ioread32be(&xor_reg->crsr);
+				iowrite32be(val | XOR_CRSR_XAE_BIT,
+					    &xor_reg->crsr);
+			} else
+				pr_err("XOR ERR 0x%x status\n", rv);
+			break;
+		}
+
+		/*  if the XORcore is idle, but there are unprocessed CBs
+		 * then refetch the s/w chain here
+		 */
+		if (!(ioread32be(&xor_reg->sr) & XOR_SR_XCP_BIT) &&
+		    do_xor_refetch)
+			ppc440spe_chan_append(chan);
+		break;
+	}
+}
+
+/**
+ * ppc440spe_chan_is_busy - get the channel status
+ */
+static inline int ppc440spe_chan_is_busy(struct ppc440spe_adma_chan *chan)
+{
+	struct dma_regs *dma_reg;
+	struct xor_regs *xor_reg;
+	int busy = 0;
+
+	switch (chan->device->id) {
+	case PPC440SPE_DMA0_ID:
+	case PPC440SPE_DMA1_ID:
+		dma_reg = chan->device->dma_reg;
+		/*  if command FIFO's head and tail pointers are equal and
+		 * status tail is the same as command, then channel is free
+		 */
+		if (ioread16(&dma_reg->cpfhp) != ioread16(&dma_reg->cpftp) ||
+		    ioread16(&dma_reg->cpftp) != ioread16(&dma_reg->csftp))
+			busy = 1;
+		break;
+	case PPC440SPE_XOR_ID:
+		/* use the special status bit for the XORcore
+		 */
+		xor_reg = chan->device->xor_reg;
+		busy = (ioread32be(&xor_reg->sr) & XOR_SR_XCP_BIT) ? 1 : 0;
+		break;
+	}
+
+	return busy;
+}
+
+/**
+ * ppc440spe_chan_set_first_xor_descriptor -  init XORcore chain
+ */
+static inline void ppc440spe_chan_set_first_xor_descriptor(struct ppc440spe_adma_chan
+							*chan,
+							struct
+							ppc440spe_adma_desc_slot
+							*next_desc)
+{
+	struct xor_regs *xor_reg = chan->device->xor_reg;
+
+	if (ioread32be(&xor_reg->sr) & XOR_SR_XCP_BIT)
+		printk(KERN_INFO "%s: Warn: XORcore is running "
+		       "when try to set the first CDB!\n", __func__);
+
+	xor_last_submit = xor_last_linked = next_desc;
+
+	iowrite32be(XOR_CRSR_64BA_BIT, &xor_reg->crsr);
+
+	iowrite32be(next_desc->phys, &xor_reg->cblalr);
+	iowrite32be(0, &xor_reg->cblahr);
+	iowrite32be(ioread32be(&xor_reg->cbcr) | XOR_CBCR_LNK_BIT,
+		    &xor_reg->cbcr);
+
+	chan->hw_chain_inited = 1;
+}
+
+/**
+ * ppc440spe_chan_run - enable the channel
+ */
+static inline void ppc440spe_chan_run(struct ppc440spe_adma_chan *chan)
+{
+	struct xor_regs *xor_reg;
+
+	switch (chan->device->id) {
+	case PPC440SPE_DMA0_ID:
+	case PPC440SPE_DMA1_ID:
+		/* DMAs are always enabled, do nothing */
+		break;
+	case PPC440SPE_XOR_ID:
+		/* drain write buffer */
+		xor_reg = chan->device->xor_reg;
+
+		/* fetch descriptor pointed to in <link> */
+		iowrite32be(XOR_CRSR_64BA_BIT | XOR_CRSR_XAE_BIT,
+			    &xor_reg->crsr);
+		break;
+	}
+}
+
+/**
+ * ppc440spe_adma_device_estimate - estimate the efficiency of processing
+ *	the operation given on this channel. It's assumed that 'chan' is
+ *	capable to process 'cap' type of operation.
+ * @chan: channel to use
+ * @cap: type of transaction
+ * @dst_lst: array of destination pointers
+ * @dst_cnt: number of destination operands
+ * @src_lst: array of source pointers
+ * @src_cnt: number of source operands
+ * @src_sz: size of each source operand
+ */
+
+#define DMA_CTRL_FLAGS_LAST	DMA_PREP_FENCE
+#define DMA_PREP_ZERO_P		(DMA_CTRL_FLAGS_LAST << 1)
+#define DMA_PREP_ZERO_Q		(DMA_PREP_ZERO_P << 1)
+
+static inline void print_cb_list(struct ppc440spe_adma_chan *chan,
+				 struct ppc440spe_adma_desc_slot *iter)
+{
+	for (; iter; iter = iter->hw_next)
+		print_cb(chan, iter->hw_desc);
+}
+
+/**
+ * ppc440spe_dma_put_desc - put DMA0,1 descriptor to FIFO.
+ * called with irqs disabled
+ */
+static inline void ppc440spe_dma_put_desc(struct ppc440spe_adma_chan *chan,
+				       struct ppc440spe_adma_desc_slot *desc)
+{
+	u32 pcdb;
+	struct dma_regs *dma_reg = chan->device->dma_reg;
+
+	pcdb = desc->phys;
+	if (!test_bit(PPC440SPE_DESC_INT, &desc->flags))
+		pcdb |= DMA_CDB_NO_INT;
+
+	chan_last_sub[chan->device->id] = desc;
+
+	ADMA_LL_DBG(print_cb(chan, desc->hw_desc));
+
+	iowrite32(pcdb, &dma_reg->cpfpl);
+}
+
+/**
+ * ppc440spe_xor_set_link - set link address in xor CB
+ */
+static inline void ppc440spe_xor_set_link(struct ppc440spe_adma_desc_slot *prev_desc,
+				       struct ppc440spe_adma_desc_slot *next_desc)
+{
+	struct xor_cb *xor_hw_desc = prev_desc->hw_desc;
+
+	if (unlikely(!next_desc || !(next_desc->phys))) {
+		printk(KERN_ERR "%s: next_desc=0x%p; next_desc->phys=0x%llx\n",
+		       __func__, next_desc, next_desc ? next_desc->phys : 0);
+		BUG();
+	}
+
+	xor_hw_desc->cbs = 0;
+	xor_hw_desc->cblal = next_desc->phys;
+	xor_hw_desc->cblah = 0;
+	xor_hw_desc->cbc |= XOR_CBCR_LNK_BIT;
+}
+
+/**
+ * ppc440spe_desc_set_link - set the address of descriptor following this
+ * descriptor in chain
+ */
+static inline void ppc440spe_desc_set_link(struct ppc440spe_adma_chan *chan,
+					struct ppc440spe_adma_desc_slot *prev_desc,
+					struct ppc440spe_adma_desc_slot *next_desc)
+{
+	unsigned long flags;
+	struct ppc440spe_adma_desc_slot *tail = next_desc;
+
+	if (unlikely(!prev_desc || !next_desc ||
+		     (prev_desc->hw_next && prev_desc->hw_next != next_desc))) {
+		/* If previous next is overwritten something is wrong.
+		 * though we may refetch from append to initiate list
+		 * processing; in this case - it's ok.
+		 */
+		printk(KERN_ERR "%s: prev_desc=0x%p; next_desc=0x%p; "
+		       "prev->hw_next=0x%p\n", __func__, prev_desc,
+		       next_desc, prev_desc ? prev_desc->hw_next : 0);
+		BUG();
+	}
+
+	local_irq_save(flags);
+
+	/* do s/w chaining both for DMA and XOR descriptors */
+	prev_desc->hw_next = next_desc;
+
+	switch (chan->device->id) {
+	case PPC440SPE_DMA0_ID:
+	case PPC440SPE_DMA1_ID:
+		break;
+	case PPC440SPE_XOR_ID:
+		/* bind descriptor to the chain */
+		while (tail->hw_next)
+			tail = tail->hw_next;
+		xor_last_linked = tail;
+
+		if (prev_desc == xor_last_submit)
+			/* do not link to the last submitted CB */
+			break;
+		ppc440spe_xor_set_link(prev_desc, next_desc);
+		break;
+	}
+
+	local_irq_restore(flags);
+}
+
+/******************************************************************************
+ * CDB field manipulation routines
+ ******************************************************************************/
+/**
+ * ppc440spe_desc_set_dest_addr - set destination address into the descriptor
+ */
+static inline void ppc440spe_desc_set_dest_addr(struct ppc440spe_adma_desc_slot *desc,
+					     struct ppc440spe_adma_chan *chan,
+					     dma_addr_t addrh, dma_addr_t addrl,
+					     u32 dst_idx)
+{
+	struct dma_cdb *dma_hw_desc;
+	struct xor_cb *xor_hw_desc;
+	phys_addr_t addr64, tmphi, tmplow;
+	u32 *psgu, *psgl;
+
+	switch (chan->device->id) {
+	case PPC440SPE_DMA0_ID:
+	case PPC440SPE_DMA1_ID:
+		if (!addrh) {
+			addr64 = addrl;
+			tmphi = (addr64 >> 32);
+			tmplow = (addr64 & 0xFFFFFFFF);
+		} else {
+			tmphi = addrh;
+			tmplow = addrl;
+		}
+		dma_hw_desc = desc->hw_desc;
+
+		psgu = dst_idx ? &dma_hw_desc->sg3u : &dma_hw_desc->sg2u;
+		psgl = dst_idx ? &dma_hw_desc->sg3l : &dma_hw_desc->sg2l;
+
+		*psgl = cpu_to_le32((u32) tmplow);
+		*psgu |= cpu_to_le32((u32) tmphi);
+		break;
+	case PPC440SPE_XOR_ID:
+		xor_hw_desc = desc->hw_desc;
+		xor_hw_desc->cbtal = addrl;
+		xor_hw_desc->cbtah |= addrh;
+		break;
+	}
+}
+
+/**
+ * ppc440spe_desc_set_src_addr - set source address into the descriptor
+ */
+static inline void ppc440spe_desc_set_src_addr(struct ppc440spe_adma_desc_slot *desc,
+					    struct ppc440spe_adma_chan *chan,
+					    int src_idx, dma_addr_t addrh,
+					    dma_addr_t addrl)
+{
+	struct dma_cdb *dma_hw_desc;
+	struct xor_cb *xor_hw_desc;
+	phys_addr_t addr64, tmplow, tmphi;
+
+	switch (chan->device->id) {
+	case PPC440SPE_DMA0_ID:
+	case PPC440SPE_DMA1_ID:
+		if (!addrh) {
+			addr64 = addrl;
+			tmphi = (addr64 >> 32);
+			tmplow = (addr64 & 0xFFFFFFFF);
+		} else {
+			tmphi = addrh;
+			tmplow = addrl;
+		}
+		dma_hw_desc = desc->hw_desc;
+		dma_hw_desc->sg1l = cpu_to_le32((u32) tmplow);
+		dma_hw_desc->sg1u |= cpu_to_le32((u32) tmphi);
+		break;
+	case PPC440SPE_XOR_ID:
+		xor_hw_desc = desc->hw_desc;
+		xor_hw_desc->ops[src_idx].l = addrl;
+		xor_hw_desc->ops[src_idx].h |= addrh;
+		break;
+	}
+}
+
+/**
+ * ppc440spe_desc_set_src_mult - set source address mult into the descriptor
+ */
+static inline void ppc440spe_desc_set_src_mult(struct ppc440spe_adma_desc_slot *desc,
+					    struct ppc440spe_adma_chan *chan,
+					    u32 mult_index, int sg_index,
+					    unsigned char mult_value)
+{
+	struct dma_cdb *dma_hw_desc;
+	struct xor_cb *xor_hw_desc;
+	u32 *psgu;
+
+	switch (chan->device->id) {
+	case PPC440SPE_DMA0_ID:
+	case PPC440SPE_DMA1_ID:
+		dma_hw_desc = desc->hw_desc;
+
+		switch (sg_index) {
+			/* for RXOR operations set multiplier
+			 * into source cued address
+			 */
+		case DMA_CDB_SG_SRC:
+			psgu = &dma_hw_desc->sg1u;
+			break;
+			/* for WXOR operations set multiplier
+			 * into destination cued address(es)
+			 */
+		case DMA_CDB_SG_DST1:
+			psgu = &dma_hw_desc->sg2u;
+			break;
+		case DMA_CDB_SG_DST2:
+			psgu = &dma_hw_desc->sg3u;
+			break;
+		default:
+			BUG();
+		}
+
+		*psgu |= cpu_to_le32(mult_value << mult_index);
+		break;
+	case PPC440SPE_XOR_ID:
+		xor_hw_desc = desc->hw_desc;
+		break;
+	default:
+		BUG();
+	}
+}
+
+/******************************************************************************
+ * ADMA channel low-level routines
+ ******************************************************************************/
+
+static void ppc440spe_adma_device_clear_eot_status(struct ppc440spe_adma_chan *chan);
+static inline int ppc440spe_adma_dma2rxor_prep_src(struct ppc440spe_adma_desc_slot
+						*hdesc,
+						struct ppc440spe_rxor *cursor,
+						int index, int src_cnt,
+						u32 addr);
+
+static int ppc440spe_chan_is_busy(struct ppc440spe_adma_chan *chan);
+static void ppc440spe_chan_set_first_xor_descriptor(struct ppc440spe_adma_chan *chan, struct ppc440spe_adma_desc_slot
+						 *next_desc);
+/**
+ * ppc440spe_chan_get_current_descriptor - get the currently executed descriptor
+ */
+static inline u32 ppc440spe_chan_get_current_descriptor(struct ppc440spe_adma_chan
+						     *chan)
+{
+	struct dma_regs *dma_reg;
+	struct xor_regs *xor_reg;
+
+	if (unlikely(!chan->hw_chain_inited))
+		/* h/w descriptor chain is not initialized yet */
+		return 0;
+
+	switch (chan->device->id) {
+	case PPC440SPE_DMA0_ID:
+	case PPC440SPE_DMA1_ID:
+		dma_reg = chan->device->dma_reg;
+		return ioread32(&dma_reg->acpl) & (~DMA_CDB_MSK);
+	case PPC440SPE_XOR_ID:
+		xor_reg = chan->device->xor_reg;
+		return ioread32be(&xor_reg->ccbalr);
+	}
+	return 0;
+}
+
+static void ppc440spe_chan_run(struct ppc440spe_adma_chan *chan);
+/**
+ * ppc440spe_adma_clean_slot - clean up CDB slot (if ack is set)
+ */
+static inline int ppc440spe_adma_clean_slot(struct ppc440spe_adma_desc_slot *desc,
+					 struct ppc440spe_adma_chan *chan)
+{
+	/* the client is allowed to attach dependent operations
+	 * until 'ack' is set
+	 */
+	if (!async_tx_test_ack(&desc->async_tx))
+		return 0;
+
+	/* leave the last descriptor in the chain
+	 * so we can append to it
+	 */
+	if (list_is_last(&desc->chain_node, &chan->chain) ||
+	    desc->phys == ppc440spe_chan_get_current_descriptor(chan))
+		return 1;
+
+	if (chan->device->id != PPC440SPE_XOR_ID) {
+		/* our DMA interrupt handler clears opc field of
+		 * each processed descriptor. For all types of
+		 * operations except for ZeroSum we do not actually
+		 * need ack from the interrupt handler. ZeroSum is a
+		 * special case since the result of this operation
+		 * is available from the handler only, so if we see
+		 * such type of descriptor (which is unprocessed yet)
+		 * then leave it in chain.
+		 */
+		struct dma_cdb *cdb = desc->hw_desc;
+		if (cdb->opc == DMA_CDB_OPC_DCHECK128)
+			return 1;
+	}
+
+	dev_dbg(chan->device->common.dev, "\tfree slot %llx: %d stride: %d\n",
+		desc->phys, desc->idx, desc->slots_per_op);
+
+	list_del(&desc->chain_node);
+	ppc440spe_adma_free_slots(desc, chan);
+	return 0;
+}
+
+/**
+ * ppc440spe_rxor_set_region_data -
+ */
+static inline void ppc440spe_rxor_set_region(struct ppc440spe_adma_desc_slot *desc,
+					  u8 xor_arg_no, u32 mask)
+{
+	struct xor_cb *xcb = desc->hw_desc;
+
+	xcb->ops[xor_arg_no].h |= mask;
+}
+
+/**
+ * ppc440spe_rxor_set_src -
+ */
+static inline void ppc440spe_rxor_set_src(struct ppc440spe_adma_desc_slot *desc,
+				       u8 xor_arg_no, dma_addr_t addr)
+{
+	struct xor_cb *xcb = desc->hw_desc;
+
+	xcb->ops[xor_arg_no].h |= DMA_CUED_XOR_BASE;
+	xcb->ops[xor_arg_no].l = addr;
+}
+
+/**
+ * ppc440spe_rxor_set_mult -
+ */
+static inline void ppc440spe_rxor_set_mult(struct ppc440spe_adma_desc_slot *desc,
+					u8 xor_arg_no, u8 idx, u8 mult)
+{
+	struct xor_cb *xcb = desc->hw_desc;
+
+	xcb->ops[xor_arg_no].h |= mult << (DMA_CUED_MULT1_OFF + idx * 8);
+}
+
+/**
+ * ppc440spe_adma_dma2rxor_set_src - set RXOR source address; it's assumed that
+ *	ppc440spe_adma_dma2rxor_prep_src() has already done prior this call
+ */
+static inline void ppc440spe_adma_dma2rxor_set_src(struct ppc440spe_adma_desc_slot
+						*desc, int index,
+						dma_addr_t addr)
+{
+	struct xor_cb *xcb = desc->hw_desc;
+	int k = 0, op = 0, lop = 0;
+
+	/* get the RXOR operand which corresponds to index addr */
+	while (op <= index) {
+		lop = op;
+		if (k == XOR_MAX_OPS) {
+			k = 0;
+			desc = list_entry(desc->chain_node.next,
+					  struct ppc440spe_adma_desc_slot,
+					  chain_node);
+			xcb = desc->hw_desc;
+
+		}
+		if ((xcb->ops[k++].h & (DMA_RXOR12 << DMA_CUED_REGION_OFF)) ==
+		    (DMA_RXOR12 << DMA_CUED_REGION_OFF))
+			op += 2;
+		else
+			op += 3;
+	}
+
+	BUG_ON(k < 1);
+
+	if (test_bit(k - 1, desc->reverse_flags)) {
+		/* reverse operand order; put last op in RXOR group */
+		if (index == op - 1)
+			ppc440spe_rxor_set_src(desc, k - 1, addr);
+	} else {
+		/* direct operand order; put first op in RXOR group */
+		if (index == lop)
+			ppc440spe_rxor_set_src(desc, k - 1, addr);
+	}
+}
+
+/**
+ * ppc440spe_adma_pq_set_src - set source address into descriptor
+ */
+static inline void ppc440spe_adma_pq_set_src(struct ppc440spe_adma_desc_slot *sw_desc,
+					  dma_addr_t addr, int index)
+{
+	struct ppc440spe_adma_chan *chan;
+	dma_addr_t haddr = 0;
+	struct ppc440spe_adma_desc_slot *iter = NULL;
+
+	chan = to_ppc440spe_adma_chan(sw_desc->async_tx.chan);
+
+	switch (chan->device->id) {
+	case PPC440SPE_DMA0_ID:
+	case PPC440SPE_DMA1_ID:
+		/* DMA0,1 may do: WXOR, RXOR, RXOR+WXORs chain
+		 */
+		if (test_bit(PPC440SPE_DESC_RXOR, &sw_desc->flags)) {
+			/* RXOR-only or RXOR/WXOR operation */
+			int iskip = test_bit(PPC440SPE_DESC_RXOR12,
+					     &sw_desc->flags) ? 2 : 3;
+
+			if (index == 0) {
+				/* 1st slot (RXOR) */
+				/* setup sources region (R1-2-3, R1-2-4,
+				 * or R1-2-5)
+				 */
+				if (test_bit(PPC440SPE_DESC_RXOR12,
+					     &sw_desc->flags))
+					haddr = DMA_RXOR12 <<
+					    DMA_CUED_REGION_OFF;
+				else if (test_bit(PPC440SPE_DESC_RXOR123,
+						  &sw_desc->flags))
+					haddr = DMA_RXOR123 <<
+					    DMA_CUED_REGION_OFF;
+				else if (test_bit(PPC440SPE_DESC_RXOR124,
+						  &sw_desc->flags))
+					haddr = DMA_RXOR124 <<
+					    DMA_CUED_REGION_OFF;
+				else if (test_bit(PPC440SPE_DESC_RXOR125,
+						  &sw_desc->flags))
+					haddr = DMA_RXOR125 <<
+					    DMA_CUED_REGION_OFF;
+				else
+					BUG();
+				haddr |= DMA_CUED_XOR_BASE;
+				iter = ppc440spe_get_group_entry(sw_desc, 0);
+			} else if (index < iskip) {
+				/* 1st slot (RXOR)
+				 * shall actually set source address only once
+				 * instead of first <iskip>
+				 */
+				iter = NULL;
+			} else {
+				/* 2nd/3d and next slots (WXOR);
+				 * skip first slot with RXOR
+				 */
+				haddr = DMA_CUED_XOR_HB;
+				iter = ppc440spe_get_group_entry(sw_desc,
+							      index - iskip +
+							      sw_desc->dst_cnt);
+			}
+		} else {
+			int znum = 0;
+
+			/* WXOR-only operation; skip first slots with
+			 * zeroing destinations
+			 */
+			if (test_bit(PPC440SPE_ZERO_P, &sw_desc->flags))
+				znum++;
+			if (test_bit(PPC440SPE_ZERO_Q, &sw_desc->flags))
+				znum++;
+
+			haddr = DMA_CUED_XOR_HB;
+			iter = ppc440spe_get_group_entry(sw_desc, index + znum);
+		}
+
+		if (likely(iter)) {
+			ppc440spe_desc_set_src_addr(iter, chan, 0, haddr, addr);
+
+			if (!index &&
+			    test_bit(PPC440SPE_DESC_RXOR, &sw_desc->flags) &&
+			    sw_desc->dst_cnt == 2) {
+				/* if we have two destinations for RXOR, then
+				 * setup source in the second descr too
+				 */
+				iter = ppc440spe_get_group_entry(sw_desc, 1);
+				ppc440spe_desc_set_src_addr(iter, chan, 0,
+							 haddr, addr);
+			}
+		}
+		break;
+
+	case PPC440SPE_XOR_ID:
+		/* DMA2 may do Biskup */
+		iter = sw_desc->group_head;
+		if (iter->dst_cnt == 2) {
+			/* both P & Q calculations required; set P src here */
+			ppc440spe_adma_dma2rxor_set_src(iter, index, addr);
+
+			/* this is for Q */
+			iter = ppc440spe_get_group_entry(sw_desc,
+						      sw_desc->descs_per_op);
+		}
+		ppc440spe_adma_dma2rxor_set_src(iter, index, addr);
+		break;
+	}
+}
+
+/**
+ * ppc440spe_adma_dma2rxor_set_mult - set RXOR multipliers; it's assumed that
+ *	ppc440spe_adma_dma2rxor_prep_src() has already done prior this call
+ */
+static inline void ppc440spe_adma_dma2rxor_set_mult(struct ppc440spe_adma_desc_slot
+						 *desc, int index, u8 mult)
+{
+	struct xor_cb *xcb = desc->hw_desc;
+	int k = 0, op = 0, lop = 0;
+
+	/* get the RXOR operand which corresponds to index mult */
+	while (op <= index) {
+		lop = op;
+		if (k == XOR_MAX_OPS) {
+			k = 0;
+			desc = list_entry(desc->chain_node.next,
+					  struct ppc440spe_adma_desc_slot,
+					  chain_node);
+			xcb = desc->hw_desc;
+
+		}
+		if ((xcb->ops[k++].h & (DMA_RXOR12 << DMA_CUED_REGION_OFF)) ==
+		    (DMA_RXOR12 << DMA_CUED_REGION_OFF))
+			op += 2;
+		else
+			op += 3;
+	}
+
+	BUG_ON(k < 1);
+	if (test_bit(k - 1, desc->reverse_flags)) {
+		/* reverse order */
+		ppc440spe_rxor_set_mult(desc, k - 1, op - index - 1, mult);
+	} else {
+		/* direct order */
+		ppc440spe_rxor_set_mult(desc, k - 1, index - lop, mult);
+	}
+}
+
+/**
+ * ppc440spe_adma_pq_set_src_mult - set multiplication coefficient into
+ * descriptor for the PQXOR operation
+ */
+static inline void ppc440spe_adma_pq_set_src_mult(struct ppc440spe_adma_desc_slot
+					       *sw_desc, unsigned char mult,
+					       int index, int dst_pos)
+{
+	struct ppc440spe_adma_chan *chan;
+	u32 mult_idx, mult_dst;
+	struct ppc440spe_adma_desc_slot *iter = NULL, *iter1 = NULL;
+
+	chan = to_ppc440spe_adma_chan(sw_desc->async_tx.chan);
+
+	switch (chan->device->id) {
+	case PPC440SPE_DMA0_ID:
+	case PPC440SPE_DMA1_ID:
+		if (test_bit(PPC440SPE_DESC_RXOR, &sw_desc->flags)) {
+			int region = test_bit(PPC440SPE_DESC_RXOR12,
+					      &sw_desc->flags) ? 2 : 3;
+
+			if (index < region) {
+				/* RXOR multipliers */
+				iter = ppc440spe_get_group_entry(sw_desc,
+							      sw_desc->
+							      dst_cnt - 1);
+				if (sw_desc->dst_cnt == 2)
+					iter1 =
+					    ppc440spe_get_group_entry(sw_desc, 0);
+
+				mult_idx = DMA_CUED_MULT1_OFF + (index << 3);
+				mult_dst = DMA_CDB_SG_SRC;
+			} else {
+				/* WXOR multiplier */
+				iter = ppc440spe_get_group_entry(sw_desc,
+							      index -
+							      region +
+							      sw_desc->dst_cnt);
+				mult_idx = DMA_CUED_MULT1_OFF;
+				mult_dst = dst_pos ? DMA_CDB_SG_DST2 :
+				    DMA_CDB_SG_DST1;
+			}
+		} else {
+			int znum = 0;
+
+			/* WXOR-only;
+			 * skip first slots with destinations (if ZERO_DST has
+			 * place)
+			 */
+			if (test_bit(PPC440SPE_ZERO_P, &sw_desc->flags))
+				znum++;
+			if (test_bit(PPC440SPE_ZERO_Q, &sw_desc->flags))
+				znum++;
+
+			iter = ppc440spe_get_group_entry(sw_desc, index + znum);
+			mult_idx = DMA_CUED_MULT1_OFF;
+			mult_dst = dst_pos ? DMA_CDB_SG_DST2 : DMA_CDB_SG_DST1;
+		}
+
+		if (likely(iter)) {
+			ppc440spe_desc_set_src_mult(iter, chan,
+						 mult_idx, mult_dst, mult);
+
+			if (unlikely(iter1)) {
+				/* if we have two destinations for RXOR, then
+				 * we've just set Q mult. Set-up P now.
+				 */
+				ppc440spe_desc_set_src_mult(iter1, chan,
+							 mult_idx, mult_dst, 1);
+			}
+
+		}
+		break;
+
+	case PPC440SPE_XOR_ID:
+		iter = sw_desc->group_head;
+		if (sw_desc->dst_cnt == 2) {
+			/* both P & Q calculations required; set P mult here */
+			ppc440spe_adma_dma2rxor_set_mult(iter, index, 1);
+
+			/* and then set Q mult */
+			iter = ppc440spe_get_group_entry(sw_desc,
+						      sw_desc->descs_per_op);
+		}
+		ppc440spe_adma_dma2rxor_set_mult(iter, index, mult);
+		break;
+	}
+}
+
+/**
+ * ppc440spe_adma_pq_zero_sum_set_dest - set destination address into descriptor
+ * for the PQ_ZERO_SUM operation
+ */
+static inline void ppc440spe_adma_pqzero_sum_set_dest(struct ppc440spe_adma_desc_slot
+						   *sw_desc, dma_addr_t paddr,
+						   dma_addr_t qaddr)
+{
+	struct ppc440spe_adma_desc_slot *iter, *end;
+	struct ppc440spe_adma_chan *chan;
+	dma_addr_t addr = 0;
+	int idx;
+
+	chan = to_ppc440spe_adma_chan(sw_desc->async_tx.chan);
+
+	/* walk through the WXOR source list and set P/Q-destinations
+	 * for each slot
+	 */
+	idx = (paddr && qaddr) ? 2 : 1;
+	/* set end */
+	list_for_each_entry_reverse(end, &sw_desc->group_list, chain_node) {
+		if (!(--idx))
+			break;
+	}
+	/* set start */
+	idx = (paddr && qaddr) ? 2 : 1;
+	iter = ppc440spe_get_group_entry(sw_desc, idx);
+
+	if (paddr && qaddr) {
+		/* two destinations */
+		list_for_each_entry_from(iter, &sw_desc->group_list, chain_node) {
+			if (unlikely(iter == end))
+				break;
+			ppc440spe_desc_set_dest_addr(iter, chan,
+						  DMA_CUED_XOR_BASE, paddr, 0);
+			ppc440spe_desc_set_dest_addr(iter, chan,
+						  DMA_CUED_XOR_BASE, qaddr, 1);
+		}
+	} else {
+		/* one destination */
+		addr = paddr ? paddr : qaddr;
+		list_for_each_entry_from(iter, &sw_desc->group_list, chain_node) {
+			if (unlikely(iter == end))
+				break;
+			ppc440spe_desc_set_dest_addr(iter, chan,
+						  DMA_CUED_XOR_BASE, addr, 0);
+		}
+	}
+
+	/*  The remaining descriptors are DATACHECK. These have no need in
+	 * destination. Actually, these destinations are used there
+	 * as sources for check operation. So, set addr as source.
+	 */
+	ppc440spe_desc_set_src_addr(end, chan, 0, 0, addr ? addr : paddr);
+
+	if (!addr) {
+		end = list_entry(end->chain_node.next,
+				 struct ppc440spe_adma_desc_slot, chain_node);
+		ppc440spe_desc_set_src_addr(end, chan, 0, 0, qaddr);
+	}
+}
+
+static inline void ppc440spe_adma_pq_zero_op(struct ppc440spe_adma_desc_slot *iter,
+					  struct ppc440spe_adma_chan *chan,
+					  dma_addr_t addr)
+{
+	/*  To clear destinations update the descriptor
+	 * (P or Q depending on index) as follows:
+	 * addr is destination (0 corresponds to SG2):
+	 */
+	ppc440spe_desc_set_dest_addr(iter, chan, DMA_CUED_XOR_BASE, addr, 0);
+
+	/* ... and the addr is source: */
+	ppc440spe_desc_set_src_addr(iter, chan, 0, DMA_CUED_XOR_HB, addr);
+
+	/* addr is always SG2 then the mult is always DST1 */
+	ppc440spe_desc_set_src_mult(iter, chan, DMA_CUED_MULT1_OFF,
+				 DMA_CDB_SG_DST1, 1);
+}
+
+/**
+ * ppc440spe_adma_pq_set_dest - set destination address into descriptor
+ * for the PQXOR operation
+ */
+static inline void ppc440spe_adma_pq_set_dest(struct ppc440spe_adma_desc_slot
+					   *sw_desc, dma_addr_t * addrs,
+					   unsigned long flags)
+{
+	struct ppc440spe_adma_desc_slot *iter;
+	struct ppc440spe_adma_chan *chan;
+	dma_addr_t paddr, qaddr;
+	dma_addr_t addr = 0, ppath, qpath;
+	int index = 0, i;
+
+	chan = to_ppc440spe_adma_chan(sw_desc->async_tx.chan);
+
+	if (flags & DMA_PREP_PQ_DISABLE_P)
+		paddr = 0;
+	else
+		paddr = addrs[0];
+
+	if (flags & DMA_PREP_PQ_DISABLE_Q)
+		qaddr = 0;
+	else
+		qaddr = addrs[1];
+
+	if (!paddr || !qaddr)
+		addr = paddr ? paddr : qaddr;
+
+	switch (chan->device->id) {
+	case PPC440SPE_DMA0_ID:
+	case PPC440SPE_DMA1_ID:
+		/* walk through the WXOR source list and set P/Q-destinations
+		 * for each slot:
+		 */
+		if (!test_bit(PPC440SPE_DESC_RXOR, &sw_desc->flags)) {
+			/* This is WXOR-only chain; may have 1/2 zero descs */
+			if (test_bit(PPC440SPE_ZERO_P, &sw_desc->flags))
+				index++;
+			if (test_bit(PPC440SPE_ZERO_Q, &sw_desc->flags))
+				index++;
+
+			iter = ppc440spe_get_group_entry(sw_desc, index);
+			if (addr) {
+				/* one destination */
+				list_for_each_entry_from(iter,
+							 &sw_desc->group_list,
+							 chain_node)
+				    ppc440spe_desc_set_dest_addr(iter, chan,
+							      DMA_CUED_XOR_BASE,
+							      addr, 0);
+			} else {
+				/* two destinations */
+				list_for_each_entry_from(iter,
+							 &sw_desc->group_list,
+							 chain_node) {
+					ppc440spe_desc_set_dest_addr(iter, chan,
+								  DMA_CUED_XOR_BASE,
+								  paddr, 0);
+					ppc440spe_desc_set_dest_addr(iter, chan,
+								  DMA_CUED_XOR_BASE,
+								  qaddr, 1);
+				}
+			}
+
+			if (index) {
+				/*  To clear destinations update the descriptor
+				 * (1st,2nd, or both depending on flags)
+				 */
+				index = 0;
+				if (test_bit(PPC440SPE_ZERO_P, &sw_desc->flags)) {
+					iter =
+					    ppc440spe_get_group_entry(sw_desc,
+								   index++);
+					ppc440spe_adma_pq_zero_op(iter, chan,
+							       paddr);
+				}
+
+				if (test_bit(PPC440SPE_ZERO_Q, &sw_desc->flags)) {
+					iter =
+					    ppc440spe_get_group_entry(sw_desc,
+								   index++);
+					ppc440spe_adma_pq_zero_op(iter, chan,
+							       qaddr);
+				}
+
+				return;
+			}
+		} else {
+			/* This is RXOR-only or RXOR/WXOR mixed chain */
+
+			/* If we want to include destination into calculations,
+			 * then make dest addresses cued with mult=1 (XOR).
+			 */
+			ppath = test_bit(PPC440SPE_ZERO_P, &sw_desc->flags) ?
+			    DMA_CUED_XOR_HB :
+			    DMA_CUED_XOR_BASE | (1 << DMA_CUED_MULT1_OFF);
+			qpath = test_bit(PPC440SPE_ZERO_Q, &sw_desc->flags) ?
+			    DMA_CUED_XOR_HB :
+			    DMA_CUED_XOR_BASE | (1 << DMA_CUED_MULT1_OFF);
+
+			/* Setup destination(s) in RXOR slot(s) */
+			iter = ppc440spe_get_group_entry(sw_desc, index++);
+			ppc440spe_desc_set_dest_addr(iter, chan,
+						  paddr ? ppath : qpath,
+						  paddr ? paddr : qaddr, 0);
+			if (!addr) {
+				/* two destinations */
+				iter = ppc440spe_get_group_entry(sw_desc, index++);
+				ppc440spe_desc_set_dest_addr(iter, chan,
+							  qpath, qaddr, 0);
+			}
+
+			if (test_bit(PPC440SPE_DESC_WXOR, &sw_desc->flags)) {
+				/* Setup destination(s) in remaining WXOR
+				 * slots
+				 */
+				iter = ppc440spe_get_group_entry(sw_desc, index);
+				if (addr) {
+					/* one destination */
+					list_for_each_entry_from(iter,
+								 &sw_desc->
+								 group_list,
+								 chain_node)
+					    ppc440spe_desc_set_dest_addr(iter,
+								      chan,
+								      DMA_CUED_XOR_BASE,
+								      addr, 0);
+
+				} else {
+					/* two destinations */
+					list_for_each_entry_from(iter,
+								 &sw_desc->
+								 group_list,
+								 chain_node) {
+						ppc440spe_desc_set_dest_addr
+						    (iter, chan,
+						     DMA_CUED_XOR_BASE, paddr,
+						     0);
+						ppc440spe_desc_set_dest_addr
+						    (iter, chan,
+						     DMA_CUED_XOR_BASE, qaddr,
+						     1);
+					}
+				}
+			}
+
+		}
+		break;
+
+	case PPC440SPE_XOR_ID:
+		/* DMA2 descriptors have only 1 destination, so there are
+		 * two chains - one for each dest.
+		 * If we want to include destination into calculations,
+		 * then make dest addresses cued with mult=1 (XOR).
+		 */
+		ppath = test_bit(PPC440SPE_ZERO_P, &sw_desc->flags) ?
+		    DMA_CUED_XOR_HB :
+		    DMA_CUED_XOR_BASE | (1 << DMA_CUED_MULT1_OFF);
+
+		qpath = test_bit(PPC440SPE_ZERO_Q, &sw_desc->flags) ?
+		    DMA_CUED_XOR_HB :
+		    DMA_CUED_XOR_BASE | (1 << DMA_CUED_MULT1_OFF);
+
+		iter = ppc440spe_get_group_entry(sw_desc, 0);
+		for (i = 0; i < sw_desc->descs_per_op; i++) {
+			ppc440spe_desc_set_dest_addr(iter, chan,
+						  paddr ? ppath : qpath,
+						  paddr ? paddr : qaddr, 0);
+			iter = list_entry(iter->chain_node.next,
+					  struct ppc440spe_adma_desc_slot,
+					  chain_node);
+		}
+
+		if (!addr) {
+			/* Two destinations; setup Q here */
+			iter = ppc440spe_get_group_entry(sw_desc,
+						      sw_desc->descs_per_op);
+			for (i = 0; i < sw_desc->descs_per_op; i++) {
+				ppc440spe_desc_set_dest_addr(iter,
+							  chan, qpath, qaddr,
+							  0);
+				iter =
+				    list_entry(iter->chain_node.next,
+					       struct ppc440spe_adma_desc_slot,
+					       chain_node);
+			}
+		}
+
+		break;
+	}
+}
+
+/**
+ * ppc440spe_dma2_pq_slot_count - get the number of slots necessary for
+ * DMA2 PQ operation
+ */
+static inline int ppc440spe_dma2_pq_slot_count(dma_addr_t * srcs, int src_cnt,
+					    size_t len)
+{
+	signed long long order = 0;
+	int state = 0;
+	int addr_count = 0;
+	int i;
+	for (i = 1; i < src_cnt; i++) {
+		dma_addr_t cur_addr = srcs[i];
+		dma_addr_t old_addr = srcs[i - 1];
+		switch (state) {
+		case 0:
+			if (cur_addr == old_addr + len) {
+				/* direct RXOR */
+				order = 1;
+				state = 1;
+				if (i == src_cnt - 1)
+					addr_count++;
+			} else if (old_addr == cur_addr + len) {
+				/* reverse RXOR */
+				order = -1;
+				state = 1;
+				if (i == src_cnt - 1)
+					addr_count++;
+			} else {
+				state = 3;
+			}
+			break;
+		case 1:
+			if (i == src_cnt - 2 || (order == -1
+						 && cur_addr !=
+						 old_addr - len)) {
+				order = 0;
+				state = 0;
+				addr_count++;
+			} else if (cur_addr == old_addr + len * order) {
+				state = 2;
+				if (i == src_cnt - 1)
+					addr_count++;
+			} else if (cur_addr == old_addr + 2 * len) {
+				state = 2;
+				if (i == src_cnt - 1)
+					addr_count++;
+			} else if (cur_addr == old_addr + 3 * len) {
+				state = 2;
+				if (i == src_cnt - 1)
+					addr_count++;
+			} else {
+				order = 0;
+				state = 0;
+				addr_count++;
+			}
+			break;
+		case 2:
+			order = 0;
+			state = 0;
+			addr_count++;
+			break;
+		}
+		if (state == 3)
+			break;
+	}
+	if (src_cnt <= 1 || (state != 1 && state != 2)) {
+		pr_err("%s: src_cnt=%d, state=%d, addr_count=%d, order=%lld\n",
+		       __func__, src_cnt, state, addr_count, order);
+		for (i = 0; i < src_cnt; i++)
+			pr_err("\t[%d] 0x%llx \n", i, srcs[i]);
+		BUG();
+	}
+
+	return (addr_count + XOR_MAX_OPS - 1) / XOR_MAX_OPS;
+}
+
+/**
+ * ppc440spe_adma_set_dest - set destination address into descriptor
+ */
+static inline void ppc440spe_adma_set_dest(struct ppc440spe_adma_desc_slot *sw_desc,
+					dma_addr_t addr, int index)
+{
+	struct ppc440spe_adma_chan *chan;
+
+	BUG_ON(index >= sw_desc->dst_cnt);
+
+	chan = to_ppc440spe_adma_chan(sw_desc->async_tx.chan);
+
+	switch (chan->device->id) {
+	case PPC440SPE_DMA0_ID:
+	case PPC440SPE_DMA1_ID:
+		/* to do: support transfers lengths >
+		 * ppc440spe_adma_DMA/XOR_MAX_BYTE_COUNT
+		 */
+		ppc440spe_desc_set_dest_addr(sw_desc->group_head,
+					  chan, 0, addr, index);
+		break;
+	case PPC440SPE_XOR_ID:
+		sw_desc = ppc440spe_get_group_entry(sw_desc, index);
+		ppc440spe_desc_set_dest_addr(sw_desc, chan, 0, addr, index);
+		break;
+	}
+}
+
+/**
+ * ppc440spe_desc_set_xor_src_cnt - set source count into descriptor
+ */
+static inline void ppc440spe_desc_set_xor_src_cnt(struct ppc440spe_adma_desc_slot
+					       *desc, int src_cnt)
+{
+	struct xor_cb *hw_desc = desc->hw_desc;
+
+	hw_desc->cbc &= ~XOR_CDCR_OAC_MSK;
+	hw_desc->cbc |= src_cnt;
+}
+
+/**
+ * ppc440spe_adma_memcpy_xor_set_src - set source address into descriptor
+ */
+static inline void ppc440spe_adma_memcpy_xor_set_src(struct ppc440spe_adma_desc_slot
+						  *sw_desc, dma_addr_t addr,
+						  int index)
+{
+	struct ppc440spe_adma_chan *chan;
+
+	chan = to_ppc440spe_adma_chan(sw_desc->async_tx.chan);
+	sw_desc = sw_desc->group_head;
+
+	if (likely(sw_desc))
+		ppc440spe_desc_set_src_addr(sw_desc, chan, index, 0, addr);
+}
+
+/**
+ * ppc440spe_adma_dma2rxor_inc_addr  -
+ */
+static inline void ppc440spe_adma_dma2rxor_inc_addr(struct ppc440spe_adma_desc_slot
+						 *desc,
+						 struct ppc440spe_rxor *cursor,
+						 int index, int src_cnt)
+{
+	cursor->addr_count++;
+	if (index == src_cnt - 1) {
+		ppc440spe_desc_set_xor_src_cnt(desc, cursor->addr_count);
+	} else if (cursor->addr_count == XOR_MAX_OPS) {
+		ppc440spe_desc_set_xor_src_cnt(desc, cursor->addr_count);
+		cursor->addr_count = 0;
+		cursor->desc_count++;
+	}
+}
+
+/**
+ * ppc440spe_adma_dma2rxor_prep_src - setup RXOR types in DMA2 CDB
+ */
+static inline int ppc440spe_adma_dma2rxor_prep_src(struct ppc440spe_adma_desc_slot
+						*hdesc,
+						struct ppc440spe_rxor *cursor,
+						int index, int src_cnt,
+						u32 addr)
+{
+	int rval = 0;
+	u32 sign;
+	struct ppc440spe_adma_desc_slot *desc = hdesc;
+	int i;
+
+	for (i = 0; i < cursor->desc_count; i++) {
+		desc = list_entry(hdesc->chain_node.next,
+				  struct ppc440spe_adma_desc_slot, chain_node);
+	}
+
+	switch (cursor->state) {
+	case 0:
+		if (addr == cursor->addrl + cursor->len) {
+			/* direct RXOR */
+			cursor->state = 1;
+			cursor->xor_count++;
+			if (index == src_cnt - 1) {
+				ppc440spe_rxor_set_region(desc,
+						       cursor->addr_count,
+						       DMA_RXOR12 <<
+						       DMA_CUED_REGION_OFF);
+				ppc440spe_adma_dma2rxor_inc_addr(desc, cursor,
+							      index, src_cnt);
+			}
+		} else if (cursor->addrl == addr + cursor->len) {
+			/* reverse RXOR */
+			cursor->state = 1;
+			cursor->xor_count++;
+			set_bit(cursor->addr_count, &desc->reverse_flags[0]);
+			if (index == src_cnt - 1) {
+				ppc440spe_rxor_set_region(desc,
+						       cursor->addr_count,
+						       DMA_RXOR12 <<
+						       DMA_CUED_REGION_OFF);
+				ppc440spe_adma_dma2rxor_inc_addr(desc, cursor,
+							      index, src_cnt);
+			}
+		} else {
+			printk(KERN_ERR "Cannot build "
+			       "DMA2 RXOR command block.\n");
+			BUG();
+		}
+		break;
+	case 1:
+		sign = test_bit(cursor->addr_count, desc->reverse_flags)
+		    ? -1 : 1;
+		if (index == src_cnt - 2 || (sign == -1
+					     && addr !=
+					     cursor->addrl - 2 * cursor->len)) {
+			cursor->state = 0;
+			cursor->xor_count = 1;
+			cursor->addrl = addr;
+			ppc440spe_rxor_set_region(desc,
+					       cursor->addr_count,
+					       DMA_RXOR12 <<
+					       DMA_CUED_REGION_OFF);
+			ppc440spe_adma_dma2rxor_inc_addr(desc, cursor, index,
+						      src_cnt);
+		} else if (addr == cursor->addrl + 2 * sign * cursor->len) {
+			cursor->state = 2;
+			cursor->xor_count = 0;
+			ppc440spe_rxor_set_region(desc,
+					       cursor->addr_count,
+					       DMA_RXOR123 <<
+					       DMA_CUED_REGION_OFF);
+			if (index == src_cnt - 1) {
+				ppc440spe_adma_dma2rxor_inc_addr(desc, cursor,
+							      index, src_cnt);
+			}
+		} else if (addr == cursor->addrl + 3 * cursor->len) {
+			cursor->state = 2;
+			cursor->xor_count = 0;
+			ppc440spe_rxor_set_region(desc,
+					       cursor->addr_count,
+					       DMA_RXOR124 <<
+					       DMA_CUED_REGION_OFF);
+			if (index == src_cnt - 1) {
+				ppc440spe_adma_dma2rxor_inc_addr(desc, cursor,
+							      index, src_cnt);
+			}
+		} else if (addr == cursor->addrl + 4 * cursor->len) {
+			cursor->state = 2;
+			cursor->xor_count = 0;
+			ppc440spe_rxor_set_region(desc,
+					       cursor->addr_count,
+					       DMA_RXOR125 <<
+					       DMA_CUED_REGION_OFF);
+			if (index == src_cnt - 1) {
+				ppc440spe_adma_dma2rxor_inc_addr(desc, cursor,
+							      index, src_cnt);
+			}
+		} else {
+			cursor->state = 0;
+			cursor->xor_count = 1;
+			cursor->addrl = addr;
+			ppc440spe_rxor_set_region(desc,
+					       cursor->addr_count,
+					       DMA_RXOR12 <<
+					       DMA_CUED_REGION_OFF);
+			ppc440spe_adma_dma2rxor_inc_addr(desc, cursor, index,
+						      src_cnt);
+		}
+		break;
+	case 2:
+		cursor->state = 0;
+		cursor->addrl = addr;
+		cursor->xor_count++;
+		if (index) {
+			ppc440spe_adma_dma2rxor_inc_addr(desc, cursor, index,
+						      src_cnt);
+		}
+		break;
+	}
+
+	return rval;
+}
+
+static inline void ppc440spe_free_ref(struct ppc440spe_adma_device *adev,
+				   struct platform_device *ofdev,
+				   struct ppc440spe_adma_chan *chan)
+{
+	if (adev->id != PPC440SPE_XOR_ID) {
+		dma_unmap_page(&ofdev->dev, chan->pdest,
+			       PAGE_SIZE, DMA_BIDIRECTIONAL);
+		dma_unmap_page(&ofdev->dev, chan->qdest,
+			       PAGE_SIZE, DMA_BIDIRECTIONAL);
+		__free_page(chan->pdest_page);
+		__free_page(chan->qdest_page);
+	}
+}
+static inline void ppc440spe_free_reg(struct ppc440spe_adma_device *adev)
+{
+	if (adev->id == PPC440SPE_XOR_ID)
+		iounmap(adev->xor_reg);
+	else
+		iounmap(adev->dma_reg);
+}
+static inline int ppc440spe_get_cdb_size(struct ppc440spe_adma_chan *ppc440spe_chan)
+{
+	int db_sz;
+	if (ppc440spe_chan->device->id != PPC440SPE_XOR_ID)
+		db_sz = sizeof(struct dma_cdb);
+	else
+		db_sz = sizeof(struct xor_cb);
+	return db_sz;
+}
+
+/*
+ * initialize the channel and the chain with a null operation
+ */
+static inline void ppc440spe_init_chan_null_op(struct ppc440spe_adma_chan
+					    *ppc440spe_chan)
+{
+	switch (ppc440spe_chan->device->id) {
+	case PPC440SPE_DMA0_ID:
+	case PPC440SPE_DMA1_ID:
+		ppc440spe_chan->hw_chain_inited = 0;
+		/* Use WXOR for self-testing */
+		if (!ppc440spe_r6_tchan)
+			ppc440spe_r6_tchan = ppc440spe_chan;
+		break;
+	case PPC440SPE_XOR_ID:
+		ppc440spe_chan_start_null_xor(ppc440spe_chan);
+		break;
+	default:
+		BUG();
+	}
+}
+static inline int ppc440spe_adma_get_devid(struct platform_device *ofdev,
+					struct device_node *np)
+{
+	unsigned int id;
+	unsigned int len;
+	const unsigned int *idx;
+	if (of_device_is_compatible(np, "amcc,xor-accelerator")) {
+		id = PPC440SPE_XOR_ID;
+	} else {
+		/* it is DMA0 or DMA1 */
+		idx = of_get_property(np, "cell-index", &len);
+		/* it is DMA0 or DMA1 */
+		if (!idx || (len != sizeof(u32))) {
+			dev_err(&ofdev->dev, "Device node %s has missing "
+				"or invalid cell-index property\n",
+				np->full_name);
+			return -EINVAL;
+		}
+		id = *idx;
+	}
+	return id;
+}
+static inline int ppc440spe_adma_get_pool_size(struct device_node *np, int id)
+{
+	unsigned int pool_size;
+	if (of_device_is_compatible(np, "amcc,xor-accelerator")) {
+		/* As far as the XOR engine is concerned, it does not
+		 * use FIFOs but uses linked list. So there is no dependency
+		 * between pool size to allocate and the engine configuration.
+		 */
+		pool_size = PAGE_SIZE << 1;
+	} else {
+		/* DMA0,1 engines use FIFO to maintain CDBs, so we
+		 * should allocate the pool accordingly to size of this
+		 * FIFO. Thus, the pool size depends on the FIFO depth:
+		 * how much CDBs pointers the FIFO may contain then so
+		 * much CDBs we should provide in the pool.
+		 * That is
+		 * CDB size = 32B;
+		 * CDBs number = (DMA0_FIFO_SIZE >> 3);
+		 * Pool size = CDBs number * CDB size =
+		 * = (DMA0_FIFO_SIZE >> 3) << 5 = DMA0_FIFO_SIZE << 2.
+		 */
+		pool_size = (id == PPC440SPE_DMA0_ID) ?
+		    DMA0_FIFO_SIZE : DMA1_FIFO_SIZE;
+		pool_size <<= 2;
+	}
+	return pool_size;
+}
+static inline void ppc440spe_adma_init_hw(struct ppc440spe_adma_device *adev,
+				       void *regs)
+{
+	if (adev->id == PPC440SPE_XOR_ID) {
+		adev->xor_reg = regs;
+		/* Reset XOR */
+		iowrite32be(XOR_CRSR_XASR_BIT, &adev->xor_reg->crsr);
+		iowrite32be(XOR_CRSR_64BA_BIT, &adev->xor_reg->crrr);
+	} else {
+		size_t fifo_size = (adev->id == PPC440SPE_DMA0_ID) ?
+		    DMA0_FIFO_SIZE : DMA1_FIFO_SIZE;
+		adev->dma_reg = regs;
+		/* DMAx_FIFO_SIZE is defined in bytes,
+		 * <fsiz> - is defined in number of CDB pointers (8byte).
+		 * DMA FIFO Length = CSlength + CPlength, where
+		 * CSlength = CPlength = (fsiz + 1) * 8.
+		 */
+		iowrite32(DMA_FIFO_ENABLE | ((fifo_size >> 3) - 2),
+			  &adev->dma_reg->fsiz);
+		/* Configure DMA engine */
+		iowrite32(DMA_CFG_DXEPR_HP | DMA_CFG_DFMPP_HP | DMA_CFG_FALGN,
+			  &adev->dma_reg->cfg);
+		/* Clear Status */
+		iowrite32(~0, &adev->dma_reg->dsts);
+	}
+}
+static inline int ppc440spe_create_helper_pages(struct ppc440spe_adma_device *adev,
+					     struct platform_device *ofdev,
+					     struct ppc440spe_adma_chan *chan)
+{
+	int ret = 0;
+	/* allocate and map helper pages for async validation or
+	 * async_mult/async_sum_product operations on DMA0/1.
+	 */
+	if (adev->id != PPC440SPE_XOR_ID) {
+		chan->pdest_page = alloc_page(GFP_KERNEL);
+		chan->qdest_page = alloc_page(GFP_KERNEL);
+		if (!chan->pdest_page || !chan->qdest_page) {
+			if (chan->pdest_page)
+				__free_page(chan->pdest_page);
+			if (chan->qdest_page)
+				__free_page(chan->qdest_page);
+			ret = -ENOMEM;
+			goto err_page_alloc;
+		}
+		chan->pdest = dma_map_page(&ofdev->dev, chan->pdest_page, 0,
+					   PAGE_SIZE, DMA_BIDIRECTIONAL);
+		chan->qdest = dma_map_page(&ofdev->dev, chan->qdest_page, 0,
+					   PAGE_SIZE, DMA_BIDIRECTIONAL);
+	}
+      err_page_alloc:
+	return ret;
+}
+
+#endif /*__PPC440SPE_ADMA_H*/
-- 
1.6.1.rc3

^ permalink raw reply related

* Re: [PATCH] PPC4xx: ADMA separating SoC specific functions
From: Wolfgang Denk @ 2010-09-30 19:08 UTC (permalink / raw)
  To: tmarri; +Cc: linux-raid, dan.j.williams, linuxppc-dev, linux-crypto, yur
In-Reply-To: <1285865736-32074-1-git-send-email-tmarri@apm.com>

Dear tmarri@apm.com,

In message <1285865736-32074-1-git-send-email-tmarri@apm.com> you wrote:
> From: Tirumala Marri <tmarri@apm.com>
> 
> This patch separates the SoC specific functions and moved
> to different files.
> 
> The reason for ppc440spe-adma.h is to define in-line functions which
> are called by both adma.c and ppc440spe-adma.c . 
> 
> Where as ppc440spe-adma.c is to define functions are completely
> completely dependent on 440spe, also which are too big to define
> as in-line functions.

When reposting a patch, please always indicate that this is new
version by using something like "[PATCH v2]" in the Subject line.

> Signed-off-by: Tirumala R Marri <tmarri@apm.com>
> Acked-by: Yuri Tikhonov <yur@emcraft.com>
> CC:  Dan Williams <dan.j.williams@intel.com>
> CC:  Josh Boyer <jwboyer@linux.vnet.ibm.com>
> ---

Also, please include here (i. e. below the "---" line, i. e. in the
comments section, a description of what was changed compared to the
previous version of this patch.

As is, you enforce us to rescan the whole patch again and check
manually if you have reacted to any of the comments sent before, and
how.  As is, you make reviewing your poatches harder than necessary.


> diff --git a/drivers/dma/ppc4xx/adma.c b/drivers/dma/ppc4xx/adma.c
> index 0d58a4a..a1053cb 100644
> --- a/drivers/dma/ppc4xx/adma.c
> +++ b/drivers/dma/ppc4xx/adma.c
...
> +#include "ppc440spe-adma.h"
> +
> +struct dma_async_tx_descriptor
> +*ppc440spe_adma_prep_dma_pq(struct dma_chan *chan,
> +			       dma_addr_t * dst,
> +			       dma_addr_t * src,
> +			       unsigned int src_cnt,
> +			       const unsigned char *scf,
> +			       size_t len,
> +			       unsigned long flags);
> +struct dma_async_tx_descriptor
> +*ppc440spe_adma_prep_dma_pqzero_sum(struct dma_chan *chan,

Should such 440SPe specific code not be removed here and placed into
ppc440spe-adma.c instead?

> +#if 0
>  static void prep_dma_pq_dbg(int id, dma_addr_t *dst, dma_addr_t *src,
>  			    unsigned int src_cnt)
>  {
> @@ -213,8 +104,9 @@ static void prep_dma_pq_dbg(int id, dma_addr_t *dst, dma_addr_t *src,
>  	for (i = 0; i < 2; i++)
>  		pr_debug("\t0x%016llx ", dst[i]);
>  }
> +#endif

Please do not add dead code - remove the whole "#if 0" block.


>  /******************************************************************************
>   * ADMA channel low-level routines
>   ******************************************************************************/
>  
> -static u32
...
...
> -}
>  
>  /******************************************************************************
>   * ADMA device level
>   ******************************************************************************/

It seems youremove all code, but leave the (now empty) comment
headers? This makes little sense to me.

...
>  /**
>   * ppc440spe_adma_free_slots - flags descriptor slots for reuse
>   * @slot: Slot to free
>   * Caller must hold &ppc440spe_chan->lock while calling this function
>   */

Again, all this is pretty low-level 440SPe specific code. Why do you
keep this in the common drive rfile instead of moving it into the new
440SPe specific file?


> diff --git a/drivers/dma/ppc4xx/ppc440spe-adma.c b/drivers/dma/ppc4xx/ppc440spe-adma.c
> new file mode 100644
> index 0000000..da467b4
...
> +	/*  In the current implementation of ppc440spe ADMA driver it
> +
> +
> +
> +	 * makes sense to pick out only pq case, because it may be

Formatting problems?


> diff --git a/drivers/dma/ppc4xx/ppc440spe-adma.h b/drivers/dma/ppc4xx/ppc440spe-adma.h
> new file mode 100644
> index 0000000..81a1f46
> --- /dev/null
> +++ b/drivers/dma/ppc4xx/ppc440spe-adma.h
...
> +/*
> + * ppc440spe_get_group_entry - get group entry with index idx
> + * @tdesc: is the last allocated slot in the group.
> + */
> +static struct ppc440spe_adma_desc_slot *ppc440spe_get_group_entry(struct
> +							    ppc440spe_adma_desc_slot
> +							    *tdesc,
> +							    u32 entry_idx)
> +{
> +	struct ppc440spe_adma_desc_slot *iter = tdesc->group_head;
> +	int i = 0;
> +
> +	if (entry_idx < 0 || entry_idx >= (tdesc->src_cnt + tdesc->dst_cnt)) {
> +		printk("%s: entry_idx %d, src_cnt %d, dst_cnt %d\n",
> +		       __func__, entry_idx, tdesc->src_cnt, tdesc->dst_cnt);
> +		BUG();
> +	}
> +
> +	list_for_each_entry(iter, &tdesc->group_list, chain_node) {
> +		if (i++ == entry_idx)
> +			break;
> +	}
> +	return iter;
> +}

This is a header file, yet you add here literally thousands of lines of
code.


Note that more or less similar questions have been asked for the
previous version of this patch, but I fail to find any good
justification in your replies.


Selecting the architecture at build time is bad as it prevents using a
sinlge kernel image across a wide range of boards.  You only replied
"We select the architecture at build time." without any explanation if
there is a pressing technical reason to do it this way, or if this was
just a arbitrary decision.

The same goes for putting so much source code in a header file - I
really see no technical need for this (especially not if you build for
a single architecture only).

Also I wonder why you still keep so many 440SPe specific code in the
common file, even though you just create new 440SPe specific header
and source files.


Please elucidate.


Thanks.

Wolfgang Denk

-- 
DENX Software Engineering GmbH,     MD: Wolfgang Denk & Detlev Zundel
HRB 165235 Munich, Office: Kirchenstr.5, D-82194 Groebenzell, Germany
Phone: (+49)-8142-66989-10 Fax: (+49)-8142-66989-80 Email: wd@denx.de
"Unix is simple, but it takes a genius to understand the simplicity."
					             - Dennis Ritchie

^ permalink raw reply

* Re: [PATCH v3 6/7] mtd: m25p80: add a read function to read page by page
From: Grant Likely @ 2010-09-30 20:57 UTC (permalink / raw)
  To: Anton Vorontsov
  Cc: kumar.gala, David Brownell, linuxppc-dev, linux-mtd,
	spi-devel-general, Mingkai Hu
In-Reply-To: <20100930150633.GA13741@oksana.dev.rtsoft.ru>

On Fri, Oct 1, 2010 at 12:06 AM, Anton Vorontsov <cbouatmailru@gmail.com> w=
rote:
> On Thu, Sep 30, 2010 at 11:41:40PM +0900, Grant Likely wrote:
>> On Thu, Sep 30, 2010 at 11:16 PM, Grant Likely
>> <grant.likely@secretlab.ca> wrote:
>> > On Thu, Sep 30, 2010 at 7:46 PM, David Brownell <david-b@pacbell.net> =
wrote:
>> >>
>> >> --- On Thu, 9/30/10, Mingkai Hu <Mingkai.hu@freescale.com> wrote:
>> >>
>> >>> From: Mingkai Hu <Mingkai.hu@freescale.com>
>> >>> Subject: [PATCH v3 6/7] mtd: m25p80: add a read function to read pag=
e by page
>> >>
>> >> NAK.
>> >>
>> >> We went over this before.
>> >
>> > Yes, I agree with David on this. =A0If large transfers don't work, the=
n
>> > it is the SPI master driver that is buggy.
>>
>> By the way, does this fix your problem?
>>
>> https://patchwork.kernel.org/patch/184752/
>
> It shouldn't. AFAIK, eSPI is PIO-only controller, and the overrun
> fix is for the DMA mode.
>
> Thanks,
>
> p.s. Btw, in patch 3/7, is_dma_mapped argument of fsl_espi_bufs()
> is unneeded.

Thanks Anton.  Please reply to that patch with this comment so that
patchwork records it and I don't forget about it.

Thanks,
g.

>
> --
> Anton Vorontsov
> email: cbouatmailru@gmail.com
> irc://irc.freenode.net/bd2
>



--=20
Grant Likely, B.Sc., P.Eng.
Secret Lab Technologies Ltd.

^ permalink raw reply

* Re: [PATCH (Option 1)] of/i2c: fix module load order issue caused by of_i2c.c
From: Grant Likely @ 2010-09-30 21:11 UTC (permalink / raw)
  To: Jean Delvare
  Cc: mikpe, linux-kernel, rdunlap, linux-i2c, linuxppc-dev, Ben Dooks
In-Reply-To: <20100929164251.0243ac7f@endymion.delvare>

On Wed, Sep 29, 2010 at 11:42 PM, Jean Delvare <khali@linux-fr.org> wrote:
> On Wed, 29 Sep 2010 00:20:54 +0100, Ben Dooks wrote:
>> On Fri, Sep 24, 2010 at 04:14:53PM -0600, Grant Likely wrote:
>> > Commit 959e85f7, "i2c: add OF-style registration and binding" caused a
>> > module dependency loop where of_i2c.c calls functions in i2c-core, and
>> > i2c-core calls of_i2c_register_devices() in of_i2c. =A0This means that
>> > when i2c support is built as a module when CONFIG_OF is set, then
>> > neither i2c_core nor of_i2c are able to be loaded.
>> >
>> > This patch fixes the problem by moving the of_i2c_register_devices()
>> > function into the body of i2c_core and renaming it to
>> > i2c_scan_of_devices (of_i2c_register_devices is analogous to the
>> > existing i2c_scan_static_board_info function and so should be named
>> > similarly). =A0This function isn't called by any code outside of
>> > i2c_core, and it must always be present when CONFIG_OF is selected, so
>> > it makes sense to locate it there. =A0When CONFIG_OF is not selected,
>> > of_i2c_register_devices() becomes a no-op.
>>
>> I sort of go with this one.
>
> Actually I would prefer option #2, even though I understand it won't
> make Grant too happy. Having a large chunk of OF-specific code in
> i2c-core, leaving of_i2c.c almost empty, doesn't seem right.

I'm fine with this.  In the grand scheme, it ends up being an unimportant p=
oint.

> I took a look at what other relevant subsystems do. SPI is boolean so it
> doesn't have the issue. MDIO is tristate, the registration function is
> in of_mdio.c and individual drivers call it. And there are a lot more
> of these (9) than i2c drivers (3).
>
> So I would let individual drivers call of_i2c_register_devices(), as it
> used to be until 2.6.35. 2 extra functions calls doesn't seem a high
> price to pay to keep the code logically separated. This also make
> things consistent, with all OF registration functions living under
> drivers/of.

This is actually historical and somewhat in transition.  Now that all
the OF core code is cleaned up and generalized, I'm looking at the bus
specific hooks and deciding what would be best to do about them.  I'm
likely to move the SPI support into drivers/spi, and I'll probably
post a patch to do the same for drivers/net/phy and for the platform
bus.  The reason being that the data extraction code is far more bus
specific than it is OF-specific.

I will however back off from putting the registration hook directly
into the shared bus registration functions for the time being.  It is
a minor issue, and it does make a certain amount of sense for the
individual drivers to control the bus population.  Proof is in the
patches anyway and we can debate it after I actually post something
concrete.

g.

^ permalink raw reply

* Re: [PATCH v3 5/7] mtd: m25p80: add support to parse the SPI flash's partitions
From: Grant Likely @ 2010-09-30 21:34 UTC (permalink / raw)
  To: Mingkai Hu; +Cc: linuxppc-dev, kumar.gala, linux-mtd, spi-devel-general
In-Reply-To: <1285833646-12006-6-git-send-email-Mingkai.hu@freescale.com>

On Thu, Sep 30, 2010 at 5:00 PM, Mingkai Hu <Mingkai.hu@freescale.com> wrot=
e:
> Signed-off-by: Mingkai Hu <Mingkai.hu@freescale.com>
> ---
> v3:
> =A0- Move the SPI flash partition code to the probe function.
>
> =A0drivers/mtd/devices/m25p80.c | =A0 39 +++++++++++++++++++++++++++-----=
-------
> =A01 files changed, 27 insertions(+), 12 deletions(-)
>
> diff --git a/drivers/mtd/devices/m25p80.c b/drivers/mtd/devices/m25p80.c
> index 6f512b5..47d53c7 100644
> --- a/drivers/mtd/devices/m25p80.c
> +++ b/drivers/mtd/devices/m25p80.c
> @@ -772,7 +772,7 @@ static const struct spi_device_id *__devinit jedec_pr=
obe(struct spi_device *spi)
> =A0static int __devinit m25p_probe(struct spi_device *spi)
> =A0{
> =A0 =A0 =A0 =A0const struct spi_device_id =A0 =A0 =A0*id =3D spi_get_devi=
ce_id(spi);
> - =A0 =A0 =A0 struct flash_platform_data =A0 =A0 =A0*data;
> + =A0 =A0 =A0 struct flash_platform_data =A0 =A0 =A0data, *pdata;
> =A0 =A0 =A0 =A0struct m25p =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 *flash=
;
> =A0 =A0 =A0 =A0struct flash_info =A0 =A0 =A0 =A0 =A0 =A0 =A0 *info;
> =A0 =A0 =A0 =A0unsigned =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0i;
> @@ -782,13 +782,27 @@ static int __devinit m25p_probe(struct spi_device *=
spi)
> =A0 =A0 =A0 =A0 * a chip ID, try the JEDEC id commands; they'll work for =
most
> =A0 =A0 =A0 =A0 * newer chips, even if we don't recognize the particular =
chip.
> =A0 =A0 =A0 =A0 */
> - =A0 =A0 =A0 data =3D spi->dev.platform_data;
> - =A0 =A0 =A0 if (data && data->type) {
> + =A0 =A0 =A0 pdata =3D spi->dev.platform_data;
> + =A0 =A0 =A0 if (!pdata && spi->dev.of_node) {
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 int nr_parts;
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 struct mtd_partition *parts;
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 struct device_node *np =3D spi->dev.of_node=
;
> +
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 nr_parts =3D of_mtd_parse_partitions(&spi->=
dev, np, &parts);
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 if (nr_parts) {
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 pdata =3D &data;
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 memset(pdata, 0, sizeof(*pd=
ata));
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 pdata->parts =3D parts;
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 pdata->nr_parts =3D nr_part=
s;
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 }
> + =A0 =A0 =A0 }

Yes, this is the correct way to go about adding the partitions.
However, this patch can be made simpler by not renaming 'data' to
'pdata' and by moving the above code down to just before the partition
information is actually used.  in the OF case, only the parts and the
nr_parts values written into data, and those values aren't used until
the last part of the probe function.

Regardless, in principle this patch is correct:

Acked-by: Grant Likely <grant.likely@secretlab.ca>

> +
> + =A0 =A0 =A0 if (pdata && pdata->type) {
> =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0const struct spi_device_id *plat_id;
>
> =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0for (i =3D 0; i < ARRAY_SIZE(m25p_ids) - 1=
; i++) {
> =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0plat_id =3D &m25p_ids[i];
> - =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 if (strcmp(data->type, plat=
_id->name))
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 if (strcmp(pdata->type, pla=
t_id->name))
> =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0continue;
> =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0break;
> =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0}
> @@ -796,7 +810,8 @@ static int __devinit m25p_probe(struct spi_device *sp=
i)
> =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0if (i < ARRAY_SIZE(m25p_ids) - 1)
> =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0id =3D plat_id;
> =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0else
> - =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 dev_warn(&spi->dev, "unreco=
gnized id %s\n", data->type);
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 dev_warn(&spi->dev, "unreco=
gnized id %s\n",
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0=
 =A0 pdata->type);
> =A0 =A0 =A0 =A0}
>
> =A0 =A0 =A0 =A0info =3D (void *)id->driver_data;
> @@ -847,8 +862,8 @@ static int __devinit m25p_probe(struct spi_device *sp=
i)
> =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0write_sr(flash, 0);
> =A0 =A0 =A0 =A0}
>
> - =A0 =A0 =A0 if (data && data->name)
> - =A0 =A0 =A0 =A0 =A0 =A0 =A0 flash->mtd.name =3D data->name;
> + =A0 =A0 =A0 if (pdata && pdata->name)
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 flash->mtd.name =3D pdata->name;
> =A0 =A0 =A0 =A0else
> =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0flash->mtd.name =3D dev_name(&spi->dev);
>
> @@ -919,9 +934,9 @@ static int __devinit m25p_probe(struct spi_device *sp=
i)
> =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =
=A0 =A0part_probes, &parts, 0);
> =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0}
>
> - =A0 =A0 =A0 =A0 =A0 =A0 =A0 if (nr_parts <=3D 0 && data && data->parts)=
 {
> - =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 parts =3D data->parts;
> - =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 nr_parts =3D data->nr_parts=
;
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 if (nr_parts <=3D 0 && pdata && pdata->part=
s) {
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 parts =3D pdata->parts;
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 nr_parts =3D pdata->nr_part=
s;
> =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0}

As per my comment earlier; since parts and nr_parts isn't needed
before this point, this block could simply be:

if (nr_parts <=3D 0 && data && data->parts) {
        parts =3D data->parts;
        nr_parts =3D data->nr_parts;
}
if (nr_parts <=3D 0 && spi->dev.of_node)
  =A0 =A0 =A0 nr_parts =3D of_mtd_parse_partitions(&spi->dev, np, &parts);

And most of the other changes to this file goes away.  Simpler, yes?

g.

^ permalink raw reply


This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox