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