Blackfin: SMP: work around anomaly 05000491
authorSonic Zhang <sonic.zhang@analog.com>
Thu, 5 Aug 2010 07:49:26 +0000 (07:49 +0000)
committerMike Frysinger <vapier@gentoo.org>
Fri, 18 Mar 2011 08:01:04 +0000 (04:01 -0400)
In order to safely work around anomaly 05000491, we have to execute IFLUSH
from L1 instruction sram.  The trouble with multi-core systems is that all
L1 sram is visible only to the active core.  So we can't just place the
functions into L1 and call it directly.  We need to setup a jump table and
place the entry point in external memory.  This will call the right func
based on the active core.

In the process, convert from the manual relocation of a small bit of code
into Core B's L1 to the more general framework we already have in place
for loading arbitrary pieces of code into L1.

Signed-off-by: Sonic Zhang <sonic.zhang@analog.com>
Signed-off-by: Mike Frysinger <vapier@gentoo.org>
arch/blackfin/Kconfig
arch/blackfin/include/asm/smp.h
arch/blackfin/kernel/setup.c
arch/blackfin/kernel/vmlinux.lds.S
arch/blackfin/mach-bf561/secondary.S
arch/blackfin/mach-bf561/smp.c
arch/blackfin/mach-common/cache.S
arch/blackfin/mach-common/smp.c

index 0f34ec58bac3fadb61f973344f22ca2ea5bcd9f6..c4cda6f52b61ea47ce59dfba7502ec125af84e13 100644 (file)
@@ -850,7 +850,6 @@ config CPLB_SWITCH_TAB_L1
 config ICACHE_FLUSH_L1
        bool "Locate icache flush funcs in L1 Inst Memory"
        default y
-       depends on !SMP
        help
          If enabled, the Blackfin icache flushing functions are linked
          into L1 instruction memory.
index 9dd4873752479cb9bf14cb35882a1da363786c21..af6c0aa79bae9435ea5dff4f92f2c945051352aa 100644 (file)
 
 #define raw_smp_processor_id()  blackfin_core_id()
 
-extern char coreb_trampoline_start, coreb_trampoline_end;
+extern void bfin_relocate_coreb_l1_mem(void);
+
+#if defined(CONFIG_SMP) && defined(CONFIG_ICACHE_FLUSH_L1)
+asmlinkage void blackfin_icache_flush_range_l1(unsigned long *ptr);
+extern unsigned long blackfin_iflush_l1_entry[NR_CPUS];
+#endif
 
 struct corelock_slot {
        int lock;
index ac71dc15cbdb501d82e6c7fe1301ae738f444368..805c6132c7796b26a632c85df14b8a58d508e1de 100644 (file)
@@ -215,11 +215,48 @@ void __init bfin_relocate_l1_mem(void)
 
        early_dma_memcpy_done();
 
+#if defined(CONFIG_SMP) && defined(CONFIG_ICACHE_FLUSH_L1)
+       blackfin_iflush_l1_entry[0] = (unsigned long)blackfin_icache_flush_range_l1;
+#endif
+
        /* if necessary, copy L2 text/data to L2 SRAM */
        if (L2_LENGTH && l2_len)
                memcpy(_stext_l2, _l2_lma, l2_len);
 }
 
