blob: d62355ec6fba7d40e6dd95a010ade09ceb3579af [file] [log] [blame]
Stefan Mavrodiev5d716042018-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 Teki97b3d5a2019-02-27 20:02:10 +053022#include <clk.h>
Stefan Mavrodiev5d716042018-02-06 15:14:33 +020023#include <dm.h>
Simon Glass0f2af882020-05-10 11:40:05 -060024#include <log.h>
Stefan Mavrodiev5d716042018-02-06 15:14:33 +020025#include <spi.h>
26#include <errno.h>
27#include <fdt_support.h>
Jagan Tekif69b4252019-02-27 20:02:11 +053028#include <reset.h>
Stefan Mavrodiev5d716042018-02-06 15:14:33 +020029#include <wait_bit.h>
Simon Glass3ba929a2020-10-30 21:38:53 -060030#include <asm/global_data.h>
Simon Glass9bc15642020-02-03 07:36:16 -070031#include <dm/device_compat.h>
Simon Glass4dcacfc2020-05-10 11:40:13 -060032#include <linux/bitops.h>
Stefan Mavrodiev5d716042018-02-06 15:14:33 +020033
34#include <asm/bitops.h>
35#include <asm/gpio.h>
36#include <asm/io.h>
37
Jagan Teki66220da2019-02-27 20:02:05 +053038#include <linux/iopoll.h>
39
Jagan Teki3f53a582019-02-27 20:02:12 +053040DECLARE_GLOBAL_DATA_PTR;
Stefan Mavrodiev5d716042018-02-06 15:14:33 +020041
Jagan Teki3f53a582019-02-27 20:02:12 +053042/* sun4i spi registers */
43#define SUN4I_RXDATA_REG 0x00
44#define SUN4I_TXDATA_REG 0x04
45#define SUN4I_CTL_REG 0x08
46#define SUN4I_CLK_CTL_REG 0x1c
47#define SUN4I_BURST_CNT_REG 0x20
48#define SUN4I_XMIT_CNT_REG 0x24
49#define SUN4I_FIFO_STA_REG 0x28
Stefan Mavrodiev5d716042018-02-06 15:14:33 +020050
Jagan Tekif69b4252019-02-27 20:02:11 +053051/* sun6i spi registers */
52#define SUN6I_GBL_CTL_REG 0x04
53#define SUN6I_TFR_CTL_REG 0x08
54#define SUN6I_FIFO_CTL_REG 0x18
55#define SUN6I_FIFO_STA_REG 0x1c
56#define SUN6I_CLK_CTL_REG 0x24
57#define SUN6I_BURST_CNT_REG 0x30
58#define SUN6I_XMIT_CNT_REG 0x34
59#define SUN6I_BURST_CTL_REG 0x38
60#define SUN6I_TXDATA_REG 0x200
61#define SUN6I_RXDATA_REG 0x300
62
Jagan Teki3f53a582019-02-27 20:02:12 +053063/* sun spi bits */
64#define SUN4I_CTL_ENABLE BIT(0)
65#define SUN4I_CTL_MASTER BIT(1)
66#define SUN4I_CLK_CTL_CDR2_MASK 0xff
67#define SUN4I_CLK_CTL_CDR2(div) ((div) & SUN4I_CLK_CTL_CDR2_MASK)
68#define SUN4I_CLK_CTL_CDR1_MASK 0xf
69#define SUN4I_CLK_CTL_CDR1(div) (((div) & SUN4I_CLK_CTL_CDR1_MASK) << 8)
70#define SUN4I_CLK_CTL_DRS BIT(12)
71#define SUN4I_MAX_XFER_SIZE 0xffffff
72#define SUN4I_BURST_CNT(cnt) ((cnt) & SUN4I_MAX_XFER_SIZE)
73#define SUN4I_XMIT_CNT(cnt) ((cnt) & SUN4I_MAX_XFER_SIZE)
74#define SUN4I_FIFO_STA_RF_CNT_BITS 0
75
76#define SUN4I_SPI_MAX_RATE 24000000
77#define SUN4I_SPI_MIN_RATE 3000
78#define SUN4I_SPI_DEFAULT_RATE 1000000
79#define SUN4I_SPI_TIMEOUT_US 1000000
Stefan Mavrodiev5d716042018-02-06 15:14:33 +020080
Jagan Teki3f53a582019-02-27 20:02:12 +053081#define SPI_REG(priv, reg) ((priv)->base + \
Jagan Tekic25058c2019-02-27 20:02:08 +053082 (priv)->variant->regs[reg])
83#define SPI_BIT(priv, bit) ((priv)->variant->bits[bit])
84#define SPI_CS(priv, cs) (((cs) << SPI_BIT(priv, SPI_TCR_CS_SEL)) & \
85 SPI_BIT(priv, SPI_TCR_CS_MASK))
86
87/* sun spi register set */
88enum sun4i_spi_regs {
89 SPI_GCR,
90 SPI_TCR,
91 SPI_FCR,
92 SPI_FSR,
93 SPI_CCR,
94 SPI_BC,
95 SPI_TC,
96 SPI_BCTL,
97 SPI_TXD,
98 SPI_RXD,
99};
100
101/* sun spi register bits */
102enum sun4i_spi_bits {
103 SPI_GCR_TP,
Jagan Tekif69b4252019-02-27 20:02:11 +0530104 SPI_GCR_SRST,
Jagan Tekic25058c2019-02-27 20:02:08 +0530105 SPI_TCR_CPHA,
106 SPI_TCR_CPOL,
107 SPI_TCR_CS_ACTIVE_LOW,
108 SPI_TCR_CS_SEL,
109 SPI_TCR_CS_MASK,
110 SPI_TCR_XCH,
111 SPI_TCR_CS_MANUAL,
112 SPI_TCR_CS_LEVEL,
113 SPI_FCR_TF_RST,
114 SPI_FCR_RF_RST,
115 SPI_FSR_RF_CNT_MASK,
Stefan Mavrodiev5d716042018-02-06 15:14:33 +0200116};
117
Jagan Tekic25058c2019-02-27 20:02:08 +0530118struct sun4i_spi_variant {
119 const unsigned long *regs;
120 const u32 *bits;
Jagan Tekic12eb6a2019-02-27 20:02:09 +0530121 u32 fifo_depth;
Jagan Tekif69b4252019-02-27 20:02:11 +0530122 bool has_soft_reset;
123 bool has_burst_ctl;
Jagan Tekic25058c2019-02-27 20:02:08 +0530124};
125
Simon Glassb75b15b2020-12-03 16:55:23 -0700126struct sun4i_spi_plat {
Jagan Tekic25058c2019-02-27 20:02:08 +0530127 struct sun4i_spi_variant *variant;
Jagan Teki3f53a582019-02-27 20:02:12 +0530128 u32 base;
Stefan Mavrodiev5d716042018-02-06 15:14:33 +0200129 u32 max_hz;
130};
131
132struct sun4i_spi_priv {
Jagan Tekic25058c2019-02-27 20:02:08 +0530133 struct sun4i_spi_variant *variant;
Jagan Teki97b3d5a2019-02-27 20:02:10 +0530134 struct clk clk_ahb, clk_mod;
Jagan Tekif69b4252019-02-27 20:02:11 +0530135 struct reset_ctl reset;
Jagan Teki3f53a582019-02-27 20:02:12 +0530136 u32 base;
Stefan Mavrodiev5d716042018-02-06 15:14:33 +0200137 u32 freq;
138 u32 mode;
139
140 const u8 *tx_buf;
141 u8 *rx_buf;
142};
143
Stefan Mavrodiev5d716042018-02-06 15:14:33 +0200144static inline void sun4i_spi_drain_fifo(struct sun4i_spi_priv *priv, int len)
145{
146 u8 byte;
147
148 while (len--) {
Jagan Tekic25058c2019-02-27 20:02:08 +0530149 byte = readb(SPI_REG(priv, SPI_RXD));
Stefan Mavrodiev165db622018-12-05 14:27:57 +0200150 if (priv->rx_buf)
151 *priv->rx_buf++ = byte;
Stefan Mavrodiev5d716042018-02-06 15:14:33 +0200152 }
153}
154
155static inline void sun4i_spi_fill_fifo(struct sun4i_spi_priv *priv, int len)
156{
157 u8 byte;
158
159 while (len--) {
160 byte = priv->tx_buf ? *priv->tx_buf++ : 0;
Jagan Tekic25058c2019-02-27 20:02:08 +0530161 writeb(byte, SPI_REG(priv, SPI_TXD));
Stefan Mavrodiev5d716042018-02-06 15:14:33 +0200162 }
163}
164
165static void sun4i_spi_set_cs(struct udevice *bus, u8 cs, bool enable)
166{
167 struct sun4i_spi_priv *priv = dev_get_priv(bus);
168 u32 reg;
169
Jagan Tekic25058c2019-02-27 20:02:08 +0530170 reg = readl(SPI_REG(priv, SPI_TCR));
Stefan Mavrodiev5d716042018-02-06 15:14:33 +0200171
Jagan Tekic25058c2019-02-27 20:02:08 +0530172 reg &= ~SPI_BIT(priv, SPI_TCR_CS_MASK);
173 reg |= SPI_CS(priv, cs);
Stefan Mavrodiev5d716042018-02-06 15:14:33 +0200174
175 if (enable)
Jagan Tekic25058c2019-02-27 20:02:08 +0530176 reg &= ~SPI_BIT(priv, SPI_TCR_CS_LEVEL);
Stefan Mavrodiev5d716042018-02-06 15:14:33 +0200177 else
Jagan Tekic25058c2019-02-27 20:02:08 +0530178 reg |= SPI_BIT(priv, SPI_TCR_CS_LEVEL);
Stefan Mavrodiev5d716042018-02-06 15:14:33 +0200179
Jagan Tekic25058c2019-02-27 20:02:08 +0530180 writel(reg, SPI_REG(priv, SPI_TCR));
Stefan Mavrodiev5d716042018-02-06 15:14:33 +0200181}
182
183static int sun4i_spi_parse_pins(struct udevice *dev)
184{
185 const void *fdt = gd->fdt_blob;
186 const char *pin_name;
187 const fdt32_t *list;
188 u32 phandle;
189 int drive, pull = 0, pin, i;
190 int offset;
191 int size;
192
193 list = fdt_getprop(fdt, dev_of_offset(dev), "pinctrl-0", &size);
194 if (!list) {
195 printf("WARNING: sun4i_spi: cannot find pinctrl-0 node\n");
196 return -EINVAL;
197 }
198
199 while (size) {
200 phandle = fdt32_to_cpu(*list++);
201 size -= sizeof(*list);
202
203 offset = fdt_node_offset_by_phandle(fdt, phandle);
204 if (offset < 0)
205 return offset;
206
207 drive = fdt_getprop_u32_default_node(fdt, offset, 0,
208 "drive-strength", 0);
209 if (drive) {
210 if (drive <= 10)
211 drive = 0;
212 else if (drive <= 20)
213 drive = 1;
214 else if (drive <= 30)
215 drive = 2;
216 else
217 drive = 3;
218 } else {
219 drive = fdt_getprop_u32_default_node(fdt, offset, 0,
220 "allwinner,drive",
221 0);
222 drive = min(drive, 3);
223 }
224
225 if (fdt_get_property(fdt, offset, "bias-disable", NULL))
226 pull = 0;
227 else if (fdt_get_property(fdt, offset, "bias-pull-up", NULL))
228 pull = 1;
229 else if (fdt_get_property(fdt, offset, "bias-pull-down", NULL))
230 pull = 2;
231 else
232 pull = fdt_getprop_u32_default_node(fdt, offset, 0,
233 "allwinner,pull",
234 0);
235 pull = min(pull, 2);
236
237 for (i = 0; ; i++) {
238 pin_name = fdt_stringlist_get(fdt, offset,
239 "pins", i, NULL);
240 if (!pin_name) {
241 pin_name = fdt_stringlist_get(fdt, offset,
242 "allwinner,pins",
243 i, NULL);
244 if (!pin_name)
245 break;
246 }
247
Samuel Hollandc7ab95d2021-09-11 16:50:48 -0500248 pin = sunxi_name_to_gpio(pin_name);
Stefan Mavrodiev5d716042018-02-06 15:14:33 +0200249 if (pin < 0)
250 break;
251
Daniel Wagenknecht47690e12021-12-16 20:42:10 +0100252 if (IS_ENABLED(CONFIG_MACH_SUN50I) ||
253 IS_ENABLED(CONFIG_SUN50I_GEN_H6))
Jagan Tekif69b4252019-02-27 20:02:11 +0530254 sunxi_gpio_set_cfgpin(pin, SUN50I_GPC_SPI0);
255 else
256 sunxi_gpio_set_cfgpin(pin, SUNXI_GPC_SPI0);
Stefan Mavrodiev5d716042018-02-06 15:14:33 +0200257 sunxi_gpio_set_drv(pin, drive);
258 sunxi_gpio_set_pull(pin, pull);
259 }
260 }
261 return 0;
262}
263
Jagan Teki97b3d5a2019-02-27 20:02:10 +0530264static inline int sun4i_spi_set_clock(struct udevice *dev, bool enable)
Stefan Mavrodiev5d716042018-02-06 15:14:33 +0200265{
Jagan Teki97b3d5a2019-02-27 20:02:10 +0530266 struct sun4i_spi_priv *priv = dev_get_priv(dev);
267 int ret;
268
269 if (!enable) {
270 clk_disable(&priv->clk_ahb);
271 clk_disable(&priv->clk_mod);
Jagan Tekif69b4252019-02-27 20:02:11 +0530272 if (reset_valid(&priv->reset))
273 reset_assert(&priv->reset);
Jagan Teki97b3d5a2019-02-27 20:02:10 +0530274 return 0;
275 }
276
277 ret = clk_enable(&priv->clk_ahb);
278 if (ret) {
279 dev_err(dev, "failed to enable ahb clock (ret=%d)\n", ret);
280 return ret;
281 }
282
283 ret = clk_enable(&priv->clk_mod);
284 if (ret) {
285 dev_err(dev, "failed to enable mod clock (ret=%d)\n", ret);
286 goto err_ahb;
287 }
Stefan Mavrodiev5d716042018-02-06 15:14:33 +0200288
Jagan Tekif69b4252019-02-27 20:02:11 +0530289 if (reset_valid(&priv->reset)) {
290 ret = reset_deassert(&priv->reset);
291 if (ret) {
292 dev_err(dev, "failed to deassert reset\n");
293 goto err_mod;
294 }
295 }
296
Jagan Teki97b3d5a2019-02-27 20:02:10 +0530297 return 0;
298
Jagan Tekif69b4252019-02-27 20:02:11 +0530299err_mod:
300 clk_disable(&priv->clk_mod);
Jagan Teki97b3d5a2019-02-27 20:02:10 +0530301err_ahb:
302 clk_disable(&priv->clk_ahb);
303 return ret;
Stefan Mavrodiev5d716042018-02-06 15:14:33 +0200304}
305
Stefan Mavrodiev5d716042018-02-06 15:14:33 +0200306static int sun4i_spi_claim_bus(struct udevice *dev)
307{
308 struct sun4i_spi_priv *priv = dev_get_priv(dev->parent);
Jagan Teki97b3d5a2019-02-27 20:02:10 +0530309 int ret;
310
311 ret = sun4i_spi_set_clock(dev->parent, true);
312 if (ret)
313 return ret;
Stefan Mavrodiev5d716042018-02-06 15:14:33 +0200314
Jagan Tekic25058c2019-02-27 20:02:08 +0530315 setbits_le32(SPI_REG(priv, SPI_GCR), SUN4I_CTL_ENABLE |
316 SUN4I_CTL_MASTER | SPI_BIT(priv, SPI_GCR_TP));
317
Jagan Tekif69b4252019-02-27 20:02:11 +0530318 if (priv->variant->has_soft_reset)
319 setbits_le32(SPI_REG(priv, SPI_GCR),
320 SPI_BIT(priv, SPI_GCR_SRST));
321
Jagan Tekic25058c2019-02-27 20:02:08 +0530322 setbits_le32(SPI_REG(priv, SPI_TCR), SPI_BIT(priv, SPI_TCR_CS_MANUAL) |
323 SPI_BIT(priv, SPI_TCR_CS_ACTIVE_LOW));
Jagan Tekif9b70122019-02-27 20:02:07 +0530324
Stefan Mavrodiev5d716042018-02-06 15:14:33 +0200325 return 0;
326}
327
328static int sun4i_spi_release_bus(struct udevice *dev)
329{
330 struct sun4i_spi_priv *priv = dev_get_priv(dev->parent);
Stefan Mavrodiev5d716042018-02-06 15:14:33 +0200331
Jagan Tekic25058c2019-02-27 20:02:08 +0530332 clrbits_le32(SPI_REG(priv, SPI_GCR), SUN4I_CTL_ENABLE);
Stefan Mavrodiev5d716042018-02-06 15:14:33 +0200333
Jagan Teki97b3d5a2019-02-27 20:02:10 +0530334 sun4i_spi_set_clock(dev->parent, false);
335
Stefan Mavrodiev5d716042018-02-06 15:14:33 +0200336 return 0;
337}
338
339static int sun4i_spi_xfer(struct udevice *dev, unsigned int bitlen,
340 const void *dout, void *din, unsigned long flags)
341{
342 struct udevice *bus = dev->parent;
343 struct sun4i_spi_priv *priv = dev_get_priv(bus);
Simon Glassb75b15b2020-12-03 16:55:23 -0700344 struct dm_spi_slave_plat *slave_plat = dev_get_parent_plat(dev);
Stefan Mavrodiev5d716042018-02-06 15:14:33 +0200345
346 u32 len = bitlen / 8;
Jagan Tekif9b70122019-02-27 20:02:07 +0530347 u32 rx_fifocnt;
Stefan Mavrodiev5d716042018-02-06 15:14:33 +0200348 u8 nbytes;
349 int ret;
350
351 priv->tx_buf = dout;
352 priv->rx_buf = din;
353
354 if (bitlen % 8) {
355 debug("%s: non byte-aligned SPI transfer.\n", __func__);
356 return -ENAVAIL;
357 }
358
359 if (flags & SPI_XFER_BEGIN)
360 sun4i_spi_set_cs(bus, slave_plat->cs, true);
361
Stefan Mavrodiev5d716042018-02-06 15:14:33 +0200362 /* Reset FIFOs */
Jagan Tekic25058c2019-02-27 20:02:08 +0530363 setbits_le32(SPI_REG(priv, SPI_FCR), SPI_BIT(priv, SPI_FCR_RF_RST) |
364 SPI_BIT(priv, SPI_FCR_TF_RST));
Stefan Mavrodiev5d716042018-02-06 15:14:33 +0200365
366 while (len) {
367 /* Setup the transfer now... */
Jagan Tekic12eb6a2019-02-27 20:02:09 +0530368 nbytes = min(len, (priv->variant->fifo_depth - 1));
Stefan Mavrodiev5d716042018-02-06 15:14:33 +0200369
370 /* Setup the counters */
Jagan Tekic25058c2019-02-27 20:02:08 +0530371 writel(SUN4I_BURST_CNT(nbytes), SPI_REG(priv, SPI_BC));
372 writel(SUN4I_XMIT_CNT(nbytes), SPI_REG(priv, SPI_TC));
Stefan Mavrodiev5d716042018-02-06 15:14:33 +0200373
Jagan Tekif69b4252019-02-27 20:02:11 +0530374 if (priv->variant->has_burst_ctl)
375 writel(SUN4I_BURST_CNT(nbytes),
376 SPI_REG(priv, SPI_BCTL));
377
Stefan Mavrodiev5d716042018-02-06 15:14:33 +0200378 /* Fill the TX FIFO */
379 sun4i_spi_fill_fifo(priv, nbytes);
380
381 /* Start the transfer */
Jagan Tekic25058c2019-02-27 20:02:08 +0530382 setbits_le32(SPI_REG(priv, SPI_TCR),
383 SPI_BIT(priv, SPI_TCR_XCH));
Stefan Mavrodiev5d716042018-02-06 15:14:33 +0200384
Jagan Teki66220da2019-02-27 20:02:05 +0530385 /* Wait till RX FIFO to be empty */
Jagan Tekic25058c2019-02-27 20:02:08 +0530386 ret = readl_poll_timeout(SPI_REG(priv, SPI_FSR),
387 rx_fifocnt,
388 (((rx_fifocnt &
389 SPI_BIT(priv, SPI_FSR_RF_CNT_MASK)) >>
Jagan Teki66220da2019-02-27 20:02:05 +0530390 SUN4I_FIFO_STA_RF_CNT_BITS) >= nbytes),
391 SUN4I_SPI_TIMEOUT_US);
392 if (ret < 0) {
Stefan Mavrodiev5d716042018-02-06 15:14:33 +0200393 printf("ERROR: sun4i_spi: Timeout transferring data\n");
394 sun4i_spi_set_cs(bus, slave_plat->cs, false);
395 return ret;
396 }
397
398 /* Drain the RX FIFO */
399 sun4i_spi_drain_fifo(priv, nbytes);
400
401 len -= nbytes;
402 }
403
404 if (flags & SPI_XFER_END)
405 sun4i_spi_set_cs(bus, slave_plat->cs, false);
406
407 return 0;
408}
409
410static int sun4i_spi_set_speed(struct udevice *dev, uint speed)
411{
Simon Glassb75b15b2020-12-03 16:55:23 -0700412 struct sun4i_spi_plat *plat = dev_get_plat(dev);
Stefan Mavrodiev5d716042018-02-06 15:14:33 +0200413 struct sun4i_spi_priv *priv = dev_get_priv(dev);
414 unsigned int div;
415 u32 reg;
416
417 if (speed > plat->max_hz)
418 speed = plat->max_hz;
419
420 if (speed < SUN4I_SPI_MIN_RATE)
421 speed = SUN4I_SPI_MIN_RATE;
422 /*
423 * Setup clock divider.
424 *
425 * We have two choices there. Either we can use the clock
426 * divide rate 1, which is calculated thanks to this formula:
427 * SPI_CLK = MOD_CLK / (2 ^ (cdr + 1))
428 * Or we can use CDR2, which is calculated with the formula:
429 * SPI_CLK = MOD_CLK / (2 * (cdr + 1))
430 * Whether we use the former or the latter is set through the
431 * DRS bit.
432 *
433 * First try CDR2, and if we can't reach the expected
434 * frequency, fall back to CDR1.
435 */
436
437 div = SUN4I_SPI_MAX_RATE / (2 * speed);
Jagan Tekic25058c2019-02-27 20:02:08 +0530438 reg = readl(SPI_REG(priv, SPI_CCR));
Stefan Mavrodiev5d716042018-02-06 15:14:33 +0200439
440 if (div <= (SUN4I_CLK_CTL_CDR2_MASK + 1)) {
441 if (div > 0)
442 div--;
443
444 reg &= ~(SUN4I_CLK_CTL_CDR2_MASK | SUN4I_CLK_CTL_DRS);
445 reg |= SUN4I_CLK_CTL_CDR2(div) | SUN4I_CLK_CTL_DRS;
446 } else {
447 div = __ilog2(SUN4I_SPI_MAX_RATE) - __ilog2(speed);
448 reg &= ~((SUN4I_CLK_CTL_CDR1_MASK << 8) | SUN4I_CLK_CTL_DRS);
449 reg |= SUN4I_CLK_CTL_CDR1(div);
450 }
451
452 priv->freq = speed;
Jagan Tekic25058c2019-02-27 20:02:08 +0530453 writel(reg, SPI_REG(priv, SPI_CCR));
Stefan Mavrodiev5d716042018-02-06 15:14:33 +0200454
455 return 0;
456}
457
458static int sun4i_spi_set_mode(struct udevice *dev, uint mode)
459{
460 struct sun4i_spi_priv *priv = dev_get_priv(dev);
461 u32 reg;
462
Jagan Tekic25058c2019-02-27 20:02:08 +0530463 reg = readl(SPI_REG(priv, SPI_TCR));
464 reg &= ~(SPI_BIT(priv, SPI_TCR_CPOL) | SPI_BIT(priv, SPI_TCR_CPHA));
Stefan Mavrodiev5d716042018-02-06 15:14:33 +0200465
466 if (mode & SPI_CPOL)
Jagan Tekic25058c2019-02-27 20:02:08 +0530467 reg |= SPI_BIT(priv, SPI_TCR_CPOL);
Stefan Mavrodiev5d716042018-02-06 15:14:33 +0200468
469 if (mode & SPI_CPHA)
Jagan Tekic25058c2019-02-27 20:02:08 +0530470 reg |= SPI_BIT(priv, SPI_TCR_CPHA);
Stefan Mavrodiev5d716042018-02-06 15:14:33 +0200471
472 priv->mode = mode;
Jagan Tekic25058c2019-02-27 20:02:08 +0530473 writel(reg, SPI_REG(priv, SPI_TCR));
Stefan Mavrodiev5d716042018-02-06 15:14:33 +0200474
475 return 0;
476}
477
478static const struct dm_spi_ops sun4i_spi_ops = {
479 .claim_bus = sun4i_spi_claim_bus,
480 .release_bus = sun4i_spi_release_bus,
481 .xfer = sun4i_spi_xfer,
482 .set_speed = sun4i_spi_set_speed,
483 .set_mode = sun4i_spi_set_mode,
484};
485
Jagan Teki3f53a582019-02-27 20:02:12 +0530486static int sun4i_spi_probe(struct udevice *bus)
487{
Simon Glassb75b15b2020-12-03 16:55:23 -0700488 struct sun4i_spi_plat *plat = dev_get_plat(bus);
Jagan Teki3f53a582019-02-27 20:02:12 +0530489 struct sun4i_spi_priv *priv = dev_get_priv(bus);
490 int ret;
491
492 ret = clk_get_by_name(bus, "ahb", &priv->clk_ahb);
493 if (ret) {
Sean Anderson64474dd2020-09-15 10:45:11 -0400494 dev_err(bus, "failed to get ahb clock\n");
Jagan Teki3f53a582019-02-27 20:02:12 +0530495 return ret;
496 }
497
498 ret = clk_get_by_name(bus, "mod", &priv->clk_mod);
499 if (ret) {
Sean Anderson64474dd2020-09-15 10:45:11 -0400500 dev_err(bus, "failed to get mod clock\n");
Jagan Teki3f53a582019-02-27 20:02:12 +0530501 return ret;
502 }
503
504 ret = reset_get_by_index(bus, 0, &priv->reset);
505 if (ret && ret != -ENOENT) {
Sean Anderson64474dd2020-09-15 10:45:11 -0400506 dev_err(bus, "failed to get reset\n");
Jagan Teki3f53a582019-02-27 20:02:12 +0530507 return ret;
508 }
509
510 sun4i_spi_parse_pins(bus);
511
512 priv->variant = plat->variant;
513 priv->base = plat->base;
514 priv->freq = plat->max_hz;
515
516 return 0;
517}
518
Simon Glassaad29ae2020-12-03 16:55:21 -0700519static int sun4i_spi_of_to_plat(struct udevice *bus)
Jagan Teki3f53a582019-02-27 20:02:12 +0530520{
Simon Glassb75b15b2020-12-03 16:55:23 -0700521 struct sun4i_spi_plat *plat = dev_get_plat(bus);
Jagan Teki3f53a582019-02-27 20:02:12 +0530522 int node = dev_of_offset(bus);
523
Masahiro Yamadaa89b4de2020-07-17 14:36:48 +0900524 plat->base = dev_read_addr(bus);
Jagan Teki3f53a582019-02-27 20:02:12 +0530525 plat->variant = (struct sun4i_spi_variant *)dev_get_driver_data(bus);
526 plat->max_hz = fdtdec_get_int(gd->fdt_blob, node,
527 "spi-max-frequency",
528 SUN4I_SPI_DEFAULT_RATE);
529
530 if (plat->max_hz > SUN4I_SPI_MAX_RATE)
531 plat->max_hz = SUN4I_SPI_MAX_RATE;
532
533 return 0;
534}
535
Jagan Tekic25058c2019-02-27 20:02:08 +0530536static const unsigned long sun4i_spi_regs[] = {
537 [SPI_GCR] = SUN4I_CTL_REG,
538 [SPI_TCR] = SUN4I_CTL_REG,
539 [SPI_FCR] = SUN4I_CTL_REG,
540 [SPI_FSR] = SUN4I_FIFO_STA_REG,
541 [SPI_CCR] = SUN4I_CLK_CTL_REG,
542 [SPI_BC] = SUN4I_BURST_CNT_REG,
543 [SPI_TC] = SUN4I_XMIT_CNT_REG,
544 [SPI_TXD] = SUN4I_TXDATA_REG,
545 [SPI_RXD] = SUN4I_RXDATA_REG,
546};
547
548static const u32 sun4i_spi_bits[] = {
549 [SPI_GCR_TP] = BIT(18),
550 [SPI_TCR_CPHA] = BIT(2),
551 [SPI_TCR_CPOL] = BIT(3),
552 [SPI_TCR_CS_ACTIVE_LOW] = BIT(4),
553 [SPI_TCR_XCH] = BIT(10),
554 [SPI_TCR_CS_SEL] = 12,
555 [SPI_TCR_CS_MASK] = 0x3000,
556 [SPI_TCR_CS_MANUAL] = BIT(16),
557 [SPI_TCR_CS_LEVEL] = BIT(17),
558 [SPI_FCR_TF_RST] = BIT(8),
559 [SPI_FCR_RF_RST] = BIT(9),
560 [SPI_FSR_RF_CNT_MASK] = GENMASK(6, 0),
561};
562
Jagan Tekif69b4252019-02-27 20:02:11 +0530563static const unsigned long sun6i_spi_regs[] = {
564 [SPI_GCR] = SUN6I_GBL_CTL_REG,
565 [SPI_TCR] = SUN6I_TFR_CTL_REG,
566 [SPI_FCR] = SUN6I_FIFO_CTL_REG,
567 [SPI_FSR] = SUN6I_FIFO_STA_REG,
568 [SPI_CCR] = SUN6I_CLK_CTL_REG,
569 [SPI_BC] = SUN6I_BURST_CNT_REG,
570 [SPI_TC] = SUN6I_XMIT_CNT_REG,
571 [SPI_BCTL] = SUN6I_BURST_CTL_REG,
572 [SPI_TXD] = SUN6I_TXDATA_REG,
573 [SPI_RXD] = SUN6I_RXDATA_REG,
574};
575
576static const u32 sun6i_spi_bits[] = {
577 [SPI_GCR_TP] = BIT(7),
578 [SPI_GCR_SRST] = BIT(31),
579 [SPI_TCR_CPHA] = BIT(0),
580 [SPI_TCR_CPOL] = BIT(1),
581 [SPI_TCR_CS_ACTIVE_LOW] = BIT(2),
582 [SPI_TCR_CS_SEL] = 4,
583 [SPI_TCR_CS_MASK] = 0x30,
584 [SPI_TCR_CS_MANUAL] = BIT(6),
585 [SPI_TCR_CS_LEVEL] = BIT(7),
586 [SPI_TCR_XCH] = BIT(31),
587 [SPI_FCR_RF_RST] = BIT(15),
588 [SPI_FCR_TF_RST] = BIT(31),
589 [SPI_FSR_RF_CNT_MASK] = GENMASK(7, 0),
590};
591
Jagan Tekic25058c2019-02-27 20:02:08 +0530592static const struct sun4i_spi_variant sun4i_a10_spi_variant = {
593 .regs = sun4i_spi_regs,
594 .bits = sun4i_spi_bits,
Jagan Tekic12eb6a2019-02-27 20:02:09 +0530595 .fifo_depth = 64,
Jagan Tekif69b4252019-02-27 20:02:11 +0530596};
597
598static const struct sun4i_spi_variant sun6i_a31_spi_variant = {
599 .regs = sun6i_spi_regs,
600 .bits = sun6i_spi_bits,
601 .fifo_depth = 128,
602 .has_soft_reset = true,
603 .has_burst_ctl = true,
604};
605
606static const struct sun4i_spi_variant sun8i_h3_spi_variant = {
607 .regs = sun6i_spi_regs,
608 .bits = sun6i_spi_bits,
609 .fifo_depth = 64,
610 .has_soft_reset = true,
611 .has_burst_ctl = true,
Jagan Tekic25058c2019-02-27 20:02:08 +0530612};
613
Stefan Mavrodiev5d716042018-02-06 15:14:33 +0200614static const struct udevice_id sun4i_spi_ids[] = {
Jagan Tekic25058c2019-02-27 20:02:08 +0530615 {
616 .compatible = "allwinner,sun4i-a10-spi",
617 .data = (ulong)&sun4i_a10_spi_variant,
618 },
Jagan Tekif69b4252019-02-27 20:02:11 +0530619 {
620 .compatible = "allwinner,sun6i-a31-spi",
621 .data = (ulong)&sun6i_a31_spi_variant,
622 },
623 {
624 .compatible = "allwinner,sun8i-h3-spi",
625 .data = (ulong)&sun8i_h3_spi_variant,
626 },
Jagan Teki3f53a582019-02-27 20:02:12 +0530627 { /* sentinel */ }
Stefan Mavrodiev5d716042018-02-06 15:14:33 +0200628};
629
630U_BOOT_DRIVER(sun4i_spi) = {
631 .name = "sun4i_spi",
632 .id = UCLASS_SPI,
633 .of_match = sun4i_spi_ids,
634 .ops = &sun4i_spi_ops,
Simon Glassaad29ae2020-12-03 16:55:21 -0700635 .of_to_plat = sun4i_spi_of_to_plat,
Simon Glassb75b15b2020-12-03 16:55:23 -0700636 .plat_auto = sizeof(struct sun4i_spi_plat),
Simon Glass8a2b47f2020-12-03 16:55:17 -0700637 .priv_auto = sizeof(struct sun4i_spi_priv),
Stefan Mavrodiev5d716042018-02-06 15:14:33 +0200638 .probe = sun4i_spi_probe,
639};