public inbox for linux-arch@vger.kernel.org
 help / color / mirror / Atom feed
* Re: [PATCH 0/5] Blackfin SMP like patchset
       [not found]   ` <386072610811182327x72df4615od53679b19cf49ed0@mail.gmail.com>
@ 2008-11-19  7:28     ` Bryan Wu
  0 siblings, 0 replies; 8+ messages in thread
From: Bryan Wu @ 2008-11-19  7:28 UTC (permalink / raw)
  To: Andrew Morton; +Cc: torvalds, mingo, linux-kernel, linux-arch

Sorry for forgetting linux-arch. post again.

-Bryan

On Wed, Nov 19, 2008 at 3:27 PM, Bryan Wu <cooloney@kernel.org> wrote:
> On Wed, Nov 19, 2008 at 2:56 PM, Andrew Morton
> <akpm@linux-foundation.org> wrote:
>> On Tue, 18 Nov 2008 17:05:03 +0800 Bryan Wu <cooloney@kernel.org> wrote:
>>
>>>
>>> Hi folks,
>>>
>>> We provide the SMP like functions for our Blackfin dual core processor
>>> BF561 for almost 1 year. And after a long time developing, debugging and
>>> internal review, we'd like to post them to LKML for other maintainer
>>> review.
>>>
>>> Please find our wiki page about this SMP like patches:
>>> http://docs.blackfin.uclinux.org/doku.php?id=linux-kernel:smp-like
>>
>> Would prefer that changelogs be self-contained, please.  Kernel
>> changelogs are for ever, and I doubt if that page will be there in 20
>> years time.
>>
>
> I guess Graf started this wiki recently although the patch exists for
> a long time.
> And Graf gave a presentation about this SMP on BF561 in AKA 2008 Linux kernel
> developer conference. If I found the link of this presentation, I will
> post it again.
>
>> Particularly when that page must be read to learn fundamental things such as
>>
>>  The SMP support in certain Blackfin processors is describe as `SMP
>>  Like' rather than just `SMP' due to the lack of hardware cache
>>  coherency.  A true SMP system would have support for cache coherency
>>  in hardware.
>>
>>  On all `SMP Like' setups, cache coherency is maintained via
>>  software mechanisms
>>
>> Interesting!
>>
>
> Exactly, SMP means hardware cache coherency. But BF561 dual core
> processor was designed almost 8 years ago.
> we have to do some workaround in software side. Fortunately, BF561
> provides a L2 memory shared by both CoreA and CoreB.
> We did some trick in this L2 memory and our Scratchpad memory.
>
> 'SMP Like' is software aided SMP solution on Blackfin dual core BF561 processor.
> Please enjoy -:)
>
> -Bryan
>

^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: [PATCH 1/5] Blackfin arch: SMP supporting patchset: BF561 related code
       [not found]   ` <20081118225625.39e660ff.akpm@linux-foundation.org>
@ 2008-11-19  7:39     ` Bryan Wu
  2008-11-19  8:10       ` gyang
  0 siblings, 1 reply; 8+ messages in thread
From: Bryan Wu @ 2008-11-19  7:39 UTC (permalink / raw)
  To: Andrew Morton
  Cc: torvalds, mingo, linux-kernel, Graf Yang, Mike Frysinger,
	linux-arch

On Wed, Nov 19, 2008 at 2:56 PM, Andrew Morton
<akpm@linux-foundation.org> wrote:
> On Tue, 18 Nov 2008 17:05:04 +0800 Bryan Wu <cooloney@kernel.org> wrote:
>
>> From: Graf Yang <graf.yang@analog.com>
>>
>> Blackfin dual core BF561 processor can support SMP like features.
>> https://docs.blackfin.uclinux.org/doku.php?id=linux-kernel:smp-like
>>
>> In this patch, we provide SMP extend to BF561 kernel code
>>
>>
>> ...
>>
>> --- a/arch/blackfin/mach-bf561/include/mach/mem_map.h
>> +++ b/arch/blackfin/mach-bf561/include/mach/mem_map.h
>> @@ -85,4 +85,124 @@
>>  #define L1_SCRATCH_START     COREA_L1_SCRATCH_START
>>  #define L1_SCRATCH_LENGTH    0x1000
>>
>> +#ifndef __ASSEMBLY__
>> +
>> +#ifdef CONFIG_SMP
>> +
>> +#define get_l1_scratch_start_cpu(cpu)                                \
>> +     ({ unsigned long __addr;                                \
>> +        __addr = (cpu) ? COREB_L1_SCRATCH_START : COREA_L1_SCRATCH_START;\
>> +        __addr; })
>> +
>> +#define get_l1_code_start_cpu(cpu)                           \
>> +     ({ unsigned long __addr;                                \
>> +        __addr = (cpu) ? COREB_L1_CODE_START : COREA_L1_CODE_START;  \
>> +        __addr; })
>> +
>> +#define get_l1_data_a_start_cpu(cpu)                         \
>> +     ({ unsigned long __addr;                                \
>> +        __addr = (cpu) ? COREB_L1_DATA_A_START : COREA_L1_DATA_A_START;\
>> +        __addr; })
>> +
>> +#define get_l1_data_b_start_cpu(cpu)                         \
>> +     ({ unsigned long __addr;                                \
>> +        __addr = (cpu) ? COREB_L1_DATA_B_START : COREA_L1_DATA_B_START;\
>> +        __addr; })
>> +
>> +#define get_l1_scratch_start()       get_l1_scratch_start_cpu(blackfin_core_id())
>> +#define get_l1_code_start()  get_l1_code_start_cpu(blackfin_core_id())
>> +#define get_l1_data_a_start()        get_l1_data_a_start_cpu(blackfin_core_id())
>> +#define get_l1_data_b_start()        get_l1_data_b_start_cpu(blackfin_core_id())
>> +
>> +#else /* !CONFIG_SMP */
>> +#define get_l1_scratch_start_cpu(cpu)        L1_SCRATCH_START
>> +#define get_l1_code_start_cpu(cpu)   L1_CODE_START
>> +#define get_l1_data_a_start_cpu(cpu) L1_DATA_A_START
>> +#define get_l1_data_b_start_cpu(cpu) L1_DATA_B_START
>> +#define get_l1_scratch_start()               L1_SCRATCH_START
>> +#define get_l1_code_start()          L1_CODE_START
>> +#define get_l1_data_a_start()                L1_DATA_A_START
>> +#define get_l1_data_b_start()                L1_DATA_B_START
>> +#endif /* !CONFIG_SMP */
>
> grumble.  These didn't need to be implemented as macros and hence
> shouldn't have been.
>
> Example:
>
>        int cpu = smp_processor_id();
>        get_l1_scratch_start_cpu(cpu);
>
> that code should generate unused variable warnings on CONFIG_SMP=n.  If
> it doesn't, you got lucky, because it _should_.
>
> Also
>
>        int cpu = smp_processor_id();
>        get_l1_scratch_start_cpu(pcu);
>
> will happily compile and run with CONFIG_SMP=n.
>
>
> macros=bad,bad,bad.
>

Yes, I also prefer inline functions rather than macros here.
Right, Graf?

>>
>> ...
>>
>> --- /dev/null
>> +++ b/arch/blackfin/mach-bf561/smp.c
>> @@ -0,0 +1,182 @@
>> +/*
>> + * File:         arch/blackfin/mach-bf561/smp.c
>> + * Author:       Philippe Gerum <rpm@xenomai.org>
>> + *
>> + *               Copyright 2007 Analog Devices Inc.
>> + *
>> + * This program is free software; you can redistribute it and/or modify
>> + * it under the terms of the GNU General Public License as published by
>> + * the Free Software Foundation; either version 2 of the License, or
>> + * (at your option) any later version.
>> + *
>> + * This program is distributed in the hope that it will be useful,
>> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
>> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
>> + * GNU General Public License for more details.
>> + *
>> + * You should have received a copy of the GNU General Public License
>> + * along with this program; if not, see the file COPYING, or write
>> + * to the Free Software Foundation, Inc.,
>> + * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
>> + */
>> +
>> +#include <linux/init.h>
>> +#include <linux/kernel.h>
>> +#include <linux/sched.h>
>> +#include <linux/delay.h>
>> +#include <asm/smp.h>
>> +#include <asm/dma.h>
>> +
>> +#define COREB_SRAM_BASE  0xff600000
>> +#define COREB_SRAM_SIZE  0x4000
>> +
>> +extern char coreb_trampoline_start, coreb_trampoline_end;
>
> OK, these are defined in .S and we do often put declarations for such
> things in .c rather than in .h.  But I think it's better to put them in
> .h anyway, to avoid possibly duplicated declarations in the future.
>

Oh, I suggested Graf to run checkpatch.pl to find some issues before I
sent out this patch.
Should this issues be catched by checkpatch.pl?


>> +static DEFINE_SPINLOCK(boot_lock);
>> +
>> +static cpumask_t cpu_callin_map;
>> +
>>
>> ...
>>
>> +void __cpuinit platform_secondary_init(unsigned int cpu)
>> +{
>> +     local_irq_disable();
>> +
>> +     /* Clone setup for peripheral interrupt sources from CoreA. */
>> +     bfin_write_SICB_IMASK0(bfin_read_SICA_IMASK0());
>> +     bfin_write_SICB_IMASK1(bfin_read_SICA_IMASK1());
>> +     SSYNC();
>> +
>> +     /* Clone setup for IARs from CoreA. */
>> +     bfin_write_SICB_IAR0(bfin_read_SICA_IAR0());
>> +     bfin_write_SICB_IAR1(bfin_read_SICA_IAR1());
>> +     bfin_write_SICB_IAR2(bfin_read_SICA_IAR2());
>> +     bfin_write_SICB_IAR3(bfin_read_SICA_IAR3());
>> +     bfin_write_SICB_IAR4(bfin_read_SICA_IAR4());
>> +     bfin_write_SICB_IAR5(bfin_read_SICA_IAR5());
>> +     bfin_write_SICB_IAR6(bfin_read_SICA_IAR6());
>> +     bfin_write_SICB_IAR7(bfin_read_SICA_IAR7());
>> +     SSYNC();
>> +
>> +     local_irq_enable();
>> +
>> +     /* Calibrate loops per jiffy value. */
>> +     calibrate_delay();
>> +
>> +     /* Store CPU-private information to the cpu_data array. */
>> +     bfin_setup_cpudata(cpu);
>> +
>> +     /* We are done with local CPU inits, unblock the boot CPU. */
>> +     cpu_set(cpu, cpu_callin_map);
>> +     spin_lock(&boot_lock);
>> +     spin_unlock(&boot_lock);
>
> Is this spin_lock()+spin_unlock() supposed to block until the secondary
> CPU is running?  If so, I don't think it works.
>

We can remove these 2 line spin_lock+spin_unlock and it also works.
But maybe we will add some operation between spin_lock and spin_unlock
here in the future,
we'd like to keep them.

P.S. also forward this patch to linux-arch

Thanks
-Bryan

>> +}
>> +
>>
>> ...
>>
>
>

