Commit | Line | Data |
---|---|---|
920dae64 AT |
1 | // ========== Copyright Header Begin ========================================== |
2 | // | |
3 | // OpenSPARC T2 Processor File: serial.cc | |
4 | // Copyright (c) 2006 Sun Microsystems, Inc. All Rights Reserved. | |
5 | // DO NOT ALTER OR REMOVE COPYRIGHT NOTICES. | |
6 | // | |
7 | // The above named program is free software; you can redistribute it and/or | |
8 | // modify it under the terms of the GNU General Public | |
9 | // License version 2 as published by the Free Software Foundation. | |
10 | // | |
11 | // The above named program is distributed in the hope that it will be | |
12 | // useful, but WITHOUT ANY WARRANTY; without even the implied warranty of | |
13 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
14 | // General Public License for more details. | |
15 | // | |
16 | // You should have received a copy of the GNU General Public | |
17 | // License along with this work; if not, write to the Free Software | |
18 | // Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. | |
19 | // | |
20 | // ========== Copyright Header End ============================================ | |
21 | /* | |
22 | * Copyright (C) 1991, 2001 Sun Microsystems, Inc. | |
23 | * All rights reserved. | |
24 | */ | |
25 | #pragma ident "@(#)1.11 04/08/04 serial.cc" | |
26 | ||
27 | #include <sys/types.h> | |
28 | #include <sys/stat.h> | |
29 | #include <assert.h> | |
30 | #include <fcntl.h> | |
31 | #include <stdio.h> | |
32 | #include <stdlib.h> | |
33 | #include <strings.h> | |
34 | #include <unistd.h> | |
35 | ||
36 | #include "types.h" | |
37 | #include "blaze_globals.h" | |
38 | #include "system.h" | |
39 | #include "dr.h" | |
40 | #include "term.h" | |
41 | #include "serial.h" | |
42 | #include "serial_impl.h" | |
43 | ||
44 | typedef void* TM_OPAQUE_DATA; | |
45 | #include "tracemod.h" | |
46 | ||
47 | #include "serial_mod.h" | |
48 | ||
49 | #define BYTE_OFFSET 7 | |
50 | ||
51 | ||
52 | extern Serial *systemConsole;; | |
53 | ||
54 | /* | |
55 | * This function is to allocate a serial object | |
56 | */ | |
57 | serialT *Serial::allocation_obj() | |
58 | { | |
59 | msp = (serialT *) calloc(1, sizeof(serialT)); | |
60 | ||
61 | if (msp == NULL) { | |
62 | debug_err("%s: malloc serial object failed\n", HERE); | |
63 | return NULL; | |
64 | } | |
65 | ||
66 | reset_scc(); | |
67 | ||
68 | return (msp); | |
69 | } | |
70 | ||
71 | ||
72 | /* | |
73 | * This routine is called to receive requests to read and write the serial's | |
74 | * registers. Return a status of 1 if error | |
75 | */ | |
76 | int | |
77 | Serial::reg_access(char *buf, uint64_t paddr, bool wr) | |
78 | { | |
79 | ||
80 | uint8_t *reg_value = (uint8_t *) (buf + BYTE_OFFSET); /* value of register read */ | |
81 | uint32_t addr = (uint32_t) paddr & SERIAL_PORT_ADDR_MASK; | |
82 | ||
83 | ||
84 | if (!wr) { | |
85 | switch (addr) { | |
86 | case PORT_A_CONTROL: | |
87 | *reg_value = rd_control(PORT_A); | |
88 | break; | |
89 | ||
90 | case PORT_B_CONTROL: | |
91 | *reg_value = rd_control(PORT_B); | |
92 | break; | |
93 | ||
94 | case PORT_A_DATA: | |
95 | *reg_value = rd_port(PORT_A); | |
96 | break; | |
97 | ||
98 | case PORT_B_DATA: | |
99 | *reg_value = rd_port(PORT_B); | |
100 | break; | |
101 | ||
102 | default: | |
103 | printf("%s: read invalid address 0x%lx\n", HERE, addr); | |
104 | debug_err("%s: read invalid address 0x%lx\n", HERE, addr); | |
105 | return 1; | |
106 | } | |
107 | } else { | |
108 | switch (addr) { | |
109 | case PORT_A_CONTROL: | |
110 | wr_control(PORT_A, | |
111 | *(uint8_t *) (buf + BYTE_OFFSET)); | |
112 | break; | |
113 | ||
114 | case PORT_B_CONTROL: | |
115 | wr_control(PORT_B, | |
116 | *(uint8_t *) (buf + BYTE_OFFSET)); | |
117 | break; | |
118 | ||
119 | case PORT_A_DATA: | |
120 | wr_port(PORT_A, | |
121 | *(uint8_t *) (buf + BYTE_OFFSET)); | |
122 | break; | |
123 | ||
124 | case PORT_B_DATA: | |
125 | wr_port(PORT_B, | |
126 | *(uint8_t *) (buf + BYTE_OFFSET)); | |
127 | break; | |
128 | ||
129 | default: | |
130 | printf("%s: write invalid address 0x%lx\n", HERE, | |
131 | paddr); | |
132 | debug_err("%s: write invalid address 0x%lx\n", HERE, | |
133 | paddr); | |
134 | return 1; | |
135 | } | |
136 | } | |
137 | ||
138 | return 0; | |
139 | } | |
140 | ||
141 | /* | |
142 | * This routine is called to receive characters as they arrive. It queues | |
143 | * them up in the serial's internal queue. | |
144 | */ | |
145 | void | |
146 | Serial::char_input(uint8_t c, int portnum) | |
147 | { | |
148 | channel_regs *rcv_regs; | |
149 | ||
150 | rcv_regs = &(msp->scc.port[portnum]); | |
151 | ||
152 | enq(rcv_regs, c); | |
153 | ||
154 | /* turn on the "ready to receive" flag */ | |
155 | rcv_regs->rr[0] |= RX_CHAR_AVAILABLE; | |
156 | ||
157 | if (INTERRUPTS_ENABLED(msp, portnum) && !RCV_INT_PENDING(msp, portnum) | |
158 | && RCV_INT_ENABLED(msp, portnum) && rcv_regs->qcnt) { | |
159 | /* | |
160 | * Update status reg - Interrupt Pending Send interrupt | |
161 | */ | |
162 | channel_regs *channelA = &(msp->scc.port[PORT_A]); | |
163 | channel_regs *channelB = &(msp->scc.port[PORT_B]); | |
164 | ||
165 | /* | |
166 | * if we're generating interrupts in Vector Include Status mode, | |
167 | * the interrupt vector on channelB includes status in the lower | |
168 | * nibble which is characteristic of the event causing the | |
169 | * interrupt. | |
170 | */ | |
171 | if (rcv_regs->wr[9] & VIS) { | |
172 | /* | |
173 | * if no interrupts are pending, clear out | |
174 | * vector include status bits before or'ing in new state | |
175 | * (channelA.rr3) is the interrupt pending register | |
176 | * (channelB.rr2) is the interrupt vector (VIS) register | |
177 | */ | |
178 | if (!channelA->rr[3]) | |
179 | /* clearing idle "special rcv condition" bits */ | |
180 | channelB->rr[2] &= ~VIS_LO_MASK; | |
181 | ||
182 | /* add "Rx ready" to status vector */ | |
183 | SET_VIS_RX_CHAR_AVAIL(msp, portnum); | |
184 | } | |
185 | ||
186 | SET_RCV_INT_PENDING(msp, portnum); | |
187 | //dev_interrupt_in(TRUE,device, 0); | |
188 | pciMaster_set_int(0,true); | |
189 | } | |
190 | } | |
191 | ||
192 | /* | |
193 | * Add character 'c' to end of port receive queue. | |
194 | */ | |
195 | void | |
196 | Serial::enq(channel_regs * rcv_regs, char c) | |
197 | { | |
198 | squeue *q, *cursor; | |
199 | ||
200 | ||
201 | ||
202 | ||
203 | if ((q = (squeue *) calloc(1, sizeof(squeue))) == NULL) { | |
204 | assert(0); | |
205 | /* fatal("serial: enq: Unable to malloc.\n"); */ | |
206 | } | |
207 | ||
208 | ||
209 | q->c = c; | |
210 | q->next = NULL; | |
211 | ||
212 | mutex_lock(&rcv_regs->serial_lock); | |
213 | ||
214 | cursor = &rcv_regs->q; | |
215 | ||
216 | while (cursor->next) { | |
217 | cursor = cursor->next; | |
218 | } | |
219 | ||
220 | cursor->next = q; | |
221 | rcv_regs->qcnt++; | |
222 | ||
223 | mutex_unlock(&rcv_regs->serial_lock); | |
224 | ||
225 | } | |
226 | ||
227 | /* | |
228 | * Remove character from front of queue and return it. If no char, return | |
229 | * the NULL character. | |
230 | */ | |
231 | char | |
232 | Serial::deq(channel_regs * rcv_regs) | |
233 | { | |
234 | squeue *cursor; | |
235 | char c = '\0'; /* still return 0 if nothing here */ | |
236 | ||
237 | mutex_lock(&rcv_regs->serial_lock); | |
238 | ||
239 | if (rcv_regs->qcnt) { | |
240 | cursor = &rcv_regs->q; | |
241 | if (cursor->next) { | |
242 | cursor = cursor->next; | |
243 | c = cursor->c; | |
244 | rcv_regs->q.next = cursor->next; | |
245 | rcv_regs->qcnt--; | |
246 | free((char *) cursor); | |
247 | } else { | |
248 | assert(0); | |
249 | /* fatal("serial: deq: qcnt inconsistent"); */ | |
250 | } | |
251 | } | |
252 | ||
253 | mutex_unlock(&rcv_regs->serial_lock); | |
254 | ||
255 | return (c); | |
256 | } | |
257 | ||
258 | char | |
259 | Serial::rd_port(int portnum) | |
260 | { | |
261 | char c; | |
262 | channel_regs *rcv_regs; | |
263 | ||
264 | rcv_regs = &(msp->scc.port[portnum]); | |
265 | ||
266 | c = deq(rcv_regs); | |
267 | ||
268 | /* turn off "ready to receive" flag if we're out of characters */ | |
269 | if (rcv_regs->qcnt == 0) { | |
270 | rcv_regs->rr[0] &= ~RX_CHAR_AVAILABLE; | |
271 | } | |
272 | ||
273 | if (RCV_INT_PENDING(msp, portnum) && !(rcv_regs->qcnt)) { | |
274 | CLEAR_RCV_INT_PENDING(msp, portnum); | |
275 | //dev_interrupt_in(FALSE, device, 0); | |
276 | pciMaster_set_int(0,false); | |
277 | } | |
278 | ||
279 | return c; | |
280 | } | |
281 | ||
282 | void | |
283 | Serial::wr_port(int portnum, uint8_t c) | |
284 | { | |
285 | channel_regs *xmit_regs = &(msp->scc.port[portnum]); | |
286 | ||
287 | ||
288 | xmit_regs->rr[0] &= ~TX_READY; | |
289 | xmit_regs->rr[1] &= ~ALL_SENT; | |
290 | xmit_regs->wr[8] = c; | |
291 | ||
292 | /* | |
293 | * Note! the transmit & subsequent status posting operation | |
294 | * may need to be delayed an indeterminate number of cycles | |
295 | * for kernel/zs driver purposes. | |
296 | * ...leaving it as a synchronous function call for now. | |
297 | */ | |
298 | transmit(portnum); | |
299 | } | |
300 | ||
301 | uint8_t | |
302 | Serial::rd_control(int portnum) | |
303 | { | |
304 | AB_Regs_t *sccp = &msp->scc; | |
305 | short read_reg; | |
306 | ||
307 | /*-------------------------------------------------------- | |
308 | * Register Addressing | |
309 | * | |
310 | * WR0(D5-D3) WR0 RR | |
311 | * (Point High) D2 D1 D0 | |
312 | * NOT 001 0 0 0 0 | |
313 | * " 0 0 1 1 | |
314 | * " 0 1 0 2 | |
315 | * " 0 1 1 3 | |
316 | * " 1 0 0 0 | |
317 | * " 1 0 1 1 | |
318 | * " 1 1 0 2 | |
319 | * " 1 1 1 3 | |
320 | * 001 0 0 0 data (not implemented in this func) | |
321 | * 001 0 0 1 - | |
322 | * 001 0 1 0 10 | |
323 | * 001 0 1 1 15 | |
324 | * 001 1 0 0 12 | |
325 | * 001 1 0 1 13 | |
326 | * 001 1 1 0 10 | |
327 | * 001 1 1 1 15 | |
328 | *------------------------------------------------------ | |
329 | */ | |
330 | ||
331 | /* Read WR0 (D2-D0) */ | |
332 | read_reg = (sccp->port[portnum].wr[0] & REG_NUMBER); | |
333 | ||
334 | if ((sccp->port[portnum].wr[0] & WR0_BITS3_5) != POINT_HIGH) { | |
335 | read_reg &= REGS_0_3; | |
336 | } else { | |
337 | ||
338 | if (read_reg & REG_2) | |
339 | read_reg = 10; | |
340 | else if (read_reg & REG_3) | |
341 | read_reg = 15; | |
342 | else if (read_reg == 1) { | |
343 | debug_err("%s: rd_control: POINT_HIGH and WR0 D2-D0 is 1.\nRequest ignored.\n", HERE); | |
344 | return (0); | |
345 | } else { | |
346 | read_reg += 8; | |
347 | } | |
348 | } | |
349 | ||
350 | /* | |
351 | * If just read a non-zero register, clear D2-D0 so it points to WR0 | |
352 | * or RR0 again | |
353 | */ | |
354 | if (read_reg != 0) | |
355 | sccp->port[portnum].wr[0] &= WR0_BITS0_4; | |
356 | ||
357 | return (sccp->port[portnum].rr[read_reg]); | |
358 | } | |
359 | ||
360 | ||
361 | void | |
362 | Serial::wr_control(int portnum, unsigned char buf) | |
363 | { | |
364 | AB_Regs_t *sccp = &msp->scc; | |
365 | short write_reg; | |
366 | ||
367 | /*-------------------------------------------------------- | |
368 | * Register Addressing | |
369 | * | |
370 | * WR0(D5-D3) WR0 WR | |
371 | * (Point High) D2 D1 D0 | |
372 | * NOT 001 0 0 0 0 (comand register) | |
373 | * " 0 0 1 1 | |
374 | * " 0 1 0 2 | |
375 | * " 0 1 1 3 | |
376 | * " 1 0 0 4 | |
377 | * " 1 0 1 5 | |
378 | * " 1 1 0 6 | |
379 | * " 1 1 1 7 | |
380 | * 001 0 0 0 data (DATA request - not in this func) | |
381 | * 001 0 0 1 9 | |
382 | * 001 0 1 0 10 | |
383 | * 001 0 1 1 11 | |
384 | * 001 1 0 0 12 | |
385 | * 001 1 0 1 13 | |
386 | * 001 1 1 0 14 | |
387 | * 001 1 1 1 15 | |
388 | *------------------------------------------------------ | |
389 | */ | |
390 | ||
391 | /* Read WR0 D2-D0 */ | |
392 | write_reg = (sccp->port[portnum].wr[0] & REG_NUMBER); | |
393 | ||
394 | if ((sccp->port[portnum].wr[0] & WR0_BITS3_5) == POINT_HIGH) { | |
395 | if (write_reg == 0) { | |
396 | /* should never get here */ | |
397 | debug_err("%s: wr_control: POINT_HIGH and WR0 D2-D0 is 0. Request Ignored.\n", HERE); | |
398 | return; | |
399 | } | |
400 | write_reg += 8; | |
401 | } | |
402 | ||
403 | ||
404 | sccp->port[portnum].wr[write_reg] = buf; | |
405 | ||
406 | /* | |
407 | * WR0 - If a channel command is being requested, | |
408 | * perform necessary house cleaning | |
409 | */ | |
410 | if (sccp->port[portnum].wr[0] & WR0_BITS4_5) | |
411 | channel_cmd(portnum); | |
412 | ||
413 | /* | |
414 | * WR2 and WR9 are shared by the two channels. | |
415 | */ | |
416 | if (write_reg == 2 || write_reg == 9) { | |
417 | int otherport; | |
418 | ||
419 | if (portnum == PORT_A) { | |
420 | otherport = PORT_B; | |
421 | } else if (portnum == PORT_B) { | |
422 | otherport = PORT_A; | |
423 | } | |
424 | sccp->port[otherport].wr[write_reg] = buf; | |
425 | } | |
426 | ||
427 | /* WR5 transmit parameter & control */ | |
428 | if (write_reg == 5) { | |
429 | /* if DTR than set CD */ | |
430 | if (buf & DTR) | |
431 | sccp->port[portnum].rr[0] |= DCD; | |
432 | ||
433 | /* if RTS than set CTS */ | |
434 | if (buf & RTS) | |
435 | sccp->port[portnum].rr[0] |= CTS; | |
436 | ||
437 | /* for status update purposes ... */ | |
438 | if (buf & (DTR | RTS)) | |
439 | status(portnum); | |
440 | } | |
441 | ||
442 | /* RR2,RR12,RR13,RR15 reflect the values in the write registers */ | |
443 | if ((write_reg == 2) || (write_reg == 12) || (write_reg == 13) || | |
444 | (write_reg == 15)) | |
445 | sccp->port[portnum].rr[write_reg] = buf; | |
446 | ||
447 | /* | |
448 | * If just written a non-zero register, clear D2-D0 so it points to | |
449 | * WR0 or RR0 again | |
450 | */ | |
451 | if (write_reg != 0) | |
452 | sccp->port[portnum].wr[0] &= WR0_BITS0_4; | |
453 | } | |
454 | ||
455 | ||
456 | void | |
457 | Serial::reset_scc() | |
458 | { | |
459 | short port, i; | |
460 | ||
461 | for (port = 0; port < NUM_PORTS; port++) { | |
462 | /* Clear out all registers for port. */ | |
463 | msp->scc.port[port].qcnt = 0; | |
464 | msp->scc.port[port].q.next = NULL; | |
465 | for (i = 0; i < SERIAL_REGS; i++) { | |
466 | msp->scc.port[port].wr[i] = 0; | |
467 | msp->scc.port[port].rr[i] = 0; | |
468 | } | |
469 | ||
470 | /* set "ready to transmit" */ | |
471 | msp->scc.port[port].rr[0] = TX_READY; | |
472 | ||
473 | /* set "all sent" */ | |
474 | msp->scc.port[port].rr[1] = ALL_SENT; | |
475 | ||
476 | /* | |
477 | * set to the "special receive condition" whose need | |
478 | * was determined by the kernel zs bringup efforts. | |
479 | */ | |
480 | msp->scc.port[port].rr[2] = SRC; | |
481 | ||
482 | ||
483 | mutex_init(&(msp->scc.port[port].serial_lock), USYNC_THREAD, | |
484 | (void *) NULL); | |
485 | ||
486 | } | |
487 | ||
488 | /* | |
489 | * Set interrrupts enabled for port A if there is an interrupt | |
490 | * interface, otherwise leave them disabled. | |
491 | */ | |
492 | /* | |
493 | * The state of WR1 after reset is all interrrupts disabled. | |
494 | * The state of WR1 after reset is bitwise: <00x00x00> | |
495 | * see pg 7-4; in the AMD Z85C30/SCC tech manual | |
496 | * | |
497 | * if (msp->intr_intf) { | |
498 | * SET_INTERRUPTS_ENABLE(msp, PORT_A); | |
499 | * SET_RCV_INT_ENABLE(msp, PORT_A); | |
500 | * } | |
501 | */ | |
502 | } | |
503 | ||
504 | void | |
505 | Serial::transmit(int portnum) | |
506 | { | |
507 | channel_regs *xmit_regs = &(msp->scc.port[portnum]); | |
508 | ||
509 | /* transmit the data character */ | |
510 | ||
511 | term_write(&xmit_regs->wr[8],portHandle[portnum]); | |
512 | //(msp == console_msp) ? portnum:portnum+2); | |
513 | ||
514 | xmit_regs->rr[0] |= TX_READY; | |
515 | xmit_regs->rr[1] |= ALL_SENT; | |
516 | ||
517 | if (TX_INT_ENABLED(msp, portnum)) { | |
518 | channel_regs *channelA = &(msp->scc.port[PORT_A]); | |
519 | channel_regs *channelB = &(msp->scc.port[PORT_B]); | |
520 | ||
521 | /* | |
522 | * if generating interrupts in Vector Include Status mode, | |
523 | * the interrupt vector on channelB includes status in the | |
524 | * lower nibble which is characteristic of the event | |
525 | * causing the interrupt. | |
526 | */ | |
527 | if (xmit_regs->wr[9] & VIS) { | |
528 | /* | |
529 | * if no interrupts are pending, clear out | |
530 | * vector include status bits before or'ing in new state | |
531 | * (channelA.rr3) is the interrupt pending register | |
532 | * (channelB.rr2) is the interrupt vector (VIS) register | |
533 | */ | |
534 | if (!channelA->rr[3]) | |
535 | /* clearing idle "special rcv condition" bits */ | |
536 | channelB->rr[2] &= ~VIS_LO_MASK; | |
537 | ||
538 | /* add "Tx ready" to status vector */ | |
539 | SET_VIS_TX_BUF_EMPTY(msp, portnum); | |
540 | } | |
541 | ||
542 | /* assert serial interrupt */ | |
543 | SET_TX_INT_PENDING(msp, portnum); | |
544 | //dev_interrupt_in(TRUE, device, 0); | |
545 | pciMaster_set_int(0,true); | |
546 | } | |
547 | } | |
548 | ||
549 | /* | |
550 | * Channel Command Register (WR0) | |
551 | * - executes various channel/port commands | |
552 | */ | |
553 | void | |
554 | Serial::channel_cmd(int portnum) | |
555 | { | |
556 | channel_regs *channelp = &(msp->scc.port[portnum]); | |
557 | channel_regs *channelA = &(msp->scc.port[PORT_A]); | |
558 | channel_regs *channelB = &(msp->scc.port[PORT_B]); | |
559 | int otherport; | |
560 | ||
561 | if (portnum == PORT_A) { | |
562 | otherport = PORT_B; | |
563 | } else if (portnum == PORT_B) { | |
564 | otherport = PORT_A; | |
565 | } | |
566 | ||
567 | /* | |
568 | * individual reset functions used by | |
569 | * kernel zs drvier: | |
570 | * reset external/status interrupts (0x10) | |
571 | * reset TxINT pending (0x28) | |
572 | * error reset (0x30) | |
573 | * reset highest Interrupt Under Service (0x38) | |
574 | */ | |
575 | switch (channelp->wr[0] & CMDCODE_MASK) { | |
576 | case ENABLE_RX_INT: | |
577 | /* | |
578 | * enable int on next Rx, used for 1st receive interrupt | |
579 | * ignore for now ! | |
580 | */ | |
581 | break; | |
582 | ||
583 | case RESET_XS_INT: | |
584 | /* | |
585 | * reset external/status interrupts (0x10) | |
586 | * (channelB RR2) is the interrupt vector (VIS) register | |
587 | * (channelA RR3) is the interrupt pending register | |
588 | */ | |
589 | ||
590 | /* reset ext/status interrrupt */ | |
591 | CLEAR_XS_INT_PENDING(msp, portnum); | |
592 | ||
593 | /* | |
594 | * if no XS interrupts pending for other channel, | |
595 | * clear Ext/Status Vector Include Status bit | |
596 | */ | |
597 | if (!XS_INT_PENDING(msp, otherport)) | |
598 | CLEAR_VIS_XS_CHANGE(msp); | |
599 | break; | |
600 | ||
601 | case RESET_TX_INT: | |
602 | /* | |
603 | * Reset TxINT Pending (0x28) | |
604 | * (channelB RR2) is the interrupt vector (VIS) register | |
605 | * (channelA RR3) is the interrupt pending register | |
606 | */ | |
607 | ||
608 | /* reset transmit interrrupt */ | |
609 | CLEAR_TX_INT_PENDING(msp, portnum); | |
610 | break; | |
611 | ||
612 | case RESET_ERROR: | |
613 | /* | |
614 | * error reset (0x30) | |
615 | */ | |
616 | channelB->rr[2] = 0; | |
617 | channelA->rr[3] = 0; | |
618 | break; | |
619 | ||
620 | case RESET_IUS: | |
621 | /* | |
622 | * Reset Highest Interrupt Under Service command (0x38), | |
623 | * clear respective bits in interrupt vector (RR2), | |
624 | * and the interrupt pending register (RR3). | |
625 | * Note, only channelB contains VIS bits, and | |
626 | * only channelA contains the IP bits. | |
627 | */ | |
628 | ||
629 | /* clear the highest pending interrupt */ | |
630 | if (RCV_INT_PENDING(msp, portnum)) { | |
631 | CLEAR_RCV_INT_PENDING(msp, portnum); | |
632 | ||
633 | /* | |
634 | * if no Rx interrupts pending for other channel, | |
635 | * clear Rx Vector Include Status bit | |
636 | */ | |
637 | if (!RCV_INT_PENDING(msp, otherport)) | |
638 | CLEAR_VIS_RX_CHAR_AVAIL(msp); | |
639 | ||
640 | } else if (TX_INT_PENDING(msp, portnum)) { | |
641 | CLEAR_TX_INT_PENDING(msp, portnum); | |
642 | ||
643 | /* | |
644 | * no VIS bits to clear for Tx interrupts | |
645 | */ | |
646 | ||
647 | } else if (XS_INT_PENDING(msp, portnum)) { | |
648 | CLEAR_XS_INT_PENDING(msp, portnum); | |
649 | ||
650 | /* | |
651 | * if no XS interrupts pending for other channel, | |
652 | * clear Ext/Status Vector Include Status bit | |
653 | */ | |
654 | if (!XS_INT_PENDING(msp, otherport)) | |
655 | CLEAR_VIS_XS_CHANGE(msp); | |
656 | } | |
657 | break; | |
658 | ||
659 | default: | |
660 | debug_err("%s: blaze warning: unsupported SCC/wr[0] command 0x%x\n", HERE, | |
661 | channelp->wr[0]); | |
662 | } | |
663 | ||
664 | /* | |
665 | * if no more interrupts pending for channelA, | |
666 | * clear the Vector Include Status channel bit | |
667 | */ | |
668 | if (!(channelA->rr[3] & A_IP_MASK)) | |
669 | channelB->rr[2] &= ~CHAN_A; | |
670 | ||
671 | /* | |
672 | * if no interrupts are pending in RR3, de-assert the serial interrupt. | |
673 | * if WR9 (master interrupt contorl) is in VIS mode, | |
674 | * force the VIS bits to be: V3,V2,V1 = 011 "special receive condition" | |
675 | * (...we currently *ONLY* support status low bits) | |
676 | * | |
677 | * (channelB RR2) is the interrupt vector (VIS) register | |
678 | * (channelA RR3) is the interrupt pending register | |
679 | */ | |
680 | if (channelA->rr[3] == 0) { | |
681 | if (channelp->wr[9] & VIS) | |
682 | channelB->rr[2] = SRC; /* special receive condition */ | |
683 | /* Deasserting the interrupt */ | |
684 | //dev_interrupt_in(FALSE,device, 0); | |
685 | pciMaster_set_int(0,false); | |
686 | } | |
687 | ||
688 | /* | |
689 | * After a reset request has been performed, | |
690 | * clear the WR0 (command register) | |
691 | * (including special resets, point high, and register indexes, ...) | |
692 | */ | |
693 | channelp->wr[0] = 0; | |
694 | } | |
695 | ||
696 | /* | |
697 | * Get the serial status | |
698 | */ | |
699 | void | |
700 | Serial::status(int portnum) | |
701 | { | |
702 | channel_regs *channelp = &(msp->scc.port[portnum]); | |
703 | ||
704 | /* if External/Status Master Interrupt Enable'd */ | |
705 | if (channelp->wr[1] & XS_INT_ENABLED) { | |
706 | channel_regs *channelA = &(msp->scc.port[PORT_A]); | |
707 | channel_regs *channelB = &(msp->scc.port[PORT_B]); | |
708 | ||
709 | /* | |
710 | * if we're generating interrupts in Vector Include Status | |
711 | * mode, the interrupt vector on channelB includes status | |
712 | * in the lower nibble which is characteristic of the | |
713 | * event causing the interrupt. | |
714 | */ | |
715 | if (channelp->wr[9] & VIS) { | |
716 | /* | |
717 | * if no interrupts are pending, clear out vector | |
718 | * include status bits before or'ing in new state | |
719 | * (channelA.rr3) is the interrupt pending register | |
720 | * (channelB.rr2) is the interrupt vector (VIS) register | |
721 | */ | |
722 | if (!channelA->rr[3]) | |
723 | /* clearing idle "special rcv condition" bits */ | |
724 | channelB->rr[2] &= ~VIS_LO_MASK; | |
725 | ||
726 | /* add "external/status change" to status vector */ | |
727 | SET_VIS_XS_CHANGE(msp, portnum); | |
728 | } | |
729 | ||
730 | /* assert "external/status change" in rr3 */ | |
731 | SET_XS_INT_PENDING(msp, portnum); | |
732 | ||
733 | /* assert serial interrupt */ | |
734 | //dev_interrupt_in(TRUE, device, 0); | |
735 | pciMaster_set_int(0,true); | |
736 | } | |
737 | } | |
738 | ||
739 | // | |
740 | // Insert the following string into the incomming stream for PORTA, the | |
741 | // console. | |
742 | // | |
743 | void | |
744 | Serial::chars_send(char *str, int portHandle) | |
745 | { | |
746 | int len = (int)strlen(str); | |
747 | int i; | |
748 | int portnum = mapHandle2Port(portHandle); | |
749 | ||
750 | for (i = 0; i < len; i++) { | |
751 | /* Jam them one at a time into the input buffer */ | |
752 | char_input(str[i], portnum); | |
753 | } | |
754 | ||
755 | } | |
756 | ||
757 | bool | |
758 | Serial::dump(FILE* fp) | |
759 | { | |
760 | int portnum; | |
761 | struct channel_regs *rcv_regs; | |
762 | debug_err("%s: dump\n", HERE); | |
763 | if (fwrite((char*)msp, sizeof(serial_structT), 1, fp) != 1) { | |
764 | debug_err("%s: serial_dump: fwrite failed\n", HERE); | |
765 | return FALSE; | |
766 | } | |
767 | ||
768 | /* Dump queue of chars waiting on each port. */ | |
769 | ||
770 | for (portnum = 0 ; portnum < NUM_PORTS ; portnum++) { | |
771 | rcv_regs = &(msp->scc.port[portnum]); | |
772 | while (rcv_regs->qcnt) { | |
773 | putc(deq(rcv_regs), fp); | |
774 | } | |
775 | } | |
776 | return genericPciDev::dump(DR_get_dir(),getName()); | |
777 | } | |
778 | ||
779 | bool | |
780 | Serial::restore(FILE* fp) | |
781 | { | |
782 | ||
783 | int portnum; | |
784 | struct channel_regs *rcv_regs; | |
785 | int count; | |
786 | ||
787 | ||
788 | if (fread((char*)msp, sizeof(serial_structT), 1, fp) != 1) { | |
789 | debug_err("%s: serial_restore: fread failed\n", HERE); | |
790 | return FALSE; | |
791 | } | |
792 | ||
793 | /* Restore queue of chars waiting on each port. */ | |
794 | for (portnum = 0 ; portnum < NUM_PORTS ; portnum++) { | |
795 | rcv_regs = &(msp->scc.port[portnum]); | |
796 | /* | |
797 | * # chars to load is in qcnt of state. Load them in but | |
798 | * first set queue to empty state since the characters | |
799 | * really aren't there yet. | |
800 | */ | |
801 | count = rcv_regs->qcnt; /* save count */ | |
802 | rcv_regs->qcnt = 0; | |
803 | rcv_regs->q.next = NULL; | |
804 | ||
805 | while (count--) { | |
806 | enq(rcv_regs, getc(fp)); | |
807 | } | |
808 | } | |
809 | ||
810 | if (dump_version() >= 5) | |
811 | return genericPciDev::restore(DR_get_dir(),getName()); | |
812 | else { | |
813 | // slot_irl[0] = 54; | |
814 | return TRUE; | |
815 | } | |
816 | } |