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 <linux/types.h>
@@ -28,18 +30,22 @@
 #include <asm/uaccess.h>
 #include <asm/sn/addrs.h>
 #include <asm/sn/clksupport.h>
+#include <linux/posix-timers.h>
+#include <linux/interrupt.h>
 
 MODULE_AUTHOR("Jesse Barnes <jbarnes@sgi.com>");
-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;
 }