Commit 1d0933eaf976ba4f3ca00356f7124b1d12ddf168
Committed by
Jagannadha Sutradharudu Teki
1 parent
004f15b600
Exists in
master
and in
53 other branches
spi: add TI QSPI driver
Adds a SPI master driver for the TI QSPI peripheral. - Added quad read support. - Added memory mapped support. Signed-off-by: Matt Porter <matt.porter@linaro.org> Signed-off-by: Sourav Poddar <sourav.poddar@ti.com> Signed-off-by: Jagannadha Sutradharudu Teki <jaganna@xilinx.com>
Showing 2 changed files with 312 additions and 0 deletions Side-by-side Diff
drivers/spi/Makefile
... | ... | @@ -38,6 +38,7 @@ |
38 | 38 | COBJS-$(CONFIG_TEGRA20_SFLASH) += tegra20_sflash.o |
39 | 39 | COBJS-$(CONFIG_TEGRA20_SLINK) += tegra20_slink.o |
40 | 40 | COBJS-$(CONFIG_TEGRA114_SPI) += tegra114_spi.o |
41 | +COBJS-$(CONFIG_TI_QSPI) += ti_qspi.o | |
41 | 42 | COBJS-$(CONFIG_XILINX_SPI) += xilinx_spi.o |
42 | 43 | COBJS-$(CONFIG_ZYNQ_SPI) += zynq_spi.o |
43 | 44 |
drivers/spi/ti_qspi.c
1 | +/* | |
2 | + * TI QSPI driver | |
3 | + * | |
4 | + * Copyright (C) 2013, Texas Instruments, Incorporated | |
5 | + * | |
6 | + * SPDX-License-Identifier: GPL-2.0+ | |
7 | + */ | |
8 | + | |
9 | +#include <common.h> | |
10 | +#include <asm/io.h> | |
11 | +#include <asm/arch/omap.h> | |
12 | +#include <malloc.h> | |
13 | +#include <spi.h> | |
14 | + | |
15 | +/* ti qpsi register bit masks */ | |
16 | +#define QSPI_TIMEOUT 2000000 | |
17 | +#define QSPI_FCLK 192000000 | |
18 | +/* clock control */ | |
19 | +#define QSPI_CLK_EN (1 << 31) | |
20 | +#define QSPI_CLK_DIV_MAX 0xffff | |
21 | +/* command */ | |
22 | +#define QSPI_EN_CS(n) (n << 28) | |
23 | +#define QSPI_WLEN(n) ((n-1) << 19) | |
24 | +#define QSPI_3_PIN (1 << 18) | |
25 | +#define QSPI_RD_SNGL (1 << 16) | |
26 | +#define QSPI_WR_SNGL (2 << 16) | |
27 | +#define QSPI_INVAL (4 << 16) | |
28 | +#define QSPI_RD_QUAD (7 << 16) | |
29 | +/* device control */ | |
30 | +#define QSPI_DD(m, n) (m << (3 + n*8)) | |
31 | +#define QSPI_CKPHA(n) (1 << (2 + n*8)) | |
32 | +#define QSPI_CSPOL(n) (1 << (1 + n*8)) | |
33 | +#define QSPI_CKPOL(n) (1 << (n*8)) | |
34 | +/* status */ | |
35 | +#define QSPI_WC (1 << 1) | |
36 | +#define QSPI_BUSY (1 << 0) | |
37 | +#define QSPI_WC_BUSY (QSPI_WC | QSPI_BUSY) | |
38 | +#define QSPI_XFER_DONE QSPI_WC | |
39 | +#define MM_SWITCH 0x01 | |
40 | +#define MEM_CS 0x100 | |
41 | +#define MEM_CS_UNSELECT 0xfffff0ff | |
42 | +#define MMAP_START_ADDR 0x5c000000 | |
43 | +#define CORE_CTRL_IO 0x4a002558 | |
44 | + | |
45 | +#define QSPI_CMD_READ (0x3 << 0) | |
46 | +#define QSPI_CMD_READ_QUAD (0x6b << 0) | |
47 | +#define QSPI_CMD_READ_FAST (0x0b << 0) | |
48 | +#define QSPI_SETUP0_NUM_A_BYTES (0x2 << 8) | |
49 | +#define QSPI_SETUP0_NUM_D_BYTES_NO_BITS (0x0 << 10) | |
50 | +#define QSPI_SETUP0_NUM_D_BYTES_8_BITS (0x1 << 10) | |
51 | +#define QSPI_SETUP0_READ_NORMAL (0x0 << 12) | |
52 | +#define QSPI_SETUP0_READ_QUAD (0x3 << 12) | |
53 | +#define QSPI_CMD_WRITE (0x2 << 16) | |
54 | +#define QSPI_NUM_DUMMY_BITS (0x0 << 24) | |
55 | + | |
56 | +/* ti qspi register set */ | |
57 | +struct ti_qspi_regs { | |
58 | + u32 pid; | |
59 | + u32 pad0[3]; | |
60 | + u32 sysconfig; | |
61 | + u32 pad1[3]; | |
62 | + u32 int_stat_raw; | |
63 | + u32 int_stat_en; | |
64 | + u32 int_en_set; | |
65 | + u32 int_en_ctlr; | |
66 | + u32 intc_eoi; | |
67 | + u32 pad2[3]; | |
68 | + u32 clk_ctrl; | |
69 | + u32 dc; | |
70 | + u32 cmd; | |
71 | + u32 status; | |
72 | + u32 data; | |
73 | + u32 setup0; | |
74 | + u32 setup1; | |
75 | + u32 setup2; | |
76 | + u32 setup3; | |
77 | + u32 memswitch; | |
78 | + u32 data1; | |
79 | + u32 data2; | |
80 | + u32 data3; | |
81 | +}; | |
82 | + | |
83 | +/* ti qspi slave */ | |
84 | +struct ti_qspi_slave { | |
85 | + struct spi_slave slave; | |
86 | + struct ti_qspi_regs *base; | |
87 | + unsigned int mode; | |
88 | + u32 cmd; | |
89 | + u32 dc; | |
90 | +}; | |
91 | + | |
92 | +static inline struct ti_qspi_slave *to_ti_qspi_slave(struct spi_slave *slave) | |
93 | +{ | |
94 | + return container_of(slave, struct ti_qspi_slave, slave); | |
95 | +} | |
96 | + | |
97 | +static void ti_spi_setup_spi_register(struct ti_qspi_slave *qslave) | |
98 | +{ | |
99 | + struct spi_slave *slave = &qslave->slave; | |
100 | + u32 memval = 0; | |
101 | + | |
102 | + slave->memory_map = (void *)MMAP_START_ADDR; | |
103 | + | |
104 | + memval |= QSPI_CMD_READ | QSPI_SETUP0_NUM_A_BYTES | | |
105 | + QSPI_SETUP0_NUM_D_BYTES_NO_BITS | | |
106 | + QSPI_SETUP0_READ_NORMAL | QSPI_CMD_WRITE | | |
107 | + QSPI_NUM_DUMMY_BITS; | |
108 | + | |
109 | + writel(memval, &qslave->base->setup0); | |
110 | +} | |
111 | + | |
112 | +static void ti_spi_set_speed(struct spi_slave *slave, uint hz) | |
113 | +{ | |
114 | + struct ti_qspi_slave *qslave = to_ti_qspi_slave(slave); | |
115 | + uint clk_div; | |
116 | + | |
117 | + debug("ti_spi_set_speed: hz: %d, clock divider %d\n", hz, clk_div); | |
118 | + | |
119 | + if (!hz) | |
120 | + clk_div = 0; | |
121 | + else | |
122 | + clk_div = (QSPI_FCLK / hz) - 1; | |
123 | + | |
124 | + /* disable SCLK */ | |
125 | + writel(readl(&qslave->base->clk_ctrl) & ~QSPI_CLK_EN, | |
126 | + &qslave->base->clk_ctrl); | |
127 | + | |
128 | + /* assign clk_div values */ | |
129 | + if (clk_div < 0) | |
130 | + clk_div = 0; | |
131 | + else if (clk_div > QSPI_CLK_DIV_MAX) | |
132 | + clk_div = QSPI_CLK_DIV_MAX; | |
133 | + | |
134 | + /* enable SCLK */ | |
135 | + writel(QSPI_CLK_EN | clk_div, &qslave->base->clk_ctrl); | |
136 | +} | |
137 | + | |
138 | +int spi_cs_is_valid(unsigned int bus, unsigned int cs) | |
139 | +{ | |
140 | + return 1; | |
141 | +} | |
142 | + | |
143 | +void spi_cs_activate(struct spi_slave *slave) | |
144 | +{ | |
145 | + /* CS handled in xfer */ | |
146 | + return; | |
147 | +} | |
148 | + | |
149 | +void spi_cs_deactivate(struct spi_slave *slave) | |
150 | +{ | |
151 | + struct ti_qspi_slave *qslave = to_ti_qspi_slave(slave); | |
152 | + | |
153 | + debug("spi_cs_deactivate: 0x%08x\n", (u32)slave); | |
154 | + | |
155 | + writel(qslave->cmd | QSPI_INVAL, &qslave->base->cmd); | |
156 | +} | |
157 | + | |
158 | +void spi_init(void) | |
159 | +{ | |
160 | + /* nothing to do */ | |
161 | +} | |
162 | + | |
163 | +struct spi_slave *spi_setup_slave(unsigned int bus, unsigned int cs, | |
164 | + unsigned int max_hz, unsigned int mode) | |
165 | +{ | |
166 | + struct ti_qspi_slave *qslave; | |
167 | + | |
168 | + qslave = spi_alloc_slave(struct ti_qspi_slave, bus, cs); | |
169 | + if (!qslave) { | |
170 | + printf("SPI_error: Fail to allocate ti_qspi_slave\n"); | |
171 | + return NULL; | |
172 | + } | |
173 | + | |
174 | + qslave->base = (struct ti_qspi_regs *)QSPI_BASE; | |
175 | + qslave->mode = mode; | |
176 | + | |
177 | + ti_spi_set_speed(&qslave->slave, max_hz); | |
178 | + | |
179 | +#ifdef CONFIG_TI_SPI_MMAP | |
180 | + ti_spi_setup_spi_register(qslave); | |
181 | +#endif | |
182 | + | |
183 | + return &qslave->slave; | |
184 | +} | |
185 | + | |
186 | +void spi_free_slave(struct spi_slave *slave) | |
187 | +{ | |
188 | + struct ti_qspi_slave *qslave = to_ti_qspi_slave(slave); | |
189 | + free(qslave); | |
190 | +} | |
191 | + | |
192 | +int spi_claim_bus(struct spi_slave *slave) | |
193 | +{ | |
194 | + struct ti_qspi_slave *qslave = to_ti_qspi_slave(slave); | |
195 | + | |
196 | + debug("spi_claim_bus: bus:%i cs:%i\n", slave->bus, slave->cs); | |
197 | + | |
198 | + qslave->dc = 0; | |
199 | + if (qslave->mode & SPI_CPHA) | |
200 | + qslave->dc |= QSPI_CKPHA(slave->cs); | |
201 | + if (qslave->mode & SPI_CPOL) | |
202 | + qslave->dc |= QSPI_CKPOL(slave->cs); | |
203 | + if (qslave->mode & SPI_CS_HIGH) | |
204 | + qslave->dc |= QSPI_CSPOL(slave->cs); | |
205 | + | |
206 | + writel(qslave->dc, &qslave->base->dc); | |
207 | + writel(0, &qslave->base->cmd); | |
208 | + writel(0, &qslave->base->data); | |
209 | + | |
210 | + return 0; | |
211 | +} | |
212 | + | |
213 | +void spi_release_bus(struct spi_slave *slave) | |
214 | +{ | |
215 | + struct ti_qspi_slave *qslave = to_ti_qspi_slave(slave); | |
216 | + | |
217 | + debug("spi_release_bus: bus:%i cs:%i\n", slave->bus, slave->cs); | |
218 | + | |
219 | + writel(0, &qslave->base->dc); | |
220 | + writel(0, &qslave->base->cmd); | |
221 | + writel(0, &qslave->base->data); | |
222 | +} | |
223 | + | |
224 | +int spi_xfer(struct spi_slave *slave, unsigned int bitlen, const void *dout, | |
225 | + void *din, unsigned long flags) | |
226 | +{ | |
227 | + struct ti_qspi_slave *qslave = to_ti_qspi_slave(slave); | |
228 | + uint words = bitlen >> 3; /* fixed 8-bit word length */ | |
229 | + const uchar *txp = dout; | |
230 | + uchar *rxp = din; | |
231 | + uint status; | |
232 | + int timeout, val; | |
233 | + | |
234 | + debug("spi_xfer: bus:%i cs:%i bitlen:%i words:%i flags:%lx\n", | |
235 | + slave->bus, slave->cs, bitlen, words, flags); | |
236 | + | |
237 | + /* Setup mmap flags */ | |
238 | + if (flags & SPI_XFER_MMAP) { | |
239 | + writel(MM_SWITCH, &qslave->base->memswitch); | |
240 | + val = readl(CORE_CTRL_IO); | |
241 | + val |= MEM_CS; | |
242 | + writel(val, CORE_CTRL_IO); | |
243 | + return 0; | |
244 | + } else if (flags & SPI_XFER_MMAP_END) { | |
245 | + writel(~MM_SWITCH, &qslave->base->memswitch); | |
246 | + val = readl(CORE_CTRL_IO); | |
247 | + val &= MEM_CS_UNSELECT; | |
248 | + writel(val, CORE_CTRL_IO); | |
249 | + return 0; | |
250 | + } | |
251 | + | |
252 | + if (bitlen == 0) | |
253 | + return -1; | |
254 | + | |
255 | + if (bitlen % 8) { | |
256 | + debug("spi_xfer: Non byte aligned SPI transfer\n"); | |
257 | + return -1; | |
258 | + } | |
259 | + | |
260 | + /* Setup command reg */ | |
261 | + qslave->cmd = 0; | |
262 | + qslave->cmd |= QSPI_WLEN(8); | |
263 | + qslave->cmd |= QSPI_EN_CS(slave->cs); | |
264 | + if (flags & SPI_3WIRE) | |
265 | + qslave->cmd |= QSPI_3_PIN; | |
266 | + qslave->cmd |= 0xfff; | |
267 | + | |
268 | + while (words--) { | |
269 | + if (txp) { | |
270 | + debug("tx cmd %08x dc %08x data %02x\n", | |
271 | + qslave->cmd | QSPI_WR_SNGL, qslave->dc, *txp); | |
272 | + writel(*txp++, &qslave->base->data); | |
273 | + writel(qslave->cmd | QSPI_WR_SNGL, | |
274 | + &qslave->base->cmd); | |
275 | + status = readl(&qslave->base->status); | |
276 | + timeout = QSPI_TIMEOUT; | |
277 | + while ((status & QSPI_WC_BUSY) != QSPI_XFER_DONE) { | |
278 | + if (--timeout < 0) { | |
279 | + printf("spi_xfer: TX timeout!\n"); | |
280 | + return -1; | |
281 | + } | |
282 | + status = readl(&qslave->base->status); | |
283 | + } | |
284 | + debug("tx done, status %08x\n", status); | |
285 | + } | |
286 | + if (rxp) { | |
287 | + qslave->cmd |= QSPI_RD_SNGL; | |
288 | + debug("rx cmd %08x dc %08x\n", | |
289 | + qslave->cmd, qslave->dc); | |
290 | + writel(qslave->cmd, &qslave->base->cmd); | |
291 | + status = readl(&qslave->base->status); | |
292 | + timeout = QSPI_TIMEOUT; | |
293 | + while ((status & QSPI_WC_BUSY) != QSPI_XFER_DONE) { | |
294 | + if (--timeout < 0) { | |
295 | + printf("spi_xfer: RX timeout!\n"); | |
296 | + return -1; | |
297 | + } | |
298 | + status = readl(&qslave->base->status); | |
299 | + } | |
300 | + *rxp++ = readl(&qslave->base->data); | |
301 | + debug("rx done, status %08x, read %02x\n", | |
302 | + status, *(rxp-1)); | |
303 | + } | |
304 | + } | |
305 | + | |
306 | + /* Terminate frame */ | |
307 | + if (flags & SPI_XFER_END) | |
308 | + spi_cs_deactivate(slave); | |
309 | + | |
310 | + return 0; | |
311 | +} |