Linux kernel mirror (for testing)
git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel
os
linux
1// SPDX-License-Identifier: MIT
2/*
3 * Copyright(c) 2025, Intel Corporation. All rights reserved.
4 */
5
6#include "regs/xe_irq_regs.h"
7#include "regs/xe_mert_regs.h"
8
9#include "xe_device.h"
10#include "xe_mert.h"
11#include "xe_mmio.h"
12#include "xe_sriov_printk.h"
13#include "xe_tile.h"
14
15/**
16 * xe_mert_init_early() - Initialize MERT data
17 * @xe: the &xe_device with MERT to init
18 */
19void xe_mert_init_early(struct xe_device *xe)
20{
21 struct xe_tile *tile = xe_device_get_root_tile(xe);
22 struct xe_mert *mert = &tile->mert;
23
24 spin_lock_init(&mert->lock);
25 init_completion(&mert->tlb_inv_done);
26}
27
28/**
29 * xe_mert_invalidate_lmtt() - Invalidate MERT LMTT
30 * @xe: the &xe_device with MERT
31 *
32 * Trigger invalidation of the MERT LMTT and wait for completion.
33 *
34 * Return: 0 on success or -ETIMEDOUT in case of a timeout.
35 */
36int xe_mert_invalidate_lmtt(struct xe_device *xe)
37{
38 struct xe_tile *tile = xe_device_get_root_tile(xe);
39 struct xe_mert *mert = &tile->mert;
40 const long timeout = HZ / 4;
41 unsigned long flags;
42
43 xe_assert(xe, xe_device_has_mert(xe));
44
45 spin_lock_irqsave(&mert->lock, flags);
46 if (!mert->tlb_inv_triggered) {
47 mert->tlb_inv_triggered = true;
48 reinit_completion(&mert->tlb_inv_done);
49 xe_mmio_write32(&tile->mmio, MERT_TLB_INV_DESC_A, MERT_TLB_INV_DESC_A_VALID);
50 }
51 spin_unlock_irqrestore(&mert->lock, flags);
52
53 if (!wait_for_completion_timeout(&mert->tlb_inv_done, timeout))
54 return -ETIMEDOUT;
55
56 return 0;
57}
58
59static void mert_handle_cat_error(struct xe_device *xe)
60{
61 struct xe_tile *tile = xe_device_get_root_tile(xe);
62 u32 reg_val, vfid, code;
63
64 reg_val = xe_mmio_read32(&tile->mmio, MERT_TLB_CT_INTR_ERR_ID_PORT);
65 if (!reg_val)
66 return;
67 xe_mmio_write32(&tile->mmio, MERT_TLB_CT_INTR_ERR_ID_PORT, 0);
68
69 vfid = FIELD_GET(CATERR_VFID, reg_val);
70 code = FIELD_GET(CATERR_CODES, reg_val);
71
72 switch (code) {
73 case CATERR_NO_ERROR:
74 break;
75 case CATERR_UNMAPPED_GGTT:
76 xe_sriov_err(xe, "MERT: CAT_ERR: Access to an unmapped GGTT!\n");
77 xe_device_declare_wedged(xe);
78 break;
79 case CATERR_LMTT_FAULT:
80 xe_sriov_dbg(xe, "MERT: CAT_ERR: VF%u LMTT fault!\n", vfid);
81 /* XXX: track/report malicious VF activity */
82 break;
83 default:
84 xe_sriov_err(xe, "MERT: Unexpected CAT_ERR code=%#x!\n", code);
85 xe_device_declare_wedged(xe);
86 break;
87 }
88}
89
90/**
91 * xe_mert_irq_handler - Handler for MERT interrupts
92 * @xe: the &xe_device
93 * @master_ctl: interrupt register
94 *
95 * Handle interrupts generated by MERT.
96 */
97void xe_mert_irq_handler(struct xe_device *xe, u32 master_ctl)
98{
99 struct xe_tile *tile = xe_device_get_root_tile(xe);
100 struct xe_mert *mert = &tile->mert;
101 unsigned long flags;
102 u32 reg_val;
103
104 if (!(master_ctl & SOC_H2DMEMINT_IRQ))
105 return;
106
107 mert_handle_cat_error(xe);
108
109 spin_lock_irqsave(&mert->lock, flags);
110 if (mert->tlb_inv_triggered) {
111 reg_val = xe_mmio_read32(&tile->mmio, MERT_TLB_INV_DESC_A);
112 if (!(reg_val & MERT_TLB_INV_DESC_A_VALID)) {
113 mert->tlb_inv_triggered = false;
114 complete_all(&mert->tlb_inv_done);
115 }
116 }
117 spin_unlock_irqrestore(&mert->lock, flags);
118}