]>
git.saurik.com Git - apple/xnu.git/blob - osfmk/ppc/POWERMAC/serial_io.c
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;
107 extern unsigned int serialmode
;
109 static struct scc_byte
{
130 1, 0x12, /* int or Rx, Tx int enable */
132 1, 0x10, /* int or Rx, no Tx int enable */
137 static int scc_init_hw_count
= sizeof(scc_init_hw
)/sizeof(scc_init_hw
[0]);
139 enum scc_error
{SCC_ERR_NONE
, SCC_ERR_PARITY
, SCC_ERR_BREAK
, SCC_ERR_OVERRUN
};
144 * ClockFrequency (115200 for Power Mac)
145 * 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
);
157 struct scc_softc scc_softc
[NSCC
];
158 caddr_t scc_std
[NSCC
] = { (caddr_t
) 0};
160 decl_simple_lock_data(,scc_stomp
)
162 /* This is called VERY early on in the init and therefore has to have
163 * hardcoded addresses of the serial hardware control registers. The
164 * serial line may be needed for console and debugging output before
165 * anything else takes place
169 initialize_serial( caddr_t scc_phys_base
)
174 assert( scc_phys_base
);
179 simple_lock_init(&scc_stomp
, FALSE
);
181 scc_softc
[0].full_modem
= TRUE
;
183 scc_std
[0] = scc_phys_base
;
185 regs
= scc_softc
[0].regs
= (scc_regmap_t
)scc_std
[0];
187 for (chan
= 0; chan
< NSCC_LINE
; chan
++) {
189 scc_init_hw
[0].val
= 0x80;
191 for (i
= 0; i
< scc_init_hw_count
; i
++) {
192 scc_write_reg(regs
, chan
,
193 scc_init_hw
[i
].reg
, scc_init_hw
[i
].val
);
197 /* Call probe so we are ready very early for remote gdb and for serial
198 console output if appropriate. */
200 for (i
= 0; i
< NSCC_LINE
; i
++) {
201 scc_softc
[0].softr
[i
].wr5
= SCC_WR5_DTR
| SCC_WR5_RTS
;
202 scc_param(scc_tty_for(i
));
203 /* Enable SCC interrupts (how many interrupts are to this thing?!?) */
204 scc_write_reg(regs
, i
, 9, SCC_WR9_NV
);
206 scc_read_reg_zero(regs
, 0, bits
);/* Clear the status */
211 serial_initted
= TRUE
;
220 register scc_regmap_t regs
;
223 regs
= (scc_regmap_t
)scc_std
[0];
225 if (regs
== (scc_regmap_t
) 0)
233 for (i
= 0; i
< NSCC_LINE
; i
++) {
234 register struct scc_tty
*tp
;
236 tp
->t_addr
= (char*)(0x80000000L
+ (i
&1));
237 /* Set default values. These will be overridden on
238 open but are needed if the port will be used
239 independently of the Mach interfaces, e.g., for
240 gdb or for a serial console. */
242 tp
->t_ispeed
= DEFAULT_PORT0_SPEED
;
243 tp
->t_ospeed
= DEFAULT_PORT0_SPEED
;
245 tp
->t_ispeed
= DEFAULT_SPEED
;
246 tp
->t_ospeed
= DEFAULT_SPEED
;
248 tp
->t_flags
= DEFAULT_FLAGS
;
249 scc
->softr
[i
].speed
= -1;
251 /* do min buffering */
252 tp
->t_state
|= TS_MIN
;
254 tp
->t_dev
= scc_dev_no(i
);
262 * Get a char from a specific SCC line
263 * [this is only used for console&screen purposes]
264 * must be splhigh since it may be called from another routine under spl
268 scc_getc(int unit
, int line
, boolean_t wait
, boolean_t raw
)
270 register scc_regmap_t regs
;
271 unsigned char c
, value
;
272 int rcvalue
, from_line
;
275 simple_lock(&scc_stomp
);
276 regs
= scc_softc
[0].regs
;
279 * wait till something available
284 scc_read_reg_zero(regs
, line
, value
);
286 if (value
& SCC_RR0_RX_AVAIL
)
290 simple_unlock(&scc_stomp
);
297 * if nothing found return -1
300 scc_read_reg(regs
, line
, SCC_RR1
, value
);
301 scc_read_data(regs
, line
, c
);
304 if (console_is_serial() &&
306 /* Drop into the debugger */
307 simple_unlock(&scc_stomp
);
308 Debugger("Serial Line Request");
309 simple_lock(&scc_stomp
);
310 scc_write_reg(regs
, line
, SCC_RR0
, SCC_RESET_HIGHEST_IUS
);
315 simple_unlock(&scc_stomp
);
319 #endif /* MACH_KDB */
324 if (value
&(SCC_RR1_PARITY_ERR
| SCC_RR1_RX_OVERRUN
| SCC_RR1_FRAME_ERR
)) {
325 scc_write_reg(regs
, line
, SCC_RR0
, SCC_RESET_ERROR
);
328 scc_write_reg(regs
, line
, SCC_RR0
, SCC_RESET_HIGHEST_IUS
);
332 scc_write_reg(regs
, line
, SCC_RR0
, SCC_RESET_HIGHEST_IUS
);
335 simple_unlock(&scc_stomp
);
341 * Put a char on a specific SCC line
342 * use splhigh since we might be doing a printf in high spl'd code
346 scc_putc(int unit
, int line
, int c
)
352 if (disableSerialOuput
)
356 simple_lock(&scc_stomp
);
358 regs
= scc_softc
[0].regs
;
361 scc_read_reg(regs
, line
, SCC_RR0
, value
);
362 if (value
& SCC_RR0_TX_EMPTY
)
367 scc_write_data(regs
, line
, c
);
368 /* wait for it to swallow the char ? */
371 scc_read_reg(regs
, line
, SCC_RR0
, value
);
372 if (value
& SCC_RR0_TX_EMPTY
)
375 scc_write_reg(regs
, line
, SCC_RR0
, SCC_RESET_HIGHEST_IUS
);
378 simple_unlock(&scc_stomp
);
385 powermac_scc_set_datum(scc_regmap_t regs
, unsigned int offset
, unsigned char value
)
387 volatile unsigned char *address
= (unsigned char *) regs
+ offset
;
394 powermac_scc_get_datum(scc_regmap_t regs
, unsigned int offset
)
396 volatile unsigned char *address
= (unsigned char *) regs
+ offset
;
405 scc_param(struct scc_tty
*tp
)
409 unsigned short speed_value
;
412 struct scc_softreg
*sr
;
416 simple_lock(&scc_stomp
);
418 chan
= scc_chan(tp
->t_dev
);
422 sr
= &scc
->softr
[chan
];
424 /* Do a quick check to see if the hardware needs to change */
425 if ((sr
->flags
& (TF_ODDP
|TF_EVENP
)) == (tp
->t_flags
& (TF_ODDP
|TF_EVENP
))
426 && sr
->speed
== tp
->t_ispeed
) {
427 simple_unlock(&scc_stomp
);
434 scc_write_reg(regs
, chan
, 3, SCC_WR3_RX_8_BITS
|SCC_WR3_RX_ENABLE
);
435 sr
->wr1
= SCC_WR1_RXI_FIRST_CHAR
| SCC_WR1_EXT_IE
;
436 scc_write_reg(regs
, chan
, 1, sr
->wr1
);
437 scc_write_reg(regs
, chan
, 15, SCC_WR15_ENABLE_ESCC
);
438 scc_write_reg(regs
, chan
, 7, SCC_WR7P_RX_FIFO
);
439 scc_write_reg(regs
, chan
, 0, SCC_IE_NEXT_CHAR
);
440 scc_write_reg(regs
, chan
, 0, SCC_RESET_EXT_IP
);
441 scc_write_reg(regs
, chan
, 0, SCC_RESET_EXT_IP
);
442 scc_write_reg(regs
, chan
, 9, SCC_WR9_MASTER_IE
|SCC_WR9_NV
);
443 scc_read_reg_zero(regs
, 0, bits
);
444 sr
->wr1
= SCC_WR1_RXI_FIRST_CHAR
| SCC_WR1_EXT_IE
;
445 scc_write_reg(regs
, chan
, 1, sr
->wr1
);
446 scc_write_reg(regs
, chan
, 0, SCC_IE_NEXT_CHAR
);
448 simple_unlock(&scc_stomp
);
453 sr
->flags
= tp
->t_flags
;
454 sr
->speed
= tp
->t_ispeed
;
457 if (tp
->t_ispeed
== 0) {
458 sr
->wr5
&= ~SCC_WR5_DTR
;
459 scc_write_reg(regs
, chan
, 5, sr
->wr5
);
461 simple_unlock(&scc_stomp
);
467 #if SCC_DMA_TRANSFERS
468 if (scc
->dma_initted
& (1<<chan
))
469 scc
->dma_ops
->scc_dma_reset_rx(chan
);
472 value
= SCC_WR4_1_STOP
;
475 * For 115K the clocking divide changes to 64.. to 230K will
476 * start at the normal clock divide 16.
478 * However, both speeds will pull from a different clocking
482 if (tp
->t_ispeed
== 115200)
483 value
|= SCC_WR4_CLK_x32
;
485 value
|= SCC_WR4_CLK_x16
;
488 if ((tp
->t_flags
& (TF_ODDP
| TF_EVENP
)) == TF_EVENP
)
489 value
|= (SCC_WR4_EVEN_PARITY
| SCC_WR4_PARITY_ENABLE
);
490 else if ((tp
->t_flags
& (TF_ODDP
| TF_EVENP
)) == TF_ODDP
)
491 value
|= SCC_WR4_PARITY_ENABLE
;
493 /* set it now, remember it must be first after reset */
496 /* Program Parity, and Stop bits */
497 scc_write_reg(regs
, chan
, 4, sr
->wr4
);
499 /* Setup for 8 bits */
500 scc_write_reg(regs
, chan
, 3, SCC_WR3_RX_8_BITS
);
502 // Set DTR, RTS, and transmitter bits/character.
503 sr
->wr5
= SCC_WR5_TX_8_BITS
| SCC_WR5_RTS
| SCC_WR5_DTR
;
505 scc_write_reg(regs
, chan
, 5, sr
->wr5
);
507 scc_write_reg(regs
, chan
, 14, 0); /* Disable baud rate */
509 /* Setup baud rate 57.6Kbps, 115K, 230K should all yeild
510 * a converted baud rate of zero
512 speed_value
= convert_baud_rate(tp
->t_ispeed
);
514 if (speed_value
== 0xffff)
517 scc_set_timing_base(regs
, chan
, speed_value
);
519 if (tp
->t_ispeed
== 115200 || tp
->t_ispeed
== 230400) {
520 /* Special case here.. change the clock source*/
521 scc_write_reg(regs
, chan
, 11, 0);
522 /* Baud rate generator is disabled.. */
524 scc_write_reg(regs
, chan
, 11, SCC_WR11_RCLK_BAUDR
|SCC_WR11_XTLK_BAUDR
);
525 /* Enable the baud rate generator */
526 scc_write_reg(regs
, chan
, 14, SCC_WR14_BAUDR_ENABLE
);
530 scc_write_reg(regs
, chan
, 3, SCC_WR3_RX_8_BITS
|SCC_WR3_RX_ENABLE
);
533 sr
->wr1
= SCC_WR1_RXI_FIRST_CHAR
| SCC_WR1_EXT_IE
;
534 scc_write_reg(regs
, chan
, 1, sr
->wr1
);
535 scc_write_reg(regs
, chan
, 15, SCC_WR15_ENABLE_ESCC
);
536 scc_write_reg(regs
, chan
, 7, SCC_WR7P_RX_FIFO
);
537 scc_write_reg(regs
, chan
, 0, SCC_IE_NEXT_CHAR
);
540 /* Clear out any pending external or status interrupts */
541 scc_write_reg(regs
, chan
, 0, SCC_RESET_EXT_IP
);
542 scc_write_reg(regs
, chan
, 0, SCC_RESET_EXT_IP
);
543 //scc_write_reg(regs, chan, 0, SCC_RESET_ERROR);
545 /* Enable SCC interrupts (how many interrupts are to this thing?!?) */
546 scc_write_reg(regs
, chan
, 9, SCC_WR9_MASTER_IE
|SCC_WR9_NV
);
548 scc_read_reg_zero(regs
, 0, bits
);/* Clear the status */
550 #if SCC_DMA_TRANSFERS
551 if (scc
->dma_initted
& (1<<chan
)) {
552 scc
->dma_ops
->scc_dma_start_rx(chan
);
553 scc
->dma_ops
->scc_dma_setup_8530(chan
);
557 sr
->wr1
= SCC_WR1_RXI_FIRST_CHAR
| SCC_WR1_EXT_IE
;
558 scc_write_reg(regs
, chan
, 1, sr
->wr1
);
559 scc_write_reg(regs
, chan
, 0, SCC_IE_NEXT_CHAR
);
562 sr
->wr5
|= SCC_WR5_TX_ENABLE
;
563 scc_write_reg(regs
, chan
, 5, sr
->wr5
);
566 simple_unlock(&scc_stomp
);
573 * This routine will start a thread that polls the serial port, listening for
574 * characters that have been typed.
578 serial_keyboard_init(void)
581 if(!(serialmode
& 2)) return; /* Leave if we do not want a serial console */
583 kprintf("Serial keyboard started\n");
584 kernel_thread_with_priority(kernel_task
, MAXPRI_STANDARD
, serial_keyboard_start
, TRUE
, TRUE
);
589 serial_keyboard_start(void)
593 cthread
= current_thread(); /* Just who the heck are we anyway? */
594 stack_privilege(cthread
); /* Make sure we don't lose our stack */
595 serial_keyboard_poll(); /* Go see if there are any characters pending now */
596 panic("serial_keyboard_start: we can't get back here\n");
600 serial_keyboard_poll(void)
604 extern void cons_cinput(char ch
); /* The BSD routine that gets characters */
606 while(1) { /* Do this for a while */
607 chr
= scc_getc(0, 1, 0, 1); /* Get a character if there is one */
608 if(chr
< 0) break; /* The serial buffer is empty */
609 cons_cinput((char)chr
); /* Buffer up the character */
612 clock_interval_to_deadline(16, 1000000, &next
); /* Get time of pop */
614 assert_wait((event_t
)serial_keyboard_poll
, THREAD_INTERRUPTIBLE
); /* Show we are "waiting" */
615 thread_set_timer_deadline(next
); /* Set the next time to check */
616 thread_block(serial_keyboard_poll
); /* Wait for it */
617 panic("serial_keyboard_poll: Shouldn't never ever get here...\n");
620 #endif /* NSCC > 0 */