|
@@ -30,12 +30,15 @@
|
|
|
#include <linux/sched.h>
|
|
|
#include <linux/iommu.h>
|
|
|
#include <linux/io.h>
|
|
|
+#include <linux/of.h>
|
|
|
+#include <linux/of_iommu.h>
|
|
|
|
|
|
#include <asm/page.h>
|
|
|
#include <asm/cacheflush.h>
|
|
|
|
|
|
#include <mach/iomap.h>
|
|
|
#include <mach/smmu.h>
|
|
|
+#include <mach/tegra-ahb.h>
|
|
|
|
|
|
/* bitmap of the page sizes currently supported */
|
|
|
#define SMMU_IOMMU_PGSIZES (SZ_4K)
|
|
@@ -111,12 +114,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 +133,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)
|
|
@@ -177,6 +175,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)
|
|
@@ -235,7 +235,7 @@ struct smmu_as {
|
|
|
* Per SMMU device - IOMMU device
|
|
|
*/
|
|
|
struct smmu_device {
|
|
|
- void __iomem *regs, *regs_ahbarb;
|
|
|
+ void __iomem *regs[NUM_SMMU_REG_BANKS];
|
|
|
unsigned long iovmm_base; /* remappable base address */
|
|
|
unsigned long page_count; /* total remappable size */
|
|
|
spinlock_t lock;
|
|
@@ -252,29 +252,47 @@ struct smmu_device {
|
|
|
unsigned long translation_enable_1;
|
|
|
unsigned long translation_enable_2;
|
|
|
unsigned long asid_security;
|
|
|
+
|
|
|
+ struct device_node *ahb;
|
|
|
};
|
|
|
|
|
|
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)
|
|
|
-{
|
|
|
- writel(val, smmu->regs + offs);
|
|
|
+ BUG_ON(offs < 0x10);
|
|
|
+ if (offs < 0x3c)
|
|
|
+ return readl(smmu->regs[0] + offs - 0x10);
|
|
|
+ BUG_ON(offs < 0x1f0);
|
|
|
+ if (offs < 0x200)
|
|
|
+ return readl(smmu->regs[1] + offs - 0x1f0);
|
|
|
+ BUG_ON(offs < 0x228);
|
|
|
+ if (offs < 0x284)
|
|
|
+ return readl(smmu->regs[2] + offs - 0x228);
|
|
|
+ BUG();
|
|
|
}
|
|
|
|
|
|
-static inline u32 ahb_read(struct smmu_device *smmu, size_t offs)
|
|
|
-{
|
|
|
- return readl(smmu->regs_ahbarb + offs);
|
|
|
-}
|
|
|
-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);
|
|
|
+ BUG_ON(offs < 0x10);
|
|
|
+ if (offs < 0x3c) {
|
|
|
+ writel(val, smmu->regs[0] + offs - 0x10);
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ BUG_ON(offs < 0x1f0);
|
|
|
+ if (offs < 0x200) {
|
|
|
+ writel(val, smmu->regs[1] + offs - 0x1f0);
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ BUG_ON(offs < 0x228);
|
|
|
+ if (offs < 0x284) {
|
|
|
+ writel(val, smmu->regs[2] + offs - 0x228);
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ BUG();
|
|
|
}
|
|
|
|
|
|
#define VA_PAGE_TO_PA(va, page) \
|
|
@@ -370,7 +388,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;
|
|
@@ -398,10 +416,7 @@ static void smmu_setup_regs(struct smmu_device *smmu)
|
|
|
|
|
|
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,
|
|
@@ -873,52 +888,72 @@ 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 base;
|
|
|
+ size_t size;
|
|
|
+ const void *prop;
|
|
|
|
|
|
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");
|
|
|
- return -ENODEV;
|
|
|
- }
|
|
|
-
|
|
|
smmu = devm_kzalloc(dev, sizeof(*smmu), 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;
|
|
|
+ for (i = 0; i < ARRAY_SIZE(smmu->regs); i++) {
|
|
|
+ struct resource *res;
|
|
|
+
|
|
|
+ res = platform_get_resource(pdev, IORESOURCE_MEM, i);
|
|
|
+ if (!res)
|
|
|
+ return -ENODEV;
|
|
|
+ smmu->regs[i] = devm_request_and_ioremap(&pdev->dev, res);
|
|
|
+ if (!smmu->regs[i])
|
|
|
+ return -EBUSY;
|
|
|
}
|
|
|
|
|
|
+ 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;
|
|
|
+
|
|
|
+ prop = of_get_property(dev->of_node, "nvidia,#asids", NULL);
|
|
|
+ if (!prop)
|
|
|
+ return -ENODEV;
|
|
|
+ asids = be32_to_cpup(prop);
|
|
|
+ if (!asids)
|
|
|
+ return -ENODEV;
|
|
|
+
|
|
|
+ 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;
|
|
@@ -945,7 +980,9 @@ static int tegra_smmu_probe(struct platform_device *pdev)
|
|
|
INIT_LIST_HEAD(&as->client);
|
|
|
}
|
|
|
spin_lock_init(&smmu->lock);
|
|
|
- smmu_setup_regs(smmu);
|
|
|
+ err = smmu_setup_regs(smmu);
|
|
|
+ if (err)
|
|
|
+ goto fail;
|
|
|
platform_set_drvdata(pdev, smmu);
|
|
|
|
|
|
smmu->avp_vector_page = alloc_page(GFP_KERNEL);
|
|
@@ -958,10 +995,6 @@ static int tegra_smmu_probe(struct platform_device *pdev)
|
|
|
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) {
|
|
@@ -993,8 +1026,6 @@ static int tegra_smmu_remove(struct platform_device *pdev)
|
|
|
__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_handle = NULL;
|
|
|
return 0;
|
|
@@ -1005,6 +1036,14 @@ const struct dev_pm_ops tegra_smmu_pm_ops = {
|
|
|
.resume = tegra_smmu_resume,
|
|
|
};
|
|
|
|
|
|
+#ifdef CONFIG_OF
|
|
|
+static struct of_device_id tegra_smmu_of_match[] __devinitdata = {
|
|
|
+ { .compatible = "nvidia,tegra30-smmu", },
|
|
|
+ { },
|
|
|
+};
|
|
|
+MODULE_DEVICE_TABLE(of, tegra_smmu_of_match);
|
|
|
+#endif
|
|
|
+
|
|
|
static struct platform_driver tegra_smmu_driver = {
|
|
|
.probe = tegra_smmu_probe,
|
|
|
.remove = tegra_smmu_remove,
|
|
@@ -1012,6 +1051,7 @@ static struct platform_driver tegra_smmu_driver = {
|
|
|
.owner = THIS_MODULE,
|
|
|
.name = "tegra-smmu",
|
|
|
.pm = &tegra_smmu_pm_ops,
|
|
|
+ .of_match_table = of_match_ptr(tegra_smmu_of_match),
|
|
|
},
|
|
|
};
|
|
|
|
|
@@ -1031,4 +1071,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");
|