Blame view

drivers/spi/tegra20_sflash.c 9.47 KB
83d290c56   Tom Rini   SPDX: Convert all...
1
  // SPDX-License-Identifier: GPL-2.0+
9112ef8d8   Tom Warren   tegra2: spi: Add ...
2
  /*
6b3a03e11   Allen Martin   tegra20: spi: mov...
3
   * Copyright (c) 2010-2013 NVIDIA Corporation
9112ef8d8   Tom Warren   tegra2: spi: Add ...
4
5
   * With help from the mpc8xxx SPI driver
   * With more help from omap3_spi SPI driver
9112ef8d8   Tom Warren   tegra2: spi: Add ...
6
7
8
   */
  
  #include <common.h>
fda6fac39   Simon Glass   dm: tegra: spi: C...
9
10
  #include <dm.h>
  #include <errno.h>
9112ef8d8   Tom Warren   tegra2: spi: Add ...
11
12
  #include <asm/io.h>
  #include <asm/gpio.h>
9112ef8d8   Tom Warren   tegra2: spi: Add ...
13
14
  #include <asm/arch/clock.h>
  #include <asm/arch/pinmux.h>
150c24936   Tom Warren   Tegra20: Move som...
15
  #include <asm/arch-tegra/clk_rst.h>
150c24936   Tom Warren   Tegra20: Move som...
16
  #include <spi.h>
8f1b46b10   Allen Martin   tegra: spi: add f...
17
  #include <fdtdec.h>
fda6fac39   Simon Glass   dm: tegra: spi: C...
18
  #include "tegra_spi.h"
8f1b46b10   Allen Martin   tegra: spi: add f...
19
20
  
  DECLARE_GLOBAL_DATA_PTR;
9112ef8d8   Tom Warren   tegra2: spi: Add ...
21

f692248f9   Jagan Teki   spi: tegra: Use B...
22
  #define SPI_CMD_GO			BIT(30)
7a49ba6e5   Allen Martin   tegra: spi: pull ...
23
24
  #define SPI_CMD_ACTIVE_SCLK_SHIFT	26
  #define SPI_CMD_ACTIVE_SCLK_MASK	(3 << SPI_CMD_ACTIVE_SCLK_SHIFT)
f692248f9   Jagan Teki   spi: tegra: Use B...
25
  #define SPI_CMD_CK_SDA			BIT(21)
7a49ba6e5   Allen Martin   tegra: spi: pull ...
26
27
  #define SPI_CMD_ACTIVE_SDA_SHIFT	18
  #define SPI_CMD_ACTIVE_SDA_MASK		(3 << SPI_CMD_ACTIVE_SDA_SHIFT)
f692248f9   Jagan Teki   spi: tegra: Use B...
28
29
30
31
32
33
34
35
36
37
38
  #define SPI_CMD_CS_POL			BIT(16)
  #define SPI_CMD_TXEN			BIT(15)
  #define SPI_CMD_RXEN			BIT(14)
  #define SPI_CMD_CS_VAL			BIT(13)
  #define SPI_CMD_CS_SOFT			BIT(12)
  #define SPI_CMD_CS_DELAY		BIT(9)
  #define SPI_CMD_CS3_EN			BIT(8)
  #define SPI_CMD_CS2_EN			BIT(7)
  #define SPI_CMD_CS1_EN			BIT(6)
  #define SPI_CMD_CS0_EN			BIT(5)
  #define SPI_CMD_BIT_LENGTH		BIT(4)
76538ec64   Jagan Teki   spi: tegra: Use G...
39
  #define SPI_CMD_BIT_LENGTH_MASK		GENMASK(4, 0)
078078cfa   Tom Warren   spi: Tegra2: Seab...
40

