diff options
Diffstat (limited to 'arch/mips/ite-boards/generic/time.c')
-rw-r--r-- | arch/mips/ite-boards/generic/time.c | 247 |
1 files changed, 247 insertions, 0 deletions
diff --git a/arch/mips/ite-boards/generic/time.c b/arch/mips/ite-boards/generic/time.c new file mode 100644 index 000000000000..30a6c0d5fc50 --- /dev/null +++ b/arch/mips/ite-boards/generic/time.c | |||
@@ -0,0 +1,247 @@ | |||
1 | /* | ||
2 | * Carsten Langgaard, carstenl@mips.com | ||
3 | * Copyright (C) 1999,2000 MIPS Technologies, Inc. All rights reserved. | ||
4 | * | ||
5 | * Copyright (C) 2003 MontaVista Software Inc. | ||
6 | * Author: Jun Sun, jsun@mvista.com or jsun@junsun.net | ||
7 | * | ||
8 | * ######################################################################## | ||
9 | * | ||
10 | * This program is free software; you can distribute it and/or modify it | ||
11 | * under the terms of the GNU General Public License (Version 2) as | ||
12 | * published by the Free Software Foundation. | ||
13 | * | ||
14 | * This program is distributed in the hope it will be useful, but WITHOUT | ||
15 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | ||
16 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License | ||
17 | * for more details. | ||
18 | * | ||
19 | * You should have received a copy of the GNU General Public License along | ||
20 | * with this program; if not, write to the Free Software Foundation, Inc., | ||
21 | * 59 Temple Place - Suite 330, Boston MA 02111-1307, USA. | ||
22 | * | ||
23 | * ######################################################################## | ||
24 | * | ||
25 | * Setting up the clock on the MIPS boards. | ||
26 | */ | ||
27 | #include <linux/init.h> | ||
28 | #include <linux/kernel_stat.h> | ||
29 | #include <linux/sched.h> | ||
30 | #include <linux/time.h> | ||
31 | #include <linux/spinlock.h> | ||
32 | |||
33 | #include <asm/time.h> | ||
34 | #include <asm/mipsregs.h> | ||
35 | #include <asm/ptrace.h> | ||
36 | #include <asm/it8172/it8172.h> | ||
37 | #include <asm/it8172/it8172_int.h> | ||
38 | #include <asm/debug.h> | ||
39 | |||
40 | #define IT8172_RTC_ADR_REG (IT8172_PCI_IO_BASE + IT_RTC_BASE) | ||
41 | #define IT8172_RTC_DAT_REG (IT8172_RTC_ADR_REG + 1) | ||
42 | #define IT8172_RTC_CENTURY_REG (IT8172_PCI_IO_BASE + IT_RTC_CENTURY) | ||
43 | |||
44 | static volatile char *rtc_adr_reg = (char*)KSEG1ADDR(IT8172_RTC_ADR_REG); | ||
45 | static volatile char *rtc_dat_reg = (char*)KSEG1ADDR(IT8172_RTC_DAT_REG); | ||
46 | static volatile char *rtc_century_reg = (char*)KSEG1ADDR(IT8172_RTC_CENTURY_REG); | ||
47 | |||
48 | unsigned char it8172_rtc_read_data(unsigned long addr) | ||
49 | { | ||
50 | unsigned char retval; | ||
51 | |||
52 | *rtc_adr_reg = addr; | ||
53 | retval = *rtc_dat_reg; | ||
54 | return retval; | ||
55 | } | ||
56 | |||
57 | void it8172_rtc_write_data(unsigned char data, unsigned long addr) | ||
58 | { | ||
59 | *rtc_adr_reg = addr; | ||
60 | *rtc_dat_reg = data; | ||
61 | } | ||
62 | |||
63 | #undef CMOS_READ | ||
64 | #undef CMOS_WRITE | ||
65 | #define CMOS_READ(addr) it8172_rtc_read_data(addr) | ||
66 | #define CMOS_WRITE(data, addr) it8172_rtc_write_data(data, addr) | ||
67 | |||
68 | static unsigned char saved_control; /* remember rtc control reg */ | ||
69 | static inline int rtc_24h(void) { return saved_control & RTC_24H; } | ||
70 | static inline int rtc_dm_binary(void) { return saved_control & RTC_DM_BINARY; } | ||
71 | |||
72 | static inline unsigned char | ||
73 | bin_to_hw(unsigned char c) | ||
74 | { | ||
75 | if (rtc_dm_binary()) | ||
76 | return c; | ||
77 | else | ||
78 | return ((c/10) << 4) + (c%10); | ||
79 | } | ||
80 | |||
81 | static inline unsigned char | ||
82 | hw_to_bin(unsigned char c) | ||
83 | { | ||
84 | if (rtc_dm_binary()) | ||
85 | return c; | ||
86 | else | ||
87 | return (c>>4)*10 + (c &0xf); | ||
88 | } | ||
89 | |||
90 | /* 0x80 bit indicates pm in 12-hour format */ | ||
91 | static inline unsigned char | ||
92 | hour_bin_to_hw(unsigned char c) | ||
93 | { | ||
94 | if (rtc_24h()) | ||
95 | return bin_to_hw(c); | ||
96 | if (c >= 12) | ||
97 | return 0x80 | bin_to_hw((c==12)?12:c-12); /* 12 is 12pm */ | ||
98 | else | ||
99 | return bin_to_hw((c==0)?12:c); /* 0 is 12 AM, not 0 am */ | ||
100 | } | ||
101 | |||
102 | static inline unsigned char | ||
103 | hour_hw_to_bin(unsigned char c) | ||
104 | { | ||
105 | unsigned char tmp = hw_to_bin(c&0x3f); | ||
106 | if (rtc_24h()) | ||
107 | return tmp; | ||
108 | if (c & 0x80) | ||
109 | return (tmp==12)?12:tmp+12; /* 12pm is 12, not 24 */ | ||
110 | else | ||
111 | return (tmp==12)?0:tmp; /* 12am is 0 */ | ||
112 | } | ||
113 | |||
114 | static unsigned long r4k_offset; /* Amount to increment compare reg each time */ | ||
115 | static unsigned long r4k_cur; /* What counter should be at next timer irq */ | ||
116 | extern unsigned int mips_hpt_frequency; | ||
117 | |||
118 | /* | ||
119 | * Figure out the r4k offset, the amount to increment the compare | ||
120 | * register for each time tick. | ||
121 | * Use the RTC to calculate offset. | ||
122 | */ | ||
123 | static unsigned long __init cal_r4koff(void) | ||
124 | { | ||
125 | unsigned int flags; | ||
126 | |||
127 | local_irq_save(flags); | ||
128 | |||
129 | /* Start counter exactly on falling edge of update flag */ | ||
130 | while (CMOS_READ(RTC_REG_A) & RTC_UIP); | ||
131 | while (!(CMOS_READ(RTC_REG_A) & RTC_UIP)); | ||
132 | |||
133 | /* Start r4k counter. */ | ||
134 | write_c0_count(0); | ||
135 | |||
136 | /* Read counter exactly on falling edge of update flag */ | ||
137 | while (CMOS_READ(RTC_REG_A) & RTC_UIP); | ||
138 | while (!(CMOS_READ(RTC_REG_A) & RTC_UIP)); | ||
139 | |||
140 | mips_hpt_frequency = read_c0_count(); | ||
141 | |||
142 | /* restore interrupts */ | ||
143 | local_irq_restore(flags); | ||
144 | |||
145 | return (mips_hpt_frequency / HZ); | ||
146 | } | ||
147 | |||
148 | static unsigned long | ||
149 | it8172_rtc_get_time(void) | ||
150 | { | ||
151 | unsigned int year, mon, day, hour, min, sec; | ||
152 | unsigned int flags; | ||
153 | |||
154 | /* avoid update-in-progress. */ | ||
155 | for (;;) { | ||
156 | local_irq_save(flags); | ||
157 | if (! (CMOS_READ(RTC_REG_A) & RTC_UIP)) | ||
158 | break; | ||
159 | /* don't hold intr closed all the time */ | ||
160 | local_irq_restore(flags); | ||
161 | } | ||
162 | |||
163 | /* Read regs. */ | ||
164 | sec = hw_to_bin(CMOS_READ(RTC_SECONDS)); | ||
165 | min = hw_to_bin(CMOS_READ(RTC_MINUTES)); | ||
166 | hour = hour_hw_to_bin(CMOS_READ(RTC_HOURS)); | ||
167 | day = hw_to_bin(CMOS_READ(RTC_DAY_OF_MONTH)); | ||
168 | mon = hw_to_bin(CMOS_READ(RTC_MONTH)); | ||
169 | year = hw_to_bin(CMOS_READ(RTC_YEAR)) + | ||
170 | hw_to_bin(*rtc_century_reg) * 100; | ||
171 | |||
172 | /* restore interrupts */ | ||
173 | local_irq_restore(flags); | ||
174 | |||
175 | return mktime(year, mon, day, hour, min, sec); | ||
176 | } | ||
177 | |||
178 | static int | ||
179 | it8172_rtc_set_time(unsigned long t) | ||
180 | { | ||
181 | struct rtc_time tm; | ||
182 | unsigned int flags; | ||
183 | |||
184 | /* convert */ | ||
185 | to_tm(t, &tm); | ||
186 | |||
187 | /* avoid update-in-progress. */ | ||
188 | for (;;) { | ||
189 | local_irq_save(flags); | ||
190 | if (! (CMOS_READ(RTC_REG_A) & RTC_UIP)) | ||
191 | break; | ||
192 | /* don't hold intr closed all the time */ | ||
193 | local_irq_restore(flags); | ||
194 | } | ||
195 | |||
196 | *rtc_century_reg = bin_to_hw(tm.tm_year/100); | ||
197 | CMOS_WRITE(bin_to_hw(tm.tm_sec), RTC_SECONDS); | ||
198 | CMOS_WRITE(bin_to_hw(tm.tm_min), RTC_MINUTES); | ||
199 | CMOS_WRITE(hour_bin_to_hw(tm.tm_hour), RTC_HOURS); | ||
200 | CMOS_WRITE(bin_to_hw(tm.tm_mday), RTC_DAY_OF_MONTH); | ||
201 | CMOS_WRITE(bin_to_hw(tm.tm_mon+1), RTC_MONTH); /* tm_mon starts from 0 */ | ||
202 | CMOS_WRITE(bin_to_hw(tm.tm_year%100), RTC_YEAR); | ||
203 | |||
204 | /* restore interrupts */ | ||
205 | local_irq_restore(flags); | ||
206 | |||
207 | return 0; | ||
208 | } | ||
209 | |||
210 | void __init it8172_time_init(void) | ||
211 | { | ||
212 | unsigned int est_freq, flags; | ||
213 | |||
214 | local_irq_save(flags); | ||
215 | |||
216 | saved_control = CMOS_READ(RTC_CONTROL); | ||
217 | |||
218 | printk("calculating r4koff... "); | ||
219 | r4k_offset = cal_r4koff(); | ||
220 | printk("%08lx(%d)\n", r4k_offset, (int) r4k_offset); | ||
221 | |||
222 | est_freq = 2*r4k_offset*HZ; | ||
223 | est_freq += 5000; /* round */ | ||
224 | est_freq -= est_freq%10000; | ||
225 | printk("CPU frequency %d.%02d MHz\n", est_freq/1000000, | ||
226 | (est_freq%1000000)*100/1000000); | ||
227 | |||
228 | local_irq_restore(flags); | ||
229 | |||
230 | rtc_get_time = it8172_rtc_get_time; | ||
231 | rtc_set_time = it8172_rtc_set_time; | ||
232 | } | ||
233 | |||
234 | #define ALLINTS (IE_IRQ0 | IE_IRQ1 | IE_IRQ2 | IE_IRQ3 | IE_IRQ4 | IE_IRQ5) | ||
235 | void __init it8172_timer_setup(struct irqaction *irq) | ||
236 | { | ||
237 | puts("timer_setup\n"); | ||
238 | put32(NR_IRQS); | ||
239 | puts(""); | ||
240 | /* we are using the cpu counter for timer interrupts */ | ||
241 | setup_irq(MIPS_CPU_TIMER_IRQ, irq); | ||
242 | |||
243 | /* to generate the first timer interrupt */ | ||
244 | r4k_cur = (read_c0_count() + r4k_offset); | ||
245 | write_c0_compare(r4k_cur); | ||
246 | set_c0_status(ALLINTS); | ||
247 | } | ||