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

Commit 06e393e7 authored by Robin Murphy's avatar Robin Murphy Committed by Patrick Daly
Browse files

iommu/arm-smmu: Convert to iommu_fwspec



In the final step of preparation for full generic configuration support,
swap our fixed-size master_cfg for the generic iommu_fwspec. For the
legacy DT bindings, the driver simply gets to act as its own 'firmware'.
Farewell, arbitrary MAX_MASTER_STREAMIDS!

Change-Id: Iac0e03ddae52231861ebcbb5b286cb35652393ca
Signed-off-by: default avatarRobin Murphy <robin.murphy@arm.com>
Signed-off-by: default avatarWill Deacon <will.deacon@arm.com>
Git-commit: adfec2e7
Git-repo: https://source.codeaurora.org/quic/la/kernel/msm-4.4


Signed-off-by: default avatarPatrick Daly <pdaly@codeaurora.org>
parent 6668f69e
Loading
Loading
Loading
Loading
+80 −68
Original line number Diff line number Diff line
@@ -42,6 +42,7 @@
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/of_device.h>
#include <linux/of_iommu.h>
#include <linux/pci.h>
#include <linux/platform_device.h>
#include <linux/slab.h>
@@ -55,9 +56,6 @@

#include "io-pgtable.h"

/* Maximum number of stream IDs assigned to a single device */
#define MAX_MASTER_STREAMIDS		128

/* Maximum number of context banks per SMMU */
#define ARM_SMMU_MAX_CBS		128

@@ -354,13 +352,13 @@ struct arm_smmu_smr {

struct arm_smmu_master_cfg {
	struct arm_smmu_device		*smmu;
	int				num_streamids;
	u16				streamids[MAX_MASTER_STREAMIDS];
	s16				smendx[MAX_MASTER_STREAMIDS];
	s16				smendx[];
};
#define INVALID_SMENDX			-1
#define for_each_cfg_sme(cfg, i, idx) \
	for (i = 0; idx = cfg->smendx[i], i < cfg->num_streamids; ++i)
#define __fwspec_cfg(fw) ((struct arm_smmu_master_cfg *)fw->iommu_priv)
#define fwspec_smmu(fw)  (__fwspec_cfg(fw)->smmu)
#define for_each_cfg_sme(fw, i, idx) \
	for (i = 0; idx = __fwspec_cfg(fw)->smendx[i], i < fw->num_ids; ++i)

/*
 * Describes resources required for on/off power operation.
@@ -620,14 +618,16 @@ static int __find_legacy_master_phandle(struct device *dev, void *data)
}

static struct platform_driver arm_smmu_driver;
static struct iommu_ops arm_smmu_ops;

static int arm_smmu_register_legacy_master(struct device *dev)
static int arm_smmu_register_legacy_master(struct device *dev,
					   struct arm_smmu_device **smmu)
{
	struct arm_smmu_device *smmu;
	struct arm_smmu_master_cfg *cfg;
	struct device *smmu_dev;
	struct device_node *np;
	struct of_phandle_iterator it;
	void *data = &it;
	u32 *sids;
	__be32 pci_sid;
	int err = 0;

@@ -641,20 +641,13 @@ static int arm_smmu_register_legacy_master(struct device *dev)
	it.node = np;
	err = driver_for_each_device(&arm_smmu_driver.driver, NULL, &data,
				     __find_legacy_master_phandle);
	smmu_dev = data;
	of_node_put(np);
	if (err == 0)
		return -ENODEV;
	if (err < 0)
		return err;

	smmu = dev_get_drvdata(data);

	if (it.cur_count > MAX_MASTER_STREAMIDS) {
		dev_err(smmu->dev,
			"reached maximum number (%d) of stream IDs for master device %s\n",
			MAX_MASTER_STREAMIDS, dev_name(dev));
		return -ENOSPC;
	}
	if (dev_is_pci(dev)) {
		/* "mmu-masters" assumes Stream ID == Requester ID */
		pci_for_each_dma_alias(to_pci_dev(dev), __arm_smmu_get_pci_sid,
@@ -663,17 +656,20 @@ static int arm_smmu_register_legacy_master(struct device *dev)
		it.cur_count = 1;
	}

	cfg = kzalloc(sizeof(*cfg), GFP_KERNEL);
	if (!cfg)
		return -ENOMEM;

	cfg->smmu = smmu;
	dev->archdata.iommu = cfg;
	err = iommu_fwspec_init(dev, &smmu_dev->of_node->fwnode,
				&arm_smmu_ops);
	if (err)
		return err;

	while (it.cur_count--)
		cfg->streamids[cfg->num_streamids++] = be32_to_cpup(it.cur++);
	sids = kcalloc(it.cur_count, sizeof(*sids), GFP_KERNEL);
	if (!sids)
		return -ENOMEM;

	return 0;
	*smmu = dev_get_drvdata(smmu_dev);
	of_phandle_iterator_args(&it, sids, it.cur_count);
	err = iommu_fwspec_add_ids(dev, sids, it.cur_count);
	kfree(sids);
	return err;
}

