xref: /openbmc/phosphor-pid-control/pid/thermalcontroller.cpp (revision 46a755fce8dc0bdd9c0c5ea09d55d3e5494f335f)
1 // SPDX-License-Identifier: Apache-2.0
2 // SPDX-FileCopyrightText: Copyright 2017 Google Inc
3 
4 #include "thermalcontroller.hpp"
5 
6 #include "conf.hpp"
7 #include "ec/pid.hpp"
8 #include "errors/exception.hpp"
9 #include "pidcontroller.hpp"
10 #include "tuning.hpp"
11 #include "util.hpp"
12 #include "zone_interface.hpp"
13 
14 #include <algorithm>
15 #include <cmath>
16 #include <iostream>
17 #include <limits>
18 #include <memory>
19 #include <string>
20 #include <vector>
21 
22 namespace pid_control
23 {
24 
getThermalType(const std::string & typeString)25 ThermalType getThermalType(const std::string& typeString)
26 {
27     if (typeString == "margin")
28     {
29         return ThermalType::margin;
30     }
31     if ((typeString == "temp") || (typeString == "power"))
32     {
33         return ThermalType::absolute;
34     }
35     if (typeString == "powersum")
36     {
37         return ThermalType::summation;
38     }
39 
40     throw ControllerBuildException("Unrecognized PID Type/Class string");
41 }
42 
isThermalType(const std::string & typeString)43 bool isThermalType(const std::string& typeString)
44 {
45     static const std::vector<std::string> thermalTypes = {
46         "temp", "margin", "power", "powersum"};
47     return std::count(thermalTypes.begin(), thermalTypes.end(), typeString);
48 }
49 
createThermalPid(ZoneInterface * owner,const std::string & id,const std::vector<pid_control::conf::SensorInput> & inputs,double setpoint,const ec::pidinfo & initial,const ThermalType & type)50 std::unique_ptr<PIDController> ThermalController::createThermalPid(
51     ZoneInterface* owner, const std::string& id,
52     const std::vector<pid_control::conf::SensorInput>& inputs, double setpoint,
53     const ec::pidinfo& initial, const ThermalType& type)
54 {
55     // ThermalController requires at least 1 input
56     if (inputs.empty())
57     {
58         throw ControllerBuildException("Thermal controller missing inputs");
59     }
60 
61     auto thermal = std::make_unique<ThermalController>(id, inputs, type, owner);
62 
63     ec::pid_info_t* info = thermal->getPIDInfo();
64     thermal->setSetpoint(setpoint);
65 
66     initializePIDStruct(info, initial);
67 
68     return thermal;
69 }
70 
71 // bmc_host_sensor_value_double
inputProc(void)72 double ThermalController::inputProc(void)
73 {
74     double value;
75     const double& (*compare)(const double&, const double&);
76     bool doSummation = false;
77 
78     if (type == ThermalType::margin)
79     {
80         value = std::numeric_limits<double>::max();
81         compare = std::min<double>;
82     }
83     else if (type == ThermalType::absolute)
84     {
85         value = std::numeric_limits<double>::lowest();
86         compare = std::max<double>;
87     }
88     else if (type == ThermalType::summation)
89     {
90         doSummation = true;
91         value = 0.0;
92     }
93     else
94     {
95         throw ControllerBuildException("Unrecognized ThermalType");
96     }
97 
98     std::string leaderName = _inputs.begin()->name;
99 
100     bool acceptable = false;
101     for (const auto& in : _inputs)
102     {
103         double cachedValue = _owner->getCachedValue(in.name);
104 
105         // Less than 0 is perfectly OK for temperature, but must not be NAN
106         if (!(std::isfinite(cachedValue)))
107         {
108             continue;
109         }
110 
111         // Perform TempToMargin conversion before further processing
112         if (type == ThermalType::margin)
113         {
114             if (in.convertTempToMargin)
115             {
116                 if (!(std::isfinite(in.convertMarginZero)))
117                 {
118                     throw ControllerBuildException("Unrecognized TempToMargin");
119                 }
120 
121                 double marginValue = in.convertMarginZero - cachedValue;
122 
123                 if (debugEnabled)
124                 {
125                     std::cerr
126                         << "Converting temp to margin: temp " << cachedValue
127                         << ", Tjmax " << in.convertMarginZero << ", margin "
128                         << marginValue << "\n";
129                 }
130 
131                 cachedValue = marginValue;
132             }
133         }
134 
135         double oldValue = value;
136 
137         if (doSummation)
138         {
139             value += cachedValue;
140         }
141         else
142         {
143             value = compare(value, cachedValue);
144         }
145 
146         if (oldValue != value)
147         {
148             leaderName = in.name;
149             _owner->updateThermalPowerDebugInterface(_id, leaderName, value, 0);
150         }
151 
152         acceptable = true;
153     }
154 
155     if (!acceptable)
156     {
157         // If none of the inputs were acceptable, use the setpoint as
158         // the input value. This will continue to run the PID loop, but
159         // make it a no-op, as the error will be zero. This provides safe
160         // behavior until the inputs become acceptable.
161         value = setptProc();
162     }
163 
164     if (debugEnabled)
165     {
166         std::cerr << getID() << " choose the temperature value: " << value
167                   << " " << leaderName << "\n";
168     }
169 
170     return value;
171 }
172 
173 // bmc_get_setpt
setptProc(void)174 double ThermalController::setptProc(void)
175 {
176     double setpoint = getSetpoint();
177 
178     /* TODO(venture): Thermal setpoint invalid? */
179 #if 0
180     if (-1 == setpoint)
181     {
182         return 0.0f;
183     }
184     else
185     {
186         return setpoint;
187     }
188 #endif
189     return setpoint;
190 }
191 
192 // bmc_set_pid_output
outputProc(double value)193 void ThermalController::outputProc(double value)
194 {
195     _owner->addSetPoint(value, _id);
196     _owner->updateThermalPowerDebugInterface(_id, "", 0, value);
197 
198     if (debugEnabled)
199     {
200         std::cerr << getID() << " pid output pwm: " << value << "\n";
201     }
202 
203     return;
204 }
205 
206 } // namespace pid_control
207