85xx: Added support for multicore boot mechanism
authorKumar Gala <galak@kernel.crashing.org>
Thu, 17 Jan 2008 22:48:33 +0000 (16:48 -0600)
committerAndrew Fleming-AFLEMING <afleming@freescale.com>
Wed, 26 Mar 2008 16:43:03 +0000 (11:43 -0500)
Added the cpu command that provides a generic mechanism to get status,
reset, and release secondary cores in multicore processors.

Added support for using the ePAPR defined spin-table mechanism on 85xx.

Signed-off-by: Kumar Gala <galak@kernel.crashing.org>
common/Makefile
common/cmd_mp.c [new file with mode: 0644]
cpu/mpc85xx/Makefile
cpu/mpc85xx/cpu_init.c
cpu/mpc85xx/fdt.c
cpu/mpc85xx/mp.c [new file with mode: 0644]
cpu/mpc85xx/mp.h [new file with mode: 0644]
cpu/mpc85xx/release.S [new file with mode: 0644]
include/asm-ppc/immap_85xx.h
include/common.h

index 1c81fcfe4f4c5fd01152a24d9202eec3229208bc..382dd018001b76d8203664f4f5f5893d3743093d 100644 (file)
@@ -140,6 +140,7 @@ COBJS-y += crc16.o
 COBJS-y += xyzModem.o
 COBJS-y += cmd_mac.o
 COBJS-$(CONFIG_CMD_MFSL) += cmd_mfsl.o
+COBJS-$(CONFIG_MP) += cmd_mp.o
 
 COBJS  := $(COBJS-y)
 SRCS   := $(AOBJS:.o=.S) $(COBJS:.o=.c)
diff --git a/common/cmd_mp.c b/common/cmd_mp.c
new file mode 100644 (file)
index 0000000..d96e6a3
--- /dev/null
@@ -0,0 +1,96 @@
+/*
+ * Copyright 2008 Freescale Semiconductor, Inc.
+ *
+ * See file CREDITS for list of people who contributed to this
+ * project.
+ *
+ * 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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
+ * MA 02111-1307 USA
+ */
+
+#include <common.h>
+#include <command.h>
+
+int
+cpu_cmd(cmd_tbl_t *cmdtp, int flag, int argc, char *argv[])
+{
+       unsigned long cpuid, val = 0;
+
+       if (argc < 3) {
+               printf ("Usage:\n%s\n", cmdtp->usage);
+               return 1;
+       }
+
+       cpuid = simple_strtoul(argv[1], NULL, 10);
+       if (cpuid >= CONFIG_NR_CPUS) {
+               printf ("Core num: %d is out of range[0..%d]\n",
+                               cpuid, CONFIG_NR_CPUS - 1);
+               return 1;
+       }
+
+
+       if (argc == 3) {
+               if (strncmp(argv[2], "reset", 5) == 0) {
+                       cpu_reset(cpuid);
+               } else if (strncmp(argv[2], "status", 6) == 0) {
+                       cpu_status(cpuid);
+               } else {
+                       printf ("Usage:\n%s\n", cmdtp->usage);
+                       return 1;
+               }
+               return 0;
+       }
+
+       /* 4 or greater, make sure its release */
+       if (strncmp(argv[2], "release", 7) != 0) {
+               printf ("Usage:\n%s\n", cmdtp->usage);
+               return 1;
+       }
+
+       val = simple_strtoul(argv[3], NULL, 16);
+
+       if (cpu_release(cpuid, val, argc - 4, argv + 4)) {
+               printf ("Usage:\n%s\n", cmdtp->usage);
+               return 1;
+       }
+
+       return 0;
+}
+
+#ifdef CONFIG_PPC
+#define CPU_ARCH_HELP \
+       "                         [args] : <pir> <r3> <r4> <r6> <r7>\n" \
+       "                                   pir - processor id (if writeable)\n" \
+       "                                    r3 - value for gpr 3\n" \
+       "                                    r4 - value for gpr 4\n" \
+       "                                    r6 - value for gpr 6\n" \
+       "                                    r7 - value for gpr 7\n" \
+       "\n" \
+       "     Use '-' for any arg if you want the default value.\n" \
+       "     Default for r3, r4, r7 is 0, r6 is 0x65504150\n" \
+       "\n" \
+       "     When cpu <num> is released r5 = 0 per the ePAPR spec.\n"
+#endif
+
+U_BOOT_CMD(
+       cpu, CFG_MAXARGS, 1, cpu_cmd,
+       "cpu     - Multiprocessor CPU boot manipulation and release\n",
+           "<num> reset                 - Reset cpu <num>\n"
+       "cpu <num> status                - Status of cpu <num>\n"
+       "cpu <num> release <addr> [args] - Release cpu <num> at <addr> with [args]\n"
+#ifdef CPU_ARCH_HELP
+       CPU_ARCH_HELP
+#endif
+       );
index 2205dca02437efb63f38f3152f080c31b27c60f6..adbc585827ab22fe6fdce611e0aef60222955467 100644 (file)
@@ -29,6 +29,9 @@ include $(TOPDIR)/config.mk
 LIB    = $(obj)lib$(CPU).a
 
 START  = start.o resetvec.o