f692248f9   Jagan Teki   spi: tegra: Use B...
41
42
43
44
45
46
47
48
49
50
51
52
  #define SPI_STAT_BSY			BIT(31)
  #define SPI_STAT_RDY			BIT(30)
  #define SPI_STAT_RXF_FLUSH		BIT(29)
  #define SPI_STAT_TXF_FLUSH		BIT(28)
  #define SPI_STAT_RXF_UNR		BIT(27)
  #define SPI_STAT_TXF_OVF		BIT(26)
  #define SPI_STAT_RXF_EMPTY		BIT(25)
  #define SPI_STAT_RXF_FULL		BIT(24)
  #define SPI_STAT_TXF_EMPTY		BIT(23)
  #define SPI_STAT_TXF_FULL		BIT(22)
  #define SPI_STAT_SEL_TXRX_N		BIT(16)
  #define SPI_STAT_CUR_BLKCNT		BIT(15)
7a49ba6e5   Allen Martin   tegra: spi: pull ...
53
54
55
56
57
58
59
60
61
62
63
64
65
  
  #define SPI_TIMEOUT		1000
  #define TEGRA_SPI_MAX_FREQ	52000000
  
  struct spi_regs {
  	u32 command;	/* SPI_COMMAND_0 register  */
  	u32 status;	/* SPI_STATUS_0 register */
  	u32 rx_cmp;	/* SPI_RX_CMP_0 register  */
  	u32 dma_ctl;	/* SPI_DMA_CTL_0 register */
  	u32 tx_fifo;	/* SPI_TX_FIFO_0 register */
  	u32 rsvd[3];	/* offsets 0x14 to 0x1F reserved */
  	u32 rx_fifo;	/* SPI_RX_FIFO_0 register */
  };
fda6fac39   Simon Glass   dm: tegra: spi: C...
66
  struct tegra20_sflash_priv {
7a49ba6e5   Allen Martin   tegra: spi: pull ...
67
  	struct spi_regs *regs;
9112ef8d8   Tom Warren   tegra2: spi: Add ...
68
69
  	unsigned int freq;
  	unsigned int mode;
8f1b46b10   Allen Martin   tegra: spi: add f...
70
  	int periph_id;
6b3a03e11   Allen Martin   tegra20: spi: mov...
71
  	int valid;
fda6fac39   Simon Glass   dm: tegra: spi: C...
72
  	int last_transaction_us;
6b3a03e11   Allen Martin   tegra20: spi: mov...
73
  };
fda6fac39   Simon Glass   dm: tegra: spi: C...
74
75
  int tegra20_sflash_cs_info(struct udevice *bus, unsigned int cs,
  			   struct spi_cs_info *info)
9112ef8d8   Tom Warren   tegra2: spi: Add ...
76
  {
00a2749d7   Allen Martin   tegra20: rename t...
77
  	/* Tegra20 SPI-Flash - only 1 device ('bus/cs') */
fda6fac39   Simon Glass   dm: tegra: spi: C...
78
79
  	if (cs != 0)
  		return -ENODEV;
9112ef8d8   Tom Warren   tegra2: spi: Add ...
80
  	else
fda6fac39   Simon Glass   dm: tegra: spi: C...
81
  		return 0;
9112ef8d8   Tom Warren   tegra2: spi: Add ...
82
  }
fda6fac39   Simon Glass   dm: tegra: spi: C...
83
  static int tegra20_sflash_ofdata_to_platdata(struct udevice *bus)