^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: [PATCH 2/5] Blackfin arch: SMP supporting patchset: Blackfin header files and machine common code
       [not found] ` <1226999108-13839-3-git-send-email-cooloney@kernel.org>
@ 2008-11-19  7:44   ` Bryan Wu
       [not found]   ` <20081118225629.eddd23ae.akpm@linux-foundation.org>
  1 sibling, 0 replies; 8+ messages in thread
From: Bryan Wu @ 2008-11-19  7:44 UTC (permalink / raw)
  To: torvalds, akpm, mingo, linux-arch; +Cc: linux-kernel, Graf Yang, Bryan Wu

Post this patch to linux-arch, maybe more people are interested in this.

-Bryan

On Tue, Nov 18, 2008 at 5:05 PM, Bryan Wu <cooloney@kernel.org> wrote:
> From: Graf Yang <graf.yang@analog.com>
>
> Blackfin dual core BF561 processor can support SMP like features.
> https://docs.blackfin.uclinux.org/doku.php?id=linux-kernel:smp-like
>
> In this patch, we provide SMP extend to Blackfin header files
> and machine common code
>
> Signed-off-by: Graf Yang <graf.yang@analog.com>
> Signed-off-by: Bryan Wu <cooloney@kernel.org>
> ---
>  arch/blackfin/include/asm/atomic.h         |  124 ++++++--
>  arch/blackfin/include/asm/bfin-global.h    |    5 +-
>  arch/blackfin/include/asm/bitops.h         |  185 ++++++++----
>  arch/blackfin/include/asm/cache.h          |   29 ++
>  arch/blackfin/include/asm/cacheflush.h     |   20 +-
>  arch/blackfin/include/asm/context.S        |    6 +-
>  arch/blackfin/include/asm/cpu.h            |   42 +++
>  arch/blackfin/include/asm/l1layout.h       |    3 +-
>  arch/blackfin/include/asm/mutex-dec.h      |  112 +++++++
>  arch/blackfin/include/asm/mutex.h          |   63 ++++
>  arch/blackfin/include/asm/pda.h            |   70 ++++
>  arch/blackfin/include/asm/percpu.h         |   12 +-
>  arch/blackfin/include/asm/processor.h      |    7 +-
>  arch/blackfin/include/asm/rwlock.h         |    6 +
>  arch/blackfin/include/asm/smp.h            |   42 +++
>  arch/blackfin/include/asm/spinlock.h       |   87 +++++-
>  arch/blackfin/include/asm/spinlock_types.h |   22 ++
>  arch/blackfin/include/asm/system.h         |  116 ++++++--
>  arch/blackfin/mach-common/Makefile         |    1 +
>  arch/blackfin/mach-common/cache.S          |   36 ++
>  arch/blackfin/mach-common/entry.S          |   92 +++---
>  arch/blackfin/mach-common/head.S           |   29 +-
>  arch/blackfin/mach-common/ints-priority.c  |   41 +++-
>  arch/blackfin/mach-common/smp.c            |  476 ++++++++++++++++++++++++++++
>  arch/blackfin/oprofile/common.c            |    2 +-
>  25 files changed, 1437 insertions(+), 191 deletions(-)
>  create mode 100644 arch/blackfin/include/asm/cpu.h
>  create mode 100644 arch/blackfin/include/asm/mutex-dec.h
>  create mode 100644 arch/blackfin/include/asm/pda.h
>  create mode 100644 arch/blackfin/include/asm/rwlock.h
>  create mode 100644 arch/blackfin/include/asm/smp.h
>  create mode 100644 arch/blackfin/include/asm/spinlock_types.h
>  create mode 100644 arch/blackfin/mach-common/smp.c
>
> diff --git a/arch/blackfin/include/asm/atomic.h b/arch/blackfin/include/asm/atomic.h
> index 7cf5087..8af0542 100644
> --- a/arch/blackfin/include/asm/atomic.h
> +++ b/arch/blackfin/include/asm/atomic.h
> @@ -13,15 +13,83 @@
>  * Tony Kou (tonyko@lineo.ca)   Lineo Inc.   2001
>  */
>
> -typedef struct {
> -       int counter;
> -} atomic_t;
> -#define ATOMIC_INIT(i) { (i) }
> +typedef struct { volatile int counter; } atomic_t;
>
> -#define atomic_read(v)         ((v)->counter)
> +#define ATOMIC_INIT(i) { (i) }
>  #define atomic_set(v, i)       (((v)->counter) = i)
>
> -static __inline__ void atomic_add(int i, atomic_t * v)
> +#ifdef CONFIG_SMP
> +
> +#define atomic_read(v) __raw_uncached_fetch_asm(&(v)->counter)
> +
> +asmlinkage int __raw_uncached_fetch_asm(const volatile int *ptr);
> +
> +asmlinkage int __raw_atomic_update_asm(volatile int *ptr, int value);
> +
> +asmlinkage int __raw_atomic_clear_asm(volatile int *ptr, int value);
> +
> +asmlinkage int __raw_atomic_set_asm(volatile int *ptr, int value);
> +
> +asmlinkage int __raw_atomic_xor_asm(volatile int *ptr, int value);
> +
> +asmlinkage int __raw_atomic_test_asm(const volatile int *ptr, int value);
> +
> +static inline void atomic_add(int i, atomic_t *v)
> +{
> +       __raw_atomic_update_asm(&v->counter, i);
> +}
> +
> +static inline void atomic_sub(int i, atomic_t *v)
> +{
> +       __raw_atomic_update_asm(&v->counter, -i);
> +}
> +
> +static inline int atomic_add_return(int i, atomic_t *v)
> +{
> +       return __raw_atomic_update_asm(&v->counter, i);
> +}
> +
> +static inline int atomic_sub_return(int i, atomic_t *v)
> +{
> +       return __raw_atomic_update_asm(&v->counter, -i);
> +}
> +
> +static inline void atomic_inc(volatile atomic_t *v)
> +{
> +       __raw_atomic_update_asm(&v->counter, 1);
> +}
> +
> +static inline void atomic_dec(volatile atomic_t *v)
> +{
> +       __raw_atomic_update_asm(&v->counter, -1);
> +}
> +
> +static inline void atomic_clear_mask(int mask, atomic_t *v)
> +{
> +       __raw_atomic_clear_asm(&v->counter, mask);
> +}
> +
> +static inline void atomic_set_mask(int mask, atomic_t *v)
> +{
> +       __raw_atomic_set_asm(&v->counter, mask);
> +}
> +
> +static inline int atomic_test_mask(int mask, atomic_t *v)
> +{
> +       return __raw_atomic_test_asm(&v->counter, mask);
> +}
> +
> +/* Atomic operations are already serializing */
> +#define smp_mb__before_atomic_dec()    barrier()
> +#define smp_mb__after_atomic_dec() barrier()
> +#define smp_mb__before_atomic_inc()    barrier()
> +#define smp_mb__after_atomic_inc() barrier()
> +
> +#else /* !CONFIG_SMP */
> +
> +#define atomic_read(v) ((v)->counter)
> +
> +static inline void atomic_add(int i, atomic_t *v)
>  {
>        long flags;
>
> @@ -30,7 +98,7 @@ static __inline__ void atomic_add(int i, atomic_t * v)
>        local_irq_restore(flags);
>  }
>
> -static __inline__ void atomic_sub(int i, atomic_t * v)
> +static inline void atomic_sub(int i, atomic_t *v)
>  {
>        long flags;
>
> @@ -40,7 +108,7 @@ static __inline__ void atomic_sub(int i, atomic_t * v)
>
>  }
>
> -static inline int atomic_add_return(int i, atomic_t * v)
> +static inline int atomic_add_return(int i, atomic_t *v)
>  {
>        int __temp = 0;
>        long flags;
> @@ -54,8 +122,7 @@ static inline int atomic_add_return(int i, atomic_t * v)
>        return __temp;
>  }
>
> -#define atomic_add_negative(a, v)      (atomic_add_return((a), (v)) < 0)
> -static inline int atomic_sub_return(int i, atomic_t * v)
> +static inline int atomic_sub_return(int i, atomic_t *v)
>  {
>        int __temp = 0;
>        long flags;
> @@ -68,7 +135,7 @@ static inline int atomic_sub_return(int i, atomic_t * v)
>        return __temp;
>  }
>
> -static __inline__ void atomic_inc(volatile atomic_t * v)
> +static inline void atomic_inc(volatile atomic_t *v)
>  {
>        long flags;
>
> @@ -77,20 +144,7 @@ static __inline__ void atomic_inc(volatile atomic_t * v)
>        local_irq_restore(flags);
>  }
>
> -#define atomic_cmpxchg(v, o, n) ((int)cmpxchg(&((v)->counter), (o), (n)))
> -#define atomic_xchg(v, new) (xchg(&((v)->counter), new))
> -
> -#define atomic_add_unless(v, a, u)                             \
> -({                                                             \
> -       int c, old;                                             \
> -       c = atomic_read(v);                                     \
> -       while (c != (u) && (old = atomic_cmpxchg((v), c, c + (a))) != c) \
> -               c = old;                                        \
> -       c != (u);                                               \
> -})
> -#define atomic_inc_not_zero(v) atomic_add_unless((v), 1, 0)
> -
> -static __inline__ void atomic_dec(volatile atomic_t * v)
> +static inline void atomic_dec(volatile atomic_t *v)
>  {
>        long flags;
>
> @@ -99,7 +153,7 @@ static __inline__ void atomic_dec(volatile atomic_t * v)
>        local_irq_restore(flags);
>  }
>
> -static __inline__ void atomic_clear_mask(unsigned int mask, atomic_t * v)
> +static inline void atomic_clear_mask(unsigned int mask, atomic_t *v)
>  {
>        long flags;
>
> @@ -108,7 +162,7 @@ static __inline__ void atomic_clear_mask(unsigned int mask, atomic_t * v)
>        local_irq_restore(flags);
>  }
>
> -static __inline__ void atomic_set_mask(unsigned int mask, atomic_t * v)
> +static inline void atomic_set_mask(unsigned int mask, atomic_t *v)
>  {
>        long flags;
>
> @@ -123,9 +177,25 @@ static __inline__ void atomic_set_mask(unsigned int mask, atomic_t * v)
>  #define smp_mb__before_atomic_inc()    barrier()
>  #define smp_mb__after_atomic_inc() barrier()
>
> +#endif /* !CONFIG_SMP */
> +
> +#define atomic_add_negative(a, v)      (atomic_add_return((a), (v)) < 0)
>  #define atomic_dec_return(v) atomic_sub_return(1,(v))
>  #define atomic_inc_return(v) atomic_add_return(1,(v))
>
> +#define atomic_cmpxchg(v, o, n) ((int)cmpxchg(&((v)->counter), (o), (n)))
> +#define atomic_xchg(v, new) (xchg(&((v)->counter), new))
> +
> +#define atomic_add_unless(v, a, u)                             \
> +({                                                             \
> +       int c, old;                                             \
> +       c = atomic_read(v);                                     \
> +       while (c != (u) && (old = atomic_cmpxchg((v), c, c + (a))) != c) \
> +               c = old;                                        \
> +       c != (u);                                               \
> +})
> +#define atomic_inc_not_zero(v) atomic_add_unless((v), 1, 0)
> +
>  /*
>  * atomic_inc_and_test - increment and test
>  * @v: pointer of type atomic_t
> diff --git a/arch/blackfin/include/asm/bfin-global.h b/arch/blackfin/include/asm/bfin-global.h
> index 7729566..1dd0805 100644
> --- a/arch/blackfin/include/asm/bfin-global.h
> +++ b/arch/blackfin/include/asm/bfin-global.h
> @@ -47,6 +47,9 @@
>  # define DMA_UNCACHED_REGION (0)
>  #endif
>
> +extern void bfin_setup_caches(unsigned int cpu);
> +extern void bfin_setup_cpudata(unsigned int cpu);
> +
>  extern unsigned long get_cclk(void);
>  extern unsigned long get_sclk(void);
>  extern unsigned long sclk_to_usecs(unsigned long sclk);
> @@ -58,8 +61,6 @@ extern void dump_bfin_trace_buffer(void);
>
>  /* init functions only */
>  extern int init_arch_irq(void);
> -extern void bfin_icache_init(void);
> -extern void bfin_dcache_init(void);
>  extern void init_exception_vectors(void);
>  extern void program_IAR(void);
>
> diff --git a/arch/blackfin/include/asm/bitops.h b/arch/blackfin/include/asm/bitops.h
> index b39a175..5872fb6 100644
> --- a/arch/blackfin/include/asm/bitops.h
> +++ b/arch/blackfin/include/asm/bitops.h
> @@ -7,7 +7,6 @@
>
>  #include <linux/compiler.h>
>  #include <asm/byteorder.h>     /* swab32 */
> -#include <asm/system.h>                /* save_flags */
>
>  #ifdef __KERNEL__
>
> @@ -20,36 +19,71 @@
>  #include <asm-generic/bitops/sched.h>
>  #include <asm-generic/bitops/ffz.h>
>
> -static __inline__ void set_bit(int nr, volatile unsigned long *addr)
> +#ifdef CONFIG_SMP
> +
> +#include <linux/linkage.h>
> +
> +asmlinkage int __raw_bit_set_asm(volatile unsigned long *addr, int nr);
> +
> +asmlinkage int __raw_bit_clear_asm(volatile unsigned long *addr, int nr);
> +
> +asmlinkage int __raw_bit_toggle_asm(volatile unsigned long *addr, int nr);
> +
> +asmlinkage int __raw_bit_test_set_asm(volatile unsigned long *addr, int nr);
> +
> +asmlinkage int __raw_bit_test_clear_asm(volatile unsigned long *addr, int nr);
> +
> +asmlinkage int __raw_bit_test_toggle_asm(volatile unsigned long *addr, int nr);
> +
> +asmlinkage int __raw_bit_test_asm(const volatile unsigned long *addr, int nr);
> +
> +static inline void set_bit(int nr, volatile unsigned long *addr)
>  {
> -       int *a = (int *)addr;
> -       int mask;
> -       unsigned long flags;
> +       volatile unsigned long *a = addr + (nr >> 5);
> +       __raw_bit_set_asm(a, nr & 0x1f);
> +}
>
> -       a += nr >> 5;
> -       mask = 1 << (nr & 0x1f);
> -       local_irq_save(flags);
> -       *a |= mask;
> -       local_irq_restore(flags);
> +static inline void clear_bit(int nr, volatile unsigned long *addr)
> +{
> +       volatile unsigned long *a = addr + (nr >> 5);
> +       __raw_bit_clear_asm(a, nr & 0x1f);
>  }
>
> -static __inline__ void __set_bit(int nr, volatile unsigned long *addr)
> +static inline void change_bit(int nr, volatile unsigned long *addr)
>  {
> -       int *a = (int *)addr;
> -       int mask;
> +       volatile unsigned long *a = addr + (nr >> 5);
> +       __raw_bit_toggle_asm(a, nr & 0x1f);
> +}
>
> -       a += nr >> 5;
> -       mask = 1 << (nr & 0x1f);
> -       *a |= mask;
> +static inline int test_bit(int nr, const volatile unsigned long *addr)
> +{
> +       volatile const unsigned long *a = addr + (nr >> 5);
> +       return __raw_bit_test_asm(a, nr & 0x1f) != 0;
>  }
>
> -/*
> - * clear_bit() doesn't provide any barrier for the compiler.
> - */
> -#define smp_mb__before_clear_bit()     barrier()
> -#define smp_mb__after_clear_bit()      barrier()
> +static inline int test_and_set_bit(int nr, volatile unsigned long *addr)
> +{
> +       volatile unsigned long *a = addr + (nr >> 5);
> +       return __raw_bit_test_set_asm(a, nr & 0x1f);
> +}
>
> -static __inline__ void clear_bit(int nr, volatile unsigned long *addr)
> +static inline int test_and_clear_bit(int nr, volatile unsigned long *addr)
> +{
> +       volatile unsigned long *a = addr + (nr >> 5);
> +       return __raw_bit_test_clear_asm(a, nr & 0x1f);
> +}
> +
> +static inline int test_and_change_bit(int nr, volatile unsigned long *addr)
> +{
> +       volatile unsigned long *a = addr + (nr >> 5);
> +       return __raw_bit_test_toggle_asm(a, nr & 0x1f);
> +}
> +
> +#else /* !CONFIG_SMP */
> +
> +#include <asm/system.h>                /* save_flags */
> +
> +static inline void set_bit(int nr, volatile unsigned long *addr)
>  {
>        int *a = (int *)addr;
>        int mask;
> @@ -57,21 +91,23 @@ static __inline__ void clear_bit(int nr, volatile unsigned long *addr)
>        a += nr >> 5;
>        mask = 1 << (nr & 0x1f);
>        local_irq_save(flags);
> -       *a &= ~mask;
> +       *a |= mask;
>        local_irq_restore(flags);
>  }
>
> -static __inline__ void __clear_bit(int nr, volatile unsigned long *addr)
> +static inline void clear_bit(int nr, volatile unsigned long *addr)
>  {
>        int *a = (int *)addr;
>        int mask;
> -
> +       unsigned long flags;
>        a += nr >> 5;
>        mask = 1 << (nr & 0x1f);
> +       local_irq_save(flags);
>        *a &= ~mask;
> +       local_irq_restore(flags);
>  }
>
> -static __inline__ void change_bit(int nr, volatile unsigned long *addr)
> +static inline void change_bit(int nr, volatile unsigned long *addr)
>  {
>        int mask, flags;
>        unsigned long *ADDR = (unsigned long *)addr;
> @@ -83,17 +119,7 @@ static __inline__ void change_bit(int nr, volatile unsigned long *addr)
>        local_irq_restore(flags);
>  }
>
> -static __inline__ void __change_bit(int nr, volatile unsigned long *addr)
> -{
> -       int mask;
> -       unsigned long *ADDR = (unsigned long *)addr;
> -
> -       ADDR += nr >> 5;
> -       mask = 1 << (nr & 31);
> -       *ADDR ^= mask;
> -}
> -
> -static __inline__ int test_and_set_bit(int nr, void *addr)
> +static inline int test_and_set_bit(int nr, volatile unsigned long *addr)
>  {
>        int mask, retval;
>        volatile unsigned int *a = (volatile unsigned int *)addr;
> @@ -109,19 +135,23 @@ static __inline__ int test_and_set_bit(int nr, void *addr)
>        return retval;
>  }
>
> -static __inline__ int __test_and_set_bit(int nr, volatile unsigned long *addr)
> +static inline int test_and_clear_bit(int nr, volatile unsigned long *addr)
>  {
>        int mask, retval;
>        volatile unsigned int *a = (volatile unsigned int *)addr;
> +       unsigned long flags;
>
>        a += nr >> 5;
>        mask = 1 << (nr & 0x1f);
> +       local_irq_save(flags);
>        retval = (mask & *a) != 0;
> -       *a |= mask;
> +       *a &= ~mask;
> +       local_irq_restore(flags);
> +
>        return retval;
>  }
>
> -static __inline__ int test_and_clear_bit(int nr, volatile unsigned long *addr)
> +static inline int test_and_change_bit(int nr, volatile unsigned long *addr)
>  {
>        int mask, retval;
>        volatile unsigned int *a = (volatile unsigned int *)addr;
> @@ -131,13 +161,59 @@ static __inline__ int test_and_clear_bit(int nr, volatile unsigned long *addr)
>        mask = 1 << (nr & 0x1f);
>        local_irq_save(flags);
>        retval = (mask & *a) != 0;
> -       *a &= ~mask;
> +       *a ^= mask;
>        local_irq_restore(flags);
> -
>        return retval;
>  }
>
> -static __inline__ int __test_and_clear_bit(int nr, volatile unsigned long *addr)
> +/*
> + * This routine doesn't need to go through raw atomic ops in UP
> + * context.
> + */
> +#define test_bit(nr,addr) \
> +(__builtin_constant_p(nr) ? \
> + __constant_test_bit((nr), (addr)) : \
> + __test_bit((nr), (addr)))
> +
> +#endif /* CONFIG_SMP */
> +
> +/*
> + * clear_bit() doesn't provide any barrier for the compiler.
> + */
> +#define smp_mb__before_clear_bit()     barrier()
> +#define smp_mb__after_clear_bit()      barrier()
> +
> +static inline void __set_bit(int nr, volatile unsigned long *addr)
> +{
> +       int *a = (int *)addr;
> +       int mask;
> +
> +       a += nr >> 5;
> +       mask = 1 << (nr & 0x1f);
> +       *a |= mask;
> +}
> +
> +static inline void __clear_bit(int nr, volatile unsigned long *addr)
> +{
> +       int *a = (int *)addr;
> +       int mask;
> +
> +       a += nr >> 5;
> +       mask = 1 << (nr & 0x1f);
> +       *a &= ~mask;
> +}
> +
> +static inline void __change_bit(int nr, volatile unsigned long *addr)
> +{
> +       int mask;
> +       unsigned long *ADDR = (unsigned long *)addr;
> +
> +       ADDR += nr >> 5;
> +       mask = 1 << (nr & 31);
> +       *ADDR ^= mask;
> +}
> +
> +static inline int __test_and_set_bit(int nr, volatile unsigned long *addr)
>  {
>        int mask, retval;
>        volatile unsigned int *a = (volatile unsigned int *)addr;
> @@ -145,26 +221,23 @@ static __inline__ int __test_and_clear_bit(int nr, volatile unsigned long *addr)
>        a += nr >> 5;
>        mask = 1 << (nr & 0x1f);
>        retval = (mask & *a) != 0;
> -       *a &= ~mask;
> +       *a |= mask;
>        return retval;
>  }
>
> -static __inline__ int test_and_change_bit(int nr, volatile unsigned long *addr)
> +static inline int __test_and_clear_bit(int nr, volatile unsigned long *addr)
>  {
>        int mask, retval;
>        volatile unsigned int *a = (volatile unsigned int *)addr;
> -       unsigned long flags;
>
>        a += nr >> 5;
>        mask = 1 << (nr & 0x1f);
> -       local_irq_save(flags);
>        retval = (mask & *a) != 0;
> -       *a ^= mask;
> -       local_irq_restore(flags);
> +       *a &= ~mask;
>        return retval;
>  }
>
> -static __inline__ int __test_and_change_bit(int nr,
> +static inline int __test_and_change_bit(int nr,
>                                            volatile unsigned long *addr)
>  {
>        int mask, retval;
> @@ -177,16 +250,13 @@ static __inline__ int __test_and_change_bit(int nr,
>        return retval;
>  }
>
> -/*
> - * This routine doesn't need to be atomic.
> - */
> -static __inline__ int __constant_test_bit(int nr, const void *addr)
> +static inline int __constant_test_bit(int nr, const void *addr)
>  {
>        return ((1UL << (nr & 31)) &
>                (((const volatile unsigned int *)addr)[nr >> 5])) != 0;
>  }
>
> -static __inline__ int __test_bit(int nr, const void *addr)
> +static inline int __test_bit(int nr, const void *addr)
>  {
>        int *a = (int *)addr;
>        int mask;
> @@ -196,11 +266,6 @@ static __inline__ int __test_bit(int nr, const void *addr)
>        return ((mask & *a) != 0);
>  }
>
> -#define test_bit(nr,addr) \
> -(__builtin_constant_p(nr) ? \
> - __constant_test_bit((nr),(addr)) : \
> - __test_bit((nr),(addr)))
> -
>  #include <asm-generic/bitops/find.h>
>  #include <asm-generic/bitops/hweight.h>
>  #include <asm-generic/bitops/lock.h>
> diff --git a/arch/blackfin/include/asm/cache.h b/arch/blackfin/include/asm/cache.h
> index 023d721..8663781 100644
> --- a/arch/blackfin/include/asm/cache.h
> +++ b/arch/blackfin/include/asm/cache.h
> @@ -12,6 +12,11 @@
>  #define L1_CACHE_BYTES (1 << L1_CACHE_SHIFT)
>  #define SMP_CACHE_BYTES        L1_CACHE_BYTES
>
> +#ifdef CONFIG_SMP
> +#define __cacheline_aligned
> +#else
> +#define ____cacheline_aligned
> +
>  /*
>  * Put cacheline_aliged data to L1 data memory
>  */
> @@ -21,9 +26,33 @@
>                __section__(".data_l1.cacheline_aligned")))
>  #endif
>
> +#endif
> +
>  /*
>  * largest L1 which this arch supports
>  */
>  #define L1_CACHE_SHIFT_MAX     5
>
> +#if defined(CONFIG_SMP) && \
> +    !defined(CONFIG_BFIN_CACHE_COHERENT) && \
> +    defined(CONFIG_BFIN_DCACHE)
> +#define __ARCH_SYNC_CORE_DCACHE
> +#ifndef __ASSEMBLY__
> +asmlinkage void __raw_smp_mark_barrier_asm(void);
> +asmlinkage void __raw_smp_check_barrier_asm(void);
> +
> +static inline void smp_mark_barrier(void)
> +{
> +       __raw_smp_mark_barrier_asm();
> +}
> +static inline void smp_check_barrier(void)
> +{
> +       __raw_smp_check_barrier_asm();
> +}
> +
> +void resync_core_dcache(void);
> +#endif
> +#endif
> +
> +
>  #endif
> diff --git a/arch/blackfin/include/asm/cacheflush.h b/arch/blackfin/include/asm/cacheflush.h
> index 4403415..1b040f5 100644
> --- a/arch/blackfin/include/asm/cacheflush.h
> +++ b/arch/blackfin/include/asm/cacheflush.h
> @@ -35,6 +35,7 @@ extern void blackfin_icache_flush_range(unsigned long start_address, unsigned lo
>  extern void blackfin_dcache_flush_range(unsigned long start_address, unsigned long end_address);
>  extern void blackfin_dcache_invalidate_range(unsigned long start_address, unsigned long end_address);
>  extern void blackfin_dflush_page(void *page);
> +extern void blackfin_invalidate_entire_dcache(void);
>
>  #define flush_dcache_mmap_lock(mapping)                do { } while (0)
>  #define flush_dcache_mmap_unlock(mapping)      do { } while (0)
> @@ -44,12 +45,20 @@ extern void blackfin_dflush_page(void *page);
>  #define flush_cache_vmap(start, end)           do { } while (0)
>  #define flush_cache_vunmap(start, end)         do { } while (0)
>
> +#ifdef CONFIG_SMP
> +#define flush_icache_range_others(start, end)  \
> +       smp_icache_flush_range_others((start), (end))
> +#else
> +#define flush_icache_range_others(start, end)  do { } while (0)
> +#endif
> +
>  static inline void flush_icache_range(unsigned start, unsigned end)
>  {
>  #if defined(CONFIG_BFIN_DCACHE) && defined(CONFIG_BFIN_ICACHE)
>
>  # if defined(CONFIG_BFIN_WT)
>        blackfin_icache_flush_range((start), (end));
> +       flush_icache_range_others(start, end);
>  # else
>        blackfin_icache_dcache_flush_range((start), (end));
>  # endif
> @@ -58,6 +67,7 @@ static inline void flush_icache_range(unsigned start, unsigned end)
>
>  # if defined(CONFIG_BFIN_ICACHE)
>        blackfin_icache_flush_range((start), (end));
> +       flush_icache_range_others(start, end);
>  # endif
>  # if defined(CONFIG_BFIN_DCACHE)
>        blackfin_dcache_flush_range((start), (end));
> @@ -66,10 +76,12 @@ static inline void flush_icache_range(unsigned start, unsigned end)
>  #endif
>  }
>
> -#define copy_to_user_page(vma, page, vaddr, dst, src, len) \
> -do { memcpy(dst, src, len); \
> -     flush_icache_range ((unsigned) (dst), (unsigned) (dst) + (len)); \
> +#define copy_to_user_page(vma, page, vaddr, dst, src, len)             \
> +do { memcpy(dst, src, len);                                            \
> +     flush_icache_range((unsigned) (dst), (unsigned) (dst) + (len));   \
> +     flush_icache_range_others((unsigned long) (dst), (unsigned long) (dst) + (len));\
>  } while (0)
> +
>  #define copy_from_user_page(vma, page, vaddr, dst, src, len)   memcpy(dst, src, len)
>
>  #if defined(CONFIG_BFIN_DCACHE)
> @@ -82,7 +94,7 @@ do { memcpy(dst, src, len); \
>  # define flush_dcache_page(page)                       blackfin_dflush_page(page_address(page))
>  #else
>  # define flush_dcache_range(start,end)         do { } while (0)
> -# define flush_dcache_page(page)                       do { } while (0)
> +# define flush_dcache_page(page)               do { } while (0)
>  #endif
>
>  extern unsigned long reserved_mem_dcache_on;
> diff --git a/arch/blackfin/include/asm/context.S b/arch/blackfin/include/asm/context.S
> index c0e630e..40d20b4 100644
> --- a/arch/blackfin/include/asm/context.S
> +++ b/arch/blackfin/include/asm/context.S
> @@ -303,9 +303,14 @@
>        RETI = [sp++];
>        RETS = [sp++];
>
> +#ifdef CONFIG_SMP
> +       GET_PDA(p0, r0);
> +       r0 = [p0 + PDA_IRQFLAGS];
> +#else
>        p0.h = _irq_flags;
>        p0.l = _irq_flags;
>        r0 = [p0];
> +#endif
>        sti r0;
>
>        sp += 4;        /* Skip Reserved */
> @@ -352,4 +357,3 @@
>        SYSCFG = [sp++];
>        csync;
>  .endm
> -
> diff --git a/arch/blackfin/include/asm/cpu.h b/arch/blackfin/include/asm/cpu.h
> new file mode 100644
> index 0000000..9b7aefe
> --- /dev/null
> +++ b/arch/blackfin/include/asm/cpu.h
> @@ -0,0 +1,42 @@
> +/*
> + * File:         arch/blackfin/include/asm/cpu.h.
> + * Author:       Philippe Gerum <rpm@xenomai.org>
> + *
> + *               Copyright 2007 Analog Devices Inc.
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License as published by
> + * the Free Software Foundation; either version 2 of the License, or
> + * (at your option) any later version.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + * GNU General Public License for more details.
> + *
> + * You should have received a copy of the GNU General Public License
> + * along with this program; if not, see the file COPYING, or write
> + * to the Free Software Foundation, Inc.,
> + * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
> + */
> +
> +#ifndef __ASM_BLACKFIN_CPU_H
> +#define __ASM_BLACKFIN_CPU_H
> +
> +#include <linux/percpu.h>
> +
> +struct task_struct;
> +
> +struct blackfin_cpudata {
> +       struct cpu cpu;
> +       struct task_struct *idle;
> +       unsigned long cclk;
> +       unsigned int imemctl;
> +       unsigned int dmemctl;
> +       unsigned long loops_per_jiffy;
> +       unsigned long dcache_invld_count;
> +};
> +
> +DECLARE_PER_CPU(struct blackfin_cpudata, cpu_data);
> +
> +#endif
> diff --git a/arch/blackfin/include/asm/l1layout.h b/arch/blackfin/include/asm/l1layout.h
> index c13ded7..06bb37f 100644
> --- a/arch/blackfin/include/asm/l1layout.h
> +++ b/arch/blackfin/include/asm/l1layout.h
> @@ -24,7 +24,8 @@ struct l1_scratch_task_info
>  };
>
>  /* A pointer to the structure in memory.  */
> -#define L1_SCRATCH_TASK_INFO ((struct l1_scratch_task_info *)L1_SCRATCH_START)
> +#define L1_SCRATCH_TASK_INFO ((struct l1_scratch_task_info *)\
> +                                               get_l1_scratch_start())
>
>  #endif
>
> diff --git a/arch/blackfin/include/asm/mutex-dec.h b/arch/blackfin/include/asm/mutex-dec.h
> new file mode 100644
> index 0000000..0134151
> --- /dev/null
> +++ b/arch/blackfin/include/asm/mutex-dec.h
> @@ -0,0 +1,112 @@
> +/*
> + * include/asm-generic/mutex-dec.h
> + *
> + * Generic implementation of the mutex fastpath, based on atomic
> + * decrement/increment.
> + */
> +#ifndef _ASM_GENERIC_MUTEX_DEC_H
> +#define _ASM_GENERIC_MUTEX_DEC_H
> +
> +/**
> + *  __mutex_fastpath_lock - try to take the lock by moving the count
> + *                          from 1 to a 0 value
> + *  @count: pointer of type atomic_t
> + *  @fail_fn: function to call if the original value was not 1
> + *
> + * Change the count from 1 to a value lower than 1, and call <fail_fn> if
> + * it wasn't 1 originally. This function MUST leave the value lower than
> + * 1 even when the "1" assertion wasn't true.
> + */
> +static inline void
> +__mutex_fastpath_lock(atomic_t *count, fastcall void (*fail_fn)(atomic_t *))
> +{
> +       if (unlikely(atomic_dec_return(count) < 0))
> +               fail_fn(count);
> +       else
> +               smp_mb();
> +}
> +
> +/**
> + *  __mutex_fastpath_lock_retval - try to take the lock by moving the count
> + *                                 from 1 to a 0 value
> + *  @count: pointer of type atomic_t
> + *  @fail_fn: function to call if the original value was not 1
> + *
> + * Change the count from 1 to a value lower than 1, and call <fail_fn> if
> + * it wasn't 1 originally. This function returns 0 if the fastpath succeeds,
> + * or anything the slow path function returns.
> + */
> +static inline int
> +__mutex_fastpath_lock_retval(atomic_t *count, fastcall int (*fail_fn)(atomic_t *))
> +{
> +       if (unlikely(atomic_dec_return(count) < 0))
> +               return fail_fn(count);
> +       else {
> +               smp_mb();
> +               return 0;
> +       }
> +}
> +
> +/**
> + *  __mutex_fastpath_unlock - try to promote the count from 0 to 1
> + *  @count: pointer of type atomic_t
> + *  @fail_fn: function to call if the original value was not 0
> + *
> + * Try to promote the count from 0 to 1. If it wasn't 0, call <fail_fn>.
> + * In the failure case, this function is allowed to either set the value to
> + * 1, or to set it to a value lower than 1.
> + *
> + * If the implementation sets it to a value of lower than 1, then the
> + * __mutex_slowpath_needs_to_unlock() macro needs to return 1, it needs
> + * to return 0 otherwise.
> + */
> +static inline void
> +__mutex_fastpath_unlock(atomic_t *count, fastcall void (*fail_fn)(atomic_t *))
> +{
> +       smp_mb();
> +       if (unlikely(atomic_inc_return(count) <= 0))
> +               fail_fn(count);
> +}
> +
> +#define __mutex_slowpath_needs_to_unlock()             1
> +
> +/**
> + * __mutex_fastpath_trylock - try to acquire the mutex, without waiting
> + *
> + *  @count: pointer of type atomic_t
> + *  @fail_fn: fallback function
> + *
> + * Change the count from 1 to a value lower than 1, and return 0 (failure)
> + * if it wasn't 1 originally, or return 1 (success) otherwise. This function
> + * MUST leave the value lower than 1 even when the "1" assertion wasn't true.
> + * Additionally, if the value was < 0 originally, this function must not leave
> + * it to 0 on failure.
> + *
> + * If the architecture has no effective trylock variant, it should call the
> + * <fail_fn> spinlock-based trylock variant unconditionally.
> + */
> +static inline int
> +__mutex_fastpath_trylock(atomic_t *count, int (*fail_fn)(atomic_t *))
> +{
> +       /*
> +        * We have two variants here. The cmpxchg based one is the best one
> +        * because it never induce a false contention state.  It is included
> +        * here because architectures using the inc/dec algorithms over the
> +        * xchg ones are much more likely to support cmpxchg natively.
> +        *
> +        * If not we fall back to the spinlock based variant - that is
> +        * just as efficient (and simpler) as a 'destructive' probing of
> +        * the mutex state would be.
> +        */
> +#ifdef __HAVE_ARCH_CMPXCHG
> +       if (likely(atomic_cmpxchg(count, 1, 0) == 1)) {
> +               smp_mb();
> +               return 1;
> +       }
> +       return 0;
> +#else
> +       return fail_fn(count);
> +#endif
> +}
> +
> +#endif
> diff --git a/arch/blackfin/include/asm/mutex.h b/arch/blackfin/include/asm/mutex.h
> index 458c1f7..5d39925 100644
> --- a/arch/blackfin/include/asm/mutex.h
> +++ b/arch/blackfin/include/asm/mutex.h
> @@ -6,4 +6,67 @@
>  * implementation. (see asm-generic/mutex-xchg.h for details)
>  */
>
> +#ifndef _ASM_MUTEX_H
> +#define _ASM_MUTEX_H
> +
> +#ifndef CONFIG_SMP
>  #include <asm-generic/mutex-dec.h>
> +#else
> +
> +static inline void
> +__mutex_fastpath_lock(atomic_t *count, void (*fail_fn)(atomic_t *))
> +{
> +       if (unlikely(atomic_dec_return(count) < 0))
> +               fail_fn(count);
> +       else
> +               smp_mb();
> +}
> +
> +static inline int
> +__mutex_fastpath_lock_retval(atomic_t *count, int (*fail_fn)(atomic_t *))
> +{
> +       if (unlikely(atomic_dec_return(count) < 0))
> +               return fail_fn(count);
> +       else {
> +               smp_mb();
> +               return 0;
> +       }
> +}
> +
> +static inline void
> +__mutex_fastpath_unlock(atomic_t *count, void (*fail_fn)(atomic_t *))
> +{
> +       smp_mb();
> +       if (unlikely(atomic_inc_return(count) <= 0))
> +               fail_fn(count);
> +}
> +
> +#define __mutex_slowpath_needs_to_unlock()             1
> +
> +static inline int
> +__mutex_fastpath_trylock(atomic_t *count, int (*fail_fn)(atomic_t *))
> +{
> +       /*
> +        * We have two variants here. The cmpxchg based one is the best one
> +        * because it never induce a false contention state.  It is included
> +        * here because architectures using the inc/dec algorithms over the
> +        * xchg ones are much more likely to support cmpxchg natively.
> +        *
> +        * If not we fall back to the spinlock based variant - that is
> +        * just as efficient (and simpler) as a 'destructive' probing of
> +        * the mutex state would be.
> +        */
> +#ifdef __HAVE_ARCH_CMPXCHG
> +       if (likely(atomic_cmpxchg(count, 1, 0) == 1)) {
> +               smp_mb();
> +               return 1;
> +       }
> +       return 0;
> +#else
> +       return fail_fn(count);
> +#endif
> +}
> +
> +#endif
> +
> +#endif
> diff --git a/arch/blackfin/include/asm/pda.h b/arch/blackfin/include/asm/pda.h
> new file mode 100644
> index 0000000..a24d130
> --- /dev/null
> +++ b/arch/blackfin/include/asm/pda.h
> @@ -0,0 +1,70 @@
> +/*
> + * File:         arch/blackfin/include/asm/pda.h
> + * Author:       Philippe Gerum <rpm@xenomai.org>
> + *
> + *               Copyright 2007 Analog Devices Inc.
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License as published by
> + * the Free Software Foundation; either version 2 of the License, or
> + * (at your option) any later version.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + * GNU General Public License for more details.
> + *
> + * You should have received a copy of the GNU General Public License
> + * along with this program; if not, see the file COPYING, or write
> + * to the Free Software Foundation, Inc.,
> + * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
> + */
> +
> +#ifndef _ASM_BLACKFIN_PDA_H
> +#define _ASM_BLACKFIN_PDA_H
> +
> +#include <asm/mem_map.h>
> +
> +#ifndef __ASSEMBLY__
> +
> +struct blackfin_pda {                  /* Per-processor Data Area */
> +       struct blackfin_pda *next;
> +
> +       unsigned long syscfg;
> +#ifdef CONFIG_SMP
> +       unsigned long imask;            /* Current IMASK value */
> +#endif
> +
> +       unsigned long *ipdt;            /* Start of switchable I-CPLB table */
> +       unsigned long *ipdt_swapcount;  /* Number of swaps in ipdt */
> +       unsigned long *dpdt;            /* Start of switchable D-CPLB table */
> +       unsigned long *dpdt_swapcount;  /* Number of swaps in dpdt */
> +
> +       /*
> +        * Single instructions can have multiple faults, which
> +        * need to be handled by traps.c, in irq5. We store
> +        * the exception cause to ensure we don't miss a
> +        * double fault condition
> +        */
> +       unsigned long ex_iptr;
> +       unsigned long ex_optr;
> +       unsigned long ex_buf[4];
> +       unsigned long ex_imask;         /* Saved imask from exception */
> +       unsigned long *ex_stack;        /* Exception stack space */
> +
> +#ifdef ANOMALY_05000261
> +       unsigned long last_cplb_fault_retx;
> +#endif
> +       unsigned long dcplb_fault_addr;
> +       unsigned long icplb_fault_addr;
> +       unsigned long retx;
> +       unsigned long seqstat;
> +};
> +
> +extern struct blackfin_pda cpu_pda[];
> +
> +void reserve_pda(void);
> +
> +#endif /* __ASSEMBLY__ */
> +
> +#endif /* _ASM_BLACKFIN_PDA_H */
> diff --git a/arch/blackfin/include/asm/percpu.h b/arch/blackfin/include/asm/percpu.h
> index 78dd61f..797c0c1 100644
> --- a/arch/blackfin/include/asm/percpu.h
> +++ b/arch/blackfin/include/asm/percpu.h
> @@ -3,4 +3,14 @@
>
>  #include <asm-generic/percpu.h>
>
> -#endif                         /* __ARCH_BLACKFIN_PERCPU__ */
> +#ifdef CONFIG_MODULES
> +#define PERCPU_MODULE_RESERVE 8192
> +#else
> +#define PERCPU_MODULE_RESERVE 0
> +#endif
> +
> +#define PERCPU_ENOUGH_ROOM \
> +       (ALIGN(__per_cpu_end - __per_cpu_start, SMP_CACHE_BYTES) + \
> +        PERCPU_MODULE_RESERVE)
> +
> +#endif /* __ARCH_BLACKFIN_PERCPU__ */
> diff --git a/arch/blackfin/include/asm/processor.h b/arch/blackfin/include/asm/processor.h
> index e3e9b41..30703c7 100644
> --- a/arch/blackfin/include/asm/processor.h
> +++ b/arch/blackfin/include/asm/processor.h
> @@ -106,7 +106,8 @@ unsigned long get_wchan(struct task_struct *p);
>        eip; })
>  #define        KSTK_ESP(tsk)   ((tsk) == current ? rdusp() : (tsk)->thread.usp)
>
> -#define cpu_relax()            barrier()
> +#define cpu_relax()            smp_mb()
> +
>
>  /* Get the Silicon Revision of the chip */
>  static inline uint32_t __pure bfin_revid(void)
> @@ -137,7 +138,11 @@ static inline uint32_t __pure bfin_revid(void)
>  static inline uint16_t __pure bfin_cpuid(void)
>  {
>        return (bfin_read_CHIPID() & CHIPID_FAMILY) >> 12;
> +}
>
> +static inline uint32_t __pure bfin_dspid(void)
> +{
> +       return bfin_read_DSPID();
>  }
>
>  static inline uint32_t __pure bfin_compiled_revid(void)
> diff --git a/arch/blackfin/include/asm/rwlock.h b/arch/blackfin/include/asm/rwlock.h
> new file mode 100644
> index 0000000..4a724b3
> --- /dev/null
> +++ b/arch/blackfin/include/asm/rwlock.h
> @@ -0,0 +1,6 @@
> +#ifndef _ASM_BLACKFIN_RWLOCK_H
> +#define _ASM_BLACKFIN_RWLOCK_H
> +
> +#define RW_LOCK_BIAS   0x01000000
> +
> +#endif
> diff --git a/arch/blackfin/include/asm/smp.h b/arch/blackfin/include/asm/smp.h
> new file mode 100644
> index 0000000..233cb8c
> --- /dev/null
> +++ b/arch/blackfin/include/asm/smp.h
> @@ -0,0 +1,42 @@
> +/*
> + * File:         arch/blackfin/include/asm/smp.h
> + * Author:       Philippe Gerum <rpm@xenomai.org>
> + *
> + *               Copyright 2007 Analog Devices Inc.
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License as published by
> + * the Free Software Foundation; either version 2 of the License, or
> + * (at your option) any later version.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + * GNU General Public License for more details.
> + *
> + * You should have received a copy of the GNU General Public License
> + * along with this program; if not, see the file COPYING, or write
> + * to the Free Software Foundation, Inc.,
> + * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
> + */
> +
> +#ifndef __ASM_BLACKFIN_SMP_H
> +#define __ASM_BLACKFIN_SMP_H
> +
> +#include <linux/kernel.h>
> +#include <linux/threads.h>
> +#include <linux/cpumask.h>
> +#include <linux/cache.h>
> +#include <asm/blackfin.h>
> +#include <mach/smp.h>
> +
> +#define raw_smp_processor_id()  blackfin_core_id()
> +
> +struct corelock_slot {
> +       int lock;
> +};
> +
> +void smp_icache_flush_range_others(unsigned long start,
> +                                  unsigned long end);
> +
> +#endif /* !__ASM_BLACKFIN_SMP_H */
> diff --git a/arch/blackfin/include/asm/spinlock.h b/arch/blackfin/include/asm/spinlock.h
> index 64e908a..0249ac3 100644
> --- a/arch/blackfin/include/asm/spinlock.h
> +++ b/arch/blackfin/include/asm/spinlock.h
> @@ -1,6 +1,89 @@
>  #ifndef __BFIN_SPINLOCK_H
>  #define __BFIN_SPINLOCK_H
>
> -#error blackfin architecture does not support SMP spin lock yet
> +#include <asm/atomic.h>
>
> -#endif
> +asmlinkage int __raw_spin_is_locked_asm(volatile int *ptr);
> +asmlinkage void __raw_spin_lock_asm(volatile int *ptr);
> +asmlinkage int __raw_spin_trylock_asm(volatile int *ptr);
> +asmlinkage void __raw_spin_unlock_asm(volatile int *ptr);
> +asmlinkage void __raw_read_lock_asm(volatile int *ptr);
> +asmlinkage int __raw_read_trylock_asm(volatile int *ptr);
> +asmlinkage void __raw_read_unlock_asm(volatile int *ptr);
> +asmlinkage void __raw_write_lock_asm(volatile int *ptr);
> +asmlinkage int __raw_write_trylock_asm(volatile int *ptr);
> +asmlinkage void __raw_write_unlock_asm(volatile int *ptr);
> +
> +static inline int __raw_spin_is_locked(raw_spinlock_t *lock)
> +{
> +       return __raw_spin_is_locked_asm(&lock->lock);
> +}
> +
> +static inline void __raw_spin_lock(raw_spinlock_t *lock)
> +{
> +       __raw_spin_lock_asm(&lock->lock);
> +}
> +
> +#define __raw_spin_lock_flags(lock, flags) __raw_spin_lock(lock)
> +
> +static inline int __raw_spin_trylock(raw_spinlock_t *lock)
> +{
> +       return __raw_spin_trylock_asm(&lock->lock);
> +}
> +
> +static inline void __raw_spin_unlock(raw_spinlock_t *lock)
> +{
> +       __raw_spin_unlock_asm(&lock->lock);
> +}
> +
> +static inline void __raw_spin_unlock_wait(raw_spinlock_t *lock)
> +{
> +       while (__raw_spin_is_locked(lock))
> +               cpu_relax();
> +}
> +
> +static inline int __raw_read_can_lock(raw_rwlock_t *rw)
> +{
> +       return __raw_uncached_fetch_asm(&rw->lock) > 0;
> +}
> +
> +static inline int __raw_write_can_lock(raw_rwlock_t *rw)
> +{
> +       return __raw_uncached_fetch_asm(&rw->lock) == RW_LOCK_BIAS;
> +}
> +
> +static inline void __raw_read_lock(raw_rwlock_t *rw)
> +{
> +       __raw_read_lock_asm(&rw->lock);
> +}
> +
> +static inline int __raw_read_trylock(raw_rwlock_t *rw)
> +{
> +       return __raw_read_trylock_asm(&rw->lock);
> +}
> +
> +static inline void __raw_read_unlock(raw_rwlock_t *rw)
> +{
> +       __raw_read_unlock_asm(&rw->lock);
> +}
> +
> +static inline void __raw_write_lock(raw_rwlock_t *rw)
> +{
> +       __raw_write_lock_asm(&rw->lock);
> +}
> +
> +static inline int __raw_write_trylock(raw_rwlock_t *rw)
> +{
> +       return __raw_write_trylock_asm(&rw->lock);
> +}
> +
> +static inline void __raw_write_unlock(raw_rwlock_t *rw)
> +{
> +       __raw_write_unlock_asm(&rw->lock);
> +}
> +
> +#define _raw_spin_relax(lock)          cpu_relax()
> +#define _raw_read_relax(lock)  cpu_relax()
> +#define _raw_write_relax(lock) cpu_relax()
> +
> +#endif /*  !__BFIN_SPINLOCK_H */
> diff --git a/arch/blackfin/include/asm/spinlock_types.h b/arch/blackfin/include/asm/spinlock_types.h
> new file mode 100644
> index 0000000..b1e3c4c
> --- /dev/null
> +++ b/arch/blackfin/include/asm/spinlock_types.h
> @@ -0,0 +1,22 @@
> +#ifndef __ASM_SPINLOCK_TYPES_H
> +#define __ASM_SPINLOCK_TYPES_H
> +
> +#ifndef __LINUX_SPINLOCK_TYPES_H
> +# error "please don't include this file directly"
> +#endif
> +
> +#include <asm/rwlock.h>
> +
> +typedef struct {
> +       volatile unsigned int lock;
> +} raw_spinlock_t;
> +
> +#define __RAW_SPIN_LOCK_UNLOCKED       { 0 }
> +
> +typedef struct {
> +       volatile unsigned int lock;
> +} raw_rwlock_t;
> +
> +#define __RAW_RW_LOCK_UNLOCKED         { RW_LOCK_BIAS }
> +
> +#endif
> diff --git a/arch/blackfin/include/asm/system.h b/arch/blackfin/include/asm/system.h
> index 8f1627d..6b368fa 100644
> --- a/arch/blackfin/include/asm/system.h
> +++ b/arch/blackfin/include/asm/system.h
> @@ -37,20 +37,16 @@
>  #include <linux/linkage.h>
>  #include <linux/compiler.h>
>  #include <mach/anomaly.h>
> +#include <asm/pda.h>
> +#include <asm/processor.h>
> +
> +/* Forward decl needed due to cdef inter dependencies */
> +static inline uint32_t __pure bfin_dspid(void);
> +#define blackfin_core_id() (bfin_dspid() & 0xff)
>
>  /*
>  * Interrupt configuring macros.
>  */
> -
> -extern unsigned long irq_flags;
> -
> -#define local_irq_enable() \
> -       __asm__ __volatile__( \
> -               "sti %0;" \
> -               : \
> -               : "d" (irq_flags) \
> -       )
> -
>  #define local_irq_disable() \
>        do { \
>                int __tmp_dummy; \
> @@ -66,6 +62,18 @@ extern unsigned long irq_flags;
>  # define NOP_PAD_ANOMALY_05000244
>  #endif
>
> +#ifdef CONFIG_SMP
> +# define irq_flags cpu_pda[blackfin_core_id()].imask
> +#else
> +extern unsigned long irq_flags;
> +#endif
> +
> +#define local_irq_enable() \
> +       __asm__ __volatile__( \
> +               "sti %0;" \
> +               : \
> +               : "d" (irq_flags) \
> +       )
>  #define idle_with_irq_disabled() \
>        __asm__ __volatile__( \
>                NOP_PAD_ANOMALY_05000244 \
> @@ -129,22 +137,85 @@ extern unsigned long irq_flags;
>  #define rmb()  asm volatile (""   : : :"memory")
>  #define wmb()  asm volatile (""   : : :"memory")
>  #define set_mb(var, value) do { (void) xchg(&var, value); } while (0)
> -
>  #define read_barrier_depends()                 do { } while(0)
>
>  #ifdef CONFIG_SMP
> -#define smp_mb()       mb()
> -#define smp_rmb()      rmb()
> -#define smp_wmb()      wmb()
> -#define smp_read_barrier_depends()     read_barrier_depends()
> +asmlinkage unsigned long __raw_xchg_1_asm(volatile void *ptr, unsigned long value);
> +asmlinkage unsigned long __raw_xchg_2_asm(volatile void *ptr, unsigned long value);
> +asmlinkage unsigned long __raw_xchg_4_asm(volatile void *ptr, unsigned long value);
> +asmlinkage unsigned long __raw_cmpxchg_1_asm(volatile void *ptr,
> +                                       unsigned long new, unsigned long old);
> +asmlinkage unsigned long __raw_cmpxchg_2_asm(volatile void *ptr,
> +                                       unsigned long new, unsigned long old);
> +asmlinkage unsigned long __raw_cmpxchg_4_asm(volatile void *ptr,
> +                                       unsigned long new, unsigned long old);
> +
> +#ifdef __ARCH_SYNC_CORE_DCACHE
> +# define smp_mb()      do { barrier(); smp_check_barrier(); smp_mark_barrier(); } while (0)
> +# define smp_rmb()     do { barrier(); smp_check_barrier(); } while (0)
> +# define smp_wmb()     do { barrier(); smp_mark_barrier(); } while (0)
>  #else
> +# define smp_mb()      barrier()
> +# define smp_rmb()     barrier()
> +# define smp_wmb()     barrier()
> +#endif
> +
> +static inline unsigned long __xchg(unsigned long x, volatile void *ptr,
> +                                  int size)
> +{
> +       unsigned long tmp;
> +
> +       switch (size) {
> +       case 1:
> +               tmp = __raw_xchg_1_asm(ptr, x);
> +               break;
> +       case 2:
> +               tmp = __raw_xchg_2_asm(ptr, x);
> +               break;
> +       case 4:
> +               tmp = __raw_xchg_4_asm(ptr, x);
> +               break;
> +       }
> +
> +       return tmp;
> +}
> +
> +/*
> + * Atomic compare and exchange.  Compare OLD with MEM, if identical,
> + * store NEW in MEM.  Return the initial value in MEM.  Success is
> + * indicated by comparing RETURN with OLD.
> + */
> +static inline unsigned long __cmpxchg(volatile void *ptr, unsigned long old,
> +                                     unsigned long new, int size)
> +{
> +       unsigned long tmp;
> +
> +       switch (size) {
> +       case 1:
> +               tmp = __raw_cmpxchg_1_asm(ptr, new, old);
> +               break;
> +       case 2:
> +               tmp = __raw_cmpxchg_2_asm(ptr, new, old);
> +               break;
> +       case 4:
> +               tmp = __raw_cmpxchg_4_asm(ptr, new, old);
> +               break;
> +       }
> +
> +       return tmp;
> +}
> +#define cmpxchg(ptr, o, n) \
> +       ((__typeof__(*(ptr)))__cmpxchg((ptr), (unsigned long)(o), \
> +               (unsigned long)(n), sizeof(*(ptr))))
> +
> +#define smp_read_barrier_depends()     smp_check_barrier()
> +
> +#else /* !CONFIG_SMP */
> +
>  #define smp_mb()       barrier()
>  #define smp_rmb()      barrier()
>  #define smp_wmb()      barrier()
>  #define smp_read_barrier_depends()     do { } while(0)
> -#endif
> -
> -#define xchg(ptr,x) ((__typeof__(*(ptr)))__xchg((unsigned long)(x),(ptr),sizeof(*(ptr))))
>
>  struct __xchg_dummy {
>        unsigned long a[100];
> @@ -194,9 +265,12 @@ static inline unsigned long __xchg(unsigned long x, volatile void *ptr,
>                        (unsigned long)(n), sizeof(*(ptr))))
>  #define cmpxchg64_local(ptr, o, n) __cmpxchg64_local_generic((ptr), (o), (n))
>
> -#ifndef CONFIG_SMP
>  #include <asm-generic/cmpxchg.h>
> -#endif
> +
> +#endif /* !CONFIG_SMP */
> +
> +#define xchg(ptr, x) ((__typeof__(*(ptr)))__xchg((unsigned long)(x), (ptr), sizeof(*(ptr))))
> +#define tas(ptr) ((void)xchg((ptr), 1))
>
>  #define prepare_to_switch()     do { } while(0)
>
> @@ -218,4 +292,4 @@ do {    \
>        (last) = resume (prev, next);   \
>  } while (0)
>
> -#endif                         /* _BLACKFIN_SYSTEM_H */
> +#endif /* _BLACKFIN_SYSTEM_H */
> diff --git a/arch/blackfin/mach-common/Makefile b/arch/blackfin/mach-common/Makefile
> index e6ed57c..9388b4a 100644
> --- a/arch/blackfin/mach-common/Makefile
> +++ b/arch/blackfin/mach-common/Makefile
> @@ -10,3 +10,4 @@ obj-$(CONFIG_BFIN_ICACHE_LOCK) += lock.o
>  obj-$(CONFIG_PM)          += pm.o dpmc_modes.o
>  obj-$(CONFIG_CPU_FREQ)    += cpufreq.o
>  obj-$(CONFIG_CPU_VOLTAGE) += dpmc.o
> +obj-$(CONFIG_SMP)         += smp.o
> diff --git a/arch/blackfin/mach-common/cache.S b/arch/blackfin/mach-common/cache.S
> index 3c98dac..1187512 100644
> --- a/arch/blackfin/mach-common/cache.S
> +++ b/arch/blackfin/mach-common/cache.S
> @@ -97,3 +97,39 @@ ENTRY(_blackfin_dflush_page)
>        P1 = 1 << (PAGE_SHIFT - L1_CACHE_SHIFT);
>        jump .Ldfr;
>  ENDPROC(_blackfin_dflush_page)
> +
> +/* Invalidate the Entire Data cache by
> + * clearing DMC[1:0] bits
> + */
> +ENTRY(_blackfin_invalidate_entire_dcache)
> +       [--SP] = ( R7:5);
> +
> +       P0.L = LO(DMEM_CONTROL);
> +       P0.H = HI(DMEM_CONTROL);
> +       R7 = [P0];
> +       R5 = R7;        /* Save DMEM_CNTR */
> +
> +       /* Clear the DMC[1:0] bits, All valid bits in the data
> +        * cache are set to the invalid state
> +        */
> +       BITCLR(R7,DMC0_P);
> +       BITCLR(R7,DMC1_P);
> +       CLI R6;
> +       SSYNC;          /* SSYNC required before writing to DMEM_CONTROL. */
> +       .align 8;
> +       [P0] = R7;
> +       SSYNC;
> +       STI R6;
> +
> +       /* Configures the data cache again */
> +
> +       CLI R6;
> +       SSYNC;          /* SSYNC required before writing to DMEM_CONTROL. */
> +       .align 8;
> +       [P0] = R5;
> +       SSYNC;
> +       STI R6;
> +
> +       ( R7:5) = [SP++];
> +       RTS;
> +ENDPROC(_blackfin_invalidate_entire_dcache)
> diff --git a/arch/blackfin/mach-common/entry.S b/arch/blackfin/mach-common/entry.S
> index c6ae844..5531f49 100644
> --- a/arch/blackfin/mach-common/entry.S
> +++ b/arch/blackfin/mach-common/entry.S
> @@ -36,6 +36,7 @@
>  #include <linux/init.h>
>  #include <linux/linkage.h>
>  #include <linux/unistd.h>
> +#include <linux/threads.h>
>  #include <asm/blackfin.h>
>  #include <asm/errno.h>
>  #include <asm/fixed_code.h>
> @@ -75,11 +76,11 @@ ENTRY(_ex_workaround_261)
>         * handle it.
>         */
>        P4 = R7;        /* Store EXCAUSE */
> -       p5.l = _last_cplb_fault_retx;
> -       p5.h = _last_cplb_fault_retx;
> -       r7 = [p5];
> +
> +       GET_PDA(p5, r7);
> +       r7 = [p5 + PDA_LFRETX];
>        r6 = retx;
> -       [p5] = r6;
> +       [p5 + PDA_LFRETX] = r6;
>        cc = r6 == r7;
>        if !cc jump _bfin_return_from_exception;
>        /* fall through */
> @@ -324,7 +325,9 @@ ENTRY(_ex_trap_c)
>        [p4] = p5;
>        csync;
>
> +       GET_PDA(p5, r6);
>  #ifndef CONFIG_DEBUG_DOUBLEFAULT
> +
>        /*
>         * Save these registers, as they are only valid in exception context
>         *  (where we are now - as soon as we defer to IRQ5, they can change)
> @@ -335,29 +338,25 @@ ENTRY(_ex_trap_c)
>        p4.l = lo(DCPLB_FAULT_ADDR);
>        p4.h = hi(DCPLB_FAULT_ADDR);
>        r7 = [p4];
> -       p5.h = _saved_dcplb_fault_addr;
> -       p5.l = _saved_dcplb_fault_addr;
> -       [p5] = r7;
> +       [p5 + PDA_DCPLB] = r7;
>
> -       r7 = [p4 + (ICPLB_FAULT_ADDR - DCPLB_FAULT_ADDR)];
> -       p5.h = _saved_icplb_fault_addr;
> -       p5.l = _saved_icplb_fault_addr;
> -       [p5] = r7;
> +       p4.l = lo(ICPLB_FAULT_ADDR);
> +       p4.h = hi(ICPLB_FAULT_ADDR);
> +       r6 = [p4];
> +       [p5 + PDA_ICPLB] = r6;
>
>        r6 = retx;
> -       p4.l = _saved_retx;
> -       p4.h = _saved_retx;
> -       [p4] = r6;
> +       [p5 + PDA_RETX] = r6;
>  #endif
>        r6 = SYSCFG;
> -       [p4 + 4] = r6;
> +       [p5 + PDA_SYSCFG] = r6;
>        BITCLR(r6, 0);
>        SYSCFG = r6;
>
>        /* Disable all interrupts, but make sure level 5 is enabled so
>         * we can switch to that level.  Save the old mask.  */
>        cli r6;
> -       [p4 + 8] = r6;
> +       [p5 + PDA_EXIMASK] = r6;
>
>        p4.l = lo(SAFE_USER_INSTRUCTION);
>        p4.h = hi(SAFE_USER_INSTRUCTION);
> @@ -424,17 +423,16 @@ ENDPROC(_double_fault)
>  ENTRY(_exception_to_level5)
>        SAVE_ALL_SYS
>
> -       p4.l = _saved_retx;
> -       p4.h = _saved_retx;
> -       r6 = [p4];
> +       GET_PDA(p4, r7);        /* Fetch current PDA */
> +       r6 = [p4 + PDA_RETX];
>        [sp + PT_PC] = r6;
>
> -       r6 = [p4 + 4];
> +       r6 = [p4 + PDA_SYSCFG];
>        [sp + PT_SYSCFG] = r6;
>
>        /* Restore interrupt mask.  We haven't pushed RETI, so this
>         * doesn't enable interrupts until we return from this handler.  */
> -       r6 = [p4 + 8];
> +       r6 = [p4 + PDA_EXIMASK];
>        sti r6;
>
>        /* Restore the hardware error vector.  */
> @@ -478,8 +476,8 @@ ENTRY(_trap) /* Exception: 4th entry into system event table(supervisor mode)*/
>         * scratch register (for want of a better option).
>         */
>        EX_SCRATCH_REG = sp;
> -       sp.l = _exception_stack_top;
> -       sp.h = _exception_stack_top;
> +       GET_PDA_SAFE(sp);
> +       sp = [sp + PDA_EXSTACK]
>        /* Try to deal with syscalls quickly.  */
>        [--sp] = ASTAT;
>        [--sp] = (R7:6,P5:4);
> @@ -501,27 +499,22 @@ ENTRY(_trap) /* Exception: 4th entry into system event table(supervisor mode)*/
>         * but they are not very interesting, so don't save them
>         */
>
> +       GET_PDA(p5, r7);
>        p4.l = lo(DCPLB_FAULT_ADDR);
>        p4.h = hi(DCPLB_FAULT_ADDR);
>        r7 = [p4];
> -       p5.h = _saved_dcplb_fault_addr;
> -       p5.l = _saved_dcplb_fault_addr;
> -       [p5] = r7;
> +       [p5 + PDA_DCPLB] = r7;
>
> -       r7 = [p4 + (ICPLB_FAULT_ADDR - DCPLB_FAULT_ADDR)];
> -       p5.h = _saved_icplb_fault_addr;
> -       p5.l = _saved_icplb_fault_addr;
> -       [p5] = r7;
> +       p4.l = lo(ICPLB_FAULT_ADDR);
> +       p4.h = hi(ICPLB_FAULT_ADDR);
> +       r7 = [p4];
> +       [p5 + PDA_ICPLB] = r7;
>
> -       p4.l = _saved_retx;
> -       p4.h = _saved_retx;
>        r6 = retx;
> -       [p4] = r6;
> +       [p5 + PDA_RETX] = r6;
>
>        r7 = SEQSTAT;           /* reason code is in bit 5:0 */
> -       p4.l = _saved_seqstat;
> -       p4.h = _saved_seqstat;
> -       [p4] = r7;
> +       [p5 + PDA_SEQSTAT] = r7;
>  #else
>        r7 = SEQSTAT;           /* reason code is in bit 5:0 */
>  #endif
> @@ -546,11 +539,11 @@ ENTRY(_kernel_execve)
>        p0 = sp;
>        r3 = SIZEOF_PTREGS / 4;
>        r4 = 0(x);
> -0:
> +.Lclear_regs:
>        [p0++] = r4;
>        r3 += -1;
>        cc = r3 == 0;
> -       if !cc jump 0b (bp);
> +       if !cc jump .Lclear_regs (bp);
>
>        p0 = sp;
>        sp += -16;
> @@ -558,7 +551,7 @@ ENTRY(_kernel_execve)
>        call _do_execve;
>        SP += 16;
>        cc = r0 == 0;
> -       if ! cc jump 1f;
> +       if ! cc jump .Lexecve_failed;
>        /* Success.  Copy our temporary pt_regs to the top of the kernel
>         * stack and do a normal exception return.
>         */
> @@ -574,12 +567,12 @@ ENTRY(_kernel_execve)
>        p0 = fp;
>        r4 = [p0--];
>        r3 = SIZEOF_PTREGS / 4;
> -0:
> +.Lcopy_regs:
>        r4 = [p0--];
>        [p1--] = r4;
>        r3 += -1;
>        cc = r3 == 0;
> -       if ! cc jump 0b (bp);
> +       if ! cc jump .Lcopy_regs (bp);
>
>        r0 = (KERNEL_STACK_SIZE - SIZEOF_PTREGS) (z);
>        p1 = r0;
> @@ -591,7 +584,7 @@ ENTRY(_kernel_execve)
>
>        RESTORE_CONTEXT;
>        rti;
> -1:
> +.Lexecve_failed:
>        unlink;
>        rts;
>  ENDPROC(_kernel_execve)
> @@ -925,9 +918,14 @@ _schedule_and_signal_from_int:
>        p1 = rets;
>        [sp + PT_RESERVED] = p1;
>
> +#ifdef CONFIG_SMP
> +       GET_PDA(p0, r0);        /* Fetch current PDA (can't migrate to other CPU here) */
> +       r0 = [p0 + PDA_IRQFLAGS];
> +#else
>        p0.l = _irq_flags;
>        p0.h = _irq_flags;
>        r0 = [p0];
> +#endif
>        sti r0;
>
>        r0 = sp;
> @@ -1539,12 +1537,6 @@ ENTRY(_sys_call_table)
>        .endr
>  END(_sys_call_table)
>
> -#if ANOMALY_05000261
> -/* Used by the assembly entry point to work around an anomaly.  */
> -_last_cplb_fault_retx:
> -       .long 0;
> -#endif
> -
>  #ifdef CONFIG_EXCEPTION_L1_SCRATCH
>  /* .section .l1.bss.scratch */
>  .set _exception_stack_top, L1_SCRATCH_START + L1_SCRATCH_LENGTH
> @@ -1554,8 +1546,8 @@ _last_cplb_fault_retx:
>  #else
>  .bss
>  #endif
> -_exception_stack:
> -       .rept 1024
> +ENTRY(_exception_stack)
> +       .rept 1024 * NR_CPUS
>        .long 0
>        .endr
>  _exception_stack_top:
> diff --git a/arch/blackfin/mach-common/head.S b/arch/blackfin/mach-common/head.S
> index c1dcaeb..a621ae4 100644
> --- a/arch/blackfin/mach-common/head.S
> +++ b/arch/blackfin/mach-common/head.S
> @@ -13,6 +13,7 @@
>  #include <asm/blackfin.h>
>  #include <asm/thread_info.h>
>  #include <asm/trace.h>
> +#include <asm/asm-offsets.h>
>
>  __INIT
>
> @@ -111,33 +112,26 @@ ENTRY(__start)
>         * This happens here, since L1 gets clobbered
>         * below
>         */
> -       p0.l = _saved_retx;
> -       p0.h = _saved_retx;
> +       GET_PDA(p0, r0);
> +       r7 = [p0 + PDA_RETX];
>        p1.l = _init_saved_retx;
>        p1.h = _init_saved_retx;
> -       r0 = [p0];
> -       [p1] = r0;
> +       [p1] = r7;
>
> -       p0.l = _saved_dcplb_fault_addr;
> -       p0.h = _saved_dcplb_fault_addr;
> +       r7 = [p0 + PDA_DCPLB];
>        p1.l = _init_saved_dcplb_fault_addr;
>        p1.h = _init_saved_dcplb_fault_addr;
> -       r0 = [p0];
> -       [p1] = r0;
> +       [p1] = r7;
>
> -       p0.l = _saved_icplb_fault_addr;
> -       p0.h = _saved_icplb_fault_addr;
> +       r7 = [p0 + PDA_ICPLB];
>        p1.l = _init_saved_icplb_fault_addr;
>        p1.h = _init_saved_icplb_fault_addr;
> -       r0 = [p0];
> -       [p1] = r0;
> +       [p1] = r7;
>
> -       p0.l = _saved_seqstat;
> -       p0.h = _saved_seqstat;
> +       r7 = [p0 + PDA_SEQSTAT];
>        p1.l = _init_saved_seqstat;
>        p1.h = _init_saved_seqstat;
> -       r0 = [p0];
> -       [p1] = r0;
> +       [p1] = r7;
>  #endif
>
>        /* Initialize stack pointer */
> @@ -255,6 +249,9 @@ ENTRY(_real_start)
>        sp = sp + p1;
>        usp = sp;
>        fp = sp;
> +       sp += -12;
> +       call _init_pda
> +       sp += 12;
>        jump.l _start_kernel;
>  ENDPROC(_real_start)
>
> diff --git a/arch/blackfin/mach-common/ints-priority.c b/arch/blackfin/mach-common/ints-priority.c
> index d45d0c5..eb8dfcf 100644
> --- a/arch/blackfin/mach-common/ints-priority.c
> +++ b/arch/blackfin/mach-common/ints-priority.c
> @@ -55,6 +55,7 @@
>  * -
>  */
>
> +#ifndef CONFIG_SMP
>  /* Initialize this to an actual value to force it into the .data
>  * section so that we know it is properly initialized at entry into
>  * the kernel but before bss is initialized to zero (which is where
> @@ -63,6 +64,7 @@
>  */
>  unsigned long irq_flags = 0x1f;
>  EXPORT_SYMBOL(irq_flags);
> +#endif
>
>  /* The number of spurious interrupts */
>  atomic_t num_spurious;
> @@ -163,6 +165,10 @@ static void bfin_internal_mask_irq(unsigned int irq)
>        mask_bit = SIC_SYSIRQ(irq) % 32;
>        bfin_write_SIC_IMASK(mask_bank, bfin_read_SIC_IMASK(mask_bank) &
>                             ~(1 << mask_bit));
> +#ifdef CONFIG_SMP
> +       bfin_write_SICB_IMASK(mask_bank, bfin_read_SICB_IMASK(mask_bank) &
> +                            ~(1 << mask_bit));
> +#endif
>  #endif
>  }
>
> @@ -177,6 +183,10 @@ static void bfin_internal_unmask_irq(unsigned int irq)
>        mask_bit = SIC_SYSIRQ(irq) % 32;
>        bfin_write_SIC_IMASK(mask_bank, bfin_read_SIC_IMASK(mask_bank) |
>                             (1 << mask_bit));
> +#ifdef CONFIG_SMP
> +       bfin_write_SICB_IMASK(mask_bank, bfin_read_SICB_IMASK(mask_bank) |
> +                            (1 << mask_bit));
> +#endif
>  #endif
>  }
>
> @@ -896,7 +906,7 @@ static struct irq_chip bfin_gpio_irqchip = {
>  #endif
>  };
>
> -void __init init_exception_vectors(void)
> +void __cpuinit init_exception_vectors(void)
>  {
>        /* cannot program in software:
>         * evt0 - emulation (jtag)
> @@ -935,6 +945,10 @@ int __init init_arch_irq(void)
>  # ifdef CONFIG_BF54x
>        bfin_write_SIC_IMASK2(SIC_UNMASK_ALL);
>  # endif
> +# ifdef CONFIG_SMP
> +       bfin_write_SICB_IMASK0(SIC_UNMASK_ALL);
> +       bfin_write_SICB_IMASK1(SIC_UNMASK_ALL);
> +# endif
>  #else
>        bfin_write_SIC_IMASK(SIC_UNMASK_ALL);
>  #endif
> @@ -995,6 +1009,17 @@ int __init init_arch_irq(void)
>
>                        break;
>  #endif
> +#ifdef CONFIG_TICK_SOURCE_SYSTMR0
> +               case IRQ_TIMER0:
> +                       set_irq_handler(irq, handle_percpu_irq);
> +                       break;
> +#endif
> +#ifdef CONFIG_SMP
> +               case IRQ_SUPPLE_0:
> +               case IRQ_SUPPLE_1:
> +                       set_irq_handler(irq, handle_percpu_irq);
> +                       break;
> +#endif
>                default:
>                        set_irq_handler(irq, handle_simple_irq);
>                        break;
> @@ -1029,7 +1054,7 @@ int __init init_arch_irq(void)
>        search_IAR();
>
>        /* Enable interrupts IVG7-15 */
> -       irq_flags = irq_flags | IMASK_IVG15 |
> +       irq_flags |= IMASK_IVG15 |
>            IMASK_IVG14 | IMASK_IVG13 | IMASK_IVG12 | IMASK_IVG11 |
>            IMASK_IVG10 | IMASK_IVG9 | IMASK_IVG8 | IMASK_IVG7 | IMASK_IVGHW;
>
> @@ -1070,8 +1095,16 @@ void do_irq(int vec, struct pt_regs *fp)
>        || defined(BF538_FAMILY) || defined(CONFIG_BF51x)
>                unsigned long sic_status[3];
>
> -               sic_status[0] = bfin_read_SIC_ISR0() & bfin_read_SIC_IMASK0();
> -               sic_status[1] = bfin_read_SIC_ISR1() & bfin_read_SIC_IMASK1();
> +               if (smp_processor_id()) {
> +#ifdef CONFIG_SMP
> +                       /* This will be optimized out in UP mode. */
> +                       sic_status[0] = bfin_read_SICB_ISR0() & bfin_read_SICB_IMASK0();
> +                       sic_status[1] = bfin_read_SICB_ISR1() & bfin_read_SICB_IMASK1();
> +#endif
> +               } else {
> +                       sic_status[0] = bfin_read_SIC_ISR0() & bfin_read_SIC_IMASK0();
> +                       sic_status[1] = bfin_read_SIC_ISR1() & bfin_read_SIC_IMASK1();
> +               }
>  #ifdef CONFIG_BF54x
>                sic_status[2] = bfin_read_SIC_ISR2() & bfin_read_SIC_IMASK2();
>  #endif
> diff --git a/arch/blackfin/mach-common/smp.c b/arch/blackfin/mach-common/smp.c
> new file mode 100644
> index 0000000..7aeeced
> --- /dev/null
> +++ b/arch/blackfin/mach-common/smp.c
> @@ -0,0 +1,476 @@
> +/*
> + * File:         arch/blackfin/kernel/smp.c
> + * Author:       Philippe Gerum <rpm@xenomai.org>
> + * IPI management based on arch/arm/kernel/smp.c.
> + *
> + *               Copyright 2007 Analog Devices Inc.
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License as published by
> + * the Free Software Foundation; either version 2 of the License, or
> + * (at your option) any later version.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + * GNU General Public License for more details.
> + *
> + * You should have received a copy of the GNU General Public License
> + * along with this program; if not, see the file COPYING, or write
> + * to the Free Software Foundation, Inc.,
> + * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
> + */
> +
> +#include <linux/module.h>
> +#include <linux/delay.h>
> +#include <linux/init.h>
> +#include <linux/spinlock.h>
> +#include <linux/sched.h>
> +#include <linux/interrupt.h>
> +#include <linux/cache.h>
> +#include <linux/profile.h>
> +#include <linux/errno.h>
> +#include <linux/mm.h>
> +#include <linux/cpu.h>
> +#include <linux/smp.h>
> +#include <linux/seq_file.h>
> +#include <linux/irq.h>
> +#include <asm/atomic.h>
> +#include <asm/cacheflush.h>
> +#include <asm/mmu_context.h>
> +#include <asm/pgtable.h>
> +#include <asm/pgalloc.h>
> +#include <asm/processor.h>
> +#include <asm/ptrace.h>
> +#include <asm/cpu.h>
> +#include <linux/err.h>
> +
> +struct corelock_slot corelock __attribute__ ((__section__(".l2.bss")));
> +
> +void __cpuinitdata *init_retx_coreb, *init_saved_retx_coreb,
> +       *init_saved_seqstat_coreb, *init_saved_icplb_fault_addr_coreb,
> +       *init_saved_dcplb_fault_addr_coreb;
> +
> +cpumask_t cpu_possible_map;
> +EXPORT_SYMBOL(cpu_possible_map);
> +
> +cpumask_t cpu_online_map;
> +EXPORT_SYMBOL(cpu_online_map);
> +
> +#define BFIN_IPI_RESCHEDULE   0
> +#define BFIN_IPI_CALL_FUNC    1
> +#define BFIN_IPI_CPU_STOP     2
> +
> +struct blackfin_flush_data {
> +       unsigned long start;
> +       unsigned long end;
> +};
> +
> +void *secondary_stack;
> +
> +
> +struct smp_call_struct {
> +       void (*func)(void *info);
> +       void *info;
> +       int wait;
> +       cpumask_t pending;
> +       cpumask_t waitmask;
> +};
> +
> +static struct blackfin_flush_data smp_flush_data;
> +
> +static DEFINE_SPINLOCK(stop_lock);
> +
> +struct ipi_message {
> +       struct list_head list;
> +       unsigned long type;
> +       struct smp_call_struct call_struct;
> +};
> +
> +struct ipi_message_queue {
> +       struct list_head head;
> +       spinlock_t lock;
> +       unsigned long count;
> +};
> +
> +static DEFINE_PER_CPU(struct ipi_message_queue, ipi_msg_queue);
> +
> +static void ipi_cpu_stop(unsigned int cpu)
> +{
> +       spin_lock(&stop_lock);
> +       printk(KERN_CRIT "CPU%u: stopping\n", cpu);
> +       dump_stack();
> +       spin_unlock(&stop_lock);
> +
> +       cpu_clear(cpu, cpu_online_map);
> +
> +       local_irq_disable();
> +
> +       while (1)
> +               SSYNC();
> +}
> +
> +static void ipi_flush_icache(void *info)
> +{
> +       struct blackfin_flush_data *fdata = info;
> +
> +       /* Invalidate the memory holding the bounds of the flushed region. */
> +       blackfin_dcache_invalidate_range((unsigned long)fdata,
> +                                        (unsigned long)fdata + sizeof(*fdata));
> +
> +       blackfin_icache_flush_range(fdata->start, fdata->end);
> +}
> +
> +static void ipi_call_function(unsigned int cpu, struct ipi_message *msg)
> +{
> +       int wait;
> +       void (*func)(void *info);
> +       void *info;
> +       func = msg->call_struct.func;
> +       info = msg->call_struct.info;
> +       wait = msg->call_struct.wait;
> +       cpu_clear(cpu, msg->call_struct.pending);
> +       func(info);
> +       if (wait)
> +               cpu_clear(cpu, msg->call_struct.waitmask);
> +       else
> +               kfree(msg);
> +}
> +
> +static irqreturn_t ipi_handler(int irq, void *dev_instance)
> +{
> +       struct ipi_message *msg, *mg;
> +       struct ipi_message_queue *msg_queue;
> +       unsigned int cpu = smp_processor_id();
> +
> +       platform_clear_ipi(cpu);
> +
> +       msg_queue = &__get_cpu_var(ipi_msg_queue);
> +       msg_queue->count++;
> +
> +       spin_lock(&msg_queue->lock);
> +       list_for_each_entry_safe(msg, mg, &msg_queue->head, list) {
> +               list_del(&msg->list);
> +               switch (msg->type) {
> +               case BFIN_IPI_RESCHEDULE:
> +                       /* That's the easiest one; leave it to
> +                        * return_from_int. */
> +                       kfree(msg);
> +                       break;
> +               case BFIN_IPI_CALL_FUNC:
> +                       ipi_call_function(cpu, msg);
> +                       break;
> +               case BFIN_IPI_CPU_STOP:
> +                       ipi_cpu_stop(cpu);
> +                       kfree(msg);
> +                       break;
> +               default:
> +                       printk(KERN_CRIT "CPU%u: Unknown IPI message \
> +                       0x%lx\n", cpu, msg->type);
> +                       kfree(msg);
> +                       break;
> +               }
> +       }
> +       spin_unlock(&msg_queue->lock);
> +       return IRQ_HANDLED;
> +}
> +
> +static void ipi_queue_init(void)
> +{
> +       unsigned int cpu;
> +       struct ipi_message_queue *msg_queue;
> +       for_each_possible_cpu(cpu) {
> +               msg_queue = &per_cpu(ipi_msg_queue, cpu);
> +               INIT_LIST_HEAD(&msg_queue->head);
> +               spin_lock_init(&msg_queue->lock);
> +               msg_queue->count = 0;
> +       }
> +}
> +
> +int smp_call_function(void (*func)(void *info), void *info, int wait)
> +{
> +       unsigned int cpu;
> +       cpumask_t callmap;
> +       unsigned long flags;
> +       struct ipi_message_queue *msg_queue;
> +       struct ipi_message *msg;
> +
> +       callmap = cpu_online_map;
> +       cpu_clear(smp_processor_id(), callmap);
> +       if (cpus_empty(callmap))
> +               return 0;
> +
> +       msg = kmalloc(sizeof(*msg), GFP_ATOMIC);
> +       INIT_LIST_HEAD(&msg->list);
> +       msg->call_struct.func = func;
> +       msg->call_struct.info = info;
> +       msg->call_struct.wait = wait;
> +       msg->call_struct.pending = callmap;
> +       msg->call_struct.waitmask = callmap;
> +       msg->type = BFIN_IPI_CALL_FUNC;
> +
> +       for_each_cpu_mask(cpu, callmap) {
> +               msg_queue = &per_cpu(ipi_msg_queue, cpu);
> +               spin_lock_irqsave(&msg_queue->lock, flags);
> +               list_add(&msg->list, &msg_queue->head);
> +               spin_unlock_irqrestore(&msg_queue->lock, flags);
> +               platform_send_ipi_cpu(cpu);
> +       }
> +       if (wait) {
> +               while (!cpus_empty(msg->call_struct.waitmask))
> +                       blackfin_dcache_invalidate_range(
> +                               (unsigned long)(&msg->call_struct.waitmask),
> +                               (unsigned long)(&msg->call_struct.waitmask));
> +               kfree(msg);
> +       }
> +       return 0;
> +}
> +EXPORT_SYMBOL_GPL(smp_call_function);
> +
> +int smp_call_function_single(int cpuid, void (*func) (void *info), void *info,
> +                               int wait)
> +{
> +       unsigned int cpu = cpuid;
> +       cpumask_t callmap;
> +       unsigned long flags;
> +       struct ipi_message_queue *msg_queue;
> +       struct ipi_message *msg;
> +
> +       if (cpu_is_offline(cpu))
> +               return 0;
> +       cpus_clear(callmap);
> +       cpu_set(cpu, callmap);
> +
> +       msg = kmalloc(sizeof(*msg), GFP_ATOMIC);
> +       INIT_LIST_HEAD(&msg->list);
> +       msg->call_struct.func = func;
> +       msg->call_struct.info = info;
> +       msg->call_struct.wait = wait;
> +       msg->call_struct.pending = callmap;
> +       msg->call_struct.waitmask = callmap;
> +       msg->type = BFIN_IPI_CALL_FUNC;
> +
> +       msg_queue = &per_cpu(ipi_msg_queue, cpu);
> +       spin_lock_irqsave(&msg_queue->lock, flags);
> +       list_add(&msg->list, &msg_queue->head);
> +       spin_unlock_irqrestore(&msg_queue->lock, flags);
> +       platform_send_ipi_cpu(cpu);
> +
> +       if (wait) {
> +               while (!cpus_empty(msg->call_struct.waitmask))
> +                       blackfin_dcache_invalidate_range(
> +                               (unsigned long)(&msg->call_struct.waitmask),
> +                               (unsigned long)(&msg->call_struct.waitmask));
> +               kfree(msg);
> +       }
> +       return 0;
> +}
> +EXPORT_SYMBOL_GPL(smp_call_function_single);
> +
> +void smp_send_reschedule(int cpu)
> +{
> +       unsigned long flags;
> +       struct ipi_message_queue *msg_queue;
> +       struct ipi_message *msg;
> +
> +       if (cpu_is_offline(cpu))
> +               return;
> +
> +       msg = kmalloc(sizeof(*msg), GFP_ATOMIC);
> +       memset(msg, 0, sizeof(msg));
> +       INIT_LIST_HEAD(&msg->list);
> +       msg->type = BFIN_IPI_RESCHEDULE;
> +
> +       msg_queue = &per_cpu(ipi_msg_queue, cpu);
> +       spin_lock_irqsave(&msg_queue->lock, flags);
> +       list_add(&msg->list, &msg_queue->head);
> +       spin_unlock_irqrestore(&msg_queue->lock, flags);
> +       platform_send_ipi_cpu(cpu);
> +
> +       return;
> +}
> +
> +void smp_send_stop(void)
> +{
> +       unsigned int cpu;
> +       cpumask_t callmap;
> +       unsigned long flags;
> +       struct ipi_message_queue *msg_queue;
> +       struct ipi_message *msg;
> +
> +       callmap = cpu_online_map;
> +       cpu_clear(smp_processor_id(), callmap);
> +       if (cpus_empty(callmap))
> +               return;
> +
> +       msg = kmalloc(sizeof(*msg), GFP_ATOMIC);
> +       memset(msg, 0, sizeof(msg));
> +       INIT_LIST_HEAD(&msg->list);
> +       msg->type = BFIN_IPI_CPU_STOP;
> +
> +       for_each_cpu_mask(cpu, callmap) {
> +               msg_queue = &per_cpu(ipi_msg_queue, cpu);
> +               spin_lock_irqsave(&msg_queue->lock, flags);
> +               list_add(&msg->list, &msg_queue->head);
> +               spin_unlock_irqrestore(&msg_queue->lock, flags);
> +               platform_send_ipi_cpu(cpu);
> +       }
> +       return;
> +}
> +
> +int __cpuinit __cpu_up(unsigned int cpu)
> +{
> +       struct task_struct *idle;
> +       int ret;
> +
> +       idle = fork_idle(cpu);
> +       if (IS_ERR(idle)) {
> +               printk(KERN_ERR "CPU%u: fork() failed\n", cpu);
> +               return PTR_ERR(idle);
> +       }
> +
> +       secondary_stack = task_stack_page(idle) + THREAD_SIZE;
> +       smp_wmb();
> +
> +       ret = platform_boot_secondary(cpu, idle);
> +
> +       if (ret) {
> +               cpu_clear(cpu, cpu_present_map);
> +               printk(KERN_CRIT "CPU%u: processor failed to boot (%d)\n", cpu, ret);
> +               free_task(idle);
> +       } else
> +               cpu_set(cpu, cpu_online_map);
> +
> +       secondary_stack = NULL;
> +
> +       return ret;
> +}
> +
> +static void __cpuinit setup_secondary(unsigned int cpu)
> +{
> +#ifndef CONFIG_TICK_SOURCE_SYSTMR0
> +       struct irq_desc *timer_desc;
> +#endif
> +       unsigned long ilat;
> +
> +       bfin_write_IMASK(0);
> +       CSYNC();
> +       ilat = bfin_read_ILAT();
> +       CSYNC();
> +       bfin_write_ILAT(ilat);
> +       CSYNC();
> +
> +       /* Reserve the PDA space for the secondary CPU. */
> +       reserve_pda();
> +
> +       /* Enable interrupt levels IVG7-15. IARs have been already
> +        * programmed by the boot CPU.  */
> +       irq_flags |= IMASK_IVG15 |
> +           IMASK_IVG14 | IMASK_IVG13 | IMASK_IVG12 | IMASK_IVG11 |
> +           IMASK_IVG10 | IMASK_IVG9 | IMASK_IVG8 | IMASK_IVG7 | IMASK_IVGHW;
> +
> +#ifdef CONFIG_TICK_SOURCE_SYSTMR0
> +       /* Power down the core timer, just to play safe. */
> +       bfin_write_TCNTL(0);
> +
> +       /* system timer0 has been setup by CoreA. */
> +#else
> +       timer_desc = irq_desc + IRQ_CORETMR;
> +       setup_core_timer();
> +       timer_desc->chip->enable(IRQ_CORETMR);
> +#endif
> +}
> +
> +void __cpuinit secondary_start_kernel(void)
> +{
> +       unsigned int cpu = smp_processor_id();
> +       struct mm_struct *mm = &init_mm;
> +
> +       if (_bfin_swrst & SWRST_DBL_FAULT_B) {
> +               printk(KERN_EMERG "CoreB Recovering from DOUBLE FAULT event\n");
> +#ifdef CONFIG_DEBUG_DOUBLEFAULT
> +               printk(KERN_EMERG " While handling exception (EXCAUSE = 0x%x) at %pF\n",
> +                       (int)init_saved_seqstat_coreb & SEQSTAT_EXCAUSE, init_saved_retx_coreb);
> +               printk(KERN_NOTICE "   DCPLB_FAULT_ADDR: %pF\n", init_saved_dcplb_fault_addr_coreb);
> +               printk(KERN_NOTICE "   ICPLB_FAULT_ADDR: %pF\n", init_saved_icplb_fault_addr_coreb);
> +#endif
> +               printk(KERN_NOTICE " The instruction at %pF caused a double exception\n",
> +                       init_retx_coreb);
> +       }
> +
> +       /*
> +        * We want the D-cache to be enabled early, in case the atomic
> +        * support code emulates cache coherence (see
> +        * __ARCH_SYNC_CORE_DCACHE).
> +        */
> +       init_exception_vectors();
> +
> +       bfin_setup_caches(cpu);
> +
> +       local_irq_disable();
> +
> +       /* Attach the new idle task to the global mm. */
> +       atomic_inc(&mm->mm_users);
> +       atomic_inc(&mm->mm_count);
> +       current->active_mm = mm;
> +       BUG_ON(current->mm);    /* Can't be, but better be safe than sorry. */
> +
> +       preempt_disable();
> +
> +       setup_secondary(cpu);
> +
> +       local_irq_enable();
> +
> +       platform_secondary_init(cpu);
> +
> +       cpu_idle();
> +}
> +
> +void __init smp_prepare_boot_cpu(void)
> +{
> +}
> +
> +void __init smp_prepare_cpus(unsigned int max_cpus)
> +{
> +       platform_prepare_cpus(max_cpus);
> +       ipi_queue_init();
> +       platform_request_ipi(&ipi_handler);
> +}
> +
> +void __init smp_cpus_done(unsigned int max_cpus)
> +{
> +       unsigned long bogosum = 0;
> +       unsigned int cpu;
> +
> +       for_each_online_cpu(cpu)
> +               bogosum += per_cpu(cpu_data, cpu).loops_per_jiffy;
> +
> +       printk(KERN_INFO "SMP: Total of %d processors activated "
> +              "(%lu.%02lu BogoMIPS).\n",
> +              num_online_cpus(),
> +              bogosum / (500000/HZ),
> +              (bogosum / (5000/HZ)) % 100);
> +}
> +
> +void smp_icache_flush_range_others(unsigned long start, unsigned long end)
> +{
> +       smp_flush_data.start = start;
> +       smp_flush_data.end = end;
> +
> +       if (smp_call_function(&ipi_flush_icache, &smp_flush_data, 1))
> +               printk(KERN_WARNING "SMP: failed to run I-cache flush request on other CPUs\n");
> +}
> +EXPORT_SYMBOL_GPL(smp_icache_flush_range_others);
> +
> +#ifdef __ARCH_SYNC_CORE_DCACHE
> +unsigned long barrier_mask __attribute__ ((__section__(".l2.bss")));
> +
> +void resync_core_dcache(void)
> +{
> +       unsigned int cpu = get_cpu();
> +       blackfin_invalidate_entire_dcache();
> +       ++per_cpu(cpu_data, cpu).dcache_invld_count;
> +       put_cpu();
> +}
> +EXPORT_SYMBOL(resync_core_dcache);
> +#endif
> diff --git a/arch/blackfin/oprofile/common.c b/arch/blackfin/oprofile/common.c
> index 0f6d303..f34795a 100644
> --- a/arch/blackfin/oprofile/common.c
> +++ b/arch/blackfin/oprofile/common.c
> @@ -130,7 +130,7 @@ int __init oprofile_arch_init(struct oprofile_operations *ops)
>
>        mutex_init(&pfmon_lock);
>
> -       dspid = bfin_read_DSPID();
> +       dspid = bfin_dspid();
>
>        printk(KERN_INFO "Oprofile got the cpu id is 0x%x. \n", dspid);
>
> --
> 1.5.6.3
>

^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: [PATCH 3/5] Blackfin arch: SMP supporting patchset: Blackfin CPLB related code
       [not found] ` <1226999108-13839-4-git-send-email-cooloney@kernel.org>
