@@ -139,6 +139,170 @@ struct irq_domain *pci_msi_create_irq_domain(struct fwnode_handle *fwnode,
139
139
}
140
140
EXPORT_SYMBOL_GPL (pci_msi_create_irq_domain );
141
141
142
+ /*
143
+ * Per device MSI[-X] domain functionality
144
+ */
145
+ static void pci_device_domain_set_desc (msi_alloc_info_t * arg , struct msi_desc * desc )
146
+ {
147
+ arg -> desc = desc ;
148
+ arg -> hwirq = desc -> msi_index ;
149
+ }
150
+
151
+ static void pci_irq_mask_msi (struct irq_data * data )
152
+ {
153
+ struct msi_desc * desc = irq_data_get_msi_desc (data );
154
+
155
+ pci_msi_mask (desc , BIT (data -> irq - desc -> irq ));
156
+ }
157
+
158
+ static void pci_irq_unmask_msi (struct irq_data * data )
159
+ {
160
+ struct msi_desc * desc = irq_data_get_msi_desc (data );
161
+
162
+ pci_msi_unmask (desc , BIT (data -> irq - desc -> irq ));
163
+ }
164
+
165
+ #ifdef CONFIG_GENERIC_IRQ_RESERVATION_MODE
166
+ # define MSI_REACTIVATE MSI_FLAG_MUST_REACTIVATE
167
+ #else
168
+ # define MSI_REACTIVATE 0
169
+ #endif
170
+
171
+ #define MSI_COMMON_FLAGS (MSI_FLAG_FREE_MSI_DESCS | \
172
+ MSI_FLAG_ACTIVATE_EARLY | \
173
+ MSI_FLAG_DEV_SYSFS | \
174
+ MSI_REACTIVATE)
175
+
176
+ static const struct msi_domain_template pci_msi_template = {
177
+ .chip = {
178
+ .name = "PCI-MSI" ,
179
+ .irq_mask = pci_irq_mask_msi ,
180
+ .irq_unmask = pci_irq_unmask_msi ,
181
+ .irq_write_msi_msg = pci_msi_domain_write_msg ,
182
+ .flags = IRQCHIP_ONESHOT_SAFE ,
183
+ },
184
+
185
+ .ops = {
186
+ .set_desc = pci_device_domain_set_desc ,
187
+ },
188
+
189
+ .info = {
190
+ .flags = MSI_COMMON_FLAGS | MSI_FLAG_MULTI_PCI_MSI ,
191
+ .bus_token = DOMAIN_BUS_PCI_DEVICE_MSI ,
192
+ },
193
+ };
194
+
195
+ static void pci_irq_mask_msix (struct irq_data * data )
196
+ {
197
+ pci_msix_mask (irq_data_get_msi_desc (data ));
198
+ }
199
+
200
+ static void pci_irq_unmask_msix (struct irq_data * data )
201
+ {
202
+ pci_msix_unmask (irq_data_get_msi_desc (data ));
203
+ }
204
+
205
+ static const struct msi_domain_template pci_msix_template = {
206
+ .chip = {
207
+ .name = "PCI-MSIX" ,
208
+ .irq_mask = pci_irq_mask_msix ,
209
+ .irq_unmask = pci_irq_unmask_msix ,
210
+ .irq_write_msi_msg = pci_msi_domain_write_msg ,
211
+ .flags = IRQCHIP_ONESHOT_SAFE ,
212
+ },
213
+
214
+ .ops = {
215
+ .set_desc = pci_device_domain_set_desc ,
216
+ },
217
+
218
+ .info = {
219
+ .flags = MSI_COMMON_FLAGS | MSI_FLAG_PCI_MSIX ,
220
+ .bus_token = DOMAIN_BUS_PCI_DEVICE_MSIX ,
221
+ },
222
+ };
223
+
224
+ static bool pci_match_device_domain (struct pci_dev * pdev , enum irq_domain_bus_token bus_token )
225
+ {
226
+ return msi_match_device_irq_domain (& pdev -> dev , MSI_DEFAULT_DOMAIN , bus_token );
227
+ }
228
+
229
+ static bool pci_create_device_domain (struct pci_dev * pdev , const struct msi_domain_template * tmpl ,
230
+ unsigned int hwsize )
231
+ {
232
+ struct irq_domain * domain = dev_get_msi_domain (& pdev -> dev );
233
+
234
+ if (!domain || !irq_domain_is_msi_parent (domain ))
235
+ return true;
236
+
237
+ return msi_create_device_irq_domain (& pdev -> dev , MSI_DEFAULT_DOMAIN , tmpl ,
238
+ hwsize , NULL , NULL );
239
+ }
240
+
241
+ /**
242
+ * pci_setup_msi_device_domain - Setup a device MSI interrupt domain
243
+ * @pdev: The PCI device to create the domain on
244
+ *
245
+ * Return:
246
+ * True when:
247
+ * - The device does not have a MSI parent irq domain associated,
248
+ * which keeps the legacy architecture specific and the global
249
+ * PCI/MSI domain models working
250
+ * - The MSI domain exists already
251
+ * - The MSI domain was successfully allocated
252
+ * False when:
253
+ * - MSI-X is enabled
254
+ * - The domain creation fails.
255
+ *
256
+ * The created MSI domain is preserved until:
257
+ * - The device is removed
258
+ * - MSI is disabled and a MSI-X domain is created
259
+ */
260
+ bool pci_setup_msi_device_domain (struct pci_dev * pdev )
261
+ {
262
+ if (WARN_ON_ONCE (pdev -> msix_enabled ))
263
+ return false;
264
+
265
+ if (pci_match_device_domain (pdev , DOMAIN_BUS_PCI_DEVICE_MSI ))
266
+ return true;
267
+ if (pci_match_device_domain (pdev , DOMAIN_BUS_PCI_DEVICE_MSIX ))
268
+ msi_remove_device_irq_domain (& pdev -> dev , MSI_DEFAULT_DOMAIN );
269
+
270
+ return pci_create_device_domain (pdev , & pci_msi_template , 1 );
271
+ }
272
+
273
+ /**
274
+ * pci_setup_msix_device_domain - Setup a device MSI-X interrupt domain
275
+ * @pdev: The PCI device to create the domain on
276
+ * @hwsize: The size of the MSI-X vector table
277
+ *
278
+ * Return:
279
+ * True when:
280
+ * - The device does not have a MSI parent irq domain associated,
281
+ * which keeps the legacy architecture specific and the global
282
+ * PCI/MSI domain models working
283
+ * - The MSI-X domain exists already
284
+ * - The MSI-X domain was successfully allocated
285
+ * False when:
286
+ * - MSI is enabled
287
+ * - The domain creation fails.
288
+ *
289
+ * The created MSI-X domain is preserved until:
290
+ * - The device is removed
291
+ * - MSI-X is disabled and a MSI domain is created
292
+ */
293
+ bool pci_setup_msix_device_domain (struct pci_dev * pdev , unsigned int hwsize )
294
+ {
295
+ if (WARN_ON_ONCE (pdev -> msi_enabled ))
296
+ return false;
297
+
298
+ if (pci_match_device_domain (pdev , DOMAIN_BUS_PCI_DEVICE_MSIX ))
299
+ return true;
300
+ if (pci_match_device_domain (pdev , DOMAIN_BUS_PCI_DEVICE_MSI ))
301
+ msi_remove_device_irq_domain (& pdev -> dev , MSI_DEFAULT_DOMAIN );
302
+
303
+ return pci_create_device_domain (pdev , & pci_msix_template , hwsize );
304
+ }
305
+
142
306
/**
143
307
* pci_msi_domain_supports - Check for support of a particular feature flag
144
308
* @pdev: The PCI device to operate on
@@ -152,13 +316,33 @@ bool pci_msi_domain_supports(struct pci_dev *pdev, unsigned int feature_mask,
152
316
{
153
317
struct msi_domain_info * info ;
154
318
struct irq_domain * domain ;
319
+ unsigned int supported ;
155
320
156
321
domain = dev_get_msi_domain (& pdev -> dev );
157
322
158
323
if (!domain || !irq_domain_is_hierarchy (domain ))
159
324
return mode == ALLOW_LEGACY ;
160
- info = domain -> host_data ;
161
- return (info -> flags & feature_mask ) == feature_mask ;
325
+
326
+ if (!irq_domain_is_msi_parent (domain )) {
327
+ /*
328
+ * For "global" PCI/MSI interrupt domains the associated
329
+ * msi_domain_info::flags is the authoritive source of
330
+ * information.
331
+ */
332
+ info = domain -> host_data ;
333
+ supported = info -> flags ;
334
+ } else {
335
+ /*
336
+ * For MSI parent domains the supported feature set
337
+ * is avaliable in the parent ops. This makes checks
338
+ * possible before actually instantiating the
339
+ * per device domain because the parent is never
340
+ * expanding the PCI/MSI functionality.
341
+ */
342
+ supported = domain -> msi_parent_ops -> supported_flags ;
343
+ }
344
+
345
+ return (supported & feature_mask ) == feature_mask ;
162
346
}
163
347
164
348
/*
0 commit comments