ftp://ftp.kernel.org/pub/linux/kernel/v2.6/linux-2.6.6.tar.bz2
[linux-2.6.git] / arch / mips / vr41xx / common / rtc.c
1 /*
2  *  rtc.c, RTC(has only timer function) routines for NEC VR4100 series.
3  *
4  *  Copyright (C) 2003  Yoichi Yuasa <yuasa@hh.iij4u.or.jp>
5  *
6  *  This program is free software; you can redistribute it and/or modify
7  *  it under the terms of the GNU General Public License as published by
8  *  the Free Software Foundation; either version 2 of the License, or
9  *  (at your option) any later version.
10  *
11  *  This program is distributed in the hope that it will be useful,
12  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
13  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  *  GNU General Public License for more details.
15  *
16  *  You should have received a copy of the GNU General Public License
17  *  along with this program; if not, write to the Free Software
18  *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
19  */
20 #include <linux/init.h>
21 #include <linux/irq.h>
22 #include <linux/smp.h>
23 #include <linux/types.h>
24
25 #include <asm/io.h>
26 #include <asm/time.h>
27 #include <asm/vr41xx/vr41xx.h>
28
29 static uint32_t rtc1_base;
30 static uint32_t rtc2_base;
31
32 static uint64_t previous_elapsedtime;
33 static unsigned int remainder_per_sec;
34 static unsigned int cycles_per_sec;
35 static unsigned int cycles_per_jiffy;
36 static unsigned long epoch_time;
37
38 #define CYCLES_PER_JIFFY        (CLOCK_TICK_RATE / HZ)
39 #define REMAINDER_PER_SEC       (CLOCK_TICK_RATE - (CYCLES_PER_JIFFY * HZ))
40 #define CYCLES_PER_100USEC      ((CLOCK_TICK_RATE + (10000 / 2)) / 10000)
41
42 #define ETIMELREG_TYPE1         KSEG1ADDR(0x0b0000c0)
43 #define TCLKLREG_TYPE1          KSEG1ADDR(0x0b0001c0)
44
45 #define ETIMELREG_TYPE2         KSEG1ADDR(0x0f000100)
46 #define TCLKLREG_TYPE2          KSEG1ADDR(0x0f000120)
47
48 /* RTC 1 registers */
49 #define ETIMELREG               0x00
50 #define ETIMEMREG               0x02
51 #define ETIMEHREG               0x04
52 /* RFU */
53 #define ECMPLREG                0x08
54 #define ECMPMREG                0x0a
55 #define ECMPHREG                0x0c
56 /* RFU */
57 #define RTCL1LREG               0x10
58 #define RTCL1HREG               0x12
59 #define RTCL1CNTLREG            0x14
60 #define RTCL1CNTHREG            0x16
61 #define RTCL2LREG               0x18
62 #define RTCL2HREG               0x1a
63 #define RTCL2CNTLREG            0x1c
64 #define RTCL2CNTHREG            0x1e
65
66 /* RTC 2 registers */
67 #define TCLKLREG                0x00
68 #define TCLKHREG                0x02
69 #define TCLKCNTLREG             0x04
70 #define TCLKCNTHREG             0x06
71 /* RFU */
72 #define RTCINTREG               0x1e
73  #define TCLOCK_INT             0x08
74  #define RTCLONG2_INT           0x04
75  #define RTCLONG1_INT           0x02
76  #define ELAPSEDTIME_INT        0x01
77
78 #define read_rtc1(offset)       readw(rtc1_base + (offset))
79 #define write_rtc1(val, offset) writew((val), rtc1_base + (offset))
80
81 #define read_rtc2(offset)       readw(rtc2_base + (offset))
82 #define write_rtc2(val, offset) writew((val), rtc2_base + (offset))
83
84 static inline uint64_t read_elapsedtime_counter(void)
85 {
86         uint64_t first, second;
87         uint32_t first_mid, first_low;
88         uint32_t second_mid, second_low;
89
90         do {
91                 first_low = (uint32_t)read_rtc1(ETIMELREG);
92                 first_mid = (uint32_t)read_rtc1(ETIMEMREG);
93                 first = (uint64_t)read_rtc1(ETIMEHREG);
94                 second_low = (uint32_t)read_rtc1(ETIMELREG);
95                 second_mid = (uint32_t)read_rtc1(ETIMEMREG);
96                 second = (uint64_t)read_rtc1(ETIMEHREG);
97         } while (first_low != second_low || first_mid != second_mid ||
98                  first != second);
99
100         return (first << 32) | (uint64_t)((first_mid << 16) | first_low);
101 }
102
103 static inline void write_elapsedtime_counter(uint64_t time)
104 {
105         write_rtc1((uint16_t)time, ETIMELREG);
106         write_rtc1((uint16_t)(time >> 16), ETIMEMREG);
107         write_rtc1((uint16_t)(time >> 32), ETIMEHREG);
108 }
109
110 static inline void write_elapsedtime_compare(uint64_t time)
111 {
112         write_rtc1((uint16_t)time, ECMPLREG);
113         write_rtc1((uint16_t)(time >> 16), ECMPMREG);
114         write_rtc1((uint16_t)(time >> 32), ECMPHREG);
115 }
116
117 void vr41xx_set_rtclong1_cycle(uint32_t cycles)
118 {
119         write_rtc1((uint16_t)cycles, RTCL1LREG);
120         write_rtc1((uint16_t)(cycles >> 16), RTCL1HREG);
121 }
122
123 uint32_t vr41xx_read_rtclong1_counter(void)
124 {
125         uint32_t first_high, first_low;
126         uint32_t second_high, second_low;
127
128         do {
129                 first_low = (uint32_t)read_rtc1(RTCL1CNTLREG);
130                 first_high = (uint32_t)read_rtc1(RTCL1CNTHREG);
131                 second_low = (uint32_t)read_rtc1(RTCL1CNTLREG);
132                 second_high = (uint32_t)read_rtc1(RTCL1CNTHREG);
133         } while (first_low != second_low || first_high != second_high);
134
135         return (first_high << 16) | first_low;
136 }
137
138 void vr41xx_set_rtclong2_cycle(uint32_t cycles)
139 {
140         write_rtc1((uint16_t)cycles, RTCL2LREG);
141         write_rtc1((uint16_t)(cycles >> 16), RTCL2HREG);
142 }
143
144 uint32_t vr41xx_read_rtclong2_counter(void)
145 {
146         uint32_t first_high, first_low;
147         uint32_t second_high, second_low;
148
149         do {
150                 first_low = (uint32_t)read_rtc1(RTCL2CNTLREG);
151                 first_high = (uint32_t)read_rtc1(RTCL2CNTHREG);
152                 second_low = (uint32_t)read_rtc1(RTCL2CNTLREG);
153                 second_high = (uint32_t)read_rtc1(RTCL2CNTHREG);
154         } while (first_low != second_low || first_high != second_high);
155
156         return (first_high << 16) | first_low;
157 }
158
159 void vr41xx_set_tclock_cycle(uint32_t cycles)
160 {
161         write_rtc2((uint16_t)cycles, TCLKLREG);
162         write_rtc2((uint16_t)(cycles >> 16), TCLKHREG);
163 }
164
165 uint32_t vr41xx_read_tclock_counter(void)
166 {
167         uint32_t first_high, first_low;
168         uint32_t second_high, second_low;
169
170         do {
171                 first_low = (uint32_t)read_rtc2(TCLKCNTLREG);
172                 first_high = (uint32_t)read_rtc2(TCLKCNTHREG);
173                 second_low = (uint32_t)read_rtc2(TCLKCNTLREG);
174                 second_high = (uint32_t)read_rtc2(TCLKCNTHREG);
175         } while (first_low != second_low || first_high != second_high);
176
177         return (first_high << 16) | first_low;
178 }
179
180 static void vr41xx_timer_ack(void)
181 {
182         uint64_t cur;
183
184         write_rtc2(ELAPSEDTIME_INT, RTCINTREG);
185
186         previous_elapsedtime += (uint64_t)cycles_per_jiffy;
187         cycles_per_sec += cycles_per_jiffy;
188
189         if (cycles_per_sec >= CLOCK_TICK_RATE) {
190                 cycles_per_sec = 0;
191                 remainder_per_sec = REMAINDER_PER_SEC;
192         }
193
194         cycles_per_jiffy = 0;
195
196         do {
197                 cycles_per_jiffy += CYCLES_PER_JIFFY;
198                 if (remainder_per_sec > 0) {
199                         cycles_per_jiffy++;
200                         remainder_per_sec--;
201                 }
202
203                 cur = read_elapsedtime_counter();
204         } while (cur >= previous_elapsedtime + (uint64_t)cycles_per_jiffy);
205
206         write_elapsedtime_compare(previous_elapsedtime + (uint64_t)cycles_per_jiffy);
207 }
208
209 static void vr41xx_hpt_init(unsigned int count)
210 {
211 }
212
213 static unsigned int vr41xx_hpt_read(void)
214 {
215         uint64_t cur;
216
217         cur = read_elapsedtime_counter();
218
219         return (unsigned int)cur;
220 }
221
222 static unsigned long vr41xx_gettimeoffset(void)
223 {
224         uint64_t cur;
225         unsigned long gap;
226
227         cur = read_elapsedtime_counter();
228         gap = (unsigned long)(cur - previous_elapsedtime);
229         gap = gap / CYCLES_PER_100USEC * 100;   /* usec */
230
231         return gap;
232 }
233
234 static unsigned long vr41xx_get_time(void)
235 {
236         uint64_t counts;
237
238         counts = read_elapsedtime_counter();
239         counts >>= 15;
240
241         return epoch_time + (unsigned long)counts;
242
243 }
244
245 static int vr41xx_set_time(unsigned long sec)
246 {
247         if (sec < epoch_time)
248                 return -EINVAL;
249
250         sec -= epoch_time;
251
252         write_elapsedtime_counter((uint64_t)sec << 15);
253
254         return 0;
255 }
256
257 void vr41xx_set_epoch_time(unsigned long time)
258 {
259         epoch_time = time;
260 }
261
262 static void __init vr41xx_time_init(void)
263 {
264         switch (current_cpu_data.cputype) {
265         case CPU_VR4111:
266         case CPU_VR4121:
267                 rtc1_base = ETIMELREG_TYPE1;
268                 rtc2_base = TCLKLREG_TYPE1;
269                 break;
270         case CPU_VR4122:
271         case CPU_VR4131:
272         case CPU_VR4133:
273                 rtc1_base = ETIMELREG_TYPE2;
274                 rtc2_base = TCLKLREG_TYPE2;
275                 break;
276         default:
277                 panic("Unexpected CPU of NEC VR4100 series");
278                 break;
279         }
280
281         mips_timer_ack = vr41xx_timer_ack;
282
283         mips_hpt_init = vr41xx_hpt_init;
284         mips_hpt_read = vr41xx_hpt_read;
285         mips_hpt_frequency = CLOCK_TICK_RATE;
286
287         if (epoch_time == 0)
288                 epoch_time = mktime(1970, 1, 1, 0, 0, 0);
289
290         rtc_get_time = vr41xx_get_time;
291         rtc_set_time = vr41xx_set_time;
292 }
293
294 static void __init vr41xx_timer_setup(struct irqaction *irq)
295 {
296         do_gettimeoffset = vr41xx_gettimeoffset;
297
298         remainder_per_sec = REMAINDER_PER_SEC;
299         cycles_per_jiffy = CYCLES_PER_JIFFY;
300
301         if (remainder_per_sec > 0) {
302                 cycles_per_jiffy++;
303                 remainder_per_sec--;
304         }
305
306         previous_elapsedtime = read_elapsedtime_counter();
307         write_elapsedtime_compare(previous_elapsedtime + (uint64_t)cycles_per_jiffy);
308         write_rtc2(ELAPSEDTIME_INT, RTCINTREG);
309
310         setup_irq(ELAPSEDTIME_IRQ, irq);
311 }
312
313 void __init vr41xx_rtc_init(void)
314 {
315         board_time_init = vr41xx_time_init;
316         board_timer_setup = vr41xx_timer_setup;
317 }