static int __arm_smmu_alloc_bitmap(unsigned long *map, int start, int end)
@@ -1877,7 +1873,8 @@ static bool arm_smmu_free_sme(struct arm_smmu_device *smmu, int idx)

static int arm_smmu_master_alloc_smes(struct device *dev)
{
	struct arm_smmu_master_cfg *cfg = dev->archdata.iommu;
	struct iommu_fwspec *fwspec = dev->iommu_fwspec;
	struct arm_smmu_master_cfg *cfg = fwspec->iommu_priv;
	struct arm_smmu_device *smmu = cfg->smmu;
	struct arm_smmu_smr *smrs = smmu->smrs;
	struct iommu_group *group;
@@ -1885,19 +1882,19 @@ static int arm_smmu_master_alloc_smes(struct device *dev)

	mutex_lock(&smmu->stream_map_mutex);
	/* Figure out a viable stream map entry allocation */
	for_each_cfg_sme(cfg, i, idx) {
	for_each_cfg_sme(fwspec, i, idx) {
		if (idx != INVALID_SMENDX) {
			ret = -EEXIST;
			goto out_err;
		}

		ret = arm_smmu_find_sme(smmu, cfg->streamids[i], 0);
		ret = arm_smmu_find_sme(smmu, fwspec->ids[i], 0);
		if (ret < 0)
			goto out_err;

		idx = ret;
		if (smrs && smmu->s2crs[idx].count == 0) {
			smrs[idx].id = cfg->streamids[i];
			smrs[idx].id = fwspec->ids[i];
			smrs[idx].mask = 0; /* We don't currently share SMRs */
			smrs[idx].valid = true;
		}
@@ -1915,7 +1912,7 @@ static int arm_smmu_master_alloc_smes(struct device *dev)
	iommu_group_put(group);

	/* It worked! Now, poke the actual hardware */
	for_each_cfg_sme(cfg, i, idx) {
	for_each_cfg_sme(fwspec, i, idx) {
		arm_smmu_write_sme(smmu, idx);
		smmu->s2crs[idx].group = group;
	}
@@ -1932,13 +1929,14 @@ static int arm_smmu_master_alloc_smes(struct device *dev)
	return ret;
}

static void arm_smmu_master_free_smes(struct arm_smmu_master_cfg *cfg)
static void arm_smmu_master_free_smes(struct iommu_fwspec *fwspec)
{
	struct arm_smmu_device *smmu = cfg->smmu;
	struct arm_smmu_device *smmu = fwspec_smmu(fwspec);
	struct arm_smmu_master_cfg *cfg = fwspec->iommu_priv;
	int i, idx;

	mutex_lock(&smmu->stream_map_mutex);
	for_each_cfg_sme(cfg, i, idx) {
	for_each_cfg_sme(fwspec, i, idx) {
		if (arm_smmu_free_sme(smmu, idx))
			arm_smmu_write_sme(smmu, idx);
		cfg->smendx[i] = INVALID_SMENDX;
@@ -1947,7 +1945,7 @@ static void arm_smmu_master_free_smes(struct arm_smmu_master_cfg *cfg)
}

static int arm_smmu_domain_add_master(struct arm_smmu_domain *smmu_domain,
				      struct arm_smmu_master_cfg *cfg)
				      struct iommu_fwspec *fwspec)
{
	struct arm_smmu_device *smmu = smmu_domain->smmu;
	struct arm_smmu_s2cr *s2cr = smmu->s2crs;
@@ -1964,7 +1962,7 @@ static int arm_smmu_domain_add_master(struct arm_smmu_domain *smmu_domain,
	if (smmu_domain->domain.type == IOMMU_DOMAIN_DMA)
		type = S2CR_TYPE_BYPASS;

	for_each_cfg_sme(cfg, i, idx) {
	for_each_cfg_sme(fwspec, i, idx) {
		if (type == s2cr[idx].type && cbndx == s2cr[idx].cbndx)
			continue;

@@ -1993,8 +1991,6 @@ static void arm_smmu_detach_dev(struct iommu_domain *domain,
		return;
	}

	dev->archdata.iommu = NULL;

	/* Remove additional vote for atomic power */
	if (atomic_domain) {
		WARN_ON(arm_smmu_power_on_atomic(smmu->pwr));
@@ -2090,16 +2086,17 @@ static int arm_smmu_prepare_pgtable(void *addr, void *cookie)
static int arm_smmu_attach_dev(struct iommu_domain *domain, struct device *dev)
{
	int ret;
	struct arm_smmu_domain *smmu_domain = to_smmu_domain(domain);
	struct arm_smmu_master_cfg *cfg = dev->archdata.iommu;
	struct iommu_fwspec *fwspec = dev->iommu_fwspec;
	struct arm_smmu_device *smmu;
	struct arm_smmu_domain *smmu_domain = to_smmu_domain(domain);
	int atomic_domain = smmu_domain->attributes & (1 << DOMAIN_ATTR_ATOMIC);

	if (!cfg) {
	if (!fwspec || fwspec->ops != &arm_smmu_ops) {
		dev_err(dev, "cannot attach to SMMU, is it on the same bus?\n");
		return -ENXIO;
	}
	smmu = cfg->smmu;

	smmu = fwspec_smmu(fwspec);

	/* Enable Clocks and Power */
	ret = arm_smmu_power_on(smmu->pwr);
@@ -2107,7 +2104,7 @@ static int arm_smmu_attach_dev(struct iommu_domain *domain, struct device *dev)
		return ret;

	/* Ensure that the domain is finalised */
	ret = arm_smmu_init_domain_context(domain, cfg->smmu);
	ret = arm_smmu_init_domain_context(domain, smmu);
	if (ret < 0)
		goto out_power_off;

@@ -2121,17 +2118,16 @@ static int arm_smmu_attach_dev(struct iommu_domain *domain, struct device *dev)
	 * Sanity check the domain. We don't support domains across
	 * different SMMUs.
	 */
	if (smmu_domain->smmu != cfg->smmu) {
	if (smmu_domain->smmu != smmu) {
		dev_err(dev,
			"cannot attach to SMMU %s whilst already attached to domain on SMMU %s\n",
			dev_name(smmu_domain->smmu->dev),
				 dev_name(cfg->smmu->dev));
			dev_name(smmu_domain->smmu->dev), dev_name(smmu->dev));
		ret = -EINVAL;
		goto out_power_off;
	}

	/* Looks ok, so add the device to the domain */
	ret = arm_smmu_domain_add_master(smmu_domain, cfg);
	ret = arm_smmu_domain_add_master(smmu_domain, fwspec);

out_power_off:
	/*
@@ -2343,57 +2339,72 @@ static bool arm_smmu_capable(enum iommu_cap cap)

static int arm_smmu_add_device(struct device *dev)
{
	struct arm_smmu_device *smmu;
	struct arm_smmu_master_cfg *cfg;
	struct iommu_fwspec *fwspec;
	int i, ret;

	ret = arm_smmu_register_legacy_master(dev);
	cfg = dev->archdata.iommu;
	ret = arm_smmu_register_legacy_master(dev, &smmu);
	fwspec = dev->iommu_fwspec;
	if (ret)
		goto out_free;

	ret = -EINVAL;
	for (i = 0; i < cfg->num_streamids; i++) {
		u16 sid = cfg->streamids[i];
	for (i = 0; i < fwspec->num_ids; i++) {
		u16 sid = fwspec->ids[i];

		if (sid & ~cfg->smmu->streamid_mask) {
		if (sid & ~smmu->streamid_mask) {
			dev_err(dev, "stream ID 0x%x out of range for SMMU (0x%x)\n",
				sid, cfg->smmu->streamid_mask);
				sid, smmu->streamid_mask);
			goto out_free;
		}
		cfg->smendx[i] = INVALID_SMENDX;
	}

	ret = -ENOMEM;
	cfg = kzalloc(offsetof(struct arm_smmu_master_cfg, smendx[i]),
		      GFP_KERNEL);
	if (!cfg)
		goto out_free;

	cfg->smmu = smmu;
	fwspec->iommu_priv = cfg;
	while (i--)
		cfg->smendx[i] = INVALID_SMENDX;

	ret = arm_smmu_master_alloc_smes(dev);
	if (!ret)
		return ret;
	if (ret)
		goto out_free;

	return 0;

out_free:
	kfree(cfg);
	dev->archdata.iommu = NULL;
	if (fwspec)
		kfree(fwspec->iommu_priv);
	iommu_fwspec_free(dev);
	return ret;
}

static void arm_smmu_remove_device(struct device *dev)
{
	struct arm_smmu_master_cfg *cfg = dev->archdata.iommu;
	struct iommu_fwspec *fwspec = dev->iommu_fwspec;

	if (!cfg)
	if (!fwspec || fwspec->ops != &arm_smmu_ops)
		return;

	arm_smmu_master_free_smes(cfg);
	arm_smmu_master_free_smes(fwspec);
	iommu_group_remove_device(dev);
	kfree(cfg);
	dev->archdata.iommu = NULL;
	kfree(fwspec->iommu_priv);
	iommu_fwspec_free(dev);
}

static struct iommu_group *arm_smmu_device_group(struct device *dev)
{
	struct arm_smmu_master_cfg *cfg = dev->archdata.iommu;
	struct arm_smmu_device *smmu = cfg->smmu;
	struct iommu_fwspec *fwspec = dev->iommu_fwspec;
	struct arm_smmu_device *smmu = fwspec_smmu(fwspec);
	struct iommu_group *group = NULL;
	int i, idx;

	for_each_cfg_sme(cfg, i, idx) {
	for_each_cfg_sme(fwspec, i, idx) {
		if (group && smmu->s2crs[idx].group &&
		    group != smmu->s2crs[idx].group)
			return ERR_PTR(-EINVAL);
@@ -3637,6 +3648,7 @@ static int arm_smmu_device_dt_probe(struct platform_device *pdev)
	if (err)
		goto out_power_off;

	of_iommu_set_ops(dev->of_node, &arm_smmu_ops);
	platform_set_drvdata(pdev, smmu);
	arm_smmu_device_reset(smmu);
	arm_smmu_power_off(smmu->pwr);