// SPDX-License-Identifier: GPL-2.0 #include #include #include "common.h" static int occ_poll(struct occ *occ) { u16 checksum = occ->poll_cmd_data + 1; u8 cmd[8]; /* big endian */ cmd[0] = 0; /* sequence number */ cmd[1] = 0; /* cmd type */ cmd[2] = 0; /* data length msb */ cmd[3] = 1; /* data length lsb */ cmd[4] = occ->poll_cmd_data; /* data */ cmd[5] = checksum >> 8; /* checksum msb */ cmd[6] = checksum & 0xFF; /* checksum lsb */ cmd[7] = 0; 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 rc; rc = occ_poll(occ); if (rc == -ESHUTDOWN) { dev_info(occ->bus_dev, "host is not ready\n"); return rc; } else if (rc < 0) { dev_err(occ->bus_dev, "failed to get OCC poll response: %d\n", rc); return rc; } occ_parse_poll_response(occ); return 0; }