blob: f895e0d4fca114966d526b700a96a54e6ae5b2b7 [file] [log] [blame]
/*
* Copyright 2017, Data61
* Commonwealth Scientific and Industrial Research Organisation (CSIRO)
* ABN 41 687 119 230.
*
* This software may be distributed and modified according to the terms of
* the BSD 2-Clause license. Note that NO WARRANTY is provided.
* See "LICENSE_BSD2.txt" for details.
*
* @TAG(DATA61_BSD)
*/
#pragma once
#include <pci/helper.h>
#include <stdbool.h>
#include <inttypes.h>
// ref: http://www.acm.uiuc.edu/sigops/roll_your_own/7.c.0.html
// PCI System Architecture, rev 4
#define PCI_CONFIG_HEADER_SIZE_BYTES PCI_STD_HEADER_SIZEOF
/* Detailed base address information about a device. */
struct libpci_device_iocfg {
/* PCI_BASE_ADDRESS_MEM address or
PCI_BASE_ADDRESS_IO address */
uint32_t base_addr[6];
/* PCI_BASE_ADDRESS_SPACE_IO or
PCI_BASE_ADDRESS_SPACE_MEMORY */
uint8_t base_addr_space[6];
/* PCI_BASE_ADDRESS_MEM_TYPE_32 or
PCI_BASE_ADDRESS_MEM_TYPE_64 */
uint8_t base_addr_type[6];
/* PCI_BASE_ADDRESS_MEM_PREFETCH */
uint8_t base_addr_prefetchable[6];
/* size */
uint32_t base_addr_size_mask[6];
uint32_t base_addr_size[6];
/* raw addr */
uint32_t base_addr_raw[6];
/* Is this BAR the higher word of a 64-bit address? If true, then this BAR is partial
and should not be directly processed in any way. */
bool base_addr_64H[6];
};
typedef struct libpci_device_iocfg libpci_device_iocfg_t;
/* Get the size of a PCI config space element. */
static inline int libpci_device_cfg_sizeof(int offset) {
switch (offset) {
case PCI_VENDOR_ID: return 2;
case PCI_DEVICE_ID: return 2;
case PCI_COMMAND: return 2;
case PCI_STATUS: return 2;
case PCI_CLASS_REVISION: return 1;
case PCI_CLASS_PROG: return 1;
case PCI_CLASS_DEVICE: return 2;
case PCI_CACHE_LINE_SIZE: return 1;
case PCI_LATENCY_TIMER: return 1;
case PCI_HEADER_TYPE: return 1;
case PCI_BIST: return 1;
case PCI_BASE_ADDRESS_0:
case PCI_BASE_ADDRESS_1:
case PCI_BASE_ADDRESS_2:
case PCI_BASE_ADDRESS_3:
case PCI_BASE_ADDRESS_4:
case PCI_BASE_ADDRESS_5:
return 4;
case PCI_CARDBUS_CIS: return 4;
case PCI_SUBSYSTEM_VENDOR_ID: return 2;
case PCI_SUBSYSTEM_ID: return 2;
case PCI_ROM_ADDRESS: return 4;
case PCI_INTERRUPT_LINE: return 1;
case PCI_INTERRUPT_PIN: return 1;
case PCI_MIN_GNT: return 1;
case PCI_MAX_LAT: return 1;
}
return 0;
}
/* Get the base address at a given index. Will automatically handle split 64-bit addreses. */
static inline uint64_t libpci_device_iocfg_get_baseaddr(libpci_device_iocfg_t *cfg, int index) {
assert(cfg && index >= 0 && index < 6);
if (cfg->base_addr_type[index] != PCI_BASE_ADDRESS_MEM_TYPE_64)
return (uint64_t) cfg->base_addr[index];
/* 64-bit mode BARs must have a word after it. */
assert(index < 5);
/* And the word before it better be set to 64L mode. */
assert(cfg->base_addr_64H[index + 1]);
return ((uint64_t) cfg->base_addr[index]) | (((uint64_t) cfg->base_addr[index + 1]) << 32);
}
/* Get the 32-bit base address at given index. Will automatically handle split 64-bit addresses,
* and cast them (with an assert check that the upper 32-bits are zero). */
static inline uint32_t libpci_device_iocfg_get_baseaddr32(libpci_device_iocfg_t *cfg, int index) {
uint64_t baddr = libpci_device_iocfg_get_baseaddr(cfg, index);
assert((baddr & 0xFFFFFFFFUL) == baddr);
if ((baddr & 0xFFFFFFFFUL) != baddr) {
printf("WARNING: get_baseaddr32 called for 64-bit address. Address will be truncated.\n");
printf(" This will most likely lead to problems.\n");
assert(!"WARNING. Zap this assert to ignore.");
}
return (uint32_t)(baddr & 0xFFFFFFFFUL);
}
/* Returns true if the given device has at least one IO port base addr associated,
* false otherwise. */
static inline bool libpci_device_iocfg_uses_iomem(libpci_device_iocfg_t *cfg) {
assert(cfg);
for (int i = 0; i < 6; i++) {
if (cfg->base_addr[i] == 0 || cfg->base_addr_64H[i]) continue;
return true;
}
return false;
}
/* Print out detailed info about a device's base addresses. */
static inline void libpci_device_iocfg_debug_print(libpci_device_iocfg_t *cfg, bool compact) {
for(int i = 0; i < 6; i++) {
if (compact) {
/* Display in compact space mode, shoving as much information as possible in a few
* lines. This is similar to how the Linux kernel PCI debug displays in dmesg. */
if (cfg->base_addr[i] == 0 || cfg->base_addr_64H[i]) continue;
if (cfg->base_addr_space[i] == PCI_BASE_ADDRESS_SPACE_IO) {
printf(" BAR%d : [ io 0x%"PRIx64" sz 0x%x szmask 0x%x ]\n", i,
libpci_device_iocfg_get_baseaddr(cfg, i),
cfg->base_addr_size[i],
cfg->base_addr_size_mask[i]);
} else {
printf(" BAR%d : [ mem 0x%"PRIx64" sz 0x%x szmask 0x%x %s %s ]\n", i,
libpci_device_iocfg_get_baseaddr(cfg, i),
cfg->base_addr_size[i],
cfg->base_addr_size_mask[i],
cfg->base_addr_type[i] == PCI_BASE_ADDRESS_MEM_TYPE_64 ? "64bit" : "",
cfg->base_addr_prefetchable[i] ? "prefetch" : "");
}
} else {
/* Very verbose and space wasting debug output. */
printf(" BASE_ADDR[%d] ----\n", i);
if (cfg->base_addr[i] == 0 || cfg->base_addr_64H[i]) continue;
printf(" base_addr_space[%d]: 0x%x [%s]\n", i, cfg->base_addr_space[i],
cfg->base_addr_space[i] ? "PCI_BASE_ADDRESS_SPACE_IO" :
"PCI_BASE_ADDRESS_SPACE_MEMORY");
printf(" base_addr_type[%d]: 0x%x [ ", i, cfg->base_addr_type[i]);
if (cfg->base_addr_type[i] == PCI_BASE_ADDRESS_MEM_TYPE_32) printf("32bit ");
if (cfg->base_addr_type[i] == PCI_BASE_ADDRESS_MEM_TYPE_64) printf("64bit ");
if (cfg->base_addr_type[i] == PCI_BASE_ADDRESS_MEM_TYPE_1M) printf("<1M ");
printf("]\n");
printf(" base_addr_prefetchable[%d]: %s\n", i, cfg->base_addr_prefetchable[i]
? "yes" : "no");
printf(" base_addr[%d]: 0x%"PRIx64"\n", i, libpci_device_iocfg_get_baseaddr(cfg, i));
printf(" base_addr_size_mask[%d]: 0x%x\n", i, cfg->base_addr_size_mask[i]);
}
}
}