Commit | Line | Data |
---|---|---|
1e3dc1d8 TW |
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 "gt/intel_gsc.h" | |
11 | #include "gt/intel_gt.h" | |
12 | ||
13 | #define GSC_BAR_LENGTH 0x00000FFC | |
14 | ||
15 | static void gsc_irq_mask(struct irq_data *d) | |
16 | { | |
17 | /* generic irq handling */ | |
18 | } | |
19 | ||
20 | static void gsc_irq_unmask(struct irq_data *d) | |
21 | { | |
22 | /* generic irq handling */ | |
23 | } | |
24 | ||
25 | static struct irq_chip gsc_irq_chip = { | |
26 | .name = "gsc_irq_chip", | |
27 | .irq_mask = gsc_irq_mask, | |
28 | .irq_unmask = gsc_irq_unmask, | |
29 | }; | |
30 | ||
31 | static int gsc_irq_init(int irq) | |
32 | { | |
33 | irq_set_chip_and_handler_name(irq, &gsc_irq_chip, | |
34 | handle_simple_irq, "gsc_irq_handler"); | |
35 | ||
36 | return irq_set_chip_data(irq, NULL); | |
37 | } | |
38 | ||
39 | struct gsc_def { | |
40 | const char *name; | |
41 | unsigned long bar; | |
42 | size_t bar_size; | |
43 | }; | |
44 | ||
45 | /* gsc resources and definitions (HECI1 and HECI2) */ | |
46 | static const struct gsc_def gsc_def_dg1[] = { | |
47 | { | |
48 | /* HECI1 not yet implemented. */ | |
49 | }, | |
50 | { | |
51 | .name = "mei-gscfi", | |
52 | .bar = DG1_GSC_HECI2_BASE, | |
53 | .bar_size = GSC_BAR_LENGTH, | |
54 | } | |
55 | }; | |
56 | ||
f15856d7 TW |
57 | static const struct gsc_def gsc_def_dg2[] = { |
58 | { | |
59 | .name = "mei-gsc", | |
60 | .bar = DG2_GSC_HECI1_BASE, | |
61 | .bar_size = GSC_BAR_LENGTH, | |
62 | }, | |
63 | { | |
64 | .name = "mei-gscfi", | |
65 | .bar = DG2_GSC_HECI2_BASE, | |
66 | .bar_size = GSC_BAR_LENGTH, | |
67 | } | |
68 | }; | |
69 | ||
1e3dc1d8 TW |
70 | static void gsc_release_dev(struct device *dev) |
71 | { | |
72 | struct auxiliary_device *aux_dev = to_auxiliary_dev(dev); | |
73 | struct mei_aux_device *adev = auxiliary_dev_to_mei_aux_dev(aux_dev); | |
74 | ||
75 | kfree(adev); | |
76 | } | |
77 | ||
78 | static void gsc_destroy_one(struct intel_gsc_intf *intf) | |
79 | { | |
80 | if (intf->adev) { | |
81 | auxiliary_device_delete(&intf->adev->aux_dev); | |
82 | auxiliary_device_uninit(&intf->adev->aux_dev); | |
83 | intf->adev = NULL; | |
84 | } | |
85 | if (intf->irq >= 0) | |
86 | irq_free_desc(intf->irq); | |
87 | intf->irq = -1; | |
88 | } | |
89 | ||
90 | static void gsc_init_one(struct drm_i915_private *i915, | |
91 | struct intel_gsc_intf *intf, | |
92 | unsigned int intf_id) | |
93 | { | |
94 | struct pci_dev *pdev = to_pci_dev(i915->drm.dev); | |
95 | struct mei_aux_device *adev; | |
96 | struct auxiliary_device *aux_dev; | |
97 | const struct gsc_def *def; | |
98 | int ret; | |
99 | ||
100 | intf->irq = -1; | |
101 | intf->id = intf_id; | |
102 | ||
103 | if (intf_id == 0 && !HAS_HECI_PXP(i915)) | |
104 | return; | |
105 | ||
f15856d7 TW |
106 | if (IS_DG1(i915)) { |
107 | def = &gsc_def_dg1[intf_id]; | |
108 | } else if (IS_DG2(i915)) { | |
109 | def = &gsc_def_dg2[intf_id]; | |
110 | } else { | |
111 | drm_warn_once(&i915->drm, "Unknown platform\n"); | |
112 | return; | |
113 | } | |
1e3dc1d8 TW |
114 | |
115 | if (!def->name) { | |
116 | drm_warn_once(&i915->drm, "HECI%d is not implemented!\n", intf_id + 1); | |
117 | return; | |
118 | } | |
119 | ||
120 | intf->irq = irq_alloc_desc(0); | |
121 | if (intf->irq < 0) { | |
122 | drm_err(&i915->drm, "gsc irq error %d\n", intf->irq); | |
123 | return; | |
124 | } | |
125 | ||
126 | ret = gsc_irq_init(intf->irq); | |
127 | if (ret < 0) { | |
128 | drm_err(&i915->drm, "gsc irq init failed %d\n", ret); | |
129 | goto fail; | |
130 | } | |
131 | ||
132 | adev = kzalloc(sizeof(*adev), GFP_KERNEL); | |
133 | if (!adev) | |
134 | goto fail; | |
135 | ||
136 | adev->irq = intf->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 | ||
143 | aux_dev = &adev->aux_dev; | |
144 | aux_dev->name = def->name; | |
145 | aux_dev->id = (pci_domain_nr(pdev->bus) << 16) | | |
146 | PCI_DEVID(pdev->bus->number, pdev->devfn); | |
147 | aux_dev->dev.parent = &pdev->dev; | |
148 | aux_dev->dev.release = gsc_release_dev; | |
149 | ||
150 | ret = auxiliary_device_init(aux_dev); | |
151 | if (ret < 0) { | |
152 | drm_err(&i915->drm, "gsc aux init failed %d\n", ret); | |
153 | kfree(adev); | |
154 | goto fail; | |
155 | } | |
156 | ||
157 | ret = auxiliary_device_add(aux_dev); | |
158 | if (ret < 0) { | |
159 | drm_err(&i915->drm, "gsc aux add failed %d\n", ret); | |
160 | /* adev will be freed with the put_device() and .release sequence */ | |
161 | auxiliary_device_uninit(aux_dev); | |
162 | goto fail; | |
163 | } | |
164 | intf->adev = adev; | |
165 | ||
166 | return; | |
167 | fail: | |
168 | gsc_destroy_one(intf); | |
169 | } | |
170 | ||
171 | static void gsc_irq_handler(struct intel_gt *gt, unsigned int intf_id) | |
172 | { | |
173 | int ret; | |
174 | ||
175 | if (intf_id >= INTEL_GSC_NUM_INTERFACES) { | |
176 | drm_warn_once(>->i915->drm, "GSC irq: intf_id %d is out of range", intf_id); | |
177 | return; | |
178 | } | |
179 | ||
180 | if (!HAS_HECI_GSC(gt->i915)) { | |
181 | drm_warn_once(>->i915->drm, "GSC irq: not supported"); | |
182 | return; | |
183 | } | |
184 | ||
185 | if (gt->gsc.intf[intf_id].irq < 0) { | |
186 | drm_err_ratelimited(>->i915->drm, "GSC irq: irq not set"); | |
187 | return; | |
188 | } | |
189 | ||
190 | ret = generic_handle_irq(gt->gsc.intf[intf_id].irq); | |
191 | if (ret) | |
192 | drm_err_ratelimited(>->i915->drm, "error handling GSC irq: %d\n", ret); | |
193 | } | |
194 | ||
195 | void intel_gsc_irq_handler(struct intel_gt *gt, u32 iir) | |
196 | { | |
197 | if (iir & GSC_IRQ_INTF(0)) | |
198 | gsc_irq_handler(gt, 0); | |
199 | if (iir & GSC_IRQ_INTF(1)) | |
200 | gsc_irq_handler(gt, 1); | |
201 | } | |
202 | ||
203 | void intel_gsc_init(struct intel_gsc *gsc, struct drm_i915_private *i915) | |
204 | { | |
205 | unsigned int i; | |
206 | ||
207 | if (!HAS_HECI_GSC(i915)) | |
208 | return; | |
209 | ||
210 | for (i = 0; i < INTEL_GSC_NUM_INTERFACES; i++) | |
211 | gsc_init_one(i915, &gsc->intf[i], i); | |
212 | } | |
213 | ||
214 | void intel_gsc_fini(struct intel_gsc *gsc) | |
215 | { | |
216 | struct intel_gt *gt = gsc_to_gt(gsc); | |
217 | unsigned int i; | |
218 | ||
219 | if (!HAS_HECI_GSC(gt->i915)) | |
220 | return; | |
221 | ||
222 | for (i = 0; i < INTEL_GSC_NUM_INTERFACES; i++) | |
223 | gsc_destroy_one(&gsc->intf[i]); | |
224 | } |