summaryrefslogtreecommitdiff
path: root/drivers/accel/habanalabs/common/decoder.c
blob: c03a6da45d002e6d8747dd3633fbff03c932c217 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
// SPDX-License-Identifier: GPL-2.0

/*
 * Copyright 2022 HabanaLabs, Ltd.
 * All Rights Reserved.
 */

#include "habanalabs.h"

#define VCMD_CONTROL_OFFSET			0x40	/* SWREG16 */
#define VCMD_IRQ_STATUS_OFFSET			0x44	/* SWREG17 */

#define VCMD_IRQ_STATUS_ENDCMD_MASK		0x1
#define VCMD_IRQ_STATUS_BUSERR_MASK		0x2
#define VCMD_IRQ_STATUS_TIMEOUT_MASK		0x4
#define VCMD_IRQ_STATUS_CMDERR_MASK		0x8
#define VCMD_IRQ_STATUS_ABORT_MASK		0x10
#define VCMD_IRQ_STATUS_RESET_MASK		0x20

static void dec_print_abnrm_intr_source(struct hl_device *hdev, u32 irq_status)
{
	const char *format = "abnormal interrupt source:%s%s%s%s%s%s\n";
	char *intr_source[6] = {"Unknown", "", "", "", "", ""};
	int i = 0;

	if (!irq_status)
		return;

	if (irq_status & VCMD_IRQ_STATUS_ENDCMD_MASK)
		intr_source[i++] = " ENDCMD";
	if (irq_status & VCMD_IRQ_STATUS_BUSERR_MASK)
		intr_source[i++] = " BUSERR";
	if (irq_status & VCMD_IRQ_STATUS_TIMEOUT_MASK)
		intr_source[i++] = " TIMEOUT";
	if (irq_status & VCMD_IRQ_STATUS_CMDERR_MASK)
		intr_source[i++] = " CMDERR";
	if (irq_status & VCMD_IRQ_STATUS_ABORT_MASK)
		intr_source[i++] = " ABORT";
	if (irq_status & VCMD_IRQ_STATUS_RESET_MASK)
		intr_source[i++] = " RESET";

	dev_err(hdev->dev, format, intr_source[0], intr_source[1],
		intr_source[2], intr_source[3], intr_source[4], intr_source[5]);
}

static void dec_abnrm_intr_work(struct work_struct *work)
{
	struct hl_dec *dec = container_of(work, struct hl_dec, abnrm_intr_work);
	struct hl_device *hdev = dec->hdev;
	u32 irq_status, event_mask = 0;
	bool reset_required = false;

	irq_status = RREG32(dec->base_addr + VCMD_IRQ_STATUS_OFFSET);

	dev_err(hdev->dev, "Decoder abnormal interrupt %#x, core %d\n", irq_status, dec->core_id);

	dec_print_abnrm_intr_source(hdev, irq_status);

	/* Clear the interrupt */
	WREG32(dec->base_addr + VCMD_IRQ_STATUS_OFFSET, irq_status);

	/* Flush the interrupt clear */
	RREG32(dec->base_addr + VCMD_IRQ_STATUS_OFFSET);

	if (irq_status & VCMD_IRQ_STATUS_TIMEOUT_MASK) {
		reset_required = true;
		event_mask |= HL_NOTIFIER_EVENT_GENERAL_HW_ERR;
	}

	if (irq_status & VCMD_IRQ_STATUS_CMDERR_MASK)
		event_mask |= HL_NOTIFIER_EVENT_UNDEFINED_OPCODE;

	if (irq_status & (VCMD_IRQ_STATUS_ENDCMD_MASK |
				VCMD_IRQ_STATUS_BUSERR_MASK |
				VCMD_IRQ_STATUS_ABORT_MASK))
		event_mask |= HL_NOTIFIER_EVENT_USER_ENGINE_ERR;

	if (reset_required) {
		event_mask |= HL_NOTIFIER_EVENT_DEVICE_RESET;
		hl_device_cond_reset(hdev, 0, event_mask);
	} else if (event_mask) {
		hl_notifier_event_send_all(hdev, event_mask);
	}
}

void hl_dec_fini(struct hl_device *hdev)
{
	kfree(hdev->dec);
}

int hl_dec_init(struct hl_device *hdev)
{
	struct asic_fixed_properties *prop = &hdev->asic_prop;
	struct hl_dec *dec;
	int rc, j;

	/* if max core is 0, nothing to do*/
	if (!prop->max_dec)
		return 0;

	hdev->dec = kcalloc(prop->max_dec, sizeof(struct hl_dec), GFP_KERNEL);
	if (!hdev->dec)
		return -ENOMEM;

	for (j = 0 ; j < prop->max_dec ; j++) {
		dec = hdev->dec + j;

		dec->hdev = hdev;
		INIT_WORK(&dec->abnrm_intr_work, dec_abnrm_intr_work);
		dec->core_id = j;
		dec->base_addr = hdev->asic_funcs->get_dec_base_addr(hdev, j);
		if (!dec->base_addr) {
			dev_err(hdev->dev, "Invalid base address of decoder %d\n", j);
			rc = -EINVAL;
			goto err_dec_fini;
		}
	}

	return 0;

err_dec_fini:
	hl_dec_fini(hdev);

	return rc;
}

void hl_dec_ctx_fini(struct hl_ctx *ctx)
{
	struct hl_device *hdev = ctx->hdev;
	struct asic_fixed_properties *prop = &hdev->asic_prop;
	struct hl_dec *dec;
	int j;

	for (j = 0 ; j < prop->max_dec ; j++) {
		if (!!(prop->decoder_enabled_mask & BIT(j))) {
			dec = hdev->dec + j;
			/* Stop the decoder */
			WREG32(dec->base_addr + VCMD_CONTROL_OFFSET, 0);
		}
	}
}