Initial Contribution

msm-2.6.38: tag AU_LINUX_ANDROID_GINGERBREAD.02.03.04.00.142

Signed-off-by: Bryan Huntsman <bryanh@codeaurora.org>
diff --git a/arch/arm/mm/vcm_mm.c b/arch/arm/mm/vcm_mm.c
new file mode 100644
index 0000000..dee51fa
--- /dev/null
+++ b/arch/arm/mm/vcm_mm.c
@@ -0,0 +1,253 @@
+/* Copyright (c) 2010, 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.
+ */
+
+/* Architecture-specific VCM functions */
+
+#include <linux/kernel.h>
+#include <linux/vcm_mm.h>
+
+#include <asm/pgtable-hwdef.h>
+#include <asm/tlbflush.h>
+
+#define MRC(reg, processor, op1, crn, crm, op2)				\
+__asm__ __volatile__ (							\
+"   mrc   "   #processor "," #op1 ", %0,"  #crn "," #crm "," #op2 " \n" \
+: "=r" (reg))
+
+#define RCP15_PRRR(reg)		MRC(reg, p15, 0, c10, c2, 0)
+#define RCP15_NMRR(reg) 	MRC(reg, p15, 0, c10, c2, 1)
+
+
+/* Local type attributes (not the same as VCM) */
+#define ARM_MT_NORMAL		2
+#define ARM_MT_STRONGLYORDERED	0
+#define ARM_MT_DEVICE		1
+
+#define ARM_CP_NONCACHED	0
+#define ARM_CP_WB_WA		1
+#define ARM_CP_WB_NWA		3
+#define ARM_CP_WT_NWA		2
+
+#define smmu_err(a, ...)						\
+	pr_err("ERROR %s %i " a, __func__, __LINE__, ##__VA_ARGS__)
+
+#define FL_OFFSET(va)	(((va) & 0xFFF00000) >> 20)
+#define SL_OFFSET(va)	(((va) & 0xFF000) >> 12)
+
+int vcm_driver_tex_class[4];
+
+static int find_tex_class(int icp, int ocp, int mt, int nos)
+{
+	int i = 0;
+	unsigned int prrr = 0;
+	unsigned int nmrr = 0;
+	int c_icp, c_ocp, c_mt, c_nos;
+
+	RCP15_PRRR(prrr);
+	RCP15_NMRR(nmrr);
+
+	/* There are only 8 classes on this architecture */
+	/* If they add more classes, registers will VASTLY change */
+	for (i = 0; i < 8; i++)	{
+		c_nos = prrr & (1 << (i + 24)) ? 1 : 0;
+		c_mt = (prrr & (3 << (i * 2))) >> (i * 2);
+		c_icp = (nmrr & (3 << (i * 2))) >> (i * 2);
+		c_ocp = (nmrr & (3 << (i * 2 + 16))) >> (i * 2 + 16);
+
+		if (icp == c_icp && ocp == c_ocp && c_mt == mt && c_nos == nos)
+			return i;
+	}
+	smmu_err("Could not find TEX class for ICP=%d, OCP=%d, MT=%d, NOS=%d\n",
+		 icp, ocp, mt, nos);
+
+	/* In reality, we may want to remove this panic. Some classes just */
+	/* will not be available, and will fail in smmu_set_attr */
+	panic("SMMU: Could not determine TEX attribute mapping.\n");
+	return -1;
+}
+
+
+int vcm_setup_tex_classes(void)
+{
+	unsigned int cpu_prrr;
+	unsigned int cpu_nmrr;
+
+	if (!(get_cr() & CR_TRE))	/* No TRE? */
+		panic("TEX remap not enabled, but the SMMU driver needs it!\n");
+
+	RCP15_PRRR(cpu_prrr);
+	RCP15_NMRR(cpu_nmrr);
+
+	vcm_driver_tex_class[VCM_DEV_ATTR_NONCACHED] =
+		find_tex_class(ARM_CP_NONCACHED, ARM_CP_NONCACHED,
+			       ARM_MT_NORMAL, 1);
+
+	vcm_driver_tex_class[VCM_DEV_ATTR_CACHED_WB_WA] =
+		find_tex_class(ARM_CP_WB_WA, ARM_CP_WB_WA,
+			       ARM_MT_NORMAL, 1);
+
+	vcm_driver_tex_class[VCM_DEV_ATTR_CACHED_WB_NWA] =
+		find_tex_class(ARM_CP_WB_NWA, ARM_CP_WB_NWA,
+			       ARM_MT_NORMAL, 1);
+
+	vcm_driver_tex_class[VCM_DEV_ATTR_CACHED_WT] =
+		find_tex_class(ARM_CP_WT_NWA, ARM_CP_WT_NWA,
+			       ARM_MT_NORMAL, 1);
+#ifdef DEBUG_TEX
+	printk(KERN_INFO "VCM driver debug: Using TEX classes: %d %d %d %d\n",
+	       vcm_driver_tex_class[VCM_DEV_ATTR_NONCACHED],
+	       vcm_driver_tex_class[VCM_DEV_ATTR_CACHED_WB_WA],
+	       vcm_driver_tex_class[VCM_DEV_ATTR_CACHED_WB_NWA],
+	       vcm_driver_tex_class[VCM_DEV_ATTR_CACHED_WT]);
+#endif
+	return 0;
+}
+
+
+int set_arm7_pte_attr(unsigned long pt_base, unsigned long va,
+					unsigned long len, unsigned int attr)
+{
+	unsigned long *fl_table = NULL;
+	unsigned long *fl_pte = NULL;
+	unsigned long fl_offset = 0;
+	unsigned long *sl_table = NULL;
+	unsigned long *sl_pte = NULL;
+	unsigned long sl_offset = 0;
+	int i;
+	int sh = 0;
+	int class = 0;
+
+	/* Alignment */
+	if (va & (len-1)) {
+		smmu_err("misaligned va: %p\n", (void *) va);
+		goto fail;
+	}
+	if (attr > 7) {
+		smmu_err("bad attribute: %d\n", attr);
+		goto fail;
+	}
+
+	sh = (attr & VCM_DEV_ATTR_SH) ? 1 : 0;
+	class = vcm_driver_tex_class[attr & 0x03];
+
+	if (class > 7 || class < 0) {	/* Bad class */
+		smmu_err("bad tex class: %d\n", class);
+		goto fail;
+	}
+
+	if (len != SZ_16M && len != SZ_1M &&
+	    len != SZ_64K && len != SZ_4K) {
+		smmu_err("bad size: %lu\n", len);
+		goto fail;
+	}
+
+	fl_table = (unsigned long *) pt_base;
+
+	if (!fl_table) {
+		smmu_err("null page table\n");
+		goto fail;
+	}
+
+	fl_offset = FL_OFFSET(va);	/* Upper 12 bits */
+	fl_pte = fl_table + fl_offset;	/* int pointers, 4 bytes */
+
+	if (*fl_pte == 0) {	/* Nothing there! */
+		smmu_err("first level pte is 0\n");
+		goto fail;
+	}
+
+	/* Supersection attributes */
+	if (len == SZ_16M) {
+		for (i = 0; i < 16; i++) {
+			/* Clear the old bits */
+			*(fl_pte+i) &= ~(PMD_SECT_S | PMD_SECT_CACHEABLE |
+					 PMD_SECT_BUFFERABLE | PMD_SECT_TEX(1));
+
+			/* Assign new class and S bit */
+			*(fl_pte+i) |= sh ? PMD_SECT_S : 0;
+			*(fl_pte+i) |= class & 0x01 ? PMD_SECT_BUFFERABLE : 0;
+			*(fl_pte+i) |= class & 0x02 ? PMD_SECT_CACHEABLE : 0;
+			*(fl_pte+i) |= class & 0x04 ? PMD_SECT_TEX(1) : 0;
+		}
+	} else	if (len == SZ_1M) {
+
+		/* Clear the old bits */
+		*(fl_pte) &= ~(PMD_SECT_S | PMD_SECT_CACHEABLE |
+			       PMD_SECT_BUFFERABLE | PMD_SECT_TEX(1));
+
+		/* Assign new class and S bit */
+		*(fl_pte) |= sh ? PMD_SECT_S : 0;
+		*(fl_pte) |= class & 0x01 ? PMD_SECT_BUFFERABLE : 0;
+		*(fl_pte) |= class & 0x02 ? PMD_SECT_CACHEABLE : 0;
+		*(fl_pte) |= class & 0x04 ? PMD_SECT_TEX(1) : 0;
+	}
+
+	sl_table = (unsigned long *) __va(((*fl_pte) & 0xFFFFFC00));
+	sl_offset = SL_OFFSET(va);
+	sl_pte = sl_table + sl_offset;
+
+	if (len == SZ_64K) {
+		for (i = 0; i < 16; i++) {
+			/* Clear the old bits */
+			*(sl_pte+i) &= ~(PTE_EXT_SHARED | PTE_CACHEABLE |
+					 PTE_BUFFERABLE | PTE_EXT_TEX(1));
+
+			/* Assign new class and S bit */
+			*(sl_pte+i) |= sh ? PTE_EXT_SHARED : 0;
+			*(sl_pte+i) |= class & 0x01 ? PTE_BUFFERABLE : 0;
+			*(sl_pte+i) |= class & 0x02 ? PTE_CACHEABLE : 0;
+			*(sl_pte+i) |= class & 0x04 ? PTE_EXT_TEX(1) : 0;
+		}
+	} else 	if (len == SZ_4K) {
+		/* Clear the old bits */
+		*(sl_pte) &= ~(PTE_EXT_SHARED | PTE_CACHEABLE |
+			       PTE_BUFFERABLE | PTE_EXT_TEX(1));
+
+		/* Assign new class and S bit */
+		*(sl_pte) |= sh ? PTE_EXT_SHARED : 0;
+		*(sl_pte) |= class & 0x01 ? PTE_BUFFERABLE : 0;
+		*(sl_pte) |= class & 0x02 ? PTE_CACHEABLE : 0;
+		*(sl_pte) |= class & 0x04 ? PTE_EXT_TEX(1) : 0;
+	}
+
+
+	mb();
+	return 0;
+fail:
+	return 1;
+}
+
+
+int cpu_set_attr(unsigned long va, unsigned long len, unsigned int attr)
+{
+	int ret;
+	pgd_t *pgd = init_mm.pgd;
+
+	if (!pgd) {
+		smmu_err("null pgd\n");
+		goto fail;
+	}
+
+	ret = set_arm7_pte_attr((unsigned long)pgd, va, len, attr);
+
+	if (ret != 0) {
+		smmu_err("could not set attribute: \
+					pgd=%p, va=%p, len=%lu, attr=%d\n",
+			 (void *) pgd, (void *) va, len, attr);
+		goto fail;
+	}
+	dmb();
+	flush_tlb_all();
+	return 0;
+fail:
+	return -1;
+}