summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorSuren A. Chilingaryan <csa@suren.me>2015-05-07 05:02:46 +0200
committerSuren A. Chilingaryan <csa@suren.me>2015-05-07 05:02:46 +0200
commitde9a65e8c7d73974816d715c0485e2756848ae5f (patch)
treebff9aa4e7b3bdca5138c96a2b646a4d0bb17f1ad
parent38237d1ecc756130bacd0f2e12659ad54d2d6544 (diff)
downloadpcitool-de9a65e8c7d73974816d715c0485e2756848ae5f.tar.gz
pcitool-de9a65e8c7d73974816d715c0485e2756848ae5f.tar.bz2
pcitool-de9a65e8c7d73974816d715c0485e2756848ae5f.tar.xz
pcitool-de9a65e8c7d73974816d715c0485e2756848ae5f.zip
Provide PCIe link information in pcilib
-rw-r--r--pcilib/pci.c112
-rw-r--r--pcilib/pci.h13
-rw-r--r--pcitool/cli.c12
3 files changed, 132 insertions, 5 deletions
diff --git a/pcilib/pci.c b/pcilib/pci.c
index a9cefda..3a18ccd 100644
--- a/pcilib/pci.c
+++ b/pcilib/pci.c
@@ -11,6 +11,8 @@
#include <unistd.h>
#include <sys/ioctl.h>
#include <sys/mman.h>
+#include <sys/types.h>
+#include <sys/stat.h>
#include <arpa/inet.h>
#include <errno.h>
#include <assert.h>
@@ -106,6 +108,7 @@ pcilib_t *pcilib_open(const char *device, const char *model) {
if (ctx) {
memset(ctx, 0, sizeof(pcilib_t));
+ ctx->pci_cfg_space_fd = -1;
ctx->handle = open(device, O_RDWR);
if (ctx->handle < 0) {
@@ -197,6 +200,7 @@ pcilib_context_t *pcilib_get_implementation_context(pcilib_t *ctx) {
return ctx->event_ctx;
}
+
int pcilib_map_data_space(pcilib_t *ctx, uintptr_t addr) {
int err;
pcilib_bar_t i;
@@ -211,7 +215,7 @@ int pcilib_map_data_space(pcilib_t *ctx, uintptr_t addr) {
return err;
}
- int data_bar = -1;
+ int data_bar = -1;
for (i = 0; i < PCILIB_MAX_BARS; i++) {
if ((ctx->bar_space[i])||(!board_info->bar_length[i])) continue;
@@ -255,7 +259,7 @@ int pcilib_map_data_space(pcilib_t *ctx, uintptr_t addr) {
return 0;
}
-char *pcilib_resolve_register_address(pcilib_t *ctx, pcilib_bar_t bar, uintptr_t addr) {
+char *pcilib_resolve_register_address(pcilib_t *ctx, pcilib_bar_t bar, uintptr_t addr) {
if (bar == PCILIB_BAR_DETECT) {
// First checking the default register bar
size_t offset = addr - ctx->board_info.bar_start[ctx->reg_bar];
@@ -347,6 +351,9 @@ void pcilib_close(pcilib_t *ctx) {
pcilib_unmap_bar(ctx, i, ptr);
}
}
+
+ if (ctx->pci_cfg_space_fd >= 0)
+ close(ctx->pci_cfg_space_fd);
if (ctx->registers)
free(ctx->registers);
@@ -361,3 +368,104 @@ void pcilib_close(pcilib_t *ctx) {
}
}
+static int pcilib_update_pci_configuration_space(pcilib_t *ctx) {
+ int err;
+ int size;
+
+ if (ctx->pci_cfg_space_fd < 0) {
+ char fname[128];
+
+ const pcilib_board_info_t *board_info = pcilib_get_board_info(ctx);
+ if (!board_info) {
+ pcilib_error("Failed to acquire board info");
+ return PCILIB_ERROR_FAILED;
+ }
+
+ sprintf(fname, "/sys/bus/pci/devices/0000:%02x:%02x.%1x/config", board_info->bus, board_info->slot, board_info->func);
+
+ ctx->pci_cfg_space_fd = open(fname, O_RDONLY);
+ if (ctx->pci_cfg_space_fd < 0) {
+ pcilib_error("Failed to open configuration space in %s", fname);
+ return PCILIB_ERROR_FAILED;
+ }
+ } else {
+ err = lseek(ctx->pci_cfg_space_fd, SEEK_SET, 0);
+ if (err) {
+ close(ctx->pci_cfg_space_fd);
+ ctx->pci_cfg_space_fd = -1;
+ return pcilib_update_pci_configuration_space(ctx);
+ }
+ }
+
+ size = read(ctx->pci_cfg_space_fd, ctx->pci_cfg_space_cache, 256);
+ if (size != 256) {
+ pcilib_error("Failed to read PCI configuration from sysfs");
+ return PCILIB_ERROR_FAILED;
+ }
+
+ return 0;
+}
+
+
+static uint32_t *pcilib_get_pci_capabilities(pcilib_t *ctx, int cap_id) {
+ int err;
+
+ uint32_t cap;
+ uint8_t cap_offset; /**< Offset of capability in the configuration space */
+
+ if (!ctx->pci_cfg_space_fd) {
+ err = pcilib_update_pci_configuration_space(ctx);
+ if (err) {
+ pcilib_error("Error (%i) reading PCI configuration space", err);
+ return NULL;
+ }
+ }
+
+ // This is just a pointer to the first cap
+ cap = ctx->pci_cfg_space_cache[(0x34>>2)];
+ cap_offset = cap&0xFC;
+
+ while ((cap_offset)&&(cap_offset < 256)) {
+ cap = ctx->pci_cfg_space_cache[cap_offset>>2];
+ if ((cap&0xFF) == cap_id)
+ return &ctx->pci_cfg_space_cache[cap_offset>>2];
+ cap_offset = (cap>>8)&0xFC;
+ }
+
+ return NULL;
+};
+
+
+static const uint32_t *pcilib_get_pcie_capabilities(pcilib_t *ctx) {
+ if (ctx->pcie_capabilities)
+ return ctx->pcie_capabilities;
+
+ ctx->pcie_capabilities = pcilib_get_pci_capabilities(ctx, 0x10);
+ return ctx->pcie_capabilities;
+}
+
+
+const pcilib_pcie_link_info_t *pcilib_get_pcie_link_info(pcilib_t *ctx) {
+ int err;
+ const uint32_t *cap;
+
+ err = pcilib_update_pci_configuration_space(ctx);
+ if (err) {
+ pcilib_error("Error (%i) updating PCI configuration space", err);
+ return NULL;
+ }
+
+ cap = pcilib_get_pcie_capabilities(ctx);
+ if (!cap) return NULL;
+
+ // Generally speaking this can be updated during the application life time
+
+ ctx->link_info.max_payload = (cap[1] & 0x07) + 7;
+ ctx->link_info.payload = ((cap[2] >> 5) & 0x07) + 7;
+ ctx->link_info.link_speed = (cap[3]&0xF);
+ ctx->link_info.link_width = (cap[3]&0x3F0) >> 4;
+ ctx->link_info.max_link_speed = (cap[4]&0xF0000) >> 16;
+ ctx->link_info.max_link_width = (cap[4]&0x3F00000) >> 20;
+
+ return &ctx->link_info;
+}
diff --git a/pcilib/pci.h b/pcilib/pci.h
index a914e84..ce8d119 100644
--- a/pcilib/pci.h
+++ b/pcilib/pci.h
@@ -25,13 +25,24 @@
#include "model.h"
#include "export.h"
+typedef struct {
+ uint8_t max_link_speed, link_speed;
+ uint8_t max_link_width, link_width;
+ uint8_t max_payload, payload;
+} pcilib_pcie_link_info_t;
+
struct pcilib_s {
int handle; /**< file handle of device */
uintptr_t page_mask; /**< Selects bits which define offset within the page */
pcilib_board_info_t board_info; /**< The mandatory information about board as defined by PCI specification */
+ pcilib_pcie_link_info_t link_info; /**< Infomation about PCIe connection */
char *bar_space[PCILIB_MAX_BARS]; /**< Pointers to the mapped BARs in virtual address space */
+ int pci_cfg_space_fd; /**< File descriptor linking to PCI configuration space in sysfs */
+ uint32_t pci_cfg_space_cache[64]; /**< Cached PCI configuration space */
+ const uint32_t *pcie_capabilities; /**< PCI Capbility structure (just a pointer at appropriate place in the pci_cfg_space) */
+
int reg_bar_mapped; /**< Indicates that all BARs used to access registers are mapped */
pcilib_bar_t reg_bar; /**< Default BAR to look for registers, other BARs will be looked as well */
int data_bar_mapped; /**< Indicates that a BAR for large PIO is mapped */
@@ -64,9 +75,9 @@ struct pcilib_s {
#endif /* PCILIB_FILE_IO */
};
-
pcilib_context_t *pcilib_get_implementation_context(pcilib_t *ctx);
const pcilib_board_info_t *pcilib_get_board_info(pcilib_t *ctx);
+const pcilib_pcie_link_info_t *pcilib_get_pcie_link_info(pcilib_t *ctx);
int pcilib_map_register_space(pcilib_t *ctx);
int pcilib_map_data_space(pcilib_t *ctx, uintptr_t addr);
diff --git a/pcitool/cli.c b/pcitool/cli.c
index 98ac5b8..580d992 100644
--- a/pcitool/cli.c
+++ b/pcitool/cli.c
@@ -527,12 +527,20 @@ void Info(pcilib_t *handle, const pcilib_model_description_t *model_info) {
struct dirent *entry;
const pcilib_model_description_t *info = NULL;
const pcilib_board_info_t *board_info = pcilib_get_board_info(handle);
+ const pcilib_pcie_link_info_t *link_info = pcilib_get_pcie_link_info(handle);
path = getenv("PCILIB_PLUGIN_DIR");
if (!path) path = PCILIB_PLUGIN_DIR;
- printf("Vendor: %x, Device: %x, Bus: %x, Slot: %x, Function: %x, Model: %s\n", board_info->vendor_id, board_info->device_id, board_info->bus, board_info->slot, board_info->func, handle->model);
- printf(" Interrupt - Pin: %i, Line: %i\n", board_info->interrupt_pin, board_info->interrupt_line);
+ if (board_info)
+ printf("Vendor: %x, Device: %x, Bus: %x, Slot: %x, Function: %x, Model: %s\n", board_info->vendor_id, board_info->device_id, board_info->bus, board_info->slot, board_info->func, handle->model);
+
+ if (link_info) {
+ printf(" PCIe x%u (gen%u), DMA Payload: %u (of %u)\n", link_info->link_width, link_info->link_speed, 1<<link_info->payload, 1<<link_info->max_payload);
+ }
+
+ if (board_info)
+ printf(" Interrupt - Pin: %i, Line: %i\n", board_info->interrupt_pin, board_info->interrupt_line);
List(handle, model_info, (char*)-1, 0);