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

Commit c3d6b932 authored by Jeremy Gebben's avatar Jeremy Gebben Committed by Tarun Karra
Browse files

msm: kgsl: Clean up IOMMU context bank configuration



Backport the 'new' iommu device structure to all targets
we currently support so that we can have only one device
tree parsing path. Targets using the 'old' iommu driver
don't have an 'iommus' property and instead must lookup
the iommu device by label.

Math is hard, so move it all to the device tree files.
The GPU register region to protect is defined by the
qcom,protect property.  The register offset to use for
GPU context bank progamming is set by the qcom,gpu-offset
property in the "gfx3d_user" device tree node.
Some targets need to use a global 'lock' register to safely
reprogram the context bank registers. This register is not
part of the context bank, so it is specified by the
qcomm,micro-mmu-ctrl property when it is present.

Change-Id: I77036d517218ba1f749b015aa09036eb796baefe
Signed-off-by: default avatarJordan Crouse <jcrouse@codeaurora.org>
Signed-off-by: default avatarLynus Vaz <lvaz@codeaurora.org>
Signed-off-by: default avatarCarter Cooper <ccooper@codeaurora.org>
Signed-off-by: default avatarJeremy Gebben <jgebben@codeaurora.org>
parent 7892968f
Loading
Loading
Loading
Loading
+36 −19
Original line number Diff line number Diff line
@@ -4,12 +4,11 @@ Required properties:

Required properties:
- compatible : one of:
	- "qcom,kgsl-smmu-v1"
	- "qcom,kgsl-smmu-v2"

- reg		: Base address and size of the SMMU.

- num_cb	: Number of context banks

- clocks	: List of clocks to be used during SMMU register access. See
		  Documentation/devicetree/bindings/clock/clock-bindings.txt
		  for information about the format. For each clock specified
@@ -20,52 +19,70 @@ Required properties:
		  the "clocks" property (above). See
		  Documentation/devicetree/bindings/clock/clock-bindings.txt
		  for more info.

- qcom,protect  : The GPU register region which must be protected by a CP
		  protected mode. On some targets this region must cover
		  the entire SMMU register space, on others there
		  is a separate aperture for CP to program context banks.

Optional properties:
- qcom,micro-mmu-control : Some targets provide an implementation defined
		  register for blocking translation requests during GPU side
		  programming.  This property specifies the offset of this
		  register within the iommu register space.
- qcom,retention :  A boolean specifying if retention is supported on this target
- qcom,global_pt :  A boolean specifying if global pagetable should be used.
		  When not set we use per process pagetables
- qcom,hyp_secure_alloc : A bool specifying if the hypervisor is used on this target
		  for secure buffer allocation
- qcom,secure_align_mask: A mask for determining how secure buffers need to
		  be aligned
- qcom,coherent-htw: A boolean specifying if coherent hardware table walks should
		  be enabled.

- List of sub nodes, one for each of the translation context banks supported.
  The driver uses the names of these nodes to determine how they are used,
  currently supported names are:
  - gfx3d_user : Used for the 'normal' GPU address space.
  - gfx3d_secure : Used for the content protection address space.
  Each sub node has the following required properties:

	- compatible : "qcom,smmu-kgsl-cb"
	- label : Name of the context bank
	- iommus : Specifies the SID's used by this context bank, this needs to be
		   <kgsl_smmu SID> pair, kgsl_smmu is the string parsed by iommu
		   driver to match this context bank with the kgsl_smmu device
		   defined in iommu device tree.
	 - retention :  A boolean specifying if retention is supported on this
			target
	 - qcom,global_pt :  A boolean specifying if global pagetable should be used.
			     When not set we use per process pagetables
	 - qcom,hyp_secure_alloc : A bool specifying if the hypervisor is used on
				   this target for secure buffer allocation
	 - qcom,secure_align_mask: A mask for determining how secure buffers need to
				   be aligned
		   defined in iommu device tree. On targets where the msm iommu
		   driver is used rather than the arm smmu driver, this property
		   may be absent.
	- qcom,gpu-offset :  Offset into the GPU register space for accessing
		   this context bank. On some targets the iommu registers are not
		   part of the GPU's register space, and a separate register aperture
		   is used. Otherwise the same register offsets may be used for CPU
		   or GPU side programming.

