blob: 85b662bb0adfee7dd84f8dd59833192ac433b0b0 [file] [log] [blame]
Stefan Mavrodiev7f25d812018-02-06 15:14:33 +02001/*
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
21#include <common.h>
Jagan Teki8d71a192019-02-27 20:02:10 +053022#include <clk.h>
Stefan Mavrodiev7f25d812018-02-06 15:14:33 +020023#include <dm.h>
Simon Glassf7ae49f2020-05-10 11:40:05 -060024#include <log.h>
Stefan Mavrodiev7f25d812018-02-06 15:14:33 +020025#include <spi.h>
26#include <errno.h>
27#include <fdt_support.h>
Jagan Teki853f4512019-02-27 20:02:11 +053028#include <reset.h>
Stefan Mavrodiev7f25d812018-02-06 15:14:33 +020029#include <wait_bit.h>
Simon Glass336d4612020-02-03 07:36:16 -070030#include <dm/device_compat.h>
Stefan Mavrodiev7f25d812018-02-06 15:14:33 +020031
32#include <asm/bitops.h>
33#include <asm/gpio.h>
34#include <asm/io.h>
35
Jagan Teki6cb6aa62019-02-27 20:02:05 +053036#include <linux/iopoll.h>
37
Jagan Teki903e7cf2019-02-27 20:02:12 +053038DECLARE_GLOBAL_DATA_PTR;
Stefan Mavrodiev7f25d812018-02-06 15:14:33 +020039
Jagan Teki903e7cf2019-02-27 20:02:12 +053040/* sun4i spi registers */
41#define SUN4I_RXDATA_REG 0x00
42#define SUN4I_TXDATA_REG 0x04
43#define SUN4I_CTL_REG 0x08
44#define SUN4I_CLK_CTL_REG 0x1c
45#define SUN4I_BURST_CNT_REG 0x20
46#define SUN4I_XMIT_CNT_REG 0x24
47#define SUN4I_FIFO_STA_REG 0x28
Stefan Mavrodiev7f25d812018-02-06 15:14:33 +020048
Jagan Teki853f4512019-02-27 20:02:11 +053049/* sun6i spi registers */
50#define SUN6I_GBL_CTL_REG 0x04
51#define SUN6I_TFR_CTL_REG 0x08
52#define SUN6I_FIFO_CTL_REG 0x18
53#define SUN6I_FIFO_STA_REG 0x1c
54#define SUN6I_CLK_CTL_REG 0x24
55#define SUN6I_BURST_CNT_REG 0x30
56#define SUN6I_XMIT_CNT_REG 0x34
57#define SUN6I_BURST_CTL_REG 0x38
58#define SUN6I_TXDATA_REG 0x200
59#define SUN6I_RXDATA_REG 0x300
60
Jagan Teki903e7cf2019-02-27 20:02:12 +053061/* sun spi bits */
62#define SUN4I_CTL_ENABLE BIT(0)
63#define SUN4I_CTL_MASTER BIT(1)
64#define SUN4I_CLK_CTL_CDR2_MASK 0xff
65#define SUN4I_CLK_CTL_CDR2(div) ((div) & SUN4I_CLK_CTL_CDR2_MASK)
66#define SUN4I_CLK_CTL_CDR1_MASK 0xf
67#define SUN4I_CLK_CTL_CDR1(div) (((div) & SUN4I_CLK_CTL_CDR1_MASK) << 8)
68#define SUN4I_CLK_CTL_DRS BIT(12)
69#define SUN4I_MAX_XFER_SIZE 0xffffff
70#define SUN4I_BURST_CNT(cnt) ((cnt) & SUN4I_MAX_XFER_SIZE)
71#define SUN4I_XMIT_CNT(cnt) ((cnt) & SUN4I_MAX_XFER_SIZE)
72#define SUN4I_FIFO_STA_RF_CNT_BITS 0
Stefan Mavrodiev7f25d812018-02-06 15:14:33 +020073
Jagan Teki903e7cf2019-02-27 20:02:12 +053074#define SUN4I_SPI_MAX_RATE 24000000
75#define SUN4I_SPI_MIN_RATE 3000
76#define SUN4I_SPI_DEFAULT_RATE 1000000
77#define SUN4I_SPI_TIMEOUT_US 1000000
78
79#define SPI_REG(priv, reg) ((priv)->base + \
Jagan Teki8d9bf462019-02-27 20:02:08 +053080 (priv)->variant->regs[reg])
81#define SPI_BIT(priv, bit) ((priv)->variant->bits[bit])
82#define SPI_CS(priv, cs) (((cs) << SPI_BIT(priv, SPI_TCR_CS_SEL)) & \
83 SPI_BIT(priv, SPI_TCR_CS_MASK))
84
85/* sun spi register set */
86enum sun4i_spi_regs {
87 SPI_GCR,
88 SPI_TCR,
89 SPI_FCR,
90 SPI_FSR,
91 SPI_CCR,
92 SPI_BC,
93 SPI_TC,
94 SPI_BCTL,
95 SPI_TXD,
96 SPI_RXD,
97};
98
99/* sun spi register bits */
100enum sun4i_spi_bits {
101 SPI_GCR_TP,
Jagan Teki853f4512019-02-27 20:02:11 +0530102 SPI_GCR_SRST,
Jagan Teki8d9bf462019-02-27 20:02:08 +0530103 SPI_TCR_CPHA,
104 SPI_TCR_CPOL,
105 SPI_TCR_CS_ACTIVE_LOW,
106 SPI_TCR_CS_SEL,
107 SPI_TCR_CS_MASK,
108 SPI_TCR_XCH,
109 SPI_TCR_CS_MANUAL,
110 SPI_TCR_CS_LEVEL,
111 SPI_FCR_TF_RST,
112 SPI_FCR_RF_RST,
113 SPI_FSR_RF_CNT_MASK,
114};
115
116struct sun4i_spi_variant {
117 const unsigned long *regs;
118 const u32 *bits;
Jagan Teki178fbd22019-02-27 20:02:09 +0530119 u32 fifo_depth;
Jagan Teki853f4512019-02-27 20:02:11 +0530120 bool has_soft_reset;
121 bool has_burst_ctl;
Stefan Mavrodiev7f25d812018-02-06 15:14:33 +0200122};
123
124struct sun4i_spi_platdata {
Jagan Teki8d9bf462019-02-27 20:02:08 +0530125 struct sun4i_spi_variant *variant;
Jagan Teki903e7cf2019-02-27 20:02:12 +0530126 u32 base;
Stefan Mavrodiev7f25d812018-02-06 15:14:33 +0200127 u32 max_hz;
128};
129
130struct sun4i_spi_priv {
Jagan Teki8d9bf462019-02-27 20:02:08 +0530131 struct sun4i_spi_variant *variant;
Jagan Teki8d71a192019-02-27 20:02:10 +0530132 struct clk clk_ahb, clk_mod;
Jagan Teki853f4512019-02-27 20:02:11 +0530133 struct reset_ctl reset;
Jagan Teki903e7cf2019-02-27 20:02:12 +0530134 u32 base;
Stefan Mavrodiev7f25d812018-02-06 15:14:33 +0200135 u32 freq;
136 u32 mode;
137
138 const u8 *tx_buf;
139 u8 *rx_buf;
140};
141
Stefan Mavrodiev7f25d812018-02-06 15:14:33 +0200142static inline void sun4i_spi_drain_fifo(struct sun4i_spi_priv *priv, int len)
143{
144 u8 byte;
145
146 while (len--) {
Jagan Teki8d9bf462019-02-27 20:02:08 +0530147 byte = readb(SPI_REG(priv, SPI_RXD));
Stefan Mavrodiev5c1a87d2018-12-05 14:27:57 +0200148 if (priv->rx_buf)
149 *priv->rx_buf++ = byte;
Stefan Mavrodiev7f25d812018-02-06 15:14:33 +0200150 }
151}
152
153static inline void sun4i_spi_fill_fifo(struct sun4i_spi_priv *priv, int len)
154{
155 u8 byte;
156
157 while (len--) {
158 byte = priv->tx_buf ? *priv->tx_buf++ : 0;
Jagan Teki8d9bf462019-02-27 20:02:08 +0530159 writeb(byte, SPI_REG(priv, SPI_TXD));
Stefan Mavrodiev7f25d812018-02-06 15:14:33 +0200160 }
161}
162
163static void sun4i_spi_set_cs(struct udevice *bus, u8 cs, bool enable)
164{
165 struct sun4i_spi_priv *priv = dev_get_priv(bus);
166 u32 reg;
167
Jagan Teki8d9bf462019-02-27 20:02:08 +0530168 reg = readl(SPI_REG(priv, SPI_TCR));
Stefan Mavrodiev7f25d812018-02-06 15:14:33 +0200169
Jagan Teki8d9bf462019-02-27 20:02:08 +0530170 reg &= ~SPI_BIT(priv, SPI_TCR_CS_MASK);
171 reg |= SPI_CS(priv, cs);
Stefan Mavrodiev7f25d812018-02-06 15:14:33 +0200172
173 if (enable)
Jagan Teki8d9bf462019-02-27 20:02:08 +0530174 reg &= ~SPI_BIT(priv, SPI_TCR_CS_LEVEL);
Stefan Mavrodiev7f25d812018-02-06 15:14:33 +0200175 else
Jagan Teki8d9bf462019-02-27 20:02:08 +0530176 reg |= SPI_BIT(priv, SPI_TCR_CS_LEVEL);
Stefan Mavrodiev7f25d812018-02-06 15:14:33 +0200177
Jagan Teki8d9bf462019-02-27 20:02:08 +0530178 writel(reg, SPI_REG(priv, SPI_TCR));
Stefan Mavrodiev7f25d812018-02-06 15:14:33 +0200179}
180
181static int sun4i_spi_parse_pins(struct udevice *dev)
182{
183 const void *fdt = gd->fdt_blob;
184 const char *pin_name;
185 const fdt32_t *list;
186 u32 phandle;
187 int drive, pull = 0, pin, i;
188 int offset;
189 int size;
190
191 list = fdt_getprop(fdt, dev_of_offset(dev), "pinctrl-0", &size);
192 if (!list) {
193 printf("WARNING: sun4i_spi: cannot find pinctrl-0 node\n");
194 return -EINVAL;
195 }
196
197 while (size) {
198 phandle = fdt32_to_cpu(*list++);
199 size -= sizeof(*list);
200
201 offset = fdt_node_offset_by_phandle(fdt, phandle);
202 if (offset < 0)
203 return offset;
204
205 drive = fdt_getprop_u32_default_node(fdt, offset, 0,
206 "drive-strength", 0);
207 if (drive) {
208 if (drive <= 10)
209 drive = 0;
210 else if (drive <= 20)
211 drive = 1;
212 else if (drive <= 30)
213 drive = 2;
214 else
215 drive = 3;
216 } else {
217 drive = fdt_getprop_u32_default_node(fdt, offset, 0,
218 "allwinner,drive",
219 0);
220 drive = min(drive, 3);
221 }
222
223 if (fdt_get_property(fdt, offset, "bias-disable", NULL))
224 pull = 0;
225 else if (fdt_get_property(fdt, offset, "bias-pull-up", NULL))
226 pull = 1;
227 else if (fdt_get_property(fdt, offset, "bias-pull-down", NULL))
228 pull = 2;
229 else
230 pull = fdt_getprop_u32_default_node(fdt, offset, 0,
231 "allwinner,pull",
232 0);
233 pull = min(pull, 2);
234
235 for (i = 0; ; i++) {
236 pin_name = fdt_stringlist_get(fdt, offset,
237 "pins", i, NULL);
238 if (!pin_name) {
239 pin_name = fdt_stringlist_get(fdt, offset,
240 "allwinner,pins",
241 i, NULL);
242 if (!pin_name)
243 break;
244 }
245
246 pin = name_to_gpio(pin_name);
247 if (pin < 0)
248 break;
249
Jagan Teki853f4512019-02-27 20:02:11 +0530250 if (IS_ENABLED(CONFIG_MACH_SUN50I))
251 sunxi_gpio_set_cfgpin(pin, SUN50I_GPC_SPI0);
252 else
253 sunxi_gpio_set_cfgpin(pin, SUNXI_GPC_SPI0);
Stefan Mavrodiev7f25d812018-02-06 15:14:33 +0200254 sunxi_gpio_set_drv(pin, drive);
255 sunxi_gpio_set_pull(pin, pull);
256 }
257 }
258 return 0;
259}
260
Jagan Teki8d71a192019-02-27 20:02:10 +0530261static inline int sun4i_spi_set_clock(struct udevice *dev, bool enable)
Stefan Mavrodiev7f25d812018-02-06 15:14:33 +0200262{
Jagan Teki8d71a192019-02-27 20:02:10 +0530263 struct sun4i_spi_priv *priv = dev_get_priv(dev);
264 int ret;
Stefan Mavrodiev7f25d812018-02-06 15:14:33 +0200265
Jagan Teki8d71a192019-02-27 20:02:10 +0530266 if (!enable) {
267 clk_disable(&priv->clk_ahb);
268 clk_disable(&priv->clk_mod);
Jagan Teki853f4512019-02-27 20:02:11 +0530269 if (reset_valid(&priv->reset))
270 reset_assert(&priv->reset);
Jagan Teki8d71a192019-02-27 20:02:10 +0530271 return 0;
272 }
273
274 ret = clk_enable(&priv->clk_ahb);
275 if (ret) {
276 dev_err(dev, "failed to enable ahb clock (ret=%d)\n", ret);
277 return ret;
278 }
279
280 ret = clk_enable(&priv->clk_mod);
281 if (ret) {
282 dev_err(dev, "failed to enable mod clock (ret=%d)\n", ret);
283 goto err_ahb;
284 }
285
Jagan Teki853f4512019-02-27 20:02:11 +0530286 if (reset_valid(&priv->reset)) {
287 ret = reset_deassert(&priv->reset);
288 if (ret) {
289 dev_err(dev, "failed to deassert reset\n");
290 goto err_mod;
291 }
292 }
293
Jagan Teki8d71a192019-02-27 20:02:10 +0530294 return 0;
295
Jagan Teki853f4512019-02-27 20:02:11 +0530296err_mod:
297 clk_disable(&priv->clk_mod);
Jagan Teki8d71a192019-02-27 20:02:10 +0530298err_ahb:
299 clk_disable(&priv->clk_ahb);
300 return ret;
Stefan Mavrodiev7f25d812018-02-06 15:14:33 +0200301}
302
Stefan Mavrodiev7f25d812018-02-06 15:14:33 +0200303static int sun4i_spi_claim_bus(struct udevice *dev)
304{
305 struct sun4i_spi_priv *priv = dev_get_priv(dev->parent);
Jagan Teki8d71a192019-02-27 20:02:10 +0530306 int ret;
307
308 ret = sun4i_spi_set_clock(dev->parent, true);
309 if (ret)
310 return ret;
Stefan Mavrodiev7f25d812018-02-06 15:14:33 +0200311
Jagan Teki8d9bf462019-02-27 20:02:08 +0530312 setbits_le32(SPI_REG(priv, SPI_GCR), SUN4I_CTL_ENABLE |
313 SUN4I_CTL_MASTER | SPI_BIT(priv, SPI_GCR_TP));
314
Jagan Teki853f4512019-02-27 20:02:11 +0530315 if (priv->variant->has_soft_reset)
316 setbits_le32(SPI_REG(priv, SPI_GCR),
317 SPI_BIT(priv, SPI_GCR_SRST));
318
Jagan Teki8d9bf462019-02-27 20:02:08 +0530319 setbits_le32(SPI_REG(priv, SPI_TCR), SPI_BIT(priv, SPI_TCR_CS_MANUAL) |
320 SPI_BIT(priv, SPI_TCR_CS_ACTIVE_LOW));
Jagan Teki8cbf09b2019-02-27 20:02:07 +0530321
Stefan Mavrodiev7f25d812018-02-06 15:14:33 +0200322 return 0;
323}
324
325static int sun4i_spi_release_bus(struct udevice *dev)
326{
327 struct sun4i_spi_priv *priv = dev_get_priv(dev->parent);
Stefan Mavrodiev7f25d812018-02-06 15:14:33 +0200328
Jagan Teki8d9bf462019-02-27 20:02:08 +0530329 clrbits_le32(SPI_REG(priv, SPI_GCR), SUN4I_CTL_ENABLE);
Stefan Mavrodiev7f25d812018-02-06 15:14:33 +0200330
Jagan Teki8d71a192019-02-27 20:02:10 +0530331 sun4i_spi_set_clock(dev->parent, false);
332
Stefan Mavrodiev7f25d812018-02-06 15:14:33 +0200333 return 0;
334}
335
336static 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);
341 struct dm_spi_slave_platdata *slave_plat = dev_get_parent_platdata(dev);
342
343 u32 len = bitlen / 8;
Jagan Teki8cbf09b2019-02-27 20:02:07 +0530344 u32 rx_fifocnt;
Stefan Mavrodiev7f25d812018-02-06 15:14:33 +0200345 u8 nbytes;
346 int ret;
347
348 priv->tx_buf = dout;
349 priv->rx_buf = din;
350
351 if (bitlen % 8) {
352 debug("%s: non byte-aligned SPI transfer.\n", __func__);
353 return -ENAVAIL;
354 }
355
356 if (flags & SPI_XFER_BEGIN)
357 sun4i_spi_set_cs(bus, slave_plat->cs, true);
358
Stefan Mavrodiev7f25d812018-02-06 15:14:33 +0200359 /* Reset FIFOs */
Jagan Teki8d9bf462019-02-27 20:02:08 +0530360 setbits_le32(SPI_REG(priv, SPI_FCR), SPI_BIT(priv, SPI_FCR_RF_RST) |
361 SPI_BIT(priv, SPI_FCR_TF_RST));
Stefan Mavrodiev7f25d812018-02-06 15:14:33 +0200362
363 while (len) {
364 /* Setup the transfer now... */
Jagan Teki178fbd22019-02-27 20:02:09 +0530365 nbytes = min(len, (priv->variant->fifo_depth - 1));
Stefan Mavrodiev7f25d812018-02-06 15:14:33 +0200366
367 /* Setup the counters */
Jagan Teki8d9bf462019-02-27 20:02:08 +0530368 writel(SUN4I_BURST_CNT(nbytes), SPI_REG(priv, SPI_BC));
369 writel(SUN4I_XMIT_CNT(nbytes), SPI_REG(priv, SPI_TC));
Stefan Mavrodiev7f25d812018-02-06 15:14:33 +0200370
Jagan Teki853f4512019-02-27 20:02:11 +0530371 if (priv->variant->has_burst_ctl)
372 writel(SUN4I_BURST_CNT(nbytes),
373 SPI_REG(priv, SPI_BCTL));
374
Stefan Mavrodiev7f25d812018-02-06 15:14:33 +0200375 /* Fill the TX FIFO */
376 sun4i_spi_fill_fifo(priv, nbytes);
377
378 /* Start the transfer */
Jagan Teki8d9bf462019-02-27 20:02:08 +0530379 setbits_le32(SPI_REG(priv, SPI_TCR),
380 SPI_BIT(priv, SPI_TCR_XCH));
Stefan Mavrodiev7f25d812018-02-06 15:14:33 +0200381
Jagan Teki6cb6aa62019-02-27 20:02:05 +0530382 /* Wait till RX FIFO to be empty */
Jagan Teki8d9bf462019-02-27 20:02:08 +0530383 ret = readl_poll_timeout(SPI_REG(priv, SPI_FSR),
384 rx_fifocnt,
385 (((rx_fifocnt &
386 SPI_BIT(priv, SPI_FSR_RF_CNT_MASK)) >>
Jagan Teki6cb6aa62019-02-27 20:02:05 +0530387 SUN4I_FIFO_STA_RF_CNT_BITS) >= nbytes),
388 SUN4I_SPI_TIMEOUT_US);
389 if (ret < 0) {
Stefan Mavrodiev7f25d812018-02-06 15:14:33 +0200390 printf("ERROR: sun4i_spi: Timeout transferring data\n");
391 sun4i_spi_set_cs(bus, slave_plat->cs, false);
392 return ret;
393 }
394
395 /* Drain the RX FIFO */
396 sun4i_spi_drain_fifo(priv, nbytes);
397
398 len -= nbytes;
399 }
400
401 if (flags & SPI_XFER_END)
402 sun4i_spi_set_cs(bus, slave_plat->cs, false);
403
404 return 0;
405}
406
407static int sun4i_spi_set_speed(struct udevice *dev, uint speed)
408{
409 struct sun4i_spi_platdata *plat = dev_get_platdata(dev);
410 struct sun4i_spi_priv *priv = dev_get_priv(dev);
411 unsigned int div;
412 u32 reg;
413
414 if (speed > plat->max_hz)
415 speed = plat->max_hz;
416
417 if (speed < SUN4I_SPI_MIN_RATE)
418 speed = SUN4I_SPI_MIN_RATE;
419 /*
420 * Setup clock divider.
421 *
422 * We have two choices there. Either we can use the clock
423 * divide rate 1, which is calculated thanks to this formula:
424 * SPI_CLK = MOD_CLK / (2 ^ (cdr + 1))
425 * Or we can use CDR2, which is calculated with the formula:
426 * SPI_CLK = MOD_CLK / (2 * (cdr + 1))
427 * Whether we use the former or the latter is set through the
428 * DRS bit.
429 *
430 * First try CDR2, and if we can't reach the expected
431 * frequency, fall back to CDR1.
432 */
433
434 div = SUN4I_SPI_MAX_RATE / (2 * speed);
Jagan Teki8d9bf462019-02-27 20:02:08 +0530435 reg = readl(SPI_REG(priv, SPI_CCR));
Stefan Mavrodiev7f25d812018-02-06 15:14:33 +0200436
437 if (div <= (SUN4I_CLK_CTL_CDR2_MASK + 1)) {
438 if (div > 0)
439 div--;
440
441 reg &= ~(SUN4I_CLK_CTL_CDR2_MASK | SUN4I_CLK_CTL_DRS);
442 reg |= SUN4I_CLK_CTL_CDR2(div) | SUN4I_CLK_CTL_DRS;
443 } else {
444 div = __ilog2(SUN4I_SPI_MAX_RATE) - __ilog2(speed);
445 reg &= ~((SUN4I_CLK_CTL_CDR1_MASK << 8) | SUN4I_CLK_CTL_DRS);
446 reg |= SUN4I_CLK_CTL_CDR1(div);
447 }
448
449 priv->freq = speed;
Jagan Teki8d9bf462019-02-27 20:02:08 +0530450 writel(reg, SPI_REG(priv, SPI_CCR));
Stefan Mavrodiev7f25d812018-02-06 15:14:33 +0200451
452 return 0;
453}
454
455static int sun4i_spi_set_mode(struct udevice *dev, uint mode)
456{
457 struct sun4i_spi_priv *priv = dev_get_priv(dev);
458 u32 reg;
459
Jagan Teki8d9bf462019-02-27 20:02:08 +0530460 reg = readl(SPI_REG(priv, SPI_TCR));
461 reg &= ~(SPI_BIT(priv, SPI_TCR_CPOL) | SPI_BIT(priv, SPI_TCR_CPHA));
Stefan Mavrodiev7f25d812018-02-06 15:14:33 +0200462
463 if (mode & SPI_CPOL)
Jagan Teki8d9bf462019-02-27 20:02:08 +0530464 reg |= SPI_BIT(priv, SPI_TCR_CPOL);
Stefan Mavrodiev7f25d812018-02-06 15:14:33 +0200465
466 if (mode & SPI_CPHA)
Jagan Teki8d9bf462019-02-27 20:02:08 +0530467 reg |= SPI_BIT(priv, SPI_TCR_CPHA);
Stefan Mavrodiev7f25d812018-02-06 15:14:33 +0200468
469 priv->mode = mode;
Jagan Teki8d9bf462019-02-27 20:02:08 +0530470 writel(reg, SPI_REG(priv, SPI_TCR));
Stefan Mavrodiev7f25d812018-02-06 15:14:33 +0200471
472 return 0;
473}
474
475static const struct dm_spi_ops sun4i_spi_ops = {
476 .claim_bus = sun4i_spi_claim_bus,
477 .release_bus = sun4i_spi_release_bus,
478 .xfer = sun4i_spi_xfer,
479 .set_speed = sun4i_spi_set_speed,
480 .set_mode = sun4i_spi_set_mode,
481};
482
Jagan Teki903e7cf2019-02-27 20:02:12 +0530483static int sun4i_spi_probe(struct udevice *bus)
484{
485 struct sun4i_spi_platdata *plat = dev_get_platdata(bus);
486 struct sun4i_spi_priv *priv = dev_get_priv(bus);
487 int ret;
488
489 ret = clk_get_by_name(bus, "ahb", &priv->clk_ahb);
490 if (ret) {
491 dev_err(dev, "failed to get ahb clock\n");
492 return ret;
493 }
494
495 ret = clk_get_by_name(bus, "mod", &priv->clk_mod);
496 if (ret) {
497 dev_err(dev, "failed to get mod clock\n");
498 return ret;
499 }
500
501 ret = reset_get_by_index(bus, 0, &priv->reset);
502 if (ret && ret != -ENOENT) {
503 dev_err(dev, "failed to get reset\n");
504 return ret;
505 }
506
507 sun4i_spi_parse_pins(bus);
508
509 priv->variant = plat->variant;
510 priv->base = plat->base;
511 priv->freq = plat->max_hz;
512
513 return 0;
514}
515
516static int sun4i_spi_ofdata_to_platdata(struct udevice *bus)
517{
518 struct sun4i_spi_platdata *plat = dev_get_platdata(bus);
519 int node = dev_of_offset(bus);
520
521 plat->base = devfdt_get_addr(bus);
522 plat->variant = (struct sun4i_spi_variant *)dev_get_driver_data(bus);
523 plat->max_hz = fdtdec_get_int(gd->fdt_blob, node,
524 "spi-max-frequency",
525 SUN4I_SPI_DEFAULT_RATE);
526
527 if (plat->max_hz > SUN4I_SPI_MAX_RATE)
528 plat->max_hz = SUN4I_SPI_MAX_RATE;
529
530 return 0;
531}
532
Jagan Teki8d9bf462019-02-27 20:02:08 +0530533static const unsigned long sun4i_spi_regs[] = {
534 [SPI_GCR] = SUN4I_CTL_REG,
535 [SPI_TCR] = SUN4I_CTL_REG,
536 [SPI_FCR] = SUN4I_CTL_REG,
537 [SPI_FSR] = SUN4I_FIFO_STA_REG,
538 [SPI_CCR] = SUN4I_CLK_CTL_REG,
539 [SPI_BC] = SUN4I_BURST_CNT_REG,
540 [SPI_TC] = SUN4I_XMIT_CNT_REG,
541 [SPI_TXD] = SUN4I_TXDATA_REG,
542 [SPI_RXD] = SUN4I_RXDATA_REG,
543};
544
545static const u32 sun4i_spi_bits[] = {
546 [SPI_GCR_TP] = BIT(18),
547 [SPI_TCR_CPHA] = BIT(2),
548 [SPI_TCR_CPOL] = BIT(3),
549 [SPI_TCR_CS_ACTIVE_LOW] = BIT(4),
550 [SPI_TCR_XCH] = BIT(10),
551 [SPI_TCR_CS_SEL] = 12,
552 [SPI_TCR_CS_MASK] = 0x3000,
553 [SPI_TCR_CS_MANUAL] = BIT(16),
554 [SPI_TCR_CS_LEVEL] = BIT(17),
555 [SPI_FCR_TF_RST] = BIT(8),
556 [SPI_FCR_RF_RST] = BIT(9),
557 [SPI_FSR_RF_CNT_MASK] = GENMASK(6, 0),
558};
559
Jagan Teki853f4512019-02-27 20:02:11 +0530560static const unsigned long sun6i_spi_regs[] = {
561 [SPI_GCR] = SUN6I_GBL_CTL_REG,
562 [SPI_TCR] = SUN6I_TFR_CTL_REG,
563 [SPI_FCR] = SUN6I_FIFO_CTL_REG,
564 [SPI_FSR] = SUN6I_FIFO_STA_REG,
565 [SPI_CCR] = SUN6I_CLK_CTL_REG,
566 [SPI_BC] = SUN6I_BURST_CNT_REG,
567 [SPI_TC] = SUN6I_XMIT_CNT_REG,
568 [SPI_BCTL] = SUN6I_BURST_CTL_REG,
569 [SPI_TXD] = SUN6I_TXDATA_REG,
570 [SPI_RXD] = SUN6I_RXDATA_REG,
571};
572
573static const u32 sun6i_spi_bits[] = {
574 [SPI_GCR_TP] = BIT(7),
575 [SPI_GCR_SRST] = BIT(31),
576 [SPI_TCR_CPHA] = BIT(0),
577 [SPI_TCR_CPOL] = BIT(1),
578 [SPI_TCR_CS_ACTIVE_LOW] = BIT(2),
579 [SPI_TCR_CS_SEL] = 4,
580 [SPI_TCR_CS_MASK] = 0x30,
581 [SPI_TCR_CS_MANUAL] = BIT(6),
582 [SPI_TCR_CS_LEVEL] = BIT(7),
583 [SPI_TCR_XCH] = BIT(31),
584 [SPI_FCR_RF_RST] = BIT(15),
585 [SPI_FCR_TF_RST] = BIT(31),
586 [SPI_FSR_RF_CNT_MASK] = GENMASK(7, 0),
587};
588
Jagan Teki8d9bf462019-02-27 20:02:08 +0530589static const struct sun4i_spi_variant sun4i_a10_spi_variant = {
590 .regs = sun4i_spi_regs,
591 .bits = sun4i_spi_bits,
Jagan Teki178fbd22019-02-27 20:02:09 +0530592 .fifo_depth = 64,
Jagan Teki8d9bf462019-02-27 20:02:08 +0530593};
594
Jagan Teki853f4512019-02-27 20:02:11 +0530595static const struct sun4i_spi_variant sun6i_a31_spi_variant = {
596 .regs = sun6i_spi_regs,
597 .bits = sun6i_spi_bits,
598 .fifo_depth = 128,
599 .has_soft_reset = true,
600 .has_burst_ctl = true,
601};
602
603static const struct sun4i_spi_variant sun8i_h3_spi_variant = {
604 .regs = sun6i_spi_regs,
605 .bits = sun6i_spi_bits,
606 .fifo_depth = 64,
607 .has_soft_reset = true,
608 .has_burst_ctl = true,
609};
610
Stefan Mavrodiev7f25d812018-02-06 15:14:33 +0200611static const struct udevice_id sun4i_spi_ids[] = {
Jagan Teki8d9bf462019-02-27 20:02:08 +0530612 {
613 .compatible = "allwinner,sun4i-a10-spi",
614 .data = (ulong)&sun4i_a10_spi_variant,
615 },
Jagan Teki853f4512019-02-27 20:02:11 +0530616 {
617 .compatible = "allwinner,sun6i-a31-spi",
618 .data = (ulong)&sun6i_a31_spi_variant,
619 },
620 {
621 .compatible = "allwinner,sun8i-h3-spi",
622 .data = (ulong)&sun8i_h3_spi_variant,
623 },
Jagan Teki903e7cf2019-02-27 20:02:12 +0530624 { /* sentinel */ }
Stefan Mavrodiev7f25d812018-02-06 15:14:33 +0200625};
626
627U_BOOT_DRIVER(sun4i_spi) = {
628 .name = "sun4i_spi",
629 .id = UCLASS_SPI,
630 .of_match = sun4i_spi_ids,
631 .ops = &sun4i_spi_ops,
632 .ofdata_to_platdata = sun4i_spi_ofdata_to_platdata,
633 .platdata_auto_alloc_size = sizeof(struct sun4i_spi_platdata),
634 .priv_auto_alloc_size = sizeof(struct sun4i_spi_priv),
635 .probe = sun4i_spi_probe,
636};