iPXE
ns16550.c
Go to the documentation of this file.
1/*
2 * Copyright (C) 2025 Michael Brown <mbrown@fensystems.co.uk>.
3 *
4 * This program is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU General Public License as
6 * published by the Free Software Foundation; either version 2 of the
7 * License, or any later version.
8 *
9 * This program is distributed in the hope that it will be useful, but
10 * WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
17 * 02110-1301, USA.
18 *
19 * You can also choose to distribute this program under the terms of
20 * the Unmodified Binary Distribution Licence (as given in the file
21 * COPYING.UBDL), provided that you have satisfied its requirements.
22 */
23
24FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
25
26/** @file
27 *
28 * 16550-compatible UART
29 *
30 */
31
32#include <unistd.h>
33#include <errno.h>
34#include <ipxe/uart.h>
35#include <ipxe/ns16550.h>
36
37/** Timeout for transmit holding register to become empty */
38#define NS16550_THRE_TIMEOUT_MS 100
39
40/** Timeout for transmitter to become empty */
41#define NS16550_TEMT_TIMEOUT_MS 1000
42
43/**
44 * Transmit data
45 *
46 * @v uart UART
47 * @v data Data
48 */
49static void ns16550_transmit ( struct uart *uart, uint8_t data ) {
50 struct ns16550_uart *ns16550 = uart->priv;
51 unsigned int i;
52 uint8_t lsr;
53
54 /* Wait for transmitter holding register to become empty */
55 for ( i = 0 ; i < NS16550_THRE_TIMEOUT_MS ; i++ ) {
56 lsr = ns16550_read ( ns16550, NS16550_LSR );
57 if ( lsr & NS16550_LSR_THRE )
58 break;
59 mdelay ( 1 );
60 }
61
62 /* Transmit data (even if we timed out) */
63 ns16550_write ( ns16550, NS16550_THR, data );
64}
65
66/**
67 * Check if data is ready
68 *
69 * @v uart UART
70 * @ret ready Data is ready
71 */
72static int ns16550_data_ready ( struct uart *uart ) {
73 struct ns16550_uart *ns16550 = uart->priv;
74 uint8_t lsr;
75
76 /* Check for receive data ready */
77 lsr = ns16550_read ( ns16550, NS16550_LSR );
78 return ( lsr & NS16550_LSR_DR );
79}
80
81/**
82 * Receive data
83 *
84 * @v uart UART
85 * @ret data Data
86 */
87static uint8_t ns16550_receive ( struct uart *uart ) {
88 struct ns16550_uart *ns16550 = uart->priv;
89 uint8_t rbr;
90
91 /* Receive byte */
92 rbr = ns16550_read ( ns16550, NS16550_RBR );
93 return rbr;
94}
95
96/**
97 * Flush transmitted data
98 *
99 * @v uart UART
100 */
101static void ns16550_flush ( struct uart *uart ) {
102 struct ns16550_uart *ns16550 = uart->priv;
103 unsigned int i;
104 uint8_t lsr;
105
106 /* Wait for transmitter to become empty */
107 for ( i = 0 ; i < NS16550_TEMT_TIMEOUT_MS ; i++ ) {
108 lsr = ns16550_read ( ns16550, NS16550_LSR );
109 if ( lsr & NS16550_LSR_TEMT )
110 break;
111 }
112}
113
114/**
115 * Initialise UART
116 *
117 * @v uart UART
118 * @ret rc Return status code
119 */
120static int ns16550_init ( struct uart *uart ) {
121 struct ns16550_uart *ns16550 = uart->priv;
122 uint8_t dlm;
123 uint8_t dll;
124
125 /* Fail if UART scratch register seems not to be present */
126 ns16550_write ( ns16550, NS16550_SCR, 0x18 );
127 if ( ns16550_read ( ns16550, NS16550_SCR ) != 0x18 )
128 return -ENODEV;
129 ns16550_write ( ns16550, NS16550_SCR, 0xae );
130 if ( ns16550_read ( ns16550, NS16550_SCR ) != 0xae )
131 return -ENODEV;
132
133 /* Wait for UART to become idle before modifying LCR */
135
136 /* Configure divisor and line control register, if applicable */
137 ns16550_write ( ns16550, NS16550_LCR,
139 if ( uart->baud ) {
140 ns16550->divisor = ( ( ns16550->clock / uart->baud ) /
142 dlm = ( ( ns16550->divisor >> 8 ) & 0xff );
143 dll = ( ( ns16550->divisor >> 0 ) & 0xff );
144 ns16550_write ( ns16550, NS16550_DLM, dlm );
145 ns16550_write ( ns16550, NS16550_DLL, dll );
146 } else {
147 dlm = ns16550_read ( ns16550, NS16550_DLM );
148 dll = ns16550_read ( ns16550, NS16550_DLL );
149 ns16550->divisor = ( ( dlm << 8 ) | dll );
150 }
152
153 /* Disable interrupts */
154 ns16550_write ( ns16550, NS16550_IER, 0 );
155
156 /* Enable FIFOs */
158
159 /* Assert DTR and RTS */
160 ns16550_write ( ns16550, NS16550_MCR,
162
163 /* Flush any stale received data */
164 while ( ns16550_data_ready ( uart ) )
166
167 return 0;
168}
169
170/** 16550 UART operations */
172 .transmit = ns16550_transmit,
173 .data_ready = ns16550_data_ready,
174 .receive = ns16550_receive,
175 .init = ns16550_init,
176 .flush = ns16550_flush,
177};
unsigned char uint8_t
Definition stdint.h:10
uint8_t data[48]
Additional event data.
Definition ena.h:11
Error codes.
#define FILE_LICENCE(_licence)
Declare a particular licence as applying to a file.
Definition compiler.h:896
#define ENODEV
No such device.
Definition errno.h:510
16550-compatible UART
#define NS16550_DLL
Divisor latch (least significant byte)
Definition ns16550.h:74
#define NS16550_CLK_BIT
Post-division clock cycles per data bit.
Definition ns16550.h:92
#define NS16550_RBR
Receiver buffer register.
Definition ns16550.h:21
#define NS16550_FCR_FE
FIFO enable.
Definition ns16550.h:28
#define NS16550_THR
Transmitter holding register.
Definition ns16550.h:18
#define NS16550_LSR
Line status register.
Definition ns16550.h:65
#define NS16550_SCR
Scratch register.
Definition ns16550.h:71
#define NS16550_MCR
Modem control register.
Definition ns16550.h:60
#define NS16550_LSR_TEMT
Transmitter empty.
Definition ns16550.h:68
#define NS16550_DLM
Divisor latch (most significant byte)
Definition ns16550.h:77
#define NS16550_MCR_DTR
Data terminal ready.
Definition ns16550.h:61
#define NS16550_LCR
Line control register.
Definition ns16550.h:31
uint8_t ns16550_read(struct ns16550_uart *ns16550, unsigned int address)
#define NS16550_MCR_RTS
Request to send.
Definition ns16550.h:62
#define NS16550_LCR_DLAB
Divisor latch access bit.
Definition ns16550.h:37
#define NS16550_IER
Interrupt enable register.
Definition ns16550.h:24
#define NS16550_FCR
FIFO control register.
Definition ns16550.h:27
void ns16550_write(struct ns16550_uart *ns16550, unsigned int address, uint8_t data)
Dummy COM1 UART for non-x86 platforms.
#define NS16550_LSR_THRE
Transmitter holding reg.
Definition ns16550.h:67
#define NS16550_LSR_DR
Data ready.
Definition ns16550.h:66
#define NS16550_LCR_8N1
Default LCR value: 8 data bits, no parity, one stop bit.
Definition ns16550.h:57
static uint8_t ns16550_receive(struct uart *uart)
Receive data.
Definition ns16550.c:87
static int ns16550_data_ready(struct uart *uart)
Check if data is ready.
Definition ns16550.c:72
static int ns16550_init(struct uart *uart)
Initialise UART.
Definition ns16550.c:120
static void ns16550_flush(struct uart *uart)
Flush transmitted data.
Definition ns16550.c:101
static void ns16550_transmit(struct uart *uart, uint8_t data)
Transmit data.
Definition ns16550.c:49
struct uart_operations ns16550_operations
16550 UART operations
Definition ns16550.c:171
#define NS16550_TEMT_TIMEOUT_MS
Timeout for transmitter to become empty.
Definition ns16550.c:41
#define NS16550_THRE_TIMEOUT_MS
Timeout for transmit holding register to become empty.
Definition ns16550.c:38
A 16550-compatible UART.
Definition ns16550.h:80
unsigned int clock
Input clock frequency.
Definition ns16550.h:86
uint16_t divisor
Baud rate divisor.
Definition ns16550.h:88
UART operations.
Definition uart.h:35
A generic UART.
Definition uart.h:17
unsigned int baud
Baud rate (if specified)
Definition uart.h:26
void * priv
Driver-private data.
Definition uart.h:31
void mdelay(unsigned long msecs)
Delay for a fixed number of milliseconds.
Definition timer.c:79
Generic UART.