Skip to content

Commit

Permalink
---
Browse files Browse the repository at this point in the history
yaml
---
r: 316586
b: refs/heads/master
c: 0760e8f
h: refs/heads/master
v: v3
  • Loading branch information
Hiroshi Doyu authored and Joerg Roedel committed Jun 25, 2012
1 parent 88084b0 commit da5e0c4
Show file tree
Hide file tree
Showing 4 changed files with 118 additions and 56 deletions.
2 changes: 1 addition & 1 deletion [refs]
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
---
refs/heads/master: 4e0ee78f2af96676c9dca898c13250f62c513058
refs/heads/master: 0760e8faa960f8ee991fa4acb802db4e20661281
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
NVIDIA Tegra 30 IOMMU H/W, SMMU (System Memory Management Unit)

Required properties:
- compatible : "nvidia,tegra30-smmu"
- reg : Should contain 3 register banks(address and length) for each
of the SMMU register blocks.
- interrupts : Should contain MC General interrupt.
- nvidia,#asids : # of ASIDs
- dma-window : IOVA start address and length.
- nvidia,ahb : phandle to the ahb bus connected to SMMU.

Example:
smmu {
compatible = "nvidia,tegra30-smmu";
reg = <0x7000f010 0x02c
0x7000f1f0 0x010
0x7000f228 0x05c>;
nvidia,#asids = <4>; /* # of ASIDs */
dma-window = <0 0x40000000>; /* IOVA start & length */
nvidia,ahb = <&ahb>;
};
2 changes: 1 addition & 1 deletion trunk/drivers/iommu/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -158,7 +158,7 @@ config TEGRA_IOMMU_GART

config TEGRA_IOMMU_SMMU
bool "Tegra SMMU IOMMU Support"
depends on ARCH_TEGRA_3x_SOC
depends on ARCH_TEGRA_3x_SOC && TEGRA_AHB
select IOMMU_API
help
Enables support for remapping discontiguous physical memory
Expand Down
149 changes: 95 additions & 54 deletions trunk/drivers/iommu/tegra-smmu.c
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down Expand Up @@ -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
Expand All @@ -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)
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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;
Expand All @@ -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) \
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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;
Expand All @@ -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);
Expand All @@ -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) {
Expand Down Expand Up @@ -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;
Expand All @@ -1005,13 +1036,22 @@ 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,
.driver = {
.owner = THIS_MODULE,
.name = "tegra-smmu",
.pm = &tegra_smmu_pm_ops,
.of_match_table = of_match_ptr(tegra_smmu_of_match),
},
};

Expand All @@ -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");

0 comments on commit da5e0c4

Please sign in to comment.