2 * Copyright (c) 2000-2005 Apple Computer, Inc. All rights reserved.
4 * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
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.
15 * Please obtain a copy of the License at
16 * http://www.opensource.apple.com/apsl/ and read it before using this file.
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.
26 * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
32 * @APPLE_FREE_COPYRIGHT@
35 * Mach Operating System
36 * Copyright (c) 1991,1990,1989 Carnegie Mellon University
37 * All Rights Reserved.
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.
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.
49 * Carnegie Mellon requests users of this software to return to
51 * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU
52 * School of Computer Science
53 * Carnegie Mellon University
54 * Pittsburgh PA 15213-3890
56 * any improvements or extensions that they make and grant Carnegie Mellon
57 * the rights to redistribute these changes.
62 * File: scc_8530_hdw.c
63 * Author: Alessandro Forin, Carnegie Mellon University
66 * Hardware-level operations for the SCC Serial Line Driver
69 #define NSCC 1 /* Number of serial chips, two ports per chip. */
73 #include <platforms.h>
75 #include <mach/std_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>
87 #include <machine/db_machdep.h>
90 #define kdebug_state() (1)
91 #define delay(x) { volatile int _d_; for (_d_ = 0; _d_ < (10000*x); _d_++) ; }
93 #define NSCC_LINE 2 /* 2 ttys per chip */
95 #define SCC_DMA_TRANSFERS 0
97 struct scc_tty scc_tty
[NSCC_LINE
];
99 #define scc_tty_for(chan) (&scc_tty[chan])
100 /* #define scc_unit(dev_no) (dev_no) */
102 #define scc_dev_no(chan) ((chan)^0x01)
103 #define scc_chan(dev_no) ((dev_no)^0x01)
105 extern unsigned int disableSerialOuput
;
107 int serial_initted
= 0;
108 unsigned int scc_parm_done
= 0;
110 extern unsigned int serialmode
;
112 static struct scc_byte
{
133 1, 0x12, /* int or Rx, Tx int enable */
135 1, 0x10, /* int or Rx, no Tx int enable */
140 static int scc_init_hw_count
= sizeof(scc_init_hw
)/sizeof(scc_init_hw
[0]);
142 enum scc_error
{SCC_ERR_NONE
, SCC_ERR_PARITY
, SCC_ERR_BREAK
, SCC_ERR_OVERRUN
};
147 * ClockFrequency (115200 for Power Mac)
148 * BRGconstant = --------------------------- - 2
152 #define SERIAL_CLOCK_FREQUENCY (115200*2) /* Power Mac value */
153 #define convert_baud_rate(rate) ((((SERIAL_CLOCK_FREQUENCY) + (rate)) / (2 * (rate))) - 2)
155 #define DEFAULT_SPEED 57600
156 #define DEFAULT_PORT0_SPEED 1200
157 #define DEFAULT_FLAGS (TF_LITOUT|TF_ECHO)
159 int scc_param(struct scc_tty
*tp
);
162 struct scc_softc scc_softc
[NSCC
];
163 caddr_t scc_std
[NSCC
] = { (caddr_t
) 0};
166 #define SCC_RR1_ERRS (SCC_RR1_FRAME_ERR|SCC_RR1_RX_OVERRUN|SCC_RR1_PARITY_ERR)
167 #define SCC_RR3_ALL (SCC_RR3_RX_IP_A|SCC_RR3_TX_IP_A|SCC_RR3_EXT_IP_A|\
168 SCC_RR3_RX_IP_B|SCC_RR3_TX_IP_B|SCC_RR3_EXT_IP_B)
174 static int total_chars
, total_ints
, total_overruns
, total_errors
, num_ints
, max_chars
;
175 static int chars_received
[8];
176 static int __SCC_STATS
= 0;
177 static int max_in_q
= 0;
178 static int max_out_q
= 0;
181 DECL_FUNNEL(, scc_funnel
) /* funnel to serialize the SCC driver */
182 boolean_t scc_funnel_initted
= FALSE
;
183 #define SCC_FUNNEL scc_funnel
184 #define SCC_FUNNEL_INITTED scc_funnel_initted
188 * Adapt/Probe/Attach functions
190 boolean_t scc_uses_modem_control
= FALSE
;/* patch this with adb */
191 decl_simple_lock_data(,scc_stomp
)
193 /* This is called VERY early on in the init and therefore has to have
194 * hardcoded addresses of the serial hardware control registers. The
195 * serial line may be needed for console and debugging output before
196 * anything else takes place
200 initialize_serial( caddr_t scc_phys_base
, int32_t serial_baud
)
206 assert( scc_phys_base
);
208 if (!SCC_FUNNEL_INITTED
) {
209 FUNNEL_INIT(&SCC_FUNNEL
, master_processor
);
210 SCC_FUNNEL_INITTED
= TRUE
;
212 FUNNEL_ENTER(&SCC_FUNNEL
);
214 if (serial_initted
) {
215 FUNNEL_EXIT(&SCC_FUNNEL
);
219 simple_lock_init(&scc_stomp
, FALSE
);
221 if (serial_baud
== -1) serial_baud
= DEFAULT_SPEED
;
223 scc_softc
[0].full_modem
= TRUE
;
225 scc_std
[0] = scc_phys_base
;
227 regs
= scc_softc
[0].regs
= (scc_regmap_t
)scc_std
[0];
229 for (chan
= 0; chan
< NSCC_LINE
; chan
++) {
231 scc_init_hw
[0].val
= 0x80;
233 for (i
= 0; i
< scc_init_hw_count
; i
++) {
234 scc_write_reg(regs
, chan
,
235 scc_init_hw
[i
].reg
, scc_init_hw
[i
].val
);
239 /* Call probe so we are ready very early for remote gdb and for serial
240 console output if appropriate. */
241 if (scc_probe(serial_baud
)) {
242 for (i
= 0; i
< NSCC_LINE
; i
++) {
243 scc_softc
[0].softr
[i
].wr5
= SCC_WR5_DTR
| SCC_WR5_RTS
;
244 scc_param(scc_tty_for(i
));
245 /* Enable SCC interrupts (how many interrupts are to this thing?!?) */
246 scc_write_reg(regs
, i
, 9, SCC_WR9_NV
);
248 scc_read_reg_zero(regs
, 0, bits
);/* Clear the status */
253 serial_initted
= TRUE
;
255 FUNNEL_EXIT(&SCC_FUNNEL
);
260 scc_probe(int32_t serial_baud
)
264 register scc_regmap_t regs
;
268 if (!SCC_FUNNEL_INITTED
) {
269 FUNNEL_INIT(&SCC_FUNNEL
, master_processor
);
270 SCC_FUNNEL_INITTED
= TRUE
;
272 FUNNEL_ENTER(&SCC_FUNNEL
);
274 /* Readjust the I/O address to handling
275 * new memory mappings.
278 regs
= (scc_regmap_t
)scc_std
[0];
280 if (regs
== (scc_regmap_t
) 0) {
281 FUNNEL_EXIT(&SCC_FUNNEL
);
290 for (i
= 0; i
< NSCC_LINE
; i
++) {
291 register struct scc_tty
*tp
;
293 tp
->t_addr
= (char*)(0x80000000L
+ (i
&1));
294 /* Set default values. These will be overridden on
295 open but are needed if the port will be used
296 independently of the Mach interfaces, e.g., for
297 gdb or for a serial console. */
299 tp
->t_ispeed
= DEFAULT_PORT0_SPEED
;
300 tp
->t_ospeed
= DEFAULT_PORT0_SPEED
;
302 tp
->t_ispeed
= serial_baud
;
303 tp
->t_ospeed
= serial_baud
;
305 tp
->t_flags
= DEFAULT_FLAGS
;
306 scc
->softr
[i
].speed
= -1;
308 /* do min buffering */
309 tp
->t_state
|= TS_MIN
;
311 tp
->t_dev
= scc_dev_no(i
);
316 FUNNEL_EXIT(&SCC_FUNNEL
);
321 * Get a char from a specific SCC line
322 * [this is only used for console&screen purposes]
323 * must be splhigh since it may be called from another routine under spl
327 scc_getc(int unit
, int line
, boolean_t wait
, boolean_t raw
)
329 register scc_regmap_t regs
;
330 unsigned char c
, value
;
331 int rcvalue
, from_line
;
336 FUNNEL_ENTER(&SCC_FUNNEL
);
339 simple_lock(&scc_stomp
);
340 regs
= scc_softc
[0].regs
;
343 * wait till something available
349 scc_read_reg_zero(regs
, line
, value
);
351 if (value
& SCC_RR0_RX_AVAIL
)
355 simple_unlock(&scc_stomp
);
357 FUNNEL_EXIT(&SCC_FUNNEL
);
363 * if nothing found return -1
366 scc_read_reg(regs
, line
, SCC_RR1
, value
);
367 scc_read_data(regs
, line
, c
);
370 if (console_is_serial() &&
372 /* Drop into the debugger */
373 simple_unlock(&scc_stomp
);
374 Debugger("Serial Line Request");
375 simple_lock(&scc_stomp
);
376 scc_write_reg(regs
, line
, SCC_RR0
, SCC_RESET_HIGHEST_IUS
);
380 simple_unlock(&scc_stomp
);
382 FUNNEL_EXIT(&SCC_FUNNEL
);
385 #endif /* MACH_KDB */
390 if (value
&(SCC_RR1_PARITY_ERR
| SCC_RR1_RX_OVERRUN
| SCC_RR1_FRAME_ERR
)) {
391 scc_write_reg(regs
, line
, SCC_RR0
, SCC_RESET_ERROR
);
394 scc_write_reg(regs
, line
, SCC_RR0
, SCC_RESET_HIGHEST_IUS
);
399 scc_write_reg(regs
, line
, SCC_RR0
, SCC_RESET_HIGHEST_IUS
);
401 simple_unlock(&scc_stomp
);
404 FUNNEL_EXIT(&SCC_FUNNEL
);
409 * Put a char on a specific SCC line
410 * use splhigh since we might be doing a printf in high spl'd code
414 scc_putc(int unit
, int line
, int c
)
423 if (disableSerialOuput
)
427 FUNNEL_ENTER(&SCC_FUNNEL
);
428 simple_lock(&scc_stomp
);
430 regs
= scc_softc
[0].regs
;
433 scc_read_reg(regs
, line
, SCC_RR0
, value
);
434 if (value
& SCC_RR0_TX_EMPTY
)
439 scc_write_data(regs
, line
, c
);
440 /* wait for it to swallow the char ? */
443 scc_read_reg(regs
, line
, SCC_RR0
, value
);
444 if (value
& SCC_RR0_TX_EMPTY
)
447 scc_write_reg(regs
, line
, SCC_RR0
, SCC_RESET_HIGHEST_IUS
);
448 simple_unlock(&scc_stomp
);
452 FUNNEL_EXIT(&SCC_FUNNEL
);
458 powermac_scc_set_datum(scc_regmap_t regs
, unsigned int offset
, unsigned char value
)
460 volatile unsigned char *address
= (unsigned char *) regs
+ offset
;
462 assert(FUNNEL_IN_USE(&SCC_FUNNEL
));
467 assert(FUNNEL_IN_USE(&SCC_FUNNEL
));
471 powermac_scc_get_datum(scc_regmap_t regs
, unsigned int offset
)
473 volatile unsigned char *address
= (unsigned char *) regs
+ offset
;
476 assert(FUNNEL_IN_USE(&SCC_FUNNEL
));
478 value
= *address
; eieio();
481 assert(FUNNEL_IN_USE(&SCC_FUNNEL
));
485 scc_param(struct scc_tty
*tp
)
489 unsigned short speed_value
;
492 struct scc_softreg
*sr
;
495 assert(FUNNEL_IN_USE(&SCC_FUNNEL
));
498 simple_lock(&scc_stomp
);
500 chan
= scc_chan(tp
->t_dev
);
504 sr
= &scc
->softr
[chan
];
506 /* Do a quick check to see if the hardware needs to change */
507 if ((sr
->flags
& (TF_ODDP
|TF_EVENP
)) == (tp
->t_flags
& (TF_ODDP
|TF_EVENP
))
508 && sr
->speed
== tp
->t_ispeed
) {
509 assert(FUNNEL_IN_USE(&SCC_FUNNEL
));
510 simple_unlock(&scc_stomp
);
517 scc_write_reg(regs
, chan
, 3, SCC_WR3_RX_8_BITS
|SCC_WR3_RX_ENABLE
);
518 sr
->wr1
= SCC_WR1_RXI_FIRST_CHAR
| SCC_WR1_EXT_IE
;
519 scc_write_reg(regs
, chan
, 1, sr
->wr1
);
520 scc_write_reg(regs
, chan
, 15, SCC_WR15_ENABLE_ESCC
);
521 scc_write_reg(regs
, chan
, 7, SCC_WR7P_RX_FIFO
);
522 scc_write_reg(regs
, chan
, 0, SCC_IE_NEXT_CHAR
);
523 scc_write_reg(regs
, chan
, 0, SCC_RESET_EXT_IP
);
524 scc_write_reg(regs
, chan
, 0, SCC_RESET_EXT_IP
);
525 scc_write_reg(regs
, chan
, 9, SCC_WR9_MASTER_IE
|SCC_WR9_NV
);
526 scc_read_reg_zero(regs
, 0, bits
);
527 sr
->wr1
= SCC_WR1_RXI_FIRST_CHAR
| SCC_WR1_EXT_IE
;
528 scc_write_reg(regs
, chan
, 1, sr
->wr1
);
529 scc_write_reg(regs
, chan
, 0, SCC_IE_NEXT_CHAR
);
530 simple_unlock(&scc_stomp
);
535 sr
->flags
= tp
->t_flags
;
536 sr
->speed
= tp
->t_ispeed
;
539 if (tp
->t_ispeed
== 0) {
540 sr
->wr5
&= ~SCC_WR5_DTR
;
541 scc_write_reg(regs
, chan
, 5, sr
->wr5
);
542 simple_unlock(&scc_stomp
);
545 assert(FUNNEL_IN_USE(&SCC_FUNNEL
));
550 #if SCC_DMA_TRANSFERS
551 if (scc
->dma_initted
& (1<<chan
))
552 scc
->dma_ops
->scc_dma_reset_rx(chan
);
555 value
= SCC_WR4_1_STOP
;
558 * For 115K the clocking divide changes to 64.. to 230K will
559 * start at the normal clock divide 16.
561 * However, both speeds will pull from a different clocking
565 if (tp
->t_ispeed
== 115200)
566 value
|= SCC_WR4_CLK_x32
;
568 value
|= SCC_WR4_CLK_x16
;
571 if ((tp
->t_flags
& (TF_ODDP
| TF_EVENP
)) == TF_EVENP
)
572 value
|= (SCC_WR4_EVEN_PARITY
| SCC_WR4_PARITY_ENABLE
);
573 else if ((tp
->t_flags
& (TF_ODDP
| TF_EVENP
)) == TF_ODDP
)
574 value
|= SCC_WR4_PARITY_ENABLE
;
576 /* set it now, remember it must be first after reset */
579 /* Program Parity, and Stop bits */
580 scc_write_reg(regs
, chan
, 4, sr
->wr4
);
582 /* Setup for 8 bits */
583 scc_write_reg(regs
, chan
, 3, SCC_WR3_RX_8_BITS
);
585 // Set DTR, RTS, and transmitter bits/character.
586 sr
->wr5
= SCC_WR5_TX_8_BITS
| SCC_WR5_RTS
| SCC_WR5_DTR
;
588 scc_write_reg(regs
, chan
, 5, sr
->wr5
);
590 scc_write_reg(regs
, chan
, 14, 0); /* Disable baud rate */
592 /* Setup baud rate 57.6Kbps, 115K, 230K should all yeild
593 * a converted baud rate of zero
595 speed_value
= convert_baud_rate(tp
->t_ispeed
);
597 if (speed_value
== 0xffff)
600 scc_set_timing_base(regs
, chan
, speed_value
);
602 if (tp
->t_ispeed
== 115200 || tp
->t_ispeed
== 230400) {
603 /* Special case here.. change the clock source*/
604 scc_write_reg(regs
, chan
, 11, 0);
605 /* Baud rate generator is disabled.. */
607 scc_write_reg(regs
, chan
, 11, SCC_WR11_RCLK_BAUDR
|SCC_WR11_XTLK_BAUDR
);
608 /* Enable the baud rate generator */
609 scc_write_reg(regs
, chan
, 14, SCC_WR14_BAUDR_ENABLE
);
613 scc_write_reg(regs
, chan
, 3, SCC_WR3_RX_8_BITS
|SCC_WR3_RX_ENABLE
);
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
, 15, SCC_WR15_ENABLE_ESCC
);
619 scc_write_reg(regs
, chan
, 7, SCC_WR7P_RX_FIFO
);
620 scc_write_reg(regs
, chan
, 0, SCC_IE_NEXT_CHAR
);
623 /* Clear out any pending external or status interrupts */
624 scc_write_reg(regs
, chan
, 0, SCC_RESET_EXT_IP
);
625 scc_write_reg(regs
, chan
, 0, SCC_RESET_EXT_IP
);
626 //scc_write_reg(regs, chan, 0, SCC_RESET_ERROR);
628 /* Enable SCC interrupts (how many interrupts are to this thing?!?) */
629 scc_write_reg(regs
, chan
, 9, SCC_WR9_MASTER_IE
|SCC_WR9_NV
);
631 scc_read_reg_zero(regs
, 0, bits
);/* Clear the status */
633 #if SCC_DMA_TRANSFERS
634 if (scc
->dma_initted
& (1<<chan
)) {
635 scc
->dma_ops
->scc_dma_start_rx(chan
);
636 scc
->dma_ops
->scc_dma_setup_8530(chan
);
640 sr
->wr1
= SCC_WR1_RXI_FIRST_CHAR
| SCC_WR1_EXT_IE
;
641 scc_write_reg(regs
, chan
, 1, sr
->wr1
);
642 scc_write_reg(regs
, chan
, 0, SCC_IE_NEXT_CHAR
);
645 sr
->wr5
|= SCC_WR5_TX_ENABLE
;
646 scc_write_reg(regs
, chan
, 5, sr
->wr5
);
648 simple_unlock(&scc_stomp
);
651 assert(FUNNEL_IN_USE(&SCC_FUNNEL
));
657 * This routine will start a thread that polls the serial port, listening for
658 * characters that have been typed.
662 serial_keyboard_init(void)
664 kern_return_t result
;
667 if(!(serialmode
& 2)) return; /* Leave if we do not want a serial console */
669 kprintf("Serial keyboard started\n");
670 result
= kernel_thread_start_priority((thread_continue_t
)serial_keyboard_start
, NULL
, MAXPRI_KERNEL
, &thread
);
671 if (result
!= KERN_SUCCESS
)
672 panic("serial_keyboard_init");
674 thread_deallocate(thread
);
678 serial_keyboard_start(void)
680 serial_keyboard_poll(); /* Go see if there are any characters pending now */
681 panic("serial_keyboard_start: we can't get back here\n");
684 static int ptestxxx
= 0;
687 serial_keyboard_poll(void)
691 extern void cons_cinput(char ch
); /* The BSD routine that gets characters */
694 while(1) { /* Do this for a while */
695 chr
= scc_getc(0, 1, 0, 1); /* Get a character if there is one */
696 if(chr
< 0) break; /* The serial buffer is empty */
697 cons_cinput((char)chr
); /* Buffer up the character */
700 clock_interval_to_deadline(16, 1000000, &next
); /* Get time of pop */
702 assert_wait_deadline((event_t
)serial_keyboard_poll
, THREAD_UNINT
, next
); /* Show we are "waiting" */
703 thread_block((thread_continue_t
)serial_keyboard_poll
); /* Wait for it */
704 panic("serial_keyboard_poll: Shouldn't never ever get here...\n");
707 #endif /* NSCC > 0 */