| // SPDX-License-Identifier: MIT | |
| /* | |
| * Copyright(c) 2025, Intel Corporation. All rights reserved. | |
| */ | |
| #include "regs/xe_irq_regs.h" | |
| #include "regs/xe_mert_regs.h" | |
| #include "xe_device.h" | |
| #include "xe_mert.h" | |
| #include "xe_mmio.h" | |
| #include "xe_sriov_printk.h" | |
| #include "xe_tile.h" | |
| /** | |
| * xe_mert_init_early() - Initialize MERT data | |
| * @xe: the &xe_device with MERT to init | |
| */ | |
| void xe_mert_init_early(struct xe_device *xe) | |
| { | |
| struct xe_tile *tile = xe_device_get_root_tile(xe); | |
| struct xe_mert *mert = &tile->mert; | |
| spin_lock_init(&mert->lock); | |
| init_completion(&mert->tlb_inv_done); | |
| } | |
| /** | |
| * xe_mert_invalidate_lmtt() - Invalidate MERT LMTT | |
| * @xe: the &xe_device with MERT | |
| * | |
| * Trigger invalidation of the MERT LMTT and wait for completion. | |
| * | |
| * Return: 0 on success or -ETIMEDOUT in case of a timeout. | |
| */ | |
| int xe_mert_invalidate_lmtt(struct xe_device *xe) | |
| { | |
| struct xe_tile *tile = xe_device_get_root_tile(xe); | |
| struct xe_mert *mert = &tile->mert; | |
| const long timeout = HZ / 4; | |
| unsigned long flags; | |
| xe_assert(xe, xe_device_has_mert(xe)); | |
| spin_lock_irqsave(&mert->lock, flags); | |
| if (!mert->tlb_inv_triggered) { | |
| mert->tlb_inv_triggered = true; | |
| reinit_completion(&mert->tlb_inv_done); | |
| xe_mmio_write32(&tile->mmio, MERT_TLB_INV_DESC_A, MERT_TLB_INV_DESC_A_VALID); | |
| } | |
| spin_unlock_irqrestore(&mert->lock, flags); | |
| if (!wait_for_completion_timeout(&mert->tlb_inv_done, timeout)) | |
| return -ETIMEDOUT; | |
| return 0; | |
| } | |
| static void mert_handle_cat_error(struct xe_device *xe) | |
| { | |
| struct xe_tile *tile = xe_device_get_root_tile(xe); | |
| u32 reg_val, vfid, code; | |
| reg_val = xe_mmio_read32(&tile->mmio, MERT_TLB_CT_INTR_ERR_ID_PORT); | |
| if (!reg_val) | |
| return; | |
| xe_mmio_write32(&tile->mmio, MERT_TLB_CT_INTR_ERR_ID_PORT, 0); | |
| vfid = FIELD_GET(CATERR_VFID, reg_val); | |
| code = FIELD_GET(CATERR_CODES, reg_val); | |
| switch (code) { | |
| case CATERR_NO_ERROR: | |
| break; | |
| case CATERR_UNMAPPED_GGTT: | |
| xe_sriov_err(xe, "MERT: CAT_ERR: Access to an unmapped GGTT!\n"); | |
| xe_device_declare_wedged(xe); | |
| break; | |
| case CATERR_LMTT_FAULT: | |
| xe_sriov_dbg(xe, "MERT: CAT_ERR: VF%u LMTT fault!\n", vfid); | |
| /* XXX: track/report malicious VF activity */ | |
| break; | |
| default: | |
| xe_sriov_err(xe, "MERT: Unexpected CAT_ERR code=%#x!\n", code); | |
| xe_device_declare_wedged(xe); | |
| break; | |
| } | |
| } | |
| /** | |
| * xe_mert_irq_handler - Handler for MERT interrupts | |
| * @xe: the &xe_device | |
| * @master_ctl: interrupt register | |
| * | |
| * Handle interrupts generated by MERT. | |
| */ | |
| void xe_mert_irq_handler(struct xe_device *xe, u32 master_ctl) | |
| { | |
| struct xe_tile *tile = xe_device_get_root_tile(xe); | |
| struct xe_mert *mert = &tile->mert; | |
| unsigned long flags; | |
| u32 reg_val; | |
| if (!(master_ctl & SOC_H2DMEMINT_IRQ)) | |
| return; | |
| mert_handle_cat_error(xe); | |
| spin_lock_irqsave(&mert->lock, flags); | |
| if (mert->tlb_inv_triggered) { | |
| reg_val = xe_mmio_read32(&tile->mmio, MERT_TLB_INV_DESC_A); | |
| if (!(reg_val & MERT_TLB_INV_DESC_A_VALID)) { | |
| mert->tlb_inv_triggered = false; | |
| complete_all(&mert->tlb_inv_done); | |
| } | |
| } | |
| spin_unlock_irqrestore(&mert->lock, flags); | |
| } |