1# How to Configure Phosphor-pid-control 2 3A system needs two groups of configurations: zones and sensors. 4 5They can come either from a dedicated config file or via D-Bus from 6e.g. `entity-manager`. 7 8## D-Bus Configuration 9 10If config file does not exist the configuration is obtained from a set of D-Bus 11interfaces. When using `entity-manager` to provide them refer to `Pid`, 12`Pid.Zone` and `Stepwise` 13[schemas](https://github.com/openbmc/entity-manager/tree/master/schemas). The 14key names are not identical to JSON but similar enough to see the 15correspondence. 16 17## JSON Configuration 18 19Default config file path `/usr/share/swampd/config.json` can be overridden by 20using `--conf` command line option. 21 22The JSON object should be a dictionary with two keys, `sensors` and `zones`. 23`sensors` is a list of the sensor dictionaries, whereas `zones` is a list of 24zones. 25 26### Sensors 27 28``` 29"sensors" : [ 30 { 31 "name": "fan1", 32 "type": "fan", 33 "readPath": "/xyz/openbmc_project/sensors/fan_tach/fan1", 34 "writePath": "/sys/devices/platform/ahb/ahb:apb/1e786000.pwm-tacho-controller/hwmon/**/pwm1", 35 "min": 0, 36 "max": 255, 37 "ignoreDbusMinMax": true 38 }, 39 { 40 "name": "fan2", 41 "type": "fan", 42 "readPath": "/xyz/openbmc_project/sensors/fan_tach/fan2", 43 "writePath": "/sys/devices/platform/ahb/ahb:apb/1e786000.pwm-tacho-controller/hwmon/**/pwm2", 44 "min": 0, 45 "max": 255, 46 "timeout": 4, 47 }, 48... 49``` 50 51A sensor has a `name`, a `type`, a `readPath`, a `writePath`, a `minimum` value, 52a `maximum` value, a `timeout`, and a `ignoreDbusMinMax` value. 53 54The `name` is used to reference the sensor in the zone portion of the 55configuration. 56 57The `type` is the type of sensor it is. This influences how its value is 58treated. Supported values are: `fan`, `temp`, and `margin`. 59 60The `readPath` is the path that tells the daemon how to read the value from this 61sensor. It is optional, allowing for write-only sensors. If the value is absent 62or `None` it'll be treated as a write-only sensor. 63 64If the `readPath` value contains: `/xyz/openbmc_project/extsensors/` it'll be 65treated as a sensor hosted by the daemon itself whose value is provided 66externally. The daemon will own the sensor and publish it to dbus. This is 67currently only supported for `temp` and `margin` sensor types. 68 69If the `readPath` value contains: `/xyz/openbmc_project/` (this is checked after 70external), then it's treated as a passive dbus sensor. A passive dbus sensor is 71one that listens for property updates to receive its value instead of actively 72reading the `Value` property. 73 74If the `readPath` value contains: `/sys/` this is treated as a directly read 75sysfs path. There are two supported paths: 76 77* `/sys/class/hwmon/hwmon0/pwm1` 78* `/sys/devices/platform/ahb/1e786000.pwm-tacho-controller/hwmon/<asterisk 79 asterisk>/pwm1` 80 81The `writePath` is the path to set the value for the sensor. This is only valid 82for a sensor of type `fan`. The path is optional. If can be empty or `None`. It 83then only supports two options. 84 85If the `writePath` value contains: `/sys/` this is treated as a directory 86written sysfs path. There are two support paths: 87 88* `/sys/class/hwmon/hwmon0/pwm1` 89* `/sys/devices/platform/ahb/1e786000.pwm-tacho-controller/hwmon/<asterisk 90 asterisk>/pwm1` 91 92If the `writePath` value contains: `/xyz/openbmc_project/sensors/fan_tach/fan{N}` it 93sets of a sensor object that writes over dbus to the 94`xyz.openbmc_project.Control.FanPwm` interface. The `writePath` should be the 95full object path. 96 97``` 98busctl introspect xyz.openbmc_project.Hwmon-1644477290.Hwmon1 /xyz/openbmc_project/sensors/fan_tach/fan1 --no-pager 99NAME TYPE SIGNATURE RESULT/VALUE FLAGS 100org.freedesktop.DBus.Introspectable interface - - - 101.Introspect method - s - 102org.freedesktop.DBus.Peer interface - - - 103.GetMachineId method - s - 104.Ping method - - - 105org.freedesktop.DBus.Properties interface - - - 106.Get method ss v - 107.GetAll method s a{sv} - 108.Set method ssv - - 109.PropertiesChanged signal sa{sv}as - - 110xyz.openbmc_project.Control.FanPwm interface - - - 111.Target property t 255 emits-change writable 112xyz.openbmc_project.Sensor.Value interface - - - 113.MaxValue property x 0 emits-change writable 114.MinValue property x 0 emits-change writable 115.Scale property x 0 emits-change writable 116.Unit property s "xyz.openbmc_project.Sensor.Value.Uni... emits-change writable 117.Value property x 2823 emits-change writable 118``` 119 120The `minimum` and `maximum` values are optional. When `maximum` is non-zero it 121expects to write a percentage value converted to a value between the minimum and 122maximum. 123 124The `timeout` value is optional and controls the sensor failure behavior. If a 125sensor is a fan the default value is 2 seconds, otherwise it's 0. When a 126sensor's timeout is 0 it isn't checked against a read timeout failure case. If a 127sensor fails to be read within the timeout period, the zone goes into failsafe 128to handle the case where it doesn't know what to do -- as it doesn't have all 129its inputs. 130 131The `ignoreDbusMinMax` value is optional and defaults to false. The dbus 132passive sensors check for a `MinValue` and `MaxValue` and scale the incoming 133values via these. Setting this property to true will ignore `MinValue` and 134`MaxValue` from dbus and therefore won't call any passive value scaling. 135 136### Zones 137 138``` 139"zones" : [ 140 { 141 "id": 1, 142 "minThermalOutput": 3000.0, 143 "failsafePercent": 75.0, 144 "pids": [], 145... 146``` 147 148Each zone has its own fields, and a list of controllers. 149 150| field | type | meaning | 151| ------------------ | --------- | ----------------------------------------- | 152| `id` | `int64_t` | This is a unique identifier for the zone. | 153| `minThermalOutput` | `double` | This is the minimum value that should be considered from the thermal outputs. Commonly used as the minimum fan RPM.| 154| `failsafePercent` | `double` | If there is a fan PID, it will use this value if the zone goes into fail-safe as the output value written to the fan's sensors.| 155| `pids` | `list of strings` | Fan and thermal controllers used by the zone.| 156 157The `id` field here is used in the d-bus path to talk to the 158`xyz.openbmc_project.Control.Mode` interface. 159 160***TODO:*** Examine how the fan controller always treating its output as a 161percentage works for future cases. 162 163A zone collects all the setpoints and ceilings from the thermal 164controllers attached to it, selects the maximum setpoint, clamps it by 165the minimum ceiling and `minThermalOutput`; the result is used to 166control fans. 167 168### Controllers 169 170There are `fan`, `temp`, `margin` (PID), and `stepwise` (discrete steps) 171controllers. 172 173The `fan` PID is meant to drive fans or other cooling devices. It's 174expecting to get the setpoint value from the owning zone and then 175drive the fans to that value. 176 177A `temp` PID is meant to drive the setpoint given an absolute 178temperature value (higher value indicates warmer temperature). 179 180A `margin` PID is meant to drive the setpoint given a margin value 181(lower value indicates warmer temperature, in other words, it's the 182safety margin remaining expressed in degrees Celsius). 183 184The setpoint output from the thermal controllers is called `RPMSetpoint()` 185However, it doesn't need to be an RPM value. 186 187***TODO:*** Rename this method and others to not say necessarily RPM. 188 189Some PID configurations have fields in common, but may be interpreted 190differently. 191 192When using D-Bus, each configuration can have a list of strings called 193`Profiles`. In this case the controller will be loaded only if at 194least one of them is returned as `Current` from an object implementing 195`xyz.openbmc_project.Control.ThermalMode` interface (which can be 196anywhere on D-Bus). `swampd` will automatically reload full 197configuration whenever `Current` is changed. 198 199D-Bus `Name` attribute is used for indexing in certain cases so should 200be unique for all defined configurations. 201 202#### PID Field 203 204If the PID `type` is not `stepwise` then the PID field is defined as follows: 205 206| field | type | meaning | 207| -------------------- | -------- | ----------------------------------------- | 208| `samplePeriod` | `double` | How frequently the value is sampled. 0.1 for fans, 1.0 for temperatures.| 209| `proportionalCoeff` | `double` | The proportional coefficient. | 210| `integralCoeff` | `double` | The integral coefficient. | 211| `feedFwdOffsetCoeff` | `double` | The feed forward offset coefficient. | 212| `feedFwdGainCoeff` | `double` | The feed forward gain coefficient. | 213| `integralLimit_min` | `double` | The integral minimum clamp value. | 214| `integralLimit_max` | `double` | The integral maximum clamp value. | 215| `outLim_min` | `double` | The output minimum clamp value. | 216| `outLim_max` | `double` | The output maximum clamp value. | 217| `slewNeg` | `double` | Negative slew value to dampen output. | 218| `slewPos` | `double` | Positive slew value to accelerate output. | 219 220The units for the coefficients depend on the configuration of the PIDs. 221 222If the PID is a `margin` controller and its `setpoint` is in centigrade and 223output in RPM: proportionalCoeff is your p value in units: RPM/C and integral 224coefficient: RPM/C sec 225 226If the PID is a fan controller whose output is pwm: proportionalCoeff is %/RPM 227and integralCoeff is %/RPM sec. 228 229***NOTE:*** The sample periods are specified in the configuration as they are 230used in the PID computations, however, they are not truly configurable as they 231are used for the update periods for the fan and thermal sensors. 232 233#### type == "fan" 234 235``` 236"name": "fan1-5", 237"type": "fan", 238"inputs": ["fan1", "fan5"], 239"setpoint": 90.0, 240"pid": { 241... 242} 243``` 244 245The type `fan` builds a `FanController` PID. 246 247| field | type | meaning | 248| ---------- | ----------------- | ------------------------------------------- | 249| `name` | `string` | The name of the PID. This is just for humans and logging.| 250| `type` | `string` | `fan` | 251| `inputs` | `list of strings` | The names of the sensor(s) that are used as input and output for the PID loop.| 252| `setpoint` | `double` | Presently UNUSED | 253| `pid` | `dictionary` | A PID dictionary detailed above. | 254 255#### type == "margin" 256 257``` 258"name": "fleetingpid0", 259"type": "margin", 260"inputs": ["fleeting0"], 261"setpoint": 10, 262"pid": { 263... 264} 265``` 266 267The type `margin` builds a `ThermalController` PID. 268 269| field | type | meaning | 270| ---------- | ----------------- | ------------------------------------------- | 271| `name` | `string` | The name of the PID. This is just for humans and logging.| 272| `type` | `string` | `margin` | 273| `inputs` | `list of strings` | The names of the sensor(s) that are used as input for the PID loop.| 274| `setpoint` | `double` | The setpoint value for the thermal PID. The setpoint for the margin sensors.| 275| `pid` | `dictionary` | A PID dictionary detailed above. | 276 277Each input is normally a temperature difference between some hardware 278threshold and the current state. E.g. a CPU sensor can be reporting 279that it's 20 degrees below the point when it starts thermal 280throttling. So the lower the margin temperature, the higher the 281corresponding absolute value. 282 283Out of all the `inputs` the minimal value is selected and used as an 284input for the PID loop. 285 286The output of a `margin` PID loop is that it sets the setpoint value for the 287zone. It does this by adding the value to a list of values. The value chosen by 288the fan PIDs (in this cascade configuration) is the maximum value. 289 290#### type == "temp" 291 292Exactly the same as `margin` but all the inputs are supposed to be 293absolute temperatures and so the maximal value is used to feed the PID 294loop. 295 296#### type == "stepwise" 297``` 298"name": "temp1", 299"type": "stepwise", 300"inputs": ["temp1"], 301"setpoint": 30.0, 302"pid": { 303 "samplePeriod": 0.1, 304 "positiveHysteresis": 1.0, 305 "negativeHysteresis": 1.0, 306 "isCeiling": false, 307 "reading": { 308 "0": 45, 309 "1": 46, 310 "2": 47, 311 }, 312 "output": { 313 "0": 5000, 314 "1": 2400, 315 "2": 2600, 316 } 317} 318``` 319 320The type `stepwise` builds a `StepwiseController`. 321 322| field | type | meaning | 323| ---------- | ----------------- | ------------------------------------------- | 324| `name` | `string` | The name of the controller. This is just for humans and logging. | 325| `type` | `string` | `stepwise` | 326| `inputs` | `list of strings` | The names of the sensor(s) that are used as input and output for the controller. | 327| `pid` | `dictionary` | A controller settings dictionary detailed below. | 328 329The `pid` dictionary (confusingly named) is defined as follows: 330 331| field | type | meaning | 332| -------------------- | -------- | ----------------------------------------- | 333| `samplePeriod` | `double` | Presently UNUSED. | 334| `reading` | `dictionary` | Enumerated list of input values, indexed from 0, must be monotonically increasing, maximum 20 items. | 335| `output` | `dictionary` | Enumerated list of output values, indexed from 0, must match the amount of `reading` items. | 336| `positiveHysteresis` | `double` | How much the input value must raise to allow the switch to the next step. | 337| `negativeHysteresis` | `double` | How much the input value must drop to allow the switch to the previous step. | 338| `isCeiling` | `bool` | Whether this controller provides a setpoint or a ceiling for the zone | 339| `setpoint` | `double` | Presently UNUSED. | 340 341***NOTE:*** `reading` and `output` are normal arrays and not embedded 342in the dictionary in Entity Manager. 343 344Each measurement cycle out of all the `inputs` the maximum value is 345selected. Then it's compared to the list of `reading` values finding 346the largest that's still lower or equal the input (the very first item 347is used even if it's larger than the input). The corresponding 348`output` value is selected if hysteresis allows the switch (the 349current input value is compared with the input present at the moment 350of the previous switch). The result is added to the list of setpoints 351or ceilings for the zone depending on `isCeiling` setting. 352