nand: boot code cleanup
[qemu/mini2440.git] / hw / s3c24xx_rtc.c
blob1a5b79cbc8b3f990d11dadd739e50a4ce6e18f19
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;
127 switch (addr) {
128 case S3C_RTC_CON:
129 return s->control;
131 case S3C_RTC_TICNT:
132 return s->tick;
134 case S3C_RTC_ALM:
135 return s->alarm;
136 case S3C_RTC_ALMSEC:
137 return s->almsec;
138 case S3C_RTC_ALMMIN:
139 return s->almmin;
140 case S3C_RTC_ALMHOUR:
141 return s->almhour;
142 case S3C_RTC_ALMDATE:
143 return s->almday;
144 case S3C_RTC_ALMMON:
145 return s->almmon;
146 case S3C_RTC_ALMYEAR:
147 return s->almyear;
149 case S3C_RTC_RST:
150 return s->reset;
152 case S3C_RTC_BCDSEC:
153 s3c_rtc_update(s);
154 return to_bcd(s->tm.tm_sec);
155 case S3C_RTC_BCDMIN:
156 s3c_rtc_update(s);
157 return to_bcd(s->tm.tm_min);
158 case S3C_RTC_BCDHOUR:
159 s3c_rtc_update(s);
160 return to_bcd(s->tm.tm_hour);
161 case S3C_RTC_BCDDATE:
162 s3c_rtc_update(s);
163 return to_bcd(s->tm.tm_mday);
164 case S3C_RTC_BCDDAY:
165 s3c_rtc_update(s);
166 return s->tm.tm_wday;
167 case S3C_RTC_BCDMON:
168 s3c_rtc_update(s);
169 return to_bcd(s->tm.tm_mon + 1);
170 case S3C_RTC_BCDYEAR:
171 s3c_rtc_update(s);
172 return to_bcd(s->tm.tm_year % 100);
173 default:
174 printf("%s: Bad register 0x%lx\n", __FUNCTION__, (unsigned long)addr);
175 break;
177 return 0;
180 static void s3c_rtc_write(void *opaque, target_phys_addr_t addr,
181 uint32_t value)
183 struct s3c_rtc_state_s *s = (struct s3c_rtc_state_s *) opaque;
184 int diff;
186 switch (addr) {
187 case S3C_RTC_CON:
188 s->control = value & 0xf;
189 s->enable = (s->control == 0x1);
190 break;
192 case S3C_RTC_TICNT:
193 s->tick = value;
194 if (s->tick & (1 << 7))
195 s3c_rtc_tick_mod(s);
196 break;
198 case S3C_RTC_ALM:
199 s->alarm = value;
200 break;
201 case S3C_RTC_ALMSEC:
202 s->almsec = value;
203 break;
204 case S3C_RTC_ALMMIN:
205 s->almmin = value;
206 break;
207 case S3C_RTC_ALMHOUR:
208 s->almhour = value;
209 break;
210 case S3C_RTC_ALMDATE:
211 s->almday = value;
212 break;
213 case S3C_RTC_ALMMON:
214 s->almmon = value;
215 break;
216 case S3C_RTC_ALMYEAR:
217 s->almyear = value;
218 break;
220 case S3C_RTC_RST:
221 s->reset = value & 0xf;
222 break;
224 /* XXX This is not very exact time setting */
225 case S3C_RTC_BCDSEC:
226 s3c_rtc_update(s);
227 diff = from_bcd(value) - s->tm.tm_sec;
228 s->sec += diff * 1;
229 break;
230 case S3C_RTC_BCDMIN:
231 s3c_rtc_update(s);
232 diff = from_bcd(value) - s->tm.tm_min;
233 s->sec += diff * 60;
234 break;
235 case S3C_RTC_BCDHOUR:
236 s3c_rtc_update(s);
237 diff = from_bcd(value) - s->tm.tm_hour;
238 s->sec += diff * 60 * 60;
239 break;
240 case S3C_RTC_BCDDATE:
241 s3c_rtc_update(s);
242 diff = from_bcd(value) - s->tm.tm_mday;
243 s->sec += diff * 60 * 60 * 24;
244 break;
245 case S3C_RTC_BCDDAY:
246 s3c_rtc_update(s);
247 diff = (value & 7) - s->tm.tm_wday;
248 s->sec += diff * 60 * 60 * 24;
249 break;
250 case S3C_RTC_BCDMON:
251 s3c_rtc_update(s);
252 diff = from_bcd(value) - s->tm.tm_mon - 1;
253 s->sec += diff * 60 * 60 * 24 * 30;
254 break;
255 case S3C_RTC_BCDYEAR:
256 s3c_rtc_update(s);
257 diff = from_bcd(value) - (s->tm.tm_year % 100);
258 s->sec += diff * 60 * 60 * 24 * 365;
259 break;
260 default:
261 printf("%s: Bad register 0x%lx\n", __FUNCTION__, (unsigned long)addr);
265 static CPUReadMemoryFunc *s3c_rtc_readfn[] = {
266 s3c_rtc_read,
267 s3c_rtc_read,
268 s3c_rtc_read,
271 static CPUWriteMemoryFunc *s3c_rtc_writefn[] = {
272 s3c_rtc_write,
273 s3c_rtc_write,
274 s3c_rtc_write,
277 static void s3c_rtc_save(QEMUFile *f, void *opaque)
279 struct s3c_rtc_state_s *s = (struct s3c_rtc_state_s *) opaque;
280 qemu_put_sbe64s(f, &s->next);
281 qemu_put_8s(f, &s->control);
282 qemu_put_8s(f, &s->tick);
283 qemu_put_8s(f, &s->alarm);
284 qemu_put_8s(f, &s->almsec);
285 qemu_put_8s(f, &s->almmin);
286 qemu_put_8s(f, &s->almday);
287 qemu_put_8s(f, &s->almhour);
288 qemu_put_8s(f, &s->almmon);
289 qemu_put_8s(f, &s->almyear);
290 qemu_put_8s(f, &s->reset);
291 qemu_put_be32s(f, &s->sec);
294 static int s3c_rtc_load(QEMUFile *f, void *opaque, int version_id)
296 struct s3c_rtc_state_s *s = (struct s3c_rtc_state_s *) opaque;
297 qemu_get_sbe64s(f, &s->next);
298 qemu_get_8s(f, &s->control);
299 qemu_get_8s(f, &s->tick);
300 qemu_get_8s(f, &s->alarm);
301 qemu_get_8s(f, &s->almsec);
302 qemu_get_8s(f, &s->almmin);
303 qemu_get_8s(f, &s->almday);
304 qemu_get_8s(f, &s->almhour);
305 qemu_get_8s(f, &s->almmon);
306 qemu_get_8s(f, &s->almyear);
307 qemu_get_8s(f, &s->reset);
308 qemu_get_be32s(f, &s->sec);
310 s->enable = (s->control == 0x1);
311 s3c_rtc_tick_mod(s);
313 return 0;
316 struct s3c_rtc_state_s *s3c_rtc_init(target_phys_addr_t base, qemu_irq irq)
318 int iomemtype;
319 struct s3c_rtc_state_s *s = (struct s3c_rtc_state_s *)
320 qemu_mallocz(sizeof(struct s3c_rtc_state_s));
322 s->base = base;
323 s->irq = irq;
324 s->timer = qemu_new_timer(vm_clock, s3c_rtc_tick, s);
325 s->hz = qemu_new_timer(rt_clock, s3c_rtc_hz, s);
327 s3c_rtc_reset(s);
328 s->next = qemu_get_clock(rt_clock) + 1000;
329 qemu_mod_timer(s->hz, s->next);
331 iomemtype = cpu_register_io_memory(0, s3c_rtc_readfn,
332 s3c_rtc_writefn, s);
333 cpu_register_physical_memory(s->base, 0xffffff, iomemtype);
335 register_savevm("s3c24xx_rtc", 0, 0, s3c_rtc_save, s3c_rtc_load, s);
337 return s;