Skip to content

Commit

Permalink
Merge branch 'for-joerg/arm-smmu/updates' of git://git.kernel.org/pub…
Browse files Browse the repository at this point in the history
…/scm/linux/kernel/git/will/linux into arm/smmu
  • Loading branch information
Joerg Roedel committed Feb 25, 2016
2 parents 81f70ba + cbf8277 commit 9e358e2
Show file tree
Hide file tree
Showing 3 changed files with 78 additions and 52 deletions.
50 changes: 27 additions & 23 deletions drivers/iommu/arm-smmu-v3.c
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
*/

#include <linux/delay.h>
#include <linux/dma-iommu.h>
#include <linux/err.h>
#include <linux/interrupt.h>
#include <linux/iommu.h>
Expand Down Expand Up @@ -1396,7 +1397,7 @@ static struct iommu_domain *arm_smmu_domain_alloc(unsigned type)
{
struct arm_smmu_domain *smmu_domain;

if (type != IOMMU_DOMAIN_UNMANAGED)
if (type != IOMMU_DOMAIN_UNMANAGED && type != IOMMU_DOMAIN_DMA)
return NULL;

/*
Expand All @@ -1408,6 +1409,12 @@ static struct iommu_domain *arm_smmu_domain_alloc(unsigned type)
if (!smmu_domain)
return NULL;

if (type == IOMMU_DOMAIN_DMA &&
iommu_get_dma_cookie(&smmu_domain->domain)) {
kfree(smmu_domain);
return NULL;
}

mutex_init(&smmu_domain->init_mutex);
spin_lock_init(&smmu_domain->pgtbl_lock);
return &smmu_domain->domain;
Expand Down Expand Up @@ -1436,6 +1443,7 @@ static void arm_smmu_domain_free(struct iommu_domain *domain)
struct arm_smmu_domain *smmu_domain = to_smmu_domain(domain);
struct arm_smmu_device *smmu = smmu_domain->smmu;

iommu_put_dma_cookie(domain);
free_io_pgtable_ops(smmu_domain->pgtbl_ops);

/* Free the CD and ASID, if we allocated them */
Expand Down Expand Up @@ -1630,6 +1638,17 @@ static int arm_smmu_install_ste_for_group(struct arm_smmu_group *smmu_group)
return 0;
}

static void arm_smmu_detach_dev(struct device *dev)
{
struct arm_smmu_group *smmu_group = arm_smmu_group_get(dev);

smmu_group->ste.bypass = true;
if (IS_ERR_VALUE(arm_smmu_install_ste_for_group(smmu_group)))
dev_warn(dev, "failed to install bypass STE\n");

smmu_group->domain = NULL;
}

static int arm_smmu_attach_dev(struct iommu_domain *domain, struct device *dev)
{
int ret = 0;
Expand All @@ -1642,7 +1661,7 @@ static int arm_smmu_attach_dev(struct iommu_domain *domain, struct device *dev)

/* Already attached to a different domain? */
if (smmu_group->domain && smmu_group->domain != smmu_domain)
return -EEXIST;
arm_smmu_detach_dev(dev);

smmu = smmu_group->smmu;
mutex_lock(&smmu_domain->init_mutex);
Expand All @@ -1668,7 +1687,12 @@ static int arm_smmu_attach_dev(struct iommu_domain *domain, struct device *dev)
goto out_unlock;

smmu_group->domain = smmu_domain;
smmu_group->ste.bypass = false;

/*
* FIXME: This should always be "false" once we have IOMMU-backed
* DMA ops for all devices behind the SMMU.
*/
smmu_group->ste.bypass = domain->type == IOMMU_DOMAIN_DMA;

ret = arm_smmu_install_ste_for_group(smmu_group);
if (IS_ERR_VALUE(ret))
Expand All @@ -1679,25 +1703,6 @@ static int arm_smmu_attach_dev(struct iommu_domain *domain, struct device *dev)
return ret;
}

static void arm_smmu_detach_dev(struct iommu_domain *domain, struct device *dev)
{
struct arm_smmu_domain *smmu_domain = to_smmu_domain(domain);
struct arm_smmu_group *smmu_group = arm_smmu_group_get(dev);

BUG_ON(!smmu_domain);
BUG_ON(!smmu_group);

mutex_lock(&smmu_domain->init_mutex);
BUG_ON(smmu_group->domain != smmu_domain);

smmu_group->ste.bypass = true;
if (IS_ERR_VALUE(arm_smmu_install_ste_for_group(smmu_group)))
dev_warn(dev, "failed to install bypass STE\n");

smmu_group->domain = NULL;
mutex_unlock(&smmu_domain->init_mutex);
}

