diff options
author | Thierry Reding <treding@nvidia.com> | 2014-06-04 12:09:56 +0200 |
---|---|---|
committer | Thierry Reding <treding@nvidia.com> | 2014-06-04 12:09:56 +0200 |
commit | 571010715abc1a007b7f2c842fab2638a2688c20 (patch) | |
tree | f3a12e9df14d9da9223c4612e33d7c5a7b77c204 | |
parent | 3e6434eab2165f0bc9ba882d6d3ca8686d195bcd (diff) | |
parent | 4e444487783cb012ec7754ed0dd36aa4e1d53405 (diff) |
Merge branch 'staging/iommu' into staging/master
-rw-r--r-- | Documentation/devicetree/bindings/iommu/nvidia,tegra30-smmu.txt | 4 | ||||
-rw-r--r-- | arch/arm/boot/dts/tegra124.dtsi | 19 | ||||
-rw-r--r-- | drivers/base/dd.c | 5 | ||||
-rw-r--r-- | drivers/iommu/Kconfig | 1 | ||||
-rw-r--r-- | drivers/iommu/iommu.c | 13 | ||||
-rw-r--r-- | drivers/iommu/of_iommu.c | 49 | ||||
-rw-r--r-- | drivers/iommu/tegra-smmu.c | 567 | ||||
-rw-r--r-- | drivers/of/base.c | 50 | ||||
-rw-r--r-- | include/dt-bindings/memory/tegra-swgroup.h | 50 | ||||
-rw-r--r-- | include/linux/iommu.h | 4 | ||||
-rw-r--r-- | include/linux/of.h | 36 | ||||
-rw-r--r-- | include/linux/of_iommu.h | 22 |
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 */ |