1 |
dpavlin |
4 |
/* |
2 |
dpavlin |
34 |
* Copyright (C) 2003-2007 Anders Gavare. All rights reserved. |
3 |
dpavlin |
4 |
* |
4 |
|
|
* Redistribution and use in source and binary forms, with or without |
5 |
|
|
* modification, are permitted provided that the following conditions are met: |
6 |
|
|
* |
7 |
|
|
* 1. Redistributions of source code must retain the above copyright |
8 |
|
|
* notice, this list of conditions and the following disclaimer. |
9 |
|
|
* 2. Redistributions in binary form must reproduce the above copyright |
10 |
|
|
* notice, this list of conditions and the following disclaimer in the |
11 |
|
|
* documentation and/or other materials provided with the distribution. |
12 |
|
|
* 3. The name of the author may not be used to endorse or promote products |
13 |
|
|
* derived from this software without specific prior written permission. |
14 |
|
|
* |
15 |
|
|
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND |
16 |
|
|
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
17 |
|
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
18 |
|
|
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE |
19 |
|
|
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL |
20 |
|
|
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS |
21 |
|
|
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) |
22 |
|
|
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT |
23 |
|
|
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY |
24 |
|
|
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF |
25 |
|
|
* SUCH DAMAGE. |
26 |
|
|
* |
27 |
|
|
* |
28 |
dpavlin |
42 |
* $Id: dev_mc146818.c,v 1.99 2007/06/15 19:57:33 debug Exp $ |
29 |
dpavlin |
4 |
* |
30 |
dpavlin |
42 |
* COMMENT: MC146818 real-time clock |
31 |
|
|
* |
32 |
dpavlin |
16 |
* (DS1687 as used in some other machines is also similar to the MC146818.) |
33 |
dpavlin |
4 |
* |
34 |
|
|
* This device contains Date/time, the machine's ethernet address (on |
35 |
|
|
* DECstation 3100), and can cause periodic (hardware) interrupts. |
36 |
|
|
* |
37 |
|
|
* NOTE: Many register offsets are multiplied by 4 in this code; this is |
38 |
|
|
* because I originally wrote it for DECstation 3100 emulation, where the |
39 |
|
|
* registered are spaced that way. |
40 |
|
|
*/ |
41 |
|
|
|
42 |
|
|
#include <stdio.h> |
43 |
|
|
#include <stdlib.h> |
44 |
|
|
#include <string.h> |
45 |
|
|
#include <time.h> |
46 |
|
|
|
47 |
|
|
#include "cpu.h" |
48 |
|
|
#include "devices.h" |
49 |
|
|
#include "machine.h" |
50 |
|
|
#include "memory.h" |
51 |
|
|
#include "misc.h" |
52 |
dpavlin |
32 |
#include "timer.h" |
53 |
dpavlin |
4 |
|
54 |
|
|
#include "mc146818reg.h" |
55 |
|
|
|
56 |
|
|
|
57 |
|
|
#define to_bcd(x) ( ((x)/10) * 16 + ((x)%10) ) |
58 |
dpavlin |
20 |
#define from_bcd(x) ( ((x)>>4) * 10 + ((x)&15) ) |
59 |
dpavlin |
4 |
|
60 |
|
|
/* #define MC146818_DEBUG */ |
61 |
|
|
|
62 |
dpavlin |
42 |
#define MC146818_TICK_SHIFT 14 |
63 |
dpavlin |
4 |
|
64 |
|
|
|
65 |
|
|
/* 256 on DECstation, SGI uses reg at 72*4 as the Century */ |
66 |
|
|
#define N_REGISTERS 1024 |
67 |
|
|
struct mc_data { |
68 |
dpavlin |
32 |
int access_style; |
69 |
|
|
int last_addr; |
70 |
dpavlin |
4 |
|
71 |
dpavlin |
32 |
int register_choice; |
72 |
|
|
int reg[N_REGISTERS]; |
73 |
|
|
int addrdiv; |
74 |
dpavlin |
4 |
|
75 |
dpavlin |
32 |
int use_bcd; |
76 |
dpavlin |
4 |
|
77 |
dpavlin |
32 |
int timebase_hz; |
78 |
|
|
int interrupt_hz; |
79 |
|
|
int old_interrupt_hz; |
80 |
dpavlin |
34 |
struct interrupt irq; |
81 |
dpavlin |
32 |
struct timer *timer; |
82 |
|
|
volatile int pending_timer_interrupts; |
83 |
dpavlin |
4 |
|
84 |
dpavlin |
32 |
int previous_second; |
85 |
|
|
int n_seconds_elapsed; |
86 |
|
|
int uip_threshold; |
87 |
dpavlin |
4 |
|
88 |
dpavlin |
32 |
int ugly_netbsd_prep_hack_done; |
89 |
|
|
int ugly_netbsd_prep_hack_sec; |
90 |
dpavlin |
4 |
}; |
91 |
|
|
|
92 |
|
|
|
93 |
|
|
/* |
94 |
dpavlin |
20 |
* Ugly hack to fool NetBSD/prep to accept the clock. (See mcclock_isa_match |
95 |
|
|
* in NetBSD's arch/prep/isa/mcclock_isa.c for details.) |
96 |
|
|
*/ |
97 |
|
|
#define NETBSD_HACK_INIT 0 |
98 |
|
|
#define NETBSD_HACK_FIRST_1 1 |
99 |
|
|
#define NETBSD_HACK_FIRST_2 2 |
100 |
|
|
#define NETBSD_HACK_SECOND_1 3 |
101 |
|
|
#define NETBSD_HACK_SECOND_2 4 |
102 |
|
|
#define NETBSD_HACK_DONE 5 |
103 |
|
|
|
104 |
|
|
|
105 |
|
|
/* |
106 |
dpavlin |
32 |
* timer_tick(): |
107 |
dpavlin |
4 |
* |
108 |
dpavlin |
32 |
* Called d->interrupt_hz times per (real-world) second. |
109 |
dpavlin |
4 |
*/ |
110 |
dpavlin |
32 |
static void timer_tick(struct timer *timer, void *extra) |
111 |
dpavlin |
4 |
{ |
112 |
dpavlin |
32 |
struct mc_data *d = (struct mc_data *) extra; |
113 |
|
|
d->pending_timer_interrupts ++; |
114 |
dpavlin |
4 |
} |
115 |
|
|
|
116 |
|
|
|
117 |
dpavlin |
32 |
DEVICE_TICK(mc146818) |
118 |
dpavlin |
4 |
{ |
119 |
|
|
struct mc_data *d = extra; |
120 |
dpavlin |
32 |
int pti = d->pending_timer_interrupts; |
121 |
dpavlin |
4 |
|
122 |
dpavlin |
32 |
if ((d->reg[MC_REGB * 4] & MC_REGB_PIE) && pti > 0) { |
123 |
|
|
#if 0 |
124 |
|
|
/* For debugging, to see how much the interrupts are |
125 |
|
|
lagging behind the real clock: */ |
126 |
|
|
{ |
127 |
|
|
static int x = 0; |
128 |
|
|
if (++x == 1) { |
129 |
|
|
x = 0; |
130 |
|
|
printf("%i ", pti); |
131 |
|
|
fflush(stdout); |
132 |
|
|
} |
133 |
|
|
} |
134 |
|
|
#endif |
135 |
dpavlin |
4 |
|
136 |
dpavlin |
34 |
INTERRUPT_ASSERT(d->irq); |
137 |
dpavlin |
4 |
|
138 |
dpavlin |
32 |
d->reg[MC_REGC * 4] |= MC_REGC_PF; |
139 |
dpavlin |
4 |
} |
140 |
|
|
|
141 |
|
|
if (d->reg[MC_REGC * 4] & MC_REGC_UF || |
142 |
|
|
d->reg[MC_REGC * 4] & MC_REGC_AF || |
143 |
|
|
d->reg[MC_REGC * 4] & MC_REGC_PF) |
144 |
|
|
d->reg[MC_REGC * 4] |= MC_REGC_IRQF; |
145 |
|
|
} |
146 |
|
|
|
147 |
|
|
|
148 |
|
|
/* |
149 |
|
|
* dev_mc146818_jazz_access(): |
150 |
|
|
* |
151 |
|
|
* It seems like JAZZ machines accesses the mc146818 by writing one byte to |
152 |
|
|
* 0x90000070 and then reading or writing another byte at 0x......0004000. |
153 |
|
|
*/ |
154 |
dpavlin |
42 |
DEVICE_ACCESS(mc146818_jazz) |
155 |
dpavlin |
4 |
{ |
156 |
|
|
struct mc_data *d = extra; |
157 |
|
|
|
158 |
|
|
#ifdef MC146818_DEBUG |
159 |
|
|
if (writeflag == MEM_WRITE) { |
160 |
|
|
int i; |
161 |
|
|
fatal("[ mc146818_jazz: write to addr=0x%04x: ", |
162 |
|
|
(int)relative_addr); |
163 |
|
|
for (i=0; i<len; i++) |
164 |
|
|
fatal("%02x ", data[i]); |
165 |
|
|
fatal("]\n"); |
166 |
|
|
} else |
167 |
|
|
fatal("[ mc146818_jazz: read from addr=0x%04x ]\n", |
168 |
|
|
(int)relative_addr); |
169 |
|
|
#endif |
170 |
|
|
|
171 |
|
|
if (writeflag == MEM_WRITE) { |
172 |
|
|
d->last_addr = data[0]; |
173 |
|
|
return 1; |
174 |
|
|
} else { |
175 |
|
|
data[0] = d->last_addr; |
176 |
|
|
return 1; |
177 |
|
|
} |
178 |
|
|
} |
179 |
|
|
|
180 |
|
|
|
181 |
|
|
/* |
182 |
|
|
* mc146818_update_time(): |
183 |
|
|
* |
184 |
|
|
* This function updates the MC146818 registers by reading |
185 |
|
|
* the host's clock. |
186 |
|
|
*/ |
187 |
|
|
static void mc146818_update_time(struct mc_data *d) |
188 |
|
|
{ |
189 |
|
|
struct tm *tmp; |
190 |
|
|
time_t timet; |
191 |
|
|
|
192 |
|
|
timet = time(NULL); |
193 |
|
|
tmp = gmtime(&timet); |
194 |
|
|
|
195 |
|
|
d->reg[4 * MC_SEC] = tmp->tm_sec; |
196 |
|
|
d->reg[4 * MC_MIN] = tmp->tm_min; |
197 |
|
|
d->reg[4 * MC_HOUR] = tmp->tm_hour; |
198 |
|
|
d->reg[4 * MC_DOW] = tmp->tm_wday + 1; |
199 |
|
|
d->reg[4 * MC_DOM] = tmp->tm_mday; |
200 |
|
|
d->reg[4 * MC_MONTH] = tmp->tm_mon + 1; |
201 |
|
|
d->reg[4 * MC_YEAR] = tmp->tm_year; |
202 |
|
|
|
203 |
dpavlin |
16 |
/* |
204 |
|
|
* Special hacks for emulating the behaviour of various machines: |
205 |
|
|
*/ |
206 |
dpavlin |
4 |
switch (d->access_style) { |
207 |
dpavlin |
20 |
case MC146818_ALGOR: |
208 |
dpavlin |
32 |
/* |
209 |
|
|
* NetBSD/evbmips sources indicate that the Algor year base |
210 |
|
|
* is 1920. This makes the time work with NetBSD in Malta |
211 |
|
|
* emulation. However, for Linux, commenting out this line |
212 |
|
|
* works better. (TODO: Find a way to make both work?) |
213 |
|
|
*/ |
214 |
dpavlin |
20 |
d->reg[4 * MC_YEAR] += 80; |
215 |
|
|
break; |
216 |
dpavlin |
4 |
case MC146818_ARC_NEC: |
217 |
|
|
d->reg[4 * MC_YEAR] += (0x18 - 104); |
218 |
|
|
break; |
219 |
dpavlin |
16 |
case MC146818_CATS: |
220 |
|
|
d->reg[4 * MC_YEAR] %= 100; |
221 |
|
|
break; |
222 |
dpavlin |
4 |
case MC146818_SGI: |
223 |
|
|
/* |
224 |
|
|
* NetBSD/sgimips assumes data in BCD format. |
225 |
|
|
* Also, IRIX stores the year value in a weird |
226 |
|
|
* format, according to ../arch/sgimips/sgimips/clockvar.h |
227 |
|
|
* in NetBSD: |
228 |
|
|
* |
229 |
|
|
* "If year < 1985, store (year - 1970), else |
230 |
|
|
* (year - 1940). This matches IRIX semantics." |
231 |
|
|
* |
232 |
|
|
* Another rule: It seems that a real SGI IP32 box |
233 |
|
|
* uses the value 5 for the year 2005. |
234 |
|
|
*/ |
235 |
|
|
d->reg[4 * MC_YEAR] = |
236 |
|
|
d->reg[4 * MC_YEAR] >= 100 ? |
237 |
|
|
(d->reg[4 * MC_YEAR] - 100) : |
238 |
|
|
( |
239 |
|
|
d->reg[4 * MC_YEAR] < 85 ? |
240 |
|
|
(d->reg[4 * MC_YEAR] - 30 + 40) |
241 |
|
|
: (d->reg[4 * MC_YEAR] - 40) |
242 |
|
|
); |
243 |
|
|
/* Century: */ |
244 |
|
|
d->reg[72 * 4] = 19 + (tmp->tm_year / 100); |
245 |
|
|
break; |
246 |
|
|
case MC146818_DEC: |
247 |
|
|
/* |
248 |
|
|
* DECstations must have 72 or 73 in the |
249 |
|
|
* Year field, or Ultrix screems. (Weird.) |
250 |
|
|
*/ |
251 |
|
|
d->reg[4 * MC_YEAR] = 72; |
252 |
|
|
|
253 |
|
|
/* |
254 |
|
|
* Linux on DECstation stores the year in register 63, |
255 |
|
|
* but no other DECstation OS does? (Hm.) |
256 |
|
|
*/ |
257 |
|
|
d->reg[4 * 63] = tmp->tm_year - 100; |
258 |
|
|
break; |
259 |
|
|
} |
260 |
|
|
|
261 |
|
|
if (d->use_bcd) { |
262 |
|
|
d->reg[4 * MC_SEC] = to_bcd(d->reg[4 * MC_SEC]); |
263 |
|
|
d->reg[4 * MC_MIN] = to_bcd(d->reg[4 * MC_MIN]); |
264 |
|
|
d->reg[4 * MC_HOUR] = to_bcd(d->reg[4 * MC_HOUR]); |
265 |
|
|
d->reg[4 * MC_DOW] = to_bcd(d->reg[4 * MC_DOW]); |
266 |
|
|
d->reg[4 * MC_DOM] = to_bcd(d->reg[4 * MC_DOM]); |
267 |
|
|
d->reg[4 * MC_MONTH] = to_bcd(d->reg[4 * MC_MONTH]); |
268 |
|
|
d->reg[4 * MC_YEAR] = to_bcd(d->reg[4 * MC_YEAR]); |
269 |
|
|
|
270 |
|
|
/* Used by Linux on DECstation: (Hm) */ |
271 |
|
|
d->reg[4 * 63] = to_bcd(d->reg[4 * 63]); |
272 |
|
|
|
273 |
|
|
/* Used on SGI: */ |
274 |
|
|
d->reg[4 * 72] = to_bcd(d->reg[4 * 72]); |
275 |
|
|
} |
276 |
|
|
} |
277 |
|
|
|
278 |
|
|
|
279 |
dpavlin |
42 |
DEVICE_ACCESS(mc146818) |
280 |
dpavlin |
4 |
{ |
281 |
dpavlin |
42 |
struct mc_data *d = extra; |
282 |
dpavlin |
4 |
struct tm *tmp; |
283 |
|
|
time_t timet; |
284 |
dpavlin |
22 |
size_t i; |
285 |
dpavlin |
4 |
|
286 |
dpavlin |
42 |
/* NOTE/TODO: This access function only handles 8-bit accesses! */ |
287 |
|
|
|
288 |
dpavlin |
4 |
relative_addr /= d->addrdiv; |
289 |
|
|
|
290 |
|
|
/* Different ways of accessing the registers: */ |
291 |
|
|
switch (d->access_style) { |
292 |
dpavlin |
20 |
case MC146818_ALGOR: |
293 |
dpavlin |
16 |
case MC146818_CATS: |
294 |
dpavlin |
4 |
case MC146818_PC_CMOS: |
295 |
dpavlin |
16 |
if ((relative_addr & 1) == 0x00) { |
296 |
dpavlin |
4 |
if (writeflag == MEM_WRITE) { |
297 |
|
|
d->last_addr = data[0]; |
298 |
|
|
return 1; |
299 |
|
|
} else { |
300 |
|
|
data[0] = d->last_addr; |
301 |
|
|
return 1; |
302 |
|
|
} |
303 |
dpavlin |
16 |
} else |
304 |
dpavlin |
4 |
relative_addr = d->last_addr * 4; |
305 |
|
|
break; |
306 |
|
|
case MC146818_ARC_NEC: |
307 |
|
|
if (relative_addr == 0x01) { |
308 |
|
|
if (writeflag == MEM_WRITE) { |
309 |
|
|
d->last_addr = data[0]; |
310 |
|
|
return 1; |
311 |
|
|
} else { |
312 |
|
|
data[0] = d->last_addr; |
313 |
|
|
return 1; |
314 |
|
|
} |
315 |
|
|
} else if (relative_addr == 0x00) |
316 |
|
|
relative_addr = d->last_addr * 4; |
317 |
|
|
else { |
318 |
|
|
fatal("[ mc146818: not accessed as an " |
319 |
|
|
"MC146818_ARC_NEC device! ]\n"); |
320 |
|
|
} |
321 |
|
|
break; |
322 |
|
|
case MC146818_ARC_JAZZ: |
323 |
|
|
/* See comment for dev_mc146818_jazz_access(). */ |
324 |
|
|
relative_addr = d->last_addr * 4; |
325 |
|
|
break; |
326 |
|
|
case MC146818_DEC: |
327 |
|
|
case MC146818_SGI: |
328 |
|
|
/* |
329 |
|
|
* This device was originally written for DECstation |
330 |
|
|
* emulation, so no changes are necessary for that access |
331 |
|
|
* style. |
332 |
|
|
* |
333 |
|
|
* SGI access bytes 0x0..0xd at offsets 0x0yz..0xdyz, where yz |
334 |
|
|
* should be ignored. It works _almost_ as DEC, if offsets are |
335 |
|
|
* divided by 0x40. |
336 |
|
|
*/ |
337 |
dpavlin |
20 |
break; |
338 |
|
|
case MC146818_PMPPC: |
339 |
|
|
relative_addr *= 4; |
340 |
|
|
break; |
341 |
dpavlin |
4 |
default: |
342 |
|
|
; |
343 |
|
|
} |
344 |
|
|
|
345 |
|
|
#ifdef MC146818_DEBUG |
346 |
|
|
if (writeflag == MEM_WRITE) { |
347 |
|
|
fatal("[ mc146818: write to addr=0x%04x (len %i): ", |
348 |
|
|
(int)relative_addr, (int)len); |
349 |
|
|
for (i=0; i<len; i++) |
350 |
dpavlin |
20 |
fatal("0x%02x ", data[i]); |
351 |
dpavlin |
4 |
fatal("]\n"); |
352 |
|
|
} |
353 |
|
|
#endif |
354 |
|
|
|
355 |
|
|
/* |
356 |
dpavlin |
12 |
* Sprite seems to wants UF interrupt status, once every second, or |
357 |
dpavlin |
4 |
* it hangs forever during bootup. (These do not cause interrupts, |
358 |
|
|
* but it is good enough... Sprite polls this, iirc.) |
359 |
dpavlin |
12 |
* |
360 |
|
|
* Linux on at least sgimips and evbmips (Malta) wants the UIP bit |
361 |
|
|
* in REGA to be updated once a second. |
362 |
dpavlin |
4 |
*/ |
363 |
dpavlin |
20 |
if (relative_addr == MC_REGA*4 || relative_addr == MC_REGC*4) { |
364 |
|
|
timet = time(NULL); |
365 |
|
|
tmp = gmtime(&timet); |
366 |
|
|
d->reg[MC_REGC * 4] &= ~MC_REGC_UF; |
367 |
|
|
if (tmp->tm_sec != d->previous_second) { |
368 |
|
|
d->n_seconds_elapsed ++; |
369 |
|
|
d->previous_second = tmp->tm_sec; |
370 |
|
|
} |
371 |
|
|
if (d->n_seconds_elapsed > d->uip_threshold) { |
372 |
|
|
d->n_seconds_elapsed = 0; |
373 |
dpavlin |
12 |
|
374 |
dpavlin |
20 |
d->reg[MC_REGA * 4] |= MC_REGA_UIP; |
375 |
dpavlin |
12 |
|
376 |
dpavlin |
20 |
d->reg[MC_REGC * 4] |= MC_REGC_UF; |
377 |
|
|
d->reg[MC_REGC * 4] |= MC_REGC_IRQF; |
378 |
dpavlin |
4 |
|
379 |
dpavlin |
20 |
/* For some reason, some Linux/DECstation KN04 |
380 |
|
|
kernels want the PF (periodic flag) bit set, |
381 |
|
|
even though interrupts are not enabled? */ |
382 |
|
|
d->reg[MC_REGC * 4] |= MC_REGC_PF; |
383 |
|
|
} else |
384 |
|
|
d->reg[MC_REGA * 4] &= ~MC_REGA_UIP; |
385 |
|
|
} |
386 |
dpavlin |
4 |
|
387 |
|
|
/* RTC data is in either BCD format or binary: */ |
388 |
dpavlin |
12 |
if (d->use_bcd) |
389 |
dpavlin |
4 |
d->reg[MC_REGB * 4] &= ~(1 << 2); |
390 |
dpavlin |
12 |
else |
391 |
dpavlin |
4 |
d->reg[MC_REGB * 4] |= (1 << 2); |
392 |
|
|
|
393 |
|
|
/* RTC date/time is always Valid: */ |
394 |
|
|
d->reg[MC_REGD * 4] |= MC_REGD_VRT; |
395 |
|
|
|
396 |
|
|
if (writeflag == MEM_WRITE) { |
397 |
|
|
/* WRITE: */ |
398 |
|
|
switch (relative_addr) { |
399 |
|
|
case MC_REGA*4: |
400 |
|
|
if ((data[0] & MC_REGA_DVMASK) == MC_BASE_32_KHz) |
401 |
|
|
d->timebase_hz = 32000; |
402 |
|
|
if ((data[0] & MC_REGA_DVMASK) == MC_BASE_1_MHz) |
403 |
|
|
d->timebase_hz = 1000000; |
404 |
|
|
if ((data[0] & MC_REGA_DVMASK) == MC_BASE_4_MHz) |
405 |
|
|
d->timebase_hz = 4000000; |
406 |
|
|
switch (data[0] & MC_REGA_RSMASK) { |
407 |
|
|
case MC_RATE_NONE: |
408 |
|
|
d->interrupt_hz = 0; |
409 |
|
|
break; |
410 |
|
|
case MC_RATE_1: |
411 |
|
|
if (d->timebase_hz == 32000) |
412 |
|
|
d->interrupt_hz = 256; |
413 |
|
|
else |
414 |
|
|
d->interrupt_hz = 32768; |
415 |
|
|
break; |
416 |
|
|
case MC_RATE_2: |
417 |
|
|
if (d->timebase_hz == 32000) |
418 |
|
|
d->interrupt_hz = 128; |
419 |
|
|
else |
420 |
|
|
d->interrupt_hz = 16384; |
421 |
|
|
break; |
422 |
|
|
case MC_RATE_8192_Hz: d->interrupt_hz = 8192; break; |
423 |
|
|
case MC_RATE_4096_Hz: d->interrupt_hz = 4096; break; |
424 |
|
|
case MC_RATE_2048_Hz: d->interrupt_hz = 2048; break; |
425 |
|
|
case MC_RATE_1024_Hz: d->interrupt_hz = 1024; break; |
426 |
|
|
case MC_RATE_512_Hz: d->interrupt_hz = 512; break; |
427 |
|
|
case MC_RATE_256_Hz: d->interrupt_hz = 256; break; |
428 |
|
|
case MC_RATE_128_Hz: d->interrupt_hz = 128; break; |
429 |
|
|
case MC_RATE_64_Hz: d->interrupt_hz = 64; break; |
430 |
|
|
case MC_RATE_32_Hz: d->interrupt_hz = 32; break; |
431 |
|
|
case MC_RATE_16_Hz: d->interrupt_hz = 16; break; |
432 |
|
|
case MC_RATE_8_Hz: d->interrupt_hz = 8; break; |
433 |
|
|
case MC_RATE_4_Hz: d->interrupt_hz = 4; break; |
434 |
|
|
case MC_RATE_2_Hz: d->interrupt_hz = 2; break; |
435 |
dpavlin |
20 |
default:/* debug("[ mc146818: unimplemented " |
436 |
dpavlin |
4 |
"MC_REGA RS: %i ]\n", |
437 |
|
|
data[0] & MC_REGA_RSMASK); */ |
438 |
|
|
; |
439 |
|
|
} |
440 |
|
|
|
441 |
dpavlin |
32 |
if (d->interrupt_hz != d->old_interrupt_hz) { |
442 |
|
|
debug("[ rtc changed to interrupt at %i Hz ]\n", |
443 |
|
|
d->interrupt_hz); |
444 |
dpavlin |
4 |
|
445 |
dpavlin |
32 |
d->old_interrupt_hz = d->interrupt_hz; |
446 |
dpavlin |
4 |
|
447 |
dpavlin |
32 |
if (d->timer == NULL) |
448 |
|
|
d->timer = timer_add(d->interrupt_hz, |
449 |
|
|
timer_tick, d); |
450 |
|
|
else |
451 |
|
|
timer_update_frequency(d->timer, |
452 |
|
|
d->interrupt_hz); |
453 |
|
|
} |
454 |
|
|
|
455 |
dpavlin |
4 |
d->reg[MC_REGA * 4] = |
456 |
|
|
data[0] & (MC_REGA_RSMASK | MC_REGA_DVMASK); |
457 |
|
|
break; |
458 |
|
|
case MC_REGB*4: |
459 |
|
|
d->reg[MC_REGB*4] = data[0]; |
460 |
|
|
if (!(data[0] & MC_REGB_PIE)) { |
461 |
dpavlin |
34 |
INTERRUPT_DEASSERT(d->irq); |
462 |
dpavlin |
4 |
} |
463 |
dpavlin |
32 |
|
464 |
dpavlin |
4 |
/* debug("[ mc146818: write to MC_REGB, data[0] " |
465 |
|
|
"= 0x%02x ]\n", data[0]); */ |
466 |
|
|
break; |
467 |
|
|
case MC_REGC*4: |
468 |
|
|
d->reg[MC_REGC * 4] = data[0]; |
469 |
|
|
debug("[ mc146818: write to MC_REGC, data[0] = " |
470 |
|
|
"0x%02x ]\n", data[0]); |
471 |
|
|
break; |
472 |
|
|
case 0x128: |
473 |
|
|
d->reg[relative_addr] = data[0]; |
474 |
|
|
if (data[0] & 8) { |
475 |
dpavlin |
22 |
int j; |
476 |
|
|
|
477 |
dpavlin |
4 |
/* Used on SGI to power off the machine. */ |
478 |
|
|
fatal("[ md146818: power off ]\n"); |
479 |
dpavlin |
22 |
for (j=0; j<cpu->machine->ncpus; j++) |
480 |
|
|
cpu->machine->cpus[j]->running = 0; |
481 |
dpavlin |
4 |
cpu->machine-> |
482 |
|
|
exit_without_entering_debugger = 1; |
483 |
|
|
} |
484 |
|
|
break; |
485 |
|
|
default: |
486 |
|
|
d->reg[relative_addr] = data[0]; |
487 |
|
|
|
488 |
|
|
debug("[ mc146818: unimplemented write to " |
489 |
|
|
"relative_addr = %08lx: ", (long)relative_addr); |
490 |
|
|
for (i=0; i<len; i++) |
491 |
|
|
debug("%02x ", data[i]); |
492 |
|
|
debug("]\n"); |
493 |
|
|
} |
494 |
|
|
} else { |
495 |
|
|
/* READ: */ |
496 |
|
|
switch (relative_addr) { |
497 |
|
|
case 0x01: /* Station's ethernet address (6 bytes) */ |
498 |
|
|
case 0x05: /* (on DECstation 3100) */ |
499 |
|
|
case 0x09: |
500 |
|
|
case 0x0d: |
501 |
|
|
case 0x11: |
502 |
|
|
case 0x15: |
503 |
|
|
break; |
504 |
|
|
case 4 * MC_SEC: |
505 |
dpavlin |
20 |
if (d->ugly_netbsd_prep_hack_done < NETBSD_HACK_DONE) { |
506 |
|
|
d->ugly_netbsd_prep_hack_done ++; |
507 |
|
|
switch (d->ugly_netbsd_prep_hack_done) { |
508 |
|
|
case NETBSD_HACK_FIRST_1: |
509 |
|
|
d->ugly_netbsd_prep_hack_sec = |
510 |
|
|
from_bcd(d->reg[relative_addr]); |
511 |
|
|
break; |
512 |
|
|
case NETBSD_HACK_FIRST_2: |
513 |
|
|
d->reg[relative_addr] = to_bcd( |
514 |
|
|
d->ugly_netbsd_prep_hack_sec); |
515 |
|
|
break; |
516 |
|
|
case NETBSD_HACK_SECOND_1: |
517 |
|
|
case NETBSD_HACK_SECOND_2: |
518 |
|
|
d->reg[relative_addr] = to_bcd((1 + |
519 |
|
|
d->ugly_netbsd_prep_hack_sec) % 60); |
520 |
|
|
break; |
521 |
|
|
} |
522 |
|
|
} |
523 |
dpavlin |
4 |
case 4 * MC_MIN: |
524 |
|
|
case 4 * MC_HOUR: |
525 |
|
|
case 4 * MC_DOW: |
526 |
|
|
case 4 * MC_DOM: |
527 |
|
|
case 4 * MC_MONTH: |
528 |
|
|
case 4 * MC_YEAR: |
529 |
|
|
case 4 * 63: /* 63 is used by Linux on DECstation */ |
530 |
|
|
case 4 * 72: /* 72 is Century, on SGI (DS1687) */ |
531 |
|
|
/* |
532 |
|
|
* If the SET bit is set, then we don't automatically |
533 |
|
|
* update the values. Otherwise, we update them by |
534 |
|
|
* reading from the host's clock: |
535 |
|
|
*/ |
536 |
|
|
if (d->reg[MC_REGB * 4] & MC_REGB_SET) |
537 |
|
|
break; |
538 |
|
|
|
539 |
dpavlin |
20 |
if (d->ugly_netbsd_prep_hack_done >= NETBSD_HACK_DONE) |
540 |
|
|
mc146818_update_time(d); |
541 |
dpavlin |
4 |
break; |
542 |
dpavlin |
20 |
case 4 * MC_REGA: |
543 |
|
|
break; |
544 |
dpavlin |
4 |
case 4 * MC_REGC: /* Interrupt ack. */ |
545 |
|
|
/* NOTE: Acking is done below, _after_ the |
546 |
|
|
register has been read. */ |
547 |
|
|
break; |
548 |
dpavlin |
20 |
default:debug("[ mc146818: read from relative_addr = " |
549 |
dpavlin |
4 |
"%04x ]\n", (int)relative_addr); |
550 |
|
|
} |
551 |
|
|
|
552 |
|
|
data[0] = d->reg[relative_addr]; |
553 |
|
|
|
554 |
|
|
if (relative_addr == MC_REGC*4) { |
555 |
dpavlin |
34 |
INTERRUPT_DEASSERT(d->irq); |
556 |
dpavlin |
32 |
|
557 |
|
|
/* |
558 |
|
|
* Acknowledging an interrupt decreases the |
559 |
|
|
* number of pending "real world" timer ticks. |
560 |
|
|
*/ |
561 |
dpavlin |
34 |
if (d->reg[MC_REGC * 4] & MC_REGC_PF && |
562 |
|
|
d->pending_timer_interrupts > 0) |
563 |
dpavlin |
32 |
d->pending_timer_interrupts --; |
564 |
|
|
|
565 |
dpavlin |
4 |
d->reg[MC_REGC * 4] = 0x00; |
566 |
|
|
} |
567 |
|
|
} |
568 |
|
|
|
569 |
|
|
#ifdef MC146818_DEBUG |
570 |
|
|
if (writeflag == MEM_READ) { |
571 |
|
|
fatal("[ mc146818: read from addr=0x%04x (len %i): ", |
572 |
|
|
(int)relative_addr, (int)len); |
573 |
|
|
for (i=0; i<len; i++) |
574 |
dpavlin |
20 |
fatal("0x%02x ", data[i]); |
575 |
dpavlin |
4 |
fatal("]\n"); |
576 |
|
|
} |
577 |
|
|
#endif |
578 |
|
|
|
579 |
|
|
return 1; |
580 |
|
|
} |
581 |
|
|
|
582 |
|
|
|
583 |
|
|
/* |
584 |
|
|
* dev_mc146818_init(): |
585 |
|
|
* |
586 |
|
|
* This needs to work for both DECstation emulation and other machine types, |
587 |
|
|
* so it contains both rtc related stuff and the station's Ethernet address. |
588 |
|
|
*/ |
589 |
|
|
void dev_mc146818_init(struct machine *machine, struct memory *mem, |
590 |
dpavlin |
34 |
uint64_t baseaddr, char *irq_path, int access_style, int addrdiv) |
591 |
dpavlin |
4 |
{ |
592 |
|
|
unsigned char ether_address[6]; |
593 |
|
|
int i, dev_len; |
594 |
|
|
struct mc_data *d; |
595 |
|
|
|
596 |
dpavlin |
42 |
CHECK_ALLOCATION(d = malloc(sizeof(struct mc_data))); |
597 |
dpavlin |
4 |
memset(d, 0, sizeof(struct mc_data)); |
598 |
dpavlin |
34 |
|
599 |
dpavlin |
4 |
d->access_style = access_style; |
600 |
|
|
d->addrdiv = addrdiv; |
601 |
|
|
|
602 |
dpavlin |
34 |
INTERRUPT_CONNECT(irq_path, d->irq); |
603 |
|
|
|
604 |
dpavlin |
6 |
d->use_bcd = 0; |
605 |
dpavlin |
20 |
switch (access_style) { |
606 |
|
|
case MC146818_SGI: |
607 |
|
|
case MC146818_PC_CMOS: |
608 |
|
|
case MC146818_PMPPC: |
609 |
dpavlin |
6 |
d->use_bcd = 1; |
610 |
dpavlin |
20 |
} |
611 |
dpavlin |
6 |
|
612 |
dpavlin |
20 |
if (machine->machine_type != MACHINE_PREP) { |
613 |
|
|
/* NetBSD/prep has a really ugly clock detection code; |
614 |
dpavlin |
22 |
no other machines/OSes don't need this. */ |
615 |
dpavlin |
20 |
d->ugly_netbsd_prep_hack_done = NETBSD_HACK_DONE; |
616 |
|
|
} |
617 |
|
|
|
618 |
dpavlin |
6 |
if (access_style == MC146818_DEC) { |
619 |
dpavlin |
42 |
/* Station Ethernet Address, on DECstation 3100: */ |
620 |
|
|
for (i=0; i<6; i++) |
621 |
|
|
ether_address[i] = 0x10 * (i+1); |
622 |
dpavlin |
4 |
|
623 |
dpavlin |
6 |
d->reg[0x01] = ether_address[0]; |
624 |
|
|
d->reg[0x05] = ether_address[1]; |
625 |
|
|
d->reg[0x09] = ether_address[2]; |
626 |
|
|
d->reg[0x0d] = ether_address[3]; |
627 |
|
|
d->reg[0x11] = ether_address[4]; |
628 |
|
|
d->reg[0x15] = ether_address[5]; |
629 |
|
|
/* TODO: 19, 1d, 21, 25 = checksum bytes 1,2,2,1 resp. */ |
630 |
|
|
d->reg[0x29] = ether_address[5]; |
631 |
|
|
d->reg[0x2d] = ether_address[4]; |
632 |
|
|
d->reg[0x31] = ether_address[3]; |
633 |
|
|
d->reg[0x35] = ether_address[2]; |
634 |
|
|
d->reg[0x39] = ether_address[1]; |
635 |
|
|
d->reg[0x3d] = ether_address[1]; |
636 |
|
|
d->reg[0x41] = ether_address[0]; |
637 |
|
|
d->reg[0x45] = ether_address[1]; |
638 |
|
|
d->reg[0x49] = ether_address[2]; |
639 |
|
|
d->reg[0x4d] = ether_address[3]; |
640 |
|
|
d->reg[0x51] = ether_address[4]; |
641 |
|
|
d->reg[0x55] = ether_address[5]; |
642 |
|
|
/* TODO: 59, 5d = checksum bytes 1,2 resp. */ |
643 |
|
|
d->reg[0x61] = 0xff; |
644 |
|
|
d->reg[0x65] = 0x00; |
645 |
|
|
d->reg[0x69] = 0x55; |
646 |
|
|
d->reg[0x6d] = 0xaa; |
647 |
|
|
d->reg[0x71] = 0xff; |
648 |
|
|
d->reg[0x75] = 0x00; |
649 |
|
|
d->reg[0x79] = 0x55; |
650 |
|
|
d->reg[0x7d] = 0xaa; |
651 |
dpavlin |
4 |
|
652 |
|
|
/* Battery valid, for DECstations */ |
653 |
|
|
d->reg[0xf8] = 1; |
654 |
|
|
} |
655 |
|
|
|
656 |
dpavlin |
20 |
/* |
657 |
|
|
* uip_threshold should ideally be 1, but when Linux polls the UIP bit |
658 |
|
|
* it looses speed. This hack gives Linux the impression that the cpu |
659 |
|
|
* is uip_threshold times faster than the slow clock it would |
660 |
|
|
* otherwise detect. |
661 |
|
|
* |
662 |
|
|
* TODO: Find out if this messes up Sprite emulation; if so, then |
663 |
|
|
* this hack has to be removed. |
664 |
|
|
*/ |
665 |
dpavlin |
22 |
d->uip_threshold = 8; |
666 |
dpavlin |
20 |
|
667 |
dpavlin |
4 |
if (access_style == MC146818_ARC_JAZZ) |
668 |
|
|
memory_device_register(mem, "mc146818_jazz", 0x90000070ULL, |
669 |
dpavlin |
20 |
1, dev_mc146818_jazz_access, d, DM_DEFAULT, NULL); |
670 |
dpavlin |
4 |
|
671 |
|
|
dev_len = DEV_MC146818_LENGTH; |
672 |
|
|
switch (access_style) { |
673 |
dpavlin |
16 |
case MC146818_CATS: |
674 |
dpavlin |
4 |
case MC146818_PC_CMOS: |
675 |
|
|
dev_len = 2; |
676 |
|
|
break; |
677 |
|
|
case MC146818_SGI: |
678 |
|
|
dev_len = 0x400; |
679 |
|
|
} |
680 |
|
|
|
681 |
|
|
memory_device_register(mem, "mc146818", baseaddr, |
682 |
|
|
dev_len * addrdiv, dev_mc146818_access, |
683 |
dpavlin |
20 |
d, DM_DEFAULT, NULL); |
684 |
dpavlin |
4 |
|
685 |
|
|
mc146818_update_time(d); |
686 |
|
|
|
687 |
dpavlin |
24 |
machine_add_tickfunction(machine, dev_mc146818_tick, d, |
688 |
dpavlin |
42 |
MC146818_TICK_SHIFT); |
689 |
dpavlin |
4 |
} |
690 |
|
|
|