Quellcode durchsuchen

[serial] Add general abstraction of a 16550-compatible UART

Signed-off-by: Michael Brown <mcb30@ipxe.org>
tags/v1.20.1
Michael Brown vor 9 Jahren
Ursprung
Commit
611c9e39da

+ 62
- 0
src/arch/x86/core/x86_uart.c Datei anzeigen

@@ -0,0 +1,62 @@
1
+/*
2
+ * Copyright (C) 2014 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 <errno.h>
33
+#include <ipxe/uart.h>
34
+
35
+/** UART port bases */
36
+static uint16_t uart_base[] = {
37
+	[COM1] = 0x3f8,
38
+	[COM2] = 0x2f8,
39
+	[COM3] = 0x3e8,
40
+	[COM4] = 0x2e8,
41
+};
42
+
43
+/**
44
+ * Select UART port
45
+ *
46
+ * @v uart		UART
47
+ * @v port		Port number, or 0 to disable
48
+ * @ret rc		Return status code
49
+ */
50
+int uart_select ( struct uart *uart, unsigned int port ) {
51
+
52
+	/* Clear UART base */
53
+	uart->base = NULL;
54
+
55
+	/* Set new UART base */
56
+	if ( port < ( sizeof ( uart_base ) / sizeof ( uart_base[0] ) ) ) {
57
+		uart->base = ( ( void * ) ( intptr_t ) uart_base[port] );
58
+		return 0;
59
+	} else {
60
+		return -ENODEV;
61
+	}
62
+}

+ 1
- 0
src/arch/x86/include/bits/errfile.h Datei anzeigen

@@ -48,6 +48,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
48 48
 #define ERRFILE_timer_bios    ( ERRFILE_ARCH | ERRFILE_DRIVER | 0x00010000 )
49 49
 #define ERRFILE_hvm	      ( ERRFILE_ARCH | ERRFILE_DRIVER | 0x00020000 )
50 50
 #define ERRFILE_hyperv	      ( ERRFILE_ARCH | ERRFILE_DRIVER | 0x00030000 )
51
+#define ERRFILE_x86_uart      ( ERRFILE_ARCH | ERRFILE_DRIVER | 0x00040000 )
51 52
 
52 53
 #define ERRFILE_cpuid_cmd      ( ERRFILE_ARCH | ERRFILE_OTHER | 0x00000000 )
53 54
 #define ERRFILE_cpuid_settings ( ERRFILE_ARCH | ERRFILE_OTHER | 0x00010000 )

+ 41
- 0
src/arch/x86/include/bits/uart.h Datei anzeigen

@@ -0,0 +1,41 @@
1
+#ifndef _BITS_UART_H
2
+#define _BITS_UART_H
3
+
4
+/** @file
5
+ *
6
+ * 16550-compatible UART
7
+ *
8
+ */
9
+
10
+FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
11
+
12
+#include <stdint.h>
13
+#include <ipxe/io.h>
14
+
15
+/**
16
+ * Write to UART register
17
+ *
18
+ * @v uart		UART
19
+ * @v addr		Register address
20
+ * @v data		Data
21
+ */
22
+static inline __attribute__ (( always_inline )) void
23
+uart_write ( struct uart *uart, unsigned int addr, uint8_t data ) {
24
+	outb ( data, ( uart->base + addr ) );
25
+}
26
+
27
+/**
28
+ * Read from UART register
29
+ *
30
+ * @v uart		UART
31
+ * @v addr		Register address
32
+ * @ret data		Data
33
+ */
34
+static inline __attribute__ (( always_inline )) uint8_t
35
+uart_read ( struct uart *uart, unsigned int addr ) {
36
+	return inb ( uart->base + addr );
37
+}
38
+
39
+extern int uart_select ( struct uart *uart, unsigned int port );
40
+
41
+#endif /* _BITS_UART_H */

+ 135
- 0
src/core/uart.c Datei anzeigen

