1 |
/* |
/* |
2 |
* Copyright (C) 2003-2006 Anders Gavare. All rights reserved. |
* Copyright (C) 2003-2007 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.86 2006/06/22 13:22:41 debug Exp $ |
* $Id: dev_mc146818.c,v 1.95 2007/02/03 16:55:55 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 other machines is also similar to the MC146818.) |
* (DS1687 as used in some other machines is also similar to the MC146818.) |
48 |
#include "machine.h" |
#include "machine.h" |
49 |
#include "memory.h" |
#include "memory.h" |
50 |
#include "misc.h" |
#include "misc.h" |
51 |
|
#include "timer.h" |
52 |
|
|
53 |
#include "mc146818reg.h" |
#include "mc146818reg.h" |
54 |
|
|
64 |
/* 256 on DECstation, SGI uses reg at 72*4 as the Century */ |
/* 256 on DECstation, SGI uses reg at 72*4 as the Century */ |
65 |
#define N_REGISTERS 1024 |
#define N_REGISTERS 1024 |
66 |
struct mc_data { |
struct mc_data { |
67 |
int access_style; |
int access_style; |
68 |
int last_addr; |
int last_addr; |
69 |
|
|
70 |
int register_choice; |
int register_choice; |
71 |
int reg[N_REGISTERS]; |
int reg[N_REGISTERS]; |
72 |
int addrdiv; |
int addrdiv; |
73 |
|
|
74 |
|
int use_bcd; |
75 |
|
|
76 |
|
int timebase_hz; |
77 |
|
int interrupt_hz; |
78 |
|
int old_interrupt_hz; |
79 |
|
struct interrupt irq; |
80 |
|
struct timer *timer; |
81 |
|
volatile int pending_timer_interrupts; |
82 |
|
|
83 |
|
int previous_second; |
84 |
|
int n_seconds_elapsed; |
85 |
|
int uip_threshold; |
86 |
|
|
87 |
int use_bcd; |
int ugly_netbsd_prep_hack_done; |
88 |
|
int ugly_netbsd_prep_hack_sec; |
|
int timebase_hz; |
|
|
int interrupt_hz; |
|
|
int irq_nr; |
|
|
|
|
|
int previous_second; |
|
|
int n_seconds_elapsed; |
|
|
int uip_threshold; |
|
|
|
|
|
int interrupt_every_x_cycles; |
|
|
int cycles_left_until_interrupt; |
|
|
|
|
|
int ugly_netbsd_prep_hack_done; |
|
|
int ugly_netbsd_prep_hack_sec; |
|
89 |
}; |
}; |
90 |
|
|
91 |
|
|
102 |
|
|
103 |
|
|
104 |
/* |
/* |
105 |
* recalc_interrupt_cycle(): |
* timer_tick(): |
106 |
* |
* |
107 |
* If automatic_clock_adjustment is turned on, then emulated_hz is modified |
* Called d->interrupt_hz times per (real-world) second. |
|
* dynamically. We have to recalculate how often interrupts are to be |
|
|
* triggered. |
|
108 |
*/ |
*/ |
109 |
static void recalc_interrupt_cycle(struct cpu *cpu, struct mc_data *d) |
static void timer_tick(struct timer *timer, void *extra) |
110 |
{ |
{ |
111 |
int64_t emulated_hz = cpu->machine->emulated_hz; |
struct mc_data *d = (struct mc_data *) extra; |
112 |
#if 0 |
d->pending_timer_interrupts ++; |
|
static int warning_printed = 0; |
|
|
|
|
|
/* |
|
|
* A hack to make Ultrix run, even on very fast host machines. |
|
|
* |
|
|
* (Ultrix was probably never meant to be run on machines with |
|
|
* faster CPUs than around 33 MHz or so.) |
|
|
*/ |
|
|
if (d->access_style == MC146818_DEC && emulated_hz > 30000000) { |
|
|
if (!warning_printed) { |
|
|
fatal("\n*********************************************" |
|
|
"**********************************\n\n Your hos" |
|
|
"t machine is too fast! The emulated CPU speed wil" |
|
|
"l be limited to\n 30 MHz, and clocks inside the" |
|
|
" emulated environment might go faster than\n in" |
|
|
" the real world. You have been warned.\n\n******" |
|
|
"*************************************************" |
|
|
"************************\n\n"); |
|
|
warning_printed = 1; |
|
|
} |
|
|
|
|
|
emulated_hz = 30000000; |
|
|
} |
|
|
#endif |
|
|
|
|
|
if (d->interrupt_hz > 0) |
|
|
d->interrupt_every_x_cycles = emulated_hz / d->interrupt_hz; |
|
|
else |
|
|
d->interrupt_every_x_cycles = 0; |
|
113 |
} |
} |
114 |
|
|
115 |
|
|
116 |
/* |
DEVICE_TICK(mc146818) |
|
* dev_mc146818_tick(): |
|
|
*/ |
|
|
void dev_mc146818_tick(struct cpu *cpu, void *extra) |
|
117 |
{ |
{ |
118 |
struct mc_data *d = extra; |
struct mc_data *d = extra; |
119 |
|
int pti = d->pending_timer_interrupts; |
120 |
|
|
121 |
recalc_interrupt_cycle(cpu, d); |
if ((d->reg[MC_REGB * 4] & MC_REGB_PIE) && pti > 0) { |
122 |
|
static int warned = 0; |
123 |
|
if (pti > 1500 && !warned) { |
124 |
|
warned = 1; |
125 |
|
fatal("[ WARNING: MC146818 interrupts lost, " |
126 |
|
"host too slow? ]\n"); |
127 |
|
} |
128 |
|
|
129 |
if ((d->reg[MC_REGB * 4] & MC_REGB_PIE) && |
#if 0 |
130 |
d->interrupt_every_x_cycles > 0) { |
/* For debugging, to see how much the interrupts are |
131 |
d->cycles_left_until_interrupt -= (1 << TICK_SHIFT); |
lagging behind the real clock: */ |
132 |
|
{ |
133 |
if (d->cycles_left_until_interrupt < 0 || |
static int x = 0; |
134 |
d->cycles_left_until_interrupt >= |
if (++x == 1) { |
135 |
d->interrupt_every_x_cycles) { |
x = 0; |
136 |
/* fatal("[ rtc interrupt (every %i cycles) ]\n", |
printf("%i ", pti); |
137 |
d->interrupt_every_x_cycles); */ |
fflush(stdout); |
138 |
cpu_interrupt(cpu, d->irq_nr); |
} |
139 |
|
} |
140 |
|
#endif |
141 |
|
|
142 |
d->reg[MC_REGC * 4] |= MC_REGC_PF; |
INTERRUPT_ASSERT(d->irq); |
143 |
|
|
144 |
/* Reset the cycle countdown: */ |
d->reg[MC_REGC * 4] |= MC_REGC_PF; |
|
while (d->cycles_left_until_interrupt < 0) |
|
|
d->cycles_left_until_interrupt += |
|
|
d->interrupt_every_x_cycles; |
|
|
} |
|
145 |
} |
} |
146 |
|
|
147 |
if (d->reg[MC_REGC * 4] & MC_REGC_UF || |
if (d->reg[MC_REGC * 4] & MC_REGC_UF || |
213 |
*/ |
*/ |
214 |
switch (d->access_style) { |
switch (d->access_style) { |
215 |
case MC146818_ALGOR: |
case MC146818_ALGOR: |
216 |
|
/* |
217 |
|
* NetBSD/evbmips sources indicate that the Algor year base |
218 |
|
* is 1920. This makes the time work with NetBSD in Malta |
219 |
|
* emulation. However, for Linux, commenting out this line |
220 |
|
* works better. (TODO: Find a way to make both work?) |
221 |
|
*/ |
222 |
d->reg[4 * MC_YEAR] += 80; |
d->reg[4 * MC_YEAR] += 80; |
223 |
break; |
break; |
224 |
case MC146818_ARC_NEC: |
case MC146818_ARC_NEC: |
452 |
; |
; |
453 |
} |
} |
454 |
|
|
455 |
recalc_interrupt_cycle(cpu, d); |
if (d->interrupt_hz != d->old_interrupt_hz) { |
456 |
|
debug("[ rtc changed to interrupt at %i Hz ]\n", |
457 |
d->cycles_left_until_interrupt = |
d->interrupt_hz); |
458 |
d->interrupt_every_x_cycles; |
|
459 |
|
d->old_interrupt_hz = d->interrupt_hz; |
460 |
|
|
461 |
|
if (d->timer == NULL) |
462 |
|
d->timer = timer_add(d->interrupt_hz, |
463 |
|
timer_tick, d); |
464 |
|
else |
465 |
|
timer_update_frequency(d->timer, |
466 |
|
d->interrupt_hz); |
467 |
|
} |
468 |
|
|
469 |
d->reg[MC_REGA * 4] = |
d->reg[MC_REGA * 4] = |
470 |
data[0] & (MC_REGA_RSMASK | MC_REGA_DVMASK); |
data[0] & (MC_REGA_RSMASK | MC_REGA_DVMASK); |
|
|
|
|
debug("[ rtc set to interrupt every %i:th cycle ]\n", |
|
|
d->interrupt_every_x_cycles); |
|
471 |
break; |
break; |
472 |
case MC_REGB*4: |
case MC_REGB*4: |
|
if (((data[0] ^ d->reg[MC_REGB*4]) & MC_REGB_PIE)) |
|
|
d->cycles_left_until_interrupt = |
|
|
d->interrupt_every_x_cycles; |
|
473 |
d->reg[MC_REGB*4] = data[0]; |
d->reg[MC_REGB*4] = data[0]; |
474 |
if (!(data[0] & MC_REGB_PIE)) { |
if (!(data[0] & MC_REGB_PIE)) { |
475 |
cpu_interrupt_ack(cpu, d->irq_nr); |
INTERRUPT_DEASSERT(d->irq); |
|
/* d->cycles_left_until_interrupt = |
|
|
d->interrupt_every_x_cycles; */ |
|
476 |
} |
} |
477 |
|
|
478 |
/* debug("[ mc146818: write to MC_REGB, data[0] " |
/* debug("[ mc146818: write to MC_REGB, data[0] " |
479 |
"= 0x%02x ]\n", data[0]); */ |
"= 0x%02x ]\n", data[0]); */ |
480 |
break; |
break; |
566 |
data[0] = d->reg[relative_addr]; |
data[0] = d->reg[relative_addr]; |
567 |
|
|
568 |
if (relative_addr == MC_REGC*4) { |
if (relative_addr == MC_REGC*4) { |
569 |
cpu_interrupt_ack(cpu, d->irq_nr); |
INTERRUPT_DEASSERT(d->irq); |
570 |
/* d->cycles_left_until_interrupt = |
|
571 |
d->interrupt_every_x_cycles; */ |
/* |
572 |
|
* Acknowledging an interrupt decreases the |
573 |
|
* number of pending "real world" timer ticks. |
574 |
|
*/ |
575 |
|
if (d->reg[MC_REGC * 4] & MC_REGC_PF && |
576 |
|
d->pending_timer_interrupts > 0) |
577 |
|
d->pending_timer_interrupts --; |
578 |
|
|
579 |
d->reg[MC_REGC * 4] = 0x00; |
d->reg[MC_REGC * 4] = 0x00; |
580 |
} |
} |
581 |
} |
} |
601 |
* so it contains both rtc related stuff and the station's Ethernet address. |
* so it contains both rtc related stuff and the station's Ethernet address. |
602 |
*/ |
*/ |
603 |
void dev_mc146818_init(struct machine *machine, struct memory *mem, |
void dev_mc146818_init(struct machine *machine, struct memory *mem, |
604 |
uint64_t baseaddr, int irq_nr, int access_style, int addrdiv) |
uint64_t baseaddr, char *irq_path, int access_style, int addrdiv) |
605 |
{ |
{ |
606 |
unsigned char ether_address[6]; |
unsigned char ether_address[6]; |
607 |
int i, dev_len; |
int i, dev_len; |
614 |
} |
} |
615 |
|
|
616 |
memset(d, 0, sizeof(struct mc_data)); |
memset(d, 0, sizeof(struct mc_data)); |
617 |
d->irq_nr = irq_nr; |
|
618 |
d->access_style = access_style; |
d->access_style = access_style; |
619 |
d->addrdiv = addrdiv; |
d->addrdiv = addrdiv; |
620 |
|
|
621 |
|
INTERRUPT_CONNECT(irq_path, d->irq); |
622 |
|
|
623 |
d->use_bcd = 0; |
d->use_bcd = 0; |
624 |
switch (access_style) { |
switch (access_style) { |
625 |
case MC146818_SGI: |
case MC146818_SGI: |