diff --git a/src/include/cpu/power/istep_6.h b/src/include/cpu/power/istep_6.h new file mode 100644 index 00000000000..7447dc99e47 --- /dev/null +++ b/src/include/cpu/power/istep_6.h @@ -0,0 +1,145 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ + +#include +#include +#include + +void istep_6_11(void); + +#define FREQ_PB_MHZ (1866) + +#define MASTER_PROC (1) + +#define OCC_FW0 (0) +#define OCC_FW1 (1) +#define CME_ERR_NOTIFY (2) +#define STOP_RCV_NOTIFY_PRD (3) +#define OCC_HB_NOTIFY (4) +#define GPE0_WD_TIMEOUT (5) +#define GPE1_WD_TIMEOUT (6) +#define GPE2_WD_TIMEOUT (7) +#define GPE3_WD_TIMEOUT (8) +#define GPE0_ERR (9) +#define GPE1_ERR (10) +#define GPE2_ERR (11) +#define GPE3_ERR (12) +#define OCB_ERR (13) +#define SRAM_UE (14) +#define SRAM_CE (15) +#define SRAM_READ_ERR (16) +#define SRAM_WRITE_ERR (17) +#define SRAM_DATAOUT_PERR (18) +#define SRAM_OCI_WDATA_PARITY (19) +#define SRAM_OCI_BE_PARITY_ERR (20) +#define SRAM_OCI_ADDR_PARITY_ERR (21) +#define GPE0_HALTED (22) +#define GPE1_HALTED (23) +#define GPE2_HALTED (24) +#define GPE3_HALTED (25) +#define EXT_TRAP (26) +#define PPC405_CORE_RESET (27) +#define PPC405_CHIP_RESET (28) +#define PPC405_SYS_RESET (29) +#define PPC405_WAIT_STATE (30) +#define PPC405_DBGSTOPACK (31) +#define OCB_DB_OCI_TIMEOUT (32) +#define OCB_DB_OCI_RDATA_PARITY (33) +#define OCB_DB_OCI_SLVERR (34) +#define OCB_PIB_ADDR_PARITY_ERR (35) +#define OCB_DB_PIB_DATA_PARITY_ERR (36) +#define OCB_IDC0_ERR (37) +#define OCB_IDC1_ERR (38) +#define OCB_IDC2_ERR (39) +#define OCB_IDC3_ERR (40) +#define SRT_FSM_ERR (41) +#define JTAGACC_ERR (42) +#define SPARE_ERR_38 (43) +#define C405_ECC_UE (44) +#define C405_ECC_CE (45) +#define C405_OCI_MC_CHK (46) +#define SRAM_SPARE_DIRERR0 (47) +#define SRAM_SPARE_DIRERR1 (48) +#define SRAM_SPARE_DIRERR2 (49) +#define SRAM_SPARE_DIRERR3 (50) +#define GPE0_OCISLV_ERR (51) +#define GPE1_OCISLV_ERR (52) +#define GPE2_OCISLV_ERR (53) +#define GPE3_OCISLV_ERR (54) +#define C405ICU_M_TIMEOUT (55) +#define C405DCU_M_TIMEOUT (56) +#define OCC_CMPLX_FAULT (57) +#define OCC_CMPLX_NOTIFY (58) +#define SPARE_59 (59) +#define SPARE_60 (60) +#define SPARE_61 (61) +#define FIR_PARITY_ERR_DUP (62) +#define FIR_PARITY_ERR (63) + +#define OCB_OCI_OCBSHCS0_PUSH_FULL (0) +#define OCB_OCI_OCBSHCS0_PUSH_ENABLE (31) + +#define OCB_PIB_OCBCSR0_OCB_STREAM_MODE (4) +#define OCB_PIB_OCBCSR0_OCB_STREAM_TYPE (5) + +#define MASK_WOR_INCR (5) + +#define PU_PBABAR0 (0x05012B00) +#define PU_PBABAR1 (0x05012B01) +#define PU_PBABAR2 (0x05012B02) +#define PU_PBABAR3 (0x05012B03) + +#define PU_OCB_PIB_OCBCSR0_OR (0x0006D013) +#define PU_OCB_PIB_OCBCSR1_OR (0x0006D033) +#define PU_OCB_PIB_OCBCSR2_OR (0x0006D053) +#define PU_OCB_PIB_OCBCSR3_OR (0x0006D073) + +#define PU_OCB_PIB_OCBAR0 (0x0006D010) +#define PU_OCB_PIB_OCBDR0 (0x0006D015) +#define PU_OCB_PIB_OCBCSR0_RO (0x0006D011) +#define PU_OCB_OCI_OCBSHCS0_SCOM (0x0006C204) + +#define OCC_405_SRAM_ADDRESS (0xFFF40000) +#define OCC_OFFSET_MAIN_EP (0x6C) + +#define OCB_OITR0 (0xc0060040) +#define OCB_OIEPR0 (0xc0060060) + +#define OCC_BRANCH_INSTR (0x4B00000200000000) +#define BRANCH_ADDR_MASK (0x00FFFFFC) + +#define OCC_OFFSET_LENGTH (0x48) +#define OCC_OFFSET_FREQ (0x94) +#define OCC_OFFSET_IPL_FLAG (0x92) +#define OCC_OFFSET_GPE0_LENGTH (0x64) +#define OCC_OFFSET_GPE1_LENGTH (0x68) +#define OCC_MODIFIED_SECTION_SIZE ((OCC_OFFSET_LENGTH) + (OCC_OFFSET_FREQ)) +#define OCC_LENGTH (0x120000) + +#define OCC_GPE0_SRAM_ADDRESS (0xFFF01000) +#define OCC_GPE1_SRAM_ADDRESS (0xFFF10000) + +#define PU_SRAM_SRBV3_SCOM (0x0006A007) + +#define PU_SPIMPSS_ADC_CTRL_REG0 (0x00070000) +#define PU_SPIPSS_ADC_CTRL_REG1 (0x00070001) +#define PU_SPIPSS_ADC_CTRL_REG2 (0x00070002) +#define PU_SPIPSS_ADC_WDATA_REG (0x00070010) + +#define PU_SPIPSS_P2S_CTRL_REG0 (0x00070040) +#define PU_SPIPSS_P2S_CTRL_REG1 (0x00070041) +#define PU_SPIPSS_P2S_CTRL_REG2 (0x00070042) +#define PU_SPIPSS_P2S_WDATA_REG (0x00070050) + +#define PU_SPIPSS_100NS_REG (0x00070028) + + +extern void mount_part_from_pnor(const char *part_name, + struct mmap_helper_region_device *mdev); + +const uint64_t PBA_BARs[4] = +{ + PU_PBABAR0, + PU_PBABAR1, + PU_PBABAR2, + PU_PBABAR3 +}; diff --git a/src/include/cpu/power/occ.h b/src/include/cpu/power/occ.h new file mode 100644 index 00000000000..f379dae4de6 --- /dev/null +++ b/src/include/cpu/power/occ.h @@ -0,0 +1,86 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ + +#ifndef CPU_PPC64_OCC_H +#define CPU_PPC64_OCC_H + +#include + +#define OCC_405_SRAM_ADDRESS (0xFFF40000) +#define OCC_OFFSET_MAIN_EP (0x6C) +#define OCC_BRANCH_INSTR (0x4B00000200000000) +#define BRANCH_ADDR_MASK (0x00FFFFFC) + +#define OCB_PIB_OCBCSR0_OCB_STREAM_MODE (4) +#define OCB_PIB_OCBCSR0_OCB_STREAM_TYPE (5) + +#define OCB_OCI_OCBSHCS0_PUSH_ENABLE (31) +#define OCB_OCI_OCBSHCS0_PUSH_FULL (0) + +#define PU_OCB_PIB_OCBCSR0_RO (0x0006D011) +#define PU_OCB_OCI_OCBSHCS0_SCOM (0x0006C204) +#define PU_OCB_PIB_OCBDR0 (0x0006D015) + +#define PU_OCB_PIB_OCBCSR0_OR (0x0006D013) +#define PU_OCB_PIB_OCBCSR1_OR (0x0006D033) +#define PU_OCB_PIB_OCBCSR2_OR (0x0006D053) +#define PU_OCB_PIB_OCBCSR3_OR (0x0006D073) + +#define PU_OCB_PIB_OCBCSR0_CLEAR (0x0006D012) +#define PU_OCB_PIB_OCBCSR1_CLEAR (0x0006D032) +#define PU_OCB_PIB_OCBCSR2_CLEAR (0x0006D052) +#define PU_OCB_PIB_OCBCSR3_CLEAR (0x0006D072) + +#define PU_OCB_PIB_OCBAR0 (0x0006D010) +#define PU_OCB_PIB_OCBAR1 (0x0006D030) +#define PU_OCB_PIB_OCBAR2 (0x0006D050) +#define PU_OCB_PIB_OCBAR3 (0x0006D070) + +#define EX_PPM_SPWKUP_OCC (0x200F010C) + +#define NUMBER_OF_EX_CHIPLETS (6) +const chiplet_id_t EX_CHIPLETS[NUMBER_OF_EX_CHIPLETS] = +{ + EP00_CHIPLET_ID, + EP01_CHIPLET_ID, + EP02_CHIPLET_ID, + EP03_CHIPLET_ID, + EP04_CHIPLET_ID, + EP05_CHIPLET_ID +}; + +const uint64_t OCBARn[4] = +{ + PU_OCB_PIB_OCBAR0, + PU_OCB_PIB_OCBAR1, + PU_OCB_PIB_OCBAR2, + PU_OCB_PIB_OCBAR3 +}; + +const uint64_t OCBCSRn_CLEAR[4] = +{ + PU_OCB_PIB_OCBCSR0_CLEAR, + PU_OCB_PIB_OCBCSR1_CLEAR, + PU_OCB_PIB_OCBCSR2_CLEAR, + PU_OCB_PIB_OCBCSR3_CLEAR +}; + +const uint64_t OCBCSRn_OR[4] = +{ + PU_OCB_PIB_OCBCSR0_OR, + PU_OCB_PIB_OCBCSR1_OR, + PU_OCB_PIB_OCBCSR2_OR, + PU_OCB_PIB_OCBCSR3_OR +}; + +void writeOCCSRAM( + const uint32_t address, + uint64_t * buffer, + size_t data_length); +void readOCCSRAM( + const uint32_t address, + uint64_t * buffer, + size_t data_length); +uint64_t makeStart405Instruction(void); +void clear_occ_special_wakeups(void); + +#endif /* CPU_PPC64_OCC_H */ diff --git a/src/include/cpu/power/scom.h b/src/include/cpu/power/scom.h index 8a30387e53d..5c8c7aff07b 100644 --- a/src/include/cpu/power/scom.h +++ b/src/include/cpu/power/scom.h @@ -217,5 +217,12 @@ static inline void scom_or_for_chiplet(chiplet_id_t chiplet, uint64_t addr, uint scom_and_or_for_chiplet(chiplet, addr, ~0, or); } +static inline uint8_t get_dd(void) +{ + uint64_t val = read_scom(0xF000F); + val = ((val >> 52) & 0x0F) | ((val >> 56) & 0xF0); + return (uint8_t) val; +} + #endif /* __ASSEMBLER__ */ #endif /* CPU_PPC64_SCOM_H */ diff --git a/src/include/cpu/power/spr.h b/src/include/cpu/power/spr.h index d0c37c071a1..63ca1db4dec 100644 --- a/src/include/cpu/power/spr.h +++ b/src/include/cpu/power/spr.h @@ -7,21 +7,29 @@ #define SPR_DEC_IMPLEMENTED_BITS 56 #define SPR_DEC_LONGEST_TIME ((1ull << (SPR_DEC_IMPLEMENTED_BITS - 1)) - 1) -#define SPR_TB 0x10C - -#define SPR_PVR 0x11F -#define SPR_PVR_REV_MASK (PPC_BITMASK(52, 55) | PPC_BITMASK(60, 63)) -#define SPR_PVR_REV(maj, min) (PPC_SHIFT((maj), 55) | PPC_SHIFT((min), 63)) +#define SPR_SRR0 0x2A +#define SPR_SRR1 0x2B +#define SPR_DAWR 0xB4 +#define SPR_CIABR 0xBB +#define SPR_DAWRX 0xBC +#define SPR_TB 0x10C +#define SPR_HSPRG0 0x130 #define SPR_HDEC 0x136 #define SPR_HRMOR 0x139 #define SPR_LPCR 0x13E +#define SPR_LPCR_HVEE PPC_BIT(17) #define SPR_LPCR_LD PPC_BIT(46) +#define SPR_LPCR_EEE PPC_BIT(49) +#define SPR_LPCR_DEE PPC_BIT(50) +#define SPR_LPCR_OEE PPC_BIT(51) #define SPR_LPCR_HEIC PPC_BIT(59) +#define SPR_LPCR_HVICE PPC_BIT(62) #define SPR_LPCR_HDICE PPC_BIT(63) #define SPR_HMER 0x150 +#define SPR_HMEER 0x151 /* Bits in HMER/HMEER */ #define SPR_HMER_MALFUNCTION_ALERT PPC_BIT(0) #define SPR_HMER_PROC_RECV_DONE PPC_BIT(2) @@ -39,6 +47,10 @@ #define SPR_HMER_XSCOM_STATUS PPC_BITMASK(21,23) #define SPR_HMER_XSCOM_OCCUPIED PPC_BIT(23) +#define SPR_PTCR 0x1D0 +#define SPR_PSSCR 0x357 +#define SPR_PMCR 0x374 + #ifndef __ASSEMBLER__ #include @@ -77,10 +89,5 @@ static inline void write_msr(uint64_t val) asm volatile("mtmsrd %0" :: "r"(val) : "memory"); } -static inline uint64_t pvr_revision(void) -{ - return read_spr(SPR_PVR) & SPR_PVR_REV_MASK; -} - #endif /* __ASSEMBLER__ */ #endif /* CPU_PPC64_SPR_H */ diff --git a/src/soc/ibm/power9/Makefile.inc b/src/soc/ibm/power9/Makefile.inc index a758a1441b4..26515af0703 100644 --- a/src/soc/ibm/power9/Makefile.inc +++ b/src/soc/ibm/power9/Makefile.inc @@ -4,9 +4,9 @@ ifeq ($(CONFIG_CPU_IBM_POWER9),y) bootblock-y += bootblock.c bootblock-y += rom_media.c -romstage-y += rom_media.c -romstage-y += romstage.c -romstage-y += vpd.c +romstage-y += ccs.c +romstage-y += i2c.c +romstage-y += istep_6_11.c romstage-y += istep_13_2.c romstage-y += istep_13_3.c romstage-y += istep_13_4.c @@ -20,10 +20,13 @@ romstage-y += istep_14_1.c romstage-y += istep_14_2.c romstage-y += istep_14_3.c romstage-y += istep_14_5.c -romstage-y += i2c.c -romstage-y += ccs.c +romstage-y += occ.c +romstage-y += rom_media.c +romstage-y += romstage.c romstage-y += timer.c +romstage-y += vpd.c ramstage-y += chip.c +ramstage-y += homer.c ramstage-y += rom_media.c ramstage-y += timer.c diff --git a/src/soc/ibm/power9/chip.c b/src/soc/ibm/power9/chip.c index 31bd1495502..2e04a72560a 100644 --- a/src/soc/ibm/power9/chip.c +++ b/src/soc/ibm/power9/chip.c @@ -3,7 +3,9 @@ #include #include #include + #include "istep_13_scom.h" +#include "chip.h" static int dt_platform_fixup(struct device_tree_fixup *fixup, struct device_tree *tree) @@ -42,6 +44,7 @@ static inline unsigned long size_k(uint64_t reg) static void enable_soc_dev(struct device *dev) { int mcs_i, idx = 0; + unsigned long reserved_size, top = 0; for (mcs_i = 0; mcs_i < MCS_PER_PROC; mcs_i++) { uint64_t reg; @@ -50,15 +53,31 @@ static void enable_soc_dev(struct device *dev) /* These registers are undocumented, see istep 14.5. */ /* MCS_MCFGP */ reg = read_scom_for_chiplet(nest, 0x0501080A); - if (reg & PPC_BIT(0)) + if (reg & PPC_BIT(0)) { ram_resource(dev, idx++, base_k(reg), size_k(reg)); + if (base_k(reg) + size_k(reg) > top) + top = base_k(reg) + size_k(reg); + } /* MCS_MCFGPM */ reg = read_scom_for_chiplet(nest, 0x0501080C); - if (reg & PPC_BIT(0)) + if (reg & PPC_BIT(0)) { ram_resource(dev, idx++, base_k(reg), size_k(reg)); + if (base_k(reg) + size_k(reg) > top) + top = base_k(reg) + size_k(reg); + } } + /* + * Reserve top 8M (OCC common area) + 4M (HOMER). + * + * TODO: 8M + (4M per CPU), hostboot reserves always 8M + 8 * 4M. + */ + reserved_size = 8*1024 + 4*1024 *8 /* * num_of_cpus */; + top -= reserved_size; + reserved_ram_resource(dev, idx++, top, reserved_size); + build_homer_image((void *)(top * 1024)); + if (CONFIG(PAYLOAD_FIT_SUPPORT)) { struct device_tree_fixup *dt_fixup; diff --git a/src/soc/ibm/power9/chip.h b/src/soc/ibm/power9/chip.h new file mode 100644 index 00000000000..d6ce11b3609 --- /dev/null +++ b/src/soc/ibm/power9/chip.h @@ -0,0 +1,11 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ + +#ifndef __SOC_IBM_POWER9_CHIP_H +#define __SOC_IBM_POWER9_CHIP_H + +struct soc_ibm_power9_config { +}; + +void build_homer_image(void *homer_bar); + +#endif /* __SOC_CAVIUM_CN81XX_CHIP_H */ diff --git a/src/soc/ibm/power9/homer.c b/src/soc/ibm/power9/homer.c new file mode 100644 index 00000000000..c016e29c203 --- /dev/null +++ b/src/soc/ibm/power9/homer.c @@ -0,0 +1,1132 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ + +#include +#include +#include +#include +#include +#include +#include // memset, memcpy +#include + +#include "chip.h" +#include "homer.h" +#include "xip.h" + +#include + +extern void mount_part_from_pnor(const char *part_name, + struct mmap_helper_region_device *mdev); + +enum operation_type { + COPY, + FIND +}; + +static size_t copy_section(void *dst, struct xip_section *section, void *base, + uint8_t dd, enum operation_type op) +{ + if (!section->dd_support) { + if (op == COPY) { + memcpy(dst, base + section->offset, section->size); + } else { + *(void **)dst = base + section->offset; + } + return section->size; + } + + struct dd_container *cont = base + section->offset; + int i; + + assert(cont->magic == DD_CONTAINER_MAGIC); + for (i = 0; i < cont->num; i++) { + if (cont->blocks[i].dd == dd) { + if (op == COPY) { + memcpy(dst, (void *)cont + cont->blocks[i].offset, + cont->blocks[i].size); + } else { + *(void **)dst = (void *)cont + cont->blocks[i].offset; + } + return cont->blocks[i].size; + } + } + + die("XIP: Can't find container for DD=%x\n", dd); +} + +static void build_sgpe(struct homer_st *homer, struct xip_sgpe_header *sgpe, + uint8_t dd) +{ + struct sgpe_img_header *hdr; + size_t size; + + assert(sgpe->magic == XIP_MAGIC_SGPE); + + /* SPGE header */ + size = copy_section(&homer->qpmr.sgpe.header, &sgpe->qpmr, sgpe, dd, COPY); + assert(size <= sizeof(struct qpmr_header)); + + /* + * 0xFFF00000 (SRAM base) + 4k (IPC) + 60k (GPE0) + 64k (GPE1) + 50k (PGPE) + * + 2k (aux) + 2k (shared) = 0xFFF2D800 + * + * WARNING: I have no idea if this is constant or depends on SPGE version. + */ + assert(homer->qpmr.sgpe.header.sram_region_start == 0xFFF2D800); + assert(homer->qpmr.sgpe.header.sram_region_size == SGPE_SRAM_IMG_SIZE); + /* + * Apart from these the only filled fields (same values for all DDs) are: + * - magic ("XIP SPGE") + * - build_date + * - build_ver + * - img_offset (0x0a00, overwritten with the same value later by code) + * - img_len (0xbf64, ~49kB, overwritten with the same value later by code) + */ + + /* SPGE L1 bootloader */ + size = copy_section(&homer->qpmr.sgpe.l1_bootloader, &sgpe->l1_bootloader, + sgpe, dd, COPY); + homer->qpmr.sgpe.header.l1_offset = offsetof(struct qpmr_st, + sgpe.l1_bootloader); + assert(size <= GPE_BOOTLOADER_SIZE); + + /* SPGE L2 bootloader */ + size = copy_section(&homer->qpmr.sgpe.l2_bootloader, &sgpe->l2_bootloader, + sgpe, dd, COPY); + homer->qpmr.sgpe.header.l2_offset = offsetof(struct qpmr_st, + sgpe.l2_bootloader); + homer->qpmr.sgpe.header.l2_len = size; + assert(size <= GPE_BOOTLOADER_SIZE); + + /* SPGE HCODE */ + size = copy_section(&homer->qpmr.sgpe.sram_image, &sgpe->hcode, sgpe, dd, + COPY); + homer->qpmr.sgpe.header.img_offset = offsetof(struct qpmr_st, + sgpe.sram_image); + homer->qpmr.sgpe.header.img_len = size; + assert(size <= SGPE_SRAM_IMG_SIZE); + assert(size > (INT_VECTOR_SIZE + sizeof(struct sgpe_img_header))); + + /* Cache SCOM region */ + homer->qpmr.sgpe.header.scom_offset = + offsetof(struct qpmr_st, cache_scom_region); + homer->qpmr.sgpe.header.scom_len = CACHE_SCOM_REGION_SIZE; + + /* Update SRAM image header */ + hdr = (struct sgpe_img_header *) + &homer->qpmr.sgpe.sram_image[INT_VECTOR_SIZE]; + hdr->ivpr_addr = homer->qpmr.sgpe.header.sram_region_start; + hdr->cmn_ring_occ_offset = homer->qpmr.sgpe.header.img_len; + hdr->cmn_ring_ovrd_occ_offset = 0; + hdr->spec_ring_occ_offset = 0; + hdr->scom_offset = 0; + /* Nest frequency divided by 64. */ + hdr->timebase_hz = (1866 * MHz) / 64; + + /* SPGE auxiliary functions */ + /* + * TODO: check if it is really enabled. This comes from hostboot attributes, + * but I don't know if/where those are set. + */ + hdr->aux_control = 1 << 24; + /* + * 0x80000000 (HOMER in OCI PBA memory space) + 1M (QPMR offset) + * + 512k (offset to aux) + * + * This probably is full address and not offset. + */ + hdr->aux_offset = 0x80000000 + offsetof(struct homer_st, qpmr.aux); + hdr->aux_len = CACHE_SCOM_AUX_SIZE; + homer->qpmr.sgpe.header.enable_24x7_ima = 1; + /* Here offset is relative to QPMR */ + homer->qpmr.sgpe.header.aux_offset = offsetof(struct qpmr_st, aux); + homer->qpmr.sgpe.header.aux_len = CACHE_SCOM_AUX_SIZE; +} + +static const uint32_t _SMF = 0x5F534D46; // "_SMF" + +static const uint32_t ATTN_OP = 0x00000200; +static const uint32_t BLR_OP = 0x4E800020; +static const uint32_t SKIP_SPR_REST_INST = 0x4800001C; +static const uint32_t MR_R0_TO_R10_OP = 0x7C0A0378; +static const uint32_t MR_R0_TO_R21_OP = 0x7C150378; +static const uint32_t MR_R0_TO_R9_OP = 0x7C090378; +static const uint32_t MTLR_R30_OP = 0x7FC803A6; +static const uint32_t MFLR_R30_OP = 0x7FC802A6; + +static const uint32_t init_cpureg_template[] = { + 0x63000000, /* ori %r24, %r0, 0 */ /* |= spr, key for lookup */ + 0x7C000278, /* xor %r0, %r0, %r0 */ + 0x64000000, /* oris %r0, %r0, 0 */ /* |= val >> 48 */ + 0x60000000, /* ori %r0, %r0, 0 */ /* |= (val >> 32) & 0x0000FFFF */ + 0x780007C6, /* rldicr %r0, %r0, 32, 31 */ + 0x64000000, /* oris %r0, %r0, 0 */ /* |= (val >> 16) & 0x0000FFFF */ + 0x60000000, /* ori %r0, %r0, 0 */ /* |= val & 0x0000FFFF */ + 0x7C0003A6, /* mtspr 0, %r0 */ /* |= spr, encoded */ +}; + +/* + * These SPRs are not described in PowerISA 3.0B. + * MSR is not SPR, but self restore code treats it this way. + */ +#define SPR_USPRG0 0x1F0 +#define SPR_USPRG1 0x1F1 +#define SPR_URMOR 0x1F9 +#define SPR_SMFCTRL 0x1FF +#define SPR_LDBAR 0x352 +#define SPR_HID 0x3F0 +#define SPR_MSR 0x7D0 + +static void add_init_cpureg_entry(uint32_t *base, uint16_t spr, uint64_t val, + int init) +{ + while ((*base != (init_cpureg_template[0] | spr)) && *base != BLR_OP) + base++; + + /* Must change next instruction from attn to blr when adding new entry. */ + if (*base == BLR_OP) + *(base + ARRAY_SIZE(init_cpureg_template)) = BLR_OP; + + memcpy(base, init_cpureg_template, sizeof(init_cpureg_template)); + base[0] |= spr; + + if (init) { + base[1] = SKIP_SPR_REST_INST; + } else { + base[2] |= (val >> 48) & 0xFFFF; + base[3] |= (val >> 32) & 0xFFFF; + base[5] |= (val >> 16) & 0xFFFF; + base[6] |= val & 0xFFFF; + } + + /* Few exceptions are handled differently. */ + if (spr == SPR_MSR) { + base[7] = MR_R0_TO_R21_OP; + } else if (spr == SPR_HRMOR) { + base[7] = MR_R0_TO_R10_OP; + } else if (spr == SPR_URMOR) { + base[7] = MR_R0_TO_R9_OP; + } else { + base[7] |= ((spr & 0x1F) << 16) | ((spr & 0x3E0) << 6); + } +} + +static const uint32_t init_save_self_template[] = { + 0x60000000, /* ori %r0, %r0, 0 */ /* |= i */ + 0x3BFF0020, /* addi %r31, %r31, 0x20 */ + 0x60000000, /* nop (ori %r0, %r0, 0) */ +}; + +/* Honestly, I have no idea why saving uses different key than restoring... */ +static void add_init_save_self_entry(uint32_t **ptr, int i) +{ + memcpy(*ptr, init_save_self_template, sizeof(init_save_self_template)); + **ptr |= i; + *ptr += ARRAY_SIZE(init_save_self_template); +} + +static const uint16_t thread_sprs[] = { + SPR_CIABR, + SPR_DAWR, + SPR_DAWRX, + SPR_HSPRG0, + SPR_LDBAR, + SPR_LPCR, + SPR_PSSCR, + SPR_MSR, + SPR_SMFCTRL, + SPR_USPRG0, + SPR_USPRG1, +}; + +static const uint16_t core_sprs[] = { + SPR_HRMOR, + SPR_HID, + SPR_HMEER, + SPR_PMCR, + SPR_PTCR, + SPR_URMOR, +}; + +static void build_self_restore(struct homer_st *homer, + struct xip_restore_header *rest, uint8_t dd) +{ + /* Assumptions: SMT4 only, SMF available but disabled. */ + size_t size; + uint32_t *ptr; + + /* + * Data in XIP has its first 256 bytes zeroed, reserved for header, so even + * though this is exe part of self restore region, we should copy it to + * header's address. + */ + size = copy_section(&homer->cpmr.header, &rest->self, rest, dd, COPY); + assert(size > sizeof(struct cpmr_header)); + + /* Now, overwrite header. */ + size = copy_section(&homer->cpmr.header, &rest->cpmr, rest, dd, COPY); + assert(size <= sizeof(struct cpmr_header)); + + /* + * According to comment in p9_hcode_image_build.C it is for Nimbus >= DD22. + * Earlier versions do things differently. For now die(), implement if + * needed. + * + * If _SMF doesn't exist: + * - fill memory from (CPMR + 8k + 256) for 192k with ATTN + * - starting from the beginning of that region change instruction at every + * 2k bytes into BLR + * + * If _SMF exists: + * - fill CPMR.core_self_restore with ATTN instructions + * - for every core: + * - change every thread's restore first instruction (at 0, 512, 1024, + * 1536 bytes) to BLR + * - change core's restore first instruction (at 3k) to BLR + */ + if (*(uint32_t *)&homer->cpmr.exe[0x1300 - sizeof(struct cpmr_header)] != + _SMF) + die("No _SMF magic number in self restore region\n"); + + ptr = (uint32_t *)homer->cpmr.core_self_restore; + for (size = 0; size < (96 * KiB) / sizeof(uint32_t); size++) { + ptr[size] = ATTN_OP; + } + + /* + * This loop combines two functions from hostboot: + * initSelfRestoreRegion() and initSelfSaveRestoreEntries(). The second one + * writes only sections for functional cores, code below does it for all. + * This will take more time, but makes the code easier to understand. + * + * TODO: check if we can skip both cpureg and save_self for nonfunctional + * cores + */ + for (int core = 0; core < MAX_CORES; core++) { + struct smf_core_self_restore *csr = &homer->cpmr.core_self_restore[core]; + uint32_t *csa = csr->core_save_area; + + for (int thread = 0; thread < 4; thread++) { + csr->thread_restore_area[thread][0] = BLR_OP; + uint32_t *tsa = csr->thread_save_area[thread]; + *tsa++ = MFLR_R30_OP; + + for (int i = 0; i < ARRAY_SIZE(thread_sprs); i++) { + /* + * Hostboot uses strange calculation for *_save_area keys. + * I don't know if this is only used by hostboot and save/restore + * code generated by it, or if something else (CME?) requires such + * format. For now leave it as hostboot does it, we can simplify + * this later. + * + * CIABR through MSR: key = 0..7 + * SMFCTRL through USPRG1: key = 1C..1E + */ + int tsa_key = i; + if (i > 7) + tsa_key += 0x14; + + add_init_cpureg_entry(csr->thread_restore_area[thread], + thread_sprs[i], 0, 1); + add_init_save_self_entry(&tsa, tsa_key); + } + + *tsa++ = MTLR_R30_OP; + *tsa++ = BLR_OP; + } + + csr->core_restore_area[0] = BLR_OP; + *csa++ = MFLR_R30_OP; + for (int i = 0; i < ARRAY_SIZE(core_sprs); i++) { + add_init_cpureg_entry(csr->core_restore_area, core_sprs[i], 0, 1); + /* + * HID through PTCR: key = 0x15..0x18 + * HRMOR and URMOR are skipped. + */ + if (core_sprs[i] != SPR_HRMOR && core_sprs[i] != SPR_URMOR) + add_init_save_self_entry(&csa, i + 0x14); + } + + *csa++ = MTLR_R30_OP; + *csa++ = BLR_OP; + } + + /* Populate CPMR header */ + homer->cpmr.header.fused_mode_status = 0xAA; // non-fused + + /* For SMF enabled */ +#if 0 + homer->cpmr.header.urmor_fix = 1; +#endif + + homer->cpmr.header.self_restore_ver = 2; + homer->cpmr.header.stop_api_ver = 2; + + /* + * WARNING: Hostboot filled CME header field with information whether cores + * are fused or not here. However, at this point CME image is not yet + * loaded, so that field will get overwritten. + */ +} + +static void build_cme(struct homer_st *homer, struct xip_cme_header *cme, + uint8_t dd) +{ + size_t size; + struct cme_img_header *hdr; + + size = copy_section(&homer->cpmr.cme_sram_region, &cme->hcode, cme, dd, + COPY); + assert(size <= CME_SRAM_IMG_SIZE); + assert(size > (INT_VECTOR_SIZE + sizeof(struct cme_img_header))); + + hdr = (struct cme_img_header *) + &homer->cpmr.cme_sram_region[INT_VECTOR_SIZE]; + + hdr->hcode_offset = 0; + hdr->hcode_len = size; + + hdr->pstate_region_offset = 0; + hdr->pstate_region_len = 0; + + hdr->cpmr_phy_addr = (uint64_t) homer | 2 * MiB; + /* With SMF disabled unsecure HOMER is the same as regular one */ + hdr->unsec_cpmr_phy_addr = hdr->cpmr_phy_addr; + + hdr->common_ring_offset = hdr->hcode_offset + hdr->hcode_len; + hdr->common_ring_len = 0; + + hdr->scom_offset = 0; + hdr->scom_len = CORE_SCOM_RESTORE_SIZE / MAX_CORES / 2; + + hdr->core_spec_ring_offset = 0; + hdr->max_spec_ring_len = 0; +} + +static void build_pgpe(struct homer_st *homer, struct xip_pgpe_header *pgpe, + uint8_t dd) +{ + size_t size; + struct pgpe_img_header *hdr; + + /* PPGE header */ + size = copy_section(&homer->ppmr.header, &pgpe->ppmr, pgpe, dd, COPY); + assert(size <= PPMR_HEADER_SIZE); + /* + * 0xFFF00000 (SRAM base) + 4k (IPC) + 60k (GPE0) + 64k (GPE1) = 0xFFF20000 + * + * WARNING: I have no idea if this is constant or depends on PPGE version. + */ + assert(homer->ppmr.header.sram_region_start == 0xFFF20000); + assert(homer->ppmr.header.sram_region_size == PGPE_SRAM_IMG_SIZE + + PGPE_AUX_TASK_SIZE + + PGPE_OCC_SHARED_SRAM_SIZE); + + /* PPGE L1 bootloader */ + size = copy_section(homer->ppmr.l1_bootloader, &pgpe->l1_bootloader, pgpe, + dd, COPY); + assert(size <= GPE_BOOTLOADER_SIZE); + homer->ppmr.header.l1_offset = offsetof(struct ppmr_st, l1_bootloader); + + /* PPGE L2 bootloader */ + size = copy_section(homer->ppmr.l2_bootloader, &pgpe->l2_bootloader, pgpe, + dd, COPY); + assert(size <= GPE_BOOTLOADER_SIZE); + homer->ppmr.header.l2_offset = offsetof(struct ppmr_st, l2_bootloader); + homer->ppmr.header.l2_len = size; + + /* PPGE HCODE */ + size = copy_section(homer->ppmr.pgpe_sram_img, &pgpe->hcode, pgpe, dd, + COPY); + assert(size <= PGPE_SRAM_IMG_SIZE); + assert(size > (INT_VECTOR_SIZE + sizeof(struct pgpe_img_header))); + homer->ppmr.header.hcode_offset = offsetof(struct ppmr_st, pgpe_sram_img); + homer->ppmr.header.hcode_len = size; + + /* PPGE auxiliary task */ + size = copy_section(homer->ppmr.aux_task, &pgpe->aux_task, pgpe, dd, COPY); + assert(size <= PGPE_AUX_TASK_SIZE); + homer->ppmr.header.aux_task_offset = offsetof(struct ppmr_st, aux_task); + homer->ppmr.header.aux_task_len = size; + + /* 0x80000000 = HOMER in OCI PBA memory space */ + homer->ppmr.header.doptrace_offset = + 0x80000000 + offsetof(struct homer_st, ppmr.doptrace); + homer->ppmr.header.doptrace_len = PGPE_DOPTRACE_SIZE; + + /* Update SRAM image header */ + hdr = (struct pgpe_img_header *) + &homer->ppmr.pgpe_sram_img[INT_VECTOR_SIZE]; + + /* SPGE auxiliary functions */ + hdr->aux_controls = 1 << 24; +} + +static void pba_reset(void) +{ + long time; + /* Stopping Block Copy Download Engine + *0x00068010 // undocumented, PU_BCDE_CTL_SCOM + [all] 0 + [0] 1 + */ + write_scom(0x00068010, PPC_BIT(0)); + + /* Stopping Block Copy Upload Engine + *0x00068015 // undocumented, PU_BCUE_CTL_SCOM + [all] 0 + [0] 1 + */ + write_scom(0x00068015, PPC_BIT(0)); + + /* Polling on, to verify that BCDE & BCUE are indeed stopped + timeout(256*256us): + *0x00068012 // undocumented, PU_BCDE_STAT_SCOM + [0] PBA_BC_STAT_RUNNING? + *0x00068017 // undocumented, PU_BCUE_STAT_SCOM + [0] PBA_BC_STAT_RUNNING? + if both bits are clear: break + */ + time = wait_us(256*256, + (((read_scom(0x00068012) & PPC_BIT(0)) == 0) && + ((read_scom(0x00068017) & PPC_BIT(0)) == 0))); + + if (!time) + die("Timed out waiting for stopping of BCDE/BCUE\n"); + + /* Clear the BCDE and BCUE stop bits */ + write_scom(0x00068010, 0); + write_scom(0x00068015, 0); + + /* Reset each slave and wait for completion + timeout(16*1us): + // This write is inside the timeout loop. I don't know if this will cause slaves to reset + // on each iteration or not, but this is how it is done in hostboot. + *0x00068001 // undocumented, PU_PBASLVRST_SCOM + [all] 0 + [0] 1 // reset? + [1-2] sl + if *0x00068001[4 + sl] == 0: break // 4 + sl: reset in progress? + if *0x00068001[8 + sl]: die() // 8 + sl: busy? + */ + for (int sl = 0; sl < 3; sl++) { // Fourth is owned by SBE, do not reset + time = wait_us(16, + (write_scom(0x00068001, PPC_BIT(0) | PPC_SHIFT(sl, 2)), + (read_scom(0x00068001) & PPC_BIT(4 + sl)) == 0)); + + if (!time || read_scom(0x00068001) & PPC_BIT(8 + sl)) + die("Timed out waiting for slave %d reset\n", sl); + } + + /* Reset PBA regs + *0x00068013 // undocumented, PU_BCDE_PBADR_SCOM + *0x00068014 // undocumented, PU_BCDE_OCIBAR_SCOM + *0x00068015 // undocumented, PU_BCUE_CTL_SCOM + *0x00068016 // undocumented, PU_BCUE_SET_SCOM + *0x00068018 // undocumented, PU_BCUE_PBADR_SCOM + *0x00068019 // undocumented, PU_BCUE_OCIBAR_SCOM + *0x00068026 // undocumented, PU_PBAXSHBR0_SCOM + *0x0006802A // undocumented, PU_PBAXSHBR1_SCOM + *0x00068027 // undocumented, PU_PBAXSHCS0_SCOM + *0x0006802B // undocumented, PU_PBAXSHCS1_SCOM + *0x00068004 // undocumented, PU_PBASLVCTL0_SCOM + *0x00068005 // undocumented, PU_PBASLVCTL1_SCOM + *0x00068006 // undocumented, PU_PBASLVCTL2_SCOM + BRIDGE.PBA.PBAFIR // 0x05012840 + BRIDGE.PBA.PBAERRRPT0 // 0x0501284C + [all] 0 + */ + write_scom(0x00068013, 0); + write_scom(0x00068014, 0); + write_scom(0x00068015, 0); + write_scom(0x00068016, 0); + write_scom(0x00068018, 0); + write_scom(0x00068019, 0); + write_scom(0x00068026, 0); + write_scom(0x0006802A, 0); + write_scom(0x00068027, 0); + write_scom(0x0006802B, 0); + write_scom(0x00068004, 0); + write_scom(0x00068005, 0); + write_scom(0x00068006, 0); + write_scom(0x05012840, 0); + write_scom(0x0501284C, 0); + + /* Perform non-zero reset operations + BRIDGE.PBA.PBACFG // 0x0501284B + [all] 0 + [38] PBACFG_CHSW_DIS_GROUP_SCOPE = 1 + */ + write_scom(0x0501284B, PPC_BIT(38)); + + /* + *0x00068021 // undocumented, PU_PBAXCFG_SCOM + [all] 0 + [2] 1 // PBAXCFG_SND_RESET? + [3] 1 // PBAXCFG_RCV_RESET? + */ + write_scom(0x00068021, PPC_BIT(2) | PPC_BIT(3)); + + /* + * The following registers are undocumented. Their fields can be decoded + * from hostboot, but the values are always the same, so why bother... + */ + /* Set the PBA_MODECTL register */ + write_scom(0x00068000, 0x00A0BA9000000000); + + /* Slave 0 (SGPE and OCC boot) */ + write_scom(0x00068004, 0xB7005E0000000000); + + /* Slave 1 (405 ICU/DCU) */ + write_scom(0x00068005, 0xD5005E4000000000); + + /* Slave 2 (PGPE Boot) */ + write_scom(0x00068006, 0xA7005E4000000000); +} + +static void stop_gpe_init(struct homer_st *homer) +{ + /* First check if SGPE_ACTIVE is not set in OCCFLAG register + if (TP.TPCHIP.OCC.OCI.OCB.OCB_OCI_OCCFLG[8] == 1): // 0x0006C08A + TP.TPCHIP.OCC.OCI.OCB.OCB_OCI_OCCFLG (CLEAR) // 0x0006C08B + [all] 0 + [8] 1 // SGPE_ACTIVE, bits in this register are defined by OCC firmware + */ + if (read_scom(0x0006C08A) & PPC_BIT(8)) { + printk(BIOS_WARNING, "SGPE_ACTIVE is set in OCCFLAG register, clearing it\n"); + write_scom(0x0006C08B, PPC_BIT(8)); + } + + /* + * Program SGPE IVPR + * ATTR_STOPGPE_BOOT_COPIER_IVPR_OFFSET is set in updateGpeAttributes() in 15.1 + TP.TPCHIP.OCC.OCI.GPE3.GPEIVPR // 0x00066001 + [all] 0 + [0-31] GPEIVPR_IVPR = ATTR_STOPGPE_BOOT_COPIER_IVPR_OFFSET + // Only bits [0-22] are actually defined, meaning IVPR must be aligned to 512B + */ + uint32_t ivpr = 0x80000000 + homer->qpmr.sgpe.header.l1_offset + + offsetof(struct homer_st, qpmr); + write_scom(0x00066001, PPC_SHIFT(ivpr, 31)); + + /* Program XCR to ACTIVATE SGPE + TP.TPCHIP.OCC.OCI.GPE3.GPENXIXCR // 0x00066010 + [all] 0 + [1-3] PPE_XIXCR_XCR = 6 // hard reset + TP.TPCHIP.OCC.OCI.GPE3.GPENXIXCR // 0x00066010 + [all] 0 + [1-3] PPE_XIXCR_XCR = 4 // toggle XSR[TRH] + TP.TPCHIP.OCC.OCI.GPE3.GPENXIXCR // 0x00066010 + [all] 0 + [1-3] PPE_XIXCR_XCR = 2 // resume + */ + write_scom(0x00066010, PPC_SHIFT(6, 3)); + write_scom(0x00066010, PPC_SHIFT(4, 3)); + write_scom(0x00066010, PPC_SHIFT(2, 3)); + + /* + * Now wait for SGPE to not be halted and for the HCode to indicate to be + * active. + * Warning: consts names in hostboot say timeouts are in ms, but code treats + * it as us. With debug output it takes much more than 20us between reads + * (~150us) and passes on 5th pass, which gives ~600us, +/- 150us on 4-core + * CPU (4 active CMEs). + timeout(125*20us): + if ((TP.TPCHIP.OCC.OCI.OCB.OCB_OCI_OCCFLG[8] == 1) && // 0x0006C08A + (TP.TPCHIP.OCC.OCI.GPE3.GPEXIXSR[0] == 0)): break // 0x00066021 + */ + long time = wait_us(125*20, ((read_scom(0x0006C08A) & PPC_BIT(8)) && + !(read_scom(0x00066021) & PPC_BIT(0)))); + + if (!time) + die("Timeout while waiting for SGPE activation\n"); +} + +static uint64_t get_available_cores(int *me) +{ + uint64_t ret = 0; + for (int i = 0; i < MAX_CORES; i++) { + uint64_t val = read_scom_for_chiplet(EC00_CHIPLET_ID + i, 0xF0040); + if (val & PPC_BIT(0)) { + printk(BIOS_SPEW, "Core %d is functional%s\n", i, + (val & PPC_BIT(1)) ? "" : " and running"); + ret |= PPC_BIT(i); + if (val & PPC_BIT(1) && me != NULL) + *me = i; + + /* Might as well set multicast groups for cores */ + if ((read_scom_for_chiplet(EC00_CHIPLET_ID + i, 0xF0001) & PPC_BITMASK(3,5)) + == PPC_BITMASK(3,5)) + scom_and_or_for_chiplet(EC00_CHIPLET_ID + i, 0xF0001, + ~(PPC_BITMASK(3,5) | PPC_BITMASK(16,23)), + PPC_BITMASK(19,21)); + + if ((read_scom_for_chiplet(EC00_CHIPLET_ID + i, 0xF0002) & PPC_BITMASK(3,5)) + == PPC_BITMASK(3,5)) + scom_and_or_for_chiplet(EC00_CHIPLET_ID + i, 0xF0002, + ~(PPC_BITMASK(3,5) | PPC_BITMASK(16,23)), + PPC_BIT(5) | PPC_BITMASK(19,21)); + } + } + return ret; +} + +#define IS_EC_FUNCTIONAL(ec, cores) (!!((cores) & PPC_BIT(ec))) +#define IS_EX_FUNCTIONAL(ex, cores) (!!((cores) & PPC_BITMASK(2*(ex), 2*(ex) + 1))) +#define IS_EQ_FUNCTIONAL(eq, cores) (!!((cores) & PPC_BITMASK(4*(eq), 4*(eq) + 3))) + +/* TODO: similar is used in 13.3. Add missing parameters and make it public? */ +static void psu_command(uint8_t flags, long time) +{ + /* TP.TPCHIP.PIB.PSU.PSU_SBE_DOORBELL_REG */ + if (read_scom(0x000D0060) & PPC_BIT(0)) + die("MBOX to SBE busy, this should not happen\n"); + + /* https://github.com/open-power/hostboot/blob/master/src/include/usr/sbeio/sbe_psudd.H#L418 */ + /* TP.TPCHIP.PIB.PSU.PSU_HOST_SBE_MBOX0_REG */ + /* REQUIRE_RESPONSE, CLASS_CORE_STATE, CMD_CONTROL_DEADMAN_LOOP, flags */ + write_scom(0x000D0050, 0x000001000000D101 | PPC_SHIFT(flags, 31)); + + /* TP.TPCHIP.PIB.PSU.PSU_HOST_SBE_MBOX0_REG */ + write_scom(0x000D0051, time); + + /* Ring the host->SBE doorbell */ + /* TP.TPCHIP.PIB.PSU.PSU_SBE_DOORBELL_REG_OR */ + write_scom(0x000D0062, PPC_BIT(0)); + + /* Wait for response */ + /* TP.TPCHIP.PIB.PSU.PSU_HOST_DOORBELL_REG */ + time = wait_ms(time, read_scom(0x000D0060) & PPC_BIT(0)); + + if (!time) + die("Timed out while waiting for SBE response\n"); + + /* Clear SBE->host doorbell */ + /* TP.TPCHIP.PIB.PSU.PSU_HOST_DOORBELL_REG_AND */ + write_scom(0x000D0064, ~PPC_BIT(0)); +} + +#define DEADMAN_LOOP_START 0x0001 +#define DEADMAN_LOOP_STOP 0x0002 + +static void block_wakeup_int(int core, int state) +{ + // TP.TPCHIP.NET.PCBSLEC14.PPMC.PPM_COMMON_REGS.PPM_GPMMR // 0x200F0100 + /* Depending on requested state we write to SCOM1 (CLEAR) or SCOM2 (OR). */ + uint64_t scom = state ? 0x200F0102 : 0x200F0101; + + /* Register is documented, but its bits are reserved... */ + write_scom_for_chiplet(EC00_CHIPLET_ID + core, scom, PPC_BIT(6)); +} + +/* + * TODO: check if we need to save current core/thread number. Both cores under + * quad are currently started, but this may be caused by re-using runtime HOMER. + * + * TODO: save and restore TB. Some time will be lost between entering and + * exiting STOP 15, but we don't have a way of calculating it (I think). + */ +struct save_state { + uint64_t r1; /* stack */ + uint64_t r2; /* TOC */ + uint64_t msr; + uint64_t nia; +} sstate; + +/* + * System reset vector: + * - read thread ID from PIR + * - thread != 0: + * - stop + * - thread == 0: + * - reload r1 and r2 from saved state + * - move nia and msr to HSRR0/1 + * - return from hypervisor interrupt + * - due to clobbers in inline assembly in cpu_winkle all other registers are + * reloaded by compiler + * - contents of vector and floating point registers are lost + */ +asm( +"\ +system_reset: \n\ + mfspr 4, 1023 \n\ + extrdi. 4, 4, 2, 62 \n\ + bne _not_thread0 \n\ + li 3, sstate@l \n\ + oris 3, 3, sstate@h \n\ + ld 1, 0(3) \n\ + ld 2, 8(3) \n\ + ld 4, 16(3) \n\ + mtspr 315, 4 \n\ + ld 4, 24(3) \n\ + mtspr 314, 4 \n\ + hrfid \n\ +_not_thread0: \n\ + stop \n\ + b . \n\ +system_reset_end: \n\ +"); + +extern uint8_t system_reset[]; +extern uint8_t system_reset_end[]; + +static void cpu_winkle(void) +{ + uint64_t psscr = read_spr(SPR_PSSCR); + uint64_t lpcr = read_spr(SPR_LPCR); + /* Clear {External, Decrementer, Other} Exit Enable */ + lpcr &= ~(SPR_LPCR_EEE | SPR_LPCR_DEE | SPR_LPCR_OEE); + /* + * Set Hypervisor Virtualization Interrupt Conditionally Enable + * and Hypervisor Virtualization Exit Enable + */ + lpcr |= SPR_LPCR_HVICE | SPR_LPCR_HVEE; + write_spr(SPR_LPCR, lpcr); + write_spr(SPR_PSSCR, 0x00000000003F00FF); + + /* + * Debugging aid - 0xE40 is Hypervisor Emulation Assistance vector. It is + * taken when processor tries to execute unimplemented instruction. All 0s + * is (and will always be) such an instruction, meaning we will get here + * when processor jumps into uninitialized memory. If this instruction were + * also uninitialized, processor would hit another exception and again jump + * here. This time, however, it would overwrite original HSRR0 value with + * 0xE40. Instruction below is 'b .'. This way HSRR0 will retain its value + * - address of instruction which generated this exception. It can be then + * read with pdbg. + */ + *(volatile uint32_t *)0xE40 = 0x48000000; + + memcpy((void*)0x100, system_reset, system_reset_end - system_reset); + + asm volatile("sync; isync" ::: "memory"); + + /* TODO: address depends on active core */ + /* TODO: do we even need this? Those threads are already stopped */ + // write_scom(0x21010A9C, 0x0008080800000000); + + /* + * Timing facilities may be lost. During their restoration Large Decrementer + * in LPCR may be initially turned off, which may (but shouldn't) result in + * a spurious Decrementer Exception. As we don't have handlers, disable all + * External Interrupts just in case. + */ + sstate.msr = read_msr() & ~PPC_BIT(48); + + /* + * Cannot clobber: + * - r1 (stack) - reloaded from sstate + * - r2 (TOC aka PIC register) - reloaded from sstate + * - r3 (address of sstate) - storage duration limited to block below + */ + { + register void *r3 asm ("r3") = &sstate; + asm volatile("std 1, 0(%0)\n" + "std 2, 8(%0)\n" + "lnia 1\n" + "__tmp_nia:" + "addi 1, 1, wakey - __tmp_nia\n" + "std 1, 24(%0)\n" + "sync\n" + "stop\n" + "wakey:\n" + : "+r"(r3) :: + "r0", "r4", "r5", "r6", "r7", "r8", "r9", "r10", "r11", + "r12", "r13", "r14", "r15", "r16", "r17", "r18", "r19", + "r20", "r21", "r22", "r23", "r24", "r25", "r26", "r27", + "r28", "r29", "r30", "r31", "memory", "cc"); + } + + write_spr(SPR_PSSCR, psscr); +} + +static void istep_16_1(int this_core) +{ + /* + * Wait time 10.5 sec, anything larger than 10737 ms can cause overflow on + * SBE side of the timeout calculations. + */ + long time = 10500; + + /* + * This will request SBE to wake us up after we enter STOP 15. Hopefully + * we will come back to the place where we were before. + */ + printk(BIOS_ERR, "Entering dead man loop\n"); + uint64_t t1 = read_spr(SPR_TB); + psu_command(DEADMAN_LOOP_START, time); + + /* REMOVE ME! */ + /* + * This masks FIR bit that is set by SBE when dead man timer expires, which + * results in checkstop. To help with debugging it is masked here, but it + * must not be in the final code! + */ + write_scom(0x0504000F, PPC_BIT(32)); + + uint64_t t2 = read_spr(SPR_TB); + block_wakeup_int(this_core, 1); + + cpu_winkle(); + + /* + * This tells SBE that we were properly awoken. Hostboot uses default + * timeout of 90 seconds, but if SBE doesn't answer in 10 there is no reason + * to believe it will answer at all. + */ + psu_command(DEADMAN_LOOP_STOP, time); + uint64_t t3 = read_spr(SPR_TB); + + printk(BIOS_ERR, "Dead man loop times: %llx, %llx\n", t2-t1, t3-t1); + printk(BIOS_ERR, "Dead man loop times: %lldns, %lldns\n", (t2-t1)*2, (t3-t1)*2); + // core_checkstop_helper_hwp(..., true) + // p9_core_checkstop_handler(___, true) + // core_checkstop_helper_homer() + // p9_stop_save_scom() and others +} + +/* + * This logic is for SMF disabled only! + */ +void build_homer_image(void *homer_bar) +{ + struct mmap_helper_region_device mdev = {0}; + struct homer_st *homer = homer_bar; + struct xip_hw_header *hw = homer_bar; + uint8_t dd = get_dd(); + int this_core = -1; + uint64_t cores = get_available_cores(&this_core); + + if (this_core == -1) + die("Couldn't found active core\n"); + + printk(BIOS_ERR, "DD%2.2x\n", dd); + + /* HOMER must be aligned to 4M because CME HRMOR has bit for 2M set */ + if (!IS_ALIGNED((uint64_t) homer_bar, 4 * MiB)) + die("HOMER (%p) is not aligned to 4MB\n", homer_bar); + + + + /* + * Temporarily use HOMER read from running system, until code for crafting + * it from HCODE is ready. + */ + if (0) { + + void *map = cbfs_map("homer", NULL); + memcpy(homer_bar, map, 4 * MiB); + cbfs_unmap(map); + + } else { + + + memset(homer_bar, 0, 4 * MiB); + + /* + * This will work as long as we don't call mmap(). mmap() calls + * mem_poll_alloc() which doesn't check if mdev->pool is valid or at least + * not NULL. + */ + mount_part_from_pnor("HCODE", &mdev); + /* First MB of HOMER is unused, we can write OCC image from PNOR there. */ + rdev_readat(&mdev.rdev, hw, 0, 1 * MiB); + + assert(hw->magic == XIP_MAGIC_HW); + assert(hw->image_size <= 1 * MiB); + + build_sgpe(homer, (struct xip_sgpe_header *)(homer_bar + hw->sgpe.offset), + dd); + + build_self_restore(homer, + (struct xip_restore_header *)(homer_bar + hw->restore.offset), + dd); + + build_cme(homer, (struct xip_cme_header *)(homer_bar + hw->cme.offset), dd); + + build_pgpe(homer, (struct xip_pgpe_header *)(homer_bar + hw->pgpe.offset), + dd); + + // TBD + // getPpeScanRings() for CME + // layoutRingsForCME() + // getPpeScanRings for SGPE + // layoutRingsForSGPE() + + // buildParameterBlock(); + // updateCpmrCmeRegion(); + + // Update QPMR Header area in HOMER + // updateQpmrHeader(); + + // Update PPMR Header area in HOMER + // updatePpmrHeader(); + + // Update L2 Epsilon SCOM Registers + // populateEpsilonL2ScomReg( pChipHomer ); + + // Update L3 Epsilon SCOM Registers + // populateEpsilonL3ScomReg( pChipHomer ); + + // Update L3 Refresh Timer Control SCOM Registers + // populateL3RefreshScomReg( pChipHomer, i_procTgt); + + // Populate HOMER with SCOM restore value of NCU RNG BAR SCOM Register + // populateNcuRngBarScomReg( pChipHomer, i_procTgt ); + + // validate SRAM Image Sizes of PPE's + // validateSramImageSize( pChipHomer, sramImgSize ); + + // Update CME/SGPE Flags in respective image header. + // updateImageFlags( pChipHomer, i_procTgt ); + + // Set the Fabric IDs + // setFabricIds( pChipHomer, i_procTgt ); + // - doesn't modify anything? + + // Customize magic word based on endianess + // customizeMagicWord( pChipHomer ); + } + + /* Set up wakeup mode */ + for (int i = 0; i < MAX_CORES; i++) { + if (!IS_EC_FUNCTIONAL(i, cores)) + continue; + + /* + TP.TPCHIP.NET.PCBSLEC14.PPMC.PPM_CORE_REGS.CPPM_CPMMR // 0x200F0106 + // These bits, when set, make core wake up in HV (not UV) + [3] CPPM_CPMMR_RESERVED_2_9 = 1 + [4] CPPM_CPMMR_RESERVED_2_9 = 1 + */ + write_scom_for_chiplet(EC00_CHIPLET_ID + i, 0x200F0106, + PPC_BIT(3) | PPC_BIT(4)); + } + + /* 15.2 set HOMER BAR */ + write_scom(0x05012B00, (uint64_t)homer); + write_scom(0x05012B04, (4 * MiB - 1) & ~((uint64_t)MiB - 1)); + write_scom(0x05012B02, (uint64_t)homer + 8 * 4 * MiB); // FIXME + write_scom(0x05012B06, (7 * MiB - 1) & ~((uint64_t)MiB - 1)); + + /* 15.3 establish EX chiplet */ + /* Multicast groups for cores were assigned in get_available_cores() */ + for (int i = 0; i < MAX_CORES/4; i++) { + if (IS_EQ_FUNCTIONAL(i, cores) && + (read_scom_for_chiplet(EP00_CHIPLET_ID + i, 0xF0001) & PPC_BITMASK(3,5)) + == PPC_BITMASK(3,5)) + scom_and_or_for_chiplet(EP00_CHIPLET_ID + i, 0xF0001, + ~(PPC_BITMASK(3,5) | PPC_BITMASK(16,23)), + PPC_BITMASK(19,21)); + } + + /* Writing OCC CCSR */ + write_scom(0x0006C090, cores); + + /* Writing OCC QCSR */ + uint64_t qcsr = 0; + for (int i = 0; i < MAX_CORES/2; i++) { + if (IS_EX_FUNCTIONAL(i, cores)) + qcsr |= PPC_BIT(i); + } + write_scom(0x0006C094, qcsr); + + /* 15.4 start STOP engine */ + + /* Initialize the PFET controllers */ + for (int i = 0; i < MAX_CORES; i++) { + if ((i % 4) == 0 && IS_EQ_FUNCTIONAL(i/4, cores)) { + /* + TP.TPCHIP.NET.PCBSLEP03.PPMQ.PPM_COMMON_REGS.PPM_PFDLY // 0x100F011B + [all] 0 + [0-3] PPM_PFDLY_POWDN_DLY = 0x9 // 250ns, converted and encoded + [4-7] PPM_PFDLY_POWUP_DLY = 0x9 + */ + write_scom_for_chiplet(EP00_CHIPLET_ID + i/4, 0x100F011B, + PPC_SHIFT(0x9, 3) | PPC_SHIFT(0x9, 7)); + /* + TP.TPCHIP.NET.PCBSLEP03.PPMQ.PPM_COMMON_REGS.PPM_PFOF // 0x100F011D + [all] 0 + [0-3] PPM_PFOFF_VDD_VOFF_SEL = 0x8 + [4-7] PPM_PFOFF_VCS_VOFF_SEL = 0x8 + */ + write_scom_for_chiplet(EP00_CHIPLET_ID + i/4, 0x100F011D, + PPC_SHIFT(0x8, 3) | PPC_SHIFT(0x8, 7)); + } + + /* + TP.TPCHIP.NET.PCBSLEC14.PPMC.PPM_COMMON_REGS.PPM_PFDLY // 0x200F011B + [all] 0 + [0-3] PPM_PFDLY_POWDN_DLY = 0x9 // 250ns, converted and encoded + [4-7] PPM_PFDLY_POWUP_DLY = 0x9 + */ + write_scom_for_chiplet(EC00_CHIPLET_ID + i, 0x200F011B, + PPC_SHIFT(0x9, 3) | PPC_SHIFT(0x9, 7)); + /* + TP.TPCHIP.NET.PCBSLEC14.PPMC.PPM_COMMON_REGS.PPM_PFOF // 0x200F011D + [all] 0 + [0-3] PPM_PFOFF_VDD_VOFF_SEL = 0x8 + [4-7] PPM_PFOFF_VCS_VOFF_SEL = 0x8 + */ + write_scom_for_chiplet(EC00_CHIPLET_ID + i, 0x200F011D, + PPC_SHIFT(0x8, 3) | PPC_SHIFT(0x8, 7)); + + // Reriodic core quiesce workaround + /* + TP.TPCHIP.NET.PCBSLEC14.PPMC.PPM_CORE_REGS.CPPM_CPMMR (WOR) // 0x200F0108 + [all] 0 + [2] CPPM_CPMMR_RESERVED_2 = 1 + */ + write_scom_for_chiplet(EC00_CHIPLET_ID + i, 0x200F0108, PPC_BIT(2)); + } + + /* Condition the PBA back to the base boot configuration */ + pba_reset(); + + /* + * TODO: this is tested only if (ATTR_VDM_ENABLED || ATTR_IVRM_ENABLED), + * both are set (or not) in 15.1 - p9_pstate_parameter_block(). For now + * assume they are enabled. + */ + /* TP.TPCHIP.TPC.ITR.FMU.KVREF_AND_VMEAS_MODE_STATUS_REG // 0x01020007 + if ([16] == 0): die() + */ + if (!(read_scom(0x01020007) & PPC_BIT(16))) + die("VDMs/IVRM are enabled but necessary VREF calibration failed\n"); + + /* First mask bit 7 in OIMR and then clear bit 7 in OISR + TP.TPCHIP.OCC.OCI.OCB.OCB_OCI_OIMR0 (OR) // 0x0006C006 + [all] 0 + [7] OCB_OCI_OISR0_GPE2_ERROR = 1 + TP.TPCHIP.OCC.OCI.OCB.OCB_OCI_OISR0 (CLEAR) // 0x0006C001 + [all] 0 + [7] OCB_OCI_OISR0_GPE2_ERROR = 1 + */ + write_scom(0x0006C006, PPC_BIT(7)); + write_scom(0x0006C001, PPC_BIT(7)); + + /* + * Setup the SGPE Timer Selects + * These hardcoded values are assumed by the SGPE Hcode for setting up + * the FIT and Watchdog values. + TP.TPCHIP.OCC.OCI.GPE3.GPETSEL // 0x00066000 + [all] 0 + [0-3] GPETSEL_FIT_SEL = 0x1 // FIT - fixed interval timer + [4-7] GPETSEL_WATCHDOG_SEL = 0xA + */ + write_scom(0x00066000, PPC_SHIFT(0x1, 3) | PPC_SHIFT(0xA, 7)); + + /* Clear error injection bits + *0x0006C18B // undocumented, PU_OCB_OCI_OCCFLG2_CLEAR + [all] 0 + [30] 1 // OCCFLG2_SGPE_HCODE_STOP_REQ_ERR_INJ + */ + write_scom(0x0006C18B, PPC_BIT(30)); + + // Boot the STOP GPE + stop_gpe_init(homer); + + istep_16_1(this_core); + + //hexdump(&homer->qpmr, sgpe->qpmr.size); +} diff --git a/src/soc/ibm/power9/homer.h b/src/soc/ibm/power9/homer.h new file mode 100644 index 00000000000..0c93789f444 --- /dev/null +++ b/src/soc/ibm/power9/homer.h @@ -0,0 +1,319 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ + +#ifndef __SOC_IBM_POWER9_HOMER_H +#define __SOC_IBM_POWER9_HOMER_H + +#include + +/* All fields are big-endian */ + +#define HOMER_ONE_REGION_SIZE (1 * MiB) + +/* + * OCC complex shares 768 kB SRAM, according to Figure 23-3 + * https://wiki.raptorcs.com/w/images/c/ce/POWER9_um_OpenPOWER_v21_10OCT2019_pub.pdf + */ +#define SGPE_SRAM_IMG_SIZE (74 * KiB) +#define PGPE_SRAM_IMG_SIZE (50 * KiB) + +/* According to above figure, CMEs have 32 kB SRAM each, how does it fit? */ +#define CME_SRAM_IMG_SIZE (64 * KiB) +#define GPE_BOOTLOADER_SIZE (1 * KiB) + +/* + * This is how CACHE_SCOM_REGION_SIZE is defined in hostboot. On the other hand, + * hostboot defines that quad has 256 entries, 16 bytes each. This gives 4kB per + * quad, and there are 6 quads (maximum) on POWER9 CPU, which gives 24kB total. + * One of the values is obviously wrong, but because this region is immediately + * followed by a padding it should not overwrite anything important. This is one + * of the reasons to clear whole HOMER, not just the used parts. + */ +#define CACHE_SCOM_REGION_SIZE (6 * KiB) +#define CACHE_SCOM_REGION_OFFSET (128 * KiB) +#define CACHE_SCOM_AUX_SIZE (64 * KiB) +#define CACHE_SCOM_AUX_OFFSET (512 * KiB) +#define SELF_RESTORE_REGION_SIZE (9 * KiB) +#define CORE_SCOM_RESTORE_SIZE (6 * KiB) +#define CORE_SCOM_RESTORE_OFFSET (256 * KiB) +#define PGPE_AUX_TASK_SIZE (2 * KiB) +#define PGPE_OCC_SHARED_SRAM_SIZE (2 * KiB) +#define PGPE_DOPTRACE_SIZE (64 * KiB) +#define PGPE_DOPTRACE_OFFSET (64 * KiB) +#define OCC_PARAM_BLOCK_REGION_SIZE (16 * KiB) +#define OCC_PARAM_BLOCK_REGION_OFFSET (128 * KiB) +#define PSTATE_OUTPUT_TABLES_SIZE (16 * KiB) +#define OCC_WOF_TABLES_SIZE (256 * KiB) +#define OCC_WOF_TABLES_OFFSET (768 * KiB) +#define PPMR_HEADER_SIZE (1 * KiB) + +/* =================== QPMR =================== */ + +struct qpmr_header { + uint64_t magic; /* "SGPE_1.0" */ + uint32_t l1_offset; + uint32_t reserved; + uint32_t l2_offset; + uint32_t l2_len; + uint32_t build_date; + uint32_t build_ver; + uint64_t reserved_flags; + uint32_t img_offset; + uint32_t img_len; + uint32_t common_ring_offset; + uint32_t common_ring_len; + uint32_t common_ovrd_offset; + uint32_t common_ovrd_len; + uint32_t spec_ring_offset; + uint32_t spec_ring_len; + uint32_t scom_offset; + uint32_t scom_len; + uint32_t aux_offset; + uint32_t aux_len; + uint32_t stop_ffdc_offset; + uint32_t stop_ffdc_len; + uint32_t boot_prog_code; + uint32_t sram_img_size; + uint32_t max_quad_restore_entry; + uint32_t enable_24x7_ima; + uint32_t sram_region_start; + uint32_t sram_region_size; +} __attribute__((packed, aligned(512))); + +/* This header is part of SRAM image, it starts after interrupt vectors. */ +#define INT_VECTOR_SIZE 384 +struct sgpe_img_header { + uint64_t magic; + uint32_t reset_addr; + uint32_t reserve1; + uint32_t ivpr_addr; + uint32_t timebase_hz; + uint32_t build_date; + uint32_t build_ver; + uint32_t reserve_flags; + uint16_t location_id; + uint16_t addr_extension; + uint32_t cmn_ring_occ_offset; + uint32_t cmn_ring_ovrd_occ_offset; + uint32_t spec_ring_occ_offset; + uint32_t scom_offset; + uint32_t scom_mem_offset; + uint32_t scom_mem_len; + uint32_t aux_offset; + uint32_t aux_len; + uint32_t aux_control; + uint32_t reserve4; + uint64_t chtm_mem_cfg; +}; + +struct sgpe_st { + struct qpmr_header header; + uint8_t l1_bootloader[GPE_BOOTLOADER_SIZE]; + uint8_t l2_bootloader[GPE_BOOTLOADER_SIZE]; + uint8_t sram_image[SGPE_SRAM_IMG_SIZE]; +}; + +check_member(sgpe_st, l1_bootloader, 512); + +struct qpmr_st { + struct sgpe_st sgpe; + uint8_t pad1[CACHE_SCOM_REGION_OFFSET - sizeof(struct sgpe_st)]; + uint8_t cache_scom_region[CACHE_SCOM_REGION_SIZE]; + uint8_t pad2[CACHE_SCOM_AUX_OFFSET + -(CACHE_SCOM_REGION_OFFSET + CACHE_SCOM_REGION_SIZE)]; + uint8_t aux[CACHE_SCOM_AUX_SIZE]; +}; + +check_member(qpmr_st, cache_scom_region, 128 * KiB); +check_member(qpmr_st, aux, 512 * KiB); + +/* =================== CPMR =================== */ + +struct cpmr_header { + uint32_t attn_opcodes[2]; + uint64_t magic; /* "CPMR_2.0" */ + uint32_t build_date; + uint32_t version; + uint8_t reserved_flags[4]; + uint8_t self_restore_ver; + uint8_t stop_api_ver; + uint8_t urmor_fix; + uint8_t fused_mode_status; + uint32_t img_offset; + uint32_t img_len; + uint32_t cme_common_ring_offset; + uint32_t cme_common_ring_len; + uint32_t cme_pstate_offset; + uint32_t cme_pstate_len; + uint32_t core_spec_ring_offset; + uint32_t core_spec_ring_len; + uint32_t core_scom_offset; + uint32_t core_scom_len; + uint32_t core_self_restore_offset; + uint32_t core_self_restore_len; + uint32_t core_max_scom_entry; + uint32_t quad0_pstate_offset; + uint32_t quad1_pstate_offset; + uint32_t quad2_pstate_offset; + uint32_t quad3_pstate_offset; + uint32_t quad4_pstate_offset; + uint32_t quad5_pstate_offset; +} __attribute__((packed, aligned(256))); + +struct smf_core_self_restore { + uint32_t thread_restore_area[4][512 / sizeof(uint32_t)]; + uint32_t thread_save_area[4][256 / sizeof(uint32_t)]; + uint32_t core_restore_area[512 / sizeof(uint32_t)]; + uint32_t core_save_area[512 / sizeof(uint32_t)]; +}; + +/* This header is part of SRAM image, it starts after interrupt vectors. */ +struct cme_img_header { + uint64_t magic; + uint32_t hcode_offset; + uint32_t hcode_len; + uint32_t common_ring_offset; + uint32_t cmn_ring_ovrd_offset; + uint32_t common_ring_len; + uint32_t pstate_region_offset; + uint32_t pstate_region_len; + uint32_t core_spec_ring_offset; + uint32_t max_spec_ring_len; + uint32_t scom_offset; + uint32_t scom_len; + uint32_t mode_flags; + uint16_t location_id; + uint16_t qm_mode_flags; + uint32_t timebase_hz; + uint64_t cpmr_phy_addr; + uint64_t unsec_cpmr_phy_addr; + uint32_t pstate_offset; + uint32_t custom_length; +}; + +#define MAX_CORES 24 + +struct cpmr_st { + struct cpmr_header header; + uint8_t exe[SELF_RESTORE_REGION_SIZE - sizeof(struct cpmr_header)]; + struct smf_core_self_restore core_self_restore[MAX_CORES]; + uint8_t pad[CORE_SCOM_RESTORE_OFFSET - + (SELF_RESTORE_REGION_SIZE + + MAX_CORES * sizeof(struct smf_core_self_restore))]; + uint8_t core_scom[CORE_SCOM_RESTORE_SIZE]; + uint8_t cme_sram_region[CME_SRAM_IMG_SIZE]; +}; + +check_member(cpmr_st, core_self_restore, 9 * KiB); +check_member(cpmr_st, core_scom, 256 * KiB); + +/* =================== PPMR =================== */ + +struct ppmr_header { + uint64_t magic; + uint32_t l1_offset; + uint32_t reserved; + uint32_t l2_offset; + uint32_t l2_len; + uint32_t build_date; + uint32_t build_ver; + uint64_t reserved_flags; + uint32_t hcode_offset; + uint32_t hcode_len; + uint32_t gppb_offset; + uint32_t gppb_len; + uint32_t lppb_offset; + uint32_t lppb_len; + uint32_t oppb_offset; + uint32_t oppb_len; + uint32_t pstables_offset; + uint32_t pstables_len; + uint32_t sram_img_size; + uint32_t boot_prog_code; + uint32_t wof_table_offset; + uint32_t wof_table_len; + uint32_t aux_task_offset; + uint32_t aux_task_len; + uint32_t doptrace_offset; + uint32_t doptrace_len; + uint32_t sram_region_start; + uint32_t sram_region_size; +} __attribute__((packed, aligned(512))); + +/* This header is part of SRAM image, it starts after interrupt vectors. */ +struct pgpe_img_header { + uint64_t magic; + uint32_t sys_reset_addr; + uint32_t shared_sram_addr; + uint32_t ivpr_addr; + uint32_t shared_sram_len; + uint32_t build_date; + uint32_t build_ver; + uint16_t flags; + uint16_t reserve1; + uint32_t timebase_hz; + uint32_t gppb_sram_addr; + uint32_t hcode_len; + uint32_t gppb_mem_offset; + uint32_t gppb_len; + uint32_t gen_pstables_mem_offset; + uint32_t gen_pstables_len; + uint32_t occ_pstables_sram_addr; + uint32_t occ_pstables_len; + uint32_t beacon_addr; + uint32_t quad_status_addr; + uint32_t wof_state_address; + uint32_t req_active_quad_address; + uint32_t wof_table_addr; + uint32_t wof_table_len; + uint32_t core_throttle_assert_cnt; + uint32_t core_throttle_deassert_cnt; + uint32_t aux_controls; + uint32_t optrace_pointer; + uint32_t doptrace_offset; + uint32_t doptrace_len; + uint32_t wof_values_address; +}; + +struct ppmr_st { + struct ppmr_header header; + uint8_t pad0[PPMR_HEADER_SIZE - sizeof(struct ppmr_header)]; + uint8_t l1_bootloader[GPE_BOOTLOADER_SIZE]; + uint8_t l2_bootloader[GPE_BOOTLOADER_SIZE]; + uint8_t pgpe_sram_img[PGPE_SRAM_IMG_SIZE]; + uint8_t aux_task[PGPE_AUX_TASK_SIZE]; + uint8_t pad1[PGPE_DOPTRACE_OFFSET - (PPMR_HEADER_SIZE + + GPE_BOOTLOADER_SIZE + GPE_BOOTLOADER_SIZE + + PGPE_SRAM_IMG_SIZE + PGPE_AUX_TASK_SIZE)]; + /* Deep Operational Trace */ + uint8_t doptrace[PGPE_DOPTRACE_SIZE]; + /* + * Two following fields in hostboot have different sizes, but are padded + * to 16kB each anyway. There are two consecutive paddings between + * pstate_table and wof_tables in hostboot. + */ + uint8_t occ_parm_block[OCC_PARAM_BLOCK_REGION_SIZE]; + uint8_t pstate_table[PSTATE_OUTPUT_TABLES_SIZE]; + uint8_t pad2[OCC_WOF_TABLES_OFFSET - (OCC_PARAM_BLOCK_REGION_OFFSET + + OCC_PARAM_BLOCK_REGION_SIZE + PSTATE_OUTPUT_TABLES_SIZE)]; + uint8_t wof_tables[OCC_WOF_TABLES_SIZE]; +}; + +check_member(ppmr_st, occ_parm_block, 128 * KiB); +check_member(ppmr_st, pstate_table, 144 * KiB); +check_member(ppmr_st, wof_tables, 768 * KiB); + +struct homer_st { + uint8_t occ_host_area[HOMER_ONE_REGION_SIZE]; + struct qpmr_st qpmr; + uint8_t pad_qpmr[HOMER_ONE_REGION_SIZE - sizeof(struct qpmr_st)]; + struct cpmr_st cpmr; + uint8_t pad_cpmr[HOMER_ONE_REGION_SIZE - sizeof(struct cpmr_st)]; + struct ppmr_st ppmr; + uint8_t pad_ppmr[HOMER_ONE_REGION_SIZE - sizeof(struct ppmr_st)]; +}; + +check_member(homer_st, qpmr, 1 * MiB); +check_member(homer_st, cpmr, 2 * MiB); +check_member(homer_st, ppmr, 3 * MiB); + +#endif /* __SOC_IBM_POWER9_HOMER_H */ diff --git a/src/soc/ibm/power9/istep_13_3.c b/src/soc/ibm/power9/istep_13_3.c index d3a1ffd3748..d98ab20eb36 100644 --- a/src/soc/ibm/power9/istep_13_3.c +++ b/src/soc/ibm/power9/istep_13_3.c @@ -60,6 +60,10 @@ void istep_13_3(void) * out of order command/response pair. Just fill a buffer, send it and make * sure the receiver (SBE) gets it. If you still want to know the details, * start digging here: https://github.com/open-power/hostboot/blob/master/src/usr/scan/scandd.C#L169 + * + * TODO: this is the only place where `putRing()` is called, but it isn't + * the only place where PSU commands are used (see 16.1-16.2). Consider + * making a function from this. */ // TP.TPCHIP.PIB.PSU.PSU_SBE_DOORBELL_REG if (read_scom(PSU_SBE_DOORBELL_REG) & PPC_BIT(0)) diff --git a/src/soc/ibm/power9/istep_14_1.c b/src/soc/ibm/power9/istep_14_1.c index 3200d3438cd..5f0624927a5 100644 --- a/src/soc/ibm/power9/istep_14_1.c +++ b/src/soc/ibm/power9/istep_14_1.c @@ -11,7 +11,7 @@ static void fir_unmask(int mcs_i) { chiplet_id_t id = mcs_ids[mcs_i]; int mca_i; - const int is_dd20 = pvr_revision() == SPR_PVR_REV(2, 0); + const int is_dd20 = get_dd() == 0x20; /* Bits in other registers (act0, mask) are already set properly. MC01.MCBIST.MBA_SCOMFIR.MCBISTFIRACT1 [3] MCBISTFIRQ_MCBIST_BRODCAST_OUT_OF_SYNC = 0 // checkstop (0,0,0) diff --git a/src/soc/ibm/power9/istep_6_11.c b/src/soc/ibm/power9/istep_6_11.c new file mode 100644 index 00000000000..de1b6678085 --- /dev/null +++ b/src/soc/ibm/power9/istep_6_11.c @@ -0,0 +1,360 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ + +#include +#include +#include +#include +#include +#include + +void istep_6_11(void) +{ + printk(BIOS_EMERG, "starting istep 14.1\n"); + report_istep(6, 11); + printk(BIOS_EMERG, "ending istep 14.1\n"); +} + +// static void* host_start_occ_xstop_handler(void *io_pArgs) +// { +// // If we have nothing external (FSP or OCC) to handle checkstops we are +// // better off just crashing and having a chance to pull the HB +// // traces off the system live + +// TARGETING::Target * l_sys = nullptr; +// TARGETING::targetService().getTopLevelTarget( l_sys ); + +// #ifndef CONFIG_HANG_ON_MFG_SRC_TERM +// //When in MNFG_FLAG_SRC_TERM mode enable reboots to allow HB +// //to analyze now that the OCC is up and alive +// auto l_mnfgFlags = l_sys->getAttr(); + +// // Check to see if SRC_TERM bit is set in MNFG flags +// if ((l_mnfgFlags & TARGETING::MNFG_FLAG_SRC_TERM) && +// !(l_mnfgFlags & TARGETING::MNFG_FLAG_IMMEDIATE_HALT)) +// { +// //If HB_VOLATILE MFG_TERM_REBOOT_ENABLE flag is set at this point +// //Create errorlog to terminate the boot. +// Util::semiPersistData_t l_semiData; +// Util::readSemiPersistData(l_semiData); +// if (l_semiData.mfg_term_reboot == Util::MFG_TERM_REBOOT_ENABLE) +// { +// reboot(); +// } + +// Util::semiPersistData_t l_newSemiData; +// Util::readSemiPersistData(l_newSemiData); +// l_newSemiData.mfg_term_reboot = Util::MFG_TERM_REBOOT_ENABLE; +// Util::writeSemiPersistData(l_newSemiData); + +// SENSOR::RebootControlSensor l_rbotCtl; +// l_rbotCtl.setRebootControl(SENSOR::RebootControlSensor::autoRebootSetting::ENABLE_REBOOTS); +// } +// #endif + +// TARGETING::Target* masterproc = NULL; +// TARGETING::targetService().masterProcChipTargetHandle(masterproc); + +// #ifdef CONFIG_IPLTIME_CHECKSTOP_ANALYSIS +// void* l_homerVirtAddrBase = VmmManager::INITIAL_MEM_SIZE; +// uint64_t l_homerPhysAddrBase = mm_virt_to_phys(l_homerVirtAddrBase); +// uint64_t l_commonPhysAddr = l_homerPhysAddrBase + VMM_HOMER_REGION_SIZE; +// HBPM::loadPMComplex(masterproc, l_homerPhysAddrBase, l_commonPhysAddr); +// HBOCC::startOCCFromSRAM(masterproc); +// #endif +// Kernel::MachineCheck::setCheckstopData(); +// } + +// static void loadPMComplex( +// TARGETING::Target * i_target, +// uint64_t i_homerPhysAddr, +// uint64_t i_commonPhysAddr) +// { +// resetPMComplex(i_target); +// void* l_homerVAddr = convertHomerPhysToVirt(i_target, i_homerPhysAddr); +// if(nullptr == l_homerVAddr) +// { +// return; +// } +// uint64_t l_occImgPaddr = i_homerPhysAddr + HOMER_OFFSET_TO_OCC_IMG; +// uint64_t occ_bootloader = l_homerVAddr + HOMER_OFFSET_TO_OCC_IMG; +// loadOCCSetup(i_target, l_occImgPaddr, occ_bootloader, i_commonPhysAddr); +// HBOCC::loadOCCImageDuringIpl(occ_bootloader); // analyzed +// #if !defined(__HOSTBOOT_RUNTIME) +// HBOCC::loadHostDataToSRAM(i_target); +// #else +// loadHostDataToHomer(i_target, occ_bootloader + HOMER_OFFSET_TO_OCC_HOST_DATA); +// loadHcode(i_target, l_homerVAddr, HBPM::PM_LOAD); +// #endif +// } + +static void loadOCCImageDuringIpl(void* occ_bootloader) +{ + uint8_t occ_partition[OCC_LENGTH] = {}; + struct mmap_helper_region_device mdev = {}; + mount_part_from_pnor("OCC", &mdev); + // maybe it could be faster and less memory hungry + // by loading only required part of OCC + // it would require loading in parts, + // because some offsets are in OCC itself. + rdev_readat(&mdev.rdev, occ_partition, 0, OCC_LENGTH); + + uint32_t bootloader_length = *(uint32_t*)(occ_partition + OCC_OFFSET_LENGTH); + + uint8_t* main_app = occ_partition + bootloader_length; + uint32_t main_app_length = *(uint32_t*)(main_app + OCC_OFFSET_LENGTH); + + uint32_t gpe_0_length = *(uint32_t*)(main_app + OCC_OFFSET_GPE0_LENGTH); + uint32_t gpe_1_length = *(uint32_t*)(main_app + OCC_OFFSET_GPE1_LENGTH); + + // GPE0 application is stored right after the 405 main in memory + uint8_t* gpe_0_app = main_app + main_app_length; + uint8_t* gpe_1_app = main_app + main_app_length + gpe_0_length; + + memcpy(occ_bootloader, occ_partition, bootloader_length); + uint8_t occ_modified_section[OCC_MODIFIED_SECTION_SIZE]; + memcpy(occ_modified_section, main_app, OCC_OFFSET_FREQ + sizeof(uint32_t)); + + *(uint16_t*)((uint8_t*)occ_modified_section + OCC_OFFSET_IPL_FLAG) |= 1; + *(uint32_t*)((uint8_t*)occ_modified_section + OCC_OFFSET_FREQ) = FREQ_PB_MHZ; + + // Write 405 Main application to SRAM + writeSRAM( + OCC_405_SRAM_ADDRESS, + (uint64_t*)main_app, + bootloader_length); + // Overwrite the modified part of Main in SRAM: + writeSRAM( + OCC_405_SRAM_ADDRESS, + (uint64_t*)occ_modified_section, + (uint32_t)OCC_OFFSET_FREQ + sizeof(uint32_t)); + writeSRAM( + OCC_GPE0_SRAM_ADDRESS, + (uint64_t*)gpe_0_app, + gpe_0_length); + writeSRAM( + OCC_GPE1_SRAM_ADDRESS, + (uint64_t*)gpe_1_app, + gpe_1_length); +} + +static void pm_pss_init(void) +{ + write_scom( + PU_SPIMPSS_ADC_CTRL_REG0, + (read_scom(PU_SPIMPSS_ADC_CTRL_REG0) + & ~PPC_BITMASK(0, 11)) + | PPC_BIT(2)); + write_scom( + PU_SPIPSS_ADC_CTRL_REG1, + (read_scom(PU_SPIPSS_ADC_CTRL_REG1) + & ~PPC_BITMASK(0, 17)) + | PPC_BIT(0) | PPC_BIT(10) | PPC_BIT(12)); + write_scom( + PU_SPIPSS_ADC_CTRL_REG2, + read_scom(PU_SPIPSS_ADC_CTRL_REG2) & ~PPC_BITMASK(0, 16)); + write_scom( + PU_SPIPSS_ADC_WDATA_REG, + 0); + write_scom( + PU_SPIPSS_P2S_CTRL_REG0, + (read_scom(PU_SPIPSS_P2S_CTRL_REG0) + & ~PPC_BITMASK(0, 11)) + | PPC_BIT(2)); + write_scom( + PU_SPIPSS_P2S_CTRL_REG1, + (read_scom(PU_SPIPSS_P2S_CTRL_REG1) + & ~PPC_BITMASK(1, 3)) + | PPC_BIT(0) | PPC_BIT(10) + | PPC_BIT(12) | PPC_BIT(17)); + write_scom( + PU_SPIPSS_P2S_CTRL_REG2, + read_scom(PU_SPIPSS_P2S_CTRL_REG2) & ~PPC_BITMASK(0, 16)); + write_scom( + PU_SPIPSS_P2S_WDATA_REG, + 0); + write_scom( + PU_SPIPSS_100NS_REG, + (read_scom(PU_SPIPSS_100NS_REG) & 0xFFFFFFFF) + | (uint64_t)(FREQ_PB_MHZ / 40) << 32); +} + +static void pm_occ_fir_init(void) +{ + uint64_t iv_mask = read_scom(iv_mask_address); + + write_scom(iv_proc, iv_fir_address, 0); + write_scom(iv_proc, iv_action0_address, C405_ECC_UE); + write_scom( + iv_proc, + iv_action1_address, + C405_ECC_CE | C405_OCI_MC_CHK + | C405DCU_M_TIMEOUT | GPE0_ERR + | GPE0_OCISLV_ERR | GPE1_ERR + | GPE1_OCISLV_ERR | GPE2_OCISLV_ERR + | GPE3_OCISLV_ERR | JTAGACC_ERR + | OCB_DB_OCI_RDATA_PARITY | OCB_DB_OCI_SLVERR + | OCB_DB_OCI_TIMEOUT | OCB_DB_PIB_DATA_PARITY_ERR + | OCB_IDC0_ERR | OCB_IDC1_ERR + | OCB_IDC2_ERR | OCB_IDC3_ERR + | OCB_PIB_ADDR_PARITY_ERR | OCC_CMPLX_FAULT + | OCC_CMPLX_NOTIFY | SRAM_CE + | SRAM_DATAOUT_PERR | SRAM_OCI_ADDR_PARITY_ERR + | SRAM_OCI_BE_PARITY_ERR | SRAM_OCI_WDATA_PARITY + | SRAM_READ_ERR | SRAM_SPARE_DIRERR0 + | SRAM_SPARE_DIRERR1 | SRAM_SPARE_DIRERR2 + | SRAM_SPARE_DIRERR3 | SRAM_UE + | SRAM_WRITE_ERR | SRT_FSM_ERR + | STOP_RCV_NOTIFY_PRD); + write_scom( + iv_proc, + iv_fir_address + MASK_WOR_INCR, + iv_mask | C405ICU_M_TIMEOUT + | CME_ERR_NOTIFY | EXT_TRAP + | FIR_PARITY_ERR_DUP | FIR_PARITY_ERR + | GPE0_HALTED | GPE0_WD_TIMEOUT + | GPE1_HALTED | GPE1_WD_TIMEOUT + | GPE2_ERR | GPE2_HALTED + | GPE2_WD_TIMEOUT | GPE3_ERR + | GPE3_HALTED | GPE3_WD_TIMEOUT + | OCB_ERR | OCC_FW0 + | OCC_FW1 | OCC_HB_NOTIFY + | PPC405_CHIP_RESET | PPC405_CORE_RESET + | PPC405_DBGSTOPACK | PPC405_SYS_RESET + | PPC405_WAIT_STATE | SPARE_59 + | SPARE_60 | SPARE_61 + | SPARE_ERR_38); + write_scom( + iv_proc, + iv_fir_address + MASK_WAND_INCR, + iv_mask & ~C405_ECC_CE + & ~C405_ECC_UE & ~C405_OCI_MC_CHK + & ~C405DCU_M_TIMEOUT & ~GPE0_ERR + & ~GPE0_OCISLV_ERR & ~GPE1_ERR + & ~GPE1_OCISLV_ERR & ~GPE2_OCISLV_ERR + & ~GPE3_OCISLV_ERR & ~JTAGACC_ERR + & ~OCB_DB_OCI_RDATA_PARITY & ~OCB_DB_OCI_SLVERR + & ~OCB_DB_OCI_TIMEOUT & ~OCB_DB_PIB_DATA_PARITY_ERR + & ~OCB_IDC0_ERR & ~OCB_IDC1_ERR + & ~OCB_IDC2_ERR & ~OCB_IDC3_ERR + & ~OCB_PIB_ADDR_PARITY_ERR & ~OCC_CMPLX_FAULT + & ~OCC_CMPLX_NOTIFY & ~SRAM_CE + & ~SRAM_DATAOUT_PERR & ~SRAM_OCI_ADDR_PARITY_ERR + & ~SRAM_OCI_BE_PARITY_ERR & ~SRAM_OCI_WDATA_PARITY + & ~SRAM_READ_ERR & ~SRAM_SPARE_DIRERR0 + & ~SRAM_SPARE_DIRERR1 & ~SRAM_SPARE_DIRERR2 + & ~SRAM_SPARE_DIRERR3 & ~SRAM_UE + & ~SRAM_WRITE_ERR & ~SRT_FSM_ERR + & ~STOP_RCV_NOTIFY_PRD); +} + +static void startOCCFromSRAM(void) +{ + // executed only for master processor!!! + if(MASTER_PROC) + { + pm_pss_init(); + } + + pm_occ_fir_init(); + // executed only for master processor!!! + if(MASTER_PROC) + { + clear_occ_special_wakeups(); + } + + write_scom(PU_OCB_OCI_OIRR0A_SCOM, 0x218780f800000000); + write_scom(PU_OCB_OCI_OIRR1A_SCOM, 0x0003d03c00000000); + write_scom(PU_OCB_OCI_OIRR0B_SCOM, 0x2181801800000000); + write_scom(PU_OCB_OCI_OIRR1B_SCOM, 0x0003d00c00000000); + write_scom(PU_OCB_OCI_OIRR0C_SCOM, 0x010280ac00000000); + write_scom(PU_OCB_OCI_OIRR1C_SCOM, 0x0001901400000000); + + // executed only for master processor!!! + if(MASTER_PROC) + { + p9_pm_occ_control( + makeStart405Instruction()); + } + + write_scom(OCB_OITR0, 0xffffffffffffffff); + write_scom(OCB_OIEPR0, 0xffffffffffffffff); +} + +static void p9_pm_occ_control(const uint64_t i_ppc405_jump_to_main_instr) +{ + write_scom(PU_SRAM_SRBV3_SCOM, i_ppc405_jump_to_main_instr); + write_scom(PU_JTG_PIB_OJCFG_AND, ~PPC_BIT(JTG_PIB_OJCFG_DBG_HALT_BIT)); + write_scom(PU_OCB_PIB_OCR_OR, PPC_BIT(OCB_PIB_OCR_CORE_RESET_BIT)); + write_scom(PU_OCB_PIB_OCR_CLEAR, PPC_BIT(OCB_PIB_OCR_CORE_RESET_BIT)); +} + +static void clear_occ_special_wakeups(void) +{ + for(size_t chiplet_index = 0; + chiplet_index < NUMBER_OF_EX_CHIPLETS; + ++chiplet_index) + { + write_scom_for_chiplet( + EX_CHIPLETS[chiplet_index], + EX_PPM_SPWKUP_OCC, + read_scom_for_chiplet(EX_CHIPLETS[chiplet_index], + EX_PPM_SPWKUP_OCC) & ~PPC_BIT(0)); + } +} + +void setCheckstopData(void) +{ + write_msr(read_msr(void) | 0x1000); +} + +static void pm_occ_fir_reset(void) +{ + putScom( + iv_fir_address + MASK_WOR_INCR, + 0xFFFFFFFFFFFFFFFF); + putScom( + iv_fir_address + MASK_WAND_INCR, + 0xFFFFFFFFFFFFFFFF & ~OCC_HB_NOTIFY); + putScom( + iv_action0_address, + read_scom(iv_action0_address) | OCC_HB_NOTIFY); + putScom( + iv_action1_address, + read_scom(iv_action1_address) & ~OCC_HB_NOTIFY); +} + +static void pm_ocb_setup(const uint32_t i_ocb_bar) +{ + write_scom(OCBCSRn_OR[0], PPC_BIT(OCB_PIB_OCBCSR0_OCB_STREAM_MODE)); + write_scom(OCBCSRn_CLEAR[0], PPC_BIT(OCB_PIB_OCBCSR0_OCB_STREAM_TYPE)); + write_scom(OCBARn[0], i_ocb_bar << 32); +} + +static void put_ocb_indirect( + const uint32_t i_ocb_req_length, + const uint32_t i_oci_address, + uint64_t* io_ocb_buffer) +{ + write_scom(PU_OCB_PIB_OCBAR0, i_oci_address << 32); + uint64_t ocb_pib = read_scom(PU_OCB_PIB_OCBCSR0_RO); + if((ocb_pib & OCB_PIB_OCBCSR0_OCB_STREAM_MODE) + && (ocb_pib & OCB_PIB_OCBCSR0_OCB_STREAM_TYPE)) + { + uint64_t stream_push_control = read_scom(PU_OCB_OCI_OCBSHCS0_SCOM); + if (stream_push_control & OCB_OCI_OCBSHCS0_PUSH_ENABLE) + for(uint8_t l_counter = 0; l_counter < 4; l_counter++) + { + if (!(stream_push_control & OCB_OCI_OCBSHCS0_PUSH_FULL)) + { + break; + } + // Hostboot has delay of 0 here + wait_us(1, false); + stream_push_control = read_scom(PU_OCB_OCI_OCBSHCS0_SCOM); + } + } + for(uint32_t l_index = 0; l_index < i_ocb_req_length; l_index++) + { + write_scom(PU_OCB_PIB_OCBDR0, io_ocb_buffer[l_index]); + } +} diff --git a/src/soc/ibm/power9/occ.c b/src/soc/ibm/power9/occ.c new file mode 100644 index 00000000000..bd03a4c3358 --- /dev/null +++ b/src/soc/ibm/power9/occ.c @@ -0,0 +1,109 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ + +#include +#include +#include + + +static void pm_ocb_setup(const uint32_t i_ocb_bar) +{ + write_scom(OCBCSRn_OR[0], PPC_BIT(OCB_PIB_OCBCSR0_OCB_STREAM_MODE)); + write_scom(OCBCSRn_CLEAR[0], PPC_BIT(OCB_PIB_OCBCSR0_OCB_STREAM_TYPE)); + write_scom(OCBARn[0], (uint64_t)i_ocb_bar << 32); +} + +static void put_ocb_indirect( + const uint32_t i_ocb_req_length, + const uint32_t i_oci_address, + uint64_t* io_ocb_buffer) +{ + write_scom(PU_OCB_PIB_OCBAR0, (uint64_t)i_oci_address << 32); + uint64_t ocb_pib = read_scom(PU_OCB_PIB_OCBCSR0_RO); + if((ocb_pib & OCB_PIB_OCBCSR0_OCB_STREAM_MODE) + && (ocb_pib & OCB_PIB_OCBCSR0_OCB_STREAM_TYPE)) + { + uint64_t stream_push_control = read_scom(PU_OCB_OCI_OCBSHCS0_SCOM); + if (stream_push_control & OCB_OCI_OCBSHCS0_PUSH_ENABLE) + for(uint8_t l_counter = 0; l_counter < 4; l_counter++) + { + if (!(stream_push_control & OCB_OCI_OCBSHCS0_PUSH_FULL)) + { + break; + } + // Hostboot has delay of 0 here + wait_us(1, false); + stream_push_control = read_scom(PU_OCB_OCI_OCBSHCS0_SCOM); + } + } + for(uint32_t l_index = 0; l_index < i_ocb_req_length; l_index++) + { + write_scom(PU_OCB_PIB_OCBDR0, io_ocb_buffer[l_index]); + } +} + +static void get_ocb_indirect( + const uint32_t i_ocb_req_length, + const uint32_t i_oci_address, + uint64_t* io_ocb_buffer) +{ + write_scom(PU_OCB_PIB_OCBAR0, (uint64_t)i_oci_address << 32); + for(uint32_t l_loopCount = 0; l_loopCount < i_ocb_req_length; l_loopCount++) + { + io_ocb_buffer[l_loopCount] = read_scom(PU_OCB_PIB_OCBDR0); + } +} + +void writeOCCSRAM( + const uint32_t address, + uint64_t * buffer, + size_t data_length) +{ + pm_ocb_setup(address); + put_ocb_indirect( + data_length / 8, + address, + buffer); +} + +void readOCCSRAM( + const uint32_t address, + uint64_t * buffer, + size_t data_length) +{ + pm_ocb_setup(address); + get_ocb_indirect( + data_length / 8, + address, + buffer); +} + +uint64_t makeStart405Instruction(void) +{ + uint64_t l_epAddr; + readOCCSRAM( + OCC_405_SRAM_ADDRESS + OCC_OFFSET_MAIN_EP, + &l_epAddr, + 8); + + // The branch instruction is of the form 0x4BXXXXX200000000, where X + // is the address of the 405 main's entry point (alligned as shown). + // Example: If 405 main's EP is FFF5B570, then the branch instruction + // will be 0x4bf5b57200000000. The last two bits of the first byte of + // the branch instruction must be '2' according to the OCC instruction + // set manual. + return OCC_BRANCH_INSTR | (((uint64_t)(BRANCH_ADDR_MASK & l_epAddr)) << 32); +} + +void clear_occ_special_wakeups(void) +{ + for(size_t chiplet_index = 0; + chiplet_index < NUMBER_OF_EX_CHIPLETS; + ++chiplet_index) + { + write_scom_for_chiplet( + EX_CHIPLETS[chiplet_index], + EX_PPM_SPWKUP_OCC, + read_scom_for_chiplet(EX_CHIPLETS[chiplet_index], + EX_PPM_SPWKUP_OCC) & ~PPC_BIT(0)); + } +} diff --git a/src/soc/ibm/power9/rom_media.c b/src/soc/ibm/power9/rom_media.c index b6522538389..be51327bdba 100644 --- a/src/soc/ibm/power9/rom_media.c +++ b/src/soc/ibm/power9/rom_media.c @@ -327,8 +327,12 @@ struct region_device_ops ecc_rdev_ops = { .readat = ecc_readat, }; -static void mount_part_from_pnor(const char *part_name, - struct mmap_helper_region_device *mdev) +/* TODO: move to some header */ +void mount_part_from_pnor(const char *part_name, + struct mmap_helper_region_device *mdev); + +void mount_part_from_pnor(const char *part_name, + struct mmap_helper_region_device *mdev) { size_t base, size; unsigned int i, block_size, entry_count = 0; @@ -406,19 +410,17 @@ static void mount_part_from_pnor(const char *part_name, continue; base = block_size * e->base; - /* This is size of the partition, it includes header and ECC */ + /* This is size of the partition, it does not include header or ECC */ size = e->actual; mdev->rdev.ops = &no_ecc_rdev_ops; /* Skip PNOR partition header */ base += 0x1000; - size -= 0x1000; if (e->user.data[0] & FFS_ENRY_INTEG_ECC) { printk(BIOS_DEBUG, "%s partition has ECC\n", part_name); mdev->rdev.ops = &ecc_rdev_ops; base += 0x200; - size -= 0x200; size = size / 9 * 8; } diff --git a/src/soc/ibm/power9/romstage.c b/src/soc/ibm/power9/romstage.c index a24f1ebe124..1406f84f07e 100644 --- a/src/soc/ibm/power9/romstage.c +++ b/src/soc/ibm/power9/romstage.c @@ -343,6 +343,7 @@ void main(void) vpd_pnor_main(); prepare_dimm_data(); + istep_6_11(); report_istep(13,1); // no-op istep_13_2(); istep_13_3(); diff --git a/src/soc/ibm/power9/xip.h b/src/soc/ibm/power9/xip.h new file mode 100644 index 00000000000..fb386c17daa --- /dev/null +++ b/src/soc/ibm/power9/xip.h @@ -0,0 +1,112 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ + +#ifndef __SOC_IBM_POWER9_XIP_H +#define __SOC_IBM_POWER9_XIP_H + +#define XIP_MAGIC_HW (0x5849502020204857) // "XIP HW" +#define XIP_MAGIC_SGPE (0x5849502053475045) // "XIP SGPE" +#define XIP_MAGIC_RESTORE (0x5849502052455354) // "XIP REST" + +/* All fields are big-endian */ + +struct xip_section { + uint32_t offset; + uint32_t size; + uint8_t alignment; + uint8_t dd_support; + uint8_t reserved8[2]; +}; + +/* Each XIP header holds 15 XIP sections, some of them are sometimes unused. */ +#define XIP_HEADER_COMMON_FIELDS_TOP \ + uint64_t magic; \ + uint64_t l1_addr; \ + uint64_t l2_addr; \ + uint64_t kernel_addr; \ + uint64_t link_address; \ + uint64_t reserved64[3]; \ + struct xip_section sections[5]; + +#define XIP_HEADER_COMMON_FIELDS_BOTTOM \ + uint32_t image_size; \ + /* In yyyymmdd format, e.g. 20110630, when read as decimal, not hex */ \ + uint32_t build_date; \ + /* In hhmm format, e.g. 0756 */ \ + uint32_t build_time; \ + char build_tag[20]; \ + uint8_t header_version; \ + uint8_t normalized; \ + uint8_t toc_sorted; \ + uint8_t reserved8[5]; \ + char build_user[16]; \ + char build_host[40]; \ + char reserved_char[8]; \ + +struct xip_hw_header { + XIP_HEADER_COMMON_FIELDS_TOP + struct xip_section sgpe; + struct xip_section restore; + struct xip_section cme; + struct xip_section pgpe; + struct xip_section ioppe; + struct xip_section fppe; + struct xip_section rings; + struct xip_section overlays; + struct xip_section unused[2]; /* Pad to 15 sections. */ + XIP_HEADER_COMMON_FIELDS_BOTTOM +}; + +struct xip_sgpe_header { + XIP_HEADER_COMMON_FIELDS_TOP + struct xip_section qpmr; + struct xip_section l1_bootloader; + struct xip_section l2_bootloader; + struct xip_section hcode; + struct xip_section unused[6]; /* Pad to 15 sections. */ + XIP_HEADER_COMMON_FIELDS_BOTTOM +}; + +struct xip_restore_header { + XIP_HEADER_COMMON_FIELDS_TOP + struct xip_section cpmr; + struct xip_section self; + struct xip_section unused[8]; /* Pad to 15 sections. */ + XIP_HEADER_COMMON_FIELDS_BOTTOM +}; + +struct xip_cme_header { + XIP_HEADER_COMMON_FIELDS_TOP + struct xip_section hcode; + struct xip_section unused[9]; /* Pad to 15 sections. */ + XIP_HEADER_COMMON_FIELDS_BOTTOM +}; + +struct xip_pgpe_header { + XIP_HEADER_COMMON_FIELDS_TOP + struct xip_section ppmr; + struct xip_section l1_bootloader; + struct xip_section l2_bootloader; + struct xip_section hcode; + struct xip_section aux_task; + struct xip_section unused[5]; /* Pad to 15 sections. */ + XIP_HEADER_COMMON_FIELDS_BOTTOM +}; + +#define DD_CONTAINER_MAGIC 0x4444434F // "DDCO" + +struct dd_block { + uint32_t offset; + uint32_t size; + uint8_t dd; + uint8_t reserved[3]; +}; + +struct dd_container { + uint32_t magic; + uint8_t num; + uint8_t reserved[3]; + struct dd_block blocks[0]; +}; + + +#endif /* __SOC_IBM_POWER9_XIP_H */