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