Switchtec Userspace PROJECT_NUMBER = 3.1
Loading...
Searching...
No Matches
linux-uart.c
1/*
2 * Microsemi Switchtec(tm) PCIe Management Library
3 * Copyright (c) 2018, Microsemi Corporation
4 *
5 * Permission is hereby granted, free of charge, to any person obtaining a
6 * copy of this software and associated documentation files (the "Software"),
7 * to deal in the Software without restriction, including without limitation
8 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
9 * and/or sell copies of the Software, and to permit persons to whom the
10 * Software is furnished to do so, subject to the following conditions:
11 *
12 * The above copyright notice and this permission notice shall be included
13 * in all copies or substantial portions of the Software.
14 *
15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
16 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
18 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
19 * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
20 * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
21 * OTHER DEALINGS IN THE SOFTWARE.
22 *
23 */
24
25#ifdef __linux__
26
27#include "../switchtec_priv.h"
28#include "../crc.h"
29#include "switchtec/switchtec.h"
30#include "gasops.h"
31
32#include <sys/types.h>
33#include <sys/stat.h>
34#include <sys/ioctl.h>
35#include <sys/mman.h>
36#include <fcntl.h>
37#include <unistd.h>
38#include <errno.h>
39#include <signal.h>
40#include <stddef.h>
41#include <assert.h>
42#include <string.h>
43#include <stdarg.h>
44
45#include <sys/file.h>
46#include <termios.h>
47
48/*
49 * Example of uart operations
50 *
51 * GAS Write:
52 *
53 * command: gaswr -c -s <offset> 0x<byte str> <crc>
54 *
55 * case 1: success
56 * input: gaswr -c -s 0x5 0xaabbccddeeff 0x84
57 * output: gas_reg_write() success
58 * CRC: [0x84/0x84]
59 * 0x00000000:1212>
60 *
61 * case 2: success
62 * input: gaswr -c -s 0x135c10 0x00000008 0xbc
63 * output: [PFF] cs addr: 0x0304, not hit
64 * gas_reg_write() success
65 * CRC: [0xbc/0xbc]
66 * 0x00000000:2172>
67 *
68
69 * case 3: crc error
70 * input: gaswr -c -s 0x5 0xaabbccddeeff 0xb
71 * output: gas_reg_write() CRC Error
72 * CRC: [0x84/0x0b]
73 * 0x00000000:0000>
74 *
75 * case 4: out of range
76 * input: gaswr -c -s 0x5135c00 0x00000000 0xe9
77 * output: Error with gas_reg_write(): 0x63006, Offset:0x5135c00
78 * CRC:[0xe9/0xe9]
79 * 0x00000000:084d>
80 *
81 * GAS Read:
82 *
83 * command: gasrd -c -s <offset> <byte count>
84 *
85 * case 1: success
86 * input: gasrd -c -s 0x3 5
87 * output: gas_reg_read <0x3> [5 Byte]
88 * 00 58 00 00 00
89 * CRC: 0x37
90 * 0x00000000:1204>
91 *
92 * case 2: success
93 * input: gasrd -c -s 0x135c00 4
94 * output: gas_reg_read <0x135c00> [4 Byte]
95 * [PFF] cs addr: 0x0300,not hit
96 * 00 00 00 00
97 * CRC: 0xb6
98 * 0x00000000:0d93>
99 *
100 * case 3: out of range
101 * input: gasrd -c -s 0x5135c00 4
102 * output: gas_reg_read <0x5135c00> [4 Byte]
103 * No access beyond the Total GAS Section
104 * ...
105 * ...
106 * 0x00000000:0d93>
107 */
108
109struct switchtec_uart{
110 struct switchtec_dev dev;
111 int fd;
112};
113
114#define to_switchtec_uart(d) \
115 ((struct switchtec_uart *) \
116 ((char *)(d) - offsetof(struct switchtec_uart, dev)))
117
118#define UART_MAX_WRITE_BYTES 100
119#define UART_MAX_READ_BYTES 1024
120#define RETRY_NUM 3
121#define SWITCHTEC_UART_BAUDRATE (B230400)
122
123static int send_cmd(int fd, const char *fmt, int write_bytes, ...)
124{
125 int ret;
126 int i;
127 int cnt;
128 char cmd[1024];
129 uint8_t *write_data;
130 uint32_t write_crc;
131 va_list argp;
132
133 va_start(argp, write_bytes);
134
135 if (write_bytes) {
136 write_data = va_arg(argp, uint8_t *);
137 write_crc = va_arg(argp, uint32_t);
138 }
139
140 cnt = vsnprintf(cmd, sizeof(cmd), fmt, argp);
141
142 if (write_bytes) {
143 for (i = 0; i< write_bytes; i++) {
144 cnt += snprintf(cmd + cnt, sizeof(cmd) - cnt,
145 "%02x", write_data[write_bytes - 1 - i]);
146 }
147
148 cnt += snprintf(cmd + cnt, sizeof(cmd) - cnt,
149 " 0x%x\r", write_crc);
150 }
151
152 va_end(argp);
153
154 ret = write(fd, cmd, cnt);
155 if (ret < 0)
156 return ret;
157
158 if (ret != cnt) {
159 errno = EIO;
160 return -errno;
161 }
162
163 return 0;
164}
165
166static int read_resp_line(int fd, char *str)
167{
168 int ret;
169 int cnt = 0;
170
171 while(1) {
172 ret = read(fd, str + cnt, sizeof(str));
173 if (ret <= 0)
174 return ret;
175
176 cnt += ret;
177 str[cnt] = '\0';
178
179 /* Prompt "0x12345678:1234>" */
180 if (strrchr(str, ':') + 5 == strrchr(str, '>'))
181 return 0;
182 }
183
184 return -1;
185}
186
187static int cli_control(struct switchtec_dev *dev, const char *str)
188{
189 int ret;
190 char rtn[1024];
191 struct switchtec_uart *udev = to_switchtec_uart(dev);
192
193 ret = send_cmd(udev->fd, str, 0);
194 if (ret)
195 return ret;
196
197 ret = read_resp_line(udev->fd, rtn);
198 if (ret)
199 return ret;
200
201 return 0;
202}
203
204#ifdef __CHECKER__
205#define __force __attribute__((force))
206#else
207#define __force
208#endif
209
210static void uart_close(struct switchtec_dev *dev)
211{
212 struct switchtec_uart *udev = to_switchtec_uart(dev);
213 cli_control(dev, "echo 1\r");
214
215 if (dev->gas_map)
216 munmap((void __force *)dev->gas_map,
217 dev->gas_map_size);
218
219 flock(udev->fd, LOCK_UN);
220 close(udev->fd);
221 free(udev);
222}
223
224static int map_gas(struct switchtec_dev *dev)
225{
226 void *addr;
227 dev->gas_map_size = 4 << 20;
228
229 addr = mmap(NULL, dev->gas_map_size,
230 PROT_NONE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
231 if (addr == MAP_FAILED)
232 return -1;
233
234 dev->gas_map = (gasptr_t __force)addr;
235
236 return 0;
237}
238
239#undef __force
240
241static gasptr_t uart_gas_map(struct switchtec_dev *dev, int writeable,
242 size_t *map_size)
243{
244 if (map_size)
245 *map_size = dev->gas_map_size;
246
247 return dev->gas_map;
248}
249
250static void uart_gas_read(struct switchtec_dev *dev, void *dest,
251 const void __gas *src, size_t n)
252{
253 int ret;
254 int raddr, rnum, rcrc;
255 int i, j;
256 char *pos;
257 struct switchtec_uart *udev = to_switchtec_uart(dev);
258 uint32_t addr = (uint32_t)(src - (void __gas *)dev->gas_map);
259 uint8_t *ptr = dest;
260 uint8_t cal;
261 char gas_rd_rtn[4096];
262
263 for (i = 0; i < RETRY_NUM; i++) {
264 ret = send_cmd(udev->fd, "gasrd -c -s 0x%x %zu\r", 0, addr, n);
265 if (ret)
266 continue;
267
268 ret = read_resp_line(udev->fd, gas_rd_rtn);
269 if (ret)
270 continue;
271
272 /* case 3 */
273 if (strstr(gas_rd_rtn, "No access beyond the Total GAS Section")){
274 memset(dest, 0xff, n);
275 break;
276 }
277 /* case 2 */
278 if (strchr(gas_rd_rtn, ',')) {
279 if (sscanf(gas_rd_rtn,
280 "%*[^<]<0x%x> [%d Byte]%*[^,],%*[^:]: 0x"
281 "%x%*[^:]:",
282 &raddr, &rnum, &rcrc) != 3)
283 continue;
284 } else {
285 /* case 1 */
286 if (sscanf(gas_rd_rtn,
287 "%*[^<]<0x%x> [%d Byte]%*[^:]: 0x%x%*[^:]:",
288 &raddr, &rnum, &rcrc) != 3)
289 continue;
290 }
291
292 if ((raddr != addr) || (rnum != n))
293 continue;
294
295 pos = strchr(gas_rd_rtn, ']');
296 if (strchr(gas_rd_rtn, ','))
297 pos = strchr(gas_rd_rtn, ',') + strlen("not hit");
298 else
299 pos += 2;
300
301 for (j = 0; j < n; j++)
302 *ptr++ = strtol(pos, &pos, 16);
303
304 addr = htobe32(addr);
305 cal = crc8((uint8_t *)&addr, sizeof(addr), 0, true);
306 cal = crc8(dest, n, cal, false);
307 if (cal == rcrc)
308 break;
309 }
310
311 if (i == RETRY_NUM)
312 raise(SIGBUS);
313}
314
315static void uart_memcpy_from_gas(struct switchtec_dev *dev, void *dest,
316 const void __gas *src, size_t n)
317{
318 ssize_t cnt;
319
320 while(n) {
321 cnt = n > UART_MAX_READ_BYTES? UART_MAX_READ_BYTES : n;
322 uart_gas_read(dev, dest, src, cnt);
323 dest += cnt;
324 src += cnt;
325 n -= cnt;
326 }
327}
328
329#define create_gas_read(type, suffix) \
330 static type uart_gas_read ## suffix(struct switchtec_dev *dev, \
331 type __gas *addr) \
332 { \
333 type ret; \
334 uart_gas_read(dev, &ret, addr, sizeof(ret)); \
335 return ret; \
336 }
337create_gas_read(uint8_t, 8);
338create_gas_read(uint16_t, 16);
339create_gas_read(uint32_t, 32);
340create_gas_read(uint64_t, 64);
341
342static void uart_gas_write(struct switchtec_dev *dev, void __gas *dest,
343 const void *src, size_t n)
344{
345 int ret;
346 int i;
347 char gas_wr_rtn[4096];
348 uint32_t crc;
349 uint32_t cal, exp;
350 struct switchtec_uart *udev = to_switchtec_uart(dev);
351 uint32_t addr = (uint32_t)(dest - (void __gas *)dev->gas_map);
352
353 addr = htobe32(addr);
354 crc = crc8((uint8_t *)&addr, sizeof(addr), 0, true);
355 for (i = n; i > 0; i--)
356 crc = crc8((uint8_t *)src + i - 1, sizeof(uint8_t), crc, false);
357
358 addr = htobe32(addr);
359 for (i = 0; i < RETRY_NUM; i++) {
360 ret = send_cmd(udev->fd, "gaswr -c -s 0x%x 0x",
361 n, src, crc, addr);
362 if (ret)
363 continue;
364
365 ret = read_resp_line(udev->fd, gas_wr_rtn);
366 if (ret)
367 continue;
368
369 /* case 4 */
370 if (strstr(gas_wr_rtn, "Error with gas_reg_write()"))
371 break;
372 /* case 2 */
373 if (strchr(gas_wr_rtn, ',')) {
374 if (sscanf(gas_wr_rtn, "%*[^,],%*[^:]: [0x%x/0x%x]%*[^:]:",
375 &cal, &exp) != 2)
376 continue;
377 } else {
378 /* case 1 and case 3 */
379 if (sscanf(gas_wr_rtn, "%*[^:]: [0x%x/0x%x]%*[^:]:",
380 &cal, &exp) != 2)
381 continue;
382 }
383 if ((exp == cal) && (cal == crc))
384 break;
385 }
386
387 if (i == RETRY_NUM)
388 raise(SIGBUS);
389}
390
391static void uart_memcpy_to_gas(struct switchtec_dev *dev, void __gas *dest,
392 const void *src, size_t n)
393{
394 size_t cnt;
395
396 while(n){
397 cnt = n > UART_MAX_WRITE_BYTES ? UART_MAX_WRITE_BYTES : n;
398 uart_gas_write(dev, dest, src, cnt);
399 dest += cnt;
400 src += cnt;
401 n -= cnt;
402 }
403}
404
405#define create_gas_write(type, suffix) \
406 static void uart_gas_write ## suffix(struct switchtec_dev *dev, \
407 type val, type __gas *addr) \
408 { \
409 uart_gas_write(dev, addr, &val, sizeof(type)); \
410 }
411
412create_gas_write(uint8_t, 8);
413create_gas_write(uint16_t, 16);
414create_gas_write(uint32_t, 32);
415create_gas_write(uint64_t, 64);
416
417static ssize_t uart_write_from_gas(struct switchtec_dev *dev, int fd,
418 const void __gas *src, size_t n)
419{
420 ssize_t ret;
421 void *buf;
422
423 buf = malloc(n);
424
425 uart_memcpy_from_gas(dev, buf, src, n);
426 ret = write(fd, buf, n);
427
428 free(buf);
429
430 return ret;
431}
432
433static const struct switchtec_ops uart_ops = {
434 .flags = SWITCHTEC_OPS_FLAG_NO_MFG,
435
436 .close = uart_close,
437 .gas_map = uart_gas_map,
438
439 .cmd = gasop_cmd,
440 .get_device_id = gasop_get_device_id,
441 .get_fw_version = gasop_get_fw_version,
442 .pff_to_port = gasop_pff_to_port,
443 .port_to_pff = gasop_port_to_pff,
444 .flash_part = gasop_flash_part,
445 .event_summary = gasop_event_summary,
446 .event_ctl = gasop_event_ctl,
447 .event_wait_for = gasop_event_wait_for,
448
449 .gas_read8 = uart_gas_read8,
450 .gas_read16 = uart_gas_read16,
451 .gas_read32 = uart_gas_read32,
452 .gas_read64 = uart_gas_read64,
453 .gas_write8 = uart_gas_write8,
454 .gas_write16 = uart_gas_write16,
455 .gas_write32 = uart_gas_write32,
456 .gas_write32_no_retry = uart_gas_write32,
457 .gas_write64 = uart_gas_write64,
458
459 .memcpy_to_gas = uart_memcpy_to_gas,
460 .memcpy_from_gas = uart_memcpy_from_gas,
461 .write_from_gas = uart_write_from_gas,
462};
463
464static int set_uart_attribs(int fd, int speed, int parity)
465{
466 int ret;
467 struct termios uart_attribs;
468 memset(&uart_attribs, 0, sizeof(uart_attribs));
469
470 ret = tcgetattr(fd, &uart_attribs);
471 if (ret)
472 return -1;
473
474 cfsetospeed(&uart_attribs, speed);
475 cfsetispeed(&uart_attribs, speed);
476
477 uart_attribs.c_iflag &= ~IGNBRK;
478 uart_attribs.c_iflag &= ~(IXON | IXOFF | IXANY);
479 uart_attribs.c_lflag = 0;
480 uart_attribs.c_oflag = 0;
481 uart_attribs.c_cflag = (uart_attribs.c_cflag & ~CSIZE) | CS8;
482 uart_attribs.c_cflag |= (CLOCAL | CREAD);
483 uart_attribs.c_cflag &= ~(PARENB | PARODD);
484 uart_attribs.c_cflag |= parity;
485 uart_attribs.c_cflag &= ~CSTOPB;
486 uart_attribs.c_cflag &= ~CRTSCTS;
487 uart_attribs.c_cc[VMIN] = 0;
488 uart_attribs.c_cc[VTIME] = 50;
489
490 ret = tcsetattr(fd, TCSANOW, &uart_attribs);
491 if (ret)
492 return -1;
493
494 return 0;
495}
496
497struct switchtec_dev *switchtec_open_uart(int fd)
498{
499 int ret;
500 struct switchtec_uart *udev;
501
502 udev = malloc(sizeof(*udev));
503 if (!udev)
504 return NULL;
505
506 udev->fd = fd;
507 if (udev->fd < 0)
508 goto err_free;
509
510 ret = flock(udev->fd, LOCK_EX | LOCK_NB);
511 if (ret)
512 goto err_close_free;
513
514 ret = set_uart_attribs(udev->fd, SWITCHTEC_UART_BAUDRATE, 0);
515 if (ret)
516 goto err_close_free;
517
518 ret = cli_control(&udev->dev, "pscdbg 0 all\r");
519 if (ret)
520 goto err_close_free;
521
522 ret = cli_control(&udev->dev, "echo 0\r");
523 if (ret)
524 goto err_close_free;
525
526 if (map_gas(&udev->dev))
527 goto err_close_free;
528
529 udev->dev.ops = &uart_ops;
530 gasop_set_partition_info(&udev->dev);
531 return &udev->dev;
532
533err_close_free:
534 close(udev->fd);
535
536err_free:
537 free(udev);
538 return NULL;
539}
540
541#endif
542
struct switchtec_dev * switchtec_open_uart(int fd)
Open a switchtec device behind a uart device.
Main Switchtec header.
__gas struct switchtec_gas * gasptr_t
Shortform for a pointer to the GAS register space.
Definition switchtec.h:80