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) 2023, Intel Corporation. All rights reserved.
4 */
5
6#include <linux/irq.h>
7#include <linux/mei_aux.h>
8#include <linux/pci.h>
9#include <linux/sizes.h>
10
11#include "xe_device_types.h"
12#include "xe_drv.h"
13#include "xe_heci_gsc.h"
14#include "regs/xe_gsc_regs.h"
15#include "xe_platform_types.h"
16#include "xe_survivability_mode.h"
17
18#define GSC_BAR_LENGTH 0x00000FFC
19
20static void heci_gsc_irq_mask(struct irq_data *d)
21{
22 /* generic irq handling */
23}
24
25static void heci_gsc_irq_unmask(struct irq_data *d)
26{
27 /* generic irq handling */
28}
29
30static const struct irq_chip heci_gsc_irq_chip = {
31 .name = "gsc_irq_chip",
32 .irq_mask = heci_gsc_irq_mask,
33 .irq_unmask = heci_gsc_irq_unmask,
34};
35
36static int heci_gsc_irq_init(int irq)
37{
38 irq_set_chip_and_handler_name(irq, &heci_gsc_irq_chip,
39 handle_simple_irq, "heci_gsc_irq_handler");
40
41 return irq_set_chip_data(irq, NULL);
42}
43
44/**
45 * struct heci_gsc_def - graphics security controller heci interface definitions
46 *
47 * @name: name of the heci device
48 * @bar: address of the mmio bar
49 * @bar_size: size of the mmio bar
50 * @use_polling: indication of using polling mode for the device
51 * @slow_firmware: indication of whether the device is slow (needs longer timeouts)
52 */
53struct heci_gsc_def {
54 const char *name;
55 unsigned long bar;
56 size_t bar_size;
57 bool use_polling;
58 bool slow_firmware;
59};
60
61/* gsc resources and definitions */
62static const struct heci_gsc_def heci_gsc_def_dg1 = {
63 .name = "mei-gscfi",
64 .bar = DG1_GSC_HECI2_BASE,
65 .bar_size = GSC_BAR_LENGTH,
66};
67
68static const struct heci_gsc_def heci_gsc_def_dg2 = {
69 .name = "mei-gscfi",
70 .bar = DG2_GSC_HECI2_BASE,
71 .bar_size = GSC_BAR_LENGTH,
72};
73
74static const struct heci_gsc_def heci_gsc_def_pvc = {
75 .name = "mei-gscfi",
76 .bar = PVC_GSC_HECI2_BASE,
77 .bar_size = GSC_BAR_LENGTH,
78 .slow_firmware = true,
79};
80
81static void heci_gsc_release_dev(struct device *dev)
82{
83 struct auxiliary_device *aux_dev = to_auxiliary_dev(dev);
84 struct mei_aux_device *adev = auxiliary_dev_to_mei_aux_dev(aux_dev);
85
86 kfree(adev);
87}
88
89static void xe_heci_gsc_fini(void *arg)
90{
91 struct xe_heci_gsc *heci_gsc = arg;
92
93 if (heci_gsc->adev) {
94 struct auxiliary_device *aux_dev = &heci_gsc->adev->aux_dev;
95
96 auxiliary_device_delete(aux_dev);
97 auxiliary_device_uninit(aux_dev);
98 heci_gsc->adev = NULL;
99 }
100
101 if (heci_gsc->irq >= 0)
102 irq_free_desc(heci_gsc->irq);
103
104 heci_gsc->irq = -1;
105}
106
107static int heci_gsc_irq_setup(struct xe_device *xe)
108{
109 struct xe_heci_gsc *heci_gsc = &xe->heci_gsc;
110 int ret;
111
112 heci_gsc->irq = irq_alloc_desc(0);
113 if (heci_gsc->irq < 0) {
114 drm_err(&xe->drm, "gsc irq error %d\n", heci_gsc->irq);
115 return heci_gsc->irq;
116 }
117
118 ret = heci_gsc_irq_init(heci_gsc->irq);
119 if (ret < 0)
120 drm_err(&xe->drm, "gsc irq init failed %d\n", ret);
121
122 return ret;
123}
124
125static int heci_gsc_add_device(struct xe_device *xe, const struct heci_gsc_def *def)
126{
127 struct xe_heci_gsc *heci_gsc = &xe->heci_gsc;
128 struct pci_dev *pdev = to_pci_dev(xe->drm.dev);
129 struct auxiliary_device *aux_dev;
130 struct mei_aux_device *adev;
131 int ret;
132
133 adev = kzalloc(sizeof(*adev), GFP_KERNEL);
134 if (!adev)
135 return -ENOMEM;
136 adev->irq = heci_gsc->irq;
137 adev->bar.parent = &pdev->resource[0];
138 adev->bar.start = def->bar + pdev->resource[0].start;
139 adev->bar.end = adev->bar.start + def->bar_size - 1;
140 adev->bar.flags = IORESOURCE_MEM;
141 adev->bar.desc = IORES_DESC_NONE;
142 adev->slow_firmware = def->slow_firmware;
143
144 aux_dev = &adev->aux_dev;
145 aux_dev->name = def->name;
146 aux_dev->id = (pci_domain_nr(pdev->bus) << 16) |
147 PCI_DEVID(pdev->bus->number, pdev->devfn);
148 aux_dev->dev.parent = &pdev->dev;
149 aux_dev->dev.release = heci_gsc_release_dev;
150
151 ret = auxiliary_device_init(aux_dev);
152 if (ret < 0) {
153 drm_err(&xe->drm, "gsc aux init failed %d\n", ret);
154 kfree(adev);
155 return ret;
156 }
157
158 heci_gsc->adev = adev; /* needed by the notifier */
159 ret = auxiliary_device_add(aux_dev);
160 if (ret < 0) {
161 drm_err(&xe->drm, "gsc aux add failed %d\n", ret);
162 heci_gsc->adev = NULL;
163
164 /* adev will be freed with the put_device() and .release sequence */
165 auxiliary_device_uninit(aux_dev);
166 }
167 return ret;
168}
169
170int xe_heci_gsc_init(struct xe_device *xe)
171{
172 struct xe_heci_gsc *heci_gsc = &xe->heci_gsc;
173 const struct heci_gsc_def *def = NULL;
174 int ret;
175
176 if (!xe->info.has_heci_gscfi && !xe->info.has_heci_cscfi)
177 return 0;
178
179 heci_gsc->irq = -1;
180
181 if (xe->info.platform == XE_BATTLEMAGE) {
182 def = &heci_gsc_def_dg2;
183 } else if (xe->info.platform == XE_PVC) {
184 def = &heci_gsc_def_pvc;
185 } else if (xe->info.platform == XE_DG2) {
186 def = &heci_gsc_def_dg2;
187 } else if (xe->info.platform == XE_DG1) {
188 def = &heci_gsc_def_dg1;
189 }
190
191 if (!def || !def->name) {
192 drm_warn(&xe->drm, "HECI is not implemented!\n");
193 return 0;
194 }
195
196 ret = devm_add_action_or_reset(xe->drm.dev, xe_heci_gsc_fini, heci_gsc);
197 if (ret)
198 return ret;
199
200 if (!def->use_polling && !xe_survivability_mode_is_enabled(xe)) {
201 ret = heci_gsc_irq_setup(xe);
202 if (ret)
203 return ret;
204 }
205
206 return heci_gsc_add_device(xe, def);
207}
208
209void xe_heci_gsc_irq_handler(struct xe_device *xe, u32 iir)
210{
211 int ret;
212
213 if ((iir & GSC_IRQ_INTF(1)) == 0)
214 return;
215
216 if (!xe->info.has_heci_gscfi) {
217 drm_warn_once(&xe->drm, "GSC irq: not supported");
218 return;
219 }
220
221 if (xe->heci_gsc.irq < 0)
222 return;
223
224 ret = generic_handle_irq(xe->heci_gsc.irq);
225 if (ret)
226 drm_err_ratelimited(&xe->drm, "error handling GSC irq: %d\n", ret);
227}
228
229void xe_heci_csc_irq_handler(struct xe_device *xe, u32 iir)
230{
231 int ret;
232
233 if ((iir & CSC_IRQ_INTF(1)) == 0)
234 return;
235
236 if (!xe->info.has_heci_cscfi) {
237 drm_warn_once(&xe->drm, "CSC irq: not supported");
238 return;
239 }
240
241 if (xe->heci_gsc.irq < 0)
242 return;
243
244 ret = generic_handle_irq(xe->heci_gsc.irq);
245 if (ret)
246 drm_err_ratelimited(&xe->drm, "error handling GSC irq: %d\n", ret);
247}