X-Git-Url: https://git.saurik.com/apple/xnu.git/blobdiff_plain/e5568f75972dfc723778653c11cb6b4dc825716a..813fb2f63a553c957e917ede5f119b021d6ce391:/pexpert/i386/pe_serial.c diff --git a/pexpert/i386/pe_serial.c b/pexpert/i386/pe_serial.c index 201b76e9c..91a10f867 100644 --- a/pexpert/i386/pe_serial.c +++ b/pexpert/i386/pe_serial.c @@ -1,23 +1,29 @@ /* - * Copyright (c) 2000 Apple Computer, Inc. All rights reserved. + * Copyright (c) 2000-2006 Apple Computer, Inc. All rights reserved. * - * @APPLE_LICENSE_HEADER_START@ + * @APPLE_OSREFERENCE_LICENSE_HEADER_START@ * - * The contents of this file constitute Original Code as defined in and - * are subject to the Apple Public Source License Version 1.1 (the - * "License"). You may not use this file except in compliance with the - * License. Please obtain a copy of the License at - * http://www.apple.com/publicsource and read it before using this file. + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. The rights granted to you under the License + * may not be used to create, or enable the creation or redistribution of, + * unlawful or unlicensed copies of an Apple operating system, or to + * circumvent, violate, or enable the circumvention or violation of, any + * terms of an Apple operating system software license agreement. * - * This Original Code and all software distributed under the License are - * distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT. Please see the - * License for the specific language governing rights and limitations - * under the License. + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. * - * @APPLE_LICENSE_HEADER_END@ + * @APPLE_OSREFERENCE_LICENSE_HEADER_END@ */ /* @@ -25,16 +31,42 @@ * Polled-mode 16x50 UART driver. */ +#include #include #include -/* standard port addresses */ +struct pe_serial_functions { + void (*uart_init) (void); + void (*uart_set_baud_rate) (int unit, uint32_t baud_rate); + int (*tr0) (void); + void (*td0) (int c); + int (*rr0) (void); + int (*rd0) (void); +}; + +static struct pe_serial_functions *gPESF; + +static int uart_initted = 0; /* 1 if init'ed */ + +#define DEFAULT_UART_BAUD_RATE 115200 + +static unsigned uart_baud_rate = DEFAULT_UART_BAUD_RATE; + +// ============================================================================= +// Legacy UART support using IO transactions to COM1 or COM2 +// ============================================================================= + +#define LEGACY_UART_PORT_ADDR COM1_PORT_ADDR +#define LEGACY_UART_CLOCK 1843200 /* 1.8432 MHz clock */ + +#define IO_WRITE(r, v) outb(LEGACY_UART_PORT_ADDR + UART_##r, v) +#define IO_READ(r) inb(LEGACY_UART_PORT_ADDR + UART_##r) + enum { COM1_PORT_ADDR = 0x3f8, COM2_PORT_ADDR = 0x2f8 }; -/* UART register offsets */ enum { UART_RBR = 0, /* receive buffer Register (R) */ UART_THR = 0, /* transmit holding register (W) */ @@ -46,7 +78,8 @@ enum { UART_LCR = 3, /* line control register */ UART_MCR = 4, /* modem control register */ UART_LSR = 5, /* line status register */ - UART_MSR = 6 /* modem status register */ + UART_MSR = 6, /* modem status register */ + UART_SCR = 7 /* scratch register */ }; enum { @@ -63,100 +96,337 @@ enum { }; enum { + UART_LSR_DR = 0x01, + UART_LSR_OE = 0x02, + UART_LSR_PE = 0x04, + UART_LSR_FE = 0x08, UART_LSR_THRE = 0x20 }; -#define UART_BAUD_RATE 115200 -#define UART_PORT_ADDR COM1_PORT_ADDR +static int +legacy_uart_probe( void ) +{ + /* Verify that the Scratch Register is accessible */ + + IO_WRITE( SCR, 0x5a ); + if (IO_READ(SCR) != 0x5a) return 0; + IO_WRITE( SCR, 0xa5 ); + if (IO_READ(SCR) != 0xa5) return 0; + return 1; +} -#define WRITE(r, v) outb(UART_PORT_ADDR + UART_##r, v) -#define READ(r) inb(UART_PORT_ADDR + UART_##r) -#define DELAY(x) { volatile int _d_; for (_d_ = 0; _d_ < (10000*x); _d_++) ; } +static void +legacy_uart_set_baud_rate( __unused int unit, uint32_t baud_rate ) +{ + const unsigned char lcr = IO_READ( LCR ); + unsigned long div; -static int uart_initted = 0; /* 1 if init'ed */ + if (baud_rate == 0) baud_rate = 9600; + div = LEGACY_UART_CLOCK / 16 / baud_rate; + IO_WRITE( LCR, lcr | UART_LCR_DLAB ); + IO_WRITE( DLM, (unsigned char)(div >> 8) ); + IO_WRITE( DLL, (unsigned char) div ); + IO_WRITE( LCR, lcr & ~UART_LCR_DLAB); +} static int -uart_probe( void ) +legacy_uart_tr0( void ) { - /* Verify that the Divisor Register is accessible */ + return (IO_READ(LSR) & UART_LSR_THRE); +} - WRITE( LCR, UART_LCR_DLAB ); - WRITE( DLL, 0x5a ); - if (READ(DLL) != 0x5a) return 0; - WRITE( DLL, 0xa5 ); - if (READ(DLL) != 0xa5) return 0; - WRITE( LCR, 0x00 ); - return 1; +static void +legacy_uart_td0( int c ) +{ + IO_WRITE( THR, c ); } static void -uart_set_baud_rate( unsigned long baud_rate ) +legacy_uart_init( void ) { - #define UART_CLOCK 1843200 /* 1.8432 MHz clock */ + /* Disable hardware interrupts */ - const unsigned char lcr = READ( LCR ); - unsigned long div; + IO_WRITE( MCR, 0 ); + IO_WRITE( IER, 0 ); - if (baud_rate == 0) baud_rate = 9600; - div = UART_CLOCK / 16 / baud_rate; - WRITE( LCR, lcr | UART_LCR_DLAB ); - WRITE( DLM, (unsigned char)(div >> 8) ); - WRITE( DLL, (unsigned char) div ); - WRITE( LCR, lcr & ~UART_LCR_DLAB); + /* Disable FIFO's for 16550 devices */ + + IO_WRITE( FCR, 0 ); + + /* Set for 8-bit, no parity, DLAB bit cleared */ + + IO_WRITE( LCR, UART_LCR_8BITS ); + + /* Set baud rate */ + + gPESF->uart_set_baud_rate ( 0, uart_baud_rate ); + + /* Assert DTR# and RTS# lines (OUT2?) */ + + IO_WRITE( MCR, UART_MCR_DTR | UART_MCR_RTS ); + + /* Clear any garbage in the input buffer */ + + IO_READ( RBR ); + + uart_initted = 1; +} + +static int +legacy_uart_rr0( void ) +{ + unsigned char lsr; + + lsr = IO_READ( LSR ); + + if ( lsr & (UART_LSR_FE | UART_LSR_PE | UART_LSR_OE) ) + { + IO_READ( RBR ); /* discard */ + return 0; + } + + return (lsr & UART_LSR_DR); +} + +static int +legacy_uart_rd0( void ) +{ + return IO_READ( RBR ); +} + +static struct pe_serial_functions legacy_uart_serial_functions = { + .uart_init = legacy_uart_init, + .uart_set_baud_rate = legacy_uart_set_baud_rate, + .tr0 = legacy_uart_tr0, + .td0 = legacy_uart_td0, + .rr0 = legacy_uart_rr0, + .rd0 = legacy_uart_rd0 +}; + +// ============================================================================= +// MMIO UART (using PCH LPSS UART2) +// ============================================================================= + +#define MMIO_UART2_BASE_LEGACY 0xFE034000 +#define MMIO_UART2_BASE 0xFE036000 + +#define MMIO_WRITE(r, v) ml_phys_write_word(mmio_uart_base + MMIO_UART_##r, v) +#define MMIO_READ(r) ml_phys_read_word(mmio_uart_base + MMIO_UART_##r) + +enum { + MMIO_UART_RBR = 0x0, /* receive buffer Register (R) */ + MMIO_UART_THR = 0x0, /* transmit holding register (W) */ + MMIO_UART_DLL = 0x0, /* DLAB = 1, divisor latch (LSB) */ + MMIO_UART_IER = 0x4, /* interrupt enable register */ + MMIO_UART_DLM = 0x4, /* DLAB = 1, divisor latch (MSB) */ + MMIO_UART_FCR = 0x8, /* fifo control register (W) */ + MMIO_UART_LCR = 0xc, /* line control register */ + MMIO_UART_MCR = 0x10, /* modem control register */ + MMIO_UART_LSR = 0x14, /* line status register */ + MMIO_UART_SCR = 0x1c /* scratch register */ +}; + +static vm_offset_t mmio_uart_base = 0; + +static int +mmio_uart_present( void ) +{ + MMIO_WRITE( SCR, 0x5a ); + if (MMIO_READ(SCR) != 0x5a) return 0; + MMIO_WRITE( SCR, 0xa5 ); + if (MMIO_READ(SCR) != 0xa5) return 0; + + return 1; +} + +static int +mmio_uart_probe( void ) +{ + unsigned new_mmio_uart_base = 0; + + // if specified, mmio_uart overrides all probing + if (PE_parse_boot_argn("mmio_uart", &new_mmio_uart_base, sizeof (new_mmio_uart_base))) + { + // mmio_uart=0 will disable mmio_uart support + if (new_mmio_uart_base == 0) { + return 0; + } + + mmio_uart_base = new_mmio_uart_base; + return 1; + } + + // probe the two possible MMIO_UART2 addresses + mmio_uart_base = MMIO_UART2_BASE; + if (mmio_uart_present()) { + return 1; + } + + mmio_uart_base = MMIO_UART2_BASE_LEGACY; + if (mmio_uart_present()) { + return 1; + } + + // no mmio uart found + return 0; } static void -uart_putc( char c ) +mmio_uart_set_baud_rate( __unused int unit, __unused uint32_t baud_rate ) { - if (!uart_initted) return; + const unsigned char lcr = MMIO_READ( LCR ); + unsigned long div; + + if (baud_rate == 0) baud_rate = 9600; + div = LEGACY_UART_CLOCK / 16 / baud_rate; - /* Wait for THR empty */ - while ( !(READ(LSR) & UART_LSR_THRE) ) DELAY(1); + MMIO_WRITE( LCR, lcr | UART_LCR_DLAB ); + MMIO_WRITE( DLM, (unsigned char)(div >> 8) ); + MMIO_WRITE( DLL, (unsigned char) div ); + MMIO_WRITE( LCR, lcr & ~UART_LCR_DLAB); +} - WRITE( THR, c ); +static int +mmio_uart_tr0( void ) +{ + return (MMIO_READ(LSR) & UART_LSR_THRE); } -int serial_init( void ) +static void +mmio_uart_td0( int c ) { - if ( uart_initted || uart_probe() == 0 ) return 0; + MMIO_WRITE( THR, c ); +} +static void +mmio_uart_init( void ) +{ /* Disable hardware interrupts */ - WRITE( MCR, 0 ); - WRITE( IER, 0 ); + MMIO_WRITE( MCR, 0 ); + MMIO_WRITE( IER, 0 ); /* Disable FIFO's for 16550 devices */ - WRITE( FCR, 0 ); + MMIO_WRITE( FCR, 0 ); /* Set for 8-bit, no parity, DLAB bit cleared */ - WRITE( LCR, UART_LCR_8BITS ); + MMIO_WRITE( LCR, UART_LCR_8BITS ); - /* Set baud rate */ + /* Leave baud rate as set by firmware unless serialbaud boot-arg overrides */ - uart_set_baud_rate( UART_BAUD_RATE ); + if (uart_baud_rate != DEFAULT_UART_BAUD_RATE) + { + gPESF->uart_set_baud_rate ( 0, uart_baud_rate ); + } /* Assert DTR# and RTS# lines (OUT2?) */ - WRITE( MCR, UART_MCR_DTR | UART_MCR_RTS ); + MMIO_WRITE( MCR, UART_MCR_DTR | UART_MCR_RTS ); /* Clear any garbage in the input buffer */ - READ( RBR ); + MMIO_READ( RBR ); uart_initted = 1; +} - return 1; +static int +mmio_uart_rr0( void ) +{ + unsigned char lsr; + + lsr = MMIO_READ( LSR ); + + if ( lsr & (UART_LSR_FE | UART_LSR_PE | UART_LSR_OE) ) + { + MMIO_READ( RBR ); /* discard */ + return 0; + } + + return (lsr & UART_LSR_DR); +} + +static int +mmio_uart_rd0( void ) +{ + return MMIO_READ( RBR ); +} + +static struct pe_serial_functions mmio_uart_serial_functions = { + .uart_init = mmio_uart_init, + .uart_set_baud_rate = mmio_uart_set_baud_rate, + .tr0 = mmio_uart_tr0, + .td0 = mmio_uart_td0, + .rr0 = mmio_uart_rr0, + .rd0 = mmio_uart_rd0 +}; + +// ============================================================================= +// Generic serial support below +// ============================================================================= + +int +serial_init( void ) +{ + unsigned new_uart_baud_rate = 0; + + if (PE_parse_boot_argn("serialbaud", &new_uart_baud_rate, sizeof (new_uart_baud_rate))) + { + /* Valid divisor? */ + if (!((LEGACY_UART_CLOCK / 16) % new_uart_baud_rate)) { + uart_baud_rate = new_uart_baud_rate; + } + } + + if ( mmio_uart_probe() ) + { + gPESF = &mmio_uart_serial_functions; + gPESF->uart_init(); + return 1; + } + else if ( legacy_uart_probe() ) + { + gPESF = &legacy_uart_serial_functions; + gPESF->uart_init(); + return 1; + } + else + { + return 0; + } + +} + +static void +uart_putc(char c) +{ + if (uart_initted) { + while (!gPESF->tr0()); /* Wait until THR is empty. */ + gPESF->td0(c); + } +} + +static int +uart_getc(void) +{ + if (uart_initted) { + if (!gPESF->rr0()) + return -1; + return gPESF->rd0(); + } + return -1; } -void serial_putc( char c ) +void +serial_putc( char c ) { uart_putc(c); - if (c == '\n') uart_putc('\r'); } -int serial_getc( void ) +int +serial_getc( void ) { - return 0; /* not supported */ + return uart_getc(); }