1 From 160cc587decac38e0f28f1583412a987a70450e9 Mon Sep 17 00:00:00 2001
2 From: Maksim Kiselev <bigunclemax@gmail.com>
3 Date: Wed, 10 May 2023 11:11:08 +0300
4 Subject: [PATCH 7/9] dt-bindings: spi: sun6i: add DT bindings for Allwinner
7 Listed above Allwinner SoCs has two SPI controllers. First is the regular
8 SPI controller and the second one has additional functionality for
11 Add compatible strings for these controllers
13 Signed-off-by: Maksim Kiselev <bigunclemax@gmail.com>
14 Acked-by: Krzysztof Kozlowski <krzysztof.kozlowski@linaro.org>
15 Reviewed-by: Andre Przywara <andre.przywara@arm.com>
16 Reviewed-by: Jernej Skrabec <jernej.skrabec@gmail.com>
18 .../bindings/spi/allwinner,sun6i-a31-spi.yaml | 10 ++
19 .../boot/dts/allwinner/sunxi-d1s-t113.dtsi | 37 +++++
20 drivers/spi/spi-sun6i.c | 131 ++++++++++++------
21 3 files changed, 138 insertions(+), 40 deletions(-)
23 diff --git a/Documentation/devicetree/bindings/spi/allwinner,sun6i-a31-spi.yaml b/Documentation/devicetree/bindings/spi/allwinner,sun6i-a31-spi.yaml
24 index 58b7056f4a70..95939684a00d 100644
25 --- a/Documentation/devicetree/bindings/spi/allwinner,sun6i-a31-spi.yaml
26 +++ b/Documentation/devicetree/bindings/spi/allwinner,sun6i-a31-spi.yaml
27 @@ -19,6 +19,7 @@ properties:
31 + - const: allwinner,sun50i-r329-spi
32 - const: allwinner,sun6i-a31-spi
33 - const: allwinner,sun8i-h3-spi
35 @@ -28,6 +29,15 @@ properties:
36 - allwinner,sun50i-h616-spi
37 - allwinner,suniv-f1c100s-spi
38 - const: allwinner,sun8i-h3-spi
41 + - allwinner,sun20i-d1-spi
42 + - allwinner,sun50i-r329-spi-dbi
43 + - const: allwinner,sun50i-r329-spi
45 + - const: allwinner,sun20i-d1-spi-dbi
46 + - const: allwinner,sun50i-r329-spi-dbi
47 + - const: allwinner,sun50i-r329-spi
51 diff --git a/arch/riscv/boot/dts/allwinner/sunxi-d1s-t113.dtsi b/arch/riscv/boot/dts/allwinner/sunxi-d1s-t113.dtsi
52 index 3723612b1fd8..6efff8f41e00 100644
53 --- a/arch/riscv/boot/dts/allwinner/sunxi-d1s-t113.dtsi
54 +++ b/arch/riscv/boot/dts/allwinner/sunxi-d1s-t113.dtsi
60 + spi0_pins: spi0-pins {
61 + pins = "PC2", "PC3", "PC4", "PC5";
66 uart1_pg6_pins: uart1-pg6-pins {
73 + compatible = "allwinner,sun20i-d1-spi",
74 + "allwinner,sun50i-r329-spi";
75 + reg = <0x04025000 0x1000>;
76 + interrupts = <SOC_PERIPHERAL_IRQ(15) IRQ_TYPE_LEVEL_HIGH>;
77 + clocks = <&ccu CLK_BUS_SPI0>, <&ccu CLK_SPI0>;
78 + clock-names = "ahb", "mod";
79 + dmas = <&dma 22>, <&dma 22>;
80 + dma-names = "rx", "tx";
81 + resets = <&ccu RST_BUS_SPI0>;
82 + status = "disabled";
83 + #address-cells = <1>;
88 + compatible = "allwinner,sun20i-d1-spi-dbi",
89 + "allwinner,sun50i-r329-spi-dbi",
90 + "allwinner,sun50i-r329-spi";
91 + reg = <0x04026000 0x1000>;
92 + interrupts = <SOC_PERIPHERAL_IRQ(16) IRQ_TYPE_LEVEL_HIGH>;
93 + clocks = <&ccu CLK_BUS_SPI1>, <&ccu CLK_SPI1>;
94 + clock-names = "ahb", "mod";
95 + dmas = <&dma 23>, <&dma 23>;
96 + dma-names = "rx", "tx";
97 + resets = <&ccu RST_BUS_SPI1>;
98 + status = "disabled";
99 + #address-cells = <1>;
103 usb_otg: usb@4100000 {
104 compatible = "allwinner,sun20i-d1-musb",
105 "allwinner,sun8i-a33-musb";
106 diff --git a/drivers/spi/spi-sun6i.c b/drivers/spi/spi-sun6i.c
107 index 23ad052528db..4f32cd99a81e 100644
108 --- a/drivers/spi/spi-sun6i.c
109 +++ b/drivers/spi/spi-sun6i.c
111 #define SUN6I_TFR_CTL_CS_MANUAL BIT(6)
112 #define SUN6I_TFR_CTL_CS_LEVEL BIT(7)
113 #define SUN6I_TFR_CTL_DHB BIT(8)
114 +#define SUN6I_TFR_CTL_SDC BIT(11)
115 #define SUN6I_TFR_CTL_FBS BIT(12)
116 +#define SUN6I_TFR_CTL_SDM BIT(13)
117 #define SUN6I_TFR_CTL_XCH BIT(31)
119 #define SUN6I_INT_CTL_REG 0x10
121 #define SUN6I_TXDATA_REG 0x200
122 #define SUN6I_RXDATA_REG 0x300
124 +struct sun6i_spi_cfg {
125 + unsigned long fifo_depth;
130 struct spi_master *master;
131 void __iomem *base_addr;
132 @@ -99,7 +106,7 @@ struct sun6i_spi {
136 - unsigned long fifo_depth;
137 + const struct sun6i_spi_cfg *cfg;
140 static inline u32 sun6i_spi_read(struct sun6i_spi *sspi, u32 reg)
141 @@ -156,7 +163,7 @@ static inline void sun6i_spi_fill_fifo(struct sun6i_spi *sspi)
144 /* See how much data we can fit */
145 - cnt = sspi->fifo_depth - sun6i_spi_get_tx_fifo_count(sspi);
146 + cnt = sspi->cfg->fifo_depth - sun6i_spi_get_tx_fifo_count(sspi);
148 len = min((int)cnt, sspi->len);
150 @@ -256,7 +263,7 @@ static int sun6i_spi_transfer_one(struct spi_master *master,
151 struct spi_transfer *tfr)
153 struct sun6i_spi *sspi = spi_master_get_devdata(master);
154 - unsigned int mclk_rate, div, div_cdr1, div_cdr2, timeout;
155 + unsigned int div, div_cdr1, div_cdr2, timeout;
156 unsigned int start, end, tx_time;
157 unsigned int trig_level;
158 unsigned int tx_len = 0, rx_len = 0;
159 @@ -289,14 +296,14 @@ static int sun6i_spi_transfer_one(struct spi_master *master,
160 * the hardcoded value used in old generation of Allwinner
161 * SPI controller. (See spi-sun4i.c)
163 - trig_level = sspi->fifo_depth / 4 * 3;
164 + trig_level = sspi->cfg->fifo_depth / 4 * 3;
167 * Setup FIFO DMA request trigger level
168 * We choose 1/2 of the full fifo depth, that value will
169 * be used as DMA burst length.
171 - trig_level = sspi->fifo_depth / 2;
172 + trig_level = sspi->cfg->fifo_depth / 2;
175 reg |= SUN6I_FIFO_CTL_TF_DRQ_EN;
176 @@ -346,39 +353,65 @@ static int sun6i_spi_transfer_one(struct spi_master *master,
178 sun6i_spi_write(sspi, SUN6I_TFR_CTL_REG, reg);
180 - /* Ensure that we have a parent clock fast enough */
181 - mclk_rate = clk_get_rate(sspi->mclk);
182 - if (mclk_rate < (2 * tfr->speed_hz)) {
183 - clk_set_rate(sspi->mclk, 2 * tfr->speed_hz);
184 - mclk_rate = clk_get_rate(sspi->mclk);
186 + if (sspi->cfg->has_clk_ctl) {
187 + unsigned int mclk_rate = clk_get_rate(sspi->mclk);
190 - * Setup clock divider.
192 - * We have two choices there. Either we can use the clock
193 - * divide rate 1, which is calculated thanks to this formula:
194 - * SPI_CLK = MOD_CLK / (2 ^ cdr)
195 - * Or we can use CDR2, which is calculated with the formula:
196 - * SPI_CLK = MOD_CLK / (2 * (cdr + 1))
197 - * Wether we use the former or the latter is set through the
200 - * First try CDR2, and if we can't reach the expected
201 - * frequency, fall back to CDR1.
203 - div_cdr1 = DIV_ROUND_UP(mclk_rate, tfr->speed_hz);
204 - div_cdr2 = DIV_ROUND_UP(div_cdr1, 2);
205 - if (div_cdr2 <= (SUN6I_CLK_CTL_CDR2_MASK + 1)) {
206 - reg = SUN6I_CLK_CTL_CDR2(div_cdr2 - 1) | SUN6I_CLK_CTL_DRS;
207 - tfr->effective_speed_hz = mclk_rate / (2 * div_cdr2);
208 + /* Ensure that we have a parent clock fast enough */
209 + if (mclk_rate < (2 * tfr->speed_hz)) {
210 + clk_set_rate(sspi->mclk, 2 * tfr->speed_hz);
211 + mclk_rate = clk_get_rate(sspi->mclk);
215 + * Setup clock divider.
217 + * We have two choices there. Either we can use the clock
218 + * divide rate 1, which is calculated thanks to this formula:
219 + * SPI_CLK = MOD_CLK / (2 ^ cdr)
220 + * Or we can use CDR2, which is calculated with the formula:
221 + * SPI_CLK = MOD_CLK / (2 * (cdr + 1))
222 + * Wether we use the former or the latter is set through the
225 + * First try CDR2, and if we can't reach the expected
226 + * frequency, fall back to CDR1.
228 + div_cdr1 = DIV_ROUND_UP(mclk_rate, tfr->speed_hz);
229 + div_cdr2 = DIV_ROUND_UP(div_cdr1, 2);
230 + if (div_cdr2 <= (SUN6I_CLK_CTL_CDR2_MASK + 1)) {
231 + reg = SUN6I_CLK_CTL_CDR2(div_cdr2 - 1) | SUN6I_CLK_CTL_DRS;
232 + tfr->effective_speed_hz = mclk_rate / (2 * div_cdr2);
234 + div = min(SUN6I_CLK_CTL_CDR1_MASK, order_base_2(div_cdr1));
235 + reg = SUN6I_CLK_CTL_CDR1(div);
236 + tfr->effective_speed_hz = mclk_rate / (1 << div);
239 + sun6i_spi_write(sspi, SUN6I_CLK_CTL_REG, reg);
241 - div = min(SUN6I_CLK_CTL_CDR1_MASK, order_base_2(div_cdr1));
242 - reg = SUN6I_CLK_CTL_CDR1(div);
243 - tfr->effective_speed_hz = mclk_rate / (1 << div);
244 + clk_set_rate(sspi->mclk, tfr->speed_hz);
245 + tfr->effective_speed_hz = clk_get_rate(sspi->mclk);
248 + * Configure work mode.
250 + * There are three work modes depending on the controller clock
252 + * - normal sample mode : CLK <= 24MHz SDM=1 SDC=0
253 + * - delay half-cycle sample mode : CLK <= 40MHz SDM=0 SDC=0
254 + * - delay one-cycle sample mode : CLK >= 80MHz SDM=0 SDC=1
256 + reg = sun6i_spi_read(sspi, SUN6I_TFR_CTL_REG);
257 + reg &= ~(SUN6I_TFR_CTL_SDM | SUN6I_TFR_CTL_SDC);
259 + if (tfr->effective_speed_hz <= 24000000)
260 + reg |= SUN6I_TFR_CTL_SDM;
261 + else if (tfr->effective_speed_hz >= 80000000)
262 + reg |= SUN6I_TFR_CTL_SDC;
264 + sun6i_spi_write(sspi, SUN6I_TFR_CTL_REG, reg);
267 - sun6i_spi_write(sspi, SUN6I_CLK_CTL_REG, reg);
268 /* Finally enable the bus - doing so before might raise SCK to HIGH */
269 reg = sun6i_spi_read(sspi, SUN6I_GBL_CTL_REG);
270 reg |= SUN6I_GBL_CTL_BUS_ENABLE;
271 @@ -410,9 +443,9 @@ static int sun6i_spi_transfer_one(struct spi_master *master,
272 reg = SUN6I_INT_CTL_TC;
275 - if (rx_len > sspi->fifo_depth)
276 + if (rx_len > sspi->cfg->fifo_depth)
277 reg |= SUN6I_INT_CTL_RF_RDY;
278 - if (tx_len > sspi->fifo_depth)
279 + if (tx_len > sspi->cfg->fifo_depth)
280 reg |= SUN6I_INT_CTL_TF_ERQ;
283 @@ -543,7 +576,7 @@ static bool sun6i_spi_can_dma(struct spi_master *master,
284 * the fifo length we can just fill the fifo and wait for a single
285 * irq, so don't bother setting up dma
287 - return xfer->len > sspi->fifo_depth;
288 + return xfer->len > sspi->cfg->fifo_depth;
291 static int sun6i_spi_probe(struct platform_device *pdev)
292 @@ -582,7 +615,7 @@ static int sun6i_spi_probe(struct platform_device *pdev)
295 sspi->master = master;
296 - sspi->fifo_depth = (unsigned long)of_device_get_match_data(&pdev->dev);
297 + sspi->cfg = of_device_get_match_data(&pdev->dev);
299 master->max_speed_hz = 100 * 1000 * 1000;
300 master->min_speed_hz = 3 * 1000;
301 @@ -696,9 +729,27 @@ static int sun6i_spi_remove(struct platform_device *pdev)
305 +static const struct sun6i_spi_cfg sun6i_a31_spi_cfg = {
306 + .fifo_depth = SUN6I_FIFO_DEPTH,
307 + .has_clk_ctl = true,
310 +static const struct sun6i_spi_cfg sun8i_h3_spi_cfg = {
311 + .fifo_depth = SUN8I_FIFO_DEPTH,
312 + .has_clk_ctl = true,
315 +static const struct sun6i_spi_cfg sun50i_r329_spi_cfg = {
316 + .fifo_depth = SUN8I_FIFO_DEPTH,
319 static const struct of_device_id sun6i_spi_match[] = {
320 - { .compatible = "allwinner,sun6i-a31-spi", .data = (void *)SUN6I_FIFO_DEPTH },
321 - { .compatible = "allwinner,sun8i-h3-spi", .data = (void *)SUN8I_FIFO_DEPTH },
322 + { .compatible = "allwinner,sun6i-a31-spi", .data = &sun6i_a31_spi_cfg },
323 + { .compatible = "allwinner,sun8i-h3-spi", .data = &sun8i_h3_spi_cfg },
325 + .compatible = "allwinner,sun50i-r329-spi",
326 + .data = &sun50i_r329_spi_cfg
330 MODULE_DEVICE_TABLE(of, sun6i_spi_match);