|
|
1.1 root 1: /* $Id: stp222x-mdu.c,v 1.5 2010/06/05 18:59:29 fredette Exp $ */
2:
3: /* ic/stp222x-mdu.c - emulation of the Mondo Dispatch Unit of the UPA
4: to SBus interface controller (STP2220) and the UPA to PCI interface
5: controller (STP2222): */
6:
7: /*
8: * Copyright (c) 2009 Matt Fredette
9: * All rights reserved.
10: *
11: * Redistribution and use in source and binary forms, with or without
12: * modification, are permitted provided that the following conditions
13: * are met:
14: * 1. Redistributions of source code must retain the above copyright
15: * notice, this list of conditions and the following disclaimer.
16: * 2. Redistributions in binary form must reproduce the above copyright
17: * notice, this list of conditions and the following disclaimer in the
18: * documentation and/or other materials provided with the distribution.
19: * 3. All advertising materials mentioning features or use of this software
20: * must display the following acknowledgement:
21: * This product includes software developed by Matt Fredette.
22: * 4. The name of the author may not be used to endorse or promote products
23: * derived from this software without specific prior written permission.
24: *
25: * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
26: * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
27: * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
28: * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
29: * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
30: * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
31: * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
32: * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
33: * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
34: * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
35: * POSSIBILITY OF SUCH DAMAGE.
36: */
37:
38: #include <tme/common.h>
39: _TME_RCSID("$Id: stp222x-mdu.c,v 1.5 2010/06/05 18:59:29 fredette Exp $");
40:
41: /* includes: */
42: #include "stp222x-impl.h"
43:
44: /* macros: */
45:
46: /* an interrupt mapping register: */
47: #define TME_STP222X_MDU_IMR_INR ((2 << 10) - (1 << 0))
48: #define TME_STP222X_MDU_IMR_TID ((2 << 30) - (tme_uint32_t) (1 << 26))
49: #define TME_STP222X_MDU_IMR_V TME_BIT(31)
50:
51: /* interrupt states: */
52: #define TME_STP222X_MDU_STATE_IDLE (0)
53: #define TME_STP222X_MDU_STATE_RECEIVED (1)
54: #define TME_STP222X_MDU_STATE_PENDING (3)
55:
56: /* the interrupt retry timer register: */
57: #define TME_STP222X_MDU_RETRY_TIMER_LIMIT ((2 << 19) - (1 << 0))
58:
59: /* dispatch states: */
60: #define TME_STP222X_MDU_DISPATCH_NOW (0)
61: #define TME_STP222X_MDU_DISPATCH_RETRY(n) (1 + (n))
62:
63: /* priorities: */
64: #define TME_STP222X_MDU_PRIORITY_NULL (9)
65:
66: /* this updates an IDI's bit in the received or pending sets: */
67: #define TME_STP222X_MDU_IDIS_UPDATE(field, op, idi) \
68: do { \
69: field[(idi) \
70: / (sizeof(tme_stp222x_idis_t) * 8)] \
71: op (((tme_stp222x_idis_t) 1) \
72: << ((idi) \
73: % (sizeof(tme_stp222x_idis_t) * 8))); \
74: } while (/* CONSTCOND */ 0)
75:
76: /* this tests an IDI's bit in an IDI set: */
77: #define TME_STP222X_MDU_IDI_TEST(field, idi) \
78: (field[(idi) \
79: / (sizeof(tme_stp222x_idis_t) * 8)] \
80: & (((tme_stp222x_idis_t) 1) \
81: << ((idi) \
82: % (sizeof(tme_stp222x_idis_t) * 8))))
83:
84: /* globals: */
85:
86: /* the stp2220 obio interrupt priorities: */
87: static const tme_uint8_t _tme_stp2220_mdu_idi_obio_priority[] = {
88: /* TME_STP222X_IDI_SCSI */ 3,
89: /* TME_STP222X_IDI_ETHER */ 3,
90: /* TME_STP222X_IDI_BPP */ 2,
91: /* TME_STP2220_IDI_AUDIO */ 8,
92: /* TME_STP2220_IDI_POWER */ 8,
93: /* TME_STP2220_IDI_ZS0_ZS1 */ 7,
94: /* TME_STP2220_IDI_FD */ 8,
95: /* TME_STP2220_IDI_THERM */ 8,
96: /* TME_STP2220_IDI_KBD */ 4,
97: /* TME_STP2220_IDI_MOUSE */ 4,
98: /* TME_STP2220_IDI_SERIAL */ 7,
99: /* TME_STP2220_IDI_TIMER(0) */ 6,
100: /* TME_STP2220_IDI_TIMER(1) */ 6,
101: /* TME_STP2220_IDI_UE */ 8,
102: /* TME_STP2220_IDI_CE */ 8,
103: /* TME_STP2220_IDI_SBUS_ASYNC */ 8,
104: /* TME_STP2220_IDI_POWER_MANAGE */ 1,
105: /* TME_STP2220_IDI_UPA */ 5,
106: /* TME_STP2220_IDI_RESERVED */ 5,
107: };
108:
109: /* this maps a register group index into an IDI for an obio
110: interrupt: */
111: static tme_uint32_t
112: _tme_stp222x_reggroup_index_to_obio_idi(struct tme_stp222x *stp222x,
113: tme_uint32_t reggroup_index)
114: {
115: tme_uint32_t idi;
116:
117: /* assume that the register group index maps directly to IDI: */
118: idi = TME_STP222X_IDI0_OBIO + reggroup_index;
119:
120: /* if this is an stp2220: */
121: if (TME_STP222X_IS_2220(stp222x)) {
122:
123: /* there is a one-register gap before the first timer interrupt: */
124: if (idi > TME_STP2220_IDI_TIMER(0)) {
125: idi--;
126: }
127: }
128:
129: return (idi);
130: }
131:
132: /* the stp222x interrupt retry thread: */
133: static void
134: _tme_stp222x_mdu_retry_th(void *_stp222x)
135: {
136: struct tme_stp222x *stp222x;
137: struct timeval *sleep;
138: signed long buffer_i;
139: unsigned int dispatch_state;
140:
141: /* recover our data structures: */
142: stp222x = (struct tme_stp222x *) _stp222x;
143:
144: /* enter: */
145: tme_stp22xx_enter(&stp222x->tme_stp222x);
146:
147: /* loop forever: */
148: for (;;) {
149:
150: /* assume that we can block forever: */
151: sleep = NULL;
152:
153: /* loop over the dispatch buffers: */
154: buffer_i = TME_STP222X_MDU_BUFFER_COUNT - 1;
155: do {
156:
157: /* if this dispatch buffer is valid: */
158: if (stp222x->tme_stp222x_mdu_dispatch_imr[buffer_i] != !TME_STP222X_MDU_IMR_V) {
159:
160: /* if this dispatch buffer is retrying: */
161: dispatch_state = stp222x->tme_stp222x_mdu_dispatch_state[buffer_i];
162: if (dispatch_state != TME_STP222X_MDU_DISPATCH_NOW) {
163:
164: /* NB: we sleep three times for each buffer before retrying
165: it. since the sleep time is half of the retry interval,
166: this means that the maximum delay is one a half times the
167: retry interval, which is the expected value of the delay.
168: the minimum delay is exactly the retry interval, and this
169: happens only on the stp2222, when the other buffer needs
170: a retry right at the beginning of one of the sleeps for
171: this buffer.
172:
173: it's impossible for more than one of the three sleeps a
174: buffer does to be interrupted in this way, because there
175: is not more than one other buffer: */
176:
177: /* if this dispatch buffer has slept three times: */
178: if (dispatch_state == TME_STP222X_MDU_DISPATCH_RETRY(3)) {
179:
180: /* it's time to try this buffer again: */
181: dispatch_state = TME_STP222X_MDU_DISPATCH_NOW;
182: }
183:
184: /* otherwise, we need to sleep for this buffer: */
185: else {
186:
187: /* advance the retry state: */
188: dispatch_state++;
189:
190: /* sleep: */
191: sleep = &stp222x->tme_stp222x_mdu_retry_sleep;
192: }
193:
194: /* update this buffer's dispatch state: */
195: stp222x->tme_stp222x_mdu_dispatch_state[buffer_i] = dispatch_state;
196: }
197: }
198: } while (--buffer_i >= 0);
199:
200: /* block: */
201: tme_stp22xx_cond_sleep_yield(&stp222x->tme_stp222x,
202: &stp222x->tme_stp222x_mdu_retry_cond,
203: sleep);
204: }
205: /* NOTREACHED */
206: }
207:
208: /* this decodes and arbitrates interrupts: */
209: static void
210: _tme_stp222x_mdu_decode_arbitrate(struct tme_stp222x *stp222x)
211: {
212: signed long buffer_i;
213: tme_uint32_t chosen_idi[2];
214: tme_uint32_t chosen_imr[2];
215: unsigned int chosen_priority[2];
216: signed long idis_i;
217: tme_stp222x_idis_t idis_arbitrate;
218: tme_uint32_t idi;
219: tme_uint32_t imr;
220: unsigned int priority;
221:
222: /* start with no chosen interrupts for any CPU buffer, but poison
223: any full CPU buffer with an impossibly high priority, to avoid
224: overwriting a dispatching interrupt: */
225: buffer_i = TME_STP222X_MDU_BUFFER_COUNT - 1;
226: do {
227: chosen_idi[buffer_i] = TME_STP222X_IDI_NULL;
228: chosen_imr[buffer_i] = !TME_STP222X_MDU_IMR_V;
229: chosen_priority[buffer_i]
230: = (stp222x->tme_stp222x_mdu_dispatch_imr[buffer_i] == !TME_STP222X_MDU_IMR_V
231: ? 0
232: : TME_STP222X_MDU_PRIORITY_NULL);
233: } while (--buffer_i >= 0);
234:
235: /* loop over sets of IDIs: */
236: idis_i = TME_ARRAY_ELS(stp222x->tme_stp222x_mdu_idis_received) - 1;
237: do {
238:
239: /* get another set of IDIs that have been received, but are not
240: pending: */
241: idis_arbitrate
242: = (stp222x->tme_stp222x_mdu_idis_received[idis_i]
243: & ~stp222x->tme_stp222x_mdu_idis_pending[idis_i]);
244: if (idis_arbitrate != 0) {
245:
246: /* loop over the IDIs in this set: */
247: idi = idis_i * (sizeof(idis_arbitrate) * 8);
248: do {
249: for (; (idis_arbitrate & 1) == 0; idi++, idis_arbitrate >>=1);
250:
251: /* get the IMR for this IDI: */
252: imr = stp222x->tme_stp222x_mdu_imrs[idi];
253:
254: /* if this IMR is valid: */
255: if (imr & TME_STP222X_MDU_IMR_V) {
256:
257: /* if this is an stp2220: */
258: if (TME_STP222X_IS_2220(stp222x)) {
259:
260: /* if this is a card IDI: */
261: if (idi < TME_STP222X_IDI0_OBIO) {
262:
263: /* the SBus priority is our priority: */
264: priority = idi % TME_SBUS_SLOT_INTS;
265: }
266:
267: /* otherwise, this is an obio IDI: */
268: else {
269:
270: /* get the priority for this obio IDI: */
271: assert ((idi - TME_STP222X_IDI0_OBIO)
272: < TME_ARRAY_ELS(_tme_stp2220_mdu_idi_obio_priority));
273: priority = _tme_stp2220_mdu_idi_obio_priority[idi - TME_STP222X_IDI0_OBIO];
274: }
275:
276: /* all stp2220 interrupts use buffer zero: */
277: buffer_i = 0;
278: }
279:
280: /* otherwise, this is an stp2222: */
281: else {
282: abort();
283: }
284:
285: /* update the chosen IDI: */
286: if (priority > chosen_priority[buffer_i]) {
287: chosen_idi[buffer_i] = idi;
288: chosen_imr[buffer_i] = imr;
289: chosen_priority[buffer_i] = priority;
290: }
291: }
292:
293: /* otherwise, this IMR is not valid: */
294: else {
295:
296: /* clear this IDI's received bit: */
297: /* XXX FIXME - is this right? shouldn't it stay received,
298: but just never be pending? */
299: TME_STP222X_MDU_IDIS_UPDATE(stp222x->tme_stp222x_mdu_idis_received, &= ~, idi);
300: }
301:
302: /* advance: */
303: idi++;
304: idis_arbitrate >>= 1;
305: } while (idis_arbitrate != 0);
306: }
307: } while (--idis_i >= 0);
308:
309: /* update the dispatching interrupts: */
310: buffer_i = TME_STP222X_MDU_BUFFER_COUNT - 1;
311: do {
312: imr = chosen_imr[buffer_i];
313: if (imr != !TME_STP222X_MDU_IMR_V) {
314: stp222x->tme_stp222x_mdu_dispatch_idi[buffer_i] = chosen_idi[buffer_i];
315: stp222x->tme_stp222x_mdu_dispatch_imr[buffer_i] = imr;
316: assert (stp222x->tme_stp222x_mdu_dispatch_state[buffer_i] == TME_STP222X_MDU_DISPATCH_NOW);
317: }
318: } while (--buffer_i >= 0);
319: }
320:
321: /* this completes dispatch of an interrupt: */
322: static void
323: _tme_stp222x_mdu_dispatch_complete(struct tme_stp22xx *stp22xx,
324: struct tme_completion *completion,
325: void *arg)
326: {
327: struct tme_stp222x *stp222x;
328: signed long buffer_i;
329: int error;
330: tme_uint32_t idi;
331:
332: /* recover our data structure: */
333: stp222x = (struct tme_stp222x *) stp22xx;
334:
335: /* get the interrupt buffer that was dispatched: */
336: buffer_i = stp222x->tme_stp222x_mdu_dispatch_buffer;
337: assert (stp222x->tme_stp222x_mdu_dispatch_imr[buffer_i] != !TME_STP222X_MDU_IMR_V);
338: assert (stp222x->tme_stp222x_mdu_dispatch_state[buffer_i] == TME_STP222X_MDU_DISPATCH_NOW);
339:
340: /* get any error code: */
341: error = completion->tme_completion_error;
342:
343: /* if this interrupt was ACKed: */
344: if (error == TME_OK) {
345:
346: /* clear the dispatched interrupt: */
347: stp222x->tme_stp222x_mdu_dispatch_imr[buffer_i] = !TME_STP222X_MDU_IMR_V;
348:
349: /* get the IDI that was dispatched: */
350: idi = stp222x->tme_stp222x_mdu_dispatch_idi[buffer_i];
351:
352: /* if the dispatched interrupt is pulse-driven: */
353: /* XXX FIXME - we assume that TME_STP2220_IDI_RESERVED is pulse-driven: */
354: if (TME_STP222X_IS_2220(stp222x)
355: ? (idi == TME_STP2220_IDI_UPA
356: || idi == TME_STP2220_IDI_RESERVED)
357: : (idi == TME_STP2222_IDI_FFB0
358: || idi == TME_STP2222_IDI_FFB1)) {
359:
360: /* nothing to do */
361: }
362:
363: /* otherwise, the dispatched interrupt is level-driven: */
364: else {
365:
366: /* set this IDI's pending bit: */
367: TME_STP222X_MDU_IDIS_UPDATE(stp222x->tme_stp222x_mdu_idis_pending, |= , idi);
368: }
369:
370: /* decode and arbitrate again: */
371: _tme_stp222x_mdu_decode_arbitrate(stp222x);
372: }
373:
374: /* otherwise, if interrupt was NACKed: */
375: else if (error == EAGAIN) {
376:
377: /* we need to retry this interrupt buffer later: */
378: stp222x->tme_stp222x_mdu_dispatch_state[buffer_i] = TME_STP222X_MDU_DISPATCH_RETRY(0);
379:
380: /* wake up the retry thread: */
381: tme_stp22xx_cond_notify(&stp222x->tme_stp222x_mdu_retry_cond);
382: }
383:
384: /* the interrupt target must not exist: */
385: else {
386: assert (error == ENOENT);
387:
388: /* XXX FIXME - what happens in this case? */
389: abort();
390: }
391:
392: /* round-robin the interrupt buffer to dispatch: */
393: buffer_i = (buffer_i + 1) % TME_STP222X_MDU_BUFFER_COUNT;
394: stp222x->tme_stp222x_mdu_dispatch_buffer = buffer_i;
395:
396: /* unused: */
397: arg = 0;
398: }
399:
400: /* this dispatches an interrupt: */
401: int
402: tme_stp222x_mdu_dispatch(struct tme_stp222x *stp222x)
403: {
404: unsigned long buffer_i;
405: struct tme_upa_bus_connection *conn_upa;
406: struct tme_completion *completion;
407: tme_uint32_t imr;
408: tme_uint32_t mid;
409: tme_uint64_t interrupt_data[8];
410: struct tme_upa_bus_connection *conn_upa_other;
411:
412: /* find a full interrupt buffer: */
413: buffer_i = stp222x->tme_stp222x_mdu_dispatch_buffer;
414: for (;;) {
415:
416: /* if this interrupt buffer is full, and can be dispatched now: */
417: if (stp222x->tme_stp222x_mdu_dispatch_imr[buffer_i] != !TME_STP222X_MDU_IMR_V
418: && stp222x->tme_stp222x_mdu_dispatch_state[buffer_i] == TME_STP222X_MDU_DISPATCH_NOW) {
419: break;
420: }
421:
422: /* round-robin to the next interrupt buffer: */
423: buffer_i = (buffer_i + 1) % TME_STP222X_MDU_BUFFER_COUNT;
424:
425: /* if all interrupt buffers are empty: */
426: if (buffer_i == stp222x->tme_stp222x_mdu_dispatch_buffer) {
427:
428: /* we didn't dispatch an interrupt: */
429: return (FALSE);
430: }
431: }
432: stp222x->tme_stp222x_mdu_dispatch_buffer = buffer_i;
433:
434: /* busy the UPA bus connection: */
435: conn_upa = tme_stp222x_busy_upa(stp222x);
436:
437: /* allocate a completion: */
438: completion
439: = tme_stp222x_completion_alloc(stp222x,
440: _tme_stp222x_mdu_dispatch_complete,
441: (void *) NULL);
442:
443: /* get the interrupt information: */
444: imr = stp222x->tme_stp222x_mdu_dispatch_imr[buffer_i];
445: mid = TME_FIELD_MASK_EXTRACTU(imr, TME_STP222X_MDU_IMR_TID);
446: memset(interrupt_data, 0, sizeof(interrupt_data));
447: interrupt_data[0] = tme_htobe_u64(TME_FIELD_MASK_EXTRACTU(imr, TME_STP222X_MDU_IMR_INR));
448:
449: /* leave: */
450: tme_stp222x_leave(stp222x);
451:
452: /* call out the interrupt: */
453: conn_upa_other = (struct tme_upa_bus_connection *) conn_upa->tme_upa_bus_connection.tme_bus_connection.tme_connection_other;
454: (*conn_upa_other->tme_upa_bus_interrupt)
455: (conn_upa_other,
456: mid,
457: interrupt_data,
458: completion);
459:
460: /* reenter: */
461: stp222x = tme_stp222x_enter_bus(&conn_upa->tme_upa_bus_connection);
462:
463: /* unbusy the UPA bus connection: */
464: tme_stp222x_unbusy_bus(stp222x, &conn_upa->tme_upa_bus_connection);
465:
466: /* we dispatched an interrupt: */
467: return (TRUE);
468: }
469:
470: /* this receives an interrupt: */
471: void
472: tme_stp222x_mdu_receive(struct tme_stp222x *stp222x,
473: tme_uint32_t idi)
474: {
475:
476: /* set this IDI's received bit: */
477: TME_STP222X_MDU_IDIS_UPDATE(stp222x->tme_stp222x_mdu_idis_received, |= , idi);
478:
479: /* decode and arbitrate again: */
480: _tme_stp222x_mdu_decode_arbitrate(stp222x);
481: }
482:
483: /* this updates the interrupt concentrator: */
484: void
485: tme_stp222x_mdu_intcon(struct tme_stp222x *stp222x,
486: tme_uint32_t idi,
487: tme_uint32_t level)
488: {
489:
490: /* NB: the interrupt concentrator is really in the RIC chip
491: (STP2210): */
492:
493: /* if this is an stp2220, and this is the zs0/zs1 IDI: */
494: if (TME_STP222X_IS_2220(stp222x)
495: && idi == TME_STP2220_IDI_ZS0_ZS1) {
496:
497: /* the interrupt signals from zs0 and zs1 are wired together
498: somewhere in a real system, probably inside the sym89c105
499: (which is apparently not identical to an ncr89c105, because the
500: latter doesn't even have output pins for its individual
501: functions' interrupts). we have to mimic this wiring here: */
502:
503: /* if the interrupt signal is being asserted, increase the active
504: count, otherwise decrease the active count. the active count
505: (which is unsigned) can never be greater than two: */
506: assert (level == TME_BUS_SIGNAL_LEVEL_ASSERTED
507: || level == TME_BUS_SIGNAL_LEVEL_NEGATED);
508: stp222x->tme_stp2220_mdu_idi_zs0_zs1_active
509: += (level == TME_BUS_SIGNAL_LEVEL_ASSERTED
510: ? 1
511: : -1);
512: assert (stp222x->tme_stp2220_mdu_idi_zs0_zs1_active <= 2);
513:
514: /* if the interrupt signal is being asserted, but the active count
515: was already one, or if the interrupt signal is being negated,
516: but the active count is still one: */
517: if ((level == TME_BUS_SIGNAL_LEVEL_ASSERTED)
518: != stp222x->tme_stp2220_mdu_idi_zs0_zs1_active) {
519:
520: /* the state of the interrupt signal at the RIC chip is
521: unchanged: */
522: return;
523: }
524: }
525:
526: /* if this interrupt signal is being asserted: */
527: if (level == TME_BUS_SIGNAL_LEVEL_ASSERTED) {
528:
529: /* set this IDI's active bit: */
530: TME_STP222X_MDU_IDIS_UPDATE(stp222x->tme_stp222x_mdu_idis_active, |= , idi);
531:
532: /* receive this interrupt: */
533: tme_stp222x_mdu_receive(stp222x, idi);
534: }
535:
536: /* otherwise, this interrupt signal must be being negated: */
537: else {
538: assert (level == TME_BUS_SIGNAL_LEVEL_NEGATED);
539:
540: /* clear this IDI's active bit: */
541: TME_STP222X_MDU_IDIS_UPDATE(stp222x->tme_stp222x_mdu_idis_active, &= ~, idi);
542: }
543: }
544:
545: /* this recalculates the interrupt retry period: */
546: static void
547: _tme_stp222x_mdu_retry_set(struct tme_stp222x *stp222x)
548: {
549: tme_uint64_t sleep_usec;
550:
551: /* the retry timer has a period of 15.7ms at the maximum limit of
552: 2^20 ticks. calculate half of the period of the retry timer for
553: _tme_stp222x_mdu_retry_th(): */
554: sleep_usec = TME_FIELD_MASK_EXTRACTU(stp222x->tme_stp222x_mdu_retry, TME_STP222X_MDU_RETRY_TIMER_LIMIT) + 1;
555: sleep_usec *= 15700;
556: #if (TME_STP222X_MDU_RETRY_TIMER_LIMIT & 1) == 0
557: #error "TME_STP222X_MDU_RETRY_TIMER_LIMIT changed"
558: #endif
559: sleep_usec = (sleep_usec + TME_STP222X_MDU_RETRY_TIMER_LIMIT) / (TME_STP222X_MDU_RETRY_TIMER_LIMIT + 1);
560: stp222x->tme_stp222x_mdu_retry_sleep.tv_sec = 0;
561: stp222x->tme_stp222x_mdu_retry_sleep.tv_usec = sleep_usec;
562: }
563:
564: /* the MDU IMR and retry register handler: */
565: void
566: tme_stp222x_mdu_regs_imr_retry(struct tme_stp222x *stp222x,
567: struct tme_stp222x_reg *reg)
568: {
569: tme_uint32_t reggroup;
570: tme_uint32_t reggroup_index;
571: tme_uint32_t imr_partial;
572: tme_uint32_t idi;
573: const char *name;
574:
575: /* get the register: */
576: reggroup = TME_STP222X_REGGROUP_WHICH(reg->tme_stp222x_reg_address);
577: reggroup_index = TME_STP222X_REGGROUP_INDEX(reg->tme_stp222x_reg_address);
578:
579: /* assume that this is a write to a partial IMR, and get a partial
580: IMR value: */
581: imr_partial
582: = (reg->tme_stp222x_reg_value
583: & (TME_STP222X_MDU_IMR_TID
584: | TME_STP222X_MDU_IMR_V));
585:
586: /* assume that this is a register for an obio interrupt: */
587: idi = _tme_stp222x_reggroup_index_to_obio_idi(stp222x, reggroup_index);
588:
589: /* dispatch on the register: */
590: name = NULL;
591: switch (reggroup) {
592:
593: /* the stp2220 SBus card IMRs and the retry register: */
594: case 0x2c:
595: if (__tme_predict_false(!TME_STP222X_IS_2220(stp222x))) {
596: return;
597: }
598:
599: /* if this is an SBus card IMR: */
600: if (reggroup_index < TME_STP2220_SLOTS_CARD) {
601:
602: /* get the IDI for this card's ipl 0 interrupt. SBus ipl 0
603: doesn't really exist, but the partial IMR for this IDI will
604: have zeros in the ipl position, which is what a read must
605: return: */
606: idi = reggroup_index * TME_SBUS_SLOT_INTS;
607:
608: /* if this is a write: */
609: if (reg->tme_stp222x_reg_write) {
610:
611: /* write the partial IMRs for all of this card's ipls, only
612: updating the V and TID fields: */
613: do {
614: stp222x->tme_stp222x_mdu_imrs[idi]
615: = ((stp222x->tme_stp222x_mdu_imrs[idi]
616: & ~(TME_STP222X_MDU_IMR_TID
617: | TME_STP222X_MDU_IMR_V))
618: | imr_partial);
619: } while (++idi % TME_SBUS_SLOT_INTS);
620: idi -= TME_SBUS_SLOT_INTS;
621: }
622:
623: /* otherwise, this is a read: */
624: else {
625:
626: /* read the partial IMR for this card: */
627: reg->tme_stp222x_reg_value = stp222x->tme_stp222x_mdu_imrs[idi];
628: }
629: break;
630: }
631: /* FALLTHROUGH */
632:
633: /* the STP2222 retry register: */
634: case 0x1a:
635: if (__tme_predict_false(reg->tme_stp222x_reg_address
636: != (TME_STP222X_IS_2220(stp222x)
637: ? 0x2c20
638: : 0x1a00))) {
639: return;
640: }
641: if (reg->tme_stp222x_reg_write) {
642: stp222x->tme_stp222x_mdu_retry = reg->tme_stp222x_reg_value;
643: _tme_stp222x_mdu_retry_set(stp222x);
644: }
645: else {
646: reg->tme_stp222x_reg_value = stp222x->tme_stp222x_mdu_retry;
647: }
648: name = "RETRY";
649: break;
650:
651: /* the STP2222 PCI card IMRs: */
652: case 0x0c:
653: if (__tme_predict_false(TME_STP222X_IS_2220(stp222x))) {
654: return;
655: }
656:
657: /* if this is not a PCI card IMR: */
658: idi = reggroup_index * TME_PCI_SLOT_INTS;
659: if (__tme_predict_false((((1 << TME_STP2222_IDI_CARD(0, 0, 0))
660: | (1 << TME_STP2222_IDI_CARD(0, 1, 0))
661: | (1 << TME_STP2222_IDI_CARD(1, 0, 0))
662: | (1 << TME_STP2222_IDI_CARD(1, 1, 0))
663: | (1 << TME_STP2222_IDI_CARD(1, 2, 0))
664: | (1 << TME_STP2222_IDI_CARD(1, 3, 0)))
665: & (1 << idi)) == 0)) {
666: return;
667: }
668:
669: /* if this is a write: */
670: if (reg->tme_stp222x_reg_write) {
671:
672: /* write the partial IMRs for all of this PCI card's INTx, only
673: updating the V and TID fields: */
674: do {
675: stp222x->tme_stp222x_mdu_imrs[idi]
676: = ((stp222x->tme_stp222x_mdu_imrs[idi]
677: & ~(TME_STP222X_MDU_IMR_TID
678: | TME_STP222X_MDU_IMR_V))
679: | imr_partial);
680: } while (++idi % TME_PCI_SLOT_INTS);
681: idi -= TME_PCI_SLOT_INTS;
682: }
683:
684: /* otherwise, this is a read: */
685: else {
686:
687: /* read the partial IMR for this PCI card's INTA: */
688: reg->tme_stp222x_reg_value = stp222x->tme_stp222x_mdu_imrs[idi];
689: }
690: break;
691:
692: /* the STP2222 alternate FFB0 and FFB1 IMRs: */
693: case 0x60:
694: case 0x80:
695: idi = (reggroup == 0x60 ? TME_STP2222_IDI_FFB0 : TME_STP2222_IDI_FFB1);
696: reggroup = 0x10;
697: /* FALLTHROUGH */
698:
699: /* the obio IMRs: */
700: default:
701:
702: /* if this is the wrong obio IMR register group for this part, or
703: if this is not an obio IMR, return failure: */
704: if (TME_STP222X_IS_2220(stp222x)) {
705: if (__tme_predict_false(reggroup != 0x30
706: || idi > TME_STP2220_IDI_RESERVED)) {
707: return;
708: }
709: }
710: else {
711: if (__tme_predict_false(reggroup != 0x10
712: || idi > TME_STP2222_IDI_FFB1)) {
713: return;
714: }
715: }
716:
717: /* if this is an obio IMR write: */
718: if (reg->tme_stp222x_reg_write) {
719:
720: /* if this is a obio full IMR write: */
721: if (TME_STP222X_IS_2220(stp222x)
722: ? (idi == TME_STP2220_IDI_UPA
723: || idi == TME_STP2220_IDI_RESERVED)
724: : (idi == TME_STP2222_IDI_FFB0
725: || idi == TME_STP2222_IDI_FFB1)) {
726:
727: /* do the obio full IMR write: */
728: stp222x->tme_stp222x_mdu_imrs[idi]
729: = (reg->tme_stp222x_reg_value
730: & (TME_STP222X_MDU_IMR_INR
731: | TME_STP222X_MDU_IMR_TID
732: | TME_STP222X_MDU_IMR_V));
733: }
734:
735: /* otherwise, this is a obio partial IMR write: */
736: else {
737:
738: /* do the obio partial IMR write: */
739: stp222x->tme_stp222x_mdu_imrs[idi]
740: = ((stp222x->tme_stp222x_mdu_imrs[idi]
741: & ~(TME_STP222X_MDU_IMR_TID
742: | TME_STP222X_MDU_IMR_V))
743: | imr_partial);
744: }
745: }
746:
747: /* otherwise, read the obio IMR: */
748: else {
749: reg->tme_stp222x_reg_value = stp222x->tme_stp222x_mdu_imrs[idi];
750: }
751: break;
752: }
753:
754: if (name == NULL) {
755: tme_log(TME_STP222X_LOG_HANDLE(stp222x), 2000, TME_OK,
756: (TME_STP222X_LOG_HANDLE(stp222x),
757: _("MDU IMR[0x%x] %s 0x%" TME_PRIx64),
758: idi,
759: (reg->tme_stp222x_reg_write
760: ? "<-"
761: : "->"),
762: reg->tme_stp222x_reg_value));
763: }
764: else {
765: tme_log(TME_STP222X_LOG_HANDLE(stp222x), 2000, TME_OK,
766: (TME_STP222X_LOG_HANDLE(stp222x),
767: _("MDU %s %s 0x%" TME_PRIx64),
768: name,
769: (reg->tme_stp222x_reg_write
770: ? "<-"
771: : "->"),
772: reg->tme_stp222x_reg_value));
773: }
774:
775: /* this register access has been completed: */
776: reg->tme_stp222x_reg_completed = TRUE;
777: }
778:
779: /* the MDU clear register handler: */
780: void
781: tme_stp222x_mdu_regs_clear(struct tme_stp222x *stp222x,
782: struct tme_stp222x_reg *reg)
783: {
784: tme_uint32_t reggroup;
785: tme_uint32_t reggroup_index;
786: tme_uint32_t idi;
787: tme_uint32_t mdu_state;
788: tme_uint32_t imr;
789: unsigned long buffer_i;
790:
791: /* get the register: */
792: reggroup = TME_STP222X_REGGROUP_WHICH(reg->tme_stp222x_reg_address);
793: reggroup_index = TME_STP222X_REGGROUP_INDEX(reg->tme_stp222x_reg_address);
794:
795: /* assume that this is a register for an obio interrupt: */
796: idi = _tme_stp222x_reggroup_index_to_obio_idi(stp222x, reggroup_index);
797:
798: /* dispatch on the register: */
799: switch (reggroup) {
800:
801: /* the STP2220 SBus and obio card clears: */
802: case 0x34:
803: idi = reggroup_index;
804: /* FALLTHROUGH */
805: case 0x38:
806: if (__tme_predict_false(!TME_STP222X_IS_2220(stp222x))) {
807: return;
808: }
809: if (__tme_predict_false(idi > TME_STP2220_IDI_POWER_MANAGE)) {
810: return;
811: }
812: break;
813:
814: /* the STP2222 PCI card clears: */
815: case 0x14:
816: if (__tme_predict_false(TME_STP222X_IS_2220(stp222x))) {
817: return;
818: }
819:
820: /* if this is not a PCI card clear register: */
821: idi = reggroup_index;
822: if (idi >= TME_STP2222_IDI_CARD(0, 2, 0)
823: && idi < TME_STP2222_IDI_CARD(1, 0, 0)) {
824: return;
825: }
826: break;
827:
828: /* the STP2222 obio clears: */
829: default:
830: assert (reggroup == 0x10);
831: if (__tme_predict_false(TME_STP222X_IS_2220(stp222x))) {
832: return;
833: }
834: if (__tme_predict_false(idi > TME_STP2222_IDI_POWER_MANAGE)) {
835: return;
836: }
837: break;
838: }
839:
840: /* if this is a write: */
841: if (reg->tme_stp222x_reg_write) {
842:
843: /* update the MDU state for this IDI: */
844: mdu_state = reg->tme_stp222x_reg_value;
845: if ((mdu_state & TME_STP222X_MDU_STATE_RECEIVED)
846: || TME_STP222X_MDU_IDI_TEST(stp222x->tme_stp222x_mdu_idis_active, idi)) {
847: TME_STP222X_MDU_IDIS_UPDATE(stp222x->tme_stp222x_mdu_idis_received, |= , idi);
848: }
849: else {
850: TME_STP222X_MDU_IDIS_UPDATE(stp222x->tme_stp222x_mdu_idis_received, &= ~, idi);
851: }
852: if (mdu_state == TME_STP222X_MDU_STATE_PENDING) {
853: TME_STP222X_MDU_IDIS_UPDATE(stp222x->tme_stp222x_mdu_idis_pending, |= , idi);
854: }
855: else {
856: TME_STP222X_MDU_IDIS_UPDATE(stp222x->tme_stp222x_mdu_idis_pending, &= ~, idi);
857:
858: /* get the IMR for this IDI: */
859: imr = stp222x->tme_stp222x_mdu_imrs[idi];
860:
861: /* loop over the interrupt dispatch buffers: */
862: for (buffer_i = 0; buffer_i < TME_STP222X_MDU_BUFFER_COUNT; buffer_i++) {
863:
864: /* if this interrupt dispatch buffer has the same V and TID
865: values as the IMR for this IDI: */
866: if (((stp222x->tme_stp222x_mdu_dispatch_imr[buffer_i]
867: ^ imr)
868: & (TME_STP222X_MDU_IMR_V
869: + TME_STP222X_MDU_IMR_TID)) == 0) {
870:
871: /* assume that this interrupt buffer has its V bit set, and
872: force it to dispatch now (if its V bit is clear, this
873: should not change its dispatch state): */
874: /* NB: this is a hack to improve interrupt latency when the
875: retry thread has high latency (due to poor
876: tme_cond_sleep_yield() sleep resolution): whenever a CPU
877: writes an MDU state other than pending for an IDI, assume
878: that the CPU can accept another interrupt and force any
879: interrupt waiting to dispatch to that CPU to dispatch
880: immediately: */
881: assert ((imr & TME_STP222X_MDU_IMR_V)
882: || (stp222x->tme_stp222x_mdu_dispatch_state[buffer_i]
883: == TME_STP222X_MDU_DISPATCH_NOW));
884: stp222x->tme_stp222x_mdu_dispatch_state[buffer_i] = TME_STP222X_MDU_DISPATCH_NOW;
885: }
886: }
887: }
888:
889: /* decode and arbitrate again: */
890: _tme_stp222x_mdu_decode_arbitrate(stp222x);
891:
892: tme_log(TME_STP222X_LOG_HANDLE(stp222x), 2000, TME_OK,
893: (TME_STP222X_LOG_HANDLE(stp222x),
894: _("MDU clear[0x%x] <- 0x%" TME_PRIx32),
895: idi,
896: mdu_state));
897: }
898:
899: /* otherwise, this is a read: */
900: else {
901: reg->tme_stp222x_reg_value = 0;
902: }
903:
904: /* this register access has been completed: */
905: reg->tme_stp222x_reg_completed = TRUE;
906: }
907:
908: /* the MDU diagnostic register handler: */
909: void
910: tme_stp222x_mdu_regs_diag(struct tme_stp222x *stp222x,
911: struct tme_stp222x_reg *reg)
912: {
913: unsigned long idis_i;
914: tme_stp222x_idis_t idis_received;
915: tme_stp222x_idis_t idis_pending;
916: tme_uint32_t bit;
917: tme_uint32_t value_32_63;
918: tme_uint32_t value_0_31;
919:
920: /* check the register and cycle type: */
921: assert (sizeof(tme_stp222x_idis_t) == sizeof(tme_uint32_t));
922: idis_i = TME_STP222X_REGGROUP_INDEX(reg->tme_stp222x_reg_address);
923: if (__tme_predict_false(idis_i > TME_ARRAY_ELS(stp222x->tme_stp222x_mdu_idis_received))) {
924: return;
925: }
926: if (__tme_predict_false(reg->tme_stp222x_reg_write)) {
927: return;
928: }
929:
930: /* get the selected internal received and pending registers: */
931: idis_received = stp222x->tme_stp222x_mdu_idis_received[idis_i];
932: idis_pending = stp222x->tme_stp222x_mdu_idis_pending[idis_i];
933:
934: /* if this diagnostic register covers the obio interrupts: */
935: if (idis_i == 1) {
936:
937: /* both parts have two pulse-driven interrupts at the end of the
938: diagnostic register, using only one bit each instead of the two
939: bits each that the level-driven interrupts use. we move the
940: received bit of the last pulse-driven interrupt into the
941: pending bit for the next to last pulse-driven interrupt, to
942: make the two pulse-driven interrupts look like a single
943: level-driven interrupt: */
944: if (TME_STP222X_IS_2220(stp222x)) {
945: idis_pending
946: |= ((idis_received & (1 << (TME_STP2220_IDI_RESERVED - TME_STP222X_IDI0_OBIO)))
947: >> (TME_STP2220_IDI_RESERVED - TME_STP2220_IDI_UPA));
948: idis_received &= ~ (tme_uint32_t) (1 << (TME_STP2220_IDI_RESERVED - TME_STP222X_IDI0_OBIO));
949: }
950: else {
951: idis_pending
952: |= ((idis_received & (1 << (TME_STP2222_IDI_FFB1 - TME_STP222X_IDI0_OBIO)))
953: >> (TME_STP2222_IDI_FFB1 - TME_STP2222_IDI_FFB0));
954: idis_received &= ~ (tme_uint32_t) (1 << (TME_STP2222_IDI_FFB1 - TME_STP222X_IDI0_OBIO));
955: }
956: }
957:
958: /* make bits 32..63 of the value: */
959: bit = ((tme_uint32_t) 1) << 31;
960: value_32_63 = 0;
961: do {
962: if (idis_pending & (((tme_uint32_t) 1) << 31)) {
963: value_32_63 += bit;
964: }
965: idis_pending <<= 1;
966: bit >>= 1;
967: if (idis_received & (((tme_uint32_t) 1) << 31)) {
968: value_32_63 += bit;
969: }
970: idis_received <<= 1;
971: bit >>= 1;
972: } while (bit != 0);
973:
974: /* make bits 0..31 of the value: */
975: value_0_31 = 0;
976: bit = ((tme_uint32_t) 1) << 31;
977: do {
978: if (idis_pending & (((tme_uint32_t) 1) << 31)) {
979: value_0_31 += bit;
980: }
981: idis_pending <<= 1;
982: bit >>= 1;
983: if (idis_received & (((tme_uint32_t) 1) << 31)) {
984: value_0_31 += bit;
985: }
986: idis_received <<= 1;
987: bit >>= 1;
988: } while (bit != 0);
989:
990: reg->tme_stp222x_reg_value
991: = ((((tme_uint64_t) value_32_63) << 32)
992: | value_0_31);
993:
994: tme_log(TME_STP222X_LOG_HANDLE(stp222x), 2000, TME_OK,
995: (TME_STP222X_LOG_HANDLE(stp222x),
996: _("MDU DIAG -> 0x%" TME_PRIx64),
997: reg->tme_stp222x_reg_value));
998:
999: /* this register access has been completed: */
1000: reg->tme_stp222x_reg_completed = TRUE;
1001: }
1002:
1003: /* this updates the IGN for the partial IMRs: */
1004: void
1005: tme_stp222x_mdu_ign_update(struct tme_stp222x *stp222x,
1006: tme_uint32_t ign)
1007: {
1008: tme_uint32_t idi;
1009: tme_uint32_t ino;
1010:
1011: /* loop over all IDIs: */
1012: for (idi = 0; idi < TME_STP222X_IDI_NULL; idi++) {
1013:
1014: /* if this IDI has a partial IMR: */
1015: if (TME_STP222X_IS_2220(stp222x)
1016: ? (idi != TME_STP2220_IDI_UPA
1017: && idi != TME_STP2220_IDI_RESERVED)
1018: : (idi != TME_STP2222_IDI_FFB0
1019: && idi != TME_STP2222_IDI_FFB1)) {
1020:
1021: /* assume that the IDI is also the INO: */
1022: ino = idi;
1023:
1024: /* if this is an stp2220 obio interrupt: */
1025: if (TME_STP222X_IS_2220(stp222x)
1026: && idi >= TME_STP222X_IDI0_OBIO) {
1027:
1028: /* the stp2220 obio interrupt to INO mapping is not regular: */
1029: switch (idi) {
1030: case TME_STP222X_IDI_SCSI: ino = 0x20; break;
1031: case TME_STP222X_IDI_ETHER: ino = 0x21; break;
1032: case TME_STP222X_IDI_BPP: ino = 0x22; break;
1033: case TME_STP2220_IDI_AUDIO: ino = 0x24; break;
1034: case TME_STP2220_IDI_POWER: ino = 0x25; break;
1035: case TME_STP2220_IDI_ZS0_ZS1: ino = 0x28; break;
1036: case TME_STP2220_IDI_FD: ino = 0x29; break;
1037: case TME_STP2220_IDI_THERM: ino = 0x2a; break;
1038: case TME_STP2220_IDI_KBD: ino = 0x2b; break;
1039: case TME_STP2220_IDI_MOUSE: ino = 0x2c; break;
1040: case TME_STP2220_IDI_SERIAL: ino = 0x2d; break;
1041: case TME_STP2220_IDI_TIMER(0): ino = 0x30; break;
1042: case TME_STP2220_IDI_TIMER(1): ino = 0x31; break;
1043: case TME_STP2220_IDI_UE: ino = 0x34; break;
1044: case TME_STP2220_IDI_CE: ino = 0x35; break;
1045: case TME_STP2220_IDI_SBUS_ASYNC: ino = 0x36; break;
1046: case TME_STP2220_IDI_POWER_MANAGE: ino = 0x37; break;
1047: case TME_STP2220_IDI_UPA: ino = 0x38; break;
1048: case TME_STP2220_IDI_RESERVED: ino = 0x39; break;
1049: default: break;
1050: }
1051: }
1052:
1053: /* update the INR in the IDI's IMR: */
1054: stp222x->tme_stp222x_mdu_imrs[idi]
1055: = ((stp222x->tme_stp222x_mdu_imrs[idi]
1056: & ~TME_STP222X_MDU_IMR_INR)
1057: + (ign * TME_STP222X_IDI_NULL)
1058: + ino);
1059: }
1060: }
1061: }
1062:
1063: /* this initializes the MDU: */
1064: void
1065: tme_stp222x_mdu_init(struct tme_stp222x *stp222x)
1066: {
1067:
1068: /* initialize the IMRs: */
1069: memset(stp222x->tme_stp222x_mdu_imrs, 0, sizeof(stp222x->tme_stp222x_mdu_imrs));
1070: tme_stp222x_mdu_ign_update(stp222x, 0);
1071:
1072: /* initialize the retry timer: */
1073: stp222x->tme_stp222x_mdu_retry = 0;
1074: _tme_stp222x_mdu_retry_set(stp222x);
1075:
1076: /* initialize the retry condition: */
1077: tme_stp22xx_cond_init(&stp222x->tme_stp222x_mdu_retry_cond);
1078:
1079: /* start the retry thread: */
1080: tme_thread_create(_tme_stp222x_mdu_retry_th, stp222x);
1081: }
1082:
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.