@ 2008-11-19  7:45   ` Bryan Wu
  0 siblings, 0 replies; 8+ messages in thread
From: Bryan Wu @ 2008-11-19  7:45 UTC (permalink / raw)
  To: torvalds, akpm, mingo; +Cc: linux-kernel, Graf Yang, Bryan Wu, linux-arch

Cc, linux-arch
-Bryan

On Tue, Nov 18, 2008 at 5:05 PM, Bryan Wu <cooloney@kernel.org> wrote:
> From: Graf Yang <graf.yang@analog.com>
>
> Blackfin dual core BF561 processor can support SMP like features.
> https://docs.blackfin.uclinux.org/doku.php?id=linux-kernel:smp-like
>
> In this patch, we provide SMP extend to Blackfin CPLB related code
>
> Signed-off-by: Graf Yang <graf.yang@analog.com>
> Signed-off-by: Bryan Wu <cooloney@kernel.org>
> ---
>  arch/blackfin/include/asm/cplb-mpu.h        |   15 ++--
>  arch/blackfin/include/asm/cplb.h            |   21 +++---
>  arch/blackfin/include/asm/cplbinit.h        |   57 ++++++++++++---
>  arch/blackfin/include/asm/mmu_context.h     |   27 +++++--
>  arch/blackfin/kernel/cplb-mpu/cacheinit.c   |    4 +-
>  arch/blackfin/kernel/cplb-mpu/cplbinfo.c    |   43 +++++++----
>  arch/blackfin/kernel/cplb-mpu/cplbinit.c    |   43 ++++++------
>  arch/blackfin/kernel/cplb-mpu/cplbmgr.c     |  102 ++++++++++++++-------------
>  arch/blackfin/kernel/cplb-nompu/cacheinit.c |    9 ++-
>  arch/blackfin/kernel/cplb-nompu/cplbinfo.c  |   55 +++++++++------
>  arch/blackfin/kernel/cplb-nompu/cplbinit.c  |   89 +++++++++---------------
>  arch/blackfin/kernel/cplb-nompu/cplbmgr.S   |   29 ++++----
>  12 files changed, 275 insertions(+), 219 deletions(-)
>
> diff --git a/arch/blackfin/include/asm/cplb-mpu.h b/arch/blackfin/include/asm/cplb-mpu.h
> index 75c67b9..80680ad 100644
> --- a/arch/blackfin/include/asm/cplb-mpu.h
> +++ b/arch/blackfin/include/asm/cplb-mpu.h
> @@ -28,6 +28,7 @@
>  */
>  #ifndef __ASM_BFIN_CPLB_MPU_H
>  #define __ASM_BFIN_CPLB_MPU_H
> +#include <linux/threads.h>
>
>  struct cplb_entry {
>        unsigned long data, addr;
> @@ -39,22 +40,22 @@ struct mem_region {
>        unsigned long icplb_data;
>  };
>
> -extern struct cplb_entry dcplb_tbl[MAX_CPLBS];
> -extern struct cplb_entry icplb_tbl[MAX_CPLBS];
> +extern struct cplb_entry dcplb_tbl[NR_CPUS][MAX_CPLBS];
> +extern struct cplb_entry icplb_tbl[NR_CPUS][MAX_CPLBS];
>  extern int first_switched_icplb;
>  extern int first_mask_dcplb;
>  extern int first_switched_dcplb;
>
> -extern int nr_dcplb_miss, nr_icplb_miss, nr_icplb_supv_miss, nr_dcplb_prot;
> -extern int nr_cplb_flush;
> +extern int nr_dcplb_miss[], nr_icplb_miss[], nr_icplb_supv_miss[];
> +extern int nr_dcplb_prot[], nr_cplb_flush[];
>
>  extern int page_mask_order;
>  extern int page_mask_nelts;
>
> -extern unsigned long *current_rwx_mask;
> +extern unsigned long *current_rwx_mask[NR_CPUS];
>
> -extern void flush_switched_cplbs(void);
> -extern void set_mask_dcplbs(unsigned long *);
> +extern void flush_switched_cplbs(unsigned int);
> +extern void set_mask_dcplbs(unsigned long *, unsigned int);
>
>  extern void __noreturn panic_cplb_error(int seqstat, struct pt_regs *);
>
> diff --git a/arch/blackfin/include/asm/cplb.h b/arch/blackfin/include/asm/cplb.h
> index 9e8b403..5f7545d 100644
> --- a/arch/blackfin/include/asm/cplb.h
> +++ b/arch/blackfin/include/asm/cplb.h
> @@ -30,7 +30,6 @@
>  #ifndef _CPLB_H
>  #define _CPLB_H
>
> -#include <asm/blackfin.h>
>  #include <mach/anomaly.h>
>
>  #define SDRAM_IGENERIC    (CPLB_L1_CHBL | CPLB_USER_RD | CPLB_VALID | CPLB_PORTPRIO)
> @@ -55,13 +54,24 @@
>  #endif
>
>  #define L1_DMEMORY       (CPLB_LOCK | CPLB_COMMON)
> +
> +#ifdef CONFIG_SMP
> +#define L2_ATTR           (INITIAL_T | I_CPLB | D_CPLB)
> +#define L2_IMEMORY         (CPLB_COMMON | CPLB_LOCK)
> +#define L2_DMEMORY         (CPLB_COMMON | CPLB_LOCK)
> +
> +#else
>  #ifdef CONFIG_BFIN_L2_CACHEABLE
>  #define L2_IMEMORY        (SDRAM_IGENERIC)
>  #define L2_DMEMORY        (SDRAM_DGENERIC)
>  #else
>  #define L2_IMEMORY        (CPLB_COMMON)
>  #define L2_DMEMORY        (CPLB_COMMON)
> -#endif
> +#endif /* CONFIG_BFIN_L2_CACHEABLE */
> +
> +#define L2_ATTR           (INITIAL_T | SWITCH_T | I_CPLB | D_CPLB)
> +#endif /* CONFIG_SMP */
> +
>  #define SDRAM_DNON_CHBL  (CPLB_COMMON)
>  #define SDRAM_EBIU       (CPLB_COMMON)
>  #define SDRAM_OOPS       (CPLB_VALID | ANOMALY_05000158_WORKAROUND | CPLB_LOCK | CPLB_DIRTY)
> @@ -71,14 +81,7 @@
>  #define SIZE_1M 0x00100000      /* 1M */
>  #define SIZE_4M 0x00400000      /* 4M */
>
> -#ifdef CONFIG_MPU
>  #define MAX_CPLBS 16
> -#else
> -#define MAX_CPLBS (16 * 2)
> -#endif
> -
> -#define ASYNC_MEMORY_CPLB_COVERAGE     ((ASYNC_BANK0_SIZE + ASYNC_BANK1_SIZE + \
> -                                ASYNC_BANK2_SIZE + ASYNC_BANK3_SIZE) / SIZE_4M)
>
>  #define CPLB_ENABLE_ICACHE_P   0
>  #define CPLB_ENABLE_DCACHE_P   1
> diff --git a/arch/blackfin/include/asm/cplbinit.h b/arch/blackfin/include/asm/cplbinit.h
> index f845b41..6bfc257 100644
> --- a/arch/blackfin/include/asm/cplbinit.h
> +++ b/arch/blackfin/include/asm/cplbinit.h
> @@ -36,6 +36,8 @@
>  #ifdef CONFIG_MPU
>
>  #include <asm/cplb-mpu.h>
> +extern void bfin_icache_init(struct cplb_entry *icplb_tbl);
> +extern void bfin_dcache_init(struct cplb_entry *icplb_tbl);
>
>  #else
>
> @@ -46,8 +48,40 @@
>
>  #define IN_KERNEL 1
>
> -enum
> -{ZERO_P, L1I_MEM, L1D_MEM, SDRAM_KERN , SDRAM_RAM_MTD, SDRAM_DMAZ, RES_MEM, ASYNC_MEM, L2_MEM};
> +#define ASYNC_MEMORY_CPLB_COVERAGE  ((ASYNC_BANK0_SIZE + ASYNC_BANK1_SIZE + \
> +                               ASYNC_BANK2_SIZE + ASYNC_BANK3_SIZE) / SIZE_4M)
> +
> +#define CPLB_MEM CONFIG_MAX_MEM_SIZE
> +
> +/*
> +* Number of required data CPLB switchtable entries
> +* MEMSIZE / 4 (we mostly install 4M page size CPLBs
> +* approx 16 for smaller 1MB page size CPLBs for allignment purposes
> +* 1 for L1 Data Memory
> +* possibly 1 for L2 Data Memory
> +* 1 for CONFIG_DEBUG_HUNT_FOR_ZERO
> +* 1 for ASYNC Memory
> +*/
> +#define MAX_SWITCH_D_CPLBS (((CPLB_MEM / 4) + 16 + 1 + 1 + 1 \
> +                                + ASYNC_MEMORY_CPLB_COVERAGE) * 2)
> +
> +/*
> +* Number of required instruction CPLB switchtable entries
> +* MEMSIZE / 4 (we mostly install 4M page size CPLBs
> +* approx 12 for smaller 1MB page size CPLBs for allignment purposes
> +* 1 for L1 Instruction Memory
> +* possibly 1 for L2 Instruction Memory
> +* 1 for CONFIG_DEBUG_HUNT_FOR_ZERO
> +*/
> +#define MAX_SWITCH_I_CPLBS (((CPLB_MEM / 4) + 12 + 1 + 1 + 1) * 2)
> +
> +/* Number of CPLB table entries, used for cplb-nompu. */
> +#define CPLB_TBL_ENTRIES (16 * 4)
> +
> +enum {
> +       ZERO_P, L1I_MEM, L1D_MEM, L2_MEM, SDRAM_KERN, SDRAM_RAM_MTD, SDRAM_DMAZ,
> +       RES_MEM, ASYNC_MEM, OCB_ROM
> +};
>
>  struct cplb_desc {
>        u32 start; /* start address */
> @@ -66,8 +100,8 @@ struct cplb_tab {
>        u16 size;
>  };
>
> -extern u_long icplb_table[];
> -extern u_long dcplb_table[];
> +extern u_long icplb_tables[NR_CPUS][CPLB_TBL_ENTRIES+1];
> +extern u_long dcplb_tables[NR_CPUS][CPLB_TBL_ENTRIES+1];
>
>  /* Till here we are discussing about the static memory management model.
>  * However, the operating envoronments commonly define more CPLB
> @@ -78,15 +112,18 @@ extern u_long dcplb_table[];
>  * This is how Page descriptor Table is implemented in uClinux/Blackfin.
>  */
>
> -extern u_long ipdt_table[];
> -extern u_long dpdt_table[];
> +extern u_long ipdt_tables[NR_CPUS][MAX_SWITCH_I_CPLBS+1];
> +extern u_long dpdt_tables[NR_CPUS][MAX_SWITCH_D_CPLBS+1];
>  #ifdef CONFIG_CPLB_INFO
> -extern u_long ipdt_swapcount_table[];
> -extern u_long dpdt_swapcount_table[];
> +extern u_long ipdt_swapcount_tables[NR_CPUS][MAX_SWITCH_I_CPLBS];
> +extern u_long dpdt_swapcount_tables[NR_CPUS][MAX_SWITCH_D_CPLBS];
>  #endif
> +extern void bfin_icache_init(u_long icplbs[]);
> +extern void bfin_dcache_init(u_long dcplbs[]);
>
>  #endif /* CONFIG_MPU */
>
> -extern void generate_cplb_tables(void);
> -
> +#if defined(CONFIG_BFIN_DCACHE) || defined(CONFIG_BFIN_ICACHE)
> +extern void generate_cplb_tables_cpu(unsigned int cpu);
> +#endif
>  #endif
> diff --git a/arch/blackfin/include/asm/mmu_context.h b/arch/blackfin/include/asm/mmu_context.h
> index 35593dd..944e29f 100644
> --- a/arch/blackfin/include/asm/mmu_context.h
> +++ b/arch/blackfin/include/asm/mmu_context.h
> @@ -37,6 +37,10 @@
>  #include <asm/pgalloc.h>
>  #include <asm/cplbinit.h>
>
> +/* Note: L1 stacks are CPU-private things, so we bluntly disable this
> +   feature in SMP mode, and use the per-CPU scratch SRAM bank only to
> +   store the PDA instead. */
> +
>  extern void *current_l1_stack_save;
>  extern int nr_l1stack_tasks;
>  extern void *l1_stack_base;
> @@ -88,12 +92,15 @@ activate_l1stack(struct mm_struct *mm, unsigned long sp_base)
>  static inline void switch_mm(struct mm_struct *prev_mm, struct mm_struct *next_mm,
>                             struct task_struct *tsk)
>  {
> +#ifdef CONFIG_MPU
> +       unsigned int cpu = smp_processor_id();
> +#endif
>        if (prev_mm == next_mm)
>                return;
>  #ifdef CONFIG_MPU
> -       if (prev_mm->context.page_rwx_mask == current_rwx_mask) {
> -               flush_switched_cplbs();
> -               set_mask_dcplbs(next_mm->context.page_rwx_mask);
> +       if (prev_mm->context.page_rwx_mask == current_rwx_mask[cpu]) {
> +               flush_switched_cplbs(cpu);
> +               set_mask_dcplbs(next_mm->context.page_rwx_mask, cpu);
>        }
>  #endif
>
> @@ -138,9 +145,10 @@ static inline void protect_page(struct mm_struct *mm, unsigned long addr,
>
>  static inline void update_protections(struct mm_struct *mm)
>  {
> -       if (mm->context.page_rwx_mask == current_rwx_mask) {
> -               flush_switched_cplbs();
> -               set_mask_dcplbs(mm->context.page_rwx_mask);
> +       unsigned int cpu = smp_processor_id();
> +       if (mm->context.page_rwx_mask == current_rwx_mask[cpu]) {
> +               flush_switched_cplbs(cpu);
> +               set_mask_dcplbs(mm->context.page_rwx_mask, cpu);
>        }
>  }
>  #endif
> @@ -165,6 +173,9 @@ init_new_context(struct task_struct *tsk, struct mm_struct *mm)
>  static inline void destroy_context(struct mm_struct *mm)
>  {
>        struct sram_list_struct *tmp;
> +#ifdef CONFIG_MPU
> +       unsigned int cpu = smp_processor_id();
> +#endif
>
>  #ifdef CONFIG_APP_STACK_L1
>        if (current_l1_stack_save == mm->context.l1_stack_save)
> @@ -179,8 +190,8 @@ static inline void destroy_context(struct mm_struct *mm)
>                kfree(tmp);
>        }
>  #ifdef CONFIG_MPU
> -       if (current_rwx_mask == mm->context.page_rwx_mask)
> -               current_rwx_mask = NULL;
> +       if (current_rwx_mask[cpu] == mm->context.page_rwx_mask)
> +               current_rwx_mask[cpu] = NULL;
>        free_pages((unsigned long)mm->context.page_rwx_mask, page_mask_order);
>  #endif
>  }
> diff --git a/arch/blackfin/kernel/cplb-mpu/cacheinit.c b/arch/blackfin/kernel/cplb-mpu/cacheinit.c
> index a8b712a..c6ff947 100644
> --- a/arch/blackfin/kernel/cplb-mpu/cacheinit.c
> +++ b/arch/blackfin/kernel/cplb-mpu/cacheinit.c
> @@ -25,7 +25,7 @@
>  #include <asm/cplbinit.h>
>
>  #if defined(CONFIG_BFIN_ICACHE)
> -void __init bfin_icache_init(void)
> +void __cpuinit bfin_icache_init(struct cplb_entry *icplb_tbl)
>  {
>        unsigned long ctrl;
>        int i;
> @@ -43,7 +43,7 @@ void __init bfin_icache_init(void)
>  #endif
>
>  #if defined(CONFIG_BFIN_DCACHE)
> -void __init bfin_dcache_init(void)
> +void __cpuinit bfin_dcache_init(struct cplb_entry *dcplb_tbl)
>  {
>        unsigned long ctrl;
>        int i;
> diff --git a/arch/blackfin/kernel/cplb-mpu/cplbinfo.c b/arch/blackfin/kernel/cplb-mpu/cplbinfo.c
> index 822beef..00cb2cf 100644
> --- a/arch/blackfin/kernel/cplb-mpu/cplbinfo.c
> +++ b/arch/blackfin/kernel/cplb-mpu/cplbinfo.c
> @@ -66,32 +66,32 @@ static char *cplb_print_entry(char *buf, struct cplb_entry *tbl, int switched)
>        return buf;
>  }
>
> -int cplbinfo_proc_output(char *buf)
> +int cplbinfo_proc_output(char *buf, void *data)
>  {
>        char *p;
> +       unsigned int cpu = (unsigned int)data;;
>
>        p = buf;
>
> -       p += sprintf(p, "------------------ CPLB Information ------------------\n\n");
> -
> +       p += sprintf(p, "------------- CPLB Information on CPU%u --------------\n\n", cpu);
>        if (bfin_read_IMEM_CONTROL() & ENICPLB) {
>                p += sprintf(p, "Instruction CPLB entry:\n");
> -               p = cplb_print_entry(p, icplb_tbl, first_switched_icplb);
> +               p = cplb_print_entry(p, icplb_tbl[cpu], first_switched_icplb);
>        } else
>                p += sprintf(p, "Instruction CPLB is disabled.\n\n");
>
>        if (1 || bfin_read_DMEM_CONTROL() & ENDCPLB) {
>                p += sprintf(p, "Data CPLB entry:\n");
> -               p = cplb_print_entry(p, dcplb_tbl, first_switched_dcplb);
> +               p = cplb_print_entry(p, dcplb_tbl[cpu], first_switched_dcplb);
>        } else
>                p += sprintf(p, "Data CPLB is disabled.\n");
>
>        p += sprintf(p, "ICPLB miss: %d\nICPLB supervisor miss: %d\n",
> -                    nr_icplb_miss, nr_icplb_supv_miss);
> +                    nr_icplb_miss[cpu], nr_icplb_supv_miss[cpu]);
>        p += sprintf(p, "DCPLB miss: %d\nDCPLB protection fault:%d\n",
> -                    nr_dcplb_miss, nr_dcplb_prot);
> +                    nr_dcplb_miss[cpu], nr_dcplb_prot[cpu]);
>        p += sprintf(p, "CPLB flushes: %d\n",
> -                    nr_cplb_flush);
> +                    nr_cplb_flush[cpu]);
>
>        return p - buf;
>  }
> @@ -101,7 +101,7 @@ static int cplbinfo_read_proc(char *page, char **start, off_t off,
>  {
>        int len;
>
> -       len = cplbinfo_proc_output(page);
> +       len = cplbinfo_proc_output(page, data);
>        if (len <= off + count)
>                *eof = 1;
>        *start = page + off;
> @@ -115,20 +115,33 @@ static int cplbinfo_read_proc(char *page, char **start, off_t off,
>
>  static int __init cplbinfo_init(void)
>  {
> -       struct proc_dir_entry *entry;
> +       struct proc_dir_entry *parent, *entry;
> +       unsigned int cpu;
> +       unsigned char str[10];
> +
> +       parent = proc_mkdir("cplbinfo", NULL);
>
> -       entry = create_proc_entry("cplbinfo", 0, NULL);
> -       if (!entry)
> -               return -ENOMEM;
> +       for_each_online_cpu(cpu) {
> +               sprintf(str, "cpu%u", cpu);
> +               entry = create_proc_entry(str, 0, parent);
> +               if (!entry)
> +                       return -ENOMEM;
>
> -       entry->read_proc = cplbinfo_read_proc;
> -       entry->data = NULL;
> +               entry->read_proc = cplbinfo_read_proc;
> +               entry->data = (void *)cpu;
> +       }
>
>        return 0;
>  }
>
>  static void __exit cplbinfo_exit(void)
>  {
> +       unsigned int cpu;
> +       unsigned char str[20];
> +       for_each_online_cpu(cpu) {
> +               sprintf(str, "cplbinfo/cpu%u", cpu);
> +               remove_proc_entry(str, NULL);
> +       }
>        remove_proc_entry("cplbinfo", NULL);
>  }
>
> diff --git a/arch/blackfin/kernel/cplb-mpu/cplbinit.c b/arch/blackfin/kernel/cplb-mpu/cplbinit.c
> index 55af729..269d2a3 100644
> --- a/arch/blackfin/kernel/cplb-mpu/cplbinit.c
> +++ b/arch/blackfin/kernel/cplb-mpu/cplbinit.c
> @@ -30,13 +30,13 @@
>  # error the MPU will not function safely while Anomaly 05000263 applies
>  #endif
>
> -struct cplb_entry icplb_tbl[MAX_CPLBS];
> -struct cplb_entry dcplb_tbl[MAX_CPLBS];
> +struct cplb_entry icplb_tbl[NR_CPUS][MAX_CPLBS];
> +struct cplb_entry dcplb_tbl[NR_CPUS][MAX_CPLBS];
>
>  int first_switched_icplb, first_switched_dcplb;
>  int first_mask_dcplb;
>
> -void __init generate_cplb_tables(void)
> +void __init generate_cplb_tables_cpu(unsigned int cpu)
>  {
>        int i_d, i_i;
>        unsigned long addr;
> @@ -55,15 +55,16 @@ void __init generate_cplb_tables(void)
>        d_cache |= CPLB_L1_AOW | CPLB_WT;
>  #endif
>  #endif
> +
>        i_d = i_i = 0;
>
>        /* Set up the zero page.  */
> -       dcplb_tbl[i_d].addr = 0;
> -       dcplb_tbl[i_d++].data = SDRAM_OOPS | PAGE_SIZE_1KB;
> +       dcplb_tbl[cpu][i_d].addr = 0;
> +       dcplb_tbl[cpu][i_d++].data = SDRAM_OOPS | PAGE_SIZE_1KB;
>
>  #if 0
> -       icplb_tbl[i_i].addr = 0;
> -       icplb_tbl[i_i++].data = i_cache | CPLB_USER_RD | PAGE_SIZE_4KB;
> +       icplb_tbl[cpu][i_i].addr = 0;
> +       icplb_tbl[cpu][i_i++].data = i_cache | CPLB_USER_RD | PAGE_SIZE_4KB;
>  #endif
>
>        /* Cover kernel memory with 4M pages.  */
> @@ -72,28 +73,28 @@ void __init generate_cplb_tables(void)
>        i_data = i_cache | CPLB_VALID | CPLB_PORTPRIO | PAGE_SIZE_4MB;
>
>        for (; addr < memory_start; addr += 4 * 1024 * 1024) {
> -               dcplb_tbl[i_d].addr = addr;
> -               dcplb_tbl[i_d++].data = d_data;
> -               icplb_tbl[i_i].addr = addr;
> -               icplb_tbl[i_i++].data = i_data | (addr == 0 ? CPLB_USER_RD : 0);
> +               dcplb_tbl[cpu][i_d].addr = addr;
> +               dcplb_tbl[cpu][i_d++].data = d_data;
> +               icplb_tbl[cpu][i_i].addr = addr;
> +               icplb_tbl[cpu][i_i++].data = i_data | (addr == 0 ? CPLB_USER_RD : 0);
>        }
>
>        /* Cover L1 memory.  One 4M area for code and data each is enough.  */
>  #if L1_DATA_A_LENGTH > 0 || L1_DATA_B_LENGTH > 0
> -       dcplb_tbl[i_d].addr = L1_DATA_A_START;
> -       dcplb_tbl[i_d++].data = L1_DMEMORY | PAGE_SIZE_4MB;
> +       dcplb_tbl[cpu][i_d].addr = get_l1_data_a_start_cpu(cpu);
> +       dcplb_tbl[cpu][i_d++].data = L1_DMEMORY | PAGE_SIZE_4MB;
>  #endif
>  #if L1_CODE_LENGTH > 0
> -       icplb_tbl[i_i].addr = L1_CODE_START;
> -       icplb_tbl[i_i++].data = L1_IMEMORY | PAGE_SIZE_4MB;
> +       icplb_tbl[cpu][i_i].addr = get_l1_code_start_cpu(cpu);
> +       icplb_tbl[cpu][i_i++].data = L1_IMEMORY | PAGE_SIZE_4MB;
>  #endif
>
>        /* Cover L2 memory */
>  #if L2_LENGTH > 0
> -       dcplb_tbl[i_d].addr = L2_START;
> -       dcplb_tbl[i_d++].data = L2_DMEMORY | PAGE_SIZE_1MB;
> -       icplb_tbl[i_i].addr = L2_START;
> -       icplb_tbl[i_i++].data = L2_IMEMORY | PAGE_SIZE_1MB;
> +       dcplb_tbl[cpu][i_d].addr = L2_START;
> +       dcplb_tbl[cpu][i_d++].data = L2_DMEMORY | PAGE_SIZE_1MB;
> +       icplb_tbl[cpu][i_i].addr = L2_START;
> +       icplb_tbl[cpu][i_i++].data = L2_IMEMORY | PAGE_SIZE_1MB;
>  #endif
>
>        first_mask_dcplb = i_d;
> @@ -101,7 +102,7 @@ void __init generate_cplb_tables(void)
>        first_switched_icplb = i_i;
>
>        while (i_d < MAX_CPLBS)
> -               dcplb_tbl[i_d++].data = 0;
> +               dcplb_tbl[cpu][i_d++].data = 0;
>        while (i_i < MAX_CPLBS)
> -               icplb_tbl[i_i++].data = 0;
> +               icplb_tbl[cpu][i_i++].data = 0;
>  }
> diff --git a/arch/blackfin/kernel/cplb-mpu/cplbmgr.c b/arch/blackfin/kernel/cplb-mpu/cplbmgr.c
> index baa52e2..76bd991 100644
> --- a/arch/blackfin/kernel/cplb-mpu/cplbmgr.c
> +++ b/arch/blackfin/kernel/cplb-mpu/cplbmgr.c
> @@ -30,10 +30,11 @@
>
>  int page_mask_nelts;
>  int page_mask_order;
> -unsigned long *current_rwx_mask;
> +unsigned long *current_rwx_mask[NR_CPUS];
>
> -int nr_dcplb_miss, nr_icplb_miss, nr_icplb_supv_miss, nr_dcplb_prot;
> -int nr_cplb_flush;
> +int nr_dcplb_miss[NR_CPUS], nr_icplb_miss[NR_CPUS];
> +int nr_icplb_supv_miss[NR_CPUS], nr_dcplb_prot[NR_CPUS];
> +int nr_cplb_flush[NR_CPUS];
>
>  static inline void disable_dcplb(void)
>  {
> @@ -98,42 +99,42 @@ static inline int write_permitted(int status, unsigned long data)
>  }
>
>  /* Counters to implement round-robin replacement.  */
> -static int icplb_rr_index, dcplb_rr_index;
> +static int icplb_rr_index[NR_CPUS], dcplb_rr_index[NR_CPUS];
>
>  /*
>  * Find an ICPLB entry to be evicted and return its index.
>  */
> -static int evict_one_icplb(void)
> +static int evict_one_icplb(unsigned int cpu)
>  {
>        int i;
>        for (i = first_switched_icplb; i < MAX_CPLBS; i++)
> -               if ((icplb_tbl[i].data & CPLB_VALID) == 0)
> +               if ((icplb_tbl[cpu][i].data & CPLB_VALID) == 0)
>                        return i;
> -       i = first_switched_icplb + icplb_rr_index;
> +       i = first_switched_icplb + icplb_rr_index[cpu];
>        if (i >= MAX_CPLBS) {
>                i -= MAX_CPLBS - first_switched_icplb;
> -               icplb_rr_index -= MAX_CPLBS - first_switched_icplb;
> +               icplb_rr_index[cpu] -= MAX_CPLBS - first_switched_icplb;
>        }
> -       icplb_rr_index++;
> +       icplb_rr_index[cpu]++;
>        return i;
>  }
>
> -static int evict_one_dcplb(void)
> +static int evict_one_dcplb(unsigned int cpu)
>  {
>        int i;
>        for (i = first_switched_dcplb; i < MAX_CPLBS; i++)
> -               if ((dcplb_tbl[i].data & CPLB_VALID) == 0)
> +               if ((dcplb_tbl[cpu][i].data & CPLB_VALID) == 0)
>                        return i;
> -       i = first_switched_dcplb + dcplb_rr_index;
> +       i = first_switched_dcplb + dcplb_rr_index[cpu];
>        if (i >= MAX_CPLBS) {
>                i -= MAX_CPLBS - first_switched_dcplb;
> -               dcplb_rr_index -= MAX_CPLBS - first_switched_dcplb;
> +               dcplb_rr_index[cpu] -= MAX_CPLBS - first_switched_dcplb;
>        }
> -       dcplb_rr_index++;
> +       dcplb_rr_index[cpu]++;
>        return i;
>  }
>
> -static noinline int dcplb_miss(void)
> +static noinline int dcplb_miss(unsigned int cpu)
>  {
>        unsigned long addr = bfin_read_DCPLB_FAULT_ADDR();
>        int status = bfin_read_DCPLB_STATUS();
> @@ -141,7 +142,7 @@ static noinline int dcplb_miss(void)
>        int idx;
>        unsigned long d_data;
>
> -       nr_dcplb_miss++;
> +       nr_dcplb_miss[cpu]++;
>
>        d_data = CPLB_SUPV_WR | CPLB_VALID | CPLB_DIRTY | PAGE_SIZE_4KB;
>  #ifdef CONFIG_BFIN_DCACHE
> @@ -168,25 +169,25 @@ static noinline int dcplb_miss(void)
>        } else if (addr >= _ramend) {
>            d_data |= CPLB_USER_RD | CPLB_USER_WR;
>        } else {
> -               mask = current_rwx_mask;
> +               mask = current_rwx_mask[cpu];
>                if (mask) {
>                        int page = addr >> PAGE_SHIFT;
> -                       int offs = page >> 5;
> +                       int idx = page >> 5;
>                        int bit = 1 << (page & 31);
>
> -                       if (mask[offs] & bit)
> +                       if (mask[idx] & bit)
>                                d_data |= CPLB_USER_RD;
>
>                        mask += page_mask_nelts;
> -                       if (mask[offs] & bit)
> +                       if (mask[idx] & bit)
>                                d_data |= CPLB_USER_WR;
>                }
>        }
> -       idx = evict_one_dcplb();
> +       idx = evict_one_dcplb(cpu);
>
>        addr &= PAGE_MASK;
> -       dcplb_tbl[idx].addr = addr;
> -       dcplb_tbl[idx].data = d_data;
> +       dcplb_tbl[cpu][idx].addr = addr;
> +       dcplb_tbl[cpu][idx].data = d_data;
>
>        disable_dcplb();
>        bfin_write32(DCPLB_DATA0 + idx * 4, d_data);
> @@ -196,21 +197,21 @@ static noinline int dcplb_miss(void)
>        return 0;
>  }
>
> -static noinline int icplb_miss(void)
> +static noinline int icplb_miss(unsigned int cpu)
>  {
>        unsigned long addr = bfin_read_ICPLB_FAULT_ADDR();
>        int status = bfin_read_ICPLB_STATUS();
>        int idx;
>        unsigned long i_data;
>
> -       nr_icplb_miss++;
> +       nr_icplb_miss[cpu]++;
>
>        /* If inside the uncached DMA region, fault.  */
>        if (addr >= _ramend - DMA_UNCACHED_REGION && addr < _ramend)
>                return CPLB_PROT_VIOL;
>
>        if (status & FAULT_USERSUPV)
> -               nr_icplb_supv_miss++;
> +               nr_icplb_supv_miss[cpu]++;
>
>        /*
>         * First, try to find a CPLB that matches this address.  If we
> @@ -218,8 +219,8 @@ static noinline int icplb_miss(void)
>         * that the instruction crosses a page boundary.
>         */
>        for (idx = first_switched_icplb; idx < MAX_CPLBS; idx++) {
> -               if (icplb_tbl[idx].data & CPLB_VALID) {
> -                       unsigned long this_addr = icplb_tbl[idx].addr;
> +               if (icplb_tbl[cpu][idx].data & CPLB_VALID) {
> +                       unsigned long this_addr = icplb_tbl[cpu][idx].addr;
>                        if (this_addr <= addr && this_addr + PAGE_SIZE > addr) {
>                                addr += PAGE_SIZE;
>                                break;
> @@ -257,23 +258,23 @@ static noinline int icplb_miss(void)
>                 * Otherwise, check the x bitmap of the current process.
>                 */
>                if (!(status & FAULT_USERSUPV)) {
> -                       unsigned long *mask = current_rwx_mask;
> +                       unsigned long *mask = current_rwx_mask[cpu];
>
>                        if (mask) {
>                                int page = addr >> PAGE_SHIFT;
> -                               int offs = page >> 5;
> +                               int idx = page >> 5;
>                                int bit = 1 << (page & 31);
>
>                                mask += 2 * page_mask_nelts;
> -                               if (mask[offs] & bit)
> +                               if (mask[idx] & bit)
>                                        i_data |= CPLB_USER_RD;
>                        }
>                }
>        }
> -       idx = evict_one_icplb();
> +       idx = evict_one_icplb(cpu);
>        addr &= PAGE_MASK;
> -       icplb_tbl[idx].addr = addr;
> -       icplb_tbl[idx].data = i_data;
> +       icplb_tbl[cpu][idx].addr = addr;
> +       icplb_tbl[cpu][idx].data = i_data;
>
>        disable_icplb();
>        bfin_write32(ICPLB_DATA0 + idx * 4, i_data);
> @@ -283,19 +284,19 @@ static noinline int icplb_miss(void)
>        return 0;
>  }
>
> -static noinline int dcplb_protection_fault(void)
> +static noinline int dcplb_protection_fault(unsigned int cpu)
>  {
>        int status = bfin_read_DCPLB_STATUS();
>
> -       nr_dcplb_prot++;
> +       nr_dcplb_prot[cpu]++;
>
>        if (status & FAULT_RW) {
>                int idx = faulting_cplb_index(status);
> -               unsigned long data = dcplb_tbl[idx].data;
> +               unsigned long data = dcplb_tbl[cpu][idx].data;
>                if (!(data & CPLB_WT) && !(data & CPLB_DIRTY) &&
>                    write_permitted(status, data)) {
>                        data |= CPLB_DIRTY;
> -                       dcplb_tbl[idx].data = data;
> +                       dcplb_tbl[cpu][idx].data = data;
>                        bfin_write32(DCPLB_DATA0 + idx * 4, data);
>                        return 0;
>                }
> @@ -306,36 +307,37 @@ static noinline int dcplb_protection_fault(void)
>  int cplb_hdr(int seqstat, struct pt_regs *regs)
>  {
>        int cause = seqstat & 0x3f;
> +       unsigned int cpu = smp_processor_id();
>        switch (cause) {
>        case 0x23:
> -               return dcplb_protection_fault();
> +               return dcplb_protection_fault(cpu);
>        case 0x2C:
> -               return icplb_miss();
> +               return icplb_miss(cpu);
>        case 0x26:
> -               return dcplb_miss();
> +               return dcplb_miss(cpu);
>        default:
>                return 1;
>        }
>  }
>
> -void flush_switched_cplbs(void)
> +void flush_switched_cplbs(unsigned int cpu)
>  {
>        int i;
>        unsigned long flags;
>
> -       nr_cplb_flush++;
> +       nr_cplb_flush[cpu]++;
>
>        local_irq_save(flags);
>        disable_icplb();
>        for (i = first_switched_icplb; i < MAX_CPLBS; i++) {
> -               icplb_tbl[i].data = 0;
> +               icplb_tbl[cpu][i].data = 0;
>                bfin_write32(ICPLB_DATA0 + i * 4, 0);
>        }
>        enable_icplb();
>
>        disable_dcplb();
>        for (i = first_switched_dcplb; i < MAX_CPLBS; i++) {
> -               dcplb_tbl[i].data = 0;
> +               dcplb_tbl[cpu][i].data = 0;
>                bfin_write32(DCPLB_DATA0 + i * 4, 0);
>        }
>        enable_dcplb();
> @@ -343,7 +345,7 @@ void flush_switched_cplbs(void)
>
>  }
>
> -void set_mask_dcplbs(unsigned long *masks)
> +void set_mask_dcplbs(unsigned long *masks, unsigned int cpu)
>  {
>        int i;
>        unsigned long addr = (unsigned long)masks;
> @@ -351,12 +353,12 @@ void set_mask_dcplbs(unsigned long *masks)
>        unsigned long flags;
>
>        if (!masks) {
> -               current_rwx_mask = masks;
> +               current_rwx_mask[cpu] = masks;
>                return;
>        }
>
>        local_irq_save(flags);
> -       current_rwx_mask = masks;
> +       current_rwx_mask[cpu] = masks;
>
>        d_data = CPLB_SUPV_WR | CPLB_VALID | CPLB_DIRTY | PAGE_SIZE_4KB;
>  #ifdef CONFIG_BFIN_DCACHE
> @@ -368,8 +370,8 @@ void set_mask_dcplbs(unsigned long *masks)
>
>        disable_dcplb();
>        for (i = first_mask_dcplb; i < first_switched_dcplb; i++) {
> -               dcplb_tbl[i].addr = addr;
> -               dcplb_tbl[i].data = d_data;
> +               dcplb_tbl[cpu][i].addr = addr;
> +               dcplb_tbl[cpu][i].data = d_data;
>                bfin_write32(DCPLB_DATA0 + i * 4, d_data);
>                bfin_write32(DCPLB_ADDR0 + i * 4, addr);
>                addr += PAGE_SIZE;
> diff --git a/arch/blackfin/kernel/cplb-nompu/cacheinit.c b/arch/blackfin/kernel/cplb-nompu/cacheinit.c
> index bd08315..3a385ae 100644
> --- a/arch/blackfin/kernel/cplb-nompu/cacheinit.c
> +++ b/arch/blackfin/kernel/cplb-nompu/cacheinit.c
> @@ -25,9 +25,9 @@
>  #include <asm/cplbinit.h>
>
>  #if defined(CONFIG_BFIN_ICACHE)
> -void __init bfin_icache_init(void)
> +void __cpuinit bfin_icache_init(u_long icplb[])
>  {
> -       unsigned long *table = icplb_table;
> +       unsigned long *table = icplb;
>        unsigned long ctrl;
>        int i;
>
> @@ -47,9 +47,9 @@ void __init bfin_icache_init(void)
>  #endif
>
>  #if defined(CONFIG_BFIN_DCACHE)
> -void __init bfin_dcache_init(void)
> +void __cpuinit bfin_dcache_init(u_long dcplb[])
>  {
> -       unsigned long *table = dcplb_table;
> +       unsigned long *table = dcplb;
>        unsigned long ctrl;
>        int i;
>
> @@ -64,6 +64,7 @@ void __init bfin_dcache_init(void)
>        ctrl = bfin_read_DMEM_CONTROL();
>        ctrl |= DMEM_CNTR;
>        bfin_write_DMEM_CONTROL(ctrl);
> +
>        SSYNC();
>  }
>  #endif
> diff --git a/arch/blackfin/kernel/cplb-nompu/cplbinfo.c b/arch/blackfin/kernel/cplb-nompu/cplbinfo.c
> index 1e74f0b..3f00809 100644
> --- a/arch/blackfin/kernel/cplb-nompu/cplbinfo.c
> +++ b/arch/blackfin/kernel/cplb-nompu/cplbinfo.c
> @@ -68,22 +68,22 @@ static int cplb_find_entry(unsigned long *cplb_addr,
>        return -1;
>  }
>
> -static char *cplb_print_entry(char *buf, int type)
> +static char *cplb_print_entry(char *buf, int type, unsigned int cpu)
>  {
> -       unsigned long *p_addr = dpdt_table;
> -       unsigned long *p_data = dpdt_table + 1;
> -       unsigned long *p_icount = dpdt_swapcount_table;
> -       unsigned long *p_ocount = dpdt_swapcount_table + 1;
> +       unsigned long *p_addr = dpdt_tables[cpu];
> +       unsigned long *p_data = dpdt_tables[cpu] + 1;
> +       unsigned long *p_icount = dpdt_swapcount_tables[cpu];
> +       unsigned long *p_ocount = dpdt_swapcount_tables[cpu] + 1;
>        unsigned long *cplb_addr = (unsigned long *)DCPLB_ADDR0;
>        unsigned long *cplb_data = (unsigned long *)DCPLB_DATA0;
>        int entry = 0, used_cplb = 0;
>
>        if (type == CPLB_I) {
>                buf += sprintf(buf, "Instruction CPLB entry:\n");
> -               p_addr = ipdt_table;
> -               p_data = ipdt_table + 1;
> -               p_icount = ipdt_swapcount_table;
> -               p_ocount = ipdt_swapcount_table + 1;
> +               p_addr = ipdt_tables[cpu];
> +               p_data = ipdt_tables[cpu] + 1;
> +               p_icount = ipdt_swapcount_tables[cpu];
> +               p_ocount = ipdt_swapcount_tables[cpu] + 1;
>                cplb_addr = (unsigned long *)ICPLB_ADDR0;
>                cplb_data = (unsigned long *)ICPLB_DATA0;
>        } else
> @@ -134,24 +134,24 @@ static char *cplb_print_entry(char *buf, int type)
>        return buf;
>  }
>
> -static int cplbinfo_proc_output(char *buf)
> +static int cplbinfo_proc_output(char *buf, void *data)
>  {
> +       unsigned int cpu = (unsigned int)data;
>        char *p;
>
>        p = buf;
>
> -       p += sprintf(p, "------------------ CPLB Information ------------------\n\n");
> +       p += sprintf(p, "------------- CPLB Information on CPU%u--------------\n\n", cpu);
>
>        if (bfin_read_IMEM_CONTROL() & ENICPLB)
> -               p = cplb_print_entry(p, CPLB_I);
> +               p = cplb_print_entry(p, CPLB_I, cpu);
>        else
>                p += sprintf(p, "Instruction CPLB is disabled.\n\n");
>
>        if (bfin_read_DMEM_CONTROL() & ENDCPLB)
> -               p = cplb_print_entry(p, CPLB_D);
> +               p = cplb_print_entry(p, CPLB_D, cpu);
>        else
>                p += sprintf(p, "Data CPLB is disabled.\n");
> -
>        return p - buf;
>  }
>
> @@ -160,7 +160,7 @@ static int cplbinfo_read_proc(char *page, char **start, off_t off,
>  {
>        int len;
>
> -       len = cplbinfo_proc_output(page);
> +       len = cplbinfo_proc_output(page, data);
>        if (len <= off + count)
>                *eof = 1;
>        *start = page + off;
> @@ -174,20 +174,33 @@ static int cplbinfo_read_proc(char *page, char **start, off_t off,
>
>  static int __init cplbinfo_init(void)
>  {
> -       struct proc_dir_entry *entry;
> +       struct proc_dir_entry *parent, *entry;
> +       unsigned int cpu;
> +       unsigned char str[10];
> +
> +       parent = proc_mkdir("cplbinfo", NULL);
>
> -       entry = create_proc_entry("cplbinfo", 0, NULL);
> -       if (!entry)
> -               return -ENOMEM;
> +       for_each_online_cpu(cpu) {
> +               sprintf(str, "cpu%u", cpu);
> +               entry = create_proc_entry(str, 0, parent);
> +               if (!entry)
> +                       return -ENOMEM;
>
> -       entry->read_proc = cplbinfo_read_proc;
> -       entry->data = NULL;
> +               entry->read_proc = cplbinfo_read_proc;
> +               entry->data = (void *)cpu;
> +       }
>
>        return 0;
>  }
>
>  static void __exit cplbinfo_exit(void)
>  {
> +       unsigned int cpu;
> +       unsigned char str[20];
> +       for_each_online_cpu(cpu) {
> +               sprintf(str, "cplbinfo/cpu%u", cpu);
> +               remove_proc_entry(str, NULL);
> +       }
>        remove_proc_entry("cplbinfo", NULL);
>  }
>
> diff --git a/arch/blackfin/kernel/cplb-nompu/cplbinit.c b/arch/blackfin/kernel/cplb-nompu/cplbinit.c
> index 2debc90..8966c70 100644
> --- a/arch/blackfin/kernel/cplb-nompu/cplbinit.c
> +++ b/arch/blackfin/kernel/cplb-nompu/cplbinit.c
> @@ -27,46 +27,20 @@
>  #include <asm/cplb.h>
>  #include <asm/cplbinit.h>
>
> -#define CPLB_MEM CONFIG_MAX_MEM_SIZE
> -
> -/*
> -* Number of required data CPLB switchtable entries
> -* MEMSIZE / 4 (we mostly install 4M page size CPLBs
> -* approx 16 for smaller 1MB page size CPLBs for allignment purposes
> -* 1 for L1 Data Memory
> -* possibly 1 for L2 Data Memory
> -* 1 for CONFIG_DEBUG_HUNT_FOR_ZERO
> -* 1 for ASYNC Memory
> -*/
> -#define MAX_SWITCH_D_CPLBS (((CPLB_MEM / 4) + 16 + 1 + 1 + 1 \
> -                                + ASYNC_MEMORY_CPLB_COVERAGE) * 2)
> -
> -/*
> -* Number of required instruction CPLB switchtable entries
> -* MEMSIZE / 4 (we mostly install 4M page size CPLBs
> -* approx 12 for smaller 1MB page size CPLBs for allignment purposes
> -* 1 for L1 Instruction Memory
> -* possibly 1 for L2 Instruction Memory
> -* 1 for CONFIG_DEBUG_HUNT_FOR_ZERO
> -*/
> -#define MAX_SWITCH_I_CPLBS (((CPLB_MEM / 4) + 12 + 1 + 1 + 1) * 2)
> -
> -
> -u_long icplb_table[MAX_CPLBS + 1];
> -u_long dcplb_table[MAX_CPLBS + 1];
> +u_long icplb_tables[NR_CPUS][CPLB_TBL_ENTRIES+1];
> +u_long dcplb_tables[NR_CPUS][CPLB_TBL_ENTRIES+1];
>
>  #ifdef CONFIG_CPLB_SWITCH_TAB_L1
> -# define PDT_ATTR __attribute__((l1_data))
> +#define PDT_ATTR __attribute__((l1_data))
>  #else
> -# define PDT_ATTR
> +#define PDT_ATTR
>  #endif
>
> -u_long ipdt_table[MAX_SWITCH_I_CPLBS + 1] PDT_ATTR;
> -u_long dpdt_table[MAX_SWITCH_D_CPLBS + 1] PDT_ATTR;
> -
> +u_long ipdt_tables[NR_CPUS][MAX_SWITCH_I_CPLBS+1] PDT_ATTR;
> +u_long dpdt_tables[NR_CPUS][MAX_SWITCH_D_CPLBS+1] PDT_ATTR;
>  #ifdef CONFIG_CPLB_INFO
> -u_long ipdt_swapcount_table[MAX_SWITCH_I_CPLBS] PDT_ATTR;
> -u_long dpdt_swapcount_table[MAX_SWITCH_D_CPLBS] PDT_ATTR;
> +u_long ipdt_swapcount_tables[NR_CPUS][MAX_SWITCH_I_CPLBS] PDT_ATTR;
> +u_long dpdt_swapcount_tables[NR_CPUS][MAX_SWITCH_D_CPLBS] PDT_ATTR;
>  #endif
>
>  struct s_cplb {
> @@ -93,8 +67,8 @@ static struct cplb_desc cplb_data[] = {
>                .name = "Zero Pointer Guard Page",
>        },
>        {
> -               .start = L1_CODE_START,
> -               .end = L1_CODE_START + L1_CODE_LENGTH,
> +               .start = 0,     /* dyanmic */
> +               .end = 0,       /* dynamic */
>                .psize = SIZE_4M,
>                .attr = INITIAL_T | SWITCH_T | I_CPLB,
>                .i_conf = L1_IMEMORY,
> @@ -103,8 +77,8 @@ static struct cplb_desc cplb_data[] = {
>                .name = "L1 I-Memory",
>        },
>        {
> -               .start = L1_DATA_A_START,
> -               .end = L1_DATA_B_START + L1_DATA_B_LENGTH,
> +               .start = 0,     /* dynamic */
> +               .end = 0,       /* dynamic */
>                .psize = SIZE_4M,
>                .attr = INITIAL_T | SWITCH_T | D_CPLB,
>                .i_conf = 0,
> @@ -117,6 +91,16 @@ static struct cplb_desc cplb_data[] = {
>                .name = "L1 D-Memory",
>        },
>        {
> +               .start = L2_START,
> +               .end = L2_START + L2_LENGTH,
> +               .psize = SIZE_1M,
> +               .attr = L2_ATTR,
> +               .i_conf = L2_IMEMORY,
> +               .d_conf = L2_DMEMORY,
> +               .valid = (L2_LENGTH > 0),
> +               .name = "L2 Memory",
> +       },
> +       {
>                .start = 0,
>                .end = 0,  /* dynamic */
>                .psize = 0,
> @@ -165,16 +149,6 @@ static struct cplb_desc cplb_data[] = {
>                .name = "Asynchronous Memory Banks",
>        },
>        {
> -               .start = L2_START,
> -               .end = L2_START + L2_LENGTH,
> -               .psize = SIZE_1M,
> -               .attr = SWITCH_T | I_CPLB | D_CPLB,
> -               .i_conf = L2_IMEMORY,
> -               .d_conf = L2_DMEMORY,
> -               .valid = (L2_LENGTH > 0),
> -               .name = "L2 Memory",
> -       },
> -       {
>                .start = BOOT_ROM_START,
>                .end = BOOT_ROM_START + BOOT_ROM_LENGTH,
>                .psize = SIZE_1M,
> @@ -310,7 +284,7 @@ __fill_data_cplbtab(struct cplb_tab *t, int i, u32 a_start, u32 a_end)
>        }
>  }
>
> -void __init generate_cplb_tables(void)
> +void __init generate_cplb_tables_cpu(unsigned int cpu)
>  {
>
>        u16 i, j, process;
> @@ -322,8 +296,8 @@ void __init generate_cplb_tables(void)
>
>        printk(KERN_INFO "NOMPU: setting up cplb tables for global access\n");
>
> -       cplb.init_i.size = MAX_CPLBS;
> -       cplb.init_d.size = MAX_CPLBS;
> +       cplb.init_i.size = CPLB_TBL_ENTRIES;
> +       cplb.init_d.size = CPLB_TBL_ENTRIES;
>        cplb.switch_i.size = MAX_SWITCH_I_CPLBS;
>        cplb.switch_d.size = MAX_SWITCH_D_CPLBS;
>
> @@ -332,11 +306,15 @@ void __init generate_cplb_tables(void)
>        cplb.switch_i.pos = 0;
>        cplb.switch_d.pos = 0;
>
> -       cplb.init_i.tab = icplb_table;
> -       cplb.init_d.tab = dcplb_table;
> -       cplb.switch_i.tab = ipdt_table;
> -       cplb.switch_d.tab = dpdt_table;
> +       cplb.init_i.tab = icplb_tables[cpu];
> +       cplb.init_d.tab = dcplb_tables[cpu];
> +       cplb.switch_i.tab = ipdt_tables[cpu];
> +       cplb.switch_d.tab = dpdt_tables[cpu];
>
> +       cplb_data[L1I_MEM].start = get_l1_code_start_cpu(cpu);
> +       cplb_data[L1I_MEM].end = cplb_data[L1I_MEM].start + L1_CODE_LENGTH;
> +       cplb_data[L1D_MEM].start = get_l1_data_a_start_cpu(cpu);
> +       cplb_data[L1D_MEM].end = get_l1_data_b_start_cpu(cpu) + L1_DATA_B_LENGTH;
>        cplb_data[SDRAM_KERN].end = memory_end;
>
>  #ifdef CONFIG_MTD_UCLINUX
> @@ -459,6 +437,5 @@ void __init generate_cplb_tables(void)
>        cplb.switch_d.tab[cplb.switch_d.pos] = -1;
>
>  }
> -
>  #endif
>
> diff --git a/arch/blackfin/kernel/cplb-nompu/cplbmgr.S b/arch/blackfin/kernel/cplb-nompu/cplbmgr.S
> index f5cf3ac..985f3fc 100644
> --- a/arch/blackfin/kernel/cplb-nompu/cplbmgr.S
> +++ b/arch/blackfin/kernel/cplb-nompu/cplbmgr.S
> @@ -52,6 +52,7 @@
>  #include <linux/linkage.h>
>  #include <asm/blackfin.h>
>  #include <asm/cplb.h>
> +#include <asm/asm-offsets.h>
>
>  #ifdef CONFIG_EXCPT_IRQ_SYSC_L1
>  .section .l1.text
> @@ -164,10 +165,9 @@ ENTRY(_cplb_mgr)
>  .Lifound_victim:
>  #ifdef CONFIG_CPLB_INFO
>        R7 = [P0 - 0x104];
> -       P2.L = _ipdt_table;
> -       P2.H = _ipdt_table;
> -       P3.L = _ipdt_swapcount_table;
> -       P3.H = _ipdt_swapcount_table;
> +       GET_PDA(P2, R2);
> +       P3 = [P2 + PDA_IPDT_SWAPCOUNT];
> +       P2 = [P2 + PDA_IPDT];
>        P3 += -4;
>  .Licount:
>        R2 = [P2];      /* address from config table */
> @@ -208,11 +208,10 @@ ENTRY(_cplb_mgr)
>         * range.
>         */
>
> -       P2.L = _ipdt_table;
> -       P2.H = _ipdt_table;
> +       GET_PDA(P3, R0);
> +       P2 = [P3 + PDA_IPDT];
>  #ifdef CONFIG_CPLB_INFO
> -       P3.L = _ipdt_swapcount_table;
> -       P3.H = _ipdt_swapcount_table;
> +       P3 = [P3 + PDA_IPDT_SWAPCOUNT];
>        P3 += -8;
>  #endif
>        P0.L = _page_size_table;
> @@ -469,10 +468,9 @@ ENTRY(_cplb_mgr)
>
>  #ifdef CONFIG_CPLB_INFO
>        R7 = [P0 - 0x104];
> -       P2.L = _dpdt_table;
> -       P2.H = _dpdt_table;
> -       P3.L = _dpdt_swapcount_table;
> -       P3.H = _dpdt_swapcount_table;
> +       GET_PDA(P2, R2);
> +       P3 = [P2 + PDA_DPDT_SWAPCOUNT];
> +       P2 = [P2 + PDA_DPDT];
>        P3 += -4;
>  .Ldicount:
>        R2 = [P2];
> @@ -541,11 +539,10 @@ ENTRY(_cplb_mgr)
>
>        R0 = I0;                /* Our faulting address */
>
> -       P2.L = _dpdt_table;
> -       P2.H = _dpdt_table;
> +       GET_PDA(P3, R1);
> +       P2 = [P3 + PDA_DPDT];
>  #ifdef CONFIG_CPLB_INFO
> -       P3.L = _dpdt_swapcount_table;
> -       P3.H = _dpdt_swapcount_table;
> +       P3 = [P3 + PDA_DPDT_SWAPCOUNT];
>        P3 += -8;
>  #endif
>
> --
> 1.5.6.3
>

^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: [PATCH 4/5] Blackfin arch: SMP supporting patchset: Blackfin kernel and memory management code
       [not found] ` <1226999108-13839-5-git-send-email-cooloney@kernel.org>
