Index: linux-2.6.9-rc4/include/linux/time.h =================================================================== --- linux-2.6.9-rc4.orig/include/linux/time.h 2004-10-13 10:25:48.000000000 -0700 +++ linux-2.6.9-rc4/include/linux/time.h 2004-10-13 14:47:02.000000000 -0700 @@ -418,7 +418,7 @@ /* * The IDs of various hardware clocks */ - +#define CLOCK_SGI_CYCLE 10 #define MAX_CLOCKS 16 #define CLOCKS_MASK (CLOCK_REALTIME | CLOCK_MONOTONIC | \ Index: linux-2.6.9-rc4/drivers/char/mmtimer.c =================================================================== --- linux-2.6.9-rc4.orig/drivers/char/mmtimer.c 2004-10-10 19:58:07.000000000 -0700 +++ linux-2.6.9-rc4/drivers/char/mmtimer.c 2004-10-13 19:11:24.000000000 -0700 @@ -13,6 +13,8 @@ * * 11/01/01 - jbarnes - initial revision * 9/10/04 - Christoph Lameter - remove interrupt support for kernel inclusion + * 10/1/04 - Christoph Lameter - provide posix clock CLOCK_SGI_CYCLE + * 10/13/04 - Christoph Lameter - provide timer interrupt support */ #include @@ -28,18 +30,22 @@ #include #include #include +#include +#include MODULE_AUTHOR("Jesse Barnes "); -MODULE_DESCRIPTION("Multimedia timer support"); +MODULE_DESCRIPTION("SGI Altix RTC Timer"); MODULE_LICENSE("GPL"); /* name of the device, usually in /dev */ #define MMTIMER_NAME "mmtimer" -#define MMTIMER_DESC "IA-PC Multimedia Timer" -#define MMTIMER_VERSION "1.0" +#define MMTIMER_DESC "SGI Altix RTC Timer" +#define MMTIMER_VERSION "2.0" #define RTC_BITS 55 /* 55 bits for this implementation */ +#define IA64_IRQ_RT 0xfc /* RT TEST IRQ */ + static int mmtimer_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg); static int mmtimer_mmap(struct file *file, struct vm_area_struct *vma); @@ -55,6 +61,41 @@ .ioctl = mmtimer_ioctl, }; +/* + * Comparators and their associated info. Bedrock has + * two comparison registers. + */ + +#define NUM_COMPARATORS 2 /* two comparison registers in SN1 */ +/* + * Check for an interrupt and clear the pending bit if + * one is waiting. +*/ +#define MMTIMER_INT_PENDING(x) (x ? *(RTC_INT_PENDING_B_ADDR) : *(RTC_INT_PENDING_A_ADDR)) + +/* + * Set interrupts on RTC 'x' to 'v' (true or false) + */ +#define MMTIMER_SET_INT(x,v) (x ? (*(RTC_INT_ENABLED_B_ADDR) = (unsigned long)(v)) : (*(RTC_INT_ENABLED_A_ADDR) = (unsigned long)(v))) + +#define MMTIMER_ENABLE_INT(x) MMTIMER_SET_INT(x, 1) +#define MMTIMER_DISABLE_INT(x) MMTIMER_SET_INT(x, 0) + +void inline remote_mmtimer_disable_int(int y, int x) { + int nas = cpuid_to_nasid(y); + unsigned long addr = (x) ? (unsigned long) RTC_INT_ENABLED_B_ADDR : (unsigned long)RTC_INT_ENABLED_A_ADDR; + + REMOTE_HUB_S(nas, addr, 0); +} + +typedef struct mmtimer { + volatile unsigned long *compare; + struct k_itimer *timer; + int interrupt_setup; +} mmtimer_t; + +static mmtimer_t timers[NUM_COMPARATORS*NR_CPUS]; + /** * mmtimer_ioctl - ioctl interface for /dev/mmtimer * @inode: inode of the device @@ -177,6 +218,208 @@ &mmtimer_fops }; +/***** + * Posix Timer Interface + * The posix timer interface provides full support for the posix timer + * interface and allows the scheduling of up to two timers. + */ + +static struct timespec sgi_clock_offset; +static int sgi_clock_period; + +static int sgi_clock_get(struct timespec *tp) { + u64 nsec; + + nsec = readq(RTC_COUNTER_ADDR) * sgi_clock_period + + sgi_clock_offset.tv_nsec; + tp->tv_sec = div_long_long_rem(nsec, NSEC_PER_SEC, &tp->tv_nsec) + + sgi_clock_offset.tv_sec; + return 0; +}; + +static int sgi_clock_set(struct timespec *tp) { + + u64 nsec; + u64 rem; + + nsec = readq(RTC_COUNTER_ADDR) * sgi_clock_period; + + sgi_clock_offset.tv_sec = tp->tv_sec - div_long_long_rem(nsec, NSEC_PER_SEC, &rem); + + if (rem <= tp->tv_nsec) + sgi_clock_offset.tv_nsec = tp->tv_sec - rem; + else { + sgi_clock_offset.tv_nsec = tp->tv_sec + NSEC_PER_SEC - rem; + sgi_clock_offset.tv_sec--; + } + return 0; +} + +/* This does not really delete a timer. It just insures + * that the timer is not active + */ +static int sgi_timer_del(struct k_itimer *timr) { + + if (timr->it_timer.data >=0) { + /* Switch active timer off */ + if (smp_processor_id() == timr->it_timer.data / NUM_COMPARATORS) + + MMTIMER_DISABLE_INT(timr->it_timer.data % NUM_COMPARATORS); + else + remote_mmtimer_disable_int(timr->it_timer.data / NUM_COMPARATORS, timr->it_timer.data % NUM_COMPARATORS); + printk("mmtimer: interrupt %ld disabled.\n",timr->it_timer.data); + timers[timr->it_timer.data].timer = NULL; + } + return 0; +} + +/** + * 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 + * them. + */ +static irqreturn_t +mmtimer_interrupt(int irq, void *dev_id, struct pt_regs *regs) +{ + int i; + int result = IRQ_NONE; + int base = smp_processor_id() * NUM_COMPARATORS; + + printk("mmtimer interrupt:\n"); + /* + * Do this once for each comparison register + */ + for(i = base;i < base + NUM_COMPARATORS; i++) { + if(MMTIMER_INT_PENDING(i % NUM_COMPARATORS)) { + struct k_itimer *t = timers[i].timer; + unsigned long flags; + + printk("mmtimer: processing int on %d\n",i); + spin_lock_irqsave(&t->it_lock, flags); + + if (timer_event(t, 0) == 0) { + if(t->it_incr) + *(timers[i].compare) += t->it_incr; + else + sgi_timer_del(t); + } else { + printk(KERN_WARNING "mmtimer unable to deliver signal"); + sgi_timer_del(t); + t->it_overrun++; + } + + spin_unlock_irqrestore(&t->it_lock, flags); + result = IRQ_HANDLED; + } + } + return result; +} + +static int sgi_timer_create(struct k_itimer *timer) { + /* Insure that a newly created structure is empty */ + timer->it_timer.data = -1; + return 0; +} + +#define timespec_to_ns(x) ((x).tv_nsec + (x).tv_sec * NSEC_PER_SEC) +#define ns_to_timespec(ts, nsec) (ts).tv_sec = div_long_long_rem(nsec, NSEC_PER_SEC, &(ts).tv_nsec) + +static void sgi_timer_get(struct k_itimer *timr, struct itimerspec *cur_setting) { + + int i = timr->it_timer.data; + + if (i < 0) { + /* Timer not active */ + cur_setting->it_interval.tv_nsec = 0; + cur_setting->it_interval.tv_sec = 0; + cur_setting->it_value.tv_nsec = 0; + cur_setting->it_value.tv_sec =0; + return; + } + + ns_to_timespec(cur_setting->it_interval, timr->it_incr * sgi_clock_period); + ns_to_timespec(cur_setting->it_value, (*(timers[i].compare) - readq(RTC_COUNTER_ADDR))* sgi_clock_period); + return; +} + + +static int sgi_timer_set(struct k_itimer *timr, int flags, + struct itimerspec * new_setting, + struct itimerspec * old_setting) { + + int i; + int base = smp_processor_id() * NUM_COMPARATORS; + unsigned long when; + + if (old_setting) sgi_timer_get(timr, old_setting); + + when = timespec_to_ns(new_setting->it_value); + if (when == 0) { + /* Clear timer */ + sgi_timer_del(timr); + return 0; + } + /* setup absolute time of when to fire? */ + if (flags & TIMER_ABSTIME) { + struct timespec n; + + getnstimeofday(&n); + when -= timespec_to_ns(n); + } + + /* If the time is less than 100ns fire now and do not set up */ + if (when < 100) { + timer_event(timr, 0); + return 0; + } + + for(i = base; i< base + NUM_COMPARATORS; i++) + if (!timers[i].timer) + break; + + if (i == base + NUM_COMPARATORS) + return -EBUSY; + + timr->it_incr = timespec_to_ns(new_setting->it_interval) / sgi_clock_period; + if (timr->it_incr && timr->it_incr < 3) + return -EINVAL; + + if (!timers[i].interrupt_setup) { + int j; + + if (request_irq(IA64_IRQ_RT, mmtimer_interrupt, SA_PERCPU_IRQ, MMTIMER_NAME, NULL)) { + printk(KERN_WARNING "mmtimer: unable to allocate interrupt."); + return -EFAULT; + } + + for(j = base; j < base + NUM_COMPARATORS; j++) + timers[j].interrupt_setup = 1; + } + + *(timers[i].compare) = when / sgi_clock_period + readq(RTC_COUNTER_ADDR); + timr->it_timer.data = i; + timers[i].timer = timr; + MMTIMER_ENABLE_INT(i % NUM_COMPARATORS); + printk("mmtimer enabled %d. Comparator=%ld. RTC=%ld\n",i,*(timers[i].compare),readq(RTC_COUNTER_ADDR)); + return 0; +} + +static struct k_clock sgi_clock = { + .res = 0, + .clock_set = sgi_clock_set, + .clock_get = sgi_clock_get, + .timer_create = sgi_timer_create, + .nsleep = do_posix_clock_nonanosleep, + .timer_set = sgi_timer_set, + .timer_del = sgi_timer_del, + .timer_get = sgi_timer_get +}; + /** * mmtimer_init - device initialization routine * @@ -184,6 +427,8 @@ */ static int __init mmtimer_init(void) { + int i; + if (!ia64_platform_is("sn2")) return -1; @@ -206,9 +451,18 @@ return -1; } + sgi_clock_period = sgi_clock.res = NSEC_PER_SEC / sn_rtc_cycles_per_second; + register_posix_clock(CLOCK_SGI_CYCLE, &sgi_clock); + printk(KERN_INFO "%s: v%s, %ld MHz\n", MMTIMER_DESC, MMTIMER_VERSION, sn_rtc_cycles_per_second/(unsigned long)1E6); + /* Initialze timers structure */ + for(i = 0; i< NR_CPUS * NUM_COMPARATORS; i+= NUM_COMPARATORS) { + timers[i].compare = (unsigned long *)RTC_COMPARE_A_ADDR; + timers[i+1].compare = (unsigned long *)RTC_COMPARE_B_ADDR; + } + return 0; }