summaryrefslogtreecommitdiffstats
path: root/sys/dev/pci/pci.c
diff options
context:
space:
mode:
Diffstat (limited to 'sys/dev/pci/pci.c')
-rw-r--r--sys/dev/pci/pci.c270
1 files changed, 270 insertions, 0 deletions
diff --git a/sys/dev/pci/pci.c b/sys/dev/pci/pci.c
index 81caa48..2f4408e 100644
--- a/sys/dev/pci/pci.c
+++ b/sys/dev/pci/pci.c
@@ -63,6 +63,11 @@ __FBSDID("$FreeBSD$");
#include <dev/pci/pcivar.h>
#include <dev/pci/pci_private.h>
+#ifdef PCI_IOV
+#include <sys/nv.h>
+#include <dev/pci/pci_iov_private.h>
+#endif
+
#include <dev/usb/controller/xhcireg.h>
#include <dev/usb/controller/ehcireg.h>
#include <dev/usb/controller/ohcireg.h>
@@ -694,6 +699,81 @@ pci_fill_devinfo(device_t pcib, int d, int b, int s, int f, uint16_t vid,
#undef REG
static void
+pci_ea_fill_info(device_t pcib, pcicfgregs *cfg)
+{
+#define REG(n, w) PCIB_READ_CONFIG(pcib, cfg->bus, cfg->slot, cfg->func, \
+ cfg->ea.ea_location + (n), w)
+ int num_ent;
+ int ptr;
+ int a, b;
+ uint32_t val;
+ int ent_size;
+ uint32_t dw[4];
+ uint64_t base, max_offset;
+ struct pci_ea_entry *eae;
+
+ if (cfg->ea.ea_location == 0)
+ return;
+
+ STAILQ_INIT(&cfg->ea.ea_entries);
+
+ /* Determine the number of entries */
+ num_ent = REG(PCIR_EA_NUM_ENT, 2);
+ num_ent &= PCIM_EA_NUM_ENT_MASK;
+
+ /* Find the first entry to care of */
+ ptr = PCIR_EA_FIRST_ENT;
+
+ /* Skip DWORD 2 for type 1 functions */
+ if ((cfg->hdrtype & PCIM_HDRTYPE) == PCIM_HDRTYPE_BRIDGE)
+ ptr += 4;
+
+ for (a = 0; a < num_ent; a++) {
+
+ eae = malloc(sizeof(*eae), M_DEVBUF, M_WAITOK | M_ZERO);
+ eae->eae_cfg_offset = cfg->ea.ea_location + ptr;
+
+ /* Read a number of dwords in the entry */
+ val = REG(ptr, 4);
+ ptr += 4;
+ ent_size = (val & PCIM_EA_ES);
+
+ for (b = 0; b < ent_size; b++) {
+ dw[b] = REG(ptr, 4);
+ ptr += 4;
+ }
+
+ eae->eae_flags = val;
+ eae->eae_bei = (PCIM_EA_BEI & val) >> PCIM_EA_BEI_OFFSET;
+
+ base = dw[0] & PCIM_EA_FIELD_MASK;
+ max_offset = dw[1] | ~PCIM_EA_FIELD_MASK;
+ b = 2;
+ if (((dw[0] & PCIM_EA_IS_64) != 0) && (b < ent_size)) {
+ base |= (uint64_t)dw[b] << 32UL;
+ b++;
+ }
+ if (((dw[1] & PCIM_EA_IS_64) != 0)
+ && (b < ent_size)) {
+ max_offset |= (uint64_t)dw[b] << 32UL;
+ b++;
+ }
+
+ eae->eae_base = base;
+ eae->eae_max_offset = max_offset;
+
+ STAILQ_INSERT_TAIL(&cfg->ea.ea_entries, eae, eae_link);
+
+ if (bootverbose) {
+ printf("PCI(EA) dev %04x:%04x, bei %d, flags #%x, base #%jx, max_offset #%jx\n",
+ cfg->vendor, cfg->device, eae->eae_bei, eae->eae_flags,
+ (uintmax_t)eae->eae_base, (uintmax_t)eae->eae_max_offset);
+ }
+ }
+}
+#undef REG
+
+static void
pci_read_cap(device_t pcib, pcicfgregs *cfg)
{
#define REG(n, w) PCIB_READ_CONFIG(pcib, cfg->bus, cfg->slot, cfg->func, n, w)
@@ -830,6 +910,10 @@ pci_read_cap(device_t pcib, pcicfgregs *cfg)
val = REG(ptr + PCIER_FLAGS, 2);
cfg->pcie.pcie_type = val & PCIEM_FLAGS_TYPE;
break;
+ case PCIY_EA: /* Enhanced Allocation */
+ cfg->ea.ea_location = ptr;
+ pci_ea_fill_info(pcib, cfg);
+ break;
default:
break;
}
@@ -3504,6 +3588,176 @@ pci_alloc_secbus(device_t dev, device_t child, int *rid, rman_res_t start,
}
#endif
+static int
+pci_ea_bei_to_rid(device_t dev, int bei)
+{
+#ifdef PCI_IOV
+ struct pci_devinfo *dinfo;
+ int iov_pos;
+ struct pcicfg_iov *iov;
+
+ dinfo = device_get_ivars(dev);
+ iov = dinfo->cfg.iov;
+ if (iov != NULL)
+ iov_pos = iov->iov_pos;
+ else
+ iov_pos = 0;
+#endif
+
+ /* Check if matches BAR */
+ if ((bei >= PCIM_EA_BEI_BAR_0) &&
+ (bei <= PCIM_EA_BEI_BAR_5))
+ return (PCIR_BAR(bei));
+
+ /* Check ROM */
+ if (bei == PCIM_EA_BEI_ROM)
+ return (PCIR_BIOS);
+
+#ifdef PCI_IOV
+ /* Check if matches VF_BAR */
+ if ((iov != NULL) && (bei >= PCIM_EA_BEI_VF_BAR_0) &&
+ (bei <= PCIM_EA_BEI_VF_BAR_5))
+ return (PCIR_SRIOV_BAR(bei - PCIM_EA_BEI_VF_BAR_0) +
+ iov_pos);
+#endif
+
+ return (-1);
+}
+
+int
+pci_ea_is_enabled(device_t dev, int rid)
+{
+ struct pci_ea_entry *ea;
+ struct pci_devinfo *dinfo;
+
+ dinfo = device_get_ivars(dev);
+
+ STAILQ_FOREACH(ea, &dinfo->cfg.ea.ea_entries, eae_link) {
+ if (pci_ea_bei_to_rid(dev, ea->eae_bei) == rid)
+ return ((ea->eae_flags & PCIM_EA_ENABLE) > 0);
+ }
+
+ return (0);
+}
+
+void
+pci_add_resources_ea(device_t bus, device_t dev, int alloc_iov)
+{
+ struct pci_ea_entry *ea;
+ struct pci_devinfo *dinfo;
+ pci_addr_t start, end, count;
+ struct resource_list *rl;
+ int type, flags, rid;
+ struct resource *res;
+ uint32_t tmp;
+#ifdef PCI_IOV
+ struct pcicfg_iov *iov;
+#endif
+
+ dinfo = device_get_ivars(dev);
+ rl = &dinfo->resources;
+ flags = 0;
+
+#ifdef PCI_IOV
+ iov = dinfo->cfg.iov;
+#endif
+
+ if (dinfo->cfg.ea.ea_location == 0)
+ return;
+
+ STAILQ_FOREACH(ea, &dinfo->cfg.ea.ea_entries, eae_link) {
+
+ /*
+ * TODO: Ignore EA-BAR if is not enabled.
+ * Currently the EA implementation supports
+ * only situation, where EA structure contains
+ * predefined entries. In case they are not enabled
+ * leave them unallocated and proceed with
+ * a legacy-BAR mechanism.
+ */
+ if ((ea->eae_flags & PCIM_EA_ENABLE) == 0)
+ continue;
+
+ switch ((ea->eae_flags & PCIM_EA_PP) >> PCIM_EA_PP_OFFSET) {
+ case PCIM_EA_P_MEM_PREFETCH:
+ case PCIM_EA_P_VF_MEM_PREFETCH:
+ flags = RF_PREFETCHABLE;
+ case PCIM_EA_P_VF_MEM:
+ case PCIM_EA_P_MEM:
+ type = SYS_RES_MEMORY;
+ break;
+ case PCIM_EA_P_IO:
+ type = SYS_RES_IOPORT;
+ break;
+ default:
+ continue;
+ }
+
+ if (alloc_iov != 0) {
+#ifdef PCI_IOV
+ /* Allocating IOV, confirm BEI matches */
+ if ((ea->eae_bei < PCIM_EA_BEI_VF_BAR_0) ||
+ (ea->eae_bei > PCIM_EA_BEI_VF_BAR_5))
+ continue;
+#else
+ continue;
+#endif
+ } else {
+ /* Allocating BAR, confirm BEI matches */
+ if (((ea->eae_bei < PCIM_EA_BEI_BAR_0) ||
+ (ea->eae_bei > PCIM_EA_BEI_BAR_5)) &&
+ (ea->eae_bei != PCIM_EA_BEI_ROM))
+ continue;
+ }
+
+ rid = pci_ea_bei_to_rid(dev, ea->eae_bei);
+ if (rid < 0)
+ continue;
+
+ /* Skip resources already allocated by EA */
+ if ((resource_list_find(rl, SYS_RES_MEMORY, rid) != NULL) ||
+ (resource_list_find(rl, SYS_RES_IOPORT, rid) != NULL))
+ continue;
+
+ start = ea->eae_base;
+ count = ea->eae_max_offset + 1;
+#ifdef PCI_IOV
+ if (iov != NULL)
+ count = count * iov->iov_num_vfs;
+#endif
+ end = start + count - 1;
+ if (count == 0)
+ continue;
+
+ resource_list_add(rl, type, rid, start, end, count);
+ res = resource_list_reserve(rl, bus, dev, type, &rid, start, end, count,
+ flags);
+ if (res == NULL) {
+ resource_list_delete(rl, type, rid);
+
+ /*
+ * Failed to allocate using EA, disable entry.
+ * Another attempt to allocation will be performed
+ * further, but this time using legacy BAR registers
+ */
+ tmp = pci_read_config(dev, ea->eae_cfg_offset, 4);
+ tmp &= ~PCIM_EA_ENABLE;
+ pci_write_config(dev, ea->eae_cfg_offset, tmp, 4);
+
+ /*
+ * Disabling entry might fail in case it is hardwired.
+ * Read flags again to match current status.
+ */
+ ea->eae_flags = pci_read_config(dev, ea->eae_cfg_offset, 4);
+
+ continue;
+ }
+
+ /* As per specification, fill BAR with zeros */
+ pci_write_config(dev, rid, 0, 4);
+ }
+}
+
void
pci_add_resources(device_t bus, device_t dev, int force, uint32_t prefetchmask)
{
@@ -3519,6 +3773,9 @@ pci_add_resources(device_t bus, device_t dev, int force, uint32_t prefetchmask)
rl = &dinfo->resources;
devid = (cfg->device << 16) | cfg->vendor;
+ /* Allocate resources using Enhanced Allocation */
+ pci_add_resources_ea(bus, dev, 0);
+
/* ATA devices needs special map treatment */
if ((pci_get_class(dev) == PCIC_STORAGE) &&
(pci_get_subclass(dev) == PCIS_STORAGE_IDE) &&
@@ -3528,6 +3785,14 @@ pci_add_resources(device_t bus, device_t dev, int force, uint32_t prefetchmask)
pci_ata_maps(bus, dev, rl, force, prefetchmask);
else
for (i = 0; i < cfg->nummaps;) {
+ /* Skip resources already managed by EA */
+ if ((resource_list_find(rl, SYS_RES_MEMORY, PCIR_BAR(i)) != NULL) ||
+ (resource_list_find(rl, SYS_RES_IOPORT, PCIR_BAR(i)) != NULL) ||
+ pci_ea_is_enabled(dev, PCIR_BAR(i))) {
+ i++;
+ continue;
+ }
+
/*
* Skip quirked resources.
*/
@@ -4629,6 +4894,11 @@ pci_reserve_map(device_t dev, device_t child, int type, int *rid,
int mapsize;
res = NULL;
+
+ /* If rid is managed by EA, ignore it */
+ if (pci_ea_is_enabled(child, *rid))
+ goto out;
+
pm = pci_find_bar(child, *rid);
if (pm != NULL) {
/* This is a BAR that we failed to allocate earlier. */
OpenPOWER on IntegriCloud