X-Git-Url: https://git.saurik.com/apple/xnu.git/blobdiff_plain/593a1d5fd87cdf5b46dd5fcb84467b432cea0f91..b226f5e54a60dc81db17b1260381d7dbfea3cdf1:/pexpert/i386/pe_serial.c diff --git a/pexpert/i386/pe_serial.c b/pexpert/i386/pe_serial.c index ddb48b162..e35457dde 100644 --- a/pexpert/i386/pe_serial.c +++ b/pexpert/i386/pe_serial.c @@ -31,16 +31,50 @@ * 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 */ + +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) + 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) */ @@ -77,134 +111,529 @@ enum { UART_LSR_THRE = 0x20 }; -static unsigned uart_baud_rate = 115200; -#define UART_PORT_ADDR COM1_PORT_ADDR - -#define UART_CLOCK 1843200 /* 1.8432 MHz clock */ - -#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 uart_initted = 0; /* 1 if init'ed */ +enum { + UART_CLK_125M_1 = 0x60002, + UART_CLK_125M_2 = 0x80060003, +}; static int -uart_probe( void ) +legacy_uart_probe( void ) { /* Verify that the Scratch Register is accessible */ - WRITE( SCR, 0x5a ); - if (READ(SCR) != 0x5a) return 0; - WRITE( SCR, 0xa5 ); - if (READ(SCR) != 0xa5) return 0; + 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 void -uart_set_baud_rate( unsigned long baud_rate ) +legacy_uart_set_baud_rate( __unused int unit, uint32_t baud_rate ) { - const unsigned char lcr = READ( LCR ); + const unsigned char lcr = IO_READ( LCR ); unsigned long div; 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); + 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 -uart_putc( char c ) +legacy_uart_init( void ) { - if (!uart_initted) return; + /* 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?) */ - /* Wait for THR empty */ - while ( !(READ(LSR) & UART_LSR_THRE) ) DELAY(1); + IO_WRITE( MCR, UART_MCR_DTR | UART_MCR_RTS ); - WRITE( THR, c ); + /* Clear any garbage in the input buffer */ + + IO_READ( RBR ); + + uart_initted = 1; } static int -uart_getc( void ) +legacy_uart_rr0( void ) { - /* - * This function returns: - * -1 : no data - * -2 : receiver error - * >0 : character received - */ - unsigned char lsr; - if (!uart_initted) return -1; - - lsr = READ( LSR ); + lsr = IO_READ( LSR ); if ( lsr & (UART_LSR_FE | UART_LSR_PE | UART_LSR_OE) ) { - READ( RBR ); /* discard */ - return -2; + IO_READ( RBR ); /* discard */ + return 0; } - if ( lsr & UART_LSR_DR ) + 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))) { - return READ( RBR ); + // 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; } - 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; } -int serial_init( void ) +static void +mmio_uart_set_baud_rate( __unused int unit, __unused uint32_t baud_rate ) { - unsigned serial_baud_rate = 0; - - if ( /*uart_initted ||*/ uart_probe() == 0 ) return 0; + 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 +mmio_uart_tr0( void ) +{ + return (MMIO_READ(LSR) & UART_LSR_THRE); +} + +static void +mmio_uart_td0( int c ) +{ + 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 - use the supplied boot-arg if available */ + /* Leave baud rate as set by firmware unless serialbaud boot-arg overrides */ - if (PE_parse_boot_argn("serialbaud", &serial_baud_rate, sizeof (serial_baud_rate))) + if (uart_baud_rate != DEFAULT_UART_BAUD_RATE) { - /* Valid divisor? */ - if (!((UART_CLOCK / 16) % serial_baud_rate)) { - uart_baud_rate = serial_baud_rate; - } + gPESF->uart_set_baud_rate ( 0, uart_baud_rate ); } - uart_set_baud_rate( 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; +} + +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 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 ) +{ + 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 + +#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) + +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; } -void serial_putc( char c ) +static int +pcie_mmio_uart_probe( void ) +{ + 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; +} + +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); +} + +static int +pcie_mmio_uart_tr0( void ) +{ + return (PCIE_MMIO_READ(LSR) & UART_LSR_THRE); +} + +static void +pcie_mmio_uart_td0( int c ) +{ + PCIE_MMIO_WRITE( THR, c ); +} + +static void +pcie_mmio_uart_init( void ) +{ + uart_initted = 1; +} + +static int +pcie_mmio_uart_rr0( void ) +{ + unsigned char lsr; + + lsr = PCIE_MMIO_READ( LSR ); + + if ( lsr & (UART_LSR_FE | UART_LSR_PE | UART_LSR_OE) ) + { + PCIE_MMIO_READ( RBR ); /* discard */ + return 0; + } + + return (lsr & UART_LSR_DR); +} + +static int +pcie_mmio_uart_rd0( void ) +{ + return PCIE_MMIO_READ( RBR ); +} + +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 +}; + +// ============================================================================= +// 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(); + 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; + } + +} + +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); + } +} + +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 ) { uart_putc(c); - if (c == '\n') uart_putc('\r'); } -int serial_getc( void ) +int +serial_getc( void ) { return uart_getc(); }