Commit fdc2de87 authored by Je Yen Tam's avatar Je Yen Tam Committed by Greg Kroah-Hartman

serial/8250: Add support for NI-Serial PXI/PXIe+485 devices

Add support for NI-Serial PXIe-RS232, PXI-RS485 and PXIe-RS485 devices.
Signed-off-by: default avatarJe Yen Tam <je.yen.tam@ni.com>
Link: https://lore.kernel.org/r/20190726074012.2590-1-je.yen.tam@ni.comSigned-off-by: default avatarGreg Kroah-Hartman <gregkh@linuxfoundation.org>
parent fe94347d
......@@ -730,8 +730,16 @@ static int pci_ni8430_init(struct pci_dev *dev)
}
/* UART Port Control Register */
#define NI8430_PORTCON 0x0f
#define NI8430_PORTCON_TXVR_ENABLE (1 << 3)
#define NI16550_PCR_OFFSET 0x0f
#define NI16550_PCR_RS422 0x00
#define NI16550_PCR_ECHO_RS485 0x01
#define NI16550_PCR_DTR_RS485 0x02
#define NI16550_PCR_AUTO_RS485 0x03
#define NI16550_PCR_WIRE_MODE_MASK 0x03
#define NI16550_PCR_TXVR_ENABLE_BIT BIT(3)
#define NI16550_PCR_RS485_TERMINATION_BIT BIT(6)
#define NI16550_ACR_DTR_AUTO_DTR (0x2 << 3)
#define NI16550_ACR_DTR_MANUAL_DTR (0x0 << 3)
static int
pci_ni8430_setup(struct serial_private *priv,
......@@ -753,14 +761,117 @@ pci_ni8430_setup(struct serial_private *priv,
return -ENOMEM;
/* enable the transceiver */
writeb(readb(p + offset + NI8430_PORTCON) | NI8430_PORTCON_TXVR_ENABLE,
p + offset + NI8430_PORTCON);
writeb(readb(p + offset + NI16550_PCR_OFFSET) | NI16550_PCR_TXVR_ENABLE_BIT,
p + offset + NI16550_PCR_OFFSET);
iounmap(p);
return setup_port(priv, port, bar, offset, board->reg_shift);
}
static int pci_ni8431_config_rs485(struct uart_port *port,
struct serial_rs485 *rs485)
{
u8 pcr, acr;
struct uart_8250_port *up;
up = container_of(port, struct uart_8250_port, port);
acr = up->acr;
pcr = port->serial_in(port, NI16550_PCR_OFFSET);
pcr &= ~NI16550_PCR_WIRE_MODE_MASK;
if (rs485->flags & SER_RS485_ENABLED) {
/* RS-485 */
if ((rs485->flags & SER_RS485_RX_DURING_TX) &&
(rs485->flags & SER_RS485_RTS_ON_SEND)) {
dev_dbg(port->dev, "Invalid 2-wire mode\n");
return -EINVAL;
}
if (rs485->flags & SER_RS485_RX_DURING_TX) {
/* Echo */
dev_vdbg(port->dev, "2-wire DTR with echo\n");
pcr |= NI16550_PCR_ECHO_RS485;
acr |= NI16550_ACR_DTR_MANUAL_DTR;
} else {
/* Auto or DTR */
if (rs485->flags & SER_RS485_RTS_ON_SEND) {
/* Auto */
dev_vdbg(port->dev, "2-wire Auto\n");
pcr |= NI16550_PCR_AUTO_RS485;
acr |= NI16550_ACR_DTR_AUTO_DTR;
} else {
/* DTR-controlled */
/* No Echo */
dev_vdbg(port->dev, "2-wire DTR no echo\n");
pcr |= NI16550_PCR_DTR_RS485;
acr |= NI16550_ACR_DTR_MANUAL_DTR;
}
}
} else {
/* RS-422 */
dev_vdbg(port->dev, "4-wire\n");
pcr |= NI16550_PCR_RS422;
acr |= NI16550_ACR_DTR_MANUAL_DTR;
}
dev_dbg(port->dev, "write pcr: 0x%08x\n", pcr);
port->serial_out(port, NI16550_PCR_OFFSET, pcr);
up->acr = acr;
port->serial_out(port, UART_SCR, UART_ACR);
port->serial_out(port, UART_ICR, up->acr);
/* Update the cache. */
port->rs485 = *rs485;
return 0;
}
static int pci_ni8431_setup(struct serial_private *priv,
const struct pciserial_board *board,
struct uart_8250_port *uart, int idx)
{
u8 pcr, acr;
struct pci_dev *dev = priv->dev;
void __iomem *addr;
unsigned int bar, offset = board->first_offset;
if (idx >= board->num_ports)
return 1;
bar = FL_GET_BASE(board->flags);
offset += idx * board->uart_offset;
addr = pci_ioremap_bar(dev, bar);
if (!addr)
return -ENOMEM;
/* enable the transceiver */
writeb(readb(addr + NI16550_PCR_OFFSET) | NI16550_PCR_TXVR_ENABLE_BIT,
addr + NI16550_PCR_OFFSET);
pcr = readb(addr + NI16550_PCR_OFFSET);
pcr &= ~NI16550_PCR_WIRE_MODE_MASK;
/* set wire mode to default RS-422 */
pcr |= NI16550_PCR_RS422;
acr = NI16550_ACR_DTR_MANUAL_DTR;
/* write port configuration to register */
writeb(pcr, addr + NI16550_PCR_OFFSET);
/* access and write to UART acr register */
writeb(UART_ACR, addr + UART_SCR);
writeb(acr, addr + UART_ICR);
uart->port.rs485_config = &pci_ni8431_config_rs485;
iounmap(addr);
return setup_port(priv, uart, bar, offset, board->reg_shift);
}
static int pci_netmos_9900_setup(struct serial_private *priv,
const struct pciserial_board *board,
struct uart_8250_port *port, int idx)
......@@ -1786,6 +1897,15 @@ pci_wch_ch38x_setup(struct serial_private *priv,
#define PCI_DEVICE_ID_ACCESIO_PCIE_COM_8SM 0x10E9
#define PCI_DEVICE_ID_ACCESIO_PCIE_ICM_4SM 0x11D8
#define PCIE_DEVICE_ID_NI_PXIE8430_2328 0x74C2
#define PCIE_DEVICE_ID_NI_PXIE8430_23216 0x74C1
#define PCI_DEVICE_ID_NI_PXI8431_4852 0x7081
#define PCI_DEVICE_ID_NI_PXI8431_4854 0x70DE
#define PCI_DEVICE_ID_NI_PXI8431_4858 0x70E3
#define PCI_DEVICE_ID_NI_PXI8433_4852 0x70E9
#define PCI_DEVICE_ID_NI_PXI8433_4854 0x70ED
#define PCIE_DEVICE_ID_NI_PXIE8431_4858 0x74C4
#define PCIE_DEVICE_ID_NI_PXIE8431_48516 0x74C3
/* Unknown vendors/cards - this should not be in linux/pci_ids.h */
......@@ -2011,6 +2131,87 @@ static struct pci_serial_quirk pci_serial_quirks[] __refdata = {
.setup = pci_ni8430_setup,
.exit = pci_ni8430_exit,
},
{
.vendor = PCI_VENDOR_ID_NI,
.device = PCIE_DEVICE_ID_NI_PXIE8430_2328,
.subvendor = PCI_ANY_ID,
.subdevice = PCI_ANY_ID,
.init = pci_ni8430_init,
.setup = pci_ni8430_setup,
.exit = pci_ni8430_exit,
},
{
.vendor = PCI_VENDOR_ID_NI,
.device = PCIE_DEVICE_ID_NI_PXIE8430_23216,
.subvendor = PCI_ANY_ID,
.subdevice = PCI_ANY_ID,
.init = pci_ni8430_init,
.setup = pci_ni8430_setup,
.exit = pci_ni8430_exit,
},
{
.vendor = PCI_VENDOR_ID_NI,
.device = PCI_DEVICE_ID_NI_PXI8431_4852,
.subvendor = PCI_ANY_ID,
.subdevice = PCI_ANY_ID,
.init = pci_ni8430_init,
.setup = pci_ni8431_setup,
.exit = pci_ni8430_exit,
},
{
.vendor = PCI_VENDOR_ID_NI,
.device = PCI_DEVICE_ID_NI_PXI8431_4854,
.subvendor = PCI_ANY_ID,
.subdevice = PCI_ANY_ID,
.init = pci_ni8430_init,
.setup = pci_ni8431_setup,
.exit = pci_ni8430_exit,
},
{
.vendor = PCI_VENDOR_ID_NI,
.device = PCI_DEVICE_ID_NI_PXI8431_4858,
.subvendor = PCI_ANY_ID,
.subdevice = PCI_ANY_ID,
.init = pci_ni8430_init,
.setup = pci_ni8431_setup,
.exit = pci_ni8430_exit,
},
{
.vendor = PCI_VENDOR_ID_NI,
.device = PCI_DEVICE_ID_NI_PXI8433_4852,
.subvendor = PCI_ANY_ID,
.subdevice = PCI_ANY_ID,
.init = pci_ni8430_init,
.setup = pci_ni8431_setup,
.exit = pci_ni8430_exit,
},
{
.vendor = PCI_VENDOR_ID_NI,
.device = PCI_DEVICE_ID_NI_PXI8433_4854,
.subvendor = PCI_ANY_ID,
.subdevice = PCI_ANY_ID,
.init = pci_ni8430_init,
.setup = pci_ni8431_setup,
.exit = pci_ni8430_exit,
},
{
.vendor = PCI_VENDOR_ID_NI,
.device = PCIE_DEVICE_ID_NI_PXIE8431_4858,
.subvendor = PCI_ANY_ID,
.subdevice = PCI_ANY_ID,
.init = pci_ni8430_init,
.setup = pci_ni8431_setup,
.exit = pci_ni8430_exit,
},
{
.vendor = PCI_VENDOR_ID_NI,
.device = PCIE_DEVICE_ID_NI_PXIE8431_48516,
.subvendor = PCI_ANY_ID,
.subdevice = PCI_ANY_ID,
.init = pci_ni8430_init,
.setup = pci_ni8431_setup,
.exit = pci_ni8430_exit,
},
/* Quatech */
{
.vendor = PCI_VENDOR_ID_QUATECH,
......@@ -2740,6 +2941,13 @@ enum pci_board_num_t {
pbn_ni8430_4,
pbn_ni8430_8,
pbn_ni8430_16,
pbn_ni8430_pxie_8,
pbn_ni8430_pxie_16,
pbn_ni8431_2,
pbn_ni8431_4,
pbn_ni8431_8,
pbn_ni8431_pxie_8,
pbn_ni8431_pxie_16,
pbn_ADDIDATA_PCIe_1_3906250,
pbn_ADDIDATA_PCIe_2_3906250,
pbn_ADDIDATA_PCIe_4_3906250,
......@@ -3381,6 +3589,55 @@ static struct pciserial_board pci_boards[] = {
.uart_offset = 0x10,
.first_offset = 0x800,
},
[pbn_ni8430_pxie_16] = {
.flags = FL_BASE0,
.num_ports = 16,
.base_baud = 3125000,
.uart_offset = 0x10,
.first_offset = 0x800,
},
[pbn_ni8430_pxie_8] = {
.flags = FL_BASE0,
.num_ports = 8,
.base_baud = 3125000,
.uart_offset = 0x10,
.first_offset = 0x800,
},
[pbn_ni8431_8] = {
.flags = FL_BASE0,
.num_ports = 8,
.base_baud = 3686400,
.uart_offset = 0x10,
.first_offset = 0x800,
},
[pbn_ni8431_4] = {
.flags = FL_BASE0,
.num_ports = 4,
.base_baud = 3686400,
.uart_offset = 0x10,
.first_offset = 0x800,
},
[pbn_ni8431_2] = {
.flags = FL_BASE0,
.num_ports = 2,
.base_baud = 3686400,
.uart_offset = 0x10,
.first_offset = 0x800,
},
[pbn_ni8431_pxie_16] = {
.flags = FL_BASE0,
.num_ports = 16,
.base_baud = 3125000,
.uart_offset = 0x10,
.first_offset = 0x800,
},
[pbn_ni8431_pxie_8] = {
.flags = FL_BASE0,
.num_ports = 8,
.base_baud = 3125000,
.uart_offset = 0x10,
.first_offset = 0x800,
},
/*
* ADDI-DATA GmbH PCI-Express communication cards <info@addi-data.com>
*/
......@@ -5063,6 +5320,33 @@ static const struct pci_device_id serial_pci_tbl[] = {
{ PCI_VENDOR_ID_NI, PCI_DEVICE_ID_NI_PCI8432_2324,
PCI_ANY_ID, PCI_ANY_ID, 0, 0,
pbn_ni8430_4 },
{ PCI_VENDOR_ID_NI, PCIE_DEVICE_ID_NI_PXIE8430_2328,
PCI_ANY_ID, PCI_ANY_ID, 0, 0,
pbn_ni8430_pxie_8 },
{ PCI_VENDOR_ID_NI, PCIE_DEVICE_ID_NI_PXIE8430_23216,
PCI_ANY_ID, PCI_ANY_ID, 0, 0,
pbn_ni8430_pxie_16 },
{ PCI_VENDOR_ID_NI, PCI_DEVICE_ID_NI_PXI8431_4852,
PCI_ANY_ID, PCI_ANY_ID, 0, 0,
pbn_ni8431_2 },
{ PCI_VENDOR_ID_NI, PCI_DEVICE_ID_NI_PXI8431_4854,
PCI_ANY_ID, PCI_ANY_ID, 0, 0,
pbn_ni8431_4 },
{ PCI_VENDOR_ID_NI, PCI_DEVICE_ID_NI_PXI8431_4858,
PCI_ANY_ID, PCI_ANY_ID, 0, 0,
pbn_ni8431_8 },
{ PCI_VENDOR_ID_NI, PCIE_DEVICE_ID_NI_PXIE8431_4858,
PCI_ANY_ID, PCI_ANY_ID, 0, 0,
pbn_ni8431_pxie_8 },
{ PCI_VENDOR_ID_NI, PCIE_DEVICE_ID_NI_PXIE8431_48516,
PCI_ANY_ID, PCI_ANY_ID, 0, 0,
pbn_ni8431_pxie_16 },
{ PCI_VENDOR_ID_NI, PCI_DEVICE_ID_NI_PXI8433_4852,
PCI_ANY_ID, PCI_ANY_ID, 0, 0,
pbn_ni8431_2 },
{ PCI_VENDOR_ID_NI, PCI_DEVICE_ID_NI_PXI8433_4854,
PCI_ANY_ID, PCI_ANY_ID, 0, 0,
pbn_ni8431_4 },
/*
* ADDI-DATA GmbH communication cards <info@addi-data.com>
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment