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> | |
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 | { | |
42a3b4f2 | 75 | if (rtc_dm_binary()) |
1da177e4 LT |
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 | { | |
42a3b4f2 | 94 | if (rtc_24h()) |
1da177e4 | 95 | return bin_to_hw(c); |
42a3b4f2 | 96 | if (c >= 12) |
1da177e4 LT |
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; | |
42a3b4f2 | 108 | if (c & 0x80) |
1da177e4 | 109 | return (tmp==12)?12:tmp+12; /* 12pm is 12, not 24 */ |
42a3b4f2 | 110 | else |
1da177e4 LT |
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 | ||
42a3b4f2 | 148 | static unsigned long |
1da177e4 LT |
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)); | |
42a3b4f2 | 169 | year = hw_to_bin(CMOS_READ(RTC_YEAR)) + |
1da177e4 LT |
170 | hw_to_bin(*rtc_century_reg) * 100; |
171 | ||
172 | /* restore interrupts */ | |
173 | local_irq_restore(flags); | |
42a3b4f2 | 174 | |
1da177e4 LT |
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 | } |