All of lore.kernel.org
 help / color / mirror / Atom feed
From: "Sean O. Stalley" <sean.stalley@intel.com>
To: bhelgaas@google.com, rajatxjain@gmail.com, mst@redhat.com,
	zajec5@gmail.com, gong.chen@linux.intel.com,
	linux-pci@vger.kernel.org, linux-kernel@vger.kernel.org,
	linux-api@vger.kernel.org
Cc: sean.stalley@intel.com
Subject: [PATCH 2/2] PCI: Add support for Enhanced Allocation devices
Date: Thu, 20 Aug 2015 09:59:07 -0700	[thread overview]
Message-ID: <1440089947-2839-3-git-send-email-sean.stalley@intel.com> (raw)
In-Reply-To: <1440089947-2839-1-git-send-email-sean.stalley@intel.com>

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 <sean.stalley@intel.com>
---
 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


WARNING: multiple messages have this Message-ID (diff)
From: "Sean O. Stalley" <sean.stalley-ral2JQCrhuEAvxtiuMwx3w@public.gmane.org>
To: bhelgaas-hpIqsD4AKlfQT0dZR+AlfA@public.gmane.org,
	rajatxjain-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org,
	mst-H+wXaHxf7aLQT0dZR+AlfA@public.gmane.org,
	zajec5-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org,
	gong.chen-VuQAYsv1563Yd54FQh9/CA@public.gmane.org,
	linux-pci-u79uwXL29TY76Z2rM5mHXA@public.gmane.org,
	linux-kernel-u79uwXL29TY76Z2rM5mHXA@public.gmane.org,
	linux-api-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
Cc: sean.stalley-ral2JQCrhuEAvxtiuMwx3w@public.gmane.org
Subject: [PATCH 2/2] PCI: Add support for Enhanced Allocation devices
Date: Thu, 20 Aug 2015 09:59:07 -0700	[thread overview]
Message-ID: <1440089947-2839-3-git-send-email-sean.stalley@intel.com> (raw)
In-Reply-To: <1440089947-2839-1-git-send-email-sean.stalley-ral2JQCrhuEAvxtiuMwx3w@public.gmane.org>

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 <sean.stalley-ral2JQCrhuEAvxtiuMwx3w@public.gmane.org>
---
 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

  parent reply	other threads:[~2015-08-20 17:01 UTC|newest]

Thread overview: 18+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2015-08-20 16:59 [PATCH 0/2] PCI: Add support for PCI Enhanced Allocation "BARs" Sean O. Stalley
2015-08-20 16:59 ` Sean O. Stalley
2015-08-20 16:59 ` [PATCH 1/2] PCI: Add Enhanced Allocation register entries Sean O. Stalley
2015-08-20 16:59 ` Sean O. Stalley [this message]
2015-08-20 16:59   ` [PATCH 2/2] PCI: Add support for Enhanced Allocation devices Sean O. Stalley
2015-09-01 23:14   ` Yinghai Lu
2015-09-02 17:46     ` Sean O. Stalley
2015-09-02 17:46       ` Sean O. Stalley
2015-09-02 19:25       ` Bjorn Helgaas
2015-09-02 19:25         ` Bjorn Helgaas
2015-09-02 20:01         ` Sean O. Stalley
2015-09-02 21:21           ` Bjorn Helgaas
2015-09-02 21:21             ` Bjorn Helgaas
2015-09-03  0:29             ` Sean O. Stalley
2015-09-03  0:29               ` Sean O. Stalley
2015-09-03 14:46               ` Bjorn Helgaas
2015-09-03 18:23                 ` Sean O. Stalley
2015-09-03 18:23                   ` Sean O. Stalley

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=1440089947-2839-3-git-send-email-sean.stalley@intel.com \
    --to=sean.stalley@intel.com \
    --cc=bhelgaas@google.com \
    --cc=gong.chen@linux.intel.com \
    --cc=linux-api@vger.kernel.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=linux-pci@vger.kernel.org \
    --cc=mst@redhat.com \
    --cc=rajatxjain@gmail.com \
    --cc=zajec5@gmail.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.