Skip to content

Commit 6bf16de

Browse files
PataterBrad Mouring
authored andcommitted
8250: Add a driver for NI 16550 UART
The National Instruments (NI) 16550 is a standard 16550 with larger FIFOs and embedded RS-485 transceiver control circuitry. This patch adds a driver that can operate this UART. Signed-off-by: Jaeden Amero <jaeden.amero@ni.com> Acked-by: Karthik Manamcheri <karthik.manamcheri@ni.com>
1 parent c38869b commit 6bf16de

File tree

7 files changed

+208
-0
lines changed

7 files changed

+208
-0
lines changed

drivers/tty/serial/8250/8250_core.c

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -333,6 +333,13 @@ static const struct serial8250_config uart_config[] = {
333333
.fcr = UART_FCR_ENABLE_FIFO | UART_FCR_R_TRIG_10,
334334
.flags = UART_CAP_FIFO | UART_CAP_AFE,
335335
},
336+
[PORT_NI16550] = {
337+
.name = "NI 16550",
338+
.fifo_size = 128,
339+
.tx_loadsz = 128,
340+
.fcr = UART_FCR_ENABLE_FIFO | UART_FCR_R_TRIG_10,
341+
.flags = UART_CAP_FIFO | UART_CAP_AFE | UART_CAP_EFR,
342+
},
336343
};
337344

338345
/* Uart divisor latch read */
Lines changed: 159 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,159 @@
1+
/*
2+
* NI 16550 Transceiver Driver
3+
*
4+
* The National Instruments (NI) 16550 has built-in RS-485 transceiver control
5+
* circuitry. This driver provides the transceiver control functionality
6+
* for the RS-485 ports and uses the 8250 driver for the UART functionality.
7+
*
8+
* Copyright 2012 National Instruments Corporation
9+
*
10+
* This program is free software; you can redistribute it and/or modify it
11+
* under the terms of the GNU General Public License as published by the Free
12+
* Software Foundation; either version 2 of the License, or (at your option)
13+
* any later version.
14+
*
15+
* This program is distributed in the hope that it will be useful, but WITHOUT
16+
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
17+
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
18+
* more details.
19+
*
20+
* You should have received a copy of the GNU General Public License along
21+
* with this program; if not, write to the Free Software Foundation, Inc., 51
22+
* Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
23+
*/
24+
25+
#include "8250.h"
26+
#include <linux/init.h>
27+
#include <linux/uaccess.h>
28+
#include <linux/device.h>
29+
30+
#define NI16550_PCR_OFFSET 0x0F
31+
#define NI16550_PCR_RS422 0x00
32+
#define NI16550_PCR_ECHO_RS485 0x01
33+
#define NI16550_PCR_DTR_RS485 0x02
34+
#define NI16550_PCR_AUTO_RS485 0x03
35+
#define NI16550_PCR_WIRE_MODE_MASK 0x03
36+
#define NI16550_PCR_TXVR_ENABLE_BIT (1 << 3)
37+
#define NI16550_PCR_RS485_TERMINATION_BIT (1 << 6)
38+
39+
static int ni16550_enable_transceivers(struct uart_port *port)
40+
{
41+
uint8_t pcr;
42+
dev_dbg(port->dev, ">ni16550_enable_transceivers\n");
43+
44+
pcr = port->serial_in(port, NI16550_PCR_OFFSET);
45+
pcr |= NI16550_PCR_TXVR_ENABLE_BIT;
46+
dev_dbg(port->dev, "write pcr: 0x%08x\n", pcr);
47+
port->serial_out(port, NI16550_PCR_OFFSET, pcr);
48+
49+
dev_dbg(port->dev, "<ni16550_enable_transceivers\n");
50+
51+
return 0;
52+
}
53+
54+
static int ni16550_disable_transceivers(struct uart_port *port)
55+
{
56+
uint8_t pcr;
57+
dev_dbg(port->dev, ">ni16550_disable_transceivers\n");
58+
59+
pcr = port->serial_in(port, NI16550_PCR_OFFSET);
60+
pcr &= ~NI16550_PCR_TXVR_ENABLE_BIT;
61+
dev_dbg(port->dev, "write pcr: 0x%08x\n", pcr);
62+
port->serial_out(port, NI16550_PCR_OFFSET, pcr);
63+
64+
dev_dbg(port->dev, "<ni16550_disable_transceivers\n");
65+
66+
return 0;
67+
}
68+
69+
static int ni16550_config_rs485(struct uart_port *port,
70+
struct serial_rs485 *rs485)
71+
{
72+
uint8_t pcr;
73+
dev_dbg(port->dev, ">ni16550_config_rs485\n");
74+
75+
/* "rs485" should be given to us non-NULL. */
76+
BUG_ON(rs485 == NULL);
77+
78+
pcr = port->serial_in(port, NI16550_PCR_OFFSET);
79+
pcr &= ~NI16550_PCR_WIRE_MODE_MASK;
80+
81+
if (rs485->flags & SER_RS485_ENABLED) {
82+
/* RS-485 */
83+
if ((rs485->flags & SER_RS485_RX_DURING_TX) &&
84+
(rs485->flags & SER_RS485_RTS_ON_SEND)) {
85+
dev_dbg(port->dev, "Invalid 2-wire mode\n");
86+
return -EINVAL;
87+
}
88+
89+
if (rs485->flags & SER_RS485_RX_DURING_TX) {
90+
/* Echo */
91+
dev_vdbg(port->dev, "2-wire DTR with echo\n");
92+
pcr |= NI16550_PCR_ECHO_RS485;
93+
} else {
94+
/* Auto or DTR */
95+
if (rs485->flags & SER_RS485_RTS_ON_SEND) {
96+
/* Auto */
97+
dev_vdbg(port->dev, "2-wire Auto\n");
98+
pcr |= NI16550_PCR_AUTO_RS485;
99+
} else {
100+
/* DTR-controlled */
101+
/* No Echo */
102+
dev_vdbg(port->dev, "2-wire DTR no echo\n");
103+
pcr |= NI16550_PCR_DTR_RS485;
104+
}
105+
}
106+
} else {
107+
/* RS-422 */
108+
dev_vdbg(port->dev, "4-wire\n");
109+
pcr |= NI16550_PCR_RS422;
110+
}
111+
112+
dev_dbg(port->dev, "write pcr: 0x%08x\n", pcr);
113+
port->serial_out(port, NI16550_PCR_OFFSET, pcr);
114+
115+
/* Update the cache. */
116+
port->rs485 = *rs485;
117+
118+
dev_dbg(port->dev, "<ni16550_config_rs485\n");
119+
return 0;
120+
}
121+
122+
static void
123+
ni16550_set_rs485_defaults(struct serial_rs485 *rs485)
124+
{
125+
rs485->flags = SER_RS485_ENABLED | SER_RS485_RTS_ON_SEND;
126+
}
127+
128+
static struct txvr_ops ni16550_txvr_ops = {
129+
.enable_transceivers = ni16550_enable_transceivers,
130+
.disable_transceivers = ni16550_disable_transceivers,
131+
.config_rs485 = ni16550_config_rs485,
132+
};
133+
134+
int ni16550_register_port(struct uart_8250_port *uart)
135+
{
136+
int line;
137+
138+
dev_dbg(uart->port.dev, ">ni16550_register_port\n");
139+
140+
uart->port.txvr_ops = &ni16550_txvr_ops;
141+
ni16550_set_rs485_defaults(&(uart->port.rs485));
142+
143+
line = serial8250_register_8250_port(uart);
144+
dev_vdbg(uart->port.dev, "line: 0x%08x\n", line);
145+
if (line < 0) {
146+
dev_dbg(uart->port.dev, "line is less than zero\n");
147+
dev_dbg(uart->port.dev, "<ni16550_register_port\n");
148+
return -ENODEV;
149+
}
150+
151+
dev_dbg(uart->port.dev, "<ni16550_register_port\n");
152+
153+
return line;
154+
}
155+
156+
void ni16550_unregister_port(int line)
157+
{
158+
serial8250_unregister_port(line);
159+
}

