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

Commit bd456266 authored by qctecmdr Service's avatar qctecmdr Service Committed by Gerrit - the friendly Code Review server
Browse files

Merge "msm: pcie: add IOMMU configuration support for root complex"

parents 80af3a92 96fb8ce7
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;
}