diff options
author | Jiang Liu <jiang.liu@linux.intel.com> | 2015-04-13 02:11:34 -0400 |
---|---|---|
committer | Thomas Gleixner <tglx@linutronix.de> | 2015-04-24 09:36:49 -0400 |
commit | 3cb96f0c97330834929abe9bd2ca3c252a83def0 (patch) | |
tree | 31238e605d8c761a1bed32b923853650f378fed8 | |
parent | 7c71d306c97bd060e1a97d6905aebcb5769890ca (diff) |
x86/hpet: Enhance HPET IRQ to support hierarchical irqdomains
Signed-off-by: Jiang Liu <jiang.liu@linux.intel.com>
Cc: Konrad Rzeszutek Wilk <konrad.wilk@oracle.com>
Cc: David Cohen <david.a.cohen@linux.intel.com>
Cc: Sander Eikelenboom <linux@eikelenboom.it>
Cc: David Vrabel <david.vrabel@citrix.com>
Cc: Tony Luck <tony.luck@intel.com>
Cc: Joerg Roedel <joro@8bytes.org>
Cc: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Cc: Srivatsa S. Bhat <srivatsa.bhat@linux.vnet.ibm.com>
Cc: Bjorn Helgaas <bhelgaas@google.com>
Cc: Benjamin Herrenschmidt <benh@kernel.crashing.org>
Cc: Rafael J. Wysocki <rjw@rjwysocki.net>
Cc: Randy Dunlap <rdunlap@infradead.org>
Cc: Yinghai Lu <yinghai@kernel.org>
Cc: Borislav Petkov <bp@alien8.de>
Cc: Dimitri Sivanich <sivanich@sgi.com>
Cc: Andy Lutomirski <luto@amacapital.net>
Link: http://lkml.kernel.org/r/1428905519-23704-13-git-send-email-jiang.liu@linux.intel.com
Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
-rw-r--r-- | arch/x86/include/asm/hpet.h | 7 | ||||
-rw-r--r-- | arch/x86/kernel/apic/msi.c | 166 | ||||
-rw-r--r-- | arch/x86/kernel/hpet.c | 57 |
3 files changed, 167 insertions, 63 deletions
diff --git a/arch/x86/include/asm/hpet.h b/arch/x86/include/asm/hpet.h index 36f7125945e3..e87e9faf87a9 100644 --- a/arch/x86/include/asm/hpet.h +++ b/arch/x86/include/asm/hpet.h | |||
@@ -74,11 +74,16 @@ extern unsigned int hpet_readl(unsigned int a); | |||
74 | extern void force_hpet_resume(void); | 74 | extern void force_hpet_resume(void); |
75 | 75 | ||
76 | struct irq_data; | 76 | struct irq_data; |
77 | struct hpet_dev; | ||
78 | struct irq_domain; | ||
79 | |||
77 | extern void hpet_msi_unmask(struct irq_data *data); | 80 | extern void hpet_msi_unmask(struct irq_data *data); |
78 | extern void hpet_msi_mask(struct irq_data *data); | 81 | extern void hpet_msi_mask(struct irq_data *data); |
79 | struct hpet_dev; | ||
80 | extern void hpet_msi_write(struct hpet_dev *hdev, struct msi_msg *msg); | 82 | extern void hpet_msi_write(struct hpet_dev *hdev, struct msi_msg *msg); |
81 | extern void hpet_msi_read(struct hpet_dev *hdev, struct msi_msg *msg); | 83 | extern void hpet_msi_read(struct hpet_dev *hdev, struct msi_msg *msg); |
84 | extern struct irq_domain *hpet_create_irq_domain(int hpet_id); | ||
85 | extern int hpet_assign_irq(struct irq_domain *domain, | ||
86 | struct hpet_dev *dev, int dev_num); | ||
82 | 87 | ||
83 | #ifdef CONFIG_PCI_MSI | 88 | #ifdef CONFIG_PCI_MSI |
84 | extern int default_setup_hpet_msi(unsigned int irq, unsigned int id); | 89 | extern int default_setup_hpet_msi(unsigned int irq, unsigned int id); |
diff --git a/arch/x86/kernel/apic/msi.c b/arch/x86/kernel/apic/msi.c index 9be7d6d8a579..10d9ae8f2166 100644 --- a/arch/x86/kernel/apic/msi.c +++ b/arch/x86/kernel/apic/msi.c | |||
@@ -51,6 +51,44 @@ void native_compose_msi_msg(struct pci_dev *pdev, | |||
51 | MSI_DATA_VECTOR(cfg->vector); | 51 | MSI_DATA_VECTOR(cfg->vector); |
52 | } | 52 | } |
53 | 53 | ||
54 | static void irq_msi_compose_msg(struct irq_data *data, struct msi_msg *msg) | ||
55 | { | ||
56 | struct irq_cfg *cfg = irqd_cfg(data); | ||
57 | |||
58 | msg->address_hi = MSI_ADDR_BASE_HI; | ||
59 | |||
60 | if (x2apic_enabled()) | ||
61 | msg->address_hi |= MSI_ADDR_EXT_DEST_ID(cfg->dest_apicid); | ||
62 | |||
63 | msg->address_lo = | ||
64 | MSI_ADDR_BASE_LO | | ||
65 | ((apic->irq_dest_mode == 0) ? | ||
66 | MSI_ADDR_DEST_MODE_PHYSICAL : | ||
67 | MSI_ADDR_DEST_MODE_LOGICAL) | | ||
68 | ((apic->irq_delivery_mode != dest_LowestPrio) ? | ||
69 | MSI_ADDR_REDIRECTION_CPU : | ||
70 | MSI_ADDR_REDIRECTION_LOWPRI) | | ||
71 | MSI_ADDR_DEST_ID(cfg->dest_apicid); | ||
72 | |||
73 | msg->data = | ||
74 | MSI_DATA_TRIGGER_EDGE | | ||
75 | MSI_DATA_LEVEL_ASSERT | | ||
76 | ((apic->irq_delivery_mode != dest_LowestPrio) ? | ||
77 | MSI_DATA_DELIVERY_FIXED : | ||
78 | MSI_DATA_DELIVERY_LOWPRI) | | ||
79 | MSI_DATA_VECTOR(cfg->vector); | ||
80 | } | ||
81 | |||
82 | static void msi_update_msg(struct msi_msg *msg, struct irq_data *irq_data) | ||
83 | { | ||
84 | struct irq_cfg *cfg = irqd_cfg(irq_data); | ||
85 | |||
86 | msg->data &= ~MSI_DATA_VECTOR_MASK; | ||
87 | msg->data |= MSI_DATA_VECTOR(cfg->vector); | ||
88 | msg->address_lo &= ~MSI_ADDR_DEST_ID_MASK; | ||
89 | msg->address_lo |= MSI_ADDR_DEST_ID(cfg->dest_apicid); | ||
90 | } | ||
91 | |||
54 | static int msi_compose_msg(struct pci_dev *pdev, unsigned int irq, | 92 | static int msi_compose_msg(struct pci_dev *pdev, unsigned int irq, |
55 | struct msi_msg *msg, u8 hpet_id) | 93 | struct msi_msg *msg, u8 hpet_id) |
56 | { | 94 | { |
@@ -239,44 +277,43 @@ void dmar_free_hwirq(int irq) | |||
239 | * MSI message composition | 277 | * MSI message composition |
240 | */ | 278 | */ |
241 | #ifdef CONFIG_HPET_TIMER | 279 | #ifdef CONFIG_HPET_TIMER |
280 | static inline int hpet_dev_id(struct irq_domain *domain) | ||
281 | { | ||
282 | return (int)(long)domain->host_data; | ||
283 | } | ||
242 | 284 | ||
243 | static int hpet_msi_set_affinity(struct irq_data *data, | 285 | static int hpet_msi_set_affinity(struct irq_data *data, |
244 | const struct cpumask *mask, bool force) | 286 | const struct cpumask *mask, bool force) |
245 | { | 287 | { |
246 | struct irq_cfg *cfg = irqd_cfg(data); | 288 | struct irq_data *parent = data->parent_data; |
247 | struct msi_msg msg; | 289 | struct msi_msg msg; |
248 | unsigned int dest; | ||
249 | int ret; | 290 | int ret; |
250 | 291 | ||
251 | ret = apic_set_affinity(data, mask, &dest); | 292 | ret = parent->chip->irq_set_affinity(parent, mask, force); |
252 | if (ret) | 293 | if (ret >= 0 && ret != IRQ_SET_MASK_OK_DONE) { |
253 | return ret; | 294 | hpet_msi_read(data->handler_data, &msg); |
254 | 295 | msi_update_msg(&msg, data); | |
255 | hpet_msi_read(data->handler_data, &msg); | 296 | hpet_msi_write(data->handler_data, &msg); |
256 | 297 | } | |
257 | msg.data &= ~MSI_DATA_VECTOR_MASK; | ||
258 | msg.data |= MSI_DATA_VECTOR(cfg->vector); | ||
259 | msg.address_lo &= ~MSI_ADDR_DEST_ID_MASK; | ||
260 | msg.address_lo |= MSI_ADDR_DEST_ID(dest); | ||
261 | |||
262 | hpet_msi_write(data->handler_data, &msg); | ||
263 | 298 | ||
264 | return IRQ_SET_MASK_OK_NOCOPY; | 299 | return ret; |
265 | } | 300 | } |
266 | 301 | ||
267 | static struct irq_chip hpet_msi_type = { | 302 | static struct irq_chip hpet_msi_controller = { |
268 | .name = "HPET_MSI", | 303 | .name = "HPET_MSI", |
269 | .irq_unmask = hpet_msi_unmask, | 304 | .irq_unmask = hpet_msi_unmask, |
270 | .irq_mask = hpet_msi_mask, | 305 | .irq_mask = hpet_msi_mask, |
271 | .irq_ack = apic_ack_edge, | 306 | .irq_ack = irq_chip_ack_parent, |
272 | .irq_set_affinity = hpet_msi_set_affinity, | 307 | .irq_set_affinity = hpet_msi_set_affinity, |
273 | .irq_retrigger = apic_retrigger_irq, | 308 | .irq_retrigger = irq_chip_retrigger_hierarchy, |
309 | .irq_print_chip = irq_remapping_print_chip, | ||
310 | .irq_compose_msi_msg = irq_msi_compose_msg, | ||
274 | .flags = IRQCHIP_SKIP_SET_WAKE, | 311 | .flags = IRQCHIP_SKIP_SET_WAKE, |
275 | }; | 312 | }; |
276 | 313 | ||
277 | int default_setup_hpet_msi(unsigned int irq, unsigned int id) | 314 | int default_setup_hpet_msi(unsigned int irq, unsigned int id) |
278 | { | 315 | { |
279 | struct irq_chip *chip = &hpet_msi_type; | 316 | struct irq_chip *chip = &hpet_msi_controller; |
280 | struct msi_msg msg; | 317 | struct msi_msg msg; |
281 | int ret; | 318 | int ret; |
282 | 319 | ||
@@ -291,4 +328,95 @@ int default_setup_hpet_msi(unsigned int irq, unsigned int id) | |||
291 | irq_set_chip_and_handler_name(irq, chip, handle_edge_irq, "edge"); | 328 | irq_set_chip_and_handler_name(irq, chip, handle_edge_irq, "edge"); |
292 | return 0; | 329 | return 0; |
293 | } | 330 | } |
331 | |||
332 | static int hpet_domain_alloc(struct irq_domain *domain, unsigned int virq, | ||
333 | unsigned int nr_irqs, void *arg) | ||
334 | { | ||
335 | struct irq_alloc_info *info = arg; | ||
336 | int ret; | ||
337 | |||
338 | if (nr_irqs > 1 || !info || info->type != X86_IRQ_ALLOC_TYPE_HPET) | ||
339 | return -EINVAL; | ||
340 | if (irq_find_mapping(domain, info->hpet_index)) { | ||
341 | pr_warn("IRQ for HPET%d already exists.\n", info->hpet_index); | ||
342 | return -EEXIST; | ||
343 | } | ||
344 | |||
345 | ret = irq_domain_alloc_irqs_parent(domain, virq, nr_irqs, arg); | ||
346 | if (ret >= 0) { | ||
347 | irq_set_status_flags(virq, IRQ_MOVE_PCNTXT); | ||
348 | irq_domain_set_hwirq_and_chip(domain, virq, info->hpet_index, | ||
349 | &hpet_msi_controller, NULL); | ||
350 | irq_set_handler_data(virq, info->hpet_data); | ||
351 | __irq_set_handler(virq, handle_edge_irq, 0, "edge"); | ||
352 | } | ||
353 | |||
354 | return ret; | ||
355 | } | ||
356 | |||
357 | static void hpet_domain_free(struct irq_domain *domain, unsigned int virq, | ||
358 | unsigned int nr_irqs) | ||
359 | { | ||
360 | BUG_ON(nr_irqs > 1); | ||
361 | irq_clear_status_flags(virq, IRQ_MOVE_PCNTXT); | ||
362 | irq_domain_free_irqs_top(domain, virq, nr_irqs); | ||
363 | } | ||
364 | |||
365 | static void hpet_domain_activate(struct irq_domain *domain, | ||
366 | struct irq_data *irq_data) | ||
367 | { | ||
368 | struct msi_msg msg; | ||
369 | |||
370 | BUG_ON(irq_chip_compose_msi_msg(irq_data, &msg)); | ||
371 | hpet_msi_write(irq_get_handler_data(irq_data->irq), &msg); | ||
372 | } | ||
373 | |||
374 | static void hpet_domain_deactivate(struct irq_domain *domain, | ||
375 | struct irq_data *irq_data) | ||
376 | { | ||
377 | struct msi_msg msg; | ||
378 | |||
379 | memset(&msg, 0, sizeof(msg)); | ||
380 | hpet_msi_write(irq_get_handler_data(irq_data->irq), &msg); | ||
381 | } | ||
382 | |||
383 | static struct irq_domain_ops hpet_domain_ops = { | ||
384 | .alloc = hpet_domain_alloc, | ||
385 | .free = hpet_domain_free, | ||
386 | .activate = hpet_domain_activate, | ||
387 | .deactivate = hpet_domain_deactivate, | ||
388 | }; | ||
389 | |||
390 | struct irq_domain *hpet_create_irq_domain(int hpet_id) | ||
391 | { | ||
392 | struct irq_domain *parent; | ||
393 | struct irq_alloc_info info; | ||
394 | |||
395 | if (x86_vector_domain == NULL) | ||
396 | return NULL; | ||
397 | |||
398 | init_irq_alloc_info(&info, NULL); | ||
399 | info.type = X86_IRQ_ALLOC_TYPE_HPET; | ||
400 | info.hpet_id = hpet_id; | ||
401 | parent = irq_remapping_get_ir_irq_domain(&info); | ||
402 | if (parent == NULL) | ||
403 | parent = x86_vector_domain; | ||
404 | |||
405 | return irq_domain_add_hierarchy(parent, 0, 0, NULL, &hpet_domain_ops, | ||
406 | (void *)(long)hpet_id); | ||
407 | } | ||
408 | |||
409 | int hpet_assign_irq(struct irq_domain *domain, struct hpet_dev *dev, | ||
410 | int dev_num) | ||
411 | { | ||
412 | struct irq_alloc_info info; | ||
413 | |||
414 | init_irq_alloc_info(&info, NULL); | ||
415 | info.type = X86_IRQ_ALLOC_TYPE_HPET; | ||
416 | info.hpet_data = dev; | ||
417 | info.hpet_id = hpet_dev_id(domain); | ||
418 | info.hpet_index = dev_num; | ||
419 | |||
420 | return irq_domain_alloc_irqs(domain, 1, NUMA_NO_NODE, NULL); | ||
421 | } | ||
294 | #endif | 422 | #endif |
diff --git a/arch/x86/kernel/hpet.c b/arch/x86/kernel/hpet.c index ae29554f57ea..e3bc18080052 100644 --- a/arch/x86/kernel/hpet.c +++ b/arch/x86/kernel/hpet.c | |||
@@ -306,8 +306,6 @@ static void hpet_legacy_clockevent_register(void) | |||
306 | printk(KERN_DEBUG "hpet clockevent registered\n"); | 306 | printk(KERN_DEBUG "hpet clockevent registered\n"); |
307 | } | 307 | } |
308 | 308 | ||
309 | static int hpet_setup_msi_irq(unsigned int irq); | ||
310 | |||
311 | static void hpet_set_mode(enum clock_event_mode mode, | 309 | static void hpet_set_mode(enum clock_event_mode mode, |
312 | struct clock_event_device *evt, int timer) | 310 | struct clock_event_device *evt, int timer) |
313 | { | 311 | { |
@@ -358,7 +356,7 @@ static void hpet_set_mode(enum clock_event_mode mode, | |||
358 | hpet_enable_legacy_int(); | 356 | hpet_enable_legacy_int(); |
359 | } else { | 357 | } else { |
360 | struct hpet_dev *hdev = EVT_TO_HPET_DEV(evt); | 358 | struct hpet_dev *hdev = EVT_TO_HPET_DEV(evt); |
361 | hpet_setup_msi_irq(hdev->irq); | 359 | irq_domain_activate_irq(irq_get_irq_data(hdev->irq)); |
362 | disable_irq(hdev->irq); | 360 | disable_irq(hdev->irq); |
363 | irq_set_affinity(hdev->irq, cpumask_of(hdev->cpu)); | 361 | irq_set_affinity(hdev->irq, cpumask_of(hdev->cpu)); |
364 | enable_irq(hdev->irq); | 362 | enable_irq(hdev->irq); |
@@ -424,6 +422,7 @@ static int hpet_legacy_next_event(unsigned long delta, | |||
424 | 422 | ||
425 | static DEFINE_PER_CPU(struct hpet_dev *, cpu_hpet_dev); | 423 | static DEFINE_PER_CPU(struct hpet_dev *, cpu_hpet_dev); |
426 | static struct hpet_dev *hpet_devs; | 424 | static struct hpet_dev *hpet_devs; |
425 | static struct irq_domain *hpet_domain; | ||
427 | 426 | ||
428 | void hpet_msi_unmask(struct irq_data *data) | 427 | void hpet_msi_unmask(struct irq_data *data) |
429 | { | 428 | { |
@@ -474,32 +473,6 @@ static int hpet_msi_next_event(unsigned long delta, | |||
474 | return hpet_next_event(delta, evt, hdev->num); | 473 | return hpet_next_event(delta, evt, hdev->num); |
475 | } | 474 | } |
476 | 475 | ||
477 | static int hpet_setup_msi_irq(unsigned int irq) | ||
478 | { | ||
479 | if (x86_msi.setup_hpet_msi(irq, hpet_blockid)) { | ||
480 | irq_domain_free_irqs(irq, 1); | ||
481 | return -EINVAL; | ||
482 | } | ||
483 | return 0; | ||
484 | } | ||
485 | |||
486 | static int hpet_assign_irq(struct hpet_dev *dev) | ||
487 | { | ||
488 | int irq; | ||
489 | |||
490 | irq = irq_domain_alloc_irqs(NULL, 1, NUMA_NO_NODE, NULL); | ||
491 | if (irq <= 0) | ||
492 | return -EINVAL; | ||
493 | |||
494 | irq_set_handler_data(irq, dev); | ||
495 | |||
496 | if (hpet_setup_msi_irq(irq)) | ||
497 | return -EINVAL; | ||
498 | |||
499 | dev->irq = irq; | ||
500 | return 0; | ||
501 | } | ||
502 | |||
503 | static irqreturn_t hpet_interrupt_handler(int irq, void *data) | 476 | static irqreturn_t hpet_interrupt_handler(int irq, void *data) |
504 | { | 477 | { |
505 | struct hpet_dev *dev = (struct hpet_dev *)data; | 478 | struct hpet_dev *dev = (struct hpet_dev *)data; |
@@ -542,9 +515,6 @@ static void init_one_hpet_msi_clockevent(struct hpet_dev *hdev, int cpu) | |||
542 | if (!(hdev->flags & HPET_DEV_VALID)) | 515 | if (!(hdev->flags & HPET_DEV_VALID)) |
543 | return; | 516 | return; |
544 | 517 | ||
545 | if (hpet_setup_msi_irq(hdev->irq)) | ||
546 | return; | ||
547 | |||
548 | hdev->cpu = cpu; | 518 | hdev->cpu = cpu; |
549 | per_cpu(cpu_hpet_dev, cpu) = hdev; | 519 | per_cpu(cpu_hpet_dev, cpu) = hdev; |
550 | evt->name = hdev->name; | 520 | evt->name = hdev->name; |
@@ -576,7 +546,7 @@ static void hpet_msi_capability_lookup(unsigned int start_timer) | |||
576 | unsigned int id; | 546 | unsigned int id; |
577 | unsigned int num_timers; | 547 | unsigned int num_timers; |
578 | unsigned int num_timers_used = 0; | 548 | unsigned int num_timers_used = 0; |
579 | int i; | 549 | int i, irq; |
580 | 550 | ||
581 | if (hpet_msi_disable) | 551 | if (hpet_msi_disable) |
582 | return; | 552 | return; |
@@ -589,6 +559,10 @@ static void hpet_msi_capability_lookup(unsigned int start_timer) | |||
589 | num_timers++; /* Value read out starts from 0 */ | 559 | num_timers++; /* Value read out starts from 0 */ |
590 | hpet_print_config(); | 560 | hpet_print_config(); |
591 | 561 | ||
562 | hpet_domain = hpet_create_irq_domain(hpet_blockid); | ||
563 | if (!hpet_domain) | ||
564 | return; | ||
565 | |||
592 | hpet_devs = kzalloc(sizeof(struct hpet_dev) * num_timers, GFP_KERNEL); | 566 | hpet_devs = kzalloc(sizeof(struct hpet_dev) * num_timers, GFP_KERNEL); |
593 | if (!hpet_devs) | 567 | if (!hpet_devs) |
594 | return; | 568 | return; |
@@ -603,15 +577,16 @@ static void hpet_msi_capability_lookup(unsigned int start_timer) | |||
603 | if (!(cfg & HPET_TN_FSB_CAP)) | 577 | if (!(cfg & HPET_TN_FSB_CAP)) |
604 | continue; | 578 | continue; |
605 | 579 | ||
580 | irq = hpet_assign_irq(hpet_domain, hdev, hdev->num); | ||
581 | if (irq < 0) | ||
582 | continue; | ||
583 | |||
584 | sprintf(hdev->name, "hpet%d", i); | ||
585 | hdev->num = i; | ||
586 | hdev->irq = irq; | ||
606 | hdev->flags = 0; | 587 | hdev->flags = 0; |
607 | if (cfg & HPET_TN_PERIODIC_CAP) | 588 | if (cfg & HPET_TN_PERIODIC_CAP) |
608 | hdev->flags |= HPET_DEV_PERI_CAP; | 589 | hdev->flags |= HPET_DEV_PERI_CAP; |
609 | hdev->num = i; | ||
610 | |||
611 | sprintf(hdev->name, "hpet%d", i); | ||
612 | if (hpet_assign_irq(hdev)) | ||
613 | continue; | ||
614 | |||
615 | hdev->flags |= HPET_DEV_FSB_CAP; | 590 | hdev->flags |= HPET_DEV_FSB_CAP; |
616 | hdev->flags |= HPET_DEV_VALID; | 591 | hdev->flags |= HPET_DEV_VALID; |
617 | num_timers_used++; | 592 | num_timers_used++; |
@@ -711,10 +686,6 @@ static int hpet_cpuhp_notify(struct notifier_block *n, | |||
711 | } | 686 | } |
712 | #else | 687 | #else |
713 | 688 | ||
714 | static int hpet_setup_msi_irq(unsigned int irq) | ||
715 | { | ||
716 | return 0; | ||
717 | } | ||
718 | static void hpet_msi_capability_lookup(unsigned int start_timer) | 689 | static void hpet_msi_capability_lookup(unsigned int start_timer) |
719 | { | 690 | { |
720 | return; | 691 | return; |