Commit | Line | Data |
---|---|---|
1da177e4 LT |
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> | |
ed00e87f | 32 | #include <linux/mc146818rtc.h> |
1da177e4 LT |
33 | |
34 | #include <asm/time.h> | |
35 | #include <asm/mipsregs.h> | |
36 | #include <asm/ptrace.h> | |
37 | #include <asm/it8172/it8172.h> | |
38 | #include <asm/it8172/it8172_int.h> | |
39 | #include <asm/debug.h> | |
40 | ||
41 | #define IT8172_RTC_ADR_REG (IT8172_PCI_IO_BASE + IT_RTC_BASE) | |
42 | #define IT8172_RTC_DAT_REG (IT8172_RTC_ADR_REG + 1) | |
43 | #define IT8172_RTC_CENTURY_REG (IT8172_PCI_IO_BASE + IT_RTC_CENTURY) | |
44 | ||
45 | static volatile char *rtc_adr_reg = (char*)KSEG1ADDR(IT8172_RTC_ADR_REG); | |
46 | static volatile char *rtc_dat_reg = (char*)KSEG1ADDR(IT8172_RTC_DAT_REG); | |
47 | static volatile char *rtc_century_reg = (char*)KSEG1ADDR(IT8172_RTC_CENTURY_REG); | |
48 | ||
49 | unsigned char it8172_rtc_read_data(unsigned long addr) | |
50 | { | |
51 | unsigned char retval; | |
52 | ||
53 | *rtc_adr_reg = addr; | |
54 | retval = *rtc_dat_reg; | |
55 | return retval; | |
56 | } | |
57 | ||
58 | void it8172_rtc_write_data(unsigned char data, unsigned long addr) | |
59 | { | |
60 | *rtc_adr_reg = addr; | |
61 | *rtc_dat_reg = data; | |
62 | } | |
63 | ||
64 | #undef CMOS_READ | |
65 | #undef CMOS_WRITE | |
66 | #define CMOS_READ(addr) it8172_rtc_read_data(addr) | |
67 | #define CMOS_WRITE(data, addr) it8172_rtc_write_data(data, addr) | |
68 | ||
69 | static unsigned char saved_control; /* remember rtc control reg */ | |
70 | static inline int rtc_24h(void) { return saved_control & RTC_24H; } | |
71 | static inline int rtc_dm_binary(void) { return saved_control & RTC_DM_BINARY; } | |
72 | ||
73 | static inline unsigned char | |
74 | bin_to_hw(unsigned char c) | |
75 | { | |
42a3b4f2 | 76 | if (rtc_dm_binary()) |
1da177e4 LT |
77 | return c; |
78 | else | |
79 | return ((c/10) << 4) + (c%10); | |
80 | } | |
81 | ||
82 | static inline unsigned char | |
83 | hw_to_bin(unsigned char c) | |
84 | { | |
85 | if (rtc_dm_binary()) | |
86 | return c; | |
87 | else | |
88 | return (c>>4)*10 + (c &0xf); | |
89 | } | |
90 | ||
91 | /* 0x80 bit indicates pm in 12-hour format */ | |
92 | static inline unsigned char | |
93 | hour_bin_to_hw(unsigned char c) | |
94 | { | |
42a3b4f2 | 95 | if (rtc_24h()) |
1da177e4 | 96 | return bin_to_hw(c); |
42a3b4f2 | 97 | if (c >= 12) |
1da177e4 LT |
98 | return 0x80 | bin_to_hw((c==12)?12:c-12); /* 12 is 12pm */ |
99 | else | |
100 | return bin_to_hw((c==0)?12:c); /* 0 is 12 AM, not 0 am */ | |
101 | } | |
102 | ||
103 | static inline unsigned char | |
104 | hour_hw_to_bin(unsigned char c) | |
105 | { | |
106 | unsigned char tmp = hw_to_bin(c&0x3f); | |
107 | if (rtc_24h()) | |
108 | return tmp; | |
42a3b4f2 | 109 | if (c & 0x80) |
1da177e4 | 110 | return (tmp==12)?12:tmp+12; /* 12pm is 12, not 24 */ |
42a3b4f2 | 111 | else |
1da177e4 LT |
112 | return (tmp==12)?0:tmp; /* 12am is 0 */ |
113 | } | |
114 | ||
115 | static unsigned long r4k_offset; /* Amount to increment compare reg each time */ | |
116 | static unsigned long r4k_cur; /* What counter should be at next timer irq */ | |
117 | extern unsigned int mips_hpt_frequency; | |
118 | ||
119 | /* | |
120 | * Figure out the r4k offset, the amount to increment the compare | |
121 | * register for each time tick. | |
122 | * Use the RTC to calculate offset. | |
123 | */ | |
124 | static unsigned long __init cal_r4koff(void) | |
125 | { | |
126 | unsigned int flags; | |
127 | ||
128 | local_irq_save(flags); | |
129 | ||
130 | /* Start counter exactly on falling edge of update flag */ | |
131 | while (CMOS_READ(RTC_REG_A) & RTC_UIP); | |
132 | while (!(CMOS_READ(RTC_REG_A) & RTC_UIP)); | |
133 | ||
134 | /* Start r4k counter. */ | |
135 | write_c0_count(0); | |
136 | ||
137 | /* Read counter exactly on falling edge of update flag */ | |
138 | while (CMOS_READ(RTC_REG_A) & RTC_UIP); | |
139 | while (!(CMOS_READ(RTC_REG_A) & RTC_UIP)); | |
140 | ||
141 | mips_hpt_frequency = read_c0_count(); | |
142 | ||
143 | /* restore interrupts */ | |
144 | local_irq_restore(flags); | |
145 | ||
146 | return (mips_hpt_frequency / HZ); | |
147 | } | |
148 | ||
42a3b4f2 | 149 | static unsigned long |
1da177e4 LT |
150 | it8172_rtc_get_time(void) |
151 | { | |
152 | unsigned int year, mon, day, hour, min, sec; | |
153 | unsigned int flags; | |
154 | ||
155 | /* avoid update-in-progress. */ | |
156 | for (;;) { | |
157 | local_irq_save(flags); | |
158 | if (! (CMOS_READ(RTC_REG_A) & RTC_UIP)) | |
159 | break; | |
160 | /* don't hold intr closed all the time */ | |
161 | local_irq_restore(flags); | |
162 | } | |
163 | ||
164 | /* Read regs. */ | |
165 | sec = hw_to_bin(CMOS_READ(RTC_SECONDS)); | |
166 | min = hw_to_bin(CMOS_READ(RTC_MINUTES)); | |
167 | hour = hour_hw_to_bin(CMOS_READ(RTC_HOURS)); | |
168 | day = hw_to_bin(CMOS_READ(RTC_DAY_OF_MONTH)); | |
169 | mon = hw_to_bin(CMOS_READ(RTC_MONTH)); | |
42a3b4f2 | 170 | year = hw_to_bin(CMOS_READ(RTC_YEAR)) + |
1da177e4 LT |
171 | hw_to_bin(*rtc_century_reg) * 100; |
172 | ||
173 | /* restore interrupts */ | |
174 | local_irq_restore(flags); | |
42a3b4f2 | 175 | |
1da177e4 LT |
176 | return mktime(year, mon, day, hour, min, sec); |
177 | } | |
178 | ||
179 | static int | |
180 | it8172_rtc_set_time(unsigned long t) | |
181 | { | |
182 | struct rtc_time tm; | |
183 | unsigned int flags; | |
184 | ||
185 | /* convert */ | |
186 | to_tm(t, &tm); | |
187 | ||
188 | /* avoid update-in-progress. */ | |
189 | for (;;) { | |
190 | local_irq_save(flags); | |
191 | if (! (CMOS_READ(RTC_REG_A) & RTC_UIP)) | |
192 | break; | |
193 | /* don't hold intr closed all the time */ | |
194 | local_irq_restore(flags); | |
195 | } | |
196 | ||
197 | *rtc_century_reg = bin_to_hw(tm.tm_year/100); | |
198 | CMOS_WRITE(bin_to_hw(tm.tm_sec), RTC_SECONDS); | |
199 | CMOS_WRITE(bin_to_hw(tm.tm_min), RTC_MINUTES); | |
200 | CMOS_WRITE(hour_bin_to_hw(tm.tm_hour), RTC_HOURS); | |
201 | CMOS_WRITE(bin_to_hw(tm.tm_mday), RTC_DAY_OF_MONTH); | |
202 | CMOS_WRITE(bin_to_hw(tm.tm_mon+1), RTC_MONTH); /* tm_mon starts from 0 */ | |
203 | CMOS_WRITE(bin_to_hw(tm.tm_year%100), RTC_YEAR); | |
204 | ||
205 | /* restore interrupts */ | |
206 | local_irq_restore(flags); | |
207 | ||
208 | return 0; | |
209 | } | |
210 | ||
211 | void __init it8172_time_init(void) | |
212 | { | |
213 | unsigned int est_freq, flags; | |
214 | ||
215 | local_irq_save(flags); | |
216 | ||
217 | saved_control = CMOS_READ(RTC_CONTROL); | |
218 | ||
219 | printk("calculating r4koff... "); | |
220 | r4k_offset = cal_r4koff(); | |
221 | printk("%08lx(%d)\n", r4k_offset, (int) r4k_offset); | |
222 | ||
223 | est_freq = 2*r4k_offset*HZ; | |
224 | est_freq += 5000; /* round */ | |
225 | est_freq -= est_freq%10000; | |
226 | printk("CPU frequency %d.%02d MHz\n", est_freq/1000000, | |
227 | (est_freq%1000000)*100/1000000); | |
228 | ||
229 | local_irq_restore(flags); | |
230 | ||
d23ee8fe YY |
231 | rtc_mips_get_time = it8172_rtc_get_time; |
232 | rtc_mips_set_time = it8172_rtc_set_time; | |
1da177e4 LT |
233 | } |
234 | ||
235 | #define ALLINTS (IE_IRQ0 | IE_IRQ1 | IE_IRQ2 | IE_IRQ3 | IE_IRQ4 | IE_IRQ5) | |
236 | void __init it8172_timer_setup(struct irqaction *irq) | |
237 | { | |
238 | puts("timer_setup\n"); | |
239 | put32(NR_IRQS); | |
240 | puts(""); | |
241 | /* we are using the cpu counter for timer interrupts */ | |
242 | setup_irq(MIPS_CPU_TIMER_IRQ, irq); | |
243 | ||
244 | /* to generate the first timer interrupt */ | |
245 | r4k_cur = (read_c0_count() + r4k_offset); | |
246 | write_c0_compare(r4k_cur); | |
247 | set_c0_status(ALLINTS); | |
248 | } |