@@ -0,0 +1,135 @@
1
+/*
2
+ * Copyright (C) 2014 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
+
36
+/** Timeout for transmit holding register to become empty */
37
+#define UART_THRE_TIMEOUT_MS 100
38
+
39
+/** Timeout for transmitter to become empty */
40
+#define UART_TEMT_TIMEOUT_MS 1000
41
+
42
+/**
43
+ * Transmit data
44
+ *
45
+ * @v uart		UART
46
+ * @v data		Data
47
+ */
48
+void uart_transmit ( struct uart *uart, uint8_t data ) {
49
+	unsigned int i;
50
+	uint8_t lsr;
51
+
52
+	/* Wait for transmitter holding register to become empty */
53
+	for ( i = 0 ; i < UART_THRE_TIMEOUT_MS ; i++ ) {
54
+		lsr = uart_read ( uart, UART_LSR );
55
+		if ( lsr & UART_LSR_THRE )
56
+			break;
57
+		mdelay ( 1 );
58
+	}
59
+
60
+	/* Transmit data (even if we timed out) */
61
+	uart_write ( uart, UART_THR, data );
62
+}
63
+
64
+/**
65
+ * Flush data
66
+ *
67
+ * @v uart		UART
68
+ */
69
+void uart_flush ( struct uart *uart ) {
70
+	unsigned int i;
71
+	uint8_t lsr;
72
+
73
+	/* Wait for transmitter and receiver to become empty */
74
+	for ( i = 0 ; i < UART_TEMT_TIMEOUT_MS ; i++ ) {
75
+		uart_read ( uart, UART_RBR );
76
+		lsr = uart_read ( uart, UART_LSR );
77
+		if ( ( lsr & UART_LSR_TEMT ) && ! ( lsr & UART_LSR_DR ) )
78
+			break;
79
+	}
80
+}
81
+
82
+/**
83
+ * Initialise UART
84
+ *
85
+ * @v uart		UART
86
+ * @v baud		Baud rate, or zero to leave unchanged
87
+ * @v lcr		Line control register value, or zero to leave unchanged
88
+ * @ret rc		Return status code
89
+ */
90
+int uart_init ( struct uart *uart, unsigned int baud, uint8_t lcr ) {
91
+	uint8_t dlm;
92
+	uint8_t dll;
93
+
94
+	/* Check for existence of UART */
95
+	if ( ! uart->base )
96
+		return -ENODEV;
97
+	uart_write ( uart, UART_SCR, 0x18 );
98
+	if ( uart_read ( uart, UART_SCR ) != 0x18 )
99
+		return -ENODEV;
100
+	uart_write ( uart, UART_SCR, 0xae );
101
+	if ( uart_read ( uart, UART_SCR ) != 0xae )
102
+		return -ENODEV;
103
+
104
+	/* Configure divisor and line control register, if applicable */
105
+	if ( ! lcr )
106
+		lcr = uart_read ( uart, UART_LCR );
107
+	uart->lcr = lcr;
108
+	uart_write ( uart, UART_LCR, ( lcr | UART_LCR_DLAB ) );
109
+	if ( baud ) {
110
+		uart->divisor = ( UART_MAX_BAUD / baud );
111
+		dlm = ( ( uart->divisor >> 8 ) & 0xff );
112
+		dll = ( ( uart->divisor >> 0 ) & 0xff );
113
+		uart_write ( uart, UART_DLM, dlm );
114
+		uart_write ( uart, UART_DLL, dll );
115
+	} else {
116
+		dlm = uart_read ( uart, UART_DLM );
117
+		dll = uart_read ( uart, UART_DLL );
118
+		uart->divisor = ( ( dlm << 8 ) | dll );
119
+	}
120
+	uart_write ( uart, UART_LCR, ( lcr & ~UART_LCR_DLAB ) );
121
+
122
+	/* Disable interrupts */
123
+	uart_write ( uart, UART_IER, 0 );
124
+
125
+	/* Enable FIFOs */
126
+	uart_write ( uart, UART_FCR, UART_FCR_FE );
127
+
128
+	/* Assert DTR and RTS */
129
+	uart_write ( uart, UART_MCR, ( UART_MCR_DTR | UART_MCR_RTS ) );
130
+
131
+	/* Flush any stale data */
132
+	uart_flush ( uart );
133
+
134
+	return 0;
135
+}

+ 1
- 0
src/include/ipxe/errfile.h Datei anzeigen

@@ -89,6 +89,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
89 89
 #define ERRFILE_i2c_bit		     ( ERRFILE_DRIVER | 0x00120000 )
90 90
 #define ERRFILE_spi_bit		     ( ERRFILE_DRIVER | 0x00130000 )
91 91
 #define ERRFILE_nvsvpd		     ( ERRFILE_DRIVER | 0x00140000 )
92
+#define ERRFILE_uart		     ( ERRFILE_DRIVER | 0x00150000 )
92 93
 
93 94
 #define ERRFILE_3c509		     ( ERRFILE_DRIVER | 0x00200000 )
94 95
 #define ERRFILE_bnx2		     ( ERRFILE_DRIVER | 0x00210000 )

+ 131
- 0
src/include/ipxe/uart.h Datei anzeigen

