]> git.karo-electronics.de Git - karo-tx-linux.git/commitdiff
Merge remote-tracking branch 'xtensa/for_next'
authorStephen Rothwell <sfr@canb.auug.org.au>
Thu, 28 Nov 2013 00:11:19 +0000 (11:11 +1100)
committerStephen Rothwell <sfr@canb.auug.org.au>
Thu, 28 Nov 2013 00:11:19 +0000 (11:11 +1100)
44 files changed:
arch/xtensa/Kconfig
arch/xtensa/include/asm/Kbuild
arch/xtensa/include/asm/barrier.h
arch/xtensa/include/asm/bitops.h
arch/xtensa/include/asm/cacheflush.h
arch/xtensa/include/asm/delay.h
arch/xtensa/include/asm/ftrace.h
arch/xtensa/include/asm/futex.h [new file with mode: 0644]
arch/xtensa/include/asm/irq.h
arch/xtensa/include/asm/mmu.h
arch/xtensa/include/asm/mmu_context.h
arch/xtensa/include/asm/mxregs.h [new file with mode: 0644]
arch/xtensa/include/asm/processor.h
arch/xtensa/include/asm/ptrace.h
arch/xtensa/include/asm/smp.h
arch/xtensa/include/asm/spinlock.h
arch/xtensa/include/asm/spinlock_types.h [new file with mode: 0644]
arch/xtensa/include/asm/timex.h
arch/xtensa/include/asm/tlbflush.h
arch/xtensa/include/asm/traps.h
arch/xtensa/include/asm/vectors.h
arch/xtensa/kernel/Makefile
arch/xtensa/kernel/head.S
arch/xtensa/kernel/irq.c
arch/xtensa/kernel/mxhead.S [new file with mode: 0644]
arch/xtensa/kernel/setup.c
arch/xtensa/kernel/smp.c [new file with mode: 0644]
arch/xtensa/kernel/time.c
arch/xtensa/kernel/traps.c
arch/xtensa/kernel/vmlinux.lds.S
arch/xtensa/mm/cache.c
arch/xtensa/mm/fault.c
arch/xtensa/mm/misc.S
arch/xtensa/mm/mmu.c
arch/xtensa/mm/tlb.c
arch/xtensa/platforms/iss/network.c
arch/xtensa/platforms/xtfpga/include/platform/hardware.h
arch/xtensa/variants/s6000/include/variant/irq.h
drivers/irqchip/Kconfig
drivers/irqchip/Makefile
drivers/irqchip/irq-xtensa-mx.c [new file with mode: 0644]
drivers/irqchip/irq-xtensa-pic.c [new file with mode: 0644]
include/linux/irqchip/xtensa-mx.h [new file with mode: 0644]
include/linux/irqchip/xtensa-pic.h [new file with mode: 0644]

index 8d24dcb7cdac6df122e6779080b5e31b234a6ee5..70a160be3464aa0858e87d5c29816085308d1ef7 100644 (file)
@@ -9,7 +9,6 @@ config XTENSA
        select GENERIC_CLOCKEVENTS
        select VIRT_TO_BUS
        select GENERIC_IRQ_SHOW
-       select GENERIC_CPU_DEVICES
        select GENERIC_SCHED_CLOCK
        select MODULES_USE_ELF_RELA
        select GENERIC_PCI_IOMAP
@@ -19,6 +18,7 @@ config XTENSA
        select IRQ_DOMAIN
        select HAVE_OPROFILE
        select HAVE_FUNCTION_TRACER
+       select HAVE_IRQ_TIME_ACCOUNTING
        help
          Xtensa processors are 32-bit RISC machines designed by Tensilica
          primarily for embedded systems.  These processors are both
@@ -64,6 +64,9 @@ config MMU
 config VARIANT_IRQ_SWITCH
        def_bool n
 
+config MAY_HAVE_SMP
+       def_bool n
+
 menu "Processor type and features"
 
 choice
@@ -104,6 +107,48 @@ config XTENSA_UNALIGNED_USER
 
 source "kernel/Kconfig.preempt"
 
+config HAVE_SMP
+       bool "System Supports SMP (MX)"
+       depends on MAY_HAVE_SMP
+       select XTENSA_MX
+       help
+         This option is use to indicate that the system-on-a-chip (SOC)
+         supports Multiprocessing. Multiprocessor support implemented above
+         the CPU core definition and currently needs to be selected manually.
+
+         Multiprocessor support in implemented with external cache and
+         interrupt controlers.
+
+         The MX interrupt distributer adds Interprocessor Interrupts
+         and causes the IRQ numbers to be increased by 4 for devices
+         like the open cores ethernet driver and the serial interface.
+
+         You still have to select "Enable SMP" to enable SMP on this SOC.
+
+config SMP
+       bool "Enable Symmetric multi-processing support"
+       depends on HAVE_SMP
+       select USE_GENERIC_SMP_HELPERS
+       select GENERIC_SMP_IDLE_THREAD
+       help
+         Enabled SMP Software; allows more than one CPU/CORE
+         to be activated during startup.
+
+config NR_CPUS
+       depends on SMP
+       int "Maximum number of CPUs (2-32)"
+       range 2 32
+       default "4"
+
+config HOTPLUG_CPU
+       bool "Enable CPU hotplug support"
+       depends on SMP
+       help
+         Say Y here to allow turning CPUs off and on. CPUs can be
+         controlled through /sys/devices/system/cpu.
+
+         Say N if you want to disable CPU hotplug.
+
 config MATH_EMULATION
        bool "Math emulation"
        help
index 228d6aee3a16528f0e5972371e5a1fd6feac9a79..5851db291583face7d032c2929dd5c91b58204d6 100644 (file)
@@ -8,7 +8,6 @@ generic-y += emergency-restart.h
 generic-y += errno.h
 generic-y += exec.h
 generic-y += fcntl.h
-generic-y += futex.h
 generic-y += hardirq.h
 generic-y += ioctl.h
 generic-y += irq_regs.h
index ef021677d536538c817021c89f0841ada0ed0c30..8e5e5c980a7a4b1b01708c8160e0559f5ad63963 100644 (file)
@@ -17,7 +17,9 @@
 #define wmb() mb()
 
 #ifdef CONFIG_SMP
-#error smp_* not defined
+#define smp_mb()       mb()
+#define smp_rmb()      rmb()
+#define smp_wmb()      wmb()
 #else
 #define smp_mb()       barrier()
 #define smp_rmb()      barrier()
index 84afe58d5d374bc7cc19f8d02dbcc279b3b0014a..7b6873ae84c295740d8183b52610ba0ce91aad91 100644 (file)
 #include <asm/processor.h>
 #include <asm/byteorder.h>
 
-#ifdef CONFIG_SMP
-# error SMP not supported on this architecture
-#endif
-
-#define smp_mb__before_clear_bit()     barrier()
-#define smp_mb__after_clear_bit()      barrier()
+#define smp_mb__before_clear_bit()     smp_mb()
+#define smp_mb__after_clear_bit()      smp_mb()
 
 #include <asm-generic/bitops/non-atomic.h>
 
index 127cd48883c4274526c63631b181bea6dc366941..555a98a1845363588ced5fd3ec26a2269846e3d8 100644 (file)
@@ -1,18 +1,14 @@
 /*
- * include/asm-xtensa/cacheflush.h
- *
  * 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.
  *
- * (C) 2001 - 2007 Tensilica Inc.
+ * (C) 2001 - 2013 Tensilica Inc.
  */
 
 #ifndef _XTENSA_CACHEFLUSH_H
 #define _XTENSA_CACHEFLUSH_H
 
-#ifdef __KERNEL__
-
 #include <linux/mm.h>
 #include <asm/processor.h>
 #include <asm/page.h>
@@ -51,7 +47,6 @@ extern void __invalidate_icache_page(unsigned long);
 extern void __invalidate_icache_range(unsigned long, unsigned long);
 extern void __invalidate_dcache_range(unsigned long, unsigned long);
 
-
 #if XCHAL_DCACHE_IS_WRITEBACK
 extern void __flush_invalidate_dcache_all(void);
 extern void __flush_dcache_page(unsigned long);
