ipq806x: add v4.9 support
[openwrt/openwrt.git] / target / linux / ipq806x / patches-4.9 / 0017-spi-qup-Fix-sg-nents-calculation.patch
1 From 5ffa559b35dd90469e1f7fc21a77c6a2d5a8ca0f Mon Sep 17 00:00:00 2001
2 From: Varadarajan Narayanan <varada@codeaurora.org>
3 Date: Wed, 25 May 2016 13:40:03 +0530
4 Subject: [PATCH 17/37] spi: qup: Fix sg nents calculation
5
6 lib/scatterlist.c:sg_nents_for_len() returns the number of SG
7 entries that total up to greater than or equal to the given
8 length. However, the spi-qup driver assumed that the returned
9 nents is for a total less than or equal to the given length. The
10 spi-qup driver requests nents for SPI_MAX_XFER, however the API
11 returns nents for SPI_MAX_XFER+delta (actually SZ_64K).
12
13 Based on this, spi_qup_do_dma() calculates n_words and programs
14 that into QUP_MX_{IN|OUT}PUT_CNT register. The calculated
15 n_words value is more than the maximum value that can fit in the
16 the 16-bit COUNT field of the QUP_MX_{IN|OUT}PUT_CNT register.
17 And, the field gets programmed to zero. Since the COUNT field is
18 zero, the i/o doesn't start eventually resulting in the i/o
19 timing out.
20
21 Signed-off-by: Varadarajan Narayanan <varada@codeaurora.org>
22 ---
23 drivers/spi/spi-qup.c | 38 ++++++++++++++++++++++++++++++++++++--
24 1 file changed, 36 insertions(+), 2 deletions(-)
25
26 --- a/drivers/spi/spi-qup.c
27 +++ b/drivers/spi/spi-qup.c
28 @@ -581,6 +581,38 @@ static unsigned int spi_qup_sgl_get_size
29 return length;
30 }
31
32 +/**
33 + * spi_qup_sg_nents_for_len - return total count of entries in scatterlist
34 + * needed to satisfy the supplied length
35 + * @sg: The scatterlist
36 + * @len: The total required length
37 + *
38 + * Description:
39 + * Determines the number of entries in sg that sum upto a maximum of
40 + * the supplied length, taking into acount chaining as well
41 + *
42 + * Returns:
43 + * the number of sg entries needed, negative error on failure
44 + *
45 + **/
46 +int spi_qup_sg_nents_for_len(struct scatterlist *sg, u64 len)
47 +{
48 + int nents;
49 + u64 total;
50 +
51 + if (!len)
52 + return 0;
53 +
54 + for (nents = 0, total = 0; sg; sg = sg_next(sg)) {
55 + nents++;
56 + total += sg_dma_len(sg);
57 + if (total > len)
58 + return (nents - 1);
59 + }
60 +
61 + return -EINVAL;
62 +}
63 +
64 static int spi_qup_do_dma(struct spi_device *spi, struct spi_transfer *xfer,
65 unsigned long timeout)
66 {
67 @@ -597,7 +629,8 @@ unsigned long timeout)
68 int rx_nents = 0, tx_nents = 0;
69
70 if (rx_sgl) {
71 - rx_nents = sg_nents_for_len(rx_sgl, SPI_MAX_XFER);
72 + rx_nents = spi_qup_sg_nents_for_len(rx_sgl,
73 + SPI_MAX_XFER);
74 if (rx_nents < 0)
75 rx_nents = sg_nents(rx_sgl);
76
77 @@ -606,7 +639,8 @@ unsigned long timeout)
78 }
79
80 if (tx_sgl) {
81 - tx_nents = sg_nents_for_len(tx_sgl, SPI_MAX_XFER);
82 + tx_nents = spi_qup_sg_nents_for_len(tx_sgl,
83 + SPI_MAX_XFER);
84 if (tx_nents < 0)
85 tx_nents = sg_nents(tx_sgl);
86