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

Commit 9cd18ff1 authored by Karthikeyan Ramasubramanian's avatar Karthikeyan Ramasubramanian Committed by Sagar Dharia
Browse files

slimbus: Add support for IOMMU S1 translation



Add support for IOMMU stage 1 translation to protect the kernel memory
during slimbus transfer.

Change-Id: I01db86e767d0b350c175534e69371986e9a024c7
Signed-off-by: default avatarKarthikeyan Ramasubramanian <kramasub@codeaurora.org>
Signed-off-by: default avatarSagar Dharia <sdharia@codeaurora.org>
parent 123357f5
Loading
Loading
Loading
Loading
+16 −0
Original line number Diff line number Diff line
@@ -65,6 +65,17 @@ Optional property:
		 and follow appropriate steps to ensure communication on the bus
		 can be resumed after subsytem restart. By default slimbus driver
		 register with ADSP subsystem.
 - qcom,iommu-s1-bypass: Boolean flag to bypass IOMMU stage 1 translation.

Optional subnodes:
qcom,iommu_slim_ctrl_cb : Child node representing the Slimbus controller
                          context bank.

Subnode Required properties:
- compatible : Must be "qcom,slim-ctrl-cb";
- iommus : A list of phandle and IOMMU specifier pairs that
           describe the IOMMU master interfaces of the device.

Example:
	slim@fe12f000 {
		cell-index = <1>;
@@ -78,4 +89,9 @@ Example:
		qcom,rxreg-access;
		qcom,apps-ch-pipes = <0x60000000>;
		qcom,ea-pc = <0x30>;

		iommu_slim_ctrl_cb: qcom,iommu_slim_ctrl_cb {
			compatible = "qcom,iommu-slim-ctrl-cb";
			iommus = <&apps_smmu 0x1 0x0>;
		};
	};
