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

Commit c26f9e16 authored by Linux Build Service Account's avatar Linux Build Service Account Committed by Gerrit - the friendly Code Review server
Browse files

Merge "dmaengine: gpi: Add support for SMMU S1 translation"

parents ea20ec5c 69484217
Loading
Loading
Loading
Loading
+25 −8
Original line number Diff line number Diff line
@@ -12,13 +12,12 @@ Main node properties:
- #dma-cells
  Usage: required
  Value type: <u32>
  Definition: Number of parameters client will provide.  Must be set to 6.
	1st parameter: gpii index
	2nd parameter: channel index
	3rd parameter: serial engine index
	4th parameter: bus protocol, 1 for SPI, 2 for UART, 3 for I2C
	5th parameter: channel ring length in transfer ring elements
	6th parameter: event processing priority, set to 0 for lowest latency
  Definition: Number of parameters client will provide.  Must be set to 5.
	1st parameter: channel index, 0 for TX, 1 for RX
	2nd parameter: serial engine index
	3rd parameter: bus protocol, 1 for SPI, 2 for UART, 3 for I2C
	4th parameter: channel ring length in transfer ring elements
	5th parameter: event processing priority, set to 0 for lowest latency

- compatible
  Usage: required
@@ -64,11 +63,27 @@ Main node properties:
	for the controller.  For more detail please check binding
	documentation arm,smmu.txt

- qcom,smmu-cfg
  Usage: required
  Value type: <u32>
  Definition: Determine whether GPI driver require to configure SMMU that
	sits behind GPI controller.
	Bit mask:
	BIT(0) : Attach address mapping to endpoint device
	BIT(1) : Set SMMU attribute S1_BYPASS
	BIT(2) : Set SMMU attribute FAST
	BIT(3) : Set SMMU attribute ATOMIC

- qcom,iova-range
  Usage: required if SMMU S1 translation is enabled
  Value type: Array of <u64>
  Definition: Pair of values describing iova base and size to allocate.

========
Example:
========
gpi_dma0: qcom,gpi-dma@0x800000 {
	#dma-cells = <6>;
	#dma-cells = <5>;
	compatible = "qcom,gpi-dma";
	reg = <0x800000 0x60000>;
	reg-names = "gpi-top";
@@ -80,5 +95,7 @@ gpi_dma0: qcom,gpi-dma@0x800000 {
	qcom,gpii-mask = <0xfa>;
	qcom,ev-factor = <2>;
	iommus = <&apps_smmu 0x0016 0x0>;
	qcom,smmu-cfg = <0x1>
	qcom,iova-range = <0x0 0x100000 0x0 0x100000>;
	status = "ok";
};
+6 −2
Original line number Diff line number Diff line
@@ -3558,7 +3558,7 @@
	};

	gpi_dma0: qcom,gpi-dma@0x800000 {
		#dma-cells = <6>;
		#dma-cells = <5>;
		compatible = "qcom,gpi-dma";
		reg = <0x800000 0x60000>;
		reg-names = "gpi-top";
@@ -3570,11 +3570,13 @@
		qcom,gpii-mask = <0xfa>;
		qcom,ev-factor = <2>;
		iommus = <&apps_smmu 0x0016 0x0>;
		qcom,smmu-cfg = <0x1>;
		qcom,iova-range = <0x0 0x100000 0x0 0x100000>;
		status = "ok";
	};

	gpi_dma1: qcom,gpi-dma@0xa00000 {
		#dma-cells = <6>;
		#dma-cells = <5>;
		compatible = "qcom,gpi-dma";
		reg = <0xa00000 0x60000>;
		reg-names = "gpi-top";
@@ -3586,6 +3588,8 @@
		qcom,gpii-mask = <0xfa>;
		qcom,ev-factor = <2>;
		iommus = <&apps_smmu 0x06d6 0x0>;
		qcom,smmu-cfg = <0x1>;
		qcom,iova-range = <0x0 0x100000 0x0 0x100000>;
		status = "ok";
	};

