[V2,3/4] sensors: occ: Scale the sensor values

Message ID 1513322463-24801-4-git-send-email-shilpa.bhat@linux.vnet.ibm.com
State Accepted
Headers show
Series
  • sensors: occ: Add energy counter and fixes for parsing sensors
Related show

Commit Message

Shilpasri G Bhat Dec. 15, 2017, 7:21 a.m.
Scale the sensor values appropriately to match the HWMON requirements.
OCC sensor values should be scaled as per "scale_factor' field and
then converted to HWMON required unit.

Sensors like temperature and power are already scaled by the kernel
driver which convert the values to millidegree Celsius and microWatt
respectively. So apart from temperature and power the remaining
sensors are converted to HWMON standards.

Signed-off-by: Shilpasri G Bhat <shilpa.bhat@linux.vnet.ibm.com>
---
 hw/occ-sensor.c | 111 ++++++++++++++++++++++++++++++++++++++++++++++++++++++--
 1 file changed, 107 insertions(+), 4 deletions(-)

Patch

diff --git a/hw/occ-sensor.c b/hw/occ-sensor.c
index eb9c7cf..bc5085a 100644
--- a/hw/occ-sensor.c
+++ b/hw/occ-sensor.c
@@ -275,32 +275,80 @@  enum sensor_attr {
 				 OCC_SENSOR_TYPE_TEMPERATURE | \
 				 OCC_SENSOR_TYPE_POWER)
 
+/*
+ * Standard HWMON linux interface expects the below units for the
+ * environment sensors:
+ * - Current		: milliampere
+ * - Voltage		: millivolt
+ * - Temperature	: millidegree Celsius (scaled in kernel)
+ * - Power		: microWatt	      (scaled in kernel)
+ * - Energy		: microJoule
+ */
+
+/*
+ * OCC sensor units are obtained after scaling the sensor values.
+ * https://github.com/open-power/occ/blob/master/src/occ_405/sensor/sensor_info.c
+ */
+
 static struct str_map {
 	const char *occ_str;
 	const char *opal_str;
 } str_maps[] = {
 	{"PWRSYS", "System"},
+	/* Bulk power of the system: Watt */
 	{"PWRFAN", "Fan"},
+	/* Power consumption of the system fans: Watt */
 	{"PWRIO", "IO"},
+	/* Power consumption of the IO subsystem: Watt */
 	{"PWRSTORE", "Storage"},
+	/* Power comsumption of the storage subsystem: Watt */
 	{"PWRGPU", "GPU"},
+	/* Power consumption for GPUs per socket read from APSS: Watt */
 	{"PWRAPSSCH", "APSS"},
+	/* Power Provided by APSS channel x (where x=0…15): Watt */
 	{"PWRPROC", ""},
+	/* Power consumption for this Processor: Watt */
 	{"PWRVDD", "Vdd"},
-	{"CURVDD", "Vdd"},
-	{"VOLTVDDSENSE", "Vdd Remote Sense"},
-	{"VOLTVDD", "Vdd"},
+	/* Power consumption for this Processor's Vdd(AVSBus readings): Watt */
 	{"PWRVDN", "Vdn"},
+	/* Power consumption for  this Processor's Vdn (nest)
+	 * Calculated from AVSBus readings: Watt */
+	{"PWRMEM", "Memory"},
+	/* Power consumption for Memory  for this Processor read from APSS:
+	 * Watt */
+	{"CURVDD", "Vdd"},
+	/* Processor Vdd Current (read from AVSBus): Ampere */
 	{"CURVDN", "Vdn"},
+	/* Processor Vdn Current (read from AVSBus): Ampere */
+	{"VOLTVDDSENSE", "Vdd Remote Sense"},
+	/* Vdd Voltage at the remote sense.
+	 * AVS reading adjusted for loadline: millivolt */
 	{"VOLTVDNSENSE", "Vdn Remote Sense"},
+	/* Vdn Voltage at the remote sense.
+	 * AVS reading adjusted for loadline: millivolt */
+	{"VOLTVDD", "Vdd"},
+	/* Processor Vdd Voltage (read from AVSBus): millivolt */
 	{"VOLTVDN", "Vdn"},
-	{"PWRMEM", "Memory"},
+	/* Processor Vdn Voltage (read from AVSBus): millivolt */
 	{"TEMPC", "Core"},
+	/* Average temperature of core DTS sensors for Processor's Core y:
+	 * Celsius */
 	{"TEMPQ", "Quad"},
+	/* Average temperature of quad (in cache) DTS sensors for
+	 * Processor’s Quad y: Celsius */
 	{"TEMPNEST", "Nest"},
+	/* Average temperature of nest DTS sensors: Celsius */
 	{"TEMPPROCTHRMC", "Core"},
+	/* The combined weighted core/quad temperature for processor core y:
+	 * Celsius */
 	{"TEMPDIMM", "DIMM"},
+	/* DIMM temperature for DIMM x: Celsius */
 	{"TEMPGPU", "GPU"},
+	/* GPU x (0..2) board temperature: Celsius */
+	/* TEMPGPUxMEM: GPU x hottest HBM temperature (individual memory
+	 * temperatures are not available): Celsius */
+	{"TEMPVDD", "VRM VDD"},
+	/* VRM Vdd temperature: Celsius */
 };
 
 static u64 occ_sensor_base;
