.. | .. |
---|
| 1 | +// SPDX-License-Identifier: GPL-2.0-only |
---|
1 | 2 | /* |
---|
2 | 3 | * Support of MSI, HPET and DMAR interrupts. |
---|
3 | 4 | * |
---|
.. | .. |
---|
5 | 6 | * Moved from arch/x86/kernel/apic/io_apic.c. |
---|
6 | 7 | * Jiang Liu <jiang.liu@linux.intel.com> |
---|
7 | 8 | * Convert to hierarchical irqdomain |
---|
8 | | - * |
---|
9 | | - * This program is free software; you can redistribute it and/or modify |
---|
10 | | - * it under the terms of the GNU General Public License version 2 as |
---|
11 | | - * published by the Free Software Foundation. |
---|
12 | 9 | */ |
---|
13 | 10 | #include <linux/mm.h> |
---|
14 | 11 | #include <linux/interrupt.h> |
---|
.. | .. |
---|
24 | 21 | #include <asm/apic.h> |
---|
25 | 22 | #include <asm/irq_remapping.h> |
---|
26 | 23 | |
---|
27 | | -static struct irq_domain *msi_default_domain; |
---|
| 24 | +struct irq_domain *x86_pci_msi_default_domain __ro_after_init; |
---|
28 | 25 | |
---|
29 | 26 | static void __irq_msi_compose_msg(struct irq_cfg *cfg, struct msi_msg *msg) |
---|
30 | 27 | { |
---|
.. | .. |
---|
48 | 45 | MSI_DATA_VECTOR(cfg->vector); |
---|
49 | 46 | } |
---|
50 | 47 | |
---|
51 | | -static void irq_msi_compose_msg(struct irq_data *data, struct msi_msg *msg) |
---|
| 48 | +void x86_vector_msi_compose_msg(struct irq_data *data, struct msi_msg *msg) |
---|
52 | 49 | { |
---|
53 | 50 | __irq_msi_compose_msg(irqd_cfg(data), msg); |
---|
54 | 51 | } |
---|
.. | .. |
---|
120 | 117 | * denote it as spurious which is no harm as this is a rare event |
---|
121 | 118 | * and interrupt handlers have to cope with spurious interrupts |
---|
122 | 119 | * anyway. If the vector is unused, then it is marked so it won't |
---|
123 | | - * trigger the 'No irq handler for vector' warning in do_IRQ(). |
---|
| 120 | + * trigger the 'No irq handler for vector' warning in |
---|
| 121 | + * common_interrupt(). |
---|
124 | 122 | * |
---|
125 | 123 | * This requires to hold vector lock to prevent concurrent updates to |
---|
126 | 124 | * the affected vector. |
---|
.. | .. |
---|
181 | 179 | .irq_mask = pci_msi_mask_irq, |
---|
182 | 180 | .irq_ack = irq_chip_ack_parent, |
---|
183 | 181 | .irq_retrigger = irq_chip_retrigger_hierarchy, |
---|
184 | | - .irq_compose_msi_msg = irq_msi_compose_msg, |
---|
185 | 182 | .irq_set_affinity = msi_set_affinity, |
---|
186 | 183 | .flags = IRQCHIP_SKIP_SET_WAKE | |
---|
187 | 184 | IRQCHIP_AFFINITY_PRE_STARTUP, |
---|
188 | 185 | }; |
---|
189 | | - |
---|
190 | | -int native_setup_msi_irqs(struct pci_dev *dev, int nvec, int type) |
---|
191 | | -{ |
---|
192 | | - struct irq_domain *domain; |
---|
193 | | - struct irq_alloc_info info; |
---|
194 | | - |
---|
195 | | - init_irq_alloc_info(&info, NULL); |
---|
196 | | - info.type = X86_IRQ_ALLOC_TYPE_MSI; |
---|
197 | | - info.msi_dev = dev; |
---|
198 | | - |
---|
199 | | - domain = irq_remapping_get_irq_domain(&info); |
---|
200 | | - if (domain == NULL) |
---|
201 | | - domain = msi_default_domain; |
---|
202 | | - if (domain == NULL) |
---|
203 | | - return -ENOSYS; |
---|
204 | | - |
---|
205 | | - return msi_domain_alloc_irqs(domain, &dev->dev, nvec); |
---|
206 | | -} |
---|
207 | | - |
---|
208 | | -void native_teardown_msi_irq(unsigned int irq) |
---|
209 | | -{ |
---|
210 | | - irq_domain_free_irqs(irq, 1); |
---|
211 | | -} |
---|
212 | | - |
---|
213 | | -static irq_hw_number_t pci_msi_get_hwirq(struct msi_domain_info *info, |
---|
214 | | - msi_alloc_info_t *arg) |
---|
215 | | -{ |
---|
216 | | - return arg->msi_hwirq; |
---|
217 | | -} |
---|
218 | 186 | |
---|
219 | 187 | int pci_msi_prepare(struct irq_domain *domain, struct device *dev, int nvec, |
---|
220 | 188 | msi_alloc_info_t *arg) |
---|
.. | .. |
---|
223 | 191 | struct msi_desc *desc = first_pci_msi_entry(pdev); |
---|
224 | 192 | |
---|
225 | 193 | init_irq_alloc_info(arg, NULL); |
---|
226 | | - arg->msi_dev = pdev; |
---|
227 | 194 | if (desc->msi_attrib.is_msix) { |
---|
228 | | - arg->type = X86_IRQ_ALLOC_TYPE_MSIX; |
---|
| 195 | + arg->type = X86_IRQ_ALLOC_TYPE_PCI_MSIX; |
---|
229 | 196 | } else { |
---|
230 | | - arg->type = X86_IRQ_ALLOC_TYPE_MSI; |
---|
| 197 | + arg->type = X86_IRQ_ALLOC_TYPE_PCI_MSI; |
---|
231 | 198 | arg->flags |= X86_IRQ_ALLOC_CONTIGUOUS_VECTORS; |
---|
232 | 199 | } |
---|
233 | 200 | |
---|
.. | .. |
---|
235 | 202 | } |
---|
236 | 203 | EXPORT_SYMBOL_GPL(pci_msi_prepare); |
---|
237 | 204 | |
---|
238 | | -void pci_msi_set_desc(msi_alloc_info_t *arg, struct msi_desc *desc) |
---|
239 | | -{ |
---|
240 | | - arg->msi_hwirq = pci_msi_domain_calc_hwirq(arg->msi_dev, desc); |
---|
241 | | -} |
---|
242 | | -EXPORT_SYMBOL_GPL(pci_msi_set_desc); |
---|
243 | | - |
---|
244 | 205 | static struct msi_domain_ops pci_msi_domain_ops = { |
---|
245 | | - .get_hwirq = pci_msi_get_hwirq, |
---|
246 | 206 | .msi_prepare = pci_msi_prepare, |
---|
247 | | - .set_desc = pci_msi_set_desc, |
---|
248 | 207 | }; |
---|
249 | 208 | |
---|
250 | 209 | static struct msi_domain_info pci_msi_domain_info = { |
---|
.. | .. |
---|
256 | 215 | .handler_name = "edge", |
---|
257 | 216 | }; |
---|
258 | 217 | |
---|
259 | | -void __init arch_init_msi_domain(struct irq_domain *parent) |
---|
| 218 | +struct irq_domain * __init native_create_pci_msi_domain(void) |
---|
260 | 219 | { |
---|
261 | 220 | struct fwnode_handle *fn; |
---|
| 221 | + struct irq_domain *d; |
---|
262 | 222 | |
---|
263 | 223 | if (disable_apic) |
---|
264 | | - return; |
---|
| 224 | + return NULL; |
---|
265 | 225 | |
---|
266 | 226 | fn = irq_domain_alloc_named_fwnode("PCI-MSI"); |
---|
267 | | - if (fn) { |
---|
268 | | - msi_default_domain = |
---|
269 | | - pci_msi_create_irq_domain(fn, &pci_msi_domain_info, |
---|
270 | | - parent); |
---|
271 | | - } |
---|
272 | | - if (!msi_default_domain) { |
---|
| 227 | + if (!fn) |
---|
| 228 | + return NULL; |
---|
| 229 | + |
---|
| 230 | + d = pci_msi_create_irq_domain(fn, &pci_msi_domain_info, |
---|
| 231 | + x86_vector_domain); |
---|
| 232 | + if (!d) { |
---|
273 | 233 | irq_domain_free_fwnode(fn); |
---|
274 | | - pr_warn("failed to initialize irqdomain for MSI/MSI-x.\n"); |
---|
| 234 | + pr_warn("Failed to initialize PCI-MSI irqdomain.\n"); |
---|
275 | 235 | } else { |
---|
276 | | - msi_default_domain->flags |= IRQ_DOMAIN_MSI_NOMASK_QUIRK; |
---|
| 236 | + d->flags |= IRQ_DOMAIN_MSI_NOMASK_QUIRK; |
---|
277 | 237 | } |
---|
| 238 | + return d; |
---|
| 239 | +} |
---|
| 240 | + |
---|
| 241 | +void __init x86_create_pci_msi_domain(void) |
---|
| 242 | +{ |
---|
| 243 | + x86_pci_msi_default_domain = x86_init.irqs.create_pci_msi_domain(); |
---|
278 | 244 | } |
---|
279 | 245 | |
---|
280 | 246 | #ifdef CONFIG_IRQ_REMAP |
---|
.. | .. |
---|
284 | 250 | .irq_mask = pci_msi_mask_irq, |
---|
285 | 251 | .irq_ack = irq_chip_ack_parent, |
---|
286 | 252 | .irq_retrigger = irq_chip_retrigger_hierarchy, |
---|
287 | | - .irq_set_vcpu_affinity = irq_chip_set_vcpu_affinity_parent, |
---|
288 | 253 | .flags = IRQCHIP_SKIP_SET_WAKE | |
---|
289 | 254 | IRQCHIP_AFFINITY_PRE_STARTUP, |
---|
290 | 255 | }; |
---|
.. | .. |
---|
327 | 292 | .irq_ack = irq_chip_ack_parent, |
---|
328 | 293 | .irq_set_affinity = msi_domain_set_affinity, |
---|
329 | 294 | .irq_retrigger = irq_chip_retrigger_hierarchy, |
---|
330 | | - .irq_compose_msi_msg = irq_msi_compose_msg, |
---|
331 | 295 | .irq_write_msi_msg = dmar_msi_write_msg, |
---|
332 | 296 | .flags = IRQCHIP_SKIP_SET_WAKE | |
---|
333 | 297 | IRQCHIP_AFFINITY_PRE_STARTUP, |
---|
334 | 298 | }; |
---|
335 | 299 | |
---|
336 | | -static irq_hw_number_t dmar_msi_get_hwirq(struct msi_domain_info *info, |
---|
337 | | - msi_alloc_info_t *arg) |
---|
338 | | -{ |
---|
339 | | - return arg->dmar_id; |
---|
340 | | -} |
---|
341 | | - |
---|
342 | 300 | static int dmar_msi_init(struct irq_domain *domain, |
---|
343 | 301 | struct msi_domain_info *info, unsigned int virq, |
---|
344 | 302 | irq_hw_number_t hwirq, msi_alloc_info_t *arg) |
---|
345 | 303 | { |
---|
346 | | - irq_domain_set_info(domain, virq, arg->dmar_id, info->chip, NULL, |
---|
347 | | - handle_edge_irq, arg->dmar_data, "edge"); |
---|
| 304 | + irq_domain_set_info(domain, virq, arg->devid, info->chip, NULL, |
---|
| 305 | + handle_edge_irq, arg->data, "edge"); |
---|
348 | 306 | |
---|
349 | 307 | return 0; |
---|
350 | 308 | } |
---|
351 | 309 | |
---|
352 | 310 | static struct msi_domain_ops dmar_msi_domain_ops = { |
---|
353 | | - .get_hwirq = dmar_msi_get_hwirq, |
---|
354 | 311 | .msi_init = dmar_msi_init, |
---|
355 | 312 | }; |
---|
356 | 313 | |
---|
357 | 314 | static struct msi_domain_info dmar_msi_domain_info = { |
---|
358 | 315 | .ops = &dmar_msi_domain_ops, |
---|
359 | 316 | .chip = &dmar_msi_controller, |
---|
| 317 | + .flags = MSI_FLAG_USE_DEF_DOM_OPS, |
---|
360 | 318 | }; |
---|
361 | 319 | |
---|
362 | 320 | static struct irq_domain *dmar_get_irq_domain(void) |
---|
.. | .. |
---|
391 | 349 | |
---|
392 | 350 | init_irq_alloc_info(&info, NULL); |
---|
393 | 351 | info.type = X86_IRQ_ALLOC_TYPE_DMAR; |
---|
394 | | - info.dmar_id = id; |
---|
395 | | - info.dmar_data = arg; |
---|
| 352 | + info.devid = id; |
---|
| 353 | + info.hwirq = id; |
---|
| 354 | + info.data = arg; |
---|
396 | 355 | |
---|
397 | 356 | return irq_domain_alloc_irqs(domain, 1, node, &info); |
---|
398 | 357 | } |
---|
.. | .. |
---|
426 | 385 | .irq_ack = irq_chip_ack_parent, |
---|
427 | 386 | .irq_set_affinity = msi_domain_set_affinity, |
---|
428 | 387 | .irq_retrigger = irq_chip_retrigger_hierarchy, |
---|
429 | | - .irq_compose_msi_msg = irq_msi_compose_msg, |
---|
430 | 388 | .irq_write_msi_msg = hpet_msi_write_msg, |
---|
431 | 389 | .flags = IRQCHIP_SKIP_SET_WAKE | IRQCHIP_AFFINITY_PRE_STARTUP, |
---|
432 | 390 | }; |
---|
433 | | - |
---|
434 | | -static irq_hw_number_t hpet_msi_get_hwirq(struct msi_domain_info *info, |
---|
435 | | - msi_alloc_info_t *arg) |
---|
436 | | -{ |
---|
437 | | - return arg->hpet_index; |
---|
438 | | -} |
---|
439 | 391 | |
---|
440 | 392 | static int hpet_msi_init(struct irq_domain *domain, |
---|
441 | 393 | struct msi_domain_info *info, unsigned int virq, |
---|
442 | 394 | irq_hw_number_t hwirq, msi_alloc_info_t *arg) |
---|
443 | 395 | { |
---|
444 | 396 | irq_set_status_flags(virq, IRQ_MOVE_PCNTXT); |
---|
445 | | - irq_domain_set_info(domain, virq, arg->hpet_index, info->chip, NULL, |
---|
446 | | - handle_edge_irq, arg->hpet_data, "edge"); |
---|
| 397 | + irq_domain_set_info(domain, virq, arg->hwirq, info->chip, NULL, |
---|
| 398 | + handle_edge_irq, arg->data, "edge"); |
---|
447 | 399 | |
---|
448 | 400 | return 0; |
---|
449 | 401 | } |
---|
.. | .. |
---|
455 | 407 | } |
---|
456 | 408 | |
---|
457 | 409 | static struct msi_domain_ops hpet_msi_domain_ops = { |
---|
458 | | - .get_hwirq = hpet_msi_get_hwirq, |
---|
459 | 410 | .msi_init = hpet_msi_init, |
---|
460 | 411 | .msi_free = hpet_msi_free, |
---|
461 | 412 | }; |
---|
.. | .. |
---|
463 | 414 | static struct msi_domain_info hpet_msi_domain_info = { |
---|
464 | 415 | .ops = &hpet_msi_domain_ops, |
---|
465 | 416 | .chip = &hpet_msi_controller, |
---|
| 417 | + .flags = MSI_FLAG_USE_DEF_DOM_OPS, |
---|
466 | 418 | }; |
---|
467 | 419 | |
---|
468 | 420 | struct irq_domain *hpet_create_irq_domain(int hpet_id) |
---|
.. | .. |
---|
483 | 435 | domain_info->data = (void *)(long)hpet_id; |
---|
484 | 436 | |
---|
485 | 437 | init_irq_alloc_info(&info, NULL); |
---|
486 | | - info.type = X86_IRQ_ALLOC_TYPE_HPET; |
---|
487 | | - info.hpet_id = hpet_id; |
---|
488 | | - parent = irq_remapping_get_ir_irq_domain(&info); |
---|
| 438 | + info.type = X86_IRQ_ALLOC_TYPE_HPET_GET_PARENT; |
---|
| 439 | + info.devid = hpet_id; |
---|
| 440 | + parent = irq_remapping_get_irq_domain(&info); |
---|
489 | 441 | if (parent == NULL) |
---|
490 | 442 | parent = x86_vector_domain; |
---|
491 | 443 | else |
---|
.. | .. |
---|
506 | 458 | return d; |
---|
507 | 459 | } |
---|
508 | 460 | |
---|
509 | | -int hpet_assign_irq(struct irq_domain *domain, struct hpet_dev *dev, |
---|
| 461 | +int hpet_assign_irq(struct irq_domain *domain, struct hpet_channel *hc, |
---|
510 | 462 | int dev_num) |
---|
511 | 463 | { |
---|
512 | 464 | struct irq_alloc_info info; |
---|
513 | 465 | |
---|
514 | 466 | init_irq_alloc_info(&info, NULL); |
---|
515 | 467 | info.type = X86_IRQ_ALLOC_TYPE_HPET; |
---|
516 | | - info.hpet_data = dev; |
---|
517 | | - info.hpet_id = hpet_dev_id(domain); |
---|
518 | | - info.hpet_index = dev_num; |
---|
| 468 | + info.data = hc; |
---|
| 469 | + info.devid = hpet_dev_id(domain); |
---|
| 470 | + info.hwirq = dev_num; |
---|
519 | 471 | |
---|
520 | 472 | return irq_domain_alloc_irqs(domain, 1, NUMA_NO_NODE, &info); |
---|
521 | 473 | } |
---|