From f0a34615add3912bf8f5354c02998aa506f4d15b Mon Sep 17 00:00:00 2001 From: Max Filippov Date: Mon, 18 Nov 2013 11:05:52 +0400 Subject: xtensa: clean up include/asm/vectors.h - drop unused PHYSICAL_MEMORY_ADDRESS and XC_PADDR - fix NMI_VECTOR_VADDR and INTLEVEL7_VECTOR_VADDR definitions: there should be no XCHAL_ prefix in them; - fix the following warning seen with gcc-4.8.1: arch/xtensa/include/asm/vectors.h:71:5: warning: "XCHAL_HAVE_VECBASE" is not defined [-Wundef] Signed-off-by: Max Filippov Signed-off-by: Chris Zankel --- arch/xtensa/include/asm/vectors.h | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) (limited to 'arch/xtensa/include') diff --git a/arch/xtensa/include/asm/vectors.h b/arch/xtensa/include/asm/vectors.h index c52b656d0310..37e073b21a50 100644 --- a/arch/xtensa/include/asm/vectors.h +++ b/arch/xtensa/include/asm/vectors.h @@ -30,11 +30,9 @@ #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) @@ -81,11 +77,9 @@ #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 -- cgit v1.2.3 From ea1d3ed3cfde34914a517f97320b80d9ccee7507 Mon Sep 17 00:00:00 2001 From: Max Filippov Date: Mon, 18 Nov 2013 11:05:53 +0400 Subject: xtensa: remove trailing colons in asm statement Signed-off-by: Max Filippov Signed-off-by: Chris Zankel --- arch/xtensa/include/asm/ftrace.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'arch/xtensa/include') diff --git a/arch/xtensa/include/asm/ftrace.h b/arch/xtensa/include/asm/ftrace.h index 73cc3f482304..736b9d214d80 100644 --- a/arch/xtensa/include/asm/ftrace.h +++ b/arch/xtensa/include/asm/ftrace.h @@ -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); -- cgit v1.2.3 From 220c062688c31bbf992921fca172ddd1e609f8f6 Mon Sep 17 00:00:00 2001 From: Baruch Siach Date: Mon, 18 Nov 2013 06:46:43 +0200 Subject: xtensa: implement robust futex atomic uaccess ops This enables the set_robust_list(2) system call. Signed-off-by: Baruch Siach Signed-off-by: Chris Zankel --- arch/xtensa/include/asm/Kbuild | 1 - arch/xtensa/include/asm/futex.h | 147 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 147 insertions(+), 1 deletion(-) create mode 100644 arch/xtensa/include/asm/futex.h (limited to 'arch/xtensa/include') diff --git a/arch/xtensa/include/asm/Kbuild b/arch/xtensa/include/asm/Kbuild index 228d6aee3a16..5851db291583 100644 --- a/arch/xtensa/include/asm/Kbuild +++ b/arch/xtensa/include/asm/Kbuild @@ -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 diff --git a/arch/xtensa/include/asm/futex.h b/arch/xtensa/include/asm/futex.h new file mode 100644 index 000000000000..b39531babec0 --- /dev/null +++ b/arch/xtensa/include/asm/futex.h @@ -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 + */ + +#ifndef _ASM_XTENSA_FUTEX_H +#define _ASM_XTENSA_FUTEX_H + +#ifdef __KERNEL__ + +#include +#include +#include + +#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 */ -- cgit v1.2.3 From 5515daa9ca5a0992cc1c55d4c6c7ddb116e829b4 Mon Sep 17 00:00:00 2001 From: Max Filippov Date: Thu, 17 Oct 2013 02:42:15 +0400 Subject: xtensa: fix arch spinlock function names Signed-off-by: Max Filippov Signed-off-by: Chris Zankel --- arch/xtensa/include/asm/spinlock.h | 31 +++++++++++++++++-------------- arch/xtensa/include/asm/spinlock_types.h | 20 ++++++++++++++++++++ 2 files changed, 37 insertions(+), 14 deletions(-) create mode 100644 arch/xtensa/include/asm/spinlock_types.h (limited to 'arch/xtensa/include') diff --git a/arch/xtensa/include/asm/spinlock.h b/arch/xtensa/include/asm/spinlock.h index 03975906b36f..1d95fa5dcd10 100644 --- a/arch/xtensa/include/asm/spinlock.h +++ b/arch/xtensa/include/asm/spinlock.h @@ -28,13 +28,13 @@ * 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 index 000000000000..7ec5ce10c9e9 --- /dev/null +++ b/arch/xtensa/include/asm/spinlock_types.h @@ -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 -- cgit v1.2.3 From 01e3b3cb2d70ca9d95769e863ca71a85f84c3917 Mon Sep 17 00:00:00 2001 From: Max Filippov Date: Thu, 17 Oct 2013 02:42:16 +0400 Subject: xtensa: fix __delay for small loop count Avoid __delay counter underflow for loop counts < 2. Signed-off-by: Max Filippov Signed-off-by: Chris Zankel --- arch/xtensa/include/asm/delay.h | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) (limited to 'arch/xtensa/include') diff --git a/arch/xtensa/include/asm/delay.h b/arch/xtensa/include/asm/delay.h index 3899610c1dff..742b89f3ca2c 100644 --- a/arch/xtensa/include/asm/delay.h +++ b/arch/xtensa/include/asm/delay.h @@ -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 -- cgit v1.2.3 From c8f3a7dc0102fc9f89cc373c6dd7beb49916a695 Mon Sep 17 00:00:00 2001 From: Max Filippov Date: Thu, 17 Oct 2013 02:42:21 +0400 Subject: xtensa: move init_mmu declaration to mmu_context.h Secondary CPUs need this declaration to initialize their MMUs. Signed-off-by: Max Filippov Signed-off-by: Chris Zankel --- arch/xtensa/include/asm/mmu_context.h | 6 ++++++ arch/xtensa/kernel/setup.c | 7 +------ 2 files changed, 7 insertions(+), 6 deletions(-) (limited to 'arch/xtensa/include') diff --git a/arch/xtensa/include/asm/mmu_context.h b/arch/xtensa/include/asm/mmu_context.h index d43525a286bb..86292c28674f 100644 --- a/arch/xtensa/include/asm/mmu_context.h +++ b/arch/xtensa/include/asm/mmu_context.h @@ -49,6 +49,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" diff --git a/arch/xtensa/kernel/setup.c b/arch/xtensa/kernel/setup.c index 9bc6f0644892..3d353140c2d0 100644 --- a/arch/xtensa/kernel/setup.c +++ b/arch/xtensa/kernel/setup.c @@ -37,6 +37,7 @@ #endif #include +#include #include #include #include @@ -85,12 +86,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); -- cgit v1.2.3 From cbd1de2e8e46207cead11034f92ea8a018b11189 Mon Sep 17 00:00:00 2001 From: Max Filippov Date: Sun, 1 Dec 2013 12:59:49 +0400 Subject: xtensa: move built-in PIC to drivers/irqchip Extract xtensa built-in interrupt controller implementation from xtensa/kernel/irq.c and move it to other irqchips, providing way to instantiate it from the device tree. Signed-off-by: Max Filippov Signed-off-by: Chris Zankel --- arch/xtensa/boot/dts/xtfpga.dtsi | 2 +- arch/xtensa/include/asm/irq.h | 7 ++ arch/xtensa/kernel/irq.c | 137 ++++++++----------------------------- drivers/irqchip/Makefile | 1 + drivers/irqchip/irq-xtensa-pic.c | 108 +++++++++++++++++++++++++++++ include/linux/irqchip/xtensa-pic.h | 18 +++++ 6 files changed, 165 insertions(+), 108 deletions(-) create mode 100644 drivers/irqchip/irq-xtensa-pic.c create mode 100644 include/linux/irqchip/xtensa-pic.h (limited to 'arch/xtensa/include') diff --git a/arch/xtensa/boot/dts/xtfpga.dtsi b/arch/xtensa/boot/dts/xtfpga.dtsi index 7eda6ecf7eef..b042c3f508e6 100644 --- a/arch/xtensa/boot/dts/xtfpga.dtsi +++ b/arch/xtensa/boot/dts/xtfpga.dtsi @@ -26,7 +26,7 @@ }; pic: pic { - compatible = "xtensa,pic"; + compatible = "cdns,xtensa-pic"; /* one cell: internal irq number, * two cells: second cell == 0: internal irq number * second cell == 1: external irq number diff --git a/arch/xtensa/include/asm/irq.h b/arch/xtensa/include/asm/irq.h index 4c0ccc9c4f4c..16464f2f8ecc 100644 --- a/arch/xtensa/include/asm/irq.h +++ b/arch/xtensa/include/asm/irq.h @@ -43,5 +43,12 @@ static __inline__ int irq_canonicalize(int irq) } struct irqaction; +struct irq_domain; + +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); #endif /* _XTENSA_IRQ_H */ diff --git a/arch/xtensa/kernel/irq.c b/arch/xtensa/kernel/irq.c index 6f4f9749cff7..ada1e4893dd6 100644 --- a/arch/xtensa/kernel/irq.c +++ b/arch/xtensa/kernel/irq.c @@ -18,28 +18,20 @@ #include #include #include +#include +#include #include #include #include #include -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", @@ -74,83 +66,57 @@ int arch_show_interrupts(struct seq_file *p, int prec) 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 +129,12 @@ 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) -{ - 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; -} - -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 - root_domain = irq_domain_add_legacy(intc, NR_IRQS, 0, 0, - &xtensa_irq_domain_ops, NULL); + xtensa_pic_init_legacy(NULL); #endif - irq_set_default_host(root_domain); - variant_init_irq(); } diff --git a/drivers/irqchip/Makefile b/drivers/irqchip/Makefile index c60b9010b152..c81a7f3f6506 100644 --- a/drivers/irqchip/Makefile +++ b/drivers/irqchip/Makefile @@ -22,3 +22,4 @@ 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 diff --git a/drivers/irqchip/irq-xtensa-pic.c b/drivers/irqchip/irq-xtensa-pic.c new file mode 100644 index 000000000000..7d71126d1ce5 --- /dev/null +++ b/drivers/irqchip/irq-xtensa-pic.c @@ -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 + * Kevin Chea + */ + +#include +#include +#include +#include + +#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, "cdns,xtensa-pic", xtensa_pic_init); diff --git a/include/linux/irqchip/xtensa-pic.h b/include/linux/irqchip/xtensa-pic.h new file mode 100644 index 000000000000..48718ae5ab68 --- /dev/null +++ b/include/linux/irqchip/xtensa-pic.h @@ -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 */ -- cgit v1.2.3 From 26a8e96a8b37e8070fa9dcb1b7490cf4d4492d50 Mon Sep 17 00:00:00 2001 From: Max Filippov Date: Sun, 1 Dec 2013 12:04:57 +0400 Subject: xtensa: add MX irqchip MX is an interrupt distributor used in some SMP-capable xtensa configurations. Signed-off-by: Max Filippov Signed-off-by: Chris Zankel --- arch/xtensa/include/asm/irq.h | 1 + arch/xtensa/include/asm/mxregs.h | 46 ++++++++++ arch/xtensa/include/asm/processor.h | 20 +++++ arch/xtensa/kernel/irq.c | 8 ++ drivers/irqchip/Kconfig | 4 + drivers/irqchip/Makefile | 1 + drivers/irqchip/irq-xtensa-mx.c | 164 ++++++++++++++++++++++++++++++++++++ include/linux/irqchip/xtensa-mx.h | 17 ++++ 8 files changed, 261 insertions(+) create mode 100644 arch/xtensa/include/asm/mxregs.h create mode 100644 drivers/irqchip/irq-xtensa-mx.c create mode 100644 include/linux/irqchip/xtensa-mx.h (limited to 'arch/xtensa/include') diff --git a/arch/xtensa/include/asm/irq.h b/arch/xtensa/include/asm/irq.h index 16464f2f8ecc..7d194d462150 100644 --- a/arch/xtensa/include/asm/irq.h +++ b/arch/xtensa/include/asm/irq.h @@ -50,5 +50,6 @@ int xtensa_irq_domain_xlate(const u32 *intspec, unsigned int intsize, 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 */ diff --git a/arch/xtensa/include/asm/mxregs.h b/arch/xtensa/include/asm/mxregs.h new file mode 100644 index 000000000000..73dcc5456f68 --- /dev/null +++ b/arch/xtensa/include/asm/mxregs.h @@ -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 */ diff --git a/arch/xtensa/include/asm/processor.h b/arch/xtensa/include/asm/processor.h index 7e409a5b0ec5..abb59708a3b7 100644 --- a/arch/xtensa/include/asm/processor.h +++ b/arch/xtensa/include/asm/processor.h @@ -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 */ diff --git a/arch/xtensa/kernel/irq.c b/arch/xtensa/kernel/irq.c index 3cef58e28332..7d49730f4056 100644 --- a/arch/xtensa/kernel/irq.c +++ b/arch/xtensa/kernel/irq.c @@ -123,6 +123,14 @@ unsigned xtensa_map_ext_irq(unsigned ext_irq) return XCHAL_NUM_INTERRUPTS; } +unsigned xtensa_get_ext_irq_no(unsigned irq) +{ + unsigned mask = (XCHAL_INTTYPE_MASK_EXTERN_EDGE | + XCHAL_INTTYPE_MASK_EXTERN_LEVEL) & + ((1u << irq) - 1); + return hweight32(mask); +} + void __init init_IRQ(void) { #ifdef CONFIG_OF diff --git a/drivers/irqchip/Kconfig b/drivers/irqchip/Kconfig index 3792a1aa52b8..07bc79c34012 100644 --- a/drivers/irqchip/Kconfig +++ b/drivers/irqchip/Kconfig @@ -61,3 +61,7 @@ config VERSATILE_FPGA_IRQ_NR int default 4 depends on VERSATILE_FPGA_IRQ + +config XTENSA_MX + bool + select IRQ_DOMAIN diff --git a/drivers/irqchip/Makefile b/drivers/irqchip/Makefile index c81a7f3f6506..66913f9587b3 100644 --- a/drivers/irqchip/Makefile +++ b/drivers/irqchip/Makefile @@ -23,3 +23,4 @@ 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 index 000000000000..f693f1bc1348 --- /dev/null +++ b/drivers/irqchip/irq-xtensa-mx.c @@ -0,0 +1,164 @@ +/* + * 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 +#include +#include +#include + +#include + +#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 int 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 { + mask = __this_cpu_read(cached_irq_mask) & ~mask; + __this_cpu_write(cached_irq_mask, mask); + set_sr(mask, intenable); + } +} + +static void xtensa_mx_irq_unmask(struct irq_data *d) +{ + unsigned int 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 { + mask |= __this_cpu_read(cached_irq_mask); + __this_cpu_write(cached_irq_mask, mask); + set_sr(mask, intenable); + } +} + +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, "cdns,xtensa-mx", xtensa_mx_init); diff --git a/include/linux/irqchip/xtensa-mx.h b/include/linux/irqchip/xtensa-mx.h new file mode 100644 index 000000000000..9c3b6ecc8b2f --- /dev/null +++ b/include/linux/irqchip/xtensa-mx.h @@ -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 */ -- cgit v1.2.3 From f615136c06a791364f5afa8b8ba965315a6440f1 Mon Sep 17 00:00:00 2001 From: Max Filippov Date: Thu, 17 Oct 2013 02:42:26 +0400 Subject: xtensa: add SMP support This is largely based on SMP code from the xtensa-2.6.29-smp tree by Piet Delaney, Marc Gauthier, Joe Taylor, Christian Zankel (and possibly other Tensilica folks). Signed-off-by: Max Filippov Signed-off-by: Chris Zankel --- arch/xtensa/Kconfig | 37 +- arch/xtensa/include/asm/barrier.h | 4 +- arch/xtensa/include/asm/bitops.h | 8 +- arch/xtensa/include/asm/cacheflush.h | 40 +- arch/xtensa/include/asm/mmu.h | 10 +- arch/xtensa/include/asm/mmu_context.h | 100 +++-- arch/xtensa/include/asm/ptrace.h | 8 + arch/xtensa/include/asm/smp.h | 29 +- arch/xtensa/include/asm/timex.h | 14 +- arch/xtensa/include/asm/tlbflush.h | 42 +- arch/xtensa/include/asm/traps.h | 1 + arch/xtensa/kernel/Makefile | 1 + arch/xtensa/kernel/head.S | 130 ++++-- arch/xtensa/kernel/irq.c | 15 +- arch/xtensa/kernel/mxhead.S | 85 ++++ arch/xtensa/kernel/setup.c | 66 ++- arch/xtensa/kernel/smp.c | 465 +++++++++++++++++++++ arch/xtensa/kernel/traps.c | 33 +- arch/xtensa/kernel/vmlinux.lds.S | 26 ++ arch/xtensa/mm/cache.c | 7 +- arch/xtensa/mm/fault.c | 2 +- arch/xtensa/mm/mmu.c | 4 +- arch/xtensa/mm/tlb.c | 37 +- .../platforms/xtfpga/include/platform/hardware.h | 2 +- 24 files changed, 967 insertions(+), 199 deletions(-) create mode 100644 arch/xtensa/kernel/mxhead.S create mode 100644 arch/xtensa/kernel/smp.c (limited to 'arch/xtensa/include') diff --git a/arch/xtensa/Kconfig b/arch/xtensa/Kconfig index fb140ae3860d..4b09c60b6b30 100644 --- a/arch/xtensa/Kconfig +++ b/arch/xtensa/Kconfig @@ -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 @@ -65,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 @@ -105,6 +107,39 @@ 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 MATH_EMULATION bool "Math emulation" help diff --git a/arch/xtensa/include/asm/barrier.h b/arch/xtensa/include/asm/barrier.h index ef021677d536..8e5e5c980a7a 100644 --- a/arch/xtensa/include/asm/barrier.h +++ b/arch/xtensa/include/asm/barrier.h @@ -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() diff --git a/arch/xtensa/include/asm/bitops.h b/arch/xtensa/include/asm/bitops.h index 84afe58d5d37..7b6873ae84c2 100644 --- a/arch/xtensa/include/asm/bitops.h +++ b/arch/xtensa/include/asm/bitops.h @@ -22,12 +22,8 @@ #include #include -#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 diff --git a/arch/xtensa/include/asm/cacheflush.h b/arch/xtensa/include/asm/cacheflush.h index 127cd48883c4..555a98a18453 100644 --- a/arch/xtensa/include/asm/cacheflush.h +++ b/arch/xtensa/include/asm/cacheflush.h @@ -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 #include #include @@ -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 */ diff --git a/arch/xtensa/include/asm/mmu.h b/arch/xtensa/include/asm/mmu.h index 8554b2c8b17a..71afe418d0e5 100644 --- a/arch/xtensa/include/asm/mmu.h +++ b/arch/xtensa/include/asm/mmu.h @@ -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 @@ -15,8 +13,10 @@ #include #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 */ diff --git a/arch/xtensa/include/asm/mmu_context.h b/arch/xtensa/include/asm/mmu_context.h index 86292c28674f..d33c71a8c9ec 100644 --- a/arch/xtensa/include/asm/mmu_context.h +++ b/arch/xtensa/include/asm/mmu_context.h @@ -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 @@ -20,22 +18,25 @@ #include #include -#include +#include #include #include #include #include +#include #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 @@ -68,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) +{ + 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; + } + cpu_asid_cache(cpu) = asid; + mm->context.asid[cpu] = asid; + mm->context.cpu = cpu; +} + +static inline void get_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; + /* + * 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); } - mm->context = asid_cache; } -static inline void -__load_mmu_context(struct mm_struct *mm) +static inline void activate_context(struct mm_struct *mm, unsigned int cpu) { - set_rasid_register(ASID_INSERT(mm->context)); + 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/ptrace.h b/arch/xtensa/include/asm/ptrace.h index 81f31bc9dde0..598e752dcbcd 100644 --- a/arch/xtensa/include/asm/ptrace.h +++ b/arch/xtensa/include/asm/ptrace.h @@ -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]) diff --git a/arch/xtensa/include/asm/smp.h b/arch/xtensa/include/asm/smp.h index 83c569e3bdbd..30ac58cc70df 100644 --- a/arch/xtensa/include/asm/smp.h +++ b/arch/xtensa/include/asm/smp.h @@ -1,27 +1,34 @@ /* - * 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); + +#endif /* CONFIG_SMP */ #endif /* _XTENSA_SMP_H */ diff --git a/arch/xtensa/include/asm/timex.h b/arch/xtensa/include/asm/timex.h index 27fa3c170662..ca929e6a38b5 100644 --- a/arch/xtensa/include/asm/timex.h +++ b/arch/xtensa/include/asm/timex.h @@ -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 #include @@ -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 */ diff --git a/arch/xtensa/include/asm/tlbflush.h b/arch/xtensa/include/asm/tlbflush.h index 43dd348a5a47..fc34274ce41b 100644 --- a/arch/xtensa/include/asm/tlbflush.h +++ b/arch/xtensa/include/asm/tlbflush.h @@ -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 #include @@ -34,12 +30,37 @@ * - 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 */ diff --git a/arch/xtensa/include/asm/traps.h b/arch/xtensa/include/asm/traps.h index 917488a0ab00..8c194f6af45e 100644 --- a/arch/xtensa/include/asm/traps.h +++ b/arch/xtensa/include/asm/traps.h @@ -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) { diff --git a/arch/xtensa/kernel/Makefile b/arch/xtensa/kernel/Makefile index f90265ec1ccc..18d962a8c0c2 100644 --- a/arch/xtensa/kernel/Makefile +++ b/arch/xtensa/kernel/Makefile @@ -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 diff --git a/arch/xtensa/kernel/head.S b/arch/xtensa/kernel/head.S index 7d740ebbe198..74ec62c892bc 100644 --- a/arch/xtensa/kernel/head.S +++ b/arch/xtensa/kernel/head.S @@ -19,6 +19,7 @@ #include #include #include +#include #include #include @@ -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 @@ -90,19 +108,6 @@ ENDPROC(_start) 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,44 @@ 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) +/* + * DATA section + */ + + .section ".data.init.refok" + .align 4 +ENTRY(start_info) + .long init_thread_union + KERNEL_STACK_SIZE + /* * BSS section */ diff --git a/arch/xtensa/kernel/irq.c b/arch/xtensa/kernel/irq.c index 7d49730f4056..fad9e0059765 100644 --- a/arch/xtensa/kernel/irq.c +++ b/arch/xtensa/kernel/irq.c @@ -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 * * @@ -19,10 +19,12 @@ #include #include #include +#include #include #include #include +#include #include #include @@ -55,6 +57,9 @@ asmlinkage void do_IRQ(int hwirq, struct pt_regs *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; @@ -135,8 +140,16 @@ void __init init_IRQ(void) { #ifdef CONFIG_OF irqchip_init(); +#else +#ifdef CONFIG_HAVE_SMP + xtensa_mx_init_legacy(NULL); #else xtensa_pic_init_legacy(NULL); +#endif +#endif + +#ifdef CONFIG_SMP + ipi_init(); #endif variant_init_irq(); } diff --git a/arch/xtensa/kernel/mxhead.S b/arch/xtensa/kernel/mxhead.S new file mode 100644 index 000000000000..77a161a112c5 --- /dev/null +++ b/arch/xtensa/kernel/mxhead.S @@ -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 + * Chris Zankel + * Marc Gauthier + * Pete Delaney + */ + +#include + +#include +#include +#include +#include + + + .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 diff --git a/arch/xtensa/kernel/setup.c b/arch/xtensa/kernel/setup.c index 3d353140c2d0..dfd8f52c05d8 100644 --- a/arch/xtensa/kernel/setup.c +++ b/arch/xtensa/kernel/setup.c @@ -21,6 +21,8 @@ #include #include #include +#include +#include #include #include @@ -46,6 +48,7 @@ #include #include #include +#include #include @@ -496,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(); @@ -512,6 +519,21 @@ 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); + register_cpu(cpu, i); + } + + return 0; +} +subsys_initcall(topology_init); + void machine_restart(char * cmd) { platform_restart(); @@ -537,21 +559,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 @@ -663,7 +691,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 * @@ -679,10 +707,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 index 000000000000..46bdd142a07d --- /dev/null +++ b/arch/xtensa/kernel/smp.c @@ -0,0 +1,465 @@ +/* + * 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 + * Joe Taylor + * Pete Delaney +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#ifdef CONFIG_SMP +# if XCHAL_HAVE_S32C1I == 0 +# error "The S32C1I option is required for SMP." +# endif +#endif + +/* 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 __init 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)); +} + +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; + + 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; +} + +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); +} diff --git a/arch/xtensa/kernel/traps.c b/arch/xtensa/kernel/traps.c index 3dbe8648df1f..3c0ff5746fe2 100644 --- a/arch/xtensa/kernel/traps.c +++ b/arch/xtensa/kernel/traps.c @@ -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); @@ -313,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 __init 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. * @@ -337,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; @@ -368,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 __init secondary_trap_init(void) +{ + trap_init_excsave(); } +#endif /* * This function dumps the current valid window frame and other base registers. diff --git a/arch/xtensa/kernel/vmlinux.lds.S b/arch/xtensa/kernel/vmlinux.lds.S index 21acd11b5df2..ee32c0085dff 100644 --- a/arch/xtensa/kernel/vmlinux.lds.S +++ b/arch/xtensa/kernel/vmlinux.lds.S @@ -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 = .; diff --git a/arch/xtensa/mm/cache.c b/arch/xtensa/mm/cache.c index 81edeab82d17..ba4c47f291b1 100644 --- a/arch/xtensa/mm/cache.c +++ b/arch/xtensa/mm/cache.c @@ -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 diff --git a/arch/xtensa/mm/fault.c b/arch/xtensa/mm/fault.c index 70fa7bc42b4a..b57c4f91f487 100644 --- a/arch/xtensa/mm/fault.c +++ b/arch/xtensa/mm/fault.c @@ -21,7 +21,7 @@ #include #include -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 diff --git a/arch/xtensa/mm/mmu.c b/arch/xtensa/mm/mmu.c index c43771c974be..5bb8e3c61d85 100644 --- a/arch/xtensa/mm/mmu.c +++ b/arch/xtensa/mm/mmu.c @@ -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. */ diff --git a/arch/xtensa/mm/tlb.c b/arch/xtensa/mm/tlb.c index ca9d2366bf12..ade623826788 100644 --- a/arch/xtensa/mm/tlb.c +++ b/arch/xtensa/mm/tlb.c @@ -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); diff --git a/arch/xtensa/platforms/xtfpga/include/platform/hardware.h b/arch/xtensa/platforms/xtfpga/include/platform/hardware.h index 4b43ff133be0..aeb316b7ff88 100644 --- a/arch/xtensa/platforms/xtfpga/include/platform/hardware.h +++ b/arch/xtensa/platforms/xtfpga/include/platform/hardware.h @@ -26,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 -- cgit v1.2.3 From 49b424fedaf88d0fa9913082b8c1ccd012a8a972 Mon Sep 17 00:00:00 2001 From: Max Filippov Date: Thu, 17 Oct 2013 02:42:28 +0400 Subject: xtensa: implement CPU hotplug Signed-off-by: Max Filippov Signed-off-by: Chris Zankel --- arch/xtensa/Kconfig | 9 +++ arch/xtensa/include/asm/irq.h | 1 + arch/xtensa/include/asm/smp.h | 9 +++ arch/xtensa/kernel/head.S | 51 ++++++++++++++++- arch/xtensa/kernel/irq.c | 49 ++++++++++++++++ arch/xtensa/kernel/setup.c | 1 + arch/xtensa/kernel/smp.c | 128 +++++++++++++++++++++++++++++++++++++++++- arch/xtensa/kernel/traps.c | 4 +- 8 files changed, 248 insertions(+), 4 deletions(-) (limited to 'arch/xtensa/include') diff --git a/arch/xtensa/Kconfig b/arch/xtensa/Kconfig index 4b09c60b6b30..70a160be3464 100644 --- a/arch/xtensa/Kconfig +++ b/arch/xtensa/Kconfig @@ -140,6 +140,15 @@ config NR_CPUS 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 diff --git a/arch/xtensa/include/asm/irq.h b/arch/xtensa/include/asm/irq.h index 7d194d462150..f71f88ea7646 100644 --- a/arch/xtensa/include/asm/irq.h +++ b/arch/xtensa/include/asm/irq.h @@ -45,6 +45,7 @@ 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); diff --git a/arch/xtensa/include/asm/smp.h b/arch/xtensa/include/asm/smp.h index 30ac58cc70df..4e43f5643891 100644 --- a/arch/xtensa/include/asm/smp.h +++ b/arch/xtensa/include/asm/smp.h @@ -29,6 +29,15 @@ 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 */ diff --git a/arch/xtensa/kernel/head.S b/arch/xtensa/kernel/head.S index 74ec62c892bc..aeeb3cc8a410 100644 --- a/arch/xtensa/kernel/head.S +++ b/arch/xtensa/kernel/head.S @@ -103,7 +103,7 @@ _SetupMMU: ENDPROC(_start) - __INIT + __REF .literal_position ENTRY(_startup) @@ -302,6 +302,55 @@ should_never_return: 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 */ diff --git a/arch/xtensa/kernel/irq.c b/arch/xtensa/kernel/irq.c index fad9e0059765..482868a2de6e 100644 --- a/arch/xtensa/kernel/irq.c +++ b/arch/xtensa/kernel/irq.c @@ -153,3 +153,52 @@ void __init init_IRQ(void) #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/setup.c b/arch/xtensa/kernel/setup.c index dfd8f52c05d8..d21bfa7a28e0 100644 --- a/arch/xtensa/kernel/setup.c +++ b/arch/xtensa/kernel/setup.c @@ -527,6 +527,7 @@ static int __init topology_init(void) for_each_possible_cpu(i) { struct cpu *cpu = &per_cpu(cpu_data, i); + cpu->hotpluggable = !!i; register_cpu(cpu, i); } diff --git a/arch/xtensa/kernel/smp.c b/arch/xtensa/kernel/smp.c index 46bdd142a07d..1c7a209795e8 100644 --- a/arch/xtensa/kernel/smp.c +++ b/arch/xtensa/kernel/smp.c @@ -40,6 +40,11 @@ # 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 @@ -106,7 +111,7 @@ 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 __init secondary_start_kernel(void) +void secondary_start_kernel(void) { struct mm_struct *mm = &init_mm; unsigned int cpu = smp_processor_id(); @@ -174,6 +179,9 @@ static void mx_cpu_stop(void *p) __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) @@ -182,6 +190,11 @@ static int boot_secondary(unsigned int cpu, struct task_struct *ts) 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) { @@ -234,6 +247,85 @@ int __cpu_up(unsigned int cpu, struct task_struct *idle) 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, @@ -463,3 +555,37 @@ void flush_icache_range(unsigned long start, unsigned long 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); +} diff --git a/arch/xtensa/kernel/traps.c b/arch/xtensa/kernel/traps.c index 3c0ff5746fe2..eebbfd8c26fc 100644 --- a/arch/xtensa/kernel/traps.c +++ b/arch/xtensa/kernel/traps.c @@ -332,7 +332,7 @@ void * __init trap_set_handler(int cause, void *handler) } -static void __init trap_init_excsave(void) +static void trap_init_excsave(void) { unsigned long excsave1 = (unsigned long)this_cpu_ptr(exc_table); __asm__ __volatile__("wsr %0, excsave1\n" : : "a" (excsave1)); @@ -384,7 +384,7 @@ void __init trap_init(void) } #ifdef CONFIG_SMP -void __init secondary_trap_init(void) +void secondary_trap_init(void) { trap_init_excsave(); } -- cgit v1.2.3 From 4809bb468f140d561dfbb785e8dcdda5bae8c1f2 Mon Sep 17 00:00:00 2001 From: Baruch Siach Date: Sun, 17 Nov 2013 08:14:54 +0200 Subject: xtensa: avoid duplicate of IO range definitions Define IO addresses in a single place, to make it easier to change for non-standard memory maps. Signed-off-by: Baruch Siach Signed-off-by: Max Filippov --- arch/xtensa/include/asm/initialize_mmu.h | 8 ++++---- arch/xtensa/include/asm/io.h | 6 +----- arch/xtensa/include/asm/vectors.h | 5 +++++ 3 files changed, 10 insertions(+), 9 deletions(-) (limited to 'arch/xtensa/include') diff --git a/arch/xtensa/include/asm/initialize_mmu.h b/arch/xtensa/include/asm/initialize_mmu.h index 722553f17db3..a2078a2e5476 100644 --- a/arch/xtensa/include/asm/initialize_mmu.h +++ b/arch/xtensa/include/asm/initialize_mmu.h @@ -123,13 +123,13 @@ wdtlb a4, a5 witlb a4, a5 - movi a5, 0xe0000006 - movi a4, 0xf0000000 + CA_WRITEBACK + movi a5, XCHAL_KIO_CACHED_VADDR + 6 + movi a4, XCHAL_KIO_PADDR + CA_WRITEBACK wdtlb a4, a5 witlb a4, a5 - movi a5, 0xf0000006 - movi a4, 0xf0000000 + CA_BYPASS + movi a5, XCHAL_KIO_BYPASS_VADDR + 6 + movi a4, XCHAL_KIO_PADDR + CA_BYPASS wdtlb a4, a5 witlb a4, a5 diff --git a/arch/xtensa/include/asm/io.h b/arch/xtensa/include/asm/io.h index 700c2e6f2d25..1482a3636381 100644 --- a/arch/xtensa/include/asm/io.h +++ b/arch/xtensa/include/asm/io.h @@ -14,16 +14,12 @@ #ifdef __KERNEL__ #include #include +#include #include #include #include -#define XCHAL_KIO_CACHED_VADDR 0xe0000000 -#define XCHAL_KIO_BYPASS_VADDR 0xf0000000 -#define XCHAL_KIO_PADDR 0xf0000000 -#define XCHAL_KIO_SIZE 0x10000000 - #define IOADDR(x) (XCHAL_KIO_BYPASS_VADDR + (x)) #define IO_SPACE_LIMIT ~0 diff --git a/arch/xtensa/include/asm/vectors.h b/arch/xtensa/include/asm/vectors.h index 37e073b21a50..221a60d804d5 100644 --- a/arch/xtensa/include/asm/vectors.h +++ b/arch/xtensa/include/asm/vectors.h @@ -20,6 +20,11 @@ #include +#define XCHAL_KIO_CACHED_VADDR 0xe0000000 +#define XCHAL_KIO_BYPASS_VADDR 0xf0000000 +#define XCHAL_KIO_PADDR 0xf0000000 +#define XCHAL_KIO_SIZE 0x10000000 + #if defined(CONFIG_MMU) /* Will Become VECBASE */ -- cgit v1.2.3 From 6cb971114f633a0bf240c20b681d989b45e3ec56 Mon Sep 17 00:00:00 2001 From: Baruch Siach Date: Sun, 29 Dec 2013 11:03:30 +0200 Subject: xtensa: remap io area defined in device tree MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Use the simple-bus node to discover the io area, and remap the cached and bypass io ranges. The parent-bus-address value of the first triplet in the "ranges" property is used. This value is rounded down to the nearest 256MB boundary. The length of the io area is fixed at 256MB; the "ranges" property length value is ignored. Other limitations: (1) only the first simple-bus node is considered, and (2) only the first triplet of the "ranges" property is considered. See ePAPR 1.1 §6.5 for the simple-bus node description, and §2.3.8 for the "ranges" property description. Signed-off-by: Baruch Siach Signed-off-by: Max Filippov --- Documentation/xtensa/mmu.txt | 18 ++++++++++++++++ arch/xtensa/include/asm/initialize_mmu.h | 9 ++++---- arch/xtensa/include/asm/io.h | 10 +++++++++ arch/xtensa/include/asm/vectors.h | 8 ++++++- arch/xtensa/kernel/setup.c | 37 ++++++++++++++++++++++++++++++++ arch/xtensa/mm/mmu.c | 16 ++++++++++++++ 6 files changed, 93 insertions(+), 5 deletions(-) (limited to 'arch/xtensa/include') diff --git a/Documentation/xtensa/mmu.txt b/Documentation/xtensa/mmu.txt index 2b1af7606d57..0312fe66475c 100644 --- a/Documentation/xtensa/mmu.txt +++ b/Documentation/xtensa/mmu.txt @@ -44,3 +44,21 @@ After step 4, we jump to intended (linked) address of this code. 40..5F -> 40 40..5F -> pc -> pc 40..5F -> pc 20..3F -> 20 -> 20 20..3F -> 20 00..1F -> 00 -> 00 00..1F -> 00 + +The default location of IO peripherals is above 0xf0000000. This may change +using a "ranges" property in a device tree simple-bus node. See ePAPR 1.1, §6.5 +for details on the syntax and semantic of simple-bus nodes. The following +limitations apply: + +1. Only top level simple-bus nodes are considered + +2. Only one (first) simple-bus node is considered + +3. Empty "ranges" properties are not supported + +4. Only the first triplet in the "ranges" property is considered + +5. The parent-bus-address value is rounded down to the nearest 256MB boundary + +6. The IO area covers the entire 256MB segment of parent-bus-address; the + "ranges" triplet length field is ignored diff --git a/arch/xtensa/include/asm/initialize_mmu.h b/arch/xtensa/include/asm/initialize_mmu.h index a2078a2e5476..600781edc8a3 100644 --- a/arch/xtensa/include/asm/initialize_mmu.h +++ b/arch/xtensa/include/asm/initialize_mmu.h @@ -26,6 +26,9 @@ #include #include +#define CA_BYPASS (_PAGE_CA_BYPASS | _PAGE_HW_WRITE | _PAGE_HW_EXEC) +#define CA_WRITEBACK (_PAGE_CA_WB | _PAGE_HW_WRITE | _PAGE_HW_EXEC) + #ifdef __ASSEMBLY__ #define XTENSA_HWVERSION_RC_2009_0 230000 @@ -80,8 +83,6 @@ /* Step 2: map 0x40000000..0x47FFFFFF to paddr containing this code * and jump to the new mapping. */ -#define CA_BYPASS (_PAGE_CA_BYPASS | _PAGE_HW_WRITE | _PAGE_HW_EXEC) -#define CA_WRITEBACK (_PAGE_CA_WB | _PAGE_HW_WRITE | _PAGE_HW_EXEC) srli a3, a0, 27 slli a3, a3, 27 @@ -124,12 +125,12 @@ witlb a4, a5 movi a5, XCHAL_KIO_CACHED_VADDR + 6 - movi a4, XCHAL_KIO_PADDR + CA_WRITEBACK + movi a4, XCHAL_KIO_DEFAULT_PADDR + CA_WRITEBACK wdtlb a4, a5 witlb a4, a5 movi a5, XCHAL_KIO_BYPASS_VADDR + 6 - movi a4, XCHAL_KIO_PADDR + CA_BYPASS + movi a4, XCHAL_KIO_DEFAULT_PADDR + CA_BYPASS wdtlb a4, a5 witlb a4, a5 diff --git a/arch/xtensa/include/asm/io.h b/arch/xtensa/include/asm/io.h index 1482a3636381..2a042d430c25 100644 --- a/arch/xtensa/include/asm/io.h +++ b/arch/xtensa/include/asm/io.h @@ -24,6 +24,16 @@ #define IO_SPACE_LIMIT ~0 #ifdef CONFIG_MMU + +#if XCHAL_HAVE_PTP_MMU && XCHAL_HAVE_SPANNING_WAY && CONFIG_OF +extern unsigned long xtensa_kio_paddr; + +static inline unsigned long xtensa_get_kio_paddr(void) +{ + return xtensa_kio_paddr; +} +#endif + /* * Return the virtual address for the specified bus memory. * Note that we currently don't support any address outside the KIO segment. diff --git a/arch/xtensa/include/asm/vectors.h b/arch/xtensa/include/asm/vectors.h index 221a60d804d5..5791b45d5a5d 100644 --- a/arch/xtensa/include/asm/vectors.h +++ b/arch/xtensa/include/asm/vectors.h @@ -22,9 +22,15 @@ #define XCHAL_KIO_CACHED_VADDR 0xe0000000 #define XCHAL_KIO_BYPASS_VADDR 0xf0000000 -#define XCHAL_KIO_PADDR 0xf0000000 +#define XCHAL_KIO_DEFAULT_PADDR 0xf0000000 #define XCHAL_KIO_SIZE 0x10000000 +#if XCHAL_HAVE_PTP_MMU && XCHAL_HAVE_SPANNING_WAY && CONFIG_OF +#define XCHAL_KIO_PADDR xtensa_get_kio_paddr() +#else +#define XCHAL_KIO_PADDR XCHAL_KIO_DEFAULT_PADDR +#endif + #if defined(CONFIG_MMU) /* Will Become VECBASE */ diff --git a/arch/xtensa/kernel/setup.c b/arch/xtensa/kernel/setup.c index f38badeb7747..7d12af1317f1 100644 --- a/arch/xtensa/kernel/setup.c +++ b/arch/xtensa/kernel/setup.c @@ -212,6 +212,42 @@ static int __init parse_bootparam(const bp_tag_t* tag) #ifdef CONFIG_OF bool __initdata dt_memory_scan = false; +#if XCHAL_HAVE_PTP_MMU && XCHAL_HAVE_SPANNING_WAY +unsigned long xtensa_kio_paddr = XCHAL_KIO_DEFAULT_PADDR; +EXPORT_SYMBOL(xtensa_kio_paddr); + +static int __init xtensa_dt_io_area(unsigned long node, const char *uname, + int depth, void *data) +{ + const __be32 *ranges; + unsigned long len; + + if (depth > 1) + return 0; + + if (!of_flat_dt_is_compatible(node, "simple-bus")) + return 0; + + ranges = of_get_flat_dt_prop(node, "ranges", &len); + if (!ranges) + return 1; + if (len == 0) + return 1; + + xtensa_kio_paddr = of_read_ulong(ranges+1, 1); + /* round down to nearest 256MB boundary */ + xtensa_kio_paddr &= 0xf0000000; + + return 1; +} +#else +static int __init xtensa_dt_io_area(unsigned long node, const char *uname, + int depth, void *data) +{ + return 1; +} +#endif + void __init early_init_dt_add_memory_arch(u64 base, u64 size) { if (!dt_memory_scan) @@ -232,6 +268,7 @@ void __init early_init_devtree(void *params) dt_memory_scan = true; early_init_dt_scan(params); + of_scan_flat_dt(xtensa_dt_io_area, NULL); if (!command_line[0]) strlcpy(command_line, boot_command_line, COMMAND_LINE_SIZE); diff --git a/arch/xtensa/mm/mmu.c b/arch/xtensa/mm/mmu.c index 5bb8e3c61d85..36ec171698b8 100644 --- a/arch/xtensa/mm/mmu.c +++ b/arch/xtensa/mm/mmu.c @@ -13,6 +13,8 @@ #include #include #include +#include +#include void __init paging_init(void) { @@ -37,6 +39,20 @@ void init_mmu(void) set_itlbcfg_register(0); set_dtlbcfg_register(0); #endif +#if XCHAL_HAVE_PTP_MMU && XCHAL_HAVE_SPANNING_WAY && CONFIG_OF + /* + * Update the IO area mapping in case xtensa_kio_paddr has changed + */ + write_dtlb_entry(__pte(xtensa_kio_paddr + CA_WRITEBACK), + XCHAL_KIO_CACHED_VADDR + 6); + write_itlb_entry(__pte(xtensa_kio_paddr + CA_WRITEBACK), + XCHAL_KIO_CACHED_VADDR + 6); + write_dtlb_entry(__pte(xtensa_kio_paddr + CA_BYPASS), + XCHAL_KIO_BYPASS_VADDR + 6); + write_itlb_entry(__pte(xtensa_kio_paddr + CA_BYPASS), + XCHAL_KIO_BYPASS_VADDR + 6); +#endif + local_flush_tlb_all(); /* Set rasid register to a known value. */ -- cgit v1.2.3 From a6f3eefad8d1c9abd98fa03ba2b3abeb708e3b95 Mon Sep 17 00:00:00 2001 From: Baruch Siach Date: Thu, 28 Nov 2013 18:00:04 +0200 Subject: xtensa: enable HAVE_PERF_EVENTS This allows the perf tool to monitor kernel tracepoint events. Signed-off-by: Baruch Siach Signed-off-by: Max Filippov --- arch/xtensa/Kconfig | 1 + arch/xtensa/include/asm/perf_event.h | 4 ++++ 2 files changed, 5 insertions(+) create mode 100644 arch/xtensa/include/asm/perf_event.h (limited to 'arch/xtensa/include') diff --git a/arch/xtensa/Kconfig b/arch/xtensa/Kconfig index 49d2e843552b..c3ccf379289a 100644 --- a/arch/xtensa/Kconfig +++ b/arch/xtensa/Kconfig @@ -19,6 +19,7 @@ config XTENSA select HAVE_OPROFILE select HAVE_FUNCTION_TRACER select HAVE_IRQ_TIME_ACCOUNTING + select HAVE_PERF_EVENTS help Xtensa processors are 32-bit RISC machines designed by Tensilica primarily for embedded systems. These processors are both diff --git a/arch/xtensa/include/asm/perf_event.h b/arch/xtensa/include/asm/perf_event.h new file mode 100644 index 000000000000..5aa4590acaae --- /dev/null +++ b/arch/xtensa/include/asm/perf_event.h @@ -0,0 +1,4 @@ +#ifndef __ASM_XTENSA_PERF_EVENT_H +#define __ASM_XTENSA_PERF_EVENT_H + +#endif /* __ASM_XTENSA_PERF_EVENT_H */ -- cgit v1.2.3 From 58f60c222e8d4503a4be654cbd6d4e190dc1a7d8 Mon Sep 17 00:00:00 2001 From: Max Filippov Date: Fri, 10 Jan 2014 12:02:18 +0400 Subject: xtensa: clean up udelay Replace division with shift, use ccount_freq instead of loops_per_jiffy. Introduce __MAX_UDELAY and (undefined) __bad_udelay, break build for too big udelay constants. Remove irrelevant comment, clean up code style. Signed-off-by: Max Filippov --- arch/xtensa/include/asm/delay.h | 21 +++++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) (limited to 'arch/xtensa/include') diff --git a/arch/xtensa/include/asm/delay.h b/arch/xtensa/include/asm/delay.h index 742b89f3ca2c..69ba4629bed0 100644 --- a/arch/xtensa/include/asm/delay.h +++ b/arch/xtensa/include/asm/delay.h @@ -27,18 +27,27 @@ static inline void __delay(unsigned long loops) : "+r" (loops)); } -/* For SMP/NUMA systems, change boot_cpu_data to something like - * local_cpu_data->... where local_cpu_data points to the current - * cpu. */ +/* Undefined function to get compile-time error */ +void __bad_udelay(void); -static __inline__ void udelay (unsigned long usecs) +#define __MAX_UDELAY 30000 + +static inline void __udelay(unsigned long usecs) { unsigned long start = get_ccount(); - unsigned long cycles = usecs * (loops_per_jiffy / (1000000UL / HZ)); + unsigned long cycles = (usecs * (ccount_freq >> 15)) >> 5; /* Note: all variables are unsigned (can wrap around)! */ while (((unsigned long)get_ccount()) - start < cycles) - ; + cpu_relax(); +} + +static inline void udelay(unsigned long usec) +{ + if (__builtin_constant_p(usec) && usec >= __MAX_UDELAY) + __bad_udelay(); + else + __udelay(usec); } #endif -- cgit v1.2.3 From 9ed82c6866e2ab671935a75ea454047e8bddb177 Mon Sep 17 00:00:00 2001 From: Max Filippov Date: Fri, 10 Jan 2014 12:03:35 +0400 Subject: xtensa: implement ndelay Proper ndelay implementation allows for faster IO rate with drivers that use ndelay to access their device registers, as otherwise ndelay is emulated with udelay. Signed-off-by: Max Filippov --- arch/xtensa/include/asm/delay.h | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) (limited to 'arch/xtensa/include') diff --git a/arch/xtensa/include/asm/delay.h b/arch/xtensa/include/asm/delay.h index 69ba4629bed0..24304b39a5c7 100644 --- a/arch/xtensa/include/asm/delay.h +++ b/arch/xtensa/include/asm/delay.h @@ -29,8 +29,10 @@ static inline void __delay(unsigned long loops) /* Undefined function to get compile-time error */ void __bad_udelay(void); +void __bad_ndelay(void); #define __MAX_UDELAY 30000 +#define __MAX_NDELAY 30000 static inline void __udelay(unsigned long usecs) { @@ -50,4 +52,24 @@ static inline void udelay(unsigned long usec) __udelay(usec); } +static inline void __ndelay(unsigned long nsec) +{ + /* + * Inner shift makes sure multiplication doesn't overflow + * for legitimate nsec values + */ + unsigned long cycles = (nsec * (ccount_freq >> 15)) >> 15; + __delay(cycles); +} + +#define ndelay(n) ndelay(n) + +static inline void ndelay(unsigned long nsec) +{ + if (__builtin_constant_p(nsec) && nsec >= __MAX_NDELAY) + __bad_ndelay(); + else + __ndelay(nsec); +} + #endif -- cgit v1.2.3