openwrt/target/linux/ipq806x/patches-4.9/0014-spi-qup-Fix-sg-nents-c...

87 lines
2.6 KiB
Diff

From f5913e137c3dac4972ac0ddd5f248924d02d3dcb Mon Sep 17 00:00:00 2001
From: Varadarajan Narayanan <varada@codeaurora.org>
Date: Wed, 25 May 2016 13:40:03 +0530
Subject: [PATCH 14/69] spi: qup: Fix sg nents calculation
lib/scatterlist.c:sg_nents_for_len() returns the number of SG
entries that total up to greater than or equal to the given
length. However, the spi-qup driver assumed that the returned
nents is for a total less than or equal to the given length. The
spi-qup driver requests nents for SPI_MAX_XFER, however the API
returns nents for SPI_MAX_XFER+delta (actually SZ_64K).
Based on this, spi_qup_do_dma() calculates n_words and programs
that into QUP_MX_{IN|OUT}PUT_CNT register. The calculated
n_words value is more than the maximum value that can fit in the
the 16-bit COUNT field of the QUP_MX_{IN|OUT}PUT_CNT register.
And, the field gets programmed to zero. Since the COUNT field is
zero, the i/o doesn't start eventually resulting in the i/o
timing out.
Signed-off-by: Varadarajan Narayanan <varada@codeaurora.org>
---
drivers/spi/spi-qup.c | 38 ++++++++++++++++++++++++++++++++++++--
1 file changed, 36 insertions(+), 2 deletions(-)
--- a/drivers/spi/spi-qup.c
+++ b/drivers/spi/spi-qup.c
@@ -581,6 +581,38 @@ static unsigned int spi_qup_sgl_get_size
return length;
}
+/**
+ * spi_qup_sg_nents_for_len - return total count of entries in scatterlist
+ * needed to satisfy the supplied length
+ * @sg: The scatterlist
+ * @len: The total required length
+ *
+ * Description:
+ * Determines the number of entries in sg that sum upto a maximum of
+ * the supplied length, taking into acount chaining as well
+ *
+ * Returns:
+ * the number of sg entries needed, negative error on failure
+ *
+ **/
+int spi_qup_sg_nents_for_len(struct scatterlist *sg, u64 len)
+{
+ int nents;
+ u64 total;
+
+ if (!len)
+ return 0;
+
+ for (nents = 0, total = 0; sg; sg = sg_next(sg)) {
+ nents++;
+ total += sg_dma_len(sg);
+ if (total > len)
+ return (nents - 1);
+ }
+
+ return -EINVAL;
+}
+
static int spi_qup_do_dma(struct spi_device *spi, struct spi_transfer *xfer,
unsigned long timeout)
{
@@ -597,7 +629,8 @@ unsigned long timeout)
int rx_nents = 0, tx_nents = 0;
if (rx_sgl) {
- rx_nents = sg_nents_for_len(rx_sgl, SPI_MAX_XFER);
+ rx_nents = spi_qup_sg_nents_for_len(rx_sgl,
+ SPI_MAX_XFER);
if (rx_nents < 0)
rx_nents = sg_nents(rx_sgl);
@@ -606,7 +639,8 @@ unsigned long timeout)
}
if (tx_sgl) {
- tx_nents = sg_nents_for_len(tx_sgl, SPI_MAX_XFER);
+ tx_nents = spi_qup_sg_nents_for_len(tx_sgl,
+ SPI_MAX_XFER);
if (tx_nents < 0)
tx_nents = sg_nents(tx_sgl);