Merge branch 'for-joerg/arm-smmu/updates' of git://git.kernel.org/pub/scm/linux/kernel/git/will/linux into arm/smmu

This commit is contained in:
Joerg Roedel 2014-03-04 14:54:27 +01:00
Родитель 0414855fdc 34fb4b37b7
Коммит dc03753b98
2 изменённых файлов: 77 добавлений и 34 удалений

Просмотреть файл

@ -48,6 +48,12 @@ conditions.
from the mmu-masters towards memory) node for this from the mmu-masters towards memory) node for this
SMMU. SMMU.
- calxeda,smmu-secure-config-access : Enable proper handling of buggy
implementations that always use secure access to
SMMU configuration registers. In this case non-secure
aliases of secure registers have to be used during
SMMU configuration.
Example: Example:
smmu { smmu {

Просмотреть файл

@ -48,7 +48,7 @@
#include <asm/pgalloc.h> #include <asm/pgalloc.h>
/* Maximum number of stream IDs assigned to a single device */ /* Maximum number of stream IDs assigned to a single device */
#define MAX_MASTER_STREAMIDS 8 #define MAX_MASTER_STREAMIDS MAX_PHANDLE_ARGS
/* Maximum number of context banks per SMMU */ /* Maximum number of context banks per SMMU */
#define ARM_SMMU_MAX_CBS 128 #define ARM_SMMU_MAX_CBS 128
@ -60,6 +60,16 @@
#define ARM_SMMU_GR0(smmu) ((smmu)->base) #define ARM_SMMU_GR0(smmu) ((smmu)->base)
#define ARM_SMMU_GR1(smmu) ((smmu)->base + (smmu)->pagesize) #define ARM_SMMU_GR1(smmu) ((smmu)->base + (smmu)->pagesize)
/*
* SMMU global address space with conditional offset to access secure
* aliases of non-secure registers (e.g. nsCR0: 0x400, nsGFSR: 0x448,
* nsGFSYNR0: 0x450)
*/
#define ARM_SMMU_GR0_NS(smmu) \
((smmu)->base + \
((smmu->options & ARM_SMMU_OPT_SECURE_CFG_ACCESS) \
? 0x400 : 0))
/* Page table bits */ /* Page table bits */
#define ARM_SMMU_PTE_XN (((pteval_t)3) << 53) #define ARM_SMMU_PTE_XN (((pteval_t)3) << 53)
#define ARM_SMMU_PTE_CONT (((pteval_t)1) << 52) #define ARM_SMMU_PTE_CONT (((pteval_t)1) << 52)
@ -351,6 +361,9 @@ struct arm_smmu_device {
#define ARM_SMMU_FEAT_TRANS_S2 (1 << 3) #define ARM_SMMU_FEAT_TRANS_S2 (1 << 3)
#define ARM_SMMU_FEAT_TRANS_NESTED (1 << 4) #define ARM_SMMU_FEAT_TRANS_NESTED (1 << 4)
u32 features; u32 features;
#define ARM_SMMU_OPT_SECURE_CFG_ACCESS (1 << 0)
u32 options;
int version; int version;
u32 num_context_banks; u32 num_context_banks;
@ -401,6 +414,29 @@ struct arm_smmu_domain {
static DEFINE_SPINLOCK(arm_smmu_devices_lock); static DEFINE_SPINLOCK(arm_smmu_devices_lock);
static LIST_HEAD(arm_smmu_devices); static LIST_HEAD(arm_smmu_devices);
struct arm_smmu_option_prop {
u32 opt;
const char *prop;
};
static struct arm_smmu_option_prop arm_smmu_options [] = {
{ ARM_SMMU_OPT_SECURE_CFG_ACCESS, "calxeda,smmu-secure-config-access" },
{ 0, NULL},
};
static void parse_driver_options(struct arm_smmu_device *smmu)
{
int i = 0;
do {
if (of_property_read_bool(smmu->dev->of_node,
arm_smmu_options[i].prop)) {
smmu->options |= arm_smmu_options[i].opt;
dev_notice(smmu->dev, "option %s\n",
arm_smmu_options[i].prop);
}
} while (arm_smmu_options[++i].opt);
}
static struct arm_smmu_master *find_smmu_master(struct arm_smmu_device *smmu, static struct arm_smmu_master *find_smmu_master(struct arm_smmu_device *smmu,
struct device_node *dev_node) struct device_node *dev_node)
{ {
@ -614,16 +650,16 @@ static irqreturn_t arm_smmu_global_fault(int irq, void *dev)
{ {
u32 gfsr, gfsynr0, gfsynr1, gfsynr2; u32 gfsr, gfsynr0, gfsynr1, gfsynr2;
struct arm_smmu_device *smmu = dev; struct arm_smmu_device *smmu = dev;
void __iomem *gr0_base = ARM_SMMU_GR0(smmu); void __iomem *gr0_base = ARM_SMMU_GR0_NS(smmu);
gfsr = readl_relaxed(gr0_base + ARM_SMMU_GR0_sGFSR); gfsr = readl_relaxed(gr0_base + ARM_SMMU_GR0_sGFSR);
if (!gfsr)
return IRQ_NONE;
gfsynr0 = readl_relaxed(gr0_base + ARM_SMMU_GR0_sGFSYNR0); gfsynr0 = readl_relaxed(gr0_base + ARM_SMMU_GR0_sGFSYNR0);
gfsynr1 = readl_relaxed(gr0_base + ARM_SMMU_GR0_sGFSYNR1); gfsynr1 = readl_relaxed(gr0_base + ARM_SMMU_GR0_sGFSYNR1);
gfsynr2 = readl_relaxed(gr0_base + ARM_SMMU_GR0_sGFSYNR2); gfsynr2 = readl_relaxed(gr0_base + ARM_SMMU_GR0_sGFSYNR2);
if (!gfsr)
return IRQ_NONE;
dev_err_ratelimited(smmu->dev, dev_err_ratelimited(smmu->dev,
"Unexpected global fault, this could be serious\n"); "Unexpected global fault, this could be serious\n");
dev_err_ratelimited(smmu->dev, dev_err_ratelimited(smmu->dev,
@ -642,7 +678,7 @@ static void arm_smmu_flush_pgtable(struct arm_smmu_device *smmu, void *addr,
/* Ensure new page tables are visible to the hardware walker */ /* Ensure new page tables are visible to the hardware walker */
if (smmu->features & ARM_SMMU_FEAT_COHERENT_WALK) { if (smmu->features & ARM_SMMU_FEAT_COHERENT_WALK) {
dsb(); dsb(ishst);
} else { } else {
/* /*
* If the SMMU can't walk tables in the CPU caches, treat them * If the SMMU can't walk tables in the CPU caches, treat them
@ -990,9 +1026,8 @@ static void arm_smmu_free_pgtables(struct arm_smmu_domain *smmu_domain)
/* /*
* Recursively free the page tables for this domain. We don't * Recursively free the page tables for this domain. We don't
* care about speculative TLB filling, because the TLB will be * care about speculative TLB filling because the tables should
* nuked next time this context bank is re-allocated and no devices * not be active in any context bank at this point (SCTLR.M is 0).
* currently map to these tables.
*/ */
pgd = pgd_base; pgd = pgd_base;
for (i = 0; i < PTRS_PER_PGD; ++i) { for (i = 0; i < PTRS_PER_PGD; ++i) {
@ -1218,7 +1253,7 @@ static bool arm_smmu_pte_is_contiguous_range(unsigned long addr,
static int arm_smmu_alloc_init_pte(struct arm_smmu_device *smmu, pmd_t *pmd, static int arm_smmu_alloc_init_pte(struct arm_smmu_device *smmu, pmd_t *pmd,
unsigned long addr, unsigned long end, unsigned long addr, unsigned long end,
unsigned long pfn, int flags, int stage) unsigned long pfn, int prot, int stage)
{ {
pte_t *pte, *start; pte_t *pte, *start;
pteval_t pteval = ARM_SMMU_PTE_PAGE | ARM_SMMU_PTE_AF | ARM_SMMU_PTE_XN; pteval_t pteval = ARM_SMMU_PTE_PAGE | ARM_SMMU_PTE_AF | ARM_SMMU_PTE_XN;
@ -1240,28 +1275,28 @@ static int arm_smmu_alloc_init_pte(struct arm_smmu_device *smmu, pmd_t *pmd,
if (stage == 1) { if (stage == 1) {
pteval |= ARM_SMMU_PTE_AP_UNPRIV | ARM_SMMU_PTE_nG; pteval |= ARM_SMMU_PTE_AP_UNPRIV | ARM_SMMU_PTE_nG;
if (!(flags & IOMMU_WRITE) && (flags & IOMMU_READ)) if (!(prot & IOMMU_WRITE) && (prot & IOMMU_READ))
pteval |= ARM_SMMU_PTE_AP_RDONLY; pteval |= ARM_SMMU_PTE_AP_RDONLY;
if (flags & IOMMU_CACHE) if (prot & IOMMU_CACHE)
pteval |= (MAIR_ATTR_IDX_CACHE << pteval |= (MAIR_ATTR_IDX_CACHE <<
ARM_SMMU_PTE_ATTRINDX_SHIFT); ARM_SMMU_PTE_ATTRINDX_SHIFT);
} else { } else {
pteval |= ARM_SMMU_PTE_HAP_FAULT; pteval |= ARM_SMMU_PTE_HAP_FAULT;
if (flags & IOMMU_READ) if (prot & IOMMU_READ)
pteval |= ARM_SMMU_PTE_HAP_READ; pteval |= ARM_SMMU_PTE_HAP_READ;
if (flags & IOMMU_WRITE) if (prot & IOMMU_WRITE)
pteval |= ARM_SMMU_PTE_HAP_WRITE; pteval |= ARM_SMMU_PTE_HAP_WRITE;
if (flags & IOMMU_CACHE) if (prot & IOMMU_CACHE)
pteval |= ARM_SMMU_PTE_MEMATTR_OIWB; pteval |= ARM_SMMU_PTE_MEMATTR_OIWB;
else else
pteval |= ARM_SMMU_PTE_MEMATTR_NC; pteval |= ARM_SMMU_PTE_MEMATTR_NC;
} }
/* If no access, create a faulting entry to avoid TLB fills */ /* If no access, create a faulting entry to avoid TLB fills */
if (flags & IOMMU_EXEC) if (prot & IOMMU_EXEC)
pteval &= ~ARM_SMMU_PTE_XN; pteval &= ~ARM_SMMU_PTE_XN;
else if (!(flags & (IOMMU_READ | IOMMU_WRITE))) else if (!(prot & (IOMMU_READ | IOMMU_WRITE)))
pteval &= ~ARM_SMMU_PTE_PAGE; pteval &= ~ARM_SMMU_PTE_PAGE;
pteval |= ARM_SMMU_PTE_SH_IS; pteval |= ARM_SMMU_PTE_SH_IS;
@ -1323,7 +1358,7 @@ static int arm_smmu_alloc_init_pte(struct arm_smmu_device *smmu, pmd_t *pmd,
static int arm_smmu_alloc_init_pmd(struct arm_smmu_device *smmu, pud_t *pud, static int arm_smmu_alloc_init_pmd(struct arm_smmu_device *smmu, pud_t *pud,
unsigned long addr, unsigned long end, unsigned long addr, unsigned long end,
phys_addr_t phys, int flags, int stage) phys_addr_t phys, int prot, int stage)
{ {
int ret; int ret;
pmd_t *pmd; pmd_t *pmd;
@ -1347,7 +1382,7 @@ static int arm_smmu_alloc_init_pmd(struct arm_smmu_device *smmu, pud_t *pud,
do { do {
next = pmd_addr_end(addr, end); next = pmd_addr_end(addr, end);
ret = arm_smmu_alloc_init_pte(smmu, pmd, addr, end, pfn, ret = arm_smmu_alloc_init_pte(smmu, pmd, addr, end, pfn,
flags, stage); prot, stage);
phys += next - addr; phys += next - addr;
} while (pmd++, addr = next, addr < end); } while (pmd++, addr = next, addr < end);
@ -1356,7 +1391,7 @@ static int arm_smmu_alloc_init_pmd(struct arm_smmu_device *smmu, pud_t *pud,
static int arm_smmu_alloc_init_pud(struct arm_smmu_device *smmu, pgd_t *pgd, static int arm_smmu_alloc_init_pud(struct arm_smmu_device *smmu, pgd_t *pgd,
unsigned long addr, unsigned long end, unsigned long addr, unsigned long end,
phys_addr_t phys, int flags, int stage) phys_addr_t phys, int prot, int stage)
{ {
int ret = 0; int ret = 0;
pud_t *pud; pud_t *pud;
@ -1380,7 +1415,7 @@ static int arm_smmu_alloc_init_pud(struct arm_smmu_device *smmu, pgd_t *pgd,
do { do {
next = pud_addr_end(addr, end); next = pud_addr_end(addr, end);
ret = arm_smmu_alloc_init_pmd(smmu, pud, addr, next, phys, ret = arm_smmu_alloc_init_pmd(smmu, pud, addr, next, phys,
flags, stage); prot, stage);
phys += next - addr; phys += next - addr;
} while (pud++, addr = next, addr < end); } while (pud++, addr = next, addr < end);
@ -1389,7 +1424,7 @@ static int arm_smmu_alloc_init_pud(struct arm_smmu_device *smmu, pgd_t *pgd,
static int arm_smmu_handle_mapping(struct arm_smmu_domain *smmu_domain, static int arm_smmu_handle_mapping(struct arm_smmu_domain *smmu_domain,
unsigned long iova, phys_addr_t paddr, unsigned long iova, phys_addr_t paddr,
size_t size, int flags) size_t size, int prot)
{ {
int ret, stage; int ret, stage;
unsigned long end; unsigned long end;
@ -1397,7 +1432,7 @@ static int arm_smmu_handle_mapping(struct arm_smmu_domain *smmu_domain,
struct arm_smmu_cfg *root_cfg = &smmu_domain->root_cfg; struct arm_smmu_cfg *root_cfg = &smmu_domain->root_cfg;
pgd_t *pgd = root_cfg->pgd; pgd_t *pgd = root_cfg->pgd;
struct arm_smmu_device *smmu = root_cfg->smmu; struct arm_smmu_device *smmu = root_cfg->smmu;
unsigned long irqflags; unsigned long flags;
if (root_cfg->cbar == CBAR_TYPE_S2_TRANS) { if (root_cfg->cbar == CBAR_TYPE_S2_TRANS) {
stage = 2; stage = 2;
@ -1420,14 +1455,14 @@ static int arm_smmu_handle_mapping(struct arm_smmu_domain *smmu_domain,
if (paddr & ~output_mask) if (paddr & ~output_mask)
return -ERANGE; return -ERANGE;
spin_lock_irqsave(&smmu_domain->lock, irqflags); spin_lock_irqsave(&smmu_domain->lock, flags);
pgd += pgd_index(iova); pgd += pgd_index(iova);
end = iova + size; end = iova + size;
do { do {
unsigned long next = pgd_addr_end(iova, end); unsigned long next = pgd_addr_end(iova, end);
ret = arm_smmu_alloc_init_pud(smmu, pgd, iova, next, paddr, ret = arm_smmu_alloc_init_pud(smmu, pgd, iova, next, paddr,
flags, stage); prot, stage);
if (ret) if (ret)
goto out_unlock; goto out_unlock;
@ -1436,13 +1471,13 @@ static int arm_smmu_handle_mapping(struct arm_smmu_domain *smmu_domain,
} while (pgd++, iova != end); } while (pgd++, iova != end);
out_unlock: out_unlock:
spin_unlock_irqrestore(&smmu_domain->lock, irqflags); spin_unlock_irqrestore(&smmu_domain->lock, flags);
return ret; return ret;
} }
static int arm_smmu_map(struct iommu_domain *domain, unsigned long iova, static int arm_smmu_map(struct iommu_domain *domain, unsigned long iova,
phys_addr_t paddr, size_t size, int flags) phys_addr_t paddr, size_t size, int prot)
{ {
struct arm_smmu_domain *smmu_domain = domain->priv; struct arm_smmu_domain *smmu_domain = domain->priv;
@ -1453,7 +1488,7 @@ static int arm_smmu_map(struct iommu_domain *domain, unsigned long iova,
if ((phys_addr_t)iova & ~smmu_domain->output_mask) if ((phys_addr_t)iova & ~smmu_domain->output_mask)
return -ERANGE; return -ERANGE;
return arm_smmu_handle_mapping(smmu_domain, iova, paddr, size, flags); return arm_smmu_handle_mapping(smmu_domain, iova, paddr, size, prot);
} }
static size_t arm_smmu_unmap(struct iommu_domain *domain, unsigned long iova, static size_t arm_smmu_unmap(struct iommu_domain *domain, unsigned long iova,
@ -1597,9 +1632,9 @@ static void arm_smmu_device_reset(struct arm_smmu_device *smmu)
int i = 0; int i = 0;
u32 reg; u32 reg;
/* Clear Global FSR */ /* clear global FSR */
reg = readl_relaxed(gr0_base + ARM_SMMU_GR0_sGFSR); reg = readl_relaxed(ARM_SMMU_GR0_NS(smmu) + ARM_SMMU_GR0_sGFSR);
writel(reg, gr0_base + 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 */
for (i = 0; i < smmu->num_mapping_groups; ++i) { for (i = 0; i < smmu->num_mapping_groups; ++i) {
@ -1619,7 +1654,7 @@ static void arm_smmu_device_reset(struct arm_smmu_device *smmu)
writel_relaxed(0, gr0_base + ARM_SMMU_GR0_TLBIALLH); writel_relaxed(0, gr0_base + ARM_SMMU_GR0_TLBIALLH);
writel_relaxed(0, gr0_base + ARM_SMMU_GR0_TLBIALLNSNH); writel_relaxed(0, gr0_base + ARM_SMMU_GR0_TLBIALLNSNH);
reg = readl_relaxed(gr0_base + ARM_SMMU_GR0_sCR0); reg = readl_relaxed(ARM_SMMU_GR0_NS(smmu) + ARM_SMMU_GR0_sCR0);
/* Enable fault reporting */ /* Enable fault reporting */
reg |= (sCR0_GFRE | sCR0_GFIE | sCR0_GCFGFRE | sCR0_GCFGFIE); reg |= (sCR0_GFRE | sCR0_GFIE | sCR0_GCFGFRE | sCR0_GCFGFIE);
@ -1638,7 +1673,7 @@ static void arm_smmu_device_reset(struct arm_smmu_device *smmu)
/* Push the button */ /* Push the button */
arm_smmu_tlb_sync(smmu); arm_smmu_tlb_sync(smmu);
writel_relaxed(reg, gr0_base + ARM_SMMU_GR0_sCR0); writel(reg, ARM_SMMU_GR0_NS(smmu) + ARM_SMMU_GR0_sCR0);
} }
static int arm_smmu_id_size_to_bits(int size) static int arm_smmu_id_size_to_bits(int size)
@ -1885,6 +1920,8 @@ static int arm_smmu_device_dt_probe(struct platform_device *pdev)
if (err) if (err)
goto out_put_parent; goto out_put_parent;
parse_driver_options(smmu);
if (smmu->version > 1 && if (smmu->version > 1 &&
smmu->num_context_banks != smmu->num_context_irqs) { smmu->num_context_banks != smmu->num_context_irqs) {
dev_err(dev, dev_err(dev,
@ -1969,7 +2006,7 @@ static int arm_smmu_device_remove(struct platform_device *pdev)
free_irq(smmu->irqs[i], smmu); free_irq(smmu->irqs[i], smmu);
/* Turn the thing off */ /* Turn the thing off */
writel_relaxed(sCR0_CLIENTPD, ARM_SMMU_GR0(smmu) + ARM_SMMU_GR0_sCR0); writel(sCR0_CLIENTPD,ARM_SMMU_GR0_NS(smmu) + ARM_SMMU_GR0_sCR0);
return 0; return 0;
} }