| // 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 = kzalloc_objs(struct hl_dec, prop->max_dec); | |
| 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); | |
| } | |
| } | |
| } |