@ 2008-11-19  7:46   ` Bryan Wu
  0 siblings, 0 replies; 8+ messages in thread
From: Bryan Wu @ 2008-11-19  7:46 UTC (permalink / raw)
  To: torvalds, akpm, mingo
  Cc: linux-kernel, Graf Yang, Mike Frysinger, Bryan Wu, linux-arch

Cc, linux-arch
-Bryan

On Tue, Nov 18, 2008 at 5:05 PM, Bryan Wu <cooloney@kernel.org> wrote:
> From: Graf Yang <graf.yang@analog.com>
>
> Blackfin dual core BF561 processor can support SMP like features.
> https://docs.blackfin.uclinux.org/doku.php?id=linux-kernel:smp-like
>
> In this patch, we provide SMP extend to Blackfin kernel and memory management code
>
> Singed-off-by: Graf Yang <graf.yang@analog.com>
> Signed-off-by: Mike Frysinger <vapier.adi@gmail.com>
> Signed-off-by: Bryan Wu <cooloney@kernel.org>
> ---
>  arch/blackfin/kernel/asm-offsets.c |   29 +++
>  arch/blackfin/kernel/bfin_ksyms.c  |   34 ++++
>  arch/blackfin/kernel/entry.S       |    1 +
>  arch/blackfin/kernel/irqchip.c     |   24 ++--
>  arch/blackfin/kernel/kgdb.c        |    4 +-
>  arch/blackfin/kernel/module.c      |   13 ++-
>  arch/blackfin/kernel/process.c     |   23 ++-
>  arch/blackfin/kernel/ptrace.c      |    8 +-
>  arch/blackfin/kernel/reboot.c      |   24 ++-
>  arch/blackfin/kernel/setup.c       |  163 ++++++++++++------
>  arch/blackfin/kernel/time.c        |  114 +++++++++----
>  arch/blackfin/kernel/traps.c       |   56 +++----
>  arch/blackfin/mm/init.c            |   60 +++++--
>  arch/blackfin/mm/sram-alloc.c      |  336 +++++++++++++++++++++---------------
>  14 files changed, 580 insertions(+), 309 deletions(-)
>
> diff --git a/arch/blackfin/kernel/asm-offsets.c b/arch/blackfin/kernel/asm-offsets.c
> index 9bb85dd..b5df945 100644
> --- a/arch/blackfin/kernel/asm-offsets.c
> +++ b/arch/blackfin/kernel/asm-offsets.c
> @@ -56,6 +56,9 @@ int main(void)
>        /* offsets into the thread struct */
>        DEFINE(THREAD_KSP, offsetof(struct thread_struct, ksp));
>        DEFINE(THREAD_USP, offsetof(struct thread_struct, usp));
> +       DEFINE(THREAD_SR, offsetof(struct thread_struct, seqstat));
> +       DEFINE(PT_SR, offsetof(struct thread_struct, seqstat));
> +       DEFINE(THREAD_ESP0, offsetof(struct thread_struct, esp0));
>        DEFINE(THREAD_PC, offsetof(struct thread_struct, pc));
>        DEFINE(KERNEL_STACK_SIZE, THREAD_SIZE);
>
> @@ -128,5 +131,31 @@ int main(void)
>        DEFINE(SIGSEGV, SIGSEGV);
>        DEFINE(SIGTRAP, SIGTRAP);
>
> +       /* PDA management (in L1 scratchpad) */
> +       DEFINE(PDA_SYSCFG, offsetof(struct blackfin_pda, syscfg));
> +#ifdef CONFIG_SMP
> +       DEFINE(PDA_IRQFLAGS, offsetof(struct blackfin_pda, imask));
> +#endif
> +       DEFINE(PDA_IPDT, offsetof(struct blackfin_pda, ipdt));
> +       DEFINE(PDA_IPDT_SWAPCOUNT, offsetof(struct blackfin_pda, ipdt_swapcount));
> +       DEFINE(PDA_DPDT, offsetof(struct blackfin_pda, dpdt));
> +       DEFINE(PDA_DPDT_SWAPCOUNT, offsetof(struct blackfin_pda, dpdt_swapcount));
> +       DEFINE(PDA_EXIPTR, offsetof(struct blackfin_pda, ex_iptr));
> +       DEFINE(PDA_EXOPTR, offsetof(struct blackfin_pda, ex_optr));
> +       DEFINE(PDA_EXBUF, offsetof(struct blackfin_pda, ex_buf));
> +       DEFINE(PDA_EXIMASK, offsetof(struct blackfin_pda, ex_imask));
> +       DEFINE(PDA_EXSTACK, offsetof(struct blackfin_pda, ex_stack));
> +#ifdef ANOMALY_05000261
> +       DEFINE(PDA_LFRETX, offsetof(struct blackfin_pda, last_cplb_fault_retx));
> +#endif
> +       DEFINE(PDA_DCPLB, offsetof(struct blackfin_pda, dcplb_fault_addr));
> +       DEFINE(PDA_ICPLB, offsetof(struct blackfin_pda, icplb_fault_addr));
> +       DEFINE(PDA_RETX, offsetof(struct blackfin_pda, retx));
> +       DEFINE(PDA_SEQSTAT, offsetof(struct blackfin_pda, seqstat));
> +#ifdef CONFIG_SMP
> +       /* Inter-core lock (in L2 SRAM) */
> +       DEFINE(SIZEOF_CORELOCK, sizeof(struct corelock_slot));
> +#endif
> +
>        return 0;
>  }
> diff --git a/arch/blackfin/kernel/bfin_ksyms.c b/arch/blackfin/kernel/bfin_ksyms.c
> index b66f1d4..763c315 100644
> --- a/arch/blackfin/kernel/bfin_ksyms.c
> +++ b/arch/blackfin/kernel/bfin_ksyms.c
> @@ -68,3 +68,37 @@ EXPORT_SYMBOL(insw_8);
>  EXPORT_SYMBOL(outsl);
>  EXPORT_SYMBOL(insl);
>  EXPORT_SYMBOL(insl_16);
> +
> +#ifdef CONFIG_SMP
> +EXPORT_SYMBOL(__raw_atomic_update_asm);
> +EXPORT_SYMBOL(__raw_atomic_clear_asm);
> +EXPORT_SYMBOL(__raw_atomic_set_asm);
> +EXPORT_SYMBOL(__raw_atomic_xor_asm);
> +EXPORT_SYMBOL(__raw_atomic_test_asm);
> +EXPORT_SYMBOL(__raw_xchg_1_asm);
> +EXPORT_SYMBOL(__raw_xchg_2_asm);
> +EXPORT_SYMBOL(__raw_xchg_4_asm);
> +EXPORT_SYMBOL(__raw_cmpxchg_1_asm);
> +EXPORT_SYMBOL(__raw_cmpxchg_2_asm);
> +EXPORT_SYMBOL(__raw_cmpxchg_4_asm);
> +EXPORT_SYMBOL(__raw_spin_is_locked_asm);
> +EXPORT_SYMBOL(__raw_spin_lock_asm);
> +EXPORT_SYMBOL(__raw_spin_trylock_asm);
> +EXPORT_SYMBOL(__raw_spin_unlock_asm);
> +EXPORT_SYMBOL(__raw_read_lock_asm);
> +EXPORT_SYMBOL(__raw_read_trylock_asm);
> +EXPORT_SYMBOL(__raw_read_unlock_asm);
> +EXPORT_SYMBOL(__raw_write_lock_asm);
> +EXPORT_SYMBOL(__raw_write_trylock_asm);
> +EXPORT_SYMBOL(__raw_write_unlock_asm);
> +EXPORT_SYMBOL(__raw_bit_set_asm);
> +EXPORT_SYMBOL(__raw_bit_clear_asm);
> +EXPORT_SYMBOL(__raw_bit_toggle_asm);
> +EXPORT_SYMBOL(__raw_bit_test_asm);
> +EXPORT_SYMBOL(__raw_bit_test_set_asm);
> +EXPORT_SYMBOL(__raw_bit_test_clear_asm);
> +EXPORT_SYMBOL(__raw_bit_test_toggle_asm);
> +EXPORT_SYMBOL(__raw_uncached_fetch_asm);
> +EXPORT_SYMBOL(__raw_smp_mark_barrier_asm);
> +EXPORT_SYMBOL(__raw_smp_check_barrier_asm);
> +#endif
> diff --git a/arch/blackfin/kernel/entry.S b/arch/blackfin/kernel/entry.S
> index faea88e..c0c3fe8 100644
> --- a/arch/blackfin/kernel/entry.S
> +++ b/arch/blackfin/kernel/entry.S
> @@ -30,6 +30,7 @@
>  #include <linux/linkage.h>
>  #include <asm/thread_info.h>
>  #include <asm/errno.h>
> +#include <asm/blackfin.h>
>  #include <asm/asm-offsets.h>
>
>  #include <asm/context.S>
> diff --git a/arch/blackfin/kernel/irqchip.c b/arch/blackfin/kernel/irqchip.c
> index 07402f5..9eebb78 100644
> --- a/arch/blackfin/kernel/irqchip.c
> +++ b/arch/blackfin/kernel/irqchip.c
> @@ -36,7 +36,7 @@
>  #include <linux/irq.h>
>  #include <asm/trace.h>
>
> -static unsigned long irq_err_count;
> +static atomic_t irq_err_count;
>  static spinlock_t irq_controller_lock;
>
>  /*
> @@ -48,7 +48,7 @@ void dummy_mask_unmask_irq(unsigned int irq)
>
>  void ack_bad_irq(unsigned int irq)
>  {
> -       irq_err_count += 1;
> +       atomic_inc(&irq_err_count);
>        printk(KERN_ERR "IRQ: spurious interrupt %d\n", irq);
>  }
>  EXPORT_SYMBOL(ack_bad_irq);
> @@ -72,7 +72,7 @@ static struct irq_desc bad_irq_desc = {
>
>  int show_interrupts(struct seq_file *p, void *v)
>  {
> -       int i = *(loff_t *) v;
> +       int i = *(loff_t *) v, j;
>        struct irqaction *action;
>        unsigned long flags;
>
> @@ -80,19 +80,20 @@ int show_interrupts(struct seq_file *p, void *v)
>                spin_lock_irqsave(&irq_desc[i].lock, flags);
>                action = irq_desc[i].action;
>                if (!action)
> -                       goto unlock;
> -
> -               seq_printf(p, "%3d: %10u ", i, kstat_irqs(i));
> +                       goto skip;
> +               seq_printf(p, "%3d: ", i);
> +               for_each_online_cpu(j)
> +                       seq_printf(p, "%10u ", kstat_cpu(j).irqs[i]);
> +               seq_printf(p, " %8s", irq_desc[i].chip->name);
>                seq_printf(p, "  %s", action->name);
>                for (action = action->next; action; action = action->next)
> -                       seq_printf(p, ", %s", action->name);
> +                       seq_printf(p, "  %s", action->name);
>
>                seq_putc(p, '\n');
> - unlock:
> + skip:
>                spin_unlock_irqrestore(&irq_desc[i].lock, flags);
> -       } else if (i == NR_IRQS) {
> -               seq_printf(p, "Err: %10lu\n", irq_err_count);
> -       }
> +       } else if (i == NR_IRQS)
> +               seq_printf(p, "Err: %10u\n",  atomic_read(&irq_err_count));
>        return 0;
>  }
>
> @@ -101,7 +102,6 @@ int show_interrupts(struct seq_file *p, void *v)
>  * come via this function.  Instead, they should provide their
>  * own 'handler'
>  */
> -
>  #ifdef CONFIG_DO_IRQ_L1
>  __attribute__((l1_text))
>  #endif
> diff --git a/arch/blackfin/kernel/kgdb.c b/arch/blackfin/kernel/kgdb.c
> index b795a20..ab40221 100644
> --- a/arch/blackfin/kernel/kgdb.c
> +++ b/arch/blackfin/kernel/kgdb.c
> @@ -363,12 +363,12 @@ void kgdb_passive_cpu_callback(void *info)
>
>  void kgdb_roundup_cpus(unsigned long flags)
>  {
> -       smp_call_function(kgdb_passive_cpu_callback, NULL, 0, 0);
> +       smp_call_function(kgdb_passive_cpu_callback, NULL, 0);
>  }
>
>  void kgdb_roundup_cpu(int cpu, unsigned long flags)
>  {
> -       smp_call_function_single(cpu, kgdb_passive_cpu_callback, NULL, 0, 0);
> +       smp_call_function_single(cpu, kgdb_passive_cpu_callback, NULL, 0);
>  }
>  #endif
>
> diff --git a/arch/blackfin/kernel/module.c b/arch/blackfin/kernel/module.c
> index e1bebc8..2e14cad 100644
> --- a/arch/blackfin/kernel/module.c
> +++ b/arch/blackfin/kernel/module.c
> @@ -343,7 +343,13 @@ apply_relocate_add(Elf_Shdr * sechdrs, const char *strtab,
>                pr_debug("location is %x, value is %x type is %d \n",
>                         (unsigned int) location32, value,
>                         ELF32_R_TYPE(rel[i].r_info));
> -
> +#ifdef CONFIG_SMP
> +               if ((unsigned long)location16 >= COREB_L1_DATA_A_START) {
> +                       printk(KERN_ERR "module %s: cannot relocate in L1: %u (SMP kernel)",
> +                                      mod->name, ELF32_R_TYPE(rel[i].r_info));
> +                       return -ENOEXEC;
> +               }
> +#endif
>                switch (ELF32_R_TYPE(rel[i].r_info)) {
>
>                case R_pcrel24:
> @@ -436,6 +442,7 @@ module_finalize(const Elf_Ehdr * hdr,
>  {
>        unsigned int i, strindex = 0, symindex = 0;
>        char *secstrings;
> +       long err = 0;
>
>        secstrings = (void *)hdr + sechdrs[hdr->e_shstrndx].sh_offset;
>
> @@ -460,8 +467,10 @@ module_finalize(const Elf_Ehdr * hdr,
>                    (strcmp(".rela.l1.text", secstrings + sechdrs[i].sh_name) == 0) ||
>                    ((strcmp(".rela.text", secstrings + sechdrs[i].sh_name) == 0) &&
>                        (hdr->e_flags & (EF_BFIN_CODE_IN_L1|EF_BFIN_CODE_IN_L2))))) {
> -                       apply_relocate_add((Elf_Shdr *) sechdrs, strtab,
> +                       err = apply_relocate_add((Elf_Shdr *) sechdrs, strtab,
>                                           symindex, i, mod);
> +                       if (err < 0)
> +                               return -ENOEXEC;
>                }
>        }
>        return 0;
> diff --git a/arch/blackfin/kernel/process.c b/arch/blackfin/kernel/process.c
> index 326e301..4359ea2 100644
> --- a/arch/blackfin/kernel/process.c
> +++ b/arch/blackfin/kernel/process.c
> @@ -171,6 +171,13 @@ asmlinkage int bfin_clone(struct pt_regs *regs)
>        unsigned long clone_flags;
>        unsigned long newsp;
>
> +#ifdef __ARCH_SYNC_CORE_DCACHE
> +       if (current->rt.nr_cpus_allowed == num_possible_cpus()) {
> +               current->cpus_allowed = cpumask_of_cpu(smp_processor_id());
> +               current->rt.nr_cpus_allowed = 1;
> +       }
> +#endif
> +
>        /* syscall2 puts clone_flags in r0 and usp in r1 */
>        clone_flags = regs->r0;
>        newsp = regs->r1;
> @@ -338,22 +345,22 @@ int _access_ok(unsigned long addr, unsigned long size)
>        if (addr >= (unsigned long)__init_begin &&
>            addr + size <= (unsigned long)__init_end)
>                return 1;
> -       if (addr >= L1_SCRATCH_START
> -           && addr + size <= L1_SCRATCH_START + L1_SCRATCH_LENGTH)
> +       if (addr >= get_l1_scratch_start()
> +           && addr + size <= get_l1_scratch_start() + L1_SCRATCH_LENGTH)
>                return 1;
>  #if L1_CODE_LENGTH != 0
> -       if (addr >= L1_CODE_START + (_etext_l1 - _stext_l1)
> -           && addr + size <= L1_CODE_START + L1_CODE_LENGTH)
> +       if (addr >= get_l1_code_start() + (_etext_l1 - _stext_l1)
> +           && addr + size <= get_l1_code_start() + L1_CODE_LENGTH)
>                return 1;
>  #endif
>  #if L1_DATA_A_LENGTH != 0
> -       if (addr >= L1_DATA_A_START + (_ebss_l1 - _sdata_l1)
> -           && addr + size <= L1_DATA_A_START + L1_DATA_A_LENGTH)
> +       if (addr >= get_l1_data_a_start() + (_ebss_l1 - _sdata_l1)
> +           && addr + size <= get_l1_data_a_start() + L1_DATA_A_LENGTH)
>                return 1;
>  #endif
>  #if L1_DATA_B_LENGTH != 0
> -       if (addr >= L1_DATA_B_START + (_ebss_b_l1 - _sdata_b_l1)
> -           && addr + size <= L1_DATA_B_START + L1_DATA_B_LENGTH)
> +       if (addr >= get_l1_data_b_start() + (_ebss_b_l1 - _sdata_b_l1)
> +           && addr + size <= get_l1_data_b_start() + L1_DATA_B_LENGTH)
>                return 1;
>  #endif
>  #if L2_LENGTH != 0
> diff --git a/arch/blackfin/kernel/ptrace.c b/arch/blackfin/kernel/ptrace.c
> index 140bf00..4de44f3 100644
> --- a/arch/blackfin/kernel/ptrace.c
> +++ b/arch/blackfin/kernel/ptrace.c
> @@ -220,8 +220,8 @@ long arch_ptrace(struct task_struct *child, long request, long addr, long data)
>                                break;
>                        pr_debug("ptrace: user address is valid\n");
>
> -                       if (L1_CODE_LENGTH != 0 && addr >= L1_CODE_START
> -                           && addr + sizeof(tmp) <= L1_CODE_START + L1_CODE_LENGTH) {
> +                       if (L1_CODE_LENGTH != 0 && addr >= get_l1_code_start()
> +                           && addr + sizeof(tmp) <= get_l1_code_start() + L1_CODE_LENGTH) {
>                                safe_dma_memcpy (&tmp, (const void *)(addr), sizeof(tmp));
>                                copied = sizeof(tmp);
>
> @@ -300,8 +300,8 @@ long arch_ptrace(struct task_struct *child, long request, long addr, long data)
>                                break;
>                        pr_debug("ptrace: user address is valid\n");
>
> -                       if (L1_CODE_LENGTH != 0 && addr >= L1_CODE_START
> -                           && addr + sizeof(data) <= L1_CODE_START + L1_CODE_LENGTH) {
> +                       if (L1_CODE_LENGTH != 0 && addr >= get_l1_code_start()
> +                           && addr + sizeof(data) <= get_l1_code_start() + L1_CODE_LENGTH) {
>                                safe_dma_memcpy ((void *)(addr), &data, sizeof(data));
>                                copied = sizeof(data);
>
> diff --git a/arch/blackfin/kernel/reboot.c b/arch/blackfin/kernel/reboot.c
> index ae97ca4..eeee8cb 100644
> --- a/arch/blackfin/kernel/reboot.c
> +++ b/arch/blackfin/kernel/reboot.c
> @@ -21,7 +21,7 @@
>  * the core reset.
>  */
>  __attribute__((l1_text))
> -static void bfin_reset(void)
> +static void _bfin_reset(void)
>  {
>        /* Wait for completion of "system" events such as cache line
>         * line fills so that we avoid infinite stalls later on as
> @@ -66,6 +66,18 @@ static void bfin_reset(void)
>        }
>  }
>
> +static void bfin_reset(void)
> +{
> +       if (ANOMALY_05000353 || ANOMALY_05000386)
> +               _bfin_reset();
> +       else
> +               /* the bootrom checks to see how it was reset and will
> +                * automatically perform a software reset for us when
> +                * it starts executing boot
> +                */
> +               asm("raise 1;");
> +}
> +
>  __attribute__((weak))
>  void native_machine_restart(char *cmd)
>  {
> @@ -75,14 +87,10 @@ void machine_restart(char *cmd)
>  {
>        native_machine_restart(cmd);
>        local_irq_disable();
> -       if (ANOMALY_05000353 || ANOMALY_05000386)
> -               bfin_reset();
> +       if (smp_processor_id())
> +               smp_call_function((void *)bfin_reset, 0, 1);
>        else
> -               /* the bootrom checks to see how it was reset and will
> -                * automatically perform a software reset for us when
> -                * it starts executing boot
> -                */
> -               asm("raise 1;");
> +               bfin_reset();
>  }
>
>  __attribute__((weak))
> diff --git a/arch/blackfin/kernel/setup.c b/arch/blackfin/kernel/setup.c
> index 71a9a8c..c644d23 100644
> --- a/arch/blackfin/kernel/setup.c
> +++ b/arch/blackfin/kernel/setup.c
> @@ -26,11 +26,10 @@
>  #include <asm/blackfin.h>
>  #include <asm/cplbinit.h>
>  #include <asm/div64.h>
> +#include <asm/cpu.h>
>  #include <asm/fixed_code.h>
>  #include <asm/early_printk.h>
>
> -static DEFINE_PER_CPU(struct cpu, cpu_devices);
> -
>  u16 _bfin_swrst;
>  EXPORT_SYMBOL(_bfin_swrst);
>
> @@ -79,29 +78,76 @@ static struct change_member *change_point[2*BFIN_MEMMAP_MAX] __initdata;
>  static struct bfin_memmap_entry *overlap_list[BFIN_MEMMAP_MAX] __initdata;
>  static struct bfin_memmap_entry new_map[BFIN_MEMMAP_MAX] __initdata;
>
> -void __init bfin_cache_init(void)
> -{
> +DEFINE_PER_CPU(struct blackfin_cpudata, cpu_data);
> +
>  #if defined(CONFIG_BFIN_DCACHE) || defined(CONFIG_BFIN_ICACHE)
> -       generate_cplb_tables();
> +void __init generate_cplb_tables(void)
> +{
> +       unsigned int cpu;
> +
> +       /* Generate per-CPU I&D CPLB tables */
> +       for (cpu = 0; cpu < num_possible_cpus(); ++cpu)
> +               generate_cplb_tables_cpu(cpu);
> +}
>  #endif
>
> +void __cpuinit bfin_setup_caches(unsigned int cpu)
> +{
>  #ifdef CONFIG_BFIN_ICACHE
> -       bfin_icache_init();
> -       printk(KERN_INFO "Instruction Cache Enabled\n");
> +#ifdef CONFIG_MPU
> +       bfin_icache_init(icplb_tbl[cpu]);
> +#else
> +       bfin_icache_init(icplb_tables[cpu]);
> +#endif
>  #endif
>
>  #ifdef CONFIG_BFIN_DCACHE
> -       bfin_dcache_init();
> -       printk(KERN_INFO "Data Cache Enabled"
> +#ifdef CONFIG_MPU
> +       bfin_dcache_init(dcplb_tbl[cpu]);
> +#else
> +       bfin_dcache_init(dcplb_tables[cpu]);
> +#endif
> +#endif
> +
> +       /*
> +        * In cache coherence emulation mode, we need to have the
> +        * D-cache enabled before running any atomic operation which
> +        * might invove cache invalidation (i.e. spinlock, rwlock).
> +        * So printk's are deferred until then.
> +        */
> +#ifdef CONFIG_BFIN_ICACHE
> +       printk(KERN_INFO "Instruction Cache Enabled for CPU%u\n", cpu);
> +#endif
> +#ifdef CONFIG_BFIN_DCACHE
> +       printk(KERN_INFO "Data Cache Enabled for CPU%u"
>  # if defined CONFIG_BFIN_WB
>                " (write-back)"
>  # elif defined CONFIG_BFIN_WT
>                " (write-through)"
>  # endif
> -               "\n");
> +               "\n", cpu);
>  #endif
>  }
>
> +void __cpuinit bfin_setup_cpudata(unsigned int cpu)
> +{
> +       struct blackfin_cpudata *cpudata = &per_cpu(cpu_data, cpu);
> +
> +       cpudata->idle = current;
> +       cpudata->loops_per_jiffy = loops_per_jiffy;
> +       cpudata->cclk = get_cclk();
> +       cpudata->imemctl = bfin_read_IMEM_CONTROL();
> +       cpudata->dmemctl = bfin_read_DMEM_CONTROL();
> +}
> +
> +void __init bfin_cache_init(void)
> +{
> +#if defined(CONFIG_BFIN_DCACHE) || defined(CONFIG_BFIN_ICACHE)
> +       generate_cplb_tables();
> +#endif
> +       bfin_setup_caches(0);
> +}
> +
>  void __init bfin_relocate_l1_mem(void)
>  {
>        unsigned long l1_code_length;
> @@ -230,7 +276,7 @@ static int __init sanitize_memmap(struct bfin_memmap_entry *map, int *pnr_map)
>        /* record all known change-points (starting and ending addresses),
>           omitting those that are for empty memory regions */
>        chgidx = 0;
> -       for (i = 0; i < old_nr; i++)    {
> +       for (i = 0; i < old_nr; i++) {
>                if (map[i].size != 0) {
>                        change_point[chgidx]->addr = map[i].addr;
>                        change_point[chgidx++]->pentry = &map[i];
> @@ -238,13 +284,13 @@ static int __init sanitize_memmap(struct bfin_memmap_entry *map, int *pnr_map)
>                        change_point[chgidx++]->pentry = &map[i];
>                }
>        }
> -       chg_nr = chgidx;        /* true number of change-points */
> +       chg_nr = chgidx;        /* true number of change-points */
>
>        /* sort change-point list by memory addresses (low -> high) */
>        still_changing = 1;
> -       while (still_changing)  {
> +       while (still_changing) {
>                still_changing = 0;
> -               for (i = 1; i < chg_nr; i++)  {
> +               for (i = 1; i < chg_nr; i++) {
>                        /* if <current_addr> > <last_addr>, swap */
>                        /* or, if current=<start_addr> & last=<end_addr>, swap */
>                        if ((change_point[i]->addr < change_point[i-1]->addr) ||
> @@ -261,10 +307,10 @@ static int __init sanitize_memmap(struct bfin_memmap_entry *map, int *pnr_map)
>        }
>
>        /* create a new memmap, removing overlaps */
> -       overlap_entries = 0;     /* number of entries in the overlap table */
> -       new_entry = 0;   /* index for creating new memmap entries */
> -       last_type = 0;           /* start with undefined memory type */
> -       last_addr = 0;           /* start with 0 as last starting address */
> +       overlap_entries = 0;    /* number of entries in the overlap table */
> +       new_entry = 0;          /* index for creating new memmap entries */
> +       last_type = 0;          /* start with undefined memory type */
> +       last_addr = 0;          /* start with 0 as last starting address */
>        /* loop through change-points, determining affect on the new memmap */
>        for (chgidx = 0; chgidx < chg_nr; chgidx++) {
>                /* keep track of all overlapping memmap entries */
> @@ -286,14 +332,14 @@ static int __init sanitize_memmap(struct bfin_memmap_entry *map, int *pnr_map)
>                        if (overlap_list[i]->type > current_type)
>                                current_type = overlap_list[i]->type;
>                /* continue building up new memmap based on this information */
> -               if (current_type != last_type)  {
> +               if (current_type != last_type) {
>                        if (last_type != 0) {
>                                new_map[new_entry].size =
>                                        change_point[chgidx]->addr - last_addr;
>                                /* move forward only if the new size was non-zero */
>                                if (new_map[new_entry].size != 0)
>                                        if (++new_entry >= BFIN_MEMMAP_MAX)
> -                                               break;  /* no more space left for new entries */
> +                                               break;  /* no more space left for new entries */
>                        }
>                        if (current_type != 0) {
>                                new_map[new_entry].addr = change_point[chgidx]->addr;
> @@ -303,9 +349,9 @@ static int __init sanitize_memmap(struct bfin_memmap_entry *map, int *pnr_map)
>                        last_type = current_type;
>                }
>        }
> -       new_nr = new_entry;   /* retain count for new entries */
> +       new_nr = new_entry;     /* retain count for new entries */
>
> -       /* copy new  mapping into original location */
> +       /* copy new mapping into original location */
>        memcpy(map, new_map, new_nr*sizeof(struct bfin_memmap_entry));
>        *pnr_map = new_nr;
>
> @@ -361,7 +407,6 @@ static __init int parse_memmap(char *arg)
>  *  - "memmap=XXX[KkmM][@][$]XXX[KkmM]" defines a memory region
>  *       @ from <start> to <start>+<mem>, type RAM
>  *       $ from <start> to <start>+<mem>, type RESERVED
> - *
>  */
>  static __init void parse_cmdline_early(char *cmdline_p)
>  {
> @@ -383,12 +428,10 @@ static __init void parse_cmdline_early(char *cmdline_p)
>                                        if (*to != ' ') {
>                                                if (*to == '$'
>                                                    || *(to + 1) == '$')
> -                                                       reserved_mem_dcache_on =
> -                                                           1;
> +                                                       reserved_mem_dcache_on = 1;
>                                                if (*to == '#'
>                                                    || *(to + 1) == '#')
> -                                                       reserved_mem_icache_on =
> -                                                           1;
> +                                                       reserved_mem_icache_on = 1;
>                                        }
>                                }
>                        } else if (!memcmp(to, "earlyprintk=", 12)) {
> @@ -417,9 +460,8 @@ static __init void parse_cmdline_early(char *cmdline_p)
>  *     [_ramend - DMA_UNCACHED_REGION,
>  *             _ramend]:                       uncached DMA region
>  *  [_ramend, physical_mem_end]:       memory not managed by kernel
> - *
>  */
> -static __init void  memory_setup(void)
> +static __init void memory_setup(void)
>  {
>  #ifdef CONFIG_MTD_UCLINUX
>        unsigned long mtd_phys = 0;
> @@ -436,7 +478,7 @@ static __init void  memory_setup(void)
>        memory_end = _ramend - DMA_UNCACHED_REGION;
>
>  #ifdef CONFIG_MPU
> -       /* Round up to multiple of 4MB.  */
> +       /* Round up to multiple of 4MB */
>        memory_start = (_ramstart + 0x3fffff) & ~0x3fffff;
>  #else
>        memory_start = PAGE_ALIGN(_ramstart);
> @@ -616,7 +658,7 @@ static __init void setup_bootmem_allocator(void)
>        end_pfn = memory_end >> PAGE_SHIFT;
>
>        /*
> -        * give all the memory to the bootmap allocator,  tell it to put the
> +        * give all the memory to the bootmap allocator, tell it to put the
>         * boot mem_map at the start of memory.
>         */
>        bootmap_size = init_bootmem_node(NODE_DATA(0),
> @@ -791,7 +833,11 @@ void __init setup_arch(char **cmdline_p)
>        bfin_write_SWRST(_bfin_swrst | DOUBLE_FAULT);
>  #endif
>
> +#ifdef CONFIG_SMP
> +       if (_bfin_swrst & SWRST_DBL_FAULT_A) {
> +#else
>        if (_bfin_swrst & RESET_DOUBLE) {
> +#endif
>                printk(KERN_EMERG "Recovering from DOUBLE FAULT event\n");
>  #ifdef CONFIG_DEBUG_DOUBLEFAULT
>                /* We assume the crashing kernel, and the current symbol table match */
> @@ -835,7 +881,7 @@ void __init setup_arch(char **cmdline_p)
>        printk(KERN_INFO "Blackfin Linux support by http://blackfin.uclinux.org/\n");
>
>        printk(KERN_INFO "Processor Speed: %lu MHz core clock and %lu MHz System Clock\n",
> -              cclk / 1000000,  sclk / 1000000);
> +              cclk / 1000000, sclk / 1000000);
>
>        if (ANOMALY_05000273 && (cclk >> 1) <= sclk)
>                printk("\n\n\nANOMALY_05000273: CCLK must be >= 2*SCLK !!!\n\n\n");
> @@ -867,18 +913,21 @@ void __init setup_arch(char **cmdline_p)
>        BUG_ON((char *)&safe_user_instruction - (char *)&fixed_code_start
>                != SAFE_USER_INSTRUCTION - FIXED_CODE_START);
>
> +#ifdef CONFIG_SMP
> +       platform_init_cpus();
> +#endif
>        init_exception_vectors();
> -       bfin_cache_init();
> +       bfin_cache_init();      /* Initialize caches for the boot CPU */
>  }
>
>  static int __init topology_init(void)
>  {
> -       int cpu;
> +       unsigned int cpu;
> +       /* Record CPU-private information for the boot processor. */
> +       bfin_setup_cpudata(0);
>
>        for_each_possible_cpu(cpu) {
> -               struct cpu *c = &per_cpu(cpu_devices, cpu);
> -
> -               register_cpu(c, cpu);
> +               register_cpu(&per_cpu(cpu_data, cpu).cpu, cpu);
>        }
>
>        return 0;
> @@ -983,15 +1032,15 @@ static int show_cpuinfo(struct seq_file *m, void *v)
>        char *cpu, *mmu, *fpu, *vendor, *cache;
>        uint32_t revid;
>
> -       u_long cclk = 0, sclk = 0;
> +       u_long sclk = 0;
>        u_int icache_size = BFIN_ICACHESIZE / 1024, dcache_size = 0, dsup_banks = 0;
> +       struct blackfin_cpudata *cpudata = &per_cpu(cpu_data, *(unsigned int *)v);
>
>        cpu = CPU;
>        mmu = "none";
>        fpu = "none";
>        revid = bfin_revid();
>
> -       cclk = get_cclk();
>        sclk = get_sclk();
>
>        switch (bfin_read_CHIPID() & CHIPID_MANUFACTURE) {
> @@ -1003,10 +1052,8 @@ static int show_cpuinfo(struct seq_file *m, void *v)
>                break;
>        }
>
> -       seq_printf(m, "processor\t: %d\n"
> -               "vendor_id\t: %s\n",
> -               *(unsigned int *)v,
> -               vendor);
> +       seq_printf(m, "processor\t: %d\n" "vendor_id\t: %s\n",
> +               *(unsigned int *)v, vendor);
>
>        if (CPUID == bfin_cpuid())
>                seq_printf(m, "cpu family\t: 0x%04x\n", CPUID);
> @@ -1016,7 +1063,7 @@ static int show_cpuinfo(struct seq_file *m, void *v)
>
>        seq_printf(m, "model name\t: ADSP-%s %lu(MHz CCLK) %lu(MHz SCLK) (%s)\n"
>                "stepping\t: %d\n",
> -               cpu, cclk/1000000, sclk/1000000,
> +               cpu, cpudata->cclk/1000000, sclk/1000000,
>  #ifdef CONFIG_MPU
>                "mpu on",
>  #else
> @@ -1025,16 +1072,16 @@ static int show_cpuinfo(struct seq_file *m, void *v)
>                revid);
>
>        seq_printf(m, "cpu MHz\t\t: %lu.%03lu/%lu.%03lu\n",
> -               cclk/1000000, cclk%1000000,
> +               cpudata->cclk/1000000, cpudata->cclk%1000000,
>                sclk/1000000, sclk%1000000);
>        seq_printf(m, "bogomips\t: %lu.%02lu\n"
>                "Calibration\t: %lu loops\n",
> -               (loops_per_jiffy * HZ) / 500000,
> -               ((loops_per_jiffy * HZ) / 5000) % 100,
> -               (loops_per_jiffy * HZ));
> +               (cpudata->loops_per_jiffy * HZ) / 500000,
> +               ((cpudata->loops_per_jiffy * HZ) / 5000) % 100,
> +               (cpudata->loops_per_jiffy * HZ));
>
>        /* Check Cache configutation */
> -       switch (bfin_read_DMEM_CONTROL() & (1 << DMC0_P | 1 << DMC1_P)) {
> +       switch (cpudata->dmemctl & (1 << DMC0_P | 1 << DMC1_P)) {
>        case ACACHE_BSRAM:
>                cache = "dbank-A/B\t: cache/sram";
>                dcache_size = 16;
> @@ -1058,10 +1105,10 @@ static int show_cpuinfo(struct seq_file *m, void *v)
>        }
>
>        /* Is it turned on? */
> -       if ((bfin_read_DMEM_CONTROL() & (ENDCPLB | DMC_ENABLE)) != (ENDCPLB | DMC_ENABLE))
> +       if ((cpudata->dmemctl & (ENDCPLB | DMC_ENABLE)) != (ENDCPLB | DMC_ENABLE))
>                dcache_size = 0;
>
> -       if ((bfin_read_IMEM_CONTROL() & (IMC | ENICPLB)) != (IMC | ENICPLB))
> +       if ((cpudata->imemctl & (IMC | ENICPLB)) != (IMC | ENICPLB))
>                icache_size = 0;
>
>        seq_printf(m, "cache size\t: %d KB(L1 icache) "
> @@ -1086,8 +1133,13 @@ static int show_cpuinfo(struct seq_file *m, void *v)
>                   "dcache setup\t: %d Super-banks/%d Sub-banks/%d Ways, %d Lines/Way\n",
>                   dsup_banks, BFIN_DSUBBANKS, BFIN_DWAYS,
>                   BFIN_DLINES);
> +#ifdef __ARCH_SYNC_CORE_DCACHE
> +       seq_printf(m,
> +               "SMP Dcache Flushes\t: %lu\n\n",
> +               per_cpu(cpu_data, *(unsigned int *)v).dcache_invld_count);
> +#endif
>  #ifdef CONFIG_BFIN_ICACHE_LOCK
> -       switch ((bfin_read_IMEM_CONTROL() >> 3) & WAYALL_L) {
> +       switch ((cpudata->imemctl >> 3) & WAYALL_L) {
>        case WAY0_L:
>                seq_printf(m, "Way0 Locked-Down\n");
>                break;
> @@ -1137,6 +1189,12 @@ static int show_cpuinfo(struct seq_file *m, void *v)
>                seq_printf(m, "No Ways are locked\n");
>        }
>  #endif
> +       if (*(unsigned int *)v != NR_CPUS-1)
> +               return 0;
> +
> +#if L2_LENGTH
> +       seq_printf(m, "L2 SRAM\t\t: %dKB\n", L2_LENGTH/0x400);
> +#endif
>        seq_printf(m, "board name\t: %s\n", bfin_board_name);
>        seq_printf(m, "board memory\t: %ld kB (0x%p -> 0x%p)\n",
>                 physical_mem_end >> 10, (void *)0, (void *)physical_mem_end);
> @@ -1144,6 +1202,7 @@ static int show_cpuinfo(struct seq_file *m, void *v)
>                ((int)memory_end - (int)_stext) >> 10,
>                _stext,
>                (void *)memory_end);
> +       seq_printf(m, "\n");
>
>        return 0;
>  }
> diff --git a/arch/blackfin/kernel/time.c b/arch/blackfin/kernel/time.c
> index eb23523..06de2ce 100644
> --- a/arch/blackfin/kernel/time.c
> +++ b/arch/blackfin/kernel/time.c
> @@ -34,9 +34,11 @@
>  #include <linux/interrupt.h>
>  #include <linux/time.h>
>  #include <linux/irq.h>
> +#include <linux/delay.h>
>
>  #include <asm/blackfin.h>
>  #include <asm/time.h>
> +#include <asm/gptimers.h>
>
>  /* This is an NTP setting */
>  #define        TICK_SIZE (tick_nsec / 1000)
> @@ -46,11 +48,14 @@ static unsigned long gettimeoffset(void);
>
>  static struct irqaction bfin_timer_irq = {
>        .name = "BFIN Timer Tick",
> +#ifdef CONFIG_IRQ_PER_CPU
> +       .flags = IRQF_DISABLED  | IRQF_PERCPU,
> +#else
>        .flags = IRQF_DISABLED
> +#endif
>  };
>
> -static void
> -time_sched_init(irq_handler_t timer_routine)
> +void setup_core_timer(void)
>  {
>        u32 tcount;
>
> @@ -71,12 +76,41 @@ time_sched_init(irq_handler_t timer_routine)
>        CSYNC();
>
>        bfin_write_TCNTL(7);
> +}
> +
> +#ifdef CONFIG_TICK_SOURCE_SYSTMR0
> +void setup_system_timer0(void)
> +{
> +       /* Power down the core timer, just to play safe. */
> +       bfin_write_TCNTL(0);
> +
> +       disable_gptimers(TIMER0bit);
> +       set_gptimer_status(0, TIMER_STATUS_TRUN0);
> +       while (get_gptimer_status(0) & TIMER_STATUS_TRUN0)
> +               udelay(10);
> +
> +       set_gptimer_config(0, 0x59); /* IRQ enable, periodic, PWM_OUT, SCLKed, OUT PAD disabled */
> +       set_gptimer_period(TIMER0_id, get_sclk() / HZ);
> +       set_gptimer_pwidth(TIMER0_id, 1);
> +       SSYNC();
> +       enable_gptimers(TIMER0bit);
> +}
> +#endif
>
> +static void
> +time_sched_init(irqreturn_t(*timer_routine) (int, void *))
> +{
> +#ifdef CONFIG_TICK_SOURCE_SYSTMR0
> +       setup_system_timer0();
> +#else
> +       setup_core_timer();
> +#endif
>        bfin_timer_irq.handler = (irq_handler_t)timer_routine;
> -       /* call setup_irq instead of request_irq because request_irq calls
> -        * kmalloc which has not been initialized yet
> -        */
> +#ifdef CONFIG_TICK_SOURCE_SYSTMR0
> +       setup_irq(IRQ_TIMER0, &bfin_timer_irq);
> +#else
>        setup_irq(IRQ_CORETMR, &bfin_timer_irq);
> +#endif
>  }
>
>  /*
> @@ -87,17 +121,23 @@ static unsigned long gettimeoffset(void)
>        unsigned long offset;
>        unsigned long clocks_per_jiffy;
>
> +#ifdef CONFIG_TICK_SOURCE_SYSTMR0
> +       clocks_per_jiffy =  bfin_read_TIMER0_PERIOD();
> +       offset =  bfin_read_TIMER0_COUNTER() / \
> +               (((clocks_per_jiffy + 1) * HZ) / USEC_PER_SEC);
> +
> +       if ((get_gptimer_status(0) & TIMER_STATUS_TIMIL0) && offset < (100000 / HZ / 2))
> +               offset += (USEC_PER_SEC / HZ);
> +#else
>        clocks_per_jiffy = bfin_read_TPERIOD();
> -       offset =
> -           (clocks_per_jiffy -
> -            bfin_read_TCOUNT()) / (((clocks_per_jiffy + 1) * HZ) /
> -                                   USEC_PER_SEC);
> +       offset = (clocks_per_jiffy - bfin_read_TCOUNT()) / \
> +               (((clocks_per_jiffy + 1) * HZ)  / USEC_PER_SEC);
>
>        /* Check if we just wrapped the counters and maybe missed a tick */
>        if ((bfin_read_ILAT() & (1 << IRQ_CORETMR))
> -           && (offset < (100000 / HZ / 2)))
> +               && (offset < (100000 / HZ / 2)))
>                offset += (USEC_PER_SEC / HZ);
> -
> +#endif
>        return offset;
>  }
>
> @@ -120,34 +160,38 @@ irqreturn_t timer_interrupt(int irq, void *dummy)
>        static long last_rtc_update;
>
>        write_seqlock(&xtime_lock);
> -
> -       do_timer(1);
> -
> -       profile_tick(CPU_PROFILING);
> -
> -       /*
> -        * If we have an externally synchronized Linux clock, then update
> -        * CMOS clock accordingly every ~11 minutes. Set_rtc_mmss() has to be
> -        * called as close as possible to 500 ms before the new second starts.
> -        */
> -
> -       if (ntp_synced() &&
> -           xtime.tv_sec > last_rtc_update + 660 &&
> -           (xtime.tv_nsec / NSEC_PER_USEC) >=
> -           500000 - ((unsigned)TICK_SIZE) / 2
> -           && (xtime.tv_nsec / NSEC_PER_USEC) <=
> -           500000 + ((unsigned)TICK_SIZE) / 2) {
> -               if (set_rtc_mmss(xtime.tv_sec) == 0)
> -                       last_rtc_update = xtime.tv_sec;
> -               else
> -                       /* Do it again in 60s. */
> -                       last_rtc_update = xtime.tv_sec - 600;
> +#ifdef CONFIG_TICK_SOURCE_SYSTMR0
> +       if (get_gptimer_status(0) & TIMER_STATUS_TIMIL0) {
> +#endif
> +               do_timer(1);
> +
> +
> +               /*
> +                * If we have an externally synchronized Linux clock, then update
> +                * CMOS clock accordingly every ~11 minutes. Set_rtc_mmss() has to be
> +                * called as close as possible to 500 ms before the new second starts.
> +                */
> +
> +               if (ntp_synced() &&
> +                   xtime.tv_sec > last_rtc_update + 660 &&
> +                   (xtime.tv_nsec / NSEC_PER_USEC) >=
> +                   500000 - ((unsigned)TICK_SIZE) / 2
> +                   && (xtime.tv_nsec / NSEC_PER_USEC) <=
> +                   500000 + ((unsigned)TICK_SIZE) / 2) {
> +                       if (set_rtc_mmss(xtime.tv_sec) == 0)
> +                               last_rtc_update = xtime.tv_sec;
> +                       else
> +                               /* Do it again in 60s. */
> +                               last_rtc_update = xtime.tv_sec - 600;
> +               }
> +#ifdef CONFIG_TICK_SOURCE_SYSTMR0
> +               set_gptimer_status(0, TIMER_STATUS_TIMIL0);
>        }
> +#endif
>        write_sequnlock(&xtime_lock);
>
> -#ifndef CONFIG_SMP
>        update_process_times(user_mode(get_irq_regs()));
> -#endif
> +       profile_tick(CPU_PROFILING);
>
>        return IRQ_HANDLED;
>  }
> diff --git a/arch/blackfin/kernel/traps.c b/arch/blackfin/kernel/traps.c
> index bef025b..af7cc43 100644
> --- a/arch/blackfin/kernel/traps.c
> +++ b/arch/blackfin/kernel/traps.c
> @@ -75,16 +75,6 @@ void __init trap_init(void)
>        CSYNC();
>  }
>
> -/*
> - * Used to save the RETX, SEQSTAT, I/D CPLB FAULT ADDR
> - * values across the transition from exception to IRQ5.
> - * We put these in L1, so they are going to be in a valid
> - * location during exception context
> - */
> -__attribute__((l1_data))
> -unsigned long saved_retx, saved_seqstat,
> -       saved_icplb_fault_addr, saved_dcplb_fault_addr;
> -
>  static void decode_address(char *buf, unsigned long address)
>  {
>  #ifdef CONFIG_DEBUG_VERBOSE
> @@ -211,18 +201,18 @@ asmlinkage void double_fault_c(struct pt_regs *fp)
>        printk(KERN_EMERG "\n" KERN_EMERG "Double Fault\n");
>  #ifdef CONFIG_DEBUG_DOUBLEFAULT_PRINT
>        if (((long)fp->seqstat &  SEQSTAT_EXCAUSE) == VEC_UNCOV) {
> +               unsigned int cpu = smp_processor_id();
>                char buf[150];
> -               decode_address(buf, saved_retx);
> +               decode_address(buf, cpu_pda[cpu].retx);
>                printk(KERN_EMERG "While handling exception (EXCAUSE = 0x%x) at %s:\n",
> -                       (int)saved_seqstat & SEQSTAT_EXCAUSE, buf);
> -               decode_address(buf, saved_dcplb_fault_addr);
> +                       (unsigned int)cpu_pda[cpu].seqstat & SEQSTAT_EXCAUSE, buf);
> +               decode_address(buf, cpu_pda[cpu].dcplb_fault_addr);
>                printk(KERN_NOTICE "   DCPLB_FAULT_ADDR: %s\n", buf);
> -               decode_address(buf, saved_icplb_fault_addr);
> +               decode_address(buf, cpu_pda[cpu].icplb_fault_addr);
>                printk(KERN_NOTICE "   ICPLB_FAULT_ADDR: %s\n", buf);
>
>                decode_address(buf, fp->retx);
> -               printk(KERN_NOTICE "The instruction at %s caused a double exception\n",
> -                       buf);
> +               printk(KERN_NOTICE "The instruction at %s caused a double exception\n", buf);
>        } else
>  #endif
>        {
> @@ -240,6 +230,9 @@ asmlinkage void trap_c(struct pt_regs *fp)
>  #ifdef CONFIG_DEBUG_BFIN_HWTRACE_ON
>        int j;
>  #endif
> +#ifdef CONFIG_DEBUG_HUNT_FOR_ZERO
> +       unsigned int cpu = smp_processor_id();
> +#endif
>        int sig = 0;
>        siginfo_t info;
>        unsigned long trapnr = fp->seqstat & SEQSTAT_EXCAUSE;
> @@ -417,7 +410,7 @@ asmlinkage void trap_c(struct pt_regs *fp)
>                info.si_code = ILL_CPLB_MULHIT;
>                sig = SIGSEGV;
>  #ifdef CONFIG_DEBUG_HUNT_FOR_ZERO
> -               if (saved_dcplb_fault_addr < FIXED_CODE_START)
> +               if (cpu_pda[cpu].dcplb_fault_addr < FIXED_CODE_START)
>                        verbose_printk(KERN_NOTICE "NULL pointer access\n");
>                else
>  #endif
> @@ -471,7 +464,7 @@ asmlinkage void trap_c(struct pt_regs *fp)
>                info.si_code = ILL_CPLB_MULHIT;
>                sig = SIGSEGV;
>  #ifdef CONFIG_DEBUG_HUNT_FOR_ZERO
> -               if (saved_icplb_fault_addr < FIXED_CODE_START)
> +               if (cpu_pda[cpu].icplb_fault_addr < FIXED_CODE_START)
>                        verbose_printk(KERN_NOTICE "Jump to NULL address\n");
>                else
>  #endif
> @@ -960,6 +953,7 @@ void dump_bfin_process(struct pt_regs *fp)
>                else
>                        verbose_printk(KERN_NOTICE "COMM= invalid\n");
>
> +               printk(KERN_NOTICE "CPU = %d\n", current_thread_info()->cpu);
>                if (!((unsigned long)current->mm & 0x3) && (unsigned long)current->mm >= FIXED_CODE_START)
>                        verbose_printk(KERN_NOTICE  "TEXT = 0x%p-0x%p        DATA = 0x%p-0x%p\n"
>                                KERN_NOTICE " BSS = 0x%p-0x%p  USER-STACK = 0x%p\n"
> @@ -1053,6 +1047,7 @@ void show_regs(struct pt_regs *fp)
>        struct irqaction *action;
>        unsigned int i;
>        unsigned long flags;
> +       unsigned int cpu = smp_processor_id();
>
>        verbose_printk(KERN_NOTICE "\n" KERN_NOTICE "SEQUENCER STATUS:\t\t%s\n", print_tainted());
>        verbose_printk(KERN_NOTICE " SEQSTAT: %08lx  IPEND: %04lx  SYSCFG: %04lx\n",
> @@ -1112,9 +1107,9 @@ unlock:
>
>        if (((long)fp->seqstat &  SEQSTAT_EXCAUSE) &&
>            (((long)fp->seqstat & SEQSTAT_EXCAUSE) != VEC_HWERR)) {
> -               decode_address(buf, saved_dcplb_fault_addr);
> +               decode_address(buf, cpu_pda[cpu].dcplb_fault_addr);
>                verbose_printk(KERN_NOTICE "DCPLB_FAULT_ADDR: %s\n", buf);
> -               decode_address(buf, saved_icplb_fault_addr);
> +               decode_address(buf, cpu_pda[cpu].icplb_fault_addr);
>                verbose_printk(KERN_NOTICE "ICPLB_FAULT_ADDR: %s\n", buf);
>        }
>
> @@ -1153,20 +1148,21 @@ unlock:
>  asmlinkage int sys_bfin_spinlock(int *spinlock)__attribute__((l1_text));
>  #endif
>
> -asmlinkage int sys_bfin_spinlock(int *spinlock)
> +static DEFINE_SPINLOCK(bfin_spinlock_lock);
> +
> +asmlinkage int sys_bfin_spinlock(int *p)
>  {
> -       int ret = 0;
> -       int tmp = 0;
> +       int ret, tmp = 0;
>
> -       local_irq_disable();
> -       ret = get_user(tmp, spinlock);
> -       if (ret == 0) {
> -               if (tmp)
> +       spin_lock(&bfin_spinlock_lock); /* This would also hold kernel preemption. */
> +       ret = get_user(tmp, p);
> +       if (likely(ret == 0)) {
> +               if (unlikely(tmp))
>                        ret = 1;
> -               tmp = 1;
> -               put_user(tmp, spinlock);
> +               else
> +                       put_user(1, p);
>        }
> -       local_irq_enable();
> +       spin_unlock(&bfin_spinlock_lock);
>        return ret;
>  }
>
> diff --git a/arch/blackfin/mm/init.c b/arch/blackfin/mm/init.c
> index bc240ab..57d306b 100644
> --- a/arch/blackfin/mm/init.c
> +++ b/arch/blackfin/mm/init.c
> @@ -31,7 +31,8 @@
>  #include <linux/bootmem.h>
>  #include <linux/uaccess.h>
>  #include <asm/bfin-global.h>
> -#include <asm/l1layout.h>
> +#include <asm/pda.h>
> +#include <asm/cplbinit.h>
>  #include "blackfin_sram.h"
>
>  /*
> @@ -53,6 +54,11 @@ static unsigned long empty_bad_page;
>
>  unsigned long empty_zero_page;
>
> +extern unsigned long exception_stack[NR_CPUS][1024];
> +
> +struct blackfin_pda cpu_pda[NR_CPUS];
> +EXPORT_SYMBOL(cpu_pda);
> +
>  /*
>  * paging_init() continues the virtual memory environment setup which
>  * was begun by the code in arch/head.S.
> @@ -98,6 +104,42 @@ void __init paging_init(void)
>        }
>  }
>
> +asmlinkage void init_pda(void)
> +{
> +       unsigned int cpu = raw_smp_processor_id();
> +
> +       /* Initialize the PDA fields holding references to other parts
> +          of the memory. The content of such memory is still
> +          undefined at the time of the call, we are only setting up
> +          valid pointers to it. */
> +       memset(&cpu_pda[cpu], 0, sizeof(cpu_pda[cpu]));
> +
> +       cpu_pda[0].next = &cpu_pda[1];
> +       cpu_pda[1].next = &cpu_pda[0];
> +
> +       cpu_pda[cpu].ex_stack = exception_stack[cpu + 1];
> +
> +#ifdef CONFIG_MPU
> +#else
> +       cpu_pda[cpu].ipdt = ipdt_tables[cpu];
> +       cpu_pda[cpu].dpdt = dpdt_tables[cpu];
> +#ifdef CONFIG_CPLB_INFO
> +       cpu_pda[cpu].ipdt_swapcount = ipdt_swapcount_tables[cpu];
> +       cpu_pda[cpu].dpdt_swapcount = dpdt_swapcount_tables[cpu];
> +#endif
> +#endif
> +
> +#ifdef CONFIG_SMP
> +       cpu_pda[cpu].imask = 0x1f;
> +#endif
> +}
> +
> +void __cpuinit reserve_pda(void)
> +{
> +       printk(KERN_INFO "PDA for CPU%u reserved at %p\n", smp_processor_id(),
> +                                       &cpu_pda[smp_processor_id()]);
> +}
> +
>  void __init mem_init(void)
>  {
>        unsigned int codek = 0, datak = 0, initk = 0;
> @@ -141,21 +183,13 @@ void __init mem_init(void)
>
>  static int __init sram_init(void)
>  {
> -       unsigned long tmp;
> -
>        /* Initialize the blackfin L1 Memory. */
>        bfin_sram_init();
>
> -       /* Allocate this once; never free it.  We assume this gives us a
> -          pointer to the start of L1 scratchpad memory; panic if it
> -          doesn't.  */
> -       tmp = (unsigned long)l1sram_alloc(sizeof(struct l1_scratch_task_info));
> -       if (tmp != (unsigned long)L1_SCRATCH_TASK_INFO) {
> -               printk(KERN_EMERG "mem_init(): Did not get the right address from l1sram_alloc: %08lx != %08lx\n",
> -                       tmp, (unsigned long)L1_SCRATCH_TASK_INFO);
> -               panic("No L1, time to give up\n");
> -       }
> -
> +       /* Reserve the PDA space for the boot CPU right after we
> +        * initialized the scratch memory allocator.
> +        */
> +       reserve_pda();
>        return 0;
>  }
>  pure_initcall(sram_init);
> diff --git a/arch/blackfin/mm/sram-alloc.c b/arch/blackfin/mm/sram-alloc.c
> index cc6f336..8f82b4c 100644
> --- a/arch/blackfin/mm/sram-alloc.c
> +++ b/arch/blackfin/mm/sram-alloc.c
> @@ -41,8 +41,10 @@
>  #include <asm/blackfin.h>
>  #include "blackfin_sram.h"
>
> -static spinlock_t l1sram_lock, l1_data_sram_lock, l1_inst_sram_lock;
> -static spinlock_t l2_sram_lock;
> +static DEFINE_PER_CPU(spinlock_t, l1sram_lock) ____cacheline_aligned_in_smp;
> +static DEFINE_PER_CPU(spinlock_t, l1_data_sram_lock) ____cacheline_aligned_in_smp;
> +static DEFINE_PER_CPU(spinlock_t, l1_inst_sram_lock) ____cacheline_aligned_in_smp;
> +static spinlock_t l2_sram_lock ____cacheline_aligned_in_smp;
>
>  /* the data structure for L1 scratchpad and DATA SRAM */
>  struct sram_piece {
> @@ -52,18 +54,22 @@ struct sram_piece {
>        struct sram_piece *next;
>  };
>
> -static struct sram_piece free_l1_ssram_head, used_l1_ssram_head;
> +static DEFINE_PER_CPU(struct sram_piece, free_l1_ssram_head);
> +static DEFINE_PER_CPU(struct sram_piece, used_l1_ssram_head);
>
>  #if L1_DATA_A_LENGTH != 0
> -static struct sram_piece free_l1_data_A_sram_head, used_l1_data_A_sram_head;
> +static DEFINE_PER_CPU(struct sram_piece, free_l1_data_A_sram_head);
> +static DEFINE_PER_CPU(struct sram_piece, used_l1_data_A_sram_head);
>  #endif
>
>  #if L1_DATA_B_LENGTH != 0
> -static struct sram_piece free_l1_data_B_sram_head, used_l1_data_B_sram_head;
> +static DEFINE_PER_CPU(struct sram_piece, free_l1_data_B_sram_head);
> +static DEFINE_PER_CPU(struct sram_piece, used_l1_data_B_sram_head);
>  #endif
>
>  #if L1_CODE_LENGTH != 0
> -static struct sram_piece free_l1_inst_sram_head, used_l1_inst_sram_head;
> +static DEFINE_PER_CPU(struct sram_piece, free_l1_inst_sram_head);
> +static DEFINE_PER_CPU(struct sram_piece, used_l1_inst_sram_head);
>  #endif
>
>  #if L2_LENGTH != 0
> @@ -75,102 +81,115 @@ static struct kmem_cache *sram_piece_cache;
>  /* L1 Scratchpad SRAM initialization function */
>  static void __init l1sram_init(void)
>  {
> -       free_l1_ssram_head.next =
> -               kmem_cache_alloc(sram_piece_cache, GFP_KERNEL);
> -       if (!free_l1_ssram_head.next) {
> -               printk(KERN_INFO "Failed to initialize Scratchpad data SRAM\n");
> -               return;
> +       unsigned int cpu;
> +       for (cpu = 0; cpu < num_possible_cpus(); ++cpu) {
> +               per_cpu(free_l1_ssram_head, cpu).next =
> +                       kmem_cache_alloc(sram_piece_cache, GFP_KERNEL);
> +               if (!per_cpu(free_l1_ssram_head, cpu).next) {
> +                       printk(KERN_INFO "Fail to initialize Scratchpad data SRAM.\n");
> +                       return;
> +               }
> +
> +               per_cpu(free_l1_ssram_head, cpu).next->paddr = (void *)get_l1_scratch_start_cpu(cpu);
> +               per_cpu(free_l1_ssram_head, cpu).next->size = L1_SCRATCH_LENGTH;
> +               per_cpu(free_l1_ssram_head, cpu).next->pid = 0;
> +               per_cpu(free_l1_ssram_head, cpu).next->next = NULL;
> +
> +               per_cpu(used_l1_ssram_head, cpu).next = NULL;
> +
> +               /* mutex initialize */
> +               spin_lock_init(&per_cpu(l1sram_lock, cpu));
> +               printk(KERN_INFO "Blackfin Scratchpad data SRAM: %d KB\n",
> +                       L1_SCRATCH_LENGTH >> 10);
>        }
> -
> -       free_l1_ssram_head.next->paddr = (void *)L1_SCRATCH_START;
> -       free_l1_ssram_head.next->size = L1_SCRATCH_LENGTH;
> -       free_l1_ssram_head.next->pid = 0;
> -       free_l1_ssram_head.next->next = NULL;
> -
> -       used_l1_ssram_head.next = NULL;
> -
> -       /* mutex initialize */
> -       spin_lock_init(&l1sram_lock);
> -
> -       printk(KERN_INFO "Blackfin Scratchpad data SRAM: %d KB\n",
> -              L1_SCRATCH_LENGTH >> 10);
>  }
>
>  static void __init l1_data_sram_init(void)
>  {
> +       unsigned int cpu;
>  #if L1_DATA_A_LENGTH != 0
> -       free_l1_data_A_sram_head.next =
> -               kmem_cache_alloc(sram_piece_cache, GFP_KERNEL);
> -       if (!free_l1_data_A_sram_head.next) {
> -               printk(KERN_INFO "Failed to initialize L1 Data A SRAM\n");
> -               return;
> +       for (cpu = 0; cpu < num_possible_cpus(); ++cpu) {
> +               per_cpu(free_l1_data_A_sram_head, cpu).next =
> +                       kmem_cache_alloc(sram_piece_cache, GFP_KERNEL);
> +               if (!per_cpu(free_l1_data_A_sram_head, cpu).next) {
> +                       printk(KERN_INFO "Fail to initialize L1 Data A SRAM.\n");
> +                       return;
> +               }
> +
> +               per_cpu(free_l1_data_A_sram_head, cpu).next->paddr =
> +                       (void *)get_l1_data_a_start_cpu(cpu) + (_ebss_l1 - _sdata_l1);
> +               per_cpu(free_l1_data_A_sram_head, cpu).next->size =
> +                       L1_DATA_A_LENGTH - (_ebss_l1 - _sdata_l1);
> +               per_cpu(free_l1_data_A_sram_head, cpu).next->pid = 0;
> +               per_cpu(free_l1_data_A_sram_head, cpu).next->next = NULL;
> +
> +               per_cpu(used_l1_data_A_sram_head, cpu).next = NULL;
> +
> +               printk(KERN_INFO "Blackfin L1 Data A SRAM: %d KB (%d KB free)\n",
> +                       L1_DATA_A_LENGTH >> 10,
> +                       per_cpu(free_l1_data_A_sram_head, cpu).next->size >> 10);
>        }
> -
> -       free_l1_data_A_sram_head.next->paddr =
> -               (void *)L1_DATA_A_START + (_ebss_l1 - _sdata_l1);
> -       free_l1_data_A_sram_head.next->size =
> -               L1_DATA_A_LENGTH - (_ebss_l1 - _sdata_l1);
> -       free_l1_data_A_sram_head.next->pid = 0;
> -       free_l1_data_A_sram_head.next->next = NULL;
> -
> -       used_l1_data_A_sram_head.next = NULL;
> -
> -       printk(KERN_INFO "Blackfin L1 Data A SRAM: %d KB (%d KB free)\n",
> -               L1_DATA_A_LENGTH >> 10,
> -               free_l1_data_A_sram_head.next->size >> 10);
>  #endif
>  #if L1_DATA_B_LENGTH != 0
> -       free_l1_data_B_sram_head.next =
> -               kmem_cache_alloc(sram_piece_cache, GFP_KERNEL);
> -       if (!free_l1_data_B_sram_head.next) {
> -               printk(KERN_INFO "Failed to initialize L1 Data B SRAM\n");
> -               return;
> +       for (cpu = 0; cpu < num_possible_cpus(); ++cpu) {
> +               per_cpu(free_l1_data_B_sram_head, cpu).next =
> +                       kmem_cache_alloc(sram_piece_cache, GFP_KERNEL);
> +               if (!per_cpu(free_l1_data_B_sram_head, cpu).next) {
> +                       printk(KERN_INFO "Fail to initialize L1 Data B SRAM.\n");
> +                       return;
> +               }
> +
> +               per_cpu(free_l1_data_B_sram_head, cpu).next->paddr =
> +                       (void *)get_l1_data_b_start_cpu(cpu) + (_ebss_b_l1 - _sdata_b_l1);
> +               per_cpu(free_l1_data_B_sram_head, cpu).next->size =
> +                       L1_DATA_B_LENGTH - (_ebss_b_l1 - _sdata_b_l1);
> +               per_cpu(free_l1_data_B_sram_head, cpu).next->pid = 0;
> +               per_cpu(free_l1_data_B_sram_head, cpu).next->next = NULL;
> +
> +               per_cpu(used_l1_data_B_sram_head, cpu).next = NULL;
> +
> +               printk(KERN_INFO "Blackfin L1 Data B SRAM: %d KB (%d KB free)\n",
> +                       L1_DATA_B_LENGTH >> 10,
> +                       per_cpu(free_l1_data_B_sram_head, cpu).next->size >> 10);
> +               /* mutex initialize */
>        }
> -
> -       free_l1_data_B_sram_head.next->paddr =
> -               (void *)L1_DATA_B_START + (_ebss_b_l1 - _sdata_b_l1);
> -       free_l1_data_B_sram_head.next->size =
> -               L1_DATA_B_LENGTH - (_ebss_b_l1 - _sdata_b_l1);
> -       free_l1_data_B_sram_head.next->pid = 0;
> -       free_l1_data_B_sram_head.next->next = NULL;
> -
> -       used_l1_data_B_sram_head.next = NULL;
> -
> -       printk(KERN_INFO "Blackfin L1 Data B SRAM: %d KB (%d KB free)\n",
> -               L1_DATA_B_LENGTH >> 10,
> -               free_l1_data_B_sram_head.next->size >> 10);
>  #endif
>
> -       /* mutex initialize */
> -       spin_lock_init(&l1_data_sram_lock);
> +#if L1_DATA_A_LENGTH != 0 || L1_DATA_B_LENGTH != 0
> +       for (cpu = 0; cpu < num_possible_cpus(); ++cpu)
> +               spin_lock_init(&per_cpu(l1_data_sram_lock, cpu));
> +#endif
>  }
>
>  static void __init l1_inst_sram_init(void)
>  {
>  #if L1_CODE_LENGTH != 0
> -       free_l1_inst_sram_head.next =
> -               kmem_cache_alloc(sram_piece_cache, GFP_KERNEL);
> -       if (!free_l1_inst_sram_head.next) {
> -               printk(KERN_INFO "Failed to initialize L1 Instruction SRAM\n");
> -               return;
> +       unsigned int cpu;
> +       for (cpu = 0; cpu < num_possible_cpus(); ++cpu) {
> +               per_cpu(free_l1_inst_sram_head, cpu).next =
> +                       kmem_cache_alloc(sram_piece_cache, GFP_KERNEL);
> +               if (!per_cpu(free_l1_inst_sram_head, cpu).next) {
> +                       printk(KERN_INFO "Failed to initialize L1 Instruction SRAM\n");
> +                       return;
> +               }
> +
> +               per_cpu(free_l1_inst_sram_head, cpu).next->paddr =
> +                       (void *)get_l1_code_start_cpu(cpu) + (_etext_l1 - _stext_l1);
> +               per_cpu(free_l1_inst_sram_head, cpu).next->size =
> +                       L1_CODE_LENGTH - (_etext_l1 - _stext_l1);
> +               per_cpu(free_l1_inst_sram_head, cpu).next->pid = 0;
> +               per_cpu(free_l1_inst_sram_head, cpu).next->next = NULL;
> +
> +               per_cpu(used_l1_inst_sram_head, cpu).next = NULL;
> +
> +               printk(KERN_INFO "Blackfin L1 Instruction SRAM: %d KB (%d KB free)\n",
> +                       L1_CODE_LENGTH >> 10,
> +                       per_cpu(free_l1_inst_sram_head, cpu).next->size >> 10);
> +
> +               /* mutex initialize */
> +               spin_lock_init(&per_cpu(l1_inst_sram_lock, cpu));
>        }
> -
> -       free_l1_inst_sram_head.next->paddr =
> -               (void *)L1_CODE_START + (_etext_l1 - _stext_l1);
> -       free_l1_inst_sram_head.next->size =
> -               L1_CODE_LENGTH - (_etext_l1 - _stext_l1);
> -       free_l1_inst_sram_head.next->pid = 0;
> -       free_l1_inst_sram_head.next->next = NULL;
> -
> -       used_l1_inst_sram_head.next = NULL;
> -
> -       printk(KERN_INFO "Blackfin L1 Instruction SRAM: %d KB (%d KB free)\n",
> -               L1_CODE_LENGTH >> 10,
> -               free_l1_inst_sram_head.next->size >> 10);
>  #endif
> -
> -       /* mutex initialize */
> -       spin_lock_init(&l1_inst_sram_lock);
>  }
>
>  static void __init l2_sram_init(void)
> @@ -179,7 +198,7 @@ static void __init l2_sram_init(void)
>        free_l2_sram_head.next =
>                kmem_cache_alloc(sram_piece_cache, GFP_KERNEL);
>        if (!free_l2_sram_head.next) {
> -               printk(KERN_INFO "Failed to initialize L2 SRAM\n");
> +               printk(KERN_INFO "Fail to initialize L2 SRAM.\n");
>                return;
>        }
>
> @@ -200,6 +219,7 @@ static void __init l2_sram_init(void)
>        /* mutex initialize */
>        spin_lock_init(&l2_sram_lock);
>  }
> +
>  void __init bfin_sram_init(void)
>  {
>        sram_piece_cache = kmem_cache_create("sram_piece_cache",
> @@ -353,20 +373,20 @@ int sram_free(const void *addr)
>  {
>
>  #if L1_CODE_LENGTH != 0
> -       if (addr >= (void *)L1_CODE_START
> -                && addr < (void *)(L1_CODE_START + L1_CODE_LENGTH))
> +       if (addr >= (void *)get_l1_code_start()
> +                && addr < (void *)(get_l1_code_start() + L1_CODE_LENGTH))
>                return l1_inst_sram_free(addr);
>        else
>  #endif
>  #if L1_DATA_A_LENGTH != 0
> -       if (addr >= (void *)L1_DATA_A_START
> -                && addr < (void *)(L1_DATA_A_START + L1_DATA_A_LENGTH))
> +       if (addr >= (void *)get_l1_data_a_start()
> +                && addr < (void *)(get_l1_data_a_start() + L1_DATA_A_LENGTH))
>                return l1_data_A_sram_free(addr);
>        else
>  #endif
>  #if L1_DATA_B_LENGTH != 0
> -       if (addr >= (void *)L1_DATA_B_START
> -                && addr < (void *)(L1_DATA_B_START + L1_DATA_B_LENGTH))
> +       if (addr >= (void *)get_l1_data_b_start()
> +                && addr < (void *)(get_l1_data_b_start() + L1_DATA_B_LENGTH))
>                return l1_data_B_sram_free(addr);
>        else
>  #endif
> @@ -384,17 +404,20 @@ void *l1_data_A_sram_alloc(size_t size)
>  {
>        unsigned long flags;
>        void *addr = NULL;
> +       unsigned int cpu;
>
> +       cpu = get_cpu();
>        /* add mutex operation */
> -       spin_lock_irqsave(&l1_data_sram_lock, flags);
> +       spin_lock_irqsave(&per_cpu(l1_data_sram_lock, cpu), flags);
>
>  #if L1_DATA_A_LENGTH != 0
> -       addr = _sram_alloc(size, &free_l1_data_A_sram_head,
> -                       &used_l1_data_A_sram_head);
> +       addr = _sram_alloc(size, &per_cpu(free_l1_data_A_sram_head, cpu),
> +                       &per_cpu(used_l1_data_A_sram_head, cpu));
>  #endif
>
>        /* add mutex operation */
> -       spin_unlock_irqrestore(&l1_data_sram_lock, flags);
> +       spin_unlock_irqrestore(&per_cpu(l1_data_sram_lock, cpu), flags);
> +       put_cpu();
>
>        pr_debug("Allocated address in l1_data_A_sram_alloc is 0x%lx+0x%lx\n",
>                 (long unsigned int)addr, size);
> @@ -407,19 +430,22 @@ int l1_data_A_sram_free(const void *addr)
>  {
>        unsigned long flags;
>        int ret;
> +       unsigned int cpu;
>
> +       cpu = get_cpu();
>        /* add mutex operation */
> -       spin_lock_irqsave(&l1_data_sram_lock, flags);
> +       spin_lock_irqsave(&per_cpu(l1_data_sram_lock, cpu), flags);
>
>  #if L1_DATA_A_LENGTH != 0
> -       ret = _sram_free(addr, &free_l1_data_A_sram_head,
> -                       &used_l1_data_A_sram_head);
> +       ret = _sram_free(addr, &per_cpu(free_l1_data_A_sram_head, cpu),
> +                       &per_cpu(used_l1_data_A_sram_head, cpu));
>  #else
>        ret = -1;
>  #endif
>
>        /* add mutex operation */
> -       spin_unlock_irqrestore(&l1_data_sram_lock, flags);
> +       spin_unlock_irqrestore(&per_cpu(l1_data_sram_lock, cpu), flags);
> +       put_cpu();
>
>        return ret;
>  }
> @@ -430,15 +456,18 @@ void *l1_data_B_sram_alloc(size_t size)
>  #if L1_DATA_B_LENGTH != 0
>        unsigned long flags;
>        void *addr;
> +       unsigned int cpu;
>
> +       cpu = get_cpu();
>        /* add mutex operation */
> -       spin_lock_irqsave(&l1_data_sram_lock, flags);
> +       spin_lock_irqsave(&per_cpu(l1_data_sram_lock, cpu), flags);
>
> -       addr = _sram_alloc(size, &free_l1_data_B_sram_head,
> -                       &used_l1_data_B_sram_head);
> +       addr = _sram_alloc(size, &per_cpu(free_l1_data_B_sram_head, cpu),
> +                       &per_cpu(used_l1_data_B_sram_head, cpu));
>
>        /* add mutex operation */
> -       spin_unlock_irqrestore(&l1_data_sram_lock, flags);
> +       spin_unlock_irqrestore(&per_cpu(l1_data_sram_lock, cpu), flags);
> +       put_cpu();
>
>        pr_debug("Allocated address in l1_data_B_sram_alloc is 0x%lx+0x%lx\n",
>                 (long unsigned int)addr, size);
> @@ -455,15 +484,18 @@ int l1_data_B_sram_free(const void *addr)
>  #if L1_DATA_B_LENGTH != 0
>        unsigned long flags;
>        int ret;
> +       unsigned int cpu;
>
> +       cpu = get_cpu();
>        /* add mutex operation */
> -       spin_lock_irqsave(&l1_data_sram_lock, flags);
> +       spin_lock_irqsave(&per_cpu(l1_data_sram_lock, cpu), flags);
>
> -       ret = _sram_free(addr, &free_l1_data_B_sram_head,
> -                       &used_l1_data_B_sram_head);
> +       ret = _sram_free(addr, &per_cpu(free_l1_data_B_sram_head, cpu),
> +                       &per_cpu(used_l1_data_B_sram_head, cpu));
>
>        /* add mutex operation */
> -       spin_unlock_irqrestore(&l1_data_sram_lock, flags);
> +       spin_unlock_irqrestore(&per_cpu(l1_data_sram_lock, cpu), flags);
> +       put_cpu();
>
>        return ret;
>  #else
> @@ -509,15 +541,18 @@ void *l1_inst_sram_alloc(size_t size)
>  #if L1_CODE_LENGTH != 0
>        unsigned long flags;
>        void *addr;
> +       unsigned int cpu;
>
> +       cpu = get_cpu();
>        /* add mutex operation */
> -       spin_lock_irqsave(&l1_inst_sram_lock, flags);
> +       spin_lock_irqsave(&per_cpu(l1_inst_sram_lock, cpu), flags);
>
> -       addr = _sram_alloc(size, &free_l1_inst_sram_head,
> -                       &used_l1_inst_sram_head);
> +       addr = _sram_alloc(size, &per_cpu(free_l1_inst_sram_head, cpu),
> +                       &per_cpu(used_l1_inst_sram_head, cpu));
>
>        /* add mutex operation */
> -       spin_unlock_irqrestore(&l1_inst_sram_lock, flags);
> +       spin_unlock_irqrestore(&per_cpu(l1_inst_sram_lock, cpu), flags);
> +       put_cpu();
>
>        pr_debug("Allocated address in l1_inst_sram_alloc is 0x%lx+0x%lx\n",
>                 (long unsigned int)addr, size);
> @@ -534,15 +569,18 @@ int l1_inst_sram_free(const void *addr)
>  #if L1_CODE_LENGTH != 0
>        unsigned long flags;
>        int ret;
> +       unsigned int cpu;
>
> +       cpu = get_cpu();
>        /* add mutex operation */
> -       spin_lock_irqsave(&l1_inst_sram_lock, flags);
> +       spin_lock_irqsave(&per_cpu(l1_inst_sram_lock, cpu), flags);
>
> -       ret = _sram_free(addr, &free_l1_inst_sram_head,
> -                       &used_l1_inst_sram_head);
> +       ret = _sram_free(addr, &per_cpu(free_l1_inst_sram_head, cpu),
> +                       &per_cpu(used_l1_inst_sram_head, cpu));
>
>        /* add mutex operation */
> -       spin_unlock_irqrestore(&l1_inst_sram_lock, flags);
> +       spin_unlock_irqrestore(&per_cpu(l1_inst_sram_lock, cpu), flags);
> +       put_cpu();
>
>        return ret;
>  #else
> @@ -556,15 +594,18 @@ void *l1sram_alloc(size_t size)
>  {
>        unsigned long flags;
>        void *addr;
> +       unsigned int cpu;
>
> +       cpu = get_cpu();
>        /* add mutex operation */
> -       spin_lock_irqsave(&l1sram_lock, flags);
> +       spin_lock_irqsave(&per_cpu(l1sram_lock, cpu), flags);
>
> -       addr = _sram_alloc(size, &free_l1_ssram_head,
> -                       &used_l1_ssram_head);
> +       addr = _sram_alloc(size, &per_cpu(free_l1_ssram_head, cpu),
> +                       &per_cpu(used_l1_ssram_head, cpu));
>
>        /* add mutex operation */
> -       spin_unlock_irqrestore(&l1sram_lock, flags);
> +       spin_unlock_irqrestore(&per_cpu(l1sram_lock, cpu), flags);
> +       put_cpu();
>
>        return addr;
>  }
> @@ -574,15 +615,18 @@ void *l1sram_alloc_max(size_t *psize)
>  {
>        unsigned long flags;
>        void *addr;
> +       unsigned int cpu;
>
> +       cpu = get_cpu();
>        /* add mutex operation */
> -       spin_lock_irqsave(&l1sram_lock, flags);
> +       spin_lock_irqsave(&per_cpu(l1sram_lock, cpu), flags);
>
> -       addr = _sram_alloc_max(&free_l1_ssram_head,
> -                       &used_l1_ssram_head, psize);
> +       addr = _sram_alloc_max(&per_cpu(free_l1_ssram_head, cpu),
> +                       &per_cpu(used_l1_ssram_head, cpu), psize);
>
>        /* add mutex operation */
> -       spin_unlock_irqrestore(&l1sram_lock, flags);
> +       spin_unlock_irqrestore(&per_cpu(l1sram_lock, cpu), flags);
> +       put_cpu();
>
>        return addr;
>  }
> @@ -592,15 +636,18 @@ int l1sram_free(const void *addr)
>  {
>        unsigned long flags;
>        int ret;
> +       unsigned int cpu;
>
> +       cpu = get_cpu();
>        /* add mutex operation */
> -       spin_lock_irqsave(&l1sram_lock, flags);
> +       spin_lock_irqsave(&per_cpu(l1sram_lock, cpu), flags);
>
> -       ret = _sram_free(addr, &free_l1_ssram_head,
> -                       &used_l1_ssram_head);
> +       ret = _sram_free(addr, &per_cpu(free_l1_ssram_head, cpu),
> +                       &per_cpu(used_l1_ssram_head, cpu));
>
>        /* add mutex operation */
> -       spin_unlock_irqrestore(&l1sram_lock, flags);
> +       spin_unlock_irqrestore(&per_cpu(l1sram_lock, cpu), flags);
> +       put_cpu();
>
>        return ret;
>  }
> @@ -761,33 +808,36 @@ static int sram_proc_read(char *buf, char **start, off_t offset, int count,
>                int *eof, void *data)
>  {
>        int len = 0;
> +       unsigned int cpu;
>
> -       if (_sram_proc_read(buf, &len, count, "Scratchpad",
> -                       &free_l1_ssram_head, &used_l1_ssram_head))
> -               goto not_done;
> +       for (cpu = 0; cpu < num_possible_cpus(); ++cpu) {
> +               if (_sram_proc_read(buf, &len, count, "Scratchpad",
> +                       &per_cpu(free_l1_ssram_head, cpu), &per_cpu(used_l1_ssram_head, cpu)))
> +                       goto not_done;
>  #if L1_DATA_A_LENGTH != 0
> -       if (_sram_proc_read(buf, &len, count, "L1 Data A",
> -                       &free_l1_data_A_sram_head,
> -                       &used_l1_data_A_sram_head))
> -               goto not_done;
> +               if (_sram_proc_read(buf, &len, count, "L1 Data A",
> +                       &per_cpu(free_l1_data_A_sram_head, cpu),
> +                       &per_cpu(used_l1_data_A_sram_head, cpu)))
> +                       goto not_done;
>  #endif
>  #if L1_DATA_B_LENGTH != 0
> -       if (_sram_proc_read(buf, &len, count, "L1 Data B",
> -                       &free_l1_data_B_sram_head,
> -                       &used_l1_data_B_sram_head))
> -               goto not_done;
> +               if (_sram_proc_read(buf, &len, count, "L1 Data B",
> +                       &per_cpu(free_l1_data_B_sram_head, cpu),
> +                       &per_cpu(used_l1_data_B_sram_head, cpu)))
> +                       goto not_done;
>  #endif
>  #if L1_CODE_LENGTH != 0
> -       if (_sram_proc_read(buf, &len, count, "L1 Instruction",
> -                       &free_l1_inst_sram_head, &used_l1_inst_sram_head))
> -               goto not_done;
> +               if (_sram_proc_read(buf, &len, count, "L1 Instruction",
> +                       &per_cpu(free_l1_inst_sram_head, cpu),
> +                       &per_cpu(used_l1_inst_sram_head, cpu)))
> +                       goto not_done;
>  #endif
> +       }
>  #if L2_LENGTH != 0
> -       if (_sram_proc_read(buf, &len, count, "L2",
> -                       &free_l2_sram_head, &used_l2_sram_head))
> +       if (_sram_proc_read(buf, &len, count, "L2", &free_l2_sram_head,
> +               &used_l2_sram_head))
>                goto not_done;
>  #endif
> -
>        *eof = 1;
>  not_done:
>        return len;
> --
> 1.5.6.3
>

^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: [PATCH 5/5] Blackfin arch: SMP supporting patchset: some other misc code
       [not found] ` <1226999108-13839-6-git-send-email-cooloney@kernel.org>
