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