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