@@ -87,9 +82,22 @@ static inline void __invalidate_icache_page_alias(unsigned long virt,
  * (see also Documentation/cachetlb.txt)
  */
 
-#if (DCACHE_WAY_SIZE > PAGE_SIZE)
+#if (DCACHE_WAY_SIZE > PAGE_SIZE) || defined(CONFIG_SMP)
+
+#ifdef CONFIG_SMP
+void flush_cache_all(void);
+void flush_cache_range(struct vm_area_struct*, ulong, ulong);
+void flush_icache_range(unsigned long start, unsigned long end);
+void flush_cache_page(struct vm_area_struct*,
+                            unsigned long, unsigned long);
+#else
+#define flush_cache_all local_flush_cache_all
+#define flush_cache_range local_flush_cache_range
+#define flush_icache_range local_flush_icache_range
+#define flush_cache_page  local_flush_cache_page
+#endif
 
-#define flush_cache_all()                                              \
+#define local_flush_cache_all()                                                \
        do {                                                            \
                __flush_invalidate_dcache_all();                        \
                __invalidate_icache_all();                              \
@@ -103,9 +111,11 @@ static inline void __invalidate_icache_page_alias(unsigned long virt,
 
 #define ARCH_IMPLEMENTS_FLUSH_DCACHE_PAGE 1
 extern void flush_dcache_page(struct page*);
-extern void flush_cache_range(struct vm_area_struct*, ulong, ulong);
-extern void flush_cache_page(struct vm_area_struct*,
-                            unsigned long, unsigned long);
+
+void local_flush_cache_range(struct vm_area_struct *vma,
+               unsigned long start, unsigned long end);
+void local_flush_cache_page(struct vm_area_struct *vma,
+               unsigned long address, unsigned long pfn);
 
 #else
 
@@ -119,13 +129,14 @@ extern void flush_cache_page(struct vm_area_struct*,
 #define ARCH_IMPLEMENTS_FLUSH_DCACHE_PAGE 0
 #define flush_dcache_page(page)                                do { } while (0)
 
-#define flush_cache_page(vma,addr,pfn)                 do { } while (0)
-#define flush_cache_range(vma,start,end)               do { } while (0)
+#define flush_icache_range local_flush_icache_range
+#define flush_cache_page(vma, addr, pfn)               do { } while (0)
+#define flush_cache_range(vma, start, end)             do { } while (0)
 
 #endif
 
 /* Ensure consistency between data and instruction cache. */
-#define flush_icache_range(start,end)                                  \
+#define local_flush_icache_range(start, end)                           \
        do {                                                            \
                __flush_dcache_range(start, (end) - (start));           \
                __invalidate_icache_range(start,(end) - (start));       \
@@ -253,5 +264,4 @@ static inline void flush_invalidate_dcache_unaligned(u32 addr, u32 size)
        }
 }
 
-#endif /* __KERNEL__ */
 #endif /* _XTENSA_CACHEFLUSH_H */
index 3899610c1dfff71a53e0144969fd990e59e75f62..742b89f3ca2cf22d535b21441383b97256bf5903 100644 (file)
@@ -19,9 +19,12 @@ extern unsigned long loops_per_jiffy;
 
 static inline void __delay(unsigned long loops)
 {
-       /* 2 cycles per loop. */
-       __asm__ __volatile__ ("1: addi %0, %0, -2; bgeui %0, 2, 1b"
-                             : "=r" (loops) : "0" (loops));
+       if (__builtin_constant_p(loops) && loops < 2)
+               __asm__ __volatile__ ("nop");
+       else if (loops >= 2)
+               /* 2 cycles per loop. */
+               __asm__ __volatile__ ("1: addi %0, %0, -2; bgeui %0, 2, 1b"
+                               : "+r" (loops));
 }
 
 /* For SMP/NUMA systems, change boot_cpu_data to something like
index 73cc3f482304e35f9144d451e3fefab59fe8c6c0..736b9d214d8016012393c7914d03d89fb5eacfa6 100644 (file)
@@ -18,7 +18,7 @@
                __asm__ __volatile__ ( \
                        "mov %0, a0\n" \
                        "mov %1, a1\n" \
-                       : "=r"(a0), "=r"(a1) : : ); \
+                       : "=r"(a0), "=r"(a1)); \
                MAKE_PC_FROM_RA(a0, a1); })
 #ifdef CONFIG_FRAME_POINTER
 extern unsigned long return_address(unsigned level);
diff --git a/arch/xtensa/include/asm/futex.h b/arch/xtensa/include/asm/futex.h
new file mode 100644 (file)
index 0000000..b39531b
--- /dev/null
@@ -0,0 +1,147 @@
+/*
+ * Atomic futex routines
+ *
+ * Based on the PowerPC implementataion
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Copyright (C) 2013 TangoTec Ltd.
+ *
+ * Baruch Siach <baruch@tkos.co.il>
+ */
+
+#ifndef _ASM_XTENSA_FUTEX_H
+#define _ASM_XTENSA_FUTEX_H
+
+#ifdef __KERNEL__
+
+#include <linux/futex.h>
+#include <linux/uaccess.h>
+#include <linux/errno.h>
+
+#define __futex_atomic_op(insn, ret, oldval, uaddr, oparg) \
+       __asm__ __volatile(                             \
+       "1:     l32i    %0, %2, 0\n"                    \
+               insn "\n"                               \
+       "       wsr     %0, scompare1\n"                \
+       "2:     s32c1i  %1, %2, 0\n"                    \
+       "       bne     %1, %0, 1b\n"                   \
+       "       movi    %1, 0\n"                        \
+       "3:\n"                                          \
+       "       .section .fixup,\"ax\"\n"               \
+       "       .align 4\n"                             \
+       "4:     .long   3b\n"                           \
+       "5:     l32r    %0, 4b\n"                       \
+       "       movi    %1, %3\n"                       \
+       "       jx      %0\n"                           \
+       "       .previous\n"                            \
+       "       .section __ex_table,\"a\"\n"            \
+       "       .long 1b,5b,2b,5b\n"                    \
+       "       .previous\n"                            \
+       : "=&r" (oldval), "=&r" (ret)                   \
+       : "r" (uaddr), "I" (-EFAULT), "r" (oparg)       \
+       : "memory")
+
+static inline int futex_atomic_op_inuser(int encoded_op, u32 __user *uaddr)
+{
+       int op = (encoded_op >> 28) & 7;
+       int cmp = (encoded_op >> 24) & 15;
+       int oparg = (encoded_op << 8) >> 20;
+       int cmparg = (encoded_op << 20) >> 20;
+       int oldval = 0, ret;
+       if (encoded_op & (FUTEX_OP_OPARG_SHIFT << 28))
+               oparg = 1 << oparg;
+
+       if (!access_ok(VERIFY_WRITE, uaddr, sizeof(u32)))
+               return -EFAULT;
+
+#if !XCHAL_HAVE_S32C1I
+       return -ENOSYS;
+#endif
+
+       pagefault_disable();
+
+       switch (op) {
+       case FUTEX_OP_SET:
+               __futex_atomic_op("mov %1, %4", ret, oldval, uaddr, oparg);
+               break;
+       case FUTEX_OP_ADD:
+               __futex_atomic_op("add %1, %0, %4", ret, oldval, uaddr,
+                               oparg);
+               break;
+       case FUTEX_OP_OR:
+               __futex_atomic_op("or %1, %0, %4", ret, oldval, uaddr,
+                               oparg);
+               break;
+       case FUTEX_OP_ANDN:
+               __futex_atomic_op("and %1, %0, %4", ret, oldval, uaddr,
+                               ~oparg);
+               break;
+       case FUTEX_OP_XOR:
+               __futex_atomic_op("xor %1, %0, %4", ret, oldval, uaddr,
+                               oparg);
+               break;
+       default:
+               ret = -ENOSYS;
+       }
+
+       pagefault_enable();
+
+       if (ret)
+               return ret;
+
+       switch (cmp) {
+       case FUTEX_OP_CMP_EQ: return (oldval == cmparg);
+       case FUTEX_OP_CMP_NE: return (oldval != cmparg);
+       case FUTEX_OP_CMP_LT: return (oldval < cmparg);
+       case FUTEX_OP_CMP_GE: return (oldval >= cmparg);
+       case FUTEX_OP_CMP_LE: return (oldval <= cmparg);
+       case FUTEX_OP_CMP_GT: return (oldval > cmparg);
+       }
+
+       return -ENOSYS;
+}
+
+static inline int
+futex_atomic_cmpxchg_inatomic(u32 *uval, u32 __user *uaddr,
+                             u32 oldval, u32 newval)
+{
+       int ret = 0;
+       u32 prev;
+
+       if (!access_ok(VERIFY_WRITE, uaddr, sizeof(u32)))
+               return -EFAULT;
+
+#if !XCHAL_HAVE_S32C1I
+       return -ENOSYS;
+#endif
+
+       __asm__ __volatile__ (
+       "       # futex_atomic_cmpxchg_inatomic\n"
+       "1:     l32i    %1, %3, 0\n"
+       "       mov     %0, %5\n"
+       "       wsr     %1, scompare1\n"
+       "2:     s32c1i  %0, %3, 0\n"
+       "3:\n"
+       "       .section .fixup,\"ax\"\n"
+       "       .align 4\n"
+       "4:     .long   3b\n"
+       "5:     l32r    %1, 4b\n"
+       "       movi    %0, %6\n"
+       "       jx      %1\n"
+       "       .previous\n"
+       "       .section __ex_table,\"a\"\n"
+       "       .long 1b,5b,2b,5b\n"
+       "       .previous\n"
+       : "+r" (ret), "=&r" (prev), "+m" (*uaddr)
+       : "r" (uaddr), "r" (oldval), "r" (newval), "I" (-EFAULT)
+       : "memory");
+
+       *uval = prev;
+       return ret;
+}
+
+#endif /* __KERNEL__ */
+#endif /* _ASM_XTENSA_FUTEX_H */
index 4c0ccc9c4f4c0adf0a8ac6212818567749147fed..f71f88ea7646dcc798067e984dddefa8a659037d 100644 (file)
@@ -43,5 +43,14 @@ static __inline__ int irq_canonicalize(int irq)
 }
 
 struct irqaction;
+struct irq_domain;
+
+void migrate_irqs(void);
+int xtensa_irq_domain_xlate(const u32 *intspec, unsigned int intsize,
+               unsigned long int_irq, unsigned long ext_irq,
+               unsigned long *out_hwirq, unsigned int *out_type);
+int xtensa_irq_map(struct irq_domain *d, unsigned int irq, irq_hw_number_t hw);
+unsigned xtensa_map_ext_irq(unsigned ext_irq);
+unsigned xtensa_get_ext_irq_no(unsigned irq);
 
 #endif /* _XTENSA_IRQ_H */
index 8554b2c8b17a0128bc873f2d67b194462f89c2bf..71afe418d0e53e2d9239b261cd34a2efc8d4f9ac 100644 (file)
@@ -1,11 +1,9 @@
 /*
- * include/asm-xtensa/mmu.h
- *
  * 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.
  *
- * Copyright (C) 2001 - 2005 Tensilica Inc.
+ * Copyright (C) 2001 - 2013 Tensilica Inc.
  */
 
 #ifndef _XTENSA_MMU_H
 #include <asm-generic/mmu.h>
 #else
 
-/* Default "unsigned long" context */
-typedef unsigned long mm_context_t;
+typedef struct {
+       unsigned long asid[NR_CPUS];
+       unsigned int cpu;
+} mm_context_t;
 
 #endif /* CONFIG_MMU */
 #endif /* _XTENSA_MMU_H */
index d43525a286bbf248b43e36fb878f6ee7fb31fa1e..d33c71a8c9ec69285fc508e653a0acfee3a691fa 100644 (file)
@@ -1,13 +1,11 @@
 /*
- * include/asm-xtensa/mmu_context.h
- *
  * Switch an MMU context.
  *
  * 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.
  *
- * Copyright (C) 2001 - 2005 Tensilica Inc.
+ * Copyright (C) 2001 - 2013 Tensilica Inc.
  */
 
 #ifndef _XTENSA_MMU_CONTEXT_H
 #include <linux/stringify.h>
 #include <linux/sched.h>
 
-#include <variant/core.h>
+#include <asm/vectors.h>
 
 #include <asm/pgtable.h>
 #include <asm/cacheflush.h>
 #include <asm/tlbflush.h>
 #include <asm-generic/mm_hooks.h>
+#include <asm-generic/percpu.h>
 
 #if (XCHAL_HAVE_TLBS != 1)
 # error "Linux must have an MMU!"
 #endif
 
-extern unsigned long asid_cache;
+DECLARE_PER_CPU(unsigned long, asid_cache);
+#define cpu_asid_cache(cpu) per_cpu(asid_cache, cpu)
 
 /*
  * NO_CONTEXT is the invalid ASID value that we don't ever assign to
- * any user or kernel context.
+ * any user or kernel context.  We use the reserved values in the
+ * ASID_INSERT macro below.
  *
  * 0 invalid
  * 1 kernel
@@ -49,6 +50,12 @@ extern unsigned long asid_cache;
 #define ASID_MASK      ((1 << XCHAL_MMU_ASID_BITS) - 1)
 #define ASID_INSERT(x) (0x03020001 | (((x) & ASID_MASK) << 8))
 
+#ifdef CONFIG_MMU
+void init_mmu(void);
+#else
+static inline void init_mmu(void) { }
+#endif
+
 static inline void set_rasid_register (unsigned long val)
 {
        __asm__ __volatile__ (" wsr %0, rasid\n\t"
@@ -62,64 +69,77 @@ static inline unsigned long get_rasid_register (void)
        return tmp;
 }
 
-static inline void
-__get_new_mmu_context(struct mm_struct *mm)
+static inline void get_new_mmu_context(struct mm_struct *mm, unsigned int cpu)
 {
-       extern void flush_tlb_all(void);
-       if (! (++asid_cache & ASID_MASK) ) {
-               flush_tlb_all(); /* start new asid cycle */
-               asid_cache += ASID_USER_FIRST;
+       unsigned long asid = cpu_asid_cache(cpu);
+       if ((++asid & ASID_MASK) == 0) {
+               /*
+                * Start new asid cycle; continue counting with next
+                * incarnation bits; skipping over 0, 1, 2, 3.
+                */
+               local_flush_tlb_all();
+               asid += ASID_USER_FIRST;
        }
-       mm->context = asid_cache;
+       cpu_asid_cache(cpu) = asid;
+       mm->context.asid[cpu] = asid;
+       mm->context.cpu = cpu;
 }
 
-static inline void
-__load_mmu_context(struct mm_struct *mm)
+static inline void get_mmu_context(struct mm_struct *mm, unsigned int cpu)
 {
-       set_rasid_register(ASID_INSERT(mm->context));
+       /*
+        * Check if our ASID is of an older version and thus invalid.
+        */
+
+       if (mm) {
+               unsigned long asid = mm->context.asid[cpu];
+
+               if (asid == NO_CONTEXT ||
+                               ((asid ^ cpu_asid_cache(cpu)) & ~ASID_MASK))
+                       get_new_mmu_context(mm, cpu);
+       }
+}
+
+static inline void activate_context(struct mm_struct *mm, unsigned int cpu)
+{
+       get_mmu_context(mm, cpu);
+       set_rasid_register(ASID_INSERT(mm->context.asid[cpu]));
        invalidate_page_directory();
 }
 
 /*
  * Initialize the context related info for a new mm_struct
- * instance.
+ * instance.  Valid cpu values are 0..(NR_CPUS-1), so initializing
+ * to -1 says the process has never run on any core.
  */
 
-static inline int
-init_new_context(struct task_struct *tsk, struct mm_struct *mm)
+static inline int init_new_context(struct task_struct *tsk,
+               struct mm_struct *mm)
 {
-       mm->context = NO_CONTEXT;
+       int cpu;
+       for_each_possible_cpu(cpu) {
+               mm->context.asid[cpu] = NO_CONTEXT;
+       }
+       mm->context.cpu = -1;
        return 0;
 }
 
-/*
- * After we have set current->mm to a new value, this activates
- * the context for the new mm so we see the new mappings.
- */
-static inline void
-activate_mm(struct mm_struct *prev, struct mm_struct *next)
-{
-       /* Unconditionally get a new ASID.  */
-
-       __get_new_mmu_context(next);
-       __load_mmu_context(next);
-}
-
-
 static inline void switch_mm(struct mm_struct *prev, struct mm_struct *next,
                             struct task_struct *tsk)
 {
-       unsigned long asid = asid_cache;
-
-       /* Check if our ASID is of an older version and thus invalid */
-
-       if (next->context == NO_CONTEXT || ((next->context^asid) & ~ASID_MASK))
-               __get_new_mmu_context(next);
-
-       __load_mmu_context(next);
+       unsigned int cpu = smp_processor_id();
+       int migrated = next->context.cpu != cpu;
+       /* Flush the icache if we migrated to a new core. */
+       if (migrated) {
+               __invalidate_icache_all();
+               next->context.cpu = cpu;
+       }
+       if (migrated || prev != next)
+               activate_context(next, cpu);
 }
 
-#define deactivate_mm(tsk, mm) do { } while(0)
+#define activate_mm(prev, next)        switch_mm((prev), (next), NULL)
+#define deactivate_mm(tsk, mm) do { } while (0)
 
 /*
  * Destroy context related info for an mm_struct that is about
diff --git a/arch/xtensa/include/asm/mxregs.h b/arch/xtensa/include/asm/mxregs.h
new file mode 100644 (file)
index 0000000..73dcc54
--- /dev/null
@@ -0,0 +1,46 @@
+/*
+ * Xtensa MX interrupt distributor
+ *
+ * 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.
+ *
+ * Copyright (C) 2008 - 2013 Tensilica Inc.
+ */
+
+#ifndef _XTENSA_MXREGS_H
+#define _XTENSA_MXREGS_H
+
+/*
+ * RER/WER at, as      Read/write external register
+ *     at: value
+ *     as: address
+ *
+ * Address     Value
+ * 00nn                0...0p..p       Interrupt Routing, route IRQ n to processor p
+ * 01pp                0...0d..d       16 bits (d) 'ored' as single IPI to processor p
+ * 0180                0...0m..m       Clear enable specified by mask (m)
+ * 0184                0...0m..m       Set enable specified by mask (m)
+ * 0190                0...0x..x       8-bit IPI partition register
+ *                             VVVVVVVVPPPPUUUUUUUUUUUUUUUUU
+ *                             V (10-bit) Release/Version
+ *                             P ( 4-bit) Number of cores - 1
+ *                             U (18-bit) ID
+ * 01a0                i.......i       32-bit ConfigID
+ * 0200                0...0m..m       RunStall core 'n'
+ * 0220                c               Cache coherency enabled
+ */
+
+#define MIROUT(irq)    (0x000 + (irq))
+#define MIPICAUSE(cpu) (0x100 + (cpu))
+#define MIPISET(cause) (0x140 + (cause))
+#define MIENG          0x180
+#define MIENGSET       0x184
+#define MIASG          0x188   /* Read Global Assert Register */
+#define MIASGSET       0x18c   /* Set Global Addert Regiter */
+#define MIPIPART       0x190
+#define SYSCFGID       0x1a0
+#define MPSCORE                0x200
+#define CCON           0x220
+
+#endif /* _XTENSA_MXREGS_H */
index 7e409a5b0ec50d8bf38848ff54b9ec4d058141dc..abb59708a3b75bf6ac8398be37e4fc90c349dff0 100644 (file)
@@ -191,5 +191,25 @@ extern unsigned long get_wchan(struct task_struct *p);
 #define set_sr(x,sr) ({unsigned int v=(unsigned int)x; WSR(v,sr);})
 #define get_sr(sr) ({unsigned int v; RSR(v,sr); v; })
 
+#ifndef XCHAL_HAVE_EXTERN_REGS
+#define XCHAL_HAVE_EXTERN_REGS 0
+#endif
+
+#if XCHAL_HAVE_EXTERN_REGS
+
+static inline void set_er(unsigned long value, unsigned long addr)
+{
+       asm volatile ("wer %0, %1" : : "a" (value), "a" (addr) : "memory");
+}
+
+static inline unsigned long get_er(unsigned long addr)
+{
+       register unsigned long value;
+       asm volatile ("rer %0, %1" : "=a" (value) : "a" (addr) : "memory");
+       return value;
+}
+
+#endif /* XCHAL_HAVE_EXTERN_REGS */
+
 #endif /* __ASSEMBLY__ */
 #endif /* _XTENSA_PROCESSOR_H */
index 81f31bc9dde0a857a95e9f794e09e84fa6f6c1d7..598e752dcbcd009392230869af2851afd14e4085 100644 (file)
@@ -59,9 +59,17 @@ struct pt_regs {
        (task_stack_page(tsk) + KERNEL_STACK_SIZE - (XCHAL_NUM_AREGS-16)*4) - 1)
 # define user_mode(regs) (((regs)->ps & 0x00000020)!=0)
 # define instruction_pointer(regs) ((regs)->pc)
+# define return_pointer(regs) (MAKE_PC_FROM_RA((regs)->areg[0], \
+                                              (regs)->areg[1]))
 
 # ifndef CONFIG_SMP
 #  define profile_pc(regs) instruction_pointer(regs)
+# else
+#  define profile_pc(regs)                                             \
+       ({                                                              \
+               in_lock_functions(instruction_pointer(regs)) ?          \
+               return_pointer(regs) : instruction_pointer(regs);       \
+       })
 # endif
 
 #define user_stack_pointer(regs) ((regs)->areg[1])
index 83c569e3bdbd23245dccf2e3ea7e92010589eafe..4e43f564389161687441c59168acb0e2f638b45b 100644 (file)
@@ -1,27 +1,43 @@
 /*
- * include/asm-xtensa/smp.h
- *
  * 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.
  *
- * Copyright (C) 2001 - 2005 Tensilica Inc.
+ * Copyright (C) 2001 - 2013 Tensilica Inc.
  */
 
 #ifndef _XTENSA_SMP_H
 #define _XTENSA_SMP_H
 
-extern struct xtensa_cpuinfo boot_cpu_data;
+#ifdef CONFIG_SMP
 
-#define cpu_data (&boot_cpu_data)
-#define current_cpu_data boot_cpu_data
+#define raw_smp_processor_id() (current_thread_info()->cpu)
+#define cpu_logical_map(cpu)   (cpu)
 
