linux-pci.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
From: Jeremy Linton <jeremy.linton@arm.com>
To: linux-pci@vger.kernel.org
Cc: will@kernel.org, bhelgaas@google.com, robh+dt@kernel.org,
	krzysztof.kozlowski+dt@linaro.org, lpieralisi@kernel.org,
	kw@linux.com, mark.rutland@arm.com, sudeep.holla@arm.com,
	boqun.feng@gmail.com, catalin.marinas@arm.com,
	linux-arm-kernel@lists.infradead.org, devicetree@vger.kernel.org,
	linux-kernel@vger.kernel.org,
	Jeremy Linton <jeremy.linton@arm.com>
Subject: [PATCH 2/4] arm64: PCI: Enable SMC conduit
Date: Mon, 25 Jul 2022 11:39:03 -0500	[thread overview]
Message-ID: <20220725163905.2024437-3-jeremy.linton@arm.com> (raw)
In-Reply-To: <20220725163905.2024437-1-jeremy.linton@arm.com>

Given that most arm64 platforms' PCI implementations need quirks
to deal with problematic config accesses, this is a good place
to apply a firmware abstraction. The ARM PCI Configuration Space
Access Firmware Interface specification details a standard SMC
conduit designed to provide a simple PCI config accessor. This
specification enhances the existing ACPI/PCI abstraction and
expects power, config, etc., is handled by the platform. It also
is very explicit that the resulting config space registers must
behave as is specified by the PCI specification.

Hook the ACPI/PCI config path, and when missing MCFG data is
detected, attempt to probe the SMC conduit. If the conduit
exists and responds to the requested segment,  provided by the
ACPI namespace, attach a custom pci_ecam_ops which redirects
all config read/write requests to the firmware.

The Arm PCI Configuration Space Access Firmware Interface:
https://developer.arm.com/documentation/den0115/latest

Signed-off-by: Jeremy Linton <jeremy.linton@arm.com>
---
 arch/arm64/kernel/pci.c | 109 ++++++++++++++++++++++++++++++++++++++++
 1 file changed, 109 insertions(+)

diff --git a/arch/arm64/kernel/pci.c b/arch/arm64/kernel/pci.c
index 2276689b5411..beb4289a7471 100644
--- a/arch/arm64/kernel/pci.c
+++ b/arch/arm64/kernel/pci.c
@@ -7,6 +7,7 @@
  */
 
 #include <linux/acpi.h>
+#include <linux/arm-smccc.h>
 #include <linux/init.h>
 #include <linux/io.h>
 #include <linux/kernel.h>
@@ -122,6 +123,112 @@ static int pci_acpi_root_prepare_resources(struct acpi_pci_root_info *ci)
 	return status;
 }
 
