| // SPDX-License-Identifier: GPL-2.0 | |
| /* | |
| * Copyright 2020 HabanaLabs, Ltd. | |
| * All Rights Reserved. | |
| */ | |
| #include "habanalabs.h" | |
| static const char * const hl_glbl_error_cause[] = { | |
| "Error due to un-priv read", | |
| "Error due to un-secure read", | |
| "Error due to read from unmapped reg", | |
| "Error due to un-priv write", | |
| "Error due to un-secure write", | |
| "Error due to write to unmapped reg", | |
| "N/A", | |
| "N/A", | |
| "N/A", | |
| "N/A", | |
| "N/A", | |
| "N/A", | |
| "N/A", | |
| "N/A", | |
| "N/A", | |
| "N/A", | |
| "External I/F write sec violation", | |
| "External I/F write to un-mapped reg", | |
| "N/A", | |
| "N/A", | |
| "N/A", | |
| "N/A", | |
| "N/A", | |
| "N/A", | |
| "Read to write only", | |
| "Write to read only" | |
| }; | |
| /** | |
| * hl_get_pb_block - return the relevant block within the block array | |
| * | |
| * @hdev: pointer to hl_device structure | |
| * @mm_reg_addr: register address in the desired block | |
| * @pb_blocks: blocks array | |
| * @array_size: blocks array size | |
| * | |
| */ | |
| static int hl_get_pb_block(struct hl_device *hdev, u32 mm_reg_addr, | |
| const u32 pb_blocks[], int array_size) | |
| { | |
| int i; | |
| u32 start_addr, end_addr; | |
| for (i = 0 ; i < array_size ; i++) { | |
| start_addr = pb_blocks[i]; | |
| end_addr = start_addr + HL_BLOCK_SIZE; | |
| if ((mm_reg_addr >= start_addr) && (mm_reg_addr < end_addr)) | |
| return i; | |
| } | |
| dev_err(hdev->dev, "No protection domain was found for 0x%x\n", | |
| mm_reg_addr); | |
| return -EDOM; | |
| } | |
| /** | |
| * hl_unset_pb_in_block - clear a specific protection bit in a block | |
| * | |
| * @hdev: pointer to hl_device structure | |
| * @reg_offset: register offset will be converted to bit offset in pb block | |
| * @sgs_entry: pb array | |
| * | |
| */ | |
| static int hl_unset_pb_in_block(struct hl_device *hdev, u32 reg_offset, | |
| struct hl_block_glbl_sec *sgs_entry) | |
| { | |
| if ((reg_offset >= HL_BLOCK_SIZE) || (reg_offset & 0x3)) { | |
| dev_err(hdev->dev, | |
| "Register offset(%d) is out of range(%d) or invalid\n", | |
| reg_offset, HL_BLOCK_SIZE); | |
| return -EINVAL; | |
| } | |
| UNSET_GLBL_SEC_BIT(sgs_entry->sec_array, | |
| (reg_offset & (HL_BLOCK_SIZE - 1)) >> 2); | |
| return 0; | |
| } | |
| /** | |
| * hl_unsecure_register - locate the relevant block for this register and | |
| * remove corresponding protection bit | |
| * | |
| * @hdev: pointer to hl_device structure | |
| * @mm_reg_addr: register address to unsecure | |
| * @offset: additional offset to the register address | |
| * @pb_blocks: blocks array | |
| * @sgs_array: pb array | |
| * @array_size: blocks array size | |
| * | |
| */ | |
| int hl_unsecure_register(struct hl_device *hdev, u32 mm_reg_addr, int offset, | |
| const u32 pb_blocks[], struct hl_block_glbl_sec sgs_array[], | |
| int array_size) | |
| { | |
| u32 reg_offset; | |
| int block_num; | |
| block_num = hl_get_pb_block(hdev, mm_reg_addr + offset, pb_blocks, | |
| array_size); | |
| if (block_num < 0) | |
| return block_num; | |
| reg_offset = (mm_reg_addr + offset) - pb_blocks[block_num]; | |
| return hl_unset_pb_in_block(hdev, reg_offset, &sgs_array[block_num]); | |
| } | |
| /** | |
| * hl_unsecure_register_range - locate the relevant block for this register | |
| * range and remove corresponding protection bit | |
| * | |
| * @hdev: pointer to hl_device structure | |
| * @mm_reg_range: register address range to unsecure | |
| * @offset: additional offset to the register address | |
| * @pb_blocks: blocks array | |
| * @sgs_array: pb array | |
| * @array_size: blocks array size | |
| * | |
| */ | |
| static int hl_unsecure_register_range(struct hl_device *hdev, | |
| struct range mm_reg_range, int offset, const u32 pb_blocks[], | |
| struct hl_block_glbl_sec sgs_array[], | |
| int array_size) | |
| { | |
| u32 reg_offset; | |
| int i, block_num, rc = 0; | |
| block_num = hl_get_pb_block(hdev, | |
| mm_reg_range.start + offset, pb_blocks, | |
| array_size); | |
| if (block_num < 0) | |
| return block_num; | |
| for (i = mm_reg_range.start ; i <= mm_reg_range.end ; i += 4) { | |
| reg_offset = (i + offset) - pb_blocks[block_num]; | |
| rc |= hl_unset_pb_in_block(hdev, reg_offset, | |
| &sgs_array[block_num]); | |
| } | |
| return rc; | |
| } | |
| /** | |
| * hl_unsecure_registers - locate the relevant block for all registers and | |
| * remove corresponding protection bit | |
| * | |
| * @hdev: pointer to hl_device structure | |
| * @mm_reg_array: register address array to unsecure | |
| * @mm_array_size: register array size | |
| * @offset: additional offset to the register address | |
| * @pb_blocks: blocks array | |
| * @sgs_array: pb array | |
| * @blocks_array_size: blocks array size | |
| * | |
| */ | |
| int hl_unsecure_registers(struct hl_device *hdev, const u32 mm_reg_array[], | |
| int mm_array_size, int offset, const u32 pb_blocks[], | |
| struct hl_block_glbl_sec sgs_array[], int blocks_array_size) | |
| { | |
| int i, rc = 0; | |
| for (i = 0 ; i < mm_array_size ; i++) { | |
| rc = hl_unsecure_register(hdev, mm_reg_array[i], offset, | |
| pb_blocks, sgs_array, blocks_array_size); | |
| if (rc) | |
| return rc; | |
| } | |
| return rc; | |
| } | |
| /** | |
| * hl_unsecure_registers_range - locate the relevant block for all register | |
| * ranges and remove corresponding protection bit | |
| * | |
| * @hdev: pointer to hl_device structure | |
| * @mm_reg_range_array: register address range array to unsecure | |
| * @mm_array_size: register array size | |
| * @offset: additional offset to the register address | |
| * @pb_blocks: blocks array | |
| * @sgs_array: pb array | |
| * @blocks_array_size: blocks array size | |
| * | |
| */ | |
| static int hl_unsecure_registers_range(struct hl_device *hdev, | |
| const struct range mm_reg_range_array[], int mm_array_size, | |
| int offset, const u32 pb_blocks[], | |
| struct hl_block_glbl_sec sgs_array[], int blocks_array_size) | |
| { | |
| int i, rc = 0; | |
| for (i = 0 ; i < mm_array_size ; i++) { | |
| rc = hl_unsecure_register_range(hdev, mm_reg_range_array[i], | |
| offset, pb_blocks, sgs_array, blocks_array_size); | |
| if (rc) | |
| return rc; | |
| } | |
| return rc; | |
| } | |
| /** | |
| * hl_ack_pb_security_violations - Ack security violation | |
| * | |
| * @hdev: pointer to hl_device structure | |
| * @pb_blocks: blocks array | |
| * @block_offset: additional offset to the block | |
| * @array_size: blocks array size | |
| * | |
| */ | |
| static void hl_ack_pb_security_violations(struct hl_device *hdev, | |
| const u32 pb_blocks[], u32 block_offset, int array_size) | |
| { | |
| int i; | |
| u32 cause, addr, block_base; | |
| for (i = 0 ; i < array_size ; i++) { | |
| block_base = pb_blocks[i] + block_offset; | |
| cause = RREG32(block_base + HL_BLOCK_GLBL_ERR_CAUSE); | |
| if (cause) { | |
| addr = RREG32(block_base + HL_BLOCK_GLBL_ERR_ADDR); | |
| hdev->asic_funcs->pb_print_security_errors(hdev, | |
| block_base, cause, addr); | |
| WREG32(block_base + HL_BLOCK_GLBL_ERR_CAUSE, cause); | |
| } | |
| } | |
| } | |
| /** | |
| * hl_config_glbl_sec - set pb in HW according to given pb array | |
| * | |
| * @hdev: pointer to hl_device structure | |
| * @pb_blocks: blocks array | |
| * @sgs_array: pb array | |
| * @block_offset: additional offset to the block | |
| * @array_size: blocks array size | |
| * | |
| */ | |
| void hl_config_glbl_sec(struct hl_device *hdev, const u32 pb_blocks[], | |
| struct hl_block_glbl_sec sgs_array[], u32 block_offset, | |
| int array_size) | |
| { | |
| int i, j; | |
| u32 sgs_base; | |
| if (hdev->pldm) | |
| usleep_range(100, 1000); | |
| for (i = 0 ; i < array_size ; i++) { | |
| sgs_base = block_offset + pb_blocks[i] + | |
| HL_BLOCK_GLBL_SEC_OFFS; | |
| for (j = 0 ; j < HL_BLOCK_GLBL_SEC_LEN ; j++) | |
| WREG32(sgs_base + j * sizeof(u32), | |
| sgs_array[i].sec_array[j]); | |
| } | |
| } | |
| /** | |
| * hl_secure_block - locally memsets a block to 0 | |
| * | |
| * @hdev: pointer to hl_device structure | |
| * @sgs_array: pb array to clear | |
| * @array_size: blocks array size | |
| * | |
| */ | |
| void hl_secure_block(struct hl_device *hdev, | |
| struct hl_block_glbl_sec sgs_array[], int array_size) | |
| { | |
| int i; | |
| for (i = 0 ; i < array_size ; i++) | |
| memset((char *)(sgs_array[i].sec_array), 0, | |
| HL_BLOCK_GLBL_SEC_SIZE); | |
| } | |
| /** | |
| * hl_init_pb_with_mask - set selected pb instances with mask in HW according | |
| * to given configuration | |
| * | |
| * @hdev: pointer to hl_device structure | |
| * @num_dcores: number of decores to apply configuration to | |
| * set to HL_PB_SHARED if need to apply only once | |
| * @dcore_offset: offset between dcores | |
| * @num_instances: number of instances to apply configuration to | |
| * @instance_offset: offset between instances | |
| * @pb_blocks: blocks array | |
| * @blocks_array_size: blocks array size | |
| * @user_regs_array: unsecured register array | |
| * @user_regs_array_size: unsecured register array size | |
| * @mask: enabled instances mask: 1- enabled, 0- disabled | |
| */ | |
| int hl_init_pb_with_mask(struct hl_device *hdev, u32 num_dcores, | |
| u32 dcore_offset, u32 num_instances, u32 instance_offset, | |
| const u32 pb_blocks[], u32 blocks_array_size, | |
| const u32 *user_regs_array, u32 user_regs_array_size, u64 mask) | |
| { | |
| int i, j; | |
| struct hl_block_glbl_sec *glbl_sec; | |
| glbl_sec = kzalloc_objs(struct hl_block_glbl_sec, blocks_array_size); | |
| if (!glbl_sec) | |
| return -ENOMEM; | |
| hl_secure_block(hdev, glbl_sec, blocks_array_size); | |
| hl_unsecure_registers(hdev, user_regs_array, user_regs_array_size, 0, | |
| pb_blocks, glbl_sec, blocks_array_size); | |
| /* Fill all blocks with the same configuration */ | |
| for (i = 0 ; i < num_dcores ; i++) { | |
| for (j = 0 ; j < num_instances ; j++) { | |
| int seq = i * num_instances + j; | |
| if (!(mask & BIT_ULL(seq))) | |
| continue; | |
| hl_config_glbl_sec(hdev, pb_blocks, glbl_sec, | |
| i * dcore_offset + j * instance_offset, | |
| blocks_array_size); | |
| } | |
| } | |
| kfree(glbl_sec); | |
| return 0; | |
| } | |
| /** | |
| * hl_init_pb - set pb in HW according to given configuration | |
| * | |
| * @hdev: pointer to hl_device structure | |
| * @num_dcores: number of decores to apply configuration to | |
| * set to HL_PB_SHARED if need to apply only once | |
| * @dcore_offset: offset between dcores | |
| * @num_instances: number of instances to apply configuration to | |
| * @instance_offset: offset between instances | |
| * @pb_blocks: blocks array | |
| * @blocks_array_size: blocks array size | |
| * @user_regs_array: unsecured register array | |
| * @user_regs_array_size: unsecured register array size | |
| * | |
| */ | |
| int hl_init_pb(struct hl_device *hdev, u32 num_dcores, u32 dcore_offset, | |
| u32 num_instances, u32 instance_offset, | |
| const u32 pb_blocks[], u32 blocks_array_size, | |
| const u32 *user_regs_array, u32 user_regs_array_size) | |
| { | |
| return hl_init_pb_with_mask(hdev, num_dcores, dcore_offset, | |
| num_instances, instance_offset, pb_blocks, | |
| blocks_array_size, user_regs_array, | |
| user_regs_array_size, ULLONG_MAX); | |
| } | |
| /** | |
| * hl_init_pb_ranges_with_mask - set pb instances using mask in HW according to | |
| * given configuration unsecurring registers | |
| * ranges instead of specific registers | |
| * | |
| * @hdev: pointer to hl_device structure | |
| * @num_dcores: number of decores to apply configuration to | |
| * set to HL_PB_SHARED if need to apply only once | |
| * @dcore_offset: offset between dcores | |
| * @num_instances: number of instances to apply configuration to | |
| * @instance_offset: offset between instances | |
| * @pb_blocks: blocks array | |
| * @blocks_array_size: blocks array size | |
| * @user_regs_range_array: unsecured register range array | |
| * @user_regs_range_array_size: unsecured register range array size | |
| * @mask: enabled instances mask: 1- enabled, 0- disabled | |
| */ | |
| int hl_init_pb_ranges_with_mask(struct hl_device *hdev, u32 num_dcores, | |
| u32 dcore_offset, u32 num_instances, u32 instance_offset, | |
| const u32 pb_blocks[], u32 blocks_array_size, | |
| const struct range *user_regs_range_array, | |
| u32 user_regs_range_array_size, u64 mask) | |
| { | |
| int i, j, rc = 0; | |
| struct hl_block_glbl_sec *glbl_sec; | |
| glbl_sec = kzalloc_objs(struct hl_block_glbl_sec, blocks_array_size); | |
| if (!glbl_sec) | |
| return -ENOMEM; | |
| hl_secure_block(hdev, glbl_sec, blocks_array_size); | |
| rc = hl_unsecure_registers_range(hdev, user_regs_range_array, | |
| user_regs_range_array_size, 0, pb_blocks, glbl_sec, | |
| blocks_array_size); | |
| if (rc) | |
| goto free_glbl_sec; | |
| /* Fill all blocks with the same configuration */ | |
| for (i = 0 ; i < num_dcores ; i++) { | |
| for (j = 0 ; j < num_instances ; j++) { | |
| int seq = i * num_instances + j; | |
| if (!(mask & BIT_ULL(seq))) | |
| continue; | |
| hl_config_glbl_sec(hdev, pb_blocks, glbl_sec, | |
| i * dcore_offset + j * instance_offset, | |
| blocks_array_size); | |
| } | |
| } | |
| free_glbl_sec: | |
| kfree(glbl_sec); | |
| return rc; | |
| } | |
| /** | |
| * hl_init_pb_ranges - set pb in HW according to given configuration unsecurring | |
| * registers ranges instead of specific registers | |
| * | |
| * @hdev: pointer to hl_device structure | |
| * @num_dcores: number of decores to apply configuration to | |
| * set to HL_PB_SHARED if need to apply only once | |
| * @dcore_offset: offset between dcores | |
| * @num_instances: number of instances to apply configuration to | |
| * @instance_offset: offset between instances | |
| * @pb_blocks: blocks array | |
| * @blocks_array_size: blocks array size | |
| * @user_regs_range_array: unsecured register range array | |
| * @user_regs_range_array_size: unsecured register range array size | |
| * | |
| */ | |
| int hl_init_pb_ranges(struct hl_device *hdev, u32 num_dcores, | |
| u32 dcore_offset, u32 num_instances, u32 instance_offset, | |
| const u32 pb_blocks[], u32 blocks_array_size, | |
| const struct range *user_regs_range_array, | |
| u32 user_regs_range_array_size) | |
| { | |
| return hl_init_pb_ranges_with_mask(hdev, num_dcores, dcore_offset, | |
| num_instances, instance_offset, pb_blocks, | |
| blocks_array_size, user_regs_range_array, | |
| user_regs_range_array_size, ULLONG_MAX); | |
| } | |
| /** | |
| * hl_init_pb_single_dcore - set pb for a single docre in HW | |
| * according to given configuration | |
| * | |
| * @hdev: pointer to hl_device structure | |
| * @dcore_offset: offset from the dcore0 | |
| * @num_instances: number of instances to apply configuration to | |
| * @instance_offset: offset between instances | |
| * @pb_blocks: blocks array | |
| * @blocks_array_size: blocks array size | |
| * @user_regs_array: unsecured register array | |
| * @user_regs_array_size: unsecured register array size | |
| * | |
| */ | |
| int hl_init_pb_single_dcore(struct hl_device *hdev, u32 dcore_offset, | |
| u32 num_instances, u32 instance_offset, | |
| const u32 pb_blocks[], u32 blocks_array_size, | |
| const u32 *user_regs_array, u32 user_regs_array_size) | |
| { | |
| int i, rc = 0; | |
| struct hl_block_glbl_sec *glbl_sec; | |
| glbl_sec = kzalloc_objs(struct hl_block_glbl_sec, blocks_array_size); | |
| if (!glbl_sec) | |
| return -ENOMEM; | |
| hl_secure_block(hdev, glbl_sec, blocks_array_size); | |
| rc = hl_unsecure_registers(hdev, user_regs_array, user_regs_array_size, | |
| 0, pb_blocks, glbl_sec, blocks_array_size); | |
| if (rc) | |
| goto free_glbl_sec; | |
| /* Fill all blocks with the same configuration */ | |
| for (i = 0 ; i < num_instances ; i++) | |
| hl_config_glbl_sec(hdev, pb_blocks, glbl_sec, | |
| dcore_offset + i * instance_offset, | |
| blocks_array_size); | |
| free_glbl_sec: | |
| kfree(glbl_sec); | |
| return rc; | |
| } | |
| /** | |
| * hl_init_pb_ranges_single_dcore - set pb for a single docre in HW according | |
| * to given configuration unsecurring | |
| * registers ranges instead of specific | |
| * registers | |
| * | |
| * @hdev: pointer to hl_device structure | |
| * @dcore_offset: offset from the dcore0 | |
| * @num_instances: number of instances to apply configuration to | |
| * @instance_offset: offset between instances | |
| * @pb_blocks: blocks array | |
| * @blocks_array_size: blocks array size | |
| * @user_regs_range_array: unsecured register range array | |
| * @user_regs_range_array_size: unsecured register range array size | |
| * | |
| */ | |
| int hl_init_pb_ranges_single_dcore(struct hl_device *hdev, u32 dcore_offset, | |
| u32 num_instances, u32 instance_offset, | |
| const u32 pb_blocks[], u32 blocks_array_size, | |
| const struct range *user_regs_range_array, u32 user_regs_range_array_size) | |
| { | |
| int i; | |
| struct hl_block_glbl_sec *glbl_sec; | |
| glbl_sec = kzalloc_objs(struct hl_block_glbl_sec, blocks_array_size); | |
| if (!glbl_sec) | |
| return -ENOMEM; | |
| hl_secure_block(hdev, glbl_sec, blocks_array_size); | |
| hl_unsecure_registers_range(hdev, user_regs_range_array, | |
| user_regs_range_array_size, 0, pb_blocks, glbl_sec, | |
| blocks_array_size); | |
| /* Fill all blocks with the same configuration */ | |
| for (i = 0 ; i < num_instances ; i++) | |
| hl_config_glbl_sec(hdev, pb_blocks, glbl_sec, | |
| dcore_offset + i * instance_offset, | |
| blocks_array_size); | |
| kfree(glbl_sec); | |
| return 0; | |
| } | |
| /** | |
| * hl_ack_pb_with_mask - ack pb with mask in HW according to given configuration | |
| * | |
| * @hdev: pointer to hl_device structure | |
| * @num_dcores: number of decores to apply configuration to | |
| * set to HL_PB_SHARED if need to apply only once | |
| * @dcore_offset: offset between dcores | |
| * @num_instances: number of instances to apply configuration to | |
| * @instance_offset: offset between instances | |
| * @pb_blocks: blocks array | |
| * @blocks_array_size: blocks array size | |
| * @mask: enabled instances mask: 1- enabled, 0- disabled | |
| * | |
| */ | |
| void hl_ack_pb_with_mask(struct hl_device *hdev, u32 num_dcores, | |
| u32 dcore_offset, u32 num_instances, u32 instance_offset, | |
| const u32 pb_blocks[], u32 blocks_array_size, u64 mask) | |
| { | |
| int i, j; | |
| /* ack all blocks */ | |
| for (i = 0 ; i < num_dcores ; i++) { | |
| for (j = 0 ; j < num_instances ; j++) { | |
| int seq = i * num_instances + j; | |
| if (!(mask & BIT_ULL(seq))) | |
| continue; | |
| hl_ack_pb_security_violations(hdev, pb_blocks, | |
| i * dcore_offset + j * instance_offset, | |
| blocks_array_size); | |
| } | |
| } | |
| } | |
| /** | |
| * hl_ack_pb - ack pb in HW according to given configuration | |
| * | |
| * @hdev: pointer to hl_device structure | |
| * @num_dcores: number of decores to apply configuration to | |
| * set to HL_PB_SHARED if need to apply only once | |
| * @dcore_offset: offset between dcores | |
| * @num_instances: number of instances to apply configuration to | |
| * @instance_offset: offset between instances | |
| * @pb_blocks: blocks array | |
| * @blocks_array_size: blocks array size | |
| * | |
| */ | |
| void hl_ack_pb(struct hl_device *hdev, u32 num_dcores, u32 dcore_offset, | |
| u32 num_instances, u32 instance_offset, | |
| const u32 pb_blocks[], u32 blocks_array_size) | |
| { | |
| hl_ack_pb_with_mask(hdev, num_dcores, dcore_offset, num_instances, | |
| instance_offset, pb_blocks, blocks_array_size, | |
| ULLONG_MAX); | |
| } | |
| /** | |
| * hl_ack_pb_single_dcore - ack pb for single docre in HW | |
| * according to given configuration | |
| * | |
| * @hdev: pointer to hl_device structure | |
| * @dcore_offset: offset from dcore0 | |
| * @num_instances: number of instances to apply configuration to | |
| * @instance_offset: offset between instances | |
| * @pb_blocks: blocks array | |
| * @blocks_array_size: blocks array size | |
| * | |
| */ | |
| void hl_ack_pb_single_dcore(struct hl_device *hdev, u32 dcore_offset, | |
| u32 num_instances, u32 instance_offset, | |
| const u32 pb_blocks[], u32 blocks_array_size) | |
| { | |
| int i; | |
| /* ack all blocks */ | |
| for (i = 0 ; i < num_instances ; i++) | |
| hl_ack_pb_security_violations(hdev, pb_blocks, | |
| dcore_offset + i * instance_offset, | |
| blocks_array_size); | |
| } | |
| static u32 hl_automated_get_block_base_addr(struct hl_device *hdev, | |
| struct hl_special_block_info *block_info, | |
| u32 major, u32 minor, u32 sub_minor) | |
| { | |
| u32 fw_block_base_address = block_info->base_addr + | |
| major * block_info->major_offset + | |
| minor * block_info->minor_offset + | |
| sub_minor * block_info->sub_minor_offset; | |
| struct asic_fixed_properties *prop = &hdev->asic_prop; | |
| /* Calculation above returns an address for FW use, and therefore should | |
| * be casted for driver use. | |
| */ | |
| return (fw_block_base_address - lower_32_bits(prop->cfg_base_address)); | |
| } | |
| static bool hl_check_block_type_exclusion(struct hl_skip_blocks_cfg *skip_blocks_cfg, | |
| int block_type) | |
| { | |
| int i; | |
| /* Check if block type is listed in the exclusion list of block types */ | |
| for (i = 0 ; i < skip_blocks_cfg->block_types_len ; i++) | |
| if (block_type == skip_blocks_cfg->block_types[i]) | |
| return true; | |
| return false; | |
| } | |
| static bool hl_check_block_range_exclusion(struct hl_device *hdev, | |
| struct hl_skip_blocks_cfg *skip_blocks_cfg, | |
| struct hl_special_block_info *block_info, | |
| u32 major, u32 minor, u32 sub_minor) | |
| { | |
| u32 blocks_in_range, block_base_addr_in_range, block_base_addr; | |
| int i, j; | |
| block_base_addr = hl_automated_get_block_base_addr(hdev, block_info, | |
| major, minor, sub_minor); | |
| for (i = 0 ; i < skip_blocks_cfg->block_ranges_len ; i++) { | |
| blocks_in_range = (skip_blocks_cfg->block_ranges[i].end - | |
| skip_blocks_cfg->block_ranges[i].start) / | |
| HL_BLOCK_SIZE + 1; | |
| for (j = 0 ; j < blocks_in_range ; j++) { | |
| block_base_addr_in_range = skip_blocks_cfg->block_ranges[i].start + | |
| j * HL_BLOCK_SIZE; | |
| if (block_base_addr == block_base_addr_in_range) | |
| return true; | |
| } | |
| } | |
| return false; | |
| } | |
| static int hl_read_glbl_errors(struct hl_device *hdev, | |
| u32 blk_idx, u32 major, u32 minor, u32 sub_minor, void *data) | |
| { | |
| struct asic_fixed_properties *prop = &hdev->asic_prop; | |
| struct hl_special_block_info *special_blocks = prop->special_blocks; | |
| struct hl_special_block_info *current_block = &special_blocks[blk_idx]; | |
| u32 glbl_err_addr, glbl_err_cause, addr_val, cause_val, block_base, | |
| base = current_block->base_addr - lower_32_bits(prop->cfg_base_address); | |
| int i; | |
| block_base = base + major * current_block->major_offset + | |
| minor * current_block->minor_offset + | |
| sub_minor * current_block->sub_minor_offset; | |
| glbl_err_cause = block_base + HL_GLBL_ERR_CAUSE_OFFSET; | |
| cause_val = RREG32(glbl_err_cause); | |
| if (!cause_val) | |
| return 0; | |
| glbl_err_addr = block_base + HL_GLBL_ERR_ADDR_OFFSET; | |
| addr_val = RREG32(glbl_err_addr); | |
| for (i = 0 ; i <= prop->glbl_err_max_cause_num ; i++) { | |
| if (cause_val & BIT(i)) | |
| dev_err_ratelimited(hdev->dev, | |
| "%s, addr %#llx\n", | |
| hl_glbl_error_cause[i], | |
| prop->cfg_base_address + block_base + | |
| FIELD_GET(HL_GLBL_ERR_ADDRESS_MASK, addr_val)); | |
| } | |
| WREG32(glbl_err_cause, cause_val); | |
| return 0; | |
| } | |
| void hl_check_for_glbl_errors(struct hl_device *hdev) | |
| { | |
| struct asic_fixed_properties *prop = &hdev->asic_prop; | |
| struct hl_special_blocks_cfg special_blocks_cfg; | |
| struct iterate_special_ctx glbl_err_iter; | |
| int rc; | |
| memset(&special_blocks_cfg, 0, sizeof(special_blocks_cfg)); | |
| special_blocks_cfg.skip_blocks_cfg = &prop->skip_special_blocks_cfg; | |
| glbl_err_iter.fn = &hl_read_glbl_errors; | |
| glbl_err_iter.data = &special_blocks_cfg; | |
| rc = hl_iterate_special_blocks(hdev, &glbl_err_iter); | |
| if (rc) | |
| dev_err_ratelimited(hdev->dev, | |
| "Could not iterate special blocks, glbl error check failed\n"); | |
| } | |
| int hl_iterate_special_blocks(struct hl_device *hdev, struct iterate_special_ctx *ctx) | |
| { | |
| struct hl_special_blocks_cfg *special_blocks_cfg = | |
| (struct hl_special_blocks_cfg *)ctx->data; | |
| struct hl_skip_blocks_cfg *skip_blocks_cfg = | |
| special_blocks_cfg->skip_blocks_cfg; | |
| u32 major, minor, sub_minor, blk_idx, num_blocks; | |
| struct hl_special_block_info *block_info_arr; | |
| int rc; | |
| block_info_arr = hdev->asic_prop.special_blocks; | |
| if (!block_info_arr) | |
| return -EINVAL; | |
| num_blocks = hdev->asic_prop.num_of_special_blocks; | |
| for (blk_idx = 0 ; blk_idx < num_blocks ; blk_idx++, block_info_arr++) { | |
| if (hl_check_block_type_exclusion(skip_blocks_cfg, block_info_arr->block_type)) | |
| continue; | |
| for (major = 0 ; major < block_info_arr->major ; major++) { | |
| minor = 0; | |
| do { | |
| sub_minor = 0; | |
| do { | |
| if ((hl_check_block_range_exclusion(hdev, | |
| skip_blocks_cfg, block_info_arr, | |
| major, minor, sub_minor)) || | |
| (skip_blocks_cfg->skip_block_hook && | |
| skip_blocks_cfg->skip_block_hook(hdev, | |
| special_blocks_cfg, | |
| blk_idx, major, minor, sub_minor))) { | |
| sub_minor++; | |
| continue; | |
| } | |
| rc = ctx->fn(hdev, blk_idx, major, minor, | |
| sub_minor, ctx->data); | |
| if (rc) | |
| return rc; | |
| sub_minor++; | |
| } while (sub_minor < block_info_arr->sub_minor); | |
| minor++; | |
| } while (minor < block_info_arr->minor); | |
| } | |
| } | |
| return 0; | |
| } |