2015-08-20 17:01:44

by Sean O. Stalley

[permalink] [raw]
Subject: [PATCH 0/2] PCI: Add support for PCI Enhanced Allocation "BARs"

PCI Enhanced Allocation is a new method of allocating MMIO & IO
resources for PCI devices & bridges. It can be used instead
of the traditional PCI method of using BARs.

EA entries are hardware-initialized to a fixed address.
Unlike BARs, regions described by EA are cannot be moved.
Because of this, only devices which are permanently connected to
the PCI bus can use EA. A removable PCI card must not use EA.

This patchset adds support for using EA entries instead of BARs.

The Enhanced Allocation ECN is publicly available here:
https://www.pcisig.com/specifications/conventional/ECN_Enhanced_Allocation_23_Oct_2014_Final.pdf

Sean O. Stalley (2):
PCI: Add Enhanced Allocation register entries
PCI: Add parsing of Enhanced Allocation entries

drivers/pci/pci.c | 219 ++++++++++++++++++++++++++++++++++++++++++
drivers/pci/pci.h | 1 +
drivers/pci/probe.c | 3 +
include/uapi/linux/pci_regs.h | 40 +++++++-
4 files changed, 262 insertions(+), 1 deletion(-)

--
1.9.1


2015-08-20 17:01:47

by Sean O. Stalley

[permalink] [raw]
Subject: [PATCH 1/2] PCI: Add Enhanced Allocation register entries

Add registers defined in PCI-SIG's Enhanced allocation ECN.

Signed-off-by: Sean O. Stalley <[email protected]>
---
include/uapi/linux/pci_regs.h | 40 +++++++++++++++++++++++++++++++++++++++-
1 file changed, 39 insertions(+), 1 deletion(-)

diff --git a/include/uapi/linux/pci_regs.h b/include/uapi/linux/pci_regs.h
index 413417f..084ce98 100644
--- a/include/uapi/linux/pci_regs.h
+++ b/include/uapi/linux/pci_regs.h
@@ -216,7 +216,8 @@
#define PCI_CAP_ID_MSIX 0x11 /* MSI-X */
#define PCI_CAP_ID_SATA 0x12 /* SATA Data/Index Conf. */
#define PCI_CAP_ID_AF 0x13 /* PCI Advanced Features */
-#define PCI_CAP_ID_MAX PCI_CAP_ID_AF
+#define PCI_CAP_ID_EA 0x14 /* PCI Enhanced Allocation */
+#define PCI_CAP_ID_MAX PCI_CAP_ID_EA
#define PCI_CAP_LIST_NEXT 1 /* Next capability in the list */
#define PCI_CAP_FLAGS 2 /* Capability defined flags (16 bits) */
#define PCI_CAP_SIZEOF 4
@@ -353,6 +354,43 @@
#define PCI_AF_STATUS_TP 0x01
#define PCI_CAP_AF_SIZEOF 6 /* size of AF registers */

