@ -1,26 +1,42 @@
// See LICENSE for license details.
# include <string.h>
# include <stdarg.h>
# include <assert.h>
# include "uart16550.h"
# include "fdt.h"
volatile uint8_t * uart16550 ;
// some devices require a shifted register index
// (e.g. 32 bit registers instead of 8 bit registers)
static uint32_t uart16550_reg_shift ;
static uint32_t uart16550_clock = 1843200 ; // a "common" base clock
# define UART_REG_QUEUE 0
# define UART_REG_LINESTAT 5
# define UART_REG_QUEUE 0 // rx/tx fifo data
# define UART_REG_DLL 0 // divisor latch (LSB)
# define UART_REG_IER 1 // interrupt enable register
# define UART_REG_DLM 1 // divisor latch (MSB)
# define UART_REG_FCR 2 // fifo control register
# define UART_REG_LCR 3 // line control register
# define UART_REG_MCR 4 // modem control register
# define UART_REG_LSR 5 // line status register
# define UART_REG_MSR 6 // modem status register
# define UART_REG_SCR 7 // scratch register
# define UART_REG_STATUS_RX 0x01
# define UART_REG_STATUS_TX 0x20
# define UART_DEFAULT_BAUD 38400
void uart16550_putchar ( uint8_t ch )
{
while ( ( uart16550 [ UART_REG_LINESTAT ] & UART_REG_STATUS_TX ) = = 0 ) ;
uart16550 [ UART_REG_QUEUE ] = ch ;
while ( ( uart16550 [ UART_REG_LSR < < uart16550_reg_shift ] & UART_REG_STATUS_TX ) = = 0 ) ;
uart16550 [ UART_REG_QUEUE < < uart16550_reg_shift ] = ch ;
}
int uart16550_getchar ( )
{
if ( uart16550 [ UART_REG_LINESTAT ] & UART_REG_STATUS_RX )
return uart16550 [ UART_REG_QUEUE ] ;
if ( uart16550 [ UART_REG_LSR < < uart16550_reg_shift ] & UART_REG_STATUS_RX )
return uart16550 [ UART_REG_QUEUE < < uart16550_reg_shift ] ;
return - 1 ;
}
@ -28,6 +44,9 @@ struct uart16550_scan
{
int compat ;
uint64_t reg ;
uint32_t reg_offset ;
uint32_t reg_shift ;
uint32_t clock_freq ;
} ;
static void uart16550_open ( const struct fdt_scan_node * node , void * extra )
@ -39,26 +58,41 @@ static void uart16550_open(const struct fdt_scan_node *node, void *extra)
static void uart16550_prop ( const struct fdt_scan_prop * prop , void * extra )
{
struct uart16550_scan * scan = ( struct uart16550_scan * ) extra ;
if ( ! strcmp ( prop - > name , " compatible " ) & & ! strcmp ( ( const char * ) prop - > value , " ns16550a " ) ) {
if ( ! strcmp ( prop - > name , " compatible " ) & & fdt_string_list_index ( prop , " ns16550a " ) ! = - 1 ) {
scan - > compat = 1 ;
} else if ( ! strcmp ( prop - > name , " reg " ) ) {
fdt_get_address ( prop - > node - > parent , prop - > value , & scan - > reg ) ;
} else if ( ! strcmp ( prop - > name , " reg-shift " ) ) {
scan - > reg_shift = fdt_get_value ( prop , 0 ) ;
} else if ( ! strcmp ( prop - > name , " reg-offset " ) ) {
scan - > reg_offset = fdt_get_value ( prop , 0 ) ;
} else if ( ! strcmp ( prop - > name , " clock-frequency " ) ) {
scan - > clock_freq = fdt_get_value ( prop , 0 ) ;
}
}
static void uart16550_done ( const struct fdt_scan_node * node , void * extra )
{
uint32_t clock_freq ;
struct uart16550_scan * scan = ( struct uart16550_scan * ) extra ;
if ( ! scan - > compat | | ! scan - > reg | | uart16550 ) return ;
uart16550 = ( void * ) ( uintptr_t ) scan - > reg ;
if ( scan - > clock_freq ! = 0 )
uart16550_clock = scan - > clock_freq ;
// if device tree doesn't supply a clock, fallback to default clock of 1843200
uint32_t divisor = uart16550_clock / ( 16 * UART_DEFAULT_BAUD ) ;
assert ( divisor < 0x10000u ) ;
uart16550 = ( void * ) ( ( uintptr_t ) scan - > reg + scan - > reg_offset ) ;
uart16550_reg_shift = scan - > reg_shift ;
// http://wiki.osdev.org/Serial_Ports
uart16550 [ 1 ] = 0x00 ; // Disable all interrupts
uart16550 [ 3 ] = 0x80 ; // Enable DLAB (set baud rate divisor)
uart16550 [ 0 ] = 0x03 ; // Set divisor to 3 (lo byte) 38400 baud
uart16550 [ 1 ] = 0x00 ; // (hi byte)
uart16550 [ 3 ] = 0x03 ; // 8 bits, no parity, one stop bit
uart16550 [ 2 ] = 0xC7 ; // Enable FIFO, clear them, with 14-byte threshold
uart16550 [ UART_REG_IER < < uart16550_reg_shift ] = 0x00 ; // Disable all interrupts
uart16550 [ UART_REG_LCR < < uart16550_reg_shift ] = 0x80 ; // Enable DLAB (set baud rate divisor)
uart16550 [ UART_REG_DLL < < uart16550_reg_shift ] = ( uint8_t ) divisor ; // Set divisor (lo byte)
uart16550 [ UART_REG_DLM < < uart16550_reg_shift ] = ( uint8_t ) ( divisor > > 8 ) ; // (hi byte)
uart16550 [ UART_REG_LCR < < uart16550_reg_shift ] = 0x03 ; // 8 bits, no parity, one stop bit
uart16550 [ UART_REG_FCR < < uart16550_reg_shift ] = 0xC7 ; // Enable FIFO, clear them, with 14-byte threshold
}
void query_uart16550 ( uintptr_t fdt )