From 44ae903b96b1e5f9ff3103cd86918619c188003f Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Thu, 20 Mar 2014 15:16:54 +0100 Subject: ARM: 8008/1: topology: Coding style fixes Use kcalloc() and ULONG_MAX rather than open coding them. Signed-off-by: Mark Brown Signed-off-by: Russell King --- arch/arm/kernel/topology.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/arch/arm/kernel/topology.c b/arch/arm/kernel/topology.c index 0bc94b1fd1ae..0fa8825cea04 100644 --- a/arch/arm/kernel/topology.c +++ b/arch/arm/kernel/topology.c @@ -91,13 +91,13 @@ static void __init parse_dt_topology(void) { const struct cpu_efficiency *cpu_eff; struct device_node *cn = NULL; - unsigned long min_capacity = (unsigned long)(-1); + unsigned long min_capacity = ULONG_MAX; unsigned long max_capacity = 0; unsigned long capacity = 0; - int alloc_size, cpu = 0; + int cpu = 0; - alloc_size = nr_cpu_ids * sizeof(*__cpu_capacity); - __cpu_capacity = kzalloc(alloc_size, GFP_NOWAIT); + __cpu_capacity = kcalloc(nr_cpu_ids, sizeof(*__cpu_capacity), + GFP_NOWAIT); for_each_possible_cpu(cpu) { const u32 *rate; -- cgit v1.2.3 From 603fb42a66499ab353466c7afa3d38beea20a8a9 Mon Sep 17 00:00:00 2001 From: Sebastian Capella Date: Tue, 25 Mar 2014 01:20:29 +0100 Subject: ARM: 8011/1: ARM hibernation / suspend-to-disk Enable hibernation for ARM architectures and provide ARM architecture specific calls used during hibernation. The swsusp hibernation framework depends on the platform first having functional suspend/resume. Then, in order to enable hibernation on a given platform, a platform_hibernation_ops structure may need to be registered with the system in order to save/restore any SoC-specific / cpu specific state needing (re)init over a suspend-to-disk/resume-from-disk cycle. For example: - "secure" SoCs that have different sets of control registers and/or different CR reg access patterns. - SoCs with L2 caches as the activation sequence there is SoC-dependent; a full off-on cycle for L2 is not done by the hibernation support code. - SoCs requiring steps on wakeup _before_ the "generic" parts done by cpu_suspend / cpu_resume can work correctly. - SoCs having persistent state which is maintained during suspend and resume, but will be lost during the power off cycle after suspend-to-disk. This is a rebase/rework of Frank Hofmann's v5 hibernation patchset. Acked-by: Russ Dill Cc: "Rafael J. Wysocki" Signed-off-by: Sebastian Capella Acked-by: Pavel Machek Reviewed-by: Lorenzo Pieralisi [fixed duplicate virt_to_pfn() definition --rmk] Signed-off-by: Russell King --- arch/arm/Kconfig | 5 +++ arch/arm/kernel/Makefile | 1 + arch/arm/kernel/hibernate.c | 107 ++++++++++++++++++++++++++++++++++++++++++++ include/linux/suspend.h | 2 + 4 files changed, 115 insertions(+) create mode 100644 arch/arm/kernel/hibernate.c diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig index ab438cb5af55..58506175a3ea 100644 --- a/arch/arm/Kconfig +++ b/arch/arm/Kconfig @@ -2294,6 +2294,11 @@ config ARCH_SUSPEND_POSSIBLE config ARM_CPU_SUSPEND def_bool PM_SLEEP +config ARCH_HIBERNATION_POSSIBLE + bool + depends on MMU + default y if ARCH_SUSPEND_POSSIBLE + endmenu source "net/Kconfig" diff --git a/arch/arm/kernel/Makefile b/arch/arm/kernel/Makefile index a766bcbaf8ad..10f0464206a2 100644 --- a/arch/arm/kernel/Makefile +++ b/arch/arm/kernel/Makefile @@ -39,6 +39,7 @@ obj-$(CONFIG_ARTHUR) += arthur.o obj-$(CONFIG_ISA_DMA) += dma-isa.o obj-$(CONFIG_PCI) += bios32.o isa.o obj-$(CONFIG_ARM_CPU_SUSPEND) += sleep.o suspend.o +obj-$(CONFIG_HIBERNATION) += hibernate.o obj-$(CONFIG_SMP) += smp.o ifdef CONFIG_MMU obj-$(CONFIG_SMP) += smp_tlb.o diff --git a/arch/arm/kernel/hibernate.c b/arch/arm/kernel/hibernate.c new file mode 100644 index 000000000000..bb8b79648643 --- /dev/null +++ b/arch/arm/kernel/hibernate.c @@ -0,0 +1,107 @@ +/* + * Hibernation support specific for ARM + * + * Derived from work on ARM hibernation support by: + * + * Ubuntu project, hibernation support for mach-dove + * Copyright (C) 2010 Nokia Corporation (Hiroshi Doyu) + * Copyright (C) 2010 Texas Instruments, Inc. (Teerth Reddy et al.) + * https://lkml.org/lkml/2010/6/18/4 + * https://lists.linux-foundation.org/pipermail/linux-pm/2010-June/027422.html + * https://patchwork.kernel.org/patch/96442/ + * + * Copyright (C) 2006 Rafael J. Wysocki + * + * License terms: GNU General Public License (GPL) version 2 + */ + +#include +#include +#include +#include +#include +#include + +extern const void __nosave_begin, __nosave_end; + +int pfn_is_nosave(unsigned long pfn) +{ + unsigned long nosave_begin_pfn = virt_to_pfn(&__nosave_begin); + unsigned long nosave_end_pfn = virt_to_pfn(&__nosave_end - 1); + + return (pfn >= nosave_begin_pfn) && (pfn <= nosave_end_pfn); +} + +void notrace save_processor_state(void) +{ + WARN_ON(num_online_cpus() != 1); + local_fiq_disable(); +} + +void notrace restore_processor_state(void) +{ + local_fiq_enable(); +} + +/* + * Snapshot kernel memory and reset the system. + * + * swsusp_save() is executed in the suspend finisher so that the CPU + * context pointer and memory are part of the saved image, which is + * required by the resume kernel image to restart execution from + * swsusp_arch_suspend(). + * + * soft_restart is not technically needed, but is used to get success + * returned from cpu_suspend. + * + * When soft reboot completes, the hibernation snapshot is written out. + */ +static int notrace arch_save_image(unsigned long unused) +{ + int ret; + + ret = swsusp_save(); + if (ret == 0) + soft_restart(virt_to_phys(cpu_resume)); + return ret; +} + +/* + * Save the current CPU state before suspend / poweroff. + */ +int notrace swsusp_arch_suspend(void) +{ + return cpu_suspend(0, arch_save_image); +} + +/* + * Restore page contents for physical pages that were in use during loading + * hibernation image. Switch to idmap_pgd so the physical page tables + * are overwritten with the same contents. + */ +static void notrace arch_restore_image(void *unused) +{ + struct pbe *pbe; + + cpu_switch_mm(idmap_pgd, &init_mm); + for (pbe = restore_pblist; pbe; pbe = pbe->next) + copy_page(pbe->orig_address, pbe->address); + + soft_restart(virt_to_phys(cpu_resume)); +} + +static u64 resume_stack[PAGE_SIZE/2/sizeof(u64)] __nosavedata; + +/* + * Resume from the hibernation image. + * Due to the kernel heap / data restore, stack contents change underneath + * and that would make function calls impossible; switch to a temporary + * stack within the nosave region to avoid that problem. + */ +int swsusp_arch_resume(void) +{ + extern void call_with_stack(void (*fn)(void *), void *arg, void *sp); + call_with_stack(arch_restore_image, 0, + resume_stack + ARRAY_SIZE(resume_stack)); + return 0; +} diff --git a/include/linux/suspend.h b/include/linux/suspend.h index f73cabf59012..38bbf95109da 100644 --- a/include/linux/suspend.h +++ b/include/linux/suspend.h @@ -320,6 +320,8 @@ extern unsigned long get_safe_page(gfp_t gfp_mask); extern void hibernation_set_ops(const struct platform_hibernation_ops *ops); extern int hibernate(void); extern bool system_entering_hibernation(void); +asmlinkage int swsusp_save(void); +extern struct pbe *restore_pblist; #else /* CONFIG_HIBERNATION */ static inline void register_nosave_region(unsigned long b, unsigned long e) {} static inline void register_nosave_region_late(unsigned long b, unsigned long e) {} -- cgit v1.2.3 From a672917ab8964228f83ac1f0728468b309d6ea19 Mon Sep 17 00:00:00 2001 From: Rabin Vincent Date: Thu, 3 Apr 2014 18:46:45 +0100 Subject: ARM: 8022/1: ftrace: work with CONFIG_DEBUG_SET_MODULE_RONX Make ftrace work with CONFIG_DEBUG_SET_MODULE_RONX by making module text writable around the place where ftrace does its work, like it is done on x86 in the patch which introduced CONFIG_DEBUG_SET_MODULE_RONX, 84e1c6bb38eb ("x86: Add RO/NX protection for loadable kernel modules"). Tested-by: Mitchel Humpherys Signed-off-by: Rabin Vincent Signed-off-by: Russell King --- arch/arm/kernel/ftrace.c | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/arch/arm/kernel/ftrace.c b/arch/arm/kernel/ftrace.c index c108ddcb9ba4..af9a8a927a4e 100644 --- a/arch/arm/kernel/ftrace.c +++ b/arch/arm/kernel/ftrace.c @@ -14,6 +14,7 @@ #include #include +#include #include #include @@ -63,6 +64,18 @@ static unsigned long adjust_address(struct dyn_ftrace *rec, unsigned long addr) } #endif +int ftrace_arch_code_modify_prepare(void) +{ + set_all_modules_text_rw(); + return 0; +} + +int ftrace_arch_code_modify_post_process(void) +{ + set_all_modules_text_ro(); + return 0; +} + static unsigned long ftrace_call_replace(unsigned long pc, unsigned long addr) { return arm_gen_branch_link(pc, addr); -- cgit v1.2.3 From 64d3b6a3f480154b6727dd2187f5f2b58c15da77 Mon Sep 17 00:00:00 2001 From: Nicolas Pitre Date: Wed, 9 Apr 2014 21:24:02 +0100 Subject: ARM: 8023/1: remove remnants of the static DMA mapping It looks like the static mapping area for DMA was replaced by dynamic allocation into the vmalloc area by commit e9da6e9905e6 but the information in Documentation/arm/memory.txt was not removed accordingly. CONSISTENT_END in arch/arm/include/asm/memory.h has no more users and can be removed as well. Signed-off-by: Nicolas Pitre Signed-off-by: Russell King --- Documentation/arm/memory.txt | 7 ------- arch/arm/include/asm/memory.h | 2 -- 2 files changed, 9 deletions(-) diff --git a/Documentation/arm/memory.txt b/Documentation/arm/memory.txt index 4bfb9ffbdbc1..d74e8a5901fc 100644 --- a/Documentation/arm/memory.txt +++ b/Documentation/arm/memory.txt @@ -44,13 +44,6 @@ fffe0000 fffe7fff ITCM mapping area for platforms with fff00000 fffdffff Fixmap mapping region. Addresses provided by fix_to_virt() will be located here. -ffc00000 ffefffff DMA memory mapping region. Memory returned - by the dma_alloc_xxx functions will be - dynamically mapped here. - -ff000000 ffbfffff Reserved for future expansion of DMA - mapping region. - fee00000 feffffff Mapping of PCI I/O space. This is a static mapping within the vmalloc space. diff --git a/arch/arm/include/asm/memory.h b/arch/arm/include/asm/memory.h index 02fa2558f662..2b751464d6ff 100644 --- a/arch/arm/include/asm/memory.h +++ b/arch/arm/include/asm/memory.h @@ -83,8 +83,6 @@ */ #define IOREMAP_MAX_ORDER 24 -#define CONSISTENT_END (0xffe00000UL) - #else /* CONFIG_MMU */ /* -- cgit v1.2.3 From 16c79a3776c17965dedab4732d925f6098df91c1 Mon Sep 17 00:00:00 2001 From: Gregory CLEMENT Date: Fri, 28 Mar 2014 12:21:16 +0100 Subject: ARM: 8013/1: PJ4B: Add cpu_suspend/cpu_resume hooks for PJ4B PJ4B needs extra instructions for suspend and resume, so instead of using the armv7 version, this commit introduces specific versions for PJ4B. Signed-off-by: Gregory CLEMENT Signed-off-by: Russell King --- arch/arm/mm/proc-v7.S | 28 +++++++++++++++++++++++++--- 1 file changed, 25 insertions(+), 3 deletions(-) diff --git a/arch/arm/mm/proc-v7.S b/arch/arm/mm/proc-v7.S index 195731d3813b..b74ea60891d5 100644 --- a/arch/arm/mm/proc-v7.S +++ b/arch/arm/mm/proc-v7.S @@ -169,9 +169,31 @@ ENDPROC(cpu_pj4b_do_idle) globl_equ cpu_pj4b_do_idle, cpu_v7_do_idle #endif globl_equ cpu_pj4b_dcache_clean_area, cpu_v7_dcache_clean_area - globl_equ cpu_pj4b_do_suspend, cpu_v7_do_suspend - globl_equ cpu_pj4b_do_resume, cpu_v7_do_resume - globl_equ cpu_pj4b_suspend_size, cpu_v7_suspend_size +#ifdef CONFIG_ARM_CPU_SUSPEND +ENTRY(cpu_pj4b_do_suspend) + stmfd sp!, {r6 - r10} + mrc p15, 1, r6, c15, c1, 0 @ save CP15 - extra features + mrc p15, 1, r7, c15, c2, 0 @ save CP15 - Aux Func Modes Ctrl 0 + mrc p15, 1, r8, c15, c1, 2 @ save CP15 - Aux Debug Modes Ctrl 2 + mrc p15, 1, r9, c15, c1, 1 @ save CP15 - Aux Debug Modes Ctrl 1 + mrc p15, 0, r10, c9, c14, 0 @ save CP15 - PMC + stmia r0!, {r6 - r10} + ldmfd sp!, {r6 - r10} + b cpu_v7_do_suspend +ENDPROC(cpu_pj4b_do_suspend) + +ENTRY(cpu_pj4b_do_resume) + ldmia r0!, {r6 - r10} + mcr p15, 1, r6, c15, c1, 0 @ save CP15 - extra features + mcr p15, 1, r7, c15, c2, 0 @ save CP15 - Aux Func Modes Ctrl 0 + mcr p15, 1, r8, c15, c1, 2 @ save CP15 - Aux Debug Modes Ctrl 2 + mcr p15, 1, r9, c15, c1, 1 @ save CP15 - Aux Debug Modes Ctrl 1 + mcr p15, 0, r10, c9, c14, 0 @ save CP15 - PMC + b cpu_v7_do_resume +ENDPROC(cpu_pj4b_do_resume) +#endif +.globl cpu_pj4b_suspend_size +.equ cpu_pj4b_suspend_size, 4 * 14 #endif -- cgit v1.2.3 From 4221e2e6b3160e4b558df14fa79f025c0e277935 Mon Sep 17 00:00:00 2001 From: Liu Hua Date: Fri, 18 Apr 2014 09:27:01 +0100 Subject: ARM: 8031/1: fixmap: remove FIX_KMAP_BEGIN and FIX_KMAP_END It seems that these two macros are not used by non architecture specific code. And on ARM FIX_KMAP_BEGIN equals zero. This patch removes these two macros. Instead, using FIX_KMAP_NR_PTES to tell the pte number belonged to fixmap mapping region. The code will become clearer when I introduce a bugfix on fixmap mapping region. Reviewed-by: Nicolas Pitre Signed-off-by: Liu Hua Signed-off-by: Russell King --- arch/arm/include/asm/fixmap.h | 5 ++--- arch/arm/mm/highmem.c | 6 +++--- 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/arch/arm/include/asm/fixmap.h b/arch/arm/include/asm/fixmap.h index bbae919bceb4..be55ebc08ed4 100644 --- a/arch/arm/include/asm/fixmap.h +++ b/arch/arm/include/asm/fixmap.h @@ -17,8 +17,7 @@ #define FIXADDR_TOP 0xfffe0000UL #define FIXADDR_SIZE (FIXADDR_TOP - FIXADDR_START) -#define FIX_KMAP_BEGIN 0 -#define FIX_KMAP_END (FIXADDR_SIZE >> PAGE_SHIFT) +#define FIX_KMAP_NR_PTES (FIXADDR_SIZE >> PAGE_SHIFT) #define __fix_to_virt(x) (FIXADDR_START + ((x) << PAGE_SHIFT)) #define __virt_to_fix(x) (((x) - FIXADDR_START) >> PAGE_SHIFT) @@ -27,7 +26,7 @@ extern void __this_fixmap_does_not_exist(void); static inline unsigned long fix_to_virt(const unsigned int idx) { - if (idx >= FIX_KMAP_END) + if (idx >= FIX_KMAP_NR_PTES) __this_fixmap_does_not_exist(); return __fix_to_virt(idx); } diff --git a/arch/arm/mm/highmem.c b/arch/arm/mm/highmem.c index 21b9e1bf9b77..e05e8ad26ba5 100644 --- a/arch/arm/mm/highmem.c +++ b/arch/arm/mm/highmem.c @@ -63,7 +63,7 @@ void *kmap_atomic(struct page *page) type = kmap_atomic_idx_push(); idx = type + KM_TYPE_NR * smp_processor_id(); - vaddr = __fix_to_virt(FIX_KMAP_BEGIN + idx); + vaddr = __fix_to_virt(idx); #ifdef CONFIG_DEBUG_HIGHMEM /* * With debugging enabled, kunmap_atomic forces that entry to 0. @@ -94,7 +94,7 @@ void __kunmap_atomic(void *kvaddr) if (cache_is_vivt()) __cpuc_flush_dcache_area((void *)vaddr, PAGE_SIZE); #ifdef CONFIG_DEBUG_HIGHMEM - BUG_ON(vaddr != __fix_to_virt(FIX_KMAP_BEGIN + idx)); + BUG_ON(vaddr != __fix_to_virt(idx)); set_top_pte(vaddr, __pte(0)); #else (void) idx; /* to kill a warning */ @@ -117,7 +117,7 @@ void *kmap_atomic_pfn(unsigned long pfn) type = kmap_atomic_idx_push(); idx = type + KM_TYPE_NR * smp_processor_id(); - vaddr = __fix_to_virt(FIX_KMAP_BEGIN + idx); + vaddr = __fix_to_virt(idx); #ifdef CONFIG_DEBUG_HIGHMEM BUG_ON(!pte_none(get_top_pte(vaddr))); #endif -- cgit v1.2.3 From a05e54c103b0b8e1dab5d04b411f1d48387c4903 Mon Sep 17 00:00:00 2001 From: Liu Hua Date: Fri, 18 Apr 2014 09:43:32 +0100 Subject: ARM: 8031/2: change fixmap mapping region to support 32 CPUs In 32-bit ARM systems, the fixmap mapping region can support no more than 14 CPUs(total: 896k; one CPU: 64K). And we can configure NR_CPUS up to 32. So there is a mismatch. This patch moves fixmapping region downwards to region 0xffc00000- 0xffe00000. Then the fixmap mapping region can support up to 32 CPUs. Reviewed-by: Nicolas Pitre Signed-off-by: Liu Hua Signed-off-by: Russell King --- Documentation/arm/memory.txt | 2 +- arch/arm/include/asm/fixmap.h | 16 ++-------------- arch/arm/include/asm/highmem.h | 1 + arch/arm/mm/highmem.c | 27 +++++++++++++++++++++------ arch/arm/mm/mmu.c | 4 ++++ 5 files changed, 29 insertions(+), 21 deletions(-) diff --git a/Documentation/arm/memory.txt b/Documentation/arm/memory.txt index d74e8a5901fc..256c5e067fb1 100644 --- a/Documentation/arm/memory.txt +++ b/Documentation/arm/memory.txt @@ -41,7 +41,7 @@ fffe8000 fffeffff DTCM mapping area for platforms with fffe0000 fffe7fff ITCM mapping area for platforms with ITCM mounted inside the CPU. -fff00000 fffdffff Fixmap mapping region. Addresses provided +fffc0000 ffdfffff Fixmap mapping region. Addresses provided by fix_to_virt() will be located here. fee00000 feffffff Mapping of PCI I/O space. This is a static diff --git a/arch/arm/include/asm/fixmap.h b/arch/arm/include/asm/fixmap.h index be55ebc08ed4..74124b0d0d79 100644 --- a/arch/arm/include/asm/fixmap.h +++ b/arch/arm/include/asm/fixmap.h @@ -1,20 +1,8 @@ #ifndef _ASM_FIXMAP_H #define _ASM_FIXMAP_H -/* - * Nothing too fancy for now. - * - * On ARM we already have well known fixed virtual addresses imposed by - * the architecture such as the vector page which is located at 0xffff0000, - * therefore a second level page table is already allocated covering - * 0xfff00000 upwards. - * - * The cache flushing code in proc-xscale.S uses the virtual area between - * 0xfffe0000 and 0xfffeffff. - */ - -#define FIXADDR_START 0xfff00000UL -#define FIXADDR_TOP 0xfffe0000UL +#define FIXADDR_START 0xffc00000UL +#define FIXADDR_TOP 0xffe00000UL #define FIXADDR_SIZE (FIXADDR_TOP - FIXADDR_START) #define FIX_KMAP_NR_PTES (FIXADDR_SIZE >> PAGE_SHIFT) diff --git a/arch/arm/include/asm/highmem.h b/arch/arm/include/asm/highmem.h index 91b99abe7a95..535579511ed0 100644 --- a/arch/arm/include/asm/highmem.h +++ b/arch/arm/include/asm/highmem.h @@ -18,6 +18,7 @@ } while (0) extern pte_t *pkmap_page_table; +extern pte_t *fixmap_page_table; extern void *kmap_high(struct page *page); extern void kunmap_high(struct page *page); diff --git a/arch/arm/mm/highmem.c b/arch/arm/mm/highmem.c index e05e8ad26ba5..45aeaaca9052 100644 --- a/arch/arm/mm/highmem.c +++ b/arch/arm/mm/highmem.c @@ -18,6 +18,21 @@ #include #include "mm.h" +pte_t *fixmap_page_table; + +static inline void set_fixmap_pte(int idx, pte_t pte) +{ + unsigned long vaddr = __fix_to_virt(idx); + set_pte_ext(fixmap_page_table + idx, pte, 0); + local_flush_tlb_kernel_page(vaddr); +} + +static inline pte_t get_fixmap_pte(unsigned long vaddr) +{ + unsigned long idx = __virt_to_fix(vaddr); + return *(fixmap_page_table + idx); +} + void *kmap(struct page *page) { might_sleep(); @@ -69,14 +84,14 @@ void *kmap_atomic(struct page *page) * With debugging enabled, kunmap_atomic forces that entry to 0. * Make sure it was indeed properly unmapped. */ - BUG_ON(!pte_none(get_top_pte(vaddr))); + BUG_ON(!pte_none(*(fixmap_page_table + idx))); #endif /* * When debugging is off, kunmap_atomic leaves the previous mapping * in place, so the contained TLB flush ensures the TLB is updated * with the new mapping. */ - set_top_pte(vaddr, mk_pte(page, kmap_prot)); + set_fixmap_pte(idx, mk_pte(page, kmap_prot)); return (void *)vaddr; } @@ -95,7 +110,7 @@ void __kunmap_atomic(void *kvaddr) __cpuc_flush_dcache_area((void *)vaddr, PAGE_SIZE); #ifdef CONFIG_DEBUG_HIGHMEM BUG_ON(vaddr != __fix_to_virt(idx)); - set_top_pte(vaddr, __pte(0)); + set_fixmap_pte(idx, __pte(0)); #else (void) idx; /* to kill a warning */ #endif @@ -119,9 +134,9 @@ void *kmap_atomic_pfn(unsigned long pfn) idx = type + KM_TYPE_NR * smp_processor_id(); vaddr = __fix_to_virt(idx); #ifdef CONFIG_DEBUG_HIGHMEM - BUG_ON(!pte_none(get_top_pte(vaddr))); + BUG_ON(!pte_none(*(fixmap_page_table + idx))); #endif - set_top_pte(vaddr, pfn_pte(pfn, kmap_prot)); + set_fixmap_pte(idx, pfn_pte(pfn, kmap_prot)); return (void *)vaddr; } @@ -133,5 +148,5 @@ struct page *kmap_atomic_to_page(const void *ptr) if (vaddr < FIXADDR_START) return virt_to_page(ptr); - return pte_page(get_top_pte(vaddr)); + return pte_page(get_fixmap_pte(vaddr)); } diff --git a/arch/arm/mm/mmu.c b/arch/arm/mm/mmu.c index b68c6b22e1c8..09c0a16165dc 100644 --- a/arch/arm/mm/mmu.c +++ b/arch/arm/mm/mmu.c @@ -35,6 +35,7 @@ #include #include #include +#include #include "mm.h" #include "tcm.h" @@ -1359,6 +1360,9 @@ static void __init kmap_init(void) #ifdef CONFIG_HIGHMEM pkmap_page_table = early_pte_alloc(pmd_off_k(PKMAP_BASE), PKMAP_BASE, _PAGE_KERNEL_TABLE); + + fixmap_page_table = early_pte_alloc(pmd_off_k(FIXADDR_START), + FIXADDR_START, _PAGE_KERNEL_TABLE); #endif } -- cgit v1.2.3 From 431a84b1a4f7d1a0085d5b91330c5053cc8e8b12 Mon Sep 17 00:00:00 2001 From: Catalin Marinas Date: Tue, 22 Apr 2014 16:14:27 +0100 Subject: ARM: 8034/1: Disable preemption in iwmmxt_task_enable() This patch is in preparation for calling the iwmmxt_task_enable() function with interrupts enabled. Signed-off-by: Catalin Marinas Signed-off-by: Russell King --- arch/arm/kernel/iwmmxt.S | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/arch/arm/kernel/iwmmxt.S b/arch/arm/kernel/iwmmxt.S index a08783823b32..fcb33a70a35f 100644 --- a/arch/arm/kernel/iwmmxt.S +++ b/arch/arm/kernel/iwmmxt.S @@ -18,6 +18,7 @@ #include #include #include +#include #if defined(CONFIG_CPU_PJ4) #define PJ4(code...) code @@ -65,13 +66,14 @@ */ ENTRY(iwmmxt_task_enable) + inc_preempt_count r10, r3 XSC(mrc p15, 0, r2, c15, c1, 0) PJ4(mrc p15, 0, r2, c1, c0, 2) @ CP0 and CP1 accessible? XSC(tst r2, #0x3) PJ4(tst r2, #0xf) - movne pc, lr @ if so no business here + bne 4f @ if so no business here @ enable access to CP0 and CP1 XSC(orr r2, r2, #0x3) XSC(mcr p15, 0, r2, c15, c1, 0) @@ -132,7 +134,7 @@ concan_dump: wstrd wR15, [r1, #MMX_WR15] 2: teq r0, #0 @ anything to load? - moveq pc, lr + beq 3f concan_load: @@ -165,8 +167,14 @@ concan_load: @ clear CUP/MUP (only if r1 != 0) teq r1, #0 mov r2, #0 - moveq pc, lr + beq 3f tmcr wCon, r2 + +3: +#ifdef CONFIG_PREEMPT_COUNT + get_thread_info r10 +#endif +4: dec_preempt_count r10, r3 mov pc, lr /* -- cgit v1.2.3 From bc94081c6ac823c4723d8e36e9604c6cf3eba0ef Mon Sep 17 00:00:00 2001 From: Catalin Marinas Date: Tue, 22 Apr 2014 16:14:28 +0100 Subject: ARM: 8035/1: Disable preemption in crunch_task_enable() This patch is in preparation for calling the crunch_task_enable() function with interrupts enabled. Signed-off-by: Catalin Marinas Cc: Hartley Sweeten Cc: Ryan Mallon Signed-off-by: Russell King --- arch/arm/mach-ep93xx/crunch-bits.S | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/arch/arm/mach-ep93xx/crunch-bits.S b/arch/arm/mach-ep93xx/crunch-bits.S index 0ec9bb48fab9..eaa5e34729d3 100644 --- a/arch/arm/mach-ep93xx/crunch-bits.S +++ b/arch/arm/mach-ep93xx/crunch-bits.S @@ -16,6 +16,7 @@ #include #include #include +#include #include /* @@ -65,11 +66,13 @@ * called from prefetch exception handler with interrupts disabled */ ENTRY(crunch_task_enable) + inc_preempt_count r10, r3 + ldr r8, =(EP93XX_APB_VIRT_BASE + 0x00130000) @ syscon addr ldr r1, [r8, #0x80] tst r1, #0x00800000 @ access to crunch enabled? - movne pc, lr @ if so no business here + bne 2f @ if so no business here mov r3, #0xaa @ unlock syscon swlock str r3, [r8, #0xc0] orr r1, r1, #0x00800000 @ enable access to crunch @@ -142,7 +145,7 @@ crunch_save: teq r0, #0 @ anything to load? cfldr64eq mvdx0, [r1, #CRUNCH_MVDX0] @ mvdx0 was clobbered - moveq pc, lr + beq 1f crunch_load: cfldr64 mvdx0, [r0, #CRUNCH_DSPSC] @ load status word @@ -190,6 +193,11 @@ crunch_load: cfldr64 mvdx14, [r0, #CRUNCH_MVDX14] cfldr64 mvdx15, [r0, #CRUNCH_MVDX15] +1: +#ifdef CONFIG_PREEMPT_COUNT + get_thread_info r10 +#endif +2: dec_preempt_count r10, r3 mov pc, lr /* -- cgit v1.2.3 From 1417a6b8dc4db73055be9a3aa288b050e9dc06ab Mon Sep 17 00:00:00 2001 From: Catalin Marinas Date: Tue, 22 Apr 2014 16:14:29 +0100 Subject: ARM: 8036/1: Enable IRQs before attempting to read user space in __und_usr The Undef abort handler in the kernel reads the undefined instruction from user space. If the page table was modified from another CPU, the user access could fail and do_page_fault() will be executed with interrupts disabled. This can potentially deadlock on ARM11MPCore or on Cortex-A15 with erratum 798181 workaround enabled (both implying IPI for TLB maintenance with page table lock held). This patch enables the IRQs in __und_usr before attempting to read the instruction from user space. Signed-off-by: Catalin Marinas Tested-by: Arun KS Cc: Hartley Sweeten Cc: Ryan Mallon Signed-off-by: Russell King --- arch/arm/kernel/entry-armv.S | 11 +++++++---- arch/arm/kernel/iwmmxt.S | 2 +- arch/arm/mach-ep93xx/crunch-bits.S | 2 +- arch/arm/vfp/entry.S | 3 +-- 4 files changed, 10 insertions(+), 8 deletions(-) diff --git a/arch/arm/kernel/entry-armv.S b/arch/arm/kernel/entry-armv.S index 1879e8dd2acc..5fc897cf409b 100644 --- a/arch/arm/kernel/entry-armv.S +++ b/arch/arm/kernel/entry-armv.S @@ -413,6 +413,11 @@ __und_usr: @ adr r9, BSYM(ret_from_exception) + @ IRQs must be enabled before attempting to read the instruction from + @ user space since that could cause a page/translation fault if the + @ page table was modified by another CPU. + enable_irq + tst r3, #PSR_T_BIT @ Thumb mode? bne __und_usr_thumb sub r4, r2, #4 @ ARM instr at LR - 4 @@ -517,7 +522,7 @@ ENDPROC(__und_usr) * r9 = normal "successful" return address * r10 = this threads thread_info structure * lr = unrecognised instruction return address - * IRQs disabled, FIQs enabled. + * IRQs enabled, FIQs enabled. */ @ @ Fall-through from Thumb-2 __und_usr @@ -624,7 +629,6 @@ call_fpe: #endif do_fpe: - enable_irq ldr r4, .LCfp add r10, r10, #TI_FPSTATE @ r10 = workspace ldr pc, [r4] @ Call FP module USR entry point @@ -652,8 +656,7 @@ __und_usr_fault_32: b 1f __und_usr_fault_16: mov r1, #2 -1: enable_irq - mov r0, sp +1: mov r0, sp adr lr, BSYM(ret_from_exception) b __und_fault ENDPROC(__und_usr_fault_32) diff --git a/arch/arm/kernel/iwmmxt.S b/arch/arm/kernel/iwmmxt.S index fcb33a70a35f..4bb029ea8917 100644 --- a/arch/arm/kernel/iwmmxt.S +++ b/arch/arm/kernel/iwmmxt.S @@ -62,7 +62,7 @@ * r9 = ret_from_exception * lr = undefined instr exit * - * called from prefetch exception handler with interrupts disabled + * called from prefetch exception handler with interrupts enabled */ ENTRY(iwmmxt_task_enable) diff --git a/arch/arm/mach-ep93xx/crunch-bits.S b/arch/arm/mach-ep93xx/crunch-bits.S index eaa5e34729d3..e96923a3017b 100644 --- a/arch/arm/mach-ep93xx/crunch-bits.S +++ b/arch/arm/mach-ep93xx/crunch-bits.S @@ -63,7 +63,7 @@ * r9 = ret_from_exception * lr = undefined instr exit * - * called from prefetch exception handler with interrupts disabled + * called from prefetch exception handler with interrupts enabled */ ENTRY(crunch_task_enable) inc_preempt_count r10, r3 diff --git a/arch/arm/vfp/entry.S b/arch/arm/vfp/entry.S index f0759e70fb86..fe6ca574d093 100644 --- a/arch/arm/vfp/entry.S +++ b/arch/arm/vfp/entry.S @@ -22,11 +22,10 @@ @ r9 = normal "successful" return address @ r10 = this threads thread_info structure @ lr = unrecognised instruction return address -@ IRQs disabled. +@ IRQs enabled. @ ENTRY(do_vfp) inc_preempt_count r10, r4 - enable_irq ldr r4, .LCvfp ldr r11, [r10, #TI_CPU] @ CPU number add r10, r10, #TI_VFPSTATE @ r10 = workspace -- cgit v1.2.3 From 86f40622af7329375e38f282f6c0aab95f3e5f72 Mon Sep 17 00:00:00 2001 From: Jianguo Wu Date: Thu, 24 Apr 2014 03:45:56 +0100 Subject: ARM: 8037/1: mm: support big-endian page tables When enable LPAE and big-endian in a hisilicon board, while specify mem=384M mem=512M@7680M, will get bad page state: Freeing unused kernel memory: 180K (c0466000 - c0493000) BUG: Bad page state in process init pfn:fa442 page:c7749840 count:0 mapcount:-1 mapping: (null) index:0x0 page flags: 0x40000400(reserved) Modules linked in: CPU: 0 PID: 1 Comm: init Not tainted 3.10.27+ #66 [] (unwind_backtrace+0x0/0x11c) from [] (show_stack+0x10/0x14) [] (show_stack+0x10/0x14) from [] (bad_page+0xd4/0x104) [] (bad_page+0xd4/0x104) from [] (free_pages_prepare+0xa8/0x14c) [] (free_pages_prepare+0xa8/0x14c) from [] (free_hot_cold_page+0x18/0xf0) [] (free_hot_cold_page+0x18/0xf0) from [] (handle_pte_fault+0xcf4/0xdc8) [] (handle_pte_fault+0xcf4/0xdc8) from [] (handle_mm_fault+0xf4/0x120) [] (handle_mm_fault+0xf4/0x120) from [] (do_page_fault+0xfc/0x354) [] (do_page_fault+0xfc/0x354) from [] (do_DataAbort+0x2c/0x90) [] (do_DataAbort+0x2c/0x90) from [] (__dabt_usr+0x34/0x40) The bad pfn:fa442 is not system memory(mem=384M mem=512M@7680M), after debugging, I find in page fault handler, will get wrong pfn from pte just after set pte, as follow: do_anonymous_page() { ... set_pte_at(mm, address, page_table, entry); //debug code pfn = pte_pfn(entry); pr_info("pfn:0x%lx, pte:0x%llxn", pfn, pte_val(entry)); //read out the pte just set new_pte = pte_offset_map(pmd, address); new_pfn = pte_pfn(*new_pte); pr_info("new pfn:0x%lx, new pte:0x%llxn", pfn, pte_val(entry)); ... } pfn: 0x1fa4f5, pte:0xc00001fa4f575f new_pfn:0xfa4f5, new_pte:0xc00000fa4f5f5f //new pfn/pte is wrong. The bug is happened in cpu_v7_set_pte_ext(ptep, pte): An LPAE PTE is a 64bit quantity, passed to cpu_v7_set_pte_ext in the r2 and r3 registers. On an LE kernel, r2 contains the LSB of the PTE, and r3 the MSB. On a BE kernel, the assignment is reversed. Unfortunately, the current code always assumes the LE case, leading to corruption of the PTE when clearing/setting bits. This patch fixes this issue much like it has been done already in the cpu_v7_switch_mm case. CC stable Signed-off-by: Jianguo Wu Acked-by: Marc Zyngier Acked-by: Will Deacon Signed-off-by: Russell King --- arch/arm/mm/proc-v7-3level.S | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/arch/arm/mm/proc-v7-3level.S b/arch/arm/mm/proc-v7-3level.S index 01a719e18bb0..22e3ad63500c 100644 --- a/arch/arm/mm/proc-v7-3level.S +++ b/arch/arm/mm/proc-v7-3level.S @@ -64,6 +64,14 @@ ENTRY(cpu_v7_switch_mm) mov pc, lr ENDPROC(cpu_v7_switch_mm) +#ifdef __ARMEB__ +#define rl r3 +#define rh r2 +#else +#define rl r2 +#define rh r3 +#endif + /* * cpu_v7_set_pte_ext(ptep, pte) * @@ -73,13 +81,13 @@ ENDPROC(cpu_v7_switch_mm) */ ENTRY(cpu_v7_set_pte_ext) #ifdef CONFIG_MMU - tst r2, #L_PTE_VALID + tst rl, #L_PTE_VALID beq 1f - tst r3, #1 << (57 - 32) @ L_PTE_NONE - bicne r2, #L_PTE_VALID + tst rh, #1 << (57 - 32) @ L_PTE_NONE + bicne rl, #L_PTE_VALID bne 1f - tst r3, #1 << (55 - 32) @ L_PTE_DIRTY - orreq r2, #L_PTE_RDONLY + tst rh, #1 << (55 - 32) @ L_PTE_DIRTY + orreq rl, #L_PTE_RDONLY 1: strd r2, r3, [r0] ALT_SMP(W(nop)) ALT_UP (mcr p15, 0, r0, c7, c10, 1) @ flush_pte -- cgit v1.2.3 From 8febcaa2aac184d7e729acb75e9c4b80b04ad1b9 Mon Sep 17 00:00:00 2001 From: Santosh Shilimkar Date: Thu, 24 Apr 2014 11:30:01 -0400 Subject: device: introduce per device dma_pfn_offset On few architectures, there are few restrictions on DMAble area of system RAM. That also means that devices needs to know about this restrictions so that the dma_masks can be updated accordingly and dma address translation helpers can add/subtract the dma offset. In most of cases DMA addresses can be performed using offset value of Bus address space relatively to physical address space as following: PFN->DMA: __pfn_to_phys(pfn + [-]dma_pfn_offset) DMA->PFN: __phys_to_pfn(dma_addr) + [-]dma_pfn_offset So we introduce per device dma_pfn_offset which can be popullated by architecture init code while creating the devices. Cc: Greg Kroah-Hartman Cc: Russell King Cc: Arnd Bergmann Cc: Olof Johansson Cc: Grant Likely Cc: Catalin Marinas Cc: Linus Walleij Reviewed-by: Rob Herring Signed-off-by: Grygorii Strashko Signed-off-by: Santosh Shilimkar --- include/linux/device.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/include/linux/device.h b/include/linux/device.h index 233bbbeb768d..85a52d698f78 100644 --- a/include/linux/device.h +++ b/include/linux/device.h @@ -691,6 +691,7 @@ struct acpi_dev_node { * @coherent_dma_mask: Like dma_mask, but for alloc_coherent mapping as not all * hardware supports 64-bit addresses for consistent allocations * such descriptors. + * @dma_pfn_offset: offset of DMA memory range relatively of RAM * @dma_parms: A low level driver may set these to teach IOMMU code about * segment limitations. * @dma_pools: Dma pools (if dma'ble device). @@ -756,6 +757,7 @@ struct device { not all hardware supports 64 bit addresses for consistent allocations such descriptors. */ + unsigned long dma_pfn_offset; struct device_dma_parameters *dma_parms; -- cgit v1.2.3 From 18308c94723e162ed121942335bc186e66820a7a Mon Sep 17 00:00:00 2001 From: Grygorii Strashko Date: Thu, 24 Apr 2014 11:30:02 -0400 Subject: of: introduce of_dma_get_range() helper The of_dma_get_range() allows to find "dma-range" property for the specified device and parse it. dma-ranges format: DMA addr (dma_addr) : naddr cells CPU addr (phys_addr_t) : pna cells size : nsize cells Cc: Greg Kroah-Hartman Cc: Russell King Cc: Arnd Bergmann Cc: Olof Johansson Cc: Grant Likely Cc: Catalin Marinas Cc: Linus Walleij Reviewed-by: Rob Herring Signed-off-by: Grygorii Strashko Signed-off-by: Santosh Shilimkar --- drivers/of/address.c | 87 ++++++++++++++++++++++++++++++++++++++++++++++ include/linux/of_address.h | 8 +++++ 2 files changed, 95 insertions(+) diff --git a/drivers/of/address.c b/drivers/of/address.c index cb4242a69cd5..c54baee87d93 100644 --- a/drivers/of/address.c +++ b/drivers/of/address.c @@ -721,3 +721,90 @@ void __iomem *of_iomap(struct device_node *np, int index) return ioremap(res.start, resource_size(&res)); } EXPORT_SYMBOL(of_iomap); + +/** + * of_dma_get_range - Get DMA range info + * @np: device node to get DMA range info + * @dma_addr: pointer to store initial DMA address of DMA range + * @paddr: pointer to store initial CPU address of DMA range + * @size: pointer to store size of DMA range + * + * Look in bottom up direction for the first "dma-ranges" property + * and parse it. + * dma-ranges format: + * DMA addr (dma_addr) : naddr cells + * CPU addr (phys_addr_t) : pna cells + * size : nsize cells + * + * It returns -ENODEV if "dma-ranges" property was not found + * for this device in DT. + */ +int of_dma_get_range(struct device_node *np, u64 *dma_addr, u64 *paddr, u64 *size) +{ + struct device_node *node = of_node_get(np); + const __be32 *ranges = NULL; + int len, naddr, nsize, pna; + int ret = 0; + u64 dmaaddr; + + if (!node) + return -EINVAL; + + while (1) { + naddr = of_n_addr_cells(node); + nsize = of_n_size_cells(node); + node = of_get_next_parent(node); + if (!node) + break; + + ranges = of_get_property(node, "dma-ranges", &len); + + /* Ignore empty ranges, they imply no translation required */ + if (ranges && len > 0) + break; + + /* + * At least empty ranges has to be defined for parent node if + * DMA is supported + */ + if (!ranges) + break; + } + + if (!ranges) { + pr_debug("%s: no dma-ranges found for node(%s)\n", + __func__, np->full_name); + ret = -ENODEV; + goto out; + } + + len /= sizeof(u32); + + pna = of_n_addr_cells(node); + + /* dma-ranges format: + * DMA addr : naddr cells + * CPU addr : pna cells + * size : nsize cells + */ + dmaaddr = of_read_number(ranges, naddr); + *paddr = of_translate_dma_address(np, ranges); + if (*paddr == OF_BAD_ADDR) { + pr_err("%s: translation of DMA address(%pad) to CPU address failed node(%s)\n", + __func__, dma_addr, np->full_name); + ret = -EINVAL; + goto out; + } + *dma_addr = dmaaddr; + + *size = of_read_number(ranges + naddr + pna, nsize); + + pr_debug("dma_addr(%llx) cpu_addr(%llx) size(%llx)\n", + *dma_addr, *paddr, *size); + +out: + of_node_put(node); + + return ret; +} +EXPORT_SYMBOL_GPL(of_dma_get_range); diff --git a/include/linux/of_address.h b/include/linux/of_address.h index 5f6ed6b182b8..4d7b325af2ca 100644 --- a/include/linux/of_address.h +++ b/include/linux/of_address.h @@ -63,6 +63,8 @@ extern int of_pci_range_parser_init(struct of_pci_range_parser *parser, extern struct of_pci_range *of_pci_range_parser_one( struct of_pci_range_parser *parser, struct of_pci_range *range); +extern int of_dma_get_range(struct device_node *np, u64 *dma_addr, + u64 *paddr, u64 *size); #else /* CONFIG_OF_ADDRESS */ static inline struct device_node *of_find_matching_node_by_address( struct device_node *from, @@ -90,6 +92,12 @@ static inline struct of_pci_range *of_pci_range_parser_one( { return NULL; } + +static inline int of_dma_get_range(struct device_node *np, u64 *dma_addr, + u64 *paddr, u64 *size) +{ + return -ENODEV; +} #endif /* CONFIG_OF_ADDRESS */ #ifdef CONFIG_OF -- cgit v1.2.3 From 92ea637edea36e58236e3124f199161da6f5c5de Mon Sep 17 00:00:00 2001 From: Santosh Shilimkar Date: Thu, 24 Apr 2014 11:30:03 -0400 Subject: of: introduce of_dma_is_coherent() helper The of_dma_is_coherent() helper parses the given DT device node to see if the "dma-coherent" property is supported and returns true or false accordingly. If the arch is always coherent or always noncoherent, then the default DMA ops has to be specified accordingly. Cc: Greg Kroah-Hartman Cc: Russell King Cc: Arnd Bergmann Cc: Olof Johansson Cc: Grant Likely Cc: Catalin Marinas Cc: Linus Walleij Reviewed-by: Rob Herring Signed-off-by: Santosh Shilimkar Signed-off-by: Grygorii Strashko --- drivers/of/address.c | 23 +++++++++++++++++++++++ include/linux/of_address.h | 6 ++++++ 2 files changed, 29 insertions(+) diff --git a/drivers/of/address.c b/drivers/of/address.c index c54baee87d93..d244b2859aac 100644 --- a/drivers/of/address.c +++ b/drivers/of/address.c @@ -808,3 +808,26 @@ out: return ret; } EXPORT_SYMBOL_GPL(of_dma_get_range); + +/** + * of_dma_is_coherent - Check if device is coherent + * @np: device node + * + * It returns true if "dma-coherent" property was found + * for this device in DT. + */ +bool of_dma_is_coherent(struct device_node *np) +{ + struct device_node *node = of_node_get(np); + + while (node) { + if (of_property_read_bool(node, "dma-coherent")) { + of_node_put(node); + return true; + } + node = of_get_next_parent(node); + } + of_node_put(node); + return false; +} +EXPORT_SYMBOL_GPL(of_dma_is_coherent); \ No newline at end of file diff --git a/include/linux/of_address.h b/include/linux/of_address.h index 4d7b325af2ca..839a3521b28e 100644 --- a/include/linux/of_address.h +++ b/include/linux/of_address.h @@ -65,6 +65,7 @@ extern struct of_pci_range *of_pci_range_parser_one( struct of_pci_range *range); extern int of_dma_get_range(struct device_node *np, u64 *dma_addr, u64 *paddr, u64 *size); +extern bool of_dma_is_coherent(struct device_node *np); #else /* CONFIG_OF_ADDRESS */ static inline struct device_node *of_find_matching_node_by_address( struct device_node *from, @@ -98,6 +99,11 @@ static inline int of_dma_get_range(struct device_node *np, u64 *dma_addr, { return -ENODEV; } + +static inline bool of_dma_is_coherent(struct device_node *np) +{ + return false; +} #endif /* CONFIG_OF_ADDRESS */ #ifdef CONFIG_OF -- cgit v1.2.3 From 591c1ee465ce5372385dbc41e7d3e36cbb477bd8 Mon Sep 17 00:00:00 2001 From: Santosh Shilimkar Date: Thu, 24 Apr 2014 11:30:04 -0400 Subject: of: configure the platform device dma parameters Retrieve DMA configuration from DT and setup platform device's DMA parameters. The DMA configuration in DT has to be specified using "dma-ranges" and "dma-coherent" properties if supported. We setup dma_pfn_offset using "dma-ranges" and dma_coherent_ops using "dma-coherent" device tree properties. The set_arch_dma_coherent_ops macro has to be defined by arch if it supports coherent dma_ops. Otherwise, set_arch_dma_coherent_ops() is declared as nop. Cc: Greg Kroah-Hartman Cc: Russell King Cc: Arnd Bergmann Cc: Olof Johansson Cc: Grant Likely Cc: Catalin Marinas Cc: Linus Walleij Reviewed-by: Rob Herring Signed-off-by: Grygorii Strashko Signed-off-by: Santosh Shilimkar --- drivers/of/platform.c | 65 ++++++++++++++++++++++++++++++++++++++++----- include/linux/dma-mapping.h | 7 +++++ 2 files changed, 66 insertions(+), 6 deletions(-) diff --git a/drivers/of/platform.c b/drivers/of/platform.c index 404d1daebefa..91fa9838b56f 100644 --- a/drivers/of/platform.c +++ b/drivers/of/platform.c @@ -186,6 +186,64 @@ struct platform_device *of_device_alloc(struct device_node *np, } EXPORT_SYMBOL(of_device_alloc); +/** + * of_dma_configure - Setup DMA configuration + * @dev: Device to apply DMA configuration + * + * Try to get devices's DMA configuration from DT and update it + * accordingly. + * + * In case if platform code need to use own special DMA configuration,it + * can use Platform bus notifier and handle BUS_NOTIFY_ADD_DEVICE event + * to fix up DMA configuration. + */ +static void of_dma_configure(struct platform_device *pdev) +{ + u64 dma_addr, paddr, size; + int ret; + struct device *dev = &pdev->dev; + +#if defined(CONFIG_MICROBLAZE) + pdev->archdata.dma_mask = 0xffffffffUL; +#endif + + /* + * Set default dma-mask to 32 bit. Drivers are expected to setup + * the correct supported dma_mask. + */ + dev->coherent_dma_mask = DMA_BIT_MASK(32); + + /* + * Set it to coherent_dma_mask by default if the architecture + * code has not set it. + */ + if (!dev->dma_mask) + dev->dma_mask = &dev->coherent_dma_mask; + + /* + * if dma-coherent property exist, call arch hook to setup + * dma coherent operations. + */ + if (of_dma_is_coherent(dev->of_node)) { + set_arch_dma_coherent_ops(dev); + dev_dbg(dev, "device is dma coherent\n"); + } + + /* + * if dma-ranges property doesn't exist - just return else + * setup the dma offset + */ + ret = of_dma_get_range(dev->of_node, &dma_addr, &paddr, &size); + if (ret < 0) { + dev_dbg(dev, "no dma range information to setup\n"); + return; + } + + /* DMA ranges found. Calculate and set dma_pfn_offset */ + dev->dma_pfn_offset = PFN_DOWN(paddr - dma_addr); + dev_dbg(dev, "dma_pfn_offset(%#08lx)\n", dev->dma_pfn_offset); +} + /** * of_platform_device_create_pdata - Alloc, initialize and register an of_device * @np: pointer to node to create device for @@ -211,12 +269,7 @@ static struct platform_device *of_platform_device_create_pdata( if (!dev) return NULL; -#if defined(CONFIG_MICROBLAZE) - dev->archdata.dma_mask = 0xffffffffUL; -#endif - dev->dev.coherent_dma_mask = DMA_BIT_MASK(32); - if (!dev->dev.dma_mask) - dev->dev.dma_mask = &dev->dev.coherent_dma_mask; + of_dma_configure(dev); dev->dev.bus = &platform_bus_type; dev->dev.platform_data = platform_data; diff --git a/include/linux/dma-mapping.h b/include/linux/dma-mapping.h index fd4aee29ad10..c7d9b1b14ce7 100644 --- a/include/linux/dma-mapping.h +++ b/include/linux/dma-mapping.h @@ -123,6 +123,13 @@ static inline int dma_coerce_mask_and_coherent(struct device *dev, u64 mask) extern u64 dma_get_required_mask(struct device *dev); +#ifndef set_arch_dma_coherent_ops +static inline int set_arch_dma_coherent_ops(struct device *dev) +{ + return 0; +} +#endif + static inline unsigned int dma_get_max_seg_size(struct device *dev) { return dev->dma_parms ? dev->dma_parms->max_segment_size : 65536; -- cgit v1.2.3 From 6ce0d20016925d031f1e24d64302e4c976d7cec6 Mon Sep 17 00:00:00 2001 From: Grygorii Strashko Date: Thu, 24 Apr 2014 11:30:05 -0400 Subject: ARM: dma: Use dma_pfn_offset for dma address translation In most of cases DMA addresses can be performed using offset value of Bus address space relatively to physical address space as following: PFN->DMA: __pfn_to_phys(pfn + [-]dma_pfn_offset) DMA->PFN: __phys_to_pfn(dma_addr) + [-]dma_pfn_offset Thanks to Russell King for suggesting the optimised macro's for conversion. Cc: Greg Kroah-Hartman Cc: Russell King Cc: Arnd Bergmann Cc: Olof Johansson Cc: Grant Likely Cc: Catalin Marinas Cc: Linus Walleij Reviewed-by: Rob Herring Signed-off-by: Grygorii Strashko Signed-off-by: Santosh Shilimkar --- arch/arm/include/asm/dma-mapping.h | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/arch/arm/include/asm/dma-mapping.h b/arch/arm/include/asm/dma-mapping.h index e701a4d9aa59..b0c79fdd9375 100644 --- a/arch/arm/include/asm/dma-mapping.h +++ b/arch/arm/include/asm/dma-mapping.h @@ -58,21 +58,37 @@ static inline int dma_set_mask(struct device *dev, u64 mask) #ifndef __arch_pfn_to_dma static inline dma_addr_t pfn_to_dma(struct device *dev, unsigned long pfn) { + if (dev) + pfn -= dev->dma_pfn_offset; return (dma_addr_t)__pfn_to_bus(pfn); } static inline unsigned long dma_to_pfn(struct device *dev, dma_addr_t addr) { - return __bus_to_pfn(addr); + unsigned long pfn = __bus_to_pfn(addr); + + if (dev) + pfn += dev->dma_pfn_offset; + + return pfn; } static inline void *dma_to_virt(struct device *dev, dma_addr_t addr) { + if (dev) { + unsigned long pfn = dma_to_pfn(dev, addr); + + return phys_to_virt(__pfn_to_phys(pfn)); + } + return (void *)__bus_to_virt((unsigned long)addr); } static inline dma_addr_t virt_to_dma(struct device *dev, void *addr) { + if (dev) + return pfn_to_dma(dev, virt_to_pfn(addr)); + return (dma_addr_t)__virt_to_bus((unsigned long)(addr)); } -- cgit v1.2.3 From 812b99e4b024d6f83e9281ec1a0bd4bf63dad90f Mon Sep 17 00:00:00 2001 From: Santosh Shilimkar Date: Thu, 24 Apr 2014 11:30:06 -0400 Subject: ARM: dma: implement set_arch_dma_coherent_ops() Implement the set_arch_dma_coherent_ops() for ARM architecture. Cc: Greg Kroah-Hartman Cc: Russell King Cc: Arnd Bergmann Cc: Olof Johansson Cc: Grant Likely Cc: Catalin Marinas Cc: Linus Walleij Reviewed-by: Rob Herring Signed-off-by: Grygorii Strashko Signed-off-by: Santosh Shilimkar --- arch/arm/include/asm/dma-mapping.h | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/arch/arm/include/asm/dma-mapping.h b/arch/arm/include/asm/dma-mapping.h index b0c79fdd9375..c45b61a4b4a5 100644 --- a/arch/arm/include/asm/dma-mapping.h +++ b/arch/arm/include/asm/dma-mapping.h @@ -121,6 +121,13 @@ static inline unsigned long dma_max_pfn(struct device *dev) } #define dma_max_pfn(dev) dma_max_pfn(dev) +static inline int set_arch_dma_coherent_ops(struct device *dev) +{ + set_dma_ops(dev, &arm_coherent_dma_ops); + return 0; +} +#define set_arch_dma_coherent_ops(dev) set_arch_dma_coherent_ops(dev) + static inline dma_addr_t phys_to_dma(struct device *dev, phys_addr_t paddr) { unsigned int offset = paddr & ~PAGE_MASK; -- cgit v1.2.3 From 2161c2485d03520060f6094359b22f33913eefa2 Mon Sep 17 00:00:00 2001 From: Santosh Shilimkar Date: Thu, 24 Apr 2014 11:30:07 -0400 Subject: ARM: dma: use phys_addr_t in __dma_page_[cpu_to_dev/dev_to_cpu] On a 32 bit ARM architecture with LPAE extension physical addresses cannot fit into unsigned long variable. So fix it by using phys_addr_t instead of unsigned long. Cc: Nicolas Pitre Cc: Russell King - ARM Linux Cc: Catalin Marinas Acked-by: Will Deacon Signed-off-by: Santosh Shilimkar --- arch/arm/mm/dma-mapping.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/arch/arm/mm/dma-mapping.c b/arch/arm/mm/dma-mapping.c index f62aa0677e5c..5260f43c3d03 100644 --- a/arch/arm/mm/dma-mapping.c +++ b/arch/arm/mm/dma-mapping.c @@ -885,7 +885,7 @@ static void dma_cache_maint_page(struct page *page, unsigned long offset, static void __dma_page_cpu_to_dev(struct page *page, unsigned long off, size_t size, enum dma_data_direction dir) { - unsigned long paddr; + phys_addr_t paddr; dma_cache_maint_page(page, off, size, dir, dmac_map_area); @@ -901,7 +901,7 @@ static void __dma_page_cpu_to_dev(struct page *page, unsigned long off, static void __dma_page_dev_to_cpu(struct page *page, unsigned long off, size_t size, enum dma_data_direction dir) { - unsigned long paddr = page_to_phys(page) + off; + phys_addr_t paddr = page_to_phys(page) + off; /* FIXME: non-speculating: not required */ /* don't bother invalidating if DMA to device */ -- cgit v1.2.3 From 265c271c822bd57677e3b286389487fd45e6960d Mon Sep 17 00:00:00 2001 From: Russell King Date: Sat, 15 Mar 2014 16:47:44 +0000 Subject: ARM: l2c: remove outer_inv_all() method No one ever calls this function anywhere in the kernel, so let's completely remove it from the outer cache API and turn it into an internal-only thing. Signed-off-by: Russell King --- arch/arm/include/asm/outercache.h | 8 -------- arch/arm/mm/cache-feroceon-l2.c | 1 - arch/arm/mm/cache-l2x0.c | 5 ----- 3 files changed, 14 deletions(-) diff --git a/arch/arm/include/asm/outercache.h b/arch/arm/include/asm/outercache.h index f94784f0e3a6..0e4420858990 100644 --- a/arch/arm/include/asm/outercache.h +++ b/arch/arm/include/asm/outercache.h @@ -28,7 +28,6 @@ struct outer_cache_fns { void (*clean_range)(unsigned long, unsigned long); void (*flush_range)(unsigned long, unsigned long); void (*flush_all)(void); - void (*inv_all)(void); void (*disable)(void); #ifdef CONFIG_OUTER_CACHE_SYNC void (*sync)(void); @@ -63,12 +62,6 @@ static inline void outer_flush_all(void) outer_cache.flush_all(); } -static inline void outer_inv_all(void) -{ - if (outer_cache.inv_all) - outer_cache.inv_all(); -} - static inline void outer_disable(void) { if (outer_cache.disable) @@ -90,7 +83,6 @@ static inline void outer_clean_range(phys_addr_t start, phys_addr_t end) static inline void outer_flush_range(phys_addr_t start, phys_addr_t end) { } static inline void outer_flush_all(void) { } -static inline void outer_inv_all(void) { } static inline void outer_disable(void) { } static inline void outer_resume(void) { } diff --git a/arch/arm/mm/cache-feroceon-l2.c b/arch/arm/mm/cache-feroceon-l2.c index dc814a548056..e028a7f2ebcc 100644 --- a/arch/arm/mm/cache-feroceon-l2.c +++ b/arch/arm/mm/cache-feroceon-l2.c @@ -350,7 +350,6 @@ void __init feroceon_l2_init(int __l2_wt_override) outer_cache.inv_range = feroceon_l2_inv_range; outer_cache.clean_range = feroceon_l2_clean_range; outer_cache.flush_range = feroceon_l2_flush_range; - outer_cache.inv_all = l2_inv_all; enable_l2(); diff --git a/arch/arm/mm/cache-l2x0.c b/arch/arm/mm/cache-l2x0.c index 7abde2ce8973..f9985e5a208c 100644 --- a/arch/arm/mm/cache-l2x0.c +++ b/arch/arm/mm/cache-l2x0.c @@ -414,7 +414,6 @@ void __init l2x0_init(void __iomem *base, u32 aux_val, u32 aux_mask) outer_cache.flush_range = l2x0_flush_range; outer_cache.sync = l2x0_cache_sync; outer_cache.flush_all = l2x0_flush_all; - outer_cache.inv_all = l2x0_inv_all; outer_cache.disable = l2x0_disable; } @@ -884,7 +883,6 @@ static const struct l2x0_of_data pl310_data = { .flush_range = l2x0_flush_range, .sync = l2x0_cache_sync, .flush_all = l2x0_flush_all, - .inv_all = l2x0_inv_all, .disable = l2x0_disable, }, }; @@ -899,7 +897,6 @@ static const struct l2x0_of_data l2x0_data = { .flush_range = l2x0_flush_range, .sync = l2x0_cache_sync, .flush_all = l2x0_flush_all, - .inv_all = l2x0_inv_all, .disable = l2x0_disable, }, }; @@ -914,7 +911,6 @@ static const struct l2x0_of_data aurora_with_outer_data = { .flush_range = aurora_flush_range, .sync = l2x0_cache_sync, .flush_all = l2x0_flush_all, - .inv_all = l2x0_inv_all, .disable = l2x0_disable, }, }; @@ -946,7 +942,6 @@ static const struct l2x0_of_data bcm_l2x0_data = { .flush_range = bcm_flush_range, .sync = l2x0_cache_sync, .flush_all = l2x0_flush_all, - .inv_all = l2x0_inv_all, .disable = l2x0_disable, }, }; -- cgit v1.2.3 From 7668fd577bae5bba50e97d257a4cf0c44ca002d3 Mon Sep 17 00:00:00 2001 From: Russell King Date: Sun, 13 Apr 2014 20:09:55 +0100 Subject: ARM: make get_cr()/set_cr() use unsigned long values Signed-off-by: Russell King --- arch/arm/include/asm/cp15.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/arch/arm/include/asm/cp15.h b/arch/arm/include/asm/cp15.h index 6493802f880a..d5bf322a0630 100644 --- a/arch/arm/include/asm/cp15.h +++ b/arch/arm/include/asm/cp15.h @@ -52,14 +52,14 @@ extern unsigned long cr_no_alignment; /* defined in entry-armv.S */ extern unsigned long cr_alignment; /* defined in entry-armv.S */ -static inline unsigned int get_cr(void) +static inline unsigned long get_cr(void) { - unsigned int val; + unsigned long val; asm("mrc p15, 0, %0, c1, c0, 0 @ get CR" : "=r" (val) : : "cc"); return val; } -static inline void set_cr(unsigned int val) +static inline void set_cr(unsigned long val) { asm volatile("mcr p15, 0, %0, c1, c0, 0 @ set CR" : : "r" (val) : "cc"); -- cgit v1.2.3 From 4585eaff634b1bbb09686895221b3645f53f7a60 Mon Sep 17 00:00:00 2001 From: Russell King Date: Sun, 13 Apr 2014 18:47:34 +0100 Subject: ARM: use get_cr() rather than cr_alignment Rather than reading the cr_alignment variable, use get_cr() to read directly from the hardware instead. We have two places where this occurs, neither of them are performance critical. Signed-off-by: Russell King --- arch/arm/include/asm/cp15.h | 7 ++++++- arch/arm/kernel/setup.c | 2 +- arch/arm/mm/alignment.c | 2 +- 3 files changed, 8 insertions(+), 3 deletions(-) diff --git a/arch/arm/include/asm/cp15.h b/arch/arm/include/asm/cp15.h index d5bf322a0630..63427266015d 100644 --- a/arch/arm/include/asm/cp15.h +++ b/arch/arm/include/asm/cp15.h @@ -42,7 +42,7 @@ #ifndef __ASSEMBLY__ #if __LINUX_ARM_ARCH__ >= 4 -#define vectors_high() (cr_alignment & CR_V) +#define vectors_high() (get_cr() & CR_V) #else #define vectors_high() (0) #endif @@ -113,6 +113,11 @@ static inline void set_copro_access(unsigned int val) #define cr_no_alignment UL(0) #define cr_alignment UL(0) +static inline unsigned long get_cr(void) +{ + return 0; +} + #endif /* ifdef CONFIG_CPU_CP15 / else */ #endif /* ifndef __ASSEMBLY__ */ diff --git a/arch/arm/kernel/setup.c b/arch/arm/kernel/setup.c index 50e198c1e9c8..df21f9f98945 100644 --- a/arch/arm/kernel/setup.c +++ b/arch/arm/kernel/setup.c @@ -590,7 +590,7 @@ static void __init setup_processor(void) pr_info("CPU: %s [%08x] revision %d (ARMv%s), cr=%08lx\n", cpu_name, read_cpuid_id(), read_cpuid_id() & 15, - proc_arch[cpu_architecture()], cr_alignment); + proc_arch[cpu_architecture()], get_cr()); snprintf(init_utsname()->machine, __NEW_UTS_LEN + 1, "%s%c", list->arch_name, ENDIANNESS); diff --git a/arch/arm/mm/alignment.c b/arch/arm/mm/alignment.c index 924036473b16..1ab611ce5009 100644 --- a/arch/arm/mm/alignment.c +++ b/arch/arm/mm/alignment.c @@ -91,7 +91,7 @@ core_param(alignment, ai_usermode, int, 0600); /* Return true if and only if the ARMv6 unaligned access model is in use. */ static bool cpu_is_v6_unaligned(void) { - return cpu_architecture() >= CPU_ARCH_ARMv6 && (cr_alignment & CR_U); + return cpu_architecture() >= CPU_ARCH_ARMv6 && get_cr() & CR_U; } static int safe_usermode(int new_usermode, bool warn) -- cgit v1.2.3 From deace4a6b440f2e05f3e073338b28901d02a15c9 Mon Sep 17 00:00:00 2001 From: Russell King Date: Sat, 3 May 2014 11:06:55 +0100 Subject: ARM: dma-mapping: avoid calling dma_cache_maint_page() on dev=>cpu Avoid calling dma_cache_maint_page() when unmapping a DMA_TO_DEVICE buffer. The L1 cache ops never do anything in this circumstance, nor do they ever need to - all that matters for this case is that the data written is visible to the device before DMA starts. What happens during the transfer (provided the buffer is not written to) is of no real consequence. We already do this optimisation for the L2 cache. Signed-off-by: Russell King --- arch/arm/mm/dma-mapping.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/arch/arm/mm/dma-mapping.c b/arch/arm/mm/dma-mapping.c index f62aa0677e5c..137463bcbeac 100644 --- a/arch/arm/mm/dma-mapping.c +++ b/arch/arm/mm/dma-mapping.c @@ -904,11 +904,12 @@ static void __dma_page_dev_to_cpu(struct page *page, unsigned long off, unsigned long paddr = page_to_phys(page) + off; /* FIXME: non-speculating: not required */ - /* don't bother invalidating if DMA to device */ - if (dir != DMA_TO_DEVICE) + /* in any case, don't bother invalidating if DMA to device */ + if (dir != DMA_TO_DEVICE) { outer_inv_range(paddr, paddr + size); - dma_cache_maint_page(page, off, size, dir, dmac_unmap_area); + dma_cache_maint_page(page, off, size, dir, dmac_unmap_area); + } /* * Mark the D-cache clean for these pages to avoid extra flushing. -- cgit v1.2.3 From 3683f44c42e991d313dc301504ee0fca1aeb8580 Mon Sep 17 00:00:00 2001 From: Russell King Date: Sat, 3 May 2014 11:03:28 +0100 Subject: ARM: stacktrace: avoid listing stacktrace functions in stacktrace While debugging the FEC ethernet driver using stacktrace, it was noticed that the stacktraces always begin as follows: [] save_stack_trace_tsk+0x0/0x98 [] save_stack_trace+0x24/0x28 ... This is because the stack trace code includes the stack frames for itself. This is incorrect behaviour, and also leads to "skip" doing the wrong thing (which is the number of stack frames to avoid recording.) Perversely, it does the right thing when passed a non-current thread. Fix this by ensuring that we have a known constant number of frames above the main stack trace function, and always skip these. Cc: Signed-off-by: Russell King --- arch/arm/kernel/stacktrace.c | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/arch/arm/kernel/stacktrace.c b/arch/arm/kernel/stacktrace.c index af4e8c8a5422..6582c4adc182 100644 --- a/arch/arm/kernel/stacktrace.c +++ b/arch/arm/kernel/stacktrace.c @@ -83,13 +83,16 @@ static int save_trace(struct stackframe *frame, void *d) return trace->nr_entries >= trace->max_entries; } -void save_stack_trace_tsk(struct task_struct *tsk, struct stack_trace *trace) +/* This must be noinline to so that our skip calculation works correctly */ +static noinline void __save_stack_trace(struct task_struct *tsk, + struct stack_trace *trace, unsigned int nosched) { struct stack_trace_data data; struct stackframe frame; data.trace = trace; data.skip = trace->skip; + data.no_sched_functions = nosched; if (tsk != current) { #ifdef CONFIG_SMP @@ -102,7 +105,6 @@ void save_stack_trace_tsk(struct task_struct *tsk, struct stack_trace *trace) trace->entries[trace->nr_entries++] = ULONG_MAX; return; #else - data.no_sched_functions = 1; frame.fp = thread_saved_fp(tsk); frame.sp = thread_saved_sp(tsk); frame.lr = 0; /* recovered from the stack */ @@ -111,11 +113,12 @@ void save_stack_trace_tsk(struct task_struct *tsk, struct stack_trace *trace) } else { register unsigned long current_sp asm ("sp"); - data.no_sched_functions = 0; + /* We don't want this function nor the caller */ + data.skip += 2; frame.fp = (unsigned long)__builtin_frame_address(0); frame.sp = current_sp; frame.lr = (unsigned long)__builtin_return_address(0); - frame.pc = (unsigned long)save_stack_trace_tsk; + frame.pc = (unsigned long)__save_stack_trace; } walk_stackframe(&frame, save_trace, &data); @@ -123,9 +126,14 @@ void save_stack_trace_tsk(struct task_struct *tsk, struct stack_trace *trace) trace->entries[trace->nr_entries++] = ULONG_MAX; } +void save_stack_trace_tsk(struct task_struct *tsk, struct stack_trace *trace) +{ + __save_stack_trace(tsk, trace, 1); +} + void save_stack_trace(struct stack_trace *trace) { - save_stack_trace_tsk(current, trace); + __save_stack_trace(current, trace, 0); } EXPORT_SYMBOL_GPL(save_stack_trace); #endif -- cgit v1.2.3 From 07b403415884e961920f55e6db462dff15d9df5a Mon Sep 17 00:00:00 2001 From: Russell King Date: Sat, 3 May 2014 16:17:16 +0100 Subject: ARM: stacktrace: include exception PC value in stacktrace output When we unwind through an exception stack, include the saved PC value into the stack trace: this fills in an otherwise missed functions from the trace (as indicated below): [] fec_enet_interrupt+0xa0/0xe8 [] handle_irq_event_percpu+0x68/0x228 [] handle_irq_event+0x4c/0x6c [] handle_fasteoi_irq+0xac/0x198 [] generic_handle_irq+0x4c/0x60 [] handle_IRQ+0x40/0x98 [] gic_handle_irq+0x30/0x64 [] __irq_svc+0x40/0x50 [] __do_softirq+0xe0/0x2fc <==== [] irq_exit+0xb0/0x100 [] handle_IRQ+0x44/0x98 [] gic_handle_irq+0x30/0x64 [] __irq_svc+0x40/0x50 [] arch_cpu_idle+0x30/0x38 <==== [] cpu_startup_entry+0xac/0x214 [] rest_init+0x68/0x80 [] start_kernel+0x2fc/0x358 Signed-off-by: Russell King --- arch/arm/kernel/stacktrace.