spi: intel: Fix byte flow and error handling

- ROR interrupt needs to be acked by resetting the bit to 0
- Rx threshold seems buggy on that controller and setting it above 1
  generates unreliable transmission as sometimes it does not trigger any
  interrupt though the rx fifo is just full.

Change-Id: I4949c1fe7b42c70973efd4e0dafd14c6171f13f6
Signed-off-by: Tomasz Bursztyka <tomasz.bursztyka@linux.intel.com>
diff --git a/drivers/spi/intel_spi.c b/drivers/spi/intel_spi.c
index 3b15701..5446555 100644
--- a/drivers/spi/intel_spi.c
+++ b/drivers/spi/intel_spi.c
@@ -82,6 +82,8 @@
 DEFINE_TEST_BIT_OP(sscr0_sse, INTEL_SPI_REG_SSCR0, INTEL_SPI_SSCR0_SSE_BIT)
 DEFINE_TEST_BIT_OP(sssr_bsy, INTEL_SPI_REG_SSSR, INTEL_SPI_SSSR_BSY_BIT)
 DEFINE_CLEAR_BIT_OP(sscr1_tie, INTEL_SPI_REG_SSCR1, INTEL_SPI_SSCR1_TIE_BIT)
+DEFINE_TEST_BIT_OP(sscr1_tie, INTEL_SPI_REG_SSCR1, INTEL_SPI_SSCR1_TIE_BIT)
+DEFINE_CLEAR_BIT_OP(sssr_ror, INTEL_SPI_REG_SSSR, INTEL_SPI_SSSR_ROR_BIT)
 
 #ifdef CONFIG_SPI_INTEL_CS_GPIO
 
@@ -133,35 +135,52 @@
 		goto out;
 	}
 
-	if (spi->t_len) {
-		return;
-	}
-
-	if (spi->tx_buf && spi->tx_buf_len == 0 && !spi->rx_buf) {
+	if (spi->tx_buf == spi->tx_buf_end && !spi->rx_buf) {
 		cb_type = SPI_CB_WRITE;
-	} else if (spi->rx_buf && spi->rx_buf_len == 0 && !spi->tx_buf) {
+	} else if (spi->rx_buf == spi->rx_buf_end && !spi->tx_buf) {
 		cb_type = SPI_CB_READ;
-	} else if (spi->tx_buf && spi->tx_buf_len == 0 &&
-			spi->rx_buf && spi->rx_buf_len == 0) {
+	} else if (spi->tx_buf == spi->tx_buf_end &&
+			spi->rx_buf == spi->rx_buf_end) {
 		cb_type = SPI_CB_TRANSCEIVE;
 	} else {
 		return;
 	}
 
 out:
-	spi->tx_buf = spi->rx_buf = NULL;
-	spi->tx_buf_len = spi->rx_buf_len = 0;
+	spi->tx_buf = spi->rx_buf = spi->tx_buf_end = spi->rx_buf_end = NULL;
+	spi->t_len = spi->r_buf_len = 0;
+
+	_spi_control_cs(dev, 0);
 
 	write_sscr1(spi->sscr1, info->regs);
 	clear_bit_sscr0_sse(info->regs);
 
-	_spi_control_cs(dev, 0);
-
 	if (spi->callback) {
 		spi->callback(dev, cb_type, spi->user_data);
 	}
 }
 
