Index: linux-2.6.9/include/linux/time.h
===================================================================
--- linux-2.6.9.orig/include/linux/time.h	2004-10-20 19:38:03.000000000 -0700
+++ linux-2.6.9/include/linux/time.h	2004-10-20 20:07:38.000000000 -0700
@@ -160,7 +160,7 @@
 /*
  * The IDs of various hardware clocks
  */
-
+#define CLOCK_SGI_CYCLE 10
 
 #define CLOCK_SGI_CYCLE 10
 #define MAX_CLOCKS 16
Index: linux-2.6.9/drivers/char/mmtimer.c
===================================================================
--- linux-2.6.9.orig/drivers/char/mmtimer.c	2004-10-20 19:38:01.000000000 -0700
+++ linux-2.6.9/drivers/char/mmtimer.c	2004-10-20 20:23:01.000000000 -0700
@@ -13,6 +13,9 @@
  *
  * 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, Dimitri Sivanich - provide timer interrupt
+ *            support.
  */
 
 #include <linux/types.h>
@@ -26,20 +29,23 @@
 #include <linux/mmtimer.h>
 #include <linux/miscdevice.h>
 #include <linux/posix-timers.h>
+#include <linux/interrupt.h>
+#include <asm/sn/arch.h>
 
 #include <asm/uaccess.h>
 #include <asm/sn/addrs.h>
 #include <asm/sn/clksupport.h>
+#include <asm/sn/intr.h>
 #include <asm/sn/shub_mmr.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 */
 
@@ -58,6 +64,171 @@
 	.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.
