Merge commit 'AU_LINUX_ANDROID_ICS.04.00.04.00.126' into msm-3.4
AU_LINUX_ANDROID_ICS.04.00.04.00.126 from msm-3.0.
First parent is from google/android-3.4.
* commit 'AU_LINUX_ANDROID_ICS.04.00.04.00.126': (8712 commits)
PRNG: Device tree entry for qrng device.
vidc:1080p: Set video core timeout value for Thumbnail mode
msm: sps: improve the debugging support in SPS driver
board-8064 msm: Overlap secure and non secure video firmware heaps.
msm: clock: Add handoff ops for 7x30 and copper XO clocks
msm_fb: display: Wait for external vsync before DTV IOMMU unmap
msm: Fix ciruclar dependency in debug UART settings
msm: gdsc: Add GDSC regulator driver for msm-copper
defconfig: Enable Mobicore Driver.
mobicore: Add mobicore driver.
mobicore: rename variable to lower case.
mobicore: rename folder.
mobicore: add makefiles
mobicore: initial import of kernel driver
ASoC: msm: Add SLIMBUS_2_RX CPU DAI
board-8064-gpio: Update FUNC for EPM SPI CS
msm_fb: display: Remove chicken bit config during video playback
mmc: msm_sdcc: enable the sanitize capability
msm-fb: display: lm2 writeback support on mpq platfroms
msm_fb: display: Disable LVDS phy & pll during panel off
...
Signed-off-by: Steve Muckle <smuckle@codeaurora.org>
diff --git a/arch/arm/mach-msm/iommu_domains.c b/arch/arm/mach-msm/iommu_domains.c
new file mode 100644
index 0000000..fec27bd
--- /dev/null
+++ b/arch/arm/mach-msm/iommu_domains.c
@@ -0,0 +1,443 @@
+/* Copyright (c) 2010-2012, Code Aurora Forum. 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/memory_alloc.h>
+#include <linux/platform_device.h>
+#include <linux/vmalloc.h>
+#include <linux/rbtree.h>
+#include <linux/slab.h>
+#include <linux/vmalloc.h>
+#include <asm/sizes.h>
+#include <asm/page.h>
+#include <mach/iommu.h>
+#include <mach/iommu_domains.h>
+#include <mach/socinfo.h>
+#include <mach/msm_subsystem_map.h>
+
+/* dummy 64K for overmapping */
+char iommu_dummy[2*SZ_64K-4];
+
+struct msm_iova_data {
+ struct rb_node node;
+ struct mem_pool *pools;
+ int npools;
+ struct iommu_domain *domain;
+ int domain_num;
+};
+
+static struct rb_root domain_root;
+DEFINE_MUTEX(domain_mutex);
+static atomic_t domain_nums = ATOMIC_INIT(-1);
+
+int msm_iommu_map_extra(struct iommu_domain *domain,
+ unsigned long start_iova,
+ unsigned long size,
+ unsigned long page_size,
+ int cached)
+{
+ int i, ret_value = 0;
+ unsigned long order = get_order(page_size);
+ unsigned long aligned_size = ALIGN(size, page_size);
+ unsigned long nrpages = aligned_size >> (PAGE_SHIFT + order);
+ unsigned long phy_addr = ALIGN(virt_to_phys(iommu_dummy), page_size);
+ unsigned long temp_iova = start_iova;
+
+ for (i = 0; i < nrpages; i++) {
+ int ret = iommu_map(domain, temp_iova, phy_addr, page_size,
+ cached);
+ if (ret) {
+ pr_err("%s: could not map %lx in domain %p, error: %d\n",
+ __func__, start_iova, domain, ret);
+ ret_value = -EAGAIN;
+ goto out;
+ }
+ temp_iova += page_size;
+ }
+ return ret_value;
+out:
+ for (; i > 0; --i) {
+ temp_iova -= page_size;
+ iommu_unmap(domain, start_iova, page_size);
+ }
+ return ret_value;
+}
+
+void msm_iommu_unmap_extra(struct iommu_domain *domain,
+ unsigned long start_iova,
+ unsigned long size,
+ unsigned long page_size)
+{
+ int i;
+ unsigned long order = get_order(page_size);
+ unsigned long aligned_size = ALIGN(size, page_size);
+ unsigned long nrpages = aligned_size >> (PAGE_SHIFT + order);
+ unsigned long temp_iova = start_iova;
+
+ for (i = 0; i < nrpages; ++i) {
+ iommu_unmap(domain, temp_iova, page_size);
+ temp_iova += page_size;
+ }
+}
+
+static int msm_iommu_map_iova_phys(struct iommu_domain *domain,
+ unsigned long iova,
+ unsigned long phys,
+ unsigned long size,
+ int cached)
+{
+ int ret;
+ struct scatterlist *sglist;
+
+ 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, cached);
+ if (ret) {
+ 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(unsigned long phys,
+ unsigned int domain_no,
+ unsigned int partition_no,
+ unsigned long size,
+ unsigned long align,
+ unsigned long cached,
+ unsigned long *iova_val)
+{
+ unsigned long iova;
+ int ret;
+
+ if (size & (align - 1))
+ return -EINVAL;
+
+ ret = msm_allocate_iova_address(domain_no, partition_no, size, align,
+ &iova);
+
+ if (ret)
+ return -ENOMEM;
+
+ ret = msm_iommu_map_iova_phys(msm_get_iommu_domain(domain_no), iova,
+ phys, size, cached);
+
+ if (ret)
+ msm_free_iova_address(iova, domain_no, partition_no, size);
+ else
+ *iova_val = iova;
+
+ return ret;
+}
+
+void msm_iommu_unmap_contig_buffer(unsigned long iova,
+ unsigned int domain_no,
+ unsigned int partition_no,
+ unsigned long size)
+{
+ iommu_unmap_range(msm_get_iommu_domain(domain_no), iova, size);
+ msm_free_iova_address(iova, domain_no, partition_no, size);
+}
+
+static struct msm_iova_data *find_domain(int domain_num)
+{
+ struct rb_root *root = &domain_root;
+ struct rb_node *p = root->rb_node;
+
+ mutex_lock(&domain_mutex);
+
+ 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 > 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;
+}
+
+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;
+ else
+ return NULL;
+}
+
+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;
+
+ 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;
+
+ va = gen_pool_alloc_aligned(pool->gpool, size, ilog2(align));
+ 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;
+
+ gen_pool_free(pool->gpool, iova, size);
+}
+
+int msm_register_domain(struct msm_iova_layout *layout)
+{
+ int i;
+ struct msm_iova_data *data;
+ struct mem_pool *pools;
+
+ if (!layout)
+ return -EINVAL;
+
+ data = kmalloc(sizeof(*data), GFP_KERNEL);
+
+ if (!data)
+ return -ENOMEM;
+
+ pools = kmalloc(sizeof(struct mem_pool) * layout->npartitions,
+ GFP_KERNEL);
+
+ if (!pools)
+ goto out;
+
+ 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;
+
+ /*
+ * 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;
+ }
+ }
+
+ data->pools = pools;
+ data->npools = layout->npartitions;
+ data->domain_num = atomic_inc_return(&domain_nums);
+ data->domain = iommu_domain_alloc(&platform_bus_type,
+ layout->domain_flags);
+
+ add_domain(data);
+
+ return data->domain_num;
+
+out:
+ kfree(data);
+
+ return -EINVAL;
+}
+
+int msm_use_iommu()
+{
+ return iommu_present(&platform_bus_type);
+}
+
+static int __init iommu_domain_probe(struct platform_device *pdev)
+{
+ struct iommu_domains_pdata *p = pdev->dev.platform_data;
+ int i, j;
+
+ 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 = msm_iommu_get_ctx(
+ p->domain_names[i].name);
+ struct iommu_domain *domain;
+
+ 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 struct platform_driver iommu_domain_driver = {
+ .driver = {
+ .name = "iommu_domains",
+ .owner = THIS_MODULE
+ },
+};
+
+static int __init msm_subsystem_iommu_init(void)
+{
+ return platform_driver_probe(&iommu_domain_driver, iommu_domain_probe);
+}
+device_initcall(msm_subsystem_iommu_init);