1 // SPDX-License-Identifier: GPL-2.0
2 
3 /*
4  * Copyright 2022 HabanaLabs, Ltd.
5  * All Rights Reserved.
6  */
7 
8 #include "habanalabs.h"
9 
10 #define VCMD_CONTROL_OFFSET			0x40	/* SWREG16 */
11 #define VCMD_IRQ_STATUS_OFFSET			0x44	/* SWREG17 */
12 
13 #define VCMD_IRQ_STATUS_ENDCMD_MASK		0x1
14 #define VCMD_IRQ_STATUS_BUSERR_MASK		0x2
15 #define VCMD_IRQ_STATUS_TIMEOUT_MASK		0x4
16 #define VCMD_IRQ_STATUS_CMDERR_MASK		0x8
17 #define VCMD_IRQ_STATUS_ABORT_MASK		0x10
18 #define VCMD_IRQ_STATUS_RESET_MASK		0x20
19 
20 static void dec_print_abnrm_intr_source(struct hl_device *hdev, u32 irq_status)
21 {
22 	const char *format = "abnormal interrupt source:%s%s%s%s%s%s\n";
23 	char *intr_source[6] = {"Unknown", "", "", "", "", ""};
24 	int i = 0;
25 
26 	if (!irq_status)
27 		return;
28 
29 	if (irq_status & VCMD_IRQ_STATUS_ENDCMD_MASK)
30 		intr_source[i++] = " ENDCMD";
31 	if (irq_status & VCMD_IRQ_STATUS_BUSERR_MASK)
32 		intr_source[i++] = " BUSERR";
33 	if (irq_status & VCMD_IRQ_STATUS_TIMEOUT_MASK)
34 		intr_source[i++] = " TIMEOUT";
35 	if (irq_status & VCMD_IRQ_STATUS_CMDERR_MASK)
36 		intr_source[i++] = " CMDERR";
37 	if (irq_status & VCMD_IRQ_STATUS_ABORT_MASK)
38 		intr_source[i++] = " ABORT";
39 	if (irq_status & VCMD_IRQ_STATUS_RESET_MASK)
40 		intr_source[i++] = " RESET";
41 
42 	dev_err(hdev->dev, format, intr_source[0], intr_source[1],
43 		intr_source[2], intr_source[3], intr_source[4], intr_source[5]);
44 }
45 
46 static void dec_error_intr_work(struct hl_device *hdev, u32 base_addr, u32 core_id)
47 {
48 	bool reset_required = false;
49 	u32 irq_status, event_mask;
50 
51 	irq_status = RREG32(base_addr + VCMD_IRQ_STATUS_OFFSET);
52 
53 	dev_err(hdev->dev, "Decoder abnormal interrupt %#x, core %d\n", irq_status, core_id);
54 
55 	dec_print_abnrm_intr_source(hdev, irq_status);
56 
57 	/* Clear the interrupt */
58 	WREG32(base_addr + VCMD_IRQ_STATUS_OFFSET, irq_status);
59 
60 	/* Flush the interrupt clear */
61 	RREG32(base_addr + VCMD_IRQ_STATUS_OFFSET);
62 
63 	if (irq_status & VCMD_IRQ_STATUS_TIMEOUT_MASK) {
64 		reset_required = true;
65 		event_mask = HL_NOTIFIER_EVENT_GENERAL_HW_ERR;
66 	} else if (irq_status & VCMD_IRQ_STATUS_CMDERR_MASK) {
67 		event_mask = HL_NOTIFIER_EVENT_UNDEFINED_OPCODE;
68 	} else {
69 		event_mask = HL_NOTIFIER_EVENT_USER_ENGINE_ERR;
70 	}
71 
72 	if (reset_required) {
73 		event_mask |= HL_NOTIFIER_EVENT_DEVICE_RESET;
74 		hl_device_cond_reset(hdev, 0, event_mask);
75 	} else {
76 		hl_notifier_event_send_all(hdev, event_mask);
77 	}
78 }
79 
80 static void dec_completion_abnrm(struct work_struct *work)
81 {
82 	struct hl_dec *dec = container_of(work, struct hl_dec, completion_abnrm_work);
83 	struct hl_device *hdev = dec->hdev;
84 
85 	dec_error_intr_work(hdev, dec->base_addr, dec->core_id);
86 }
87 
88 void hl_dec_fini(struct hl_device *hdev)
89 {
90 	kfree(hdev->dec);
91 }
92 
93 int hl_dec_init(struct hl_device *hdev)
94 {
95 	struct asic_fixed_properties *prop = &hdev->asic_prop;
96 	struct hl_dec *dec;
97 	int rc, j;
98 
99 	/* if max core is 0, nothing to do*/
100 	if (!prop->max_dec)
101 		return 0;
102 
103 	hdev->dec = kcalloc(prop->max_dec, sizeof(struct hl_dec), GFP_KERNEL);
104 	if (!hdev->dec)
105 		return -ENOMEM;
106 
107 	for (j = 0 ; j < prop->max_dec ; j++) {
108 		dec = hdev->dec + j;
109 
110 		dec->hdev = hdev;
111 		INIT_WORK(&dec->completion_abnrm_work, dec_completion_abnrm);
112 		dec->core_id = j;
113 		dec->base_addr = hdev->asic_funcs->get_dec_base_addr(hdev, j);
114 		if (!dec->base_addr) {
115 			dev_err(hdev->dev, "Invalid base address of decoder %d\n", j);
116 			rc = -EINVAL;
117 			goto err_dec_fini;
118 		}
119 	}
120 
121 	return 0;
122 
123 err_dec_fini:
124 	hl_dec_fini(hdev);
125 
126 	return rc;
127 }
128 
129 void hl_dec_ctx_fini(struct hl_ctx *ctx)
130 {
131 	struct hl_device *hdev = ctx->hdev;
132 	struct asic_fixed_properties *prop = &hdev->asic_prop;
133 	struct hl_dec *dec;
134 	int j;
135 
136 	for (j = 0 ; j < prop->max_dec ; j++) {
137 		if (!!(prop->decoder_enabled_mask & BIT(j))) {
138 			dec = hdev->dec + j;
139 			/* Stop the decoder */
140 			WREG32(dec->base_addr + VCMD_CONTROL_OFFSET, 0);
141 		}
142 	}
143 }
144