summaryrefslogtreecommitdiff
path: root/drivers/spi/sunxi_spi.c
blob: 871c88d87109833f04ef0c5da30693ed9086dc13 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
/*
 * SPI driver for Allwinner sunxi SoCs
 *
 * Copyright (C) 2015-2017 Theobroma Systems Design und Consulting GmbH
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License as
 * published by the Free Software Foundation; either version 2 of
 * the License, or (at your option) any later version.
 */

#include <common.h>
#ifdef CONFIG_DM_GPIO
#include <asm/gpio.h>
#endif
#include <asm/io.h>
#include <clk.h>
#include <dm.h>
#include <errno.h>
#include <fdtdec.h>
#include <wait_bit.h>
#include <reset.h>
#include <spi.h>

DECLARE_GLOBAL_DATA_PTR;

/* The SPI flash opcode for a FAST READ DUAL OUTPUT operation. */
#define CMD_READ_DUAL_OUTPUT_FAST 0x3b

struct sunxi_spi_platdata {
	void *base;
	unsigned int max_hz;

	struct reset_ctl reset_ctl;
	struct clk ahb_clk_gate;
	struct clk spi_clk;

	/* We could do with a single delay counter, but it won't do harm
	   to have two, as the same is the case for most other driver. */
	uint deactivate_delay_us;	/* Delay to wait after deactivate */
	uint activate_delay_us;		/* Delay to wait after activate */

#if defined(CONFIG_DM_GPIO)
	int cs_gpios_num;
	struct gpio_desc *cs_gpios;
#endif
};

struct sunxi_spi_driverdata {
	unsigned int  fifo_depth;
};

enum {
	NONE = 0,
	OPC_READ_DUAL_CMD,
};

struct sunxi_spi_privdata {
	int  transaction_type;
	ulong last_transaction_us;	/* Time of last transaction end */
	unsigned int hz_requested;      /* last requested bitrate */
	unsigned int hz_actual;         /* currently set bitrate */
};

struct sunxi_spi_reg {
	u8	_rsvd[0x4];
	u32	GCR;   /* SPI Global Control register */
	u32	TCR;   /* SPI Transfer Control register */
	u8	_rsvd1[0x4];
	u32	IER;   /* SPI Interrupt Control register */
	u32	ISR;   /* SPI Interrupt Status register */
	u32	FCR;   /* SPI FIFO Control register */
	u32	FSR;   /* SPI FIFO Status register */
	u32	WCR;   /* SPI Wait Clock Counter register */
	u32	CCR;   /* SPI Clock Rate Control register */
	u8	_rsvd2[0x8];
	u32	MBC;   /* SPI Burst Counter register */
	u32	MTC;   /* SPI Transmit Counter register */
	u32	BCC;   /* SPI Burst Control register */
	u8      _rsvd3[0x4c];
	u32     NDMA_MODE_CTL;
	u8	_rsvd4[0x174];
	u32	TXD;   /* SPI TX Data register */
	u8	_rsvd5[0xfc];
	u32	RXD;   /* SPI RX Data register */
};


#define GCR_MASTER	 BIT(1)
#define GCR_EN		 BIT(0)

#define TCR_XCH          BIT(31)
#define TCR_SDC          BIT(11)
#define TCR_DHB          BIT(8)
#define TCR_SSSEL_SHIFT  (4)
#define TCR_SSSEL_MASK   (0x3 << TCR_SSSEL_SHIFT)
#define TCR_SSLEVEL      BIT(7)
#define TCR_SSOWNER      BIT(6)
#define TCR_CPOL         BIT(1)
#define TCR_CPHA         BIT(0)

#define FCR_RX_FIFO_RST  BIT(31)
#define FCR_TX_FIFO_RST  BIT(15)

#define BCC_STC_MASK     (0x00FFFFFF)

#define CCTL_SEL_CDR1    0
#define CCTL_SEL_CDR2    BIT(12)
#define CDR1(n)          ((n & 0xf) << 8)
#define CDR2(n)          (((n/2) - 1) & 0xff)