drivers/tty/serial/8250/Kconfig

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -301,3 +301,9 @@ config SERIAL_8250_RT288X
301301
If you have a Ralink RT288x/RT305x SoC based board and want to use the
302302
serial port, say Y to this option. The driver can handle up to 2 serial
303303
ports. If unsure, say N.
304+
305+
config SERIAL_8250_NI16550
306+
bool "NI 16550 UART support"
307+
depends on SERIAL_8250
308+
help
309+
This driver supports the National Instruments (NI) 16550 UART port.

drivers/tty/serial/8250/Makefile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,3 +20,4 @@ obj-$(CONFIG_SERIAL_8250_HUB6) += 8250_hub6.o
2020
obj-$(CONFIG_SERIAL_8250_FSL) += 8250_fsl.o
2121
obj-$(CONFIG_SERIAL_8250_DW) += 8250_dw.o
2222
obj-$(CONFIG_SERIAL_8250_EM) += 8250_em.o
23+
obj-$(CONFIG_SERIAL_8250_NI16550) += 8250_ni16550.o

include/linux/ni16550.h

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
/*
2+
* NI 16550 Transceiver Driver
3+
*
4+
* The National Instruments (NI) 16550 has built-in RS-485 transceiver control
5+
* circuitry. This driver provides the transceiver control functionality
6+
* for the RS-485 ports and uses the 8250 driver for the UART functionality.
7+
*
8+
* Copyright 2012 National Instruments Corporation
9+
*
10+
* This program is free software; you can redistribute it and/or modify it
11+
* under the terms of the GNU General Public License as published by the Free
12+
* Software Foundation; either version 2 of the License, or (at your option)
13+
* any later version.
14+
*
15+
* This program is distributed in the hope that it will be useful, but WITHOUT
16+
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
17+
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
18+
* more details.
19+
*
20+
* You should have received a copy of the GNU General Public License along
21+
* with this program; if not, write to the Free Software Foundation, Inc., 51
22+
* Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
23+
*/
24+
25+
#ifndef _NI16550_H
26+
#define _NI16550_H
27+
28+
int ni16550_register_port(struct uart_8250_port *uart);
29+
void ni16550_unregister_port(int line);
30+
31+
#endif

include/uapi/linux/serial_core.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -229,4 +229,7 @@
229229
/* SH-SCI */
230230
#define PORT_HSCIF 103
231231

232+
/* National Instruments 16550 UART */
233+
#define PORT_NI16550 104
234+
232235
#endif /* _UAPILINUX_SERIAL_CORE_H */

include/uapi/linux/serial_reg.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,7 @@
6262
* TI16C750: 1 16 32 56 xx xx xx xx PORT_16750
6363
* TI16C752: 8 16 56 60 8 16 32 56
6464
* Tegra: 1 4 8 14 16 8 4 1 PORT_TEGRA
65+
* NI 16550: 1 32 64 112 xx xx xx xx PORT_NI16550
6566
*/
6667
#define UART_FCR_R_TRIG_00 0x00
6768
#define UART_FCR_R_TRIG_01 0x40

0 commit comments

Comments
 (0)