+ */
+#define NUM_COMPARATORS 3
+/*
+ * Check for an interrupt and clear the pending bit if
+ * one is waiting.
+*/
+static int inline mmtimer_int_pending(int comparator) {
+	int pending = 0;
+
+	switch (comparator) {
+	case 0:
+		if (HUB_L((unsigned long *)LOCAL_MMR_ADDR(SH_EVENT_OCCURRED)) &
+					SH_EVENT_OCCURRED_RTC1_INT_MASK) {
+			HUB_S((u64 *)LOCAL_MMR_ADDR(SH_EVENT_OCCURRED_ALIAS),
+				SH_EVENT_OCCURRED_RTC1_INT_MASK);
+			pending = 1;
+		}
+		break;
+	case 1:
+		if (HUB_L((unsigned long *)LOCAL_MMR_ADDR(SH_EVENT_OCCURRED)) &
+					SH_EVENT_OCCURRED_RTC2_INT_MASK) {
+			HUB_S((u64 *)LOCAL_MMR_ADDR(SH_EVENT_OCCURRED_ALIAS),
+				SH_EVENT_OCCURRED_RTC2_INT_MASK);
+			pending = 1;
+		}
+		break;
+	case 2:
+		if (HUB_L((unsigned long *)LOCAL_MMR_ADDR(SH_EVENT_OCCURRED)) &
+					SH_EVENT_OCCURRED_RTC3_INT_MASK) {
+			HUB_S((u64 *)LOCAL_MMR_ADDR(SH_EVENT_OCCURRED_ALIAS),
+				SH_EVENT_OCCURRED_RTC3_INT_MASK);
+			pending = 1;
+		}
+		break;
+	default:
+		return -EFAULT;
+	}
+	return pending;
+}
+
+static void inline mmtimer_set_comparator(int comparator, unsigned long expires) {
+
+	switch (comparator) {
+	case 0:
+		HUB_S((u64 *)LOCAL_MMR_ADDR(SH_INT_CMPB), expires);
+		break;
+	case 1:
+		HUB_S((u64 *)LOCAL_MMR_ADDR(SH_INT_CMPC), expires);
+		break;
+	case 2:
+		HUB_S((u64 *)LOCAL_MMR_ADDR(SH_INT_CMPD), expires);
+	}
+}
+
+static void inline mmtimer_set_int_0(long expires) {
+	u64 val;
+
+	/* Disable interrupt */
+	HUB_S((u64 *)LOCAL_MMR_ADDR(SH_RTC1_INT_ENABLE), 0UL);
+
+	val = ((u64)SGI_MMTIMER_VECTOR << SH_RTC1_INT_CONFIG_IDX_SHFT) |
+		((u64)cpu_physical_id(smp_processor_id()) <<
+			SH_RTC1_INT_CONFIG_PID_SHFT);
+
+	/* Set configuration */
+	HUB_S((u64 *)LOCAL_MMR_ADDR(SH_RTC1_INT_CONFIG), val);
+
+	/*
+	 * Enable interrupt before setting compare value.
+	 * Doing this after setting the compare value can result
+	 * in an interrupt pending without ever calling the handler
+	 * (probably a timing issue).
+	 */
+	HUB_S((u64 *)LOCAL_MMR_ADDR(SH_RTC1_INT_ENABLE), 1UL);
+
+	/* Set compare value */
+	HUB_S((u64 *)LOCAL_MMR_ADDR(SH_INT_CMPB), expires);
+}
+
+static void inline mmtimer_set_int_1(long expires) {
+	u64 val;
+
+	HUB_S((u64 *)LOCAL_MMR_ADDR(SH_RTC2_INT_ENABLE), 0UL);
+
+	val = ((u64)SGI_MMTIMER_VECTOR << SH_RTC2_INT_CONFIG_IDX_SHFT) |
+		((u64)cpu_physical_id(smp_processor_id()) <<
+			SH_RTC2_INT_CONFIG_PID_SHFT);
+
+	HUB_S((u64 *)LOCAL_MMR_ADDR(SH_RTC2_INT_CONFIG), val);
+
+	HUB_S((u64 *)LOCAL_MMR_ADDR(SH_RTC2_INT_ENABLE), 1UL);
+
+	HUB_S((u64 *)LOCAL_MMR_ADDR(SH_INT_CMPC), expires);
+}
+
+static void inline mmtimer_set_int_2(long expires) {
+	u64 val;
+
+	HUB_S((u64 *)LOCAL_MMR_ADDR(SH_RTC3_INT_ENABLE), 0UL);
+
+	val = ((u64)SGI_MMTIMER_VECTOR << SH_RTC3_INT_CONFIG_IDX_SHFT) |
+		((u64)cpu_physical_id(smp_processor_id()) <<
+			SH_RTC3_INT_CONFIG_PID_SHFT);
+
+	HUB_S((u64 *)LOCAL_MMR_ADDR(SH_RTC3_INT_CONFIG), val);
+
+	HUB_S((u64 *)LOCAL_MMR_ADDR(SH_RTC3_INT_ENABLE), 1UL);
+
+	HUB_S((u64 *)LOCAL_MMR_ADDR(SH_INT_CMPD), expires);
+}
+
+static int inline mmtimer_set_int(int comparator, long expires) {
+	switch (comparator) {
+	case 0:
+		mmtimer_set_int_0(expires);
+		break;
+	case 1:
+		mmtimer_set_int_1(expires);
+		break;
+	case 2:
+		mmtimer_set_int_2(expires);
+		break;
+	default:
+		return -EFAULT;
+	}
+	return 0;
+}
+
+static int inline mmtimer_disable_int(int comparator) {
+	switch (comparator) {
+	case 0:
+		HUB_S((u64 *)LOCAL_MMR_ADDR(SH_RTC1_INT_ENABLE), 0UL);
+		break;
+	case 1:
+		HUB_S((u64 *)LOCAL_MMR_ADDR(SH_RTC2_INT_ENABLE), 0UL);
+		break;
+	case 2:
+		HUB_S((u64 *)LOCAL_MMR_ADDR(SH_RTC3_INT_ENABLE), 0UL);
+		break;
+	default:
+		return -EFAULT;
+	}
+	return 0;
+}
+
+#define TIMER_OFF 0xbadcabLL
+
+typedef struct mmtimer {
+	struct k_itimer *timer;
+	spinlock_t lock;
+} mmtimer_t;
+
+/*
+ * Total number of comparators is comparators/node * MAX nodes/running kernel
+ */
+static mmtimer_t timers[NUM_COMPARATORS*MAX_COMPACT_NODES];
+
 /**
  * mmtimer_ioctl - ioctl interface for /dev/mmtimer
  * @inode: inode of the device
@@ -183,6 +354,15 @@
 static struct timespec sgi_clock_offset;
 static int sgi_clock_period;
 
+/*****
+ * Posix Timer Interface
+ * The posix timer interface provides full support for the posix timer
+ * interface and allows the scheduling of up to three timers.
+ */
+
+static struct timespec sgi_clock_offset;
+static int sgi_clock_period;
+
 static int sgi_clock_get(struct timespec *tp) {
 	u64 nsec;
 
@@ -194,12 +374,12 @@
 };
 
 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)