static int sunxi_spi_cs_activate(struct udevice *dev, unsigned cs)
{
	struct udevice *bus = dev->parent;
	struct sunxi_spi_platdata *plat = dev_get_platdata(bus);
	struct sunxi_spi_reg *spi = (struct sunxi_spi_reg *)plat->base;
	struct sunxi_spi_privdata *priv = dev_get_priv(bus);
	int ret = 0;

	debug("%s (%s): cs %d cs_gpios_num %d cs_gpios %p\n",
	      dev->name, __func__, cs, plat->cs_gpios_num, plat->cs_gpios);

	/* If it's too soon to do another transaction, wait... */
	if (plat->deactivate_delay_us && priv->last_transaction_us) {
		ulong delay_us;
		delay_us = timer_get_us() - priv->last_transaction_us;
		if (delay_us < plat->deactivate_delay_us)
			udelay(plat->deactivate_delay_us - delay_us);
	}

#if defined(CONFIG_DM_GPIO)
	/* Use GPIOs as chip selects? */
	if (plat->cs_gpios) {
		/* Guard against out-of-bounds accesses */
		if (!(cs < plat->cs_gpios_num))
			return -ENOENT;

		if (dm_gpio_is_valid(&plat->cs_gpios[cs])) {
			ret = dm_gpio_set_value(&plat->cs_gpios[cs], 1);
			goto done;
		}
	}
#endif
	/* The hardware can control up to 4 CS, however not all of
	   them will be going to pads. We don't try to second-guess
	   the DT or higher-level drivers though and just test against
	   the hard limit. */

	if (!(cs < 4))
		return -ENOENT;

	/* Control the positional CS output */
	clrsetbits_le32(&spi->TCR, TCR_SSSEL_MASK, cs << TCR_SSSEL_SHIFT);
	clrsetbits_le32(&spi->TCR, TCR_SSLEVEL, TCR_SSOWNER);

done:
	/* We'll delay, even it this is an error return... */
	if (plat->activate_delay_us)
		udelay(plat->activate_delay_us);

	return ret;
}

static void sunxi_spi_cs_deactivate(struct udevice *dev, unsigned cs)
{
	struct udevice *bus = dev->parent;
	struct sunxi_spi_platdata *plat = dev_get_platdata(bus);
	struct sunxi_spi_reg *spi = (struct sunxi_spi_reg *)plat->base;
	struct sunxi_spi_privdata *priv = dev_get_priv(bus);

#if defined(CONFIG_DM_GPIO)
	/* Use GPIOs as chip selects? */
	if (plat->cs_gpios) {
		if (dm_gpio_is_valid(&plat->cs_gpios[cs])) {
			dm_gpio_set_value(&plat->cs_gpios[cs], 0);
			return;
		}
	}
#endif

	/* We have only the hardware chip select, so use those */
	setbits_le32(&spi->TCR, TCR_SSLEVEL | TCR_SSOWNER);

	/* Remember time of this transaction for the next delay */
	if (plat->deactivate_delay_us)
		priv->last_transaction_us = timer_get_us();
}

static inline uint8_t *spi_fill_writefifo(struct sunxi_spi_reg *spi,
					  uint8_t *dout, int cnt)
{
	debug("%s: dout = %p, cnt = %d\n", __func__, dout, cnt);

	if (dout) {
		int i;

		for (i = 0; i < cnt; i++)
			writeb(dout[i], &spi->TXD);

		dout += cnt;
	}

	return dout;
}

static inline uint8_t *spi_drain_readfifo(struct sunxi_spi_reg *spi,
					  uint8_t *din, int cnt)
{
	debug("%s: din = %p, cnt = %d\n", __func__, din, cnt);

	if (din) {
		int i;

		for (i = 0; i < cnt; i++)
			din[i] = readb(&spi->RXD);

		din += cnt;
	}

	return din;
}

static int sunxi_spi_trans_setup(struct sunxi_spi_privdata *priv,
				 const uint8_t *dout,
				 const uint8_t *din,
				 unsigned int n_bytes)
{
	if (!dout) {
		error("%s: SPI flash command requires at least an opcode\n",
		      __func__);
		return -EPROTO;
	}

	/* Detect dual-IO read commands */
	if (dout[0] == CMD_READ_DUAL_OUTPUT_FAST) {
		/* This is always called as two xfer-requests from the
		 * higher layers:
		 *  1. a write-only request with the 1-byte opcode,
		 *     4-byte address and a dummy byte
		 *  2. a read-only for the requested amount of data
		 */

		/* TODO: The "cmd, addr, dummy" sequence should be
		 *       changed to "cmd, addr" w/ the controller
		 *       generating the dummy cycles, so the Hi-Z
		 *       state for IO0 and IO1 can already be
		 *       generated during the dummy cycles.
		 */
		priv->transaction_type = OPC_READ_DUAL_CMD;
	}

	return 0;
}

