]>
Commit | Line | Data |
---|---|---|
7155cd2e | 1 | /* |
6720e4ae | 2 | * Andestech ATCSPI200 SPI controller driver. |
7155cd2e | 3 | * |
4 | * Copyright 2017 Andes Technology, Inc. | |
5 | * Author: Rick Chen (rick@andestech.com) | |
6 | * | |
7 | * SPDX-License-Identifier: GPL-2.0+ | |
8 | */ | |
9 | ||
10 | #include <clk.h> | |
11 | #include <common.h> | |
12 | #include <malloc.h> | |
13 | #include <spi.h> | |
14 | #include <asm/io.h> | |
15 | #include <dm.h> | |
16 | ||
17 | DECLARE_GLOBAL_DATA_PTR; | |
18 | ||
19 | #define MAX_TRANSFER_LEN 512 | |
20 | #define CHUNK_SIZE 1 | |
21 | #define SPI_TIMEOUT 0x100000 | |
22 | #define SPI0_BUS 0 | |
23 | #define SPI1_BUS 1 | |
24 | #define SPI0_BASE 0xf0b00000 | |
25 | #define SPI1_BASE 0xf0f00000 | |
26 | #define NSPI_MAX_CS_NUM 1 | |
27 | ||
6720e4ae | 28 | struct atcspi200_spi_regs { |
7155cd2e | 29 | u32 rev; |
30 | u32 reserve1[3]; | |
31 | u32 format; /* 0x10 */ | |
32 | #define DATA_LENGTH(x) ((x-1)<<8) | |
33 | u32 pio; | |
34 | u32 reserve2[2]; | |
35 | u32 tctrl; /* 0x20 */ | |
36 | #define TRAMODE_OFFSET 24 | |
37 | #define TRAMODE_MASK (0x0F<<TRAMODE_OFFSET) | |
38 | #define TRAMODE_WR_SYNC (0<<TRAMODE_OFFSET) | |
39 | #define TRAMODE_WO (1<<TRAMODE_OFFSET) | |
40 | #define TRAMODE_RO (2<<TRAMODE_OFFSET) | |
41 | #define TRAMODE_WR (3<<TRAMODE_OFFSET) | |
42 | #define TRAMODE_RW (4<<TRAMODE_OFFSET) | |
43 | #define TRAMODE_WDR (5<<TRAMODE_OFFSET) | |
44 | #define TRAMODE_RDW (6<<TRAMODE_OFFSET) | |
45 | #define TRAMODE_NONE (7<<TRAMODE_OFFSET) | |
46 | #define TRAMODE_DW (8<<TRAMODE_OFFSET) | |
47 | #define TRAMODE_DR (9<<TRAMODE_OFFSET) | |
48 | #define WCNT_OFFSET 12 | |
49 | #define WCNT_MASK (0x1FF<<WCNT_OFFSET) | |
50 | #define RCNT_OFFSET 0 | |
51 | #define RCNT_MASK (0x1FF<<RCNT_OFFSET) | |
52 | u32 cmd; | |
53 | u32 addr; | |
54 | u32 data; | |
55 | u32 ctrl; /* 0x30 */ | |
56 | #define TXFTH_OFFSET 16 | |
57 | #define RXFTH_OFFSET 8 | |
58 | #define TXDMAEN (1<<4) | |
59 | #define RXDMAEN (1<<3) | |
60 | #define TXFRST (1<<2) | |
61 | #define RXFRST (1<<1) | |
62 | #define SPIRST (1<<0) | |
63 | u32 status; | |
64 | #define TXFFL (1<<23) | |
65 | #define TXEPTY (1<<22) | |
66 | #define TXFVE_MASK (0x1F<<16) | |
67 | #define RXFEM (1<<14) | |
68 | #define RXFVE_OFFSET (8) | |
69 | #define RXFVE_MASK (0x1F<<RXFVE_OFFSET) | |
70 | #define SPIBSY (1<<0) | |
71 | u32 inten; | |
72 | u32 intsta; | |
73 | u32 timing; /* 0x40 */ | |
74 | #define SCLK_DIV_MASK 0xFF | |
75 | }; | |
76 | ||
77 | struct nds_spi_slave { | |
78 | #ifndef CONFIG_DM_SPI | |
79 | struct spi_slave slave; | |
80 | #endif | |
6720e4ae | 81 | volatile struct atcspi200_spi_regs *regs; |
7155cd2e | 82 | int to; |
83 | unsigned int freq; | |
84 | ulong clock; | |
85 | unsigned int mode; | |
86 | u8 num_cs; | |
87 | unsigned int mtiming; | |
88 | size_t cmd_len; | |
89 | u8 cmd_buf[16]; | |
90 | size_t data_len; | |
91 | size_t tran_len; | |
92 | u8 *din; | |
93 | u8 *dout; | |
94 | unsigned int max_transfer_length; | |
95 | }; | |
96 | ||
6720e4ae | 97 | static int __atcspi200_spi_set_speed(struct nds_spi_slave *ns) |
7155cd2e | 98 | { |
99 | u32 tm; | |
100 | u8 div; | |
101 | tm = ns->regs->timing; | |
102 | tm &= ~SCLK_DIV_MASK; | |
103 | ||
104 | if(ns->freq >= ns->clock) | |
105 | div =0xff; | |
106 | else{ | |
107 | for (div = 0; div < 0xff; div++) { | |
108 | if (ns->freq >= ns->clock / (2 * (div + 1))) | |
109 | break; | |
110 | } | |
111 | } | |
112 | ||
113 | tm |= div; | |
114 | ns->regs->timing = tm; | |
115 | ||
116 | return 0; | |
117 | ||
118 | } | |
119 | ||
6720e4ae | 120 | static int __atcspi200_spi_claim_bus(struct nds_spi_slave *ns) |
7155cd2e | 121 | { |
122 | unsigned int format=0; | |
123 | ns->regs->ctrl |= (TXFRST|RXFRST|SPIRST); | |
124 | while((ns->regs->ctrl &(TXFRST|RXFRST|SPIRST))&&(ns->to--)) | |
125 | if(!ns->to) | |
126 | return -EINVAL; | |
127 | ||
128 | ns->cmd_len = 0; | |
129 | format = ns->mode|DATA_LENGTH(8); | |
130 | ns->regs->format = format; | |
6720e4ae | 131 | __atcspi200_spi_set_speed(ns); |
7155cd2e | 132 | |
133 | return 0; | |
134 | } | |
135 | ||
6720e4ae | 136 | static int __atcspi200_spi_release_bus(struct nds_spi_slave *ns) |
7155cd2e | 137 | { |
138 | /* do nothing */ | |
139 | return 0; | |
140 | } | |
141 | ||
6720e4ae | 142 | static int __atcspi200_spi_start(struct nds_spi_slave *ns) |
7155cd2e | 143 | { |
144 | int i,olen=0; | |
145 | int tc = ns->regs->tctrl; | |
146 | ||
147 | tc &= ~(WCNT_MASK|RCNT_MASK|TRAMODE_MASK); | |
148 | if ((ns->din)&&(ns->cmd_len)) | |
149 | tc |= TRAMODE_WR; | |
150 | else if (ns->din) | |
151 | tc |= TRAMODE_RO; | |
152 | else | |
153 | tc |= TRAMODE_WO; | |
154 | ||
155 | if(ns->dout) | |
156 | olen = ns->tran_len; | |
157 | tc |= (ns->cmd_len+olen-1) << WCNT_OFFSET; | |
158 | ||
159 | if(ns->din) | |
160 | tc |= (ns->tran_len-1) << RCNT_OFFSET; | |
161 | ||
162 | ns->regs->tctrl = tc; | |
163 | ns->regs->cmd = 1; | |
164 | ||
165 | for (i=0;i<ns->cmd_len;i++) | |
166 | ns->regs->data = ns->cmd_buf[i]; | |
167 | ||
168 | return 0; | |
169 | } | |
170 | ||
6720e4ae | 171 | static int __atcspi200_spi_stop(struct nds_spi_slave *ns) |
7155cd2e | 172 | { |
173 | ns->regs->timing = ns->mtiming; | |
174 | while ((ns->regs->status & SPIBSY)&&(ns->to--)) | |
175 | if (!ns->to) | |
176 | return -EINVAL; | |
177 | ||
178 | return 0; | |
179 | } | |
180 | ||
181 | static void __nspi_espi_tx(struct nds_spi_slave *ns, const void *dout) | |
182 | { | |
183 | ns->regs->data = *(u8 *)dout; | |
184 | } | |
185 | ||
186 | static int __nspi_espi_rx(struct nds_spi_slave *ns, void *din, unsigned int bytes) | |
187 | { | |
188 | *(u8 *)din = ns->regs->data; | |
189 | return bytes; | |
190 | } | |
191 | ||
192 | ||
6720e4ae | 193 | static int __atcspi200_spi_xfer(struct nds_spi_slave *ns, |
7155cd2e | 194 | unsigned int bitlen, const void *data_out, void *data_in, |
195 | unsigned long flags) | |
196 | { | |
197 | unsigned int event, rx_bytes; | |
198 | const void *dout = NULL; | |
199 | void *din = NULL; | |
200 | int num_blks, num_chunks, max_tran_len, tran_len; | |
201 | int num_bytes; | |
202 | u8 *cmd_buf = ns->cmd_buf; | |
203 | size_t cmd_len = ns->cmd_len; | |
204 | size_t data_len = bitlen / 8; | |
205 | int rf_cnt; | |
206 | int ret = 0; | |
207 | ||
208 | max_tran_len = ns->max_transfer_length; | |
209 | switch (flags) { | |
210 | case SPI_XFER_BEGIN: | |
211 | cmd_len = ns->cmd_len = data_len; | |
212 | memcpy(cmd_buf, data_out, cmd_len); | |
213 | return 0; | |
214 | ||
215 | case 0: | |
216 | case SPI_XFER_END: | |
217 | if (bitlen == 0) { | |
218 | return 0; | |
219 | } | |
220 | ns->data_len = data_len; | |
221 | ns->din = (u8 *)data_in; | |
222 | ns->dout = (u8 *)data_out; | |
223 | break; | |
224 | ||
225 | case SPI_XFER_BEGIN | SPI_XFER_END: | |
226 | ns->data_len = 0; | |
227 | ns->din = 0; | |
228 | ns->dout = 0; | |
229 | cmd_len = ns->cmd_len = data_len; | |
230 | memcpy(cmd_buf, data_out, cmd_len); | |
231 | data_out = 0; | |
232 | data_len = 0; | |
6720e4ae | 233 | __atcspi200_spi_start(ns); |
7155cd2e | 234 | break; |
235 | } | |
236 | debug("spi_xfer: data_out %08X(%p) data_in %08X(%p) data_len %u\n", | |
237 | *(uint *)data_out, data_out, *(uint *)data_in, data_in, data_len); | |
238 | num_chunks = DIV_ROUND_UP(data_len, max_tran_len); | |
239 | din = data_in; | |
240 | dout = data_out; | |
241 | while (num_chunks--) { | |
242 | tran_len = min(data_len, (size_t)max_tran_len); | |
243 | ns->tran_len = tran_len; | |
244 | num_blks = DIV_ROUND_UP(tran_len , CHUNK_SIZE); | |
245 | num_bytes = (tran_len) % CHUNK_SIZE; | |
246 | if(num_bytes == 0) | |
247 | num_bytes = CHUNK_SIZE; | |
6720e4ae | 248 | __atcspi200_spi_start(ns); |
7155cd2e | 249 | |
250 | while (num_blks) { | |
251 | event = in_le32(&ns->regs->status); | |
252 | if ((event & TXEPTY) && (data_out)) { | |
253 | __nspi_espi_tx(ns, dout); | |
254 | num_blks -= CHUNK_SIZE; | |
255 | dout += CHUNK_SIZE; | |
256 | } | |
257 | ||
258 | if ((event & RXFVE_MASK) && (data_in)) { | |
259 | rf_cnt = ((event & RXFVE_MASK)>> RXFVE_OFFSET); | |
260 | if (rf_cnt >= CHUNK_SIZE) | |
261 | rx_bytes = CHUNK_SIZE; | |
262 | else if (num_blks == 1 && rf_cnt == num_bytes) | |
263 | rx_bytes = num_bytes; | |
264 | else | |
265 | continue; | |
266 | ||
267 | if (__nspi_espi_rx(ns, din, rx_bytes) == rx_bytes) { | |
268 | num_blks -= CHUNK_SIZE; | |
269 | din = (unsigned char *)din + rx_bytes; | |
270 | } | |
271 | } | |
272 | } | |
273 | ||
274 | data_len -= tran_len; | |
275 | if(data_len) | |
276 | { | |
277 | ns->cmd_buf[1] += ((tran_len>>16)&0xff); | |
278 | ns->cmd_buf[2] += ((tran_len>>8)&0xff); | |
279 | ns->cmd_buf[3] += ((tran_len)&0xff); | |
280 | ns->data_len = data_len; | |
281 | } | |
6720e4ae | 282 | ret = __atcspi200_spi_stop(ns); |
7155cd2e | 283 | } |
6720e4ae | 284 | ret = __atcspi200_spi_stop(ns); |
7155cd2e | 285 | |
286 | return ret; | |
287 | } | |
288 | ||
289 | #ifndef CONFIG_DM_SPI | |
290 | #define to_nds_spi_slave(s) container_of(s, struct nds_spi_slave, slave) | |
291 | struct spi_slave *spi_setup_slave(unsigned int bus, unsigned int cs, | |
292 | unsigned int max_hz, unsigned int mode) | |
293 | { | |
294 | struct nds_spi_slave *ns; | |
295 | ||
296 | if (!spi_cs_is_valid(bus, cs)) | |
297 | return NULL; | |
298 | ||
299 | ns = spi_alloc_slave(struct nds_spi_slave, bus, cs); | |
300 | ||
301 | switch (bus) { | |
302 | case SPI0_BUS: | |
6720e4ae | 303 | ns->regs = (struct atcspi200_spi_regs *)SPI0_BASE; |
7155cd2e | 304 | break; |
305 | ||
306 | case SPI1_BUS: | |
6720e4ae | 307 | ns->regs = (struct atcspi200_spi_regs *)SPI1_BASE; |
7155cd2e | 308 | break; |
309 | ||
310 | default: | |
311 | return NULL; | |
312 | } | |
313 | ||
314 | ns->freq= max_hz; | |
315 | ns->mode = mode; | |
316 | ns->to = SPI_TIMEOUT; | |
317 | ns->max_transfer_length = MAX_TRANSFER_LEN; | |
318 | ns->slave.max_write_size = MAX_TRANSFER_LEN; | |
319 | if (!ns) | |
320 | return NULL; | |
321 | ||
322 | return &ns->slave; | |
323 | } | |
324 | ||
325 | void spi_free_slave(struct spi_slave *slave) | |
326 | { | |
327 | struct nds_spi_slave *ns = to_nds_spi_slave(slave); | |
328 | free(ns); | |
329 | } | |
330 | ||
331 | void spi_init(void) | |
332 | { | |
333 | /* do nothing */ | |
334 | } | |
335 | ||
336 | int spi_claim_bus(struct spi_slave *slave) | |
337 | { | |
338 | struct nds_spi_slave *ns = to_nds_spi_slave(slave); | |
6720e4ae | 339 | return __atcspi200_spi_claim_bus(ns); |
7155cd2e | 340 | } |
341 | ||
342 | void spi_release_bus(struct spi_slave *slave) | |
343 | { | |
344 | struct nds_spi_slave *ns = to_nds_spi_slave(slave); | |
6720e4ae | 345 | __atcspi200_spi_release_bus(ns); |
7155cd2e | 346 | } |
347 | ||
348 | int spi_xfer(struct spi_slave *slave, unsigned int bitlen, const void *data_out, | |
349 | void *data_in, unsigned long flags) | |
350 | { | |
351 | struct nds_spi_slave *ns = to_nds_spi_slave(slave); | |
6720e4ae | 352 | return __atcspi200_spi_xfer(ns, bitlen, data_out, data_in, flags); |
7155cd2e | 353 | } |
354 | ||
355 | int spi_cs_is_valid(unsigned int bus, unsigned int cs) | |
356 | { | |
357 | return bus == 0 && cs < NSPI_MAX_CS_NUM; | |
358 | } | |
359 | ||
360 | void spi_cs_activate(struct spi_slave *slave) | |
361 | { | |
362 | struct nds_spi_slave *ns = to_nds_spi_slave(slave); | |
6720e4ae | 363 | __atcspi200_spi_start(ns); |
7155cd2e | 364 | } |
365 | ||
366 | void spi_cs_deactivate(struct spi_slave *slave) | |
367 | { | |
368 | struct nds_spi_slave *ns = to_nds_spi_slave(slave); | |
6720e4ae | 369 | __atcspi200_spi_stop(ns); |
7155cd2e | 370 | } |
371 | #else | |
6720e4ae | 372 | static int atcspi200_spi_set_speed(struct udevice *bus, uint max_hz) |
7155cd2e | 373 | { |
374 | struct nds_spi_slave *ns = dev_get_priv(bus); | |
375 | ||
376 | debug("%s speed %u\n", __func__, max_hz); | |
377 | ||
378 | ns->freq = max_hz; | |
6720e4ae | 379 | __atcspi200_spi_set_speed(ns); |
7155cd2e | 380 | |
381 | return 0; | |
382 | } | |
383 | ||
6720e4ae | 384 | static int atcspi200_spi_set_mode(struct udevice *bus, uint mode) |
7155cd2e | 385 | { |
386 | struct nds_spi_slave *ns = dev_get_priv(bus); | |
387 | ||
388 | debug("%s mode %u\n", __func__, mode); | |
389 | ns->mode = mode; | |
390 | ||
391 | return 0; | |
392 | } | |
393 | ||
6720e4ae | 394 | static int atcspi200_spi_claim_bus(struct udevice *dev) |
7155cd2e | 395 | { |
396 | struct dm_spi_slave_platdata *slave_plat = | |
397 | dev_get_parent_platdata(dev); | |
398 | struct udevice *bus = dev->parent; | |
399 | struct nds_spi_slave *ns = dev_get_priv(bus); | |
400 | ||
401 | if (slave_plat->cs >= ns->num_cs) { | |
402 | printf("Invalid SPI chipselect\n"); | |
403 | return -EINVAL; | |
404 | } | |
405 | ||
6720e4ae | 406 | return __atcspi200_spi_claim_bus(ns); |
7155cd2e | 407 | } |
408 | ||
6720e4ae | 409 | static int atcspi200_spi_release_bus(struct udevice *dev) |
7155cd2e | 410 | { |
411 | struct nds_spi_slave *ns = dev_get_priv(dev->parent); | |
412 | ||
6720e4ae | 413 | return __atcspi200_spi_release_bus(ns); |
7155cd2e | 414 | } |
415 | ||
6720e4ae | 416 | static int atcspi200_spi_xfer(struct udevice *dev, unsigned int bitlen, |
7155cd2e | 417 | const void *dout, void *din, |
418 | unsigned long flags) | |
419 | { | |
420 | struct udevice *bus = dev->parent; | |
421 | struct nds_spi_slave *ns = dev_get_priv(bus); | |
422 | ||
6720e4ae | 423 | return __atcspi200_spi_xfer(ns, bitlen, dout, din, flags); |
7155cd2e | 424 | } |
425 | ||
6720e4ae | 426 | static int atcspi200_spi_get_clk(struct udevice *bus) |
7155cd2e | 427 | { |
428 | struct nds_spi_slave *ns = dev_get_priv(bus); | |
429 | struct clk clk; | |
430 | ulong clk_rate; | |
431 | int ret; | |
432 | ||
433 | ret = clk_get_by_index(bus, 0, &clk); | |
434 | if (ret) | |
435 | return -EINVAL; | |
436 | ||
437 | clk_rate = clk_get_rate(&clk); | |
438 | if (!clk_rate) | |
439 | return -EINVAL; | |
440 | ||
441 | ns->clock = clk_rate; | |
442 | clk_free(&clk); | |
443 | ||
444 | return 0; | |
445 | } | |
446 | ||
6720e4ae | 447 | static int atcspi200_spi_probe(struct udevice *bus) |
7155cd2e | 448 | { |
449 | struct nds_spi_slave *ns = dev_get_priv(bus); | |
450 | ||
451 | ns->to = SPI_TIMEOUT; | |
452 | ns->max_transfer_length = MAX_TRANSFER_LEN; | |
453 | ns->mtiming = ns->regs->timing; | |
6720e4ae | 454 | atcspi200_spi_get_clk(bus); |
7155cd2e | 455 | |
456 | return 0; | |
457 | } | |
458 | ||
6720e4ae | 459 | static int atcspi200_ofdata_to_platadata(struct udevice *bus) |
7155cd2e | 460 | { |
461 | struct nds_spi_slave *ns = dev_get_priv(bus); | |
462 | const void *blob = gd->fdt_blob; | |
463 | int node = dev_of_offset(bus); | |
464 | ||
465 | ns->regs = map_physmem(devfdt_get_addr(bus), | |
6720e4ae | 466 | sizeof(struct atcspi200_spi_regs), |
7155cd2e | 467 | MAP_NOCACHE); |
468 | if (!ns->regs) { | |
469 | printf("%s: could not map device address\n", __func__); | |
470 | return -EINVAL; | |
471 | } | |
472 | ns->num_cs = fdtdec_get_int(blob, node, "num-cs", 4); | |
473 | ||
474 | return 0; | |
475 | } | |
476 | ||
6720e4ae RC |
477 | static const struct dm_spi_ops atcspi200_spi_ops = { |
478 | .claim_bus = atcspi200_spi_claim_bus, | |
479 | .release_bus = atcspi200_spi_release_bus, | |
480 | .xfer = atcspi200_spi_xfer, | |
481 | .set_speed = atcspi200_spi_set_speed, | |
482 | .set_mode = atcspi200_spi_set_mode, | |
7155cd2e | 483 | }; |
484 | ||
6720e4ae | 485 | static const struct udevice_id atcspi200_spi_ids[] = { |
7155cd2e | 486 | { .compatible = "andestech,atcspi200" }, |
487 | { } | |
488 | }; | |
489 | ||
6720e4ae RC |
490 | U_BOOT_DRIVER(atcspi200_spi) = { |
491 | .name = "atcspi200_spi", | |
7155cd2e | 492 | .id = UCLASS_SPI, |
6720e4ae RC |
493 | .of_match = atcspi200_spi_ids, |
494 | .ops = &atcspi200_spi_ops, | |
495 | .ofdata_to_platdata = atcspi200_ofdata_to_platadata, | |
7155cd2e | 496 | .priv_auto_alloc_size = sizeof(struct nds_spi_slave), |
6720e4ae | 497 | .probe = atcspi200_spi_probe, |
7155cd2e | 498 | }; |
499 | #endif |