@ 2008-11-19  7:47   ` Bryan Wu
  0 siblings, 0 replies; 8+ messages in thread
From: Bryan Wu @ 2008-11-19  7:47 UTC (permalink / raw)
  To: torvalds, akpm, mingo; +Cc: linux-kernel, Graf Yang, Bryan Wu, linux-arch

Cc, linux-arch
-Bryan

On Tue, Nov 18, 2008 at 5:05 PM, Bryan Wu <cooloney@kernel.org> wrote:
> From: Graf Yang <graf.yang@analog.com>
>
> Blackfin dual core BF561 processor can support SMP like features.
> https://docs.blackfin.uclinux.org/doku.php?id=linux-kernel:smp-like
>
> In this patch, we provide SMP extend to some other misc code
>
> Singed-off-by: Graf Yang <graf.yang@analog.com>
> Signed-off-by: Bryan Wu <cooloney@kernel.org>
> ---
>  arch/blackfin/Kconfig                           |   32 +++++++++++++++++++++-
>  arch/blackfin/kernel/vmlinux.lds.S              |    4 +-
>  arch/blackfin/mach-bf518/include/mach/mem_map.h |   15 ++++++++++
>  arch/blackfin/mach-bf527/include/mach/mem_map.h |   15 ++++++++++
>  arch/blackfin/mach-bf533/include/mach/mem_map.h |   15 ++++++++++
>  arch/blackfin/mach-bf537/include/mach/mem_map.h |   15 ++++++++++
>  arch/blackfin/mach-bf538/include/mach/mem_map.h |   15 ++++++++++
>  arch/blackfin/mach-bf548/include/mach/mem_map.h |   15 ++++++++++
>  8 files changed, 122 insertions(+), 4 deletions(-)
>
> diff --git a/arch/blackfin/Kconfig b/arch/blackfin/Kconfig
> index 004c06c..7fc8a51 100644
> --- a/arch/blackfin/Kconfig
> +++ b/arch/blackfin/Kconfig
> @@ -200,6 +200,32 @@ config BF561
>
>  endchoice
>
> +config SMP
> +       depends on BF561
> +       bool "Symmetric multi-processing support"
> +       ---help---
> +         This enables support for systems with more than one CPU,
> +         like the dual core BF561. If you have a system with only one
> +         CPU, say N. If you have a system with more than one CPU, say Y.
> +
> +         If you don't know what to do here, say N.
> +
> +config NR_CPUS
> +       int
> +       depends on SMP
> +       default 2 if BF561
> +
> +config IRQ_PER_CPU
> +       bool
> +       depends on SMP
> +       default y
> +
> +config TICK_SOURCE_SYSTMR0
> +       bool
> +       select BFIN_GPTIMERS
> +       depends on SMP
> +       default y
> +
>  config BF_REV_MIN
>        int
>        default 0 if (BF51x || BF52x || BF54x)
> @@ -502,6 +528,7 @@ source kernel/Kconfig.hz
>
>  config GENERIC_TIME
>        bool "Generic time"
> +       depends on !SMP
>        default y
>
>  config GENERIC_CLOCKEVENTS
> @@ -576,6 +603,7 @@ endmenu
>
>
>  menu "Blackfin Kernel Optimizations"
> +       depends on !SMP
>
>  comment "Memory Optimizations"
>
> @@ -738,7 +766,6 @@ config BFIN_INS_LOWOVERHEAD
>
>  endmenu
>
> -
>  choice
>        prompt "Kernel executes from"
>        help
> @@ -804,7 +831,8 @@ config BFIN_ICACHE_LOCK
>  choice
>        prompt "Policy"
>        depends on BFIN_DCACHE
> -       default BFIN_WB
> +       default BFIN_WB if !SMP
> +       default BFIN_WT if SMP
>  config BFIN_WB
>        bool "Write back"
>        help
> diff --git a/arch/blackfin/kernel/vmlinux.lds.S b/arch/blackfin/kernel/vmlinux.lds.S
> index 7d12c66..2a48535 100644
> --- a/arch/blackfin/kernel/vmlinux.lds.S
> +++ b/arch/blackfin/kernel/vmlinux.lds.S
> @@ -109,7 +109,7 @@ SECTIONS
>  #endif
>
>                DATA_DATA
> -               *(.data.*)
> +               *(.data)
>                CONSTRUCTORS
>
>                /* make sure the init_task is aligned to the
> @@ -161,6 +161,7 @@ SECTIONS
>                *(.con_initcall.init)
>                ___con_initcall_end = .;
>        }
> +       PERCPU(4)
>        SECURITY_INIT
>        .init.ramfs :
>        {
> @@ -236,7 +237,6 @@ SECTIONS
>                . = ALIGN(4);
>                __ebss_l2 = .;
>        }
> -
>        /* Force trailing alignment of our init section so that when we
>         * free our init memory, we don't leave behind a partial page.
>         */
> diff --git a/arch/blackfin/mach-bf518/include/mach/mem_map.h b/arch/blackfin/mach-bf518/include/mach/mem_map.h
> index 10f678f..ac95d33 100644
> --- a/arch/blackfin/mach-bf518/include/mach/mem_map.h
> +++ b/arch/blackfin/mach-bf518/include/mach/mem_map.h
> @@ -99,4 +99,19 @@
>  #define L1_SCRATCH_START       0xFFB00000
>  #define L1_SCRATCH_LENGTH      0x1000
>
> +#define get_l1_scratch_start_cpu(cpu)          L1_SCRATCH_START
> +#define get_l1_code_start_cpu(cpu)             L1_CODE_START
> +#define get_l1_data_a_start_cpu(cpu)           L1_DATA_A_START
> +#define get_l1_data_b_start_cpu(cpu)           L1_DATA_B_START
> +#define get_l1_scratch_start()                 L1_SCRATCH_START
> +#define get_l1_code_start()                    L1_CODE_START
> +#define get_l1_data_a_start()                  L1_DATA_A_START
> +#define get_l1_data_b_start()                  L1_DATA_B_START
> +
> +#define GET_PDA_SAFE(preg)             \
> +       preg.l = _cpu_pda;              \
> +       preg.h = _cpu_pda;
> +
> +#define GET_PDA(preg, dreg)    GET_PDA_SAFE(preg)
> +
>  #endif                         /* _MEM_MAP_518_H_ */
> diff --git a/arch/blackfin/mach-bf527/include/mach/mem_map.h b/arch/blackfin/mach-bf527/include/mach/mem_map.h
> index ef46dc9..bd7fe0f 100644
> --- a/arch/blackfin/mach-bf527/include/mach/mem_map.h
> +++ b/arch/blackfin/mach-bf527/include/mach/mem_map.h
> @@ -99,4 +99,19 @@
>  #define L1_SCRATCH_START       0xFFB00000
>  #define L1_SCRATCH_LENGTH      0x1000
>
> +#define get_l1_scratch_start_cpu(cpu)          L1_SCRATCH_START
> +#define get_l1_code_start_cpu(cpu)             L1_CODE_START
> +#define get_l1_data_a_start_cpu(cpu)           L1_DATA_A_START
> +#define get_l1_data_b_start_cpu(cpu)           L1_DATA_B_START
> +#define get_l1_scratch_start()                 L1_SCRATCH_START
> +#define get_l1_code_start()                    L1_CODE_START
> +#define get_l1_data_a_start()                  L1_DATA_A_START
> +#define get_l1_data_b_start()                  L1_DATA_B_START
> +
> +#define GET_PDA_SAFE(preg)             \
> +       preg.l = _cpu_pda;              \
> +       preg.h = _cpu_pda;
> +
> +#define GET_PDA(preg, dreg)    GET_PDA_SAFE(preg)
> +
>  #endif                         /* _MEM_MAP_527_H_ */
> diff --git a/arch/blackfin/mach-bf533/include/mach/mem_map.h b/arch/blackfin/mach-bf533/include/mach/mem_map.h
> index 581fc6e..d5eaef2 100644
> --- a/arch/blackfin/mach-bf533/include/mach/mem_map.h
> +++ b/arch/blackfin/mach-bf533/include/mach/mem_map.h
> @@ -168,4 +168,19 @@
>  #define L1_SCRATCH_START       0xFFB00000
>  #define L1_SCRATCH_LENGTH      0x1000
>
> +#define get_l1_scratch_start_cpu(cpu)          L1_SCRATCH_START
> +#define get_l1_code_start_cpu(cpu)             L1_CODE_START
> +#define get_l1_data_a_start_cpu(cpu)           L1_DATA_A_START
> +#define get_l1_data_b_start_cpu(cpu)           L1_DATA_B_START
> +#define get_l1_scratch_start()                 L1_SCRATCH_START
> +#define get_l1_code_start()                    L1_CODE_START
> +#define get_l1_data_a_start()                  L1_DATA_A_START
> +#define get_l1_data_b_start()                  L1_DATA_B_START
> +
> +#define GET_PDA_SAFE(preg)             \
> +       preg.l = _cpu_pda;              \
> +       preg.h = _cpu_pda;
> +
> +#define GET_PDA(preg, dreg)    GET_PDA_SAFE(preg)
> +
>  #endif                         /* _MEM_MAP_533_H_ */
> diff --git a/arch/blackfin/mach-bf537/include/mach/mem_map.h b/arch/blackfin/mach-bf537/include/mach/mem_map.h
> index 5078b66..be4de76 100644
> --- a/arch/blackfin/mach-bf537/include/mach/mem_map.h
> +++ b/arch/blackfin/mach-bf537/include/mach/mem_map.h
> @@ -176,4 +176,19 @@
>  #define L1_SCRATCH_START       0xFFB00000
>  #define L1_SCRATCH_LENGTH      0x1000
>
> +#define get_l1_scratch_start_cpu(cpu)          L1_SCRATCH_START
> +#define get_l1_code_start_cpu(cpu)             L1_CODE_START
> +#define get_l1_data_a_start_cpu(cpu)           L1_DATA_A_START
> +#define get_l1_data_b_start_cpu(cpu)           L1_DATA_B_START
> +#define get_l1_scratch_start()                 L1_SCRATCH_START
> +#define get_l1_code_start()                    L1_CODE_START
> +#define get_l1_data_a_start()                  L1_DATA_A_START
> +#define get_l1_data_b_start()                  L1_DATA_B_START
> +
> +#define GET_PDA_SAFE(preg)             \
> +       preg.l = _cpu_pda;              \
> +       preg.h = _cpu_pda;
> +
> +#define GET_PDA(preg, dreg)    GET_PDA_SAFE(preg)
> +
>  #endif                         /* _MEM_MAP_537_H_ */
> diff --git a/arch/blackfin/mach-bf538/include/mach/mem_map.h b/arch/blackfin/mach-bf538/include/mach/mem_map.h
> index d65d430..c134057 100644
> --- a/arch/blackfin/mach-bf538/include/mach/mem_map.h
> +++ b/arch/blackfin/mach-bf538/include/mach/mem_map.h
> @@ -104,4 +104,19 @@
>  #define L1_SCRATCH_START       0xFFB00000
>  #define L1_SCRATCH_LENGTH      0x1000
>
> +#define get_l1_scratch_start_cpu(cpu)          L1_SCRATCH_START
> +#define get_l1_code_start_cpu(cpu)             L1_CODE_START
> +#define get_l1_data_a_start_cpu(cpu)           L1_DATA_A_START
> +#define get_l1_data_b_start_cpu(cpu)           L1_DATA_B_START
> +#define get_l1_scratch_start()                 L1_SCRATCH_START
> +#define get_l1_code_start()                    L1_CODE_START
> +#define get_l1_data_a_start()                  L1_DATA_A_START
> +#define get_l1_data_b_start()                  L1_DATA_B_START
> +
> +#define GET_PDA_SAFE(preg)             \
> +       preg.l = _cpu_pda;              \
> +       preg.h = _cpu_pda;
> +
> +#define GET_PDA(preg, dreg)    GET_PDA_SAFE(preg)
> +
>  #endif                         /* _MEM_MAP_538_H_ */
> diff --git a/arch/blackfin/mach-bf548/include/mach/mem_map.h b/arch/blackfin/mach-bf548/include/mach/mem_map.h
> index a222842..361eb0e 100644
> --- a/arch/blackfin/mach-bf548/include/mach/mem_map.h
> +++ b/arch/blackfin/mach-bf548/include/mach/mem_map.h
> @@ -108,4 +108,19 @@
>  #define L1_SCRATCH_START       0xFFB00000
>  #define L1_SCRATCH_LENGTH      0x1000
>
> +#define get_l1_scratch_start_cpu(cpu)          L1_SCRATCH_START
> +#define get_l1_code_start_cpu(cpu)             L1_CODE_START
> +#define get_l1_data_a_start_cpu(cpu)           L1_DATA_A_START
> +#define get_l1_data_b_start_cpu(cpu)           L1_DATA_B_START
> +#define get_l1_scratch_start()                 L1_SCRATCH_START
> +#define get_l1_code_start()                    L1_CODE_START
> +#define get_l1_data_a_start()                  L1_DATA_A_START
> +#define get_l1_data_b_start()                  L1_DATA_B_START
> +
> +#define GET_PDA_SAFE(preg)             \
> +       preg.l = _cpu_pda;              \
> +       preg.h = _cpu_pda;
> +
> +#define GET_PDA(preg, dreg)    GET_PDA_SAFE(preg)
> +
>  #endif/* _MEM_MAP_548_H_ */
> --
> 1.5.6.3
>

^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: [PATCH 1/5] Blackfin arch: SMP supporting patchset: BF561 related code
  2008-11-19  7:39     ` [PATCH 1/5] Blackfin arch: SMP supporting patchset: BF561 related code Bryan Wu
