X-Git-Url: https://git.saurik.com/apple/xnu.git/blobdiff_plain/8f6c56a50524aa785f7e596d52dddfb331e18961..2a1bd2d3eef5c7a7bb14f4bb9fdbca9a96ee4752:/pexpert/i386/pe_serial.c diff --git a/pexpert/i386/pe_serial.c b/pexpert/i386/pe_serial.c index ff104bba5..0c5abf15c 100644 --- a/pexpert/i386/pe_serial.c +++ b/pexpert/i386/pe_serial.c @@ -1,8 +1,8 @@ /* - * Copyright (c) 2000 Apple Computer, Inc. All rights reserved. + * Copyright (c) 2000-2006 Apple Computer, Inc. All rights reserved. * * @APPLE_OSREFERENCE_LICENSE_HEADER_START@ - * + * * 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 @@ -11,10 +11,10 @@ * 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. - * + * * 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, @@ -22,7 +22,7 @@ * 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_OSREFERENCE_LICENSE_HEADER_END@ */ @@ -31,142 +31,616 @@ * Polled-mode 16x50 UART driver. */ +#include #include #include -void serial_putc(char); -int serial_getc(void); -int serial_init(void); +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 */ + +static unsigned int legacy_uart_enabled = 0; /* 1 Legacy IO based UART is supported on platform */ + +static boolean_t lpss_uart_supported = 0; /* 1 if LPSS UART is supported on platform */ +static unsigned int lpss_uart_enabled = 0; /* 1 if it is LPSS UART is in D0 state */ +static void lpss_uart_re_init(void); + +static boolean_t pcie_uart_enabled = 0; /* 1 if PCIe UART is supported on platform */ + +#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) -/* standard port addresses */ enum { - COM1_PORT_ADDR = 0x3f8, - COM2_PORT_ADDR = 0x2f8 + 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) */ - UART_DLL = 0, /* DLAB = 1, divisor latch (LSB) */ - UART_IER = 1, /* interrupt enable register */ - UART_DLM = 1, /* DLAB = 1, divisor latch (MSB) */ - UART_IIR = 2, /* interrupt ident register (R) */ - UART_FCR = 2, /* fifo control register (W) */ - 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_RBR = 0, /* receive buffer Register (R) */ + UART_THR = 0, /* transmit holding register (W) */ + UART_DLL = 0, /* DLAB = 1, divisor latch (LSB) */ + UART_IER = 1, /* interrupt enable register */ + UART_DLM = 1, /* DLAB = 1, divisor latch (MSB) */ + UART_IIR = 2, /* interrupt ident register (R) */ + UART_FCR = 2, /* fifo control register (W) */ + 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_SCR = 7 /* scratch register */ }; enum { - UART_LCR_8BITS = 0x03, - UART_LCR_DLAB = 0x80 + UART_LCR_8BITS = 0x03, + UART_LCR_DLAB = 0x80 }; enum { - UART_MCR_DTR = 0x01, - UART_MCR_RTS = 0x02, - UART_MCR_OUT1 = 0x04, - UART_MCR_OUT2 = 0x08, - UART_MCR_LOOP = 0x10 + UART_MCR_DTR = 0x01, + UART_MCR_RTS = 0x02, + UART_MCR_OUT1 = 0x04, + UART_MCR_OUT2 = 0x08, + UART_MCR_LOOP = 0x10 }; enum { - UART_LSR_THRE = 0x20 + 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 +enum { + UART_CLK_125M_1 = 0x60002, + UART_CLK_125M_2 = 0x80060003, +}; -#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 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; +} -static int uart_initted = 0; /* 1 if init'ed */ +static void +legacy_uart_set_baud_rate( __unused int unit, uint32_t baud_rate ) +{ + const unsigned char lcr = IO_READ( LCR ); + unsigned long div; + + 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 +legacy_uart_tr0( void ) +{ + return IO_READ(LSR) & UART_LSR_THRE; +} + +static void +legacy_uart_td0( int c ) +{ + IO_WRITE( THR, c ); +} + +static void +legacy_uart_init( void ) +{ + /* Disable hardware interrupts */ + + IO_WRITE( MCR, 0 ); + IO_WRITE( IER, 0 ); + + /* 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 /* Legacy MMIO Config space */ +#define MMIO_UART2_BASE 0xFE036000 /* MMIO Config space */ +#define PCI_UART2 0xFE037000 /* PCI Config Space */ + +#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 */ + MMIO_UART_CLK = 0x200, /* clocks register */ + MMIO_UART_RST = 0x204 /* Reset 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 +mmio_uart_set_baud_rate( __unused int unit, __unused uint32_t baud_rate ) +{ + const unsigned char lcr = MMIO_READ( LCR ); + unsigned long div; + + if (baud_rate == 0) { + baud_rate = 9600; + } + div = LEGACY_UART_CLOCK / 16 / baud_rate; + + 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); +} static int -uart_probe( void ) +mmio_uart_tr0( void ) { - /* Verify that the Divisor Register is accessible */ + return MMIO_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 +mmio_uart_td0( int c ) +{ + MMIO_WRITE( THR, c ); } static void -uart_set_baud_rate( unsigned long baud_rate ) +mmio_uart_init( void ) { - #define UART_CLOCK 1843200 /* 1.8432 MHz clock */ + /* Disable hardware interrupts */ + + MMIO_WRITE( MCR, 0 ); + MMIO_WRITE( IER, 0 ); + + /* Disable FIFO's for 16550 devices */ + + MMIO_WRITE( FCR, 0 ); + + /* Set for 8-bit, no parity, DLAB bit cleared */ - const unsigned char lcr = READ( LCR ); - unsigned long div; + MMIO_WRITE( LCR, UART_LCR_8BITS ); - 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); + /* Leave baud rate as set by firmware unless serialbaud boot-arg overrides */ + + if (uart_baud_rate != DEFAULT_UART_BAUD_RATE) { + gPESF->uart_set_baud_rate( 0, uart_baud_rate ); + } + + /* Assert DTR# and RTS# lines (OUT2?) */ + + MMIO_WRITE( MCR, UART_MCR_DTR | UART_MCR_RTS ); + + /* Clear any garbage in the input buffer */ + + MMIO_READ( RBR ); + + uart_initted = 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; +} + +void +lpss_uart_enable( boolean_t on_off ) +{ + unsigned int pmcs_reg; + + if (!lpss_uart_supported) { + return; + } + + pmcs_reg = ml_phys_read_byte(PCI_UART2 + 0x84); + if (on_off == FALSE) { + pmcs_reg |= 0x03; + lpss_uart_enabled = 0; + } else { + pmcs_reg &= ~(0x03); + } + + ml_phys_write_byte(PCI_UART2 + 0x84, pmcs_reg); + pmcs_reg = ml_phys_read_byte(PCI_UART2 + 0x84); + + if (on_off == TRUE) { + lpss_uart_re_init(); + lpss_uart_enabled = 1; + } } static void -uart_putc( char c ) +lpss_uart_re_init( void ) +{ + uint32_t register_read; + + MMIO_WRITE(RST, 0x7); /* LPSS UART2 controller out ot reset */ + register_read = MMIO_READ(RST); + + MMIO_WRITE(LCR, UART_LCR_DLAB); /* Set DLAB bit to enable reading/writing of DLL, DLH */ + register_read = MMIO_READ(LCR); + + MMIO_WRITE(DLL, 1); /* Divisor Latch Low Register */ + register_read = MMIO_READ(DLL); + + MMIO_WRITE(DLM, 0); /* Divisor Latch High Register */ + register_read = MMIO_READ(DLM); + + MMIO_WRITE(FCR, 1); /* Enable FIFO */ + register_read = MMIO_READ(FCR); + + MMIO_WRITE(LCR, UART_LCR_8BITS); /* Set 8 bits, clear DLAB */ + register_read = MMIO_READ(LCR); + + MMIO_WRITE(MCR, UART_MCR_RTS); /* Request to send */ + register_read = MMIO_READ(MCR); + + MMIO_WRITE(CLK, UART_CLK_125M_1); /* 1.25M Clock speed */ + register_read = MMIO_READ(CLK); + + MMIO_WRITE(CLK, UART_CLK_125M_2); /* 1.25M Clock speed */ + register_read = MMIO_READ(CLK); +} + +static int +mmio_uart_rd0( void ) { - if (!uart_initted) return; + 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 +}; + +// ============================================================================= +// PCIE_MMIO UART +// ============================================================================= + +#define PCIE_MMIO_UART_BASE 0xFE410000 - /* Wait for THR empty */ - while ( !(READ(LSR) & UART_LSR_THRE) ) DELAY(1); +#define PCIE_MMIO_WRITE(r, v) ml_phys_write_byte(pcie_mmio_uart_base + PCIE_MMIO_UART_##r, v) +#define PCIE_MMIO_READ(r) ml_phys_read_byte(pcie_mmio_uart_base + PCIE_MMIO_UART_##r) - WRITE( THR, c ); +enum { + PCIE_MMIO_UART_RBR = 0x0, /* receive buffer Register (R) */ + PCIE_MMIO_UART_THR = 0x0, /* transmit holding register (W) */ + PCIE_MMIO_UART_IER = 0x1, /* interrupt enable register */ + PCIE_MMIO_UART_FCR = 0x2, /* fifo control register (W) */ + PCIE_MMIO_UART_LCR = 0x4, /* line control register */ + PCIE_MMIO_UART_MCR = 0x4, /* modem control register */ + PCIE_MMIO_UART_LSR = 0x5, /* line status register */ + PCIE_MMIO_UART_DLL = 0x8, /* DLAB = 1, divisor latch (LSB) */ + PCIE_MMIO_UART_DLM = 0x9, /* DLAB = 1, divisor latch (MSB) */ + PCIE_MMIO_UART_SCR = 0x30, /* scratch register */ +}; + +static vm_offset_t pcie_mmio_uart_base = 0; + +static int +pcie_mmio_uart_present( void ) +{ + PCIE_MMIO_WRITE( SCR, 0x5a ); + if (PCIE_MMIO_READ(SCR) != 0x5a) { + return 0; + } + PCIE_MMIO_WRITE( SCR, 0xa5 ); + if (PCIE_MMIO_READ(SCR) != 0xa5) { + return 0; + } + + return 1; } -int serial_init( void ) +static int +pcie_mmio_uart_probe( void ) { - if ( /*uart_initted ||*/ uart_probe() == 0 ) return 0; + unsigned new_pcie_mmio_uart_base = 0; + + // if specified, pcie_mmio_uart overrides all probing + if (PE_parse_boot_argn("pcie_mmio_uart", &new_pcie_mmio_uart_base, sizeof(new_pcie_mmio_uart_base))) { + // pcie_mmio_uart=0 will disable pcie_mmio_uart support + if (new_pcie_mmio_uart_base == 0) { + return 0; + } + pcie_mmio_uart_base = new_pcie_mmio_uart_base; + return 1; + } + + pcie_mmio_uart_base = PCIE_MMIO_UART_BASE; + if (pcie_mmio_uart_present()) { + return 1; + } + + // no pcie_mmio uart found + return 0; +} - /* Disable hardware interrupts */ +static void +pcie_mmio_uart_set_baud_rate( __unused int unit, __unused uint32_t baud_rate ) +{ + const unsigned char lcr = PCIE_MMIO_READ( LCR ); + unsigned long div; + + if (baud_rate == 0) { + baud_rate = 9600; + } + div = LEGACY_UART_CLOCK / 16 / baud_rate; + + PCIE_MMIO_WRITE( LCR, lcr | UART_LCR_DLAB ); + PCIE_MMIO_WRITE( DLM, (unsigned char)(div >> 8)); + PCIE_MMIO_WRITE( DLL, (unsigned char) div ); + PCIE_MMIO_WRITE( LCR, lcr & ~UART_LCR_DLAB); +} - WRITE( MCR, 0 ); - WRITE( IER, 0 ); +static int +pcie_mmio_uart_tr0( void ) +{ + return PCIE_MMIO_READ(LSR) & UART_LSR_THRE; +} - /* Disable FIFO's for 16550 devices */ +static void +pcie_mmio_uart_td0( int c ) +{ + PCIE_MMIO_WRITE( THR, c ); +} - WRITE( FCR, 0 ); +static void +pcie_mmio_uart_init( void ) +{ + uart_initted = 1; +} - /* Set for 8-bit, no parity, DLAB bit cleared */ +static int +pcie_mmio_uart_rr0( void ) +{ + unsigned char lsr; - WRITE( LCR, UART_LCR_8BITS ); + lsr = PCIE_MMIO_READ( LSR ); - /* Set baud rate */ + if (lsr & (UART_LSR_FE | UART_LSR_PE | UART_LSR_OE)) { + PCIE_MMIO_READ( RBR ); /* discard */ + return 0; + } - uart_set_baud_rate( UART_BAUD_RATE ); + return lsr & UART_LSR_DR; +} - /* Assert DTR# and RTS# lines (OUT2?) */ +static int +pcie_mmio_uart_rd0( void ) +{ + return PCIE_MMIO_READ( RBR ); +} - WRITE( MCR, UART_MCR_DTR | UART_MCR_RTS ); +static struct pe_serial_functions pcie_mmio_uart_serial_functions = { + .uart_init = pcie_mmio_uart_init, + .uart_set_baud_rate = pcie_mmio_uart_set_baud_rate, + .tr0 = pcie_mmio_uart_tr0, + .td0 = pcie_mmio_uart_td0, + .rr0 = pcie_mmio_uart_rr0, + .rd0 = pcie_mmio_uart_rd0 +}; - /* Clear any garbage in the input buffer */ +// ============================================================================= +// Generic serial support below +// ============================================================================= - READ( RBR ); +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(); + lpss_uart_supported = 1; + lpss_uart_enabled = 1; + return 1; + } else if (legacy_uart_probe()) { + gPESF = &legacy_uart_serial_functions; + gPESF->uart_init(); + legacy_uart_enabled = 1; + return 1; + } else if (pcie_mmio_uart_probe()) { + gPESF = &pcie_mmio_uart_serial_functions; + gPESF->uart_init(); + pcie_uart_enabled = 1; + return 1; + } else { + return 0; + } +} - uart_initted = 1; +static void +uart_putc(char c) +{ + if (uart_initted && (legacy_uart_enabled || lpss_uart_enabled || pcie_uart_enabled)) { + while (!gPESF->tr0()) { + ; /* Wait until THR is empty. */ + } + gPESF->td0(c); + } +} - return 1; +static int +uart_getc(void) +{ + if (uart_initted && (legacy_uart_enabled || lpss_uart_enabled || pcie_uart_enabled)) { + 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'); + uart_putc(c); } -int serial_getc( void ) +int +serial_getc( void ) { - return 0; /* not supported */ + return uart_getc(); }