]>
Commit | Line | Data |
---|---|---|
9f95a23c TL |
1 | /* SPDX-License-Identifier: BSD-3-Clause |
2 | * Copyright(c) 2010-2019 Intel Corporation | |
3 | */ | |
4 | ||
5 | #include "opae_spi.h" | |
6 | #include "ifpga_compat.h" | |
7 | ||
8 | /*transaction opcodes*/ | |
9 | #define SPI_TRAN_SEQ_WRITE 0x04 /* SPI transaction sequential write */ | |
10 | #define SPI_TRAN_SEQ_READ 0x14 /* SPI transaction sequential read */ | |
11 | #define SPI_TRAN_NON_SEQ_WRITE 0x00 /* SPI transaction non-sequential write */ | |
12 | #define SPI_TRAN_NON_SEQ_READ 0x10 /* SPI transaction non-sequential read*/ | |
13 | ||
14 | /*specail packet characters*/ | |
15 | #define SPI_PACKET_SOP 0x7a | |
16 | #define SPI_PACKET_EOP 0x7b | |
17 | #define SPI_PACKET_CHANNEL 0x7c | |
18 | #define SPI_PACKET_ESC 0x7d | |
19 | ||
20 | /*special byte characters*/ | |
21 | #define SPI_BYTE_IDLE 0x4a | |
22 | #define SPI_BYTE_ESC 0x4d | |
23 | ||
24 | #define SPI_REG_BYTES 4 | |
25 | ||
26 | #define INIT_SPI_TRAN_HEADER(trans_type, size, address) \ | |
27 | ({ \ | |
28 | header.trans_type = trans_type; \ | |
29 | header.reserve = 0; \ | |
30 | header.size = cpu_to_be16(size); \ | |
31 | header.addr = cpu_to_be32(addr); \ | |
32 | }) | |
33 | ||
34 | #ifdef OPAE_SPI_DEBUG | |
35 | static void print_buffer(const char *string, void *buffer, int len) | |
36 | { | |
37 | int i; | |
38 | unsigned char *p = buffer; | |
39 | ||
40 | printf("%s print buffer, len=%d\n", string, len); | |
41 | ||
42 | for (i = 0; i < len; i++) | |
43 | printf("%x ", *(p+i)); | |
44 | printf("\n"); | |
45 | } | |
46 | #else | |
47 | static void print_buffer(const char *string, void *buffer, int len) | |
48 | { | |
49 | UNUSED(string); | |
50 | UNUSED(buffer); | |
51 | UNUSED(len); | |
52 | } | |
53 | #endif | |
54 | ||
55 | static unsigned char xor_20(unsigned char val) | |
56 | { | |
57 | return val^0x20; | |
58 | } | |
59 | ||
60 | static void reorder_phy_data(u8 bits_per_word, | |
61 | void *buf, unsigned int len) | |
62 | { | |
63 | unsigned int count = len / (bits_per_word/8); | |
64 | u32 *p; | |
65 | ||
66 | if (bits_per_word == 32) { | |
67 | p = (u32 *)buf; | |
68 | while (count--) { | |
69 | *p = cpu_to_be32(*p); | |
70 | p++; | |
71 | } | |
72 | } | |
73 | } | |
74 | ||
75 | enum { | |
76 | SPI_FOUND_SOP, | |
77 | SPI_FOUND_EOP, | |
78 | SPI_NOT_FOUND, | |
79 | }; | |
80 | ||
81 | static int resp_find_sop_eop(unsigned char *resp, unsigned int len, | |
82 | int flags) | |
83 | { | |
84 | int ret = SPI_NOT_FOUND; | |
85 | ||
86 | unsigned char *b = resp; | |
87 | ||
88 | /* find SOP */ | |
89 | if (flags != SPI_FOUND_SOP) { | |
90 | while (b < resp + len && *b != SPI_PACKET_SOP) | |
91 | b++; | |
92 | ||
93 | if (*b != SPI_PACKET_SOP) | |
94 | goto done; | |
95 | ||
96 | ret = SPI_FOUND_SOP; | |
97 | } | |
98 | ||
99 | /* find EOP */ | |
100 | while (b < resp + len && *b != SPI_PACKET_EOP) | |
101 | b++; | |
102 | ||
103 | if (*b != SPI_PACKET_EOP) | |
104 | goto done; | |
105 | ||
106 | ret = SPI_FOUND_EOP; | |
107 | ||
108 | done: | |
109 | return ret; | |
110 | } | |
111 | ||
112 | static int byte_to_core_convert(struct spi_transaction_dev *dev, | |
113 | unsigned int send_len, unsigned char *send_data, | |
114 | unsigned int resp_len, unsigned char *resp_data, | |
115 | unsigned int *valid_resp_len) | |
116 | { | |
117 | unsigned int i; | |
118 | int ret = 0; | |
119 | unsigned char *send_packet = dev->buffer->bytes_send; | |
120 | unsigned char *resp_packet = dev->buffer->bytes_resp; | |
121 | unsigned char *p; | |
122 | unsigned char current_byte; | |
123 | unsigned char *tx_buffer; | |
124 | unsigned int tx_len = 0; | |
125 | unsigned char *rx_buffer; | |
126 | unsigned int rx_len = 0; | |
127 | int retry = 0; | |
128 | int spi_flags; | |
129 | unsigned int resp_max_len = 2 * resp_len; | |
130 | ||
131 | print_buffer("before bytes:", send_data, send_len); | |
132 | ||
133 | p = send_packet; | |
134 | ||
135 | for (i = 0; i < send_len; i++) { | |
136 | current_byte = send_data[i]; | |
137 | switch (current_byte) { | |
138 | case SPI_BYTE_IDLE: | |
139 | *p++ = SPI_BYTE_IDLE; | |
140 | *p++ = xor_20(current_byte); | |
141 | break; | |
142 | case SPI_BYTE_ESC: | |
143 | *p++ = SPI_BYTE_ESC; | |
144 | *p++ = xor_20(current_byte); | |
145 | break; | |
146 | default: | |
147 | *p++ = current_byte; | |
148 | break; | |
149 | } | |
150 | } | |
151 | ||
152 | print_buffer("before spi:", send_packet, p-send_packet); | |
153 | ||
154 | reorder_phy_data(32, send_packet, p - send_packet); | |
155 | ||
156 | print_buffer("after order to spi:", send_packet, p-send_packet); | |
157 | ||
158 | /* call spi */ | |
159 | tx_buffer = send_packet; | |
160 | tx_len = p - send_packet; | |
161 | rx_buffer = resp_packet; | |
162 | rx_len = resp_max_len; | |
163 | spi_flags = SPI_NOT_FOUND; | |
164 | ||
165 | read_again: | |
166 | ret = spi_command(dev->dev, dev->chipselect, tx_len, tx_buffer, | |
167 | rx_len, rx_buffer); | |
168 | if (ret) | |
169 | return -EBUSY; | |
170 | ||
171 | print_buffer("read from spi:", rx_buffer, rx_len); | |
172 | ||
173 | /* look for SOP firstly*/ | |
174 | ret = resp_find_sop_eop(rx_buffer, rx_len - 1, spi_flags); | |
175 | if (ret != SPI_FOUND_EOP) { | |
176 | tx_buffer = NULL; | |
177 | tx_len = 0; | |
178 | if (retry++ > 10) { | |
179 | dev_err(NULL, "cannot found valid data from SPI\n"); | |
180 | return -EBUSY; | |
181 | } | |
182 | ||
183 | if (ret == SPI_FOUND_SOP) { | |
184 | rx_buffer += rx_len; | |
185 | resp_max_len += rx_len; | |
186 | } | |
187 | ||
188 | spi_flags = ret; | |
189 | goto read_again; | |
190 | } | |
191 | ||
192 | print_buffer("found valid data:", resp_packet, resp_max_len); | |
193 | ||
194 | /* analyze response packet */ | |
195 | i = 0; | |
196 | p = resp_data; | |
197 | while (i < resp_max_len) { | |
198 | current_byte = resp_packet[i]; | |
199 | switch (current_byte) { | |
200 | case SPI_BYTE_IDLE: | |
201 | i++; | |
202 | break; | |
203 | case SPI_BYTE_ESC: | |
204 | i++; | |
205 | current_byte = resp_packet[i]; | |
206 | *p++ = xor_20(current_byte); | |
207 | i++; | |
208 | break; | |
209 | default: | |
210 | *p++ = current_byte; | |
211 | i++; | |
212 | break; | |
213 | } | |
214 | } | |
215 | ||
216 | /* receive "4a" means the SPI is idle, not valid data */ | |
217 | *valid_resp_len = p - resp_data; | |
218 | if (*valid_resp_len == 0) { | |
219 | dev_err(NULL, "error: repond package without valid data\n"); | |
220 | return -EINVAL; | |
221 | } | |
222 | ||
223 | return 0; | |
224 | } | |
225 | ||
226 | static int packet_to_byte_conver(struct spi_transaction_dev *dev, | |
227 | unsigned int send_len, unsigned char *send_buf, | |
228 | unsigned int resp_len, unsigned char *resp_buf, | |
229 | unsigned int *valid) | |
230 | { | |
231 | int ret = 0; | |
232 | unsigned int i; | |
233 | unsigned char current_byte; | |
234 | unsigned int resp_max_len; | |
235 | unsigned char *send_packet = dev->buffer->packet_send; | |
236 | unsigned char *resp_packet = dev->buffer->packet_resp; | |
237 | unsigned char *p; | |
238 | unsigned int valid_resp_len = 0; | |
239 | ||
240 | print_buffer("before packet:", send_buf, send_len); | |
241 | ||
242 | resp_max_len = 2 * resp_len + 4; | |
243 | ||
244 | p = send_packet; | |
245 | ||
246 | /* SOP header */ | |
247 | *p++ = SPI_PACKET_SOP; | |
248 | ||
249 | *p++ = SPI_PACKET_CHANNEL; | |
250 | *p++ = 0; | |
251 | ||
252 | /* append the data into a packet */ | |
253 | for (i = 0; i < send_len; i++) { | |
254 | current_byte = send_buf[i]; | |
255 | ||
256 | /* EOP for last byte */ | |
257 | if (i == send_len - 1) | |
258 | *p++ = SPI_PACKET_EOP; | |
259 | ||
260 | switch (current_byte) { | |
261 | case SPI_PACKET_SOP: | |
262 | case SPI_PACKET_EOP: | |
263 | case SPI_PACKET_CHANNEL: | |
264 | case SPI_PACKET_ESC: | |
265 | *p++ = SPI_PACKET_ESC; | |
266 | *p++ = xor_20(current_byte); | |
267 | break; | |
268 | default: | |
269 | *p++ = current_byte; | |
270 | } | |
271 | } | |
272 | ||
273 | ret = byte_to_core_convert(dev, p - send_packet, | |
274 | send_packet, resp_max_len, resp_packet, | |
275 | &valid_resp_len); | |
276 | if (ret) | |
277 | return -EBUSY; | |
278 | ||
279 | print_buffer("after byte conver:", resp_packet, valid_resp_len); | |
280 | ||
281 | /* analyze the response packet */ | |
282 | p = resp_buf; | |
283 | ||
284 | /* look for SOP */ | |
285 | for (i = 0; i < valid_resp_len; i++) { | |
286 | if (resp_packet[i] == SPI_PACKET_SOP) | |
287 | break; | |
288 | } | |
289 | ||
290 | if (i == valid_resp_len) { | |
291 | dev_err(NULL, "error on analyze response packet 0x%x\n", | |
292 | resp_packet[i]); | |
293 | return -EINVAL; | |
294 | } | |
295 | ||
296 | i++; | |
297 | ||
298 | /* continue parsing data after SOP */ | |
299 | while (i < valid_resp_len) { | |
300 | current_byte = resp_packet[i]; | |
301 | ||
302 | switch (current_byte) { | |
303 | case SPI_PACKET_ESC: | |
304 | case SPI_PACKET_CHANNEL: | |
305 | case SPI_PACKET_SOP: | |
306 | i++; | |
307 | current_byte = resp_packet[i]; | |
308 | *p++ = xor_20(current_byte); | |
309 | i++; | |
310 | break; | |
311 | case SPI_PACKET_EOP: | |
312 | i++; | |
313 | current_byte = resp_packet[i]; | |
314 | if (current_byte == SPI_PACKET_ESC || | |
315 | current_byte == SPI_PACKET_CHANNEL || | |
316 | current_byte == SPI_PACKET_SOP) { | |
317 | i++; | |
318 | current_byte = resp_packet[i]; | |
319 | *p++ = xor_20(current_byte); | |
320 | } else | |
321 | *p++ = current_byte; | |
322 | i = valid_resp_len; | |
323 | break; | |
324 | default: | |
325 | *p++ = current_byte; | |
326 | i++; | |
327 | } | |
328 | ||
329 | } | |
330 | ||
331 | *valid = p - resp_buf; | |
332 | ||
333 | print_buffer("after packet:", resp_buf, *valid); | |
334 | ||
335 | return ret; | |
336 | } | |
337 | ||
338 | static int do_transaction(struct spi_transaction_dev *dev, unsigned int addr, | |
339 | unsigned int size, unsigned char *data, | |
340 | unsigned int trans_type) | |
341 | { | |
342 | ||
343 | struct spi_tran_header header; | |
344 | unsigned char *transaction = dev->buffer->tran_send; | |
345 | unsigned char *response = dev->buffer->tran_resp; | |
346 | unsigned char *p; | |
347 | int ret = 0; | |
348 | unsigned int i; | |
349 | unsigned int valid_len = 0; | |
350 | ||
351 | /* make transacation header */ | |
352 | INIT_SPI_TRAN_HEADER(trans_type, size, addr); | |
353 | ||
354 | /* fill the header */ | |
355 | p = transaction; | |
356 | opae_memcpy(p, &header, sizeof(struct spi_tran_header)); | |
357 | p = p + sizeof(struct spi_tran_header); | |
358 | ||
359 | switch (trans_type) { | |
360 | case SPI_TRAN_SEQ_WRITE: | |
361 | case SPI_TRAN_NON_SEQ_WRITE: | |
362 | for (i = 0; i < size; i++) | |
363 | *p++ = *data++; | |
364 | ||
365 | ret = packet_to_byte_conver(dev, size + HEADER_LEN, | |
366 | transaction, RESPONSE_LEN, response, | |
367 | &valid_len); | |
368 | if (ret) | |
369 | return -EBUSY; | |
370 | ||
371 | /* check the result */ | |
372 | if (size != ((unsigned int)(response[2] & 0xff) << 8 | | |
373 | (unsigned int)(response[3] & 0xff))) | |
374 | ret = -EBUSY; | |
375 | ||
376 | break; | |
377 | case SPI_TRAN_SEQ_READ: | |
378 | case SPI_TRAN_NON_SEQ_READ: | |
379 | ret = packet_to_byte_conver(dev, HEADER_LEN, | |
380 | transaction, size, response, | |
381 | &valid_len); | |
382 | if (ret || valid_len != size) | |
383 | return -EBUSY; | |
384 | ||
385 | for (i = 0; i < size; i++) | |
386 | *data++ = *response++; | |
387 | ||
388 | ret = 0; | |
389 | break; | |
390 | } | |
391 | ||
392 | return ret; | |
393 | } | |
394 | ||
395 | int spi_transaction_read(struct spi_transaction_dev *dev, unsigned int addr, | |
396 | unsigned int size, unsigned char *data) | |
397 | { | |
398 | return do_transaction(dev, addr, size, data, | |
399 | (size > SPI_REG_BYTES) ? | |
400 | SPI_TRAN_SEQ_READ : SPI_TRAN_NON_SEQ_READ); | |
401 | } | |
402 | ||
403 | int spi_transaction_write(struct spi_transaction_dev *dev, unsigned int addr, | |
404 | unsigned int size, unsigned char *data) | |
405 | { | |
406 | return do_transaction(dev, addr, size, data, | |
407 | (size > SPI_REG_BYTES) ? | |
408 | SPI_TRAN_SEQ_WRITE : SPI_TRAN_NON_SEQ_WRITE); | |
409 | } | |
410 | ||
411 | struct spi_transaction_dev *spi_transaction_init(struct altera_spi_device *dev, | |
412 | int chipselect) | |
413 | { | |
414 | struct spi_transaction_dev *spi_tran_dev; | |
415 | ||
416 | spi_tran_dev = opae_malloc(sizeof(struct spi_transaction_dev)); | |
417 | if (!spi_tran_dev) | |
418 | return NULL; | |
419 | ||
420 | spi_tran_dev->dev = dev; | |
421 | spi_tran_dev->chipselect = chipselect; | |
422 | ||
423 | spi_tran_dev->buffer = opae_malloc(sizeof(struct spi_tran_buffer)); | |
424 | if (!spi_tran_dev->buffer) { | |
425 | opae_free(spi_tran_dev); | |
426 | return NULL; | |
427 | } | |
428 | ||
429 | return spi_tran_dev; | |
430 | } | |
431 | ||
432 | void spi_transaction_remove(struct spi_transaction_dev *dev) | |
433 | { | |
434 | if (dev && dev->buffer) | |
435 | opae_free(dev->buffer); | |
436 | if (dev) | |
437 | opae_free(dev); | |
438 | } |