25 |
* SUCH DAMAGE. |
* SUCH DAMAGE. |
26 |
* |
* |
27 |
* |
* |
28 |
* $Id: dev_mc146818.c,v 1.68 2005/02/07 05:51:54 debug Exp $ |
* $Id: dev_mc146818.c,v 1.75 2005/10/09 22:21:31 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. |
56 |
|
|
57 |
/* #define MC146818_DEBUG */ |
/* #define MC146818_DEBUG */ |
58 |
|
|
59 |
#define TICK_STEPS_SHIFT 14 |
#define TICK_SHIFT 14 |
60 |
|
|
61 |
|
|
62 |
/* 256 on DECstation, SGI uses reg at 72*4 as the Century */ |
/* 256 on DECstation, SGI uses reg at 72*4 as the Century */ |
140 |
|
|
141 |
if ((d->reg[MC_REGB * 4] & MC_REGB_PIE) && |
if ((d->reg[MC_REGB * 4] & MC_REGB_PIE) && |
142 |
d->interrupt_every_x_cycles > 0) { |
d->interrupt_every_x_cycles > 0) { |
143 |
d->cycles_left_until_interrupt -= |
d->cycles_left_until_interrupt -= (1 << TICK_SHIFT); |
|
(1 << TICK_STEPS_SHIFT); |
|
144 |
|
|
145 |
if (d->cycles_left_until_interrupt < 0 || |
if (d->cycles_left_until_interrupt < 0 || |
146 |
d->cycles_left_until_interrupt >= |
d->cycles_left_until_interrupt >= |
222 |
d->reg[4 * MC_MONTH] = tmp->tm_mon + 1; |
d->reg[4 * MC_MONTH] = tmp->tm_mon + 1; |
223 |
d->reg[4 * MC_YEAR] = tmp->tm_year; |
d->reg[4 * MC_YEAR] = tmp->tm_year; |
224 |
|
|
225 |
|
/* |
226 |
|
* Special hacks for emulating the behaviour of various machines: |
227 |
|
*/ |
228 |
switch (d->access_style) { |
switch (d->access_style) { |
229 |
case MC146818_ARC_NEC: |
case MC146818_ARC_NEC: |
230 |
d->reg[4 * MC_YEAR] += (0x18 - 104); |
d->reg[4 * MC_YEAR] += (0x18 - 104); |
231 |
break; |
break; |
232 |
|
case MC146818_CATS: |
233 |
|
d->reg[4 * MC_YEAR] %= 100; |
234 |
|
break; |
235 |
case MC146818_SGI: |
case MC146818_SGI: |
236 |
/* |
/* |
237 |
* NetBSD/sgimips assumes data in BCD format. |
* NetBSD/sgimips assumes data in BCD format. |
253 |
(d->reg[4 * MC_YEAR] - 30 + 40) |
(d->reg[4 * MC_YEAR] - 30 + 40) |
254 |
: (d->reg[4 * MC_YEAR] - 40) |
: (d->reg[4 * MC_YEAR] - 40) |
255 |
); |
); |
|
|
|
256 |
/* Century: */ |
/* Century: */ |
257 |
d->reg[72 * 4] = 19 + (tmp->tm_year / 100); |
d->reg[72 * 4] = 19 + (tmp->tm_year / 100); |
|
|
|
258 |
break; |
break; |
259 |
case MC146818_DEC: |
case MC146818_DEC: |
260 |
/* |
/* |
307 |
|
|
308 |
/* Different ways of accessing the registers: */ |
/* Different ways of accessing the registers: */ |
309 |
switch (d->access_style) { |
switch (d->access_style) { |
310 |
|
case MC146818_CATS: |
311 |
case MC146818_PC_CMOS: |
case MC146818_PC_CMOS: |
312 |
if (relative_addr == 0x70 || relative_addr == 0x00) { |
if ((relative_addr & 1) == 0x00) { |
313 |
if (writeflag == MEM_WRITE) { |
if (writeflag == MEM_WRITE) { |
314 |
d->last_addr = data[0]; |
d->last_addr = data[0]; |
315 |
return 1; |
return 1; |
317 |
data[0] = d->last_addr; |
data[0] = d->last_addr; |
318 |
return 1; |
return 1; |
319 |
} |
} |
320 |
} else if (relative_addr == 0x71 || relative_addr == 0x01) |
} else |
321 |
relative_addr = d->last_addr * 4; |
relative_addr = d->last_addr * 4; |
|
else { |
|
|
fatal("[ mc146818: not accessed as an " |
|
|
"MC146818_PC_CMOS device! ]\n"); |
|
|
} |
|
322 |
break; |
break; |
323 |
case MC146818_ARC_NEC: |
case MC146818_ARC_NEC: |
324 |
if (relative_addr == 0x01) { |
if (relative_addr == 0x01) { |
366 |
#endif |
#endif |
367 |
|
|
368 |
/* |
/* |
369 |
* 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 |
|
370 |
* it hangs forever during bootup. (These do not cause interrupts, |
* it hangs forever during bootup. (These do not cause interrupts, |
371 |
* but it is good enough... Sprite polls this, iirc.) |
* but it is good enough... Sprite polls this, iirc.) |
372 |
|
* |
373 |
|
* Linux on at least sgimips and evbmips (Malta) wants the UIP bit |
374 |
|
* in REGA to be updated once a second. |
375 |
*/ |
*/ |
376 |
timet = time(NULL); |
timet = time(NULL); |
377 |
tmp = gmtime(&timet); |
tmp = gmtime(&timet); |
378 |
d->reg[MC_REGC * 4] &= ~MC_REGC_UF; |
d->reg[MC_REGC * 4] &= ~MC_REGC_UF; |
379 |
|
|
380 |
if (tmp->tm_sec != d->previous_second) { |
if (tmp->tm_sec != d->previous_second) { |
381 |
|
d->reg[MC_REGA * 4] &= ~MC_REGA_UIP; |
382 |
|
|
383 |
d->reg[MC_REGC * 4] |= MC_REGC_UF; |
d->reg[MC_REGC * 4] |= MC_REGC_UF; |
384 |
d->reg[MC_REGC * 4] |= MC_REGC_IRQF; |
d->reg[MC_REGC * 4] |= MC_REGC_IRQF; |
385 |
d->previous_second = tmp->tm_sec; |
d->previous_second = tmp->tm_sec; |
388 |
the PF (periodic flag) bit set, even though interrupts |
the PF (periodic flag) bit set, even though interrupts |
389 |
are not enabled? */ |
are not enabled? */ |
390 |
d->reg[MC_REGC * 4] |= MC_REGC_PF; |
d->reg[MC_REGC * 4] |= MC_REGC_PF; |
391 |
} |
} else |
392 |
|
d->reg[MC_REGA * 4] |= MC_REGA_UIP; |
393 |
|
|
394 |
/* RTC data is in either BCD format or binary: */ |
/* RTC data is in either BCD format or binary: */ |
395 |
if (d->use_bcd) { |
if (d->use_bcd) |
396 |
d->reg[MC_REGB * 4] &= ~(1 << 2); |
d->reg[MC_REGB * 4] &= ~(1 << 2); |
397 |
} else { |
else |
398 |
d->reg[MC_REGB * 4] |= (1 << 2); |
d->reg[MC_REGB * 4] |= (1 << 2); |
|
} |
|
399 |
|
|
400 |
/* RTC date/time is always Valid: */ |
/* RTC date/time is always Valid: */ |
401 |
d->reg[MC_REGD * 4] |= MC_REGD_VRT; |
d->reg[MC_REGD * 4] |= MC_REGD_VRT; |
582 |
d->access_style = access_style; |
d->access_style = access_style; |
583 |
d->addrdiv = addrdiv; |
d->addrdiv = addrdiv; |
584 |
|
|
585 |
|
/* Only SGIs and PCs use BCD format (?) */ |
586 |
|
d->use_bcd = 0; |
587 |
|
if (access_style == MC146818_SGI || access_style == MC146818_PC_CMOS) |
588 |
|
d->use_bcd = 1; |
589 |
|
|
590 |
|
if (access_style == MC146818_DEC) { |
591 |
/* Station Ethernet Address, on DECstation 3100: */ |
/* Station Ethernet Address, on DECstation 3100: */ |
592 |
for (i=0; i<6; i++) |
for (i=0; i<6; i++) |
593 |
ether_address[i] = 0x10 * (i+1); |
ether_address[i] = 0x10 * (i+1); |
594 |
|
|
595 |
d->reg[0x01] = ether_address[0]; |
d->reg[0x01] = ether_address[0]; |
596 |
d->reg[0x05] = ether_address[1]; |
d->reg[0x05] = ether_address[1]; |
597 |
d->reg[0x09] = ether_address[2]; |
d->reg[0x09] = ether_address[2]; |
598 |
d->reg[0x0d] = ether_address[3]; |
d->reg[0x0d] = ether_address[3]; |
599 |
d->reg[0x11] = ether_address[4]; |
d->reg[0x11] = ether_address[4]; |
600 |
d->reg[0x15] = ether_address[5]; |
d->reg[0x15] = ether_address[5]; |
601 |
/* TODO: 19, 1d, 21, 25 = checksum bytes 1,2,2,1 resp. */ |
/* TODO: 19, 1d, 21, 25 = checksum bytes 1,2,2,1 resp. */ |
602 |
d->reg[0x29] = ether_address[5]; |
d->reg[0x29] = ether_address[5]; |
603 |
d->reg[0x2d] = ether_address[4]; |
d->reg[0x2d] = ether_address[4]; |
604 |
d->reg[0x31] = ether_address[3]; |
d->reg[0x31] = ether_address[3]; |
605 |
d->reg[0x35] = ether_address[2]; |
d->reg[0x35] = ether_address[2]; |
606 |
d->reg[0x39] = ether_address[1]; |
d->reg[0x39] = ether_address[1]; |
607 |
d->reg[0x3d] = ether_address[1]; |
d->reg[0x3d] = ether_address[1]; |
608 |
d->reg[0x41] = ether_address[0]; |
d->reg[0x41] = ether_address[0]; |
609 |
d->reg[0x45] = ether_address[1]; |
d->reg[0x45] = ether_address[1]; |
610 |
d->reg[0x49] = ether_address[2]; |
d->reg[0x49] = ether_address[2]; |
611 |
d->reg[0x4d] = ether_address[3]; |
d->reg[0x4d] = ether_address[3]; |
612 |
d->reg[0x51] = ether_address[4]; |
d->reg[0x51] = ether_address[4]; |
613 |
d->reg[0x55] = ether_address[5]; |
d->reg[0x55] = ether_address[5]; |
614 |
/* TODO: 59, 5d = checksum bytes 1,2 resp. */ |
/* TODO: 59, 5d = checksum bytes 1,2 resp. */ |
615 |
d->reg[0x61] = 0xff; |
d->reg[0x61] = 0xff; |
616 |
d->reg[0x65] = 0x00; |
d->reg[0x65] = 0x00; |
617 |
d->reg[0x69] = 0x55; |
d->reg[0x69] = 0x55; |
618 |
d->reg[0x6d] = 0xaa; |
d->reg[0x6d] = 0xaa; |
619 |
d->reg[0x71] = 0xff; |
d->reg[0x71] = 0xff; |
620 |
d->reg[0x75] = 0x00; |
d->reg[0x75] = 0x00; |
621 |
d->reg[0x79] = 0x55; |
d->reg[0x79] = 0x55; |
622 |
d->reg[0x7d] = 0xaa; |
d->reg[0x7d] = 0xaa; |
623 |
|
|
|
/* Only SGI uses BCD format (?) */ |
|
|
d->use_bcd = 0; |
|
|
if (access_style == MC146818_SGI) |
|
|
d->use_bcd = 1; |
|
|
|
|
|
if (access_style == MC146818_DEC) { |
|
624 |
/* Battery valid, for DECstations */ |
/* Battery valid, for DECstations */ |
625 |
d->reg[0xf8] = 1; |
d->reg[0xf8] = 1; |
626 |
} |
} |
631 |
|
|
632 |
dev_len = DEV_MC146818_LENGTH; |
dev_len = DEV_MC146818_LENGTH; |
633 |
switch (access_style) { |
switch (access_style) { |
634 |
|
case MC146818_CATS: |
635 |
case MC146818_PC_CMOS: |
case MC146818_PC_CMOS: |
636 |
dev_len = 2; |
dev_len = 2; |
637 |
break; |
break; |
645 |
|
|
646 |
mc146818_update_time(d); |
mc146818_update_time(d); |
647 |
|
|
648 |
machine_add_tickfunction(machine, dev_mc146818_tick, |
machine_add_tickfunction(machine, dev_mc146818_tick, d, TICK_SHIFT); |
|
d, TICK_STEPS_SHIFT); |
|
649 |
} |
} |
650 |
|
|