Skip to content

Commit b4aa1c6

Browse files
Eddie Jamesgroeck
authored andcommitted
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: Eddie James <eajames@linux.ibm.com> Signed-off-by: Guenter Roeck <linux@roeck-us.net>
1 parent 52128f6 commit b4aa1c6

File tree

2 files changed

+116
-0
lines changed

2 files changed

+116
-0
lines changed

drivers/hwmon/occ/common.c

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
// SPDX-License-Identifier: GPL-2.0
22

33
#include <linux/device.h>
4+
#include <linux/kernel.h>
45

56
#include "common.h"
67

@@ -22,6 +23,64 @@ static int occ_poll(struct occ *occ)
2223
return occ->send_cmd(occ, cmd);
2324
}
2425

26+
/* only need to do this once at startup, as OCC won't change sensors on us */
27+
static void occ_parse_poll_response(struct occ *occ)
28+
{
29+
unsigned int i, old_offset, offset = 0, size = 0;
30+
struct occ_sensor *sensor;
31+
struct occ_sensors *sensors = &occ->sensors;
32+
struct occ_response *resp = &occ->resp;
33+
struct occ_poll_response *poll =
34+
(struct occ_poll_response *)&resp->data[0];
35+
struct occ_poll_response_header *header = &poll->header;
36+
struct occ_sensor_data_block *block = &poll->block;
37+
38+
dev_info(occ->bus_dev, "OCC found, code level: %.16s\n",
39+
header->occ_code_level);
40+
41+
for (i = 0; i < header->num_sensor_data_blocks; ++i) {
42+
block = (struct occ_sensor_data_block *)((u8 *)block + offset);
43+
old_offset = offset;
44+
offset = (block->header.num_sensors *
45+
block->header.sensor_length) + sizeof(block->header);
46+
size += offset;
47+
48+
/* validate all the length/size fields */
49+
if ((size + sizeof(*header)) >= OCC_RESP_DATA_BYTES) {
50+
dev_warn(occ->bus_dev, "exceeded response buffer\n");
51+
return;
52+
}
53+
54+
dev_dbg(occ->bus_dev, " %04x..%04x: %.4s (%d sensors)\n",
55+
old_offset, offset - 1, block->header.eye_catcher,
56+
block->header.num_sensors);
57+
58+
/* match sensor block type */
59+
if (strncmp(block->header.eye_catcher, "TEMP", 4) == 0)
60+
sensor = &sensors->temp;
61+
else if (strncmp(block->header.eye_catcher, "FREQ", 4) == 0)
62+
sensor = &sensors->freq;
63+
else if (strncmp(block->header.eye_catcher, "POWR", 4) == 0)
64+
sensor = &sensors->power;
65+
else if (strncmp(block->header.eye_catcher, "CAPS", 4) == 0)
66+
sensor = &sensors->caps;
67+
else if (strncmp(block->header.eye_catcher, "EXTN", 4) == 0)
68+
sensor = &sensors->extended;
69+
else {
70+
dev_warn(occ->bus_dev, "sensor not supported %.4s\n",
71+
block->header.eye_catcher);
72+
continue;
73+
}
74+
75+
sensor->num_sensors = block->header.num_sensors;
76+
sensor->version = block->header.sensor_format;
77+
sensor->data = &block->data;
78+
}
79+
80+
dev_dbg(occ->bus_dev, "Max resp size: %u+%zd=%zd\n", size,
81+
sizeof(*header), size + sizeof(*header));
82+
}
83+
2584
int occ_setup(struct occ *occ, const char *name)
2685
{
2786
int rc;
@@ -36,5 +95,7 @@ int occ_setup(struct occ *occ, const char *name)
3695
return rc;
3796
}
3897

98+
occ_parse_poll_response(occ);
99+
39100
return 0;
40101
}

drivers/hwmon/occ/common.h

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,10 +20,65 @@ struct occ_response {
2020
__be16 checksum;
2121
} __packed;
2222

23+
struct occ_sensor_data_block_header {
24+
u8 eye_catcher[4];
25+
u8 reserved;
26+
u8 sensor_format;
27+
u8 sensor_length;
28+
u8 num_sensors;
29+
} __packed;
30+
31+
struct occ_sensor_data_block {
32+
struct occ_sensor_data_block_header header;
33+
u32 data;
34+
} __packed;
35+
36+
struct occ_poll_response_header {
37+
u8 status;
38+
u8 ext_status;
39+
u8 occs_present;
40+
u8 config_data;
41+
u8 occ_state;
42+
u8 mode;
43+
u8 ips_status;
44+
u8 error_log_id;
45+
__be32 error_log_start_address;
46+
__be16 error_log_length;
47+
u16 reserved;
48+
u8 occ_code_level[16];
49+
u8 eye_catcher[6];
50+
u8 num_sensor_data_blocks;
51+
u8 sensor_data_block_header_version;
52+
} __packed;
53+
54+
struct occ_poll_response {
55+
struct occ_poll_response_header header;
56+
struct occ_sensor_data_block block;
57+
} __packed;
58+
59+
struct occ_sensor {
60+
u8 num_sensors;
61+
u8 version;
62+
void *data; /* pointer to sensor data start within response */
63+
};
64+
65+
/*
66+
* OCC only provides one sensor data block of each type, but any number of
67+
* sensors within that block.
68+
*/
69+
struct occ_sensors {
70+
struct occ_sensor temp;
71+
struct occ_sensor freq;
72+
struct occ_sensor power;
73+
struct occ_sensor caps;
74+
struct occ_sensor extended;
75+
};
76+
2377
struct occ {
2478
struct device *bus_dev;
2579

2680
struct occ_response resp;
81+
struct occ_sensors sensors;
2782

2883
u8 poll_cmd_data; /* to perform OCC poll command */
2984
int (*send_cmd)(struct occ *occ, u8 *cmd);

0 commit comments

Comments
 (0)