+205 −45
Original line number Diff line number Diff line
@@ -131,9 +131,11 @@ enum EV_PRIORITY {
#define GPI_LABEL_SIZE (256)
#define GPI_DBG_COMMON (99)
#define MAX_CHANNELS_PER_GPII (2)
#define GPI_TX_CHAN (0)
#define GPI_RX_CHAN (1)
#define CMD_TIMEOUT_MS (50)
#define STATE_IGNORE (U32_MAX)
#define REQ_OF_DMA_ARGS (6) /* # of arguments required from client */
#define REQ_OF_DMA_ARGS (5) /* # of arguments required from client */

struct __packed gpi_error_log_entry {
	u32 routine : 4;
@@ -437,6 +439,9 @@ struct gpi_dev {
	u32 max_gpii; /* maximum # of gpii instances available per gpi block */
	u32 gpii_mask; /* gpii instances available for apps */
	u32 ev_factor; /* ev ring length factor */
	u32 smmu_cfg;
	dma_addr_t iova_base;
	size_t iova_size;
	struct gpii *gpiis;
	void *ilctxt;
	u32 ipc_log_lvl;
@@ -509,6 +514,11 @@ struct gpi_desc {
	struct gpii_chan *gpii_chan;
};

#define GPI_SMMU_ATTACH BIT(0)
#define GPI_SMMU_S1_BYPASS BIT(1)
#define GPI_SMMU_FAST BIT(2)
#define GPI_SMMU_ATOMIC BIT(3)

const u32 GPII_CHAN_DIR[MAX_CHANNELS_PER_GPII] = {
	GPI_CHTYPE_DIR_OUT, GPI_CHTYPE_DIR_IN
};
@@ -2464,12 +2474,53 @@ static int gpi_alloc_chan_resources(struct dma_chan *chan)
	return ret;
}

static int gpi_find_avail_gpii(struct gpi_dev *gpi_dev, u32 seid)
{
	int gpii;
	struct gpii_chan *tx_chan, *rx_chan;

	/* check if same seid is already configured for another chid */
	for (gpii = 0; gpii < gpi_dev->max_gpii; gpii++) {
		if (!((1 << gpii) & gpi_dev->gpii_mask))
			continue;

		tx_chan = &gpi_dev->gpiis[gpii].gpii_chan[GPI_TX_CHAN];
		rx_chan = &gpi_dev->gpiis[gpii].gpii_chan[GPI_RX_CHAN];

		if (rx_chan->vc.chan.client_count && rx_chan->seid == seid)
			return gpii;
		if (tx_chan->vc.chan.client_count && tx_chan->seid == seid)
			return gpii;
	}

	/* no channels configured with same seid, return next avail gpii */
	for (gpii = 0; gpii < gpi_dev->max_gpii; gpii++) {
		if (!((1 << gpii) & gpi_dev->gpii_mask))
			continue;

		tx_chan = &gpi_dev->gpiis[gpii].gpii_chan[GPI_TX_CHAN];
		rx_chan = &gpi_dev->gpiis[gpii].gpii_chan[GPI_RX_CHAN];

		/* check if gpii is configured */
		if (tx_chan->vc.chan.client_count ||
		    rx_chan->vc.chan.client_count)
			continue;

		/* found a free gpii */
		return gpii;
	}

	/* no gpii instance available to use */
	return -EIO;
}

/* gpi_of_dma_xlate: open client requested channel */
static struct dma_chan *gpi_of_dma_xlate(struct of_phandle_args *args,
					 struct of_dma *of_dma)
{
	struct gpi_dev *gpi_dev = (struct gpi_dev *)of_dma->of_dma_data;
	u32 gpii, chid;
	u32 seid, chid;
	int gpii;
	struct gpii_chan *gpii_chan;

	if (args->args_count < REQ_OF_DMA_ARGS) {
@@ -2479,25 +2530,33 @@ static struct dma_chan *gpi_of_dma_xlate(struct of_phandle_args *args,
		return NULL;
	}

	/* Check if valid gpii instance */
	gpii = args->args[0];
	if (!((1 << gpii) & gpi_dev->gpii_mask)) {
		GPI_ERR(gpi_dev, "gpii instance:%d is not supported\n", gpii);
	chid = args->args[0];
	if (chid >= MAX_CHANNELS_PER_GPII) {
		GPI_ERR(gpi_dev, "gpii channel:%d not valid\n", chid);
		return NULL;
	}

	chid = args->args[1];
	if (chid >= MAX_CHANNELS_PER_GPII) {
		GPI_ERR(gpi_dev, "gpii channel:%d not valid\n", chid);
	seid = args->args[1];

	/* find next available gpii to use */
	gpii = gpi_find_avail_gpii(gpi_dev, seid);
	if (gpii < 0) {
		GPI_ERR(gpi_dev, "no available gpii instances\n");
		return NULL;
	}

	/* get ring size, protocol, se_id, and priority */
	gpii_chan = &gpi_dev->gpiis[gpii].gpii_chan[chid];
	gpii_chan->seid = args->args[2];
	gpii_chan->protocol = args->args[3];
	gpii_chan->req_tres = args->args[4];
	gpii_chan->priority = args->args[5];
	if (gpii_chan->vc.chan.client_count) {
		GPI_ERR(gpi_dev, "gpii:%d chid:%d seid:%d already configured\n",
			gpii, chid, gpii_chan->seid);
		return NULL;
	}

	/* get ring size, protocol, se_id, and priority */
	gpii_chan->seid = seid;
	gpii_chan->protocol = args->args[2];
	gpii_chan->req_tres = args->args[3];
	gpii_chan->priority = args->args[4];

	GPI_LOG(gpi_dev,
		"client req. gpii:%u chid:%u #_tre:%u priority:%u protocol:%u\n",
@@ -2562,52 +2621,123 @@ static void gpi_setup_debug(struct gpi_dev *gpi_dev)
	}
}

static struct dma_iommu_mapping *gpi_create_mapping(struct gpi_dev *gpi_dev)
{
	dma_addr_t base;
	size_t size;

	/*
	 * If S1_BYPASS enabled then iommu space is not used, however framework
	 * still require clients to create a mapping space before attaching. So
	 * set to smallest size required by iommu framework.
	 */
	if (gpi_dev->smmu_cfg & GPI_SMMU_S1_BYPASS) {
		base = 0;
		size = PAGE_SIZE;
	} else {
		base = gpi_dev->iova_base;
		size = gpi_dev->iova_size;
	}

	GPI_LOG(gpi_dev, "Creating iommu mapping of base:0x%llx size:%lu\n",
		base, size);

	return arm_iommu_create_mapping(&platform_bus_type, base, size);
}

static int gpi_dma_mask(struct gpi_dev *gpi_dev)
{
	int mask = 64;

	if (gpi_dev->smmu_cfg && !(gpi_dev->smmu_cfg & GPI_SMMU_S1_BYPASS)) {
		unsigned long addr;

		addr = gpi_dev->iova_base + gpi_dev->iova_size + 1;
		mask = find_last_bit(&addr, 64);
	}

	GPI_LOG(gpi_dev, "Setting dma mask to %d\n", mask);

	return dma_set_mask(gpi_dev->dev, DMA_BIT_MASK(mask));
}

static int gpi_smmu_init(struct gpi_dev *gpi_dev)
{
	u64 size = PAGE_SIZE;
	dma_addr_t base = 0x0;
	struct dma_iommu_mapping *map;
	int attr, ret;
	struct dma_iommu_mapping *mapping = NULL;
	int ret;

	map = arm_iommu_create_mapping(&platform_bus_type, base, size);
	if (IS_ERR_OR_NULL(map)) {
		ret = PTR_ERR(map) ? : -EIO;
		GPI_ERR(gpi_dev, "error create_mapping, ret:%d\n", ret);
		return ret;
	if (gpi_dev->smmu_cfg) {

		/* create mapping table */
		mapping = gpi_create_mapping(gpi_dev);
		if (IS_ERR(mapping)) {
			GPI_ERR(gpi_dev,
				"Failed to create iommu mapping, ret:%ld\n",
				PTR_ERR(mapping));
			return PTR_ERR(mapping);
		}

		if (gpi_dev->smmu_cfg & GPI_SMMU_S1_BYPASS) {
			int s1_bypass = 1;

			ret = iommu_domain_set_attr(mapping->domain,
					DOMAIN_ATTR_S1_BYPASS, &s1_bypass);
			if (ret) {
				GPI_ERR(gpi_dev,
					"Failed to set attr S1_BYPASS, ret:%d\n",
					ret);
				goto release_mapping;
			}
		}

		if (gpi_dev->smmu_cfg & GPI_SMMU_FAST) {
			int fast = 1;

	attr = 1;
	ret = iommu_domain_set_attr(map->domain, DOMAIN_ATTR_ATOMIC, &attr);
			ret = iommu_domain_set_attr(mapping->domain,
						    DOMAIN_ATTR_FAST, &fast);
			if (ret) {
		GPI_ERR(gpi_dev, "error setting ATTTR_ATOMIC, ret:%d\n", ret);
		goto error_smmu;
				GPI_ERR(gpi_dev,
					"Failed to set attr FAST, ret:%d\n",
					ret);
				goto release_mapping;
			}
		}

	attr = 1;
	ret = iommu_domain_set_attr(map->domain, DOMAIN_ATTR_S1_BYPASS, &attr);
		if (gpi_dev->smmu_cfg & GPI_SMMU_ATOMIC) {
			int atomic = 1;

			ret = iommu_domain_set_attr(mapping->domain,
						DOMAIN_ATTR_ATOMIC, &atomic);
			if (ret) {
		GPI_ERR(gpi_dev, "error setting S1_BYPASS, ret:%d\n", ret);
		goto error_smmu;
				GPI_ERR(gpi_dev,
					"Failed to set attr ATOMIC, ret:%d\n",
					ret);
				goto release_mapping;
			}
		}

	ret = arm_iommu_attach_device(gpi_dev->dev, map);
		ret = arm_iommu_attach_device(gpi_dev->dev, mapping);
		if (ret) {
		GPI_ERR(gpi_dev, "error iommu_attach, ret:%d\n", ret);
		goto error_smmu;
			GPI_ERR(gpi_dev,
				"Failed with iommu_attach, ret:%d\n", ret);
			goto release_mapping;
		}
	}

	ret = dma_set_mask(gpi_dev->dev, DMA_BIT_MASK(64));
	ret = gpi_dma_mask(gpi_dev);
	if (ret) {
		GPI_ERR(gpi_dev, "error setting dma_mask, ret:%d\n", ret);
		GPI_ERR(gpi_dev, "Error setting dma_mask, ret:%d\n", ret);
		goto error_set_mask;
	}

	return ret;

error_set_mask:
	if (gpi_dev->smmu_cfg)
		arm_iommu_detach_device(gpi_dev->dev);
error_smmu:
	arm_iommu_release_mapping(map);
release_mapping:
	if (mapping)
		arm_iommu_release_mapping(mapping);
	return ret;
}

@@ -2656,6 +2786,36 @@ static int gpi_probe(struct platform_device *pdev)
		return ret;
	}

	ret = of_property_read_u32(gpi_dev->dev->of_node, "qcom,smmu-cfg",
				   &gpi_dev->smmu_cfg);
	if (ret) {
		GPI_ERR(gpi_dev, "missing 'qcom,smmu-cfg' DT node\n");
		return ret;
	}
	if (gpi_dev->smmu_cfg && !(gpi_dev->smmu_cfg & GPI_SMMU_S1_BYPASS)) {
		u64 iova_range[2];

		ret = of_property_count_elems_of_size(gpi_dev->dev->of_node,
						      "qcom,iova-range",
						      sizeof(iova_range));
		if (ret != 1) {
			GPI_ERR(gpi_dev,
				"missing or incorrect 'qcom,iova-range' DT node ret:%d\n",
				ret);
		}

		ret = of_property_read_u64_array(gpi_dev->dev->of_node,
					"qcom,iova-range", iova_range,
					sizeof(iova_range) / sizeof(u64));
		if (ret) {
			GPI_ERR(gpi_dev,
				"could not read DT prop 'qcom,iova-range\n");
			return ret;
		}
		gpi_dev->iova_base = iova_range[0];
		gpi_dev->iova_size = iova_range[1];
	}

	ret = gpi_smmu_init(gpi_dev);
	if (ret) {
		GPI_ERR(gpi_dev, "error configuring smmu, ret:%d\n", ret);