summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorShilpasri G Bhat <shilpa.bhat@linux.vnet.ibm.com>2018-03-14 14:39:39 +0530
committerShilpasri G Bhat <shilpa.bhat@linux.vnet.ibm.com>2018-03-14 15:49:41 +0530
commitfe9056d96aa9190d7b2c8bef497ba418782ca129 (patch)
tree61b379f0aed04ed7f6ab9d955a9d2102215d8a37
parent226d19fdcbf2ac2ca25daa132db4ae7f2a5237be (diff)
downloadocc-inband-sensors-fe9056d96aa9190d7b2c8bef497ba418782ca129.tar.gz
occ-inband-sensors-fe9056d96aa9190d7b2c8bef497ba418782ca129.zip
sensors: Add support to read inband-sensors in P9HEADmaster
Signed-off-by: Shilpasri G Bhat <shilpa.bhat@linux.vnet.ibm.com>
-rw-r--r--p9_inband_sensors.c399
1 files changed, 399 insertions, 0 deletions
diff --git a/p9_inband_sensors.c b/p9_inband_sensors.c
new file mode 100644
index 0000000..91c9aed
--- /dev/null
+++ b/p9_inband_sensors.c
@@ -0,0 +1,399 @@
+/*
+ * p9_inband_sensors.c : C wrapper to read all the inband sensors in P9
+ *
+ * To build:
+ * # gcc p9_inband_sensors.c -lm -o p9sensors
+ *
+ * To run:
+ * # ./p9sensors --> Default read Chip 0's data
+ * # ./p9sensors <occ_num>
+ * # ./p9sensors 0 --> Chip 0
+ * # ./p9sensors 1 --> Chip 1
+ */
+
+#include <stdio.h>
+#include <stdint.h>
+#include <fcntl.h>
+#include <sys/stat.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <math.h>
+
+/*
+ * OCC Sensor Data
+ *
+ * OCC sensor data will use BAR2 (OCC Common is per physical drawer).
+ * Starting address is at offset 0x00580000 from BAR2 base address.
+ * Maximum size is 1.5MB.
+ *
+ * -------------------------------------------------------------------------
+ * | Start (Offset from | End | Size |Description |
+ * | BAR2 base address) | | | |
+ * -------------------------------------------------------------------------
+ * | 0x00580000 | 0x005A57FF |150kB |OCC 0 Sensor Data Block|
+ * | 0x005A5800 | 0x005CAFFF |150kB |OCC 1 Sensor Data Block|
+ * | : | : | : | : |
+ * | 0x00686800 | 0x006ABFFF |150kB |OCC 7 Sensor Data Block|
+ * | 0x006AC000 | 0x006FFFFF |336kB |Reserved |
+ * -------------------------------------------------------------------------
+ *
+ *
+ * OCC N Sensor Data Block Layout (150kB)
+ *
+ * The sensor data block layout is the same for each OCC N. It contains
+ * sensor-header-block, sensor-names buffer, sensor-readings-ping buffer and
+ * sensor-readings-pong buffer.
+ *
+ * ----------------------------------------------------------------------------
+ * | Start (Offset from OCC | End | Size |Description |
+ * | N Sensor Data Block) | | | |
+ * ----------------------------------------------------------------------------
+ * | 0x00000000 | 0x000003FF |1kB |Sensor Data Header Block |
+ * | 0x00000400 | 0x0000CBFF |50kB |Sensor Names |
+ * | 0x0000CC00 | 0x0000DBFF |4kB |Reserved |
+ * | 0x0000DC00 | 0x00017BFF |40kB |Sensor Readings ping buffer|
+ * | 0x00017C00 | 0x00018BFF |4kB |Reserved |
+ * | 0x00018C00 | 0x00022BFF |40kB |Sensor Readings pong buffer|
+ * | 0x00022C00 | 0x000257FF |11kB |Reserved |
+ * ----------------------------------------------------------------------------
+ *
+ * Sensor Data Header Block : This is written once by the OCC during
+ * initialization after a load or reset. Layout is defined in 'struct
+ * occ_sensor_data_header'
+ *
+ * Sensor Names : This is written once by the OCC during initialization after a
+ * load or reset. It contains static information for each sensor. The number of
+ * sensors, format version and length of each sensor is defined in
+ * 'Sensor Data Header Block'. Format of each sensor name is defined in
+ * 'struct occ_sensor_name'. The first sensor starts at offset 0 followed
+ * immediately by the next sensor.
+ *
+ * Sensor Readings Ping/Pong Buffer:
+ * There are two 40kB buffers to store the sensor readings. One buffer that
+ * is currently being updated by the OCC and one that is available to be read.
+ * Each of these buffers will be of the same format. The number of sensors and
+ * the format version of the ping and pong buffers is defined in the
+ * 'Sensor Data Header Block'.
+ *
+ * Each sensor within the ping and pong buffers may be of a different format
+ * and length. For each sensor the length and format is determined by its
+ * 'struct occ_sensor_name.structure_type' in the Sensor Names buffer.
+ *
+ * --------------------------------------------------------------------------
+ * | Offset | Byte0 | Byte1 | Byte2 | Byte3 | Byte4 | Byte5 | Byte6 | Byte7 |
+ * --------------------------------------------------------------------------
+ * | 0x0000 |Valid | Reserved |
+ * | |(0x01) | |
+ * --------------------------------------------------------------------------
+ * | 0x0008 | Sensor Readings |
+ * --------------------------------------------------------------------------
+ * | : | : |
+ * --------------------------------------------------------------------------
+ * | 0xA000 | End of Data |
+ * --------------------------------------------------------------------------
+ *
+ */
+
+#define MAX_OCCS 8
+#define MAX_CHARS_SENSOR_NAME 16
+#define MAX_CHARS_SENSOR_UNIT 4
+
+#define OCC_SENSOR_DATA_BLOCK_OFFSET 0x00580000
+#define OCC_SENSOR_DATA_BLOCK_SIZE 0x00025800
+
+enum occ_sensor_type {
+ OCC_SENSOR_TYPE_GENERIC = 0x0001,
+ OCC_SENSOR_TYPE_CURRENT = 0x0002,
+ OCC_SENSOR_TYPE_VOLTAGE = 0x0004,
+ OCC_SENSOR_TYPE_TEMPERATURE = 0x0008,
+ OCC_SENSOR_TYPE_UTILIZATION = 0x0010,
+ OCC_SENSOR_TYPE_TIME = 0x0020,
+ OCC_SENSOR_TYPE_FREQUENCY = 0x0040,
+ OCC_SENSOR_TYPE_POWER = 0x0080,
+ OCC_SENSOR_TYPE_PERFORMANCE = 0x0200,
+};
+
+enum occ_sensor_location {
+ OCC_SENSOR_LOC_SYSTEM = 0x0001,
+ OCC_SENSOR_LOC_PROCESSOR = 0x0002,
+ OCC_SENSOR_LOC_PARTITION = 0x0004,
+ OCC_SENSOR_LOC_MEMORY = 0x0008,
+ OCC_SENSOR_LOC_VRM = 0x0010,
+ OCC_SENSOR_LOC_OCC = 0x0020,
+ OCC_SENSOR_LOC_CORE = 0x0040,
+ OCC_SENSOR_LOC_GPU = 0x0080,
+ OCC_SENSOR_LOC_QUAD = 0x0100,
+};
+
+enum sensor_struct_type {
+ OCC_SENSOR_READING_FULL = 0x01,
+ OCC_SENSOR_READING_COUNTER = 0x02,
+};
+
+/**
+ * struct occ_sensor_data_header - Sensor Data Header Block
+ * @valid: When the value is 0x01 it indicates
+ * that this header block and the sensor
+ * names buffer are ready
+ * @version: Format version of this block
+ * @nr_sensors: Number of sensors in names, ping and
+ * pong buffer
+ * @reading_version: Format version of the Ping/Pong buffer
+ * @names_offset: Offset to the location of names buffer
+ * @names_version: Format version of names buffer
+ * @names_length: Length of each sensor in names buffer
+ * @reading_ping_offset: Offset to the location of Ping buffer
+ * @reading_pong_offset: Offset to the location of Pong buffer
+ * @pad/reserved: Unused data
+ */
+struct occ_sensor_data_header {
+ uint8_t valid;
+ uint8_t version;
+ uint16_t nr_sensors;
+ uint8_t reading_version;
+ uint8_t pad[3];
+ uint32_t names_offset;
+ uint8_t names_version;
+ uint8_t name_length;
+ uint16_t reserved;
+ uint32_t reading_ping_offset;
+ uint32_t reading_pong_offset;
+} __attribute__((__packed__));
+
+/**
+ * struct occ_sensor_name - Format of Sensor Name
+ * @name: Sensor name
+ * @units: Sensor units of measurement
+ * @gsid: Global sensor id (OCC)
+ * @freq: Update frequency
+ * @scale_factor: Scaling factor
+ * @type: Sensor type as defined in
+ * 'enum occ_sensor_type'
+ * @location: Sensor location as defined in
+ * 'enum occ_sensor_location'
+ * @structure_type: Indicates type of data structure used
+ * for the sensor readings in the ping and
+ * pong buffers for this sensor as defined
+ * in 'enum sensor_struct_type'
+ * @reading_offset: Offset from the start of the ping/pong
+ * reading buffers for this sensor
+ * @sensor_data: Sensor specific info
+ * @pad: Padding to fit the size of 48 bytes.
+ */
+struct occ_sensor_name {
+ char name[MAX_CHARS_SENSOR_NAME];
+ char units[MAX_CHARS_SENSOR_UNIT];
+ uint16_t gsid;
+ uint32_t freq;
+ uint32_t scale_factor;
+ uint16_t type;
+ uint16_t location;
+ uint8_t structure_type;
+ uint32_t reading_offset;
+ uint8_t sensor_data;
+ uint8_t pad[8];
+} __attribute__((__packed__));
+
+/**
+ * struct occ_sensor_record - Sensor Reading Full
+ * @gsid: Global sensor id (OCC)
+ * @timestamp: Time base counter value while updating
+ * the sensor
+ * @sample: Latest sample of this sensor
+ * @sample_min: Minimum value since last OCC reset
+ * @sample_max: Maximum value since last OCC reset
+ * @csm_min: Minimum value since last reset request
+ * by CSM (CORAL)
+ * @csm_max: Maximum value since last reset request
+ * by CSM (CORAL)
+ * @profiler_min: Minimum value since last reset request
+ * by profiler (CORAL)
+ * @profiler_max: Maximum value since last reset request
+ * by profiler (CORAL)
+ * @job_scheduler_min: Minimum value since last reset request
+ * by job scheduler(CORAL)
+ * @job_scheduler_max: Maximum value since last reset request
+ * by job scheduler (CORAL)
+ * @accumulator: Accumulator for this sensor
+ * @update_tag: Count of the number of ticks that have
+ * passed between updates
+ * @pad: Padding to fit the size of 48 bytes
+ */
+struct occ_sensor_record {
+ uint16_t gsid;
+ uint64_t timestamp;
+ uint16_t sample;
+ uint16_t sample_min;
+ uint16_t sample_max;
+ uint16_t csm_min;
+ uint16_t csm_max;
+ uint16_t profiler_min;
+ uint16_t profiler_max;
+ uint16_t job_scheduler_min;
+ uint16_t job_scheduler_max;
+ uint64_t accumulator;
+ uint32_t update_tag;
+ uint8_t pad[8];
+} __attribute__((__packed__));
+
+/**
+ * struct occ_sensor_counter - Sensor Reading Counter
+ * @gsid: Global sensor id (OCC)
+ * @timestamp: Time base counter value while updating
+ * the sensor
+ * @accumulator: Accumulator/Counter
+ * @sample: Latest sample of this sensor (0/1)
+ * @pad: Padding to fit the size of 24 bytes
+ */
+struct occ_sensor_counter {
+ uint16_t gsid;
+ uint64_t timestamp;
+ uint64_t accumulator;
+ uint8_t sample;
+ uint8_t pad[5];
+} __attribute__((__packed__));
+
+unsigned long read_counter(struct occ_sensor_data_header *hb, uint32_t offset)
+{
+ struct occ_sensor_counter *sping, *spong;
+ struct occ_sensor_counter *sensor = NULL;
+ uint8_t *ping, *pong;
+
+ ping = (uint8_t *)((uint64_t)hb + be32toh(hb->reading_ping_offset));
+ pong = (uint8_t *)((uint64_t)hb + be32toh(hb->reading_pong_offset));
+ sping = (struct occ_sensor_counter *)((uint64_t)ping + offset);
+ spong = (struct occ_sensor_counter *)((uint64_t)pong + offset);
+
+ if (*ping && *pong) {
+ if (be64toh(sping->timestamp) > be64toh(spong->timestamp))
+ sensor = sping;
+ else
+ sensor = spong;
+ } else if (*ping && !*pong) {
+ sensor = sping;
+ } else if (!*ping && *pong) {
+ sensor = spong;
+ } else if (!*ping && !*pong) {
+ return 0;
+ }
+
+ return be64toh(sensor->accumulator);
+}
+
+enum sensor_attr {
+ SENSOR_SAMPLE,
+ SENSOR_ACCUMULATOR,
+};
+
+unsigned long read_sensor(struct occ_sensor_data_header *hb, uint32_t offset,
+ int attr)
+{
+ struct occ_sensor_record *sping, *spong;
+ struct occ_sensor_record *sensor = NULL;
+ uint8_t *ping, *pong;
+
+ ping = (uint8_t *)((uint64_t)hb + be32toh(hb->reading_ping_offset));
+ pong = (uint8_t *)((uint64_t)hb + be32toh(hb->reading_pong_offset));
+ sping = (struct occ_sensor_record *)((uint64_t)ping + offset);
+ spong = (struct occ_sensor_record *)((uint64_t)pong + offset);
+
+ if (*ping && *pong) {
+ if (be64toh(sping->timestamp) > be64toh(spong->timestamp))
+ sensor = sping;
+ else
+ sensor = spong;
+ } else if (*ping && !*pong) {
+ sensor = sping;
+ } else if (!*ping && *pong) {
+ sensor = spong;
+ } else if (!*ping && !*pong) {
+ return 0;
+ }
+
+ switch (attr) {
+ case SENSOR_SAMPLE:
+ return be16toh(sensor->sample);
+ case SENSOR_ACCUMULATOR:
+ return be64toh(sensor->accumulator);
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+#define TO_FP(f) ((f >> 8) * pow(10, ((int8_t)(f & 0xFF))))
+
+void print_all_sensors(void *buf)
+{
+ struct occ_sensor_data_header *hb;
+ struct occ_sensor_name *md;
+ int i = 0;
+
+ hb = (struct occ_sensor_data_header *)(uint64_t)buf;
+ md = (struct occ_sensor_name *)((uint64_t)hb +
+ be32toh(hb->names_offset));
+
+ for (i = 0; i < be16toh(hb->nr_sensors); i++) {
+ uint32_t offset = be32toh(md[i].reading_offset);
+ uint32_t scale = be32toh(md[i].scale_factor);
+ uint64_t sample;
+
+ if (md[i].structure_type == OCC_SENSOR_READING_FULL)
+ sample = read_sensor(hb, offset, SENSOR_SAMPLE);
+ else
+ sample = read_counter(hb, offset);
+
+ if (be16toh(md[i].type) ==OCC_SENSOR_TYPE_POWER) {
+ uint64_t energy = read_sensor(hb, offset,
+ SENSOR_ACCUMULATOR);
+ uint32_t freq = be32toh(md[i].freq);
+
+ printf("%5d %-20s %16lu %s %16luJ\n", i, md[i].name,
+ (uint64_t)(sample * TO_FP(scale)), md[i].units,
+ (uint64_t)(energy / TO_FP(freq)));
+ } else {
+ printf("%5d %-20s %16lu %s\n", i, md[i].name,
+ (uint64_t)(sample * TO_FP(scale)), md[i].units);
+ }
+ }
+}
+
+int main(int argc, char *argv[])
+{
+ void *buf;
+ int fd, rc, bytes;
+ int chipid;
+
+ if (argc < 2)
+ chipid = 0;
+ else
+ chipid = atoi(argv[1]);
+
+ fd = open("/sys/firmware/opal/exports/occ_inband_sensors", O_RDONLY);
+ if (fd < 0) {
+ printf("Failed to open occ_inband_sensors file\n");
+ return -1;
+ }
+
+ buf = malloc(OCC_SENSOR_DATA_BLOCK_SIZE);
+ if (!buf) {
+ printf("Failed to allocate\n");
+ return -1;
+ }
+
+ lseek(fd, chipid * OCC_SENSOR_DATA_BLOCK_SIZE, SEEK_CUR);
+ for (rc = bytes = 0; bytes < OCC_SENSOR_DATA_BLOCK_SIZE; bytes += rc) {
+ rc = read(fd, buf + bytes, OCC_SENSOR_DATA_BLOCK_SIZE - bytes);
+ if (!rc || rc < 0)
+ break;
+ }
+
+ if (bytes != OCC_SENSOR_DATA_BLOCK_SIZE) {
+ printf("Failed to read data\n");
+ return -1;
+ }
+
+ print_all_sensors(buf);
+}
OpenPOWER on IntegriCloud