@ 2008-11-19  8:10       ` gyang
  0 siblings, 0 replies; 8+ messages in thread
From: gyang @ 2008-11-19  8:10 UTC (permalink / raw)
  To: Bryan Wu
  Cc: Andrew Morton, torvalds, mingo, linux-kernel, Mike Frysinger,
	linux-arch


在 2008-11-19三的 15:39 +0800,Bryan Wu写道:
> On Wed, Nov 19, 2008 at 2:56 PM, Andrew Morton
> <akpm@linux-foundation.org> wrote:
> > On Tue, 18 Nov 2008 17:05:04 +0800 Bryan Wu <cooloney@kernel.org> wrote:
> >
> >> From: Graf Yang <graf.yang@analog.com>
> >>
> >> Blackfin dual core BF561 processor can support SMP like features.
> >> https://docs.blackfin.uclinux.org/doku.php?id=linux-kernel:smp-like
> >>
> >> In this patch, we provide SMP extend to BF561 kernel code
> >>
> >>
> >> ...
> >>
> >> --- a/arch/blackfin/mach-bf561/include/mach/mem_map.h
> >> +++ b/arch/blackfin/mach-bf561/include/mach/mem_map.h
> >> @@ -85,4 +85,124 @@
> >>  #define L1_SCRATCH_START     COREA_L1_SCRATCH_START
> >>  #define L1_SCRATCH_LENGTH    0x1000
> >>
> >> +#ifndef __ASSEMBLY__
> >> +
> >> +#ifdef CONFIG_SMP
> >> +
> >> +#define get_l1_scratch_start_cpu(cpu)                                \
> >> +     ({ unsigned long __addr;                                \
> >> +        __addr = (cpu) ? COREB_L1_SCRATCH_START : COREA_L1_SCRATCH_START;\
> >> +        __addr; })
> >> +
> >> +#define get_l1_code_start_cpu(cpu)                           \
> >> +     ({ unsigned long __addr;                                \
> >> +        __addr = (cpu) ? COREB_L1_CODE_START : COREA_L1_CODE_START;  \
> >> +        __addr; })
> >> +
> >> +#define get_l1_data_a_start_cpu(cpu)                         \
> >> +     ({ unsigned long __addr;                                \
> >> +        __addr = (cpu) ? COREB_L1_DATA_A_START : COREA_L1_DATA_A_START;\
> >> +        __addr; })
> >> +
> >> +#define get_l1_data_b_start_cpu(cpu)                         \
> >> +     ({ unsigned long __addr;                                \
> >> +        __addr = (cpu) ? COREB_L1_DATA_B_START : COREA_L1_DATA_B_START;\
> >> +        __addr; })
> >> +
> >> +#define get_l1_scratch_start()       get_l1_scratch_start_cpu(blackfin_core_id())
> >> +#define get_l1_code_start()  get_l1_code_start_cpu(blackfin_core_id())
> >> +#define get_l1_data_a_start()        get_l1_data_a_start_cpu(blackfin_core_id())
> >> +#define get_l1_data_b_start()        get_l1_data_b_start_cpu(blackfin_core_id())
> >> +
> >> +#else /* !CONFIG_SMP */
> >> +#define get_l1_scratch_start_cpu(cpu)        L1_SCRATCH_START
> >> +#define get_l1_code_start_cpu(cpu)   L1_CODE_START
> >> +#define get_l1_data_a_start_cpu(cpu) L1_DATA_A_START
> >> +#define get_l1_data_b_start_cpu(cpu) L1_DATA_B_START
> >> +#define get_l1_scratch_start()               L1_SCRATCH_START
> >> +#define get_l1_code_start()          L1_CODE_START
> >> +#define get_l1_data_a_start()                L1_DATA_A_START
> >> +#define get_l1_data_b_start()                L1_DATA_B_START
> >> +#endif /* !CONFIG_SMP */
> >
> > grumble.  These didn't need to be implemented as macros and hence
> > shouldn't have been.
> >
> > Example:
> >
> >        int cpu = smp_processor_id();
> >        get_l1_scratch_start_cpu(cpu);
> >
> > that code should generate unused variable warnings on CONFIG_SMP=n.  If
> > it doesn't, you got lucky, because it _should_.
> >
> > Also
> >
> >        int cpu = smp_processor_id();
> >        get_l1_scratch_start_cpu(pcu);
> >
> > will happily compile and run with CONFIG_SMP=n.
> >
> >
> > macros=bad,bad,bad.
> >
> 
> Yes, I also prefer inline functions rather than macros here.
> Right, Graf?
OK!

> 
> >>
> >> ...
> >>
> >> --- /dev/null
> >> +++ b/arch/blackfin/mach-bf561/smp.c
> >> @@ -0,0 +1,182 @@
> >> +/*
> >> + * File:         arch/blackfin/mach-bf561/smp.c
> >> + * Author:       Philippe Gerum <rpm@xenomai.org>
> >> + *
> >> + *               Copyright 2007 Analog Devices Inc.
> >> + *
> >> + * This program is free software; you can redistribute it and/or modify
> >> + * it under the terms of the GNU General Public License as published by
> >> + * the Free Software Foundation; either version 2 of the License, or
> >> + * (at your option) any later version.
> >> + *
> >> + * This program is distributed in the hope that it will be useful,
> >> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> >> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> >> + * GNU General Public License for more details.
> >> + *
> >> + * You should have received a copy of the GNU General Public License
> >> + * along with this program; if not, see the file COPYING, or write
> >> + * to the Free Software Foundation, Inc.,
> >> + * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
> >> + */
> >> +
> >> +#include <linux/init.h>
> >> +#include <linux/kernel.h>
> >> +#include <linux/sched.h>
> >> +#include <linux/delay.h>
> >> +#include <asm/smp.h>
> >> +#include <asm/dma.h>
> >> +
> >> +#define COREB_SRAM_BASE  0xff600000
> >> +#define COREB_SRAM_SIZE  0x4000
> >> +
> >> +extern char coreb_trampoline_start, coreb_trampoline_end;
> >
> > OK, these are defined in .S and we do often put declarations for such
> > things in .c rather than in .h.  But I think it's better to put them in
> > .h anyway, to avoid possibly duplicated declarations in the future.
> >
> 
> Oh, I suggested Graf to run checkpatch.pl to find some issues before I
> sent out this patch.
> Should this issues be catched by checkpatch.pl?
OK, I will remove them.
> 
> 
> >> +static DEFINE_SPINLOCK(boot_lock);
> >> +
> >> +static cpumask_t cpu_callin_map;
> >> +
> >>
> >> ...
> >>
> >> +void __cpuinit platform_secondary_init(unsigned int cpu)
> >> +{
> >> +     local_irq_disable();
> >> +
> >> +     /* Clone setup for peripheral interrupt sources from CoreA. */
> >> +     bfin_write_SICB_IMASK0(bfin_read_SICA_IMASK0());
> >> +     bfin_write_SICB_IMASK1(bfin_read_SICA_IMASK1());
> >> +     SSYNC();
> >> +
> >> +     /* Clone setup for IARs from CoreA. */
> >> +     bfin_write_SICB_IAR0(bfin_read_SICA_IAR0());
> >> +     bfin_write_SICB_IAR1(bfin_read_SICA_IAR1());
> >> +     bfin_write_SICB_IAR2(bfin_read_SICA_IAR2());
> >> +     bfin_write_SICB_IAR3(bfin_read_SICA_IAR3());
> >> +     bfin_write_SICB_IAR4(bfin_read_SICA_IAR4());
> >> +     bfin_write_SICB_IAR5(bfin_read_SICA_IAR5());
> >> +     bfin_write_SICB_IAR6(bfin_read_SICA_IAR6());
> >> +     bfin_write_SICB_IAR7(bfin_read_SICA_IAR7());
> >> +     SSYNC();
> >> +
> >> +     local_irq_enable();
> >> +
> >> +     /* Calibrate loops per jiffy value. */
> >> +     calibrate_delay();
> >> +
> >> +     /* Store CPU-private information to the cpu_data array. */
> >> +     bfin_setup_cpudata(cpu);
> >> +
> >> +     /* We are done with local CPU inits, unblock the boot CPU. */
> >> +     cpu_set(cpu, cpu_callin_map);
> >> +     spin_lock(&boot_lock);
> >> +     spin_unlock(&boot_lock);
> >
> > Is this spin_lock()+spin_unlock() supposed to block until the secondary
> > CPU is running?  If so, I don't think it works.
> >
> 
> We can remove these 2 line spin_lock+spin_unlock and it also works.
> But maybe we will add some operation between spin_lock and spin_unlock
> here in the future,
> we'd like to keep them.
> 
> P.S. also forward this patch to linux-arch
> 
> Thanks
> -Bryan
> 
> >> +}
> >> +
> >>
> >> ...
> >>
> >
> >

^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: [PATCH 2/5] Blackfin arch: SMP supporting patchset: Blackfin header files and machine common code
       [not found]     ` <1227081170.24481.41.camel@dyang>
