| // SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later |
| /* |
| * This code will enable the 'powernv' to retrieve sensor related data from FSP |
| * using SPCN passthru mailbox commands. |
| * |
| * The OPAL read sensor API in Sapphire is implemented as an 'asynchronous' read |
| * call that returns after queuing the read request. A unique sensor-id is |
| * expected as an argument for OPAL read call which has already been exported |
| * to the device tree during fsp init. The sapphire code decodes this Id to |
| * determine requested attribute and sensor. |
| * |
| * Copyright 2013-2017 IBM Corp. |
| */ |
| |
| #include <skiboot.h> |
| #include <fsp.h> |
| #include <lock.h> |
| #include <device.h> |
| #include <spcn.h> |
| #include <opal-api.h> |
| #include <opal-msg.h> |
| #include <errorlog.h> |
| #include <sensor.h> |
| |
| #define INVALID_DATA ((uint32_t)-1) |
| |
| /* Entry size of PRS command modifiers */ |
| #define PRS_STATUS_ENTRY_SZ 0x08 |
| #define SENSOR_PARAM_ENTRY_SZ 0x10 |
| #define SENSOR_DATA_ENTRY_SZ 0x08 |
| #define PROC_JUNC_ENTRY_SZ 0x04 |
| |
| DEFINE_LOG_ENTRY(OPAL_RC_SENSOR_INIT, OPAL_PLATFORM_ERR_EVT, OPAL_SENSOR, |
| OPAL_MISC_SUBSYSTEM, |
| OPAL_PREDICTIVE_ERR_FAULT_RECTIFY_REBOOT, |
| OPAL_NA); |
| |
| DEFINE_LOG_ENTRY(OPAL_RC_SENSOR_READ, OPAL_PLATFORM_ERR_EVT, OPAL_SENSOR, |
| OPAL_MISC_SUBSYSTEM, OPAL_INFO, |
| OPAL_NA); |
| |
| DEFINE_LOG_ENTRY(OPAL_RC_SENSOR_ASYNC_COMPLETE, OPAL_PLATFORM_ERR_EVT, |
| OPAL_SENSOR, OPAL_MISC_SUBSYSTEM, OPAL_INFO, |
| OPAL_NA); |
| |
| /* FSP response status codes */ |
| enum { |
| SP_RSP_STATUS_VALID_DATA = 0x00, |
| SP_RSP_STATUS_INVALID_DATA = 0x22, |
| SP_RSP_STATUS_SPCN_ERR = 0xA8, |
| SP_RSP_STATUS_DMA_ERR = 0x24, |
| }; |
| |
| enum sensor_state { |
| SENSOR_VALID_DATA, |
| SENSOR_INVALID_DATA, |
| SENSOR_SPCN_ERROR, |
| SENSOR_DMA_ERROR, |
| SENSOR_PERMANENT_ERROR, |
| SENSOR_OPAL_ERROR, |
| }; |
| |
| enum spcn_attr { |
| SENSOR_STATUS, |
| SENSOR_THRS, |
| SENSOR_DATA, |
| SENSOR_MAX, |
| }; |
| |
| /* Parsed sensor attributes, passed through OPAL */ |
| struct opal_sensor_data { |
| uint64_t async_token; /* Asynchronous token */ |
| __be64 *sensor_data; /* Kernel pointer to copy data */ |
| enum spcn_attr spcn_attr; /* Modifier attribute */ |
| uint16_t rid; /* Sensor RID */ |
| uint8_t frc; /* Sensor resource class */ |
| uint32_t mod_index; /* Modifier index*/ |
| uint32_t offset; /* Offset in sensor buffer */ |
| }; |
| |
| struct spcn_mod { |
| uint8_t mod; /* Modifier code */ |
| uint8_t entry_size; /* Size of each entry in response buffer */ |
| uint16_t entry_count; /* Number of entries */ |
| }; |
| |
| static struct spcn_mod spcn_mod_data[] = { |
| {SPCN_MOD_PRS_STATUS_FIRST, PRS_STATUS_ENTRY_SZ, 0 }, |
| {SPCN_MOD_PRS_STATUS_SUBS, PRS_STATUS_ENTRY_SZ, 0 }, |
| {SPCN_MOD_SENSOR_PARAM_FIRST, SENSOR_PARAM_ENTRY_SZ, 0 }, |
| {SPCN_MOD_SENSOR_PARAM_SUBS, SENSOR_PARAM_ENTRY_SZ, 0 }, |
| {SPCN_MOD_SENSOR_DATA_FIRST, SENSOR_DATA_ENTRY_SZ, 0 }, |
| {SPCN_MOD_SENSOR_DATA_SUBS, SENSOR_DATA_ENTRY_SZ, 0 }, |
| /* TODO Support this modifier '0x14', if required */ |
| /* {SPCN_MOD_PROC_JUNC_TEMP, PROC_JUNC_ENTRY_SZ, 0, NULL}, */ |
| {SPCN_MOD_SENSOR_POWER, SENSOR_DATA_ENTRY_SZ, 0 }, |
| {SPCN_MOD_LAST, 0xff, 0xffff} |
| }; |
| |
| /* Frame resource class (FRC) names */ |
| static const char *frc_names[] = { |
| /* 0x00 and 0x01 are reserved */ |
| NULL, |
| NULL, |
| "power-controller", |
| "power", |
| "regulator", |
| "cooling-fan", |
| "cooling-controller", |
| "battery-charger", |
| "battery-pack", |
| "amb-temp", |
| "temp", |
| "vrm", |
| "riser-card", |
| "io-backplane" |
| }; |
| |
| #define SENSOR_MAX_SIZE 0x00100000 |
| static void *sensor_buffer = NULL; |
| static enum sensor_state sensor_state; |
| static bool prev_msg_consumed = true; |
| static struct lock sensor_lock; |
| |
| /* Function prototypes */ |
| static int64_t fsp_sensor_send_read_request(struct opal_sensor_data *attr); |
| static void queue_msg_for_delivery(int rc, struct opal_sensor_data *attr); |
| |
| |
| /* |
| * Power Resource Status (PRS) |
| * Command: 0x42 |
| * |
| * Modifier: 0x01 |
| * -------------------------------------------------------------------------- |
| * | 0 1 2 3 4 5 6 7 | |
| * -------------------------------------------------------------------------- |
| * |Frame resrc class| PRID | SRC | Status | |
| * -------------------------------------------------------------------------- |
| * |
| * |
| * Modifier: 0x10 |
| * -------------------------------------------------------------------------- |
| * | 0 1 2 3 4 5 6 7 | |
| * -------------------------------------------------------------------------- |
| * |Frame resrc class| PRID | Sensor location | |
| * -------------------------------------------------------------------------- |
| * -------------------------------------------------------------------------- |
| * | 8 9 10 11 12 13 14 15 | |
| * -------------------------------------------------------------------------- |
| * | Reserved | Reserved | Threshold | Status | |
| * -------------------------------------------------------------------------- |
| * |
| * |
| * Modifier: 0x12 |
| * -------------------------------------------------------------------------- |
| * | 0 1 2 3 4 5 6 7 | |
| * -------------------------------------------------------------------------- |
| * |Frame resrc class| PRID | Sensor data | Status | |
| * -------------------------------------------------------------------------- |
| * |
| * |
| * Modifier: 0x14 |
| * -------------------------------------------------------------------------- |
| * | 0 1 2 3 | |
| * -------------------------------------------------------------------------- |
| * |Enclosure Tj Avg | Chip Tj Avg | Reserved | Reserved | |
| * -------------------------------------------------------------------------- |
| */ |
| |
| |
| /* |
| * When coming from a SENSOR_POWER modifier command, the resource id |
| * of a power supply is on one byte and misses a "subclass" byte |
| * (0x10). This routine adds it to be consistent with the PRS_STATUS |
| * modifier command. |
| */ |
| #define normalize_power_rid(rid) (0x1000|(rid)) |
| |
| static uint32_t sensor_power_process_data(uint16_t rid, |
| struct sensor_power *power) |
| { |
| int i; |
| |
| if (!sensor_power_is_valid(power)) { |
| prlog(PR_TRACE, "Power Sensor data not valid\n"); |
| return INVALID_DATA; |
| } |
| |
| for (i = 0; i < sensor_power_count(power); i++) { |
| prlog(PR_TRACE, "Power[%d]: %d mW\n", i, |
| power->supplies[i].milliwatts); |
| if (rid == normalize_power_rid(power->supplies[i].rid)) |
| return be32_to_cpu(power->supplies[i].milliwatts) / 1000; |
| } |
| |
| return 0; |
| } |
| |
| static inline uint16_t convert_status_to_fault(uint16_t status) |
| { |
| return status & 0x06; |
| } |
| |
| static void fsp_sensor_process_data(struct opal_sensor_data *attr) |
| { |
| uint8_t *sensor_buf_ptr = (uint8_t *)sensor_buffer; |
| uint32_t sensor_data = INVALID_DATA; |
| __be16 sensor_mod_data[8]; |
| int count; |
| |
| for (count = 0; count < spcn_mod_data[attr->mod_index].entry_count; |
| count++) { |
| memcpy((void *)sensor_mod_data, sensor_buf_ptr, |
| spcn_mod_data[attr->mod_index].entry_size); |
| if (spcn_mod_data[attr->mod_index].mod == SPCN_MOD_PROC_JUNC_TEMP) { |
| /* TODO Support this modifier '0x14', if required */ |
| |
| } else if (spcn_mod_data[attr->mod_index].mod == SPCN_MOD_SENSOR_POWER) { |
| sensor_data = sensor_power_process_data(attr->rid, |
| (struct sensor_power *) sensor_buf_ptr); |
| break; |
| } else if (be16_to_cpu(sensor_mod_data[0]) == attr->frc && |
| be16_to_cpu(sensor_mod_data[1]) == attr->rid) { |
| switch (attr->spcn_attr) { |
| case SENSOR_STATUS: |
| sensor_data = |
| convert_status_to_fault(be16_to_cpu(sensor_mod_data[3])); |
| break; |
| case SENSOR_THRS: |
| sensor_data = be16_to_cpu(sensor_mod_data[6]); |
| break; |
| case SENSOR_DATA: |
| sensor_data = be16_to_cpu(sensor_mod_data[2]); |
| break; |
| default: |
| break; |
| } |
| |
| break; |
| } |
| |
| sensor_buf_ptr += spcn_mod_data[attr->mod_index].entry_size; |
| } |
| |
| *attr->sensor_data = cpu_to_be64(sensor_data); |
| if (sensor_data == INVALID_DATA) |
| queue_msg_for_delivery(OPAL_PARTIAL, attr); |
| else |
| queue_msg_for_delivery(OPAL_SUCCESS, attr); |
| } |
| |
| static int fsp_sensor_process_read(struct fsp_msg *resp_msg) |
| { |
| uint8_t mbx_rsp_status; |
| uint32_t size = 0; |
| |
| mbx_rsp_status = (resp_msg->word1 >> 8) & 0xff; |
| switch (mbx_rsp_status) { |
| case SP_RSP_STATUS_VALID_DATA: |
| sensor_state = SENSOR_VALID_DATA; |
| size = fsp_msg_get_data_word(resp_msg, 1) & 0xffff; |
| break; |
| case SP_RSP_STATUS_INVALID_DATA: |
| log_simple_error(&e_info(OPAL_RC_SENSOR_READ), |
| "SENSOR: %s: Received invalid data\n", __func__); |
| sensor_state = SENSOR_INVALID_DATA; |
| break; |
| case SP_RSP_STATUS_SPCN_ERR: |
| log_simple_error(&e_info(OPAL_RC_SENSOR_READ), |
| "SENSOR: %s: Failure due to SPCN error\n", __func__); |
| sensor_state = SENSOR_SPCN_ERROR; |
| break; |
| case SP_RSP_STATUS_DMA_ERR: |
| log_simple_error(&e_info(OPAL_RC_SENSOR_READ), |
| "SENSOR: %s: Failure due to DMA error\n", __func__); |
| sensor_state = SENSOR_DMA_ERROR; |
| break; |
| default: |
| log_simple_error(&e_info(OPAL_RC_SENSOR_READ), |
| "SENSOR %s: Read failed, status:0x%02X\n", |
| __func__, mbx_rsp_status); |
| sensor_state = SENSOR_INVALID_DATA; |
| break; |
| } |
| |
| return size; |
| } |
| |
| static void queue_msg_for_delivery(int rc, struct opal_sensor_data *attr) |
| { |
| prlog(PR_INSANE, "%s: rc:%d, data:%lld\n", |
| __func__, rc, *(attr->sensor_data)); |
| check_sensor_read(attr->async_token); |
| opal_queue_msg(OPAL_MSG_ASYNC_COMP, NULL, NULL, |
| cpu_to_be64(attr->async_token), |
| cpu_to_be64(rc)); |
| spcn_mod_data[attr->mod_index].entry_count = 0; |
| free(attr); |
| prev_msg_consumed = true; |
| } |
| |
| static void fsp_sensor_read_complete(struct fsp_msg *msg) |
| { |
| struct opal_sensor_data *attr = msg->user_data; |
| enum spcn_rsp_status status; |
| int rc, size; |
| |
| prlog(PR_INSANE, "%s()\n", __func__); |
| |
| status = (fsp_msg_get_data_word(msg->resp, 1) >> 24) & 0xff; |
| size = fsp_sensor_process_read(msg->resp); |
| fsp_freemsg(msg); |
| |
| lock(&sensor_lock); |
| if (sensor_state == SENSOR_VALID_DATA) { |
| spcn_mod_data[attr->mod_index].entry_count += (size / |
| spcn_mod_data[attr->mod_index].entry_size); |
| attr->offset += size; |
| /* Fetch the subsequent entries of the same modifier type */ |
| if (status == SPCN_RSP_STATUS_COND_SUCCESS) { |
| switch (spcn_mod_data[attr->mod_index].mod) { |
| case SPCN_MOD_PRS_STATUS_FIRST: |
| case SPCN_MOD_SENSOR_PARAM_FIRST: |
| case SPCN_MOD_SENSOR_DATA_FIRST: |
| attr->mod_index++; |
| spcn_mod_data[attr->mod_index].entry_count = |
| spcn_mod_data[attr->mod_index - 1]. |
| entry_count; |
| spcn_mod_data[attr->mod_index - 1].entry_count = 0; |
| break; |
| default: |
| break; |
| } |
| |
| rc = fsp_sensor_send_read_request(attr); |
| if (rc != OPAL_ASYNC_COMPLETION) |
| goto err; |
| } else { /* Notify 'powernv' of read completion */ |
| fsp_sensor_process_data(attr); |
| } |
| } else { |
| rc = OPAL_INTERNAL_ERROR; |
| goto err; |
| } |
| unlock(&sensor_lock); |
| return; |
| err: |
| *attr->sensor_data = cpu_to_be64(INVALID_DATA); |
| queue_msg_for_delivery(rc, attr); |
| unlock(&sensor_lock); |
| log_simple_error(&e_info(OPAL_RC_SENSOR_ASYNC_COMPLETE), |
| "SENSOR: %s: Failed to queue the " |
| "read request to fsp\n", __func__); |
| } |
| |
| static int64_t fsp_sensor_send_read_request(struct opal_sensor_data *attr) |
| { |
| int rc; |
| struct fsp_msg *msg; |
| uint32_t align; |
| uint32_t cmd_header; |
| |
| if (fsp_in_rr()) |
| return OPAL_BUSY; |
| |
| prlog(PR_INSANE, "Get the data for modifier [%x]\n", |
| spcn_mod_data[attr->mod_index].mod); |
| |
| if (spcn_mod_data[attr->mod_index].mod == SPCN_MOD_PROC_JUNC_TEMP) { |
| /* TODO Support this modifier '0x14', if required */ |
| align = attr->offset % sizeof(uint32_t); |
| if (align) |
| attr->offset += (sizeof(uint32_t) - align); |
| |
| /* TODO Add 8 byte command data required for mod 0x14 */ |
| |
| attr->offset += 8; |
| |
| cmd_header = spcn_mod_data[attr->mod_index].mod << 24 | |
| SPCN_CMD_PRS << 16 | 0x0008; |
| } else { |
| cmd_header = spcn_mod_data[attr->mod_index].mod << 24 | |
| SPCN_CMD_PRS << 16; |
| } |
| |
| msg = fsp_mkmsg(FSP_CMD_SPCN_PASSTHRU, 4, |
| SPCN_ADDR_MODE_CEC_NODE, cmd_header, 0, |
| PSI_DMA_SENSOR_BUF + attr->offset); |
| |
| if (!msg) { |
| log_simple_error(&e_info(OPAL_RC_SENSOR_READ), "SENSOR: Failed " |
| "to allocate read message\n"); |
| return OPAL_INTERNAL_ERROR; |
| } |
| |
| msg->user_data = attr; |
| rc = fsp_queue_msg(msg, fsp_sensor_read_complete); |
| if (rc) { |
| fsp_freemsg(msg); |
| msg = NULL; |
| log_simple_error(&e_info(OPAL_RC_SENSOR_READ), "SENSOR: Failed " |
| "to queue read message (%d)\n", rc); |
| return OPAL_INTERNAL_ERROR; |
| } |
| |
| return OPAL_ASYNC_COMPLETION; |
| } |
| |
| /* |
| * These are the resources we know about and for which we provide a |
| * mapping in the device tree to capture data from the OS. Just |
| * discard the other ones for the moment. |
| */ |
| static inline bool sensor_frc_is_valid(uint16_t frc) |
| { |
| switch (frc) { |
| case SENSOR_FRC_POWER_SUPPLY: |
| case SENSOR_FRC_COOLING_FAN: |
| case SENSOR_FRC_AMB_TEMP: |
| return true; |
| default: |
| return false; |
| } |
| } |
| |
| /* |
| * Each attribute of a resource needs a request to the FSP to capture |
| * its data. The routine below provides the mapping between the |
| * attribute and the PRS command modifier to use. |
| * |
| * resource | data | thrs | status | |
| * ----------------+--------+--------+-----------+ |
| * power_supply | POWER | | | |
| * | | | PRS | |
| * ----------------+--------+--------+-----------+ |
| * amb-temp | DATA | | DATA | |
| * | | PARAM | PARAM (*) | |
| * ----------------+--------+--------+-----------+ |
| * fan | DATA | | DATA (*) | |
| * | | PARAM | PARAM (*) | |
| * | | | PRS | |
| * |
| * (*) don't use the attribute given by this command modifier |
| */ |
| static int64_t parse_sensor_id(uint32_t handler, struct opal_sensor_data *attr) |
| { |
| uint32_t mod, index; |
| |
| attr->frc = sensor_get_frc(handler); |
| attr->rid = sensor_get_rid(handler); |
| attr->spcn_attr = sensor_get_attr(handler); |
| |
| if (!sensor_frc_is_valid(attr->frc)) |
| return OPAL_PARAMETER; |
| |
| /* now compute the PRS command modifier which will be used to |
| * request a resource attribute from the FSP */ |
| switch (attr->spcn_attr) { |
| case SENSOR_DATA: |
| if (attr->frc == SENSOR_FRC_POWER_SUPPLY) |
| mod = SPCN_MOD_SENSOR_POWER; |
| else |
| mod = SPCN_MOD_SENSOR_DATA_FIRST; |
| break; |
| |
| case SENSOR_THRS: |
| mod = SPCN_MOD_SENSOR_PARAM_FIRST; |
| break; |
| |
| case SENSOR_STATUS: |
| switch (attr->frc) { |
| case SENSOR_FRC_AMB_TEMP: |
| mod = SPCN_MOD_SENSOR_DATA_FIRST; |
| break; |
| case SENSOR_FRC_POWER_SUPPLY: |
| case SENSOR_FRC_COOLING_FAN: |
| mod = SPCN_MOD_PRS_STATUS_FIRST; |
| break; |
| default: |
| return OPAL_PARAMETER; |
| } |
| break; |
| |
| default: |
| return OPAL_PARAMETER; |
| } |
| |
| for (index = 0; spcn_mod_data[index].mod != SPCN_MOD_LAST; index++) { |
| if (spcn_mod_data[index].mod == mod) |
| break; |
| } |
| |
| attr->mod_index = index; |
| return 0; |
| } |
| |
| |
| int64_t fsp_opal_read_sensor(uint32_t sensor_hndl, int token, |
| __be64 *sensor_data) |
| { |
| struct opal_sensor_data *attr; |
| int64_t rc; |
| |
| prlog(PR_INSANE, "fsp_opal_read_sensor [%08x]\n", sensor_hndl); |
| |
| if (fsp_in_rr()) |
| return OPAL_BUSY; |
| |
| if (sensor_state == SENSOR_PERMANENT_ERROR) { |
| rc = OPAL_HARDWARE; |
| goto out; |
| } |
| |
| if (!sensor_hndl) { |
| rc = OPAL_PARAMETER; |
| goto out; |
| } |
| |
| lock(&sensor_lock); |
| if (prev_msg_consumed) { |
| attr = zalloc(sizeof(*attr)); |
| if (!attr) { |
| log_simple_error(&e_info(OPAL_RC_SENSOR_READ), |
| "SENSOR: Failed to allocate memory\n"); |
| rc = OPAL_NO_MEM; |
| goto out_lock; |
| } |
| |
| /* Parse the sensor id and store them to the local structure */ |
| rc = parse_sensor_id(sensor_hndl, attr); |
| if (rc) { |
| log_simple_error(&e_info(OPAL_RC_SENSOR_READ), |
| "SENSOR: %s: Failed to parse the sensor " |
| "handle[0x%08x]\n", __func__, sensor_hndl); |
| goto out_free; |
| } |
| /* Kernel buffer pointer to copy the data later when ready */ |
| attr->sensor_data = sensor_data; |
| attr->async_token = token; |
| |
| rc = fsp_sensor_send_read_request(attr); |
| if (rc != OPAL_ASYNC_COMPLETION) { |
| log_simple_error(&e_info(OPAL_RC_SENSOR_READ), |
| "SENSOR: %s: Failed to queue the read " |
| "request to fsp\n", __func__); |
| goto out_free; |
| } |
| |
| prev_msg_consumed = false; |
| } else { |
| rc = OPAL_BUSY_EVENT; |
| } |
| |
| unlock(&sensor_lock); |
| return rc; |
| |
| out_free: |
| free(attr); |
| out_lock: |
| unlock(&sensor_lock); |
| out: |
| return rc; |
| } |
| |
| |
| #define MAX_NAME 64 |
| |
| static struct dt_node *sensor_get_node(struct dt_node *sensors, |
| struct sensor_header *header, const char* attrname) |
| { |
| char name[MAX_NAME]; |
| struct dt_node *node; |
| |
| /* |
| * Just use the resource class name and resource id. This |
| * should be obvious enough for a node name. |
| */ |
| snprintf(name, sizeof(name), "%s#%d-%s", frc_names[be16_to_cpu(header->frc)], be16_to_cpu(header->rid), attrname); |
| |
| /* |
| * The same resources are reported by the different PRS |
| * subcommands (PRS_STATUS, SENSOR_PARAM, SENSOR_DATA). So we |
| * need to check that we did not already create the device |
| * node. |
| */ |
| node = dt_find_by_path(sensors, name); |
| if (!node) { |
| prlog(PR_INFO, "SENSOR: creating node %s\n", name); |
| |
| node = dt_new(sensors, name); |
| |
| snprintf(name, sizeof(name), "ibm,opal-sensor-%s", |
| frc_names[be16_to_cpu(header->frc)]); |
| dt_add_property_string(node, "compatible", name); |
| } else { |
| /** |
| * @fwts-label OPALSensorNodeExists |
| * @fwts-advice OPAL had trouble creating the sensor |
| * nodes in the device tree as there was already one there. |
| * This indicates either the device tree from Hostboot |
| * already filled in sensors or an OPAL bug. |
| */ |
| prlog(PR_ERR, "SENSOR: node %s exists\n", name); |
| } |
| return node; |
| } |
| |
| #define sensor_handler(header, attr_num) \ |
| sensor_make_handler(SENSOR_FSP, be16_to_cpu((header).frc), be16_to_cpu((header).rid), attr_num) |
| |
| static int add_sensor_prs(struct dt_node *sensors, struct sensor_prs *prs) |
| { |
| struct dt_node *node; |
| |
| node = sensor_get_node(sensors, &prs->header, "faulted"); |
| if (!node) |
| return -1; |
| |
| dt_add_property_cells(node, "sensor-id", |
| sensor_handler(prs->header, SENSOR_STATUS)); |
| return 0; |
| } |
| |
| static int add_sensor_param(struct dt_node *sensors, struct sensor_param *param) |
| { |
| struct dt_node *node; |
| |
| node = sensor_get_node(sensors, ¶m->header, "thrs"); |
| if (!node) |
| return -1; |
| |
| dt_add_property_string(node, "ibm,loc-code", param->location); |
| dt_add_property_cells(node, "sensor-id", |
| sensor_handler(param->header, SENSOR_THRS)); |
| /* don't use the status coming from the response of the |
| * SENSOR_PARAM subcommand */ |
| return 0; |
| } |
| |
| static int add_sensor_data(struct dt_node *sensors, |
| struct sensor_data *data) |
| { |
| struct dt_node *node; |
| |
| node = sensor_get_node(sensors, &data->header, "data"); |
| if (!node) |
| return -1; |
| |
| dt_add_property_cells(node, "sensor-id", |
| sensor_handler(data->header, SENSOR_DATA)); |
| |
| /* Let's make sure we are not adding a duplicate device node. |
| * Some resource, like fans, get their status attribute from |
| * three different commands ... |
| */ |
| if (be16_to_cpu(data->header.frc) == SENSOR_FRC_AMB_TEMP) { |
| node = sensor_get_node(sensors, &data->header, "faulted"); |
| if (!node) |
| return -1; |
| |
| dt_add_property_cells(node, "sensor-id", |
| sensor_handler(data->header, SENSOR_STATUS)); |
| } |
| |
| return 0; |
| } |
| |
| static int add_sensor_power(struct dt_node *sensors, struct sensor_power *power) |
| { |
| int i; |
| struct dt_node *node; |
| |
| if (!sensor_power_is_valid(power)) |
| return -1; |
| |
| for (i = 0; i < sensor_power_count(power); i++) { |
| struct sensor_header header = { |
| cpu_to_be16(SENSOR_FRC_POWER_SUPPLY), |
| cpu_to_be16(normalize_power_rid(power->supplies[i].rid)) |
| }; |
| |
| node = sensor_get_node(sensors, &header, "data"); |
| |
| prlog(PR_TRACE, "SENSOR: Power[%d] : %d mW\n", |
| power->supplies[i].rid, |
| be32_to_cpu(power->supplies[i].milliwatts)); |
| |
| dt_add_property_cells(node, "sensor-id", |
| sensor_handler(header, SENSOR_DATA)); |
| } |
| return 0; |
| } |
| |
| static void add_sensor_ids(struct dt_node *sensors) |
| { |
| uint8_t *sensor_buf_ptr = (uint8_t *)sensor_buffer; |
| struct spcn_mod *smod; |
| int i; |
| |
| for (smod = spcn_mod_data; smod->mod != SPCN_MOD_LAST; smod++) { |
| /* |
| * SPCN_MOD_SENSOR_POWER (0x1C) has a different layout. |
| */ |
| if (smod->mod == SPCN_MOD_SENSOR_POWER) { |
| add_sensor_power(sensors, |
| (struct sensor_power *) sensor_buf_ptr); |
| |
| sensor_buf_ptr += smod->entry_size * smod->entry_count; |
| continue; |
| } |
| |
| for (i = 0; i < smod->entry_count; i++) { |
| struct sensor_header *header = |
| (struct sensor_header *) sensor_buf_ptr; |
| |
| if (!sensor_frc_is_valid(be16_to_cpu(header->frc))) |
| goto out_sensor; |
| |
| switch (smod->mod) { |
| case SPCN_MOD_PROC_JUNC_TEMP: |
| /* TODO Support this modifier '0x14', |
| if required */ |
| break; |
| |
| case SPCN_MOD_PRS_STATUS_FIRST: |
| case SPCN_MOD_PRS_STATUS_SUBS: |
| add_sensor_prs(sensors, |
| (struct sensor_prs *) header); |
| break; |
| |
| case SPCN_MOD_SENSOR_PARAM_FIRST: |
| case SPCN_MOD_SENSOR_PARAM_SUBS: |
| add_sensor_param(sensors, |
| (struct sensor_param *) header); |
| break; |
| |
| case SPCN_MOD_SENSOR_DATA_FIRST: |
| case SPCN_MOD_SENSOR_DATA_SUBS: |
| add_sensor_data(sensors, |
| (struct sensor_data *) header); |
| |
| break; |
| |
| default: |
| prerror("SENSOR: unknown modifier : %x\n", |
| smod->mod); |
| } |
| |
| out_sensor: |
| sensor_buf_ptr += smod->entry_size; |
| } |
| } |
| } |
| |
| static void add_opal_sensor_node(void) |
| { |
| int index; |
| |
| if (!fsp_present()) |
| return; |
| |
| add_sensor_ids(sensor_node); |
| |
| /* Reset the entry count of each modifier */ |
| for (index = 0; spcn_mod_data[index].mod != SPCN_MOD_LAST; |
| index++) |
| spcn_mod_data[index].entry_count = 0; |
| } |
| |
| void fsp_init_sensor(void) |
| { |
| uint32_t cmd_header, align, size, psi_dma_offset = 0; |
| enum spcn_rsp_status status; |
| struct fsp_msg msg, resp; |
| int index, rc; |
| |
| if (!fsp_present()) { |
| sensor_state = SENSOR_PERMANENT_ERROR; |
| return; |
| } |
| |
| sensor_buffer = memalign(TCE_PSIZE, SENSOR_MAX_SIZE); |
| if (!sensor_buffer) { |
| log_simple_error(&e_info(OPAL_RC_SENSOR_INIT), "SENSOR: could " |
| "not allocate sensor_buffer!\n"); |
| return; |
| } |
| |
| /* Map TCE */ |
| fsp_tce_map(PSI_DMA_SENSOR_BUF, sensor_buffer, PSI_DMA_SENSOR_BUF_SZ); |
| |
| msg.resp = &resp; |
| |
| /* Traverse using all the modifiers to know all the sensors available |
| * in the system */ |
| for (index = 0; spcn_mod_data[index].mod != SPCN_MOD_LAST && |
| sensor_state == SENSOR_VALID_DATA;) { |
| prlog(PR_TRACE, "Get the data for modifier [%d]\n", |
| spcn_mod_data[index].mod); |
| if (spcn_mod_data[index].mod == SPCN_MOD_PROC_JUNC_TEMP) { |
| /* TODO Support this modifier 0x14, if required */ |
| align = psi_dma_offset % sizeof(uint32_t); |
| if (align) |
| psi_dma_offset += (sizeof(uint32_t) - align); |
| |
| /* TODO Add 8 byte command data required for mod 0x14 */ |
| psi_dma_offset += 8; |
| |
| cmd_header = spcn_mod_data[index].mod << 24 | |
| SPCN_CMD_PRS << 16 | 0x0008; |
| } else { |
| cmd_header = spcn_mod_data[index].mod << 24 | |
| SPCN_CMD_PRS << 16; |
| } |
| |
| fsp_fillmsg(&msg, FSP_CMD_SPCN_PASSTHRU, 4, |
| SPCN_ADDR_MODE_CEC_NODE, cmd_header, 0, |
| PSI_DMA_SENSOR_BUF + psi_dma_offset); |
| |
| rc = fsp_sync_msg(&msg, false); |
| if (rc >= 0) { |
| status = (fsp_msg_get_data_word(&resp, 1) >> 24) & 0xff; |
| size = fsp_sensor_process_read(&resp); |
| psi_dma_offset += size; |
| spcn_mod_data[index].entry_count += (size / |
| spcn_mod_data[index].entry_size); |
| } else { |
| sensor_state = SENSOR_PERMANENT_ERROR; |
| break; |
| } |
| |
| switch (spcn_mod_data[index].mod) { |
| case SPCN_MOD_PRS_STATUS_FIRST: |
| case SPCN_MOD_SENSOR_PARAM_FIRST: |
| case SPCN_MOD_SENSOR_DATA_FIRST: |
| if (status == SPCN_RSP_STATUS_COND_SUCCESS) |
| index++; |
| else |
| index += 2; |
| |
| break; |
| case SPCN_MOD_PRS_STATUS_SUBS: |
| case SPCN_MOD_SENSOR_PARAM_SUBS: |
| case SPCN_MOD_SENSOR_DATA_SUBS: |
| if (status != SPCN_RSP_STATUS_COND_SUCCESS) |
| index++; |
| break; |
| case SPCN_MOD_SENSOR_POWER: |
| index++; |
| default: |
| break; |
| } |
| } |
| |
| if (sensor_state != SENSOR_VALID_DATA) |
| sensor_state = SENSOR_PERMANENT_ERROR; |
| else |
| add_opal_sensor_node(); |
| } |