1// SPDX-License-Identifier: GPL-2.0
2
3/*
4 * Copyright 2020 HabanaLabs, Ltd.
5 * All Rights Reserved.
6 */
7
8#include "habanalabs.h"
9
10static const char * const hl_glbl_error_cause[] = {
11	"Error due to un-priv read",
12	"Error due to un-secure read",
13	"Error due to read from unmapped reg",
14	"Error due to un-priv write",
15	"Error due to un-secure write",
16	"Error due to write to unmapped reg",
17	"N/A",
18	"N/A",
19	"N/A",
20	"N/A",
21	"N/A",
22	"N/A",
23	"N/A",
24	"N/A",
25	"N/A",
26	"N/A",
27	"External I/F write sec violation",
28	"External I/F write to un-mapped reg",
29	"N/A",
30	"N/A",
31	"N/A",
32	"N/A",
33	"N/A",
34	"N/A",
35	"Read to write only",
36	"Write to read only"
37};
38
39/**
40 * hl_get_pb_block - return the relevant block within the block array
41 *
42 * @hdev: pointer to hl_device structure
43 * @mm_reg_addr: register address in the desired block
44 * @pb_blocks: blocks array
45 * @array_size: blocks array size
46 *
47 */
48static int hl_get_pb_block(struct hl_device *hdev, u32 mm_reg_addr,
49		const u32 pb_blocks[], int array_size)
50{
51	int i;
52	u32 start_addr, end_addr;
53
54	for (i = 0 ; i < array_size ; i++) {
55		start_addr = pb_blocks[i];
56		end_addr = start_addr + HL_BLOCK_SIZE;
57
58		if ((mm_reg_addr >= start_addr) && (mm_reg_addr < end_addr))
59			return i;
60	}
61
62	dev_err(hdev->dev, "No protection domain was found for 0x%x\n",
63			mm_reg_addr);
64	return -EDOM;
65}
66
67/**
68 * hl_unset_pb_in_block - clear a specific protection bit in a block
69 *
70 * @hdev: pointer to hl_device structure
71 * @reg_offset: register offset will be converted to bit offset in pb block
72 * @sgs_entry: pb array
73 *
74 */
75static int hl_unset_pb_in_block(struct hl_device *hdev, u32 reg_offset,
76				struct hl_block_glbl_sec *sgs_entry)
77{
78	if ((reg_offset >= HL_BLOCK_SIZE) || (reg_offset & 0x3)) {
79		dev_err(hdev->dev,
80			"Register offset(%d) is out of range(%d) or invalid\n",
81			reg_offset, HL_BLOCK_SIZE);
82		return -EINVAL;
83	}
84
85	UNSET_GLBL_SEC_BIT(sgs_entry->sec_array,
86			 (reg_offset & (HL_BLOCK_SIZE - 1)) >> 2);
87
88	return 0;
89}
90
91/**
92 * hl_unsecure_register - locate the relevant block for this register and
93 *                        remove corresponding protection bit
94 *
95 * @hdev: pointer to hl_device structure
96 * @mm_reg_addr: register address to unsecure
97 * @offset: additional offset to the register address
98 * @pb_blocks: blocks array
99 * @sgs_array: pb array
100 * @array_size: blocks array size
101 *
102 */
103int hl_unsecure_register(struct hl_device *hdev, u32 mm_reg_addr, int offset,
104		const u32 pb_blocks[], struct hl_block_glbl_sec sgs_array[],
105		int array_size)
106{
107	u32 reg_offset;
108	int block_num;
109
110	block_num = hl_get_pb_block(hdev, mm_reg_addr + offset, pb_blocks,
111			array_size);
112	if (block_num < 0)
113		return block_num;
114
115	reg_offset = (mm_reg_addr + offset) - pb_blocks[block_num];
116
117	return hl_unset_pb_in_block(hdev, reg_offset, &sgs_array[block_num]);
118}
119
120/**
121 * hl_unsecure_register_range - locate the relevant block for this register
122 *                              range and remove corresponding protection bit
123 *
124 * @hdev: pointer to hl_device structure
125 * @mm_reg_range: register address range to unsecure
126 * @offset: additional offset to the register address
127 * @pb_blocks: blocks array
128 * @sgs_array: pb array
129 * @array_size: blocks array size
130 *
131 */
132static int hl_unsecure_register_range(struct hl_device *hdev,
133		struct range mm_reg_range, int offset, const u32 pb_blocks[],
134		struct hl_block_glbl_sec sgs_array[],
135		int array_size)
136{
137	u32 reg_offset;
138	int i, block_num, rc = 0;
139
140	block_num = hl_get_pb_block(hdev,
141			mm_reg_range.start + offset, pb_blocks,
142			array_size);
143	if (block_num < 0)
144		return block_num;
145
146	for (i = mm_reg_range.start ; i <= mm_reg_range.end ; i += 4) {
147		reg_offset = (i + offset) - pb_blocks[block_num];
148		rc |= hl_unset_pb_in_block(hdev, reg_offset,
149					&sgs_array[block_num]);
150	}
151
152	return rc;
153}
154
155/**
156 * hl_unsecure_registers - locate the relevant block for all registers and
157 *                        remove corresponding protection bit
158 *
159 * @hdev: pointer to hl_device structure
160 * @mm_reg_array: register address array to unsecure
161 * @mm_array_size: register array size
162 * @offset: additional offset to the register address
163 * @pb_blocks: blocks array
164 * @sgs_array: pb array
165 * @blocks_array_size: blocks array size
166 *
167 */
168int hl_unsecure_registers(struct hl_device *hdev, const u32 mm_reg_array[],
169		int mm_array_size, int offset, const u32 pb_blocks[],
170		struct hl_block_glbl_sec sgs_array[], int blocks_array_size)
171{
172	int i, rc = 0;
173
174	for (i = 0 ; i < mm_array_size ; i++) {
175		rc = hl_unsecure_register(hdev, mm_reg_array[i], offset,
176				pb_blocks, sgs_array, blocks_array_size);
177
178		if (rc)
179			return rc;
180	}
181
182	return rc;
183}
184
185/**
186 * hl_unsecure_registers_range - locate the relevant block for all register
187 *                        ranges and remove corresponding protection bit
188 *
189 * @hdev: pointer to hl_device structure
190 * @mm_reg_range_array: register address range array to unsecure
191 * @mm_array_size: register array size
192 * @offset: additional offset to the register address
193 * @pb_blocks: blocks array
194 * @sgs_array: pb array
195 * @blocks_array_size: blocks array size
196 *
197 */
198static int hl_unsecure_registers_range(struct hl_device *hdev,
199		const struct range mm_reg_range_array[], int mm_array_size,
200		int offset, const u32 pb_blocks[],
201		struct hl_block_glbl_sec sgs_array[], int blocks_array_size)
202{
203	int i, rc = 0;
204
205	for (i = 0 ; i < mm_array_size ; i++) {
206		rc = hl_unsecure_register_range(hdev, mm_reg_range_array[i],
207			offset, pb_blocks, sgs_array, blocks_array_size);
208
209		if (rc)
210			return rc;
211	}
212
213	return rc;
214}
215
216/**
217 * hl_ack_pb_security_violations - Ack security violation
218 *
219 * @hdev: pointer to hl_device structure
220 * @pb_blocks: blocks array
221 * @block_offset: additional offset to the block
222 * @array_size: blocks array size
223 *
224 */
225static void hl_ack_pb_security_violations(struct hl_device *hdev,
226		const u32 pb_blocks[], u32 block_offset, int array_size)
227{
228	int i;
229	u32 cause, addr, block_base;
230
231	for (i = 0 ; i < array_size ; i++) {
232		block_base = pb_blocks[i] + block_offset;
233		cause = RREG32(block_base + HL_BLOCK_GLBL_ERR_CAUSE);
234		if (cause) {
235			addr = RREG32(block_base + HL_BLOCK_GLBL_ERR_ADDR);
236			hdev->asic_funcs->pb_print_security_errors(hdev,
237					block_base, cause, addr);
238			WREG32(block_base + HL_BLOCK_GLBL_ERR_CAUSE, cause);
239		}
240	}
241}
242
243/**
244 * hl_config_glbl_sec - set pb in HW according to given pb array
245 *
246 * @hdev: pointer to hl_device structure
247 * @pb_blocks: blocks array
248 * @sgs_array: pb array
249 * @block_offset: additional offset to the block
250 * @array_size: blocks array size
251 *
252 */
253void hl_config_glbl_sec(struct hl_device *hdev, const u32 pb_blocks[],
254		struct hl_block_glbl_sec sgs_array[], u32 block_offset,
255		int array_size)
256{
257	int i, j;
258	u32 sgs_base;
259
260	if (hdev->pldm)
261		usleep_range(100, 1000);
262
263	for (i = 0 ; i < array_size ; i++) {
264		sgs_base = block_offset + pb_blocks[i] +
265				HL_BLOCK_GLBL_SEC_OFFS;
266
267		for (j = 0 ; j < HL_BLOCK_GLBL_SEC_LEN ; j++)
268			WREG32(sgs_base + j * sizeof(u32),
269				sgs_array[i].sec_array[j]);
270	}
271}
272
273/**
274 * hl_secure_block - locally memsets a block to 0
275 *
276 * @hdev: pointer to hl_device structure
277 * @sgs_array: pb array to clear
278 * @array_size: blocks array size
279 *
280 */
281void hl_secure_block(struct hl_device *hdev,
282		struct hl_block_glbl_sec sgs_array[], int array_size)
283{
284	int i;
285
286	for (i = 0 ; i < array_size ; i++)
287		memset((char *)(sgs_array[i].sec_array), 0,
288			HL_BLOCK_GLBL_SEC_SIZE);
289}
290
291/**
292 * hl_init_pb_with_mask - set selected pb instances with mask in HW according
293 *                        to given configuration
294 *
295 * @hdev: pointer to hl_device structure
296 * @num_dcores: number of decores to apply configuration to
297 *              set to HL_PB_SHARED if need to apply only once
298 * @dcore_offset: offset between dcores
299 * @num_instances: number of instances to apply configuration to
300 * @instance_offset: offset between instances
301 * @pb_blocks: blocks array
302 * @blocks_array_size: blocks array size
303 * @user_regs_array: unsecured register array
304 * @user_regs_array_size: unsecured register array size
305 * @mask: enabled instances mask: 1- enabled, 0- disabled
306 */
307int hl_init_pb_with_mask(struct hl_device *hdev, u32 num_dcores,
308		u32 dcore_offset, u32 num_instances, u32 instance_offset,
309		const u32 pb_blocks[], u32 blocks_array_size,
310		const u32 *user_regs_array, u32 user_regs_array_size, u64 mask)
311{
312	int i, j;
313	struct hl_block_glbl_sec *glbl_sec;
314
315	glbl_sec = kcalloc(blocks_array_size,
316			sizeof(struct hl_block_glbl_sec),
317			GFP_KERNEL);
318	if (!glbl_sec)
319		return -ENOMEM;
320
321	hl_secure_block(hdev, glbl_sec, blocks_array_size);
322	hl_unsecure_registers(hdev, user_regs_array, user_regs_array_size, 0,
323			pb_blocks, glbl_sec, blocks_array_size);
324
325	/* Fill all blocks with the same configuration */
326	for (i = 0 ; i < num_dcores ; i++) {
327		for (j = 0 ; j < num_instances ; j++) {
328			int seq = i * num_instances + j;
329
330			if (!(mask & BIT_ULL(seq)))
331				continue;
332
333			hl_config_glbl_sec(hdev, pb_blocks, glbl_sec,
334					i * dcore_offset + j * instance_offset,
335					blocks_array_size);
336		}
337	}
338
339	kfree(glbl_sec);
340
341	return 0;
342}
343
344/**
345 * hl_init_pb - set pb in HW according to given configuration
346 *
347 * @hdev: pointer to hl_device structure
348 * @num_dcores: number of decores to apply configuration to
349 *              set to HL_PB_SHARED if need to apply only once
350 * @dcore_offset: offset between dcores
351 * @num_instances: number of instances to apply configuration to
352 * @instance_offset: offset between instances
353 * @pb_blocks: blocks array
354 * @blocks_array_size: blocks array size
355 * @user_regs_array: unsecured register array
356 * @user_regs_array_size: unsecured register array size
357 *
358 */
359int hl_init_pb(struct hl_device *hdev, u32 num_dcores, u32 dcore_offset,
360		u32 num_instances, u32 instance_offset,
361		const u32 pb_blocks[], u32 blocks_array_size,
362		const u32 *user_regs_array, u32 user_regs_array_size)
363{
364	return hl_init_pb_with_mask(hdev, num_dcores, dcore_offset,
365			num_instances, instance_offset, pb_blocks,
366			blocks_array_size, user_regs_array,
367			user_regs_array_size, ULLONG_MAX);
368}
369
370/**
371 * hl_init_pb_ranges_with_mask - set pb instances using mask in HW according to
372 *                               given configuration unsecurring registers
373 *                               ranges instead of specific registers
374 *
375 * @hdev: pointer to hl_device structure
376 * @num_dcores: number of decores to apply configuration to
377 *              set to HL_PB_SHARED if need to apply only once
378 * @dcore_offset: offset between dcores
379 * @num_instances: number of instances to apply configuration to
380 * @instance_offset: offset between instances
381 * @pb_blocks: blocks array
382 * @blocks_array_size: blocks array size
383 * @user_regs_range_array: unsecured register range array
384 * @user_regs_range_array_size: unsecured register range array size
385 * @mask: enabled instances mask: 1- enabled, 0- disabled
386 */
387int hl_init_pb_ranges_with_mask(struct hl_device *hdev, u32 num_dcores,
388		u32 dcore_offset, u32 num_instances, u32 instance_offset,
389		const u32 pb_blocks[], u32 blocks_array_size,
390		const struct range *user_regs_range_array,
391		u32 user_regs_range_array_size, u64 mask)
392{
393	int i, j, rc = 0;
394	struct hl_block_glbl_sec *glbl_sec;
395
396	glbl_sec = kcalloc(blocks_array_size,
397			sizeof(struct hl_block_glbl_sec),
398			GFP_KERNEL);
399	if (!glbl_sec)
400		return -ENOMEM;
401
402	hl_secure_block(hdev, glbl_sec, blocks_array_size);
403	rc = hl_unsecure_registers_range(hdev, user_regs_range_array,
404			user_regs_range_array_size, 0, pb_blocks, glbl_sec,
405			blocks_array_size);
406	if (rc)
407		goto free_glbl_sec;
408
409	/* Fill all blocks with the same configuration */
410	for (i = 0 ; i < num_dcores ; i++) {
411		for (j = 0 ; j < num_instances ; j++) {
412			int seq = i * num_instances + j;
413
414			if (!(mask & BIT_ULL(seq)))
415				continue;
416
417			hl_config_glbl_sec(hdev, pb_blocks, glbl_sec,
418					i * dcore_offset + j * instance_offset,
419					blocks_array_size);
420		}
421	}
422
423free_glbl_sec:
424	kfree(glbl_sec);
425
426	return rc;
427}
428
429/**
430 * hl_init_pb_ranges - set pb in HW according to given configuration unsecurring
431 *                     registers ranges instead of specific registers
432 *
433 * @hdev: pointer to hl_device structure
434 * @num_dcores: number of decores to apply configuration to
435 *              set to HL_PB_SHARED if need to apply only once
436 * @dcore_offset: offset between dcores
437 * @num_instances: number of instances to apply configuration to
438 * @instance_offset: offset between instances
439 * @pb_blocks: blocks array
440 * @blocks_array_size: blocks array size
441 * @user_regs_range_array: unsecured register range array
442 * @user_regs_range_array_size: unsecured register range array size
443 *
444 */
445int hl_init_pb_ranges(struct hl_device *hdev, u32 num_dcores,
446		u32 dcore_offset, u32 num_instances, u32 instance_offset,
447		const u32 pb_blocks[], u32 blocks_array_size,
448		const struct range *user_regs_range_array,
449		u32 user_regs_range_array_size)
450{
451	return hl_init_pb_ranges_with_mask(hdev, num_dcores, dcore_offset,
452			num_instances, instance_offset, pb_blocks,
453			blocks_array_size, user_regs_range_array,
454			user_regs_range_array_size, ULLONG_MAX);
455}
456
457/**
458 * hl_init_pb_single_dcore - set pb for a single docre in HW
459 * according to given configuration
460 *
461 * @hdev: pointer to hl_device structure
462 * @dcore_offset: offset from the dcore0
463 * @num_instances: number of instances to apply configuration to
464 * @instance_offset: offset between instances
465 * @pb_blocks: blocks array
466 * @blocks_array_size: blocks array size
467 * @user_regs_array: unsecured register array
468 * @user_regs_array_size: unsecured register array size
469 *
470 */
471int hl_init_pb_single_dcore(struct hl_device *hdev, u32 dcore_offset,
472		u32 num_instances, u32 instance_offset,
473		const u32 pb_blocks[], u32 blocks_array_size,
474		const u32 *user_regs_array, u32 user_regs_array_size)
475{
476	int i, rc = 0;
477	struct hl_block_glbl_sec *glbl_sec;
478
479	glbl_sec = kcalloc(blocks_array_size,
480			sizeof(struct hl_block_glbl_sec),
481			GFP_KERNEL);
482	if (!glbl_sec)
483		return -ENOMEM;
484
485	hl_secure_block(hdev, glbl_sec, blocks_array_size);
486	rc = hl_unsecure_registers(hdev, user_regs_array, user_regs_array_size,
487			0, pb_blocks, glbl_sec, blocks_array_size);
488	if (rc)
489		goto free_glbl_sec;
490
491	/* Fill all blocks with the same configuration */
492	for (i = 0 ; i < num_instances ; i++)
493		hl_config_glbl_sec(hdev, pb_blocks, glbl_sec,
494				dcore_offset + i * instance_offset,
495				blocks_array_size);
496
497free_glbl_sec:
498	kfree(glbl_sec);
499
500	return rc;
501}
502
503/**
504 * hl_init_pb_ranges_single_dcore - set pb for a single docre in HW according
505 *                                  to given configuration unsecurring
506 *                                  registers ranges instead of specific
507 *                                  registers
508 *
509 * @hdev: pointer to hl_device structure
510 * @dcore_offset: offset from the dcore0
511 * @num_instances: number of instances to apply configuration to
512 * @instance_offset: offset between instances
513 * @pb_blocks: blocks array
514 * @blocks_array_size: blocks array size
515 * @user_regs_range_array: unsecured register range array
516 * @user_regs_range_array_size: unsecured register range array size
517 *
518 */
519int hl_init_pb_ranges_single_dcore(struct hl_device *hdev, u32 dcore_offset,
520		u32 num_instances, u32 instance_offset,
521		const u32 pb_blocks[], u32 blocks_array_size,
522		const struct range *user_regs_range_array, u32 user_regs_range_array_size)
523{
524	int i;
525	struct hl_block_glbl_sec *glbl_sec;
526
527	glbl_sec = kcalloc(blocks_array_size,
528			sizeof(struct hl_block_glbl_sec),
529			GFP_KERNEL);
530	if (!glbl_sec)
531		return -ENOMEM;
532
533	hl_secure_block(hdev, glbl_sec, blocks_array_size);
534	hl_unsecure_registers_range(hdev, user_regs_range_array,
535			user_regs_range_array_size, 0, pb_blocks, glbl_sec,
536			blocks_array_size);
537
538	/* Fill all blocks with the same configuration */
539	for (i = 0 ; i < num_instances ; i++)
540		hl_config_glbl_sec(hdev, pb_blocks, glbl_sec,
541				dcore_offset + i * instance_offset,
542				blocks_array_size);
543
544	kfree(glbl_sec);
545
546	return 0;
547}
548
549/**
550 * hl_ack_pb_with_mask - ack pb with mask in HW according to given configuration
551 *
552 * @hdev: pointer to hl_device structure
553 * @num_dcores: number of decores to apply configuration to
554 *              set to HL_PB_SHARED if need to apply only once
555 * @dcore_offset: offset between dcores
556 * @num_instances: number of instances to apply configuration to
557 * @instance_offset: offset between instances
558 * @pb_blocks: blocks array
559 * @blocks_array_size: blocks array size
560 * @mask: enabled instances mask: 1- enabled, 0- disabled
561 *
562 */
563void hl_ack_pb_with_mask(struct hl_device *hdev, u32 num_dcores,
564		u32 dcore_offset, u32 num_instances, u32 instance_offset,
565		const u32 pb_blocks[], u32 blocks_array_size, u64 mask)
566{
567	int i, j;
568
569	/* ack all blocks */
570	for (i = 0 ; i < num_dcores ; i++) {
571		for (j = 0 ; j < num_instances ; j++) {
572			int seq = i * num_instances + j;
573
574			if (!(mask & BIT_ULL(seq)))
575				continue;
576
577			hl_ack_pb_security_violations(hdev, pb_blocks,
578					i * dcore_offset + j * instance_offset,
579					blocks_array_size);
580		}
581	}
582}
583
584/**
585 * hl_ack_pb - ack pb in HW according to given configuration
586 *
587 * @hdev: pointer to hl_device structure
588 * @num_dcores: number of decores to apply configuration to
589 *              set to HL_PB_SHARED if need to apply only once
590 * @dcore_offset: offset between dcores
591 * @num_instances: number of instances to apply configuration to
592 * @instance_offset: offset between instances
593 * @pb_blocks: blocks array
594 * @blocks_array_size: blocks array size
595 *
596 */
597void hl_ack_pb(struct hl_device *hdev, u32 num_dcores, u32 dcore_offset,
598		u32 num_instances, u32 instance_offset,
599		const u32 pb_blocks[], u32 blocks_array_size)
600{
601	hl_ack_pb_with_mask(hdev, num_dcores, dcore_offset, num_instances,
602			instance_offset, pb_blocks, blocks_array_size,
603			ULLONG_MAX);
604}
605
606/**
607 * hl_ack_pb_single_dcore - ack pb for single docre in HW
608 * according to given configuration
609 *
610 * @hdev: pointer to hl_device structure
611 * @dcore_offset: offset from dcore0
612 * @num_instances: number of instances to apply configuration to
613 * @instance_offset: offset between instances
614 * @pb_blocks: blocks array
615 * @blocks_array_size: blocks array size
616 *
617 */
618void hl_ack_pb_single_dcore(struct hl_device *hdev, u32 dcore_offset,
619		u32 num_instances, u32 instance_offset,
620		const u32 pb_blocks[], u32 blocks_array_size)
621{
622	int i;
623
624	/* ack all blocks */
625	for (i = 0 ; i < num_instances ; i++)
626		hl_ack_pb_security_violations(hdev, pb_blocks,
627				dcore_offset + i * instance_offset,
628				blocks_array_size);
629
630}
631
632static u32 hl_automated_get_block_base_addr(struct hl_device *hdev,
633		struct hl_special_block_info *block_info,
634		u32 major, u32 minor, u32 sub_minor)
635{
636	u32 fw_block_base_address = block_info->base_addr +
637			major * block_info->major_offset +
638			minor * block_info->minor_offset +
639			sub_minor * block_info->sub_minor_offset;
640	struct asic_fixed_properties *prop = &hdev->asic_prop;
641
642	/* Calculation above returns an address for FW use, and therefore should
643	 * be casted for driver use.
644	 */
645	return (fw_block_base_address - lower_32_bits(prop->cfg_base_address));
646}
647
648static bool hl_check_block_type_exclusion(struct hl_skip_blocks_cfg *skip_blocks_cfg,
649		int block_type)
650{
651	int i;
652
653	/* Check if block type is listed in the exclusion list of block types */
654	for (i = 0 ; i < skip_blocks_cfg->block_types_len ; i++)
655		if (block_type == skip_blocks_cfg->block_types[i])
656			return true;
657
658	return false;
659}
660
661static bool hl_check_block_range_exclusion(struct hl_device *hdev,
662		struct hl_skip_blocks_cfg *skip_blocks_cfg,
663		struct hl_special_block_info *block_info,
664		u32 major, u32 minor, u32 sub_minor)
665{
666	u32 blocks_in_range, block_base_addr_in_range, block_base_addr;
667	int i, j;
668
669	block_base_addr = hl_automated_get_block_base_addr(hdev, block_info,
670			major, minor, sub_minor);
671
672	for (i = 0 ; i < skip_blocks_cfg->block_ranges_len ; i++) {
673		blocks_in_range = (skip_blocks_cfg->block_ranges[i].end -
674				skip_blocks_cfg->block_ranges[i].start) /
675				HL_BLOCK_SIZE + 1;
676		for (j = 0 ; j < blocks_in_range ; j++) {
677			block_base_addr_in_range = skip_blocks_cfg->block_ranges[i].start +
678					j * HL_BLOCK_SIZE;
679			if (block_base_addr == block_base_addr_in_range)
680				return true;
681		}
682	}
683
684	return false;
685}
686
687static int hl_read_glbl_errors(struct hl_device *hdev,
688		u32 blk_idx, u32 major, u32 minor, u32 sub_minor, void *data)
689{
690	struct asic_fixed_properties *prop = &hdev->asic_prop;
691	struct hl_special_block_info *special_blocks = prop->special_blocks;
692	struct hl_special_block_info *current_block = &special_blocks[blk_idx];
693	u32 glbl_err_addr, glbl_err_cause, addr_val, cause_val, block_base,
694		base = current_block->base_addr - lower_32_bits(prop->cfg_base_address);
695	int i;
696
697	block_base = base + major * current_block->major_offset +
698			minor * current_block->minor_offset +
699			sub_minor * current_block->sub_minor_offset;
700
701	glbl_err_cause = block_base + HL_GLBL_ERR_CAUSE_OFFSET;
702	cause_val = RREG32(glbl_err_cause);
703	if (!cause_val)
704		return 0;
705
706	glbl_err_addr = block_base + HL_GLBL_ERR_ADDR_OFFSET;
707	addr_val = RREG32(glbl_err_addr);
708
709	for (i = 0 ; i <= prop->glbl_err_max_cause_num ; i++) {
710		if (cause_val & BIT(i))
711			dev_err_ratelimited(hdev->dev,
712					"%s, addr %#llx\n",
713					hl_glbl_error_cause[i],
714					prop->cfg_base_address + block_base +
715						FIELD_GET(HL_GLBL_ERR_ADDRESS_MASK, addr_val));
716	}
717
718	WREG32(glbl_err_cause, cause_val);
719
720	return 0;
721}
722
723void hl_check_for_glbl_errors(struct hl_device *hdev)
724{
725	struct asic_fixed_properties *prop = &hdev->asic_prop;
726	struct hl_special_blocks_cfg special_blocks_cfg;
727	struct iterate_special_ctx glbl_err_iter;
728	int rc;
729
730	memset(&special_blocks_cfg, 0, sizeof(special_blocks_cfg));
731	special_blocks_cfg.skip_blocks_cfg = &prop->skip_special_blocks_cfg;
732
733	glbl_err_iter.fn = &hl_read_glbl_errors;
734	glbl_err_iter.data = &special_blocks_cfg;
735
736	rc = hl_iterate_special_blocks(hdev, &glbl_err_iter);
737	if (rc)
738		dev_err_ratelimited(hdev->dev,
739			"Could not iterate special blocks, glbl error check failed\n");
740}
741
742int hl_iterate_special_blocks(struct hl_device *hdev, struct iterate_special_ctx *ctx)
743{
744	struct hl_special_blocks_cfg *special_blocks_cfg =
745			(struct hl_special_blocks_cfg *)ctx->data;
746	struct hl_skip_blocks_cfg *skip_blocks_cfg =
747			special_blocks_cfg->skip_blocks_cfg;
748	u32 major, minor, sub_minor, blk_idx, num_blocks;
749	struct hl_special_block_info *block_info_arr;
750	int rc;
751
752	block_info_arr = hdev->asic_prop.special_blocks;
753	if (!block_info_arr)
754		return -EINVAL;
755
756	num_blocks = hdev->asic_prop.num_of_special_blocks;
757
758	for (blk_idx = 0 ; blk_idx < num_blocks ; blk_idx++, block_info_arr++) {
759		if (hl_check_block_type_exclusion(skip_blocks_cfg, block_info_arr->block_type))
760			continue;
761
762		for (major = 0 ; major < block_info_arr->major ; major++) {
763			minor = 0;
764			do {
765				sub_minor = 0;
766				do {
767					if ((hl_check_block_range_exclusion(hdev,
768							skip_blocks_cfg, block_info_arr,
769							major, minor, sub_minor)) ||
770						(skip_blocks_cfg->skip_block_hook &&
771						skip_blocks_cfg->skip_block_hook(hdev,
772							special_blocks_cfg,
773							blk_idx, major, minor, sub_minor))) {
774						sub_minor++;
775						continue;
776					}
777
778					rc = ctx->fn(hdev, blk_idx, major, minor,
779								sub_minor, ctx->data);
780					if (rc)
781						return rc;
782
783					sub_minor++;
784				} while (sub_minor < block_info_arr->sub_minor);
785
786				minor++;
787			} while (minor < block_info_arr->minor);
788		}
789	}
790
791	return 0;
792}
793