From ec2b74ffd36f02c6123725e7c2533dd2deaf4b64 Mon Sep 17 00:00:00 2001 From: Kumar Gala Date: Thu, 17 Jan 2008 16:48:33 -0600 Subject: [PATCH] 85xx: Added support for multicore boot mechanism 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 --- common/Makefile | 1 + common/cmd_mp.c | 96 ++++++++++++++++++++++ cpu/mpc85xx/Makefile | 3 + cpu/mpc85xx/cpu_init.c | 4 + cpu/mpc85xx/fdt.c | 52 ++++++++++++ cpu/mpc85xx/mp.c | 190 +++++++++++++++++++++++++++++++++++++++++++ cpu/mpc85xx/mp.h | 8 ++ cpu/mpc85xx/release.S | 148 +++++++++++++++++++++++++++++++++ include/asm-ppc/immap_85xx.h | 4 + include/common.h | 7 ++ 10 files changed, 513 insertions(+) create mode 100644 common/cmd_mp.c create mode 100644 cpu/mpc85xx/mp.c create mode 100644 cpu/mpc85xx/mp.h create mode 100644 cpu/mpc85xx/release.S diff --git a/common/Makefile b/common/Makefile index 1c81fcf..382dd01 100644 --- a/common/Makefile +++ b/common/Makefile @@ -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 index 0000000..d96e6a3 --- /dev/null +++ b/common/cmd_mp.c @@ -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 +#include + +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] : \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 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", + " reset - Reset cpu \n" + "cpu status - Status of cpu \n" + "cpu release [args] - Release cpu at with [args]\n" +#ifdef CPU_ARCH_HELP + CPU_ARCH_HELP +#endif + ); diff --git a/cpu/mpc85xx/Makefile b/cpu/mpc85xx/Makefile index 2205dca..adbc585 100644 --- a/cpu/mpc85xx/Makefile +++ b/cpu/mpc85xx/Makefile @@ -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 \ diff --git a/cpu/mpc85xx/cpu_init.c b/cpu/mpc85xx/cpu_init.c index 5f02e0e..fce0c48 100644 --- a/cpu/mpc85xx/cpu_init.c +++ b/cpu/mpc85xx/cpu_init.c @@ -33,6 +33,7 @@ #include #include #include +#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; } diff --git a/cpu/mpc85xx/fdt.c b/cpu/mpc85xx/fdt.c index a6b014c..43df1c7 100644 --- a/cpu/mpc85xx/fdt.c +++ b/cpu/mpc85xx/fdt.c @@ -28,6 +28,54 @@ #include 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 index 0000000..aa91cea --- /dev/null +++ b/cpu/mpc85xx/mp.c @@ -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 +#include +#include +#include +#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 index 0000000..d9fbb82 --- /dev/null +++ b/cpu/mpc85xx/mp.h @@ -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 index 0000000..fe1775c --- /dev/null +++ b/cpu/mpc85xx/release.S @@ -0,0 +1,148 @@ +#include +#include +#include + +#define _LINUX_CONFIG_H 1 /* avoid reading Linux autoconf.h file */ + +#include +#include + +#include +#include + +/* 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 diff --git a/include/asm-ppc/immap_85xx.h b/include/asm-ppc/immap_85xx.h index d769d70..712a078 100644 --- a/include/asm-ppc/immap_85xx.h +++ b/include/asm-ppc/immap_85xx.h @@ -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 diff --git a/include/common.h b/include/common.h index e03ead1..f496073 100644 --- a/include/common.h +++ b/include/common.h @@ -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_ */