1 |
/* |
/* |
2 |
* Copyright (C) 2003-2005 Anders Gavare. All rights reserved. |
* Copyright (C) 2003-2006 Anders Gavare. All rights reserved. |
3 |
* |
* |
4 |
* Redistribution and use in source and binary forms, with or without |
* Redistribution and use in source and binary forms, with or without |
5 |
* modification, are permitted provided that the following conditions are met: |
* modification, are permitted provided that the following conditions are met: |
25 |
* SUCH DAMAGE. |
* SUCH DAMAGE. |
26 |
* |
* |
27 |
* |
* |
28 |
* $Id: dev_mc146818.c,v 1.69 2005/05/20 07:42:12 debug Exp $ |
* $Id: dev_mc146818.c,v 1.86 2006/06/22 13:22:41 debug Exp $ |
29 |
* |
* |
30 |
* MC146818 real-time clock, used by many different machines types. |
* MC146818 real-time clock, used by many different machines types. |
31 |
* (DS1687 as used in some SGI machines is similar to MC146818.) |
* (DS1687 as used in some other machines is also similar to the MC146818.) |
32 |
* |
* |
33 |
* This device contains Date/time, the machine's ethernet address (on |
* This device contains Date/time, the machine's ethernet address (on |
34 |
* DECstation 3100), and can cause periodic (hardware) interrupts. |
* DECstation 3100), and can cause periodic (hardware) interrupts. |
53 |
|
|
54 |
|
|
55 |
#define to_bcd(x) ( ((x)/10) * 16 + ((x)%10) ) |
#define to_bcd(x) ( ((x)/10) * 16 + ((x)%10) ) |
56 |
|
#define from_bcd(x) ( ((x)>>4) * 10 + ((x)&15) ) |
57 |
|
|
58 |
/* #define MC146818_DEBUG */ |
/* #define MC146818_DEBUG */ |
59 |
|
|
60 |
#define TICK_STEPS_SHIFT 14 |
#define TICK_SHIFT 14 |
61 |
|
|
62 |
|
|
63 |
/* 256 on DECstation, SGI uses reg at 72*4 as the Century */ |
/* 256 on DECstation, SGI uses reg at 72*4 as the Century */ |
77 |
int irq_nr; |
int irq_nr; |
78 |
|
|
79 |
int previous_second; |
int previous_second; |
80 |
|
int n_seconds_elapsed; |
81 |
|
int uip_threshold; |
82 |
|
|
83 |
int interrupt_every_x_cycles; |
int interrupt_every_x_cycles; |
84 |
int cycles_left_until_interrupt; |
int cycles_left_until_interrupt; |
85 |
|
|
86 |
|
int ugly_netbsd_prep_hack_done; |
87 |
|
int ugly_netbsd_prep_hack_sec; |
88 |
}; |
}; |
89 |
|
|
90 |
|
|
91 |
/* |
/* |
92 |
|
* Ugly hack to fool NetBSD/prep to accept the clock. (See mcclock_isa_match |
93 |
|
* in NetBSD's arch/prep/isa/mcclock_isa.c for details.) |
94 |
|
*/ |
95 |
|
#define NETBSD_HACK_INIT 0 |
96 |
|
#define NETBSD_HACK_FIRST_1 1 |
97 |
|
#define NETBSD_HACK_FIRST_2 2 |
98 |
|
#define NETBSD_HACK_SECOND_1 3 |
99 |
|
#define NETBSD_HACK_SECOND_2 4 |
100 |
|
#define NETBSD_HACK_DONE 5 |
101 |
|
|
102 |
|
|
103 |
|
/* |
104 |
* recalc_interrupt_cycle(): |
* recalc_interrupt_cycle(): |
105 |
* |
* |
106 |
* If automatic_clock_adjustment is turned on, then emulated_hz is modified |
* If automatic_clock_adjustment is turned on, then emulated_hz is modified |
137 |
#endif |
#endif |
138 |
|
|
139 |
if (d->interrupt_hz > 0) |
if (d->interrupt_hz > 0) |
140 |
d->interrupt_every_x_cycles = |
d->interrupt_every_x_cycles = emulated_hz / d->interrupt_hz; |
|
emulated_hz / d->interrupt_hz; |
|
141 |
else |
else |
142 |
d->interrupt_every_x_cycles = 0; |
d->interrupt_every_x_cycles = 0; |
143 |
} |
} |
150 |
{ |
{ |
151 |
struct mc_data *d = extra; |
struct mc_data *d = extra; |
152 |
|
|
|
if (d == NULL) |
|
|
return; |
|
|
|
|
153 |
recalc_interrupt_cycle(cpu, d); |
recalc_interrupt_cycle(cpu, d); |
154 |
|
|
155 |
if ((d->reg[MC_REGB * 4] & MC_REGB_PIE) && |
if ((d->reg[MC_REGB * 4] & MC_REGB_PIE) && |
156 |
d->interrupt_every_x_cycles > 0) { |
d->interrupt_every_x_cycles > 0) { |
157 |
d->cycles_left_until_interrupt -= |
d->cycles_left_until_interrupt -= (1 << TICK_SHIFT); |
|
(1 << TICK_STEPS_SHIFT); |
|
158 |
|
|
159 |
if (d->cycles_left_until_interrupt < 0 || |
if (d->cycles_left_until_interrupt < 0 || |
160 |
d->cycles_left_until_interrupt >= |
d->cycles_left_until_interrupt >= |
161 |
d->interrupt_every_x_cycles) { |
d->interrupt_every_x_cycles) { |
162 |
/* debug("[ rtc interrupt (every %i cycles) ]\n", |
/* fatal("[ rtc interrupt (every %i cycles) ]\n", |
163 |
d->interrupt_every_x_cycles); */ |
d->interrupt_every_x_cycles); */ |
164 |
cpu_interrupt(cpu, d->irq_nr); |
cpu_interrupt(cpu, d->irq_nr); |
165 |
|
|
236 |
d->reg[4 * MC_MONTH] = tmp->tm_mon + 1; |
d->reg[4 * MC_MONTH] = tmp->tm_mon + 1; |
237 |
d->reg[4 * MC_YEAR] = tmp->tm_year; |
d->reg[4 * MC_YEAR] = tmp->tm_year; |
238 |
|
|
239 |
|
/* |
240 |
|
* Special hacks for emulating the behaviour of various machines: |
241 |
|
*/ |
242 |
switch (d->access_style) { |
switch (d->access_style) { |
243 |
|
case MC146818_ALGOR: |
244 |
|
d->reg[4 * MC_YEAR] += 80; |
245 |
|
break; |
246 |
case MC146818_ARC_NEC: |
case MC146818_ARC_NEC: |
247 |
d->reg[4 * MC_YEAR] += (0x18 - 104); |
d->reg[4 * MC_YEAR] += (0x18 - 104); |
248 |
break; |
break; |
249 |
|
case MC146818_CATS: |
250 |
|
d->reg[4 * MC_YEAR] %= 100; |
251 |
|
break; |
252 |
case MC146818_SGI: |
case MC146818_SGI: |
253 |
/* |
/* |
254 |
* NetBSD/sgimips assumes data in BCD format. |
* NetBSD/sgimips assumes data in BCD format. |
270 |
(d->reg[4 * MC_YEAR] - 30 + 40) |
(d->reg[4 * MC_YEAR] - 30 + 40) |
271 |
: (d->reg[4 * MC_YEAR] - 40) |
: (d->reg[4 * MC_YEAR] - 40) |
272 |
); |
); |
|
|
|
273 |
/* Century: */ |
/* Century: */ |
274 |
d->reg[72 * 4] = 19 + (tmp->tm_year / 100); |
d->reg[72 * 4] = 19 + (tmp->tm_year / 100); |
|
|
|
275 |
break; |
break; |
276 |
case MC146818_DEC: |
case MC146818_DEC: |
277 |
/* |
/* |
318 |
struct tm *tmp; |
struct tm *tmp; |
319 |
time_t timet; |
time_t timet; |
320 |
struct mc_data *d = extra; |
struct mc_data *d = extra; |
321 |
int i, relative_addr = r; |
int relative_addr = r; |
322 |
|
size_t i; |
323 |
|
|
324 |
relative_addr /= d->addrdiv; |
relative_addr /= d->addrdiv; |
325 |
|
|
326 |
/* Different ways of accessing the registers: */ |
/* Different ways of accessing the registers: */ |
327 |
switch (d->access_style) { |
switch (d->access_style) { |
328 |
|
case MC146818_ALGOR: |
329 |
|
case MC146818_CATS: |
330 |
case MC146818_PC_CMOS: |
case MC146818_PC_CMOS: |
331 |
if (relative_addr == 0x70 || relative_addr == 0x00) { |
if ((relative_addr & 1) == 0x00) { |
332 |
if (writeflag == MEM_WRITE) { |
if (writeflag == MEM_WRITE) { |
333 |
d->last_addr = data[0]; |
d->last_addr = data[0]; |
334 |
return 1; |
return 1; |
336 |
data[0] = d->last_addr; |
data[0] = d->last_addr; |
337 |
return 1; |
return 1; |
338 |
} |
} |
339 |
} else if (relative_addr == 0x71 || relative_addr == 0x01) |
} else |
340 |
relative_addr = d->last_addr * 4; |
relative_addr = d->last_addr * 4; |
|
else { |
|
|
fatal("[ mc146818: not accessed as an " |
|
|
"MC146818_PC_CMOS device! ]\n"); |
|
|
} |
|
341 |
break; |
break; |
342 |
case MC146818_ARC_NEC: |
case MC146818_ARC_NEC: |
343 |
if (relative_addr == 0x01) { |
if (relative_addr == 0x01) { |
370 |
* should be ignored. It works _almost_ as DEC, if offsets are |
* should be ignored. It works _almost_ as DEC, if offsets are |
371 |
* divided by 0x40. |
* divided by 0x40. |
372 |
*/ |
*/ |
373 |
|
break; |
374 |
|
case MC146818_PMPPC: |
375 |
|
relative_addr *= 4; |
376 |
|
break; |
377 |
default: |
default: |
378 |
; |
; |
379 |
} |
} |
383 |
fatal("[ mc146818: write to addr=0x%04x (len %i): ", |
fatal("[ mc146818: write to addr=0x%04x (len %i): ", |
384 |
(int)relative_addr, (int)len); |
(int)relative_addr, (int)len); |
385 |
for (i=0; i<len; i++) |
for (i=0; i<len; i++) |
386 |
fatal("%02x ", data[i]); |
fatal("0x%02x ", data[i]); |
387 |
fatal("]\n"); |
fatal("]\n"); |
388 |
} |
} |
389 |
#endif |
#endif |
390 |
|
|
391 |
/* |
/* |
392 |
* For some reason, Linux/sgimips relies on the UIP bit to go |
* Sprite seems to wants UF interrupt status, once every second, or |
|
* on and off. Without this code, booting Linux takes forever: |
|
|
*/ |
|
|
d->reg[MC_REGA * 4] &= ~MC_REGA_UIP; |
|
|
#if 1 |
|
|
/* TODO: solve this more nicely */ |
|
|
if ((random() & 0xff) == 0) |
|
|
d->reg[MC_REGA * 4] ^= MC_REGA_UIP; |
|
|
#endif |
|
|
|
|
|
/* |
|
|
* Sprite seens to wants UF interrupt status, once every second, or |
|
393 |
* it hangs forever during bootup. (These do not cause interrupts, |
* it hangs forever during bootup. (These do not cause interrupts, |
394 |
* but it is good enough... Sprite polls this, iirc.) |
* but it is good enough... Sprite polls this, iirc.) |
395 |
|
* |
396 |
|
* Linux on at least sgimips and evbmips (Malta) wants the UIP bit |
397 |
|
* in REGA to be updated once a second. |
398 |
*/ |
*/ |
399 |
timet = time(NULL); |
if (relative_addr == MC_REGA*4 || relative_addr == MC_REGC*4) { |
400 |
tmp = gmtime(&timet); |
timet = time(NULL); |
401 |
d->reg[MC_REGC * 4] &= ~MC_REGC_UF; |
tmp = gmtime(&timet); |
402 |
if (tmp->tm_sec != d->previous_second) { |
d->reg[MC_REGC * 4] &= ~MC_REGC_UF; |
403 |
d->reg[MC_REGC * 4] |= MC_REGC_UF; |
if (tmp->tm_sec != d->previous_second) { |
404 |
d->reg[MC_REGC * 4] |= MC_REGC_IRQF; |
d->n_seconds_elapsed ++; |
405 |
d->previous_second = tmp->tm_sec; |
d->previous_second = tmp->tm_sec; |
406 |
|
} |
407 |
|
if (d->n_seconds_elapsed > d->uip_threshold) { |
408 |
|
d->n_seconds_elapsed = 0; |
409 |
|
|
410 |
|
d->reg[MC_REGA * 4] |= MC_REGA_UIP; |
411 |
|
|
412 |
/* For some reason, some Linux/DECstation KN04 kernels want |
d->reg[MC_REGC * 4] |= MC_REGC_UF; |
413 |
the PF (periodic flag) bit set, even though interrupts |
d->reg[MC_REGC * 4] |= MC_REGC_IRQF; |
414 |
are not enabled? */ |
|
415 |
d->reg[MC_REGC * 4] |= MC_REGC_PF; |
/* For some reason, some Linux/DECstation KN04 |
416 |
|
kernels want the PF (periodic flag) bit set, |
417 |
|
even though interrupts are not enabled? */ |
418 |
|
d->reg[MC_REGC * 4] |= MC_REGC_PF; |
419 |
|
} else |
420 |
|
d->reg[MC_REGA * 4] &= ~MC_REGA_UIP; |
421 |
} |
} |
422 |
|
|
423 |
/* RTC data is in either BCD format or binary: */ |
/* RTC data is in either BCD format or binary: */ |
424 |
if (d->use_bcd) { |
if (d->use_bcd) |
425 |
d->reg[MC_REGB * 4] &= ~(1 << 2); |
d->reg[MC_REGB * 4] &= ~(1 << 2); |
426 |
} else { |
else |
427 |
d->reg[MC_REGB * 4] |= (1 << 2); |
d->reg[MC_REGB * 4] |= (1 << 2); |
|
} |
|
428 |
|
|
429 |
/* RTC date/time is always Valid: */ |
/* RTC date/time is always Valid: */ |
430 |
d->reg[MC_REGD * 4] |= MC_REGD_VRT; |
d->reg[MC_REGD * 4] |= MC_REGD_VRT; |
468 |
case MC_RATE_8_Hz: d->interrupt_hz = 8; break; |
case MC_RATE_8_Hz: d->interrupt_hz = 8; break; |
469 |
case MC_RATE_4_Hz: d->interrupt_hz = 4; break; |
case MC_RATE_4_Hz: d->interrupt_hz = 4; break; |
470 |
case MC_RATE_2_Hz: d->interrupt_hz = 2; break; |
case MC_RATE_2_Hz: d->interrupt_hz = 2; break; |
471 |
default: |
default:/* debug("[ mc146818: unimplemented " |
|
/* debug("[ mc146818: unimplemented " |
|
472 |
"MC_REGA RS: %i ]\n", |
"MC_REGA RS: %i ]\n", |
473 |
data[0] & MC_REGA_RSMASK); */ |
data[0] & MC_REGA_RSMASK); */ |
474 |
; |
; |
506 |
case 0x128: |
case 0x128: |
507 |
d->reg[relative_addr] = data[0]; |
d->reg[relative_addr] = data[0]; |
508 |
if (data[0] & 8) { |
if (data[0] & 8) { |
509 |
|
int j; |
510 |
|
|
511 |
/* Used on SGI to power off the machine. */ |
/* Used on SGI to power off the machine. */ |
512 |
fatal("[ md146818: power off ]\n"); |
fatal("[ md146818: power off ]\n"); |
513 |
for (i=0; i<cpu->machine->ncpus; i++) |
for (j=0; j<cpu->machine->ncpus; j++) |
514 |
cpu->machine->cpus[i]->running = 0; |
cpu->machine->cpus[j]->running = 0; |
515 |
cpu->machine-> |
cpu->machine-> |
516 |
exit_without_entering_debugger = 1; |
exit_without_entering_debugger = 1; |
517 |
} |
} |
536 |
case 0x15: |
case 0x15: |
537 |
break; |
break; |
538 |
case 4 * MC_SEC: |
case 4 * MC_SEC: |
539 |
|
if (d->ugly_netbsd_prep_hack_done < NETBSD_HACK_DONE) { |
540 |
|
d->ugly_netbsd_prep_hack_done ++; |
541 |
|
switch (d->ugly_netbsd_prep_hack_done) { |
542 |
|
case NETBSD_HACK_FIRST_1: |
543 |
|
d->ugly_netbsd_prep_hack_sec = |
544 |
|
from_bcd(d->reg[relative_addr]); |
545 |
|
break; |
546 |
|
case NETBSD_HACK_FIRST_2: |
547 |
|
d->reg[relative_addr] = to_bcd( |
548 |
|
d->ugly_netbsd_prep_hack_sec); |
549 |
|
break; |
550 |
|
case NETBSD_HACK_SECOND_1: |
551 |
|
case NETBSD_HACK_SECOND_2: |
552 |
|
d->reg[relative_addr] = to_bcd((1 + |
553 |
|
d->ugly_netbsd_prep_hack_sec) % 60); |
554 |
|
break; |
555 |
|
} |
556 |
|
} |
557 |
case 4 * MC_MIN: |
case 4 * MC_MIN: |
558 |
case 4 * MC_HOUR: |
case 4 * MC_HOUR: |
559 |
case 4 * MC_DOW: |
case 4 * MC_DOW: |
570 |
if (d->reg[MC_REGB * 4] & MC_REGB_SET) |
if (d->reg[MC_REGB * 4] & MC_REGB_SET) |
571 |
break; |
break; |
572 |
|
|
573 |
mc146818_update_time(d); |
if (d->ugly_netbsd_prep_hack_done >= NETBSD_HACK_DONE) |
574 |
|
mc146818_update_time(d); |
575 |
|
break; |
576 |
|
case 4 * MC_REGA: |
577 |
break; |
break; |
578 |
case 4 * MC_REGC: /* Interrupt ack. */ |
case 4 * MC_REGC: /* Interrupt ack. */ |
579 |
/* NOTE: Acking is done below, _after_ the |
/* NOTE: Acking is done below, _after_ the |
580 |
register has been read. */ |
register has been read. */ |
581 |
break; |
break; |
582 |
default: |
default:debug("[ mc146818: read from relative_addr = " |
|
debug("[ mc146818: read from relative_addr = " |
|
583 |
"%04x ]\n", (int)relative_addr); |
"%04x ]\n", (int)relative_addr); |
|
; |
|
584 |
} |
} |
585 |
|
|
586 |
data[0] = d->reg[relative_addr]; |
data[0] = d->reg[relative_addr]; |
598 |
fatal("[ mc146818: read from addr=0x%04x (len %i): ", |
fatal("[ mc146818: read from addr=0x%04x (len %i): ", |
599 |
(int)relative_addr, (int)len); |
(int)relative_addr, (int)len); |
600 |
for (i=0; i<len; i++) |
for (i=0; i<len; i++) |
601 |
fatal("%02x ", data[i]); |
fatal("0x%02x ", data[i]); |
602 |
fatal("]\n"); |
fatal("]\n"); |
603 |
} |
} |
604 |
#endif |
#endif |
631 |
d->access_style = access_style; |
d->access_style = access_style; |
632 |
d->addrdiv = addrdiv; |
d->addrdiv = addrdiv; |
633 |
|
|
|
/* Only SGIs and PCs use BCD format (?) */ |
|
634 |
d->use_bcd = 0; |
d->use_bcd = 0; |
635 |
if (access_style == MC146818_SGI || access_style == MC146818_PC_CMOS) |
switch (access_style) { |
636 |
|
case MC146818_SGI: |
637 |
|
case MC146818_PC_CMOS: |
638 |
|
case MC146818_PMPPC: |
639 |
d->use_bcd = 1; |
d->use_bcd = 1; |
640 |
|
} |
641 |
|
|
642 |
|
if (machine->machine_type != MACHINE_PREP) { |
643 |
|
/* NetBSD/prep has a really ugly clock detection code; |
644 |
|
no other machines/OSes don't need this. */ |
645 |
|
d->ugly_netbsd_prep_hack_done = NETBSD_HACK_DONE; |
646 |
|
} |
647 |
|
|
648 |
if (access_style == MC146818_DEC) { |
if (access_style == MC146818_DEC) { |
649 |
/* Station Ethernet Address, on DECstation 3100: */ |
/* Station Ethernet Address, on DECstation 3100: */ |
683 |
d->reg[0xf8] = 1; |
d->reg[0xf8] = 1; |
684 |
} |
} |
685 |
|
|
686 |
|
/* |
687 |
|
* uip_threshold should ideally be 1, but when Linux polls the UIP bit |
688 |
|
* it looses speed. This hack gives Linux the impression that the cpu |
689 |
|
* is uip_threshold times faster than the slow clock it would |
690 |
|
* otherwise detect. |
691 |
|
* |
692 |
|
* TODO: Find out if this messes up Sprite emulation; if so, then |
693 |
|
* this hack has to be removed. |
694 |
|
*/ |
695 |
|
d->uip_threshold = 8; |
696 |
|
|
697 |
if (access_style == MC146818_ARC_JAZZ) |
if (access_style == MC146818_ARC_JAZZ) |
698 |
memory_device_register(mem, "mc146818_jazz", 0x90000070ULL, |
memory_device_register(mem, "mc146818_jazz", 0x90000070ULL, |
699 |
1, dev_mc146818_jazz_access, d, MEM_DEFAULT, NULL); |
1, dev_mc146818_jazz_access, d, DM_DEFAULT, NULL); |
700 |
|
|
701 |
dev_len = DEV_MC146818_LENGTH; |
dev_len = DEV_MC146818_LENGTH; |
702 |
switch (access_style) { |
switch (access_style) { |
703 |
|
case MC146818_CATS: |
704 |
case MC146818_PC_CMOS: |
case MC146818_PC_CMOS: |
705 |
dev_len = 2; |
dev_len = 2; |
706 |
break; |
break; |
710 |
|
|
711 |
memory_device_register(mem, "mc146818", baseaddr, |
memory_device_register(mem, "mc146818", baseaddr, |
712 |
dev_len * addrdiv, dev_mc146818_access, |
dev_len * addrdiv, dev_mc146818_access, |
713 |
d, MEM_DEFAULT, NULL); |
d, DM_DEFAULT, NULL); |
714 |
|
|
715 |
mc146818_update_time(d); |
mc146818_update_time(d); |
716 |
|
|
717 |
machine_add_tickfunction(machine, dev_mc146818_tick, |
machine_add_tickfunction(machine, dev_mc146818_tick, d, |
718 |
d, TICK_STEPS_SHIFT); |
TICK_SHIFT, 0.0); |
719 |
} |
} |
720 |
|
|