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
20static 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
46static void dec_abnrm_intr_work(struct work_struct *work)
47{
48	struct hl_dec *dec = container_of(work, struct hl_dec, abnrm_intr_work);
49	struct hl_device *hdev = dec->hdev;
50	u32 irq_status, event_mask = 0;
51	bool reset_required = false;
52
53	irq_status = RREG32(dec->base_addr + VCMD_IRQ_STATUS_OFFSET);
54
55	dev_err(hdev->dev, "Decoder abnormal interrupt %#x, core %d\n", irq_status, dec->core_id);
56
57	dec_print_abnrm_intr_source(hdev, irq_status);
58
59	/* Clear the interrupt */
60	WREG32(dec->base_addr + VCMD_IRQ_STATUS_OFFSET, irq_status);
61
62	/* Flush the interrupt clear */
63	RREG32(dec->base_addr + VCMD_IRQ_STATUS_OFFSET);
64
65	if (irq_status & VCMD_IRQ_STATUS_TIMEOUT_MASK) {
66		reset_required = true;
67		event_mask |= HL_NOTIFIER_EVENT_GENERAL_HW_ERR;
68	}
69
70	if (irq_status & VCMD_IRQ_STATUS_CMDERR_MASK)
71		event_mask |= HL_NOTIFIER_EVENT_UNDEFINED_OPCODE;
72
73	if (irq_status & (VCMD_IRQ_STATUS_ENDCMD_MASK |
74				VCMD_IRQ_STATUS_BUSERR_MASK |
75				VCMD_IRQ_STATUS_ABORT_MASK))
76		event_mask |= HL_NOTIFIER_EVENT_USER_ENGINE_ERR;
77
78	if (reset_required) {
79		event_mask |= HL_NOTIFIER_EVENT_DEVICE_RESET;
80		hl_device_cond_reset(hdev, 0, event_mask);
81	} else if (event_mask) {
82		hl_notifier_event_send_all(hdev, event_mask);
83	}
84}
85
86void hl_dec_fini(struct hl_device *hdev)
87{
88	kfree(hdev->dec);
89}
90
91int hl_dec_init(struct hl_device *hdev)
92{
93	struct asic_fixed_properties *prop = &hdev->asic_prop;
94	struct hl_dec *dec;
95	int rc, j;
96
97	/* if max core is 0, nothing to do*/
98	if (!prop->max_dec)
99		return 0;
100
101	hdev->dec = kcalloc(prop->max_dec, sizeof(struct hl_dec), GFP_KERNEL);
102	if (!hdev->dec)
103		return -ENOMEM;
104
105	for (j = 0 ; j < prop->max_dec ; j++) {
106		dec = hdev->dec + j;
107
108		dec->hdev = hdev;
109		INIT_WORK(&dec->abnrm_intr_work, dec_abnrm_intr_work);
110		dec->core_id = j;
111		dec->base_addr = hdev->asic_funcs->get_dec_base_addr(hdev, j);
112		if (!dec->base_addr) {
113			dev_err(hdev->dev, "Invalid base address of decoder %d\n", j);
114			rc = -EINVAL;
115			goto err_dec_fini;
116		}
117	}
118
119	return 0;
120
121err_dec_fini:
122	hl_dec_fini(hdev);
123
124	return rc;
125}
126
127void hl_dec_ctx_fini(struct hl_ctx *ctx)
128{
129	struct hl_device *hdev = ctx->hdev;
130	struct asic_fixed_properties *prop = &hdev->asic_prop;
131	struct hl_dec *dec;
132	int j;
133
134	for (j = 0 ; j < prop->max_dec ; j++) {
135		if (!!(prop->decoder_enabled_mask & BIT(j))) {
136			dec = hdev->dec + j;
137			/* Stop the decoder */
138			WREG32(dec->base_addr + VCMD_CONTROL_OFFSET, 0);
139		}
140	}
141}
142