Example:

	msm_iommu: qcom,kgsl-iommu {
		compatible = "qcom,kgsl-smmu-v2";
		reg = <0xb40000 0x20000>;
		qcom,protect = <0x40000 0x20000>;
		clocks = <&clock_mmss clk_gpu_ahb_clk>,
			<&clock_gcc clk_gcc_mmss_bimc_gfx_clk>,
			<&clock_mmss clk_mmss_mmagic_ahb_clk>,
			<&clock_mmss clk_mmss_mmagic_cfg_ahb_clk>;
		clock-names = "gpu_ahb_clk", "bimc_gfx_clk", "mmagic_ahb_clk", "mmagic_cfg_ahb_clk";
		num_cb = <2>;
		qcom,secure_align_mask = <0xfff>;
		retention;
		qcom,retention;
		qcom,global_pt;

		iommu_kgsl_cb2: iommu_kgsl_cb2 {
		gfx3d_user: gfx3d_user {
			compatible = "qcom,smmu-kgsl-cb";
			label = "gfx3d_user";
			iommus = <&kgsl_smmu 0>,
				 <&kgsl_smmu 1>;
			qcom,gpu-offset = <0x48000>;
		};

		iommu_kgsl_cb3: iommu_kgsl_cb3 {
		gfx3d_secure: gfx3d_secure {
			compatible = "qcom,smmu-kgsl-cb";
			label = "gfx3d_secure";
			iommus = <&kgsl_smmu 2>;
		};
	};
+20 −0
Original line number Diff line number Diff line
@@ -179,4 +179,24 @@
		};

	};

	kgsl_msm_iommu: qcom,kgsl-iommu {
		compatible = "qcom,kgsl-smmu-v2";
		reg = <0x1f00000 0x10000>;
		/*
		* The gpu can only program a single context bank
		* at this fixed offset.
		*/
		qcom,protect = <0x48000 0x1000>;
		clocks = <&clock_gcc clk_gcc_smmu_cfg_clk>,
			<&clock_gcc clk_gcc_gfx_tcu_clk>,
			<&clock_gcc clk_gcc_gtcu_ahb_clk>,
			<&clock_gcc clk_gcc_gfx_tbu_clk>;
		clock-names = "iface_clk", "core_clk", "gtcu_iface_clk",
				"gtbu_clk";
		gfx3d_user: gfx3d_user {
			compatible = "qcom,smmu-kgsl-cb";
			qcom,gpu-offset = <0x48000>;
		};
	};
};
+3 −3
Original line number Diff line number Diff line
@@ -27,7 +27,7 @@
		clock-names = "iface_clk", "core_clk";
		status = "ok";

		gfx3d_user: qcom,iommu-ctx@1f08000 {
		qcom,iommu-ctx@1f08000 {
			compatible = "qcom,msm-smmu-v2-ctx";
			reg = <0x1f08000 0x1000>;
			interrupts = <0 240 0>;
@@ -35,7 +35,7 @@
			label = "gfx3d_user";
		};

		gfx3d_secure: qcom,iommu-ctx@1f09000 {
		qcom,iommu-ctx@1f09000 {
			compatible = "qcom,msm-smmu-v2-ctx";
			reg = <0x1f09000 0x1000>;
			qcom,secure-context;
@@ -44,7 +44,7 @@
			label = "gfx3d_secure";
		};

		gfx3d_priv: qcom,iommu-ctx@1f0b000 {
		qcom,iommu-ctx@1f0b000 {
			compatible = "qcom,msm-smmu-v2-ctx";
			reg = <0x1f0b000 0x1000>;
			interrupts = <0 245 0>;
+9 −7
Original line number Diff line number Diff line
@@ -176,10 +176,13 @@

	};

	/* IOMMU Data */
	kgsl_msm_iommu: qcom,kgsl-iommu {
		compatible = "qcom,kgsl-smmu-v2";

		reg = <0xb40000 0x20000>;
		qcom,protect = <0x40000 0x20000>;
		qcom,micro-mmu-control = <0x6000>;

		clocks = <&clock_mmss clk_mmss_mmagic_ahb_clk>,
			<&clock_mmss clk_mmss_mmagic_cfg_ahb_clk>,
			<&clock_gpu clk_gpu_ahb_clk>,
@@ -187,21 +190,20 @@
			<&clock_gcc clk_gcc_bimc_gfx_clk>;
		clock-names = "mmagic_ahb_clk", "mmagic_cfg_ahb_clk", "gpu_ahb_clk",
			"gcc_mmss_bimc_gfx_clk", "gcc_bimc_gfx_clk";
		num_cb = <2>;

		qcom,secure_align_mask = <0xfff>;
		retention;
		qcom,retention;

		iommu_kgsl_cb2: iommu_kgsl_cb2 {
		gfx3d_user: gfx3d_user {
			compatible = "qcom,smmu-kgsl-cb";
			label = "gfx3d_user";
			iommus = <&kgsl_smmu 0>;
			qcom,gpu-offset = <0x48000>;
		};

		iommu_kgsl_cb3: iommu_kgsl_cb3 {
		gfx3d_secure: gfx3d_secure {
			compatible = "qcom,smmu-kgsl-cb";
			label = "gfx3d_secure";
			iommus = <&kgsl_smmu 2>;
		};
	};

};
+84 −194
Original line number Diff line number Diff line
@@ -60,6 +60,8 @@ static struct devfreq_msm_adreno_tz_data adreno_tz_data = {

static const struct kgsl_functable adreno_functable;

static struct kgsl_iommu device_3d0_iommu;

static struct adreno_device device_3d0 = {
	.dev = {
		KGSL_DEVICE_COMMON_INIT(device_3d0.dev),
@@ -173,142 +175,142 @@ static inline int adreno_of_read_property(struct device_node *node,
	return ret;
}

static struct kgsl_device_iommu_data iommu_pdev_data;

/**
/*
 * adreno_iommu_cb_probe() - Adreno iommu context bank probe
 * @pdev: Platform device
 *
 * Iommu context bank probe function.
 */
static int adreno_iommu_cb_probe(struct platform_device *pdev)
{
	static int ctx;
	int ret = 0;

	if (ctx >=  iommu_pdev_data.iommu_ctx_count)
		return -ENOMEM;

	iommu_pdev_data.iommu_ctxs[ctx].dev = &pdev->dev;
	ret = of_property_read_string(pdev->dev.of_node, "label",
			&iommu_pdev_data.iommu_ctxs[ctx].iommu_ctx_name);

	if (!strcmp("gfx3d_user",
		iommu_pdev_data.iommu_ctxs[ctx].iommu_ctx_name)) {
			iommu_pdev_data.iommu_ctxs[ctx].ctx_id =
				KGSL_IOMMU_CONTEXT_USER;
	} else if (!strcmp("gfx3d_secure",
		iommu_pdev_data.iommu_ctxs[ctx].iommu_ctx_name)) {
			iommu_pdev_data.iommu_ctxs[ctx].ctx_id =
				KGSL_IOMMU_CONTEXT_SECURE;
	struct kgsl_iommu_context *ctx = NULL;
	struct device_node *node = pdev->dev.of_node;
	struct kgsl_iommu *iommu = &device_3d0_iommu;

	/* Map context names from dt to id's */
	if (!strcmp("gfx3d_user", node->name)) {
		ctx = &iommu->ctx[KGSL_IOMMU_CONTEXT_USER];
		ctx->id = KGSL_IOMMU_CONTEXT_USER;
		ctx->cb_num = -1;
	} else if (!strcmp("gfx3d_secure", node->name)) {
		ctx = &iommu->ctx[KGSL_IOMMU_CONTEXT_SECURE];
		ctx->id = KGSL_IOMMU_CONTEXT_SECURE;
		ctx->cb_num = -1;
		device_3d0.dev.mmu.secured = true;
	} else {
		KGSL_CORE_ERR("dt: IOMMU context %s is invalid\n",
			iommu_pdev_data.iommu_ctxs[ctx].iommu_ctx_name);
		KGSL_CORE_ERR("dt: Unknown context label %s\n", node->name);
		return -EINVAL;
	}

	if (ctx->name != NULL) {
		KGSL_CORE_ERR("dt: %s appears multiple times\n", node->name);
		return -EINVAL;
	}
	ctx->name = node->name;

	/* this property won't be found for all context banks */
	if (of_property_read_u32(node, "qcom,gpu-offset",
				&ctx->gpu_offset))
		ctx->gpu_offset = UINT_MAX;

	/*
	 * With the arm-smmu driver we'll have the right device pointer here.
	 * With the old msm_iommu driver we'll need to query it by name later.
	 */
	if (of_find_property(node, "iommus", NULL))
		ctx->dev = &pdev->dev;

	ctx++;
	return 0;
}

static struct of_device_id iommu_match_table[] = {
	{ .compatible = "qcom,kgsl-smmu-v1", },
	{ .compatible = "qcom,kgsl-smmu-v2", },
	{ .compatible = "qcom,smmu-kgsl-cb", },
	{}
};

/**
 * adreno_iommu_cb_probe() - Adreno iommu context bank probe
 * adreno_iommu_pdev_probe() - Adreno iommu context bank probe
 * @pdev: Platform device
 *
 * Iommu probe function.
 */
static int kgsl_iommu_pdev_probe(struct platform_device *pdev)
static int adreno_iommu_pdev_probe(struct platform_device *pdev)
{
	struct device *dev = &pdev->dev;
	const char *cname;
	struct property *prop;
	struct kgsl_device_iommu_data *data = &iommu_pdev_data;
	struct kgsl_iommu_ctx *ctxs = NULL;
	u32 reg_val[2];
	int result = -EINVAL, i = 0;
	int i = 0;
	struct kgsl_iommu *iommu = &device_3d0_iommu;

	if (of_device_is_compatible(dev->of_node, "qcom,smmu-kgsl-cb"))
		return adreno_iommu_cb_probe(pdev);
	else if (of_device_is_compatible(dev->of_node, "qcom,kgsl-smmu-v1"))
		iommu->version = 1;
	else
		iommu->version = 2;

	if (of_property_read_u32_array(pdev->dev.of_node, "reg", reg_val, 2)) {
		KGSL_CORE_ERR("dt: Unable to read KGSL IOMMU register range\n");
		goto err;
	}

	data->regstart = reg_val[0];
	data->regsize = reg_val[1];

	data->features |= KGSL_MMU_DMA_API;

	result = adreno_of_read_property(pdev->dev.of_node, "num_cb",
					&data->iommu_ctx_count);
	if (result)
		goto err;

	if (of_property_read_u32(pdev->dev.of_node,
			"qcom,secure_align_mask", &data->secure_align_mask))
		data->secure_align_mask = 0xfff;

	if (!data->iommu_ctx_count) {
		KGSL_CORE_ERR(
			"dt: KGSL IOMMU context bank count cannot be zero\n");
		goto err;
		return -EINVAL;
	}
	iommu->regstart = reg_val[0];
	iommu->regsize = reg_val[1];

	ctxs = kzalloc(data->iommu_ctx_count * sizeof(struct kgsl_iommu_ctx),
					GFP_KERNEL);

	if (ctxs == NULL) {
		result = -ENOMEM;
		goto err;
	/* Protecting the SMMU registers is mandatory */
	if (of_property_read_u32_array(pdev->dev.of_node, "qcom,protect",
					reg_val, 2)) {
		KGSL_CORE_ERR("dt: no iommu protection range specified\n");
		return -EINVAL;
	}

	data->iommu_ctxs = ctxs;
	iommu->protect.base = reg_val[0] / sizeof(u32);
	iommu->protect.range = ilog2(reg_val[1] / sizeof(u32));

	of_property_for_each_string(dev->of_node, "clock-names", prop, cname) {
		struct clk *c = devm_clk_get(dev, cname);
		if (IS_ERR(c)) {
			KGSL_CORE_ERR("dt: Couldn't get clock: %s\n", cname);
			result = -ENODEV;
			goto err;
			return -ENODEV;
		}
		if (i >= KGSL_IOMMU_MAX_CLKS) {
			KGSL_CORE_ERR("dt: too many clocks defined.\n");
			return -EINVAL;
		}
		data->clks[i] = c;

		iommu->clks[i] = c;
		++i;
	}

	if (of_property_read_bool(pdev->dev.of_node, "retention"))
		data->features |= KGSL_MMU_RETENTION;
	if (of_property_read_bool(pdev->dev.of_node, "qcom,retention"))
		device_3d0.dev.mmu.features |= KGSL_MMU_RETENTION;

	if (of_property_read_bool(pdev->dev.of_node, "qcom,global_pt"))
		data->features |= KGSL_MMU_GLOBAL_PAGETABLE;
		device_3d0.dev.mmu.features |= KGSL_MMU_GLOBAL_PAGETABLE;

	if (of_property_read_bool(pdev->dev.of_node, "qcom,hyp_secure_alloc"))
		data->features |= KGSL_MMU_HYP_SECURE_ALLOC;
		device_3d0.dev.mmu.features |= KGSL_MMU_HYP_SECURE_ALLOC;

	if (of_property_read_bool(pdev->dev.of_node, "qcom,force-32bit"))
		data->features |= KGSL_MMU_FORCE_32BIT;
		device_3d0.dev.mmu.features |= KGSL_MMU_FORCE_32BIT;

	result = of_platform_populate(pdev->dev.of_node, iommu_match_table,
				NULL, &pdev->dev);
	if (!result)
		return 0;
	if (of_property_read_u32(pdev->dev.of_node, "qcom,micro-mmu-control",
				&iommu->micro_mmu_ctrl))
		iommu->micro_mmu_ctrl = UINT_MAX;

err:
	kfree(ctxs);
	for (; i >= 0 && data->clks[i]; i--)
		devm_clk_put(dev, data->clks[i]);
	if (of_property_read_bool(pdev->dev.of_node, "qcom,coherent-htw"))
		device_3d0.dev.mmu.features |= KGSL_MMU_COHERENT_HTW;

	if (of_property_read_u32(pdev->dev.of_node, "qcom,secure_align_mask",
		&device_3d0.dev.mmu.secure_align_mask))
		device_3d0.dev.mmu.secure_align_mask = 0xfff;

	return result;
	return of_platform_populate(pdev->dev.of_node, iommu_match_table,
					NULL, &pdev->dev);
}

static struct platform_driver kgsl_iommu_platform_driver = {
	.probe = kgsl_iommu_pdev_probe,
	.probe = adreno_iommu_pdev_probe,
	.driver = {
		.owner = THIS_MODULE,
		.name = "kgsl-iommu",
@@ -837,102 +839,6 @@ static inline struct adreno_device *adreno_get_dev(struct platform_device *pdev)
	return of_id ? (struct adreno_device *) of_id->data : NULL;
}

static int adreno_of_get_iommu(struct platform_device *pdev,
	struct kgsl_device_platform_data *pdata)
{
	struct device_node *parent = pdev->dev.of_node;
	struct adreno_device *adreno_dev;
	int result = -EINVAL;
	struct device_node *node, *child;
	struct kgsl_device_iommu_data *data = NULL;
	struct kgsl_iommu_ctx *ctxs = NULL;
	u32 reg_val[2];
	u32 secure_id;
	int ctx_index = 0;

	node = of_parse_phandle(parent, "iommu", 0);
	if (node == NULL)
		return -EINVAL;

	adreno_dev = adreno_get_dev(pdev);
	if (adreno_dev == NULL)
		return -EINVAL;

	adreno_dev->dev.mmu.secured =
		(of_property_read_u32(node, "qcom,iommu-secure-id",
			&secure_id) == 0) ?  true : false;

	data = kzalloc(sizeof(*data), GFP_KERNEL);
	if (data == NULL) {
		result = -ENOMEM;
		goto err;
	}

	if (of_property_read_u32_array(node, "reg", reg_val, 2))
		goto err;

	data->regstart = reg_val[0];
	data->regsize = reg_val[1];

	data->iommu_ctx_count = 0;

	for_each_child_of_node(node, child)
		data->iommu_ctx_count++;

	ctxs = kzalloc(data->iommu_ctx_count * sizeof(struct kgsl_iommu_ctx),
		GFP_KERNEL);

	if (ctxs == NULL) {
		result = -ENOMEM;
		goto err;
	}

	for_each_child_of_node(node, child) {
		int ret = of_property_read_string(child, "label",
				&ctxs[ctx_index].iommu_ctx_name);

		if (ret) {
			KGSL_CORE_ERR("Unable to read KGSL IOMMU 'label'\n");
			goto err;
		}

		if (!strcmp("gfx3d_user", ctxs[ctx_index].iommu_ctx_name)) {
			ctxs[ctx_index].ctx_id = 0;
		} else if (!strcmp("gfx3d_priv",
					ctxs[ctx_index].iommu_ctx_name)) {
			ctxs[ctx_index].ctx_id = 1;
		} else if (!strcmp("gfx3d_spare",
					ctxs[ctx_index].iommu_ctx_name)) {
			ctxs[ctx_index].ctx_id = 2;
		/*
		 * Context bank 2 is secure context bank if content protection
		 * is supported
		 */
		} else if (!strcmp("gfx3d_secure",
					ctxs[ctx_index].iommu_ctx_name)) {
			ctxs[ctx_index].ctx_id = 2;
		} else {
			KGSL_CORE_ERR("dt: IOMMU context %s is invalid\n",
				ctxs[ctx_index].iommu_ctx_name);
			goto err;
		}

		ctx_index++;
	}

	data->iommu_ctxs = ctxs;

	pdata->iommu_data = data;

	return 0;

err:
	kfree(ctxs);
	kfree(data);

	return result;
}

static int adreno_of_get_pdata(struct platform_device *pdev)
{
	struct kgsl_device_platform_data *pdata = NULL;
@@ -993,15 +899,6 @@ static int adreno_of_get_pdata(struct platform_device *pdev)
		goto err;
	}

	/* If iommu phandle present parse iommu data old way */
	if (of_parse_phandle(pdev->dev.of_node, "iommu", 0)) {
		ret = adreno_of_get_iommu(pdev, pdata);
		if (ret)
			goto err;
	} else
		pdata->iommu_data = &iommu_pdev_data;


	pdata->coresight_pdata = of_get_coresight_platform_data(&pdev->dev,
			pdev->dev.of_node);

@@ -1009,13 +906,6 @@ static int adreno_of_get_pdata(struct platform_device *pdev)
	return 0;

err:
	if (pdata) {
		if (pdata->iommu_data)
			kfree(pdata->iommu_data->iommu_ctxs);

		kfree(pdata->iommu_data);
	}

	kfree(pdata);

	return ret;
@@ -1071,8 +961,7 @@ static int adreno_probe(struct platform_device *pdev)
	int status;

	/* Defer adreno probe if IOMMU is not already probed */
	if (!of_parse_phandle(pdev->dev.of_node, "iommu", 0) &&
			(iommu_pdev_data.regstart == 0))
	if (device_3d0_iommu.regstart == 0)
		return -EPROBE_DEFER;

	adreno_dev = adreno_get_dev(pdev);
@@ -1084,6 +973,7 @@ static int adreno_probe(struct platform_device *pdev)

	device = &adreno_dev->dev;
	device->pdev = pdev;
	device->mmu.priv = &device_3d0_iommu;

	status = adreno_of_get_pdata(pdev);
	if (status) {
@@ -2793,7 +2683,7 @@ static struct of_device_id busmon_match_table[] = {
	{}
};

static int kgsl_busmon_probe(struct platform_device *pdev)
static int adreno_busmon_probe(struct platform_device *pdev)
{
	struct kgsl_device *device;
	const struct of_device_id *pdid =
@@ -2810,7 +2700,7 @@ static int kgsl_busmon_probe(struct platform_device *pdev)
}

static struct platform_driver kgsl_bus_platform_driver = {
	.probe = kgsl_busmon_probe,
	.probe = adreno_busmon_probe,
	.driver = {
		.owner = THIS_MODULE,
		.name = "kgsl-busmon",
Loading