static void sunxi_spi_trans_end(struct sunxi_spi_privdata *priv)
{
	priv->transaction_type = NONE;
}

static int sunxi_spi_xfer(struct udevice *dev, unsigned int bitlen,
			  const void *out, void *in, unsigned long flags)
{
	struct udevice *bus = dev->parent;
	struct sunxi_spi_platdata *plat = dev_get_platdata(bus);
	struct sunxi_spi_privdata *priv = dev_get_priv(bus);
	struct sunxi_spi_reg *spi = (struct sunxi_spi_reg *)plat->base;
	struct sunxi_spi_driverdata *data =
		(struct sunxi_spi_driverdata *)dev_get_driver_data(dev->parent);
	struct dm_spi_slave_platdata *slave = dev_get_parent_platdata(dev);
	uint8_t *dout = (uint8_t *)out;
	uint8_t *din = (uint8_t *)in;
	int fifo_depth = data->fifo_depth;
	unsigned int n_bytes = DIV_ROUND_UP(bitlen, 8);
	int ret = 0;
	/*
	 * We assume that 1ms (for any delays within the module to
	 * start the transfer) + 2x the time to transfer a full FIFO
	 * (for the data- and bitrate-dependent part) is a reasonable
	 * timeout to detect the module being stuck.
	 */
	ulong timeout_ms =
		(DIV_ROUND_UP(fifo_depth * 16000, priv->hz_actual)) + 1;

	debug("%s (%s): regs %p bitlen %d din %p flags %lx fifo_depth %d\n",
	      dev->name, __func__, spi, bitlen, din, flags, fifo_depth);

	if (flags & SPI_XFER_BEGIN) {
		/* For dual-IO support, we need to detect flash read
		 * commands here... this is actually a layering
		 * violation, but can't be fixed non-intrusively now
		 * and other drivers (e.g. Freescale QSPI, Intel ICH)
		 * follow this pattern as well.
		 */
		if (device_get_uclass_id(dev) == UCLASS_SPI_FLASH) {
			ret = sunxi_spi_trans_setup(priv, dout, din, n_bytes);
			if (ret < 0)
				return ret;
		}

		ret = sunxi_spi_cs_activate(dev, slave->cs);
		if (ret < 0) {
			error("%s: failed to activate chip-select %d\n",
			      dev->name, slave->cs);
			return ret;
		}
	}

	/* Reset FIFO */
	writel(FCR_RX_FIFO_RST | FCR_TX_FIFO_RST, &spi->FCR);
	/* Wait until the FIFO reset autoclears */
	ret = wait_for_bit(dev->name, &spi->FCR,
			   FCR_RX_FIFO_RST | FCR_TX_FIFO_RST,
			   false, 10, true);
	if (ret < 0) {
		error("%s: failed to reset FIFO within 10ms\n", bus->name);
		return ret;
	}

	/* Set the discard burst bits depending on whether we are receiving */
	clrbits_le32(&spi->TCR, TCR_DHB);
	if (!din)
		setbits_le32(&spi->TCR, TCR_DHB);

	/* Set the dual-mode input bit */
	if (priv->transaction_type == OPC_READ_DUAL_CMD)
		setbits_le32(&spi->BCC, BIT(28));
	else
		clrbits_le32(&spi->BCC, BIT(28));

	/* Transfer in blocks of FIFO_DEPTH */
	while (n_bytes > 0) {
		int cnt = (n_bytes < fifo_depth) ? n_bytes : fifo_depth;
		int txcnt = dout ? cnt : 0;

		/* We need to set up the transfer counters in every
		   iteration, as the hardware block counts those down
		   to 0 and leaves the 0 in the register (i.e. there's
		   no shadow register within the controller that these
		   values are copied into). */

		/* master burst counter:     total length (tx + rx + dummy) */
		writel(cnt, &spi->MBC);
		/* master transmit counter:  tx */
		writel(txcnt, &spi->MTC);
		/* burst control counter:    single-mode tx */
		clrsetbits_le32(&spi->BCC, BCC_STC_MASK, txcnt & BCC_STC_MASK);

		dout = spi_fill_writefifo(spi, dout, txcnt);

		/* Start transfer ... */
		setbits_le32(&spi->TCR, TCR_XCH);
		/* ... and wait until it finshes. */
		ret = wait_for_bit(dev->name, &spi->TCR, TCR_XCH,
				   false, timeout_ms, true);
		if (ret < 0) {
			error("%s: stuck in XCH for %ld ms\n",
			      bus->name, timeout_ms);
			goto fail;
		}

		din = spi_drain_readfifo(spi, din, cnt);

		n_bytes -= cnt;
	}

 fail:
	if (flags & SPI_XFER_END) {
		sunxi_spi_cs_deactivate(dev, slave->cs);
		sunxi_spi_trans_end(priv);
	}

	return 0;
};

