From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S932232AbbKCHKo (ORCPT ); Tue, 3 Nov 2015 02:10:44 -0500 Received: from mail-bn1on0134.outbound.protection.outlook.com ([157.56.110.134]:25372 "EHLO na01-bn1-obe.outbound.protection.outlook.com" rhost-flags-OK-OK-OK-FAIL) by vger.kernel.org with ESMTP id S1752724AbbKCHKl (ORCPT ); Tue, 3 Nov 2015 02:10:41 -0500 Authentication-Results: spf=fail (sender IP is 192.88.168.50) smtp.mailfrom=freescale.com; pengutronix.de; dkim=none (message not signed) header.d=none;pengutronix.de; dmarc=none action=none header.from=freescale.com; Date: Tue, 3 Nov 2015 15:08:40 +0800 From: Robin Gong To: Anton Bondarenko CC: , , , , , , Subject: Re: [PATCH v3 1/7] spi: imx: Fix DMA transfer Message-ID: <20151103070837.GA15428@shlinux2> References: <1446388901-6073-1-git-send-email-anton.bondarenko.sama@gmail.com> <1446388901-6073-2-git-send-email-anton.bondarenko.sama@gmail.com> MIME-Version: 1.0 Content-Type: text/plain; charset="us-ascii" Content-Disposition: inline In-Reply-To: <1446388901-6073-2-git-send-email-anton.bondarenko.sama@gmail.com> User-Agent: Mutt/1.5.21 (2010-09-15) X-EOPAttributedMessage: 0 X-Microsoft-Exchange-Diagnostics: 1;BY2FFO11FD035;1:m2B75JwpPsjmcupuYCETUPJWJ9PQnY0d3qaESfBA49kUHzqd3QKCce2Vmlz8yXf1DLRE6th3u4KkJSFRnaiCe3GIs1hy81rHWQkLVDkGNZ1LWYXGt5QLuFaB40l9a3CnJKPqhcBZypqRQ4znQ3msnQNj0Ks1gzDfhtGWFSZP7QP7vhDzgCtnlzep1L2tpq2gfCZx4F1vkONr4A6/7Bj3o8qtlR7nuH/JnSkn9pCGETnpL6cKx3VGdNIdwY80yql3izFrn2PqQ+z/8DAzzfHk0F1QCKRUgtmS1Nt0fp6ZVTCEIdh/+eb2JeF4uOB5ueZ33jWtnOtik1a6XbP+zPaEy6m4vzYZQIiJJlOIFas+f7FeabjXPjY92Xl6Pq0JZM0A X-Forefront-Antispam-Report: CIP:192.88.168.50;CTRY:US;IPV:NLI;EFV:NLI;SFV:NSPM;SFS:(10019020)(6009001)(2980300002)(1109001)(1110001)(339900001)(189002)(24454002)(199003)(47776003)(77096005)(104016004)(97756001)(6806005)(5007970100001)(97736004)(81156007)(19580405001)(4001350100001)(19580395003)(106466001)(105606002)(33716001)(46406003)(87936001)(83506001)(50466002)(2950100001)(217423001)(110136002)(85426001)(551934003)(5001960100002)(76176999)(5008740100001)(23726002)(92566002)(189998001)(33656002)(54356999)(50986999)(42262002);DIR:OUT;SFP:1102;SCL:1;SRVR:SN2PR03MB093;H:tx30smr01.am.freescale.net;FPR:;SPF:Fail;PTR:InfoDomainNonexistent;MX:1;A:1;LANG:en; X-Microsoft-Exchange-Diagnostics: 1;SN2PR03MB093;2:A8gKZoChJXKHVM/qcSulbTAZxRQSJcu9rFr52MwznRlzanh6+SJ8CAidJ3qs/zXUUK0ejY4CRfMLVVH0ZZc/OJrpZRHEQrH4TaggOVQbX/sFzJi97aFGswz7NHQ3gR3Riv6c35YBo+hxwdrImgmiZUd3tXXD2ivji16GkXjmF28=;3:U1U/m1MTY9rtatcmtNdc+i71ZSUQaEpwcNWvrr9AWJ0CEAXebwTwptzqpXOLf9XsFsd1rrsSs+TtTfYsGV7H7fWQ4sxZ8Hsp2QRWdcX33CZqwceSqsjUHqrcdhyY2Q4EbrQiQfn3tgu3CfGg6dGxjwBX53bqCN5EeAgCvqs1EV9ESFKhhCGlHHzAA5XlgFdD5QWtogkrRG5qZ06p7jfkuGx4ep7U7oRjyYT0RZvwWYo=;25:/SaIfaYet82L0/BEWyYTXN6DzHzcqVgbupw4cCNyKhaqYRACVzj8NOAU/wGZI1a1AYVRCb2dg/QxenoQ3WYVyr9VplQhs24XwNFPSUsgtqkcG3Amz66F7NF4/ZrqqUlUYZ/vgXA1vuBp+pNRSs3N8cw2MsUSLW2zQvPovojfLWCIGL9LnxRSplvFPZDb1feZ7pqps7SkJN6ifZwFCbDQGpfPqwOHJomine+ORhC96caXzbZVk5qy2Y7SjOnVs880WL6VJ4aS4eCcGxfRUussKQ== X-Microsoft-Antispam: UriScan:;BCL:0;PCL:0;RULEID:;SRVR:SN2PR03MB093; X-Microsoft-Exchange-Diagnostics: 1;SN2PR03MB093;20:C6vnlmJEgAz50oIM4wFmB6ifAue7E5TiWRjiFjtl1w1lJG/BCdX+HJ/IkZa7DF2OfcQSHoFkY/Herc2s4zuPcFncKuaw7CDP1cH5epUuZW20ZCEbUrHtzpp5xBmmZW/AS5hjCq3ZIvNhCXPDro+dv0PBrfjxk89bXEE6atNzy/UYyQydxgu9ArpaPxS6TRp/JK4YKjeucL81BHm8V4jQssbNGxUhRaQL1vlZBKH8BtgAtmbe/Gi8Aie2ly2CNKwrgKaAqmE1cTLYqoy0i9y4WpGKcOia0RcMOzp2jcGLAYVslJZWnvbYS8iQSNNWxjCSppFB0V7QcsWPsHalBmWRHyrB0Cj9WnNrl81d397doj0=;4:oCqPgoDWiQq9uFNK8cB36Z017BZJrYi5t7GrNy33SLeN9i4lQMj6vULgAKBM+98TutAfTSBC4P6Gbbd06SaGXCVhih/Js8Tfj5VclnARgKEPb0YZcj0b/WfqS6s06e2Mjxu9lJovEL1pjaPozrDnCjI0Jw3d7OhoCbMs3cXCmsWn267ubxSTrDosXXCEm3teMNjrjTIgXjXqybHWYU0ds+Mq+CWb/zx3x6f9URLXUumBlzP2/+v2v8gFAu9AU4i/i9uYEIyk8VIrzgHQS64Ze5VK/nduwSydEjopCzxBMQ0BvcvRgvzw4bzAp3SEOYpX3RQoVMi92o4bUsf3lj10kfYSCWSYME2AymOGTNdKb7nehTAZzsQQDTe1f882DFVf X-Microsoft-Antispam-PRVS: X-Exchange-Antispam-Report-Test: UriScan:; X-Exchange-Antispam-Report-CFA-Test: BCL:0;PCL:0;RULEID:(601004)(2401047)(5005006)(520078)(8121501046)(3002001)(10201501046);SRVR:SN2PR03MB093;BCL:0;PCL:0;RULEID:;SRVR:SN2PR03MB093; X-Forefront-PRVS: 0749DC2CE6 X-Microsoft-Exchange-Diagnostics: =?us-ascii?Q?1;SN2PR03MB093;23:DX+NDiF63zFlA2ta0OJlPY1aeAs+pFwml1/TjOKstg?= =?us-ascii?Q?//YdAujYHOQmsSryabcnkDu1sOstdsQxR+fDHyxen2X7E+cUMkVGtXRo3gvJ?= =?us-ascii?Q?eSpuwIV7fwVSAQOj3xEyjHnitx6V0rNSsjmljBXhjnTiha3ujUYsBVEnXpjy?= =?us-ascii?Q?OV0XqiPBxvU8+bkoMu3o2LOehmMtUtdYxHw3sGWgQqy+Wr1cijSEE8jPZhFK?= =?us-ascii?Q?tO5FvEkHPzrZQWzpntlY4sBT2lsxgtPuc0lzgoHM1eq8BKEFpr9/b65vDceQ?= =?us-ascii?Q?nyGewWY9GlxE5FzmPMvcweMy4qmYksFtTafgMfDvQi2QG6TRQtUIp4mw2WLD?= =?us-ascii?Q?bjkSl0vKabGR2nJjcgbWI4Lu4Vhm1TmDhFmuu08AdJTm8ax2NsSfprU8ijcg?= =?us-ascii?Q?4iZwlENPTY5Iawz5mLyzriajn4Ya2knNg6YH1znjj/b1FjiqtxFuDdaCr9Pf?= =?us-ascii?Q?NY+1h/Rq/U1pTgagAKnggRoHxTDJThr8oQ5ypGHoaG4qbF+QKFTHTjMDwFlT?= =?us-ascii?Q?k/u0WInQPy8rBie7gWDPQ+2N2QzqUElTSDtaP+kjZ+nYGz1eUCJzMDXKv2my?= =?us-ascii?Q?/s4DIKvGJx6S8c3jz8d1Oo6GtuxHieCevy+OE54SBdTXR145lmvteiXafIYZ?= =?us-ascii?Q?DWBAHHvtL661kZRAyLqJvOxLfL1JaeKbNZ+qLKinOjRuJyNgtRBvxeWLPtXC?= =?us-ascii?Q?l7TsLtdDRMSLdawuBwxoaT+J2Ad/Y040OMwv8bf5hHnNj9Hez7YeSUjAji1y?= =?us-ascii?Q?UnfEZjVDrD3BUGfcbBSJg01VpX9utVVpm56+/ZtrvNpW3a3SLDT+YOsyxnXh?= =?us-ascii?Q?T0i8r8A7jBj0YVdEQFtUAz5v2gZeOMeGiKdKL4NeZvyxLtfFZTx1Y0u78ue9?= =?us-ascii?Q?/Pf3nJi6ktOa3xWw341+9gGmqVdlFVQ/ILs28/wrDGqzOQblsVIe2elEnFQR?= =?us-ascii?Q?vZyyO4OfPT8/fdUzjUTlmkIoCHMyILVj81MdKJQwmv0fDPWjDX4XuRDShZUH?= =?us-ascii?Q?TRt4aYcQsy1+82jxPvupli/iTa5y6gI8IZfKVzFpXmWR9QhKxteHtbwHakN/?= =?us-ascii?Q?G5mlcjNLurjl3O0iUPg+GCaQb88A7Sl6C86V+WpptqhgiLdQ=3D=3D?= X-Microsoft-Exchange-Diagnostics: 1;SN2PR03MB093;5:wYKFcriuNDTHApT61ECW1DtLewPD0o662HAgbvrcaeK26Hpo07eipYQFVWN4+US6bO1W/4awGWLmIgb101/taCtN91wSd2RyXAxFnNkWLE0buLo5WHaaO/WClpNAvKAOt9BUESh81B3VI8IKw9p4hw==;24:CrUDwG4XTBhefFGOolUN4uwv+yt1BqTG8j7MsVqTgCBVzyoGyPZQ78joHqZK9Z1E7oHntPugzx5PmZllpUKs6sT2L3b1oQaESzRB9D2q4NA=;20:vfgPtnWjOeio3DgglW7pkRE2rBBvYOicQajdUeQ3dUaZJ7PhnnAPzwwzT0fPUUsFHcULlqHPazan6ZRbxfnoDQ== SpamDiagnosticOutput: 1:23 SpamDiagnosticMetadata: NSPM X-OriginatorOrg: freescale.com X-MS-Exchange-CrossTenant-OriginalArrivalTime: 03 Nov 2015 07:10:35.0634 (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.168.50];Helo=[tx30smr01.am.freescale.net] X-MS-Exchange-CrossTenant-FromEntityHeader: HybridOnPrem X-MS-Exchange-Transport-CrossTenantHeadersStamped: SN2PR03MB093 Sender: linux-kernel-owner@vger.kernel.org List-ID: X-Mailing-List: linux-kernel@vger.kernel.org On Sun, Nov 01, 2015 at 03:41:35PM +0100, Anton Bondarenko wrote: > From: Anton Bondarenko > > 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 | 115 +++++++++++++++++++++++++++++++++----------------- > 1 file changed, 76 insertions(+), 39 deletions(-) > > diff --git a/drivers/spi/spi-imx.c b/drivers/spi/spi-imx.c > index 0e5723a..bd7b721 100644 > --- a/drivers/spi/spi-imx.c > +++ b/drivers/spi/spi-imx.c > @@ -53,6 +53,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 +105,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 +200,7 @@ 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) > return true; > return false; > } > @@ -228,6 +225,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 +290,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 +312,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; > > /* > @@ -376,19 +378,9 @@ 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 > + | (1 << MX51_ECSPI_DMA_TEDEN_OFFSET) > + | (1 << MX51_ECSPI_DMA_RXDEN_OFFSET); > > writel(dma, spi_imx->base + MX51_ECSPI_DMA); > } > @@ -832,6 +824,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) { > @@ -843,7 +837,7 @@ 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->wml; > ret = dmaengine_slave_config(master->dma_tx, &slave_config); > if (ret) { > dev_err(dev, "error in TX dma configuration.\n"); > @@ -861,7 +855,7 @@ 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->wml; > ret = dmaengine_slave_config(master->dma_rx, &slave_config); > if (ret) { > dev_err(dev, "error in RX dma configuration.\n"); > @@ -874,8 +868,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; > @@ -904,8 +896,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; > > @@ -922,9 +913,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; > > @@ -939,17 +944,18 @@ 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); > + /* > + * Set these order to avoid potential RX overflow. The overflow may > + * happen if we enable SPI HW before starting RX DMA due to rescheduling > + * for another task and/or interrupt. > + * So RX DMA enabled first to make sure data would be read out from FIFO > + * ASAP. TX DMA enabled next to start filling TX FIFO with new data. > + * And finaly SPI HW enabled to start actual data transfer. > + */ > + 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); > /* Wait SDMA to finish the data transfer.*/ > timeout = wait_for_completion_timeout(&spi_imx->dma_tx_completion, > IMX_DMA_TIMEOUT); > @@ -958,6 +964,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); > @@ -967,10 +974,40 @@ 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 - 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)); > + } > + > + /* > + * WARNING: this call will cause DMA debug complains > + * about wrong combination of DMA direction and sync > + * function. But we must use it to make sure the data > + * read by PIO mode will be cleared from CPU cache. > + * Otherwise SPI core will invalidate it during unmap of > + * SG buffers. > + */ > + dma_sync_sg_for_device(master->dma_rx->device->dev, > + &rx->sgl[rx->nents - 1], 1, > + DMA_TO_DEVICE); I think the above dma_sync_sg_for_cpu for reading the last sgl by PIO mode is enough, that move the right data into rx_buf which map by SPI core. And why 'DMA_TO_DEVICE' for rx here? > } > - writel(dma | > - spi_imx->rxt_wml << MX51_ECSPI_DMA_RXT_WML_OFFSET, > - spi_imx->base + MX51_ECSPI_DMA); > } > > spi_imx->dma_finished = 1; > -- > 2.6.2 >