]> www.pilppa.org Git - linux-2.6-omap-h63xx.git/blob - arch/mips/mips-boards/generic/time.c
2830f656fe2fbdc51228e6b1f0a5a1c7942bddbd
[linux-2.6-omap-h63xx.git] / arch / mips / mips-boards / generic / time.c
1 /*
2  * Carsten Langgaard, carstenl@mips.com
3  * Copyright (C) 1999,2000 MIPS Technologies, Inc.  All rights reserved.
4  *
5  *  This program is free software; you can distribute it and/or modify it
6  *  under the terms of the GNU General Public License (Version 2) as
7  *  published by the Free Software Foundation.
8  *
9  *  This program is distributed in the hope it will be useful, but WITHOUT
10  *  ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11  *  FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
12  *  for more details.
13  *
14  *  You should have received a copy of the GNU General Public License along
15  *  with this program; if not, write to the Free Software Foundation, Inc.,
16  *  59 Temple Place - Suite 330, Boston MA 02111-1307, USA.
17  *
18  * Setting up the clock on the MIPS boards.
19  */
20
21 #include <linux/types.h>
22 #include <linux/config.h>
23 #include <linux/init.h>
24 #include <linux/kernel_stat.h>
25 #include <linux/sched.h>
26 #include <linux/spinlock.h>
27 #include <linux/interrupt.h>
28 #include <linux/time.h>
29 #include <linux/timex.h>
30 #include <linux/mc146818rtc.h>
31
32 #include <asm/mipsregs.h>
33 #include <asm/ptrace.h>
34 #include <asm/hardirq.h>
35 #include <asm/irq.h>
36 #include <asm/div64.h>
37 #include <asm/cpu.h>
38 #include <asm/time.h>
39 #include <asm/mc146818-time.h>
40 #include <asm/msc01_ic.h>
41
42 #include <asm/mips-boards/generic.h>
43 #include <asm/mips-boards/prom.h>
44 #include <asm/mips-boards/maltaint.h>
45 #include <asm/mc146818-time.h>
46
47 unsigned long cpu_khz;
48
49 #if defined(CONFIG_MIPS_ATLAS)
50 static char display_string[] = "        LINUX ON ATLAS       ";
51 #endif
52 #if defined(CONFIG_MIPS_MALTA)
53 static char display_string[] = "        LINUX ON MALTA       ";
54 #endif
55 #if defined(CONFIG_MIPS_SEAD)
56 static char display_string[] = "        LINUX ON SEAD       ";
57 #endif
58 static unsigned int display_count = 0;
59 #define MAX_DISPLAY_COUNT (sizeof(display_string) - 8)
60
61 static unsigned int timer_tick_count=0;
62 static int mips_cpu_timer_irq;
63
64 static inline void scroll_display_message(void)
65 {
66         if ((timer_tick_count++ % HZ) == 0) {
67                 mips_display_message(&display_string[display_count++]);
68                 if (display_count == MAX_DISPLAY_COUNT)
69                         display_count = 0;
70         }
71 }
72
73 static void mips_timer_dispatch (struct pt_regs *regs)
74 {
75         do_IRQ (mips_cpu_timer_irq, regs);
76 }
77
78 irqreturn_t mips_timer_interrupt(int irq, void *dev_id, struct pt_regs *regs)
79 {
80         int cpu = smp_processor_id();
81
82         if (cpu == 0) {
83                 /*
84                  * CPU 0 handles the global timer interrupt job and process accounting
85                  * resets count/compare registers to trigger next timer int.
86                  */
87                 timer_interrupt(irq, dev_id, regs);
88                 scroll_display_message();
89         } else {
90                 /* Everyone else needs to reset the timer int here as
91                    ll_local_timer_interrupt doesn't */
92                 /*
93                  * FIXME: need to cope with counter underflow.
94                  * More support needs to be added to kernel/time for
95                  * counter/timer interrupts on multiple CPU's
96                  */
97                 write_c0_compare (read_c0_count() + (mips_hpt_frequency/HZ));
98                 /*
99                  * other CPUs should do profiling and process accounting
100                  */
101                 local_timer_interrupt (irq, dev_id, regs);
102         }
103
104         return IRQ_HANDLED;
105 }
106
107 /*
108  * Estimate CPU frequency.  Sets mips_counter_frequency as a side-effect
109  */
110 static unsigned int __init estimate_cpu_frequency(void)
111 {
112         unsigned int prid = read_c0_prid() & 0xffff00;
113         unsigned int count;
114
115 #ifdef CONFIG_MIPS_SEAD
116         /*
117          * The SEAD board doesn't have a real time clock, so we can't
118          * really calculate the timer frequency
119          * For now we hardwire the SEAD board frequency to 12MHz.
120          */
121
122         if ((prid == (PRID_COMP_MIPS | PRID_IMP_20KC)) ||
123             (prid == (PRID_COMP_MIPS | PRID_IMP_25KF)))
124                 count = 12000000;
125         else
126                 count = 6000000;
127 #endif
128 #if defined(CONFIG_MIPS_ATLAS) || defined(CONFIG_MIPS_MALTA)
129         unsigned int flags;
130
131         local_irq_save(flags);
132
133         /* Start counter exactly on falling edge of update flag */
134         while (CMOS_READ(RTC_REG_A) & RTC_UIP);
135         while (!(CMOS_READ(RTC_REG_A) & RTC_UIP));
136
137         /* Start r4k counter. */
138         write_c0_count(0);
139
140         /* Read counter exactly on falling edge of update flag */
141         while (CMOS_READ(RTC_REG_A) & RTC_UIP);
142         while (!(CMOS_READ(RTC_REG_A) & RTC_UIP));
143
144         count = read_c0_count();
145
146         /* restore interrupts */
147         local_irq_restore(flags);
148 #endif
149
150         mips_hpt_frequency = count;
151         if ((prid != (PRID_COMP_MIPS | PRID_IMP_20KC)) &&
152             (prid != (PRID_COMP_MIPS | PRID_IMP_25KF)))
153                 count *= 2;
154
155         count += 5000;    /* round */
156         count -= count%10000;
157
158         return count;
159 }
160
161 unsigned long __init mips_rtc_get_time(void)
162 {
163         return mc146818_get_cmos_time();
164 }
165
166 void __init mips_time_init(void)
167 {
168         unsigned int est_freq, flags;
169
170         local_irq_save(flags);
171
172         /* Set Data mode - binary. */
173         CMOS_WRITE(CMOS_READ(RTC_CONTROL) | RTC_DM_BINARY, RTC_CONTROL);
174
175         est_freq = estimate_cpu_frequency ();
176
177         printk("CPU frequency %d.%02d MHz\n", est_freq/1000000,
178                (est_freq%1000000)*100/1000000);
179
180         cpu_khz = est_freq / 1000;
181
182         local_irq_restore(flags);
183 }
184
185 void __init mips_timer_setup(struct irqaction *irq)
186 {
187         if (cpu_has_veic) {
188                 set_vi_handler (MSC01E_INT_CPUCTR, mips_timer_dispatch);
189                 mips_cpu_timer_irq = MSC01E_INT_BASE + MSC01E_INT_CPUCTR;
190         }
191         else {
192                 if (cpu_has_vint)
193                         set_vi_handler (MIPSCPU_INT_CPUCTR, mips_timer_dispatch);
194                 mips_cpu_timer_irq = MIPSCPU_INT_BASE + MIPSCPU_INT_CPUCTR;
195         }
196
197
198         /* we are using the cpu counter for timer interrupts */
199         irq->handler = mips_timer_interrupt;    /* we use our own handler */
200         setup_irq(mips_cpu_timer_irq, irq);
201
202 #ifdef CONFIG_SMP
203         /* irq_desc(riptor) is a global resource, when the interrupt overlaps
204            on seperate cpu's the first one tries to handle the second interrupt.
205            The effect is that the int remains disabled on the second cpu.
206            Mark the interrupt with IRQ_PER_CPU to avoid any confusion */
207         irq_desc[mips_cpu_timer_irq].status |= IRQ_PER_CPU;
208 #endif
209
210         /* to generate the first timer interrupt */
211         write_c0_compare (read_c0_count() + mips_hpt_frequency/HZ);
212 }