2 * Copyright (c) 2000 Apple Computer, Inc. All rights reserved.
4 * @APPLE_LICENSE_HEADER_START@
6 * The contents of this file constitute Original Code as defined in and
7 * are subject to the Apple Public Source License Version 1.1 (the
8 * "License"). You may not use this file except in compliance with the
9 * License. Please obtain a copy of the License at
10 * http://www.apple.com/publicsource and read it before using this file.
12 * This Original Code and all software distributed under the License are
13 * distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, EITHER
14 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
15 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
16 * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT. Please see the
17 * License for the specific language governing rights and limitations
20 * @APPLE_LICENSE_HEADER_END@
26 * @APPLE_FREE_COPYRIGHT@
29 * Mach Operating System
30 * Copyright (c) 1991,1990,1989 Carnegie Mellon University
31 * All Rights Reserved.
33 * Permission to use, copy, modify and distribute this software and its
34 * documentation is hereby granted, provided that both the copyright
35 * notice and this permission notice appear in all copies of the
36 * software, derivative works or modified versions, and any portions
37 * thereof, and that both notices appear in supporting documentation.
39 * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
40 * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
41 * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
43 * Carnegie Mellon requests users of this software to return to
45 * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU
46 * School of Computer Science
47 * Carnegie Mellon University
48 * Pittsburgh PA 15213-3890
50 * any improvements or extensions that they make and grant Carnegie Mellon
51 * the rights to redistribute these changes.
56 * File: scc_8530_hdw.c
57 * Author: Alessandro Forin, Carnegie Mellon University
60 * Hardware-level operations for the SCC Serial Line Driver
63 #define NSCC 1 /* Number of serial chips, two ports per chip. */
67 #include <platforms.h>
69 #include <mach/std_types.h>
71 #include <sys/syslog.h>
72 #include <ppc/misc_protos.h>
73 #include <ppc/proc_reg.h>
74 #include <ppc/exception.h>
75 #include <ppc/Firmware.h>
76 #include <ppc/POWERMAC/serial_io.h>
77 #include <pexpert/ppc/powermac.h>
78 #include <ppc/POWERMAC/scc_8530.h>
81 #include <machine/db_machdep.h>
84 #define kdebug_state() (1)
85 #define delay(x) { volatile int _d_; for (_d_ = 0; _d_ < (10000*x); _d_++) ; }
87 #define NSCC_LINE 2 /* 2 ttys per chip */
89 #define SCC_DMA_TRANSFERS 0
91 struct scc_tty scc_tty
[NSCC_LINE
];
93 #define scc_tty_for(chan) (&scc_tty[chan])
94 /* #define scc_unit(dev_no) (dev_no) */
96 #define scc_dev_no(chan) ((chan)^0x01)
97 #define scc_chan(dev_no) ((dev_no)^0x01)
99 extern unsigned int disableSerialOuput
;
101 int serial_initted
= 0;
102 unsigned int scc_parm_done
= 0; /* (TEST/DEBUG) */
104 static struct scc_byte
{
125 1, 0x12, /* int or Rx, Tx int enable */
127 1, 0x10, /* int or Rx, no Tx int enable */
132 static int scc_init_hw_count
= sizeof(scc_init_hw
)/sizeof(scc_init_hw
[0]);
134 enum scc_error
{SCC_ERR_NONE
, SCC_ERR_PARITY
, SCC_ERR_BREAK
, SCC_ERR_OVERRUN
};
139 * ClockFrequency (115200 for Power Mac)
140 * BRGconstant = --------------------------- - 2
144 #define SERIAL_CLOCK_FREQUENCY (115200*2) /* Power Mac value */
145 #define convert_baud_rate(rate) ((((SERIAL_CLOCK_FREQUENCY) + (rate)) / (2 * (rate))) - 2)
147 #define DEFAULT_SPEED 57600
148 #define DEFAULT_FLAGS (TF_LITOUT|TF_ECHO)
150 int scc_param(struct scc_tty
*tp
);
153 struct scc_softc scc_softc
[NSCC
];
154 caddr_t scc_std
[NSCC
] = { (caddr_t
) 0};
157 #define SCC_RR1_ERRS (SCC_RR1_FRAME_ERR|SCC_RR1_RX_OVERRUN|SCC_RR1_PARITY_ERR)
158 #define SCC_RR3_ALL (SCC_RR3_RX_IP_A|SCC_RR3_TX_IP_A|SCC_RR3_EXT_IP_A|\
159 SCC_RR3_RX_IP_B|SCC_RR3_TX_IP_B|SCC_RR3_EXT_IP_B)
165 static int total_chars
, total_ints
, total_overruns
, total_errors
, num_ints
, max_chars
;
166 static int chars_received
[8];
167 static int __SCC_STATS
= 0;
168 static int max_in_q
= 0;
169 static int max_out_q
= 0;
172 DECL_FUNNEL(, scc_funnel
) /* funnel to serialize the SCC driver */
173 boolean_t scc_funnel_initted
= FALSE
;
174 #define SCC_FUNNEL scc_funnel
175 #define SCC_FUNNEL_INITTED scc_funnel_initted
179 * Adapt/Probe/Attach functions
181 boolean_t scc_uses_modem_control
= FALSE
;/* patch this with adb */
182 decl_simple_lock_data(,scc_stomp
) /* (TEST/DEBUG) */
184 /* This is called VERY early on in the init and therefore has to have
185 * hardcoded addresses of the serial hardware control registers. The
186 * serial line may be needed for console and debugging output before
187 * anything else takes place
191 initialize_serial( caddr_t scc_phys_base
)
197 assert( scc_phys_base
);
199 if (!SCC_FUNNEL_INITTED
) {
200 FUNNEL_INIT(&SCC_FUNNEL
, master_processor
);
201 SCC_FUNNEL_INITTED
= TRUE
;
203 FUNNEL_ENTER(&SCC_FUNNEL
);
205 if (serial_initted
) {
206 FUNNEL_EXIT(&SCC_FUNNEL
);
210 simple_lock_init(&scc_stomp
, FALSE
); /* (TEST/DEBUG) */
212 scc_softc
[0].full_modem
= TRUE
;
214 scc_std
[0] = scc_phys_base
;
216 regs
= scc_softc
[0].regs
= (scc_regmap_t
)scc_std
[0];
218 for (chan
= 0; chan
< NSCC_LINE
; chan
++) {
220 scc_init_hw
[0].val
= 0x80;
222 for (i
= 0; i
< scc_init_hw_count
; i
++) {
223 scc_write_reg(regs
, chan
,
224 scc_init_hw
[i
].reg
, scc_init_hw
[i
].val
);
228 /* Call probe so we are ready very early for remote gdb and for serial
229 console output if appropriate. */
231 for (i
= 0; i
< NSCC_LINE
; i
++) {
232 scc_softc
[0].softr
[i
].wr5
= SCC_WR5_DTR
| SCC_WR5_RTS
;
233 scc_param(scc_tty_for(i
));
234 /* Enable SCC interrupts (how many interrupts are to this thing?!?) */
235 scc_write_reg(regs
, i
, 9, SCC_WR9_NV
);
237 scc_read_reg_zero(regs
, 0, bits
);/* Clear the status */
239 scc_parm_done
= 1; /* (TEST/DEBUG) */
242 serial_initted
= TRUE
;
244 FUNNEL_EXIT(&SCC_FUNNEL
);
253 register scc_regmap_t regs
;
257 if (!SCC_FUNNEL_INITTED
) {
258 FUNNEL_INIT(&SCC_FUNNEL
, master_processor
);
259 SCC_FUNNEL_INITTED
= TRUE
;
261 FUNNEL_ENTER(&SCC_FUNNEL
);
263 /* Readjust the I/O address to handling
264 * new memory mappings.
267 // scc_std[0] = POWERMAC_IO(scc_std[0]);
269 regs
= (scc_regmap_t
)scc_std
[0];
271 if (regs
== (scc_regmap_t
) 0) {
272 FUNNEL_EXIT(&SCC_FUNNEL
);
281 for (i
= 0; i
< NSCC_LINE
; i
++) {
282 register struct scc_tty
*tp
;
284 tp
->t_addr
= (char*)(0x80000000L
+ (i
&1));
285 /* Set default values. These will be overridden on
286 open but are needed if the port will be used
287 independently of the Mach interfaces, e.g., for
288 gdb or for a serial console. */
289 tp
->t_ispeed
= DEFAULT_SPEED
;
290 tp
->t_ospeed
= DEFAULT_SPEED
;
291 tp
->t_flags
= DEFAULT_FLAGS
;
292 scc
->softr
[i
].speed
= -1;
294 /* do min buffering */
295 tp
->t_state
|= TS_MIN
;
297 tp
->t_dev
= scc_dev_no(i
);
302 FUNNEL_EXIT(&SCC_FUNNEL
);
307 * Get a char from a specific SCC line
308 * [this is only used for console&screen purposes]
309 * must be splhigh since it may be called from another routine under spl
313 scc_getc(int unit
, int line
, boolean_t wait
, boolean_t raw
)
315 register scc_regmap_t regs
;
316 unsigned char c
, value
;
317 int rcvalue
, from_line
;
321 FUNNEL_ENTER(&SCC_FUNNEL
);
323 simple_lock(&scc_stomp
); /* (TEST/DEBUG) */
324 regs
= scc_softc
[0].regs
;
327 * wait till something available
333 scc_read_reg_zero(regs
, line
, value
);
335 if (value
& SCC_RR0_RX_AVAIL
)
339 simple_unlock(&scc_stomp
); /* (TEST/DEBUG) */
341 FUNNEL_EXIT(&SCC_FUNNEL
);
347 * if nothing found return -1
350 scc_read_reg(regs
, line
, SCC_RR1
, value
);
351 scc_read_data(regs
, line
, c
);
354 if (console_is_serial() &&
356 /* Drop into the debugger */
357 simple_unlock(&scc_stomp
); /* (TEST/DEBUG) */
358 Debugger("Serial Line Request");
359 simple_lock(&scc_stomp
); /* (TEST/DEBUG) */
360 scc_write_reg(regs
, line
, SCC_RR0
, SCC_RESET_HIGHEST_IUS
);
364 simple_unlock(&scc_stomp
); /* (TEST/DEBUG) */
366 FUNNEL_EXIT(&SCC_FUNNEL
);
369 #endif /* MACH_KDB */
374 if (value
&(SCC_RR1_PARITY_ERR
| SCC_RR1_RX_OVERRUN
| SCC_RR1_FRAME_ERR
)) {
375 scc_write_reg(regs
, line
, SCC_RR0
, SCC_RESET_ERROR
);
378 scc_write_reg(regs
, line
, SCC_RR0
, SCC_RESET_HIGHEST_IUS
);
383 scc_write_reg(regs
, line
, SCC_RR0
, SCC_RESET_HIGHEST_IUS
);
385 simple_unlock(&scc_stomp
); /* (TEST/DEBUG) */
388 FUNNEL_EXIT(&SCC_FUNNEL
);
393 * Put a char on a specific SCC line
394 * use splhigh since we might be doing a printf in high spl'd code
398 scc_putc(int unit
, int line
, int c
)
405 if (disableSerialOuput
)
409 FUNNEL_ENTER(&SCC_FUNNEL
);
410 simple_lock(&scc_stomp
); /* (TEST/DEBUG) */
412 regs
= scc_softc
[0].regs
;
415 scc_read_reg(regs
, line
, SCC_RR0
, value
);
416 if (value
& SCC_RR0_TX_EMPTY
)
421 scc_write_data(regs
, line
, c
);
422 /* wait for it to swallow the char ? */
425 scc_read_reg(regs
, line
, SCC_RR0
, value
);
426 if (value
& SCC_RR0_TX_EMPTY
)
429 scc_write_reg(regs
, line
, SCC_RR0
, SCC_RESET_HIGHEST_IUS
);
430 simple_unlock(&scc_stomp
); /* (TEST/DEBUG) */
434 FUNNEL_EXIT(&SCC_FUNNEL
);
440 powermac_scc_set_datum(scc_regmap_t regs
, unsigned int offset
, unsigned char value
)
442 volatile unsigned char *address
= (unsigned char *) regs
+ offset
;
444 assert(FUNNEL_IN_USE(&SCC_FUNNEL
));
449 assert(FUNNEL_IN_USE(&SCC_FUNNEL
));
453 powermac_scc_get_datum(scc_regmap_t regs
, unsigned int offset
)
455 volatile unsigned char *address
= (unsigned char *) regs
+ offset
;
458 assert(FUNNEL_IN_USE(&SCC_FUNNEL
));
460 value
= *address
; eieio();
463 assert(FUNNEL_IN_USE(&SCC_FUNNEL
));
467 scc_param(struct scc_tty
*tp
)
471 unsigned short speed_value
;
474 struct scc_softreg
*sr
;
477 assert(FUNNEL_IN_USE(&SCC_FUNNEL
));
480 simple_lock(&scc_stomp
); /* (TEST/DEBUG) */
482 chan
= scc_chan(tp
->t_dev
);
486 sr
= &scc
->softr
[chan
];
488 /* Do a quick check to see if the hardware needs to change */
489 if ((sr
->flags
& (TF_ODDP
|TF_EVENP
)) == (tp
->t_flags
& (TF_ODDP
|TF_EVENP
))
490 && sr
->speed
== tp
->t_ispeed
) {
491 assert(FUNNEL_IN_USE(&SCC_FUNNEL
));
492 simple_unlock(&scc_stomp
); /* (TEST/DEBUG) */
493 splx(s
); /* (TEST/DEBUG) */
494 return 0; /* (TEST/DEBUG) */
499 scc_write_reg(regs
, chan
, 3, SCC_WR3_RX_8_BITS
|SCC_WR3_RX_ENABLE
); /* (TEST/DEBUG) */
500 sr
->wr1
= SCC_WR1_RXI_FIRST_CHAR
| SCC_WR1_EXT_IE
; /* (TEST/DEBUG) */
501 scc_write_reg(regs
, chan
, 1, sr
->wr1
); /* (TEST/DEBUG) */
502 scc_write_reg(regs
, chan
, 15, SCC_WR15_ENABLE_ESCC
); /* (TEST/DEBUG) */
503 scc_write_reg(regs
, chan
, 7, SCC_WR7P_RX_FIFO
); /* (TEST/DEBUG) */
504 scc_write_reg(regs
, chan
, 0, SCC_IE_NEXT_CHAR
); /* (TEST/DEBUG) */
505 scc_write_reg(regs
, chan
, 0, SCC_RESET_EXT_IP
); /* (TEST/DEBUG) */
506 scc_write_reg(regs
, chan
, 0, SCC_RESET_EXT_IP
); /* (TEST/DEBUG) */
507 scc_write_reg(regs
, chan
, 9, SCC_WR9_MASTER_IE
|SCC_WR9_NV
); /* (TEST/DEBUG) */
508 scc_read_reg_zero(regs
, 0, bits
); /* (TEST/DEBUG) */
509 sr
->wr1
= SCC_WR1_RXI_FIRST_CHAR
| SCC_WR1_EXT_IE
; /* (TEST/DEBUG) */
510 scc_write_reg(regs
, chan
, 1, sr
->wr1
); /* (TEST/DEBUG) */
511 scc_write_reg(regs
, chan
, 0, SCC_IE_NEXT_CHAR
); /* (TEST/DEBUG) */
512 simple_unlock(&scc_stomp
); /* (TEST/DEBUG) */
513 splx(s
); /* (TEST/DEBUG) */
514 return 0; /* (TEST/DEBUG) */
517 sr
->flags
= tp
->t_flags
;
518 sr
->speed
= tp
->t_ispeed
;
521 if (tp
->t_ispeed
== 0) {
522 sr
->wr5
&= ~SCC_WR5_DTR
;
523 scc_write_reg(regs
, chan
, 5, sr
->wr5
);
524 simple_unlock(&scc_stomp
); /* (TEST/DEBUG) */
527 assert(FUNNEL_IN_USE(&SCC_FUNNEL
));
532 #if SCC_DMA_TRANSFERS
533 if (scc
->dma_initted
& (1<<chan
))
534 scc
->dma_ops
->scc_dma_reset_rx(chan
);
537 value
= SCC_WR4_1_STOP
;
540 * For 115K the clocking divide changes to 64.. to 230K will
541 * start at the normal clock divide 16.
543 * However, both speeds will pull from a different clocking
547 if (tp
->t_ispeed
== 115200)
548 value
|= SCC_WR4_CLK_x32
;
550 value
|= SCC_WR4_CLK_x16
;
553 if ((tp
->t_flags
& (TF_ODDP
| TF_EVENP
)) == TF_EVENP
)
554 value
|= (SCC_WR4_EVEN_PARITY
| SCC_WR4_PARITY_ENABLE
);
555 else if ((tp
->t_flags
& (TF_ODDP
| TF_EVENP
)) == TF_ODDP
)
556 value
|= SCC_WR4_PARITY_ENABLE
;
558 /* set it now, remember it must be first after reset */
561 /* Program Parity, and Stop bits */
562 scc_write_reg(regs
, chan
, 4, sr
->wr4
);
564 /* Setup for 8 bits */
565 scc_write_reg(regs
, chan
, 3, SCC_WR3_RX_8_BITS
);
567 // Set DTR, RTS, and transmitter bits/character.
568 sr
->wr5
= SCC_WR5_TX_8_BITS
| SCC_WR5_RTS
| SCC_WR5_DTR
;
570 scc_write_reg(regs
, chan
, 5, sr
->wr5
);
572 scc_write_reg(regs
, chan
, 14, 0); /* Disable baud rate */
574 /* Setup baud rate 57.6Kbps, 115K, 230K should all yeild
575 * a converted baud rate of zero
577 speed_value
= convert_baud_rate(tp
->t_ispeed
);
579 if (speed_value
== 0xffff)
582 scc_set_timing_base(regs
, chan
, speed_value
);
584 if (tp
->t_ispeed
== 115200 || tp
->t_ispeed
== 230400) {
585 /* Special case here.. change the clock source*/
586 scc_write_reg(regs
, chan
, 11, 0);
587 /* Baud rate generator is disabled.. */
589 scc_write_reg(regs
, chan
, 11, SCC_WR11_RCLK_BAUDR
|SCC_WR11_XTLK_BAUDR
);
590 /* Enable the baud rate generator */
591 scc_write_reg(regs
, chan
, 14, SCC_WR14_BAUDR_ENABLE
);
595 scc_write_reg(regs
, chan
, 3, SCC_WR3_RX_8_BITS
|SCC_WR3_RX_ENABLE
);
598 sr
->wr1
= SCC_WR1_RXI_FIRST_CHAR
| SCC_WR1_EXT_IE
;
599 scc_write_reg(regs
, chan
, 1, sr
->wr1
);
600 scc_write_reg(regs
, chan
, 15, SCC_WR15_ENABLE_ESCC
);
601 scc_write_reg(regs
, chan
, 7, SCC_WR7P_RX_FIFO
);
602 scc_write_reg(regs
, chan
, 0, SCC_IE_NEXT_CHAR
);
605 /* Clear out any pending external or status interrupts */
606 scc_write_reg(regs
, chan
, 0, SCC_RESET_EXT_IP
);
607 scc_write_reg(regs
, chan
, 0, SCC_RESET_EXT_IP
);
608 //scc_write_reg(regs, chan, 0, SCC_RESET_ERROR);
610 /* Enable SCC interrupts (how many interrupts are to this thing?!?) */
611 scc_write_reg(regs
, chan
, 9, SCC_WR9_MASTER_IE
|SCC_WR9_NV
);
613 scc_read_reg_zero(regs
, 0, bits
);/* Clear the status */
615 #if SCC_DMA_TRANSFERS
616 if (scc
->dma_initted
& (1<<chan
)) {
617 scc
->dma_ops
->scc_dma_start_rx(chan
);
618 scc
->dma_ops
->scc_dma_setup_8530(chan
);
622 sr
->wr1
= SCC_WR1_RXI_FIRST_CHAR
| SCC_WR1_EXT_IE
;
623 scc_write_reg(regs
, chan
, 1, sr
->wr1
);
624 scc_write_reg(regs
, chan
, 0, SCC_IE_NEXT_CHAR
);
627 sr
->wr5
|= SCC_WR5_TX_ENABLE
;
628 scc_write_reg(regs
, chan
, 5, sr
->wr5
);
630 simple_unlock(&scc_stomp
); /* (TEST/DEBUG) */
633 assert(FUNNEL_IN_USE(&SCC_FUNNEL
));
638 #endif /* NSCC > 0 */