Switchtec Userspace PROJECT_NUMBER = 4.0
Loading...
Searching...
No Matches
linux.c
1/*
2 * Microsemi Switchtec(tm) PCIe Management Library
3 * Copyright (c) 2017, 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#define SWITCHTEC_LIB_LINUX
28
29#include "../switchtec_priv.h"
30#include "switchtec/switchtec.h"
31#include "switchtec/pci.h"
32#include "switchtec/utils.h"
33#include "mmap_gas.h"
34#include "gasops.h"
35
36#include <linux/switchtec_ioctl.h>
37
38#include <unistd.h>
39#include <fcntl.h>
40#include <endian.h>
41#include <dirent.h>
42#include <libgen.h>
43#include <sys/stat.h>
44#include <sys/ioctl.h>
45#include <sys/mman.h>
46#include <sys/sysmacros.h>
47#include <glob.h>
48#include <poll.h>
49
50#include <errno.h>
51#include <string.h>
52#include <stddef.h>
53
54static const char *sys_path = "/sys/class/switchtec";
55
56struct switchtec_linux {
57 struct switchtec_dev dev;
58 int fd;
59};
60
61#define to_switchtec_linux(d) \
62 ((struct switchtec_linux *) \
63 ((char *)d - offsetof(struct switchtec_linux, dev)))
64
65const char *platform_strerror(void)
66{
67 return "Success";
68}
69
70static int dev_to_sysfs_path(struct switchtec_linux *ldev, const char *suffix,
71 char *buf, size_t buflen)
72{
73 int ret;
74 struct stat stat;
75
76 ret = fstat(ldev->fd, &stat);
77 if (ret < 0)
78 return ret;
79
80 snprintf(buf, buflen,
81 "/sys/dev/char/%d:%d/%s",
82 major(stat.st_rdev), minor(stat.st_rdev), suffix);
83
84 return 0;
85}
86
87static int sysfs_read_str(const char *path, char *buf, size_t buflen)
88{
89 int ret;
90 int fd;
91
92 fd = open(path, O_RDONLY);
93 if (fd < 0)
94 return -1;
95
96 ret = read(fd, buf, buflen);
97
98 close(fd);
99
100 return ret;
101}
102
103static long long sysfs_read_int(const char *path, int base)
104{
105 int ret;
106 char buf[64];
107
108 ret = sysfs_read_str(path, buf, sizeof(buf));
109 if (ret < 0)
110 return ret;
111
112 return strtoll(buf, NULL, base);
113}
114
115static int check_switchtec_device(struct switchtec_linux *ldev)
116{
117 int ret;
118 char syspath[PATH_MAX];
119
120 ret = dev_to_sysfs_path(ldev, "device/switchtec", syspath,
121 sizeof(syspath));
122 if (ret)
123 return ret;
124
125 ret = access(syspath, F_OK);
126 if (ret)
127 errno = ENOTTY;
128
129 return ret;
130}
131
132static int get_partition(struct switchtec_linux *ldev)
133{
134 int ret;
135 char syspath[PATH_MAX];
136
137 ret = dev_to_sysfs_path(ldev, "partition", syspath,
138 sizeof(syspath));
139 if (ret)
140 return ret;
141
142 ldev->dev.partition = sysfs_read_int(syspath, 10);
143 if (ldev->dev.partition < 0)
144 return ldev->dev.partition;
145
146 ret = dev_to_sysfs_path(ldev, "partition_count", syspath,
147 sizeof(syspath));
148 if (ret)
149 return ret;
150
151 ldev->dev.partition_count = sysfs_read_int(syspath, 10);
152 if (ldev->dev.partition_count < 1)
153 return -1;
154
155 return 0;
156}
157
158static void linux_close(struct switchtec_dev *dev)
159{
160 struct switchtec_linux *ldev = to_switchtec_linux(dev);
161
162 close(ldev->fd);
163 free(ldev);
164}
165
166static int scan_dev_filter(const struct dirent *d)
167{
168 if (d->d_name[0] == '.')
169 return 0;
170
171 return 1;
172}
173
174static void get_device_str(const char *path, const char *file,
175 char *buf, size_t buflen)
176{
177 char sysfs_path[PATH_MAX];
178 int ret;
179
180 snprintf(sysfs_path, sizeof(sysfs_path), "%s/%s",
181 path, file);
182
183 ret = sysfs_read_str(sysfs_path, buf, buflen);
184 if (ret < 0 || buf[0] == -1)
185 snprintf(buf, buflen, "unknown");
186
187 buf[strcspn(buf, "\n")] = 0;
188}
189
190static void get_fw_version(const char *path, char *buf, size_t buflen)
191{
192 char sysfs_path[PATH_MAX];
193 int fw_ver;
194 int ret;
195
196 ret = snprintf(sysfs_path, sizeof(sysfs_path), "%s/fw_version",
197 path);
198 if (ret >= sizeof(sysfs_path))
199 goto unknown_version;
200
201 fw_ver = sysfs_read_int(sysfs_path, 16);
202
203 if (fw_ver < 0)
204 goto unknown_version;
205
206 version_to_string(fw_ver, buf, buflen);
207 return;
208
209unknown_version:
210 snprintf(buf, buflen, "unknown");
211}
212
213int switchtec_list(struct switchtec_device_info **devlist)
214{
215 struct dirent **devices;
216 int i, n;
217 char link_path[PATH_MAX];
218 char pci_path[PATH_MAX] = "";
219 struct switchtec_device_info *dl;
220
221 n = scandir(sys_path, &devices, scan_dev_filter, alphasort);
222 if (n <= 0)
223 return n;
224
225 dl = *devlist = calloc(n, sizeof(struct switchtec_device_info));
226
227 if (!dl) {
228 for (i = 0; i < n; i++)
229 free(devices[i]);
230 free(devices);
231 errno = ENOMEM;
232 return -errno;
233 }
234
235 for (i = 0; i < n; i++) {
236 snprintf(dl[i].name, sizeof(dl[i].name),
237 "%s", devices[i]->d_name);
238 snprintf(dl[i].path, sizeof(dl[i].path),
239 "/dev/%s", devices[i]->d_name);
240
241 snprintf(link_path, sizeof(link_path), "%s/%s/device",
242 sys_path, devices[i]->d_name);
243
244 if (readlink(link_path, pci_path, sizeof(pci_path)) > 0)
245 snprintf(dl[i].pci_dev, sizeof(dl[i].pci_dev),
246 "%s", basename(pci_path));
247 else
248 snprintf(dl[i].pci_dev, sizeof(dl[i].pci_dev),
249 "unknown pci device");
250
251 snprintf(link_path, sizeof(link_path), "%s/%s",
252 sys_path, devices[i]->d_name);
253
254 get_device_str(link_path, "product_id", dl[i].product_id,
255 sizeof(dl[i].product_id));
256 get_device_str(link_path, "product_revision",
257 dl[i].product_rev, sizeof(dl[i].product_rev));
258 get_fw_version(link_path, dl[i].fw_version,
259 sizeof(dl[i].fw_version));
260
261 free(devices[i]);
262 }
263
264 free(devices);
265 return n;
266}
267
268static int linux_get_device_id(struct switchtec_dev *dev)
269{
270 int ret;
271 char link_path[PATH_MAX];
272 struct switchtec_linux *ldev = to_switchtec_linux(dev);
273
274 ret = dev_to_sysfs_path(ldev, "device/device", link_path,
275 sizeof(link_path));
276 if (ret)
277 return ret;
278
279 return sysfs_read_int(link_path, 16);
280}
281
282static int linux_get_fw_version(struct switchtec_dev *dev, char *buf,
283 size_t buflen)
284{
285 int ret;
286 long long version;
287 char syspath[PATH_MAX];
288 struct switchtec_linux *ldev = to_switchtec_linux(dev);
289
290 ret = dev_to_sysfs_path(ldev, "fw_version", syspath, sizeof(syspath));
291 if (ret)
292 return ret;
293
294 version = sysfs_read_int(syspath, 16);
295 if (version < 0)
296 return version;
297
298 version_to_string(version, buf, buflen);
299
300 return 0;
301}
302
303static int submit_cmd(struct switchtec_linux *ldev, uint32_t cmd,
304 const void *payload, size_t payload_len)
305{
306 int ret;
307 size_t bufsize = payload_len + sizeof(cmd);
308 char buf[bufsize];
309
310 cmd = htole32(cmd);
311 memcpy(buf, &cmd, sizeof(cmd));
312 memcpy(&buf[sizeof(cmd)], payload, payload_len);
313
314 ret = write(ldev->fd, buf, bufsize);
315
316 if (ret < 0)
317 return ret;
318
319 if (ret != bufsize) {
320 errno = EIO;
321 return -errno;
322 }
323
324 return 0;
325}
326
327static int read_resp(struct switchtec_linux *ldev, void *resp,
328 size_t resp_len)
329{
330 int32_t ret;
331 size_t bufsize = sizeof(uint32_t) + resp_len;
332 char buf[bufsize];
333
334 ret = read(ldev->fd, buf, bufsize);
335
336 if (ret < 0)
337 return ret;
338
339 if (ret != bufsize) {
340 errno = EIO;
341 return -errno;
342 }
343
344 memcpy(&ret, buf, sizeof(ret));
345 if (ret)
346 errno = ret;
347
348 if (!resp)
349 return ret;
350
351 memcpy(resp, &buf[sizeof(ret)], resp_len);
352
353 return ret;
354}
355
356static int linux_cmd(struct switchtec_dev *dev, uint32_t cmd,
357 const void *payload, size_t payload_len, void *resp,
358 size_t resp_len)
359{
360 int ret;
361 struct switchtec_linux *ldev = to_switchtec_linux(dev);
362
363retry:
364 ret = submit_cmd(ldev, cmd, payload, payload_len);
365 if (errno == EBADE) {
366 read_resp(ldev, NULL, 0);
367 errno = 0;
368 goto retry;
369 }
370
371 if (ret < 0)
372 return ret;
373
374 return read_resp(ldev, resp, resp_len);
375}
376
377static int get_class_devices(const char *searchpath,
378 struct switchtec_status *status)
379{
380 int i;
381 ssize_t len;
382 char syspath[PATH_MAX];
383 glob_t paths;
384 int found = 0;
385 const size_t MAX_LEN = 256;
386
387 snprintf(syspath, sizeof(syspath), "%s*/*/device", searchpath);
388 glob(syspath, 0, NULL, &paths);
389
390 for (i = 0; i < paths.gl_pathc; i++) {
391 char *p = paths.gl_pathv[i];
392
393 len = readlink(p, syspath, sizeof(syspath));
394 if (len <= 0)
395 continue;
396
397 p = dirname(p);
398
399 if (!status->class_devices) {
400 status->class_devices = calloc(MAX_LEN, 1);
401 strcpy(status->class_devices, basename(p));
402 } else {
403 len = strlen(status->class_devices);
404 snprintf(&status->class_devices[len], MAX_LEN - len,
405 ", %s", basename(p));
406 }
407
408 found = 1;
409 }
410
411 globfree(&paths);
412 return found;
413}
414
415static void get_port_bdf(const char *searchpath, int port,
416 struct switchtec_status *status)
417{
418 char syspath[PATH_MAX];
419 glob_t paths;
420 int ret;
421
422 ret = snprintf(syspath, sizeof(syspath), "%s/*:*:%02x.?",
423 searchpath, port);
424 if (ret >= sizeof(syspath))
425 return;
426
427 glob(syspath, 0, NULL, &paths);
428
429 if (paths.gl_pathc == 1)
430 status->pci_bdf = strdup(basename(paths.gl_pathv[0]));
431
432 globfree(&paths);
433}
434
435static void get_port_bdf_path(struct switchtec_status *status)
436{
437 char path[PATH_MAX];
438 char rpath[PATH_MAX];
439 int domain, bus, dev, fn;
440 int ptr = 0;
441 char *subpath;
442 int ret;
443
444 if (!status->pci_bdf)
445 return;
446
447 snprintf(path, sizeof(path), "/sys/bus/pci/devices/%s",
448 status->pci_bdf);
449
450 if (!realpath(path, rpath))
451 return;
452
453 subpath = strtok(rpath, "/");
454 while (subpath) {
455 ret = sscanf(subpath, "%x:%x:%x.%x", &domain, &bus, &dev, &fn);
456 if (ret == 4) {
457 if (ptr == 0)
458 ret = snprintf(path + ptr, sizeof(path) - ptr,
459 "%04x:%02x:%02x:%x/",
460 domain, bus, dev, fn);
461 else
462 ret = snprintf(path + ptr, sizeof(path) - ptr,
463 "%02x.%x/", dev, fn);
464
465 if (ret <= 0 || ret >= sizeof(path) - ptr)
466 break;
467
468 ptr += ret;
469 }
470 subpath = strtok(NULL, "/");
471 }
472
473 if (ptr)
474 path[ptr - 1] = 0;
475
476 status->pci_bdf_path = strdup(path);
477}
478
479static void get_port_info(struct switchtec_status *status)
480{
481 int i;
482 char syspath[PATH_MAX];
483 glob_t paths;
484
485 if (!status->pci_bdf)
486 return;
487
488 snprintf(syspath, sizeof(syspath), "/sys/bus/pci/devices/%s/*:*:*/",
489 status->pci_bdf);
490
491 glob(syspath, 0, NULL, &paths);
492
493 for (i = 0; i < paths.gl_pathc; i++) {
494 char *p = paths.gl_pathv[i];
495
496 snprintf(syspath, sizeof(syspath), "%s/vendor", p);
497 status->vendor_id = sysfs_read_int(syspath, 16);
498 if (status->vendor_id < 0)
499 continue;
500
501 snprintf(syspath, sizeof(syspath), "%s/device", p);
502 status->device_id = sysfs_read_int(syspath, 16);
503 if (status->device_id < 0)
504 continue;
505
506 if (get_class_devices(p, status)) {
507 if (status->pci_dev)
508 free(status->pci_dev);
509 status->pci_dev = strdup(basename(p));
510 }
511
512 if (!status->pci_dev)
513 status->pci_dev = strdup(basename(p));
514 }
515
516 globfree(&paths);
517}
518
519static void get_config_info(struct switchtec_status *status)
520{
521 int ret;
522 int fd;
523 char syspath[PATH_MAX];
524 uint32_t extcap;
525 int pos = PCI_EXT_CAP_OFFSET;
526 uint16_t acs;
527
528 snprintf(syspath, sizeof(syspath), "/sys/bus/pci/devices/%s/config",
529 status->pci_bdf);
530
531 fd = open(syspath, O_RDONLY);
532 if (fd < -1)
533 return;
534
535 while (1) {
536 ret = pread(fd, &extcap, sizeof(extcap), pos);
537 if (ret != sizeof(extcap) || !extcap)
538 goto close_and_exit;
539
540 if (PCI_EXT_CAP_ID(extcap) == PCI_EXT_CAP_ID_ACS)
541 break;
542
543 pos = PCI_EXT_CAP_NEXT(extcap);
544 if (pos < PCI_EXT_CAP_OFFSET)
545 goto close_and_exit;
546 }
547
548 ret = pread(fd, &acs, sizeof(acs), pos + PCI_ACS_CTRL);
549 if (ret != sizeof(acs))
550 goto close_and_exit;
551
552 status->acs_ctrl = acs;
553
554close_and_exit:
555 close(fd);
556}
557
558static int linux_get_devices(struct switchtec_dev *dev,
559 struct switchtec_status *status,
560 int ports)
561{
562 int ret;
563 int i;
564 int local_part;
565 char syspath[PATH_MAX];
566 char searchpath[PATH_MAX];
567 struct switchtec_linux *ldev = to_switchtec_linux(dev);
568
569 ret = dev_to_sysfs_path(ldev, "device", syspath,
570 sizeof(syspath));
571 if (ret)
572 return ret;
573
574 if (!realpath(syspath, searchpath)) {
575 errno = ENXIO;
576 return -errno;
577 }
578
579 //Replace eg "0000:03:00.1" into "0000:03:00.0"
580 searchpath[strlen(searchpath) - 1] = '0';
581
582 local_part = switchtec_partition(dev);
583
584 for (i = 0; i < ports; i++) {
585 if (status[i].port.partition != local_part)
586 continue;
587
588 if (status[i].port.upstream) {
589 status[i].pci_bdf = strdup(basename(searchpath));
590 get_port_bdf_path(&status[i]);
591 continue;
592 }
593
594 get_port_bdf(searchpath, status[i].port.log_id - 1, &status[i]);
595 get_port_bdf_path(&status[i]);
596 get_port_info(&status[i]);
597 get_config_info(&status[i]);
598 }
599
600 return 0;
601}
602
603static int linux_pff_to_port(struct switchtec_dev *dev, int pff,
604 int *partition, int *port)
605{
606 int ret;
607 struct switchtec_ioctl_pff_port p;
608 struct switchtec_linux *ldev = to_switchtec_linux(dev);
609
610 p.pff = pff;
611 ret = ioctl(ldev->fd, SWITCHTEC_IOCTL_PFF_TO_PORT, &p);
612 if (ret)
613 return ret;
614
615 if (partition)
616 *partition = p.partition;
617 if (port)
618 *port = p.port;
619
620 return 0;
621}
622
623static int linux_port_to_pff(struct switchtec_dev *dev, int partition,
624 int port, int *pff)
625{
626 int ret;
627 struct switchtec_ioctl_pff_port p;
628 struct switchtec_linux *ldev = to_switchtec_linux(dev);
629
630 p.port = port;
631 p.partition = partition;
632
633 ret = ioctl(ldev->fd, SWITCHTEC_IOCTL_PORT_TO_PFF, &p);
634 if (ret)
635 return ret;
636
637 if (pff)
638 *pff = p.pff;
639
640 return 0;
641}
642
643#ifdef __CHECKER__
644#define __force __attribute__((force))
645#else
646#define __force
647#endif
648
649static ssize_t resource_size(struct switchtec_linux *ldev, const char *fname)
650{
651 char respath[PATH_MAX];
652 struct stat stat;
653 int fd, ret;
654
655 ret = dev_to_sysfs_path(ldev, fname, respath,
656 sizeof(respath));
657 if (ret) {
658 errno = ret;
659 return -1;
660 }
661
662 fd = open(respath, O_RDONLY);
663 if (fd < 0)
664 return -1;
665
666 ret = fstat(fd, &stat);
667 if (ret < 0) {
668 close(fd);
669 return -1;
670 }
671
672 close(fd);
673 return stat.st_size;
674}
675
676static int mmap_resource(struct switchtec_linux *ldev, const char *fname,
677 void *addr, size_t offset, size_t size, int writeable)
678{
679 char respath[PATH_MAX];
680 void *map;
681 int fd, ret = 0;
682
683 ret = dev_to_sysfs_path(ldev, fname, respath,
684 sizeof(respath));
685 if (ret) {
686 errno = ret;
687 return -1;
688 }
689
690 fd = open(respath, writeable ? O_RDWR : O_RDONLY);
691 if (fd < 0)
692 return -1;
693
694 map = mmap(addr, size, (writeable ? PROT_WRITE : 0) | PROT_READ,
695 MAP_SHARED | MAP_FIXED, fd, offset);
696 if (map == MAP_FAILED)
697 ret = -1;
698
699 close(fd);
700 return ret;
701}
702
703/*
704 * GAS map maps the hardware registers into user memory space.
705 * Needless to say, this can be very dangerous and should only
706 * be done if you know what you are doing. Any register accesses
707 * that use this will remain unsupported by Microsemi unless it's
708 * done within the switchtec user project or otherwise specified.
709 */
710static gasptr_t linux_gas_map(struct switchtec_dev *dev, int writeable,
711 size_t *map_size)
712{
713 int ret;
714 void *map;
715 ssize_t msize;
716 struct switchtec_linux *ldev = to_switchtec_linux(dev);
717
718 msize = resource_size(ldev, "device/resource0");
719 if (msize <= 0)
720 return SWITCHTEC_MAP_FAILED;
721
722 /*
723 * Reserve virtual address space for the entire GAS mapping.
724 */
725 map = mmap(NULL, msize, PROT_NONE,
726 MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
727 if (map == MAP_FAILED)
728 return SWITCHTEC_MAP_FAILED;
729
730 ret = mmap_resource(ldev, "device/resource0_wc", map, 0,
731 SWITCHTEC_GAS_TOP_CFG_OFFSET, writeable);
732 if (ret) {
733 ret = mmap_resource(ldev, "device/resource0", map, 0,
734 SWITCHTEC_GAS_TOP_CFG_OFFSET,
735 writeable);
736 if (ret)
737 goto unmap_and_exit;
738 }
739
740 ret = mmap_resource(ldev, "device/resource0",
741 map + SWITCHTEC_GAS_TOP_CFG_OFFSET,
742 SWITCHTEC_GAS_TOP_CFG_OFFSET,
743 msize - SWITCHTEC_GAS_TOP_CFG_OFFSET,
744 writeable);
745 if (ret)
746 goto unmap_and_exit;
747
748 if (map_size)
749 *map_size = msize;
750
751 dev->gas_map = (gasptr_t __force)map;
752 dev->gas_map_size = msize;
753
754 ret = gasop_access_check(dev);
755 if (ret) {
756 errno = ENODEV;
757 goto unmap_and_exit;
758 }
759 return (gasptr_t __force)map;
760
761unmap_and_exit:
762 munmap(map, msize);
763 return SWITCHTEC_MAP_FAILED;
764}
765
766static void linux_gas_unmap(struct switchtec_dev *dev, gasptr_t map)
767{
768 munmap((void __force *)map, dev->gas_map_size);
769}
770
771static int linux_flash_part(struct switchtec_dev *dev,
772 struct switchtec_fw_image_info *info,
773 enum switchtec_fw_image_part_id_gen3 part)
774{
775 struct switchtec_linux *ldev = to_switchtec_linux(dev);
776 struct switchtec_ioctl_flash_part_info ioctl_info = {0};
777 int ret;
778
779 switch (part) {
780 case SWITCHTEC_FW_PART_ID_G3_IMG0:
781 ioctl_info.flash_partition = SWITCHTEC_IOCTL_PART_IMG0;
782 break;
783 case SWITCHTEC_FW_PART_ID_G3_IMG1:
784 ioctl_info.flash_partition = SWITCHTEC_IOCTL_PART_IMG1;
785 break;
786 case SWITCHTEC_FW_PART_ID_G3_DAT0:
787 ioctl_info.flash_partition = SWITCHTEC_IOCTL_PART_CFG0;
788 break;
789 case SWITCHTEC_FW_PART_ID_G3_DAT1:
790 ioctl_info.flash_partition = SWITCHTEC_IOCTL_PART_CFG1;
791 break;
792 case SWITCHTEC_FW_PART_ID_G3_NVLOG:
793 ioctl_info.flash_partition = SWITCHTEC_IOCTL_PART_NVLOG;
794 break;
795 default:
796 return -EINVAL;
797 }
798
799 ret = ioctl(ldev->fd, SWITCHTEC_IOCTL_FLASH_PART_INFO, &ioctl_info);
800 if (ret)
801 return ret;
802
803 info->part_addr = ioctl_info.address;
804 info->part_len = ioctl_info.length;
805 info->active = false;
806 info->running = false;
807
808 if (ioctl_info.active & SWITCHTEC_IOCTL_PART_ACTIVE)
809 info->active = true;
810
811 if (ioctl_info.active & SWITCHTEC_IOCTL_PART_RUNNING)
812 info->running = true;
813
814 return 0;
815}
816
817static void event_summary_copy(struct switchtec_event_summary *dst,
818 struct switchtec_ioctl_event_summary *src,
819 int size)
820{
821 int i;
822
823 dst->global = src->global;
824 dst->part_bitmap = src->part_bitmap;
825 dst->local_part = src->local_part;
826
827 for (i = 0; i < SWITCHTEC_MAX_PARTS; i++)
828 dst->part[i] = src->part[i];
829
830 for (i = 0; i < SWITCHTEC_MAX_PFF_CSR && i < size; i++)
831 dst->pff[i] = src->pff[i];
832
833 for (; i < SWITCHTEC_MAX_PFF_CSR; i++)
834 dst->pff[i] = 0;
835}
836
837#define EV(t, n)[SWITCHTEC_ ## t ## _EVT_ ## n] = \
838 SWITCHTEC_IOCTL_EVENT_ ## n
839
840static const int event_map[] = {
841 EV(GLOBAL, STACK_ERROR),
842 EV(GLOBAL, PPU_ERROR),
843 EV(GLOBAL, ISP_ERROR),
844 EV(GLOBAL, SYS_RESET),
845 EV(GLOBAL, FW_EXC),
846 EV(GLOBAL, FW_NMI),
847 EV(GLOBAL, FW_NON_FATAL),
848 EV(GLOBAL, FW_FATAL),
849 EV(GLOBAL, TWI_MRPC_COMP),
850 EV(GLOBAL, TWI_MRPC_COMP_ASYNC),
851 EV(GLOBAL, CLI_MRPC_COMP),
852 EV(GLOBAL, CLI_MRPC_COMP_ASYNC),
853 EV(GLOBAL, GPIO_INT),
854 EV(GLOBAL, GFMS),
855 EV(PART, PART_RESET),
856 EV(PART, MRPC_COMP),
857 EV(PART, MRPC_COMP_ASYNC),
858 EV(PART, DYN_PART_BIND_COMP),
859 EV(PFF, AER_IN_P2P),
860 EV(PFF, AER_IN_VEP),
861 EV(PFF, DPC),
862 EV(PFF, CTS),
863 EV(PFF, UEC),
864 EV(PFF, HOTPLUG),
865 EV(PFF, IER),
866 EV(PFF, THRESH),
867 EV(PFF, POWER_MGMT),
868 EV(PFF, TLP_THROTTLING),
869 EV(PFF, FORCE_SPEED),
870 EV(PFF, CREDIT_TIMEOUT),
871 EV(PFF, LINK_STATE),
872};
873
874static int linux_event_summary(struct switchtec_dev *dev,
875 struct switchtec_event_summary *sum)
876{
877 int ret;
878 struct switchtec_ioctl_event_summary isum;
879 struct switchtec_ioctl_event_summary_legacy isum_legacy;
880 struct switchtec_linux *ldev = to_switchtec_linux(dev);
881
882 if (!sum)
883 return 0;
884
885 ret = ioctl(ldev->fd, SWITCHTEC_IOCTL_EVENT_SUMMARY, &isum);
886 if (!ret) {
887 event_summary_copy(sum, &isum, ARRAY_SIZE(isum.pff));
888 return ret;
889 }
890
891 ret = ioctl(ldev->fd, SWITCHTEC_IOCTL_EVENT_SUMMARY_LEGACY, &isum);
892 if (ret < 0)
893 return ret;
894
895 event_summary_copy(sum, &isum, ARRAY_SIZE(isum_legacy.pff));
896
897 return 0;
898}
899
900static int linux_event_ctl(struct switchtec_dev *dev,
901 enum switchtec_event_id e,
902 int index, int flags,
903 uint32_t data[5])
904{
905 int ret;
906 struct switchtec_ioctl_event_ctl ctl;
907 struct switchtec_linux *ldev = to_switchtec_linux(dev);
908
909 if (e >= SWITCHTEC_MAX_EVENTS)
910 return -EINVAL;
911
912 ctl.event_id = event_map[e];
913 ctl.flags = 0;
914
915 if (flags & SWITCHTEC_EVT_FLAG_CLEAR)
916 ctl.flags |= SWITCHTEC_IOCTL_EVENT_FLAG_CLEAR;
917 if (flags & SWITCHTEC_EVT_FLAG_EN_POLL)
918 ctl.flags |= SWITCHTEC_IOCTL_EVENT_FLAG_EN_POLL;
919 if (flags & SWITCHTEC_EVT_FLAG_EN_LOG)
920 ctl.flags |= SWITCHTEC_IOCTL_EVENT_FLAG_EN_LOG;
921 if (flags & SWITCHTEC_EVT_FLAG_EN_CLI)
922 ctl.flags |= SWITCHTEC_IOCTL_EVENT_FLAG_EN_CLI;
923 if (flags & SWITCHTEC_EVT_FLAG_EN_FATAL)
924 ctl.flags |= SWITCHTEC_IOCTL_EVENT_FLAG_EN_FATAL;
925 if (flags & SWITCHTEC_EVT_FLAG_DIS_POLL)
926 ctl.flags |= SWITCHTEC_IOCTL_EVENT_FLAG_DIS_POLL;
927 if (flags & SWITCHTEC_EVT_FLAG_DIS_LOG)
928 ctl.flags |= SWITCHTEC_IOCTL_EVENT_FLAG_DIS_LOG;
929 if (flags & SWITCHTEC_EVT_FLAG_DIS_CLI)
930 ctl.flags |= SWITCHTEC_IOCTL_EVENT_FLAG_DIS_CLI;
931 if (flags & SWITCHTEC_EVT_FLAG_DIS_FATAL)
932 ctl.flags |= SWITCHTEC_IOCTL_EVENT_FLAG_DIS_FATAL;
933
934 ctl.index = index;
935 ret = ioctl(ldev->fd, SWITCHTEC_IOCTL_EVENT_CTL, &ctl);
936
937 if (ret)
938 return ret;
939
940 if (data)
941 memcpy(data, ctl.data, sizeof(ctl.data));
942
943 return ctl.count;
944}
945
946static int linux_event_wait(struct switchtec_dev *dev, int timeout_ms)
947{
948 int ret;
949 struct switchtec_linux *ldev = to_switchtec_linux(dev);
950 struct pollfd fds = {
951 .fd = ldev->fd,
952 .events = POLLPRI,
953 };
954
955 ret = poll(&fds, 1, timeout_ms);
956 if (ret <= 0)
957 return ret;
958
959 if (fds.revents & POLLERR) {
960 errno = ENODEV;
961 return -1;
962 }
963
964 if (fds.revents & POLLPRI)
965 return 1;
966
967 return 0;
968}
969
970static const struct switchtec_ops linux_ops = {
971 .close = linux_close,
972 .get_device_id = linux_get_device_id,
973 .get_fw_version = linux_get_fw_version,
974 .cmd = linux_cmd,
975 .get_devices = linux_get_devices,
976 .pff_to_port = linux_pff_to_port,
977 .port_to_pff = linux_port_to_pff,
978 .gas_map = linux_gas_map,
979 .gas_unmap = linux_gas_unmap,
980 .flash_part = linux_flash_part,
981 .event_summary = linux_event_summary,
982 .event_ctl = linux_event_ctl,
983 .event_wait = linux_event_wait,
984
985 .gas_read8 = mmap_gas_read8,
986 .gas_read16 = mmap_gas_read16,
987 .gas_read32 = mmap_gas_read32,
988 .gas_read64 = mmap_gas_read64,
989 .gas_write8 = mmap_gas_write8,
990 .gas_write16 = mmap_gas_write16,
991 .gas_write32 = mmap_gas_write32,
992 .gas_write32_no_retry = mmap_gas_write32,
993 .gas_write64 = mmap_gas_write64,
994 .memcpy_to_gas = mmap_memcpy_to_gas,
995 .memcpy_from_gas = mmap_memcpy_from_gas,
996 .write_from_gas = mmap_write_from_gas,
997};
998
999struct switchtec_dev *switchtec_open_by_path(const char *path)
1000{
1001 struct switchtec_linux *ldev;
1002 int fd;
1003
1004 fd = open(path, O_RDWR | O_CLOEXEC);
1005 if (fd < 0)
1006 return NULL;
1007
1008 if (isatty(fd))
1009 return switchtec_open_uart(fd);
1010 else
1011 errno = 0;
1012
1013 ldev = malloc(sizeof(*ldev));
1014 if (!ldev)
1015 return NULL;
1016
1017 ldev->fd = fd;
1018
1019 if (check_switchtec_device(ldev))
1020 goto err_close_free;
1021
1022 if (get_partition(ldev))
1023 goto err_close_free;
1024
1025 ldev->dev.ops = &linux_ops;
1026
1027 return &ldev->dev;
1028
1029err_close_free:
1030 close(ldev->fd);
1031 free(ldev);
1032 return NULL;
1033}
1034
1035struct switchtec_dev *switchtec_open_by_index(int index)
1036{
1037 char path[PATH_MAX];
1038 struct switchtec_dev *dev;
1039
1040 snprintf(path, sizeof(path), "/dev/switchtec%d", index);
1041
1042 dev = switchtec_open_by_path(path);
1043
1044 if (errno == ENOENT)
1045 errno = ENODEV;
1046
1047 return dev;
1048}
1049
1050struct switchtec_dev *switchtec_open_by_pci_addr(int domain, int bus,
1051 int device, int func)
1052{
1053 char path[PATH_MAX];
1054 struct switchtec_dev *dev;
1055 struct dirent *dirent;
1056 DIR *dir;
1057
1058 snprintf(path, sizeof(path),
1059 "/sys/bus/pci/devices/%04x:%02x:%02x.%x/switchtec",
1060 domain, bus, device, func);
1061
1062 dir = opendir(path);
1063 if (!dir)
1064 goto err_out;
1065
1066 while ((dirent = readdir(dir))) {
1067 if (dirent->d_name[0] != '.')
1068 break;
1069 }
1070
1071 if (!dirent)
1072 goto err_close;
1073
1074 /*
1075 * Should only be one switchtec device, if there are
1076 * more then something is wrong
1077 */
1078 if (readdir(dir))
1079 goto err_close;
1080
1081 snprintf(path, sizeof(path), "/dev/%s", dirent->d_name);
1082 printf("%s\n", path);
1083 dev = switchtec_open(path);
1084
1085 closedir(dir);
1086 return dev;
1087
1088err_close:
1089 closedir(dir);
1090err_out:
1091 errno = ENODEV;
1092 return NULL;
1093}
1094
1095#endif
struct switchtec_dev * switchtec_open(const char *device)
Open a Switchtec device by string.
Definition switchtec.c:256
struct switchtec_dev * switchtec_open_uart(int fd)
Open a switchtec device behind a uart device.
int switchtec_list(struct switchtec_device_info **devlist)
List all the switchtec devices in the system.
struct switchtec_dev * switchtec_open_by_index(int index)
Open a switchtec device by index.
struct switchtec_dev * switchtec_open_by_path(const char *path)
Open a switchtec device by path.
_PURE int switchtec_partition(struct switchtec_dev *dev)
Get the partiton number of the device that was opened.
Definition switchtec.c:399
struct switchtec_dev * switchtec_open_by_pci_addr(int domain, int bus, int device, int func)
Open a switchtec device by PCI address (BDF)
Gas Operations for platforms that the gas is mapped into the address space.
Represents a Switchtec device in the switchtec_list() function.
Definition switchtec.h:131
char fw_version[32]
Firmware version.
Definition switchtec.h:137
char pci_dev[256]
PCI BDF string.
Definition switchtec.h:134
char path[PATH_MAX]
Path to the device.
Definition switchtec.h:138
char name[256]
Device name, eg. switchtec0.
Definition switchtec.h:132
char product_id[32]
Product ID.
Definition switchtec.h:135
char product_rev[8]
Product revision.
Definition switchtec.h:136
Event summary bitmaps.
Definition switchtec.h:289
uint64_t part_bitmap
Bitmap of partitions with active events.
Definition switchtec.h:291
uint64_t global
Bitmap of global events.
Definition switchtec.h:290
unsigned part[SWITCHTEC_MAX_PARTS]
Bitmap of events in each partition.
Definition switchtec.h:295
unsigned local_part
Bitmap of events in the local partition.
Definition switchtec.h:292
unsigned pff[SWITCHTEC_MAX_PFF_CSR]
Bitmap of events in each port function.
Definition switchtec.h:298
Information about a firmware image or partition.
Definition switchtec.h:252
size_t part_addr
Address of the partition.
Definition switchtec.h:257
size_t part_len
Length of the partition.
Definition switchtec.h:258
Port status structure.
Definition switchtec.h:160
unsigned int acs_ctrl
ACS Setting of the Port.
Definition switchtec.h:180
int vendor_id
Vendor ID.
Definition switchtec.h:177
char * pci_bdf_path
PCI BDF path of the port.
Definition switchtec.h:174
char * pci_bdf
PCI BDF of the port.
Definition switchtec.h:173
int device_id
Device ID.
Definition switchtec.h:178
char * class_devices
Comma seperated list of classes.
Definition switchtec.h:179
char * pci_dev
PCI BDF of the device on the port.
Definition switchtec.h:176
Main Switchtec header.
switchtec_event_id
Enumeration of all possible events.
Definition switchtec.h:304
__gas struct switchtec_gas * gasptr_t
Shortform for a pointer to the GAS register space.
Definition switchtec.h:80