]> git.saurik.com Git - apple/xnu.git/blob - osfmk/ppc/serial_io.c
f944d2b4a53f03ede2e97867dab0f13fd133b517
[apple/xnu.git] / osfmk / ppc / serial_io.c
1 /*
2 * Copyright (c) 2000-2005 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 <kern/thread.h>
73 #include <ppc/misc_protos.h>
74 #include <ppc/proc_reg.h>
75 #include <ppc/exception.h>
76 #include <ppc/Firmware.h>
77 #include <ppc/serial_io.h>
78 #include <ppc/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;
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_PORT0_SPEED 1200
149 #define DEFAULT_FLAGS (TF_LITOUT|TF_ECHO)
150
151 int scc_param(struct scc_tty *tp);
152
153
154 struct scc_softc scc_softc[NSCC];
155 caddr_t scc_std[NSCC] = { (caddr_t) 0};
156
157
158 #define SCC_RR1_ERRS (SCC_RR1_FRAME_ERR|SCC_RR1_RX_OVERRUN|SCC_RR1_PARITY_ERR)
159 #define SCC_RR3_ALL (SCC_RR3_RX_IP_A|SCC_RR3_TX_IP_A|SCC_RR3_EXT_IP_A|\
160 SCC_RR3_RX_IP_B|SCC_RR3_TX_IP_B|SCC_RR3_EXT_IP_B)
161
162 #define DEBUG_SCC
163 #undef DEBUG_SCC
164
165 #ifdef DEBUG_SCC
166 static int total_chars, total_ints, total_overruns, total_errors, num_ints, max_chars;
167 static int chars_received[8];
168 static int __SCC_STATS = 0;
169 static int max_in_q = 0;
170 static int max_out_q = 0;
171 #endif
172
173 DECL_FUNNEL(, scc_funnel) /* funnel to serialize the SCC driver */
174 boolean_t scc_funnel_initted = FALSE;
175 #define SCC_FUNNEL scc_funnel
176 #define SCC_FUNNEL_INITTED scc_funnel_initted
177
178
179 /*
180 * Adapt/Probe/Attach functions
181 */
182 boolean_t scc_uses_modem_control = FALSE;/* patch this with adb */
183 decl_simple_lock_data(,scc_stomp)
184
185 /* This is called VERY early on in the init and therefore has to have
186 * hardcoded addresses of the serial hardware control registers. The
187 * serial line may be needed for console and debugging output before
188 * anything else takes place
189 */
190
191 void
192 initialize_serial( caddr_t scc_phys_base, int32_t serial_baud )
193 {
194 int i, chan, bits;
195 scc_regmap_t regs;
196 DECL_FUNNEL_VARS
197
198 assert( scc_phys_base );
199
200 if (!SCC_FUNNEL_INITTED) {
201 FUNNEL_INIT(&SCC_FUNNEL, master_processor);
202 SCC_FUNNEL_INITTED = TRUE;
203 }
204 FUNNEL_ENTER(&SCC_FUNNEL);
205
206 if (serial_initted) {
207 FUNNEL_EXIT(&SCC_FUNNEL);
208 return;
209 }
210
211 simple_lock_init(&scc_stomp, FALSE);
212
213 if (serial_baud == -1) serial_baud = DEFAULT_SPEED;
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(serial_baud)) {
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;
243 }
244
245 serial_initted = TRUE;
246
247 FUNNEL_EXIT(&SCC_FUNNEL);
248 return;
249 }
250
251 int
252 scc_probe(int32_t serial_baud)
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 regs = (scc_regmap_t)scc_std[0];
271
272 if (regs == (scc_regmap_t) 0) {
273 FUNNEL_EXIT(&SCC_FUNNEL);
274 return 0;
275 }
276
277 scc = &scc_softc[0];
278 scc->regs = regs;
279
280 s = splhigh();
281
282 for (i = 0; i < NSCC_LINE; i++) {
283 register struct scc_tty *tp;
284 tp = scc_tty_for(i);
285 tp->t_addr = (char*)(0x80000000L + (i&1));
286 /* Set default values. These will be overridden on
287 open but are needed if the port will be used
288 independently of the Mach interfaces, e.g., for
289 gdb or for a serial console. */
290 if (i == 0) {
291 tp->t_ispeed = DEFAULT_PORT0_SPEED;
292 tp->t_ospeed = DEFAULT_PORT0_SPEED;
293 } else {
294 tp->t_ispeed = serial_baud;
295 tp->t_ospeed = serial_baud;
296 }
297 tp->t_flags = DEFAULT_FLAGS;
298 scc->softr[i].speed = -1;
299
300 /* do min buffering */
301 tp->t_state |= TS_MIN;
302
303 tp->t_dev = scc_dev_no(i);
304 }
305
306 splx(s);
307
308 FUNNEL_EXIT(&SCC_FUNNEL);
309 return 1;
310 }
311
312 /*
313 * Get a char from a specific SCC line
314 * [this is only used for console&screen purposes]
315 * must be splhigh since it may be called from another routine under spl
316 */
317
318 int
319 scc_getc(int unit, int line, boolean_t wait, boolean_t raw)
320 {
321 register scc_regmap_t regs;
322 unsigned char c, value;
323 int rcvalue, from_line;
324 uint32_t fcrmunge;
325 spl_t s = splhigh();
326 DECL_FUNNEL_VARS
327
328 FUNNEL_ENTER(&SCC_FUNNEL);
329
330
331 simple_lock(&scc_stomp);
332 regs = scc_softc[0].regs;
333
334 /*
335 * wait till something available
336 *
337 */
338 again:
339 rcvalue = 0;
340 while (1) {
341 scc_read_reg_zero(regs, line, value);
342
343 if (value & SCC_RR0_RX_AVAIL)
344 break;
345
346 if (!wait) {
347 simple_unlock(&scc_stomp);
348 splx(s);
349 FUNNEL_EXIT(&SCC_FUNNEL);
350 return -1;
351 }
352 }
353
354 /*
355 * if nothing found return -1
356 */
357
358 scc_read_reg(regs, line, SCC_RR1, value);
359 scc_read_data(regs, line, c);
360
361 #if MACH_KDB
362 if (console_is_serial() &&
363 c == ('_' & 0x1f)) {
364 /* Drop into the debugger */
365 simple_unlock(&scc_stomp);
366 Debugger("Serial Line Request");
367 simple_lock(&scc_stomp);
368 scc_write_reg(regs, line, SCC_RR0, SCC_RESET_HIGHEST_IUS);
369 if (wait) {
370 goto again;
371 }
372 simple_unlock(&scc_stomp);
373 splx(s);
374 FUNNEL_EXIT(&SCC_FUNNEL);
375 return -1;
376 }
377 #endif /* MACH_KDB */
378
379 /*
380 * bad chars not ok
381 */
382 if (value&(SCC_RR1_PARITY_ERR | SCC_RR1_RX_OVERRUN | SCC_RR1_FRAME_ERR)) {
383 scc_write_reg(regs, line, SCC_RR0, SCC_RESET_ERROR);
384
385 if (wait) {
386 scc_write_reg(regs, line, SCC_RR0, SCC_RESET_HIGHEST_IUS);
387 goto again;
388 }
389 }
390
391 scc_write_reg(regs, line, SCC_RR0, SCC_RESET_HIGHEST_IUS);
392
393 simple_unlock(&scc_stomp);
394 splx(s);
395
396 FUNNEL_EXIT(&SCC_FUNNEL);
397 return c;
398 }
399
400
401 /*
402 * This front-ends scc_getc to make some intel changes easier
403 */
404
405 int _serial_getc(int unit, int line, boolean_t wait, boolean_t raw) {
406
407 return(scc_getc(unit, line, wait, raw));
408
409 }
410
411 /*
412 * Put a char on a specific SCC line
413 * use splhigh since we might be doing a printf in high spl'd code
414 */
415
416 int
417 scc_putc(int unit, int line, int c)
418 {
419 scc_regmap_t regs;
420 spl_t s;
421 unsigned char value;
422 uint32_t fcrmunge;
423 DECL_FUNNEL_VARS
424
425
426 if (disableSerialOuput)
427 return 0;
428
429 s = splhigh();
430 FUNNEL_ENTER(&SCC_FUNNEL);
431 simple_lock(&scc_stomp);
432
433 regs = scc_softc[0].regs;
434
435 do {
436 scc_read_reg(regs, line, SCC_RR0, value);
437 if (value & SCC_RR0_TX_EMPTY)
438 break;
439 delay(1);
440 } while (1);
441
442 scc_write_data(regs, line, c);
443 /* wait for it to swallow the char ? */
444
445 do {
446 scc_read_reg(regs, line, SCC_RR0, value);
447 if (value & SCC_RR0_TX_EMPTY)
448 break;
449 } while (1);
450 scc_write_reg(regs, line, SCC_RR0, SCC_RESET_HIGHEST_IUS);
451 simple_unlock(&scc_stomp);
452
453 splx(s);
454
455 FUNNEL_EXIT(&SCC_FUNNEL);
456 return 0;
457 }
458
459
460 void
461 powermac_scc_set_datum(scc_regmap_t regs, unsigned int offset, unsigned char value)
462 {
463 volatile unsigned char *address = (unsigned char *) regs + offset;
464
465 assert(FUNNEL_IN_USE(&SCC_FUNNEL));
466
467 *address = value;
468 eieio();
469
470 assert(FUNNEL_IN_USE(&SCC_FUNNEL));
471 }
472
473 unsigned char
474 powermac_scc_get_datum(scc_regmap_t regs, unsigned int offset)
475 {
476 volatile unsigned char *address = (unsigned char *) regs + offset;
477 unsigned char value;
478
479 assert(FUNNEL_IN_USE(&SCC_FUNNEL));
480
481 value = *address; eieio();
482 return value;
483
484 assert(FUNNEL_IN_USE(&SCC_FUNNEL));
485 }
486
487 int
488 scc_param(struct scc_tty *tp)
489 {
490 scc_regmap_t regs;
491 unsigned char value;
492 unsigned short speed_value;
493 int bits, chan;
494 spl_t s;
495 struct scc_softreg *sr;
496 scc_softc_t scc;
497
498 assert(FUNNEL_IN_USE(&SCC_FUNNEL));
499
500 s = splhigh();
501 simple_lock(&scc_stomp);
502
503 chan = scc_chan(tp->t_dev);
504 scc = &scc_softc[0];
505 regs = scc->regs;
506
507 sr = &scc->softr[chan];
508
509 /* Do a quick check to see if the hardware needs to change */
510 if ((sr->flags & (TF_ODDP|TF_EVENP)) == (tp->t_flags & (TF_ODDP|TF_EVENP))
511 && sr->speed == tp->t_ispeed) {
512 assert(FUNNEL_IN_USE(&SCC_FUNNEL));
513 simple_unlock(&scc_stomp);
514 splx(s);
515 return 0;
516 }
517
518 if(scc_parm_done) {
519
520 scc_write_reg(regs, chan, 3, SCC_WR3_RX_8_BITS|SCC_WR3_RX_ENABLE);
521 sr->wr1 = SCC_WR1_RXI_FIRST_CHAR | SCC_WR1_EXT_IE;
522 scc_write_reg(regs, chan, 1, sr->wr1);
523 scc_write_reg(regs, chan, 15, SCC_WR15_ENABLE_ESCC);
524 scc_write_reg(regs, chan, 7, SCC_WR7P_RX_FIFO);
525 scc_write_reg(regs, chan, 0, SCC_IE_NEXT_CHAR);
526 scc_write_reg(regs, chan, 0, SCC_RESET_EXT_IP);
527 scc_write_reg(regs, chan, 0, SCC_RESET_EXT_IP);
528 scc_write_reg(regs, chan, 9, SCC_WR9_MASTER_IE|SCC_WR9_NV);
529 scc_read_reg_zero(regs, 0, bits);
530 sr->wr1 = SCC_WR1_RXI_FIRST_CHAR | SCC_WR1_EXT_IE;
531 scc_write_reg(regs, chan, 1, sr->wr1);
532 scc_write_reg(regs, chan, 0, SCC_IE_NEXT_CHAR);
533 simple_unlock(&scc_stomp);
534 splx(s);
535 return 0;
536 }
537
538 sr->flags = tp->t_flags;
539 sr->speed = tp->t_ispeed;
540
541
542 if (tp->t_ispeed == 0) {
543 sr->wr5 &= ~SCC_WR5_DTR;
544 scc_write_reg(regs, chan, 5, sr->wr5);
545 simple_unlock(&scc_stomp);
546 splx(s);
547
548 assert(FUNNEL_IN_USE(&SCC_FUNNEL));
549 return 0;
550 }
551
552
553 #if SCC_DMA_TRANSFERS
554 if (scc->dma_initted & (1<<chan))
555 scc->dma_ops->scc_dma_reset_rx(chan);
556 #endif
557
558 value = SCC_WR4_1_STOP;
559
560 /*
561 * For 115K the clocking divide changes to 64.. to 230K will
562 * start at the normal clock divide 16.
563 *
564 * However, both speeds will pull from a different clocking
565 * source
566 */
567
568 if (tp->t_ispeed == 115200)
569 value |= SCC_WR4_CLK_x32;
570 else
571 value |= SCC_WR4_CLK_x16 ;
572
573 /* .. and parity */
574 if ((tp->t_flags & (TF_ODDP | TF_EVENP)) == TF_EVENP)
575 value |= (SCC_WR4_EVEN_PARITY | SCC_WR4_PARITY_ENABLE);
576 else if ((tp->t_flags & (TF_ODDP | TF_EVENP)) == TF_ODDP)
577 value |= SCC_WR4_PARITY_ENABLE;
578
579 /* set it now, remember it must be first after reset */
580 sr->wr4 = value;
581
582 /* Program Parity, and Stop bits */
583 scc_write_reg(regs, chan, 4, sr->wr4);
584
585 /* Setup for 8 bits */
586 scc_write_reg(regs, chan, 3, SCC_WR3_RX_8_BITS);
587
588 // Set DTR, RTS, and transmitter bits/character.
589 sr->wr5 = SCC_WR5_TX_8_BITS | SCC_WR5_RTS | SCC_WR5_DTR;
590
591 scc_write_reg(regs, chan, 5, sr->wr5);
592
593 scc_write_reg(regs, chan, 14, 0); /* Disable baud rate */
594
595 /* Setup baud rate 57.6Kbps, 115K, 230K should all yeild
596 * a converted baud rate of zero
597 */
598 speed_value = convert_baud_rate(tp->t_ispeed);
599
600 if (speed_value == 0xffff)
601 speed_value = 0;
602
603 scc_set_timing_base(regs, chan, speed_value);
604
605 if (tp->t_ispeed == 115200 || tp->t_ispeed == 230400) {
606 /* Special case here.. change the clock source*/
607 scc_write_reg(regs, chan, 11, 0);
608 /* Baud rate generator is disabled.. */
609 } else {
610 scc_write_reg(regs, chan, 11, SCC_WR11_RCLK_BAUDR|SCC_WR11_XTLK_BAUDR);
611 /* Enable the baud rate generator */
612 scc_write_reg(regs, chan, 14, SCC_WR14_BAUDR_ENABLE);
613 }
614
615
616 scc_write_reg(regs, chan, 3, SCC_WR3_RX_8_BITS|SCC_WR3_RX_ENABLE);
617
618
619 sr->wr1 = SCC_WR1_RXI_FIRST_CHAR | SCC_WR1_EXT_IE;
620 scc_write_reg(regs, chan, 1, sr->wr1);
621 scc_write_reg(regs, chan, 15, SCC_WR15_ENABLE_ESCC);
622 scc_write_reg(regs, chan, 7, SCC_WR7P_RX_FIFO);
623 scc_write_reg(regs, chan, 0, SCC_IE_NEXT_CHAR);
624
625
626 /* Clear out any pending external or status interrupts */
627 scc_write_reg(regs, chan, 0, SCC_RESET_EXT_IP);
628 scc_write_reg(regs, chan, 0, SCC_RESET_EXT_IP);
629 //scc_write_reg(regs, chan, 0, SCC_RESET_ERROR);
630
631 /* Enable SCC interrupts (how many interrupts are to this thing?!?) */
632 scc_write_reg(regs, chan, 9, SCC_WR9_MASTER_IE|SCC_WR9_NV);
633
634 scc_read_reg_zero(regs, 0, bits);/* Clear the status */
635
636 #if SCC_DMA_TRANSFERS
637 if (scc->dma_initted & (1<<chan)) {
638 scc->dma_ops->scc_dma_start_rx(chan);
639 scc->dma_ops->scc_dma_setup_8530(chan);
640 } else
641 #endif
642 {
643 sr->wr1 = SCC_WR1_RXI_FIRST_CHAR | SCC_WR1_EXT_IE;
644 scc_write_reg(regs, chan, 1, sr->wr1);
645 scc_write_reg(regs, chan, 0, SCC_IE_NEXT_CHAR);
646 }
647
648 sr->wr5 |= SCC_WR5_TX_ENABLE;
649 scc_write_reg(regs, chan, 5, sr->wr5);
650
651 simple_unlock(&scc_stomp);
652 splx(s);
653
654 assert(FUNNEL_IN_USE(&SCC_FUNNEL));
655 return 0;
656
657 }
658
659
660 #endif /* NSCC > 0 */