diff mbox

[v2] spi: atmel: Refactor spi-atmel to use SPI framework queue

Message ID 1389244755-16188-1-git-send-email-wenyou.yang@atmel.com (mailing list archive)
State Accepted
Commit 8090d6d1a415d3ae1a7208995decfab8f60f4f36
Headers show

Commit Message

Wenyou Yang Jan. 9, 2014, 5:19 a.m. UTC
Replace the deprecated master->transfer with transfer_one_message()
and allow the SPI subsystem handle all the queuing of messages.

Signed-off-by: Wenyou Yang <wenyou.yang@atmel.com>
Tested-by: Richard Genoud <richard.genoud@gmail.com>
---
Hi Mark,

Thanks for your feedback.

According to your advice, I prepared this version 2.

The patch is based on the for-next branch of
	git://git.kernel.org/pub/scm/linux/kernel/git/broonie/spi.git

I tested it on sama5d3xek and at91sam9m10g45ek board.
Richard helped tested it on at91sam9g35-ek board.

Best Regards,
Wenyou Yang

v2 changelog:
 1./ Rebase the latest for-next branch included the patch from Richard.
 2./ remove the lock on complete(&as->xfer_completion).
 3./ rework the xfer->cs_change is true.
 4./ remove the member stopping of struct atmel_spi.

 drivers/spi/spi-atmel.c |  678 +++++++++++++++--------------------------------
 1 file changed, 220 insertions(+), 458 deletions(-)

Comments

Mark Brown Jan. 9, 2014, 5:42 p.m. UTC | #1
On Thu, Jan 09, 2014 at 01:19:15PM +0800, Wenyou Yang wrote:
> Replace the deprecated master->transfer with transfer_one_message()
> and allow the SPI subsystem handle all the queuing of messages.

Applied, thanks.
Mans Rullgard Jan. 5, 2016, 7:27 p.m. UTC | #2
Wenyou Yang <wenyou.yang@atmel.com> writes:

> Replace the deprecated master->transfer with transfer_one_message()
> and allow the SPI subsystem handle all the queuing of messages.
>
> Signed-off-by: Wenyou Yang <wenyou.yang@atmel.com>
> Tested-by: Richard Genoud <richard.genoud@gmail.com>
> ---
> Hi Mark,
>
> Thanks for your feedback.
>
> According to your advice, I prepared this version 2.
>
> The patch is based on the for-next branch of
> 	git://git.kernel.org/pub/scm/linux/kernel/git/broonie/spi.git
>
> I tested it on sama5d3xek and at91sam9m10g45ek board.
> Richard helped tested it on at91sam9g35-ek board.
>
> Best Regards,
> Wenyou Yang
>
> v2 changelog:
>  1./ Rebase the latest for-next branch included the patch from Richard.
>  2./ remove the lock on complete(&as->xfer_completion).
>  3./ rework the xfer->cs_change is true.
>  4./ remove the member stopping of struct atmel_spi.
>
>  drivers/spi/spi-atmel.c |  678 +++++++++++++++--------------------------------
>  1 file changed, 220 insertions(+), 458 deletions(-)

This patch broke the display control on the AT32STK1000 board.  I don't
know which part of the patch is causing the problem.  This shows up in
the kernel log (with debug messages enabled):

atmel_spi atmel_spi.0: version: 0x171
atmel_spi atmel_spi.0: Atmel SPI Controller at 0xffe00000 (irq 3)
spi spi0.0: setup: bpw 8 mode 0x1 -> csr0 00000008
spi spi0.1: setup: bpw 8 mode 0x3 -> csr1 00000009
ltv350qv spi0.1: new message 93c29cb4 submitted for spi0.1
ltv350qv spi0.1: activate 1, mr 000e0011
ltv350qv spi0.1:   start xfer 93c29c78: len 3 tx 93c69ccc/13c69ccc rx   (null)/ffffffff
------------[ cut here ]------------
WARNING: at drivers/spi/spi-atmel.c:1351
CPU: 0 PID: 1 Comm: swapper Not tainted 4.4.0-rc8+ #14
task: 93c1e000 ti: 93c28000 task.ti: 93c28000
PC is at atmel_spi_transfer_one_message+0x33a/0x72c
LR is at schedule_timeout+0x72/0xc0
pc : [<90139a26>]    lr : [<901d44b6>]    Not tainted
sp : 93c29b2c  r12: 00000000  r11: 93d1adb8
r10: 93c1e364  r9 : 00000200  r8 : 00400022
r7 : 93d12dfc  r6 : 93d12dfc  r5 : 93c29c78  r4 : 93c29bb8
r3 : 93c29cb4  r2 : 93d12c00  r1 : 00000000  r0 : 00000000
Flags: qvnZc
Mode bits: hjmde....G
CPU Mode: Supervisor
Call trace:
 [<900ebfea>] ida_get_new_above+0x5a/0x148
 [<900ec07e>] ida_get_new_above+0xee/0x148
 [<900efa96>] put_dec+0xa2/0xbc
 [<900efdea>] number+0x2e6/0x2f4
 [<90137e6a>] __spi_pump_messages+0x21e/0x304
 [<90137910>] spi_queued_transfer+0x0/0x8
 [<90138046>] __spi_sync+0xf6/0xf8
 [<90137910>] spi_queued_transfer+0x0/0x8
 [<90138056>] spi_sync+0x6/0x8
 [<9010a39e>] ltv350qv_write_reg+0x100/0x106
 [<90080000>] __generic_block_fiemap+0x78/0x250
 [<90138150>] spi_complete+0x0/0x8
 [<9010a3cc>] ltv350qv_power+0x28/0x1e4
 [<9010a5f6>] ltv350qv_probe+0x4e/0x68
 [<90136b68>] spi_drv_probe+0x20/0x22
 [<901336e6>] driver_probe_device+0xe6/0x188
 [<90133826>] __device_attach_driver+0x4a/0x54
 [<901327ae>] bus_for_each_drv+0x32/0x54
 [<901337dc>] __device_attach_driver+0x0/0x54
 [<901338a2>] __device_attach+0x5a/0x7c
 [<901338ca>] device_initial_probe+0x6/0x8
 [<90132e2c>] bus_probe_device+0x58/0x5c
 [<90131d94>] device_add+0x1fc/0x344
 [<9013823c>] spi_setup+0x88/0xf8
 [<90139e18>] atmel_spi_setup+0x0/0x160
 [<901386e8>] spi_add_device+0x70/0xf0
 [<9013881e>] spi_new_device+0x56/0x6c
 [<9013884e>] spi_match_master_to_boardinfo+0x1a/0x30
 [<90138938>] spi_register_master+0xd4/0x22c
 [<90138aac>] devm_spi_register_master+0x1c/0x44
 [<90138faa>] atmel_spi_probe+0x29a/0x38c
 [<900b06c0>] sysfs_do_create_link_sd+0x34/0x60
 [<90134430>] platform_drv_probe+0x14/0x40
 [<901336e6>] driver_probe_device+0xe6/0x188
 [<900ecbca>] kobject_add_internal+0x56/0x1c8
 [<901337d8>] __driver_attach+0x50/0x54
 [<90000418>] repair_env_string+0x0/0x58
 [<9013280e>] bus_for_each_dev+0x3e/0x58
 [<90133788>] __driver_attach+0x0/0x54
 [<90133508>] driver_attach+0x10/0x14
 [<90132cb2>] bus_add_driver+0xf6/0x15c
 [<90133aae>] driver_register+0x3e/0x98
 [<901343b6>] __platform_driver_register+0x22/0x28
 [<9000aba8>] atmel_spi_driver_init+0x0/0x10
 [<9000abb2>] atmel_spi_driver_init+0xa/0x10
 [<9000023e>] do_one_initcall+0xaa/0x130
 [<90027ae2>] parse_args+0x19e/0x27c
 [<90000418>] repair_env_string+0x0/0x58
 [<90000374>] kernel_init_freeable+0x88/0x12c
 [<90000382>] kernel_init_freeable+0x96/0x12c
 [<90000418>] repair_env_string+0x0/0x58
 [<901d2588>] kernel_init+0x8/0xa4
 [<90013130>] syscall_return+0x0/0x12
 [<901d2580>] kernel_init+0x0/0xa4
 [<90013130>] syscall_return+0x0/0x12

