| // SPDX-License-Identifier: MIT | |
| /* | |
| * Copyright 2025 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.h" | |
| #include "aca.h" | |
| #include "aca_v1_0.h" | |
| #include "ras_mp1_v13_0.h" | |
| #define ACA_MARK_FATAL_FLAG 0x100 | |
| #define ACA_MARK_UE_READ_FLAG 0x1 | |
| #define blk_name(block_id) ras_core_get_ras_block_name(block_id) | |
| static struct aca_regs_dump { | |
| const char *name; | |
| int reg_idx; | |
| } aca_regs[] = { | |
| {"CONTROL", ACA_REG_IDX__CTL}, | |
| {"STATUS", ACA_REG_IDX__STATUS}, | |
| {"ADDR", ACA_REG_IDX__ADDR}, | |
| {"MISC", ACA_REG_IDX__MISC0}, | |
| {"CONFIG", ACA_REG_IDX__CONFG}, | |
| {"IPID", ACA_REG_IDX__IPID}, | |
| {"SYND", ACA_REG_IDX__SYND}, | |
| {"DESTAT", ACA_REG_IDX__DESTAT}, | |
| {"DEADDR", ACA_REG_IDX__DEADDR}, | |
| {"CONTROL_MASK", ACA_REG_IDX__CTL_MASK}, | |
| }; | |
| static void aca_report_ecc_info(struct ras_core_context *ras_core, | |
| u64 seq_no, u32 blk, u32 skt, u32 aid, | |
| struct aca_aid_ecc *aid_ecc, | |
| struct aca_bank_ecc *new_ecc) | |
| { | |
| struct aca_ecc_count ecc_count = {0}; | |
| ecc_count.new_ue_count = new_ecc->ue_count; | |
| ecc_count.new_de_count = new_ecc->de_count; | |
| ecc_count.new_ce_count = new_ecc->ce_count; | |
| if (blk == RAS_BLOCK_ID__GFX) { | |
| struct aca_ecc_count *xcd_ecc; | |
| int xcd_id; | |
| for (xcd_id = 0; xcd_id < aid_ecc->xcd.xcd_num; xcd_id++) { | |
| xcd_ecc = &aid_ecc->xcd.xcd[xcd_id].ecc_err; | |
| ecc_count.total_ue_count += xcd_ecc->total_ue_count; | |
| ecc_count.total_de_count += xcd_ecc->total_de_count; | |
| ecc_count.total_ce_count += xcd_ecc->total_ce_count; | |
| } | |
| } else { | |
| ecc_count.total_ue_count = aid_ecc->ecc_err.total_ue_count; | |
| ecc_count.total_de_count = aid_ecc->ecc_err.total_de_count; | |
| ecc_count.total_ce_count = aid_ecc->ecc_err.total_ce_count; | |
| } | |
| if (ecc_count.new_ue_count) { | |
| RAS_DEV_INFO(ras_core->dev, | |
| "{%llu} socket: %d, die: %d, %u new uncorrectable hardware errors detected in %s block\n", | |
| seq_no, skt, aid, ecc_count.new_ue_count, blk_name(blk)); | |
| RAS_DEV_INFO(ras_core->dev, | |
| "{%llu} socket: %d, die: %d, %u uncorrectable hardware errors detected in total in %s block\n", | |
| seq_no, skt, aid, ecc_count.total_ue_count, blk_name(blk)); | |
| } | |
| if (ecc_count.new_de_count && blk == RAS_BLOCK_ID__UMC) { | |
| RAS_DEV_INFO(ras_core->dev, | |
| "{%llu} socket: %d, die: %d, %u new deferred hardware errors detected in %s block\n", | |
| seq_no, skt, aid, ecc_count.new_de_count, | |
| blk_name(blk)); | |
| RAS_DEV_INFO(ras_core->dev, | |
| "{%llu} socket: %d, die: %d, %u deferred hardware errors detected in total in %s block\n", | |
| seq_no, skt, aid, ecc_count.total_de_count, | |
| blk_name(blk)); | |
| } | |
| if (ecc_count.new_ce_count) { | |
| if (ras_core_is_ce_log_disabled(ras_core)) | |
| return; | |
| RAS_DEV_INFO(ras_core->dev, | |
| "{%llu} socket: %d, die: %d, %u new correctable hardware errors detected in %s block\n", | |
| seq_no, skt, aid, ecc_count.new_ce_count, blk_name(blk)); | |
| RAS_DEV_INFO(ras_core->dev, | |
| "{%llu} socket: %d, die: %d, %u correctable hardware errors detected in total in %s block\n", | |
| seq_no, skt, aid, ecc_count.total_ce_count, blk_name(blk)); | |
| } | |
| } | |
| static void aca_bank_log(struct ras_core_context *ras_core, | |
| int idx, int total, struct aca_bank_reg *bank, | |
| struct aca_bank_ecc *bank_ecc) | |
| { | |
| int i; | |
| if (ras_core_is_ce_log_disabled(ras_core) && | |
| bank->ecc_type == RAS_ERR_TYPE__CE && | |
| !bank_ecc->de_count) | |
| return; | |
| RAS_DEV_INFO(ras_core->dev, | |
| "{%llu}" RAS_HW_ERR "Accelerator Check Architecture events logged\n", | |
| bank->seq_no); | |
| /* plus 1 for output format, e.g: ACA[08/08]: xxxx */ | |
| for (i = 0; i < ARRAY_SIZE(aca_regs); i++) | |
| RAS_DEV_INFO(ras_core->dev, | |
| "{%llu}" RAS_HW_ERR "ACA[%02d/%02d].%s=0x%016llx\n", | |
| bank->seq_no, idx + 1, total, | |
| aca_regs[i].name, bank->regs[aca_regs[i].reg_idx]); | |
| } | |
| static void aca_log_bank_data(struct ras_core_context *ras_core, | |
| struct aca_bank_reg *bank, struct aca_bank_ecc *bank_ecc, | |
| struct ras_log_batch_tag *batch) | |
| { | |
| if (bank_ecc->ue_count) | |
| ras_log_ring_add_log_event(ras_core, RAS_LOG_EVENT_UE, bank->regs, batch); | |
| else if (bank_ecc->de_count) | |
| ras_log_ring_add_log_event(ras_core, RAS_LOG_EVENT_DE, bank->regs, batch); | |
| else | |
| ras_log_ring_add_log_event(ras_core, RAS_LOG_EVENT_CE, bank->regs, batch); | |
| } | |
| static int aca_get_bank_count(struct ras_core_context *ras_core, | |
| enum ras_err_type type, u32 *count) | |
| { | |
| return ras_mp1_get_bank_count(ras_core, type, count); | |
| } | |
| static bool aca_match_bank(struct aca_block *aca_blk, struct aca_bank_reg *bank) | |
| { | |
| const struct aca_bank_hw_ops *bank_ops; | |
| if (!aca_blk->blk_info) | |
| return false; | |
| bank_ops = &aca_blk->blk_info->bank_ops; | |
| if (!bank_ops->bank_match) | |
| return false; | |
| return bank_ops->bank_match(aca_blk, bank); | |
| } | |
| static int aca_parse_bank(struct ras_core_context *ras_core, | |
| struct aca_block *aca_blk, | |
| struct aca_bank_reg *bank, | |
| struct aca_bank_ecc *ecc) | |
| { | |
| const struct aca_bank_hw_ops *bank_ops = &aca_blk->blk_info->bank_ops; | |
| if (!bank_ops || !bank_ops->bank_parse) | |
| return -RAS_CORE_NOT_SUPPORTED; | |
| return bank_ops->bank_parse(ras_core, aca_blk, bank, ecc); | |
| } | |
| static int aca_check_block_ecc_info(struct ras_core_context *ras_core, | |
| struct aca_block *aca_blk, struct aca_ecc_info *info) | |
| { | |
| if (info->socket_id >= aca_blk->ecc.socket_num_per_hive) { | |
| RAS_DEV_ERR(ras_core->dev, | |
| "Socket id (%d) is out of config! max:%u\n", | |
| info->socket_id, aca_blk->ecc.socket_num_per_hive); | |
| return -ENODATA; | |
| } | |
| if (info->die_id >= aca_blk->ecc.socket[info->socket_id].aid_num) { | |
| RAS_DEV_ERR(ras_core->dev, | |
| "Die id (%d) is out of config! max:%u\n", | |
| info->die_id, aca_blk->ecc.socket[info->socket_id].aid_num); | |
| return -ENODATA; | |
| } | |
| if ((aca_blk->blk_info->ras_block_id == RAS_BLOCK_ID__GFX) && | |
| (info->xcd_id >= | |
| aca_blk->ecc.socket[info->socket_id].aid[info->die_id].xcd.xcd_num)) { | |
| RAS_DEV_ERR(ras_core->dev, | |
| "Xcd id (%d) is out of config! max:%u\n", | |
| info->xcd_id, | |
| aca_blk->ecc.socket[info->socket_id].aid[info->die_id].xcd.xcd_num); | |
| return -ENODATA; | |
| } | |
| return 0; | |
| } | |
| static int aca_log_bad_bank(struct ras_core_context *ras_core, | |
| struct aca_block *aca_blk, struct aca_bank_reg *bank, | |
| struct aca_bank_ecc *bank_ecc) | |
| { | |
| struct aca_ecc_info *info; | |
| struct aca_ecc_count *ecc_err; | |
| struct aca_aid_ecc *aid_ecc; | |
| int ret; | |
| info = &bank_ecc->bank_info; | |
| ret = aca_check_block_ecc_info(ras_core, aca_blk, info); | |
| if (ret) | |
| return ret; | |
| mutex_lock(&ras_core->ras_aca.aca_lock); | |
| aid_ecc = &aca_blk->ecc.socket[info->socket_id].aid[info->die_id]; | |
| if (aca_blk->blk_info->ras_block_id == RAS_BLOCK_ID__GFX) | |
| ecc_err = &aid_ecc->xcd.xcd[info->xcd_id].ecc_err; | |
| else | |
| ecc_err = &aid_ecc->ecc_err; | |
| ecc_err->new_ce_count += bank_ecc->ce_count; | |
| ecc_err->total_ce_count += bank_ecc->ce_count; | |
| ecc_err->new_ue_count += bank_ecc->ue_count; | |
| ecc_err->total_ue_count += bank_ecc->ue_count; | |
| ecc_err->new_de_count += bank_ecc->de_count; | |
| ecc_err->total_de_count += bank_ecc->de_count; | |
| mutex_unlock(&ras_core->ras_aca.aca_lock); | |
| if ((aca_blk->blk_info->ras_block_id == RAS_BLOCK_ID__UMC) && | |
| bank_ecc->de_count) { | |
| struct ras_bank_ecc ras_ecc = {0}; | |
| if (ras_fw_eeprom_supported(ras_core)) { | |
| ret = ras_fw_eeprom_update_record(ras_core, &ras_ecc); | |
| if (!ret) { | |
| ras_ecc.nps = ras_core_get_curr_nps_mode(ras_core); | |
| ras_ecc.status = bank_ecc->bank_info.status; | |
| ras_ecc.seq_no = bank->seq_no; | |
| } | |
| } else { | |
| ras_ecc.nps = ras_core_get_curr_nps_mode(ras_core); | |
| ras_ecc.addr = bank_ecc->bank_info.addr; | |
| ras_ecc.ipid = bank_ecc->bank_info.ipid; | |
| ras_ecc.status = bank_ecc->bank_info.status; | |
| ras_ecc.seq_no = bank->seq_no; | |
| } | |
| if (!ret) { | |
| if (ras_core_gpu_in_reset(ras_core)) | |
| ras_umc_log_bad_bank_pending(ras_core, &ras_ecc); | |
| else | |
| ras_umc_log_bad_bank(ras_core, &ras_ecc); | |
| } | |
| } | |
| aca_report_ecc_info(ras_core, | |
| bank->seq_no, aca_blk->blk_info->ras_block_id, info->socket_id, info->die_id, | |
| &aca_blk->ecc.socket[info->socket_id].aid[info->die_id], bank_ecc); | |
| return 0; | |
| } | |
| static struct aca_block *aca_get_bank_aca_block(struct ras_core_context *ras_core, | |
| struct aca_bank_reg *bank) | |
| { | |
| int i = 0; | |
| for (i = 0; i < RAS_BLOCK_ID__LAST; i++) | |
| if (aca_match_bank(&ras_core->ras_aca.aca_blk[i], bank)) | |
| return &ras_core->ras_aca.aca_blk[i]; | |
| return NULL; | |
| } | |
| static int aca_dump_bank(struct ras_core_context *ras_core, u32 ecc_type, | |
| int idx, void *data) | |
| { | |
| struct aca_bank_reg *bank = (struct aca_bank_reg *)data; | |
| int i, ret, reg_cnt; | |
| reg_cnt = min_t(int, 16, ARRAY_SIZE(bank->regs)); | |
| for (i = 0; i < reg_cnt; i++) { | |
| ret = ras_mp1_dump_bank(ras_core, ecc_type, idx, i, &bank->regs[i]); | |
| if (ret) | |
| return ret; | |
| } | |
| return 0; | |
| } | |
| static uint64_t aca_get_bank_seqno(struct ras_core_context *ras_core, | |
| enum ras_err_type err_type, struct aca_block *aca_blk, | |
| struct aca_bank_ecc *bank_ecc) | |
| { | |
| uint64_t seq_no = 0; | |
| if (bank_ecc->de_count) { | |
| if (aca_blk->blk_info->ras_block_id == RAS_BLOCK_ID__UMC) | |
| seq_no = ras_core_get_seqno(ras_core, RAS_SEQNO_TYPE_DE, true); | |
| else | |
| seq_no = ras_core_get_seqno(ras_core, | |
| RAS_SEQNO_TYPE_POISON_CONSUMPTION, true); | |
| } else if (bank_ecc->ue_count) { | |
| seq_no = ras_core_get_seqno(ras_core, RAS_SEQNO_TYPE_UE, true); | |
| } else { | |
| seq_no = ras_core_get_seqno(ras_core, RAS_SEQNO_TYPE_CE, true); | |
| } | |
| return seq_no; | |
| } | |
| static bool aca_dup_update_ue_in_fatal(struct ras_core_context *ras_core, | |
| u32 ecc_type) | |
| { | |
| struct ras_aca *aca = &ras_core->ras_aca; | |
| if (ecc_type != RAS_ERR_TYPE__UE) | |
| return false; | |
| if (aca->ue_updated_mark & ACA_MARK_FATAL_FLAG) { | |
| if (aca->ue_updated_mark & ACA_MARK_UE_READ_FLAG) | |
| return true; | |
| aca->ue_updated_mark |= ACA_MARK_UE_READ_FLAG; | |
| } | |
| return false; | |
| } | |
| void ras_aca_mark_fatal_flag(struct ras_core_context *ras_core) | |
| { | |
| struct ras_aca *aca = &ras_core->ras_aca; | |
| if (!aca) | |
| return; | |
| aca->ue_updated_mark |= ACA_MARK_FATAL_FLAG; | |
| } | |
| void ras_aca_clear_fatal_flag(struct ras_core_context *ras_core) | |
| { | |
| struct ras_aca *aca = &ras_core->ras_aca; | |
| if (!aca) | |
| return; | |
| if ((aca->ue_updated_mark & ACA_MARK_FATAL_FLAG) && | |
| (aca->ue_updated_mark & ACA_MARK_UE_READ_FLAG)) | |
| aca->ue_updated_mark = 0; | |
| } | |
| static int aca_banks_update(struct ras_core_context *ras_core, | |
| u32 ecc_type, void *data) | |
| { | |
| struct aca_bank_reg bank; | |
| struct aca_block *aca_blk; | |
| struct aca_bank_ecc bank_ecc; | |
| struct ras_log_batch_tag *batch_tag = NULL; | |
| u32 count = 0; | |
| int ret = 0; | |
| int i; | |
| mutex_lock(&ras_core->ras_aca.bank_op_lock); | |
| if (aca_dup_update_ue_in_fatal(ras_core, ecc_type)) | |
| goto out; | |
| ret = aca_get_bank_count(ras_core, ecc_type, &count); | |
| if (ret) | |
| goto out; | |
| if (!count) | |
| goto out; | |
| batch_tag = ras_log_ring_create_batch_tag(ras_core); | |
| for (i = 0; i < count; i++) { | |
| memset(&bank, 0, sizeof(bank)); | |
| ret = aca_dump_bank(ras_core, ecc_type, i, &bank); | |
| if (ret) | |
| break; | |
| bank.ecc_type = ecc_type; | |
| memset(&bank_ecc, 0, sizeof(bank_ecc)); | |
| aca_blk = aca_get_bank_aca_block(ras_core, &bank); | |
| if (aca_blk) | |
| ret = aca_parse_bank(ras_core, aca_blk, &bank, &bank_ecc); | |
| bank.seq_no = aca_get_bank_seqno(ras_core, ecc_type, aca_blk, &bank_ecc); | |
| aca_log_bank_data(ras_core, &bank, &bank_ecc, batch_tag); | |
| aca_bank_log(ras_core, i, count, &bank, &bank_ecc); | |
| if (!ret && aca_blk) | |
| ret = aca_log_bad_bank(ras_core, aca_blk, &bank, &bank_ecc); | |
| if (ret) | |
| break; | |
| } | |
| ras_log_ring_destroy_batch_tag(ras_core, batch_tag); | |
| out: | |
| mutex_unlock(&ras_core->ras_aca.bank_op_lock); | |
| return ret; | |
| } | |
| int ras_aca_update_ecc(struct ras_core_context *ras_core, u32 type, void *data) | |
| { | |
| /* Update aca bank to aca source error_cache first */ | |
| return aca_banks_update(ras_core, type, data); | |
| } | |
| static struct aca_block *ras_aca_get_block_handle(struct ras_core_context *ras_core, uint32_t blk) | |
| { | |
| return &ras_core->ras_aca.aca_blk[blk]; | |
| } | |
| static int ras_aca_clear_block_ecc_count(struct ras_core_context *ras_core, u32 blk) | |
| { | |
| struct aca_block *aca_blk; | |
| struct aca_aid_ecc *aid_ecc; | |
| int skt, aid, xcd; | |
| mutex_lock(&ras_core->ras_aca.aca_lock); | |
| aca_blk = ras_aca_get_block_handle(ras_core, blk); | |
| for (skt = 0; skt < aca_blk->ecc.socket_num_per_hive; skt++) { | |
| for (aid = 0; aid < aca_blk->ecc.socket[skt].aid_num; aid++) { | |
| aid_ecc = &aca_blk->ecc.socket[skt].aid[aid]; | |
| if (blk == RAS_BLOCK_ID__GFX) { | |
| for (xcd = 0; xcd < aid_ecc->xcd.xcd_num; xcd++) | |
| memset(&aid_ecc->xcd.xcd[xcd], | |
| 0, sizeof(struct aca_xcd_ecc)); | |
| } else { | |
| memset(&aid_ecc->ecc_err, 0, sizeof(aid_ecc->ecc_err)); | |
| } | |
| } | |
| } | |
| mutex_unlock(&ras_core->ras_aca.aca_lock); | |
| return 0; | |
| } | |
| int ras_aca_clear_all_blocks_ecc_count(struct ras_core_context *ras_core) | |
| { | |
| enum ras_block_id blk; | |
| int ret; | |
| for (blk = RAS_BLOCK_ID__UMC; blk < RAS_BLOCK_ID__LAST; blk++) { | |
| ret = ras_aca_clear_block_ecc_count(ras_core, blk); | |
| if (ret) | |
| break; | |
| } | |
| return ret; | |
| } | |
| int ras_aca_clear_block_new_ecc_count(struct ras_core_context *ras_core, u32 blk) | |
| { | |
| struct aca_block *aca_blk; | |
| int skt, aid, xcd; | |
| struct aca_ecc_count *ecc_err; | |
| struct aca_aid_ecc *aid_ecc; | |
| mutex_lock(&ras_core->ras_aca.aca_lock); | |
| aca_blk = ras_aca_get_block_handle(ras_core, blk); | |
| for (skt = 0; skt < aca_blk->ecc.socket_num_per_hive; skt++) { | |
| for (aid = 0; aid < aca_blk->ecc.socket[skt].aid_num; aid++) { | |
| aid_ecc = &aca_blk->ecc.socket[skt].aid[aid]; | |
| if (blk == RAS_BLOCK_ID__GFX) { | |
| for (xcd = 0; xcd < aid_ecc->xcd.xcd_num; xcd++) { | |
| ecc_err = &aid_ecc->xcd.xcd[xcd].ecc_err; | |
| ecc_err->new_ce_count = 0; | |
| ecc_err->new_ue_count = 0; | |
| ecc_err->new_de_count = 0; | |
| } | |
| } else { | |
| ecc_err = &aid_ecc->ecc_err; | |
| ecc_err->new_ce_count = 0; | |
| ecc_err->new_ue_count = 0; | |
| ecc_err->new_de_count = 0; | |
| } | |
| } | |
| } | |
| mutex_unlock(&ras_core->ras_aca.aca_lock); | |
| return 0; | |
| } | |
| static int ras_aca_get_block_each_aid_ecc_count(struct ras_core_context *ras_core, | |
| u32 blk, u32 skt, u32 aid, u32 xcd, | |
| struct aca_ecc_count *ecc_count) | |
| { | |
| struct aca_block *aca_blk; | |
| struct aca_ecc_count *ecc_err; | |
| aca_blk = ras_aca_get_block_handle(ras_core, blk); | |
| if (blk == RAS_BLOCK_ID__GFX) | |
| ecc_err = &aca_blk->ecc.socket[skt].aid[aid].xcd.xcd[xcd].ecc_err; | |
| else | |
| ecc_err = &aca_blk->ecc.socket[skt].aid[aid].ecc_err; | |
| ecc_count->new_ce_count = ecc_err->new_ce_count; | |
| ecc_count->total_ce_count = ecc_err->total_ce_count; | |
| ecc_count->new_ue_count = ecc_err->new_ue_count; | |
| ecc_count->total_ue_count = ecc_err->total_ue_count; | |
| ecc_count->new_de_count = ecc_err->new_de_count; | |
| ecc_count->total_de_count = ecc_err->total_de_count; | |
| return 0; | |
| } | |
| static inline void _add_ecc_count(struct aca_ecc_count *des, struct aca_ecc_count *src) | |
| { | |
| des->new_ce_count += src->new_ce_count; | |
| des->total_ce_count += src->total_ce_count; | |
| des->new_ue_count += src->new_ue_count; | |
| des->total_ue_count += src->total_ue_count; | |
| des->new_de_count += src->new_de_count; | |
| des->total_de_count += src->total_de_count; | |
| } | |
| static const struct ras_aca_ip_func *aca_get_ip_func( | |
| struct ras_core_context *ras_core, uint32_t ip_version) | |
| { | |
| switch (ip_version) { | |
| case IP_VERSION(1, 0, 0): | |
| return &ras_aca_func_v1_0; | |
| default: | |
| RAS_DEV_ERR(ras_core->dev, | |
| "ACA ip version(0x%x) is not supported!\n", ip_version); | |
| break; | |
| } | |
| return NULL; | |
| } | |
| int ras_aca_get_block_ecc_count(struct ras_core_context *ras_core, | |
| u32 blk, void *data) | |
| { | |
| struct ras_ecc_count *err_data = (struct ras_ecc_count *)data; | |
| struct aca_block *aca_blk; | |
| int skt, aid, xcd; | |
| struct aca_ecc_count ecc_xcd; | |
| struct aca_ecc_count ecc_aid; | |
| struct aca_ecc_count ecc; | |
| if (blk >= RAS_BLOCK_ID__LAST) | |
| return -EINVAL; | |
| if (!err_data) | |
| return -EINVAL; | |
| aca_blk = ras_aca_get_block_handle(ras_core, blk); | |
| memset(&ecc, 0, sizeof(ecc)); | |
| mutex_lock(&ras_core->ras_aca.aca_lock); | |
| if (blk == RAS_BLOCK_ID__GFX) { | |
| for (skt = 0; skt < aca_blk->ecc.socket_num_per_hive; skt++) { | |
| for (aid = 0; aid < aca_blk->ecc.socket[skt].aid_num; aid++) { | |
| memset(&ecc_aid, 0, sizeof(ecc_aid)); | |
| for (xcd = 0; | |
| xcd < aca_blk->ecc.socket[skt].aid[aid].xcd.xcd_num; | |
| xcd++) { | |
| memset(&ecc_xcd, 0, sizeof(ecc_xcd)); | |
| if (ras_aca_get_block_each_aid_ecc_count(ras_core, | |
| blk, skt, aid, xcd, &ecc_xcd)) | |
| continue; | |
| _add_ecc_count(&ecc_aid, &ecc_xcd); | |
| } | |
| _add_ecc_count(&ecc, &ecc_aid); | |
| } | |
| } | |
| } else { | |
| for (skt = 0; skt < aca_blk->ecc.socket_num_per_hive; skt++) { | |
| for (aid = 0; aid < aca_blk->ecc.socket[skt].aid_num; aid++) { | |
| memset(&ecc_aid, 0, sizeof(ecc_aid)); | |
| if (ras_aca_get_block_each_aid_ecc_count(ras_core, | |
| blk, skt, aid, 0, &ecc_aid)) | |
| continue; | |
| _add_ecc_count(&ecc, &ecc_aid); | |
| } | |
| } | |
| } | |
| err_data->new_ce_count = ecc.new_ce_count; | |
| err_data->total_ce_count = ecc.total_ce_count; | |
| err_data->new_ue_count = ecc.new_ue_count; | |
| err_data->total_ue_count = ecc.total_ue_count; | |
| err_data->new_de_count = ecc.new_de_count; | |
| err_data->total_de_count = ecc.total_de_count; | |
| mutex_unlock(&ras_core->ras_aca.aca_lock); | |
| return 0; | |
| } | |
| int ras_aca_sw_init(struct ras_core_context *ras_core) | |
| { | |
| struct ras_aca *ras_aca = &ras_core->ras_aca; | |
| struct ras_aca_config *aca_cfg = &ras_core->config->aca_cfg; | |
| struct aca_block *aca_blk; | |
| uint32_t socket_num_per_hive; | |
| uint32_t aid_num_per_socket; | |
| uint32_t xcd_num_per_aid; | |
| int blk, skt, aid; | |
| socket_num_per_hive = aca_cfg->socket_num_per_hive; | |
| aid_num_per_socket = aca_cfg->aid_num_per_socket; | |
| xcd_num_per_aid = aca_cfg->xcd_num_per_aid; | |
| if (!xcd_num_per_aid || !aid_num_per_socket || | |
| (socket_num_per_hive > MAX_SOCKET_NUM_PER_HIVE) || | |
| (aid_num_per_socket > MAX_AID_NUM_PER_SOCKET) || | |
| (xcd_num_per_aid > MAX_XCD_NUM_PER_AID)) { | |
| RAS_DEV_ERR(ras_core->dev, "Invalid ACA system configuration: %d, %d, %d\n", | |
| socket_num_per_hive, aid_num_per_socket, xcd_num_per_aid); | |
| return -EINVAL; | |
| } | |
| memset(ras_aca, 0, sizeof(*ras_aca)); | |
| for (blk = 0; blk < RAS_BLOCK_ID__LAST; blk++) { | |
| aca_blk = &ras_aca->aca_blk[blk]; | |
| aca_blk->ecc.socket_num_per_hive = socket_num_per_hive; | |
| for (skt = 0; skt < aca_blk->ecc.socket_num_per_hive; skt++) { | |
| aca_blk->ecc.socket[skt].aid_num = aid_num_per_socket; | |
| if (blk == RAS_BLOCK_ID__GFX) { | |
| for (aid = 0; aid < aca_blk->ecc.socket[skt].aid_num; aid++) | |
| aca_blk->ecc.socket[skt].aid[aid].xcd.xcd_num = | |
| xcd_num_per_aid; | |
| } | |
| } | |
| } | |
| mutex_init(&ras_aca->aca_lock); | |
| mutex_init(&ras_aca->bank_op_lock); | |
| return 0; | |
| } | |
| int ras_aca_sw_fini(struct ras_core_context *ras_core) | |
| { | |
| struct ras_aca *ras_aca = &ras_core->ras_aca; | |
| mutex_destroy(&ras_aca->aca_lock); | |
| mutex_destroy(&ras_aca->bank_op_lock); | |
| return 0; | |
| } | |
| int ras_aca_hw_init(struct ras_core_context *ras_core) | |
| { | |
| struct ras_aca *ras_aca = &ras_core->ras_aca; | |
| struct aca_block *aca_blk; | |
| const struct ras_aca_ip_func *ip_func; | |
| int i; | |
| ras_aca->aca_ip_version = ras_core->config->aca_ip_version; | |
| ip_func = aca_get_ip_func(ras_core, ras_aca->aca_ip_version); | |
| if (!ip_func) | |
| return -EINVAL; | |
| for (i = 0; i < ip_func->block_num; i++) { | |
| aca_blk = &ras_aca->aca_blk[ip_func->block_info[i]->ras_block_id]; | |
| aca_blk->blk_info = ip_func->block_info[i]; | |
| } | |
| ras_aca->ue_updated_mark = 0; | |
| return 0; | |
| } | |
| int ras_aca_hw_fini(struct ras_core_context *ras_core) | |
| { | |
| struct ras_aca *ras_aca = &ras_core->ras_aca; | |
| ras_aca->ue_updated_mark = 0; | |
| return 0; | |
| } |