+SOBJS-$(CONFIG_MP) += release.o
+SOBJS  = $(SOBJS-y)
+COBJS-$(CONFIG_MP) += mp.o
 COBJS-$(CONFIG_OF_LIBFDT) += fdt.o
 COBJS  = traps.o cpu.o cpu_init.o speed.o interrupts.o tlb.o \
          pci.o serial_scc.o commproc.o ether_fcc.o spd_sdram.o qe_io.o \
index 5f02e0e21bb036354abe512298f51fd3082f9bcd..fce0c4805ad538142667f747c0db31e88da00f74 100644 (file)
@@ -33,6 +33,7 @@
 #include <asm/io.h>
 #include <asm/mmu.h>
 #include <asm/fsl_law.h>
+#include "mp.h"
 
 DECLARE_GLOBAL_DATA_PTR;
 
@@ -328,5 +329,8 @@ int cpu_init_r(void)
        qe_reset();
 #endif
 
+#if defined(CONFIG_MP)
+       setup_mp();
+#endif
        return 0;
 }
index a6b014cec06f7475ba898edd1e1eb88fe5dac4f2..43df1c70b2b282958bab41c49fe34e7d6eb1f9b1 100644 (file)
 #include <fdt_support.h>
 
 extern void ft_qe_setup(void *blob);
+#ifdef CONFIG_MP
+#include "mp.h"
+DECLARE_GLOBAL_DATA_PTR;
+
+void ft_fixup_cpu(void *blob, u64 memory_limit)
+{
+       int off;
+       ulong spin_tbl_addr = get_spin_addr();
+       u32 bootpg, id = get_my_id();
+
+       /* if we have 4G or more of memory, put the boot page at 4Gb-4k */
+       if ((u64)gd->ram_size > 0xfffff000)
+               bootpg = 0xfffff000;
+       else
+               bootpg = gd->ram_size - 4096;
+
+       off = fdt_node_offset_by_prop_value(blob, -1, "device_type", "cpu", 4);
+       while (off != -FDT_ERR_NOTFOUND) {
+               u32 *reg = (u32 *)fdt_getprop(blob, off, "reg", 0);
+
+               if (reg) {
+                       if (*reg == id) {
+                               fdt_setprop_string(blob, off, "status", "okay");
+                       } else {
+                               u32 val = *reg * 24 + spin_tbl_addr;
+                               val = cpu_to_fdt32(val);
+                               fdt_setprop_string(blob, off, "status",
+                                                               "disabled");
+                               fdt_setprop_string(blob, off, "enable-method",
+                                                               "spin-table");
+                               fdt_setprop(blob, off, "cpu-release-addr",
+                                               &val, sizeof(val));
+                       }
+               } else {
+                       printf ("cpu NULL\n");
+               }
+               off = fdt_node_offset_by_prop_value(blob, off,
+                               "device_type", "cpu", 4);
+       }
+
+       /* Reserve the boot page so OSes dont use it */
+       if ((u64)bootpg < memory_limit) {
+               off = fdt_add_mem_rsv(blob, bootpg, (u64)4096);
+               if (off < 0)
+                       printf("%s: %s\n", __FUNCTION__, fdt_strerror(off));
+       }
+}
+#endif
 
 void ft_cpu_setup(void *blob, bd_t *bd)
 {
@@ -62,4 +110,8 @@ void ft_cpu_setup(void *blob, bd_t *bd)
 #endif
 
        fdt_fixup_memory(blob, (u64)bd->bi_memstart, (u64)bd->bi_memsize);
+
+#ifdef CONFIG_MP
+       ft_fixup_cpu(blob, (u64)bd->bi_memstart + (u64)bd->bi_memsize);
+#endif
 }