@ 2008-11-19  8:20       ` Bryan Wu
  0 siblings, 0 replies; 8+ messages in thread
From: Bryan Wu @ 2008-11-19  8:20 UTC (permalink / raw)
  To: gyang; +Cc: Andrew Morton, torvalds, mingo, LKML, linux-arch

On Wed, Nov 19, 2008 at 3:52 PM, gyang <graf.yang@analog.com> wrote:
>
> 在 2008-11-18二的 22:56 -0800,Andrew Morton写道:
>> On Tue, 18 Nov 2008 17:05:05 +0800 Bryan Wu <cooloney@kernel.org> wrote:
>>
>> > From: Graf Yang <graf.yang@analog.com>
>> >
>> > Blackfin dual core BF561 processor can support SMP like features.
>> > https://docs.blackfin.uclinux.org/doku.php?id=linux-kernel:smp-like
>> >
>> > In this patch, we provide SMP extend to Blackfin header files
>> > and machine common code
>> >
>> >
>> > ...
>> >
>> > +#define atomic_add_unless(v, a, u)                         \
>> > +({                                                         \
>> > +   int c, old;                                             \
>> > +   c = atomic_read(v);                                     \
>> > +   while (c != (u) && (old = atomic_cmpxchg((v), c, c + (a))) != c) \
>> > +           c = old;                                        \
>> > +   c != (u);                                               \
>> > +})
>>
>> The macro references its args multiple times and will do weird or
>> inefficient things when called with expressions which have
>> side-effects, or which do slow things.
>>
>> >
>> > ...
>> >
>> > +#include <asm/system.h>            /* save_flags */
>> > +
>> > +static inline void set_bit(int nr, volatile unsigned long *addr)
>> >  {
>> >     int *a = (int *)addr;
>> >     int mask;
>> > @@ -57,21 +91,23 @@ static __inline__ void clear_bit(int nr, volatile unsigned long *addr)
>> >     a += nr >> 5;
>> >     mask = 1 << (nr & 0x1f);
>> >     local_irq_save(flags);
>> > -   *a &= ~mask;
>> > +   *a |= mask;
>>
>> I think you just broke clear_bit().  Maybe I'm misreading the diff.
> OK, We have corrected it on our own tree.

Both the code and the patch are all right. Because the mess from diff,
we all misread it.

-Bryan

>>
>> >     local_irq_restore(flags);
>> >  }
>> >
>> >
>> > ...
>> >
>> > +#define smp_mb__before_clear_bit() barrier()
>> > +#define smp_mb__after_clear_bit()  barrier()
>> > +
>> > +static inline void __set_bit(int nr, volatile unsigned long *addr)
>> > +{
>> > +   int *a = (int *)addr;
>> > +   int mask;
>> > +
>> > +   a += nr >> 5;
>> > +   mask = 1 << (nr & 0x1f);
>> > +   *a |= mask;
>> > +}
>> > +
>> > +static inline void __clear_bit(int nr, volatile unsigned long *addr)
>> > +{
>> > +   int *a = (int *)addr;
>> > +   int mask;
>> > +
>> > +   a += nr >> 5;
>> > +   mask = 1 << (nr & 0x1f);
>> > +   *a &= ~mask;
>> > +}
>> > +
>> > +static inline void __change_bit(int nr, volatile unsigned long *addr)
>> > +{
>> > +   int mask;
>> > +   unsigned long *ADDR = (unsigned long *)addr;
>> > +
>> > +   ADDR += nr >> 5;
>> > +   mask = 1 << (nr & 31);
>> > +   *ADDR ^= mask;
>> > +}
>>
>> I'm surprised there isn't any generic code which can be used for the above.
>>
>> >
>> > ...
>> >
>>
>> Gad what a lot of code.  I don't think I have time to read it all, sorry.
>

^ permalink raw reply	[flat|nested] 8+ messages in thread

end of thread, other threads:[~2008-11-19  8:20 UTC | newest]

Thread overview: 8+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
     [not found] <1226999108-13839-1-git-send-email-cooloney@kernel.org>
     [not found] ` <20081118225621.540416ae.akpm@linux-foundation.org>
     [not found]   ` <386072610811182327x72df4615od53679b19cf49ed0@mail.gmail.com>
2008-11-19  7:28     ` [PATCH 0/5] Blackfin SMP like patchset Bryan Wu
     [not found] ` <1226999108-13839-2-git-send-email-cooloney@kernel.org>
     [not found]   ` <20081118225625.39e660ff.akpm@linux-foundation.org>
