1 /* $Id: sun-mie.c,v 1.4 2010/06/05 13:57:27 fredette Exp $ */
2
3 /* bus/multibus/sun_mie.c - implementation of the Sun Intel Ethernet Multibus emulation: */
4
5 /*
6 * Copyright (c) 2003 Matt Fredette
7 * All rights reserved.
8 *
9 * Redistribution and use in source and binary forms, with or without
10 * modification, are permitted provided that the following conditions
11 * are met:
12 * 1. Redistributions of source code must retain the above copyright
13 * notice, this list of conditions and the following disclaimer.
14 * 2. Redistributions in binary form must reproduce the above copyright
15 * notice, this list of conditions and the following disclaimer in the
16 * documentation and/or other materials provided with the distribution.
17 * 3. All advertising materials mentioning features or use of this software
18 * must display the following acknowledgement:
19 * This product includes software developed by Matt Fredette.
20 * 4. The name of the author may not be used to endorse or promote products
21 * derived from this software without specific prior written permission.
22 *
23 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
24 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
25 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
26 * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
27 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
28 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
29 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
30 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
31 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
32 * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
33 * POSSIBILITY OF SUCH DAMAGE.
34 */
35
36 #include <tme/common.h>
37 _TME_RCSID("$Id: sun-mie.c,v 1.4 2010/06/05 13:57:27 fredette Exp $");
38
39 /* includes: */
40 #include <tme/element.h>
41 #undef TME_BUS_VERSION
42 #define TME_BUS_VERSION TME_X_VERSION(0, 0)
43 #include <tme/generic/bus.h>
44 #undef TME_I825X6_VERSION
45 #define TME_I825X6_VERSION TME_X_VERSION(0, 0)
46 #include <tme/ic/i825x6.h>
47
48 /* macros: */
49
50 /* the amount of memory on the board: */
51 #define TME_SUN_MIE_MEMSIZE (256 * 1024)
52
53 /* the board page size: */
54 #define TME_SUN_MIE_PAGESIZE (1024)
55
56 /* the number of page map entries: */
57 #define TME_SUN_MIE_PGMAP_COUNT (1024)
58
59 /* the number of active TLB entries we can have per page map entry: */
60 #define TME_SUN_MIE_PGMAP_TLBS (4)
61
62 /* register offsets and sizes: */
63 #define TME_SUN_MIE_REG_PGMAP (0)
64 #define TME_SUN_MIE_SIZ_PGMAP (TME_SUN_MIE_PGMAP_COUNT * sizeof(tme_uint16_t))
65 #define TME_SUN_MIE_REG_PROM (TME_SUN_MIE_REG_PGMAP + TME_SUN_MIE_SIZ_PGMAP)
66 #define TME_SUN_MIE_SIZ_PROM (32 * sizeof(tme_uint16_t))
67 #define TME_SUN_MIE_REG_CSR (TME_SUN_MIE_REG_PROM + TME_SUN_MIE_SIZ_PROM)
68 #define TME_SUN_MIE_SIZ_CSR (sizeof(tme_uint16_t))
69 #define TME_SUN_MIE_REG_PCR (TME_SUN_MIE_REG_CSR + TME_SUN_MIE_SIZ_CSR + sizeof(tme_uint16_t))
70 #define TME_SUN_MIE_SIZ_PCR (sizeof(tme_uint16_t))
71 #define TME_SUN_MIE_REG_PE_ALO (TME_SUN_MIE_REG_PCR + TME_SUN_MIE_SIZ_PCR)
72 #define TME_SUN_MIE_SIZ_PE_ALO (sizeof(tme_uint16_t))
73 #define TME_SUN_MIE_SIZ_REGS (TME_SUN_MIE_REG_PE_ALO + TME_SUN_MIE_SIZ_PE_ALO)
74
75 /* the bits in a pagemap entry: */
76 #define TME_SUN_MIE_PGMAP_SWAB (0x8000)
77 /* 0x4000 unused */
78 #define TME_SUN_MIE_PGMAP_P2MEM (0x2000)
79 /* 0x1000 unused */
80 #define TME_SUN_MIE_PGMAP_PFNUM (0x0fff)
81
82 /* the bits in the Control/Status Register: */
83 #define TME_SUN_MIE_CSR_RESET (0x8000)
84 #define TME_SUN_MIE_CSR_NOLOOP (0x4000)
85 #define TME_SUN_MIE_CSR_CA (0x2000)
86 #define TME_SUN_MIE_CSR_IE (0x1000)
87 #define TME_SUN_MIE_CSR_PIE (0x0800)
88 /* 0x0400 unused */
89 #define TME_SUN_MIE_CSR_PE (0x0200)
90 #define TME_SUN_MIE_CSR_INTR (0x0100)
91 /* 0x0080 unused */
92 /* 0x0040 unused */
93 #define TME_SUN_MIE_CSR_P2MEM (0x0020)
94 #define TME_SUN_MIE_CSR_BIGRAM (0x0010)
95 #define TME_SUN_MIE_CSR_MPMHI (0x000f)
96 #define TME_SUN_MIE_CSR_READONLY (0x0400 \
97 | TME_SUN_MIE_CSR_PE \
98 | TME_SUN_MIE_CSR_INTR \
99 | 0x0080 \
100 | 0x0040 \
101 | TME_SUN_MIE_CSR_BIGRAM\
102 | TME_SUN_MIE_CSR_MPMHI)
103
104 /* the bits in the Parity Control Register: */
105 #define TME_SUN_MIE_PCR_PEACK (0x0100)
106 #define TME_SUN_MIE_PCR_PESRC (0x0080)
107 #define TME_SUN_MIE_PCR_PEBYTE (0x0040)
108 /* 0x0020 unused */
109 /* 0x0010 unused */
110 #define TME_SUN_MIE_PCR_PE_AHI (0x000f)
111 #define TME_SUN_MIE_PCR_READONLY (~TME_SUN_MIE_PCR_PEACK)
112
113 /* the size of the memory port: */
114 #define TME_SUN_MIE_SIZ_MBM (0x10000)
115
116 /* these get and put the CSR: */
117 #define TME_SUN_MIE_CSR_GET(sun_mie) \
118 tme_betoh_u16(*((tme_uint16_t *) &(sun_mie)->tme_sun_mie_regs[TME_SUN_MIE_REG_CSR]))
119 #define TME_SUN_MIE_CSR_PUT(sun_mie, csr) \
120 (*((tme_uint16_t *) &(sun_mie)->tme_sun_mie_regs[TME_SUN_MIE_REG_CSR]) = tme_htobe_u16(csr))
121
122 /* these get and put the PCR: */
123 #define TME_SUN_MIE_PCR_GET(sun_mie) \
124 tme_betoh_u16(*((tme_uint16_t *) &(sun_mie)->tme_sun_mie_regs[TME_SUN_MIE_REG_PCR]))
125 #define TME_SUN_MIE_PCR_PUT(sun_mie, pcr) \
126 (*((tme_uint16_t *) &(sun_mie)->tme_sun_mie_regs[TME_SUN_MIE_REG_PCR]) = tme_htobe_u16(pcr))
127
128 /* the callout flags: */
129 #define TME_SUN_MIE_CALLOUT_CHECK (0)
130 #define TME_SUN_MIE_CALLOUT_RUNNING TME_BIT(0)
131 #define TME_SUN_MIE_CALLOUTS_MASK (-2)
132 #define TME_SUN_MIE_CALLOUT_SIGNALS TME_BIT(1)
133 #define TME_SUN_MIE_CALLOUT_INT TME_BIT(2)
134
135 /* structures: */
136
137 /* the card: */
138 struct tme_sun_mie {
139
140 /* backpointer to our element: */
141 struct tme_element *tme_sun_mie_element;
142
143 /* the mutex protecting the card: */
144 tme_mutex_t tme_sun_mie_mutex;
145
146 /* the rwlock protecting the card: */
147 tme_rwlock_t tme_sun_mie_rwlock;
148
149 /* the bus connection for the card's registers: */
150 struct tme_bus_connection *tme_sun_mie_conn_regs;
151
152 /* the bus connection for the card's memory: */
153 struct tme_bus_connection *tme_sun_mie_conn_memory;
154
155 /* the bus connection for the card's i825x6 chip: */
156 struct tme_bus_connection *tme_sun_mie_conn_i825x6;
157
158 /* the callout flags: */
159 int tme_sun_mie_callout_flags;
160
161 /* if our interrupt line is currently asserted: */
162 int tme_sun_mie_int_asserted;
163
164 /* it's easiest to just model the board registers as a chunk of memory: */
165 tme_uint8_t tme_sun_mie_regs[TME_SUN_MIE_SIZ_REGS];
166
167 /* the board memory really is a chunk of memory: */
168 tme_uint8_t tme_sun_mie_memory[TME_SUN_MIE_MEMSIZE];
169
170 /* the active TLB entries for each page map entry on the board: */
171 struct tme_token *tme_sun_mie_tlb_tokens[TME_SUN_MIE_PGMAP_COUNT * TME_SUN_MIE_PGMAP_TLBS];
172 unsigned int tme_sun_mie_tlb_head[TME_SUN_MIE_PGMAP_COUNT];
173
174 /* the i825x6 image of the CSR: */
175 tme_uint16_t tme_sun_mie_csr_i825x6;
176
177 #ifndef TME_NO_LOG
178 tme_uint16_t tme_sun_mie_last_log_csr;
179 #endif /* !TME_NO_LOG */
180 };
181
182 /* a sun_mie internal bus connection: */
183 struct tme_sun_mie_connection {
184
185 /* the external bus connection: */
186 struct tme_bus_connection tme_sun_mie_connection;
187
188 /* this is nonzero if a TME_CONNECTION_BUS_GENERIC chip connection
189 is for the registers: */
190 tme_uint8_t tme_sun_mie_connection_regs;
191
192 /* if this connection is for the memory port, this is the high
193 nibble (A19..A16) of that port's connection: */
194 tme_uint8_t tme_sun_mie_connection_mpmhi;
195 };
196
197 /* globals: */
198
199 /* our bus signals sets: */
200 static const struct tme_bus_signals _tme_sun_mie_bus_signals_generic = TME_BUS_SIGNALS_GENERIC;
201 static const struct tme_bus_signals _tme_sun_mie_bus_signals_i825x6 = TME_BUS_SIGNALS_I825X6;
202
203 /* the sun_mie callout function. it must be called with the mutex locked: */
204 static void
_tme_sun_mie_callout(struct tme_sun_mie * sun_mie,int new_callouts)205 _tme_sun_mie_callout(struct tme_sun_mie *sun_mie, int new_callouts)
206 {
207 struct tme_bus_connection *conn_i825x6;
208 struct tme_bus_connection *conn_bus;
209 tme_uint16_t csr, csr_diff;
210 unsigned int signal, level;
211 int callouts, later_callouts;
212 int rc;
213 int int_asserted;
214
215 /* add in any new callouts: */
216 sun_mie->tme_sun_mie_callout_flags |= new_callouts;
217
218 /* if this function is already running in another thread, simply
219 return now. the other thread will do our work: */
220 if (sun_mie->tme_sun_mie_callout_flags & TME_SUN_MIE_CALLOUT_RUNNING) {
221 return;
222 }
223
224 /* callouts are now running: */
225 sun_mie->tme_sun_mie_callout_flags |= TME_SUN_MIE_CALLOUT_RUNNING;
226
227 /* assume that we won't need any later callouts: */
228 later_callouts = 0;
229
230 /* loop while callouts are needed: */
231 for (; (callouts = sun_mie->tme_sun_mie_callout_flags) & TME_SUN_MIE_CALLOUTS_MASK; ) {
232
233 /* clear the needed callouts: */
234 sun_mie->tme_sun_mie_callout_flags = callouts & ~TME_SUN_MIE_CALLOUTS_MASK;
235 callouts &= TME_SUN_MIE_CALLOUTS_MASK;
236
237 /* if we need to call out one or more signals to the i825x6: */
238 if (callouts & TME_SUN_MIE_CALLOUT_SIGNALS) {
239
240 /* get the current CSR value: */
241 csr = TME_SUN_MIE_CSR_GET(sun_mie);
242
243 /* get the next signal to call out to the i825x6: */
244 csr_diff = ((csr
245 ^ sun_mie->tme_sun_mie_csr_i825x6)
246 & (TME_SUN_MIE_CSR_RESET
247 | TME_SUN_MIE_CSR_NOLOOP
248 | TME_SUN_MIE_CSR_CA));
249 csr_diff = (csr_diff ^ (csr_diff - 1)) & csr_diff;
250
251 /* if there is a signal to call out: */
252 if (csr_diff != 0) {
253
254 /* assume that if the signal's bit is set in the CSR, it will
255 be asserted: */
256 level = csr & csr_diff;
257
258 /* assume that we're calling out an i825x6 signal: */
259 signal = (_tme_sun_mie_bus_signals_generic.tme_bus_signals_first
260 + TME_BUS_SIGNAL_X(_tme_sun_mie_bus_signals_generic.tme_bus_signals_count));
261
262 /* dispatch on the CSR bit: */
263 switch (csr_diff) {
264 default:
265 assert (FALSE);
266 case TME_SUN_MIE_CSR_RESET:
267 signal = TME_BUS_SIGNAL_RESET;
268 break;
269 case TME_SUN_MIE_CSR_NOLOOP:
270 signal += TME_I825X6_SIGNAL_LOOP;
271 level = !level;
272 break;
273 case TME_SUN_MIE_CSR_CA:
274 signal += TME_I825X6_SIGNAL_CA;
275 break;
276 }
277
278 /* create a real signal level value: */
279 level = (level
280 ? TME_BUS_SIGNAL_LEVEL_ASSERTED
281 : TME_BUS_SIGNAL_LEVEL_NEGATED);
282
283 /* get this card's i825x6 connection: */
284 conn_i825x6 = sun_mie->tme_sun_mie_conn_i825x6;
285
286 /* unlock the mutex: */
287 tme_mutex_unlock(&sun_mie->tme_sun_mie_mutex);
288
289 /* do the callout: */
290 rc = (conn_i825x6 != NULL
291 ? ((*conn_i825x6->tme_bus_signal)
292 (conn_i825x6,
293 signal | level))
294 : TME_OK);
295
296 /* lock the mutex: */
297 tme_mutex_lock(&sun_mie->tme_sun_mie_mutex);
298
299 /* if the callout was unsuccessful, remember that at some later
300 time this callout should be attempted again: */
301 if (rc != TME_OK) {
302 later_callouts |= TME_SUN_MIE_CALLOUT_SIGNALS;
303 }
304
305 /* otherwise, the callout was successful: */
306 else {
307
308 /* update the i825x6 image of the CSR: */
309 sun_mie->tme_sun_mie_csr_i825x6 =
310 ((sun_mie->tme_sun_mie_csr_i825x6 & ~csr_diff)
311 | (csr & csr_diff));
312
313 /* there may be more signals to call out, so attempt this
314 callout again now: */
315 sun_mie->tme_sun_mie_callout_flags |= TME_SUN_MIE_CALLOUT_SIGNALS;
316 }
317 }
318 }
319
320 /* if we need to call out a possible change to our interrupt
321 signal: */
322 if (callouts & TME_SUN_MIE_CALLOUT_INT) {
323
324 /* get the current CSR value: */
325 csr = TME_SUN_MIE_CSR_GET(sun_mie);
326
327 /* see if the interrupt signal should be asserted or negated: */
328 int_asserted = ((csr & (TME_SUN_MIE_CSR_IE
329 | TME_SUN_MIE_CSR_INTR))
330 == (TME_SUN_MIE_CSR_IE
331 | TME_SUN_MIE_CSR_INTR));
332
333 /* if the interrupt signal doesn't already have the right state: */
334 if (!int_asserted != !sun_mie->tme_sun_mie_int_asserted) {
335
336 /* get our bus connection: */
337 conn_bus = sun_mie->tme_sun_mie_conn_regs;
338
339 /* unlock our mutex: */
340 tme_mutex_unlock(&sun_mie->tme_sun_mie_mutex);
341
342 /* call out the bus interrupt signal edge: */
343 rc = (conn_bus != NULL
344 ? ((*conn_bus->tme_bus_signal)
345 (conn_bus,
346 TME_BUS_SIGNAL_INT_UNSPEC
347 | (int_asserted
348 ? TME_BUS_SIGNAL_LEVEL_ASSERTED
349 : TME_BUS_SIGNAL_LEVEL_NEGATED)))
350 : TME_OK);
351
352 /* lock our mutex: */
353 tme_mutex_lock(&sun_mie->tme_sun_mie_mutex);
354
355 /* if this callout was successful, note the new state of the
356 interrupt signal: */
357 if (rc == TME_OK) {
358 sun_mie->tme_sun_mie_int_asserted = int_asserted;
359 }
360
361 /* otherwise, remember that at some later time this callout
362 should be attempted again: */
363 else {
364 later_callouts |= TME_SUN_MIE_CALLOUT_INT;
365 }
366 }
367 }
368 }
369
370 /* put in any later callouts, and clear that callouts are running: */
371 sun_mie->tme_sun_mie_callout_flags = later_callouts;
372 }
373
374 /* the sun_mie bus cycle handler for the board memory: */
375 static int
_tme_sun_mie_bus_cycle(void * _sun_mie,struct tme_bus_cycle * cycle_init)376 _tme_sun_mie_bus_cycle(void *_sun_mie,
377 struct tme_bus_cycle *cycle_init)
378 {
379 struct tme_sun_mie *sun_mie;
380
381 /* recover our data structure: */
382 sun_mie = (struct tme_sun_mie *) _sun_mie;
383
384 /* run the cycle: */
385 tme_bus_cycle_xfer_memory(cycle_init,
386 sun_mie->tme_sun_mie_memory,
387 TME_SUN_MIE_MEMSIZE - 1);
388
389 /* no faults: */
390 return (TME_OK);
391 }
392
393 /* the sun_mie bus cycle handler for the board registers: */
394 static int
_tme_sun_mie_bus_cycle_regs(void * _sun_mie,struct tme_bus_cycle * cycle_init)395 _tme_sun_mie_bus_cycle_regs(void *_sun_mie,
396 struct tme_bus_cycle *cycle_init)
397 {
398 struct tme_sun_mie *sun_mie;
399 unsigned int pgmap_i;
400 unsigned int pgmap_j;
401 unsigned int tlb_i;
402 unsigned int tlb_j;
403 tme_uint16_t csr_old, csr_new, csr_diff;
404 tme_uint16_t pcr_old, pcr_new, pcr_diff;
405 int new_callouts;
406
407 /* recover our data structure: */
408 sun_mie = (struct tme_sun_mie *) _sun_mie;
409
410 /* assume we won't need any new callouts: */
411 new_callouts = 0;
412
413 /* lock the mutex: */
414 tme_mutex_lock(&sun_mie->tme_sun_mie_mutex);
415
416 /* if this is a write cycle and the address falls within one or more
417 page map entries, invalidate the TLBs associated with those
418 entries: */
419 if ((cycle_init->tme_bus_cycle_type
420 & TME_BUS_CYCLE_WRITE)
421 && (TME_SUN_MIE_REG_PGMAP
422 <= cycle_init->tme_bus_cycle_address)
423 && (cycle_init->tme_bus_cycle_address
424 < (TME_SUN_MIE_REG_PGMAP
425 + TME_SUN_MIE_SIZ_PGMAP))) {
426
427 /* get the range of page map entries: */
428 pgmap_i
429 = (cycle_init->tme_bus_cycle_address
430 / sizeof(tme_uint16_t));
431 pgmap_j
432 = ((cycle_init->tme_bus_cycle_address
433 + cycle_init->tme_bus_cycle_size
434 + sizeof(tme_uint16_t)
435 - 1)
436 / sizeof(tme_uint16_t));
437 pgmap_j = TME_MIN(pgmap_j, TME_SUN_MIE_PGMAP_COUNT);
438
439 /* get the range of TLB handles: */
440 tlb_i = pgmap_i * TME_SUN_MIE_PGMAP_TLBS;
441 tlb_j = pgmap_j * TME_SUN_MIE_PGMAP_TLBS;
442
443 /* invalidate the TLB entries: */
444 for (; tlb_i < tlb_j; tlb_i++) {
445 if (sun_mie->tme_sun_mie_tlb_tokens[tlb_i] != NULL) {
446 tme_token_invalidate(sun_mie->tme_sun_mie_tlb_tokens[tlb_i]);
447 sun_mie->tme_sun_mie_tlb_tokens[tlb_i] = NULL;
448 }
449 }
450 }
451
452 /* get the previous CSR and PCR values: */
453 csr_old = TME_SUN_MIE_CSR_GET(sun_mie);
454 pcr_old = TME_SUN_MIE_PCR_GET(sun_mie);
455
456 /* unless this address falls within the PROM, run the cycle: */
457 if ((cycle_init->tme_bus_cycle_address
458 < TME_SUN_MIE_REG_PROM)
459 || (cycle_init->tme_bus_cycle_address
460 >= (TME_SUN_MIE_REG_PROM
461 + TME_SUN_MIE_SIZ_PROM))) {
462 tme_bus_cycle_xfer_memory(cycle_init,
463 sun_mie->tme_sun_mie_regs,
464 TME_SUN_MIE_SIZ_REGS - 1);
465 }
466
467 /* get the current CSR and PCR values, and put back any bits that
468 software can't change: */
469 csr_new = ((TME_SUN_MIE_CSR_GET(sun_mie)
470 & ~TME_SUN_MIE_CSR_READONLY)
471 | (csr_old
472 & TME_SUN_MIE_CSR_READONLY));
473 TME_SUN_MIE_CSR_PUT(sun_mie, csr_new);
474 pcr_new = ((TME_SUN_MIE_PCR_GET(sun_mie)
475 & ~TME_SUN_MIE_PCR_READONLY)
476 | (pcr_old
477 & TME_SUN_MIE_PCR_READONLY));
478 TME_SUN_MIE_PCR_PUT(sun_mie, pcr_new);
479
480 /* get the sets of CSR and PCR bits that have changed: */
481 csr_diff = (csr_old ^ csr_new);
482 pcr_diff = (pcr_old ^ pcr_new);
483
484 /* if this is a RESET, NOLOOP or CA change, possibly call out the
485 appropriate signal change to the i825x6: */
486 if (csr_diff & (TME_SUN_MIE_CSR_RESET
487 | TME_SUN_MIE_CSR_NOLOOP
488 | TME_SUN_MIE_CSR_CA)) {
489 new_callouts |= TME_SUN_MIE_CALLOUT_SIGNALS;
490 }
491
492 /* if this is an interrupt mask change, possibly call out an
493 interrupt signal change to the bus: */
494 if (csr_diff & TME_SUN_MIE_CSR_IE) {
495 new_callouts |= TME_SUN_MIE_CALLOUT_INT;
496 }
497
498 /* abort on any attempt to enable the P2 bus: */
499 if (csr_new & TME_SUN_MIE_CSR_P2MEM) {
500 abort ();
501 }
502
503 /* if this is acknowledging a parity error: */
504 if (pcr_diff & TME_SUN_MIE_PCR_PEACK) {
505 /* nothing to do */
506 }
507
508 #ifndef TME_NO_LOG
509 if (csr_new != sun_mie->tme_sun_mie_last_log_csr) {
510 sun_mie->tme_sun_mie_last_log_csr = csr_new;
511 tme_log(&sun_mie->tme_sun_mie_element->tme_element_log_handle,
512 1000, TME_OK,
513 (&sun_mie->tme_sun_mie_element->tme_element_log_handle,
514 "csr now 0x%04x",
515 csr_new));
516 }
517 #endif /* !TME_NO_LOG */
518
519 /* make any new callouts: */
520 _tme_sun_mie_callout(sun_mie, new_callouts);
521
522 /* unlock the mutex: */
523 tme_mutex_unlock(&sun_mie->tme_sun_mie_mutex);
524
525 /* no faults: */
526 return (TME_OK);
527 }
528
529 /* the sun_mie bus signal handler: */
530 static int
_tme_sun_mie_bus_signal(struct tme_bus_connection * conn_bus,unsigned int signal)531 _tme_sun_mie_bus_signal(struct tme_bus_connection *conn_bus,
532 unsigned int signal)
533 {
534 struct tme_sun_mie *sun_mie;
535
536 /* return now if this is not a generic bus signal: */
537 if (TME_BUS_SIGNAL_INDEX(signal)
538 > _tme_sun_mie_bus_signals_generic.tme_bus_signals_count) {
539 return (TME_OK);
540 }
541
542 /* recover our data structures: */
543 sun_mie = conn_bus->tme_bus_connection.tme_connection_element->tme_element_private;
544
545 /* since this function is currently only given to the i825x6,
546 just copy its signal through to the Multibus: */
547 conn_bus = sun_mie->tme_sun_mie_conn_regs;
548 return ((*conn_bus->tme_bus_signal)(conn_bus, signal));
549 }
550
551 /* the sun_mie bus signals adder for the i825x6: */
552 static int
_tme_sun_mie_bus_signals_add(struct tme_bus_connection * conn_bus,struct tme_bus_signals * bus_signals_caller)553 _tme_sun_mie_bus_signals_add(struct tme_bus_connection *conn_bus,
554 struct tme_bus_signals *bus_signals_caller)
555 {
556 const struct tme_bus_signals *bus_signals;
557 tme_uint32_t signal_first;
558
559 /* we only support the generic and i825x6 bus signals: */
560 switch (bus_signals_caller->tme_bus_signals_id) {
561 case TME_BUS_SIGNALS_ID_GENERIC:
562 bus_signals = &_tme_sun_mie_bus_signals_generic;
563 signal_first = _tme_sun_mie_bus_signals_generic.tme_bus_signals_first;
564 break;
565 case TME_BUS_SIGNALS_ID_I825X6:
566 bus_signals = &_tme_sun_mie_bus_signals_i825x6;
567 signal_first = (_tme_sun_mie_bus_signals_generic.tme_bus_signals_first
568 + TME_BUS_SIGNAL_X(_tme_sun_mie_bus_signals_generic.tme_bus_signals_count));
569 break;
570 default:
571 return (ENOENT);
572 }
573
574 /* XXX we should check versions here: */
575 *bus_signals_caller = *bus_signals;
576 bus_signals_caller->tme_bus_signals_first = signal_first;
577 return (TME_OK);
578 }
579
580 /* the sun_mie TLB adder for the i825x6: */
581 static int
_tme_sun_mie_tlb_set_add(struct tme_bus_connection * conn_bus,struct tme_bus_tlb_set_info * tlb_set_info)582 _tme_sun_mie_tlb_set_add(struct tme_bus_connection *conn_bus,
583 struct tme_bus_tlb_set_info *tlb_set_info)
584 {
585
586 /* if this TLB set provides a bus context register: */
587 if (tlb_set_info->tme_bus_tlb_set_info_bus_context != NULL) {
588
589 /* this bus only has one context: */
590 (*tlb_set_info->tme_bus_tlb_set_info_bus_context) = 0;
591 tlb_set_info->tme_bus_tlb_set_info_bus_context_max = 0;
592 }
593
594 return (TME_OK);
595 }
596
597 /* the sun_mie TLB filler for the board memory: */
598 static int
_tme_sun_mie_tlb_fill(struct tme_bus_connection * conn_bus,struct tme_bus_tlb * tlb,tme_bus_addr_t address_wider,unsigned int cycles)599 _tme_sun_mie_tlb_fill(struct tme_bus_connection *conn_bus,
600 struct tme_bus_tlb *tlb,
601 tme_bus_addr_t address_wider,
602 unsigned int cycles)
603 {
604 struct tme_sun_mie *sun_mie;
605 tme_bus_addr32_t address;
606 unsigned int pgmap_i;
607 unsigned int tlb_i;
608 tme_uint16_t pgmap;
609
610 /* recover our data structures: */
611 sun_mie = conn_bus->tme_bus_connection.tme_connection_element->tme_element_private;
612
613 /* get the normal-width address: */
614 address = address_wider;
615 assert (address == address_wider);
616
617 /* the address must be within range: */
618 assert(address <= 0xffffff);
619
620 /* mask the address with the board page size: */
621 address &= -TME_SUN_MIE_PAGESIZE;
622
623 /* lock our mutex: */
624 tme_mutex_lock(&sun_mie->tme_sun_mie_mutex);
625
626 /* get the pagemap entry: */
627 pgmap_i = (address / TME_SUN_MIE_PAGESIZE) & (TME_SUN_MIE_PGMAP_COUNT - 1);
628 pgmap = tme_betoh_u16(((tme_uint16_t *) &sun_mie->tme_sun_mie_regs[TME_SUN_MIE_REG_PGMAP])[pgmap_i]);
629
630 /* update the head pointer for this page map entry's active TLB
631 entry list: */
632 tlb_i = sun_mie->tme_sun_mie_tlb_head[pgmap_i];
633 if (++tlb_i == TME_SUN_MIE_PGMAP_TLBS) {
634 tlb_i = 0;
635 }
636 sun_mie->tme_sun_mie_tlb_head[pgmap_i] = tlb_i;
637 tlb_i += pgmap_i * TME_SUN_MIE_PGMAP_TLBS;
638
639 /* if the new head pointer already has a TLB entry, and it doesn't
640 happen to be the same one that we're filling now, invalidate it: */
641 if (sun_mie->tme_sun_mie_tlb_tokens[tlb_i] != NULL
642 && sun_mie->tme_sun_mie_tlb_tokens[tlb_i] != tlb->tme_bus_tlb_token) {
643 tme_token_invalidate(sun_mie->tme_sun_mie_tlb_tokens[tlb_i]);
644 }
645
646 /* initialize the TLB entry: */
647 tme_bus_tlb_initialize(tlb);
648
649 /* this TLB entry covers this range: */
650 tlb->tme_bus_tlb_addr_first = address;
651 tlb->tme_bus_tlb_addr_last = address | (TME_SUN_MIE_PAGESIZE - 1);
652
653 /* allow reading and writing: */
654 tlb->tme_bus_tlb_cycles_ok = TME_BUS_CYCLE_READ | TME_BUS_CYCLE_WRITE;
655
656 /* our bus cycle handler: */
657 tlb->tme_bus_tlb_cycle_private = sun_mie;
658 tlb->tme_bus_tlb_cycle = _tme_sun_mie_bus_cycle;
659
660 /* this TLB entry allows fast reading and writing: */
661 tlb->tme_bus_tlb_emulator_off_write =
662 (&sun_mie->tme_sun_mie_memory[((pgmap & TME_SUN_MIE_PGMAP_PFNUM)
663 * TME_SUN_MIE_PAGESIZE)]
664 - address);
665 tlb->tme_bus_tlb_emulator_off_read =
666 tlb->tme_bus_tlb_emulator_off_write;
667
668 /* add this TLB entry to the active list: */
669 sun_mie->tme_sun_mie_tlb_tokens[tlb_i] =
670 tlb->tme_bus_tlb_token;
671
672 /* unlock our mutex: */
673 tme_mutex_unlock(&sun_mie->tme_sun_mie_mutex);
674
675 return (TME_OK);
676 }
677
678 /* the sun_mie TLB filler for the board registers: */
679 static int
_tme_sun_mie_tlb_fill_regs(struct tme_bus_connection * conn_bus,struct tme_bus_tlb * tlb,tme_bus_addr_t address_wider,unsigned int cycles)680 _tme_sun_mie_tlb_fill_regs(struct tme_bus_connection *conn_bus,
681 struct tme_bus_tlb *tlb,
682 tme_bus_addr_t address_wider,
683 unsigned int cycles)
684 {
685 struct tme_sun_mie *sun_mie;
686 tme_bus_addr32_t address;
687
688 /* recover our data structures: */
689 sun_mie = conn_bus->tme_bus_connection.tme_connection_element->tme_element_private;
690
691 /* get the normal-width address: */
692 address = address_wider;
693 assert (address == address_wider);
694
695 /* the address must be within range: */
696 assert(address < TME_SUN_MIE_SIZ_REGS);
697
698 /* initialize the TLB entry: */
699 tme_bus_tlb_initialize(tlb);
700
701 /* if the address falls in the page map: */
702 if (TME_SUN_MIE_REG_PGMAP <= address
703 && address < (TME_SUN_MIE_REG_PGMAP
704 + TME_SUN_MIE_SIZ_PGMAP)) {
705
706 /* this TLB entry covers this range: */
707 tlb->tme_bus_tlb_addr_first = TME_SUN_MIE_REG_PGMAP;
708 tlb->tme_bus_tlb_addr_last = (TME_SUN_MIE_REG_PGMAP + TME_SUN_MIE_SIZ_PGMAP - 1);
709 }
710
711 /* if this address falls in the PROM: */
712 else if (TME_SUN_MIE_REG_PROM <= address
713 && address < (TME_SUN_MIE_REG_PROM
714 + TME_SUN_MIE_SIZ_PROM)) {
715
716
717 /* this TLB entry covers this range: */
718 tlb->tme_bus_tlb_addr_first = TME_SUN_MIE_REG_PROM;
719 tlb->tme_bus_tlb_addr_last = TME_SUN_MIE_REG_PROM + TME_SUN_MIE_SIZ_PROM - 1;
720 }
721
722 /* if this address falls in the CSR: */
723 else if (TME_SUN_MIE_REG_CSR <= address
724 && address < (TME_SUN_MIE_REG_CSR
725 + TME_SUN_MIE_SIZ_CSR)) {
726
727
728 /* this TLB entry covers this range: */
729 tlb->tme_bus_tlb_addr_first = TME_SUN_MIE_REG_CSR;
730 tlb->tme_bus_tlb_addr_last = TME_SUN_MIE_REG_CSR + TME_SUN_MIE_SIZ_CSR - 1;
731 }
732
733 /* otherwise, this address must fall in the unused hole or in the
734 parity registers: */
735 else {
736 assert (address >= (TME_SUN_MIE_REG_CSR
737 + TME_SUN_MIE_SIZ_CSR));
738
739 /* this TLB entry covers this range: */
740 tlb->tme_bus_tlb_addr_first = (TME_SUN_MIE_REG_CSR + TME_SUN_MIE_SIZ_CSR);
741 tlb->tme_bus_tlb_addr_last = (TME_SUN_MIE_REG_PE_ALO + TME_SUN_MIE_SIZ_PE_ALO - 1);
742 }
743
744 /* all address ranges allow fast reading: */
745 tlb->tme_bus_tlb_emulator_off_read = &sun_mie->tme_sun_mie_regs[0];
746 tlb->tme_bus_tlb_rwlock = &sun_mie->tme_sun_mie_rwlock;
747
748 /* allow reading and writing: */
749 tlb->tme_bus_tlb_cycles_ok = TME_BUS_CYCLE_READ | TME_BUS_CYCLE_WRITE;
750
751 /* our bus cycle handler: */
752 tlb->tme_bus_tlb_cycle_private = sun_mie;
753 tlb->tme_bus_tlb_cycle = _tme_sun_mie_bus_cycle_regs;
754
755 return (TME_OK);
756 }
757
758 /* this scores a new connection: */
759 static int
_tme_sun_mie_connection_score(struct tme_connection * conn,unsigned int * _score)760 _tme_sun_mie_connection_score(struct tme_connection *conn, unsigned int *_score)
761 {
762 struct tme_sun_mie *sun_mie;
763 struct tme_sun_mie_connection *conn_sun_mie;
764
765 /* both sides must be generic bus connections: */
766 assert(conn->tme_connection_type == TME_CONNECTION_BUS_GENERIC);
767 assert(conn->tme_connection_other->tme_connection_type
768 == conn->tme_connection_type);
769
770 /* recover our data structures: */
771 sun_mie = conn->tme_connection_element->tme_element_private;
772 conn_sun_mie = (struct tme_sun_mie_connection *)conn;
773
774 /* this is a generic bus connection, so just score it nonzero and
775 return. note that there's no good way to differentiate a
776 connection to a bus from a connection to just another chip, so we
777 always return a nonzero score here: */
778 *_score = 1;
779 return (TME_OK);
780 }
781
782 /* this makes a new connection: */
783 static int
_tme_sun_mie_connection_make(struct tme_connection * conn,unsigned int state)784 _tme_sun_mie_connection_make(struct tme_connection *conn, unsigned int state)
785 {
786 struct tme_sun_mie *sun_mie;
787 struct tme_sun_mie_connection *conn_sun_mie;
788 struct tme_bus_connection *conn_bus;
789 tme_uint16_t csr;
790
791 /* both sides must be generic bus connections: */
792 assert(conn->tme_connection_type == TME_CONNECTION_BUS_GENERIC);
793 assert(conn->tme_connection_other->tme_connection_type
794 == conn->tme_connection_type);
795
796 /* recover our data structures: */
797 sun_mie = conn->tme_connection_element->tme_element_private;
798 conn_sun_mie = (struct tme_sun_mie_connection *)conn;
799 conn_bus = &conn_sun_mie->tme_sun_mie_connection;
800
801 /* we're always set up to answer calls across the connection, so we
802 only have to do work when the connection has gone full, namely
803 taking the other side of the connection: */
804 if (state == TME_CONNECTION_FULL) {
805
806 /* lock our mutex: */
807 tme_mutex_lock(&sun_mie->tme_sun_mie_mutex);
808
809 /* save our connection: */
810 if (conn_bus->tme_bus_signals_add != NULL) {
811 sun_mie->tme_sun_mie_conn_i825x6 = (struct tme_bus_connection *) conn->tme_connection_other;
812 }
813 else if (conn_sun_mie->tme_sun_mie_connection_regs) {
814 sun_mie->tme_sun_mie_conn_regs = (struct tme_bus_connection *) conn->tme_connection_other;
815 }
816 else {
817 sun_mie->tme_sun_mie_conn_memory = (struct tme_bus_connection *) conn->tme_connection_other;
818 csr = TME_SUN_MIE_CSR_GET(sun_mie);
819 csr &= ~TME_SUN_MIE_CSR_MPMHI;
820 csr |= conn_sun_mie->tme_sun_mie_connection_mpmhi;
821 TME_SUN_MIE_CSR_PUT(sun_mie, csr);
822 }
823
824 /* unlock our mutex: */
825 tme_mutex_unlock(&sun_mie->tme_sun_mie_mutex);
826 }
827
828 return (TME_OK);
829 }
830
831 /* this breaks a connection: */
832 static int
_tme_sun_mie_connection_break(struct tme_connection * conn,unsigned int state)833 _tme_sun_mie_connection_break(struct tme_connection *conn, unsigned int state)
834 {
835 abort();
836 }
837
838 /* this makes a new connection side for a sun_mie: */
839 static int
_tme_sun_mie_connections_new(struct tme_element * element,const char * const * args,struct tme_connection ** _conns,char ** _output)840 _tme_sun_mie_connections_new(struct tme_element *element,
841 const char * const *args,
842 struct tme_connection **_conns,
843 char **_output)
844 {
845 struct tme_sun_mie *sun_mie;
846 struct tme_sun_mie_connection *conn_sun_mie;
847 struct tme_bus_connection *conn_bus;
848 struct tme_connection *conn;
849 unsigned int i825x6;
850 tme_uint8_t regs;
851 tme_bus_addr_t mpmhi;
852 int usage;
853 int rc;
854
855 /* recover our data structure: */
856 sun_mie = (struct tme_sun_mie *) element->tme_element_private;
857
858 /* we don't bother locking the mutex simply to check if connections
859 already exist: */
860
861 /* check our arguments: */
862 usage = FALSE;
863 rc = 0;
864 i825x6 = FALSE;
865 regs = FALSE;
866 mpmhi = 0;
867
868 /* if this connection is for the registers: */
869 if (TME_ARG_IS(args[1], "csr")) {
870
871 /* if we already have a register connection, complain: */
872 if (sun_mie->tme_sun_mie_conn_regs != NULL) {
873 rc = EEXIST;
874 }
875
876 /* otherwise, make the new connection: */
877 else {
878 regs = TRUE;
879 }
880 }
881
882 /* else, if this connection is for the memory: */
883 else if (TME_ARG_IS(args[1], "memory")) {
884
885 /* if we already have a memory connection, complain: */
886 if (sun_mie->tme_sun_mie_conn_memory != NULL) {
887 rc = EEXIST;
888 }
889
890 /* otherwise, check the value after "memory". it must be the low
891 20 bits of the bus address of the board's memory; in our csr we
892 have to report the most significant nibble (A19-A16, as A15..A0
893 are expected to be zero) to software so it can find that
894 memory: */
895 else {
896 mpmhi = tme_bus_addr_parse_any(args[2], &usage);
897 if (!usage
898 && ((mpmhi > 0xfffff)
899 || (mpmhi & (TME_SUN_MIE_SIZ_MBM - 1)))) {
900 tme_output_append_error(_output,
901 "%s %s, ",
902 args[2],
903 _(" is not a 20-bit address with A15..A0 zero"));
904 usage = TRUE;
905 }
906 }
907 }
908
909 /* else, the connection must be for the i825x6: */
910 else if (args[1] == NULL) {
911
912 /* if we already have an i825x6 connection, complain: */
913 if (sun_mie->tme_sun_mie_conn_i825x6 != NULL) {
914 rc = EEXIST;
915 }
916
917 /* otherwise, make the new conection: */
918 else {
919 i825x6 = TRUE;
920 }
921 }
922
923 /* otherwise, this is a bad argument: */
924 else {
925 tme_output_append_error(_output,
926 "%s %s, ",
927 args[1],
928 _("unexpected"));
929 usage = TRUE;
930 }
931
932 if (usage) {
933 tme_output_append_error(_output,
934 "%s %s [ csr | memory %s ]",
935 _("usage:"),
936 args[0],
937 _("BUS-ADDRESS"));
938 rc = EINVAL;
939 }
940
941 if (rc) {
942 return (rc);
943 }
944
945 /* make a new connection: */
946 conn_sun_mie = tme_new0(struct tme_sun_mie_connection, 1);
947 conn_bus = &conn_sun_mie->tme_sun_mie_connection;
948 conn = &conn_bus->tme_bus_connection;
949
950 /* fill in the generic connection: */
951 conn->tme_connection_next = *_conns;
952 conn->tme_connection_type = TME_CONNECTION_BUS_GENERIC;
953 conn->tme_connection_score = _tme_sun_mie_connection_score;
954 conn->tme_connection_make = _tme_sun_mie_connection_make;
955 conn->tme_connection_break = _tme_sun_mie_connection_break;
956
957 /* fill in the generic bus connection: */
958 conn_bus->tme_bus_subregions.tme_bus_subregion_address_first = 0;
959 conn_bus->tme_bus_subregions.tme_bus_subregion_next = NULL;
960 if (i825x6) {
961 conn_bus->tme_bus_subregions.tme_bus_subregion_address_last = 0xffffff;
962 conn_bus->tme_bus_signals_add = _tme_sun_mie_bus_signals_add;
963 conn_bus->tme_bus_signal = _tme_sun_mie_bus_signal;
964 conn_bus->tme_bus_tlb_set_add = _tme_sun_mie_tlb_set_add;
965 conn_bus->tme_bus_tlb_fill = _tme_sun_mie_tlb_fill;
966 }
967 else if (regs) {
968 conn_bus->tme_bus_subregions.tme_bus_subregion_address_last = TME_SUN_MIE_SIZ_REGS - 1;
969 conn_bus->tme_bus_tlb_fill = _tme_sun_mie_tlb_fill_regs;
970 }
971 else {
972 conn_bus->tme_bus_subregions.tme_bus_subregion_address_last = TME_SUN_MIE_SIZ_MBM - 1;
973 conn_bus->tme_bus_tlb_fill = _tme_sun_mie_tlb_fill;
974 }
975
976 /* fill in the internal information: */
977 conn_sun_mie->tme_sun_mie_connection_regs = regs;
978 conn_sun_mie->tme_sun_mie_connection_mpmhi = (mpmhi >> 16);
979
980 /* return the connection side possibility: */
981 *_conns = conn;
982 return (TME_OK);
983 }
984
985 /* the new sun_mie function: */
TME_ELEMENT_SUB_NEW_DECL(tme_bus_multibus,sun_mie)986 TME_ELEMENT_SUB_NEW_DECL(tme_bus_multibus,sun_mie) {
987 struct tme_sun_mie *sun_mie;
988 int arg_i;
989 int usage;
990
991 /* check our arguments: */
992 usage = 0;
993 arg_i = 1;
994 for (;;) {
995
996 if (0) {
997 }
998
999 /* if we ran out of arguments: */
1000 else if (args[arg_i] == NULL) {
1001
1002 break;
1003 }
1004
1005 /* otherwise this is a bad argument: */
1006 else {
1007 tme_output_append_error(_output,
1008 "%s %s, ",
1009 args[arg_i],
1010 _("unexpected"));
1011 usage = TRUE;
1012 break;
1013 }
1014 }
1015
1016 if (usage) {
1017 tme_output_append_error(_output,
1018 "%s %s",
1019 _("usage:"),
1020 args[0]);
1021 return (EINVAL);
1022 }
1023
1024 /* start the sun_mie structure: */
1025 sun_mie = tme_new0(struct tme_sun_mie, 1);
1026 sun_mie->tme_sun_mie_element = element;
1027 TME_SUN_MIE_CSR_PUT(sun_mie,
1028 (TME_SUN_MIE_CSR_NOLOOP
1029 | TME_SUN_MIE_CSR_BIGRAM));
1030 tme_mutex_init(&sun_mie->tme_sun_mie_mutex);
1031 tme_rwlock_init(&sun_mie->tme_sun_mie_rwlock);
1032
1033 /* fill the element: */
1034 element->tme_element_private = sun_mie;
1035 element->tme_element_connections_new = _tme_sun_mie_connections_new;
1036
1037 return (TME_OK);
1038 }
1039