]>
Commit | Line | Data |
---|---|---|
1d0933ea MP |
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> | |
570533b8 SP |
14 | #include <asm/gpio.h> |
15 | #include <asm/omap_gpio.h> | |
1d0933ea MP |
16 | |
17 | /* ti qpsi register bit masks */ | |
18 | #define QSPI_TIMEOUT 2000000 | |
19 | #define QSPI_FCLK 192000000 | |
20 | /* clock control */ | |
21 | #define QSPI_CLK_EN (1 << 31) | |
22 | #define QSPI_CLK_DIV_MAX 0xffff | |
23 | /* command */ | |
24 | #define QSPI_EN_CS(n) (n << 28) | |
25 | #define QSPI_WLEN(n) ((n-1) << 19) | |
26 | #define QSPI_3_PIN (1 << 18) | |
27 | #define QSPI_RD_SNGL (1 << 16) | |
28 | #define QSPI_WR_SNGL (2 << 16) | |
29 | #define QSPI_INVAL (4 << 16) | |
30 | #define QSPI_RD_QUAD (7 << 16) | |
31 | /* device control */ | |
32 | #define QSPI_DD(m, n) (m << (3 + n*8)) | |
33 | #define QSPI_CKPHA(n) (1 << (2 + n*8)) | |
34 | #define QSPI_CSPOL(n) (1 << (1 + n*8)) | |
35 | #define QSPI_CKPOL(n) (1 << (n*8)) | |
36 | /* status */ | |
37 | #define QSPI_WC (1 << 1) | |
38 | #define QSPI_BUSY (1 << 0) | |
39 | #define QSPI_WC_BUSY (QSPI_WC | QSPI_BUSY) | |
40 | #define QSPI_XFER_DONE QSPI_WC | |
41 | #define MM_SWITCH 0x01 | |
42 | #define MEM_CS 0x100 | |
43 | #define MEM_CS_UNSELECT 0xfffff0ff | |
570533b8 SP |
44 | #define MMAP_START_ADDR_DRA 0x5c000000 |
45 | #define MMAP_START_ADDR_AM43x 0x30000000 | |
1d0933ea MP |
46 | #define CORE_CTRL_IO 0x4a002558 |
47 | ||
48 | #define QSPI_CMD_READ (0x3 << 0) | |
49 | #define QSPI_CMD_READ_QUAD (0x6b << 0) | |
50 | #define QSPI_CMD_READ_FAST (0x0b << 0) | |
51 | #define QSPI_SETUP0_NUM_A_BYTES (0x2 << 8) | |
52 | #define QSPI_SETUP0_NUM_D_BYTES_NO_BITS (0x0 << 10) | |
53 | #define QSPI_SETUP0_NUM_D_BYTES_8_BITS (0x1 << 10) | |
54 | #define QSPI_SETUP0_READ_NORMAL (0x0 << 12) | |
55 | #define QSPI_SETUP0_READ_QUAD (0x3 << 12) | |
56 | #define QSPI_CMD_WRITE (0x2 << 16) | |
57 | #define QSPI_NUM_DUMMY_BITS (0x0 << 24) | |
58 | ||
59 | /* ti qspi register set */ | |
60 | struct ti_qspi_regs { | |
61 | u32 pid; | |
62 | u32 pad0[3]; | |
63 | u32 sysconfig; | |
64 | u32 pad1[3]; | |
65 | u32 int_stat_raw; | |
66 | u32 int_stat_en; | |
67 | u32 int_en_set; | |
68 | u32 int_en_ctlr; | |
69 | u32 intc_eoi; | |
70 | u32 pad2[3]; | |
71 | u32 clk_ctrl; | |
72 | u32 dc; | |
73 | u32 cmd; | |
74 | u32 status; | |
75 | u32 data; | |
76 | u32 setup0; | |
77 | u32 setup1; | |
78 | u32 setup2; | |
79 | u32 setup3; | |
80 | u32 memswitch; | |
81 | u32 data1; | |
82 | u32 data2; | |
83 | u32 data3; | |
84 | }; | |
85 | ||
86 | /* ti qspi slave */ | |
87 | struct ti_qspi_slave { | |
88 | struct spi_slave slave; | |
89 | struct ti_qspi_regs *base; | |
90 | unsigned int mode; | |
91 | u32 cmd; | |
92 | u32 dc; | |
93 | }; | |
94 | ||
95 | static inline struct ti_qspi_slave *to_ti_qspi_slave(struct spi_slave *slave) | |
96 | { | |
97 | return container_of(slave, struct ti_qspi_slave, slave); | |
98 | } | |
99 | ||
100 | static void ti_spi_setup_spi_register(struct ti_qspi_slave *qslave) | |
101 | { | |
102 | struct spi_slave *slave = &qslave->slave; | |
103 | u32 memval = 0; | |
104 | ||
d11ac4b5 | 105 | #if defined(CONFIG_DRA7XX) || defined(CONFIG_AM57XX) |
570533b8 SP |
106 | slave->memory_map = (void *)MMAP_START_ADDR_DRA; |
107 | #else | |
108 | slave->memory_map = (void *)MMAP_START_ADDR_AM43x; | |
ce3cc8ec | 109 | slave->op_mode_rx = 8; |
570533b8 | 110 | #endif |
1d0933ea MP |
111 | |
112 | memval |= QSPI_CMD_READ | QSPI_SETUP0_NUM_A_BYTES | | |
113 | QSPI_SETUP0_NUM_D_BYTES_NO_BITS | | |
114 | QSPI_SETUP0_READ_NORMAL | QSPI_CMD_WRITE | | |
115 | QSPI_NUM_DUMMY_BITS; | |
116 | ||
117 | writel(memval, &qslave->base->setup0); | |
118 | } | |
119 | ||
120 | static void ti_spi_set_speed(struct spi_slave *slave, uint hz) | |
121 | { | |
122 | struct ti_qspi_slave *qslave = to_ti_qspi_slave(slave); | |
123 | uint clk_div; | |
124 | ||
125 | debug("ti_spi_set_speed: hz: %d, clock divider %d\n", hz, clk_div); | |
126 | ||
127 | if (!hz) | |
128 | clk_div = 0; | |
129 | else | |
130 | clk_div = (QSPI_FCLK / hz) - 1; | |
131 | ||
132 | /* disable SCLK */ | |
133 | writel(readl(&qslave->base->clk_ctrl) & ~QSPI_CLK_EN, | |
134 | &qslave->base->clk_ctrl); | |
135 | ||
136 | /* assign clk_div values */ | |
137 | if (clk_div < 0) | |
138 | clk_div = 0; | |
139 | else if (clk_div > QSPI_CLK_DIV_MAX) | |
140 | clk_div = QSPI_CLK_DIV_MAX; | |
141 | ||
142 | /* enable SCLK */ | |
143 | writel(QSPI_CLK_EN | clk_div, &qslave->base->clk_ctrl); | |
144 | } | |
145 | ||
146 | int spi_cs_is_valid(unsigned int bus, unsigned int cs) | |
147 | { | |
148 | return 1; | |
149 | } | |
150 | ||
151 | void spi_cs_activate(struct spi_slave *slave) | |
152 | { | |
153 | /* CS handled in xfer */ | |
154 | return; | |
155 | } | |
156 | ||
157 | void spi_cs_deactivate(struct spi_slave *slave) | |
158 | { | |
159 | struct ti_qspi_slave *qslave = to_ti_qspi_slave(slave); | |
160 | ||
161 | debug("spi_cs_deactivate: 0x%08x\n", (u32)slave); | |
162 | ||
163 | writel(qslave->cmd | QSPI_INVAL, &qslave->base->cmd); | |
164 | } | |
165 | ||
166 | void spi_init(void) | |
167 | { | |
168 | /* nothing to do */ | |
169 | } | |
170 | ||
171 | struct spi_slave *spi_setup_slave(unsigned int bus, unsigned int cs, | |
172 | unsigned int max_hz, unsigned int mode) | |
173 | { | |
174 | struct ti_qspi_slave *qslave; | |
175 | ||
570533b8 SP |
176 | #ifdef CONFIG_AM43XX |
177 | gpio_request(CONFIG_QSPI_SEL_GPIO, "qspi_gpio"); | |
178 | gpio_direction_output(CONFIG_QSPI_SEL_GPIO, 1); | |
179 | #endif | |
180 | ||
1d0933ea MP |
181 | qslave = spi_alloc_slave(struct ti_qspi_slave, bus, cs); |
182 | if (!qslave) { | |
183 | printf("SPI_error: Fail to allocate ti_qspi_slave\n"); | |
184 | return NULL; | |
185 | } | |
186 | ||
187 | qslave->base = (struct ti_qspi_regs *)QSPI_BASE; | |
188 | qslave->mode = mode; | |
189 | ||
190 | ti_spi_set_speed(&qslave->slave, max_hz); | |
191 | ||
192 | #ifdef CONFIG_TI_SPI_MMAP | |
193 | ti_spi_setup_spi_register(qslave); | |
194 | #endif | |
195 | ||
196 | return &qslave->slave; | |
197 | } | |
198 | ||
199 | void spi_free_slave(struct spi_slave *slave) | |
200 | { | |
201 | struct ti_qspi_slave *qslave = to_ti_qspi_slave(slave); | |
202 | free(qslave); | |
203 | } | |
204 | ||
205 | int spi_claim_bus(struct spi_slave *slave) | |
206 | { | |
207 | struct ti_qspi_slave *qslave = to_ti_qspi_slave(slave); | |
208 | ||
209 | debug("spi_claim_bus: bus:%i cs:%i\n", slave->bus, slave->cs); | |
210 | ||
211 | qslave->dc = 0; | |
212 | if (qslave->mode & SPI_CPHA) | |
213 | qslave->dc |= QSPI_CKPHA(slave->cs); | |
214 | if (qslave->mode & SPI_CPOL) | |
215 | qslave->dc |= QSPI_CKPOL(slave->cs); | |
216 | if (qslave->mode & SPI_CS_HIGH) | |
217 | qslave->dc |= QSPI_CSPOL(slave->cs); | |
218 | ||
219 | writel(qslave->dc, &qslave->base->dc); | |
220 | writel(0, &qslave->base->cmd); | |
221 | writel(0, &qslave->base->data); | |
222 | ||
223 | return 0; | |
224 | } | |
225 | ||
226 | void spi_release_bus(struct spi_slave *slave) | |
227 | { | |
228 | struct ti_qspi_slave *qslave = to_ti_qspi_slave(slave); | |
229 | ||
230 | debug("spi_release_bus: bus:%i cs:%i\n", slave->bus, slave->cs); | |
231 | ||
232 | writel(0, &qslave->base->dc); | |
233 | writel(0, &qslave->base->cmd); | |
234 | writel(0, &qslave->base->data); | |
235 | } | |
236 | ||
237 | int spi_xfer(struct spi_slave *slave, unsigned int bitlen, const void *dout, | |
238 | void *din, unsigned long flags) | |
239 | { | |
240 | struct ti_qspi_slave *qslave = to_ti_qspi_slave(slave); | |
241 | uint words = bitlen >> 3; /* fixed 8-bit word length */ | |
242 | const uchar *txp = dout; | |
243 | uchar *rxp = din; | |
244 | uint status; | |
570533b8 SP |
245 | int timeout; |
246 | ||
d11ac4b5 | 247 | #if defined(CONFIG_DRA7XX) || defined(CONFIG_AM57XX) |
570533b8 SP |
248 | int val; |
249 | #endif | |
1d0933ea MP |
250 | |
251 | debug("spi_xfer: bus:%i cs:%i bitlen:%i words:%i flags:%lx\n", | |
252 | slave->bus, slave->cs, bitlen, words, flags); | |
253 | ||
254 | /* Setup mmap flags */ | |
255 | if (flags & SPI_XFER_MMAP) { | |
256 | writel(MM_SWITCH, &qslave->base->memswitch); | |
d11ac4b5 | 257 | #if defined(CONFIG_DRA7XX) || defined(CONFIG_AM57XX) |
1d0933ea MP |
258 | val = readl(CORE_CTRL_IO); |
259 | val |= MEM_CS; | |
260 | writel(val, CORE_CTRL_IO); | |
570533b8 | 261 | #endif |
1d0933ea MP |
262 | return 0; |
263 | } else if (flags & SPI_XFER_MMAP_END) { | |
264 | writel(~MM_SWITCH, &qslave->base->memswitch); | |
d11ac4b5 | 265 | #if defined(CONFIG_DRA7XX) || defined(CONFIG_AM57XX) |
1d0933ea MP |
266 | val = readl(CORE_CTRL_IO); |
267 | val &= MEM_CS_UNSELECT; | |
268 | writel(val, CORE_CTRL_IO); | |
570533b8 | 269 | #endif |
1d0933ea MP |
270 | return 0; |
271 | } | |
272 | ||
273 | if (bitlen == 0) | |
274 | return -1; | |
275 | ||
276 | if (bitlen % 8) { | |
277 | debug("spi_xfer: Non byte aligned SPI transfer\n"); | |
278 | return -1; | |
279 | } | |
280 | ||
281 | /* Setup command reg */ | |
282 | qslave->cmd = 0; | |
283 | qslave->cmd |= QSPI_WLEN(8); | |
284 | qslave->cmd |= QSPI_EN_CS(slave->cs); | |
285 | if (flags & SPI_3WIRE) | |
286 | qslave->cmd |= QSPI_3_PIN; | |
287 | qslave->cmd |= 0xfff; | |
288 | ||
bb7cd0dd SP |
289 | /* FIXME: This delay is required for successfull |
290 | * completion of read/write/erase. Once its root | |
291 | * caused, it will be remove from the driver. | |
292 | */ | |
293 | #ifdef CONFIG_AM43XX | |
294 | udelay(100); | |
295 | #endif | |
1d0933ea MP |
296 | while (words--) { |
297 | if (txp) { | |
298 | debug("tx cmd %08x dc %08x data %02x\n", | |
299 | qslave->cmd | QSPI_WR_SNGL, qslave->dc, *txp); | |
300 | writel(*txp++, &qslave->base->data); | |
301 | writel(qslave->cmd | QSPI_WR_SNGL, | |
302 | &qslave->base->cmd); | |
303 | status = readl(&qslave->base->status); | |
304 | timeout = QSPI_TIMEOUT; | |
305 | while ((status & QSPI_WC_BUSY) != QSPI_XFER_DONE) { | |
306 | if (--timeout < 0) { | |
307 | printf("spi_xfer: TX timeout!\n"); | |
308 | return -1; | |
309 | } | |
310 | status = readl(&qslave->base->status); | |
311 | } | |
312 | debug("tx done, status %08x\n", status); | |
313 | } | |
314 | if (rxp) { | |
315 | qslave->cmd |= QSPI_RD_SNGL; | |
316 | debug("rx cmd %08x dc %08x\n", | |
317 | qslave->cmd, qslave->dc); | |
b545a98f PS |
318 | #ifdef CONFIG_DRA7XX |
319 | udelay(500); | |
320 | #endif | |
1d0933ea MP |
321 | writel(qslave->cmd, &qslave->base->cmd); |
322 | status = readl(&qslave->base->status); | |
323 | timeout = QSPI_TIMEOUT; | |
324 | while ((status & QSPI_WC_BUSY) != QSPI_XFER_DONE) { | |
325 | if (--timeout < 0) { | |
326 | printf("spi_xfer: RX timeout!\n"); | |
327 | return -1; | |
328 | } | |
329 | status = readl(&qslave->base->status); | |
330 | } | |
331 | *rxp++ = readl(&qslave->base->data); | |
332 | debug("rx done, status %08x, read %02x\n", | |
333 | status, *(rxp-1)); | |
334 | } | |
335 | } | |
336 | ||
337 | /* Terminate frame */ | |
338 | if (flags & SPI_XFER_END) | |
339 | spi_cs_deactivate(slave); | |
340 | ||
341 | return 0; | |
342 | } |