-struct xtensa_cpuinfo {
-       unsigned long   *pgd_cache;
-       unsigned long   *pte_cache;
-       unsigned long   pgtable_cache_sz;
+struct start_info {
+       unsigned long stack;
 };
+extern struct start_info start_info;
 
-#define cpu_logical_map(cpu)   (cpu)
+struct cpumask;
+void arch_send_call_function_ipi_mask(const struct cpumask *mask);
+void arch_send_call_function_single_ipi(int cpu);
+
+void smp_init_cpus(void);
+void secondary_init_irq(void);
+void ipi_init(void);
+struct seq_file;
+void show_ipi_list(struct seq_file *p, int prec);
+
+#ifdef CONFIG_HOTPLUG_CPU
+
+void __cpu_die(unsigned int cpu);
+int __cpu_disable(void);
+void cpu_die(void);
+void cpu_restart(void);
+
+#endif /* CONFIG_HOTPLUG_CPU */
+
+#endif /* CONFIG_SMP */
 
 #endif /* _XTENSA_SMP_H */
index 03975906b36fc34dc6a47abbe979c899ddc4f3e0..1d95fa5dcd10f0121f394e22fb108682c17741aa 100644 (file)
  *    1         somebody owns the spinlock
  */
 
-#define __raw_spin_is_locked(x) ((x)->slock != 0)
-#define __raw_spin_unlock_wait(lock) \
-       do { while (__raw_spin_is_locked(lock)) cpu_relax(); } while (0)
+#define arch_spin_is_locked(x) ((x)->slock != 0)
+#define arch_spin_unlock_wait(lock) \
+       do { while (arch_spin_is_locked(lock)) cpu_relax(); } while (0)
 
-#define __raw_spin_lock_flags(lock, flags) __raw_spin_lock(lock)
+#define arch_spin_lock_flags(lock, flags) arch_spin_lock(lock)
 
-static inline void __raw_spin_lock(raw_spinlock_t *lock)
+static inline void arch_spin_lock(arch_spinlock_t *lock)
 {
        unsigned long tmp;
 
@@ -51,7 +51,7 @@ static inline void __raw_spin_lock(raw_spinlock_t *lock)
 
 /* Returns 1 if the lock is obtained, 0 otherwise. */
 
-static inline int __raw_spin_trylock(raw_spinlock_t *lock)
+static inline int arch_spin_trylock(arch_spinlock_t *lock)
 {
        unsigned long tmp;
 
@@ -67,7 +67,7 @@ static inline int __raw_spin_trylock(raw_spinlock_t *lock)
        return tmp == 0 ? 1 : 0;
 }
 
-static inline void __raw_spin_unlock(raw_spinlock_t *lock)
+static inline void arch_spin_unlock(arch_spinlock_t *lock)
 {
        unsigned long tmp;
 
@@ -96,9 +96,9 @@ static inline void __raw_spin_unlock(raw_spinlock_t *lock)
  *  0x80000000  one writer owns the rwlock, no other writers, no readers
  */
 
-#define __raw_write_can_lock(x)  ((x)->lock == 0)
+#define arch_write_can_lock(x)  ((x)->lock == 0)
 
-static inline void __raw_write_lock(raw_rwlock_t *rw)
+static inline void arch_write_lock(arch_rwlock_t *rw)
 {
        unsigned long tmp;
 
@@ -116,7 +116,7 @@ static inline void __raw_write_lock(raw_rwlock_t *rw)
 
 /* Returns 1 if the lock is obtained, 0 otherwise. */
 
-static inline int __raw_write_trylock(raw_rwlock_t *rw)
+static inline int arch_write_trylock(arch_rwlock_t *rw)
 {
        unsigned long tmp;
 
@@ -133,7 +133,7 @@ static inline int __raw_write_trylock(raw_rwlock_t *rw)
        return tmp == 0 ? 1 : 0;
 }
 
-static inline void __raw_write_unlock(raw_rwlock_t *rw)
+static inline void arch_write_unlock(arch_rwlock_t *rw)
 {
        unsigned long tmp;
 
@@ -145,7 +145,7 @@ static inline void __raw_write_unlock(raw_rwlock_t *rw)
                        : "memory");
 }
 
-static inline void __raw_read_lock(raw_rwlock_t *rw)
+static inline void arch_read_lock(arch_rwlock_t *rw)
 {
        unsigned long tmp;
        unsigned long result;
@@ -164,7 +164,7 @@ static inline void __raw_read_lock(raw_rwlock_t *rw)
 
 /* Returns 1 if the lock is obtained, 0 otherwise. */
 
-static inline int __raw_read_trylock(raw_rwlock_t *rw)
+static inline int arch_read_trylock(arch_rwlock_t *rw)
 {
        unsigned long result;
        unsigned long tmp;
@@ -184,7 +184,7 @@ static inline int __raw_read_trylock(raw_rwlock_t *rw)
        return result == 0;
 }
 
-static inline void __raw_read_unlock(raw_rwlock_t *rw)
+static inline void arch_read_unlock(arch_rwlock_t *rw)
 {
        unsigned long tmp1, tmp2;
 
@@ -199,4 +199,7 @@ static inline void __raw_read_unlock(raw_rwlock_t *rw)
                        : "memory");
 }
 
+#define arch_read_lock_flags(lock, flags)      arch_read_lock(lock)
+#define arch_write_lock_flags(lock, flags)     arch_write_lock(lock)
+
 #endif /* _XTENSA_SPINLOCK_H */
diff --git a/arch/xtensa/include/asm/spinlock_types.h b/arch/xtensa/include/asm/spinlock_types.h
new file mode 100644 (file)
index 0000000..7ec5ce1
--- /dev/null
@@ -0,0 +1,20 @@
+#ifndef __ASM_SPINLOCK_TYPES_H
+#define __ASM_SPINLOCK_TYPES_H
+
+#ifndef __LINUX_SPINLOCK_TYPES_H
+# error "please don't include this file directly"
+#endif
+
+typedef struct {
+       volatile unsigned int slock;
+} arch_spinlock_t;
+
+#define __ARCH_SPIN_LOCK_UNLOCKED      { 0 }
+
+typedef struct {
+       volatile unsigned int lock;
+} arch_rwlock_t;
+
+#define __ARCH_RW_LOCK_UNLOCKED                { 0 }
+
+#endif
index 27fa3c1706623805168f6914f952d777993edbf2..ca929e6a38b5f70a5e1e2d1492b1cdde5e5f0e2a 100644 (file)
@@ -1,18 +1,14 @@
 /*
- * include/asm-xtensa/timex.h
- *
  * 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.
  *
- * Copyright (C) 2001 - 2008 Tensilica Inc.
+ * Copyright (C) 2001 - 2013 Tensilica Inc.
  */
 
 #ifndef _XTENSA_TIMEX_H
 #define _XTENSA_TIMEX_H
 
-#ifdef __KERNEL__
-
 #include <asm/processor.h>
 #include <linux/stringify.h>
 
@@ -39,14 +35,9 @@ extern unsigned long ccount_freq;
 
 typedef unsigned long long cycles_t;
 
-/*
- * Only used for SMP.
- */
-
-extern cycles_t cacheflush_time;
-
 #define get_cycles()   (0)
 
+void local_timer_setup(unsigned cpu);
 
 /*
  * Register access.
@@ -81,5 +72,4 @@ static inline void set_linux_timer (unsigned long ccompare)
        WSR_CCOMPARE(LINUX_TIMER, ccompare);
 }
 
-#endif /* __KERNEL__ */
 #endif /* _XTENSA_TIMEX_H */
index 43dd348a5a478d27daa70f2e8b019b8cfa0fcd22..fc34274ce41bc81b3ddaa167fe887179f04c8ad7 100644 (file)
@@ -1,18 +1,14 @@
 /*
- * include/asm-xtensa/tlbflush.h
- *
  * 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.
  *
- * Copyright (C) 2001 - 2005 Tensilica Inc.
+ * Copyright (C) 2001 - 2013 Tensilica Inc.
  */
 
 #ifndef _XTENSA_TLBFLUSH_H
 #define _XTENSA_TLBFLUSH_H
 
-#ifdef __KERNEL__
-
 #include <linux/stringify.h>
 #include <asm/processor.h>
 
  *  - flush_tlb_range(mm, start, end) flushes a range of pages
  */
 
-extern void flush_tlb_all(void);
-extern void flush_tlb_mm(struct mm_struct*);
-extern void flush_tlb_page(struct vm_area_struct*,unsigned long);
-extern void flush_tlb_range(struct vm_area_struct*,unsigned long,unsigned long);
+void local_flush_tlb_all(void);
+void local_flush_tlb_mm(struct mm_struct *mm);
+void local_flush_tlb_page(struct vm_area_struct *vma,
+               unsigned long page);
+void local_flush_tlb_range(struct vm_area_struct *vma,
+               unsigned long start, unsigned long end);
+
+#ifdef CONFIG_SMP
+
+void flush_tlb_all(void);
+void flush_tlb_mm(struct mm_struct *);
+void flush_tlb_page(struct vm_area_struct *, unsigned long);
+void flush_tlb_range(struct vm_area_struct *, unsigned long,
+               unsigned long);
+
+static inline void flush_tlb_kernel_range(unsigned long start,
+               unsigned long end)
+{
+       flush_tlb_all();
+}
+
+#else /* !CONFIG_SMP */
+
+#define flush_tlb_all()                           local_flush_tlb_all()
+#define flush_tlb_mm(mm)                  local_flush_tlb_mm(mm)
+#define flush_tlb_page(vma, page)         local_flush_tlb_page(vma, page)
+#define flush_tlb_range(vma, vmaddr, end)  local_flush_tlb_range(vma, vmaddr, \
+                                                                end)
+#define flush_tlb_kernel_range(start, end) local_flush_tlb_all()
 
-#define flush_tlb_kernel_range(start,end) flush_tlb_all()
+#endif /* CONFIG_SMP */
 
 /* TLB operations. */
 
@@ -187,5 +208,4 @@ static inline unsigned long read_itlb_translation (int way)
 }
 
 #endif /* __ASSEMBLY__ */
-#endif /* __KERNEL__ */
 #endif /* _XTENSA_TLBFLUSH_H */
index 917488a0ab00f12b9c49ac7e13b72ea5ea28eecb..8c194f6af45e09932976b3f71d8d70a8f556488f 100644 (file)
@@ -19,6 +19,7 @@
  */
 extern void * __init trap_set_handler(int cause, void *handler);
 extern void do_unhandled(struct pt_regs *regs, unsigned long exccause);
+void secondary_trap_init(void);
 
 static inline void spill_registers(void)
 {
index c52b656d0310cb994085844a9b90e32543b36d5b..37e073b21a50e7d0d713ca002fa745ebe3305349 100644 (file)
 
 #if defined(XCHAL_HAVE_PTP_MMU) && XCHAL_HAVE_PTP_MMU && XCHAL_HAVE_SPANNING_WAY
   /* MMU v3  - XCHAL_HAVE_PTP_MMU  == 1 */
-  #define PHYSICAL_MEMORY_ADDRESS      0x00000000
   #define LOAD_MEMORY_ADDRESS          0x00003000
 #else
   /* MMU V2 -  XCHAL_HAVE_PTP_MMU  == 0 */
-  #define PHYSICAL_MEMORY_ADDRESS      0xD0000000
   #define LOAD_MEMORY_ADDRESS          0xD0003000
 #endif
 
@@ -46,7 +44,6 @@
 
   /* Location of the start of the kernel text, _start */
   #define KERNELOFFSET                 0x00003000
-  #define PHYSICAL_MEMORY_ADDRESS      0x00000000
 
   /* Loaded just above possibly live vectors */
   #define LOAD_MEMORY_ADDRESS          0x00003000
@@ -54,7 +51,6 @@
 #endif /* CONFIG_MMU */
 
 #define XC_VADDR(offset)               (VIRTUAL_MEMORY_ADDRESS  + offset)
-#define XC_PADDR(offset)               (PHYSICAL_MEMORY_ADDRESS + offset)
 
 /* Used to set VECBASE register */
 #define VECBASE_RESET_VADDR            VIRTUAL_MEMORY_ADDRESS
@@ -67,7 +63,7 @@
                                                VECBASE_RESET_VADDR)
 #define RESET_VECTOR1_VADDR            XC_VADDR(RESET_VECTOR1_VECOFS)
 
-#if XCHAL_HAVE_VECBASE
+#if defined(XCHAL_HAVE_VECBASE) && XCHAL_HAVE_VECBASE
 
 #define USER_VECTOR_VADDR              XC_VADDR(XCHAL_USER_VECOFS)
 #define KERNEL_VECTOR_VADDR            XC_VADDR(XCHAL_KERNEL_VECOFS)
 
 #define DEBUG_VECTOR_VADDR             XC_VADDR(XCHAL_DEBUG_VECOFS)
 
-#undef  XCHAL_NMI_VECTOR_VADDR
-#define XCHAL_NMI_VECTOR_VADDR         XC_VADDR(XCHAL_NMI_VECOFS)
+#define NMI_VECTOR_VADDR               XC_VADDR(XCHAL_NMI_VECOFS)
 
-#undef  XCHAL_INTLEVEL7_VECTOR_VADDR
-#define XCHAL_INTLEVEL7_VECTOR_VADDR   XC_VADDR(XCHAL_INTLEVEL7_VECOFS)
+#define INTLEVEL7_VECTOR_VADDR         XC_VADDR(XCHAL_INTLEVEL7_VECOFS)
 
 /*
  * These XCHAL_* #defines from varian/core.h
index f90265ec1ccca1bd5fed0849ffb708d6f7d965e8..18d962a8c0c2d1fccc5411fadbdc087bb5b05573 100644 (file)
@@ -12,6 +12,7 @@ obj-$(CONFIG_KGDB) += xtensa-stub.o
 obj-$(CONFIG_PCI) += pci.o
 obj-$(CONFIG_MODULES) += xtensa_ksyms.o module.o
 obj-$(CONFIG_FUNCTION_TRACER) += mcount.o
+obj-$(CONFIG_SMP) += smp.o mxhead.o
 
 AFLAGS_head.o += -mtext-section-literals
 
index 7d740ebbe198ec77a8011601f4172ebc809b9be8..aeeb3cc8a4109e59bc6ce5f23fd88c21c0450f3f 100644 (file)
@@ -19,6 +19,7 @@
 #include <asm/page.h>
 #include <asm/cacheasm.h>
 #include <asm/initialize_mmu.h>
+#include <asm/mxregs.h>
 
 #include <linux/init.h>
 #include <linux/linkage.h>
@@ -54,7 +55,7 @@ ENTRY(_start)
 
        /* Preserve the pointer to the boot parameter list in EXCSAVE_1 */
        wsr     a2, excsave1
-       _j      _SetupMMU
+       _j      _SetupOCD
 
        .align  4
        .literal_position
@@ -62,6 +63,23 @@ ENTRY(_start)
        .word   _startup
 
        .align  4
+_SetupOCD:
+       /*
+        * Initialize WB, WS, and clear PS.EXCM (to allow loop instructions).
+        * Set Interrupt Level just below XCHAL_DEBUGLEVEL to allow
+        * xt-gdb to single step via DEBUG exceptions received directly
+        * by ocd.
+        */
+       movi    a1, 1
+       movi    a0, 0
+       wsr     a1, windowstart
+       wsr     a0, windowbase
+       rsync
+
+       movi    a1, LOCKLEVEL
+       wsr     a1, ps
+       rsync
+
        .global _SetupMMU
 _SetupMMU:
        Offset = _SetupMMU - _start
@@ -85,24 +103,11 @@ _SetupMMU:
 
 ENDPROC(_start)
 
-       __INIT
+       __REF
        .literal_position
 
 ENTRY(_startup)
 
-       /* Disable interrupts and exceptions. */
-
-       movi    a0, LOCKLEVEL
-       wsr     a0, ps
-
-       /* Start with a fresh windowbase and windowstart.  */
-
-       movi    a1, 1
-       movi    a0, 0
-       wsr     a1, windowstart
-       wsr     a0, windowbase
-       rsync
-
        /* Set a0 to 0 for the remaining initialization. */
 
        movi    a0, 0
@@ -154,17 +159,6 @@ ENTRY(_startup)
        wsr     a0, cpenable
 #endif
 
-       /* Set PS.INTLEVEL=LOCKLEVEL, PS.WOE=0, kernel stack, PS.EXCM=0
-        *
-        * Note: PS.EXCM must be cleared before using any loop
-        *       instructions; otherwise, they are silently disabled, and
-        *       at most one iteration of the loop is executed.
-        */
-
-       movi    a1, LOCKLEVEL
-       wsr     a1, ps
-       rsync
-
        /*  Initialize the caches.
         *  a2, a3 are just working registers (clobbered).
         */
@@ -182,6 +176,37 @@ ENTRY(_startup)
 
        isync
 
+#ifdef CONFIG_HAVE_SMP
+       movi    a2, CCON        # MX External Register to Configure Cache
+       movi    a3, 1
+       wer     a3, a2
+#endif
+
+       /* Setup stack and enable window exceptions (keep irqs disabled) */
+
+       movi    a1, start_info
+       l32i    a1, a1, 0
+
+       movi    a2, (1 << PS_WOE_BIT) | LOCKLEVEL
+                                       # WOE=1, INTLEVEL=LOCKLEVEL, UM=0
+       wsr     a2, ps                  # (enable reg-windows; progmode stack)
+       rsync
+
+       /* Set up EXCSAVE[DEBUGLEVEL] to point to the Debug Exception Handler.*/
+
+       movi    a2, debug_exception
+       wsr     a2, SREG_EXCSAVE + XCHAL_DEBUGLEVEL
+
+#ifdef CONFIG_SMP
+       /*
+        * Notice that we assume with SMP that cores have PRID
+        * supported by the cores.
+        */
+       rsr     a2, prid
+       bnez    a2, .Lboot_secondary
+
+#endif  /* CONFIG_SMP */
+
        /* Unpack data sections
         *
         * The linker script used to build the Linux kernel image
@@ -234,24 +259,7 @@ ENTRY(_startup)
        ___invalidate_icache_all a2 a3
        isync
 
-       /* Setup stack and enable window exceptions (keep irqs disabled) */
-
-       movi    a1, init_thread_union
-       addi    a1, a1, KERNEL_STACK_SIZE
-
-       movi    a2, (1 << PS_WOE_BIT) | LOCKLEVEL
-                                       # WOE=1, INTLEVEL=LOCKLEVEL, UM=0
-       wsr     a2, ps                  # (enable reg-windows; progmode stack)
-       rsync
-
-       /* Set up EXCSAVE[DEBUGLEVEL] to point to the Debug Exception Handler.*/
-
-       movi    a2, debug_exception
-       wsr     a2, SREG_EXCSAVE + XCHAL_DEBUGLEVEL
-
-       /* Set up EXCSAVE[1] to point to the exc_table. */
-
-       movi    a6, exc_table
+       movi    a6, 0
        xsr     a6, excsave1
 
        /* init_arch kick-starts the linux kernel */
@@ -265,8 +273,93 @@ ENTRY(_startup)
 should_never_return:
        j       should_never_return
 
+#ifdef CONFIG_SMP
+.Lboot_secondary:
+
+       movi    a2, cpu_start_ccount
+1:
+       l32i    a3, a2, 0
+       beqi    a3, 0, 1b
+       movi    a3, 0
+       s32i    a3, a2, 0
+       memw
+1:
+       l32i    a3, a2, 0
+       beqi    a3, 0, 1b
+       wsr     a3, ccount
+       movi    a3, 0
+       s32i    a3, a2, 0
+       memw
+
+       movi    a6, 0
+       wsr     a6, excsave1
+
+       movi    a4, secondary_start_kernel
+       callx4  a4
+       j       should_never_return
+
+#endif  /* CONFIG_SMP */
+
 ENDPROC(_startup)
 
+#ifdef CONFIG_HOTPLUG_CPU
+
+ENTRY(cpu_restart)
+
+#if XCHAL_DCACHE_IS_WRITEBACK
+       ___flush_invalidate_dcache_all a2 a3
+#else
+       ___invalidate_dcache_all a2 a3
+#endif
+       memw
+       movi    a2, CCON        # MX External Register to Configure Cache
+       movi    a3, 0
+       wer     a3, a2
+       extw
+
+       rsr     a0, prid
+       neg     a2, a0
+       movi    a3, cpu_start_id
+       s32i    a2, a3, 0
+#if XCHAL_DCACHE_IS_WRITEBACK
+       dhwbi   a3, 0
+#endif
+1:
+       l32i    a2, a3, 0
+       dhi     a3, 0
+       bne     a2, a0, 1b
+
+       /*
+        * Initialize WB, WS, and clear PS.EXCM (to allow loop instructions).
+        * Set Interrupt Level just below XCHAL_DEBUGLEVEL to allow
+        * xt-gdb to single step via DEBUG exceptions received directly
+        * by ocd.
+        */
+       movi    a1, 1
+       movi    a0, 0
+       wsr     a1, windowstart
+       wsr     a0, windowbase
+       rsync
+
+       movi    a1, LOCKLEVEL
+       wsr     a1, ps
+       rsync
+
+       j       _startup
+
+ENDPROC(cpu_restart)
+
+#endif  /* CONFIG_HOTPLUG_CPU */
+
+/*
+ * DATA section
+ */
+
+        .section ".data.init.refok"
+        .align  4
+ENTRY(start_info)
+        .long   init_thread_union + KERNEL_STACK_SIZE
+
 /*
  * BSS section
  */
index 6f4f9749cff773f229a76a35bf50373ec18731ab..482868a2de6ebde7995002509445859bf7a002b0 100644 (file)
@@ -4,7 +4,7 @@
  * Xtensa built-in interrupt controller and some generic functions copied
  * from i386.
  *
- * Copyright (C) 2002 - 2006 Tensilica, Inc.
+ * Copyright (C) 2002 - 2013 Tensilica, Inc.
  * Copyright (C) 1992, 1998 Linus Torvalds, Ingo Molnar
  *
  *
 #include <linux/interrupt.h>
 #include <linux/irq.h>
 #include <linux/kernel_stat.h>
+#include <linux/irqchip.h>
+#include <linux/irqchip/xtensa-mx.h>
+#include <linux/irqchip/xtensa-pic.h>
 #include <linux/irqdomain.h>
 #include <linux/of.h>
 
+#include <asm/mxregs.h>
 #include <asm/uaccess.h>
 #include <asm/platform.h>
 
-static unsigned int cached_irq_mask;
-
 atomic_t irq_err_count;
 
-static struct irq_domain *root_domain;
-
-/*
- * do_IRQ handles all normal device IRQ's (the special
- * SMP cross-CPU interrupts have their own specific
- * handlers).
- */
-
 asmlinkage void do_IRQ(int hwirq, struct pt_regs *regs)
 {
-       struct pt_regs *old_regs = set_irq_regs(regs);
-       int irq = irq_find_mapping(root_domain, hwirq);
+       int irq = irq_find_mapping(NULL, hwirq);
 
        if (hwirq >= NR_IRQS) {
                printk(KERN_EMERG "%s: cannot handle IRQ %d\n",
                                __func__, hwirq);
        }
 
-       irq_enter();
-
 #ifdef CONFIG_DEBUG_STACKOVERFLOW
        /* Debugging check for stack overflow: is there less than 1KB free? */
        {
@@ -62,95 +53,69 @@ asmlinkage void do_IRQ(int hwirq, struct pt_regs *regs)
        }
 #endif
        generic_handle_irq(irq);
-
-       irq_exit();
-       set_irq_regs(old_regs);
 }
 
 int arch_show_interrupts(struct seq_file *p, int prec)
 {
+#ifdef CONFIG_SMP
+       show_ipi_list(p, prec);
+#endif
        seq_printf(p, "%*s: ", prec, "ERR");
        seq_printf(p, "%10u\n", atomic_read(&irq_err_count));
        return 0;
 }
 
-static void xtensa_irq_mask(struct irq_data *d)
-{
-       cached_irq_mask &= ~(1 << d->hwirq);
-       set_sr (cached_irq_mask, intenable);
-}
-
-static void xtensa_irq_unmask(struct irq_data *d)
-{
-       cached_irq_mask |= 1 << d->hwirq;
-       set_sr (cached_irq_mask, intenable);
-}
-
-static void xtensa_irq_enable(struct irq_data *d)
-{
-       variant_irq_enable(d->hwirq);
-       xtensa_irq_unmask(d);
-}
-
-static void xtensa_irq_disable(struct irq_data *d)
-{
-       xtensa_irq_mask(d);
-       variant_irq_disable(d->hwirq);
-}
-
-static void xtensa_irq_ack(struct irq_data *d)
-{
-       set_sr(1 << d->hwirq, intclear);
-}
-
-static int xtensa_irq_retrigger(struct irq_data *d)
+int xtensa_irq_domain_xlate(const u32 *intspec, unsigned int intsize,
+               unsigned long int_irq, unsigned long ext_irq,
+               unsigned long *out_hwirq, unsigned int *out_type)
 {
-       set_sr(1 << d->hwirq, intset);
-       return 1;
+       if (WARN_ON(intsize < 1 || intsize > 2))
+               return -EINVAL;
+       if (intsize == 2 && intspec[1] == 1) {
+               int_irq = xtensa_map_ext_irq(ext_irq);
+               if (int_irq < XCHAL_NUM_INTERRUPTS)
+                       *out_hwirq = int_irq;
+               else
+                       return -EINVAL;
+       } else {
+               *out_hwirq = int_irq;
+       }
+       *out_type = IRQ_TYPE_NONE;
+       return 0;
 }
 
-static struct irq_chip xtensa_irq_chip = {
-       .name           = "xtensa",
-       .irq_enable     = xtensa_irq_enable,
-       .irq_disable    = xtensa_irq_disable,
-       .irq_mask       = xtensa_irq_mask,
-       .irq_unmask     = xtensa_irq_unmask,
-       .irq_ack        = xtensa_irq_ack,
-       .irq_retrigger  = xtensa_irq_retrigger,
-};
-
-static int xtensa_irq_map(struct irq_domain *d, unsigned int irq,
+int xtensa_irq_map(struct irq_domain *d, unsigned int irq,
                irq_hw_number_t hw)
 {
+       struct irq_chip *irq_chip = d->host_data;
        u32 mask = 1 << hw;
 
        if (mask & XCHAL_INTTYPE_MASK_SOFTWARE) {
-               irq_set_chip_and_handler_name(irq, &xtensa_irq_chip,
+               irq_set_chip_and_handler_name(irq, irq_chip,
                                handle_simple_irq, "level");
                irq_set_status_flags(irq, IRQ_LEVEL);
        } else if (mask & XCHAL_INTTYPE_MASK_EXTERN_EDGE) {
-               irq_set_chip_and_handler_name(irq, &xtensa_irq_chip,
+               irq_set_chip_and_handler_name(irq, irq_chip,
                                handle_edge_irq, "edge");
                irq_clear_status_flags(irq, IRQ_LEVEL);
        } else if (mask & XCHAL_INTTYPE_MASK_EXTERN_LEVEL) {
-               irq_set_chip_and_handler_name(irq, &xtensa_irq_chip,
+               irq_set_chip_and_handler_name(irq, irq_chip,
                                handle_level_irq, "level");
                irq_set_status_flags(irq, IRQ_LEVEL);
        } else if (mask & XCHAL_INTTYPE_MASK_TIMER) {
-               irq_set_chip_and_handler_name(irq, &xtensa_irq_chip,
-                               handle_edge_irq, "edge");
+               irq_set_chip_and_handler_name(irq, irq_chip,
+                               handle_percpu_irq, "timer");
                irq_clear_status_flags(irq, IRQ_LEVEL);
        } else {/* XCHAL_INTTYPE_MASK_WRITE_ERROR */
                /* XCHAL_INTTYPE_MASK_NMI */
-
-               irq_set_chip_and_handler_name(irq, &xtensa_irq_chip,
+               irq_set_chip_and_handler_name(irq, irq_chip,
                                handle_level_irq, "level");
                irq_set_status_flags(irq, IRQ_LEVEL);
        }
        return 0;
 }
 
-static unsigned map_ext_irq(unsigned ext_irq)
+unsigned xtensa_map_ext_irq(unsigned ext_irq)
 {
        unsigned mask = XCHAL_INTTYPE_MASK_EXTERN_EDGE |
                XCHAL_INTTYPE_MASK_EXTERN_LEVEL;
@@ -163,55 +128,77 @@ static unsigned map_ext_irq(unsigned ext_irq)
        return XCHAL_NUM_INTERRUPTS;
 }
 
-/*
- * Device Tree IRQ specifier translation function which works with one or
- * two cell bindings. First cell value maps directly to the hwirq number.
- * Second cell if present specifies whether hwirq number is external (1) or
- * internal (0).
- */
-int xtensa_irq_domain_xlate(struct irq_domain *d, struct device_node *ctrlr,
-               const u32 *intspec, unsigned int intsize,
-               unsigned long *out_hwirq, unsigned int *out_type)
+unsigned xtensa_get_ext_irq_no(unsigned irq)
 {
-       if (WARN_ON(intsize < 1 || intsize > 2))
-               return -EINVAL;
-       if (intsize == 2 && intspec[1] == 1) {
-               unsigned int_irq = map_ext_irq(intspec[0]);
-               if (int_irq < XCHAL_NUM_INTERRUPTS)
-                       *out_hwirq = int_irq;
-               else
-                       return -EINVAL;
-       } else {
-               *out_hwirq = intspec[0];
-       }
-       *out_type = IRQ_TYPE_NONE;
-       return 0;
+       unsigned mask = (XCHAL_INTTYPE_MASK_EXTERN_EDGE |
+               XCHAL_INTTYPE_MASK_EXTERN_LEVEL) &
+               ((1u << irq) - 1);
+       return hweight32(mask);
 }
 
-static const struct irq_domain_ops xtensa_irq_domain_ops = {
-       .xlate = xtensa_irq_domain_xlate,
-       .map = xtensa_irq_map,
-};
-
 void __init init_IRQ(void)
 {
-       struct device_node *intc = NULL;
-
-       cached_irq_mask = 0;
-       set_sr(~0, intclear);
-
 #ifdef CONFIG_OF
-       /* The interrupt controller device node is mandatory */
-       intc = of_find_compatible_node(NULL, NULL, "xtensa,pic");
-       BUG_ON(!intc);
-
-       root_domain = irq_domain_add_linear(intc, NR_IRQS,
-                       &xtensa_irq_domain_ops, NULL);
+       irqchip_init();
+#else
+#ifdef CONFIG_HAVE_SMP
+       xtensa_mx_init_legacy(NULL);
 #else
-       root_domain = irq_domain_add_legacy(intc, NR_IRQS, 0, 0,
-                       &xtensa_irq_domain_ops, NULL);
+       xtensa_pic_init_legacy(NULL);
+#endif
 #endif
-       irq_set_default_host(root_domain);
 
+#ifdef CONFIG_SMP
+       ipi_init();
+#endif
        variant_init_irq();
 }
+
+#ifdef CONFIG_HOTPLUG_CPU
+static void route_irq(struct irq_data *data, unsigned int irq, unsigned int cpu)
+{
+       struct irq_desc *desc = irq_to_desc(irq);
+       struct irq_chip *chip = irq_data_get_irq_chip(data);
+       unsigned long flags;
+
+       raw_spin_lock_irqsave(&desc->lock, flags);
+       if (chip->irq_set_affinity)
+               chip->irq_set_affinity(data, cpumask_of(cpu), false);
+       raw_spin_unlock_irqrestore(&desc->lock, flags);
+}
+
+/*
+ * The CPU has been marked offline.  Migrate IRQs off this CPU.  If
+ * the affinity settings do not allow other CPUs, force them onto any
+ * available CPU.
+ */
+void migrate_irqs(void)
+{
+       unsigned int i, cpu = smp_processor_id();
+       struct irq_desc *desc;
+
+       for_each_irq_desc(i, desc) {
+               struct irq_data *data = irq_desc_get_irq_data(desc);
+               unsigned int newcpu;
+
+               if (irqd_is_per_cpu(data))
+                       continue;
+
+               if (!cpumask_test_cpu(cpu, data->affinity))
+                       continue;
+
+               newcpu = cpumask_any_and(data->affinity, cpu_online_mask);
+
+               if (newcpu >= nr_cpu_ids) {
+                       pr_info_ratelimited("IRQ%u no longer affine to CPU%u\n",
+                                           i, cpu);
+
+                       cpumask_setall(data->affinity);
+                       newcpu = cpumask_any_and(data->affinity,
+                                                cpu_online_mask);
+               }
+
+               route_irq(data, i, newcpu);
+       }
+}
+#endif /* CONFIG_HOTPLUG_CPU */
diff --git a/arch/xtensa/kernel/mxhead.S b/arch/xtensa/kernel/mxhead.S
new file mode 100644 (file)
index 0000000..77a161a
--- /dev/null
@@ -0,0 +1,85 @@
+/*
+ * Xtensa Secondary Processors startup 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.
+ *
+ * Copyright (C) 2001 - 2013 Tensilica Inc.
+ *
+ * Joe Taylor <joe@tensilica.com>
+ * Chris Zankel <chris@zankel.net>
+ * Marc Gauthier <marc@tensilica.com, marc@alumni.uwaterloo.ca>
+ * Pete Delaney <piet@tensilica.com>
+ */
+
+#include <linux/linkage.h>
+
+#include <asm/cacheasm.h>
+#include <asm/initialize_mmu.h>
+#include <asm/mxregs.h>
+#include <asm/regs.h>
+
+
+       .section .SecondaryResetVector.text, "ax"
+
+
+ENTRY(_SecondaryResetVector)
+       _j _SetupOCD
+
+       .begin  no-absolute-literals
+       .literal_position
+
+_SetupOCD:
+       /*
+        * Initialize WB, WS, and clear PS.EXCM (to allow loop instructions).
+        * Set Interrupt Level just below XCHAL_DEBUGLEVEL to allow
+        * xt-gdb to single step via DEBUG exceptions received directly
+        * by ocd.
+        */
+       movi    a1, 1
+       movi    a0, 0
+       wsr     a1, windowstart
+       wsr     a0, windowbase
+       rsync
+
+       movi    a1, LOCKLEVEL
+       wsr     a1, ps
+       rsync
+
+_SetupMMU:
+       Offset = _SetupMMU - _SecondaryResetVector
+
+#ifdef CONFIG_INITIALIZE_XTENSA_MMU_INSIDE_VMLINUX
+       initialize_mmu
+#endif
+
+       /*
+        * Start Secondary Processors with NULL pointer to boot params.
+        */
+       movi    a2, 0                           #  a2 == NULL
+       movi    a3, _startup
+       jx      a3
+
+       .end    no-absolute-literals
+
+
+       .section        .SecondaryResetVector.remapped_text, "ax"
+       .global         _RemappedSecondaryResetVector
+
+       .org 0                                  # Need to do org before literals
+
+_RemappedSecondaryResetVector:
+       .begin  no-absolute-literals
+       .literal_position
+
+       _j      _RemappedSetupMMU
+       . = _RemappedSecondaryResetVector + Offset
+
+_RemappedSetupMMU:
+
+#ifdef CONFIG_INITIALIZE_XTENSA_MMU_INSIDE_VMLINUX
+       initialize_mmu
+#endif
+
+       .end    no-absolute-literals
index 6e2b6638122de71374c99ac0be7df584b0cf7fd9..d21bfa7a28e0f026b7e28b170279d2aa4dac64bb 100644 (file)
@@ -21,6 +21,8 @@
 #include <linux/screen_info.h>
 #include <linux/bootmem.h>
 #include <linux/kernel.h>
+#include <linux/percpu.h>
+#include <linux/cpu.h>
 #include <linux/of_fdt.h>
 #include <linux/of_platform.h>
 
@@ -37,6 +39,7 @@
 #endif
 
 #include <asm/bootparam.h>
+#include <asm/mmu_context.h>
 #include <asm/pgtable.h>
 #include <asm/processor.h>
 #include <asm/timex.h>
@@ -45,6 +48,7 @@
 #include <asm/setup.h>
 #include <asm/param.h>
 #include <asm/traps.h>
+#include <asm/smp.h>
 
 #include <platform/hardware.h>
 
@@ -85,12 +89,6 @@ static char default_command_line[COMMAND_LINE_SIZE] __initdata = CONFIG_CMDLINE;
 
 sysmem_info_t __initdata sysmem;
 
-#ifdef CONFIG_MMU
-extern void init_mmu(void);
-#else
-static inline void init_mmu(void) { }
-#endif
-
 extern int mem_reserve(unsigned long, unsigned long, int);
 extern void bootmem_init(void);
 extern void zones_init(void);
@@ -354,7 +352,8 @@ static inline int probed_compare_swap(int *v, int cmp, int set)
 
 /* Handle probed exception */
 
-void __init do_probed_exception(struct pt_regs *regs, unsigned long exccause)
+static void __init do_probed_exception(struct pt_regs *regs,
+               unsigned long exccause)
 {
        if (regs->pc == rcw_probe_pc) { /* exception on s32c1i ? */
                regs->pc += 3;          /* skip the s32c1i instruction */
@@ -366,7 +365,7 @@ void __init do_probed_exception(struct pt_regs *regs, unsigned long exccause)
 
 /* Simple test of S32C1I (soc bringup assist) */
 
-void __init check_s32c1i(void)
+static int __init check_s32c1i(void)
 {
        int n, cause1, cause2;
        void *handbus, *handdata, *handaddr; /* temporarily saved handlers */
@@ -421,24 +420,21 @@ void __init check_s32c1i(void)
        trap_set_handler(EXCCAUSE_LOAD_STORE_ERROR, handbus);
        trap_set_handler(EXCCAUSE_LOAD_STORE_DATA_ERROR, handdata);
        trap_set_handler(EXCCAUSE_LOAD_STORE_ADDR_ERROR, handaddr);
+       return 0;
 }
 
 #else /* XCHAL_HAVE_S32C1I */
 
 /* This condition should not occur with a commercially deployed processor.
    Display reminder for early engr test or demo chips / FPGA bitstreams */
-void __init check_s32c1i(void)
+static int __init check_s32c1i(void)
 {
        pr_warn("Processor configuration lacks atomic compare-and-swap support!\n");
+       return 0;
 }
 
 #endif /* XCHAL_HAVE_S32C1I */
-#else /* CONFIG_S32C1I_SELFTEST */
-
-void __init check_s32c1i(void)
-{
-}
-
+early_initcall(check_s32c1i);
 #endif /* CONFIG_S32C1I_SELFTEST */
 
 
@@ -447,8 +443,6 @@ void __init setup_arch(char **cmdline_p)
        strlcpy(boot_command_line, command_line, COMMAND_LINE_SIZE);
        *cmdline_p = command_line;
 
-       check_s32c1i();
-
        /* Reserve some memory regions */
 
 #ifdef CONFIG_BLK_DEV_INITRD
@@ -505,6 +499,10 @@ void __init setup_arch(char **cmdline_p)
 
        platform_setup(cmdline_p);
 
+#ifdef CONFIG_SMP
+       smp_init_cpus();
+#endif
+
        paging_init();
        zones_init();
 
@@ -521,6 +519,22 @@ void __init setup_arch(char **cmdline_p)
 #endif
 }
 
+static DEFINE_PER_CPU(struct cpu, cpu_data);
+
+static int __init topology_init(void)
+{
+       int i;
+
+       for_each_possible_cpu(i) {
+               struct cpu *cpu = &per_cpu(cpu_data, i);
+               cpu->hotpluggable = !!i;
+               register_cpu(cpu, i);
+       }
+
+       return 0;
+}
+subsys_initcall(topology_init);
+
 void machine_restart(char * cmd)
 {
        platform_restart();
@@ -546,21 +560,27 @@ void machine_power_off(void)
 static int
 c_show(struct seq_file *f, void *slot)
 {
+       char buf[NR_CPUS * 5];
+
+       cpulist_scnprintf(buf, sizeof(buf), cpu_online_mask);
        /* high-level stuff */
-       seq_printf(f,"processor\t: 0\n"
-                    "vendor_id\t: Tensilica\n"
-                    "model\t\t: Xtensa " XCHAL_HW_VERSION_NAME "\n"
-                    "core ID\t\t: " XCHAL_CORE_ID "\n"
-                    "build ID\t: 0x%x\n"
-                    "byte order\t: %s\n"
-                    "cpu MHz\t\t: %lu.%02lu\n"
-                    "bogomips\t: %lu.%02lu\n",
-                    XCHAL_BUILD_UNIQUE_ID,
-                    XCHAL_HAVE_BE ?  "big" : "little",
-                    ccount_freq/1000000,
-                    (ccount_freq/10000) % 100,
-                    loops_per_jiffy/(500000/HZ),
-                    (loops_per_jiffy/(5000/HZ)) % 100);
+       seq_printf(f, "CPU count\t: %u\n"
+                     "CPU list\t: %s\n"
+                     "vendor_id\t: Tensilica\n"
+                     "model\t\t: Xtensa " XCHAL_HW_VERSION_NAME "\n"
+                     "core ID\t\t: " XCHAL_CORE_ID "\n"
+                     "build ID\t: 0x%x\n"
+                     "byte order\t: %s\n"
+                     "cpu MHz\t\t: %lu.%02lu\n"
+                     "bogomips\t: %lu.%02lu\n",
+                     num_online_cpus(),
+                     buf,
+                     XCHAL_BUILD_UNIQUE_ID,
+                     XCHAL_HAVE_BE ?  "big" : "little",
+                     ccount_freq/1000000,
+                     (ccount_freq/10000) % 100,
+                     loops_per_jiffy/(500000/HZ),
+                     (loops_per_jiffy/(5000/HZ)) % 100);
 
        seq_printf(f,"flags\t\t: "
 #if XCHAL_HAVE_NMI
@@ -672,7 +692,7 @@ c_show(struct seq_file *f, void *slot)
 static void *
 c_start(struct seq_file *f, loff_t *pos)
 {
-       return (void *) ((*pos == 0) ? (void *)1 : NULL);
+       return (*pos == 0) ? (void *)1 : NULL;
 }
 
 static void *
@@ -688,10 +708,10 @@ c_stop(struct seq_file *f, void *v)
 
 const struct seq_operations cpuinfo_op =
 {
-       start:  c_start,
-       next:   c_next,
-       stop:   c_stop,
-       show:   c_show
+       .start  = c_start,
+       .next   = c_next,
+       .stop   = c_stop,
+       .show   = c_show,
 };
 
 #endif /* CONFIG_PROC_FS */
diff --git a/arch/xtensa/kernel/smp.c b/arch/xtensa/kernel/smp.c
new file mode 100644 (file)
index 0000000..1c7a209
--- /dev/null
@@ -0,0 +1,591 @@
+/*
+ * Xtensa SMP support functions.
+ *
+ * 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.
+ *
+ * Copyright (C) 2008 - 2013 Tensilica Inc.
+ *
+ * Chris Zankel <chris@zankel.net>
+ * Joe Taylor <joe@tensilica.com>
+ * Pete Delaney <piet@tensilica.com
+ */
+
+#include <linux/cpu.h>
+#include <linux/cpumask.h>
+#include <linux/delay.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/irqdomain.h>
+#include <linux/irq.h>
+#include <linux/kdebug.h>
+#include <linux/module.h>
+#include <linux/reboot.h>
+#include <linux/seq_file.h>
+#include <linux/smp.h>
+#include <linux/thread_info.h>
+
+#include <asm/cacheflush.h>
+#include <asm/kdebug.h>
+#include <asm/mmu_context.h>
+#include <asm/mxregs.h>
+#include <asm/platform.h>
+#include <asm/tlbflush.h>
+#include <asm/traps.h>
+
+#ifdef CONFIG_SMP
+# if XCHAL_HAVE_S32C1I == 0
+#  error "The S32C1I option is required for SMP."
+# endif
+#endif
+
+static void system_invalidate_dcache_range(unsigned long start,
+               unsigned long size);
+static void system_flush_invalidate_dcache_range(unsigned long start,
+               unsigned long size);
+
+/* IPI (Inter Process Interrupt) */
+
+#define IPI_IRQ        0
+
+static irqreturn_t ipi_interrupt(int irq, void *dev_id);
+static struct irqaction ipi_irqaction = {
+       .handler =      ipi_interrupt,
+       .flags =        IRQF_PERCPU,
+       .name =         "ipi",
+};
+
+void ipi_init(void)
+{
+       unsigned irq = irq_create_mapping(NULL, IPI_IRQ);
+       setup_irq(irq, &ipi_irqaction);
+}
+
+static inline unsigned int get_core_count(void)
+{
+       /* Bits 18..21 of SYSCFGID contain the core count minus 1. */
+       unsigned int syscfgid = get_er(SYSCFGID);
+       return ((syscfgid >> 18) & 0xf) + 1;
+}
+
+static inline int get_core_id(void)
+{
+       /* Bits 0...18 of SYSCFGID contain the core id  */
+       unsigned int core_id = get_er(SYSCFGID);
+       return core_id & 0x3fff;
+}
+
+void __init smp_prepare_cpus(unsigned int max_cpus)
+{
+       unsigned i;
+
+       for (i = 0; i < max_cpus; ++i)
+               set_cpu_present(i, true);
+}
+
+void __init smp_init_cpus(void)
+{
+       unsigned i;
+       unsigned int ncpus = get_core_count();
+       unsigned int core_id = get_core_id();
+
+       pr_info("%s: Core Count = %d\n", __func__, ncpus);
+       pr_info("%s: Core Id = %d\n", __func__, core_id);
+
+       for (i = 0; i < ncpus; ++i)
+               set_cpu_possible(i, true);
+}
+
+void __init smp_prepare_boot_cpu(void)
+{
+       unsigned int cpu = smp_processor_id();
+       BUG_ON(cpu != 0);
+       cpu_asid_cache(cpu) = ASID_USER_FIRST;
+}
+
+void __init smp_cpus_done(unsigned int max_cpus)
+{
+}
+
+static int boot_secondary_processors = 1; /* Set with xt-gdb via .xt-gdb */
+static DECLARE_COMPLETION(cpu_running);
+
+void secondary_start_kernel(void)
+{
+       struct mm_struct *mm = &init_mm;
+       unsigned int cpu = smp_processor_id();
+
+       init_mmu();
+
+#ifdef CONFIG_DEBUG_KERNEL
+       if (boot_secondary_processors == 0) {
+               pr_debug("%s: boot_secondary_processors:%d; Hanging cpu:%d\n",
+                       __func__, boot_secondary_processors, cpu);
+               for (;;)
+                       __asm__ __volatile__ ("waiti " __stringify(LOCKLEVEL));
+       }
+
+       pr_debug("%s: boot_secondary_processors:%d; Booting cpu:%d\n",
+               __func__, boot_secondary_processors, cpu);
+#endif
+       /* Init EXCSAVE1 */
+
+       secondary_trap_init();
+
+       /* All kernel threads share the same mm context. */
+
+       atomic_inc(&mm->mm_users);
+       atomic_inc(&mm->mm_count);
+       current->active_mm = mm;
+       cpumask_set_cpu(cpu, mm_cpumask(mm));
+       enter_lazy_tlb(mm, current);
+
+       preempt_disable();
+       trace_hardirqs_off();
+
+       calibrate_delay();
+
+       notify_cpu_starting(cpu);
+
+       secondary_init_irq();
+       local_timer_setup(cpu);
+
+       local_irq_enable();
+
+       set_cpu_online(cpu, true);
+       complete(&cpu_running);
+
+       cpu_startup_entry(CPUHP_ONLINE);
+}
+
+static void mx_cpu_start(void *p)
+{
+       unsigned cpu = (unsigned)p;
+       unsigned long run_stall_mask = get_er(MPSCORE);
+
+       set_er(run_stall_mask & ~(1u << cpu), MPSCORE);
+       pr_debug("%s: cpu: %d, run_stall_mask: %lx ---> %lx\n",
+                       __func__, cpu, run_stall_mask, get_er(MPSCORE));
+}
+
+static void mx_cpu_stop(void *p)
+{
+       unsigned cpu = (unsigned)p;
+       unsigned long run_stall_mask = get_er(MPSCORE);
+
+       set_er(run_stall_mask | (1u << cpu), MPSCORE);
+       pr_debug("%s: cpu: %d, run_stall_mask: %lx ---> %lx\n",
+                       __func__, cpu, run_stall_mask, get_er(MPSCORE));
+}
+
+#ifdef CONFIG_HOTPLUG_CPU
+unsigned long cpu_start_id __cacheline_aligned;
+#endif
+unsigned long cpu_start_ccount;
+
+static int boot_secondary(unsigned int cpu, struct task_struct *ts)
+{
+       unsigned long timeout = jiffies + msecs_to_jiffies(1000);
+       unsigned long ccount;
+       int i;
+
+#ifdef CONFIG_HOTPLUG_CPU
+       cpu_start_id = cpu;
+       system_flush_invalidate_dcache_range(
+                       (unsigned long)&cpu_start_id, sizeof(cpu_start_id));
+#endif
+       smp_call_function_single(0, mx_cpu_start, (void *)cpu, 1);
+
+       for (i = 0; i < 2; ++i) {
+               do
+                       ccount = get_ccount();
+               while (!ccount);
+
+               cpu_start_ccount = ccount;
+
+               while (time_before(jiffies, timeout)) {
+                       mb();
+                       if (!cpu_start_ccount)
+                               break;
+               }
+
+               if (cpu_start_ccount) {
+                       smp_call_function_single(0, mx_cpu_stop,
+                                       (void *)cpu, 1);
+                       cpu_start_ccount = 0;
+                       return -EIO;
+               }
+       }
+       return 0;
+}
+
+int __cpu_up(unsigned int cpu, struct task_struct *idle)
+{
+       int ret = 0;
+
+       if (cpu_asid_cache(cpu) == 0)
+               cpu_asid_cache(cpu) = ASID_USER_FIRST;
+
+       start_info.stack = (unsigned long)task_pt_regs(idle);
+       wmb();
+
+       pr_debug("%s: Calling wakeup_secondary(cpu:%d, idle:%p, sp: %08lx)\n",
+                       __func__, cpu, idle, start_info.stack);
+
+       ret = boot_secondary(cpu, idle);
+       if (ret == 0) {
+               wait_for_completion_timeout(&cpu_running,
+                               msecs_to_jiffies(1000));
+               if (!cpu_online(cpu))
+                       ret = -EIO;
+       }
+
+       if (ret)
+               pr_err("CPU %u failed to boot\n", cpu);
+
+       return ret;
+}
+
+#ifdef CONFIG_HOTPLUG_CPU
+
+/*
+ * __cpu_disable runs on the processor to be shutdown.
+ */
+int __cpu_disable(void)
+{
+       unsigned int cpu = smp_processor_id();
+
+       /*
+        * Take this CPU offline.  Once we clear this, we can't return,
+        * and we must not schedule until we're ready to give up the cpu.
+        */
+       set_cpu_online(cpu, false);
+
+       /*
+        * OK - migrate IRQs away from this CPU
+        */
+       migrate_irqs();
+
+       /*
+        * Flush user cache and TLB mappings, and then remove this CPU
+        * from the vm mask set of all processes.
+        */
+       local_flush_cache_all();
+       local_flush_tlb_all();
+       invalidate_page_directory();
+
+       clear_tasks_mm_cpumask(cpu);
+
+       return 0;
+}
+
+static void platform_cpu_kill(unsigned int cpu)
+{
+       smp_call_function_single(0, mx_cpu_stop, (void *)cpu, true);
+}
+
+/*
+ * called on the thread which is asking for a CPU to be shutdown -
+ * waits until shutdown has completed, or it is timed out.
+ */
+void __cpu_die(unsigned int cpu)
+{
+       unsigned long timeout = jiffies + msecs_to_jiffies(1000);
+       while (time_before(jiffies, timeout)) {
+               system_invalidate_dcache_range((unsigned long)&cpu_start_id,
+                               sizeof(cpu_start_id));
+               if (cpu_start_id == -cpu) {
+                       platform_cpu_kill(cpu);
+                       return;
+               }
+       }
+       pr_err("CPU%u: unable to kill\n", cpu);
+}
+
+void arch_cpu_idle_dead(void)
+{
+       cpu_die();
+}
+/*
+ * Called from the idle thread for the CPU which has been shutdown.
+ *
+ * Note that we disable IRQs here, but do not re-enable them
+ * before returning to the caller. This is also the behaviour
+ * of the other hotplug-cpu capable cores, so presumably coming
+ * out of idle fixes this.
+ */
+void __ref cpu_die(void)
+{
+       idle_task_exit();
+       local_irq_disable();
+       __asm__ __volatile__(
+                       "       movi    a2, cpu_restart\n"
+                       "       jx      a2\n");
+}
+
+#endif /* CONFIG_HOTPLUG_CPU */
+
+enum ipi_msg_type {
+       IPI_RESCHEDULE = 0,
+       IPI_CALL_FUNC,
+       IPI_CPU_STOP,
+       IPI_MAX
+};
+
+static const struct {
+       const char *short_text;
+       const char *long_text;
+} ipi_text[] = {
+       { .short_text = "RES", .long_text = "Rescheduling interrupts" },
+       { .short_text = "CAL", .long_text = "Function call interrupts" },
+       { .short_text = "DIE", .long_text = "CPU shutdown interrupts" },
+};
+
+struct ipi_data {
+       unsigned long ipi_count[IPI_MAX];
+};
+
+static DEFINE_PER_CPU(struct ipi_data, ipi_data);
+
+static void send_ipi_message(const struct cpumask *callmask,
+               enum ipi_msg_type msg_id)
+{
+       int index;
+       unsigned long mask = 0;
+
+       for_each_cpu(index, callmask)
+               if (index != smp_processor_id())
+                       mask |= 1 << index;
+
+       set_er(mask, MIPISET(msg_id));
+}
+
+void arch_send_call_function_ipi_mask(const struct cpumask *mask)
+{
+       send_ipi_message(mask, IPI_CALL_FUNC);
+}
+
+void arch_send_call_function_single_ipi(int cpu)
+{
+       send_ipi_message(cpumask_of(cpu), IPI_CALL_FUNC);
+}
+
+void smp_send_reschedule(int cpu)
+{
+       send_ipi_message(cpumask_of(cpu), IPI_RESCHEDULE);
+}
+
+void smp_send_stop(void)
+{
+       struct cpumask targets;
+
+       cpumask_copy(&targets, cpu_online_mask);
+       cpumask_clear_cpu(smp_processor_id(), &targets);
+       send_ipi_message(&targets, IPI_CPU_STOP);
+}
+
+static void ipi_cpu_stop(unsigned int cpu)
+{
+       set_cpu_online(cpu, false);
+       machine_halt();
+}
+
+irqreturn_t ipi_interrupt(int irq, void *dev_id)
+{
+       unsigned int cpu = smp_processor_id();
+       struct ipi_data *ipi = &per_cpu(ipi_data, cpu);
+       unsigned int msg;
+       unsigned i;
+
+       msg = get_er(MIPICAUSE(cpu));
+       for (i = 0; i < IPI_MAX; i++)
+               if (msg & (1 << i)) {
+                       set_er(1 << i, MIPICAUSE(cpu));
+                       ++ipi->ipi_count[i];
+               }
+
+       if (msg & (1 << IPI_RESCHEDULE))
+               scheduler_ipi();
+       if (msg & (1 << IPI_CALL_FUNC))
+               generic_smp_call_function_interrupt();
+       if (msg & (1 << IPI_CPU_STOP))
+               ipi_cpu_stop(cpu);
+
+       return IRQ_HANDLED;
+}
+
+void show_ipi_list(struct seq_file *p, int prec)
+{
+       unsigned int cpu;
+       unsigned i;
+
+       for (i = 0; i < IPI_MAX; ++i) {
+               seq_printf(p, "%*s:", prec, ipi_text[i].short_text);
+               for_each_online_cpu(cpu)
+                       seq_printf(p, " %10lu",
+                                       per_cpu(ipi_data, cpu).ipi_count[i]);
+               seq_printf(p, "   %s\n", ipi_text[i].long_text);
+       }
+}
+
+int setup_profiling_timer(unsigned int multiplier)
+{
+       pr_debug("setup_profiling_timer %d\n", multiplier);
+       return 0;
+}
+
+/* TLB flush functions */
+
+struct flush_data {
+       struct vm_area_struct *vma;
+       unsigned long addr1;
+       unsigned long addr2;
+};
+
+static void ipi_flush_tlb_all(void *arg)
+{
+       local_flush_tlb_all();
+}
+
+void flush_tlb_all(void)
+{
+       on_each_cpu(ipi_flush_tlb_all, NULL, 1);
+}
+
+static void ipi_flush_tlb_mm(void *arg)
+{
+       local_flush_tlb_mm(arg);
+}
+
+void flush_tlb_mm(struct mm_struct *mm)
+{
+       on_each_cpu(ipi_flush_tlb_mm, mm, 1);
+}
+
+static void ipi_flush_tlb_page(void *arg)
+{
+       struct flush_data *fd = arg;
+       local_flush_tlb_page(fd->vma, fd->addr1);
+}
+
+void flush_tlb_page(struct vm_area_struct *vma, unsigned long addr)
+{
+       struct flush_data fd = {
+               .vma = vma,
+               .addr1 = addr,
+       };
+       on_each_cpu(ipi_flush_tlb_page, &fd, 1);
+}
+
+static void ipi_flush_tlb_range(void *arg)
+{
+       struct flush_data *fd = arg;
+       local_flush_tlb_range(fd->vma, fd->addr1, fd->addr2);
+}
+
+void flush_tlb_range(struct vm_area_struct *vma,
+                    unsigned long start, unsigned long end)
+{
+       struct flush_data fd = {
+               .vma = vma,
+               .addr1 = start,
+               .addr2 = end,
+       };
+       on_each_cpu(ipi_flush_tlb_range, &fd, 1);
+}
+
+/* Cache flush functions */
+
+static void ipi_flush_cache_all(void *arg)
+{
+       local_flush_cache_all();
+}
+
+void flush_cache_all(void)
+{
+       on_each_cpu(ipi_flush_cache_all, NULL, 1);
+}
+
+static void ipi_flush_cache_page(void *arg)
+{
+       struct flush_data *fd = arg;
+       local_flush_cache_page(fd->vma, fd->addr1, fd->addr2);
+}
+
+void flush_cache_page(struct vm_area_struct *vma,
+                    unsigned long address, unsigned long pfn)
+{
+       struct flush_data fd = {
+               .vma = vma,
+               .addr1 = address,
+               .addr2 = pfn,
+       };
+       on_each_cpu(ipi_flush_cache_page, &fd, 1);
+}
+
+static void ipi_flush_cache_range(void *arg)
+{
+       struct flush_data *fd = arg;
+       local_flush_cache_range(fd->vma, fd->addr1, fd->addr2);
+}
+
+void flush_cache_range(struct vm_area_struct *vma,
+                    unsigned long start, unsigned long end)
+{
+       struct flush_data fd = {
+               .vma = vma,
+               .addr1 = start,
+               .addr2 = end,
+       };
+       on_each_cpu(ipi_flush_cache_range, &fd, 1);
+}
+
+static void ipi_flush_icache_range(void *arg)
+{
+       struct flush_data *fd = arg;
+       local_flush_icache_range(fd->addr1, fd->addr2);
+}
+
+void flush_icache_range(unsigned long start, unsigned long end)
+{
+       struct flush_data fd = {
+               .addr1 = start,
+               .addr2 = end,
+       };
+       on_each_cpu(ipi_flush_icache_range, &fd, 1);
+}
+
+/* ------------------------------------------------------------------------- */
+
+static void ipi_invalidate_dcache_range(void *arg)
+{
+       struct flush_data *fd = arg;
+       __invalidate_dcache_range(fd->addr1, fd->addr2);
+}
+
+static void system_invalidate_dcache_range(unsigned long start,
+               unsigned long size)
+{
+       struct flush_data fd = {
+               .addr1 = start,
+               .addr2 = size,
+       };
+       on_each_cpu(ipi_invalidate_dcache_range, &fd, 1);
+}
+
+static void ipi_flush_invalidate_dcache_range(void *arg)
+{
+       struct flush_data *fd = arg;
+       __flush_invalidate_dcache_range(fd->addr1, fd->addr2);
+}
+
+static void system_flush_invalidate_dcache_range(unsigned long start,
+               unsigned long size)
+{
+       struct flush_data fd = {
+               .addr1 = start,
+               .addr2 = size,
+       };
+       on_each_cpu(ipi_flush_invalidate_dcache_range, &fd, 1);
+}
index 9af3dd88ad7eac172e4fb835e94c70e369547438..60dcb3fcaeae8ad978f8eeec4d073a88abb9f064 100644 (file)
@@ -46,24 +46,19 @@ static struct clocksource ccount_clocksource = {
        .rating = 200,
        .read = ccount_read,
        .mask = CLOCKSOURCE_MASK(32),
+       .flags = CLOCK_SOURCE_IS_CONTINUOUS,
 };
 
 static int ccount_timer_set_next_event(unsigned long delta,
                struct clock_event_device *dev);
 static void ccount_timer_set_mode(enum clock_event_mode mode,
                struct clock_event_device *evt);
-static struct ccount_timer_t {
+struct ccount_timer {
        struct clock_event_device evt;
        int irq_enabled;
-} ccount_timer = {
-       .evt = {
-               .name           = "ccount_clockevent",
-               .features       = CLOCK_EVT_FEAT_ONESHOT,
-               .rating         = 300,
-               .set_next_event = ccount_timer_set_next_event,
-               .set_mode       = ccount_timer_set_mode,
-       },
+       char name[24];
 };
+static DEFINE_PER_CPU(struct ccount_timer, ccount_timer);
 
 static int ccount_timer_set_next_event(unsigned long delta,
                struct clock_event_device *dev)
@@ -84,8 +79,8 @@ static int ccount_timer_set_next_event(unsigned long delta,
 static void ccount_timer_set_mode(enum clock_event_mode mode,
                struct clock_event_device *evt)
 {
-       struct ccount_timer_t *timer =
-               container_of(evt, struct ccount_timer_t, evt);
+       struct ccount_timer *timer =
+               container_of(evt, struct ccount_timer, evt);
 
        /*
         * There is no way to disable the timer interrupt at the device level,
@@ -117,9 +112,28 @@ static struct irqaction timer_irqaction = {
        .handler =      timer_interrupt,
        .flags =        IRQF_TIMER,
        .name =         "timer",
-       .dev_id =       &ccount_timer,
 };
 
+void local_timer_setup(unsigned cpu)
+{
+       struct ccount_timer *timer = &per_cpu(ccount_timer, cpu);
+       struct clock_event_device *clockevent = &timer->evt;
+
+       timer->irq_enabled = 1;
+       clockevent->name = timer->name;
+       snprintf(timer->name, sizeof(timer->name), "ccount_clockevent_%u", cpu);
+       clockevent->features = CLOCK_EVT_FEAT_ONESHOT;
+       clockevent->rating = 300;
+       clockevent->set_next_event = ccount_timer_set_next_event;
+       clockevent->set_mode = ccount_timer_set_mode;
+       clockevent->cpumask = cpumask_of(cpu);
+       clockevent->irq = irq_create_mapping(NULL, LINUX_TIMER_INT);
+       if (WARN(!clockevent->irq, "error: can't map timer irq"))
+               return;
+       clockevents_config_and_register(clockevent, ccount_freq,
+                                       0xf, 0xffffffff);
+}
+
 void __init time_init(void)
 {
 #ifdef CONFIG_XTENSA_CALIBRATE_CCOUNT
@@ -131,16 +145,8 @@ void __init time_init(void)
        ccount_freq = CONFIG_XTENSA_CPU_CLOCK*1000000UL;
 #endif
        clocksource_register_hz(&ccount_clocksource, ccount_freq);
-
-       ccount_timer.evt.cpumask = cpumask_of(0);
-       ccount_timer.evt.irq = irq_create_mapping(NULL, LINUX_TIMER_INT);
-       if (WARN(!ccount_timer.evt.irq, "error: can't map timer irq"))
-               return;
-       clockevents_config_and_register(&ccount_timer.evt, ccount_freq, 0xf,
-                       0xffffffff);
-       setup_irq(ccount_timer.evt.irq, &timer_irqaction);
-       ccount_timer.irq_enabled = 1;
-
+       local_timer_setup(0);
+       setup_irq(this_cpu_ptr(&ccount_timer)->evt.irq, &timer_irqaction);
        setup_sched_clock(ccount_sched_clock_read, 32, ccount_freq);
 }
 
@@ -148,11 +154,11 @@ void __init time_init(void)
  * The timer interrupt is called HZ times per second.
  */
 
-irqreturn_t timer_interrupt (int irq, void *dev_id)
+irqreturn_t timer_interrupt(int irq, void *dev_id)
 {
-       struct ccount_timer_t *timer = dev_id;
-       struct clock_event_device *evt = &timer->evt;
+       struct clock_event_device *evt = &this_cpu_ptr(&ccount_timer)->evt;
 
+       set_linux_timer(get_linux_timer());
        evt->event_handler(evt);
 
        /* Allow platform to do something useful (Wdog). */
index 3e8a05c874cd2719951929ff348b4d24f34c773e..eebbfd8c26fc25121bced6cb18c074a55c6cb5f5 100644 (file)
@@ -157,7 +157,7 @@ COPROCESSOR(7),
  * 2. it is a temporary memory buffer for the exception handlers.
  */
 
-unsigned long exc_table[EXC_TABLE_SIZE/4];
+DEFINE_PER_CPU(unsigned long, exc_table[EXC_TABLE_SIZE/4]);
 
 void die(const char*, struct pt_regs*, long);
 
@@ -212,6 +212,9 @@ void do_interrupt(struct pt_regs *regs)
                XCHAL_INTLEVEL6_MASK,
                XCHAL_INTLEVEL7_MASK,
        };
+       struct pt_regs *old_regs = set_irq_regs(regs);
+
+       irq_enter();
 
        for (;;) {
                unsigned intread = get_sr(interrupt);
@@ -227,21 +230,13 @@ void do_interrupt(struct pt_regs *regs)
                }
 
                if (level == 0)
-                       return;
-
-               /*
-                * Clear the interrupt before processing, in case it's
-                *  edge-triggered or software-generated
-                */
-               while (int_at_level) {
-                       unsigned i = __ffs(int_at_level);
-                       unsigned mask = 1 << i;
-
-                       int_at_level ^= mask;
-                       set_sr(mask, intclear);
-                       do_IRQ(i, regs);
-               }
+                       break;
+
+               do_IRQ(__ffs(int_at_level), regs);
        }
+
+       irq_exit();
+       set_irq_regs(old_regs);
 }
 
 /*
@@ -318,17 +313,31 @@ do_debug(struct pt_regs *regs)
 }
 
 
+static void set_handler(int idx, void *handler)
+{
+       unsigned int cpu;
+
+       for_each_possible_cpu(cpu)
+               per_cpu(exc_table, cpu)[idx] = (unsigned long)handler;
+}
+
 /* Set exception C handler - for temporary use when probing exceptions */
 
 void * __init trap_set_handler(int cause, void *handler)
 {
-       unsigned long *entry = &exc_table[EXC_TABLE_DEFAULT / 4 + cause];
-       void *previous = (void *)*entry;
-       *entry = (unsigned long)handler;
+       void *previous = (void *)per_cpu(exc_table, 0)[
+               EXC_TABLE_DEFAULT / 4 + cause];
+       set_handler(EXC_TABLE_DEFAULT / 4 + cause, handler);
        return previous;
 }
 
 
+static void trap_init_excsave(void)
+{
+       unsigned long excsave1 = (unsigned long)this_cpu_ptr(exc_table);
+       __asm__ __volatile__("wsr  %0, excsave1\n" : : "a" (excsave1));
+}
+
 /*
  * Initialize dispatch tables.
  *
@@ -342,8 +351,6 @@ void * __init trap_set_handler(int cause, void *handler)
  * See vectors.S for more details.
  */
 
-#define set_handler(idx,handler) (exc_table[idx] = (unsigned long) (handler))
-
 void __init trap_init(void)
 {
        int i;
@@ -373,10 +380,15 @@ void __init trap_init(void)
        }
 
        /* Initialize EXCSAVE_1 to hold the address of the exception table. */
+       trap_init_excsave();
+}
 
-       i = (unsigned long)exc_table;
-       __asm__ __volatile__("wsr  %0, excsave1\n" : : "a" (i));
+#ifdef CONFIG_SMP
+void secondary_trap_init(void)
+{
+       trap_init_excsave();
 }
+#endif
 
 /*
  * This function dumps the current valid window frame and other base registers.
index 21acd11b5df2f689f0079f47a3910aaaa4ffbf41..ee32c0085dff4296fb9290e3706733db9402460d 100644 (file)
@@ -165,6 +165,13 @@ SECTIONS
                   .DoubleExceptionVector.text);
     RELOCATE_ENTRY(_DebugInterruptVector_text,
                   .DebugInterruptVector.text);
+#if defined(CONFIG_SMP)
+    RELOCATE_ENTRY(_SecondaryResetVector_literal,
+                  .SecondaryResetVector.literal);
+    RELOCATE_ENTRY(_SecondaryResetVector_text,
+                  .SecondaryResetVector.text);
+#endif
+
   
     __boot_reloc_table_end = ABSOLUTE(.) ;
 
@@ -272,6 +279,25 @@ SECTIONS
                  .DoubleExceptionVector.literal)
 
   . = (LOADADDR( .DoubleExceptionVector.text ) + SIZEOF( .DoubleExceptionVector.text ) + 3) & ~ 3;
+
+#if defined(CONFIG_SMP)
+
+  SECTION_VECTOR (_SecondaryResetVector_literal,
+                 .SecondaryResetVector.literal,
+                 RESET_VECTOR1_VADDR - 4,
+                 SIZEOF(.DoubleExceptionVector.text),
+                 .DoubleExceptionVector.text)
+
+  SECTION_VECTOR (_SecondaryResetVector_text,
+                 .SecondaryResetVector.text,
+                 RESET_VECTOR1_VADDR,
+                 4,
+                 .SecondaryResetVector.literal)
+
+  . = LOADADDR(.SecondaryResetVector.text)+SIZEOF(.SecondaryResetVector.text);
+
+#endif
+
   . = ALIGN(PAGE_SIZE);
 
   __init_end = .;
index 81edeab82d174eb478ad303001da881d6758f2bb..ba4c47f291b17843047a410549b09cb59ba52967 100644 (file)
@@ -118,7 +118,7 @@ void flush_dcache_page(struct page *page)
  * For now, flush the whole cache. FIXME??
  */
 
-void flush_cache_range(struct vm_area_struct* vma,
+void local_flush_cache_range(struct vm_area_struct *vma,
                       unsigned long start, unsigned long end)
 {
        __flush_invalidate_dcache_all();
@@ -132,7 +132,7 @@ void flush_cache_range(struct vm_area_struct* vma,
  * alias versions of the cache flush functions.
  */
 
-void flush_cache_page(struct vm_area_struct* vma, unsigned long address,
+void local_flush_cache_page(struct vm_area_struct *vma, unsigned long address,
                      unsigned long pfn)
 {
        /* Note that we have to use the 'alias' address to avoid multi-hit */
@@ -159,8 +159,7 @@ update_mmu_cache(struct vm_area_struct * vma, unsigned long addr, pte_t *ptep)
 
        /* Invalidate old entry in TLBs */
 
-       invalidate_itlb_mapping(addr);
-       invalidate_dtlb_mapping(addr);
+       flush_tlb_page(vma, addr);
 
 #if (DCACHE_WAY_SIZE > PAGE_SIZE) && XCHAL_DCACHE_IS_WRITEBACK
 
index 70fa7bc42b4a0853012af6f6daaeb254f9c5a086..b57c4f91f487efdc6b2f3f44fe43cfb12e9a7e0c 100644 (file)
@@ -21,7 +21,7 @@
 #include <asm/uaccess.h>
 #include <asm/pgalloc.h>
 
-unsigned long asid_cache = ASID_USER_FIRST;
+DEFINE_PER_CPU(unsigned long, asid_cache) = ASID_USER_FIRST;
 void bad_page_fault(struct pt_regs*, unsigned long, int);
 
 #undef DEBUG_PAGE_FAULT
index d97ed1ba7b0addd36d81782469ead96889535fc0..1f68558dbcc2264f99f917fcb18659d90f46a8cb 100644 (file)
@@ -140,7 +140,7 @@ ENTRY(clear_user_page)
 
        /* Setup a temporary DTLB with the color of the VPN */
 
-       movi    a4, -PAGE_OFFSET + (PAGE_KERNEL | _PAGE_HW_WRITE)
+       movi    a4, ((PAGE_KERNEL | _PAGE_HW_WRITE) - PAGE_OFFSET) & 0xffffffff
        movi    a5, TLBTEMP_BASE_1                      # virt
        add     a6, a2, a4                              # ppn
        add     a2, a5, a3                              # add 'color'
@@ -194,7 +194,7 @@ ENTRY(copy_user_page)
        or      a9, a9, a8
        slli    a4, a4, PAGE_SHIFT
        s32i    a9, a5, PAGE_FLAGS
-       movi    a5, -PAGE_OFFSET + (PAGE_KERNEL | _PAGE_HW_WRITE)
+       movi    a5, ((PAGE_KERNEL | _PAGE_HW_WRITE) - PAGE_OFFSET) & 0xffffffff
 
        beqz    a6, 1f
 
index c43771c974be94ceea2152bf77cec9777b3c4b1a..5bb8e3c61d850ca05cde23dbdf9872ffe7b262af 100644 (file)
@@ -22,7 +22,7 @@ void __init paging_init(void)
 /*
  * Flush the mmu and reset associated register to default values.
  */
-void __init init_mmu(void)
+void init_mmu(void)
 {
 #if !(XCHAL_HAVE_PTP_MMU && XCHAL_HAVE_SPANNING_WAY)
        /*
@@ -37,7 +37,7 @@ void __init init_mmu(void)
        set_itlbcfg_register(0);
        set_dtlbcfg_register(0);
 #endif
-       flush_tlb_all();
+       local_flush_tlb_all();
 
        /* Set rasid register to a known value. */
 
index ca9d2366bf12b5b85dad9c4fd1295ed14a02d2a5..ade623826788b387f150cfae5a90821f07f6b028 100644 (file)
@@ -48,7 +48,7 @@ static inline void __flush_dtlb_all (void)
 }
 
 
-void flush_tlb_all (void)
+void local_flush_tlb_all(void)
 {
        __flush_itlb_all();
        __flush_dtlb_all();
@@ -60,19 +60,23 @@ void flush_tlb_all (void)
  * a new context will be assigned to it.
  */
 
-void flush_tlb_mm(struct mm_struct *mm)
+void local_flush_tlb_mm(struct mm_struct *mm)
 {
+       int cpu = smp_processor_id();
+
        if (mm == current->active_mm) {
                unsigned long flags;
                local_irq_save(flags);
-               __get_new_mmu_context(mm);
-               __load_mmu_context(mm);
+               mm->context.asid[cpu] = NO_CONTEXT;
+               activate_context(mm, cpu);
                local_irq_restore(flags);
+       } else {
+               mm->context.asid[cpu] = NO_CONTEXT;
+               mm->context.cpu = -1;
        }
-       else
-               mm->context = 0;
 }
 
+
 #define _ITLB_ENTRIES (ITLB_ARF_WAYS << XCHAL_ITLB_ARF_ENTRIES_LOG2)
 #define _DTLB_ENTRIES (DTLB_ARF_WAYS << XCHAL_DTLB_ARF_ENTRIES_LOG2)
 #if _ITLB_ENTRIES > _DTLB_ENTRIES
@@ -81,24 +85,26 @@ void flush_tlb_mm(struct mm_struct *mm)
 # define _TLB_ENTRIES _DTLB_ENTRIES
 #endif
 
-void flush_tlb_range (struct vm_area_struct *vma,
-                     unsigned long start, unsigned long end)
+void local_flush_tlb_range(struct vm_area_struct *vma,
+               unsigned long start, unsigned long end)
 {
+       int cpu = smp_processor_id();
        struct mm_struct *mm = vma->vm_mm;
        unsigned long flags;
 
-       if (mm->context == NO_CONTEXT)
+       if (mm->context.asid[cpu] == NO_CONTEXT)
                return;
 
 #if 0
        printk("[tlbrange<%02lx,%08lx,%08lx>]\n",
-                       (unsigned long)mm->context, start, end);
+                       (unsigned long)mm->context.asid[cpu], start, end);
 #endif
        local_irq_save(flags);
 
        if (end-start + (PAGE_SIZE-1) <= _TLB_ENTRIES << PAGE_SHIFT) {
                int oldpid = get_rasid_register();
-               set_rasid_register (ASID_INSERT(mm->context));
+
+               set_rasid_register(ASID_INSERT(mm->context.asid[cpu]));
                start &= PAGE_MASK;
                if (vma->vm_flags & VM_EXEC)
                        while(start < end) {
@@ -114,24 +120,25 @@ void flush_tlb_range (struct vm_area_struct *vma,
 
                set_rasid_register(oldpid);
        } else {
-               flush_tlb_mm(mm);
+               local_flush_tlb_mm(mm);
        }
        local_irq_restore(flags);
 }
 
-void flush_tlb_page (struct vm_area_struct *vma, unsigned long page)
+void local_flush_tlb_page(struct vm_area_struct *vma, unsigned long page)
 {
+       int cpu = smp_processor_id();
        struct mm_struct* mm = vma->vm_mm;
        unsigned long flags;
        int oldpid;
 
-       if(mm->context == NO_CONTEXT)
+       if (mm->context.asid[cpu] == NO_CONTEXT)
                return;
 
        local_irq_save(flags);
 
        oldpid = get_rasid_register();
-       set_rasid_register(ASID_INSERT(mm->context));
+       set_rasid_register(ASID_INSERT(mm->context.asid[cpu]));
 
        if (vma->vm_flags & VM_EXEC)
                invalidate_itlb_mapping(page);
index e9e1aad8c271048d4fde5bdac7ebef7e1cc79a96..5ae928275db67c6044f348e8922a2a4454050c7b 100644 (file)
@@ -38,7 +38,7 @@
 #define DRIVER_NAME "iss-netdev"
 #define ETH_MAX_PACKET 1500
 #define ETH_HEADER_OTHER 14
-#define ISS_NET_TIMER_VALUE (2 * HZ)
+#define ISS_NET_TIMER_VALUE (HZ / 10)
 
 
 static DEFINE_SPINLOCK(opened_lock);
@@ -56,8 +56,6 @@ static LIST_HEAD(devices);
 
 struct tuntap_info {
        char dev_name[IFNAMSIZ];
-       int fixed_config;
-       unsigned char gw[ETH_ALEN];
        int fd;
 };
 
@@ -67,7 +65,6 @@ struct tuntap_info {
 /* This structure contains out private information for the driver. */
 
 struct iss_net_private {
-
        struct list_head device_list;
        struct list_head opened_list;
 
@@ -83,9 +80,6 @@ struct iss_net_private {
        int index;
        int mtu;
 
-       unsigned char mac[ETH_ALEN];
-       int have_mac;
-
        struct {
                union {
                        struct tuntap_info tuntap;
@@ -118,68 +112,55 @@ static char *split_if_spec(char *str, ...)
                        *arg = str;
                if (end == NULL)
                        return NULL;
-               *end ++ = '\0';
+               *end++ = '\0';
                str = end;
        }
        va_end(ap);
        return str;
 }
 
+/* Set Ethernet address of the specified device. */
 
-#if 0
-/* Adjust SKB. */
-
-struct sk_buff *ether_adjust_skb(struct sk_buff *skb, int extra)
+static void setup_etheraddr(struct net_device *dev, char *str)
 {
-       if ((skb != NULL) && (skb_tailroom(skb) < extra)) {
-               struct sk_buff *skb2;
-
-               skb2 = skb_copy_expand(skb, 0, extra, GFP_ATOMIC);
-               dev_kfree_skb(skb);
-               skb = skb2;
+       unsigned char *addr = dev->dev_addr;
+       char *end;
+       int i;
+
+       if (str == NULL)
+               goto random;
+
+       for (i = 0; i < ETH_ALEN; i++) {
+               addr[i] = kstrtoul(str, 16, &end);
+               if ((end == str) ||
+                   ((*end != ':') && (*end != ',') && (*end != '\0'))) {
+                       pr_err("%s: failed to parse '%s' as an ethernet address\n",
+                              dev->name, str);
+                       goto random;
+               }
+               str = end + 1;
        }
-       if (skb != NULL)
-               skb_put(skb, extra);
-
-       return skb;
-}
-#endif
-
-/* Return the IP address as a string for a given device. */
-
-static void dev_ip_addr(void *d, char *buf, char *bin_buf)
-{
-       struct net_device *dev = d;
-       struct in_device *ip = dev->ip_ptr;
-       struct in_ifaddr *in;
-       __be32 addr;
-
-       if ((ip == NULL) || ((in = ip->ifa_list) == NULL)) {
-               printk(KERN_WARNING "Device not assigned an IP address!\n");
-               return;
+       if (is_multicast_ether_addr(addr)) {
+               pr_err("%s: attempt to assign a multicast ethernet address\n",
+                      dev->name);
+               goto random;
        }
-
-       addr = in->ifa_address;
-       sprintf(buf, "%d.%d.%d.%d", addr & 0xff, (addr >> 8) & 0xff,
-               (addr >> 16) & 0xff, addr >> 24);
-
-       if (bin_buf) {
-               bin_buf[0] = addr & 0xff;
-               bin_buf[1] = (addr >> 8) & 0xff;
-               bin_buf[2] = (addr >> 16) & 0xff;
-               bin_buf[3] = addr >> 24;
+       if (!is_valid_ether_addr(addr)) {
+               pr_err("%s: attempt to assign an invalid ethernet address\n",
+                      dev->name);
+               goto random;
        }
+       if (!is_local_ether_addr(addr))
+               pr_warn("%s: assigning a globally valid ethernet address\n",
+                       dev->name);
+       return;
+
+random:
+       pr_info("%s: choosing a random ethernet address\n",
+               dev->name);
+       eth_hw_addr_random(dev);
 }
 
-/* Set Ethernet address of the specified device. */
-
-static void inline set_ether_mac(void *d, unsigned char *addr)
-{
-       struct net_device *dev = d;
-       memcpy(dev->dev_addr, addr, ETH_ALEN);
-}
-
-
 /* ======================= TUNTAP TRANSPORT INTERFACE ====================== */
 
 static int tuntap_open(struct iss_net_private *lp)
@@ -189,24 +170,21 @@ static int tuntap_open(struct iss_net_private *lp)
        int err = -EINVAL;
        int fd;
 
-       /* We currently only support a fixed configuration. */
-
-       if (!lp->tp.info.tuntap.fixed_config)
-               return -EINVAL;
-
-       if ((fd = simc_open("/dev/net/tun", 02, 0)) < 0) {      /* O_RDWR */
-               printk("Failed to open /dev/net/tun, returned %d "
-                      "(errno = %d)\n", fd, errno);
+       fd = simc_open("/dev/net/tun", 02, 0); /* O_RDWR */
+       if (fd < 0) {
+               pr_err("%s: failed to open /dev/net/tun, returned %d (errno = %d)\n",
+                      lp->dev->name, fd, errno);
                return fd;
        }
 
-       memset(&ifr, 0, sizeof ifr);
+       memset(&ifr, 0, sizeof(ifr));
        ifr.ifr_flags = IFF_TAP | IFF_NO_PI;
-       strlcpy(ifr.ifr_name, dev_name, sizeof ifr.ifr_name);
+       strlcpy(ifr.ifr_name, dev_name, sizeof(ifr.ifr_name));
 
-       if ((err = simc_ioctl(fd, TUNSETIFF, (void*) &ifr)) < 0) {
-               printk("Failed to set interface, returned %d "
-                      "(errno = %d)\n", err, errno);
+       err = simc_ioctl(fd, TUNSETIFF, &ifr);
+       if (err < 0) {
+               pr_err("%s: failed to set interface %s, returned %d (errno = %d)\n",
+                      lp->dev->name, dev_name, err, errno);
                simc_close(fd);
                return err;
        }
@@ -217,27 +195,17 @@ static int tuntap_open(struct iss_net_private *lp)
 
 static void tuntap_close(struct iss_net_private *lp)
 {
-#if 0
-       if (lp->tp.info.tuntap.fixed_config)
-               iter_addresses(lp->tp.info.tuntap.dev, close_addr, lp->host.dev_name);
-#endif
        simc_close(lp->tp.info.tuntap.fd);
        lp->tp.info.tuntap.fd = -1;
 }
 
-static int tuntap_read (struct iss_net_private *lp, struct sk_buff **skb)
+static int tuntap_read(struct iss_net_private *lp, struct sk_buff **skb)
 {
-#if 0
-       *skb = ether_adjust_skb(*skb, ETH_HEADER_OTHER);
-       if (*skb == NULL)
-               return -ENOMEM;
-#endif
-
        return simc_read(lp->tp.info.tuntap.fd,
                        (*skb)->data, (*skb)->dev->mtu + ETH_HEADER_OTHER);
 }
 
-static int tuntap_write (struct iss_net_private *lp, struct sk_buff **skb)
+static int tuntap_write(struct iss_net_private *lp, struct sk_buff **skb)
 {
        return simc_write(lp->tp.info.tuntap.fd, (*skb)->data, (*skb)->len);
 }
@@ -253,45 +221,45 @@ static int tuntap_poll(struct iss_net_private *lp)
 }
 
 /*
- * Currently only a device name is supported.
- * ethX=tuntap[,[mac address][,[device name]]]
+ * ethX=tuntap,[mac address],device name
  */
 
 static int tuntap_probe(struct iss_net_private *lp, int index, char *init)
 {
-       const int len = strlen(TRANSPORT_TUNTAP_NAME);
+       struct net_device *dev = lp->dev;
        char *dev_name = NULL, *mac_str = NULL, *rem = NULL;
 
        /* Transport should be 'tuntap': ethX=tuntap,mac,dev_name */
 
-       if (strncmp(init, TRANSPORT_TUNTAP_NAME, len))
+       if (strncmp(init, TRANSPORT_TUNTAP_NAME,
+                   sizeof(TRANSPORT_TUNTAP_NAME) - 1))
                return 0;
 
-       if (*(init += strlen(TRANSPORT_TUNTAP_NAME)) == ',') {
-               if ((rem=split_if_spec(init+1, &mac_str, &dev_name)) != NULL) {
-                       printk("Extra garbage on specification : '%s'\n", rem);
+       init += sizeof(TRANSPORT_TUNTAP_NAME) - 1;
+       if (*init == ',') {
+               rem = split_if_spec(init + 1, &mac_str, &dev_name);
+               if (rem != NULL) {
+                       pr_err("%s: extra garbage on specification : '%s'\n",
+                              dev->name, rem);
                        return 0;
                }
        } else if (*init != '\0') {
-               printk("Invalid argument: %s. Skipping device!\n", init);
+               pr_err("%s: invalid argument: %s. Skipping device!\n",
+                      dev->name, init);
                return 0;
        }
 
-       if (dev_name) {
-               strncpy(lp->tp.info.tuntap.dev_name, dev_name,
-                        sizeof lp->tp.info.tuntap.dev_name);
-               lp->tp.info.tuntap.fixed_config = 1;
-       } else
-               strcpy(lp->tp.info.tuntap.dev_name, TRANSPORT_TUNTAP_NAME);
+       if (!dev_name) {
+               pr_err("%s: missing tuntap device name\n", dev->name);
+               return 0;
+       }
 
+       strlcpy(lp->tp.info.tuntap.dev_name, dev_name,
+               sizeof(lp->tp.info.tuntap.dev_name));
 
-#if 0
-       if (setup_etheraddr(mac_str, lp->mac))
-               lp->have_mac = 1;
-#endif
-       lp->mtu = TRANSPORT_TUNTAP_MTU;
+       setup_etheraddr(dev, mac_str);
 
-       //lp->info.tuntap.gate_addr = gate_addr;
+       lp->mtu = TRANSPORT_TUNTAP_MTU;
 
        lp->tp.info.tuntap.fd = -1;
 
@@ -302,13 +270,6 @@ static int tuntap_probe(struct iss_net_private *lp, int index, char *init)
        lp->tp.protocol = tuntap_protocol;
        lp->tp.poll = tuntap_poll;
 
-       printk("TUN/TAP backend - ");
-#if 0
-       if (lp->host.gate_addr != NULL)
-               printk("IP = %s", lp->host.gate_addr);
-#endif
-       printk("\n");
-
        return 1;
 }
 
@@ -327,7 +288,8 @@ static int iss_net_rx(struct net_device *dev)
 
        /* Try to allocate memory, if it fails, try again next round. */
 
-       if ((skb = dev_alloc_skb(dev->mtu + 2 + ETH_HEADER_OTHER)) == NULL) {
+       skb = dev_alloc_skb(dev->mtu + 2 + ETH_HEADER_OTHER);
+       if (skb == NULL) {
                lp->stats.rx_dropped++;
                return 0;
        }
@@ -347,7 +309,6 @@ static int iss_net_rx(struct net_device *dev)
 
                lp->stats.rx_bytes += skb->len;
                lp->stats.rx_packets++;
-       //      netif_rx(skb);
                netif_rx_ni(skb);
                return pkt_len;
        }
@@ -378,11 +339,11 @@ static int iss_net_poll(void)
                spin_unlock(&lp->lock);
 
                if (err < 0) {
-                       printk(KERN_ERR "Device '%s' read returned %d, "
-                              "shutting it down\n", lp->dev->name, err);
+                       pr_err("Device '%s' read returned %d, shutting it down\n",
+                              lp->dev->name, err);
                        dev_close(lp->dev);
                } else {
-                       // FIXME reactivate_fd(lp->fd, ISS_ETH_IRQ);
+                       /* FIXME reactivate_fd(lp->fd, ISS_ETH_IRQ); */
                }
        }
 
@@ -393,14 +354,11 @@ static int iss_net_poll(void)
 
 static void iss_net_timer(unsigned long priv)
 {
-       struct iss_net_private* lp = (struct iss_net_private*) priv;
+       struct iss_net_private *lp = (struct iss_net_private *)priv;
 
        spin_lock(&lp->lock);
-
        iss_net_poll();
-
        mod_timer(&lp->timer, jiffies + lp->timer_val);
-
        spin_unlock(&lp->lock);
 }
 
@@ -408,19 +366,14 @@ static void iss_net_timer(unsigned long priv)
 static int iss_net_open(struct net_device *dev)
 {
        struct iss_net_private *lp = netdev_priv(dev);
-       char addr[sizeof "255.255.255.255\0"];
        int err;
 
        spin_lock(&lp->lock);
 
-       if ((err = lp->tp.open(lp)) < 0)
+       err = lp->tp.open(lp);
+       if (err < 0)
                goto out;
 
-       if (!lp->have_mac) {
-               dev_ip_addr(dev, addr, &lp->mac[2]);
-               set_ether_mac(dev, lp->mac);
-       }
-
        netif_start_queue(dev);
 
        /* clear buffer - it can happen that the host side of the interface
@@ -448,7 +401,6 @@ out:
 static int iss_net_close(struct net_device *dev)
 {
        struct iss_net_private *lp = netdev_priv(dev);
-printk("iss_net_close!\n");
        netif_stop_queue(dev);
        spin_lock(&lp->lock);
 
@@ -490,7 +442,7 @@ static int iss_net_start_xmit(struct sk_buff *skb, struct net_device *dev)
 
        } else {
                netif_start_queue(dev);
-               printk(KERN_ERR "iss_net_start_xmit: failed(%d)\n", len);
+               pr_err("%s: %s failed(%d)\n", dev->name, __func__, len);
        }
 
        spin_unlock_irqrestore(&lp->lock, flags);
@@ -508,56 +460,27 @@ static struct net_device_stats *iss_net_get_stats(struct net_device *dev)
 
 static void iss_net_set_multicast_list(struct net_device *dev)
 {
-#if 0
-       if (dev->flags & IFF_PROMISC)
-               return;
-       else if (!netdev_mc_empty(dev))
-               dev->flags |= IFF_ALLMULTI;
-       else
-               dev->flags &= ~IFF_ALLMULTI;
-#endif
 }
 
 static void iss_net_tx_timeout(struct net_device *dev)
 {
-#if 0
-       dev->trans_start = jiffies;
-       netif_wake_queue(dev);
-#endif
 }
 
 static int iss_net_set_mac(struct net_device *dev, void *addr)
 {
-#if 0
        struct iss_net_private *lp = netdev_priv(dev);
        struct sockaddr *hwaddr = addr;
 
+       if (!is_valid_ether_addr(hwaddr->sa_data))
+               return -EADDRNOTAVAIL;
        spin_lock(&lp->lock);
        memcpy(dev->dev_addr, hwaddr->sa_data, ETH_ALEN);
        spin_unlock(&lp->lock);
-#endif
-
        return 0;
 }
 
 static int iss_net_change_mtu(struct net_device *dev, int new_mtu)
 {
-#if 0
-       struct iss_net_private *lp = netdev_priv(dev);
-       int err = 0;
-
-       spin_lock(&lp->lock);
-
-       // FIXME not needed new_mtu = transport_set_mtu(new_mtu, &lp->user);
-
-       if (new_mtu < 0)
-               err = new_mtu;
-       else
-               dev->mtu = new_mtu;
-
-       spin_unlock(&lp->lock);
-       return err;
-#endif
        return -EINVAL;
 }
 
@@ -582,7 +505,6 @@ static const struct net_device_ops iss_netdev_ops = {
        .ndo_validate_addr      = eth_validate_addr,
        .ndo_change_mtu         = iss_net_change_mtu,
        .ndo_set_mac_address    = iss_net_set_mac,
-       //.ndo_do_ioctl         = iss_net_ioctl,
        .ndo_tx_timeout         = iss_net_tx_timeout,
        .ndo_set_rx_mode        = iss_net_set_multicast_list,
 };
@@ -593,24 +515,29 @@ static int iss_net_configure(int index, char *init)
        struct iss_net_private *lp;
        int err;
 
-       if ((dev = alloc_etherdev(sizeof *lp)) == NULL) {
-               printk(KERN_ERR "eth_configure: failed to allocate device\n");
+       dev = alloc_etherdev(sizeof(*lp));
+       if (dev == NULL) {
+               pr_err("eth_configure: failed to allocate device\n");
                return 1;
        }
 
        /* Initialize private element. */
 
        lp = netdev_priv(dev);
-       *lp = ((struct iss_net_private) {
+       *lp = (struct iss_net_private) {
                .device_list            = LIST_HEAD_INIT(lp->device_list),
                .opened_list            = LIST_HEAD_INIT(lp->opened_list),
                .lock                   = __SPIN_LOCK_UNLOCKED(lp.lock),
                .dev                    = dev,
                .index                  = index,
-               //.fd                   = -1,
-               .mac                    = { 0xfe, 0xfd, 0x0, 0x0, 0x0, 0x0 },
-               .have_mac               = 0,
-               });
+               };
+
+       /*
+        * If this name ends up conflicting with an existing registered
+        * netdevice, that is OK, register_netdev{,ice}() will notice this
+        * and fail.
+        */
+       snprintf(dev->name, sizeof(dev->name), "eth%d", index);
 
        /*
         * Try all transport protocols.
@@ -618,14 +545,12 @@ static int iss_net_configure(int index, char *init)
         */
 
        if (!tuntap_probe(lp, index, init)) {
-               printk("Invalid arguments. Skipping device!\n");
+               pr_err("%s: invalid arguments. Skipping device!\n",
+                      dev->name);
                goto errout;
        }
 
-       printk(KERN_INFO "Netdevice %d ", index);
-       if (lp->have_mac)
-               printk("(%pM) ", lp->mac);
-       printk(": ");
+       pr_info("Netdevice %d (%pM)\n", index, dev->dev_addr);
 
        /* sysfs register */
 
@@ -641,14 +566,7 @@ static int iss_net_configure(int index, char *init)
        lp->pdev.id = index;
        lp->pdev.name = DRIVER_NAME;
        platform_device_register(&lp->pdev);
-       SET_NETDEV_DEV(dev,&lp->pdev.dev);
-
-       /*
-        * If this name ends up conflicting with an existing registered
-        * netdevice, that is OK, register_netdev{,ice}() will notice this
-        * and fail.
-        */
-       snprintf(dev->name, sizeof dev->name, "eth%d", index);
+       SET_NETDEV_DEV(dev, &lp->pdev.dev);
 
        dev->netdev_ops = &iss_netdev_ops;
        dev->mtu = lp->mtu;
@@ -660,7 +578,7 @@ static int iss_net_configure(int index, char *init)
        rtnl_unlock();
 
        if (err) {
-               printk("Error registering net device!\n");
+               pr_err("%s: error registering net device!\n", dev->name);
                /* XXX: should we call ->remove() here? */
                free_netdev(dev);
                return 1;
@@ -669,16 +587,11 @@ static int iss_net_configure(int index, char *init)
        init_timer(&lp->tl);
        lp->tl.function = iss_net_user_timer_expire;
 
-#if 0
-       if (lp->have_mac)
-               set_ether_mac(dev, lp->mac);
-#endif
        return 0;
 
 errout:
-       // FIXME: unregister; free, etc..
+       /* FIXME: unregister; free, etc.. */
        return -EIO;
-
 }
 
 /* ------------------------------------------------------------------------- */
@@ -717,7 +630,8 @@ static int __init iss_net_setup(char *str)
                printk(ERR "Device %d is negative\n", n);
                return 1;
        }
-       if (*(str = end) != '=') {
+       str = end;
+       if (*str != '=') {
                printk(ERR "Expected '=' after device number\n");
                return 1;
        }
@@ -739,7 +653,7 @@ static int __init iss_net_setup(char *str)
 
        new = alloc_bootmem(sizeof(*new));
        if (new == NULL) {
-               printk("Alloc_bootmem failed\n");
+               printk(ERR "Alloc_bootmem failed\n");
                return 1;
        }
 
@@ -753,7 +667,7 @@ static int __init iss_net_setup(char *str)
 
 #undef ERR
 
-__setup("eth=", iss_net_setup);
+__setup("eth", iss_net_setup);
 
 /*
  * Initialize all ISS Ethernet devices previously registered in iss_net_setup.
index 4416773cbde5c1755c17abf3d5028babaf80ef6c..aeb316b7ff8838f9bcff5d8f29202bac7043823c 100644 (file)
 #ifndef __XTENSA_XTAVNET_HARDWARE_H
 #define __XTENSA_XTAVNET_HARDWARE_H
 
-/* By default NO_IRQ is defined to 0 in Linux, but we use the
-   interrupt 0 for UART... */
-#define NO_IRQ                 -1
-
 /* Memory configuration. */
 
 #define PLATFORM_DEFAULT_MEM_START 0x00000000
@@ -30,7 +26,7 @@
 
 /* Default assignment of LX60 devices to external interrupts. */
 
-#ifdef CONFIG_ARCH_HAS_SMP
+#ifdef CONFIG_XTENSA_MX
 #define DUART16552_INTNUM      XCHAL_EXTINT3_NUM
 #define OETH_IRQ               XCHAL_EXTINT4_NUM
 #else
index 97d6fc48deff036b1ea5a1bd940b3d998411deec..39ca751a62551c72cc2d0582a08b0ba8667ca43a 100644 (file)
@@ -1,7 +1,6 @@
 #ifndef _XTENSA_S6000_IRQ_H
 #define _XTENSA_S6000_IRQ_H
 
-#define NO_IRQ         (-1)
 #define VARIANT_NR_IRQS 8 /* GPIO interrupts */
 
 extern void variant_irq_enable(unsigned int irq);
index 3792a1aa52b88d3439cdc66195230cc1b20f0f2a..07bc79c340125be220f7b667b9cb8e16db36d4ab 100644 (file)
@@ -61,3 +61,7 @@ config VERSATILE_FPGA_IRQ_NR
        int
        default 4
        depends on VERSATILE_FPGA_IRQ
+
+config XTENSA_MX
+       bool
+       select IRQ_DOMAIN
index f743006ce7adaafcb7b497c2b9ced838684d7c5d..679e2d102a296e84a60acdc9d24a3cbcffe7d5d5 100644 (file)
@@ -23,3 +23,5 @@ obj-$(CONFIG_RENESAS_IRQC)            += irq-renesas-irqc.o
 obj-$(CONFIG_VERSATILE_FPGA_IRQ)       += irq-versatile-fpga.o
 obj-$(CONFIG_ARCH_VT8500)              += irq-vt8500.o
 obj-$(CONFIG_TB10X_IRQC)               += irq-tb10x.o
+obj-$(CONFIG_XTENSA)                   += irq-xtensa-pic.o
+obj-$(CONFIG_XTENSA_MX)                        += irq-xtensa-mx.o
diff --git a/drivers/irqchip/irq-xtensa-mx.c b/drivers/irqchip/irq-xtensa-mx.c
new file mode 100644 (file)
index 0000000..86f91f0
--- /dev/null
@@ -0,0 +1,166 @@
+/*
+ * Xtensa MX interrupt distributor
+ *
+ * Copyright (C) 2002 - 2013 Tensilica, Inc.
+ *
+ * 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/interrupt.h>
+#include <linux/irqdomain.h>
+#include <linux/irq.h>
+#include <linux/of.h>
+
+#include <asm/mxregs.h>
+
+#include "irqchip.h"
+
+#define HW_IRQ_IPI_COUNT 2
+#define HW_IRQ_MX_BASE 2
+#define HW_IRQ_EXTERN_BASE 3
+
+static DEFINE_PER_CPU(unsigned int, cached_irq_mask);
+
+static int xtensa_mx_irq_map(struct irq_domain *d, unsigned int irq,
+               irq_hw_number_t hw)
+{
+       if (hw < HW_IRQ_IPI_COUNT) {
+               struct irq_chip *irq_chip = d->host_data;
+               irq_set_chip_and_handler_name(irq, irq_chip,
+                               handle_percpu_irq, "ipi");
+               irq_set_status_flags(irq, IRQ_LEVEL);
+               return 0;
+       }
+       return xtensa_irq_map(d, irq, hw);
+}
+
+/*
+ * Device Tree IRQ specifier translation function which works with one or
+ * two cell bindings. First cell value maps directly to the hwirq number.
+ * Second cell if present specifies whether hwirq number is external (1) or
+ * internal (0).
+ */
+static int xtensa_mx_irq_domain_xlate(struct irq_domain *d,
+               struct device_node *ctrlr,
+               const u32 *intspec, unsigned int intsize,
+               unsigned long *out_hwirq, unsigned int *out_type)
+{
+       return xtensa_irq_domain_xlate(intspec, intsize,
+                       intspec[0], intspec[0] + HW_IRQ_EXTERN_BASE,
+                       out_hwirq, out_type);
+}
+
+static const struct irq_domain_ops xtensa_mx_irq_domain_ops = {
+       .xlate = xtensa_mx_irq_domain_xlate,
+       .map = xtensa_mx_irq_map,
+};
+
+void secondary_init_irq(void)
+{
+       this_cpu_write(cached_irq_mask,
+                       XCHAL_INTTYPE_MASK_EXTERN_EDGE |
+                       XCHAL_INTTYPE_MASK_EXTERN_LEVEL);
+       set_sr(XCHAL_INTTYPE_MASK_EXTERN_EDGE |
+                       XCHAL_INTTYPE_MASK_EXTERN_LEVEL, intenable);
+}
+
+static void xtensa_mx_irq_mask(struct irq_data *d)
+{
+       unsigned long mask = 1u << d->hwirq;
+
+       if (mask & (XCHAL_INTTYPE_MASK_EXTERN_EDGE |
+                               XCHAL_INTTYPE_MASK_EXTERN_LEVEL)) {
+               set_er(1u << (xtensa_get_ext_irq_no(d->hwirq) -
+                                       HW_IRQ_MX_BASE), MIENG);
+       } else {
+               int cpu = get_cpu();
+               per_cpu(cached_irq_mask, cpu) &= ~mask;
+               set_sr(per_cpu(cached_irq_mask, cpu), intenable);
+               put_cpu();
+       }
+}
+
+static void xtensa_mx_irq_unmask(struct irq_data *d)
+{
+       unsigned long mask = 1u << d->hwirq;
+
+       if (mask & (XCHAL_INTTYPE_MASK_EXTERN_EDGE |
+                               XCHAL_INTTYPE_MASK_EXTERN_LEVEL)) {
+               set_er(1u << (xtensa_get_ext_irq_no(d->hwirq) -
+                                       HW_IRQ_MX_BASE), MIENGSET);
+       } else {
+               int cpu = get_cpu();
+               per_cpu(cached_irq_mask, cpu) |= mask;
+               set_sr(per_cpu(cached_irq_mask, cpu), intenable);
+               put_cpu();
+       }
+}
+
+static void xtensa_mx_irq_enable(struct irq_data *d)
+{
+       variant_irq_enable(d->hwirq);
+       xtensa_mx_irq_unmask(d);
+}
+
+static void xtensa_mx_irq_disable(struct irq_data *d)
+{
+       xtensa_mx_irq_mask(d);
+       variant_irq_disable(d->hwirq);
+}
+
+static void xtensa_mx_irq_ack(struct irq_data *d)
+{
+       set_sr(1 << d->hwirq, intclear);
+}
+
+static int xtensa_mx_irq_retrigger(struct irq_data *d)
+{
+       set_sr(1 << d->hwirq, intset);
+       return 1;
+}
+
+static int xtensa_mx_irq_set_affinity(struct irq_data *d,
+               const struct cpumask *dest, bool force)
+{
+       unsigned mask = 1u << cpumask_any(dest);
+
+       set_er(mask, MIROUT(d->hwirq - HW_IRQ_MX_BASE));
+       return 0;
+
+}
+
+static struct irq_chip xtensa_mx_irq_chip = {
+       .name           = "xtensa-mx",
+       .irq_enable     = xtensa_mx_irq_enable,
+       .irq_disable    = xtensa_mx_irq_disable,
+       .irq_mask       = xtensa_mx_irq_mask,
+       .irq_unmask     = xtensa_mx_irq_unmask,
+       .irq_ack        = xtensa_mx_irq_ack,
+       .irq_retrigger  = xtensa_mx_irq_retrigger,
+       .irq_set_affinity = xtensa_mx_irq_set_affinity,
+};
+
+int __init xtensa_mx_init_legacy(struct device_node *interrupt_parent)
+{
+       struct irq_domain *root_domain =
+               irq_domain_add_legacy(NULL, NR_IRQS, 0, 0,
+                               &xtensa_mx_irq_domain_ops,
+                               &xtensa_mx_irq_chip);
+       irq_set_default_host(root_domain);
+       secondary_init_irq();
+       return 0;
+}
+
+static int __init xtensa_mx_init(struct device_node *np,
+               struct device_node *interrupt_parent)
+{
+       struct irq_domain *root_domain =
+               irq_domain_add_linear(np, NR_IRQS, &xtensa_mx_irq_domain_ops,
+                               &xtensa_mx_irq_chip);
+       irq_set_default_host(root_domain);
+       secondary_init_irq();
+       return 0;
+}
+IRQCHIP_DECLARE(xtensa_mx_irq_chip, "xtensa,mx", xtensa_mx_init);
diff --git a/drivers/irqchip/irq-xtensa-pic.c b/drivers/irqchip/irq-xtensa-pic.c
new file mode 100644 (file)
index 0000000..846d181
--- /dev/null
@@ -0,0 +1,108 @@
+/*
+ * Xtensa built-in interrupt controller
+ *
+ * Copyright (C) 2002 - 2013 Tensilica, Inc.
+ * Copyright (C) 1992, 1998 Linus Torvalds, Ingo Molnar
+ *
+ * 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.
+ *
+ * Chris Zankel <chris@zankel.net>
+ * Kevin Chea
+ */
+
+#include <linux/interrupt.h>
+#include <linux/irqdomain.h>
+#include <linux/irq.h>
+#include <linux/of.h>
+
+#include "irqchip.h"
+
+unsigned int cached_irq_mask;
+
+/*
+ * Device Tree IRQ specifier translation function which works with one or
+ * two cell bindings. First cell value maps directly to the hwirq number.
+ * Second cell if present specifies whether hwirq number is external (1) or
+ * internal (0).
+ */
+static int xtensa_pic_irq_domain_xlate(struct irq_domain *d,
+               struct device_node *ctrlr,
+               const u32 *intspec, unsigned int intsize,
+               unsigned long *out_hwirq, unsigned int *out_type)
+{
+       return xtensa_irq_domain_xlate(intspec, intsize,
+                       intspec[0], intspec[0],
+                       out_hwirq, out_type);
+}
+
+static const struct irq_domain_ops xtensa_irq_domain_ops = {
+       .xlate = xtensa_pic_irq_domain_xlate,
+       .map = xtensa_irq_map,
+};
+
+static void xtensa_irq_mask(struct irq_data *d)
+{
+       cached_irq_mask &= ~(1 << d->hwirq);
+       set_sr(cached_irq_mask, intenable);
+}
+
+static void xtensa_irq_unmask(struct irq_data *d)
+{
+       cached_irq_mask |= 1 << d->hwirq;
+       set_sr(cached_irq_mask, intenable);
+}
+
+static void xtensa_irq_enable(struct irq_data *d)
+{
+       variant_irq_enable(d->hwirq);
+       xtensa_irq_unmask(d);
+}
+
+static void xtensa_irq_disable(struct irq_data *d)
+{
+       xtensa_irq_mask(d);
+       variant_irq_disable(d->hwirq);
+}
+
+static void xtensa_irq_ack(struct irq_data *d)
+{
+       set_sr(1 << d->hwirq, intclear);
+}
+
+static int xtensa_irq_retrigger(struct irq_data *d)
+{
+       set_sr(1 << d->hwirq, intset);
+       return 1;
+}
+
+static struct irq_chip xtensa_irq_chip = {
+       .name           = "xtensa",
+       .irq_enable     = xtensa_irq_enable,
+       .irq_disable    = xtensa_irq_disable,
+       .irq_mask       = xtensa_irq_mask,
+       .irq_unmask     = xtensa_irq_unmask,
+       .irq_ack        = xtensa_irq_ack,
+       .irq_retrigger  = xtensa_irq_retrigger,
+};
+
+int __init xtensa_pic_init_legacy(struct device_node *interrupt_parent)
+{
+       struct irq_domain *root_domain =
+               irq_domain_add_legacy(NULL, NR_IRQS, 0, 0,
+                               &xtensa_irq_domain_ops, &xtensa_irq_chip);
+       irq_set_default_host(root_domain);
+       return 0;
+}
+
+static int __init xtensa_pic_init(struct device_node *np,
+               struct device_node *interrupt_parent)
+{
+       struct irq_domain *root_domain =
+               irq_domain_add_linear(np, NR_IRQS, &xtensa_irq_domain_ops,
+                               &xtensa_irq_chip);
+       irq_set_default_host(root_domain);
+       return 0;
+}
+IRQCHIP_DECLARE(xtensa_irq_chip, "xtensa,pic", xtensa_pic_init);
diff --git a/include/linux/irqchip/xtensa-mx.h b/include/linux/irqchip/xtensa-mx.h
new file mode 100644 (file)
index 0000000..9c3b6ec
--- /dev/null
@@ -0,0 +1,17 @@
+/*
+ * Xtensa MX interrupt distributor
+ *
+ * Copyright (C) 2002 - 2013 Tensilica, Inc.
+ *
+ * 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.
+ */
+
+#ifndef __LINUX_IRQCHIP_XTENSA_MX_H
+#define __LINUX_IRQCHIP_XTENSA_MX_H
+
+struct device_node;
+int xtensa_mx_init_legacy(struct device_node *interrupt_parent);
+
+#endif /* __LINUX_IRQCHIP_XTENSA_MX_H */
diff --git a/include/linux/irqchip/xtensa-pic.h b/include/linux/irqchip/xtensa-pic.h
new file mode 100644 (file)
index 0000000..48718ae
--- /dev/null
@@ -0,0 +1,18 @@
+/*
+ * Xtensa built-in interrupt controller
+ *
+ * Copyright (C) 2002 - 2013 Tensilica, Inc.
+ * Copyright (C) 1992, 1998 Linus Torvalds, Ingo Molnar
+ *
+ * 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.
+ */
+
+#ifndef __LINUX_IRQCHIP_XTENSA_PIC_H
+#define __LINUX_IRQCHIP_XTENSA_PIC_H
+
+struct device_node;
+int xtensa_pic_init_legacy(struct device_node *interrupt_parent);
+
+#endif /* __LINUX_IRQCHIP_XTENSA_PIC_H */