diff --git a/drivers/spi/spi-sifive.c b/drivers/spi/spi-sifive.c
index 0ea4930a0a..4cab0391f7 100644
--- a/drivers/spi/spi-sifive.c
+++ b/drivers/spi/spi-sifive.c
@@ -11,6 +11,7 @@
 #include <dm/device_compat.h>
 #include <malloc.h>
 #include <spi-mem.h>
+#include <wait_bit.h>
 #include <asm/io.h>
 #include <linux/log2.h>
 #include <clk.h>
@@ -134,8 +135,8 @@ static void sifive_spi_clear_cs(struct sifive_spi *spi)
 }
 
 static void sifive_spi_prep_transfer(struct sifive_spi *spi,
-				     bool is_rx_xfer,
-				     struct dm_spi_slave_platdata *slave_plat)
+				     struct dm_spi_slave_platdata *slave_plat,
+				     u8 *rx_ptr)
 {
 	u32 cr;
 
@@ -167,7 +168,7 @@ static void sifive_spi_prep_transfer(struct sifive_spi *spi,
 
 	/* SPI direction in/out ? */
 	cr &= ~SIFIVE_SPI_FMT_DIR;
-	if (!is_rx_xfer)
+	if (!rx_ptr)
 		cr |= SIFIVE_SPI_FMT_DIR;
 
 	writel(cr, spi->regs + SIFIVE_SPI_REG_FMT);
@@ -198,13 +199,19 @@ static void sifive_spi_tx(struct sifive_spi *spi, const u8 *tx_ptr)
 	writel(tx_data, spi->regs + SIFIVE_SPI_REG_TXDATA);
 }
 
+static int sifive_spi_wait(struct sifive_spi *spi, u32 bit)
+{
+	return wait_for_bit_le32(spi->regs + SIFIVE_SPI_REG_IP,
+				 bit, true, 100, false);
+}
+
 static int sifive_spi_xfer(struct udevice *dev, unsigned int bitlen,
 			   const void *dout, void *din, unsigned long flags)
 {
 	struct udevice *bus = dev->parent;
 	struct sifive_spi *spi = dev_get_priv(bus);
 	struct dm_spi_slave_platdata *slave_plat = dev_get_parent_platdata(dev);
-	const unsigned char *tx_ptr = dout;
+	const u8 *tx_ptr = dout;
 	u8 *rx_ptr = din;
 	u32 remaining_len;
 	int ret;
@@ -217,31 +224,37 @@ static int sifive_spi_xfer(struct udevice *dev, unsigned int bitlen,
 			return ret;
 	}
 
-	sifive_spi_prep_transfer(spi, true, slave_plat);
+	sifive_spi_prep_transfer(spi, slave_plat, rx_ptr);
 
 	remaining_len = bitlen / 8;
 
 	while (remaining_len) {
-		int n_words, tx_words, rx_words;
-
-		n_words = min(remaining_len, spi->fifo_depth);
+		unsigned int n_words = min(remaining_len, spi->fifo_depth);
+		unsigned int tx_words, rx_words;
 
 		/* Enqueue n_words for transmission */
-		if (tx_ptr) {
-			for (tx_words = 0; tx_words < n_words; ++tx_words) {
-				sifive_spi_tx(spi, tx_ptr);
-				sifive_spi_rx(spi, NULL);
-				tx_ptr++;
-			}
+		for (tx_words = 0; tx_words < n_words; tx_words++) {
+			if (!tx_ptr)
+				sifive_spi_tx(spi, NULL);
+			else
+				sifive_spi_tx(spi, tx_ptr++);
 		}
 
-		/* Read out all the data from the RX FIFO */
 		if (rx_ptr) {
-			for (rx_words = 0; rx_words < n_words; ++rx_words) {
-				sifive_spi_tx(spi, NULL);
-				sifive_spi_rx(spi, rx_ptr);
-				rx_ptr++;
-			}
+			/* Wait for transmission + reception to complete */
+			writel(n_words - 1, spi->regs + SIFIVE_SPI_REG_RXMARK);
+			ret = sifive_spi_wait(spi, SIFIVE_SPI_IP_RXWM);
+			if (ret)
+				return ret;
+
+			/* Read out all the data from the RX FIFO */
+			for (rx_words = 0; rx_words < n_words; rx_words++)
+				sifive_spi_rx(spi, rx_ptr++);
+		} else {
+			/* Wait for transmission to complete */
+			ret = sifive_spi_wait(spi, SIFIVE_SPI_IP_TXWM);
+			if (ret)
+				return ret;
 		}
 
 		remaining_len -= n_words;
@@ -395,6 +408,10 @@ static void sifive_spi_init_hw(struct sifive_spi *spi)
 	/* Watermark interrupts are disabled by default */
 	writel(0, spi->regs + SIFIVE_SPI_REG_IE);
 
+	/* Default watermark FIFO threshold values */
+	writel(1, spi->regs + SIFIVE_SPI_REG_TXMARK);
+	writel(0, spi->regs + SIFIVE_SPI_REG_RXMARK);
+
 	/* Set CS/SCK Delays and Inactive Time to defaults */
 	writel(SIFIVE_SPI_DELAY0_CSSCK(1) | SIFIVE_SPI_DELAY0_SCKCS(1),
 	       spi->regs + SIFIVE_SPI_REG_DELAY0);
