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