162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
262306a36Sopenharmony_ci
362306a36Sopenharmony_ci/*
462306a36Sopenharmony_ci * Copyright 2020 HabanaLabs, Ltd.
562306a36Sopenharmony_ci * All Rights Reserved.
662306a36Sopenharmony_ci */
762306a36Sopenharmony_ci
862306a36Sopenharmony_ci#include "habanalabs.h"
962306a36Sopenharmony_ci
1062306a36Sopenharmony_cistatic const char * const hl_glbl_error_cause[HL_MAX_NUM_OF_GLBL_ERR_CAUSE] = {
1162306a36Sopenharmony_ci	"Error due to un-priv read",
1262306a36Sopenharmony_ci	"Error due to un-secure read",
1362306a36Sopenharmony_ci	"Error due to read from unmapped reg",
1462306a36Sopenharmony_ci	"Error due to un-priv write",
1562306a36Sopenharmony_ci	"Error due to un-secure write",
1662306a36Sopenharmony_ci	"Error due to write to unmapped reg",
1762306a36Sopenharmony_ci	"External I/F write sec violation",
1862306a36Sopenharmony_ci	"External I/F write to un-mapped reg",
1962306a36Sopenharmony_ci	"Read to write only",
2062306a36Sopenharmony_ci	"Write to read only"
2162306a36Sopenharmony_ci};
2262306a36Sopenharmony_ci
2362306a36Sopenharmony_ci/**
2462306a36Sopenharmony_ci * hl_get_pb_block - return the relevant block within the block array
2562306a36Sopenharmony_ci *
2662306a36Sopenharmony_ci * @hdev: pointer to hl_device structure
2762306a36Sopenharmony_ci * @mm_reg_addr: register address in the desired block
2862306a36Sopenharmony_ci * @pb_blocks: blocks array
2962306a36Sopenharmony_ci * @array_size: blocks array size
3062306a36Sopenharmony_ci *
3162306a36Sopenharmony_ci */
3262306a36Sopenharmony_cistatic int hl_get_pb_block(struct hl_device *hdev, u32 mm_reg_addr,
3362306a36Sopenharmony_ci		const u32 pb_blocks[], int array_size)
3462306a36Sopenharmony_ci{
3562306a36Sopenharmony_ci	int i;
3662306a36Sopenharmony_ci	u32 start_addr, end_addr;
3762306a36Sopenharmony_ci
3862306a36Sopenharmony_ci	for (i = 0 ; i < array_size ; i++) {
3962306a36Sopenharmony_ci		start_addr = pb_blocks[i];
4062306a36Sopenharmony_ci		end_addr = start_addr + HL_BLOCK_SIZE;
4162306a36Sopenharmony_ci
4262306a36Sopenharmony_ci		if ((mm_reg_addr >= start_addr) && (mm_reg_addr < end_addr))
4362306a36Sopenharmony_ci			return i;
4462306a36Sopenharmony_ci	}
4562306a36Sopenharmony_ci
4662306a36Sopenharmony_ci	dev_err(hdev->dev, "No protection domain was found for 0x%x\n",
4762306a36Sopenharmony_ci			mm_reg_addr);
4862306a36Sopenharmony_ci	return -EDOM;
4962306a36Sopenharmony_ci}
5062306a36Sopenharmony_ci
5162306a36Sopenharmony_ci/**
5262306a36Sopenharmony_ci * hl_unset_pb_in_block - clear a specific protection bit in a block
5362306a36Sopenharmony_ci *
5462306a36Sopenharmony_ci * @hdev: pointer to hl_device structure
5562306a36Sopenharmony_ci * @reg_offset: register offset will be converted to bit offset in pb block
5662306a36Sopenharmony_ci * @sgs_entry: pb array
5762306a36Sopenharmony_ci *
5862306a36Sopenharmony_ci */
5962306a36Sopenharmony_cistatic int hl_unset_pb_in_block(struct hl_device *hdev, u32 reg_offset,
6062306a36Sopenharmony_ci				struct hl_block_glbl_sec *sgs_entry)
6162306a36Sopenharmony_ci{
6262306a36Sopenharmony_ci	if ((reg_offset >= HL_BLOCK_SIZE) || (reg_offset & 0x3)) {
6362306a36Sopenharmony_ci		dev_err(hdev->dev,
6462306a36Sopenharmony_ci			"Register offset(%d) is out of range(%d) or invalid\n",
6562306a36Sopenharmony_ci			reg_offset, HL_BLOCK_SIZE);
6662306a36Sopenharmony_ci		return -EINVAL;
6762306a36Sopenharmony_ci	}
6862306a36Sopenharmony_ci
6962306a36Sopenharmony_ci	UNSET_GLBL_SEC_BIT(sgs_entry->sec_array,
7062306a36Sopenharmony_ci			 (reg_offset & (HL_BLOCK_SIZE - 1)) >> 2);
7162306a36Sopenharmony_ci
7262306a36Sopenharmony_ci	return 0;
7362306a36Sopenharmony_ci}
7462306a36Sopenharmony_ci
7562306a36Sopenharmony_ci/**
7662306a36Sopenharmony_ci * hl_unsecure_register - locate the relevant block for this register and
7762306a36Sopenharmony_ci *                        remove corresponding protection bit
7862306a36Sopenharmony_ci *
7962306a36Sopenharmony_ci * @hdev: pointer to hl_device structure
8062306a36Sopenharmony_ci * @mm_reg_addr: register address to unsecure
8162306a36Sopenharmony_ci * @offset: additional offset to the register address
8262306a36Sopenharmony_ci * @pb_blocks: blocks array
8362306a36Sopenharmony_ci * @sgs_array: pb array
8462306a36Sopenharmony_ci * @array_size: blocks array size
8562306a36Sopenharmony_ci *
8662306a36Sopenharmony_ci */
8762306a36Sopenharmony_ciint hl_unsecure_register(struct hl_device *hdev, u32 mm_reg_addr, int offset,
8862306a36Sopenharmony_ci		const u32 pb_blocks[], struct hl_block_glbl_sec sgs_array[],
8962306a36Sopenharmony_ci		int array_size)
9062306a36Sopenharmony_ci{
9162306a36Sopenharmony_ci	u32 reg_offset;
9262306a36Sopenharmony_ci	int block_num;
9362306a36Sopenharmony_ci
9462306a36Sopenharmony_ci	block_num = hl_get_pb_block(hdev, mm_reg_addr + offset, pb_blocks,
9562306a36Sopenharmony_ci			array_size);
9662306a36Sopenharmony_ci	if (block_num < 0)
9762306a36Sopenharmony_ci		return block_num;
9862306a36Sopenharmony_ci
9962306a36Sopenharmony_ci	reg_offset = (mm_reg_addr + offset) - pb_blocks[block_num];
10062306a36Sopenharmony_ci
10162306a36Sopenharmony_ci	return hl_unset_pb_in_block(hdev, reg_offset, &sgs_array[block_num]);
10262306a36Sopenharmony_ci}
10362306a36Sopenharmony_ci
10462306a36Sopenharmony_ci/**
10562306a36Sopenharmony_ci * hl_unsecure_register_range - locate the relevant block for this register
10662306a36Sopenharmony_ci *                              range and remove corresponding protection bit
10762306a36Sopenharmony_ci *
10862306a36Sopenharmony_ci * @hdev: pointer to hl_device structure
10962306a36Sopenharmony_ci * @mm_reg_range: register address range to unsecure
11062306a36Sopenharmony_ci * @offset: additional offset to the register address
11162306a36Sopenharmony_ci * @pb_blocks: blocks array
11262306a36Sopenharmony_ci * @sgs_array: pb array
11362306a36Sopenharmony_ci * @array_size: blocks array size
11462306a36Sopenharmony_ci *
11562306a36Sopenharmony_ci */
11662306a36Sopenharmony_cistatic int hl_unsecure_register_range(struct hl_device *hdev,
11762306a36Sopenharmony_ci		struct range mm_reg_range, int offset, const u32 pb_blocks[],
11862306a36Sopenharmony_ci		struct hl_block_glbl_sec sgs_array[],
11962306a36Sopenharmony_ci		int array_size)
12062306a36Sopenharmony_ci{
12162306a36Sopenharmony_ci	u32 reg_offset;
12262306a36Sopenharmony_ci	int i, block_num, rc = 0;
12362306a36Sopenharmony_ci
12462306a36Sopenharmony_ci	block_num = hl_get_pb_block(hdev,
12562306a36Sopenharmony_ci			mm_reg_range.start + offset, pb_blocks,
12662306a36Sopenharmony_ci			array_size);
12762306a36Sopenharmony_ci	if (block_num < 0)
12862306a36Sopenharmony_ci		return block_num;
12962306a36Sopenharmony_ci
13062306a36Sopenharmony_ci	for (i = mm_reg_range.start ; i <= mm_reg_range.end ; i += 4) {
13162306a36Sopenharmony_ci		reg_offset = (i + offset) - pb_blocks[block_num];
13262306a36Sopenharmony_ci		rc |= hl_unset_pb_in_block(hdev, reg_offset,
13362306a36Sopenharmony_ci					&sgs_array[block_num]);
13462306a36Sopenharmony_ci	}
13562306a36Sopenharmony_ci
13662306a36Sopenharmony_ci	return rc;
13762306a36Sopenharmony_ci}
13862306a36Sopenharmony_ci
13962306a36Sopenharmony_ci/**
14062306a36Sopenharmony_ci * hl_unsecure_registers - locate the relevant block for all registers and
14162306a36Sopenharmony_ci *                        remove corresponding protection bit
14262306a36Sopenharmony_ci *
14362306a36Sopenharmony_ci * @hdev: pointer to hl_device structure
14462306a36Sopenharmony_ci * @mm_reg_array: register address array to unsecure
14562306a36Sopenharmony_ci * @mm_array_size: register array size
14662306a36Sopenharmony_ci * @offset: additional offset to the register address
14762306a36Sopenharmony_ci * @pb_blocks: blocks array
14862306a36Sopenharmony_ci * @sgs_array: pb array
14962306a36Sopenharmony_ci * @blocks_array_size: blocks array size
15062306a36Sopenharmony_ci *
15162306a36Sopenharmony_ci */
15262306a36Sopenharmony_ciint hl_unsecure_registers(struct hl_device *hdev, const u32 mm_reg_array[],
15362306a36Sopenharmony_ci		int mm_array_size, int offset, const u32 pb_blocks[],
15462306a36Sopenharmony_ci		struct hl_block_glbl_sec sgs_array[], int blocks_array_size)
15562306a36Sopenharmony_ci{
15662306a36Sopenharmony_ci	int i, rc = 0;
15762306a36Sopenharmony_ci
15862306a36Sopenharmony_ci	for (i = 0 ; i < mm_array_size ; i++) {
15962306a36Sopenharmony_ci		rc = hl_unsecure_register(hdev, mm_reg_array[i], offset,
16062306a36Sopenharmony_ci				pb_blocks, sgs_array, blocks_array_size);
16162306a36Sopenharmony_ci
16262306a36Sopenharmony_ci		if (rc)
16362306a36Sopenharmony_ci			return rc;
16462306a36Sopenharmony_ci	}
16562306a36Sopenharmony_ci
16662306a36Sopenharmony_ci	return rc;
16762306a36Sopenharmony_ci}
16862306a36Sopenharmony_ci
16962306a36Sopenharmony_ci/**
17062306a36Sopenharmony_ci * hl_unsecure_registers_range - locate the relevant block for all register
17162306a36Sopenharmony_ci *                        ranges and remove corresponding protection bit
17262306a36Sopenharmony_ci *
17362306a36Sopenharmony_ci * @hdev: pointer to hl_device structure
17462306a36Sopenharmony_ci * @mm_reg_range_array: register address range array to unsecure
17562306a36Sopenharmony_ci * @mm_array_size: register array size
17662306a36Sopenharmony_ci * @offset: additional offset to the register address
17762306a36Sopenharmony_ci * @pb_blocks: blocks array
17862306a36Sopenharmony_ci * @sgs_array: pb array
17962306a36Sopenharmony_ci * @blocks_array_size: blocks array size
18062306a36Sopenharmony_ci *
18162306a36Sopenharmony_ci */
18262306a36Sopenharmony_cistatic int hl_unsecure_registers_range(struct hl_device *hdev,
18362306a36Sopenharmony_ci		const struct range mm_reg_range_array[], int mm_array_size,
18462306a36Sopenharmony_ci		int offset, const u32 pb_blocks[],
18562306a36Sopenharmony_ci		struct hl_block_glbl_sec sgs_array[], int blocks_array_size)
18662306a36Sopenharmony_ci{
18762306a36Sopenharmony_ci	int i, rc = 0;
18862306a36Sopenharmony_ci
18962306a36Sopenharmony_ci	for (i = 0 ; i < mm_array_size ; i++) {
19062306a36Sopenharmony_ci		rc = hl_unsecure_register_range(hdev, mm_reg_range_array[i],
19162306a36Sopenharmony_ci			offset, pb_blocks, sgs_array, blocks_array_size);
19262306a36Sopenharmony_ci
19362306a36Sopenharmony_ci		if (rc)
19462306a36Sopenharmony_ci			return rc;
19562306a36Sopenharmony_ci	}
19662306a36Sopenharmony_ci
19762306a36Sopenharmony_ci	return rc;
19862306a36Sopenharmony_ci}
19962306a36Sopenharmony_ci
20062306a36Sopenharmony_ci/**
20162306a36Sopenharmony_ci * hl_ack_pb_security_violations - Ack security violation
20262306a36Sopenharmony_ci *
20362306a36Sopenharmony_ci * @hdev: pointer to hl_device structure
20462306a36Sopenharmony_ci * @pb_blocks: blocks array
20562306a36Sopenharmony_ci * @block_offset: additional offset to the block
20662306a36Sopenharmony_ci * @array_size: blocks array size
20762306a36Sopenharmony_ci *
20862306a36Sopenharmony_ci */
20962306a36Sopenharmony_cistatic void hl_ack_pb_security_violations(struct hl_device *hdev,
21062306a36Sopenharmony_ci		const u32 pb_blocks[], u32 block_offset, int array_size)
21162306a36Sopenharmony_ci{
21262306a36Sopenharmony_ci	int i;
21362306a36Sopenharmony_ci	u32 cause, addr, block_base;
21462306a36Sopenharmony_ci
21562306a36Sopenharmony_ci	for (i = 0 ; i < array_size ; i++) {
21662306a36Sopenharmony_ci		block_base = pb_blocks[i] + block_offset;
21762306a36Sopenharmony_ci		cause = RREG32(block_base + HL_BLOCK_GLBL_ERR_CAUSE);
21862306a36Sopenharmony_ci		if (cause) {
21962306a36Sopenharmony_ci			addr = RREG32(block_base + HL_BLOCK_GLBL_ERR_ADDR);
22062306a36Sopenharmony_ci			hdev->asic_funcs->pb_print_security_errors(hdev,
22162306a36Sopenharmony_ci					block_base, cause, addr);
22262306a36Sopenharmony_ci			WREG32(block_base + HL_BLOCK_GLBL_ERR_CAUSE, cause);
22362306a36Sopenharmony_ci		}
22462306a36Sopenharmony_ci	}
22562306a36Sopenharmony_ci}
22662306a36Sopenharmony_ci
22762306a36Sopenharmony_ci/**
22862306a36Sopenharmony_ci * hl_config_glbl_sec - set pb in HW according to given pb array
22962306a36Sopenharmony_ci *
23062306a36Sopenharmony_ci * @hdev: pointer to hl_device structure
23162306a36Sopenharmony_ci * @pb_blocks: blocks array
23262306a36Sopenharmony_ci * @sgs_array: pb array
23362306a36Sopenharmony_ci * @block_offset: additional offset to the block
23462306a36Sopenharmony_ci * @array_size: blocks array size
23562306a36Sopenharmony_ci *
23662306a36Sopenharmony_ci */
23762306a36Sopenharmony_civoid hl_config_glbl_sec(struct hl_device *hdev, const u32 pb_blocks[],
23862306a36Sopenharmony_ci		struct hl_block_glbl_sec sgs_array[], u32 block_offset,
23962306a36Sopenharmony_ci		int array_size)
24062306a36Sopenharmony_ci{
24162306a36Sopenharmony_ci	int i, j;
24262306a36Sopenharmony_ci	u32 sgs_base;
24362306a36Sopenharmony_ci
24462306a36Sopenharmony_ci	if (hdev->pldm)
24562306a36Sopenharmony_ci		usleep_range(100, 1000);
24662306a36Sopenharmony_ci
24762306a36Sopenharmony_ci	for (i = 0 ; i < array_size ; i++) {
24862306a36Sopenharmony_ci		sgs_base = block_offset + pb_blocks[i] +
24962306a36Sopenharmony_ci				HL_BLOCK_GLBL_SEC_OFFS;
25062306a36Sopenharmony_ci
25162306a36Sopenharmony_ci		for (j = 0 ; j < HL_BLOCK_GLBL_SEC_LEN ; j++)
25262306a36Sopenharmony_ci			WREG32(sgs_base + j * sizeof(u32),
25362306a36Sopenharmony_ci				sgs_array[i].sec_array[j]);
25462306a36Sopenharmony_ci	}
25562306a36Sopenharmony_ci}
25662306a36Sopenharmony_ci
25762306a36Sopenharmony_ci/**
25862306a36Sopenharmony_ci * hl_secure_block - locally memsets a block to 0
25962306a36Sopenharmony_ci *
26062306a36Sopenharmony_ci * @hdev: pointer to hl_device structure
26162306a36Sopenharmony_ci * @sgs_array: pb array to clear
26262306a36Sopenharmony_ci * @array_size: blocks array size
26362306a36Sopenharmony_ci *
26462306a36Sopenharmony_ci */
26562306a36Sopenharmony_civoid hl_secure_block(struct hl_device *hdev,
26662306a36Sopenharmony_ci		struct hl_block_glbl_sec sgs_array[], int array_size)
26762306a36Sopenharmony_ci{
26862306a36Sopenharmony_ci	int i;
26962306a36Sopenharmony_ci
27062306a36Sopenharmony_ci	for (i = 0 ; i < array_size ; i++)
27162306a36Sopenharmony_ci		memset((char *)(sgs_array[i].sec_array), 0,
27262306a36Sopenharmony_ci			HL_BLOCK_GLBL_SEC_SIZE);
27362306a36Sopenharmony_ci}
27462306a36Sopenharmony_ci
27562306a36Sopenharmony_ci/**
27662306a36Sopenharmony_ci * hl_init_pb_with_mask - set selected pb instances with mask in HW according
27762306a36Sopenharmony_ci *                        to given configuration
27862306a36Sopenharmony_ci *
27962306a36Sopenharmony_ci * @hdev: pointer to hl_device structure
28062306a36Sopenharmony_ci * @num_dcores: number of decores to apply configuration to
28162306a36Sopenharmony_ci *              set to HL_PB_SHARED if need to apply only once
28262306a36Sopenharmony_ci * @dcore_offset: offset between dcores
28362306a36Sopenharmony_ci * @num_instances: number of instances to apply configuration to
28462306a36Sopenharmony_ci * @instance_offset: offset between instances
28562306a36Sopenharmony_ci * @pb_blocks: blocks array
28662306a36Sopenharmony_ci * @blocks_array_size: blocks array size
28762306a36Sopenharmony_ci * @user_regs_array: unsecured register array
28862306a36Sopenharmony_ci * @user_regs_array_size: unsecured register array size
28962306a36Sopenharmony_ci * @mask: enabled instances mask: 1- enabled, 0- disabled
29062306a36Sopenharmony_ci */
29162306a36Sopenharmony_ciint hl_init_pb_with_mask(struct hl_device *hdev, u32 num_dcores,
29262306a36Sopenharmony_ci		u32 dcore_offset, u32 num_instances, u32 instance_offset,
29362306a36Sopenharmony_ci		const u32 pb_blocks[], u32 blocks_array_size,
29462306a36Sopenharmony_ci		const u32 *user_regs_array, u32 user_regs_array_size, u64 mask)
29562306a36Sopenharmony_ci{
29662306a36Sopenharmony_ci	int i, j;
29762306a36Sopenharmony_ci	struct hl_block_glbl_sec *glbl_sec;
29862306a36Sopenharmony_ci
29962306a36Sopenharmony_ci	glbl_sec = kcalloc(blocks_array_size,
30062306a36Sopenharmony_ci			sizeof(struct hl_block_glbl_sec),
30162306a36Sopenharmony_ci			GFP_KERNEL);
30262306a36Sopenharmony_ci	if (!glbl_sec)
30362306a36Sopenharmony_ci		return -ENOMEM;
30462306a36Sopenharmony_ci
30562306a36Sopenharmony_ci	hl_secure_block(hdev, glbl_sec, blocks_array_size);
30662306a36Sopenharmony_ci	hl_unsecure_registers(hdev, user_regs_array, user_regs_array_size, 0,
30762306a36Sopenharmony_ci			pb_blocks, glbl_sec, blocks_array_size);
30862306a36Sopenharmony_ci
30962306a36Sopenharmony_ci	/* Fill all blocks with the same configuration */
31062306a36Sopenharmony_ci	for (i = 0 ; i < num_dcores ; i++) {
31162306a36Sopenharmony_ci		for (j = 0 ; j < num_instances ; j++) {
31262306a36Sopenharmony_ci			int seq = i * num_instances + j;
31362306a36Sopenharmony_ci
31462306a36Sopenharmony_ci			if (!(mask & BIT_ULL(seq)))
31562306a36Sopenharmony_ci				continue;
31662306a36Sopenharmony_ci
31762306a36Sopenharmony_ci			hl_config_glbl_sec(hdev, pb_blocks, glbl_sec,
31862306a36Sopenharmony_ci					i * dcore_offset + j * instance_offset,
31962306a36Sopenharmony_ci					blocks_array_size);
32062306a36Sopenharmony_ci		}
32162306a36Sopenharmony_ci	}
32262306a36Sopenharmony_ci
32362306a36Sopenharmony_ci	kfree(glbl_sec);
32462306a36Sopenharmony_ci
32562306a36Sopenharmony_ci	return 0;
32662306a36Sopenharmony_ci}
32762306a36Sopenharmony_ci
32862306a36Sopenharmony_ci/**
32962306a36Sopenharmony_ci * hl_init_pb - set pb in HW according to given configuration
33062306a36Sopenharmony_ci *
33162306a36Sopenharmony_ci * @hdev: pointer to hl_device structure
33262306a36Sopenharmony_ci * @num_dcores: number of decores to apply configuration to
33362306a36Sopenharmony_ci *              set to HL_PB_SHARED if need to apply only once
33462306a36Sopenharmony_ci * @dcore_offset: offset between dcores
33562306a36Sopenharmony_ci * @num_instances: number of instances to apply configuration to
33662306a36Sopenharmony_ci * @instance_offset: offset between instances
33762306a36Sopenharmony_ci * @pb_blocks: blocks array
33862306a36Sopenharmony_ci * @blocks_array_size: blocks array size
33962306a36Sopenharmony_ci * @user_regs_array: unsecured register array
34062306a36Sopenharmony_ci * @user_regs_array_size: unsecured register array size
34162306a36Sopenharmony_ci *
34262306a36Sopenharmony_ci */
34362306a36Sopenharmony_ciint hl_init_pb(struct hl_device *hdev, u32 num_dcores, u32 dcore_offset,
34462306a36Sopenharmony_ci		u32 num_instances, u32 instance_offset,
34562306a36Sopenharmony_ci		const u32 pb_blocks[], u32 blocks_array_size,
34662306a36Sopenharmony_ci		const u32 *user_regs_array, u32 user_regs_array_size)
34762306a36Sopenharmony_ci{
34862306a36Sopenharmony_ci	return hl_init_pb_with_mask(hdev, num_dcores, dcore_offset,
34962306a36Sopenharmony_ci			num_instances, instance_offset, pb_blocks,
35062306a36Sopenharmony_ci			blocks_array_size, user_regs_array,
35162306a36Sopenharmony_ci			user_regs_array_size, ULLONG_MAX);
35262306a36Sopenharmony_ci}
35362306a36Sopenharmony_ci
35462306a36Sopenharmony_ci/**
35562306a36Sopenharmony_ci * hl_init_pb_ranges_with_mask - set pb instances using mask in HW according to
35662306a36Sopenharmony_ci *                               given configuration unsecurring registers
35762306a36Sopenharmony_ci *                               ranges instead of specific registers
35862306a36Sopenharmony_ci *
35962306a36Sopenharmony_ci * @hdev: pointer to hl_device structure
36062306a36Sopenharmony_ci * @num_dcores: number of decores to apply configuration to
36162306a36Sopenharmony_ci *              set to HL_PB_SHARED if need to apply only once
36262306a36Sopenharmony_ci * @dcore_offset: offset between dcores
36362306a36Sopenharmony_ci * @num_instances: number of instances to apply configuration to
36462306a36Sopenharmony_ci * @instance_offset: offset between instances
36562306a36Sopenharmony_ci * @pb_blocks: blocks array
36662306a36Sopenharmony_ci * @blocks_array_size: blocks array size
36762306a36Sopenharmony_ci * @user_regs_range_array: unsecured register range array
36862306a36Sopenharmony_ci * @user_regs_range_array_size: unsecured register range array size
36962306a36Sopenharmony_ci * @mask: enabled instances mask: 1- enabled, 0- disabled
37062306a36Sopenharmony_ci */
37162306a36Sopenharmony_ciint hl_init_pb_ranges_with_mask(struct hl_device *hdev, u32 num_dcores,
37262306a36Sopenharmony_ci		u32 dcore_offset, u32 num_instances, u32 instance_offset,
37362306a36Sopenharmony_ci		const u32 pb_blocks[], u32 blocks_array_size,
37462306a36Sopenharmony_ci		const struct range *user_regs_range_array,
37562306a36Sopenharmony_ci		u32 user_regs_range_array_size, u64 mask)
37662306a36Sopenharmony_ci{
37762306a36Sopenharmony_ci	int i, j, rc = 0;
37862306a36Sopenharmony_ci	struct hl_block_glbl_sec *glbl_sec;
37962306a36Sopenharmony_ci
38062306a36Sopenharmony_ci	glbl_sec = kcalloc(blocks_array_size,
38162306a36Sopenharmony_ci			sizeof(struct hl_block_glbl_sec),
38262306a36Sopenharmony_ci			GFP_KERNEL);
38362306a36Sopenharmony_ci	if (!glbl_sec)
38462306a36Sopenharmony_ci		return -ENOMEM;
38562306a36Sopenharmony_ci
38662306a36Sopenharmony_ci	hl_secure_block(hdev, glbl_sec, blocks_array_size);
38762306a36Sopenharmony_ci	rc = hl_unsecure_registers_range(hdev, user_regs_range_array,
38862306a36Sopenharmony_ci			user_regs_range_array_size, 0, pb_blocks, glbl_sec,
38962306a36Sopenharmony_ci			blocks_array_size);
39062306a36Sopenharmony_ci	if (rc)
39162306a36Sopenharmony_ci		goto free_glbl_sec;
39262306a36Sopenharmony_ci
39362306a36Sopenharmony_ci	/* Fill all blocks with the same configuration */
39462306a36Sopenharmony_ci	for (i = 0 ; i < num_dcores ; i++) {
39562306a36Sopenharmony_ci		for (j = 0 ; j < num_instances ; j++) {
39662306a36Sopenharmony_ci			int seq = i * num_instances + j;
39762306a36Sopenharmony_ci
39862306a36Sopenharmony_ci			if (!(mask & BIT_ULL(seq)))
39962306a36Sopenharmony_ci				continue;
40062306a36Sopenharmony_ci
40162306a36Sopenharmony_ci			hl_config_glbl_sec(hdev, pb_blocks, glbl_sec,
40262306a36Sopenharmony_ci					i * dcore_offset + j * instance_offset,
40362306a36Sopenharmony_ci					blocks_array_size);
40462306a36Sopenharmony_ci		}
40562306a36Sopenharmony_ci	}
40662306a36Sopenharmony_ci
40762306a36Sopenharmony_cifree_glbl_sec:
40862306a36Sopenharmony_ci	kfree(glbl_sec);
40962306a36Sopenharmony_ci
41062306a36Sopenharmony_ci	return rc;
41162306a36Sopenharmony_ci}
41262306a36Sopenharmony_ci
41362306a36Sopenharmony_ci/**
41462306a36Sopenharmony_ci * hl_init_pb_ranges - set pb in HW according to given configuration unsecurring
41562306a36Sopenharmony_ci *                     registers ranges instead of specific registers
41662306a36Sopenharmony_ci *
41762306a36Sopenharmony_ci * @hdev: pointer to hl_device structure
41862306a36Sopenharmony_ci * @num_dcores: number of decores to apply configuration to
41962306a36Sopenharmony_ci *              set to HL_PB_SHARED if need to apply only once
42062306a36Sopenharmony_ci * @dcore_offset: offset between dcores
42162306a36Sopenharmony_ci * @num_instances: number of instances to apply configuration to
42262306a36Sopenharmony_ci * @instance_offset: offset between instances
42362306a36Sopenharmony_ci * @pb_blocks: blocks array
42462306a36Sopenharmony_ci * @blocks_array_size: blocks array size
42562306a36Sopenharmony_ci * @user_regs_range_array: unsecured register range array
42662306a36Sopenharmony_ci * @user_regs_range_array_size: unsecured register range array size
42762306a36Sopenharmony_ci *
42862306a36Sopenharmony_ci */
42962306a36Sopenharmony_ciint hl_init_pb_ranges(struct hl_device *hdev, u32 num_dcores,
43062306a36Sopenharmony_ci		u32 dcore_offset, u32 num_instances, u32 instance_offset,
43162306a36Sopenharmony_ci		const u32 pb_blocks[], u32 blocks_array_size,
43262306a36Sopenharmony_ci		const struct range *user_regs_range_array,
43362306a36Sopenharmony_ci		u32 user_regs_range_array_size)
43462306a36Sopenharmony_ci{
43562306a36Sopenharmony_ci	return hl_init_pb_ranges_with_mask(hdev, num_dcores, dcore_offset,
43662306a36Sopenharmony_ci			num_instances, instance_offset, pb_blocks,
43762306a36Sopenharmony_ci			blocks_array_size, user_regs_range_array,
43862306a36Sopenharmony_ci			user_regs_range_array_size, ULLONG_MAX);
43962306a36Sopenharmony_ci}
44062306a36Sopenharmony_ci
44162306a36Sopenharmony_ci/**
44262306a36Sopenharmony_ci * hl_init_pb_single_dcore - set pb for a single docre in HW
44362306a36Sopenharmony_ci * according to given configuration
44462306a36Sopenharmony_ci *
44562306a36Sopenharmony_ci * @hdev: pointer to hl_device structure
44662306a36Sopenharmony_ci * @dcore_offset: offset from the dcore0
44762306a36Sopenharmony_ci * @num_instances: number of instances to apply configuration to
44862306a36Sopenharmony_ci * @instance_offset: offset between instances
44962306a36Sopenharmony_ci * @pb_blocks: blocks array
45062306a36Sopenharmony_ci * @blocks_array_size: blocks array size
45162306a36Sopenharmony_ci * @user_regs_array: unsecured register array
45262306a36Sopenharmony_ci * @user_regs_array_size: unsecured register array size
45362306a36Sopenharmony_ci *
45462306a36Sopenharmony_ci */
45562306a36Sopenharmony_ciint hl_init_pb_single_dcore(struct hl_device *hdev, u32 dcore_offset,
45662306a36Sopenharmony_ci		u32 num_instances, u32 instance_offset,
45762306a36Sopenharmony_ci		const u32 pb_blocks[], u32 blocks_array_size,
45862306a36Sopenharmony_ci		const u32 *user_regs_array, u32 user_regs_array_size)
45962306a36Sopenharmony_ci{
46062306a36Sopenharmony_ci	int i, rc = 0;
46162306a36Sopenharmony_ci	struct hl_block_glbl_sec *glbl_sec;
46262306a36Sopenharmony_ci
46362306a36Sopenharmony_ci	glbl_sec = kcalloc(blocks_array_size,
46462306a36Sopenharmony_ci			sizeof(struct hl_block_glbl_sec),
46562306a36Sopenharmony_ci			GFP_KERNEL);
46662306a36Sopenharmony_ci	if (!glbl_sec)
46762306a36Sopenharmony_ci		return -ENOMEM;
46862306a36Sopenharmony_ci
46962306a36Sopenharmony_ci	hl_secure_block(hdev, glbl_sec, blocks_array_size);
47062306a36Sopenharmony_ci	rc = hl_unsecure_registers(hdev, user_regs_array, user_regs_array_size,
47162306a36Sopenharmony_ci			0, pb_blocks, glbl_sec, blocks_array_size);
47262306a36Sopenharmony_ci	if (rc)
47362306a36Sopenharmony_ci		goto free_glbl_sec;
47462306a36Sopenharmony_ci
47562306a36Sopenharmony_ci	/* Fill all blocks with the same configuration */
47662306a36Sopenharmony_ci	for (i = 0 ; i < num_instances ; i++)
47762306a36Sopenharmony_ci		hl_config_glbl_sec(hdev, pb_blocks, glbl_sec,
47862306a36Sopenharmony_ci				dcore_offset + i * instance_offset,
47962306a36Sopenharmony_ci				blocks_array_size);
48062306a36Sopenharmony_ci
48162306a36Sopenharmony_cifree_glbl_sec:
48262306a36Sopenharmony_ci	kfree(glbl_sec);
48362306a36Sopenharmony_ci
48462306a36Sopenharmony_ci	return rc;
48562306a36Sopenharmony_ci}
48662306a36Sopenharmony_ci
48762306a36Sopenharmony_ci/**
48862306a36Sopenharmony_ci * hl_init_pb_ranges_single_dcore - set pb for a single docre in HW according
48962306a36Sopenharmony_ci *                                  to given configuration unsecurring
49062306a36Sopenharmony_ci *                                  registers ranges instead of specific
49162306a36Sopenharmony_ci *                                  registers
49262306a36Sopenharmony_ci *
49362306a36Sopenharmony_ci * @hdev: pointer to hl_device structure
49462306a36Sopenharmony_ci * @dcore_offset: offset from the dcore0
49562306a36Sopenharmony_ci * @num_instances: number of instances to apply configuration to
49662306a36Sopenharmony_ci * @instance_offset: offset between instances
49762306a36Sopenharmony_ci * @pb_blocks: blocks array
49862306a36Sopenharmony_ci * @blocks_array_size: blocks array size
49962306a36Sopenharmony_ci * @user_regs_range_array: unsecured register range array
50062306a36Sopenharmony_ci * @user_regs_range_array_size: unsecured register range array size
50162306a36Sopenharmony_ci *
50262306a36Sopenharmony_ci */
50362306a36Sopenharmony_ciint hl_init_pb_ranges_single_dcore(struct hl_device *hdev, u32 dcore_offset,
50462306a36Sopenharmony_ci		u32 num_instances, u32 instance_offset,
50562306a36Sopenharmony_ci		const u32 pb_blocks[], u32 blocks_array_size,
50662306a36Sopenharmony_ci		const struct range *user_regs_range_array, u32 user_regs_range_array_size)
50762306a36Sopenharmony_ci{
50862306a36Sopenharmony_ci	int i;
50962306a36Sopenharmony_ci	struct hl_block_glbl_sec *glbl_sec;
51062306a36Sopenharmony_ci
51162306a36Sopenharmony_ci	glbl_sec = kcalloc(blocks_array_size,
51262306a36Sopenharmony_ci			sizeof(struct hl_block_glbl_sec),
51362306a36Sopenharmony_ci			GFP_KERNEL);
51462306a36Sopenharmony_ci	if (!glbl_sec)
51562306a36Sopenharmony_ci		return -ENOMEM;
51662306a36Sopenharmony_ci
51762306a36Sopenharmony_ci	hl_secure_block(hdev, glbl_sec, blocks_array_size);
51862306a36Sopenharmony_ci	hl_unsecure_registers_range(hdev, user_regs_range_array,
51962306a36Sopenharmony_ci			user_regs_range_array_size, 0, pb_blocks, glbl_sec,
52062306a36Sopenharmony_ci			blocks_array_size);
52162306a36Sopenharmony_ci
52262306a36Sopenharmony_ci	/* Fill all blocks with the same configuration */
52362306a36Sopenharmony_ci	for (i = 0 ; i < num_instances ; i++)
52462306a36Sopenharmony_ci		hl_config_glbl_sec(hdev, pb_blocks, glbl_sec,
52562306a36Sopenharmony_ci				dcore_offset + i * instance_offset,
52662306a36Sopenharmony_ci				blocks_array_size);
52762306a36Sopenharmony_ci
52862306a36Sopenharmony_ci	kfree(glbl_sec);
52962306a36Sopenharmony_ci
53062306a36Sopenharmony_ci	return 0;
53162306a36Sopenharmony_ci}
53262306a36Sopenharmony_ci
53362306a36Sopenharmony_ci/**
53462306a36Sopenharmony_ci * hl_ack_pb_with_mask - ack pb with mask in HW according to given configuration
53562306a36Sopenharmony_ci *
53662306a36Sopenharmony_ci * @hdev: pointer to hl_device structure
53762306a36Sopenharmony_ci * @num_dcores: number of decores to apply configuration to
53862306a36Sopenharmony_ci *              set to HL_PB_SHARED if need to apply only once
53962306a36Sopenharmony_ci * @dcore_offset: offset between dcores
54062306a36Sopenharmony_ci * @num_instances: number of instances to apply configuration to
54162306a36Sopenharmony_ci * @instance_offset: offset between instances
54262306a36Sopenharmony_ci * @pb_blocks: blocks array
54362306a36Sopenharmony_ci * @blocks_array_size: blocks array size
54462306a36Sopenharmony_ci * @mask: enabled instances mask: 1- enabled, 0- disabled
54562306a36Sopenharmony_ci *
54662306a36Sopenharmony_ci */
54762306a36Sopenharmony_civoid hl_ack_pb_with_mask(struct hl_device *hdev, u32 num_dcores,
54862306a36Sopenharmony_ci		u32 dcore_offset, u32 num_instances, u32 instance_offset,
54962306a36Sopenharmony_ci		const u32 pb_blocks[], u32 blocks_array_size, u64 mask)
55062306a36Sopenharmony_ci{
55162306a36Sopenharmony_ci	int i, j;
55262306a36Sopenharmony_ci
55362306a36Sopenharmony_ci	/* ack all blocks */
55462306a36Sopenharmony_ci	for (i = 0 ; i < num_dcores ; i++) {
55562306a36Sopenharmony_ci		for (j = 0 ; j < num_instances ; j++) {
55662306a36Sopenharmony_ci			int seq = i * num_instances + j;
55762306a36Sopenharmony_ci
55862306a36Sopenharmony_ci			if (!(mask & BIT_ULL(seq)))
55962306a36Sopenharmony_ci				continue;
56062306a36Sopenharmony_ci
56162306a36Sopenharmony_ci			hl_ack_pb_security_violations(hdev, pb_blocks,
56262306a36Sopenharmony_ci					i * dcore_offset + j * instance_offset,
56362306a36Sopenharmony_ci					blocks_array_size);
56462306a36Sopenharmony_ci		}
56562306a36Sopenharmony_ci	}
56662306a36Sopenharmony_ci}
56762306a36Sopenharmony_ci
56862306a36Sopenharmony_ci/**
56962306a36Sopenharmony_ci * hl_ack_pb - ack pb in HW according to given configuration
57062306a36Sopenharmony_ci *
57162306a36Sopenharmony_ci * @hdev: pointer to hl_device structure
57262306a36Sopenharmony_ci * @num_dcores: number of decores to apply configuration to
57362306a36Sopenharmony_ci *              set to HL_PB_SHARED if need to apply only once
57462306a36Sopenharmony_ci * @dcore_offset: offset between dcores
57562306a36Sopenharmony_ci * @num_instances: number of instances to apply configuration to
57662306a36Sopenharmony_ci * @instance_offset: offset between instances
57762306a36Sopenharmony_ci * @pb_blocks: blocks array
57862306a36Sopenharmony_ci * @blocks_array_size: blocks array size
57962306a36Sopenharmony_ci *
58062306a36Sopenharmony_ci */
58162306a36Sopenharmony_civoid hl_ack_pb(struct hl_device *hdev, u32 num_dcores, u32 dcore_offset,
58262306a36Sopenharmony_ci		u32 num_instances, u32 instance_offset,
58362306a36Sopenharmony_ci		const u32 pb_blocks[], u32 blocks_array_size)
58462306a36Sopenharmony_ci{
58562306a36Sopenharmony_ci	hl_ack_pb_with_mask(hdev, num_dcores, dcore_offset, num_instances,
58662306a36Sopenharmony_ci			instance_offset, pb_blocks, blocks_array_size,
58762306a36Sopenharmony_ci			ULLONG_MAX);
58862306a36Sopenharmony_ci}
58962306a36Sopenharmony_ci
59062306a36Sopenharmony_ci/**
59162306a36Sopenharmony_ci * hl_ack_pb_single_dcore - ack pb for single docre in HW
59262306a36Sopenharmony_ci * according to given configuration
59362306a36Sopenharmony_ci *
59462306a36Sopenharmony_ci * @hdev: pointer to hl_device structure
59562306a36Sopenharmony_ci * @dcore_offset: offset from dcore0
59662306a36Sopenharmony_ci * @num_instances: number of instances to apply configuration to
59762306a36Sopenharmony_ci * @instance_offset: offset between instances
59862306a36Sopenharmony_ci * @pb_blocks: blocks array
59962306a36Sopenharmony_ci * @blocks_array_size: blocks array size
60062306a36Sopenharmony_ci *
60162306a36Sopenharmony_ci */
60262306a36Sopenharmony_civoid hl_ack_pb_single_dcore(struct hl_device *hdev, u32 dcore_offset,
60362306a36Sopenharmony_ci		u32 num_instances, u32 instance_offset,
60462306a36Sopenharmony_ci		const u32 pb_blocks[], u32 blocks_array_size)
60562306a36Sopenharmony_ci{
60662306a36Sopenharmony_ci	int i;
60762306a36Sopenharmony_ci
60862306a36Sopenharmony_ci	/* ack all blocks */
60962306a36Sopenharmony_ci	for (i = 0 ; i < num_instances ; i++)
61062306a36Sopenharmony_ci		hl_ack_pb_security_violations(hdev, pb_blocks,
61162306a36Sopenharmony_ci				dcore_offset + i * instance_offset,
61262306a36Sopenharmony_ci				blocks_array_size);
61362306a36Sopenharmony_ci
61462306a36Sopenharmony_ci}
61562306a36Sopenharmony_ci
61662306a36Sopenharmony_cistatic u32 hl_automated_get_block_base_addr(struct hl_device *hdev,
61762306a36Sopenharmony_ci		struct hl_special_block_info *block_info,
61862306a36Sopenharmony_ci		u32 major, u32 minor, u32 sub_minor)
61962306a36Sopenharmony_ci{
62062306a36Sopenharmony_ci	u32 fw_block_base_address = block_info->base_addr +
62162306a36Sopenharmony_ci			major * block_info->major_offset +
62262306a36Sopenharmony_ci			minor * block_info->minor_offset +
62362306a36Sopenharmony_ci			sub_minor * block_info->sub_minor_offset;
62462306a36Sopenharmony_ci	struct asic_fixed_properties *prop = &hdev->asic_prop;
62562306a36Sopenharmony_ci
62662306a36Sopenharmony_ci	/* Calculation above returns an address for FW use, and therefore should
62762306a36Sopenharmony_ci	 * be casted for driver use.
62862306a36Sopenharmony_ci	 */
62962306a36Sopenharmony_ci	return (fw_block_base_address - lower_32_bits(prop->cfg_base_address));
63062306a36Sopenharmony_ci}
63162306a36Sopenharmony_ci
63262306a36Sopenharmony_cistatic bool hl_check_block_type_exclusion(struct hl_skip_blocks_cfg *skip_blocks_cfg,
63362306a36Sopenharmony_ci		int block_type)
63462306a36Sopenharmony_ci{
63562306a36Sopenharmony_ci	int i;
63662306a36Sopenharmony_ci
63762306a36Sopenharmony_ci	/* Check if block type is listed in the exclusion list of block types */
63862306a36Sopenharmony_ci	for (i = 0 ; i < skip_blocks_cfg->block_types_len ; i++)
63962306a36Sopenharmony_ci		if (block_type == skip_blocks_cfg->block_types[i])
64062306a36Sopenharmony_ci			return true;
64162306a36Sopenharmony_ci
64262306a36Sopenharmony_ci	return false;
64362306a36Sopenharmony_ci}
64462306a36Sopenharmony_ci
64562306a36Sopenharmony_cistatic bool hl_check_block_range_exclusion(struct hl_device *hdev,
64662306a36Sopenharmony_ci		struct hl_skip_blocks_cfg *skip_blocks_cfg,
64762306a36Sopenharmony_ci		struct hl_special_block_info *block_info,
64862306a36Sopenharmony_ci		u32 major, u32 minor, u32 sub_minor)
64962306a36Sopenharmony_ci{
65062306a36Sopenharmony_ci	u32 blocks_in_range, block_base_addr_in_range, block_base_addr;
65162306a36Sopenharmony_ci	int i, j;
65262306a36Sopenharmony_ci
65362306a36Sopenharmony_ci	block_base_addr = hl_automated_get_block_base_addr(hdev, block_info,
65462306a36Sopenharmony_ci			major, minor, sub_minor);
65562306a36Sopenharmony_ci
65662306a36Sopenharmony_ci	for (i = 0 ; i < skip_blocks_cfg->block_ranges_len ; i++) {
65762306a36Sopenharmony_ci		blocks_in_range = (skip_blocks_cfg->block_ranges[i].end -
65862306a36Sopenharmony_ci				skip_blocks_cfg->block_ranges[i].start) /
65962306a36Sopenharmony_ci				HL_BLOCK_SIZE + 1;
66062306a36Sopenharmony_ci		for (j = 0 ; j < blocks_in_range ; j++) {
66162306a36Sopenharmony_ci			block_base_addr_in_range = skip_blocks_cfg->block_ranges[i].start +
66262306a36Sopenharmony_ci					j * HL_BLOCK_SIZE;
66362306a36Sopenharmony_ci			if (block_base_addr == block_base_addr_in_range)
66462306a36Sopenharmony_ci				return true;
66562306a36Sopenharmony_ci		}
66662306a36Sopenharmony_ci	}
66762306a36Sopenharmony_ci
66862306a36Sopenharmony_ci	return false;
66962306a36Sopenharmony_ci}
67062306a36Sopenharmony_ci
67162306a36Sopenharmony_cistatic int hl_read_glbl_errors(struct hl_device *hdev,
67262306a36Sopenharmony_ci		u32 blk_idx, u32 major, u32 minor, u32 sub_minor, void *data)
67362306a36Sopenharmony_ci{
67462306a36Sopenharmony_ci	struct hl_special_block_info *special_blocks = hdev->asic_prop.special_blocks;
67562306a36Sopenharmony_ci	struct hl_special_block_info *current_block = &special_blocks[blk_idx];
67662306a36Sopenharmony_ci	u32 glbl_err_addr, glbl_err_cause, addr_val, cause_val, block_base,
67762306a36Sopenharmony_ci		base = current_block->base_addr - lower_32_bits(hdev->asic_prop.cfg_base_address);
67862306a36Sopenharmony_ci	int i;
67962306a36Sopenharmony_ci
68062306a36Sopenharmony_ci	block_base = base + major * current_block->major_offset +
68162306a36Sopenharmony_ci			minor * current_block->minor_offset +
68262306a36Sopenharmony_ci			sub_minor * current_block->sub_minor_offset;
68362306a36Sopenharmony_ci
68462306a36Sopenharmony_ci	glbl_err_cause = block_base + HL_GLBL_ERR_CAUSE_OFFSET;
68562306a36Sopenharmony_ci	cause_val = RREG32(glbl_err_cause);
68662306a36Sopenharmony_ci	if (!cause_val)
68762306a36Sopenharmony_ci		return 0;
68862306a36Sopenharmony_ci
68962306a36Sopenharmony_ci	glbl_err_addr = block_base + HL_GLBL_ERR_ADDR_OFFSET;
69062306a36Sopenharmony_ci	addr_val = RREG32(glbl_err_addr);
69162306a36Sopenharmony_ci
69262306a36Sopenharmony_ci	for (i = 0 ; i < hdev->asic_prop.glbl_err_cause_num ; i++) {
69362306a36Sopenharmony_ci		if (cause_val & BIT(i))
69462306a36Sopenharmony_ci			dev_err_ratelimited(hdev->dev,
69562306a36Sopenharmony_ci				"%s, addr %#llx\n",
69662306a36Sopenharmony_ci				hl_glbl_error_cause[i],
69762306a36Sopenharmony_ci				hdev->asic_prop.cfg_base_address + block_base +
69862306a36Sopenharmony_ci				FIELD_GET(HL_GLBL_ERR_ADDRESS_MASK, addr_val));
69962306a36Sopenharmony_ci	}
70062306a36Sopenharmony_ci
70162306a36Sopenharmony_ci	WREG32(glbl_err_cause, cause_val);
70262306a36Sopenharmony_ci
70362306a36Sopenharmony_ci	return 0;
70462306a36Sopenharmony_ci}
70562306a36Sopenharmony_ci
70662306a36Sopenharmony_civoid hl_check_for_glbl_errors(struct hl_device *hdev)
70762306a36Sopenharmony_ci{
70862306a36Sopenharmony_ci	struct asic_fixed_properties *prop = &hdev->asic_prop;
70962306a36Sopenharmony_ci	struct hl_special_blocks_cfg special_blocks_cfg;
71062306a36Sopenharmony_ci	struct iterate_special_ctx glbl_err_iter;
71162306a36Sopenharmony_ci	int rc;
71262306a36Sopenharmony_ci
71362306a36Sopenharmony_ci	memset(&special_blocks_cfg, 0, sizeof(special_blocks_cfg));
71462306a36Sopenharmony_ci	special_blocks_cfg.skip_blocks_cfg = &prop->skip_special_blocks_cfg;
71562306a36Sopenharmony_ci
71662306a36Sopenharmony_ci	glbl_err_iter.fn = &hl_read_glbl_errors;
71762306a36Sopenharmony_ci	glbl_err_iter.data = &special_blocks_cfg;
71862306a36Sopenharmony_ci
71962306a36Sopenharmony_ci	rc = hl_iterate_special_blocks(hdev, &glbl_err_iter);
72062306a36Sopenharmony_ci	if (rc)
72162306a36Sopenharmony_ci		dev_err_ratelimited(hdev->dev,
72262306a36Sopenharmony_ci			"Could not iterate special blocks, glbl error check failed\n");
72362306a36Sopenharmony_ci}
72462306a36Sopenharmony_ci
72562306a36Sopenharmony_ciint hl_iterate_special_blocks(struct hl_device *hdev, struct iterate_special_ctx *ctx)
72662306a36Sopenharmony_ci{
72762306a36Sopenharmony_ci	struct hl_special_blocks_cfg *special_blocks_cfg =
72862306a36Sopenharmony_ci			(struct hl_special_blocks_cfg *)ctx->data;
72962306a36Sopenharmony_ci	struct hl_skip_blocks_cfg *skip_blocks_cfg =
73062306a36Sopenharmony_ci			special_blocks_cfg->skip_blocks_cfg;
73162306a36Sopenharmony_ci	u32 major, minor, sub_minor, blk_idx, num_blocks;
73262306a36Sopenharmony_ci	struct hl_special_block_info *block_info_arr;
73362306a36Sopenharmony_ci	int rc;
73462306a36Sopenharmony_ci
73562306a36Sopenharmony_ci	block_info_arr = hdev->asic_prop.special_blocks;
73662306a36Sopenharmony_ci	if (!block_info_arr)
73762306a36Sopenharmony_ci		return -EINVAL;
73862306a36Sopenharmony_ci
73962306a36Sopenharmony_ci	num_blocks = hdev->asic_prop.num_of_special_blocks;
74062306a36Sopenharmony_ci
74162306a36Sopenharmony_ci	for (blk_idx = 0 ; blk_idx < num_blocks ; blk_idx++, block_info_arr++) {
74262306a36Sopenharmony_ci		if (hl_check_block_type_exclusion(skip_blocks_cfg, block_info_arr->block_type))
74362306a36Sopenharmony_ci			continue;
74462306a36Sopenharmony_ci
74562306a36Sopenharmony_ci		for (major = 0 ; major < block_info_arr->major ; major++) {
74662306a36Sopenharmony_ci			minor = 0;
74762306a36Sopenharmony_ci			do {
74862306a36Sopenharmony_ci				sub_minor = 0;
74962306a36Sopenharmony_ci				do {
75062306a36Sopenharmony_ci					if ((hl_check_block_range_exclusion(hdev,
75162306a36Sopenharmony_ci							skip_blocks_cfg, block_info_arr,
75262306a36Sopenharmony_ci							major, minor, sub_minor)) ||
75362306a36Sopenharmony_ci						(skip_blocks_cfg->skip_block_hook &&
75462306a36Sopenharmony_ci						skip_blocks_cfg->skip_block_hook(hdev,
75562306a36Sopenharmony_ci							special_blocks_cfg,
75662306a36Sopenharmony_ci							blk_idx, major, minor, sub_minor))) {
75762306a36Sopenharmony_ci						sub_minor++;
75862306a36Sopenharmony_ci						continue;
75962306a36Sopenharmony_ci					}
76062306a36Sopenharmony_ci
76162306a36Sopenharmony_ci					rc = ctx->fn(hdev, blk_idx, major, minor,
76262306a36Sopenharmony_ci								sub_minor, ctx->data);
76362306a36Sopenharmony_ci					if (rc)
76462306a36Sopenharmony_ci						return rc;
76562306a36Sopenharmony_ci
76662306a36Sopenharmony_ci					sub_minor++;
76762306a36Sopenharmony_ci				} while (sub_minor < block_info_arr->sub_minor);
76862306a36Sopenharmony_ci
76962306a36Sopenharmony_ci				minor++;
77062306a36Sopenharmony_ci			} while (minor < block_info_arr->minor);
77162306a36Sopenharmony_ci		}
77262306a36Sopenharmony_ci	}
77362306a36Sopenharmony_ci
77462306a36Sopenharmony_ci	return 0;
77562306a36Sopenharmony_ci}
776