Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 96fb8ce7 authored by Tony Truong's avatar Tony Truong
Browse files

msm: pcie: add IOMMU configuration support for root complex



If SMMU is present and stage 1 is enabled then root complex
needs to attach and configure the IOMMU based on its
requirements. Add support in PCIe bus driver to get
root complex requirements from devicetree.

Change-Id: I57c1ca60d2eb1e77bb95d809298507528c910eff
Signed-off-by: default avatarTony Truong <truong@codeaurora.org>
parent f136459e
Loading
Loading
Loading
Loading
+13 −2
Original line number Diff line number Diff line
@@ -97,7 +97,6 @@ Optional Properties:
  - iommus: the phandle and stream IDs for the SMMU used by this root
    complex. This should be used in separate nodes from the main root
    complex nodes, and is the only property needed in that case.
  - qcom,smmu-exist: PCIe uses a SMMU.
  - qcom,smmu-sid-base: The base SMMU SID that PCIe bus driver will use to calculate
    and assign for each endpoint.
  - qcom,ep-latency: The time (unit: ms) to wait for the PCIe endpoint to become
@@ -141,6 +140,17 @@ Required properties:
  determined by pci bus topology. Assign the other cells 0 since they are not
  used.

Optional properties:
  - qcom,iommu-cfg: Determines whether PCIe bus driver is required to configure
    SMMU that sits behind the PCIe controller.
	Bit mask:
	BIT(0) : Indicates if SMMU is present
	BIT(1) : Set IOMMU attribute S1_BYPASS
	BIT(2) : Set IOMMU attribute FAST
	BIT(3) : Set IOMMU attribute ATOMIC
	BIT(4) : Set IOMMU attribute FORCE COHERENT
  - qcom,iommu-range: Pair of values describing iova base and size to allocate.

Example:

	pcie0: qcom,pcie@fc520000 {
@@ -282,7 +292,6 @@ Example:
		qcom,msi-gicm-base = <0x160>;
		qcom,ext-ref-clk;
		qcom,tlp-rd-size = <0x5>;
		qcom,smmu-exist;
		qcom,smmu-sid-base = <0x1480>;
		qcom,ep-latency = <100>;
		qcom,switch-latency = <100>;
@@ -320,5 +329,7 @@ Example:

		pcie_rc0: pcie_rc0 {
			reg = <0x0 0x0 0x0 0x0 0x0>;
			qcom,iommu-cfg = <0x3> /* SMMU PRESENT. SET S1 BYPASS */
			qcom,iommu-range = <0x0 0x10000000 0x0 0x40000000>;
		};
	};
+170 −10
Original line number Diff line number Diff line
@@ -26,6 +26,7 @@
#include <linux/of_pci.h>
#include <linux/pci.h>
#include <linux/iommu.h>
#include <asm/dma-iommu.h>
#include <linux/platform_device.h>
#include <linux/regulator/consumer.h>
#include <dt-bindings/regulator/qcom,rpmh-regulator.h>
@@ -172,6 +173,12 @@
#define GEN2_SPEED 0x2
#define GEN3_SPEED 0x3

#define MSM_PCIE_IOMMU_PRESENT BIT(0)
#define MSM_PCIE_IOMMU_S1_BYPASS BIT(1)
#define MSM_PCIE_IOMMU_FAST BIT(2)
#define MSM_PCIE_IOMMU_ATOMIC BIT(3)
#define MSM_PCIE_IOMMU_FORCE_COHERENT BIT(4)

