mirror of
https://github.com/ipxe/ipxe
synced 2026-05-14 20:00:42 +03:00
[uart] Allow for the existence of non-16550 UARTs
Remove the assumption that all platforms use a fixed number of 16550 UARTs identifiable by a simple numeric index. Create an abstraction allowing for dynamic instantiation and registration of any number of arbitrary UART models. The common case of the serial console on x86 uses a single fixed UART specified at compile time. Avoid unnecessarily dragging in the dynamic instantiation code in this use case by allowing COMCONSOLE to refer to a single static UART object representing the relevant port. When selecting a UART by command-line argument (as used in the "gdbstub serial <port>" command), allow the UART to be specified as either a numeric index (to retain backwards compatiblity) or a case-insensitive port name such as "COM2". Signed-off-by: Michael Brown <mcb30@ipxe.org>
This commit is contained in:
@@ -1,9 +1,9 @@
|
||||
#ifndef _BITS_UART_H
|
||||
#define _BITS_UART_H
|
||||
#ifndef _BITS_NS16550_H
|
||||
#define _BITS_NS16550_H
|
||||
|
||||
/** @file
|
||||
*
|
||||
* Dummy architecture-specific UART
|
||||
* Dummy architecture-specific 16550-compatible UART
|
||||
*
|
||||
* This file is included only if the architecture does not provide its
|
||||
* own version of this file.
|
||||
@@ -12,4 +12,4 @@
|
||||
|
||||
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
|
||||
|
||||
#endif /* _BITS_UART_H */
|
||||
#endif /* _BITS_NS16550_H */
|
||||
@@ -109,6 +109,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
|
||||
#define ERRFILE_spi_bit ( ERRFILE_DRIVER | 0x00130000 )
|
||||
#define ERRFILE_nvsvpd ( ERRFILE_DRIVER | 0x00140000 )
|
||||
#define ERRFILE_uart ( ERRFILE_DRIVER | 0x00150000 )
|
||||
#define ERRFILE_ns16550 ( ERRFILE_DRIVER | 0x00160000 )
|
||||
|
||||
#define ERRFILE_3c509 ( ERRFILE_DRIVER | 0x00200000 )
|
||||
#define ERRFILE_bnx2 ( ERRFILE_DRIVER | 0x00210000 )
|
||||
|
||||
@@ -11,7 +11,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
|
||||
|
||||
struct gdb_transport;
|
||||
|
||||
extern struct gdb_transport * gdbserial_configure ( unsigned int port,
|
||||
extern struct gdb_transport * gdbserial_configure ( const char *port,
|
||||
unsigned int baud );
|
||||
|
||||
#endif /* _IPXE_GDBSERIAL_H */
|
||||
|
||||
@@ -0,0 +1,112 @@
|
||||
#ifndef _IPXE_NS16550_H
|
||||
#define _IPXE_NS16550_H
|
||||
|
||||
/** @file
|
||||
*
|
||||
* 16550-compatible UART
|
||||
*
|
||||
*/
|
||||
|
||||
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
|
||||
|
||||
#include <ipxe/uart.h>
|
||||
|
||||
/** Transmitter holding register */
|
||||
#define NS16550_THR 0x00
|
||||
|
||||
/** Receiver buffer register */
|
||||
#define NS16550_RBR 0x00
|
||||
|
||||
/** Interrupt enable register */
|
||||
#define NS16550_IER 0x01
|
||||
|
||||
/** FIFO control register */
|
||||
#define NS16550_FCR 0x02
|
||||
#define NS16550_FCR_FE 0x01 /**< FIFO enable */
|
||||
|
||||
/** Line control register */
|
||||
#define NS16550_LCR 0x03
|
||||
#define NS16550_LCR_WLS0 0x01 /**< Word length select bit 0 */
|
||||
#define NS16550_LCR_WLS1 0x02 /**< Word length select bit 1 */
|
||||
#define NS16550_LCR_STB 0x04 /**< Number of stop bits */
|
||||
#define NS16550_LCR_PEN 0x08 /**< Parity enable */
|
||||
#define NS16550_LCR_EPS 0x10 /**< Even parity select */
|
||||
#define NS16550_LCR_DLAB 0x80 /**< Divisor latch access bit */
|
||||
|
||||
#define NS16550_LCR_WORD_LEN(x) ( ( (x) - 5 ) << 0 ) /**< Word length */
|
||||
#define NS16550_LCR_STOP_BITS(x) ( ( (x) - 1 ) << 2 ) /**< Stop bits */
|
||||
#define NS16550_LCR_PARITY(x) ( ( (x) - 0 ) << 3 ) /**< Parity */
|
||||
|
||||
/**
|
||||
* Calculate line control register value
|
||||
*
|
||||
* @v word_len Word length (5-8)
|
||||
* @v parity Parity (0=none, 1=odd, 3=even)
|
||||
* @v stop_bits Stop bits (1-2)
|
||||
* @ret lcr Line control register value
|
||||
*/
|
||||
#define NS16550_LCR_WPS( word_len, parity, stop_bits ) \
|
||||
( NS16550_LCR_WORD_LEN ( (word_len) ) | \
|
||||
NS16550_LCR_PARITY ( (parity) ) | \
|
||||
NS16550_LCR_STOP_BITS ( (stop_bits) ) )
|
||||
|
||||
/** Default LCR value: 8 data bits, no parity, one stop bit */
|
||||
#define NS16550_LCR_8N1 NS16550_LCR_WPS ( 8, 0, 1 )
|
||||
|
||||
/** Modem control register */
|
||||
#define NS16550_MCR 0x04
|
||||
#define NS16550_MCR_DTR 0x01 /**< Data terminal ready */
|
||||
#define NS16550_MCR_RTS 0x02 /**< Request to send */
|
||||
|
||||
/** Line status register */
|
||||
#define NS16550_LSR 0x05
|
||||
#define NS16550_LSR_DR 0x01 /**< Data ready */
|
||||
#define NS16550_LSR_THRE 0x20 /**< Transmitter holding reg. empty */
|
||||
#define NS16550_LSR_TEMT 0x40 /**< Transmitter empty */
|
||||
|
||||
/** Scratch register */
|
||||
#define NS16550_SCR 0x07
|
||||
|
||||
/** Divisor latch (least significant byte) */
|
||||
#define NS16550_DLL 0x00
|
||||
|
||||
/** Divisor latch (most significant byte) */
|
||||
#define NS16550_DLM 0x01
|
||||
|
||||
/** Maximum baud rate */
|
||||
#define NS16550_MAX_BAUD 115200
|
||||
|
||||
/** A 16550-compatible UART */
|
||||
struct ns16550_uart {
|
||||
/** Generic UART */
|
||||
struct uart uart;
|
||||
/** Register base address */
|
||||
void *base;
|
||||
/** Baud rate divisor */
|
||||
uint16_t divisor;
|
||||
};
|
||||
|
||||
#include <bits/ns16550.h>
|
||||
|
||||
/** Dummy COM1 UART for non-x86 platforms
|
||||
*
|
||||
* The architecture-independent config/serial.h header has long
|
||||
* included the line
|
||||
*
|
||||
* #define COMCONSOLE COM1
|
||||
*
|
||||
* which is meaningless on non-x86 platforms where there is no COM1
|
||||
* port. Allow COM1 to be treated as equivalent to "no UART" on
|
||||
* non-x86 platforms, to avoid breaking existing build configurations.
|
||||
*/
|
||||
#ifndef COM1
|
||||
#define COM1 NULL
|
||||
#endif
|
||||
|
||||
void ns16550_write ( struct ns16550_uart *ns16550, unsigned int address,
|
||||
uint8_t data );
|
||||
uint8_t ns16550_read ( struct ns16550_uart *ns16550, unsigned int address );
|
||||
|
||||
extern struct uart_operations ns16550_operations;
|
||||
|
||||
#endif /* _IPXE_NS16550_H */
|
||||
@@ -11,6 +11,6 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
|
||||
|
||||
#include <ipxe/uart.h>
|
||||
|
||||
extern struct uart serial_console;
|
||||
extern struct uart *serial_console;
|
||||
|
||||
#endif /* _IPXE_SERIAL_H */
|
||||
|
||||
+142
-96
@@ -3,128 +3,174 @@
|
||||
|
||||
/** @file
|
||||
*
|
||||
* 16550-compatible UART
|
||||
* Generic UART
|
||||
*
|
||||
*/
|
||||
|
||||
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
|
||||
|
||||
#include <stdint.h>
|
||||
#include <ipxe/refcnt.h>
|
||||
#include <ipxe/list.h>
|
||||
|
||||
/** Transmitter holding register */
|
||||
#define UART_THR 0x00
|
||||
|
||||
/** Receiver buffer register */
|
||||
#define UART_RBR 0x00
|
||||
|
||||
/** Interrupt enable register */
|
||||
#define UART_IER 0x01
|
||||
|
||||
/** FIFO control register */
|
||||
#define UART_FCR 0x02
|
||||
#define UART_FCR_FE 0x01 /**< FIFO enable */
|
||||
|
||||
/** Line control register */
|
||||
#define UART_LCR 0x03
|
||||
#define UART_LCR_WLS0 0x01 /**< Word length select bit 0 */
|
||||
#define UART_LCR_WLS1 0x02 /**< Word length select bit 1 */
|
||||
#define UART_LCR_STB 0x04 /**< Number of stop bits */
|
||||
#define UART_LCR_PEN 0x08 /**< Parity enable */
|
||||
#define UART_LCR_EPS 0x10 /**< Even parity select */
|
||||
#define UART_LCR_DLAB 0x80 /**< Divisor latch access bit */
|
||||
|
||||
#define UART_LCR_WORD_LEN(x) ( ( (x) - 5 ) << 0 ) /**< Word length */
|
||||
#define UART_LCR_STOP_BITS(x) ( ( (x) - 1 ) << 2 ) /**< Stop bits */
|
||||
#define UART_LCR_PARITY(x) ( ( (x) - 0 ) << 3 ) /**< Parity */
|
||||
|
||||
/**
|
||||
* Calculate line control register value
|
||||
*
|
||||
* @v word_len Word length (5-8)
|
||||
* @v parity Parity (0=none, 1=odd, 3=even)
|
||||
* @v stop_bits Stop bits (1-2)
|
||||
* @ret lcr Line control register value
|
||||
*/
|
||||
#define UART_LCR_WPS( word_len, parity, stop_bits ) \
|
||||
( UART_LCR_WORD_LEN ( (word_len) ) | \
|
||||
UART_LCR_PARITY ( (parity) ) | \
|
||||
UART_LCR_STOP_BITS ( (stop_bits) ) )
|
||||
|
||||
/** Default LCR value: 8 data bits, no parity, one stop bit */
|
||||
#define UART_LCR_8N1 UART_LCR_WPS ( 8, 0, 1 )
|
||||
|
||||
/** Modem control register */
|
||||
#define UART_MCR 0x04
|
||||
#define UART_MCR_DTR 0x01 /**< Data terminal ready */
|
||||
#define UART_MCR_RTS 0x02 /**< Request to send */
|
||||
|
||||
/** Line status register */
|
||||
#define UART_LSR 0x05
|
||||
#define UART_LSR_DR 0x01 /**< Data ready */
|
||||
#define UART_LSR_THRE 0x20 /**< Transmitter holding register empty */
|
||||
#define UART_LSR_TEMT 0x40 /**< Transmitter empty */
|
||||
|
||||
/** Scratch register */
|
||||
#define UART_SCR 0x07
|
||||
|
||||
/** Divisor latch (least significant byte) */
|
||||
#define UART_DLL 0x00
|
||||
|
||||
/** Divisor latch (most significant byte) */
|
||||
#define UART_DLM 0x01
|
||||
|
||||
/** Maximum baud rate */
|
||||
#define UART_MAX_BAUD 115200
|
||||
|
||||
/** A 16550-compatible UART */
|
||||
/** A generic UART */
|
||||
struct uart {
|
||||
/** I/O port base address */
|
||||
void *base;
|
||||
/** Baud rate divisor */
|
||||
uint16_t divisor;
|
||||
/** Reference count */
|
||||
struct refcnt refcnt;
|
||||
/** Name */
|
||||
const char *name;
|
||||
/** List of registered UARTs */
|
||||
struct list_head list;
|
||||
|
||||
/** UART operations */
|
||||
struct uart_operations *op;
|
||||
/** Driver-private data */
|
||||
void *priv;
|
||||
};
|
||||
|
||||
/** Symbolic names for port indexes */
|
||||
enum uart_port {
|
||||
COM1 = 1,
|
||||
COM2 = 2,
|
||||
COM3 = 3,
|
||||
COM4 = 4,
|
||||
/** UART operations */
|
||||
struct uart_operations {
|
||||
/**
|
||||
* Transmit byte
|
||||
*
|
||||
* @v uart UART
|
||||
* @v byte Byte to transmit
|
||||
* @ret rc Return status code
|
||||
*/
|
||||
void ( * transmit ) ( struct uart *uart, uint8_t byte );
|
||||
/**
|
||||
* Check if data is ready
|
||||
*
|
||||
* @v uart UART
|
||||
* @ret ready Data is ready
|
||||
*/
|
||||
int ( * data_ready ) ( struct uart *uart );
|
||||
/**
|
||||
* Receive byte
|
||||
*
|
||||
* @v uart UART
|
||||
* @ret byte Received byte
|
||||
*/
|
||||
uint8_t ( * receive ) ( struct uart *uart );
|
||||
/**
|
||||
* Initialise UART
|
||||
*
|
||||
* @v uart UART
|
||||
* @v baud Baud rate, or zero to leave unchanged
|
||||
* @ret rc Return status code
|
||||
*/
|
||||
int ( * init ) ( struct uart *uart, unsigned int baud );
|
||||
/**
|
||||
* Flush transmitted data
|
||||
*
|
||||
* @v uart UART
|
||||
*/
|
||||
void ( * flush ) ( struct uart *uart );
|
||||
};
|
||||
|
||||
#include <bits/uart.h>
|
||||
|
||||
void uart_write ( struct uart *uart, unsigned int addr, uint8_t data );
|
||||
uint8_t uart_read ( struct uart *uart, unsigned int addr );
|
||||
int uart_select ( struct uart *uart, unsigned int port );
|
||||
|
||||
/**
|
||||
* Check if received data is ready
|
||||
* Transmit byte
|
||||
*
|
||||
* @v uart UART
|
||||
* @v byte Byte to transmit
|
||||
* @ret rc Return status code
|
||||
*/
|
||||
static inline __attribute__ (( always_inline )) void
|
||||
uart_transmit ( struct uart *uart, uint8_t byte ) {
|
||||
|
||||
uart->op->transmit ( uart, byte );
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if data is ready
|
||||
*
|
||||
* @v uart UART
|
||||
* @ret ready Data is ready
|
||||
*/
|
||||
static inline int uart_data_ready ( struct uart *uart ) {
|
||||
uint8_t lsr;
|
||||
static inline __attribute__ (( always_inline )) int
|
||||
uart_data_ready ( struct uart *uart ) {
|
||||
|
||||
lsr = uart_read ( uart, UART_LSR );
|
||||
return ( lsr & UART_LSR_DR );
|
||||
return uart->op->data_ready ( uart );
|
||||
}
|
||||
|
||||
/**
|
||||
* Receive data
|
||||
* Receive byte
|
||||
*
|
||||
* @v uart UART
|
||||
* @ret data Data
|
||||
* @ret byte Received byte
|
||||
*/
|
||||
static inline uint8_t uart_receive ( struct uart *uart ) {
|
||||
static inline __attribute__ (( always_inline )) uint8_t
|
||||
uart_receive ( struct uart *uart ) {
|
||||
|
||||
return uart_read ( uart, UART_RBR );
|
||||
return uart->op->receive ( uart );
|
||||
}
|
||||
|
||||
extern void uart_transmit ( struct uart *uart, uint8_t data );
|
||||
extern void uart_flush ( struct uart *uart );
|
||||
extern int uart_exists ( struct uart *uart );
|
||||
extern int uart_init ( struct uart *uart, unsigned int baud );
|
||||
/**
|
||||
* Initialise UART
|
||||
*
|
||||
* @v uart UART
|
||||
* @v baud Baud rate, or zero to leave unchanged
|
||||
* @ret rc Return status code
|
||||
*/
|
||||
static inline __attribute__ (( always_inline )) int
|
||||
uart_init ( struct uart *uart, unsigned int baud ) {
|
||||
|
||||
return uart->op->init ( uart, baud );
|
||||
}
|
||||
|
||||
/**
|
||||
* Flush transmitted data
|
||||
*
|
||||
* @v uart UART
|
||||
*/
|
||||
static inline __attribute__ (( always_inline )) void
|
||||
uart_flush ( struct uart *uart ) {
|
||||
|
||||
uart->op->flush ( uart );
|
||||
}
|
||||
|
||||
extern struct list_head uarts;
|
||||
extern struct uart_operations null_uart_operations;
|
||||
|
||||
/**
|
||||
* Get reference to UART
|
||||
*
|
||||
* @v uart UART
|
||||
* @ret uart UART
|
||||
*/
|
||||
static inline __attribute__ (( always_inline )) struct uart *
|
||||
uart_get ( struct uart *uart ) {
|
||||
|
||||
ref_get ( &uart->refcnt );
|
||||
return uart;
|
||||
}
|
||||
|
||||
/**
|
||||
* Drop reference to UART
|
||||
*
|
||||
* @v uart UART
|
||||
*/
|
||||
static inline __attribute__ (( always_inline )) void
|
||||
uart_put ( struct uart *uart ) {
|
||||
|
||||
ref_put ( &uart->refcnt );
|
||||
}
|
||||
|
||||
/**
|
||||
* Nullify UART
|
||||
*
|
||||
* @v uart UART
|
||||
*/
|
||||
static inline __attribute__ (( always_inline )) void
|
||||
uart_nullify ( struct uart *uart ) {
|
||||
|
||||
uart->op = &null_uart_operations;
|
||||
}
|
||||
|
||||
extern struct uart * alloc_uart ( size_t priv_len );
|
||||
extern int uart_register ( struct uart *uart );
|
||||
extern int uart_register_fixed ( void );
|
||||
extern void uart_unregister ( struct uart *uart );
|
||||
extern struct uart * uart_find ( const char *name );
|
||||
|
||||
#endif /* _IPXE_UART_H */
|
||||
|
||||
Reference in New Issue
Block a user