blob: eb522fd7b3d99d65a45cad5de6fdce7e70d0de15 [file] [log] [blame]
Jassi Brar717bab22021-06-04 18:44:27 +09001// SPDX-License-Identifier: GPL-2.0+
2/*
3 * spi-synquacer.c - Socionext Synquacer SPI driver
4 * Copyright 2021 Linaro Ltd.
5 * Copyright 2021 Socionext, Inc.
6 */
7
8#include <clk.h>
Jassi Brar717bab22021-06-04 18:44:27 +09009#include <dm.h>
10#include <log.h>
11#include <time.h>
12#include <dm/device_compat.h>
13#include <linux/bitfield.h>
14#include <linux/bitops.h>
15#include <linux/delay.h>
16#include <linux/io.h>
17#include <spi.h>
18#include <wait_bit.h>
19
20#define MCTRL 0x0
21#define MEN 0
22#define CSEN 1
23#define IPCLK 3
24#define MES 4
25#define SYNCON 5
26
27#define PCC0 0x4
28#define PCC(n) (PCC0 + (n) * 4)
29#define RTM 3
30#define ACES 2
31#define SAFESYNC 16
32#define CPHA 0
33#define CPOL 1
34#define SSPOL 4
35#define SDIR 7
36#define SS2CD 5
37#define SENDIAN 8
38#define CDRS_SHIFT 9
39#define CDRS_MASK 0x7f
40
41#define TXF 0x14
42#define TXE 0x18
43#define TXC 0x1c
44#define RXF 0x20
45#define RXE 0x24
46#define RXC 0x28
Masahisa Kojimaa5ab5e12022-05-17 17:41:39 +090047#define TFES 1
Jassi Brar717bab22021-06-04 18:44:27 +090048#define TFLETE 4
Masahisa Kojima05c284e2022-05-17 17:41:37 +090049#define TSSRS 6
Jassi Brar717bab22021-06-04 18:44:27 +090050#define RFMTE 5
Masahisa Kojima05c284e2022-05-17 17:41:37 +090051#define RSSRS 6
Jassi Brar717bab22021-06-04 18:44:27 +090052
53#define FAULTF 0x2c
54#define FAULTC 0x30
55
56#define DMCFG 0x34
57#define SSDC 1
58#define MSTARTEN 2
59
60#define DMSTART 0x38
61#define TRIGGER 0
62#define DMSTOP 8
63#define CS_MASK 3
64#define CS_SHIFT 16
65#define DATA_TXRX 0
66#define DATA_RX 1
67#define DATA_TX 2
68#define DATA_MASK 3
69#define DATA_SHIFT 26
70#define BUS_WIDTH 24
71
72#define DMBCC 0x3c
73#define DMSTATUS 0x40
74#define RX_DATA_MASK 0x1f
75#define RX_DATA_SHIFT 8
76#define TX_DATA_MASK 0x1f
77#define TX_DATA_SHIFT 16
78
79#define TXBITCNT 0x44
80
81#define FIFOCFG 0x4c
82#define BPW_MASK 0x3
83#define BPW_SHIFT 8
84#define RX_FLUSH 11
85#define TX_FLUSH 12
86#define RX_TRSHLD_MASK 0xf
87#define RX_TRSHLD_SHIFT 0
88#define TX_TRSHLD_MASK 0xf
89#define TX_TRSHLD_SHIFT 4
90
91#define TXFIFO 0x50
92#define RXFIFO 0x90
93#define MID 0xfc
94
95#define FIFO_DEPTH 16
96#define TX_TRSHLD 4
97#define RX_TRSHLD (FIFO_DEPTH - TX_TRSHLD)
98
99#define TXBIT 1
100#define RXBIT 2
101
102DECLARE_GLOBAL_DATA_PTR;
103
104struct synquacer_spi_plat {
105 void __iomem *base;
106 bool aces, rtm;
107};
108
109struct synquacer_spi_priv {
110 void __iomem *base;
111 bool aces, rtm;
112 int speed, cs, mode, rwflag;
113 void *rx_buf;
114 const void *tx_buf;
115 unsigned int tx_words, rx_words;
116};
117
118static void read_fifo(struct synquacer_spi_priv *priv)
119{
120 u32 len = readl(priv->base + DMSTATUS);
121 u8 *buf = priv->rx_buf;
122 int i;
123
124 len = (len >> RX_DATA_SHIFT) & RX_DATA_MASK;
125 len = min_t(unsigned int, len, priv->rx_words);
126
127 for (i = 0; i < len; i++)
128 *buf++ = readb(priv->base + RXFIFO);
129
130 priv->rx_buf = buf;
131 priv->rx_words -= len;
132}
133
134static void write_fifo(struct synquacer_spi_priv *priv)
135{
136 u32 len = readl(priv->base + DMSTATUS);
137 const u8 *buf = priv->tx_buf;
138 int i;
139
140 len = (len >> TX_DATA_SHIFT) & TX_DATA_MASK;
141 len = min_t(unsigned int, FIFO_DEPTH - len, priv->tx_words);
142
143 for (i = 0; i < len; i++)
144 writeb(*buf++, priv->base + TXFIFO);
145
146 priv->tx_buf = buf;
147 priv->tx_words -= len;
148}
149
150static void synquacer_cs_set(struct synquacer_spi_priv *priv, bool active)
151{
152 u32 val;
153
154 val = readl(priv->base + DMSTART);
155 val &= ~(CS_MASK << CS_SHIFT);
156 val |= priv->cs << CS_SHIFT;
157
158 if (active) {
159 writel(val, priv->base + DMSTART);
160
161 val = readl(priv->base + DMSTART);
162 val &= ~BIT(DMSTOP);
163 writel(val, priv->base + DMSTART);
164 } else {
165 val |= BIT(DMSTOP);
166 writel(val, priv->base + DMSTART);
167
168 if (priv->rx_buf) {
169 u32 buf[16];
170
171 priv->rx_buf = buf;
172 priv->rx_words = 16;
173 read_fifo(priv);
174 }
Masahisa Kojima05c284e2022-05-17 17:41:37 +0900175
176 /* wait until slave is deselected */
177 while (!(readl(priv->base + TXF) & BIT(TSSRS)) ||
178 !(readl(priv->base + RXF) & BIT(RSSRS)))
179 ;
Jassi Brar717bab22021-06-04 18:44:27 +0900180 }
181}
182
183static void synquacer_spi_config(struct udevice *dev, void *rx, const void *tx)
184{
185 struct udevice *bus = dev->parent;
186 struct synquacer_spi_priv *priv = dev_get_priv(bus);
187 struct dm_spi_slave_plat *slave_plat = dev_get_parent_plat(dev);
Masahisa Kojimaeb8d2622023-05-24 16:32:46 +0900188 u32 val, div, bus_width;
Jassi Brar717bab22021-06-04 18:44:27 +0900189 int rwflag;
190
191 rwflag = (rx ? 1 : 0) | (tx ? 2 : 0);
192
193 /* if nothing to do */
194 if (slave_plat->mode == priv->mode &&
195 rwflag == priv->rwflag &&
196 slave_plat->cs == priv->cs &&
197 slave_plat->max_hz == priv->speed)
198 return;
199
200 priv->rwflag = rwflag;
201 priv->cs = slave_plat->cs;
202 priv->mode = slave_plat->mode;
203 priv->speed = slave_plat->max_hz;
204
Masahisa Kojimaeb8d2622023-05-24 16:32:46 +0900205 if (priv->mode & SPI_TX_DUAL)
Jassi Brar717bab22021-06-04 18:44:27 +0900206 bus_width = 2;
207 else if (priv->mode & SPI_TX_QUAD)
208 bus_width = 4;
209 else if (priv->mode & SPI_TX_OCTAL)
210 bus_width = 8;
Ilias Apalodimasae748f82023-04-07 12:13:00 +0300211 else
Masahisa Kojimaeb8d2622023-05-24 16:32:46 +0900212 bus_width = 1; /* default is single bit mode */
Jassi Brar717bab22021-06-04 18:44:27 +0900213
214 div = DIV_ROUND_UP(125000000, priv->speed);
215
216 val = readl(priv->base + PCC(priv->cs));
217 val &= ~BIT(RTM);
218 val &= ~BIT(ACES);
219 val &= ~BIT(SAFESYNC);
220 if ((priv->mode & (SPI_TX_DUAL | SPI_RX_DUAL)) && div < 3)
221 val |= BIT(SAFESYNC);
222 if ((priv->mode & (SPI_TX_QUAD | SPI_RX_QUAD)) && div < 6)
223 val |= BIT(SAFESYNC);
224
225 if (priv->mode & SPI_CPHA)
226 val |= BIT(CPHA);
227 else
228 val &= ~BIT(CPHA);
229
230 if (priv->mode & SPI_CPOL)
231 val |= BIT(CPOL);
232 else
233 val &= ~BIT(CPOL);
234
235 if (priv->mode & SPI_CS_HIGH)
236 val |= BIT(SSPOL);
237 else
238 val &= ~BIT(SSPOL);
239
240 if (priv->mode & SPI_LSB_FIRST)
241 val |= BIT(SDIR);
242 else
243 val &= ~BIT(SDIR);
244
245 if (priv->aces)
246 val |= BIT(ACES);
247
248 if (priv->rtm)
249 val |= BIT(RTM);
250
251 val |= (3 << SS2CD);
252 val |= BIT(SENDIAN);
253
254 val &= ~(CDRS_MASK << CDRS_SHIFT);
255 val |= ((div >> 1) << CDRS_SHIFT);
256
257 writel(val, priv->base + PCC(priv->cs));
258
259 val = readl(priv->base + FIFOCFG);
260 val &= ~(BPW_MASK << BPW_SHIFT);
261 val |= (0 << BPW_SHIFT);
262 writel(val, priv->base + FIFOCFG);
263
264 val = readl(priv->base + DMSTART);
265 val &= ~(DATA_MASK << DATA_SHIFT);
266
267 if (tx && rx)
268 val |= (DATA_TXRX << DATA_SHIFT);
269 else if (rx)
270 val |= (DATA_RX << DATA_SHIFT);
271 else
272 val |= (DATA_TX << DATA_SHIFT);
273
274 val &= ~(3 << BUS_WIDTH);
275 val |= ((bus_width >> 1) << BUS_WIDTH);
276 writel(val, priv->base + DMSTART);
277}
278
279static int synquacer_spi_xfer(struct udevice *dev, unsigned int bitlen,
280 const void *tx_buf, void *rx_buf,
281 unsigned long flags)
282{
283 struct udevice *bus = dev->parent;
284 struct synquacer_spi_priv *priv = dev_get_priv(bus);
Masahisa Kojimacfa0aeb2022-05-17 17:41:36 +0900285 u32 val, words, busy = 0;
Jassi Brar717bab22021-06-04 18:44:27 +0900286
287 val = readl(priv->base + FIFOCFG);
288 val |= (1 << RX_FLUSH);
289 val |= (1 << TX_FLUSH);
290 writel(val, priv->base + FIFOCFG);
291
292 synquacer_spi_config(dev, rx_buf, tx_buf);
293
294 priv->tx_buf = tx_buf;
295 priv->rx_buf = rx_buf;
296
297 words = bitlen / 8;
298
299 if (tx_buf) {
300 busy |= BIT(TXBIT);
301 priv->tx_words = words;
302 } else {
303 busy &= ~BIT(TXBIT);
304 priv->tx_words = 0;
305 }
306
307 if (rx_buf) {
308 busy |= BIT(RXBIT);
309 priv->rx_words = words;
310 } else {
311 busy &= ~BIT(RXBIT);
312 priv->rx_words = 0;
313 }
314
315 if (flags & SPI_XFER_BEGIN)
316 synquacer_cs_set(priv, true);
317
318 if (tx_buf)
319 write_fifo(priv);
320
321 if (rx_buf) {
322 val = readl(priv->base + FIFOCFG);
323 val &= ~(RX_TRSHLD_MASK << RX_TRSHLD_SHIFT);
324 val |= ((priv->rx_words > FIFO_DEPTH ?
325 RX_TRSHLD : priv->rx_words) << RX_TRSHLD_SHIFT);
326 writel(val, priv->base + FIFOCFG);
327 }
328
329 writel(~0, priv->base + TXC);
330 writel(~0, priv->base + RXC);
331
332 /* Trigger */
Masahisa Kojima71125412022-05-17 17:41:38 +0900333 if (flags & SPI_XFER_BEGIN) {
334 val = readl(priv->base + DMSTART);
335 val |= BIT(TRIGGER);
336 writel(val, priv->base + DMSTART);
337 }
Jassi Brar717bab22021-06-04 18:44:27 +0900338
339 while (busy & (BIT(RXBIT) | BIT(TXBIT))) {
340 if (priv->rx_words)
341 read_fifo(priv);
342 else
343 busy &= ~BIT(RXBIT);
344
345 if (priv->tx_words) {
346 write_fifo(priv);
347 } else {
Masahisa Kojimaa5ab5e12022-05-17 17:41:39 +0900348 /* wait for shifter to empty out */
349 while (!(readl(priv->base + TXF) & BIT(TFES)))
Jassi Brar717bab22021-06-04 18:44:27 +0900350 cpu_relax();
Masahisa Kojimaa5ab5e12022-05-17 17:41:39 +0900351
Jassi Brar717bab22021-06-04 18:44:27 +0900352 busy &= ~BIT(TXBIT);
353 }
354 }
355
356 if (flags & SPI_XFER_END)
357 synquacer_cs_set(priv, false);
358
359 return 0;
360}
361
362static int synquacer_spi_set_speed(struct udevice *bus, uint speed)
363{
364 return 0;
365}
366
367static int synquacer_spi_set_mode(struct udevice *bus, uint mode)
368{
369 return 0;
370}
371
372static int synquacer_spi_claim_bus(struct udevice *dev)
373{
374 return 0;
375}
376
377static int synquacer_spi_release_bus(struct udevice *dev)
378{
379 return 0;
380}
381
382static void synquacer_spi_disable_module(struct synquacer_spi_priv *priv)
383{
384 writel(0, priv->base + MCTRL);
385 while (readl(priv->base + MCTRL) & BIT(MES))
386 cpu_relax();
387}
388
389static void synquacer_spi_init(struct synquacer_spi_priv *priv)
390{
391 u32 val;
392
393 synquacer_spi_disable_module(priv);
394
395 writel(0, priv->base + TXE);
396 writel(0, priv->base + RXE);
397 val = readl(priv->base + TXF);
398 writel(val, priv->base + TXC);
399 val = readl(priv->base + RXF);
400 writel(val, priv->base + RXC);
401 val = readl(priv->base + FAULTF);
402 writel(val, priv->base + FAULTC);
403
404 val = readl(priv->base + DMCFG);
405 val &= ~BIT(SSDC);
406 val &= ~BIT(MSTARTEN);
407 writel(val, priv->base + DMCFG);
408
409 /* Enable module with direct mode */
410 val = readl(priv->base + MCTRL);
411 val &= ~BIT(IPCLK);
412 val &= ~BIT(CSEN);
413 val |= BIT(MEN);
414 val |= BIT(SYNCON);
415 writel(val, priv->base + MCTRL);
416}
417
418static void synquacer_spi_exit(struct synquacer_spi_priv *priv)
419{
420 u32 val;
421
422 synquacer_spi_disable_module(priv);
423
424 /* Enable module with command sequence mode */
425 val = readl(priv->base + MCTRL);
426 val &= ~BIT(IPCLK);
427 val |= BIT(CSEN);
428 val |= BIT(MEN);
429 val |= BIT(SYNCON);
430 writel(val, priv->base + MCTRL);
431
432 while (!(readl(priv->base + MCTRL) & BIT(MES)))
433 cpu_relax();
434}
435
436static int synquacer_spi_probe(struct udevice *bus)
437{
438 struct synquacer_spi_plat *plat = dev_get_plat(bus);
439 struct synquacer_spi_priv *priv = dev_get_priv(bus);
440
441 priv->base = plat->base;
442 priv->aces = plat->aces;
443 priv->rtm = plat->rtm;
444
445 synquacer_spi_init(priv);
446 return 0;
447}
448
449static int synquacer_spi_remove(struct udevice *bus)
450{
451 struct synquacer_spi_priv *priv = dev_get_priv(bus);
452
453 synquacer_spi_exit(priv);
454 return 0;
455}
456
457static int synquacer_spi_of_to_plat(struct udevice *bus)
458{
459 struct synquacer_spi_plat *plat = dev_get_plat(bus);
460 struct clk clk;
461
462 plat->base = dev_read_addr_ptr(bus);
463
464 plat->aces = dev_read_bool(bus, "socionext,set-aces");
465 plat->rtm = dev_read_bool(bus, "socionext,use-rtm");
466
467 clk_get_by_name(bus, "iHCLK", &clk);
468 clk_enable(&clk);
469
470 return 0;
471}
472
473static const struct dm_spi_ops synquacer_spi_ops = {
474 .claim_bus = synquacer_spi_claim_bus,
475 .release_bus = synquacer_spi_release_bus,
476 .xfer = synquacer_spi_xfer,
477 .set_speed = synquacer_spi_set_speed,
478 .set_mode = synquacer_spi_set_mode,
479};
480
481static const struct udevice_id synquacer_spi_ids[] = {
482 { .compatible = "socionext,synquacer-spi" },
483 { /* Sentinel */ }
484};
485
486U_BOOT_DRIVER(synquacer_spi) = {
487 .name = "synquacer_spi",
488 .id = UCLASS_SPI,
489 .of_match = synquacer_spi_ids,
490 .ops = &synquacer_spi_ops,
491 .of_to_plat = synquacer_spi_of_to_plat,
492 .plat_auto = sizeof(struct synquacer_spi_plat),
493 .priv_auto = sizeof(struct synquacer_spi_priv),
494 .probe = synquacer_spi_probe,
495 .flags = DM_FLAG_OS_PREPARE,
496 .remove = synquacer_spi_remove,
497};