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

Commit d93295a8 authored by Hamad Kadmany's avatar Hamad Kadmany Committed by Matt Wagantall
Browse files

wil6210: Add IOMMU support for MSM targets



Initialize IOMMU if target supports it.

Change-Id: Ice8d7d1e2fb3db5b3f96131945c628529172cbe4
Signed-off-by: default avatarHamad Kadmany <hkadmany@codeaurora.org>
parent c65f043e
Loading
Loading
Loading
Loading
+110 −41
Original line number Diff line number Diff line
@@ -17,42 +17,61 @@
#include <linux/of.h>
#include <linux/slab.h>
#include <linux/msm-bus.h>
#include <asm/dma-iommu.h>
#include <linux/iommu.h>

#include "wil_platform.h"
#include "wil_platform_msm.h"

#define VIRTUAL_ADDR_RANGE_BASE	0x10000000 /* avoid NULL address */
#define VIRTUAL_ADDR_RANGE_SIZE	0x40000000

/**
 * struct wil_platform_data - wil6210 msm platform data
 *
 * @bus_scale: bus scaling information
 * @smmu_support: indication whether PCIe has SMMU support
 */
struct wil_platform_data {
	struct msm_bus_scale_pdata *bus_scale;
	bool smmu_support;
};

/**
 * struct wil_platform_msm - wil6210 msm platform module info
 *
 * @dev: device object
 * @msm_bus_handle: handle for using msm_bus API
 * @pdata: bus scale info retrieved from DT
 * @mapping: the SMMU virtual addresses pool
 */
struct wil_platform_msm {
	struct device *dev;
	uint32_t msm_bus_handle;
	struct msm_bus_scale_pdata *pdata;
	struct wil_platform_data pdata;
	struct dma_iommu_mapping *mapping;
};

#define KBTOB(a) (a * 1000ULL)

/**
 * wil_platform_get_pdata() - Generate bus client data from device tree
 * wil_platform_get_pdata() - Generate platform data from device tree
 * provided by clients.
 *
 * dev: device object
 * of_node: Device tree node to extract information from
 * pdata: Generated device tree information
 *
 * The function returns a valid pointer to the allocated bus-scale-pdata
 * if the vectors were correctly read from the client's device node.
 * Any error in reading or parsing the device node will return NULL
 * to the caller.
 * The function parses client's device tree node and provide
 * it back to user.
 * Note that bus_scale is dynamically allocated and it is
 * the caller's responsibility to free it when not needed any longer.
 */
static struct msm_bus_scale_pdata *wil_platform_get_pdata(
static int wil_platform_get_pdata(
		struct device *dev,
		struct device_node *of_node)
		struct device_node *of_node,
		struct wil_platform_data *pdata)
{
	struct msm_bus_scale_pdata *pdata;
	struct msm_bus_paths *usecase;
	int i, j, ret, len;
	unsigned int num_usecases, num_paths, mem_size;
@@ -66,17 +85,17 @@ static struct msm_bus_scale_pdata *wil_platform_get_pdata(
				   &num_usecases);
	if (ret) {
		dev_err(dev, "Error: num-usecases not found\n");
		return NULL;
		return -EINVAL;
	}

	ret = of_property_read_u32(of_node, "qcom,msm-bus,num-paths",
				   &num_paths);
	if (ret) {
		dev_err(dev, "Error: num_paths not found\n");
		return NULL;
		return -EINVAL;
	}