diff --git a/cpu/mpc85xx/mp.c b/cpu/mpc85xx/mp.c
new file mode 100644 (file)
index 0000000..aa91cea
--- /dev/null
@@ -0,0 +1,190 @@
+/*
+ * Copyright 2008 Freescale Semiconductor.
+ *
+ * See file CREDITS for list of people who contributed to this
+ * project.
+ *
+ * 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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
+ * MA 02111-1307 USA
+ */
+
+#include <common.h>
+#include <asm/processor.h>
+#include <ioports.h>
+#include <asm/io.h>
+#include "mp.h"
+
+DECLARE_GLOBAL_DATA_PTR;
+
+#define BOOT_ENTRY_ADDR        0
+#define BOOT_ENTRY_PIR 1
+#define BOOT_ENTRY_R3  2
+#define BOOT_ENTRY_R4  3
+#define BOOT_ENTRY_R6  4
+#define BOOT_ENTRY_R7  5
+#define NUM_BOOT_ENTRY 6
+
+u32 get_my_id()
+{
+       return mfspr(SPRN_PIR);
+}
+
+int cpu_reset(int nr)
+{
+       volatile ccsr_pic_t *pic = (void *)(CFG_MPC85xx_PIC_ADDR);
+       out_be32(&pic->pir, 1 << nr);
+       (void)in_be32(&pic->pir);
+       out_be32(&pic->pir, 0x0);
+
+       return 0;
+}
+
+int cpu_status(int nr)
+{
+       u32 *table, id = get_my_id();
+
+       if (nr == id) {
+               table = (u32 *)get_spin_addr();
+               printf("table base @ 0x%08x\n", table);
+       } else {
+               table = (u32 *)get_spin_addr() + nr * NUM_BOOT_ENTRY;
+               printf("Running on cpu %d\n", id);
+               printf("\n");
+               printf("table @ 0x%08x:\n", table);
+               printf("   addr - 0x%08x\n", table[BOOT_ENTRY_ADDR]);
+               printf("   pir  - 0x%08x\n", table[BOOT_ENTRY_PIR]);
+               printf("   r3   - 0x%08x\n", table[BOOT_ENTRY_R3]);
+               printf("   r4   - 0x%08x\n", table[BOOT_ENTRY_R4]);
+               printf("   r6   - 0x%08x\n", table[BOOT_ENTRY_R6]);
+               printf("   r7   - 0x%08x\n", table[BOOT_ENTRY_R7]);
+       }
+
+       return 0;
+}
+
+int cpu_release(int nr, unsigned long boot_addr, int argc, char *argv[])
+{
+       u32 i, val, *table = (u32 *)get_spin_addr() + nr * NUM_BOOT_ENTRY;
+
+       if (nr == get_my_id()) {
+               printf("Invalid to release the boot core.\n\n");
+               return 1;
+       }
+
+       if (argc != 5) {
+               printf("Invalid number of arguments to release.\n\n");
+               return 1;
+       }
+
+       /* handle pir, r3, r4, r6, r7 */
+       for (i = 0; i < 5; i++) {
+               if (argv[i][0] != '-') {
+                       val = simple_strtoul(argv[i], NULL, 16);
+                       table[i+BOOT_ENTRY_PIR] = val;
+               }
+       }
+
+       table[BOOT_ENTRY_ADDR] = boot_addr;
+
+       return 0;
+}
+
+ulong get_spin_addr(void)
+{
+       extern ulong __secondary_start_page;
+       extern ulong __spin_table;
+
+       ulong addr =
+               (ulong)&__spin_table - (ulong)&__secondary_start_page;
+       addr += 0xfffff000;
+
+       return addr;
+}
+
+static void pq3_mp_up(unsigned long bootpg)
+{
+       u32 up, cpu_up_mask, whoami;
+       u32 *table = (u32 *)get_spin_addr();
+       volatile u32 bpcr;
+       volatile ccsr_local_ecm_t *ecm = (void *)(CFG_MPC85xx_ECM_ADDR);
+       volatile ccsr_gur_t *gur = (void *)(CFG_MPC85xx_GUTS_ADDR);
+       volatile ccsr_pic_t *pic = (void *)(CFG_MPC85xx_PIC_ADDR);
+       u32 devdisr;
+       int timeout = 10;
+
+       whoami = in_be32(&pic->whoami);
+       out_be32(&ecm->bptr, 0x80000000 | (bootpg >> 12));
+
+       /* disable time base at the platform */
+       devdisr = in_be32(&gur->devdisr);
+       if (whoami)
+               devdisr |= MPC85xx_DEVDISR_TB0;
+       else
+               devdisr |= MPC85xx_DEVDISR_TB1;
+       out_be32(&gur->devdisr, devdisr);
+
+       /* release the hounds */
+       up = ((1 << CONFIG_NR_CPUS) - 1);
+       bpcr = in_be32(&ecm->eebpcr);
+       bpcr |= (up << 24);
+       out_be32(&ecm->eebpcr, bpcr);
+       asm("sync; isync; msync");
+
+       cpu_up_mask = 1 << whoami;
+       /* wait for everyone */
+       while (timeout) {
+               int i;
+               for (i = 1; i < CONFIG_NR_CPUS; i++) {
+                       if (table[i * NUM_BOOT_ENTRY])
+                               cpu_up_mask |= (1 << i);
+               };
+
+               if ((cpu_up_mask & up) == up)
+                       break;
+
+               udelay(100);
+               timeout--;
+       }
+
+       /* enable time base at the platform */
+       if (whoami)
+               devdisr |= MPC85xx_DEVDISR_TB1;
+       else
+               devdisr |= MPC85xx_DEVDISR_TB0;
+       out_be32(&gur->devdisr, devdisr);
+       mtspr(SPRN_TBWU, 0);
+       mtspr(SPRN_TBWL, 0);
+
+       devdisr &= ~(MPC85xx_DEVDISR_TB0 | MPC85xx_DEVDISR_TB1);
+       out_be32(&gur->devdisr, devdisr);
+}
+
+void setup_mp(void)
+{
+       extern ulong __secondary_start_page;
+       ulong fixup = (ulong)&__secondary_start_page;
+       u32 bootpg;
+
+       /* if we have 4G or more of memory, put the boot page at 4Gb-4k */
+       if ((u64)gd->ram_size > 0xfffff000)
+               bootpg = 0xfffff000;
+       else
+               bootpg = gd->ram_size - 4096;
+
+       memcpy((void *)bootpg, (void *)fixup, 4096);
+       flush_cache(bootpg, 4096);
+
+       pq3_mp_up(bootpg);
+}
diff --git a/cpu/mpc85xx/mp.h b/cpu/mpc85xx/mp.h
new file mode 100644 (file)
index 0000000..d9fbb82
--- /dev/null
@@ -0,0 +1,8 @@
+#ifndef __MPC85XX_MP_H_
+#define __MPC85XX_MP_H_
+
+ulong get_spin_addr(void);
+void setup_mp(void);
+u32 get_my_id(void);
+
+#endif
diff --git a/cpu/mpc85xx/release.S b/cpu/mpc85xx/release.S
new file mode 100644 (file)
index 0000000..fe1775c
--- /dev/null
@@ -0,0 +1,148 @@
+#include <config.h>
+#include <mpc85xx.h>
+#include <version.h>
+
+#define _LINUX_CONFIG_H 1      /* avoid reading Linux autoconf.h file  */
+
+#include <ppc_asm.tmpl>
+#include <ppc_defs.h>
+
+#include <asm/cache.h>
+#include <asm/mmu.h>
+
+/* To boot secondary cpus, we need a place for them to start up.
+ * Normally, they start at 0xfffffffc, but that's usually the
+ * firmware, and we don't want to have to run the firmware again.
+ * Instead, the primary cpu will set the BPTR to point here to
+ * this page.  We then set up the core, and head to
+ * start_secondary.  Note that this means that the code below
+ * must never exceed 1023 instructions (the branch at the end
+ * would then be the 1024th).
+ */
+       .globl  __secondary_start_page
+       .align  12
+__secondary_start_page:
+/* First do some preliminary setup */
+       lis     r3, HID0_EMCP@h         /* enable machine check */
+       ori     r3,r3,HID0_TBEN@l       /* enable Timebase */
+#ifdef CONFIG_PHYS_64BIT
+       ori     r3,r3,HID0_ENMAS7@l     /* enable MAS7 updates */
+#endif
+       mtspr   SPRN_HID0,r3
+
+       li      r3,(HID1_ASTME|HID1_ABE)@l      /* Addr streaming & broadcast */
+       mtspr   SPRN_HID1,r3
+
+       /* Enable branch prediction */
+       li      r3,0x201
+       mtspr   SPRN_BUCSR,r3
+
+       /* Enable/invalidate the I-Cache */
+       mfspr   r0,SPRN_L1CSR1
+       ori     r0,r0,(L1CSR1_ICFI|L1CSR1_ICE)
+       mtspr   SPRN_L1CSR1,r0
+       isync
+
+       /* Enable/invalidate the D-Cache */
+       mfspr   r0,SPRN_L1CSR0
+       ori     r0,r0,(L1CSR0_DCFI|L1CSR0_DCE)
+       msync
+       isync
+       mtspr   SPRN_L1CSR0,r0
+       isync
+
+#define toreset(x) (x - __secondary_start_page + 0xfffff000)
+
+       /* get our PIR to figure out our table entry */
+       lis     r3,toreset(__spin_table)@h
+       ori     r3,r3,toreset(__spin_table)@l
+
+       /* r9 has the base address for the entry */
+       mfspr   r0,SPRN_PIR
+       mr      r4,r0
+       slwi    r8,r4,4
+       slwi    r9,r4,3
+       add     r8,r8,r9
+       add     r9,r3,r8
+
+#define EPAPR_MAGIC    (0x65504150)
+#define ENTRY_ADDR     0
+#define ENTRY_PIR      4
+#define ENTRY_R3       8
+#define ENTRY_R4       12
+#define ENTRY_R6       16
+#define ENTRY_R7       20
+
+       /* setup the entry */
+       li      r4,0
+       li      r8,1
+       lis     r6,EPAPR_MAGIC@h
+       ori     r6,r6,EPAPR_MAGIC@l
+       stw     r0,ENTRY_PIR(r9)
+       stw     r8,ENTRY_ADDR(r9)
+       stw     r4,ENTRY_R3(r9)
+       stw     r4,ENTRY_R4(r9)
+       stw     r6,ENTRY_R6(r9)
+       stw     r4,ENTRY_R7(r9)
+
+       /* spin waiting for addr */
+1:     lwz     r4,ENTRY_ADDR(r9)
+       andi.   r11,r4,1
+       bne     1b
+
+       /* setup branch addr */
+       mtctr   r4
+
+       /* mark the entry as released */
+       li      r8,3
+       stw     r8,ENTRY_ADDR(r9)
+
+       /* mask by ~64M to setup our tlb we will jump to */
+       rlwinm  r8,r4,0,0,5
+
+       /* setup r3, r5, r6, r7 */
+       lwz     r3,ENTRY_R3(r9)
+       lwz     r4,ENTRY_R4(r9)
+       li      r5,0
+       lwz     r6,ENTRY_R6(r9)
+       lwz     r7,ENTRY_R7(r9)
+
+       /* load up the pir */
+       lwz     r0,ENTRY_PIR(r9)
+       mtspr   SPRN_PIR,r0
+       mfspr   r0,SPRN_PIR
+       stw     r0,ENTRY_PIR(r9)
+
+/*
+ * Coming here, we know the cpu has one TLB mapping in TLB1[0]
+ * which maps 0xfffff000-0xffffffff one-to-one.  We set up a
+ * second mapping that maps addr 1:1 for 64M, and then we jump to
+ * addr
+ */
+       lis     r9,(MAS0_TLBSEL(1)|MAS0_ESEL(1))@h
+       mtspr   SPRN_MAS0,r9
+       lis     r9,(MAS1_VALID|MAS1_IPROT)@h
+       ori     r9,r9,(MAS1_TSIZE(BOOKE_PAGESZ_64M))@l
+       mtspr   SPRN_MAS1,r9
+       /* WIMGE = 0b00000 for now */
+       mtspr   SPRN_MAS2,r8
+       ori     r8,r8,(MAS3_SX|MAS3_SW|MAS3_SR)
+       mtspr   SPRN_MAS3,r8
+       tlbwe
+
+/* Now we have another mapping for this page, so we jump to that
+ * mapping
+ */
+       bctr
+
+       .align 3
+       .globl __spin_table
+__spin_table:
+       .space CONFIG_NR_CPUS*24
+
+       /* Fill in the empty space.  The actual reset vector is
+        * the last word of the page */
+__secondary_start_code_end:
+       .space 4092 - (__secondary_start_code_end - __secondary_start_page)
+__secondary_reset_vector:
+       b       __secondary_start_page
index d769d7012035c39f322ab5483156e729d9ce2378..712a0788f8f03f0c077d5e4abfa0e79a69332dc3 100644 (file)
@@ -1578,7 +1578,11 @@ typedef struct ccsr_gur {
 #define MPC85xx_DEVDISR_RMSG           0x00040000
 #define MPC85xx_DEVDISR_DDR            0x00010000
 #define MPC85xx_DEVDISR_CPU            0x00008000
+#define MPC85xx_DEVDISR_CPU0           MPC85xx_DEVDISR_CPU
 #define MPC85xx_DEVDISR_TB             0x00004000
+#define MPC85xx_DEVDISR_TB0            MPC85xx_DEVDISR_TB
+#define MPC85xx_DEVDISR_CPU1           0x00002000
+#define MPC85xx_DEVDISR_TB1            0x00001000
 #define MPC85xx_DEVDISR_DMA            0x00000400
 #define MPC85xx_DEVDISR_TSEC1          0x00000080
 #define MPC85xx_DEVDISR_TSEC2          0x00000040
index e03ead10bc4ced20b8bde16310b6dc3ac10e1c69..f49607376aaad0520b53fe5a42a7ba5cf878ae14 100644 (file)
@@ -669,4 +669,11 @@ void inline show_boot_progress (int val);
 
 #define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0]))
 
+/* Multicore arch functions */
+#ifdef CONFIG_MP
+int cpu_status(int nr);
+int cpu_reset(int nr);
+int cpu_release(int nr, unsigned long boot_addr, int argc, char *argv[]);
+#endif
+
 #endif /* __COMMON_H_ */