diff options
Diffstat (limited to 'src/usr/fapi2/plat_mmio_access.C')
-rw-r--r-- | src/usr/fapi2/plat_mmio_access.C | 310 |
1 files changed, 297 insertions, 13 deletions
diff --git a/src/usr/fapi2/plat_mmio_access.C b/src/usr/fapi2/plat_mmio_access.C index 60526761b..365aa1f22 100644 --- a/src/usr/fapi2/plat_mmio_access.C +++ b/src/usr/fapi2/plat_mmio_access.C @@ -5,7 +5,7 @@ /* */ /* OpenPOWER HostBoot Project */ /* */ -/* Contributors Listed Below - COPYRIGHT 2018 */ +/* Contributors Listed Below - COPYRIGHT 2018,2019 */ /* [+] International Business Machines Corp. */ /* */ /* */ @@ -39,9 +39,272 @@ #include <hwpf_fapi2_reasoncodes.H> #include <fapi2/plat_mmio_access.H> - namespace fapi2 { +// Address bit that designates it as an Explorer MMIO address +static const uint64_t EXPLR_IB_MMIO_OFFSET = 0x0000000100000000ull; + +// Valid I2C access to 256MB SRAM space, starts at offset 0x01000000 +static const uint64_t MIN_I2C_SRAM_SPACE_ADDRESS = 0x0000000001000000ull; +static const uint64_t MAX_I2C_SRAM_SPACE_ADDRESS = 0x0000000001030000ull; + +// byte transaction sizes for i2c +static const size_t I2C_TRANSACTION_SIZE = 4; // actual size sent +static const size_t SCOM_I2C_TRANSACTION_SIZE = 8; + +// Convert MMIO to I2C address (need these bits set) +static const uint64_t EXPLR_MMIO_TO_I2C_ADDRESS_MASK = 0xA0000000; + + +/** + * @brief Explorer Inband read via i2c + * @param[in] i_target - OCMB target + * @param[in/out] io_data_read - buffer to be filled with read data + * @param[in/out] io_get_size - size of buffer (returns read size) + * @param[in] i_i2c_addr - i2c scom address + * @return errlHndl_t indicating success or failure + */ +errlHndl_t explrIbI2cRead(TARGETING::Target * i_target, + uint8_t * io_data_read, + size_t io_get_size, + const uint64_t i_i2c_addr) +{ + errlHndl_t l_err = nullptr; + + if ( io_get_size % I2C_TRANSACTION_SIZE ) + { + // invalid size expected (needs to be a multiple of I2C_TRANSACTION_SIZE) + FAPI_ERR("explrIbI2cRead: read size %d is not a multiple of %d", + io_get_size, I2C_TRANSACTION_SIZE); + /*@ + * @errortype + * @moduleid fapi2::MOD_FAPI2_EXPLR_IB_I2C_READ + * @reasoncode fapi2::RC_INVALID_BUFFER_SIZE + * @userdata1[0:31] Buffer size + * @userdata1[32:63] Transaction size + * @userdata2[0:31] HUID of input target + * @userdata2[32:63] i2c_address + * @devdesc Invalid read buffer size, needs to be divisible by transaction size + * @custdesc Internal firmware error + */ + l_err = new ERRORLOG::ErrlEntry(ERRORLOG::ERRL_SEV_UNRECOVERABLE, + fapi2::MOD_FAPI2_EXPLR_IB_I2C_READ, + fapi2::RC_INVALID_BUFFER_SIZE, + TWO_UINT32_TO_UINT64(io_get_size, + I2C_TRANSACTION_SIZE), + TWO_UINT32_TO_UINT64( + TARGETING::get_huid(i_target), + i_i2c_addr), + ERRORLOG::ErrlEntry::ADD_SW_CALLOUT); + l_err->collectTrace(FAPI_TRACE_NAME); + io_get_size = 0; // no data being read + } + else + { + FAPI_INF("explrIbI2cRead: deviceRead() starting at i2cscom address " + "0x%08X for %d bytes", i_i2c_addr, io_get_size); + + // keep track of total bytes read + size_t total_bytes_read = 0; + + // Increment this address after each read transaction + uint64_t i2cAddr = i_i2c_addr; + + // scom transaction variables + size_t scomTransSize = SCOM_I2C_TRANSACTION_SIZE; + uint8_t scomReadData[SCOM_I2C_TRANSACTION_SIZE]; + + // Only able to read 4 bytes at a time with i2c, so + // need to break into multiple i2c read transactions + while( total_bytes_read < io_get_size ) + { + FAPI_DBG("explrIbI2cRead: deviceRead() at i2cscom address 0x%08X", i2cAddr); + l_err = deviceOp( DeviceFW::READ, + i_target, + scomReadData, + scomTransSize, + DEVICE_I2CSCOM_ADDRESS(i2cAddr) ); + if (l_err) + { + FAPI_ERR("explrIbI2cRead: target 0x%08X deviceRead() at address 0x%08X failed", + TARGETING::get_huid(i_target), i2cAddr); + l_err->collectTrace(FAPI_TRACE_NAME); + break; + } + else + { + FAPI_DBG("explrIbI2cRead: read %02X%02X%02X%02X", + scomReadData[4], scomReadData[5], scomReadData[6], scomReadData[7]); + } + + // The MMIO hardware does this byteswap for us, since we are + // running this over i2c we must reorder the bytes here + io_data_read[total_bytes_read] = scomReadData[7]; + io_data_read[total_bytes_read+1] = scomReadData[6]; + io_data_read[total_bytes_read+2] = scomReadData[5]; + io_data_read[total_bytes_read+3] = scomReadData[4]; + + // Only able to read 4 bytes at a time + total_bytes_read += I2C_TRANSACTION_SIZE; + i2cAddr += I2C_TRANSACTION_SIZE; + + // make sure this value is correct for next op + scomTransSize = SCOM_I2C_TRANSACTION_SIZE; + } + } + return l_err; +} + + +/** + * @brief Explorer Inband write via i2c + * @param[in] i_target - OCMB target + * @param[in] i_write_data - data to write out + * @param[in/out] io_write_size - how much data to write (returns how much written) + * @param[in] i_i2c_addr - i2c scom address + * @return errlHndl_t indicating success or failure + */ +errlHndl_t explrIbI2cWrite(TARGETING::Target * i_target, + const uint8_t * i_write_data, + size_t io_write_size, + const uint64_t i_i2c_addr) +{ + errlHndl_t l_err = nullptr; + + // Verify write can be divide up evenly + if ( io_write_size % I2C_TRANSACTION_SIZE ) + { + // invalid size expected (needs to be a multiple of I2C_TRANSACTION_SIZE) + FAPI_ERR("explrIbI2cWrite: write size %d is not a multiple of %d", + io_write_size, I2C_TRANSACTION_SIZE); + /*@ + * @errortype + * @moduleid fapi2::MOD_FAPI2_EXPLR_IB_I2C_WRITE + * @reasoncode fapi2::RC_INVALID_BUFFER_SIZE + * @userdata1[0:31] Buffer size + * @userdata1[32:63] Transaction size + * @userdata2[0:31] HUID of input target + * @userdata2[32:63] i2c_address + * @devdesc Invalid write buffer size, needs to be divisible by transaction size + * @custdesc Internal firmware error + */ + l_err = new ERRORLOG::ErrlEntry(ERRORLOG::ERRL_SEV_UNRECOVERABLE, + fapi2::MOD_FAPI2_EXPLR_IB_I2C_READ, + fapi2::RC_INVALID_BUFFER_SIZE, + TWO_UINT32_TO_UINT64(io_write_size, + I2C_TRANSACTION_SIZE), + TWO_UINT32_TO_UINT64( + TARGETING::get_huid(i_target), + i_i2c_addr), + ERRORLOG::ErrlEntry::ADD_SW_CALLOUT); + l_err->collectTrace(FAPI_TRACE_NAME); + } + else + { + // counter for bytes written out to SRAM space + size_t total_bytes_written = 0; + + // going to alter this address to cycle through write data + uint64_t i2cAddr = i_i2c_addr; + + // scom transaction variables + size_t scomTransSize = SCOM_I2C_TRANSACTION_SIZE; + uint8_t scomWriteData[SCOM_I2C_TRANSACTION_SIZE]; + + FAPI_INF("explrIbI2cWrite: deviceWrite() starting at i2cscom address " + "0x%08X for %d bytes", i_i2c_addr, io_write_size); + + // Only able to write 4 bytes at a time with i2c, + // so need to break into multiple i2c write transactions + while( total_bytes_written < io_write_size ) + { + memset(scomWriteData, 0x00, SCOM_I2C_TRANSACTION_SIZE); + + // Only last four bytes are actually written out + // NOTE: MMIO hardware does this byteswap for us, but since we + // are running this over i2c we must reorder the bytes here + scomWriteData[7] = i_write_data[total_bytes_written]; + scomWriteData[6] = i_write_data[total_bytes_written+1]; + scomWriteData[5] = i_write_data[total_bytes_written+2]; + scomWriteData[4] = i_write_data[total_bytes_written+3]; + + l_err = deviceOp( DeviceFW::WRITE, + i_target, + scomWriteData, + scomTransSize, + DEVICE_I2CSCOM_ADDRESS(i2cAddr) ); + if (l_err) + { + FAPI_ERR("explrIbI2cWrite: i2cscom write(0x%02X%02X%02X%02X) at address 0x%08X failed", + scomWriteData[4], scomWriteData[5], scomWriteData[6], scomWriteData[7], i2cAddr); + l_err->collectTrace(FAPI_TRACE_NAME); + break; + } + // Really only doing 4-byte transactions + i2cAddr += I2C_TRANSACTION_SIZE; + total_bytes_written += I2C_TRANSACTION_SIZE; + + // Update for next scom operation + scomTransSize = SCOM_I2C_TRANSACTION_SIZE; + } + } + return l_err; +} + +/** + * @brief Checks if all the conditions are met to allow i2c + * operation instead of MMIO. + * @param[in] i_ocmb - OCMB target + * @param[in] i_mmioAddress - address passed into get/putMMIO + * @return true if i2c should be used instead of MMIO + */ +bool useI2cInsteadOfMmio( const TARGETING::Target * i_ocmb, + const uint64_t i_mmioAddress ) +{ + bool useI2c = false; // default to use MMIO + + uint8_t attrAllowedI2c = 0; + + // Check force i2c attribute first + TARGETING::Target* l_sys = nullptr; + TARGETING::targetService().getTopLevelTarget(l_sys); + crit_assert(l_sys != nullptr); + attrAllowedI2c = l_sys->getAttr<TARGETING::ATTR_FORCE_SRAM_MMIO_OVER_I2C>(); + + // If not forced to use i2c, then check if that is the current scom setting + if (!attrAllowedI2c) + { + // The SCOM_SWITCHES attribute will keep track of when it is safe + // to access the ocmb via inband vs when we should do accesses over + // i2c. Use this attribute to decide which we want to use. + auto ocmb_info = i_ocmb->getAttr<TARGETING::ATTR_SCOM_SWITCHES>(); + if (!ocmb_info.useInbandScom) + { + attrAllowedI2c = 1; + } + } + + // Attribute settings must allow i2c operation before checking + // for a valid address range + if (attrAllowedI2c) + { + // Verify address is within valid SRAM range + if ( ((i_mmioAddress & 0x0F00000000) == EXPLR_IB_MMIO_OFFSET) && + ((i_mmioAddress & 0x0FFFFFFFF) >= MIN_I2C_SRAM_SPACE_ADDRESS) && + ((i_mmioAddress & 0x0FFFFFFFF) <= MAX_I2C_SRAM_SPACE_ADDRESS) ) + { + useI2c = true; + } + else + { + FAPI_INF("0x%08X OCMB address 0x%.8X is outside of SRAM range so using mmio", + TARGETING::get_huid(i_ocmb), i_mmioAddress); + } + } + + return useI2c; +} + //------------------------------------------------------------------------------ // HW Communication Functions to be implemented at the platform layer. //------------------------------------------------------------------------------ @@ -83,12 +346,22 @@ ReturnCode platGetMMIO( const Target<TARGET_TYPE_ALL>& i_target, break; //return with error } - // call MMIO driver - l_err = DeviceFW::deviceRead(l_target, - l_data_read, - l_get_size, - DEVICE_MMIO_ADDRESS(i_mmioAddr, i_transSize)); - + // Run mmio if inband i2c isn't enabled or address is outside of SRAM range + if ( !useI2cInsteadOfMmio(l_target, i_mmioAddr) ) + { + // call MMIO driver + l_err = DeviceFW::deviceRead(l_target, + l_data_read, + l_get_size, + DEVICE_MMIO_ADDRESS(i_mmioAddr, i_transSize)); + } + else + { + // Use i2c instead of MMMIO + // Explorer i2c addresses are actually 32-bit and need 0xA at the beginning + uint64_t i2cAddr = (i_mmioAddr & 0x00000000FFFFFFFF) | EXPLR_MMIO_TO_I2C_ADDRESS_MASK; + l_err = explrIbI2cRead(l_target, l_data_read, l_get_size, i2cAddr); + } if (l_traceit) { // Only trace the first 8 bytes of data read @@ -167,11 +440,22 @@ ReturnCode platPutMMIO( const Target<TARGET_TYPE_ALL>& i_target, std::copy(i_data.begin(), i_data.end(), l_writeDataPtr); size_t l_dataSize = i_data.size(); - // call MMIO driver - l_err = DeviceFW::deviceWrite(l_target, - l_writeDataPtr, - l_dataSize, - DEVICE_MMIO_ADDRESS(i_mmioAddr, i_transSize)); + // Run mmio if inband i2c isn't enabled or address is outside of SRAM range + if ( !useI2cInsteadOfMmio(l_target, i_mmioAddr) ) + { + // call MMIO driver + l_err = DeviceFW::deviceWrite(l_target, + l_writeDataPtr, + l_dataSize, + DEVICE_MMIO_ADDRESS(i_mmioAddr, i_transSize)); + } + else + { + // Address is an Explorer SRAM address, so I2C will work + // Explorer i2c addresses are actually 32-bit and need 0xA at the beginning + uint64_t i2cAddr = (i_mmioAddr & 0x00000000FFFFFFFF) | EXPLR_MMIO_TO_I2C_ADDRESS_MASK; + l_err = explrIbI2cWrite(l_target, l_writeDataPtr, l_dataSize, i2cAddr); + } if (l_traceit) { // trace the first 8 bytes of written data |