]>
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 | 111 | |
46122960 RB |
112 | #ifdef CONFIG_QSPI_QUAD_SUPPORT |
113 | memval |= (QSPI_CMD_READ_QUAD | QSPI_SETUP0_NUM_A_BYTES | | |
114 | QSPI_SETUP0_NUM_D_BYTES_8_BITS | | |
115 | QSPI_SETUP0_READ_QUAD | QSPI_CMD_WRITE | | |
116 | QSPI_NUM_DUMMY_BITS); | |
117 | #else | |
1d0933ea MP |
118 | memval |= QSPI_CMD_READ | QSPI_SETUP0_NUM_A_BYTES | |
119 | QSPI_SETUP0_NUM_D_BYTES_NO_BITS | | |
120 | QSPI_SETUP0_READ_NORMAL | QSPI_CMD_WRITE | | |
121 | QSPI_NUM_DUMMY_BITS; | |
46122960 | 122 | #endif |
1d0933ea MP |
123 | |
124 | writel(memval, &qslave->base->setup0); | |
125 | } | |
126 | ||
127 | static void ti_spi_set_speed(struct spi_slave *slave, uint hz) | |
128 | { | |
129 | struct ti_qspi_slave *qslave = to_ti_qspi_slave(slave); | |
130 | uint clk_div; | |
131 | ||
132 | debug("ti_spi_set_speed: hz: %d, clock divider %d\n", hz, clk_div); | |
133 | ||
134 | if (!hz) | |
135 | clk_div = 0; | |
136 | else | |
137 | clk_div = (QSPI_FCLK / hz) - 1; | |
138 | ||
139 | /* disable SCLK */ | |
140 | writel(readl(&qslave->base->clk_ctrl) & ~QSPI_CLK_EN, | |
141 | &qslave->base->clk_ctrl); | |
142 | ||
143 | /* assign clk_div values */ | |
144 | if (clk_div < 0) | |
145 | clk_div = 0; | |
146 | else if (clk_div > QSPI_CLK_DIV_MAX) | |
147 | clk_div = QSPI_CLK_DIV_MAX; | |
148 | ||
149 | /* enable SCLK */ | |
150 | writel(QSPI_CLK_EN | clk_div, &qslave->base->clk_ctrl); | |
151 | } | |
152 | ||
153 | int spi_cs_is_valid(unsigned int bus, unsigned int cs) | |
154 | { | |
155 | return 1; | |
156 | } | |
157 | ||
158 | void spi_cs_activate(struct spi_slave *slave) | |
159 | { | |
160 | /* CS handled in xfer */ | |
161 | return; | |
162 | } | |
163 | ||
164 | void spi_cs_deactivate(struct spi_slave *slave) | |
165 | { | |
166 | struct ti_qspi_slave *qslave = to_ti_qspi_slave(slave); | |
167 | ||
168 | debug("spi_cs_deactivate: 0x%08x\n", (u32)slave); | |
169 | ||
170 | writel(qslave->cmd | QSPI_INVAL, &qslave->base->cmd); | |
171 | } | |
172 | ||
173 | void spi_init(void) | |
174 | { | |
175 | /* nothing to do */ | |
176 | } | |
177 | ||
178 | struct spi_slave *spi_setup_slave(unsigned int bus, unsigned int cs, | |
179 | unsigned int max_hz, unsigned int mode) | |
180 | { | |
181 | struct ti_qspi_slave *qslave; | |
182 | ||
570533b8 SP |
183 | #ifdef CONFIG_AM43XX |
184 | gpio_request(CONFIG_QSPI_SEL_GPIO, "qspi_gpio"); | |
185 | gpio_direction_output(CONFIG_QSPI_SEL_GPIO, 1); | |
186 | #endif | |
187 | ||
1d0933ea MP |
188 | qslave = spi_alloc_slave(struct ti_qspi_slave, bus, cs); |
189 | if (!qslave) { | |
190 | printf("SPI_error: Fail to allocate ti_qspi_slave\n"); | |
191 | return NULL; | |
192 | } | |
193 | ||
194 | qslave->base = (struct ti_qspi_regs *)QSPI_BASE; | |
195 | qslave->mode = mode; | |
196 | ||
197 | ti_spi_set_speed(&qslave->slave, max_hz); | |
198 | ||
199 | #ifdef CONFIG_TI_SPI_MMAP | |
200 | ti_spi_setup_spi_register(qslave); | |
201 | #endif | |
202 | ||
203 | return &qslave->slave; | |
204 | } | |
205 | ||
206 | void spi_free_slave(struct spi_slave *slave) | |
207 | { | |
208 | struct ti_qspi_slave *qslave = to_ti_qspi_slave(slave); | |
209 | free(qslave); | |
210 | } | |
211 | ||
212 | int spi_claim_bus(struct spi_slave *slave) | |
213 | { | |
214 | struct ti_qspi_slave *qslave = to_ti_qspi_slave(slave); | |
215 | ||
216 | debug("spi_claim_bus: bus:%i cs:%i\n", slave->bus, slave->cs); | |
217 | ||
218 | qslave->dc = 0; | |
219 | if (qslave->mode & SPI_CPHA) | |
220 | qslave->dc |= QSPI_CKPHA(slave->cs); | |
221 | if (qslave->mode & SPI_CPOL) | |
222 | qslave->dc |= QSPI_CKPOL(slave->cs); | |
223 | if (qslave->mode & SPI_CS_HIGH) | |
224 | qslave->dc |= QSPI_CSPOL(slave->cs); | |
225 | ||
226 | writel(qslave->dc, &qslave->base->dc); | |
227 | writel(0, &qslave->base->cmd); | |
228 | writel(0, &qslave->base->data); | |
229 | ||
230 | return 0; | |
231 | } | |
232 | ||
233 | void spi_release_bus(struct spi_slave *slave) | |
234 | { | |
235 | struct ti_qspi_slave *qslave = to_ti_qspi_slave(slave); | |
236 | ||
237 | debug("spi_release_bus: bus:%i cs:%i\n", slave->bus, slave->cs); | |
238 | ||
239 | writel(0, &qslave->base->dc); | |
240 | writel(0, &qslave->base->cmd); | |
241 | writel(0, &qslave->base->data); | |
242 | } | |
243 | ||
244 | int spi_xfer(struct spi_slave *slave, unsigned int bitlen, const void *dout, | |
245 | void *din, unsigned long flags) | |
246 | { | |
247 | struct ti_qspi_slave *qslave = to_ti_qspi_slave(slave); | |
248 | uint words = bitlen >> 3; /* fixed 8-bit word length */ | |
249 | const uchar *txp = dout; | |
250 | uchar *rxp = din; | |
251 | uint status; | |
570533b8 SP |
252 | int timeout; |
253 | ||
d11ac4b5 | 254 | #if defined(CONFIG_DRA7XX) || defined(CONFIG_AM57XX) |
570533b8 SP |
255 | int val; |
256 | #endif | |
1d0933ea MP |
257 | |
258 | debug("spi_xfer: bus:%i cs:%i bitlen:%i words:%i flags:%lx\n", | |
259 | slave->bus, slave->cs, bitlen, words, flags); | |
260 | ||
261 | /* Setup mmap flags */ | |
262 | if (flags & SPI_XFER_MMAP) { | |
263 | writel(MM_SWITCH, &qslave->base->memswitch); | |
d11ac4b5 | 264 | #if defined(CONFIG_DRA7XX) || defined(CONFIG_AM57XX) |
1d0933ea MP |
265 | val = readl(CORE_CTRL_IO); |
266 | val |= MEM_CS; | |
267 | writel(val, CORE_CTRL_IO); | |
570533b8 | 268 | #endif |
1d0933ea MP |
269 | return 0; |
270 | } else if (flags & SPI_XFER_MMAP_END) { | |
271 | writel(~MM_SWITCH, &qslave->base->memswitch); | |
d11ac4b5 | 272 | #if defined(CONFIG_DRA7XX) || defined(CONFIG_AM57XX) |
1d0933ea MP |
273 | val = readl(CORE_CTRL_IO); |
274 | val &= MEM_CS_UNSELECT; | |
275 | writel(val, CORE_CTRL_IO); | |
570533b8 | 276 | #endif |
1d0933ea MP |
277 | return 0; |
278 | } | |
279 | ||
280 | if (bitlen == 0) | |
281 | return -1; | |
282 | ||
283 | if (bitlen % 8) { | |
284 | debug("spi_xfer: Non byte aligned SPI transfer\n"); | |
285 | return -1; | |
286 | } | |
287 | ||
288 | /* Setup command reg */ | |
289 | qslave->cmd = 0; | |
290 | qslave->cmd |= QSPI_WLEN(8); | |
291 | qslave->cmd |= QSPI_EN_CS(slave->cs); | |
292 | if (flags & SPI_3WIRE) | |
293 | qslave->cmd |= QSPI_3_PIN; | |
294 | qslave->cmd |= 0xfff; | |
295 | ||
bb7cd0dd SP |
296 | /* FIXME: This delay is required for successfull |
297 | * completion of read/write/erase. Once its root | |
298 | * caused, it will be remove from the driver. | |
299 | */ | |
300 | #ifdef CONFIG_AM43XX | |
301 | udelay(100); | |
302 | #endif | |
1d0933ea MP |
303 | while (words--) { |
304 | if (txp) { | |
305 | debug("tx cmd %08x dc %08x data %02x\n", | |
306 | qslave->cmd | QSPI_WR_SNGL, qslave->dc, *txp); | |
307 | writel(*txp++, &qslave->base->data); | |
308 | writel(qslave->cmd | QSPI_WR_SNGL, | |
309 | &qslave->base->cmd); | |
310 | status = readl(&qslave->base->status); | |
311 | timeout = QSPI_TIMEOUT; | |
312 | while ((status & QSPI_WC_BUSY) != QSPI_XFER_DONE) { | |
313 | if (--timeout < 0) { | |
314 | printf("spi_xfer: TX timeout!\n"); | |
315 | return -1; | |
316 | } | |
317 | status = readl(&qslave->base->status); | |
318 | } | |
319 | debug("tx done, status %08x\n", status); | |
320 | } | |
321 | if (rxp) { | |
322 | qslave->cmd |= QSPI_RD_SNGL; | |
323 | debug("rx cmd %08x dc %08x\n", | |
324 | qslave->cmd, qslave->dc); | |
b545a98f PS |
325 | #ifdef CONFIG_DRA7XX |
326 | udelay(500); | |
327 | #endif | |
1d0933ea MP |
328 | writel(qslave->cmd, &qslave->base->cmd); |
329 | status = readl(&qslave->base->status); | |
330 | timeout = QSPI_TIMEOUT; | |
331 | while ((status & QSPI_WC_BUSY) != QSPI_XFER_DONE) { | |
332 | if (--timeout < 0) { | |
333 | printf("spi_xfer: RX timeout!\n"); | |
334 | return -1; | |
335 | } | |
336 | status = readl(&qslave->base->status); | |
337 | } | |
338 | *rxp++ = readl(&qslave->base->data); | |
339 | debug("rx done, status %08x, read %02x\n", | |
340 | status, *(rxp-1)); | |
341 | } | |
342 | } | |
343 | ||
344 | /* Terminate frame */ | |
345 | if (flags & SPI_XFER_END) | |
346 | spi_cs_deactivate(slave); | |
347 | ||
348 | return 0; | |
349 | } |