+/* PCI Enhanced Allocation registers */
+
+#define PCI_EA_NUM_ENT 2 /* Number of Capability Entries */
+#define PCI_EA_NUM_ENT_MASK 0x3f /* Num Entries Mask */
+#define PCI_EA_FIRST_ENT 4 /* First EA Entry in List */
+#define PCI_EA_FIRST_ENT_BRIDGE 8 /* First EA Entry for Bridges */
+#define PCI_EA_ES 0x7 /* Entry Size */
+#define PCI_EA_BEI(x) (((x) >> 4) & 0xf) /* BAR Equivalent Indicator */
+/* 0-5 map to BARs 0-5 respectively */
+#define PCI_EA_BEI_BRIDGE 6 /* Resource behind bridge */
+#define PCI_EA_BEI_ENI 7 /* Equivalent Not Indicated */
+#define PCI_EA_BEI_ROM 8 /* Expansion ROM */
+/* 9-14 map to VF BARs 0-5 respectively */
+#define PCI_EA_BEI_RESERVED 15 /* Reserved - Treat like ENI */
+
+#define PCI_EA_PP(x) (((x) >> 8) & 0xff) /* Primary Properties */
+#define PCI_EA_SP(x) (((x) >> 16) & 0xff) /* Secondary Properties */
+#define PCI_EA_P_MEM 0x00 /* Non-Prefetch Memory */
+#define PCI_EA_P_MEM_PREFETCH 0x01 /* Prefetchable Memory */
+#define PCI_EA_P_IO 0x02 /* I/O Space */
+#define PCI_EA_P_VIRT_MEM_PREFETCH 0x03 /* VF Prefetchable Memory */
+#define PCI_EA_P_VIRT_MEM 0x04 /* VF Non-Prefetch Memory */
+#define PCI_EA_P_BRIDGE_MEM 0x05 /* Bridge Non-Prefetch Memory */
+#define PCI_EA_P_BRIDGE_MEM_PREFETCH 0x06 /* Bridge Prefetchable Memory */
+#define PCI_EA_P_BRIDGE_IO 0x07 /* Bridge I/O Space */
+/* 0x08-0xfc reserved */
+#define PCI_EA_P_MEM_RESERVED 0xfd /* Reserved Memory */
+#define PCI_EA_P_IO_RESERVED 0xfe /* Reserved I/O Space */
+#define PCI_EA_P_UNAVAILABLE 0xff /* Entry Unavailable */
+#define PCI_EA_WRITEABLE BIT(30) /* Writable, 1 = RW, 0 = HwInit */
+#define PCI_EA_ENABLE BIT(31) /* Enable for this entry */
+#define PCI_EA_BASE 4 /* Base Address Offset */
+#define PCI_EA_MAX_OFFSET 8 /* MaxOffset (resource length) */
+/* bit 0 is reserved */
+#define PCI_EA_IS_64 BIT(1) /* 64-bit field flag */
+#define PCI_EA_FIELD_MASK 0xfffffffc /* For Base & Max Offset */
+
/* PCI-X registers (Type 0 (non-bridge) devices) */

#define PCI_X_CMD 2 /* Modes & Features */
--
1.9.1

2015-08-20 17:01:58

by Sean O. Stalley

[permalink] [raw]
Subject: [PATCH 2/2] PCI: Add support for Enhanced Allocation devices

Add support for devices using Enhanced Allocation entries instead of BARs.
This patch allows the kernel to parse the EA Extended Capability structure
in PCI configspace and claim the BAR-equivalent resources.

Signed-off-by: Sean O. Stalley <[email protected]>
---
drivers/pci/pci.c | 219 ++++++++++++++++++++++++++++++++++++++++++++++++++++
drivers/pci/pci.h | 1 +
drivers/pci/probe.c | 3 +
3 files changed, 223 insertions(+)

diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c
index 0008c95..c8217a8 100644
--- a/drivers/pci/pci.c
+++ b/drivers/pci/pci.c
@@ -2134,6 +2134,225 @@ void pci_pm_init(struct pci_dev *dev)
}
}

