From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1754670AbbI3IY2 (ORCPT ); Wed, 30 Sep 2015 04:24:28 -0400 Received: from mail-bn1on0136.outbound.protection.outlook.com ([157.56.110.136]:5730 "EHLO na01-bn1-obe.outbound.protection.outlook.com" rhost-flags-OK-OK-OK-FAIL) by vger.kernel.org with ESMTP id S1753846AbbI3IYU (ORCPT ); Wed, 30 Sep 2015 04:24:20 -0400 Authentication-Results: spf=fail (sender IP is 192.88.158.2) smtp.mailfrom=freescale.com; mentor.com; dkim=none (message not signed) header.d=none;mentor.com; dmarc=none action=none header.from=freescale.com; Date: Wed, 30 Sep 2015 16:23:42 +0800 From: Robin Gong To: Anton Bondarenko , CC: , , , , , , Subject: Re: [PATCH v2 1/8] spi: imx: Fix DMA transfer Message-ID: <20150930082341.GB2709@shlinux2> References: MIME-Version: 1.0 Content-Type: text/plain; charset="us-ascii" Content-Disposition: inline In-Reply-To: User-Agent: Mutt/1.5.21 (2010-09-15) X-EOPAttributedMessage: 0 X-Microsoft-Exchange-Diagnostics: 1;BL2FFO11FD038;1:l8NlLiwf+0q63a6FEaDfbEa37+mp0RKbZxGOVGGPMU7pB/P3+yIbNUaNh1GIUGZC5NksGpjVnAnqKwuT2hyg/NXc8MI3bU8/ZPJCL02fqDJUVCZlBJTcAbAcgmeMneBHLIiaIRuft9FNtCiNTzUoaKsWc9Oxt08InMP0u8iaa3RzBDxIIgylN3KJ8mqUSEfB1qlgbq6tCsBDG9E8tmasPiDvbPGvluYC8NpA2so9t5Bml91KDqZyjEtUMYAzyGVV3PZYcdVmWHEtLoLXf0ne8VrAeolDTAvzIFefjyXfPnGfyS8qK17n7GJ/L2a6aQaYvl8VClG/K9Ma6paSATcd0A== X-Forefront-Antispam-Report: CIP:192.88.158.2;CTRY:US;IPV:NLI;EFV:NLI;SFV:NSPM;SFS:(10019020)(6009001)(2980300002)(1110001)(1109001)(339900001)(199003)(24454002)(189002)(189998001)(50466002)(106466001)(11100500001)(4001540100001)(4001350100001)(76176999)(5001830100001)(97736004)(46102003)(77156002)(5001960100002)(68736005)(5001770100001)(81156007)(83506001)(92566002)(5007970100001)(50986999)(97756001)(104016004)(62966003)(15975445007)(54356999)(19580395003)(105606002)(19580405001)(5001860100001)(2950100001)(47776003)(6806005)(69596002)(23726002)(64706001)(217423001)(77096005)(85426001)(33716001)(33656002)(87936001)(46406003)(5008740100001)(551934003)(42262002);DIR:OUT;SFP:1102;SCL:1;SRVR:BY1PR0301MB1269;H:az84smr01.freescale.net;FPR:;SPF:Fail;PTR:InfoDomainNonexistent;A:1;MX:1;LANG:en; X-Microsoft-Exchange-Diagnostics: 1;BY1PR0301MB1269;2:nSDFiblwYUMsxHtTatiZWgclGqfDIqtwnoJKvOy1e02ZkdwehdUkea6mgsjGoacdYE3Jw+0LEM6GsHkAFLsQl04R/0Ygp02Ez/SXpwYC7Sr+7AUVsG4NnzYg+xdK2xN7Ykr6Ebuta6W/bo3yu5AwbWOgBdLgi2AaCIYs2WSx8fk=;3:+uTJPqL7ldpahVPOji0Y4Jhyp4t951OKrclIBjZYxN325EPonourfvt9KOBWLaeAFrGbQMRhFqA/8C18Wshqm5SVT7U8ZBzObAbhsPJFiDxsUmOpxg52Vo+c4TsWAX9ItcjduCX8HaQyW03b0VfilcFzqZdWF+OLKfpdIyajTGjRHm73ifceFHM3zoDQH1T5cGiMFEe04NfaOr2If3MCYQnjOWnMy97SatSd7VCjaYY=;25:d3xbqW7I7iKFNgPnrujhLCXhzSGPCWniaEHe5yrFe3L26juIrtHpFP/EggAZBcqGgPsFgdq1O0ZmeQtNmW0tD2y2U3J3GA2kE7l2JS4kOL9uzyi8/Y1GcBah2nCSFdYAY7g3XoAAn97GQcF11NKWxwGttBd64Tg266lJ+r8NCtTwtwNK+XMJMo5mO9ZJr0Tob4NUL2/phKLA/7JKF5S7QTIWUcZkSnZXFC+hy8j/jahSTTJaVodFhg0+08cHEYsmuDNTNoGOOE0ktKwpNassiw== X-Microsoft-Antispam: UriScan:;BCL:0;PCL:0;RULEID:;SRVR:BY1PR0301MB1269; X-Microsoft-Exchange-Diagnostics: 1;BY1PR0301MB1269;20:skkcWqB7xIi+Gok09xvo+X7WWnDAOa2Cs/NIZfRlf13o+Xs1/y5szOxlDMhyiOhPzkaYSsdOfkDV/NQiX5y2do2rnrLRrZw/+gsHpIvP/lQqBO9eBkfn/2TugZA79MKUwVwp97uEsjaG9I+nszzXCq546hLaA2KYHZkPdB5gD0vmINsrJVZlaqrCtm7BYvt+SIZdnwgDcHwRHyVJdZhDC5Lj0LVje341eIYSRlIXfvDFkO78awWoa3tocf8KXkJwZE3EhF67w7lFxlc6iUnKKj+zCEc9kRi0AkOU6Sgk05Kl902nTuVI7VyPXgSxH/XtVc4IOj6Bk76rFx6MZMMnNMPPkViDidQnrfafGG2NzgA=;4:yhA+2+Hqiofr7hUhFMZSgiznrAOyzRuXzdiih2fv/bf+Dm4cJqclaOSGNPNeYnZGyxElCabHVvPTATuMqFSypnVNE3S1hi4dDY7hc4nPjra+uCEN8I+qmdrNi1HZ882ZMMN1aC3Ezr76+vY+4Kc/urj5Wxj2eIL4lZLkjgaFWTBrVhiyYACY3T8txzBuI2DzSiYgZzmUXXZ5nPLHss5ZUfwgnfxUnEXkDUtn20rap1zyUu7Zf+YNS4bpXK9dnKiBzyfA6i5wb1GaWVYso1gUmIcnqgN8J29J4aH0Qt82Aw5GomEoSOLY3BWWkadAfZar7XnJqKe2sLmhjodbmwLSan6owh1W4IFQwiV2PavNtp0= X-Microsoft-Antispam-PRVS: X-Exchange-Antispam-Report-Test: UriScan:; X-Exchange-Antispam-Report-CFA-Test: BCL:0;PCL:0;RULEID:(601004)(2401047)(520078)(5005006)(8121501046)(3002001);SRVR:BY1PR0301MB1269;BCL:0;PCL:0;RULEID:;SRVR:BY1PR0301MB1269; X-Forefront-PRVS: 071518EF63 X-Microsoft-Exchange-Diagnostics: =?us-ascii?Q?1;BY1PR0301MB1269;23:5oElqAfcdb9l/yRKRvOEQSZGZBU4bqT4pgK0wjQ?= =?us-ascii?Q?6cbK2wSX+VkefOslf6uSnNSq5FLSOMfEtpRab49HwiqChEgQ1dRjMrFEpvKa?= =?us-ascii?Q?xN5I+7Yd8fQdY0eXdux84NeseB03Y/9kGCqf+RIGM2qc6P/wANhB2L3uDP0t?= =?us-ascii?Q?vjwbBPUe/plGypwYoh8wBu5PqZgT0rUeYtuOuSfVv9uxMndFwwHMOMf86huv?= =?us-ascii?Q?LvJqkLM26lE3qsAoz6Rm+clCF4GTWJNWzqWAiR83uoWrIjM0uJ/bIZNBbFbR?= =?us-ascii?Q?JcoskTtIfazUAuwi4dT2O3s2oI/Wi6PFZXQmZqHwLRpJ9rJKnRBZlyPJJ4LZ?= =?us-ascii?Q?Bbpe7y0fRN+Vz/UJ6KTldDNrY6W8ENr/w+1tHVKSEc02XeP0V7mqoQ8IheZM?= =?us-ascii?Q?xyeRGU3Dkeljh9zAmxL/LaP8fPb7meNDbJv9x73+eoH2+WHs05yZnupdnKBP?= =?us-ascii?Q?Uqzu+Q9WS1oKrAv2yybY+Wwc5kd5bqIpGlOACrTzgnCy1Rd/GlUJmeHffH2T?= =?us-ascii?Q?TPZjjdHiYZRNDMOtNHrsW2PgnoZkDqdsS2uypRqzXsJO/szRReLXXEIwTOPV?= =?us-ascii?Q?5IWlAHWkV8qvXLdCYkJMpjR88qHQjPHOhs3V5Es24+7nxq/RBsY+bBo4ktZg?= =?us-ascii?Q?S6YWHOvSWtRqECGt1cxW1NcQ2/aoEKK0SJGG9wqSIj25E1ZFfZXpz52Zoslr?= =?us-ascii?Q?nM2zB81ZHuWvG9kFgzv/F7crCd4Gi6kwomJivfzPv5ysNirU81btQgzNashX?= =?us-ascii?Q?RykJk7cjL9LivGHOxVilz03ihnXrngBK5zc/SWPl7iU68Qrbhs2jp6nD0GJw?= =?us-ascii?Q?i7tYUh0FInETmJ5GnNf2NJtiXrhZxpPARE0o39n8pg/1o2BGBF4OQSlukpFU?= =?us-ascii?Q?u6iZKQG3eQuYiQFG0lgstjzhrNuTQbpBCRpT3dEMhafI57e+obufQ2/zz/26?= =?us-ascii?Q?Ba0n4NoS5L5zTXLOIeHJjCGs/8t9f71DFqzMi47+G4hEGdsVkPTg+5kYnlvH?= =?us-ascii?Q?K/r+Etu7x9APDC2Bqqe44aK2CkxN8A4ZiMQ4w3K7N5FuTr8u3UsnQBfYYy6B?= =?us-ascii?Q?PQg4EgFFXSrT/FseMuZd5P4/EhNfaMTr23Kbx75RuvH1Jm3Cph1ZlGPgyjez?= =?us-ascii?Q?ubD4EuX4VtXG60r8/6Wy2tmgDYJIQJ39lT4vv6pyBuWqxCkYdgAlbmUtlCMM?= =?us-ascii?Q?Y1Gy3muTaVmFumLH9wlj5I3rQGGAK7XRCrgxVGzuMcJGDC9V599aQMRl336F?= =?us-ascii?Q?qBLbEYbbC6f6AS5yVwQD322Hoc5lgRjjs4N/CALr7ZoD+g590K+x5gzWWIjt?= =?us-ascii?Q?+g8CBn6f0Wu9JGP9FTjIg7smKW/NNEXDcwySE6jlToe4LpWhjFb4jAHQhbGl?= =?us-ascii?Q?DK19qJZq0utpgEFwj24L7Mzz5kk0T5BCITuLVtmzlU7yUEszX?= X-Microsoft-Exchange-Diagnostics: 1;BY1PR0301MB1269;5:dgAetJ2DnCSArrAuaNrv2CRRz95plcJGbFPR3VcfyqrwSg0h7mE0q+Of5F+UOhOUhgmDVOL8dPYpFyOhBhfNvfqR5Bmh5ffHWE+8wfgv3LMaPCgElJWpIxRdlAElbTefBL72hu47x1nKhSNi4y1ipA==;24:/TcgJJfGnfWsixSD/2jowu5lGyDt1ccdxx8a+O+HxnZXdj03WPKwhtMLo2HV3NSCxDw9wFkKW0VgLbSTc8CbvKZTpro17YWUBirlArmjdoo=;20:8S7+12G92w0Zn8DUCur4or6lMTpkkCUc7JEOufJ/ARlU26yn5r442ICV8UOzi5S1tNxR1quaoChGz3MB3Om7Tw== SpamDiagnosticOutput: 1:23 SpamDiagnosticMetadata: NSPM X-OriginatorOrg: freescale.com X-MS-Exchange-CrossTenant-OriginalArrivalTime: 30 Sep 2015 08:24:16.9009 (UTC) X-MS-Exchange-CrossTenant-Id: 710a03f5-10f6-4d38-9ff4-a80b81da590d X-MS-Exchange-CrossTenant-OriginalAttributedTenantConnectingIp: TenantId=710a03f5-10f6-4d38-9ff4-a80b81da590d;Ip=[192.88.158.2];Helo=[az84smr01.freescale.net] X-MS-Exchange-CrossTenant-FromEntityHeader: HybridOnPrem X-MS-Exchange-Transport-CrossTenantHeadersStamped: BY1PR0301MB1269 Sender: linux-kernel-owner@vger.kernel.org List-ID: X-Mailing-List: linux-kernel@vger.kernel.org On Fri, Sep 25, 2015 at 07:57:08PM +0200, Anton Bondarenko wrote: > RX DMA tail data handling doesn't work correctly in many cases with > current implementation. It happens because SPI core was setup > to generates both RX watermark level and RX DATA TAIL events > incorrectly. SPI transfer triggering for DMA also done in wrong way. > > SPI client wants to transfer 70 words for example. The old DMA > implementation setup RX DATA TAIL equal 6 words. In this case > RX DMA event will be generated after 6 words read from RX FIFO. > The garbage can be read out from RX FIFO because SPI HW does > not receive all required words to trigger RX watermark event. > > New implementation change handling of RX data tail. DMA is used to process > all TX data and only full chunks of RX data with size aligned to FIFO/2. > Driver is waiting until both TX and RX DMA transaction done and all > TX data are pushed out. At that moment there is only RX data tail in > the RX FIFO. This data read out using PIO. > > Transfer triggering changed to avoid RX data loss. > > Signed-off-by: Anton Bondarenko > --- > drivers/spi/spi-imx.c | 105 +++++++++++++++++++++++++++++++------------------- > 1 file changed, 66 insertions(+), 39 deletions(-) > > diff --git a/drivers/spi/spi-imx.c b/drivers/spi/spi-imx.c > index f9deb84..165bc2c 100644 > --- a/drivers/spi/spi-imx.c > +++ b/drivers/spi/spi-imx.c > @@ -39,6 +39,8 @@ > #include > #include > > +#include > + > #include > #include > > @@ -53,6 +55,7 @@ > /* generic defines to abstract from the different register layouts */ > #define MXC_INT_RR (1 << 0) /* Receive data ready interrupt */ > #define MXC_INT_TE (1 << 1) /* Transmit FIFO empty interrupt */ > +#define MXC_INT_TCEN BIT(7) /* Transfer complete */ > > /* The maximum bytes that a sdma BD can transfer.*/ > #define MAX_SDMA_BD_BYTES (1 << 15) > @@ -104,9 +107,7 @@ struct spi_imx_data { > unsigned int dma_is_inited; > unsigned int dma_finished; > bool usedma; > - u32 rx_wml; > - u32 tx_wml; > - u32 rxt_wml; > + u32 wml; > struct completion dma_rx_completion; > struct completion dma_tx_completion; > > @@ -201,9 +202,8 @@ static bool spi_imx_can_dma(struct spi_master *master, struct spi_device *spi, > { > struct spi_imx_data *spi_imx = spi_master_get_devdata(master); > > - if (spi_imx->dma_is_inited > - && transfer->len > spi_imx->rx_wml * sizeof(u32) > - && transfer->len > spi_imx->tx_wml * sizeof(u32)) > + if (spi_imx->dma_is_inited && > + (transfer->len > spi_imx->wml * sizeof(u32))) Add Sascha in the loop. I don't think "* sizeof(u32)", since even 1 byte data will consume one position of 32bit FIFO Thus if here spi_imx->wml = spi_imx_get_fifosize(spi_imx) / 2 = 32, the threshold value which judge DMA mode used or not should be 32 not 32 * 4. Of course, it will not cause any function break since both DMA and PIO can work ,but I think we'd better correct it. > return true; > return false; > } > @@ -228,6 +228,7 @@ static bool spi_imx_can_dma(struct spi_master *master, struct spi_device *spi, > #define MX51_ECSPI_INT 0x10 > #define MX51_ECSPI_INT_TEEN (1 << 0) > #define MX51_ECSPI_INT_RREN (1 << 3) > +#define MX51_ECSPI_INT_TCEN BIT(7) > > #define MX51_ECSPI_DMA 0x14 > #define MX51_ECSPI_DMA_TX_WML_OFFSET 0 > @@ -292,6 +293,9 @@ static void __maybe_unused mx51_ecspi_intctrl(struct spi_imx_data *spi_imx, int > if (enable & MXC_INT_RR) > val |= MX51_ECSPI_INT_RREN; > > + if (enable & MXC_INT_TCEN) > + val |= MX51_ECSPI_INT_TCEN; > + > writel(val, spi_imx->base + MX51_ECSPI_INT); > } > > @@ -311,8 +315,9 @@ static void __maybe_unused mx51_ecspi_trigger(struct spi_imx_data *spi_imx) > static int __maybe_unused mx51_ecspi_config(struct spi_imx_data *spi_imx, > struct spi_imx_config *config) > { > - u32 ctrl = MX51_ECSPI_CTRL_ENABLE, cfg = 0, dma = 0; > - u32 tx_wml_cfg, rx_wml_cfg, rxt_wml_cfg; > + u32 ctrl = MX51_ECSPI_CTRL_ENABLE, dma = 0; > + u32 cfg = readl(spi_imx->base + MX51_ECSPI_CONFIG); > + > u32 clk = config->speed_hz, delay; > > /* > @@ -369,19 +374,10 @@ static int __maybe_unused mx51_ecspi_config(struct spi_imx_data *spi_imx, > * and enable DMA request. > */ > if (spi_imx->dma_is_inited) { > - dma = readl(spi_imx->base + MX51_ECSPI_DMA); > - > - spi_imx->rxt_wml = spi_imx_get_fifosize(spi_imx) / 2; > - rx_wml_cfg = spi_imx->rx_wml << MX51_ECSPI_DMA_RX_WML_OFFSET; > - tx_wml_cfg = spi_imx->tx_wml << MX51_ECSPI_DMA_TX_WML_OFFSET; > - rxt_wml_cfg = spi_imx->rxt_wml << MX51_ECSPI_DMA_RXT_WML_OFFSET; > - dma = (dma & ~MX51_ECSPI_DMA_TX_WML_MASK > - & ~MX51_ECSPI_DMA_RX_WML_MASK > - & ~MX51_ECSPI_DMA_RXT_WML_MASK) > - | rx_wml_cfg | tx_wml_cfg | rxt_wml_cfg > - |(1 << MX51_ECSPI_DMA_TEDEN_OFFSET) > - |(1 << MX51_ECSPI_DMA_RXDEN_OFFSET) > - |(1 << MX51_ECSPI_DMA_RXTDEN_OFFSET); > + dma = (spi_imx->wml - 1) << MX51_ECSPI_DMA_RX_WML_OFFSET > + | (spi_imx->wml - 1) << MX51_ECSPI_DMA_TX_WML_OFFSET > + | (1 << MX51_ECSPI_DMA_TEDEN_OFFSET) > + | (1 << MX51_ECSPI_DMA_RXDEN_OFFSET); Please set tx threshold as 0 as your v1 patch if I remember right, as our internal tree done: http://git.freescale.com/git/cgit.cgi/imx/linux-2.6-imx.git/commit/drivers/spi/spi-imx.c?h=imx_3.14.28_7d_alpha&id=2e7615e2f399e39c58dd31f84a31f7c2592da7e7 > > writel(dma, spi_imx->base + MX51_ECSPI_DMA); > } > @@ -825,6 +821,8 @@ static int spi_imx_sdma_init(struct device *dev, struct spi_imx_data *spi_imx, > if (of_machine_is_compatible("fsl,imx6dl")) > return 0; > > + spi_imx->wml = spi_imx_get_fifosize(spi_imx) / 2; > + > /* Prepare for TX DMA: */ > master->dma_tx = dma_request_slave_channel(dev, "tx"); > if (!master->dma_tx) { > @@ -836,7 +834,8 @@ static int spi_imx_sdma_init(struct device *dev, struct spi_imx_data *spi_imx, > slave_config.direction = DMA_MEM_TO_DEV; > slave_config.dst_addr = res->start + MXC_CSPITXDATA; > slave_config.dst_addr_width = DMA_SLAVE_BUSWIDTH_1_BYTE; > - slave_config.dst_maxburst = spi_imx_get_fifosize(spi_imx) / 2; > + slave_config.dst_maxburst = spi_imx_get_fifosize(spi_imx) > + - spi_imx->wml; slave_config.dst_maxburst = spi_imx->wml;? > ret = dmaengine_slave_config(master->dma_tx, &slave_config); > if (ret) { > dev_err(dev, "error in TX dma configuration.\n"); > @@ -854,7 +853,8 @@ static int spi_imx_sdma_init(struct device *dev, struct spi_imx_data *spi_imx, > slave_config.direction = DMA_DEV_TO_MEM; > slave_config.src_addr = res->start + MXC_CSPIRXDATA; > slave_config.src_addr_width = DMA_SLAVE_BUSWIDTH_1_BYTE; > - slave_config.src_maxburst = spi_imx_get_fifosize(spi_imx) / 2; > + slave_config.src_maxburst = spi_imx_get_fifosize(spi_imx) > + - spi_imx->wml; slave_config.src_maxburst = spi_imx->wml;? > ret = dmaengine_slave_config(master->dma_rx, &slave_config); > if (ret) { > dev_err(dev, "error in RX dma configuration.\n"); > @@ -867,8 +867,6 @@ static int spi_imx_sdma_init(struct device *dev, struct spi_imx_data *spi_imx, > master->max_dma_len = MAX_SDMA_BD_BYTES; > spi_imx->bitbang.master->flags = SPI_MASTER_MUST_RX | > SPI_MASTER_MUST_TX; > - spi_imx->tx_wml = spi_imx_get_fifosize(spi_imx) / 2; > - spi_imx->rx_wml = spi_imx_get_fifosize(spi_imx) / 2; > spi_imx->dma_is_inited = 1; > > return 0; > @@ -897,8 +895,7 @@ static int spi_imx_dma_transfer(struct spi_imx_data *spi_imx, > struct dma_async_tx_descriptor *desc_tx = NULL, *desc_rx = NULL; > int ret; > unsigned long timeout; > - u32 dma; > - int left; > + const int left = transfer->len % spi_imx->wml; > struct spi_master *master = spi_imx->bitbang.master; > struct sg_table *tx = &transfer->tx_sg, *rx = &transfer->rx_sg; > > @@ -915,9 +912,23 @@ static int spi_imx_dma_transfer(struct spi_imx_data *spi_imx, > } > > if (rx) { > + /* Cut RX data tail */ > + const unsigned int old_nents = rx->nents; > + > + WARN_ON(sg_dma_len(&rx->sgl[rx->nents - 1]) < left); > + sg_dma_len(&rx->sgl[rx->nents - 1]) -= left; > + if (sg_dma_len(&rx->sgl[rx->nents - 1]) == 0) > + --rx->nents; > + > desc_rx = dmaengine_prep_slave_sg(master->dma_rx, > rx->sgl, rx->nents, DMA_DEV_TO_MEM, > DMA_PREP_INTERRUPT | DMA_CTRL_ACK); > + > + /* Restore old SG table state */ > + if (old_nents > rx->nents) > + ++rx->nents; > + sg_dma_len(&rx->sgl[rx->nents - 1]) += left; > + > if (!desc_rx) > goto no_dma; > > @@ -932,17 +943,10 @@ static int spi_imx_dma_transfer(struct spi_imx_data *spi_imx, > /* Trigger the cspi module. */ > spi_imx->dma_finished = 0; > > - dma = readl(spi_imx->base + MX51_ECSPI_DMA); > - dma = dma & (~MX51_ECSPI_DMA_RXT_WML_MASK); > - /* Change RX_DMA_LENGTH trigger dma fetch tail data */ > - left = transfer->len % spi_imx->rxt_wml; > - if (left) > - writel(dma | (left << MX51_ECSPI_DMA_RXT_WML_OFFSET), > - spi_imx->base + MX51_ECSPI_DMA); > + dma_async_issue_pending(master->dma_rx); > + dma_async_issue_pending(master->dma_tx); > spi_imx->devtype_data->trigger(spi_imx); > > - dma_async_issue_pending(master->dma_tx); > - dma_async_issue_pending(master->dma_rx); why change the sequence of issue_pending and trigger? I don't think need to do so. > /* Wait SDMA to finish the data transfer.*/ > timeout = wait_for_completion_timeout(&spi_imx->dma_tx_completion, > IMX_DMA_TIMEOUT); > @@ -951,6 +955,7 @@ static int spi_imx_dma_transfer(struct spi_imx_data *spi_imx, > dev_driver_string(&master->dev), > dev_name(&master->dev)); > dmaengine_terminate_all(master->dma_tx); > + dmaengine_terminate_all(master->dma_rx); > } else { > timeout = wait_for_completion_timeout( > &spi_imx->dma_rx_completion, IMX_DMA_TIMEOUT); > @@ -960,10 +965,32 @@ static int spi_imx_dma_transfer(struct spi_imx_data *spi_imx, > dev_name(&master->dev)); > spi_imx->devtype_data->reset(spi_imx); > dmaengine_terminate_all(master->dma_rx); > + } else if (left) { > + void *pio_buffer = transfer->rx_buf > + + (transfer->len - left); > + > + dma_sync_sg_for_cpu(master->dma_rx->device->dev, > + rx->sgl, rx->nents, > + DMA_FROM_DEVICE); Only the last entry needed: dma_sync_sg_for_cpu(master->dma_rx->device->dev, rx->sgl[rx->nents - 1], 1, DMA_FROM_DEVICE); > + > + spi_imx->rx_buf = pio_buffer; > + spi_imx->txfifo = left; > + reinit_completion(&spi_imx->xfer_done); > + > + spi_imx->devtype_data->intctrl(spi_imx, MXC_INT_TCEN); > + > + timeout = wait_for_completion_timeout( > + &spi_imx->xfer_done, IMX_DMA_TIMEOUT); > + if (!timeout) { > + pr_warn("%s %s: I/O Error in RX tail\n", > + dev_driver_string(&master->dev), > + dev_name(&master->dev)); > + } > + > + dmac_flush_range(pio_buffer, pio_buffer + left); > + outer_flush_range(virt_to_phys(pio_buffer), > + virt_to_phys(pio_buffer) + left); > } > - writel(dma | > - spi_imx->rxt_wml << MX51_ECSPI_DMA_RXT_WML_OFFSET, > - spi_imx->base + MX51_ECSPI_DMA); > } > > spi_imx->dma_finished = 1; > -- > 2.5.2 >