9112ef8d8   Tom Warren   tegra2: spi: Add ...
84
  {
fda6fac39   Simon Glass   dm: tegra: spi: C...
85
86
  	struct tegra_spi_platdata *plat = bus->platdata;
  	const void *blob = gd->fdt_blob;
e160f7d43   Simon Glass   dm: core: Replace...
87
  	int node = dev_of_offset(bus);
9112ef8d8   Tom Warren   tegra2: spi: Add ...
88

a821c4af7   Simon Glass   dm: Rename dev_ad...
89
  	plat->base = devfdt_get_addr(bus);
000f15fa1   Simon Glass   dm: tegra: Conver...
90
  	plat->periph_id = clock_decode_periph_id(bus);
9112ef8d8   Tom Warren   tegra2: spi: Add ...
91

fda6fac39   Simon Glass   dm: tegra: spi: C...
92
93
94
95
96
  	if (plat->periph_id == PERIPH_ID_NONE) {
  		debug("%s: could not decode periph id %d
  ", __func__,
  		      plat->periph_id);
  		return -FDT_ERR_NOTFOUND;
9112ef8d8   Tom Warren   tegra2: spi: Add ...
97
  	}
fda6fac39   Simon Glass   dm: tegra: spi: C...
98
99
100
101
102
103
104
105
106
  	/* Use 500KHz as a suitable default */
  	plat->frequency = fdtdec_get_int(blob, node, "spi-max-frequency",
  					500000);
  	plat->deactivate_delay_us = fdtdec_get_int(blob, node,
  					"spi-deactivate-delay", 0);
  	debug("%s: base=%#08lx, periph_id=%d, max-frequency=%d, deactivate_delay=%d
  ",
  	      __func__, plat->base, plat->periph_id, plat->frequency,
  	      plat->deactivate_delay_us);
6b3a03e11   Allen Martin   tegra20: spi: mov...
107

fda6fac39   Simon Glass   dm: tegra: spi: C...
108
  	return 0;
9112ef8d8   Tom Warren   tegra2: spi: Add ...
109
  }
fda6fac39   Simon Glass   dm: tegra: spi: C...
110
  static int tegra20_sflash_probe(struct udevice *bus)
9112ef8d8   Tom Warren   tegra2: spi: Add ...
111
  {
fda6fac39   Simon Glass   dm: tegra: spi: C...
112
113
  	struct tegra_spi_platdata *plat = dev_get_platdata(bus);
  	struct tegra20_sflash_priv *priv = dev_get_priv(bus);
9112ef8d8   Tom Warren   tegra2: spi: Add ...
114

fda6fac39   Simon Glass   dm: tegra: spi: C...
115
  	priv->regs = (struct spi_regs *)plat->base;
6b3a03e11   Allen Martin   tegra20: spi: mov...
116

fda6fac39   Simon Glass   dm: tegra: spi: C...
117
118
119
  	priv->last_transaction_us = timer_get_us();
  	priv->freq = plat->frequency;
  	priv->periph_id = plat->periph_id;
6b3a03e11   Allen Martin   tegra20: spi: mov...
120

4832c7f5f   Stephen Warren   spi: tegra: fix h...
121
122
123
  	/* Change SPI clock to correct frequency, PLLP_OUT0 source */
  	clock_start_periph_pll(priv->periph_id, CLOCK_ID_PERIPH,
  			       priv->freq);
fda6fac39   Simon Glass   dm: tegra: spi: C...
124
  	return 0;
9112ef8d8   Tom Warren   tegra2: spi: Add ...
125
  }
9694b7244   Simon Glass   dm: spi: Correct ...
126
  static int tegra20_sflash_claim_bus(struct udevice *dev)
