2 * Copyright (c) 2000-2006 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 int serial_initted
= 0;
106 unsigned int scc_parm_done
= 0;
108 static struct scc_byte
{
129 {1, 0x12}, /* int or Rx, Tx int enable */
131 {1, 0x10}, /* int or Rx, no Tx int enable */
136 static int scc_init_hw_count
= sizeof(scc_init_hw
)/sizeof(scc_init_hw
[0]);
138 enum scc_error
{SCC_ERR_NONE
, SCC_ERR_PARITY
, SCC_ERR_BREAK
, SCC_ERR_OVERRUN
};
143 * ClockFrequency (115200 for Power Mac)
144 * BRGconstant = --------------------------- - 2
148 #define SERIAL_CLOCK_FREQUENCY (115200*2) /* Power Mac value */
149 #define convert_baud_rate(rate) ((((SERIAL_CLOCK_FREQUENCY) + (rate)) / (2 * (rate))) - 2)
151 #define DEFAULT_SPEED 57600
152 #define DEFAULT_PORT0_SPEED 1200
153 #define DEFAULT_FLAGS (TF_LITOUT|TF_ECHO)
155 int scc_param(struct scc_tty
*tp
);
158 struct scc_softc scc_softc
[NSCC
];
159 caddr_t scc_std
[NSCC
] = { (caddr_t
) 0};
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)
170 static int total_chars
, total_ints
, total_overruns
, total_errors
, num_ints
, max_chars
;
171 static int chars_received
[8];
172 static int __SCC_STATS
= 0;
173 static int max_in_q
= 0;
174 static int max_out_q
= 0;
177 DECL_FUNNEL(, scc_funnel
) /* funnel to serialize the SCC driver */
178 boolean_t scc_funnel_initted
= FALSE
;
179 #define SCC_FUNNEL scc_funnel
180 #define SCC_FUNNEL_INITTED scc_funnel_initted
184 * Adapt/Probe/Attach functions
186 boolean_t scc_uses_modem_control
= FALSE
;/* patch this with adb */
187 decl_simple_lock_data(,scc_stomp
)
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
196 initialize_serial( caddr_t scc_phys_base
, int32_t serial_baud
)
202 assert( scc_phys_base
);
204 if (!SCC_FUNNEL_INITTED
) {
205 FUNNEL_INIT(&SCC_FUNNEL
, master_processor
);
206 SCC_FUNNEL_INITTED
= TRUE
;
208 FUNNEL_ENTER(&SCC_FUNNEL
);
210 if (serial_initted
) {
211 FUNNEL_EXIT(&SCC_FUNNEL
);
215 simple_lock_init(&scc_stomp
, FALSE
);
217 if (serial_baud
== -1) serial_baud
= DEFAULT_SPEED
;
219 scc_softc
[0].full_modem
= TRUE
;
221 scc_std
[0] = scc_phys_base
;
223 regs
= scc_softc
[0].regs
= (scc_regmap_t
)scc_std
[0];
225 for (chan
= 0; chan
< NSCC_LINE
; chan
++) {
227 scc_init_hw
[0].val
= 0x80;
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
);
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
);
244 scc_read_reg_zero(regs
, 0, bits
);/* Clear the status */
249 serial_initted
= TRUE
;
251 FUNNEL_EXIT(&SCC_FUNNEL
);
256 scc_probe(int32_t serial_baud
)
264 if (!SCC_FUNNEL_INITTED
) {
265 FUNNEL_INIT(&SCC_FUNNEL
, master_processor
);
266 SCC_FUNNEL_INITTED
= TRUE
;
268 FUNNEL_ENTER(&SCC_FUNNEL
);
270 /* Readjust the I/O address to handling
271 * new memory mappings.
274 regs
= (scc_regmap_t
)scc_std
[0];
276 if (regs
== (scc_regmap_t
) 0) {
277 FUNNEL_EXIT(&SCC_FUNNEL
);
286 for (i
= 0; i
< NSCC_LINE
; i
++) {
287 register struct scc_tty
*tp
;
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. */
295 tp
->t_ispeed
= DEFAULT_PORT0_SPEED
;
296 tp
->t_ospeed
= DEFAULT_PORT0_SPEED
;
298 tp
->t_ispeed
= serial_baud
;
299 tp
->t_ospeed
= serial_baud
;
301 tp
->t_flags
= DEFAULT_FLAGS
;
302 scc
->softr
[i
].speed
= -1;
304 /* do min buffering */
305 tp
->t_state
|= TS_MIN
;
307 tp
->t_dev
= scc_dev_no(i
);
312 FUNNEL_EXIT(&SCC_FUNNEL
);
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
323 scc_getc(__unused
int unit
, int line
, boolean_t wait
, __unused boolean_t raw
)
326 unsigned char c
, value
;
331 FUNNEL_ENTER(&SCC_FUNNEL
);
334 simple_lock(&scc_stomp
);
335 regs
= scc_softc
[0].regs
;
338 * wait till something available
344 scc_read_reg_zero(regs
, line
, value
);
346 if (value
& SCC_RR0_RX_AVAIL
)
350 simple_unlock(&scc_stomp
);
352 FUNNEL_EXIT(&SCC_FUNNEL
);
358 * if nothing found return -1
361 scc_read_reg(regs
, line
, SCC_RR1
, value
);
362 scc_read_data(regs
, line
, c
);
365 if (console_is_serial() &&
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
);
375 simple_unlock(&scc_stomp
);
377 FUNNEL_EXIT(&SCC_FUNNEL
);
380 #endif /* MACH_KDB */
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
);
389 scc_write_reg(regs
, line
, SCC_RR0
, SCC_RESET_HIGHEST_IUS
);
394 scc_write_reg(regs
, line
, SCC_RR0
, SCC_RESET_HIGHEST_IUS
);
396 simple_unlock(&scc_stomp
);
399 FUNNEL_EXIT(&SCC_FUNNEL
);
405 * This front-ends scc_getc to make some intel changes easier
408 int _serial_getc(int unit
, int line
, boolean_t wait
, boolean_t raw
) {
410 return(scc_getc(unit
, line
, wait
, raw
));
415 * Put a char on a specific SCC line
416 * use splhigh since we might be doing a printf in high spl'd code
420 scc_putc(__unused
int unit
, int line
, int c
)
428 if (disable_serial_output
)
432 FUNNEL_ENTER(&SCC_FUNNEL
);
433 simple_lock(&scc_stomp
);
435 regs
= scc_softc
[0].regs
;
438 scc_read_reg(regs
, line
, SCC_RR0
, value
);
439 if (value
& SCC_RR0_TX_EMPTY
)
444 scc_write_data(regs
, line
, c
);
445 /* wait for it to swallow the char ? */
448 scc_read_reg(regs
, line
, SCC_RR0
, value
);
449 if (value
& SCC_RR0_TX_EMPTY
)
452 scc_write_reg(regs
, line
, SCC_RR0
, SCC_RESET_HIGHEST_IUS
);
453 simple_unlock(&scc_stomp
);
457 FUNNEL_EXIT(&SCC_FUNNEL
);
462 powermac_scc_set_datum(scc_regmap_t regs
, unsigned int offset
, unsigned char value
)
464 volatile unsigned char *address
= (unsigned char *) regs
+ offset
;
466 assert(FUNNEL_IN_USE(&SCC_FUNNEL
));
471 assert(FUNNEL_IN_USE(&SCC_FUNNEL
));
475 powermac_scc_get_datum(scc_regmap_t regs
, unsigned int offset
)
477 volatile unsigned char *address
= (unsigned char *) regs
+ offset
;
480 assert(FUNNEL_IN_USE(&SCC_FUNNEL
));
482 value
= *address
; eieio();
485 assert(FUNNEL_IN_USE(&SCC_FUNNEL
));
489 scc_param(struct scc_tty
*tp
)
493 unsigned short speed_value
;
496 struct scc_softreg
*sr
;
499 assert(FUNNEL_IN_USE(&SCC_FUNNEL
));
502 simple_lock(&scc_stomp
);
504 chan
= scc_chan(tp
->t_dev
);
508 sr
= &scc
->softr
[chan
];
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
);
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
);
539 sr
->flags
= tp
->t_flags
;
540 sr
->speed
= tp
->t_ispeed
;
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
);
549 assert(FUNNEL_IN_USE(&SCC_FUNNEL
));
554 #if SCC_DMA_TRANSFERS
555 if (scc
->dma_initted
& (1<<chan
))
556 scc
->dma_ops
->scc_dma_reset_rx(chan
);
559 value
= SCC_WR4_1_STOP
;
562 * For 115K the clocking divide changes to 64.. to 230K will
563 * start at the normal clock divide 16.
565 * However, both speeds will pull from a different clocking
569 if (tp
->t_ispeed
== 115200)
570 value
|= SCC_WR4_CLK_x32
;
572 value
|= SCC_WR4_CLK_x16
;
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
;
580 /* set it now, remember it must be first after reset */
583 /* Program Parity, and Stop bits */
584 scc_write_reg(regs
, chan
, 4, sr
->wr4
);
586 /* Setup for 8 bits */
587 scc_write_reg(regs
, chan
, 3, SCC_WR3_RX_8_BITS
);
589 // Set DTR, RTS, and transmitter bits/character.
590 sr
->wr5
= SCC_WR5_TX_8_BITS
| SCC_WR5_RTS
| SCC_WR5_DTR
;
592 scc_write_reg(regs
, chan
, 5, sr
->wr5
);
594 scc_write_reg(regs
, chan
, 14, 0); /* Disable baud rate */
596 /* Setup baud rate 57.6Kbps, 115K, 230K should all yeild
597 * a converted baud rate of zero
599 speed_value
= convert_baud_rate(tp
->t_ispeed
);
601 if (speed_value
== 0xffff)
604 scc_set_timing_base(regs
, chan
, speed_value
);
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.. */
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
);
617 scc_write_reg(regs
, chan
, 3, SCC_WR3_RX_8_BITS
|SCC_WR3_RX_ENABLE
);
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
);
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);
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
);
635 scc_read_reg_zero(regs
, 0, bits
);/* Clear the status */
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
);
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
);
649 sr
->wr5
|= SCC_WR5_TX_ENABLE
;
650 scc_write_reg(regs
, chan
, 5, sr
->wr5
);
652 simple_unlock(&scc_stomp
);
655 assert(FUNNEL_IN_USE(&SCC_FUNNEL
));
659 #endif /* NSCC > 0 */