+static void pull_data(struct device *dev)
+{
+	struct spi_intel_config *info = dev->config->config_info;
+	struct spi_intel_data *spi = dev->driver_data;
+	uint32_t cnt = 0;
+	uint8_t data = 0;
+
+	while (read_sssr(info->regs) & INTEL_SPI_SSSR_RNE) {
+		data = (uint8_t) read_ssdr(info->regs);
+		cnt++;
+
+		if (spi->rx_buf < spi->rx_buf_end) {
+			*(uint8_t *)(spi->rx_buf) = data;
+			spi->rx_buf++;
+		}
+	}
+
+	DBG("Pulled: %d (total: %d)\n",
+		cnt, spi->r_buf_len - (spi->rx_buf_end - spi->rx_buf));
+}
+
 static void push_data(struct device *dev)
 {
 	struct spi_intel_config *info = dev->config->config_info;
@@ -169,59 +188,32 @@
 	uint32_t cnt = 0;
 	uint8_t data;
 
-	DBG("spi: push_data\n");
-
 	while (read_sssr(info->regs) & INTEL_SPI_SSSR_TNF) {
-		if (spi->tx_buf && spi->tx_buf_len > 0) {
+		if (spi->tx_buf < spi->tx_buf_end) {
 			data = *(uint8_t *)(spi->tx_buf);
 			spi->tx_buf++;
-			spi->tx_buf_len--;
-		} else if (spi->rx_buf && spi->rx_buf_len > 0) {
-			/* No need to push more than necessary */
-			if (spi->rx_buf_len - cnt <= 0) {
-				break;
-			}
-
+		} else if (spi->t_len + cnt < spi->r_buf_len) {
 			data = 0;
 		} else {
 			/* Nothing to push anymore for now */
 			break;
 		}
 
-		write_ssdr(data, info->regs);
 		cnt++;
+		DBG("Pushing 1 byte (total: %d)\n", cnt);
+		write_ssdr(data, info->regs);
+
+		pull_data(dev);
 	}
 
-	DBG("Pushed: %d\n", cnt);
 	spi->t_len += cnt;
+	DBG("Pushed: %d (total: %d)\n", cnt, spi->t_len);
 
-	if (!spi->tx_buf_len && !spi->rx_buf_len) {
+	if (spi->tx_buf == spi->tx_buf_end) {
 		clear_bit_sscr1_tie(info->regs);
 	}
 }
 
-static void pull_data(struct device *dev)
-{
-	struct spi_intel_config *info = dev->config->config_info;
-	struct spi_intel_data *spi = dev->driver_data;
-	uint32_t cnt = 0;
-	uint8_t data;
-
-	while (read_sssr(info->regs) & INTEL_SPI_SSSR_RNE) {
-		data = (uint8_t) read_ssdr(info->regs);
-		cnt++;
-
-		if (spi->rx_buf && spi->rx_buf_len > 0) {
-			*(uint8_t *)(spi->rx_buf) = data;
-			spi->rx_buf++;
-			spi->rx_buf_len--;
-		}
-	}
-
-	DBG("Pulled: %d\n", cnt);
-	spi->t_len -= cnt;
-}
-
 static int spi_intel_configure(struct device *dev,
 				struct spi_config *config, void *user_data)
 {
@@ -243,7 +235,8 @@
 	write_sscr0(spi->sscr0, info->regs);
 	write_sscr1(spi->sscr1, info->regs);
 
-	DBG("spi_intel_configure: DDS_RATE: 0x%x SCR: %d\n",
+	DBG("spi_intel_configure: WS: %d, DDS_RATE: 0x%x SCR: %d\n",
+			SPI_WORD_SIZE_GET(flags),
 			INTEL_SPI_DSS_RATE(config->max_sys_freq),
 			INTEL_SPI_SSCR0_SCR(config->max_sys_freq) >> 8);
 
@@ -251,6 +244,14 @@
 	spi->sscr0 = INTEL_SPI_SSCR0_DSS(SPI_WORD_SIZE_GET(flags)) |
 				INTEL_SPI_SSCR0_SCR(config->max_sys_freq);
 
+	/* Tx/Rx thresholds
+	 * Note: Rx thresholds needs to be 1, it does not seem to be able
+	 * to trigger reliably any interrupt with another value though the
+	 * rx fifo would be full
+	 */
+	spi->sscr1 |= INTEL_SPI_SSCR1_TFT(INTEL_SPI_SSCR1_TFT_DFLT) |
+		      INTEL_SPI_SSCR1_RFT(INTEL_SPI_SSCR1_RFT_DFLT);
+
 	/* SPI mode */
 	mode = SPI_MODE(flags);
 	if (mode & SPI_MODE_CPOL) {
@@ -265,15 +266,11 @@
 		spi->sscr1 |= INTEL_SPI_SSCR1_LBM;
 	}
 
-	/* Tx/Rx Threshold */
-	spi->sscr1 |= INTEL_SPI_SSCR1_TFT(INTEL_SPI_SSCR1_TFT_DFLT)
-			| INTEL_SPI_SSCR1_RFT(INTEL_SPI_SSCR1_RFT_DFLT);
-
 	/* Configuring the rate */
 	write_dds_rate(INTEL_SPI_DSS_RATE(config->max_sys_freq), info->regs);
 
-	spi->tx_buf = spi->rx_buf = NULL;
-	spi->tx_buf_len = spi->rx_buf_len = spi->t_len = 0;
+	spi->tx_buf = spi->tx_buf_end = spi->rx_buf = spi->rx_buf_end = NULL;
+	spi->t_len = spi->r_buf_len = 0;
 	spi->callback = config->callback;
 	spi->user_data = user_data;
 
@@ -296,18 +293,25 @@
 		return DEV_USED;
 	}
 
+	/* Flushing recv fifo */
+	spi->rx_buf = spi->rx_buf_end = NULL;
+	pull_data(dev);
+
 	/* Set buffers info */
 	spi->tx_buf = tx_buf;
-	spi->tx_buf_len = tx_buf_len;
+	spi->tx_buf_end = tx_buf + tx_buf_len;
 	spi->rx_buf = rx_buf;
-	spi->rx_buf_len = rx_buf_len;
+	spi->rx_buf_end = rx_buf + rx_buf_len;
+	spi->r_buf_len = rx_buf_len;
 
 	_spi_control_cs(dev, 1);
 
-	/* Installing the registers (enabling interrupts and controller) */
+	/* Enabling the controller */
+	write_sscr0(spi->sscr0 | INTEL_SPI_SSCR0_SSE, info->regs);
+
+	/* Installing the registers */
 	write_sscr1(spi->sscr1 | INTEL_SPI_SSCR1_RIE |
 				INTEL_SPI_SSCR1_TIE, info->regs);
-	write_sscr0(spi->sscr0 | INTEL_SPI_SSCR0_SSE, info->regs);
 
 	return DEV_OK;
 }
@@ -348,7 +352,8 @@
 	status = read_sssr(info->regs);
 
 	if (status & INTEL_SPI_SSSR_ROR) {
-		/* Unrecoverable error */
+		/* Unrecoverable error, ack it */
+		clear_bit_sssr_ror(info->regs);
 		error = 1;
 		goto out;
 	}
@@ -357,10 +362,11 @@
 		pull_data(dev);
 	}
 
-	if (status & INTEL_SPI_SSSR_TFS) {
-		push_data(dev);
+	if (test_bit_sscr1_tie(info->regs)) {
+		if (status & INTEL_SPI_SSSR_TFS) {
+			push_data(dev);
+		}
 	}
-
 out:
 	completed(dev, error);
 }