diff options
Diffstat (limited to 'drivers/iommu/tegra-smmu.c')
| -rw-r--r-- | drivers/iommu/tegra-smmu.c | 571 |
1 files changed, 416 insertions, 155 deletions
diff --git a/drivers/iommu/tegra-smmu.c b/drivers/iommu/tegra-smmu.c index 3f3d09d560e..605b5b46a90 100644 --- a/drivers/iommu/tegra-smmu.c +++ b/drivers/iommu/tegra-smmu.c @@ -1,7 +1,7 @@ /* * IOMMU API for SMMU in Tegra30 * - * Copyright (c) 2011-2012, NVIDIA CORPORATION. All rights reserved. + * Copyright (c) 2011-2013, NVIDIA CORPORATION. All rights reserved. * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU General Public License, @@ -19,6 +19,7 @@ #define pr_fmt(fmt) "%s(): " fmt, __func__ +#include <linux/err.h> #include <linux/module.h> #include <linux/platform_device.h> #include <linux/spinlock.h> @@ -30,12 +31,54 @@ #include <linux/sched.h> #include <linux/iommu.h> #include <linux/io.h> +#include <linux/of.h> +#include <linux/of_iommu.h> +#include <linux/debugfs.h> +#include <linux/seq_file.h> +#include <linux/tegra-ahb.h> #include <asm/page.h> #include <asm/cacheflush.h> -#include <mach/iomap.h> -#include <mach/smmu.h> +enum smmu_hwgrp { + HWGRP_AFI, + HWGRP_AVPC, + HWGRP_DC, + HWGRP_DCB, + HWGRP_EPP, + HWGRP_G2, + HWGRP_HC, + HWGRP_HDA, + HWGRP_ISP, + HWGRP_MPE, + HWGRP_NV, + HWGRP_NV2, + HWGRP_PPCS, + HWGRP_SATA, + HWGRP_VDE, + HWGRP_VI, + + HWGRP_COUNT, + + HWGRP_END = ~0, +}; + +#define HWG_AFI (1 << HWGRP_AFI) +#define HWG_AVPC (1 << HWGRP_AVPC) +#define HWG_DC (1 << HWGRP_DC) +#define HWG_DCB (1 << HWGRP_DCB) +#define HWG_EPP (1 << HWGRP_EPP) +#define HWG_G2 (1 << HWGRP_G2) +#define HWG_HC (1 << HWGRP_HC) +#define HWG_HDA (1 << HWGRP_HDA) +#define HWG_ISP (1 << HWGRP_ISP) +#define HWG_MPE (1 << HWGRP_MPE) +#define HWG_NV (1 << HWGRP_NV) +#define HWG_NV2 (1 << HWGRP_NV2) +#define HWG_PPCS (1 << HWGRP_PPCS) +#define HWG_SATA (1 << HWGRP_SATA) +#define HWG_VDE (1 << HWGRP_VDE) +#define HWG_VI (1 << HWGRP_VI) /* bitmap of the page sizes currently supported */ #define SMMU_IOMMU_PGSIZES (SZ_4K) @@ -44,16 +87,29 @@ #define SMMU_CONFIG_DISABLE 0 #define SMMU_CONFIG_ENABLE 1 -#define SMMU_TLB_CONFIG 0x14 -#define SMMU_TLB_CONFIG_STATS__MASK (1 << 31) -#define SMMU_TLB_CONFIG_STATS__ENABLE (1 << 31) +/* REVISIT: To support multiple MCs */ +enum { + _MC = 0, +}; + +enum { + _TLB = 0, + _PTC, +}; + +#define SMMU_CACHE_CONFIG_BASE 0x14 +#define __SMMU_CACHE_CONFIG(mc, cache) (SMMU_CACHE_CONFIG_BASE + 4 * cache) +#define SMMU_CACHE_CONFIG(cache) __SMMU_CACHE_CONFIG(_MC, cache) + +#define SMMU_CACHE_CONFIG_STATS_SHIFT 31 +#define SMMU_CACHE_CONFIG_STATS_ENABLE (1 << SMMU_CACHE_CONFIG_STATS_SHIFT) +#define SMMU_CACHE_CONFIG_STATS_TEST_SHIFT 30 +#define SMMU_CACHE_CONFIG_STATS_TEST (1 << SMMU_CACHE_CONFIG_STATS_TEST_SHIFT) + #define SMMU_TLB_CONFIG_HIT_UNDER_MISS__ENABLE (1 << 29) #define SMMU_TLB_CONFIG_ACTIVE_LINES__VALUE 0x10 #define SMMU_TLB_CONFIG_RESET_VAL 0x20000010 -#define SMMU_PTC_CONFIG 0x18 -#define SMMU_PTC_CONFIG_STATS__MASK (1 << 31) -#define SMMU_PTC_CONFIG_STATS__ENABLE (1 << 31) #define SMMU_PTC_CONFIG_CACHE__ENABLE (1 << 29) #define SMMU_PTC_CONFIG_INDEX_MAP__PATTERN 0x3f #define SMMU_PTC_CONFIG_RESET_VAL 0x2000003f @@ -83,10 +139,10 @@ #define SMMU_ASID_SECURITY 0x38 -#define SMMU_STATS_TLB_HIT_COUNT 0x1f0 -#define SMMU_STATS_TLB_MISS_COUNT 0x1f4 -#define SMMU_STATS_PTC_HIT_COUNT 0x1f8 -#define SMMU_STATS_PTC_MISS_COUNT 0x1fc +#define SMMU_STATS_CACHE_COUNT_BASE 0x1f0 + +#define SMMU_STATS_CACHE_COUNT(mc, cache, hitmiss) \ + (SMMU_STATS_CACHE_COUNT_BASE + 8 * cache + 4 * hitmiss) #define SMMU_TRANSLATION_ENABLE_0 0x228 #define SMMU_TRANSLATION_ENABLE_1 0x22c @@ -111,12 +167,6 @@ #define SMMU_PDE_NEXT_SHIFT 28 -/* AHB Arbiter Registers */ -#define AHB_XBAR_CTRL 0xe0 -#define AHB_XBAR_CTRL_SMMU_INIT_DONE_DONE 1 -#define AHB_XBAR_CTRL_SMMU_INIT_DONE_SHIFT 17 - -#define SMMU_NUM_ASIDS 4 #define SMMU_TLB_FLUSH_VA_SECTION__MASK 0xffc00000 #define SMMU_TLB_FLUSH_VA_SECTION__SHIFT 12 /* right shift */ #define SMMU_TLB_FLUSH_VA_GROUP__MASK 0xffffc000 @@ -136,6 +186,7 @@ #define SMMU_PAGE_SHIFT 12 #define SMMU_PAGE_SIZE (1 << SMMU_PAGE_SHIFT) +#define SMMU_PAGE_MASK ((1 << SMMU_PAGE_SHIFT) - 1) #define SMMU_PDIR_COUNT 1024 #define SMMU_PDIR_SIZE (sizeof(unsigned long) * SMMU_PDIR_COUNT) @@ -148,7 +199,7 @@ #define SMMU_ADDR_TO_PFN(addr) ((addr) >> 12) #define SMMU_ADDR_TO_PDN(addr) ((addr) >> 22) -#define SMMU_PDN_TO_ADDR(addr) ((pdn) << 22) +#define SMMU_PDN_TO_ADDR(pdn) ((pdn) << 22) #define _READABLE (1 << SMMU_PTB_DATA_ASID_READABLE_SHIFT) #define _WRITABLE (1 << SMMU_PTB_DATA_ASID_WRITABLE_SHIFT) @@ -177,6 +228,8 @@ #define SMMU_ASID_DISABLE 0 #define SMMU_ASID_ASID(n) ((n) & ~SMMU_ASID_ENABLE(0)) +#define NUM_SMMU_REG_BANKS 3 + #define smmu_client_enable_hwgrp(c, m) smmu_client_set_hwgrp(c, m, 1) #define smmu_client_disable_hwgrp(c) smmu_client_set_hwgrp(c, 0, 0) #define __smmu_client_enable_hwgrp(c, m) __smmu_client_set_hwgrp(c, m, 1) @@ -231,18 +284,26 @@ struct smmu_as { spinlock_t client_lock; /* for client list */ }; +struct smmu_debugfs_info { + struct smmu_device *smmu; + int mc; + int cache; +}; + /* * Per SMMU device - IOMMU device */ struct smmu_device { - void __iomem *regs, *regs_ahbarb; + void __iomem *regbase; /* register offset base */ + void __iomem **regs; /* register block start address array */ + void __iomem **rege; /* register block end address array */ + int nregs; /* number of register blocks */ + unsigned long iovmm_base; /* remappable base address */ unsigned long page_count; /* total remappable size */ spinlock_t lock; char *name; struct device *dev; - int num_as; - struct smmu_as *as; /* Run-time allocated array */ struct page *avp_vector_page; /* dummy page shared by all AS's */ /* @@ -252,29 +313,52 @@ struct smmu_device { unsigned long translation_enable_1; unsigned long translation_enable_2; unsigned long asid_security; + + struct dentry *debugfs_root; + struct smmu_debugfs_info *debugfs_info; + + struct device_node *ahb; + + int num_as; + struct smmu_as as[0]; /* Run-time allocated array */ }; static struct smmu_device *smmu_handle; /* unique for a system */ /* - * SMMU/AHB register accessors + * SMMU register accessors */ -static inline u32 smmu_read(struct smmu_device *smmu, size_t offs) -{ - return readl(smmu->regs + offs); -} -static inline void smmu_write(struct smmu_device *smmu, u32 val, size_t offs) +static bool inline smmu_valid_reg(struct smmu_device *smmu, + void __iomem *addr) { - writel(val, smmu->regs + offs); + int i; + + for (i = 0; i < smmu->nregs; i++) { + if (addr < smmu->regs[i]) + break; + if (addr <= smmu->rege[i]) + return true; + } + + return false; } -static inline u32 ahb_read(struct smmu_device *smmu, size_t offs) +static inline u32 smmu_read(struct smmu_device *smmu, size_t offs) { - return readl(smmu->regs_ahbarb + offs); + void __iomem *addr = smmu->regbase + offs; + + BUG_ON(!smmu_valid_reg(smmu, addr)); + + return readl(addr); } -static inline void ahb_write(struct smmu_device *smmu, u32 val, size_t offs) + +static inline void smmu_write(struct smmu_device *smmu, u32 val, size_t offs) { - writel(val, smmu->regs_ahbarb + offs); + void __iomem *addr = smmu->regbase + offs; + + BUG_ON(!smmu_valid_reg(smmu, addr)); + + writel(val, addr); } #define VA_PAGE_TO_PA(va, page) \ @@ -370,7 +454,7 @@ static void smmu_flush_regs(struct smmu_device *smmu, int enable) FLUSH_SMMU_REGS(smmu); } -static void smmu_setup_regs(struct smmu_device *smmu) +static int smmu_setup_regs(struct smmu_device *smmu) { int i; u32 val; @@ -393,15 +477,12 @@ static void smmu_setup_regs(struct smmu_device *smmu) smmu_write(smmu, smmu->translation_enable_1, SMMU_TRANSLATION_ENABLE_1); smmu_write(smmu, smmu->translation_enable_2, SMMU_TRANSLATION_ENABLE_2); smmu_write(smmu, smmu->asid_security, SMMU_ASID_SECURITY); - smmu_write(smmu, SMMU_TLB_CONFIG_RESET_VAL, SMMU_TLB_CONFIG); - smmu_write(smmu, SMMU_PTC_CONFIG_RESET_VAL, SMMU_PTC_CONFIG); + smmu_write(smmu, SMMU_TLB_CONFIG_RESET_VAL, SMMU_CACHE_CONFIG(_TLB)); + smmu_write(smmu, SMMU_PTC_CONFIG_RESET_VAL, SMMU_CACHE_CONFIG(_PTC)); smmu_flush_regs(smmu, 1); - val = ahb_read(smmu, AHB_XBAR_CTRL); - val |= AHB_XBAR_CTRL_SMMU_INIT_DONE_DONE << - AHB_XBAR_CTRL_SMMU_INIT_DONE_SHIFT; - ahb_write(smmu, val, AHB_XBAR_CTRL); + return tegra_ahb_enable_smmu(smmu->ahb); } static void flush_ptc_and_tlb(struct smmu_device *smmu, @@ -537,33 +618,42 @@ static inline void put_signature(struct smmu_as *as, #endif /* - * Caller must lock/unlock as + * Caller must not hold as->lock */ static int alloc_pdir(struct smmu_as *as) { - unsigned long *pdir; - int pdn; + unsigned long *pdir, flags; + int pdn, err = 0; u32 val; struct smmu_device *smmu = as->smmu; + struct page *page; + unsigned int *cnt; + + /* + * do the allocation, then grab as->lock + */ + cnt = devm_kzalloc(smmu->dev, + sizeof(cnt[0]) * SMMU_PDIR_COUNT, + GFP_KERNEL); + page = alloc_page(GFP_KERNEL | __GFP_DMA); - if (as->pdir_page) - return 0; + spin_lock_irqsave(&as->lock, flags); - as->pte_count = devm_kzalloc(smmu->dev, - sizeof(as->pte_count[0]) * SMMU_PDIR_COUNT, GFP_ATOMIC); - if (!as->pte_count) { - dev_err(smmu->dev, - "failed to allocate smmu_device PTE cunters\n"); - return -ENOMEM; + if (as->pdir_page) { + /* We raced, free the redundant */ + err = -EAGAIN; + goto err_out; } - as->pdir_page = alloc_page(GFP_ATOMIC | __GFP_DMA); - if (!as->pdir_page) { - dev_err(smmu->dev, - "failed to allocate smmu_device page directory\n"); - devm_kfree(smmu->dev, as->pte_count); - as->pte_count = NULL; - return -ENOMEM; + + if (!page || !cnt) { + dev_err(smmu->dev, "failed to allocate at %s\n", __func__); + err = -ENOMEM; + goto err_out; } + + as->pdir_page = page; + as->pte_count = cnt; + SetPageReserved(as->pdir_page); pdir = page_address(as->pdir_page); @@ -579,7 +669,17 @@ static int alloc_pdir(struct smmu_as *as) smmu_write(smmu, val, SMMU_TLB_FLUSH); FLUSH_SMMU_REGS(as->smmu); + spin_unlock_irqrestore(&as->lock, flags); + return 0; + +err_out: + spin_unlock_irqrestore(&as->lock, flags); + + devm_kfree(smmu->dev, cnt); + if (page) + __free_page(page); + return err; } static void __smmu_iommu_unmap(struct smmu_as *as, dma_addr_t iova) @@ -598,10 +698,8 @@ static void __smmu_iommu_unmap(struct smmu_as *as, dma_addr_t iova) *pte = _PTE_VACANT(iova); FLUSH_CPU_DCACHE(pte, page, sizeof(*pte)); flush_ptc_and_tlb(as->smmu, as, iova, pte, page, 0); - if (!--(*count)) { + if (!--(*count)) free_ptbl(as, iova); - smmu_flush_regs(as->smmu, 0); - } } static void __smmu_iommu_map_pfn(struct smmu_as *as, dma_addr_t iova, @@ -633,7 +731,7 @@ static int smmu_iommu_map(struct iommu_domain *domain, unsigned long iova, unsigned long pfn = __phys_to_pfn(pa); unsigned long flags; - dev_dbg(as->smmu->dev, "[%d] %08lx:%08x\n", as->asid, iova, pa); + dev_dbg(as->smmu->dev, "[%d] %08lx:%pa\n", as->asid, iova, &pa); if (!pfn_valid(pfn)) return -ENOMEM; @@ -659,7 +757,7 @@ static size_t smmu_iommu_unmap(struct iommu_domain *domain, unsigned long iova, } static phys_addr_t smmu_iommu_iova_to_phys(struct iommu_domain *domain, - unsigned long iova) + dma_addr_t iova) { struct smmu_as *as = domain->priv; unsigned long *pte; @@ -674,7 +772,8 @@ static phys_addr_t smmu_iommu_iova_to_phys(struct iommu_domain *domain, pfn = *pte & SMMU_PFN_MASK; WARN_ON(!pfn_valid(pfn)); dev_dbg(as->smmu->dev, - "iova:%08lx pfn:%08lx asid:%d\n", iova, pfn, as->asid); + "iova:%08llx pfn:%08lx asid:%d\n", (unsigned long long)iova, + pfn, as->asid); spin_unlock_irqrestore(&as->lock, flags); return PFN_PHYS(pfn); @@ -764,37 +863,38 @@ static void smmu_iommu_detach_dev(struct iommu_domain *domain, goto out; } } - dev_err(smmu->dev, "Couldn't find %s\n", dev_name(c->dev)); + dev_err(smmu->dev, "Couldn't find %s\n", dev_name(dev)); out: spin_unlock(&as->client_lock); } static int smmu_iommu_domain_init(struct iommu_domain *domain) { - int i; + int i, err = -EAGAIN; unsigned long flags; struct smmu_as *as; struct smmu_device *smmu = smmu_handle; /* Look for a free AS with lock held */ for (i = 0; i < smmu->num_as; i++) { - struct smmu_as *tmp = &smmu->as[i]; + as = &smmu->as[i]; - spin_lock_irqsave(&tmp->lock, flags); - if (!tmp->pdir_page) { - as = tmp; + if (as->pdir_page) + continue; + + err = alloc_pdir(as); + if (!err) goto found; - } - spin_unlock_irqrestore(&tmp->lock, flags); + + if (err != -EAGAIN) + break; } - dev_err(smmu->dev, "no free AS\n"); - return -ENODEV; + if (i == smmu->num_as) + dev_err(smmu->dev, "no free AS\n"); + return err; found: - if (alloc_pdir(as) < 0) - goto err_alloc_pdir; - - spin_lock(&smmu->lock); + spin_lock_irqsave(&smmu->lock, flags); /* Update PDIR register */ smmu_write(smmu, SMMU_PTB_ASID_CUR(as->asid), SMMU_PTB_ASID); @@ -802,17 +902,18 @@ found: SMMU_MK_PDIR(as->pdir_page, as->pdir_attr), SMMU_PTB_DATA); FLUSH_SMMU_REGS(smmu); - spin_unlock(&smmu->lock); + spin_unlock_irqrestore(&smmu->lock, flags); - spin_unlock_irqrestore(&as->lock, flags); domain->priv = as; + domain->geometry.aperture_start = smmu->iovmm_base; + domain->geometry.aperture_end = smmu->iovmm_base + + smmu->page_count * SMMU_PAGE_SIZE - 1; + domain->geometry.force_aperture = true; + dev_dbg(smmu->dev, "smmu_as@%p\n", as); - return 0; -err_alloc_pdir: - spin_unlock_irqrestore(&as->lock, flags); - return -ENODEV; + return 0; } static void smmu_iommu_domain_destroy(struct iommu_domain *domain) @@ -858,6 +959,168 @@ static struct iommu_ops smmu_iommu_ops = { .pgsize_bitmap = SMMU_IOMMU_PGSIZES, }; +/* Should be in the order of enum */ +static const char * const smmu_debugfs_mc[] = { "mc", }; +static const char * const smmu_debugfs_cache[] = { "tlb", "ptc", }; + +static ssize_t smmu_debugfs_stats_write(struct file *file, + const char __user *buffer, + size_t count, loff_t *pos) +{ + struct smmu_debugfs_info *info; + struct smmu_device *smmu; + int i; + enum { + _OFF = 0, + _ON, + _RESET, + }; + const char * const command[] = { + [_OFF] = "off", + [_ON] = "on", + [_RESET] = "reset", + }; + char str[] = "reset"; + u32 val; + size_t offs; + + count = min_t(size_t, count, sizeof(str)); + if (copy_from_user(str, buffer, count)) + return -EINVAL; + + for (i = 0; i < ARRAY_SIZE(command); i++) + if (strncmp(str, command[i], + strlen(command[i])) == 0) + break; + + if (i == ARRAY_SIZE(command)) + return -EINVAL; + + info = file_inode(file)->i_private; + smmu = info->smmu; + + offs = SMMU_CACHE_CONFIG(info->cache); + val = smmu_read(smmu, offs); + switch (i) { + case _OFF: + val &= ~SMMU_CACHE_CONFIG_STATS_ENABLE; + val &= ~SMMU_CACHE_CONFIG_STATS_TEST; + smmu_write(smmu, val, offs); + break; + case _ON: + val |= SMMU_CACHE_CONFIG_STATS_ENABLE; + val &= ~SMMU_CACHE_CONFIG_STATS_TEST; + smmu_write(smmu, val, offs); + break; + case _RESET: + val |= SMMU_CACHE_CONFIG_STATS_TEST; + smmu_write(smmu, val, offs); + val &= ~SMMU_CACHE_CONFIG_STATS_TEST; + smmu_write(smmu, val, offs); + break; + default: + BUG(); + break; + } + + dev_dbg(smmu->dev, "%s() %08x, %08x @%08x\n", __func__, + val, smmu_read(smmu, offs), offs); + + return count; +} + +static int smmu_debugfs_stats_show(struct seq_file *s, void *v) +{ + struct smmu_debugfs_info *info = s->private; + struct smmu_device *smmu = info->smmu; + int i; + const char * const stats[] = { "hit", "miss", }; + + + for (i = 0; i < ARRAY_SIZE(stats); i++) { + u32 val; + size_t offs; + + offs = SMMU_STATS_CACHE_COUNT(info->mc, info->cache, i); + val = smmu_read(smmu, offs); + seq_printf(s, "%s:%08x ", stats[i], val); + + dev_dbg(smmu->dev, "%s() %s %08x @%08x\n", __func__, + stats[i], val, offs); + } + seq_printf(s, "\n"); + return 0; +} + +static int smmu_debugfs_stats_open(struct inode *inode, struct file *file) +{ + return single_open(file, smmu_debugfs_stats_show, inode->i_private); +} + +static const struct file_operations smmu_debugfs_stats_fops = { + .open = smmu_debugfs_stats_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, + .write = smmu_debugfs_stats_write, +}; + +static void smmu_debugfs_delete(struct smmu_device *smmu) +{ + debugfs_remove_recursive(smmu->debugfs_root); + kfree(smmu->debugfs_info); +} + +static void smmu_debugfs_create(struct smmu_device *smmu) +{ + int i; + size_t bytes; + struct dentry *root; + + bytes = ARRAY_SIZE(smmu_debugfs_mc) * ARRAY_SIZE(smmu_debugfs_cache) * + sizeof(*smmu->debugfs_info); + smmu->debugfs_info = kmalloc(bytes, GFP_KERNEL); + if (!smmu->debugfs_info) + return; + + root = debugfs_create_dir(dev_name(smmu->dev), NULL); + if (!root) + goto err_out; + smmu->debugfs_root = root; + + for (i = 0; i < ARRAY_SIZE(smmu_debugfs_mc); i++) { + int j; + struct dentry *mc; + + mc = debugfs_create_dir(smmu_debugfs_mc[i], root); + if (!mc) + goto err_out; + + for (j = 0; j < ARRAY_SIZE(smmu_debugfs_cache); j++) { + struct dentry *cache; + struct smmu_debugfs_info *info; + + info = smmu->debugfs_info; + info += i * ARRAY_SIZE(smmu_debugfs_mc) + j; + info->smmu = smmu; + info->mc = i; + info->cache = j; + + cache = debugfs_create_file(smmu_debugfs_cache[j], + S_IWUGO | S_IRUGO, mc, + (void *)info, + &smmu_debugfs_stats_fops); + if (!cache) + goto err_out; + } + } + + return; + +err_out: + smmu_debugfs_delete(smmu); +} + static int tegra_smmu_suspend(struct device *dev) { struct smmu_device *smmu = dev_get_drvdata(dev); @@ -873,65 +1136,80 @@ static int tegra_smmu_resume(struct device *dev) { struct smmu_device *smmu = dev_get_drvdata(dev); unsigned long flags; + int err; spin_lock_irqsave(&smmu->lock, flags); - smmu_setup_regs(smmu); + err = smmu_setup_regs(smmu); spin_unlock_irqrestore(&smmu->lock, flags); - return 0; + return err; } static int tegra_smmu_probe(struct platform_device *pdev) { struct smmu_device *smmu; - struct resource *regs, *regs2, *window; struct device *dev = &pdev->dev; - int i, err = 0; + int i, asids, err = 0; + dma_addr_t uninitialized_var(base); + size_t bytes, uninitialized_var(size); if (smmu_handle) return -EIO; BUILD_BUG_ON(PAGE_SHIFT != SMMU_PAGE_SHIFT); - regs = platform_get_resource(pdev, IORESOURCE_MEM, 0); - regs2 = platform_get_resource(pdev, IORESOURCE_MEM, 1); - window = platform_get_resource(pdev, IORESOURCE_MEM, 2); - if (!regs || !regs2 || !window) { - dev_err(dev, "No SMMU resources\n"); + if (of_property_read_u32(dev->of_node, "nvidia,#asids", &asids)) return -ENODEV; - } - smmu = devm_kzalloc(dev, sizeof(*smmu), GFP_KERNEL); + bytes = sizeof(*smmu) + asids * sizeof(*smmu->as); + smmu = devm_kzalloc(dev, bytes, GFP_KERNEL); if (!smmu) { dev_err(dev, "failed to allocate smmu_device\n"); return -ENOMEM; } - smmu->dev = dev; - smmu->num_as = SMMU_NUM_ASIDS; - smmu->iovmm_base = (unsigned long)window->start; - smmu->page_count = resource_size(window) >> SMMU_PAGE_SHIFT; - smmu->regs = devm_ioremap(dev, regs->start, resource_size(regs)); - smmu->regs_ahbarb = devm_ioremap(dev, regs2->start, - resource_size(regs2)); - if (!smmu->regs || !smmu->regs_ahbarb) { - dev_err(dev, "failed to remap SMMU registers\n"); - err = -ENXIO; - goto fail; + smmu->nregs = pdev->num_resources; + smmu->regs = devm_kzalloc(dev, 2 * smmu->nregs * sizeof(*smmu->regs), + GFP_KERNEL); + smmu->rege = smmu->regs + smmu->nregs; + if (!smmu->regs) + return -ENOMEM; + for (i = 0; i < smmu->nregs; i++) { + struct resource *res; + + res = platform_get_resource(pdev, IORESOURCE_MEM, i); + smmu->regs[i] = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(smmu->regs[i])) + return PTR_ERR(smmu->regs[i]); + smmu->rege[i] = smmu->regs[i] + resource_size(res) - 1; } + /* Same as "mc" 1st regiter block start address */ + smmu->regbase = (void __iomem *)((u32)smmu->regs[0] & PAGE_MASK); + + err = of_get_dma_window(dev->of_node, NULL, 0, NULL, &base, &size); + if (err) + return -ENODEV; + + if (size & SMMU_PAGE_MASK) + return -EINVAL; + + size >>= SMMU_PAGE_SHIFT; + if (!size) + return -EINVAL; + + smmu->ahb = of_parse_phandle(dev->of_node, "nvidia,ahb", 0); + if (!smmu->ahb) + return -ENODEV; + + smmu->dev = dev; + smmu->num_as = asids; + smmu->iovmm_base = base; + smmu->page_count = size; smmu->translation_enable_0 = ~0; smmu->translation_enable_1 = ~0; smmu->translation_enable_2 = ~0; smmu->asid_security = 0; - smmu->as = devm_kzalloc(dev, - sizeof(smmu->as[0]) * smmu->num_as, GFP_KERNEL); - if (!smmu->as) { - dev_err(dev, "failed to allocate smmu_as\n"); - err = -ENOMEM; - goto fail; - } - for (i = 0; i < smmu->num_as; i++) { struct smmu_as *as = &smmu->as[i]; @@ -942,69 +1220,51 @@ static int tegra_smmu_probe(struct platform_device *pdev) as->pte_attr = _PTE_ATTR; spin_lock_init(&as->lock); + spin_lock_init(&as->client_lock); INIT_LIST_HEAD(&as->client); } spin_lock_init(&smmu->lock); - smmu_setup_regs(smmu); + err = smmu_setup_regs(smmu); + if (err) + return err; platform_set_drvdata(pdev, smmu); smmu->avp_vector_page = alloc_page(GFP_KERNEL); if (!smmu->avp_vector_page) - goto fail; + return -ENOMEM; + smmu_debugfs_create(smmu); smmu_handle = smmu; + bus_set_iommu(&platform_bus_type, &smmu_iommu_ops); return 0; - -fail: - if (smmu->avp_vector_page) - __free_page(smmu->avp_vector_page); - if (smmu->regs) - devm_iounmap(dev, smmu->regs); - if (smmu->regs_ahbarb) - devm_iounmap(dev, smmu->regs_ahbarb); - if (smmu && smmu->as) { - for (i = 0; i < smmu->num_as; i++) { - if (smmu->as[i].pdir_page) { - ClearPageReserved(smmu->as[i].pdir_page); - __free_page(smmu->as[i].pdir_page); - } - } - devm_kfree(dev, smmu->as); - } - devm_kfree(dev, smmu); - return err; } static int tegra_smmu_remove(struct platform_device *pdev) { struct smmu_device *smmu = platform_get_drvdata(pdev); - struct device *dev = smmu->dev; + int i; - smmu_write(smmu, SMMU_CONFIG_DISABLE, SMMU_CONFIG); - platform_set_drvdata(pdev, NULL); - if (smmu->as) { - int i; + smmu_debugfs_delete(smmu); - for (i = 0; i < smmu->num_as; i++) - free_pdir(&smmu->as[i]); - devm_kfree(dev, smmu->as); - } - if (smmu->avp_vector_page) - __free_page(smmu->avp_vector_page); - if (smmu->regs) - devm_iounmap(dev, smmu->regs); - if (smmu->regs_ahbarb) - devm_iounmap(dev, smmu->regs_ahbarb); - devm_kfree(dev, smmu); + smmu_write(smmu, SMMU_CONFIG_DISABLE, SMMU_CONFIG); + for (i = 0; i < smmu->num_as; i++) + free_pdir(&smmu->as[i]); + __free_page(smmu->avp_vector_page); smmu_handle = NULL; return 0; } -const struct dev_pm_ops tegra_smmu_pm_ops = { +static const struct dev_pm_ops tegra_smmu_pm_ops = { .suspend = tegra_smmu_suspend, .resume = tegra_smmu_resume, }; +static struct of_device_id tegra_smmu_of_match[] = { + { .compatible = "nvidia,tegra30-smmu", }, + { }, +}; +MODULE_DEVICE_TABLE(of, tegra_smmu_of_match); + static struct platform_driver tegra_smmu_driver = { .probe = tegra_smmu_probe, .remove = tegra_smmu_remove, @@ -1012,12 +1272,12 @@ static struct platform_driver tegra_smmu_driver = { .owner = THIS_MODULE, .name = "tegra-smmu", .pm = &tegra_smmu_pm_ops, + .of_match_table = tegra_smmu_of_match, }, }; -static int __devinit tegra_smmu_init(void) +static int tegra_smmu_init(void) { - bus_set_iommu(&platform_bus_type, &smmu_iommu_ops); return platform_driver_register(&tegra_smmu_driver); } @@ -1031,4 +1291,5 @@ module_exit(tegra_smmu_exit); MODULE_DESCRIPTION("IOMMU API for SMMU in Tegra30"); MODULE_AUTHOR("Hiroshi DOYU <hdoyu@nvidia.com>"); +MODULE_ALIAS("platform:tegra-smmu"); MODULE_LICENSE("GPL v2"); |
