|
|
1.1 root 1: /*
2: * Copyright (c) 2000 Apple Computer, Inc. All rights reserved.
3: *
4: * @APPLE_LICENSE_HEADER_START@
5: *
6: * The contents of this file constitute Original Code as defined in and
7: * are subject to the Apple Public Source License Version 1.1 (the
8: * "License"). You may not use this file except in compliance with the
9: * License. Please obtain a copy of the License at
10: * http://www.apple.com/publicsource and read it before using this file.
11: *
12: * This Original Code and all software distributed under the License are
13: * distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, EITHER
14: * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
15: * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
16: * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT. Please see the
17: * License for the specific language governing rights and limitations
18: * under the License.
19: *
20: * @APPLE_LICENSE_HEADER_END@
21: */
22: /*
23: * @OSF_COPYRIGHT@
24: */
25: /*
26: * @APPLE_FREE_COPYRIGHT@
27: */
28: /*
29: * Mach Operating System
30: * Copyright (c) 1991,1990,1989 Carnegie Mellon University
31: * All Rights Reserved.
32: *
33: * Permission to use, copy, modify and distribute this software and its
34: * documentation is hereby granted, provided that both the copyright
35: * notice and this permission notice appear in all copies of the
36: * software, derivative works or modified versions, and any portions
37: * thereof, and that both notices appear in supporting documentation.
38: *
39: * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
40: * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
41: * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
42: *
43: * Carnegie Mellon requests users of this software to return to
44: *
45: * Software Distribution Coordinator or [email protected]
46: * School of Computer Science
47: * Carnegie Mellon University
48: * Pittsburgh PA 15213-3890
49: *
50: * any improvements or extensions that they make and grant Carnegie Mellon
51: * the rights to redistribute these changes.
52: */
53: /*
54: */
55: /*
56: * File: scc_8530_hdw.c
57: * Author: Alessandro Forin, Carnegie Mellon University
58: * Date: 6/91
59: *
60: * Hardware-level operations for the SCC Serial Line Driver
61: */
62:
63: #define NSCC 1 /* Number of serial chips, two ports per chip. */
64: #if NSCC > 0
65:
66: #include <mach_kdb.h>
67: #include <platforms.h>
68: #include <kern/spl.h>
69: #include <mach/std_types.h>
70: #include <types.h>
71: #include <sys/syslog.h>
72: #include <ppc/misc_protos.h>
73: #include <ppc/proc_reg.h>
74: #include <ppc/exception.h>
75: #include <ppc/Firmware.h>
76: #include <ppc/POWERMAC/serial_io.h>
77: #include <pexpert/ppc/powermac.h>
78: #include <ppc/POWERMAC/scc_8530.h>
79:
80: #if MACH_KDB
81: #include <machine/db_machdep.h>
82: #endif /* MACH_KDB */
83:
84: #define kdebug_state() (1)
85: #define delay(x) { volatile int _d_; for (_d_ = 0; _d_ < (10000*x); _d_++) ; }
86:
87: #define NSCC_LINE 2 /* 2 ttys per chip */
88:
89: #define SCC_DMA_TRANSFERS 0
90:
91: struct scc_tty scc_tty[NSCC_LINE];
92:
93: #define scc_tty_for(chan) (&scc_tty[chan])
94: /* #define scc_unit(dev_no) (dev_no) */
95:
96: #define scc_dev_no(chan) ((chan)^0x01)
97: #define scc_chan(dev_no) ((dev_no)^0x01)
98:
99: int serial_initted = 0;
100: unsigned int scc_parm_done = 0; /* (TEST/DEBUG) */
101:
102: static struct scc_byte {
103: unsigned char reg;
104: unsigned char val;
105: } scc_init_hw[] = {
106:
107: 9, 0x80,
108: 4, 0x44,
109: 3, 0xC0,
110: 5, 0xE2,
111: 2, 0x00,
112: 10, 0x00,
113: 11, 0x50,
114: 12, 0x0A,
115: 13, 0x00,
116: 3, 0xC1,
117: 5, 0xEA,
118: 14, 0x01,
119: 15, 0x00,
120: 0, 0x10,
121: 0, 0x10,
122: #if 0
123: 1, 0x12, /* int or Rx, Tx int enable */
124: #else
125: 1, 0x10, /* int or Rx, no Tx int enable */
126: #endif
127: 9, 0x0A
128: };
129:
130: static int scc_init_hw_count = sizeof(scc_init_hw)/sizeof(scc_init_hw[0]);
131:
132: enum scc_error {SCC_ERR_NONE, SCC_ERR_PARITY, SCC_ERR_BREAK, SCC_ERR_OVERRUN};
133:
134:
135: /*
136: * BRG formula is:
137: * ClockFrequency (115200 for Power Mac)
138: * BRGconstant = --------------------------- - 2
139: * BaudRate
140: */
141:
142: #define SERIAL_CLOCK_FREQUENCY (115200*2) /* Power Mac value */
143: #define convert_baud_rate(rate) ((((SERIAL_CLOCK_FREQUENCY) + (rate)) / (2 * (rate))) - 2)
144:
145: #define DEFAULT_SPEED 38400
146: #define DEFAULT_FLAGS (TF_LITOUT|TF_ECHO)
147:
148: int scc_param(struct scc_tty *tp);
149:
150:
151: struct scc_softc scc_softc[NSCC];
152: caddr_t scc_std[NSCC] = { (caddr_t) 0};
153:
154:
155: #define SCC_RR1_ERRS (SCC_RR1_FRAME_ERR|SCC_RR1_RX_OVERRUN|SCC_RR1_PARITY_ERR)
156: #define SCC_RR3_ALL (SCC_RR3_RX_IP_A|SCC_RR3_TX_IP_A|SCC_RR3_EXT_IP_A|\
157: SCC_RR3_RX_IP_B|SCC_RR3_TX_IP_B|SCC_RR3_EXT_IP_B)
158:
159: #define DEBUG_SCC
160: #undef DEBUG_SCC
161:
162: #ifdef DEBUG_SCC
163: static int total_chars, total_ints, total_overruns, total_errors, num_ints, max_chars;
164: static int chars_received[8];
165: static int __SCC_STATS = 0;
166: static int max_in_q = 0;
167: static int max_out_q = 0;
168: #endif
169:
170: DECL_FUNNEL(, scc_funnel) /* funnel to serialize the SCC driver */
171: boolean_t scc_funnel_initted = FALSE;
172: #define SCC_FUNNEL scc_funnel
173: #define SCC_FUNNEL_INITTED scc_funnel_initted
174:
175:
176: /*
177: * Adapt/Probe/Attach functions
178: */
179: boolean_t scc_uses_modem_control = FALSE;/* patch this with adb */
180: decl_simple_lock_data(,scc_stomp) /* (TEST/DEBUG) */
181:
182: /* This is called VERY early on in the init and therefore has to have
183: * hardcoded addresses of the serial hardware control registers. The
184: * serial line may be needed for console and debugging output before
185: * anything else takes place
186: */
187:
188: void
189: initialize_serial( caddr_t scc_phys_base )
190: {
191: int i, chan, bits;
192: scc_regmap_t regs;
193: DECL_FUNNEL_VARS
194:
195: assert( scc_phys_base );
196:
197: if (!SCC_FUNNEL_INITTED) {
198: FUNNEL_INIT(&SCC_FUNNEL, master_processor);
199: SCC_FUNNEL_INITTED = TRUE;
200: }
201: FUNNEL_ENTER(&SCC_FUNNEL);
202:
203: if (serial_initted) {
204: FUNNEL_EXIT(&SCC_FUNNEL);
205: return;
206: }
207:
208: simple_lock_init(&scc_stomp, FALSE); /* (TEST/DEBUG) */
209:
210: scc_softc[0].full_modem = TRUE;
211:
212: scc_std[0] = scc_phys_base;
213:
214: regs = scc_softc[0].regs = (scc_regmap_t)scc_std[0];
215:
216: for (chan = 0; chan < NSCC_LINE; chan++) {
217: if (chan == 1)
218: scc_init_hw[0].val = 0x80;
219:
220: for (i = 0; i < scc_init_hw_count; i++) {
221: scc_write_reg(regs, chan,
222: scc_init_hw[i].reg, scc_init_hw[i].val);
223: }
224: }
225:
226: /* Call probe so we are ready very early for remote gdb and for serial
227: console output if appropriate. */
228: if (scc_probe()) {
229: for (i = 0; i < NSCC_LINE; i++) {
230: scc_softc[0].softr[i].wr5 = SCC_WR5_DTR | SCC_WR5_RTS;
231: scc_param(scc_tty_for(i));
232: /* Enable SCC interrupts (how many interrupts are to this thing?!?) */
233: scc_write_reg(regs, i, 9, SCC_WR9_NV);
234:
235: scc_read_reg_zero(regs, 0, bits);/* Clear the status */
236: }
237: scc_parm_done = 1; /* (TEST/DEBUG) */
238: }
239:
240: serial_initted = TRUE;
241:
242: FUNNEL_EXIT(&SCC_FUNNEL);
243: return;
244: }
245:
246: int
247: scc_probe(void)
248: {
249: scc_softc_t scc;
250: register int val, i;
251: register scc_regmap_t regs;
252: spl_t s;
253: DECL_FUNNEL_VARS
254:
255: if (!SCC_FUNNEL_INITTED) {
256: FUNNEL_INIT(&SCC_FUNNEL, master_processor);
257: SCC_FUNNEL_INITTED = TRUE;
258: }
259: FUNNEL_ENTER(&SCC_FUNNEL);
260:
261: /* Readjust the I/O address to handling
262: * new memory mappings.
263: */
264:
265: // scc_std[0] = POWERMAC_IO(scc_std[0]);
266:
267: regs = (scc_regmap_t)scc_std[0];
268:
269: if (regs == (scc_regmap_t) 0) {
270: FUNNEL_EXIT(&SCC_FUNNEL);
271: return 0;
272: }
273:
274: scc = &scc_softc[0];
275: scc->regs = regs;
276:
277: s = splhigh();
278:
279: for (i = 0; i < NSCC_LINE; i++) {
280: register struct scc_tty *tp;
281: tp = scc_tty_for(i);
282: tp->t_addr = (char*)(0x80000000L + (i&1));
283: /* Set default values. These will be overridden on
284: open but are needed if the port will be used
285: independently of the Mach interfaces, e.g., for
286: gdb or for a serial console. */
287: tp->t_ispeed = DEFAULT_SPEED;
288: tp->t_ospeed = DEFAULT_SPEED;
289: tp->t_flags = DEFAULT_FLAGS;
290: scc->softr[i].speed = -1;
291:
292: /* do min buffering */
293: tp->t_state |= TS_MIN;
294:
295: tp->t_dev = scc_dev_no(i);
296: }
297:
298: splx(s);
299:
300: FUNNEL_EXIT(&SCC_FUNNEL);
301: return 1;
302: }
303:
304: /*
305: * Get a char from a specific SCC line
306: * [this is only used for console&screen purposes]
307: * must be splhigh since it may be called from another routine under spl
308: */
309:
310: int
311: scc_getc(int unit, int line, boolean_t wait, boolean_t raw)
312: {
313: register scc_regmap_t regs;
314: unsigned char c, value;
315: int rcvalue, from_line;
316: spl_t s = splhigh();
317: DECL_FUNNEL_VARS
318:
319: FUNNEL_ENTER(&SCC_FUNNEL);
320:
321: simple_lock(&scc_stomp); /* (TEST/DEBUG) */
322: regs = scc_softc[0].regs;
323:
324: /*
325: * wait till something available
326: *
327: */
328: again:
329: rcvalue = 0;
330: while (1) {
331: scc_read_reg_zero(regs, line, value);
332:
333: if (value & SCC_RR0_RX_AVAIL)
334: break;
335:
336: if (!wait) {
337: simple_unlock(&scc_stomp); /* (TEST/DEBUG) */
338: splx(s);
339: FUNNEL_EXIT(&SCC_FUNNEL);
340: return -1;
341: }
342: }
343:
344: /*
345: * if nothing found return -1
346: */
347:
348: scc_read_reg(regs, line, SCC_RR1, value);
349: scc_read_data(regs, line, c);
350:
351: #if MACH_KDB
352: if (console_is_serial() &&
353: c == ('_' & 0x1f)) {
354: /* Drop into the debugger */
355: simple_unlock(&scc_stomp); /* (TEST/DEBUG) */
356: Debugger("Serial Line Request");
357: simple_lock(&scc_stomp); /* (TEST/DEBUG) */
358: scc_write_reg(regs, line, SCC_RR0, SCC_RESET_HIGHEST_IUS);
359: if (wait) {
360: goto again;
361: }
362: simple_unlock(&scc_stomp); /* (TEST/DEBUG) */
363: splx(s);
364: FUNNEL_EXIT(&SCC_FUNNEL);
365: return -1;
366: }
367: #endif /* MACH_KDB */
368:
369: /*
370: * bad chars not ok
371: */
372: if (value&(SCC_RR1_PARITY_ERR | SCC_RR1_RX_OVERRUN | SCC_RR1_FRAME_ERR)) {
373: scc_write_reg(regs, line, SCC_RR0, SCC_RESET_ERROR);
374:
375: if (wait) {
376: scc_write_reg(regs, line, SCC_RR0, SCC_RESET_HIGHEST_IUS);
377: goto again;
378: }
379: }
380:
381: scc_write_reg(regs, line, SCC_RR0, SCC_RESET_HIGHEST_IUS);
382:
383: simple_unlock(&scc_stomp); /* (TEST/DEBUG) */
384: splx(s);
385:
386: FUNNEL_EXIT(&SCC_FUNNEL);
387: return c;
388: }
389:
390: /*
391: * Put a char on a specific SCC line
392: * use splhigh since we might be doing a printf in high spl'd code
393: */
394:
395: int
396: scc_putc(int unit, int line, int c)
397: {
398: scc_regmap_t regs;
399: spl_t s = splhigh();
400: unsigned char value;
401: DECL_FUNNEL_VARS
402:
403: FUNNEL_ENTER(&SCC_FUNNEL);
404: simple_lock(&scc_stomp); /* (TEST/DEBUG) */
405:
406: regs = scc_softc[0].regs;
407:
408: do {
409: scc_read_reg(regs, line, SCC_RR0, value);
410: if (value & SCC_RR0_TX_EMPTY)
411: break;
412: delay(1);
413: } while (1);
414:
415: scc_write_data(regs, line, c);
416: /* wait for it to swallow the char ? */
417:
418: do {
419: scc_read_reg(regs, line, SCC_RR0, value);
420: if (value & SCC_RR0_TX_EMPTY)
421: break;
422: } while (1);
423: scc_write_reg(regs, line, SCC_RR0, SCC_RESET_HIGHEST_IUS);
424: simple_unlock(&scc_stomp); /* (TEST/DEBUG) */
425:
426: splx(s);
427:
428: FUNNEL_EXIT(&SCC_FUNNEL);
429: return 0;
430: }
431:
432:
433: void
434: powermac_scc_set_datum(scc_regmap_t regs, unsigned int offset, unsigned char value)
435: {
436: volatile unsigned char *address = (unsigned char *) regs + offset;
437:
438: assert(FUNNEL_IN_USE(&SCC_FUNNEL));
439:
440: *address = value;
441: eieio();
442:
443: assert(FUNNEL_IN_USE(&SCC_FUNNEL));
444: }
445:
446: unsigned char
447: powermac_scc_get_datum(scc_regmap_t regs, unsigned int offset)
448: {
449: volatile unsigned char *address = (unsigned char *) regs + offset;
450: unsigned char value;
451:
452: assert(FUNNEL_IN_USE(&SCC_FUNNEL));
453:
454: value = *address; eieio();
455: return value;
456:
457: assert(FUNNEL_IN_USE(&SCC_FUNNEL));
458: }
459:
460: int
461: scc_param(struct scc_tty *tp)
462: {
463: scc_regmap_t regs;
464: unsigned char value;
465: unsigned short speed_value;
466: int bits, chan;
467: spl_t s;
468: struct scc_softreg *sr;
469: scc_softc_t scc;
470:
471: assert(FUNNEL_IN_USE(&SCC_FUNNEL));
472:
473: s = splhigh();
474: simple_lock(&scc_stomp); /* (TEST/DEBUG) */
475:
476: chan = scc_chan(tp->t_dev);
477: scc = &scc_softc[0];
478: regs = scc->regs;
479:
480: sr = &scc->softr[chan];
481:
482: /* Do a quick check to see if the hardware needs to change */
483: if ((sr->flags & (TF_ODDP|TF_EVENP)) == (tp->t_flags & (TF_ODDP|TF_EVENP))
484: && sr->speed == tp->t_ispeed) {
485: assert(FUNNEL_IN_USE(&SCC_FUNNEL));
486: simple_unlock(&scc_stomp); /* (TEST/DEBUG) */
487: splx(s); /* (TEST/DEBUG) */
488: return 0; /* (TEST/DEBUG) */
489: }
490:
491: if(scc_parm_done) {
492:
493: scc_write_reg(regs, chan, 3, SCC_WR3_RX_8_BITS|SCC_WR3_RX_ENABLE); /* (TEST/DEBUG) */
494: sr->wr1 = SCC_WR1_RXI_FIRST_CHAR | SCC_WR1_EXT_IE; /* (TEST/DEBUG) */
495: scc_write_reg(regs, chan, 1, sr->wr1); /* (TEST/DEBUG) */
496: scc_write_reg(regs, chan, 15, SCC_WR15_ENABLE_ESCC); /* (TEST/DEBUG) */
497: scc_write_reg(regs, chan, 7, SCC_WR7P_RX_FIFO); /* (TEST/DEBUG) */
498: scc_write_reg(regs, chan, 0, SCC_IE_NEXT_CHAR); /* (TEST/DEBUG) */
499: scc_write_reg(regs, chan, 0, SCC_RESET_EXT_IP); /* (TEST/DEBUG) */
500: scc_write_reg(regs, chan, 0, SCC_RESET_EXT_IP); /* (TEST/DEBUG) */
501: scc_write_reg(regs, chan, 9, SCC_WR9_MASTER_IE|SCC_WR9_NV); /* (TEST/DEBUG) */
502: scc_read_reg_zero(regs, 0, bits); /* (TEST/DEBUG) */
503: sr->wr1 = SCC_WR1_RXI_FIRST_CHAR | SCC_WR1_EXT_IE; /* (TEST/DEBUG) */
504: scc_write_reg(regs, chan, 1, sr->wr1); /* (TEST/DEBUG) */
505: scc_write_reg(regs, chan, 0, SCC_IE_NEXT_CHAR); /* (TEST/DEBUG) */
506: simple_unlock(&scc_stomp); /* (TEST/DEBUG) */
507: splx(s); /* (TEST/DEBUG) */
508: return 0; /* (TEST/DEBUG) */
509: }
510:
511: sr->flags = tp->t_flags;
512: sr->speed = tp->t_ispeed;
513:
514:
515: if (tp->t_ispeed == 0) {
516: sr->wr5 &= ~SCC_WR5_DTR;
517: scc_write_reg(regs, chan, 5, sr->wr5);
518: simple_unlock(&scc_stomp); /* (TEST/DEBUG) */
519: splx(s);
520:
521: assert(FUNNEL_IN_USE(&SCC_FUNNEL));
522: return 0;
523: }
524:
525:
526: #if SCC_DMA_TRANSFERS
527: if (scc->dma_initted & (1<<chan))
528: scc->dma_ops->scc_dma_reset_rx(chan);
529: #endif
530:
531: value = SCC_WR4_1_STOP;
532:
533: /*
534: * For 115K the clocking divide changes to 64.. to 230K will
535: * start at the normal clock divide 16.
536: *
537: * However, both speeds will pull from a different clocking
538: * source
539: */
540:
541: if (tp->t_ispeed == 115200)
542: value |= SCC_WR4_CLK_x32;
543: else
544: value |= SCC_WR4_CLK_x16 ;
545:
546: /* .. and parity */
547: if ((tp->t_flags & (TF_ODDP | TF_EVENP)) == TF_EVENP)
548: value |= (SCC_WR4_EVEN_PARITY | SCC_WR4_PARITY_ENABLE);
549: else if ((tp->t_flags & (TF_ODDP | TF_EVENP)) == TF_ODDP)
550: value |= SCC_WR4_PARITY_ENABLE;
551:
552: /* set it now, remember it must be first after reset */
553: sr->wr4 = value;
554:
555: /* Program Parity, and Stop bits */
556: scc_write_reg(regs, chan, 4, sr->wr4);
557:
558: /* Setup for 8 bits */
559: scc_write_reg(regs, chan, 3, SCC_WR3_RX_8_BITS);
560:
561: // Set DTR, RTS, and transmitter bits/character.
562: sr->wr5 = SCC_WR5_TX_8_BITS | SCC_WR5_RTS | SCC_WR5_DTR;
563:
564: scc_write_reg(regs, chan, 5, sr->wr5);
565:
566: scc_write_reg(regs, chan, 14, 0); /* Disable baud rate */
567:
568: /* Setup baud rate 57.6Kbps, 115K, 230K should all yeild
569: * a converted baud rate of zero
570: */
571: speed_value = convert_baud_rate(tp->t_ispeed);
572:
573: if (speed_value == 0xffff)
574: speed_value = 0;
575:
576: scc_set_timing_base(regs, chan, speed_value);
577:
578: if (tp->t_ispeed == 115200 || tp->t_ispeed == 230400) {
579: /* Special case here.. change the clock source*/
580: scc_write_reg(regs, chan, 11, 0);
581: /* Baud rate generator is disabled.. */
582: } else {
583: scc_write_reg(regs, chan, 11, SCC_WR11_RCLK_BAUDR|SCC_WR11_XTLK_BAUDR);
584: /* Enable the baud rate generator */
585: scc_write_reg(regs, chan, 14, SCC_WR14_BAUDR_ENABLE);
586: }
587:
588:
589: scc_write_reg(regs, chan, 3, SCC_WR3_RX_8_BITS|SCC_WR3_RX_ENABLE);
590:
591:
592: sr->wr1 = SCC_WR1_RXI_FIRST_CHAR | SCC_WR1_EXT_IE;
593: scc_write_reg(regs, chan, 1, sr->wr1);
594: scc_write_reg(regs, chan, 15, SCC_WR15_ENABLE_ESCC);
595: scc_write_reg(regs, chan, 7, SCC_WR7P_RX_FIFO);
596: scc_write_reg(regs, chan, 0, SCC_IE_NEXT_CHAR);
597:
598:
599: /* Clear out any pending external or status interrupts */
600: scc_write_reg(regs, chan, 0, SCC_RESET_EXT_IP);
601: scc_write_reg(regs, chan, 0, SCC_RESET_EXT_IP);
602: //scc_write_reg(regs, chan, 0, SCC_RESET_ERROR);
603:
604: /* Enable SCC interrupts (how many interrupts are to this thing?!?) */
605: scc_write_reg(regs, chan, 9, SCC_WR9_MASTER_IE|SCC_WR9_NV);
606:
607: scc_read_reg_zero(regs, 0, bits);/* Clear the status */
608:
609: #if SCC_DMA_TRANSFERS
610: if (scc->dma_initted & (1<<chan)) {
611: scc->dma_ops->scc_dma_start_rx(chan);
612: scc->dma_ops->scc_dma_setup_8530(chan);
613: } else
614: #endif
615: {
616: sr->wr1 = SCC_WR1_RXI_FIRST_CHAR | SCC_WR1_EXT_IE;
617: scc_write_reg(regs, chan, 1, sr->wr1);
618: scc_write_reg(regs, chan, 0, SCC_IE_NEXT_CHAR);
619: }
620:
621: sr->wr5 |= SCC_WR5_TX_ENABLE;
622: scc_write_reg(regs, chan, 5, sr->wr5);
623:
624: simple_unlock(&scc_stomp); /* (TEST/DEBUG) */
625: splx(s);
626:
627: assert(FUNNEL_IN_USE(&SCC_FUNNEL));
628: return 0;
629:
630: }
631:
632: #endif /* NSCC > 0 */
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.