aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJiang Liu <jiang.liu@linux.intel.com>2015-04-13 02:11:34 -0400
committerThomas Gleixner <tglx@linutronix.de>2015-04-24 09:36:49 -0400
commit3cb96f0c97330834929abe9bd2ca3c252a83def0 (patch)
tree31238e605d8c761a1bed32b923853650f378fed8
parent7c71d306c97bd060e1a97d6905aebcb5769890ca (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.h7
-rw-r--r--arch/x86/kernel/apic/msi.c166
-rw-r--r--arch/x86/kernel/hpet.c57
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);
74extern void force_hpet_resume(void); 74extern void force_hpet_resume(void);
75 75
76struct irq_data; 76struct irq_data;
77struct hpet_dev;
78struct irq_domain;
79
77extern void hpet_msi_unmask(struct irq_data *data); 80extern void hpet_msi_unmask(struct irq_data *data);
78extern void hpet_msi_mask(struct irq_data *data); 81extern void hpet_msi_mask(struct irq_data *data);
79struct hpet_dev;
80extern void hpet_msi_write(struct hpet_dev *hdev, struct msi_msg *msg); 82extern void hpet_msi_write(struct hpet_dev *hdev, struct msi_msg *msg);
81extern void hpet_msi_read(struct hpet_dev *hdev, struct msi_msg *msg); 83extern void hpet_msi_read(struct hpet_dev *hdev, struct msi_msg *msg);
84extern struct irq_domain *hpet_create_irq_domain(int hpet_id);
85extern 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
84extern int default_setup_hpet_msi(unsigned int irq, unsigned int id); 89extern 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
54static 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
82static 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
54static int msi_compose_msg(struct pci_dev *pdev, unsigned int irq, 92static 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
280static inline int hpet_dev_id(struct irq_domain *domain)
281{
282 return (int)(long)domain->host_data;
283}
242 284
243static int hpet_msi_set_affinity(struct irq_data *data, 285static 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
267static struct irq_chip hpet_msi_type = { 302static 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
277int default_setup_hpet_msi(unsigned int irq, unsigned int id) 314int 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
332static 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
357static 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
365static 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
374static 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
383static 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
390struct 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
409int 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
309static int hpet_setup_msi_irq(unsigned int irq);
310
311static void hpet_set_mode(enum clock_event_mode mode, 309static 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
425static DEFINE_PER_CPU(struct hpet_dev *, cpu_hpet_dev); 423static DEFINE_PER_CPU(struct hpet_dev *, cpu_hpet_dev);
426static struct hpet_dev *hpet_devs; 424static struct hpet_dev *hpet_devs;
425static struct irq_domain *hpet_domain;
427 426
428void hpet_msi_unmask(struct irq_data *data) 427void 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
477static 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
486static 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
503static irqreturn_t hpet_interrupt_handler(int irq, void *data) 476static 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
714static int hpet_setup_msi_irq(unsigned int irq)
715{
716 return 0;
717}
718static void hpet_msi_capability_lookup(unsigned int start_timer) 689static void hpet_msi_capability_lookup(unsigned int start_timer)
719{ 690{
720 return; 691 return;