1# Sensor Support for OpenBMC
2OpenBMC makes it easy to add sensors for your hardware and is compliant with
3the traditional Linux HWMon sensor format.  The architecture of OpenBMC
4sensors is to map sensors to [D-Bus][1]
5objects.  The D-Bus object will broadcast the `PropertiesChanged` signal when either
6the sensor or threshold value changes. It is the responsibility of other
7applications to determine the effect of the signal on the system.
8
9## D-Bus
10
11```
12Service     xyz.openbmc_project.Hwmon-<hash>.Hwmon1
13Path        /xyz/openbmc_project/sensors/<type>/<label>
14Interfaces  xyz.openbmc_project.Sensor.[*], others (see below)
15
16Signals: All properties for an interface will broadcast signal changed
17```
18
19**Path definitions**
20
21* **<type\>** : The [HWMon class][2] name in lower case.
22    - Examples include `temperature, fan_tach, voltage`.
23
24* **<label\>** : User defined name of the sensor.
25    - Examples include `ambient, cpu0, fan5`
26
27**Note**: The label shall comply with "Valid Object Paths" of [D-Bus Spec][3],
28that shall only contain the ASCII characters "[A-Z][a-z][0-9]_".
29
30**Hash definition**
31
32The hash value in the service name is used to give the service a unique
33and stable name.  It is a decimal number that is obtained by hashing
34characteristics of the device it is monitoring using std::hash().
35
36## Redfish
37
38The [BMCWeb Redfish][10] support returns information about sensors.  The
39support depends on two types of [ObjectMapper associations][11] to find the
40necessary sensor information on D-Bus.
41
42### Association Type #1: Linking a chassis to all sensors within the chassis
43
44Sensors are grouped by chassis in Redfish.  An ObjectMapper association is used
45to link a chassis to all the sensors within the chassis.  This includes the
46sensors for all hardware that is considered to be within the chassis.  For
47example, a chassis might contain two fan sensors, an ambient temperature
48sensor, and a VRM output voltage sensor.
49
50#### D-Bus object paths
51
52The association links the following D-Bus object paths together:
53* Chassis inventory item object path
54* List of sensor object paths for sensors within the chassis
55
56#### Association names
57* "all_sensors"
58  * Contains the list of all sensors for this chassis
59* "chassis"
60  * Contains the chassis associated with this sensor
61
62#### Example associations
63* /xyz/openbmc_project/inventory/system/chassis/all_sensors
64  * "endpoints" property contains
65    * /xyz/openbmc_project/sensors/fan_tach/fan0_0
66    * /xyz/openbmc_project/sensors/fan_tach/fan0_1
67    * /xyz/openbmc_project/sensors/temperature/ambient
68    * /xyz/openbmc_project/sensors/voltage/p0_vdn_voltage
69* /xyz/openbmc_project/sensors/fan_tach/fan0_0/chassis
70  * "endpoints" property contains
71    * /xyz/openbmc_project/inventory/system/chassis
72
73### Association Type #2: Linking a low-level hardware item to its sensors
74
75A sensor is usually related to a low-level hardware item, such as a fan, power
76supply, VRM, or CPU.  The Redfish sensor support can obtain the following
77information from the related hardware item:
78* Presence ([Inventory.Item interface][12])
79* Functional state ([OperationalStatus interface][13])
80* Manufacturer, Model, PartNumber, SerialNumber ([Decorator.Asset interface][14])
81
82For this reason, an ObjectMapper association is used to link a low-level
83hardware item to its sensors.  For example, a processor VRM could have
84temperature and output voltage sensors, or a dual-rotor fan could have
85two tach sensors.
86
87#### D-Bus object paths
88
89The association links the following D-Bus object paths together:
90* Low-level hardware inventory item object path
91* List of sensor object paths for sensors related to that hardware item
92
93#### Association names
94* "sensors"
95  * Contains the list of sensors for this low-level hardware item
96* "inventory"
97  * Contains the low-level hardware inventory item for this sensor
98
99#### Example associations
100* /xyz/openbmc_project/inventory/system/chassis/motherboard/fan0/sensors
101  * "endpoints" property contains
102    * /xyz/openbmc_project/sensors/fan_tach/fan0_0
103    * /xyz/openbmc_project/sensors/fan_tach/fan0_1
104* /xyz/openbmc_project/sensors/fan_tach/fan0_0/inventory
105  * "endpoints" property contains
106    * /xyz/openbmc_project/inventory/system/chassis/motherboard/fan0
107
108## Development Details
109
110Sensor properties are standardized based on the type of sensor.  A Threshold
111sensor contains specific properties associated with the rise and fall of a
112sensor value.  The [Sensor Interfaces][4]
113are described in their respective YAML files.  The path location in the source
114tree is identical to the interface being described below the
115[phosphor-dbus-interfaces][5] parent directory.
116
117&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;example:
118[openbmc/phosphor-dbus-interfaces/xyz/openbmc_project/Sensor/Threshold/Warning.yaml][6]
119
120
121&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Maps to D-Bus interface
122`xyz.openbmc_project.Sensor.Threshold.Warning`
123
124Each 'name' property in the YAML file maps directly to D-Bus properties.
125
126&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;example:
127
128&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;[Warning.interface.yaml][6]
129
130```
131properties:
132    - name: WarningHigh
133      type: int64
134    - name: WarningLow
135      type: int64
136    - name: WarningAlarmHigh
137      type: boolean
138    - name: WarningAlarmLow
139      type: boolean
140```
141
142Maps to
143
144```
145busctl --system introspect xyz.openbmc_project.Hwmon-3301914901.Hwmon1 \
146 /xyz/openbmc_project/Sensors/temperature/ambient \
147 xyz.openbmc_project.Sensor.Threshold.Warning | grep property
148
149.WarningAlarmHigh                            property  b         false        emits-change writable
150.WarningAlarmLow                             property  b         false        emits-change writable
151.WarningHigh                                 property  x         40000        emits-change writable
152.WarningLow                                  property  x         10000        emits-change writable
153
154```
155
156### REST
157
158```
159"/xyz/openbmc_project/Sensors/temperature/ambient": {
160      "Scale": -3,
161      "Unit": "xyz.openbmc_project.Sensor.Value.Unit.DegreesC",
162      "Value": 30125,
163      "WarningAlarmHigh": 0,
164      "WarningAlarmLow": 0,
165      "WarningHigh": 40000,
166      "WarningLow": 10000
167}
168```
169
170### Other Interfaces
171
172Aside from the `xyz.openbmc_project.Sensor` interfaces, the sensor D-Bus objects
173may also expose the following interfaces:
174
1751.  `xyz.openbmc_project.Control.FanSpeed`
176    - Provides a Target property to set a fan RPM value
1772.  `xyz.openbmc_project.Control.FanPwm`
178    - Provides a Target property to set a fan PWM value
1793.  `xyz.openbmc_project.State.Decorator.OperationalStatus`
180    - Provides a Functional property that tracks the state of any fault files
181
182### Signals
183
184Any property value change broadcasts a signal on D-Bus.  When a value trips
185past a threshold, an additional D-Bus signal is sent.
186
187Example, if the value of WarningLow is 5...
188
189| From | To | propertyChanged Signals |
190|---|---|---|
191| 1 | 5 | "xyz.openbmc_project.Sensor.Value" : value = 5 |
192| 1 | 6 | "xyz.openbmc\_project.Sensor.Value" : value = 6 ,<br>"xyz.openbmc\_project.Sensor.Threshold.Warning" : WarningAlarmLow = 0    |
193| 5 | 6 | "xyz.openbmc\_project.Sensor.Value" : value = 6  |
194| 6 | 1 | "xyz.openbmc\_project.Sensor.Value" : value = 1 ,<br>"xyz.openbmc\_project.Sensor.Threshold.Warning" : WarningAlarmLow = 1 |
195
196
197### System Configuration
198
199On the BMC each sensor's configuration is located in a file.  These files
200can be found as a child of the `/etc/default/obmc/hwmon` path.
201
202
203## Creating a Sensor
204
205There are two techniques to add a sensor to your system and which to use
206depends on if your system defines sensors via an MRW (Machine Readable
207Workbook) or not.
208
209
210### My sensors are not defined in an MRW
211
212HWMon sensors are defined in the `recipes-phosphor/sensor/phosphor-hwmon%`
213path within the [machine configuration][7].
214The children of the `obmc/hwmon` directory should follow the path of either:
215
2161.  The children of the `devicetree/base` directory path on the system,
217as defined by the kernel.  The code obtains this from the OF_FULLNAME udev
218environment variable.
219
2202.  If the device isn't in the device tree, then the device path can be used.
221
222As an example, the Palmetto [configuration][8]
223file for the ambient temperature sensor.
224
225```
226recipes-phosphor/sensors/phosphor-hwmon%/obmc/hwmon/ahb/apb/i2c@1e78a000/i2c-bus@c0/tmp423@4c.conf
227```
228
229which maps to this specific sensor and conf file on the system...
230
231```
232/sys/firmware/devicetree/base/ahb/apb/i2c@1e78a000/i2c-bus@c0/tmp423@4c
233/etc/default/obmc/hwmon/ahb/apb/i2c@1e78a000/i2c-bus@c0/tmp423@4c.conf
234```
235
236This next example shows using the device path as opposed to the devicetree
237path for the OCC device on an OpenPOWER system.
238Note how a '--' replaces a ':' in the directory names for the conf file.
239
240```
241recipes-phosphor/sensors/phosphor-hwmon%/obmc/hwmon/devices/platform/gpio-fsi/fsi0/slave@00--00/00--00--00--06/sbefifo1-dev0/occ-hwmon.1.conf
242```
243
244which maps to this specific sensor and conf file on the system...
245
246```
247/sys/devices/platform/gpio-fsi/fsi0/slave@00:00/00:00:00:06/sbefifo1-dev0/occ-hwmon.1
248/etc/default/obmc/hwmon/devices/platform/gpio-fsi/fsi0/slave@00--00/00--00--00--06/sbefifo1-dev0/occ-hwmon.1.conf
249```
250
251In order for the sensor to be exposed to D-Bus, the configuration file must
252describe the sensor attributes.  Attributes follow a format.
253
254```
255xxx_yyy#=value
256
257xxx = Attribute
258#   = Association number (i.e. 1-n)
259yyy = HWMon sensor type (i.e. temp, pwm)
260```
261
262| Attribute | Interfaces Added |
263|---|---|
264|LABEL | xyz.openbmc\_project.Sensor.Value |
265| WARNHI, WARNLO | xyz.openbmc\_project.Threshold.Warning |
266| CRITHI, CRITLO | xyz.openbmc\_project.Threshold.Critical |
267
268
269The HWMon sensor type
270
271| [HWMon sensor type][2] | type |
272|---|---|
273| temp | temperature |
274| in | voltage |
275| * | All other names map directly |
276
277
278
279See the [HWMon interface][2]
280definitions for more definitions and keyword details
281
282
283
284In this conf example the tmp423 chip is wired to two temperature sensors.
285The values must be described in 10<sup>-3</sup> degrees Celsius.
286
287
288```
289LABEL_temp1=ambient
290WARNLO_temp1=10000
291WARNHI_temp1=40000
292
293LABEL_temp2=cpu
294WARNLO_temp2=10000
295WARNHI_temp2=80000
296```
297
298#### Additional Config File Entries
299The phosphor-hwmon code supports these additional config file entries:
300
301**INTERVAL**
302
303The interval, in microseconds, at which sensors should be read.  If
304not specified the interval is 1000000us (1 second).
305```
306INTERVAL=1000000
307```
308
309**GAIN**, **OFFSET**
310
311Used to support scaled sensor readings, where
312value = (raw sensor reading) * gain + offset
313```
314GAIN_in3 = 5.0  #GAIN is a double
315OFFSET_in3 = 6  #OFFSET is an integer
316```
317
318**MINVALUE**, **MAXVALUE**
319
320If found, will be used to set the MinValue/MaxValue properties
321on the `xyz.openbmc_project.Sensor.Value` interface.
322```
323MINVALUE_temp1 = 1
324```
325
326**MODE**
327
328Needed for certain device drivers, specifically the OpenPOWER OCC driver,
329where the instance number (the N in tempN_input) is dynamic and instead
330another file contains the known ID.
331
332For example
333```
334temp26_input:29000
335temp26_label:171
336```
337Where the 26 is just what hwmon assigns, but the 171 corresponds
338to something like an IPMI sensor value for a DIMM temperature.
339
340The config file would then have:
341```
342MODE_temp26 = "label"  #Tells the code to look in temp26_label
343LABEL_temp171 = "dimm3_temp" #Says that temp26_input holds dimm3_temp
344```
345
346**REMOVERCS**
347
348Contains a list of device driver errno values where if these are obtained
349when reading the hardware, the corresponding sensor object will be removed
350from D-Bus until it is successfully read again.
351
352```
353REMOVERCS = "5,6"  #If any sensor on the device returns a 5 or 6, remove it.
354REMOVERCS_temp1 = "42"  #If reading temp1_input returns a 42, remove it.
355```
356
357**TARGET\_MODE**
358
359Allows one to choose the fan target mode, either RPM or PWM,
360if the device driver exposes both methods.
361```
362TARGET_MODE = "RPM"
363```
364
365**PWM\_TARGET**
366
367For fans that are PWM controlled, can be used to map the pwmN file to a fan M.
368```
369PWM_TARGET_fan0 = 1 #Use the pwm1 file to control fan 0
370```
371
372**ENABLE**
373
374Will write a value to a pwmN\_enable file on startup if present.
375```
376ENABLE_fan1 = 2 #Write a 2 to pwm1_enable
377```
378
379### Defining sensors in an IPMI YAML configuration file
380For an example of how sensors entries are defined, consult the [example
381YAML](https://github.com/openbmc/phosphor-host-ipmid/blob/master/scripts/sensor-example.yaml)
382
383#### How to best choose coefficients
384Sensor reading, according to IPMI spec, is calculated as:
385
386```none
387y = L[(Mx + B * 10^(bExp)) * 10^(rExp)]
388```
389
390- y: the 'final value' as reported by IPMItool
391- x: 8 bits, unsigned, reading data encoded in IPMI response packet
392- M: 10 bits, signed integer multiplier, `multiplierM` in YAML
393- B: 10 bits, signed additive offset, `offsetB` in YAML
394- bExp: 4 bits, signed, `bExp` in YAML
395- rExp: 4 bits, signed, `rExp` in YAML
396
397In addition, phosphor-ipmi-host configuration also supports `scale` property,
398which applies for analog sensors, meaning the value read over DBus should be
399scaled by 10^S.
400
401As you can tell, one should choose the coefficients based on possible sensor
402reading range and desired resolution. Commonly, B=0, we would have
403
404    Supported range: [0, 255 * M * 10^(scale - rExp)]
405    Resolution: M * 10^(scale - rExp)
406
407For a concrete example, let's say a voltage sensor reports between 0 to 5.0V.
408hwmon sysfs scales the value by 1000, so the sensor value read over DBus is
409between 0 and 5000. A possible configuration for this is:
410
411```none
412multiplierM: 20
413offsetB: 0
414bExp: 0
415rExp: -3
416scale: -3
417```
418
419so for a DBus sensor value of 4986 meaning 4.986V, phosphor-ipmi-host would
420encode it as
421
422    x: 4986 / 20 = 249
423    M: 20
424    rExp: -3
425
426When ipmitool sensor list is called, the tool fetches sensor factors and
427computes value as:
428
429```none
430y = 20 * 249 * 10^-3 = 4.98 (V)
431```
432
433### My sensors are defined in an MRW
434
435Setting up sensor support with an MRW is done by adding a unit-hwmon-feature
436unit, for each hwmon feature needing to be monitored and then filling in the
437HWMON_FEATURE attribute.  The XML field is required however a D-Bus interface
438will only be generated when the value property is not null. Values for
439Thresholds follow the HWMon format of no decimals.  Temperature values must
440be described in 10<sup>-3</sup> degrees Celsius. The HWMON\_NAME will be used
441to derive the \<type> while the DESCRIPTIVE\_NAME creates the \<label> for the
442instance path.
443
444| Field id | Value Required | Interfaces Added |
445|---|:---:|---|
446| HWMON\_NAME | Y | xyz.org.openbmc\_project.Sensor.Value |
447| WARN\_LOW, WARN\_HIGH | N | xyz.openbmc\_project.Threshold.Warning |
448| CRIT\_LOW, CRIT\_HIGH | N | xyz.openbmc\_project.Threshold.Critical |
449
450
451Here is an example of a Fan sensor.  If the RPMs go above 80000 or below 1000
452addition signals will be sent over D-Bus.  Note that neither CRIT\_LOW or
453CRIT\_HIGH is set so `xyz.org.openbmc_project.Threshold.Critical` will not be
454added.  The instance path will be `/xyz/openbmc_project/Sensors/fan/fan0`.
455
456```
457<targetInstance>
458	<id>MAX31785.hwmon2</id>
459	<type>unit-hwmon-feature</type>
460	...
461	<attribute>
462		<id>HWMON_FEATURE</id>
463		<default>
464				<field><id>HWMON_NAME</id><value>fan1</value></field>
465				<field><id>DESCRIPTIVE_NAME</id><value>fan0</value></field>
466				<field><id>WARN_LOW</id><value>1000</value></field>
467				<field><id>WARN_HIGH</id><value>80000</value></field>
468				<field><id>CRIT_LOW</id><value></value></field>
469				<field><id>CRIT_HIGH</id><value></value></field>
470		</default>
471	</attribute>
472```
473
474
475## Additional Reading
476Mailing List [Comments on Sensor design][9]
477
478
479[1]: https://dbus.freedesktop.org/doc/dbus-tutorial.html
480[2]: https://www.kernel.org/doc/Documentation/hwmon/sysfs-interface
481[3]: https://dbus.freedesktop.org/doc/dbus-specification.html#basic-types
482[4]: https://github.com/openbmc/phosphor-dbus-interfaces/tree/master/xyz/openbmc_project/Sensor
483[5]: https://github.com/openbmc/phosphor-dbus-interfaces
484[6]: https://github.com/openbmc/phosphor-dbus-interfaces/blob/master/xyz/openbmc_project/Sensor/Threshold/Warning.interface.yaml
485[7]: https://github.com/openbmc/openbmc/tree/master/meta-openbmc-machines
486[8]: https://github.com/openbmc/openbmc/blob/master/meta-openbmc-machines/meta-openpower/meta-ibm/meta-palmetto/recipes-phosphor/sensors/phosphor-hwmon%25/obmc/hwmon/ahb/apb/i2c%401e78a000/i2c-bus%40c0/tmp423%404c.conf
487[9]: https://lists.ozlabs.org/pipermail/openbmc/2016-November/005309.html
488[10]: https://github.com/openbmc/bmcweb/blob/master/DEVELOPING.md#redfish
489[11]: https://github.com/openbmc/docs/blob/master/architecture/object-mapper.md#associations
490[12]: https://github.com/openbmc/phosphor-dbus-interfaces/blob/master/xyz/openbmc_project/Inventory/Item.interface.yaml
491[13]: https://github.com/openbmc/phosphor-dbus-interfaces/blob/master/xyz/openbmc_project/State/Decorator/OperationalStatus.interface.yaml
492[14]: https://github.com/openbmc/phosphor-dbus-interfaces/blob/master/xyz/openbmc_project/Inventory/Decorator/Asset.interface.yaml
493