1// 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 TFES		1
49#define TFLETE		4
50#define TSSRS		6
51#define RFMTE		5
52#define RSSRS		6
53
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		}
176
177		/* wait until slave is deselected */
178		while (!(readl(priv->base + TXF) & BIT(TSSRS)) ||
179		       !(readl(priv->base + RXF) & BIT(RSSRS)))
180			;
181	}
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);
189	u32 val, div, bus_width;
190	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_DUAL)
207		bus_width = 2;
208	else if (priv->mode & SPI_TX_QUAD)
209		bus_width = 4;
210	else if (priv->mode & SPI_TX_OCTAL)
211		bus_width = 8;
212	else
213		bus_width = 1; /* default is single bit mode */
214
215	div = DIV_ROUND_UP(125000000, priv->speed);
216
217	val = readl(priv->base + PCC(priv->cs));
218	val &= ~BIT(RTM);
219	val &= ~BIT(ACES);
220	val &= ~BIT(SAFESYNC);
221	if ((priv->mode & (SPI_TX_DUAL | SPI_RX_DUAL)) && div < 3)
222		val |= BIT(SAFESYNC);
223	if ((priv->mode & (SPI_TX_QUAD | SPI_RX_QUAD)) && div < 6)
224		val |= BIT(SAFESYNC);
225
226	if (priv->mode & SPI_CPHA)
227		val |= BIT(CPHA);
228	else
229		val &= ~BIT(CPHA);
230
231	if (priv->mode & SPI_CPOL)
232		val |= BIT(CPOL);
233	else
234		val &= ~BIT(CPOL);
235
236	if (priv->mode & SPI_CS_HIGH)
237		val |= BIT(SSPOL);
238	else
239		val &= ~BIT(SSPOL);
240
241	if (priv->mode & SPI_LSB_FIRST)
242		val |= BIT(SDIR);
243	else
244		val &= ~BIT(SDIR);
245
246	if (priv->aces)
247		val |= BIT(ACES);
248
249	if (priv->rtm)
250		val |= BIT(RTM);
251
252	val |= (3 << SS2CD);
253	val |= BIT(SENDIAN);
254
255	val &= ~(CDRS_MASK << CDRS_SHIFT);
256	val |= ((div >> 1) << CDRS_SHIFT);
257
258	writel(val, priv->base + PCC(priv->cs));
259
260	val = readl(priv->base + FIFOCFG);
261	val &= ~(BPW_MASK << BPW_SHIFT);
262	val |= (0 << BPW_SHIFT);
263	writel(val, priv->base + FIFOCFG);
264
265	val = readl(priv->base + DMSTART);
266	val &= ~(DATA_MASK << DATA_SHIFT);
267
268	if (tx && rx)
269		val |= (DATA_TXRX << DATA_SHIFT);
270	else if (rx)
271		val |= (DATA_RX << DATA_SHIFT);
272	else
273		val |= (DATA_TX << DATA_SHIFT);
274
275	val &= ~(3 << BUS_WIDTH);
276	val |= ((bus_width >> 1) << BUS_WIDTH);
277	writel(val, priv->base + DMSTART);
278}
279
280static int synquacer_spi_xfer(struct udevice *dev, unsigned int bitlen,
281			      const void *tx_buf, void *rx_buf,
282			      unsigned long flags)
283{
284	struct udevice *bus = dev->parent;
285	struct synquacer_spi_priv *priv = dev_get_priv(bus);
286	u32 val, words, busy = 0;
287
288	val = readl(priv->base + FIFOCFG);
289	val |= (1 << RX_FLUSH);
290	val |= (1 << TX_FLUSH);
291	writel(val, priv->base + FIFOCFG);
292
293	synquacer_spi_config(dev, rx_buf, tx_buf);
294
295	priv->tx_buf = tx_buf;
296	priv->rx_buf = rx_buf;
297
298	words = bitlen / 8;
299
300	if (tx_buf) {
301		busy |= BIT(TXBIT);
302		priv->tx_words = words;
303	} else {
304		busy &= ~BIT(TXBIT);
305		priv->tx_words = 0;
306	}
307
308	if (rx_buf) {
309		busy |= BIT(RXBIT);
310		priv->rx_words = words;
311	} else {
312		busy &= ~BIT(RXBIT);
313		priv->rx_words = 0;
314	}
315
316	if (flags & SPI_XFER_BEGIN)
317		synquacer_cs_set(priv, true);
318
319	if (tx_buf)
320		write_fifo(priv);
321
322	if (rx_buf) {
323		val = readl(priv->base + FIFOCFG);
324		val &= ~(RX_TRSHLD_MASK << RX_TRSHLD_SHIFT);
325		val |= ((priv->rx_words > FIFO_DEPTH ?
326			RX_TRSHLD : priv->rx_words) << RX_TRSHLD_SHIFT);
327		writel(val, priv->base + FIFOCFG);
328	}
329
330	writel(~0, priv->base + TXC);
331	writel(~0, priv->base + RXC);
332
333	/* Trigger */
334	if (flags & SPI_XFER_BEGIN) {
335		val = readl(priv->base + DMSTART);
336		val |= BIT(TRIGGER);
337		writel(val, priv->base + DMSTART);
338	}
339
340	while (busy & (BIT(RXBIT) | BIT(TXBIT))) {
341		if (priv->rx_words)
342			read_fifo(priv);
343		else
344			busy &= ~BIT(RXBIT);
345
346		if (priv->tx_words) {
347			write_fifo(priv);
348		} else {
349			/* wait for shifter to empty out */
350			while (!(readl(priv->base + TXF) & BIT(TFES)))
351				cpu_relax();
352
353			busy &= ~BIT(TXBIT);
354		}
355	}
356
357	if (flags & SPI_XFER_END)
358		synquacer_cs_set(priv, false);
359
360	return 0;
361}
362
363static int synquacer_spi_set_speed(struct udevice *bus, uint speed)
364{
365	return 0;
366}
367
368static int synquacer_spi_set_mode(struct udevice *bus, uint mode)
369{
370	return 0;
371}
372
373static int synquacer_spi_claim_bus(struct udevice *dev)
374{
375	return 0;
376}
377
378static int synquacer_spi_release_bus(struct udevice *dev)
379{
380	return 0;
381}
382
383static void synquacer_spi_disable_module(struct synquacer_spi_priv *priv)
384{
385	writel(0, priv->base + MCTRL);
386	while (readl(priv->base + MCTRL) & BIT(MES))
387		cpu_relax();
388}
389
390static void synquacer_spi_init(struct synquacer_spi_priv *priv)
391{
392	u32 val;
393
394	synquacer_spi_disable_module(priv);
395
396	writel(0, priv->base + TXE);
397	writel(0, priv->base + RXE);
398	val = readl(priv->base + TXF);
399	writel(val, priv->base + TXC);
400	val = readl(priv->base + RXF);
401	writel(val, priv->base + RXC);
402	val = readl(priv->base + FAULTF);
403	writel(val, priv->base + FAULTC);
404
405	val = readl(priv->base + DMCFG);
406	val &= ~BIT(SSDC);
407	val &= ~BIT(MSTARTEN);
408	writel(val, priv->base + DMCFG);
409
410	/* Enable module with direct mode */
411	val = readl(priv->base + MCTRL);
412	val &= ~BIT(IPCLK);
413	val &= ~BIT(CSEN);
414	val |= BIT(MEN);
415	val |= BIT(SYNCON);
416	writel(val, priv->base + MCTRL);
417}
418
419static void synquacer_spi_exit(struct synquacer_spi_priv *priv)
420{
421	u32 val;
422
423	synquacer_spi_disable_module(priv);
424
425	/* Enable module with command sequence mode */
426	val = readl(priv->base + MCTRL);
427	val &= ~BIT(IPCLK);
428	val |= BIT(CSEN);
429	val |= BIT(MEN);
430	val |= BIT(SYNCON);
431	writel(val, priv->base + MCTRL);
432
433	while (!(readl(priv->base + MCTRL) & BIT(MES)))
434		cpu_relax();
435}
436
437static int synquacer_spi_probe(struct udevice *bus)
438{
439	struct synquacer_spi_plat *plat = dev_get_plat(bus);
440	struct synquacer_spi_priv *priv = dev_get_priv(bus);
441
442	priv->base = plat->base;
443	priv->aces = plat->aces;
444	priv->rtm = plat->rtm;
445
446	synquacer_spi_init(priv);
447	return 0;
448}
449
450static int synquacer_spi_remove(struct udevice *bus)
451{
452	struct synquacer_spi_priv *priv = dev_get_priv(bus);
453
454	synquacer_spi_exit(priv);
455	return 0;
456}
457
458static int synquacer_spi_of_to_plat(struct udevice *bus)
459{
460	struct synquacer_spi_plat *plat = dev_get_plat(bus);
461	struct clk clk;
462
463	plat->base = dev_read_addr_ptr(bus);
464
465	plat->aces = dev_read_bool(bus, "socionext,set-aces");
466	plat->rtm = dev_read_bool(bus, "socionext,use-rtm");
467
468	clk_get_by_name(bus, "iHCLK", &clk);
469	clk_enable(&clk);
470
471	return 0;
472}
473
474static const struct dm_spi_ops synquacer_spi_ops = {
475	.claim_bus	= synquacer_spi_claim_bus,
476	.release_bus	= synquacer_spi_release_bus,
477	.xfer		= synquacer_spi_xfer,
478	.set_speed	= synquacer_spi_set_speed,
479	.set_mode	= synquacer_spi_set_mode,
480};
481
482static const struct udevice_id synquacer_spi_ids[] = {
483	{ .compatible = "socionext,synquacer-spi" },
484	{ /* Sentinel */ }
485};
486
487U_BOOT_DRIVER(synquacer_spi) = {
488	.name		= "synquacer_spi",
489	.id		= UCLASS_SPI,
490	.of_match	= synquacer_spi_ids,
491	.ops		= &synquacer_spi_ops,
492	.of_to_plat	= synquacer_spi_of_to_plat,
493	.plat_auto	= sizeof(struct synquacer_spi_plat),
494	.priv_auto	= sizeof(struct synquacer_spi_priv),
495	.probe		= synquacer_spi_probe,
496	.flags		= DM_FLAG_OS_PREPARE,
497	.remove		= synquacer_spi_remove,
498};
499