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 #pragma once
17 
18 #include <cmath>
19 #include <cstdint>
20 #include <string>
21 
22 /**
23  * @namespace pmbus_utils
24  *
25  * Contains utilities for sending PMBus commands over an I2C interface.
26  */
27 namespace phosphor::power::regulators::pmbus_utils
28 {
29 
30 /*
31  * PMBus command codes.
32  *
33  * The constant names are all uppercase to match the PMBus documentation.
34  *
35  * Only the commands that are currently used by this application are defined.
36  * See the PMBus documentation for all valid command codes.
37  */
38 const uint8_t VOUT_MODE{0x20u};
39 const uint8_t VOUT_COMMAND{0x21u};
40 
41 /**
42  * Sensor data format.
43  */
44 enum class SensorDataFormat
45 {
46     /**
47      * Linear data format used for values not related to voltage output, such
48      * as output current, input voltage, and temperature. Two byte value with
49      * an 11-bit, two's complement mantissa and a 5-bit, two's complement
50      * exponent.
51      */
52     linear_11,
53 
54     /**
55      * Linear data format used for values related to voltage output. Two
56      * byte (16-bit), unsigned integer that is raised to the power of an
57      * exponent. The exponent is not stored within the two bytes.
58      */
59     linear_16
60 };
61 
62 /**
63  * Sensor Value Type.
64  */
65 enum class SensorValueType
66 {
67     /**
68      * Output current.
69      */
70     iout,
71 
72     /**
73      * Highest output current.
74      */
75     iout_peak,
76 
77     /**
78      * Lowest output current.
79      */
80     iout_valley,
81 
82     /**
83      * Output power.
84      */
85     pout,
86 
87     /**
88      * Temperature.
89      */
90     temperature,
91 
92     /**
93      * Highest temperature.
94      */
95     temperature_peak,
96 
97     /**
98      * Output voltage.
99      */
100     vout,
101 
102     /**
103      * Highest output voltage.
104      */
105     vout_peak,
106 
107     /**
108      * Lowest output voltage.
109      */
110     vout_valley
111 };
112 
113 /**
114  * Data formats for output voltage.
115  *
116  * These formats are used for commanding and reading output voltage and related
117  * parameters.
118  */
119 enum class VoutDataFormat
120 {
121     /**
122      * Linear scale that uses a two byte unsigned binary integer with a scaling
123      * factor.
124      */
125     linear,
126 
127     /**
128      * Format that supports transmitting VID codes.
129      */
130     vid,
131 
132     /**
133      * Direct format that uses an equation and device supplied coefficients.
134      */
135     direct,
136 
137     /**
138      * Half-precision floating point format that follows the IEEE-754 standard
139      * for representing magnitudes in 16 bits.
140      */
141     ieee
142 };
143 
144 /**
145  * This struct represents one sensor reading from a device.
146  */
147 struct SensorReading
148 {
149     /**
150      * Sensor value type that was read.
151      */
152     SensorValueType type;
153 
154     /**
155      * Sensor value that was read.
156      */
157     double value;
158 };
159 
160 /**
161  * Parse the one byte value of the VOUT_MODE command.
162  *
163  * VOUT_MODE contains a 'mode' field that indicates the data format used for
164  * output voltage values.
165  *
166  * VOUT_MODE also contains a 'parameter' field whose value is dependent on the
167  * data format:
168  *  - Linear format: value is an exponent
169  *  - VID format: value is a VID code
170  *  - IEEE and Direct formats: value is not used
171  *
172  * @param voutModeValue one byte value of VOUT_MODE command
173  * @param format data format from the 'mode' field
174  * @param parameter parameter value from the 'parameter' field
175  */
176 void parseVoutMode(uint8_t voutModeValue, VoutDataFormat& format,
177                    int8_t& parameter);
178 
179 /**
180  * Converts the specified SensorDataFormat value to a string.
181  *
182  * @param format SensorDataFormat value
183  * @return string corresponding to the enum value
184  */
185 std::string toString(SensorDataFormat format);
186 
187 /**
188  * Converts the specified SensorValueType value to string.
189  *
190  * @param type SensorValueType type
191  * @return string corresponding to the enum value
192  */
193 std::string toString(SensorValueType type);
194 
195 /**
196  * Converts the specified VoutDataFormat value to string.
197  *
198  * @param format VoutDataFormat format
199  * @return string corresponding to the enum value
200  */
201 std::string toString(VoutDataFormat format);
202 
203 /**
204  * Converts a linear data format value to a double value.
205  *
206  * This data format consists of the following:
207  *   - Two byte value
208  *   - 11-bit two's complement mantissa value stored in the two bytes
209  *   - 5-bit two's complement exponent value stored in the two bytes
210  *
211  * @param value linear data format value
212  * @return double value
213  */
214 inline double convertFromLinear(uint16_t value)
215 {
216     // extract exponent from most significant 5 bits
217     uint8_t exponentField = value >> 11;
218 
219     // extract mantissa from least significant 11 bits
220     uint16_t mantissaField = value & 0x7FFu;
221 
222     // sign extend exponent
223     if (exponentField > 0x0Fu)
224     {
225         exponentField |= 0xE0u;
226     }
227 
228     // sign extend mantissa
229     if (mantissaField > 0x03FFu)
230     {
231         mantissaField |= 0xF800u;
232     }
233 
234     int8_t exponent = static_cast<int8_t>(exponentField);
235     int16_t mantissa = static_cast<int16_t>(mantissaField);
236 
237     // compute value as mantissa * 2^(exponent)
238     double decimal = mantissa * std::pow(2.0, exponent);
239     return decimal;
240 }
241 
242 /**
243  * Converts a linear data format output voltage value to a volts value.
244  *
245  * This data format consists of the following:
246  *   - Two byte value
247  *   - 16-bit unsigned mantissa value stored in the two bytes
248  *   - 5-bit signed exponent value that is not stored in the two bytes
249  *
250  * @param value linear data format output voltage value
251  * @param exponent exponent value obtained from VOUT_MODE or device
252  *        documentation
253  * @return normal decimal number
254  */
255 inline double convertFromVoutLinear(uint16_t value, int8_t exponent)
256 {
257     // compute value as mantissa * 2^(exponent)
258     double decimal = value * std::pow(2.0, exponent);
259     return decimal;
260 }
261 
262 /**
263  * Converts a volts value to the linear data format for output voltage.
264  *
265  * This data format consists of the following:
266  *   - Two byte value
267  *   - 16-bit unsigned mantissa value stored in the two bytes
268  *   - 5-bit signed exponent value that is not stored in the two bytes
269  *
270  * The exponent value is typically obtained from the PMBus VOUT_MODE command
271  * or from the hardware device documentation (data sheet).
272  *
273  * Note that this format differs from the linear data format for values
274  * unrelated to output voltage.
275  *
276  * @param volts volts value to convert; must not be negative
277  * @param exponent 5-bit signed exponent used to convert value
278  * @return linear data format value
279  */
280 inline uint16_t convertToVoutLinear(double volts, int8_t exponent)
281 {
282     // Obtain mantissa using equation 'mantissa = volts / 2^exponent'
283     double mantissa = volts / std::pow(2.0, static_cast<double>(exponent));
284 
285     // Return the mantissa value after converting to a rounded uint16_t
286     return static_cast<uint16_t>(std::lround(mantissa));
287 }
288 
289 } // namespace phosphor::power::regulators::pmbus_utils
290