1# How to Configure Phosphor-pid-control 2 3A system needs two groups of configurations: zones and sensors. 4 5## Json Configuration 6 7The json object should be a dictionary with two keys, `sensors` and `zones`. 8`sensors` is a list of the sensor dictionaries, whereas `zones` is a list of 9zones. 10 11### Sensors 12 13``` 14"sensors" : [ 15 { 16 "name": "fan1", 17 "type": "fan", 18 "readPath": "/xyz/openbmc_project/sensors/fan_tach/fan1", 19 "writePath": "/sys/devices/platform/ahb/ahb:apb/1e786000.pwm-tacho-controller/hwmon/**/pwm1", 20 "min": 0, 21 "max": 255 22 }, 23 { 24 "name": "fan2", 25 "type": "fan", 26 "readPath": "/xyz/openbmc_project/sensors/fan_tach/fan2", 27 "writePath": "/sys/devices/platform/ahb/ahb:apb/1e786000.pwm-tacho-controller/hwmon/**/pwm2", 28 "min": 0, 29 "max": 255, 30 "timeout": 4, 31 }, 32... 33``` 34 35A sensor has a `name`, a `type`, a `readPath`, a `writePath`, a `minimum` value, 36a `maximum` value, and a `timeout`. 37 38The `name` is used to reference the sensor in the zone portion of the 39configuration. 40 41The `type` is the type of sensor it is. This influences how its value is 42treated. Supports values are: `fan`, `temp`, and `margin`. 43 44**TODO: Add further details on what the types mean.** 45 46The `readPath` is the path that tells the daemon how to read the value from this 47sensor. It is optional, allowing for write-only sensors. If the value is absent 48or `None` it'll be treated as a write-only sensor. 49 50If the `readPath` value contains: `/xyz/openbmc_project/extsensors/` it'll be 51treated as a sensor hosted by the daemon itself whose value is provided 52externally. The daemon will own the sensor and publish it to dbus. This is 53currently only supported for `temp` and `margin` sensor types. 54 55If the `readPath` value contains: `/xyz/openbmc_project/` (this is checked after 56external), then it's treated as a passive dbus sensor. A passive dbus sensor is 57one that listens for property updates to receive its value instead of actively 58reading the `Value` property. 59 60If the `readPath` value contains: `/sys/` this is treated as a directly read 61sysfs path. There are two supported paths: 62 63* `/sys/class/hwmon/hwmon0/pwm1` 64* `/sys/devices/platform/ahb/1e786000.pwm-tacho-controller/hwmon/<asterisk 65 asterisk>/pwm1` 66 67The `writePath` is the path to set the value for the sensor. This is only valid 68for a sensor of type `fan`. The path is optional. If can be empty or `None`. It 69then only supports two options. 70 71If the `writePath` value contains: `/sys/` this is treated as a directory 72written sysfs path. There are two support paths: 73 74* `/sys/class/hwmon/hwmon0/pwm1` 75* `/sys/devices/platform/ahb/1e786000.pwm-tacho-controller/hwmon/<asterisk 76 asterisk>/pwm1` 77 78If the `writePath` value contains: `/xyz/openbmc_project/control/fanpwm/` it 79sets of a sensor object that writes over dbus to the 80`xyz.openbmc_project.Control.FanPwm` interface. The `writePath` should end with 81the sensor's dbus name. 82 83**BUG NOTE** It's currently using the writePath specified for fanpwm as the 84sensor path, when in fact, the interface is attached to: 85 86``` 87busctl introspect xyz.openbmc_project.Hwmon-1644477290.Hwmon1 /xyz/openbmc_project/sensors/fan_tach/fan1 --no-pager 88NAME TYPE SIGNATURE RESULT/VALUE FLAGS 89org.freedesktop.DBus.Introspectable interface - - - 90.Introspect method - s - 91org.freedesktop.DBus.Peer interface - - - 92.GetMachineId method - s - 93.Ping method - - - 94org.freedesktop.DBus.Properties interface - - - 95.Get method ss v - 96.GetAll method s a{sv} - 97.Set method ssv - - 98.PropertiesChanged signal sa{sv}as - - 99xyz.openbmc_project.Control.FanPwm interface - - - 100.Target property t 255 emits-change writable 101xyz.openbmc_project.Sensor.Value interface - - - 102.MaxValue property x 0 emits-change writable 103.MinValue property x 0 emits-change writable 104.Scale property x 0 emits-change writable 105.Unit property s "xyz.openbmc_project.Sensor.Value.Uni... emits-change writable 106.Value property x 2823 emits-change writable 107``` 108 109The `minimum` and `maximum` values are optional. When `maximum` is non-zero it 110expects to write a percentage value converted to a value between the minimum and 111maximum. 112 113The `timeout` value is optional and controls the sensor failure behavior. If a 114sensor is a fan the default value is 2 seconds, otherwise it's 0. When a 115sensor's timeout is 0 it isn't checked against a read timeout failure case. If a 116sensor fails to be read within the timeout period, the zone goes into failsafe 117to handle the case where it doesn't know what to do -- as it doesn't have all 118its inputs. 119 120### Zones 121 122``` 123"zones" : [ 124 { 125 "id": 1, 126 "minThermalOutput": 3000.0, 127 "failsafePercent": 75.0, 128 "pids": [], 129... 130``` 131 132Each zone has its own fields, and a list of PIDs. 133 134| field | type | meaning | 135| ------------------ | --------- | ----------------------------------------- | 136| `id` | `int64_t` | This is a unique identifier for the zone. | 137| `minThermalOutput` | `double` | This is the minimum value that should be considered from the thermal outputs. Commonly used as the minimum fan RPM.| 138| `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.| 139 140The `id` field here is used in the d-bus path to talk to the 141`xyz.openbmc_project.Control.Mode` interface. 142 143***TODO:*** Examine how the fan controller always treating its output as a 144percentage works for future cases. 145 146### PIDs 147 148There are a few PID types: `fan`, `temp`, `margin`, and `stepwise`. 149 150The `fan` PID is meant to drive fans. It's expecting to get the maximum RPM 151setpoint value from the owning zone and then drive the fans to that value. 152 153A `temp` PID is meant to drive the RPM setpoint given an absolute temperature 154value (higher value indicates a warmer temperature). 155 156A `margin` PID is meant to drive the RPM setpoint given a margin value (lower 157value indicates a warmer temperature). 158 159The setpoint output from the thermal controllers is called `RPMSetpoint()` 160However, it doesn't need to be an RPM value. 161 162***TODO:*** Rename this method and others to not say necessarily RPM. 163 164Some PID configurations have fields in common, but may be interpreted 165differently. 166 167#### PID Field 168 169If the PID `type` is not `stepwise` then the PID field is defined as follows: 170 171| field | type | meaning | 172| -------------------- | -------- | ----------------------------------------- | 173| `samplePeriod` | `double` | How frequently the value is sampled. 0.1 for fans, 1.0 for temperatures.| 174| `proportionalCoeff` | `double` | The proportional coefficient. | 175| `integralCoeff` | `double` | The integral coefficient. | 176| `feedFwdOffsetCoeff` | `double` | The feed forward offset coefficient. | 177| `feedFwdGainCoeff` | `double` | The feed forward gain coefficient. | 178| `integralLimit_min` | `double` | The integral minimum clamp value. | 179| `integralLimit_max` | `double` | The integral maximum clamp value. | 180| `outLim_min` | `double` | The output minimum clamp value. | 181| `outLim_max` | `double` | The output maximum clamp value. | 182| `slewNeg` | `double` | Negative slew value to dampen output. | 183| `slewPos` | `double` | Positive slew value to accelerate output. | 184 185The units for the coefficients depend on the configuration of the PIDs. 186 187If the PID is a `margin` controller and its `setpoint` is in centigrade and 188output in RPM: proportionalCoeff is your p value in units: RPM/C and integral 189coefficient: RPM/C sec 190 191If the PID is a fan controller whose output is pwm: proportionalCoeff is %/RPM 192and integralCoeff is %/RPM sec. 193 194***NOTE:*** The sample periods are specified in the configuration as they are 195used in the PID computations, however, they are not truly configurable as they 196are used for the update periods for the fan and thermal sensors. 197 198#### type == "fan" 199 200``` 201"name": "fan1-5", 202"type": "fan", 203"inputs": ["fan1", "fan5"], 204"setpoint": 90.0, 205"pid": { 206... 207} 208``` 209 210The type `fan` builds a `FanController` PID. 211 212| field | type | meaning | 213| ---------- | ----------------- | ------------------------------------------- | 214| `name` | `string` | The name of the PID. This is just for humans and logging.| 215| `type` | `string` | `fan` | 216| `inputs` | `list of strings` | The names of the sensor(s) that are used as input and output for the PID loop.| 217| `setpoint` | `double` | Presently UNUSED | 218| `pid` | `dictionary` | A PID dictionary detailed above. | 219 220#### type == "temp" 221 222***TODO:*** Add notes for temperature configuration. 223 224#### type == "margin" 225 226``` 227"name": "fleetingpid0", 228"type": "margin", 229"inputs": ["fleeting0"], 230"setpoint": 10, 231"pid": { 232... 233} 234``` 235 236The type `margin` builds a `ThermalController` PID. 237 238| field | type | meaning | 239| ---------- | ----------------- | ------------------------------------------- | 240| `name` | `string` | The name of the PID. This is just for humans and logging.| 241| `type` | `string` | `margin` | 242| `inputs` | `list of strings` | The names of the sensor(s) that are used as input for the PID loop.| 243| `setpoint` | `double` | The setpoint value for the thermal PID. The setpoint for the margin sensors.| 244| `pid` | `dictionary` | A PID dictionary detailed above. | 245 246The output of a `margin` PID loop is that it sets the setpoint value for the 247zone. It does this by adding the value to a list of values. The value chosen by 248the fan PIDs (in this cascade configuration) is the maximum value. 249 250#### type == "stepwise" 251 252***TODO:*** Write up `stepwise` details. 253