static int arm_smmu_map(struct iommu_domain *domain, unsigned long iova,
phys_addr_t paddr, size_t size, int prot)
{
Expand Down Expand Up @@ -1935,7 +1940,6 @@ static struct iommu_ops arm_smmu_ops = {
.domain_alloc = arm_smmu_domain_alloc,
.domain_free = arm_smmu_domain_free,
.attach_dev = arm_smmu_attach_dev,
.detach_dev = arm_smmu_detach_dev,
.map = arm_smmu_map,
.unmap = arm_smmu_unmap,
.iova_to_phys = arm_smmu_iova_to_phys,
Expand Down
79 changes: 50 additions & 29 deletions drivers/iommu/arm-smmu.c
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
#define pr_fmt(fmt) "arm-smmu: " fmt

#include <linux/delay.h>
#include <linux/dma-iommu.h>
#include <linux/dma-mapping.h>
#include <linux/err.h>
#include <linux/interrupt.h>
Expand Down Expand Up @@ -167,6 +168,9 @@
#define S2CR_TYPE_BYPASS (1 << S2CR_TYPE_SHIFT)
#define S2CR_TYPE_FAULT (2 << S2CR_TYPE_SHIFT)

#define S2CR_PRIVCFG_SHIFT 24
#define S2CR_PRIVCFG_UNPRIV (2 << S2CR_PRIVCFG_SHIFT)

/* Context bank attribute registers */
#define ARM_SMMU_GR1_CBAR(n) (0x0 + ((n) << 2))
#define CBAR_VMID_SHIFT 0
Expand Down Expand Up @@ -257,9 +261,13 @@
#define FSYNR0_WNR (1 << 4)

static int force_stage;
module_param_named(force_stage, force_stage, int, S_IRUGO);
module_param(force_stage, int, S_IRUGO);
MODULE_PARM_DESC(force_stage,
"Force SMMU mappings to be installed at a particular stage of translation. A value of '1' or '2' forces the corresponding stage. All other values are ignored (i.e. no stage is forced). Note that selecting a specific stage will disable support for nested translation.");
static bool disable_bypass;
module_param(disable_bypass, bool, S_IRUGO);
MODULE_PARM_DESC(disable_bypass,
"Disable bypass streams such that incoming transactions from devices that are not attached to an iommu domain will report an abort back to the device and will not be allowed to pass through the SMMU.");

enum arm_smmu_arch_version {
ARM_SMMU_V1 = 1,
Expand Down Expand Up @@ -963,7 +971,7 @@ static struct iommu_domain *arm_smmu_domain_alloc(unsigned type)
{
struct arm_smmu_domain *smmu_domain;

if (type != IOMMU_DOMAIN_UNMANAGED)
if (type != IOMMU_DOMAIN_UNMANAGED && type != IOMMU_DOMAIN_DMA)
return NULL;
/*
* Allocate the domain and initialise some of its data structures.
Expand All @@ -974,6 +982,12 @@ static struct iommu_domain *arm_smmu_domain_alloc(unsigned type)
if (!smmu_domain)
return NULL;

if (type == IOMMU_DOMAIN_DMA &&
iommu_get_dma_cookie(&smmu_domain->domain)) {
kfree(smmu_domain);
return NULL;
}

mutex_init(&smmu_domain->init_mutex);
spin_lock_init(&smmu_domain->pgtbl_lock);

Expand All @@ -988,6 +1002,7 @@ static void arm_smmu_domain_free(struct iommu_domain *domain)
* Free the domain resources. We assume that all devices have
* already been detached.
*/
iommu_put_dma_cookie(domain);
arm_smmu_destroy_domain_context(domain);
kfree(smmu_domain);
}
Expand Down Expand Up @@ -1079,11 +1094,18 @@ static int arm_smmu_domain_add_master(struct arm_smmu_domain *smmu_domain,
if (ret)
return ret == -EEXIST ? 0 : ret;

/*
* FIXME: This won't be needed once we have IOMMU-backed DMA ops
* for all devices behind the SMMU.
*/
if (smmu_domain->domain.type == IOMMU_DOMAIN_DMA)
return 0;

for (i = 0; i < cfg->num_streamids; ++i) {
u32 idx, s2cr;

idx = cfg->smrs ? cfg->smrs[i].idx : cfg->streamids[i];
s2cr = S2CR_TYPE_TRANS |
s2cr = S2CR_TYPE_TRANS | S2CR_PRIVCFG_UNPRIV |
(smmu_domain->cfg.cbndx << S2CR_CBNDX_SHIFT);
writel_relaxed(s2cr, gr0_base + ARM_SMMU_GR0_S2CR(idx));
}
Expand All @@ -1108,14 +1130,24 @@ static void arm_smmu_domain_remove_master(struct arm_smmu_domain *smmu_domain,
*/
for (i = 0; i < cfg->num_streamids; ++i) {
u32 idx = cfg->smrs ? cfg->smrs[i].idx : cfg->streamids[i];
u32 reg = disable_bypass ? S2CR_TYPE_FAULT : S2CR_TYPE_BYPASS;

writel_relaxed(S2CR_TYPE_BYPASS,
gr0_base + ARM_SMMU_GR0_S2CR(idx));
writel_relaxed(reg, gr0_base + ARM_SMMU_GR0_S2CR(idx));
}

arm_smmu_master_free_smrs(smmu, cfg);
}

static void arm_smmu_detach_dev(struct device *dev,
struct arm_smmu_master_cfg *cfg)
{
struct iommu_domain *domain = dev->archdata.iommu;
struct arm_smmu_domain *smmu_domain = to_smmu_domain(domain);

dev->archdata.iommu = NULL;
arm_smmu_domain_remove_master(smmu_domain, cfg);
}

static int arm_smmu_attach_dev(struct iommu_domain *domain, struct device *dev)
{
int ret;
Expand All @@ -1129,11 +1161,6 @@ static int arm_smmu_attach_dev(struct iommu_domain *domain, struct device *dev)
return -ENXIO;
}

if (dev->archdata.iommu) {
dev_err(dev, "already attached to IOMMU domain\n");
return -EEXIST;
}

/* Ensure that the domain is finalised */
ret = arm_smmu_init_domain_context(domain, smmu);
if (IS_ERR_VALUE(ret))
Expand All @@ -1155,25 +1182,16 @@ static int arm_smmu_attach_dev(struct iommu_domain *domain, struct device *dev)
if (!cfg)
return -ENODEV;

/* Detach the dev from its current domain */
if (dev->archdata.iommu)
arm_smmu_detach_dev(dev, cfg);

ret = arm_smmu_domain_add_master(smmu_domain, cfg);
if (!ret)
dev->archdata.iommu = domain;
return ret;
}

static void arm_smmu_detach_dev(struct iommu_domain *domain, struct device *dev)
{
struct arm_smmu_domain *smmu_domain = to_smmu_domain(domain);
struct arm_smmu_master_cfg *cfg;

cfg = find_smmu_master_cfg(dev);
if (!cfg)
return;

dev->archdata.iommu = NULL;
arm_smmu_domain_remove_master(smmu_domain, cfg);
}

static int arm_smmu_map(struct iommu_domain *domain, unsigned long iova,
phys_addr_t paddr, size_t size, int prot)
{
Expand Down Expand Up @@ -1449,7 +1467,6 @@ static struct iommu_ops arm_smmu_ops = {
.domain_alloc = arm_smmu_domain_alloc,
.domain_free = arm_smmu_domain_free,
.attach_dev = arm_smmu_attach_dev,
.detach_dev = arm_smmu_detach_dev,
.map = arm_smmu_map,
.unmap = arm_smmu_unmap,
.map_sg = default_iommu_map_sg,
Expand All @@ -1473,11 +1490,11 @@ static void arm_smmu_device_reset(struct arm_smmu_device *smmu)
reg = readl_relaxed(ARM_SMMU_GR0_NS(smmu) + ARM_SMMU_GR0_sGFSR);
writel(reg, ARM_SMMU_GR0_NS(smmu) + ARM_SMMU_GR0_sGFSR);

/* Mark all SMRn as invalid and all S2CRn as bypass */
/* Mark all SMRn as invalid and all S2CRn as bypass unless overridden */
reg = disable_bypass ? S2CR_TYPE_FAULT : S2CR_TYPE_BYPASS;
for (i = 0; i < smmu->num_mapping_groups; ++i) {
writel_relaxed(0, gr0_base + ARM_SMMU_GR0_SMR(i));
writel_relaxed(S2CR_TYPE_BYPASS,
gr0_base + ARM_SMMU_GR0_S2CR(i));
writel_relaxed(reg, gr0_base + ARM_SMMU_GR0_S2CR(i));
}

/* Make sure all context banks are disabled and clear CB_FSR */
Expand All @@ -1499,8 +1516,12 @@ static void arm_smmu_device_reset(struct arm_smmu_device *smmu)
/* Disable TLB broadcasting. */
reg |= (sCR0_VMIDPNE | sCR0_PTM);

/* Enable client access, but bypass when no mapping is found */
reg &= ~(sCR0_CLIENTPD | sCR0_USFCFG);
/* Enable client access, handling unmatched streams as appropriate */
reg &= ~sCR0_CLIENTPD;
if (disable_bypass)
reg |= sCR0_USFCFG;
else
reg &= ~sCR0_USFCFG;

/* Disable forced broadcasting */
reg &= ~sCR0_FB;
Expand Down
1 change: 1 addition & 0 deletions drivers/iommu/of_iommu.c
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,7 @@ void of_iommu_set_ops(struct device_node *np, struct iommu_ops *ops)
if (WARN_ON(!iommu))
return;

of_node_get(np);
INIT_LIST_HEAD(&iommu->list);
iommu->np = np;
iommu->ops = ops;
Expand Down

0 comments on commit 9e358e2

Please sign in to comment.