aboutsummaryrefslogtreecommitdiffstats
path: root/target/linux/ipq806x/patches-4.9/0014-spi-qup-Fix-sg-nents-calculation.patch
diff options
context:
space:
mode:
Diffstat (limited to 'target/linux/ipq806x/patches-4.9/0014-spi-qup-Fix-sg-nents-calculation.patch')
-rw-r--r--target/linux/ipq806x/patches-4.9/0014-spi-qup-Fix-sg-nents-calculation.patch86
1 files changed, 86 insertions, 0 deletions
diff --git a/target/linux/ipq806x/patches-4.9/0014-spi-qup-Fix-sg-nents-calculation.patch b/target/linux/ipq806x/patches-4.9/0014-spi-qup-Fix-sg-nents-calculation.patch
new file mode 100644
index 0000000000..2d321f1d28
--- /dev/null
+++ b/target/linux/ipq806x/patches-4.9/0014-spi-qup-Fix-sg-nents-calculation.patch
@@ -0,0 +1,86 @@
+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);
+