+15 −0
Original line number Diff line number Diff line
@@ -1832,6 +1832,15 @@
		interrupt-names = "slimbus_irq", "slimbus_bam_irq";
		qcom,apps-ch-pipes = <0x780000>;
		qcom,ea-pc = <0x270>;
		qcom,iommu-s1-bypass;

		iommu_slim_aud_ctrl_cb: qcom,iommu_slim_ctrl_cb {
			compatible = "qcom,iommu-slim-ctrl-cb";
			iommus = <&apps_smmu 0x1806 0x0>,
				 <&apps_smmu 0x180d 0x0>,
				 <&apps_smmu 0x180e 0x1>,
				 <&apps_smmu 0x1810 0x1>;
		};
	};

	slim_qca: slim@17240000 {
@@ -1843,6 +1852,12 @@
		reg-names = "slimbus_physical", "slimbus_bam_physical";
		interrupts = <0 291 0>, <0 292 0>;
		interrupt-names = "slimbus_irq", "slimbus_bam_irq";
		qcom,iommu-s1-bypass;

		iommu_slim_qca_ctrl_cb: qcom,iommu_slim_ctrl_cb {
			compatible = "qcom,iommu-slim-ctrl-cb";
			iommus = <&apps_smmu 0x1813 0x0>;
		};

		/* Slimbus Slave DT for WCN3990 */
		btfmslim_codec: wcn3990 {
+59 −7
Original line number Diff line number Diff line
@@ -9,11 +9,13 @@
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 */
#include <asm/dma-iommu.h>
#include <linux/irq.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/slab.h>
#include <linux/io.h>
#include <linux/iommu.h>
#include <linux/interrupt.h>
#include <linux/platform_device.h>
#include <linux/dma-mapping.h>
@@ -23,6 +25,7 @@
#include <linux/clk.h>
#include <linux/pm_runtime.h>
#include <linux/of.h>
#include <linux/of_platform.h>
#include <linux/of_slimbus.h>
#include <linux/timer.h>
#include <linux/msm-sps.h>
@@ -1665,6 +1668,43 @@ static ssize_t set_mask(struct device *device, struct device_attribute *attr,

static DEVICE_ATTR(debug_mask, 0644, show_mask, set_mask);

static const struct of_device_id ngd_slim_dt_match[] = {
	{
		.compatible = "qcom,slim-ngd",
	},
	{
		.compatible = "qcom,iommu-slim-ctrl-cb",
	},
	{}
};

static int ngd_slim_iommu_probe(struct device *dev)
{
	struct platform_device *pdev;
	struct msm_slim_ctrl *ctrl_dev;

	if (unlikely(!dev->parent)) {
		dev_err(dev, "%s no parent for this device\n", __func__);
		return -EINVAL;
	}

	pdev = to_platform_device(dev->parent);
	if (!pdev) {
		dev_err(dev, "%s Parent platform device not found\n", __func__);
		return -EINVAL;
	}

	ctrl_dev = platform_get_drvdata(pdev);
	if (!ctrl_dev) {
		dev_err(dev, "%s NULL controller device\n", __func__);
		return -EINVAL;

	}
	ctrl_dev->iommu_desc.cb_dev = dev;
	SLIM_INFO(ctrl_dev, "NGD IOMMU initialization complete\n");
	return 0;
}

static int ngd_slim_probe(struct platform_device *pdev)
{
	struct msm_slim_ctrl *dev;
@@ -1676,6 +1716,10 @@ static int ngd_slim_probe(struct platform_device *pdev)
	bool			slim_mdm = false;
	const char		*ext_modem_id = NULL;

	if (of_device_is_compatible(pdev->dev.of_node,
				    "qcom,iommu-slim-ctrl-cb"))
		return ngd_slim_iommu_probe(&pdev->dev);

	slim_mem = platform_get_resource_byname(pdev, IORESOURCE_MEM,
						"slimbus_physical");
	if (!slim_mem) {
@@ -1774,6 +1818,17 @@ static int ngd_slim_probe(struct platform_device *pdev)
					"qcom,slim-mdm", &ext_modem_id);
		if (!ret)
			slim_mdm = true;

		dev->iommu_desc.s1_bypass = of_property_read_bool(
							pdev->dev.of_node,
							"qcom,iommu-s1-bypass");
		ret = of_platform_populate(pdev->dev.of_node, ngd_slim_dt_match,
					   NULL, &pdev->dev);
		if (ret) {
			dev_err(dev->dev, "%s: Failed to of_platform_populate %d\n",
				__func__, ret);
			goto err_ctrl_failed;
		}
	} else {
		dev->ctrl.nr = pdev->id;
	}
@@ -1920,6 +1975,10 @@ static int ngd_slim_remove(struct platform_device *pdev)
	struct msm_slim_ctrl *dev = platform_get_drvdata(pdev);

	ngd_slim_enable(dev, false);
	if (!IS_ERR_OR_NULL(dev->iommu_desc.iommu_map)) {
		arm_iommu_detach_device(dev->iommu_desc.cb_dev);
		arm_iommu_release_mapping(dev->iommu_desc.iommu_map);
	}
	if (dev->sysfs_created)
		sysfs_remove_file(&dev->dev->kobj,
				&dev_attr_debug_mask.attr);
@@ -2091,13 +2150,6 @@ static const struct dev_pm_ops ngd_slim_dev_pm_ops = {
	)
};

static const struct of_device_id ngd_slim_dt_match[] = {
	{
		.compatible = "qcom,slim-ngd",
	},
	{}
};

static struct platform_driver ngd_slim_driver = {
	.probe = ngd_slim_probe,
	.remove = ngd_slim_remove,
+103 −8
Original line number Diff line number Diff line
@@ -9,17 +9,21 @@
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 */
#include <linux/pm_runtime.h>
#include <linux/dma-mapping.h>
#include <asm/dma-iommu.h>
#include <linux/delay.h>
#include <linux/dma-mapping.h>
#include <linux/gcd.h>
#include <linux/msm-sps.h>
#include <linux/platform_device.h>
#include <linux/pm_runtime.h>
#include <linux/slab.h>
#include <linux/slimbus/slimbus.h>
#include <linux/msm-sps.h>
#include <linux/gcd.h>
#include "slim-msm.h"

/* Pipe Number Offset Mask */
#define P_OFF_MASK 0x3FC
#define MSM_SLIM_VA_START	(0x40000000)
#define MSM_SLIM_VA_SIZE	(0xC0000000)

int msm_slim_rx_enqueue(struct msm_slim_ctrl *dev, u32 *buf, u8 len)
{
@@ -164,17 +168,61 @@ void msm_slim_free_endpoint(struct msm_slim_endp *ep)
	ep->sps = NULL;
}

static int msm_slim_iommu_attach(struct msm_slim_ctrl *ctrl_dev)
{
	struct dma_iommu_mapping *iommu_map;
	dma_addr_t va_start = MSM_SLIM_VA_START;
	size_t va_size = MSM_SLIM_VA_SIZE;
	int bypass = 1;
	struct device *dev;

	if (unlikely(!ctrl_dev))
		return -EINVAL;

	if (!ctrl_dev->iommu_desc.cb_dev)
		return 0;

	dev = ctrl_dev->iommu_desc.cb_dev;
	iommu_map = arm_iommu_create_mapping(&platform_bus_type,
						va_start, va_size);
	if (IS_ERR(iommu_map)) {
		dev_err(dev, "%s iommu_create_mapping failure\n", __func__);
		return PTR_ERR(iommu_map);
	}

	if (ctrl_dev->iommu_desc.s1_bypass) {
		if (iommu_domain_set_attr(iommu_map->domain,
					DOMAIN_ATTR_S1_BYPASS, &bypass)) {
			dev_err(dev, "%s Can't bypass s1 translation\n",
				__func__);
			arm_iommu_release_mapping(iommu_map);
			return -EIO;
		}
	}

	if (arm_iommu_attach_device(dev, iommu_map)) {
		dev_err(dev, "%s can't arm_iommu_attach_device\n", __func__);
		arm_iommu_release_mapping(iommu_map);
		return -EIO;
	}
	ctrl_dev->iommu_desc.iommu_map = iommu_map;
	SLIM_INFO(ctrl_dev, "NGD IOMMU Attach complete\n");
	return 0;
}

int msm_slim_sps_mem_alloc(
		struct msm_slim_ctrl *dev, struct sps_mem_buffer *mem, u32 len)
{
	dma_addr_t phys;
	struct device *dma_dev = dev->iommu_desc.cb_dev ?
					dev->iommu_desc.cb_dev : dev->dev;

	mem->size = len;
	mem->min_size = 0;
	mem->base = dma_alloc_coherent(dev->dev, mem->size, &phys, GFP_KERNEL);
	mem->base = dma_alloc_coherent(dma_dev, mem->size, &phys, GFP_KERNEL);

	if (!mem->base) {
		dev_err(dev->dev, "dma_alloc_coherent(%d) failed\n", len);
		dev_err(dma_dev, "dma_alloc_coherent(%d) failed\n", len);
		return -ENOMEM;
	}

@@ -387,6 +435,10 @@ int msm_alloc_port(struct slim_controller *ctrl, u8 pn)
	if (pn >= dev->port_nums)
		return -ENODEV;

	ret = msm_slim_iommu_attach(dev);
	if (ret)
		return ret;

	endpoint = &dev->pipes[pn];
	ret = msm_slim_init_endpoint(dev, endpoint);
	dev_dbg(dev->dev, "sps register bam error code:%x\n", ret);
@@ -435,9 +487,37 @@ enum slim_port_err msm_slim_port_xfer_status(struct slim_controller *ctr,
	return SLIM_P_INPROGRESS;
}

static void msm_slim_port_cb(struct sps_event_notify *ev)
static int msm_slim_iommu_map(struct msm_slim_ctrl *dev, phys_addr_t iobuf,
			      u32 len)
{
	int ret;

	if (!dev->iommu_desc.cb_dev)
		return 0;

	ret = iommu_map(dev->iommu_desc.iommu_map->domain,
			rounddown(iobuf, PAGE_SIZE),
			rounddown(iobuf, PAGE_SIZE),
			roundup((len + (iobuf - rounddown(iobuf, PAGE_SIZE))),
				PAGE_SIZE), IOMMU_READ | IOMMU_WRITE);
	return ret;
}

static void msm_slim_iommu_unmap(struct msm_slim_ctrl *dev, phys_addr_t iobuf,
				u32 len)
{
	if (!dev->iommu_desc.cb_dev)
		return;

	iommu_unmap(dev->iommu_desc.iommu_map->domain,
		    rounddown(iobuf, PAGE_SIZE),
		    roundup((len + (iobuf - rounddown(iobuf, PAGE_SIZE))),
			    PAGE_SIZE));
}

static void msm_slim_port_cb(struct sps_event_notify *ev)
{
	struct msm_slim_ctrl *dev = ev->user;
	struct completion *comp = ev->data.transfer.user;
	struct sps_iovec *iovec = &ev->data.transfer.iovec;

@@ -450,6 +530,8 @@ static void msm_slim_port_cb(struct sps_event_notify *ev)
		pr_err("%s: ERR event %d\n",
					__func__, ev->event_id);
	}
	if (dev)
		msm_slim_iommu_unmap(dev, iovec->addr, iovec->size);
	if (comp)
		complete(comp);
}
@@ -467,14 +549,19 @@ int msm_slim_port_xfer(struct slim_controller *ctrl, u8 pn, phys_addr_t iobuf,
	if (!dev->pipes[pn].connected)
		return -ENOTCONN;

	ret = msm_slim_iommu_map(dev, iobuf, len);
	if (ret)
		return ret;

	sreg.options = (SPS_EVENT_DESC_DONE|SPS_EVENT_ERROR);
	sreg.mode = SPS_TRIGGER_WAIT;
	sreg.xfer_done = NULL;
	sreg.callback = msm_slim_port_cb;
	sreg.user = NULL;
	sreg.user = dev;
	ret = sps_register_event(dev->pipes[pn].sps, &sreg);
	if (ret) {
		dev_dbg(dev->dev, "sps register event error:%x\n", ret);
		msm_slim_iommu_unmap(dev, iobuf, len);
		return ret;
	}
	ret = sps_transfer_one(dev->pipes[pn].sps, iobuf, len, comp,
@@ -490,6 +577,8 @@ int msm_slim_port_xfer(struct slim_controller *ctrl, u8 pn, phys_addr_t iobuf,
				PGD_THIS_EE(PGD_PORT_INT_EN_EEn, dev->ver));
		/* Make sure that port registers are updated before returning */
		mb();
	} else {
		msm_slim_iommu_unmap(dev, iobuf, len);
	}

	return ret;
@@ -1102,6 +1191,12 @@ int msm_slim_sps_init(struct msm_slim_ctrl *dev, struct resource *bam_mem,
	}

init_msgq:
	ret = msm_slim_iommu_attach(dev);
	if (ret) {
		sps_deregister_bam_device(bam_handle);
		return ret;
	}

	ret = msm_slim_init_rx_msgq(dev, pipe_reg);
	if (ret)
		dev_err(dev->dev, "msm_slim_init_rx_msgq failed 0x%x\n", ret);
+8 −1
Original line number Diff line number Diff line
/* Copyright (c) 2011-2016, The Linux Foundation. All rights reserved.
/* Copyright (c) 2011-2017, The Linux Foundation. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 2 and
@@ -263,10 +263,17 @@ struct msm_slim_bulk_wr {
	bool		in_progress;
};

struct msm_slim_iommu {
	struct device			*cb_dev;
	struct dma_iommu_mapping	*iommu_map;
	bool				s1_bypass;
};

struct msm_slim_ctrl {
	struct slim_controller  ctrl;
	struct slim_framer	framer;
	struct device		*dev;
	struct msm_slim_iommu	iommu_desc;
	void __iomem		*base;
	struct resource		*slew_mem;
	struct resource		*bam_mem;