summaryrefslogtreecommitdiff
path: root/drivers/spi
diff options
context:
space:
mode:
authorEugeniy Paltsev <Eugeniy.Paltsev@synopsys.com>2018-03-22 13:50:43 +0300
committerJagan Teki <jagan@amarulasolutions.com>2018-03-22 20:31:35 +0300
commitc6b4f031d96a4e1d59761b294829b058b098f3df (patch)
treeb4b0c7dc260387c4b5f7dcf4fea03109c61b071e /drivers/spi
parent2511930193a420eb8bb6cfa9c60912626f68ae67 (diff)
downloadu-boot-c6b4f031d96a4e1d59761b294829b058b098f3df.tar.xz
DW SPI: fix tx data loss on FIFO flush
In current implementation if some data still exists in Tx FIFO it can be silently flushed, i.e. dropped on disabling of the controller, which happens when writing 0 to DW_SPI_SSIENR (it happens in the beginning of new transfer) So add wait for current transmit operation to complete to be sure that current transmit operation is finished before new one. Signed-off-by: Eugeniy Paltsev <Eugeniy.Paltsev@synopsys.com> Reviewed-by: Jagan Teki <jagan@openedev.com>
Diffstat (limited to 'drivers/spi')
-rw-r--r--drivers/spi/designware_spi.c15
1 files changed, 15 insertions, 0 deletions
diff --git a/drivers/spi/designware_spi.c b/drivers/spi/designware_spi.c
index c501aeea16..5e196b21c9 100644
--- a/drivers/spi/designware_spi.c
+++ b/drivers/spi/designware_spi.c
@@ -18,6 +18,7 @@
#include <spi.h>
#include <fdtdec.h>
#include <linux/compat.h>
+#include <linux/iopoll.h>
#include <asm/io.h>
DECLARE_GLOBAL_DATA_PTR;
@@ -342,6 +343,7 @@ static int dw_spi_xfer(struct udevice *dev, unsigned int bitlen,
u8 *rx = din;
int ret = 0;
u32 cr0 = 0;
+ u32 val;
u32 cs;
/* spi core configured to do 8 bit transfers */
@@ -394,6 +396,19 @@ static int dw_spi_xfer(struct udevice *dev, unsigned int bitlen,
/* Start transfer in a polling loop */
ret = poll_transfer(priv);
+ /*
+ * Wait for current transmit operation to complete.
+ * Otherwise if some data still exists in Tx FIFO it can be
+ * silently flushed, i.e. dropped on disabling of the controller,
+ * which happens when writing 0 to DW_SPI_SSIENR which happens
+ * in the beginning of new transfer.
+ */
+ if (readl_poll_timeout(priv->regs + DW_SPI_SR, val,
+ !(val & SR_TF_EMPT) || (val & SR_BUSY),
+ RX_TIMEOUT * 1000)) {
+ ret = -ETIMEDOUT;
+ }
+
return ret;
}