summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorThierry Reding <treding@nvidia.com>2014-06-04 12:09:56 +0200
committerThierry Reding <treding@nvidia.com>2014-06-04 12:09:56 +0200
commit571010715abc1a007b7f2c842fab2638a2688c20 (patch)
treef3a12e9df14d9da9223c4612e33d7c5a7b77c204
parent3e6434eab2165f0bc9ba882d6d3ca8686d195bcd (diff)
parent4e444487783cb012ec7754ed0dd36aa4e1d53405 (diff)
Merge branch 'staging/iommu' into staging/master
-rw-r--r--Documentation/devicetree/bindings/iommu/nvidia,tegra30-smmu.txt4
-rw-r--r--arch/arm/boot/dts/tegra124.dtsi19
-rw-r--r--drivers/base/dd.c5
-rw-r--r--drivers/iommu/Kconfig1
-rw-r--r--drivers/iommu/iommu.c13
-rw-r--r--drivers/iommu/of_iommu.c49
-rw-r--r--drivers/iommu/tegra-smmu.c567
-rw-r--r--drivers/of/base.c50
-rw-r--r--include/dt-bindings/memory/tegra-swgroup.h50
-rw-r--r--include/linux/iommu.h4
-rw-r--r--include/linux/of.h36
-rw-r--r--include/linux/of_iommu.h22
12 files changed, 626 insertions, 194 deletions
diff --git a/Documentation/devicetree/bindings/iommu/nvidia,tegra30-smmu.txt b/Documentation/devicetree/bindings/iommu/nvidia,tegra30-smmu.txt
index 89fb5434b73..38b444ff1d4 100644
--- a/Documentation/devicetree/bindings/iommu/nvidia,tegra30-smmu.txt
+++ b/Documentation/devicetree/bindings/iommu/nvidia,tegra30-smmu.txt
@@ -1,8 +1,8 @@
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
+- compatible : "nvidia,tegra124-smmu", "nvidia,tegra30-smmu"
+- reg : Can contain multiple register banks(address and length) for each
of the SMMU register blocks.
- interrupts : Should contain MC General interrupt.
- nvidia,#asids : # of ASIDs
diff --git a/arch/arm/boot/dts/tegra124.dtsi b/arch/arm/boot/dts/tegra124.dtsi
index 6e6bc4e8185..011831ffd39 100644
--- a/arch/arm/boot/dts/tegra124.dtsi
+++ b/arch/arm/boot/dts/tegra124.dtsi
@@ -1,5 +1,6 @@
#include <dt-bindings/clock/tegra124-car.h>
#include <dt-bindings/gpio/tegra-gpio.h>
+#include <dt-bindings/memory/tegra-swgroup.h>
#include <dt-bindings/pinctrl/pinctrl-tegra.h>
#include <dt-bindings/interrupt-controller/arm-gic.h>
@@ -449,6 +450,20 @@
clock-names = "pclk", "clk32k_in";
};
+ smmu: iommu {
+ compatible = "nvidia,tegra124-smmu";
+ reg = <0x0 0x70019010 0x0 0x34
+ 0x0 0x700191f0 0x0 0x10
+ 0x0 0x70019228 0x0 0x58
+ 0x0 0x70019600 0x0 0x4
+ 0x0 0x700199b8 0x0 0x4
+ 0x0 0x700199e0 0x0 0x18
+ 0x0 0x70019a88 0x0 0x24>;
+ nvidia,#asids = <128>;
+ dma-window = <0x0 0x0 0x0 0x40000000>;
+ iommu-cells = <2>;
+ };
+
sdhci@0,700b0000 {
compatible = "nvidia,tegra124-sdhci";
reg = <0x0 0x700b0000 0x0 0x200>;
@@ -457,6 +472,7 @@
resets = <&tegra_car 14>;
reset-names = "sdhci";
status = "disabled";
+ iommus=<&smmu TEGRA_SWGROUP_CELLS(SDMMC1A)>;
};
sdhci@0,700b0200 {
@@ -467,6 +483,7 @@
resets = <&tegra_car 9>;
reset-names = "sdhci";
status = "disabled";
+ iommus=<&smmu TEGRA_SWGROUP_CELLS(SDMMC2A)>;
};
sdhci@0,700b0400 {
@@ -477,6 +494,7 @@
resets = <&tegra_car 69>;
reset-names = "sdhci";
status = "disabled";
+ iommus=<&smmu TEGRA_SWGROUP_CELLS(SDMMC3A)>;
};
sdhci@0,700b0600 {
@@ -487,6 +505,7 @@
resets = <&tegra_car 15>;
reset-names = "sdhci";
status = "disabled";
+ iommus=<&smmu TEGRA_SWGROUP_CELLS(SDMMC4A)>;
};
ahub@0,70300000 {
diff --git a/drivers/base/dd.c b/drivers/base/dd.c
index e4ffbcf2f51..34ef56539be 100644
--- a/drivers/base/dd.c
+++ b/drivers/base/dd.c
@@ -25,6 +25,7 @@
#include <linux/async.h>
#include <linux/pm_runtime.h>
#include <linux/pinctrl/devinfo.h>
+#include <linux/of_iommu.h>
#include "base.h"
#include "power/power.h"
@@ -287,6 +288,10 @@ static int really_probe(struct device *dev, struct device_driver *drv)
dev->driver = drv;
+ ret = of_iommu_attach(dev);
+ if (ret)
+ goto probe_failed;
+
/* If using pinctrl, bind pins now before probing */
ret = pinctrl_bind_pins(dev);
if (ret)
diff --git a/drivers/iommu/Kconfig b/drivers/iommu/Kconfig
index d260605e6d5..ed4aba0b62e 100644
--- a/drivers/iommu/Kconfig
+++ b/drivers/iommu/Kconfig
@@ -170,6 +170,7 @@ config TEGRA_IOMMU_SMMU
bool "Tegra SMMU IOMMU Support"
depends on ARCH_TEGRA && TEGRA_AHB
select IOMMU_API
+ select ARM_DMA_USE_IOMMU
help
Enables support for remapping discontiguous physical memory
shared with the operating system into contiguous I/O virtual
diff --git a/drivers/iommu/iommu.c b/drivers/iommu/iommu.c
index e5555fcfe70..5469d361e7b 100644
--- a/drivers/iommu/iommu.c
+++ b/drivers/iommu/iommu.c
@@ -540,14 +540,23 @@ static int iommu_bus_notifier(struct notifier_block *nb,
* ADD/DEL call into iommu driver ops if provided, which may
* result in ADD/DEL notifiers to group->notifier
*/
- if (action == BUS_NOTIFY_ADD_DEVICE) {
+ switch (action) {
+ case BUS_NOTIFY_ADD_DEVICE:
if (ops->add_device)
return ops->add_device(dev);
- } else if (action == BUS_NOTIFY_DEL_DEVICE) {
+ case BUS_NOTIFY_DEL_DEVICE:
if (ops->remove_device && dev->iommu_group) {
ops->remove_device(dev);
return 0;
}
+ case BUS_NOTIFY_BOUND_DRIVER:
+ if (ops->bound_driver)
+ ops->bound_driver(dev);
+ break;
+ case BUS_NOTIFY_UNBIND_DRIVER:
+ if (ops->unbind_driver)
+ ops->unbind_driver(dev);
+ break;
}
/*
diff --git a/drivers/iommu/of_iommu.c b/drivers/iommu/of_iommu.c
index e550ccb7634..b9f5081515a 100644
--- a/drivers/iommu/of_iommu.c
+++ b/drivers/iommu/of_iommu.c
@@ -21,6 +21,42 @@
#include <linux/limits.h>
#include <linux/of.h>
#include <linux/of_iommu.h>
+#include <linux/device.h>
+
+static DEFINE_MUTEX(iommus_lock);
+static LIST_HEAD(iommus_list);
+
+void iommu_add(struct iommu *iommu)
+{
+ INIT_LIST_HEAD(&iommu->list);
+ mutex_lock(&iommus_lock);
+ list_add_tail(&iommu->list, &iommus_list);
+ mutex_unlock(&iommus_lock);
+}
+
+void iommu_del(struct iommu *iommu)
+{
+ INIT_LIST_HEAD(&iommu->list);
+ mutex_lock(&iommus_lock);
+ list_del(&iommu->list);
+ mutex_unlock(&iommus_lock);
+}
+
+static struct iommu *of_find_iommu_by_node(struct device_node *np)
+{
+ struct iommu *iommu;
+
+ mutex_lock(&iommus_lock);
+ list_for_each_entry(iommu, &iommus_list, list) {
+ if (iommu->dev->of_node == np) {
+ mutex_unlock(&iommus_lock);
+ return iommu;
+ }
+ }
+ mutex_unlock(&iommus_lock);
+
+ return NULL;
+}
/**
* of_get_dma_window - Parse *dma-window property and returns 0 if found.
@@ -89,3 +125,16 @@ int of_get_dma_window(struct device_node *dn, const char *prefix, int index,
return 0;
}
EXPORT_SYMBOL_GPL(of_get_dma_window);
+
+int of_iommu_attach(struct device *dev)
+{
+ struct of_phandle_iter iter;
+
+ of_property_for_each_phandle_with_args(iter, dev->of_node, "iommus",
+ "iommu-cells", 0) {
+ if (!of_find_iommu_by_node(iter.out_args.np))
+ return -EPROBE_DEFER;
+ }
+
+ return 0;
+}
diff --git a/drivers/iommu/tegra-smmu.c b/drivers/iommu/tegra-smmu.c
index 605b5b46a90..53a64a1ac24 100644
--- a/drivers/iommu/tegra-smmu.c
+++ b/drivers/iommu/tegra-smmu.c
@@ -32,6 +32,7 @@
#include <linux/iommu.h>
#include <linux/io.h>
#include <linux/of.h>
+#include <linux/of_device.h>
#include <linux/of_iommu.h>
#include <linux/debugfs.h>
#include <linux/seq_file.h>
@@ -39,46 +40,9 @@
#include <asm/page.h>
#include <asm/cacheflush.h>
+#include <asm/dma-iommu.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)
+#include <dt-bindings/memory/tegra-swgroup.h>
/* bitmap of the page sizes currently supported */
#define SMMU_IOMMU_PGSIZES (SZ_4K)
@@ -107,12 +71,13 @@ enum {
#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_TLB_CONFIG_RESET_VAL 0x20000000
+#define SMMU_TLB_RR_ARB (1 << 28)
#define SMMU_PTC_CONFIG_CACHE__ENABLE (1 << 29)
#define SMMU_PTC_CONFIG_INDEX_MAP__PATTERN 0x3f
#define SMMU_PTC_CONFIG_RESET_VAL 0x2000003f
+#define SMMU_PTC_REQ_LIMIT (8 << 24)
#define SMMU_PTB_ASID 0x1c
#define SMMU_PTB_ASID_CURRENT_SHIFT 0
@@ -127,17 +92,29 @@ enum {
#define SMMU_TLB_FLUSH_VA_MATCH_ALL 0
#define SMMU_TLB_FLUSH_VA_MATCH_SECTION 2
#define SMMU_TLB_FLUSH_VA_MATCH_GROUP 3
-#define SMMU_TLB_FLUSH_ASID_SHIFT 29
+#define SMMU_TLB_FLUSH_ASID_SHIFT_BASE 31
#define SMMU_TLB_FLUSH_ASID_MATCH_DISABLE 0
#define SMMU_TLB_FLUSH_ASID_MATCH_ENABLE 1
#define SMMU_TLB_FLUSH_ASID_MATCH_SHIFT 31
+#define SMMU_TLB_FLUSH_ASID_SHIFT(as) \
+ (SMMU_TLB_FLUSH_ASID_SHIFT_BASE - __ffs((as)->smmu->num_as))
+
#define SMMU_PTC_FLUSH 0x34
#define SMMU_PTC_FLUSH_TYPE_ALL 0
#define SMMU_PTC_FLUSH_TYPE_ADR 1
#define SMMU_PTC_FLUSH_ADR_SHIFT 4
+#define SMMU_PTC_FLUSH_1 0x9b8
+
#define SMMU_ASID_SECURITY 0x38
+#define SMMU_ASID_SECURITY_1 0x3c
+#define SMMU_ASID_SECURITY_2 0x9e0
+#define SMMU_ASID_SECURITY_3 0x9e4
+#define SMMU_ASID_SECURITY_4 0x9e8
+#define SMMU_ASID_SECURITY_5 0x9ec
+#define SMMU_ASID_SECURITY_6 0x9f0
+#define SMMU_ASID_SECURITY_7 0x9f4
#define SMMU_STATS_CACHE_COUNT_BASE 0x1f0
@@ -145,25 +122,9 @@ enum {
(SMMU_STATS_CACHE_COUNT_BASE + 8 * cache + 4 * hitmiss)
#define SMMU_TRANSLATION_ENABLE_0 0x228
-#define SMMU_TRANSLATION_ENABLE_1 0x22c
-#define SMMU_TRANSLATION_ENABLE_2 0x230
#define SMMU_AFI_ASID 0x238 /* PCIE */
-#define SMMU_AVPC_ASID 0x23c /* AVP */
-#define SMMU_DC_ASID 0x240 /* Display controller */
-#define SMMU_DCB_ASID 0x244 /* Display controller B */
-#define SMMU_EPP_ASID 0x248 /* Encoder pre-processor */
-#define SMMU_G2_ASID 0x24c /* 2D engine */
-#define SMMU_HC_ASID 0x250 /* Host1x */
-#define SMMU_HDA_ASID 0x254 /* High-def audio */
-#define SMMU_ISP_ASID 0x258 /* Image signal processor */
-#define SMMU_MPE_ASID 0x264 /* MPEG encoder */
-#define SMMU_NV_ASID 0x268 /* (3D) */
-#define SMMU_NV2_ASID 0x26c /* (3D) */
-#define SMMU_PPCS_ASID 0x270 /* AHB */
-#define SMMU_SATA_ASID 0x278 /* SATA */
-#define SMMU_VDE_ASID 0x27c /* Video decoder */
-#define SMMU_VI_ASID 0x280 /* Video input */
+#define SMMU_ASID_BASE SMMU_AFI_ASID
#define SMMU_PDE_NEXT_SHIFT 28
@@ -195,7 +156,7 @@ enum {
#define SMMU_PDIR_SHIFT 12
#define SMMU_PDE_SHIFT 12
#define SMMU_PTE_SHIFT 12
-#define SMMU_PFN_MASK 0x000fffff
+#define SMMU_PFN_MASK 0x0fffffff
#define SMMU_ADDR_TO_PFN(addr) ((addr) >> 12)
#define SMMU_ADDR_TO_PDN(addr) ((addr) >> 22)
@@ -230,41 +191,21 @@ enum {
#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)
-#define __smmu_client_disable_hwgrp(c) __smmu_client_set_hwgrp(c, 0, 0)
-
-#define HWGRP_INIT(client) [HWGRP_##client] = SMMU_##client##_ASID
-
-static const u32 smmu_hwgrp_asid_reg[] = {
- HWGRP_INIT(AFI),
- HWGRP_INIT(AVPC),
- HWGRP_INIT(DC),
- HWGRP_INIT(DCB),
- HWGRP_INIT(EPP),
- HWGRP_INIT(G2),
- HWGRP_INIT(HC),
- HWGRP_INIT(HDA),
- HWGRP_INIT(ISP),
- HWGRP_INIT(MPE),
- HWGRP_INIT(NV),
- HWGRP_INIT(NV2),
- HWGRP_INIT(PPCS),
- HWGRP_INIT(SATA),
- HWGRP_INIT(VDE),
- HWGRP_INIT(VI),
-};
-#define HWGRP_ASID_REG(x) (smmu_hwgrp_asid_reg[x])
+#define smmu_client_enable_swgroups(c, m) smmu_client_set_swgroups(c, m, 1)
+#define smmu_client_disable_swgroups(c) smmu_client_set_swgroups(c, 0, 0)
+#define __smmu_client_enable_swgroups(c, m) __smmu_client_set_swgroups(c, m, 1)
+#define __smmu_client_disable_swgroups(c) __smmu_client_set_swgroups(c, 0, 0)
/*
* Per client for address space
*/
struct smmu_client {
+ struct device_node *of_node;
+ struct rb_node node;
struct device *dev;
struct list_head list;
struct smmu_as *as;
- u32 hwgrp;
+ unsigned long swgroups[2];
};
/*
@@ -294,6 +235,8 @@ struct smmu_debugfs_info {
* Per SMMU device - IOMMU device
*/
struct smmu_device {
+ struct iommu iommu;
+
void __iomem *regbase; /* register offset base */
void __iomem **regs; /* register block start address array */
void __iomem **rege; /* register block end address array */
@@ -303,26 +246,40 @@ struct smmu_device {
unsigned long page_count; /* total remappable size */
spinlock_t lock;
char *name;
- struct device *dev;
+ struct rb_root clients;
struct page *avp_vector_page; /* dummy page shared by all AS's */
+ int nr_xlats; /* number of translation_enable registers */
+ int nr_asid_secs; /* number of asid_security registers */
+ u32 tlb_reset; /* TLB config reset value */
+ u32 ptc_reset; /* PTC config reset value */
+
/*
* Register image savers for suspend/resume
*/
- unsigned long translation_enable_0;
- unsigned long translation_enable_1;
- unsigned long translation_enable_2;
- unsigned long asid_security;
+ u32 *xlat;
+ u32 *asid_sec;
struct dentry *debugfs_root;
struct smmu_debugfs_info *debugfs_info;
struct device_node *ahb;
+ struct dma_iommu_mapping **map;
+
int num_as;
struct smmu_as as[0]; /* Run-time allocated array */
};
+struct smmu_platform_data {
+ int asids; /* number of asids */
+ int nr_xlats; /* number of translation_enable registers */
+ bool lpae; /* PA > 32 bit */
+ int nr_asid_secs; /* number of asid_security registers */
+ u32 tlb_reset; /* TLB config reset value */
+ u32 ptc_reset; /* PTC config reset value */
+};
+
static struct smmu_device *smmu_handle; /* unique for a system */
/*
@@ -364,6 +321,8 @@ static inline void smmu_write(struct smmu_device *smmu, u32 val, size_t offs)
#define VA_PAGE_TO_PA(va, page) \
(page_to_phys(page) + ((unsigned long)(va) & ~PAGE_MASK))
+#define VA_PAGE_TO_PA_HI(va, page) (u32)((u64)page_to_phys(page) >> 32)
+
#define FLUSH_CPU_DCACHE(va, page, size) \
do { \
unsigned long _pa_ = VA_PAGE_TO_PA(va, page); \
@@ -379,60 +338,170 @@ static inline void smmu_write(struct smmu_device *smmu, u32 val, size_t offs)
*/
#define FLUSH_SMMU_REGS(smmu) smmu_read(smmu, SMMU_CONFIG)
-#define smmu_client_hwgrp(c) (u32)((c)->dev->platform_data)
+static const u32 smmu_asid_sec_ofs[] = {
+ SMMU_ASID_SECURITY,
+ SMMU_ASID_SECURITY_1,
+ SMMU_ASID_SECURITY_2,
+ SMMU_ASID_SECURITY_3,
+ SMMU_ASID_SECURITY_4,
+ SMMU_ASID_SECURITY_5,
+ SMMU_ASID_SECURITY_6,
+ SMMU_ASID_SECURITY_7,
+};
+
+static size_t smmu_get_asid_offset(int id)
+{
+ switch (id) {
+ case TEGRA_SWGROUP_DC14:
+ return 0x490;
+ case TEGRA_SWGROUP_DC12:
+ return 0xa88;
+ case TEGRA_SWGROUP_AFI...TEGRA_SWGROUP_ISP:
+ case TEGRA_SWGROUP_MPE...TEGRA_SWGROUP_PPCS1:
+ return (id - TEGRA_SWGROUP_AFI) * sizeof(u32) + SMMU_ASID_BASE;
+ case TEGRA_SWGROUP_SDMMC1A...63:
+ return (id - TEGRA_SWGROUP_SDMMC1A) * sizeof(u32) + 0xa94;
+ };
+
+ BUG();
+}
+
+static struct smmu_client *find_smmu_client(struct smmu_device *smmu,
+ struct device_node *dev_node)
+{
+ struct rb_node *node = smmu->clients.rb_node;
+
+ while (node) {
+ struct smmu_client *client;
+
+ client = container_of(node, struct smmu_client, node);
+ if (dev_node < client->of_node)
+ node = node->rb_left;
+ else if (dev_node > client->of_node)
+ node = node->rb_right;
+ else
+ return client;
+ }
+
+ return NULL;
+}
+
+static int insert_smmu_client(struct smmu_device *smmu,
+ struct smmu_client *client)
+{
+ struct rb_node **new, *parent;
+
+ new = &smmu->clients.rb_node;
+ parent = NULL;
+ while (*new) {
+ struct smmu_client *this;
+
+ this = container_of(*new, struct smmu_client, node);
+ parent = *new;
+ if (client->of_node < this->of_node)
+ new = &((*new)->rb_left);
+ else if (client->of_node > this->of_node)
+ new = &((*new)->rb_right);
+ else
+ return -EEXIST;
+ }
+
+ rb_link_node(&client->node, parent, new);
+ rb_insert_color(&client->node, &smmu->clients);
+ return 0;
+}
+
+static int register_smmu_client(struct smmu_device *smmu,
+ struct device *dev, unsigned long *swgroups)
+{
+ struct smmu_client *client;
+
+ client = find_smmu_client(smmu, dev->of_node);
+ if (client) {
+ dev_err(dev,
+ "rejecting multiple registrations for client device %s\n",
+ dev->of_node->full_name);
+ return -EBUSY;
+ }
+
+ client = devm_kzalloc(smmu->iommu.dev, sizeof(*client), GFP_KERNEL);
+ if (!client)
+ return -ENOMEM;
+
+ client->dev = dev;
+ client->of_node = dev->of_node;
+ memcpy(client->swgroups, swgroups, sizeof(u64));
+ return insert_smmu_client(smmu, client);
+}
-static int __smmu_client_set_hwgrp(struct smmu_client *c,
- unsigned long map, int on)
+static int smmu_of_get_swgroups(struct device *dev, unsigned long *swgroups)
+{
+ struct of_phandle_iter iter;
+
+ of_property_for_each_phandle_with_args(iter, dev->of_node, "iommus",
+ "iommu-cells", 0) {
+ if (iter.out_args.np != smmu_handle->iommu.dev->of_node)
+ continue;
+
+ BUG_ON(iter.out_args.args_count != 2);
+
+ memcpy(swgroups, iter.out_args.args, sizeof(u64));
+ pr_debug("swgroups=%08lx %08lx ops=%p %s\n",
+ swgroups[0], swgroups[1],
+ dev->bus->iommu_ops, dev_name(dev));
+ return 0;
+ }
+
+ return -ENODEV;
+}
+
+static int __smmu_client_set_swgroups(struct smmu_client *c,
+ unsigned long *map, int on)
{
int i;
struct smmu_as *as = c->as;
u32 val, offs, mask = SMMU_ASID_ENABLE(as->asid);
struct smmu_device *smmu = as->smmu;
- WARN_ON(!on && map);
- if (on && !map)
- return -EINVAL;
if (!on)
- map = smmu_client_hwgrp(c);
+ map = c->swgroups;
- for_each_set_bit(i, &map, HWGRP_COUNT) {
- offs = HWGRP_ASID_REG(i);
+ for_each_set_bit(i, map, TEGRA_SWGROUP_MAX) {
+ offs = smmu_get_asid_offset(i);
val = smmu_read(smmu, offs);
if (on) {
- if (WARN_ON(val & mask))
- goto err_hw_busy;
- val |= mask;
+ if (val) {
+ if (WARN_ON(val != mask))
+ return -EINVAL;
+ goto skip;
+ }
+
+ val = mask;
+ memcpy(c->swgroups, map, sizeof(u64));
} else {
WARN_ON((val & mask) == mask);
val &= ~mask;
}
smmu_write(smmu, val, offs);
}
+
FLUSH_SMMU_REGS(smmu);
- c->hwgrp = map;
+skip:
return 0;
-
-err_hw_busy:
- for_each_set_bit(i, &map, HWGRP_COUNT) {
- offs = HWGRP_ASID_REG(i);
- val = smmu_read(smmu, offs);
- val &= ~mask;
- smmu_write(smmu, val, offs);
- }
- return -EBUSY;
}
-static int smmu_client_set_hwgrp(struct smmu_client *c, u32 map, int on)
+static int smmu_client_set_swgroups(struct smmu_client *c,
+ unsigned long *map, int on)
{
- u32 val;
+ int err;
unsigned long flags;
struct smmu_as *as = c->as;
struct smmu_device *smmu = as->smmu;
spin_lock_irqsave(&smmu->lock, flags);
- val = __smmu_client_set_hwgrp(c, map, on);
+ err = __smmu_client_set_swgroups(c, map, on);
spin_unlock_irqrestore(&smmu->lock, flags);
- return val;
+ return err;
}
/*
@@ -470,19 +539,39 @@ static int smmu_setup_regs(struct smmu_device *smmu)
smmu_write(smmu, val, SMMU_PTB_DATA);
list_for_each_entry(c, &as->client, list)
- __smmu_client_set_hwgrp(c, c->hwgrp, 1);
+ __smmu_client_set_swgroups(c, c->swgroups, 1);
}
- smmu_write(smmu, smmu->translation_enable_0, SMMU_TRANSLATION_ENABLE_0);
- 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_CACHE_CONFIG(_TLB));
- smmu_write(smmu, SMMU_PTC_CONFIG_RESET_VAL, SMMU_CACHE_CONFIG(_PTC));
+ for (i = 0; i < smmu->nr_xlats; i++)
+ smmu_write(smmu, smmu->xlat[i],
+ SMMU_TRANSLATION_ENABLE_0 + i * sizeof(u32));
+
+ for (i = 0; i < smmu->nr_asid_secs; i++)
+ smmu_write(smmu, smmu->asid_sec[i], smmu_asid_sec_ofs[i]);
+
+ smmu_write(smmu, smmu->ptc_reset, SMMU_CACHE_CONFIG(_PTC));
+ smmu_write(smmu, smmu->tlb_reset, SMMU_CACHE_CONFIG(_TLB));
smmu_flush_regs(smmu, 1);
- return tegra_ahb_enable_smmu(smmu->ahb);
+ if (smmu->ahb)
+ return tegra_ahb_enable_smmu(smmu->ahb);
+
+ return 0;
+}
+
+static void flush_ptc_by_addr(struct smmu_device *smmu, unsigned long *pte,
+ struct page *page)
+{
+ u32 val;
+
+ val = VA_PAGE_TO_PA_HI(pte, page);
+ smmu_write(smmu, val, SMMU_PTC_FLUSH_1);
+
+ val = SMMU_PTC_FLUSH_TYPE_ADR | VA_PAGE_TO_PA(pte, page);
+ smmu_write(smmu, val, SMMU_PTC_FLUSH);
+
+ FLUSH_SMMU_REGS(smmu);
}
static void flush_ptc_and_tlb(struct smmu_device *smmu,
@@ -494,12 +583,11 @@ static void flush_ptc_and_tlb(struct smmu_device *smmu,
? SMMU_TLB_FLUSH_VA(iova, SECTION)
: SMMU_TLB_FLUSH_VA(iova, GROUP);
- val = SMMU_PTC_FLUSH_TYPE_ADR | VA_PAGE_TO_PA(pte, page);
- smmu_write(smmu, val, SMMU_PTC_FLUSH);
- FLUSH_SMMU_REGS(smmu);
+ flush_ptc_by_addr(smmu, pte, page);
+
val = tlb_flush_va |
SMMU_TLB_FLUSH_ASID_MATCH__ENABLE |
- (as->asid << SMMU_TLB_FLUSH_ASID_SHIFT);
+ (as->asid << SMMU_TLB_FLUSH_ASID_SHIFT(as));
smmu_write(smmu, val, SMMU_TLB_FLUSH);
FLUSH_SMMU_REGS(smmu);
}
@@ -510,7 +598,7 @@ static void free_ptbl(struct smmu_as *as, dma_addr_t iova)
unsigned long *pdir = (unsigned long *)page_address(as->pdir_page);
if (pdir[pdn] != _PDE_VACANT(pdn)) {
- dev_dbg(as->smmu->dev, "pdn: %lx\n", pdn);
+ dev_dbg(as->smmu->iommu.dev, "pdn: %lx\n", pdn);
ClearPageReserved(SMMU_EX_PTBL_PAGE(pdir[pdn]));
__free_page(SMMU_EX_PTBL_PAGE(pdir[pdn]));
@@ -525,7 +613,7 @@ static void free_pdir(struct smmu_as *as)
{
unsigned addr;
int count;
- struct device *dev = as->smmu->dev;
+ struct device *dev = as->smmu->iommu.dev;
if (!as->pdir_page)
return;
@@ -568,11 +656,11 @@ static unsigned long *locate_pte(struct smmu_as *as,
unsigned long addr = SMMU_PDN_TO_ADDR(pdn);
/* Vacant - allocate a new page table */
- dev_dbg(as->smmu->dev, "New PTBL pdn: %lx\n", pdn);
+ dev_dbg(as->smmu->iommu.dev, "New PTBL pdn: %lx\n", pdn);
*ptbl_page_p = alloc_page(GFP_ATOMIC);
if (!*ptbl_page_p) {
- dev_err(as->smmu->dev,
+ dev_err(as->smmu->iommu.dev,
"failed to allocate smmu_device page table\n");
return NULL;
}
@@ -632,7 +720,7 @@ static int alloc_pdir(struct smmu_as *as)
/*
* do the allocation, then grab as->lock
*/
- cnt = devm_kzalloc(smmu->dev,
+ cnt = devm_kzalloc(smmu->iommu.dev,
sizeof(cnt[0]) * SMMU_PDIR_COUNT,
GFP_KERNEL);
page = alloc_page(GFP_KERNEL | __GFP_DMA);
@@ -646,7 +734,8 @@ static int alloc_pdir(struct smmu_as *as)
}
if (!page || !cnt) {
- dev_err(smmu->dev, "failed to allocate at %s\n", __func__);
+ dev_err(smmu->iommu.dev,
+ "failed to allocate at %s\n", __func__);
err = -ENOMEM;
goto err_out;
}
@@ -660,12 +749,12 @@ static int alloc_pdir(struct smmu_as *as)
for (pdn = 0; pdn < SMMU_PDIR_COUNT; pdn++)
pdir[pdn] = _PDE_VACANT(pdn);
FLUSH_CPU_DCACHE(pdir, as->pdir_page, SMMU_PDIR_SIZE);
- val = SMMU_PTC_FLUSH_TYPE_ADR | VA_PAGE_TO_PA(pdir, as->pdir_page);
- smmu_write(smmu, val, SMMU_PTC_FLUSH);
- FLUSH_SMMU_REGS(as->smmu);
+
+ flush_ptc_by_addr(as->smmu, pdir, page);
+
val = SMMU_TLB_FLUSH_VA_MATCH_ALL |
SMMU_TLB_FLUSH_ASID_MATCH__ENABLE |
- (as->asid << SMMU_TLB_FLUSH_ASID_SHIFT);
+ (as->asid << SMMU_TLB_FLUSH_ASID_SHIFT(as));
smmu_write(smmu, val, SMMU_TLB_FLUSH);
FLUSH_SMMU_REGS(as->smmu);
@@ -676,7 +765,7 @@ static int alloc_pdir(struct smmu_as *as)
err_out:
spin_unlock_irqrestore(&as->lock, flags);
- devm_kfree(smmu->dev, cnt);
+ devm_kfree(smmu->iommu.dev, cnt);
if (page)
__free_page(page);
return err;
@@ -731,7 +820,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:%pa\n", as->asid, iova, &pa);
+ dev_dbg(as->smmu->iommu.dev, "[%d] %08lx:%pa\n", as->asid, iova, &pa);
if (!pfn_valid(pfn))
return -ENOMEM;
@@ -748,7 +837,7 @@ static size_t smmu_iommu_unmap(struct iommu_domain *domain, unsigned long iova,
struct smmu_as *as = domain->priv;
unsigned long flags;
- dev_dbg(as->smmu->dev, "[%d] %08lx\n", as->asid, iova);
+ dev_dbg(as->smmu->iommu.dev, "[%d] %08lx\n", as->asid, iova);
spin_lock_irqsave(&as->lock, flags);
__smmu_iommu_unmap(as, iova);
@@ -771,7 +860,7 @@ static phys_addr_t smmu_iommu_iova_to_phys(struct iommu_domain *domain,
pte = locate_pte(as, iova, true, &page, &count);
pfn = *pte & SMMU_PFN_MASK;
WARN_ON(!pfn_valid(pfn));
- dev_dbg(as->smmu->dev,
+ dev_dbg(as->smmu->iommu.dev,
"iova:%08llx pfn:%08lx asid:%d\n", (unsigned long long)iova,
pfn, as->asid);
@@ -791,26 +880,21 @@ static int smmu_iommu_attach_dev(struct iommu_domain *domain,
struct smmu_as *as = domain->priv;
struct smmu_device *smmu = as->smmu;
struct smmu_client *client, *c;
- u32 map;
int err;
- client = devm_kzalloc(smmu->dev, sizeof(*c), GFP_KERNEL);
+ client = find_smmu_client(smmu, dev->of_node);
if (!client)
return -ENOMEM;
- client->dev = dev;
- client->as = as;
- map = (unsigned long)dev->platform_data;
- if (!map)
- return -EINVAL;
- err = smmu_client_enable_hwgrp(client, map);
+ client->as = as;
+ err = smmu_client_enable_swgroups(client, client->swgroups);
if (err)
- goto err_hwgrp;
+ return -EINVAL;
spin_lock(&as->client_lock);
list_for_each_entry(c, &as->client, list) {
if (c->dev == dev) {
- dev_err(smmu->dev,
+ dev_err(smmu->iommu.dev,
"%s is already attached\n", dev_name(c->dev));
err = -EINVAL;
goto err_client;
@@ -823,7 +907,7 @@ static int smmu_iommu_attach_dev(struct iommu_domain *domain,
* Reserve "page zero" for AVP vectors using a common dummy
* page.
*/
- if (map & HWG_AVPC) {
+ if (test_bit(TEGRA_SWGROUP_AVPC, client->swgroups)) {
struct page *page;
page = as->smmu->avp_vector_page;
@@ -832,14 +916,12 @@ static int smmu_iommu_attach_dev(struct iommu_domain *domain,
pr_info("Reserve \"page zero\" for AVP vectors using a common dummy\n");
}
- dev_dbg(smmu->dev, "%s is attached\n", dev_name(dev));
+ dev_dbg(smmu->iommu.dev, "%s is attached\n", dev_name(dev));
return 0;
err_client:
- smmu_client_disable_hwgrp(client);
+ smmu_client_disable_swgroups(client);
spin_unlock(&as->client_lock);
-err_hwgrp:
- devm_kfree(smmu->dev, client);
return err;
}
@@ -854,16 +936,15 @@ static void smmu_iommu_detach_dev(struct iommu_domain *domain,
list_for_each_entry(c, &as->client, list) {
if (c->dev == dev) {
- smmu_client_disable_hwgrp(c);
+ smmu_client_disable_swgroups(c);
list_del(&c->list);
- devm_kfree(smmu->dev, c);
c->as = NULL;
- dev_dbg(smmu->dev,
+ dev_dbg(smmu->iommu.dev,
"%s is detached\n", dev_name(c->dev));
goto out;
}
}
- dev_err(smmu->dev, "Couldn't find %s\n", dev_name(dev));
+ dev_err(smmu->iommu.dev, "Couldn't find %s\n", dev_name(dev));
out:
spin_unlock(&as->client_lock);
}
@@ -890,7 +971,7 @@ static int smmu_iommu_domain_init(struct iommu_domain *domain)
break;
}
if (i == smmu->num_as)
- dev_err(smmu->dev, "no free AS\n");
+ dev_err(smmu->iommu.dev, "no free AS\n");
return err;
found:
@@ -911,7 +992,7 @@ found:
smmu->page_count * SMMU_PAGE_SIZE - 1;
domain->geometry.force_aperture = true;
- dev_dbg(smmu->dev, "smmu_as@%p\n", as);
+ dev_dbg(smmu->iommu.dev, "smmu_as@%p\n", as);
return 0;
}
@@ -944,7 +1025,58 @@ static void smmu_iommu_domain_destroy(struct iommu_domain *domain)
spin_unlock_irqrestore(&as->lock, flags);
domain->priv = NULL;
- dev_dbg(smmu->dev, "smmu_as@%p\n", as);
+ dev_dbg(smmu->iommu.dev, "smmu_as@%p\n", as);
+}
+
+/*
+ * ASID[0] for the system default
+ * ASID[1] for PPCS("AHB bus children"), which has SDMMC
+ * ASID[2][3].. open for drivers, first come, first served.
+ */
+enum {
+ SYSTEM_DEFAULT,
+ SYSTEM_PROTECTED,
+ NUM_OF_STATIC_MAPS,
+};
+
+static int smmu_iommu_bound_driver(struct device *dev)
+{
+ int err;
+ unsigned long swgroups[2];
+ struct dma_iommu_mapping *map = NULL;
+
+ err = smmu_of_get_swgroups(dev, swgroups);
+ if (err)
+ return -ENODEV;
+
+ if (!find_smmu_client(smmu_handle, dev->of_node)) {
+ err = register_smmu_client(smmu_handle, dev, swgroups);
+ if (err) {
+ dev_err(dev, "failed to add client %s\n",
+ dev_name(dev));
+ return -EINVAL;
+ }
+ }
+
+ if (test_bit(TEGRA_SWGROUP_PPCS, swgroups))
+ map = smmu_handle->map[SYSTEM_PROTECTED];
+ else
+ map = smmu_handle->map[SYSTEM_DEFAULT];
+
+ if (map)
+ err = arm_iommu_attach_device(dev, map);
+ else
+ return -ENODEV;
+
+ pr_debug("swgroups=%08lx %08lx map=%p err=%d %s\n",
+ swgroups[0], swgroups[1], map, err, dev_name(dev));
+ return err;
+}
+
+static void smmu_iommu_unbind_driver(struct device *dev)
+{
+ dev_dbg(dev, "Detaching from map %p\n", to_dma_iommu_mapping(dev));
+ arm_iommu_detach_device(dev);
}
static struct iommu_ops smmu_iommu_ops = {
@@ -956,6 +1088,8 @@ static struct iommu_ops smmu_iommu_ops = {
.unmap = smmu_iommu_unmap,
.iova_to_phys = smmu_iommu_iova_to_phys,
.domain_has_cap = smmu_iommu_domain_has_cap,
+ .bound_driver = smmu_iommu_bound_driver,
+ .unbind_driver = smmu_iommu_unbind_driver,
.pgsize_bitmap = SMMU_IOMMU_PGSIZES,
};
@@ -1023,7 +1157,7 @@ static ssize_t smmu_debugfs_stats_write(struct file *file,
break;
}
- dev_dbg(smmu->dev, "%s() %08x, %08x @%08x\n", __func__,
+ dev_dbg(smmu->iommu.dev, "%s() %08x, %08x @%08x\n", __func__,
val, smmu_read(smmu, offs), offs);
return count;
@@ -1045,7 +1179,7 @@ static int smmu_debugfs_stats_show(struct seq_file *s, void *v)
val = smmu_read(smmu, offs);
seq_printf(s, "%s:%08x ", stats[i], val);
- dev_dbg(smmu->dev, "%s() %s %08x @%08x\n", __func__,
+ dev_dbg(smmu->iommu.dev, "%s() %s %08x @%08x\n", __func__,
stats[i], val, offs);
}
seq_printf(s, "\n");
@@ -1083,7 +1217,7 @@ static void smmu_debugfs_create(struct smmu_device *smmu)
if (!smmu->debugfs_info)
return;
- root = debugfs_create_dir(dev_name(smmu->dev), NULL);
+ root = debugfs_create_dir(dev_name(smmu->iommu.dev), NULL);
if (!root)
goto err_out;
smmu->debugfs_root = root;
@@ -1123,12 +1257,16 @@ err_out:
static int tegra_smmu_suspend(struct device *dev)
{
+ int i;
struct smmu_device *smmu = dev_get_drvdata(dev);
- smmu->translation_enable_0 = smmu_read(smmu, SMMU_TRANSLATION_ENABLE_0);
- smmu->translation_enable_1 = smmu_read(smmu, SMMU_TRANSLATION_ENABLE_1);
- smmu->translation_enable_2 = smmu_read(smmu, SMMU_TRANSLATION_ENABLE_2);
- smmu->asid_security = smmu_read(smmu, SMMU_ASID_SECURITY);
+ for (i = 0; i < smmu->nr_xlats; i++)
+ smmu->xlat[i] = smmu_read(smmu,
+ SMMU_TRANSLATION_ENABLE_0 + i * sizeof(u32));
+
+ for (i = 0; i < smmu->nr_asid_secs; i++)
+ smmu->asid_sec[i] =
+ smmu_read(smmu, smmu_asid_sec_ofs[i]);
return 0;
}
@@ -1144,6 +1282,39 @@ static int tegra_smmu_resume(struct device *dev)
return err;
}
+static void tegra_smmu_create_default_map(struct smmu_device *smmu)
+{
+ int i;
+
+ for (i = 0; i < smmu->num_as; i++) {
+ dma_addr_t base = smmu->iovmm_base;
+ size_t size = smmu->page_count << PAGE_SHIFT;
+
+ smmu->map[i] = arm_iommu_create_mapping(&platform_bus_type,
+ base, size);
+ if (IS_ERR(smmu->map[i]))
+ dev_err(smmu->iommu.dev,
+ "Couldn't create: asid=%d map=%p %pa-%pa\n",
+ i, smmu->map[i], &base, &base + size - 1);
+ }
+}
+
+static const struct smmu_platform_data tegra124_smmu_pdata = {
+ .asids = 128,
+ .nr_xlats = 4,
+ .lpae = true,
+ .nr_asid_secs = 8,
+ .tlb_reset = SMMU_TLB_CONFIG_RESET_VAL | SMMU_TLB_RR_ARB | 0x20,
+ .ptc_reset = SMMU_PTC_CONFIG_RESET_VAL | SMMU_PTC_REQ_LIMIT,
+};
+
+static struct of_device_id tegra_smmu_of_match[] = {
+ { .compatible = "nvidia,tegra124-smmu", .data = &tegra124_smmu_pdata, },
+ { .compatible = "nvidia,tegra30-smmu", },
+ { },
+};
+MODULE_DEVICE_TABLE(of, tegra_smmu_of_match);
+
static int tegra_smmu_probe(struct platform_device *pdev)
{
struct smmu_device *smmu;
@@ -1151,23 +1322,45 @@ static int tegra_smmu_probe(struct platform_device *pdev)
int i, asids, err = 0;
dma_addr_t uninitialized_var(base);
size_t bytes, uninitialized_var(size);
+ const struct of_device_id *match;
+ const struct smmu_platform_data *pdata;
+ int nr_xlats, nr_asid_secs;
if (smmu_handle)
return -EIO;
BUILD_BUG_ON(PAGE_SHIFT != SMMU_PAGE_SHIFT);
+ match = of_match_device(tegra_smmu_of_match, &pdev->dev);
+ if (!match)
+ return -EINVAL;
+ pdata = match->data;
+ nr_xlats = (pdata && pdata->nr_xlats) ? pdata->nr_xlats : 3;
+ nr_asid_secs = (pdata && pdata->nr_asid_secs) ? pdata->nr_asid_secs : 1;
+
if (of_property_read_u32(dev->of_node, "nvidia,#asids", &asids))
- return -ENODEV;
+ asids = (pdata && pdata->asids) ? pdata->asids : 4;
+ if (asids < NUM_OF_STATIC_MAPS)
+ return -EINVAL;
- bytes = sizeof(*smmu) + asids * sizeof(*smmu->as);
+ bytes = sizeof(*smmu) + asids * (sizeof(*smmu->as) +
+ sizeof(struct dma_iommu_mapping *));
+ bytes += sizeof(u32) * (nr_asid_secs + nr_xlats);
smmu = devm_kzalloc(dev, bytes, GFP_KERNEL);
if (!smmu) {
dev_err(dev, "failed to allocate smmu_device\n");
return -ENOMEM;
}
+ smmu->clients = RB_ROOT;
+ smmu->map = (struct dma_iommu_mapping **)(smmu->as + asids);
+ smmu->xlat = (u32 *)(smmu->map + smmu->num_as);
+ smmu->asid_sec = smmu->xlat + smmu->nr_xlats;
smmu->nregs = pdev->num_resources;
+ smmu->tlb_reset = (pdata && pdata->tlb_reset) ? pdata->tlb_reset :
+ (SMMU_TLB_CONFIG_RESET_VAL | 0x10);
+ smmu->ptc_reset = (pdata && pdata->ptc_reset) ? pdata->ptc_reset :
+ (SMMU_PTC_CONFIG_RESET_VAL | SMMU_PTC_REQ_LIMIT);
smmu->regs = devm_kzalloc(dev, 2 * smmu->nregs * sizeof(*smmu->regs),
GFP_KERNEL);
smmu->rege = smmu->regs + smmu->nregs;
@@ -1197,18 +1390,14 @@ static int tegra_smmu_probe(struct platform_device *pdev)
return -EINVAL;
smmu->ahb = of_parse_phandle(dev->of_node, "nvidia,ahb", 0);
- if (!smmu->ahb)
- return -ENODEV;
-
- smmu->dev = dev;
+ smmu->iommu.dev = dev;
smmu->num_as = asids;
+ smmu->nr_xlats = nr_xlats;
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->nr_asid_secs = nr_asid_secs;
+ for (i = 0; i < smmu->nr_xlats; i++)
+ smmu->xlat[i] = ~0;
for (i = 0; i < smmu->num_as; i++) {
struct smmu_as *as = &smmu->as[i];
@@ -1236,6 +1425,9 @@ static int tegra_smmu_probe(struct platform_device *pdev)
smmu_debugfs_create(smmu);
smmu_handle = smmu;
bus_set_iommu(&platform_bus_type, &smmu_iommu_ops);
+ tegra_smmu_create_default_map(smmu);
+
+ iommu_add(&smmu->iommu);
return 0;
}
@@ -1251,6 +1443,7 @@ static int tegra_smmu_remove(struct platform_device *pdev)
free_pdir(&smmu->as[i]);
__free_page(smmu->avp_vector_page);
smmu_handle = NULL;
+ iommu_del(&smmu->iommu);
return 0;
}
@@ -1259,12 +1452,6 @@ static const struct dev_pm_ops tegra_smmu_pm_ops = {
.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,
diff --git a/drivers/of/base.c b/drivers/of/base.c
index 03e7fc6c93e..9c6834794fd 100644
--- a/drivers/of/base.c
+++ b/drivers/of/base.c
@@ -1492,6 +1492,56 @@ void of_print_phandle_args(const char *msg, const struct of_phandle_args *args)
printk("\n");
}
+void of_phandle_iter_next(struct of_phandle_iter *iter)
+{
+ struct device_node *dn;
+ int i, count;
+
+ if (!iter->cur || (iter->cur >= iter->end))
+ goto err_out;
+
+ dn = of_find_node_by_phandle(be32_to_cpup(iter->cur++));
+ if (!dn)
+ goto err_out;
+
+ if (iter->cells_name) {
+ if (of_property_read_u32(dn, iter->cells_name, &count))
+ goto err_out;
+ } else {
+ count = iter->cell_count;
+ }
+
+ iter->out_args.np = dn;
+ iter->out_args.args_count = count;
+ for (i = 0; i < count; i++)
+ iter->out_args.args[i] = be32_to_cpup(iter->cur++);
+
+ return;
+
+err_out:
+ iter->cur = NULL;
+}
+EXPORT_SYMBOL_GPL(of_phandle_iter_next);
+
+void of_phandle_iter_start(struct of_phandle_iter *iter,
+ const struct device_node *np,
+ const char *list_name, const char *cells_name,
+ int cell_count)
+{
+ size_t bytes;
+
+ iter->cur = of_get_property(np, list_name, &bytes);
+ if (!iter->cur)
+ return;
+ iter->end = iter->cur;
+ if (bytes)
+ iter->end += bytes / sizeof(*iter->cur);
+ iter->cells_name = cells_name;
+ iter->cell_count = cell_count;
+ of_phandle_iter_next(iter);
+}
+EXPORT_SYMBOL_GPL(of_phandle_iter_start);
+
static int __of_parse_phandle_with_args(const struct device_node *np,
const char *list_name,
const char *cells_name,
diff --git a/include/dt-bindings/memory/tegra-swgroup.h b/include/dt-bindings/memory/tegra-swgroup.h
new file mode 100644
index 00000000000..32dd307e5f8
--- /dev/null
+++ b/include/dt-bindings/memory/tegra-swgroup.h
@@ -0,0 +1,50 @@
+/*
+ * This header provides constants for binding nvidia,swgroup ID
+ */
+
+#ifndef _DT_BINDINGS_MEMORY_TEGRA_SWGROUP_H
+#define _DT_BINDINGS_MEMORY_TEGRA_SWGROUP_H
+
+#define TEGRA_SWGROUP_AFI 0 /* 0x238 */
+#define TEGRA_SWGROUP_AVPC 1 /* 0x23c */
+#define TEGRA_SWGROUP_DC 2 /* 0x240 */
+#define TEGRA_SWGROUP_DCB 3 /* 0x244 */
+#define TEGRA_SWGROUP_EPP 4 /* 0x248 */
+#define TEGRA_SWGROUP_G2 5 /* 0x24c */
+#define TEGRA_SWGROUP_HC 6 /* 0x250 */
+#define TEGRA_SWGROUP_HDA 7 /* 0x254 */
+#define TEGRA_SWGROUP_ISP 8 /* 0x258 */
+#define TEGRA_SWGROUP_ISP2 8
+#define TEGRA_SWGROUP_DC14 9 /* 0x490 *//* Exceptional non-linear */
+#define TEGRA_SWGROUP_DC12 10 /* 0xa88 *//* Exceptional non-linear */
+#define TEGRA_SWGROUP_MPE 11 /* 0x264 */
+#define TEGRA_SWGROUP_MSENC 11
+#define TEGRA_SWGROUP_NV 12 /* 0x268 */
+#define TEGRA_SWGROUP_NV2 13 /* 0x26c */
+#define TEGRA_SWGROUP_PPCS 14 /* 0x270 */
+#define TEGRA_SWGROUP_SATA2 15 /* 0x274 */
+#define TEGRA_SWGROUP_SATA 16 /* 0x278 */
+#define TEGRA_SWGROUP_VDE 17 /* 0x27c */
+#define TEGRA_SWGROUP_VI 18 /* 0x280 */
+#define TEGRA_SWGROUP_VIC 19 /* 0x284 */
+#define TEGRA_SWGROUP_XUSB_HOST 20 /* 0x288 */
+#define TEGRA_SWGROUP_XUSB_DEV 21 /* 0x28c */
+#define TEGRA_SWGROUP_A9AVP 22 /* 0x290 */
+#define TEGRA_SWGROUP_TSEC 23 /* 0x294 */
+#define TEGRA_SWGROUP_PPCS1 24 /* 0x298 */
+#define TEGRA_SWGROUP_SDMMC1A 25 /* 0xa94 *//* Linear shift again */
+#define TEGRA_SWGROUP_SDMMC2A 26 /* 0xa98 */
+#define TEGRA_SWGROUP_SDMMC3A 27 /* 0xa9c */
+#define TEGRA_SWGROUP_SDMMC4A 28 /* 0xaa0 */
+#define TEGRA_SWGROUP_ISP2B 29 /* 0xaa4 */
+#define TEGRA_SWGROUP_GPU 30 /* 0xaa8 */
+#define TEGRA_SWGROUP_GPUB 31 /* 0xaac */
+#define TEGRA_SWGROUP_PPCS2 32 /* 0xab0 */
+
+#define TWO_U32_OF_U64(x) ((x) & 0xffffffff) ((x) >> 32)
+#define TEGRA_SWGROUP_BIT(x) (1ULL << TEGRA_SWGROUP_##x)
+#define TEGRA_SWGROUP_CELLS(x) TWO_U32_OF_U64(TEGRA_SWGROUP_BIT(x))
+
+#define TEGRA_SWGROUP_MAX 64
+
+#endif /* _DT_BINDINGS_MEMORY_TEGRA_SWGROUP_H */
diff --git a/include/linux/iommu.h b/include/linux/iommu.h
index b96a5b2136e..141eea25bde 100644
--- a/include/linux/iommu.h
+++ b/include/linux/iommu.h
@@ -97,6 +97,8 @@ enum iommu_attr {
* @domain_has_cap: domain capabilities query
* @add_device: add device to iommu grouping
* @remove_device: remove device from iommu grouping
+ * @bound_driver: called at BUS_NOTIFY_BOUND_DRIVER
+ * @unbind_driver: called at BUS_NOTIFY_UNBIND_DRIVER
* @domain_get_attr: Query domain attributes
* @domain_set_attr: Change domain attributes
* @pgsize_bitmap: bitmap of supported page sizes
@@ -115,6 +117,8 @@ struct iommu_ops {
unsigned long cap);
int (*add_device)(struct device *dev);
void (*remove_device)(struct device *dev);
+ int (*bound_driver)(struct device *dev);
+ void (*unbind_driver)(struct device *dev);
int (*device_group)(struct device *dev, unsigned int *groupid);
int (*domain_get_attr)(struct iommu_domain *domain,
enum iommu_attr attr, void *data);
diff --git a/include/linux/of.h b/include/linux/of.h
index 196b34c1ef4..76096d9de60 100644
--- a/include/linux/of.h
+++ b/include/linux/of.h
@@ -74,6 +74,18 @@ struct of_phandle_args {
uint32_t args[MAX_PHANDLE_ARGS];
};
+/*
+ * keep the state at iterating a list of phandles with variable number
+ * of args
+ */
+struct of_phandle_iter {
+ const __be32 *cur; /* current phandle */
+ const __be32 *end; /* end of the last phandle */
+ const char *cells_name;
+ int cell_count;
+ struct of_phandle_args out_args;
+};
+
extern int of_node_add(struct device_node *node);
/* initialize a node */
@@ -303,6 +315,12 @@ extern int of_parse_phandle_with_fixed_args(const struct device_node *np,
extern int of_count_phandle_with_args(const struct device_node *np,
const char *list_name, const char *cells_name);
+extern void of_phandle_iter_start(struct of_phandle_iter *iter,
+ const struct device_node *np,
+ const char *list_name,
+ const char *cells_name, int cell_count);
+extern void of_phandle_iter_next(struct of_phandle_iter *iter);
+
extern void of_alias_scan(void * (*dt_alloc)(u64 size, u64 align));
extern int of_alias_get_id(struct device_node *np, const char *stem);
@@ -554,6 +572,18 @@ static inline int of_count_phandle_with_args(struct device_node *np,
return -ENOSYS;
}
+static inline void of_phandle_iter_start(struct of_phandle_iter *iter,
+ const struct device_node *np,
+ const char *list_name,
+ const char *cells_name,
+ int cell_count);
+{
+}
+
+static inline void of_phandle_iter_next(struct of_phandle_iter *iter)
+{
+}
+
static inline int of_alias_get_id(struct device_node *np, const char *stem)
{
return -ENOSYS;
@@ -742,6 +772,12 @@ static inline int of_property_read_u32(const struct device_node *np,
for (dn = of_find_node_with_property(NULL, prop_name); dn; \
dn = of_find_node_with_property(dn, prop_name))
+#define of_property_for_each_phandle_with_args(iter, np, list_name, \
+ cells_name, cell_count) \
+ for (of_phandle_iter_start(&iter, np, list_name, \
+ cells_name, cell_count); \
+ iter.cur; of_phandle_iter_next(&iter))
+
static inline int of_get_child_count(const struct device_node *np)
{
struct device_node *child;
diff --git a/include/linux/of_iommu.h b/include/linux/of_iommu.h
index 51a560f34bc..0e2f5681b45 100644
--- a/include/linux/of_iommu.h
+++ b/include/linux/of_iommu.h
@@ -3,10 +3,19 @@
#ifdef CONFIG_OF_IOMMU
+struct iommu {
+ struct list_head list;
+ struct device *dev;
+};
+
extern int of_get_dma_window(struct device_node *dn, const char *prefix,
int index, unsigned long *busno, dma_addr_t *addr,
size_t *size);
+void iommu_add(struct iommu *iommu);
+void iommu_del(struct iommu *iommu);
+int of_iommu_attach(struct device *dev);
+
#else
static inline int of_get_dma_window(struct device_node *dn, const char *prefix,
@@ -16,6 +25,19 @@ static inline int of_get_dma_window(struct device_node *dn, const char *prefix,
return -EINVAL;
}
+static inline void iommu_add(struct iommu *iommu)
+{
+}
+
+static inline void iommu_del(struct iommu *iommu)
+{
+}
+
+static inline int of_iommu_attach(struct device *dev)
+{
+ return 0;
+}
+
#endif /* CONFIG_OF_IOMMU */
#endif /* __OF_IOMMU_H */