[MIPS] Implement clockevents for R4000-style cp0 count/compare interrupt

Signed-off-by: Ralf Baechle <ralf@linux-mips.org>
diff --git a/arch/mips/Kconfig b/arch/mips/Kconfig
index 0cd0f83..ecce3aa 100644
--- a/arch/mips/Kconfig
+++ b/arch/mips/Kconfig
@@ -335,6 +335,7 @@
 	select GENERIC_ISA_DMA
 	select HAVE_STD_PC_SERIAL_PORT
 	select I8259
+	select IRQ_CPU
 	select ISA
 	select PCSPEAKER
 	select SWAP_IO_SPACE
@@ -667,6 +668,10 @@
 	bool
 	default y
 
+config GENERIC_CLOCKEVENTS
+	bool
+	default y
+
 config GENERIC_TIME
 	bool
 	default y
@@ -901,6 +906,8 @@
 
 menu "CPU selection"
 
+source "kernel/time/Kconfig"
+
 choice
 	prompt "CPU type"
 	default CPU_R4X00
diff --git a/arch/mips/au1000/common/irq.c b/arch/mips/au1000/common/irq.c
index 47949d6..a6640b9 100644
--- a/arch/mips/au1000/common/irq.c
+++ b/arch/mips/au1000/common/irq.c
@@ -633,7 +633,7 @@
 	unsigned int pending = read_c0_status() & read_c0_cause() & ST0_IM;
 
 	if (pending & CAUSEF_IP7)
-		ll_timer_interrupt(63);
+		do_IRQ(63);
 	else if (pending & CAUSEF_IP2)
 		intc0_req0_irqdispatch();
 	else if (pending & CAUSEF_IP3)
diff --git a/arch/mips/configs/bigsur_defconfig b/arch/mips/configs/bigsur_defconfig
index 700a3a2..30f3e9a 100644
--- a/arch/mips/configs/bigsur_defconfig
+++ b/arch/mips/configs/bigsur_defconfig
@@ -69,7 +69,6 @@
 CONFIG_SIBYTE_CFE=y
 # CONFIG_SIBYTE_CFE_CONSOLE is not set
 # CONFIG_SIBYTE_BUS_WATCHER is not set
-# CONFIG_SIBYTE_SB1250_PROF is not set
 # CONFIG_SIBYTE_TBPROF is not set
 CONFIG_RWSEM_GENERIC_SPINLOCK=y
 # CONFIG_ARCH_HAS_ILOG2_U32 is not set
diff --git a/arch/mips/configs/sb1250-swarm_defconfig b/arch/mips/configs/sb1250-swarm_defconfig
index 93f9e83..3d1b628 100644
--- a/arch/mips/configs/sb1250-swarm_defconfig
+++ b/arch/mips/configs/sb1250-swarm_defconfig
@@ -70,7 +70,6 @@
 CONFIG_SIBYTE_CFE=y
 # CONFIG_SIBYTE_CFE_CONSOLE is not set
 # CONFIG_SIBYTE_BUS_WATCHER is not set
-# CONFIG_SIBYTE_SB1250_PROF is not set
 # CONFIG_SIBYTE_TBPROF is not set
 CONFIG_RWSEM_GENERIC_SPINLOCK=y
 # CONFIG_ARCH_HAS_ILOG2_U32 is not set
diff --git a/arch/mips/kernel/process.c b/arch/mips/kernel/process.c
index aadd2cd..f99bb40 100644
--- a/arch/mips/kernel/process.c
+++ b/arch/mips/kernel/process.c
@@ -11,6 +11,7 @@
 #include <linux/errno.h>
 #include <linux/module.h>
 #include <linux/sched.h>
+#include <linux/tick.h>
 #include <linux/kernel.h>
 #include <linux/mm.h>
 #include <linux/stddef.h>