---[ end trace 6b6391f8ee24f957 ]---
ltv350qv spi0.1: spi transfer timeout
atmel_spi atmel_spi.0: overrun (2/3 remaining)
atmel_spi atmel_spi.0: timeout waiting for TXEMPTY
Wenyou Yang Jan. 14, 2016, 3:23 a.m. UTC | #3
Hello Måns,

Sorry for late answer.

> -----Original Message-----

> From: Måns Rullgård [mailto:mans@mansr.com]

> Sent: 2016?1?6? 3:28

> To: Yang, Wenyou <Wenyou.Yang@atmel.com>

> Cc: broonie@kernel.org; richard.genoud@gmail.com; Ferre, Nicolas

> <Nicolas.FERRE@atmel.com>; linux-kernel@vger.kernel.org; linux-

> spi@vger.kernel.org; linux-arm-kernel@lists.infradead.org

> Subject: Re: [PATCH v2] spi: atmel: Refactor spi-atmel to use SPI framework

> queue

> 

> Wenyou Yang <wenyou.yang@atmel.com> writes:

> 

> > Replace the deprecated master->transfer with transfer_one_message()

> > and allow the SPI subsystem handle all the queuing of messages.

> >

> > Signed-off-by: Wenyou Yang <wenyou.yang@atmel.com>

> > Tested-by: Richard Genoud <richard.genoud@gmail.com>

> > ---

> > Hi Mark,

> >

> > Thanks for your feedback.

> >

> > According to your advice, I prepared this version 2.

> >

> > The patch is based on the for-next branch of

> > 	git://git.kernel.org/pub/scm/linux/kernel/git/broonie/spi.git

> >

> > I tested it on sama5d3xek and at91sam9m10g45ek board.

> > Richard helped tested it on at91sam9g35-ek board.

> >

> > Best Regards,

> > Wenyou Yang

> >

> > v2 changelog:

> >  1./ Rebase the latest for-next branch included the patch from Richard.

> >  2./ remove the lock on complete(&as->xfer_completion).

> >  3./ rework the xfer->cs_change is true.

> >  4./ remove the member stopping of struct atmel_spi.

> >

> >  drivers/spi/spi-atmel.c |  678

> > +++++++++++++++--------------------------------

> >  1 file changed, 220 insertions(+), 458 deletions(-)

> 

> This patch broke the display control on the AT32STK1000 board.  I don't know

> which part of the patch is causing the problem.  This shows up in the kernel log

> (with debug messages enabled):

> 

> atmel_spi atmel_spi.0: version: 0x171

> atmel_spi atmel_spi.0: Atmel SPI Controller at 0xffe00000 (irq 3) spi spi0.0: setup:

> bpw 8 mode 0x1 -> csr0 00000008 spi spi0.1: setup: bpw 8 mode 0x3 -> csr1

> 00000009 ltv350qv spi0.1: new message 93c29cb4 submitted for spi0.1 ltv350qv

> spi0.1: activate 1, mr 000e0011

> ltv350qv spi0.1:   start xfer 93c29c78: len 3 tx 93c69ccc/13c69ccc rx   (null)/ffffffff

> ------------[ cut here ]------------

> WARNING: at drivers/spi/spi-atmel.c:1351

> CPU: 0 PID: 1 Comm: swapper Not tainted 4.4.0-rc8+ #14

> task: 93c1e000 ti: 93c28000 task.ti: 93c28000 PC is at

> atmel_spi_transfer_one_message+0x33a/0x72c

> LR is at schedule_timeout+0x72/0xc0

> pc : [<90139a26>]    lr : [<901d44b6>]    Not tainted

> sp : 93c29b2c  r12: 00000000  r11: 93d1adb8

> r10: 93c1e364  r9 : 00000200  r8 : 00400022

> r7 : 93d12dfc  r6 : 93d12dfc  r5 : 93c29c78  r4 : 93c29bb8

> r3 : 93c29cb4  r2 : 93d12c00  r1 : 00000000  r0 : 00000000

> Flags: qvnZc

> Mode bits: hjmde....G

> CPU Mode: Supervisor

> Call trace:

>  [<900ebfea>] ida_get_new_above+0x5a/0x148  [<900ec07e>]

> ida_get_new_above+0xee/0x148  [<900efa96>] put_dec+0xa2/0xbc  [<900efdea>]

> number+0x2e6/0x2f4  [<90137e6a>] __spi_pump_messages+0x21e/0x304

> [<90137910>] spi_queued_transfer+0x0/0x8  [<90138046>] __spi_sync+0xf6/0xf8

> [<90137910>] spi_queued_transfer+0x0/0x8  [<90138056>] spi_sync+0x6/0x8

> [<9010a39e>] ltv350qv_write_reg+0x100/0x106  [<90080000>]

> __generic_block_fiemap+0x78/0x250  [<90138150>] spi_complete+0x0/0x8

> [<9010a3cc>] ltv350qv_power+0x28/0x1e4  [<9010a5f6>]

> ltv350qv_probe+0x4e/0x68  [<90136b68>] spi_drv_probe+0x20/0x22

> [<901336e6>] driver_probe_device+0xe6/0x188  [<90133826>]

> __device_attach_driver+0x4a/0x54  [<901327ae>] bus_for_each_drv+0x32/0x54

> [<901337dc>] __device_attach_driver+0x0/0x54  [<901338a2>]

> __device_attach+0x5a/0x7c  [<901338ca>] device_initial_probe+0x6/0x8

> [<90132e2c>] bus_probe_device+0x58/0x5c  [<90131d94>]

> device_add+0x1fc/0x344  [<9013823c>] spi_setup+0x88/0xf8  [<90139e18>]

> atmel_spi_setup+0x0/0x160  [<901386e8>] spi_add_device+0x70/0xf0

> [<9013881e>] spi_new_device+0x56/0x6c  [<9013884e>]

> spi_match_master_to_boardinfo+0x1a/0x30

>  [<90138938>] spi_register_master+0xd4/0x22c  [<90138aac>]

> devm_spi_register_master+0x1c/0x44

>  [<90138faa>] atmel_spi_probe+0x29a/0x38c  [<900b06c0>]

> sysfs_do_create_link_sd+0x34/0x60  [<90134430>]

> platform_drv_probe+0x14/0x40  [<901336e6>] driver_probe_device+0xe6/0x188

> [<900ecbca>] kobject_add_internal+0x56/0x1c8  [<901337d8>]

> __driver_attach+0x50/0x54  [<90000418>] repair_env_string+0x0/0x58

> [<9013280e>] bus_for_each_dev+0x3e/0x58  [<90133788>]

> __driver_attach+0x0/0x54  [<90133508>] driver_attach+0x10/0x14  [<90132cb2>]

> bus_add_driver+0xf6/0x15c  [<90133aae>] driver_register+0x3e/0x98

> [<901343b6>] __platform_driver_register+0x22/0x28

>  [<9000aba8>] atmel_spi_driver_init+0x0/0x10  [<9000abb2>]

