Commit 9112ef8d89e3e496ba6f73276f17dd5c2b93877f
Committed by
Albert ARIBAUD
1 parent
1be0d75973
Exists in
master
and in
55 other branches
tegra2: spi: Add SPI driver for Tegra2 SOC
This driver supports SPI on Tegra2, running at 48MHz. Signed-off-by: Tom Warren <twarren@nvidia.com> Acked-by: Mike Frysinger <vapier@gentoo.org>
Showing 5 changed files with 347 additions and 0 deletions Side-by-side Diff
arch/arm/include/asm/arch-tegra2/tegra2.h
... | ... | @@ -38,6 +38,7 @@ |
38 | 38 | #define NV_PA_APB_UARTC_BASE (NV_PA_APB_MISC_BASE + 0x6200) |
39 | 39 | #define NV_PA_APB_UARTD_BASE (NV_PA_APB_MISC_BASE + 0x6300) |
40 | 40 | #define NV_PA_APB_UARTE_BASE (NV_PA_APB_MISC_BASE + 0x6400) |
41 | +#define TEGRA2_SPI_BASE (NV_PA_APB_MISC_BASE + 0xC380) | |
41 | 42 | #define NV_PA_PMC_BASE 0x7000E400 |
42 | 43 | #define NV_PA_CSITE_BASE 0x70040000 |
43 | 44 |
arch/arm/include/asm/arch-tegra2/tegra2_spi.h
1 | +/* | |
2 | + * NVIDIA Tegra2 SPI-FLASH controller | |
3 | + * | |
4 | + * Copyright 2010-2011 NVIDIA Corporation | |
5 | + * | |
6 | + * This software may be used and distributed according to the | |
7 | + * terms of the GNU Public License, Version 2, incorporated | |
8 | + * herein by reference. | |
9 | + * | |
10 | + * This program is free software; you can redistribute it and/or | |
11 | + * modify it under the terms of the GNU General Public License | |
12 | + * Version 2 as published by the Free Software Foundation. | |
13 | + * | |
14 | + * This program is distributed in the hope that it will be useful, | |
15 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
16 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
17 | + * GNU General Public License for more details. | |
18 | + * | |
19 | + * You should have received a copy of the GNU General Public License | |
20 | + * along with this program; if not, write to the Free Software | |
21 | + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, | |
22 | + * MA 02111-1307 USA | |
23 | + */ | |
24 | + | |
25 | +#ifndef _TEGRA2_SPI_H_ | |
26 | +#define _TEGRA2_SPI_H_ | |
27 | + | |
28 | +#include <asm/types.h> | |
29 | + | |
30 | +struct spi_tegra { | |
31 | + u32 command; /* SPI_COMMAND_0 register */ | |
32 | + u32 status; /* SPI_STATUS_0 register */ | |
33 | + u32 rx_cmp; /* SPI_RX_CMP_0 register */ | |
34 | + u32 dma_ctl; /* SPI_DMA_CTL_0 register */ | |
35 | + u32 tx_fifo; /* SPI_TX_FIFO_0 register */ | |
36 | + u32 rsvd[3]; /* offsets 0x14 to 0x1F reserved */ | |
37 | + u32 rx_fifo; /* SPI_RX_FIFO_0 register */ | |
38 | +}; | |
39 | + | |
40 | +#define SPI_CMD_GO (1 << 30) | |
41 | +#define SPI_CMD_ACTIVE_SCLK_SHIFT 26 | |
42 | +#define SPI_CMD_ACTIVE_SCLK_MASK (3 << SPI_CMD_ACTIVE_SCLK_SHIFT) | |
43 | +#define SPI_CMD_CK_SDA (1 << 21) | |
44 | +#define SPI_CMD_ACTIVE_SDA_SHIFT 18 | |
45 | +#define SPI_CMD_ACTIVE_SDA_MASK (3 << SPI_CMD_ACTIVE_SDA_SHIFT) | |
46 | +#define SPI_CMD_CS_POL (1 << 16) | |
47 | +#define SPI_CMD_TXEN (1 << 15) | |
48 | +#define SPI_CMD_RXEN (1 << 14) | |
49 | +#define SPI_CMD_CS_VAL (1 << 13) | |
50 | +#define SPI_CMD_CS_SOFT (1 << 12) | |
51 | +#define SPI_CMD_CS_DELAY (1 << 9) | |
52 | +#define SPI_CMD_CS3_EN (1 << 8) | |
53 | +#define SPI_CMD_CS2_EN (1 << 7) | |
54 | +#define SPI_CMD_CS1_EN (1 << 6) | |
55 | +#define SPI_CMD_CS0_EN (1 << 5) | |
56 | +#define SPI_CMD_BIT_LENGTH (1 << 4) | |
57 | +#define SPI_CMD_BIT_LENGTH_MASK 0x0000001F | |
58 | + | |
59 | +#define SPI_STAT_BSY (1 << 31) | |
60 | +#define SPI_STAT_RDY (1 << 30) | |
61 | +#define SPI_STAT_RXF_FLUSH (1 << 29) | |
62 | +#define SPI_STAT_TXF_FLUSH (1 << 28) | |
63 | +#define SPI_STAT_RXF_UNR (1 << 27) | |
64 | +#define SPI_STAT_TXF_OVF (1 << 26) | |
65 | +#define SPI_STAT_RXF_EMPTY (1 << 25) | |
66 | +#define SPI_STAT_RXF_FULL (1 << 24) | |
67 | +#define SPI_STAT_TXF_EMPTY (1 << 23) | |
68 | +#define SPI_STAT_TXF_FULL (1 << 22) | |
69 | +#define SPI_STAT_SEL_TXRX_N (1 << 16) | |
70 | +#define SPI_STAT_CUR_BLKCNT (1 << 15) | |
71 | + | |
72 | +#define SPI_TIMEOUT 1000 | |
73 | +#define TEGRA2_SPI_MAX_FREQ 52000000 | |
74 | + | |
75 | + | |
76 | +#endif /* _TEGRA2_SPI_H_ */ |
board/nvidia/common/board.c
... | ... | @@ -31,6 +31,7 @@ |
31 | 31 | #include <asm/arch/clock.h> |
32 | 32 | #include <asm/arch/pinmux.h> |
33 | 33 | #include <asm/arch/uart.h> |
34 | +#include <spi.h> | |
34 | 35 | #include "board.h" |
35 | 36 | |
36 | 37 | DECLARE_GLOBAL_DATA_PTR; |
... | ... | @@ -114,6 +115,9 @@ |
114 | 115 | clock_init(); |
115 | 116 | clock_verify(); |
116 | 117 | |
118 | +#ifdef CONFIG_TEGRA2_SPI | |
119 | + spi_init(); | |
120 | +#endif | |
117 | 121 | /* boot param addr */ |
118 | 122 | gd->bd->bi_boot_params = (NV_PA_SDRAM_BASE + 0x100); |
119 | 123 |
drivers/spi/Makefile
drivers/spi/tegra2_spi.c
1 | +/* | |
2 | + * Copyright (c) 2010-2011 NVIDIA Corporation | |
3 | + * With help from the mpc8xxx SPI driver | |
4 | + * With more help from omap3_spi SPI driver | |
5 | + * | |
6 | + * See file CREDITS for list of people who contributed to this | |
7 | + * project. | |
8 | + * | |
9 | + * This program is free software; you can redistribute it and/or | |
10 | + * modify it under the terms of the GNU General Public License as | |
11 | + * published by the Free Software Foundation; either version 2 of | |
12 | + * the License, or (at your option) any later version. | |
13 | + * | |
14 | + * This program is distributed in the hope that it will be useful, | |
15 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
16 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
17 | + * GNU General Public License for more details. | |
18 | + * | |
19 | + * You should have received a copy of the GNU General Public License | |
20 | + * along with this program; if not, write to the Free Software | |
21 | + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, | |
22 | + * MA 02111-1307 USA | |
23 | + */ | |
24 | + | |
25 | +#include <common.h> | |
26 | + | |
27 | +#include <malloc.h> | |
28 | +#include <spi.h> | |
29 | +#include <asm/io.h> | |
30 | +#include <asm/gpio.h> | |
31 | +#include <asm/arch/clk_rst.h> | |
32 | +#include <asm/arch/clock.h> | |
33 | +#include <asm/arch/pinmux.h> | |
34 | +#include <asm/arch/tegra2_spi.h> | |
35 | + | |
36 | +struct tegra_spi_slave { | |
37 | + struct spi_slave slave; | |
38 | + struct spi_tegra *regs; | |
39 | + unsigned int freq; | |
40 | + unsigned int mode; | |
41 | +}; | |
42 | + | |
43 | +static inline struct tegra_spi_slave *to_tegra_spi(struct spi_slave *slave) | |
44 | +{ | |
45 | + return container_of(slave, struct tegra_spi_slave, slave); | |
46 | +} | |
47 | + | |
48 | +int spi_cs_is_valid(unsigned int bus, unsigned int cs) | |
49 | +{ | |
50 | + /* Tegra2 SPI-Flash - only 1 device ('bus/cs') */ | |
51 | + if (bus != 0 || cs != 0) | |
52 | + return 0; | |
53 | + else | |
54 | + return 1; | |
55 | +} | |
56 | + | |
57 | +struct spi_slave *spi_setup_slave(unsigned int bus, unsigned int cs, | |
58 | + unsigned int max_hz, unsigned int mode) | |
59 | +{ | |
60 | + struct tegra_spi_slave *spi; | |
61 | + | |
62 | + if (!spi_cs_is_valid(bus, cs)) { | |
63 | + printf("SPI error: unsupported bus %d / chip select %d\n", | |
64 | + bus, cs); | |
65 | + return NULL; | |
66 | + } | |
67 | + | |
68 | + if (max_hz > TEGRA2_SPI_MAX_FREQ) { | |
69 | + printf("SPI error: unsupported frequency %d Hz. Max frequency" | |
70 | + " is %d Hz\n", max_hz, TEGRA2_SPI_MAX_FREQ); | |
71 | + return NULL; | |
72 | + } | |
73 | + | |
74 | + spi = malloc(sizeof(struct tegra_spi_slave)); | |
75 | + if (!spi) { | |
76 | + printf("SPI error: malloc of SPI structure failed\n"); | |
77 | + return NULL; | |
78 | + } | |
79 | + spi->slave.bus = bus; | |
80 | + spi->slave.cs = cs; | |
81 | + spi->freq = max_hz; | |
82 | + spi->regs = (struct spi_tegra *)TEGRA2_SPI_BASE; | |
83 | + spi->mode = mode; | |
84 | + | |
85 | + return &spi->slave; | |
86 | +} | |
87 | + | |
88 | +void spi_free_slave(struct spi_slave *slave) | |
89 | +{ | |
90 | + struct tegra_spi_slave *spi = to_tegra_spi(slave); | |
91 | + | |
92 | + free(spi); | |
93 | +} | |
94 | + | |
95 | +void spi_init(void) | |
96 | +{ | |
97 | + /* do nothing */ | |
98 | +} | |
99 | + | |
100 | +int spi_claim_bus(struct spi_slave *slave) | |
101 | +{ | |
102 | + struct tegra_spi_slave *spi = to_tegra_spi(slave); | |
103 | + struct spi_tegra *regs = spi->regs; | |
104 | + u32 reg; | |
105 | + | |
106 | + /* Change SPI clock to correct frequency, PLLP_OUT0 source */ | |
107 | + clock_start_periph_pll(PERIPH_ID_SPI1, CLOCK_ID_PERIPH, spi->freq); | |
108 | + | |
109 | + /* Clear stale status here */ | |
110 | + reg = SPI_STAT_RDY | SPI_STAT_RXF_FLUSH | SPI_STAT_TXF_FLUSH | \ | |
111 | + SPI_STAT_RXF_UNR | SPI_STAT_TXF_OVF; | |
112 | + writel(reg, ®s->status); | |
113 | + debug("spi_init: STATUS = %08x\n", readl(®s->status)); | |
114 | + | |
115 | + /* | |
116 | + * Use sw-controlled CS, so we can clock in data after ReadID, etc. | |
117 | + */ | |
118 | + reg = (spi->mode & 1) << SPI_CMD_ACTIVE_SDA_SHIFT; | |
119 | + if (spi->mode & 2) | |
120 | + reg |= 1 << SPI_CMD_ACTIVE_SCLK_SHIFT; | |
121 | + clrsetbits_le32(®s->command, SPI_CMD_ACTIVE_SCLK_MASK | | |
122 | + SPI_CMD_ACTIVE_SDA_MASK, SPI_CMD_CS_SOFT | reg); | |
123 | + debug("spi_init: COMMAND = %08x\n", readl(®s->command)); | |
124 | + | |
125 | + /* | |
126 | + * SPI pins on Tegra2 are muxed - change pinmux later due to UART | |
127 | + * issue. | |
128 | + */ | |
129 | + pinmux_set_func(PINGRP_GMD, PMUX_FUNC_SFLASH); | |
130 | + pinmux_tristate_disable(PINGRP_LSPI); | |
131 | + return 0; | |
132 | +} | |
133 | + | |
134 | +void spi_release_bus(struct spi_slave *slave) | |
135 | +{ | |
136 | + /* | |
137 | + * We can't release UART_DISABLE and set pinmux to UART4 here since | |
138 | + * some code (e,g, spi_flash_probe) uses printf() while the SPI | |
139 | + * bus is held. That is arguably bad, but it has the advantage of | |
140 | + * already being in the source tree. | |
141 | + */ | |
142 | +} | |
143 | + | |
144 | +void spi_cs_activate(struct spi_slave *slave) | |
145 | +{ | |
146 | + struct tegra_spi_slave *spi = to_tegra_spi(slave); | |
147 | + | |
148 | + /* CS is negated on Tegra, so drive a 1 to get a 0 */ | |
149 | + setbits_le32(&spi->regs->command, SPI_CMD_CS_VAL); | |
150 | +} | |
151 | + | |
152 | +void spi_cs_deactivate(struct spi_slave *slave) | |
153 | +{ | |
154 | + struct tegra_spi_slave *spi = to_tegra_spi(slave); | |
155 | + | |
156 | + /* CS is negated on Tegra, so drive a 0 to get a 1 */ | |
157 | + clrbits_le32(&spi->regs->command, SPI_CMD_CS_VAL); | |
158 | +} | |
159 | + | |
160 | +int spi_xfer(struct spi_slave *slave, unsigned int bitlen, | |
161 | + const void *data_out, void *data_in, unsigned long flags) | |
162 | +{ | |
163 | + struct tegra_spi_slave *spi = to_tegra_spi(slave); | |
164 | + struct spi_tegra *regs = spi->regs; | |
165 | + u32 reg, tmpdout, tmpdin = 0; | |
166 | + const u8 *dout = data_out; | |
167 | + u8 *din = data_in; | |
168 | + int num_bytes; | |
169 | + int ret; | |
170 | + | |
171 | + debug("spi_xfer: slave %u:%u dout %08X din %08X bitlen %u\n", | |
172 | + slave->bus, slave->cs, *(u8 *)dout, *(u8 *)din, bitlen); | |
173 | + if (bitlen % 8) | |
174 | + return -1; | |
175 | + num_bytes = bitlen / 8; | |
176 | + | |
177 | + ret = 0; | |
178 | + | |
179 | + reg = readl(®s->status); | |
180 | + writel(reg, ®s->status); /* Clear all SPI events via R/W */ | |
181 | + debug("spi_xfer entry: STATUS = %08x\n", reg); | |
182 | + | |
183 | + reg = readl(®s->command); | |
184 | + reg |= SPI_CMD_TXEN | SPI_CMD_RXEN; | |
185 | + writel(reg, ®s->command); | |
186 | + debug("spi_xfer: COMMAND = %08x\n", readl(®s->command)); | |
187 | + | |
188 | + if (flags & SPI_XFER_BEGIN) | |
189 | + spi_cs_activate(slave); | |
190 | + | |
191 | + /* handle data in 32-bit chunks */ | |
192 | + while (num_bytes > 0) { | |
193 | + int bytes; | |
194 | + int is_read = 0; | |
195 | + int tm, i; | |
196 | + | |
197 | + tmpdout = 0; | |
198 | + bytes = (num_bytes > 4) ? 4 : num_bytes; | |
199 | + | |
200 | + if (dout != NULL) { | |
201 | + for (i = 0; i < bytes; ++i) | |
202 | + tmpdout = (tmpdout << 8) | dout[i]; | |
203 | + } | |
204 | + | |
205 | + num_bytes -= bytes; | |
206 | + if (dout) | |
207 | + dout += bytes; | |
208 | + | |
209 | + clrsetbits_le32(®s->command, SPI_CMD_BIT_LENGTH_MASK, | |
210 | + bytes * 8 - 1); | |
211 | + writel(tmpdout, ®s->tx_fifo); | |
212 | + setbits_le32(®s->command, SPI_CMD_GO); | |
213 | + | |
214 | + /* | |
215 | + * Wait for SPI transmit FIFO to empty, or to time out. | |
216 | + * The RX FIFO status will be read and cleared last | |
217 | + */ | |
218 | + for (tm = 0, is_read = 0; tm < SPI_TIMEOUT; ++tm) { | |
219 | + u32 status; | |
220 | + | |
221 | + status = readl(®s->status); | |
222 | + | |
223 | + /* We can exit when we've had both RX and TX activity */ | |
224 | + if (is_read && (status & SPI_STAT_TXF_EMPTY)) | |
225 | + break; | |
226 | + | |
227 | + if ((status & (SPI_STAT_BSY | SPI_STAT_RDY)) != | |
228 | + SPI_STAT_RDY) | |
229 | + tm++; | |
230 | + | |
231 | + else if (!(status & SPI_STAT_RXF_EMPTY)) { | |
232 | + tmpdin = readl(®s->rx_fifo); | |
233 | + is_read = 1; | |
234 | + | |
235 | + /* swap bytes read in */ | |
236 | + if (din != NULL) { | |
237 | + for (i = bytes - 1; i >= 0; --i) { | |
238 | + din[i] = tmpdin & 0xff; | |
239 | + tmpdin >>= 8; | |
240 | + } | |
241 | + din += bytes; | |
242 | + } | |
243 | + } | |
244 | + } | |
245 | + | |
246 | + if (tm >= SPI_TIMEOUT) | |
247 | + ret = tm; | |
248 | + | |
249 | + /* clear ACK RDY, etc. bits */ | |
250 | + writel(readl(®s->status), ®s->status); | |
251 | + } | |
252 | + | |
253 | + if (flags & SPI_XFER_END) | |
254 | + spi_cs_deactivate(slave); | |
255 | + | |
256 | + debug("spi_xfer: transfer ended. Value=%08x, status = %08x\n", | |
257 | + tmpdin, readl(®s->status)); | |
258 | + | |
259 | + if (ret) { | |
260 | + printf("spi_xfer: timeout during SPI transfer, tm %d\n", ret); | |
261 | + return -1; | |
262 | + } | |
263 | + | |
264 | + return 0; | |
265 | +} |