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      */
90     explicit PMBusWriteVoutCommandAction(std::optional<double> volts,
91                                          pmbus_utils::VoutDataFormat format,
92                                          std::optional<int8_t> exponent,
93                                          bool isVerified) :
94         volts{volts},
95         format{format}, exponent{exponent}, isWriteVerified{isVerified}
96     {
97         // Currently only linear format is supported
98         if (format != pmbus_utils::VoutDataFormat::linear)
99         {
100             throw std::invalid_argument{"Unsupported data format specified"};
101         }
102     }
103 
104     /**
105      * Executes this action.
106      *
107      * Writes a volts value to VOUT_COMMAND using the I2C interface.
108      *
109      * If a volts value was specified in the constructor, that value will be
110      * used.  Otherwise the volts value will be obtained from the
111      * ActionEnvironment.
112      *
113      * The data format is specified in the constructor.  Currently only the
114      * linear format is supported.
115      *
116      * An exponent value is required to convert the volts value to linear
117      * format.  If an exponent value was specified in the constructor, that
118      * value will be used.  Otherwise the exponent value will be obtained from
119      * the VOUT_MODE command.
120      *
121      * Write verification will be performed if specified in the constructor.
122      *
123      * The device is obtained from the ActionEnvironment.
124      *
125      * Throws an exception if an error occurs.
126      *
127      * @param environment action execution environment
128      * @return true
129      */
130     virtual bool execute(ActionEnvironment& environment) override;
131 
132     /**
133      * Returns the optional exponent value used to convert the volts value to
134      * linear data format.
135      *
136      * @return optional exponent value
137      */
138     std::optional<int8_t> getExponent() const
139     {
140         return exponent;
141     }
142 
143     /**
144      * Returns the data format of the value written to VOUT_COMMAND.
145      *
146      * @return data format
147      */
148     pmbus_utils::VoutDataFormat getFormat() const
149     {
150         return format;
151     }
152 
153     /**
154      * Returns the optional volts value to write to VOUT_COMMAND.
155      *
156      * @return optional volts value
157      */
158     std::optional<double> getVolts() const
159     {
160         return volts;
161     }
162 
163     /**
164      * Returns whether write verification will be performed when writing to
165      * VOUT_COMMAND.
166      *
167      * @return true if write verification will be performed, false otherwise
168      */
169     bool isVerified() const
170     {
171         return isWriteVerified;
172     }
173 
174     /**
175      * Returns a string description of this action.
176      *
177      * @return description of action
178      */
179     virtual std::string toString() const override;
180 
181   private:
182     /**
183      * Gets the exponent value to use to convert the volts value to linear data
184      * format.
185      *
186      * If an exponent value is defined for this action, that value is returned.
187      * Otherwise VOUT_MODE is read from the current device to obtain the
188      * exponent value.
189      *
190      * Throws an exception if an error occurs.
191      *
192      * @param environment action execution environment
193      * @param interface I2C interface to the current device
194      * @return exponent value
195      */
196     int8_t getExponentValue(ActionEnvironment& environment,
197                             i2c::I2CInterface& interface);
198 
199     /**
200      * Gets the volts value to write to VOUT_COMMAND.
201      *
202      * If a volts value is defined for this action, that value is returned.
203      * Otherwise the volts value is obtained from the specified
204      * ActionEnvironment.
205      *
206      * Throws an exception if no volts value is defined.
207      *
208      * @param environment action execution environment
209      * @return volts value
210      */
211     double getVoltsValue(ActionEnvironment& environment);
212 
213     /**
214      * Verifies the value written to VOUT_COMMAND.  Reads the current value of
215      * VOUT_COMMAND and ensures that it matches the value written.
216      *
217      * Throws an exception if the values do not match or a communication error
218      * occurs.
219      *
220      * @param environment action execution environment
221      * @param interface I2C interface to the current device
222      * @param valueWritten linear format volts value written to VOUT_COMMAND
223      */
224     void verifyWrite(ActionEnvironment& environment,
225                      i2c::I2CInterface& interface, uint16_t valueWritten);
226 
227     /**
228      * Optional volts value to write.
229      */
230     const std::optional<double> volts{};
231 
232     /**
233      * Data format of the volts value written to VOUT_COMMAND.
234      */
235     const pmbus_utils::VoutDataFormat format{};
236 
237     /**
238      * Optional exponent value to use to convert the volts value to linear data
239      * format.
240      */
241     const std::optional<int8_t> exponent{};
242 
243     /**
244      * Indicates whether write verification will be performed when writing to
245      * VOUT_COMMAND.
246      */
247     const bool isWriteVerified{false};
248 };
249 
250 } // namespace phosphor::power::regulators
251