	/* pdata memory layout:
	/* pdata->bus_scale memory layout:
	 *   msm_bus_scale_pdata
	 *   msm_bus_paths[num_usecases]
	 *   msm_bus_vectors[num_usecases][num_paths]
@@ -85,41 +104,45 @@ static struct msm_bus_scale_pdata *wil_platform_get_pdata(
		   sizeof(struct msm_bus_paths) * num_usecases +
		   sizeof(struct msm_bus_vectors) * num_usecases * num_paths;

	pdata = kzalloc(mem_size, GFP_KERNEL);
	if (!pdata)
		return NULL;
	pdata->bus_scale = kzalloc(mem_size, GFP_KERNEL);
	if (!pdata->bus_scale)
		return -ENOMEM;

	ret = of_property_read_string(of_node, "qcom,msm-bus,name",
				      &pdata->name);
				      &pdata->bus_scale->name);
	if (ret) {
		dev_err(dev, "Error: Client name not found\n");
		goto err;
	}

	if (of_property_read_bool(of_node, "qcom,msm-bus,active-only")) {
		pdata->active_only = 1;
		pdata->bus_scale->active_only = 1;
	} else {
		dev_info(dev, "active_only flag absent.\n");
		dev_info(dev, "Using dual context by default\n");
	}

	pdata->num_usecases = num_usecases;
	pdata->usecase = (struct msm_bus_paths *)(pdata + 1);
	pdata->bus_scale->num_usecases = num_usecases;
	pdata->bus_scale->usecase =
		(struct msm_bus_paths *)(pdata->bus_scale + 1);

	vec_arr = of_get_property(of_node, "qcom,msm-bus,vectors-KBps", &len);
	if (vec_arr == NULL) {
		dev_err(dev, "Error: Vector array not found\n");
		ret = -ENOMEM;
		goto err;
	}

	if (len != num_usecases * num_paths * sizeof(uint32_t) * 4) {
		dev_err(dev, "Error: Length-error on getting vectors\n");
		ret = -EINVAL;
		goto err;
	}

	vectors = (struct msm_bus_vectors *)(pdata->usecase + num_usecases);
	vectors = (struct msm_bus_vectors *)
		(pdata->bus_scale->usecase + num_usecases);
	for (i = 0; i < num_usecases; i++) {
		usecase = &pdata->usecase[i];
		usecase = &pdata->bus_scale->usecase[i];
		usecase->num_paths = num_paths;
		usecase->vectors = &vectors[i];

@@ -136,12 +159,18 @@ static struct msm_bus_scale_pdata *wil_platform_get_pdata(
		}
	}

	return pdata;
	if (of_property_read_bool(of_node, "qcom,smmu-support")) {
		pdata->smmu_support = 1;
	} else {
		dev_info(dev, "smmu_support flag absent.\n");
		pdata->smmu_support = 0;
	}

err:
	kfree(pdata);
	return 0;

	return NULL;
err:
	kfree(pdata->bus_scale);
	return ret;
}

/* wil_platform API (callbacks) */
@@ -157,8 +186,8 @@ static int wil_platform_bus_request(void *handle,
	uint32_t min_kbps = ~0;

	/* find the lowest usecase that is bigger than requested kbps */
	for (i = 0; i < msm->pdata->num_usecases; i++) {
		usecase = &msm->pdata->usecase[i];
	for (i = 0; i < msm->pdata.bus_scale->num_usecases; i++) {
		usecase = &msm->pdata.bus_scale->usecase[i];
		/* assume we have single path (vectors[0]). If we ever
		 * have multiple paths, need to define the behavior */
		usecase_kbps = div64_u64(usecase->vectors[0].ib, 1000);
@@ -189,20 +218,19 @@ static void wil_platform_uninit(void *handle)
	if (msm->msm_bus_handle)
		msm_bus_scale_unregister_client(msm->msm_bus_handle);

	kfree(msm->pdata);
	kfree(msm);
	if (msm->pdata.smmu_support) {
		arm_iommu_detach_device(msm->dev);
		arm_iommu_release_mapping(msm->mapping);
	}

static int wil_platform_msm_bus_register(struct wil_platform_msm *msm,
					 struct device_node *node)
{
	msm->pdata = wil_platform_get_pdata(msm->dev, node);
	if (!msm->pdata) {
		dev_err(msm->dev, "Failed getting DT info\n");
		return -EINVAL;
	kfree(msm->pdata.bus_scale);
	kfree(msm);
}

	msm->msm_bus_handle = msm_bus_scale_register_client(msm->pdata);
static int wil_platform_msm_bus_register(struct wil_platform_msm *msm)
{
	msm->msm_bus_handle =
		msm_bus_scale_register_client(msm->pdata.bus_scale);
	if (!msm->msm_bus_handle) {
		dev_err(msm->dev, "Failed msm_bus registration\n");
		return -EINVAL;
@@ -225,8 +253,14 @@ void *wil_platform_msm_init(struct device *dev, struct wil_platform_ops *ops)
{
	struct device_node *of_node;
	struct wil_platform_msm *msm;
	int disable_htw = 1;
	dma_addr_t base = VIRTUAL_ADDR_RANGE_BASE;
	size_t size = VIRTUAL_ADDR_RANGE_SIZE;
	int order = 0;
	int rc;

	memset(ops, 0, sizeof(*ops));

	of_node = of_find_compatible_node(NULL, NULL, "qcom,wil6210");
	if (!of_node) {
		/* this could mean non-msm platform */
@@ -240,18 +274,53 @@ void *wil_platform_msm_init(struct device *dev, struct wil_platform_ops *ops)

	msm->dev = dev;

	rc = wil_platform_get_pdata(msm->dev, of_node, &msm->pdata);
	if (rc) {
		dev_err(msm->dev, "Failed getting DT info (%d)\n", rc);
		goto free_mem;
	}

	/* register with msm_bus module for scaling requests */
	rc = wil_platform_msm_bus_register(msm, of_node);
	rc = wil_platform_msm_bus_register(msm);
	if (rc)
		goto cleanup;
		goto free_mem;

	if (!msm->pdata.smmu_support)
		return (void *)msm;

	msm->mapping = arm_iommu_create_mapping(&platform_bus_type,
		base, size, order);

	if (IS_ERR_OR_NULL(msm->mapping)) {
		rc = PTR_ERR(msm->mapping) ?: -ENODEV;
		dev_err(dev, "Failed to create IOMMU mapping (%d)\n", rc);
		goto free_mem;
	}

	rc = iommu_domain_set_attr(msm->mapping->domain,
			DOMAIN_ATTR_COHERENT_HTW_DISABLE,
			&disable_htw);
	if (rc) {
		/* This error can be ignored and not considered fatal,
		 * but let the users know this happened
		 */
		dev_err(dev, "Warning: disable coherent HTW failed (%d)\n", rc);
	}

	rc = arm_iommu_attach_device(dev, msm->mapping);
	if (rc) {
		dev_err(dev, "arm_iommu_attach_device failed (%d)\n", rc);
		goto release_mapping;
	}

	memset(ops, 0, sizeof(*ops));
	ops->bus_request = wil_platform_bus_request;
	ops->uninit = wil_platform_uninit;

	return (void *)msm;

cleanup:
release_mapping:
	arm_iommu_release_mapping(msm->mapping);
free_mem:
	kfree(msm);
	return NULL;
}