2 * Copyright (c) 2000 Apple Computer, Inc. All rights reserved.
4 * @APPLE_LICENSE_HEADER_START@
6 * Copyright (c) 1999-2003 Apple Computer, Inc. All Rights Reserved.
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
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.
23 * @APPLE_LICENSE_HEADER_END@
29 * @APPLE_FREE_COPYRIGHT@
32 * Mach Operating System
33 * Copyright (c) 1991,1990,1989 Carnegie Mellon University
34 * All Rights Reserved.
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.
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.
46 * Carnegie Mellon requests users of this software to return to
48 * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU
49 * School of Computer Science
50 * Carnegie Mellon University
51 * Pittsburgh PA 15213-3890
53 * any improvements or extensions that they make and grant Carnegie Mellon
54 * the rights to redistribute these changes.
59 * File: scc_8530_hdw.c
60 * Author: Alessandro Forin, Carnegie Mellon University
63 * Hardware-level operations for the SCC Serial Line Driver
66 #define NSCC 1 /* Number of serial chips, two ports per chip. */
70 #include <platforms.h>
72 #include <mach/std_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/serial_io.h>
80 #include <ppc/scc_8530.h>
83 #include <machine/db_machdep.h>
86 #define kdebug_state() (1)
87 #define delay(x) { volatile int _d_; for (_d_ = 0; _d_ < (10000*x); _d_++) ; }
89 #define NSCC_LINE 2 /* 2 ttys per chip */
91 #define SCC_DMA_TRANSFERS 0
93 struct scc_tty scc_tty
[NSCC_LINE
];
95 #define scc_tty_for(chan) (&scc_tty[chan])
96 /* #define scc_unit(dev_no) (dev_no) */
98 #define scc_dev_no(chan) ((chan)^0x01)
99 #define scc_chan(dev_no) ((dev_no)^0x01)
101 extern unsigned int disableSerialOuput
;
103 int serial_initted
= 0;
104 unsigned int scc_parm_done
= 0; /* (TEST/DEBUG) */
106 extern unsigned int serialmode
;
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
) /* (TEST/DEBUG) */
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
)
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
); /* (TEST/DEBUG) */
217 scc_softc
[0].full_modem
= TRUE
;
219 scc_std
[0] = scc_phys_base
;
221 regs
= scc_softc
[0].regs
= (scc_regmap_t
)scc_std
[0];
223 for (chan
= 0; chan
< NSCC_LINE
; chan
++) {
225 scc_init_hw
[0].val
= 0x80;
227 for (i
= 0; i
< scc_init_hw_count
; i
++) {
228 scc_write_reg(regs
, chan
,
229 scc_init_hw
[i
].reg
, scc_init_hw
[i
].val
);
233 /* Call probe so we are ready very early for remote gdb and for serial
234 console output if appropriate. */
236 for (i
= 0; i
< NSCC_LINE
; i
++) {
237 scc_softc
[0].softr
[i
].wr5
= SCC_WR5_DTR
| SCC_WR5_RTS
;
238 scc_param(scc_tty_for(i
));
239 /* Enable SCC interrupts (how many interrupts are to this thing?!?) */
240 scc_write_reg(regs
, i
, 9, SCC_WR9_NV
);
242 scc_read_reg_zero(regs
, 0, bits
);/* Clear the status */
244 scc_parm_done
= 1; /* (TEST/DEBUG) */
247 serial_initted
= TRUE
;
249 FUNNEL_EXIT(&SCC_FUNNEL
);
258 register scc_regmap_t regs
;
262 if (!SCC_FUNNEL_INITTED
) {
263 FUNNEL_INIT(&SCC_FUNNEL
, master_processor
);
264 SCC_FUNNEL_INITTED
= TRUE
;
266 FUNNEL_ENTER(&SCC_FUNNEL
);
268 /* Readjust the I/O address to handling
269 * new memory mappings.
272 regs
= (scc_regmap_t
)scc_std
[0];
274 if (regs
== (scc_regmap_t
) 0) {
275 FUNNEL_EXIT(&SCC_FUNNEL
);
284 for (i
= 0; i
< NSCC_LINE
; i
++) {
285 register struct scc_tty
*tp
;
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. */
293 tp
->t_ispeed
= DEFAULT_PORT0_SPEED
;
294 tp
->t_ospeed
= DEFAULT_PORT0_SPEED
;
296 tp
->t_ispeed
= DEFAULT_SPEED
;
297 tp
->t_ospeed
= DEFAULT_SPEED
;
299 tp
->t_flags
= DEFAULT_FLAGS
;
300 scc
->softr
[i
].speed
= -1;
302 /* do min buffering */
303 tp
->t_state
|= TS_MIN
;
305 tp
->t_dev
= scc_dev_no(i
);
310 FUNNEL_EXIT(&SCC_FUNNEL
);
315 * Get a char from a specific SCC line
316 * [this is only used for console&screen purposes]
317 * must be splhigh since it may be called from another routine under spl
321 scc_getc(int unit
, int line
, boolean_t wait
, boolean_t raw
)
323 register scc_regmap_t regs
;
324 unsigned char c
, value
;
325 int rcvalue
, from_line
;
329 FUNNEL_ENTER(&SCC_FUNNEL
);
331 simple_lock(&scc_stomp
); /* (TEST/DEBUG) */
332 regs
= scc_softc
[0].regs
;
335 * wait till something available
341 scc_read_reg_zero(regs
, line
, value
);
343 if (value
& SCC_RR0_RX_AVAIL
)
347 simple_unlock(&scc_stomp
); /* (TEST/DEBUG) */
349 FUNNEL_EXIT(&SCC_FUNNEL
);
355 * if nothing found return -1
358 scc_read_reg(regs
, line
, SCC_RR1
, value
);
359 scc_read_data(regs
, line
, c
);
362 if (console_is_serial() &&
364 /* Drop into the debugger */
365 simple_unlock(&scc_stomp
); /* (TEST/DEBUG) */
366 Debugger("Serial Line Request");
367 simple_lock(&scc_stomp
); /* (TEST/DEBUG) */
368 scc_write_reg(regs
, line
, SCC_RR0
, SCC_RESET_HIGHEST_IUS
);
372 simple_unlock(&scc_stomp
); /* (TEST/DEBUG) */
374 FUNNEL_EXIT(&SCC_FUNNEL
);
377 #endif /* MACH_KDB */
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
);
386 scc_write_reg(regs
, line
, SCC_RR0
, SCC_RESET_HIGHEST_IUS
);
391 scc_write_reg(regs
, line
, SCC_RR0
, SCC_RESET_HIGHEST_IUS
);
393 simple_unlock(&scc_stomp
); /* (TEST/DEBUG) */
396 FUNNEL_EXIT(&SCC_FUNNEL
);
401 * Put a char on a specific SCC line
402 * use splhigh since we might be doing a printf in high spl'd code
406 scc_putc(int unit
, int line
, int c
)
413 if (disableSerialOuput
)
417 FUNNEL_ENTER(&SCC_FUNNEL
);
418 simple_lock(&scc_stomp
); /* (TEST/DEBUG) */
420 regs
= scc_softc
[0].regs
;
423 scc_read_reg(regs
, line
, SCC_RR0
, value
);
424 if (value
& SCC_RR0_TX_EMPTY
)
429 scc_write_data(regs
, line
, c
);
430 /* wait for it to swallow the char ? */
433 scc_read_reg(regs
, line
, SCC_RR0
, value
);
434 if (value
& SCC_RR0_TX_EMPTY
)
437 scc_write_reg(regs
, line
, SCC_RR0
, SCC_RESET_HIGHEST_IUS
);
438 simple_unlock(&scc_stomp
); /* (TEST/DEBUG) */
442 FUNNEL_EXIT(&SCC_FUNNEL
);
448 powermac_scc_set_datum(scc_regmap_t regs
, unsigned int offset
, unsigned char value
)
450 volatile unsigned char *address
= (unsigned char *) regs
+ offset
;
452 assert(FUNNEL_IN_USE(&SCC_FUNNEL
));
457 assert(FUNNEL_IN_USE(&SCC_FUNNEL
));
461 powermac_scc_get_datum(scc_regmap_t regs
, unsigned int offset
)
463 volatile unsigned char *address
= (unsigned char *) regs
+ offset
;
466 assert(FUNNEL_IN_USE(&SCC_FUNNEL
));
468 value
= *address
; eieio();
471 assert(FUNNEL_IN_USE(&SCC_FUNNEL
));
475 scc_param(struct scc_tty
*tp
)
479 unsigned short speed_value
;
482 struct scc_softreg
*sr
;
485 assert(FUNNEL_IN_USE(&SCC_FUNNEL
));
488 simple_lock(&scc_stomp
); /* (TEST/DEBUG) */
490 chan
= scc_chan(tp
->t_dev
);
494 sr
= &scc
->softr
[chan
];
496 /* Do a quick check to see if the hardware needs to change */
497 if ((sr
->flags
& (TF_ODDP
|TF_EVENP
)) == (tp
->t_flags
& (TF_ODDP
|TF_EVENP
))
498 && sr
->speed
== tp
->t_ispeed
) {
499 assert(FUNNEL_IN_USE(&SCC_FUNNEL
));
500 simple_unlock(&scc_stomp
); /* (TEST/DEBUG) */
501 splx(s
); /* (TEST/DEBUG) */
502 return 0; /* (TEST/DEBUG) */
507 scc_write_reg(regs
, chan
, 3, SCC_WR3_RX_8_BITS
|SCC_WR3_RX_ENABLE
); /* (TEST/DEBUG) */
508 sr
->wr1
= SCC_WR1_RXI_FIRST_CHAR
| SCC_WR1_EXT_IE
; /* (TEST/DEBUG) */
509 scc_write_reg(regs
, chan
, 1, sr
->wr1
); /* (TEST/DEBUG) */
510 scc_write_reg(regs
, chan
, 15, SCC_WR15_ENABLE_ESCC
); /* (TEST/DEBUG) */
511 scc_write_reg(regs
, chan
, 7, SCC_WR7P_RX_FIFO
); /* (TEST/DEBUG) */
512 scc_write_reg(regs
, chan
, 0, SCC_IE_NEXT_CHAR
); /* (TEST/DEBUG) */
513 scc_write_reg(regs
, chan
, 0, SCC_RESET_EXT_IP
); /* (TEST/DEBUG) */
514 scc_write_reg(regs
, chan
, 0, SCC_RESET_EXT_IP
); /* (TEST/DEBUG) */
515 scc_write_reg(regs
, chan
, 9, SCC_WR9_MASTER_IE
|SCC_WR9_NV
); /* (TEST/DEBUG) */
516 scc_read_reg_zero(regs
, 0, bits
); /* (TEST/DEBUG) */
517 sr
->wr1
= SCC_WR1_RXI_FIRST_CHAR
| SCC_WR1_EXT_IE
; /* (TEST/DEBUG) */
518 scc_write_reg(regs
, chan
, 1, sr
->wr1
); /* (TEST/DEBUG) */
519 scc_write_reg(regs
, chan
, 0, SCC_IE_NEXT_CHAR
); /* (TEST/DEBUG) */
520 simple_unlock(&scc_stomp
); /* (TEST/DEBUG) */
521 splx(s
); /* (TEST/DEBUG) */
522 return 0; /* (TEST/DEBUG) */
525 sr
->flags
= tp
->t_flags
;
526 sr
->speed
= tp
->t_ispeed
;
529 if (tp
->t_ispeed
== 0) {
530 sr
->wr5
&= ~SCC_WR5_DTR
;
531 scc_write_reg(regs
, chan
, 5, sr
->wr5
);
532 simple_unlock(&scc_stomp
); /* (TEST/DEBUG) */
535 assert(FUNNEL_IN_USE(&SCC_FUNNEL
));
540 #if SCC_DMA_TRANSFERS
541 if (scc
->dma_initted
& (1<<chan
))
542 scc
->dma_ops
->scc_dma_reset_rx(chan
);
545 value
= SCC_WR4_1_STOP
;
548 * For 115K the clocking divide changes to 64.. to 230K will
549 * start at the normal clock divide 16.
551 * However, both speeds will pull from a different clocking
555 if (tp
->t_ispeed
== 115200)
556 value
|= SCC_WR4_CLK_x32
;
558 value
|= SCC_WR4_CLK_x16
;
561 if ((tp
->t_flags
& (TF_ODDP
| TF_EVENP
)) == TF_EVENP
)
562 value
|= (SCC_WR4_EVEN_PARITY
| SCC_WR4_PARITY_ENABLE
);
563 else if ((tp
->t_flags
& (TF_ODDP
| TF_EVENP
)) == TF_ODDP
)
564 value
|= SCC_WR4_PARITY_ENABLE
;
566 /* set it now, remember it must be first after reset */
569 /* Program Parity, and Stop bits */
570 scc_write_reg(regs
, chan
, 4, sr
->wr4
);
572 /* Setup for 8 bits */
573 scc_write_reg(regs
, chan
, 3, SCC_WR3_RX_8_BITS
);
575 // Set DTR, RTS, and transmitter bits/character.
576 sr
->wr5
= SCC_WR5_TX_8_BITS
| SCC_WR5_RTS
| SCC_WR5_DTR
;
578 scc_write_reg(regs
, chan
, 5, sr
->wr5
);
580 scc_write_reg(regs
, chan
, 14, 0); /* Disable baud rate */
582 /* Setup baud rate 57.6Kbps, 115K, 230K should all yeild
583 * a converted baud rate of zero
585 speed_value
= convert_baud_rate(tp
->t_ispeed
);
587 if (speed_value
== 0xffff)
590 scc_set_timing_base(regs
, chan
, speed_value
);
592 if (tp
->t_ispeed
== 115200 || tp
->t_ispeed
== 230400) {
593 /* Special case here.. change the clock source*/
594 scc_write_reg(regs
, chan
, 11, 0);
595 /* Baud rate generator is disabled.. */
597 scc_write_reg(regs
, chan
, 11, SCC_WR11_RCLK_BAUDR
|SCC_WR11_XTLK_BAUDR
);
598 /* Enable the baud rate generator */
599 scc_write_reg(regs
, chan
, 14, SCC_WR14_BAUDR_ENABLE
);
603 scc_write_reg(regs
, chan
, 3, SCC_WR3_RX_8_BITS
|SCC_WR3_RX_ENABLE
);
606 sr
->wr1
= SCC_WR1_RXI_FIRST_CHAR
| SCC_WR1_EXT_IE
;
607 scc_write_reg(regs
, chan
, 1, sr
->wr1
);
608 scc_write_reg(regs
, chan
, 15, SCC_WR15_ENABLE_ESCC
);
609 scc_write_reg(regs
, chan
, 7, SCC_WR7P_RX_FIFO
);
610 scc_write_reg(regs
, chan
, 0, SCC_IE_NEXT_CHAR
);
613 /* Clear out any pending external or status interrupts */
614 scc_write_reg(regs
, chan
, 0, SCC_RESET_EXT_IP
);
615 scc_write_reg(regs
, chan
, 0, SCC_RESET_EXT_IP
);
616 //scc_write_reg(regs, chan, 0, SCC_RESET_ERROR);
618 /* Enable SCC interrupts (how many interrupts are to this thing?!?) */
619 scc_write_reg(regs
, chan
, 9, SCC_WR9_MASTER_IE
|SCC_WR9_NV
);
621 scc_read_reg_zero(regs
, 0, bits
);/* Clear the status */
623 #if SCC_DMA_TRANSFERS
624 if (scc
->dma_initted
& (1<<chan
)) {
625 scc
->dma_ops
->scc_dma_start_rx(chan
);
626 scc
->dma_ops
->scc_dma_setup_8530(chan
);
630 sr
->wr1
= SCC_WR1_RXI_FIRST_CHAR
| SCC_WR1_EXT_IE
;
631 scc_write_reg(regs
, chan
, 1, sr
->wr1
);
632 scc_write_reg(regs
, chan
, 0, SCC_IE_NEXT_CHAR
);
635 sr
->wr5
|= SCC_WR5_TX_ENABLE
;
636 scc_write_reg(regs
, chan
, 5, sr
->wr5
);
638 simple_unlock(&scc_stomp
); /* (TEST/DEBUG) */
641 assert(FUNNEL_IN_USE(&SCC_FUNNEL
));
647 * This routine will start a thread that polls the serial port, listening for
648 * characters that have been typed.
652 serial_keyboard_init(void)
655 if(!(serialmode
& 2)) return; /* Leave if we do not want a serial console */
657 kprintf("Serial keyboard started\n");
658 kernel_thread_with_priority(serial_keyboard_start
, MAXPRI_STANDARD
);
663 serial_keyboard_start(void)
667 cthread
= current_thread(); /* Just who the heck are we anyway? */
668 stack_privilege(cthread
); /* Make sure we don't lose our stack */
669 serial_keyboard_poll(); /* Go see if there are any characters pending now */
670 panic("serial_keyboard_start: we can't get back here\n");
674 serial_keyboard_poll(void)
678 extern void cons_cinput(char ch
); /* The BSD routine that gets characters */
680 while(1) { /* Do this for a while */
681 chr
= scc_getc(0, 1, 0, 1); /* Get a character if there is one */
682 if(chr
< 0) break; /* The serial buffer is empty */
683 cons_cinput((char)chr
); /* Buffer up the character */
686 clock_interval_to_deadline(16, 1000000, &next
); /* Get time of pop */
688 assert_wait((event_t
)serial_keyboard_poll
, THREAD_INTERRUPTIBLE
); /* Show we are "waiting" */
689 thread_set_timer_deadline(next
); /* Set the next time to check */
690 thread_block(serial_keyboard_poll
); /* Wait for it */
691 panic("serial_keyboard_poll: Shouldn't never ever get here...\n");
694 #endif /* NSCC > 0 */