Merge branch 'master' of git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux-2.6
[linux-2.6] / arch / mips / mipssim / sim_time.c
1 #include <linux/types.h>
2 #include <linux/init.h>
3 #include <linux/kernel_stat.h>
4 #include <linux/sched.h>
5 #include <linux/spinlock.h>
6 #include <linux/interrupt.h>
7 #include <linux/mc146818rtc.h>
8 #include <linux/smp.h>
9 #include <linux/timex.h>
10
11 #include <asm/hardirq.h>
12 #include <asm/div64.h>
13 #include <asm/cpu.h>
14 #include <asm/time.h>
15 #include <asm/irq.h>
16 #include <asm/mc146818-time.h>
17 #include <asm/msc01_ic.h>
18
19 #include <asm/mips-boards/generic.h>
20 #include <asm/mips-boards/prom.h>
21 #include <asm/mips-boards/simint.h>
22
23
24 unsigned long cpu_khz;
25
26 /*
27  * Estimate CPU frequency.  Sets mips_hpt_frequency as a side-effect
28  */
29 static unsigned int __init estimate_cpu_frequency(void)
30 {
31         unsigned int prid = read_c0_prid() & 0xffff00;
32         unsigned int count;
33
34 #if 1
35         /*
36          * hardwire the board frequency to 12MHz.
37          */
38
39         if ((prid == (PRID_COMP_MIPS | PRID_IMP_20KC)) ||
40             (prid == (PRID_COMP_MIPS | PRID_IMP_25KF)))
41                 count = 12000000;
42         else
43                 count =  6000000;
44 #else
45         unsigned int flags;
46
47         local_irq_save(flags);
48
49         /* Start counter exactly on falling edge of update flag */
50         while (CMOS_READ(RTC_REG_A) & RTC_UIP);
51         while (!(CMOS_READ(RTC_REG_A) & RTC_UIP));
52
53         /* Start r4k counter. */
54         write_c0_count(0);
55
56         /* Read counter exactly on falling edge of update flag */
57         while (CMOS_READ(RTC_REG_A) & RTC_UIP);
58         while (!(CMOS_READ(RTC_REG_A) & RTC_UIP));
59
60         count = read_c0_count();
61
62         /* restore interrupts */
63         local_irq_restore(flags);
64 #endif
65
66         mips_hpt_frequency = count;
67
68         if ((prid != (PRID_COMP_MIPS | PRID_IMP_20KC)) &&
69             (prid != (PRID_COMP_MIPS | PRID_IMP_25KF)))
70                 count *= 2;
71
72         count += 5000;    /* round */
73         count -= count%10000;
74
75         return count;
76 }
77
78 void __init plat_time_init(void)
79 {
80         unsigned int est_freq, flags;
81
82         local_irq_save(flags);
83
84         /* Set Data mode - binary. */
85         CMOS_WRITE(CMOS_READ(RTC_CONTROL) | RTC_DM_BINARY, RTC_CONTROL);
86
87         est_freq = estimate_cpu_frequency();
88
89         printk(KERN_INFO "CPU frequency %d.%02d MHz\n", est_freq / 1000000,
90                (est_freq % 1000000) * 100 / 1000000);
91
92         cpu_khz = est_freq / 1000;
93
94         local_irq_restore(flags);
95 }
96
97 static int mips_cpu_timer_irq;
98
99 static void mips_timer_dispatch(void)
100 {
101         do_IRQ(mips_cpu_timer_irq);
102 }
103
104
105 void __init plat_timer_setup(struct irqaction *irq)
106 {
107         if (cpu_has_veic) {
108                 set_vi_handler(MSC01E_INT_CPUCTR, mips_timer_dispatch);
109                 mips_cpu_timer_irq = MSC01E_INT_BASE + MSC01E_INT_CPUCTR;
110         } else {
111                 if (cpu_has_vint)
112                         set_vi_handler(cp0_compare_irq, mips_timer_dispatch);
113                 mips_cpu_timer_irq = MIPS_CPU_IRQ_BASE + cp0_compare_irq;
114         }
115
116         /* we are using the cpu counter for timer interrupts */
117         setup_irq(mips_cpu_timer_irq, irq);
118
119 #ifdef CONFIG_SMP
120         /* irq_desc(riptor) is a global resource, when the interrupt overlaps
121            on seperate cpu's the first one tries to handle the second interrupt.
122            The effect is that the int remains disabled on the second cpu.
123            Mark the interrupt with IRQ_PER_CPU to avoid any confusion */
124         irq_desc[mips_cpu_timer_irq].flags |= IRQ_PER_CPU;
125         set_irq_handler(mips_cpu_timer_irq, handle_percpu_irq);
126 #endif
127 }