> atmel_spi_driver_init+0xa/0x10  [<9000023e>] do_one_initcall+0xaa/0x130

> [<90027ae2>] parse_args+0x19e/0x27c  [<90000418>]

> repair_env_string+0x0/0x58  [<90000374>] kernel_init_freeable+0x88/0x12c

> [<90000382>] kernel_init_freeable+0x96/0x12c  [<90000418>]

> repair_env_string+0x0/0x58  [<901d2588>] kernel_init+0x8/0xa4  [<90013130>]

> syscall_return+0x0/0x12  [<901d2580>] kernel_init+0x0/0xa4  [<90013130>]

> syscall_return+0x0/0x12

> 

> ---[ end trace 6b6391f8ee24f957 ]---

> ltv350qv spi0.1: spi transfer timeout

> atmel_spi atmel_spi.0: overrun (2/3 remaining) atmel_spi atmel_spi.0: timeout

> waiting for TXEMPTY


I tried to reproduce this issue, but I failed.

Could you send me your .config file? Thank you.

> 

> --

> Måns Rullgård



Best Regards,
Wenyou Yang
Mans Rullgard Jan. 18, 2016, 1:26 a.m. UTC | #4
"Yang, Wenyou" <Wenyou.Yang@atmel.com> writes:

> Hello Måns,
>
> Sorry for late answer.
>
>> -----Original Message-----
>> From: Måns Rullgård [mailto:mans@mansr.com]
>> Sent: 2016?1?6? 3:28
>> To: Yang, Wenyou <Wenyou.Yang@atmel.com>
>> Cc: broonie@kernel.org; richard.genoud@gmail.com; Ferre, Nicolas
>> <Nicolas.FERRE@atmel.com>; linux-kernel@vger.kernel.org; linux-
>> spi@vger.kernel.org; linux-arm-kernel@lists.infradead.org
>> Subject: Re: [PATCH v2] spi: atmel: Refactor spi-atmel to use SPI framework
>> queue
>> 
>> Wenyou Yang <wenyou.yang@atmel.com> writes:
>> 
>> > Replace the deprecated master->transfer with transfer_one_message()
>> > and allow the SPI subsystem handle all the queuing of messages.
>> >
>> > Signed-off-by: Wenyou Yang <wenyou.yang@atmel.com>
>> > Tested-by: Richard Genoud <richard.genoud@gmail.com>
>> > ---
>> > Hi Mark,
>> >
>> > Thanks for your feedback.
>> >
>> > According to your advice, I prepared this version 2.
>> >
>> > The patch is based on the for-next branch of
>> > 	git://git.kernel.org/pub/scm/linux/kernel/git/broonie/spi.git
>> >
>> > I tested it on sama5d3xek and at91sam9m10g45ek board.
>> > Richard helped tested it on at91sam9g35-ek board.
>> >
>> > Best Regards,
>> > Wenyou Yang
>> >
>> > v2 changelog:
>> >  1./ Rebase the latest for-next branch included the patch from Richard.
>> >  2./ remove the lock on complete(&as->xfer_completion).
>> >  3./ rework the xfer->cs_change is true.
>> >  4./ remove the member stopping of struct atmel_spi.
>> >
>> >  drivers/spi/spi-atmel.c |  678
>> > +++++++++++++++--------------------------------
>> >  1 file changed, 220 insertions(+), 458 deletions(-)
>> 
>> This patch broke the display control on the AT32STK1000 board.  I don't know
>> which part of the patch is causing the problem.  This shows up in the kernel log
>> (with debug messages enabled):
>> 
>> atmel_spi atmel_spi.0: version: 0x171
>> atmel_spi atmel_spi.0: Atmel SPI Controller at 0xffe00000 (irq 3) spi spi0.0: setup:
>> bpw 8 mode 0x1 -> csr0 00000008 spi spi0.1: setup: bpw 8 mode 0x3 -> csr1
>> 00000009 ltv350qv spi0.1: new message 93c29cb4 submitted for spi0.1 ltv350qv
>> spi0.1: activate 1, mr 000e0011
>> ltv350qv spi0.1:   start xfer 93c29c78: len 3 tx 93c69ccc/13c69ccc rx   (null)/ffffffff
>> ------------[ cut here ]------------
>> WARNING: at drivers/spi/spi-atmel.c:1351
>> CPU: 0 PID: 1 Comm: swapper Not tainted 4.4.0-rc8+ #14
>> task: 93c1e000 ti: 93c28000 task.ti: 93c28000 PC is at
>> atmel_spi_transfer_one_message+0x33a/0x72c
>> LR is at schedule_timeout+0x72/0xc0
>> pc : [<90139a26>]    lr : [<901d44b6>]    Not tainted
>> sp : 93c29b2c  r12: 00000000  r11: 93d1adb8
>> r10: 93c1e364  r9 : 00000200  r8 : 00400022
>> r7 : 93d12dfc  r6 : 93d12dfc  r5 : 93c29c78  r4 : 93c29bb8
>> r3 : 93c29cb4  r2 : 93d12c00  r1 : 00000000  r0 : 00000000
>> Flags: qvnZc
>> Mode bits: hjmde....G
>> CPU Mode: Supervisor
>> Call trace:
>>  [<900ebfea>] ida_get_new_above+0x5a/0x148  [<900ec07e>]
>> ida_get_new_above+0xee/0x148  [<900efa96>] put_dec+0xa2/0xbc  [<900efdea>]
>> number+0x2e6/0x2f4  [<90137e6a>] __spi_pump_messages+0x21e/0x304
>> [<90137910>] spi_queued_transfer+0x0/0x8  [<90138046>] __spi_sync+0xf6/0xf8
>> [<90137910>] spi_queued_transfer+0x0/0x8  [<90138056>] spi_sync+0x6/0x8
>> [<9010a39e>] ltv350qv_write_reg+0x100/0x106  [<90080000>]
>> __generic_block_fiemap+0x78/0x250  [<90138150>] spi_complete+0x0/0x8
>> [<9010a3cc>] ltv350qv_power+0x28/0x1e4  [<9010a5f6>]
>> ltv350qv_probe+0x4e/0x68  [<90136b68>] spi_drv_probe+0x20/0x22
>> [<901336e6>] driver_probe_device+0xe6/0x188  [<90133826>]
>> __device_attach_driver+0x4a/0x54  [<901327ae>] bus_for_each_drv+0x32/0x54
>> [<901337dc>] __device_attach_driver+0x0/0x54  [<901338a2>]
>> __device_attach+0x5a/0x7c  [<901338ca>] device_initial_probe+0x6/0x8
>> [<90132e2c>] bus_probe_device+0x58/0x5c  [<90131d94>]
>> device_add+0x1fc/0x344  [<9013823c>] spi_setup+0x88/0xf8  [<90139e18>]
>> atmel_spi_setup+0x0/0x160  [<901386e8>] spi_add_device+0x70/0xf0
>> [<9013881e>] spi_new_device+0x56/0x6c  [<9013884e>]
>> spi_match_master_to_boardinfo+0x1a/0x30
>>  [<90138938>] spi_register_master+0xd4/0x22c  [<90138aac>]
>> devm_spi_register_master+0x1c/0x44
>>  [<90138faa>] atmel_spi_probe+0x29a/0x38c  [<900b06c0>]
>> sysfs_do_create_link_sd+0x34/0x60  [<90134430>]
>> platform_drv_probe+0x14/0x40  [<901336e6>] driver_probe_device+0xe6/0x188
>> [<900ecbca>] kobject_add_internal+0x56/0x1c8  [<901337d8>]
>> __driver_attach+0x50/0x54  [<90000418>] repair_env_string+0x0/0x58
>> [<9013280e>] bus_for_each_dev+0x3e/0x58  [<90133788>]
>> __driver_attach+0x0/0x54  [<90133508>] driver_attach+0x10/0x14  [<90132cb2>]
>> bus_add_driver+0xf6/0x15c  [<90133aae>] driver_register+0x3e/0x98
>> [<901343b6>] __platform_driver_register+0x22/0x28
>>  [<9000aba8>] atmel_spi_driver_init+0x0/0x10  [<9000abb2>]
>> atmel_spi_driver_init+0xa/0x10  [<9000023e>] do_one_initcall+0xaa/0x130
>> [<90027ae2>] parse_args+0x19e/0x27c  [<90000418>]
>> repair_env_string+0x0/0x58  [<90000374>] kernel_init_freeable+0x88/0x12c
>> [<90000382>] kernel_init_freeable+0x96/0x12c  [<90000418>]
>> repair_env_string+0x0/0x58  [<901d2588>] kernel_init+0x8/0xa4  [<90013130>]
>> syscall_return+0x0/0x12  [<901d2580>] kernel_init+0x0/0xa4  [<90013130>]
>> syscall_return+0x0/0x12
>> 
>> ---[ end trace 6b6391f8ee24f957 ]---
>> ltv350qv spi0.1: spi transfer timeout
>> atmel_spi atmel_spi.0: overrun (2/3 remaining) atmel_spi atmel_spi.0: timeout
>> waiting for TXEMPTY
>
> I tried to reproduce this issue, but I failed.
>
> Could you send me your .config file? Thank you.

