aboutsummaryrefslogtreecommitdiffstats
path: root/target/linux/ipq806x/patches-4.9/0014-spi-qup-Fix-sg-nents-calculation.patch
blob: 2d321f1d28754952dcb57a15519d7f0daeefabe9 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
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);