| .. | .. |
|---|
| 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 | } |
|---|