+static unsigned long pci_ea_set_flags(struct pci_dev *dev, u8 prop)
+{
+ unsigned long flags = IORESOURCE_PCI_FIXED;
+
+ switch (prop) {
+ case PCI_EA_P_MEM:
+ flags |= IORESOURCE_MEM;
+ break;
+ case PCI_EA_P_MEM_PREFETCH:
+ flags |= IORESOURCE_MEM | IORESOURCE_PREFETCH;
+ break;
+ case PCI_EA_P_IO:
+ flags |= IORESOURCE_IO;
+ break;
+ default:
+ dev_warn(&dev->dev, "%s: Property type %x not supported\n",
+ __func__, prop);
+ return 0;
+ }
+
+ return flags;
+}
+
+static struct resource *pci_ea_get_parent_resource(struct pci_dev *dev,
+ struct resource *res)
+{
+ struct resource *parent;
+
+ parent = pci_find_parent_resource(dev, res);
+ if (parent)
+ return parent;
+
+ /* for resources not claimed by a bridge */
+ if (res->flags & IORESOURCE_MEM)
+ return &iomem_resource;
+
+ if (res->flags & IORESOURCE_IO)
+ return &ioport_resource;
+
+ return NULL;
+}
+
+/* claim the memory for this device in the proper location */
+static void pci_ea_claim_resource(struct pci_dev *dev, struct resource *res)
+{
+ struct resource *parent;
+ struct resource *conflict;
+
+ parent = pci_ea_get_parent_resource(dev, res);
+ if (!parent) {
+ dev_warn(&dev->dev, "can't find parent resource for EA entry %s %pR\n",
+ res->name, res);
+ return;
+ }
+
+ /* claim the appropriate resource */
+ conflict = request_resource_conflict(parent, res);
+ if (conflict) {
+ dev_warn(&dev->dev, "can't claim EA entry %s %pR: address conflict with %s %pR\n",
+ res->name, res, conflict->name, conflict);
+ }
+}
+
+static struct resource *pci_ea_get_resource(struct pci_dev *dev, u8 bei)
+{
+ if (bei <= PCI_STD_RESOURCE_END)
+ return &dev->resource[bei];
+ else if (bei == PCI_EA_BEI_ROM)
+ return &dev->resource[PCI_ROM_RESOURCE];
+ else
+ return NULL;
+}
+
+/* Read an Enhanced Allocation (EA) entry */
+static int pci_ea_read(struct pci_dev *dev, int offset)
+{
+ struct resource *res;
+ int ent_offset = offset;
+ int ent_size;
+ resource_size_t start;
+ resource_size_t end;
+ unsigned long flags;
+ u32 dw0;
+ u32 base;
+ u32 max_offset;
+ bool support_64 = (sizeof(resource_size_t) >= 8);
+
+ pci_read_config_dword(dev, ent_offset, &dw0);
+ ent_offset += 4;
+
+ /* Entry size field indicates DWORDs after 1st */
+ ent_size = ((dw0 & PCI_EA_ES) + 1) << 2;
+
+ if (!(dw0 & PCI_EA_ENABLE)) {
+ dev_err(&dev->dev, "%s: Entry not enabled\n", __func__);
+ goto out;
+ }
+
+ res = pci_ea_get_resource(dev, PCI_EA_BEI(dw0));
+ if (!res) {
+ dev_err(&dev->dev, "%s: Unsupported EA entry BEI\n", __func__);
+ goto out;
+ }
+
+ flags = pci_ea_set_flags(dev, PCI_EA_PP(dw0));
+ if (!flags)
+ flags = pci_ea_set_flags(dev, PCI_EA_SP(dw0));
+ if (!flags) {
+ dev_err(&dev->dev, "%s: Entry EA properties not supported\n",
+ __func__);
+ goto out;
+ }
+
+ /* Read Base */
+ pci_read_config_dword(dev, ent_offset, &base);
+ start = (base & PCI_EA_FIELD_MASK);
+ ent_offset += 4;
+
+ /* Read MaxOffset */
+ pci_read_config_dword(dev, ent_offset, &max_offset);
+ ent_offset += 4;
+
+ /* Read Base MSBs (if 64-bit entry) */
+ if (base & PCI_EA_IS_64) {
+ u32 base_upper;
+
+ pci_read_config_dword(dev, ent_offset, &base_upper);
+ ent_offset += 4;
+
+ flags |= IORESOURCE_MEM_64;
+
+ /* entry starts above 32-bit boundary, can't use */
+ if (!support_64 && base_upper)
+ goto out;
+
+ if (support_64)
+ start |= ((u64)base_upper << 32);
+ }
+
+ dev_dbg(&dev->dev, "%s: start = %pa\n", __func__, &start);
+
+ end = start + (max_offset | 0x03);
+
+ /* Read MaxOffset MSBs (if 64-bit entry) */
+ if (max_offset & PCI_EA_IS_64) {
+ u32 max_offset_upper;
+
+ pci_read_config_dword(dev, ent_offset, &max_offset_upper);
+ ent_offset += 4;
+
+ flags |= IORESOURCE_MEM_64;
+
+ /* entry too big, can't use */
+ if (!support_64 && max_offset_upper)
+ goto out;
+
+ if (support_64)
+ end += ((u64)max_offset_upper << 32);
+ }
+
+ dev_dbg(&dev->dev, "%s: end = %pa\n", __func__, &end);
+
+ if (end < start) {
+ dev_err(&dev->dev, "EA Entry crosses address boundary\n");
+ goto out;
+ }
+
+ if (ent_size != ent_offset - offset) {
+ dev_err(&dev->dev, "EA entry size does not match length read\n"
+ "(Entry Size:%u Length Read:%u)\n",
+ ent_size, ent_offset - offset);
+ goto out;
+ }
+
+ res->name = pci_name(dev);
+ res->start = start;
+ res->end = end;
+ res->flags = flags;
+
+ pci_ea_claim_resource(dev, res);
+
+out:
+ return offset + ent_size;
+}
+
+/* Enhanced Allocation Initalization */
+void pci_ea_init(struct pci_dev *dev)
+{
+ int ea;
+ u8 num_ent;
+ int offset;
+ int i;
+
+ /* find PCI EA capability in list */
+ ea = pci_find_capability(dev, PCI_CAP_ID_EA);
+ if (!ea)
+ return;
+
+ dev_dbg(&dev->dev, "%s: capability found!\n", __func__);
+
+ /* determine the number of entries */
+ pci_bus_read_config_byte(dev->bus, dev->devfn, ea + PCI_EA_NUM_ENT,
+ &num_ent);
+ num_ent &= PCI_EA_NUM_ENT_MASK;
+
+ offset = ea + PCI_EA_FIRST_ENT;
+
+ /* Skip DWORD 2 for type 1 functions */
+ if (dev->hdr_type == PCI_HEADER_TYPE_BRIDGE)
+ offset += 4;
+ /* TODO: Support fixed bus numbers */
+
+ for (i = 0; i < num_ent; ++i) {
+ /* parse each EA entry */
+ dev_dbg(&dev->dev, "%s: parsing entry %i...\n", __func__, i);
+ offset = pci_ea_read(dev, offset);
+ }
+}
+
static void pci_add_saved_cap(struct pci_dev *pci_dev,
struct pci_cap_saved_state *new_cap)
{
diff --git a/drivers/pci/pci.h b/drivers/pci/pci.h
index 4ff0ff1..92fbef0 100644
--- a/drivers/pci/pci.h
+++ b/drivers/pci/pci.h
@@ -76,6 +76,7 @@ bool pci_dev_keep_suspended(struct pci_dev *dev);
void pci_config_pm_runtime_get(struct pci_dev *dev);
void pci_config_pm_runtime_put(struct pci_dev *dev);
void pci_pm_init(struct pci_dev *dev);
+void pci_ea_init(struct pci_dev *dev);
void pci_allocate_cap_save_buffers(struct pci_dev *dev);
void pci_free_cap_save_buffers(struct pci_dev *dev);

diff --git a/drivers/pci/probe.c b/drivers/pci/probe.c
index cefd636..4cadf35 100644
--- a/drivers/pci/probe.c
+++ b/drivers/pci/probe.c
@@ -1522,6 +1522,9 @@ static struct pci_dev *pci_scan_device(struct pci_bus *bus, int devfn)

static void pci_init_capabilities(struct pci_dev *dev)
{
+ /* Enhanced Allocation */
+ pci_ea_init(dev);
+
/* MSI/MSI-X list */
pci_msi_init_pci_dev(dev);

--
1.9.1