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  * Data formats for output voltage.
64  *
65  * These formats are used for commanding and reading output voltage and related
66  * parameters.
67  */
68 enum class VoutDataFormat
69 {
70     /**
71      * Linear scale that uses a two byte unsigned binary integer with a scaling
72      * factor.
73      */
74     linear,
75 
76     /**
77      * Format that supports transmitting VID codes.
78      */
79     vid,
80 
81     /**
82      * Direct format that uses an equation and device supplied coefficients.
83      */
84     direct,
85 
86     /**
87      * Half-precision floating point format that follows the IEEE-754 standard
88      * for representing magnitudes in 16 bits.
89      */
90     ieee
91 };
92 
93 /**
94  * Parse the one byte value of the VOUT_MODE command.
95  *
96  * VOUT_MODE contains a 'mode' field that indicates the data format used for
97  * output voltage values.
98  *
99  * VOUT_MODE also contains a 'parameter' field whose value is dependent on the
100  * data format:
101  *  - Linear format: value is an exponent
102  *  - VID format: value is a VID code
103  *  - IEEE and Direct formats: value is not used
104  *
105  * @param voutModeValue one byte value of VOUT_MODE command
106  * @param format data format from the 'mode' field
107  * @param parameter parameter value from the 'parameter' field
108  */
109 void parseVoutMode(uint8_t voutModeValue, VoutDataFormat& format,
110                    int8_t& parameter);
111 
112 /**
113  * Converts the specified SensorDataFormat value to a string.
114  *
115  * @param format SensorDataFormat value
116  * @return string corresponding to the enum value
117  */
118 std::string toString(SensorDataFormat format);
119 
120 /**
121  * Converts the specified VoutDataFormat value to string.
122  *
123  * @param format VoutDataFormat format
124  * @return string corresponding to the enum value
125  */
126 std::string toString(VoutDataFormat format);
127 
128 /**
129  * Converts a linear data format value to a double value.
130  *
131  * This data format consists of the following:
132  *   - Two byte value
133  *   - 11-bit two's complement mantissa value stored in the two bytes
134  *   - 5-bit two's complement exponent value stored in the two bytes
135  *
136  * @param value linear data format value
137  * @return double value
138  */
139 inline double convertFromLinear(uint16_t value)
140 {
141     // extract exponent from most significant 5 bits
142     uint8_t exponentField = value >> 11;
143 
144     // extract mantissa from least significant 11 bits
145     uint16_t mantissaField = value & 0x7FFu;
146 
147     // sign extend exponent
148     if (exponentField > 0x0Fu)
149     {
150         exponentField |= 0xE0u;
151     }
152 
153     // sign extend mantissa
154     if (mantissaField > 0x03FFu)
155     {
156         mantissaField |= 0xF800u;
157     }
158 
159     int8_t exponent = static_cast<int8_t>(exponentField);
160     int16_t mantissa = static_cast<int16_t>(mantissaField);
161 
162     // compute value as mantissa * 2^(exponent)
163     double decimal = mantissa * std::pow(2.0, exponent);
164     return decimal;
165 }
166 
167 /**
168  * Converts a linear data format output voltage value to a volts value.
169  *
170  * This data format consists of the following:
171  *   - Two byte value
172  *   - 16-bit unsigned mantissa value stored in the two bytes
173  *   - 5-bit signed exponent value that is not stored in the two bytes
174  *
175  * @param value linear data format output voltage value
176  * @param exponent exponent value obtained from VOUT_MODE or device
177  *        documentation
178  * @return normal decimal number
179  */
180 inline double convertFromVoutLinear(uint16_t value, int8_t exponent)
181 {
182     // compute value as mantissa * 2^(exponent)
183     double decimal = value * std::pow(2.0, exponent);
184     return decimal;
185 }
186 
187 /**
188  * Converts a volts value to the linear data format for output voltage.
189  *
190  * This data format consists of the following:
191  *   - Two byte value
192  *   - 16-bit unsigned mantissa value stored in the two bytes
193  *   - 5-bit signed exponent value that is not stored in the two bytes
194  *
195  * The exponent value is typically obtained from the PMBus VOUT_MODE command
196  * or from the hardware device documentation (data sheet).
197  *
198  * Note that this format differs from the linear data format for values
199  * unrelated to output voltage.
200  *
201  * @param volts volts value to convert; must not be negative
202  * @param exponent 5-bit signed exponent used to convert value
203  * @return linear data format value
204  */
205 inline uint16_t convertToVoutLinear(double volts, int8_t exponent)
206 {
207     // Obtain mantissa using equation 'mantissa = volts / 2^exponent'
208     double mantissa = volts / std::pow(2.0, static_cast<double>(exponent));
209 
210     // Return the mantissa value after converting to a rounded uint16_t
211     return static_cast<uint16_t>(std::lround(mantissa));
212 }
213 
214 } // namespace phosphor::power::regulators::pmbus_utils
215