+int pcie_has_fw_conduit(void)
+{
+	struct arm_smccc_res res;
+
+	if (arm_smccc_1_1_get_conduit() == SMCCC_CONDUIT_NONE)
+		return -EOPNOTSUPP;
+
+	arm_smccc_1_1_invoke(SMCCC_PCI_VERSION, 0, 0, 0, 0, 0, 0, 0, &res);
+	if ((int)res.a0 < 0)
+		return -EOPNOTSUPP;
+
+	arm_smccc_1_1_invoke(SMCCC_PCI_FEATURES,
+			     SMCCC_PCI_WRITE, 0, 0, 0, 0, 0, 0, &res);
+	if ((int)res.a0 < 0)
+		return -EOPNOTSUPP;
+
+	arm_smccc_1_1_invoke(SMCCC_PCI_FEATURES,
+			     SMCCC_PCI_READ, 0, 0, 0, 0, 0, 0, &res);
+	if ((int)res.a0 < 0)
+		return -EOPNOTSUPP;
+
+	arm_smccc_1_1_invoke(SMCCC_PCI_FEATURES,
+			     SMCCC_PCI_SEG_INFO, 0, 0, 0, 0, 0, 0, &res);
+	if ((int)res.a0 < 0)
+		return -EOPNOTSUPP;
+
+	return 0;
+}
+
+static int smccc_pcie_config_read(struct pci_bus *bus, unsigned int devfn,
+				  int where, int size, u32 *val)
+{
+	struct arm_smccc_res res;
+
+	devfn |= bus->number << 8;
+	devfn |= bus->domain_nr << 16;
+
+	arm_smccc_1_1_invoke(SMCCC_PCI_READ, devfn, where, size,
+			     0, 0, 0, 0, &res);
+	if (res.a0) {
+		*val = ~0;
+		return -PCIBIOS_BAD_REGISTER_NUMBER;
+	}
+
+	*val = res.a1;
+	return PCIBIOS_SUCCESSFUL;
+}
+
+static int smccc_pcie_config_write(struct pci_bus *bus, unsigned int devfn,
+				   int where, int size, u32 val)
+{
+	struct arm_smccc_res res;
+
+	devfn |= bus->number << 8;
+	devfn |= bus->domain_nr << 16;
+
+	arm_smccc_1_1_invoke(SMCCC_PCI_WRITE, devfn, where, size, val,
+			     0, 0, 0, &res);
+	if (res.a0)
+		return -PCIBIOS_BAD_REGISTER_NUMBER;
+
+	return PCIBIOS_SUCCESSFUL;
+}
+
+static const struct pci_ecam_ops smccc_pcie_ops = {
+	.pci_ops	= {
+		.read		= smccc_pcie_config_read,
+		.write		= smccc_pcie_config_write,
+	}
+};
+
+
+struct pci_config_window *
+pci_setup_fw_mapping(struct device *dev, u16 seg, struct resource *bus_res)
+{
+	struct arm_smccc_res res;
+	struct pci_config_window *cfg;
+
+	arm_smccc_1_1_invoke(SMCCC_PCI_SEG_INFO, seg, 0, 0, 0, 0, 0, 0, &res);
+	if ((int)res.a0 < 0) {
+		pr_warn("PCI: SMC segment %d doesn't exist\n", seg);
+		return NULL;
+	}
+
+	if (FIELD_GET(SMCCC_PCI_SEG_INFO_START_BUS, res.a1) != bus_res->start ||
+	    FIELD_GET(SMCCC_PCI_SEG_INFO_END_BUS, res.a1) != bus_res->end) {
+		pr_warn("PCI: SMC segment %d doesn't match ACPI description\n", seg);
+		return NULL;
+	}
+
+	cfg = kzalloc(sizeof(*cfg), GFP_KERNEL);
+	if (!cfg)
+		return NULL;
+
+	cfg->parent = dev;
+	cfg->ops = &smccc_pcie_ops;
+	cfg->busr.start = FIELD_GET(SMCCC_PCI_SEG_INFO_START_BUS, res.a1);
+	cfg->busr.end = FIELD_GET(SMCCC_PCI_SEG_INFO_END_BUS, res.a1);
+	cfg->busr.flags = IORESOURCE_BUS;
+	cfg->res.name = "PCI SMCCC";
+
+	pr_info("PCI: SMC conduit attached to segment %d\n", seg);
+
+	return cfg;
+}
+
 /*
  * Lookup the bus range for the domain in MCFG, and set up config space
  * mapping.
@@ -140,6 +247,8 @@ pci_acpi_setup_ecam_mapping(struct acpi_pci_root *root)
 
 	ret = pci_mcfg_lookup(root, &cfgres, &ecam_ops);
 	if (ret) {
+		if (!pcie_has_fw_conduit())
+			return pci_setup_fw_mapping(dev, seg, bus_res);
 		dev_err(dev, "%04x:%pR ECAM region not found\n", seg, bus_res);
 		return NULL;
 	}
-- 
2.37.1


  parent reply	other threads:[~2022-07-25 16:39 UTC|newest]

Thread overview: 9+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2022-07-25 16:39 [PATCH 0/4] PCI SMC conduit, now with DT support Jeremy Linton
2022-07-25 16:39 ` [PATCH 1/4] arm64: smccc: Add PCI SMCCCs Jeremy Linton
2022-07-25 16:39 ` Jeremy Linton [this message]
2022-07-25 16:39 ` [PATCH 3/4] PCI: host-generic: Add firmware managed config ops Jeremy Linton
2022-07-25 16:39 ` [PATCH 4/4] dt-bindings: PCI: Note the use of pci-host-smc-generic Jeremy Linton
2022-07-26 11:40 ` [PATCH 0/4] PCI SMC conduit, now with DT support Will Deacon
2022-07-28 17:20   ` Jeremy Linton
2022-08-16  7:59     ` Catalin Marinas
2022-08-18 21:55       ` Pali Rohár

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=20220725163905.2024437-3-jeremy.linton@arm.com \
    --to=jeremy.linton@arm.com \
    --cc=bhelgaas@google.com \
    --cc=boqun.feng@gmail.com \
    --cc=catalin.marinas@arm.com \
    --cc=devicetree@vger.kernel.org \
    --cc=krzysztof.kozlowski+dt@linaro.org \
    --cc=kw@linux.com \
    --cc=linux-arm-kernel@lists.infradead.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=linux-pci@vger.kernel.org \
    --cc=lpieralisi@kernel.org \
    --cc=mark.rutland@arm.com \
    --cc=robh+dt@kernel.org \
    --cc=sudeep.holla@arm.com \
    --cc=will@kernel.org \
    /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 a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).