diff options
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.patch | 86 |
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); + |