blob: c59fee10a89e2020ddba975531696f51020b8961 [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>
24#include <spi.h>
25#include <errno.h>
26#include <fdt_support.h>
Jagan Teki853f4512019-02-27 20:02:11 +053027#include <reset.h>
Stefan Mavrodiev7f25d812018-02-06 15:14:33 +020028#include <wait_bit.h>
Simon Glass336d4612020-02-03 07:36:16 -070029#include <dm/device_compat.h>
Stefan Mavrodiev7f25d812018-02-06 15:14:33 +020030
31#include <asm/bitops.h>
32#include <asm/gpio.h>
33#include <asm/io.h>
34
Jagan Teki6cb6aa62019-02-27 20:02:05 +053035#include <linux/iopoll.h>
36
Jagan Teki903e7cf2019-02-27 20:02:12 +053037DECLARE_GLOBAL_DATA_PTR;
Stefan Mavrodiev7f25d812018-02-06 15:14:33 +020038
Jagan Teki903e7cf2019-02-27 20:02:12 +053039/* sun4i spi registers */
40#define SUN4I_RXDATA_REG 0x00
41#define SUN4I_TXDATA_REG 0x04
42#define SUN4I_CTL_REG 0x08
43#define SUN4I_CLK_CTL_REG 0x1c
44#define SUN4I_BURST_CNT_REG 0x20
45#define SUN4I_XMIT_CNT_REG 0x24
46#define SUN4I_FIFO_STA_REG 0x28
Stefan Mavrodiev7f25d812018-02-06 15:14:33 +020047
Jagan Teki853f4512019-02-27 20:02:11 +053048/* sun6i spi registers */
49#define SUN6I_GBL_CTL_REG 0x04
50#define SUN6I_TFR_CTL_REG 0x08
51#define SUN6I_FIFO_CTL_REG 0x18
52#define SUN6I_FIFO_STA_REG 0x1c
53#define SUN6I_CLK_CTL_REG 0x24
54#define SUN6I_BURST_CNT_REG 0x30
55#define SUN6I_XMIT_CNT_REG 0x34
56#define SUN6I_BURST_CTL_REG 0x38
57#define SUN6I_TXDATA_REG 0x200
58#define SUN6I_RXDATA_REG 0x300
59
Jagan Teki903e7cf2019-02-27 20:02:12 +053060/* sun spi bits */
61#define SUN4I_CTL_ENABLE BIT(0)
62#define SUN4I_CTL_MASTER BIT(1)
63#define SUN4I_CLK_CTL_CDR2_MASK 0xff
64#define SUN4I_CLK_CTL_CDR2(div) ((div) & SUN4I_CLK_CTL_CDR2_MASK)
65#define SUN4I_CLK_CTL_CDR1_MASK 0xf
66#define SUN4I_CLK_CTL_CDR1(div) (((div) & SUN4I_CLK_CTL_CDR1_MASK) << 8)
67#define SUN4I_CLK_CTL_DRS BIT(12)
68#define SUN4I_MAX_XFER_SIZE 0xffffff
69#define SUN4I_BURST_CNT(cnt) ((cnt) & SUN4I_MAX_XFER_SIZE)
70#define SUN4I_XMIT_CNT(cnt) ((cnt) & SUN4I_MAX_XFER_SIZE)
71#define SUN4I_FIFO_STA_RF_CNT_BITS 0
Stefan Mavrodiev7f25d812018-02-06 15:14:33 +020072
Jagan Teki903e7cf2019-02-27 20:02:12 +053073#define SUN4I_SPI_MAX_RATE 24000000
74#define SUN4I_SPI_MIN_RATE 3000
75#define SUN4I_SPI_DEFAULT_RATE 1000000
76#define SUN4I_SPI_TIMEOUT_US 1000000
77
78#define SPI_REG(priv, reg) ((priv)->base + \
Jagan Teki8d9bf462019-02-27 20:02:08 +053079 (priv)->variant->regs[reg])
80#define SPI_BIT(priv, bit) ((priv)->variant->bits[bit])
81#define SPI_CS(priv, cs) (((cs) << SPI_BIT(priv, SPI_TCR_CS_SEL)) & \
82 SPI_BIT(priv, SPI_TCR_CS_MASK))
83
84/* sun spi register set */
85enum sun4i_spi_regs {
86 SPI_GCR,
87 SPI_TCR,
88 SPI_FCR,
89 SPI_FSR,
90 SPI_CCR,
91 SPI_BC,
92 SPI_TC,
93 SPI_BCTL,
94 SPI_TXD,
95 SPI_RXD,
96};
97
98/* sun spi register bits */
99enum sun4i_spi_bits {
100 SPI_GCR_TP,
Jagan Teki853f4512019-02-27 20:02:11 +0530101 SPI_GCR_SRST,
Jagan Teki8d9bf462019-02-27 20:02:08 +0530102 SPI_TCR_CPHA,
103 SPI_TCR_CPOL,
104 SPI_TCR_CS_ACTIVE_LOW,
105 SPI_TCR_CS_SEL,
106 SPI_TCR_CS_MASK,
107 SPI_TCR_XCH,
108 SPI_TCR_CS_MANUAL,
109 SPI_TCR_CS_LEVEL,
110 SPI_FCR_TF_RST,
111 SPI_FCR_RF_RST,
112 SPI_FSR_RF_CNT_MASK,
113};
114
115struct sun4i_spi_variant {
116 const unsigned long *regs;
117 const u32 *bits;
Jagan Teki178fbd22019-02-27 20:02:09 +0530118 u32 fifo_depth;
Jagan Teki853f4512019-02-27 20:02:11 +0530119 bool has_soft_reset;
120 bool has_burst_ctl;
Stefan Mavrodiev7f25d812018-02-06 15:14:33 +0200121};
122
123struct sun4i_spi_platdata {
Jagan Teki8d9bf462019-02-27 20:02:08 +0530124 struct sun4i_spi_variant *variant;
Jagan Teki903e7cf2019-02-27 20:02:12 +0530125 u32 base;
Stefan Mavrodiev7f25d812018-02-06 15:14:33 +0200126 u32 max_hz;
127};
128
129struct sun4i_spi_priv {
Jagan Teki8d9bf462019-02-27 20:02:08 +0530130 struct sun4i_spi_variant *variant;
Jagan Teki8d71a192019-02-27 20:02:10 +0530131 struct clk clk_ahb, clk_mod;
Jagan Teki853f4512019-02-27 20:02:11 +0530132 struct reset_ctl reset;
Jagan Teki903e7cf2019-02-27 20:02:12 +0530133 u32 base;
Stefan Mavrodiev7f25d812018-02-06 15:14:33 +0200134 u32 freq;
135 u32 mode;
136
137 const u8 *tx_buf;
138 u8 *rx_buf;
139};
140
Stefan Mavrodiev7f25d812018-02-06 15:14:33 +0200141static inline void sun4i_spi_drain_fifo(struct sun4i_spi_priv *priv, int len)
142{
143 u8 byte;
144
145 while (len--) {
Jagan Teki8d9bf462019-02-27 20:02:08 +0530146 byte = readb(SPI_REG(priv, SPI_RXD));
Stefan Mavrodiev5c1a87d2018-12-05 14:27:57 +0200147 if (priv->rx_buf)
148 *priv->rx_buf++ = byte;
Stefan Mavrodiev7f25d812018-02-06 15:14:33 +0200149 }
150}
151
152static inline void sun4i_spi_fill_fifo(struct sun4i_spi_priv *priv, int len)
153{
154 u8 byte;
155
156 while (len--) {
157 byte = priv->tx_buf ? *priv->tx_buf++ : 0;
Jagan Teki8d9bf462019-02-27 20:02:08 +0530158 writeb(byte, SPI_REG(priv, SPI_TXD));
Stefan Mavrodiev7f25d812018-02-06 15:14:33 +0200159 }
160}
161
162static void sun4i_spi_set_cs(struct udevice *bus, u8 cs, bool enable)
163{
164 struct sun4i_spi_priv *priv = dev_get_priv(bus);
165 u32 reg;
166
Jagan Teki8d9bf462019-02-27 20:02:08 +0530167 reg = readl(SPI_REG(priv, SPI_TCR));
Stefan Mavrodiev7f25d812018-02-06 15:14:33 +0200168
Jagan Teki8d9bf462019-02-27 20:02:08 +0530169 reg &= ~SPI_BIT(priv, SPI_TCR_CS_MASK);
170 reg |= SPI_CS(priv, cs);
Stefan Mavrodiev7f25d812018-02-06 15:14:33 +0200171
172 if (enable)
Jagan Teki8d9bf462019-02-27 20:02:08 +0530173 reg &= ~SPI_BIT(priv, SPI_TCR_CS_LEVEL);
Stefan Mavrodiev7f25d812018-02-06 15:14:33 +0200174 else
Jagan Teki8d9bf462019-02-27 20:02:08 +0530175 reg |= SPI_BIT(priv, SPI_TCR_CS_LEVEL);
Stefan Mavrodiev7f25d812018-02-06 15:14:33 +0200176
Jagan Teki8d9bf462019-02-27 20:02:08 +0530177 writel(reg, SPI_REG(priv, SPI_TCR));
Stefan Mavrodiev7f25d812018-02-06 15:14:33 +0200178}
179
180static int sun4i_spi_parse_pins(struct udevice *dev)
181{
182 const void *fdt = gd->fdt_blob;
183 const char *pin_name;
184 const fdt32_t *list;
185 u32 phandle;
186 int drive, pull = 0, pin, i;
187 int offset;
188 int size;
189
190 list = fdt_getprop(fdt, dev_of_offset(dev), "pinctrl-0", &size);
191 if (!list) {
192 printf("WARNING: sun4i_spi: cannot find pinctrl-0 node\n");
193 return -EINVAL;
194 }
195
196 while (size) {
197 phandle = fdt32_to_cpu(*list++);
198 size -= sizeof(*list);
199
200 offset = fdt_node_offset_by_phandle(fdt, phandle);
201 if (offset < 0)
202 return offset;
203
204 drive = fdt_getprop_u32_default_node(fdt, offset, 0,
205 "drive-strength", 0);
206 if (drive) {
207 if (drive <= 10)
208 drive = 0;
209 else if (drive <= 20)
210 drive = 1;
211 else if (drive <= 30)
212 drive = 2;
213 else
214 drive = 3;
215 } else {
216 drive = fdt_getprop_u32_default_node(fdt, offset, 0,
217 "allwinner,drive",
218 0);
219 drive = min(drive, 3);
220 }
221
222 if (fdt_get_property(fdt, offset, "bias-disable", NULL))
223 pull = 0;
224 else if (fdt_get_property(fdt, offset, "bias-pull-up", NULL))
225 pull = 1;
226 else if (fdt_get_property(fdt, offset, "bias-pull-down", NULL))
227 pull = 2;
228 else
229 pull = fdt_getprop_u32_default_node(fdt, offset, 0,
230 "allwinner,pull",
231 0);
232 pull = min(pull, 2);
233
234 for (i = 0; ; i++) {
235 pin_name = fdt_stringlist_get(fdt, offset,
236 "pins", i, NULL);
237 if (!pin_name) {
238 pin_name = fdt_stringlist_get(fdt, offset,
239 "allwinner,pins",
240 i, NULL);
241 if (!pin_name)
242 break;
243 }
244
245 pin = name_to_gpio(pin_name);
246 if (pin < 0)
247 break;
248
Jagan Teki853f4512019-02-27 20:02:11 +0530249 if (IS_ENABLED(CONFIG_MACH_SUN50I))
250 sunxi_gpio_set_cfgpin(pin, SUN50I_GPC_SPI0);
251 else
252 sunxi_gpio_set_cfgpin(pin, SUNXI_GPC_SPI0);
Stefan Mavrodiev7f25d812018-02-06 15:14:33 +0200253 sunxi_gpio_set_drv(pin, drive);
254 sunxi_gpio_set_pull(pin, pull);
255 }
256 }
257 return 0;
258}
259
Jagan Teki8d71a192019-02-27 20:02:10 +0530260static inline int sun4i_spi_set_clock(struct udevice *dev, bool enable)
Stefan Mavrodiev7f25d812018-02-06 15:14:33 +0200261{
Jagan Teki8d71a192019-02-27 20:02:10 +0530262 struct sun4i_spi_priv *priv = dev_get_priv(dev);
263 int ret;
Stefan Mavrodiev7f25d812018-02-06 15:14:33 +0200264
Jagan Teki8d71a192019-02-27 20:02:10 +0530265 if (!enable) {
266 clk_disable(&priv->clk_ahb);
267 clk_disable(&priv->clk_mod);
Jagan Teki853f4512019-02-27 20:02:11 +0530268 if (reset_valid(&priv->reset))
269 reset_assert(&priv->reset);
Jagan Teki8d71a192019-02-27 20:02:10 +0530270 return 0;
271 }
272
273 ret = clk_enable(&priv->clk_ahb);
274 if (ret) {
275 dev_err(dev, "failed to enable ahb clock (ret=%d)\n", ret);
276 return ret;
277 }
278
279 ret = clk_enable(&priv->clk_mod);
280 if (ret) {
281 dev_err(dev, "failed to enable mod clock (ret=%d)\n", ret);
282 goto err_ahb;
283 }
284
Jagan Teki853f4512019-02-27 20:02:11 +0530285 if (reset_valid(&priv->reset)) {
286 ret = reset_deassert(&priv->reset);
287 if (ret) {
288 dev_err(dev, "failed to deassert reset\n");
289 goto err_mod;
290 }
291 }
292
Jagan Teki8d71a192019-02-27 20:02:10 +0530293 return 0;
294
Jagan Teki853f4512019-02-27 20:02:11 +0530295err_mod:
296 clk_disable(&priv->clk_mod);
Jagan Teki8d71a192019-02-27 20:02:10 +0530297err_ahb:
298 clk_disable(&priv->clk_ahb);
299 return ret;
Stefan Mavrodiev7f25d812018-02-06 15:14:33 +0200300}
301
Stefan Mavrodiev7f25d812018-02-06 15:14:33 +0200302static int sun4i_spi_claim_bus(struct udevice *dev)
303{
304 struct sun4i_spi_priv *priv = dev_get_priv(dev->parent);
Jagan Teki8d71a192019-02-27 20:02:10 +0530305 int ret;
306
307 ret = sun4i_spi_set_clock(dev->parent, true);
308 if (ret)
309 return ret;
Stefan Mavrodiev7f25d812018-02-06 15:14:33 +0200310
Jagan Teki8d9bf462019-02-27 20:02:08 +0530311 setbits_le32(SPI_REG(priv, SPI_GCR), SUN4I_CTL_ENABLE |
312 SUN4I_CTL_MASTER | SPI_BIT(priv, SPI_GCR_TP));
313
Jagan Teki853f4512019-02-27 20:02:11 +0530314 if (priv->variant->has_soft_reset)
315 setbits_le32(SPI_REG(priv, SPI_GCR),
316 SPI_BIT(priv, SPI_GCR_SRST));
317
Jagan Teki8d9bf462019-02-27 20:02:08 +0530318 setbits_le32(SPI_REG(priv, SPI_TCR), SPI_BIT(priv, SPI_TCR_CS_MANUAL) |
319 SPI_BIT(priv, SPI_TCR_CS_ACTIVE_LOW));
Jagan Teki8cbf09b2019-02-27 20:02:07 +0530320
Stefan Mavrodiev7f25d812018-02-06 15:14:33 +0200321 return 0;
322}
323
324static int sun4i_spi_release_bus(struct udevice *dev)
325{
326 struct sun4i_spi_priv *priv = dev_get_priv(dev->parent);
Stefan Mavrodiev7f25d812018-02-06 15:14:33 +0200327
Jagan Teki8d9bf462019-02-27 20:02:08 +0530328 clrbits_le32(SPI_REG(priv, SPI_GCR), SUN4I_CTL_ENABLE);
Stefan Mavrodiev7f25d812018-02-06 15:14:33 +0200329
Jagan Teki8d71a192019-02-27 20:02:10 +0530330 sun4i_spi_set_clock(dev->parent, false);
331
Stefan Mavrodiev7f25d812018-02-06 15:14:33 +0200332 return 0;
333}
334
335static int sun4i_spi_xfer(struct udevice *dev, unsigned int bitlen,
336 const void *dout, void *din, unsigned long flags)
337{
338 struct udevice *bus = dev->parent;
339 struct sun4i_spi_priv *priv = dev_get_priv(bus);
340 struct dm_spi_slave_platdata *slave_plat = dev_get_parent_platdata(dev);
341
342 u32 len = bitlen / 8;
Jagan Teki8cbf09b2019-02-27 20:02:07 +0530343 u32 rx_fifocnt;
Stefan Mavrodiev7f25d812018-02-06 15:14:33 +0200344 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
Stefan Mavrodiev7f25d812018-02-06 15:14:33 +0200358 /* Reset FIFOs */
Jagan Teki8d9bf462019-02-27 20:02:08 +0530359 setbits_le32(SPI_REG(priv, SPI_FCR), SPI_BIT(priv, SPI_FCR_RF_RST) |
360 SPI_BIT(priv, SPI_FCR_TF_RST));
Stefan Mavrodiev7f25d812018-02-06 15:14:33 +0200361
362 while (len) {
363 /* Setup the transfer now... */
Jagan Teki178fbd22019-02-27 20:02:09 +0530364 nbytes = min(len, (priv->variant->fifo_depth - 1));
Stefan Mavrodiev7f25d812018-02-06 15:14:33 +0200365
366 /* Setup the counters */
Jagan Teki8d9bf462019-02-27 20:02:08 +0530367 writel(SUN4I_BURST_CNT(nbytes), SPI_REG(priv, SPI_BC));
368 writel(SUN4I_XMIT_CNT(nbytes), SPI_REG(priv, SPI_TC));
Stefan Mavrodiev7f25d812018-02-06 15:14:33 +0200369
Jagan Teki853f4512019-02-27 20:02:11 +0530370 if (priv->variant->has_burst_ctl)
371 writel(SUN4I_BURST_CNT(nbytes),
372 SPI_REG(priv, SPI_BCTL));
373
Stefan Mavrodiev7f25d812018-02-06 15:14:33 +0200374 /* Fill the TX FIFO */
375 sun4i_spi_fill_fifo(priv, nbytes);
376
377 /* Start the transfer */
Jagan Teki8d9bf462019-02-27 20:02:08 +0530378 setbits_le32(SPI_REG(priv, SPI_TCR),
379 SPI_BIT(priv, SPI_TCR_XCH));
Stefan Mavrodiev7f25d812018-02-06 15:14:33 +0200380
Jagan Teki6cb6aa62019-02-27 20:02:05 +0530381 /* Wait till RX FIFO to be empty */
Jagan Teki8d9bf462019-02-27 20:02:08 +0530382 ret = readl_poll_timeout(SPI_REG(priv, SPI_FSR),
383 rx_fifocnt,
384 (((rx_fifocnt &
385 SPI_BIT(priv, SPI_FSR_RF_CNT_MASK)) >>
Jagan Teki6cb6aa62019-02-27 20:02:05 +0530386 SUN4I_FIFO_STA_RF_CNT_BITS) >= nbytes),
387 SUN4I_SPI_TIMEOUT_US);
388 if (ret < 0) {
Stefan Mavrodiev7f25d812018-02-06 15:14:33 +0200389 printf("ERROR: sun4i_spi: Timeout transferring data\n");
390 sun4i_spi_set_cs(bus, slave_plat->cs, false);
391 return ret;
392 }
393
394 /* Drain the RX FIFO */
395 sun4i_spi_drain_fifo(priv, nbytes);
396
397 len -= nbytes;
398 }
399
400 if (flags & SPI_XFER_END)
401 sun4i_spi_set_cs(bus, slave_plat->cs, false);
402
403 return 0;
404}
405
406static int sun4i_spi_set_speed(struct udevice *dev, uint speed)
407{
408 struct sun4i_spi_platdata *plat = dev_get_platdata(dev);
409 struct sun4i_spi_priv *priv = dev_get_priv(dev);
410 unsigned int div;
411 u32 reg;
412
413 if (speed > plat->max_hz)
414 speed = plat->max_hz;
415
416 if (speed < SUN4I_SPI_MIN_RATE)
417 speed = SUN4I_SPI_MIN_RATE;
418 /*
419 * Setup clock divider.
420 *
421 * We have two choices there. Either we can use the clock
422 * divide rate 1, which is calculated thanks to this formula:
423 * SPI_CLK = MOD_CLK / (2 ^ (cdr + 1))
424 * Or we can use CDR2, which is calculated with the formula:
425 * SPI_CLK = MOD_CLK / (2 * (cdr + 1))
426 * Whether we use the former or the latter is set through the
427 * DRS bit.
428 *
429 * First try CDR2, and if we can't reach the expected
430 * frequency, fall back to CDR1.
431 */
432
433 div = SUN4I_SPI_MAX_RATE / (2 * speed);
Jagan Teki8d9bf462019-02-27 20:02:08 +0530434 reg = readl(SPI_REG(priv, SPI_CCR));
Stefan Mavrodiev7f25d812018-02-06 15:14:33 +0200435
436 if (div <= (SUN4I_CLK_CTL_CDR2_MASK + 1)) {
437 if (div > 0)
438 div--;
439
440 reg &= ~(SUN4I_CLK_CTL_CDR2_MASK | SUN4I_CLK_CTL_DRS);
441 reg |= SUN4I_CLK_CTL_CDR2(div) | SUN4I_CLK_CTL_DRS;
442 } else {
443 div = __ilog2(SUN4I_SPI_MAX_RATE) - __ilog2(speed);
444 reg &= ~((SUN4I_CLK_CTL_CDR1_MASK << 8) | SUN4I_CLK_CTL_DRS);
445 reg |= SUN4I_CLK_CTL_CDR1(div);
446 }
447
448 priv->freq = speed;
Jagan Teki8d9bf462019-02-27 20:02:08 +0530449 writel(reg, SPI_REG(priv, SPI_CCR));
Stefan Mavrodiev7f25d812018-02-06 15:14:33 +0200450
451 return 0;
452}
453
454static int sun4i_spi_set_mode(struct udevice *dev, uint mode)
455{
456 struct sun4i_spi_priv *priv = dev_get_priv(dev);
457 u32 reg;
458
Jagan Teki8d9bf462019-02-27 20:02:08 +0530459 reg = readl(SPI_REG(priv, SPI_TCR));
460 reg &= ~(SPI_BIT(priv, SPI_TCR_CPOL) | SPI_BIT(priv, SPI_TCR_CPHA));
Stefan Mavrodiev7f25d812018-02-06 15:14:33 +0200461
462 if (mode & SPI_CPOL)
Jagan Teki8d9bf462019-02-27 20:02:08 +0530463 reg |= SPI_BIT(priv, SPI_TCR_CPOL);
Stefan Mavrodiev7f25d812018-02-06 15:14:33 +0200464
465 if (mode & SPI_CPHA)
Jagan Teki8d9bf462019-02-27 20:02:08 +0530466 reg |= SPI_BIT(priv, SPI_TCR_CPHA);
Stefan Mavrodiev7f25d812018-02-06 15:14:33 +0200467
468 priv->mode = mode;
Jagan Teki8d9bf462019-02-27 20:02:08 +0530469 writel(reg, SPI_REG(priv, SPI_TCR));
Stefan Mavrodiev7f25d812018-02-06 15:14:33 +0200470
471 return 0;
472}
473
474static const struct dm_spi_ops sun4i_spi_ops = {
475 .claim_bus = sun4i_spi_claim_bus,
476 .release_bus = sun4i_spi_release_bus,
477 .xfer = sun4i_spi_xfer,
478 .set_speed = sun4i_spi_set_speed,
479 .set_mode = sun4i_spi_set_mode,
480};
481
Jagan Teki903e7cf2019-02-27 20:02:12 +0530482static int sun4i_spi_probe(struct udevice *bus)
483{
484 struct sun4i_spi_platdata *plat = dev_get_platdata(bus);
485 struct sun4i_spi_priv *priv = dev_get_priv(bus);
486 int ret;
487
488 ret = clk_get_by_name(bus, "ahb", &priv->clk_ahb);
489 if (ret) {
490 dev_err(dev, "failed to get ahb clock\n");
491 return ret;
492 }
493
494 ret = clk_get_by_name(bus, "mod", &priv->clk_mod);
495 if (ret) {
496 dev_err(dev, "failed to get mod clock\n");
497 return ret;
498 }
499
500 ret = reset_get_by_index(bus, 0, &priv->reset);
501 if (ret && ret != -ENOENT) {
502 dev_err(dev, "failed to get reset\n");
503 return ret;
504 }
505
506 sun4i_spi_parse_pins(bus);
507
508 priv->variant = plat->variant;
509 priv->base = plat->base;
510 priv->freq = plat->max_hz;
511
512 return 0;
513}
514
515static int sun4i_spi_ofdata_to_platdata(struct udevice *bus)
516{
517 struct sun4i_spi_platdata *plat = dev_get_platdata(bus);
518 int node = dev_of_offset(bus);
519
520 plat->base = devfdt_get_addr(bus);
521 plat->variant = (struct sun4i_spi_variant *)dev_get_driver_data(bus);
522 plat->max_hz = fdtdec_get_int(gd->fdt_blob, node,
523 "spi-max-frequency",
524 SUN4I_SPI_DEFAULT_RATE);
525
526 if (plat->max_hz > SUN4I_SPI_MAX_RATE)
527 plat->max_hz = SUN4I_SPI_MAX_RATE;
528
529 return 0;
530}
531
Jagan Teki8d9bf462019-02-27 20:02:08 +0530532static const unsigned long sun4i_spi_regs[] = {
533 [SPI_GCR] = SUN4I_CTL_REG,
534 [SPI_TCR] = SUN4I_CTL_REG,
535 [SPI_FCR] = SUN4I_CTL_REG,
536 [SPI_FSR] = SUN4I_FIFO_STA_REG,
537 [SPI_CCR] = SUN4I_CLK_CTL_REG,
538 [SPI_BC] = SUN4I_BURST_CNT_REG,
539 [SPI_TC] = SUN4I_XMIT_CNT_REG,
540 [SPI_TXD] = SUN4I_TXDATA_REG,
541 [SPI_RXD] = SUN4I_RXDATA_REG,
542};
543
544static const u32 sun4i_spi_bits[] = {
545 [SPI_GCR_TP] = BIT(18),
546 [SPI_TCR_CPHA] = BIT(2),
547 [SPI_TCR_CPOL] = BIT(3),
548 [SPI_TCR_CS_ACTIVE_LOW] = BIT(4),
549 [SPI_TCR_XCH] = BIT(10),
550 [SPI_TCR_CS_SEL] = 12,
551 [SPI_TCR_CS_MASK] = 0x3000,
552 [SPI_TCR_CS_MANUAL] = BIT(16),
553 [SPI_TCR_CS_LEVEL] = BIT(17),
554 [SPI_FCR_TF_RST] = BIT(8),
555 [SPI_FCR_RF_RST] = BIT(9),
556 [SPI_FSR_RF_CNT_MASK] = GENMASK(6, 0),
557};
558
Jagan Teki853f4512019-02-27 20:02:11 +0530559static const unsigned long sun6i_spi_regs[] = {
560 [SPI_GCR] = SUN6I_GBL_CTL_REG,
561 [SPI_TCR] = SUN6I_TFR_CTL_REG,
562 [SPI_FCR] = SUN6I_FIFO_CTL_REG,
563 [SPI_FSR] = SUN6I_FIFO_STA_REG,
564 [SPI_CCR] = SUN6I_CLK_CTL_REG,
565 [SPI_BC] = SUN6I_BURST_CNT_REG,
566 [SPI_TC] = SUN6I_XMIT_CNT_REG,
567 [SPI_BCTL] = SUN6I_BURST_CTL_REG,
568 [SPI_TXD] = SUN6I_TXDATA_REG,
569 [SPI_RXD] = SUN6I_RXDATA_REG,
570};
571
572static const u32 sun6i_spi_bits[] = {
573 [SPI_GCR_TP] = BIT(7),
574 [SPI_GCR_SRST] = BIT(31),
575 [SPI_TCR_CPHA] = BIT(0),
576 [SPI_TCR_CPOL] = BIT(1),
577 [SPI_TCR_CS_ACTIVE_LOW] = BIT(2),
578 [SPI_TCR_CS_SEL] = 4,
579 [SPI_TCR_CS_MASK] = 0x30,
580 [SPI_TCR_CS_MANUAL] = BIT(6),
581 [SPI_TCR_CS_LEVEL] = BIT(7),
582 [SPI_TCR_XCH] = BIT(31),
583 [SPI_FCR_RF_RST] = BIT(15),
584 [SPI_FCR_TF_RST] = BIT(31),
585 [SPI_FSR_RF_CNT_MASK] = GENMASK(7, 0),
586};
587
Jagan Teki8d9bf462019-02-27 20:02:08 +0530588static const struct sun4i_spi_variant sun4i_a10_spi_variant = {
589 .regs = sun4i_spi_regs,
590 .bits = sun4i_spi_bits,
Jagan Teki178fbd22019-02-27 20:02:09 +0530591 .fifo_depth = 64,
Jagan Teki8d9bf462019-02-27 20:02:08 +0530592};
593
Jagan Teki853f4512019-02-27 20:02:11 +0530594static const struct sun4i_spi_variant sun6i_a31_spi_variant = {
595 .regs = sun6i_spi_regs,
596 .bits = sun6i_spi_bits,
597 .fifo_depth = 128,
598 .has_soft_reset = true,
599 .has_burst_ctl = true,
600};
601
602static const struct sun4i_spi_variant sun8i_h3_spi_variant = {
603 .regs = sun6i_spi_regs,
604 .bits = sun6i_spi_bits,
605 .fifo_depth = 64,
606 .has_soft_reset = true,
607 .has_burst_ctl = true,
608};
609
Stefan Mavrodiev7f25d812018-02-06 15:14:33 +0200610static const struct udevice_id sun4i_spi_ids[] = {
Jagan Teki8d9bf462019-02-27 20:02:08 +0530611 {
612 .compatible = "allwinner,sun4i-a10-spi",
613 .data = (ulong)&sun4i_a10_spi_variant,
614 },
Jagan Teki853f4512019-02-27 20:02:11 +0530615 {
616 .compatible = "allwinner,sun6i-a31-spi",
617 .data = (ulong)&sun6i_a31_spi_variant,
618 },
619 {
620 .compatible = "allwinner,sun8i-h3-spi",
621 .data = (ulong)&sun8i_h3_spi_variant,
622 },
Jagan Teki903e7cf2019-02-27 20:02:12 +0530623 { /* sentinel */ }
Stefan Mavrodiev7f25d812018-02-06 15:14:33 +0200624};
625
626U_BOOT_DRIVER(sun4i_spi) = {
627 .name = "sun4i_spi",
628 .id = UCLASS_SPI,
629 .of_match = sun4i_spi_ids,
630 .ops = &sun4i_spi_ops,
631 .ofdata_to_platdata = sun4i_spi_ofdata_to_platdata,
632 .platdata_auto_alloc_size = sizeof(struct sun4i_spi_platdata),
633 .priv_auto_alloc_size = sizeof(struct sun4i_spi_priv),
634 .probe = sun4i_spi_probe,
635};