]> git.saurik.com Git - apple/xnu.git/blob - osfmk/ppc/POWERMAC/serial_io.c
xnu-344.32.tar.gz
[apple/xnu.git] / osfmk / ppc / POWERMAC / serial_io.c
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 Software.Distribution@CS.CMU.EDU
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 extern unsigned int disableSerialOuput;
100
101 int serial_initted = 0;
102 unsigned int scc_parm_done = 0; /* (TEST/DEBUG) */
103
104 static struct scc_byte {
105 unsigned char reg;
106 unsigned char val;
107 } scc_init_hw[] = {
108
109 9, 0x80,
110 4, 0x44,
111 3, 0xC0,
112 5, 0xE2,
113 2, 0x00,
114 10, 0x00,
115 11, 0x50,
116 12, 0x0A,
117 13, 0x00,
118 3, 0xC1,
119 5, 0xEA,
120 14, 0x01,
121 15, 0x00,
122 0, 0x10,
123 0, 0x10,
124 #if 0
125 1, 0x12, /* int or Rx, Tx int enable */
126 #else
127 1, 0x10, /* int or Rx, no Tx int enable */
128 #endif
129 9, 0x0A
130 };
131
132 static int scc_init_hw_count = sizeof(scc_init_hw)/sizeof(scc_init_hw[0]);
133
134 enum scc_error {SCC_ERR_NONE, SCC_ERR_PARITY, SCC_ERR_BREAK, SCC_ERR_OVERRUN};
135
136
137 /*
138 * BRG formula is:
139 * ClockFrequency (115200 for Power Mac)
140 * BRGconstant = --------------------------- - 2
141 * BaudRate
142 */
143
144 #define SERIAL_CLOCK_FREQUENCY (115200*2) /* Power Mac value */
145 #define convert_baud_rate(rate) ((((SERIAL_CLOCK_FREQUENCY) + (rate)) / (2 * (rate))) - 2)
146
147 #define DEFAULT_SPEED 57600
148 #define DEFAULT_FLAGS (TF_LITOUT|TF_ECHO)
149
150 int scc_param(struct scc_tty *tp);
151
152
153 struct scc_softc scc_softc[NSCC];
154 caddr_t scc_std[NSCC] = { (caddr_t) 0};
155
156
157 #define SCC_RR1_ERRS (SCC_RR1_FRAME_ERR|SCC_RR1_RX_OVERRUN|SCC_RR1_PARITY_ERR)
158 #define SCC_RR3_ALL (SCC_RR3_RX_IP_A|SCC_RR3_TX_IP_A|SCC_RR3_EXT_IP_A|\
159 SCC_RR3_RX_IP_B|SCC_RR3_TX_IP_B|SCC_RR3_EXT_IP_B)
160
161 #define DEBUG_SCC
162 #undef DEBUG_SCC
163
164 #ifdef DEBUG_SCC
165 static int total_chars, total_ints, total_overruns, total_errors, num_ints, max_chars;
166 static int chars_received[8];
167 static int __SCC_STATS = 0;
168 static int max_in_q = 0;
169 static int max_out_q = 0;
170 #endif
171
172 DECL_FUNNEL(, scc_funnel) /* funnel to serialize the SCC driver */
173 boolean_t scc_funnel_initted = FALSE;
174 #define SCC_FUNNEL scc_funnel
175 #define SCC_FUNNEL_INITTED scc_funnel_initted
176
177
178 /*
179 * Adapt/Probe/Attach functions
180 */
181 boolean_t scc_uses_modem_control = FALSE;/* patch this with adb */
182 decl_simple_lock_data(,scc_stomp) /* (TEST/DEBUG) */
183
184 /* This is called VERY early on in the init and therefore has to have
185 * hardcoded addresses of the serial hardware control registers. The
186 * serial line may be needed for console and debugging output before
187 * anything else takes place
188 */
189
190 void
191 initialize_serial( caddr_t scc_phys_base )
192 {
193 int i, chan, bits;
194 scc_regmap_t regs;
195 DECL_FUNNEL_VARS
196
197 assert( scc_phys_base );
198
199 if (!SCC_FUNNEL_INITTED) {
200 FUNNEL_INIT(&SCC_FUNNEL, master_processor);
201 SCC_FUNNEL_INITTED = TRUE;
202 }
203 FUNNEL_ENTER(&SCC_FUNNEL);
204
205 if (serial_initted) {
206 FUNNEL_EXIT(&SCC_FUNNEL);
207 return;
208 }
209
210 simple_lock_init(&scc_stomp, FALSE); /* (TEST/DEBUG) */
211
212 scc_softc[0].full_modem = TRUE;
213
214 scc_std[0] = scc_phys_base;
215
216 regs = scc_softc[0].regs = (scc_regmap_t)scc_std[0];
217
218 for (chan = 0; chan < NSCC_LINE; chan++) {
219 if (chan == 1)
220 scc_init_hw[0].val = 0x80;
221
222 for (i = 0; i < scc_init_hw_count; i++) {
223 scc_write_reg(regs, chan,
224 scc_init_hw[i].reg, scc_init_hw[i].val);
225 }
226 }
227
228 /* Call probe so we are ready very early for remote gdb and for serial
229 console output if appropriate. */
230 if (scc_probe()) {
231 for (i = 0; i < NSCC_LINE; i++) {
232 scc_softc[0].softr[i].wr5 = SCC_WR5_DTR | SCC_WR5_RTS;
233 scc_param(scc_tty_for(i));
234 /* Enable SCC interrupts (how many interrupts are to this thing?!?) */
235 scc_write_reg(regs, i, 9, SCC_WR9_NV);
236
237 scc_read_reg_zero(regs, 0, bits);/* Clear the status */
238 }
239 scc_parm_done = 1; /* (TEST/DEBUG) */
240 }
241
242 serial_initted = TRUE;
243
244 FUNNEL_EXIT(&SCC_FUNNEL);
245 return;
246 }
247
248 int
249 scc_probe(void)
250 {
251 scc_softc_t scc;
252 register int val, i;
253 register scc_regmap_t regs;
254 spl_t s;
255 DECL_FUNNEL_VARS
256
257 if (!SCC_FUNNEL_INITTED) {
258 FUNNEL_INIT(&SCC_FUNNEL, master_processor);
259 SCC_FUNNEL_INITTED = TRUE;
260 }
261 FUNNEL_ENTER(&SCC_FUNNEL);
262
263 /* Readjust the I/O address to handling
264 * new memory mappings.
265 */
266
267 // scc_std[0] = POWERMAC_IO(scc_std[0]);
268
269 regs = (scc_regmap_t)scc_std[0];
270
271 if (regs == (scc_regmap_t) 0) {
272 FUNNEL_EXIT(&SCC_FUNNEL);
273 return 0;
274 }
275
276 scc = &scc_softc[0];
277 scc->regs = regs;
278
279 s = splhigh();
280
281 for (i = 0; i < NSCC_LINE; i++) {
282 register struct scc_tty *tp;
283 tp = scc_tty_for(i);
284 tp->t_addr = (char*)(0x80000000L + (i&1));
285 /* Set default values. These will be overridden on
286 open but are needed if the port will be used
287 independently of the Mach interfaces, e.g., for
288 gdb or for a serial console. */
289 tp->t_ispeed = DEFAULT_SPEED;
290 tp->t_ospeed = DEFAULT_SPEED;
291 tp->t_flags = DEFAULT_FLAGS;
292 scc->softr[i].speed = -1;
293
294 /* do min buffering */
295 tp->t_state |= TS_MIN;
296
297 tp->t_dev = scc_dev_no(i);
298 }
299
300 splx(s);
301
302 FUNNEL_EXIT(&SCC_FUNNEL);
303 return 1;
304 }
305
306 /*
307 * Get a char from a specific SCC line
308 * [this is only used for console&screen purposes]
309 * must be splhigh since it may be called from another routine under spl
310 */
311
312 int
313 scc_getc(int unit, int line, boolean_t wait, boolean_t raw)
314 {
315 register scc_regmap_t regs;
316 unsigned char c, value;
317 int rcvalue, from_line;
318 spl_t s = splhigh();
319 DECL_FUNNEL_VARS
320
321 FUNNEL_ENTER(&SCC_FUNNEL);
322
323 simple_lock(&scc_stomp); /* (TEST/DEBUG) */
324 regs = scc_softc[0].regs;
325
326 /*
327 * wait till something available
328 *
329 */
330 again:
331 rcvalue = 0;
332 while (1) {
333 scc_read_reg_zero(regs, line, value);
334
335 if (value & SCC_RR0_RX_AVAIL)
336 break;
337
338 if (!wait) {
339 simple_unlock(&scc_stomp); /* (TEST/DEBUG) */
340 splx(s);
341 FUNNEL_EXIT(&SCC_FUNNEL);
342 return -1;
343 }
344 }
345
346 /*
347 * if nothing found return -1
348 */
349
350 scc_read_reg(regs, line, SCC_RR1, value);
351 scc_read_data(regs, line, c);
352
353 #if MACH_KDB
354 if (console_is_serial() &&
355 c == ('_' & 0x1f)) {
356 /* Drop into the debugger */
357 simple_unlock(&scc_stomp); /* (TEST/DEBUG) */
358 Debugger("Serial Line Request");
359 simple_lock(&scc_stomp); /* (TEST/DEBUG) */
360 scc_write_reg(regs, line, SCC_RR0, SCC_RESET_HIGHEST_IUS);
361 if (wait) {
362 goto again;
363 }
364 simple_unlock(&scc_stomp); /* (TEST/DEBUG) */
365 splx(s);
366 FUNNEL_EXIT(&SCC_FUNNEL);
367 return -1;
368 }
369 #endif /* MACH_KDB */
370
371 /*
372 * bad chars not ok
373 */
374 if (value&(SCC_RR1_PARITY_ERR | SCC_RR1_RX_OVERRUN | SCC_RR1_FRAME_ERR)) {
375 scc_write_reg(regs, line, SCC_RR0, SCC_RESET_ERROR);
376
377 if (wait) {
378 scc_write_reg(regs, line, SCC_RR0, SCC_RESET_HIGHEST_IUS);
379 goto again;
380 }
381 }
382
383 scc_write_reg(regs, line, SCC_RR0, SCC_RESET_HIGHEST_IUS);
384
385 simple_unlock(&scc_stomp); /* (TEST/DEBUG) */
386 splx(s);
387
388 FUNNEL_EXIT(&SCC_FUNNEL);
389 return c;
390 }
391
392 /*
393 * Put a char on a specific SCC line
394 * use splhigh since we might be doing a printf in high spl'd code
395 */
396
397 int
398 scc_putc(int unit, int line, int c)
399 {
400 scc_regmap_t regs;
401 spl_t s;
402 unsigned char value;
403 DECL_FUNNEL_VARS
404
405 if (disableSerialOuput)
406 return 0;
407
408 s = splhigh();
409 FUNNEL_ENTER(&SCC_FUNNEL);
410 simple_lock(&scc_stomp); /* (TEST/DEBUG) */
411
412 regs = scc_softc[0].regs;
413
414 do {
415 scc_read_reg(regs, line, SCC_RR0, value);
416 if (value & SCC_RR0_TX_EMPTY)
417 break;
418 delay(1);
419 } while (1);
420
421 scc_write_data(regs, line, c);
422 /* wait for it to swallow the char ? */
423
424 do {
425 scc_read_reg(regs, line, SCC_RR0, value);
426 if (value & SCC_RR0_TX_EMPTY)
427 break;
428 } while (1);
429 scc_write_reg(regs, line, SCC_RR0, SCC_RESET_HIGHEST_IUS);
430 simple_unlock(&scc_stomp); /* (TEST/DEBUG) */
431
432 splx(s);
433
434 FUNNEL_EXIT(&SCC_FUNNEL);
435 return 0;
436 }
437
438
439 void
440 powermac_scc_set_datum(scc_regmap_t regs, unsigned int offset, unsigned char value)
441 {
442 volatile unsigned char *address = (unsigned char *) regs + offset;
443
444 assert(FUNNEL_IN_USE(&SCC_FUNNEL));
445
446 *address = value;
447 eieio();
448
449 assert(FUNNEL_IN_USE(&SCC_FUNNEL));
450 }
451
452 unsigned char
453 powermac_scc_get_datum(scc_regmap_t regs, unsigned int offset)
454 {
455 volatile unsigned char *address = (unsigned char *) regs + offset;
456 unsigned char value;
457
458 assert(FUNNEL_IN_USE(&SCC_FUNNEL));
459
460 value = *address; eieio();
461 return value;
462
463 assert(FUNNEL_IN_USE(&SCC_FUNNEL));
464 }
465
466 int
467 scc_param(struct scc_tty *tp)
468 {
469 scc_regmap_t regs;
470 unsigned char value;
471 unsigned short speed_value;
472 int bits, chan;
473 spl_t s;
474 struct scc_softreg *sr;
475 scc_softc_t scc;
476
477 assert(FUNNEL_IN_USE(&SCC_FUNNEL));
478
479 s = splhigh();
480 simple_lock(&scc_stomp); /* (TEST/DEBUG) */
481
482 chan = scc_chan(tp->t_dev);
483 scc = &scc_softc[0];
484 regs = scc->regs;
485
486 sr = &scc->softr[chan];
487
488 /* Do a quick check to see if the hardware needs to change */
489 if ((sr->flags & (TF_ODDP|TF_EVENP)) == (tp->t_flags & (TF_ODDP|TF_EVENP))
490 && sr->speed == tp->t_ispeed) {
491 assert(FUNNEL_IN_USE(&SCC_FUNNEL));
492 simple_unlock(&scc_stomp); /* (TEST/DEBUG) */
493 splx(s); /* (TEST/DEBUG) */
494 return 0; /* (TEST/DEBUG) */
495 }
496
497 if(scc_parm_done) {
498
499 scc_write_reg(regs, chan, 3, SCC_WR3_RX_8_BITS|SCC_WR3_RX_ENABLE); /* (TEST/DEBUG) */
500 sr->wr1 = SCC_WR1_RXI_FIRST_CHAR | SCC_WR1_EXT_IE; /* (TEST/DEBUG) */
501 scc_write_reg(regs, chan, 1, sr->wr1); /* (TEST/DEBUG) */
502 scc_write_reg(regs, chan, 15, SCC_WR15_ENABLE_ESCC); /* (TEST/DEBUG) */
503 scc_write_reg(regs, chan, 7, SCC_WR7P_RX_FIFO); /* (TEST/DEBUG) */
504 scc_write_reg(regs, chan, 0, SCC_IE_NEXT_CHAR); /* (TEST/DEBUG) */
505 scc_write_reg(regs, chan, 0, SCC_RESET_EXT_IP); /* (TEST/DEBUG) */
506 scc_write_reg(regs, chan, 0, SCC_RESET_EXT_IP); /* (TEST/DEBUG) */
507 scc_write_reg(regs, chan, 9, SCC_WR9_MASTER_IE|SCC_WR9_NV); /* (TEST/DEBUG) */
508 scc_read_reg_zero(regs, 0, bits); /* (TEST/DEBUG) */
509 sr->wr1 = SCC_WR1_RXI_FIRST_CHAR | SCC_WR1_EXT_IE; /* (TEST/DEBUG) */
510 scc_write_reg(regs, chan, 1, sr->wr1); /* (TEST/DEBUG) */
511 scc_write_reg(regs, chan, 0, SCC_IE_NEXT_CHAR); /* (TEST/DEBUG) */
512 simple_unlock(&scc_stomp); /* (TEST/DEBUG) */
513 splx(s); /* (TEST/DEBUG) */
514 return 0; /* (TEST/DEBUG) */
515 }
516
517 sr->flags = tp->t_flags;
518 sr->speed = tp->t_ispeed;
519
520
521 if (tp->t_ispeed == 0) {
522 sr->wr5 &= ~SCC_WR5_DTR;
523 scc_write_reg(regs, chan, 5, sr->wr5);
524 simple_unlock(&scc_stomp); /* (TEST/DEBUG) */
525 splx(s);
526
527 assert(FUNNEL_IN_USE(&SCC_FUNNEL));
528 return 0;
529 }
530
531
532 #if SCC_DMA_TRANSFERS
533 if (scc->dma_initted & (1<<chan))
534 scc->dma_ops->scc_dma_reset_rx(chan);
535 #endif
536
537 value = SCC_WR4_1_STOP;
538
539 /*
540 * For 115K the clocking divide changes to 64.. to 230K will
541 * start at the normal clock divide 16.
542 *
543 * However, both speeds will pull from a different clocking
544 * source
545 */
546
547 if (tp->t_ispeed == 115200)
548 value |= SCC_WR4_CLK_x32;
549 else
550 value |= SCC_WR4_CLK_x16 ;
551
552 /* .. and parity */
553 if ((tp->t_flags & (TF_ODDP | TF_EVENP)) == TF_EVENP)
554 value |= (SCC_WR4_EVEN_PARITY | SCC_WR4_PARITY_ENABLE);
555 else if ((tp->t_flags & (TF_ODDP | TF_EVENP)) == TF_ODDP)
556 value |= SCC_WR4_PARITY_ENABLE;
557
558 /* set it now, remember it must be first after reset */
559 sr->wr4 = value;
560
561 /* Program Parity, and Stop bits */
562 scc_write_reg(regs, chan, 4, sr->wr4);
563
564 /* Setup for 8 bits */
565 scc_write_reg(regs, chan, 3, SCC_WR3_RX_8_BITS);
566
567 // Set DTR, RTS, and transmitter bits/character.
568 sr->wr5 = SCC_WR5_TX_8_BITS | SCC_WR5_RTS | SCC_WR5_DTR;
569
570 scc_write_reg(regs, chan, 5, sr->wr5);
571
572 scc_write_reg(regs, chan, 14, 0); /* Disable baud rate */
573
574 /* Setup baud rate 57.6Kbps, 115K, 230K should all yeild
575 * a converted baud rate of zero
576 */
577 speed_value = convert_baud_rate(tp->t_ispeed);
578
579 if (speed_value == 0xffff)
580 speed_value = 0;
581
582 scc_set_timing_base(regs, chan, speed_value);
583
584 if (tp->t_ispeed == 115200 || tp->t_ispeed == 230400) {
585 /* Special case here.. change the clock source*/
586 scc_write_reg(regs, chan, 11, 0);
587 /* Baud rate generator is disabled.. */
588 } else {
589 scc_write_reg(regs, chan, 11, SCC_WR11_RCLK_BAUDR|SCC_WR11_XTLK_BAUDR);
590 /* Enable the baud rate generator */
591 scc_write_reg(regs, chan, 14, SCC_WR14_BAUDR_ENABLE);
592 }
593
594
595 scc_write_reg(regs, chan, 3, SCC_WR3_RX_8_BITS|SCC_WR3_RX_ENABLE);
596
597
598 sr->wr1 = SCC_WR1_RXI_FIRST_CHAR | SCC_WR1_EXT_IE;
599 scc_write_reg(regs, chan, 1, sr->wr1);
600 scc_write_reg(regs, chan, 15, SCC_WR15_ENABLE_ESCC);
601 scc_write_reg(regs, chan, 7, SCC_WR7P_RX_FIFO);
602 scc_write_reg(regs, chan, 0, SCC_IE_NEXT_CHAR);
603
604
605 /* Clear out any pending external or status interrupts */
606 scc_write_reg(regs, chan, 0, SCC_RESET_EXT_IP);
607 scc_write_reg(regs, chan, 0, SCC_RESET_EXT_IP);
608 //scc_write_reg(regs, chan, 0, SCC_RESET_ERROR);
609
610 /* Enable SCC interrupts (how many interrupts are to this thing?!?) */
611 scc_write_reg(regs, chan, 9, SCC_WR9_MASTER_IE|SCC_WR9_NV);
612
613 scc_read_reg_zero(regs, 0, bits);/* Clear the status */
614
615 #if SCC_DMA_TRANSFERS
616 if (scc->dma_initted & (1<<chan)) {
617 scc->dma_ops->scc_dma_start_rx(chan);
618 scc->dma_ops->scc_dma_setup_8530(chan);
619 } else
620 #endif
621 {
622 sr->wr1 = SCC_WR1_RXI_FIRST_CHAR | SCC_WR1_EXT_IE;
623 scc_write_reg(regs, chan, 1, sr->wr1);
624 scc_write_reg(regs, chan, 0, SCC_IE_NEXT_CHAR);
625 }
626
627 sr->wr5 |= SCC_WR5_TX_ENABLE;
628 scc_write_reg(regs, chan, 5, sr->wr5);
629
630 simple_unlock(&scc_stomp); /* (TEST/DEBUG) */
631 splx(s);
632
633 assert(FUNNEL_IN_USE(&SCC_FUNNEL));
634 return 0;
635
636 }
637
638 #endif /* NSCC > 0 */