diff options
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/char/mmtimer.c | 90 |
1 files changed, 57 insertions, 33 deletions
diff --git a/drivers/char/mmtimer.c b/drivers/char/mmtimer.c index 78c89a3e7825..c92378121b4c 100644 --- a/drivers/char/mmtimer.c +++ b/drivers/char/mmtimer.c | |||
@@ -1,11 +1,11 @@ | |||
1 | /* | 1 | /* |
2 | * Intel Multimedia Timer device implementation for SGI SN platforms. | 2 | * Timer device implementation for SGI SN platforms. |
3 | * | 3 | * |
4 | * This file is subject to the terms and conditions of the GNU General Public | 4 | * This file is subject to the terms and conditions of the GNU General Public |
5 | * License. See the file "COPYING" in the main directory of this archive | 5 | * License. See the file "COPYING" in the main directory of this archive |
6 | * for more details. | 6 | * for more details. |
7 | * | 7 | * |
8 | * Copyright (c) 2001-2004 Silicon Graphics, Inc. All rights reserved. | 8 | * Copyright (c) 2001-2006 Silicon Graphics, Inc. All rights reserved. |
9 | * | 9 | * |
10 | * This driver exports an API that should be supportable by any HPET or IA-PC | 10 | * This driver exports an API that should be supportable by any HPET or IA-PC |
11 | * multimedia timer. The code below is currently specific to the SGI Altix | 11 | * multimedia timer. The code below is currently specific to the SGI Altix |
@@ -45,7 +45,7 @@ MODULE_LICENSE("GPL"); | |||
45 | /* name of the device, usually in /dev */ | 45 | /* name of the device, usually in /dev */ |
46 | #define MMTIMER_NAME "mmtimer" | 46 | #define MMTIMER_NAME "mmtimer" |
47 | #define MMTIMER_DESC "SGI Altix RTC Timer" | 47 | #define MMTIMER_DESC "SGI Altix RTC Timer" |
48 | #define MMTIMER_VERSION "2.0" | 48 | #define MMTIMER_VERSION "2.1" |
49 | 49 | ||
50 | #define RTC_BITS 55 /* 55 bits for this implementation */ | 50 | #define RTC_BITS 55 /* 55 bits for this implementation */ |
51 | 51 | ||
@@ -227,10 +227,7 @@ typedef struct mmtimer { | |||
227 | struct tasklet_struct tasklet; | 227 | struct tasklet_struct tasklet; |
228 | } mmtimer_t; | 228 | } mmtimer_t; |
229 | 229 | ||
230 | /* | 230 | static mmtimer_t ** timers; |
231 | * Total number of comparators is comparators/node * MAX nodes/running kernel | ||
232 | */ | ||
233 | static mmtimer_t timers[NUM_COMPARATORS*MAX_COMPACT_NODES]; | ||
234 | 231 | ||
235 | /** | 232 | /** |
236 | * mmtimer_ioctl - ioctl interface for /dev/mmtimer | 233 | * mmtimer_ioctl - ioctl interface for /dev/mmtimer |
@@ -441,29 +438,29 @@ static irqreturn_t | |||
441 | mmtimer_interrupt(int irq, void *dev_id, struct pt_regs *regs) | 438 | mmtimer_interrupt(int irq, void *dev_id, struct pt_regs *regs) |
442 | { | 439 | { |
443 | int i; | 440 | int i; |
444 | mmtimer_t *base = timers + cpu_to_node(smp_processor_id()) * | ||
445 | NUM_COMPARATORS; | ||
446 | unsigned long expires = 0; | 441 | unsigned long expires = 0; |
447 | int result = IRQ_NONE; | 442 | int result = IRQ_NONE; |
443 | unsigned indx = cpu_to_node(smp_processor_id()); | ||
448 | 444 | ||
449 | /* | 445 | /* |
450 | * Do this once for each comparison register | 446 | * Do this once for each comparison register |
451 | */ | 447 | */ |
452 | for (i = 0; i < NUM_COMPARATORS; i++) { | 448 | for (i = 0; i < NUM_COMPARATORS; i++) { |
449 | mmtimer_t *base = timers[indx] + i; | ||
453 | /* Make sure this doesn't get reused before tasklet_sched */ | 450 | /* Make sure this doesn't get reused before tasklet_sched */ |
454 | spin_lock(&base[i].lock); | 451 | spin_lock(&base->lock); |
455 | if (base[i].cpu == smp_processor_id()) { | 452 | if (base->cpu == smp_processor_id()) { |
456 | if (base[i].timer) | 453 | if (base->timer) |
457 | expires = base[i].timer->it.mmtimer.expires; | 454 | expires = base->timer->it.mmtimer.expires; |
458 | /* expires test won't work with shared irqs */ | 455 | /* expires test won't work with shared irqs */ |
459 | if ((mmtimer_int_pending(i) > 0) || | 456 | if ((mmtimer_int_pending(i) > 0) || |
460 | (expires && (expires < rtc_time()))) { | 457 | (expires && (expires < rtc_time()))) { |
461 | mmtimer_clr_int_pending(i); | 458 | mmtimer_clr_int_pending(i); |
462 | tasklet_schedule(&base[i].tasklet); | 459 | tasklet_schedule(&base->tasklet); |
463 | result = IRQ_HANDLED; | 460 | result = IRQ_HANDLED; |
464 | } | 461 | } |
465 | } | 462 | } |
466 | spin_unlock(&base[i].lock); | 463 | spin_unlock(&base->lock); |
467 | expires = 0; | 464 | expires = 0; |
468 | } | 465 | } |
469 | return result; | 466 | return result; |
@@ -523,7 +520,7 @@ static int sgi_timer_del(struct k_itimer *timr) | |||
523 | { | 520 | { |
524 | int i = timr->it.mmtimer.clock; | 521 | int i = timr->it.mmtimer.clock; |
525 | cnodeid_t nodeid = timr->it.mmtimer.node; | 522 | cnodeid_t nodeid = timr->it.mmtimer.node; |
526 | mmtimer_t *t = timers + nodeid * NUM_COMPARATORS +i; | 523 | mmtimer_t *t = timers[nodeid] + i; |
527 | unsigned long irqflags; | 524 | unsigned long irqflags; |
528 | 525 | ||
529 | if (i != TIMER_OFF) { | 526 | if (i != TIMER_OFF) { |
@@ -609,11 +606,11 @@ static int sgi_timer_set(struct k_itimer *timr, int flags, | |||
609 | preempt_disable(); | 606 | preempt_disable(); |
610 | 607 | ||
611 | nodeid = cpu_to_node(smp_processor_id()); | 608 | nodeid = cpu_to_node(smp_processor_id()); |
612 | base = timers + nodeid * NUM_COMPARATORS; | ||
613 | retry: | 609 | retry: |
614 | /* Don't use an allocated timer, or a deleted one that's pending */ | 610 | /* Don't use an allocated timer, or a deleted one that's pending */ |
615 | for(i = 0; i< NUM_COMPARATORS; i++) { | 611 | for(i = 0; i< NUM_COMPARATORS; i++) { |
616 | if (!base[i].timer && !base[i].tasklet.state) { | 612 | base = timers[nodeid] + i; |
613 | if (!base->timer && !base->tasklet.state) { | ||
617 | break; | 614 | break; |
618 | } | 615 | } |
619 | } | 616 | } |
@@ -623,14 +620,14 @@ retry: | |||
623 | return -EBUSY; | 620 | return -EBUSY; |
624 | } | 621 | } |
625 | 622 | ||
626 | spin_lock_irqsave(&base[i].lock, irqflags); | 623 | spin_lock_irqsave(&base->lock, irqflags); |
627 | 624 | ||
628 | if (base[i].timer || base[i].tasklet.state != 0) { | 625 | if (base->timer || base->tasklet.state != 0) { |
629 | spin_unlock_irqrestore(&base[i].lock, irqflags); | 626 | spin_unlock_irqrestore(&base->lock, irqflags); |
630 | goto retry; | 627 | goto retry; |
631 | } | 628 | } |
632 | base[i].timer = timr; | 629 | base->timer = timr; |
633 | base[i].cpu = smp_processor_id(); | 630 | base->cpu = smp_processor_id(); |
634 | 631 | ||
635 | timr->it.mmtimer.clock = i; | 632 | timr->it.mmtimer.clock = i; |
636 | timr->it.mmtimer.node = nodeid; | 633 | timr->it.mmtimer.node = nodeid; |
@@ -645,11 +642,11 @@ retry: | |||
645 | } | 642 | } |
646 | } else { | 643 | } else { |
647 | timr->it.mmtimer.expires -= period; | 644 | timr->it.mmtimer.expires -= period; |
648 | if (reschedule_periodic_timer(base+i)) | 645 | if (reschedule_periodic_timer(base)) |
649 | err = -EINVAL; | 646 | err = -EINVAL; |
650 | } | 647 | } |
651 | 648 | ||
652 | spin_unlock_irqrestore(&base[i].lock, irqflags); | 649 | spin_unlock_irqrestore(&base->lock, irqflags); |
653 | 650 | ||
654 | preempt_enable(); | 651 | preempt_enable(); |
655 | 652 | ||
@@ -675,6 +672,7 @@ static struct k_clock sgi_clock = { | |||
675 | static int __init mmtimer_init(void) | 672 | static int __init mmtimer_init(void) |
676 | { | 673 | { |
677 | unsigned i; | 674 | unsigned i; |
675 | cnodeid_t node, maxn = -1; | ||
678 | 676 | ||
679 | if (!ia64_platform_is("sn2")) | 677 | if (!ia64_platform_is("sn2")) |
680 | return -1; | 678 | return -1; |
@@ -691,14 +689,6 @@ static int __init mmtimer_init(void) | |||
691 | mmtimer_femtoperiod = ((unsigned long)1E15 + sn_rtc_cycles_per_second / | 689 | mmtimer_femtoperiod = ((unsigned long)1E15 + sn_rtc_cycles_per_second / |
692 | 2) / sn_rtc_cycles_per_second; | 690 | 2) / sn_rtc_cycles_per_second; |
693 | 691 | ||
694 | for (i=0; i< NUM_COMPARATORS*MAX_COMPACT_NODES; i++) { | ||
695 | spin_lock_init(&timers[i].lock); | ||
696 | timers[i].timer = NULL; | ||
697 | timers[i].cpu = 0; | ||
698 | timers[i].i = i % NUM_COMPARATORS; | ||
699 | tasklet_init(&timers[i].tasklet, mmtimer_tasklet, (unsigned long) (timers+i)); | ||
700 | } | ||
701 | |||
702 | if (request_irq(SGI_MMTIMER_VECTOR, mmtimer_interrupt, SA_PERCPU_IRQ, MMTIMER_NAME, NULL)) { | 692 | if (request_irq(SGI_MMTIMER_VECTOR, mmtimer_interrupt, SA_PERCPU_IRQ, MMTIMER_NAME, NULL)) { |
703 | printk(KERN_WARNING "%s: unable to allocate interrupt.", | 693 | printk(KERN_WARNING "%s: unable to allocate interrupt.", |
704 | MMTIMER_NAME); | 694 | MMTIMER_NAME); |
@@ -712,6 +702,40 @@ static int __init mmtimer_init(void) | |||
712 | return -1; | 702 | return -1; |
713 | } | 703 | } |
714 | 704 | ||
705 | /* Get max numbered node, calculate slots needed */ | ||
706 | for_each_online_node(node) { | ||
707 | maxn = node; | ||
708 | } | ||
709 | maxn++; | ||
710 | |||
711 | /* Allocate list of node ptrs to mmtimer_t's */ | ||
712 | timers = kmalloc(sizeof(mmtimer_t *)*maxn, GFP_KERNEL); | ||
713 | if (timers == NULL) { | ||
714 | printk(KERN_ERR "%s: failed to allocate memory for device\n", | ||
715 | MMTIMER_NAME); | ||
716 | return -1; | ||
717 | } | ||
718 | |||
719 | /* Allocate mmtimer_t's for each online node */ | ||
720 | for_each_online_node(node) { | ||
721 | timers[node] = kmalloc_node(sizeof(mmtimer_t)*NUM_COMPARATORS, GFP_KERNEL, node); | ||
722 | if (timers[node] == NULL) { | ||
723 | printk(KERN_ERR "%s: failed to allocate memory for device\n", | ||
724 | MMTIMER_NAME); | ||
725 | return -1; | ||
726 | } | ||
727 | for (i=0; i< NUM_COMPARATORS; i++) { | ||
728 | mmtimer_t * base = timers[node] + i; | ||
729 | |||
730 | spin_lock_init(&base->lock); | ||
731 | base->timer = NULL; | ||
732 | base->cpu = 0; | ||
733 | base->i = i; | ||
734 | tasklet_init(&base->tasklet, mmtimer_tasklet, | ||
735 | (unsigned long) (base)); | ||
736 | } | ||
737 | } | ||
738 | |||
715 | sgi_clock_period = sgi_clock.res = NSEC_PER_SEC / sn_rtc_cycles_per_second; | 739 | sgi_clock_period = sgi_clock.res = NSEC_PER_SEC / sn_rtc_cycles_per_second; |
716 | register_posix_clock(CLOCK_SGI_CYCLE, &sgi_clock); | 740 | register_posix_clock(CLOCK_SGI_CYCLE, &sgi_clock); |
717 | 741 | ||