9112ef8d8   Tom Warren   tegra2: spi: Add ...
127
  {
9694b7244   Simon Glass   dm: spi: Correct ...
128
  	struct udevice *bus = dev->parent;
fda6fac39   Simon Glass   dm: tegra: spi: C...
129
130
  	struct tegra20_sflash_priv *priv = dev_get_priv(bus);
  	struct spi_regs *regs = priv->regs;
9112ef8d8   Tom Warren   tegra2: spi: Add ...
131
132
133
  	u32 reg;
  
  	/* Change SPI clock to correct frequency, PLLP_OUT0 source */
fda6fac39   Simon Glass   dm: tegra: spi: C...
134
135
  	clock_start_periph_pll(priv->periph_id, CLOCK_ID_PERIPH,
  			       priv->freq);
9112ef8d8   Tom Warren   tegra2: spi: Add ...
136
137
138
139
140
  
  	/* Clear stale status here */
  	reg = SPI_STAT_RDY | SPI_STAT_RXF_FLUSH | SPI_STAT_TXF_FLUSH | \
  		SPI_STAT_RXF_UNR | SPI_STAT_TXF_OVF;
  	writel(reg, &regs->status);
78f47b735   Allen Martin   spi: add common f...
141
142
  	debug("%s: STATUS = %08x
  ", __func__, readl(&regs->status));
9112ef8d8   Tom Warren   tegra2: spi: Add ...
143
144
145
146
  
  	/*
  	 * Use sw-controlled CS, so we can clock in data after ReadID, etc.
  	 */
fda6fac39   Simon Glass   dm: tegra: spi: C...
147
148
  	reg = (priv->mode & 1) << SPI_CMD_ACTIVE_SDA_SHIFT;
  	if (priv->mode & 2)
9112ef8d8   Tom Warren   tegra2: spi: Add ...
149
150
151
  		reg |= 1 << SPI_CMD_ACTIVE_SCLK_SHIFT;
  	clrsetbits_le32(&regs->command, SPI_CMD_ACTIVE_SCLK_MASK |
  		SPI_CMD_ACTIVE_SDA_MASK, SPI_CMD_CS_SOFT | reg);
78f47b735   Allen Martin   spi: add common f...
152
153
  	debug("%s: COMMAND = %08x
  ", __func__, readl(&regs->command));
9112ef8d8   Tom Warren   tegra2: spi: Add ...
154
155
  
  	/*
00a2749d7   Allen Martin   tegra20: rename t...
156
  	 * SPI pins on Tegra20 are muxed - change pinmux later due to UART
9112ef8d8   Tom Warren   tegra2: spi: Add ...
157
158
  	 * issue.
  	 */
70ad375ee   Stephen Warren   ARM: tegra: Tegra...
159
160
161
  	pinmux_set_func(PMUX_PINGRP_GMD, PMUX_FUNC_SFLASH);
  	pinmux_tristate_disable(PMUX_PINGRP_LSPI);
  	pinmux_set_func(PMUX_PINGRP_GMC, PMUX_FUNC_SFLASH);
4560c7dec   Simon Glass   tegra2: spi: Supp...
162

9112ef8d8   Tom Warren   tegra2: spi: Add ...
163
164
  	return 0;
  }
fda6fac39   Simon Glass   dm: tegra: spi: C...
165
  static void spi_cs_activate(struct udevice *dev)
9112ef8d8   Tom Warren   tegra2: spi: Add ...
166
  {
fda6fac39   Simon Glass   dm: tegra: spi: C...
167
168
169
170
171
172
173
174
175
176
177
178
  	struct udevice *bus = dev->parent;
  	struct tegra_spi_platdata *pdata = dev_get_platdata(bus);
  	struct tegra20_sflash_priv *priv = dev_get_priv(bus);
  
  	/* If it's too soon to do another transaction, wait */
  	if (pdata->deactivate_delay_us &&
  	    priv->last_transaction_us) {
  		ulong delay_us;		/* The delay completed so far */
  		delay_us = timer_get_us() - priv->last_transaction_us;
  		if (delay_us < pdata->deactivate_delay_us)
  			udelay(pdata->deactivate_delay_us - delay_us);
  	}
4560c7dec   Simon Glass   tegra2: spi: Supp...
179

9112ef8d8   Tom Warren   tegra2: spi: Add ...
180
  	/* CS is negated on Tegra, so drive a 1 to get a 0 */
fda6fac39   Simon Glass   dm: tegra: spi: C...
181
  	setbits_le32(&priv->regs->command, SPI_CMD_CS_VAL);
9112ef8d8   Tom Warren   tegra2: spi: Add ...
182
  }
fda6fac39   Simon Glass   dm: tegra: spi: C...
183
  static void spi_cs_deactivate(struct udevice *dev)
9112ef8d8   Tom Warren   tegra2: spi: Add ...
184
  {
fda6fac39   Simon Glass   dm: tegra: spi: C...
185
186
187
  	struct udevice *bus = dev->parent;
  	struct tegra_spi_platdata *pdata = dev_get_platdata(bus);
  	struct tegra20_sflash_priv *priv = dev_get_priv(bus);
078078cfa   Tom Warren   spi: Tegra2: Seab...
188

9112ef8d8   Tom Warren   tegra2: spi: Add ...
189
  	/* CS is negated on Tegra, so drive a 0 to get a 1 */
fda6fac39   Simon Glass   dm: tegra: spi: C...
190
191
192
193
194
  	clrbits_le32(&priv->regs->command, SPI_CMD_CS_VAL);
  
  	/* Remember time of this transaction so we can honour the bus delay */
  	if (pdata->deactivate_delay_us)
  		priv->last_transaction_us = timer_get_us();
9112ef8d8   Tom Warren   tegra2: spi: Add ...
195
  }
fda6fac39   Simon Glass   dm: tegra: spi: C...
196
197
198
  static int tegra20_sflash_xfer(struct udevice *dev, unsigned int bitlen,
  			     const void *data_out, void *data_in,
  			     unsigned long flags)
9112ef8d8   Tom Warren   tegra2: spi: Add ...
199
  {
fda6fac39   Simon Glass   dm: tegra: spi: C...
200
201
202
  	struct udevice *bus = dev->parent;
  	struct tegra20_sflash_priv *priv = dev_get_priv(bus);
  	struct spi_regs *regs = priv->regs;
9112ef8d8   Tom Warren   tegra2: spi: Add ...
203
204
205
206
207
  	u32 reg, tmpdout, tmpdin = 0;
  	const u8 *dout = data_out;
  	u8 *din = data_in;
  	int num_bytes;
  	int ret;
fda6fac39   Simon Glass   dm: tegra: spi: C...
208
209
210
  	debug("%s: slave %u:%u dout %p din %p bitlen %u
  ",
  	      __func__, bus->seq, spi_chip_select(dev), dout, din, bitlen);
9112ef8d8   Tom Warren   tegra2: spi: Add ...
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
  	if (bitlen % 8)
  		return -1;
  	num_bytes = bitlen / 8;
  
  	ret = 0;
  
  	reg = readl(&regs->status);
  	writel(reg, &regs->status);	/* Clear all SPI events via R/W */
  	debug("spi_xfer entry: STATUS = %08x
  ", reg);
  
  	reg = readl(&regs->command);
  	reg |= SPI_CMD_TXEN | SPI_CMD_RXEN;
  	writel(reg, &regs->command);
  	debug("spi_xfer: COMMAND = %08x
  ", readl(&regs->command));
  
  	if (flags & SPI_XFER_BEGIN)
fda6fac39   Simon Glass   dm: tegra: spi: C...
229
  		spi_cs_activate(dev);
9112ef8d8   Tom Warren   tegra2: spi: Add ...
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
  
  	/* handle data in 32-bit chunks */
  	while (num_bytes > 0) {
  		int bytes;
  		int is_read = 0;
  		int tm, i;
  
  		tmpdout = 0;
  		bytes = (num_bytes > 4) ?  4 : num_bytes;
  
  		if (dout != NULL) {
  			for (i = 0; i < bytes; ++i)
  				tmpdout = (tmpdout << 8) | dout[i];
  		}
  
  		num_bytes -= bytes;
  		if (dout)
  			dout += bytes;
  
  		clrsetbits_le32(&regs->command, SPI_CMD_BIT_LENGTH_MASK,
  				bytes * 8 - 1);
  		writel(tmpdout, &regs->tx_fifo);
  		setbits_le32(&regs->command, SPI_CMD_GO);
  
  		/*
  		 * Wait for SPI transmit FIFO to empty, or to time out.
  		 * The RX FIFO status will be read and cleared last
  		 */
  		for (tm = 0, is_read = 0; tm < SPI_TIMEOUT; ++tm) {
  			u32 status;
  
  			status = readl(&regs->status);
  
  			/* We can exit when we've had both RX and TX activity */
  			if (is_read && (status & SPI_STAT_TXF_EMPTY))
  				break;
  
  			if ((status & (SPI_STAT_BSY | SPI_STAT_RDY)) !=
  					SPI_STAT_RDY)
  				tm++;
  
  			else if (!(status & SPI_STAT_RXF_EMPTY)) {
  				tmpdin = readl(&regs->rx_fifo);
  				is_read = 1;
  
  				/* swap bytes read in */
  				if (din != NULL) {
  					for (i = bytes - 1; i >= 0; --i) {
  						din[i] = tmpdin & 0xff;
  						tmpdin >>= 8;
  					}
  					din += bytes;
  				}
  			}
  		}
  
  		if (tm >= SPI_TIMEOUT)
  			ret = tm;
  
  		/* clear ACK RDY, etc. bits */
  		writel(readl(&regs->status), &regs->status);
  	}
  
  	if (flags & SPI_XFER_END)
fda6fac39   Simon Glass   dm: tegra: spi: C...
294
  		spi_cs_deactivate(dev);
9112ef8d8   Tom Warren   tegra2: spi: Add ...
295
296
297
298
299
300
301
302
303
304
305
306
307
  
  	debug("spi_xfer: transfer ended. Value=%08x, status = %08x
  ",
  		tmpdin, readl(&regs->status));
  
  	if (ret) {
  		printf("spi_xfer: timeout during SPI transfer, tm %d
  ", ret);
  		return -1;
  	}
  
  	return 0;
  }
fda6fac39   Simon Glass   dm: tegra: spi: C...
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
  
  static int tegra20_sflash_set_speed(struct udevice *bus, uint speed)
  {
  	struct tegra_spi_platdata *plat = bus->platdata;
  	struct tegra20_sflash_priv *priv = dev_get_priv(bus);
  
  	if (speed > plat->frequency)
  		speed = plat->frequency;
  	priv->freq = speed;
  	debug("%s: regs=%p, speed=%d
  ", __func__, priv->regs, priv->freq);
  
  	return 0;
  }
  
  static int tegra20_sflash_set_mode(struct udevice *bus, uint mode)
  {
  	struct tegra20_sflash_priv *priv = dev_get_priv(bus);
  
  	priv->mode = mode;
  	debug("%s: regs=%p, mode=%d
  ", __func__, priv->regs, priv->mode);
  
  	return 0;
  }
  
  static const struct dm_spi_ops tegra20_sflash_ops = {
  	.claim_bus	= tegra20_sflash_claim_bus,
  	.xfer		= tegra20_sflash_xfer,
  	.set_speed	= tegra20_sflash_set_speed,
  	.set_mode	= tegra20_sflash_set_mode,
  	.cs_info	= tegra20_sflash_cs_info,
  };
  
  static const struct udevice_id tegra20_sflash_ids[] = {
  	{ .compatible = "nvidia,tegra20-sflash" },
  	{ }
  };
  
  U_BOOT_DRIVER(tegra20_sflash) = {
  	.name	= "tegra20_sflash",
  	.id	= UCLASS_SPI,
  	.of_match = tegra20_sflash_ids,
  	.ops	= &tegra20_sflash_ops,
  	.ofdata_to_platdata = tegra20_sflash_ofdata_to_platdata,
  	.platdata_auto_alloc_size = sizeof(struct tegra_spi_platdata),
  	.priv_auto_alloc_size = sizeof(struct tegra20_sflash_priv),
fda6fac39   Simon Glass   dm: tegra: spi: C...
355
356
  	.probe	= tegra20_sflash_probe,
  };