Skip to contentSkip to conversation

Added 64-bit MMIO accessors for ARMv8

PATCH Superseded York Sun 5 participants 29 messages May 29, 2014 – Jun 18, 2014

Subsystems (5)

v4 of 9 Superseded Latest is v9 →

lore
Share & tools
lore

York Sun

author
#0 lore raw

PATCH v4 1/5 Added 64-bit MMIO accessors for ARMv8

From: "J. German Rivera" <[email protected]>

This is needed for accessing peripherals with 64-bit MMIO registers, from ARMv8 processors.

---
Change log
v4: no change
v3: no change

 arch/arm/include/asm/io.h |    8 ++++++++
 1 file changed, 8 insertions(+)
arch/arm/include/asm/io.h +8 -0
index 6a1f05a..95528dd 100644
@@ -70,10 +70,12 @@ static inline phys_addr_t virt_to_phys(void * vaddr)
 #define __arch_getb(a)			(*(volatile unsigned char *)(a))
 #define __arch_getw(a)			(*(volatile unsigned short *)(a))
 #define __arch_getl(a)			(*(volatile unsigned int *)(a))
+#define __arch_getq(a)			(*(volatile unsigned long long *)(a))
 
 #define __arch_putb(v,a)		(*(volatile unsigned char *)(a) = (v))
 #define __arch_putw(v,a)		(*(volatile unsigned short *)(a) = (v))
 #define __arch_putl(v,a)		(*(volatile unsigned int *)(a) = (v))
+#define __arch_putq(v,a)		(*(volatile unsigned long long *)(a) = (v))
 
 extern inline void __raw_writesb(unsigned long addr, const void *data,
 				 int bytelen)
@@ -123,10 +125,12 @@ extern inline void __raw_readsl(unsigned long addr, void *data, int longlen)
 #define __raw_writeb(v,a)	__arch_putb(v,a)
 #define __raw_writew(v,a)	__arch_putw(v,a)
 #define __raw_writel(v,a)	__arch_putl(v,a)
+#define __raw_writeq(v,a)	__arch_putq(v,a)
 
 #define __raw_readb(a)		__arch_getb(a)
 #define __raw_readw(a)		__arch_getw(a)
 #define __raw_readl(a)		__arch_getl(a)
+#define __raw_readq(a)		__arch_getq(a)
 
 /*
  * TODO: The kernel offers some more advanced versions of barriers, it might
@@ -139,10 +143,12 @@ extern inline void __raw_readsl(unsigned long addr, void *data, int longlen)
 #define writeb(v,c)	({ u8  __v = v; __iowmb(); __arch_putb(__v,c); __v; })
 #define writew(v,c)	({ u16 __v = v; __iowmb(); __arch_putw(__v,c); __v; })
 #define writel(v,c)	({ u32 __v = v; __iowmb(); __arch_putl(__v,c); __v; })
+#define writeq(v,c)	({ u64 __v = v; __iowmb(); __arch_putq(__v,c); __v; })
 
 #define readb(c)	({ u8  __v = __arch_getb(c); __iormb(); __v; })
 #define readw(c)	({ u16 __v = __arch_getw(c); __iormb(); __v; })
 #define readl(c)	({ u32 __v = __arch_getl(c); __iormb(); __v; })
+#define readq(c)	({ u64 __v = __arch_getq(c); __iormb(); __v; })
 
 /*
  * The compiler seems to be incapable of optimising constants
@@ -168,9 +174,11 @@ extern inline void __raw_readsl(unsigned long addr, void *data, int longlen)
 #define out_arch(type,endian,a,v)	__raw_write##type(cpu_to_##endian(v),a)
 #define in_arch(type,endian,a)		endian##_to_cpu(__raw_read##type(a))
 
+#define out_le64(a,v)	out_arch(q,le64,a,v)
 #define out_le32(a,v)	out_arch(l,le32,a,v)
 #define out_le16(a,v)	out_arch(w,le16,a,v)
 
+#define in_le64(a)	in_arch(q,le64,a)
 #define in_le32(a)	in_arch(l,le32,a)
 #define in_le16(a)	in_arch(w,le16,a)
 
Signature · 1 line
1.7.9.5
author
#1 lore raw

PATCH v4 2/5 ARMv8: Adjust MMU setup

Make MMU functions reusable. Platform code can setup its own MMU tables. Also fix a typo of "TCR_EL3_IPS_BITS" in cache_v8.c.

---
Change log:
v4: new patch, splitted from v3 2/4
Revise set_pgtable_section() to be reused by platform MMU code
Add inline function set_ttbr_tcr_mair() to be used by this and platform mmu code

 arch/arm/cpu/armv8/cache_v8.c    |   49 ++++++++++++++++----------------------
 arch/arm/include/asm/armv8/mmu.h |   23 ++++++++++++++++++
 2 files changed, 43 insertions(+), 29 deletions(-)
arch/arm/cpu/armv8/cache_v8.c +20 -29
index a96ecda..67dbd46 100644
@@ -12,15 +12,14 @@
 DECLARE_GLOBAL_DATA_PTR;
 
 #ifndef CONFIG_SYS_DCACHE_OFF
-
-static void set_pgtable_section(u64 section, u64 memory_type)
+void set_pgtable_section(u64 *page_table, u64 index, u64 section,
+			 u64 memory_type)
 {
-	u64 *page_table = (u64 *)gd->arch.tlb_addr;
 	u64 value;
 
-	value = (section << SECTION_SHIFT) | PMD_TYPE_SECT | PMD_SECT_AF;
+	value = section | PMD_TYPE_SECT | PMD_SECT_AF;
 	value |= PMD_ATTRINDX(memory_type);
-	page_table[section] = value;
+	page_table[index] = value;
 }
 
 /* to activate the MMU we need to set up virtual memory */
@@ -28,10 +27,13 @@ static void mmu_setup(void)
 {
 	int i, j, el;
 	bd_t *bd = gd->bd;
+	u64 *page_table = (u64 *)gd->arch.tlb_addr;
 
 	/* Setup an identity-mapping for all spaces */
-	for (i = 0; i < (PGTABLE_SIZE >> 3); i++)
-		set_pgtable_section(i, MT_DEVICE_NGNRNE);
+	for (i = 0; i < (PGTABLE_SIZE >> 3); i++) {
+		set_pgtable_section(page_table, i, i << SECTION_SHIFT,
+				    MT_DEVICE_NGNRNE);
+	}
 
 	/* Setup an identity-mapping for all RAM space */
 	for (i = 0; i < CONFIG_NR_DRAM_BANKS; i++) {
@@ -39,36 +41,25 @@ static void mmu_setup(void)
 		ulong end = bd->bi_dram[i].start + bd->bi_dram[i].size;
 		for (j = start >> SECTION_SHIFT;
 		     j < end >> SECTION_SHIFT; j++) {
-			set_pgtable_section(j, MT_NORMAL);
+			set_pgtable_section(page_table, j, j << SECTION_SHIFT,
+					    MT_NORMAL);
 		}
 	}
 
 	/* load TTBR0 */
 	el = current_el();
 	if (el == 1) {
-		asm volatile("msr ttbr0_el1, %0"
-			     : : "r" (gd->arch.tlb_addr) : "memory");
-		asm volatile("msr tcr_el1, %0"
-			     : : "r" (TCR_FLAGS | TCR_EL1_IPS_BITS)
-			     : "memory");
-		asm volatile("msr mair_el1, %0"
-			     : : "r" (MEMORY_ATTRIBUTES) : "memory");
+		set_ttbr_tcr_mair(el, gd->arch.tlb_addr,
+				  TCR_FLAGS | TCR_EL1_IPS_BITS,
+				  MEMORY_ATTRIBUTES);
 	} else if (el == 2) {
-		asm volatile("msr ttbr0_el2, %0"
-			     : : "r" (gd->arch.tlb_addr) : "memory");
-		asm volatile("msr tcr_el2, %0"
-			     : : "r" (TCR_FLAGS | TCR_EL2_IPS_BITS)
-			     : "memory");
-		asm volatile("msr mair_el2, %0"
-			     : : "r" (MEMORY_ATTRIBUTES) : "memory");
+		set_ttbr_tcr_mair(el, gd->arch.tlb_addr,
+				  TCR_FLAGS | TCR_EL2_IPS_BITS,
+				  MEMORY_ATTRIBUTES);
 	} else {
-		asm volatile("msr ttbr0_el3, %0"
-			     : : "r" (gd->arch.tlb_addr) : "memory");
-		asm volatile("msr tcr_el3, %0"
-			     : : "r" (TCR_FLAGS | TCR_EL2_IPS_BITS)
-			     : "memory");
-		asm volatile("msr mair_el3, %0"
-			     : : "r" (MEMORY_ATTRIBUTES) : "memory");
+		set_ttbr_tcr_mair(el, gd->arch.tlb_addr,
+				  TCR_FLAGS | TCR_EL3_IPS_BITS,
+				  MEMORY_ATTRIBUTES);
 	}
 
 	/* enable the mmu */
arch/arm/include/asm/armv8/mmu.h +23 -0
index 1193e76..7de4ff9 100644
@@ -108,4 +108,27 @@
 				TCR_IRGN_WBWA |		\
 				TCR_T0SZ(VA_BITS))
 
+#ifndef __ASSEMBLY__
+void set_pgtable_section(u64 *page_table, u64 index,
+			 u64 section, u64 memory_type);
+static inline void set_ttbr_tcr_mair(int el, u64 table, u64 tcr, u64 attr)
+{
+	asm volatile("dsb sy;isb");
+	if (el == 1) {
+		asm volatile("msr ttbr0_el1, %0" : : "r" (table) : "memory");
+		asm volatile("msr tcr_el1, %0" : : "r" (tcr) : "memory");
+		asm volatile("msr mair_el1, %0" : : "r" (attr) : "memory");
+	} else if (el == 2) {
+		asm volatile("msr ttbr0_el2, %0" : : "r" (table) : "memory");
+		asm volatile("msr tcr_el2, %0" : : "r" (tcr) : "memory");
+		asm volatile("msr mair_el2, %0" : : "r" (attr) : "memory");
+	} else if (el == 3) {
+		asm volatile("msr ttbr0_el3, %0" : : "r" (table) : "memory");
+		asm volatile("msr tcr_el3, %0" : : "r" (tcr) : "memory");
+		asm volatile("msr mair_el3, %0" : : "r" (attr) : "memory");
+	} else {
+		hang();
+	}
+}
+#endif
 #endif /* _ASM_ARMV8_MMU_H_ */
Signature · 1 line
1.7.9.5
#2 lore raw

PATCH v4 2/5 ARMv8: Adjust MMU setup

Quoted 1 line · York Sun

Make MMU functions reusable. Platform code can setup its own MMU tables.

What exactly does platform code need to setup its own tables for?

Quoted 107 lines

Also fix a typo of "TCR_EL3_IPS_BITS" in cache_v8.c.

---
Change log:
v4: new patch, splitted from v3 2/4
Revise set_pgtable_section() to be reused by platform MMU code
Add inline function set_ttbr_tcr_mair() to be used by this and platform mmu code

 arch/arm/cpu/armv8/cache_v8.c    |   49 ++++++++++++++++----------------------
 arch/arm/include/asm/armv8/mmu.h |   23 ++++++++++++++++++
 2 files changed, 43 insertions(+), 29 deletions(-)
arch/arm/cpu/armv8/cache_v8.c +20 -29
index a96ecda..67dbd46 100644
@@ -12,15 +12,14 @@
 DECLARE_GLOBAL_DATA_PTR;
 
 #ifndef CONFIG_SYS_DCACHE_OFF
-
-static void set_pgtable_section(u64 section, u64 memory_type)
+void set_pgtable_section(u64 *page_table, u64 index, u64 section,
+			 u64 memory_type)
 {
-	u64 *page_table = (u64 *)gd->arch.tlb_addr;
 	u64 value;
 
-	value = (section << SECTION_SHIFT) | PMD_TYPE_SECT | PMD_SECT_AF;
+	value = section | PMD_TYPE_SECT | PMD_SECT_AF;
 	value |= PMD_ATTRINDX(memory_type);
-	page_table[section] = value;
+	page_table[index] = value;
 }
 
 /* to activate the MMU we need to set up virtual memory */
@@ -28,10 +27,13 @@ static void mmu_setup(void)
 {
 	int i, j, el;
 	bd_t *bd = gd->bd;
+	u64 *page_table = (u64 *)gd->arch.tlb_addr;
 
 	/* Setup an identity-mapping for all spaces */
-	for (i = 0; i < (PGTABLE_SIZE >> 3); i++)
-		set_pgtable_section(i, MT_DEVICE_NGNRNE);
+	for (i = 0; i < (PGTABLE_SIZE >> 3); i++) {
+		set_pgtable_section(page_table, i, i << SECTION_SHIFT,
+				    MT_DEVICE_NGNRNE);
+	}
 
 	/* Setup an identity-mapping for all RAM space */
 	for (i = 0; i < CONFIG_NR_DRAM_BANKS; i++) {
@@ -39,36 +41,25 @@ static void mmu_setup(void)
 		ulong end = bd->bi_dram[i].start + bd->bi_dram[i].size;
 		for (j = start >> SECTION_SHIFT;
 		     j < end >> SECTION_SHIFT; j++) {
-			set_pgtable_section(j, MT_NORMAL);
+			set_pgtable_section(page_table, j, j << SECTION_SHIFT,
+					    MT_NORMAL);
 		}
 	}
 
 	/* load TTBR0 */
 	el = current_el();
 	if (el == 1) {
-		asm volatile("msr ttbr0_el1, %0"
-			     : : "r" (gd->arch.tlb_addr) : "memory");
-		asm volatile("msr tcr_el1, %0"
-			     : : "r" (TCR_FLAGS | TCR_EL1_IPS_BITS)
-			     : "memory");
-		asm volatile("msr mair_el1, %0"
-			     : : "r" (MEMORY_ATTRIBUTES) : "memory");
+		set_ttbr_tcr_mair(el, gd->arch.tlb_addr,
+				  TCR_FLAGS | TCR_EL1_IPS_BITS,
+				  MEMORY_ATTRIBUTES);
 	} else if (el == 2) {
-		asm volatile("msr ttbr0_el2, %0"
-			     : : "r" (gd->arch.tlb_addr) : "memory");
-		asm volatile("msr tcr_el2, %0"
-			     : : "r" (TCR_FLAGS | TCR_EL2_IPS_BITS)
-			     : "memory");
-		asm volatile("msr mair_el2, %0"
-			     : : "r" (MEMORY_ATTRIBUTES) : "memory");
+		set_ttbr_tcr_mair(el, gd->arch.tlb_addr,
+				  TCR_FLAGS | TCR_EL2_IPS_BITS,
+				  MEMORY_ATTRIBUTES);
 	} else {
-		asm volatile("msr ttbr0_el3, %0"
-			     : : "r" (gd->arch.tlb_addr) : "memory");
-		asm volatile("msr tcr_el3, %0"
-			     : : "r" (TCR_FLAGS | TCR_EL2_IPS_BITS)
-			     : "memory");
-		asm volatile("msr mair_el3, %0"
-			     : : "r" (MEMORY_ATTRIBUTES) : "memory");
+		set_ttbr_tcr_mair(el, gd->arch.tlb_addr,
+				  TCR_FLAGS | TCR_EL3_IPS_BITS,
+				  MEMORY_ATTRIBUTES);
 	}
 
 	/* enable the mmu */
arch/arm/include/asm/armv8/mmu.h +0 -0
index 1193e76..7de4ff9 100644

arch/arm/include/asm/armv8/mmu.h line 116

Quoted 9 lines
arch/arm/include/asm/armv8/mmu.h +6 -0
@@ -108,4 +108,27 @@
 				TCR_IRGN_WBWA |		\
 				TCR_T0SZ(VA_BITS))
 
