0.8.0-alt1
[qemu] / qemu / hw / m48t59.c
1 /*
2  * QEMU M48T59 and M48T08 NVRAM emulation for PPC PREP and Sparc platforms
3  * 
4  * Copyright (c) 2003-2005 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 DEBUG_NVRAM
28
29 #if defined(DEBUG_NVRAM)
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 /*
36  * The M48T08 and M48T59 chips are very similar. The newer '59 has
37  * alarm and a watchdog timer and related control registers. In the
38  * PPC platform there is also a nvram lock function.
39  */
40 struct m48t59_t {
41     /* Model parameters */
42     int type; // 8 = m48t08, 59 = m48t59
43     /* Hardware parameters */
44     int      IRQ;
45     int mem_index;
46     uint32_t mem_base;
47     uint32_t io_base;
48     uint16_t size;
49     /* RTC management */
50     time_t   time_offset;
51     time_t   stop_time;
52     /* Alarm & watchdog */
53     time_t   alarm;
54     struct QEMUTimer *alrm_timer;
55     struct QEMUTimer *wd_timer;
56     /* NVRAM storage */
57     uint8_t  lock;
58     uint16_t addr;
59     uint8_t *buffer;
60 };
61
62 /* Fake timer functions */
63 /* Generic helpers for BCD */
64 static inline uint8_t toBCD (uint8_t value)
65 {
66     return (((value / 10) % 10) << 4) | (value % 10);
67 }
68
69 static inline uint8_t fromBCD (uint8_t BCD)
70 {
71     return ((BCD >> 4) * 10) + (BCD & 0x0F);
72 }
73
74 /* RTC management helpers */
75 static void get_time (m48t59_t *NVRAM, struct tm *tm)
76 {
77     time_t t;
78
79     t = time(NULL) + NVRAM->time_offset;
80 #ifdef _WIN32
81     memcpy(tm,localtime(&t),sizeof(*tm));
82 #else
83     localtime_r (&t, tm) ;
84 #endif
85 }
86
87 static void set_time (m48t59_t *NVRAM, struct tm *tm)
88 {
89     time_t now, new_time;
90     
91     new_time = mktime(tm);
92     now = time(NULL);
93     NVRAM->time_offset = new_time - now;
94 }
95
96 /* Alarm management */
97 static void alarm_cb (void *opaque)
98 {
99     struct tm tm, tm_now;
100     uint64_t next_time;
101     m48t59_t *NVRAM = opaque;
102
103     pic_set_irq(NVRAM->IRQ, 1);
104     if ((NVRAM->buffer[0x1FF5] & 0x80) == 0 && 
105         (NVRAM->buffer[0x1FF4] & 0x80) == 0 &&
106         (NVRAM->buffer[0x1FF3] & 0x80) == 0 &&
107         (NVRAM->buffer[0x1FF2] & 0x80) == 0) {
108         /* Repeat once a month */
109         get_time(NVRAM, &tm_now);
110         memcpy(&tm, &tm_now, sizeof(struct tm));
111         tm.tm_mon++;
112         if (tm.tm_mon == 13) {
113             tm.tm_mon = 1;
114             tm.tm_year++;
115         }
116         next_time = mktime(&tm);
117     } else if ((NVRAM->buffer[0x1FF5] & 0x80) != 0 &&
118                (NVRAM->buffer[0x1FF4] & 0x80) == 0 &&
119                (NVRAM->buffer[0x1FF3] & 0x80) == 0 &&
120                (NVRAM->buffer[0x1FF2] & 0x80) == 0) {
121         /* Repeat once a day */
122         next_time = 24 * 60 * 60 + mktime(&tm_now);
123     } else if ((NVRAM->buffer[0x1FF5] & 0x80) != 0 &&
124                (NVRAM->buffer[0x1FF4] & 0x80) != 0 &&
125                (NVRAM->buffer[0x1FF3] & 0x80) == 0 &&
126                (NVRAM->buffer[0x1FF2] & 0x80) == 0) {
127         /* Repeat once an hour */
128         next_time = 60 * 60 + mktime(&tm_now);
129     } else if ((NVRAM->buffer[0x1FF5] & 0x80) != 0 &&
130                (NVRAM->buffer[0x1FF4] & 0x80) != 0 &&
131                (NVRAM->buffer[0x1FF3] & 0x80) != 0 &&
132                (NVRAM->buffer[0x1FF2] & 0x80) == 0) {
133         /* Repeat once a minute */
134         next_time = 60 + mktime(&tm_now);
135     } else {
136         /* Repeat once a second */
137         next_time = 1 + mktime(&tm_now);
138     }
139     qemu_mod_timer(NVRAM->alrm_timer, next_time * 1000);
140     pic_set_irq(NVRAM->IRQ, 0);
141 }
142
143
144 static void get_alarm (m48t59_t *NVRAM, struct tm *tm)
145 {
146 #ifdef _WIN32
147     memcpy(tm,localtime(&NVRAM->alarm),sizeof(*tm));
148 #else
149     localtime_r (&NVRAM->alarm, tm);
150 #endif
151 }
152
153 static void set_alarm (m48t59_t *NVRAM, struct tm *tm)
154 {
155     NVRAM->alarm = mktime(tm);
156     if (NVRAM->alrm_timer != NULL) {
157         qemu_del_timer(NVRAM->alrm_timer);
158         NVRAM->alrm_timer = NULL;
159     }
160     if (NVRAM->alarm - time(NULL) > 0)
161         qemu_mod_timer(NVRAM->alrm_timer, NVRAM->alarm * 1000);
162 }
163
164 /* Watchdog management */
165 static void watchdog_cb (void *opaque)
166 {
167     m48t59_t *NVRAM = opaque;
168
169     NVRAM->buffer[0x1FF0] |= 0x80;
170     if (NVRAM->buffer[0x1FF7] & 0x80) {
171         NVRAM->buffer[0x1FF7] = 0x00;
172         NVRAM->buffer[0x1FFC] &= ~0x40;
173         /* May it be a hw CPU Reset instead ? */
174         qemu_system_reset_request();
175     } else {
176         pic_set_irq(NVRAM->IRQ, 1);
177         pic_set_irq(NVRAM->IRQ, 0);
178     }
179 }
180
181 static void set_up_watchdog (m48t59_t *NVRAM, uint8_t value)
182 {
183     uint64_t interval; /* in 1/16 seconds */
184
185     if (NVRAM->wd_timer != NULL) {
186         qemu_del_timer(NVRAM->wd_timer);
187         NVRAM->wd_timer = NULL;
188     }
189     NVRAM->buffer[0x1FF0] &= ~0x80;
190     if (value != 0) {
191         interval = (1 << (2 * (value & 0x03))) * ((value >> 2) & 0x1F);
192         qemu_mod_timer(NVRAM->wd_timer, ((uint64_t)time(NULL) * 1000) +
193                        ((interval * 1000) >> 4));
194     }
195 }
196
197 /* Direct access to NVRAM */
198 void m48t59_write (m48t59_t *NVRAM, uint32_t addr, uint32_t val)
199 {
200     struct tm tm;
201     int tmp;
202
203     if (addr > 0x1FF8 && addr < 0x2000)
204         NVRAM_PRINTF("%s: 0x%08x => 0x%08x\n", __func__, addr, val);
205     if (NVRAM->type == 8 && 
206         (addr >= 0x1ff0 && addr <= 0x1ff7))
207         goto do_write;
208     switch (addr) {
209     case 0x1FF0:
210         /* flags register : read-only */
211         break;
212     case 0x1FF1:
213         /* unused */
214         break;
215     case 0x1FF2:
216         /* alarm seconds */
217         tmp = fromBCD(val & 0x7F);
218         if (tmp >= 0 && tmp <= 59) {
219             get_alarm(NVRAM, &tm);
220             tm.tm_sec = tmp;
221             NVRAM->buffer[0x1FF2] = val;
222             set_alarm(NVRAM, &tm);
223         }
224         break;
225     case 0x1FF3:
226         /* alarm minutes */
227         tmp = fromBCD(val & 0x7F);
228         if (tmp >= 0 && tmp <= 59) {
229             get_alarm(NVRAM, &tm);
230             tm.tm_min = tmp;
231             NVRAM->buffer[0x1FF3] = val;
232             set_alarm(NVRAM, &tm);
233         }
234         break;
235     case 0x1FF4:
236         /* alarm hours */
237         tmp = fromBCD(val & 0x3F);
238         if (tmp >= 0 && tmp <= 23) {
239             get_alarm(NVRAM, &tm);
240             tm.tm_hour = tmp;
241             NVRAM->buffer[0x1FF4] = val;
242             set_alarm(NVRAM, &tm);
243         }
244         break;
245     case 0x1FF5:
246         /* alarm date */
247         tmp = fromBCD(val & 0x1F);
248         if (tmp != 0) {
249             get_alarm(NVRAM, &tm);
250             tm.tm_mday = tmp;
251             NVRAM->buffer[0x1FF5] = val;
252             set_alarm(NVRAM, &tm);
253         }
254         break;
255     case 0x1FF6:
256         /* interrupts */
257         NVRAM->buffer[0x1FF6] = val;
258         break;
259     case 0x1FF7:
260         /* watchdog */
261         NVRAM->buffer[0x1FF7] = val;
262         set_up_watchdog(NVRAM, val);
263         break;
264     case 0x1FF8:
265         /* control */
266         NVRAM->buffer[0x1FF8] = (val & ~0xA0) | 0x90;
267         break;
268     case 0x1FF9:
269         /* seconds (BCD) */
270         tmp = fromBCD(val & 0x7F);
271         if (tmp >= 0 && tmp <= 59) {
272             get_time(NVRAM, &tm);
273             tm.tm_sec = tmp;
274             set_time(NVRAM, &tm);
275         }
276         if ((val & 0x80) ^ (NVRAM->buffer[0x1FF9] & 0x80)) {
277             if (val & 0x80) {
278                 NVRAM->stop_time = time(NULL);
279             } else {
280                 NVRAM->time_offset += NVRAM->stop_time - time(NULL);
281                 NVRAM->stop_time = 0;
282             }
283         }
284         NVRAM->buffer[0x1FF9] = val & 0x80;
285         break;
286     case 0x1FFA:
287         /* minutes (BCD) */
288         tmp = fromBCD(val & 0x7F);
289         if (tmp >= 0 && tmp <= 59) {
290             get_time(NVRAM, &tm);
291             tm.tm_min = tmp;
292             set_time(NVRAM, &tm);
293         }
294         break;
295     case 0x1FFB:
296         /* hours (BCD) */
297         tmp = fromBCD(val & 0x3F);
298         if (tmp >= 0 && tmp <= 23) {
299             get_time(NVRAM, &tm);
300             tm.tm_hour = tmp;
301             set_time(NVRAM, &tm);
302         }
303         break;
304     case 0x1FFC:
305         /* day of the week / century */
306         tmp = fromBCD(val & 0x07);
307         get_time(NVRAM, &tm);
308         tm.tm_wday = tmp;
309         set_time(NVRAM, &tm);
310         NVRAM->buffer[0x1FFC] = val & 0x40;
311         break;
312     case 0x1FFD:
313         /* date */
314         tmp = fromBCD(val & 0x1F);
315         if (tmp != 0) {
316             get_time(NVRAM, &tm);
317             tm.tm_mday = tmp;
318             set_time(NVRAM, &tm);
319         }
320         break;
321     case 0x1FFE:
322         /* month */
323         tmp = fromBCD(val & 0x1F);
324         if (tmp >= 1 && tmp <= 12) {
325             get_time(NVRAM, &tm);
326             tm.tm_mon = tmp - 1;
327             set_time(NVRAM, &tm);
328         }
329         break;
330     case 0x1FFF:
331         /* year */
332         tmp = fromBCD(val);
333         if (tmp >= 0 && tmp <= 99) {
334             get_time(NVRAM, &tm);
335             tm.tm_year = fromBCD(val);
336             set_time(NVRAM, &tm);
337         }
338         break;
339     default:
340         /* Check lock registers state */
341         if (addr >= 0x20 && addr <= 0x2F && (NVRAM->lock & 1))
342             break;
343         if (addr >= 0x30 && addr <= 0x3F && (NVRAM->lock & 2))
344             break;
345     do_write:
346         if (addr < NVRAM->size) {
347             NVRAM->buffer[addr] = val & 0xFF;
348         }
349         break;
350     }
351 }
352
353 uint32_t m48t59_read (m48t59_t *NVRAM, uint32_t addr)
354 {
355     struct tm tm;
356     uint32_t retval = 0xFF;
357
358     if (NVRAM->type == 8 && 
359         (addr >= 0x1ff0 && addr <= 0x1ff7))
360         goto do_read;
361     switch (addr) {
362     case 0x1FF0:
363         /* flags register */
364         goto do_read;
365     case 0x1FF1:
366         /* unused */
367         retval = 0;
368         break;
369     case 0x1FF2:
370         /* alarm seconds */
371         goto do_read;
372     case 0x1FF3:
373         /* alarm minutes */
374         goto do_read;
375     case 0x1FF4:
376         /* alarm hours */
377         goto do_read;
378     case 0x1FF5:
379         /* alarm date */
380         goto do_read;
381     case 0x1FF6:
382         /* interrupts */
383         goto do_read;
384     case 0x1FF7:
385         /* A read resets the watchdog */
386         set_up_watchdog(NVRAM, NVRAM->buffer[0x1FF7]);
387         goto do_read;
388     case 0x1FF8:
389         /* control */
390         goto do_read;
391     case 0x1FF9:
392         /* seconds (BCD) */
393         get_time(NVRAM, &tm);
394         retval = (NVRAM->buffer[0x1FF9] & 0x80) | toBCD(tm.tm_sec);
395         break;
396     case 0x1FFA:
397         /* minutes (BCD) */
398         get_time(NVRAM, &tm);
399         retval = toBCD(tm.tm_min);
400         break;
401     case 0x1FFB:
402         /* hours (BCD) */
403         get_time(NVRAM, &tm);
404         retval = toBCD(tm.tm_hour);
405         break;
406     case 0x1FFC:
407         /* day of the week / century */
408         get_time(NVRAM, &tm);
409         retval = NVRAM->buffer[0x1FFC] | tm.tm_wday;
410         break;
411     case 0x1FFD:
412         /* date */
413         get_time(NVRAM, &tm);
414         retval = toBCD(tm.tm_mday);
415         break;
416     case 0x1FFE:
417         /* month */
418         get_time(NVRAM, &tm);
419         retval = toBCD(tm.tm_mon + 1);
420         break;
421     case 0x1FFF:
422         /* year */
423         get_time(NVRAM, &tm);
424         retval = toBCD(tm.tm_year);
425         break;
426     default:
427         /* Check lock registers state */
428         if (addr >= 0x20 && addr <= 0x2F && (NVRAM->lock & 1))
429             break;
430         if (addr >= 0x30 && addr <= 0x3F && (NVRAM->lock & 2))
431             break;
432     do_read:
433         if (addr < NVRAM->size) {
434             retval = NVRAM->buffer[addr];
435         }
436         break;
437     }
438     if (addr > 0x1FF9 && addr < 0x2000)
439         NVRAM_PRINTF("0x%08x <= 0x%08x\n", addr, retval);
440
441     return retval;
442 }
443
444 void m48t59_set_addr (m48t59_t *NVRAM, uint32_t addr)
445 {
446     NVRAM->addr = addr;
447 }
448
449 void m48t59_toggle_lock (m48t59_t *NVRAM, int lock)
450 {
451     NVRAM->lock ^= 1 << lock;
452 }
453
454 /* IO access to NVRAM */
455 static void NVRAM_writeb (void *opaque, uint32_t addr, uint32_t val)
456 {
457     m48t59_t *NVRAM = opaque;
458
459     addr -= NVRAM->io_base;
460     NVRAM_PRINTF("0x%08x => 0x%08x\n", addr, val);
461     switch (addr) {
462     case 0:
463         NVRAM->addr &= ~0x00FF;
464         NVRAM->addr |= val;
465         break;
466     case 1:
467         NVRAM->addr &= ~0xFF00;
468         NVRAM->addr |= val << 8;
469         break;
470     case 3:
471         m48t59_write(NVRAM, val, NVRAM->addr);
472         NVRAM->addr = 0x0000;
473         break;
474     default:
475         break;
476     }
477 }
478
479 static uint32_t NVRAM_readb (void *opaque, uint32_t addr)
480 {
481     m48t59_t *NVRAM = opaque;
482     uint32_t retval;
483
484     addr -= NVRAM->io_base;
485     switch (addr) {
486     case 3:
487         retval = m48t59_read(NVRAM, NVRAM->addr);
488         break;
489     default:
490         retval = -1;
491         break;
492     }
493     NVRAM_PRINTF("0x%08x <= 0x%08x\n", addr, retval);
494
495     return retval;
496 }
497
498 static void nvram_writeb (void *opaque, target_phys_addr_t addr, uint32_t value)
499 {
500     m48t59_t *NVRAM = opaque;
501     
502     addr -= NVRAM->mem_base;
503     m48t59_write(NVRAM, addr, value & 0xff);
504 }
505
506 static void nvram_writew (void *opaque, target_phys_addr_t addr, uint32_t value)
507 {
508     m48t59_t *NVRAM = opaque;
509     
510     addr -= NVRAM->mem_base;
511     m48t59_write(NVRAM, addr, (value >> 8) & 0xff);
512     m48t59_write(NVRAM, addr + 1, value & 0xff);
513 }
514
515 static void nvram_writel (void *opaque, target_phys_addr_t addr, uint32_t value)
516 {
517     m48t59_t *NVRAM = opaque;
518     
519     addr -= NVRAM->mem_base;
520     m48t59_write(NVRAM, addr, (value >> 24) & 0xff);
521     m48t59_write(NVRAM, addr + 1, (value >> 16) & 0xff);
522     m48t59_write(NVRAM, addr + 2, (value >> 8) & 0xff);
523     m48t59_write(NVRAM, addr + 3, value & 0xff);
524 }
525
526 static uint32_t nvram_readb (void *opaque, target_phys_addr_t addr)
527 {
528     m48t59_t *NVRAM = opaque;
529     uint32_t retval;
530     
531     addr -= NVRAM->mem_base;
532     retval = m48t59_read(NVRAM, addr);
533     return retval;
534 }
535
536 static uint32_t nvram_readw (void *opaque, target_phys_addr_t addr)
537 {
538     m48t59_t *NVRAM = opaque;
539     uint32_t retval;
540     
541     addr -= NVRAM->mem_base;
542     retval = m48t59_read(NVRAM, addr) << 8;
543     retval |= m48t59_read(NVRAM, addr + 1);
544     return retval;
545 }
546
547 static uint32_t nvram_readl (void *opaque, target_phys_addr_t addr)
548 {
549     m48t59_t *NVRAM = opaque;
550     uint32_t retval;
551
552     addr -= NVRAM->mem_base;
553     retval = m48t59_read(NVRAM, addr) << 24;
554     retval |= m48t59_read(NVRAM, addr + 1) << 16;
555     retval |= m48t59_read(NVRAM, addr + 2) << 8;
556     retval |= m48t59_read(NVRAM, addr + 3);
557     return retval;
558 }
559
560 static CPUWriteMemoryFunc *nvram_write[] = {
561     &nvram_writeb,
562     &nvram_writew,
563     &nvram_writel,
564 };
565
566 static CPUReadMemoryFunc *nvram_read[] = {
567     &nvram_readb,
568     &nvram_readw,
569     &nvram_readl,
570 };
571
572 /* Initialisation routine */
573 m48t59_t *m48t59_init (int IRQ, target_ulong mem_base,
574                        uint32_t io_base, uint16_t size,
575                        int type)
576 {
577     m48t59_t *s;
578
579     s = qemu_mallocz(sizeof(m48t59_t));
580     if (!s)
581         return NULL;
582     s->buffer = qemu_mallocz(size);
583     if (!s->buffer) {
584         qemu_free(s);
585         return NULL;
586     }
587     s->IRQ = IRQ;
588     s->size = size;
589     s->mem_base = mem_base;
590     s->io_base = io_base;
591     s->addr = 0;
592     s->type = type;
593     if (io_base != 0) {
594         register_ioport_read(io_base, 0x04, 1, NVRAM_readb, s);
595         register_ioport_write(io_base, 0x04, 1, NVRAM_writeb, s);
596     }
597     if (mem_base != 0) {
598         s->mem_index = cpu_register_io_memory(0, nvram_read, nvram_write, s);
599         cpu_register_physical_memory(mem_base, 0x4000, s->mem_index);
600     }
601     if (type == 59) {
602         s->alrm_timer = qemu_new_timer(vm_clock, &alarm_cb, s);
603         s->wd_timer = qemu_new_timer(vm_clock, &watchdog_cb, s);
604     }
605     s->lock = 0;
606
607     return s;
608 }