From 5ffa559b35dd90469e1f7fc21a77c6a2d5a8ca0f Mon Sep 17 00:00:00 2001 From: Varadarajan Narayanan Date: Wed, 25 May 2016 13:40:03 +0530 Subject: [PATCH 17/37] 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 --- 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);