summaryrefslogtreecommitdiff
path: root/arch/microblaze/kernel
diff options
context:
space:
mode:
Diffstat (limited to 'arch/microblaze/kernel')
-rw-r--r--arch/microblaze/kernel/Makefile14
-rw-r--r--arch/microblaze/kernel/cpu/Makefile4
-rw-r--r--arch/microblaze/kernel/cpu/cache.c663
-rw-r--r--arch/microblaze/kernel/cpu/cpuinfo-pvr-full.c15
-rw-r--r--arch/microblaze/kernel/cpu/cpuinfo-static.c17
-rw-r--r--arch/microblaze/kernel/cpu/cpuinfo.c7
-rw-r--r--arch/microblaze/kernel/cpu/mb.c8
-rw-r--r--arch/microblaze/kernel/cpu/pvr.c2
-rw-r--r--arch/microblaze/kernel/entry-nommu.S2
-rw-r--r--arch/microblaze/kernel/entry.S19
-rw-r--r--arch/microblaze/kernel/ftrace.c237
-rw-r--r--arch/microblaze/kernel/heartbeat.c15
-rw-r--r--arch/microblaze/kernel/intc.c10
-rw-r--r--arch/microblaze/kernel/mcount.S170
-rw-r--r--arch/microblaze/kernel/microblaze_ksyms.c5
-rw-r--r--arch/microblaze/kernel/process.c1
-rw-r--r--arch/microblaze/kernel/reset.c140
-rw-r--r--arch/microblaze/kernel/setup.c40
-rw-r--r--arch/microblaze/kernel/signal.c35
-rw-r--r--arch/microblaze/kernel/stacktrace.c65
-rw-r--r--arch/microblaze/kernel/syscall_table.S4
-rw-r--r--arch/microblaze/kernel/timer.c28
-rw-r--r--arch/microblaze/kernel/vmlinux.lds.S6
23 files changed, 1238 insertions, 269 deletions
diff --git a/arch/microblaze/kernel/Makefile b/arch/microblaze/kernel/Makefile
index d487729683de..b07594eccf9b 100644
--- a/arch/microblaze/kernel/Makefile
+++ b/arch/microblaze/kernel/Makefile
@@ -2,12 +2,22 @@
# Makefile
#
+ifdef CONFIG_FUNCTION_TRACER
+# Do not trace early boot code and low level code
+CFLAGS_REMOVE_timer.o = -pg
+CFLAGS_REMOVE_intc.o = -pg
+CFLAGS_REMOVE_early_printk.o = -pg
+CFLAGS_REMOVE_selfmod.o = -pg
+CFLAGS_REMOVE_heartbeat.o = -pg
+CFLAGS_REMOVE_ftrace.o = -pg
+endif
+
extra-y := head.o vmlinux.lds
obj-y += exceptions.o \
hw_exception_handler.o init_task.o intc.o irq.o of_device.o \
of_platform.o process.o prom.o prom_parse.o ptrace.o \
- setup.o signal.o sys_microblaze.o timer.o traps.o
+ setup.o signal.o sys_microblaze.o timer.o traps.o reset.o
obj-y += cpu/
@@ -16,5 +26,7 @@ obj-$(CONFIG_SELFMOD) += selfmod.o
obj-$(CONFIG_HEART_BEAT) += heartbeat.o
obj-$(CONFIG_MODULES) += microblaze_ksyms.o module.o
obj-$(CONFIG_MMU) += misc.o
+obj-$(CONFIG_STACKTRACE) += stacktrace.o
+obj-$(CONFIG_FUNCTION_TRACER) += ftrace.o mcount.o
obj-y += entry$(MMU).o
diff --git a/arch/microblaze/kernel/cpu/Makefile b/arch/microblaze/kernel/cpu/Makefile
index 20646e549271..59cc7bceaf8c 100644
--- a/arch/microblaze/kernel/cpu/Makefile
+++ b/arch/microblaze/kernel/cpu/Makefile
@@ -2,6 +2,10 @@
# Build the appropriate CPU version support
#
+ifdef CONFIG_FUNCTION_TRACER
+CFLAGS_REMOVE_cache.o = -pg
+endif
+
EXTRA_CFLAGS += -DCPU_MAJOR=$(CPU_MAJOR) -DCPU_MINOR=$(CPU_MINOR) \
-DCPU_REV=$(CPU_REV)
diff --git a/arch/microblaze/kernel/cpu/cache.c b/arch/microblaze/kernel/cpu/cache.c
index af866a450125..d9d63831cc2f 100644
--- a/arch/microblaze/kernel/cpu/cache.c
+++ b/arch/microblaze/kernel/cpu/cache.c
@@ -3,7 +3,7 @@
*
* Copyright (C) 2007-2009 Michal Simek <monstr@monstr.eu>
* Copyright (C) 2007-2009 PetaLogix
- * Copyright (C) 2007 John Williams <john.williams@petalogix.com>
+ * Copyright (C) 2007-2009 John Williams <john.williams@petalogix.com>
*
* This file is subject to the terms and conditions of the GNU General
* Public License. See the file COPYING in the main directory of this
@@ -13,243 +13,534 @@
#include <asm/cacheflush.h>
#include <linux/cache.h>
#include <asm/cpuinfo.h>
+#include <asm/pvr.h>
-/* Exported functions */
+static inline void __invalidate_flush_icache(unsigned int addr)
+{
+ __asm__ __volatile__ ("wic %0, r0;" \
+ : : "r" (addr));
+}
+
+static inline void __flush_dcache(unsigned int addr)
+{
+ __asm__ __volatile__ ("wdc.flush %0, r0;" \
+ : : "r" (addr));
+}
+
+static inline void __invalidate_dcache(unsigned int baseaddr,
+ unsigned int offset)
+{
+ __asm__ __volatile__ ("wdc.clear %0, %1;" \
+ : : "r" (baseaddr), "r" (offset));
+}
-void _enable_icache(void)
+static inline void __enable_icache_msr(void)
{
- if (cpuinfo.use_icache) {
-#if CONFIG_XILINX_MICROBLAZE0_USE_MSR_INSTR
- __asm__ __volatile__ (" \
- msrset r0, %0; \
- nop; " \
- : \
- : "i" (MSR_ICE) \
+ __asm__ __volatile__ (" msrset r0, %0; \
+ nop; " \
+ : : "i" (MSR_ICE) : "memory");
+}
+
+static inline void __disable_icache_msr(void)
+{
+ __asm__ __volatile__ (" msrclr r0, %0; \
+ nop; " \
+ : : "i" (MSR_ICE) : "memory");
+}
+
+static inline void __enable_dcache_msr(void)
+{
+ __asm__ __volatile__ (" msrset r0, %0; \
+ nop; " \
+ : \
+ : "i" (MSR_DCE) \
: "memory");
-#else
- __asm__ __volatile__ (" \
- mfs r12, rmsr; \
- nop; \
- ori r12, r12, %0; \
- mts rmsr, r12; \
- nop; " \
- : \
- : "i" (MSR_ICE) \
- : "memory", "r12");
-#endif
- }
}
-void _disable_icache(void)
+static inline void __disable_dcache_msr(void)
{
- if (cpuinfo.use_icache) {
-#if CONFIG_XILINX_MICROBLAZE0_USE_MSR_INSTR
- __asm__ __volatile__ (" \
- msrclr r0, %0; \
- nop; " \
- : \
- : "i" (MSR_ICE) \
+ __asm__ __volatile__ (" msrclr r0, %0; \
+ nop; " \
+ : \
+ : "i" (MSR_DCE) \
: "memory");
-#else
- __asm__ __volatile__ (" \
- mfs r12, rmsr; \
- nop; \
- andi r12, r12, ~%0; \
- mts rmsr, r12; \
- nop; " \
- : \
- : "i" (MSR_ICE) \
+}
+
+static inline void __enable_icache_nomsr(void)
+{
+ __asm__ __volatile__ (" mfs r12, rmsr; \
+ nop; \
+ ori r12, r12, %0; \
+ mts rmsr, r12; \
+ nop; " \
+ : \
+ : "i" (MSR_ICE) \
: "memory", "r12");
-#endif
- }
}
-void _invalidate_icache(unsigned int addr)
+static inline void __disable_icache_nomsr(void)
{
- if (cpuinfo.use_icache) {
- __asm__ __volatile__ (" \
- wic %0, r0" \
- : \
- : "r" (addr));
- }
+ __asm__ __volatile__ (" mfs r12, rmsr; \
+ nop; \
+ andi r12, r12, ~%0; \
+ mts rmsr, r12; \
+ nop; " \
+ : \
+ : "i" (MSR_ICE) \
+ : "memory", "r12");
}
-void _enable_dcache(void)
+static inline void __enable_dcache_nomsr(void)
{
- if (cpuinfo.use_dcache) {
-#if CONFIG_XILINX_MICROBLAZE0_USE_MSR_INSTR
- __asm__ __volatile__ (" \
- msrset r0, %0; \
- nop; " \
- : \
- : "i" (MSR_DCE) \
- : "memory");
-#else
- __asm__ __volatile__ (" \
- mfs r12, rmsr; \
- nop; \
- ori r12, r12, %0; \
- mts rmsr, r12; \
- nop; " \
- : \
- : "i" (MSR_DCE) \
+ __asm__ __volatile__ (" mfs r12, rmsr; \
+ nop; \
+ ori r12, r12, %0; \
+ mts rmsr, r12; \
+ nop; " \
+ : \
+ : "i" (MSR_DCE) \
: "memory", "r12");
-#endif
- }
}
-void _disable_dcache(void)
+static inline void __disable_dcache_nomsr(void)
{
-#if CONFIG_XILINX_MICROBLAZE0_USE_MSR_INSTR
- __asm__ __volatile__ (" \
- msrclr r0, %0; \
- nop; " \
- : \
- : "i" (MSR_DCE) \
- : "memory");
-#else
- __asm__ __volatile__ (" \
- mfs r12, rmsr; \
- nop; \
- andi r12, r12, ~%0; \
- mts rmsr, r12; \
- nop; " \
- : \
- : "i" (MSR_DCE) \
+ __asm__ __volatile__ (" mfs r12, rmsr; \
+ nop; \
+ andi r12, r12, ~%0; \
+ mts rmsr, r12; \
+ nop; " \
+ : \
+ : "i" (MSR_DCE) \
: "memory", "r12");
-#endif
}
-void _invalidate_dcache(unsigned int addr)
+
+/* Helper macro for computing the limits of cache range loops */
+#define CACHE_LOOP_LIMITS(start, end, cache_line_length, cache_size) \
+do { \
+ int align = ~(cache_line_length - 1); \
+ end = min(start + cache_size, end); \
+ start &= align; \
+ end = ((end & align) + cache_line_length); \
+} while (0);
+
+/*
+ * Helper macro to loop over the specified cache_size/line_length and
+ * execute 'op' on that cacheline
+ */
+#define CACHE_ALL_LOOP(cache_size, line_length, op) \
+do { \
+ unsigned int len = cache_size; \
+ int step = -line_length; \
+ BUG_ON(step >= 0); \
+ \
+ __asm__ __volatile__ (" 1: " #op " %0, r0; \
+ bgtid %0, 1b; \
+ addk %0, %0, %1; \
+ " : : "r" (len), "r" (step) \
+ : "memory"); \
+} while (0);
+
+
+#define CACHE_ALL_LOOP2(cache_size, line_length, op) \
+do { \
+ unsigned int len = cache_size; \
+ int step = -line_length; \
+ BUG_ON(step >= 0); \
+ \
+ __asm__ __volatile__ (" 1: " #op " r0, %0; \
+ bgtid %0, 1b; \
+ addk %0, %0, %1; \
+ " : : "r" (len), "r" (step) \
+ : "memory"); \
+} while (0);
+
+/* for wdc.flush/clear */
+#define CACHE_RANGE_LOOP_2(start, end, line_length, op) \
+do { \
+ int step = -line_length; \
+ int count = end - start; \
+ BUG_ON(count <= 0); \
+ \
+ __asm__ __volatile__ (" 1: " #op " %0, %1; \
+ bgtid %1, 1b; \
+ addk %1, %1, %2; \
+ " : : "r" (start), "r" (count), \
+ "r" (step) : "memory"); \
+} while (0);
+
+/* It is used only first parameter for OP - for wic, wdc */
+#define CACHE_RANGE_LOOP_1(start, end, line_length, op) \
+do { \
+ int step = -line_length; \
+ int count = end - start; \
+ BUG_ON(count <= 0); \
+ \
+ __asm__ __volatile__ (" 1: addk %0, %0, %1; \
+ " #op " %0, r0; \
+ bgtid %1, 1b; \
+ addk %1, %1, %2; \
+ " : : "r" (start), "r" (count), \
+ "r" (step) : "memory"); \
+} while (0);
+
+static void __flush_icache_range_msr_irq(unsigned long start, unsigned long end)
{
- __asm__ __volatile__ (" \
- wdc %0, r0" \
- : \
- : "r" (addr));
+ unsigned long flags;
+
+ pr_debug("%s: start 0x%x, end 0x%x\n", __func__,
+ (unsigned int)start, (unsigned int) end);
+
+ CACHE_LOOP_LIMITS(start, end,
+ cpuinfo.icache_line_length, cpuinfo.icache_size);
+
+ local_irq_save(flags);
+ __disable_icache_msr();
+
+ CACHE_RANGE_LOOP_1(start, end, cpuinfo.icache_line_length, wic);
+
+ __enable_icache_msr();
+ local_irq_restore(flags);
}
-void __invalidate_icache_all(void)
+static void __flush_icache_range_nomsr_irq(unsigned long start,
+ unsigned long end)
{
- unsigned int i;
- unsigned flags;
+ unsigned long flags;
- if (cpuinfo.use_icache) {
- local_irq_save(flags);
- __disable_icache();
+ pr_debug("%s: start 0x%x, end 0x%x\n", __func__,
+ (unsigned int)start, (unsigned int) end);
- /* Just loop through cache size and invalidate, no need to add
- CACHE_BASE address */
- for (i = 0; i < cpuinfo.icache_size;
- i += cpuinfo.icache_line)
- __invalidate_icache(i);
+ CACHE_LOOP_LIMITS(start, end,
+ cpuinfo.icache_line_length, cpuinfo.icache_size);
- __enable_icache();
- local_irq_restore(flags);
- }
+ local_irq_save(flags);
+ __disable_icache_nomsr();
+
+ CACHE_RANGE_LOOP_1(start, end, cpuinfo.icache_line_length, wic);
+
+ __enable_icache_nomsr();
+ local_irq_restore(flags);
}
-void __invalidate_icache_range(unsigned long start, unsigned long end)
+static void __flush_icache_range_noirq(unsigned long start,
+ unsigned long end)
{
- unsigned int i;
- unsigned flags;
- unsigned int align;
-
- if (cpuinfo.use_icache) {
- /*
- * No need to cover entire cache range,
- * just cover cache footprint
- */
- end = min(start + cpuinfo.icache_size, end);
- align = ~(cpuinfo.icache_line - 1);
- start &= align; /* Make sure we are aligned */
- /* Push end up to the next cache line */
- end = ((end & align) + cpuinfo.icache_line);
-
- local_irq_save(flags);
- __disable_icache();
-
- for (i = start; i < end; i += cpuinfo.icache_line)
- __invalidate_icache(i);
-
- __enable_icache();
- local_irq_restore(flags);
- }
+ pr_debug("%s: start 0x%x, end 0x%x\n", __func__,
+ (unsigned int)start, (unsigned int) end);
+
+ CACHE_LOOP_LIMITS(start, end,
+ cpuinfo.icache_line_length, cpuinfo.icache_size);
+ CACHE_RANGE_LOOP_1(start, end, cpuinfo.icache_line_length, wic);
+}
+
+static void __flush_icache_all_msr_irq(void)
+{
+ unsigned long flags;
+
+ pr_debug("%s\n", __func__);
+
+ local_irq_save(flags);
+ __disable_icache_msr();
+
+ CACHE_ALL_LOOP(cpuinfo.icache_size, cpuinfo.icache_line_length, wic);
+
+ __enable_icache_msr();
+ local_irq_restore(flags);
+}
+
+static void __flush_icache_all_nomsr_irq(void)
+{
+ unsigned long flags;
+
+ pr_debug("%s\n", __func__);
+
+ local_irq_save(flags);
+ __disable_icache_nomsr();
+
+ CACHE_ALL_LOOP(cpuinfo.icache_size, cpuinfo.icache_line_length, wic);
+
+ __enable_icache_nomsr();
+ local_irq_restore(flags);
}
-void __invalidate_icache_page(struct vm_area_struct *vma, struct page *page)
+static void __flush_icache_all_noirq(void)
{
- __invalidate_icache_all();
+ pr_debug("%s\n", __func__);
+ CACHE_ALL_LOOP(cpuinfo.icache_size, cpuinfo.icache_line_length, wic);
}
-void __invalidate_icache_user_range(struct vm_area_struct *vma,
- struct page *page, unsigned long adr,
- int len)
+static void __invalidate_dcache_all_msr_irq(void)
{
- __invalidate_icache_all();
+ unsigned long flags;
+
+ pr_debug("%s\n", __func__);
+
+ local_irq_save(flags);
+ __disable_dcache_msr();
+
+ CACHE_ALL_LOOP(cpuinfo.dcache_size, cpuinfo.dcache_line_length, wdc);
+
+ __enable_dcache_msr();
+ local_irq_restore(flags);
}
-void __invalidate_cache_sigtramp(unsigned long addr)
+static void __invalidate_dcache_all_nomsr_irq(void)
{
- __invalidate_icache_range(addr, addr + 8);
+ unsigned long flags;
+
+ pr_debug("%s\n", __func__);
+
+ local_irq_save(flags);
+ __disable_dcache_nomsr();
+
+ CACHE_ALL_LOOP(cpuinfo.dcache_size, cpuinfo.dcache_line_length, wdc);
+
+ __enable_dcache_nomsr();
+ local_irq_restore(flags);
}
-void __invalidate_dcache_all(void)
+static void __invalidate_dcache_all_noirq_wt(void)
{
- unsigned int i;
- unsigned flags;
-
- if (cpuinfo.use_dcache) {
- local_irq_save(flags);
- __disable_dcache();
-
- /*
- * Just loop through cache size and invalidate,
- * no need to add CACHE_BASE address
- */
- for (i = 0; i < cpuinfo.dcache_size;
- i += cpuinfo.dcache_line)
- __invalidate_dcache(i);
-
- __enable_dcache();
- local_irq_restore(flags);
- }
+ pr_debug("%s\n", __func__);
+ CACHE_ALL_LOOP(cpuinfo.dcache_size, cpuinfo.dcache_line_length, wdc)
}
-void __invalidate_dcache_range(unsigned long start, unsigned long end)
+/* FIXME this is weird - should be only wdc but not work
+ * MS: I am getting bus errors and other weird things */
+static void __invalidate_dcache_all_wb(void)
{
+ pr_debug("%s\n", __func__);
+ CACHE_ALL_LOOP2(cpuinfo.dcache_size, cpuinfo.dcache_line_length,
+ wdc.clear)
+
+#if 0
unsigned int i;
- unsigned flags;
- unsigned int align;
-
- if (cpuinfo.use_dcache) {
- /*
- * No need to cover entire cache range,
- * just cover cache footprint
- */
- end = min(start + cpuinfo.dcache_size, end);
- align = ~(cpuinfo.dcache_line - 1);
- start &= align; /* Make sure we are aligned */
- /* Push end up to the next cache line */
- end = ((end & align) + cpuinfo.dcache_line);
- local_irq_save(flags);
- __disable_dcache();
-
- for (i = start; i < end; i += cpuinfo.dcache_line)
- __invalidate_dcache(i);
-
- __enable_dcache();
- local_irq_restore(flags);
- }
+
+ pr_debug("%s\n", __func__);
+
+ /* Just loop through cache size and invalidate it */
+ for (i = 0; i < cpuinfo.dcache_size; i += cpuinfo.dcache_line_length)
+ __invalidate_dcache(0, i);
+#endif
+}
+
+static void __invalidate_dcache_range_wb(unsigned long start,
+ unsigned long end)
+{
+ pr_debug("%s: start 0x%x, end 0x%x\n", __func__,
+ (unsigned int)start, (unsigned int) end);
+
+ CACHE_LOOP_LIMITS(start, end,
+ cpuinfo.dcache_line_length, cpuinfo.dcache_size);
+ CACHE_RANGE_LOOP_2(start, end, cpuinfo.dcache_line_length, wdc.clear);
+}
+
+static void __invalidate_dcache_range_nomsr_wt(unsigned long start,
+ unsigned long end)
+{
+ pr_debug("%s: start 0x%x, end 0x%x\n", __func__,
+ (unsigned int)start, (unsigned int) end);
+ CACHE_LOOP_LIMITS(start, end,
+ cpuinfo.dcache_line_length, cpuinfo.dcache_size);
+
+ CACHE_RANGE_LOOP_1(start, end, cpuinfo.dcache_line_length, wdc);
}
-void __invalidate_dcache_page(struct vm_area_struct *vma, struct page *page)
+static void __invalidate_dcache_range_msr_irq_wt(unsigned long start,
+ unsigned long end)
{
- __invalidate_dcache_all();
+ unsigned long flags;
+
+ pr_debug("%s: start 0x%x, end 0x%x\n", __func__,
+ (unsigned int)start, (unsigned int) end);
+ CACHE_LOOP_LIMITS(start, end,
+ cpuinfo.dcache_line_length, cpuinfo.dcache_size);
+
+ local_irq_save(flags);
+ __disable_dcache_msr();
+
+ CACHE_RANGE_LOOP_1(start, end, cpuinfo.dcache_line_length, wdc);
+
+ __enable_dcache_msr();
+ local_irq_restore(flags);
+}
+
+static void __invalidate_dcache_range_nomsr_irq(unsigned long start,
+ unsigned long end)
+{
+ unsigned long flags;
+
+ pr_debug("%s: start 0x%x, end 0x%x\n", __func__,
+ (unsigned int)start, (unsigned int) end);
+
+ CACHE_LOOP_LIMITS(start, end,
+ cpuinfo.dcache_line_length, cpuinfo.dcache_size);
+
+ local_irq_save(flags);
+ __disable_dcache_nomsr();
+
+ CACHE_RANGE_LOOP_1(start, end, cpuinfo.dcache_line_length, wdc);
+
+ __enable_dcache_nomsr();
+ local_irq_restore(flags);
+}
+
+static void __flush_dcache_all_wb(void)
+{
+ pr_debug("%s\n", __func__);
+ CACHE_ALL_LOOP(cpuinfo.dcache_size, cpuinfo.dcache_line_length,
+ wdc.flush);
}
-void __invalidate_dcache_user_range(struct vm_area_struct *vma,
- struct page *page, unsigned long adr,
- int len)
+static void __flush_dcache_range_wb(unsigned long start, unsigned long end)
{
- __invalidate_dcache_all();
+ pr_debug("%s: start 0x%x, end 0x%x\n", __func__,
+ (unsigned int)start, (unsigned int) end);
+
+ CACHE_LOOP_LIMITS(start, end,
+ cpuinfo.dcache_line_length, cpuinfo.dcache_size);
+ CACHE_RANGE_LOOP_2(start, end, cpuinfo.dcache_line_length, wdc.flush);
+}
+
+/* struct for wb caches and for wt caches */
+struct scache *mbc;
+
+/* new wb cache model */
+const struct scache wb_msr = {
+ .ie = __enable_icache_msr,
+ .id = __disable_icache_msr,
+ .ifl = __flush_icache_all_noirq,
+ .iflr = __flush_icache_range_noirq,
+ .iin = __flush_icache_all_noirq,
+ .iinr = __flush_icache_range_noirq,
+ .de = __enable_dcache_msr,
+ .dd = __disable_dcache_msr,
+ .dfl = __flush_dcache_all_wb,
+ .dflr = __flush_dcache_range_wb,
+ .din = __invalidate_dcache_all_wb,
+ .dinr = __invalidate_dcache_range_wb,
+};
+
+/* There is only difference in ie, id, de, dd functions */
+const struct scache wb_nomsr = {
+ .ie = __enable_icache_nomsr,
+ .id = __disable_icache_nomsr,
+ .ifl = __flush_icache_all_noirq,
+ .iflr = __flush_icache_range_noirq,
+ .iin = __flush_icache_all_noirq,
+ .iinr = __flush_icache_range_noirq,
+ .de = __enable_dcache_nomsr,
+ .dd = __disable_dcache_nomsr,
+ .dfl = __flush_dcache_all_wb,
+ .dflr = __flush_dcache_range_wb,
+ .din = __invalidate_dcache_all_wb,
+ .dinr = __invalidate_dcache_range_wb,
+};
+
+/* Old wt cache model with disabling irq and turn off cache */
+const struct scache wt_msr = {
+ .ie = __enable_icache_msr,
+ .id = __disable_icache_msr,
+ .ifl = __flush_icache_all_msr_irq,
+ .iflr = __flush_icache_range_msr_irq,
+ .iin = __flush_icache_all_msr_irq,
+ .iinr = __flush_icache_range_msr_irq,
+ .de = __enable_dcache_msr,
+ .dd = __disable_dcache_msr,
+ .dfl = __invalidate_dcache_all_msr_irq,
+ .dflr = __invalidate_dcache_range_msr_irq_wt,
+ .din = __invalidate_dcache_all_msr_irq,
+ .dinr = __invalidate_dcache_range_msr_irq_wt,
+};
+
+const struct scache wt_nomsr = {
+ .ie = __enable_icache_nomsr,
+ .id = __disable_icache_nomsr,
+ .ifl = __flush_icache_all_nomsr_irq,
+ .iflr = __flush_icache_range_nomsr_irq,
+ .iin = __flush_icache_all_nomsr_irq,
+ .iinr = __flush_icache_range_nomsr_irq,
+ .de = __enable_dcache_nomsr,
+ .dd = __disable_dcache_nomsr,
+ .dfl = __invalidate_dcache_all_nomsr_irq,
+ .dflr = __invalidate_dcache_range_nomsr_irq,
+ .din = __invalidate_dcache_all_nomsr_irq,
+ .dinr = __invalidate_dcache_range_nomsr_irq,
+};
+
+/* New wt cache model for newer Microblaze versions */
+const struct scache wt_msr_noirq = {
+ .ie = __enable_icache_msr,
+ .id = __disable_icache_msr,
+ .ifl = __flush_icache_all_noirq,
+ .iflr = __flush_icache_range_noirq,
+ .iin = __flush_icache_all_noirq,
+ .iinr = __flush_icache_range_noirq,
+ .de = __enable_dcache_msr,
+ .dd = __disable_dcache_msr,
+ .dfl = __invalidate_dcache_all_noirq_wt,
+ .dflr = __invalidate_dcache_range_nomsr_wt,
+ .din = __invalidate_dcache_all_noirq_wt,
+ .dinr = __invalidate_dcache_range_nomsr_wt,
+};
+
+const struct scache wt_nomsr_noirq = {
+ .ie = __enable_icache_nomsr,
+ .id = __disable_icache_nomsr,
+ .ifl = __flush_icache_all_noirq,
+ .iflr = __flush_icache_range_noirq,
+ .iin = __flush_icache_all_noirq,
+ .iinr = __flush_icache_range_noirq,
+ .de = __enable_dcache_nomsr,
+ .dd = __disable_dcache_nomsr,
+ .dfl = __invalidate_dcache_all_noirq_wt,
+ .dflr = __invalidate_dcache_range_nomsr_wt,
+ .din = __invalidate_dcache_all_noirq_wt,
+ .dinr = __invalidate_dcache_range_nomsr_wt,
+};
+
+/* CPU version code for 7.20.c - see arch/microblaze/kernel/cpu/cpuinfo.c */
+#define CPUVER_7_20_A 0x0c
+#define CPUVER_7_20_D 0x0f
+
+#define INFO(s) printk(KERN_INFO "cache: " s " \n");
+
+void microblaze_cache_init(void)
+{
+ if (cpuinfo.use_instr & PVR2_USE_MSR_INSTR) {
+ if (cpuinfo.dcache_wb) {
+ INFO("wb_msr");
+ mbc = (struct scache *)&wb_msr;
+ if (cpuinfo.ver_code < CPUVER_7_20_D) {
+ /* MS: problem with signal handling - hw bug */
+ INFO("WB won't work properly");
+ }
+ } else {
+ if (cpuinfo.ver_code >= CPUVER_7_20_A) {
+ INFO("wt_msr_noirq");
+ mbc = (struct scache *)&wt_msr_noirq;
+ } else {
+ INFO("wt_msr");
+ mbc = (struct scache *)&wt_msr;
+ }
+ }
+ } else {
+ if (cpuinfo.dcache_wb) {
+ INFO("wb_nomsr");
+ mbc = (struct scache *)&wb_nomsr;
+ if (cpuinfo.ver_code < CPUVER_7_20_D) {
+ /* MS: problem with signal handling - hw bug */
+ INFO("WB won't work properly");
+ }
+ } else {
+ if (cpuinfo.ver_code >= CPUVER_7_20_A) {
+ INFO("wt_nomsr_noirq");
+ mbc = (struct scache *)&wt_nomsr_noirq;
+ } else {
+ INFO("wt_nomsr");
+ mbc = (struct scache *)&wt_nomsr;
+ }
+ }
+ }
}
diff --git a/arch/microblaze/kernel/cpu/cpuinfo-pvr-full.c b/arch/microblaze/kernel/cpu/cpuinfo-pvr-full.c
index c259786e7faa..f72dbd66c844 100644
--- a/arch/microblaze/kernel/cpu/cpuinfo-pvr-full.c
+++ b/arch/microblaze/kernel/cpu/cpuinfo-pvr-full.c
@@ -21,8 +21,14 @@
*/
#define CI(c, p) { ci->c = PVR_##p(pvr); }
+
+#if defined(CONFIG_EARLY_PRINTK) && defined(CONFIG_SERIAL_UARTLITE_CONSOLE)
#define err_printk(x) \
early_printk("ERROR: Microblaze " x "-different for PVR and DTS\n");
+#else
+#define err_printk(x) \
+ printk(KERN_INFO "ERROR: Microblaze " x "-different for PVR and DTS\n");
+#endif
void set_cpuinfo_pvr_full(struct cpuinfo *ci, struct device_node *cpu)
{
@@ -70,7 +76,7 @@ void set_cpuinfo_pvr_full(struct cpuinfo *ci, struct device_node *cpu)
CI(use_icache, USE_ICACHE);
CI(icache_tagbits, ICACHE_ADDR_TAG_BITS);
CI(icache_write, ICACHE_ALLOW_WR);
- CI(icache_line, ICACHE_LINE_LEN);
+ ci->icache_line_length = PVR_ICACHE_LINE_LEN(pvr) << 2;
CI(icache_size, ICACHE_BYTE_SIZE);
CI(icache_base, ICACHE_BASEADDR);
CI(icache_high, ICACHE_HIGHADDR);
@@ -78,11 +84,16 @@ void set_cpuinfo_pvr_full(struct cpuinfo *ci, struct device_node *cpu)
CI(use_dcache, USE_DCACHE);
CI(dcache_tagbits, DCACHE_ADDR_TAG_BITS);
CI(dcache_write, DCACHE_ALLOW_WR);
- CI(dcache_line, DCACHE_LINE_LEN);
+ ci->dcache_line_length = PVR_DCACHE_LINE_LEN(pvr) << 2;
CI(dcache_size, DCACHE_BYTE_SIZE);
CI(dcache_base, DCACHE_BASEADDR);
CI(dcache_high, DCACHE_HIGHADDR);
+ temp = PVR_DCACHE_USE_WRITEBACK(pvr);
+ if (ci->dcache_wb != temp)
+ err_printk("DCACHE WB");
+ ci->dcache_wb = temp;
+
CI(use_dopb, D_OPB);
CI(use_iopb, I_OPB);
CI(use_dlmb, D_LMB);
diff --git a/arch/microblaze/kernel/cpu/cpuinfo-static.c b/arch/microblaze/kernel/cpu/cpuinfo-static.c
index adb448f93d5f..6095aa6b5c88 100644
--- a/arch/microblaze/kernel/cpu/cpuinfo-static.c
+++ b/arch/microblaze/kernel/cpu/cpuinfo-static.c
@@ -72,12 +72,12 @@ void __init set_cpuinfo_static(struct cpuinfo *ci, struct device_node *cpu)
ci->use_icache = fcpu(cpu, "xlnx,use-icache");
ci->icache_tagbits = fcpu(cpu, "xlnx,addr-tag-bits");
ci->icache_write = fcpu(cpu, "xlnx,allow-icache-wr");
- ci->icache_line = fcpu(cpu, "xlnx,icache-line-len") << 2;
- if (!ci->icache_line) {
+ ci->icache_line_length = fcpu(cpu, "xlnx,icache-line-len") << 2;
+ if (!ci->icache_line_length) {
if (fcpu(cpu, "xlnx,icache-use-fsl"))
- ci->icache_line = 4 << 2;
+ ci->icache_line_length = 4 << 2;
else
- ci->icache_line = 1 << 2;
+ ci->icache_line_length = 1 << 2;
}
ci->icache_size = fcpu(cpu, "i-cache-size");
ci->icache_base = fcpu(cpu, "i-cache-baseaddr");
@@ -86,16 +86,17 @@ void __init set_cpuinfo_static(struct cpuinfo *ci, struct device_node *cpu)
ci->use_dcache = fcpu(cpu, "xlnx,use-dcache");
ci->dcache_tagbits = fcpu(cpu, "xlnx,dcache-addr-tag");
ci->dcache_write = fcpu(cpu, "xlnx,allow-dcache-wr");
- ci->dcache_line = fcpu(cpu, "xlnx,dcache-line-len") << 2;
- if (!ci->dcache_line) {
+ ci->dcache_line_length = fcpu(cpu, "xlnx,dcache-line-len") << 2;
+ if (!ci->dcache_line_length) {
if (fcpu(cpu, "xlnx,dcache-use-fsl"))
- ci->dcache_line = 4 << 2;
+ ci->dcache_line_length = 4 << 2;
else
- ci->dcache_line = 1 << 2;
+ ci->dcache_line_length = 1 << 2;
}
ci->dcache_size = fcpu(cpu, "d-cache-size");
ci->dcache_base = fcpu(cpu, "d-cache-baseaddr");
ci->dcache_high = fcpu(cpu, "d-cache-highaddr");
+ ci->dcache_wb = fcpu(cpu, "xlnx,dcache-use-writeback");
ci->use_dopb = fcpu(cpu, "xlnx,d-opb");
ci->use_iopb = fcpu(cpu, "xlnx,i-opb");
diff --git a/arch/microblaze/kernel/cpu/cpuinfo.c b/arch/microblaze/kernel/cpu/cpuinfo.c
index 3539babc1c18..991d71311b0e 100644
--- a/arch/microblaze/kernel/cpu/cpuinfo.c
+++ b/arch/microblaze/kernel/cpu/cpuinfo.c
@@ -29,11 +29,8 @@ const struct cpu_ver_key cpu_ver_lookup[] = {
{"7.20.a", 0x0c},
{"7.20.b", 0x0d},
{"7.20.c", 0x0e},
- /* FIXME There is no keycode defined in MBV for these versions */
- {"2.10.a", 0x10},
- {"3.00.a", 0x20},
- {"4.00.a", 0x30},
- {"4.00.b", 0x40},
+ {"7.20.d", 0x0f},
+ {"7.30.a", 0x10},
{NULL, 0},
};
diff --git a/arch/microblaze/kernel/cpu/mb.c b/arch/microblaze/kernel/cpu/mb.c
index 4dcfccdbc364..0c912b2a8e03 100644
--- a/arch/microblaze/kernel/cpu/mb.c
+++ b/arch/microblaze/kernel/cpu/mb.c
@@ -103,11 +103,15 @@ static int show_cpuinfo(struct seq_file *m, void *v)
else
count += seq_printf(m, "Icache:\t\tno\n");
- if (cpuinfo.use_dcache)
+ if (cpuinfo.use_dcache) {
count += seq_printf(m,
"Dcache:\t\t%ukB\n",
cpuinfo.dcache_size >> 10);
- else
+ if (cpuinfo.dcache_wb)
+ count += seq_printf(m, "\t\twrite-back\n");
+ else
+ count += seq_printf(m, "\t\twrite-through\n");
+ } else
count += seq_printf(m, "Dcache:\t\tno\n");
count += seq_printf(m,
diff --git a/arch/microblaze/kernel/cpu/pvr.c b/arch/microblaze/kernel/cpu/pvr.c
index c9a4340ddd53..9bee9382bf74 100644
--- a/arch/microblaze/kernel/cpu/pvr.c
+++ b/arch/microblaze/kernel/cpu/pvr.c
@@ -45,7 +45,7 @@
int cpu_has_pvr(void)
{
- unsigned flags;
+ unsigned long flags;
unsigned pvr0;
local_save_flags(flags);
diff --git a/arch/microblaze/kernel/entry-nommu.S b/arch/microblaze/kernel/entry-nommu.S
index 9083d85376a4..95b0855802df 100644
--- a/arch/microblaze/kernel/entry-nommu.S
+++ b/arch/microblaze/kernel/entry-nommu.S
@@ -208,8 +208,6 @@ ENTRY(_user_exception)
lwi r1, r1, TS_THREAD_INFO /* get the thread info */
/* calculate kernel stack pointer */
addik r1, r1, THREAD_SIZE - PT_SIZE
- swi r11, r0, PER_CPU(R11_SAVE) /* temporarily save r11 */
- lwi r11, r0, PER_CPU(KM) /* load mode indicator */
2:
swi r11, r1, PT_MODE /* store the mode */
lwi r11, r0, PER_CPU(R11_SAVE) /* reload r11 */
diff --git a/arch/microblaze/kernel/entry.S b/arch/microblaze/kernel/entry.S
index e3ecb36dd554..3bad4ff49471 100644
--- a/arch/microblaze/kernel/entry.S
+++ b/arch/microblaze/kernel/entry.S
@@ -31,6 +31,8 @@
#include <linux/errno.h>
#include <asm/signal.h>
+#undef DEBUG
+
/* The size of a state save frame. */
#define STATE_SAVE_SIZE (PT_SIZE + STATE_SAVE_ARG_SPACE)
@@ -352,10 +354,12 @@ C_ENTRY(_user_exception):
add r12, r12, r12; /* convert num -> ptr */
add r12, r12, r12;
+#ifdef DEBUG
/* Trac syscalls and stored them to r0_ram */
lwi r3, r12, 0x400 + r0_ram
addi r3, r3, 1
swi r3, r12, 0x400 + r0_ram
+#endif
# Find and jump into the syscall handler.
lwi r12, r12, sys_call_table
@@ -496,17 +500,6 @@ C_ENTRY(sys_execve):
brid microblaze_execve; /* Do real work (tail-call).*/
nop;
-C_ENTRY(sys_rt_sigsuspend_wrapper):
- swi r3, r1, PTO+PT_R3; /* restore saved r3, r4 registers */
- swi r4, r1, PTO+PT_R4;
- la r7, r1, PTO; /* add user context as 3rd arg */
- brlid r15, sys_rt_sigsuspend; /* Do real work.*/
- nop;
- lwi r3, r1, PTO+PT_R3; /* restore saved r3, r4 registers */
- lwi r4, r1, PTO+PT_R4;
- bri ret_from_trap /* fall through will not work here due to align */
- nop;
-
C_ENTRY(sys_rt_sigreturn_wrapper):
swi r3, r1, PTO+PT_R3; /* restore saved r3, r4 registers */
swi r4, r1, PTO+PT_R4;
@@ -711,15 +704,11 @@ C_ENTRY(ret_from_exc):
* (in a possibly modified form) after do_signal returns.
* store return registers separately because this macros is use
* for others exceptions */
- swi r3, r1, PTO + PT_R3;
- swi r4, r1, PTO + PT_R4;
la r5, r1, PTO; /* Arg 1: struct pt_regs *regs */
add r6, r0, r0; /* Arg 2: sigset_t *oldset */
addi r7, r0, 0; /* Arg 3: int in_syscall */
bralid r15, do_signal; /* Handle any signals */
nop;
- lwi r3, r1, PTO+PT_R3; /* restore saved r3, r4 registers */
- lwi r4, r1, PTO+PT_R4;
/* Finally, return to user state. */
1: swi r0, r0, PER_CPU(KM); /* Now officially in user state. */
diff --git a/arch/microblaze/kernel/ftrace.c b/arch/microblaze/kernel/ftrace.c
new file mode 100644
index 000000000000..388b31ca65a1
--- /dev/null
+++ b/arch/microblaze/kernel/ftrace.c
@@ -0,0 +1,237 @@
+/*
+ * Ftrace support for Microblaze.
+ *
+ * Copyright (C) 2009 Michal Simek <monstr@monstr.eu>
+ * Copyright (C) 2009 PetaLogix
+ *
+ * Based on MIPS and PowerPC ftrace code
+ *
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License. See the file "COPYING" in the main directory of this archive
+ * for more details.
+ */
+
+#include <asm/cacheflush.h>
+#include <linux/ftrace.h>
+
+#ifdef CONFIG_FUNCTION_GRAPH_TRACER
+/*
+ * Hook the return address and push it in the stack of return addrs
+ * in current thread info.
+ */
+void prepare_ftrace_return(unsigned long *parent, unsigned long self_addr)
+{
+ unsigned long old;
+ int faulted, err;
+ struct ftrace_graph_ent trace;
+ unsigned long return_hooker = (unsigned long)
+ &return_to_handler;
+
+ if (unlikely(atomic_read(&current->tracing_graph_pause)))
+ return;
+
+ /*
+ * Protect against fault, even if it shouldn't
+ * happen. This tool is too much intrusive to
+ * ignore such a protection.
+ */
+ asm volatile(" 1: lwi %0, %2, 0; \
+ 2: swi %3, %2, 0; \
+ addik %1, r0, 0; \
+ 3: \
+ .section .fixup, \"ax\"; \
+ 4: brid 3b; \
+ addik %1, r0, 1; \
+ .previous; \
+ .section __ex_table,\"a\"; \
+ .word 1b,4b; \
+ .word 2b,4b; \
+ .previous;" \
+ : "=&r" (old), "=r" (faulted)
+ : "r" (parent), "r" (return_hooker)
+ );
+
+ if (unlikely(faulted)) {
+ ftrace_graph_stop();
+ WARN_ON(1);
+ return;
+ }
+
+ err = ftrace_push_return_trace(old, self_addr, &trace.depth, 0);
+ if (err == -EBUSY) {
+ *parent = old;
+ return;
+ }
+
+ trace.func = self_addr;
+ /* Only trace if the calling function expects to */
+ if (!ftrace_graph_entry(&trace)) {
+ current->curr_ret_stack--;
+ *parent = old;
+ }
+}
+#endif /* CONFIG_FUNCTION_GRAPH_TRACER */
+
+#ifdef CONFIG_DYNAMIC_FTRACE
+/* save value to addr - it is save to do it in asm */
+static int ftrace_modify_code(unsigned long addr, unsigned int value)
+{
+ int faulted = 0;
+
+ __asm__ __volatile__(" 1: swi %2, %1, 0; \
+ addik %0, r0, 0; \
+ 2: \
+ .section .fixup, \"ax\"; \
+ 3: brid 2b; \
+ addik %0, r0, 1; \
+ .previous; \
+ .section __ex_table,\"a\"; \
+ .word 1b,3b; \
+ .previous;" \
+ : "=r" (faulted)
+ : "r" (addr), "r" (value)
+ );
+
+ if (unlikely(faulted))
+ return -EFAULT;
+
+ return 0;
+}
+
+#define MICROBLAZE_NOP 0x80000000
+#define MICROBLAZE_BRI 0xb800000C
+
+static unsigned int recorded; /* if save was or not */
+static unsigned int imm; /* saving whole imm instruction */
+
+/* There are two approaches howto solve ftrace_make nop function - look below */
+#undef USE_FTRACE_NOP
+
+#ifdef USE_FTRACE_NOP
+static unsigned int bralid; /* saving whole bralid instruction */
+#endif
+
+int ftrace_make_nop(struct module *mod,
+ struct dyn_ftrace *rec, unsigned long addr)
+{
+ /* we have this part of code which we are working with
+ * b000c000 imm -16384
+ * b9fc8e30 bralid r15, -29136 // c0008e30 <_mcount>
+ * 80000000 or r0, r0, r0
+ *
+ * The first solution (!USE_FTRACE_NOP-could be called branch solution)
+ * b000c000 bri 12 (0xC - jump to any other instruction)
+ * b9fc8e30 bralid r15, -29136 // c0008e30 <_mcount>
+ * 80000000 or r0, r0, r0
+ * any other instruction
+ *
+ * The second solution (USE_FTRACE_NOP) - no jump just nops
+ * 80000000 or r0, r0, r0
+ * 80000000 or r0, r0, r0
+ * 80000000 or r0, r0, r0
+ */
+ int ret = 0;
+
+ if (recorded == 0) {
+ recorded = 1;
+ imm = *(unsigned int *)rec->ip;
+ pr_debug("%s: imm:0x%x\n", __func__, imm);
+#ifdef USE_FTRACE_NOP
+ bralid = *(unsigned int *)(rec->ip + 4);
+ pr_debug("%s: bralid 0x%x\n", __func__, bralid);
+#endif /* USE_FTRACE_NOP */
+ }
+
+#ifdef USE_FTRACE_NOP
+ ret = ftrace_modify_code(rec->ip, MICROBLAZE_NOP);
+ ret += ftrace_modify_code(rec->ip + 4, MICROBLAZE_NOP);
+#else /* USE_FTRACE_NOP */
+ ret = ftrace_modify_code(rec->ip, MICROBLAZE_BRI);
+#endif /* USE_FTRACE_NOP */
+ return ret;
+}
+
+static int ret_addr; /* initialized as 0 by default */
+
+/* I believe that first is called ftrace_make_nop before this function */
+int ftrace_make_call(struct dyn_ftrace *rec, unsigned long addr)
+{
+ int ret;
+ ret_addr = addr; /* saving where the barrier jump is */
+ pr_debug("%s: addr:0x%x, rec->ip: 0x%x, imm:0x%x\n",
+ __func__, (unsigned int)addr, (unsigned int)rec->ip, imm);
+ ret = ftrace_modify_code(rec->ip, imm);
+#ifdef USE_FTRACE_NOP
+ pr_debug("%s: bralid:0x%x\n", __func__, bralid);
+ ret += ftrace_modify_code(rec->ip + 4, bralid);
+#endif /* USE_FTRACE_NOP */
+ return ret;
+}
+
+int __init ftrace_dyn_arch_init(void *data)
+{
+ /* The return code is retured via data */
+ *(unsigned long *)data = 0;
+
+ return 0;
+}
+
+int ftrace_update_ftrace_func(ftrace_func_t func)
+{
+ unsigned long ip = (unsigned long)(&ftrace_call);
+ unsigned int upper = (unsigned int)func;
+ unsigned int lower = (unsigned int)func;
+ int ret = 0;
+
+ /* create proper saving to ftrace_call poll */
+ upper = 0xb0000000 + (upper >> 16); /* imm func_upper */
+ lower = 0x32800000 + (lower & 0xFFFF); /* addik r20, r0, func_lower */
+
+ pr_debug("%s: func=0x%x, ip=0x%x, upper=0x%x, lower=0x%x\n",
+ __func__, (unsigned int)func, (unsigned int)ip, upper, lower);
+
+ /* save upper and lower code */
+ ret = ftrace_modify_code(ip, upper);
+ ret += ftrace_modify_code(ip + 4, lower);
+
+ /* We just need to remove the rtsd r15, 8 by NOP */
+ BUG_ON(!ret_addr);
+ if (ret_addr)
+ ret += ftrace_modify_code(ret_addr, MICROBLAZE_NOP);
+ else
+ ret = 1; /* fault */
+
+ /* All changes are done - lets do caches consistent */
+ flush_icache();
+ return ret;
+}
+
+#ifdef CONFIG_FUNCTION_GRAPH_TRACER
+unsigned int old_jump; /* saving place for jump instruction */
+
+int ftrace_enable_ftrace_graph_caller(void)
+{
+ unsigned int ret;
+ unsigned long ip = (unsigned long)(&ftrace_call_graph);
+
+ old_jump = *(unsigned int *)ip; /* save jump over instruction */
+ ret = ftrace_modify_code(ip, MICROBLAZE_NOP);
+ flush_icache();
+
+ pr_debug("%s: Replace instruction: 0x%x\n", __func__, old_jump);
+ return ret;
+}
+
+int ftrace_disable_ftrace_graph_caller(void)
+{
+ unsigned int ret;
+ unsigned long ip = (unsigned long)(&ftrace_call_graph);
+
+ ret = ftrace_modify_code(ip, old_jump);
+ flush_icache();
+
+ pr_debug("%s\n", __func__);
+ return ret;
+}
+#endif /* CONFIG_FUNCTION_GRAPH_TRACER */
+#endif /* CONFIG_DYNAMIC_FTRACE */
diff --git a/arch/microblaze/kernel/heartbeat.c b/arch/microblaze/kernel/heartbeat.c
index 1bdf20222b92..522751737cfa 100644
--- a/arch/microblaze/kernel/heartbeat.c
+++ b/arch/microblaze/kernel/heartbeat.c
@@ -45,6 +45,7 @@ void heartbeat(void)
void setup_heartbeat(void)
{
struct device_node *gpio = NULL;
+ int *prop;
int j;
char *gpio_list[] = {
"xlnx,xps-gpio-1.00.a",
@@ -58,10 +59,14 @@ void setup_heartbeat(void)
break;
}
- base_addr = *(int *) of_get_property(gpio, "reg", NULL);
- base_addr = (unsigned long) ioremap(base_addr, PAGE_SIZE);
- printk(KERN_NOTICE "Heartbeat GPIO at 0x%x\n", base_addr);
+ if (gpio) {
+ base_addr = *(int *) of_get_property(gpio, "reg", NULL);
+ base_addr = (unsigned long) ioremap(base_addr, PAGE_SIZE);
+ printk(KERN_NOTICE "Heartbeat GPIO at 0x%x\n", base_addr);
- if (*(int *) of_get_property(gpio, "xlnx,is-bidir", NULL))
- out_be32(base_addr + 4, 0); /* GPIO is configured as output */
+ /* GPIO is configured as output */
+ prop = (int *) of_get_property(gpio, "xlnx,is-bidir", NULL);
+ if (prop)
+ out_be32(base_addr + 4, 0);
+ }
}
diff --git a/arch/microblaze/kernel/intc.c b/arch/microblaze/kernel/intc.c
index 6eea6f92b84e..03172c1da770 100644
--- a/arch/microblaze/kernel/intc.c
+++ b/arch/microblaze/kernel/intc.c
@@ -42,8 +42,16 @@ unsigned int nr_irq;
static void intc_enable_or_unmask(unsigned int irq)
{
+ unsigned long mask = 1 << irq;
pr_debug("enable_or_unmask: %d\n", irq);
- out_be32(INTC_BASE + SIE, 1 << irq);
+ out_be32(INTC_BASE + SIE, mask);
+
+ /* ack level irqs because they can't be acked during
+ * ack function since the handle_level_irq function
+ * acks the irq before calling the interrupt handler
+ */
+ if (irq_desc[irq].status & IRQ_LEVEL)
+ out_be32(INTC_BASE + IAR, mask);
}
static void intc_disable_or_mask(unsigned int irq)
diff --git a/arch/microblaze/kernel/mcount.S b/arch/microblaze/kernel/mcount.S
new file mode 100644
index 000000000000..e7eaa7a8cbd3
--- /dev/null
+++ b/arch/microblaze/kernel/mcount.S
@@ -0,0 +1,170 @@
+/*
+ * Low-level ftrace handling
+ *
+ * Copyright (C) 2009 Michal Simek <monstr@monstr.eu>
+ * Copyright (C) 2009 PetaLogix
+ *
+ * This file is subject to the terms and conditions of the GNU General
+ * Public License. See the file COPYING in the main directory of this
+ * archive for more details.
+ */
+
+#include <linux/linkage.h>
+
+#define NOALIGN_ENTRY(name) .globl name; name:
+
+/* FIXME MS: I think that I don't need to save all regs */
+#define SAVE_REGS \
+ addik r1, r1, -120; \
+ swi r2, r1, 4; \
+ swi r3, r1, 8; \
+ swi r4, r1, 12; \
+ swi r5, r1, 116; \
+ swi r6, r1, 16; \
+ swi r7, r1, 20; \
+ swi r8, r1, 24; \
+ swi r9, r1, 28; \
+ swi r10, r1, 32; \
+ swi r11, r1, 36; \
+ swi r12, r1, 40; \
+ swi r13, r1, 44; \
+ swi r14, r1, 48; \
+ swi r16, r1, 52; \
+ swi r17, r1, 56; \
+ swi r18, r1, 60; \
+ swi r19, r1, 64; \
+ swi r20, r1, 68; \
+ swi r21, r1, 72; \
+ swi r22, r1, 76; \
+ swi r23, r1, 80; \
+ swi r24, r1, 84; \
+ swi r25, r1, 88; \
+ swi r26, r1, 92; \
+ swi r27, r1, 96; \
+ swi r28, r1, 100; \
+ swi r29, r1, 104; \
+ swi r30, r1, 108; \
+ swi r31, r1, 112;
+
+#define RESTORE_REGS \
+ lwi r2, r1, 4; \
+ lwi r3, r1, 8; \
+ lwi r4, r1, 12; \
+ lwi r5, r1, 116; \
+ lwi r6, r1, 16; \
+ lwi r7, r1, 20; \
+ lwi r8, r1, 24; \
+ lwi r9, r1, 28; \
+ lwi r10, r1, 32; \
+ lwi r11, r1, 36; \
+ lwi r12, r1, 40; \
+ lwi r13, r1, 44; \
+ lwi r14, r1, 48; \
+ lwi r16, r1, 52; \
+ lwi r17, r1, 56; \
+ lwi r18, r1, 60; \
+ lwi r19, r1, 64; \
+ lwi r20, r1, 68; \
+ lwi r21, r1, 72; \
+ lwi r22, r1, 76; \
+ lwi r23, r1, 80; \
+ lwi r24, r1, 84; \
+ lwi r25, r1, 88; \
+ lwi r26, r1, 92; \
+ lwi r27, r1, 96; \
+ lwi r28, r1, 100; \
+ lwi r29, r1, 104; \
+ lwi r30, r1, 108; \
+ lwi r31, r1, 112; \
+ addik r1, r1, 120;
+
+ENTRY(ftrace_stub)
+ rtsd r15, 8;
+ nop;
+
+ENTRY(_mcount)
+#ifdef CONFIG_DYNAMIC_FTRACE
+ENTRY(ftrace_caller)
+ /* MS: It is just barrier which is removed from C code */
+ rtsd r15, 8
+ nop
+#endif /* CONFIG_DYNAMIC_FTRACE */
+ SAVE_REGS
+ swi r15, r1, 0;
+ /* MS: HAVE_FUNCTION_TRACE_MCOUNT_TEST begin of checking */
+ lwi r5, r0, function_trace_stop;
+ bneid r5, end;
+ nop;
+ /* MS: HAVE_FUNCTION_TRACE_MCOUNT_TEST end of checking */
+#ifdef CONFIG_FUNCTION_GRAPH_TRACER
+#ifndef CONFIG_DYNAMIC_FTRACE
+ lwi r5, r0, ftrace_graph_return;
+ addik r6, r0, ftrace_stub; /* asm implementation */
+ cmpu r5, r5, r6; /* ftrace_graph_return != ftrace_stub */
+ beqid r5, end_graph_tracer;
+ nop;
+
+ lwi r6, r0, ftrace_graph_entry;
+ addik r5, r0, ftrace_graph_entry_stub; /* implemented in C */
+ cmpu r5, r5, r6; /* ftrace_graph_entry != ftrace_graph_entry_stub */
+ beqid r5, end_graph_tracer;
+ nop;
+#else /* CONFIG_DYNAMIC_FTRACE */
+NOALIGN_ENTRY(ftrace_call_graph)
+ /* MS: jump over graph function - replaced from C code */
+ bri end_graph_tracer
+#endif /* CONFIG_DYNAMIC_FTRACE */
+ addik r5, r1, 120; /* MS: load parent addr */
+ addik r6, r15, 0; /* MS: load current function addr */
+ bralid r15, prepare_ftrace_return;
+ nop;
+ /* MS: graph was taken that's why - can jump over function trace */
+ brid end;
+ nop;
+end_graph_tracer:
+#endif /* CONFIG_FUNCTION_GRAPH_TRACER */
+#ifndef CONFIG_DYNAMIC_FTRACE
+ /* MS: test function trace if is taken or not */
+ lwi r20, r0, ftrace_trace_function;
+ addik r6, r0, ftrace_stub;
+ cmpu r5, r20, r6; /* ftrace_trace_function != ftrace_stub */
+ beqid r5, end; /* MS: not taken -> jump over */
+ nop;
+#else /* CONFIG_DYNAMIC_FTRACE */
+NOALIGN_ENTRY(ftrace_call)
+/* instruction for setup imm FUNC_part1, addik r20, r0, FUNC_part2 */
+ nop
+ nop
+#endif /* CONFIG_DYNAMIC_FTRACE */
+/* static normal trace */
+ lwi r6, r1, 120; /* MS: load parent addr */
+ addik r5, r15, 0; /* MS: load current function addr */
+ /* MS: here is dependency on previous code */
+ brald r15, r20; /* MS: jump to ftrace handler */
+ nop;
+end:
+ lwi r15, r1, 0;
+ RESTORE_REGS
+
+ rtsd r15, 8; /* MS: jump back */
+ nop;
+
+#ifdef CONFIG_FUNCTION_GRAPH_TRACER
+ENTRY(return_to_handler)
+ nop; /* MS: just barrier for rtsd r15, 8 */
+ nop;
+ SAVE_REGS
+ swi r15, r1, 0;
+
+ /* MS: find out returning address */
+ bralid r15, ftrace_return_to_handler;
+ nop;
+
+ /* MS: return value from ftrace_return_to_handler is my returning addr
+ * must be before restore regs because I have to restore r3 content */
+ addik r15, r3, 0;
+ RESTORE_REGS
+
+ rtsd r15, 8; /* MS: jump back */
+ nop;
+#endif /* CONFIG_FUNCTION_TRACER */
diff --git a/arch/microblaze/kernel/microblaze_ksyms.c b/arch/microblaze/kernel/microblaze_ksyms.c
index 59ff20e33e0c..bc4dcb7d3861 100644
--- a/arch/microblaze/kernel/microblaze_ksyms.c
+++ b/arch/microblaze/kernel/microblaze_ksyms.c
@@ -18,6 +18,7 @@
#include <linux/io.h>
#include <asm/page.h>
#include <asm/system.h>
+#include <linux/ftrace.h>
#include <linux/uaccess.h>
/*
@@ -47,3 +48,7 @@ extern void __umodsi3(void);
EXPORT_SYMBOL(__umodsi3);
extern char *_ebss;
EXPORT_SYMBOL_GPL(_ebss);
+#ifdef CONFIG_FUNCTION_TRACER
+extern void _mcount(void);
+EXPORT_SYMBOL(_mcount);
+#endif
diff --git a/arch/microblaze/kernel/process.c b/arch/microblaze/kernel/process.c
index c592d475b3d8..812f1bf06c9e 100644
--- a/arch/microblaze/kernel/process.c
+++ b/arch/microblaze/kernel/process.c
@@ -15,6 +15,7 @@
#include <linux/bitops.h>
#include <asm/system.h>
#include <asm/pgalloc.h>
+#include <asm/cacheflush.h>
void show_regs(struct pt_regs *regs)
{
diff --git a/arch/microblaze/kernel/reset.c b/arch/microblaze/kernel/reset.c
new file mode 100644
index 000000000000..a1721a33042e
--- /dev/null
+++ b/arch/microblaze/kernel/reset.c
@@ -0,0 +1,140 @@
+/*
+ * Copyright (C) 2009 Michal Simek <monstr@monstr.eu>
+ * Copyright (C) 2009 PetaLogix
+ *
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License. See the file "COPYING" in the main directory of this archive
+ * for more details.
+ */
+
+#include <linux/init.h>
+#include <linux/of_platform.h>
+#include <asm/prom.h>
+
+/* Trigger specific functions */
+#ifdef CONFIG_GPIOLIB
+
+#include <linux/of_gpio.h>
+
+static int handle; /* reset pin handle */
+static unsigned int reset_val;
+
+static int of_reset_gpio_handle(void)
+{
+ int ret; /* variable which stored handle reset gpio pin */
+ struct device_node *root; /* root node */
+ struct device_node *gpio; /* gpio node */
+ struct of_gpio_chip *of_gc = NULL;
+ enum of_gpio_flags flags ;
+ const void *gpio_spec;
+
+ /* find out root node */
+ root = of_find_node_by_path("/");
+
+ /* give me handle for gpio node to be possible allocate pin */
+ ret = of_parse_phandles_with_args(root, "hard-reset-gpios",
+ "#gpio-cells", 0, &gpio, &gpio_spec);
+ if (ret) {
+ pr_debug("%s: can't parse gpios property\n", __func__);
+ goto err0;
+ }
+
+ of_gc = gpio->data;
+ if (!of_gc) {
+ pr_debug("%s: gpio controller %s isn't registered\n",
+ root->full_name, gpio->full_name);
+ ret = -ENODEV;
+ goto err1;
+ }
+
+ ret = of_gc->xlate(of_gc, root, gpio_spec, &flags);
+ if (ret < 0)
+ goto err1;
+
+ ret += of_gc->gc.base;
+err1:
+ of_node_put(gpio);
+err0:
+ pr_debug("%s exited with status %d\n", __func__, ret);
+ return ret;
+}
+
+void of_platform_reset_gpio_probe(void)
+{
+ int ret;
+ handle = of_reset_gpio_handle();
+
+ if (!gpio_is_valid(handle)) {
+ printk(KERN_INFO "Skipping unavailable RESET gpio %d (%s)\n",
+ handle, "reset");
+ }
+
+ ret = gpio_request(handle, "reset");
+ if (ret < 0) {
+ printk(KERN_INFO "GPIO pin is already allocated\n");
+ return;
+ }
+
+ /* get current setup value */
+ reset_val = gpio_get_value(handle);
+ /* FIXME maybe worth to perform any action */
+ pr_debug("Reset: Gpio output state: 0x%x\n", reset_val);
+
+ /* Setup GPIO as output */
+ ret = gpio_direction_output(handle, 0);
+ if (ret < 0)
+ goto err;
+
+ /* Setup output direction */
+ gpio_set_value(handle, 0);
+
+ printk(KERN_INFO "RESET: Registered gpio device: %d, current val: %d\n",
+ handle, reset_val);
+ return;
+err:
+ gpio_free(handle);
+ return;
+}
+
+
+static void gpio_system_reset(void)
+{
+ gpio_set_value(handle, 1 - reset_val);
+}
+#else
+#define gpio_system_reset() do {} while (0)
+void of_platform_reset_gpio_probe(void)
+{
+ return;
+}
+#endif
+
+void machine_restart(char *cmd)
+{
+ printk(KERN_NOTICE "Machine restart...\n");
+ gpio_system_reset();
+ dump_stack();
+ while (1)
+ ;
+}
+
+void machine_shutdown(void)
+{
+ printk(KERN_NOTICE "Machine shutdown...\n");
+ while (1)
+ ;
+}
+
+void machine_halt(void)
+{
+ printk(KERN_NOTICE "Machine halt...\n");
+ while (1)
+ ;
+}
+
+void machine_power_off(void)
+{
+ printk(KERN_NOTICE "Machine power off...\n");
+ while (1)
+ ;
+}
diff --git a/arch/microblaze/kernel/setup.c b/arch/microblaze/kernel/setup.c
index 8c1e0f4dcf18..5372b24ad049 100644
--- a/arch/microblaze/kernel/setup.c
+++ b/arch/microblaze/kernel/setup.c
@@ -52,13 +52,12 @@ void __init setup_arch(char **cmdline_p)
/* irq_early_init(); */
setup_cpuinfo();
- __invalidate_icache_all();
- __enable_icache();
+ microblaze_cache_init();
- __invalidate_dcache_all();
- __enable_dcache();
+ enable_dcache();
- panic_timeout = 120;
+ invalidate_icache();
+ enable_icache();
setup_memory();
@@ -131,6 +130,8 @@ void __init machine_early_init(const char *cmdline, unsigned int ram,
strlcpy(cmd_line, cmdline, COMMAND_LINE_SIZE);
#endif
+ lockdep_init();
+
/* initialize device tree for usage in early_printk */
early_init_devtree((void *)_fdt_start);
@@ -186,32 +187,3 @@ static int microblaze_debugfs_init(void)
}
arch_initcall(microblaze_debugfs_init);
#endif
-
-void machine_restart(char *cmd)
-{
- printk(KERN_NOTICE "Machine restart...\n");
- dump_stack();
- while (1)
- ;
-}
-
-void machine_shutdown(void)
-{
- printk(KERN_NOTICE "Machine shutdown...\n");
- while (1)
- ;
-}
-
-void machine_halt(void)
-{
- printk(KERN_NOTICE "Machine halt...\n");
- while (1)
- ;
-}
-
-void machine_power_off(void)
-{
- printk(KERN_NOTICE "Machine power off...\n");
- while (1)
- ;
-}
diff --git a/arch/microblaze/kernel/signal.c b/arch/microblaze/kernel/signal.c
index 1c80e4fc40ce..d8d3bb396cd6 100644
--- a/arch/microblaze/kernel/signal.c
+++ b/arch/microblaze/kernel/signal.c
@@ -44,7 +44,6 @@
asmlinkage int do_signal(struct pt_regs *regs, sigset_t *oldset, int in_sycall);
-
asmlinkage long
sys_sigaltstack(const stack_t __user *uss, stack_t __user *uoss,
struct pt_regs *regs)
@@ -176,6 +175,11 @@ static void setup_rt_frame(int sig, struct k_sigaction *ka, siginfo_t *info,
struct rt_sigframe __user *frame;
int err = 0;
int signal;
+ unsigned long address = 0;
+#ifdef CONFIG_MMU
+ pmd_t *pmdp;
+ pte_t *ptep;
+#endif
frame = get_sigframe(ka, regs, sizeof(*frame));
@@ -216,8 +220,29 @@ static void setup_rt_frame(int sig, struct k_sigaction *ka, siginfo_t *info,
Negative 8 offset because return is rtsd r15, 8 */
regs->r15 = ((unsigned long)frame->tramp)-8;
- __invalidate_cache_sigtramp((unsigned long)frame->tramp);
-
+ address = ((unsigned long)frame->tramp);
+#ifdef CONFIG_MMU
+ pmdp = pmd_offset(pud_offset(
+ pgd_offset(current->mm, address),
+ address), address);
+
+ preempt_disable();
+ ptep = pte_offset_map(pmdp, address);
+ if (pte_present(*ptep)) {
+ address = (unsigned long) page_address(pte_page(*ptep));
+ /* MS: I need add offset in page */
+ address += ((unsigned long)frame->tramp) & ~PAGE_MASK;
+ /* MS address is virtual */
+ address = virt_to_phys(address);
+ invalidate_icache_range(address, address + 8);
+ flush_dcache_range(address, address + 8);
+ }
+ pte_unmap(ptep);
+ preempt_enable();
+#else
+ flush_icache_range(address, address + 8);
+ flush_dcache_range(address, address + 8);
+#endif
if (err)
goto give_sigsegv;
@@ -233,6 +258,10 @@ static void setup_rt_frame(int sig, struct k_sigaction *ka, siginfo_t *info,
set_fs(USER_DS);
+ /* the tracer may want to single-step inside the handler */
+ if (test_thread_flag(TIF_SINGLESTEP))
+ ptrace_notify(SIGTRAP);
+
#ifdef DEBUG_SIG
printk(KERN_INFO "SIG deliver (%s:%d): sp=%p pc=%08lx\n",
current->comm, current->pid, frame, regs->pc);
diff --git a/arch/microblaze/kernel/stacktrace.c b/arch/microblaze/kernel/stacktrace.c
new file mode 100644
index 000000000000..123692f22647
--- /dev/null
+++ b/arch/microblaze/kernel/stacktrace.c
@@ -0,0 +1,65 @@
+/*
+ * Stack trace support for Microblaze.
+ *
+ * Copyright (C) 2009 Michal Simek <monstr@monstr.eu>
+ * Copyright (C) 2009 PetaLogix
+ *
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License. See the file "COPYING" in the main directory of this archive
+ * for more details.
+ */
+
+#include <linux/sched.h>
+#include <linux/stacktrace.h>
+#include <linux/thread_info.h>
+#include <linux/ptrace.h>
+#include <linux/module.h>
+
+/* FIXME initial support */
+void save_stack_trace(struct stack_trace *trace)
+{
+ unsigned long *sp;
+ unsigned long addr;
+ asm("addik %0, r1, 0" : "=r" (sp));
+
+ while (!kstack_end(sp)) {
+ addr = *sp++;
+ if (__kernel_text_address(addr)) {
+ if (trace->skip > 0)
+ trace->skip--;
+ else
+ trace->entries[trace->nr_entries++] = addr;
+
+ if (trace->nr_entries >= trace->max_entries)
+ break;
+ }
+ }
+}
+EXPORT_SYMBOL_GPL(save_stack_trace);
+
+void save_stack_trace_tsk(struct task_struct *tsk, struct stack_trace *trace)
+{
+ unsigned int *sp;
+ unsigned long addr;
+
+ struct thread_info *ti = task_thread_info(tsk);
+
+ if (tsk == current)
+ asm("addik %0, r1, 0" : "=r" (sp));
+ else
+ sp = (unsigned int *)ti->cpu_context.r1;
+
+ while (!kstack_end(sp)) {
+ addr = *sp++;
+ if (__kernel_text_address(addr)) {
+ if (trace->skip > 0)
+ trace->skip--;
+ else
+ trace->entries[trace->nr_entries++] = addr;
+
+ if (trace->nr_entries >= trace->max_entries)
+ break;
+ }
+ }
+}
+EXPORT_SYMBOL_GPL(save_stack_trace_tsk);
diff --git a/arch/microblaze/kernel/syscall_table.S b/arch/microblaze/kernel/syscall_table.S
index b96f365ea6b1..4088be7d4e29 100644
--- a/arch/microblaze/kernel/syscall_table.S
+++ b/arch/microblaze/kernel/syscall_table.S
@@ -183,7 +183,7 @@ ENTRY(sys_call_table)
.long sys_rt_sigpending
.long sys_rt_sigtimedwait
.long sys_rt_sigqueueinfo
- .long sys_rt_sigsuspend_wrapper
+ .long sys_rt_sigsuspend
.long sys_pread64 /* 180 */
.long sys_pwrite64
.long sys_chown
@@ -303,7 +303,7 @@ ENTRY(sys_call_table)
.long sys_mkdirat
.long sys_mknodat
.long sys_fchownat
- .long sys_ni_syscall
+ .long sys_futimesat
.long sys_fstatat64 /* 300 */
.long sys_unlinkat
.long sys_renameat
diff --git a/arch/microblaze/kernel/timer.c b/arch/microblaze/kernel/timer.c
index 5499deae7fa6..ed61b2f17719 100644
--- a/arch/microblaze/kernel/timer.c
+++ b/arch/microblaze/kernel/timer.c
@@ -183,6 +183,31 @@ static cycle_t microblaze_read(struct clocksource *cs)
return (cycle_t) (in_be32(TIMER_BASE + TCR1));
}
+static struct timecounter microblaze_tc = {
+ .cc = NULL,
+};
+
+static cycle_t microblaze_cc_read(const struct cyclecounter *cc)
+{
+ return microblaze_read(NULL);
+}
+
+static struct cyclecounter microblaze_cc = {
+ .read = microblaze_cc_read,
+ .mask = CLOCKSOURCE_MASK(32),
+ .shift = 24,
+};
+
+int __init init_microblaze_timecounter(void)
+{
+ microblaze_cc.mult = div_sc(cpuinfo.cpu_clock_freq, NSEC_PER_SEC,
+ microblaze_cc.shift);
+
+ timecounter_init(&microblaze_tc, &microblaze_cc, sched_clock());
+
+ return 0;
+}
+
static struct clocksource clocksource_microblaze = {
.name = "microblaze_clocksource",
.rating = 300,
@@ -204,6 +229,9 @@ static int __init microblaze_clocksource_init(void)
out_be32(TIMER_BASE + TCSR1, in_be32(TIMER_BASE + TCSR1) & ~TCSR_ENT);
/* start timer1 - up counting without interrupt */
out_be32(TIMER_BASE + TCSR1, TCSR_TINT|TCSR_ENT|TCSR_ARHT);
+
+ /* register timecounter - for ftrace support */
+ init_microblaze_timecounter();
return 0;
}
diff --git a/arch/microblaze/kernel/vmlinux.lds.S b/arch/microblaze/kernel/vmlinux.lds.S
index e704188d7855..5ef619aad634 100644
--- a/arch/microblaze/kernel/vmlinux.lds.S
+++ b/arch/microblaze/kernel/vmlinux.lds.S
@@ -26,11 +26,12 @@ SECTIONS {
_stext = . ;
*(.text .text.*)
*(.fixup)
- EXIT_TEXT
- EXIT_CALL
+ EXIT_TEXT
+ EXIT_CALL
SCHED_TEXT
LOCK_TEXT
KPROBES_TEXT
+ IRQENTRY_TEXT
. = ALIGN (4) ;
_etext = . ;
}
@@ -86,6 +87,7 @@ SECTIONS {
_KERNEL_SDA_BASE_ = _ssro + (_ssro_size / 2) ;
}
+ . = ALIGN(PAGE_SIZE);
__init_begin = .;
INIT_TEXT_SECTION(PAGE_SIZE)