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 "action_environment.hpp"
19 #include "i2c_action.hpp"
20 #include "i2c_interface.hpp"
21 #include "pmbus_utils.hpp"
22 
23 #include <cstdint>
24 #include <optional>
25 #include <stdexcept>
26 #include <string>
27 
28 namespace phosphor::power::regulators
29 {
30 
31 /**
32  * @class PMBusWriteVoutCommandAction
33  *
34  * Writes the value of VOUT_COMMAND to set the output voltage of a PMBus
35  * regulator rail.  Communicates with the device directly using the I2C
36  * interface.
37  *
38  * Implements the pmbus_write_vout_command action in the JSON config file.
39  *
40  * The volts value to write can be specified in the constructor.  Otherwise, the
41  * volts value will be obtained from the ActionEnvironment.
42  *
43  * The PMBus specification defines four data formats for the value of
44  * VOUT_COMMAND:
45  * - Linear
46  * - VID
47  * - Direct
48  * - IEEE Half-Precision Floating Point
49  * Currently only the linear data format is supported.  The volts value is
50  * converted into linear format before being written.
51  *
52  * The linear data format requires an exponent value.  The exponent value can be
53  * specified in the constructor.  Otherwise the exponent value will be obtained
54  * from the PMBus VOUT_MODE command.  Note that some PMBus devices do not
55  * support the VOUT_MODE command.  The exponent value for a device is often
56  * found in the device documentation (data sheet).
57  *
58  * If desired, write verification can be performed.  The value of VOUT_COMMAND
59  * will be read from the device after it is written to ensure that it contains
60  * the expected value.  If VOUT_COMMAND contains an unexpected value, a
61  * WriteVerificationError is thrown.  To perform verification, the device must
62  * return all 16 bits of voltage data that were written to VOUT_COMMAND.
63  */
64 class PMBusWriteVoutCommandAction : public I2CAction
65 {
66   public:
67     // Specify which compiler-generated methods we want
68     PMBusWriteVoutCommandAction() = delete;
69     PMBusWriteVoutCommandAction(const PMBusWriteVoutCommandAction&) = delete;
70     PMBusWriteVoutCommandAction(PMBusWriteVoutCommandAction&&) = delete;
71     PMBusWriteVoutCommandAction&
72         operator=(const PMBusWriteVoutCommandAction&) = delete;
73     PMBusWriteVoutCommandAction&
74         operator=(PMBusWriteVoutCommandAction&&) = delete;
75     virtual ~PMBusWriteVoutCommandAction() = default;
76 
77     /**
78      * Constructor.
79      *
80      * Throws an exception if any of the input parameters are invalid.
81      *
82      * @param volts Optional volts value to write to VOUT_COMMAND.
83      * @param format Data format of the volts value written to VOUT_COMMAND.
84      *               Currently the only supported format is linear.
85      * @param exponent Optional exponent to use to convert the volts value to
86      *                 linear data format.
87      * @param isVerified Specifies whether the updated value of VOUT_COMMAND is
88      *                   verified by reading it from the device.
89      */
PMBusWriteVoutCommandAction(std::optional<double> volts,pmbus_utils::VoutDataFormat format,std::optional<int8_t> exponent,bool isVerified)90     explicit PMBusWriteVoutCommandAction(
91         std::optional<double> volts, pmbus_utils::VoutDataFormat format,
92         std::optional<int8_t> exponent, bool isVerified) :
93         volts{volts}, format{format}, exponent{exponent},
94         isWriteVerified{isVerified}
95     {
96         // Currently only linear format is supported
97         if (format != pmbus_utils::VoutDataFormat::linear)
98         {
99             throw std::invalid_argument{"Unsupported data format specified"};
100         }
101     }
102 
103     /**
104      * Executes this action.
105      *
106      * Writes a volts value to VOUT_COMMAND using the I2C interface.
107      *
108      * If a volts value was specified in the constructor, that value will be
109      * used.  Otherwise the volts value will be obtained from the
110      * ActionEnvironment.
111      *
112      * The data format is specified in the constructor.  Currently only the
113      * linear format is supported.
114      *
115      * An exponent value is required to convert the volts value to linear
116      * format.  If an exponent value was specified in the constructor, that
117      * value will be used.  Otherwise the exponent value will be obtained from
118      * the VOUT_MODE command.
119      *
120      * Write verification will be performed if specified in the constructor.
121      *
122      * The device is obtained from the ActionEnvironment.
123      *
124      * Throws an exception if an error occurs.
125      *
126      * @param environment action execution environment
127      * @return true
128      */
129     virtual bool execute(ActionEnvironment& environment) override;
130 
131     /**
132      * Returns the optional exponent value used to convert the volts value to
133      * linear data format.
134      *
135      * @return optional exponent value
136      */
getExponent() const137     std::optional<int8_t> getExponent() const
138     {
139         return exponent;
140     }
141 
142     /**
143      * Returns the data format of the value written to VOUT_COMMAND.
144      *
145      * @return data format
146      */
getFormat() const147     pmbus_utils::VoutDataFormat getFormat() const
148     {
149         return format;
150     }
151 
152     /**
153      * Returns the optional volts value to write to VOUT_COMMAND.
154      *
155      * @return optional volts value
156      */
getVolts() const157     std::optional<double> getVolts() const
158     {
159         return volts;
160     }
161 
162     /**
163      * Returns whether write verification will be performed when writing to
164      * VOUT_COMMAND.
165      *
166      * @return true if write verification will be performed, false otherwise
167      */
isVerified() const168     bool isVerified() const
169     {
170         return isWriteVerified;
171     }
172 
173     /**
174      * Returns a string description of this action.
175      *
176      * @return description of action
177      */
178     virtual std::string toString() const override;
179 
180   private:
181     /**
182      * Gets the exponent value to use to convert the volts value to linear data
183      * format.
184      *
185      * If an exponent value is defined for this action, that value is returned.
186      * Otherwise VOUT_MODE is read from the current device to obtain the
187      * exponent value.
188      *
189      * Throws an exception if an error occurs.
190      *
191      * @param environment action execution environment
192      * @param interface I2C interface to the current device
193      * @return exponent value
194      */
195     int8_t getExponentValue(ActionEnvironment& environment,
196                             i2c::I2CInterface& interface);
197 
198     /**
199      * Gets the volts value to write to VOUT_COMMAND.
200      *
201      * If a volts value is defined for this action, that value is returned.
202      * Otherwise the volts value is obtained from the specified
203      * ActionEnvironment.
204      *
205      * Throws an exception if no volts value is defined.
206      *
207      * @param environment action execution environment
208      * @return volts value
209      */
210     double getVoltsValue(ActionEnvironment& environment);
211 
212     /**
213      * Verifies the value written to VOUT_COMMAND.  Reads the current value of
214      * VOUT_COMMAND and ensures that it matches the value written.
215      *
216      * Throws an exception if the values do not match or a communication error
217      * occurs.
218      *
219      * @param environment action execution environment
220      * @param interface I2C interface to the current device
221      * @param valueWritten linear format volts value written to VOUT_COMMAND
222      */
223     void verifyWrite(ActionEnvironment& environment,
224                      i2c::I2CInterface& interface, uint16_t valueWritten);
225 
226     /**
227      * Optional volts value to write.
228      */
229     const std::optional<double> volts{};
230 
231     /**
232      * Data format of the volts value written to VOUT_COMMAND.
233      */
234     const pmbus_utils::VoutDataFormat format{};
235 
236     /**
237      * Optional exponent value to use to convert the volts value to linear data
238      * format.
239      */
240     const std::optional<int8_t> exponent{};
241 
242     /**
243      * Indicates whether write verification will be performed when writing to
244      * VOUT_COMMAND.
245      */
246     const bool isWriteVerified{false};
247 };
248 
249 } // namespace phosphor::power::regulators
250