From mboxrd@z Thu Jan 1 00:00:00 1970 From: Kuo-Jung Su Date: Thu, 4 Jul 2013 11:40:37 +0800 Subject: [U-Boot] [PATCH v6 05/12] arm: add MMU/D-Cache support for Faraday cores In-Reply-To: <1372909244-25211-1-git-send-email-dantesu@gmail.com> References: <1372909244-25211-1-git-send-email-dantesu@gmail.com> Message-ID: <1372909244-25211-6-git-send-email-dantesu@gmail.com> List-Id: MIME-Version: 1.0 Content-Type: text/plain; charset="us-ascii" Content-Transfer-Encoding: 7bit To: u-boot@lists.denx.de From: Kuo-Jung Su This updates the map_physmem()/unmap_physmem(), and use them to implement dma_alloc_coherent() & dma_free_coherent(). It uses 1MB section for each mapping, and thus wastes lots of address space, however this should still be good enough for tiny systems (i.e. u-boot). Signed-off-by: Kuo-Jung Su CC: Albert Aribaud --- Changes for v6: - Nothing updates Changes for v5: - Add void dram_bank_mmu_setup() into 'arch/arm/cpu/faraday/cpu.c' to override the weak function in "cache-cp15.c". - Use small page (4KB) to map relocated exception table to 0x0000 Changes for v4: - Coding Style cleanup. Changes for v3: - Coding Style cleanup. - Always insert a blank line between declarations and code. - dma-mapping.h: Have the global data ptr declared outside functions. - dma-mapping.h: Add #if...#else...#endif to dma_free_coherent(). - Drop static non-cached region, now we use map_physmem()/unmap_physmem() for dynamic mappings. Changes for v2: - Coding Style cleanup. - cache-cp15: Enable write buffer in write-through mode. arch/arm/include/asm/dma-mapping.h | 59 ++++++++++++- arch/arm/include/asm/global_data.h | 4 + arch/arm/include/asm/io.h | 160 ++++++++++++++++++++++++++++++++++-- arch/arm/include/asm/system.h | 7 +- arch/arm/lib/cache-cp15.c | 12 +++ 5 files changed, 230 insertions(+), 12 deletions(-) diff --git a/arch/arm/include/asm/dma-mapping.h b/arch/arm/include/asm/dma-mapping.h index a11178f..5a13af5 100644 --- a/arch/arm/include/asm/dma-mapping.h +++ b/arch/arm/include/asm/dma-mapping.h @@ -3,6 +3,9 @@ * Stelian Pop * Lead Tech Design * + * (C) Copyright 2010 + * Dante Su + * * See file CREDITS for list of people who contributed to this * project. * @@ -24,22 +27,76 @@ #ifndef __ASM_ARM_DMA_MAPPING_H #define __ASM_ARM_DMA_MAPPING_H +#if defined(CONFIG_FARADAY) && !defined(CONFIG_SYS_DCACHE_OFF) +#include +#include +#include +#include + +DECLARE_GLOBAL_DATA_PTR; +#endif /* CONFIG_FARADAY && !CONFIG_SYS_DCACHE_OFF */ + enum dma_data_direction { DMA_BIDIRECTIONAL = 0, DMA_TO_DEVICE = 1, DMA_FROM_DEVICE = 2, }; -static void *dma_alloc_coherent(size_t len, unsigned long *handle) +static inline void *dma_alloc_coherent(size_t len, unsigned long *handle) { +#if defined(CONFIG_FARADAY) && !defined(CONFIG_SYS_DCACHE_OFF) + void *map, *va = memalign(ARCH_DMA_MINALIGN, len); + + if (va && gd->arch.cpu_mmu) { + invalidate_dcache_range((ulong)va, (ulong)va + len); + map = map_physmem((phys_addr_t)va, len, MAP_NOCACHE); + if (!map) + free(va); + va = map; + } + + if (handle) + *handle = virt_to_phys(va); + + return va; +#else /* CONFIG_FARADAY && !CONFIG_SYS_DCACHE_OFF */ *handle = (unsigned long)memalign(ARCH_DMA_MINALIGN, len); return (void *)*handle; +#endif /* CONFIG_FARADAY && !CONFIG_SYS_DCACHE_OFF */ +} + +static inline void dma_free_coherent(void *vaddr, ulong len) +{ +#if defined(CONFIG_FARADAY) && !defined(CONFIG_SYS_DCACHE_OFF) + void *tmp = (void *)virt_to_phys(vaddr); + unmap_physmem(vaddr, len); + vaddr = tmp; +#endif + free(vaddr); } static inline unsigned long dma_map_single(volatile void *vaddr, size_t len, enum dma_data_direction dir) { +#if defined(CONFIG_FARADAY) && !defined(CONFIG_SYS_DCACHE_OFF) + if (gd->arch.cpu_mmu) { + switch (dir) { + case DMA_BIDIRECTIONAL: + case DMA_TO_DEVICE: + flush_dcache_range((ulong)vaddr, + (ulong)vaddr + len); + break; + + case DMA_FROM_DEVICE: + invalidate_dcache_range((ulong)vaddr, + (ulong)vaddr + len); + break; + } + } + return virt_to_phys((void *)vaddr); +#else /* CONFIG_FARADAY && !CONFIG_SYS_DCACHE_OFF */ return (unsigned long)vaddr; +#endif /* CONFIG_FARADAY && !CONFIG_SYS_DCACHE_OFF */ } static inline void dma_unmap_single(volatile void *vaddr, size_t len, diff --git a/arch/arm/include/asm/global_data.h b/arch/arm/include/asm/global_data.h index 7611d0a..fc78c6a 100644 --- a/arch/arm/include/asm/global_data.h +++ b/arch/arm/include/asm/global_data.h @@ -42,6 +42,10 @@ struct arch_global_data { unsigned long pllb_rate_hz; unsigned long at91_pllb_usb_init; #endif +#ifdef CONFIG_FARADAY + unsigned long cpu_id; + unsigned long cpu_mmu; /* has mmu */ +#endif /* "static data" needed by most of timer.c on ARM platforms */ unsigned long timer_rate_hz; unsigned long tbu; diff --git a/arch/arm/include/asm/io.h b/arch/arm/include/asm/io.h index 1fbc531..37c737e 100644 --- a/arch/arm/include/asm/io.h +++ b/arch/arm/include/asm/io.h @@ -2,6 +2,7 @@ * linux/include/asm-arm/io.h * * Copyright (C) 1996-2000 Russell King + * Copyright (C) 2009-2010 Dante Su * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as @@ -28,9 +29,36 @@ #if 0 /* XXX###XXX */ #include #endif /* XXX###XXX */ +#if defined(CONFIG_FARADAY) && !defined(CONFIG_SYS_DCACHE_OFF) +#include +#include +#include + +#ifndef CONFIG_MMAP_START +#define CONFIG_MMAP_START 0xd0000000 +#endif + +#ifndef CONFIG_MMAP_END +#define CONFIG_MMAP_END 0xfff00000 +#endif + +DECLARE_GLOBAL_DATA_PTR; + +#endif /* CONFIG_FARADAY && !CONFIG_SYS_DCACHE_OFF */ + +/* arch/$(ARCH)/lib/cache.c */ +void invalidate_icache_all(void); +void flush_dcache_all(void); +void flush_dcache_range(ulong start, ulong stop); static inline void sync(void) { +#ifndef CONFIG_SYS_DCACHE_OFF + flush_dcache_all(); +#endif +#ifndef CONFIG_SYS_ICACHE_OFF + invalidate_icache_all(); +#endif } /* @@ -39,27 +67,143 @@ static inline void sync(void) * properties specified by "flags". */ #define MAP_NOCACHE (0) -#define MAP_WRCOMBINE (0) -#define MAP_WRBACK (0) -#define MAP_WRTHROUGH (0) +#define MAP_WRCOMBINE (1) +#define MAP_WRBACK (2) +#define MAP_WRTHROUGH (3) + +#if defined(CONFIG_FARADAY) && !defined(CONFIG_SYS_DCACHE_OFF) +static inline void map_flush(ulong start, ulong end) +{ + flush_dcache_range(start, end); + + /* invalidate D-TLB */ + start &= 0xfff00000; + end = (end + 0x000fffff) & 0xfff00000; + __asm__ __volatile__ ( + "mov r3, %0\n" + "1:\n" + "mcr p15, 0, r3, c8, c6, 1\n" + "add r3, r3, #4096\n" + "cmp r3, %1\n" + "blo 1b\n" + : /* output */ + : "r"(start), "r"(end) /* input */ + : "r3" /* clobber list */ + ); +} +#endif /* CONFIG_FARADAY && !CONFIG_SYS_DCACHE_OFF */ static inline void * -map_physmem(phys_addr_t paddr, unsigned long len, unsigned long flags) +map_physmem(phys_addr_t paddr, ulong len, ulong flags) { +#if defined(CONFIG_FARADAY) && !defined(CONFIG_SYS_DCACHE_OFF) + u32 *page_table = (u32 *)gd->arch.tlb_addr; + u32 vaddr, nattr, oattr, addr, size, end; + + /* 1. check if we have to create a mapping for it */ + vaddr = paddr; + addr = page_table[vaddr >> 20] & 0xfff00000; + oattr = page_table[vaddr >> 20] & 0x1f; + switch (flags) { + case MAP_WRCOMBINE: + nattr = DCACHE_WRITECOMBINE; + break; + case MAP_WRTHROUGH: + nattr = DCACHE_WRITETHROUGH; + break; + case MAP_WRBACK: + nattr = DCACHE_WRITEBACK; + break; + default: + nattr = DCACHE_OFF; + break; + } + if ((nattr == oattr) && (vaddr == addr)) + return (void *)paddr; + + /* 2. find a contiguous region for it */ + end = (paddr + len + 0x000fffff) & 0xfff00000; + len = end - (paddr & 0xfff00000); + size = 0; + addr = CONFIG_MMAP_START; + vaddr = addr; + while (addr < CONFIG_MMAP_END) { + /* if va == pa, then it's free to use */ + if (addr == (page_table[addr >> 20] & 0xfff00000)) { + size += SZ_1M; + } else { + size = 0; + vaddr = addr + SZ_1M; + } + if (size >= len) + break; + addr += SZ_1M; + } + if (size < len) + return NULL; + + /* 3. create the map */ + map_flush(vaddr, vaddr + size); + addr = vaddr; + vaddr += paddr & 0x000fffff; + paddr &= 0xfff00000; + while (size) { + page_table[addr >> 20] = paddr | (3 << 10) | nattr; + size -= SZ_1M; + addr += SZ_1M; + paddr += SZ_1M; + } + return (void *)vaddr; +#else /* CONFIG_FARADAY && !CONFIG_SYS_DCACHE_OFF */ return (void *)paddr; +#endif /* CONFIG_FARADAY && !CONFIG_SYS_DCACHE_OFF */ } /* * Take down a mapping set up by map_physmem(). */ -static inline void unmap_physmem(void *vaddr, unsigned long flags) +static inline void unmap_physmem(void *vaddr, ulong len) { - +#if defined(CONFIG_FARADAY) && !defined(CONFIG_SYS_DCACHE_OFF) + u32 *page_table = (u32 *)gd->arch.tlb_addr; + u32 addr, end; + + /* 1. skip on NULL pointer */ + if (!vaddr) + return; + + /* 2. check if it's the right address map */ + addr = (u32)vaddr; + if ((page_table[addr >> 20] & 0xfff00000) == addr) + return; + + /* 3. reset the map */ + end = (addr + len + 0x000fffff) & 0xfff00000; + addr &= 0xfff00000; + map_flush(addr, end); + while (addr < end) { + page_table[addr >> 20] = addr | (3 << 10) | DCACHE_OFF; + addr += SZ_1M; + } +#endif /* CONFIG_FARADAY && !CONFIG_SYS_DCACHE_OFF */ } -static inline phys_addr_t virt_to_phys(void * vaddr) +static inline phys_addr_t virt_to_phys(void *vaddr) { - return (phys_addr_t)(vaddr); +#if defined(CONFIG_FARADAY) && !defined(CONFIG_SYS_DCACHE_OFF) + u32 *page_table = (u32 *)gd->arch.tlb_addr; + phys_addr_t phys = (phys_addr_t)vaddr; + + if (!gd->arch.cpu_mmu || !vaddr) + return phys; + + phys = page_table[(u32)vaddr >> 20] & 0xfff00000; + phys += (u32)vaddr & 0x000fffff; + + return phys; +#else /* CONFIG_FARADAY && !CONFIG_SYS_DCACHE_OFF */ + return (phys_addr_t)vaddr; +#endif /* CONFIG_FARADAY && !CONFIG_SYS_DCACHE_OFF */ } /* diff --git a/arch/arm/include/asm/system.h b/arch/arm/include/asm/system.h index 760345f..050b707 100644 --- a/arch/arm/include/asm/system.h +++ b/arch/arm/include/asm/system.h @@ -97,9 +97,10 @@ static inline void set_dacr(unsigned int val) /* options available for data cache on each page */ enum dcache_option { - DCACHE_OFF = 0x12, - DCACHE_WRITETHROUGH = 0x1a, - DCACHE_WRITEBACK = 0x1e, + DCACHE_OFF = 0x12, /* non-cached + non-buffered */ + DCACHE_WRITECOMBINE = 0x16, /* non-cached + buffered */ + DCACHE_WRITETHROUGH = 0x1a, /* cached + non-buffered */ + DCACHE_WRITEBACK = 0x1e, /* cached + buffered */ }; /* Size of an MMU section */ diff --git a/arch/arm/lib/cache-cp15.c b/arch/arm/lib/cache-cp15.c index 4abe1cf..97436f6 100644 --- a/arch/arm/lib/cache-cp15.c +++ b/arch/arm/lib/cache-cp15.c @@ -1,6 +1,8 @@ /* * (C) Copyright 2002 * Wolfgang Denk, DENX Software Engineering, wd at denx.de. + * (C) Copyright 2010 + * Dante Su * * See file CREDITS for list of people who contributed to this * project. @@ -126,6 +128,10 @@ static inline void mmu_setup(void) /* and enable the mmu */ reg = get_cr(); /* get control reg. */ +#ifdef CONFIG_FARADAY + reg |= CR_W; /* enable write buffer */ + reg |= CR_Z; /* enable branch prediction */ +#endif cp_delay(); set_cr(reg | CR_M); } @@ -140,9 +146,15 @@ static void cache_enable(uint32_t cache_bit) { uint32_t reg; +#ifdef CONFIG_FARADAY + if (!gd->arch.cpu_mmu && (cache_bit == CR_C)) + return; +#endif + /* The data cache is not active unless the mmu is enabled too */ if ((cache_bit == CR_C) && !mmu_enabled()) mmu_setup(); + reg = get_cr(); /* get control reg. */ cp_delay(); set_cr(reg | cache_bit); -- 1.7.9.5