From patchwork Mon Jun 17 12:06:57 2013 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Kuo-Jung Su X-Patchwork-Id: 251829 X-Patchwork-Delegate: albert.aribaud@free.fr Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Received: from theia.denx.de (theia.denx.de [85.214.87.163]) by ozlabs.org (Postfix) with ESMTP id 04DC32C029B for ; Mon, 17 Jun 2013 22:09:30 +1000 (EST) Received: from localhost (localhost [127.0.0.1]) by theia.denx.de (Postfix) with ESMTP id BC26B4A1BC; Mon, 17 Jun 2013 14:09:20 +0200 (CEST) X-Virus-Scanned: Debian amavisd-new at theia.denx.de Received: from theia.denx.de ([127.0.0.1]) by localhost (theia.denx.de [127.0.0.1]) (amavisd-new, port 10024) with ESMTP id 4k9BXSkvskuU; Mon, 17 Jun 2013 14:09:20 +0200 (CEST) Received: from theia.denx.de (localhost [127.0.0.1]) by theia.denx.de (Postfix) with ESMTP id BDA394A1C9; Mon, 17 Jun 2013 14:08:20 +0200 (CEST) Received: from localhost (localhost [127.0.0.1]) by theia.denx.de (Postfix) with ESMTP id B71764A1C2 for ; Mon, 17 Jun 2013 14:08:06 +0200 (CEST) X-Virus-Scanned: Debian amavisd-new at theia.denx.de Received: from theia.denx.de ([127.0.0.1]) by localhost (theia.denx.de [127.0.0.1]) (amavisd-new, port 10024) with ESMTP id kY-sX81fj56S for ; Mon, 17 Jun 2013 14:08:00 +0200 (CEST) X-policyd-weight: NOT_IN_SBL_XBL_SPAMHAUS=-1.5 NOT_IN_SPAMCOP=-1.5 NOT_IN_BL_NJABL=-1.5 (only DNSBL check requested) Received: from mail-pd0-f171.google.com (mail-pd0-f171.google.com [209.85.192.171]) by theia.denx.de (Postfix) with ESMTPS id 485AA4A1BE for ; Mon, 17 Jun 2013 14:07:38 +0200 (CEST) Received: by mail-pd0-f171.google.com with SMTP id y14so2722880pdi.2 for ; Mon, 17 Jun 2013 05:07:36 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20120113; h=from:to:cc:subject:date:message-id:x-mailer:in-reply-to:references :in-reply-to:references; bh=ypGafw9nt5Ew1wx4iMT6OQOD3UPxUJlQ67Ph2sbC9Rc=; b=eSl6g9/aOdr3ZST82VJMw50Arf0ZHv2f8UOa+t4wEPbG9Ca+UZlatdLRmEcaqS2aQX h88pF+5oo9x7BOeoNNftjKAW2am3g6fRYnCjfuyS/N1bV1uaPUulohVsthRZ/Gnm5YRJ aPWLg1L1z6EXp0YZ3m3c2p3ST94JvtLqC5Es8wxW3VqYrMwlYG0RBBYXJk1NXj7vkage 2TXO2fUYpEwzsh1J/wwsLGZPR2DjU8hdMxj6C0SqdoEjP5yqrlYXLGNcCXlfntOs7Hug hWZC6FJBSY+LaA7sp+1ONf0dHd37ojFICg5EH0bKQRjYiPl60apJcm55h+RZtjBefxIE Q5HA== X-Received: by 10.66.135.18 with SMTP id po18mr12839530pab.1.1371470856586; Mon, 17 Jun 2013 05:07:36 -0700 (PDT) Received: from localhost.localdomain (114-35-170-161.HINET-IP.hinet.net. [114.35.170.161]) by mx.google.com with ESMTPSA id wt5sm13621839pbc.38.2013.06.17.05.07.34 for (version=TLSv1 cipher=DES-CBC3-SHA bits=168/168); Mon, 17 Jun 2013 05:07:35 -0700 (PDT) From: Kuo-Jung Su To: u-boot@lists.denx.de Date: Mon, 17 Jun 2013 20:06:57 +0800 Message-Id: <1371470824-3228-8-git-send-email-dantesu@gmail.com> X-Mailer: git-send-email 1.7.9.5 In-Reply-To: <1371470824-3228-1-git-send-email-dantesu@gmail.com> References: <1371470824-3228-1-git-send-email-dantesu@gmail.com> In-Reply-To: <1364540788-13943-1-git-send-email-dantesu@gmail.com> References: <1364540788-13943-1-git-send-email-dantesu@gmail.com> Cc: Kuo-Jung Su Subject: [U-Boot] [PATCH v5 07/14] arm: add MMU/D-Cache support for Faraday cores X-BeenThere: u-boot@lists.denx.de X-Mailman-Version: 2.1.11 Precedence: list List-Id: U-Boot discussion List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , MIME-Version: 1.0 Sender: u-boot-bounces@lists.denx.de Errors-To: u-boot-bounces@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 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(-) -- 1.7.9.5 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@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);