/* IBM_PROLOG_BEGIN_TAG */ /* This is an automatically generated prolog. */ /* */ /* $Source: src/usr/i2c/runtime/rt_i2c.C $ */ /* */ /* OpenPOWER HostBoot Project */ /* */ /* Contributors Listed Below - COPYRIGHT 2015,2020 */ /* [+] 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 */ /** * @file rt_i2c.C * * @brief Runtime implementation of the i2c device driver * */ // ---------------------------------------------- // Includes // ---------------------------------------------- #include #include #include #include #include #include #include #include #include #include #include "../errlud_i2c.H" // ---------------------------------------------- // Trace definitions // ---------------------------------------------- trace_desc_t* g_trac_i2c = NULL; TRAC_INIT( & g_trac_i2c, I2C_COMP_NAME, KILOBYTE ); // Easy macro replace for unit testing //#define TRACUCOMP(args...) TRACFCOMP(args) #define TRACUCOMP(args...) namespace I2C { const uint64_t SCAC_CONFIG_REG = 0x00000000020115CEULL; const uint64_t SCAC_CONFIG_SET = 0x00000000020115CFULL; const uint64_t SCAC_CONFIG_CLR = 0x00000000020115D0ULL; const uint64_t SCAC_ENABLE_MSK = 0x8000000000000000ULL; // ------------------------------------------------------------------ // i2cPerformOp // ------------------------------------------------------------------ errlHndl_t i2cPerformOp( DeviceFW::OperationType i_opType, TARGETING::Target * i_target, void * io_buffer, size_t & io_buflen, int64_t i_accessType, va_list i_args ) { errlHndl_t err = NULL; TRACDCOMP( g_trac_i2c, ENTER_MRK"i2cPerformOp()" ); // Get the args out of the va_list // Address, Port, Engine, Device Addr. // Other args set below misc_args_t args; // Read in the sub-operation const auto subop = static_cast(va_arg(i_args,uint64_t)); args.port = va_arg( i_args, uint64_t ); args.engine = va_arg( i_args, uint64_t ); args.devAddr = va_arg( i_args, uint64_t ); // i2c addresses are 7 bits so shift that right 1 bit args.devAddr >>= 1; // These are additional parms in the case an offset is passed in // via va_list, as well args.offset_length = va_arg( i_args, uint64_t); uint32_t offset = 0; if ( args.offset_length != 0 ) { args.offset_buffer = reinterpret_cast (va_arg(i_args, uint64_t)); if ( args.offset_length == 1 ) { offset = *(args.offset_buffer); } else if ( args.offset_length == 2 ) { offset = *(reinterpret_cast(args.offset_buffer)); } else if ( args.offset_length == 4 ) { offset = *(reinterpret_cast(args.offset_buffer)); } else { TRACFCOMP(g_trac_i2c, ERR_MRK"Invalid Offset length: 0x%.8X." "Previous parameters: i2c subop 0x%.8X, " "port 0x%.8X, engine 0x%.8X, deviceAddr 0x%.8X", args.offset_length, subop, args.port, args.engine, args.devAddr); /*@ * @errortype * @moduleid I2C_PERFORM_OP * @reasoncode I2C_RUNTIME_INVALID_OFFSET_LENGTH * @userdata1 Offset length * @userdata2[0:31] Operation Type * @userdata2[32:64] Target * @devdesc I2C offset length is invalid */ err = new ERRORLOG::ErrlEntry(ERRORLOG::ERRL_SEV_INFORMATIONAL, I2C_PERFORM_OP, I2C_RUNTIME_INVALID_OFFSET_LENGTH, args.offset_length, TWO_UINT32_TO_UINT64(i_opType, TARGETING::get_huid(i_target))); err->addProcedureCallout(HWAS::EPUB_PRC_HB_CODE, HWAS::SRCI_PRIORITY_HIGH); return err; } } int rc = 0; bool l_host_if_enabled = true; TARGETING::rtChipId_t proc_id = 0; // Convert target to proc id err = TARGETING::getRtTarget( i_target, proc_id); if(err) { return err; } // Combine proc/engine/port uint64_t proc_engine_port = 0; proc_engine_port |= proc_id << HBRT_I2C_MASTER_CHIP_SHIFT; proc_engine_port |= (uint64_t)(args.engine) << HBRT_I2C_MASTER_ENGINE_SHIFT; proc_engine_port |= (uint64_t)(args.port) << HBRT_I2C_MASTER_PORT_SHIFT; // Send I2C op to host interface if(i_opType == DeviceFW::READ) { if(g_hostInterfaces->i2c_read != NULL) { rc = g_hostInterfaces->i2c_read ( proc_engine_port, // Master Chip/Engine/Port args.devAddr, // Dev Addr args.offset_length, // Offset size offset, // Offset io_buflen, // Buffer length io_buffer // Buffer ); } else { TRACFCOMP(g_trac_i2c, ERR_MRK"Hypervisor I2C read interface not linked"); l_host_if_enabled = false; } } else if (i_opType == DeviceFW::WRITE) { if(g_hostInterfaces->i2c_write != NULL) { rc = g_hostInterfaces->i2c_write ( proc_engine_port, // Master Chip/Engine/Port args.devAddr, // Dev Addr args.offset_length, // Offset size offset, // Offset io_buflen, // Buffer length io_buffer // Buffer ); } else { TRACFCOMP(g_trac_i2c, ERR_MRK"Hypervisor I2C write interface not linked"); l_host_if_enabled = false; } } if(!l_host_if_enabled) { /*@ * @errortype * @moduleid I2C_PERFORM_OP * @reasoncode I2C_RUNTIME_INTERFACE_ERR * @userdata1 0 * @userdata2 Op type * @devdesc I2C read/write interface not linked. */ err = new ERRORLOG::ErrlEntry(ERRORLOG::ERRL_SEV_INFORMATIONAL, I2C_PERFORM_OP, I2C_RUNTIME_INTERFACE_ERR, 0, i_opType); err->addProcedureCallout(HWAS::EPUB_PRC_HB_CODE, HWAS::SRCI_PRIORITY_HIGH); } if(rc) { // convert rc to error log /*@ * @errortype * @moduleid I2C_PERFORM_OP * @reasoncode I2C_RUNTIME_ERR * @userdata1 Hypervisor return code * @userdata2 Op type * @devdesc I2C access error */ err = new ERRORLOG::ErrlEntry(ERRORLOG::ERRL_SEV_INFORMATIONAL, I2C_PERFORM_OP, I2C_RUNTIME_ERR, rc, i_opType); err->addHwCallout(i_target, HWAS::SRCI_PRIORITY_LOW, HWAS::NO_DECONFIG, HWAS::GARD_NULL); // Note: no trace buffer available at runtime } // If there is an error, add parameter info to log if ( err != NULL ) { I2C::UdI2CParms( i_opType, i_target, io_buflen, i_accessType, args ) .addToLog(err); } TRACDCOMP( g_trac_i2c, EXIT_MRK"i2cPerformOp() - %s", ((NULL == err) ? "No Error" : "With Error") ); return err; } // end i2cPerformOp // Register the generic I2C perform Op with the routing code for Procs. DEVICE_REGISTER_ROUTE( DeviceFW::WILDCARD, DeviceFW::I2C, TARGETING::TYPE_PROC, i2cPerformOp ); // Register the generic I2C perform Op with the routing code for Memory Buffers. DEVICE_REGISTER_ROUTE( DeviceFW::WILDCARD, DeviceFW::I2C, TARGETING::TYPE_MEMBUF, i2cPerformOp ); // ------------------------------------------------------------------ // i2cDisableSensorCache // ------------------------------------------------------------------ errlHndl_t i2cDisableSensorCache ( TARGETING::Target * i_target, bool & o_disabled ) { errlHndl_t err = NULL; o_disabled = false; TRACDCOMP( g_trac_i2c, ENTER_MRK"i2cDisableSensorCache()" ); do { // There must be a 30ms window between the last time the cache // was enabled and the next time it is disabled. Since we // have no way to easily track the last enablement, we will // just take the hit inside every disable call. TRACDCOMP( g_trac_i2c, "Delaying 30ms before disable" ); nanosleep(0,30 * NS_PER_MSEC); uint64_t scacData = 0x0; size_t dataSize = sizeof(scacData); // Read the scac config reg to get the enabled/disabled bit err = DeviceFW::deviceOp( DeviceFW::READ, i_target, &scacData, dataSize, DEVICE_SCOM_ADDRESS(SCAC_CONFIG_REG) ); if ( err ) { break; } // Disable SCAC if it's enabled if( scacData & SCAC_ENABLE_MSK ) { o_disabled = true; // Enable SCAC again after op completes scacData = SCAC_ENABLE_MSK; // Write the scac clear reg to disable the sensor cache err = DeviceFW::deviceOp( DeviceFW::WRITE, i_target, &scacData, dataSize, DEVICE_SCOM_ADDRESS(SCAC_CONFIG_CLR) ); if ( err ) { break; } // Wait 30 msec for outstanding sensor cache // operations to complete nanosleep(0,30 * NS_PER_MSEC); } } while( 0 ); TRACDCOMP( g_trac_i2c, EXIT_MRK"i2cDisableSensorCache()" ); return err; } // end i2cDisableSensorCache // ------------------------------------------------------------------ // i2cEnableSensorCache // ------------------------------------------------------------------ errlHndl_t i2cEnableSensorCache ( TARGETING::Target * i_target ) { errlHndl_t err = NULL; TRACDCOMP( g_trac_i2c, ENTER_MRK"i2cEnableSensorCache()" ); // There must be a 2ms window where the cache is disabled to avoid // some thrashing in the Centaur logic. TRACDCOMP( g_trac_i2c, "Delaying 2ms before enable" ); nanosleep(0,2 * NS_PER_MSEC); uint64_t scacData = SCAC_ENABLE_MSK; size_t dataSize = sizeof(scacData); // Write the scac set reg to enable the sensor cache err = DeviceFW::deviceOp( DeviceFW::WRITE, i_target, &scacData, dataSize, DEVICE_SCOM_ADDRESS(SCAC_CONFIG_SET) ); TRACDCOMP( g_trac_i2c, EXIT_MRK"i2cEnableSensorCache()" ); return err; } // end i2cEnableSensorCache } // end namespace I2C