#define PHY_READY_TIMEOUT_COUNT		   10
#define XMLH_LINK_UP				  0x400
#define MAX_LINK_RETRIES 5
@@ -587,7 +594,6 @@ struct msm_pcie_dev_t {
	bool				clk_power_manage_en;
	bool				 aux_clk_sync;
	bool				aer_enable;
	bool				smmu_exist;
	uint32_t			smmu_sid_base;
	uint32_t			   n_fts;
	uint32_t			max_link_speed;
@@ -652,6 +658,9 @@ struct msm_pcie_dev_t {
struct msm_root_dev_t {
	struct msm_pcie_dev_t *pcie_dev;
	struct pci_dev *pci_dev;
	uint32_t iommu_cfg;
	dma_addr_t iommu_base;
	size_t iommu_size;
};

/* debug mask sys interface */
@@ -1250,8 +1259,6 @@ static void msm_pcie_show_status(struct msm_pcie_dev_t *dev)
		dev->msi_gicm_base);
	PCIE_DBG_FS(dev, "bus_client: %d\n",
		dev->bus_client);
	PCIE_DBG_FS(dev, "smmu does %s exist\n",
		dev->smmu_exist ? "" : "not");
	PCIE_DBG_FS(dev, "smmu_sid_base: 0x%x\n",
		dev->smmu_sid_base);
	PCIE_DBG_FS(dev, "n_fts: %d\n",
@@ -5917,13 +5924,6 @@ static int msm_pcie_probe(struct platform_device *pdev)
		"AUX clock frequency is %s 19.2MHz.\n",
		msm_pcie_dev[rc_idx].use_19p2mhz_aux_clk ? "" : "not");

	msm_pcie_dev[rc_idx].smmu_exist =
		of_property_read_bool((&pdev->dev)->of_node,
				"qcom,smmu-exist");
	PCIE_DBG(&msm_pcie_dev[rc_idx],
		"SMMU does %s exist.\n",
		msm_pcie_dev[rc_idx].smmu_exist ? "" : "not");

	msm_pcie_dev[rc_idx].smmu_sid_base = 0;
	ret = of_property_read_u32((&pdev->dev)->of_node, "qcom,smmu-sid-base",
				&msm_pcie_dev[rc_idx].smmu_sid_base);
@@ -6338,10 +6338,160 @@ static int msm_pcie_remove(struct platform_device *pdev)
	return ret;
}

static int msm_pci_iommu_parse_dt(struct msm_root_dev_t *root_dev)
{
	int ret;
	struct msm_pcie_dev_t *pcie_dev = root_dev->pcie_dev;
	struct pci_dev *pci_dev = root_dev->pci_dev;
	struct device_node *pci_of_node = pci_dev->dev.of_node;

	ret = of_property_read_u32(pci_of_node, "qcom,iommu-cfg",
				&root_dev->iommu_cfg);
	if (ret) {
		PCIE_DBG(pcie_dev, "PCIe: RC%d: no iommu-cfg present in DT\n",
			pcie_dev->rc_idx);
		return 0;
	}

	if (root_dev->iommu_cfg & MSM_PCIE_IOMMU_S1_BYPASS) {
		root_dev->iommu_base = 0;
		root_dev->iommu_size = PAGE_SIZE;
	} else {
		u64 iommu_range[2];

		ret = of_property_count_elems_of_size(pci_of_node,
							"qcom,iommu-range",
							sizeof(iommu_range));
		if (ret != 1) {
			PCIE_ERR(pcie_dev,
				"invalid entry for iommu address: %d\n",
				ret);
			return ret;
		}

		ret = of_property_read_u64_array(pci_of_node,
						"qcom,iommu-range",
						iommu_range, 2);
		if (ret) {
			PCIE_ERR(pcie_dev,
				"failed to get iommu address: %d\n", ret);
			return ret;
		}

		root_dev->iommu_base = (dma_addr_t)iommu_range[0];
		root_dev->iommu_size = (size_t)iommu_range[1];
	}

	PCIE_DBG(pcie_dev,
		"iommu-cfg: 0x%x iommu-base: %pad iommu-size: 0x%zx\n",
		root_dev->iommu_cfg, &root_dev->iommu_base,
		root_dev->iommu_size);

	return 0;
}

