877 lines
23 KiB
C
877 lines
23 KiB
C
/* Copyright 2013-2014 IBM Corp.
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
|
* implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
/*
|
|
*/
|
|
|
|
|
|
/*
|
|
* Design note:
|
|
* 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.
|
|
*/
|
|
|
|
#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 */
|
|
uint64_t *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 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;
|
|
uint16_t 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 (sensor_mod_data[0] == attr->frc &&
|
|
sensor_mod_data[1] == attr->rid) {
|
|
switch (attr->spcn_attr) {
|
|
case SENSOR_STATUS:
|
|
sensor_data =
|
|
convert_status_to_fault(sensor_mod_data[3]);
|
|
break;
|
|
case SENSOR_THRS:
|
|
sensor_data = sensor_mod_data[6];
|
|
break;
|
|
case SENSOR_DATA:
|
|
sensor_data = sensor_mod_data[2];
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
sensor_buf_ptr += spcn_mod_data[attr->mod_index].entry_size;
|
|
}
|
|
|
|
*(attr->sensor_data) = 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 = resp_msg->data.words[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,
|
|
attr->async_token, 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 = (msg->resp->data.words[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) = 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,
|
|
uint64_t *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[header->frc],
|
|
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[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, (header).frc, (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 (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 = {
|
|
SENSOR_FRC_POWER_SUPPLY,
|
|
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,
|
|
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(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 = (resp.data.words[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();
|
|
}
|