+#ifndef __ASSEMBLY__
+void set_pgtable_section(u64 *page_table, u64 index,
+			 u64 section, u64 memory_type);
+static inline void set_ttbr_tcr_mair(int el, u64 table, u64 tcr, u64 attr)
+{
+	asm volatile("dsb sy;isb");

Huh? This wasn't anywhere before. Is the isb necessary?

When is this function expected to be called? Is the MMU expected to be on?

arch/arm/include/asm/armv8/mmu.h line 120

Quoted 4 lines
arch/arm/include/asm/armv8/mmu.h +4 -0
+	if (el == 1) {
+		asm volatile("msr ttbr0_el1, %0" : : "r" (table) : "memory");
+		asm volatile("msr tcr_el1, %0" : : "r" (tcr) : "memory");
+		asm volatile("msr mair_el1, %0" : : "r" (attr) : "memory");

If the MMU is on, this looks really scary -- what are you expecting to change in a single invocation?

arch/arm/include/asm/armv8/mmu.h line 131

Quoted 11 lines
arch/arm/include/asm/armv8/mmu.h +11 -0
+	} else if (el == 2) {
+		asm volatile("msr ttbr0_el2, %0" : : "r" (table) : "memory");
+		asm volatile("msr tcr_el2, %0" : : "r" (tcr) : "memory");
+		asm volatile("msr mair_el2, %0" : : "r" (attr) : "memory");
+	} else if (el == 3) {
+		asm volatile("msr ttbr0_el3, %0" : : "r" (table) : "memory");
+		asm volatile("msr tcr_el3, %0" : : "r" (tcr) : "memory");
+		asm volatile("msr mair_el3, %0" : : "r" (attr) : "memory");
+	} else {
+		hang();
+	}

And no synchronisation to ensure that these writes are complete or even ordered w.r.t. each other?

Mark.

author
#3 lore raw

PATCH v4 2/5 ARMv8: Adjust MMU setup

Quoted 4 lines · Mark Rutland
Quoted 1 line · York Sun

Make MMU functions reusable. Platform code can setup its own MMU tables.

What exactly does platform code need to setup its own tables for?

The general ARMv8 MMU table is not detail enough to control memory attribute like cache for all addresses. We have devices mapping to addresses with different requirement for cache control.

Quoted 111 lines
Quoted 111 lines

Also fix a typo of "TCR_EL3_IPS_BITS" in cache_v8.c.

---
Change log:
v4: new patch, splitted from v3 2/4
Revise set_pgtable_section() to be reused by platform MMU code
Add inline function set_ttbr_tcr_mair() to be used by this and platform mmu code

 arch/arm/cpu/armv8/cache_v8.c    |   49 ++++++++++++++++----------------------
 arch/arm/include/asm/armv8/mmu.h |   23 ++++++++++++++++++
 2 files changed, 43 insertions(+), 29 deletions(-)
arch/arm/cpu/armv8/cache_v8.c +20 -29
index a96ecda..67dbd46 100644
@@ -12,15 +12,14 @@
 DECLARE_GLOBAL_DATA_PTR;
 
 #ifndef CONFIG_SYS_DCACHE_OFF
-
-static void set_pgtable_section(u64 section, u64 memory_type)
+void set_pgtable_section(u64 *page_table, u64 index, u64 section,
+			 u64 memory_type)
 {
-	u64 *page_table = (u64 *)gd->arch.tlb_addr;
 	u64 value;
 
-	value = (section << SECTION_SHIFT) | PMD_TYPE_SECT | PMD_SECT_AF;
+	value = section | PMD_TYPE_SECT | PMD_SECT_AF;
 	value |= PMD_ATTRINDX(memory_type);
-	page_table[section] = value;
+	page_table[index] = value;
 }
 
 /* to activate the MMU we need to set up virtual memory */
@@ -28,10 +27,13 @@ static void mmu_setup(void)
 {
 	int i, j, el;
 	bd_t *bd = gd->bd;
+	u64 *page_table = (u64 *)gd->arch.tlb_addr;
 
 	/* Setup an identity-mapping for all spaces */
-	for (i = 0; i < (PGTABLE_SIZE >> 3); i++)
-		set_pgtable_section(i, MT_DEVICE_NGNRNE);
+	for (i = 0; i < (PGTABLE_SIZE >> 3); i++) {
+		set_pgtable_section(page_table, i, i << SECTION_SHIFT,
+				    MT_DEVICE_NGNRNE);
+	}
 
 	/* Setup an identity-mapping for all RAM space */
 	for (i = 0; i < CONFIG_NR_DRAM_BANKS; i++) {
@@ -39,36 +41,25 @@ static void mmu_setup(void)
 		ulong end = bd->bi_dram[i].start + bd->bi_dram[i].size;
 		for (j = start >> SECTION_SHIFT;
 		     j < end >> SECTION_SHIFT; j++) {
-			set_pgtable_section(j, MT_NORMAL);
+			set_pgtable_section(page_table, j, j << SECTION_SHIFT,
+					    MT_NORMAL);
 		}
 	}
 
 	/* load TTBR0 */
 	el = current_el();
 	if (el == 1) {
-		asm volatile("msr ttbr0_el1, %0"
-			     : : "r" (gd->arch.tlb_addr) : "memory");
-		asm volatile("msr tcr_el1, %0"
-			     : : "r" (TCR_FLAGS | TCR_EL1_IPS_BITS)
-			     : "memory");
-		asm volatile("msr mair_el1, %0"
-			     : : "r" (MEMORY_ATTRIBUTES) : "memory");
+		set_ttbr_tcr_mair(el, gd->arch.tlb_addr,
+				  TCR_FLAGS | TCR_EL1_IPS_BITS,
+				  MEMORY_ATTRIBUTES);
 	} else if (el == 2) {
-		asm volatile("msr ttbr0_el2, %0"
-			     : : "r" (gd->arch.tlb_addr) : "memory");
-		asm volatile("msr tcr_el2, %0"
-			     : : "r" (TCR_FLAGS | TCR_EL2_IPS_BITS)
-			     : "memory");
-		asm volatile("msr mair_el2, %0"
-			     : : "r" (MEMORY_ATTRIBUTES) : "memory");
+		set_ttbr_tcr_mair(el, gd->arch.tlb_addr,
+				  TCR_FLAGS | TCR_EL2_IPS_BITS,
+				  MEMORY_ATTRIBUTES);
 	} else {
-		asm volatile("msr ttbr0_el3, %0"
-			     : : "r" (gd->arch.tlb_addr) : "memory");
-		asm volatile("msr tcr_el3, %0"
-			     : : "r" (TCR_FLAGS | TCR_EL2_IPS_BITS)
-			     : "memory");
-		asm volatile("msr mair_el3, %0"
-			     : : "r" (MEMORY_ATTRIBUTES) : "memory");
+		set_ttbr_tcr_mair(el, gd->arch.tlb_addr,
+				  TCR_FLAGS | TCR_EL3_IPS_BITS,
+				  MEMORY_ATTRIBUTES);
 	}
 
 	/* enable the mmu */
arch/arm/include/asm/armv8/mmu.h +0 -0
index 1193e76..7de4ff9 100644
@@ -108,4 +108,27 @@
 				TCR_IRGN_WBWA |		\
 				TCR_T0SZ(VA_BITS))
 
Quoted 8 lines

arch/arm/include/asm/armv8/mmu.h line 116

Quoted 6 lines
arch/arm/include/asm/armv8/mmu.h +6 -0
+#ifndef __ASSEMBLY__
+void set_pgtable_section(u64 *page_table, u64 index,
+			 u64 section, u64 memory_type);
+static inline void set_ttbr_tcr_mair(int el, u64 table, u64 tcr, u64 attr)
+{
+	asm volatile("dsb sy;isb");

Huh? This wasn't anywhere before. Is the isb necessary?

Probably not, but to make sure all previous instructions finish.

Quoted 3 lines

When is this function expected to be called? Is the MMU expected to be on?

The general ARMv8 code calls this when MMU is off. For the SoC I am enabling, it is called when MMU is off for the first time, but MMU on for the 2nd time.

Quoted 8 lines

arch/arm/include/asm/armv8/mmu.h line 120

Quoted 5 lines
arch/arm/include/asm/armv8/mmu.h +4 -0
+	if (el == 1) {
+		asm volatile("msr ttbr0_el1, %0" : : "r" (table) : "memory");
+		asm volatile("msr tcr_el1, %0" : : "r" (tcr) : "memory");
+		asm volatile("msr mair_el1, %0" : : "r" (attr) : "memory");

If the MMU is on, this looks really scary -- what are you expecting to change in a single invocation?

It is not scary for general ARMv8 code. MMU is off then this is called. For FSL SoC, I have these called for the 2nd time, when MMU is on. The 2nd time call points to a new MMU table.

Quoted 16 lines

arch/arm/include/asm/armv8/mmu.h line 131

Quoted 12 lines
arch/arm/include/asm/armv8/mmu.h +11 -0
+	} else if (el == 2) {
+		asm volatile("msr ttbr0_el2, %0" : : "r" (table) : "memory");
+		asm volatile("msr tcr_el2, %0" : : "r" (tcr) : "memory");
+		asm volatile("msr mair_el2, %0" : : "r" (attr) : "memory");
+	} else if (el == 3) {
+		asm volatile("msr ttbr0_el3, %0" : : "r" (table) : "memory");
+		asm volatile("msr tcr_el3, %0" : : "r" (tcr) : "memory");
+		asm volatile("msr mair_el3, %0" : : "r" (attr) : "memory");
+	} else {
+		hang();
+	}

And no synchronisation to ensure that these writes are complete or even ordered w.r.t. each other?

That's why I added asm volatile("dsb sy;isb") before them. The order of these write doesn't matter. See the code before my change
http://git.denx.de/?p=u-boot.git;a=blob;f=arch/arm/cpu/armv8/cache_v8.c;h=a96ecda7e3146996d20a2e46b975dced2769255c;hb=HEAD

For the 2nd write used by FSL SoC, I need to flush the cache to make sure the table is in main DDR and perform TLB invalidation. That's when the loading of new MMU table happens.

York

#4 lore raw

PATCH v4 2/5 ARMv8: Adjust MMU setup

Quoted 9 lines · York Sun
Quoted 4 lines · Mark Rutland
Quoted 1 line · York Sun

Make MMU functions reusable. Platform code can setup its own MMU tables.

What exactly does platform code need to setup its own tables for?

The general ARMv8 MMU table is not detail enough to control memory attribute like cache for all addresses. We have devices mapping to addresses with different requirement for cache control.

And there are no APIs for creating device mappings rather than exporting the raw pagetable accessors and hard-coding them differently in every board file?

Quoted 121 lines
Quoted 112 lines
Quoted 112 lines

Also fix a typo of "TCR_EL3_IPS_BITS" in cache_v8.c.

---
Change log:
v4: new patch, splitted from v3 2/4
Revise set_pgtable_section() to be reused by platform MMU code
Add inline function set_ttbr_tcr_mair() to be used by this and platform mmu code

 arch/arm/cpu/armv8/cache_v8.c    |   49 ++++++++++++++++----------------------
 arch/arm/include/asm/armv8/mmu.h |   23 ++++++++++++++++++
 2 files changed, 43 insertions(+), 29 deletions(-)
arch/arm/cpu/armv8/cache_v8.c +20 -29
index a96ecda..67dbd46 100644
@@ -12,15 +12,14 @@
 DECLARE_GLOBAL_DATA_PTR;
 
 #ifndef CONFIG_SYS_DCACHE_OFF
-
-static void set_pgtable_section(u64 section, u64 memory_type)
+void set_pgtable_section(u64 *page_table, u64 index, u64 section,
+			 u64 memory_type)
 {
-	u64 *page_table = (u64 *)gd->arch.tlb_addr;
 	u64 value;
 
-	value = (section << SECTION_SHIFT) | PMD_TYPE_SECT | PMD_SECT_AF;
+	value = section | PMD_TYPE_SECT | PMD_SECT_AF;
 	value |= PMD_ATTRINDX(memory_type);
-	page_table[section] = value;
+	page_table[index] = value;
 }
 
 /* to activate the MMU we need to set up virtual memory */
@@ -28,10 +27,13 @@ static void mmu_setup(void)
 {
 	int i, j, el;
 	bd_t *bd = gd->bd;
+	u64 *page_table = (u64 *)gd->arch.tlb_addr;
 
 	/* Setup an identity-mapping for all spaces */
-	for (i = 0; i < (PGTABLE_SIZE >> 3); i++)
-		set_pgtable_section(i, MT_DEVICE_NGNRNE);
+	for (i = 0; i < (PGTABLE_SIZE >> 3); i++) {
+		set_pgtable_section(page_table, i, i << SECTION_SHIFT,
+				    MT_DEVICE_NGNRNE);
+	}
 
 	/* Setup an identity-mapping for all RAM space */
 	for (i = 0; i < CONFIG_NR_DRAM_BANKS; i++) {
@@ -39,36 +41,25 @@ static void mmu_setup(void)
 		ulong end = bd->bi_dram[i].start + bd->bi_dram[i].size;
 		for (j = start >> SECTION_SHIFT;
 		     j < end >> SECTION_SHIFT; j++) {
-			set_pgtable_section(j, MT_NORMAL);
+			set_pgtable_section(page_table, j, j << SECTION_SHIFT,
+					    MT_NORMAL);
 		}
 	}
 
 	/* load TTBR0 */
 	el = current_el();
 	if (el == 1) {
-		asm volatile("msr ttbr0_el1, %0"
-			     : : "r" (gd->arch.tlb_addr) : "memory");
-		asm volatile("msr tcr_el1, %0"
-			     : : "r" (TCR_FLAGS | TCR_EL1_IPS_BITS)
-			     : "memory");
-		asm volatile("msr mair_el1, %0"
-			     : : "r" (MEMORY_ATTRIBUTES) : "memory");
+		set_ttbr_tcr_mair(el, gd->arch.tlb_addr,
+				  TCR_FLAGS | TCR_EL1_IPS_BITS,
+				  MEMORY_ATTRIBUTES);
 	} else if (el == 2) {
-		asm volatile("msr ttbr0_el2, %0"
-			     : : "r" (gd->arch.tlb_addr) : "memory");
-		asm volatile("msr tcr_el2, %0"
-			     : : "r" (TCR_FLAGS | TCR_EL2_IPS_BITS)
-			     : "memory");
-		asm volatile("msr mair_el2, %0"
-			     : : "r" (MEMORY_ATTRIBUTES) : "memory");
+		set_ttbr_tcr_mair(el, gd->arch.tlb_addr,
+				  TCR_FLAGS | TCR_EL2_IPS_BITS,
+				  MEMORY_ATTRIBUTES);
 	} else {
-		asm volatile("msr ttbr0_el3, %0"
-			     : : "r" (gd->arch.tlb_addr) : "memory");
-		asm volatile("msr tcr_el3, %0"
-			     : : "r" (TCR_FLAGS | TCR_EL2_IPS_BITS)
-			     : "memory");
-		asm volatile("msr mair_el3, %0"
-			     : : "r" (MEMORY_ATTRIBUTES) : "memory");
+		set_ttbr_tcr_mair(el, gd->arch.tlb_addr,
+				  TCR_FLAGS | TCR_EL3_IPS_BITS,
+				  MEMORY_ATTRIBUTES);
 	}
 
 	/* enable the mmu */
arch/arm/include/asm/armv8/mmu.h +0 -0
index 1193e76..7de4ff9 100644
@@ -108,4 +108,27 @@
 				TCR_IRGN_WBWA |		\
 				TCR_T0SZ(VA_BITS))
 
Quoted 8 lines

arch/arm/include/asm/armv8/mmu.h line 116

Quoted 6 lines
arch/arm/include/asm/armv8/mmu.h +6 -0
+#ifndef __ASSEMBLY__
+void set_pgtable_section(u64 *page_table, u64 index,
+			 u64 section, u64 memory_type);
+static inline void set_ttbr_tcr_mair(int el, u64 table, u64 tcr, u64 attr)
+{
+	asm volatile("dsb sy;isb");

Huh? This wasn't anywhere before. Is the isb necessary?

Quoted 1 line

Probably not, but to make sure all previous instructions finish.

Which instructions do you care about completing?

You'll certainly want any page table writes to complete, but is anything else in-flight at this point in time?

Quoted 6 lines
Quoted 3 lines

When is this function expected to be called? Is the MMU expected to be on?

The general ARMv8 code calls this when MMU is off. For the SoC I am enabling, it is called when MMU is off for the first time, but MMU on for the 2nd time.

Ok.

Quoted 10 lines
Quoted 9 lines

arch/arm/include/asm/armv8/mmu.h line 120

Quoted 6 lines
arch/arm/include/asm/armv8/mmu.h +4 -0
+	if (el == 1) {
+		asm volatile("msr ttbr0_el1, %0" : : "r" (table) : "memory");
+		asm volatile("msr tcr_el1, %0" : : "r" (tcr) : "memory");
+		asm volatile("msr mair_el1, %0" : : "r" (attr) : "memory");

If the MMU is on, this looks really scary -- what are you expecting to change in a single invocation?

Quoted 3 lines

It is not scary for general ARMv8 code. MMU is off then this is called. For FSL SoC, I have these called for the 2nd time, when MMU is on. The 2nd time call points to a new MMU table.

Oh but it is ;)

Quoted 18 lines
Quoted 17 lines

arch/arm/include/asm/armv8/mmu.h line 131

Quoted 13 lines
arch/arm/include/asm/armv8/mmu.h +11 -0
+	} else if (el == 2) {
+		asm volatile("msr ttbr0_el2, %0" : : "r" (table) : "memory");
+		asm volatile("msr tcr_el2, %0" : : "r" (tcr) : "memory");
+		asm volatile("msr mair_el2, %0" : : "r" (attr) : "memory");
+	} else if (el == 3) {
+		asm volatile("msr ttbr0_el3, %0" : : "r" (table) : "memory");
+		asm volatile("msr tcr_el3, %0" : : "r" (tcr) : "memory");
+		asm volatile("msr mair_el3, %0" : : "r" (attr) : "memory");
+	} else {
+		hang();
+	}

And no synchronisation to ensure that these writes are complete or even ordered w.r.t. each other?

Quoted 3 lines

That's why I added asm volatile("dsb sy;isb") before them. The order of these write doesn't matter. See the code before my change
http://git.denx.de/?p=u-boot.git;a=blob;f=arch/arm/cpu/armv8/cache_v8.c;h=a96ecda7e3146996d20a2e46b975dced2769255c;hb=HEAD

Ok, so that orders them against everything _before_ them, but not with respect to each other, or anything _after_ them.

When the MMU is off, you are correct that the ordering of these operations w.r.t. each other doesn't matter. With the MMU on that's not true as the CPU can rerder the operations and can walk the page tables asynchronously.

Changing the MAIR at runtime is somewhat scary because you may be changing attributes for entries which are active in the cache and/or TLBs. I'm not sure I follow why you need to change it once the MMU is on -- there are plenty of subfields in the MAIR that you could configure before turning the MMU on for use later.

Likewise changing the TCR at runtime is somewhat scary because you can change attributes of active cache and/or TLB entries, change fields that affect the way page tables are walked (TG*. T*SZ), all asynchronously w.r.t. the logic that walsk the page tables.

Changing the TTBR is fine, except that we didn't invalidate old entries with a TLBI, so we may even have partial translations cahced in the TLBs, and we can get odd translations generated from the combination of the old and new tables.

Quoted 3 lines

For the 2nd write used by FSL SoC, I need to flush the cache to make sure the table is in main DDR and perform TLB invalidation. That's when the loading of new MMU table happens.

I missed the TLB invaliation -- where is that meant to happen?

Mark.

author
#5 lore raw

PATCH v4 2/5 ARMv8: Adjust MMU setup

Quoted 7 lines · Mark Rutland
Quoted 6 lines · York Sun
Quoted 4 lines · Mark Rutland
Quoted 1 line · York Sun

Make MMU functions reusable. Platform code can setup its own MMU tables.

What exactly does platform code need to setup its own tables for?

Quoted 8 lines
Quoted 3 lines

The general ARMv8 MMU table is not detail enough to control memory attribute like cache for all addresses. We have devices mapping to addresses with different requirement for cache control.

And there are no APIs for creating device mappings rather than exporting the raw pagetable accessors and hard-coding them differently in every board file?

That's a good question. At this point, only two platforms are using ARMv8 code. I am expecting FSL ARMv8 implementation will stay similar, i.e. covered by the file I added. If that's not the case, or more ARMv8 SoCs need special MMU table, we then should introduce such API. Having a full function MMU API may be an overkill for U-boot. We don't need dynamic MMU anyway.

Quoted 121 lines
Quoted 121 lines
Quoted 120 lines
Quoted 109 lines

Also fix a typo of "TCR_EL3_IPS_BITS" in cache_v8.c.

---
Change log:
v4: new patch, splitted from v3 2/4
Revise set_pgtable_section() to be reused by platform MMU code
Add inline function set_ttbr_tcr_mair() to be used by this and platform mmu code

 arch/arm/cpu/armv8/cache_v8.c    |   49 ++++++++++++++++----------------------
 arch/arm/include/asm/armv8/mmu.h |   23 ++++++++++++++++++
 2 files changed, 43 insertions(+), 29 deletions(-)
arch/arm/cpu/armv8/cache_v8.c +20 -29
index a96ecda..67dbd46 100644
@@ -12,15 +12,14 @@
 DECLARE_GLOBAL_DATA_PTR;
 
 #ifndef CONFIG_SYS_DCACHE_OFF
-
-static void set_pgtable_section(u64 section, u64 memory_type)
+void set_pgtable_section(u64 *page_table, u64 index, u64 section,
+			 u64 memory_type)
 {
-	u64 *page_table = (u64 *)gd->arch.tlb_addr;
 	u64 value;
 
-	value = (section << SECTION_SHIFT) | PMD_TYPE_SECT | PMD_SECT_AF;
+	value = section | PMD_TYPE_SECT | PMD_SECT_AF;
 	value |= PMD_ATTRINDX(memory_type);
-	page_table[section] = value;
+	page_table[index] = value;
 }
 
 /* to activate the MMU we need to set up virtual memory */
@@ -28,10 +27,13 @@ static void mmu_setup(void)
 {
 	int i, j, el;
 	bd_t *bd = gd->bd;
+	u64 *page_table = (u64 *)gd->arch.tlb_addr;
 
 	/* Setup an identity-mapping for all spaces */
-	for (i = 0; i < (PGTABLE_SIZE >> 3); i++)
-		set_pgtable_section(i, MT_DEVICE_NGNRNE);
+	for (i = 0; i < (PGTABLE_SIZE >> 3); i++) {
+		set_pgtable_section(page_table, i, i << SECTION_SHIFT,
+				    MT_DEVICE_NGNRNE);
+	}
 
 	/* Setup an identity-mapping for all RAM space */
 	for (i = 0; i < CONFIG_NR_DRAM_BANKS; i++) {
@@ -39,36 +41,25 @@ static void mmu_setup(void)
 		ulong end = bd->bi_dram[i].start + bd->bi_dram[i].size;
 		for (j = start >> SECTION_SHIFT;
 		     j < end >> SECTION_SHIFT; j++) {
-			set_pgtable_section(j, MT_NORMAL);
+			set_pgtable_section(page_table, j, j << SECTION_SHIFT,
+					    MT_NORMAL);
 		}
 	}
 
 	/* load TTBR0 */
 	el = current_el();
 	if (el == 1) {
-		asm volatile("msr ttbr0_el1, %0"
-			     : : "r" (gd->arch.tlb_addr) : "memory");
-		asm volatile("msr tcr_el1, %0"
-			     : : "r" (TCR_FLAGS | TCR_EL1_IPS_BITS)
-			     : "memory");
-		asm volatile("msr mair_el1, %0"
-			     : : "r" (MEMORY_ATTRIBUTES) : "memory");
+		set_ttbr_tcr_mair(el, gd->arch.tlb_addr,
+				  TCR_FLAGS | TCR_EL1_IPS_BITS,
+				  MEMORY_ATTRIBUTES);
 	} else if (el == 2) {
-		asm volatile("msr ttbr0_el2, %0"
-			     : : "r" (gd->arch.tlb_addr) : "memory");
-		asm volatile("msr tcr_el2, %0"
-			     : : "r" (TCR_FLAGS | TCR_EL2_IPS_BITS)
-			     : "memory");
-		asm volatile("msr mair_el2, %0"
-			     : : "r" (MEMORY_ATTRIBUTES) : "memory");
+		set_ttbr_tcr_mair(el, gd->arch.tlb_addr,
+				  TCR_FLAGS | TCR_EL2_IPS_BITS,
+				  MEMORY_ATTRIBUTES);
 	} else {
-		asm volatile("msr ttbr0_el3, %0"
-			     : : "r" (gd->arch.tlb_addr) : "memory");
-		asm volatile("msr tcr_el3, %0"
-			     : : "r" (TCR_FLAGS | TCR_EL2_IPS_BITS)
-			     : "memory");
-		asm volatile("msr mair_el3, %0"
-			     : : "r" (MEMORY_ATTRIBUTES) : "memory");
+		set_ttbr_tcr_mair(el, gd->arch.tlb_addr,
+				  TCR_FLAGS | TCR_EL3_IPS_BITS,
+				  MEMORY_ATTRIBUTES);
 	}
 
 	/* enable the mmu */
arch/arm/include/asm/armv8/mmu.h +0 -0
index 1193e76..7de4ff9 100644

arch/arm/include/asm/armv8/mmu.h line 116

Quoted 9 lines
arch/arm/include/asm/armv8/mmu.h +6 -0
@@ -108,4 +108,27 @@
 				TCR_IRGN_WBWA |		\
 				TCR_T0SZ(VA_BITS))
 
+#ifndef __ASSEMBLY__
+void set_pgtable_section(u64 *page_table, u64 index,
+			 u64 section, u64 memory_type);
+static inline void set_ttbr_tcr_mair(int el, u64 table, u64 tcr, u64 attr)
+{
+	asm volatile("dsb sy;isb");

Huh? This wasn't anywhere before. Is the isb necessary?

Quoted 6 lines
Quoted 1 line

Probably not, but to make sure all previous instructions finish.

Which instructions do you care about completing?

You'll certainly want any page table writes to complete, but is anything else in-flight at this point in time?

Before calling this inline function, MMU tables were created. We want the writing completes before setting these registers.

Quoted 67 lines
Quoted 7 lines
Quoted 4 lines

When is this function expected to be called? Is the MMU expected to be on?

The general ARMv8 code calls this when MMU is off. For the SoC I am enabling, it is called when MMU is off for the first time, but MMU on for the 2nd time.

Ok.

Quoted 10 lines
Quoted 9 lines

arch/arm/include/asm/armv8/mmu.h line 120

Quoted 6 lines
arch/arm/include/asm/armv8/mmu.h +4 -0
+	if (el == 1) {
+		asm volatile("msr ttbr0_el1, %0" : : "r" (table) : "memory");
+		asm volatile("msr tcr_el1, %0" : : "r" (tcr) : "memory");
+		asm volatile("msr mair_el1, %0" : : "r" (attr) : "memory");

If the MMU is on, this looks really scary -- what are you expecting to change in a single invocation?

Quoted 3 lines

It is not scary for general ARMv8 code. MMU is off then this is called. For FSL SoC, I have these called for the 2nd time, when MMU is on. The 2nd time call points to a new MMU table.

Oh but it is ;)

Quoted 18 lines
Quoted 17 lines

arch/arm/include/asm/armv8/mmu.h line 131

Quoted 13 lines
arch/arm/include/asm/armv8/mmu.h +11 -0
+	} else if (el == 2) {
+		asm volatile("msr ttbr0_el2, %0" : : "r" (table) : "memory");
+		asm volatile("msr tcr_el2, %0" : : "r" (tcr) : "memory");
+		asm volatile("msr mair_el2, %0" : : "r" (attr) : "memory");
+	} else if (el == 3) {
+		asm volatile("msr ttbr0_el3, %0" : : "r" (table) : "memory");
+		asm volatile("msr tcr_el3, %0" : : "r" (tcr) : "memory");
+		asm volatile("msr mair_el3, %0" : : "r" (attr) : "memory");
+	} else {
+		hang();
+	}

And no synchronisation to ensure that these writes are complete or even ordered w.r.t. each other?

Quoted 3 lines

That's why I added asm volatile("dsb sy;isb") before them. The order of these write doesn't matter. See the code before my change
http://git.denx.de/?p=u-boot.git;a=blob;f=arch/arm/cpu/armv8/cache_v8.c;h=a96ecda7e3146996d20a2e46b975dced2769255c;hb=HEAD

Ok, so that orders them against everything _before_ them, but not with respect to each other, or anything _after_ them.

When the MMU is off, you are correct that the ordering of these operations w.r.t. each other doesn't matter. With the MMU on that's not true as the CPU can rerder the operations and can walk the page tables asynchronously.

Changing the MAIR at runtime is somewhat scary because you may be changing attributes for entries which are active in the cache and/or TLBs. I'm not sure I follow why you need to change it once the MMU is on -- there are plenty of subfields in the MAIR that you could configure before turning the MMU on for use later.

Likewise changing the TCR at runtime is somewhat scary because you can change attributes of active cache and/or TLB entries, change fields that affect the way page tables are walked (TG*. T*SZ), all asynchronously w.r.t. the logic that walsk the page tables.

Quoted 4 lines

Changing the TTBR is fine, except that we didn't invalidate old entries with a TLBI, so we may even have partial translations cahced in the TLBs, and we can get odd translations generated from the combination of the old and new tables.

Maybe a more proper way to do so is to change TTBR only. That's actually what I really need. Or if I really need to change all of them, I should turn off MMU first.

Quoted 7 lines
Quoted 4 lines

For the 2nd write used by FSL SoC, I need to flush the cache to make sure the table is in main DDR and perform TLB invalidation. That's when the loading of new MMU table happens.

I missed the TLB invaliation -- where is that meant to happen?

After flushing the new MMU table into DDR. To your point above, I will send a new version with updating TTBR only.

York

#6 lore raw

PATCH v4 2/5 ARMv8: Adjust MMU setup

Quoted 17 lines · York Sun
Quoted 7 lines · Mark Rutland
Quoted 6 lines · York Sun
Quoted 4 lines · Mark Rutland
Quoted 1 line · York Sun

Make MMU functions reusable. Platform code can setup its own MMU tables.

What exactly does platform code need to setup its own tables for?

Quoted 8 lines
Quoted 3 lines

The general ARMv8 MMU table is not detail enough to control memory attribute like cache for all addresses. We have devices mapping to addresses with different requirement for cache control.

And there are no APIs for creating device mappings rather than exporting the raw pagetable accessors and hard-coding them differently in every board file?

Quoted 5 lines

That's a good question. At this point, only two platforms are using ARMv8 code. I am expecting FSL ARMv8 implementation will stay similar, i.e. covered by the file I added. If that's not the case, or more ARMv8 SoCs need special MMU table, we then should introduce such API. Having a full function MMU API may be an overkill for U-boot. We don't need dynamic MMU anyway.

Maybe. It just seems to me that it would be possible to pre-allocate an empty table that we could place device (nGnRnE?) mappings in. Then all you'd need to call from board code is a function to map a range, rather than having to duplicate logic for creating the tables you want.

Quoted 124 lines
Quoted 124 lines
Quoted 123 lines
Quoted 113 lines
Quoted 113 lines

Also fix a typo of "TCR_EL3_IPS_BITS" in cache_v8.c.

---
Change log:
v4: new patch, splitted from v3 2/4
Revise set_pgtable_section() to be reused by platform MMU code
Add inline function set_ttbr_tcr_mair() to be used by this and platform mmu code

 arch/arm/cpu/armv8/cache_v8.c    |   49 ++++++++++++++++----------------------
 arch/arm/include/asm/armv8/mmu.h |   23 ++++++++++++++++++
 2 files changed, 43 insertions(+), 29 deletions(-)
arch/arm/cpu/armv8/cache_v8.c +20 -29
index a96ecda..67dbd46 100644
@@ -12,15 +12,14 @@
 DECLARE_GLOBAL_DATA_PTR;
 
 #ifndef CONFIG_SYS_DCACHE_OFF
-
-static void set_pgtable_section(u64 section, u64 memory_type)
+void set_pgtable_section(u64 *page_table, u64 index, u64 section,
+			 u64 memory_type)
 {
-	u64 *page_table = (u64 *)gd->arch.tlb_addr;
 	u64 value;
 
-	value = (section << SECTION_SHIFT) | PMD_TYPE_SECT | PMD_SECT_AF;
+	value = section | PMD_TYPE_SECT | PMD_SECT_AF;
 	value |= PMD_ATTRINDX(memory_type);
-	page_table[section] = value;
+	page_table[index] = value;
 }
 
 /* to activate the MMU we need to set up virtual memory */
@@ -28,10 +27,13 @@ static void mmu_setup(void)
 {
 	int i, j, el;
 	bd_t *bd = gd->bd;
+	u64 *page_table = (u64 *)gd->arch.tlb_addr;
 
 	/* Setup an identity-mapping for all spaces */
-	for (i = 0; i < (PGTABLE_SIZE >> 3); i++)
-		set_pgtable_section(i, MT_DEVICE_NGNRNE);
+	for (i = 0; i < (PGTABLE_SIZE >> 3); i++) {
+		set_pgtable_section(page_table, i, i << SECTION_SHIFT,
+				    MT_DEVICE_NGNRNE);
+	}
 
 	/* Setup an identity-mapping for all RAM space */
 	for (i = 0; i < CONFIG_NR_DRAM_BANKS; i++) {
@@ -39,36 +41,25 @@ static void mmu_setup(void)
 		ulong end = bd->bi_dram[i].start + bd->bi_dram[i].size;
 		for (j = start >> SECTION_SHIFT;
 		     j < end >> SECTION_SHIFT; j++) {
-			set_pgtable_section(j, MT_NORMAL);
+			set_pgtable_section(page_table, j, j << SECTION_SHIFT,
+					    MT_NORMAL);
 		}
 	}
 
 	/* load TTBR0 */
 	el = current_el();
 	if (el == 1) {
-		asm volatile("msr ttbr0_el1, %0"
-			     : : "r" (gd->arch.tlb_addr) : "memory");
-		asm volatile("msr tcr_el1, %0"
-			     : : "r" (TCR_FLAGS | TCR_EL1_IPS_BITS)
-			     : "memory");
-		asm volatile("msr mair_el1, %0"
-			     : : "r" (MEMORY_ATTRIBUTES) : "memory");
+		set_ttbr_tcr_mair(el, gd->arch.tlb_addr,
+				  TCR_FLAGS | TCR_EL1_IPS_BITS,
+				  MEMORY_ATTRIBUTES);
 	} else if (el == 2) {
-		asm volatile("msr ttbr0_el2, %0"
-			     : : "r" (gd->arch.tlb_addr) : "memory");
-		asm volatile("msr tcr_el2, %0"
-			     : : "r" (TCR_FLAGS | TCR_EL2_IPS_BITS)
-			     : "memory");
-		asm volatile("msr mair_el2, %0"
-			     : : "r" (MEMORY_ATTRIBUTES) : "memory");
+		set_ttbr_tcr_mair(el, gd->arch.tlb_addr,
+				  TCR_FLAGS | TCR_EL2_IPS_BITS,
+				  MEMORY_ATTRIBUTES);
 	} else {
-		asm volatile("msr ttbr0_el3, %0"
-			     : : "r" (gd->arch.tlb_addr) : "memory");
-		asm volatile("msr tcr_el3, %0"
-			     : : "r" (TCR_FLAGS | TCR_EL2_IPS_BITS)
-			     : "memory");
-		asm volatile("msr mair_el3, %0"
-			     : : "r" (MEMORY_ATTRIBUTES) : "memory");
+		set_ttbr_tcr_mair(el, gd->arch.tlb_addr,
+				  TCR_FLAGS | TCR_EL3_IPS_BITS,
+				  MEMORY_ATTRIBUTES);
 	}
 
 	/* enable the mmu */
arch/arm/include/asm/armv8/mmu.h +0 -0
index 1193e76..7de4ff9 100644
@@ -108,4 +108,27 @@
 				TCR_IRGN_WBWA |		\
 				TCR_T0SZ(VA_BITS))
 
Quoted 8 lines

arch/arm/include/asm/armv8/mmu.h line 116

Quoted 6 lines
arch/arm/include/asm/armv8/mmu.h +6 -0
+#ifndef __ASSEMBLY__
+void set_pgtable_section(u64 *page_table, u64 index,
+			 u64 section, u64 memory_type);
+static inline void set_ttbr_tcr_mair(int el, u64 table, u64 tcr, u64 attr)
+{
+	asm volatile("dsb sy;isb");

Huh? This wasn't anywhere before. Is the isb necessary?

Probably not, but to make sure all previous instructions finish.

Quoted 7 lines
Quoted 4 lines

Which instructions do you care about completing?

You'll certainly want any page table writes to complete, but is anything else in-flight at this point in time?

Before calling this inline function, MMU tables were created. We want the writing completes before setting these registers.

Sure, but the dsb sy guarantees that.

The dsb sy will ensure that all reads and writes before it have become visible to all observers in the full system shareability domain (including the MMU) before any subsequent instructions (in program order) can execute.

I'd only expect to see a "dsb $DOMAIN; isb" sequence is when we're changing things that affect the instruction stream (I-cache maintenance, changing the mapping underlying the currently executing stream).

Quoted 67 lines
Quoted 67 lines
Quoted 7 lines
Quoted 4 lines

When is this function expected to be called? Is the MMU expected to be on?

The general ARMv8 code calls this when MMU is off. For the SoC I am enabling, it is called when MMU is off for the first time, but MMU on for the 2nd time.

Ok.

Quoted 10 lines
Quoted 9 lines

arch/arm/include/asm/armv8/mmu.h line 120

Quoted 6 lines
arch/arm/include/asm/armv8/mmu.h +4 -0
+	if (el == 1) {
+		asm volatile("msr ttbr0_el1, %0" : : "r" (table) : "memory");
+		asm volatile("msr tcr_el1, %0" : : "r" (tcr) : "memory");
+		asm volatile("msr mair_el1, %0" : : "r" (attr) : "memory");

If the MMU is on, this looks really scary -- what are you expecting to change in a single invocation?

Quoted 3 lines

It is not scary for general ARMv8 code. MMU is off then this is called. For FSL SoC, I have these called for the 2nd time, when MMU is on. The 2nd time call points to a new MMU table.

Oh but it is ;)

Quoted 18 lines
Quoted 17 lines

arch/arm/include/asm/armv8/mmu.h line 131

Quoted 13 lines
arch/arm/include/asm/armv8/mmu.h +11 -0
+	} else if (el == 2) {
+		asm volatile("msr ttbr0_el2, %0" : : "r" (table) : "memory");
+		asm volatile("msr tcr_el2, %0" : : "r" (tcr) : "memory");
+		asm volatile("msr mair_el2, %0" : : "r" (attr) : "memory");
+	} else if (el == 3) {
+		asm volatile("msr ttbr0_el3, %0" : : "r" (table) : "memory");
+		asm volatile("msr tcr_el3, %0" : : "r" (tcr) : "memory");
+		asm volatile("msr mair_el3, %0" : : "r" (attr) : "memory");
+	} else {
+		hang();
+	}

And no synchronisation to ensure that these writes are complete or even ordered w.r.t. each other?

Quoted 3 lines

That's why I added asm volatile("dsb sy;isb") before them. The order of these write doesn't matter. See the code before my change
http://git.denx.de/?p=u-boot.git;a=blob;f=arch/arm/cpu/armv8/cache_v8.c;h=a96ecda7e3146996d20a2e46b975dced2769255c;hb=HEAD

Ok, so that orders them against everything _before_ them, but not with respect to each other, or anything _after_ them.

When the MMU is off, you are correct that the ordering of these operations w.r.t. each other doesn't matter. With the MMU on that's not true as the CPU can rerder the operations and can walk the page tables asynchronously.

Changing the MAIR at runtime is somewhat scary because you may be changing attributes for entries which are active in the cache and/or TLBs. I'm not sure I follow why you need to change it once the MMU is on -- there are plenty of subfields in the MAIR that you could configure before turning the MMU on for use later.

Likewise changing the TCR at runtime is somewhat scary because you can change attributes of active cache and/or TLB entries, change fields that affect the way page tables are walked (TG*. T*SZ), all asynchronously w.r.t. the logic that walsk the page tables.

Quoted 8 lines
Quoted 4 lines

Changing the TTBR is fine, except that we didn't invalidate old entries with a TLBI, so we may even have partial translations cahced in the TLBs, and we can get odd translations generated from the combination of the old and new tables.

Maybe a more proper way to do so is to change TTBR only. That's actually what I really need. Or if I really need to change all of them, I should turn off MMU first.

In fact, if you had an API for creating device mappings, you wouldn't even need to change the TTBR -- you'd just need to replace some existing invalid entries with the new mapping, make them visible to the MMU with a dsb, and then carry on. The TLBs aren't allowed to cache invalid mappings, so on the next access to those mappings, the appropriate page table entry will be read into the TLBs.

If you alter multiple levels of a live table then you'll need barriers (DMBs) between populating those levels. If you back device mappings by a single level of table then you can avoid that. Even better, with an API you can do the simple thing today then make it more advanced in future if necessary without having to change your board code.

Quoted 8 lines
Quoted 6 lines
Quoted 3 lines

For the 2nd write used by FSL SoC, I need to flush the cache to make sure the table is in main DDR and perform TLB invalidation. That's when the loading of new MMU table happens.

I missed the TLB invaliation -- where is that meant to happen?

After flushing the new MMU table into DDR. To your point above, I will send a new version with updating TTBR only.

Unfortunately, that's too early. The MMU is asynchronous w.r.t. the CPU and so it can re-read the existing tables into the TLBs _before_ it has been programmed with the new table address. The TLBI has to happen _after_ the old tables are no longer pointed to by the TTBR.

What you need to do to replace the active set of tables (assuming that the new mapping has the instruction stream mapped in an identical way) is:

- Write the tables.

- DSB to make them visible to the MMU.

- Write to the appropriate TTBR_*.

- ISB to complete the write to the TTBR_*.

- TLBI to invalidate the old mappings the the TLBs.

- DSB to complete the TLBI.

- If you've changed the instruction stream or system state that will affect the instruction stream, ISB to flush the CPU pipeline.

Cheers,
Mark.

author
#7 lore raw

PATCH v4 2/5 ARMv8: Adjust MMU setup

Quoted 24 lines · Mark Rutland
Quoted 17 lines · York Sun
Quoted 7 lines · Mark Rutland
Quoted 6 lines · York Sun
Quoted 4 lines · Mark Rutland
Quoted 1 line · York Sun

Make MMU functions reusable. Platform code can setup its own MMU tables.

What exactly does platform code need to setup its own tables for?

Quoted 8 lines
Quoted 3 lines

The general ARMv8 MMU table is not detail enough to control memory attribute like cache for all addresses. We have devices mapping to addresses with different requirement for cache control.

And there are no APIs for creating device mappings rather than exporting the raw pagetable accessors and hard-coding them differently in every board file?

Quoted 5 lines

That's a good question. At this point, only two platforms are using ARMv8 code. I am expecting FSL ARMv8 implementation will stay similar, i.e. covered by the file I added. If that's not the case, or more ARMv8 SoCs need special MMU table, we then should introduce such API. Having a full function MMU API may be an overkill for U-boot. We don't need dynamic MMU anyway.

Quoted 4 lines

Maybe. It just seems to me that it would be possible to pre-allocate an empty table that we could place device (nGnRnE?) mappings in. Then all you'd need to call from board code is a function to map a range, rather than having to duplicate logic for creating the tables you want.

It sounds good, but not the case. For the three level tables I am using (level0, level1, level2), I don't have level2 table for every address, that will be too many. Instead, I have a lot of blocks for level1. When I need some fine control within a level1 block range, I have to create a new level2 table. It is doable, but I will hold on that if I can use static table.

Quoted 135 lines
Quoted 125 lines
Quoted 125 lines
Quoted 124 lines
Quoted 114 lines
Quoted 114 lines

Also fix a typo of "TCR_EL3_IPS_BITS" in cache_v8.c.

---
Change log:
v4: new patch, splitted from v3 2/4
Revise set_pgtable_section() to be reused by platform MMU code
Add inline function set_ttbr_tcr_mair() to be used by this and platform mmu code

 arch/arm/cpu/armv8/cache_v8.c    |   49 ++++++++++++++++----------------------
 arch/arm/include/asm/armv8/mmu.h |   23 ++++++++++++++++++
 2 files changed, 43 insertions(+), 29 deletions(-)
arch/arm/cpu/armv8/cache_v8.c +20 -29
index a96ecda..67dbd46 100644
@@ -12,15 +12,14 @@
 DECLARE_GLOBAL_DATA_PTR;
 
 #ifndef CONFIG_SYS_DCACHE_OFF
-
-static void set_pgtable_section(u64 section, u64 memory_type)
+void set_pgtable_section(u64 *page_table, u64 index, u64 section,
+			 u64 memory_type)
 {
-	u64 *page_table = (u64 *)gd->arch.tlb_addr;
 	u64 value;
 
-	value = (section << SECTION_SHIFT) | PMD_TYPE_SECT | PMD_SECT_AF;
+	value = section | PMD_TYPE_SECT | PMD_SECT_AF;
 	value |= PMD_ATTRINDX(memory_type);
-	page_table[section] = value;
+	page_table[index] = value;
 }
 
 /* to activate the MMU we need to set up virtual memory */
@@ -28,10 +27,13 @@ static void mmu_setup(void)
 {
 	int i, j, el;
 	bd_t *bd = gd->bd;
+	u64 *page_table = (u64 *)gd->arch.tlb_addr;
 
 	/* Setup an identity-mapping for all spaces */
-	for (i = 0; i < (PGTABLE_SIZE >> 3); i++)
-		set_pgtable_section(i, MT_DEVICE_NGNRNE);
+	for (i = 0; i < (PGTABLE_SIZE >> 3); i++) {
+		set_pgtable_section(page_table, i, i << SECTION_SHIFT,
+				    MT_DEVICE_NGNRNE);
+	}
 
 	/* Setup an identity-mapping for all RAM space */
 	for (i = 0; i < CONFIG_NR_DRAM_BANKS; i++) {
@@ -39,36 +41,25 @@ static void mmu_setup(void)
 		ulong end = bd->bi_dram[i].start + bd->bi_dram[i].size;
 		for (j = start >> SECTION_SHIFT;
 		     j < end >> SECTION_SHIFT; j++) {
-			set_pgtable_section(j, MT_NORMAL);
+			set_pgtable_section(page_table, j, j << SECTION_SHIFT,
+					    MT_NORMAL);
 		}
 	}
 
 	/* load TTBR0 */
 	el = current_el();
 	if (el == 1) {
-		asm volatile("msr ttbr0_el1, %0"
-			     : : "r" (gd->arch.tlb_addr) : "memory");
-		asm volatile("msr tcr_el1, %0"
-			     : : "r" (TCR_FLAGS | TCR_EL1_IPS_BITS)
-			     : "memory");
-		asm volatile("msr mair_el1, %0"
-			     : : "r" (MEMORY_ATTRIBUTES) : "memory");
+		set_ttbr_tcr_mair(el, gd->arch.tlb_addr,
+				  TCR_FLAGS | TCR_EL1_IPS_BITS,
+				  MEMORY_ATTRIBUTES);
 	} else if (el == 2) {
-		asm volatile("msr ttbr0_el2, %0"
-			     : : "r" (gd->arch.tlb_addr) : "memory");
-		asm volatile("msr tcr_el2, %0"
-			     : : "r" (TCR_FLAGS | TCR_EL2_IPS_BITS)
-			     : "memory");
-		asm volatile("msr mair_el2, %0"
-			     : : "r" (MEMORY_ATTRIBUTES) : "memory");
+		set_ttbr_tcr_mair(el, gd->arch.tlb_addr,
+				  TCR_FLAGS | TCR_EL2_IPS_BITS,
+				  MEMORY_ATTRIBUTES);
 	} else {
-		asm volatile("msr ttbr0_el3, %0"
-			     : : "r" (gd->arch.tlb_addr) : "memory");
-		asm volatile("msr tcr_el3, %0"
-			     : : "r" (TCR_FLAGS | TCR_EL2_IPS_BITS)
-			     : "memory");
-		asm volatile("msr mair_el3, %0"
-			     : : "r" (MEMORY_ATTRIBUTES) : "memory");
+		set_ttbr_tcr_mair(el, gd->arch.tlb_addr,
+				  TCR_FLAGS | TCR_EL3_IPS_BITS,
+				  MEMORY_ATTRIBUTES);
 	}
 
 	/* enable the mmu */
arch/arm/include/asm/armv8/mmu.h +0 -0
index 1193e76..7de4ff9 100644
@@ -108,4 +108,27 @@
 				TCR_IRGN_WBWA |		\
 				TCR_T0SZ(VA_BITS))
 
Quoted 8 lines

arch/arm/include/asm/armv8/mmu.h line 116

Quoted 6 lines
arch/arm/include/asm/armv8/mmu.h +6 -0
+#ifndef __ASSEMBLY__
+void set_pgtable_section(u64 *page_table, u64 index,
+			 u64 section, u64 memory_type);
+static inline void set_ttbr_tcr_mair(int el, u64 table, u64 tcr, u64 attr)
+{
+	asm volatile("dsb sy;isb");

Huh? This wasn't anywhere before. Is the isb necessary?

Probably not, but to make sure all previous instructions finish.

Quoted 7 lines
Quoted 4 lines

Which instructions do you care about completing?

You'll certainly want any page table writes to complete, but is anything else in-flight at this point in time?

Before calling this inline function, MMU tables were created. We want the writing completes before setting these registers.

Sure, but the dsb sy guarantees that.

Quoted 8 lines

The dsb sy will ensure that all reads and writes before it have become visible to all observers in the full system shareability domain (including the MMU) before any subsequent instructions (in program order) can execute.

I'd only expect to see a "dsb $DOMAIN; isb" sequence is when we're changing things that affect the instruction stream (I-cache maintenance, changing the mapping underlying the currently executing stream).

Thanks. I can drop "isb". Please comment on v5 patches.

Quoted 84 lines
Quoted 68 lines
Quoted 68 lines
Quoted 8 lines
Quoted 5 lines

When is this function expected to be called? Is the MMU expected to be on?

The general ARMv8 code calls this when MMU is off. For the SoC I am enabling, it is called when MMU is off for the first time, but MMU on for the 2nd time.

Ok.

Quoted 10 lines
Quoted 9 lines

arch/arm/include/asm/armv8/mmu.h line 120

Quoted 6 lines
arch/arm/include/asm/armv8/mmu.h +4 -0
+	if (el == 1) {
+		asm volatile("msr ttbr0_el1, %0" : : "r" (table) : "memory");
+		asm volatile("msr tcr_el1, %0" : : "r" (tcr) : "memory");
+		asm volatile("msr mair_el1, %0" : : "r" (attr) : "memory");

If the MMU is on, this looks really scary -- what are you expecting to change in a single invocation?

Quoted 3 lines

It is not scary for general ARMv8 code. MMU is off then this is called. For FSL SoC, I have these called for the 2nd time, when MMU is on. The 2nd time call points to a new MMU table.

Oh but it is ;)

Quoted 18 lines
Quoted 17 lines

arch/arm/include/asm/armv8/mmu.h line 131

Quoted 13 lines
arch/arm/include/asm/armv8/mmu.h +11 -0
+	} else if (el == 2) {
+		asm volatile("msr ttbr0_el2, %0" : : "r" (table) : "memory");
+		asm volatile("msr tcr_el2, %0" : : "r" (tcr) : "memory");
+		asm volatile("msr mair_el2, %0" : : "r" (attr) : "memory");
+	} else if (el == 3) {
+		asm volatile("msr ttbr0_el3, %0" : : "r" (table) : "memory");
+		asm volatile("msr tcr_el3, %0" : : "r" (tcr) : "memory");
+		asm volatile("msr mair_el3, %0" : : "r" (attr) : "memory");
+	} else {
+		hang();
+	}

And no synchronisation to ensure that these writes are complete or even ordered w.r.t. each other?

Quoted 3 lines

That's why I added asm volatile("dsb sy;isb") before them. The order of these write doesn't matter. See the code before my change
http://git.denx.de/?p=u-boot.git;a=blob;f=arch/arm/cpu/armv8/cache_v8.c;h=a96ecda7e3146996d20a2e46b975dced2769255c;hb=HEAD

Ok, so that orders them against everything _before_ them, but not with respect to each other, or anything _after_ them.

When the MMU is off, you are correct that the ordering of these operations w.r.t. each other doesn't matter. With the MMU on that's not true as the CPU can rerder the operations and can walk the page tables asynchronously.

Changing the MAIR at runtime is somewhat scary because you may be changing attributes for entries which are active in the cache and/or TLBs. I'm not sure I follow why you need to change it once the MMU is on -- there are plenty of subfields in the MAIR that you could configure before turning the MMU on for use later.

Likewise changing the TCR at runtime is somewhat scary because you can change attributes of active cache and/or TLB entries, change fields that affect the way page tables are walked (TG*. T*SZ), all asynchronously w.r.t. the logic that walsk the page tables.

Quoted 8 lines
Quoted 4 lines

Changing the TTBR is fine, except that we didn't invalidate old entries with a TLBI, so we may even have partial translations cahced in the TLBs, and we can get odd translations generated from the combination of the old and new tables.

Maybe a more proper way to do so is to change TTBR only. That's actually what I really need. Or if I really need to change all of them, I should turn off MMU first.

In fact, if you had an API for creating device mappings, you wouldn't even need to change the TTBR -- you'd just need to replace some existing invalid entries with the new mapping, make them visible to the MMU with a dsb, and then carry on. The TLBs aren't allowed to cache invalid mappings, so on the next access to those mappings, the appropriate page table entry will be read into the TLBs.

Quoted 6 lines

If you alter multiple levels of a live table then you'll need barriers (DMBs) between populating those levels. If you back device mappings by a single level of table then you can avoid that. Even better, with an API you can do the simple thing today then make it more advanced in future if necessary without having to change your board code.

No objection here on the idea. But again this is not the case. My first MMU table is in SRAM, which is small and will be used for other purpose. The 2nd MMU table is in DDR. I could copy the table and do the maintenance as you said. For now, I want to stick with the static table and only create the API when I have to.

Quoted 24 lines
Quoted 8 lines
Quoted 6 lines
Quoted 3 lines

For the 2nd write used by FSL SoC, I need to flush the cache to make sure the table is in main DDR and perform TLB invalidation. That's when the loading of new MMU table happens.

I missed the TLB invaliation -- where is that meant to happen?

After flushing the new MMU table into DDR. To your point above, I will send a new version with updating TTBR only.

Unfortunately, that's too early. The MMU is asynchronous w.r.t. the CPU and so it can re-read the existing tables into the TLBs _before_ it has been programmed with the new table address. The TLBI has to happen _after_ the old tables are no longer pointed to by the TTBR.

What you need to do to replace the active set of tables (assuming that the new mapping has the instruction stream mapped in an identical way) is:

- Write the tables.

- DSB to make them visible to the MMU.

- Write to the appropriate TTBR_*.

Quoted 10 lines

- ISB to complete the write to the TTBR_*.

- TLBI to invalidate the old mappings the the TLBs.

- DSB to complete the TLBI.

- If you've changed the instruction stream or system state that will affect the instruction stream, ISB to flush the CPU pipeline.

Here is the flow I have (as of v5 patch)

Write the tables

(I removed dsb here in v5, need to add back)

Write TTBR

(I missed isb here, need to add)

Flush dcache (otherwise the table will not be in DDR. Yes, I verified)

TLBI

(DSB and ISB TLBI function)

I will need to send v6 patch to add the missing parts.

York

#8 lore raw

PATCH v4 2/5 ARMv8: Adjust MMU setup

Quoted 30 lines · York Sun
Quoted 24 lines · Mark Rutland
Quoted 17 lines · York Sun
Quoted 7 lines · Mark Rutland
Quoted 6 lines · York Sun
Quoted 4 lines · Mark Rutland
Quoted 1 line · York Sun

Make MMU functions reusable. Platform code can setup its own MMU tables.

What exactly does platform code need to setup its own tables for?

Quoted 8 lines
Quoted 3 lines

The general ARMv8 MMU table is not detail enough to control memory attribute like cache for all addresses. We have devices mapping to addresses with different requirement for cache control.

And there are no APIs for creating device mappings rather than exporting the raw pagetable accessors and hard-coding them differently in every board file?

Quoted 5 lines

That's a good question. At this point, only two platforms are using ARMv8 code. I am expecting FSL ARMv8 implementation will stay similar, i.e. covered by the file I added. If that's not the case, or more ARMv8 SoCs need special MMU table, we then should introduce such API. Having a full function MMU API may be an overkill for U-boot. We don't need dynamic MMU anyway.

Quoted 4 lines

Maybe. It just seems to me that it would be possible to pre-allocate an empty table that we could place device (nGnRnE?) mappings in. Then all you'd need to call from board code is a function to map a range, rather than having to duplicate logic for creating the tables you want.

Quoted 5 lines

It sounds good, but not the case. For the three level tables I am using (level0, level1, level2), I don't have level2 table for every address, that will be too many. Instead, I have a lot of blocks for level1. When I need some fine control within a level1 block range, I have to create a new level2 table. It is doable, but I will hold on that if I can use static table.

While my suggestion might not be the best, I'm not sure I follow, unless you always want to idmap devices?

If you don't idmap devices, then you can place all of the disparate physical mappings within a single table unless you have very large peripherals to map?

Quoted 141 lines
Quoted 141 lines
Quoted 126 lines
Quoted 126 lines
Quoted 125 lines
Quoted 115 lines
Quoted 115 lines

Also fix a typo of "TCR_EL3_IPS_BITS" in cache_v8.c.

---
Change log:
v4: new patch, splitted from v3 2/4
Revise set_pgtable_section() to be reused by platform MMU code
Add inline function set_ttbr_tcr_mair() to be used by this and platform mmu code

 arch/arm/cpu/armv8/cache_v8.c    |   49 ++++++++++++++++----------------------
 arch/arm/include/asm/armv8/mmu.h |   23 ++++++++++++++++++
 2 files changed, 43 insertions(+), 29 deletions(-)
arch/arm/cpu/armv8/cache_v8.c +20 -29
index a96ecda..67dbd46 100644
@@ -12,15 +12,14 @@
 DECLARE_GLOBAL_DATA_PTR;
 
 #ifndef CONFIG_SYS_DCACHE_OFF
-
-static void set_pgtable_section(u64 section, u64 memory_type)
+void set_pgtable_section(u64 *page_table, u64 index, u64 section,
+                         u64 memory_type)
 {
-        u64 *page_table = (u64 *)gd->arch.tlb_addr;
         u64 value;
 
-        value = (section << SECTION_SHIFT) | PMD_TYPE_SECT | PMD_SECT_AF;
+        value = section | PMD_TYPE_SECT | PMD_SECT_AF;
         value |= PMD_ATTRINDX(memory_type);
-        page_table[section] = value;
+        page_table[index] = value;
 }
 
 /* to activate the MMU we need to set up virtual memory */
@@ -28,10 +27,13 @@ static void mmu_setup(void)
 {
         int i, j, el;
         bd_t *bd = gd->bd;
+        u64 *page_table = (u64 *)gd->arch.tlb_addr;
 
         /* Setup an identity-mapping for all spaces */
-        for (i = 0; i < (PGTABLE_SIZE >> 3); i++)
-                set_pgtable_section(i, MT_DEVICE_NGNRNE);
+        for (i = 0; i < (PGTABLE_SIZE >> 3); i++) {
+                set_pgtable_section(page_table, i, i << SECTION_SHIFT,
+                                    MT_DEVICE_NGNRNE);
+        }
 
         /* Setup an identity-mapping for all RAM space */
         for (i = 0; i < CONFIG_NR_DRAM_BANKS; i++) {
@@ -39,36 +41,25 @@ static void mmu_setup(void)
                 ulong end = bd->bi_dram[i].start + bd->bi_dram[i].size;
                 for (j = start >> SECTION_SHIFT;
                      j < end >> SECTION_SHIFT; j++) {
-                        set_pgtable_section(j, MT_NORMAL);
+                        set_pgtable_section(page_table, j, j << SECTION_SHIFT,
+                                            MT_NORMAL);
                 }
         }
 
         /* load TTBR0 */
         el = current_el();
         if (el == 1) {
-                asm volatile("msr ttbr0_el1, %0"
-                             : : "r" (gd->arch.tlb_addr) : "memory");
-                asm volatile("msr tcr_el1, %0"
-                             : : "r" (TCR_FLAGS | TCR_EL1_IPS_BITS)
-                             : "memory");
-                asm volatile("msr mair_el1, %0"
-                             : : "r" (MEMORY_ATTRIBUTES) : "memory");
+                set_ttbr_tcr_mair(el, gd->arch.tlb_addr,
+                                  TCR_FLAGS | TCR_EL1_IPS_BITS,
+                                  MEMORY_ATTRIBUTES);
         } else if (el == 2) {
-                asm volatile("msr ttbr0_el2, %0"
-                             : : "r" (gd->arch.tlb_addr) : "memory");
-                asm volatile("msr tcr_el2, %0"
-                             : : "r" (TCR_FLAGS | TCR_EL2_IPS_BITS)
-                             : "memory");
-                asm volatile("msr mair_el2, %0"
-                             : : "r" (MEMORY_ATTRIBUTES) : "memory");
+                set_ttbr_tcr_mair(el, gd->arch.tlb_addr,
+                                  TCR_FLAGS | TCR_EL2_IPS_BITS,
+                                  MEMORY_ATTRIBUTES);
         } else {
-                asm volatile("msr ttbr0_el3, %0"
-                             : : "r" (gd->arch.tlb_addr) : "memory");
-                asm volatile("msr tcr_el3, %0"
-                             : : "r" (TCR_FLAGS | TCR_EL2_IPS_BITS)
-                             : "memory");
-                asm volatile("msr mair_el3, %0"
-                             : : "r" (MEMORY_ATTRIBUTES) : "memory");
+                set_ttbr_tcr_mair(el, gd->arch.tlb_addr,
+                                  TCR_FLAGS | TCR_EL3_IPS_BITS,
+                                  MEMORY_ATTRIBUTES);
         }
 
         /* enable the mmu */
arch/arm/include/asm/armv8/mmu.h +0 -0
index 1193e76..7de4ff9 100644
@@ -108,4 +108,27 @@
                                 TCR_IRGN_WBWA |         \
                                 TCR_T0SZ(VA_BITS))
 
Quoted 8 lines

arch/arm/include/asm/armv8/mmu.h line 116

Quoted 6 lines
arch/arm/include/asm/armv8/mmu.h +6 -0
+#ifndef __ASSEMBLY__
+void set_pgtable_section(u64 *page_table, u64 index,
+                         u64 section, u64 memory_type);
+static inline void set_ttbr_tcr_mair(int el, u64 table, u64 tcr, u64 attr)
+{
+        asm volatile("dsb sy;isb");

Huh? This wasn't anywhere before. Is the isb necessary?

Probably not, but to make sure all previous instructions finish.

Quoted 7 lines
Quoted 4 lines

Which instructions do you care about completing?

You'll certainly want any page table writes to complete, but is anything else in-flight at this point in time?

Before calling this inline function, MMU tables were created. We want the writing completes before setting these registers.

Sure, but the dsb sy guarantees that.

The dsb sy will ensure that all reads and writes before it have become visible to all observers in the full system shareability domain (including the MMU) before any subsequent instructions (in program order) can execute.

Quoted 5 lines
Quoted 3 lines

I'd only expect to see a "dsb $DOMAIN; isb" sequence is when we're changing things that affect the instruction stream (I-cache maintenance, changing the mapping underlying the currently executing stream).

Thanks. I can drop "isb". Please comment on v5 patches.

Will do momentarily.

Quoted 92 lines
Quoted 85 lines
Quoted 69 lines
Quoted 69 lines
Quoted 9 lines
Quoted 6 lines

When is this function expected to be called? Is the MMU expected to be on?

The general ARMv8 code calls this when MMU is off. For the SoC I am enabling, it is called when MMU is off for the first time, but MMU on for the 2nd time.

Ok.

Quoted 7 lines
Quoted 7 lines
Quoted 6 lines
arch/arm/include/asm/armv8/mmu.h +4 -0
+        if (el == 1) {
+                asm volatile("msr ttbr0_el1, %0" : : "r" (table) : "memory");
+                asm volatile("msr tcr_el1, %0" : : "r" (tcr) : "memory");
+                asm volatile("msr mair_el1, %0" : : "r" (attr) : "memory");
Quoted 6 lines
Quoted 2 lines

If the MMU is on, this looks really scary -- what are you expecting to change in a single invocation?

It is not scary for general ARMv8 code. MMU is off then this is called. For FSL SoC, I have these called for the 2nd time, when MMU is on. The 2nd time call points to a new MMU table.

Oh but it is ;)

Quoted 14 lines
Quoted 14 lines
Quoted 13 lines
arch/arm/include/asm/armv8/mmu.h +11 -0
+        } else if (el == 2) {
+                asm volatile("msr ttbr0_el2, %0" : : "r" (table) : "memory");
+                asm volatile("msr tcr_el2, %0" : : "r" (tcr) : "memory");
+                asm volatile("msr mair_el2, %0" : : "r" (attr) : "memory");
+        } else if (el == 3) {
+                asm volatile("msr ttbr0_el3, %0" : : "r" (table) : "memory");
+                asm volatile("msr tcr_el3, %0" : : "r" (tcr) : "memory");
+                asm volatile("msr mair_el3, %0" : : "r" (attr) : "memory");
+        } else {
+                hang();
+        }
Quoted 7 lines
Quoted 3 lines

And no synchronisation to ensure that these writes are complete or even ordered w.r.t. each other?

That's why I added asm volatile("dsb sy;isb") before them. The order of these write doesn't matter. See the code before my change
http://git.denx.de/?p=u-boot.git;a=blob;f=arch/arm/cpu/armv8/cache_v8.c;h=a96ecda7e3146996d20a2e46b975dced2769255c;hb=HEAD

Ok, so that orders them against everything _before_ them, but not with respect to each other, or anything _after_ them.

When the MMU is off, you are correct that the ordering of these operations w.r.t. each other doesn't matter. With the MMU on that's not true as the CPU can rerder the operations and can walk the page tables asynchronously.

Changing the MAIR at runtime is somewhat scary because you may be changing attributes for entries which are active in the cache and/or TLBs. I'm not sure I follow why you need to change it once the MMU is on -- there are plenty of subfields in the MAIR that you could configure before turning the MMU on for use later.

Likewise changing the TCR at runtime is somewhat scary because you can change attributes of active cache and/or TLB entries, change fields that affect the way page tables are walked (TG*. T*SZ), all asynchronously w.r.t. the logic that walsk the page tables.

Quoted 8 lines
Quoted 4 lines

Changing the TTBR is fine, except that we didn't invalidate old entries with a TLBI, so we may even have partial translations cahced in the TLBs, and we can get odd translations generated from the combination of the old and new tables.

Maybe a more proper way to do so is to change TTBR only. That's actually what I really need. Or if I really need to change all of them, I should turn off MMU first.

In fact, if you had an API for creating device mappings, you wouldn't even need to change the TTBR -- you'd just need to replace some existing invalid entries with the new mapping, make them visible to the MMU with a dsb, and then carry on. The TLBs aren't allowed to cache invalid mappings, so on the next access to those mappings, the appropriate page table entry will be read into the TLBs.

Quoted 6 lines

If you alter multiple levels of a live table then you'll need barriers (DMBs) between populating those levels. If you back device mappings by a single level of table then you can avoid that. Even better, with an API you can do the simple thing today then make it more advanced in future if necessary without having to change your board code.

Quoted 4 lines

No objection here on the idea. But again this is not the case. My first MMU table is in SRAM, which is small and will be used for other purpose. The 2nd MMU table is in DDR. I could copy the table and do the maintenance as you said. For now, I want to stick with the static table and only create the API when I have to.

Sure, if your tables are in SRAM then trying to do a load of dynamic allocation isn't going to work.

My fear is that while that sounds OK with a single user to do a quick havk and poke the tables directly, we'll end up with everyone doing that, and no-one will try to unify things. It is very diffifcult to unify such variation after the fact.

Quoted 38 lines
Quoted 24 lines
Quoted 8 lines
Quoted 6 lines
Quoted 3 lines

For the 2nd write used by FSL SoC, I need to flush the cache to make sure the table is in main DDR and perform TLB invalidation. That's when the loading of new MMU table happens.

I missed the TLB invaliation -- where is that meant to happen?

After flushing the new MMU table into DDR. To your point above, I will send a new version with updating TTBR only.

Unfortunately, that's too early. The MMU is asynchronous w.r.t. the CPU and so it can re-read the existing tables into the TLBs _before_ it has been programmed with the new table address. The TLBI has to happen _after_ the old tables are no longer pointed to by the TTBR.

What you need to do to replace the active set of tables (assuming that the new mapping has the instruction stream mapped in an identical way) is:

- Write the tables.

- DSB to make them visible to the MMU.

- Write to the appropriate TTBR_*.

Quoted 10 lines

- ISB to complete the write to the TTBR_*.

- TLBI to invalidate the old mappings the the TLBs.

- DSB to complete the TLBI.

- If you've changed the instruction stream or system state that will affect the instruction stream, ISB to flush the CPU pipeline.

Here is the flow I have (as of v5 patch)

Write the tables

Quoted 7 lines

(I removed dsb here in v5, need to add back)

Write TTBR

(I missed isb here, need to add)

Flush dcache (otherwise the table will not be in DDR. Yes, I verified)

This looks odd -- why do we need the tables to be in DDR? Why would we flush them here, when the address is partially visible to the MMU?

Quoted 3 lines

TLBI

(DSB and ISB TLBI function)

Why another TLBI?

Quoted 2 lines

I will need to send v6 patch to add the missing parts.

Sure, I'll hold off replies here until then.

Cheers,
Mark

author
#9 lore raw

PATCH v4 2/5 ARMv8: Adjust MMU setup

Quoted 37 lines · Mark Rutland
Quoted 30 lines · York Sun
Quoted 24 lines · Mark Rutland
Quoted 17 lines · York Sun
Quoted 7 lines · Mark Rutland
Quoted 6 lines · York Sun
Quoted 4 lines · Mark Rutland
Quoted 1 line · York Sun

Make MMU functions reusable. Platform code can setup its own MMU tables.

What exactly does platform code need to setup its own tables for?

Quoted 8 lines
Quoted 3 lines

The general ARMv8 MMU table is not detail enough to control memory attribute like cache for all addresses. We have devices mapping to addresses with different requirement for cache control.

And there are no APIs for creating device mappings rather than exporting the raw pagetable accessors and hard-coding them differently in every board file?

Quoted 5 lines

That's a good question. At this point, only two platforms are using ARMv8 code. I am expecting FSL ARMv8 implementation will stay similar, i.e. covered by the file I added. If that's not the case, or more ARMv8 SoCs need special MMU table, we then should introduce such API. Having a full function MMU API may be an overkill for U-boot. We don't need dynamic MMU anyway.

Quoted 4 lines

Maybe. It just seems to me that it would be possible to pre-allocate an empty table that we could place device (nGnRnE?) mappings in. Then all you'd need to call from board code is a function to map a range, rather than having to duplicate logic for creating the tables you want.

Quoted 5 lines

It sounds good, but not the case. For the three level tables I am using (level0, level1, level2), I don't have level2 table for every address, that will be too many. Instead, I have a lot of blocks for level1. When I need some fine control within a level1 block range, I have to create a new level2 table. It is doable, but I will hold on that if I can use static table.

Quoted 6 lines

While my suggestion might not be the best, I'm not sure I follow, unless you always want to idmap devices?

If you don't idmap devices, then you can place all of the disparate physical mappings within a single table unless you have very large peripherals to map?

If you mean identical map as idmap, yes I am creating identical map for devices. I got your point. For this particular SoC, if I can get it work with these simple static tables, I will stay with them. But if I need to maintain the tables for various SoCs, I will convert to dynamic API.

Quoted 247 lines
Quoted 142 lines
Quoted 142 lines
Quoted 127 lines
Quoted 127 lines
Quoted 126 lines
Quoted 116 lines
Quoted 116 lines

Also fix a typo of "TCR_EL3_IPS_BITS" in cache_v8.c.

---
Change log:
v4: new patch, splitted from v3 2/4
Revise set_pgtable_section() to be reused by platform MMU code
Add inline function set_ttbr_tcr_mair() to be used by this and platform mmu code

 arch/arm/cpu/armv8/cache_v8.c    |   49 ++++++++++++++++----------------------
 arch/arm/include/asm/armv8/mmu.h |   23 ++++++++++++++++++
 2 files changed, 43 insertions(+), 29 deletions(-)
arch/arm/cpu/armv8/cache_v8.c +20 -29
index a96ecda..67dbd46 100644
@@ -12,15 +12,14 @@
 DECLARE_GLOBAL_DATA_PTR;
 
 #ifndef CONFIG_SYS_DCACHE_OFF
-
-static void set_pgtable_section(u64 section, u64 memory_type)
+void set_pgtable_section(u64 *page_table, u64 index, u64 section,
+                         u64 memory_type)
 {
-        u64 *page_table = (u64 *)gd->arch.tlb_addr;
         u64 value;
 
-        value = (section << SECTION_SHIFT) | PMD_TYPE_SECT | PMD_SECT_AF;
+        value = section | PMD_TYPE_SECT | PMD_SECT_AF;
         value |= PMD_ATTRINDX(memory_type);
-        page_table[section] = value;
+        page_table[index] = value;
 }
 
 /* to activate the MMU we need to set up virtual memory */
@@ -28,10 +27,13 @@ static void mmu_setup(void)
 {
         int i, j, el;
         bd_t *bd = gd->bd;
+        u64 *page_table = (u64 *)gd->arch.tlb_addr;
 
         /* Setup an identity-mapping for all spaces */
-        for (i = 0; i < (PGTABLE_SIZE >> 3); i++)
-                set_pgtable_section(i, MT_DEVICE_NGNRNE);
+        for (i = 0; i < (PGTABLE_SIZE >> 3); i++) {
+                set_pgtable_section(page_table, i, i << SECTION_SHIFT,
+                                    MT_DEVICE_NGNRNE);
+        }
 
         /* Setup an identity-mapping for all RAM space */
         for (i = 0; i < CONFIG_NR_DRAM_BANKS; i++) {
@@ -39,36 +41,25 @@ static void mmu_setup(void)
                 ulong end = bd->bi_dram[i].start + bd->bi_dram[i].size;
                 for (j = start >> SECTION_SHIFT;
                      j < end >> SECTION_SHIFT; j++) {
-                        set_pgtable_section(j, MT_NORMAL);
+                        set_pgtable_section(page_table, j, j << SECTION_SHIFT,
+                                            MT_NORMAL);
                 }
         }
 
         /* load TTBR0 */
         el = current_el();
         if (el == 1) {
-                asm volatile("msr ttbr0_el1, %0"
-                             : : "r" (gd->arch.tlb_addr) : "memory");
-                asm volatile("msr tcr_el1, %0"
-                             : : "r" (TCR_FLAGS | TCR_EL1_IPS_BITS)
-                             : "memory");
-                asm volatile("msr mair_el1, %0"
-                             : : "r" (MEMORY_ATTRIBUTES) : "memory");
+                set_ttbr_tcr_mair(el, gd->arch.tlb_addr,
+                                  TCR_FLAGS | TCR_EL1_IPS_BITS,
+                                  MEMORY_ATTRIBUTES);
         } else if (el == 2) {
-                asm volatile("msr ttbr0_el2, %0"
-                             : : "r" (gd->arch.tlb_addr) : "memory");
-                asm volatile("msr tcr_el2, %0"
-                             : : "r" (TCR_FLAGS | TCR_EL2_IPS_BITS)
-                             : "memory");
-                asm volatile("msr mair_el2, %0"
-                             : : "r" (MEMORY_ATTRIBUTES) : "memory");
+                set_ttbr_tcr_mair(el, gd->arch.tlb_addr,
+                                  TCR_FLAGS | TCR_EL2_IPS_BITS,
+                                  MEMORY_ATTRIBUTES);
         } else {
-                asm volatile("msr ttbr0_el3, %0"
-                             : : "r" (gd->arch.tlb_addr) : "memory");
-                asm volatile("msr tcr_el3, %0"
-                             : : "r" (TCR_FLAGS | TCR_EL2_IPS_BITS)
-                             : "memory");
-                asm volatile("msr mair_el3, %0"
-                             : : "r" (MEMORY_ATTRIBUTES) : "memory");
+                set_ttbr_tcr_mair(el, gd->arch.tlb_addr,
+                                  TCR_FLAGS | TCR_EL3_IPS_BITS,
+                                  MEMORY_ATTRIBUTES);
         }
 
         /* enable the mmu */
arch/arm/include/asm/armv8/mmu.h +0 -0
index 1193e76..7de4ff9 100644
@@ -108,4 +108,27 @@
                                 TCR_IRGN_WBWA |         \
                                 TCR_T0SZ(VA_BITS))
 
Quoted 8 lines

arch/arm/include/asm/armv8/mmu.h line 116

Quoted 6 lines
arch/arm/include/asm/armv8/mmu.h +6 -0
+#ifndef __ASSEMBLY__
+void set_pgtable_section(u64 *page_table, u64 index,
+                         u64 section, u64 memory_type);
+static inline void set_ttbr_tcr_mair(int el, u64 table, u64 tcr, u64 attr)
+{
+        asm volatile("dsb sy;isb");

Huh? This wasn't anywhere before. Is the isb necessary?

Probably not, but to make sure all previous instructions finish.

Quoted 7 lines
Quoted 4 lines

Which instructions do you care about completing?

You'll certainly want any page table writes to complete, but is anything else in-flight at this point in time?

Before calling this inline function, MMU tables were created. We want the writing completes before setting these registers.

Sure, but the dsb sy guarantees that.

The dsb sy will ensure that all reads and writes before it have become visible to all observers in the full system shareability domain (including the MMU) before any subsequent instructions (in program order) can execute.

Quoted 5 lines
Quoted 3 lines

I'd only expect to see a "dsb $DOMAIN; isb" sequence is when we're changing things that affect the instruction stream (I-cache maintenance, changing the mapping underlying the currently executing stream).

Thanks. I can drop "isb". Please comment on v5 patches.

Will do momentarily.

Quoted 92 lines
Quoted 85 lines
Quoted 69 lines
Quoted 69 lines
Quoted 9 lines
Quoted 6 lines

When is this function expected to be called? Is the MMU expected to be on?

The general ARMv8 code calls this when MMU is off. For the SoC I am enabling, it is called when MMU is off for the first time, but MMU on for the 2nd time.

Ok.

Quoted 7 lines
Quoted 7 lines
Quoted 6 lines
arch/arm/include/asm/armv8/mmu.h +4 -0
+        if (el == 1) {
+                asm volatile("msr ttbr0_el1, %0" : : "r" (table) : "memory");
+                asm volatile("msr tcr_el1, %0" : : "r" (tcr) : "memory");
+                asm volatile("msr mair_el1, %0" : : "r" (attr) : "memory");
Quoted 6 lines
Quoted 2 lines

If the MMU is on, this looks really scary -- what are you expecting to change in a single invocation?

It is not scary for general ARMv8 code. MMU is off then this is called. For FSL SoC, I have these called for the 2nd time, when MMU is on. The 2nd time call points to a new MMU table.

Oh but it is ;)

Quoted 14 lines
Quoted 14 lines
Quoted 13 lines
arch/arm/include/asm/armv8/mmu.h +11 -0
+        } else if (el == 2) {
+                asm volatile("msr ttbr0_el2, %0" : : "r" (table) : "memory");
+                asm volatile("msr tcr_el2, %0" : : "r" (tcr) : "memory");
+                asm volatile("msr mair_el2, %0" : : "r" (attr) : "memory");
+        } else if (el == 3) {
+                asm volatile("msr ttbr0_el3, %0" : : "r" (table) : "memory");
+                asm volatile("msr tcr_el3, %0" : : "r" (tcr) : "memory");
+                asm volatile("msr mair_el3, %0" : : "r" (attr) : "memory");
+        } else {
+                hang();
+        }
Quoted 7 lines
Quoted 3 lines

And no synchronisation to ensure that these writes are complete or even ordered w.r.t. each other?

That's why I added asm volatile("dsb sy;isb") before them. The order of these write doesn't matter. See the code before my change
http://git.denx.de/?p=u-boot.git;a=blob;f=arch/arm/cpu/armv8/cache_v8.c;h=a96ecda7e3146996d20a2e46b975dced2769255c;hb=HEAD

Ok, so that orders them against everything _before_ them, but not with respect to each other, or anything _after_ them.

When the MMU is off, you are correct that the ordering of these operations w.r.t. each other doesn't matter. With the MMU on that's not true as the CPU can rerder the operations and can walk the page tables asynchronously.

Changing the MAIR at runtime is somewhat scary because you may be changing attributes for entries which are active in the cache and/or TLBs. I'm not sure I follow why you need to change it once the MMU is on -- there are plenty of subfields in the MAIR that you could configure before turning the MMU on for use later.

Likewise changing the TCR at runtime is somewhat scary because you can change attributes of active cache and/or TLB entries, change fields that affect the way page tables are walked (TG*. T*SZ), all asynchronously w.r.t. the logic that walsk the page tables.

Quoted 8 lines
Quoted 4 lines

Changing the TTBR is fine, except that we didn't invalidate old entries with a TLBI, so we may even have partial translations cahced in the TLBs, and we can get odd translations generated from the combination of the old and new tables.

Maybe a more proper way to do so is to change TTBR only. That's actually what I really need. Or if I really need to change all of them, I should turn off MMU first.

In fact, if you had an API for creating device mappings, you wouldn't even need to change the TTBR -- you'd just need to replace some existing invalid entries with the new mapping, make them visible to the MMU with a dsb, and then carry on. The TLBs aren't allowed to cache invalid mappings, so on the next access to those mappings, the appropriate page table entry will be read into the TLBs.

Quoted 6 lines

If you alter multiple levels of a live table then you'll need barriers (DMBs) between populating those levels. If you back device mappings by a single level of table then you can avoid that. Even better, with an API you can do the simple thing today then make it more advanced in future if necessary without having to change your board code.

Quoted 4 lines

No objection here on the idea. But again this is not the case. My first MMU table is in SRAM, which is small and will be used for other purpose. The 2nd MMU table is in DDR. I could copy the table and do the maintenance as you said. For now, I want to stick with the static table and only create the API when I have to.

Quoted 7 lines

Sure, if your tables are in SRAM then trying to do a load of dynamic allocation isn't going to work.

My fear is that while that sounds OK with a single user to do a quick havk and poke the tables directly, we'll end up with everyone doing that, and no-one will try to unify things. It is very diffifcult to unify such variation after the fact.

That's a good reason. Let me start to code the API. It will take a while to cover the complexity of the multilevel tables and sizes. It will be a separated patch for later review. I don't want that to delay this patch set. I am hoping to get this set in for 2014.07 release.

Quoted 41 lines
Quoted 41 lines
Quoted 25 lines
Quoted 9 lines
Quoted 7 lines
Quoted 4 lines

For the 2nd write used by FSL SoC, I need to flush the cache to make sure the table is in main DDR and perform TLB invalidation. That's when the loading of new MMU table happens.

I missed the TLB invaliation -- where is that meant to happen?

After flushing the new MMU table into DDR. To your point above, I will send a new version with updating TTBR only.

Unfortunately, that's too early. The MMU is asynchronous w.r.t. the CPU and so it can re-read the existing tables into the TLBs _before_ it has been programmed with the new table address. The TLBI has to happen _after_ the old tables are no longer pointed to by the TTBR.

What you need to do to replace the active set of tables (assuming that the new mapping has the instruction stream mapped in an identical way) is:

- Write the tables.

- DSB to make them visible to the MMU.

- Write to the appropriate TTBR_*.

Quoted 10 lines

- ISB to complete the write to the TTBR_*.

- TLBI to invalidate the old mappings the the TLBs.

- DSB to complete the TLBI.

- If you've changed the instruction stream or system state that will affect the instruction stream, ISB to flush the CPU pipeline.

Here is the flow I have (as of v5 patch)

Write the tables

(I removed dsb here in v5, need to add back)

Quoted 8 lines
Quoted 5 lines

Write TTBR

(I missed isb here, need to add)

Flush dcache (otherwise the table will not be in DDR. Yes, I verified)

This looks odd -- why do we need the tables to be in DDR? Why would we flush them here, when the address is partially visible to the MMU?

This sounds odd but it actually makes sense. Let's say we have new tables created by u-boot. The new tables are in the address of DDR with D-cache enabled. If we don't flush the cache, the moment TLBs are invalidated, MMU will fetch the tables from TTBR which points to DDR. Without a valid TLB, MMU cannot get the table from D-cache, it has to fetch from the DDR directly. I have verified this by checking waveforms of the SoC and exercised code in both ways.

Quoted 6 lines
Quoted 4 lines

TLBI

(DSB and ISB TLBI function)

Why another TLBI?

Not another one. I just point out the DSB and ISB are part of the existing function in u-boot.

Quoted 6 lines
Quoted 3 lines

I will need to send v6 patch to add the missing parts.

Sure, I'll hold off replies here until then.

v6 is coming soon today.

York

#10 lore raw

PATCH v4 2/5 ARMv8: Adjust MMU setup

[...]

Quoted 35 lines
Quoted 26 lines
Quoted 26 lines
Quoted 10 lines

What you need to do to replace the active set of tables (assuming that the new mapping has the instruction stream mapped in an identical way) is:

- Write the tables.

- DSB to make them visible to the MMU.

- Write to the appropriate TTBR_*.

Quoted 10 lines

- ISB to complete the write to the TTBR_*.

- TLBI to invalidate the old mappings the the TLBs.

- DSB to complete the TLBI.

- If you've changed the instruction stream or system state that will affect the instruction stream, ISB to flush the CPU pipeline.

Here is the flow I have (as of v5 patch)

Write the tables

(I removed dsb here in v5, need to add back)

Quoted 8 lines
Quoted 5 lines

Write TTBR

(I missed isb here, need to add)

Flush dcache (otherwise the table will not be in DDR. Yes, I verified)

This looks odd -- why do we need the tables to be in DDR? Why would we flush them here, when the address is partially visible to the MMU?

Quoted 6 lines

This sounds odd but it actually makes sense. Let's say we have new tables created by u-boot. The new tables are in the address of DDR with D-cache enabled. If we don't flush the cache, the moment TLBs are invalidated, MMU will fetch the tables from TTBR which points to DDR. Without a valid TLB, MMU cannot get the table from D-cache, it has to fetch from the DDR directly. I have verified this by checking waveforms of the SoC and exercised code in both ways.

How is TCR_EL2.SH0 (or TCR_EL1.SH*) configured?

You'll only need to flush the cache if they're configured non shareable.

Cheers,
Mark.

author
#11 lore raw

PATCH v4 2/5 ARMv8: Adjust MMU setup

Quoted 44 lines · Mark Rutland

[...]

Quoted 35 lines
Quoted 26 lines
Quoted 26 lines
Quoted 10 lines

What you need to do to replace the active set of tables (assuming that the new mapping has the instruction stream mapped in an identical way) is:

- Write the tables.

- DSB to make them visible to the MMU.

- Write to the appropriate TTBR_*.

Quoted 10 lines

- ISB to complete the write to the TTBR_*.

- TLBI to invalidate the old mappings the the TLBs.

- DSB to complete the TLBI.

- If you've changed the instruction stream or system state that will affect the instruction stream, ISB to flush the CPU pipeline.

Here is the flow I have (as of v5 patch)

Write the tables

(I removed dsb here in v5, need to add back)

Quoted 8 lines
Quoted 5 lines

Write TTBR

(I missed isb here, need to add)

Flush dcache (otherwise the table will not be in DDR. Yes, I verified)

This looks odd -- why do we need the tables to be in DDR? Why would we flush them here, when the address is partially visible to the MMU?

Quoted 6 lines

This sounds odd but it actually makes sense. Let's say we have new tables created by u-boot. The new tables are in the address of DDR with D-cache enabled. If we don't flush the cache, the moment TLBs are invalidated, MMU will fetch the tables from TTBR which points to DDR. Without a valid TLB, MMU cannot get the table from D-cache, it has to fetch from the DDR directly. I have verified this by checking waveforms of the SoC and exercised code in both ways.

Quoted 3 lines

How is TCR_EL2.SH0 (or TCR_EL1.SH*) configured?

You'll only need to flush the cache if they're configured non shareable.

It is configured as non shareable.

York

#12 lore raw

PATCH v4 2/5 ARMv8: Adjust MMU setup

Quoted 45 lines · York Sun
Quoted 44 lines · Mark Rutland

[...]

Quoted 41 lines
Quoted 26 lines
Quoted 26 lines
Quoted 10 lines

What you need to do to replace the active set of tables (assuming that the new mapping has the instruction stream mapped in an identical way) is:

- Write the tables.

- DSB to make them visible to the MMU.

- Write to the appropriate TTBR_*.

Quoted 10 lines

- ISB to complete the write to the TTBR_*.

- TLBI to invalidate the old mappings the the TLBs.

- DSB to complete the TLBI.

- If you've changed the instruction stream or system state that will affect the instruction stream, ISB to flush the CPU pipeline.

Here is the flow I have (as of v5 patch)

Write the tables

(I removed dsb here in v5, need to add back)

Quoted 8 lines
Quoted 5 lines

Write TTBR

(I missed isb here, need to add)

Flush dcache (otherwise the table will not be in DDR. Yes, I verified)

This looks odd -- why do we need the tables to be in DDR? Why would we flush them here, when the address is partially visible to the MMU?

This sounds odd but it actually makes sense. Let's say we have new tables created by u-boot. The new tables are in the address of DDR with D-cache enabled. If we don't flush the cache, the moment TLBs are invalidated, MMU will fetch the tables from TTBR which points to DDR. Without a valid TLB, MMU cannot get the table from D-cache, it has to fetch from the DDR directly. I have verified this by checking waveforms of the SoC and exercised code in both ways.

Quoted 5 lines
Quoted 3 lines

How is TCR_EL2.SH0 (or TCR_EL1.SH*) configured?

You'll only need to flush the cache if they're configured non shareable.

It is configured as non shareable.

Is there any reason not to configure them as inner shareable? That way the MMU will look in the D-cache, and you won't have to spend time flushing them.

Cheers,
Mark.

author
#13 lore raw

PATCH v4 2/5 ARMv8: Adjust MMU setup

Quoted 48 lines · Mark Rutland
Quoted 47 lines · York Sun
Quoted 46 lines · Mark Rutland

[...]

Quoted 35 lines
Quoted 26 lines
Quoted 26 lines
Quoted 10 lines

What you need to do to replace the active set of tables (assuming that the new mapping has the instruction stream mapped in an identical way) is:

- Write the tables.

- DSB to make them visible to the MMU.

- Write to the appropriate TTBR_*.

Quoted 10 lines

- ISB to complete the write to the TTBR_*.

- TLBI to invalidate the old mappings the the TLBs.

- DSB to complete the TLBI.

- If you've changed the instruction stream or system state that will affect the instruction stream, ISB to flush the CPU pipeline.

Here is the flow I have (as of v5 patch)

Write the tables

(I removed dsb here in v5, need to add back)

Quoted 8 lines
Quoted 5 lines

Write TTBR

(I missed isb here, need to add)

Flush dcache (otherwise the table will not be in DDR. Yes, I verified)

This looks odd -- why do we need the tables to be in DDR? Why would we flush them here, when the address is partially visible to the MMU?

Quoted 6 lines

This sounds odd but it actually makes sense. Let's say we have new tables created by u-boot. The new tables are in the address of DDR with D-cache enabled. If we don't flush the cache, the moment TLBs are invalidated, MMU will fetch the tables from TTBR which points to DDR. Without a valid TLB, MMU cannot get the table from D-cache, it has to fetch from the DDR directly. I have verified this by checking waveforms of the SoC and exercised code in both ways.

How is TCR_EL2.SH0 (or TCR_EL1.SH*) configured?

Quoted 8 lines
Quoted 3 lines
Quoted 1 line

You'll only need to flush the cache if they're configured non shareable.

It is configured as non shareable.

Is there any reason not to configure them as inner shareable? That way the MMU will look in the D-cache, and you won't have to spend time flushing them.

You may be right. I need to test it. It will take me a little while. In the meantime, would you comment on v6 patches please? If flushing is the only question left, I can address it after testing.

York

author
#14 lore raw

PATCH v4 2/5 ARMv8: Adjust MMU setup

In reply to Mark Rutland
Quoted 10 lines · Mark Rutland
Quoted 5 lines
Quoted 3 lines

How is TCR_EL2.SH0 (or TCR_EL1.SH*) configured?

You'll only need to flush the cache if they're configured non shareable.

It is configured as non shareable.

Is there any reason not to configure them as inner shareable? That way the MMU will look in the D-cache, and you won't have to spend time flushing them.

Mark,

I appreciate the reminder. I tried on our emulator. With inner share set for TCR SH0 bits, u-boot works with the flushing, but doesn't work without flushing. It went to exception.

Can you share more information about the inner share? I need to follow up with our designer to confirm.

York

author
#15 lore raw

PATCH v4 2/5 ARMv8: Adjust MMU setup

Quoted 12 lines · York Sun
Quoted 10 lines · Mark Rutland
Quoted 5 lines
Quoted 3 lines

How is TCR_EL2.SH0 (or TCR_EL1.SH*) configured?

You'll only need to flush the cache if they're configured non shareable.

It is configured as non shareable.

Is there any reason not to configure them as inner shareable? That way the MMU will look in the D-cache, and you won't have to spend time flushing them.

Quoted 9 lines

Mark,

I appreciate the reminder. I tried on our emulator. With inner share set for TCR SH0 bits, u-boot works with the flushing, but doesn't work without flushing. It went to exception.

Can you share more information about the inner share? I need to follow up with our designer to confirm.

A second thought, do I need to set the first MMU table so DDR is inner shareable?

York

#16 lore raw

PATCH v4 2/5 ARMv8: Adjust MMU setup

Hi,

Apologies for the delay in replying.

Quoted 19 lines · York Sun
Quoted 18 lines · York Sun
Quoted 10 lines · Mark Rutland
Quoted 5 lines
Quoted 3 lines

How is TCR_EL2.SH0 (or TCR_EL1.SH*) configured?

You'll only need to flush the cache if they're configured non shareable.

It is configured as non shareable.

Is there any reason not to configure them as inner shareable? That way the MMU will look in the D-cache, and you won't have to spend time flushing them.

Mark,

I appreciate the reminder. I tried on our emulator. With inner share set for TCR SH0 bits, u-boot works with the flushing, but doesn't work without flushing. It went to exception.

Quoted 5 lines
Quoted 3 lines

Can you share more information about the inner share? I need to follow up with our designer to confirm.

A second thought, do I need to set the first MMU table so DDR is inner shareable?

I assume you mean configuring MAIR_ELx such that the mapping covering DDR is cacheable for the inner shareable domain? If so, yes.

Cheers,
Mark.

author
#17 lore raw

PATCH v4 2/5 ARMv8: Adjust MMU setup

Quoted 24 lines · Mark Rutland

Hi,

Apologies for the delay in replying.

Quoted 19 lines · York Sun
Quoted 18 lines · York Sun
Quoted 10 lines · Mark Rutland
Quoted 5 lines
Quoted 3 lines

How is TCR_EL2.SH0 (or TCR_EL1.SH*) configured?

You'll only need to flush the cache if they're configured non shareable.

It is configured as non shareable.

Is there any reason not to configure them as inner shareable? That way the MMU will look in the D-cache, and you won't have to spend time flushing them.

Mark,

I appreciate the reminder. I tried on our emulator. With inner share set for TCR SH0 bits, u-boot works with the flushing, but doesn't work without flushing. It went to exception.

Quoted 9 lines
Quoted 5 lines
Quoted 3 lines

Can you share more information about the inner share? I need to follow up with our designer to confirm.

A second thought, do I need to set the first MMU table so DDR is inner shareable?

I assume you mean configuring MAIR_ELx such that the mapping covering DDR is cacheable for the inner shareable domain? If so, yes.

Mark,

I will continue to experiment this idea. In the meantime, please comment on my v6 patch set.

York

author
#18 lore raw

PATCH v4 2/5 ARMv8: Adjust MMU setup

In reply to Mark Rutland
Quoted 24 lines · Mark Rutland

Hi,

Apologies for the delay in replying.

Quoted 19 lines · York Sun
Quoted 18 lines · York Sun
Quoted 10 lines · Mark Rutland
Quoted 5 lines
Quoted 3 lines

How is TCR_EL2.SH0 (or TCR_EL1.SH*) configured?

You'll only need to flush the cache if they're configured non shareable.

It is configured as non shareable.

Is there any reason not to configure them as inner shareable? That way the MMU will look in the D-cache, and you won't have to spend time flushing them.

Mark,

I appreciate the reminder. I tried on our emulator. With inner share set for TCR SH0 bits, u-boot works with the flushing, but doesn't work without flushing. It went to exception.

Quoted 9 lines
Quoted 5 lines
Quoted 3 lines

Can you share more information about the inner share? I need to follow up with our designer to confirm.

A second thought, do I need to set the first MMU table so DDR is inner shareable?

I assume you mean configuring MAIR_ELx such that the mapping covering DDR is cacheable for the inner shareable domain? If so, yes.

Mark,

I tried both inner share and outer. It doesn't work without flushing the cache. I will keep this part of code until I learn otherwise.

York

#19 lore raw

PATCH v4 2/5 ARMv8: Adjust MMU setup

hi York,

Quoted 35 lines · Mark Rutland
Quoted 24 lines

Hi,

Apologies for the delay in replying.

Quoted 19 lines · York Sun
Quoted 18 lines · York Sun
Quoted 10 lines · Mark Rutland
Quoted 5 lines
Quoted 3 lines

How is TCR_EL2.SH0 (or TCR_EL1.SH*) configured?

You'll only need to flush the cache if they're configured non shareable.

It is configured as non shareable.

Is there any reason not to configure them as inner shareable? That way the MMU will look in the D-cache, and you won't have to spend time flushing them.

Mark,

I appreciate the reminder. I tried on our emulator. With inner share set for TCR SH0 bits, u-boot works with the flushing, but doesn't work without flushing. It went to exception.

Quoted 9 lines
Quoted 5 lines
Quoted 3 lines

Can you share more information about the inner share? I need to follow up with our designer to confirm.

A second thought, do I need to set the first MMU table so DDR is inner shareable?

I assume you mean configuring MAIR_ELx such that the mapping covering DDR is cacheable for the inner shareable domain? If so, yes.

Quoted 7 lines

Mark,

I tried both inner share and outer. It doesn't work without flushing the cache. I will keep this part of code until I learn otherwise.

York

The shareability attribute is different with cacheablilty attribute, it means whether the memory region would be accessed by other processors. If a memory region is configured as non sharable the access will not be snooped but still can be cached. It is the situation that current u-boot-armv8 used due to only master processor is active. If secondary processors also access the same memory region it should be configured as inner-sharable or outer-sharable otherwise the cache coherence between processors will not be maintained.
The above is what I know. Wish this could help.

David

author
#20 lore raw

PATCH v4 2/5 ARMv8: Adjust MMU setup

Quoted 45 lines · fenghua at phytium.com.cn

hi York,

Quoted 35 lines · Mark Rutland
Quoted 24 lines

Hi,

Apologies for the delay in replying.

Quoted 19 lines · York Sun
Quoted 18 lines · York Sun
Quoted 10 lines · Mark Rutland
Quoted 5 lines
Quoted 3 lines

How is TCR_EL2.SH0 (or TCR_EL1.SH*) configured?

You'll only need to flush the cache if they're configured non shareable.

It is configured as non shareable.

Is there any reason not to configure them as inner shareable? That way the MMU will look in the D-cache, and you won't have to spend time flushing them.

Mark,

I appreciate the reminder. I tried on our emulator. With inner share set for TCR SH0 bits, u-boot works with the flushing, but doesn't work without flushing. It went to exception.

Quoted 9 lines
Quoted 5 lines
Quoted 3 lines

Can you share more information about the inner share? I need to follow up with our designer to confirm.

A second thought, do I need to set the first MMU table so DDR is inner shareable?

I assume you mean configuring MAIR_ELx such that the mapping covering DDR is cacheable for the inner shareable domain? If so, yes.

Quoted 7 lines

Mark,

I tried both inner share and outer. It doesn't work without flushing the cache. I will keep this part of code until I learn otherwise.

York

Quoted 8 lines

The shareability attribute is different with cacheablilty attribute, it means whether the memory region would be accessed by other processors. If a memory region is configured as non sharable the access will not be snooped but still can be cached. It is the situation that current u-boot-armv8 used due to only master processor is active. If secondary processors also access the same memory region it should be configured as inner-sharable or outer-sharable otherwise the cache coherence between processors will not be maintained.
The above is what I know. Wish this could help.

David,

Thanks for chime in. It is not clear to me if I can put a new MMU table in d-cache and have MMU fetch from the d-cache. I tried several ways but it doesn't work.

York

#21 lore raw

PATCH v4 2/5 ARMv8: Adjust MMU setup

In reply to York Sun
Quoted 3 lines · York Sun
Quoted 2 lines · Mark Rutland
Quoted 1 line · York Sun

On 06/05/2014 03:09 AM, Mark Rutland wrote:

[...]

Quoted 7 lines
Quoted 7 lines
Quoted 4 lines

No objection here on the idea. But again this is not the case. My first MMU table is in SRAM, which is small and will be used for other purpose. The 2nd MMU table is in DDR. I could copy the table and do the maintenance as you said. For now, I want to stick with the static table and only create the API when I have to.

Sure, if your tables are in SRAM then trying to do a load of dynamic allocation isn't going to work.

Why do you need to turn on the MMU early using SRAM in the first place? Can't you delay that until after DDR setup?

Quoted 9 lines
Quoted 4 lines

My fear is that while that sounds OK with a single user to do a quick havk and poke the tables directly, we'll end up with everyone doing that, and no-one will try to unify things. It is very diffifcult to unify such variation after the fact.

That's a good reason. Let me start to code the API. It will take a while to cover the complexity of the multilevel tables and sizes. It will be a separated patch for later review. I don't want that to delay this patch set. I am hoping to get this set in for 2014.07 release.

If I was maintainer I would say no because few people come back later to clean-up their mess. If you want to get platform support in now, perhaps you should just add the base platform first and add mmu setup later. Surely you don't need the MMU to just boot to u-boot shell.

Rob

author
#22 lore raw

PATCH v4 2/5 ARMv8: Adjust MMU setup

Quoted 12 lines · Rob Herring
Quoted 3 lines · York Sun
Quoted 2 lines · Mark Rutland
Quoted 1 line · York Sun

On 06/05/2014 03:09 AM, Mark Rutland wrote:

[...]

Quoted 5 lines
Quoted 5 lines
Quoted 4 lines

No objection here on the idea. But again this is not the case. My first MMU table is in SRAM, which is small and will be used for other purpose. The 2nd MMU table is in DDR. I could copy the table and do the maintenance as you said. For now, I want to stick with the static table and only create the API when I have to.

Quoted 5 lines
Quoted 2 lines
Quoted 2 lines

Sure, if your tables are in SRAM then trying to do a load of dynamic allocation isn't going to work.

Why do you need to turn on the MMU early using SRAM in the first place? Can't you delay that until after DDR setup?

Logically yes. But it runs too slow without cache on emulator.

Quoted 11 lines
Quoted 10 lines
Quoted 5 lines

My fear is that while that sounds OK with a single user to do a quick havk and poke the tables directly, we'll end up with everyone doing that, and no-one will try to unify things. It is very diffifcult to unify such variation after the fact.

That's a good reason. Let me start to code the API. It will take a while to cover the complexity of the multilevel tables and sizes. It will be a separated patch for later review. I don't want that to delay this patch set. I am hoping to get this set in for 2014.07 release.

Quoted 5 lines

If I was maintainer I would say no because few people come back later to clean-up their mess. If you want to get platform support in now, perhaps you should just add the base platform first and add mmu setup later. Surely you don't need the MMU to just boot to u-boot shell.

My plan is to get the first platform in, then I will maintain Freescale stuff. So you can be sure I will continue to improve it. One reason to get these code in early is to enable our partners and early adopters to use u-boot. After all, this is the second ARMv8 platform. The first one vexpress_aemv8a doesn't even support cache.

York

#23 lore raw

PATCH v4 2/5 ARMv8: Adjust MMU setup

Quoted 38 lines · York Sun
Quoted 12 lines · Rob Herring
Quoted 3 lines · York Sun
Quoted 2 lines · Mark Rutland
Quoted 1 line · York Sun

On 06/05/2014 03:09 AM, Mark Rutland wrote:

[...]

Quoted 5 lines
Quoted 5 lines
Quoted 4 lines

No objection here on the idea. But again this is not the case. My first MMU table is in SRAM, which is small and will be used for other purpose. The 2nd MMU table is in DDR. I could copy the table and do the maintenance as you said. For now, I want to stick with the static table and only create the API when I have to.

Quoted 5 lines
Quoted 2 lines
Quoted 2 lines

Sure, if your tables are in SRAM then trying to do a load of dynamic allocation isn't going to work.

Why do you need to turn on the MMU early using SRAM in the first place? Can't you delay that until after DDR setup?

Logically yes. But it runs too slow without cache on emulator.

Quoted 11 lines
Quoted 10 lines
Quoted 5 lines

My fear is that while that sounds OK with a single user to do a quick havk and poke the tables directly, we'll end up with everyone doing that, and no-one will try to unify things. It is very diffifcult to unify such variation after the fact.

That's a good reason. Let me start to code the API. It will take a while to cover the complexity of the multilevel tables and sizes. It will be a separated patch for later review. I don't want that to delay this patch set. I am hoping to get this set in for 2014.07 release.

Quoted 5 lines

If I was maintainer I would say no because few people come back later to clean-up their mess. If you want to get platform support in now, perhaps you should just add the base platform first and add mmu setup later. Surely you don't need the MMU to just boot to u-boot shell.

Quoted 5 lines

My plan is to get the first platform in, then I will maintain Freescale stuff. So you can be sure I will continue to improve it. One reason to get these code in early is to enable our partners and early adopters to use u-boot. After all, this is the second ARMv8 platform. The first one vexpress_aemv8a doesn't even support cache.

For the record, I'm OK with this plan given York's track record in the U-Boot community.

Signature · 8 lines
Tom
-------------- next part --------------
A non-text attachment was scrubbed...
Name: not available
Type: application/pgp-signature
Size: 836 bytes
Desc: Digital signature
URL: <http://lists.denx.de/pipermail/u-boot/attachments/20140606/c368254a/attachment.pgp>
author
#24 lore raw

PATCH v4 3/5 ARMv8/FSL_LSCH3: Add FSL_LSCH3 SoC

In reply to York Sun

Freescale LayerScape with Chassis Generation 3 is a set of SoCs with ARMv8 cores and 3rd generation of Chassis. We use different MMU setup to support memory map and cache attribute for these SoCs. MMU and cache are enabled very early to bootst performance, especially for early development on emulators. After u-boot relocates to DDR, a new MMU table with QBMan cache access is created in DDR. SMMU pagesize is set in SMMU_sACR register. Both DDR3 and DDR4 are supported.

---
Change log:
v4: Slit ARMv8 MMU change to another patch, v4 2/5
Add more comment for final MMU table setup Remove mmu.h added in v3 and v2
Remove GICv2 macro testing in lowlevel.S Rename function init_type() to initiator_type() Use ARMv8 generic timer code

v3: Remove blank lines at the of files
Fix cluster PLL GSR register for accessing beyond array size Update final MMU table to support QBMan memory with cache Set SMMU pagesize in SMMU_sACR register in lowlevel init. Add DDR4 support
Remove forcing L3 cache flusing
Update GICv3 redistributor base address
Some of these changes are caused by model change.

 arch/arm/cpu/armv8/cache_v8.c                     |    7 +-
 arch/arm/cpu/armv8/fsl-lsch3/Makefile             |    9 +
 arch/arm/cpu/armv8/fsl-lsch3/README               |   10 +
 arch/arm/cpu/armv8/fsl-lsch3/cpu.c                |  417 +++++++++++++++++++++
 arch/arm/cpu/armv8/fsl-lsch3/cpu.h                |    7 +
 arch/arm/cpu/armv8/fsl-lsch3/lowlevel.S           |   56 +++
 arch/arm/cpu/armv8/fsl-lsch3/speed.c              |  176 +++++++++
 arch/arm/cpu/armv8/fsl-lsch3/speed.h              |    7 +
 arch/arm/include/asm/arch-fsl-lsch3/clock.h       |   23 ++
 arch/arm/include/asm/arch-fsl-lsch3/config.h      |   65 ++++
 arch/arm/include/asm/arch-fsl-lsch3/gpio.h        |    9 +
 arch/arm/include/asm/arch-fsl-lsch3/immap_lsch3.h |  116 ++++++
 arch/arm/include/asm/arch-fsl-lsch3/imx-regs.h    |   13 +
 arch/arm/include/asm/config.h                     |    4 +
 arch/arm/include/asm/system.h                     |    2 +
 drivers/i2c/mxc_i2c.c                             |    5 +
 include/common.h                                  |    5 +-
 17 files changed, 927 insertions(+), 4 deletions(-)
 create mode 100644 arch/arm/cpu/armv8/fsl-lsch3/Makefile
 create mode 100644 arch/arm/cpu/armv8/fsl-lsch3/README
 create mode 100644 arch/arm/cpu/armv8/fsl-lsch3/cpu.c
 create mode 100644 arch/arm/cpu/armv8/fsl-lsch3/cpu.h
 create mode 100644 arch/arm/cpu/armv8/fsl-lsch3/lowlevel.S
 create mode 100644 arch/arm/cpu/armv8/fsl-lsch3/speed.c
 create mode 100644 arch/arm/cpu/armv8/fsl-lsch3/speed.h
 create mode 100644 arch/arm/include/asm/arch-fsl-lsch3/clock.h
 create mode 100644 arch/arm/include/asm/arch-fsl-lsch3/config.h
 create mode 100644 arch/arm/include/asm/arch-fsl-lsch3/gpio.h
 create mode 100644 arch/arm/include/asm/arch-fsl-lsch3/immap_lsch3.h
 create mode 100644 arch/arm/include/asm/arch-fsl-lsch3/imx-regs.h
arch/arm/cpu/armv8/cache_v8.c +6 -1
index 67dbd46..104cda5 100644
@@ -74,12 +74,17 @@ void invalidate_dcache_all(void)
 	__asm_invalidate_dcache_all();
 }
 
+void __weak flush_l3_cache(void)
+{
+}
+
 /*
  * Performs a clean & invalidation of the entire data cache at all levels
  */
 void flush_dcache_all(void)
 {
 	__asm_flush_dcache_all();
+	flush_l3_cache();
 }
 
 /*
@@ -212,7 +217,7 @@ void invalidate_icache_all(void)
  * Enable dCache & iCache, whether cache is actually enabled
  * depend on CONFIG_SYS_DCACHE_OFF and CONFIG_SYS_ICACHE_OFF
  */
-void enable_caches(void)
+void __weak enable_caches(void)
 {
 	icache_enable();
 	dcache_enable();
arch/arm/cpu/armv8/fsl-lsch3/Makefile new file +9 -0
new file mode 100644
index 0000000..9249537
@@ -0,0 +1,9 @@
+#
+# Copyright 2014, Freescale Semiconductor
+#
+# SPDX-License-Identifier:	GPL-2.0+
+#
+
+obj-y += cpu.o
+obj-y += lowlevel.o
+obj-y += speed.o
arch/arm/cpu/armv8/fsl-lsch3/README new file +10 -0
new file mode 100644
index 0000000..de34a91
@@ -0,0 +1,10 @@
+#
+# Copyright 2014 Freescale Semiconductor
+#
+# SPDX-License-Identifier:      GPL-2.0+
+#
+
+Freescale LayerScape with Chassis Generation 3
+
+This architecture supports Freescale ARMv8 SoCs with Chassis generation 3,
+for example LS2100A.
arch/arm/cpu/armv8/fsl-lsch3/cpu.c new file +417 -0

417 lines not on this page · raw message

arch/arm/cpu/armv8/fsl-lsch3/cpu.h new file +7 -0
new file mode 100644
index 0000000..28544d7
@@ -0,0 +1,7 @@
+/*
+ * Copyright 2014, Freescale Semiconductor
+ *
+ * SPDX-License-Identifier:	GPL-2.0+
+ */
+
+int fsl_qoriq_core_to_cluster(unsigned int core);
arch/arm/cpu/armv8/fsl-lsch3/lowlevel.S new file +56 -0

56 lines not on this page · raw message

arch/arm/cpu/armv8/fsl-lsch3/speed.c new file +176 -0

176 lines not on this page · raw message

arch/arm/cpu/armv8/fsl-lsch3/speed.h new file +7 -0
new file mode 100644
index 0000000..15af5b9
@@ -0,0 +1,7 @@
+/*
+ * Copyright 2014, Freescale Semiconductor, Inc.
+ *
+ * SPDX-License-Identifier:	GPL-2.0+
+ */
+
+void get_sys_info(struct sys_info *sys_info);
arch/arm/include/asm/arch-fsl-lsch3/clock.h new file +23 -0
new file mode 100644
index 0000000..831af0b
@@ -0,0 +1,23 @@
+/*
+ * Copyright 2014 Freescale Semiconductor, Inc.
+ *
+ * SPDX-License-Identifier:	GPL-2.0+
+ *
+ */
+
+#ifndef __ASM_ARCH_FSL_LSCH3_CLOCK_H_
+#define __ASM_ARCH_FSL_LSCH3_CLOCK_H_
+
+#include <common.h>
+
+enum mxc_clock {
+	MXC_ARM_CLK = 0,
+	MXC_BUS_CLK,
+	MXC_UART_CLK,
+	MXC_ESDHC_CLK,
+	MXC_I2C_CLK,
+};
+
+unsigned int mxc_get_clock(enum mxc_clock clk);
+
+#endif /* __ASM_ARCH_FSL_LSCH3_CLOCK_H_ */
arch/arm/include/asm/arch-fsl-lsch3/config.h new file +65 -0

65 lines not on this page · raw message

arch/arm/include/asm/arch-fsl-lsch3/gpio.h new file +9 -0
new file mode 100644
index 0000000..f23a78c
@@ -0,0 +1,9 @@
+/*
+ * Copyright 2014, Freescale Semiconductor
+ *
+ * SPDX-License-Identifier:	GPL-2.0+
+ */
+
+#ifndef _ASM_ARMV8_FSL_LSCH3_GPIO_H_
+#define _ASM_ARMV8_FSL_LSCH3_GPIO_H_
+#endif	/* _ASM_ARMV8_FSL_LSCH3_GPIO_H_ */
arch/arm/include/asm/arch-fsl-lsch3/immap_lsch3.h new file +116 -0

116 lines not on this page · raw message

arch/arm/include/asm/arch-fsl-lsch3/imx-regs.h new file +13 -0
new file mode 100644
index 0000000..8f00535
@@ -0,0 +1,13 @@
+/*
+ * Copyright 2014 Freescale Semiconductor, Inc.
+ *
+ * SPDX-License-Identifier:	GPL-2.0+
+ *
+ */
+
+#ifndef __ASM_ARCH_FSL_LSCH3_IMX_REGS_H_
+#define __ASM_ARCH_FSL_LSCH3_IMX_REGS_H_
+
+#define I2C_QUIRK_REG	/* enable 8-bit driver */
+
+#endif /* __ASM_ARCH_FSL_LSCH3_IMX_REGS_H_ */
arch/arm/include/asm/config.h +4 -0
index 2a20a77..d3433da 100644
@@ -17,4 +17,8 @@
 #define CONFIG_STATIC_RELA
 #endif
 
+#ifdef CONFIG_FSL_LSCH3
+#include <asm/arch-fsl-lsch3/config.h>
+#endif
+
 #endif
arch/arm/include/asm/system.h +2 -0
index 74ee9a4..d51ba66 100644
@@ -78,6 +78,8 @@ void gic_send_sgi(unsigned long sgino);
 void wait_for_wakeup(void);
 void smp_kick_all_cpus(void);
 
+void flush_l3_cache(void);
+
 #endif	/* __ASSEMBLY__ */
 
 #else /* CONFIG_ARM64 */
drivers/i2c/mxc_i2c.c +5 -0
index 48468d7..c14797c 100644
@@ -429,6 +429,11 @@ static void * const i2c_bases[] = {
 	(void *)I2C3_BASE_ADDR
 #elif defined(CONFIG_VF610)
 	(void *)I2C0_BASE_ADDR
+#elif defined(CONFIG_FSL_LSCH3)
+	(void *)I2C1_BASE_ADDR,
+	(void *)I2C2_BASE_ADDR,
+	(void *)I2C3_BASE_ADDR,
+	(void *)I2C4_BASE_ADDR
 #else
 #error "architecture not supported"
 #endif
include/common.h +2 -3
index 232136c..a42d6d2 100644
@@ -693,9 +693,6 @@ ulong get_PERCLK3(void);
 ulong	get_bus_freq  (ulong);
 int get_serial_clock(void);
 
-#if defined(CONFIG_MPC83xx) || defined(CONFIG_MPC85xx)
-ulong get_ddr_freq(ulong);
-#endif
 #if defined(CONFIG_MPC85xx)
 typedef MPC85xx_SYS_INFO sys_info_t;
 void	get_sys_info  ( sys_info_t * );
@@ -711,6 +708,8 @@ static inline ulong get_ddr_freq(ulong dummy)
 {
 	return get_bus_freq(dummy);
 }
+#else
+ulong get_ddr_freq(ulong);
 #endif
 
 #if defined(CONFIG_4xx)
Signature · 1 line
1.7.9.5
#25 lore raw

PATCH v4 3/5 ARMv8/FSL_LSCH3: Add FSL_LSCH3 SoC

Dear York,
My mailing list disabled a few days. Maybe I missed something important.

Quoted 9 lines

/*
* Performs a clean & invalidation of the entire data cache at all levels */
void flush_dcache_all(void)

{
	__asm_flush_dcache_all();
arch/arm/cpu/armv8/cache_v8.c +1 -0
+	flush_l3_cache();

}

I thought the L3 cache is not included in the cache hierarchy.
So, how about define it as external cache operations named with outer_cache_* just like armv7 did?

Best regards,
David

author
#26 lore raw

PATCH v4 3/5 ARMv8/FSL_LSCH3: Add FSL_LSCH3 SoC

Quoted 4 lines · fenghua at phytium.com.cn

Dear York,
My mailing list disabled a few days. Maybe I missed something important.

Quoted 13 lines
Quoted 9 lines

/*
* Performs a clean & invalidation of the entire data cache at all levels */
void flush_dcache_all(void)

{
	__asm_flush_dcache_all();
arch/arm/cpu/armv8/cache_v8.c +1 -0
+	flush_l3_cache();

}

I thought the L3 cache is not included in the cache hierarchy.
So, how about define it as external cache operations named with outer_cache_* just like armv7 did?

I have it declared as a weak function. If you think the name needs to change, I can do so.

York

author
#27 lore raw

PATCH v4 4/5 armv8/fsl-lsch3: Add support to load and start MC Firmware

In reply to York Sun

From: "J. German Rivera" <[email protected]>

Adding supoort to load and start the Layerscape Management Complex (MC) firmware. First, the MC GCR register is set to 0 to reset all cores. MC firmware and DPL images are copied from their location in NOR flash to DDR. MC registers are updated with the location of these images. Deasserting the reset bit of MC GCR register releases core 0 to run. Core 1 will be released by MC firmware. Stop bits are not touched for this step. U-boot waits for MC until it boots up. In case of a failure, device tree is updated accordingly. The MC firmware image uses FIT format.

---
Change log
v4: no change
v3: Add error detection and update device tree if failure
Revise loading address to avoid overlap
Use FIT image for the firmware
Remove blank lines at the end of files

 README                             |   27 ++++
 arch/arm/cpu/armv8/fsl-lsch3/cpu.c |   11 ++
 drivers/net/Makefile               |    1 +
 drivers/net/fsl_mc/Makefile        |    8 ++
 drivers/net/fsl_mc/mc.c            |  274 ++++++++++++++++++++++++++++++++++++
 include/fdt_support.h              |   14 +-
 include/fsl_mc.h                   |   59 ++++++++
 7 files changed, 391 insertions(+), 3 deletions(-)
 create mode 100644 drivers/net/fsl_mc/Makefile
 create mode 100644 drivers/net/fsl_mc/mc.c
 create mode 100644 include/fsl_mc.h
README +27 -0
index a280435..95a0282 100644
@@ -4640,6 +4640,33 @@ within that device.
 	window->master inbound window->master LAW->the ucode address in
 	master's memory space.
 
+Freescale Layerscape Management Complex Firmware Support:
+---------------------------------------------------------
+The Freescale Layerscape Management Complex (MC) supports the loading of
+"firmware".
+This firmware often needs to be loaded during U-Boot booting, so macros
+are used to identify the storage device (NOR flash, SPI, etc) and the address
+within that device.
+
+- CONFIG_FSL_MC_ENET
+	Enable the MC driver for Layerscape SoCs.
+
+- CONFIG_SYS_LS_MC_FW_ADDR
+	The address in the storage device where the firmware is located.  The
+	meaning of this address depends on which CONFIG_SYS_LS_MC_FW_IN_xxx macro
+	is also specified.
+
+- CONFIG_SYS_LS_MC_FW_LENGTH
+	The maximum possible size of the firmware.  The firmware binary format
+	has a field that specifies the actual size of the firmware, but it
+	might not be possible to read any part of the firmware unless some
+	local storage is allocated to hold the entire firmware first.
+
+- CONFIG_SYS_LS_MC_FW_IN_NOR
+	Specifies that MC firmware is located in NOR flash, mapped as
+	normal addressable memory via the LBC. CONFIG_SYS_LS_MC_FW_ADDR is the
+	virtual address in NOR flash.
+
 Building the Software:
 ======================
 
arch/arm/cpu/armv8/fsl-lsch3/cpu.c +11 -0
index f0f62b5..f334ebe 100644
@@ -12,6 +12,7 @@
 #include <asm/arch-fsl-lsch3/immap_lsch3.h>
 #include "cpu.h"
 #include "speed.h"
+#include <fsl_mc.h>
 
 DECLARE_GLOBAL_DATA_PTR;
 
@@ -415,3 +416,13 @@ int print_cpuinfo(void)
 	return 0;
 }
 #endif
+
+int cpu_eth_init(bd_t *bis)
+{
+	int error = 0;
+
+#ifdef CONFIG_FSL_MC_ENET
+	error = mc_init(bis);
+#endif
+	return error;
+}
drivers/net/Makefile +1 -0
index 6005f7e..6226cb2 100644
@@ -64,3 +64,4 @@ obj-$(CONFIG_XILINX_EMACLITE) += xilinx_emaclite.o
 obj-$(CONFIG_XILINX_LL_TEMAC) += xilinx_ll_temac.o xilinx_ll_temac_mdio.o \
 		xilinx_ll_temac_fifo.o xilinx_ll_temac_sdma.o
 obj-$(CONFIG_ZYNQ_GEM) += zynq_gem.o
+obj-$(CONFIG_FSL_MC_ENET) += fsl_mc/
drivers/net/fsl_mc/Makefile new file +8 -0
new file mode 100644
index 0000000..4834086
@@ -0,0 +1,8 @@
+#
+# Copyright 2014 Freescale Semiconductor, Inc.
+#
+# SPDX-License-Identifier:	GPL-2.0+
+#
+
+# Layerscape MC driver
+obj-y += mc.o
drivers/net/fsl_mc/mc.c new file +274 -0

274 lines not on this page · raw message

include/fdt_support.h +11 -3
index ae010bb..f928306 100644
@@ -113,17 +113,25 @@ static inline int fdt_status_disabled(void *fdt, int nodeoffset)
 {
 	return fdt_set_node_status(fdt, nodeoffset, FDT_STATUS_DISABLED, 0);
 }
+static inline int fdt_status_fail(void *fdt, int nodeoffset)
+{
+	return fdt_set_node_status(fdt, nodeoffset, FDT_STATUS_FAIL, 0);
+}
 
-int fdt_set_status_by_alias(void *fdt, const char* alias,
+int fdt_set_status_by_alias(void *fdt, const char *alias,
 			    enum fdt_status status, unsigned int error_code);
-static inline int fdt_status_okay_by_alias(void *fdt, const char* alias)
+static inline int fdt_status_okay_by_alias(void *fdt, const char *alias)
 {
 	return fdt_set_status_by_alias(fdt, alias, FDT_STATUS_OKAY, 0);
 }
-static inline int fdt_status_disabled_by_alias(void *fdt, const char* alias)
+static inline int fdt_status_disabled_by_alias(void *fdt, const char *alias)
 {
 	return fdt_set_status_by_alias(fdt, alias, FDT_STATUS_DISABLED, 0);
 }
+static inline int fdt_status_fail_by_alias(void *fdt, const char *alias)
+{
+	return fdt_set_status_by_alias(fdt, alias, FDT_STATUS_FAIL, 0);
+}
 
 #endif /* ifdef CONFIG_OF_LIBFDT */
 
include/fsl_mc.h new file +59 -0

59 lines not on this page · raw message

Signature · 1 line
1.7.9.5
author
#28 lore raw

PATCH v4 5/5 ARMv8/ls2100a_emu: Add LS2100A emulator and simulator board support

In reply to York Sun

LS2100A is an ARMv8 implementation. This adds board support for emulator and simulator:
Two DDR controllers
UART2 is used as the console
IFC timing is tightened for speedy booting
Support DDR3 and DDR4 as separated targets
Management Complex (MC) is enabled

---
Change log
v4: no change
v3: Add support for DDR4 target and simulator target
Squash Manage complex patch (previous 5/5) into this
Reserve last 512MB memory for MC use
Change MC firmware location in NOR flash
Fix UART clock source speed
Update IFC address mux
Use generic board
Disable DDR memory beyound 39 physical address space due to Linux limitation

 board/freescale/ls2100a/Makefile  |    8 ++
 board/freescale/ls2100a/README    |   16 +++
 board/freescale/ls2100a/ddr.c     |  175 ++++++++++++++++++++++++++++
 board/freescale/ls2100a/ddr.h     |   57 ++++++++++
 board/freescale/ls2100a/ls2100a.c |  100 ++++++++++++++++
 boards.cfg                        |    3 +
 include/configs/ls2100a_common.h  |  226 +++++++++++++++++++++++++++++++++++++
 include/configs/ls2100a_emu.h     |   19 ++++
 include/configs/ls2100a_simu.h    |   16 +++
 9 files changed, 620 insertions(+)
 create mode 100644 board/freescale/ls2100a/Makefile
 create mode 100644 board/freescale/ls2100a/README
 create mode 100644 board/freescale/ls2100a/ddr.c
 create mode 100644 board/freescale/ls2100a/ddr.h
 create mode 100644 board/freescale/ls2100a/ls2100a.c
 create mode 100644 include/configs/ls2100a_common.h
 create mode 100644 include/configs/ls2100a_emu.h
 create mode 100644 include/configs/ls2100a_simu.h
board/freescale/ls2100a/Makefile new file +8 -0
new file mode 100644
index 0000000..c8da338
@@ -0,0 +1,8 @@
+#
+# Copyright 2014 Freescale Semiconductor
+#
+# SPDX-License-Identifier:	GPL-2.0+
+#
+
+obj-y += ls2100a.o
+obj-y += ddr.o
board/freescale/ls2100a/README new file +16 -0
new file mode 100644
index 0000000..9a8a618
@@ -0,0 +1,16 @@
+Freescale ls2100a_emu
+
+This is a emulator target with limited peripherals.
+
+Memory map from core's view
+
+0x00_0000_0000 .. 0x00_000F_FFFF	Boot Rom
+0x00_0100_0000 .. 0x00_0FFF_FFFF	CCSR
+0x00_1800_0000 .. 0x00_181F_FFFF	OCRAM
+0x00_3000_0000 .. 0x00_3FFF_FFFF	IFC region #1
+0x00_8000_0000 .. 0x00_FFFF_FFFF	DDR region #1
+0x05_1000_0000 .. 0x05_FFFF_FFFF	IFC region #2
+0x80_8000_0000 .. 0xFF_FFFF_FFFF	DDR region #2
+
+Other addresses are either reserved, or not used directly by u-boot.
+This list should be updated when more addresses are used.
board/freescale/ls2100a/ddr.c new file +175 -0

175 lines not on this page · raw message

board/freescale/ls2100a/ddr.h new file +57 -0

57 lines not on this page · raw message

board/freescale/ls2100a/ls2100a.c new file +100 -0

100 lines not on this page · raw message

boards.cfg +3 -0
index 8b1177c..6670a4c 100644
@@ -44,6 +44,9 @@
 ###########################################################################################################
 
 Active  aarch64     armv8          -           armltd          vexpress64          vexpress_aemv8a                       vexpress_aemv8a:ARM64                                                                                                             David Feng <[email protected]>
+Active  aarch64     armv8          fsl-lsch3   freescale       ls2100a             ls2100a_emu                          ls2100a_emu:ARM64,EMU                                                                                                             York Sun <[email protected]>
+Active  aarch64     armv8          fsl-lsch3   freescale       ls2100a             ls2100a_emu_D4                       ls2100a_emu:ARM64,EMU,SYS_FSL_DDR4                                                                                                York Sun <[email protected]>
+Active  aarch64     armv8          fsl-lsch3   freescale       ls2100a             ls2100a_simu                         ls2100a_simu:ARM64,SIMU                                                                                                           York Sun <[email protected]>
 Active  arc         arc700         -           synopsys        -                   axs101                                -                                                                                                                                 Alexey Brodkin <[email protected]>
 Active  arc         arc700         -           synopsys        <none>              arcangel4                             -                                                                                                                                 Alexey Brodkin <[email protected]>
 Active  arc         arc700         -           synopsys        <none>              arcangel4-be                          -                                                                                                                                 Alexey Brodkin <[email protected]>
include/configs/ls2100a_common.h new file +226 -0

226 lines not on this page · raw message

include/configs/ls2100a_emu.h new file +19 -0
new file mode 100644
index 0000000..3bcb5a0
@@ -0,0 +1,19 @@
+/*
+ * Copyright 2014 Freescale Semiconductor
+ *
+ * SPDX-License-Identifier:	GPL-2.0+
+ */
+
+#ifndef __LS2_EMU_H
+#define __LS2_EMU_H
+
+#include "ls2100a_common.h"
+
+#define CONFIG_DDR_SPD
+#define CONFIG_SYS_FSL_DDR_EMU		/* Support emulator */
+#define SPD_EEPROM_ADDRESS1	0x51
+#define SPD_EEPROM_ADDRESS2	0x52
+#define SPD_EEPROM_ADDRESS	SPD_EEPROM_ADDRESS1
+#define CONFIG_SYS_SPD_BUS_NUM	1	/* SPD on I2C bus 1 */
+
+#endif /* __LS2_EMU_H */
include/configs/ls2100a_simu.h new file +16 -0
new file mode 100644
index 0000000..53e1982
@@ -0,0 +1,16 @@
+/*
+ * Copyright (C) 2014 Freescale Semiconductor
+ *
+ * SPDX-License-Identifier:	GPL-2.0+
+ */
+
+#ifndef __LS2_SIMU_H
+#define __LS2_SIMU_H
+
+#include "ls2100a_common.h"
+
+/* SMSC 91C111 ethernet configuration */
+#define CONFIG_SMC91111
+#define CONFIG_SMC91111_BASE	(0x2210000)
+
+#endif /* __LS2_SIMU_H */
Signature · 1 line
1.7.9.5