fedora core 6 1.2949 + vserver 2.2.0
[linux-2.6.git] / arch / mips / sibyte / sb1250 / time.c
index eb8ff64..2efffe1 100644 (file)
  * code to do general bookkeeping (e.g. update jiffies, run
  * bottom halves, etc.)
  */
-#include <linux/config.h>
 #include <linux/interrupt.h>
 #include <linux/sched.h>
 #include <linux/spinlock.h>
 #include <linux/kernel_stat.h>
 
 #include <asm/irq.h>
-#include <asm/ptrace.h>
 #include <asm/addrspace.h>
 #include <asm/time.h>
 #include <asm/io.h>
 #define IMR_IP3_VAL    K_INT_MAP_I1
 #define IMR_IP4_VAL    K_INT_MAP_I2
 
+#define SB1250_HPT_NUM         3
+#define SB1250_HPT_VALUE       M_SCD_TIMER_CNT /* max value */
+
+
 extern int sb1250_steal_irq(int irq);
 
+static cycle_t sb1250_hpt_read(void);
+
+void __init sb1250_hpt_setup(void)
+{
+       int cpu = smp_processor_id();
+
+       if (!cpu) {
+               /* Setup hpt using timer #3 but do not enable irq for it */
+               __raw_writeq(0, IOADDR(A_SCD_TIMER_REGISTER(SB1250_HPT_NUM, R_SCD_TIMER_CFG)));
+               __raw_writeq(SB1250_HPT_VALUE,
+                            IOADDR(A_SCD_TIMER_REGISTER(SB1250_HPT_NUM, R_SCD_TIMER_INIT)));
+               __raw_writeq(M_SCD_TIMER_ENABLE | M_SCD_TIMER_MODE_CONTINUOUS,
+                            IOADDR(A_SCD_TIMER_REGISTER(SB1250_HPT_NUM, R_SCD_TIMER_CFG)));
+
+               mips_hpt_frequency = V_SCD_TIMER_FREQ;
+               clocksource_mips.read = sb1250_hpt_read;
+               clocksource_mips.mask = M_SCD_TIMER_INIT;
+       }
+}
+
+
 void sb1250_time_init(void)
 {
        int cpu = smp_processor_id();
        int irq = K_INT_TIMER_0+cpu;
 
-       /* Only have 4 general purpose timers */
-       if (cpu > 3) {
+       /* Only have 4 general purpose timers, and we use last one as hpt */
+       if (cpu > 2) {
                BUG();
        }
 
-       if (!cpu) {
-               /* Use our own gettimeoffset() routine */
-               do_gettimeoffset = sb1250_gettimeoffset;
-       }
-
        sb1250_mask_irq(cpu, irq);
 
        /* Map the timer interrupt to ip[4] of this cpu */
-       __raw_writeq(IMR_IP4_VAL, IOADDR(A_IMR_REGISTER(cpu, R_IMR_INTERRUPT_MAP_BASE) +
-                                        (irq << 3)));
+       __raw_writeq(IMR_IP4_VAL,
+                    IOADDR(A_IMR_REGISTER(cpu, R_IMR_INTERRUPT_MAP_BASE) +
+                           (irq << 3)));
 
        /* the general purpose timer ticks at 1 Mhz independent if the rest of the system */
        /* Disable the timer and set up the count */
        __raw_writeq(0, IOADDR(A_SCD_TIMER_REGISTER(cpu, R_SCD_TIMER_CFG)));
 #ifdef CONFIG_SIMULATION
-       __raw_writeq(50000 / HZ, IOADDR(A_SCD_TIMER_REGISTER(cpu, R_SCD_TIMER_INIT)));
+       __raw_writeq((50000 / HZ) - 1,
+                    IOADDR(A_SCD_TIMER_REGISTER(cpu, R_SCD_TIMER_INIT)));
 #else
-       __raw_writeq(1000000/HZ, IOADDR(A_SCD_TIMER_REGISTER(cpu, R_SCD_TIMER_INIT)));
+       __raw_writeq((V_SCD_TIMER_FREQ / HZ) - 1,
+                    IOADDR(A_SCD_TIMER_REGISTER(cpu, R_SCD_TIMER_INIT)));
 #endif
 
        /* Set the timer running */
-       __raw_writeq(M_SCD_TIMER_ENABLE|M_SCD_TIMER_MODE_CONTINUOUS,
+       __raw_writeq(M_SCD_TIMER_ENABLE | M_SCD_TIMER_MODE_CONTINUOUS,
                     IOADDR(A_SCD_TIMER_REGISTER(cpu, R_SCD_TIMER_CFG)));
 
        sb1250_unmask_irq(cpu, irq);
@@ -95,39 +116,38 @@ void sb1250_time_init(void)
         */
 }
 
-void sb1250_timer_interrupt(struct pt_regs *regs)
+void sb1250_timer_interrupt(void)
 {
-       extern asmlinkage void ll_local_timer_interrupt(int irq, struct pt_regs *regs);
        int cpu = smp_processor_id();
        int irq = K_INT_TIMER_0 + cpu;
 
-       /* Reset the timer */
-       ____raw_writeq(M_SCD_TIMER_ENABLE|M_SCD_TIMER_MODE_CONTINUOUS,
+       /* ACK interrupt */
+       ____raw_writeq(M_SCD_TIMER_ENABLE | M_SCD_TIMER_MODE_CONTINUOUS,
                       IOADDR(A_SCD_TIMER_REGISTER(cpu, R_SCD_TIMER_CFG)));
 
-       /*
-        * CPU 0 handles the global timer interrupt job
-        */
        if (cpu == 0) {
-               ll_timer_interrupt(irq, regs);
+               /*
+                * CPU 0 handles the global timer interrupt job
+                */
+               ll_timer_interrupt(irq);
+       }
+       else {
+               /*
+                * other CPUs should just do profiling and process accounting
+                */
+               ll_local_timer_interrupt(irq);
        }
-
-       /*
-        * every CPU should do profiling and process accouting
-        */
-       ll_local_timer_interrupt(irq, regs);
 }
 
 /*
- * We use our own do_gettimeoffset() instead of the generic one,
- * because the generic one does not work for SMP case.
- * In addition, since we use general timer 0 for system time,
- * we can get accurate intra-jiffy offset without calibration.
+ * The HPT is free running from SB1250_HPT_VALUE down to 0 then starts over
+ * again.
  */
-unsigned long sb1250_gettimeoffset(void)
+static cycle_t sb1250_hpt_read(void)
 {
-       unsigned long count =
-               __raw_readq(IOADDR(A_SCD_TIMER_REGISTER(0, R_SCD_TIMER_CNT)));
+       unsigned int count;
 
-       return 1000000/HZ - count;
- }
+       count = G_SCD_TIMER_CNT(__raw_readq(IOADDR(A_SCD_TIMER_REGISTER(SB1250_HPT_NUM, R_SCD_TIMER_CNT))));
+
+       return SB1250_HPT_VALUE - count;
+}