@@ -52,6 +53,7 @@
 {
 	/* endless idle loop with no priority at all */
 	while (1) {
+		tick_nohz_stop_sched_tick();
 		while (!need_resched()) {
 #ifdef CONFIG_SMTC_IDLE_HOOK_DEBUG
 			extern void smtc_idle_loop_hook(void);
@@ -61,6 +63,7 @@
 			if (cpu_wait)
 				(*cpu_wait)();
 		}
+		tick_nohz_restart_sched_tick();
 		preempt_enable_no_resched();
 		schedule();
 		preempt_disable();
diff --git a/arch/mips/kernel/smp.c b/arch/mips/kernel/smp.c
index 73b0dab..500a7ec 100644
--- a/arch/mips/kernel/smp.c
+++ b/arch/mips/kernel/smp.c
@@ -38,6 +38,7 @@
 #include <asm/system.h>
 #include <asm/mmu_context.h>
 #include <asm/smp.h>
+#include <asm/time.h>
 
 #ifdef CONFIG_MIPS_MT_SMTC
 #include <asm/mipsmtregs.h>
@@ -70,6 +71,7 @@
 	cpu_probe();
 	cpu_report();
 	per_cpu_trap_init();
+	mips_clockevent_init();
 	prom_init_secondary();
 
 	/*
diff --git a/arch/mips/kernel/smtc.c b/arch/mips/kernel/smtc.c
index a7afbf2..137183b 100644
--- a/arch/mips/kernel/smtc.c
+++ b/arch/mips/kernel/smtc.c
@@ -867,7 +867,7 @@
 #ifdef CONFIG_SMTC_IDLE_HOOK_DEBUG
 		clock_hang_reported[dest_copy] = 0;
 #endif /* CONFIG_SMTC_IDLE_HOOK_DEBUG */
-		local_timer_interrupt(0);
+		local_timer_interrupt(0, NULL);
 		irq_exit();
 		break;
 	case LINUX_SMP_IPI:
diff --git a/arch/mips/kernel/time.c b/arch/mips/kernel/time.c
index d23e682..3598884 100644
--- a/arch/mips/kernel/time.c
+++ b/arch/mips/kernel/time.c
@@ -11,6 +11,7 @@
  * Free Software Foundation;  either version 2 of the  License, or (at your
  * option) any later version.
  */
+#include <linux/clockchips.h>
 #include <linux/types.h>
 #include <linux/kernel.h>
 #include <linux/init.h>
@@ -34,6 +35,8 @@
 #include <asm/sections.h>
 #include <asm/time.h>
 
+#include <irq.h>
+
 /*
  * The integer part of the number of usecs per jiffy is taken from tick,
  * but the fractional part is not recorded, so we calculate it using the
@@ -70,10 +73,6 @@
 /* how many counter cycles in a jiffy */
 static unsigned long cycles_per_jiffy __read_mostly;
 
-/* expirelo is the count value for next CPU timer interrupt */
-static unsigned int expirelo;
-
-
 /*
  * Null timer ack for systems not needing one (e.g. i8254).
  */
@@ -92,18 +91,7 @@
  */
 static void c0_timer_ack(void)
 {
-	unsigned int count;
-
-	/* Ack this timer interrupt and set the next one.  */
-	expirelo += cycles_per_jiffy;
-	write_c0_compare(expirelo);
-
-	/* Check to see if we have missed any timer interrupts.  */
-	while (((count = read_c0_count()) - expirelo) < 0x7fffffff) {
-		/* missed_timer_count++; */
-		expirelo = count + cycles_per_jiffy;
-		write_c0_compare(expirelo);
-	}
+	write_c0_compare(read_c0_compare());
 }
 
 /*
@@ -114,13 +102,6 @@
 	return read_c0_count();
 }
 
-/* For use both as a high precision timer and an interrupt source.  */
-static void __init c0_hpt_timer_init(void)
-{
-	expirelo = read_c0_count() + cycles_per_jiffy;
-	write_c0_compare(expirelo);
-}
-
 int (*mips_timer_state)(void);
 void (*mips_timer_ack)(void);
 
@@ -140,35 +121,6 @@
 	update_process_times(user_mode(get_irq_regs()));
 }
 
-/*
- * High-level timer interrupt service routines.  This function
- * is set as irqaction->handler and is invoked through do_IRQ.
- */
-static irqreturn_t timer_interrupt(int irq, void *dev_id)
-{
-	write_seqlock(&xtime_lock);
-
-	mips_timer_ack();
-
-	/*
-	 * call the generic timer interrupt handling
-	 */
-	do_timer(1);
-
-	write_sequnlock(&xtime_lock);
-
-	/*
-	 * In UP mode, we call local_timer_interrupt() to do profiling
-	 * and process accouting.
-	 *
-	 * In SMP mode, local_timer_interrupt() is invoked by appropriate
-	 * low-level local timer interrupt handler.
-	 */
-	local_timer_interrupt(irq, dev_id);
-
-	return IRQ_HANDLED;
-}
-
 int null_perf_irq(void)
 {
 	return 0;
@@ -209,81 +161,6 @@
 		!r2;
 }
 
-void ll_timer_interrupt(int irq, void *dev_id)
-{
-	int cpu = smp_processor_id();
-
-#ifdef CONFIG_MIPS_MT_SMTC
-	/*
-	 *  In an SMTC system, one Count/Compare set exists per VPE.
-	 *  Which TC within a VPE gets the interrupt is essentially
-	 *  random - we only know that it shouldn't be one with
-	 *  IXMT set. Whichever TC gets the interrupt needs to
-	 *  send special interprocessor interrupts to the other
-	 *  TCs to make sure that they schedule, etc.
-	 *
-	 *  That code is specific to the SMTC kernel, not to
-	 *  the a particular platform, so it's invoked from
-	 *  the general MIPS timer_interrupt routine.
-	 */
-
-	/*
-	 * We could be here due to timer interrupt,
-	 * perf counter overflow, or both.
-	 */
-	(void) handle_perf_irq(1);
-
-	if (read_c0_cause() & (1 << 30)) {
-		/*
-		 * There are things we only want to do once per tick
-		 * in an "MP" system.   One TC of each VPE will take
-		 * the actual timer interrupt.  The others will get
-		 * timer broadcast IPIs. We use whoever it is that takes
-		 * the tick on VPE 0 to run the full timer_interrupt().
-		 */
-		if (cpu_data[cpu].vpe_id == 0) {
-			timer_interrupt(irq, NULL);
-		} else {
-			write_c0_compare(read_c0_count() +
-			                 (mips_hpt_frequency/HZ));
-			local_timer_interrupt(irq, dev_id);
-		}
-		smtc_timer_broadcast(cpu_data[cpu].vpe_id);
-	}
-#else /* CONFIG_MIPS_MT_SMTC */
-	int r2 = cpu_has_mips_r2;
-
-	if (handle_perf_irq(r2))
-		return;
-
-	if (r2 && ((read_c0_cause() & (1 << 30)) == 0))
-		return;
-
-	if (cpu == 0) {
-		/*
-		 * CPU 0 handles the global timer interrupt job and process
-		 * accounting resets count/compare registers to trigger next
-		 * timer int.
-		 */
-		timer_interrupt(irq, NULL);
-	} else {
-		/* Everyone else needs to reset the timer int here as
-		   ll_local_timer_interrupt doesn't */
-		/*
-		 * FIXME: need to cope with counter underflow.
-		 * More support needs to be added to kernel/time for
-		 * counter/timer interrupts on multiple CPU's
-		 */
-		write_c0_compare(read_c0_count() + (mips_hpt_frequency/HZ));
-
-		/*
-		 * Other CPUs should do profiling and process accounting
-		 */
-		local_timer_interrupt(irq, dev_id);
-	}
-#endif /* CONFIG_MIPS_MT_SMTC */
-}
-
 /*
  * time_init() - it does the following things.
  *
@@ -301,12 +178,6 @@
 
 unsigned int mips_hpt_frequency;
 
-static struct irqaction timer_irqaction = {
-	.handler = timer_interrupt,
-	.flags = IRQF_DISABLED | IRQF_PERCPU,
-	.name = "timer",
-};
-
 static unsigned int __init calibrate_hpt(void)
 {
 	cycle_t frequency, hpt_start, hpt_end, hpt_count, hz;
@@ -355,6 +226,65 @@
 	.flags		= CLOCK_SOURCE_IS_CONTINUOUS,
 };
 
+static int mips_next_event(unsigned long delta,
+                           struct clock_event_device *evt)
+{
+	unsigned int cnt;
+
+	cnt = read_c0_count();
+	cnt += delta;
+	write_c0_compare(cnt);
+
+	return ((long)(read_c0_count() - cnt ) > 0) ? -ETIME : 0;
+}
+
+static void mips_set_mode(enum clock_event_mode mode,
+                          struct clock_event_device *evt)
+{
+	/* Nothing to do ...  */
+}
+
+struct clock_event_device mips_clockevent;
+
+static struct clock_event_device *global_cd[NR_CPUS];
+static int cp0_timer_irq_installed;
+
+static irqreturn_t timer_interrupt(int irq, void *dev_id)
+{
+	const int r2 = cpu_has_mips_r2;
+	struct clock_event_device *cd;
+	int cpu = smp_processor_id();
+
+	/*
+	 * Suckage alert:
+	 * Before R2 of the architecture there was no way to see if a
+	 * performance counter interrupt was pending, so we have to run
+	 * the performance counter interrupt handler anyway.
+	 */
+	if (handle_perf_irq(r2))
+		goto out;
+
+	/*
+	 * The same applies to performance counter interrupts.  But with the
+	 * above we now know that the reason we got here must be a timer
+	 * interrupt.  Being the paranoiacs we are we check anyway.
+	 */
+	if (!r2 || (read_c0_cause() & (1 << 30))) {
+		c0_timer_ack();
+		cd = global_cd[cpu];
+		cd->event_handler(cd);
+	}
+
+out:
+	return IRQ_HANDLED;
+}
+
+static struct irqaction timer_irqaction = {
+	.handler = timer_interrupt,
+	.flags = IRQF_DISABLED | IRQF_PERCPU,
+	.name = "timer",
+};
+
 static void __init init_mips_clocksource(void)
 {
 	u64 temp;
@@ -382,6 +312,56 @@
 {
 }
 
+void __init __weak plat_timer_setup(struct irqaction *irq)
+{
+}
+
+void __cpuinit mips_clockevent_init(void)
+{
+	uint64_t mips_freq = mips_hpt_frequency;
+	unsigned int cpu = smp_processor_id();
+	struct clock_event_device *cd;
+	unsigned int irq = MIPS_CPU_IRQ_BASE + 7;
+
+	if (!cpu_has_counter)
+		return;
+
+	if (cpu == 0)
+		cd = &mips_clockevent;
+	else
+		cd = kzalloc(sizeof(*cd), GFP_ATOMIC);
+	if (!cd)
+		return;		/* We're probably roadkill ...  */
+
+	cd->name		= "MIPS";
+	cd->features		= CLOCK_EVT_FEAT_ONESHOT;
+
+	/* Calculate the min / max delta */
+	cd->mult	= div_sc((unsigned long) mips_freq, NSEC_PER_SEC, 32);
+	cd->shift		= 32;
+	cd->max_delta_ns	= clockevent_delta2ns(0x7fffffff, cd);
+	cd->min_delta_ns	= clockevent_delta2ns(0x30, cd);
+
+	cd->rating		= 300;
+	cd->irq			= irq;
+	cd->cpumask		= cpumask_of_cpu(cpu);
+	cd->set_next_event	= mips_next_event;
+	cd->set_mode		= mips_set_mode;
+
+	global_cd[cpu] = cd;
+	clockevents_register_device(cd);
+
+	if (!cp0_timer_irq_installed) {
+#ifdef CONFIG_MIPS_MT_SMTC
+#define CPUCTR_IMASKBIT (0x100 << cp0_compare_irq)
+		setup_irq_smtc(irq, &timer_irqaction, CPUCTR_IMASKBIT);
+#else
+		setup_irq(irq, &timer_irqaction);
+#endif /* CONFIG_MIPS_MT_SMTC */
+		cp0_timer_irq_installed = 1;
+	}
+}
+
 void __init time_init(void)
 {
 	plat_time_init();
@@ -407,11 +387,6 @@
 				/* Calculate cache parameters.  */
 				cycles_per_jiffy =
 					(mips_hpt_frequency + HZ / 2) / HZ;
-				/*
-				 * This sets up the high precision
-				 * timer for the first interrupt.
-				 */
-				c0_hpt_timer_init();
 			}
 		}
 		if (!mips_hpt_frequency)
