]>
Commit | Line | Data |
---|---|---|
7f25d817 SM |
1 | /* |
2 | * (C) Copyright 2017 Whitebox Systems / Northend Systems B.V. | |
3 | * S.J.R. van Schaik <stephan@whiteboxsystems.nl> | |
4 | * M.B.W. Wajer <merlijn@whiteboxsystems.nl> | |
5 | * | |
6 | * (C) Copyright 2017 Olimex Ltd.. | |
7 | * Stefan Mavrodiev <stefan@olimex.com> | |
8 | * | |
9 | * Based on linux spi driver. Original copyright follows: | |
10 | * linux/drivers/spi/spi-sun4i.c | |
11 | * | |
12 | * Copyright (C) 2012 - 2014 Allwinner Tech | |
13 | * Pan Nan <pannan@allwinnertech.com> | |
14 | * | |
15 | * Copyright (C) 2014 Maxime Ripard | |
16 | * Maxime Ripard <maxime.ripard@free-electrons.com> | |
17 | * | |
18 | * SPDX-License-Identifier: GPL-2.0+ | |
19 | */ | |
20 | ||
d678a59d | 21 | #include <common.h> |
8d71a19e | 22 | #include <clk.h> |
7f25d817 | 23 | #include <dm.h> |
f7ae49fc | 24 | #include <log.h> |
7f25d817 SM |
25 | #include <spi.h> |
26 | #include <errno.h> | |
27 | #include <fdt_support.h> | |
853f4511 | 28 | #include <reset.h> |
7f25d817 | 29 | #include <wait_bit.h> |
401d1c4f | 30 | #include <asm/global_data.h> |
336d4615 | 31 | #include <dm/device_compat.h> |
cd93d625 | 32 | #include <linux/bitops.h> |
7f25d817 SM |
33 | |
34 | #include <asm/bitops.h> | |
7f25d817 SM |
35 | #include <asm/io.h> |
36 | ||
6cb6aa60 JT |
37 | #include <linux/iopoll.h> |
38 | ||
903e7cf3 | 39 | DECLARE_GLOBAL_DATA_PTR; |
7f25d817 | 40 | |
903e7cf3 JT |
41 | /* sun4i spi registers */ |
42 | #define SUN4I_RXDATA_REG 0x00 | |
43 | #define SUN4I_TXDATA_REG 0x04 | |
44 | #define SUN4I_CTL_REG 0x08 | |
45 | #define SUN4I_CLK_CTL_REG 0x1c | |
46 | #define SUN4I_BURST_CNT_REG 0x20 | |
47 | #define SUN4I_XMIT_CNT_REG 0x24 | |
48 | #define SUN4I_FIFO_STA_REG 0x28 | |
7f25d817 | 49 | |
853f4511 JT |
50 | /* sun6i spi registers */ |
51 | #define SUN6I_GBL_CTL_REG 0x04 | |
52 | #define SUN6I_TFR_CTL_REG 0x08 | |
53 | #define SUN6I_FIFO_CTL_REG 0x18 | |
54 | #define SUN6I_FIFO_STA_REG 0x1c | |
55 | #define SUN6I_CLK_CTL_REG 0x24 | |
56 | #define SUN6I_BURST_CNT_REG 0x30 | |
57 | #define SUN6I_XMIT_CNT_REG 0x34 | |
58 | #define SUN6I_BURST_CTL_REG 0x38 | |
59 | #define SUN6I_TXDATA_REG 0x200 | |
60 | #define SUN6I_RXDATA_REG 0x300 | |
61 | ||
903e7cf3 JT |
62 | /* sun spi bits */ |
63 | #define SUN4I_CTL_ENABLE BIT(0) | |
64 | #define SUN4I_CTL_MASTER BIT(1) | |
65 | #define SUN4I_CLK_CTL_CDR2_MASK 0xff | |
66 | #define SUN4I_CLK_CTL_CDR2(div) ((div) & SUN4I_CLK_CTL_CDR2_MASK) | |
67 | #define SUN4I_CLK_CTL_CDR1_MASK 0xf | |
68 | #define SUN4I_CLK_CTL_CDR1(div) (((div) & SUN4I_CLK_CTL_CDR1_MASK) << 8) | |
69 | #define SUN4I_CLK_CTL_DRS BIT(12) | |
70 | #define SUN4I_MAX_XFER_SIZE 0xffffff | |
71 | #define SUN4I_BURST_CNT(cnt) ((cnt) & SUN4I_MAX_XFER_SIZE) | |
72 | #define SUN4I_XMIT_CNT(cnt) ((cnt) & SUN4I_MAX_XFER_SIZE) | |
73 | #define SUN4I_FIFO_STA_RF_CNT_BITS 0 | |
74 | ||
8649995c AP |
75 | #ifdef CONFIG_MACH_SUNIV |
76 | /* the AHB clock, which we programmed to be 1/3 of PLL_PERIPH@600MHz */ | |
77 | #define SUNXI_INPUT_CLOCK 200000000 /* 200 MHz */ | |
78 | #define SUN4I_SPI_MAX_RATE (SUNXI_INPUT_CLOCK / 2) | |
79 | #else | |
fcd6d936 AP |
80 | /* the SPI mod clock, defaulting to be 1/1 of the HOSC@24MHz */ |
81 | #define SUNXI_INPUT_CLOCK 24000000 /* 24 MHz */ | |
82 | #define SUN4I_SPI_MAX_RATE SUNXI_INPUT_CLOCK | |
8649995c | 83 | #endif |
903e7cf3 JT |
84 | #define SUN4I_SPI_MIN_RATE 3000 |
85 | #define SUN4I_SPI_DEFAULT_RATE 1000000 | |
56e497eb | 86 | #define SUN4I_SPI_TIMEOUT_MS 1000 |
7f25d817 | 87 | |
903e7cf3 | 88 | #define SPI_REG(priv, reg) ((priv)->base + \ |
8d9bf468 JT |
89 | (priv)->variant->regs[reg]) |
90 | #define SPI_BIT(priv, bit) ((priv)->variant->bits[bit]) | |
91 | #define SPI_CS(priv, cs) (((cs) << SPI_BIT(priv, SPI_TCR_CS_SEL)) & \ | |
92 | SPI_BIT(priv, SPI_TCR_CS_MASK)) | |
93 | ||
94 | /* sun spi register set */ | |
95 | enum sun4i_spi_regs { | |
96 | SPI_GCR, | |
97 | SPI_TCR, | |
98 | SPI_FCR, | |
99 | SPI_FSR, | |
100 | SPI_CCR, | |
101 | SPI_BC, | |
102 | SPI_TC, | |
103 | SPI_BCTL, | |
104 | SPI_TXD, | |
105 | SPI_RXD, | |
106 | }; | |
107 | ||
108 | /* sun spi register bits */ | |
109 | enum sun4i_spi_bits { | |
110 | SPI_GCR_TP, | |
853f4511 | 111 | SPI_GCR_SRST, |
8d9bf468 JT |
112 | SPI_TCR_CPHA, |
113 | SPI_TCR_CPOL, | |
114 | SPI_TCR_CS_ACTIVE_LOW, | |
115 | SPI_TCR_CS_SEL, | |
116 | SPI_TCR_CS_MASK, | |
117 | SPI_TCR_XCH, | |
118 | SPI_TCR_CS_MANUAL, | |
119 | SPI_TCR_CS_LEVEL, | |
2b9d6a18 MK |
120 | SPI_TCR_SDC, |
121 | SPI_TCR_SDM, | |
8d9bf468 JT |
122 | SPI_FCR_TF_RST, |
123 | SPI_FCR_RF_RST, | |
124 | SPI_FSR_RF_CNT_MASK, | |
125 | }; | |
126 | ||
127 | struct sun4i_spi_variant { | |
128 | const unsigned long *regs; | |
129 | const u32 *bits; | |
178fbd24 | 130 | u32 fifo_depth; |
853f4511 JT |
131 | bool has_soft_reset; |
132 | bool has_burst_ctl; | |
2b9d6a18 | 133 | bool has_clk_ctl; |
7f25d817 SM |
134 | }; |
135 | ||
8a8d24bd | 136 | struct sun4i_spi_plat { |
8d9bf468 | 137 | struct sun4i_spi_variant *variant; |
903e7cf3 | 138 | u32 base; |
7f25d817 SM |
139 | u32 max_hz; |
140 | }; | |
141 | ||
142 | struct sun4i_spi_priv { | |
8d9bf468 | 143 | struct sun4i_spi_variant *variant; |
8d71a19e | 144 | struct clk clk_ahb, clk_mod; |
853f4511 | 145 | struct reset_ctl reset; |
903e7cf3 | 146 | u32 base; |
7f25d817 SM |
147 | u32 freq; |
148 | u32 mode; | |
149 | ||
150 | const u8 *tx_buf; | |
151 | u8 *rx_buf; | |
152 | }; | |
153 | ||
7f25d817 SM |
154 | static inline void sun4i_spi_drain_fifo(struct sun4i_spi_priv *priv, int len) |
155 | { | |
156 | u8 byte; | |
157 | ||
158 | while (len--) { | |
8d9bf468 | 159 | byte = readb(SPI_REG(priv, SPI_RXD)); |
5c1a87de SM |
160 | if (priv->rx_buf) |
161 | *priv->rx_buf++ = byte; | |
7f25d817 SM |
162 | } |
163 | } | |
164 | ||
165 | static inline void sun4i_spi_fill_fifo(struct sun4i_spi_priv *priv, int len) | |
166 | { | |
167 | u8 byte; | |
168 | ||
169 | while (len--) { | |
170 | byte = priv->tx_buf ? *priv->tx_buf++ : 0; | |
8d9bf468 | 171 | writeb(byte, SPI_REG(priv, SPI_TXD)); |
7f25d817 SM |
172 | } |
173 | } | |
174 | ||
175 | static void sun4i_spi_set_cs(struct udevice *bus, u8 cs, bool enable) | |
176 | { | |
177 | struct sun4i_spi_priv *priv = dev_get_priv(bus); | |
178 | u32 reg; | |
179 | ||
8d9bf468 | 180 | reg = readl(SPI_REG(priv, SPI_TCR)); |
7f25d817 | 181 | |
8d9bf468 JT |
182 | reg &= ~SPI_BIT(priv, SPI_TCR_CS_MASK); |
183 | reg |= SPI_CS(priv, cs); | |
7f25d817 SM |
184 | |
185 | if (enable) | |
8d9bf468 | 186 | reg &= ~SPI_BIT(priv, SPI_TCR_CS_LEVEL); |
7f25d817 | 187 | else |
8d9bf468 | 188 | reg |= SPI_BIT(priv, SPI_TCR_CS_LEVEL); |
7f25d817 | 189 | |
8d9bf468 | 190 | writel(reg, SPI_REG(priv, SPI_TCR)); |
7f25d817 SM |
191 | } |
192 | ||
8d71a19e | 193 | static inline int sun4i_spi_set_clock(struct udevice *dev, bool enable) |
7f25d817 | 194 | { |
8d71a19e JT |
195 | struct sun4i_spi_priv *priv = dev_get_priv(dev); |
196 | int ret; | |
197 | ||
198 | if (!enable) { | |
199 | clk_disable(&priv->clk_ahb); | |
200 | clk_disable(&priv->clk_mod); | |
853f4511 JT |
201 | if (reset_valid(&priv->reset)) |
202 | reset_assert(&priv->reset); | |
8d71a19e JT |
203 | return 0; |
204 | } | |
205 | ||
206 | ret = clk_enable(&priv->clk_ahb); | |
207 | if (ret) { | |
208 | dev_err(dev, "failed to enable ahb clock (ret=%d)\n", ret); | |
209 | return ret; | |
210 | } | |
211 | ||
212 | ret = clk_enable(&priv->clk_mod); | |
213 | if (ret) { | |
214 | dev_err(dev, "failed to enable mod clock (ret=%d)\n", ret); | |
215 | goto err_ahb; | |
216 | } | |
217 | ||
853f4511 JT |
218 | if (reset_valid(&priv->reset)) { |
219 | ret = reset_deassert(&priv->reset); | |
220 | if (ret) { | |
221 | dev_err(dev, "failed to deassert reset\n"); | |
222 | goto err_mod; | |
223 | } | |
224 | } | |
225 | ||
8d71a19e | 226 | return 0; |
7f25d817 | 227 | |
853f4511 JT |
228 | err_mod: |
229 | clk_disable(&priv->clk_mod); | |
8d71a19e JT |
230 | err_ahb: |
231 | clk_disable(&priv->clk_ahb); | |
232 | return ret; | |
7f25d817 SM |
233 | } |
234 | ||
239dfd11 AP |
235 | static void sun4i_spi_set_speed_mode(struct udevice *dev) |
236 | { | |
237 | struct sun4i_spi_priv *priv = dev_get_priv(dev); | |
238 | unsigned int div; | |
239 | u32 reg; | |
240 | ||
241 | /* | |
242 | * Setup clock divider. | |
243 | * | |
244 | * We have two choices there. Either we can use the clock | |
245 | * divide rate 1, which is calculated thanks to this formula: | |
246 | * SPI_CLK = MOD_CLK / (2 ^ (cdr + 1)) | |
247 | * Or we can use CDR2, which is calculated with the formula: | |
248 | * SPI_CLK = MOD_CLK / (2 * (cdr + 1)) | |
249 | * Whether we use the former or the latter is set through the | |
250 | * DRS bit. | |
251 | * | |
252 | * First try CDR2, and if we can't reach the expected | |
253 | * frequency, fall back to CDR1. | |
254 | */ | |
255 | ||
fcd6d936 | 256 | div = DIV_ROUND_UP(SUNXI_INPUT_CLOCK, priv->freq); |
239dfd11 AP |
257 | reg = readl(SPI_REG(priv, SPI_CCR)); |
258 | ||
fcd6d936 AP |
259 | if ((div / 2) <= (SUN4I_CLK_CTL_CDR2_MASK + 1)) { |
260 | div /= 2; | |
239dfd11 AP |
261 | if (div > 0) |
262 | div--; | |
263 | ||
264 | reg &= ~(SUN4I_CLK_CTL_CDR2_MASK | SUN4I_CLK_CTL_DRS); | |
265 | reg |= SUN4I_CLK_CTL_CDR2(div) | SUN4I_CLK_CTL_DRS; | |
266 | } else { | |
fcd6d936 | 267 | div = fls(div - 1); |
8649995c AP |
268 | /* The F1C100s encodes the divider as 2^(n+1) */ |
269 | if (IS_ENABLED(CONFIG_MACH_SUNIV)) | |
270 | div--; | |
239dfd11 AP |
271 | reg &= ~((SUN4I_CLK_CTL_CDR1_MASK << 8) | SUN4I_CLK_CTL_DRS); |
272 | reg |= SUN4I_CLK_CTL_CDR1(div); | |
273 | } | |
274 | ||
275 | writel(reg, SPI_REG(priv, SPI_CCR)); | |
276 | ||
277 | reg = readl(SPI_REG(priv, SPI_TCR)); | |
278 | reg &= ~(SPI_BIT(priv, SPI_TCR_CPOL) | SPI_BIT(priv, SPI_TCR_CPHA)); | |
279 | ||
280 | if (priv->mode & SPI_CPOL) | |
281 | reg |= SPI_BIT(priv, SPI_TCR_CPOL); | |
282 | ||
283 | if (priv->mode & SPI_CPHA) | |
284 | reg |= SPI_BIT(priv, SPI_TCR_CPHA); | |
285 | ||
286 | writel(reg, SPI_REG(priv, SPI_TCR)); | |
287 | } | |
288 | ||
7f25d817 SM |
289 | static int sun4i_spi_claim_bus(struct udevice *dev) |
290 | { | |
291 | struct sun4i_spi_priv *priv = dev_get_priv(dev->parent); | |
8d71a19e JT |
292 | int ret; |
293 | ||
294 | ret = sun4i_spi_set_clock(dev->parent, true); | |
295 | if (ret) | |
296 | return ret; | |
7f25d817 | 297 | |
8d9bf468 JT |
298 | setbits_le32(SPI_REG(priv, SPI_GCR), SUN4I_CTL_ENABLE | |
299 | SUN4I_CTL_MASTER | SPI_BIT(priv, SPI_GCR_TP)); | |
300 | ||
853f4511 JT |
301 | if (priv->variant->has_soft_reset) |
302 | setbits_le32(SPI_REG(priv, SPI_GCR), | |
303 | SPI_BIT(priv, SPI_GCR_SRST)); | |
304 | ||
8d9bf468 JT |
305 | setbits_le32(SPI_REG(priv, SPI_TCR), SPI_BIT(priv, SPI_TCR_CS_MANUAL) | |
306 | SPI_BIT(priv, SPI_TCR_CS_ACTIVE_LOW)); | |
8cbf09ba | 307 | |
2b9d6a18 MK |
308 | if (priv->variant->has_clk_ctl) { |
309 | sun4i_spi_set_speed_mode(dev->parent); | |
310 | } else { | |
311 | /* | |
312 | * At this moment there is no ability to change input clock. | |
313 | * Therefore, we can only use default HOSC@24MHz clock and | |
314 | * set SPI sampling mode to normal | |
315 | */ | |
316 | clrsetbits_le32(SPI_REG(priv, SPI_TCR), | |
317 | SPI_BIT(priv, SPI_TCR_SDC) | | |
318 | SPI_BIT(priv, SPI_TCR_SDM), | |
319 | SPI_BIT(priv, SPI_TCR_SDM)); | |
320 | } | |
239dfd11 | 321 | |
7f25d817 SM |
322 | return 0; |
323 | } | |
324 | ||
325 | static int sun4i_spi_release_bus(struct udevice *dev) | |
326 | { | |
327 | struct sun4i_spi_priv *priv = dev_get_priv(dev->parent); | |
7f25d817 | 328 | |
8d9bf468 | 329 | clrbits_le32(SPI_REG(priv, SPI_GCR), SUN4I_CTL_ENABLE); |
7f25d817 | 330 | |
8d71a19e JT |
331 | sun4i_spi_set_clock(dev->parent, false); |
332 | ||
7f25d817 SM |
333 | return 0; |
334 | } | |
335 | ||
336 | static int sun4i_spi_xfer(struct udevice *dev, unsigned int bitlen, | |
337 | const void *dout, void *din, unsigned long flags) | |
338 | { | |
339 | struct udevice *bus = dev->parent; | |
340 | struct sun4i_spi_priv *priv = dev_get_priv(bus); | |
8a8d24bd | 341 | struct dm_spi_slave_plat *slave_plat = dev_get_parent_plat(dev); |
7f25d817 SM |
342 | |
343 | u32 len = bitlen / 8; | |
7f25d817 SM |
344 | u8 nbytes; |
345 | int ret; | |
346 | ||
347 | priv->tx_buf = dout; | |
348 | priv->rx_buf = din; | |
349 | ||
350 | if (bitlen % 8) { | |
351 | debug("%s: non byte-aligned SPI transfer.\n", __func__); | |
352 | return -ENAVAIL; | |
353 | } | |
354 | ||
355 | if (flags & SPI_XFER_BEGIN) | |
356 | sun4i_spi_set_cs(bus, slave_plat->cs, true); | |
357 | ||
7f25d817 | 358 | /* Reset FIFOs */ |
8d9bf468 JT |
359 | setbits_le32(SPI_REG(priv, SPI_FCR), SPI_BIT(priv, SPI_FCR_RF_RST) | |
360 | SPI_BIT(priv, SPI_FCR_TF_RST)); | |
7f25d817 SM |
361 | |
362 | while (len) { | |
363 | /* Setup the transfer now... */ | |
178fbd24 | 364 | nbytes = min(len, (priv->variant->fifo_depth - 1)); |
7f25d817 SM |
365 | |
366 | /* Setup the counters */ | |
8d9bf468 JT |
367 | writel(SUN4I_BURST_CNT(nbytes), SPI_REG(priv, SPI_BC)); |
368 | writel(SUN4I_XMIT_CNT(nbytes), SPI_REG(priv, SPI_TC)); | |
7f25d817 | 369 | |
853f4511 JT |
370 | if (priv->variant->has_burst_ctl) |
371 | writel(SUN4I_BURST_CNT(nbytes), | |
372 | SPI_REG(priv, SPI_BCTL)); | |
373 | ||
7f25d817 SM |
374 | /* Fill the TX FIFO */ |
375 | sun4i_spi_fill_fifo(priv, nbytes); | |
376 | ||
377 | /* Start the transfer */ | |
8d9bf468 JT |
378 | setbits_le32(SPI_REG(priv, SPI_TCR), |
379 | SPI_BIT(priv, SPI_TCR_XCH)); | |
7f25d817 | 380 | |
56e497eb IZ |
381 | /* Wait for the transfer to be done */ |
382 | ret = wait_for_bit_le32((const void *)SPI_REG(priv, SPI_TCR), | |
383 | SPI_BIT(priv, SPI_TCR_XCH), | |
384 | false, SUN4I_SPI_TIMEOUT_MS, false); | |
6cb6aa60 | 385 | if (ret < 0) { |
7f25d817 SM |
386 | printf("ERROR: sun4i_spi: Timeout transferring data\n"); |
387 | sun4i_spi_set_cs(bus, slave_plat->cs, false); | |
388 | return ret; | |
389 | } | |
390 | ||
391 | /* Drain the RX FIFO */ | |
392 | sun4i_spi_drain_fifo(priv, nbytes); | |
393 | ||
394 | len -= nbytes; | |
395 | } | |
396 | ||
397 | if (flags & SPI_XFER_END) | |
398 | sun4i_spi_set_cs(bus, slave_plat->cs, false); | |
399 | ||
400 | return 0; | |
401 | } | |
402 | ||
403 | static int sun4i_spi_set_speed(struct udevice *dev, uint speed) | |
404 | { | |
8a8d24bd | 405 | struct sun4i_spi_plat *plat = dev_get_plat(dev); |
7f25d817 | 406 | struct sun4i_spi_priv *priv = dev_get_priv(dev); |
7f25d817 SM |
407 | |
408 | if (speed > plat->max_hz) | |
409 | speed = plat->max_hz; | |
410 | ||
411 | if (speed < SUN4I_SPI_MIN_RATE) | |
412 | speed = SUN4I_SPI_MIN_RATE; | |
7f25d817 SM |
413 | |
414 | priv->freq = speed; | |
7f25d817 SM |
415 | |
416 | return 0; | |
417 | } | |
418 | ||
419 | static int sun4i_spi_set_mode(struct udevice *dev, uint mode) | |
420 | { | |
421 | struct sun4i_spi_priv *priv = dev_get_priv(dev); | |
7f25d817 SM |
422 | |
423 | priv->mode = mode; | |
7f25d817 SM |
424 | |
425 | return 0; | |
426 | } | |
427 | ||
428 | static const struct dm_spi_ops sun4i_spi_ops = { | |
429 | .claim_bus = sun4i_spi_claim_bus, | |
430 | .release_bus = sun4i_spi_release_bus, | |
431 | .xfer = sun4i_spi_xfer, | |
432 | .set_speed = sun4i_spi_set_speed, | |
433 | .set_mode = sun4i_spi_set_mode, | |
434 | }; | |
435 | ||
903e7cf3 JT |
436 | static int sun4i_spi_probe(struct udevice *bus) |
437 | { | |
8a8d24bd | 438 | struct sun4i_spi_plat *plat = dev_get_plat(bus); |
903e7cf3 JT |
439 | struct sun4i_spi_priv *priv = dev_get_priv(bus); |
440 | int ret; | |
441 | ||
442 | ret = clk_get_by_name(bus, "ahb", &priv->clk_ahb); | |
443 | if (ret) { | |
32bbe5b5 | 444 | dev_err(bus, "failed to get ahb clock\n"); |
903e7cf3 JT |
445 | return ret; |
446 | } | |
447 | ||
448 | ret = clk_get_by_name(bus, "mod", &priv->clk_mod); | |
449 | if (ret) { | |
32bbe5b5 | 450 | dev_err(bus, "failed to get mod clock\n"); |
903e7cf3 JT |
451 | return ret; |
452 | } | |
453 | ||
454 | ret = reset_get_by_index(bus, 0, &priv->reset); | |
455 | if (ret && ret != -ENOENT) { | |
32bbe5b5 | 456 | dev_err(bus, "failed to get reset\n"); |
903e7cf3 JT |
457 | return ret; |
458 | } | |
459 | ||
903e7cf3 JT |
460 | priv->variant = plat->variant; |
461 | priv->base = plat->base; | |
462 | priv->freq = plat->max_hz; | |
463 | ||
464 | return 0; | |
465 | } | |
466 | ||
d1998a9f | 467 | static int sun4i_spi_of_to_plat(struct udevice *bus) |
903e7cf3 | 468 | { |
8a8d24bd | 469 | struct sun4i_spi_plat *plat = dev_get_plat(bus); |
903e7cf3 JT |
470 | int node = dev_of_offset(bus); |
471 | ||
2548493a | 472 | plat->base = dev_read_addr(bus); |
903e7cf3 JT |
473 | plat->variant = (struct sun4i_spi_variant *)dev_get_driver_data(bus); |
474 | plat->max_hz = fdtdec_get_int(gd->fdt_blob, node, | |
475 | "spi-max-frequency", | |
476 | SUN4I_SPI_DEFAULT_RATE); | |
477 | ||
478 | if (plat->max_hz > SUN4I_SPI_MAX_RATE) | |
479 | plat->max_hz = SUN4I_SPI_MAX_RATE; | |
480 | ||
481 | return 0; | |
482 | } | |
483 | ||
8d9bf468 JT |
484 | static const unsigned long sun4i_spi_regs[] = { |
485 | [SPI_GCR] = SUN4I_CTL_REG, | |
486 | [SPI_TCR] = SUN4I_CTL_REG, | |
487 | [SPI_FCR] = SUN4I_CTL_REG, | |
488 | [SPI_FSR] = SUN4I_FIFO_STA_REG, | |
489 | [SPI_CCR] = SUN4I_CLK_CTL_REG, | |
490 | [SPI_BC] = SUN4I_BURST_CNT_REG, | |
491 | [SPI_TC] = SUN4I_XMIT_CNT_REG, | |
492 | [SPI_TXD] = SUN4I_TXDATA_REG, | |
493 | [SPI_RXD] = SUN4I_RXDATA_REG, | |
494 | }; | |
495 | ||
496 | static const u32 sun4i_spi_bits[] = { | |
497 | [SPI_GCR_TP] = BIT(18), | |
498 | [SPI_TCR_CPHA] = BIT(2), | |
499 | [SPI_TCR_CPOL] = BIT(3), | |
500 | [SPI_TCR_CS_ACTIVE_LOW] = BIT(4), | |
501 | [SPI_TCR_XCH] = BIT(10), | |
502 | [SPI_TCR_CS_SEL] = 12, | |
503 | [SPI_TCR_CS_MASK] = 0x3000, | |
504 | [SPI_TCR_CS_MANUAL] = BIT(16), | |
505 | [SPI_TCR_CS_LEVEL] = BIT(17), | |
506 | [SPI_FCR_TF_RST] = BIT(8), | |
507 | [SPI_FCR_RF_RST] = BIT(9), | |
508 | [SPI_FSR_RF_CNT_MASK] = GENMASK(6, 0), | |
509 | }; | |
510 | ||
853f4511 JT |
511 | static const unsigned long sun6i_spi_regs[] = { |
512 | [SPI_GCR] = SUN6I_GBL_CTL_REG, | |
513 | [SPI_TCR] = SUN6I_TFR_CTL_REG, | |
514 | [SPI_FCR] = SUN6I_FIFO_CTL_REG, | |
515 | [SPI_FSR] = SUN6I_FIFO_STA_REG, | |
516 | [SPI_CCR] = SUN6I_CLK_CTL_REG, | |
517 | [SPI_BC] = SUN6I_BURST_CNT_REG, | |
518 | [SPI_TC] = SUN6I_XMIT_CNT_REG, | |
519 | [SPI_BCTL] = SUN6I_BURST_CTL_REG, | |
520 | [SPI_TXD] = SUN6I_TXDATA_REG, | |
521 | [SPI_RXD] = SUN6I_RXDATA_REG, | |
522 | }; | |
523 | ||
524 | static const u32 sun6i_spi_bits[] = { | |
525 | [SPI_GCR_TP] = BIT(7), | |
526 | [SPI_GCR_SRST] = BIT(31), | |
527 | [SPI_TCR_CPHA] = BIT(0), | |
528 | [SPI_TCR_CPOL] = BIT(1), | |
529 | [SPI_TCR_CS_ACTIVE_LOW] = BIT(2), | |
530 | [SPI_TCR_CS_SEL] = 4, | |
531 | [SPI_TCR_CS_MASK] = 0x30, | |
532 | [SPI_TCR_CS_MANUAL] = BIT(6), | |
533 | [SPI_TCR_CS_LEVEL] = BIT(7), | |
2b9d6a18 MK |
534 | [SPI_TCR_SDC] = BIT(11), |
535 | [SPI_TCR_SDM] = BIT(13), | |
853f4511 JT |
536 | [SPI_TCR_XCH] = BIT(31), |
537 | [SPI_FCR_RF_RST] = BIT(15), | |
538 | [SPI_FCR_TF_RST] = BIT(31), | |
539 | [SPI_FSR_RF_CNT_MASK] = GENMASK(7, 0), | |
540 | }; | |
541 | ||
8d9bf468 JT |
542 | static const struct sun4i_spi_variant sun4i_a10_spi_variant = { |
543 | .regs = sun4i_spi_regs, | |
544 | .bits = sun4i_spi_bits, | |
178fbd24 | 545 | .fifo_depth = 64, |
2b9d6a18 | 546 | .has_clk_ctl = true, |
8d9bf468 JT |
547 | }; |
548 | ||
853f4511 JT |
549 | static const struct sun4i_spi_variant sun6i_a31_spi_variant = { |
550 | .regs = sun6i_spi_regs, | |
551 | .bits = sun6i_spi_bits, | |
552 | .fifo_depth = 128, | |
553 | .has_soft_reset = true, | |
554 | .has_burst_ctl = true, | |
2b9d6a18 | 555 | .has_clk_ctl = true, |
853f4511 JT |
556 | }; |
557 | ||
558 | static const struct sun4i_spi_variant sun8i_h3_spi_variant = { | |
559 | .regs = sun6i_spi_regs, | |
560 | .bits = sun6i_spi_bits, | |
561 | .fifo_depth = 64, | |
562 | .has_soft_reset = true, | |
563 | .has_burst_ctl = true, | |
2b9d6a18 MK |
564 | .has_clk_ctl = true, |
565 | }; | |
566 | ||
567 | static const struct sun4i_spi_variant sun50i_r329_spi_variant = { | |
568 | .regs = sun6i_spi_regs, | |
569 | .bits = sun6i_spi_bits, | |
570 | .fifo_depth = 64, | |
571 | .has_soft_reset = true, | |
572 | .has_burst_ctl = true, | |
853f4511 JT |
573 | }; |
574 | ||
7f25d817 | 575 | static const struct udevice_id sun4i_spi_ids[] = { |
8d9bf468 JT |
576 | { |
577 | .compatible = "allwinner,sun4i-a10-spi", | |
578 | .data = (ulong)&sun4i_a10_spi_variant, | |
579 | }, | |
853f4511 JT |
580 | { |
581 | .compatible = "allwinner,sun6i-a31-spi", | |
582 | .data = (ulong)&sun6i_a31_spi_variant, | |
583 | }, | |
584 | { | |
585 | .compatible = "allwinner,sun8i-h3-spi", | |
586 | .data = (ulong)&sun8i_h3_spi_variant, | |
587 | }, | |
2b9d6a18 MK |
588 | { |
589 | .compatible = "allwinner,sun50i-r329-spi", | |
590 | .data = (ulong)&sun50i_r329_spi_variant, | |
591 | }, | |
903e7cf3 | 592 | { /* sentinel */ } |
7f25d817 SM |
593 | }; |
594 | ||
595 | U_BOOT_DRIVER(sun4i_spi) = { | |
596 | .name = "sun4i_spi", | |
597 | .id = UCLASS_SPI, | |
598 | .of_match = sun4i_spi_ids, | |
599 | .ops = &sun4i_spi_ops, | |
d1998a9f | 600 | .of_to_plat = sun4i_spi_of_to_plat, |
8a8d24bd | 601 | .plat_auto = sizeof(struct sun4i_spi_plat), |
41575d8e | 602 | .priv_auto = sizeof(struct sun4i_spi_priv), |
7f25d817 SM |
603 | .probe = sun4i_spi_probe, |
604 | }; |