@@ -323,6 +371,52 @@  static inline u32 sensor_handler(int occ_num, int sensor_id, int attr)
 	return sensor_make_handler(SENSOR_OCC, occ_num, sensor_id, attr);
 }
 
+/*
+ * The scaling factor for the sensors is encoded in the below format:
+ * (((UINT32)mantissa << 8) | (UINT32)((UINT8) 256 + (UINT8)exp))
+ * https://github.com/open-power/occ/blob/master/src/occ_405/sensor/sensor.h
+ */
+static void scale_sensor(struct occ_sensor_name *md, u64 *sensor)
+{
+	u32 factor = md->scale_factor;
+	int i;
+	s8 exp;
+
+	if (md->type == OCC_SENSOR_TYPE_CURRENT)
+		*sensor *= 1000; //convert to mA
+
+	*sensor *= factor >> 8;
+	exp = factor & 0xFF;
+
+	if (exp > 0) {
+		for (i = labs(exp); i > 0; i--)
+			*sensor *= 10;
+	} else {
+		for (i = labs(exp); sensor && i > 0; i--)
+			*sensor /= 10;
+	}
+}
+
+static void scale_energy(struct occ_sensor_name *md, u64 *sensor)
+{
+	u32 factor = md->freq;
+	int i;
+	s8 exp;
+
+	*sensor *= 1000000; //convert to uJ
+
+	*sensor /= factor >> 8;
+	exp = factor & 0xFF;
+
+	if (exp > 0) {
+		for (i = labs(exp); sensor && i > 0; i--)
+			*sensor /= 10;
+	} else {
+		for (i = labs(exp); i > 0; i--)
+			*sensor *= 10;
+	}
+}
+
 static u64 read_sensor(struct occ_sensor_record *sensor, int attr)
 {
 	switch (attr) {
@@ -393,6 +487,7 @@  static void *select_sensor_buffer(struct occ_sensor_data_header *hb, int id)
 int occ_sensor_read(u32 handle, u64 *data)
 {
 	struct occ_sensor_data_header *hb;
+	struct occ_sensor_name *md;
 	u16 id = sensor_get_rid(handle);
 	u8 occ_num = sensor_get_frc(handle);
 	u8 attr = sensor_get_attr(handle);
@@ -417,6 +512,14 @@  int occ_sensor_read(u32 handle, u64 *data)
 		return OPAL_HARDWARE;
 
 	*data = read_sensor(buff, attr);
+	if (!*data)
+		return OPAL_SUCCESS;
+
+	md = get_names_block(hb);
+	if (md[id].type == OCC_SENSOR_TYPE_POWER && attr == SENSOR_ACCUMULATOR)
+		scale_energy(&md[id], data);
+	else
+		scale_sensor(&md[id], data);
 
 	return OPAL_SUCCESS;
 }