diff options
author | William Bryan <wilbryan@us.ibm.com> | 2015-08-03 12:38:58 -0500 |
---|---|---|
committer | William A. Bryan <wilbryan@us.ibm.com> | 2015-08-03 15:32:27 -0500 |
commit | 420e6d248cc6d2b3c39bc3970e3bb6747b3bddc3 (patch) | |
tree | c9f6691eddba39193e39aa769367e1267fb9fc86 /src/occ_405/firdata | |
parent | adade8c8ef30ed519322674c762d95663009c5d4 (diff) | |
download | talos-occ-420e6d248cc6d2b3c39bc3970e3bb6747b3bddc3.tar.gz talos-occ-420e6d248cc6d2b3c39bc3970e3bb6747b3bddc3.zip |
new ssx and lib files
Change-Id: I2328b1e86d59e3788910687d762fb70ec680058f
Reviewed-on: http://gfw160.aus.stglabs.ibm.com:8080/gerrit/19503
Reviewed-by: William A. Bryan <wilbryan@us.ibm.com>
Tested-by: William A. Bryan <wilbryan@us.ibm.com>
Diffstat (limited to 'src/occ_405/firdata')
25 files changed, 4729 insertions, 0 deletions
diff --git a/src/occ_405/firdata/ecc.c b/src/occ_405/firdata/ecc.c new file mode 100644 index 0000000..6696678 --- /dev/null +++ b/src/occ_405/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_405/firdata/ecc.h b/src/occ_405/firdata/ecc.h new file mode 100644 index 0000000..12ffe4e --- /dev/null +++ b/src/occ_405/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_405/firdata/firData.c b/src/occ_405/firdata/firData.c new file mode 100644 index 0000000..b446e4a --- /dev/null +++ b/src/occ_405/firdata/firData.c @@ -0,0 +1,756 @@ +/* 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 ) + { + TRAC_ERR( "[FirData_addRegToPnor] t=%d p=%d u=%d rc=%d " + "addr=0x%08x val=0x%08x%08x", i_sTrgt.type, + i_sTrgt.procPos, i_sTrgt.procUnitPos, rc, i_addr, + (uint32_t)(reg.val >> 32), (uint32_t)reg.val ); + + 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; + } + } + + 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; + } + } + + 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; + + uint64_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. */ + + TRAC_IMP( "FIRDATA: t=%d p=%d u=%d FSI=0x%08x isM=%c", i_sTrgt.type, + i_sTrgt.procPos, i_sTrgt.procUnitPos, i_sTrgt.fsiBaseAddr, + i_sTrgt.isMaster ? 'T' : 'F' ); + + 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; + + /* 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; + + /* 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; + + /* 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; + + /* 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; + + /* 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 ) + { + TRAC_ERR( 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 ) + { + TRAC_ERR( 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 ) + { + TRAC_ERR( 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] " + + 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 ) + { + TRAC_ERR( FUNC"Failed to init FIR data" ); + break; + } + + /* Check for valid HOMER data. */ + if ( HOMER_FIR1 != fd.hData->header ) + { + TRAC_ERR( FUNC"No HOMER data detected: header=0x%08x", + 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 ) + { + TRAC_ERR( FUNC"Failed to process FIR data" ); + break; + } + + } while (0); + + if ( SUCCESS != rc ) + { + TRAC_ERR( FUNC"Failed: i_hBuf=%p, i_hBufSize=0x%08x, i_pBuf=%p, " + "i_pBufSize=%08x", i_hBuf, i_hBufSize, i_pBuf, i_pBufSize ); + } + + return rc; + + #undef FUNC +} + diff --git a/src/occ_405/firdata/firData.h b/src/occ_405/firdata/firData.h new file mode 100644 index 0000000..f1475db --- /dev/null +++ b/src/occ_405/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_405/firdata/firDataConst_common.h b/src/occ_405/firdata/firDataConst_common.h new file mode 100644 index 0000000..17952df --- /dev/null +++ b/src/occ_405/firdata/firDataConst_common.h @@ -0,0 +1,76 @@ +/* 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. Also, this must be C, not C++, + * because OCC strictly uses C. */ + +#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_405/firdata/fir_data_collect.c b/src/occ_405/firdata/fir_data_collect.c new file mode 100644 index 0000000..1930fa5 --- /dev/null +++ b/src/occ_405/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_405/firdata/fir_data_collect.h b/src/occ_405/firdata/fir_data_collect.h new file mode 100644 index 0000000..c313956 --- /dev/null +++ b/src/occ_405/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_405/firdata/fsi.c b/src/occ_405/firdata/fsi.c new file mode 100644 index 0000000..4bf105a --- /dev/null +++ b/src/occ_405/firdata/fsi.c @@ -0,0 +1,152 @@ +/* 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 0x00020010 +#define OPB_REG_STAT 0x00020011 +#define OPB_REG_RES 0x00020014 +#define OPB_STAT_BUSY 0x0001000000000000 /**< 15 is the Busy bit */ +#define NS_PER_MSEC (1000000ull) + + +void fsi_recovery() +{ + int32_t rc = SUCCESS; + + /* Clear out OPB error */ + uint64_t scom_data = 0; + scom_data = 0x8000000000000000; /*0=Unit Reset*/ + rc |= xscom_write( OPB_REG_RES, scom_data ); + rc |= xscom_write( OPB_REG_STAT, scom_data ); + + /* Check if we have any errors left */ + rc |= xscom_read( OPB_REG_STAT, &scom_data ); + + TRACFCOMP( "PIB2OPB Status after cleanup = %08X%08X (rc=%d)", + (uint32_t)(scom_data >> 32), (uint32_t)scom_data, rc ); +} + +/** + * @brief Poll for completion of a FSI operation, return data on read + */ +int32_t poll_for_complete( uint32_t * o_val ) +{ + int32_t rc = SUCCESS; + + enum { MAX_OPB_TIMEOUT_NS = 10*NS_PER_MSEC }; /*=10ms */ + + *o_val = 0; + + uint64_t read_data = 0; + uint64_t elapsed_time_ns = 0; + do + { + rc = xscom_read( OPB_REG_STAT, &read_data ); + if ( SUCCESS != rc ) + { + fsi_recovery(); /* Try to recover the engine. */ + return rc; + } + + /* Check for completion. Note: not checking for FSI errors. */ + if ( (read_data & OPB_STAT_BUSY) == 0 ) break; /* Not busy */ + + sleep( 10000 ); /* sleep for 10,000 ns */ + elapsed_time_ns += 10000; + + } while ( elapsed_time_ns <= MAX_OPB_TIMEOUT_NS ); + + if ( MAX_OPB_TIMEOUT_NS < elapsed_time_ns ) + { + TRAC_ERR( "[poll_for_complete] FSI request timed out." ); + return FAIL; + } + + *o_val = (uint32_t)read_data; /* Data in the bottom half. */ + + return rc; +} + +/** + * @brief Read a FSI register + */ +int32_t getfsi( SCOM_Trgt_t i_trgt, uint32_t i_addr, uint32_t * o_val ) +{ + int32_t rc = SUCCESS; + + uint32_t fsi_addr = i_trgt.fsiBaseAddr | i_addr; + + /* 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 */ + rc = xscom_write( OPB_REG_CMD, fsi_cmd ); + if ( SUCCESS != rc ) + { + fsi_recovery(); /* Try to recover the engine. */ + return rc; + } + + /* Poll for complete and get the data back. */ + rc = poll_for_complete( o_val ); + + return rc; +} + +/** + * @brief Write a FSI register + */ +int32_t putfsi( SCOM_Trgt_t i_trgt, uint32_t i_addr, uint32_t i_val ) +{ + int32_t rc = SUCCESS; + + uint32_t fsi_addr = i_trgt.fsiBaseAddr | i_addr; + + /* 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_val; /* Data is in the bottom 32-bits */ + + /* Write the OPB command register to trigger the read */ + rc = xscom_write( OPB_REG_CMD, fsi_cmd ); + if ( SUCCESS != rc ) + { + fsi_recovery(); /* Try to recover the engine. */ + return rc; + } + + /* Poll for complete */ + uint32_t junk = 0; // Not used. + rc = poll_for_complete( &junk ); + + return rc; +} + diff --git a/src/occ_405/firdata/fsi.h b/src/occ_405/firdata/fsi.h new file mode 100644 index 0000000..870e47f --- /dev/null +++ b/src/occ_405/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> + +/** + * @brief Read a FSI register. + * @param i_trgt Chip/unit to read from. + * @param i_addr FSI address to read, relative to slave's base address. + * @param o_val Returned value. + * @return Non-SUCCESS if an internal function fails. SUCCESS otherwise. + */ +int32_t getfsi( SCOM_Trgt_t i_trgt, uint32_t i_addr, uint32_t * o_val ); + +/** + * @brief Write a FSI register. + * @param i_trgt Chip/unit to write to. + * @param i_addr FSI address to write, relative to slave's base address. + * @param o_val Value to write. + * @return Non-SUCCESS if an internal function fails. SUCCESS otherwise. + */ +int32_t putfsi( SCOM_Trgt_t i_trgt, uint32_t i_addr, uint32_t i_val ); + diff --git a/src/occ_405/firdata/homerData_common.h b/src/occ_405/firdata/homerData_common.h new file mode 100644 index 0000000..9dfa579 --- /dev/null +++ b/src/occ_405/firdata/homerData_common.h @@ -0,0 +1,172 @@ +/* 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. Also, this must be C, not C++, + * because OCC strictly uses C. */ + +#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) + * - ID FIR + * - ID MASK (ID FIR address + 0x300000000ll) + * - ID ACT0 (ID FIR address + 0x600000000ll) + * - ID ACT1 (ID FIR address + 0x700000000ll) + * Note that not all FIRs have a corresponding WOF register. So any WOFs needed + * for analysis will need to be explicitly listed in the corresponding + * 'Registers' lists. + */ + +typedef enum +{ + 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_405/firdata/lpc.c b/src/occ_405/firdata/lpc.c new file mode 100644 index 0000000..fe69efc --- /dev/null +++ b/src/occ_405/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 + { + xscom_read( ECCB_STAT_REG, &(o_stat->data64) ); + 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_405/firdata/lpc.h b/src/occ_405/firdata/lpc.h new file mode 100644 index 0000000..11290cc --- /dev/null +++ b/src/occ_405/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_405/firdata/native.c b/src/occ_405/firdata/native.c new file mode 100644 index 0000000..10ec524 --- /dev/null +++ b/src/occ_405/firdata/native.c @@ -0,0 +1,76 @@ +/* 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; + +int32_t xscom_read( uint32_t i_address, uint64_t * o_data ) +{ + int32_t rc = SUCCESS; + + *o_data = 0; + + rc = getscom_ffdc( i_address, o_data, NULL ); + if ( SUCCESS != rc ) + { + TRAC_ERR( "SCOM error in xscom_read wrapper, rc=%d", rc ); + } + + if ( TRACE_XSCOM ) + { + TRACFCOMP( "xscom_read(%08X)=%08X%08X", i_address, + (uint32_t)(*o_data>>32), (uint32_t)(*o_data) ); + } + + return rc; +} + +int32_t xscom_write( uint32_t i_address, uint64_t i_data ) +{ + int32_t rc = SUCCESS; + + rc = putscom_ffdc( i_address, i_data, NULL ); + if ( SUCCESS != rc ) + { + TRAC_ERR( "SCOM error in xscom_write wrapper, rc=%d", rc ); + } + + if ( TRACE_XSCOM ) + { + TRACFCOMP( "xscom_write(%08X)=%08X%08X", i_address, + (uint32_t)(i_data>>32), (uint32_t)i_data); + } + + return rc; +} + diff --git a/src/occ_405/firdata/native.h b/src/occ_405/firdata/native.h new file mode 100644 index 0000000..242c5e3 --- /dev/null +++ b/src/occ_405/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 */ +int32_t xscom_read( uint32_t i_address, uint64_t * o_data ); + +/* XSCOM Write */ +int32_t xscom_write( uint32_t i_address, uint64_t i_data ); + +/* Sleep */ +void sleep( SsxInterval i_nanoseconds ); + + +#endif diff --git a/src/occ_405/firdata/nor_micron.c b/src/occ_405/firdata/nor_micron.c new file mode 100644 index 0000000..3d0329a --- /dev/null +++ b/src/occ_405/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_405/firdata/norflash.h b/src/occ_405/firdata/norflash.h new file mode 100644 index 0000000..d41c878 --- /dev/null +++ b/src/occ_405/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_405/firdata/pnorData_common.h b/src/occ_405/firdata/pnorData_common.h new file mode 100644 index 0000000..d73f937 --- /dev/null +++ b/src/occ_405/firdata/pnorData_common.h @@ -0,0 +1,153 @@ +/* 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. Also, this must be C, not C++, + * because OCC strictly uses C. */ + +#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 + * register is still needed for FFDC. + * - 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 +{ + 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; + +#endif // __pnorData_common_h + diff --git a/src/occ_405/firdata/pnor_util.c b/src/occ_405/firdata/pnor_util.c new file mode 100644 index 0000000..bf32480 --- /dev/null +++ b/src/occ_405/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_405/firdata/pnor_util.h b/src/occ_405/firdata/pnor_util.h new file mode 100644 index 0000000..d8b5518 --- /dev/null +++ b/src/occ_405/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_405/firdata/scom_trgt.c b/src/occ_405/firdata/scom_trgt.c new file mode 100644 index 0000000..ec38122 --- /dev/null +++ b/src/occ_405/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_405/firdata/scom_trgt.h b/src/occ_405/firdata/scom_trgt.h new file mode 100644 index 0000000..4ffe8b5 --- /dev/null +++ b/src/occ_405/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_405/firdata/scom_util.c b/src/occ_405/firdata/scom_util.c new file mode 100644 index 0000000..4f6a391 --- /dev/null +++ b/src/occ_405/firdata/scom_util.c @@ -0,0 +1,427 @@ +/* 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 Translates a relative unit address to a real physical address + * @param i_trgt Chip/unit to SCOM. + * @param i_addr Address to SCOM (the unit's 0th address for unit SCOMs). + * @param o_addr Return address. + * @return Non-SUCCESS if an internal function fails. SUCCESS otherwise. + */ +int32_t translate_addr( SCOM_Trgt_t i_trgt, uint64_t i_addr, uint64_t * o_addr ) +{ + #define FUNC "[translate_addr] " + + int32_t rc = SUCCESS; + + TrgtType_t l_type = i_trgt.type; + + *o_addr = i_addr; + + /* No translation needed for non-unit scoms */ + if( (l_type == PROC) || (l_type == MEMB) ) + { + *o_addr = i_addr; + } + else /* it is a Unit */ + { + uint8_t l_num = SCOM_Trgt_getChipUnitPos(i_trgt); + + if( l_type == EX ) + { + /*first byte is 0x10, second nibble of that byte is the EX number */ + *o_addr |= (l_num << 24); + } + else if( l_type == MCS ) + { + /*Non-DMI address */ + if( (i_addr & 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 */ + { + *o_addr |= 0x100; + } + else if( (l_num / 2) == 2) /*range 2 */ + { + *o_addr |= 0x400; + } + else if( (l_num / 2) == 3) /*range 3 */ + { + *o_addr |= 0x500; + } + + /* Add 0x80 for the odd numbers */ + if( l_num % 2) + { + *o_addr |= 0x80; + } + } + else if( (i_addr & 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 ) + { + *o_addr |= 0x400; /* A00->E00 */ + } + } + else if( (i_addr & 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 */ + *o_addr &= 0xFFFFFF9FFFFFFFFF; + switch( l_num ) + { + case(0): + case(4): + *o_addr |= 0x0000006000000000; + break; + case(1): + case(5): + *o_addr |= 0x0000004000000000; + break; + case(2): + case(6): + /*nothing to do */ + break; + case(3): + case(7): + *o_addr |= 0x0000002000000000; + break; + default: + TRAC_ERR(FUNC"unsupported MCS unit position %d", l_num); + rc = FAIL; + } + if( l_num > 3 ) + { + *o_addr |= 0x400; /* A00->E00 */ + } + } + } + else if( l_type == MBA ) + { + if( (i_addr & MBA_MASK) == MBA_BASEADDR ) + { + /* 0x00000000_03010400 MBA 0 # MBA01 */ + /* 0x00000000_03010C00 MBA 1 # MBA23 */ + if( l_num == 1 ) + { + *o_addr |= 0x00000800; + } + } + else if( (i_addr & MBA_MASK) == TCM_MBA_BASEADDR ) + { + /* 0x00000000_03010880 MBA 0 # Trace for MBA01 */ + /* 0x00000000_030110C0 MBA 1 # Trace for MBA23 */ + *o_addr |= (l_num * 0x840); + } + else if( (i_addr & 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 */ + *o_addr &= 0xFFFFFFFFFFFFFBFF; + *o_addr |= 0x0000000000000800; + } + } + } + else + { + TRAC_ERR( FUNC"unsupported unit type %d", l_type ); + rc = FAIL; + } + } + + return rc; + + #undef FUNC +} + +/** + * @brief Performs a getscom operation with no address translation. + * @param i_chip Chip to SCOM. + * @param i_addr Address to SCOM. + * @param o_val Returned value. + * @return Non-SUCCESS if an internal function fails. SUCCESS otherwise. + */ +int32_t getscomraw( SCOM_Trgt_t i_chip, uint32_t i_addr, uint64_t * o_val ) +{ + int32_t rc = SUCCESS; + + *o_val = 0; + + /* SCOMs to the master chip are done via XSCOM. */ + if ( i_chip.isMaster ) + { + return xscom_read( i_addr, o_val ); + } + + /* 1) Sent the command to do the SCOM read. */ + rc = putfsi( i_chip, COMMAND_REG, i_addr ); + if ( SUCCESS != rc ) return rc; + + /* 2) Check status next -- TODO */ + + /* 3) Read the two data registers. */ + uint32_t data0, data1; + + rc = getfsi( i_chip, DATA0_REG, &data0 ); + if ( SUCCESS != rc ) return rc; + + rc = getfsi( i_chip, DATA1_REG, &data1 ); + if ( SUCCESS != rc ) return rc; + + *o_val = ((uint64_t)data0 << 32) | (uint64_t)data1; + + return rc; +} + +/** + * @brief Perform a putscom operation with no address translation. + * @param i_chip Chip to SCOM. + * @param i_addr Address to SCOM. + * @param i_val Value to write. + * @return Non-SUCCESS if an internal function fails. SUCCESS otherwise. + */ +int32_t putscomraw( SCOM_Trgt_t i_chip, uint32_t i_addr, uint64_t i_val ) +{ + int32_t rc = SUCCESS; + + /* SCOMs to the master chip are done via XSCOM. */ + if ( i_chip.isMaster ) + { + return xscom_write( i_addr, i_val ); + } + + /* 1) Write the two data registers. */ + rc = putfsi( i_chip, DATA0_REG, i_val >> 32 ); + if ( SUCCESS != rc ) return rc; + + rc = putfsi( i_chip, DATA1_REG, (uint32_t)i_val ); + if ( SUCCESS != rc ) return rc; + + /* 2) Send the command to do the SCOM write. */ + rc = putfsi( i_chip, COMMAND_REG, i_addr | 0x80000000 ); + if ( SUCCESS != rc ) return rc; + + /* 3) Check status next -- TODO */ + + return rc; +} + +/** + * @brief Executes standard getscom. + * @param i_trgt Chip to SCOM. + * @param i_addr Address to SCOM. + * @param o_val Returned value. + * @return Non-SUCCESS if an internal function fails. SUCCESS otherwise. + */ +int32_t SCOM_getScom( SCOM_Trgt_t i_trgt, uint32_t i_addr, uint64_t * o_val ) +{ + int32_t rc = SUCCESS; + + /* Get the parent chip. */ + SCOM_Trgt_t chip_targ = SCOM_Trgt_getParentChip(i_trgt); + + /* Get the address relative to the parent chip. */ + uint64_t trans_addr; + rc = translate_addr( i_trgt, i_addr, &trans_addr ); + if ( SUCCESS == rc ) + { + /* Do the SCOM. */ + rc = getscomraw( chip_targ, trans_addr, o_val ); + } + + return rc; +} + +/** + * @brief Execute indirect getscom. + * @param i_trgt Chip to SCOM. + * @param i_addr Address to SCOM. + * @param o_val Returned value. + * @return Non-SUCCESS if an internal function fails. SUCCESS otherwise. + */ +int32_t SCOM_getIdScom( SCOM_Trgt_t i_trgt, uint64_t i_addr, uint32_t * o_val ) +{ + #define FUNC "[SCOM_getIdScom] " + + int32_t rc = SUCCESS; + + *o_val = 0; + + /* Get the parent chip */ + SCOM_Trgt_t chip_targ = SCOM_Trgt_getParentChip(i_trgt); + + /* Get the address relative to the parent chip. */ + uint64_t trans_addr; + rc = translate_addr( i_trgt, i_addr, &trans_addr ); + if ( SUCCESS != rc ) return rc; + + /* 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 */ + rc = putscomraw( i_trgt, phys_addr, data_buffer ); + if ( SUCCESS != rc ) return rc; + + // 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. */ + rc = getscomraw( chip_targ, phys_addr, &(scomout.data64) ); + if ( SUCCESS != rc ) return rc; + + /* Check for PIB error. */ + if ( scomout.piberr ) + { + TRAC_ERR( FUNC"ID SCOM PIB error: phys_addr=0x%08x " + "trans_addr=0x%08x%08x", phys_addr, + (uint32_t)(trans_addr >> 32), (uint32_t)trans_addr ); + return FAIL; + } + + /* Jump out when done. */ + if ( scomout.done ) break; + + sleep( 10000 ); /* sleep for 10,000 ns */ + elapsed_indScom_time_ns += 10000; + + } while ( elapsed_indScom_time_ns <= 100000 ); /* wait for .1ms */ + + if ( !scomout.done ) + { + TRAC_ERR( FUNC"ID SCOM loop timeout exceeded: phys_addr=0x%08x " + "trans_addr=0x%08x%08x", phys_addr, + (uint32_t)(trans_addr >> 32), (uint32_t)trans_addr ); + return FAIL; + } + + *o_val = scomout.data; + + return rc; + + #undef FUNC +} diff --git a/src/occ_405/firdata/scom_util.h b/src/occ_405/firdata/scom_util.h new file mode 100644 index 0000000..039ac64 --- /dev/null +++ b/src/occ_405/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_405/firdata/sfc_ast2400.c b/src/occ_405/firdata/sfc_ast2400.c new file mode 100644 index 0000000..fbdb0b3 --- /dev/null +++ b/src/occ_405/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_405/firdata/sfc_ast2400.h b/src/occ_405/firdata/sfc_ast2400.h new file mode 100644 index 0000000..30a9052 --- /dev/null +++ b/src/occ_405/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 |