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 
24 FILE_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  */
49 static 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  */
72 static 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  */
87 static 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  */
101 static 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  */
120 static 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 */
134  ns16550_flush ( uart );
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 ) /
141  NS16550_CLK_BIT );
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 ) )
165  ns16550_receive ( uart );
166 
167  return 0;
168 }
169 
170 /** 16550 UART operations */
173  .data_ready = ns16550_data_ready,
174  .receive = ns16550_receive,
175  .init = ns16550_init,
176  .flush = ns16550_flush,
177 };
Generic UART.
#define NS16550_LCR
Line control register.
Definition: ns16550.h:31
Error codes.
A generic UART.
Definition: uart.h:17
static uint8_t ns16550_receive(struct uart *uart)
Receive data.
Definition: ns16550.c:87
static int ns16550_init(struct uart *uart)
Initialise UART.
Definition: ns16550.c:120
FILE_LICENCE(GPL2_OR_LATER_OR_UBDL)
#define NS16550_SCR
Scratch register.
Definition: ns16550.h:71
#define NS16550_DLM
Divisor latch (most significant byte)
Definition: ns16550.h:77
static void ns16550_flush(struct uart *uart)
Flush transmitted data.
Definition: ns16550.c:101
#define NS16550_FCR_FE
FIFO enable.
Definition: ns16550.h:28
#define NS16550_LCR_DLAB
Divisor latch access bit.
Definition: ns16550.h:37
static void ns16550_transmit(struct uart *uart, uint8_t data)
Transmit data.
Definition: ns16550.c:49
#define NS16550_TEMT_TIMEOUT_MS
Timeout for transmitter to become empty.
Definition: ns16550.c:41
16550-compatible UART
unsigned int baud
Baud rate (if specified)
Definition: uart.h:26
void(* transmit)(struct uart *uart, uint8_t byte)
Transmit byte.
Definition: uart.h:43
UART operations.
Definition: uart.h:35
#define NS16550_MCR_DTR
Data terminal ready.
Definition: ns16550.h:61
#define NS16550_LSR_TEMT
Transmitter empty.
Definition: ns16550.h:68
uint16_t divisor
Baud rate divisor.
Definition: ns16550.h:88
#define NS16550_IER
Interrupt enable register.
Definition: ns16550.h:24
unsigned int clock
Input clock frequency.
Definition: ns16550.h:86
void * priv
Driver-private data.
Definition: uart.h:31
A 16550-compatible UART.
Definition: ns16550.h:80
#define ENODEV
No such device.
Definition: errno.h:509
unsigned char uint8_t
Definition: stdint.h:10
struct uart_operations ns16550_operations
16550 UART operations
Definition: ns16550.c:171
#define NS16550_MCR
Modem control register.
Definition: ns16550.h:60
uint8_t ns16550_read(struct ns16550_uart *ns16550, unsigned int address)
#define NS16550_LSR_DR
Data ready.
Definition: ns16550.h:66
#define NS16550_THR
Transmitter holding register.
Definition: ns16550.h:18
void mdelay(unsigned long msecs)
Delay for a fixed number of milliseconds.
Definition: timer.c:78
#define NS16550_LSR
Line status register.
Definition: ns16550.h:65
#define NS16550_MCR_RTS
Request to send.
Definition: ns16550.h:62
#define NS16550_RBR
Receiver buffer register.
Definition: ns16550.h:21
#define NS16550_FCR
FIFO control register.
Definition: ns16550.h:27
#define NS16550_LSR_THRE
Transmitter holding reg.
Definition: ns16550.h:67
uint8_t data[48]
Additional event data.
Definition: ena.h:22
#define NS16550_CLK_BIT
Post-division clock cycles per data bit.
Definition: ns16550.h:92
#define NS16550_DLL
Divisor latch (least significant byte)
Definition: ns16550.h:74
#define NS16550_THRE_TIMEOUT_MS
Timeout for transmit holding register to become empty.
Definition: ns16550.c:38
void ns16550_write(struct ns16550_uart *ns16550, unsigned int address, uint8_t data)
Dummy COM1 UART for non-x86 platforms.
#define NS16550_LCR_8N1
Default LCR value: 8 data bits, no parity, one stop bit.
Definition: ns16550.h:57
static int ns16550_data_ready(struct uart *uart)
Check if data is ready.
Definition: ns16550.c:72