1// SPDX-License-Identifier: GPL-2.0+
2/*
3 * Andestech ATCSPI200 SPI controller driver.
4 *
5 * Copyright 2017 Andes Technology, Inc.
6 * Author: Rick Chen (rick@andestech.com)
7 */
8
9#include <common.h>
10#include <clk.h>
11#include <log.h>
12#include <malloc.h>
13#include <spi.h>
14#include <asm/global_data.h>
15#include <asm/io.h>
16#include <dm.h>
17
18DECLARE_GLOBAL_DATA_PTR;
19
20#define MAX_TRANSFER_LEN	512
21#define CHUNK_SIZE		1
22#define SPI_TIMEOUT		0x100000
23#define SPI0_BUS		0
24#define SPI1_BUS		1
25#define SPI0_BASE		0xf0b00000
26#define SPI1_BASE		0xf0f00000
27#define NSPI_MAX_CS_NUM		1
28
29struct atcspi200_spi_regs {
30	u32	rev;
31	u32	reserve1[3];
32	u32	format;		/* 0x10 */
33#define DATA_LENGTH(x)	((x-1)<<8)
34	u32	pio;
35	u32	reserve2[2];
36	u32	tctrl;		/* 0x20 */
37#define TRAMODE_OFFSET	24
38#define TRAMODE_MASK	(0x0F<<TRAMODE_OFFSET)
39#define TRAMODE_WR_SYNC	(0<<TRAMODE_OFFSET)
40#define TRAMODE_WO	(1<<TRAMODE_OFFSET)
41#define TRAMODE_RO	(2<<TRAMODE_OFFSET)
42#define TRAMODE_WR	(3<<TRAMODE_OFFSET)
43#define TRAMODE_RW	(4<<TRAMODE_OFFSET)
44#define TRAMODE_WDR	(5<<TRAMODE_OFFSET)
45#define TRAMODE_RDW	(6<<TRAMODE_OFFSET)
46#define TRAMODE_NONE	(7<<TRAMODE_OFFSET)
47#define TRAMODE_DW	(8<<TRAMODE_OFFSET)
48#define TRAMODE_DR	(9<<TRAMODE_OFFSET)
49#define WCNT_OFFSET	12
50#define WCNT_MASK	(0x1FF<<WCNT_OFFSET)
51#define RCNT_OFFSET	0
52#define RCNT_MASK	(0x1FF<<RCNT_OFFSET)
53	u32	cmd;
54	u32	addr;
55	u32	data;
56	u32	ctrl;		/* 0x30 */
57#define TXFTH_OFFSET	16
58#define RXFTH_OFFSET	8
59#define TXDMAEN		(1<<4)
60#define RXDMAEN		(1<<3)
61#define TXFRST		(1<<2)
62#define RXFRST		(1<<1)
63#define SPIRST		(1<<0)
64	u32	status;
65#define TXFFL		(1<<23)
66#define TXEPTY		(1<<22)
67#define TXFVE_MASK	(0x1F<<16)
68#define RXFEM		(1<<14)
69#define RXFVE_OFFSET	(8)
70#define RXFVE_MASK	(0x1F<<RXFVE_OFFSET)
71#define SPIBSY		(1<<0)
72	u32	inten;
73	u32	intsta;
74	u32	timing;		/* 0x40 */
75#define SCLK_DIV_MASK	0xFF
76};
77
78struct nds_spi_slave {
79	volatile struct atcspi200_spi_regs *regs;
80	int		to;
81	unsigned int	freq;
82	ulong		clock;
83	unsigned int	mode;
84	u8		num_cs;
85	unsigned int	mtiming;
86	size_t		cmd_len;
87	u8		cmd_buf[16];
88	size_t		data_len;
89	size_t		tran_len;
90	u8		*din;
91	u8		*dout;
92	unsigned int    max_transfer_length;
93};
94
95static int __atcspi200_spi_set_speed(struct nds_spi_slave *ns)
96{
97	u32 tm;
98	u8 div;
99	tm = ns->regs->timing;
100	tm &= ~SCLK_DIV_MASK;
101
102	if(ns->freq >= ns->clock)
103		div =0xff;
104	else{
105		for (div = 0; div < 0xff; div++) {
106			if (ns->freq >= ns->clock / (2 * (div + 1)))
107				break;
108		}
109	}
110
111	tm |= div;
112	ns->regs->timing = tm;
113
114	return 0;
115
116}
117
118static int __atcspi200_spi_claim_bus(struct nds_spi_slave *ns)
119{
120		unsigned int format=0;
121		ns->regs->ctrl |= (TXFRST|RXFRST|SPIRST);
122		while((ns->regs->ctrl &(TXFRST|RXFRST|SPIRST))&&(ns->to--))
123			if(!ns->to)
124				return -EINVAL;
125
126		ns->cmd_len = 0;
127		format = ns->mode|DATA_LENGTH(8);
128		ns->regs->format = format;
129		__atcspi200_spi_set_speed(ns);
130
131		return 0;
132}
133
134static int __atcspi200_spi_release_bus(struct nds_spi_slave *ns)
135{
136	/* do nothing */
137	return 0;
138}
139
140static int __atcspi200_spi_start(struct nds_spi_slave *ns)
141{
142	int i,olen=0;
143	int tc = ns->regs->tctrl;
144
145	tc &= ~(WCNT_MASK|RCNT_MASK|TRAMODE_MASK);
146	if ((ns->din)&&(ns->cmd_len))
147		tc |= TRAMODE_WR;
148	else if (ns->din)
149		tc |= TRAMODE_RO;
150	else
151		tc |= TRAMODE_WO;
152
153	if(ns->dout)
154		olen = ns->tran_len;
155	tc |= (ns->cmd_len+olen-1) << WCNT_OFFSET;
156
157	if(ns->din)
158		tc |= (ns->tran_len-1) << RCNT_OFFSET;
159
160	ns->regs->tctrl = tc;
161	ns->regs->cmd = 1;
162
163	for (i=0;i<ns->cmd_len;i++)
164		ns->regs->data = ns->cmd_buf[i];
165
166	return 0;
167}
168
169static int __atcspi200_spi_stop(struct nds_spi_slave *ns)
170{
171	ns->regs->timing = ns->mtiming;
172	while ((ns->regs->status & SPIBSY)&&(ns->to--))
173		if (!ns->to)
174			return -EINVAL;
175
176	return 0;
177}
178
179static void __nspi_espi_tx(struct nds_spi_slave *ns, const void *dout)
180{
181	ns->regs->data = *(u8 *)dout;
182}
183
184static int __nspi_espi_rx(struct nds_spi_slave *ns, void *din, unsigned int bytes)
185{
186	*(u8 *)din = ns->regs->data;
187	return bytes;
188}
189
190
191static int __atcspi200_spi_xfer(struct nds_spi_slave *ns,
192		unsigned int bitlen,  const void *data_out, void *data_in,
193		unsigned long flags)
194{
195		unsigned int event, rx_bytes;
196		const void *dout = NULL;
197		void *din = NULL;
198		int num_blks, num_chunks, max_tran_len, tran_len;
199		int num_bytes;
200		u8 *cmd_buf = ns->cmd_buf;
201		size_t cmd_len = ns->cmd_len;
202		unsigned long data_len = bitlen / 8;
203		int rf_cnt;
204		int ret = 0, timeout = 0;
205
206		max_tran_len = ns->max_transfer_length;
207		switch (flags) {
208		case SPI_XFER_BEGIN:
209			cmd_len = ns->cmd_len = data_len;
210			memcpy(cmd_buf, data_out, cmd_len);
211			return 0;
212
213		case 0:
214		case SPI_XFER_END:
215			if (bitlen == 0) {
216				return 0;
217			}
218			ns->data_len = data_len;
219			ns->din = (u8 *)data_in;
220			ns->dout = (u8 *)data_out;
221			break;
222
223		case SPI_XFER_BEGIN | SPI_XFER_END:
224			ns->data_len = 0;
225			ns->din = 0;
226			ns->dout = 0;
227			cmd_len = ns->cmd_len = data_len;
228			memcpy(cmd_buf, data_out, cmd_len);
229			data_out = 0;
230			data_len = 0;
231			__atcspi200_spi_start(ns);
232			break;
233		}
234		if (data_out)
235			debug("spi_xfer: data_out %08X(%p) data_in %08X(%p) data_len %lu\n",
236			      *(uint *)data_out, data_out, *(uint *)data_in,
237			      data_in, data_len);
238		num_chunks = DIV_ROUND_UP(data_len, max_tran_len);
239		din = data_in;
240		dout = data_out;
241		while (num_chunks--) {
242			tran_len = min((size_t)data_len, (size_t)max_tran_len);
243			ns->tran_len = tran_len;
244			num_blks = DIV_ROUND_UP(tran_len , CHUNK_SIZE);
245			num_bytes = (tran_len) % CHUNK_SIZE;
246			timeout = SPI_TIMEOUT;
247			if(num_bytes == 0)
248				num_bytes = CHUNK_SIZE;
249			__atcspi200_spi_start(ns);
250
251			while (num_blks && (timeout--)) {
252				event = in_le32(&ns->regs->status);
253				if ((event & TXEPTY) && (data_out)) {
254					__nspi_espi_tx(ns, dout);
255					num_blks -= CHUNK_SIZE;
256					dout += CHUNK_SIZE;
257				}
258
259				if ((event & RXFVE_MASK) && (data_in)) {
260					rf_cnt = ((event & RXFVE_MASK)>> RXFVE_OFFSET);
261					if (rf_cnt >= CHUNK_SIZE)
262						rx_bytes = CHUNK_SIZE;
263					else if (num_blks == 1 && rf_cnt == num_bytes)
264						rx_bytes = num_bytes;
265					else
266						continue;
267
268					if (__nspi_espi_rx(ns, din, rx_bytes) == rx_bytes) {
269						num_blks -= CHUNK_SIZE;
270						din = (unsigned char *)din + rx_bytes;
271					}
272				}
273
274				if (!timeout) {
275					debug("spi_xfer: %s() timeout\n", __func__);
276					break;
277				}
278			}
279
280			data_len -= tran_len;
281			if(data_len)
282			{
283				ns->cmd_buf[1] += ((tran_len>>16)&0xff);
284				ns->cmd_buf[2] += ((tran_len>>8)&0xff);
285				ns->cmd_buf[3] += ((tran_len)&0xff);
286				ns->data_len = data_len;
287			}
288			ret = __atcspi200_spi_stop(ns);
289		}
290		ret = __atcspi200_spi_stop(ns);
291
292		return ret;
293}
294
295static int atcspi200_spi_set_speed(struct udevice *bus, uint max_hz)
296{
297	struct nds_spi_slave *ns = dev_get_priv(bus);
298
299	debug("%s speed %u\n", __func__, max_hz);
300
301	ns->freq = max_hz;
302	__atcspi200_spi_set_speed(ns);
303
304	return 0;
305}
306
307static int atcspi200_spi_set_mode(struct udevice *bus, uint mode)
308{
309	struct nds_spi_slave *ns = dev_get_priv(bus);
310
311	debug("%s mode %u\n", __func__, mode);
312	ns->mode = mode;
313
314	return 0;
315}
316
317static int atcspi200_spi_claim_bus(struct udevice *dev)
318{
319	struct dm_spi_slave_plat *slave_plat =
320		dev_get_parent_plat(dev);
321	struct udevice *bus = dev->parent;
322	struct nds_spi_slave *ns = dev_get_priv(bus);
323
324	if (slave_plat->cs >= ns->num_cs) {
325		printf("Invalid SPI chipselect\n");
326		return -EINVAL;
327	}
328
329	return __atcspi200_spi_claim_bus(ns);
330}
331
332static int atcspi200_spi_release_bus(struct udevice *dev)
333{
334	struct nds_spi_slave *ns = dev_get_priv(dev->parent);
335
336	return __atcspi200_spi_release_bus(ns);
337}
338
339static int atcspi200_spi_xfer(struct udevice *dev, unsigned int bitlen,
340			    const void *dout, void *din,
341			    unsigned long flags)
342{
343	struct udevice *bus = dev->parent;
344	struct nds_spi_slave *ns = dev_get_priv(bus);
345
346	return __atcspi200_spi_xfer(ns, bitlen, dout, din, flags);
347}
348
349static int atcspi200_spi_get_clk(struct udevice *bus)
350{
351	struct nds_spi_slave *ns = dev_get_priv(bus);
352	struct clk clk;
353	ulong clk_rate;
354	int ret;
355
356	ret = clk_get_by_index(bus, 0, &clk);
357	if (ret)
358		return -EINVAL;
359
360	clk_rate = clk_get_rate(&clk);
361	if (!clk_rate)
362		return -EINVAL;
363
364	ns->clock = clk_rate;
365
366	return 0;
367}
368
369static int atcspi200_spi_probe(struct udevice *bus)
370{
371	struct nds_spi_slave *ns = dev_get_priv(bus);
372
373	ns->to = SPI_TIMEOUT;
374	ns->max_transfer_length = MAX_TRANSFER_LEN;
375	ns->mtiming = ns->regs->timing;
376	atcspi200_spi_get_clk(bus);
377
378	return 0;
379}
380
381static int atcspi200_ofdata_to_platadata(struct udevice *bus)
382{
383	struct nds_spi_slave *ns = dev_get_priv(bus);
384	const void *blob = gd->fdt_blob;
385	int node = dev_of_offset(bus);
386
387	ns->regs = map_physmem(dev_read_addr(bus),
388				 sizeof(struct atcspi200_spi_regs),
389				 MAP_NOCACHE);
390	if (!ns->regs) {
391		printf("%s: could not map device address\n", __func__);
392		return -EINVAL;
393	}
394	ns->num_cs = fdtdec_get_int(blob, node, "num-cs", 4);
395
396	return 0;
397}
398
399static const struct dm_spi_ops atcspi200_spi_ops = {
400	.claim_bus	= atcspi200_spi_claim_bus,
401	.release_bus	= atcspi200_spi_release_bus,
402	.xfer		= atcspi200_spi_xfer,
403	.set_speed	= atcspi200_spi_set_speed,
404	.set_mode	= atcspi200_spi_set_mode,
405};
406
407static const struct udevice_id atcspi200_spi_ids[] = {
408	{ .compatible = "andestech,atcspi200" },
409	{ }
410};
411
412U_BOOT_DRIVER(atcspi200_spi) = {
413	.name = "atcspi200_spi",
414	.id = UCLASS_SPI,
415	.of_match = atcspi200_spi_ids,
416	.ops = &atcspi200_spi_ops,
417	.of_to_plat = atcspi200_ofdata_to_platadata,
418	.priv_auto	= sizeof(struct nds_spi_slave),
419	.probe = atcspi200_spi_probe,
420};
421