[DM9000] Imported Simtec files
[qemu/mini2440.git] / hw / s3c24xx_rtc.c
blobbae17d58236d707266d5d1e11f57b9a5c816efd8
1 /*
2 * Samsung S3C24xx series Real-Time Clock.
4 * Copyright (c) 2007 OpenMoko, Inc.
5 * Author: Andrzej Zaborowski <andrew@openedhand.com>
7 * This code is licenced under the GNU GPL v2.
8 */
10 #include "s3c.h"
11 #include "qemu-timer.h"
12 #include "hw.h"
13 #include "sysemu.h"
15 struct s3c_rtc_state_s {
16 target_phys_addr_t base;
17 qemu_irq irq;
18 int enable;
19 QEMUTimer *timer;
20 QEMUTimer *hz;
21 int64_t next;
22 struct tm tm;
24 uint8_t control;
25 uint8_t tick;
26 uint8_t alarm;
27 uint8_t almsec;
28 uint8_t almmin;
29 uint8_t almday;
30 uint8_t almhour;
31 uint8_t almmon;
32 uint8_t almyear;
33 uint8_t reset;
34 uint32_t sec;
37 void s3c_rtc_reset(struct s3c_rtc_state_s *s)
39 time_t ti;
40 s->control = 0x00;
41 s->enable = 0;
42 s->tick = 0x00;
43 s->alarm = 0x00;
44 s->almsec = 0x00;
45 s->almmin = 0x00;
46 s->almhour = 0x00;
47 s->almday = 0x01;
48 s->almmon = 0x01;
49 s->almyear = 0x00;
50 s->reset = 0;
51 time(&ti);
52 s->sec = ti;
55 static void s3c_rtc_tick_mod(struct s3c_rtc_state_s *s)
57 qemu_mod_timer(s->timer,
58 qemu_get_clock(vm_clock) + muldiv64((s->tick & 0x7f) + 1,
59 ticks_per_sec, S3C_XTAL_FREQ));
62 static void s3c_rtc_tick(void *opaque)
64 struct s3c_rtc_state_s *s = (struct s3c_rtc_state_s *) opaque;
65 if (!(s->tick & (1 << 7)))
66 return;
67 qemu_irq_raise(s->irq);
69 s3c_rtc_tick_mod(s);
72 static void s3c_rtc_hz(void *opaque)
74 struct s3c_rtc_state_s *s = (struct s3c_rtc_state_s *) opaque;
76 s->sec ++;
77 s->next += 1000;
78 qemu_mod_timer(s->hz, s->next);
81 static void s3c_rtc_update(struct s3c_rtc_state_s *s)
83 #if 1
84 qemu_get_timedate(&s->tm, 0);
85 #else
86 void *ret;
87 time_t ti = s->sec;
88 if (rtc_utc)
89 ret = gmtime_r(&ti, &s->tm);
90 else
91 ret = localtime_r(&ti, &s->tm);
92 #endif
95 static inline uint32_t to_bcd(int val)
97 return ((val / 10) << 4) | (val % 10);
100 static inline int from_bcd(uint32_t val)
102 return ((val >> 4) * 10) + (val & 0x0f);
105 #define S3C_RTC_CON 0x40 /* RTC Control register */
106 #define S3C_RTC_TICNT 0x44 /* Tick Time Count register */
107 #define S3C_RTC_ALM 0x50 /* RTC Alarm Control register */
108 #define S3C_RTC_ALMSEC 0x54 /* Alarm Second Data register */
109 #define S3C_RTC_ALMMIN 0x58 /* Alarm Minute Data register */
110 #define S3C_RTC_ALMHOUR 0x5c /* Alarm Hour Data register */
111 #define S3C_RTC_ALMDATE 0x60 /* Alarm Date Data register */
112 #define S3C_RTC_ALMMON 0x64 /* Alarm Month Data register */
113 #define S3C_RTC_ALMYEAR 0x68 /* Alarm Year Data register */
114 #define S3C_RTC_RST 0x6c /* RTC Round Reset register */
115 #define S3C_RTC_BCDSEC 0x70 /* RTC BCD Second register */
116 #define S3C_RTC_BCDMIN 0x74 /* RTC BCD Minute register */
117 #define S3C_RTC_BCDHOUR 0x78 /* RTC BCD Hour register */
118 #define S3C_RTC_BCDDATE 0x7c /* RTC BCD Day register */
119 #define S3C_RTC_BCDDAY 0x80 /* RTC BCD Day register */
120 #define S3C_RTC_BCDMON 0x84 /* RTC BCD Month register */
121 #define S3C_RTC_BCDYEAR 0x88 /* RTC BCD Year register */
123 static uint32_t s3c_rtc_read(void *opaque, target_phys_addr_t addr)
125 struct s3c_rtc_state_s *s = (struct s3c_rtc_state_s *) opaque;
126 addr -= s->base;
128 switch (addr) {
129 case S3C_RTC_CON:
130 return s->control;
132 case S3C_RTC_TICNT:
133 return s->tick;
135 case S3C_RTC_ALM:
136 return s->alarm;
137 case S3C_RTC_ALMSEC:
138 return s->almsec;
139 case S3C_RTC_ALMMIN:
140 return s->almmin;
141 case S3C_RTC_ALMHOUR:
142 return s->almhour;
143 case S3C_RTC_ALMDATE:
144 return s->almday;
145 case S3C_RTC_ALMMON:
146 return s->almmon;
147 case S3C_RTC_ALMYEAR:
148 return s->almyear;
150 case S3C_RTC_RST:
151 return s->reset;
153 case S3C_RTC_BCDSEC:
154 s3c_rtc_update(s);
155 return to_bcd(s->tm.tm_sec);
156 case S3C_RTC_BCDMIN:
157 s3c_rtc_update(s);
158 return to_bcd(s->tm.tm_min);
159 case S3C_RTC_BCDHOUR:
160 s3c_rtc_update(s);
161 return to_bcd(s->tm.tm_hour);
162 case S3C_RTC_BCDDATE:
163 s3c_rtc_update(s);
164 return to_bcd(s->tm.tm_mday);
165 case S3C_RTC_BCDDAY:
166 s3c_rtc_update(s);
167 return s->tm.tm_wday;
168 case S3C_RTC_BCDMON:
169 s3c_rtc_update(s);
170 return to_bcd(s->tm.tm_mon + 1);
171 case S3C_RTC_BCDYEAR:
172 s3c_rtc_update(s);
173 return to_bcd(s->tm.tm_year % 100);
174 default:
175 printf("%s: Bad register 0x%lx\n", __FUNCTION__, (unsigned long)addr);
176 break;
178 return 0;
181 static void s3c_rtc_write(void *opaque, target_phys_addr_t addr,
182 uint32_t value)
184 struct s3c_rtc_state_s *s = (struct s3c_rtc_state_s *) opaque;
185 int diff;
186 addr -= s->base;
188 switch (addr) {
189 case S3C_RTC_CON:
190 s->control = value & 0xf;
191 s->enable = (s->control == 0x1);
192 break;
194 case S3C_RTC_TICNT:
195 s->tick = value;
196 if (s->tick & (1 << 7))
197 s3c_rtc_tick_mod(s);
198 break;
200 case S3C_RTC_ALM:
201 s->alarm = value;
202 break;
203 case S3C_RTC_ALMSEC:
204 s->almsec = value;
205 break;
206 case S3C_RTC_ALMMIN:
207 s->almmin = value;
208 break;
209 case S3C_RTC_ALMHOUR:
210 s->almhour = value;
211 break;
212 case S3C_RTC_ALMDATE:
213 s->almday = value;
214 break;
215 case S3C_RTC_ALMMON:
216 s->almmon = value;
217 break;
218 case S3C_RTC_ALMYEAR:
219 s->almyear = value;
220 break;
222 case S3C_RTC_RST:
223 s->reset = value & 0xf;
224 break;
226 /* XXX This is not very exact time setting */
227 case S3C_RTC_BCDSEC:
228 s3c_rtc_update(s);
229 diff = from_bcd(value) - s->tm.tm_sec;
230 s->sec += diff * 1;
231 break;
232 case S3C_RTC_BCDMIN:
233 s3c_rtc_update(s);
234 diff = from_bcd(value) - s->tm.tm_min;
235 s->sec += diff * 60;
236 break;
237 case S3C_RTC_BCDHOUR:
238 s3c_rtc_update(s);
239 diff = from_bcd(value) - s->tm.tm_hour;
240 s->sec += diff * 60 * 60;
241 break;
242 case S3C_RTC_BCDDATE:
243 s3c_rtc_update(s);
244 diff = from_bcd(value) - s->tm.tm_mday;
245 s->sec += diff * 60 * 60 * 24;
246 break;
247 case S3C_RTC_BCDDAY:
248 s3c_rtc_update(s);
249 diff = (value & 7) - s->tm.tm_wday;
250 s->sec += diff * 60 * 60 * 24;
251 break;
252 case S3C_RTC_BCDMON:
253 s3c_rtc_update(s);
254 diff = from_bcd(value) - s->tm.tm_mon - 1;
255 s->sec += diff * 60 * 60 * 24 * 30;
256 break;
257 case S3C_RTC_BCDYEAR:
258 s3c_rtc_update(s);
259 diff = from_bcd(value) - (s->tm.tm_year % 100);
260 s->sec += diff * 60 * 60 * 24 * 365;
261 break;
262 default:
263 printf("%s: Bad register 0x%lx\n", __FUNCTION__, (unsigned long)addr);
267 static CPUReadMemoryFunc *s3c_rtc_readfn[] = {
268 s3c_rtc_read,
269 s3c_rtc_read,
270 s3c_rtc_read,
273 static CPUWriteMemoryFunc *s3c_rtc_writefn[] = {
274 s3c_rtc_write,
275 s3c_rtc_write,
276 s3c_rtc_write,
279 static void s3c_rtc_save(QEMUFile *f, void *opaque)
281 struct s3c_rtc_state_s *s = (struct s3c_rtc_state_s *) opaque;
282 qemu_put_be64s(f, &s->next);
283 qemu_put_8s(f, &s->control);
284 qemu_put_8s(f, &s->tick);
285 qemu_put_8s(f, &s->alarm);
286 qemu_put_8s(f, &s->almsec);
287 qemu_put_8s(f, &s->almmin);
288 qemu_put_8s(f, &s->almday);
289 qemu_put_8s(f, &s->almhour);
290 qemu_put_8s(f, &s->almmon);
291 qemu_put_8s(f, &s->almyear);
292 qemu_put_8s(f, &s->reset);
293 qemu_put_be32s(f, &s->sec);
296 static int s3c_rtc_load(QEMUFile *f, void *opaque, int version_id)
298 struct s3c_rtc_state_s *s = (struct s3c_rtc_state_s *) opaque;
299 qemu_get_be64s(f, &s->next);
300 qemu_get_8s(f, &s->control);
301 qemu_get_8s(f, &s->tick);
302 qemu_get_8s(f, &s->alarm);
303 qemu_get_8s(f, &s->almsec);
304 qemu_get_8s(f, &s->almmin);
305 qemu_get_8s(f, &s->almday);
306 qemu_get_8s(f, &s->almhour);
307 qemu_get_8s(f, &s->almmon);
308 qemu_get_8s(f, &s->almyear);
309 qemu_get_8s(f, &s->reset);
310 qemu_get_be32s(f, &s->sec);
312 s->enable = (s->control == 0x1);
313 s3c_rtc_tick_mod(s);
315 return 0;
318 struct s3c_rtc_state_s *s3c_rtc_init(target_phys_addr_t base, qemu_irq irq)
320 int iomemtype;
321 struct s3c_rtc_state_s *s = (struct s3c_rtc_state_s *)
322 qemu_mallocz(sizeof(struct s3c_rtc_state_s));
324 s->base = base;
325 s->irq = irq;
326 s->timer = qemu_new_timer(vm_clock, s3c_rtc_tick, s);
327 s->hz = qemu_new_timer(rt_clock, s3c_rtc_hz, s);
329 s3c_rtc_reset(s);
330 s->next = qemu_get_clock(rt_clock) + 1000;
331 qemu_mod_timer(s->hz, s->next);
333 iomemtype = cpu_register_io_memory(0, s3c_rtc_readfn,
334 s3c_rtc_writefn, s);
335 cpu_register_physical_memory(s->base, 0xffffff, iomemtype);
337 register_savevm("s3c24xx_rtc", 0, 0, s3c_rtc_save, s3c_rtc_load, s);
339 return s;