1 | // SPDX-License-Identifier: MIT |
2 | /* |
3 | * Copyright(c) 2019-2022, Intel Corporation. All rights reserved. |
4 | */ |
5 | |
6 | #include <linux/irq.h> |
7 | #include <linux/mei_aux.h> |
8 | #include "i915_drv.h" |
9 | #include "i915_reg.h" |
10 | #include "gem/i915_gem_lmem.h" |
11 | #include "gem/i915_gem_region.h" |
12 | #include "gt/intel_gsc.h" |
13 | #include "gt/intel_gt.h" |
14 | #include "gt/intel_gt_print.h" |
15 | |
16 | #define GSC_BAR_LENGTH 0x00000FFC |
17 | |
18 | static void gsc_irq_mask(struct irq_data *d) |
19 | { |
20 | /* generic irq handling */ |
21 | } |
22 | |
23 | static void gsc_irq_unmask(struct irq_data *d) |
24 | { |
25 | /* generic irq handling */ |
26 | } |
27 | |
28 | static struct irq_chip gsc_irq_chip = { |
29 | .name = "gsc_irq_chip" , |
30 | .irq_mask = gsc_irq_mask, |
31 | .irq_unmask = gsc_irq_unmask, |
32 | }; |
33 | |
34 | static int gsc_irq_init(int irq) |
35 | { |
36 | irq_set_chip_and_handler_name(irq, chip: &gsc_irq_chip, |
37 | handle: handle_simple_irq, name: "gsc_irq_handler" ); |
38 | |
39 | return irq_set_chip_data(irq, NULL); |
40 | } |
41 | |
42 | static int |
43 | gsc_ext_om_alloc(struct intel_gsc *gsc, struct intel_gsc_intf *intf, size_t size) |
44 | { |
45 | struct intel_gt *gt = gsc_to_gt(gsc); |
46 | struct drm_i915_gem_object *obj; |
47 | int err; |
48 | |
49 | obj = i915_gem_object_create_lmem(i915: gt->i915, size, |
50 | I915_BO_ALLOC_CONTIGUOUS | |
51 | I915_BO_ALLOC_CPU_CLEAR); |
52 | if (IS_ERR(ptr: obj)) { |
53 | gt_err(gt, "Failed to allocate gsc memory\n" ); |
54 | return PTR_ERR(ptr: obj); |
55 | } |
56 | |
57 | err = i915_gem_object_pin_pages_unlocked(obj); |
58 | if (err) { |
59 | gt_err(gt, "Failed to pin pages for gsc memory\n" ); |
60 | goto out_put; |
61 | } |
62 | |
63 | intf->gem_obj = obj; |
64 | |
65 | return 0; |
66 | |
67 | out_put: |
68 | i915_gem_object_put(obj); |
69 | return err; |
70 | } |
71 | |
72 | static void gsc_ext_om_destroy(struct intel_gsc_intf *intf) |
73 | { |
74 | struct drm_i915_gem_object *obj = fetch_and_zero(&intf->gem_obj); |
75 | |
76 | if (!obj) |
77 | return; |
78 | |
79 | if (i915_gem_object_has_pinned_pages(obj)) |
80 | i915_gem_object_unpin_pages(obj); |
81 | |
82 | i915_gem_object_put(obj); |
83 | } |
84 | |
85 | struct gsc_def { |
86 | const char *name; |
87 | unsigned long bar; |
88 | size_t bar_size; |
89 | bool use_polling; |
90 | bool slow_firmware; |
91 | size_t lmem_size; |
92 | }; |
93 | |
94 | /* gsc resources and definitions (HECI1 and HECI2) */ |
95 | static const struct gsc_def gsc_def_dg1[] = { |
96 | { |
97 | /* HECI1 not yet implemented. */ |
98 | }, |
99 | { |
100 | .name = "mei-gscfi" , |
101 | .bar = DG1_GSC_HECI2_BASE, |
102 | .bar_size = GSC_BAR_LENGTH, |
103 | } |
104 | }; |
105 | |
106 | static const struct gsc_def gsc_def_xehpsdv[] = { |
107 | { |
108 | /* HECI1 not enabled on the device. */ |
109 | }, |
110 | { |
111 | .name = "mei-gscfi" , |
112 | .bar = DG1_GSC_HECI2_BASE, |
113 | .bar_size = GSC_BAR_LENGTH, |
114 | .use_polling = true, |
115 | .slow_firmware = true, |
116 | } |
117 | }; |
118 | |
119 | static const struct gsc_def gsc_def_dg2[] = { |
120 | { |
121 | .name = "mei-gsc" , |
122 | .bar = DG2_GSC_HECI1_BASE, |
123 | .bar_size = GSC_BAR_LENGTH, |
124 | .lmem_size = SZ_4M, |
125 | }, |
126 | { |
127 | .name = "mei-gscfi" , |
128 | .bar = DG2_GSC_HECI2_BASE, |
129 | .bar_size = GSC_BAR_LENGTH, |
130 | } |
131 | }; |
132 | |
133 | static void gsc_release_dev(struct device *dev) |
134 | { |
135 | struct auxiliary_device *aux_dev = to_auxiliary_dev(dev); |
136 | struct mei_aux_device *adev = auxiliary_dev_to_mei_aux_dev(aux_dev); |
137 | |
138 | kfree(objp: adev); |
139 | } |
140 | |
141 | static void gsc_destroy_one(struct drm_i915_private *i915, |
142 | struct intel_gsc *gsc, unsigned int intf_id) |
143 | { |
144 | struct intel_gsc_intf *intf = &gsc->intf[intf_id]; |
145 | |
146 | if (intf->adev) { |
147 | struct auxiliary_device *aux_dev = &intf->adev->aux_dev; |
148 | |
149 | if (intf_id == 0) |
150 | intel_huc_unregister_gsc_notifier(huc: &gsc_to_gt(gsc)->uc.huc, |
151 | bus: aux_dev->dev.bus); |
152 | |
153 | auxiliary_device_delete(auxdev: aux_dev); |
154 | auxiliary_device_uninit(auxdev: aux_dev); |
155 | intf->adev = NULL; |
156 | } |
157 | |
158 | if (intf->irq >= 0) |
159 | irq_free_desc(irq: intf->irq); |
160 | intf->irq = -1; |
161 | |
162 | gsc_ext_om_destroy(intf); |
163 | } |
164 | |
165 | static void gsc_init_one(struct drm_i915_private *i915, struct intel_gsc *gsc, |
166 | unsigned int intf_id) |
167 | { |
168 | struct pci_dev *pdev = to_pci_dev(i915->drm.dev); |
169 | struct mei_aux_device *adev; |
170 | struct auxiliary_device *aux_dev; |
171 | const struct gsc_def *def; |
172 | struct intel_gsc_intf *intf = &gsc->intf[intf_id]; |
173 | int ret; |
174 | |
175 | intf->irq = -1; |
176 | intf->id = intf_id; |
177 | |
178 | /* |
179 | * On the multi-tile setups the GSC is functional on the first tile only |
180 | */ |
181 | if (gsc_to_gt(gsc)->info.id != 0) { |
182 | drm_dbg(&i915->drm, "Not initializing gsc for remote tiles\n" ); |
183 | return; |
184 | } |
185 | |
186 | if (intf_id == 0 && !HAS_HECI_PXP(i915)) |
187 | return; |
188 | |
189 | if (IS_DG1(i915)) { |
190 | def = &gsc_def_dg1[intf_id]; |
191 | } else if (IS_XEHPSDV(i915)) { |
192 | def = &gsc_def_xehpsdv[intf_id]; |
193 | } else if (IS_DG2(i915)) { |
194 | def = &gsc_def_dg2[intf_id]; |
195 | } else { |
196 | drm_warn_once(&i915->drm, "Unknown platform\n" ); |
197 | return; |
198 | } |
199 | |
200 | if (!def->name) { |
201 | drm_warn_once(&i915->drm, "HECI%d is not implemented!\n" , intf_id + 1); |
202 | return; |
203 | } |
204 | |
205 | /* skip irq initialization */ |
206 | if (def->use_polling) |
207 | goto add_device; |
208 | |
209 | intf->irq = irq_alloc_desc(0); |
210 | if (intf->irq < 0) { |
211 | drm_err(&i915->drm, "gsc irq error %d\n" , intf->irq); |
212 | goto fail; |
213 | } |
214 | |
215 | ret = gsc_irq_init(irq: intf->irq); |
216 | if (ret < 0) { |
217 | drm_err(&i915->drm, "gsc irq init failed %d\n" , ret); |
218 | goto fail; |
219 | } |
220 | |
221 | add_device: |
222 | adev = kzalloc(size: sizeof(*adev), GFP_KERNEL); |
223 | if (!adev) |
224 | goto fail; |
225 | |
226 | if (def->lmem_size) { |
227 | drm_dbg(&i915->drm, "setting up GSC lmem\n" ); |
228 | |
229 | if (gsc_ext_om_alloc(gsc, intf, size: def->lmem_size)) { |
230 | drm_err(&i915->drm, "setting up gsc extended operational memory failed\n" ); |
231 | kfree(objp: adev); |
232 | goto fail; |
233 | } |
234 | |
235 | adev->ext_op_mem.start = i915_gem_object_get_dma_address(intf->gem_obj, 0); |
236 | adev->ext_op_mem.end = adev->ext_op_mem.start + def->lmem_size; |
237 | } |
238 | |
239 | adev->irq = intf->irq; |
240 | adev->bar.parent = &pdev->resource[0]; |
241 | adev->bar.start = def->bar + pdev->resource[0].start; |
242 | adev->bar.end = adev->bar.start + def->bar_size - 1; |
243 | adev->bar.flags = IORESOURCE_MEM; |
244 | adev->bar.desc = IORES_DESC_NONE; |
245 | adev->slow_firmware = def->slow_firmware; |
246 | |
247 | aux_dev = &adev->aux_dev; |
248 | aux_dev->name = def->name; |
249 | aux_dev->id = (pci_domain_nr(bus: pdev->bus) << 16) | |
250 | PCI_DEVID(pdev->bus->number, pdev->devfn); |
251 | aux_dev->dev.parent = &pdev->dev; |
252 | aux_dev->dev.release = gsc_release_dev; |
253 | |
254 | ret = auxiliary_device_init(auxdev: aux_dev); |
255 | if (ret < 0) { |
256 | drm_err(&i915->drm, "gsc aux init failed %d\n" , ret); |
257 | kfree(objp: adev); |
258 | goto fail; |
259 | } |
260 | |
261 | intf->adev = adev; /* needed by the notifier */ |
262 | |
263 | if (intf_id == 0) |
264 | intel_huc_register_gsc_notifier(huc: &gsc_to_gt(gsc)->uc.huc, |
265 | bus: aux_dev->dev.bus); |
266 | |
267 | ret = auxiliary_device_add(aux_dev); |
268 | if (ret < 0) { |
269 | drm_err(&i915->drm, "gsc aux add failed %d\n" , ret); |
270 | if (intf_id == 0) |
271 | intel_huc_unregister_gsc_notifier(huc: &gsc_to_gt(gsc)->uc.huc, |
272 | bus: aux_dev->dev.bus); |
273 | intf->adev = NULL; |
274 | |
275 | /* adev will be freed with the put_device() and .release sequence */ |
276 | auxiliary_device_uninit(auxdev: aux_dev); |
277 | goto fail; |
278 | } |
279 | |
280 | return; |
281 | fail: |
282 | gsc_destroy_one(i915, gsc, intf_id: intf->id); |
283 | } |
284 | |
285 | static void gsc_irq_handler(struct intel_gt *gt, unsigned int intf_id) |
286 | { |
287 | int ret; |
288 | |
289 | if (intf_id >= INTEL_GSC_NUM_INTERFACES) { |
290 | gt_warn_once(gt, "GSC irq: intf_id %d is out of range" , intf_id); |
291 | return; |
292 | } |
293 | |
294 | if (!HAS_HECI_GSC(gt->i915)) { |
295 | gt_warn_once(gt, "GSC irq: not supported" ); |
296 | return; |
297 | } |
298 | |
299 | if (gt->gsc.intf[intf_id].irq < 0) |
300 | return; |
301 | |
302 | ret = generic_handle_irq(irq: gt->gsc.intf[intf_id].irq); |
303 | if (ret) |
304 | gt_err_ratelimited(gt, "error handling GSC irq: %d\n" , ret); |
305 | } |
306 | |
307 | void intel_gsc_irq_handler(struct intel_gt *gt, u32 iir) |
308 | { |
309 | if (iir & GSC_IRQ_INTF(0)) |
310 | gsc_irq_handler(gt, intf_id: 0); |
311 | if (iir & GSC_IRQ_INTF(1)) |
312 | gsc_irq_handler(gt, intf_id: 1); |
313 | } |
314 | |
315 | void intel_gsc_init(struct intel_gsc *gsc, struct drm_i915_private *i915) |
316 | { |
317 | unsigned int i; |
318 | |
319 | if (!HAS_HECI_GSC(i915)) |
320 | return; |
321 | |
322 | for (i = 0; i < INTEL_GSC_NUM_INTERFACES; i++) |
323 | gsc_init_one(i915, gsc, intf_id: i); |
324 | } |
325 | |
326 | void intel_gsc_fini(struct intel_gsc *gsc) |
327 | { |
328 | struct intel_gt *gt = gsc_to_gt(gsc); |
329 | unsigned int i; |
330 | |
331 | if (!HAS_HECI_GSC(gt->i915)) |
332 | return; |
333 | |
334 | for (i = 0; i < INTEL_GSC_NUM_INTERFACES; i++) |
335 | gsc_destroy_one(i915: gt->i915, gsc, intf_id: i); |
336 | } |
337 | |