1 /** 2 * Copyright © 2020 IBM Corporation 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 #include "pmbus_write_vout_command_action.hpp" 18 19 #include "action_error.hpp" 20 #include "pmbus_error.hpp" 21 #include "write_verification_error.hpp" 22 23 #include <exception> 24 #include <ios> 25 #include <sstream> 26 27 namespace phosphor::power::regulators 28 { 29 30 bool PMBusWriteVoutCommandAction::execute(ActionEnvironment& environment) 31 { 32 try 33 { 34 // Get volts value 35 double voltsValue = getVoltsValue(environment); 36 37 // Get I2C interface to current device 38 i2c::I2CInterface& interface = getI2CInterface(environment); 39 40 // Get exponent value for converting volts value to linear format 41 int8_t exponentValue = getExponentValue(environment, interface); 42 43 // Convert volts value to linear data format 44 uint16_t linearValue = pmbus_utils::convertToVoutLinear(voltsValue, 45 exponentValue); 46 47 // Write linear format value to VOUT_COMMAND. I2CInterface method 48 // writes low-order byte first as required by PMBus. 49 interface.write(pmbus_utils::VOUT_COMMAND, linearValue); 50 51 // Verify write if necessary 52 if (isWriteVerified) 53 { 54 verifyWrite(environment, interface, linearValue); 55 } 56 } 57 // Nest the following exception types within an ActionError so the caller 58 // will have both the low level error information and the action information 59 catch (const i2c::I2CException& i2cError) 60 { 61 std::throw_with_nested(ActionError(*this)); 62 } 63 catch (const PMBusError& pmbusError) 64 { 65 std::throw_with_nested(ActionError(*this)); 66 } 67 catch (const WriteVerificationError& verifyError) 68 { 69 std::throw_with_nested(ActionError(*this)); 70 } 71 return true; 72 } 73 74 std::string PMBusWriteVoutCommandAction::toString() const 75 { 76 std::ostringstream ss; 77 ss << "pmbus_write_vout_command: { "; 78 79 if (volts.has_value()) 80 { 81 ss << "volts: " << volts.value() << ", "; 82 } 83 84 ss << "format: " << pmbus_utils::toString(format); 85 86 if (exponent.has_value()) 87 { 88 ss << ", exponent: " << static_cast<int16_t>(exponent.value()); 89 } 90 91 ss << ", is_verified: " << std::boolalpha << isWriteVerified << " }"; 92 return ss.str(); 93 } 94 95 int8_t PMBusWriteVoutCommandAction::getExponentValue( 96 ActionEnvironment& environment, i2c::I2CInterface& interface) 97 { 98 // Check if an exponent value is defined for this action 99 if (exponent.has_value()) 100 { 101 return exponent.value(); 102 } 103 104 // Read value of the VOUT_MODE command 105 uint8_t voutModeValue; 106 interface.read(pmbus_utils::VOUT_MODE, voutModeValue); 107 108 // Parse VOUT_MODE value to get data format and parameter value 109 pmbus_utils::VoutDataFormat format; 110 int8_t parameter; 111 pmbus_utils::parseVoutMode(voutModeValue, format, parameter); 112 113 // Verify format is linear; other formats not currently supported 114 if (format != pmbus_utils::VoutDataFormat::linear) 115 { 116 throw PMBusError("VOUT_MODE contains unsupported data format", 117 environment.getDeviceID(), 118 environment.getDevice().getFRU()); 119 } 120 121 // Return parameter value; it contains the exponent when format is linear 122 return parameter; 123 } 124 125 double 126 PMBusWriteVoutCommandAction::getVoltsValue(ActionEnvironment& environment) 127 { 128 double voltsValue; 129 130 if (volts.has_value()) 131 { 132 // A volts value is defined for this action 133 voltsValue = volts.value(); 134 } 135 else if (environment.getVolts().has_value()) 136 { 137 // A volts value is defined in the ActionEnvironment 138 voltsValue = environment.getVolts().value(); 139 } 140 else 141 { 142 throw ActionError(*this, "No volts value defined"); 143 } 144 145 return voltsValue; 146 } 147 148 void PMBusWriteVoutCommandAction::verifyWrite(ActionEnvironment& environment, 149 i2c::I2CInterface& interface, 150 uint16_t valueWritten) 151 { 152 // Read current value of VOUT_COMMAND. I2CInterface method reads low byte 153 // first as required by PMBus. 154 uint16_t valueRead{0x00}; 155 interface.read(pmbus_utils::VOUT_COMMAND, valueRead); 156 157 // Verify value read equals value written 158 if (valueRead != valueWritten) 159 { 160 std::ostringstream ss; 161 ss << "device: " << environment.getDeviceID() 162 << ", register: VOUT_COMMAND, value_written: 0x" << std::hex 163 << std::uppercase << valueWritten << ", value_read: 0x" << valueRead; 164 throw WriteVerificationError(ss.str(), environment.getDeviceID(), 165 environment.getDevice().getFRU()); 166 } 167 } 168 169 } // namespace phosphor::power::regulators 170