Commit aa195fe4 authored by Eddie James's avatar Eddie James Committed by Guenter Roeck

hwmon (occ): Parse OCC poll response

Add method to parse the response from the OCC poll command. This only
needs to be done during probe(), since the OCC shouldn't change the
number or format of sensors while it's running. The parsed response
allows quick access to sensor data, as well as information on the
number and version of sensors, which we need to instantiate hwmon
attributes.
Signed-off-by: default avatarEddie James <eajames@linux.ibm.com>
Signed-off-by: default avatarGuenter Roeck <linux@roeck-us.net>
parent 88be37c0
// SPDX-License-Identifier: GPL-2.0 // SPDX-License-Identifier: GPL-2.0
#include <linux/device.h> #include <linux/device.h>
#include <linux/kernel.h>
#include "common.h" #include "common.h"
...@@ -22,6 +23,64 @@ static int occ_poll(struct occ *occ) ...@@ -22,6 +23,64 @@ static int occ_poll(struct occ *occ)
return occ->send_cmd(occ, cmd); return occ->send_cmd(occ, cmd);
} }
/* only need to do this once at startup, as OCC won't change sensors on us */
static void occ_parse_poll_response(struct occ *occ)
{
unsigned int i, old_offset, offset = 0, size = 0;
struct occ_sensor *sensor;
struct occ_sensors *sensors = &occ->sensors;
struct occ_response *resp = &occ->resp;
struct occ_poll_response *poll =
(struct occ_poll_response *)&resp->data[0];
struct occ_poll_response_header *header = &poll->header;
struct occ_sensor_data_block *block = &poll->block;
dev_info(occ->bus_dev, "OCC found, code level: %.16s\n",
header->occ_code_level);
for (i = 0; i < header->num_sensor_data_blocks; ++i) {
block = (struct occ_sensor_data_block *)((u8 *)block + offset);
old_offset = offset;
offset = (block->header.num_sensors *
block->header.sensor_length) + sizeof(block->header);
size += offset;
/* validate all the length/size fields */
if ((size + sizeof(*header)) >= OCC_RESP_DATA_BYTES) {
dev_warn(occ->bus_dev, "exceeded response buffer\n");
return;
}
dev_dbg(occ->bus_dev, " %04x..%04x: %.4s (%d sensors)\n",
old_offset, offset - 1, block->header.eye_catcher,
block->header.num_sensors);
/* match sensor block type */
if (strncmp(block->header.eye_catcher, "TEMP", 4) == 0)
sensor = &sensors->temp;
else if (strncmp(block->header.eye_catcher, "FREQ", 4) == 0)
sensor = &sensors->freq;
else if (strncmp(block->header.eye_catcher, "POWR", 4) == 0)
sensor = &sensors->power;
else if (strncmp(block->header.eye_catcher, "CAPS", 4) == 0)
sensor = &sensors->caps;
else if (strncmp(block->header.eye_catcher, "EXTN", 4) == 0)
sensor = &sensors->extended;
else {
dev_warn(occ->bus_dev, "sensor not supported %.4s\n",
block->header.eye_catcher);
continue;
}
sensor->num_sensors = block->header.num_sensors;
sensor->version = block->header.sensor_format;
sensor->data = &block->data;
}
dev_dbg(occ->bus_dev, "Max resp size: %u+%zd=%zd\n", size,
sizeof(*header), size + sizeof(*header));
}
int occ_setup(struct occ *occ, const char *name) int occ_setup(struct occ *occ, const char *name)
{ {
int rc; int rc;
...@@ -36,5 +95,7 @@ int occ_setup(struct occ *occ, const char *name) ...@@ -36,5 +95,7 @@ int occ_setup(struct occ *occ, const char *name)
return rc; return rc;
} }
occ_parse_poll_response(occ);
return 0; return 0;
} }
...@@ -20,10 +20,65 @@ struct occ_response { ...@@ -20,10 +20,65 @@ struct occ_response {
__be16 checksum; __be16 checksum;
} __packed; } __packed;
struct occ_sensor_data_block_header {
u8 eye_catcher[4];
u8 reserved;
u8 sensor_format;
u8 sensor_length;
u8 num_sensors;
} __packed;
struct occ_sensor_data_block {
struct occ_sensor_data_block_header header;
u32 data;
} __packed;
struct occ_poll_response_header {
u8 status;
u8 ext_status;
u8 occs_present;
u8 config_data;
u8 occ_state;
u8 mode;
u8 ips_status;
u8 error_log_id;
__be32 error_log_start_address;
__be16 error_log_length;
u16 reserved;
u8 occ_code_level[16];
u8 eye_catcher[6];
u8 num_sensor_data_blocks;
u8 sensor_data_block_header_version;
} __packed;
struct occ_poll_response {
struct occ_poll_response_header header;
struct occ_sensor_data_block block;
} __packed;
struct occ_sensor {
u8 num_sensors;
u8 version;
void *data; /* pointer to sensor data start within response */
};
/*
* OCC only provides one sensor data block of each type, but any number of
* sensors within that block.
*/
struct occ_sensors {
struct occ_sensor temp;
struct occ_sensor freq;
struct occ_sensor power;
struct occ_sensor caps;
struct occ_sensor extended;
};
struct occ { struct occ {
struct device *bus_dev; struct device *bus_dev;
struct occ_response resp; struct occ_response resp;
struct occ_sensors sensors;
u8 poll_cmd_data; /* to perform OCC poll command */ u8 poll_cmd_data; /* to perform OCC poll command */
int (*send_cmd)(struct occ *occ, u8 *cmd); int (*send_cmd)(struct occ *occ, u8 *cmd);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment