[PATCH 041/109] drm/amd/ras: add interfaces to manage multiple EEPROMs
Alex Deucher <[email protected]>
| Newsgroups | org.freedesktop.lists.amd-gfx |
|---|---|
| Message-ID | <[email protected]> |
From: YiPeng Chai <[email protected]> Add interfaces to manage multiple EEPROMs. Signed-off-by: YiPeng Chai <[email protected]> Reviewed-by: Hawking Zhang <[email protected]> Signed-off-by: Alex Deucher <[email protected]> --- drivers/gpu/drm/amd/ras/core/ras_eeprom_mgr.c | 413 ++++++++++++++++++ drivers/gpu/drm/amd/ras/core/ras_eeprom_mgr.h | 123 ++++++ 2 files changed, 536 insertions(+) create mode 100644 drivers/gpu/drm/amd/ras/core/ras_eeprom_mgr.c create mode 100644 drivers/gpu/drm/amd/ras/core/ras_eeprom_mgr.h diff --git a/drivers/gpu/drm/amd/ras/core/ras_eeprom_mgr.c b/drivers/gpu/drm/amd/ras/core/ras_eeprom_mgr.c new file mode 100644 index 0000000000000..d56e065ccfc74 --- /dev/null +++ b/drivers/gpu/drm/amd/ras/core/ras_eeprom_mgr.c @@ -0,0 +1,413 @@ +// SPDX-License-Identifier: MIT +/* + * Copyright 2026 Advanced Micro Devices, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + */ + +#include "ras_eeprom_mgr.h" +#include "ras.h" +#include "eeprom.h" + +static bool __ras_eeprom_disabled(struct ras_core_context *ras_core) +{ + return !ras_core->ras_eeprom_supported; +} + +static const struct ras_eeprom_ops *ras_eeprom_mgr_get_ip_func( + struct ras_core_context *ras_core, uint32_t ip_version) +{ + struct ras_eeprom_mgr *mgr = &ras_core->eeprom_mgr; + + switch (ip_version) { + case IP_VERSION(13, 0, 6): + case IP_VERSION(13, 0, 14): + case IP_VERSION(13, 0, 12): + mgr->eeprom_early_init_service_supported = false; + return &ras_drv_eeprom_ops; + default: + RAS_DEV_ERR(ras_core->dev, + "EEPROM(MP1) ip version(0x%x) is not supported!\n", ip_version); + break; + } + + return NULL; +} + +int ras_eeprom_mgr_sw_init(struct ras_core_context *ras_core) +{ + struct ras_eeprom_mgr *mgr = &ras_core->eeprom_mgr; + struct ras_eeprom_config *cfg; + struct ras_eeprom_param_config param_cfg = {0}; + struct ras_eeprom_param eeprom_param = {0}; + int ret = 0; + + if (__ras_eeprom_disabled(ras_core)) + return 0; + + cfg = &ras_core->config->eeprom_cfg; + if (!cfg || !cfg->eeprom_sys_fn || !cfg->eeprom_sys_fn->get_eeprom_config) { + RAS_DEV_ERR(ras_core->dev, "Ras eeprom not configured!\n"); + return -EINVAL; + } + + memset(mgr, 0, sizeof(*mgr)); + + mgr->sys_func = cfg->eeprom_sys_fn; + ret = mgr->sys_func->get_eeprom_config(ras_core, ¶m_cfg); + if (ret) { + RAS_DEV_ERR(ras_core->dev, "Failed to get ras eeprom param config!\n"); + return -EPERM; + } + + mgr->ras_err_threshold = param_cfg.eeprom_record_threshold_count; + mgr->work_mode_over_thresh = param_cfg.work_mode_over_thresh; + mgr->eeprom_ip_version = param_cfg.eeprom_ip_version; + mgr->eeprom_ops = ras_eeprom_mgr_get_ip_func(ras_core, mgr->eeprom_ip_version); + if (!mgr->eeprom_ops) + return -ENODEV; + + eeprom_param.sys_fn = mgr->sys_func; + eeprom_param.eeprom_ip_version = param_cfg.eeprom_ip_version; + eeprom_param.record_threshold = param_cfg.eeprom_record_threshold_count; + eeprom_param.max_i2c_read_len = param_cfg.max_i2c_read_len; + eeprom_param.max_i2c_write_len = param_cfg.max_i2c_write_len; + eeprom_param.eeprom_i2c_adapter = param_cfg.eeprom_i2c_adapter; + eeprom_param.eeprom_i2c_port = param_cfg.eeprom_i2c_port; + eeprom_param.eeprom_i2c_addr = param_cfg.eeprom_i2c_addr; + + if (mgr->eeprom_ops->sw_init) + ret = mgr->eeprom_ops->sw_init(ras_core, &eeprom_param); + + return ret; +} + +int ras_eeprom_mgr_sw_fini(struct ras_core_context *ras_core) +{ + struct ras_eeprom_mgr *mgr = &ras_core->eeprom_mgr; + int ret = 0; + + if (__ras_eeprom_disabled(ras_core)) + return 0; + + if (!mgr->eeprom_ops) + return -EINVAL; + + if (mgr->eeprom_ops->sw_fini) + ret = mgr->eeprom_ops->sw_fini(ras_core); + + return ret; +} + +int ras_eeprom_mgr_hw_init(struct ras_core_context *ras_core) +{ + struct ras_eeprom_mgr *mgr = &ras_core->eeprom_mgr; + struct ras_eeprom_param_config param_cfg = {0}; + struct ras_eeprom_param eeprom_param = {0}; + int ret = 0; + + if (__ras_eeprom_disabled(ras_core)) + return 0; + + if (!mgr->eeprom_ops) + return -EINVAL; + + ret = mgr->sys_func->get_eeprom_config(ras_core, ¶m_cfg); + if (ret) { + RAS_DEV_ERR(ras_core->dev, "Failed to get ras eeprom param config!\n"); + return -EPERM; + } + + eeprom_param.max_i2c_read_len = param_cfg.max_i2c_read_len; + eeprom_param.max_i2c_write_len = param_cfg.max_i2c_write_len; + eeprom_param.eeprom_i2c_adapter = param_cfg.eeprom_i2c_adapter; + eeprom_param.eeprom_i2c_port = param_cfg.eeprom_i2c_port; + eeprom_param.eeprom_i2c_addr = param_cfg.eeprom_i2c_addr; + + if (mgr->eeprom_ops->hw_init) + ret = mgr->eeprom_ops->hw_init(ras_core, &eeprom_param); + + return ret; +} + +int ras_eeprom_mgr_hw_fini(struct ras_core_context *ras_core) +{ + struct ras_eeprom_mgr *mgr = &ras_core->eeprom_mgr; + int ret = 0; + + if (__ras_eeprom_disabled(ras_core)) + return 0; + + if (!mgr->eeprom_ops) + return -EINVAL; + + if (mgr->eeprom_ops->hw_fini) + ret = mgr->eeprom_ops->hw_fini(ras_core); + + return ret; +} + +int ras_eeprom_mgr_reset_table(struct ras_core_context *ras_core) +{ + struct ras_eeprom_mgr *mgr = &ras_core->eeprom_mgr; + int ret = -EOPNOTSUPP; + + if (__ras_eeprom_disabled(ras_core)) + return 0; + + if (!mgr->eeprom_ops) + return -EINVAL; + + if (mgr->eeprom_ops->reset_table) + ret = mgr->eeprom_ops->reset_table(ras_core); + + return ret; +} + +int ras_eeprom_mgr_get_record_count(struct ras_core_context *ras_core) +{ + struct ras_eeprom_mgr *mgr = &ras_core->eeprom_mgr; + int ret = 0; + + if (__ras_eeprom_disabled(ras_core)) + return 0; + + if (!mgr->eeprom_ops) + return -EINVAL; + + if (mgr->eeprom_ops && mgr->eeprom_ops->get_record_count) + ret = mgr->eeprom_ops->get_record_count(ras_core); + + return ret; +} + +int ras_eeprom_mgr_get_records(struct ras_core_context *ras_core, u32 start, + struct eeprom_umc_record *record, u32 num) +{ + struct ras_eeprom_mgr *mgr = &ras_core->eeprom_mgr; + int ret = -EOPNOTSUPP; + + if (__ras_eeprom_disabled(ras_core)) + return -RAS_CORE_NOT_SUPPORTED; + + if (!mgr->eeprom_ops) + return -EINVAL; + + if (mgr->eeprom_ops->get_records) + ret = mgr->eeprom_ops->get_records(ras_core, + start, record, num); + + return ret; +} + +int ras_eeprom_mgr_append_records(struct ras_core_context *ras_core, + struct eeprom_umc_record *record, const u32 num) +{ + struct ras_eeprom_mgr *mgr = &ras_core->eeprom_mgr; + int ret = -EOPNOTSUPP; + + if (__ras_eeprom_disabled(ras_core)) + return -RAS_CORE_NOT_SUPPORTED; + + if (!mgr->eeprom_ops) + return -EINVAL; + + if (mgr->eeprom_ops->append_records) + ret = mgr->eeprom_ops->append_records(ras_core, + record, num); + + return ret; +} + +int ras_eeprom_mgr_get_eeprom_info(struct ras_core_context *ras_core, + struct ras_eeprom_info *eeprom_info) +{ + struct ras_eeprom_mgr *mgr = &ras_core->eeprom_mgr; + + if (__ras_eeprom_disabled(ras_core)) + return -EOPNOTSUPP; + + if (!mgr->eeprom_ops || !mgr->eeprom_ops->get_eeprom_info) + return -EOPNOTSUPP; + + return mgr->eeprom_ops->get_eeprom_info(ras_core, + eeprom_info, ras_core_gpu_in_reset(ras_core)); +} + +int ras_eeprom_mgr_check_and_report_status(struct ras_core_context *ras_core, bool chk_hw) +{ + struct ras_eeprom_mgr *mgr = &ras_core->eeprom_mgr; + struct ras_eeprom_info info = {0}; + int valid_err_count; + int ret = 0; + + if (__ras_eeprom_disabled(ras_core)) + return 0; + + if (!mgr->eeprom_ops || !mgr->eeprom_ops->get_eeprom_info) + return -EOPNOTSUPP; + + ret = mgr->eeprom_ops->get_eeprom_info(ras_core, &info, !chk_hw); + if (ret) + return ret; + + valid_err_count = ras_umc_get_saved_eeprom_count(ras_core); + if (info.eeprom_status == RAS_EEPROM_OK) { + RAS_DEV_DBG(ras_core->dev, + "Found existing EEPROM table with %d records\n", + valid_err_count); + + /* Warn if error count reaches or exceeds 90% of the threshold */ + if (10 * valid_err_count >= 9 * mgr->ras_err_threshold) + RAS_DEV_WARN(ras_core->dev, + "RAS records:%u exceeds 90%% of threshold:%d\n", + valid_err_count, mgr->ras_err_threshold); + + } else if (info.eeprom_status == RAS_EEPROM_LOCKED) { + if (info.rma_status || (valid_err_count >= mgr->ras_err_threshold)) { + if (info.rma_status) + RAS_DEV_ERR(ras_core->dev, "RAS records:%d exceed error threshold", + valid_err_count); + else + RAS_DEV_ERR(ras_core->dev, "RAS records:%d exceed threshold:%d", + valid_err_count, mgr->ras_err_threshold); + + if (mgr->work_mode_over_thresh == RAS_WORK_MODE_OVER_THRESH_STRICT) { + RAS_DEV_WARN(ras_core->dev, + "Please consult AMD Service Action Guide (SAG) for appropriate service procedures\n"); + } else if (mgr->work_mode_over_thresh == RAS_WORK_MODE_OVER_THRESH_RMA) { + 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"); + } + } else if ((valid_err_count < mgr->ras_err_threshold) && + mgr->eeprom_ops->unlock_eeprom) { + /* This means that, the threshold was increased since + * the last time the system was booted, and now, + * the saved error count is less than threshold, + * so that at least one more record can be saved, + * before the page count threshold is reached. + */ + ret = mgr->eeprom_ops->unlock_eeprom(ras_core); + if (ret) { + RAS_DEV_ERR(ras_core->dev, "Failed to resize RAS table\n"); + } else { + ras_core->is_rma = false; + RAS_DEV_INFO(ras_core->dev, + "records:%d threshold:%d, resetting RAS table header signature", + valid_err_count, mgr->ras_err_threshold); + } + } else { + RAS_DEV_ERR(ras_core->dev, + "records:%d threshold:%d, not support to resize RAS table\n", + valid_err_count, mgr->ras_err_threshold); + } + } else { + RAS_DEV_ERR(ras_core->dev, "Error ras eeprom status:0x%x\n", info.eeprom_status); + } + + return ret; +} + +enum ras_gpu_op_status + ras_eeprom_mgr_get_gpu_op_status(struct ras_core_context *ras_core) +{ + struct ras_eeprom_mgr *mgr = &ras_core->eeprom_mgr; + struct ras_eeprom_info info = {0}; + int ret; + + if (__ras_eeprom_disabled(ras_core)) + return RAS_GPU_OP_STATUS_OK; + + if (!mgr->eeprom_ops || !mgr->eeprom_ops->get_eeprom_info) + return RAS_GPU_OP_STATUS_UNKNOWN; + + ret = mgr->eeprom_ops->get_eeprom_info(ras_core, + &info, ras_core_gpu_in_reset(ras_core)); + if (ret) + return RAS_GPU_OP_STATUS_UNKNOWN; + + if (info.rma_status || ras_core->is_rma) + return RAS_GPU_OP_STATUS_RMA; + + if (info.eeprom_status == RAS_EEPROM_FAULT) + return RAS_GPU_OP_STATUS_FAULT; + else if (info.eeprom_status == RAS_EEPROM_LOCKED) + return RAS_GPU_OP_STATUS_LOCKED; + else if (info.eeprom_status == RAS_EEPROM_UNKNOWN) + return RAS_GPU_OP_STATUS_UNKNOWN; + + return RAS_GPU_OP_STATUS_OK; +} + +bool ras_eeprom_mgr_check_safety_watermark(struct ras_core_context *ras_core) +{ + enum ras_gpu_op_status status; + + if (__ras_eeprom_disabled(ras_core)) + return true; + + ras_eeprom_mgr_check_and_report_status(ras_core, false); + + status = ras_eeprom_mgr_get_gpu_op_status(ras_core); + + return (status == RAS_GPU_OP_STATUS_OK); +} + +int ras_eeprom_mgr_get_version(struct ras_core_context *ras_core, u32 *version) +{ + struct ras_eeprom_mgr *mgr = &ras_core->eeprom_mgr; + struct ras_eeprom_info info = {0}; + int ret = 0; + + if (__ras_eeprom_disabled(ras_core)) + return 0; + + if (!mgr->eeprom_ops || !mgr->eeprom_ops->get_eeprom_info) + return -EOPNOTSUPP; + + ret = mgr->eeprom_ops->get_eeprom_info(ras_core, &info, true); + if (ret) + return ret; + + *version = info.record_format_version; + + return 0; +} + +bool ras_eeprom_mgr_early_init_service_supported(struct ras_core_context *ras_core) +{ + struct ras_eeprom_mgr *mgr = &ras_core->eeprom_mgr; + + if (__ras_eeprom_disabled(ras_core)) + return false; + + return mgr->eeprom_early_init_service_supported; +} + +bool ras_eeprom_mgr_fw_record_enabled(struct ras_core_context *ras_core) +{ + struct ras_eeprom_mgr *mgr = &ras_core->eeprom_mgr; + + if (__ras_eeprom_disabled(ras_core)) + return false; + + return mgr->fw_record_enabled; +} diff --git a/drivers/gpu/drm/amd/ras/core/ras_eeprom_mgr.h b/drivers/gpu/drm/amd/ras/core/ras_eeprom_mgr.h new file mode 100644 index 0000000000000..5f249ede0d40b --- /dev/null +++ b/drivers/gpu/drm/amd/ras/core/ras_eeprom_mgr.h @@ -0,0 +1,123 @@ +/* SPDX-License-Identifier: MIT */ +/* + * Copyright 2026 Advanced Micro Devices, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + */ + +#ifndef __RAS_EEPROM_MGR_H__ +#define __RAS_EEPROM_MGR_H__ + +enum ras_gpu_op_status { + RAS_GPU_OP_STATUS_UNKNOWN = 0, + RAS_GPU_OP_STATUS_OK = 1, + RAS_GPU_OP_STATUS_LOCKED = 2, + RAS_GPU_OP_STATUS_FAULT = 3, + RAS_GPU_OP_STATUS_RMA = 4, +}; + +enum ras_eeprom_status { + RAS_EEPROM_UNKNOWN, + /*Read and write */ + RAS_EEPROM_OK, + /* Only read */ + RAS_EEPROM_LOCKED, + /* Invalid data */ + RAS_EEPROM_FAULT, +}; + +struct ras_eeprom_param { + u32 eeprom_ip_version; + uint32_t record_threshold; + void *eeprom_i2c_adapter; + u32 eeprom_i2c_addr; + u32 eeprom_i2c_port; + u16 max_i2c_read_len; + u16 max_i2c_write_len; + const struct ras_eeprom_sys_func *sys_fn; +}; + +struct ras_eeprom_info { + u32 record_format_version; + u32 eeprom_status; + u32 rma_status; + /* Raw record count in eeprom, but data may be duplicated, + * therefore not a valid record count + */ + u32 record_count; + u32 max_record_count; + u64 bad_channel_bitmap; +}; + +struct ras_core_context; +struct eeprom_umc_record; + +struct ras_eeprom_ops { + int (*sw_init)(struct ras_core_context *ras_core, struct ras_eeprom_param *param); + int (*sw_fini)(struct ras_core_context *ras_core); + int (*hw_init)(struct ras_core_context *ras_core, struct ras_eeprom_param *param); + int (*hw_fini)(struct ras_core_context *ras_core); + int (*reset_table)(struct ras_core_context *ras_core); + int (*get_records)(struct ras_core_context *ras_core, u32 start, + struct eeprom_umc_record *records, u32 num); + int (*append_records)(struct ras_core_context *ras_core, + struct eeprom_umc_record *records, u32 num); + u32 (*get_record_count)(struct ras_core_context *ras_core); + int (*get_eeprom_info)(struct ras_core_context *ras_core, + struct ras_eeprom_info *eeprom_info, bool fast_mode); + int (*unlock_eeprom)(struct ras_core_context *ras_core); +}; + +struct ras_eeprom_mgr { + u32 eeprom_ip_version; + const struct ras_eeprom_sys_func *sys_func; + void *ras_eeprom; + const struct ras_eeprom_ops *eeprom_ops; + u32 ras_err_threshold; + u32 work_mode_over_thresh; + bool eeprom_early_init_service_supported; + bool fw_record_enabled; +}; + +struct ras_core_context; +int ras_eeprom_mgr_sw_init(struct ras_core_context *ras_core); +int ras_eeprom_mgr_sw_fini(struct ras_core_context *ras_core); +int ras_eeprom_mgr_hw_init(struct ras_core_context *ras_core); +int ras_eeprom_mgr_hw_fini(struct ras_core_context *ras_core); +int ras_eeprom_mgr_reset_table(struct ras_core_context *ras_core); +int ras_eeprom_mgr_get_record_count(struct ras_core_context *ras_core); +int ras_eeprom_mgr_get_records(struct ras_core_context *ras_core, u32 start, + struct eeprom_umc_record *record, const u32 num); +int ras_eeprom_mgr_append_records(struct ras_core_context *ras_core, + struct eeprom_umc_record *record, const u32 num); +int ras_eeprom_mgr_check_and_report_status(struct ras_core_context *ras_core, + bool check_hw); +enum ras_gpu_op_status + ras_eeprom_mgr_get_gpu_op_status(struct ras_core_context *ras_core); +int ras_eeprom_mgr_get_version(struct ras_core_context *ras_core, u32 *version); +bool ras_eeprom_mgr_check_safety_watermark(struct ras_core_context *ras_core); +int ras_eeprom_mgr_get_eeprom_info(struct ras_core_context *ras_core, + struct ras_eeprom_info *eeprom_info); +int ras_core_enable_early_init_service(struct ras_core_context *ras_core, + bool enabled); +bool ras_eeprom_mgr_early_init_service_supported(struct ras_core_context *ras_core); +bool ras_eeprom_mgr_fw_record_enabled(struct ras_core_context *ras_core); +int ras_eeprom_mgr_dump_fw_records(struct ras_core_context *ras_core); +#endif -- 2.55.0