27#define SWITCHTEC_LIB_LINUX
29#include "../switchtec_priv.h"
31#include "switchtec/pci.h"
32#include "switchtec/utils.h"
36#include <linux/switchtec_ioctl.h>
46#include <sys/sysmacros.h>
54static const char *sys_path =
"/sys/class/switchtec";
56struct switchtec_linux {
57 struct switchtec_dev dev;
61#define to_switchtec_linux(d) \
62 ((struct switchtec_linux *) \
63 ((char *)d - offsetof(struct switchtec_linux, dev)))
65const char *platform_strerror(
void)
70static int dev_to_sysfs_path(
struct switchtec_linux *ldev,
const char *suffix,
71 char *buf,
size_t buflen)
76 ret = fstat(ldev->fd, &stat);
81 "/sys/dev/char/%d:%d/%s",
82 major(stat.st_rdev), minor(stat.st_rdev), suffix);
87static int sysfs_read_str(
const char *path,
char *buf,
size_t buflen)
92 fd = open(path, O_RDONLY);
96 ret = read(fd, buf, buflen);
103static long long sysfs_read_int(
const char *path,
int base)
108 ret = sysfs_read_str(path, buf,
sizeof(buf));
112 return strtoll(buf, NULL, base);
115static int check_switchtec_device(
struct switchtec_linux *ldev)
118 char syspath[PATH_MAX];
120 ret = dev_to_sysfs_path(ldev,
"device/switchtec", syspath,
125 ret = access(syspath, F_OK);
132static int get_partition(
struct switchtec_linux *ldev)
135 char syspath[PATH_MAX];
137 ret = dev_to_sysfs_path(ldev,
"partition", syspath,
142 ldev->dev.partition = sysfs_read_int(syspath, 10);
143 if (ldev->dev.partition < 0)
144 return ldev->dev.partition;
146 ret = dev_to_sysfs_path(ldev,
"partition_count", syspath,
151 ldev->dev.partition_count = sysfs_read_int(syspath, 10);
152 if (ldev->dev.partition_count < 1)
158static void linux_close(
struct switchtec_dev *dev)
160 struct switchtec_linux *ldev = to_switchtec_linux(dev);
166static int scan_dev_filter(
const struct dirent *d)
168 if (d->d_name[0] ==
'.')
174static void get_device_str(
const char *path,
const char *file,
175 char *buf,
size_t buflen)
177 char sysfs_path[PATH_MAX];
180 snprintf(sysfs_path,
sizeof(sysfs_path),
"%s/%s",
183 ret = sysfs_read_str(sysfs_path, buf, buflen);
184 if (ret < 0 || buf[0] == -1)
185 snprintf(buf, buflen,
"unknown");
187 buf[strcspn(buf,
"\n")] = 0;
190static void get_fw_version(
const char *path,
char *buf,
size_t buflen)
192 char sysfs_path[PATH_MAX];
196 ret = snprintf(sysfs_path,
sizeof(sysfs_path),
"%s/fw_version",
198 if (ret >=
sizeof(sysfs_path))
199 goto unknown_version;
201 fw_ver = sysfs_read_int(sysfs_path, 16);
204 goto unknown_version;
206 version_to_string(fw_ver, buf, buflen);
210 snprintf(buf, buflen,
"unknown");
215 struct dirent **devices;
217 char link_path[PATH_MAX];
218 char pci_path[PATH_MAX] =
"";
221 n = scandir(sys_path, &devices, scan_dev_filter, alphasort);
228 for (i = 0; i < n; i++)
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);
241 snprintf(link_path,
sizeof(link_path),
"%s/%s/device",
242 sys_path, devices[i]->d_name);
244 if (readlink(link_path, pci_path,
sizeof(pci_path)) > 0)
246 "%s", basename(pci_path));
249 "unknown pci device");
251 snprintf(link_path,
sizeof(link_path),
"%s/%s",
252 sys_path, devices[i]->d_name);
254 get_device_str(link_path,
"product_id", dl[i].
product_id,
256 get_device_str(link_path,
"product_revision",
268static int linux_get_device_id(
struct switchtec_dev *dev)
271 char link_path[PATH_MAX];
272 struct switchtec_linux *ldev = to_switchtec_linux(dev);
274 ret = dev_to_sysfs_path(ldev,
"device/device", link_path,
279 return sysfs_read_int(link_path, 16);
282static int linux_get_fw_version(
struct switchtec_dev *dev,
char *buf,
287 char syspath[PATH_MAX];
288 struct switchtec_linux *ldev = to_switchtec_linux(dev);
290 ret = dev_to_sysfs_path(ldev,
"fw_version", syspath,
sizeof(syspath));
294 version = sysfs_read_int(syspath, 16);
298 version_to_string(version, buf, buflen);
303static int linux_get_device_version(
struct switchtec_dev *dev,
int *version_res)
307 char syspath[PATH_MAX];
308 struct switchtec_linux *ldev = to_switchtec_linux(dev);
310 ret = dev_to_sysfs_path(ldev,
"device_version", syspath,
sizeof(syspath));
314 version = sysfs_read_int(syspath, 16);
318 memcpy(version_res, &version,
sizeof(
int));
323static int submit_cmd(
struct switchtec_linux *ldev, uint32_t cmd,
324 const void *payload,
size_t payload_len)
327 size_t bufsize = payload_len +
sizeof(cmd);
331 memcpy(buf, &cmd,
sizeof(cmd));
332 memcpy(&buf[
sizeof(cmd)], payload, payload_len);
334 ret = write(ldev->fd, buf, bufsize);
339 if (ret != bufsize) {
347static int read_resp(
struct switchtec_linux *ldev,
void *resp,
351 size_t bufsize =
sizeof(uint32_t) + resp_len;
354 ret = read(ldev->fd, buf, bufsize);
359 if (ret != bufsize) {
364 memcpy(&ret, buf,
sizeof(ret));
374 memcpy(resp, &buf[
sizeof(ret)], resp_len);
379static int linux_cmd(
struct switchtec_dev *dev, uint32_t cmd,
380 const void *payload,
size_t payload_len,
void *resp,
384 struct switchtec_linux *ldev = to_switchtec_linux(dev);
387 ret = submit_cmd(ldev, cmd, payload, payload_len);
388 if (errno == EBADE) {
389 read_resp(ldev, NULL, 0);
397 return read_resp(ldev, resp, resp_len);
400static int get_class_devices(
const char *searchpath,
405 char syspath[PATH_MAX];
408 const size_t MAX_LEN = 256;
410 snprintf(syspath,
sizeof(syspath),
"%s*/*/device", searchpath);
411 glob(syspath, 0, NULL, &paths);
413 for (i = 0; i < paths.gl_pathc; i++) {
414 char *p = paths.gl_pathv[i];
416 len = readlink(p, syspath,
sizeof(syspath));
428 ", %s", basename(p));
438static void get_port_bdf(
const char *searchpath,
int port,
441 char syspath[PATH_MAX];
445 ret = snprintf(syspath,
sizeof(syspath),
"%s/*:*:%02x.?",
447 if (ret >=
sizeof(syspath))
450 glob(syspath, 0, NULL, &paths);
452 if (paths.gl_pathc == 1)
453 status->
pci_bdf = strdup(basename(paths.gl_pathv[0]));
461 char rpath[PATH_MAX];
462 int domain, bus, dev, fn;
470 snprintf(path,
sizeof(path),
"/sys/bus/pci/devices/%s",
473 if (!realpath(path, rpath))
476 subpath = strtok(rpath,
"/");
478 ret = sscanf(subpath,
"%x:%x:%x.%x", &domain, &bus, &dev, &fn);
481 ret = snprintf(path + ptr,
sizeof(path) - ptr,
482 "%04x:%02x:%02x:%x/",
483 domain, bus, dev, fn);
485 ret = snprintf(path + ptr,
sizeof(path) - ptr,
486 "%02x.%x/", dev, fn);
488 if (ret <= 0 || ret >=
sizeof(path) - ptr)
493 subpath = strtok(NULL,
"/");
505 char syspath[PATH_MAX];
511 snprintf(syspath,
sizeof(syspath),
"/sys/bus/pci/devices/%s/*:*:*/",
514 glob(syspath, 0, NULL, &paths);
516 for (i = 0; i < paths.gl_pathc; i++) {
517 char *p = paths.gl_pathv[i];
519 snprintf(syspath,
sizeof(syspath),
"%s/vendor", p);
520 status->
vendor_id = sysfs_read_int(syspath, 16);
524 snprintf(syspath,
sizeof(syspath),
"%s/device", p);
525 status->
device_id = sysfs_read_int(syspath, 16);
529 if (get_class_devices(p, status)) {
532 status->
pci_dev = strdup(basename(p));
536 status->
pci_dev = strdup(basename(p));
546 char syspath[PATH_MAX];
548 int pos = PCI_EXT_CAP_OFFSET;
551 snprintf(syspath,
sizeof(syspath),
"/sys/bus/pci/devices/%s/config",
554 fd = open(syspath, O_RDONLY);
559 ret = pread(fd, &extcap,
sizeof(extcap), pos);
560 if (ret !=
sizeof(extcap) || !extcap)
563 if (PCI_EXT_CAP_ID(extcap) == PCI_EXT_CAP_ID_ACS)
566 pos = PCI_EXT_CAP_NEXT(extcap);
567 if (pos < PCI_EXT_CAP_OFFSET)
571 ret = pread(fd, &acs,
sizeof(acs), pos + PCI_ACS_CTRL);
572 if (ret !=
sizeof(acs))
581static int linux_get_devices(
struct switchtec_dev *dev,
588 char syspath[PATH_MAX];
589 char searchpath[PATH_MAX];
590 struct switchtec_linux *ldev = to_switchtec_linux(dev);
592 ret = dev_to_sysfs_path(ldev,
"device", syspath,
597 if (!realpath(syspath, searchpath)) {
603 searchpath[strlen(searchpath) - 1] =
'0';
607 for (i = 0; i < ports; i++) {
608 if (status[i].port.partition != local_part)
611 if (status[i].port.upstream) {
612 status[i].
pci_bdf = strdup(basename(searchpath));
613 get_port_bdf_path(&status[i]);
617 get_port_bdf(searchpath, status[i].port.log_id - 1, &status[i]);
618 get_port_bdf_path(&status[i]);
619 get_port_info(&status[i]);
620 get_config_info(&status[i]);
626static int linux_pff_to_port(
struct switchtec_dev *dev,
int pff,
627 int *partition,
int *port)
630 struct switchtec_ioctl_pff_port p;
631 struct switchtec_linux *ldev = to_switchtec_linux(dev);
634 ret = ioctl(ldev->fd, SWITCHTEC_IOCTL_PFF_TO_PORT, &p);
639 *partition = p.partition;
646static int linux_port_to_pff(
struct switchtec_dev *dev,
int partition,
650 struct switchtec_ioctl_pff_port p;
651 struct switchtec_linux *ldev = to_switchtec_linux(dev);
654 p.partition = partition;
656 ret = ioctl(ldev->fd, SWITCHTEC_IOCTL_PORT_TO_PFF, &p);
667#define __force __attribute__((force))
672static ssize_t resource_size(
struct switchtec_linux *ldev,
const char *fname)
674 char respath[PATH_MAX];
678 ret = dev_to_sysfs_path(ldev, fname, respath,
685 fd = open(respath, O_RDONLY);
689 ret = fstat(fd, &stat);
699static int mmap_resource(
struct switchtec_linux *ldev,
const char *fname,
700 void *addr,
size_t offset,
size_t size,
int writeable)
702 char respath[PATH_MAX];
706 ret = dev_to_sysfs_path(ldev, fname, respath,
713 fd = open(respath, writeable ? O_RDWR : O_RDONLY);
717 map = mmap(addr, size, (writeable ? PROT_WRITE : 0) | PROT_READ,
718 MAP_SHARED | MAP_FIXED, fd, offset);
719 if (map == MAP_FAILED)
733static gasptr_t linux_gas_map(
struct switchtec_dev *dev,
int writeable,
739 struct switchtec_linux *ldev = to_switchtec_linux(dev);
741 msize = resource_size(ldev,
"device/resource0");
743 return SWITCHTEC_MAP_FAILED;
748 map = mmap(NULL, msize, PROT_NONE,
749 MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
750 if (map == MAP_FAILED)
751 return SWITCHTEC_MAP_FAILED;
753 ret = mmap_resource(ldev,
"device/resource0_wc", map, 0,
754 SWITCHTEC_GAS_TOP_CFG_OFFSET, writeable);
756 ret = mmap_resource(ldev,
"device/resource0", map, 0,
757 SWITCHTEC_GAS_TOP_CFG_OFFSET,
763 ret = mmap_resource(ldev,
"device/resource0",
764 map + SWITCHTEC_GAS_TOP_CFG_OFFSET,
765 SWITCHTEC_GAS_TOP_CFG_OFFSET,
766 msize - SWITCHTEC_GAS_TOP_CFG_OFFSET,
774 dev->gas_map = (
gasptr_t __force)map;
775 dev->gas_map_size = msize;
777 ret = gasop_access_check(dev);
786 return SWITCHTEC_MAP_FAILED;
789static void linux_gas_unmap(
struct switchtec_dev *dev,
gasptr_t map)
791 munmap((
void __force *)map, dev->gas_map_size);
794static int linux_flash_part(
struct switchtec_dev *dev,
796 enum switchtec_fw_image_part_id_gen3 part)
798 struct switchtec_linux *ldev = to_switchtec_linux(dev);
799 struct switchtec_ioctl_flash_part_info ioctl_info = {0};
803 case SWITCHTEC_FW_PART_ID_G3_IMG0:
804 ioctl_info.flash_partition = SWITCHTEC_IOCTL_PART_IMG0;
806 case SWITCHTEC_FW_PART_ID_G3_IMG1:
807 ioctl_info.flash_partition = SWITCHTEC_IOCTL_PART_IMG1;
809 case SWITCHTEC_FW_PART_ID_G3_DAT0:
810 ioctl_info.flash_partition = SWITCHTEC_IOCTL_PART_CFG0;
812 case SWITCHTEC_FW_PART_ID_G3_DAT1:
813 ioctl_info.flash_partition = SWITCHTEC_IOCTL_PART_CFG1;
815 case SWITCHTEC_FW_PART_ID_G3_NVLOG:
816 ioctl_info.flash_partition = SWITCHTEC_IOCTL_PART_NVLOG;
822 ret = ioctl(ldev->fd, SWITCHTEC_IOCTL_FLASH_PART_INFO, &ioctl_info);
828 info->active =
false;
829 info->running =
false;
831 if (ioctl_info.active & SWITCHTEC_IOCTL_PART_ACTIVE)
834 if (ioctl_info.active & SWITCHTEC_IOCTL_PART_RUNNING)
835 info->running =
true;
841 struct switchtec_ioctl_event_summary *src,
846 dst->
global = src->global;
850 for (i = 0; i < SWITCHTEC_MAX_PARTS; i++)
851 dst->
part[i] = src->part[i];
853 for (i = 0; i < SWITCHTEC_MAX_PFF_CSR && i < size; i++)
854 dst->
pff[i] = src->pff[i];
856 for (; i < SWITCHTEC_MAX_PFF_CSR; i++)
860#define EV(t, n)[SWITCHTEC_ ## t ## _EVT_ ## n] = \
861 SWITCHTEC_IOCTL_EVENT_ ## n
863static const int event_map[] = {
864 EV(GLOBAL, STACK_ERROR),
865 EV(GLOBAL, PPU_ERROR),
866 EV(GLOBAL, ISP_ERROR),
867 EV(GLOBAL, SYS_RESET),
870 EV(GLOBAL, FW_NON_FATAL),
871 EV(GLOBAL, FW_FATAL),
872 EV(GLOBAL, TWI_MRPC_COMP),
873 EV(GLOBAL, TWI_MRPC_COMP_ASYNC),
874 EV(GLOBAL, CLI_MRPC_COMP),
875 EV(GLOBAL, CLI_MRPC_COMP_ASYNC),
876 EV(GLOBAL, GPIO_INT),
878 EV(PART, PART_RESET),
880 EV(PART, MRPC_COMP_ASYNC),
881 EV(PART, DYN_PART_BIND_COMP),
891 EV(PFF, TLP_THROTTLING),
892 EV(PFF, FORCE_SPEED),
893 EV(PFF, CREDIT_TIMEOUT),
897static int linux_event_summary(
struct switchtec_dev *dev,
901 struct switchtec_ioctl_event_summary isum;
902 struct switchtec_ioctl_event_summary_legacy isum_legacy;
903 struct switchtec_linux *ldev = to_switchtec_linux(dev);
908 ret = ioctl(ldev->fd, SWITCHTEC_IOCTL_EVENT_SUMMARY, &isum);
910 event_summary_copy(sum, &isum, ARRAY_SIZE(isum.pff));
914 ret = ioctl(ldev->fd, SWITCHTEC_IOCTL_EVENT_SUMMARY_LEGACY, &isum);
918 event_summary_copy(sum, &isum, ARRAY_SIZE(isum_legacy.pff));
923static int linux_event_ctl(
struct switchtec_dev *dev,
925 int index,
int flags,
929 struct switchtec_ioctl_event_ctl ctl;
930 struct switchtec_linux *ldev = to_switchtec_linux(dev);
932 if (e >= SWITCHTEC_MAX_EVENTS)
935 ctl.event_id = event_map[e];
938 if (flags & SWITCHTEC_EVT_FLAG_CLEAR)
939 ctl.flags |= SWITCHTEC_IOCTL_EVENT_FLAG_CLEAR;
940 if (flags & SWITCHTEC_EVT_FLAG_EN_POLL)
941 ctl.flags |= SWITCHTEC_IOCTL_EVENT_FLAG_EN_POLL;
942 if (flags & SWITCHTEC_EVT_FLAG_EN_LOG)
943 ctl.flags |= SWITCHTEC_IOCTL_EVENT_FLAG_EN_LOG;
944 if (flags & SWITCHTEC_EVT_FLAG_EN_CLI)
945 ctl.flags |= SWITCHTEC_IOCTL_EVENT_FLAG_EN_CLI;
946 if (flags & SWITCHTEC_EVT_FLAG_EN_FATAL)
947 ctl.flags |= SWITCHTEC_IOCTL_EVENT_FLAG_EN_FATAL;
948 if (flags & SWITCHTEC_EVT_FLAG_DIS_POLL)
949 ctl.flags |= SWITCHTEC_IOCTL_EVENT_FLAG_DIS_POLL;
950 if (flags & SWITCHTEC_EVT_FLAG_DIS_LOG)
951 ctl.flags |= SWITCHTEC_IOCTL_EVENT_FLAG_DIS_LOG;
952 if (flags & SWITCHTEC_EVT_FLAG_DIS_CLI)
953 ctl.flags |= SWITCHTEC_IOCTL_EVENT_FLAG_DIS_CLI;
954 if (flags & SWITCHTEC_EVT_FLAG_DIS_FATAL)
955 ctl.flags |= SWITCHTEC_IOCTL_EVENT_FLAG_DIS_FATAL;
958 ret = ioctl(ldev->fd, SWITCHTEC_IOCTL_EVENT_CTL, &ctl);
964 memcpy(data, ctl.data,
sizeof(ctl.data));
969static int linux_event_wait(
struct switchtec_dev *dev,
int timeout_ms)
972 struct switchtec_linux *ldev = to_switchtec_linux(dev);
973 struct pollfd fds = {
978 ret = poll(&fds, 1, timeout_ms);
982 if (fds.revents & POLLERR) {
987 if (fds.revents & POLLPRI)
993static const struct switchtec_ops linux_ops = {
994 .close = linux_close,
995 .get_device_id = linux_get_device_id,
996 .get_fw_version = linux_get_fw_version,
997 .get_device_version = linux_get_device_version,
999 .get_devices = linux_get_devices,
1000 .pff_to_port = linux_pff_to_port,
1001 .port_to_pff = linux_port_to_pff,
1002 .gas_map = linux_gas_map,
1003 .gas_unmap = linux_gas_unmap,
1004 .flash_part = linux_flash_part,
1005 .event_summary = linux_event_summary,
1006 .event_ctl = linux_event_ctl,
1007 .event_wait = linux_event_wait,
1009 .gas_read8 = mmap_gas_read8,
1010 .gas_read16 = mmap_gas_read16,
1011 .gas_read32 = mmap_gas_read32,
1012 .gas_read64 = mmap_gas_read64,
1013 .gas_write8 = mmap_gas_write8,
1014 .gas_write16 = mmap_gas_write16,
1015 .gas_write32 = mmap_gas_write32,
1016 .gas_write32_no_retry = mmap_gas_write32,
1017 .gas_write64 = mmap_gas_write64,
1018 .memcpy_to_gas = mmap_memcpy_to_gas,
1019 .memcpy_from_gas = mmap_memcpy_from_gas,
1020 .write_from_gas = mmap_write_from_gas,
1025 struct switchtec_linux *ldev;
1028 fd = open(path, O_RDWR | O_CLOEXEC);
1037 ldev = malloc(
sizeof(*ldev));
1043 if (check_switchtec_device(ldev))
1044 goto err_close_free;
1046 if (get_partition(ldev))
1047 goto err_close_free;
1049 ldev->dev.ops = &linux_ops;
1061 char path[PATH_MAX];
1062 struct switchtec_dev *dev;
1064 snprintf(path,
sizeof(path),
"/dev/switchtec%d", index);
1068 if (errno == ENOENT)
1075 int device,
int func)
1077 char path[PATH_MAX];
1078 struct switchtec_dev *dev;
1079 struct dirent *dirent;
1082 snprintf(path,
sizeof(path),
1083 "/sys/bus/pci/devices/%04x:%02x:%02x.%x/switchtec",
1084 domain, bus, device, func);
1086 dir = opendir(path);
1090 while ((dirent = readdir(dir))) {
1091 if (dirent->d_name[0] !=
'.')
1105 snprintf(path,
sizeof(path),
"/dev/%s", dirent->d_name);
1106 printf(
"%s\n", path);
struct switchtec_dev * switchtec_open(const char *device)
Open a Switchtec device by string.
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.
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.
char fw_version[32]
Firmware version.
char pci_dev[256]
PCI BDF string.
char path[PATH_MAX]
Path to the device.
char name[256]
Device name, eg. switchtec0.
char product_id[32]
Product ID.
char product_rev[8]
Product revision.
uint64_t part_bitmap
Bitmap of partitions with active events.
uint64_t global
Bitmap of global events.
unsigned part[SWITCHTEC_MAX_PARTS]
Bitmap of events in each partition.
unsigned local_part
Bitmap of events in the local partition.
unsigned pff[SWITCHTEC_MAX_PFF_CSR]
Bitmap of events in each port function.
Information about a firmware image or partition.
size_t part_addr
Address of the partition.
size_t part_len
Length of the partition.
unsigned int acs_ctrl
ACS Setting of the Port.
char * pci_bdf_path
PCI BDF path of the port.
char * pci_bdf
PCI BDF of the port.
char * class_devices
Comma seperated list of classes.
char * pci_dev
PCI BDF of the device on the port.
switchtec_event_id
Enumeration of all possible events.
__gas struct switchtec_gas * gasptr_t
Shortform for a pointer to the GAS register space.