+#ifdef CONFIG_SMP
+void __init bfin_relocate_coreb_l1_mem(void)
+{
+       unsigned long text_l1_len = (unsigned long)_text_l1_len;
+       unsigned long data_l1_len = (unsigned long)_data_l1_len;
+       unsigned long data_b_l1_len = (unsigned long)_data_b_l1_len;
+
+       blackfin_dma_early_init();
+
+       /* if necessary, copy L1 text to L1 instruction SRAM */
+       if (L1_CODE_LENGTH && text_l1_len)
+               early_dma_memcpy((void *)COREB_L1_CODE_START, _text_l1_lma,
+                               text_l1_len);
+
+       /* if necessary, copy L1 data to L1 data bank A SRAM */
+       if (L1_DATA_A_LENGTH && data_l1_len)
+               early_dma_memcpy((void *)COREB_L1_DATA_A_START, _data_l1_lma,
+                               data_l1_len);
+
+       /* if necessary, copy L1 data B to L1 data bank B SRAM */
+       if (L1_DATA_B_LENGTH && data_b_l1_len)
+               early_dma_memcpy((void *)COREB_L1_DATA_B_START, _data_b_l1_lma,
+                               data_b_l1_len);
+
+       early_dma_memcpy_done();
+
+#ifdef CONFIG_ICACHE_FLUSH_L1
+       blackfin_iflush_l1_entry[1] = (unsigned long)blackfin_icache_flush_range_l1 -
+                       (unsigned long)_stext_l1 + COREB_L1_CODE_START;
+#endif
+}
+#endif
+
 #ifdef CONFIG_ROMKERNEL
 void __init bfin_relocate_xip_data(void)
 {
index f5fd234e57732efda271a68d90a6bd011e3150eb..854fa49f1c3ec964f7f105fd9c0019d133cca420 100644 (file)
@@ -176,6 +176,7 @@ SECTIONS
        {
                . = ALIGN(4);
                __stext_l1 = .;
+               *(.l1.text.head)
                *(.l1.text)
 #ifdef CONFIG_SCHEDULE_L1
                SCHED_TEXT
index 148e50764555f9dcdf607d4cbecf61e529fdbe7e..4c462838f4e1fc0f45123d6415fb445441d7d6c3 100644 (file)
 #include <asm/asm-offsets.h>
 #include <asm/trace.h>
 
-__INIT
+/*
+ * This code must come first as CoreB is hardcoded (in hardware)
+ * to start at the beginning of its L1 instruction memory.
+ */
+.section .l1.text.head
 
 /* Lay the initial stack into the L1 scratch area of Core B */
 #define INITIAL_STACK  (COREB_L1_SCRATCH_START + L1_SCRATCH_LENGTH - 12)
@@ -160,7 +164,6 @@ ENTRY(_coreb_trampoline_start)
 .LWAIT_HERE:
        jump .LWAIT_HERE;
 ENDPROC(_coreb_trampoline_start)
-ENTRY(_coreb_trampoline_end)
 
 #ifdef CONFIG_HOTPLUG_CPU
 .section ".text"
index 1074a7ef81c7ec3b530ef932212e9f5a2af9e238..82b94e1378860a49f77c087f3b08b01059bd5120 100644 (file)
@@ -30,18 +30,11 @@ void __init platform_init_cpus(void)
 
 void __init platform_prepare_cpus(unsigned int max_cpus)
 {
-       int len;
-
-       len = &coreb_trampoline_end - &coreb_trampoline_start + 1;
-       BUG_ON(len > L1_CODE_LENGTH);
-
-       dma_memcpy((void *)COREB_L1_CODE_START, &coreb_trampoline_start, len);
+       bfin_relocate_coreb_l1_mem();
 
        /* Both cores ought to be present on a bf561! */
        cpu_set(0, cpu_present_map); /* CoreA */
        cpu_set(1, cpu_present_map); /* CoreB */
-
-       printk(KERN_INFO "CoreB bootstrap code to SRAM %p via DMA.\n", (void *)COREB_L1_CODE_START);
 }
 
 int __init setup_profiling_timer(unsigned int multiplier) /* not supported */
index 85aadeb76658535954da90d62ce396beb5fcfea9..9f4dd35bfd743fbc91cf680c6f4cc90867fc0a05 100644 (file)
 #endif
 
 /* Invalidate all instruction cache lines assocoiated with this memory area */
+#ifdef CONFIG_SMP
+# define _blackfin_icache_flush_range _blackfin_icache_flush_range_l1
+#endif
 ENTRY(_blackfin_icache_flush_range)
        do_flush IFLUSH
 ENDPROC(_blackfin_icache_flush_range)
 
+#ifdef CONFIG_SMP
+.text
+# undef _blackfin_icache_flush_range
+ENTRY(_blackfin_icache_flush_range)
+       p0.L = LO(DSPID);
+       p0.H = HI(DSPID);
+       r3 = [p0];
+       r3 = r3.b (z);
+       p2 = r3;
+       p0.L = _blackfin_iflush_l1_entry;
+       p0.H = _blackfin_iflush_l1_entry;
+       p0 = p0 + (p2 << 2);
+       p1 = [p0];
+       jump (p1);
+ENDPROC(_blackfin_icache_flush_range)
+#endif
+
 #ifdef CONFIG_DCACHE_FLUSH_L1
 .section .l1.text
 #else
index 5f7617d669055fdee72d67e42a0293994aba5308..6e17a265c4d3158761450463b3c5a66e63a39bab 100644 (file)
  */
 struct corelock_slot corelock __attribute__ ((__section__(".l2.bss")));
 
+#ifdef CONFIG_ICACHE_FLUSH_L1
+unsigned long blackfin_iflush_l1_entry[NR_CPUS];
+#endif
+
 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;