X-Git-Url: http://git.onelab.eu/?a=blobdiff_plain;f=drivers%2Fchar%2Fmmtimer.c;h=c09160383a5332c9635c4813215dabd9063bade8;hb=97bf2856c6014879bd04983a3e9dfcdac1e7fe85;hp=3539b4acfc94d4d4bbc5cce6cb1d3494f3006534;hpb=6a77f38946aaee1cd85eeec6cf4229b204c15071;p=linux-2.6.git diff --git a/drivers/char/mmtimer.c b/drivers/char/mmtimer.c index 3539b4acf..c09160383 100644 --- a/drivers/char/mmtimer.c +++ b/drivers/char/mmtimer.c @@ -1,11 +1,11 @@ /* - * Intel Multimedia Timer device implementation for SGI SN platforms. + * Timer device implementation for SGI SN platforms. * * This file is subject to the terms and conditions of the GNU General Public * License. See the file "COPYING" in the main directory of this archive * for more details. * - * Copyright (c) 2001-2004 Silicon Graphics, Inc. All rights reserved. + * Copyright (c) 2001-2006 Silicon Graphics, Inc. All rights reserved. * * This driver exports an API that should be supportable by any HPET or IA-PC * multimedia timer. The code below is currently specific to the SGI Altix @@ -25,7 +25,6 @@ #include #include #include -#include #include #include #include @@ -45,7 +44,7 @@ MODULE_LICENSE("GPL"); /* name of the device, usually in /dev */ #define MMTIMER_NAME "mmtimer" #define MMTIMER_DESC "SGI Altix RTC Timer" -#define MMTIMER_VERSION "2.0" +#define MMTIMER_VERSION "2.1" #define RTC_BITS 55 /* 55 bits for this implementation */ @@ -64,17 +63,12 @@ static int mmtimer_mmap(struct file *file, struct vm_area_struct *vma); */ static unsigned long mmtimer_femtoperiod = 0; -static struct file_operations mmtimer_fops = { +static const struct file_operations mmtimer_fops = { .owner = THIS_MODULE, .mmap = mmtimer_mmap, .ioctl = mmtimer_ioctl, }; -/* - * Comparators and their associated info. Shub has - * three comparison registers. - */ - /* * We only have comparison registers RTC1-4 currently available per * node. RTC0 is used by SAL. @@ -174,14 +168,10 @@ static void inline mmtimer_setup_int_2(u64 expires) * This function must be called with interrupts disabled and preemption off * in order to insure that the setup succeeds in a deterministic time frame. * It will check if the interrupt setup succeeded. - * mmtimer_setup will return the cycles that we were too late if the - * initialization failed. */ static int inline mmtimer_setup(int comparator, unsigned long expires) { - long diff; - switch (comparator) { case 0: mmtimer_setup_int_0(expires); @@ -194,17 +184,14 @@ static int inline mmtimer_setup(int comparator, unsigned long expires) break; } /* We might've missed our expiration time */ - diff = rtc_time() - expires; - if (diff > 0) { - if (mmtimer_int_pending(comparator)) { - /* We'll get an interrupt for this once we're done */ - return 0; - } - /* Looks like we missed it */ - return diff; - } + if (rtc_time() < expires) + return 1; - return 0; + /* + * If an interrupt is already pending then its okay + * if not then we failed + */ + return mmtimer_int_pending(comparator); } static int inline mmtimer_disable_int(long nasid, int comparator) @@ -239,10 +226,7 @@ typedef struct mmtimer { struct tasklet_struct tasklet; } mmtimer_t; -/* - * Total number of comparators is comparators/node * MAX nodes/running kernel - */ -static mmtimer_t timers[NUM_COMPARATORS*MAX_COMPACT_NODES]; +static mmtimer_t ** timers; /** * mmtimer_ioctl - ioctl interface for /dev/mmtimer @@ -344,7 +328,6 @@ static int mmtimer_mmap(struct file *file, struct vm_area_struct *vma) if (PAGE_SIZE > (1 << 16)) return -ENOSYS; - vma->vm_flags |= (VM_IO | VM_SHM | VM_LOCKED ); vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot); mmtimer_addr = __pa(RTC_COUNTER_ADDR); @@ -376,7 +359,7 @@ static int sgi_clock_period; static struct timespec sgi_clock_offset; static int sgi_clock_period; -static int sgi_clock_get(struct timespec *tp) +static int sgi_clock_get(clockid_t clockid, struct timespec *tp) { u64 nsec; @@ -387,7 +370,7 @@ static int sgi_clock_get(struct timespec *tp) return 0; }; -static int sgi_clock_set(struct timespec *tp) +static int sgi_clock_set(clockid_t clockid, struct timespec *tp) { u64 nsec; @@ -418,19 +401,19 @@ static int inline reschedule_periodic_timer(mmtimer_t *x) int n; struct k_itimer *t = x->timer; - t->it_timer.magic = x->i; + t->it.mmtimer.clock = x->i; t->it_overrun--; n = 0; do { - t->it_timer.expires += t->it_incr << n; + t->it.mmtimer.expires += t->it.mmtimer.incr << n; t->it_overrun += 1 << n; n++; if (n > 20) return 1; - } while (mmtimer_setup(x->i, t->it_timer.expires)); + } while (!mmtimer_setup(x->i, t->it.mmtimer.expires)); return 0; } @@ -439,7 +422,6 @@ static int inline reschedule_periodic_timer(mmtimer_t *x) * mmtimer_interrupt - timer interrupt handler * @irq: irq received * @dev_id: device the irq came from - * @regs: register state upon receipt of the interrupt * * Called when one of the comarators matches the counter, This * routine will send signals to processes that have requested @@ -450,32 +432,32 @@ static int inline reschedule_periodic_timer(mmtimer_t *x) * registers. */ static irqreturn_t -mmtimer_interrupt(int irq, void *dev_id, struct pt_regs *regs) +mmtimer_interrupt(int irq, void *dev_id) { int i; - mmtimer_t *base = timers + cpuid_to_cnodeid(smp_processor_id()) * - NUM_COMPARATORS; unsigned long expires = 0; int result = IRQ_NONE; + unsigned indx = cpu_to_node(smp_processor_id()); /* * Do this once for each comparison register */ for (i = 0; i < NUM_COMPARATORS; i++) { + mmtimer_t *base = timers[indx] + i; /* Make sure this doesn't get reused before tasklet_sched */ - spin_lock(&base[i].lock); - if (base[i].cpu == smp_processor_id()) { - if (base[i].timer) - expires = base[i].timer->it_timer.expires; + spin_lock(&base->lock); + if (base->cpu == smp_processor_id()) { + if (base->timer) + expires = base->timer->it.mmtimer.expires; /* expires test won't work with shared irqs */ if ((mmtimer_int_pending(i) > 0) || (expires && (expires < rtc_time()))) { mmtimer_clr_int_pending(i); - tasklet_schedule(&base[i].tasklet); + tasklet_schedule(&base->tasklet); result = IRQ_HANDLED; } } - spin_unlock(&base[i].lock); + spin_unlock(&base->lock); expires = 0; } return result; @@ -497,13 +479,13 @@ void mmtimer_tasklet(unsigned long data) { goto out; t->it_overrun = 0; - if (tasklist_lock.write_lock || posix_timer_event(t, 0) != 0) { + if (posix_timer_event(t, 0) != 0) { // printk(KERN_WARNING "mmtimer: cannot deliver signal.\n"); t->it_overrun++; } - if(t->it_incr) { + if(t->it.mmtimer.incr) { /* Periodic timer */ if (reschedule_periodic_timer(x)) { printk(KERN_WARNING "mmtimer: unable to reschedule\n"); @@ -511,7 +493,7 @@ void mmtimer_tasklet(unsigned long data) { } } else { /* Ensure we don't false trigger in mmtimer_interrupt */ - t->it_timer.expires = 0; + t->it.mmtimer.expires = 0; } t->it_overrun_last = t->it_overrun; out: @@ -522,7 +504,7 @@ out: static int sgi_timer_create(struct k_itimer *timer) { /* Insure that a newly created timer is off */ - timer->it_timer.magic = TIMER_OFF; + timer->it.mmtimer.clock = TIMER_OFF; return 0; } @@ -533,17 +515,17 @@ static int sgi_timer_create(struct k_itimer *timer) */ static int sgi_timer_del(struct k_itimer *timr) { - int i = timr->it_timer.magic; - cnodeid_t nodeid = timr->it_timer.data; - mmtimer_t *t = timers + nodeid * NUM_COMPARATORS +i; + int i = timr->it.mmtimer.clock; + cnodeid_t nodeid = timr->it.mmtimer.node; + mmtimer_t *t = timers[nodeid] + i; unsigned long irqflags; if (i != TIMER_OFF) { spin_lock_irqsave(&t->lock, irqflags); mmtimer_disable_int(cnodeid_to_nasid(nodeid),i); t->timer = NULL; - timr->it_timer.magic = TIMER_OFF; - timr->it_timer.expires = 0; + timr->it.mmtimer.clock = TIMER_OFF; + timr->it.mmtimer.expires = 0; spin_unlock_irqrestore(&t->lock, irqflags); } return 0; @@ -556,7 +538,7 @@ static int sgi_timer_del(struct k_itimer *timr) static void sgi_timer_get(struct k_itimer *timr, struct itimerspec *cur_setting) { - if (timr->it_timer.magic == TIMER_OFF) { + if (timr->it.mmtimer.clock == TIMER_OFF) { cur_setting->it_interval.tv_nsec = 0; cur_setting->it_interval.tv_sec = 0; cur_setting->it_value.tv_nsec = 0; @@ -564,8 +546,8 @@ static void sgi_timer_get(struct k_itimer *timr, struct itimerspec *cur_setting) return; } - ns_to_timespec(cur_setting->it_interval, timr->it_incr * sgi_clock_period); - ns_to_timespec(cur_setting->it_value, (timr->it_timer.expires - rtc_time())* sgi_clock_period); + ns_to_timespec(cur_setting->it_interval, timr->it.mmtimer.incr * sgi_clock_period); + ns_to_timespec(cur_setting->it_value, (timr->it.mmtimer.expires - rtc_time())* sgi_clock_period); return; } @@ -594,9 +576,15 @@ static int sgi_timer_set(struct k_itimer *timr, int flags, if (flags & TIMER_ABSTIME) { struct timespec n; + unsigned long now; getnstimeofday(&n); - when -= timespec_to_ns(n); + now = timespec_to_ns(n); + if (when > now) + when -= now; + else + /* Fire the timer immediately */ + when = 0; } /* @@ -614,12 +602,12 @@ static int sgi_timer_set(struct k_itimer *timr, int flags, */ preempt_disable(); - nodeid = cpuid_to_cnodeid(smp_processor_id()); - base = timers + nodeid * NUM_COMPARATORS; + nodeid = cpu_to_node(smp_processor_id()); retry: /* Don't use an allocated timer, or a deleted one that's pending */ for(i = 0; i< NUM_COMPARATORS; i++) { - if (!base[i].timer && !base[i].tasklet.state) { + base = timers[nodeid] + i; + if (!base->timer && !base->tasklet.state) { break; } } @@ -629,33 +617,33 @@ retry: return -EBUSY; } - spin_lock_irqsave(&base[i].lock, irqflags); + spin_lock_irqsave(&base->lock, irqflags); - if (base[i].timer || base[i].tasklet.state != 0) { - spin_unlock_irqrestore(&base[i].lock, irqflags); + if (base->timer || base->tasklet.state != 0) { + spin_unlock_irqrestore(&base->lock, irqflags); goto retry; } - base[i].timer = timr; - base[i].cpu = smp_processor_id(); + base->timer = timr; + base->cpu = smp_processor_id(); - timr->it_timer.magic = i; - timr->it_timer.data = nodeid; - timr->it_incr = period; - timr->it_timer.expires = when; + timr->it.mmtimer.clock = i; + timr->it.mmtimer.node = nodeid; + timr->it.mmtimer.incr = period; + timr->it.mmtimer.expires = when; if (period == 0) { - if (mmtimer_setup(i, when)) { + if (!mmtimer_setup(i, when)) { mmtimer_disable_int(-1, i); posix_timer_event(timr, 0); - timr->it_timer.expires = 0; + timr->it.mmtimer.expires = 0; } } else { - timr->it_timer.expires -= period; - if (reschedule_periodic_timer(base+i)) + timr->it.mmtimer.expires -= period; + if (reschedule_periodic_timer(base)) err = -EINVAL; } - spin_unlock_irqrestore(&base[i].lock, irqflags); + spin_unlock_irqrestore(&base->lock, irqflags); preempt_enable(); @@ -681,9 +669,10 @@ static struct k_clock sgi_clock = { static int __init mmtimer_init(void) { unsigned i; + cnodeid_t node, maxn = -1; if (!ia64_platform_is("sn2")) - return -1; + return 0; /* * Sanity check the cycles/sec variable @@ -691,31 +680,58 @@ static int __init mmtimer_init(void) if (sn_rtc_cycles_per_second < 100000) { printk(KERN_ERR "%s: unable to determine clock frequency\n", MMTIMER_NAME); - return -1; + goto out1; } mmtimer_femtoperiod = ((unsigned long)1E15 + sn_rtc_cycles_per_second / 2) / sn_rtc_cycles_per_second; - for (i=0; i< NUM_COMPARATORS*MAX_COMPACT_NODES; i++) { - spin_lock_init(&timers[i].lock); - timers[i].timer = NULL; - timers[i].cpu = 0; - timers[i].i = i % NUM_COMPARATORS; - tasklet_init(&timers[i].tasklet, mmtimer_tasklet, (unsigned long) (timers+i)); - } - - if (request_irq(SGI_MMTIMER_VECTOR, mmtimer_interrupt, SA_PERCPU_IRQ, MMTIMER_NAME, NULL)) { + if (request_irq(SGI_MMTIMER_VECTOR, mmtimer_interrupt, IRQF_PERCPU, MMTIMER_NAME, NULL)) { printk(KERN_WARNING "%s: unable to allocate interrupt.", MMTIMER_NAME); - return -1; + goto out1; } - strcpy(mmtimer_miscdev.devfs_name, MMTIMER_NAME); if (misc_register(&mmtimer_miscdev)) { printk(KERN_ERR "%s: failed to register device\n", MMTIMER_NAME); - return -1; + goto out2; + } + + /* Get max numbered node, calculate slots needed */ + for_each_online_node(node) { + maxn = node; + } + maxn++; + + /* Allocate list of node ptrs to mmtimer_t's */ + timers = kmalloc(sizeof(mmtimer_t *)*maxn, GFP_KERNEL); + if (timers == NULL) { + printk(KERN_ERR "%s: failed to allocate memory for device\n", + MMTIMER_NAME); + goto out3; + } + + memset(timers,0,(sizeof(mmtimer_t *)*maxn)); + + /* Allocate mmtimer_t's for each online node */ + for_each_online_node(node) { + timers[node] = kmalloc_node(sizeof(mmtimer_t)*NUM_COMPARATORS, GFP_KERNEL, node); + if (timers[node] == NULL) { + printk(KERN_ERR "%s: failed to allocate memory for device\n", + MMTIMER_NAME); + goto out4; + } + for (i=0; i< NUM_COMPARATORS; i++) { + mmtimer_t * base = timers[node] + i; + + spin_lock_init(&base->lock); + base->timer = NULL; + base->cpu = 0; + base->i = i; + tasklet_init(&base->tasklet, mmtimer_tasklet, + (unsigned long) (base)); + } } sgi_clock_period = sgi_clock.res = NSEC_PER_SEC / sn_rtc_cycles_per_second; @@ -725,6 +741,17 @@ static int __init mmtimer_init(void) sn_rtc_cycles_per_second/(unsigned long)1E6); return 0; + +out4: + for_each_online_node(node) { + kfree(timers[node]); + } +out3: + misc_deregister(&mmtimer_miscdev); +out2: + free_irq(SGI_MMTIMER_VECTOR, NULL); +out1: + return -1; } module_init(mmtimer_init);