IOMMU Updates for Linux v3.12

This round the updates contain:
 
 	* A new driver for the Freescale PAMU IOMMU from Varun Sethi.
 	  This driver has cooked for a while and required changes to the
 	  IOMMU-API and infrastructure that were already merged before.
 	* Updates for the ARM-SMMU driver from Will Deacon
 	* Various fixes, the most important one is probably a fix from
 	  Alex Williamson for a memory leak in the VT-d page-table
 	  freeing code
 
 In summary not all that much. The biggest part in the diffstat is the
 new PAMU driver.
 -----BEGIN PGP SIGNATURE-----
 Version: GnuPG v1.4.11 (GNU/Linux)
 
 iQIcBAABAgAGBQJSMdWFAAoJECvwRC2XARrjZbkP/3lYpEjd1SmqZAVUTPQw/H1Y
 9DHFs39WZddlz73YkF2yDyprjdi2b8wUzOJGr0BJ0AWb97l3bcvouqRaw0Q8Sghc
 sHYHHF/L/n6xkDVd8OXTGgQukjOu16yb1Ai1jlvlNgrB8T9lA0QKjSIDfVVJb99c
 qGnO58UqnxOC7zzL5iqDfkgffre+dw4Ik2BddN6+gdPV907wsk7ze5nTDNTMkXso
 oGi7jwbOTkuWyI6ST1GnkSV9bB1yUPR0Np0sFSOtGbsRSDOA4Ta96AHygZ3kPza+
 ErylGBlHj0KG7oH7m3GOQAso6MeNdHa+7aIewaLz2NKundhPA6Kb3hFdghjGGPzR
 ubJ3IiG7X/MPrp8iwNsPDoCaRkWWGR80L9vIlhD+yvfCx8PkkEUoEIbf1k4Gm0Ry
 5ouROU77Ha2P6ZuGvPCTlok4ggKkV2mHdUuetC/04ETvA3kN+2TGjya/1wL+X+H/
 fV3jyBRYWFaXNzKl3qKfol2ETG3hQA5NGNKuHMTJz8CF8jHSJeijDCeiWv363h62
 oQ+CrUG7FJ4B9ZITGDzxA0MdFs5TIqRRp2vY8onaok5YAR3U/iiKRRv+YjIjZuE4
 CTshhbb/mwwaTKvq8pq9xs/3rhGX+3HSP4jAzNWUJPYgouE+rvHq/H1ApI89IxJF
 1wYemwLPo3fMcgOvw8pm
 =UZoD
 -----END PGP SIGNATURE-----

Merge tag 'iommu-updates-v3.12' of git://git.kernel.org/pub/scm/linux/kernel/git/joro/iommu

Pull IOMMU Updates from Joerg Roedel:
 "This round the updates contain:

   - A new driver for the Freescale PAMU IOMMU from Varun Sethi.

     This driver has cooked for a while and required changes to the
     IOMMU-API and infrastructure that were already merged before.

   - Updates for the ARM-SMMU driver from Will Deacon

   - Various fixes, the most important one is probably a fix from Alex
     Williamson for a memory leak in the VT-d page-table freeing code

  In summary not all that much.  The biggest part in the diffstat is the
  new PAMU driver"

* tag 'iommu-updates-v3.12' of git://git.kernel.org/pub/scm/linux/kernel/git/joro/iommu:
  intel-iommu: Fix leaks in pagetable freeing
  iommu/amd: Fix resource leak in iommu_init_device()
  iommu/amd: Clean up unnecessary MSI/MSI-X capability find
  iommu/arm-smmu: Simplify VMID and ASID allocation
  iommu/arm-smmu: Don't use VMIDs for stage-1 translations
  iommu/arm-smmu: Tighten up global fault reporting
  iommu/arm-smmu: Remove broken big-endian check
  iommu/fsl: Remove unnecessary 'fsl-pamu' prefixes
  iommu/fsl: Fix whitespace problems noticed by git-am
  iommu/fsl: Freescale PAMU driver and iommu implementation.
  iommu/fsl: Add additional iommu attributes required by the PAMU driver.
  powerpc: Add iommu domain pointer to device archdata
  iommu/exynos: Remove dead code (set_prefbuf)
This commit is contained in:
Linus Torvalds 2013-09-12 11:29:26 -07:00
commit e5d0c87439
15 changed files with 3145 additions and 120 deletions

View file

@ -28,6 +28,9 @@ struct dev_archdata {
void *iommu_table_base;
} dma_data;
#ifdef CONFIG_IOMMU_API
void *iommu_domain;
#endif
#ifdef CONFIG_SWIOTLB
dma_addr_t max_direct_dma_addr;
#endif

View file

@ -0,0 +1,39 @@
/*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License, version 2, as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* Copyright (C) 2013 Freescale Semiconductor, Inc.
*
*/
#ifndef __FSL_PAMU_STASH_H
#define __FSL_PAMU_STASH_H
/* cache stash targets */
enum pamu_stash_target {
PAMU_ATTR_CACHE_L1 = 1,
PAMU_ATTR_CACHE_L2,
PAMU_ATTR_CACHE_L3,
};
/*
* This attribute allows configuring stashig specific parameters
* in the PAMU hardware.
*/
struct pamu_stash_attribute {
u32 cpu; /* cpu number */
u32 cache; /* cache to stash to: L1,L2,L3 */
};
#endif /* __FSL_PAMU_STASH_H */

View file

@ -16,6 +16,11 @@
struct platform_device;
/* FSL PCI controller BRR1 register */
#define PCI_FSL_BRR1 0xbf8
#define PCI_FSL_BRR1_VER 0xffff
#define PCIE_LTSSM 0x0404 /* PCIE Link Training and Status */
#define PCIE_LTSSM_L0 0x16 /* L0 state */
#define PCIE_IP_REV_2_2 0x02080202 /* PCIE IP block version Rev2.2 */

View file