I can do better yet.  I just sent a patch for the issue.
diff mbox

Patch

diff --git a/drivers/spi/spi-atmel.c b/drivers/spi/spi-atmel.c
index b96f9a8..b0842f7 100644
--- a/drivers/spi/spi-atmel.c
+++ b/drivers/spi/spi-atmel.c
@@ -189,6 +189,8 @@ 
  */
 #define DMA_MIN_BYTES	16
 
+#define SPI_DMA_TIMEOUT		(msecs_to_jiffies(1000))
+
 struct atmel_spi_dma {
 	struct dma_chan			*chan_rx;
 	struct dma_chan			*chan_tx;
@@ -220,17 +222,13 @@  struct atmel_spi {
 	int			irq;
 	struct clk		*clk;
 	struct platform_device	*pdev;
-	struct spi_device	*stay;
 
-	u8			stopping;
-	struct list_head	queue;
-	struct tasklet_struct	tasklet;
 	struct spi_transfer	*current_transfer;
 	unsigned long		current_remaining_bytes;
-	struct spi_transfer	*next_transfer;
-	unsigned long		next_remaining_bytes;
 	int			done_status;
 
+	struct completion	xfer_completion;
+
 	/* scratch buffer */
 	void			*buffer;
 	dma_addr_t		buffer_dma;
@@ -241,6 +239,9 @@  struct atmel_spi {
 	bool			use_pdc;
 	/* dmaengine data */
 	struct atmel_spi_dma	dma;
+
+	bool			keep_cs;
+	bool			cs_active;
 };
 
 /* Controller-specific per-slave state */
@@ -376,17 +377,6 @@  static inline bool atmel_spi_use_dma(struct atmel_spi *as,
 	return as->use_dma && xfer->len >= DMA_MIN_BYTES;
 }
 
-static inline int atmel_spi_xfer_is_last(struct spi_message *msg,
-					struct spi_transfer *xfer)
-{
-	return msg->transfers.prev == &xfer->transfer_list;
-}
-
-static inline int atmel_spi_xfer_can_be_chained(struct spi_transfer *xfer)
-{
-	return xfer->delay_usecs == 0 && !xfer->cs_change;
-}
-
 static int atmel_spi_dma_slave_config(struct atmel_spi *as,
 				struct dma_slave_config *slave_config,
 				u8 bits_per_word)
@@ -513,23 +503,20 @@  static void dma_callback(void *data)
 	struct spi_master	*master = data;
 	struct atmel_spi	*as = spi_master_get_devdata(master);
 
-	/* trigger SPI tasklet */
-	tasklet_schedule(&as->tasklet);
+	complete(&as->xfer_completion);
 }
 
 /*
  * Next transfer using PIO.
- * lock is held, spi tasklet is blocked
  */
 static void atmel_spi_next_xfer_pio(struct spi_master *master,
 				struct spi_transfer *xfer)
 {
 	struct atmel_spi	*as = spi_master_get_devdata(master);
+	unsigned long xfer_pos = xfer->len - as->current_remaining_bytes;
 
 	dev_vdbg(master->dev.parent, "atmel_spi_next_xfer_pio\n");
 
-	as->current_remaining_bytes = xfer->len;
-
 	/* Make sure data is not remaining in RDR */
 	spi_readl(as, RDR);
 	while (spi_readl(as, SR) & SPI_BIT(RDRF)) {
@@ -537,13 +524,14 @@  static void atmel_spi_next_xfer_pio(struct spi_master *master,
 		cpu_relax();
 	}
 
-	if (xfer->tx_buf)
+	if (xfer->tx_buf) {
 		if (xfer->bits_per_word > 8)
-			spi_writel(as, TDR, *(u16 *)(xfer->tx_buf));
+			spi_writel(as, TDR, *(u16 *)(xfer->tx_buf + xfer_pos));
 		else
-			spi_writel(as, TDR, *(u8 *)(xfer->tx_buf));
-	else
+			spi_writel(as, TDR, *(u8 *)(xfer->tx_buf + xfer_pos));
+	} else {
 		spi_writel(as, TDR, 0);
+	}
 
 	dev_dbg(master->dev.parent,
 		"  start pio xfer %p: len %u tx %p rx %p bitpw %d\n",
@@ -556,7 +544,6 @@  static void atmel_spi_next_xfer_pio(struct spi_master *master,
 
 /*
  * Submit next transfer for DMA.
- * lock is held, spi tasklet is blocked
  */
 static int atmel_spi_next_xfer_dma_submit(struct spi_master *master,
 				struct spi_transfer *xfer,
@@ -747,71 +734,37 @@  static int atmel_spi_set_xfer_speed(struct atmel_spi *as,
  * lock is held, spi irq is blocked
  */
 static void atmel_spi_pdc_next_xfer(struct spi_master *master,
-				struct spi_message *msg)
+					struct spi_message *msg,
+					struct spi_transfer *xfer)
 {
 	struct atmel_spi	*as = spi_master_get_devdata(master);
-	struct spi_transfer	*xfer;
-	u32			len, remaining;
-	u32			ieval;
+	u32			len;
 	dma_addr_t		tx_dma, rx_dma;
 
-	if (!as->current_transfer)
-		xfer = list_entry(msg->transfers.next,
-				struct spi_transfer, transfer_list);
-	else if (!as->next_transfer)
-		xfer = list_entry(as->current_transfer->transfer_list.next,
-				struct spi_transfer, transfer_list);
-	else
-		xfer = NULL;
-
-	if (xfer) {
-		spi_writel(as, PTCR, SPI_BIT(RXTDIS) | SPI_BIT(TXTDIS));
-
-		len = xfer->len;
-		atmel_spi_next_xfer_data(master, xfer, &tx_dma, &rx_dma, &len);
-		remaining = xfer->len - len;
-
-		spi_writel(as, RPR, rx_dma);
-		spi_writel(as, TPR, tx_dma);
-
-		if (msg->spi->bits_per_word > 8)
-			len >>= 1;
-		spi_writel(as, RCR, len);
-		spi_writel(as, TCR, len);
-
-		atmel_spi_set_xfer_speed(as, msg->spi, xfer);
-
-		dev_dbg(&msg->spi->dev,
-			"  start xfer %p: len %u tx %p/%08llx rx %p/%08llx\n",
-			xfer, xfer->len, xfer->tx_buf,
-			(unsigned long long)xfer->tx_dma, xfer->rx_buf,
-			(unsigned long long)xfer->rx_dma);
-	} else {
-		xfer = as->next_transfer;
-		remaining = as->next_remaining_bytes;
-	}
+	spi_writel(as, PTCR, SPI_BIT(RXTDIS) | SPI_BIT(TXTDIS));
 
-	as->current_transfer = xfer;
-	as->current_remaining_bytes = remaining;
+	len = as->current_remaining_bytes;
+	atmel_spi_next_xfer_data(master, xfer, &tx_dma, &rx_dma, &len);
+	as->current_remaining_bytes -= len;
 
-	if (remaining > 0)
-		len = remaining;
-	else if (!atmel_spi_xfer_is_last(msg, xfer)
-			&& atmel_spi_xfer_can_be_chained(xfer)) {
-		xfer = list_entry(xfer->transfer_list.next,
-				struct spi_transfer, transfer_list);
-		len = xfer->len;
-	} else
-		xfer = NULL;
+	spi_writel(as, RPR, rx_dma);
+	spi_writel(as, TPR, tx_dma);
 
-	as->next_transfer = xfer;
+	if (msg->spi->bits_per_word > 8)
+		len >>= 1;
+	spi_writel(as, RCR, len);
+	spi_writel(as, TCR, len);
 
-	if (xfer) {
-		u32	total;
+	dev_dbg(&msg->spi->dev,
+		"  start xfer %p: len %u tx %p/%08llx rx %p/%08llx\n",
+		xfer, xfer->len, xfer->tx_buf,
+		(unsigned long long)xfer->tx_dma, xfer->rx_buf,
+		(unsigned long long)xfer->rx_dma);
 
-		total = len;
+	if (as->current_remaining_bytes) {
+		len = as->current_remaining_bytes;
 		atmel_spi_next_xfer_data(master, xfer, &tx_dma, &rx_dma, &len);
-		as->next_remaining_bytes = total - len;
+		as->current_remaining_bytes -= len;
 
 		spi_writel(as, RNPR, rx_dma);
 		spi_writel(as, TNPR, tx_dma);
@@ -826,11 +779,6 @@  static void atmel_spi_pdc_next_xfer(struct spi_master *master,
 			xfer, xfer->len, xfer->tx_buf,
 			(unsigned long long)xfer->tx_dma, xfer->rx_buf,
 			(unsigned long long)xfer->rx_dma);
-		ieval = SPI_BIT(ENDRX) | SPI_BIT(OVRES);
-	} else {
-		spi_writel(as, RNCR, 0);
-		spi_writel(as, TNCR, 0);
-		ieval = SPI_BIT(RXBUFF) | SPI_BIT(ENDRX) | SPI_BIT(OVRES);
 	}
 
 	/* REVISIT: We're waiting for ENDRX before we start the next
@@ -843,84 +791,11 @@  static void atmel_spi_pdc_next_xfer(struct spi_master *master,
 	 *
 	 * It should be doable, though. Just not now...
 	 */
-	spi_writel(as, IER, ieval);
+	spi_writel(as, IER, SPI_BIT(ENDRX) | SPI_BIT(OVRES));
 	spi_writel(as, PTCR, SPI_BIT(TXTEN) | SPI_BIT(RXTEN));
 }
 
 /*
- * Choose way to submit next transfer and start it.
- * lock is held, spi tasklet is blocked
- */
-static void atmel_spi_dma_next_xfer(struct spi_master *master,
-				struct spi_message *msg)
-{
-	struct atmel_spi	*as = spi_master_get_devdata(master);
-	struct spi_transfer	*xfer;
-	u32	remaining, len;
-
-	remaining = as->current_remaining_bytes;
-	if (remaining) {
-		xfer = as->current_transfer;
-		len = remaining;
-	} else {
-		if (!as->current_transfer)
-			xfer = list_entry(msg->transfers.next,
-				struct spi_transfer, transfer_list);
-		else
-			xfer = list_entry(
-				as->current_transfer->transfer_list.next,
-					struct spi_transfer, transfer_list);
-
-		as->current_transfer = xfer;
-		len = xfer->len;
-		atmel_spi_set_xfer_speed(as, msg->spi, xfer);
-	}
-
-	if (atmel_spi_use_dma(as, xfer)) {
-		u32 total = len;
-		if (!atmel_spi_next_xfer_dma_submit(master, xfer, &len)) {
-			as->current_remaining_bytes = total - len;
-			return;
-		} else {
-			dev_err(&msg->spi->dev, "unable to use DMA, fallback to PIO\n");
-		}
-	}
-
-	/* use PIO if error appened using DMA */
-	atmel_spi_next_xfer_pio(master, xfer);
-}
-
-static void atmel_spi_next_message(struct spi_master *master)
-{
-	struct atmel_spi	*as = spi_master_get_devdata(master);
-	struct spi_message	*msg;
-	struct spi_device	*spi;
-
-	BUG_ON(as->current_transfer);
-
-	msg = list_entry(as->queue.next, struct spi_message, queue);
-	spi = msg->spi;
-
-	dev_dbg(master->dev.parent, "start message %p for %s\n",
-			msg, dev_name(&spi->dev));
-
-	/* select chip if it's not still active */
-	if (as->stay) {
-		if (as->stay != spi) {
-			cs_deactivate(as, as->stay);
-			cs_activate(as, spi);
-		}
-		as->stay = NULL;
-	} else
-		cs_activate(as, spi);
-
-	if (as->use_pdc)
-		atmel_spi_pdc_next_xfer(master, msg);
-	else
-		atmel_spi_dma_next_xfer(master, msg);
-}
-
-/*
  * For DMA, tx_buf/tx_dma have the same relationship as rx_buf/rx_dma:
  *  - The buffer is either valid for CPU access, else NULL
  *  - If the buffer is valid, so is its DMA address
@@ -975,41 +850,7 @@  static void atmel_spi_disable_pdc_transfer(struct atmel_spi *as)
 	spi_writel(as, PTCR, SPI_BIT(RXTDIS) | SPI_BIT(TXTDIS));
 }
 
-static void
-atmel_spi_msg_done(struct spi_master *master, struct atmel_spi *as,
-		struct spi_message *msg, int stay)
-{
-	if (!stay || as->done_status < 0)
-		cs_deactivate(as, msg->spi);
-	else
-		as->stay = msg->spi;
-
-	list_del(&msg->queue);
-	msg->status = as->done_status;
-
-	dev_dbg(master->dev.parent,
-		"xfer complete: %u bytes transferred\n",
-		msg->actual_length);
-
-	atmel_spi_unlock(as);
-	msg->complete(msg->context);
-	atmel_spi_lock(as);
-
-	as->current_transfer = NULL;
-	as->next_transfer = NULL;
-	as->done_status = 0;
-
-	/* continue if needed */
-	if (list_empty(&as->queue) || as->stopping) {
-		if (as->use_pdc)
-			atmel_spi_disable_pdc_transfer(as);
-	} else {
-		atmel_spi_next_message(master);
-	}
-}
-
 /* Called from IRQ
- * lock is held
  *
  * Must update "current_remaining_bytes" to keep track of data
  * to transfer.
@@ -1017,9 +858,7 @@  atmel_spi_msg_done(struct spi_master *master, struct atmel_spi *as,
 static void
 atmel_spi_pump_pio_data(struct atmel_spi *as, struct spi_transfer *xfer)
 {
-	u8		*txp;
 	u8		*rxp;
-	u16		*txp16;
 	u16		*rxp16;
 	unsigned long	xfer_pos = xfer->len - as->current_remaining_bytes;
 
@@ -1041,96 +880,12 @@  atmel_spi_pump_pio_data(struct atmel_spi *as, struct spi_transfer *xfer)
 	} else {
 		as->current_remaining_bytes--;
 	}
-
-	if (as->current_remaining_bytes) {
-		if (xfer->tx_buf) {
-			if (xfer->bits_per_word > 8) {
-				txp16 = (u16 *)(((u8 *)xfer->tx_buf)
-							+ xfer_pos + 2);
-				spi_writel(as, TDR, *txp16);
-			} else {
-				txp = ((u8 *)xfer->tx_buf) + xfer_pos + 1;
-				spi_writel(as, TDR, *txp);
-			}
-		} else {
-			spi_writel(as, TDR, 0);
-		}
-	}
-}
-
-/* Tasklet
- * Called from DMA callback + pio transfer and overrun IRQ.
- */
-static void atmel_spi_tasklet_func(unsigned long data)
-{
-	struct spi_master	*master = (struct spi_master *)data;
-	struct atmel_spi	*as = spi_master_get_devdata(master);
-	struct spi_message	*msg;
-	struct spi_transfer	*xfer;
-
-	dev_vdbg(master->dev.parent, "atmel_spi_tasklet_func\n");
-
-	atmel_spi_lock(as);
-
-	xfer = as->current_transfer;
-
-	if (xfer == NULL)
-		/* already been there */
-		goto tasklet_out;
-
-	msg = list_entry(as->queue.next, struct spi_message, queue);
-
-	if (as->current_remaining_bytes == 0) {
-		if (as->done_status < 0) {
-			/* error happened (overrun) */
-			if (atmel_spi_use_dma(as, xfer))
-				atmel_spi_stop_dma(as);
-		} else {
-			/* only update length if no error */
-			msg->actual_length += xfer->len;
-		}
-
-		if (atmel_spi_use_dma(as, xfer))
-			if (!msg->is_dma_mapped)
-				atmel_spi_dma_unmap_xfer(master, xfer);
-
-		if (xfer->delay_usecs)
-			udelay(xfer->delay_usecs);
-
-		if (atmel_spi_xfer_is_last(msg, xfer) || as->done_status < 0) {
-			/* report completed (or erroneous) message */
-			atmel_spi_msg_done(master, as, msg, xfer->cs_change);
-		} else {
-			if (xfer->cs_change) {
-				cs_deactivate(as, msg->spi);
-				udelay(1);
-				cs_activate(as, msg->spi);
-			}
-
-			/*
-			 * Not done yet. Submit the next transfer.
-			 *
-			 * FIXME handle protocol options for xfer
-			 */
-			atmel_spi_dma_next_xfer(master, msg);
-		}
-	} else {
-		/*
-		 * Keep going, we still have data to send in
-		 * the current transfer.
-		 */
-		atmel_spi_dma_next_xfer(master, msg);
-	}
-
-tasklet_out:
-	atmel_spi_unlock(as);
 }
 
 /* Interrupt
  *
  * No need for locking in this Interrupt handler: done_status is the
- * only information modified. What we need is the update of this field
- * before tasklet runs. This is ensured by using barrier.
+ * only information modified.
  */
 static irqreturn_t
 atmel_spi_pio_interrupt(int irq, void *dev_id)
@@ -1158,8 +913,6 @@  atmel_spi_pio_interrupt(int irq, void *dev_id)
 		 *
 		 * We will also not process any remaning transfers in
 		 * the message.
-		 *
-		 * All actions are done in tasklet with done_status indication
 		 */
 		as->done_status = -EIO;
 		smp_wmb();
@@ -1167,7 +920,7 @@  atmel_spi_pio_interrupt(int irq, void *dev_id)
 		/* Clear any overrun happening while cleaning up */
 		spi_readl(as, SR);
 
-		tasklet_schedule(&as->tasklet);
+		complete(&as->xfer_completion);
 
 	} else if (pending & SPI_BIT(RDRF)) {
 		atmel_spi_lock(as);
@@ -1176,11 +929,10 @@  atmel_spi_pio_interrupt(int irq, void *dev_id)
 			ret = IRQ_HANDLED;
 			xfer = as->current_transfer;
 			atmel_spi_pump_pio_data(as, xfer);
-			if (!as->current_remaining_bytes) {
-				/* no more data to xfer, kick tasklet */
+			if (!as->current_remaining_bytes)
 				spi_writel(as, IDR, pending);
-				tasklet_schedule(&as->tasklet);
-			}
+
+			complete(&as->xfer_completion);
 		}
 
 		atmel_spi_unlock(as);
@@ -1198,116 +950,35 @@  atmel_spi_pdc_interrupt(int irq, void *dev_id)
 {
 	struct spi_master	*master = dev_id;
 	struct atmel_spi	*as = spi_master_get_devdata(master);
-	struct spi_message	*msg;
-	struct spi_transfer	*xfer;
 	u32			status, pending, imr;
 	int			ret = IRQ_NONE;
 
-	atmel_spi_lock(as);
-
-	xfer = as->current_transfer;
-	msg = list_entry(as->queue.next, struct spi_message, queue);
-
 	imr = spi_readl(as, IMR);
 	status = spi_readl(as, SR);
 	pending = status & imr;
 
 	if (pending & SPI_BIT(OVRES)) {
-		int timeout;
 
 		ret = IRQ_HANDLED;
 
 		spi_writel(as, IDR, (SPI_BIT(RXBUFF) | SPI_BIT(ENDRX)
 				     | SPI_BIT(OVRES)));
 
-		/*
-		 * When we get an overrun, we disregard the current
-		 * transfer. Data will not be copied back from any
-		 * bounce buffer and msg->actual_len will not be
-		 * updated with the last xfer.
-		 *
-		 * We will also not process any remaning transfers in
-		 * the message.
-		 *
-		 * First, stop the transfer and unmap the DMA buffers.
-		 */
-		spi_writel(as, PTCR, SPI_BIT(RXTDIS) | SPI_BIT(TXTDIS));
-		if (!msg->is_dma_mapped)
-			atmel_spi_dma_unmap_xfer(master, xfer);
-
-		/* REVISIT: udelay in irq is unfriendly */
-		if (xfer->delay_usecs)
-			udelay(xfer->delay_usecs);
-
-		dev_warn(master->dev.parent, "overrun (%u/%u remaining)\n",
-			 spi_readl(as, TCR), spi_readl(as, RCR));
-
-		/*
-		 * Clean up DMA registers and make sure the data
-		 * registers are empty.
-		 */
-		spi_writel(as, RNCR, 0);
-		spi_writel(as, TNCR, 0);
-		spi_writel(as, RCR, 0);
-		spi_writel(as, TCR, 0);
-		for (timeout = 1000; timeout; timeout--)
-			if (spi_readl(as, SR) & SPI_BIT(TXEMPTY))
-				break;
-		if (!timeout)
-			dev_warn(master->dev.parent,
-				 "timeout waiting for TXEMPTY");
-		while (spi_readl(as, SR) & SPI_BIT(RDRF))
-			spi_readl(as, RDR);
-
 		/* Clear any overrun happening while cleaning up */
 		spi_readl(as, SR);
 
 		as->done_status = -EIO;
-		atmel_spi_msg_done(master, as, msg, 0);
+
+		complete(&as->xfer_completion);
+
 	} else if (pending & (SPI_BIT(RXBUFF) | SPI_BIT(ENDRX))) {
 		ret = IRQ_HANDLED;
 
 		spi_writel(as, IDR, pending);
 
-		if (as->current_remaining_bytes == 0) {
-			msg->actual_length += xfer->len;
-
-			if (!msg->is_dma_mapped)
-				atmel_spi_dma_unmap_xfer(master, xfer);
-
-			/* REVISIT: udelay in irq is unfriendly */
-			if (xfer->delay_usecs)
-				udelay(xfer->delay_usecs);
-
-			if (atmel_spi_xfer_is_last(msg, xfer)) {
-				/* report completed message */
-				atmel_spi_msg_done(master, as, msg,
-						xfer->cs_change);
-			} else {
-				if (xfer->cs_change) {
-					cs_deactivate(as, msg->spi);
-					udelay(1);
-					cs_activate(as, msg->spi);
-				}
-
-				/*
-				 * Not done yet. Submit the next transfer.
-				 *
-				 * FIXME handle protocol options for xfer
-				 */
-				atmel_spi_pdc_next_xfer(master, msg);
-			}
-		} else {
-			/*
-			 * Keep going, we still have data to send in
-			 * the current transfer.
-			 */
-			atmel_spi_pdc_next_xfer(master, msg);
-		}
+		complete(&as->xfer_completion);
 	}
 
-	atmel_spi_unlock(as);
-
 	return ret;
 }
 
@@ -1322,9 +993,6 @@  static int atmel_spi_setup(struct spi_device *spi)
 
 	as = spi_master_get_devdata(spi->master);
 
-	if (as->stopping)
-		return -ESHUTDOWN;
-
 	if (spi->chip_select > spi->master->num_chipselect) {
 		dev_dbg(&spi->dev,
 				"setup: invalid chipselect %u (%u defined)\n",
@@ -1376,12 +1044,6 @@  static int atmel_spi_setup(struct spi_device *spi)
 		asd->npcs_pin = npcs_pin;
 		spi->controller_state = asd;
 		gpio_direction_output(npcs_pin, !(spi->mode & SPI_CS_HIGH));
-	} else {
-		atmel_spi_lock(as);
-		if (as->stay == spi)
-			as->stay = NULL;
-		cs_deactivate(as, spi);
-		atmel_spi_unlock(as);
 	}
 
 	asd->csr = csr;
@@ -1396,97 +1058,218 @@  static int atmel_spi_setup(struct spi_device *spi)
 	return 0;
 }
 
-static int atmel_spi_transfer(struct spi_device *spi, struct spi_message *msg)
+static int atmel_spi_one_transfer(struct spi_master *master,
+					struct spi_message *msg,
+					struct spi_transfer *xfer)
 {
 	struct atmel_spi	*as;
-	struct spi_transfer	*xfer;
-	struct device		*controller = spi->master->dev.parent;
+	struct spi_device	*spi = msg->spi;
 	u8			bits;
+	u32			len;
 	struct atmel_spi_device	*asd;
+	int			timeout;
+	int			ret;
 
-	as = spi_master_get_devdata(spi->master);
-
-	dev_dbg(controller, "new message %p submitted for %s\n",
-			msg, dev_name(&spi->dev));
+	as = spi_master_get_devdata(master);
 
-	if (unlikely(list_empty(&msg->transfers)))
+	if (!(xfer->tx_buf || xfer->rx_buf) && xfer->len) {
+		dev_dbg(&spi->dev, "missing rx or tx buf\n");
 		return -EINVAL;
+	}
 
-	if (as->stopping)
-		return -ESHUTDOWN;
+	if (xfer->bits_per_word) {
+		asd = spi->controller_state;
+		bits = (asd->csr >> 4) & 0xf;
+		if (bits != xfer->bits_per_word - 8) {
+			dev_dbg(&spi->dev,
+			"you can't yet change bits_per_word in transfers\n");
+			return -ENOPROTOOPT;
+		}
+	}
 
-	list_for_each_entry(xfer, &msg->transfers, transfer_list) {
-		if (!(xfer->tx_buf || xfer->rx_buf) && xfer->len) {
-			dev_dbg(&spi->dev, "missing rx or tx buf\n");
+	if (xfer->bits_per_word > 8) {
+		if (xfer->len % 2) {
+			dev_dbg(&spi->dev,
+			"buffer len should be 16 bits aligned\n");
 			return -EINVAL;
 		}
+	}
+
+	/*
+	 * DMA map early, for performance (empties dcache ASAP) and
+	 * better fault reporting.
+	 */
+	if ((!msg->is_dma_mapped)
+		&& (atmel_spi_use_dma(as, xfer)	|| as->use_pdc)) {
+		if (atmel_spi_dma_map_xfer(as, xfer) < 0)
+			return -ENOMEM;
+	}
 
-		if (xfer->bits_per_word) {
-			asd = spi->controller_state;
-			bits = (asd->csr >> 4) & 0xf;
-			if (bits != xfer->bits_per_word - 8) {
-				dev_dbg(&spi->dev,
-					"you can't yet change bits_per_word in transfers\n");
-				return -ENOPROTOOPT;
+	atmel_spi_set_xfer_speed(as, msg->spi, xfer);
+
+	as->done_status = 0;
+	as->current_transfer = xfer;
+	as->current_remaining_bytes = xfer->len;
+	while (as->current_remaining_bytes) {
+		reinit_completion(&as->xfer_completion);
+
+		if (as->use_pdc) {
+			atmel_spi_pdc_next_xfer(master, msg, xfer);
+		} else if (atmel_spi_use_dma(as, xfer)) {
+			len = as->current_remaining_bytes;
+			ret = atmel_spi_next_xfer_dma_submit(master,
+								xfer, &len);
+			if (ret) {
+				dev_err(&spi->dev,
+					"unable to use DMA, fallback to PIO\n");
+				atmel_spi_next_xfer_pio(master, xfer);
+			} else {
+				as->current_remaining_bytes -= len;
 			}
+		} else {
+			atmel_spi_next_xfer_pio(master, xfer);
 		}
 
-		if (xfer->bits_per_word > 8) {
-			if (xfer->len % 2) {
-				dev_dbg(&spi->dev, "buffer len should be 16 bits aligned\n");
-				return -EINVAL;
-			}
+		ret = wait_for_completion_timeout(&as->xfer_completion,
+							SPI_DMA_TIMEOUT);
+		if (WARN_ON(ret == 0)) {
+			dev_err(&spi->dev,
+				"spi trasfer timeout, err %d\n", ret);
+			as->done_status = -EIO;
+		} else {
+			ret = 0;
 		}
 
-		/*
-		 * DMA map early, for performance (empties dcache ASAP) and
-		 * better fault reporting.
-		 */
-		if ((!msg->is_dma_mapped) && (atmel_spi_use_dma(as, xfer)
-			|| as->use_pdc)) {
-			if (atmel_spi_dma_map_xfer(as, xfer) < 0)
-				return -ENOMEM;
+		if (as->done_status)
+			break;
+	}
+
+	if (as->done_status) {
+		if (as->use_pdc) {
+			dev_warn(master->dev.parent,
+				"overrun (%u/%u remaining)\n",
+				spi_readl(as, TCR), spi_readl(as, RCR));
+
+			/*
+			 * Clean up DMA registers and make sure the data
+			 * registers are empty.
+			 */
+			spi_writel(as, RNCR, 0);
+			spi_writel(as, TNCR, 0);
+			spi_writel(as, RCR, 0);
+			spi_writel(as, TCR, 0);
+			for (timeout = 1000; timeout; timeout--)
+				if (spi_readl(as, SR) & SPI_BIT(TXEMPTY))
+					break;
+			if (!timeout)
+				dev_warn(master->dev.parent,
+					 "timeout waiting for TXEMPTY");
+			while (spi_readl(as, SR) & SPI_BIT(RDRF))
+				spi_readl(as, RDR);
+
+			/* Clear any overrun happening while cleaning up */
+			spi_readl(as, SR);
+
+		} else if (atmel_spi_use_dma(as, xfer)) {
+			atmel_spi_stop_dma(as);
+		}
+
+		if (!msg->is_dma_mapped
+			&& (atmel_spi_use_dma(as, xfer) || as->use_pdc))
+			atmel_spi_dma_unmap_xfer(master, xfer);
+
+		return 0;
+
+	} else {
+		/* only update length if no error */
+		msg->actual_length += xfer->len;
+	}
+
+	if (!msg->is_dma_mapped
+		&& (atmel_spi_use_dma(as, xfer) || as->use_pdc))
+		atmel_spi_dma_unmap_xfer(master, xfer);
+
+	if (xfer->delay_usecs)
+		udelay(xfer->delay_usecs);
+
+	if (xfer->cs_change) {
+		if (list_is_last(&xfer->transfer_list,
+				 &msg->transfers)) {
+			as->keep_cs = true;
+		} else {
+			as->cs_active = !as->cs_active;
+			if (as->cs_active)
+				cs_activate(as, msg->spi);
+			else
+				cs_deactivate(as, msg->spi);
 		}
 	}
 
-#ifdef VERBOSE
+	return 0;
+}
+
+static int atmel_spi_transfer_one_message(struct spi_master *master,
+						struct spi_message *msg)
+{
+	struct atmel_spi *as;
+	struct spi_transfer *xfer;
+	struct spi_device *spi = msg->spi;
+	int ret = 0;
+
+	as = spi_master_get_devdata(master);
+
+	dev_dbg(&spi->dev, "new message %p submitted for %s\n",
+					msg, dev_name(&spi->dev));
+
+	if (unlikely(list_empty(&msg->transfers)))
+		return -EINVAL;
+
+	atmel_spi_lock(as);
+	cs_activate(as, spi);
+
+	as->cs_active = true;
+	as->keep_cs = false;
+
+	msg->status = 0;
+	msg->actual_length = 0;
+
+	list_for_each_entry(xfer, &msg->transfers, transfer_list) {
+		ret = atmel_spi_one_transfer(master, msg, xfer);
+		if (ret)
+			goto msg_done;
+	}
+
+	if (as->use_pdc)
+		atmel_spi_disable_pdc_transfer(as);
+
 	list_for_each_entry(xfer, &msg->transfers, transfer_list) {
-		dev_dbg(controller,
+		dev_dbg(&spi->dev,
 			"  xfer %p: len %u tx %p/%08x rx %p/%08x\n",
 			xfer, xfer->len,
 			xfer->tx_buf, xfer->tx_dma,
 			xfer->rx_buf, xfer->rx_dma);
 	}
-#endif
 
-	msg->status = -EINPROGRESS;
-	msg->actual_length = 0;
+msg_done:
+	if (!as->keep_cs)
+		cs_deactivate(as, msg->spi);
 
-	atmel_spi_lock(as);
-	list_add_tail(&msg->queue, &as->queue);
-	if (!as->current_transfer)
-		atmel_spi_next_message(spi->master);
 	atmel_spi_unlock(as);
 
-	return 0;
+	msg->status = as->done_status;
+	spi_finalize_current_message(spi->master);
+
+	return ret;
 }
 
 static void atmel_spi_cleanup(struct spi_device *spi)
 {
-	struct atmel_spi	*as = spi_master_get_devdata(spi->master);
 	struct atmel_spi_device	*asd = spi->controller_state;
 	unsigned		gpio = (unsigned) spi->controller_data;
 
 	if (!asd)
 		return;
 
-	atmel_spi_lock(as);
-	if (as->stay == spi) {
-		as->stay = NULL;
-		cs_deactivate(as, spi);
-	}
-	atmel_spi_unlock(as);
-
 	spi->controller_state = NULL;
 	gpio_free(gpio);
 	kfree(asd);
@@ -1545,7 +1328,7 @@  static int atmel_spi_probe(struct platform_device *pdev)
 	master->bus_num = pdev->id;
 	master->num_chipselect = master->dev.of_node ? 0 : 4;
 	master->setup = atmel_spi_setup;
-	master->transfer = atmel_spi_transfer;
+	master->transfer_one_message = atmel_spi_transfer_one_message;
 	master->cleanup = atmel_spi_cleanup;
 	platform_set_drvdata(pdev, master);
 
@@ -1561,7 +1344,6 @@  static int atmel_spi_probe(struct platform_device *pdev)
 		goto out_free;
 
 	spin_lock_init(&as->lock);
-	INIT_LIST_HEAD(&as->queue);
 
 	as->pdev = pdev;
 	as->regs = devm_ioremap_resource(&pdev->dev, regs);
@@ -1573,6 +1355,8 @@  static int atmel_spi_probe(struct platform_device *pdev)
 	as->irq = irq;
 	as->clk = clk;
 
+	init_completion(&as->xfer_completion);
+
 	atmel_get_caps(as);
 
 	as->use_dma = false;
@@ -1591,9 +1375,6 @@  static int atmel_spi_probe(struct platform_device *pdev)
 		ret = devm_request_irq(&pdev->dev, irq, atmel_spi_pdc_interrupt,
 					0, dev_name(&pdev->dev), master);
 	} else {
-		tasklet_init(&as->tasklet, atmel_spi_tasklet_func,
-					(unsigned long)master);
-
 		ret = devm_request_irq(&pdev->dev, irq, atmel_spi_pio_interrupt,
 					0, dev_name(&pdev->dev), master);
 	}
@@ -1637,8 +1418,6 @@  out_free_dma:
 out_free_irq:
 out_unmap_regs:
 out_free_buffer:
-	if (!as->use_pdc)
-		tasklet_kill(&as->tasklet);
 	dma_free_coherent(&pdev->dev, BUFFER_SIZE, as->buffer,
 			as->buffer_dma);
 out_free:
@@ -1650,12 +1429,9 @@  static int atmel_spi_remove(struct platform_device *pdev)
 {
 	struct spi_master	*master = platform_get_drvdata(pdev);
 	struct atmel_spi	*as = spi_master_get_devdata(master);
-	struct spi_message	*msg;
-	struct spi_transfer	*xfer;
 
 	/* reset the hardware and block queue progress */
 	spin_lock_irq(&as->lock);
-	as->stopping = 1;
 	if (as->use_dma) {
 		atmel_spi_stop_dma(as);
 		atmel_spi_release_dma(as);
@@ -1666,20 +1442,6 @@  static int atmel_spi_remove(struct platform_device *pdev)
 	spi_readl(as, SR);
 	spin_unlock_irq(&as->lock);
 
-	/* Terminate remaining queued transfers */
-	list_for_each_entry(msg, &as->queue, queue) {
-		list_for_each_entry(xfer, &msg->transfers, transfer_list) {
-			if (!msg->is_dma_mapped
-				&& (atmel_spi_use_dma(as, xfer)
-					|| as->use_pdc))
-				atmel_spi_dma_unmap_xfer(master, xfer);
-		}
-		msg->status = -ESHUTDOWN;
-		msg->complete(msg->context);
-	}
-
-	if (!as->use_pdc)
-		tasklet_kill(&as->tasklet);
 	dma_free_coherent(&pdev->dev, BUFFER_SIZE, as->buffer,
 			as->buffer_dma);