static int sunxi_spi_ofdata_to_platdata(struct udevice *dev)
{
	struct sunxi_spi_platdata *plat = dev_get_platdata(dev);
	const void *blob = gd->fdt_blob;
	int node = dev->of_offset;
	fdt_addr_t addr;
	fdt_size_t size;
	int ret;

	debug("%s: %p\n", __func__, dev);

	addr = fdtdec_get_addr_size_auto_noparent(blob, node, "reg", 0,
						  &size, false);
	if (addr == FDT_ADDR_T_NONE) {
		debug("%s: failed to find base address\n", dev->name);
		return -ENODEV;
	}
	plat->base = (void *)addr;
	plat->max_hz = fdtdec_get_int(blob, node, "spi-max-frequency", 0);
	plat->activate_delay_us = fdtdec_get_int(blob, node,
						 "spi-activate_delay", 0);
	plat->deactivate_delay_us = fdtdec_get_int(blob, node,
						   "spi-deactivate-delay", 0);

#if defined(CONFIG_DM_GPIO)
	plat->cs_gpios_num = gpio_get_list_count(dev, "cs-gpios");
	if (plat->cs_gpios_num > 0) {
		int i;

		plat->cs_gpios = calloc(plat->cs_gpios_num,
					sizeof(struct gpio_desc));
		if (!plat->cs_gpios)
			return -ENOMEM;

		for (i = 0; i < plat->cs_gpios_num; ++i)
			gpio_request_by_name(dev, "cs-gpios", i,
					     &plat->cs_gpios[i], 0);
	}
#endif

	ret = reset_get_by_index(dev, 0, &plat->reset_ctl);
	if (ret) {
		error("%s: reset_get_by_index() with return code %d\n",
		      dev->name, ret);
		return ret;
	}

	if (clk_get_by_name(dev, "ahb", &plat->ahb_clk_gate) ||
	    clk_get_by_name(dev, "spi", &plat->spi_clk)) {
		error("%s: failed to get our clocks: ahb, spi\n", dev->name);
		return -EINVAL;
	}

	return 0;
}

static int sunxi_spi_probe(struct udevice *dev)
{
	return 0;
}

static int sunxi_spi_claim_bus(struct udevice *dev)
{
	struct sunxi_spi_platdata *plat = dev_get_platdata(dev->parent);
	struct spi_slave *spi_slave = dev_get_parent_priv(dev);
	struct sunxi_spi_reg *spi = (struct sunxi_spi_reg *)plat->base;

	debug("%s: %p %p\n", __func__, dev, dev->parent);

	/* Enable in master-mode */
	setbits_le32(&spi->GCR, GCR_MASTER | GCR_EN);
	/* All CS control is manual and set them to inactive */
	clrbits_le32(&spi->TCR, TCR_SSSEL_MASK);
	setbits_le32(&spi->TCR, TCR_SSOWNER);
	/* Apply polarity and phase from the mode bits */
	if (spi_slave->mode & SPI_CPOL)
		setbits_le32(&spi->TCR, TCR_CPOL);
	if (spi_slave->mode & SPI_CPHA)
		setbits_le32(&spi->TCR, TCR_CPHA);

#if defined(DM_GPIO)
	/* Set all cs-gpios to inactive */
	for (i = 0; i < plat->cs_gpios_num; ++i)
		if (dm_gpio_is_valid(&plat->cs_gpios[i]))
			dm_gpio_set_value(&plat->cs_gpios[i], 0);
#endif


	return 0;
}

