diff options
author | Stephan Broyles <sbroyles@us.ibm.com> | 2015-02-24 16:36:44 -0600 |
---|---|---|
committer | Stephan Broyles <sbroyles@us.ibm.com> | 2015-04-13 13:05:36 -0500 |
commit | 8fdf501b07d6eaf715539fae9977372b12673da8 (patch) | |
tree | 89d7b4b51dec8c2e21b081a636e082d008a081de /src/occ | |
parent | c473df288f702b1803205ac6eda4cc1781fec4f8 (diff) | |
download | talos-occ-8fdf501b07d6eaf715539fae9977372b12673da8.tar.gz talos-occ-8fdf501b07d6eaf715539fae9977372b12673da8.zip |
Adding firdata feature to OCC
Adding the fir data collection code to capture
FIR information to PNOR upon a runtime checkstop.
Change-Id: Ia7d6f9a234ca2c86783f7ac8f51407c15895358e
RTC: 115282
Reviewed-on: http://gfw160.aus.stglabs.ibm.com:8080/gerrit/15970
Reviewed-by: Stephan Broyles <sbroyles@us.ibm.com>
Tested-by: Stephan Broyles <sbroyles@us.ibm.com>
Diffstat (limited to 'src/occ')
44 files changed, 5378 insertions, 284 deletions
diff --git a/src/occ/Makefile b/src/occ/Makefile index ad2bacd..bba6075 100755 --- a/src/occ/Makefile +++ b/src/occ/Makefile @@ -5,15 +5,15 @@ # # OpenPOWER OnChipController Project # -# Contributors Listed Below - COPYRIGHT 2011,2014 -# [+] Google Inc. +# Contributors Listed Below - COPYRIGHT 2011,2015 # [+] International Business Machines Corp. # +# # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # -# http://www.apache.org/licenses/LICENSE-2.0 +# http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, @@ -49,6 +49,7 @@ APP_INCLUDES += -I. APP_INCLUDES += -I../occ APP_INCLUDES += -I../../occ APP_INCLUDES += -I./timer +APP_INCLUDES += -I./firdata export APP_INCLUDES diff --git a/src/occ/app.mk b/src/occ/app.mk index 2ce5e81..025b100 100755 --- a/src/occ/app.mk +++ b/src/occ/app.mk @@ -5,15 +5,15 @@ # # OpenPOWER OnChipController Project # -# Contributors Listed Below - COPYRIGHT 2011,2014 -# [+] Google Inc. +# Contributors Listed Below - COPYRIGHT 2011,2015 # [+] International Business Machines Corp. # +# # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # -# http://www.apache.org/licenses/LICENSE-2.0 +# http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, @@ -107,6 +107,7 @@ LDFLAGS = \ -L $(OCC)/thread \ -L $(OCC)/timer \ -L $(OCC)/trac \ + -L $(OCC)/firdata \ -lssx -lppc32 --oformat=elf32-powerpc -melf32ppc # Added for linking files compiled using gnu assembler diff --git a/src/occ/cfiles.mk b/src/occ/cfiles.mk index f8173a0..8fc299d 100755 --- a/src/occ/cfiles.mk +++ b/src/occ/cfiles.mk @@ -91,8 +91,21 @@ occ_CFILES = \ occbuildname.c \ common.c \ +occ_CFILES += firdata/ecc.c +occ_CFILES += firdata/fir_data_collect.c +occ_CFILES += firdata/firData.c +occ_CFILES += firdata/fsi.c +occ_CFILES += firdata/lpc.c +occ_CFILES += firdata/native.c +occ_CFILES += firdata/nor_micron.c +occ_CFILES += firdata/pnor_util.c +occ_CFILES += firdata/scom_trgt.c +occ_CFILES += firdata/scom_util.c +occ_CFILES += firdata/sfc_ast2400.c + occ_SFILES = cmdh/ll_ffdc.S + occ_cfiles = ${occ_CFILES:.c=.o} occ_Sfiles = ${occ_SFILES:.S=.o} diff --git a/src/occ/cmdh/cmdh_fsp_cmds.c b/src/occ/cmdh/cmdh_fsp_cmds.c index d0f268c..d15decb 100755 --- a/src/occ/cmdh/cmdh_fsp_cmds.c +++ b/src/occ/cmdh/cmdh_fsp_cmds.c @@ -216,9 +216,10 @@ ERRL_RC cmdh_poll_v10(cmdh_fsp_rsp_t * o_rsp_ptr) cmdh_poll_resp_v10_fixed_t * l_poll_rsp = (cmdh_poll_resp_v10_fixed_t *) o_rsp_ptr; // Byte 1 - l_poll_rsp->status.word = SMGR_validate_get_valid_states(); //TODO: Do we need to set FIR master? + l_poll_rsp->status.word = SMGR_validate_get_valid_states(); // Byte 2 - l_poll_rsp->ext_status.word = 0; //TODO: What values should we set for memthrotOT and n power? + //TODO: What values should we set for memthrotOT and n power? + l_poll_rsp->ext_status.word = 0; for ( k = 0; k < MAX_NUM_CORES; k++ ) { diff --git a/src/occ/cmdh/cmdh_fsp_cmds_datacnfg.c b/src/occ/cmdh/cmdh_fsp_cmds_datacnfg.c index 730b98b..09bc805 100755 --- a/src/occ/cmdh/cmdh_fsp_cmds_datacnfg.c +++ b/src/occ/cmdh/cmdh_fsp_cmds_datacnfg.c @@ -1073,7 +1073,10 @@ errlHndl_t data_store_pstate_super(const cmdh_fsp_cmd_t * i_cmd_ptr, // // Name: data_store_role // -// Description: TODO Add description +// Description: Tell the OCC if it should run as a master or slave. HTMGT knows +// which OCC is the master from the MRW. To be the master OCC +// requires a connection to the APSS. Until an OCC is told a role +// it should default to running as a slave // // End Function Specification errlHndl_t data_store_role(const cmdh_fsp_cmd_t * i_cmd_ptr, @@ -1082,17 +1085,13 @@ errlHndl_t data_store_role(const cmdh_fsp_cmd_t * i_cmd_ptr, errlHndl_t l_errlHndl = NULL; uint8_t l_old_role = G_occ_role; uint8_t l_new_role = OCC_SLAVE; - uint8_t l_fir_master = 0x00; ERRL_RC l_rc = ERRL_RC_SUCCESS; // Cast the command to the struct for this format cmdh_set_role_t * l_cmd_ptr = (cmdh_set_role_t *)i_cmd_ptr; - // With BMC-based systems, there is a FIR Master bit - l_fir_master = l_cmd_ptr->role >> 7; - - // Mask off the FIR Master bit - l_new_role = l_cmd_ptr->role & 0x7F; + // Mask off the OCC role + l_new_role = l_cmd_ptr->role & OCC_ROLE_MASTER_MASK; // Must be in standby state before we can change roles diff --git a/src/occ/cmdh/cmdh_fsp_cmds_datacnfg.h b/src/occ/cmdh/cmdh_fsp_cmds_datacnfg.h index 4f20f0d..a17b982 100755 --- a/src/occ/cmdh/cmdh_fsp_cmds_datacnfg.h +++ b/src/occ/cmdh/cmdh_fsp_cmds_datacnfg.h @@ -84,6 +84,10 @@ typedef enum DATA_FRU_MAX, } eConfigDataFruType; +// Set OCC Role Masks +#define OCC_ROLE_MASTER_MASK 0x01 +#define OCC_ROLE_FIR_MASTER_MASK 0x40 + // Used by TMGT to send OCC the PstateSuperStruct typedef struct __attribute__ ((packed)) { diff --git a/src/occ/errl/errl.c b/src/occ/errl/errl.c index 646be8f..ca0a279 100755 --- a/src/occ/errl/errl.c +++ b/src/occ/errl/errl.c @@ -59,6 +59,7 @@ errlHndl_t G_occErrSlots[ERRL_MAX_SLOTS] = { }; extern uint8_t G_occ_interrupt_type; +extern bool G_fir_collection_required; void hexDumpLog( errlHndl_t i_log ); @@ -354,7 +355,9 @@ void addTraceToErrl( void * l_traceAddr = io_err; uint16_t l_actualSizeOfUsrDtls = 0; pore_status_t l_gpe0_status; + ocb_oisr0_t l_oisr0_status; static bool L_gpe_halt_traced = FALSE; + static bool L_sys_checkstop_traced = FALSE; // check if GPE was frozen due to a checkstop @@ -362,10 +365,20 @@ void addTraceToErrl( if(l_gpe0_status.fields.freeze_action && !L_gpe_halt_traced) { L_gpe_halt_traced = TRUE; - TRAC_ERR("addTraceToErrl: OCC GPE halted due to checkstop. GPE0 status[0x%08x%08x]", - l_gpe0_status.words.high_order, l_gpe0_status.words.low_order); + TRAC_IMP("addTraceToErrl: Frozen GPE0 detected. GPE0 status[0x%08x%08x]", + l_gpe0_status.words.high_order, + l_gpe0_status.words.low_order); } + // Check if there is a system checkstop + l_oisr0_status.value = in32(OCB_OISR0); + if (l_oisr0_status.fields.check_stop && !L_sys_checkstop_traced) + { + L_sys_checkstop_traced = TRUE; + TRAC_IMP("addTraceToErrl: System checkstop detected"); + } + + // 1. Check if error log is not null // 2. error log is not invalid // 3. error log has not been commited @@ -520,16 +533,20 @@ void reportErrorLog( errlHndl_t i_err, uint16_t i_entrySize ) i_err->iv_userDetails.iv_modId, i_err->iv_reasonCode, i_err->iv_userDetails.iv_userData1, i_err->iv_userDetails.iv_userData2); - // If this system is using PSIHB complex, send an interrupt to Host so that - // Host can inform HTMGT to collect the error log - if (G_occ_interrupt_type == PSIHB_INTERRUPT) + // Defer the interrupt if FIR collection is required + if (!G_fir_collection_required) { - // From OCC OpenPower Interface v1.1, OCC needs to set bits 0 and 1 of - // the OCB_OCCMISC register - l_reg.fields.core_ext_intr = 1; - l_reg.fields.reason_intr = 1; + // If this system is using PSIHB complex, send an interrupt to Host so that + // Host can inform HTMGT to collect the error log + if (G_occ_interrupt_type == PSIHB_INTERRUPT) + { + // From OCC OpenPower Interface v1.1, OCC needs to set bits 0 and 1 of + // the OCB_OCCMISC register + l_reg.fields.core_ext_intr = 1; + l_reg.fields.reason_intr = 1; - out32(OCB_OCCMISC_OR, l_reg.value); + out32(OCB_OCCMISC_OR, l_reg.value); + } } } @@ -544,16 +561,33 @@ void reportErrorLog( errlHndl_t i_err, uint16_t i_entrySize ) void commitErrl( errlHndl_t *io_err ) { pore_status_t l_gpe0_status; + ocb_oisr0_t l_oisr0_status; + static bool L_log_commits_suspended_by_safe_mode = FALSE; - if ( io_err != NULL ) + if (!L_log_commits_suspended_by_safe_mode && io_err != NULL) { // check if handle is valid and is NOT empty if ((*io_err != NULL ) && ( *io_err != INVALID_ERR_HNDL )) { - // check if GPE was frozen due to a checkstop + // Check if the GPE is frozen or if a system + // checkstop has occurred l_gpe0_status.value = in64(PORE_GPE0_STATUS); - if(l_gpe0_status.fields.freeze_action) + l_oisr0_status.value = in32(OCB_OISR0); + + if (l_gpe0_status.fields.freeze_action + || + l_oisr0_status.fields.check_stop) { + if (l_gpe0_status.fields.freeze_action) + { + TRAC_IMP("Frozen GPE0 detected by commitErrl"); + } + + if (l_oisr0_status.fields.check_stop) + { + TRAC_IMP("System checkstop detected by commitErrl"); + } + //Go to the reset state to minimize errors reset_state_request(RESET_REQUESTED_DUE_TO_ERROR); @@ -565,6 +599,14 @@ void commitErrl( errlHndl_t *io_err ) //set callouts to 0 (*io_err)->iv_numCallouts = 0; + + TRAC_IMP("SAFE mode required, suspending error log commits. FIR capture required."); + + // Suspend all error log commits + L_log_commits_suspended_by_safe_mode = TRUE; + + // Motivate FIR data collection + G_fir_collection_required = TRUE; } // if reset action bit is set force severity to unrecoverable and diff --git a/src/occ/firdata/ecc.c b/src/occ/firdata/ecc.c new file mode 100644 index 0000000..6696678 --- /dev/null +++ b/src/occ/firdata/ecc.c @@ -0,0 +1,272 @@ +/* IBM_PROLOG_BEGIN_TAG */ +/* This is an automatically generated prolog. */ +/* */ +/* $Source: src/occ/firdata/ecc.C $ */ +/* */ +/* OpenPOWER OnChipController Project */ +/* */ +/* Contributors Listed Below - COPYRIGHT 2015 */ +/* [+] International Business Machines Corp. */ +/* */ +/* */ +/* Licensed under the Apache License, Version 2.0 (the "License"); */ +/* you may not use this file except in compliance with the License. */ +/* You may obtain a copy of the License at */ +/* */ +/* http://www.apache.org/licenses/LICENSE-2.0 */ +/* */ +/* Unless required by applicable law or agreed to in writing, software */ +/* distributed under the License is distributed on an "AS IS" BASIS, */ +/* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or */ +/* implied. See the License for the specific language governing */ +/* permissions and limitations under the License. */ +/* */ +/* IBM_PROLOG_END_TAG */ + +/*#include <stdio.h> */ +#include <endian.h> +#include <assert.h> + +#include <native.h> +#include <ecc.h> + +/** Matrix used for ECC calculation. + * + * Each row of this is the set of data word bits that are used for + * the calculation of the corresponding ECC bit. The parity of the + * bitset is the value of the ECC bit. + * + * ie. ECC[n] = eccMatrix[n] & data + * + * Note: To make the math easier (and less shifts in resulting code), + * row0 = ECC7. HW numbering is MSB, order here is LSB. + * + * These values come from the HW design of the ECC algorithm. + */ +static uint64_t eccMatrix[] = { + /*0000000000000000111010000100001000111100000011111001100111111111 */ + 0x0000e8423c0f99ff, + /*0000000011101000010000100011110000001111100110011111111100000000 */ + 0x00e8423c0f99ff00, + /*1110100001000010001111000000111110011001111111110000000000000000 */ + 0xe8423c0f99ff0000, + /*0100001000111100000011111001100111111111000000000000000011101000 */ + 0x423c0f99ff0000e8, + /*0011110000001111100110011111111100000000000000001110100001000010 */ + 0x3c0f99ff0000e842, + /*0000111110011001111111110000000000000000111010000100001000111100 */ + 0x0f99ff0000e8423c, + /*1001100111111111000000000000000011101000010000100011110000001111 */ + 0x99ff0000e8423c0f, + /*1111111100000000000000001110100001000010001111000000111110011001 */ + 0xff0000e8423c0f99 +}; + +/** Syndrome calculation matrix. + * + * Maps syndrome to flipped bit. + * + * To perform ECC correction, this matrix is a look-up of the bit + * that is bad based on the binary difference of the good and bad + * ECC. This difference is called the "syndrome". + * + * When a particular bit is on in the data, it cause a column from + * eccMatrix being XOR'd into the ECC field. This column is the + * "effect" of each bit. If a bit is flipped in the data then its + * "effect" is missing from the ECC. You can calculate ECC on unknown + * quality data and compare the ECC field between the calculated + * value and the stored value. If the difference is zero, then the + * data is clean. If the difference is non-zero, you look up the + * difference in the syndrome table to identify the "effect" that + * is missing, which is the bit that is flipped. + * + * Notice that ECC bit flips are recorded by a single "effect" + * bit (ie. 0x1, 0x2, 0x4, 0x8 ...) and double bit flips are identified + * by the UE status in the table. + * + * Bits are in MSB order. + */ +static uint8_t syndromeMatrix[] = { + ECC_GD, ECC_E7, ECC_E6, ECC_UE, ECC_E5, ECC_UE, ECC_UE, 47, + ECC_E4, ECC_UE, ECC_UE, 37, ECC_UE, 35, 39, ECC_UE, + ECC_E3, ECC_UE, ECC_UE, 48, ECC_UE, 30, 29, ECC_UE, + ECC_UE, 57, 27, ECC_UE, 31, ECC_UE, ECC_UE, ECC_UE, + ECC_E2, ECC_UE, ECC_UE, 17, ECC_UE, 18, 40, ECC_UE, + ECC_UE, 58, 22, ECC_UE, 21, ECC_UE, ECC_UE, ECC_UE, + ECC_UE, 16, 49, ECC_UE, 19, ECC_UE, ECC_UE, ECC_UE, + 23, ECC_UE, ECC_UE, ECC_UE, ECC_UE, 20, ECC_UE, ECC_UE, + ECC_E1, ECC_UE, ECC_UE, 51, ECC_UE, 46, 9, ECC_UE, + ECC_UE, 34, 10, ECC_UE, 32, ECC_UE, ECC_UE, 36, + ECC_UE, 62, 50, ECC_UE, 14, ECC_UE, ECC_UE, ECC_UE, + 13, ECC_UE, ECC_UE, ECC_UE, ECC_UE, ECC_UE, ECC_UE, ECC_UE, + ECC_UE, 61, 8, ECC_UE, 41, ECC_UE, ECC_UE, ECC_UE, + 11, ECC_UE, ECC_UE, ECC_UE, ECC_UE, ECC_UE, ECC_UE, ECC_UE, + 15, ECC_UE, ECC_UE, ECC_UE, ECC_UE, ECC_UE, ECC_UE, ECC_UE, + ECC_UE, ECC_UE, 12, ECC_UE, ECC_UE, ECC_UE, ECC_UE, ECC_UE, + ECC_E0, ECC_UE, ECC_UE, 55, ECC_UE, 45, 43, ECC_UE, + ECC_UE, 56, 38, ECC_UE, 1, ECC_UE, ECC_UE, ECC_UE, + ECC_UE, 25, 26, ECC_UE, 2, ECC_UE, ECC_UE, ECC_UE, + 24, ECC_UE, ECC_UE, ECC_UE, ECC_UE, ECC_UE, 28, ECC_UE, + ECC_UE, 59, 54, ECC_UE, 42, ECC_UE, ECC_UE, 44, + 6, ECC_UE, ECC_UE, ECC_UE, ECC_UE, ECC_UE, ECC_UE, ECC_UE, + 5, ECC_UE, ECC_UE, ECC_UE, ECC_UE, ECC_UE, ECC_UE, ECC_UE, + ECC_UE, ECC_UE, ECC_UE, ECC_UE, ECC_UE, ECC_UE, ECC_UE, ECC_UE, + ECC_UE, 63, 53, ECC_UE, 0, ECC_UE, ECC_UE, ECC_UE, + 33, ECC_UE, ECC_UE, ECC_UE, ECC_UE, ECC_UE, ECC_UE, ECC_UE, + 3, ECC_UE, ECC_UE, 52, ECC_UE, ECC_UE, ECC_UE, ECC_UE, + ECC_UE, ECC_UE, ECC_UE, ECC_UE, ECC_UE, ECC_UE, ECC_UE, ECC_UE, + 7, ECC_UE, ECC_UE, ECC_UE, ECC_UE, ECC_UE, ECC_UE, ECC_UE, + ECC_UE, 60, ECC_UE, ECC_UE, ECC_UE, ECC_UE, ECC_UE, ECC_UE, + ECC_UE, ECC_UE, ECC_UE, ECC_UE, 4, ECC_UE, ECC_UE, ECC_UE, + ECC_UE, ECC_UE, ECC_UE, ECC_UE, ECC_UE, ECC_UE, ECC_UE, ECC_UE, +}; + +/** Returns the parity of x, i.e. the number of 1-bits in x modulo 2. + * Replacement for __builtin_parityl + */ +uint8_t parity_check( uint64_t i_data ) +{ + int ones = 0; + size_t x; + for( x=0; x<(sizeof(i_data)*8); x++ ) + { + if( i_data & (0x8000000000000000ull >> x) ) + { + ones++; + } + } + return ones%2; +} + +/** Create the ECC field corresponding to a 8-byte data field + * + * @param[in] i_data - The 8 byte data to generate ECC for. + * @return The 1 byte ECC corresponding to the data. + */ +uint8_t generateECC(uint64_t i_data) +{ + uint8_t result = 0; + + int i = 0; + for (i = 0; i < 8; i++) + { + result |= (parity_check(eccMatrix[i] & i_data) << i); + } + + return result; +} + +/** Verify the data and ECC match or indicate how they are wrong. + * + * @param[in] i_data - The data to check ECC on. + * @param[in] i_ecc - The [supposed] ECC for the data. + * + * @return eccBitfield or 0-64. + * + * @retval GD - Indicates the data is good (matches ECC). + * @retval UE - Indicates the data is uncorrectable. + * @retval all others - Indication of which bit is incorrect. + */ +uint8_t verifyECC(uint64_t i_data, uint8_t i_ecc) +{ + return syndromeMatrix[generateECC(i_data) ^ i_ecc]; +} + +/** Correct the data and/or ECC. + * + * @param[in,out] io_data - Data to check / correct. + * @param[in,out] io_ecc - ECC to check / correct. + * + * @return eccBitfield or 0-64. + * + * @retval GD - Data is good. + * @retval UE - Data is uncorrectable. + * @retval all others - which bit was corrected. + */ +uint8_t correctECC(uint64_t* io_data, uint8_t* io_ecc) +{ + uint8_t badBit = verifyECC(*io_data, *io_ecc); + + if ((badBit != ECC_GD) && (badBit != ECC_UE)) /* Good is done, UE is hopeless. */ + { + /* Determine if the ECC or data part is bad, do bit flip. */ + if (badBit >= ECC_E7) + { + *io_ecc ^= (1 << (badBit - ECC_E7)); + } + else + { + *io_data ^= (1ul << (63 - badBit)); + } + } + return badBit; +} + +void injectECC(const uint8_t* i_src, + size_t i_srcSz, + uint8_t* o_dst) +{ + assert(0 == (i_srcSz % sizeof(uint64_t))); + + size_t i = 0; + size_t o = 0; + for(i = 0, o = 0; + i < i_srcSz; + i += sizeof(uint64_t), o += sizeof(uint64_t) + sizeof(uint8_t)) + { + /* Read data word, copy to destination. */ + uint64_t data = *((const uint64_t*)(&i_src[i])); + *((uint64_t*)(&o_dst[o])) = data; + data = be64toh(data); + + /* Calculate ECC, copy to destination. */ + uint8_t ecc = generateECC(data); + o_dst[o + sizeof(uint64_t)] = ecc; + } +} + +eccStatus removeECC(uint8_t* io_src, + uint8_t* o_dst, size_t i_dstSz) +{ + assert(0 == (i_dstSz % sizeof(uint64_t))); + + eccStatus rc = ECC_CLEAN; + + size_t i = 0, o = 0; + for(i = 0, o = 0; + o < i_dstSz; + i += sizeof(uint64_t) + sizeof(uint8_t), o += sizeof(uint64_t)) + { + /* Read data and ECC parts. */ + uint64_t data = *((uint64_t*)(&io_src[i])); + data = be64toh(data); + uint8_t ecc = io_src[i + sizeof(uint64_t)]; + + /* Calculate failing bit and fix data. */ + uint8_t badBit = correctECC(&data, &ecc); + + /* Return data to big endian. */ + data = htobe64(data); + + /* Perform correction and status update. */ + if (badBit == ECC_UE) + { + rc = ECC_UNCORRECTABLE; + } + else if (badBit != ECC_GD) + { + if (rc != ECC_UNCORRECTABLE) + { + rc = ECC_CORRECTED; + } + *((uint64_t*)(&io_src[i])) = data; + io_src[i + sizeof(uint64_t)] = ecc; + } + + /* Copy fixed data to destination buffer. */ + *((uint64_t*)(&o_dst[o])) = data; + } + + return rc; +} + diff --git a/src/occ/firdata/ecc.h b/src/occ/firdata/ecc.h new file mode 100644 index 0000000..12ffe4e --- /dev/null +++ b/src/occ/firdata/ecc.h @@ -0,0 +1,76 @@ +/* IBM_PROLOG_BEGIN_TAG */ +/* This is an automatically generated prolog. */ +/* */ +/* $Source: src/occ/firdata/ecc.H $ */ +/* */ +/* OpenPOWER OnChipController Project */ +/* */ +/* Contributors Listed Below - COPYRIGHT 2015 */ +/* [+] International Business Machines Corp. */ +/* */ +/* */ +/* Licensed under the Apache License, Version 2.0 (the "License"); */ +/* you may not use this file except in compliance with the License. */ +/* You may obtain a copy of the License at */ +/* */ +/* http://www.apache.org/licenses/LICENSE-2.0 */ +/* */ +/* Unless required by applicable law or agreed to in writing, software */ +/* distributed under the License is distributed on an "AS IS" BASIS, */ +/* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or */ +/* implied. See the License for the specific language governing */ +/* permissions and limitations under the License. */ +/* */ +/* IBM_PROLOG_END_TAG */ +#ifndef __PNOR_ECC_H +#define __PNOR_ECC_H + +#include <native.h> + +/** @file ecc.H + * @brief Interfaces for the P8 8-byte ECC algorithm. + */ + +/** Status for the ECC removal function. */ +typedef uint8_t eccStatus; +#define ECC_CLEAN 0x00 /*< No ECC Error was detected. */ +#define ECC_CORRECTED 0x01 /*< ECC error detected and corrected. */ +#define ECC_UNCORRECTABLE 0x02 /*< ECC error detected and uncorrectable. */ + +/** Bit field identifiers for syndrome calculations. */ +typedef uint8_t eccBitfields; +#define ECC_GD 0xff /*< Good ECC matches. */ +#define ECC_UE 0xfe /*< Uncorrectable. */ +#define ECC_E0 71 /*< Error in ECC bit 0 */ +#define ECC_E1 70 /*< Error in ECC bit 1 */ +#define ECC_E2 69 /*< Error in ECC bit 2 */ +#define ECC_E3 68 /*< Error in ECC bit 3 */ +#define ECC_E4 67 /*< Error in ECC bit 4 */ +#define ECC_E5 66 /*< Error in ECC bit 5 */ +#define ECC_E6 65 /*< Error in ECC bit 6 */ +#define ECC_E7 64 /*< Error in ECC bit 7 */ + +/** Inject ECC into a data stream. + * + * @param[in] i_src - Source data to create ECC on. + * @param[in] i_srcSz - Size in bytes of source data. + * @param[out] o_dst - Destination buffer of data+ECC. + * + * @note i_srcSz must be a multiple of 8 bytes. + */ +void injectECC(const uint8_t* i_src, size_t i_srcSz, + uint8_t* o_dst); + +/** Remove ECC from a data stream. + * + * @param[in,out] io_src - Source data+ECC stream. + * @param[out] o_dst - Destination buffer for data only. + * @param[in] i_dstSz - Size in bytes of destination ((srcSz / 9) * 8). + * + * @note i_dstSz must be a multiple of 8 bytes. + */ +eccStatus removeECC(uint8_t* io_src, + uint8_t* o_dst, size_t i_dstSz); + + +#endif diff --git a/src/occ/firdata/firData.c b/src/occ/firdata/firData.c new file mode 100644 index 0000000..e84e8fd --- /dev/null +++ b/src/occ/firdata/firData.c @@ -0,0 +1,754 @@ +/* IBM_PROLOG_BEGIN_TAG */ +/* This is an automatically generated prolog. */ +/* */ +/* $Source: src/occ/firdata/firData.C $ */ +/* */ +/* OpenPOWER OnChipController Project */ +/* */ +/* Contributors Listed Below - COPYRIGHT 2015 */ +/* [+] International Business Machines Corp. */ +/* */ +/* */ +/* Licensed under the Apache License, Version 2.0 (the "License"); */ +/* you may not use this file except in compliance with the License. */ +/* You may obtain a copy of the License at */ +/* */ +/* http://www.apache.org/licenses/LICENSE-2.0 */ +/* */ +/* Unless required by applicable law or agreed to in writing, software */ +/* distributed under the License is distributed on an "AS IS" BASIS, */ +/* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or */ +/* implied. See the License for the specific language governing */ +/* permissions and limitations under the License. */ +/* */ +/* IBM_PROLOG_END_TAG */ + +#include <native.h> + +#include <homerData_common.h> +#include <pnorData_common.h> +#include <pnor_util.h> +#include <scom_trgt.h> +#include <scom_util.h> + +/** Keeps track of pointers to register lists in the HOMER data for each target + * type. */ +typedef struct +{ + uint32_t * glbl; /*/< Global registers */ + uint32_t * fir; /*/< Normal FIRs */ + uint32_t * reg; /*/< Normal registers */ + uint64_t * idFir; /*/< Indirect-SCOM FIRs */ + uint64_t * idReg; /*/< Indirect-SCOM registers */ + +} FirData_ListPointers_t; + +/** Contains pointers and sizes for the HOMER and PNOR data buffers. */ +typedef struct +{ + uint8_t * hBuf; /*/< Pointer to the HOMER data buffer */ + uint32_t maxHBufSize; /*/< Maximum size of the HOMER data buffer */ + HOMER_Data_t * hData; /*/< Pointer to the HOMER header data */ + + uint8_t * pBuf; /*/< Pointer to the PNOR data buffer */ + uint32_t maxPBufSize; /*/< Maximum size of the PNOR data buffer */ + PNOR_Data_t * pData; /*/< Pointer to the PNOR header data */ + uint32_t pBufSize; /*/< Current size of the PNOR data buffer */ + + FirData_ListPointers_t hPtrs[MAX_TRGTS]; /*/< Pointers to the register lists */ + +} FirData_t; + +/*------------------------------------------------------------------------------ */ + +/** @brief Add generic data to the PNOR buffer. + * @param io_fd The FIR data stuct. + * @param i_data Pointer to the data to add to the buffer. + * @param i_dataSize Size of the data to add to the buffer. + * @return True, if where is no room to add the new data. False, otherwise. + */ +bool FirData_addDataToPnor( FirData_t * io_fd, void * i_data, + uint32_t i_dataSize ) +{ + bool full = (io_fd->maxPBufSize < io_fd->pBufSize + i_dataSize); + + if ( full ) + { + /* Indicate the PNOR data is full. */ + io_fd->pData->full = 1; + } + else + { + /* Copy data to PNOR buffer. */ + memcpy( &io_fd->pBuf[io_fd->pBufSize], i_data, i_dataSize ); + io_fd->pBufSize += i_dataSize; + } + + return full; +} + +/*------------------------------------------------------------------------------ */ + +/** @brief SCOMs hardware and adds a normal register to PNOR. + * @param io_fd The FIR data stuct. + * @param io_pTrgt Pointer to PNOR target. + * @param i_sTrgt SCOM target. + * @param i_addr 32-bit address to SCOM. + * @param o_nonZero True if the value of the register was non-zero. False, + * otherwise. + * @return True if the PNOR buffer is full, false if there was room. + */ +bool FirData_addRegToPnor( FirData_t * io_fd, PNOR_Trgt_t * io_pTrgt, + SCOM_Trgt_t i_sTrgt, uint32_t i_addr, + bool * o_nonZero ) +{ + bool full = false; + + int32_t rc = 0; + PNOR_Reg_t reg = { i_addr, 0 }; + + *o_nonZero = false; + + do + { + rc = SCOM_getScom( i_sTrgt, i_addr, &(reg.val) ); + if ( SUCCESS != rc ) + { + if ( io_pTrgt->scomErrs < PNOR_Trgt_MAX_SCOM_ERRORS ) + io_pTrgt->scomErrs++; + break; + } + + if ( 0 == reg.val ) break; // Skip zero value registers. + + full = FirData_addDataToPnor( io_fd, ®, sizeof(reg) ); + if ( full ) break; + + *o_nonZero = true; + + if ( io_pTrgt->regs < PNOR_Trgt_MAX_REGS_PER_TRGT ) + io_pTrgt->regs++; + + } while (0); + + return full; +} + +/*------------------------------------------------------------------------------ */ + +/** @brief SCOMs hardware and add a indirect-SCOM register to PNOR. + * @param io_fd The FIR data stuct. + * @param io_pTrgt Pointer to PNOR target. + * @param i_sTrgt SCOM target. + * @param i_sTrgt Target to SCOM. + * @param i_addr 64-bit address to SCOM. + * @param o_nonZero True if the value of the register was non-zero. False, + * otherwise. + * @return True if the PNOR buffer is full, false if there was room. + */ +bool FirData_addIdRegToPnor( FirData_t * io_fd, PNOR_Trgt_t * io_pTrgt, + SCOM_Trgt_t i_sTrgt, uint64_t i_addr, + bool * o_nonZero ) +{ + bool full = false; + + int32_t rc = 0; + PNOR_IdReg_t reg = { i_addr, 0 }; + + *o_nonZero = false; + + do + { + rc = SCOM_getIdScom( i_sTrgt, i_addr, &(reg.val) ); + if ( SUCCESS != rc ) + { + if ( io_pTrgt->scomErrs < PNOR_Trgt_MAX_SCOM_ERRORS ) + io_pTrgt->scomErrs++; + break; + } + + if ( 0 == reg.val ) break; // Skip zero value registers. + + full = FirData_addDataToPnor( io_fd, ®, sizeof(reg) ); + if ( full ) break; + + *o_nonZero = true; + + if ( io_pTrgt->idRegs < PNOR_Trgt_MAX_ID_REGS_PER_TRGT ) + io_pTrgt->idRegs++; + + } while (0); + + return full; +} + +/*------------------------------------------------------------------------------ */ + +/** @brief Iterates a list of global registers and adds them to the PNOR. + * @param io_fd The FIR data stuct. + * @param io_pTrgt Pointer to PNOR target. + * @param i_sTrgt SCOM target. + * @param o_noAttn True, if the global registers showed no active attentions + * on the target. False, otherwise. + * @return True if the PNOR buffer is full, false if there was room. + */ +bool FirData_addGlblsToPnor( FirData_t * io_fd, PNOR_Trgt_t * io_pTrgt, + SCOM_Trgt_t i_sTrgt, bool * o_noAttn ) +{ + bool full = false; + + uint8_t t = i_sTrgt.type; + uint8_t cnt = io_fd->hData->counts[t][GLBL]; + + uint32_t i = 0; + + uint32_t addr = 0; + bool nonZero = false; + + *o_noAttn = false; /* Must be false if there are no global regs. */ + + if ( 0 != cnt ) + { + *o_noAttn = true; /* Assume no attentions. */ + for ( i = 0; i < cnt; i++ ) + { + addr = io_fd->hPtrs[t].glbl[i]; + nonZero = false; + + full = FirData_addRegToPnor( io_fd, io_pTrgt, i_sTrgt, addr, + &nonZero ); + if ( full ) break; + + if ( nonZero ) *o_noAttn = false; + } + } + + return full; +} + +/*------------------------------------------------------------------------------ */ + +/** @brief Iterates a list of FIRs and adds them to the PNOR. + * @param io_fd The FIR data stuct. + * @param io_pTrgt Pointer to PNOR target. + * @param i_sTrgt SCOM target. + * @return True if the PNOR buffer is full, false if there was room. + */ +bool FirData_addFirsToPnor( FirData_t * io_fd, PNOR_Trgt_t * io_pTrgt, + SCOM_Trgt_t i_sTrgt ) +{ + bool full = false; + + uint8_t t = i_sTrgt.type; + uint8_t cnt = io_fd->hData->counts[t][FIR]; + + uint32_t i = 0; + + uint32_t addr = 0; + bool nonZero = false; + bool tmp = false; /* ignored, not used */ + + for ( i = 0; i < cnt; i++ ) + { + addr = io_fd->hPtrs[t].fir[i]; + nonZero = false; + + /* Add FIR */ + full = FirData_addRegToPnor( io_fd, io_pTrgt, i_sTrgt, addr, &nonZero ); + if ( full ) break; + + /* Add MASK */ + full = FirData_addRegToPnor( io_fd, io_pTrgt, i_sTrgt, addr + 3, &tmp ); + if ( full ) break; + + if ( nonZero ) + { + /* Add ACT0 */ + full = FirData_addRegToPnor( io_fd, io_pTrgt, i_sTrgt, addr + 6, + &tmp ); + if ( full ) break; + + /* Add ACT1 */ + full = FirData_addRegToPnor( io_fd, io_pTrgt, i_sTrgt, addr + 7, + &tmp ); + if ( full ) break; + } + + /* Add WOF */ + full = FirData_addRegToPnor( io_fd, io_pTrgt, i_sTrgt, addr + 8, &tmp ); + if ( full ) break; + } + + return full; +} + +/*------------------------------------------------------------------------------ */ + +/** @brief Iterates a list of REGs and adds them to the PNOR. + * @param io_fd The FIR data stuct. + * @param io_pTrgt Pointer to PNOR target. + * @param i_sTrgt SCOM target. + * @return True if the PNOR buffer is full, false if there was room. + */ +bool FirData_addRegsToPnor( FirData_t * io_fd, PNOR_Trgt_t * io_pTrgt, + SCOM_Trgt_t i_sTrgt ) +{ + bool full = false; + + uint8_t t = i_sTrgt.type; + uint8_t cnt = io_fd->hData->counts[t][REG]; + + uint32_t i = 0; + + uint32_t addr = 0; + bool tmp = false; /* ignored, not used */ + + for ( i = 0; i < cnt; i++ ) + { + addr = io_fd->hPtrs[t].reg[i]; + + full = FirData_addRegToPnor( io_fd, io_pTrgt, i_sTrgt, addr, &tmp ); + if ( full ) break; + } + + return full; +} + +/*------------------------------------------------------------------------------ */ + +/** @brief Iterates a list of IDFIRs and adds them to the PNOR. + * @param io_fd The FIR data stuct. + * @param io_pTrgt Pointer to PNOR target. + * @param i_sTrgt SCOM target. + * @return True if the PNOR buffer is full, false if there was room. + */ +bool FirData_addIdFirsToPnor( FirData_t * io_fd, PNOR_Trgt_t * io_pTrgt, + SCOM_Trgt_t i_sTrgt ) +{ + bool full = false; + + uint8_t t = i_sTrgt.type; + uint8_t cnt = io_fd->hData->counts[t][IDFIR]; + + uint32_t i = 0; + + uint64_t addr = 0; + bool nonZero = false; + bool tmp = false; /* ignored, not used */ + + for ( i = 0; i < cnt; i++ ) + { + addr = io_fd->hPtrs[t].idFir[i]; + nonZero = false; + + /* Add FIR */ + full = FirData_addIdRegToPnor( io_fd, io_pTrgt, i_sTrgt, addr, + &nonZero ); + if ( full ) break; + + /* Add MASK */ + full = FirData_addIdRegToPnor( io_fd, io_pTrgt, i_sTrgt, + addr + 0x300000000ll, &tmp ); + if ( full ) break; + + if ( nonZero ) + { + /* Add ACT0 */ + full = FirData_addIdRegToPnor( io_fd, io_pTrgt, i_sTrgt, + addr + 0x600000000ll, &tmp ); + if ( full ) break; + + /* Add ACT1 */ + full = FirData_addIdRegToPnor( io_fd, io_pTrgt, i_sTrgt, + addr + 0x700000000ll, &tmp ); + if ( full ) break; + } + + /* Add WOF */ + full = FirData_addIdRegToPnor( io_fd, io_pTrgt, i_sTrgt, + addr + 0x800000000ll, &tmp ); + if ( full ) break; + } + + return full; +} + +/*------------------------------------------------------------------------------ */ + +/** @brief Iterates a list of IDREGs and adds them to the PNOR. + * @param io_fd The FIR data stuct. + * @param io_pTrgt Pointer to PNOR target. + * @param i_sTrgt SCOM target. + * @return True if the PNOR buffer is full, false if there was room. + */ +bool FirData_addIdRegsToPnor( FirData_t * io_fd, PNOR_Trgt_t * io_pTrgt, + SCOM_Trgt_t i_sTrgt ) +{ + bool full = false; + + uint8_t t = i_sTrgt.type; + uint8_t cnt = io_fd->hData->counts[t][IDREG]; + + uint32_t i = 0; + + uint32_t addr = 0; + bool tmp = false; /* ignored, not used */ + + for ( i = 0; i < cnt; i++ ) + { + addr = io_fd->hPtrs[t].idReg[i]; + + full = FirData_addIdRegToPnor( io_fd, io_pTrgt, i_sTrgt, addr, &tmp ); + if ( full ) break; + } + + return full; +} + +/*------------------------------------------------------------------------------ */ + +/** @brief Adds a target to the PNOR. + * @param io_fd The FIR data stuct. + * @param i_sTrgt SCOM Target. + * @param o_noAttn True, if the global registers showed no active + * attentions on the target. False, otherwise. + * @return True if the PNOR buffer is full, false if there was room. + */ +bool FirData_addTrgtToPnor( FirData_t * io_fd, SCOM_Trgt_t i_sTrgt, + bool * o_noAttn ) +{ + bool full = false; + + PNOR_Trgt_t * pTrgt = (PNOR_Trgt_t *)(&io_fd->pBuf[io_fd->pBufSize]); + + PNOR_Trgt_t tmp_pTrgt = PNOR_getTrgt( i_sTrgt.type, i_sTrgt.procPos, + i_sTrgt.procUnitPos ); + + *o_noAttn = false; /* Must be false if there are no global regs. */ + + do + { + /* Add the target info to PNOR. */ + full = FirData_addDataToPnor( io_fd, &tmp_pTrgt, sizeof(tmp_pTrgt) ); + if ( full ) break; + + /* Update the number of targets in the PNOR data. */ + io_fd->pData->trgts++; + + /* NOTE: Must add all regular registers (GLBL, FIR, REG) before all */ + /* indirect-SCOM registers. Also, must check GLBL registers first */ + /* to determine whether it is necessary to do the other registers. */ + + /* Add the GLBLs. */ + full = FirData_addGlblsToPnor( io_fd, pTrgt, i_sTrgt, o_noAttn ); + if ( full || *o_noAttn ) break; + + /* Add the FIRs. */ + full = FirData_addFirsToPnor( io_fd, pTrgt, i_sTrgt ); + if ( full ) break; + + /* Add the REGs. */ + full = FirData_addRegsToPnor( io_fd, pTrgt, i_sTrgt ); + if ( full ) break; + + /* Add the IDFIRs. */ + full = FirData_addIdFirsToPnor( io_fd, pTrgt, i_sTrgt ); + if ( full ) break; + + /* Add the IDREGs. */ + full = FirData_addIdRegsToPnor( io_fd, pTrgt, i_sTrgt ); + if ( full ) break; + + } while (0); + + return full; +} + +/*------------------------------------------------------------------------------ */ + +/** @brief Iterates through configured targets and adds the data to PNOR. + * @param io_fd The FIR data stuct. + */ +void FirData_addTrgtsToPnor( FirData_t * io_fd ) +{ + bool full = false; + bool noAttn = false; + + uint8_t p = 0; + uint8_t u = 0; + uint8_t mu = 0; + uint8_t i = 0; + uint8_t j = 0; + + bool isM = false; + uint32_t fsi = 0; + + SCOM_Trgt_t sTrgt; + + do + { + /* Iterate all PROCs. */ + for ( p = 0; p < MAX_PROC_PER_NODE; p++ ) + { + /* Check if the PROC is configured. */ + if ( 0 == (io_fd->hData->procMask & (0x80 >> p)) ) continue; + TRACFCOMP("proc %d",p); + + /* Check if this PROC is the master PROC and get the FSI base addr. */ + isM = ( p == io_fd->hData->masterProc ); + fsi = io_fd->hData->procFsiBaseAddr[p]; + + /* Add this PROC to the PNOR. */ + sTrgt = SCOM_Trgt_getTrgt(PROC, p, 0, fsi, isM); + full = FirData_addTrgtToPnor( io_fd, sTrgt, &noAttn ); + if ( full ) break; + if ( noAttn ) continue; /* Skip the PROC, EXs, and MCSs */ + + for ( u = 0; u < MAX_EX_PER_PROC; u++ ) + { + /* Check if the EX is configured. */ + if ( 0 == (io_fd->hData->exMasks[p] & (0x8000 >> u)) ) continue; + TRACFCOMP("ex %d",u); + + /* Add this EX to the PNOR. */ + sTrgt = SCOM_Trgt_getTrgt(EX, p, u, fsi, isM); + full = FirData_addTrgtToPnor( io_fd, sTrgt, &noAttn ); + if ( full ) break; + if ( noAttn ) continue; /* Skip the EX */ + } + if ( full ) break; + + for ( u = 0; u < MAX_MCS_PER_PROC; u++ ) + { + /* Check if the MCS is configured. */ + if ( 0 == (io_fd->hData->mcsMasks[p] & (0x80 >> u)) ) continue; + TRACFCOMP("mcs %d",u); + + /* Add this MCS to the PNOR. */ + sTrgt = SCOM_Trgt_getTrgt(MCS, p, u, fsi, isM); + full = FirData_addTrgtToPnor( io_fd, sTrgt, &noAttn ); + if ( full ) break; + if ( noAttn ) continue; /* Skip the MCS */ + } + if ( full ) break; + } + if ( full ) break; + + /* Iterate all MEMBs. Must do this separate of from the PROCs because */ + /* it is possible a MEMB could be reporting an attention but the */ + /* connected PROC is not. */ + for ( i = 0; i < MAX_MEMB_PER_NODE; i++ ) + { + p = i / MAX_MEMB_PER_PROC; + u = i % MAX_MEMB_PER_PROC; + + /* Check if the MEMB is configured. */ + if ( 0 == (io_fd->hData->membMasks[p] & (0x80 >> u)) ) continue; + TRACFCOMP("memb %d",u); + + /* Get the FSI base address. */ + fsi = io_fd->hData->membFsiBaseAddr[p][u]; + + /* Add this MEMB to the PNOR. */ + sTrgt = SCOM_Trgt_getTrgt(MEMB, p, u, fsi, false); + full = FirData_addTrgtToPnor( io_fd, sTrgt, &noAttn ); + if ( full ) break; + if ( noAttn ) continue; /* Skip the MEMB and MBAs */ + + for ( j = 0; j < MAX_MBA_PER_MEMB; j++ ) + { + mu = u * MAX_MBA_PER_MEMB + j; + + /* Check if the MBA is configured. */ + if ( 0 == (io_fd->hData->mbaMasks[p] & (0x8000 >> mu)) ) + continue; + TRACFCOMP("mba %d",mu); + + /* Add this MBA to the PNOR. */ + sTrgt = SCOM_Trgt_getTrgt(MBA, p, mu, fsi, false); + full = FirData_addTrgtToPnor( io_fd, sTrgt, &noAttn ); + if ( full ) break; + if ( noAttn ) continue; /* Skip the MEMB */ + } + if ( full ) break; + } + if ( full ) break; + + } while (0); + +} + +/*------------------------------------------------------------------------------ */ + +/** @brief Initializes the FIR data struct. Does range checking for the HOMER + * data. Initializes the PNOR header data. + * @param io_fd The FIR data stuct. + * @param i_hBuf SRAM pointer to the beginning of the HOMER data buffer. + * This should contain the FIR data information provided by + * PRD that is used to define which registers the OCC will + * need to SCOM. + * @param i_hBufSize Total size of the HOMER data buffer. + * @param i_pBuf SRAM pointer to the beginning of the PNOR data buffer. + * This will be used by this function as a temporary area of + * memory to store the PNOR data before writing that data to + * the PNOR. + * @param i_pBufSize Total size of the PNOR data buffer. + * @return Non-SUCCESS if HOMER or PNOR range checking fails or if an + * internal function fails. SUCCESS otherwise. + */ +int32_t FirData_init( FirData_t * io_fd, + uint8_t * i_hBuf, uint32_t i_hBufSize, + uint8_t * i_pBuf, uint32_t i_pBufSize ) +{ + #define FUNC "[FirData_init] " + + int32_t rc = SUCCESS; + + size_t sz_hData = sizeof(HOMER_Data_t); + size_t sz_pnoNoEcc = 0; + size_t sz_u32 = sizeof(uint32_t); + size_t sz_u64 = sizeof(uint64_t); + + bool full = false; + + uint32_t x[MAX_TRGTS][MAX_REGS]; + size_t curIdx = 0; + + uint32_t t = FIRST_TRGT; + + uint8_t * reglist = NULL; + + PNOR_Data_t pData = PNOR_getData(); + + do + { + /* Init the struct. */ + io_fd->hBuf = i_hBuf; + io_fd->maxHBufSize = i_hBufSize; + io_fd->hData = (HOMER_Data_t *)i_hBuf; + + io_fd->pBuf = i_pBuf; + io_fd->maxPBufSize = i_pBufSize; + io_fd->pData = (PNOR_Data_t *)i_pBuf; + io_fd->pBufSize = 0; + + memset( io_fd->hPtrs, 0x00, sizeof(io_fd->hPtrs) ); + + /* Check HOMER header data size. */ + if ( io_fd->maxHBufSize < sz_hData ) + { + TRACFCOMP( FUNC"HOMER header data size %d is larger than HOMER " + "data buffer %d", sz_hData, io_fd->maxHBufSize ); + rc = FAIL; + break; + } + + /* Check for valid HOMER data. */ + if ( HOMER_FIR1 != io_fd->hData->header ) + { + break; /* nothing to analyze. */ + } + + /* The actual maximum PNOR size may possibly be less then the PNOR data */ + /* buffer. If so, adjust maximum size. */ + sz_pnoNoEcc = (io_fd->hData->pnorInfo.pnorSize / 9) * 8; + if ( sz_pnoNoEcc < io_fd->maxPBufSize ) + io_fd->maxPBufSize = sz_pnoNoEcc; + + /* Initialize the PNOR header data. */ + full = FirData_addDataToPnor( io_fd, &pData, sizeof(pData) ); + if ( full ) + { + TRACFCOMP( FUNC"Unable to add header to PNOR buffer" ); + rc = FAIL; + break; + } + + /* Get the register list byte indexes in HOMER data buffer */ + memset( x, 0x00, sizeof(x) ); + for ( t = FIRST_TRGT; t < MAX_TRGTS; t++ ) + { + x[t][GLBL] = curIdx; + x[t][FIR] = x[t][GLBL] + sz_u32 * io_fd->hData->counts[t][GLBL]; + x[t][REG] = x[t][FIR] + sz_u32 * io_fd->hData->counts[t][FIR]; + x[t][IDFIR] = x[t][REG] + sz_u32 * io_fd->hData->counts[t][REG]; + x[t][IDREG] = x[t][IDFIR] + sz_u64 * io_fd->hData->counts[t][IDFIR]; + curIdx = x[t][IDREG] + sz_u64 * io_fd->hData->counts[t][IDREG]; + } + + /* Check to make sure the list data is not larger than the available */ + /* Homer buffer. */ + if ( io_fd->maxHBufSize - sz_hData < curIdx ) + { + TRACFCOMP( FUNC"HOMER list size %d is larger than HOMER data " + "buffer %d", curIdx, io_fd->maxHBufSize - sz_hData ); + rc = FAIL; + break; + } + + /* Now, get the pointers for each list. */ + reglist = io_fd->hBuf + sz_hData; + for ( t = FIRST_TRGT; t < MAX_TRGTS; t++ ) + { + (io_fd->hPtrs[t]).glbl = (uint32_t *)(reglist + x[t][GLBL] ); + (io_fd->hPtrs[t]).fir = (uint32_t *)(reglist + x[t][FIR] ); + (io_fd->hPtrs[t]).reg = (uint32_t *)(reglist + x[t][REG] ); + (io_fd->hPtrs[t]).idFir = (uint64_t *)(reglist + x[t][IDFIR]); + (io_fd->hPtrs[t]).idReg = (uint64_t *)(reglist + x[t][IDREG]); + } + + } while (0); + + return rc; + + #undef FUNC +} + + +/*------------------------------------------------------------------------------ */ +/* External functions */ +/*------------------------------------------------------------------------------ */ + +int32_t FirData_captureCsFirData( uint8_t * i_hBuf, uint32_t i_hBufSize, + uint8_t * i_pBuf, uint32_t i_pBufSize ) +{ + #define FUNC "[FirData_captureCsFirData] " + TRACFCOMP( "FirData_captureCsFirData> hBuf=%p, hBufSize=%X, pBuf=%p, pBufSize=%.8X", i_hBuf, i_hBufSize, i_pBuf, i_pBufSize ); + int32_t rc = SUCCESS; + + do + { + /* Init the FIR data struct. */ + FirData_t fd; + rc = FirData_init( &fd, i_hBuf, i_hBufSize, i_pBuf, i_pBufSize ); + if ( SUCCESS != rc ) + { + TRACFCOMP( FUNC"Failed to init FIR data" ); + break; + } + + /* Check for valid HOMER data. */ + if ( HOMER_FIR1 != fd.hData->header ) + { + TRACFCOMP( FUNC"No HOMER data=%.8X",fd.hData->header ); + break; /* nothing to analyze. */ + } + + /* Start adding register data to PNOR for each target. */ + FirData_addTrgtsToPnor( &fd ); + + /* Write the buffer to PNOR. */ + rc = PNOR_writeFirData( fd.hData->pnorInfo, fd.pBuf, fd.pBufSize ); + if ( SUCCESS != rc ) + { + TRACFCOMP( FUNC"Failed to process FIR data" ); + break; + } + + } while (0); + + return rc; + + #undef FUNC +} + diff --git a/src/occ/firdata/firData.h b/src/occ/firdata/firData.h new file mode 100644 index 0000000..f1475db --- /dev/null +++ b/src/occ/firdata/firData.h @@ -0,0 +1,48 @@ +/* IBM_PROLOG_BEGIN_TAG */ +/* This is an automatically generated prolog. */ +/* */ +/* $Source: src/occ/firdata/firData.H $ */ +/* */ +/* OpenPOWER OnChipController Project */ +/* */ +/* Contributors Listed Below - COPYRIGHT 2015 */ +/* [+] International Business Machines Corp. */ +/* */ +/* */ +/* Licensed under the Apache License, Version 2.0 (the "License"); */ +/* you may not use this file except in compliance with the License. */ +/* You may obtain a copy of the License at */ +/* */ +/* http://www.apache.org/licenses/LICENSE-2.0 */ +/* */ +/* Unless required by applicable law or agreed to in writing, software */ +/* distributed under the License is distributed on an "AS IS" BASIS, */ +/* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or */ +/* implied. See the License for the specific language governing */ +/* permissions and limitations under the License. */ +/* */ +/* IBM_PROLOG_END_TAG */ + +#ifndef __firData_h +#define __firData_h + +#include "native.h" + +/** @brief Reads the register list from the HOMER data, SCOMs hardware, and + * stores the register values in PNOR. + * @param i_hBuf SRAM pointer to the beginning of the HOMER data buffer. + * This should contain the FIR data information provided by + * PRD that is used to define which registers the OCC will + * need to SCOM. + * @param i_hBufSize Total size of the HOMER data buffer. + * @param i_pBuf SRAM pointer to the beginning of the PNOR data buffer. + * This will be used by this function as a temporary area + * of memory to store the PNOR data before writing that + * data to the PNOR. + * @param i_pBufSize Total size of the PNOR data buffer. + * @return Non-SUCCESS if an internal function fails. SUCCESS otherwise. + */ +int32_t FirData_captureCsFirData( uint8_t * i_hBuf, uint32_t i_hBufSize, + uint8_t * i_pBuf, uint32_t i_pBufSize ); + +#endif /* __firData_h */ diff --git a/src/occ/firdata/firDataConst_common.h b/src/occ/firdata/firDataConst_common.h new file mode 100644 index 0000000..86a3ccc --- /dev/null +++ b/src/occ/firdata/firDataConst_common.h @@ -0,0 +1,75 @@ +/* IBM_PROLOG_BEGIN_TAG */ +/* This is an automatically generated prolog. */ +/* */ +/* $Source: src/occ/firdata/occ_const.H $ */ +/* */ +/* OpenPOWER OnChipController Project */ +/* */ +/* Contributors Listed Below - COPYRIGHT 2015 */ +/* [+] International Business Machines Corp. */ +/* */ +/* */ +/* Licensed under the Apache License, Version 2.0 (the "License"); */ +/* you may not use this file except in compliance with the License. */ +/* You may obtain a copy of the License at */ +/* */ +/* http://www.apache.org/licenses/LICENSE-2.0 */ +/* */ +/* Unless required by applicable law or agreed to in writing, software */ +/* distributed under the License is distributed on an "AS IS" BASIS, */ +/* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or */ +/* implied. See the License for the specific language governing */ +/* permissions and limitations under the License. */ +/* */ +/* IBM_PROLOG_END_TAG */ + +#ifndef __firDataConst_common_h +#define __firDataConst_common_h + +/** NOTE: This file is common between OCC and Hosboot. Any change to this file + * must be mirrored to both repositories. */ + +#include <stdint.h> + +/** Target types for all supported targets. */ +typedef enum +{ + /* NOTE: These will be used as array indexes. */ + FIRST_TRGT = 0, + PROC = FIRST_TRGT, + EX, + MCS, + MEMB, + MBA, + MAX_TRGTS, + +} TrgtType_t; + +/** Boundary/position ranges for each target type. */ +typedef enum +{ + MAX_PROC_PER_NODE = 8, + MAX_EX_PER_PROC = 16, + MAX_MCS_PER_PROC = 8, + MAX_MEMB_PER_PROC = MAX_MCS_PER_PROC, + MAX_MEMB_PER_NODE = MAX_MEMB_PER_PROC * MAX_PROC_PER_NODE, + MAX_MBA_PER_MEMB = 2, + MAX_MBA_PER_PROC = MAX_MEMB_PER_PROC * MAX_MBA_PER_MEMB, + +} TrgtPos_t; + +/** All register types. */ +typedef enum +{ + /* NOTE: These will be used as array indexes. */ + FIRST_REG = 0, + GLBL = FIRST_REG, + FIR, + REG, + IDFIR, + IDREG, + MAX_REGS, + +} RegType_t; + +#endif /* __firDataConst_common_h */ diff --git a/src/occ/firdata/fir_data_collect.c b/src/occ/firdata/fir_data_collect.c new file mode 100644 index 0000000..1930fa5 --- /dev/null +++ b/src/occ/firdata/fir_data_collect.c @@ -0,0 +1,163 @@ +/* IBM_PROLOG_BEGIN_TAG */ +/* This is an automatically generated prolog. */ +/* */ +/* $Source: src/occ/firdata/fir_data_collect.c $ */ +/* */ +/* OpenPOWER OnChipController Project */ +/* */ +/* Contributors Listed Below - COPYRIGHT 2015 */ +/* [+] International Business Machines Corp. */ +/* */ +/* */ +/* Licensed under the Apache License, Version 2.0 (the "License"); */ +/* you may not use this file except in compliance with the License. */ +/* You may obtain a copy of the License at */ +/* */ +/* http://www.apache.org/licenses/LICENSE-2.0 */ +/* */ +/* Unless required by applicable law or agreed to in writing, software */ +/* distributed under the License is distributed on an "AS IS" BASIS, */ +/* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or */ +/* implied. See the License for the specific language governing */ +/* permissions and limitations under the License. */ +/* */ +/* IBM_PROLOG_END_TAG */ + + +#include <fir_data_collect.h> +#include <scom.h> +#include <occ_service_codes.h> +#include <errl.h> +#include "tpc_firmware_registers.h" +#include "tpc_register_addresses.h" +#include <trac.h> +#include <homer.h> + +FIR_HEAP_BUFFER(uint8_t G_fir_heap[FIR_HEAP_SECTION_SIZE]); +FIR_PARMS_BUFFER(uint8_t G_fir_data_parms[FIR_PARMS_SECTION_SIZE]); +uint32_t G_fir_master = FIR_OCC_NOT_FIR_MASTER; + +/* + * Function Specification + * + * Name: fir_data_collect + * + * Description: Collects FIR data on checkstop. + * + * End Function Specification + */ +void fir_data_collect(void) +{ + int32_t l_rc = 0; + + // Homer data section and size + uint8_t *l_hBuf = FIR_PARMS_SECTION_BASE_ADDRESS; + uint32_t l_hBufSize = HOMER_FIR_PARM_SIZE; + // PNOR working buffer in SRAM and size + uint8_t *l_pBuf = FIR_HEAP_SECTION_BASE_ADDRESS; + uint32_t l_pBufSize = FIR_HEAP_SECTION_SIZE; + + l_rc = FirData_captureCsFirData(l_hBuf, + l_hBufSize, + l_pBuf, + l_pBufSize); + + // Trace the rc only, error logs cannot be collected in this state + TRAC_IMP("Checkstop FIR data capture completed with rc=%d", l_rc); +} + + +/* + * Function Specification + * + * Name: pnor_access_ok + * + * Description: Determines if it is ok for this OCC to access the PNOR. + * + * End Function Specification + */ +bool pnor_access_allowed(void) +{ + /* BMC ownership of the PNOR is indicated by bit 18 in TPC_GP0 */ + int l_rc = 0; + tpc_gp0_t l_tp_gp0_read; + bool l_access_allowed = FALSE; + + l_tp_gp0_read.words.high_order = 0x00000000; + l_tp_gp0_read.words.low_order = 0x00000000; + + l_rc = getscom_ffdc(TPC_GP0, (uint64_t *)&l_tp_gp0_read, NULL); + + if (l_rc == 0) + { + if ((l_tp_gp0_read.words.high_order & TPC_GP0_BIT18_PNOR_OWNER_MASK) == 0) + { + TRAC_INFO("PNOR access allowed at this time"); + l_access_allowed = TRUE; + } + else + { + TRAC_INFO("PNOR access NOT allowed at this time, tpc_gp0.hi = 0x%08x", + l_tp_gp0_read.words.high_order); + + /* @ + * @errortype + * @moduleid FIR_DATA_MID + * @reasoncode INTERNAL_FAILURE + * @userdata1 TPC_GP0 high word + * @userdata4 ERC_PNOR_OWNERSHIP_NOT_AVAILABLE + * @devdesc PNOR access not allowed at this time. + */ + errlHndl_t l_errl = createErrl( + FIR_DATA_MID, /*ModId */ + INTERNAL_FAILURE, /*Reasoncode */ + ERC_PNOR_OWNERSHIP_NOT_AVAILABLE, /*Extended reasoncode */ + ERRL_SEV_INFORMATIONAL, /*Severity */ + NULL, /*Trace Buf */ + DEFAULT_TRACE_SIZE, /*Trace Size */ + l_tp_gp0_read.words.high_order, /*Userdata1 */ + 0 /*Userdata2 */ + ); + + /* Commit log */ + commitErrl(&l_errl); + } + } + else + { + /* getscom failure */ + TRAC_ERR("TPC_GP0 getscom failure rc = 0x%08x", -l_rc ); + + /* @ + * @errortype + * @moduleid FIR_DATA_MID + * @reasoncode INTERNAL_HW_FAILURE + * @userdata1 getscom failure rc + * @userdata4 ERC_GETSCOM_TPC_GP0_FAILURE + * @devdesc Failure determining PNOR ownership. Cannot read TPC_GP0. + */ + errlHndl_t l_errl = createErrl( + FIR_DATA_MID, /*ModId */ + INTERNAL_HW_FAILURE, /*Reasoncode */ + ERC_GETSCOM_TPC_GP0_FAILURE, /*Extended reasoncode */ + ERRL_SEV_PREDICTIVE, /*Severity */ + NULL, /*Trace Buf */ + DEFAULT_TRACE_SIZE, /*Trace Size */ + l_rc, /*Userdata1 */ + 0 /*Userdata2 */ + ); + + /* Callout firmware */ + addCalloutToErrl(l_errl, + ERRL_CALLOUT_TYPE_COMPONENT_ID, + ERRL_COMPONENT_ID_FIRMWARE, + ERRL_CALLOUT_PRIORITY_HIGH); + + /* Commit log */ + commitErrl(&l_errl); + } + + return l_access_allowed; +} + + diff --git a/src/occ/firdata/fir_data_collect.h b/src/occ/firdata/fir_data_collect.h new file mode 100644 index 0000000..c313956 --- /dev/null +++ b/src/occ/firdata/fir_data_collect.h @@ -0,0 +1,56 @@ +/* IBM_PROLOG_BEGIN_TAG */ +/* This is an automatically generated prolog. */ +/* */ +/* $Source: src/occ/firdata/fir_data_collect.h $ */ +/* */ +/* OpenPOWER OnChipController Project */ +/* */ +/* Contributors Listed Below - COPYRIGHT 2015 */ +/* [+] International Business Machines Corp. */ +/* */ +/* */ +/* Licensed under the Apache License, Version 2.0 (the "License"); */ +/* you may not use this file except in compliance with the License. */ +/* You may obtain a copy of the License at */ +/* */ +/* http://www.apache.org/licenses/LICENSE-2.0 */ +/* */ +/* Unless required by applicable law or agreed to in writing, software */ +/* distributed under the License is distributed on an "AS IS" BASIS, */ +/* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or */ +/* implied. See the License for the specific language governing */ +/* permissions and limitations under the License. */ +/* */ +/* IBM_PROLOG_END_TAG */ + + +#ifndef _FIR_DATA_COLLECT_H +#define _FIR_DATA_COLLECT_H + +#include <common_types.h> + +/* This size has to agree with the size _FIR_PARMS_SECTION_SIZE defined in the */ +/* OCC linker command file. */ +#define FIR_PARMS_SECTION_SIZE 0x1000 +// This size has to agree with the size _FIR_HEAP_SECTION_SIZE defined in the +// OCC linker command file. +#define FIR_HEAP_SECTION_SIZE 0x3000 + +enum fir_master +{ + FIR_OCC_NOT_FIR_MASTER = 0x00000000, + FIR_OCC_IS_FIR_MASTER = 0x00000001 +}; + +extern uint8_t G_fir_data_parms[FIR_PARMS_SECTION_SIZE]; +extern uint8_t G_fir_heap[FIR_HEAP_SECTION_SIZE]; +extern uint32_t G_fir_master; + +#define OCC_SET_FIR_MASTER(_fm_t) G_fir_master = _fm_t +#define OCC_IS_FIR_MASTER() (G_fir_master == FIR_OCC_IS_FIR_MASTER) ? TRUE : FALSE +#define TPC_GP0_BIT18_PNOR_OWNER_MASK 0x00002000 + +void fir_data_collect(void); +bool pnor_access_allowed(void); + +#endif /* _FIR_DATA_COLLECT_H */ diff --git a/src/occ/firdata/fsi.c b/src/occ/firdata/fsi.c new file mode 100644 index 0000000..111bf2e --- /dev/null +++ b/src/occ/firdata/fsi.c @@ -0,0 +1,115 @@ +/* IBM_PROLOG_BEGIN_TAG */ +/* This is an automatically generated prolog. */ +/* */ +/* $Source: src/occ/firdata/fsi.C $ */ +/* */ +/* OpenPOWER OnChipController Project */ +/* */ +/* Contributors Listed Below - COPYRIGHT 2015 */ +/* [+] International Business Machines Corp. */ +/* */ +/* */ +/* Licensed under the Apache License, Version 2.0 (the "License"); */ +/* you may not use this file except in compliance with the License. */ +/* You may obtain a copy of the License at */ +/* */ +/* http://www.apache.org/licenses/LICENSE-2.0 */ +/* */ +/* Unless required by applicable law or agreed to in writing, software */ +/* distributed under the License is distributed on an "AS IS" BASIS, */ +/* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or */ +/* implied. See the License for the specific language governing */ +/* permissions and limitations under the License. */ +/* */ +/* IBM_PROLOG_END_TAG */ + +#include <fsi.h> +#include <scom_util.h> +#include <native.h> + +#define OPB_REG_CMD 0x00020000 +#define OPB_REG_STAT 0x00020001 +#define OPB_STAT_BUSY 0x0001000000000000 /**< 15 is the Busy bit */ +#define NS_PER_MSEC (1000000ull) + + +/** + * @brief Poll for completion of a FSI operation, return data on read + */ +uint32_t poll_for_complete( void ) +{ + enum { MAX_OPB_TIMEOUT_NS = 10*NS_PER_MSEC }; /*=10ms */ + + /* poll for complete */ + uint64_t read_data = FSIFAIL; + uint64_t elapsed_time_ns = 0; + do + { + read_data = xscom_read(OPB_REG_STAT); + /* check for completion or xscom error */ + /* note: not checking for FSI errors */ + if( ((read_data & OPB_STAT_BUSY) == 0) /*not busy */ + || (read_data == SCOMFAIL) ) /*scom error */ + { + break; + } + else + { + read_data = FSIFAIL; + } + + sleep( 10000 ); /*sleep for 10,000 ns */ + elapsed_time_ns += 10000; + } while( elapsed_time_ns <= MAX_OPB_TIMEOUT_NS ); + + return (uint32_t)read_data; /*data in the bottom half */ +} + +/** + * @brief Read a FSI register + */ +uint32_t getfsi( SCOM_Trgt_t i_target, uint32_t i_address ) +{ + uint32_t fsi_base = i_target.fsiBaseAddr; + uint32_t fsi_addr = fsi_base | i_address; + + /* setup the OPB command register */ + /* only supporting 4-byte access */ + uint64_t fsi_cmd = fsi_addr | 0x60000000; /*011=Read Full Word */ + fsi_cmd <<= 32; /* Command is in the upper word of the scom */ + + /* write the OPB command register to trigger the read */ + xscom_write( OPB_REG_CMD, fsi_cmd ); + + /* poll for complete and get the data back */ + uint32_t out_data = poll_for_complete(); + + return out_data; +} + + +/** + * @brief Write a FSI register + */ +void putfsi( SCOM_Trgt_t i_target, + uint32_t i_address, + uint32_t i_data ) +{ + uint32_t fsi_base = i_target.fsiBaseAddr; + uint32_t fsi_addr = fsi_base | i_address; + + /* setup the OPB command register */ + /* only supporting 4-byte access */ + uint64_t fsi_cmd = fsi_addr | 0xE0000000; /* 111=Write Full Word */ + fsi_cmd <<= 32; /* Command is in the upper word of the scom */ + fsi_cmd |= i_data; /* Data is in the bottom 32-bits */ + + /* write the OPB command register to trigger the read */ + xscom_write( OPB_REG_CMD, fsi_cmd ); + + /* poll for complete */ + poll_for_complete(); + + return; +} + diff --git a/src/occ/firdata/fsi.h b/src/occ/firdata/fsi.h new file mode 100644 index 0000000..f3e6fab --- /dev/null +++ b/src/occ/firdata/fsi.h @@ -0,0 +1,47 @@ +/* IBM_PROLOG_BEGIN_TAG */ +/* This is an automatically generated prolog. */ +/* */ +/* $Source: src/occ/firdata/fsi.H $ */ +/* */ +/* OpenPOWER OnChipController Project */ +/* */ +/* Contributors Listed Below - COPYRIGHT 2015 */ +/* [+] International Business Machines Corp. */ +/* */ +/* */ +/* Licensed under the Apache License, Version 2.0 (the "License"); */ +/* you may not use this file except in compliance with the License. */ +/* You may obtain a copy of the License at */ +/* */ +/* http://www.apache.org/licenses/LICENSE-2.0 */ +/* */ +/* Unless required by applicable law or agreed to in writing, software */ +/* distributed under the License is distributed on an "AS IS" BASIS, */ +/* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or */ +/* implied. See the License for the specific language governing */ +/* permissions and limitations under the License. */ +/* */ +/* IBM_PROLOG_END_TAG */ + +/* Interfaces to read/write FSI registers */ + +#include <scom_trgt.h> + +#define FSIFAIL 0xDEADBEEF + +/** + * @brief Read a FSI register + * @param[in] Chip/unit to read from + * @param[in] FSI address to read, relative to slave's base address + * @return FSI data on success, FSIFAIL on error + */ +uint32_t getfsi( SCOM_Trgt_t i_target, uint32_t i_address ); + +/** + * @brief Write a FSI register + * @param[in] Chip/unit to write to + * @param[in] FSI address to write, relative to slave's base address + * @param[in] Data to write + */ +void putfsi( SCOM_Trgt_t i_target, uint32_t i_address, uint32_t i_data ); + diff --git a/src/occ/firdata/homerData_common.h b/src/occ/firdata/homerData_common.h new file mode 100644 index 0000000..c010293 --- /dev/null +++ b/src/occ/firdata/homerData_common.h @@ -0,0 +1,170 @@ +/* IBM_PROLOG_BEGIN_TAG */ +/* This is an automatically generated prolog. */ +/* */ +/* $Source: src/occ/firdata/homerData.H $ */ +/* */ +/* OpenPOWER OnChipController Project */ +/* */ +/* Contributors Listed Below - COPYRIGHT 2015 */ +/* [+] International Business Machines Corp. */ +/* */ +/* */ +/* Licensed under the Apache License, Version 2.0 (the "License"); */ +/* you may not use this file except in compliance with the License. */ +/* You may obtain a copy of the License at */ +/* */ +/* http://www.apache.org/licenses/LICENSE-2.0 */ +/* */ +/* Unless required by applicable law or agreed to in writing, software */ +/* distributed under the License is distributed on an "AS IS" BASIS, */ +/* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or */ +/* implied. See the License for the specific language governing */ +/* permissions and limitations under the License. */ +/* */ +/* IBM_PROLOG_END_TAG */ + +#ifndef __homerData_common_h +#define __homerData_common_h + +/** NOTE: This file is common between OCC and Hosboot. Any change to this file + * must be mirrored to both repositories. */ + +#include <firDataConst_common.h> +#include <string.h> + +/** This file is used to define the format of the register list stored in the + * HOMER data that the OCC will use to determine what register data to capture + * in the event of a system checkstop. The data will be stored in the following + * format: + * + * - HOMER_Data_t struct - This has all of the information characterizing what + * hardware is configured and how many addresses are in each register + * list. See the struct definition below. + * + * - Rgister address lists - These lists vary in size depending on the number + * of register addresses needed in each list. The list counts are + * stored in HOMER_Data_t::counts. All lists for each target type will + * be stored in the following order: + * - PROC lists + * - EX lists + * - MCS lists + * - MEMB lists + * - MBA lists + * Each target type will have a set of lists that will be stored in the + * following order: + * - Global FIRs 32-bit addresses + * - FIRs 32-bit addresses + * - Registers 32-bit addresses + * - Indirect-SCOM FIRs 64-bit addresses + * - Indirect-SCOM registers 64-bit addresses + * + * Note that FIRs and indirect-SCOM FIRs characterize a set of registers to + * capture. In addition to capturing the FIR (or ID FIR), the OCC will need to + * capture the following addresses for each type: + * - FIR + * - MASK (FIR address + 3) + * - ACT0 (FIR address + 6) + * - ACT1 (FIR address + 7) + * - WOF (FIR address + 8) + * - ID FIR + * - ID MASK (ID FIR address + 0x300000000ll) + * - ID ACT0 (ID FIR address + 0x600000000ll) + * - ID ACT1 (ID FIR address + 0x700000000ll) + * - ID WOF (ID FIR address + 0x800000000ll) + */ + +typedef enum Version +{ + HOMER_FIR1 = 0x46495231, /*/< FIR data version 1 ("FIR1" in ascii) */ + +} HOMER_Version_t; + +/** PNOR information contained within the HOMER data. */ +typedef struct __attribute__((packed)) +{ + uint32_t pnorOffset; /*/< Physical offset of FIRDATA in PNOR */ + uint32_t pnorSize; /*/< Maximum size of FIRDATA (includes ECC) */ + uint32_t mmioOffset; /*/< Address of MMIO access */ + uint32_t norWorkarounds; /*/< NOR flash vendor */ + +} HOMER_PnorInfo_t; + +/** HOMER data header information containing hardware configurations and + * register counts. */ +typedef struct __attribute__((packed)) +{ + uint32_t header; /*/< Magic number to indicate valid data and version */ + + uint16_t reserved; + + uint8_t masterProc; /*/< The position of the master PROC */ + + /** Bitwise mask to indicate which PROCs are configured (max 8). The mask + * bit position is consistant with PROC ATTR_POSITION attribute. */ + uint8_t procMask; + + /** Bitwise masks to indicate which EXs are configured (16 per PROC). The + * array index is the associated PROC position. The mask bit position is + * consistant with the EX's ATTR_CHIP_UNIT attribute. */ + uint16_t exMasks[MAX_PROC_PER_NODE]; + + /** Bitwise masks to indicate which MCSs are configured (8 per PROC). The + * array index is the associated PROC position. The mask bit position is + * consistant with the MCS's ATTR_CHIP_UNIT attribute. */ + uint8_t mcsMasks[MAX_PROC_PER_NODE]; + + /** Bitwise masks to indicate which MEMBs are configured (8 per PROC). The + * array index is the associated PROC position. The mask bit position is + * consistant with the ATTR_CHIP_UNIT attribute of the connected MCS. */ + uint8_t membMasks[MAX_PROC_PER_NODE]; + + /** Bitwise masks to indicate which MBAs are configured (16 per PROC). The + * array index is the associated PROC position. The mask bit position is + * calculated as: + * (MEMB position * MAX_MBA_PER_MEMB) + MBA's ATTR_CHIP_UNIT attribute + */ + uint16_t mbaMasks[MAX_PROC_PER_NODE]; + + /** Contains number of registers per type for each target type. */ + uint8_t counts[MAX_TRGTS][MAX_REGS]; + + /** FSI base address for each PROC chip. */ + uint32_t procFsiBaseAddr[MAX_PROC_PER_NODE]; + + /** FSI base address for each MEMB chip. */ + uint32_t membFsiBaseAddr[MAX_PROC_PER_NODE][MAX_MEMB_PER_PROC]; + + /** Information regarding the PNOR location and size. */ + HOMER_PnorInfo_t pnorInfo; + +} HOMER_Data_t; + +/** @return An initialized HOMER_Data_t struct. */ +static inline HOMER_Data_t HOMER_getData() +{ + HOMER_PnorInfo_t p; + HOMER_Data_t d; + + p.pnorOffset = 0; + p.pnorSize = 0; + p.mmioOffset = 0; + p.norWorkarounds = 0; + + d.header = HOMER_FIR1; + d.reserved = 0; + d.masterProc = 0; + d.procMask = 0; + d.pnorInfo = p; + + memset( d.exMasks, 0x00, sizeof(d.exMasks) ); + memset( d.mcsMasks, 0x00, sizeof(d.mcsMasks) ); + memset( d.membMasks, 0x00, sizeof(d.membMasks) ); + memset( d.mbaMasks, 0x00, sizeof(d.mbaMasks) ); + memset( d.counts, 0x00, sizeof(d.counts) ); + memset( d.procFsiBaseAddr, 0xff, sizeof(d.procFsiBaseAddr) ); + memset( d.membFsiBaseAddr, 0xff, sizeof(d.membFsiBaseAddr) ); + + return d; +} + +#endif /* __homerData_common_h */ diff --git a/src/occ/firdata/lpc.c b/src/occ/firdata/lpc.c new file mode 100644 index 0000000..8bcdb5f --- /dev/null +++ b/src/occ/firdata/lpc.c @@ -0,0 +1,302 @@ +/* IBM_PROLOG_BEGIN_TAG */ +/* This is an automatically generated prolog. */ +/* */ +/* $Source: src/occ/firdata/lpc.C $ */ +/* */ +/* OpenPOWER OnChipController Project */ +/* */ +/* Contributors Listed Below - COPYRIGHT 2015 */ +/* [+] International Business Machines Corp. */ +/* */ +/* */ +/* Licensed under the Apache License, Version 2.0 (the "License"); */ +/* you may not use this file except in compliance with the License. */ +/* You may obtain a copy of the License at */ +/* */ +/* http://www.apache.org/licenses/LICENSE-2.0 */ +/* */ +/* Unless required by applicable law or agreed to in writing, software */ +/* distributed under the License is distributed on an "AS IS" BASIS, */ +/* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or */ +/* implied. See the License for the specific language governing */ +/* permissions and limitations under the License. */ +/* */ +/* IBM_PROLOG_END_TAG */ + +#include <native.h> +#include <lpc.h> +#include <trac_interface.h> + +#define LPCHC_FW_SPACE 0xF0000000 /**< LPC Host Controller FW Space */ +#define LPCHC_MEM_SPACE 0xE0000000 /**< LPC Host Controller Mem Space */ +#define LPCHC_IO_SPACE 0xD0010000 /**< LPC Host Controller I/O Space */ +#define LPCHC_REG_SPACE 0xC0012000 /**< LPC Host Ctlr Register Space */ + +#define ECCB_NON_FW_RESET_REG 0x000B0001 /**< ECCB Reset Reg (non-FW) */ + +#define ECCB_CTL_REG 0x000B0020 /**< ECCB Control Reg (FW) */ +#define ECCB_RESET_REG 0x000B0021 /**< ECCB Reset Reg (FW) */ +#define ECCB_STAT_REG 0x000B0022 /**< ECCB Status Reg (FW) */ +#define ECCB_DATA_REG 0x000B0023 /**< ECCB Data Reg (FW) */ + +/* Default Values to set for all operations + 1101.0100.0000.000x.0000.0001.0000.0000.<address> */ +#define ECCB_CTL_REG_DEFAULT 0xD400010000000000 + +/* Error bits: 41-43 56 (52cmd complete) (not 57: only non-fw use) */ +#define ECCB_STAT_REG_ERROR_MASK 0x0000000000700080 /**< Error Bits */ + +/**< OPB LPCM Sync FIR Reg - used to read the FIR*/ +#define OPB_LPCM_FIR_REG 0x01010C00 + +/**< OPB LPCM Sync FIR Reg WOX_AND - used to clear the FIR */ +#define OPB_LPCM_FIR_WOX_AND_REG 0x01010C01 + +/**< OPB LPCM Sync FIR Mask Reg WO_OR - used to set the mask */ +#define OPB_LPCM_FIR_MASK_WO_OR_REG 0x01010C05 + +#define OPB_LPCM_FIR_ERROR_MASK 0xFF00000000000000 /**< Error Bits MASK */ + +/* LPCHC reset-related registers */ +#define OPB_MASTER_LS_CONTROL_REG 0x008 /**<OPBM LS Control Reg */ +#define LPCHC_RESET_REG 0x0FC /**<LPC HC Reset Register */ + +#define ECCB_RESET_LPC_FAST_RESET (1ULL << 62) /**< bit 1 Fast reset */ + +#define ECCB_POLL_TIME_NS 400000 /**< max time should be 400ms */ +//dc99 #define ECCB_POLL_INCR_NS 10 /**< minimum increment during poll */ +#define ECCB_POLL_INCR_NS 100000 /**< increase for testing */ + +#define LPCHC_SYNC_CYCLE_COUNTER_INFINITE 0xFF000000 + +int TRACE_LPC = 0; +#define TRACZCOMP(args...) if(TRACE_LPC){TRACFCOMP(args);} + +/* Set to enable LPC tracing. */ +/* #define LPC_TRACING 1 */ +#ifdef LPC_TRACING +#define LPC_TRACFCOMP(des,printf_string,args...) \ + TRACFCOMP(des,printf_string,##args) /* FIX FIRDATA */ +#else +#define LPC_TRACFCOMP(args...) +#endif + +/** + * @brief ECCB Control Register Layout + */ +typedef union +{ + uint64_t data64; + struct + { + /* unused sections should be set to zero */ + uint64_t magic1 : 4; /**< 0:3 = b1101 per spec */ + uint64_t data_len : 4; /**< 4:7 = b0100 means 4 byte */ + uint64_t unused1 : 7; /**< 8:14 */ + uint64_t read_op : 1; /**< 15 = set for read operation */ + uint64_t unused2 : 7; /**< 16:22 */ + uint64_t addr_len : 3; /**< 23:25 = b100 means 4 byte */ + uint64_t unused3 : 6; /**< 26:31 */ + uint64_t address : 32; /**< 32:63 = LPC Address */ + }; +} ControlReg_t; + +/** + * @brief ECCB Status Register Layout + */ +typedef union +{ + uint64_t data64; + struct + { + uint64_t unused : 6; /**< 0:5 */ + uint64_t read_data : 32; /**< 6:37 */ + uint64_t unused1 : 3; /**< 38:40 */ + uint64_t eccb_err : 3; /**< 41:43 = ECCB_Error_Info */ + uint64_t busy : 1; /**< 44 = Operation Busy */ + uint64_t unused2 : 7; /**< 45:51 */ + uint64_t op_done : 1; /**< 52 = Command Complete */ + uint64_t unused3 : 3; /**< 53:55 */ + uint64_t addr_parity_err : 1; /**< 56 = ECC Address Register + Parity Error */ + uint64_t unused4 : 7; /**< 57:63 */ + }; +} StatusReg_t; + + +uint32_t checkAddr(LpcTransType i_type, + uint32_t i_addr) +{ + uint32_t full_addr = 0; + switch ( i_type ) + { + case LPC_TRANS_IO: + full_addr = i_addr | LPCHC_IO_SPACE; + break; + case LPC_TRANS_FW: + full_addr = i_addr | LPCHC_FW_SPACE; + break; + } + return full_addr; +} + + +errorHndl_t pollComplete(const ControlReg_t* i_ctrl, + StatusReg_t* o_stat) +{ + errorHndl_t l_err = NO_ERROR; + + do { + uint64_t poll_time = 0; + uint64_t loop = 0; + do + { + o_stat->data64 = xscom_read( ECCB_STAT_REG ); + LPC_TRACFCOMP( "writeLPC> Poll on ECCB Status, " + "poll_time=0x%.16x, stat=0x%.16x", + poll_time, + o_stat->data64 ); + if( l_err ) + { + break; + } + + if( o_stat->op_done ) + { + break; + } + + /* want to start out incrementing by small numbers then get bigger + to avoid a really tight loop in an error case so we'll increase + the wait each time through */ + sleep( ECCB_POLL_INCR_NS*(++loop) ); + poll_time += ECCB_POLL_INCR_NS * loop; + } while ( poll_time < ECCB_POLL_TIME_NS ); + + /* Check for hw errors or timeout if no previous logs */ + if( (l_err == NO_ERROR) && + ((o_stat->data64 & ECCB_STAT_REG_ERROR_MASK) + || (!o_stat->op_done)) ) + { + TRACFCOMP( "LpcDD::pollComplete> LPC error or timeout: addr=0x%.8X, status=0x%.8X%.8X", + i_ctrl->address, (uint32_t)(o_stat->data64>>32), (uint32_t)o_stat->data64 ); + l_err = -1; + break; + } + } while(0); + + return l_err; +} + + +/*========================================================*/ + +errorHndl_t lpc_read( LpcTransType i_type, + uint32_t i_addr, + uint8_t* o_data, + size_t i_size ) +{ + errorHndl_t l_err = NO_ERROR; + uint32_t l_addr = 0; + + do { + if( o_data == NULL ) + { + TRACFCOMP( "o_data is NULL!" ); + l_err = -2; + break; + } + + /* Generate the full absolute LPC address */ + l_addr = checkAddr( i_type, i_addr ); + + /* Execute command. */ + ControlReg_t eccb_cmd; + eccb_cmd.data64 = ECCB_CTL_REG_DEFAULT; + eccb_cmd.data_len = i_size; + eccb_cmd.read_op = 1; + eccb_cmd.addr_len = sizeof(l_addr); + eccb_cmd.address = l_addr; + xscom_write( ECCB_CTL_REG, eccb_cmd.data64 ); + + /* Poll for completion */ + StatusReg_t eccb_stat; + l_err = pollComplete( &eccb_cmd, &eccb_stat ); + if( l_err ) { break; } + + /* Copy data out to caller's buffer. */ + if( i_size <= sizeof(uint32_t) ) + { + uint32_t tmpbuf = eccb_stat.read_data; + memcpy( o_data, &tmpbuf, i_size ); + } + else + { + TRACFCOMP( "readLPC> Unsupported buffer size : %d", i_size ); + l_err = -1; + break; + } + + } while(0); + + LPC_TRACFCOMP( "readLPC> %08X[%d] = %08X", l_addr, i_size, *reinterpret_cast<uint32_t*>( o_data ) >> (8 * (4 - i_size)) ); + + return l_err; +} + +errorHndl_t lpc_write( LpcTransType i_type, + uint32_t i_addr, + uint8_t* i_data, + size_t i_size ) +{ + errorHndl_t l_err = NO_ERROR; + uint32_t l_addr = 0; + + do { + /* Generate the full absolute LPC address */ + l_addr = checkAddr( i_type, i_addr ); + + uint64_t eccb_data = 0; + /* Left-justify user data into data register. */ + switch ( i_size ) + { + case 1: + eccb_data = (uint64_t) + (*(const uint8_t*)(i_data)) << 56; + break; + case 2: + eccb_data = (uint64_t) + (*(const uint16_t*)( i_data ) ) << 48; + break; + case 4: + eccb_data = (uint64_t) + (*(const uint32_t*)( i_data ) ) << 32; + break; + default: + TRACFCOMP( "writeLPC> Unsupported buffer size : %d", i_size ); + break; + } + + /* Write data out */ + TRACZCOMP("ECCB_DATA_REG=%.8X%.8X",(uint32_t)(eccb_data>>32),(uint32_t)eccb_data); + xscom_write( ECCB_DATA_REG, eccb_data ); + + /* Execute command. */ + ControlReg_t eccb_cmd; + eccb_cmd.data64 = ECCB_CTL_REG_DEFAULT; + eccb_cmd.data_len = i_size; + eccb_cmd.read_op = 0; + eccb_cmd.addr_len = sizeof(l_addr); + eccb_cmd.address = l_addr; + xscom_write( ECCB_CTL_REG, eccb_cmd.data64 ); + TRACZCOMP("ECCB_CTL_REG=%.8X%.8X",(uint32_t)(eccb_cmd.data64>>32),(uint32_t)eccb_cmd.data64); + + /* Poll for completion */ + StatusReg_t eccb_stat; + l_err = pollComplete( &eccb_cmd, &eccb_stat ); + if( l_err ) { break; } + + } while(0); + + return l_err; +} diff --git a/src/occ/firdata/lpc.h b/src/occ/firdata/lpc.h new file mode 100644 index 0000000..11290cc --- /dev/null +++ b/src/occ/firdata/lpc.h @@ -0,0 +1,51 @@ +/* IBM_PROLOG_BEGIN_TAG */ +/* This is an automatically generated prolog. */ +/* */ +/* $Source: src/occ/firdata/lpc.H $ */ +/* */ +/* OpenPOWER OnChipController Project */ +/* */ +/* Contributors Listed Below - COPYRIGHT 2015 */ +/* [+] International Business Machines Corp. */ +/* */ +/* */ +/* Licensed under the Apache License, Version 2.0 (the "License"); */ +/* you may not use this file except in compliance with the License. */ +/* You may obtain a copy of the License at */ +/* */ +/* http://www.apache.org/licenses/LICENSE-2.0 */ +/* */ +/* Unless required by applicable law or agreed to in writing, software */ +/* distributed under the License is distributed on an "AS IS" BASIS, */ +/* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or */ +/* implied. See the License for the specific language governing */ +/* permissions and limitations under the License. */ +/* */ +/* IBM_PROLOG_END_TAG */ + +#ifndef _LPC_H +#define _LPC_H + +#include <native.h> +#include <errl.h> + +/** + * @enum LPC::TransType + * @brief LPC Transaction Types + */ +typedef enum { + LPC_TRANS_IO = 1, /* LPC IO Space */ + LPC_TRANS_FW = 2, /* LPC Firmware Space */ +} LpcTransType; + +errorHndl_t lpc_read( LpcTransType i_type, + uint32_t i_addr, + uint8_t* o_data, + size_t i_size ); + +errorHndl_t lpc_write( LpcTransType i_type, + uint32_t i_addr, + uint8_t* i_data, + size_t i_size ); + +#endif diff --git a/src/occ/firdata/native.c b/src/occ/firdata/native.c new file mode 100644 index 0000000..d31868f --- /dev/null +++ b/src/occ/firdata/native.c @@ -0,0 +1,68 @@ +/* IBM_PROLOG_BEGIN_TAG */ +/* This is an automatically generated prolog. */ +/* */ +/* $Source: src/occ/firdata/native.C $ */ +/* */ +/* OpenPOWER OnChipController Project */ +/* */ +/* Contributors Listed Below - COPYRIGHT 2015 */ +/* [+] International Business Machines Corp. */ +/* */ +/* */ +/* Licensed under the Apache License, Version 2.0 (the "License"); */ +/* you may not use this file except in compliance with the License. */ +/* You may obtain a copy of the License at */ +/* */ +/* http://www.apache.org/licenses/LICENSE-2.0 */ +/* */ +/* Unless required by applicable law or agreed to in writing, software */ +/* distributed under the License is distributed on an "AS IS" BASIS, */ +/* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or */ +/* implied. See the License for the specific language governing */ +/* permissions and limitations under the License. */ +/* */ +/* IBM_PROLOG_END_TAG */ + +#include <native.h> +#include <scom.h> +#include <trac.h> + +void sleep( SsxInterval i_nanoseconds ) +{ + ssx_sleep(SSX_NANOSECONDS(i_nanoseconds)); +} + +int TRACE_XSCOM=0; + +uint64_t xscom_read( uint32_t i_address ) +{ + uint64_t l_data = 0; + int l_rc = 0; + + l_rc = getscom_ffdc(i_address, + &l_data, + NULL); + if (l_rc) + { + TRAC_ERR("SCOM error in xscom_read wrapper, rc=%d", l_rc); + } + + if(TRACE_XSCOM){TRACFCOMP("xscom_read (%.8X)=%.8X%.8X", i_address, (uint32_t)(l_data>>32), (uint32_t)l_data);} + return l_data; +} + +void xscom_write( uint32_t i_address, uint64_t i_data ) +{ + int l_rc = 0; + + l_rc = putscom_ffdc(i_address, + i_data, + NULL); + + if (l_rc) + { + TRAC_ERR("SCOM error in xscom_write wrapper, rc=%d", l_rc); + } + if(TRACE_XSCOM){TRACFCOMP("xscom_write(%.8X)=%.8X%.8X", i_address, (uint32_t)(i_data>>32), (uint32_t)i_data);} +} + diff --git a/src/occ/firdata/native.h b/src/occ/firdata/native.h new file mode 100644 index 0000000..ad722dd --- /dev/null +++ b/src/occ/firdata/native.h @@ -0,0 +1,99 @@ +/* IBM_PROLOG_BEGIN_TAG */ +/* This is an automatically generated prolog. */ +/* */ +/* $Source: src/occ/firdata/native.H $ */ +/* */ +/* OpenPOWER OnChipController Project */ +/* */ +/* Contributors Listed Below - COPYRIGHT 2015 */ +/* [+] International Business Machines Corp. */ +/* */ +/* */ +/* Licensed under the Apache License, Version 2.0 (the "License"); */ +/* you may not use this file except in compliance with the License. */ +/* You may obtain a copy of the License at */ +/* */ +/* http://www.apache.org/licenses/LICENSE-2.0 */ +/* */ +/* Unless required by applicable law or agreed to in writing, software */ +/* distributed under the License is distributed on an "AS IS" BASIS, */ +/* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or */ +/* implied. See the License for the specific language governing */ +/* permissions and limitations under the License. */ +/* */ +/* IBM_PROLOG_END_TAG */ + +/* Native functions provided by OCC code */ +#ifndef _NATIVE_H +#define _NATIVE_H + +#include <common_types.h> +#include <trac.h> + +#ifdef __cplusplus +extern "C" { +#endif +#include "ssx.h" +#ifdef __cplusplus +} +#endif + +#ifndef NO_TRAC_STRINGS + +#ifdef FIRD_DEBUG +#define TRACDCOMP(frmt,args...) DBG_PRINT(frmt,##args) +#else +#define TRACDCOMP(frmt,args...) +#endif // FIRD_DEBUG + +#define TRACFCOMP(frmt,args...) TRACE(g_trac_inf,INFO_MRK frmt,##args) + +#else // NO_TRAC_STRINGS + +#define TRACDCOMP(frmt,args...) +#define TRACFCOMP(frmt,args...) + +#endif // NO_TRAC_STRINGS + +typedef uint32_t errorHndl_t; + +#define ENTER_MRK +#define NO_ERROR 0 + +/* Return a number >= input that is aligned up to the next 4-byte boundary */ +#define ALIGN_4(u) (((u) + 0x3ull) & ~0x3ull) + +#define NS_PER_SEC (1000000000ull) + +#undef be64toh +#undef htobe64 +#define be64toh(x) (x) +#define htobe64(x) (x) + +#define KILOBYTE (1024ul) /**< 1 KB */ +#define MEGABYTE (1024 * 1024ul) /**< 1 MB */ +#define GIGABYTE (MEGABYTE * 1024ul) /**< 1 GB */ +#define TERABYTE (GIGABYTE * 1024ul) /**< 1 TB */ + +#define PAGESIZE (4*KILOBYTE) /**< 4 KB */ +#define PAGE_SIZE PAGESIZE + +#undef SUCCESS +#define SUCCESS 0 + +#undef FAIL +#define FAIL -1 + +/*================================================ */ + +/* XSCOM Read */ +uint64_t xscom_read( uint32_t i_address ); + +/* XSCOM Write */ +void xscom_write( uint32_t i_address, uint64_t i_data ); + +/* Sleep */ +void sleep( SsxInterval i_nanoseconds ); + + +#endif diff --git a/src/occ/firdata/nor_micron.c b/src/occ/firdata/nor_micron.c new file mode 100644 index 0000000..3d0329a --- /dev/null +++ b/src/occ/firdata/nor_micron.c @@ -0,0 +1,73 @@ +/* IBM_PROLOG_BEGIN_TAG */ +/* This is an automatically generated prolog. */ +/* */ +/* $Source: src/occ/firdata/nor_micron.C $ */ +/* */ +/* OpenPOWER OnChipController Project */ +/* */ +/* Contributors Listed Below - COPYRIGHT 2015 */ +/* [+] International Business Machines Corp. */ +/* */ +/* */ +/* Licensed under the Apache License, Version 2.0 (the "License"); */ +/* you may not use this file except in compliance with the License. */ +/* You may obtain a copy of the License at */ +/* */ +/* http://www.apache.org/licenses/LICENSE-2.0 */ +/* */ +/* Unless required by applicable law or agreed to in writing, software */ +/* distributed under the License is distributed on an "AS IS" BASIS, */ +/* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or */ +/* implied. See the License for the specific language governing */ +/* permissions and limitations under the License. */ +/* */ +/* IBM_PROLOG_END_TAG */ + +#include "norflash.h" +#include "sfc_ast2400.h" +#include <native.h> + + + + +/** + * @brief Check flag status bit on Micron NOR chips + * Some versions of Micron parts require the Flag + * Status register be read after a write or erase operation, + * otherwise all future operations won't work.. + */ +errorHndl_t micronFlagStatus( Sfc_t* i_sfc ) +{ + errorHndl_t l_err = NO_ERROR; + /*TRACDCOMP( g_trac_pnor, "micronFlagStatus>" ); */ + + do { + /*Read Micron 'flag status' register */ + uint8_t flagstat = 0; + l_err = sendSpiCmd( i_sfc, + SPI_MICRON_FLAG_STAT, + NO_ADDRESS, + 0, NULL, + sizeof(flagstat), &flagstat ); + if(l_err) { break; } + + /*TRACDCOMP(g_trac_pnor, */ + /* "micronFlagStatus> (0x%.2X)", */ + /* flagstat); */ + + /* check for ready and no errors */ + /* bit 0 = ready, bit 2=erase fail, bit 3=Program (Write) failure */ + if( (flagstat & 0xB0) != 0x80) + { + /*TRACFCOMP(g_trac_pnor, "micronFlagStatus> Error or timeout from Micron Flag Status Register (0x%.2X)", flagstat); */ + l_err = -1; + break; + } + + + }while(0); + + return l_err; + +} + diff --git a/src/occ/firdata/norflash.h b/src/occ/firdata/norflash.h new file mode 100644 index 0000000..d41c878 --- /dev/null +++ b/src/occ/firdata/norflash.h @@ -0,0 +1,154 @@ +/* IBM_PROLOG_BEGIN_TAG */ +/* This is an automatically generated prolog. */ +/* */ +/* $Source: src/occ/firdata/norflash.H $ */ +/* */ +/* OpenPOWER OnChipController Project */ +/* */ +/* Contributors Listed Below - COPYRIGHT 2015 */ +/* [+] International Business Machines Corp. */ +/* */ +/* */ +/* Licensed under the Apache License, Version 2.0 (the "License"); */ +/* you may not use this file except in compliance with the License. */ +/* You may obtain a copy of the License at */ +/* */ +/* http://www.apache.org/licenses/LICENSE-2.0 */ +/* */ +/* Unless required by applicable law or agreed to in writing, software */ +/* distributed under the License is distributed on an "AS IS" BASIS, */ +/* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or */ +/* implied. See the License for the specific language governing */ +/* permissions and limitations under the License. */ +/* */ +/* IBM_PROLOG_END_TAG */ + +#ifndef __PNOR_NORFLASH_H +#define __PNOR_NORFLASH_H + +#include <native.h> +#include <sfc_ast2400.h> + +/** @file norflash.H + * @brief Contains constants related to specific types of + * of NOR flash chips + */ + +/** + * @brief Supported NOR Chip IDs + */ +enum NorChipIDs +{ + UNKNOWN_NOR_ID = 0x12345600, /**< Initial value before read */ + + MICRON_MFG_ID = 0x20000000, /**< Micron Mfg ID */ + MICRON_NOR_ID = 0x20ba2000, /**< Micron NOR */ + + MACRONIX_MFG_ID = 0xC2000000, /**< Macronix Mfg ID */ + MACRONIX32_NOR_ID = 0xC2201A00, /**< Macronix NOR MXxxL51235F */ + MACRONIX64_NOR_ID = 0xC2201900, /**< Macronix NOR MXxxL25635F */ + + /* Note: Simics currently models Micron NOR */ + VPO_NOR_ID = 0x20201800, /**< VPO NOR chip ID */ + FAKE_NOR_ID = 0xBADBAD00, /**< ID used during fake pnor */ + + ID_MASK = 0xFFFFFF00, /**< Only look at 3 bytes */ + MFGID_MASK = 0xFF000000, /**< Manufacturer ID is the first byte */ +}; + +/** + * @brief SPI Config Info + * OP Codes and other MISC info for configuring SFC + */ +typedef enum +{ + SPI_NO_OPCODE = 0x00, /**< Undefined value */ + + /* + * Micron Flash Commands + */ + SPI_MICRON_FLAG_STAT = 0x70, /**< Check write/erase complete */ + SPI_MICRON_CLRFLAG_STAT = 0x50, /**< Clear write/erase Status reg */ + + /* + * Macronix Flash Commands + */ + SPI_MACRONIX_EN4B = 0xB7, /**< Enable Macronix 4-Byte addressing */ + + /* SPI protocol commands */ + SPI_JEDEC_WRITE_STATUS = 0x01, /*WRSR */ + SPI_JEDEC_PAGE_PROGRAM = 0x02, /*PP */ + SPI_JEDEC_READ = 0x03, /*READ */ + SPI_JEDEC_WRITE_DISABLE = 0x04, /*WRDI */ + SPI_JEDEC_READ_STATUS = 0x05, /*RDSR */ + SPI_JEDEC_WRITE_ENABLE = 0x06, /*WREN */ + SPI_JEDEC_FAST_READ = 0x0B, /*FAST_READ */ + SPI_JEDEC_SECTOR_ERASE = 0x20, /*SE */ + SPI_JEDEC_READ_SFDP = 0x5A, /*RDSFDP */ + SPI_JEDEC_CHIPID = 0x9F, /*RDID */ + SPI_JEDEC_BLOCK_ERASE = 0xD8, /*BE */ + +} SpiConfigInfo; + +/** + * @brief General Constants related to flash + */ +enum +{ + PAGE_PROGRAM_BYTES = 256, /***< 256 bytes per PP command */ +}; + + +/** + * Common format of Status Register + */ +typedef union +{ + uint8_t data8; + struct + { + uint8_t writeProtect : 1; /*0 */ + uint8_t rsvd : 5; /*1:5 */ + uint8_t writeEnable : 1; /*6 */ + uint8_t writeInProgress : 1; /*7 */ + }; +} NorStatusReg_t; + +/** + * Flags used to trigger Hardware workarounds + */ +enum +{ + /* No workarounds present */ + HWWK_NO_WORKAROUNDS = 0x00000000, + + /* Must perform 'read flag status' commands after */ + /* any write or erase */ + HWWK_MICRON_WRT_ERASE = 0x00000001, + + /* Must do a read of a low flash address before issuing read */ + /* commands that return more than 1 word of data */ + HWWK_MICRON_EXT_READ = 0x00000002, +}; + + +/* + * Vendor-specific interfaces + */ + +/** + * @brief Check flag status bit on Micron NOR chips + * The current version of Micron parts require the Flag + * Status register be read after a read or erase operation, + * otherwise all future operations won't work.. + * + * @parm i_sfc SFC driver to operate on + * + * @return Error from operation + */ +errorHndl_t micronFlagStatus( Sfc_t* i_sfc ); + + + + +#endif diff --git a/src/occ/firdata/pnorData_common.h b/src/occ/firdata/pnorData_common.h new file mode 100644 index 0000000..5ef10a1 --- /dev/null +++ b/src/occ/firdata/pnorData_common.h @@ -0,0 +1,164 @@ +/* IBM_PROLOG_BEGIN_TAG */ +/* This is an automatically generated prolog. */ +/* */ +/* $Source: src/occ/firdata/pnorData.H $ */ +/* */ +/* OpenPOWER OnChipController Project */ +/* */ +/* Contributors Listed Below - COPYRIGHT 2015 */ +/* [+] International Business Machines Corp. */ +/* */ +/* */ +/* Licensed under the Apache License, Version 2.0 (the "License"); */ +/* you may not use this file except in compliance with the License. */ +/* You may obtain a copy of the License at */ +/* */ +/* http://www.apache.org/licenses/LICENSE-2.0 */ +/* */ +/* Unless required by applicable law or agreed to in writing, software */ +/* distributed under the License is distributed on an "AS IS" BASIS, */ +/* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or */ +/* implied. See the License for the specific language governing */ +/* permissions and limitations under the License. */ +/* */ +/* IBM_PROLOG_END_TAG */ + +#ifndef __pnorData_common_h +#define __pnorData_common_h + +/** NOTE: This file is common between OCC and Hosboot. Any change to this file + * must be mirrored to both repositories. */ + +#include <firDataConst_common.h> + +/** This file is used to define the format of the register data captured by the + * OCC and stored in PNOR. The data will be stored in the following format: + * + * - PNOR_Data_t struct - This has all of the information characterizing how + * many targets that have register data. + * - For each target with register data, the following format will be used: + * - PNOR_Trgt_t struct - Contains the target type, position, and how many + * registers are in the register list. + * - A list of all regular registers (PNOR_Reg_t). + * - A list of all indirect-SCOM registers (PNOR_IdReg_t). + * + * The PNOR has limited data space. So the following rules will apply: + * - Any registers with the value of zero will not be captured. + * - Registers with SCOM errors will not be captured, however, the number + * of SCOM errors detected should be stored in each PNOR_Trgt_t struct. + * - If the value of a FIR (or ID FIR) is zero, do not capture the + * associated ACT0 and ACT1 registers. Note that the associated MASK and + * WOF registers are still needed. + * - Each target type may have associated global registers. If none exist, + * simply capture all registers for that type. However, if they do exist + * and the values of ALL the global registers are zero, skip capturing + * the associated targets using the following rules: + * - For a PROC, skip this PROC and all associated EXs, and MCSs. + * - For an EX, skip this EX. + * - For an MCS, skip this MCS. + * - For a MEMB, skip this MEMB and all associated MBAs. + * - For an MBA, skip this MBA. + * - If for some reason we run out of space in the PNOR, do not SCOM any + * more registers, set the 'full' bit in the PNOR_Data_t struct, and + * write all data successfully captured to PNOR. + */ + +typedef enum +{ + PNOR_FIR1 = 0x46495231, /*/< FIR data version 1 ("FIR1" in ascii) */ + +} PNOR_Version_t; + +/** PNOR data header information. */ +typedef struct __attribute__((packed)) +{ + uint32_t header; /*/< Magic number to indicate valid data and version */ + + uint32_t trgts : 8; /*/< Number of targets with register data */ + uint32_t full : 1; /*/< 1 if PNOR data is full and data may be missing */ + uint32_t reserved : 23; + +} PNOR_Data_t; + +/** @return An initialized PNOR_Data_t struct. */ +static inline PNOR_Data_t PNOR_getData() +{ + PNOR_Data_t d; + d.header = PNOR_FIR1; + d.trgts = 0; + d.full = 0; + d.reserved = 0; + + return d; +}; + +/** These values will match the corresponding bit fields in PNOR_Trgt_t. */ +typedef enum RegLimits +{ + PNOR_Trgt_MAX_REGS_PER_TRGT = 511, /* Currently expect 266 on the PROC */ + PNOR_Trgt_MAX_ID_REGS_PER_TRGT = 15, /* Currently expect 9 on the MBA */ + PNOR_Trgt_MAX_SCOM_ERRORS = 511, /* Should be plenty */ + +} PNOR_Trgt_RegLimits_t; + +/** Information for each target with SCOM data. */ +typedef struct __attribute__((packed)) +{ + uint32_t type : 3; /*/< Target type. See enum TrgtType_t */ + uint32_t procPos : 3; /*/< The processor position (0-7) */ + uint32_t unitPos : 4; /*/< Unit position relative to the processor (0-15) */ + uint32_t regs : 9; /*/< Number of normal registers */ + uint32_t idRegs : 4; /*/< Number of indirect-SCOM registers */ + uint32_t scomErrs : 9; /*/< Number of SCOM errors detected */ + +} PNOR_Trgt_t; + +/** @param i_type Target type. See enum TrgtType_t. + * @param i_procPos The processor position. + * @param i_procUnitPos Unit position relative to the processor. + * @return An initialized PNOR_Data_t struct. + */ +static inline PNOR_Trgt_t PNOR_getTrgt( uint32_t i_type, uint32_t i_procPos, + uint32_t i_procUnitPos ) +{ + PNOR_Trgt_t t; + t.type = i_type; + t.procPos = i_procPos; + t.unitPos = i_procUnitPos; + t.regs = 0; + t.idRegs = 0; + t.scomErrs = 0; + + return t; +}; + +/** Information for a normal register. */ +typedef struct __attribute__((packed)) +{ + uint32_t addr; /*/< 32-bit address */ + uint64_t val; /*/< 64-bit value */ + +} PNOR_Reg_t; + +/** Information for an indirect-SCOM register. */ +typedef struct __attribute__((packed)) +{ + uint64_t addr; /*/< 64-bit address */ + uint32_t val; /*/< 32-bit value */ + +} PNOR_IdReg_t; + +/** @return An initialized PNOR_IdReg_t struct. */ +inline +PNOR_IdReg_t PNOR_getIdReg() +{ + PNOR_IdReg_t r = { + .addr = 0, + .val = 0, + }; + + return r; +}; + +#endif /* __pnorData_common_h */ + diff --git a/src/occ/firdata/pnor_util.c b/src/occ/firdata/pnor_util.c new file mode 100644 index 0000000..bf32480 --- /dev/null +++ b/src/occ/firdata/pnor_util.c @@ -0,0 +1,236 @@ +/* IBM_PROLOG_BEGIN_TAG */ +/* This is an automatically generated prolog. */ +/* */ +/* $Source: src/occ/firdata/pnor_util.C $ */ +/* */ +/* OpenPOWER OnChipController Project */ +/* */ +/* Contributors Listed Below - COPYRIGHT 2015 */ +/* [+] International Business Machines Corp. */ +/* */ +/* */ +/* Licensed under the Apache License, Version 2.0 (the "License"); */ +/* you may not use this file except in compliance with the License. */ +/* You may obtain a copy of the License at */ +/* */ +/* http://www.apache.org/licenses/LICENSE-2.0 */ +/* */ +/* Unless required by applicable law or agreed to in writing, software */ +/* distributed under the License is distributed on an "AS IS" BASIS, */ +/* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or */ +/* implied. See the License for the specific language governing */ +/* permissions and limitations under the License. */ +/* */ +/* IBM_PROLOG_END_TAG */ + +/* Interfaces to write into PNOR */ + +#include <native.h> +#include <sfc_ast2400.h> +#include <ecc.h> +#include <pnor_util.h> +#include <norflash.h> + +/*================================================================= */ +/* The offset of the next byte to write */ +uint32_t g_next_byte = 0xFFFFFFFF; +/* Size of the FIRDATA section of PNOR */ +uint32_t g_pnor_size = 0; +/* Global SFC object */ +Sfc_t g_sfc; +/* Cache to queue up PNOR writes */ +uint8_t g_write_cache[PAGE_PROGRAM_BYTES]; +/* Current position of data inside write cache */ +size_t g_write_cache_index = 0; + +/** + * @brief Write 8 bytes of data into PNOR starting + */ +int32_t pnor_write_8B( uint64_t i_data ) +{ + int32_t rc = SUCCESS; + + if ( (g_next_byte == 0xFFFFFFFF) || /* initialized data */ + ((g_next_byte + g_pnor_size) < (g_next_byte + 9)) ) /* make sure there is room */ + { + TRACFCOMP("pnor_write_8B> g_next_byte=%.8X, g_pnor_size=%.8X",g_next_byte,g_pnor_size); + /* must have been some error in the prep */ + return FAIL; + } + + /* Create 9-byte ECC-ified version */ + uint8_t data9[9]; + injectECC( (uint8_t*)(&i_data), 8, data9 ); + + /* Copy data into the write cache until we queue up + a big chunk of data to write. This is more efficient + and avoids handling the write boundary of the PP + command internally. */ + size_t cpsz = 9; + if( (g_write_cache_index + cpsz) > PAGE_PROGRAM_BYTES ) + { + cpsz = PAGE_PROGRAM_BYTES - g_write_cache_index; + } + memcpy( &(g_write_cache[g_write_cache_index]), data9, cpsz ); + g_write_cache_index += cpsz; + + /* Write a complete chunk into the flash */ + if( g_write_cache_index == PAGE_PROGRAM_BYTES ) + { + errorHndl_t tmp = writeFlash( &g_sfc, + g_next_byte, + PAGE_PROGRAM_BYTES, + g_write_cache ); + if ( NO_ERROR != tmp ) + { + TRACFCOMP("pnor_write_8B> writeFlash failed"); + /* hit an error, stop any more writes from happening */ + g_next_byte = 0xFFFFFFFF; + g_pnor_size = 0; + return FAIL; + } + g_next_byte += PAGE_PROGRAM_BYTES; + memset( g_write_cache, 0xFF, PAGE_PROGRAM_BYTES ); + g_write_cache_index = 0; + + /* Handle the overflow */ + if( (9 - cpsz) > 0 ) + { + memcpy( &(g_write_cache[0]), &(data9[cpsz]), 9 - cpsz ); + g_write_cache_index = 9 - cpsz; + } + } + + return rc; +} + + +/** + * @brief Perform any necessary operations to prepare + * the PNOR hw/code for writing + */ +errorHndl_t pnor_prep( HOMER_PnorInfo_t* i_pnorInfo ) +{ + errorHndl_t l_err = NO_ERROR; + + g_sfc.iv_mmioOffset = i_pnorInfo->mmioOffset; + g_sfc.iv_flashWorkarounds = i_pnorInfo->norWorkarounds; + + /* Figure out where to start */ + TRACFCOMP("FIRDATA is at %.8X..%.8X", i_pnorInfo->pnorOffset, i_pnorInfo->pnorOffset+i_pnorInfo->pnorSize ); + g_next_byte = i_pnorInfo->pnorOffset; + g_pnor_size = i_pnorInfo->pnorSize; + memset( g_write_cache, 0xFF, PAGE_PROGRAM_BYTES ); + + /* Can we rely on skiboot leaving things in a good state? */ + l_err = hwInit(&g_sfc); + if( l_err ) + { + TRACFCOMP("hwInit failed"); + /* hit an error, stop any writes from happening */ + g_next_byte = 0xFFFFFFFF; + g_pnor_size = 0; + } + + /* Future Improvement + Enable write mode once at the beginning to avoid extra + reg operations turning it on and off + l_err = enableWriteMode(g_sfc); + if( l_err ) + { + g_next_byte = 0xFFFFFFFF; + g_pnor_size = 0; + } + */ + + return l_err; +} + +/*------------------------------------------------------------------------------ */ + +int32_t PNOR_writeFirData( HOMER_PnorInfo_t i_pnorInfo, + uint8_t * i_buf, uint32_t i_bufSize ) +{ + int32_t rc = SUCCESS; + TRACFCOMP(">>PNOR_writeFirData"); + + do + { + /* Initialize the PNOR data. */ + errorHndl_t l_err = pnor_prep( &i_pnorInfo ); + if( l_err ) + { + TRACFCOMP("pnor_prep failed"); + rc = FAIL; + break; /*nothing more to do here*/ + } + + uint32_t idx = 0; + + /* Erase the section. */ + for( idx = i_pnorInfo.pnorOffset; + idx < (i_pnorInfo.pnorOffset+i_pnorInfo.pnorSize); + idx += 4096 ) + { + l_err = eraseFlash(&g_sfc,idx); + if( l_err ) + { + TRACFCOMP("eraseFlash failed"); + rc = FAIL; + break; /*nothing more to do here*/ + } + } + + uint64_t dataChunk = 0; + size_t sz_dataChunk = sizeof(uint64_t); + + /* Add PNOR data 8 bytes at a time. */ + for ( idx = 0; idx < i_bufSize; idx += sz_dataChunk ) + { + memcpy( &dataChunk, &i_buf[idx], sz_dataChunk ); + + rc = pnor_write_8B( dataChunk ); + if ( SUCCESS != rc ) + { + TRACFCOMP( "pnor_write_8B() failed during FIR write" ); + break; + } + } + if ( SUCCESS != rc ) break; + + /* Add any extra bytes if they exist at the end of the buffer. */ + if ( idx != i_bufSize ) + { + uint32_t extraBytes = idx - i_bufSize; + + dataChunk = 0; + memcpy( &dataChunk, &i_buf[idx], extraBytes ); + + rc = pnor_write_8B( dataChunk ); + if ( SUCCESS != rc ) + { + TRACFCOMP( "pnor_write_8B() failed during blank fill" ); + break; + } + } + + /* Fill the rest of the page with good ECC */ + for ( idx = i_bufSize; + idx < i_pnorInfo.pnorSize; + idx += sz_dataChunk ) + { + dataChunk = 0xFFFFFFFFFFFFFFFFull; + rc = pnor_write_8B( dataChunk ); + if ( SUCCESS != rc ) + { + TRACFCOMP( "pnor_write_8B() failed during ECC fill" ); + break; + } + } + + } while (0); + + TRACFCOMP("<<PNOR_writeFirData"); + return rc; +} + diff --git a/src/occ/firdata/pnor_util.h b/src/occ/firdata/pnor_util.h new file mode 100644 index 0000000..d8b5518 --- /dev/null +++ b/src/occ/firdata/pnor_util.h @@ -0,0 +1,41 @@ +/* IBM_PROLOG_BEGIN_TAG */ +/* This is an automatically generated prolog. */ +/* */ +/* $Source: src/occ/firdata/pnor_util.H $ */ +/* */ +/* OpenPOWER OnChipController Project */ +/* */ +/* Contributors Listed Below - COPYRIGHT 2015 */ +/* [+] International Business Machines Corp. */ +/* */ +/* */ +/* Licensed under the Apache License, Version 2.0 (the "License"); */ +/* you may not use this file except in compliance with the License. */ +/* You may obtain a copy of the License at */ +/* */ +/* http://www.apache.org/licenses/LICENSE-2.0 */ +/* */ +/* Unless required by applicable law or agreed to in writing, software */ +/* distributed under the License is distributed on an "AS IS" BASIS, */ +/* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or */ +/* implied. See the License for the specific language governing */ +/* permissions and limitations under the License. */ +/* */ +/* IBM_PROLOG_END_TAG */ + +#ifndef __pnor_util_h +#define __pnor_util_h + +#include <homerData_common.h> + +/** + * @brief Writes a buffer containing the FIR data to PNOR (adding ECC). + * @param i_pnorInfo Information regarding PNOR location, size, etc. + * @param i_buf Data buffer (no ECC included). + * @param i_bufSize Size of the data buffer. + * @return Non-SUCCESS, if the write fails. SUCCESS, otherwise. + */ +int32_t PNOR_writeFirData( HOMER_PnorInfo_t i_pnorInfo, + uint8_t * i_buf, uint32_t i_bufSize ); + +#endif /* __pnor_util_h */ diff --git a/src/occ/firdata/scom_trgt.c b/src/occ/firdata/scom_trgt.c new file mode 100644 index 0000000..ec38122 --- /dev/null +++ b/src/occ/firdata/scom_trgt.c @@ -0,0 +1,135 @@ +/* IBM_PROLOG_BEGIN_TAG */ +/* This is an automatically generated prolog. */ +/* */ +/* $Source: src/occ/firdata/scoms.C $ */ +/* */ +/* OpenPOWER OnChipController Project */ +/* */ +/* Contributors Listed Below - COPYRIGHT 2015 */ +/* [+] International Business Machines Corp. */ +/* */ +/* */ +/* Licensed under the Apache License, Version 2.0 (the "License"); */ +/* you may not use this file except in compliance with the License. */ +/* You may obtain a copy of the License at */ +/* */ +/* http://www.apache.org/licenses/LICENSE-2.0 */ +/* */ +/* Unless required by applicable law or agreed to in writing, software */ +/* distributed under the License is distributed on an "AS IS" BASIS, */ +/* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or */ +/* implied. See the License for the specific language governing */ +/* permissions and limitations under the License. */ +/* */ +/* IBM_PROLOG_END_TAG */ + +#include <native.h> +#include <scom_trgt.h> +#include <scom_util.h> + +/*------------------------------------------------------------------------------ */ + +SCOM_Trgt_t SCOM_Trgt_getTrgt( TrgtType_t i_type, uint8_t i_procPos, + uint8_t i_procUnitPos, uint32_t i_fsiBaseAddr, + bool i_isMaster ) +{ + SCOM_Trgt_t trgt = { + .type = i_type, + .procPos = i_procPos, + .procUnitPos = i_procUnitPos, + .isMaster = i_isMaster, + .fsiBaseAddr = i_fsiBaseAddr, + }; + + if ( PROC == trgt.type ) trgt.procUnitPos = 0; + + if ( MEMB == trgt.type || MBA == trgt.type ) trgt.isMaster = false; + + return trgt; +} + +/*------------------------------------------------------------------------------ */ + +uint8_t SCOM_Trgt_getChipPos( SCOM_Trgt_t i_trgt ) +{ + uint32_t p = 0; + + switch ( i_trgt.type ) + { + case PROC: + case EX: + case MCS: + p = i_trgt.procPos; + break; + + case MEMB: + p = (i_trgt.procPos * MAX_MEMB_PER_PROC) + i_trgt.procUnitPos; + break; + + case MBA: + p = (i_trgt.procPos * MAX_MEMB_PER_PROC) + + (i_trgt.procUnitPos / MAX_MBA_PER_MEMB); + break; + + default: ; + } + + return p; +} + +/*------------------------------------------------------------------------------ */ + +uint8_t SCOM_Trgt_getChipUnitPos( SCOM_Trgt_t i_trgt ) +{ + uint32_t u = 0; + + switch ( i_trgt.type ) + { + case PROC: + case MEMB: u = 0; break; + + case EX: + case MCS: u = i_trgt.procUnitPos; break; + + case MBA: u = i_trgt.procUnitPos % MAX_MBA_PER_MEMB; break; + + default: ; + } + + return u; +} + +/*------------------------------------------------------------------------------ */ + +SCOM_Trgt_t SCOM_Trgt_getParentChip( SCOM_Trgt_t i_trgt ) +{ + TrgtType_t t = MAX_TRGTS; + switch ( i_trgt.type ) + { + case PROC: + case EX: + case MCS: t = PROC; break; + + case MEMB: + case MBA: t = MEMB; break; + + default: ; + } + + uint8_t u = 0; + switch ( i_trgt.type ) + { + case PROC: + case EX: + case MCS: + case MEMB: u = i_trgt.procUnitPos; break; + + case MBA: u = i_trgt.procUnitPos / MAX_MBA_PER_MEMB; break; + + default: ; + } + + return SCOM_Trgt_getTrgt( t, i_trgt.procPos, u, i_trgt.fsiBaseAddr, + i_trgt.isMaster ); +} + diff --git a/src/occ/firdata/scom_trgt.h b/src/occ/firdata/scom_trgt.h new file mode 100644 index 0000000..4ffe8b5 --- /dev/null +++ b/src/occ/firdata/scom_trgt.h @@ -0,0 +1,89 @@ +/* IBM_PROLOG_BEGIN_TAG */ +/* This is an automatically generated prolog. */ +/* */ +/* $Source: src/occ/firdata/scom.H $ */ +/* */ +/* OpenPOWER OnChipController Project */ +/* */ +/* Contributors Listed Below - COPYRIGHT 2015 */ +/* [+] International Business Machines Corp. */ +/* */ +/* */ +/* Licensed under the Apache License, Version 2.0 (the "License"); */ +/* you may not use this file except in compliance with the License. */ +/* You may obtain a copy of the License at */ +/* */ +/* http://www.apache.org/licenses/LICENSE-2.0 */ +/* */ +/* Unless required by applicable law or agreed to in writing, software */ +/* distributed under the License is distributed on an "AS IS" BASIS, */ +/* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or */ +/* implied. See the License for the specific language governing */ +/* permissions and limitations under the License. */ +/* */ +/* IBM_PROLOG_END_TAG */ + +#ifndef __scom_trgt_h +#define __scom_trgt_h + +#include <firDataConst_common.h> +#include <native.h> + +typedef struct +{ + /** See enum TrgtType_t. NOTE: This value is not consistant with Hostboot + * target types. */ + TrgtType_t type; + + /** Absolute position of the connected PROC within the node. This value + * should be consistant with the Hostboot target positions. */ + uint8_t procPos; + + /** Unit position relative to the connected PROC. This value should be + * consistant with the Hostboot target positions. */ + uint8_t procUnitPos; + + /** Indicates this target is, or is connected to, the master processor. */ + bool isMaster; + + /** This target's FSI base address. */ + uint32_t fsiBaseAddr; + +} SCOM_Trgt_t; + +/** @param i_type See enum Type. + * @param i_procPos Absolute position within the node of the connected + * PROC target. + * @param i_procUnitPos Unit position relative to the connected PROC. Will be + * explicitly set to 0 for PROC targets. + * @param i_fsiBaseAddr For EX and MCS, the FSI base address for the + * connected PROC. For MEMB and MBA, the FSI base + * address for the connected MEMB. + * @param i_isMaster True, if this target is, or is connected to, the + * master processor. False, otherwise. Will be explicitly + * set to false for MEMB and MBA targets. + * @return A SCOM_Trgt_t struct. + */ +SCOM_Trgt_t SCOM_Trgt_getTrgt( TrgtType_t i_type, uint8_t i_procPos, + uint8_t i_procUnitPos, uint32_t i_fsiBaseAddr, + bool i_isMaster ); + +/** @param i_trgt The SCOM target. + * @return This target's absolute position of the parent chip (PROC or + * MEMB) within the node. + */ +uint8_t SCOM_Trgt_getChipPos( SCOM_Trgt_t i_trgt ); + +/** @param i_trgt The SCOM target. + * @return This target's unit position relative to the parent chip. Only + * valid for EX, MCS, and MBA units. Will return 0 for PROC and + * MEMB chips. + */ +uint8_t SCOM_Trgt_getChipUnitPos( SCOM_Trgt_t i_trgt ); + +/** @param i_trgt The SCOM target. + * @return A target for the containing parent chip (PROC or MEMB). + */ +SCOM_Trgt_t SCOM_Trgt_getParentChip( SCOM_Trgt_t i_trgt ); + +#endif /* __scom_trgt_h */ diff --git a/src/occ/firdata/scom_util.c b/src/occ/firdata/scom_util.c new file mode 100644 index 0000000..4762b3f --- /dev/null +++ b/src/occ/firdata/scom_util.c @@ -0,0 +1,386 @@ +/* IBM_PROLOG_BEGIN_TAG */ +/* This is an automatically generated prolog. */ +/* */ +/* $Source: src/occ/firdata/scom_util.C $ */ +/* */ +/* OpenPOWER OnChipController Project */ +/* */ +/* Contributors Listed Below - COPYRIGHT 2015 */ +/* [+] International Business Machines Corp. */ +/* */ +/* */ +/* Licensed under the Apache License, Version 2.0 (the "License"); */ +/* you may not use this file except in compliance with the License. */ +/* You may obtain a copy of the License at */ +/* */ +/* http://www.apache.org/licenses/LICENSE-2.0 */ +/* */ +/* Unless required by applicable law or agreed to in writing, software */ +/* distributed under the License is distributed on an "AS IS" BASIS, */ +/* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or */ +/* implied. See the License for the specific language governing */ +/* permissions and limitations under the License. */ +/* */ +/* IBM_PROLOG_END_TAG */ + +/* Support for SCOM operations */ +#include <scom_util.h> +#include <fsi.h> +#include <native.h> + +enum { + /*FSI addresses are byte offsets, so need to multiply by 4 + since each register is 4 bytes long. + prefix with 0x10xx for FSI2PIB engine offset */ + DATA0_REG = 0x1000, /* SCOM Data Register 0 (0x00) */ + DATA1_REG = 0x1004, /* SCOM Data Register 1 (0x01) */ + COMMAND_REG = 0x1008, /* SCOM Command Register (0x02) */ + ENGINE_RESET_REG = 0x1018, /* Engine Reset Register (0x06) */ + STATUS_REG = 0x101C, /* STATUS Register (0x07) */ + PIB_RESET_REG = 0x101C, /* PIB Reset Register (0x07) */ + + PIB_ABORT_BIT = 0x00100000, /* 12= PIB Abort */ + PIB_ERROR_BITS = 0x00007000, /* 17:19= PCB/PIB Errors */ +}; + +/** + * @brief Indirect SCOM Status + */ +typedef union +{ + uint64_t data64; + struct + { + uint64_t :12; /*0:11*/ + uint64_t addr:20; /*12:31*/ + uint64_t done:1; /*32*/ + uint64_t piberr:3; /*33:35*/ + uint64_t userstat:4; /*36:39*/ + uint64_t :8; /*40:47*/ + uint64_t data:16; /*48:63*/ + }; +} IndirectScom_t; + +enum { + MCS_MASK = 0xFFFFFFFF7FFFFF80, + MCS_BASEADDR = 0x0000000002011800, + MCS_DMI_BASEADDR = 0x0000000002011A00, + IND_MCS_BASEADDR = 0x8000006002011A00, + IND_MCS_DMI_BASEADDR = 0x8000006002011A3F, + MBA_MASK = 0xFFFFFFFF7FFFFC00, + MBA_BASEADDR = 0x0000000003010400, + TCM_MBA_BASEADDR = 0x0000000003010800, + IND_MBA_BASEADDR = 0x800000000301143f, +}; + + + +/** + * @brief Translate a relative unit address to a real physical address + * @param[in] Chip/unit to scom + * @param[in] Address to scom, unit0's address for unit scoms + * @return Physical address + */ +uint32_t translate_scom( SCOM_Trgt_t i_target, + uint32_t i_address ) +{ + TrgtType_t l_type = i_target.type; + uint32_t l_addr = i_address; + + /* No translation needed for non-unit scoms */ + if( (l_type == PROC) || (l_type == MEMB) ) + { + l_addr = i_address; + } + else /* it is a Unit */ + { + uint8_t l_num = SCOM_Trgt_getChipUnitPos(i_target); + + if( l_type == EX ) + { + /*first byte is 0x10, second nibble of that byte is the EX number */ + l_addr |= (l_num << 24); + } + else if( l_type == MCS ) + { + /*Non-DMI address */ + if( (i_address & MCS_MASK) == MCS_BASEADDR ) + { + /* MC0 MCS0 = 0x02011800 MCS-0 range 0 */ + /* MC0 MCS1 = 0x02011880 MCS-1 range 0 + remainder */ + /* MC1 MCS0 = 0x02011900 MCS-2 range 1 */ + /* MC1 MCS0 = 0x02011980 MCS-3 range 1 + remainder */ + /* MC2 MCS0 = 0x02011C00 MCS-4 range 2 */ + /* MC2 MCS1 = 0x02011C80 MCS-5 range 2 + remainder */ + /* MC3 MCS0 = 0x02011D00 MCS-6 range 3 */ + /* MC3 MCS1 = 0x02011D80 MCS-7 range 3 + remainder */ + if( (l_num / 2) == 1) /*range 1 */ + { + l_addr += 0x100; + } + else if( (l_num / 2) == 2) /*range 2 */ + { + l_addr += 0x400; + } + else if( (l_num / 2) == 3) /*range 3 */ + { + l_addr += 0x500; + } + + /* Add 0x80 for the odd numbers */ + if( l_num % 2) + { + l_addr += 0x80; + } + } + else if( (i_address & MCS_MASK) == MCS_DMI_BASEADDR ) + { + /* 0x00000000_02011A00 MCS 0-3 # MCS/DMI0 Direct SCOM */ + /* 0x00000000_02011E00 MCS 4-7 # MCS/DMI4 Direct SCOM */ + if( l_num > 3 ) + { + l_addr |= 0x400; /* A00->E00 */ + } + } + else if( (i_address & MCS_MASK) == IND_MCS_DMI_BASEADDR ) + { + /* 0x80000060_02011A3F MCS 0 # DMI0 Indirect SCOM RX3 */ + /* 0x80000040_02011A3F MCS 1 # DMI1 Indirect SCOM RX2 */ + /* 0x80000000_02011A3F MCS 2 # DMI3 Indirect SCOM RX0 */ + /* 0x80000020_02011A3F MCS 3 # DMI2 Indirect SCOM RX1 */ + /* */ + /* 0x80000060_02011E3F MCS 4 # DMI4 Indirect SCOM RX3 */ + /* 0x80000040_02011E3F MCS 5 # DMI5 Indirect SCOM RX2 */ + /* 0x80000000_02011E3F MCS 6 # DMI7 Indirect SCOM RX0 */ + /* 0x80000020_02011E3F MCS 7 # DMI6 Indirect SCOM RX1 */ + /* */ + /* 0x80000460_02011A3F MCS 0 # DMI0 Indirect SCOM TX3 */ + /* 0x80000440_02011A3F MCS 1 # DMI1 Indirect SCOM TX2 */ + /* 0x80000400_02011A3F MCS 2 # DMI3 Indirect SCOM TX0 */ + /* 0x80000420_02011A3F MCS 3 # DMI2 Indirect SCOM TX1 */ + /* */ + /* 0x80000460_02011E3F MCS 4 # DMI4 Indirect SCOM TX3 */ + /* 0x80000440_02011E3F MCS 5 # DMI5 Indirect SCOM TX2 */ + /* 0x80000400_02011E3F MCS 6 # DMI7 Indirect SCOM TX0 */ + /* 0x80000420_02011E3F MCS 7 # DMI6 Indirect SCOM TX1 */ + + /* zero out the instance bits */ + l_addr &= 0xFFFFFF9FFFFFFFFF; + switch( l_num ) + { + case(0): + case(4): + l_addr |= 0x0000006000000000; + break; + case(1): + case(5): + l_addr |= 0x0000004000000000; + break; + case(2): + case(6): + /*nothing to do */ + break; + case(3): + case(7): + l_addr |= 0x0000002000000000; + break; + default: + l_addr = SCOMFAIL; + } + if( l_num > 3 ) + { + l_addr |= 0x400; /* A00->E00 */ + } + } + } + else if( l_type == MBA ) + { + if( (i_address & MBA_MASK) == MBA_BASEADDR ) + { + /* 0x00000000_03010400 MBA 0 # MBA01 */ + /* 0x00000000_03010C00 MBA 1 # MBA23 */ + if( l_num == 1 ) + { + l_addr |= 0x00000800; + } + } + else if( (i_address & MBA_MASK) == TCM_MBA_BASEADDR ) + { + /* 0x00000000_03010880 MBA 0 # Trace for MBA01 */ + /* 0x00000000_030110C0 MBA 1 # Trace for MBA23 */ + l_addr += (l_num * 0x840); + } + else if( (i_address & MBA_MASK) == IND_MBA_BASEADDR ) + { + /* 0x00000000_03011400 MBA 0 # DPHY01 (indirect addressing) */ + /* 0x00000000_03011800 MBA 1 # DPHY23 (indirect addressing) */ + /* 0x80000000_0301143f MBA 0 # DPHY01 (indirect addressing) */ + /* 0x80000000_0301183f MBA 1 # DPHY23 (indirect addressing) */ + /* 0x80000000_0701143f MBA 0 # DPHY01 (indirect addressing) */ + /* 0x80000000_0701183f MBA 1 # DPHY23 (indirect addressing) */ + if( l_num == 1 ) + { + /* 030114zz->030118zz */ + l_addr &= 0xFFFFFFFFFFFFFBFF; + l_addr |= 0x0000000000000800; + } + } + } + else + { + l_addr = SCOMFAIL; + } + } + + return l_addr; +} + + +/** + * @brief Perform a getscom operation with no address translation + */ +uint64_t getscomraw( SCOM_Trgt_t i_chip, + uint32_t i_address ) +{ + /* Scoms to the master chip are done via xscom */ + if( i_chip.isMaster ) + { + return xscom_read(i_address); + } + + /*1) send the command to do the scom read */ + putfsi( i_chip, COMMAND_REG, i_address ); + /*2) check status next -- TODO */ + /*3) read the two data regs */ + uint64_t scomdata = SCOMFAIL; + uint32_t data = getfsi( i_chip, DATA0_REG ); + if( data == FSIFAIL ) + { + return SCOMFAIL; + } + scomdata = data; + scomdata <<= 32; + data = getfsi( i_chip, DATA1_REG ); + if( data == FSIFAIL ) + { + return SCOMFAIL; + } + scomdata |= (uint64_t)data; + + return scomdata; +} + + +/** + * @brief Perform a scom operation with no address translation + */ +void putscomraw( SCOM_Trgt_t i_chip, + uint32_t i_address, + uint64_t i_data ) +{ + /* Scoms to the master chip are done via xscom */ + if( i_chip.isMaster ) + { + xscom_write( i_address, i_data ); + return; + } + + /*1) write the two data regs */ + putfsi( i_chip, DATA0_REG, i_data >> 32 ); + putfsi( i_chip, DATA1_REG, (uint32_t)i_data ); + + /*2) send the command to do the scom write */ + putfsi( i_chip, COMMAND_REG, i_address | 0x80000000 ); + + /*3) check status next -- TODO */ +} + + +/** + * @brief Execute standard getscom and return result + */ +int32_t SCOM_getScom( SCOM_Trgt_t i_trgt, uint32_t i_addr, uint64_t * o_val ) +{ + int32_t rc = SUCCESS; + + /* Translate the input args into physical addr/chip */ + uint32_t real_addr = translate_scom( i_trgt, i_addr ); + SCOM_Trgt_t chip_targ = SCOM_Trgt_getParentChip(i_trgt); + + /* Do the scom */ + *o_val = getscomraw( chip_targ, real_addr ); + if ( SCOMFAIL == *o_val ) { rc = FAIL; } + + return rc; +} + +/** + * @brief Execute indirect getscom and return result + */ +int32_t SCOM_getIdScom( SCOM_Trgt_t i_trgt, uint64_t i_addr, uint32_t * o_val ) +{ + int32_t rc = SUCCESS; + + /* First translate the address */ + uint64_t trans_addr = translate_scom( i_trgt, i_addr ); + SCOM_Trgt_t chip_targ = SCOM_Trgt_getParentChip(i_trgt); + + /* An indirect scom is performed by putting the top of the */ + /* 64-bit address into the first data word of the scom */ + + /* zero out the indirect address from the buffer.. */ + /* bit 0-31 - indirect area.. */ + /* bit 32 - always 0 */ + /* bit 33-47 - bcast/chipletID/port */ + /* bit 48-63 - local addr */ + uint32_t phys_addr = trans_addr & 0x000000007FFFFFFF; + + /* To do a read we need to do a write first */ + + /* start with the 20bit indirect address */ + uint64_t data_buffer = trans_addr & 0x001FFFFF00000000; + /* turn the read bit on. */ + data_buffer |= 0x8000000000000000; + + /* perform write before the read with the new */ + putscomraw( i_trgt, phys_addr, data_buffer ); + + // Loop on read until we see done, error, or we timeout + IndirectScom_t scomout; + uint64_t elapsed_indScom_time_ns = 0; + do + { + /* Now perform the op requested using the passed in */ + /* IO_Buffer to pass the read data back to caller. */ + scomout.data64 = getscomraw( chip_targ, phys_addr ); + if( scomout.data64 == SCOMFAIL ) + { + break; + } + + /* jump out on done or error */ + if( scomout.done || scomout.piberr ) + { + break; + } + + sleep( 10000 ); /*sleep for 10,000 ns */ + elapsed_indScom_time_ns += 10000; + + } while( elapsed_indScom_time_ns <= 100000 ); /*wait for .1ms */ + + if( (scomout.data64 == SCOMFAIL) + || (scomout.piberr) + || !(scomout.done) ) + { + *o_val = SCOMFAIL; + } + else + { + *o_val = scomout.data; + } + + if ( SCOMFAIL == *o_val ) { rc = FAIL; } + + return rc; +} diff --git a/src/occ/firdata/scom_util.h b/src/occ/firdata/scom_util.h new file mode 100644 index 0000000..039ac64 --- /dev/null +++ b/src/occ/firdata/scom_util.h @@ -0,0 +1,49 @@ +/* IBM_PROLOG_BEGIN_TAG */ +/* This is an automatically generated prolog. */ +/* */ +/* $Source: src/occ/firdata/scom_util.H $ */ +/* */ +/* OpenPOWER OnChipController Project */ +/* */ +/* Contributors Listed Below - COPYRIGHT 2015 */ +/* [+] International Business Machines Corp. */ +/* */ +/* */ +/* Licensed under the Apache License, Version 2.0 (the "License"); */ +/* you may not use this file except in compliance with the License. */ +/* You may obtain a copy of the License at */ +/* */ +/* http://www.apache.org/licenses/LICENSE-2.0 */ +/* */ +/* Unless required by applicable law or agreed to in writing, software */ +/* distributed under the License is distributed on an "AS IS" BASIS, */ +/* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or */ +/* implied. See the License for the specific language governing */ +/* permissions and limitations under the License. */ +/* */ +/* IBM_PROLOG_END_TAG */ + +/* Interfaces to read SCOM registers */ + +/*#include <stdint.h> */ +#include <native.h> +#include <scom_trgt.h> + +#define SCOMFAIL 0xDEADBEEF + +/** @brief Performs a hardware scom on a regular register. + * @param i_trgt The SCOM target. + * @param i_addr 32-bit SCOM address. + * @param o_val 64-bit returned value. + * @return Non-SUCCESS if the SCOM fails. SUCCESS otherwise. + */ +int32_t SCOM_getScom( SCOM_Trgt_t i_trgt, uint32_t i_addr, uint64_t * o_val ); + +/** @brief Performs a hardware scom on an indirect-SCOM register. + * @param i_trgt The SCOM target. + * @param i_addr 64-bit SCOM address. + * @param o_val 32-bit returned value. + * @return Non-SUCCESS if the SCOM fails. SUCCESS otherwise. + */ +int32_t SCOM_getIdScom( SCOM_Trgt_t i_trgt, uint64_t i_addr, uint32_t * o_val ); + diff --git a/src/occ/firdata/sfc_ast2400.c b/src/occ/firdata/sfc_ast2400.c new file mode 100644 index 0000000..fbdb0b3 --- /dev/null +++ b/src/occ/firdata/sfc_ast2400.c @@ -0,0 +1,728 @@ +/* IBM_PROLOG_BEGIN_TAG */ +/* This is an automatically generated prolog. */ +/* */ +/* $Source: src/occ/firdata/sfc_ast2400.C $ */ +/* */ +/* OpenPOWER OnChipController Project */ +/* */ +/* Contributors Listed Below - COPYRIGHT 2015 */ +/* [+] International Business Machines Corp. */ +/* */ +/* */ +/* Licensed under the Apache License, Version 2.0 (the "License"); */ +/* you may not use this file except in compliance with the License. */ +/* You may obtain a copy of the License at */ +/* */ +/* http://www.apache.org/licenses/LICENSE-2.0 */ +/* */ +/* Unless required by applicable law or agreed to in writing, software */ +/* distributed under the License is distributed on an "AS IS" BASIS, */ +/* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or */ +/* implied. See the License for the specific language governing */ +/* permissions and limitations under the License. */ +/* */ +/* IBM_PROLOG_END_TAG */ + +/*****************************************************************************/ +/* I n c l u d e s */ +/*****************************************************************************/ +#include <native.h> +#include <norflash.h> +/*#include <string.h> */ +#include <sfc_ast2400.h> +#include <lpc.h> +extern int TRACE_LPC; +int TRACE_SFC = 0; + +/*****************************************************************************/ +/* C o n s t a n t s */ +/*****************************************************************************/ + + + + +/*****************************************************************************/ +/* G l o b a l s */ +/*****************************************************************************/ + + +/*****************************************************************************/ +/* M e t h o d s */ +/*****************************************************************************/ + +/** + * @brief Read data from the flash + */ +errorHndl_t readFlash( Sfc_t* i_sfc, + uint32_t i_addr, + size_t i_size, + void* o_data ) +{ + /*TRACDCOMP( ENTER_MRK"readFlash> i_addr=0x%.8x, i_size=0x%.8x", i_addr, i_size ); */ + errorHndl_t l_err = 0; + + do{ + uint32_t* word_ptr = (uint32_t*)(o_data); + uint32_t word_size = (ALIGN_4(i_size))/4; + uint32_t words_read = 0; + for( words_read = 0; + words_read < word_size; + words_read ++ ) + { + /*Read directly from MMIO space */ + uint32_t lpc_addr = i_sfc->iv_mmioOffset | (i_addr + words_read*4); + size_t reg_size = sizeof(uint32_t); + l_err = lpc_read( LPC_TRANS_FW, + lpc_addr, + (uint8_t*)&(word_ptr[words_read]), + reg_size ); + if( l_err ) { break; } + } + if( l_err ) { break; } + }while(0); + + /*TRACDCOMP( EXIT_MRK"readFlash> err=%.8X", ERRL_GETEID_SAFE(l_err) ); */ + return l_err; +} + + +/** + * @brief Write data into flash + */ +errorHndl_t writeFlash( Sfc_t* i_sfc, + uint32_t i_addr, + size_t i_size, + void* i_data ) +{ + TRACFCOMP( "writeFlash> i_addr=0x%.8x, i_size=0x%.8x", i_addr, i_size ); + errorHndl_t l_err = 0; + size_t l_bytes_left = i_size; + size_t l_bytes_to_write = 0; + uint32_t l_addr_to_write = i_addr; + uint8_t* l_dataptr = (uint8_t*)(i_data); + + do { + /* Enable write mode */ + l_err = enableWriteMode(i_sfc); + if( l_err ) { break; } + + /* Page Program (PP) command only supports 256 bytes at a time */ + if( l_bytes_left <= PAGE_PROGRAM_BYTES ) + { + l_bytes_to_write = l_bytes_left; + l_bytes_left = 0; + } + else + { + l_bytes_to_write = PAGE_PROGRAM_BYTES; + l_bytes_left -= PAGE_PROGRAM_BYTES; + } + + /* Send in the Page Program command with the data to write */ + uint8_t opcode = SPI_JEDEC_PAGE_PROGRAM; + l_err = sendSpiCmd( i_sfc, opcode, l_addr_to_write, + l_bytes_to_write, + l_dataptr, + 0, NULL ); + if( l_err ) { break; } + + /* Move to the next chunk */ + l_addr_to_write += l_bytes_to_write; + l_dataptr += l_bytes_to_write; + + /* Wait for idle */ + l_err = pollOpComplete(i_sfc); + if( l_err ) { break; } + + /*check for special Micron Flag Status reg */ + if(i_sfc->iv_flashWorkarounds & HWWK_MICRON_WRT_ERASE) + { + l_err = micronFlagStatus(i_sfc); + if(l_err) { break; } + } + + } while(l_bytes_left); + + /*TRACDCOMP( EXIT_MRK"writeFlash> err=%.8X", ERRL_GETEID_SAFE(l_err) ); */ + return l_err; +} + +/** + * @brief Erase a block of flash + */ +errorHndl_t eraseFlash( Sfc_t* i_sfc, + uint32_t i_addr ) +{ + TRACFCOMP(">>eraseFlash> Block 0x%.8X", i_addr ); + errorHndl_t l_err = 0; + + do { + // Enable write mode + l_err = enableWriteMode(i_sfc); + if( l_err ) { break; } + + // Send erase command + uint8_t opcode = SPI_JEDEC_SECTOR_ERASE; + l_err = sendSpiCmd( i_sfc, opcode, i_addr, 0, 0, 0, NULL ); + if( l_err ) { break; } + + // Wait for idle + l_err = pollOpComplete(i_sfc); + if( l_err ) { break; } + + //check for special Micron Flag Status reg + if(i_sfc->iv_flashWorkarounds & HWWK_MICRON_WRT_ERASE) + { + l_err = micronFlagStatus(i_sfc); + if(l_err) { break; } + } + } while(0); + + return l_err; +} + + +/** + * @brief Initialize and configure the SFC hardware + */ +errorHndl_t hwInit( Sfc_t* i_sfc ) +{ + TRACFCOMP( ENTER_MRK"hwInit>" ); + errorHndl_t l_err = NO_ERROR; + + do { + size_t reg_size = sizeof(uint8_t); + + /*** Initialize the LPC2AHB logic */ + + /* Send SuperIO password - send A5 twice */ + uint8_t data = 0xA5; + l_err = lpc_write( LPC_TRANS_IO, SIO_ADDR_2E, + &data, reg_size ); + if( l_err ) { break; } + + l_err = lpc_write( LPC_TRANS_IO, SIO_ADDR_2E, + &data, reg_size ); + if( l_err ) { break; } + + + /* Select logical device D (iLPC2AHB) */ + l_err = writeRegSIO( i_sfc, 0x07, 0x0D ); + if( l_err ) { break; } + + + /* Enable iLPC->AHB */ + l_err = writeRegSIO( i_sfc, 0x30, 0x01 ); + if( l_err ) { break; } + + + /*** Setup the SPI Controller */ + + /* Enable writing to the controller */ + SpiControlReg04_t ctlreg; + l_err = readRegSPIC( i_sfc, CTLREG_04, &ctlreg.data32 ); + if( l_err ) { break; } + TRACFCOMP("dc99> First read of CTLREG_04=%.8X", ctlreg.data32); + ctlreg.cmdMode = 2;/*0b10; //10:Normal Write (CMD + Address + Write data) */ + l_err = writeRegSPIC( i_sfc, CTLREG_04, ctlreg.data32 ); + if( l_err ) { break; } + + SpiConfigReg00_t confreg; + l_err = readRegSPIC( i_sfc, CONFREG_00, &confreg.data32 ); + if( l_err ) { break; } + confreg.inactiveX2mode = 1; /*Enable CE# Inactive pulse width X2 mode */ + confreg.enableWrite = 1; /*Enable flash memory write */ + l_err = writeRegSPIC( i_sfc, CONFREG_00, confreg.data32 ); + if( l_err ) { break; } + + + /* + * Setup control reg and for our use, switching + * to 1-bit mode, clearing user mode if set, etc... + * + * Also configure SPI clock to something safe + * like HCLK/8 (24Mhz) + */ + ctlreg.fourByteMode = 1; + ctlreg.ioMode = 0;/*0b00; //single bit or controlled by bit[3] */ + ctlreg.pulseWidth = 0x0; /*0000: 16T (1T = 1 HCLK clock) */ + ctlreg.cmdData = 0x00; + ctlreg.spiClkFreq = 0x4; /*HCLK/8 */ + ctlreg.dummyCycleRead1 = 0; /*no dummy cycles */ + ctlreg.dummyCycleRead2 = 0;/*0b00; //no dummy cycles */ + ctlreg.cmdMode = 0;/*0b00; //00:Normal Read (03h + Address + Read data) */ + i_sfc->iv_ctlRegDefault = ctlreg; /* Default setup is regular read mode */ + + /* Configure for read */ + l_err = writeRegSPIC( i_sfc, CTLREG_04, ctlreg.data32 ); + if( l_err ) { break; } + + /* Setup flash-specific settings here, if there are any */ + + } while(0); + + TRACFCOMP( "hwInit> err=%.8X", l_err ); + return l_err; +} + +/** + * @brief Send a SPI command + */ +errorHndl_t sendSpiCmd( Sfc_t* i_sfc, + uint8_t i_opCode, + uint32_t i_address, + size_t i_writeCnt, + const uint8_t* i_writeData, + size_t i_readCnt, + uint8_t* o_readData ) +{ + errorHndl_t l_err = NO_ERROR; + size_t opsize = 0; + if(TRACE_SFC){ + TRACFCOMP( ENTER_MRK"sendSpiCmd> i_opCode=%.2X, i_address=%.8X, i_writeCnt=0x%X, i_writeData=%p", i_opCode, i_address, i_writeCnt, i_writeData ); + TRACFCOMP( " , i_readCnt=0x%X, o_readData=%p", i_readCnt, o_readData ); + } + + do { + /*Do a read of flash address zero to workaround */ + /* a micron bug with extended reads */ + if( (HWWK_MICRON_EXT_READ & i_sfc->iv_flashWorkarounds) + && (i_readCnt > 4) ) + { + uint32_t ignored = 0; + l_err = readFlash( i_sfc, 0, 1, &ignored ); + if(l_err) { break; } + } + + /* Put controller into command mode (instead of read mode) */ + l_err = commandMode( i_sfc, true ); + if( l_err ) { break; } + + /* Write command to the beginning of the flash space */ + opsize = sizeof(i_opCode); + l_err = lpc_write( LPC_TRANS_FW, i_sfc->iv_mmioOffset, + &i_opCode, + opsize ); /*just send opcode */ + if( l_err ) { break; } + + /* Send address if there is one */ + if( i_address != NO_ADDRESS ) + { + /* Write address to the beginning of the flash space */ + opsize = sizeof(i_address); + l_err = lpc_write( LPC_TRANS_FW, i_sfc->iv_mmioOffset, + (uint8_t*)&i_address, + opsize ); /*only supporting 4-byte addresses */ + if( l_err ) { break; } + } + + /* Send in the rest of the write data */ + if( i_writeCnt && i_writeData ) + { + size_t bytes_left = i_writeCnt; + uint8_t* curptr = (uint8_t*)(i_writeData); + while( bytes_left ) + { + /* Write the last partial word if there is one */ + if( bytes_left < 4 ) + { + opsize = bytes_left; + l_err = lpc_write( LPC_TRANS_FW, i_sfc->iv_mmioOffset, + curptr, + opsize ); + break; + } + + /* Write data into the beginning of the flash space, */ + /* in command mode this doesn't write the flash */ + /* but instead is a pass-through to the area we */ + /* really want to write */ + opsize = sizeof(uint32_t); + l_err = lpc_write( LPC_TRANS_FW, i_sfc->iv_mmioOffset, + curptr, + opsize ); + if( l_err ) { break; } + + curptr += 4; + bytes_left -= 4; + } + if( l_err ) { break; } + } + + /* Read back the results */ + if( i_readCnt && o_readData ) + { + size_t bytes_left = i_readCnt; + uint8_t* curptr = o_readData; + while( bytes_left ) + { + /* Grab the last partial word if there is one */ + if( bytes_left < 4 ) + { + opsize = bytes_left; + l_err = lpc_read( LPC_TRANS_FW, i_sfc->iv_mmioOffset, + curptr, + opsize ); + break; + } + + /* Read data from the beginning of the flash space, */ + /* in command mode this doesn't read the flash */ + /* but instead is a pass-through to the data we */ + /* really want */ + opsize = sizeof(uint32_t); + l_err = lpc_read( LPC_TRANS_FW, i_sfc->iv_mmioOffset, + curptr, + opsize ); + if( l_err ) { break; } + + curptr += 4; + bytes_left -= 4; + } + if( l_err ) { break; } + } + } while(0); + + + /* No matter what, put the logic back into read mode */ + int tmp_err = commandMode( i_sfc, false ); + if( tmp_err ) + { + if( !l_err ) + { + l_err = tmp_err; + } + } + + /*TRACDCOMP( EXIT_MRK"sendSpiCmd> o_readData=%.2X, err=%.8X", o_readData == NULL ? 0 : o_readData[0], ERRL_GETEID_SAFE(l_err) ); */ + return l_err; +} + +/** + * @brief Enter/exit command mode + */ +errorHndl_t commandMode( Sfc_t* i_sfc, + bool i_enter ) +{ + errorHndl_t l_err = NO_ERROR; + /*TRACDCOMP( ENTER_MRK"commandMode(%d)", i_enter ); */ + + /* + * There is only a limited addressable window within LPC space. The AST + * has its control register space at too far of a distance from the read + * space for them both to fit in a single window. Rather than moving the + * window around we will use the iLPC2AHB backdoor inside the SuperIO + * controller to do both register accesses and to write into the flash. + * + * High level flow to write into control space: + * Stop active control (SPI04 Control Reg) + * Enable command mode (SPI04 Control Reg) + * Write actual command into flash base addr (0x0E000000) + */ + + do { + SpiControlReg04_t ctlreg = i_sfc->iv_ctlRegDefault; + + /* Switch to user mode, CE# dropped */ + ctlreg.stopActiveCtl = 1; + ctlreg.cmdMode = 3;/*0b11; //User Mode (Read/Write Data) */ + l_err = writeRegSPIC( i_sfc, CTLREG_04, ctlreg.data32 ); + if( l_err ) { break; } + + if( i_enter ) /*ast_sf_start_cmd */ + { + /* user mode, CE# active */ + ctlreg.stopActiveCtl = 0; + l_err = writeRegSPIC( i_sfc, CTLREG_04, ctlreg.data32 ); + if( l_err ) { break; } + } + else /*ast_sf_end_cmd */ + { + /* Switch back to read mode */ + l_err = writeRegSPIC( i_sfc, CTLREG_04, i_sfc->iv_ctlRegDefault.data32 ); + if( l_err ) { break; } + } + } while(0); + + /*TRACDCOMP( EXIT_MRK"commandMode> err=%.8X", ERRL_GETEID_SAFE(l_err) ); */ + return l_err; +} + +/** + * @brief Enable write mode + */ +errorHndl_t enableWriteMode( Sfc_t* i_sfc ) +{ + errorHndl_t l_err = NO_ERROR; + /*TRACDCOMP( ENTER_MRK"enableWriteMode>" ); */ + + /* Some flashes need it to be hammered */ + NorStatusReg_t status; +// status.data8 = 0x55; + size_t i = 0; + for( i = 0; i < 10; i++ ) + { + /* Send the command to enable writes */ + uint8_t opcode = SPI_JEDEC_WRITE_ENABLE; + l_err = sendSpiCmd( i_sfc, opcode, NO_ADDRESS, 0, NULL, 0, NULL ); + if( l_err ) { break; } + + /* Check to see if it worked */ + opcode = SPI_JEDEC_READ_STATUS; + l_err = sendSpiCmd( i_sfc, opcode, NO_ADDRESS, 0, NULL, 1, &(status.data8) ); + if( l_err ) { break; } + TRACDCOMP( "SPI_JEDEC_READ_STATUS=%.2X", status.data8 ); + + if( status.writeEnable ) + { + break; + } + } + + if( !l_err && !status.writeEnable ) + { + TRACFCOMP( "Could not enable writes" ); + l_err = -1; + } + + /*TRACDCOMP( EXIT_MRK"enableWriteMode> err=%.8X", ERRL_GETEID_SAFE(l_err) ); */ + return l_err; +} + +/** + * @brief Write a single byte into the SIO + */ +errorHndl_t writeRegSIO( Sfc_t* i_sfc, + uint8_t i_regAddr, + uint8_t i_data ) +{ /*lpc_sio_outb */ + errorHndl_t l_err = NO_ERROR; + /*TRACFCOMP( "writeRegSIO> i_regAddr=0x%.2X, i_data=0x%.2X", i_regAddr, i_data );*/ + + do { + size_t reg_size = sizeof(uint8_t); + + /* AST2400 integrates a Super I/O module with */ + /* LPC protocol (I/O cycle 0x2E/0x2F) */ + + /* Write out the register address */ + l_err = lpc_write( LPC_TRANS_IO, SIO_ADDR_2E, + &i_regAddr, + reg_size ); + if( l_err ) { break; } + + /* Write out the register data */ + l_err = lpc_write( LPC_TRANS_IO, SIO_DATA_2F, + &i_data, + reg_size ); + if( l_err ) { break; } + + } while(0); + + /*TRACDCOMP( EXIT_MRK"writeRegSIO> err=%.8X", ERRL_GETEID_SAFE(l_err) ); */ + return l_err; +} + +/** + * @brief Read a single byte from the SIO + */ +errorHndl_t readRegSIO( Sfc_t* i_sfc, + uint8_t i_regAddr, + uint8_t* o_data ) +{ + errorHndl_t l_err = NO_ERROR; + /*TRACDCOMP( ENTER_MRK"readRegSIO> i_regAddr=0x%.2X", i_regAddr ); */ + + do { + size_t reg_size = sizeof(uint8_t); + + /* AST2400 integrates a Super I/O module with */ + /* LPC protocol (I/O cycle 0x2E/0x2F) */ + + /* Write out the register address */ + l_err = lpc_write( LPC_TRANS_IO, SIO_ADDR_2E, + &i_regAddr, + reg_size ); + if( l_err ) { break; } + + /* Read in the register data */ + l_err = lpc_read( LPC_TRANS_IO, SIO_DATA_2F, + o_data, + reg_size ); + if( l_err ) { break; } + + } while(0); + + /*TRACDCOMP( EXIT_MRK"readRegSIO> o_data=0x%.2X, err-%.8X", *o_data, ERRL_GETEID_SAFE(l_err) ); */ + return l_err; +} + +/** + * @brief Prepare the iLPC2AHB address regs + */ +errorHndl_t setupAddrLPC2AHB( Sfc_t* i_sfc, + uint32_t i_addr ) +{ + errorHndl_t l_err = NO_ERROR; + /*TRACDCOMP( ENTER_MRK"setupAddrLPC2AHB> i_addr=0x%X", i_addr ); */ + + do { + /* Select logical device D (iLPC2AHB) */ + l_err = writeRegSIO( i_sfc, 0x07, 0x0D ); + if( l_err ) { break; } + + /* Push 4 address bytes into SIO regs 0xF0-0xF3 */ + size_t i; + for( i=sizeof(i_addr); i>0; i-- ) + { + l_err = writeRegSIO( i_sfc, 0xF3-(i-1), /*F0,F1,F2,F3 */ + (uint8_t)(i_addr >> ((i-1)*8)) ); + if( l_err ) { break; } + } + if( l_err ) { break; } + + /* Configure 4 byte length */ + l_err = writeRegSIO( i_sfc, 0xF8, 0x02 ); + if( l_err ) { break; } + + } while(0); + + /*TRACDCOMP( EXIT_MRK"setupAddrLPC2AHB> err=%.8X", ERRL_GETEID_SAFE(l_err) ); */ + return l_err; +} + +/** + * @brief Write SPI Controller Register + */ +errorHndl_t writeRegSPIC( Sfc_t* i_sfc, + SpicReg_t i_reg, + uint32_t i_data ) +{ + errorHndl_t l_err = NO_ERROR; + //TRACFCOMP( "writeRegSPIC> i_reg=0x%.2X, i_data=0x%.8X", i_reg, i_data ); + + do { + /* Compute the full LPC address */ + uint32_t lpc_addr = i_reg | SPIC_BASE_ADDR_AHB; + + /* Setup the logic for the write */ + l_err = setupAddrLPC2AHB( i_sfc, lpc_addr ); + if( l_err ) { break; } + + /* Push 4 data bytes into SIO regs 0xF4-0xF7 */ + uint8_t* ptr8 = (uint8_t*)(&i_data); + size_t i; + for( i=0; i<sizeof(i_data); i++ ) + { + l_err = writeRegSIO( i_sfc, 0xF4+i, /*F4,F5,F6,F7 */ + ptr8[i] ); + if( l_err ) { break; } + } + if( l_err ) { break; } + + /* Trigger the write operation by writing the magic 0xCF value */ + l_err = writeRegSIO( i_sfc, 0xFE, 0xCF ); + if( l_err ) { break; } + + } while(0); + + /*TRACDCOMP( EXIT_MRK"writeRegSPIC> err=%.8X", ERRL_GETEID_SAFE(l_err) ); */ + return l_err; +} + +/** + * @brief Read SPI Controller Register + */ +errorHndl_t readRegSPIC( Sfc_t* i_sfc, + SpicReg_t i_reg, + uint32_t* o_data ) +{ + //TRACFCOMP( "readRegSPIC> i_reg=0x%.2X", i_reg ); + errorHndl_t l_err = NO_ERROR; + + do { + /* Compute the full LPC address */ + uint32_t lpc_addr = i_reg | SPIC_BASE_ADDR_AHB; + + /* Setup the logic for the write */ + l_err = setupAddrLPC2AHB( i_sfc, lpc_addr ); + if( l_err ) { break; } + + /* Trigger the write operation by reading the magic register */ + uint8_t ignored = 0; + l_err = readRegSIO( i_sfc, 0xFE, &ignored ); + if( l_err ) { break; } + + /* Read 4 data bytes into SIO regs 0xF4-0xF7 */ + uint8_t* ptr8 = (uint8_t*)(o_data); + size_t i; + for( i=0; i<sizeof(*o_data); i++ ) + { + l_err = readRegSIO( i_sfc, 0xF4+i, /*F4,F5,F6,F7 */ + &(ptr8[i]) ); + if( l_err ) { break; } + } + if( l_err ) { break; } + + } while(0); + + /*TRACDCOMP( EXIT_MRK"readRegSPIC> o_data=0x%.8X, l_err=%.8X", *o_data, ERRL_GETEID_SAFE(l_err) ); */ + return l_err; +} + +/** + * @brief Poll for completion of SPI operation + */ +errorHndl_t pollOpComplete( Sfc_t* i_sfc ) +{ + errorHndl_t l_err = NO_ERROR; + /* TRACDCOMP( "pollOpComplete>" ); */ +int trace_lpc=TRACE_LPC; TRACE_LPC=0; +int trace_sfc=TRACE_SFC; TRACE_SFC=0; + + do { + /* Send RDSR command until write-in-progress clears */ + NorStatusReg_t status; + uint64_t poll_time = 0; + uint64_t loop = 0; + while( poll_time < MAX_WRITE_TIME_NS ) + { + uint8_t opcode = SPI_JEDEC_READ_STATUS; + l_err = sendSpiCmd( i_sfc, opcode, + NO_ADDRESS, + 0, NULL, + 1, &(status.data8) ); + if( l_err ) { break; } + + /* check if any op is still going */ + if( !status.writeInProgress ) + { + break; + } + + /* want to start out incrementing by small numbers then get bigger + to avoid a really tight loop in an error case so we'll increase + the wait each time through */ + ++loop; + sleep( 100*loop ); + poll_time += 100*loop; + } + if( l_err ) { break; } + + /*TRACDCOMP(g_trac_pnor,"pollOpComplete> command took %d ns", poll_time); */ + + /* No status regs to check so just look for timeout */ + if( status.writeInProgress ) + { + TRACFCOMP( "pollOpComplete> Timeout during write or erase" ); + l_err = -1; + break; + } + } while(0); + +TRACE_LPC=trace_lpc; +TRACE_SFC=trace_sfc; + /*TRACDCOMP( EXIT_MRK"pollOpComplete> err=%.8X", ERRL_GETEID_SAFE(l_err) ); */ + return l_err; +} + diff --git a/src/occ/firdata/sfc_ast2400.h b/src/occ/firdata/sfc_ast2400.h new file mode 100644 index 0000000..30a9052 --- /dev/null +++ b/src/occ/firdata/sfc_ast2400.h @@ -0,0 +1,298 @@ +/* IBM_PROLOG_BEGIN_TAG */ +/* This is an automatically generated prolog. */ +/* */ +/* $Source: src/occ/firdata/sfc_ast2400.H $ */ +/* */ +/* OpenPOWER OnChipController Project */ +/* */ +/* Contributors Listed Below - COPYRIGHT 2015 */ +/* [+] International Business Machines Corp. */ +/* */ +/* */ +/* Licensed under the Apache License, Version 2.0 (the "License"); */ +/* you may not use this file except in compliance with the License. */ +/* You may obtain a copy of the License at */ +/* */ +/* http://www.apache.org/licenses/LICENSE-2.0 */ +/* */ +/* Unless required by applicable law or agreed to in writing, software */ +/* distributed under the License is distributed on an "AS IS" BASIS, */ +/* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or */ +/* implied. See the License for the specific language governing */ +/* permissions and limitations under the License. */ +/* */ +/* IBM_PROLOG_END_TAG */ +#ifndef __PNOR_SFCAST2400_H +#define __PNOR_SFCAST2400_H + +#include <native.h> +#include <homerData_common.h> + +/** @file sfc_ast2400.H + * @brief Provides the logic to access and configure the + * AST2400 BMC in order to access the PNOR + */ + +/** + * @brief SPI04 Control Register + */ +typedef union +{ + uint32_t data32; + struct + { /*Little-endian bit positions*/ + uint32_t rsvd : 2; /*31:30*/ + uint32_t ioMode : 2; /*29:28*/ + uint32_t pulseWidth : 4; /*27:24*/ + uint32_t cmdData : 8; /*23:16*/ + uint32_t dummyCycleCmd : 1; /*15*/ + uint32_t dummyCycleRead1 : 1; /*14*/ + uint32_t fourByteMode : 1; /*13*/ + uint32_t disableCmdMerge : 1; /*12*/ + uint32_t spiClkFreq : 4; /*11:8*/ + uint32_t dummyCycleRead2 : 2; /*7:6*/ + uint32_t lsbFirst : 1; /*5*/ + uint32_t useClkMode3 : 1; /*4*/ + uint32_t dualInputMode : 1; /*3*/ + uint32_t stopActiveCtl : 1; /*2*/ + uint32_t cmdMode : 2; /*1:0*/ + }; +} SpiControlReg04_t; + + +/** + * @brief Structure to hold information about the SFC + */ +typedef struct +{ + /** + * @brief Default value of SPI04 (saves a read) + */ + SpiControlReg04_t iv_ctlRegDefault; + + /** + * @brief Hardware workarounds + */ + uint32_t iv_flashWorkarounds; + + /** + * @brief LPC address that marks the beginning of flash + */ + uint32_t iv_mmioOffset; +} Sfc_t; + + +/** + * @brief Initialize the SFC Hardware + * @param[in] Pointer to Sfc struct + * + * @return void + */ +errorHndl_t hwInit(Sfc_t* i_sfc); + +/** + * @brief Read data from the PNOR flash + * + * @param[in] Pointer to Sfc struct + * @parm[in] i_addr PNOR flash Address to read + * @parm[in] i_size Amount of data to read, in bytes. + * @parm[out] o_data Buffer to read data into + * + * @return Error from operation + */ +errorHndl_t readFlash(Sfc_t* i_sfc, + uint32_t i_addr, + size_t i_size, + void* o_data); + +/** + * @brief Write data to the PNOR flash + * + * @param[in] Pointer to Sfc struct + * @parm i_addr PNOR flash Address to write + * @parm i_size Amount of data to write, in bytes. + * @parm i_data Buffer containing data to write + * + * @return Error from operation + */ +errorHndl_t writeFlash(Sfc_t* i_sfc, + uint32_t i_addr, + size_t i_size, + void* i_data); + +/** + * @brief Erase a block of flash + */ +errorHndl_t eraseFlash( Sfc_t* i_sfc, + uint32_t i_addr ); + +/** @brief Constant for sendSpiCmd parameter */ +static const uint32_t NO_ADDRESS = UINT32_MAX; + + +/** + * @brief Send a SPI command + * + * @param[in] Pointer to Sfc struct + * @parm[in] i_opCode: command to send into controller first + * @parm[in] i_address: address for those commands that need it + * @parm[in] i_writeCnt: number of bytes to write to device + * @parm[in] i_writeData: write data buffer + * @parm[in] i_readCnt: number of bytes to read from device + * @parm[out] o_readData: read data buffer + * + * @return Error from operation + */ +errorHndl_t sendSpiCmd( Sfc_t* i_sfc, + uint8_t i_opCode, + uint32_t i_address, + size_t i_writeCnt, + const uint8_t* i_writeData, + size_t i_readCnt, + uint8_t* o_readData ); + +/** + * @brief List of registers in the SPI Controller logic + */ +typedef enum +{ + CONFREG_00 = 0x00, + CTLREG_04 = 0x04, + MISCCTLREG_10 = 0x10, + READTIMEREG_14 = 0x14 +} SpicReg_t; + +/** + * @brief Write a SPI Controller register + * + * @param[in] Pointer to Sfc struct + * @param[in] i_reg: Register to write + * @param[in] i_data: Data to write + * + * @return Error from operation + */ +errorHndl_t writeRegSPIC( Sfc_t* i_sfc, + SpicReg_t i_reg, + uint32_t i_data ); + +/** + * @brief Write a SPI Controller register + * + * @param[in] Pointer to Sfc struct + * @param[in] i_reg: Register to write + * @param[in] o_data: Data that was read + * + * @return Error from operation + */ +errorHndl_t readRegSPIC( Sfc_t* i_sfc, + SpicReg_t i_reg, + uint32_t* o_data ); + +/** + * @brief Write a single byte into a SIO register + * + * @param[in] Pointer to Sfc struct + * @param[in] i_reg: Register to write + * @param[in] i_data: Data to write + * + * @return Error from operation + */ +errorHndl_t writeRegSIO( Sfc_t* i_sfc, + uint8_t i_regAddr, + uint8_t i_data ); + +/** + * @brief Read a single byte from a SIO register + * + * @param[in] Pointer to Sfc struct + * @param[in] i_reg: Register to read + * @param[in] o_data: Data that was read + * + * @return Error from operation + */ +errorHndl_t readRegSIO( Sfc_t* i_sfc, + uint8_t i_regAddr, + uint8_t* o_data ); + +/** + * @brief Enable write mode + * + * @param[in] Pointer to Sfc struct + * @return Error from operation + */ +errorHndl_t enableWriteMode( Sfc_t* i_sfc ); + +/** + * @brief Enter/exit command mode + * + * @param[in] Pointer to Sfc struct + * @param[in] i_enter: true=enter cmd mode, false=exit cmd mode + * + * @return Error from operation + */ +errorHndl_t commandMode( Sfc_t* i_sfc, + bool i_enter ); + +/** + * @brief Poll for completion of SPI operation + * + * @param[in] Pointer to Sfc struct + * @return Error from operation + */ +errorHndl_t pollOpComplete( Sfc_t* i_sfc ); + +/** + * @brief Prepare the iLPC2AHB address regs + * + * @param[in] Pointer to Sfc struct + * @param[in] i_addr: LPC address to access + * + * @return Error from operation + */ +errorHndl_t setupAddrLPC2AHB( Sfc_t* i_sfc, + uint32_t i_addr ); + + +/** + * @brief SPI0 Configuration Register + */ +typedef union +{ + uint32_t data32; + struct + { /*Little-endian bit positions*/ + uint32_t rsvd : 30; /*31:2*/ + uint32_t inactiveX2mode : 1; /*1*/ + uint32_t enableWrite : 1; /*0*/ + }; +} SpiConfigReg00_t; + +/** @brief General Constants */ +enum +{ + LPC_TOP_OF_FLASH_OFFSET = 0x0FFFFFFF, + + /**< Offset to SPI Controller Register Space */ + LPC_SFC_CTLR_BASE = 0x1E789000, + + /**< AHB address of SPI Flash controller */ + SPIC_BASE_ADDR_AHB = 0x1E630000, + + /**< AHB address of flash */ + FLASH_BASE_ADDR_AHB = 0x30000000, + + /**< AHB address of LPC registers */ + LPC_CTLR_BASE_ADDR_AHB = 0x1E789000, + + /**< Maximum time to wait for a write/erase */ + MAX_WRITE_TIME_NS = NS_PER_SEC, + + /**< SuperIO Address Cycle */ + SIO_ADDR_2E = 0x2E, + + /**< SuperIO Data Cycle */ + SIO_DATA_2F = 0x2F, +}; + + +#endif diff --git a/src/occ/homer.c b/src/occ/homer.c index d7b2a3b..67ada05 100755 --- a/src/occ/homer.c +++ b/src/occ/homer.c @@ -5,10 +5,10 @@ /* */ /* OpenPOWER OnChipController Project */ /* */ -/* Contributors Listed Below - COPYRIGHT 2011,2014 */ -/* [+] Google Inc. */ +/* Contributors Listed Below - COPYRIGHT 2011,2015 */ /* [+] International Business Machines Corp. */ /* */ +/* */ /* Licensed under the Apache License, Version 2.0 (the "License"); */ /* you may not use this file except in compliance with the License. */ /* You may obtain a copy of the License at */ @@ -26,6 +26,9 @@ // Description: HOMER specific functions. #include "ssx.h" +#include <errl.h> +#include <occ_service_codes.h> +#include <occ_common.h> #include <homer.h> /* @@ -49,26 +52,22 @@ */ homer_rc_t homer_hd_map_read_unmap(const homer_read_var_t i_id, - uint32_t *o_host_data, - int *o_ssx_rc) + void * const o_host_data, + int * const o_ssx_rc) { - // Locals Ppc405MmuMap l_mmuMapHomer = 0; homer_rc_t l_rc = HOMER_SUCCESS; occHostConfigDataArea_t *l_hdcfg_data = 0x00000000; - *o_ssx_rc = SSX_OK; - // Validate the parms and id - if (!o_host_data) + // Validate the pointers + if (!o_host_data || !o_ssx_rc || ((uint32_t)o_host_data % 4)) { l_rc = HOMER_BAD_PARM; } - else if (HOMER_LAST_VAR <= i_id) - { - l_rc = HOMER_UNKNOWN_ID; - } else { + *o_ssx_rc = SSX_OK; + /* * Map to mainstore at HOMER host data offset. The first parameter is * the effective address where the data can be accessed once mapped, the @@ -88,31 +87,79 @@ homer_rc_t homer_hd_map_read_unmap(const homer_read_var_t i_id, } else { - // Check version, if ok return data requested. We need to support + // Check version, if ok handle ID requested. We need to support // current version as well as older ones - if (HOMER_HD_VERSION_SUPPORT < l_hdcfg_data->version) + if ((HOMER_VERSION_MIN > l_hdcfg_data->version) + || + (HOMER_VERSION_MAX < l_hdcfg_data->version)) { l_rc = HOMER_UNSUPPORTED_HD_VERSION; } else { - switch (i_id) + // Version guaranteed to be within supported range + + // HOMER Version 1 support + if (HOMER_VERSION_1 == l_hdcfg_data->version) + { + switch (i_id) + { + case HOMER_VERSION: + *(uint32_t *)o_host_data = l_hdcfg_data->version; + break; + case HOMER_NEST_FREQ: + *(uint32_t *)o_host_data = l_hdcfg_data->nestFrequency; + break; + default: + l_rc = HOMER_UNKNOWN_ID; + break; + } + } + else if (HOMER_VERSION_2 == l_hdcfg_data->version) { - case HOMER_VERSION: - *o_host_data = l_hdcfg_data->version; - break; - case HOMER_NEST_FREQ: - *o_host_data = l_hdcfg_data->nestFrequency; - break; - case HOMER_INT_TYPE: - *o_host_data = l_hdcfg_data->occInterruptType; - break; - default: - // Nothing to do, range checked above - break; + switch (i_id) + { + case HOMER_VERSION: + *(uint32_t *)o_host_data = l_hdcfg_data->version; + break; + case HOMER_NEST_FREQ: + *(uint32_t *)o_host_data = l_hdcfg_data->nestFrequency; + break; + case HOMER_INT_TYPE: + *(uint32_t *)o_host_data = l_hdcfg_data->occInterruptType; + break; + default: + l_rc = HOMER_UNKNOWN_ID; + break; + } + } + else if (HOMER_VERSION_3 == l_hdcfg_data->version) + { + switch (i_id) + { + case HOMER_VERSION: + *(uint32_t *)o_host_data = l_hdcfg_data->version; + break; + case HOMER_NEST_FREQ: + *(uint32_t *)o_host_data = l_hdcfg_data->nestFrequency; + break; + case HOMER_INT_TYPE: + *(uint32_t *)o_host_data = l_hdcfg_data->occInterruptType; + break; + case HOMER_FIR_MASTER: + *(uint32_t *)o_host_data = l_hdcfg_data->firMaster; + break; + case HOMER_FIR_PARMS: + memcpy(o_host_data, &(l_hdcfg_data->firParms[0]), HOMER_FIR_PARM_SIZE); + break; + default: + l_rc = HOMER_UNKNOWN_ID; + break; + } } } + // Unmap the HOMER before returning to caller *o_ssx_rc = ppc405_mmu_unmap(&l_mmuMapHomer); if ((SSX_OK != *o_ssx_rc) && (HOMER_SUCCESS == l_rc)) { @@ -125,4 +172,75 @@ homer_rc_t homer_hd_map_read_unmap(const homer_read_var_t i_id, } // End of homer_hd_map_read_unmap + +/* + * Function Specification + * + * Name: homer_log_access_error + * + * Description: Utility function to log an error that occurred while accessing + * the HOMER. + * + * End Function Specification + */ +void homer_log_access_error(const homer_rc_t i_homer_rc, + const int i_ssx_rc, + const uint32_t i_usr_data2) +{ + // Catch and log the homer error + if (HOMER_SUCCESS != i_homer_rc) + { + // We could potentially have both an internal error dealing with the + // homer and an SSX error, for example we could find an unsupported + // version number in the homer and then have an ssx error trying to + // unmap the homer address space. This check catches all those cases. + if (SSX_OK != i_ssx_rc) + { + /* @ + * @errortype + * @moduleid MAIN_MID + * @reasoncode SSX_GENERIC_FAILURE + * @userdata1 HOMER and SSX return codes + * @userdata2 Host interrupt type used + * @userdata4 ERC_HOMER_MAIN_SSX_ERROR + * @devdesc An SSX error occurred mapping the HOMER host data + * into the OCC address space. User word 1 contains + * both the internal and SSX return codes returned + * by the method used to access the HOMER data. + */ + errlHndl_t l_err = createErrl(MAIN_MID, //modId + SSX_GENERIC_FAILURE, //reasoncode + ERC_HOMER_MAIN_SSX_ERROR, //Extended reason code + ERRL_SEV_PREDICTIVE, //Severity + NULL, //Trace Buf + DEFAULT_TRACE_SIZE, //Trace Size + (i_homer_rc << 16) | (0xFFFF & (uint32_t)i_ssx_rc), //userdata1 + i_usr_data2); //userdata2 + commitErrl(&l_err); + } + else + { + /* @ + * @errortype + * @moduleid MAIN_MID + * @reasoncode INTERNAL_FAILURE + * @userdata1 HOMER return code + * @userdata2 Default host interrupt type used. + * @userdata4 ERC_HOMER_MAIN_ACCESS_ERROR + * @devdesc Error accessing initialization data + */ + errlHndl_t l_err = createErrl(MAIN_MID, //modId + INTERNAL_FAILURE, //reasoncode + ERC_HOMER_MAIN_ACCESS_ERROR,//Extended reason code + ERRL_SEV_INFORMATIONAL, //Severity + NULL, //Trace Buf + DEFAULT_TRACE_SIZE, //Trace Size + i_homer_rc, //userdata1 + i_usr_data2); //userdata2 + commitErrl(&l_err); + } + } +} +// End of homer_log_access_error + // End of homer.c diff --git a/src/occ/homer.h b/src/occ/homer.h index 4a0036d..b4d94ee 100755 --- a/src/occ/homer.h +++ b/src/occ/homer.h @@ -5,10 +5,10 @@ /* */ /* OpenPOWER OnChipController Project */ /* */ -/* Contributors Listed Below - COPYRIGHT 2011,2014 */ -/* [+] Google Inc. */ +/* Contributors Listed Below - COPYRIGHT 2011,2015 */ /* [+] International Business Machines Corp. */ /* */ +/* */ /* Licensed under the Apache License, Version 2.0 (the "License"); */ /* you may not use this file except in compliance with the License. */ /* You may obtain a copy of the License at */ @@ -32,9 +32,17 @@ // Offset into the HOMER of the host data section and the size #define HOMER_HD_OFFSET 0x00100000 #define HOMER_HD_SZ (128 * 1024) +#define HOMER_FIR_PARM_SIZE (3 * 1024) -// Version of HOMER host data currently supported -#define HOMER_HD_VERSION_SUPPORT 2 +// Version(s) of HOMER host data currently supported +typedef enum homer_version +{ + HOMER_VERSION_MIN = 1, + HOMER_VERSION_1 = 1, + HOMER_VERSION_2 = 2, + HOMER_VERSION_3 = 3, + HOMER_VERSION_MAX = 3, +} homer_version_t; // ID of host data variables typedef enum homer_read_var @@ -42,6 +50,8 @@ typedef enum homer_read_var HOMER_VERSION, HOMER_NEST_FREQ, HOMER_INT_TYPE, + HOMER_FIR_MASTER, + HOMER_FIR_PARMS, HOMER_LAST_VAR } homer_read_var_t; @@ -63,10 +73,13 @@ struct occHostConfigDataArea uint32_t version; uint32_t nestFrequency; uint32_t occInterruptType; - uint8_t __reserved[(1024*128)-12]; + uint32_t firMaster; + uint8_t firParms[HOMER_FIR_PARM_SIZE]; + uint8_t __reserved[HOMER_HD_SZ - (4 * sizeof(uint32_t)) - HOMER_FIR_PARM_SIZE]; }__attribute__ ((__packed__)); typedef struct occHostConfigDataArea occHostConfigDataArea_t; -homer_rc_t homer_hd_map_read_unmap(const homer_read_var_t, uint32_t *, int *); +homer_rc_t homer_hd_map_read_unmap(const homer_read_var_t, void * const, int * const); +void homer_log_access_error(const homer_rc_t, const int, const uint32_t); #endif // _homer_h diff --git a/src/occ/incl/common_types.h b/src/occ/incl/common_types.h index f13f99a..51ea3ed 100755 --- a/src/occ/incl/common_types.h +++ b/src/occ/incl/common_types.h @@ -5,10 +5,10 @@ /* */ /* OpenPOWER OnChipController Project */ /* */ -/* Contributors Listed Below - COPYRIGHT 2011,2014 */ -/* [+] Google Inc. */ +/* Contributors Listed Below - COPYRIGHT 2011,2015 */ /* [+] International Business Machines Corp. */ /* */ +/* */ /* Licensed under the Apache License, Version 2.0 (the "License"); */ /* you may not use this file except in compliance with the License. */ /* You may obtain a copy of the License at */ @@ -59,6 +59,9 @@ typedef enum } BOOLEAN; #endif +#define true 1 +#define false 0 + #ifndef NULL #define NULL (VOID *) 0 #endif diff --git a/src/occ/incl/occ_common.h b/src/occ/incl/occ_common.h index 0c28f89..032683f 100755 --- a/src/occ/incl/occ_common.h +++ b/src/occ/incl/occ_common.h @@ -5,10 +5,10 @@ /* */ /* OpenPOWER OnChipController Project */ /* */ -/* Contributors Listed Below - COPYRIGHT 2011,2014 */ -/* [+] Google Inc. */ +/* Contributors Listed Below - COPYRIGHT 2011,2015 */ /* [+] International Business Machines Corp. */ /* */ +/* */ /* Licensed under the Apache License, Version 2.0 (the "License"); */ /* you may not use this file except in compliance with the License. */ /* You may obtain a copy of the License at */ @@ -31,12 +31,11 @@ // From Linker Script extern void _LINEAR_WR_WINDOW_SECTION_BASE; -// From Linker Script extern void _LINEAR_WR_WINDOW_SECTION_SIZE; -// From Linker Script extern void _LINEAR_RD_WINDOW_SECTION_BASE; -// From Linker Script extern void _LINEAR_RD_WINDOW_SECTION_SIZE; +extern void _FIR_PARMS_SECTION_BASE; +extern void _FIR_HEAP_SECTION_BASE; // Declare aligned data structures for Async access in a noncacheable section // @@ -60,6 +59,12 @@ extern void _LINEAR_RD_WINDOW_SECTION_SIZE; #define GPE_BUFFER(declaration) \ declaration __attribute__ ((__aligned__ (8))) __attribute__ ((section (".noncacheable"))) +#define FIR_HEAP_BUFFER(declaration) \ + declaration __attribute__ ((section (".firHeap"))) + +#define FIR_PARMS_BUFFER(declaration) \ + declaration __attribute__ ((section (".firParms"))) + #define LINEAR_WINDOW_WR_BUFFER(declaration) \ declaration __attribute__ ((section (".linear_wr"))) @@ -74,6 +79,9 @@ extern void _LINEAR_RD_WINDOW_SECTION_SIZE; #define LINEAR_WR_WINDOW_SECTION_SIZE ((uint32_t) &_LINEAR_WR_WINDOW_SECTION_SIZE) #define CMDH_OCC_RESPONSE_BASE_ADDRESS ((uint32_t) &_LINEAR_RD_WINDOW_SECTION_BASE) #define LINEAR_RD_WINDOW_SECTION_SIZE ((uint32_t) &_LINEAR_RD_WINDOW_SECTION_SIZE) +#define FIR_PARMS_SECTION_BASE_ADDRESS ((uint32_t) &_FIR_PARMS_SECTION_BASE) +#define FIR_HEAP_SECTION_BASE_ADDRESS ((uint32_t) &_FIR_HEAP_SECTION_BASE) + // Conversion Macro's @@ -231,6 +239,7 @@ enum SSX_STARTING = 0x0210, SSX_INITIALIZED = 0x02ff, TRACE_INITIALIZED = 0x0310, + HOMER_ACCESS_INITS = 0x0318, INITIALIZING_IRQS = 0x0320, IRQS_INITIALIZED = 0x032f, MAIN_THREAD_STARTED = 0x03ff, diff --git a/src/occ/linkocc.cmd b/src/occ/linkocc.cmd index 783b414..0deabc5 100755 --- a/src/occ/linkocc.cmd +++ b/src/occ/linkocc.cmd @@ -1,27 +1,27 @@ -// IBM_PROLOG_BEGIN_TAG -// This is an automatically generated prolog. -// -// $Source: src/occ/linkocc.cmd $ -// -// OpenPOWER OnChipController Project -// -// Contributors Listed Below - COPYRIGHT 2011,2014 -// [+] Google Inc. -// [+] International Business Machines Corp. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or -// implied. See the License for the specific language governing -// permissions and limitations under the License. -// -// IBM_PROLOG_END_TAG +/* IBM_PROLOG_BEGIN_TAG */ +/* This is an automatically generated prolog. */ +/* */ +/* $Source: src/occ/linkocc.cmd $ */ +/* */ +/* OpenPOWER OnChipController Project */ +/* */ +/* Contributors Listed Below - COPYRIGHT 2011,2015 */ +/* [+] International Business Machines Corp. */ +/* */ +/* */ +/* Licensed under the Apache License, Version 2.0 (the "License"); */ +/* you may not use this file except in compliance with the License. */ +/* You may obtain a copy of the License at */ +/* */ +/* http://www.apache.org/licenses/LICENSE-2.0 */ +/* */ +/* Unless required by applicable law or agreed to in writing, software */ +/* distributed under the License is distributed on an "AS IS" BASIS, */ +/* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or */ +/* implied. See the License for the specific language governing */ +/* permissions and limitations under the License. */ +/* */ +/* IBM_PROLOG_END_TAG */ // Description @@ -546,12 +546,32 @@ SECTIONS __WRITEABLE_DATA_LEN__ = . - __WRITEABLE_DATA_ADDR__ ; _EX_FREE_SECTION_SIZE = 0 - _EX_FREE_SECTION_BASE; #else - _DATA_SECTION_SIZE = _LINEAR_WR_WINDOW_SECTION_BASE - _DATA_SECTION_BASE; - __WRITEABLE_DATA_LEN__ = _LINEAR_WR_WINDOW_SECTION_BASE - __WRITEABLE_DATA_ADDR__ ; + _DATA_SECTION_SIZE = _FIR_HEAP_SECTION_BASE - _DATA_SECTION_BASE; + __WRITEABLE_DATA_LEN__ = _FIR_HEAP_SECTION_BASE - __WRITEABLE_DATA_ADDR__ ; _EX_FREE_SECTION_SIZE = 0; #endif - _SSX_FREE_END = _LINEAR_WR_WINDOW_SECTION_BASE - 1; + _SSX_FREE_END = _FIR_HEAP_SECTION_BASE - 1; + + //////////////////////////////// + // FIR data heap section + //////////////////////////////// + __CUR_COUNTER__ = .; + _FIR_HEAP_SECTION_BASE = 0xffff2000; + _FIR_HEAP_SECTION_SIZE = 0x3000; + . = _FIR_HEAP_SECTION_BASE; + .firHeap . : {*(firHeap) . = ALIGN(1024);} > sram + . = __CUR_COUNTER__; + + //////////////////////////////// + // FIR data parms section + //////////////////////////////// + __CUR_COUNTER__ = .; + _FIR_PARMS_SECTION_BASE = 0xffff5000; + _FIR_PARMS_SECTION_SIZE = 0x1000; + . = _FIR_PARMS_SECTION_BASE; + .firParms . : {*(firParms) . = ALIGN(1024);} > sram + . = __CUR_COUNTER__; //////////////////////////////// // FSP Command Buffer diff --git a/src/occ/main.c b/src/occ/main.c index 6700964..06e9702 100755 --- a/src/occ/main.c +++ b/src/occ/main.c @@ -5,10 +5,10 @@ /* */ /* OpenPOWER OnChipController Project */ /* */ -/* Contributors Listed Below - COPYRIGHT 2011,2014 */ -/* [+] Google Inc. */ +/* Contributors Listed Below - COPYRIGHT 2011,2015 */ /* [+] International Business Machines Corp. */ /* */ +/* */ /* Licensed under the Apache License, Version 2.0 (the "License"); */ /* you may not use this file except in compliance with the License. */ /* You may obtain a copy of the License at */ @@ -53,6 +53,7 @@ #include <amec_freq.h> #include <thrm_thread.h> #include "scom.h" +#include <fir_data_collect.h> extern void __ssx_boot; extern uint32_t G_occ_phantom_critical_count; @@ -68,7 +69,6 @@ IMAGE_HEADER (G_mainAppImageHdr,__ssx_boot,MAIN_APP_ID,ID_NUM_INVALID); SimicsStdio G_simics_stdout; SimicsStdio G_simics_stderr; - // Critical /non Critical Stacks uint8_t G_noncritical_stack[NONCRITICAL_STACK_SIZE]; uint8_t G_critical_stack[CRITICAL_STACK_SIZE]; @@ -85,8 +85,14 @@ SsxSemaphore G_ffdcSem; // Timer for posting thermal, health monitor and FFDC semaphore SsxTimer G_mainThrdTimer; -// @sb022 Variable holding main thread loop count +// Variable holding main thread loop count uint32_t G_mainThreadLoopCounter = 0x0; +// Global flag indicating FIR collection is required +bool G_fir_collection_required = FALSE; + +extern uint8_t g_trac_inf_buffer[]; +extern uint8_t g_trac_imp_buffer[]; +extern uint8_t g_trac_err_buffer[]; void pmc_hw_error_isr(void *private, SsxIrqId irq, int priority); @@ -337,7 +343,7 @@ void occ_irq_setup() if(l_rc) { //single error for all error cases, just look at trace to see where it failed. - /*@ + /* @ * @moduleid OCC_IRQ_SETUP * @reasonCode SSX_GENERIC_FAILURE * @severity ERRL_SEV_UNRECOVERABLE @@ -703,7 +709,7 @@ void Main_thread_routine(void *private) TRAC_INFO("Main Thread Started ... " ); - workaround_HW258436(); //gm016 + workaround_HW258436(); // NOTE: At present, we are not planning to use any config data from // mainstore. OCC Role will be provided by FSP after FSP communication @@ -711,7 +717,6 @@ void Main_thread_routine(void *private) // dcom_initialize_roles. If in future design changes, we will make // change to use config_data_init at that time. // Default role initialization and determine OCC/Chip Id - // @03a get our role dcom_initialize_roles(); CHECKPOINT(ROLES_INITIALIZED); @@ -721,16 +726,9 @@ void Main_thread_routine(void *private) sensor_init_all(); CHECKPOINT(SENSORS_INITIALIZED); - // SPIVID Initialization - // Must be done before Pstates - // @sb001 Removed SPIVID inits + // SPIVID Initialization must be done before Pstates // All SPIVID inits are done by Hostboot, remove this section. - //#if 0 - // o2s_initialize(); - // spivid_initialize(7); - //#endif - //@04a //Initialize structures for collecting core data. //It needs to run before RTLoop start as pore initialization needs to be // done before task to collect core data starts. @@ -739,10 +737,9 @@ void Main_thread_routine(void *private) // Run slave OCC init on all OCCs. Master-only initialization will be // done after determining actual role. By default all OCCs are slave. - slave_occ_init(); // @th00b - Moved after proc core init + slave_occ_init(); CHECKPOINT(SLAVE_OCC_INITIALIZED); -#ifndef OCC_SIMICS_RESPONDER // Initialize watchdog timers. This needs to be right before // start rtl to make sure timer doesn't timeout. This timer is being // reset from the rtl task. @@ -753,18 +750,13 @@ void Main_thread_routine(void *private) // Initialize Real time Loop Timer Interrupt rtl_ocb_init(); CHECKPOINT(RTL_TIMER_INITIALIZED); -#else - TRAC_ERR("---------------------------------"); - TRAC_ERR(" OCC Responder for Simics"); - TRAC_ERR("---------------------------------"); -#endif // Initialize semaphores and timer for handling thermal, health monitor and // FFDC functions. initMainThrdSemAndTimer(); CHECKPOINT(SEMS_AND_TIMERS_INITIALIZED); - //Initialize the Applet Manager @02a + //Initialize the Applet Manager // This needs to be done before initThreadScheduler because command line // handler thread might start using applet as soon as it starts. Command // line handler thread is started as part of the initThreadScheduler along @@ -778,28 +770,57 @@ void Main_thread_routine(void *private) // through cmd handler thread in middle of the initialization. initThreadScheduler(); -#ifdef FSPLESS_SIMICS - // In order to avoid having to run a ton of separate commands to each - // occ when we want to test in Simics w/o FSP, we can run with a FSPLESS - // config, using hardcoded default data from occ_sys_config.c - sysConfigFspLess(); -#endif - int l_ssxrc = SSX_OK; - // @sb022 initWatchdogTimers called before will start running the timer but + // initWatchdogTimers called before will start running the timer but // the interrupt handler will just restart the timer until we use this // enable switch to actually start the watchdog function. ENABLE_WDOG; while (TRUE) { - // @sb022 Count each loop so the watchdog can tell the main thread is + // Count each loop so the watchdog can tell the main thread is // running. G_mainThreadLoopCounter++; + // Flush the loop counter and trace buffers on each loop, this makes + // debug easier if the cmd interface doesn't respond + dcache_flush_line(&G_mainThreadLoopCounter); + dcache_flush(g_trac_inf_buffer, TRACE_BUFFER_SIZE); + dcache_flush(g_trac_imp_buffer, TRACE_BUFFER_SIZE); + dcache_flush(g_trac_err_buffer, TRACE_BUFFER_SIZE); + // Wait for thermal semaphore l_ssxrc = ssx_semaphore_pend(&G_thrmSem,SSX_WAIT_FOREVER); + static bool L_fir_collection_completed = FALSE; + // Look for FIR collection flag and status + if (G_fir_collection_required && !L_fir_collection_completed) + { + // If this OCC is the FIR master and PNOR access is allowed perform + // FIR collection + if (OCC_IS_FIR_MASTER() && pnor_access_allowed()) + { + fir_data_collect(); + L_fir_collection_completed = TRUE; + } + + G_fir_collection_required = FALSE; + // Error reporting is skipped while FIR collection is required so we + // don't get reset in flight. If anyone is listening send the + // error alert now. + // If this system is using PSIHB complex, send an interrupt to Host so that + // Host can inform HTMGT to collect the error log + if (G_occ_interrupt_type == PSIHB_INTERRUPT) + { + // From OCC OpenPower Interface v1.1, OCC needs to set bits 0 and 1 of + // the OCB_OCCMISC register + ocb_occmisc_t l_occmiscreg; + l_occmiscreg.fields.core_ext_intr = 1; + l_occmiscreg.fields.reason_intr = 1; + out32(OCB_OCCMISC_OR, l_occmiscreg.value); + } + } + if ( l_ssxrc != SSX_OK ) { TRAC_ERR("thermal Semaphore pending failure RC[0x%08X]", -l_ssxrc ); @@ -883,6 +904,7 @@ void Main_thread_routine(void *private) int main(int argc, char **argv) { int l_ssxrc; + int l_ssxrc2; // ---------------------------------------------------- // Initialize TLB for Linear Window access here so we @@ -896,6 +918,7 @@ int main(int argc, char **argv) TLBLO_WR | TLBLO_I, NULL ); + if(l_ssxrc != SSX_OK) { //failure means we can't talk to FSP. @@ -910,26 +933,58 @@ int main(int argc, char **argv) TLBLO_I, NULL ); + if(l_ssxrc != SSX_OK) { //failure means we can't talk to FSP. SSX_PANIC(0x01000002); } + // Setup the TLB for writing to the FIR parms section + l_ssxrc = ppc405_mmu_map(FIR_PARMS_SECTION_BASE_ADDRESS, + FIR_PARMS_SECTION_BASE_ADDRESS, + FIR_PARMS_SECTION_SIZE, + 0, + TLBLO_WR | TLBLO_I, + NULL); + + if (l_ssxrc != SSX_OK) + { + // Panic, this section is required for FIR collection on checkstops + SSX_PANIC(0x01000003); + } + + // Setup the TLB for writing to the FIR heap section + l_ssxrc = ppc405_mmu_map(FIR_HEAP_SECTION_BASE_ADDRESS, + FIR_HEAP_SECTION_BASE_ADDRESS, + FIR_HEAP_SECTION_SIZE, + 0, + TLBLO_WR | TLBLO_I, + NULL); + + if (l_ssxrc != SSX_OK) + { + // Panic, this section is required for FIR collection on checkstops + SSX_PANIC(0x01000004); + } + CHECKPOINT_INIT(); CHECKPOINT(MAIN_STARTED); - // Initialize stdout so we can do printf from within simics env -#ifdef OCC_SIMICS_RESPONDER - simics_stdout_create(&G_simics_stdout); - simics_stderr_create(&G_simics_stderr); - stdout = (FILE *)(&G_simics_stdout); - stderr = (FILE *)(&G_simics_stderr); - ssxout = (FILE *)(&G_simics_stdout); -#endif + homer_rc_t l_homerrc = HOMER_SUCCESS; + homer_rc_t l_homerrc2 = HOMER_SUCCESS; + + // Get the homer version + uint32_t l_homer_version = 0; + l_homerrc = homer_hd_map_read_unmap(HOMER_VERSION, + &l_homer_version, + &l_ssxrc); - // Initialize SSX Stacks - // NOTE: This also reinitializes the time base to 0 + if ((HOMER_SUCCESS != l_homerrc) && (HOMER_SSX_UNMAP_ERR != l_homerrc)) + { + // Attempt to use max version if we can't read the homer. + l_homer_version = HOMER_VERSION_MAX; + } // Get proc_pb_frequency from HOMER host data and calculate the timebase // frequency for the OCC. Pass the timebase frequency to ssx_initialize. @@ -937,16 +992,19 @@ int main(int argc, char **argv) // frequency so the passed value is 1/4 of the proc_pb_frequency from the // HOMER, ie. if the MRW says that proc_pb_frequency is 2400 MHz, then // pass 600000000 (600MHz) + // The offset from the start of the HOMER is 0x00100000, we will need to // create a temporary mapping to this section of the HOMER with ppc405_mmu_map // (at address 0x00000000) read the value, convert it, and then unmap. - homer_rc_t l_homerrc = HOMER_SUCCESS; + + // Don't do a version check before reading the nest freq, it's present in + // all HOMER versions. uint32_t l_tb_freq_hz = 0; - l_homerrc = homer_hd_map_read_unmap(HOMER_NEST_FREQ, - &l_tb_freq_hz, - &l_ssxrc); + l_homerrc2 = homer_hd_map_read_unmap(HOMER_NEST_FREQ, + &l_tb_freq_hz, + &l_ssxrc2); - if ((HOMER_SUCCESS == l_homerrc) || (HOMER_SSX_UNMAP_ERR == l_homerrc)) + if ((HOMER_SUCCESS == l_homerrc2) || (HOMER_SSX_UNMAP_ERR == l_homerrc2)) { // Data is in Mhz upon return and needs to be converted to Hz and then // quartered. @@ -958,25 +1016,9 @@ int main(int argc, char **argv) l_tb_freq_hz = 400000000; } - - // Get OCC interrupt type from HOMER host data area. This will tell OCC - // which interrupt to Host it should be using. - uint32_t l_occ_int_type = 0; - l_homerrc = homer_hd_map_read_unmap(HOMER_INT_TYPE, - &l_occ_int_type, - &l_ssxrc); - - if ((HOMER_SUCCESS == l_homerrc) || (HOMER_SSX_UNMAP_ERR == l_homerrc)) - { - G_occ_interrupt_type = (uint8_t) l_occ_int_type; - } - else - { - G_occ_interrupt_type = FSP_SUPPORTED_OCC; - } - CHECKPOINT(SSX_STARTING); + // Initialize SSX Stacks. This also reinitializes the time base to 0 ssx_initialize((SsxAddress)G_noncritical_stack, NONCRITICAL_STACK_SIZE, (SsxAddress)G_critical_stack, @@ -986,67 +1028,104 @@ int main(int argc, char **argv) CHECKPOINT(SSX_INITIALIZED); - // TRAC_XXX needs ssx service, so they can only be called after ssx_initialize + // TRAC_XXX needs ssx services, traces can only be done after ssx_initialize TRAC_init_buffers(); CHECKPOINT(TRACE_INITIALIZED); TRAC_INFO("Inside OCC Main"); // Trace what happened before ssx initialization - TRAC_INFO("HOMER accessed, rc=%d, nest_freq=%d, int_type=%d, ssx_rc=%d", - l_homerrc, l_tb_freq_hz, l_occ_int_type, l_ssxrc); + TRAC_INFO("HOMER accessed, rc=%d, version=%d, ssx_rc=%d", + l_homerrc, l_homer_version, l_ssxrc); + + TRAC_INFO("HOMER accessed, rc=%d, nest_freq=%d, ssx_rc=%d", + l_homerrc2, l_tb_freq_hz, l_ssxrc2); + + // Handle any errors from the version access + homer_log_access_error(l_homerrc, + l_ssxrc, + l_homer_version); + + // Handle any errors from the nest freq access + homer_log_access_error(l_homerrc2, + l_ssxrc2, + l_tb_freq_hz); + + // Time to access any initialization data needed from the HOMER (besides the + // nest frequency which was required above to enable SSX and tracing). + CHECKPOINT(HOMER_ACCESS_INITS); - // Catch and log the homer error after inits are done - if (HOMER_SUCCESS != l_homerrc) + if (l_homer_version >= HOMER_VERSION_2) { - // We could potentially have both an internal error dealing with the - // homer and an SSX error, for example we could find an unsupported - // version number in the homer and then have an ssx error trying to - // unmap the homer address space. This in catches all those cases - if (SSX_OK != l_ssxrc) + // Get OCC interrupt type from HOMER host data area. This will tell OCC + // which interrupt to Host it should be using. + uint32_t l_occ_int_type = 0; + l_homerrc = homer_hd_map_read_unmap(HOMER_INT_TYPE, + &l_occ_int_type, + &l_ssxrc); + + if ((HOMER_SUCCESS == l_homerrc) || (HOMER_SSX_UNMAP_ERR == l_homerrc)) { - /* @ - * @errortype - * @moduleid MAIN_MID - * @reasoncode SSX_GENERIC_FAILURE - * @userdata1 HOMER and SSX return codes - * @userdata2 Timebase frequency applied - * @userdata4 ERC_HOMER_MAIN_SSX_ERROR - * @devdesc An SSX error ocurred mapping the HOMER host data - * into the OCC address space. User word 1 contains - * both the internal and SSX return codes returned - * by the method used to access the HOMER data. - */ - errlHndl_t l_err = createErrl(MAIN_MID, //modId - SSX_GENERIC_FAILURE, //reasoncode - ERC_HOMER_MAIN_SSX_ERROR, //Extended reason code - ERRL_SEV_PREDICTIVE, //Severity - NULL, //Trace Buf - DEFAULT_TRACE_SIZE, //Trace Size - (l_homerrc << 16) | (0xFFFF & (uint32_t)l_ssxrc), //userdata1 - l_tb_freq_hz); //userdata2 - commitErrl(&l_err); + G_occ_interrupt_type = (uint8_t) l_occ_int_type; } else { - /* @ - * @errortype - * @moduleid MAIN_MID - * @reasoncode INTERNAL_FAILURE - * @userdata1 HOMER return code - * @userdata2 Default timebase frequency applied - * @userdata4 ERC_HOMER_MAIN_ACCESS_ERROR - * @devdesc Error accessing initialization data - */ - errlHndl_t l_err = createErrl(MAIN_MID, //modId - INTERNAL_FAILURE, //reasoncode - ERC_HOMER_MAIN_ACCESS_ERROR,//Extended reason code - ERRL_SEV_INFORMATIONAL, //Severity - NULL, //Trace Buf - DEFAULT_TRACE_SIZE, //Trace Size - l_homerrc, //userdata1 - l_tb_freq_hz); //userdata2 - commitErrl(&l_err); + G_occ_interrupt_type = FSP_SUPPORTED_OCC; + } + + TRAC_INFO("HOMER accessed, rc=%d, host interrupt type=%d, ssx_rc=%d", + l_homerrc, l_occ_int_type, l_ssxrc); + + // Handle any errors from the interrupt type access + homer_log_access_error(l_homerrc, + l_ssxrc, + l_occ_int_type); + } + + if (l_homer_version >= HOMER_VERSION_3) + { + // Get the FIR Master indicator + uint32_t l_fir_master = FIR_OCC_NOT_FIR_MASTER; + l_homerrc = homer_hd_map_read_unmap(HOMER_FIR_MASTER, + &l_fir_master, + &l_ssxrc); + + if (((HOMER_SUCCESS == l_homerrc) || (HOMER_SSX_UNMAP_ERR == l_homerrc)) + && + (FIR_OCC_IS_FIR_MASTER == l_fir_master)) + { + OCC_SET_FIR_MASTER(FIR_OCC_IS_FIR_MASTER); + } + else + { + OCC_SET_FIR_MASTER(FIR_OCC_NOT_FIR_MASTER); + } + + TRAC_INFO("HOMER accessed, rc=%d, FIR master=%d, ssx_rc=%d", + l_homerrc, l_fir_master, l_ssxrc); + + // Handle any errors from the FIR master access + homer_log_access_error(l_homerrc, + l_ssxrc, + l_fir_master); + + // If this OCC is the FIR master read in the FIR collection parms + if (OCC_IS_FIR_MASTER()) + { + TRAC_IMP("I am the FIR master"); + + // Read the FIR parms buffer + l_homerrc = homer_hd_map_read_unmap(HOMER_FIR_PARMS, + &G_fir_data_parms[0], + &l_ssxrc); + + TRAC_INFO("HOMER accessed, rc=%d, FIR parms buffer 0x%x, ssx_rc=%d", + l_homerrc, &G_fir_data_parms[0], l_ssxrc); + + // Handle any errors from the FIR master access + homer_log_access_error(l_homerrc, + l_ssxrc, + (uint32_t)&G_fir_data_parms[0]); } } @@ -1086,7 +1165,7 @@ int main(int argc, char **argv) -l_rc, //userdata1 0); //userdata2 // Commit Error log - REQUEST_RESET(l_err); // @gm006 + REQUEST_RESET(l_err); } // Enter SSX Kernel diff --git a/src/occ/occLinkInputFile b/src/occ/occLinkInputFile index 7e5dd54..892bcd4 100644 --- a/src/occ/occLinkInputFile +++ b/src/occ/occLinkInputFile @@ -1 +1,109 @@ -INPUT ( amec_init.o amec_tasks.o amec_slave_smh.o amec_master_smh.o amec_sensors_core.o amec_sensors_centaur.o amec_sensors_power.o amec_sensors_fw.o amec_data.o amec_freq.o amec_amester.o amec_dps.o amec_part.o amec_perfcount.o amec_controller.o amec_oversub.o amec_pcap.o amec_health.o amec_parm.o amec_parm_table.o amec_analytics.o appletManager.o centaur_data.o centaur_control.o cmdh_fsp.o cmdh_thread.o cmdh_fsp_cmds.o cmdh_fsp_cmds_datacnfg.o cmdh_mnfg_intf.o cmdh_tunable_parms.o cmdh_snapshot.o ffdc.o dcom.o dcom_thread.o dcomSlaveTx.o dcomSlaveRx.o dcomMasterTx.o dcomMasterRx.o errl.o proc_data.o proc_data_control.o proc_pstate.o apss.o dpss.o rtls_tables.o rtls.o sensor.o sensor_table.o threadSch.o chom.o thrm_thread.o timer.o trac_interface.o occ_sys_config.o arl_test.o state.o reset.o mode.o main.o scom.o homer.o occbuildname.o common.o ll_ffdc.o ssx_core.o ssx_init.o ssx_stack_init.o ssx_timer_core.o ssx_timer_init.o ssx_thread_init.o ssx_thread_core.o ssx_semaphore_init.o ssx_semaphore_core.o pgp_init.o pgp_irq_init.o pgp_pmc.o pgp_ocb.o pgp_pba.o pgp_id.o pgp_centaur.o pgp_cache.o pgp_async.o pgp_async_pore.o pgp_async_ocb.o pgp_async_pba.o ppc405_core.o ppc405_lib_core.o ppc405_cache_core.o ppc405_init.o ppc405_irq_core.o ppc405_irq_init.o ppc405_boot.o ppc405_exceptions.o ppc405_cache_init.o ppc405_mmu_asm.o ppc405_breakpoint.o ppc405_thread_init.o ppc405_mmu.o ) +INPUT ( amec_init.o + amec_tasks.o + amec_slave_smh.o + amec_master_smh.o + amec_sensors_core.o + amec_sensors_centaur.o + amec_sensors_power.o + amec_sensors_fw.o + amec_data.o + amec_freq.o + amec_amester.o + amec_dps.o amec_part.o + amec_perfcount.o + amec_controller.o + amec_oversub.o + amec_pcap.o + amec_health.o + amec_parm.o + amec_parm_table.o + amec_analytics.o + appletManager.o + centaur_data.o + centaur_control.o + cmdh_fsp.o + cmdh_thread.o + cmdh_fsp_cmds.o + cmdh_fsp_cmds_datacnfg.o + cmdh_mnfg_intf.o + cmdh_tunable_parms.o + cmdh_snapshot.o + ffdc.o + dcom.o + dcom_thread.o + dcomSlaveTx.o + dcomSlaveRx.o + dcomMasterTx.o + dcomMasterRx.o + errl.o + ecc.o + fir_data_collect.o + firData.o + fsi.o + lpc.o + native.o + nor_micron.o + pnor_util.o + scom_trgt.o + scom_util.o + sfc_ast2400.o + memcpy.o + proc_data.o + proc_data_control.o + proc_pstate.o + apss.o + dpss.o + rtls_tables.o + rtls.o + sensor.o + sensor_table.o + threadSch.o + chom.o + thrm_thread.o + timer.o + trac_interface.o + occ_sys_config.o + arl_test.o + state.o + reset.o + mode.o + main.o + scom.o + homer.o + occbuildname.o + common.o + ll_ffdc.o + ssx_core.o + ssx_init.o + ssx_stack_init.o + ssx_timer_core.o + ssx_timer_init.o + ssx_thread_init.o + ssx_thread_core.o + ssx_semaphore_init.o + ssx_semaphore_core.o + pgp_init.o + pgp_irq_init.o + pgp_pmc.o + pgp_ocb.o + pgp_pba.o + pgp_id.o + pgp_centaur.o + pgp_cache.o + pgp_async.o + pgp_async_pore.o + pgp_async_ocb.o + pgp_async_pba.o + ppc405_core.o + ppc405_lib_core.o + ppc405_cache_core.o + ppc405_init.o + ppc405_irq_core.o + ppc405_irq_init.o + ppc405_boot.o + ppc405_exceptions.o + ppc405_cache_init.o + ppc405_mmu_asm.o + ppc405_breakpoint.o + ppc405_thread_init.o + ppc405_mmu.o ) diff --git a/src/occ/occ_service_codes.h b/src/occ/occ_service_codes.h index 313c7fa..f1f4c70 100644 --- a/src/occ/occ_service_codes.h +++ b/src/occ/occ_service_codes.h @@ -5,10 +5,10 @@ /* */ /* OpenPOWER OnChipController Project */ /* */ -/* Contributors Listed Below - COPYRIGHT 2011,2014 */ -/* [+] Google Inc. */ +/* Contributors Listed Below - COPYRIGHT 2011,2015 */ /* [+] International Business Machines Corp. */ /* */ +/* */ /* Licensed under the Apache License, Version 2.0 (the "License"); */ /* you may not use this file except in compliance with the License. */ /* You may obtain a copy of the License at */ @@ -53,8 +53,7 @@ enum occReasonCode PROC_ERROR_TEMP = 0x10, /// Timed out reading processor temperature PROC_TEMP_TIMEOUT = 0x11, - /// @wb003 -- Removed PROCESSOR_NOT_SUPPORTED and changed error to INTERNAL_FAILURE - /// Processor SCOM failure -- gm025 + /// Processor SCOM failure PROC_SCOM_ERROR = 0x16, /// Any failure coming from the SSX RTOS code SSX_GENERIC_FAILURE = 0x17, @@ -88,6 +87,8 @@ enum occReasonCode INTERNAL_INVALID_INPUT_DATA = 0xB3, /// A core was not at the expected frequency TARGET_FREQ_FAILURE = 0xB4, + /// RTL detected a system checkstop + OCC_SYSTEM_HALTED = 0xB5, /// Request to read APSS data failed. APSS_GPE_FAILURE = 0xC0, /// Connector overcurrent pin still asserted. @@ -182,6 +183,8 @@ enum occExtReasonCode ERC_CHIP_IDS_INVALID = 0x00000050, ERC_GETSCOM_FAILURE = 0x00000051, + ERC_GETSCOM_TPC_GP0_FAILURE = 0x00000052, + ERC_PNOR_OWNERSHIP_NOT_AVAILABLE = 0x00000053, ERC_HOMER_MAIN_ACCESS_ERROR = 0x00000060, ERC_HOMER_MAIN_SSX_ERROR = 0x00000061, @@ -210,13 +213,14 @@ enum occModuleId MAIN_THRD_SEM_INIT_MID = MAIN_COMP_ID | 0x04, MAIN_STATE_TRANSITION_MID = MAIN_COMP_ID | 0x05, MAIN_MODE_TRANSITION_MID = MAIN_COMP_ID | 0x06, - MAIN_GPE_HALTED_MID = MAIN_COMP_ID | 0x07, + MAIN_SYSTEM_HALTED_MID = MAIN_COMP_ID | 0x07, OCC_IRQ_SETUP = MAIN_COMP_ID | 0x08, PMC_HW_ERROR_ISR = MAIN_COMP_ID | 0x09, GETSCOM_FFDC_MID = MAIN_COMP_ID | 0x0a, PUTSCOM_FFDC_MID = MAIN_COMP_ID | 0x0b, HMON_ROUTINE_MID = MAIN_COMP_ID | 0x0c, - AMEC_VERIFY_FREQ_MID = MAIN_COMP_ID | 0x0d, + AMEC_VERIFY_FREQ_MID = MAIN_COMP_ID | 0x0d, + FIR_DATA_MID = MAIN_COMP_ID | 0x0e, }; enum occUserDataType diff --git a/src/occ/reset.c b/src/occ/reset.c index 2ec233f..f99fc40 100755 --- a/src/occ/reset.c +++ b/src/occ/reset.c @@ -5,10 +5,10 @@ /* */ /* OpenPOWER OnChipController Project */ /* */ -/* Contributors Listed Below - COPYRIGHT 2011,2014 */ -/* [+] Google Inc. */ +/* Contributors Listed Below - COPYRIGHT 2011,2015 */ /* [+] International Business Machines Corp. */ /* */ +/* */ /* Licensed under the Apache License, Version 2.0 (the "License"); */ /* you may not use this file except in compliance with the License. */ /* You may obtain a copy of the License at */ @@ -96,7 +96,7 @@ void reset_state_request(uint8_t i_request) // state to reset to enter the reset state machine. if(G_reset_state < RESET_REQUESTED_DUE_TO_ERROR) { - TRAC_ERR("Resetting via Reset State Machine"); + TRAC_IMP("Activating reset required state."); G_reset_state = RESET_REQUESTED_DUE_TO_ERROR; @@ -150,50 +150,74 @@ void reset_state_request(uint8_t i_request) void task_check_for_checkstop(task_t *i_self) { pore_status_t l_gpe0_status; - static bool L_gpe_halt_traced = FALSE; + ocb_oisr0_t l_oisr0_status; + static bool L_checkstop_traced = FALSE; + uint8_t l_reason_code = 0; do { - //only do this once - if(L_gpe_halt_traced) + // This check is disabled once a checkstop or frozen GPE is detected + if(L_checkstop_traced) { break; } - L_gpe_halt_traced = TRUE; - + // Looked for a frozen GPE, a sign that the chip has stopped working or + // check-stopped. This check also looks for an interrupt status flag that + // indicates if the system has check-stopped. l_gpe0_status.value = in64(PORE_GPE0_STATUS); + l_oisr0_status.value = in32(OCB_OISR0); - if (l_gpe0_status.fields.freeze_action) + if (l_gpe0_status.fields.freeze_action + || + l_oisr0_status.fields.check_stop) { - errlHndl_t l_err = NULL; + errlHndl_t l_err = NULL; + + if (l_gpe0_status.fields.freeze_action) + { + TRAC_IMP("Frozen GPE0 detected by RTL"); + l_reason_code = OCC_GPE_HALTED; + } - TRAC_ERR("task_check_for_checkstop: OCC GPE halted due to checkstop"); + if (l_oisr0_status.fields.check_stop) + { + TRAC_IMP("System checkstop detected by RTL"); + l_reason_code = OCC_SYSTEM_HALTED; + } + + L_checkstop_traced = TRUE; /* * @errortype - * @moduleid MAIN_GPE_HALTED_MID + * @moduleid MAIN_SYSTEM_HALTED_MID * @reasoncode OCC_GPE_HALTED - * @userdata1 0 - * @userdata2 0 - * @devdesc OCC GPE halted due to checkstop + * @userdata1 High order word of PORE_GPE0_STATUS + * @userdata2 OCB_OISR0 + * @devdesc OCC detected frozen GPE0 */ - l_err = createErrl(MAIN_GPE_HALTED_MID, - OCC_GPE_HALTED, + /* + * @errortype + * @moduleid MAIN_SYSTEM_HALTED_MID + * @reasoncode OCC_SYSTEM_HALTED + * @userdata1 High order word of PORE_GPE0_STATUS + * @userdata2 OCB_OISR0 + * @devdesc OCC detected system checkstop + */ + l_err = createErrl(MAIN_SYSTEM_HALTED_MID, + l_reason_code, OCC_NO_EXTENDED_RC, ERRL_SEV_INFORMATIONAL, NULL, DEFAULT_TRACE_SIZE, - 0, - 0); + l_gpe0_status.words.high_order, + l_oisr0_status.value); - //This will request safe mode under the covers due to the GPE being halted. + // The commit code will check for the frozen GPE0 and system + // checkstop conditions and take appropriate actions. commitErrl(&l_err); } - - } while (0); + } + while(0); } - - - diff --git a/src/occ/state.c b/src/occ/state.c index 7288191..716c10c 100755 --- a/src/occ/state.c +++ b/src/occ/state.c @@ -5,10 +5,10 @@ /* */ /* OpenPOWER OnChipController Project */ /* */ -/* Contributors Listed Below - COPYRIGHT 2011,2014 */ -/* [+] Google Inc. */ +/* Contributors Listed Below - COPYRIGHT 2011,2015 */ /* [+] International Business Machines Corp. */ /* */ +/* */ /* Licensed under the Apache License, Version 2.0 (the "License"); */ /* you may not use this file except in compliance with the License. */ /* You may obtain a copy of the License at */ @@ -38,7 +38,7 @@ #include "proc_data.h" #include "heartbeat.h" #include "scom.h" - +#include <fir_data_collect.h> extern proc_gpsm_dcm_sync_occfw_t G_proc_dcm_sync_state; extern bool G_mem_monitoring_allowed; @@ -55,7 +55,6 @@ errlHndl_t SMGR_active_to_observation(); errlHndl_t SMGR_active_to_standby(); errlHndl_t SMGR_all_to_safe(); - // State that OCC is currently in OCC_STATE G_occ_internal_state = OCC_STATE_STANDBY; @@ -615,16 +614,15 @@ errlHndl_t SMGR_set_state(OCC_STATE i_new_state) * @userdata4 ERC_RUNNING_SEM_PENDING_FAILURE * @devdesc SSX semaphore related failure */ - l_transResult = createErrl(MAIN_STATE_TRANSITION_MID, //modId - SSX_GENERIC_FAILURE, //reasoncode - ERC_RUNNING_SEM_PENDING_FAILURE, //Extended reason code - ERRL_SEV_UNRECOVERABLE, //Severity - NULL, //Trace Buf - DEFAULT_TRACE_SIZE, //Trace Size - 0, //userdata1 - 0); //userdata2 - - // @wb001 -- Callout firmware + l_transResult = createErrl(MAIN_STATE_TRANSITION_MID, //modId + SSX_GENERIC_FAILURE, //reasoncode + ERC_RUNNING_SEM_PENDING_FAILURE, //Extended reason code + ERRL_SEV_UNRECOVERABLE, //Severity + NULL, //Trace Buf + DEFAULT_TRACE_SIZE, //Trace Size + 0, //userdata1 + 0); //userdata2 + addCalloutToErrl(l_transResult, ERRL_CALLOUT_TYPE_COMPONENT_ID, ERRL_COMPONENT_ID_FIRMWARE, @@ -643,32 +641,34 @@ errlHndl_t SMGR_set_state(OCC_STATE i_new_state) // transition function that matches the transition we need to do. for(jj=0; jj<G_smgr_state_trans_count; jj++) { - if( ((G_smgr_state_trans[jj].old_state == G_occ_internal_state) - || (G_smgr_state_trans[jj].old_state == OCC_STATE_ALL) ) - && (G_smgr_state_trans[jj].new_state == i_new_state) ) - { - // We found the transtion that matches, now run the function - // that is associated with that state transition. - if(NULL != G_smgr_state_trans[jj].trans_func_ptr) - { - // Signal that we are now in a state transition - G_state_transition_occuring = TRUE; - // Run transition function - l_transResult = (G_smgr_state_trans[jj].trans_func_ptr)(); - // Signal that we are done with the transition - G_state_transition_occuring = FALSE; - break; - } - } + if(((G_smgr_state_trans[jj].old_state == G_occ_internal_state) + || + (G_smgr_state_trans[jj].old_state == OCC_STATE_ALL)) + && + (G_smgr_state_trans[jj].new_state == i_new_state)) + { + // We found the transtion that matches, now run the function + // that is associated with that state transition. + if(NULL != G_smgr_state_trans[jj].trans_func_ptr) + { + // Signal that we are now in a state transition + G_state_transition_occuring = TRUE; + // Run transition function + l_transResult = (G_smgr_state_trans[jj].trans_func_ptr)(); + // Signal that we are done with the transition + G_state_transition_occuring = FALSE; + break; + } + } } // Check if we hit the end of the table without finding a valid // state transition. If we did, log an internal error. if(G_smgr_state_trans_count == jj) { - TRAC_ERR("No transition (or NULL) found for the state change\n"); - l_transResult = NULL; - break; + TRAC_ERR("No transition (or NULL) found for the state change"); + l_transResult = NULL; + break; } // If the state OCC requested from TMGT is the state we are now in, @@ -690,14 +690,15 @@ errlHndl_t SMGR_set_state(OCC_STATE i_new_state) // // Name: SMGR_validate_get_valid_states // -// Description: +// Description: Return a byte of status masks that correspond to the v10 poll +// response definition status byte. // // End Function Specification uint8_t SMGR_validate_get_valid_states(void) { - uint8_t l_valid_states = 0; - uint32_t l_datamask = DATA_get_present_cnfgdata(); - static BOOLEAN l_throttle_traced = FALSE; + uint8_t l_valid_states = 0; + uint32_t l_datamask = DATA_get_present_cnfgdata(); + static BOOLEAN l_throttle_traced = FALSE; // If we have everything we need to go to observation state if((l_datamask & SMGR_VALIDATE_DATA_OBSERVATION_MASK) == @@ -731,6 +732,12 @@ uint8_t SMGR_validate_get_valid_states(void) l_valid_states |= SMGR_MASK_MASTER_OCC; } + // Indicate if this OCC is the FIR master. + if (OCC_IS_FIR_MASTER()) + { + l_valid_states |= OCC_ROLE_FIR_MASTER_MASK; + } + return l_valid_states; } diff --git a/src/occ/trac/trac.h b/src/occ/trac/trac.h index 3ed4b05..87f669b 100755 --- a/src/occ/trac/trac.h +++ b/src/occ/trac/trac.h @@ -5,10 +5,10 @@ /* */ /* OpenPOWER OnChipController Project */ /* */ -/* Contributors Listed Below - COPYRIGHT 2011,2014 */ -/* [+] Google Inc. */ +/* Contributors Listed Below - COPYRIGHT 2011,2015 */ /* [+] International Business Machines Corp. */ /* */ +/* */ /* Licensed under the Apache License, Version 2.0 (the "License"); */ /* you may not use this file except in compliance with the License. */ /* You may obtain a copy of the License at */ @@ -71,7 +71,6 @@ extern void dumpHexString(const void *i_data, const unsigned int len, const char *string); #define DEBUG_HEXDUMP(data, len, string) \ dumpHexString(data, len, string) -//TODO: Do we want to have one buffer for tracing binary #else //TRAC_TO_SIMICS diff --git a/src/occ/trac/trac_interface.h b/src/occ/trac/trac_interface.h index 9370e8e..d448a0e 100755 --- a/src/occ/trac/trac_interface.h +++ b/src/occ/trac/trac_interface.h @@ -5,10 +5,10 @@ /* */ /* OpenPOWER OnChipController Project */ /* */ -/* Contributors Listed Below - COPYRIGHT 2011,2014 */ -/* [+] Google Inc. */ +/* Contributors Listed Below - COPYRIGHT 2011,2015 */ /* [+] International Business Machines Corp. */ /* */ +/* */ /* Licensed under the Apache License, Version 2.0 (the "License"); */ /* you may not use this file except in compliance with the License. */ /* You may obtain a copy of the License at */ |