ppc: suppressed unneeded globals and headers - added explicit type for ppc nvram
[qemu] / hw / m48t59.c
1 /*
2  * QEMU M48T59 NVRAM emulation for PPC PREP platform
3  * 
4  * Copyright (c) 2003-2004 Jocelyn Mayer
5  * 
6  * Permission is hereby granted, free of charge, to any person obtaining a copy
7  * of this software and associated documentation files (the "Software"), to deal
8  * in the Software without restriction, including without limitation the rights
9  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10  * copies of the Software, and to permit persons to whom the Software is
11  * furnished to do so, subject to the following conditions:
12  *
13  * The above copyright notice and this permission notice shall be included in
14  * all copies or substantial portions of the Software.
15  *
16  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
19  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22  * THE SOFTWARE.
23  */
24 #include "vl.h"
25 #include "m48t59.h"
26
27 //#define NVRAM_DEBUG
28
29 #if defined(NVRAM_DEBUG)
30 #define NVRAM_PRINTF(fmt, args...) do { printf(fmt , ##args); } while (0)
31 #else
32 #define NVRAM_PRINTF(fmt, args...) do { } while (0)
33 #endif
34
35 struct m48t59_t {
36     /* Hardware parameters */
37     int      IRQ;
38     uint32_t io_base;
39     uint16_t size;
40     /* RTC management */
41     time_t   time_offset;
42     time_t   stop_time;
43     /* Alarm & watchdog */
44     time_t   alarm;
45     struct QEMUTimer *alrm_timer;
46     struct QEMUTimer *wd_timer;
47     /* NVRAM storage */
48     uint16_t addr;
49     uint8_t *buffer;
50 };
51
52 /* Fake timer functions */
53 /* Generic helpers for BCD */
54 static inline uint8_t toBCD (uint8_t value)
55 {
56     return (((value / 10) % 10) << 4) | (value % 10);
57 }
58
59 static inline uint8_t fromBCD (uint8_t BCD)
60 {
61     return ((BCD >> 4) * 10) + (BCD & 0x0F);
62 }
63
64 /* RTC management helpers */
65 static void get_time (m48t59_t *NVRAM, struct tm *tm)
66 {
67     time_t t;
68
69     t = time(NULL) + NVRAM->time_offset;
70     localtime_r(&t, tm);
71 }
72
73 static void set_time (m48t59_t *NVRAM, struct tm *tm)
74 {
75     time_t now, new_time;
76     
77     new_time = mktime(tm);
78     now = time(NULL);
79     NVRAM->time_offset = new_time - now;
80 }
81
82 /* Alarm management */
83 static void alarm_cb (void *opaque)
84 {
85     struct tm tm, tm_now;
86     uint64_t next_time;
87     m48t59_t *NVRAM = opaque;
88
89     pic_set_irq(NVRAM->IRQ, 1);
90     if ((NVRAM->buffer[0x1FF5] & 0x80) == 0 && 
91         (NVRAM->buffer[0x1FF4] & 0x80) == 0 &&
92         (NVRAM->buffer[0x1FF3] & 0x80) == 0 &&
93         (NVRAM->buffer[0x1FF2] & 0x80) == 0) {
94         /* Repeat once a month */
95         get_time(NVRAM, &tm_now);
96         memcpy(&tm, &tm_now, sizeof(struct tm));
97         tm.tm_mon++;
98         if (tm.tm_mon == 13) {
99             tm.tm_mon = 1;
100             tm.tm_year++;
101         }
102         next_time = mktime(&tm);
103     } else if ((NVRAM->buffer[0x1FF5] & 0x80) != 0 &&
104                (NVRAM->buffer[0x1FF4] & 0x80) == 0 &&
105                (NVRAM->buffer[0x1FF3] & 0x80) == 0 &&
106                (NVRAM->buffer[0x1FF2] & 0x80) == 0) {
107         /* Repeat once a day */
108         next_time = 24 * 60 * 60 + mktime(&tm_now);
109     } else if ((NVRAM->buffer[0x1FF5] & 0x80) != 0 &&
110                (NVRAM->buffer[0x1FF4] & 0x80) != 0 &&
111                (NVRAM->buffer[0x1FF3] & 0x80) == 0 &&
112                (NVRAM->buffer[0x1FF2] & 0x80) == 0) {
113         /* Repeat once an hour */
114         next_time = 60 * 60 + mktime(&tm_now);
115     } else if ((NVRAM->buffer[0x1FF5] & 0x80) != 0 &&
116                (NVRAM->buffer[0x1FF4] & 0x80) != 0 &&
117                (NVRAM->buffer[0x1FF3] & 0x80) != 0 &&
118                (NVRAM->buffer[0x1FF2] & 0x80) == 0) {
119         /* Repeat once a minute */
120         next_time = 60 + mktime(&tm_now);
121     } else {
122         /* Repeat once a second */
123         next_time = 1 + mktime(&tm_now);
124     }
125     qemu_mod_timer(NVRAM->alrm_timer, next_time * 1000);
126     pic_set_irq(NVRAM->IRQ, 0);
127 }
128
129
130 static void get_alarm (m48t59_t *NVRAM, struct tm *tm)
131 {
132     localtime_r(&NVRAM->alarm, tm);
133 }
134
135 static void set_alarm (m48t59_t *NVRAM, struct tm *tm)
136 {
137     NVRAM->alarm = mktime(tm);
138     if (NVRAM->alrm_timer != NULL) {
139         qemu_del_timer(NVRAM->alrm_timer);
140         NVRAM->alrm_timer = NULL;
141     }
142     if (NVRAM->alarm - time(NULL) > 0)
143         qemu_mod_timer(NVRAM->alrm_timer, NVRAM->alarm * 1000);
144 }
145
146 /* Watchdog management */
147 static void watchdog_cb (void *opaque)
148 {
149     m48t59_t *NVRAM = opaque;
150
151     NVRAM->buffer[0x1FF0] |= 0x80;
152     if (NVRAM->buffer[0x1FF7] & 0x80) {
153         NVRAM->buffer[0x1FF7] = 0x00;
154         NVRAM->buffer[0x1FFC] &= ~0x40;
155         //      reset_CPU();
156     } else {
157         pic_set_irq(NVRAM->IRQ, 1);
158         pic_set_irq(NVRAM->IRQ, 0);
159     }
160 }
161
162 static void set_up_watchdog (m48t59_t *NVRAM, uint8_t value)
163 {
164     uint64_t interval; /* in 1/16 seconds */
165
166     if (NVRAM->wd_timer != NULL) {
167         qemu_del_timer(NVRAM->wd_timer);
168         NVRAM->wd_timer = NULL;
169     }
170     NVRAM->buffer[0x1FF0] &= ~0x80;
171     if (value != 0) {
172         interval = (1 << (2 * (value & 0x03))) * ((value >> 2) & 0x1F);
173         qemu_mod_timer(NVRAM->wd_timer, ((uint64_t)time(NULL) * 1000) +
174                        ((interval * 1000) >> 4));
175     }
176 }
177
178 /* Direct access to NVRAM */
179 void m48t59_write (m48t59_t *NVRAM, uint32_t val)
180 {
181     struct tm tm;
182     int tmp;
183
184     if (NVRAM->addr > 0x1FF8 && NVRAM->addr < 0x2000)
185         NVRAM_PRINTF("%s: 0x%08x => 0x%08x\n", __func__, NVRAM->addr, val);
186     switch (NVRAM->addr) {
187     case 0x1FF0:
188         /* flags register : read-only */
189         break;
190     case 0x1FF1:
191         /* unused */
192         break;
193     case 0x1FF2:
194         /* alarm seconds */
195         tmp = fromBCD(val & 0x7F);
196         if (tmp >= 0 && tmp <= 59) {
197             get_alarm(NVRAM, &tm);
198             tm.tm_sec = tmp;
199             NVRAM->buffer[0x1FF2] = val;
200             set_alarm(NVRAM, &tm);
201         }
202         break;
203     case 0x1FF3:
204         /* alarm minutes */
205         tmp = fromBCD(val & 0x7F);
206         if (tmp >= 0 && tmp <= 59) {
207             get_alarm(NVRAM, &tm);
208             tm.tm_min = tmp;
209             NVRAM->buffer[0x1FF3] = val;
210             set_alarm(NVRAM, &tm);
211         }
212         break;
213     case 0x1FF4:
214         /* alarm hours */
215         tmp = fromBCD(val & 0x3F);
216         if (tmp >= 0 && tmp <= 23) {
217             get_alarm(NVRAM, &tm);
218             tm.tm_hour = tmp;
219             NVRAM->buffer[0x1FF4] = val;
220             set_alarm(NVRAM, &tm);
221         }
222         break;
223     case 0x1FF5:
224         /* alarm date */
225         tmp = fromBCD(val & 0x1F);
226         if (tmp != 0) {
227             get_alarm(NVRAM, &tm);
228             tm.tm_mday = tmp;
229             NVRAM->buffer[0x1FF5] = val;
230             set_alarm(NVRAM, &tm);
231         }
232         break;
233     case 0x1FF6:
234         /* interrupts */
235         NVRAM->buffer[0x1FF6] = val;
236         break;
237     case 0x1FF7:
238         /* watchdog */
239         NVRAM->buffer[0x1FF7] = val;
240         set_up_watchdog(NVRAM, val);
241         break;
242     case 0x1FF8:
243         /* control */
244         NVRAM->buffer[0x1FF8] = (val & ~0xA0) | 0x90;
245         break;
246     case 0x1FF9:
247         /* seconds (BCD) */
248         tmp = fromBCD(val & 0x7F);
249         if (tmp >= 0 && tmp <= 59) {
250             get_time(NVRAM, &tm);
251             tm.tm_sec = tmp;
252             set_time(NVRAM, &tm);
253         }
254         if ((val & 0x80) ^ (NVRAM->buffer[0x1FF9] & 0x80)) {
255             if (val & 0x80) {
256                 NVRAM->stop_time = time(NULL);
257             } else {
258                 NVRAM->time_offset += NVRAM->stop_time - time(NULL);
259                 NVRAM->stop_time = 0;
260             }
261         }
262         NVRAM->buffer[0x1FF9] = val & 0x80;
263         break;
264     case 0x1FFA:
265         /* minutes (BCD) */
266         tmp = fromBCD(val & 0x7F);
267         if (tmp >= 0 && tmp <= 59) {
268             get_time(NVRAM, &tm);
269             tm.tm_min = tmp;
270             set_time(NVRAM, &tm);
271         }
272         break;
273     case 0x1FFB:
274         /* hours (BCD) */
275         tmp = fromBCD(val & 0x3F);
276         if (tmp >= 0 && tmp <= 23) {
277             get_time(NVRAM, &tm);
278             tm.tm_hour = tmp;
279             set_time(NVRAM, &tm);
280         }
281         break;
282     case 0x1FFC:
283         /* day of the week / century */
284         tmp = fromBCD(val & 0x07);
285         get_time(NVRAM, &tm);
286         tm.tm_wday = tmp;
287         set_time(NVRAM, &tm);
288         NVRAM->buffer[0x1FFC] = val & 0x40;
289         break;
290     case 0x1FFD:
291         /* date */
292         tmp = fromBCD(val & 0x1F);
293         if (tmp != 0) {
294             get_time(NVRAM, &tm);
295             tm.tm_mday = tmp;
296             set_time(NVRAM, &tm);
297         }
298         break;
299     case 0x1FFE:
300         /* month */
301         tmp = fromBCD(val & 0x1F);
302         if (tmp >= 1 && tmp <= 12) {
303             get_time(NVRAM, &tm);
304             tm.tm_mon = tmp - 1;
305             set_time(NVRAM, &tm);
306         }
307         break;
308     case 0x1FFF:
309         /* year */
310         tmp = fromBCD(val);
311         if (tmp >= 0 && tmp <= 99) {
312             get_time(NVRAM, &tm);
313             tm.tm_year = fromBCD(val);
314             set_time(NVRAM, &tm);
315         }
316         break;
317     default:
318         if (NVRAM->addr < 0x1FF0 ||
319             (NVRAM->addr > 0x1FFF && NVRAM->addr < NVRAM->size)) {
320             NVRAM->buffer[NVRAM->addr] = val & 0xFF;
321         }
322         break;
323     }
324 }
325
326 uint32_t m48t59_read (m48t59_t *NVRAM)
327 {
328     struct tm tm;
329     uint32_t retval = 0xFF;
330
331     switch (NVRAM->addr) {
332     case 0x1FF0:
333         /* flags register */
334         goto do_read;
335     case 0x1FF1:
336         /* unused */
337         retval = 0;
338         break;
339     case 0x1FF2:
340         /* alarm seconds */
341         goto do_read;
342     case 0x1FF3:
343         /* alarm minutes */
344         goto do_read;
345     case 0x1FF4:
346         /* alarm hours */
347         goto do_read;
348     case 0x1FF5:
349         /* alarm date */
350         goto do_read;
351     case 0x1FF6:
352         /* interrupts */
353         goto do_read;
354     case 0x1FF7:
355         /* A read resets the watchdog */
356         set_up_watchdog(NVRAM, NVRAM->buffer[0x1FF7]);
357         goto do_read;
358     case 0x1FF8:
359         /* control */
360         goto do_read;
361     case 0x1FF9:
362         /* seconds (BCD) */
363         get_time(NVRAM, &tm);
364         retval = (NVRAM->buffer[0x1FF9] & 0x80) | toBCD(tm.tm_sec);
365         break;
366     case 0x1FFA:
367         /* minutes (BCD) */
368         get_time(NVRAM, &tm);
369         retval = toBCD(tm.tm_min);
370         break;
371     case 0x1FFB:
372         /* hours (BCD) */
373         get_time(NVRAM, &tm);
374         retval = toBCD(tm.tm_hour);
375         break;
376     case 0x1FFC:
377         /* day of the week / century */
378         get_time(NVRAM, &tm);
379         retval = NVRAM->buffer[0x1FFC] | tm.tm_wday;
380         break;
381     case 0x1FFD:
382         /* date */
383         get_time(NVRAM, &tm);
384         retval = toBCD(tm.tm_mday);
385         break;
386     case 0x1FFE:
387         /* month */
388         get_time(NVRAM, &tm);
389         retval = toBCD(tm.tm_mon + 1);
390         break;
391     case 0x1FFF:
392         /* year */
393         get_time(NVRAM, &tm);
394         retval = toBCD(tm.tm_year);
395         break;
396     default:
397         if (NVRAM->addr < 0x1FF0 ||
398             (NVRAM->addr > 0x1FFF && NVRAM->addr < NVRAM->size)) {
399         do_read:
400             retval = NVRAM->buffer[NVRAM->addr];
401         }
402         break;
403     }
404     if (NVRAM->addr > 0x1FF9 && NVRAM->addr < 0x2000)
405         NVRAM_PRINTF("0x%08x <= 0x%08x\n", NVRAM->addr, retval);
406
407     return retval;
408 }
409
410 void m48t59_set_addr (m48t59_t *NVRAM, uint32_t addr)
411 {
412     NVRAM->addr = addr;
413 }
414
415 /* IO access to NVRAM */
416 static void NVRAM_writeb (void *opaque, uint32_t addr, uint32_t val)
417 {
418     m48t59_t *NVRAM = opaque;
419
420     addr -= NVRAM->io_base;
421     switch (addr) {
422     case 0:
423         NVRAM->addr &= ~0x00FF;
424         NVRAM->addr |= val;
425         break;
426     case 1:
427         NVRAM->addr &= ~0xFF00;
428         NVRAM->addr |= val << 8;
429         break;
430     case 3:
431         m48t59_write(NVRAM, val);
432         NVRAM->addr = 0x0000;
433         break;
434     default:
435         break;
436     }
437 }
438
439 static uint32_t NVRAM_readb (void *opaque, uint32_t addr)
440 {
441     m48t59_t *NVRAM = opaque;
442
443     if (addr == NVRAM->io_base + 3)
444         return m48t59_read(NVRAM);
445
446     return 0xFF;
447 }
448
449 /* Initialisation routine */
450 m48t59_t *m48t59_init (int IRQ, uint32_t io_base, uint16_t size)
451 {
452     m48t59_t *s;
453
454     s = qemu_mallocz(sizeof(m48t59_t));
455     if (!s)
456         return NULL;
457     s->buffer = qemu_mallocz(size);
458     if (!s->buffer) {
459         qemu_free(s);
460         return NULL;
461     }
462     s->IRQ = IRQ;
463     s->size = size;
464     s->io_base = io_base;
465     s->addr = 0;
466     register_ioport_read(io_base, 0x04, 1, NVRAM_readb, s);
467     register_ioport_write(io_base, 0x04, 1, NVRAM_writeb, s);
468     s->alrm_timer = qemu_new_timer(vm_clock, &alarm_cb, s);
469     s->wd_timer = qemu_new_timer(vm_clock, &watchdog_cb, s);
470     return s;
471 }