iPXE
uart.c
Go to the documentation of this file.
00001 /*
00002  * Copyright (C) 2014 Michael Brown <mbrown@fensystems.co.uk>.
00003  *
00004  * This program is free software; you can redistribute it and/or
00005  * modify it under the terms of the GNU General Public License as
00006  * published by the Free Software Foundation; either version 2 of the
00007  * License, or any later version.
00008  *
00009  * This program is distributed in the hope that it will be useful, but
00010  * WITHOUT ANY WARRANTY; without even the implied warranty of
00011  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00012  * General Public License for more details.
00013  *
00014  * You should have received a copy of the GNU General Public License
00015  * along with this program; if not, write to the Free Software
00016  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
00017  * 02110-1301, USA.
00018  *
00019  * You can also choose to distribute this program under the terms of
00020  * the Unmodified Binary Distribution Licence (as given in the file
00021  * COPYING.UBDL), provided that you have satisfied its requirements.
00022  */
00023 
00024 FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
00025 
00026 /** @file
00027  *
00028  * 16550-compatible UART
00029  *
00030  */
00031 
00032 #include <unistd.h>
00033 #include <errno.h>
00034 #include <ipxe/uart.h>
00035 
00036 /** Timeout for transmit holding register to become empty */
00037 #define UART_THRE_TIMEOUT_MS 100
00038 
00039 /** Timeout for transmitter to become empty */
00040 #define UART_TEMT_TIMEOUT_MS 1000
00041 
00042 /**
00043  * Transmit data
00044  *
00045  * @v uart              UART
00046  * @v data              Data
00047  */
00048 void uart_transmit ( struct uart *uart, uint8_t data ) {
00049         unsigned int i;
00050         uint8_t lsr;
00051 
00052         /* Wait for transmitter holding register to become empty */
00053         for ( i = 0 ; i < UART_THRE_TIMEOUT_MS ; i++ ) {
00054                 lsr = uart_read ( uart, UART_LSR );
00055                 if ( lsr & UART_LSR_THRE )
00056                         break;
00057                 mdelay ( 1 );
00058         }
00059 
00060         /* Transmit data (even if we timed out) */
00061         uart_write ( uart, UART_THR, data );
00062 }
00063 
00064 /**
00065  * Flush data
00066  *
00067  * @v uart              UART
00068  */
00069 void uart_flush ( struct uart *uart ) {
00070         unsigned int i;
00071         uint8_t lsr;
00072 
00073         /* Wait for transmitter and receiver to become empty */
00074         for ( i = 0 ; i < UART_TEMT_TIMEOUT_MS ; i++ ) {
00075                 uart_read ( uart, UART_RBR );
00076                 lsr = uart_read ( uart, UART_LSR );
00077                 if ( ( lsr & UART_LSR_TEMT ) && ! ( lsr & UART_LSR_DR ) )
00078                         break;
00079         }
00080 }
00081 
00082 /**
00083  * Check for existence of UART
00084  *
00085  * @v uart              UART
00086  * @ret rc              Return status code
00087  */
00088 int uart_exists ( struct uart *uart ) {
00089 
00090         /* Fail if no UART port is defined */
00091         if ( ! uart->base )
00092                 return -ENODEV;
00093 
00094         /* Fail if UART scratch register seems not to be present */
00095         uart_write ( uart, UART_SCR, 0x18 );
00096         if ( uart_read ( uart, UART_SCR ) != 0x18 )
00097                 return -ENODEV;
00098         uart_write ( uart, UART_SCR, 0xae );
00099         if ( uart_read ( uart, UART_SCR ) != 0xae )
00100                 return -ENODEV;
00101 
00102         return 0;
00103 }
00104 
00105 /**
00106  * Initialise UART
00107  *
00108  * @v uart              UART
00109  * @v baud              Baud rate, or zero to leave unchanged
00110  * @v lcr               Line control register value, or zero to leave unchanged
00111  * @ret rc              Return status code
00112  */
00113 int uart_init ( struct uart *uart, unsigned int baud, uint8_t lcr ) {
00114         uint8_t dlm;
00115         uint8_t dll;
00116         int rc;
00117 
00118         /* Check for existence of UART */
00119         if ( ( rc = uart_exists ( uart ) ) != 0 )
00120                 return rc;
00121 
00122         /* Configure divisor and line control register, if applicable */
00123         if ( ! lcr )
00124                 lcr = uart_read ( uart, UART_LCR );
00125         uart->lcr = lcr;
00126         uart_write ( uart, UART_LCR, ( lcr | UART_LCR_DLAB ) );
00127         if ( baud ) {
00128                 uart->divisor = ( UART_MAX_BAUD / baud );
00129                 dlm = ( ( uart->divisor >> 8 ) & 0xff );
00130                 dll = ( ( uart->divisor >> 0 ) & 0xff );
00131                 uart_write ( uart, UART_DLM, dlm );
00132                 uart_write ( uart, UART_DLL, dll );
00133         } else {
00134                 dlm = uart_read ( uart, UART_DLM );
00135                 dll = uart_read ( uart, UART_DLL );
00136                 uart->divisor = ( ( dlm << 8 ) | dll );
00137         }
00138         uart_write ( uart, UART_LCR, ( lcr & ~UART_LCR_DLAB ) );
00139 
00140         /* Disable interrupts */
00141         uart_write ( uart, UART_IER, 0 );
00142 
00143         /* Enable FIFOs */
00144         uart_write ( uart, UART_FCR, UART_FCR_FE );
00145 
00146         /* Assert DTR and RTS */
00147         uart_write ( uart, UART_MCR, ( UART_MCR_DTR | UART_MCR_RTS ) );
00148 
00149         /* Flush any stale data */
00150         uart_flush ( uart );
00151 
00152         return 0;
00153 }