aboutsummaryrefslogtreecommitdiffstats
path: root/roms/skiboot/hw/fsp/fsp-sensor.c
diff options
context:
space:
mode:
Diffstat (limited to 'roms/skiboot/hw/fsp/fsp-sensor.c')
-rw-r--r--roms/skiboot/hw/fsp/fsp-sensor.c860
1 files changed, 860 insertions, 0 deletions
diff --git a/roms/skiboot/hw/fsp/fsp-sensor.c b/roms/skiboot/hw/fsp/fsp-sensor.c
new file mode 100644
index 000000000..ffcd004f3
--- /dev/null
+++ b/roms/skiboot/hw/fsp/fsp-sensor.c
@@ -0,0 +1,860 @@
+// 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, &param->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();
+}