Statistics
| Branch: | Revision:

root / hw / m48t59.c @ c5df018e

History | View | Annotate | Download (11.5 kB)

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
}