Skip to content

Commit

Permalink
xtensa: add SMP support
Browse files Browse the repository at this point in the history
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 <jcmvbkbc@gmail.com>
Signed-off-by: Chris Zankel <chris@zankel.net>
  • Loading branch information
Max Filippov authored and Chris Zankel committed Jan 14, 2014
1 parent 26a8e96 commit f615136
Show file tree
Hide file tree
Showing 24 changed files with 967 additions and 199 deletions.
37 changes: 36 additions & 1 deletion arch/xtensa/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down
4 changes: 3 additions & 1 deletion arch/xtensa/include/asm/barrier.h
Original file line number Diff line number Diff line change
Expand Up @@ -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()
Expand Down
8 changes: 2 additions & 6 deletions arch/xtensa/include/asm/bitops.h
Original file line number Diff line number Diff line change
Expand Up @@ -22,12 +22,8 @@
#include <asm/processor.h>
#include <asm/byteorder.h>

#ifdef CONFIG_SMP
# error SMP not supported on this architecture
#endif

#define smp_mb__before_clear_bit() barrier()
#define smp_mb__after_clear_bit() barrier()
#define smp_mb__before_clear_bit() smp_mb()
#define smp_mb__after_clear_bit() smp_mb()

#include <asm-generic/bitops/non-atomic.h>

Expand Down
40 changes: 25 additions & 15 deletions arch/xtensa/include/asm/cacheflush.h
Original file line number Diff line number Diff line change
@@ -1,18 +1,14 @@
/*
* include/asm-xtensa/cacheflush.h
*
* This file is subject to the terms and conditions of the GNU General Public
* License. See the file "COPYING" in the main directory of this archive
* for more details.
*
* (C) 2001 - 2007 Tensilica Inc.
* (C) 2001 - 2013 Tensilica Inc.
*/

#ifndef _XTENSA_CACHEFLUSH_H
#define _XTENSA_CACHEFLUSH_H

#ifdef __KERNEL__

#include <linux/mm.h>
#include <asm/processor.h>
#include <asm/page.h>
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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(); \
Expand 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

Expand All @@ -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)); \
Expand Down Expand Up @@ -253,5 +264,4 @@ static inline void flush_invalidate_dcache_unaligned(u32 addr, u32 size)
}
}

#endif /* __KERNEL__ */
#endif /* _XTENSA_CACHEFLUSH_H */
10 changes: 5 additions & 5 deletions arch/xtensa/include/asm/mmu.h
Original file line number Diff line number Diff line change
@@ -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
Expand All @@ -15,8 +13,10 @@
#include <asm-generic/mmu.h>
#else

/* Default "unsigned long" context */
typedef unsigned long mm_context_t;
typedef struct {
unsigned long asid[NR_CPUS];
unsigned int cpu;
} mm_context_t;

#endif /* CONFIG_MMU */
#endif /* _XTENSA_MMU_H */
100 changes: 57 additions & 43 deletions arch/xtensa/include/asm/mmu_context.h
Original file line number Diff line number Diff line change
@@ -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
Expand All @@ -20,22 +18,25 @@
#include <linux/stringify.h>
#include <linux/sched.h>

#include <variant/core.h>
#include <asm/vectors.h>

#include <asm/pgtable.h>
#include <asm/cacheflush.h>
#include <asm/tlbflush.h>
#include <asm-generic/mm_hooks.h>
#include <asm-generic/percpu.h>

#if (XCHAL_HAVE_TLBS != 1)
# error "Linux must have an MMU!"
#endif

extern unsigned long asid_cache;
DECLARE_PER_CPU(unsigned long, asid_cache);
#define cpu_asid_cache(cpu) per_cpu(asid_cache, cpu)

/*
* NO_CONTEXT is the invalid ASID value that we don't ever assign to
* any user or kernel context.
* any user or kernel context. We use the reserved values in the
* ASID_INSERT macro below.
*
* 0 invalid
* 1 kernel
Expand Down Expand Up @@ -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
Expand Down
8 changes: 8 additions & 0 deletions arch/xtensa/include/asm/ptrace.h
Original file line number Diff line number Diff line change
Expand Up @@ -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])
Expand Down
Loading

0 comments on commit f615136

Please sign in to comment.