@@ -211,12 +391,201 @@
 	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.
+ *
+ * This interrupt is run in an interrupt context on cpus services
+ * by the SHUB. It is therefore safe to locally access to  SHub
+ * registers.
+ */
+static irqreturn_t
+mmtimer_interrupt(int irq, void *dev_id, struct pt_regs *regs)
+{
+	int i;
+	mmtimer_t *base = timers + cpuid_to_cnodeid(smp_processor_id()) * NUM_COMPARATORS;
+	int result = IRQ_NONE;
+
+	printk("mmtimer interrupt:\n");
+	/*
+	 * Do this once for each comparison register
+	 */
+	for (i = 0; i < NUM_COMPARATORS; i++) {
+		if (mmtimer_int_pending(i)>0) {
+			struct k_itimer *t = base[i].timer;
+			unsigned long flags;
+
+			printk("mmtimer: processing int on %d expires=%lu incr=%lu rtc=%lu\n",i,t->it_timer.expires, t->it_incr,readq(RTC_COUNTER_ADDR));
+			spin_lock_irqsave(&t->it_lock, flags);
+
+			if (timer_event(t, 0) == 0) {
+				if(t->it_incr == 0) goto clear_timer;
+				t->it_timer.expires += t->it_incr;
+				mmtimer_set_comparator(i, t->it_timer.expires);
+			} else {
+				printk(KERN_WARNING "mmtimer unable to deliver signal");
+				t->it_overrun++;
+clear_timer:
+				mmtimer_disable_int(i);
+				t->it_timer.magic = TIMER_OFF;
+				base[i].timer = NULL;
+			}
+			
+			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 timer is off */
+	timer->it_timer.magic = TIMER_OFF;
+	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) {
+	int i = timr->it_timer.magic;
+	cnodeid_t nodeid = timr->it_timer.data;
+	mmtimer_t *t = timers + nodeid * NUM_COMPARATORS +i;
+
+	if (i != TIMER_OFF) {
+		spin_lock(&t->lock);
+		mmtimer_disable_int(i);
+		t->timer = NULL;
+		timr->it_timer.magic = TIMER_OFF;
+		spin_unlock(&t->lock);
+		printk("mmtimer: interrupt %d node %d disabled.\n",i, nodeid);
+	}
+	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) {
+
+	if (timr->it_timer.magic == TIMER_OFF) {
+		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);
+	/* readq is atomic so no locking needed */
+	ns_to_timespec(cur_setting->it_value, (timr->it_timer.expires - 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;
+	unsigned long when, irqflags;
+	int err = 0;
+	cnodeid_t nodeid =  cpuid_to_cnodeid(smp_processor_id());
+	mmtimer_t *base = timers + nodeid * NUM_COMPARATORS;
+
+	if (old_setting)
+		sgi_timer_get(timr, old_setting);
+
+	sgi_timer_del(timr);
+	when = timespec_to_ns(new_setting->it_value);
+
+	if (when == 0)
+		/* Clear timer */
+		return 0;
+
+	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;
+	}
+
+	/* We are allocating a local SHub comparator. If we would be moved to another
+	 * cpu then another SHub may be local to us. Prohibit that by switching off
+	 * preemption.
+	 */
+	preempt_disable();
+retry:
+	for(i = 0; i< NUM_COMPARATORS; i++) {
+		if (!base[i].timer) {
+			break;
+		}
+	}
+
+	if (i == NUM_COMPARATORS) {
+		preempt_enable();
+		return -EBUSY;
+	}
+
+	/* Avoid someone else using this timer by locking .. */
+	spin_lock_irqsave(&base[i].lock, irqflags);
+
+	if (base[i].timer) {
+		spin_unlock_irqrestore(&base[i].lock, irqflags);
+		goto retry;
+	}
+
+	/* Check once more in case we got held off by another IRQ */
+	if (when < 100) {
+		spin_unlock_irqrestore(&base[i].lock, irqflags);
+		preempt_enable();
+		timer_event(timr, 0);
+		return 0;
+	}
+
+	timr->it_timer.magic = i;
+	timr->it_timer.data = nodeid;
+	timr->it_incr = timespec_to_ns(new_setting->it_interval) / sgi_clock_period;
+
+	if (timr->it_incr && timr->it_incr < 3) {
+		err = -EINVAL;
+		goto out;
+	}
+
+	timr->it_timer.expires = when / sgi_clock_period + readq(RTC_COUNTER_ADDR);
+	base[i].timer = timr;
+	if (mmtimer_set_int(i, timr->it_timer.expires)) {
+		err = -EINVAL;
+	}
+out:
+	spin_unlock_irqrestore(&base[i].lock, irqflags);
+	preempt_enable();
+	printk("mmtimer enabled %d. Comparator=%ld. RTC=%ld\n",i, timr->it_timer.expires,readq(RTC_COUNTER_ADDR));
+
+	return err;
+}
+
 static struct k_clock sgi_clock = {
 	.res = 0,
 	.clock_set = sgi_clock_set,
 	.clock_get = sgi_clock_get,
-	.timer_create = do_posix_clock_notimer_create,
-	.nsleep = do_posix_clock_nonanosleep
+	.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
 };
 
 /**
@@ -226,6 +595,8 @@
  */
 static int __init mmtimer_init(void)
 {
+	unsigned i;
+
 	if (!ia64_platform_is("sn2"))
 		return -1;
 
@@ -241,6 +612,17 @@
 	mmtimer_femtoperiod = ((unsigned long)1E15 + sn_rtc_cycles_per_second /
 			       2) / sn_rtc_cycles_per_second;
 
+	if (request_irq(SGI_MMTIMER_VECTOR, mmtimer_interrupt, SA_PERCPU_IRQ, MMTIMER_NAME, NULL)) {
+		printk(KERN_WARNING "%s: unable to allocate interrupt.",
+			MMTIMER_NAME);
+		return -1;
+	}
+
+	for (i=0; i< NUM_COMPARATORS*MAX_COMPACT_NODES; i++) {
+		spin_lock_init(&timers[i].lock);
+		timers[i].timer = NULL;
+	}
+
 	strcpy(mmtimer_miscdev.devfs_name, MMTIMER_NAME);
 	if (misc_register(&mmtimer_miscdev)) {
 		printk(KERN_ERR "%s: failed to register device\n",
@@ -251,6 +633,9 @@
 	sgi_clock_period = sgi_clock.res = NSEC_PER_SEC / sn_rtc_cycles_per_second;
 	register_posix_clock(CLOCK_SGI_CYCLE, &sgi_clock);
 
+	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);
 
Index: linux-2.6.9/include/asm/sn/intr.h
===================================================================
--- linux-2.6.9.orig/include/asm/sn/intr.h	2004-10-20 19:38:03.000000000 -0700
+++ linux-2.6.9/include/asm/sn/intr.h	2004-10-20 20:21:32.000000000 -0700
@@ -11,6 +11,7 @@
 
 #define SGI_UART_VECTOR		(0xe9)
 #define SGI_PCIBR_ERROR		(0x33)
+#define SGI_MMTIMER_VECTOR	(0xed)
 
 // These two IRQ's are used by partitioning.
 #define SGI_XPC_ACTIVATE                (0x30)