#include "ras.h"
#define RAS_SMU_MESSAGE_TIMEOUT_MS 1000
void ras_fw_init_feature_flags(struct ras_core_context *ras_core)
{
struct ras_mp1 *mp1 = &ras_core->ras_mp1;
const struct ras_mp1_sys_func *sys_func = mp1->sys_func;
uint64_t flags = 0ULL;
if (!sys_func || !sys_func->mp1_get_ras_enabled_mask)
return;
if (!sys_func->mp1_get_ras_enabled_mask(ras_core, &flags))
ras_core->ras_fw_features = flags;
}
bool ras_fw_eeprom_supported(struct ras_core_context *ras_core)
{
return !!(ras_core->ras_fw_features & RAS_CORE_FW_FEATURE_BIT__RAS_EEPROM);
}
int ras_fw_get_table_version(struct ras_core_context *ras_core,
uint32_t *table_version)
{
struct ras_mp1 *mp1 = &ras_core->ras_mp1;
const struct ras_mp1_sys_func *sys_func = mp1->sys_func;
return sys_func->mp1_send_eeprom_msg(ras_core,
RAS_SMU_GetRASTableVersion, 0, table_version);
}
int ras_fw_get_badpage_count(struct ras_core_context *ras_core,
uint32_t *count, uint32_t timeout)
{
struct ras_mp1 *mp1 = &ras_core->ras_mp1;
const struct ras_mp1_sys_func *sys_func = mp1->sys_func;
uint64_t end, now;
int ret = 0;
now = (uint64_t)ktime_to_ms(ktime_get());
end = now + timeout;
do {
ret = sys_func->mp1_send_eeprom_msg(ras_core,
RAS_SMU_GetBadPageCount, 0, count);
if (ret != -EBUSY)
return ret;
mdelay(10);
now = (uint64_t)ktime_to_ms(ktime_get());
} while (now < end);
RAS_DEV_ERR(ras_core->dev,
"smu get bad page count timeout!\n");
return ret;
}
int ras_fw_get_badpage_mca_addr(struct ras_core_context *ras_core,
uint16_t index, uint64_t *mca_addr)
{
struct ras_mp1 *mp1 = &ras_core->ras_mp1;
const struct ras_mp1_sys_func *sys_func = mp1->sys_func;
uint32_t temp_arg, temp_addr_lo, temp_addr_high;
int ret;
temp_arg = index | (1 << 16);
ret = sys_func->mp1_send_eeprom_msg(ras_core,
RAS_SMU_GetBadPageMcaAddr, temp_arg, &temp_addr_lo);
if (ret)
return ret;
temp_arg = index | (2 << 16);
ret = sys_func->mp1_send_eeprom_msg(ras_core,
RAS_SMU_GetBadPageMcaAddr, temp_arg, &temp_addr_high);
if (!ret)
*mca_addr = (uint64_t)temp_addr_high << 32 | temp_addr_lo;
return ret;
}
int ras_fw_set_timestamp(struct ras_core_context *ras_core,
uint64_t timestamp)
{
struct ras_mp1 *mp1 = &ras_core->ras_mp1;
const struct ras_mp1_sys_func *sys_func = mp1->sys_func;
return sys_func->mp1_send_eeprom_msg(ras_core,
RAS_SMU_SetTimestamp, (uint32_t)timestamp, 0);
}
int ras_fw_get_timestamp(struct ras_core_context *ras_core,
uint16_t index, uint64_t *timestamp)
{
struct ras_mp1 *mp1 = &ras_core->ras_mp1;
const struct ras_mp1_sys_func *sys_func = mp1->sys_func;
uint32_t temp = 0;
int ret;
ret = sys_func->mp1_send_eeprom_msg(ras_core,
RAS_SMU_GetTimestamp, index, &temp);
if (!ret)
*timestamp = temp;
return ret;
}
int ras_fw_get_badpage_ipid(struct ras_core_context *ras_core,
uint16_t index, uint64_t *ipid)
{
struct ras_mp1 *mp1 = &ras_core->ras_mp1;
const struct ras_mp1_sys_func *sys_func = mp1->sys_func;
uint32_t temp_arg, temp_ipid_lo, temp_ipid_high;
int ret;
temp_arg = index | (1 << 16);
ret = sys_func->mp1_send_eeprom_msg(ras_core,
RAS_SMU_GetBadPageIpid, temp_arg, &temp_ipid_lo);
if (ret)
return ret;
temp_arg = index | (2 << 16);
ret = sys_func->mp1_send_eeprom_msg(ras_core,
RAS_SMU_GetBadPageIpid, temp_arg, &temp_ipid_high);
if (!ret)
*ipid = (uint64_t)temp_ipid_high << 32 | temp_ipid_lo;
return ret;
}
int ras_fw_erase_ras_table(struct ras_core_context *ras_core,
uint32_t *result)
{
struct ras_mp1 *mp1 = &ras_core->ras_mp1;
const struct ras_mp1_sys_func *sys_func = mp1->sys_func;
return sys_func->mp1_send_eeprom_msg(ras_core,
RAS_SMU_EraseRasTable, 0, result);
}
int ras_fw_eeprom_reset_table(struct ras_core_context *ras_core)
{
struct ras_fw_eeprom_control *control = &ras_core->ras_fw_eeprom;
u32 erase_res = 0;
int res;
mutex_lock(&control->ras_tbl_mutex);
res = ras_fw_erase_ras_table(ras_core, &erase_res);
if (res || erase_res) {
RAS_DEV_WARN(ras_core->dev, "RAS EEPROM reset failed, res:%d result:%d",
res, erase_res);
if (!res)
res = -EIO;
}
control->ras_num_recs = 0;
control->bad_channel_bitmap = 0;
ras_core_event_notify(ras_core, RAS_EVENT_ID__UPDATE_BAD_PAGE_NUM,
&control->ras_num_recs);
ras_core_event_notify(ras_core, RAS_EVENT_ID__UPDATE_BAD_CHANNEL_BITMAP,
&control->bad_channel_bitmap);
control->update_channel_flag = false;
mutex_unlock(&control->ras_tbl_mutex);
return res;
}
bool ras_fw_eeprom_check_safety_watermark(struct ras_core_context *ras_core)
{
struct ras_fw_eeprom_control *control = &ras_core->ras_fw_eeprom;
bool ret = false;
int bad_page_count;
if (!control->record_threshold_config)
return false;
bad_page_count = ras_umc_get_badpage_count(ras_core);
if (bad_page_count > control->record_threshold_count)
RAS_DEV_WARN(ras_core->dev, "RAS records:%d exceed threshold:%d",
bad_page_count, control->record_threshold_count);
if ((control->record_threshold_config == WARN_NONSTOP_OVER_THRESHOLD) ||
(control->record_threshold_config == NONSTOP_OVER_THRESHOLD)) {
RAS_DEV_WARN(ras_core->dev,
"Please consult AMD Service Action Guide (SAG) for appropriate service procedures.\n");
ret = false;
} else {
ras_core->is_rma = true;
RAS_DEV_WARN(ras_core->dev,
"Please consider adjusting the customized threshold.\n");
ret = true;
}
return ret;
}
int ras_fw_eeprom_append(struct ras_core_context *ras_core,
struct eeprom_umc_record *record, const u32 num)
{
struct ras_fw_eeprom_control *control = &ras_core->ras_fw_eeprom;
int threshold_config = control->record_threshold_config;
int i, bad_page_count;
mutex_lock(&control->ras_tbl_mutex);
for (i = 0; i < num; i++) {
if ((record[i].mem_channel < BITS_PER_TYPE(control->bad_channel_bitmap)) &&
!(control->bad_channel_bitmap & (1 << record[i].mem_channel))) {
control->bad_channel_bitmap |= 1 << record[i].mem_channel;
control->update_channel_flag = true;
}
}
control->ras_num_recs += num;
bad_page_count = ras_umc_get_badpage_count(ras_core);
if (threshold_config != 0 &&
bad_page_count > control->record_threshold_count) {
RAS_DEV_WARN(ras_core->dev,
"Saved bad pages %d reaches threshold value %d\n",
bad_page_count, control->record_threshold_count);
if ((threshold_config != WARN_NONSTOP_OVER_THRESHOLD) &&
(threshold_config != NONSTOP_OVER_THRESHOLD))
ras_core->is_rma = true;
ras_core_event_notify(ras_core, RAS_EVENT_ID__DEVICE_RMA, NULL);
}
mutex_unlock(&control->ras_tbl_mutex);
return 0;
}
int ras_fw_eeprom_read_idx(struct ras_core_context *ras_core,
struct eeprom_umc_record *record_umc,
struct ras_bank_ecc *ras_ecc,
u32 rec_idx, const u32 num)
{
struct ras_fw_eeprom_control *control = &ras_core->ras_fw_eeprom;
int i, ret, end_idx;
u64 mca, ipid, ts;
u32 cu, mem_channel, mcumc_id;
if (!ras_core->ras_umc.ip_func ||
!ras_core->ras_umc.ip_func->mca_ipid_parse)
return -EOPNOTSUPP;
mutex_lock(&control->ras_tbl_mutex);
end_idx = rec_idx + num;
for (i = rec_idx; i < end_idx; i++) {
ret = ras_fw_get_badpage_mca_addr(ras_core, i, &mca);
if (ret)
goto out;
ret = ras_fw_get_badpage_ipid(ras_core, i, &ipid);
if (ret)
goto out;
ret = ras_fw_get_timestamp(ras_core, i, &ts);
if (ret)
goto out;
if (record_umc) {
record_umc[i - rec_idx].address = mca;
record_umc[i - rec_idx].retired_row_pfn = 0x1ULL;
record_umc[i - rec_idx].ts = ts;
record_umc[i - rec_idx].err_type = RAS_EEPROM_ERR_NON_RECOVERABLE;
ras_core->ras_umc.ip_func->mca_ipid_parse(ras_core, ipid,
&cu, &mem_channel, &mcumc_id, NULL);
record_umc[i - rec_idx].cu = (u8)cu;
record_umc[i - rec_idx].mem_channel = (u8)mem_channel;
record_umc[i - rec_idx].mcumc_id = (u8)mcumc_id;
if ((record_umc[i - rec_idx].mem_channel < BITS_PER_TYPE(control->bad_channel_bitmap)) &&
!(control->bad_channel_bitmap & (1 << record_umc[i - rec_idx].mem_channel))) {
control->bad_channel_bitmap |= 1 << record_umc[i - rec_idx].mem_channel;
control->update_channel_flag = true;
}
}
if (ras_ecc) {
ras_ecc[i - rec_idx].addr = mca;
ras_ecc[i - rec_idx].ipid = ipid;
ras_ecc[i - rec_idx].ts = ts;
}
}
out:
mutex_unlock(&control->ras_tbl_mutex);
return ret;
}
uint32_t ras_fw_eeprom_get_record_count(struct ras_core_context *ras_core)
{
if (!ras_core)
return 0;
return ras_core->ras_fw_eeprom.ras_num_recs;
}
int ras_fw_eeprom_update_record(struct ras_core_context *ras_core,
struct ras_bank_ecc *ras_ecc)
{
struct ras_fw_eeprom_control *control = &ras_core->ras_fw_eeprom;
int ret, retry = 20;
u32 recs_num_new = control->ras_num_recs;
do {
ret = ras_fw_get_badpage_count(ras_core,
&recs_num_new, RAS_SMU_MESSAGE_TIMEOUT_MS);
if (!ret &&
(recs_num_new == control->ras_num_recs)) {
msleep(50);
retry--;
} else {
break;
}
} while (retry);
if (ret)
return ret;
if (recs_num_new > control->ras_num_recs)
ret = ras_fw_eeprom_read_idx(ras_core, 0,
ras_ecc, control->ras_num_recs, 1);
else
ret = -EINVAL;
return ret;
}
static int __check_ras_fw_table_status(struct ras_core_context *ras_core)
{
struct ras_fw_eeprom_control *control = &ras_core->ras_fw_eeprom;
uint64_t local_time;
int res;
mutex_init(&control->ras_tbl_mutex);
res = ras_fw_get_table_version(ras_core, &(control->version));
if (res)
return res;
res = ras_fw_get_badpage_count(ras_core, &(control->ras_num_recs), 100);
if (res)
return res;
local_time = (uint64_t)ktime_get_real_seconds();
res = ras_fw_set_timestamp(ras_core, local_time);
if (res)
return res;
control->ras_max_record_count = 4000;
if (control->ras_num_recs > control->ras_max_record_count) {
RAS_DEV_ERR(ras_core->dev,
"RAS header invalid, records in header: %u max allowed :%u",
control->ras_num_recs, control->ras_max_record_count);
return -EINVAL;
}
return 0;
}
int ras_fw_eeprom_hw_init(struct ras_core_context *ras_core)
{
struct ras_fw_eeprom_control *control;
struct ras_eeprom_config *eeprom_cfg;
struct ras_mp1 *mp1;
const struct ras_mp1_sys_func *sys_func;
if (!ras_core)
return -EINVAL;
mp1 = &ras_core->ras_mp1;
sys_func = mp1->sys_func;
if (!sys_func || !sys_func->mp1_send_eeprom_msg)
return -EINVAL;
ras_core->is_rma = false;
control = &ras_core->ras_fw_eeprom;
memset(control, 0, sizeof(*control));
eeprom_cfg = &ras_core->config->eeprom_cfg;
control->record_threshold_config =
eeprom_cfg->eeprom_record_threshold_config;
control->record_threshold_count = 4000;
if (eeprom_cfg->eeprom_record_threshold_count <
control->record_threshold_count)
control->record_threshold_count =
eeprom_cfg->eeprom_record_threshold_count;
control->update_channel_flag = false;
return __check_ras_fw_table_status(ras_core);
}
int ras_fw_eeprom_hw_fini(struct ras_core_context *ras_core)
{
struct ras_fw_eeprom_control *control;
if (!ras_core)
return -EINVAL;
control = &ras_core->ras_fw_eeprom;
mutex_destroy(&control->ras_tbl_mutex);
return 0;
}
int ras_fw_eeprom_check_storage_status(struct ras_core_context *ras_core)
{
struct ras_fw_eeprom_control *control = &ras_core->ras_fw_eeprom;
int bad_page_count;
bad_page_count = ras_umc_get_badpage_count(ras_core);
if ((control->record_threshold_count < bad_page_count) &&
(control->record_threshold_config != 0)) {
RAS_DEV_ERR(ras_core->dev, "RAS records:%d exceed threshold:%d",
bad_page_count, control->record_threshold_count);
if ((control->record_threshold_config == WARN_NONSTOP_OVER_THRESHOLD) ||
(control->record_threshold_config == NONSTOP_OVER_THRESHOLD)) {
RAS_DEV_WARN(ras_core->dev,
"Please consult AMD Service Action Guide (SAG) for appropriate service procedures\n");
} else {
ras_core->is_rma = true;
RAS_DEV_ERR(ras_core->dev,
"User defined threshold is set, runtime service will be halt when threshold is reached\n");
}
return 0;
}
RAS_DEV_INFO(ras_core->dev,
"Found existing EEPROM table with %d records\n",
bad_page_count);
if (10 * bad_page_count >= 9 * control->record_threshold_count)
RAS_DEV_WARN(ras_core->dev,
"RAS records:%u exceeds 90%% of threshold:%d\n",
bad_page_count,
control->record_threshold_count);
return 0;
}
enum ras_gpu_health_status
ras_fw_eeprom_check_gpu_status(struct ras_core_context *ras_core)
{
struct ras_fw_eeprom_control *control = &ras_core->ras_fw_eeprom;
if (!control->record_threshold_config)
return RAS_GPU_HEALTH_NONE;
if (ras_core->is_rma)
return RAS_GPU_RETIRED__ECC_REACH_THRESHOLD;
return RAS_GPU_HEALTH_USABLE;
}
void ras_fw_eeprom_sync_info(struct ras_core_context *ras_core)
{
struct ras_fw_eeprom_control *control;
if (!ras_core)
return;
control = &ras_core->ras_fw_eeprom;
ras_core_event_notify(ras_core, RAS_EVENT_ID__UPDATE_BAD_PAGE_NUM,
&control->ras_num_recs);
ras_core_event_notify(ras_core, RAS_EVENT_ID__UPDATE_BAD_CHANNEL_BITMAP,
&control->bad_channel_bitmap);
}