static int msm_pci_iommu_init(struct msm_root_dev_t *root_dev)
{
	int ret;
	struct dma_iommu_mapping *mapping;
	struct msm_pcie_dev_t *pcie_dev = root_dev->pcie_dev;
	struct pci_dev *pci_dev = root_dev->pci_dev;

	ret = msm_pci_iommu_parse_dt(root_dev);
	if (ret)
		return ret;

	if (!(root_dev->iommu_cfg & MSM_PCIE_IOMMU_PRESENT))
		return 0;

	mapping = arm_iommu_create_mapping(&pci_bus_type, root_dev->iommu_base,
						root_dev->iommu_size);
	if (IS_ERR_OR_NULL(mapping)) {
		ret = PTR_ERR(mapping);
		PCIE_ERR(pcie_dev,
			"PCIe: RC%d: Failed to create IOMMU mapping (%d)\n",
			pcie_dev->rc_idx, ret);
		return ret;
	}

	if (root_dev->iommu_cfg & MSM_PCIE_IOMMU_S1_BYPASS) {
		int iommu_s1_bypass = 1;

		ret = iommu_domain_set_attr(mapping->domain,
					DOMAIN_ATTR_S1_BYPASS,
					&iommu_s1_bypass);
		if (ret) {
			PCIE_ERR(pcie_dev,
				"PCIe: RC%d: failed to set attribute S1_BYPASS: %d\n",
				pcie_dev->rc_idx, ret);
			goto release_mapping;
		}
	}

	if (root_dev->iommu_cfg & MSM_PCIE_IOMMU_FAST) {
		int iommu_fast = 1;

		ret = iommu_domain_set_attr(mapping->domain,
					DOMAIN_ATTR_FAST,
					&iommu_fast);
		if (ret) {
			PCIE_ERR(pcie_dev,
				"PCIe: RC%d: failed to set attribute FAST: %d\n",
				pcie_dev->rc_idx, ret);
			goto release_mapping;
		}
	}

	if (root_dev->iommu_cfg & MSM_PCIE_IOMMU_ATOMIC) {
		int iommu_atomic = 1;

		ret = iommu_domain_set_attr(mapping->domain,
					DOMAIN_ATTR_ATOMIC,
					&iommu_atomic);
		if (ret) {
			PCIE_ERR(pcie_dev,
				"PCIe: RC%d: failed to set attribute ATOMIC: %d\n",
				pcie_dev->rc_idx, ret);
			goto release_mapping;
		}
	}

	if (root_dev->iommu_cfg & MSM_PCIE_IOMMU_FORCE_COHERENT) {
		int iommu_force_coherent = 1;

		ret = iommu_domain_set_attr(mapping->domain,
				DOMAIN_ATTR_PAGE_TABLE_FORCE_COHERENT,
				&iommu_force_coherent);
		if (ret) {
			PCIE_ERR(pcie_dev,
				"PCIe: RC%d: failed to set attribute FORCE_COHERENT: %d\n",
				pcie_dev->rc_idx, ret);
			goto release_mapping;
		}
	}

	ret = arm_iommu_attach_device(&pci_dev->dev, mapping);
	if (ret) {
		PCIE_ERR(pcie_dev,
			"failed to iommu attach device (%d)\n",
			pcie_dev->rc_idx, ret);
		goto release_mapping;
	}

	PCIE_DBG(pcie_dev, "PCIe: RC%d: successful iommu attach\n",
		pcie_dev->rc_idx);
	return 0;

release_mapping:
	arm_iommu_release_mapping(mapping);

	return ret;
}

int msm_pci_probe(struct pci_dev *pci_dev,
		  const struct pci_device_id *device_id)
{
	int ret;
	struct msm_pcie_dev_t *pcie_dev = PCIE_BUS_PRIV_DATA(pci_dev->bus);
	struct msm_root_dev_t *root_dev;

@@ -6358,6 +6508,16 @@ int msm_pci_probe(struct pci_dev *pci_dev,
	root_dev->pci_dev = pci_dev;
	dev_set_drvdata(&pci_dev->dev, root_dev);

	ret = msm_pci_iommu_init(root_dev);
	if (ret)
		return ret;

	ret = dma_set_mask_and_coherent(&pci_dev->dev, DMA_BIT_MASK(64));
	if (ret) {
		PCIE_ERR(pcie_dev, "DMA set mask failed (%d)\n", ret);
		return ret;
	}

	return 0;
}