2008-11-19  7:39     ` [PATCH 1/5] Blackfin arch: SMP supporting patchset: BF561 related code Bryan Wu
2008-11-19  8:10       ` gyang
     [not found] ` <1226999108-13839-3-git-send-email-cooloney@kernel.org>
2008-11-19  7:44   ` [PATCH 2/5] Blackfin arch: SMP supporting patchset: Blackfin header files and machine common code Bryan Wu
     [not found]   ` <20081118225629.eddd23ae.akpm@linux-foundation.org>
     [not found]     ` <1227081170.24481.41.camel@dyang>
2008-11-19  8:20       ` Bryan Wu
     [not found] ` <1226999108-13839-4-git-send-email-cooloney@kernel.org>
2008-11-19  7:45   ` [PATCH 3/5] Blackfin arch: SMP supporting patchset: Blackfin CPLB related code Bryan Wu
     [not found] ` <1226999108-13839-5-git-send-email-cooloney@kernel.org>
2008-11-19  7:46   ` [PATCH 4/5] Blackfin arch: SMP supporting patchset: Blackfin kernel and memory management code Bryan Wu
     [not found] ` <1226999108-13839-6-git-send-email-cooloney@kernel.org>
2008-11-19  7:47   ` [PATCH 5/5] Blackfin arch: SMP supporting patchset: some other misc code Bryan Wu

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox