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/POWERMAC/serial_io.h>
80 #include <pexpert/ppc/powermac.h>
81 #include <ppc/POWERMAC/scc_8530.h>
84 #include <machine/db_machdep.h>
87 #define kdebug_state() (1)
88 #define delay(x) { volatile int _d_; for (_d_ = 0; _d_ < (10000*x); _d_++) ; }
90 #define NSCC_LINE 2 /* 2 ttys per chip */
92 #define SCC_DMA_TRANSFERS 0
94 struct scc_tty scc_tty
[NSCC_LINE
];
96 #define scc_tty_for(chan) (&scc_tty[chan])
97 /* #define scc_unit(dev_no) (dev_no) */
99 #define scc_dev_no(chan) ((chan)^0x01)
100 #define scc_chan(dev_no) ((dev_no)^0x01)
102 extern unsigned int disableSerialOuput
;
104 int serial_initted
= 0;
105 unsigned int scc_parm_done
= 0; /* (TEST/DEBUG) */
107 static struct scc_byte
{
128 1, 0x12, /* int or Rx, Tx int enable */
130 1, 0x10, /* int or Rx, no Tx int enable */
135 static int scc_init_hw_count
= sizeof(scc_init_hw
)/sizeof(scc_init_hw
[0]);
137 enum scc_error
{SCC_ERR_NONE
, SCC_ERR_PARITY
, SCC_ERR_BREAK
, SCC_ERR_OVERRUN
};
142 * ClockFrequency (115200 for Power Mac)
143 * BRGconstant = --------------------------- - 2
147 #define SERIAL_CLOCK_FREQUENCY (115200*2) /* Power Mac value */
148 #define convert_baud_rate(rate) ((((SERIAL_CLOCK_FREQUENCY) + (rate)) / (2 * (rate))) - 2)
150 #define DEFAULT_SPEED 57600
151 #define DEFAULT_FLAGS (TF_LITOUT|TF_ECHO)
153 int scc_param(struct scc_tty
*tp
);
156 struct scc_softc scc_softc
[NSCC
];
157 caddr_t scc_std
[NSCC
] = { (caddr_t
) 0};
160 #define SCC_RR1_ERRS (SCC_RR1_FRAME_ERR|SCC_RR1_RX_OVERRUN|SCC_RR1_PARITY_ERR)
161 #define SCC_RR3_ALL (SCC_RR3_RX_IP_A|SCC_RR3_TX_IP_A|SCC_RR3_EXT_IP_A|\
162 SCC_RR3_RX_IP_B|SCC_RR3_TX_IP_B|SCC_RR3_EXT_IP_B)
168 static int total_chars
, total_ints
, total_overruns
, total_errors
, num_ints
, max_chars
;
169 static int chars_received
[8];
170 static int __SCC_STATS
= 0;
171 static int max_in_q
= 0;
172 static int max_out_q
= 0;
175 DECL_FUNNEL(, scc_funnel
) /* funnel to serialize the SCC driver */
176 boolean_t scc_funnel_initted
= FALSE
;
177 #define SCC_FUNNEL scc_funnel
178 #define SCC_FUNNEL_INITTED scc_funnel_initted
182 * Adapt/Probe/Attach functions
184 boolean_t scc_uses_modem_control
= FALSE
;/* patch this with adb */
185 decl_simple_lock_data(,scc_stomp
) /* (TEST/DEBUG) */
187 /* This is called VERY early on in the init and therefore has to have
188 * hardcoded addresses of the serial hardware control registers. The
189 * serial line may be needed for console and debugging output before
190 * anything else takes place
194 initialize_serial( caddr_t scc_phys_base
)
200 assert( scc_phys_base
);
202 if (!SCC_FUNNEL_INITTED
) {
203 FUNNEL_INIT(&SCC_FUNNEL
, master_processor
);
204 SCC_FUNNEL_INITTED
= TRUE
;
206 FUNNEL_ENTER(&SCC_FUNNEL
);
208 if (serial_initted
) {
209 FUNNEL_EXIT(&SCC_FUNNEL
);
213 simple_lock_init(&scc_stomp
, FALSE
); /* (TEST/DEBUG) */
215 scc_softc
[0].full_modem
= TRUE
;
217 scc_std
[0] = scc_phys_base
;
219 regs
= scc_softc
[0].regs
= (scc_regmap_t
)scc_std
[0];
221 for (chan
= 0; chan
< NSCC_LINE
; chan
++) {
223 scc_init_hw
[0].val
= 0x80;
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
);
231 /* Call probe so we are ready very early for remote gdb and for serial
232 console output if appropriate. */
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
);
240 scc_read_reg_zero(regs
, 0, bits
);/* Clear the status */
242 scc_parm_done
= 1; /* (TEST/DEBUG) */
245 serial_initted
= TRUE
;
247 FUNNEL_EXIT(&SCC_FUNNEL
);
256 register scc_regmap_t regs
;
260 if (!SCC_FUNNEL_INITTED
) {
261 FUNNEL_INIT(&SCC_FUNNEL
, master_processor
);
262 SCC_FUNNEL_INITTED
= TRUE
;
264 FUNNEL_ENTER(&SCC_FUNNEL
);
266 /* Readjust the I/O address to handling
267 * new memory mappings.
270 // scc_std[0] = POWERMAC_IO(scc_std[0]);
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. */
292 tp
->t_ispeed
= DEFAULT_SPEED
;
293 tp
->t_ospeed
= DEFAULT_SPEED
;
294 tp
->t_flags
= DEFAULT_FLAGS
;
295 scc
->softr
[i
].speed
= -1;
297 /* do min buffering */
298 tp
->t_state
|= TS_MIN
;
300 tp
->t_dev
= scc_dev_no(i
);
305 FUNNEL_EXIT(&SCC_FUNNEL
);
310 * Get a char from a specific SCC line
311 * [this is only used for console&screen purposes]
312 * must be splhigh since it may be called from another routine under spl
316 scc_getc(int unit
, int line
, boolean_t wait
, boolean_t raw
)
318 register scc_regmap_t regs
;
319 unsigned char c
, value
;
320 int rcvalue
, from_line
;
324 FUNNEL_ENTER(&SCC_FUNNEL
);
326 simple_lock(&scc_stomp
); /* (TEST/DEBUG) */
327 regs
= scc_softc
[0].regs
;
330 * wait till something available
336 scc_read_reg_zero(regs
, line
, value
);
338 if (value
& SCC_RR0_RX_AVAIL
)
342 simple_unlock(&scc_stomp
); /* (TEST/DEBUG) */
344 FUNNEL_EXIT(&SCC_FUNNEL
);
350 * if nothing found return -1
353 scc_read_reg(regs
, line
, SCC_RR1
, value
);
354 scc_read_data(regs
, line
, c
);
357 if (console_is_serial() &&
359 /* Drop into the debugger */
360 simple_unlock(&scc_stomp
); /* (TEST/DEBUG) */
361 Debugger("Serial Line Request");
362 simple_lock(&scc_stomp
); /* (TEST/DEBUG) */
363 scc_write_reg(regs
, line
, SCC_RR0
, SCC_RESET_HIGHEST_IUS
);
367 simple_unlock(&scc_stomp
); /* (TEST/DEBUG) */
369 FUNNEL_EXIT(&SCC_FUNNEL
);
372 #endif /* MACH_KDB */
377 if (value
&(SCC_RR1_PARITY_ERR
| SCC_RR1_RX_OVERRUN
| SCC_RR1_FRAME_ERR
)) {
378 scc_write_reg(regs
, line
, SCC_RR0
, SCC_RESET_ERROR
);
381 scc_write_reg(regs
, line
, SCC_RR0
, SCC_RESET_HIGHEST_IUS
);
386 scc_write_reg(regs
, line
, SCC_RR0
, SCC_RESET_HIGHEST_IUS
);
388 simple_unlock(&scc_stomp
); /* (TEST/DEBUG) */
391 FUNNEL_EXIT(&SCC_FUNNEL
);
396 * Put a char on a specific SCC line
397 * use splhigh since we might be doing a printf in high spl'd code
401 scc_putc(int unit
, int line
, int c
)
408 if (disableSerialOuput
)
412 FUNNEL_ENTER(&SCC_FUNNEL
);
413 simple_lock(&scc_stomp
); /* (TEST/DEBUG) */
415 regs
= scc_softc
[0].regs
;
418 scc_read_reg(regs
, line
, SCC_RR0
, value
);
419 if (value
& SCC_RR0_TX_EMPTY
)
424 scc_write_data(regs
, line
, c
);
425 /* wait for it to swallow the char ? */
428 scc_read_reg(regs
, line
, SCC_RR0
, value
);
429 if (value
& SCC_RR0_TX_EMPTY
)
432 scc_write_reg(regs
, line
, SCC_RR0
, SCC_RESET_HIGHEST_IUS
);
433 simple_unlock(&scc_stomp
); /* (TEST/DEBUG) */
437 FUNNEL_EXIT(&SCC_FUNNEL
);
443 powermac_scc_set_datum(scc_regmap_t regs
, unsigned int offset
, unsigned char value
)
445 volatile unsigned char *address
= (unsigned char *) regs
+ offset
;
447 assert(FUNNEL_IN_USE(&SCC_FUNNEL
));
452 assert(FUNNEL_IN_USE(&SCC_FUNNEL
));
456 powermac_scc_get_datum(scc_regmap_t regs
, unsigned int offset
)
458 volatile unsigned char *address
= (unsigned char *) regs
+ offset
;
461 assert(FUNNEL_IN_USE(&SCC_FUNNEL
));
463 value
= *address
; eieio();
466 assert(FUNNEL_IN_USE(&SCC_FUNNEL
));
470 scc_param(struct scc_tty
*tp
)
474 unsigned short speed_value
;
477 struct scc_softreg
*sr
;
480 assert(FUNNEL_IN_USE(&SCC_FUNNEL
));
483 simple_lock(&scc_stomp
); /* (TEST/DEBUG) */
485 chan
= scc_chan(tp
->t_dev
);
489 sr
= &scc
->softr
[chan
];
491 /* Do a quick check to see if the hardware needs to change */
492 if ((sr
->flags
& (TF_ODDP
|TF_EVENP
)) == (tp
->t_flags
& (TF_ODDP
|TF_EVENP
))
493 && sr
->speed
== tp
->t_ispeed
) {
494 assert(FUNNEL_IN_USE(&SCC_FUNNEL
));
495 simple_unlock(&scc_stomp
); /* (TEST/DEBUG) */
496 splx(s
); /* (TEST/DEBUG) */
497 return 0; /* (TEST/DEBUG) */
502 scc_write_reg(regs
, chan
, 3, SCC_WR3_RX_8_BITS
|SCC_WR3_RX_ENABLE
); /* (TEST/DEBUG) */
503 sr
->wr1
= SCC_WR1_RXI_FIRST_CHAR
| SCC_WR1_EXT_IE
; /* (TEST/DEBUG) */
504 scc_write_reg(regs
, chan
, 1, sr
->wr1
); /* (TEST/DEBUG) */
505 scc_write_reg(regs
, chan
, 15, SCC_WR15_ENABLE_ESCC
); /* (TEST/DEBUG) */
506 scc_write_reg(regs
, chan
, 7, SCC_WR7P_RX_FIFO
); /* (TEST/DEBUG) */
507 scc_write_reg(regs
, chan
, 0, SCC_IE_NEXT_CHAR
); /* (TEST/DEBUG) */
508 scc_write_reg(regs
, chan
, 0, SCC_RESET_EXT_IP
); /* (TEST/DEBUG) */
509 scc_write_reg(regs
, chan
, 0, SCC_RESET_EXT_IP
); /* (TEST/DEBUG) */
510 scc_write_reg(regs
, chan
, 9, SCC_WR9_MASTER_IE
|SCC_WR9_NV
); /* (TEST/DEBUG) */
511 scc_read_reg_zero(regs
, 0, bits
); /* (TEST/DEBUG) */
512 sr
->wr1
= SCC_WR1_RXI_FIRST_CHAR
| SCC_WR1_EXT_IE
; /* (TEST/DEBUG) */
513 scc_write_reg(regs
, chan
, 1, sr
->wr1
); /* (TEST/DEBUG) */
514 scc_write_reg(regs
, chan
, 0, SCC_IE_NEXT_CHAR
); /* (TEST/DEBUG) */
515 simple_unlock(&scc_stomp
); /* (TEST/DEBUG) */
516 splx(s
); /* (TEST/DEBUG) */
517 return 0; /* (TEST/DEBUG) */
520 sr
->flags
= tp
->t_flags
;
521 sr
->speed
= tp
->t_ispeed
;
524 if (tp
->t_ispeed
== 0) {
525 sr
->wr5
&= ~SCC_WR5_DTR
;
526 scc_write_reg(regs
, chan
, 5, sr
->wr5
);
527 simple_unlock(&scc_stomp
); /* (TEST/DEBUG) */
530 assert(FUNNEL_IN_USE(&SCC_FUNNEL
));
535 #if SCC_DMA_TRANSFERS
536 if (scc
->dma_initted
& (1<<chan
))
537 scc
->dma_ops
->scc_dma_reset_rx(chan
);
540 value
= SCC_WR4_1_STOP
;
543 * For 115K the clocking divide changes to 64.. to 230K will
544 * start at the normal clock divide 16.
546 * However, both speeds will pull from a different clocking
550 if (tp
->t_ispeed
== 115200)
551 value
|= SCC_WR4_CLK_x32
;
553 value
|= SCC_WR4_CLK_x16
;
556 if ((tp
->t_flags
& (TF_ODDP
| TF_EVENP
)) == TF_EVENP
)
557 value
|= (SCC_WR4_EVEN_PARITY
| SCC_WR4_PARITY_ENABLE
);
558 else if ((tp
->t_flags
& (TF_ODDP
| TF_EVENP
)) == TF_ODDP
)
559 value
|= SCC_WR4_PARITY_ENABLE
;
561 /* set it now, remember it must be first after reset */
564 /* Program Parity, and Stop bits */
565 scc_write_reg(regs
, chan
, 4, sr
->wr4
);
567 /* Setup for 8 bits */
568 scc_write_reg(regs
, chan
, 3, SCC_WR3_RX_8_BITS
);
570 // Set DTR, RTS, and transmitter bits/character.
571 sr
->wr5
= SCC_WR5_TX_8_BITS
| SCC_WR5_RTS
| SCC_WR5_DTR
;
573 scc_write_reg(regs
, chan
, 5, sr
->wr5
);
575 scc_write_reg(regs
, chan
, 14, 0); /* Disable baud rate */
577 /* Setup baud rate 57.6Kbps, 115K, 230K should all yeild
578 * a converted baud rate of zero
580 speed_value
= convert_baud_rate(tp
->t_ispeed
);
582 if (speed_value
== 0xffff)
585 scc_set_timing_base(regs
, chan
, speed_value
);
587 if (tp
->t_ispeed
== 115200 || tp
->t_ispeed
== 230400) {
588 /* Special case here.. change the clock source*/
589 scc_write_reg(regs
, chan
, 11, 0);
590 /* Baud rate generator is disabled.. */
592 scc_write_reg(regs
, chan
, 11, SCC_WR11_RCLK_BAUDR
|SCC_WR11_XTLK_BAUDR
);
593 /* Enable the baud rate generator */
594 scc_write_reg(regs
, chan
, 14, SCC_WR14_BAUDR_ENABLE
);
598 scc_write_reg(regs
, chan
, 3, SCC_WR3_RX_8_BITS
|SCC_WR3_RX_ENABLE
);
601 sr
->wr1
= SCC_WR1_RXI_FIRST_CHAR
| SCC_WR1_EXT_IE
;
602 scc_write_reg(regs
, chan
, 1, sr
->wr1
);
603 scc_write_reg(regs
, chan
, 15, SCC_WR15_ENABLE_ESCC
);
604 scc_write_reg(regs
, chan
, 7, SCC_WR7P_RX_FIFO
);
605 scc_write_reg(regs
, chan
, 0, SCC_IE_NEXT_CHAR
);
608 /* Clear out any pending external or status interrupts */
609 scc_write_reg(regs
, chan
, 0, SCC_RESET_EXT_IP
);
610 scc_write_reg(regs
, chan
, 0, SCC_RESET_EXT_IP
);
611 //scc_write_reg(regs, chan, 0, SCC_RESET_ERROR);
613 /* Enable SCC interrupts (how many interrupts are to this thing?!?) */
614 scc_write_reg(regs
, chan
, 9, SCC_WR9_MASTER_IE
|SCC_WR9_NV
);
616 scc_read_reg_zero(regs
, 0, bits
);/* Clear the status */
618 #if SCC_DMA_TRANSFERS
619 if (scc
->dma_initted
& (1<<chan
)) {
620 scc
->dma_ops
->scc_dma_start_rx(chan
);
621 scc
->dma_ops
->scc_dma_setup_8530(chan
);
625 sr
->wr1
= SCC_WR1_RXI_FIRST_CHAR
| SCC_WR1_EXT_IE
;
626 scc_write_reg(regs
, chan
, 1, sr
->wr1
);
627 scc_write_reg(regs
, chan
, 0, SCC_IE_NEXT_CHAR
);
630 sr
->wr5
|= SCC_WR5_TX_ENABLE
;
631 scc_write_reg(regs
, chan
, 5, sr
->wr5
);
633 simple_unlock(&scc_stomp
); /* (TEST/DEBUG) */
636 assert(FUNNEL_IN_USE(&SCC_FUNNEL
));
641 #endif /* NSCC > 0 */