aboutsummaryrefslogtreecommitdiff
path: root/target/linux/d1/patches-6.1/0076-spi-spi-sun6i-Add-Allwinner-R329-support.patch
blob: 04e1c17f6695b439b891fe211e7e5a5bf6fa54b1 (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
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
From ec8dfb455da3822451129257ab21e2f0d03a6ae3 Mon Sep 17 00:00:00 2001
From: Samuel Holland <samuel@sholland.org>
Date: Fri, 16 Jul 2021 21:46:31 -0500
Subject: [PATCH 076/117] spi: spi-sun6i: Add Allwinner R329 support

Signed-off-by: Samuel Holland <samuel@sholland.org>
---
 drivers/spi/spi-sun6i.c | 78 ++++++++++++++++++++++++++---------------
 1 file changed, 49 insertions(+), 29 deletions(-)

--- a/drivers/spi/spi-sun6i.c
+++ b/drivers/spi/spi-sun6i.c
@@ -30,6 +30,7 @@
 #define SUN6I_GBL_CTL_REG		0x04
 #define SUN6I_GBL_CTL_BUS_ENABLE		BIT(0)
 #define SUN6I_GBL_CTL_MASTER			BIT(1)
+#define SUN6I_GBL_CTL_SAMPLE_MODE		BIT(2)
 #define SUN6I_GBL_CTL_TP			BIT(7)
 #define SUN6I_GBL_CTL_RST			BIT(31)
 
@@ -87,6 +88,8 @@
 
 struct sun6i_spi_quirks {
 	unsigned long		fifo_depth;
+	bool			has_divider : 1;
+	bool			has_new_sample_mode : 1;
 };
 
 struct sun6i_spi {
@@ -362,38 +365,44 @@ static int sun6i_spi_transfer_one(struct
 	sun6i_spi_write(sspi, SUN6I_TFR_CTL_REG, reg);
 
 	/* Ensure that we have a parent clock fast enough */
-	mclk_rate = clk_get_rate(sspi->mclk);
-	if (mclk_rate < (2 * tfr->speed_hz)) {
-		clk_set_rate(sspi->mclk, 2 * tfr->speed_hz);
+	if (sspi->quirks->has_divider) {
 		mclk_rate = clk_get_rate(sspi->mclk);
-	}
+		if (mclk_rate < (2 * tfr->speed_hz)) {
+			clk_set_rate(sspi->mclk, 2 * tfr->speed_hz);
+			mclk_rate = clk_get_rate(sspi->mclk);
+		}
 
-	/*
-	 * Setup clock divider.
-	 *
-	 * We have two choices there. Either we can use the clock
-	 * divide rate 1, which is calculated thanks to this formula:
-	 * SPI_CLK = MOD_CLK / (2 ^ cdr)
-	 * Or we can use CDR2, which is calculated with the formula:
-	 * SPI_CLK = MOD_CLK / (2 * (cdr + 1))
-	 * Wether we use the former or the latter is set through the
-	 * DRS bit.
-	 *
-	 * First try CDR2, and if we can't reach the expected
-	 * frequency, fall back to CDR1.
-	 */
-	div_cdr1 = DIV_ROUND_UP(mclk_rate, tfr->speed_hz);
-	div_cdr2 = DIV_ROUND_UP(div_cdr1, 2);
-	if (div_cdr2 <= (SUN6I_CLK_CTL_CDR2_MASK + 1)) {
-		reg = SUN6I_CLK_CTL_CDR2(div_cdr2 - 1) | SUN6I_CLK_CTL_DRS;
-		tfr->effective_speed_hz = mclk_rate / (2 * div_cdr2);
+		/*
+		 * Setup clock divider.
+		 *
+		 * We have two choices there. Either we can use the clock
+		 * divide rate 1, which is calculated thanks to this formula:
+		 * SPI_CLK = MOD_CLK / (2 ^ cdr)
+		 * Or we can use CDR2, which is calculated with the formula:
+		 * SPI_CLK = MOD_CLK / (2 * (cdr + 1))
+		 * Wether we use the former or the latter is set through the
+		 * DRS bit.
+		 *
+		 * First try CDR2, and if we can't reach the expected
+		 * frequency, fall back to CDR1.
+		 */
+		div_cdr1 = DIV_ROUND_UP(mclk_rate, tfr->speed_hz);
+		div_cdr2 = DIV_ROUND_UP(div_cdr1, 2);
+		if (div_cdr2 <= (SUN6I_CLK_CTL_CDR2_MASK + 1)) {
+			reg = SUN6I_CLK_CTL_CDR2(div_cdr2 - 1) | SUN6I_CLK_CTL_DRS;
+			tfr->effective_speed_hz = mclk_rate / (2 * div_cdr2);
+		} else {
+			div = min(SUN6I_CLK_CTL_CDR1_MASK, order_base_2(div_cdr1));
+			reg = SUN6I_CLK_CTL_CDR1(div);
+			tfr->effective_speed_hz = mclk_rate / (1 << div);
+		}
+		sun6i_spi_write(sspi, SUN6I_CLK_CTL_REG, reg);
 	} else {
-		div = min(SUN6I_CLK_CTL_CDR1_MASK, order_base_2(div_cdr1));
-		reg = SUN6I_CLK_CTL_CDR1(div);
-		tfr->effective_speed_hz = mclk_rate / (1 << div);
+		clk_set_rate(sspi->mclk, tfr->speed_hz);
+		mclk_rate = clk_get_rate(sspi->mclk);
+		tfr->effective_speed_hz = mclk_rate;
 	}
 
-	sun6i_spi_write(sspi, SUN6I_CLK_CTL_REG, reg);
 	/* Finally enable the bus - doing so before might raise SCK to HIGH */
 	reg = sun6i_spi_read(sspi, SUN6I_GBL_CTL_REG);
 	reg |= SUN6I_GBL_CTL_BUS_ENABLE;
@@ -518,6 +527,7 @@ static int sun6i_spi_runtime_resume(stru
 	struct spi_master *master = dev_get_drvdata(dev);
 	struct sun6i_spi *sspi = spi_master_get_devdata(master);
 	int ret;
+	u32 reg;
 
 	ret = clk_prepare_enable(sspi->hclk);
 	if (ret) {
@@ -537,8 +547,10 @@ static int sun6i_spi_runtime_resume(stru
 		goto err2;
 	}
 
-	sun6i_spi_write(sspi, SUN6I_GBL_CTL_REG,
-			SUN6I_GBL_CTL_MASTER | SUN6I_GBL_CTL_TP);
+	reg = SUN6I_GBL_CTL_MASTER | SUN6I_GBL_CTL_TP;
+	if (sspi->quirks->has_new_sample_mode)
+		reg |= SUN6I_GBL_CTL_SAMPLE_MODE;
+	sun6i_spi_write(sspi, SUN6I_GBL_CTL_REG, reg);
 
 	return 0;
 
@@ -729,15 +741,23 @@ static int sun6i_spi_remove(struct platf
 
 static const struct sun6i_spi_quirks sun6i_a31_spi_quirks = {
 	.fifo_depth		= SUN6I_FIFO_DEPTH,
+	.has_divider		= true,
 };
 
 static const struct sun6i_spi_quirks sun8i_h3_spi_quirks = {
 	.fifo_depth		= SUN8I_FIFO_DEPTH,
+	.has_divider		= true,
+};
+
+static const struct sun6i_spi_quirks sun50i_r329_spi_quirks = {
+	.fifo_depth		= SUN8I_FIFO_DEPTH,
+	.has_new_sample_mode	= true,
 };
 
 static const struct of_device_id sun6i_spi_match[] = {
 	{ .compatible = "allwinner,sun6i-a31-spi", .data = &sun6i_a31_spi_quirks },
 	{ .compatible = "allwinner,sun8i-h3-spi", .data = &sun8i_h3_spi_quirks },
+	{ .compatible = "allwinner,sun50i-r329-spi", .data = &sun50i_r329_spi_quirks },
 	{}
 };
 MODULE_DEVICE_TABLE(of, sun6i_spi_match);