1 | // SPDX-License-Identifier: MIT |
2 | /* |
3 | * Copyright(c) 2020 Intel Corporation. |
4 | */ |
5 | #include <linux/workqueue.h> |
6 | |
7 | #include "gt/intel_gt_irq.h" |
8 | #include "gt/intel_gt_regs.h" |
9 | #include "gt/intel_gt_types.h" |
10 | |
11 | #include "i915_irq.h" |
12 | #include "i915_reg.h" |
13 | |
14 | #include "intel_pxp.h" |
15 | #include "intel_pxp_irq.h" |
16 | #include "intel_pxp_session.h" |
17 | #include "intel_pxp_types.h" |
18 | #include "intel_runtime_pm.h" |
19 | |
20 | /** |
21 | * intel_pxp_irq_handler - Handles PXP interrupts. |
22 | * @pxp: pointer to pxp struct |
23 | * @iir: interrupt vector |
24 | */ |
25 | void intel_pxp_irq_handler(struct intel_pxp *pxp, u16 iir) |
26 | { |
27 | struct intel_gt *gt; |
28 | |
29 | if (GEM_WARN_ON(!intel_pxp_is_enabled(pxp))) |
30 | return; |
31 | |
32 | gt = pxp->ctrl_gt; |
33 | |
34 | lockdep_assert_held(gt->irq_lock); |
35 | |
36 | if (unlikely(!iir)) |
37 | return; |
38 | |
39 | if (iir & (GEN12_DISPLAY_PXP_STATE_TERMINATED_INTERRUPT | |
40 | GEN12_DISPLAY_APP_TERMINATED_PER_FW_REQ_INTERRUPT)) { |
41 | /* immediately mark PXP as inactive on termination */ |
42 | intel_pxp_mark_termination_in_progress(pxp); |
43 | pxp->session_events |= PXP_TERMINATION_REQUEST | PXP_INVAL_REQUIRED | |
44 | PXP_EVENT_TYPE_IRQ; |
45 | } |
46 | |
47 | if (iir & GEN12_DISPLAY_STATE_RESET_COMPLETE_INTERRUPT) |
48 | pxp->session_events |= PXP_TERMINATION_COMPLETE | PXP_EVENT_TYPE_IRQ; |
49 | |
50 | if (pxp->session_events) |
51 | queue_work(wq: system_unbound_wq, work: &pxp->session_work); |
52 | } |
53 | |
54 | static inline void __pxp_set_interrupts(struct intel_gt *gt, u32 interrupts) |
55 | { |
56 | struct intel_uncore *uncore = gt->uncore; |
57 | const u32 mask = interrupts << 16; |
58 | |
59 | intel_uncore_write(uncore, GEN11_CRYPTO_RSVD_INTR_ENABLE, val: mask); |
60 | intel_uncore_write(uncore, GEN11_CRYPTO_RSVD_INTR_MASK, val: ~mask); |
61 | } |
62 | |
63 | static inline void pxp_irq_reset(struct intel_gt *gt) |
64 | { |
65 | spin_lock_irq(lock: gt->irq_lock); |
66 | gen11_gt_reset_one_iir(gt, bank: 0, GEN11_KCR); |
67 | spin_unlock_irq(lock: gt->irq_lock); |
68 | } |
69 | |
70 | void intel_pxp_irq_enable(struct intel_pxp *pxp) |
71 | { |
72 | struct intel_gt *gt = pxp->ctrl_gt; |
73 | |
74 | spin_lock_irq(lock: gt->irq_lock); |
75 | |
76 | if (!pxp->irq_enabled) |
77 | WARN_ON_ONCE(gen11_gt_reset_one_iir(gt, 0, GEN11_KCR)); |
78 | |
79 | __pxp_set_interrupts(gt, GEN12_PXP_INTERRUPTS); |
80 | pxp->irq_enabled = true; |
81 | |
82 | spin_unlock_irq(lock: gt->irq_lock); |
83 | } |
84 | |
85 | void intel_pxp_irq_disable(struct intel_pxp *pxp) |
86 | { |
87 | struct intel_gt *gt = pxp->ctrl_gt; |
88 | |
89 | /* |
90 | * We always need to submit a global termination when we re-enable the |
91 | * interrupts, so there is no need to make sure that the session state |
92 | * makes sense at the end of this function. Just make sure this is not |
93 | * called in a path were the driver consider the session as valid and |
94 | * doesn't call a termination on restart. |
95 | */ |
96 | GEM_WARN_ON(intel_pxp_is_active(pxp)); |
97 | |
98 | spin_lock_irq(lock: gt->irq_lock); |
99 | |
100 | pxp->irq_enabled = false; |
101 | __pxp_set_interrupts(gt, interrupts: 0); |
102 | |
103 | spin_unlock_irq(lock: gt->irq_lock); |
104 | intel_synchronize_irq(i915: gt->i915); |
105 | |
106 | pxp_irq_reset(gt); |
107 | |
108 | flush_work(work: &pxp->session_work); |
109 | } |
110 | |