162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
262306a36Sopenharmony_ci
362306a36Sopenharmony_ci/*
462306a36Sopenharmony_ci * Copyright 2022 HabanaLabs, Ltd.
562306a36Sopenharmony_ci * All Rights Reserved.
662306a36Sopenharmony_ci */
762306a36Sopenharmony_ci
862306a36Sopenharmony_ci#include "habanalabs.h"
962306a36Sopenharmony_ci
1062306a36Sopenharmony_ci#define VCMD_CONTROL_OFFSET			0x40	/* SWREG16 */
1162306a36Sopenharmony_ci#define VCMD_IRQ_STATUS_OFFSET			0x44	/* SWREG17 */
1262306a36Sopenharmony_ci
1362306a36Sopenharmony_ci#define VCMD_IRQ_STATUS_ENDCMD_MASK		0x1
1462306a36Sopenharmony_ci#define VCMD_IRQ_STATUS_BUSERR_MASK		0x2
1562306a36Sopenharmony_ci#define VCMD_IRQ_STATUS_TIMEOUT_MASK		0x4
1662306a36Sopenharmony_ci#define VCMD_IRQ_STATUS_CMDERR_MASK		0x8
1762306a36Sopenharmony_ci#define VCMD_IRQ_STATUS_ABORT_MASK		0x10
1862306a36Sopenharmony_ci#define VCMD_IRQ_STATUS_RESET_MASK		0x20
1962306a36Sopenharmony_ci
2062306a36Sopenharmony_cistatic void dec_print_abnrm_intr_source(struct hl_device *hdev, u32 irq_status)
2162306a36Sopenharmony_ci{
2262306a36Sopenharmony_ci	const char *format = "abnormal interrupt source:%s%s%s%s%s%s\n";
2362306a36Sopenharmony_ci	char *intr_source[6] = {"Unknown", "", "", "", "", ""};
2462306a36Sopenharmony_ci	int i = 0;
2562306a36Sopenharmony_ci
2662306a36Sopenharmony_ci	if (!irq_status)
2762306a36Sopenharmony_ci		return;
2862306a36Sopenharmony_ci
2962306a36Sopenharmony_ci	if (irq_status & VCMD_IRQ_STATUS_ENDCMD_MASK)
3062306a36Sopenharmony_ci		intr_source[i++] = " ENDCMD";
3162306a36Sopenharmony_ci	if (irq_status & VCMD_IRQ_STATUS_BUSERR_MASK)
3262306a36Sopenharmony_ci		intr_source[i++] = " BUSERR";
3362306a36Sopenharmony_ci	if (irq_status & VCMD_IRQ_STATUS_TIMEOUT_MASK)
3462306a36Sopenharmony_ci		intr_source[i++] = " TIMEOUT";
3562306a36Sopenharmony_ci	if (irq_status & VCMD_IRQ_STATUS_CMDERR_MASK)
3662306a36Sopenharmony_ci		intr_source[i++] = " CMDERR";
3762306a36Sopenharmony_ci	if (irq_status & VCMD_IRQ_STATUS_ABORT_MASK)
3862306a36Sopenharmony_ci		intr_source[i++] = " ABORT";
3962306a36Sopenharmony_ci	if (irq_status & VCMD_IRQ_STATUS_RESET_MASK)
4062306a36Sopenharmony_ci		intr_source[i++] = " RESET";
4162306a36Sopenharmony_ci
4262306a36Sopenharmony_ci	dev_err(hdev->dev, format, intr_source[0], intr_source[1],
4362306a36Sopenharmony_ci		intr_source[2], intr_source[3], intr_source[4], intr_source[5]);
4462306a36Sopenharmony_ci}
4562306a36Sopenharmony_ci
4662306a36Sopenharmony_cistatic void dec_abnrm_intr_work(struct work_struct *work)
4762306a36Sopenharmony_ci{
4862306a36Sopenharmony_ci	struct hl_dec *dec = container_of(work, struct hl_dec, abnrm_intr_work);
4962306a36Sopenharmony_ci	struct hl_device *hdev = dec->hdev;
5062306a36Sopenharmony_ci	u32 irq_status, event_mask = 0;
5162306a36Sopenharmony_ci	bool reset_required = false;
5262306a36Sopenharmony_ci
5362306a36Sopenharmony_ci	irq_status = RREG32(dec->base_addr + VCMD_IRQ_STATUS_OFFSET);
5462306a36Sopenharmony_ci
5562306a36Sopenharmony_ci	dev_err(hdev->dev, "Decoder abnormal interrupt %#x, core %d\n", irq_status, dec->core_id);
5662306a36Sopenharmony_ci
5762306a36Sopenharmony_ci	dec_print_abnrm_intr_source(hdev, irq_status);
5862306a36Sopenharmony_ci
5962306a36Sopenharmony_ci	/* Clear the interrupt */
6062306a36Sopenharmony_ci	WREG32(dec->base_addr + VCMD_IRQ_STATUS_OFFSET, irq_status);
6162306a36Sopenharmony_ci
6262306a36Sopenharmony_ci	/* Flush the interrupt clear */
6362306a36Sopenharmony_ci	RREG32(dec->base_addr + VCMD_IRQ_STATUS_OFFSET);
6462306a36Sopenharmony_ci
6562306a36Sopenharmony_ci	if (irq_status & VCMD_IRQ_STATUS_TIMEOUT_MASK) {
6662306a36Sopenharmony_ci		reset_required = true;
6762306a36Sopenharmony_ci		event_mask |= HL_NOTIFIER_EVENT_GENERAL_HW_ERR;
6862306a36Sopenharmony_ci	}
6962306a36Sopenharmony_ci
7062306a36Sopenharmony_ci	if (irq_status & VCMD_IRQ_STATUS_CMDERR_MASK)
7162306a36Sopenharmony_ci		event_mask |= HL_NOTIFIER_EVENT_UNDEFINED_OPCODE;
7262306a36Sopenharmony_ci
7362306a36Sopenharmony_ci	if (irq_status & (VCMD_IRQ_STATUS_ENDCMD_MASK |
7462306a36Sopenharmony_ci				VCMD_IRQ_STATUS_BUSERR_MASK |
7562306a36Sopenharmony_ci				VCMD_IRQ_STATUS_ABORT_MASK))
7662306a36Sopenharmony_ci		event_mask |= HL_NOTIFIER_EVENT_USER_ENGINE_ERR;
7762306a36Sopenharmony_ci
7862306a36Sopenharmony_ci	if (reset_required) {
7962306a36Sopenharmony_ci		event_mask |= HL_NOTIFIER_EVENT_DEVICE_RESET;
8062306a36Sopenharmony_ci		hl_device_cond_reset(hdev, 0, event_mask);
8162306a36Sopenharmony_ci	} else if (event_mask) {
8262306a36Sopenharmony_ci		hl_notifier_event_send_all(hdev, event_mask);
8362306a36Sopenharmony_ci	}
8462306a36Sopenharmony_ci}
8562306a36Sopenharmony_ci
8662306a36Sopenharmony_civoid hl_dec_fini(struct hl_device *hdev)
8762306a36Sopenharmony_ci{
8862306a36Sopenharmony_ci	kfree(hdev->dec);
8962306a36Sopenharmony_ci}
9062306a36Sopenharmony_ci
9162306a36Sopenharmony_ciint hl_dec_init(struct hl_device *hdev)
9262306a36Sopenharmony_ci{
9362306a36Sopenharmony_ci	struct asic_fixed_properties *prop = &hdev->asic_prop;
9462306a36Sopenharmony_ci	struct hl_dec *dec;
9562306a36Sopenharmony_ci	int rc, j;
9662306a36Sopenharmony_ci
9762306a36Sopenharmony_ci	/* if max core is 0, nothing to do*/
9862306a36Sopenharmony_ci	if (!prop->max_dec)
9962306a36Sopenharmony_ci		return 0;
10062306a36Sopenharmony_ci
10162306a36Sopenharmony_ci	hdev->dec = kcalloc(prop->max_dec, sizeof(struct hl_dec), GFP_KERNEL);
10262306a36Sopenharmony_ci	if (!hdev->dec)
10362306a36Sopenharmony_ci		return -ENOMEM;
10462306a36Sopenharmony_ci
10562306a36Sopenharmony_ci	for (j = 0 ; j < prop->max_dec ; j++) {
10662306a36Sopenharmony_ci		dec = hdev->dec + j;
10762306a36Sopenharmony_ci
10862306a36Sopenharmony_ci		dec->hdev = hdev;
10962306a36Sopenharmony_ci		INIT_WORK(&dec->abnrm_intr_work, dec_abnrm_intr_work);
11062306a36Sopenharmony_ci		dec->core_id = j;
11162306a36Sopenharmony_ci		dec->base_addr = hdev->asic_funcs->get_dec_base_addr(hdev, j);
11262306a36Sopenharmony_ci		if (!dec->base_addr) {
11362306a36Sopenharmony_ci			dev_err(hdev->dev, "Invalid base address of decoder %d\n", j);
11462306a36Sopenharmony_ci			rc = -EINVAL;
11562306a36Sopenharmony_ci			goto err_dec_fini;
11662306a36Sopenharmony_ci		}
11762306a36Sopenharmony_ci	}
11862306a36Sopenharmony_ci
11962306a36Sopenharmony_ci	return 0;
12062306a36Sopenharmony_ci
12162306a36Sopenharmony_cierr_dec_fini:
12262306a36Sopenharmony_ci	hl_dec_fini(hdev);
12362306a36Sopenharmony_ci
12462306a36Sopenharmony_ci	return rc;
12562306a36Sopenharmony_ci}
12662306a36Sopenharmony_ci
12762306a36Sopenharmony_civoid hl_dec_ctx_fini(struct hl_ctx *ctx)
12862306a36Sopenharmony_ci{
12962306a36Sopenharmony_ci	struct hl_device *hdev = ctx->hdev;
13062306a36Sopenharmony_ci	struct asic_fixed_properties *prop = &hdev->asic_prop;
13162306a36Sopenharmony_ci	struct hl_dec *dec;
13262306a36Sopenharmony_ci	int j;
13362306a36Sopenharmony_ci
13462306a36Sopenharmony_ci	for (j = 0 ; j < prop->max_dec ; j++) {
13562306a36Sopenharmony_ci		if (!!(prop->decoder_enabled_mask & BIT(j))) {
13662306a36Sopenharmony_ci			dec = hdev->dec + j;
13762306a36Sopenharmony_ci			/* Stop the decoder */
13862306a36Sopenharmony_ci			WREG32(dec->base_addr + VCMD_CONTROL_OFFSET, 0);
13962306a36Sopenharmony_ci		}
14062306a36Sopenharmony_ci	}
14162306a36Sopenharmony_ci}
142