[ARM] 3439/2: xsc3: add I/O coherency support

Patch from Lennert Buytenhek

This patch adds support for the I/O coherent cache available on the
xsc3.  The approach is to provide a simple API to determine whether the
chipset supports coherency by calling arch_is_coherent() and then
setting the appropriate system memory PTE and PMD bits.  In addition,
we call this API on dma_alloc_coherent() and dma_map_single() calls.
A generic version exists that will compile out all the coherency-related
code that is not needed on the majority of ARM systems.

Note that we do not check for coherency in the dma_alloc_writecombine()
function as that still requires a special PTE setting.  We also don't
touch dma_mmap_coherent() as that is a special ARM-only API that is by
definition only used on non-coherent system.

Signed-off-by: Deepak Saxena <dsaxena@plexity.net>
Signed-off-by: Lennert Buytenhek <buytenh@wantstofly.org>
Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
diff --git a/include/asm-arm/arch-ixp23xx/memory.h b/include/asm-arm/arch-ixp23xx/memory.h
index bebcf0a..6e19f46 100644
--- a/include/asm-arm/arch-ixp23xx/memory.h
+++ b/include/asm-arm/arch-ixp23xx/memory.h
@@ -28,6 +28,7 @@
  *		to an address that the kernel can use.
  */
 #ifndef __ASSEMBLY__
+#include <asm/mach-types.h>
 
 #define __virt_to_bus(v)						\
 	({ unsigned int ret;						\
@@ -40,6 +41,22 @@
 	data = *((volatile int *)IXP23XX_PCI_SDRAM_BAR);		\
 	 __phys_to_virt((((b - (data & 0xfffffff0)) + 0x00000000))); })
 
+/*
+ * Coherency support.  Only supported on A2 CPUs or on A1
+ * systems that have the cache coherency workaround.
+ */
+static inline int __ixp23xx_arch_is_coherent(void)
+{
+	extern unsigned int processor_id;
+
+	if (((processor_id & 15) >= 2) || machine_is_roadrunner())
+		return 1;
+
+	return 0;
+}
+
+#define arch_is_coherent()	__ixp23xx_arch_is_coherent()
+
 #endif
 
 
diff --git a/include/asm-arm/dma-mapping.h b/include/asm-arm/dma-mapping.h
index e3e8541..63ca741 100644
--- a/include/asm-arm/dma-mapping.h
+++ b/include/asm-arm/dma-mapping.h
@@ -47,7 +47,7 @@
 
 static inline int dma_is_consistent(dma_addr_t handle)
 {
-	return 0;
+	return !!arch_is_coherent();
 }
 
 /*
@@ -145,7 +145,9 @@
 dma_map_single(struct device *dev, void *cpu_addr, size_t size,
 	       enum dma_data_direction dir)
 {
-	consistent_sync(cpu_addr, size, dir);
+	if (!arch_is_coherent())
+		consistent_sync(cpu_addr, size, dir);
+
 	return virt_to_dma(dev, (unsigned long)cpu_addr);
 }
 #else
@@ -255,7 +257,9 @@
 
 		sg->dma_address = page_to_dma(dev, sg->page) + sg->offset;
 		virt = page_address(sg->page) + sg->offset;
-		consistent_sync(virt, sg->length, dir);
+
+		if (!arch_is_coherent())
+			consistent_sync(virt, sg->length, dir);
 	}
 
 	return nents;
@@ -310,14 +314,16 @@
 dma_sync_single_for_cpu(struct device *dev, dma_addr_t handle, size_t size,
 			enum dma_data_direction dir)
 {
-	consistent_sync((void *)dma_to_virt(dev, handle), size, dir);
+	if (!arch_is_coherent())
+		consistent_sync((void *)dma_to_virt(dev, handle), size, dir);
 }
 
 static inline void
 dma_sync_single_for_device(struct device *dev, dma_addr_t handle, size_t size,
 			   enum dma_data_direction dir)
 {
-	consistent_sync((void *)dma_to_virt(dev, handle), size, dir);
+	if (!arch_is_coherent())
+		consistent_sync((void *)dma_to_virt(dev, handle), size, dir);
 }
 #else
 extern void dma_sync_single_for_cpu(struct device*, dma_addr_t, size_t, enum dma_data_direction);
@@ -347,7 +353,8 @@
 
 	for (i = 0; i < nents; i++, sg++) {
 		char *virt = page_address(sg->page) + sg->offset;
-		consistent_sync(virt, sg->length, dir);
+		if (!arch_is_coherent())
+			consistent_sync(virt, sg->length, dir);
 	}
 }
 
@@ -359,7 +366,8 @@
 
 	for (i = 0; i < nents; i++, sg++) {
 		char *virt = page_address(sg->page) + sg->offset;
-		consistent_sync(virt, sg->length, dir);
+		if (!arch_is_coherent())
+			consistent_sync(virt, sg->length, dir);
 	}
 }
 #else
diff --git a/include/asm-arm/memory.h b/include/asm-arm/memory.h
index afa5c3e..2b3cf69 100644
--- a/include/asm-arm/memory.h
+++ b/include/asm-arm/memory.h
@@ -234,6 +234,14 @@
 #define virt_to_dma(dev, addr)		(__arch_virt_to_dma(dev, addr))
 #endif
 
+/*
+ * Optional coherency support.  Currently used only by selected
+ * Intel XSC3-based systems.
+ */
+#ifndef arch_is_coherent
+#define arch_is_coherent()		0
+#endif
+
 #endif
 
 #include <asm-generic/memory_model.h>
diff --git a/include/asm-arm/pgtable-hwdef.h b/include/asm-arm/pgtable-hwdef.h
index 1d03349..1bc1f99 100644
--- a/include/asm-arm/pgtable-hwdef.h
+++ b/include/asm-arm/pgtable-hwdef.h
@@ -73,6 +73,7 @@
 #define PTE_EXT_AP_URW_SRW	(PTE_EXT_AP1|PTE_EXT_AP0)
 #define PTE_EXT_TEX(x)		((x) << 6)	/* v5 */
 #define PTE_EXT_APX		(1 << 9)	/* v6 */
+#define PTE_EXT_COHERENT	(1 << 9)	/* XScale3 */
 #define PTE_EXT_SHARED		(1 << 10)	/* v6 */
 #define PTE_EXT_NG		(1 << 11)	/* v6 */
 
diff --git a/include/asm-arm/pgtable.h b/include/asm-arm/pgtable.h
index e595ae2..e85c08d 100644
--- a/include/asm-arm/pgtable.h
+++ b/include/asm-arm/pgtable.h
@@ -156,6 +156,7 @@
 #define L_PTE_WRITE		(1 << 5)
 #define L_PTE_EXEC		(1 << 6)
 #define L_PTE_DIRTY		(1 << 7)
+#define L_PTE_COHERENT		(1 << 9)	/* I/O coherent (xsc3) */
 #define L_PTE_SHARED		(1 << 10)	/* shared between CPUs (v6) */
 #define L_PTE_ASID		(1 << 11)	/* non-global (use ASID, v6) */