static int sunxi_spi_release_bus(struct udevice *dev)
{
	struct sunxi_spi_platdata *plat = dev_get_platdata(dev->parent);
	struct sunxi_spi_reg *spi = (struct sunxi_spi_reg *)plat->base;

	clrbits_le32(&spi->GCR, GCR_EN);

	return 0;
}

static int sunxi_spi_set_speed(struct udevice *bus, unsigned int hz)
{
	struct sunxi_spi_platdata *plat = dev_get_platdata(bus);
	struct sunxi_spi_reg *spi = (struct sunxi_spi_reg *)plat->base;
	struct sunxi_spi_privdata *priv = dev_get_priv(bus);
	unsigned sclk_shift, hz_ahb, hz_sclk;

	debug("%s: %p, %d\n", __func__, bus, hz);

	if (plat->max_hz && (hz > plat->max_hz)) {
		debug("%s: selected speed (%d) exceeds maximum of %d\n",
		      bus->name, hz, plat->max_hz);
		hz = plat->max_hz;
	}

	/* If the last request was for the same speed, we're done */
	if (priv->hz_requested == hz)
		return 0;

	/* The CCU section in the manual recommends to have the module
	   reset deasserted before the module clock gate is opened. */
	reset_deassert(&plat->reset_ctl);

	/* Enable and set the module clock.
	 *
	 * At least for the A31, there's a requirements to provide at
	 * least 2x the sample clock, so we should never go below that
	 * ratio between the AHB clock and the (ampling) SCLK. On the
	 * low end of the clock, we use the provide two step-downs for
	 * clocks on the low end (below 375kHz).
	 *
	 * However, testing shows that for high-speed modes (on the
	 * A64), we may not divide SCLK from the AHB clock.
	 */
	if (hz < 100000)
		sclk_shift = 8;
	else if (hz < 50000000)
		sclk_shift = 2;
	else
		sclk_shift = 0;

	/* Program the SPI clock control */
	writel(CCTL_SEL_CDR1 | CDR1(sclk_shift), &spi->CCR);

	hz_ahb = clk_set_rate(&plat->spi_clk, hz * (1 << sclk_shift));
	clk_enable(&plat->spi_clk);
	/* Pass the clock to the module */
	clk_enable(&plat->ahb_clk_gate);

	hz_sclk = hz_ahb >> sclk_shift;
	priv->hz_actual = hz_sclk;
	debug("%s: hz_ahb %d  hz_sclk %d\n", bus->name, hz_ahb, hz_sclk);

	/* If this is a high-speed mode (which we define---based upon
	   empirical testing---to be above 50 MHz), we need to move the
	   sampling point during data read. */
	if (hz_sclk > 50000000)
		setbits_le32(&spi->TCR, TCR_SDC);
	else
		clrbits_le32(&spi->TCR, TCR_SDC);

	return 0;
};

static int sunxi_spi_set_mode(struct udevice *bus, unsigned int mode)
{
	return 0;
};

static const struct dm_spi_ops sunxi_spi_ops = {
	.claim_bus	= sunxi_spi_claim_bus,
	.release_bus	= sunxi_spi_release_bus,
	.xfer		= sunxi_spi_xfer,
	.set_speed	= sunxi_spi_set_speed,
	.set_mode	= sunxi_spi_set_mode,
	/*
	 * cs_info is not needed, since we require all chip selects to be
	 * in the device tree explicitly
	 */
};

static struct sunxi_spi_driverdata  sun6i_a31_data = {
	.fifo_depth = 128,
};

static struct sunxi_spi_driverdata  sun50i_a64_data = {
	.fifo_depth = 64,
};

static const struct udevice_id sunxi_spi_ids[] = {
	{ .compatible = "allwinner,sun6i-a31-spi",
	  .data = (uintptr_t)&sun6i_a31_data },
	{ .compatible = "allwinner,sun8i-h3-spi",
	  .data = (uintptr_t)&sun50i_a64_data },
	{ }
};

U_BOOT_DRIVER(sunxi_spi) = {
	.name = "sunxi_spi",
	.id = UCLASS_SPI,
	.of_match = sunxi_spi_ids,
	.ofdata_to_platdata = sunxi_spi_ofdata_to_platdata,
	.platdata_auto_alloc_size = sizeof(struct sunxi_spi_platdata),
	.priv_auto_alloc_size = sizeof(struct sunxi_spi_privdata),
	.probe = sunxi_spi_probe,
	.ops = &sunxi_spi_ops,
};