@ -17,6 +17,16 @@ config OF_IOMMU
def_bool y
depends on OF
config FSL_PAMU
bool "Freescale IOMMU support"
depends on PPC_E500MC
select IOMMU_API
select GENERIC_ALLOCATOR
help
Freescale PAMU support. PAMU is the IOMMU present on Freescale QorIQ platforms.
PAMU can authorize memory access, remap the memory address, and remap I/O
transaction types.
# MSM IOMMU support
config MSM_IOMMU
bool "MSM IOMMU Support"

View file

@ -16,3 +16,4 @@ obj-$(CONFIG_TEGRA_IOMMU_SMMU) += tegra-smmu.o
obj-$(CONFIG_EXYNOS_IOMMU) += exynos-iommu.o
obj-$(CONFIG_SHMOBILE_IOMMU) += shmobile-iommu.o
obj-$(CONFIG_SHMOBILE_IPMMU) += shmobile-ipmmu.o
obj-$(CONFIG_FSL_PAMU) += fsl_pamu.o fsl_pamu_domain.o

View file

@ -456,8 +456,10 @@ static int iommu_init_device(struct device *dev)
}
ret = init_iommu_group(dev);
if (ret)
if (ret) {
free_dev_data(dev_data);
return ret;
}
if (pci_iommuv2_capable(pdev)) {
struct amd_iommu *iommu;

View file

@ -1384,7 +1384,7 @@ static int iommu_init_msi(struct amd_iommu *iommu)
if (iommu->int_enabled)
goto enable_faults;
if (pci_find_capability(iommu->dev, PCI_CAP_ID_MSI))
if (iommu->dev->msi_cap)
ret = iommu_setup_msi(iommu);
else
ret = -ENODEV;

View file

@ -56,9 +56,6 @@
/* Maximum number of mapping groups per SMMU */
#define ARM_SMMU_MAX_SMRS 128
/* Number of VMIDs per SMMU */
#define ARM_SMMU_NUM_VMIDS 256
/* SMMU global address space */
#define ARM_SMMU_GR0(smmu) ((smmu)->base)
#define ARM_SMMU_GR1(smmu) ((smmu)->base + (smmu)->pagesize)
@ -87,6 +84,7 @@
#define ARM_SMMU_PTE_AP_UNPRIV (((pteval_t)1) << 6)
#define ARM_SMMU_PTE_AP_RDONLY (((pteval_t)2) << 6)
#define ARM_SMMU_PTE_ATTRINDX_SHIFT 2
#define ARM_SMMU_PTE_nG (((pteval_t)1) << 11)
/* Stage-2 PTE */
#define ARM_SMMU_PTE_HAP_FAULT (((pteval_t)0) << 6)
@ -223,6 +221,7 @@
#define ARM_SMMU_CB_FAR_LO 0x60
#define ARM_SMMU_CB_FAR_HI 0x64
#define ARM_SMMU_CB_FSYNR0 0x68
#define ARM_SMMU_CB_S1_TLBIASID 0x610
#define SCTLR_S1_ASIDPNE (1 << 12)
#define SCTLR_CFCFG (1 << 7)
@ -282,6 +281,8 @@
#define TTBCR2_ADDR_44 4
#define TTBCR2_ADDR_48 5
#define TTBRn_HI_ASID_SHIFT 16
#define MAIR_ATTR_SHIFT(n) ((n) << 3)
#define MAIR_ATTR_MASK 0xff
#define MAIR_ATTR_DEVICE 0x04
@ -305,7 +306,7 @@
#define FSR_IGN (FSR_AFF | FSR_ASF | FSR_TLBMCF | \
FSR_TLBLKF)
#define FSR_FAULT (FSR_MULTI | FSR_SS | FSR_UUT | \
FSR_EF | FSR_PF | FSR_TF)
FSR_EF | FSR_PF | FSR_TF | FSR_IGN)
#define FSYNR0_WNR (1 << 4)
@ -365,21 +366,21 @@ struct arm_smmu_device {
u32 num_context_irqs;
unsigned int *irqs;
DECLARE_BITMAP(vmid_map, ARM_SMMU_NUM_VMIDS);
struct list_head list;
struct rb_root masters;
};
struct arm_smmu_cfg {
struct arm_smmu_device *smmu;
u8 vmid;
u8 cbndx;
u8 irptndx;
u32 cbar;
pgd_t *pgd;
};
#define ARM_SMMU_CB_ASID(cfg) ((cfg)->cbndx)
#define ARM_SMMU_CB_VMID(cfg) ((cfg)->cbndx + 1)
struct arm_smmu_domain {
/*
* A domain can span across multiple, chained SMMUs and requires
@ -533,6 +534,25 @@ static void arm_smmu_tlb_sync(struct arm_smmu_device *smmu)
}
}
static void arm_smmu_tlb_inv_context(struct arm_smmu_cfg *cfg)
{
struct arm_smmu_device *smmu = cfg->smmu;
void __iomem *base = ARM_SMMU_GR0(smmu);
bool stage1 = cfg->cbar != CBAR_TYPE_S2_TRANS;
if (stage1) {
base = ARM_SMMU_CB_BASE(smmu) + ARM_SMMU_CB(smmu, cfg->cbndx);
writel_relaxed(ARM_SMMU_CB_ASID(cfg),
base + ARM_SMMU_CB_S1_TLBIASID);
} else {
base = ARM_SMMU_GR0(smmu);
writel_relaxed(ARM_SMMU_CB_VMID(cfg),
base + ARM_SMMU_GR0_TLBIVMID);
}
arm_smmu_tlb_sync(smmu);
}
static irqreturn_t arm_smmu_context_fault(int irq, void *dev)
{
int flags, ret;
@ -590,6 +610,9 @@ static irqreturn_t arm_smmu_global_fault(int irq, void *dev)
void __iomem *gr0_base = ARM_SMMU_GR0(smmu);
gfsr = readl_relaxed(gr0_base + ARM_SMMU_GR0_sGFSR);
if (!gfsr)
return IRQ_NONE;
gfsynr0 = readl_relaxed(gr0_base + ARM_SMMU_GR0_sGFSYNR0);
gfsynr1 = readl_relaxed(gr0_base + ARM_SMMU_GR0_sGFSYNR1);
gfsynr2 = readl_relaxed(gr0_base + ARM_SMMU_GR0_sGFSYNR2);
@ -601,7 +624,7 @@ static irqreturn_t arm_smmu_global_fault(int irq, void *dev)
gfsr, gfsynr0, gfsynr1, gfsynr2);
writel(gfsr, gr0_base + ARM_SMMU_GR0_sGFSR);
return IRQ_NONE;
return IRQ_HANDLED;
}
static void arm_smmu_init_context_bank(struct arm_smmu_domain *smmu_domain)
@ -618,14 +641,15 @@ static void arm_smmu_init_context_bank(struct arm_smmu_domain *smmu_domain)
cb_base = ARM_SMMU_CB_BASE(smmu) + ARM_SMMU_CB(smmu, root_cfg->cbndx);
/* CBAR */
reg = root_cfg->cbar |
(root_cfg->vmid << CBAR_VMID_SHIFT);
reg = root_cfg->cbar;
if (smmu->version == 1)
reg |= root_cfg->irptndx << CBAR_IRPTNDX_SHIFT;
/* Use the weakest memory type, so it is overridden by the pte */
if (stage1)
reg |= (CBAR_S1_MEMATTR_WB << CBAR_S1_MEMATTR_SHIFT);
else
reg |= ARM_SMMU_CB_VMID(root_cfg) << CBAR_VMID_SHIFT;
writel_relaxed(reg, gr1_base + ARM_SMMU_GR1_CBAR(root_cfg->cbndx));
if (smmu->version > 1) {
@ -687,15 +711,11 @@ static void arm_smmu_init_context_bank(struct arm_smmu_domain *smmu_domain)
/* TTBR0 */
reg = __pa(root_cfg->pgd);
#ifndef __BIG_ENDIAN
writel_relaxed(reg, cb_base + ARM_SMMU_CB_TTBR0_LO);
reg = (phys_addr_t)__pa(root_cfg->pgd) >> 32;
if (stage1)
reg |= ARM_SMMU_CB_ASID(root_cfg) << TTBRn_HI_ASID_SHIFT;
writel_relaxed(reg, cb_base + ARM_SMMU_CB_TTBR0_HI);
#else
writel_relaxed(reg, cb_base + ARM_SMMU_CB_TTBR0_HI);
reg = (phys_addr_t)__pa(root_cfg->pgd) >> 32;
writel_relaxed(reg, cb_base + ARM_SMMU_CB_TTBR0_LO);
#endif
/*
* TTBCR
@ -750,10 +770,6 @@ static void arm_smmu_init_context_bank(struct arm_smmu_domain *smmu_domain)
writel_relaxed(reg, cb_base + ARM_SMMU_CB_S1_MAIR0);
}
/* Nuke the TLB */
writel_relaxed(root_cfg->vmid, gr0_base + ARM_SMMU_GR0_TLBIVMID);
arm_smmu_tlb_sync(smmu);
/* SCTLR */
reg = SCTLR_CFCFG | SCTLR_CFIE | SCTLR_CFRE | SCTLR_M | SCTLR_EAE_SBOP;
if (stage1)
@ -790,11 +806,6 @@ static int arm_smmu_init_domain_context(struct iommu_domain *domain,
return -ENODEV;
}
ret = __arm_smmu_alloc_bitmap(smmu->vmid_map, 0, ARM_SMMU_NUM_VMIDS);
if (IS_ERR_VALUE(ret))
return ret;
root_cfg->vmid = ret;
if (smmu->features & ARM_SMMU_FEAT_TRANS_NESTED) {
/*
* We will likely want to change this if/when KVM gets
@ -813,10 +824,9 @@ static int arm_smmu_init_domain_context(struct iommu_domain *domain,
ret = __arm_smmu_alloc_bitmap(smmu->context_map, start,
smmu->num_context_banks);
if (IS_ERR_VALUE(ret))
goto out_free_vmid;
return ret;
root_cfg->cbndx = ret;
if (smmu->version == 1) {
root_cfg->irptndx = atomic_inc_return(&smmu->irptndx);
root_cfg->irptndx %= smmu->num_context_irqs;
@ -840,8 +850,6 @@ static int arm_smmu_init_domain_context(struct iommu_domain *domain,
out_free_context:
__arm_smmu_free_bitmap(smmu->context_map, root_cfg->cbndx);
out_free_vmid:
__arm_smmu_free_bitmap(smmu->vmid_map, root_cfg->vmid);
return ret;
}
@ -850,17 +858,22 @@ static void arm_smmu_destroy_domain_context(struct iommu_domain *domain)
struct arm_smmu_domain *smmu_domain = domain->priv;
struct arm_smmu_cfg *root_cfg = &smmu_domain->root_cfg;
struct arm_smmu_device *smmu = root_cfg->smmu;
void __iomem *cb_base;
int irq;
if (!smmu)
return;
/* Disable the context bank and nuke the TLB before freeing it. */
cb_base = ARM_SMMU_CB_BASE(smmu) + ARM_SMMU_CB(smmu, root_cfg->cbndx);
writel_relaxed(0, cb_base + ARM_SMMU_CB_SCTLR);
arm_smmu_tlb_inv_context(root_cfg);
if (root_cfg->irptndx != -1) {
irq = smmu->irqs[smmu->num_global_irqs + root_cfg->irptndx];
free_irq(irq, domain);
}
__arm_smmu_free_bitmap(smmu->vmid_map, root_cfg->vmid);
__arm_smmu_free_bitmap(smmu->context_map, root_cfg->cbndx);
}
@ -959,6 +972,11 @@ static void arm_smmu_free_pgtables(struct arm_smmu_domain *smmu_domain)
static void arm_smmu_domain_destroy(struct iommu_domain *domain)
{
struct arm_smmu_domain *smmu_domain = domain->priv;
/*
* Free the domain resources. We assume that all devices have
* already been detached.
*/
arm_smmu_destroy_domain_context(domain);
arm_smmu_free_pgtables(smmu_domain);
kfree(smmu_domain);
@ -1199,7 +1217,7 @@ static int arm_smmu_alloc_init_pte(struct arm_smmu_device *smmu, pmd_t *pmd,
}
if (stage == 1) {
pteval |= ARM_SMMU_PTE_AP_UNPRIV;
pteval |= ARM_SMMU_PTE_AP_UNPRIV | ARM_SMMU_PTE_nG;
if (!(flags & IOMMU_WRITE) && (flags & IOMMU_READ))
pteval |= ARM_SMMU_PTE_AP_RDONLY;
@ -1415,13 +1433,9 @@ static size_t arm_smmu_unmap(struct iommu_domain *domain, unsigned long iova,
{
int ret;
struct arm_smmu_domain *smmu_domain = domain->priv;
struct arm_smmu_cfg *root_cfg = &smmu_domain->root_cfg;
struct arm_smmu_device *smmu = root_cfg->smmu;
void __iomem *gr0_base = ARM_SMMU_GR0(smmu);
ret = arm_smmu_handle_mapping(smmu_domain, iova, 0, size, 0);
writel_relaxed(root_cfg->vmid, gr0_base + ARM_SMMU_GR0_TLBIVMID);
arm_smmu_tlb_sync(smmu);
arm_smmu_tlb_inv_context(&smmu_domain->root_cfg);
return ret ? ret : size;
}
@ -1544,6 +1558,7 @@ static struct iommu_ops arm_smmu_ops = {
static void arm_smmu_device_reset(struct arm_smmu_device *smmu)
{
void __iomem *gr0_base = ARM_SMMU_GR0(smmu);
void __iomem *sctlr_base = ARM_SMMU_CB_BASE(smmu) + ARM_SMMU_CB_SCTLR;
int i = 0;
u32 scr0 = readl_relaxed(gr0_base + ARM_SMMU_GR0_sCR0);
@ -1553,6 +1568,10 @@ static void arm_smmu_device_reset(struct arm_smmu_device *smmu)
writel_relaxed(S2CR_TYPE_BYPASS, gr0_base + ARM_SMMU_GR0_S2CR(i));
}
/* Make sure all context banks are disabled */
for (i = 0; i < smmu->num_context_banks; ++i)
writel_relaxed(0, sctlr_base + ARM_SMMU_CB(smmu, i));
/* Invalidate the TLB, just in case */
writel_relaxed(0, gr0_base + ARM_SMMU_GR0_STLBIALL);
writel_relaxed(0, gr0_base + ARM_SMMU_GR0_TLBIALLH);
@ -1906,7 +1925,7 @@ static int arm_smmu_device_remove(struct platform_device *pdev)
of_node_put(master->of_node);
}
if (!bitmap_empty(smmu->vmid_map, ARM_SMMU_NUM_VMIDS))
if (!bitmap_empty(smmu->context_map, ARM_SMMU_MAX_CBS))
dev_err(dev, "removing device with active domains!\n");
for (i = 0; i < smmu->num_global_irqs; ++i)

View file

@ -247,50 +247,6 @@ static void __sysmmu_set_prefbuf(void __iomem *sfrbase, unsigned long base,
__raw_writel(size - 1 + base, sfrbase + REG_PB0_EADDR + idx * 8);
}
void exynos_sysmmu_set_prefbuf(struct device *dev,
unsigned long base0, unsigned long size0,
unsigned long base1, unsigned long size1)
{
struct sysmmu_drvdata *data = dev_get_drvdata(dev->archdata.iommu);
unsigned long flags;
int i;
BUG_ON((base0 + size0) <= base0);
BUG_ON((size1 > 0) && ((base1 + size1) <= base1));
read_lock_irqsave(&data->lock, flags);
if (!is_sysmmu_active(data))
goto finish;
for (i = 0; i < data->nsfrs; i++) {
if ((readl(data->sfrbases[i] + REG_MMU_VERSION) >> 28) == 3) {
if (!sysmmu_block(data->sfrbases[i]))
continue;
if (size1 == 0) {
if (size0 <= SZ_128K) {
base1 = base0;
size1 = size0;
} else {
size1 = size0 -
ALIGN(size0 / 2, SZ_64K);
size0 = size0 - size1;
base1 = base0 + size0;
}
}
__sysmmu_set_prefbuf(
data->sfrbases[i], base0, size0, 0);
__sysmmu_set_prefbuf(
data->sfrbases[i], base1, size1, 1);
sysmmu_unblock(data->sfrbases[i]);
}
}
finish:
read_unlock_irqrestore(&data->lock, flags);
}
static void __set_fault_handler(struct sysmmu_drvdata *data,
sysmmu_fault_handler_t handler)
{

1309
drivers/iommu/fsl_pamu.c Normal file

File diff suppressed because it is too large Load diff

410
drivers/iommu/fsl_pamu.h Normal file
View file

@ -0,0 +1,410 @@
/*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License, version 2, as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* Copyright (C) 2013 Freescale Semiconductor, Inc.
*
*/
#ifndef __FSL_PAMU_H
#define __FSL_PAMU_H
#include <asm/fsl_pamu_stash.h>
/* Bit Field macros
* v = bit field variable; m = mask, m##_SHIFT = shift, x = value to load
*/
#define set_bf(v, m, x) (v = ((v) & ~(m)) | (((x) << (m##_SHIFT)) & (m)))
#define get_bf(v, m) (((v) & (m)) >> (m##_SHIFT))
/* PAMU CCSR space */
#define PAMU_PGC 0x00000000 /* Allows all peripheral accesses */
#define PAMU_PE 0x40000000 /* enable PAMU */
/* PAMU_OFFSET to the next pamu space in ccsr */
#define PAMU_OFFSET 0x1000
#define PAMU_MMAP_REGS_BASE 0
struct pamu_mmap_regs {
u32 ppbah;
u32 ppbal;
u32 pplah;
u32 pplal;
u32 spbah;
u32 spbal;
u32 splah;
u32 splal;
u32 obah;
u32 obal;
u32 olah;
u32 olal;
};
/* PAMU Error Registers */
#define PAMU_POES1 0x0040
#define PAMU_POES2 0x0044
#define PAMU_POEAH 0x0048
#define PAMU_POEAL 0x004C
#define PAMU_AVS1 0x0050
#define PAMU_AVS1_AV 0x1
#define PAMU_AVS1_OTV 0x6
#define PAMU_AVS1_APV 0x78
#define PAMU_AVS1_WAV 0x380
#define PAMU_AVS1_LAV 0x1c00
#define PAMU_AVS1_GCV 0x2000
#define PAMU_AVS1_PDV 0x4000
#define PAMU_AV_MASK (PAMU_AVS1_AV | PAMU_AVS1_OTV | PAMU_AVS1_APV | PAMU_AVS1_WAV \
| PAMU_AVS1_LAV | PAMU_AVS1_GCV | PAMU_AVS1_PDV)
#define PAMU_AVS1_LIODN_SHIFT 16
#define PAMU_LAV_LIODN_NOT_IN_PPAACT 0x400
#define PAMU_AVS2 0x0054
#define PAMU_AVAH 0x0058
#define PAMU_AVAL 0x005C
#define PAMU_EECTL 0x0060
#define PAMU_EEDIS 0x0064
#define PAMU_EEINTEN 0x0068
#define PAMU_EEDET 0x006C
#define PAMU_EEATTR 0x0070
#define PAMU_EEAHI 0x0074
#define PAMU_EEALO 0x0078
#define PAMU_EEDHI 0X007C
#define PAMU_EEDLO 0x0080
#define PAMU_EECC 0x0084
#define PAMU_UDAD 0x0090
/* PAMU Revision Registers */
#define PAMU_PR1 0x0BF8
#define PAMU_PR2 0x0BFC
/* PAMU version mask */
#define PAMU_PR1_MASK 0xffff
/* PAMU Capabilities Registers */
#define PAMU_PC1 0x0C00
#define PAMU_PC2 0x0C04
#define PAMU_PC3 0x0C08
#define PAMU_PC4 0x0C0C
/* PAMU Control Register */
#define PAMU_PC 0x0C10
/* PAMU control defs */
#define PAMU_CONTROL 0x0C10
#define PAMU_PC_PGC 0x80000000 /* PAMU gate closed bit */
#define PAMU_PC_PE 0x40000000 /* PAMU enable bit */
#define PAMU_PC_SPCC 0x00000010 /* sPAACE cache enable */
#define PAMU_PC_PPCC 0x00000001 /* pPAACE cache enable */
#define PAMU_PC_OCE 0x00001000 /* OMT cache enable */
#define PAMU_PFA1 0x0C14
#define PAMU_PFA2 0x0C18
#define PAMU_PC2_MLIODN(X) ((X) >> 16)
#define PAMU_PC3_MWCE(X) (((X) >> 21) & 0xf)
/* PAMU Interrupt control and Status Register */
#define PAMU_PICS 0x0C1C
#define PAMU_ACCESS_VIOLATION_STAT 0x8
#define PAMU_ACCESS_VIOLATION_ENABLE 0x4
/* PAMU Debug Registers */
#define PAMU_PD1 0x0F00
#define PAMU_PD2 0x0F04
#define PAMU_PD3 0x0F08
#define PAMU_PD4 0x0F0C
#define PAACE_AP_PERMS_DENIED 0x0
#define PAACE_AP_PERMS_QUERY 0x1
#define PAACE_AP_PERMS_UPDATE 0x2
#define PAACE_AP_PERMS_ALL 0x3
#define PAACE_DD_TO_HOST 0x0
#define PAACE_DD_TO_IO 0x1
#define PAACE_PT_PRIMARY 0x0
#define PAACE_PT_SECONDARY 0x1
#define PAACE_V_INVALID 0x0
#define PAACE_V_VALID 0x1
#define PAACE_MW_SUBWINDOWS 0x1
#define PAACE_WSE_4K 0xB
#define PAACE_WSE_8K 0xC
#define PAACE_WSE_16K 0xD
#define PAACE_WSE_32K 0xE
#define PAACE_WSE_64K 0xF
#define PAACE_WSE_128K 0x10
#define PAACE_WSE_256K 0x11
#define PAACE_WSE_512K 0x12
#define PAACE_WSE_1M 0x13
#define PAACE_WSE_2M 0x14
#define PAACE_WSE_4M 0x15
#define PAACE_WSE_8M 0x16
#define PAACE_WSE_16M 0x17
#define PAACE_WSE_32M 0x18
#define PAACE_WSE_64M 0x19
#define PAACE_WSE_128M 0x1A
#define PAACE_WSE_256M 0x1B
#define PAACE_WSE_512M 0x1C
#define PAACE_WSE_1G 0x1D
#define PAACE_WSE_2G 0x1E
#define PAACE_WSE_4G 0x1F
#define PAACE_DID_PCI_EXPRESS_1 0x00
#define PAACE_DID_PCI_EXPRESS_2 0x01
#define PAACE_DID_PCI_EXPRESS_3 0x02
#define PAACE_DID_PCI_EXPRESS_4 0x03
#define PAACE_DID_LOCAL_BUS 0x04
#define PAACE_DID_SRIO 0x0C
#define PAACE_DID_MEM_1 0x10
#define PAACE_DID_MEM_2 0x11
#define PAACE_DID_MEM_3 0x12
#define PAACE_DID_MEM_4 0x13
#define PAACE_DID_MEM_1_2 0x14
#define PAACE_DID_MEM_3_4 0x15
#define PAACE_DID_MEM_1_4 0x16
#define PAACE_DID_BM_SW_PORTAL 0x18
#define PAACE_DID_PAMU 0x1C
#define PAACE_DID_CAAM 0x21
#define PAACE_DID_QM_SW_PORTAL 0x3C
#define PAACE_DID_CORE0_INST 0x80
#define PAACE_DID_CORE0_DATA 0x81
#define PAACE_DID_CORE1_INST 0x82
#define PAACE_DID_CORE1_DATA 0x83
#define PAACE_DID_CORE2_INST 0x84
#define PAACE_DID_CORE2_DATA 0x85
#define PAACE_DID_CORE3_INST 0x86
#define PAACE_DID_CORE3_DATA 0x87
#define PAACE_DID_CORE4_INST 0x88
#define PAACE_DID_CORE4_DATA 0x89
#define PAACE_DID_CORE5_INST 0x8A
#define PAACE_DID_CORE5_DATA 0x8B
#define PAACE_DID_CORE6_INST 0x8C
#define PAACE_DID_CORE6_DATA 0x8D
#define PAACE_DID_CORE7_INST 0x8E
#define PAACE_DID_CORE7_DATA 0x8F
#define PAACE_DID_BROADCAST 0xFF
#define PAACE_ATM_NO_XLATE 0x00
#define PAACE_ATM_WINDOW_XLATE 0x01
#define PAACE_ATM_PAGE_XLATE 0x02
#define PAACE_ATM_WIN_PG_XLATE \
(PAACE_ATM_WINDOW_XLATE | PAACE_ATM_PAGE_XLATE)
#define PAACE_OTM_NO_XLATE 0x00
#define PAACE_OTM_IMMEDIATE 0x01
#define PAACE_OTM_INDEXED 0x02
#define PAACE_OTM_RESERVED 0x03
#define PAACE_M_COHERENCE_REQ 0x01
#define PAACE_PID_0 0x0
#define PAACE_PID_1 0x1
#define PAACE_PID_2 0x2
#define PAACE_PID_3 0x3
#define PAACE_PID_4 0x4
#define PAACE_PID_5 0x5
#define PAACE_PID_6 0x6
#define PAACE_PID_7 0x7
#define PAACE_TCEF_FORMAT0_8B 0x00
#define PAACE_TCEF_FORMAT1_RSVD 0x01
/*
* Hard coded value for the PAACT size to accomodate
* maximum LIODN value generated by u-boot.
*/
#define PAACE_NUMBER_ENTRIES 0x500
/* Hard coded value for the SPAACT size */
#define SPAACE_NUMBER_ENTRIES 0x800
#define OME_NUMBER_ENTRIES 16
/* PAACE Bit Field Defines */
#define PPAACE_AF_WBAL 0xfffff000
#define PPAACE_AF_WBAL_SHIFT 12
#define PPAACE_AF_WSE 0x00000fc0
#define PPAACE_AF_WSE_SHIFT 6
#define PPAACE_AF_MW 0x00000020
#define PPAACE_AF_MW_SHIFT 5
#define SPAACE_AF_LIODN 0xffff0000
#define SPAACE_AF_LIODN_SHIFT 16
#define PAACE_AF_AP 0x00000018
#define PAACE_AF_AP_SHIFT 3
#define PAACE_AF_DD 0x00000004
#define PAACE_AF_DD_SHIFT 2
#define PAACE_AF_PT 0x00000002
#define PAACE_AF_PT_SHIFT 1
#define PAACE_AF_V 0x00000001
#define PAACE_AF_V_SHIFT 0
#define PAACE_DA_HOST_CR 0x80
#define PAACE_DA_HOST_CR_SHIFT 7
#define PAACE_IA_CID 0x00FF0000
#define PAACE_IA_CID_SHIFT 16
#define PAACE_IA_WCE 0x000000F0
#define PAACE_IA_WCE_SHIFT 4
#define PAACE_IA_ATM 0x0000000C
#define PAACE_IA_ATM_SHIFT 2
#define PAACE_IA_OTM 0x00000003
#define PAACE_IA_OTM_SHIFT 0
#define PAACE_WIN_TWBAL 0xfffff000
#define PAACE_WIN_TWBAL_SHIFT 12
#define PAACE_WIN_SWSE 0x00000fc0
#define PAACE_WIN_SWSE_SHIFT 6
/* PAMU Data Structures */
/* primary / secondary paact structure */
struct paace {
/* PAACE Offset 0x00 */
u32 wbah; /* only valid for Primary PAACE */
u32 addr_bitfields; /* See P/S PAACE_AF_* */
/* PAACE Offset 0x08 */
/* Interpretation of first 32 bits dependent on DD above */
union {
struct {
/* Destination ID, see PAACE_DID_* defines */
u8 did;
/* Partition ID */
u8 pid;
/* Snoop ID */
u8 snpid;
/* coherency_required : 1 reserved : 7 */
u8 coherency_required; /* See PAACE_DA_* */
} to_host;
struct {
/* Destination ID, see PAACE_DID_* defines */
u8 did;
u8 reserved1;
u16 reserved2;
} to_io;
} domain_attr;
/* Implementation attributes + window count + address & operation translation modes */
u32 impl_attr; /* See PAACE_IA_* */
/* PAACE Offset 0x10 */
/* Translated window base address */
u32 twbah;
u32 win_bitfields; /* See PAACE_WIN_* */
/* PAACE Offset 0x18 */
/* first secondary paace entry */
u32 fspi; /* only valid for Primary PAACE */
union {
struct {
u8 ioea;
u8 moea;
u8 ioeb;
u8 moeb;
} immed_ot;
struct {
u16 reserved;
u16 omi;
} index_ot;
} op_encode;
/* PAACE Offsets 0x20-0x38 */
u32 reserved[8]; /* not currently implemented */
};
/* OME : Operation mapping entry
* MOE : Mapped Operation Encodings
* The operation mapping table is table containing operation mapping entries (OME).
* The index of a particular OME is programmed in the PAACE entry for translation
* in bound I/O operations corresponding to an LIODN. The OMT is used for translation
* specifically in case of the indexed translation mode. Each OME contains a 128
* byte mapped operation encoding (MOE), where each byte represents an MOE.
*/
#define NUM_MOE 128
struct ome {
u8 moe[NUM_MOE];
} __attribute__((packed));
#define PAACT_SIZE (sizeof(struct paace) * PAACE_NUMBER_ENTRIES)
#define SPAACT_SIZE (sizeof(struct paace) * SPAACE_NUMBER_ENTRIES)
#define OMT_SIZE (sizeof(struct ome) * OME_NUMBER_ENTRIES)
#define PAMU_PAGE_SHIFT 12
#define PAMU_PAGE_SIZE 4096ULL
#define IOE_READ 0x00
#define IOE_READ_IDX 0x00
#define IOE_WRITE 0x81
#define IOE_WRITE_IDX 0x01
#define IOE_EREAD0 0x82 /* Enhanced read type 0 */
#define IOE_EREAD0_IDX 0x02 /* Enhanced read type 0 */
#define IOE_EWRITE0 0x83 /* Enhanced write type 0 */
#define IOE_EWRITE0_IDX 0x03 /* Enhanced write type 0 */
#define IOE_DIRECT0 0x84 /* Directive type 0 */
#define IOE_DIRECT0_IDX 0x04 /* Directive type 0 */
#define IOE_EREAD1 0x85 /* Enhanced read type 1 */
#define IOE_EREAD1_IDX 0x05 /* Enhanced read type 1 */
#define IOE_EWRITE1 0x86 /* Enhanced write type 1 */
#define IOE_EWRITE1_IDX 0x06 /* Enhanced write type 1 */
#define IOE_DIRECT1 0x87 /* Directive type 1 */
#define IOE_DIRECT1_IDX 0x07 /* Directive type 1 */
#define IOE_RAC 0x8c /* Read with Atomic clear */
#define IOE_RAC_IDX 0x0c /* Read with Atomic clear */
#define IOE_RAS 0x8d /* Read with Atomic set */
#define IOE_RAS_IDX 0x0d /* Read with Atomic set */
#define IOE_RAD 0x8e /* Read with Atomic decrement */
#define IOE_RAD_IDX 0x0e /* Read with Atomic decrement */
#define IOE_RAI 0x8f /* Read with Atomic increment */
#define IOE_RAI_IDX 0x0f /* Read with Atomic increment */
#define EOE_READ 0x00
#define EOE_WRITE 0x01
#define EOE_RAC 0x0c /* Read with Atomic clear */
#define EOE_RAS 0x0d /* Read with Atomic set */
#define EOE_RAD 0x0e /* Read with Atomic decrement */
#define EOE_RAI 0x0f /* Read with Atomic increment */
#define EOE_LDEC 0x10 /* Load external cache */
#define EOE_LDECL 0x11 /* Load external cache with stash lock */
#define EOE_LDECPE 0x12 /* Load external cache with preferred exclusive */
#define EOE_LDECPEL 0x13 /* Load external cache with preferred exclusive and lock */
#define EOE_LDECFE 0x14 /* Load external cache with forced exclusive */
#define EOE_LDECFEL 0x15 /* Load external cache with forced exclusive and lock */
#define EOE_RSA 0x16 /* Read with stash allocate */
#define EOE_RSAU 0x17 /* Read with stash allocate and unlock */
#define EOE_READI 0x18 /* Read with invalidate */
#define EOE_RWNITC 0x19 /* Read with no intention to cache */
#define EOE_WCI 0x1a /* Write cache inhibited */
#define EOE_WWSA 0x1b /* Write with stash allocate */
#define EOE_WWSAL 0x1c /* Write with stash allocate and lock */
#define EOE_WWSAO 0x1d /* Write with stash allocate only */
#define EOE_WWSAOL 0x1e /* Write with stash allocate only and lock */
#define EOE_VALID 0x80
/* Function prototypes */
int pamu_domain_init(void);
int pamu_enable_liodn(int liodn);
int pamu_disable_liodn(int liodn);
void pamu_free_subwins(int liodn);
int pamu_config_ppaace(int liodn, phys_addr_t win_addr, phys_addr_t win_size,
u32 omi, unsigned long rpn, u32 snoopid, uint32_t stashid,
u32 subwin_cnt, int prot);
int pamu_config_spaace(int liodn, u32 subwin_cnt, u32 subwin_addr,
phys_addr_t subwin_size, u32 omi, unsigned long rpn,
uint32_t snoopid, u32 stashid, int enable, int prot);
u32 get_stash_id(u32 stash_dest_hint, u32 vcpu);
void get_ome_index(u32 *omi_index, struct device *dev);
int pamu_update_paace_stash(int liodn, u32 subwin, u32 value);
int pamu_disable_spaace(int liodn, u32 subwin);
u32 pamu_get_max_subwin_cnt(void);
#endif /* __FSL_PAMU_H */

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,85 @@
/*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License, version 2, as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* Copyright (C) 2013 Freescale Semiconductor, Inc.
*
*/
#ifndef __FSL_PAMU_DOMAIN_H
#define __FSL_PAMU_DOMAIN_H
#include "fsl_pamu.h"
struct dma_window {
phys_addr_t paddr;
u64 size;
int valid;
int prot;
};
struct fsl_dma_domain {
/*
* Indicates the geometry size for the domain.
* This would be set when the geometry is
* configured for the domain.
*/
dma_addr_t geom_size;
/*
* Number of windows assocaited with this domain.
* During domain initialization, it is set to the
* the maximum number of subwindows allowed for a LIODN.
* Minimum value for this is 1 indicating a single PAMU
* window, without any sub windows. Value can be set/
* queried by set_attr/get_attr API for DOMAIN_ATTR_WINDOWS.
* Value can only be set once the geometry has been configured.
*/
u32 win_cnt;
/*
* win_arr contains information of the configured
* windows for a domain. This is allocated only
* when the number of windows for the domain are
* set.
*/
struct dma_window *win_arr;
/* list of devices associated with the domain */
struct list_head devices;
/* dma_domain states:
* mapped - A particular mapping has been created
* within the configured geometry.
* enabled - DMA has been enabled for the given
* domain. This translates to setting of the
* valid bit for the primary PAACE in the PAMU
* PAACT table. Domain geometry should be set and
* it must have a valid mapping before DMA can be
* enabled for it.
*
*/
int mapped;
int enabled;
/* stash_id obtained from the stash attribute details */
u32 stash_id;
struct pamu_stash_attribute dma_stash;
u32 snoop_id;
struct iommu_domain *iommu_domain;
spinlock_t domain_lock;
};
/* domain-device relationship */
struct device_domain_info {
struct list_head link; /* link to domain siblings */
struct device *dev;
u32 liodn;
struct fsl_dma_domain *domain; /* pointer to domain */
};
#endif /* __FSL_PAMU_DOMAIN_H */

View file

@ -890,56 +890,54 @@ static int dma_pte_clear_range(struct dmar_domain *domain,
return order;
}
static void dma_pte_free_level(struct dmar_domain *domain, int level,
struct dma_pte *pte, unsigned long pfn,
unsigned long start_pfn, unsigned long last_pfn)
{
pfn = max(start_pfn, pfn);
pte = &pte[pfn_level_offset(pfn, level)];
do {
unsigned long level_pfn;
struct dma_pte *level_pte;
if (!dma_pte_present(pte) || dma_pte_superpage(pte))
goto next;
level_pfn = pfn & level_mask(level - 1);
level_pte = phys_to_virt(dma_pte_addr(pte));
if (level > 2)
dma_pte_free_level(domain, level - 1, level_pte,
level_pfn, start_pfn, last_pfn);
/* If range covers entire pagetable, free it */
if (!(start_pfn > level_pfn ||
last_pfn < level_pfn + level_size(level))) {
dma_clear_pte(pte);
domain_flush_cache(domain, pte, sizeof(*pte));
free_pgtable_page(level_pte);
}
next:
pfn += level_size(level);
} while (!first_pte_in_page(++pte) && pfn <= last_pfn);
}
/* free page table pages. last level pte should already be cleared */
static void dma_pte_free_pagetable(struct dmar_domain *domain,
unsigned long start_pfn,
unsigned long last_pfn)
{
int addr_width = agaw_to_width(domain->agaw) - VTD_PAGE_SHIFT;
struct dma_pte *first_pte, *pte;
int total = agaw_to_level(domain->agaw);
int level;
unsigned long tmp;
int large_page = 2;
BUG_ON(addr_width < BITS_PER_LONG && start_pfn >> addr_width);
BUG_ON(addr_width < BITS_PER_LONG && last_pfn >> addr_width);
BUG_ON(start_pfn > last_pfn);
/* We don't need lock here; nobody else touches the iova range */
level = 2;
while (level <= total) {
tmp = align_to_level(start_pfn, level);
dma_pte_free_level(domain, agaw_to_level(domain->agaw),
domain->pgd, 0, start_pfn, last_pfn);
/* If we can't even clear one PTE at this level, we're done */
if (tmp + level_size(level) - 1 > last_pfn)
return;
do {
large_page = level;
first_pte = pte = dma_pfn_level_pte(domain, tmp, level, &large_page);
if (large_page > level)
level = large_page + 1;
if (!pte) {
tmp = align_to_level(tmp + 1, level + 1);
continue;
}
do {
if (dma_pte_present(pte)) {
free_pgtable_page(phys_to_virt(dma_pte_addr(pte)));
dma_clear_pte(pte);
}
pte++;
tmp += level_size(level);
} while (!first_pte_in_page(pte) &&
tmp + level_size(level) - 1 <= last_pfn);
domain_flush_cache(domain, first_pte,
(void *)pte - (void *)first_pte);
} while (tmp && tmp + level_size(level) - 1 <= last_pfn);
level++;
}
/* free pgd */
if (start_pfn == 0 && last_pfn == DOMAIN_MAX_PFN(domain->gaw)) {
free_pgtable_page(domain->pgd);

View file

@ -58,10 +58,26 @@ struct iommu_domain {
#define IOMMU_CAP_CACHE_COHERENCY 0x1
#define IOMMU_CAP_INTR_REMAP 0x2 /* isolates device intrs */
/*
* Following constraints are specifc to FSL_PAMUV1:
* -aperture must be power of 2, and naturally aligned
* -number of windows must be power of 2, and address space size
* of each window is determined by aperture size / # of windows
* -the actual size of the mapped region of a window must be power
* of 2 starting with 4KB and physical address must be naturally
* aligned.
* DOMAIN_ATTR_FSL_PAMUV1 corresponds to the above mentioned contraints.
* The caller can invoke iommu_domain_get_attr to check if the underlying
* iommu implementation supports these constraints.
*/
enum iommu_attr {
DOMAIN_ATTR_GEOMETRY,
DOMAIN_ATTR_PAGING,
DOMAIN_ATTR_WINDOWS,
DOMAIN_ATTR_FSL_PAMU_STASH,
DOMAIN_ATTR_FSL_PAMU_ENABLE,
DOMAIN_ATTR_FSL_PAMUV1,
DOMAIN_ATTR_MAX,
};