def_bool y
depends on MSM_IOMMU && MMU && SMP && CPU_DCACHE_DISABLE=n
+source "drivers/iommu/qcom/Kconfig"
+
# AMD IOMMU support
config AMD_IOMMU
bool "AMD IOMMU support"
obj-$(CONFIG_IOMMU_IOVA) += iova.o
obj-$(CONFIG_OF_IOMMU) += of_iommu.o
obj-$(CONFIG_MSM_IOMMU) += msm_iommu.o msm_iommu_dev.o
+obj-$(CONFIG_QCOM_IOMMU_V1) += qcom/
obj-$(CONFIG_AMD_IOMMU) += amd_iommu.o amd_iommu_init.o
obj-$(CONFIG_AMD_IOMMU_V2) += amd_iommu_v2.o
obj-$(CONFIG_ARM_SMMU) += arm-smmu.o
# Qualcomm IOMMU support
# QCOM IOMMUv1 support
-config QCOM_IOMMU
+config QCOM_IOMMU_V1
bool "Qualcomm IOMMUv1 Support"
depends on ARCH_QCOM
select IOMMU_API
config MMU500_ACTIVE_PREFETCH_BUG_WITH_SECTION_MAPPING
bool "Don't align virtual address at 1MB boundary"
- depends on QCOM_IOMMU
+ depends on QCOM_IOMMU_V1
help
Say Y here if the MMU500 revision has a bug in active prefetch
which can cause TLB corruptions due to 1MB alignment of a buffer.
config IOMMU_PGTABLES_L2
bool "Allow SMMU page tables in the L2 cache (Experimental)"
- depends on QCOM_IOMMU && MMU && SMP && CPU_DCACHE_DISABLE=n
+ depends on QCOM_IOMMU_V1 && MMU && SMP && CPU_DCACHE_DISABLE=n
help
Improves TLB miss latency at the expense of potential L2 pollution.
However, with large multimedia buffers, the TLB should mostly contain
-obj-$(CONFIG_QCOM_IOMMU) += qcom-iommu.o
+obj-$(CONFIG_QCOM_IOMMU_V1) += qcom-iommu.o
qcom-iommu-y += msm_iommu.o
-qcom-iommu-y += msm_iommu_domains.o
qcom-iommu-y += msm_iommu-v1.o
qcom-iommu-y += msm_iommu_dev-v1.o
qcom-iommu-y += msm_iommu_sec.o
qcom-iommu-y += msm_iommu_pagetable.o
-#qcom-iommu-y += msm_iommu_mapping.o
ctx_drvdata->secure_context = 0;
}
#else
-static inline int is_vfe_smmu(char const *iommu_name)
-{
- return (strcmp(iommu_name, "vfe_iommu") == 0);
-}
-
static void get_secure_id(struct device_node *node,
struct msm_iommu_drvdata *drvdata)
{
- if (msm_iommu_get_scm_call_avail()) {
- if (!is_vfe_smmu(drvdata->name) || is_vfe_secure())
- of_property_read_u32(node, "qcom,iommu-secure-id",
- &drvdata->sec_id);
- else
- pr_info("vfe_iommu: Keeping vfe non-secure\n");
- }
+ if (msm_iommu_get_scm_call_avail())
+ of_property_read_u32(node, "qcom,iommu-secure-id",
+ &drvdata->sec_id);
}
static void get_secure_ctx(struct device_node *node,
{
u32 secure_ctx = 0;
- if (msm_iommu_get_scm_call_avail()) {
- if (!is_vfe_smmu(iommu_drvdata->name) || is_vfe_secure()) {
- secure_ctx =
- of_property_read_bool(node, "qcom,secure-context");
- }
- }
+ if (msm_iommu_get_scm_call_avail())
+ secure_ctx = of_property_read_bool(node, "qcom,secure-context");
+
ctx_drvdata->secure_context = secure_ctx;
}
#endif
+++ /dev/null
-/* Copyright (c) 2010-2015, The Linux Foundation. All rights reserved.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 and
- * only version 2 as published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- */
-
-#include <linux/module.h>
-#include <linux/init.h>
-#include <linux/iommu.h>
-#include <linux/platform_device.h>
-#include <linux/rbtree.h>
-#include <linux/slab.h>
-#include <linux/vmalloc.h>
-#include <linux/of.h>
-#include <linux/of_address.h>
-#include <linux/of_device.h>
-#include <linux/idr.h>
-#include <asm/sizes.h>
-#include <asm/page.h>
-#include <linux/qcom_iommu.h>
-#include <linux/msm_iommu_domains.h>
-#include <soc/qcom/socinfo.h>
-
-#include "msm_iommu_priv.h"
-
-struct msm_iova_data {
- struct rb_node node;
- struct mem_pool *pools;
- int npools;
- struct iommu_domain *domain;
- int domain_num;
-};
-
-struct msm_iommu_data_entry {
- struct list_head list;
- void *data;
-};
-
-static struct rb_root domain_root;
-static DEFINE_MUTEX(domain_mutex);
-static DEFINE_IDA(domain_nums);
-
-void msm_iommu_set_client_name(struct iommu_domain *domain, char const *name)
-{
- struct msm_iommu_priv *priv = domain->priv;
-
- priv->client_name = name;
-}
-
-int msm_use_iommu(void)
-{
- return iommu_present(&platform_bus_type);
-}
-
-bool msm_iommu_page_size_is_supported(unsigned long page_size)
-{
- return page_size == SZ_4K || page_size == SZ_64K ||
- page_size == SZ_1M || page_size == SZ_16M;
-}
-
-static int msm_iommu_map_iova_phys(struct iommu_domain *domain,
- unsigned long iova,
- phys_addr_t phys,
- unsigned long size,
- int cached)
-{
- int ret;
- struct scatterlist *sglist;
- int prot = IOMMU_WRITE | IOMMU_READ;
- size_t mapped;
-
- prot |= cached ? IOMMU_CACHE : 0;
-
- sglist = vmalloc(sizeof(*sglist));
- if (!sglist) {
- ret = -ENOMEM;
- goto err1;
- }
-
- sg_init_table(sglist, 1);
- sglist->length = size;
- sglist->offset = 0;
- sglist->dma_address = phys;
-
-/* ret = iommu_map_range(domain, iova, sglist, size, prot);*/
- mapped = iommu_map_sg(domain, iova, sglist, size, prot);
- if (mapped <= 0) {
- ret = -EFAULT;
- pr_err("%s: could not map extra %lx in domain %p\n",
- __func__, iova, domain);
- }
-
- vfree(sglist);
-err1:
- return ret;
-
-}
-
-int msm_iommu_map_contig_buffer(phys_addr_t phys,
- unsigned int domain_no,
- unsigned int partition_no,
- unsigned long size,
- unsigned long align,
- unsigned long cached,
- dma_addr_t *iova_val)
-{
- unsigned long iova;
- int ret;
- struct iommu_domain *domain;
-
- if (size & (align - 1))
- return -EINVAL;
-
- if (!msm_use_iommu()) {
- *iova_val = phys;
- return 0;
- }
-
- ret = msm_allocate_iova_address(domain_no, partition_no, size, align,
- &iova);
-
- if (ret)
- return -ENOMEM;
-
- domain = msm_get_iommu_domain(domain_no);
- if (!domain) {
- pr_err("%s: Could not find domain %u. Unable to map\n",
- __func__, domain_no);
- msm_free_iova_address(iova, domain_no, partition_no, size);
- return -EINVAL;
- }
-
- ret = msm_iommu_map_iova_phys(domain, iova, phys, size, cached);
- if (ret)
- msm_free_iova_address(iova, domain_no, partition_no, size);
- else
- *iova_val = iova;
-
- return ret;
-}
-EXPORT_SYMBOL(msm_iommu_map_contig_buffer);
-
-void msm_iommu_unmap_contig_buffer(dma_addr_t iova,
- unsigned int domain_no,
- unsigned int partition_no,
- unsigned long size)
-{
- struct iommu_domain *domain;
-
- if (!msm_use_iommu())
- return;
-
- domain = msm_get_iommu_domain(domain_no);
- if (domain) {
-/* iommu_unmap_range(domain, iova, size);*/
- iommu_unmap(domain, iova, size);
- } else {
- pr_err("%s: Could not find domain %u. Unable to unmap\n",
- __func__, domain_no);
- }
-
- msm_free_iova_address(iova, domain_no, partition_no, size);
-}
-EXPORT_SYMBOL(msm_iommu_unmap_contig_buffer);
-
-static struct msm_iova_data *find_domain(int domain_num)
-{
- struct rb_root *root = &domain_root;
- struct rb_node *p;
-
- mutex_lock(&domain_mutex);
- p = root->rb_node;
- while (p) {
- struct msm_iova_data *node;
-
- node = rb_entry(p, struct msm_iova_data, node);
- if (domain_num < node->domain_num)
- p = p->rb_left;
- else if (domain_num > node->domain_num)
- p = p->rb_right;
- else {
- mutex_unlock(&domain_mutex);
- return node;
- }
- }
- mutex_unlock(&domain_mutex);
-
- return NULL;
-}
-
-static int add_domain(struct msm_iova_data *node)
-{
- struct rb_root *root = &domain_root;
- struct rb_node **p = &root->rb_node;
- struct rb_node *parent = NULL;
-
- mutex_lock(&domain_mutex);
- while (*p) {
- struct msm_iova_data *tmp;
- parent = *p;
-
- tmp = rb_entry(parent, struct msm_iova_data, node);
-
- if (node->domain_num < tmp->domain_num)
- p = &(*p)->rb_left;
- else if (node->domain_num > tmp->domain_num)
- p = &(*p)->rb_right;
- else
- BUG();
- }
- rb_link_node(&node->node, parent, p);
- rb_insert_color(&node->node, root);
- mutex_unlock(&domain_mutex);
-
- return 0;
-}
-
-static int remove_domain(struct iommu_domain *domain)
-{
- struct rb_root *root = &domain_root;
- struct rb_node *n;
- struct msm_iova_data *node;
- int ret = -EINVAL;
-
- mutex_lock(&domain_mutex);
-
- for (n = rb_first(root); n; n = rb_next(n)) {
- node = rb_entry(n, struct msm_iova_data, node);
- if (node->domain == domain) {
- rb_erase(&node->node, &domain_root);
- ret = 0;
- break;
- }
- }
- mutex_unlock(&domain_mutex);
-
- return ret;
-}
-
-struct iommu_domain *msm_get_iommu_domain(int domain_num)
-{
- struct msm_iova_data *data;
-
- data = find_domain(domain_num);
- if (data)
- return data->domain;
-
- return NULL;
-}
-EXPORT_SYMBOL(msm_get_iommu_domain);
-
-static struct msm_iova_data *msm_domain_to_iova_data(struct iommu_domain
- const *domain)
-{
- struct rb_root *root = &domain_root;
- struct rb_node *n;
- struct msm_iova_data *node;
- struct msm_iova_data *iova_data = ERR_PTR(-EINVAL);
-
- mutex_lock(&domain_mutex);
-
- for (n = rb_first(root); n; n = rb_next(n)) {
- node = rb_entry(n, struct msm_iova_data, node);
- if (node->domain == domain) {
- iova_data = node;
- break;
- }
- }
- mutex_unlock(&domain_mutex);
-
- return iova_data;
-}
-
-#ifdef CONFIG_MMU500_ACTIVE_PREFETCH_BUG_WITH_SECTION_MAPPING
-static unsigned long get_alignment_order(unsigned long align)
-{
- if (align >= SZ_1M && align < SZ_2M)
- return SZ_2M;
- else
- return align;
-}
-#else
-static unsigned long get_alignment_order(unsigned long align)
-{
- return ilog2(align);
-}
-#endif
-
-int msm_allocate_iova_address(unsigned int iommu_domain,
- unsigned int partition_no,
- unsigned long size,
- unsigned long align,
- unsigned long *iova)
-{
- struct msm_iova_data *data;
- struct mem_pool *pool;
- unsigned long va;
- unsigned long aligned;
-
- data = find_domain(iommu_domain);
- if (!data)
- return -EINVAL;
-
- if (partition_no >= data->npools)
- return -EINVAL;
-
- pool = &data->pools[partition_no];
- if (!pool->gpool)
- return -EINVAL;
-
- aligned = get_alignment_order(align);
-
- size = ALIGN(size, aligned);
-
- mutex_lock(&pool->pool_mutex);
- va = gen_pool_alloc(pool->gpool, size);
- mutex_unlock(&pool->pool_mutex);
-
- if (va) {
- pool->free -= size;
- /* Offset because genpool can't handle 0 addresses */
- if (pool->paddr == 0)
- va -= SZ_4K;
- *iova = va;
- return 0;
- }
-
- return -ENOMEM;
-}
-
-void msm_free_iova_address(unsigned long iova, unsigned int iommu_domain,
- unsigned int partition_no, unsigned long size)
-{
- struct msm_iova_data *data;
- struct mem_pool *pool;
-
- data = find_domain(iommu_domain);
-
- if (!data) {
- WARN(1, "Invalid domain %d\n", iommu_domain);
- return;
- }
-
- if (partition_no >= data->npools) {
- WARN(1, "Invalid partition %d for domain %d\n",
- partition_no, iommu_domain);
- return;
- }
-
- pool = &data->pools[partition_no];
-
- if (!pool)
- return;
-
- pool->free += size;
-
- /* Offset because genpool can't handle 0 addresses */
- if (pool->paddr == 0)
- iova += SZ_4K;
-
- mutex_lock(&pool->pool_mutex);
- gen_pool_free(pool->gpool, iova, size);
- mutex_unlock(&pool->pool_mutex);
-}
-
-int msm_register_domain(struct msm_iova_layout *layout)
-{
- int i;
- struct msm_iova_data *data;
- struct mem_pool *pools;
- struct bus_type *bus;
- int no_redirect;
-
- if (!layout)
- return -EINVAL;
-
- data = kmalloc(sizeof(*data), GFP_KERNEL);
- if (!data)
- return -ENOMEM;
-
- pools = kzalloc(sizeof(struct mem_pool) * layout->npartitions,
- GFP_KERNEL);
- if (!pools)
- goto free_data;
-
- for (i = 0; i < layout->npartitions; i++) {
- if (layout->partitions[i].size == 0)
- continue;
-
- pools[i].gpool = gen_pool_create(PAGE_SHIFT, -1);
-
- if (!pools[i].gpool)
- continue;
-
- pools[i].paddr = layout->partitions[i].start;
- pools[i].size = layout->partitions[i].size;
- mutex_init(&pools[i].pool_mutex);
-
- /*
- * genalloc can't handle a pool starting at address 0.
- * For now, solve this problem by offsetting the value
- * put in by 4k.
- * gen pool address = actual address + 4k
- */
- if (pools[i].paddr == 0)
- layout->partitions[i].start += SZ_4K;
-
- if (gen_pool_add(pools[i].gpool,
- layout->partitions[i].start,
- layout->partitions[i].size, -1)) {
- gen_pool_destroy(pools[i].gpool);
- pools[i].gpool = NULL;
- continue;
- }
-
- gen_pool_set_algo(pools[i].gpool,
- gen_pool_first_fit_order_align,
- (void *)PAGE_SHIFT);
- }
-
- bus = layout->is_secure == MSM_IOMMU_DOMAIN_SECURE ?
- &msm_iommu_sec_bus_type : &platform_bus_type;
-
- data->pools = pools;
- data->npools = layout->npartitions;
- data->domain_num = ida_simple_get(&domain_nums, 0, 0, GFP_KERNEL);
- if (data->domain_num < 0)
- goto free_pools;
-
- data->domain = iommu_domain_alloc(bus);
- if (!data->domain)
- goto free_domain_num;
-
- no_redirect = !(layout->domain_flags & MSM_IOMMU_DOMAIN_PT_CACHEABLE);
-
- iommu_domain_set_attr(data->domain,
- DOMAIN_ATTR_QCOM_COHERENT_HTW_DISABLE,
- &no_redirect);
-
- msm_iommu_set_client_name(data->domain, layout->client_name);
-
- add_domain(data);
-
- return data->domain_num;
-
-free_domain_num:
- ida_simple_remove(&domain_nums, data->domain_num);
-
-free_pools:
- for (i = 0; i < layout->npartitions; i++) {
- if (pools[i].gpool)
- gen_pool_destroy(pools[i].gpool);
- }
- kfree(pools);
-free_data:
- kfree(data);
-
- return -EINVAL;
-}
-EXPORT_SYMBOL(msm_register_domain);
-
-int msm_unregister_domain(struct iommu_domain *domain)
-{
- unsigned int i;
- struct msm_iova_data *data = msm_domain_to_iova_data(domain);
-
- if (IS_ERR_OR_NULL(data)) {
- pr_err("%s: Could not find iova_data\n", __func__);
- return -EINVAL;
- }
-
- if (remove_domain(data->domain)) {
- pr_err("%s: Domain not found. Failed to remove domain\n",
- __func__);
- }
-
- iommu_domain_free(domain);
-
- ida_simple_remove(&domain_nums, data->domain_num);
-
- for (i = 0; i < data->npools; ++i)
- if (data->pools[i].gpool)
- gen_pool_destroy(data->pools[i].gpool);
-
- kfree(data->pools);
- kfree(data);
-
- return 0;
-}
-EXPORT_SYMBOL(msm_unregister_domain);
-
-static int find_and_add_contexts(struct iommu_group *group,
- const struct device_node *node,
- unsigned int num_contexts)
-{
- unsigned int i;
- struct device *ctx;
- const char *name;
- struct device_node *ctx_node;
- int ret = 0;
-
- for (i = 0; i < num_contexts; ++i) {
- ctx_node = of_parse_phandle((struct device_node *) node,
- "qcom,iommu-contexts", i);
- if (!ctx_node) {
- pr_err("Unable to parse phandle #%u\n", i);
- ret = -EINVAL;
- goto out;
- }
-
- if (of_property_read_string(ctx_node, "label", &name)) {
- pr_err("Could not find label property\n");
- ret = -EINVAL;
- goto out;
- }
-
- ctx = msm_iommu_get_ctx(name);
- if (IS_ERR(ctx)) {
- ret = PTR_ERR(ctx);
- goto out;
- }
-
- ret = iommu_group_add_device(group, ctx);
- if (ret)
- goto out;
- }
-out:
- return ret;
-}
-
-static int create_and_add_domain(struct iommu_group *group,
- struct device_node const *node,
- char const *name)
-{
- unsigned int ret = 0;
- unsigned int i, j;
- struct msm_iova_layout l;
- struct msm_iova_partition *part = NULL;
- struct iommu_domain *domain = NULL;
- unsigned int *addr_array = NULL;
- unsigned int array_size;
- int domain_no;
- int secure_domain;
- int l2_redirect;
-
- if (of_get_property(node, "qcom,virtual-addr-pool", &array_size)) {
- l.npartitions = array_size / sizeof(unsigned int) / 2;
- part = kmalloc(sizeof(*part) * l.npartitions, GFP_KERNEL);
- if (!part) {
- pr_err("%s: could not allocate space for partition",
- __func__);
- ret = -ENOMEM;
- goto out;
- }
-
- addr_array = kmalloc(array_size, GFP_KERNEL);
- if (!addr_array) {
- pr_err("%s: could not allocate space for partition",
- __func__);
- ret = -ENOMEM;
- goto free_mem;
- }
-
- ret = of_property_read_u32_array(node,
- "qcom,virtual-addr-pool",
- addr_array,
- array_size /
- sizeof(unsigned int));
- if (ret) {
- ret = -EINVAL;
- goto free_mem;
- }
-
- for (i = 0, j = 0; j < l.npartitions * 2; i++, j += 2) {
- part[i].start = addr_array[j];
- part[i].size = addr_array[j + 1];
- }
- } else {
- l.npartitions = 1;
- part = kmalloc(sizeof(*part) * l.npartitions, GFP_KERNEL);
- if (!part) {
- pr_err("%s: could not allocate space for partition",
- __func__);
- ret = -ENOMEM;
- goto out;
- }
-
- part[0].start = 0x0;
- part[0].size = 0xFFFFFFFF;
- }
-
- l.client_name = name;
- l.partitions = part;
-
- secure_domain = of_property_read_bool(node, "qcom,secure-domain");
- l.is_secure = secure_domain ? MSM_IOMMU_DOMAIN_SECURE : 0;
-
- l2_redirect = of_property_read_bool(node, "qcom,l2-redirect");
- l.domain_flags = l2_redirect ? MSM_IOMMU_DOMAIN_PT_CACHEABLE : 0;
-
- domain_no = msm_register_domain(&l);
- if (domain_no >= 0)
- domain = msm_get_iommu_domain(domain_no);
- else
- ret = domain_no;
-
- iommu_group_set_iommudata(group, domain, NULL);
-
-free_mem:
- kfree(addr_array);
- kfree(part);
-out:
- return ret;
-}
-
-static int __msm_group_get_domain(struct device *dev, void *data)
-{
- struct msm_iommu_data_entry *list_entry;
- struct list_head *dev_list = data;
-
- list_entry = kmalloc(sizeof(*list_entry), GFP_KERNEL);
- if (!list_entry)
- return -ENOMEM;
-
- list_entry->data = dev;
- list_add(&list_entry->list, dev_list);
-
- return 0;
-}
-
-static void __msm_iommu_group_remove_device(struct iommu_group *grp)
-{
- struct msm_iommu_data_entry *tmp;
- struct msm_iommu_data_entry *list_entry;
- struct list_head dev_list;
-
- INIT_LIST_HEAD(&dev_list);
- iommu_group_for_each_dev(grp, &dev_list, __msm_group_get_domain);
-
- list_for_each_entry_safe(list_entry, tmp, &dev_list, list) {
- iommu_group_remove_device(list_entry->data);
- list_del(&list_entry->list);
- kfree(list_entry);
- }
-}
-
-static int iommu_domain_parse_dt(const struct device_node *dt_node)
-{
- struct device_node *node;
- int sz;
- unsigned int num_contexts;
- int ret = 0;
- struct iommu_group *group = 0;
- const char *name;
- struct msm_iommu_data_entry *grp_list_entry;
- struct msm_iommu_data_entry *tmp;
- struct list_head iommu_group_list;
- INIT_LIST_HEAD(&iommu_group_list);
-
- for_each_child_of_node(dt_node, node) {
- group = iommu_group_alloc();
- if (IS_ERR(group)) {
- ret = PTR_ERR(group);
- group = 0;
- goto free_group;
- }
-
- /* This is only needed to clean up memory if something fails */
- grp_list_entry = kmalloc(sizeof(*grp_list_entry), GFP_KERNEL);
- if (!grp_list_entry) {
- ret = -ENOMEM;
- goto free_group;
- }
-
- grp_list_entry->data = group;
- list_add(&grp_list_entry->list, &iommu_group_list);
-
- if (of_property_read_string(node, "label", &name)) {
- ret = -EINVAL;
- goto free_group;
- }
-
- iommu_group_set_name(group, name);
-
- if (!of_get_property(node, "qcom,iommu-contexts", &sz)) {
- pr_err("Could not find qcom,iommu-contexts property\n");
- ret = -EINVAL;
- goto free_group;
- }
-
- num_contexts = sz / sizeof(unsigned int);
-
- ret = find_and_add_contexts(group, node, num_contexts);
- if (ret)
- goto free_group;
-
- ret = create_and_add_domain(group, node, name);
- if (ret) {
- ret = -EINVAL;
- goto free_group;
- }
-
- /* Remove reference to the group that is taken when the group
- * is allocated. This will ensure that when all the devices in
- * the group are removed the group will be released.
- */
- iommu_group_put(group);
- }
-
- list_for_each_entry_safe(grp_list_entry, tmp, &iommu_group_list, list) {
- list_del(&grp_list_entry->list);
- kfree(grp_list_entry);
- }
-
- return 0;
-
-free_group:
- list_for_each_entry_safe(grp_list_entry, tmp, &iommu_group_list, list) {
- struct iommu_domain *d;
-
- d = iommu_group_get_iommudata(grp_list_entry->data);
- if (d)
- msm_unregister_domain(d);
-
- __msm_iommu_group_remove_device(grp_list_entry->data);
- list_del(&grp_list_entry->list);
- kfree(grp_list_entry);
- }
-
- iommu_group_put(group);
-
- return ret;
-}
-
-static int iommu_domain_probe(struct platform_device *pdev)
-{
- struct iommu_domains_pdata *p = pdev->dev.platform_data;
- int i, j;
-
- if (!msm_use_iommu())
- return -ENODEV;
-
- if (pdev->dev.of_node)
- return iommu_domain_parse_dt(pdev->dev.of_node);
- else if (!p)
- return -ENODEV;
-
- for (i = 0; i < p->ndomains; i++) {
- struct msm_iova_layout l;
- struct msm_iova_partition *part;
- struct msm_iommu_domain *domains;
-
- domains = p->domains;
- l.npartitions = domains[i].npools;
- part = kmalloc(
- sizeof(struct msm_iova_partition) * l.npartitions,
- GFP_KERNEL);
-
- if (!part) {
- pr_info("%s: could not allocate space for domain %d",
- __func__, i);
- continue;
- }
-
- for (j = 0; j < l.npartitions; j++) {
- part[j].start = p->domains[i].iova_pools[j].paddr;
- part[j].size = p->domains[i].iova_pools[j].size;
- }
-
- l.partitions = part;
-
- msm_register_domain(&l);
-
- kfree(part);
- }
-
- for (i = 0; i < p->nnames; i++) {
- struct device *ctx;
- struct iommu_domain *domain;
-
- ctx = msm_iommu_get_ctx(p->domain_names[i].name);
- if (!ctx)
- continue;
-
- domain = msm_get_iommu_domain(p->domain_names[i].domain);
- if (!domain)
- continue;
-
- if (iommu_attach_device(domain, ctx)) {
- WARN(1, "%s: could not attach domain %p to context %s."
- " iommu programming will not occur.\n",
- __func__, domain, p->domain_names[i].name);
- continue;
- }
- }
-
- return 0;
-}
-
-static int iommu_domain_exit(struct platform_device *pdev)
-{
- return 0;
-}
-
-static struct of_device_id msm_iommu_domain_match_table[] = {
- { .name = "qcom,iommu-domains", },
- {}
-};
-
-static struct platform_driver iommu_domain_driver = {
- .driver = {
- .name = "iommu_domains",
- .of_match_table = msm_iommu_domain_match_table,
- .owner = THIS_MODULE
- },
- .probe = iommu_domain_probe,
- .remove = iommu_domain_exit,
-};
-
-static int __init msm_subsystem_iommu_init(void)
-{
- int ret;
-
- ret = platform_driver_register(&iommu_domain_driver);
- if (ret) {
- pr_err("Failed to register IOMMU domain driver\n");
- return ret;
- }
-
- return 0;
-}
-
-static void __exit msm_subsystem_iommu_exit(void)
-{
- platform_driver_unregister(&iommu_domain_driver);
-}
-
-device_initcall(msm_subsystem_iommu_init);
-module_exit(msm_subsystem_iommu_exit);
-
-MODULE_LICENSE("GPL v2");
-MODULE_DESCRIPTION("MSM SMMU v2 domains driver");
+++ /dev/null
-/* Copyright (c) 2011-2014, The Linux Foundation. All rights reserved.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 and
- * only version 2 as published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- */
-
-#include <linux/dma-buf.h>
-#include <linux/export.h>
-#include <linux/iommu.h>
-#include <linux/kernel.h>
-#include <linux/kref.h>
-#include <linux/scatterlist.h>
-#include <linux/slab.h>
-
-#include <linux/msm_iommu_domains.h>
-
-enum {
- DI_PARTITION_NUM = 0,
- DI_DOMAIN_NUM = 1,
- DI_MAX,
-};
-
-#define iommu_map_domain(__m) ((__m)->domain_info[1])
-#define iommu_map_partition(__m) ((__m)->domain_info[0])
-
-/**
- * struct msm_iommu_map - represents a mapping of an ion buffer to an iommu
- * @iova_addr - iommu virtual address
- * @node - rb node to exist in the buffer's tree of iommu mappings
- * @domain_info - contains the partition number and domain number
- * domain_info[1] = domain number
- * domain_info[0] = partition number
- * @ref - for reference counting this mapping
- * @mapped_size - size of the iova space mapped
- * (may not be the same as the buffer size)
- * @flags - iommu domain/partition specific flags.
- *
- * Represents a mapping of one ion buffer to a particular iommu domain
- * and address range. There may exist other mappings of this buffer in
- * different domains or address ranges. All mappings will have the same
- * cacheability and security.
- */
-struct msm_iommu_map {
- unsigned long iova_addr;
- struct rb_node node;
- union {
- int domain_info[DI_MAX];
- uint64_t key;
- };
- struct msm_iommu_meta *meta;
- struct kref ref;
- int mapped_size;
- unsigned long flags;
-};
-
-struct msm_iommu_meta {
- struct rb_node node;
- struct rb_root iommu_maps;
- struct kref ref;
- struct sg_table *table;
- unsigned long size;
- struct mutex lock;
- struct dma_buf *dbuf;
-};
-
-static struct rb_root iommu_root;
-static DEFINE_MUTEX(msm_iommu_map_mutex);
-
-static void msm_iommu_meta_add(struct msm_iommu_meta *meta)
-{
- struct rb_root *root = &iommu_root;
- struct rb_node **p = &root->rb_node;
- struct rb_node *parent = NULL;
- struct msm_iommu_meta *entry;
-
- while (*p) {
- parent = *p;
- entry = rb_entry(parent, struct msm_iommu_meta, node);
-
- if (meta->table < entry->table) {
- p = &(*p)->rb_left;
- } else if (meta->table > entry->table) {
- p = &(*p)->rb_right;
- } else {
- pr_err("%s: dma_buf %p already exists\n", __func__,
- entry->dbuf);
- BUG();
- }
- }
-
- rb_link_node(&meta->node, parent, p);
- rb_insert_color(&meta->node, root);
-}
-
-static struct msm_iommu_meta *msm_iommu_meta_lookup(struct sg_table *table)
-{
- struct rb_root *root = &iommu_root;
- struct rb_node **p = &root->rb_node;
- struct rb_node *parent = NULL;
- struct msm_iommu_meta *entry = NULL;
-
- while (*p) {
- parent = *p;
- entry = rb_entry(parent, struct msm_iommu_meta, node);
-
- if (table < entry->table)
- p = &(*p)->rb_left;
- else if (table > entry->table)
- p = &(*p)->rb_right;
- else
- return entry;
- }
-
- return NULL;
-}
-
-static void msm_iommu_add(struct msm_iommu_meta *meta,
- struct msm_iommu_map *iommu)
-{
- struct rb_node **p = &meta->iommu_maps.rb_node;
- struct rb_node *parent = NULL;
- struct msm_iommu_map *entry;
-
- while (*p) {
- parent = *p;
- entry = rb_entry(parent, struct msm_iommu_map, node);
-
- if (iommu->key < entry->key) {
- p = &(*p)->rb_left;
- } else if (iommu->key > entry->key) {
- p = &(*p)->rb_right;
- } else {
- pr_err("%s: dma_buf %p already has mapping for domain %d and partition %d\n",
- __func__,
- meta->dbuf,
- iommu_map_domain(iommu),
- iommu_map_partition(iommu));
- BUG();
- }
- }
-
- rb_link_node(&iommu->node, parent, p);
- rb_insert_color(&iommu->node, &meta->iommu_maps);
-}
-
-static struct msm_iommu_map *msm_iommu_lookup(struct msm_iommu_meta *meta,
- unsigned int domain_no,
- unsigned int partition_no)
-{
- struct rb_node **p = &meta->iommu_maps.rb_node;
- struct rb_node *parent = NULL;
- struct msm_iommu_map *entry;
- uint64_t key = domain_no;
-
- key = key << 32 | partition_no;
-
- while (*p) {
- parent = *p;
- entry = rb_entry(parent, struct msm_iommu_map, node);
-
- if (key < entry->key)
- p = &(*p)->rb_left;
- else if (key > entry->key)
- p = &(*p)->rb_right;
- else
- return entry;
- }
-
- return NULL;
-}
-
-static int msm_iommu_map_iommu(struct msm_iommu_meta *meta,
- struct msm_iommu_map *data,
- unsigned int domain_num,
- unsigned int partition_num,
- unsigned long align,
- unsigned long iova_length,
- unsigned long flags)
-{
- struct iommu_domain *domain;
- int ret = 0;
- unsigned long extra, size;
- struct sg_table *table;
- int prot = IOMMU_WRITE | IOMMU_READ;
-
- size = meta->size;
- data->mapped_size = iova_length;
- extra = iova_length - size;
- table = meta->table;
-
- /* Use the biggest alignment to allow bigger IOMMU mappings.
- * Use the first entry since the first entry will always be the
- * biggest entry. To take advantage of bigger mapping sizes both the
- * VA and PA addresses have to be aligned to the biggest size.
- */
- if (table->sgl->length > align)
- align = table->sgl->length;
-
- ret = msm_allocate_iova_address(domain_num, partition_num,
- data->mapped_size, align,
- &data->iova_addr);
- if (ret)
- goto out;
-
- domain = msm_get_iommu_domain(domain_num);
- if (!domain) {
- ret = -ENOMEM;
- goto out1;
- }
-
- ret = iommu_map_sg(domain, data->iova_addr, table->sgl, size, prot);
- if (ret) {
- pr_err("%s: could not map %lx in domain %p\n",
- __func__, data->iova_addr, domain);
- goto out1;
- }
-
- if (extra) {
- unsigned long extra_iova_addr = data->iova_addr + size;
- unsigned long phys_addr = sg_phys(table->sgl);
- ret = msm_iommu_map_extra(domain, extra_iova_addr, phys_addr,
- extra, SZ_4K, prot);
- if (ret)
- goto out2;
- }
-
- return ret;
-
-out2:
- iommu_unmap(domain, data->iova_addr, size);
-out1:
- msm_free_iova_address(data->iova_addr, domain_num, partition_num, size);
-out:
- return ret;
-}
-
-static void msm_iommu_heap_unmap_iommu(struct msm_iommu_map *data)
-{
- unsigned int domain_num;
- unsigned int partition_num;
- struct iommu_domain *domain;
- int ret;
-
- BUG_ON(!msm_use_iommu());
-
- domain_num = iommu_map_domain(data);
- partition_num = iommu_map_partition(data);
- domain = msm_get_iommu_domain(domain_num);
-
- if (!domain) {
- WARN(1, "Could not get domain %d. Corruption?\n", domain_num);
- return;
- }
-
- ret = iommu_unmap(domain, data->iova_addr, data->mapped_size);
- WARN_ON(ret < 0);
-
- msm_free_iova_address(data->iova_addr, domain_num, partition_num,
- data->mapped_size);
-
- return;
-}
-
-static struct msm_iommu_map *
-__msm_iommu_map(struct msm_iommu_meta *meta,
- int domain_num, int partition_num, unsigned long align,
- unsigned long iova_length, unsigned long flags,
- unsigned long *iova)
-{
- struct msm_iommu_map *data;
- int ret;
-
- data = kmalloc(sizeof(*data), GFP_ATOMIC);
- if (!data)
- return ERR_PTR(-ENOMEM);
-
- iommu_map_domain(data) = domain_num;
- iommu_map_partition(data) = partition_num;
-
- ret = msm_iommu_map_iommu(meta, data, domain_num, partition_num, align,
- iova_length, flags);
- if (ret)
- goto out;
-
- kref_init(&data->ref);
- *iova = data->iova_addr;
- data->meta = meta;
-
- msm_iommu_add(meta, data);
-
- return data;
-
-out:
- kfree(data);
- return ERR_PTR(ret);
-}
-
-static struct msm_iommu_meta *msm_iommu_meta_create(struct dma_buf *dma_buf,
- struct sg_table *table,
- unsigned long size)
-{
- struct msm_iommu_meta *meta;
-
- meta = kzalloc(sizeof(*meta), GFP_KERNEL);
- if (!meta)
- return ERR_PTR(-ENOMEM);
-
- meta->table = table;
- meta->size = size;
-
- /*
- * The caller is expected to have taken a reference to this dma_buf
- * before calling this function
- */
- meta->dbuf = dma_buf;
- kref_init(&meta->ref);
- mutex_init(&meta->lock);
- msm_iommu_meta_add(meta);
-
- return meta;
-}
-
-static void msm_iommu_meta_destroy(struct kref *kref)
-{
- struct msm_iommu_meta *meta;
-
- meta = container_of(kref, struct msm_iommu_meta, ref);
-
- rb_erase(&meta->node, &iommu_root);
- dma_buf_put(meta->dbuf);
- kfree(meta);
-}
-
-static void msm_iommu_meta_put(struct msm_iommu_meta *meta)
-{
- /* Need to lock here to prevent race against map/unmap */
- mutex_lock(&msm_iommu_map_mutex);
- kref_put(&meta->ref, msm_iommu_meta_destroy);
- mutex_unlock(&msm_iommu_map_mutex);
-}
-
-static int
-__msm_map_iommu_common(struct dma_buf *dma_buf, struct sg_table *table,
- int domain_num, int partition_num, unsigned long align,
- unsigned long iova_length, unsigned long *iova,
- unsigned long *buffer_size,
- unsigned long flags, unsigned long iommu_flags)
-
-{
- struct msm_iommu_map *iommu_map;
- struct msm_iommu_meta *iommu_meta = NULL;
- struct scatterlist *sg;
- unsigned long size = 0;
- int ret = 0;
- int i;
-
- for_each_sg(table->sgl, sg, table->nents, i)
- size += sg->length;
-
- if (!msm_use_iommu()) {
- unsigned long pa = sg_dma_address(table->sgl);
- if (pa == 0)
- pa = sg_phys(table->sgl);
- *iova = pa;
- *buffer_size = size;
- }
-
- /*
- * If clients don't want a custom iova length, just use whatever
- * the buffer size is
- */
- if (!iova_length)
- iova_length = size;
-
- if (size > iova_length) {
- pr_debug("%s: iova length %lx is not at least buffer size %lx\n",
- __func__, iova_length, size);
- ret = -EINVAL;
- goto out;
- }
-
- if (size & ~PAGE_MASK) {
- pr_debug("%s: buffer size %lx is not aligned to %lx", __func__,
- size, PAGE_SIZE);
- ret = -EINVAL;
- goto out;
- }
-
- if (iova_length & ~PAGE_MASK) {
- pr_debug("%s: iova_length %lx is not aligned to %lx", __func__,
- iova_length, PAGE_SIZE);
- ret = -EINVAL;
- goto out;
- }
-
- mutex_lock(&msm_iommu_map_mutex);
-
- iommu_meta = msm_iommu_meta_lookup(table);
- if (!iommu_meta) {
- iommu_meta = msm_iommu_meta_create(dma_buf, table, size);
- if (IS_ERR(iommu_meta)) {
- ret = PTR_ERR(iommu_meta);
- goto out;
- }
- } else {
- /*
- * Drop the dma_buf reference here. We took the reference
- * during meta creation so we need to drop it if we are
- * just taking a reference to the meta itself.
- */
- dma_buf_put(dma_buf);
- kref_get(&iommu_meta->ref);
- }
-
- BUG_ON(iommu_meta->size != size);
-
- mutex_unlock(&msm_iommu_map_mutex);
-
- mutex_lock(&iommu_meta->lock);
- iommu_map = msm_iommu_lookup(iommu_meta, domain_num, partition_num);
- if (!iommu_map) {
- iommu_map = __msm_iommu_map(iommu_meta, domain_num,
- partition_num, align, iova_length,
- flags, iova);
- if (!IS_ERR_OR_NULL(iommu_map)) {
- iommu_map->flags = iommu_flags;
- ret = 0;
- } else {
- ret = PTR_ERR(iommu_map);
- goto out_unlock;
- }
- } else {
- if (iommu_map->flags != iommu_flags) {
- pr_err("%s: dma_buf %p is already mapped with iommu flags %lx, trying to map with flags %lx\n",
- __func__, dma_buf,
- iommu_map->flags, iommu_flags);
- ret = -EINVAL;
- goto out_unlock;
- } else if (iommu_map->mapped_size != iova_length) {
- pr_err("%s: dma_buf %p is already mapped with length %x, trying to map with length %lx\n",
- __func__, dma_buf, iommu_map->mapped_size,
- iova_length);
- ret = -EINVAL;
- goto out_unlock;
- } else {
- kref_get(&iommu_map->ref);
- *iova = iommu_map->iova_addr;
- }
- }
- mutex_unlock(&iommu_meta->lock);
- *buffer_size = size;
-
- return ret;
-
-out_unlock:
- mutex_unlock(&iommu_meta->lock);
-out:
- if (iommu_meta)
- msm_iommu_meta_put(iommu_meta);
- return ret;
-}
-
-int msm_map_dma_buf(struct dma_buf *dma_buf, struct sg_table *table,
- int domain_num, int partition_num, unsigned long align,
- unsigned long iova_length, unsigned long *iova,
- unsigned long *buffer_size,
- unsigned long flags, unsigned long iommu_flags)
-{
- int ret;
-
- if (IS_ERR_OR_NULL(dma_buf)) {
- pr_err("%s: dma_buf pointer is invalid\n", __func__);
- return -EINVAL;
- }
-
- if (IS_ERR_OR_NULL(table)) {
- pr_err("%s: table pointer is invalid\n", __func__);
- return -EINVAL;
- }
-
- get_dma_buf(dma_buf);
-
- ret = __msm_map_iommu_common(dma_buf, table, domain_num,
- partition_num, align, iova_length, iova,
- buffer_size, flags, iommu_flags);
-
- if (ret)
- dma_buf_put(dma_buf);
-
- return ret;
-}
-
-static void msm_iommu_map_release(struct kref *kref)
-{
- struct msm_iommu_map *map;
- struct msm_iommu_meta *meta;
-
- map = container_of(kref, struct msm_iommu_map, ref);
- meta = map->meta;
-
- rb_erase(&map->node, &meta->iommu_maps);
- msm_iommu_heap_unmap_iommu(map);
- kfree(map);
-}
-
-static void __msm_unmap_iommu_common(struct sg_table *table, int domain_num,
- int partition_num)
-{
- struct msm_iommu_map *iommu_map;
- struct msm_iommu_meta *meta;
-
- mutex_lock(&msm_iommu_map_mutex);
- meta = msm_iommu_meta_lookup(table);
- if (!meta) {
- WARN(1, "%s: (%d,%d) was never mapped for %p\n", __func__,
- domain_num, partition_num, table);
- mutex_unlock(&msm_iommu_map_mutex);
- goto out;
-
- }
- mutex_unlock(&msm_iommu_map_mutex);
-
- mutex_lock(&meta->lock);
- iommu_map = msm_iommu_lookup(meta, domain_num, partition_num);
- if (!iommu_map) {
- WARN(1, "%s: (%d,%d) was never mapped for %p\n", __func__,
- domain_num, partition_num, table);
- mutex_unlock(&meta->lock);
- goto out;
- }
-
- kref_put(&iommu_map->ref, msm_iommu_map_release);
- mutex_unlock(&meta->lock);
-
- msm_iommu_meta_put(meta);
-
-out:
- return;
-}
-
-void msm_unmap_dma_buf(struct sg_table *table, int domain_num,
- int partition_num)
-{
- return __msm_unmap_iommu_common(table, domain_num, partition_num);
-}
+++ /dev/null
-/* Copyright (c) 2012-2014, The Linux Foundation. All rights reserved.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 and
- * only version 2 as published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- */
-#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
-
-#include <linux/kernel.h>
-#include <linux/module.h>
-#include <linux/string.h>
-#include <linux/iommu.h>
-#include <linux/slab.h>
-#include <linux/device.h>
-#include <linux/interrupt.h>
-#include <linux/bitops.h>
-#include <linux/debugfs.h>
-#include <linux/qcom_iommu.h>
-#include "msm_iommu_perfmon.h"
-
-static LIST_HEAD(iommu_list);
-static struct dentry *msm_iommu_root_debugfs_dir;
-static const char *NO_EVENT_CLASS_NAME = "none";
-static const unsigned int MAX_EVEN_CLASS_NAME_LEN = 36;
-
-struct event_class {
- unsigned int event_number;
- const char *desc;
-};
-
-static struct event_class pmu_event_classes[] = {
- { 0x00, "cycle_count" },
- { 0x01, "cycle_count64" },
- { 0x08, "tlb_refill" },
- { 0x09, "tlb_refill_read" },
- { 0x0A, "tlb_refill_write" },
- { 0x10, "access" },
- { 0x11, "access_read" },
- { 0x12, "access_write" },
- { 0x80, "full_misses" },
- { 0x81, "partial_miss_1lbfb_hit" },
- { 0x82, "partial_miss_2lbfb_hit" },
- { 0x83, "full_hit" },
- { 0x90, "pred_req_full_miss" },
- { 0x91, "pred_req_partial_miss_1lbfb_hit" },
- { 0x92, "pred_req_partial_miss_2lbfb_hit" },
- { 0xb0, "tot_num_miss_axi_htw_read_req" },
- { 0xb1, "tot_num_pred_axi_htw_read_req" },
-};
-
-static unsigned int iommu_pm_create_sup_cls_str(char **buf,
- struct iommu_pmon *pmon)
-{
- unsigned long buf_size = ARRAY_SIZE(pmu_event_classes) *
- MAX_EVEN_CLASS_NAME_LEN;
- unsigned int pos = 0;
- unsigned int nevent_cls = pmon->nevent_cls_supported;
-
- *buf = kzalloc(buf_size, GFP_KERNEL);
- if (*buf) {
- unsigned int j;
- int i;
- struct event_class *ptr;
- size_t array_len = ARRAY_SIZE(pmu_event_classes);
- ptr = pmu_event_classes;
-
- for (j = 0; j < nevent_cls; ++j) {
- for (i = 0; i < array_len; ++i) {
-
- if (ptr[i].event_number !=
- pmon->event_cls_supported[j])
- continue;
-
- if (pos < buf_size) {
- pos += snprintf(&(*buf)[pos],
- buf_size-pos,
- "[%u] %s\n",
- ptr[i].event_number,
- ptr[i].desc);
- }
- break;
- }
- }
- }
- return pos;
-}
-
-static int iommu_pm_event_class_supported(struct iommu_pmon *pmon,
- int event_class)
-{
- unsigned int nevent_cls = pmon->nevent_cls_supported;
- unsigned int i;
-
- for (i = 0; i < nevent_cls; ++i) {
- if (event_class == pmon->event_cls_supported[i])
- return event_class;
- }
- return MSM_IOMMU_PMU_NO_EVENT_CLASS;
-}
-
-static const char *iommu_pm_find_event_class_name(int event_class)
-{
- size_t array_len;
- struct event_class *ptr;
- int i;
- const char *event_class_name = NO_EVENT_CLASS_NAME;
- if (event_class < 0)
- goto out;
-
- array_len = ARRAY_SIZE(pmu_event_classes);
- ptr = pmu_event_classes;
-
- for (i = 0; i < array_len; ++i) {
- if (ptr[i].event_number == event_class) {
- event_class_name = ptr[i].desc;
- break;
- }
- }
-
-out:
- return event_class_name;
-}
-
-static int iommu_pm_find_event_class(struct iommu_pmon *pmon,
- const char *event_class_name)
-{
- size_t array_len;
- struct event_class *ptr;
- int i;
- int event_class = MSM_IOMMU_PMU_NO_EVENT_CLASS;
-
- if (strcmp(event_class_name, NO_EVENT_CLASS_NAME) == 0)
- goto out;
-
- array_len = ARRAY_SIZE(pmu_event_classes);
- ptr = pmu_event_classes;
-
- for (i = 0; i < array_len; ++i) {
- if (strcmp(ptr[i].desc, event_class_name) == 0) {
- event_class = ptr[i].event_number;
- goto out;
- }
- }
-
-out:
- event_class = iommu_pm_event_class_supported(pmon, event_class);
- return event_class;
-}
-
-static inline void iommu_pm_add_to_iommu_list(struct iommu_pmon *iommu_pmon)
-{
- list_add(&iommu_pmon->iommu_list, &iommu_list);
-}
-
-static inline void iommu_pm_del_from_iommu_list(struct iommu_pmon *iommu_pmon)
-{
- list_del(&iommu_pmon->iommu_list);
-}
-
-static struct iommu_pmon *iommu_pm_get_pm_by_dev(struct device *dev)
-{
- struct iommu_pmon *pmon;
- struct iommu_info *info;
- struct list_head *ent;
- list_for_each(ent, &iommu_list) {
- pmon = list_entry(ent, struct iommu_pmon, iommu_list);
- info = &pmon->iommu;
- if (dev == info->iommu_dev)
- return pmon;
- }
- return NULL;
-}
-
-static void iommu_pm_set_event_type(struct iommu_pmon *pmon,
- struct iommu_pmon_counter *counter)
-{
- int event_class;
- unsigned int count_no;
- struct iommu_info *iommu = &pmon->iommu;
-
- event_class = counter->current_event_class;
- count_no = counter->absolute_counter_no;
-
- if (event_class == MSM_IOMMU_PMU_NO_EVENT_CLASS) {
- if (iommu->hw_ops->is_hw_access_OK(pmon)) {
- iommu->ops->iommu_lock_acquire(1);
- iommu->hw_ops->counter_disable(iommu, counter);
- iommu->hw_ops->ovfl_int_disable(iommu, counter);
- iommu->hw_ops->set_event_class(pmon, count_no, 0);
- iommu->ops->iommu_lock_release(1);
- }
- counter->overflow_count = 0;
- counter->value = 0;
- } else {
- counter->overflow_count = 0;
- counter->value = 0;
- if (iommu->hw_ops->is_hw_access_OK(pmon)) {
- iommu->ops->iommu_lock_acquire(1);
- iommu->hw_ops->set_event_class(pmon, count_no,
- event_class);
- iommu->hw_ops->ovfl_int_enable(iommu, counter);
- iommu->hw_ops->counter_enable(iommu, counter);
- iommu->ops->iommu_lock_release(1);
- }
- }
-}
-
-static void iommu_pm_reset_counts(struct iommu_pmon *pmon)
-{
- unsigned int i;
- unsigned int j;
- for (i = 0; i < pmon->num_groups; ++i) {
- struct iommu_pmon_cnt_group *cnt_grp = &pmon->cnt_grp[i];
- for (j = 0; j < cnt_grp->num_counters; ++j) {
- cnt_grp->counters[j].value = 0;
- cnt_grp->counters[j].overflow_count = 0;
- }
- }
-}
-
-static void iommu_pm_set_all_counters(struct iommu_pmon *pmon)
-{
- unsigned int i;
- unsigned int j;
- for (i = 0; i < pmon->num_groups; ++i) {
- struct iommu_pmon_cnt_group *cnt_grp = &pmon->cnt_grp[i];
- for (j = 0; j < cnt_grp->num_counters; ++j)
- iommu_pm_set_event_type(pmon, &cnt_grp->counters[j]);
- }
-}
-
-static void iommu_pm_read_all_counters(struct iommu_pmon *pmon)
-{
- unsigned int i;
- unsigned int j;
- struct iommu_info *iommu = &pmon->iommu;
- for (i = 0; i < pmon->num_groups; ++i) {
- struct iommu_pmon_cnt_group *cnt_grp = &pmon->cnt_grp[i];
- for (j = 0; j < cnt_grp->num_counters; ++j) {
- struct iommu_pmon_counter *counter;
- counter = &cnt_grp->counters[j];
- counter->value = iommu->hw_ops->read_counter(counter);
- }
- }
-}
-
-static void iommu_pm_on(struct iommu_pmon *pmon)
-{
- unsigned int i;
- struct iommu_info *iommu = &pmon->iommu;
- struct msm_iommu_drvdata *iommu_drvdata =
- dev_get_drvdata(iommu->iommu_dev);
-
- iommu->ops->iommu_power_on(iommu_drvdata);
- iommu->ops->iommu_bus_vote(iommu_drvdata, 1);
- iommu->ops->iommu_clk_on(iommu_drvdata);
-
- /* Reset counters in HW */
- iommu->ops->iommu_lock_acquire(1);
- iommu->hw_ops->reset_counters(&pmon->iommu);
- iommu->ops->iommu_lock_release(1);
-
- /* Reset SW counters */
- iommu_pm_reset_counts(pmon);
-
- pmon->enabled = 1;
-
- iommu_pm_set_all_counters(pmon);
-
- iommu->ops->iommu_lock_acquire(1);
-
- /* enable all counter group */
- for (i = 0; i < pmon->num_groups; ++i)
- iommu->hw_ops->grp_enable(iommu, i);
-
- /* enable global counters */
- iommu->hw_ops->enable_pm(iommu);
- iommu->ops->iommu_lock_release(1);
-
- pr_info("%s: TLB performance monitoring turned ON\n",
- pmon->iommu.iommu_name);
-}
-
-static void iommu_pm_off(struct iommu_pmon *pmon)
-{
- unsigned int i;
- struct iommu_info *iommu = &pmon->iommu;
- struct msm_iommu_drvdata *iommu_drvdata =
- dev_get_drvdata(iommu->iommu_dev);
-
- pmon->enabled = 0;
-
- iommu->ops->iommu_lock_acquire(1);
-
- /* disable global counters */
- iommu->hw_ops->disable_pm(iommu);
-
- /* Check if we overflowed just before turning off pmon */
- iommu->hw_ops->check_for_overflow(pmon);
-
- /* disable all counter group */
- for (i = 0; i < pmon->num_groups; ++i)
- iommu->hw_ops->grp_disable(iommu, i);
-
- /* Update cached copy of counters before turning off power */
- iommu_pm_read_all_counters(pmon);
-
- iommu->ops->iommu_lock_release(1);
- iommu->ops->iommu_clk_off(iommu_drvdata);
- iommu->ops->iommu_bus_vote(iommu_drvdata, 0);
- iommu->ops->iommu_power_off(iommu_drvdata);
-
- pr_info("%s: TLB performance monitoring turned OFF\n",
- pmon->iommu.iommu_name);
-}
-
-static int iommu_pm_debug_open(struct inode *inode, struct file *file)
-{
- file->private_data = inode->i_private;
- return 0;
-}
-
-static ssize_t iommu_pm_count_value_read(struct file *fp,
- char __user *user_buff,
- size_t count, loff_t *pos)
-{
- size_t rd_cnt;
- unsigned long long full_count;
-
- struct iommu_pmon_counter *counter = fp->private_data;
- struct iommu_pmon *pmon = counter->cnt_group->pmon;
- struct iommu_info *iommu = &pmon->iommu;
- char buf[50];
- size_t len;
-
- mutex_lock(&pmon->lock);
-
- if (iommu->hw_ops->is_hw_access_OK(pmon)) {
- iommu->ops->iommu_lock_acquire(1);
- counter->value = iommu->hw_ops->read_counter(counter);
- iommu->ops->iommu_lock_release(1);
- }
- full_count = (unsigned long long) counter->value +
- ((unsigned long long)counter->overflow_count *
- 0x100000000ULL);
-
- len = snprintf(buf, 50, "%llu\n", full_count);
- rd_cnt = simple_read_from_buffer(user_buff, count, pos, buf, len);
- mutex_unlock(&pmon->lock);
-
- return rd_cnt;
-}
-
-static const struct file_operations cnt_value_file_ops = {
- .open = iommu_pm_debug_open,
- .read = iommu_pm_count_value_read,
-};
-
-static ssize_t iommu_pm_event_class_read(struct file *fp,
- char __user *user_buff,
- size_t count, loff_t *pos)
-{
- size_t rd_cnt;
- struct iommu_pmon_counter *counter = fp->private_data;
- struct iommu_pmon *pmon = counter->cnt_group->pmon;
- char buf[50];
- const char *event_class_name;
- size_t len;
-
- mutex_lock(&pmon->lock);
- event_class_name = iommu_pm_find_event_class_name(
- counter->current_event_class);
- len = snprintf(buf, 50, "%s\n", event_class_name);
-
- rd_cnt = simple_read_from_buffer(user_buff, count, pos, buf, len);
- mutex_unlock(&pmon->lock);
- return rd_cnt;
-}
-
-static ssize_t iommu_pm_event_class_write(struct file *fp,
- const char __user *user_buff,
- size_t count, loff_t *pos)
-{
- size_t wr_cnt;
- char buf[50];
- size_t buf_size = sizeof(buf);
- struct iommu_pmon_counter *counter = fp->private_data;
- struct iommu_pmon *pmon = counter->cnt_group->pmon;
- int current_event_class;
-
- if ((count + *pos) >= buf_size)
- return -EINVAL;
-
- mutex_lock(&pmon->lock);
- current_event_class = counter->current_event_class;
- wr_cnt = simple_write_to_buffer(buf, buf_size, pos, user_buff, count);
- if (wr_cnt >= 1) {
- int rv;
- long value;
- buf[wr_cnt-1] = '\0';
- rv = kstrtol(buf, 10, &value);
- if (!rv) {
- counter->current_event_class =
- iommu_pm_find_event_class(pmon,
- iommu_pm_find_event_class_name(value));
- } else {
- counter->current_event_class =
- iommu_pm_find_event_class(pmon, buf);
- } }
-
- if (current_event_class != counter->current_event_class)
- iommu_pm_set_event_type(pmon, counter);
-
- mutex_unlock(&pmon->lock);
- return wr_cnt;
-}
-
-static const struct file_operations event_class_file_ops = {
- .open = iommu_pm_debug_open,
- .read = iommu_pm_event_class_read,
- .write = iommu_pm_event_class_write,
-};
-
-static ssize_t iommu_reset_counters_write(struct file *fp,
- const char __user *user_buff,
- size_t count, loff_t *pos)
-{
- size_t wr_cnt;
- char buf[10];
- size_t buf_size = sizeof(buf);
- struct iommu_pmon *pmon = fp->private_data;
- struct iommu_info *iommu = &pmon->iommu;
-
- if ((count + *pos) >= buf_size)
- return -EINVAL;
-
- mutex_lock(&pmon->lock);
- wr_cnt = simple_write_to_buffer(buf, buf_size, pos, user_buff, count);
- if (wr_cnt >= 1) {
- unsigned long cmd = 0;
- int rv;
- buf[wr_cnt-1] = '\0';
- rv = kstrtoul(buf, 10, &cmd);
- if (!rv && (cmd == 1)) {
- if (iommu->hw_ops->is_hw_access_OK(pmon)) {
- iommu->ops->iommu_lock_acquire(1);
- iommu->hw_ops->reset_counters(&pmon->iommu);
- iommu->ops->iommu_lock_release(1);
- }
- iommu_pm_reset_counts(pmon);
- pr_info("TLB performance counters reset\n");
- } else {
- pr_err("Unknown performance monitor command: %lu\n",
- cmd);
- }
- }
- mutex_unlock(&pmon->lock);
- return wr_cnt;
-}
-
-static const struct file_operations reset_file_ops = {
- .open = iommu_pm_debug_open,
- .write = iommu_reset_counters_write,
-};
-
-static ssize_t iommu_pm_enable_counters_read(struct file *fp,
- char __user *user_buff,
- size_t count, loff_t *pos)
-{
- size_t rd_cnt;
- char buf[5];
- size_t len;
- struct iommu_pmon *pmon = fp->private_data;
-
- mutex_lock(&pmon->lock);
- len = snprintf(buf, 5, "%u\n", pmon->enabled);
- rd_cnt = simple_read_from_buffer(user_buff, count, pos, buf, len);
- mutex_unlock(&pmon->lock);
- return rd_cnt;
-}
-
-static ssize_t iommu_pm_enable_counters_write(struct file *fp,
- const char __user *user_buff,
- size_t count, loff_t *pos)
-{
- size_t wr_cnt;
- char buf[10];
- size_t buf_size = sizeof(buf);
- struct iommu_pmon *pmon = fp->private_data;
-
- if ((count + *pos) >= buf_size)
- return -EINVAL;
-
- mutex_lock(&pmon->lock);
- wr_cnt = simple_write_to_buffer(buf, buf_size, pos, user_buff, count);
- if (wr_cnt >= 1) {
- unsigned long cmd;
- int rv;
- buf[wr_cnt-1] = '\0';
- rv = kstrtoul(buf, 10, &cmd);
- if (!rv && (cmd < 2)) {
- if (pmon->enabled == 1 && cmd == 0) {
- if (pmon->iommu.always_on ||
- pmon->iommu_attach_count > 0)
- iommu_pm_off(pmon);
- } else if (pmon->enabled == 0 && cmd == 1) {
- /* We can only turn on perf. monitoring if
- * iommu is attached (if not always on).
- * Delay turning on perf. monitoring until
- * we are attached.
- */
- if (pmon->iommu.always_on ||
- pmon->iommu_attach_count > 0)
- iommu_pm_on(pmon);
- else
- pmon->enabled = 1;
- }
- } else {
- pr_err("Unknown performance monitor command: %lu\n",
- cmd);
- }
- }
- mutex_unlock(&pmon->lock);
- return wr_cnt;
-}
-
-static const struct file_operations event_enable_file_ops = {
- .open = iommu_pm_debug_open,
- .read = iommu_pm_enable_counters_read,
- .write = iommu_pm_enable_counters_write,
-};
-
-static ssize_t iommu_pm_avail_event_cls_read(struct file *fp,
- char __user *user_buff,
- size_t count, loff_t *pos)
-{
- size_t rd_cnt = 0;
- struct iommu_pmon *pmon = fp->private_data;
- char *buf;
- size_t len;
-
- mutex_lock(&pmon->lock);
-
- len = iommu_pm_create_sup_cls_str(&buf, pmon);
- if (buf) {
- rd_cnt = simple_read_from_buffer(user_buff, count, pos,
- buf, len);
- kfree(buf);
- }
- mutex_unlock(&pmon->lock);
- return rd_cnt;
-}
-
-static const struct file_operations available_event_cls_file_ops = {
- .open = iommu_pm_debug_open,
- .read = iommu_pm_avail_event_cls_read,
-};
-
-
-
-static int iommu_pm_create_grp_debugfs_counters_hierarchy(
- struct iommu_pmon_cnt_group *cnt_grp,
- unsigned int *abs_counter_no)
-{
- int ret = 0;
- int j;
- char name[20];
-
- for (j = 0; j < cnt_grp->num_counters; ++j) {
- struct dentry *grp_dir = cnt_grp->group_dir;
- struct dentry *counter_dir;
- cnt_grp->counters[j].cnt_group = cnt_grp;
- cnt_grp->counters[j].counter_no = j;
- cnt_grp->counters[j].absolute_counter_no = *abs_counter_no;
- (*abs_counter_no)++;
- cnt_grp->counters[j].value = 0;
- cnt_grp->counters[j].overflow_count = 0;
- cnt_grp->counters[j].current_event_class =
- MSM_IOMMU_PMU_NO_EVENT_CLASS;
-
- snprintf(name, 20, "counter%u", j);
-
- counter_dir = debugfs_create_dir(name, grp_dir);
-
- if (IS_ERR_OR_NULL(counter_dir)) {
- pr_err("unable to create counter debugfs dir %s\n",
- name);
- ret = -ENOMEM;
- goto out;
- }
-
- cnt_grp->counters[j].counter_dir = counter_dir;
-
- if (!debugfs_create_file("value", 0644, counter_dir,
- &cnt_grp->counters[j],
- &cnt_value_file_ops)) {
- ret = -EIO;
- goto out;
- }
-
- if (!debugfs_create_file("current_event_class", 0644,
- counter_dir, &cnt_grp->counters[j],
- &event_class_file_ops)) {
- ret = -EIO;
- goto out;
- }
- }
-out:
- return ret;
-}
-
-static int iommu_pm_create_group_debugfs_hierarchy(struct iommu_info *iommu,
- struct iommu_pmon *pmon_entry)
-{
- int i;
- int ret = 0;
- char name[20];
- unsigned int abs_counter_no = 0;
-
- for (i = 0; i < pmon_entry->num_groups; ++i) {
- pmon_entry->cnt_grp[i].pmon = pmon_entry;
- pmon_entry->cnt_grp[i].grp_no = i;
- pmon_entry->cnt_grp[i].num_counters = pmon_entry->num_counters;
- pmon_entry->cnt_grp[i].counters =
- kzalloc(sizeof(*pmon_entry->cnt_grp[i].counters)
- * pmon_entry->cnt_grp[i].num_counters, GFP_KERNEL);
-
- if (!pmon_entry->cnt_grp[i].counters) {
- pr_err("Unable to allocate memory for counters\n");
- ret = -ENOMEM;
- goto out;
- }
- snprintf(name, 20, "group%u", i);
- pmon_entry->cnt_grp[i].group_dir = debugfs_create_dir(name,
- pmon_entry->iommu_dir);
- if (IS_ERR_OR_NULL(pmon_entry->cnt_grp[i].group_dir)) {
- pr_err("unable to create group debugfs dir %s\n", name);
- ret = -ENOMEM;
- goto out;
- }
-
- ret = iommu_pm_create_grp_debugfs_counters_hierarchy(
- &pmon_entry->cnt_grp[i],
- &abs_counter_no);
- if (ret)
- goto out;
- }
-out:
- return ret;
-}
-
-int msm_iommu_pm_iommu_register(struct iommu_pmon *pmon_entry)
-{
- int ret = 0;
- struct iommu_info *iommu = &pmon_entry->iommu;
- int i;
-
- if (!iommu->ops || !iommu->iommu_name || !iommu->base
- || !iommu->iommu_dev) {
- ret = -EINVAL;
- goto out;
- }
-
- if (!msm_iommu_root_debugfs_dir) {
- msm_iommu_root_debugfs_dir = debugfs_create_dir("iommu", NULL);
- if (IS_ERR_OR_NULL(msm_iommu_root_debugfs_dir)) {
- pr_err("Failed creating iommu debugfs dir \"iommu\"\n");
- ret = -EIO;
- goto out;
- }
- }
-
- pmon_entry->cnt_grp = kzalloc(sizeof(*pmon_entry->cnt_grp)
- * pmon_entry->num_groups, GFP_KERNEL);
- if (!pmon_entry->cnt_grp) {
- pr_err("Unable to allocate memory for counter groups\n");
- ret = -ENOMEM;
- goto file_err;
- }
- pmon_entry->iommu_dir = debugfs_create_dir(iommu->iommu_name,
- msm_iommu_root_debugfs_dir);
- if (IS_ERR_OR_NULL(pmon_entry->iommu_dir)) {
- pr_err("unable to create iommu debugfs dir %s\n",
- iommu->iommu_name);
- ret = -ENOMEM;
- goto free_mem;
- }
-
- if (!debugfs_create_file("reset_counters", 0644,
- pmon_entry->iommu_dir, pmon_entry, &reset_file_ops)) {
- ret = -EIO;
- goto free_mem;
- }
-
- if (!debugfs_create_file("enable_counters", 0644,
- pmon_entry->iommu_dir, pmon_entry, &event_enable_file_ops)) {
- ret = -EIO;
- goto free_mem;
- }
-
- if (!debugfs_create_file("available_event_classes", 0644,
- pmon_entry->iommu_dir, pmon_entry,
- &available_event_cls_file_ops)) {
- ret = -EIO;
- goto free_mem;
- }
-
- ret = iommu_pm_create_group_debugfs_hierarchy(iommu, pmon_entry);
- if (ret)
- goto free_mem;
-
- iommu->hw_ops->initialize_hw(pmon_entry);
-
- if (iommu->evt_irq > 0) {
- ret = request_threaded_irq(iommu->evt_irq, NULL,
- iommu->hw_ops->evt_ovfl_int_handler,
- IRQF_ONESHOT | IRQF_SHARED,
- "msm_iommu_pmon_nonsecure_irq", pmon_entry);
- if (ret) {
- pr_err("Request IRQ %d failed with ret=%d\n",
- iommu->evt_irq,
- ret);
- goto free_mem;
- }
- } else {
- pr_info("%s: Overflow interrupt not available\n", __func__);
- }
-
- dev_dbg(iommu->iommu_dev, "%s iommu registered\n", iommu->iommu_name);
-
- goto out;
-free_mem:
- if (pmon_entry->cnt_grp) {
- for (i = 0; i < pmon_entry->num_groups; ++i) {
- kfree(pmon_entry->cnt_grp[i].counters);
- pmon_entry->cnt_grp[i].counters = 0;
- }
- }
- kfree(pmon_entry->cnt_grp);
- pmon_entry->cnt_grp = 0;
-file_err:
- debugfs_remove_recursive(msm_iommu_root_debugfs_dir);
-out:
- return ret;
-}
-EXPORT_SYMBOL(msm_iommu_pm_iommu_register);
-
-void msm_iommu_pm_iommu_unregister(struct device *dev)
-{
- int i;
- struct iommu_pmon *pmon_entry = iommu_pm_get_pm_by_dev(dev);
-
- if (!pmon_entry)
- return;
-
- free_irq(pmon_entry->iommu.evt_irq, pmon_entry->iommu.iommu_dev);
-
- if (!pmon_entry)
- goto remove_debugfs;
-
- if (pmon_entry->cnt_grp) {
- for (i = 0; i < pmon_entry->num_groups; ++i)
- kfree(pmon_entry->cnt_grp[i].counters);
- }
-
- kfree(pmon_entry->cnt_grp);
-
-remove_debugfs:
- debugfs_remove_recursive(msm_iommu_root_debugfs_dir);
-
- return;
-}
-EXPORT_SYMBOL(msm_iommu_pm_iommu_unregister);
-
-struct iommu_pmon *msm_iommu_pm_alloc(struct device *dev)
-{
- struct iommu_pmon *pmon_entry;
- struct iommu_info *info;
- pmon_entry = devm_kzalloc(dev, sizeof(*pmon_entry), GFP_KERNEL);
- if (!pmon_entry)
- return NULL;
- info = &pmon_entry->iommu;
- info->iommu_dev = dev;
- mutex_init(&pmon_entry->lock);
- iommu_pm_add_to_iommu_list(pmon_entry);
- return pmon_entry;
-}
-EXPORT_SYMBOL(msm_iommu_pm_alloc);
-
-void msm_iommu_pm_free(struct device *dev)
-{
- struct iommu_pmon *pmon = iommu_pm_get_pm_by_dev(dev);
- if (pmon)
- iommu_pm_del_from_iommu_list(pmon);
-}
-EXPORT_SYMBOL(msm_iommu_pm_free);
-
-void msm_iommu_attached(struct device *dev)
-{
- struct iommu_pmon *pmon = iommu_pm_get_pm_by_dev(dev);
- if (pmon) {
- mutex_lock(&pmon->lock);
- ++pmon->iommu_attach_count;
- if (pmon->iommu_attach_count == 1) {
- /* If perf. mon was enabled before we attached we do
- * the actual enabling after we attach.
- */
- if (pmon->enabled && !pmon->iommu.always_on)
- iommu_pm_on(pmon);
- }
- mutex_unlock(&pmon->lock);
- }
-}
-EXPORT_SYMBOL(msm_iommu_attached);
-
-void msm_iommu_detached(struct device *dev)
-{
- struct iommu_pmon *pmon = iommu_pm_get_pm_by_dev(dev);
- if (pmon) {
- mutex_lock(&pmon->lock);
- if (pmon->iommu_attach_count == 1) {
- /* If perf. mon is still enabled we have to disable
- * before we do the detach if iommu is not always on.
- */
- if (pmon->enabled && !pmon->iommu.always_on)
- iommu_pm_off(pmon);
- }
- BUG_ON(pmon->iommu_attach_count == 0);
- --pmon->iommu_attach_count;
- mutex_unlock(&pmon->lock);
- }
-}
-EXPORT_SYMBOL(msm_iommu_detached);
-
#include <linux/of_device.h>
#include <linux/kmemleak.h>
#include <linux/dma-mapping.h>
-#include <soc/qcom/scm.h>
+#include <linux/qcom_scm.h>
#include <asm/cacheflush.h>
#include <asm/sizes.h>
struct msm_scm_fault_regs_dump *regs)
{
int ret;
- struct scm_desc desc = {0};
-
- struct msm_scm_fault_regs_dump_req {
- uint32_t id;
- uint32_t cb_num;
- uint32_t buff;
- uint32_t len;
- } req_info;
- int resp = 0;
-
- desc.args[0] = req_info.id = smmu_id;
- desc.args[1] = req_info.cb_num = cb_num;
- desc.args[2] = req_info.buff = virt_to_phys(regs);
- desc.args[3] = req_info.len = sizeof(*regs);
- desc.arginfo = SCM_ARGS(4, SCM_VAL, SCM_VAL, SCM_RW, SCM_VAL);
dmac_clean_range(regs, regs + 1);
- if (!is_scm_armv8())
- ret = scm_call(SCM_SVC_UTIL, IOMMU_DUMP_SMMU_FAULT_REGS,
- &req_info, sizeof(req_info), &resp, 1);
- else
- ret = scm_call2(SCM_SIP_FNID(SCM_SVC_UTIL,
- IOMMU_DUMP_SMMU_FAULT_REGS), &desc);
+
+ ret = qcom_scm_iommu_dump_fault_regs(smmu_id, cb_num,
+ virt_to_phys(regs), sizeof(*regs));
+
dmac_inv_range(regs, regs + 1);
return ret;
return ret;
}
+#define SCM_SVC_MP 0xc
+
static int msm_iommu_sec_ptbl_init(void)
{
struct device_node *np;
- struct msm_scm_ptbl_init {
- unsigned int paddr;
- unsigned int size;
- unsigned int spare;
- } pinit = {0};
int psize[2] = {0, 0};
- unsigned int spare;
- int ret, ptbl_ret = 0;
+ unsigned int spare = 0;
+ int ret;
int version;
/* Use a dummy device for dma_alloc_attrs allocation */
struct device dev = { 0 };
void *cpu_addr;
dma_addr_t paddr;
DEFINE_DMA_ATTRS(attrs);
- struct scm_desc desc = {0};
for_each_matching_node(np, msm_smmu_list)
if (of_find_property(np, "qcom,iommu-secure-id", NULL) &&
of_node_put(np);
- version = scm_get_feat_version(SCM_SVC_MP);
-
- pr_err("iommu sec: version:%x, is_scm_armv8:%d\n", version,
- is_scm_armv8());
+ version = qcom_scm_get_feat_version(SCM_SVC_MP);
if (version >= MAKE_VERSION(1, 1, 1)) {
- struct msm_cp_pool_size psize;
- int retval;
- struct scm_desc desc = {0};
-
- desc.args[0] = psize.size = MAXIMUM_VIRT_SIZE;
- desc.args[1] = psize.spare = 0;
- desc.arginfo = SCM_ARGS(2);
-
- if (!is_scm_armv8())
- ret = scm_call(SCM_SVC_MP, IOMMU_SET_CP_POOL_SIZE,
- &psize, sizeof(psize), &retval,
- sizeof(retval));
- else
- ret = scm_call2(SCM_SIP_FNID(SCM_SVC_MP,
- IOMMU_SET_CP_POOL_SIZE), &desc);
-
+ ret = qcom_scm_iommu_set_cp_pool_size(MAXIMUM_VIRT_SIZE, 0);
if (ret) {
pr_err("scm call IOMMU_SET_CP_POOL_SIZE failed\n");
goto fail;
}
-
}
- if (!is_scm_armv8()) {
- ret = scm_call(SCM_SVC_MP, IOMMU_SECURE_PTBL_SIZE, &spare,
- sizeof(spare), psize, sizeof(psize));
- } else {
- struct scm_desc desc = {0};
-
- desc.args[0] = spare;
- desc.arginfo = SCM_ARGS(1);
- ret = scm_call2(SCM_SIP_FNID(SCM_SVC_MP,
- IOMMU_SECURE_PTBL_SIZE), &desc);
- psize[0] = desc.ret[0];
- psize[1] = desc.ret[1];
- }
+ ret = qcom_scm_iommu_secure_ptbl_size(spare, psize);
if (ret) {
pr_err("scm call IOMMU_SECURE_PTBL_SIZE failed\n");
goto fail;
}
+ pr_err("iommu sec: psize[0]: %d, psize[1]: %d\n", psize[0], psize[1]);
+
if (psize[1]) {
pr_err("scm call IOMMU_SECURE_PTBL_SIZE failed\n");
goto fail;
dma_set_attr(DMA_ATTR_NO_KERNEL_MAPPING, &attrs);
dev.coherent_dma_mask = DMA_BIT_MASK(sizeof(dma_addr_t) * 8);
- pr_err("iommu sec: psize[0]: %d\n", psize[0]);
-
cpu_addr = dma_alloc_attrs(&dev, psize[0], &paddr, GFP_KERNEL, &attrs);
if (!cpu_addr) {
pr_err("%s: Failed to allocate %d bytes for PTBL\n",
goto fail;
}
- desc.args[0] = pinit.paddr = (unsigned int)paddr;
- desc.args[1] = pinit.size = psize[0];
- desc.args[2] = pinit.spare;
- desc.arginfo = SCM_ARGS(3, SCM_RW, SCM_VAL, SCM_VAL);
+ ret = qcom_scm_iommu_secure_ptbl_init(paddr, psize[0], spare);
- if (!is_scm_armv8()) {
- ret = scm_call(SCM_SVC_MP, IOMMU_SECURE_PTBL_INIT, &pinit,
- sizeof(pinit), &ptbl_ret, sizeof(ptbl_ret));
- } else {
- ret = scm_call2(SCM_SIP_FNID(SCM_SVC_MP,
- IOMMU_SECURE_PTBL_INIT), &desc);
- ptbl_ret = desc.ret[0];
- }
if (ret) {
pr_err("scm call IOMMU_SECURE_PTBL_INIT failed (%d)\n", ret);
goto fail_mem;
}
- if (ptbl_ret) {
- pr_err("scm call IOMMU_SECURE_PTBL_INIT extended ret fail (%d)\n",
- ptbl_ret);
- goto fail_mem;
- }
return 0;
int msm_iommu_sec_program_iommu(struct msm_iommu_drvdata *drvdata,
struct msm_iommu_ctx_drvdata *ctx_drvdata)
{
- int ret, scm_ret = 0;
-
if (drvdata->smmu_local_base) {
- writel_relaxed(0xFFFFFFFF, drvdata->smmu_local_base +
- SMMU_INTR_SEL_NS);
+ writel_relaxed(0xFFFFFFFF,
+ drvdata->smmu_local_base + SMMU_INTR_SEL_NS);
mb();
}
- ret = scm_restore_sec_cfg(drvdata->sec_id, ctx_drvdata->num, &scm_ret);
- if (ret || scm_ret) {
- pr_err("scm call IOMMU_SECURE_CFG failed\n");
- return ret ? ret : -EINVAL;
- }
-
- return ret;
+ return qcom_scm_restore_sec_cfg(drvdata->sec_id, ctx_drvdata->num);
}
static int msm_iommu_sec_map2(struct msm_scm_map2_req *map)
{
- struct scm_desc desc = {0};
- u32 resp;
- int ret;
+ u32 flags;
- desc.args[0] = map->plist.list;
- desc.args[1] = map->plist.list_size;
- desc.args[2] = map->plist.size;
- desc.args[3] = map->info.id;
- desc.args[4] = map->info.ctx_id;
- desc.args[5] = map->info.va;
- desc.args[6] = map->info.size;
#ifdef CONFIG_MSM_IOMMU_TLBINVAL_ON_MAP
- desc.args[7] = map->flags = IOMMU_TLBINVAL_FLAG;
+ flags = IOMMU_TLBINVAL_FLAG;
#else
- desc.args[7] = map->flags = 0;
+ flags = 0;
#endif
- desc.arginfo = SCM_ARGS(8, SCM_RW, SCM_VAL, SCM_VAL, SCM_VAL, SCM_VAL,
- SCM_VAL, SCM_VAL, SCM_VAL);
- if (!is_scm_armv8()) {
- ret = scm_call(SCM_SVC_MP, IOMMU_SECURE_MAP2, map, sizeof(*map),
- &resp, sizeof(resp));
- } else {
- ret = scm_call2(SCM_SIP_FNID(SCM_SVC_MP,
- IOMMU_SECURE_MAP2_FLAT), &desc);
- resp = desc.ret[0];
- }
- if (ret || resp)
- return -EINVAL;
- return 0;
+ return qcom_scm_iommu_secure_map(map->plist.list,
+ map->plist.list_size,
+ map->plist.size,
+ map->info.id,
+ map->info.ctx_id,
+ map->info.va,
+ map->info.size,
+ flags);
}
static int msm_iommu_sec_ptbl_map(struct msm_iommu_drvdata *iommu_drvdata,
flush_va = pa_list;
}
-/* trace_iommu_sec_ptbl_map_range_start(map.info.id, map.info.ctx_id, va,
- pa, len);*/
-
/*
* Ensure that the buffer is in RAM by the time it gets to TZ
*/
ret = msm_iommu_sec_map2(&map);
kfree(pa_list);
-/* trace_iommu_sec_ptbl_map_range_end(map.info.id, map.info.ctx_id, va, pa,
- len);*/
-
return ret;
}
static int msm_iommu_sec_ptbl_unmap(struct msm_iommu_drvdata *iommu_drvdata,
- struct msm_iommu_ctx_drvdata *ctx_drvdata,
- unsigned long va, size_t len)
+ struct msm_iommu_ctx_drvdata *ctx_drvdata,
+ unsigned long va, size_t len)
{
- struct msm_scm_unmap2_req unmap;
- int ret, scm_ret;
- struct scm_desc desc = {0};
-
if (!IS_ALIGNED(va, SZ_1M) || !IS_ALIGNED(len, SZ_1M))
return -EINVAL;
- desc.args[0] = unmap.info.id = iommu_drvdata->sec_id;
- desc.args[1] = unmap.info.ctx_id = ctx_drvdata->num;
- desc.args[2] = unmap.info.va = va;
- desc.args[3] = unmap.info.size = len;
- desc.args[4] = unmap.flags = IOMMU_TLBINVAL_FLAG;
- desc.arginfo = SCM_ARGS(5);
-
- if (!is_scm_armv8())
- ret = scm_call(SCM_SVC_MP, IOMMU_SECURE_UNMAP2, &unmap,
- sizeof(unmap), &scm_ret, sizeof(scm_ret));
- else
- ret = scm_call2(SCM_SIP_FNID(SCM_SVC_MP,
- IOMMU_SECURE_UNMAP2_FLAT), &desc);
- return ret;
+ return qcom_scm_iommu_secure_unmap(iommu_drvdata->sec_id,
+ ctx_drvdata->num,
+ va,
+ len,
+ IOMMU_TLBINVAL_FLAG);
}
static int msm_iommu_domain_init(struct iommu_domain *domain)
return 0;
}
-static phys_addr_t msm_iommu_get_pt_base_addr(struct iommu_domain *domain)
-{
- return 0;
-}
-
void msm_iommu_check_scm_call_avail(void)
{
- is_secure = scm_is_call_available(SCM_SVC_MP, IOMMU_SECURE_CFG);
+ is_secure = qcom_scm_is_call_available(SCM_SVC_MP, IOMMU_SECURE_CFG);
}
int msm_iommu_get_scm_call_avail(void)
return is_secure;
}
-/*
- * VFE SMMU is changing from being non-secure to being secure.
- * For backwards compatibility we need to check whether the secure environment
- * has support for this.
- */
-static s32 secure_camera_enabled = -1;
-int is_vfe_secure(void)
-{
- if (secure_camera_enabled == -1) {
- u32 ver = scm_get_feat_version(SCM_SVC_SEC_CAMERA);
- secure_camera_enabled = ver >= MAKE_VERSION(1, 0, 0);
- }
-
- return secure_camera_enabled;
-}
-
static struct iommu_ops msm_iommu_ops = {
.domain_init = msm_iommu_domain_init,
.domain_destroy = msm_iommu_domain_destroy,
.detach_dev = msm_iommu_detach_dev,
.map = msm_iommu_map,
.unmap = msm_iommu_unmap,
-// .map_range = msm_iommu_map_range,
+/* .map_range = msm_iommu_map_range,*/
.map_sg = default_iommu_map_sg,
-// .unmap_range = msm_iommu_unmap_range,
+/* .unmap_range = msm_iommu_unmap_range,*/
.iova_to_phys = msm_iommu_iova_to_phys,
-// .get_pt_base_addr = msm_iommu_get_pt_base_addr,
.pgsize_bitmap = MSM_IOMMU_PGSIZES,
};
uint32_t turn;
};
-#ifdef CONFIG_QCOM_IOMMU
+#ifdef CONFIG_QCOM_IOMMU_V1
void *msm_iommu_lock_initialize(void);
void msm_iommu_mutex_lock(void);
void msm_iommu_mutex_unlock(void);
}
#endif
-#ifdef CONFIG_QCOM_IOMMU_SYNC
-void msm_iommu_remote_p0_spin_lock(unsigned int need_lock);
-void msm_iommu_remote_p0_spin_unlock(unsigned int need_lock);
-
-#define msm_iommu_remote_lock_init() _msm_iommu_remote_spin_lock_init()
-#define msm_iommu_remote_spin_lock(need_lock) \
- msm_iommu_remote_p0_spin_lock(need_lock)
-#define msm_iommu_remote_spin_unlock(need_lock) \
- msm_iommu_remote_p0_spin_unlock(need_lock)
-#else
-#define msm_iommu_remote_lock_init()
-#define msm_iommu_remote_spin_lock(need_lock)
-#define msm_iommu_remote_spin_unlock(need_lock)
-#endif
-
-#ifdef CONFIG_QCOM_IOMMU
+#ifdef CONFIG_QCOM_IOMMU_V1
/*
* Look up an IOMMU context device by its context name. NULL if none found.
* Useful for testing and drivers that do not yet fully have IOMMU stuff in
/* events for notifiers passed to msm_iommu_register_notify */
#define TLB_SYNC_TIMEOUT 1
-#ifdef CONFIG_QCOM_IOMMU
+#ifdef CONFIG_QCOM_IOMMU_V1
void msm_iommu_register_notify(struct notifier_block *nb);
#else
static inline void msm_iommu_register_notify(struct notifier_block *nb)