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 int serial_initted
= 0;
100 unsigned int scc_parm_done
= 0; /* (TEST/DEBUG) */
102 static struct scc_byte
{
123 1, 0x12, /* int or Rx, Tx int enable */
125 1, 0x10, /* int or Rx, no Tx int enable */
130 static int scc_init_hw_count
= sizeof(scc_init_hw
)/sizeof(scc_init_hw
[0]);
132 enum scc_error
{SCC_ERR_NONE
, SCC_ERR_PARITY
, SCC_ERR_BREAK
, SCC_ERR_OVERRUN
};
137 * ClockFrequency (115200 for Power Mac)
138 * BRGconstant = --------------------------- - 2
142 #define SERIAL_CLOCK_FREQUENCY (115200*2) /* Power Mac value */
143 #define convert_baud_rate(rate) ((((SERIAL_CLOCK_FREQUENCY) + (rate)) / (2 * (rate))) - 2)
145 #define DEFAULT_SPEED 57600
146 #define DEFAULT_FLAGS (TF_LITOUT|TF_ECHO)
148 int scc_param(struct scc_tty
*tp
);
151 struct scc_softc scc_softc
[NSCC
];
152 caddr_t scc_std
[NSCC
] = { (caddr_t
) 0};
155 #define SCC_RR1_ERRS (SCC_RR1_FRAME_ERR|SCC_RR1_RX_OVERRUN|SCC_RR1_PARITY_ERR)
156 #define SCC_RR3_ALL (SCC_RR3_RX_IP_A|SCC_RR3_TX_IP_A|SCC_RR3_EXT_IP_A|\
157 SCC_RR3_RX_IP_B|SCC_RR3_TX_IP_B|SCC_RR3_EXT_IP_B)
163 static int total_chars
, total_ints
, total_overruns
, total_errors
, num_ints
, max_chars
;
164 static int chars_received
[8];
165 static int __SCC_STATS
= 0;
166 static int max_in_q
= 0;
167 static int max_out_q
= 0;
170 DECL_FUNNEL(, scc_funnel
) /* funnel to serialize the SCC driver */
171 boolean_t scc_funnel_initted
= FALSE
;
172 #define SCC_FUNNEL scc_funnel
173 #define SCC_FUNNEL_INITTED scc_funnel_initted
177 * Adapt/Probe/Attach functions
179 boolean_t scc_uses_modem_control
= FALSE
;/* patch this with adb */
180 decl_simple_lock_data(,scc_stomp
) /* (TEST/DEBUG) */
182 /* This is called VERY early on in the init and therefore has to have
183 * hardcoded addresses of the serial hardware control registers. The
184 * serial line may be needed for console and debugging output before
185 * anything else takes place
189 initialize_serial( caddr_t scc_phys_base
)
195 assert( scc_phys_base
);
197 if (!SCC_FUNNEL_INITTED
) {
198 FUNNEL_INIT(&SCC_FUNNEL
, master_processor
);
199 SCC_FUNNEL_INITTED
= TRUE
;
201 FUNNEL_ENTER(&SCC_FUNNEL
);
203 if (serial_initted
) {
204 FUNNEL_EXIT(&SCC_FUNNEL
);
208 simple_lock_init(&scc_stomp
, FALSE
); /* (TEST/DEBUG) */
210 scc_softc
[0].full_modem
= TRUE
;
212 scc_std
[0] = scc_phys_base
;
214 regs
= scc_softc
[0].regs
= (scc_regmap_t
)scc_std
[0];
216 for (chan
= 0; chan
< NSCC_LINE
; chan
++) {
218 scc_init_hw
[0].val
= 0x80;
220 for (i
= 0; i
< scc_init_hw_count
; i
++) {
221 scc_write_reg(regs
, chan
,
222 scc_init_hw
[i
].reg
, scc_init_hw
[i
].val
);
226 /* Call probe so we are ready very early for remote gdb and for serial
227 console output if appropriate. */
229 for (i
= 0; i
< NSCC_LINE
; i
++) {
230 scc_softc
[0].softr
[i
].wr5
= SCC_WR5_DTR
| SCC_WR5_RTS
;
231 scc_param(scc_tty_for(i
));
232 /* Enable SCC interrupts (how many interrupts are to this thing?!?) */
233 scc_write_reg(regs
, i
, 9, SCC_WR9_NV
);
235 scc_read_reg_zero(regs
, 0, bits
);/* Clear the status */
237 scc_parm_done
= 1; /* (TEST/DEBUG) */
240 serial_initted
= TRUE
;
242 FUNNEL_EXIT(&SCC_FUNNEL
);
251 register scc_regmap_t regs
;
255 if (!SCC_FUNNEL_INITTED
) {
256 FUNNEL_INIT(&SCC_FUNNEL
, master_processor
);
257 SCC_FUNNEL_INITTED
= TRUE
;
259 FUNNEL_ENTER(&SCC_FUNNEL
);
261 /* Readjust the I/O address to handling
262 * new memory mappings.
265 // scc_std[0] = POWERMAC_IO(scc_std[0]);
267 regs
= (scc_regmap_t
)scc_std
[0];
269 if (regs
== (scc_regmap_t
) 0) {
270 FUNNEL_EXIT(&SCC_FUNNEL
);
279 for (i
= 0; i
< NSCC_LINE
; i
++) {
280 register struct scc_tty
*tp
;
282 tp
->t_addr
= (char*)(0x80000000L
+ (i
&1));
283 /* Set default values. These will be overridden on
284 open but are needed if the port will be used
285 independently of the Mach interfaces, e.g., for
286 gdb or for a serial console. */
287 tp
->t_ispeed
= DEFAULT_SPEED
;
288 tp
->t_ospeed
= DEFAULT_SPEED
;
289 tp
->t_flags
= DEFAULT_FLAGS
;
290 scc
->softr
[i
].speed
= -1;
292 /* do min buffering */
293 tp
->t_state
|= TS_MIN
;
295 tp
->t_dev
= scc_dev_no(i
);
300 FUNNEL_EXIT(&SCC_FUNNEL
);
305 * Get a char from a specific SCC line
306 * [this is only used for console&screen purposes]
307 * must be splhigh since it may be called from another routine under spl
311 scc_getc(int unit
, int line
, boolean_t wait
, boolean_t raw
)
313 register scc_regmap_t regs
;
314 unsigned char c
, value
;
315 int rcvalue
, from_line
;
319 FUNNEL_ENTER(&SCC_FUNNEL
);
321 simple_lock(&scc_stomp
); /* (TEST/DEBUG) */
322 regs
= scc_softc
[0].regs
;
325 * wait till something available
331 scc_read_reg_zero(regs
, line
, value
);
333 if (value
& SCC_RR0_RX_AVAIL
)
337 simple_unlock(&scc_stomp
); /* (TEST/DEBUG) */
339 FUNNEL_EXIT(&SCC_FUNNEL
);
345 * if nothing found return -1
348 scc_read_reg(regs
, line
, SCC_RR1
, value
);
349 scc_read_data(regs
, line
, c
);
352 if (console_is_serial() &&
354 /* Drop into the debugger */
355 simple_unlock(&scc_stomp
); /* (TEST/DEBUG) */
356 Debugger("Serial Line Request");
357 simple_lock(&scc_stomp
); /* (TEST/DEBUG) */
358 scc_write_reg(regs
, line
, SCC_RR0
, SCC_RESET_HIGHEST_IUS
);
362 simple_unlock(&scc_stomp
); /* (TEST/DEBUG) */
364 FUNNEL_EXIT(&SCC_FUNNEL
);
367 #endif /* MACH_KDB */
372 if (value
&(SCC_RR1_PARITY_ERR
| SCC_RR1_RX_OVERRUN
| SCC_RR1_FRAME_ERR
)) {
373 scc_write_reg(regs
, line
, SCC_RR0
, SCC_RESET_ERROR
);
376 scc_write_reg(regs
, line
, SCC_RR0
, SCC_RESET_HIGHEST_IUS
);
381 scc_write_reg(regs
, line
, SCC_RR0
, SCC_RESET_HIGHEST_IUS
);
383 simple_unlock(&scc_stomp
); /* (TEST/DEBUG) */
386 FUNNEL_EXIT(&SCC_FUNNEL
);
391 * Put a char on a specific SCC line
392 * use splhigh since we might be doing a printf in high spl'd code
396 scc_putc(int unit
, int line
, int c
)
403 FUNNEL_ENTER(&SCC_FUNNEL
);
404 simple_lock(&scc_stomp
); /* (TEST/DEBUG) */
406 regs
= scc_softc
[0].regs
;
409 scc_read_reg(regs
, line
, SCC_RR0
, value
);
410 if (value
& SCC_RR0_TX_EMPTY
)
415 scc_write_data(regs
, line
, c
);
416 /* wait for it to swallow the char ? */
419 scc_read_reg(regs
, line
, SCC_RR0
, value
);
420 if (value
& SCC_RR0_TX_EMPTY
)
423 scc_write_reg(regs
, line
, SCC_RR0
, SCC_RESET_HIGHEST_IUS
);
424 simple_unlock(&scc_stomp
); /* (TEST/DEBUG) */
428 FUNNEL_EXIT(&SCC_FUNNEL
);
434 powermac_scc_set_datum(scc_regmap_t regs
, unsigned int offset
, unsigned char value
)
436 volatile unsigned char *address
= (unsigned char *) regs
+ offset
;
438 assert(FUNNEL_IN_USE(&SCC_FUNNEL
));
443 assert(FUNNEL_IN_USE(&SCC_FUNNEL
));
447 powermac_scc_get_datum(scc_regmap_t regs
, unsigned int offset
)
449 volatile unsigned char *address
= (unsigned char *) regs
+ offset
;
452 assert(FUNNEL_IN_USE(&SCC_FUNNEL
));
454 value
= *address
; eieio();
457 assert(FUNNEL_IN_USE(&SCC_FUNNEL
));
461 scc_param(struct scc_tty
*tp
)
465 unsigned short speed_value
;
468 struct scc_softreg
*sr
;
471 assert(FUNNEL_IN_USE(&SCC_FUNNEL
));
474 simple_lock(&scc_stomp
); /* (TEST/DEBUG) */
476 chan
= scc_chan(tp
->t_dev
);
480 sr
= &scc
->softr
[chan
];
482 /* Do a quick check to see if the hardware needs to change */
483 if ((sr
->flags
& (TF_ODDP
|TF_EVENP
)) == (tp
->t_flags
& (TF_ODDP
|TF_EVENP
))
484 && sr
->speed
== tp
->t_ispeed
) {
485 assert(FUNNEL_IN_USE(&SCC_FUNNEL
));
486 simple_unlock(&scc_stomp
); /* (TEST/DEBUG) */
487 splx(s
); /* (TEST/DEBUG) */
488 return 0; /* (TEST/DEBUG) */
493 scc_write_reg(regs
, chan
, 3, SCC_WR3_RX_8_BITS
|SCC_WR3_RX_ENABLE
); /* (TEST/DEBUG) */
494 sr
->wr1
= SCC_WR1_RXI_FIRST_CHAR
| SCC_WR1_EXT_IE
; /* (TEST/DEBUG) */
495 scc_write_reg(regs
, chan
, 1, sr
->wr1
); /* (TEST/DEBUG) */
496 scc_write_reg(regs
, chan
, 15, SCC_WR15_ENABLE_ESCC
); /* (TEST/DEBUG) */
497 scc_write_reg(regs
, chan
, 7, SCC_WR7P_RX_FIFO
); /* (TEST/DEBUG) */
498 scc_write_reg(regs
, chan
, 0, SCC_IE_NEXT_CHAR
); /* (TEST/DEBUG) */
499 scc_write_reg(regs
, chan
, 0, SCC_RESET_EXT_IP
); /* (TEST/DEBUG) */
500 scc_write_reg(regs
, chan
, 0, SCC_RESET_EXT_IP
); /* (TEST/DEBUG) */
501 scc_write_reg(regs
, chan
, 9, SCC_WR9_MASTER_IE
|SCC_WR9_NV
); /* (TEST/DEBUG) */
502 scc_read_reg_zero(regs
, 0, bits
); /* (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
, 0, SCC_IE_NEXT_CHAR
); /* (TEST/DEBUG) */
506 simple_unlock(&scc_stomp
); /* (TEST/DEBUG) */
507 splx(s
); /* (TEST/DEBUG) */
508 return 0; /* (TEST/DEBUG) */
511 sr
->flags
= tp
->t_flags
;
512 sr
->speed
= tp
->t_ispeed
;
515 if (tp
->t_ispeed
== 0) {
516 sr
->wr5
&= ~SCC_WR5_DTR
;
517 scc_write_reg(regs
, chan
, 5, sr
->wr5
);
518 simple_unlock(&scc_stomp
); /* (TEST/DEBUG) */
521 assert(FUNNEL_IN_USE(&SCC_FUNNEL
));
526 #if SCC_DMA_TRANSFERS
527 if (scc
->dma_initted
& (1<<chan
))
528 scc
->dma_ops
->scc_dma_reset_rx(chan
);
531 value
= SCC_WR4_1_STOP
;
534 * For 115K the clocking divide changes to 64.. to 230K will
535 * start at the normal clock divide 16.
537 * However, both speeds will pull from a different clocking
541 if (tp
->t_ispeed
== 115200)
542 value
|= SCC_WR4_CLK_x32
;
544 value
|= SCC_WR4_CLK_x16
;
547 if ((tp
->t_flags
& (TF_ODDP
| TF_EVENP
)) == TF_EVENP
)
548 value
|= (SCC_WR4_EVEN_PARITY
| SCC_WR4_PARITY_ENABLE
);
549 else if ((tp
->t_flags
& (TF_ODDP
| TF_EVENP
)) == TF_ODDP
)
550 value
|= SCC_WR4_PARITY_ENABLE
;
552 /* set it now, remember it must be first after reset */
555 /* Program Parity, and Stop bits */
556 scc_write_reg(regs
, chan
, 4, sr
->wr4
);
558 /* Setup for 8 bits */
559 scc_write_reg(regs
, chan
, 3, SCC_WR3_RX_8_BITS
);
561 // Set DTR, RTS, and transmitter bits/character.
562 sr
->wr5
= SCC_WR5_TX_8_BITS
| SCC_WR5_RTS
| SCC_WR5_DTR
;
564 scc_write_reg(regs
, chan
, 5, sr
->wr5
);
566 scc_write_reg(regs
, chan
, 14, 0); /* Disable baud rate */
568 /* Setup baud rate 57.6Kbps, 115K, 230K should all yeild
569 * a converted baud rate of zero
571 speed_value
= convert_baud_rate(tp
->t_ispeed
);
573 if (speed_value
== 0xffff)
576 scc_set_timing_base(regs
, chan
, speed_value
);
578 if (tp
->t_ispeed
== 115200 || tp
->t_ispeed
== 230400) {
579 /* Special case here.. change the clock source*/
580 scc_write_reg(regs
, chan
, 11, 0);
581 /* Baud rate generator is disabled.. */
583 scc_write_reg(regs
, chan
, 11, SCC_WR11_RCLK_BAUDR
|SCC_WR11_XTLK_BAUDR
);
584 /* Enable the baud rate generator */
585 scc_write_reg(regs
, chan
, 14, SCC_WR14_BAUDR_ENABLE
);
589 scc_write_reg(regs
, chan
, 3, SCC_WR3_RX_8_BITS
|SCC_WR3_RX_ENABLE
);
592 sr
->wr1
= SCC_WR1_RXI_FIRST_CHAR
| SCC_WR1_EXT_IE
;
593 scc_write_reg(regs
, chan
, 1, sr
->wr1
);
594 scc_write_reg(regs
, chan
, 15, SCC_WR15_ENABLE_ESCC
);
595 scc_write_reg(regs
, chan
, 7, SCC_WR7P_RX_FIFO
);
596 scc_write_reg(regs
, chan
, 0, SCC_IE_NEXT_CHAR
);
599 /* Clear out any pending external or status interrupts */
600 scc_write_reg(regs
, chan
, 0, SCC_RESET_EXT_IP
);
601 scc_write_reg(regs
, chan
, 0, SCC_RESET_EXT_IP
);
602 //scc_write_reg(regs, chan, 0, SCC_RESET_ERROR);
604 /* Enable SCC interrupts (how many interrupts are to this thing?!?) */
605 scc_write_reg(regs
, chan
, 9, SCC_WR9_MASTER_IE
|SCC_WR9_NV
);
607 scc_read_reg_zero(regs
, 0, bits
);/* Clear the status */
609 #if SCC_DMA_TRANSFERS
610 if (scc
->dma_initted
& (1<<chan
)) {
611 scc
->dma_ops
->scc_dma_start_rx(chan
);
612 scc
->dma_ops
->scc_dma_setup_8530(chan
);
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
, 0, SCC_IE_NEXT_CHAR
);
621 sr
->wr5
|= SCC_WR5_TX_ENABLE
;
622 scc_write_reg(regs
, chan
, 5, sr
->wr5
);
624 simple_unlock(&scc_stomp
); /* (TEST/DEBUG) */
627 assert(FUNNEL_IN_USE(&SCC_FUNNEL
));
632 #endif /* NSCC > 0 */