@@ -421,6 +396,10 @@
 		printk("Using %u.%03u MHz high precision timer.\n",
 		       ((mips_hpt_frequency + 500) / 1000) / 1000,
 		       ((mips_hpt_frequency + 500) / 1000) % 1000);
+
+#ifdef CONFIG_IRQ_CPU
+		setup_irq(MIPS_CPU_IRQ_BASE + 7, &timer_irqaction);
+#endif
 	}
 
 	if (!mips_timer_ack)
@@ -441,4 +420,5 @@
 	plat_timer_setup(&timer_irqaction);
 
 	init_mips_clocksource();
+	mips_clockevent_init();
 }
diff --git a/arch/mips/mips-boards/generic/time.c b/arch/mips/mips-boards/generic/time.c
index 345de88..2c8db3e 100644
--- a/arch/mips/mips-boards/generic/time.c
+++ b/arch/mips/mips-boards/generic/time.c
@@ -144,20 +144,20 @@
 	mips_scroll_message();
 }
 
-static irqreturn_t mips_perf_interrupt(int irq, void *dev_id)
-{
-	return perf_irq();
-}
+//static irqreturn_t mips_perf_interrupt(int irq, void *dev_id)
+//{
+//	return perf_irq();
+//}
 
-static struct irqaction perf_irqaction = {
-	.handler = mips_perf_interrupt,
-	.flags = IRQF_DISABLED | IRQF_PERCPU,
-	.name = "performance",
-};
+//static struct irqaction perf_irqaction = {
+//	.handler = mips_perf_interrupt,
+//	.flags = IRQF_DISABLED | IRQF_PERCPU,
+//	.name = "performance",
+//};
 
 void __init plat_perf_setup(void)
 {
-	struct irqaction *irq = &perf_irqaction;
+//	struct irqaction *irq = &perf_irqaction;
 
 	cp0_perfcount_irq = -1;
 
@@ -170,12 +170,6 @@
 	if (cp0_perfcount_irq >= 0) {
 		if (cpu_has_vint)
 			set_vi_handler(cp0_perfcount_irq, mips_perf_dispatch);
-#ifdef CONFIG_MIPS_MT_SMTC
-		setup_irq_smtc(cp0_perfcount_irq, irq,
-		               0x100 << cp0_perfcount_irq);
-#else
-		setup_irq(cp0_perfcount_irq, irq);
-#endif /* CONFIG_MIPS_MT_SMTC */
 #ifdef CONFIG_SMP
 		set_irq_handler(cp0_perfcount_irq, handle_percpu_irq);
 #endif
diff --git a/arch/mips/qemu/q-irq.c b/arch/mips/qemu/q-irq.c
index 89891e9..4681757 100644
--- a/arch/mips/qemu/q-irq.c
+++ b/arch/mips/qemu/q-irq.c
@@ -2,6 +2,7 @@
 #include <linux/linkage.h>
 
 #include <asm/i8259.h>
+#include <asm/irq_cpu.h>
 #include <asm/mipsregs.h>
 #include <asm/qemu.h>
 #include <asm/system.h>
@@ -12,7 +13,7 @@
 	unsigned int pending = read_c0_status() & read_c0_cause();
 
 	if (pending & 0x8000) {
-		ll_timer_interrupt(Q_COUNT_COMPARE_IRQ);
+		do_IRQ(Q_COUNT_COMPARE_IRQ);
 		return;
 	}
 	if (pending & 0x0400) {
@@ -29,6 +30,7 @@
 {
 	mips_hpt_frequency = QEMU_C0_COUNTER_CLOCK;		/* 100MHz */
 
+	mips_cpu_irq_init();
 	init_i8259_irqs();
 	set_c0_status(0x8400);
 }
diff --git a/arch/mips/qemu/q-setup.c b/arch/mips/qemu/q-setup.c
index 8413943..89a2076 100644
--- a/arch/mips/qemu/q-setup.c
+++ b/arch/mips/qemu/q-setup.c
@@ -17,7 +17,6 @@
 	outb_p(0x34,0x43);		/* binary, mode 2, LSB/MSB, ch 0 */
 	outb_p(LATCH & 0xff , 0x40);	/* LSB */
 	outb(LATCH >> 8 , 0x40);	/* MSB */
-	setup_irq(0, irq);
 }
 
 void __init plat_mem_setup(void)
diff --git a/arch/mips/sgi-ip22/ip22-int.c b/arch/mips/sgi-ip22/ip22-int.c
index 338c0d7..f199da7 100644
--- a/arch/mips/sgi-ip22/ip22-int.c
+++ b/arch/mips/sgi-ip22/ip22-int.c
@@ -242,7 +242,7 @@
 	 * First we check for r4k counter/timer IRQ.
 	 */
 	if (pending & CAUSEF_IP7)
-		ll_timer_interrupt(SGI_TIMER_IRQ, NULL);
+		do_IRQ(SGI_TIMER_IRQ);
 	else if (pending & CAUSEF_IP2)
 		indy_local0_irqdispatch();
 	else if (pending & CAUSEF_IP3)
diff --git a/arch/mips/sgi-ip32/ip32-irq.c b/arch/mips/sgi-ip32/ip32-irq.c
index 5bad1b7..002095b 100644
--- a/arch/mips/sgi-ip32/ip32-irq.c
+++ b/arch/mips/sgi-ip32/ip32-irq.c
@@ -457,7 +457,7 @@
 
 static void ip32_irq5(void)
 {
-	ll_timer_interrupt(IP32_R4K_TIMER_IRQ);
+	do_IRQ(IP32_R4K_TIMER_IRQ);
 }
 
 asmlinkage void plat_irq_dispatch(void)
diff --git a/arch/mips/sibyte/Kconfig b/arch/mips/sibyte/Kconfig
index 841b301..e8fb880 100644
--- a/arch/mips/sibyte/Kconfig
+++ b/arch/mips/sibyte/Kconfig
@@ -1,6 +1,7 @@
 config SIBYTE_SB1250
 	bool
 	select HW_HAS_PCI
+	select IRQ_CPU
 	select SIBYTE_ENABLE_LDT_IF_PCI
 	select SIBYTE_HAS_ZBUS_PROFILING
 	select SIBYTE_SB1xxx_SOC
@@ -8,6 +9,7 @@
 
 config SIBYTE_BCM1120
 	bool
+	select IRQ_CPU
 	select SIBYTE_BCM112X
 	select SIBYTE_HAS_ZBUS_PROFILING
 	select SIBYTE_SB1xxx_SOC
@@ -15,6 +17,7 @@
 config SIBYTE_BCM1125
 	bool
 	select HW_HAS_PCI
+	select IRQ_CPU
 	select SIBYTE_BCM112X
 	select SIBYTE_HAS_ZBUS_PROFILING
 	select SIBYTE_SB1xxx_SOC
@@ -22,6 +25,7 @@
 config SIBYTE_BCM1125H
 	bool
 	select HW_HAS_PCI
+	select IRQ_CPU
 	select SIBYTE_BCM112X
 	select SIBYTE_ENABLE_LDT_IF_PCI
 	select SIBYTE_HAS_ZBUS_PROFILING
@@ -29,12 +33,14 @@
 
 config SIBYTE_BCM112X
 	bool
+	select IRQ_CPU
 	select SIBYTE_SB1xxx_SOC
 	select SIBYTE_HAS_ZBUS_PROFILING
 
 config SIBYTE_BCM1x80
 	bool
 	select HW_HAS_PCI
+	select IRQ_CPU
 	select SIBYTE_HAS_ZBUS_PROFILING
 	select SIBYTE_SB1xxx_SOC
 	select SYS_SUPPORTS_SMP
@@ -42,6 +48,7 @@
 config SIBYTE_BCM1x55
 	bool
 	select HW_HAS_PCI
+	select IRQ_CPU
 	select SIBYTE_SB1xxx_SOC
 	select SIBYTE_HAS_ZBUS_PROFILING
 	select SYS_SUPPORTS_SMP
@@ -49,6 +56,7 @@
 config SIBYTE_SB1xxx_SOC
 	bool
 	select DMA_COHERENT
+	select IRQ_CPU
 	select SIBYTE_CFE
 	select SWAP_IO_SPACE
 	select SYS_SUPPORTS_32BIT_KERNEL
@@ -166,10 +174,6 @@
 	  buffer activity.  Raw buffer data is dumped to console, and
 	  must be processed off-line.
 
-config SIBYTE_SB1250_PROF
-	bool "Support for SB1/SOC profiling - SB1/SCD perf counters"
-	depends on SIBYTE_SB1xxx_SOC
-
 config SIBYTE_TBPROF
 	tristate "Support for ZBbus profiling"
 	depends on SIBYTE_HAS_ZBUS_PROFILING
diff --git a/arch/mips/sibyte/bcm1480/irq.c b/arch/mips/sibyte/bcm1480/irq.c
index e729b5f..cf979db 100644
--- a/arch/mips/sibyte/bcm1480/irq.c
+++ b/arch/mips/sibyte/bcm1480/irq.c
@@ -450,7 +450,6 @@
 
 #endif 	/* CONFIG_KGDB */
 
-extern void bcm1480_timer_interrupt(void);
 extern void bcm1480_mailbox_interrupt(void);
 
 asmlinkage void plat_irq_dispatch(void)
@@ -470,8 +469,16 @@
 	else
 #endif
 
-	if (pending & CAUSEF_IP4)
-		bcm1480_timer_interrupt();
+	if (pending & CAUSEF_IP4) {
+		int cpu = smp_processor_id();
+		int irq = K_BCM1480_INT_TIMER_0 + cpu;
+
+		/* Reset the timer */
+		__raw_writeq(M_SCD_TIMER_ENABLE|M_SCD_TIMER_MODE_CONTINUOUS,
+		            IOADDR(A_SCD_TIMER_REGISTER(cpu, R_SCD_TIMER_CFG)));
+
+		do_IRQ(irq);
+	}
 
 #ifdef CONFIG_SMP
 	else if (pending & CAUSEF_IP3)
diff --git a/arch/mips/sibyte/sb1250/irq.c b/arch/mips/sibyte/sb1250/irq.c
index ad593a6..6a4cc84 100644
--- a/arch/mips/sibyte/sb1250/irq.c
+++ b/arch/mips/sibyte/sb1250/irq.c
@@ -28,6 +28,7 @@
 #include <asm/errno.h>
 #include <asm/signal.h>
 #include <asm/system.h>
+#include <asm/time.h>
 #include <asm/io.h>
 
 #include <asm/sibyte/sb1250_regs.h>
@@ -399,18 +400,45 @@
 
 #endif 	/* CONFIG_KGDB */
 
-extern void sb1250_timer_interrupt(void);
+static inline void sb1250_timer_interrupt(void)
+{
+	int cpu = smp_processor_id();
+	int irq = K_INT_TIMER_0 + cpu;
+
+	irq_enter();
+	kstat_this_cpu.irqs[irq]++;
+
+	write_seqlock(&xtime_lock);
+
+	/* ACK interrupt */
+	____raw_writeq(M_SCD_TIMER_ENABLE | M_SCD_TIMER_MODE_CONTINUOUS,
+		       IOADDR(A_SCD_TIMER_REGISTER(cpu, R_SCD_TIMER_CFG)));
+
+	/*
+	 * call the generic timer interrupt handling
+	 */
+	do_timer(1);
+
+	write_sequnlock(&xtime_lock);
+
+	/*
+	 * In UP mode, we call local_timer_interrupt() to do profiling
+	 * and process accouting.
+	 *
+	 * In SMP mode, local_timer_interrupt() is invoked by appropriate
+	 * low-level local timer interrupt handler.
+	 */
+	local_timer_interrupt(irq);
+
+	irq_exit();
+}
+
 extern void sb1250_mailbox_interrupt(void);
 
 asmlinkage void plat_irq_dispatch(void)
 {
 	unsigned int pending;
 
-#ifdef CONFIG_SIBYTE_SB1250_PROF
-	/* Set compare to count to silence count/compare timer interrupts */
-	write_c0_compare(read_c0_count());
-#endif
-
 	/*
 	 * What a pain. We have to be really careful saving the upper 32 bits
 	 * of any * register across function calls if we don't want them
@@ -423,13 +451,9 @@
 
 	pending = read_c0_cause() & read_c0_status() & ST0_IM;
 
-#ifdef CONFIG_SIBYTE_SB1250_PROF
-	if (pending & CAUSEF_IP7) /* Cpu performance counter interrupt */
-		sbprof_cpu_intr();
-	else
-#endif
-
-	if (pending & CAUSEF_IP4)
+	if (pending & CAUSEF_IP7) /* CPU performance counter interrupt */
+		do_IRQ(MIPS_CPU_IRQ_BASE + 7);
+	else if (pending & CAUSEF_IP4)
 		sb1250_timer_interrupt();
 
 #ifdef CONFIG_SMP
diff --git a/arch/mips/sibyte/sb1250/time.c b/arch/mips/sibyte/sb1250/time.c
index 5bb83cd..eb17707 100644
--- a/arch/mips/sibyte/sb1250/time.c
+++ b/arch/mips/sibyte/sb1250/time.c
@@ -116,18 +116,6 @@
 	 */
 }
 
-void sb1250_timer_interrupt(void)
-{
-	int cpu = smp_processor_id();
-	int irq = K_INT_TIMER_0 + cpu;
-
-	/* ACK interrupt */
-	____raw_writeq(M_SCD_TIMER_ENABLE | M_SCD_TIMER_MODE_CONTINUOUS,
-		       IOADDR(A_SCD_TIMER_REGISTER(cpu, R_SCD_TIMER_CFG)));
-
-	ll_timer_interrupt(irq);
-}
-
 /*
  * The HPT is free running from SB1250_HPT_VALUE down to 0 then starts over
  * again.
diff --git a/include/asm-mips/mach-ip27/irq.h b/include/asm-mips/mach-ip27/irq.h
index 25f0c3f..cf4384b 100644
--- a/include/asm-mips/mach-ip27/irq.h
+++ b/include/asm-mips/mach-ip27/irq.h
@@ -17,4 +17,6 @@
  */
 #define NR_IRQS 256
 
+#include_next <irq.h>
+
 #endif /* __ASM_MACH_IP27_IRQ_H */
diff --git a/include/asm-mips/qemu.h b/include/asm-mips/qemu.h
index 531caf4..487ced4 100644
--- a/include/asm-mips/qemu.h
+++ b/include/asm-mips/qemu.h
@@ -12,7 +12,7 @@
  * Interrupt numbers
  */
 #define Q_PIC_IRQ_BASE		0
-#define Q_COUNT_COMPARE_IRQ	16
+#define Q_COUNT_COMPARE_IRQ	23
 
 /*
  * Qemu clock rate.  Unlike on real MIPS this has no relation to the
diff --git a/include/asm-mips/time.h b/include/asm-mips/time.h
index 3516b32..35555bd 100644
--- a/include/asm-mips/time.h
+++ b/include/asm-mips/time.h
@@ -49,11 +49,6 @@
 extern struct clocksource clocksource_mips;
 
 /*
- * The low-level timer interrupt routine.
- */
-extern void ll_timer_interrupt(int irq, void *dev_id);
-
-/*
  * profiling and process accouting is done separately in local_timer_interrupt
  */
 extern void local_timer_interrupt(int irq, void *dev_id);
@@ -78,4 +73,9 @@
  */
 extern int (*perf_irq)(void);
 
+/*
+ * Initialize the calling CPU's compare interrupt as clockevent device
+ */
+extern void mips_clockevent_init(void);
+
 #endif /* _ASM_TIME_H */