@@ -0,0 +1,131 @@
1
+#ifndef _IPXE_UART_H
2
+#define _IPXE_UART_H
3
+
4
+/** @file
5
+ *
6
+ * 16550-compatible UART
7
+ *
8
+ */
9
+
10
+FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
11
+
12
+#include <stdint.h>
13
+
14
+/** Transmitter holding register */
15
+#define UART_THR 0x00
16
+
17
+/** Receiver buffer register */
18
+#define UART_RBR 0x00
19
+
20
+/** Interrupt enable register */
21
+#define UART_IER 0x01
22
+
23
+/** FIFO control register */
24
+#define UART_FCR 0x02
25
+#define UART_FCR_FE	0x01	/**< FIFO enable */
26
+
27
+/** Line control register */
28
+#define UART_LCR 0x03
29
+#define UART_LCR_WLS0	0x01	/**< Word length select bit 0 */
30
+#define UART_LCR_WLS1	0x02	/**< Word length select bit 1 */
31
+#define UART_LCR_STB	0x04	/**< Number of stop bits */
32
+#define UART_LCR_PEN	0x08	/**< Parity enable */
33
+#define UART_LCR_EPS	0x10	/**< Even parity select */
34
+#define UART_LCR_DLAB	0x80	/**< Divisor latch access bit */
35
+
36
+#define UART_LCR_WORD_LEN(x)	( ( (x) - 5 ) << 0 )	/**< Word length */
37
+#define UART_LCR_STOP_BITS(x)	( ( (x) - 1 ) << 2 )	/**< Stop bits */
38
+#define UART_LCR_PARITY(x)	( ( (x) - 0 ) << 3 )	/**< Parity */
39
+
40
+/**
41
+ * Calculate line control register value
42
+ *
43
+ * @v word_len		Word length (5-8)
44
+ * @v parity		Parity (0=none, 1=odd, 3=even)
45
+ * @v stop_bits		Stop bits (1-2)
46
+ * @ret lcr		Line control register value
47
+ */
48
+#define UART_LCR_WPS( word_len, parity, stop_bits )	\
49
+	( UART_LCR_WORD_LEN ( (word_len) ) |		\
50
+	  UART_LCR_PARITY ( (parity) ) |		\
51
+	  UART_LCR_STOP_BITS ( (stop_bits) ) )
52
+
53
+/** Default LCR value: 8 data bits, no parity, one stop bit */
54
+#define UART_LCR_8N1 UART_LCR_WPS ( 8, 0, 1 )
55
+
56
+/** Modem control register */
57
+#define UART_MCR 0x04
58
+#define UART_MCR_DTR	0x01	/**< Data terminal ready */
59
+#define UART_MCR_RTS	0x02	/**< Request to send */
60
+
61
+/** Line status register */
62
+#define UART_LSR 0x05
63
+#define UART_LSR_DR	0x01	/**< Data ready */
64
+#define UART_LSR_THRE	0x20	/**< Transmitter holding register empty */
65
+#define UART_LSR_TEMT	0x40	/**< Transmitter empty */
66
+
67
+/** Scratch register */
68
+#define UART_SCR 0x07
69
+
70
+/** Divisor latch (least significant byte) */
71
+#define UART_DLL 0x00
72
+
73
+/** Divisor latch (most significant byte) */
74
+#define UART_DLM 0x01
75
+
76
+/** Maximum baud rate */
77
+#define UART_MAX_BAUD 115200
78
+
79
+/** A 16550-compatible UART */
80
+struct uart {
81
+	/** I/O port base address */
82
+	void *base;
83
+	/** Baud rate divisor */
84
+	uint16_t divisor;
85
+	/** Line control register */
86
+	uint8_t lcr;
87
+};
88
+
89
+/** Symbolic names for port indexes */
90
+enum uart_port {
91
+	COM1 = 1,
92
+	COM2 = 2,
93
+	COM3 = 3,
94
+	COM4 = 4,
95
+};
96
+
97
+#include <bits/uart.h>
98
+
99
+void uart_write ( struct uart *uart, unsigned int addr, uint8_t data );
100
+uint8_t uart_read ( struct uart *uart, unsigned int addr );
101
+int uart_select ( struct uart *uart, unsigned int port );
102
+
103
+/**
104
+ * Check if received data is ready
105
+ *
106
+ * @v uart		UART
107
+ * @ret ready		Data is ready
108
+ */
109
+static inline int uart_data_ready ( struct uart *uart ) {
110
+	uint8_t lsr;
111
+
112
+	lsr = uart_read ( uart, UART_LSR );
113
+	return ( lsr & UART_LSR_DR );
114
+}
115
+
116
+/**
117
+ * Receive data
118
+ *
119
+ * @v uart		UART
120
+ * @ret data		Data
121
+ */
122
+static inline uint8_t uart_receive ( struct uart *uart ) {
123
+
124
+	return uart_read ( uart, UART_RBR );
125
+}
126
+
127
+extern void uart_transmit ( struct uart *uart, uint8_t data );
128
+extern void uart_flush ( struct uart *uart );
129
+extern int uart_init ( struct uart *uart, unsigned int baud, uint8_t lcr );
130
+
131
+#endif /* _IPXE_UART_H */

Laden…
Abbrechen
Speichern